From ff44267e4ef6a96688b969c1a93e8c0772fa0009 Mon Sep 17 00:00:00 2001 From: CjS77 Date: Tue, 3 Mar 2020 17:00:15 +0200 Subject: [PATCH] Alpha release 0.9 This is a testnet release candidate. The major contributions of this release: * Testnet node release candidate * Wallet library * libWallet FFi bindings * Peer-to-peer communications using noise protocol, tokio, and ToR. * LMDB and memory-based blockchain database * Simple testnet miner The tari_crypto, tari_utilities, oubsub crates have been moved into their own repos --- .circleci/config.yml | 97 +- .gitignore | 7 +- .gitmodules | 8 - Cargo.toml | 17 +- README.md | 14 - RFC/src/RFC-0152_EmojiId.md | 173 + RFC/src/SUMMARY.md | 1 + applications/cli_wallet/Cargo.toml | 5 - .../console_text_messenger/Cargo.toml | 26 - .../console_text_messenger/src/main.rs | 400 - applications/grpc_wallet/Cargo.toml | 44 - .../grpc_wallet/proto/wallet_rpc.proto | 85 - .../sample_config/node1_peers.json | 14 - .../sample_config/node2_peers.json | 14 - .../sample_config/node3_peers.json | 14 - .../sample_config/wallet_config_node1.toml | 17 - .../sample_config/wallet_config_node2.toml | 13 - .../sample_config/wallet_config_node3.toml | 13 - .../grpc_wallet/src/grpc_interface.rs | 395 - applications/grpc_wallet/src/main.rs | 275 - applications/grpc_wallet/src/wallet_server.rs | 82 - .../tests/wallet_grpc_server/mod.rs | 22 - .../wallet_grpc_server/wallet_grpc_server.rs | 631 - applications/tari_base_node/Cargo.toml | 36 + applications/tari_base_node/README.md | 11 + applications/tari_base_node/src/builder.rs | 640 + applications/tari_base_node/src/cli.rs | 60 + applications/tari_base_node/src/consts.rs | 25 + applications/tari_base_node/src/main.rs | 223 + applications/tari_base_node/src/miner.rs | 47 + applications/tari_base_node/src/parser.rs | 268 + applications/tari_basenode/Cargo.toml | 5 - applications/tari_miner/Cargo.toml | 5 - applications/tari_pool_miner/Cargo.toml | 5 - applications/test_faucet/Cargo.toml | 24 + applications/test_faucet/src/main.rs | 126 + base_layer/core/Cargo.toml | 54 +- base_layer/core/build.rs | 33 + base_layer/core/src/base_node/backoff.rs | 137 + base_layer/core/src/base_node/base_node.rs | 159 +- .../base_node/chain_metadata_service/error.rs | 40 + .../chain_metadata_service/handle.rs | 49 + .../chain_metadata_service/initializer.rs | 71 + .../base_node/chain_metadata_service/mod.rs | 32 + .../chain_metadata_service/service.rs | 415 + .../comms_interface/comms_request.rs | 78 + .../comms_interface/comms_response.rs | 42 + .../src/base_node/comms_interface/error.rs | 43 + .../comms_interface/inbound_handlers.rs | 247 + .../comms_interface/local_interface.rs | 118 + .../core/src/base_node/comms_interface/mod.rs | 36 + .../comms_interface/outbound_interface.rs | 189 + .../core/src/base_node/consts.rs | 18 +- base_layer/core/src/base_node/mod.rs | 27 +- .../src/base_node/proto/chain_metadata.proto | 15 + .../src/base_node/proto/chain_metadata.rs | 44 + .../core/src/base_node/proto/mmr_tree.proto | 12 + .../core/src/base_node/proto/mmr_tree.rs | 50 + base_layer/core/src/base_node/proto/mod.rs | 38 + .../core/src/base_node/proto/request.proto | 38 + .../core/src/base_node/proto/request.rs | 80 + .../core/src/base_node/proto/response.proto | 46 + .../core/src/base_node/proto/response.rs | 135 + .../src/base_node/service}/error.rs | 19 +- .../core/src/base_node/service/initializer.rs | 213 + base_layer/core/src/base_node/service/mod.rs | 33 + .../core/src/base_node/service/service.rs | 495 + .../src/base_node/service/service_request.rs | 41 +- .../src/base_node/service/service_response.rs | 24 +- .../core/src/base_node/states/block_sync.rs | 192 + .../src/base_node/states/error.rs} | 26 +- .../core/src/base_node/states/helpers.rs | 55 + .../core/src/base_node/states/initial_sync.rs | 265 + .../core/src/base_node/states/listening.rs | 131 + base_layer/core/src/base_node/states/mod.rs | 124 + .../src/base_node/states/shutdown_state.rs | 36 + .../src/base_node/states/starting_state.rs | 54 + base_layer/core/src/blockchain/chain.rs | 184 - base_layer/core/src/blocks/block.rs | 146 +- base_layer/core/src/blocks/blockheader.rs | 168 +- .../src/blocks/faucets/alphanet_faucet.json | 4000 ++++ base_layer/core/src/blocks/genesis_block.rs | 173 +- base_layer/core/src/blocks/mod.rs | 7 +- .../new_block_template.rs} | 55 +- .../src/blocks/new_blockheader_template.rs | 80 + base_layer/core/src/chain_storage/async_db.rs | 83 + .../src/chain_storage/blockchain_database.rs | 666 +- .../core/src/chain_storage/db_transaction.rs | 87 +- base_layer/core/src/chain_storage/error.rs | 20 +- .../src/chain_storage/historical_block.rs | 18 +- .../core/src/chain_storage/lmdb_db/lmdb.rs | 178 + .../core/src/chain_storage/lmdb_db/lmdb_db.rs | 883 + .../src/chain_storage/lmdb_db/lmdb_vec.rs | 239 + .../core/src/chain_storage/lmdb_db/mod.rs | 41 + .../core/src/chain_storage/memory_db.rs | 330 - .../src/chain_storage/memory_db/mem_db_vec.rs | 162 + .../src/chain_storage/memory_db/memory_db.rs | 603 + .../core/src/chain_storage/memory_db/mod.rs | 28 + base_layer/core/src/chain_storage/metadata.rs | 85 +- base_layer/core/src/chain_storage/mod.rs | 25 +- .../src/chain_storage/test/chain_storage.rs | 258 - .../core/src/consensus/consensus_constants.rs | 212 + .../core/src/consensus/consensus_manager.rs | 275 + .../core/src/{ => consensus}/emission.rs | 9 +- base_layer/core/src/consensus/mod.rs | 31 + base_layer/core/src/consensus/network.rs | 45 + base_layer/core/src/consts.rs | 42 - base_layer/core/src/helpers/mock_backend.rs | 116 + .../core/src/helpers/mod.rs | 62 +- base_layer/core/src/lib.rs | 55 +- base_layer/core/src/mempool/config.rs | 177 + base_layer/core/src/mempool/consts.rs | 46 + base_layer/core/src/mempool/error.rs | 19 +- base_layer/core/src/mempool/mempool.rs | 230 +- base_layer/core/src/mempool/mod.rs | 54 +- .../core/src/mempool/orphan_pool}/error.rs | 17 +- .../core/src/mempool/orphan_pool/mod.rs | 4 + .../src/mempool/orphan_pool/orphan_pool.rs | 293 +- .../orphan_pool/orphan_pool_storage.rs | 146 + .../core/src/mempool/pending_pool/error.rs | 5 +- .../src/mempool/pending_pool/pending_pool.rs | 139 +- .../pending_pool/pending_pool_storage.rs | 47 +- base_layer/core/src/mempool/priority/error.rs | 2 +- .../priority/prioritized_transaction.rs | 19 +- .../priority/timelocked_transaction.rs | 21 +- .../core/src/mempool/proto/mempool_request.rs | 69 + .../src/mempool/proto/mempool_response.rs | 75 + base_layer/core/src/mempool/proto/mod.rs | 34 + .../src/mempool/proto/service_request.proto | 16 + .../src/mempool/proto/service_response.proto | 16 + .../src/mempool/proto/stats_response.proto | 12 + .../core/src/mempool/proto/stats_response.rs | 52 + .../mempool/proto/tx_storage_response.proto | 14 + .../src/mempool/proto/tx_storage_response.rs | 64 +- .../core/src/mempool/reorg_pool}/error.rs | 11 +- base_layer/core/src/mempool/reorg_pool/mod.rs | 4 + .../core/src/mempool/reorg_pool/reorg_pool.rs | 304 +- .../mempool/reorg_pool/reorg_pool_storage.rs | 127 + .../src/mempool/service/error.rs} | 41 +- .../src/mempool/service/inbound_handlers.rs | 100 + .../core/src/mempool/service/initializer.rs | 197 + base_layer/core/src/mempool/service/mod.rs | 42 + .../src/mempool/service/outbound_interface.rs | 103 + .../core/src/mempool/service/request.rs | 36 +- .../src/mempool/service/response.rs} | 31 +- .../core/src/mempool/service/service.rs | 422 + .../src/mempool/unconfirmed_pool/error.rs | 5 +- .../unconfirmed_pool/unconfirmed_pool.rs | 177 +- .../unconfirmed_pool_storage.rs | 47 +- base_layer/core/src/mining/blake_miner.rs | 98 + .../core/src/mining/coinbase_builder.rs | 227 + base_layer/core/src/mining/error.rs | 36 + base_layer/core/src/mining/miner.rs | 296 + base_layer/core/src/mining/mod.rs | 29 + .../core/src/proof_of_work/blake_pow.rs | 120 +- .../diff_adj_manager/diff_adj_manager.rs | 103 + .../diff_adj_manager/diff_adj_storage.rs | 274 + .../proof_of_work/diff_adj_manager/error.rs | 32 + .../src/proof_of_work/diff_adj_manager/mod.rs | 28 + .../core/src/proof_of_work/difficulty.rs | 35 +- base_layer/core/src/proof_of_work/error.rs | 10 + .../core/src/proof_of_work/lwma_diff.rs | 236 + base_layer/core/src/proof_of_work/mod.rs | 19 +- .../core/src/proof_of_work/monero_rx.rs | 106 + .../core/src/proof_of_work/proof_of_work.rs | 287 + base_layer/core/src/proto/block.proto | 85 + base_layer/core/src/proto/block.rs | 246 + base_layer/core/src/proto/mod.rs | 35 + base_layer/core/src/proto/utils.rs | 37 + base_layer/core/src/test_utils/builders.rs | 143 - .../aggregated_body.rs | 114 +- .../{ => transactions}/bullet_rangeproofs.rs | 4 +- base_layer/core/src/{ => transactions}/fee.rs | 2 +- base_layer/core/src/transactions/helpers.rs | 374 + base_layer/core/src/transactions/mod.rs | 15 + .../core/src/transactions/proto/mod.rs | 12 +- .../src/transactions/proto/transaction.proto | 82 + .../src/transactions/proto/transaction.rs | 233 + .../core/src/transactions/proto/types.proto | 25 + .../src/transactions/proto/types_impls.rs | 96 + .../core/src/transactions/proto/utils.rs | 36 + .../src/{ => transactions}/tari_amount.rs | 28 +- .../src/{ => transactions}/transaction.rs | 449 +- .../transaction_protocol/mod.rs | 42 +- .../transaction_protocol/proto/mod.rs | 34 + .../proto/recipient_signed_message.proto | 14 + .../proto/recipient_signed_message.rs | 64 + .../proto/transaction_finalized.proto | 13 + .../proto/transaction_metadata.proto | 17 + .../proto/transaction_metadata.rs | 52 + .../proto/transaction_sender.proto | 31 + .../proto/transaction_sender.rs | 124 + .../transaction_protocol/recipient.rs | 64 +- .../transaction_protocol/sender.rs | 235 +- .../transaction_protocol/single_receiver.rs | 56 +- .../transaction_initializer.rs | 143 +- .../core/src/{ => transactions}/types.rs | 68 +- .../core/src/validation/block_validators.rs | 189 + .../pow.rs => validation/error.rs} | 36 +- base_layer/core/src/validation/helpers.rs | 126 + base_layer/core/src/validation/mocks.rs | 67 + base_layer/core/src/validation/mod.rs | 38 + base_layer/core/src/validation/traits.rs | 35 + .../src/validation/transaction_validators.rs | 194 + base_layer/core/tests/async_db.rs | 239 + base_layer/core/tests/block_validation.rs | 48 + base_layer/core/tests/chain/chain.json | 17497 ---------------- .../core/tests/chain_storage.rs | 5 +- .../chain_storage_tests/chain_backend.rs | 1161 + .../chain_storage_tests/chain_storage.rs | 872 + .../test => tests/chain_storage_tests}/mod.rs | 1 + base_layer/core/tests/diff_adj_manager.rs | 462 + .../core/tests/helpers/block_builders.rs | 387 + base_layer/core/tests/helpers/mod.rs | 8 + base_layer/core/tests/helpers/nodes.rs | 514 + .../core/tests/helpers/sample_blockchains.rs | 176 + base_layer/core/tests/mempool.rs | 859 + base_layer/core/tests/node_comms_interface.rs | 383 + base_layer/core/tests/node_service.rs | 698 + base_layer/core/tests/node_state_machine.rs | 506 + .../core/tests/support/simple_block_chain.rs | 480 - base_layer/key_manager/Cargo.toml | 19 + base_layer/key_manager/README.md | 1 + .../src/diacritics.rs | 2 +- .../src/file_backup.rs | 0 .../src/key_manager.rs} | 28 +- .../{keymanager => key_manager}/src/lib.rs | 2 +- .../src/mnemonic.rs | 60 +- .../src/mnemonic_wordlists.rs | 0 base_layer/keymanager/Cargo.toml | 16 - base_layer/mining/Cargo.toml | 5 - base_layer/mmr/Cargo.toml | 14 +- base_layer/mmr/benches/bench.rs | 6 +- base_layer/mmr/src/backend.rs | 53 +- base_layer/mmr/src/change_tracker.rs | 274 - base_layer/mmr/src/common.rs | 36 +- base_layer/mmr/src/error.rs | 9 +- .../mmr/src/{pruned_mmr.rs => functions.rs} | 47 + base_layer/mmr/src/lib.rs | 22 +- base_layer/mmr/src/mem_backend_vec.rs | 130 + base_layer/mmr/src/merkle_checkpoint.rs | 184 + base_layer/mmr/src/merkle_mountain_range.rs | 153 +- base_layer/mmr/src/merkle_proof.rs | 31 +- base_layer/mmr/src/mmr_cache.rs | 218 + base_layer/mmr/src/mutable_mmr.rs | 128 +- base_layer/mmr/src/mutable_mmr_leaf_nodes.rs | 155 + base_layer/mmr/src/pruned_hashset.rs | 38 +- base_layer/mmr/tests/change_tracker.rs | 151 - base_layer/mmr/tests/mem_backend_vec.rs | 53 + base_layer/mmr/tests/merkle_mountain_range.rs | 70 +- base_layer/mmr/tests/merkle_proof.rs | 19 +- base_layer/mmr/tests/mmr_cache.rs | 91 + base_layer/mmr/tests/mutable_mmr.rs | 78 +- base_layer/mmr/tests/pruned_mmr.rs | 73 +- base_layer/mmr/tests/support/mod.rs | 12 +- base_layer/mmr/tests/with_blake512_hash.rs | 6 +- base_layer/p2p/Cargo.toml | 61 +- base_layer/p2p/README.md | 1 + base_layer/p2p/build.rs | 31 + base_layer/p2p/examples/README.md | 5 +- .../p2p/examples/example-log-config.yml | 4 +- base_layer/p2p/examples/gen_node_identity.rs | 26 +- base_layer/p2p/examples/gen_tor_identity.rs | 98 + base_layer/p2p/examples/pingpong.rs | 335 +- base_layer/p2p/examples/pingpong_async.rs | 249 - .../sample_identities/node-identity1.json | 13 +- .../sample_identities/node-identity2.json | 13 +- .../src/comms_connector/inbound_connector.rs | 159 + .../p2p/src/comms_connector/mod.rs | 16 +- .../p2p/src/comms_connector/peer_message.rs | 60 + base_layer/p2p/src/comms_connector/pubsub.rs | 64 + base_layer/p2p/src/consts.rs | 33 - .../p2p/src/dht_service/dht_messages.rs | 64 - base_layer/p2p/src/dht_service/dht_service.rs | 455 - base_layer/p2p/src/dht_service/error.rs | 53 - base_layer/p2p/src/dht_service/mod.rs | 31 - base_layer/p2p/src/domain_message.rs | 109 + base_layer/p2p/src/initialization.rs | 255 +- base_layer/p2p/src/lib.rs | 15 +- base_layer/p2p/src/peer.rs | 2 +- base_layer/p2p/src/ping_pong.rs | 321 - base_layer/p2p/src/proto/liveness.proto | 30 + base_layer/p2p/src/proto/message_type.proto | 31 + base_layer/p2p/src/proto/mod.rs | 27 + base_layer/p2p/src/proto/tari.p2p.liveness.rs | 31 + .../p2p/src/proto/tari.p2p.message_type.rs | 23 + base_layer/p2p/src/saf_service/error.rs | 55 - base_layer/p2p/src/saf_service/mod.rs | 31 - .../p2p/src/saf_service/saf_messages.rs | 57 - base_layer/p2p/src/saf_service/saf_service.rs | 437 - .../mod.rs => comms_outbound.rs} | 43 +- .../p2p/src/services/comms_outbound/handle.rs | 226 - .../src/services/comms_outbound/service.rs | 224 - .../p2p/src/services/domain_deserializer.rs | 111 - .../p2p/src/services/liveness/config.rs | 47 + base_layer/p2p/src/services/liveness/error.rs | 17 +- .../p2p/src/services/liveness/handle.rs | 179 + .../p2p/src/services/liveness/handler.rs | 170 - .../p2p/src/services/liveness/message.rs | 52 + base_layer/p2p/src/services/liveness/mock.rs | 147 + base_layer/p2p/src/services/liveness/mod.rs | 217 +- .../p2p/src/services/liveness/neighbours.rs | 66 + .../p2p/src/services/liveness/service.rs | 639 +- base_layer/p2p/src/services/liveness/state.rs | 267 +- base_layer/p2p/src/services/mod.rs | 16 +- .../services/utils.rs} | 62 +- base_layer/p2p/src/sync_services/error.rs | 48 - base_layer/p2p/src/sync_services/executor.rs | 301 - base_layer/p2p/src/sync_services/mod.rs | 33 - base_layer/p2p/src/sync_services/registry.rs | 85 - base_layer/p2p/src/sync_services/service.rs | 76 - base_layer/p2p/src/tari_message.rs | 125 +- base_layer/p2p/src/test_utils.rs | 98 + base_layer/p2p/src/transport.rs | 55 + base_layer/p2p/tests/dht/mod.rs | 311 - base_layer/p2p/tests/mod.rs | 5 +- base_layer/p2p/tests/ping_pong/mod.rs | 140 - base_layer/p2p/tests/saf/mod.rs | 178 - base_layer/p2p/tests/services/liveness.rs | 212 +- .../p2p/tests/support/comms_and_services.rs | 66 + base_layer/p2p/tests/support/mod.rs | 34 +- base_layer/service_framework/Cargo.toml | 18 +- base_layer/service_framework/README.md | 1 + .../service_framework/src/handles/future.rs | 171 +- .../src/handles/lazy_service.rs | 79 +- .../service_framework/src/handles/mod.rs | 54 +- .../service_framework/src/initializer.rs | 161 + base_layer/service_framework/src/lib.rs | 84 +- .../service_framework/src/reply_channel.rs | 324 + base_layer/service_framework/src/stack.rs | 236 +- .../src/tower}/mod.rs | 7 +- .../src/tower/service_ext.rs | 161 + base_layer/service_framework/src/transport.rs | 439 - base_layer/wallet/Cargo.toml | 50 +- base_layer/wallet/README.md | 1 + .../2019-06-26-130555_initial/down.sql | 7 +- .../2019-06-26-130555_initial/up.sql | 47 +- .../down.sql | 3 + .../up.sql | 22 + .../down.sql | 4 + .../up.sql | 37 + .../2019-11-26-105357_contacts/down.sql | 1 + .../2019-11-26-105357_contacts/up.sql | 4 + .../2019-11-26-120903_peers/down.sql | 1 + .../migrations/2019-11-26-120903_peers/up.sql | 4 + .../wallet/src/contacts_service/error.rs | 57 + .../wallet/src/contacts_service/handle.rs | 90 + base_layer/wallet/src/contacts_service/mod.rs | 93 + .../wallet/src/contacts_service/service.rs | 115 + .../src/contacts_service/storage/database.rs | 183 + .../src/contacts_service/storage/memory_db.rs | 90 + .../src/contacts_service/storage/mod.rs} | 4 +- .../src/contacts_service/storage/sqlite_db.rs | 297 + base_layer/wallet/src/error.rs | 71 + base_layer/wallet/src/lib.rs | 23 +- base_layer/wallet/src/macros.rs | 17 +- .../src/output_manager_service/config.rs | 20 +- .../src/output_manager_service/error.rs | 65 +- .../src/output_manager_service/handle.rs | 274 + .../wallet/src/output_manager_service/mod.rs | 130 +- .../output_manager_service.rs | 676 - .../src/output_manager_service/service.rs | 722 + .../storage/database.rs | 508 + .../storage/memory_db.rs | 281 + .../output_manager_service/storage/mod.rs} | 4 +- .../storage/sqlite_db.rs | 1091 + base_layer/wallet/src/schema.rs | 98 +- .../wallet/src/storage/connection_manager.rs | 52 + base_layer/wallet/src/storage/database.rs | 273 + base_layer/wallet/src/storage/memory_db.rs | 97 + .../test_utils => wallet/src/storage}/mod.rs | 6 +- base_layer/wallet/src/storage/sqlite_db.rs | 179 + base_layer/wallet/src/testnet_utils.rs | 753 + .../wallet/src/text_message_service/error.rs | 29 +- .../wallet/src/text_message_service/handle.rs | 175 + .../wallet/src/text_message_service/mod.rs | 130 +- .../wallet/src/text_message_service/model.rs | 178 +- .../src/text_message_service/service.rs | 846 +- base_layer/wallet/src/transaction_manager.rs | 611 - base_layer/wallet/src/transaction_service.rs | 490 - .../wallet/src/transaction_service/config.rs | 38 + .../wallet/src/transaction_service/error.rs | 116 + .../wallet/src/transaction_service/handle.rs | 347 + .../wallet/src/transaction_service/mod.rs | 211 + .../wallet/src/transaction_service/service.rs | 1625 ++ .../transaction_service/storage/database.rs | 590 + .../transaction_service/storage/memory_db.rs | 319 + .../src/transaction_service/storage/mod.rs | 25 + .../transaction_service/storage/sqlite_db.rs | 1134 + base_layer/wallet/src/types.rs | 4 - base_layer/wallet/src/util/emoji.rs | 106 + base_layer/wallet/src/util/futures.rs | 74 + base_layer/wallet/src/util/luhn.rs | 75 + .../wallet/src/util}/mod.rs | 4 +- base_layer/wallet/src/wallet.rs | 329 +- .../wallet/tests/contacts_service/mod.rs | 187 + base_layer/wallet/tests/mod.rs | 5 +- .../tests/output_manager_service/mod.rs | 370 +- .../tests/output_manager_service/service.rs | 790 + .../tests/output_manager_service/storage.rs | 348 + .../tests/support/comms_and_services.rs | 102 +- base_layer/wallet/tests/support/data.rs | 16 - base_layer/wallet/tests/support/utils.rs | 28 +- .../wallet/tests/text_message_service/mod.rs | 290 +- .../wallet/tests/transaction_service/mod.rs | 292 +- .../tests/transaction_service/service.rs | 2081 ++ .../tests/transaction_service/storage.rs | 308 + base_layer/wallet/tests/wallet/mod.rs | 444 +- base_layer/wallet_ffi/Cargo.toml | 39 + base_layer/wallet_ffi/README.md | 115 + base_layer/wallet_ffi/build.sample.config | 8 + base_layer/wallet_ffi/cbindgen.toml | 96 + base_layer/wallet_ffi/mobile_build.sh | 300 + base_layer/wallet_ffi/src/callback_handler.rs | 259 + base_layer/wallet_ffi/src/error.rs | 333 + base_layer/wallet_ffi/src/lib.rs | 3842 ++++ base_layer/wallet_ffi/wallet.h | 405 + common/Cargo.toml | 12 +- common/logging/log4rs-sample.yml | 2 +- common/src/configuration.rs | 379 +- common/src/dir_utils.rs | 4 +- common/src/lib.rs | 51 +- common/src/logging.rs | 2 +- .../lib.rs => common/src/protobuf_build.rs | 5 +- comms/Cargo.toml | 46 +- comms/benches/benches_main.rs | 30 - comms/benches/connection/connection.rs | 58 - comms/benches/connection/mod.rs | 24 - comms/benches/connection/peer_connection.rs | 200 - comms/build.rs | 29 + comms/dht/Cargo.toml | 56 + comms/dht/README.md | 3 + comms/dht/build.rs | 33 + comms/dht/src/actor.rs | 603 + comms/dht/src/broadcast_strategy.rs | 163 + comms/dht/src/builder.rs | 118 + comms/dht/src/config.rs | 118 + comms/dht/src/consts.rs | 24 + comms/dht/src/crypt.rs | 72 + comms/dht/src/dht.rs | 462 + comms/dht/src/discovery/error.rs | 68 + comms/dht/src/discovery/mod.rs | 29 + comms/dht/src/discovery/requester.rs | 143 + comms/dht/src/discovery/service.rs | 402 + comms/dht/src/domain_message.rs | 65 + comms/dht/src/envelope.rs | 298 + comms/dht/src/inbound/decryption.rs | 260 + comms/dht/src/inbound/dedup.rs | 162 + comms/dht/src/inbound/deserialize.rs | 159 + comms/dht/src/inbound/dht_handler/layer.rs | 69 + .../dht/src/inbound/dht_handler/middleware.rs | 94 + .../dht/src/inbound/dht_handler/mod.rs | 8 +- comms/dht/src/inbound/dht_handler/task.rs | 401 + comms/dht/src/inbound/error.rs | 47 + comms/dht/src/inbound/message.rs | 106 + comms/dht/src/inbound/mod.rs | 38 + comms/dht/src/inbound/validate.rs | 207 + comms/dht/src/lib.rs | 144 + comms/dht/src/macros.rs | 45 + comms/dht/src/outbound/broadcast.rs | 628 + comms/dht/src/outbound/encryption.rs | 202 + .../src/outbound}/error.rs | 34 +- comms/dht/src/outbound/message.rs | 164 + comms/dht/src/outbound/message_params.rs | 170 + comms/dht/src/outbound/mock.rs | 191 + comms/dht/src/outbound/mod.rs | 42 + comms/dht/src/outbound/requester.rs | 225 + comms/dht/src/outbound/serialize.rs | 204 + comms/dht/src/proto/dht.proto | 47 + comms/dht/src/proto/envelope.proto | 62 + .../dht/src/proto/google.protobuf.rs | 0 comms/dht/src/proto/message_header.proto | 9 + comms/dht/src/proto/mod.rs | 33 + comms/dht/src/proto/store_forward.proto | 27 + comms/dht/src/proto/tari.dht.envelope.rs | 77 + .../dht/src/proto/tari.dht.message_header.rs | 8 + comms/dht/src/proto/tari.dht.rs | 58 + comms/dht/src/proto/tari.dht.store_forward.rs | 26 + comms/dht/src/store_forward/error.rs | 61 + comms/dht/src/store_forward/forward.rs | 256 + comms/dht/src/store_forward/message.rs | 71 + comms/dht/src/store_forward/mod.rs | 36 + .../src/store_forward/saf_handler/layer.rs | 73 + .../store_forward/saf_handler/middleware.rs | 96 + .../dht/src/store_forward/saf_handler/mod.rs | 27 + .../dht/src/store_forward/saf_handler/task.rs | 595 + comms/dht/src/store_forward/state.rs | 56 + comms/dht/src/store_forward/store.rs | 364 + comms/dht/src/test_utils/dht_actor_mock.rs | 111 + .../dht/src/test_utils/dht_discovery_mock.rs | 113 + comms/dht/src/test_utils/makers.rs | 148 + comms/dht/src/test_utils/mod.rs | 48 + comms/dht/src/test_utils/service.rs | 125 + comms/dht/src/tower_filter/error.rs | 46 + comms/dht/src/tower_filter/future.rs | 91 + comms/dht/src/tower_filter/layer.rs | 24 + comms/dht/src/tower_filter/mod.rs | 63 + comms/dht/src/tower_filter/predicate.rs | 25 + comms/dht/src/tower_filter/test.rs | 81 + comms/dht/src/utils.rs | 30 + comms/dht/tests/dht.rs | 300 + .../tari_util => comms/dht}/tests/mod.rs | 4 +- comms/examples/tor.rs | 275 + comms/src/backoff.rs | 109 + comms/src/bounded_executor.rs | 144 + comms/src/builder/builder.rs | 794 +- comms/src/builder/consts.rs | 44 + comms/src/builder/mod.rs | 57 +- comms/src/builder/placeholder.rs | 51 + comms/src/builder/routes.rs | 48 - comms/src/builder/shutdown.rs | 53 + comms/src/builder/tests.rs | 335 + comms/src/compat.rs | 93 + comms/src/connection/connection.rs | 579 - comms/src/connection/dealer_proxy.rs | 219 - comms/src/connection/error.rs | 65 - comms/src/connection/macros.rs | 36 - comms/src/connection/mod.rs | 98 - comms/src/connection/monitor.rs | 259 - comms/src/connection/net_address/i2p.rs | 84 - comms/src/connection/net_address/ip.rs | 126 - comms/src/connection/net_address/mod.rs | 202 - .../connection/net_address/net_addresses.rs | 382 - comms/src/connection/net_address/onion.rs | 111 - comms/src/connection/net_address/parser.rs | 211 - .../connection/peer_connection/connection.rs | 839 - .../src/connection/peer_connection/context.rs | 327 - .../src/connection/peer_connection/control.rs | 124 - comms/src/connection/peer_connection/error.rs | 50 - comms/src/connection/peer_connection/mod.rs | 95 - .../src/connection/peer_connection/worker.rs | 722 - comms/src/connection/types.rs | 89 - comms/src/connection/zmq/context.rs | 57 - comms/src/connection/zmq/curve_keypair.rs | 139 - comms/src/connection/zmq/endpoint.rs | 35 - comms/src/connection/zmq/inproc_address.rs | 103 - comms/src/connection/zmq/mod.rs | 36 - comms/src/connection_manager/common.rs | 147 + comms/src/connection_manager/connections.rs | 339 - comms/src/connection_manager/dial_state.rs | 72 + comms/src/connection_manager/dialer.rs | 545 + comms/src/connection_manager/error.rs | 146 +- comms/src/connection_manager/establisher.rs | 312 - comms/src/connection_manager/listener.rs | 269 + comms/src/connection_manager/manager.rs | 887 +- comms/src/connection_manager/mod.rs | 133 +- .../src/connection_manager/peer_connection.rs | 392 + comms/src/connection_manager/protocol.rs | 130 - comms/src/connection_manager/repository.rs | 218 - comms/src/connection_manager/requester.rs | 125 + .../tests/listener_dialer.rs | 194 + comms/src/connection_manager/tests/manager.rs | 334 + .../src/connection_manager}/tests/mod.rs | 5 +- comms/src/connection_manager/types.rs | 67 +- comms/src/consts.rs | 18 +- comms/src/control_service/client.rs | 198 - comms/src/control_service/error.rs | 62 - comms/src/control_service/messages.rs | 95 - comms/src/control_service/mod.rs | 106 - comms/src/control_service/service.rs | 146 - comms/src/control_service/types.rs | 32 - comms/src/control_service/worker.rs | 503 - comms/src/dispatcher/dispatcher.rs | 217 - comms/src/dispatcher/mod.rs | 32 - comms/src/domain_subscriber.rs | 197 - .../comms_msg_handlers.rs | 236 - comms/src/inbound_message_service/error.rs | 47 - .../inbound_message_publisher.rs | 83 - .../inbound_message_service.rs | 299 - .../inbound_message_worker.rs | 406 - .../inbound_message_service/message_cache.rs | 129 - comms/src/inbound_message_service/mod.rs | 53 - comms/src/lib.rs | 53 +- comms/src/macros.rs | 96 +- comms/src/memsocket/mod.rs | 689 + comms/src/message/envelope.rs | 557 +- comms/src/message/error.rs | 21 +- .../asserts.rs => src/message/inbound.rs} | 45 +- comms/src/message/message.rs | 124 - comms/src/message/message_context.rs | 76 - comms/src/message/message_data.rs | 102 - comms/src/message/mod.rs | 54 +- comms/src/message/outbound.rs | 86 + .../src/lib.rs => comms/src/message/tag.rs | 37 +- comms/src/multiplexing/mod.rs | 24 + comms/src/multiplexing/yamux.rs | 330 + comms/{tests => src/net_address}/mod.rs | 11 +- .../multiaddr_with_stats.rs} | 98 +- .../net_address/mutliaddresses_with_stats.rs | 370 + comms/src/noise/config.rs | 155 + comms/src/noise/crypto_resolver.rs | 157 + comms/src/noise/error.rs | 31 + comms/src/noise/mod.rs | 32 + comms/src/noise/socket.rs | 757 + .../broadcast_strategy.rs | 130 - comms/src/outbound_message_service/mod.rs | 68 - .../outbound_message_pool/error.rs | 46 - .../outbound_message_pool/mod.rs | 35 - .../outbound_message_pool/outbound_message.rs | 81 - .../outbound_message_pool/pool.rs | 360 - .../outbound_message_pool/retry_queue.rs | 702 - .../outbound_message_pool/worker.rs | 354 - .../outbound_message_service.rs | 293 - comms/src/peer_manager/async_peer_manager.rs | 177 + comms/src/peer_manager/connection_stats.rs | 167 + comms/src/peer_manager/error.rs | 42 +- comms/src/peer_manager/mod.rs | 52 +- comms/src/peer_manager/node_features.rs | 118 + comms/src/peer_manager/node_id.rs | 201 +- comms/src/peer_manager/node_identity.rs | 147 +- comms/src/peer_manager/peer.rs | 154 +- comms/src/peer_manager/peer_features.rs | 42 + .../peer_manager/{peer_key.rs => peer_id.rs} | 10 +- comms/src/peer_manager/peer_manager.rs | 430 +- comms/src/peer_manager/peer_query.rs | 344 + comms/src/peer_manager/peer_storage.rs | 823 +- comms/src/pipeline/builder.rs | 188 + comms/src/pipeline/inbound.rs | 108 + comms/src/pipeline/mod.rs | 45 + comms/src/pipeline/outbound.rs | 166 + comms/src/pipeline/sink.rs | 61 + comms/src/proto/control_service/header.proto | 17 + comms/src/proto/control_service/ping.proto | 6 + .../control_service/request_connection.proto | 35 + comms/src/proto/envelope.proto | 24 + comms/src/proto/identity.proto | 9 + comms/src/proto/mod.rs | 30 + comms/src/proto/tari.comms.control_service.rs | 61 + comms/src/proto/tari.comms.envelope.rs | 28 + comms/src/proto/tari.comms.identity.rs | 9 + comms/src/protocol/error.rs | 39 + comms/src/protocol/identity.rs | 176 + .../src/protocol/messaging/error.rs | 41 +- comms/src/protocol/messaging/inbound.rs | 74 + comms/src/protocol/messaging/messaging.rs | 480 + comms/src/protocol/messaging/mod.rs | 39 + comms/src/protocol/messaging/outbound.rs | 234 + comms/src/protocol/messaging/test.rs | 304 + comms/src/protocol/mod.rs | 37 + comms/src/protocol/negotiation.rs | 201 + comms/src/protocol/protocols.rs | 154 + comms/src/pub_sub_channel.rs | 200 - comms/src/socks/client.rs | 429 + .../blockchain => comms/src/socks}/error.rs | 76 +- comms/src/socks/mod.rs | 29 + .../src/test_utils/connection_manager_mock.rs | 152 + .../test_utils}/factories/macros.rs | 0 .../test_utils}/factories/mod.rs | 11 +- .../test_utils}/factories/net_address.rs | 48 +- .../test_utils}/factories/node_identity.rs | 40 +- .../test_utils}/factories/peer.rs | 47 +- .../test_utils}/factories/peer_manager.rs | 11 +- comms/src/test_utils/mod.rs | 36 + comms/src/test_utils/node_id.rs | 30 + .../src/test_utils/node_identity.rs | 30 +- comms/src/test_utils/peer_connection_mock.rs | 154 + .../src/test_utils/peer_manager.rs | 14 +- comms/src/test_utils/test_node.rs | 104 + comms/src/test_utils/transport.rs | 56 + comms/src/tor/client/client.rs | 490 + comms/src/tor/client/commands/add_onion.rs | 200 + comms/src/tor/client/commands/del_onion.rs | 65 + comms/src/tor/client/commands/key_value.rs | 112 + comms/src/tor/client/commands/mod.rs | 40 + comms/src/tor/client/error.rs | 56 + comms/src/tor/client/mod.rs | 38 + .../src/tor/client/parsers.rs | 91 +- comms/src/tor/client/response.rs | 65 + comms/src/tor/client/test_server.rs | 130 + comms/src/tor/client/types.rs | 131 + comms/src/tor/hidden_service/builder.rs | 224 + .../src/tor/hidden_service/hidden_service.rs | 116 + comms/src/tor/hidden_service/mod.rs | 27 + comms/src/tor/mod.rs | 37 + comms/src/transports/memory.rs | 186 + comms/src/transports/mod.rs | 58 + comms/src/transports/socks.rs | 115 + comms/src/transports/tcp.rs | 231 + comms/src/types.rs | 30 +- comms/src/utils.rs | 58 - .../tests/support => comms/src/utils}/mod.rs | 5 +- comms/src/utils/multiaddr.rs | 105 + .../src/utils/signature.rs | 59 +- comms/tests/connection/connection.rs | 194 - comms/tests/connection/mod.rs | 25 - comms/tests/connection/monitor.rs | 80 - comms/tests/connection/peer_connection.rs | 463 - comms/tests/connection_manager/establisher.rs | 329 - comms/tests/connection_manager/manager.rs | 212 - comms/tests/connection_manager/mod.rs | 24 - comms/tests/control_service/client.rs | 59 - comms/tests/control_service/mod.rs | 24 - comms/tests/control_service/service.rs | 215 - comms/tests/data/.gitkeep | 1 - comms/tests/outbound_message_service/mod.rs | 23 - .../outbound_message_pool.rs | 313 - comms/tests/support/comms_patterns.rs | 168 - .../support/factories/connection_manager.rs | 90 - .../support/factories/peer_connection.rs | 59 - .../factories/peer_connection_context.rs | 130 - .../helpers/connection_message_counter.rs | 120 - comms/tests/support/helpers/mod.rs | 27 - comms/tests/support/helpers/ports.rs | 44 - comms/tests/support/makers/comms_keys.rs | 33 - comms/tests/support/makers/mod.rs | 24 - comms/tests/support/makers/node_id.rs | 36 - comms/tests/support/mod.rs | 30 - config/tari_config_sample.toml | 180 +- digital_assets_layer/core/Cargo.toml | 5 - digital_assets_layer/core/src/lib.rs | 1 - doc/build.md | 26 - infrastructure/broadcast_channel/.gitignore | 5 - infrastructure/broadcast_channel/Cargo.toml | 32 - infrastructure/broadcast_channel/README.md | 47 - .../broadcast_channel/benches/channel.rs | 100 - .../examples/async-simple.rs | 18 - .../broadcast_channel/examples/raw-simple.rs | 12 - .../broadcast_channel/src/async_channel.rs | 176 - .../broadcast_channel/src/atomic_counter.rs | 50 - .../broadcast_channel/src/channel.rs | 286 - infrastructure/broadcast_channel/src/lib.rs | 97 - infrastructure/crypto/Cargo.toml | 44 - infrastructure/crypto/README.md | 13 - infrastructure/crypto/benches/range_proof.rs | 78 - infrastructure/crypto/benches/signatures.rs | 75 - infrastructure/crypto/src/commitment.rs | 146 - infrastructure/crypto/src/keys.rs | 82 - infrastructure/crypto/src/lib.rs | 18 - infrastructure/crypto/src/macros.rs | 118 - infrastructure/crypto/src/musig.rs | 276 - .../crypto/src/ristretto/constants.rs | 122 - .../crypto/src/ristretto/dalek_range_proof.rs | 165 - infrastructure/crypto/src/ristretto/musig.rs | 1089 - .../crypto/src/ristretto/pedersen.rs | 215 - .../crypto/src/ristretto/ristretto_keys.rs | 677 - .../crypto/src/ristretto/ristretto_sig.rs | 172 - .../crypto/src/ristretto/serialize.rs | 129 - infrastructure/crypto/src/signatures.rs | 161 - .../crypto/test_vectors/edDSA_testvectors | 128 - infrastructure/derive/Cargo.toml | 2 +- infrastructure/derive/src/extend_bytes.rs | 6 +- infrastructure/derive/src/hashable.rs | 21 +- .../src/merklemountainrange.rs | 0 infrastructure/protobuf_build/Cargo.toml | 16 - infrastructure/pubsub/Cargo.toml | 16 - infrastructure/pubsub/src/lib.rs | 201 - infrastructure/shutdown/Cargo.toml | 6 +- infrastructure/shutdown/README.md | 33 + infrastructure/shutdown/src/lib.rs | 10 +- infrastructure/storage/Cargo.toml | 5 +- .../src/key_val_store/lmdb_database.rs | 137 +- .../storage/src/lmdb_store/store.rs | 4 +- infrastructure/storage/tests/lmdb.rs | 215 +- infrastructure/tari_util/Cargo.toml | 24 - infrastructure/tari_util/README.md | 21 - infrastructure/tari_util/src/bit.rs | 79 - infrastructure/tari_util/src/byte_array.rs | 103 - .../tari_util/src/ciphers/chacha20.rs | 497 - .../tari_util/src/ciphers/cipher.rs | 53 - infrastructure/tari_util/src/ciphers/mod.rs | 24 - infrastructure/tari_util/src/epoch_time.rs | 118 - infrastructure/tari_util/src/extend_bytes.rs | 138 - infrastructure/tari_util/src/fixed_set.rs | 199 - infrastructure/tari_util/src/hex.rs | 127 - .../tari_util/src/message_format.rs | 216 - .../tari_util/src/thread_join/mod.rs | 26 - .../tari_util/src/thread_join/thread_join.rs | 104 - infrastructure/test_utils/Cargo.toml | 11 +- infrastructure/test_utils/README.md | 1 + infrastructure/test_utils/src/address.rs | 10 +- infrastructure/test_utils/src/enums.rs | 14 +- .../src/futures/async_assert_eventually.rs | 2 +- infrastructure/test_utils/src/futures/mod.rs | 2 +- infrastructure/test_utils/src/runtime.rs | 133 +- infrastructure/test_utils/src/streams/mod.rs | 41 +- rust-toolchain | 2 +- rustfmt.toml | 4 +- scripts/code_coverage_crypto.sh | 10 - scripts/code_coverage_merklemountainrange.sh | 10 - scripts/publish_crates.sh | 13 +- scripts/test_in_docker.sh | 14 +- scripts/update_crate_metadata.sh | 8 +- 782 files changed, 80057 insertions(+), 56121 deletions(-) delete mode 100644 .gitmodules create mode 100644 RFC/src/RFC-0152_EmojiId.md delete mode 100644 applications/cli_wallet/Cargo.toml delete mode 100644 applications/console_text_messenger/Cargo.toml delete mode 100644 applications/console_text_messenger/src/main.rs delete mode 100644 applications/grpc_wallet/Cargo.toml delete mode 100644 applications/grpc_wallet/proto/wallet_rpc.proto delete mode 100644 applications/grpc_wallet/sample_config/node1_peers.json delete mode 100644 applications/grpc_wallet/sample_config/node2_peers.json delete mode 100644 applications/grpc_wallet/sample_config/node3_peers.json delete mode 100644 applications/grpc_wallet/sample_config/wallet_config_node1.toml delete mode 100644 applications/grpc_wallet/sample_config/wallet_config_node2.toml delete mode 100644 applications/grpc_wallet/sample_config/wallet_config_node3.toml delete mode 100644 applications/grpc_wallet/src/grpc_interface.rs delete mode 100644 applications/grpc_wallet/src/main.rs delete mode 100644 applications/grpc_wallet/src/wallet_server.rs delete mode 100644 applications/grpc_wallet/tests/wallet_grpc_server/mod.rs delete mode 100644 applications/grpc_wallet/tests/wallet_grpc_server/wallet_grpc_server.rs create mode 100644 applications/tari_base_node/Cargo.toml create mode 100644 applications/tari_base_node/README.md create mode 100644 applications/tari_base_node/src/builder.rs create mode 100644 applications/tari_base_node/src/cli.rs create mode 100644 applications/tari_base_node/src/consts.rs create mode 100644 applications/tari_base_node/src/main.rs create mode 100644 applications/tari_base_node/src/miner.rs create mode 100644 applications/tari_base_node/src/parser.rs delete mode 100644 applications/tari_basenode/Cargo.toml delete mode 100644 applications/tari_miner/Cargo.toml delete mode 100644 applications/tari_pool_miner/Cargo.toml create mode 100644 applications/test_faucet/Cargo.toml create mode 100644 applications/test_faucet/src/main.rs create mode 100644 base_layer/core/build.rs create mode 100644 base_layer/core/src/base_node/backoff.rs create mode 100644 base_layer/core/src/base_node/chain_metadata_service/error.rs create mode 100644 base_layer/core/src/base_node/chain_metadata_service/handle.rs create mode 100644 base_layer/core/src/base_node/chain_metadata_service/initializer.rs create mode 100644 base_layer/core/src/base_node/chain_metadata_service/mod.rs create mode 100644 base_layer/core/src/base_node/chain_metadata_service/service.rs create mode 100644 base_layer/core/src/base_node/comms_interface/comms_request.rs create mode 100644 base_layer/core/src/base_node/comms_interface/comms_response.rs create mode 100644 base_layer/core/src/base_node/comms_interface/error.rs create mode 100644 base_layer/core/src/base_node/comms_interface/inbound_handlers.rs create mode 100644 base_layer/core/src/base_node/comms_interface/local_interface.rs create mode 100644 base_layer/core/src/base_node/comms_interface/mod.rs create mode 100644 base_layer/core/src/base_node/comms_interface/outbound_interface.rs rename infrastructure/crypto/benches/mod.rs => base_layer/core/src/base_node/consts.rs (78%) create mode 100644 base_layer/core/src/base_node/proto/chain_metadata.proto create mode 100644 base_layer/core/src/base_node/proto/chain_metadata.rs create mode 100644 base_layer/core/src/base_node/proto/mmr_tree.proto create mode 100644 base_layer/core/src/base_node/proto/mmr_tree.rs create mode 100644 base_layer/core/src/base_node/proto/mod.rs create mode 100644 base_layer/core/src/base_node/proto/request.proto create mode 100644 base_layer/core/src/base_node/proto/request.rs create mode 100644 base_layer/core/src/base_node/proto/response.proto create mode 100644 base_layer/core/src/base_node/proto/response.rs rename base_layer/{p2p/src/services/comms_outbound => core/src/base_node/service}/error.rs (79%) create mode 100644 base_layer/core/src/base_node/service/initializer.rs create mode 100644 base_layer/core/src/base_node/service/mod.rs create mode 100644 base_layer/core/src/base_node/service/service.rs rename comms/src/message/domain_message_context.rs => base_layer/core/src/base_node/service/service_request.rs (59%) rename infrastructure/crypto/src/ristretto/mod.rs => base_layer/core/src/base_node/service/service_response.rs (81%) create mode 100644 base_layer/core/src/base_node/states/block_sync.rs rename base_layer/{p2p/src/macros.rs => core/src/base_node/states/error.rs} (80%) create mode 100644 base_layer/core/src/base_node/states/helpers.rs create mode 100644 base_layer/core/src/base_node/states/initial_sync.rs create mode 100644 base_layer/core/src/base_node/states/listening.rs create mode 100644 base_layer/core/src/base_node/states/mod.rs create mode 100644 base_layer/core/src/base_node/states/shutdown_state.rs create mode 100644 base_layer/core/src/base_node/states/starting_state.rs delete mode 100644 base_layer/core/src/blockchain/chain.rs create mode 100644 base_layer/core/src/blocks/faucets/alphanet_faucet.json rename base_layer/core/src/{consensus.rs => blocks/new_block_template.rs} (58%) create mode 100644 base_layer/core/src/blocks/new_blockheader_template.rs create mode 100644 base_layer/core/src/chain_storage/async_db.rs create mode 100644 base_layer/core/src/chain_storage/lmdb_db/lmdb.rs create mode 100644 base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs create mode 100644 base_layer/core/src/chain_storage/lmdb_db/lmdb_vec.rs create mode 100644 base_layer/core/src/chain_storage/lmdb_db/mod.rs delete mode 100644 base_layer/core/src/chain_storage/memory_db.rs create mode 100644 base_layer/core/src/chain_storage/memory_db/mem_db_vec.rs create mode 100644 base_layer/core/src/chain_storage/memory_db/memory_db.rs create mode 100644 base_layer/core/src/chain_storage/memory_db/mod.rs delete mode 100644 base_layer/core/src/chain_storage/test/chain_storage.rs create mode 100644 base_layer/core/src/consensus/consensus_constants.rs create mode 100644 base_layer/core/src/consensus/consensus_manager.rs rename base_layer/core/src/{ => consensus}/emission.rs (96%) create mode 100644 base_layer/core/src/consensus/mod.rs create mode 100644 base_layer/core/src/consensus/network.rs delete mode 100644 base_layer/core/src/consts.rs create mode 100644 base_layer/core/src/helpers/mock_backend.rs rename infrastructure/crypto/src/range_proof.rs => base_layer/core/src/helpers/mod.rs (51%) create mode 100644 base_layer/core/src/mempool/config.rs create mode 100644 base_layer/core/src/mempool/consts.rs rename {infrastructure/tari_util/src/thread_join => base_layer/core/src/mempool/orphan_pool}/error.rs (82%) create mode 100644 base_layer/core/src/mempool/orphan_pool/orphan_pool_storage.rs create mode 100644 base_layer/core/src/mempool/proto/mempool_request.rs create mode 100644 base_layer/core/src/mempool/proto/mempool_response.rs create mode 100644 base_layer/core/src/mempool/proto/mod.rs create mode 100644 base_layer/core/src/mempool/proto/service_request.proto create mode 100644 base_layer/core/src/mempool/proto/service_response.proto create mode 100644 base_layer/core/src/mempool/proto/stats_response.proto create mode 100644 base_layer/core/src/mempool/proto/stats_response.rs create mode 100644 base_layer/core/src/mempool/proto/tx_storage_response.proto rename infrastructure/tari_util/src/locks.rs => base_layer/core/src/mempool/proto/tx_storage_response.rs (54%) rename {comms/src/connection/zmq => base_layer/core/src/mempool/reorg_pool}/error.rs (89%) create mode 100644 base_layer/core/src/mempool/reorg_pool/reorg_pool_storage.rs rename base_layer/{p2p/src/services/comms_outbound/messages.rs => core/src/mempool/service/error.rs} (68%) create mode 100644 base_layer/core/src/mempool/service/inbound_handlers.rs create mode 100644 base_layer/core/src/mempool/service/initializer.rs create mode 100644 base_layer/core/src/mempool/service/mod.rs create mode 100644 base_layer/core/src/mempool/service/outbound_interface.rs rename comms/tests/support/macros.rs => base_layer/core/src/mempool/service/request.rs (66%) rename base_layer/{p2p/src/services/liveness/messages.rs => core/src/mempool/service/response.rs} (76%) create mode 100644 base_layer/core/src/mempool/service/service.rs create mode 100644 base_layer/core/src/mining/blake_miner.rs create mode 100644 base_layer/core/src/mining/coinbase_builder.rs create mode 100644 base_layer/core/src/mining/error.rs create mode 100644 base_layer/core/src/mining/miner.rs create mode 100644 base_layer/core/src/mining/mod.rs create mode 100644 base_layer/core/src/proof_of_work/diff_adj_manager/diff_adj_manager.rs create mode 100644 base_layer/core/src/proof_of_work/diff_adj_manager/diff_adj_storage.rs create mode 100644 base_layer/core/src/proof_of_work/diff_adj_manager/error.rs create mode 100644 base_layer/core/src/proof_of_work/diff_adj_manager/mod.rs create mode 100644 base_layer/core/src/proof_of_work/lwma_diff.rs create mode 100644 base_layer/core/src/proof_of_work/monero_rx.rs create mode 100644 base_layer/core/src/proof_of_work/proof_of_work.rs create mode 100644 base_layer/core/src/proto/block.proto create mode 100644 base_layer/core/src/proto/block.rs create mode 100644 base_layer/core/src/proto/mod.rs create mode 100644 base_layer/core/src/proto/utils.rs delete mode 100644 base_layer/core/src/test_utils/builders.rs rename base_layer/core/src/{blocks => transactions}/aggregated_body.rs (66%) rename base_layer/core/src/{ => transactions}/bullet_rangeproofs.rs (97%) rename base_layer/core/src/{ => transactions}/fee.rs (97%) create mode 100644 base_layer/core/src/transactions/helpers.rs create mode 100644 base_layer/core/src/transactions/mod.rs rename infrastructure/tari_util/src/hash.rs => base_layer/core/src/transactions/proto/mod.rs (90%) create mode 100644 base_layer/core/src/transactions/proto/transaction.proto create mode 100644 base_layer/core/src/transactions/proto/transaction.rs create mode 100644 base_layer/core/src/transactions/proto/types.proto create mode 100644 base_layer/core/src/transactions/proto/types_impls.rs create mode 100644 base_layer/core/src/transactions/proto/utils.rs rename base_layer/core/src/{ => transactions}/tari_amount.rs (88%) rename base_layer/core/src/{ => transactions}/transaction.rs (59%) rename base_layer/core/src/{ => transactions}/transaction_protocol/mod.rs (73%) create mode 100644 base_layer/core/src/transactions/transaction_protocol/proto/mod.rs create mode 100644 base_layer/core/src/transactions/transaction_protocol/proto/recipient_signed_message.proto create mode 100644 base_layer/core/src/transactions/transaction_protocol/proto/recipient_signed_message.rs create mode 100644 base_layer/core/src/transactions/transaction_protocol/proto/transaction_finalized.proto create mode 100644 base_layer/core/src/transactions/transaction_protocol/proto/transaction_metadata.proto create mode 100644 base_layer/core/src/transactions/transaction_protocol/proto/transaction_metadata.rs create mode 100644 base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.proto create mode 100644 base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.rs rename base_layer/core/src/{ => transactions}/transaction_protocol/recipient.rs (84%) rename base_layer/core/src/{ => transactions}/transaction_protocol/sender.rs (76%) rename base_layer/core/src/{ => transactions}/transaction_protocol/single_receiver.rs (80%) rename base_layer/core/src/{ => transactions}/transaction_protocol/transaction_initializer.rs (83%) rename base_layer/core/src/{ => transactions}/types.rs (65%) create mode 100644 base_layer/core/src/validation/block_validators.rs rename base_layer/core/src/{proof_of_work/pow.rs => validation/error.rs} (63%) create mode 100644 base_layer/core/src/validation/helpers.rs create mode 100644 base_layer/core/src/validation/mocks.rs create mode 100644 base_layer/core/src/validation/mod.rs create mode 100644 base_layer/core/src/validation/traits.rs create mode 100644 base_layer/core/src/validation/transaction_validators.rs create mode 100644 base_layer/core/tests/async_db.rs create mode 100644 base_layer/core/tests/block_validation.rs delete mode 100644 base_layer/core/tests/chain/chain.json rename applications/grpc_wallet/src/lib.rs => base_layer/core/tests/chain_storage.rs (96%) create mode 100644 base_layer/core/tests/chain_storage_tests/chain_backend.rs create mode 100644 base_layer/core/tests/chain_storage_tests/chain_storage.rs rename base_layer/core/{src/chain_storage/test => tests/chain_storage_tests}/mod.rs (98%) create mode 100644 base_layer/core/tests/diff_adj_manager.rs create mode 100644 base_layer/core/tests/helpers/block_builders.rs create mode 100644 base_layer/core/tests/helpers/mod.rs create mode 100644 base_layer/core/tests/helpers/nodes.rs create mode 100644 base_layer/core/tests/helpers/sample_blockchains.rs create mode 100644 base_layer/core/tests/mempool.rs create mode 100644 base_layer/core/tests/node_comms_interface.rs create mode 100644 base_layer/core/tests/node_service.rs create mode 100644 base_layer/core/tests/node_state_machine.rs delete mode 100644 base_layer/core/tests/support/simple_block_chain.rs create mode 100644 base_layer/key_manager/Cargo.toml create mode 100644 base_layer/key_manager/README.md rename base_layer/{keymanager => key_manager}/src/diacritics.rs (99%) rename base_layer/{keymanager => key_manager}/src/file_backup.rs (100%) rename base_layer/{keymanager/src/keymanager.rs => key_manager/src/key_manager.rs} (94%) rename base_layer/{keymanager => key_manager}/src/lib.rs (80%) rename base_layer/{keymanager => key_manager}/src/mnemonic.rs (92%) rename base_layer/{keymanager => key_manager}/src/mnemonic_wordlists.rs (100%) delete mode 100644 base_layer/keymanager/Cargo.toml delete mode 100644 base_layer/mining/Cargo.toml delete mode 100644 base_layer/mmr/src/change_tracker.rs rename base_layer/mmr/src/{pruned_mmr.rs => functions.rs} (68%) create mode 100644 base_layer/mmr/src/mem_backend_vec.rs create mode 100644 base_layer/mmr/src/merkle_checkpoint.rs create mode 100644 base_layer/mmr/src/mmr_cache.rs create mode 100644 base_layer/mmr/src/mutable_mmr_leaf_nodes.rs delete mode 100644 base_layer/mmr/tests/change_tracker.rs create mode 100644 base_layer/mmr/tests/mem_backend_vec.rs create mode 100644 base_layer/mmr/tests/mmr_cache.rs create mode 100644 base_layer/p2p/README.md create mode 100644 base_layer/p2p/build.rs create mode 100644 base_layer/p2p/examples/gen_tor_identity.rs delete mode 100644 base_layer/p2p/examples/pingpong_async.rs create mode 100644 base_layer/p2p/src/comms_connector/inbound_connector.rs rename infrastructure/tari_util/src/protobuf.rs => base_layer/p2p/src/comms_connector/mod.rs (87%) create mode 100644 base_layer/p2p/src/comms_connector/peer_message.rs create mode 100644 base_layer/p2p/src/comms_connector/pubsub.rs delete mode 100644 base_layer/p2p/src/consts.rs delete mode 100644 base_layer/p2p/src/dht_service/dht_messages.rs delete mode 100644 base_layer/p2p/src/dht_service/dht_service.rs delete mode 100644 base_layer/p2p/src/dht_service/error.rs delete mode 100644 base_layer/p2p/src/dht_service/mod.rs create mode 100644 base_layer/p2p/src/domain_message.rs delete mode 100644 base_layer/p2p/src/ping_pong.rs create mode 100644 base_layer/p2p/src/proto/liveness.proto create mode 100644 base_layer/p2p/src/proto/message_type.proto create mode 100644 base_layer/p2p/src/proto/mod.rs create mode 100644 base_layer/p2p/src/proto/tari.p2p.liveness.rs create mode 100644 base_layer/p2p/src/proto/tari.p2p.message_type.rs delete mode 100644 base_layer/p2p/src/saf_service/error.rs delete mode 100644 base_layer/p2p/src/saf_service/mod.rs delete mode 100644 base_layer/p2p/src/saf_service/saf_messages.rs delete mode 100644 base_layer/p2p/src/saf_service/saf_service.rs rename base_layer/p2p/src/services/{comms_outbound/mod.rs => comms_outbound.rs} (59%) delete mode 100644 base_layer/p2p/src/services/comms_outbound/handle.rs delete mode 100644 base_layer/p2p/src/services/comms_outbound/service.rs delete mode 100644 base_layer/p2p/src/services/domain_deserializer.rs create mode 100644 base_layer/p2p/src/services/liveness/config.rs create mode 100644 base_layer/p2p/src/services/liveness/handle.rs delete mode 100644 base_layer/p2p/src/services/liveness/handler.rs create mode 100644 base_layer/p2p/src/services/liveness/message.rs create mode 100644 base_layer/p2p/src/services/liveness/mock.rs create mode 100644 base_layer/p2p/src/services/liveness/neighbours.rs rename base_layer/p2p/{tests/support/comms_outbound.rs => src/services/utils.rs} (56%) delete mode 100644 base_layer/p2p/src/sync_services/error.rs delete mode 100644 base_layer/p2p/src/sync_services/executor.rs delete mode 100644 base_layer/p2p/src/sync_services/mod.rs delete mode 100644 base_layer/p2p/src/sync_services/registry.rs delete mode 100644 base_layer/p2p/src/sync_services/service.rs create mode 100644 base_layer/p2p/src/test_utils.rs create mode 100644 base_layer/p2p/src/transport.rs delete mode 100644 base_layer/p2p/tests/dht/mod.rs delete mode 100644 base_layer/p2p/tests/ping_pong/mod.rs delete mode 100644 base_layer/p2p/tests/saf/mod.rs create mode 100644 base_layer/p2p/tests/support/comms_and_services.rs create mode 100644 base_layer/service_framework/README.md create mode 100644 base_layer/service_framework/src/initializer.rs create mode 100644 base_layer/service_framework/src/reply_channel.rs rename base_layer/{core/tests => service_framework/src/tower}/mod.rs (95%) create mode 100644 base_layer/service_framework/src/tower/service_ext.rs delete mode 100644 base_layer/service_framework/src/transport.rs create mode 100644 base_layer/wallet/README.md create mode 100644 base_layer/wallet/migrations/2019-10-30-084148_output_manager_service/down.sql create mode 100644 base_layer/wallet/migrations/2019-10-30-084148_output_manager_service/up.sql create mode 100644 base_layer/wallet/migrations/2019-11-20-090620_transaction_service/down.sql create mode 100644 base_layer/wallet/migrations/2019-11-20-090620_transaction_service/up.sql create mode 100644 base_layer/wallet/migrations/2019-11-26-105357_contacts/down.sql create mode 100644 base_layer/wallet/migrations/2019-11-26-105357_contacts/up.sql create mode 100644 base_layer/wallet/migrations/2019-11-26-120903_peers/down.sql create mode 100644 base_layer/wallet/migrations/2019-11-26-120903_peers/up.sql create mode 100644 base_layer/wallet/src/contacts_service/error.rs create mode 100644 base_layer/wallet/src/contacts_service/handle.rs create mode 100644 base_layer/wallet/src/contacts_service/mod.rs create mode 100644 base_layer/wallet/src/contacts_service/service.rs create mode 100644 base_layer/wallet/src/contacts_service/storage/database.rs create mode 100644 base_layer/wallet/src/contacts_service/storage/memory_db.rs rename base_layer/{core/src/base_node/block_validation_service.rs => wallet/src/contacts_service/storage/mod.rs} (96%) create mode 100644 base_layer/wallet/src/contacts_service/storage/sqlite_db.rs create mode 100644 base_layer/wallet/src/error.rs rename applications/grpc_wallet/build.rs => base_layer/wallet/src/output_manager_service/config.rs (83%) create mode 100644 base_layer/wallet/src/output_manager_service/handle.rs delete mode 100644 base_layer/wallet/src/output_manager_service/output_manager_service.rs create mode 100644 base_layer/wallet/src/output_manager_service/service.rs create mode 100644 base_layer/wallet/src/output_manager_service/storage/database.rs create mode 100644 base_layer/wallet/src/output_manager_service/storage/memory_db.rs rename base_layer/{core/src/base_node/transaction_validation_service.rs => wallet/src/output_manager_service/storage/mod.rs} (96%) create mode 100644 base_layer/wallet/src/output_manager_service/storage/sqlite_db.rs create mode 100644 base_layer/wallet/src/storage/connection_manager.rs create mode 100644 base_layer/wallet/src/storage/database.rs create mode 100644 base_layer/wallet/src/storage/memory_db.rs rename base_layer/{core/src/test_utils => wallet/src/storage}/mod.rs (94%) create mode 100644 base_layer/wallet/src/storage/sqlite_db.rs create mode 100644 base_layer/wallet/src/testnet_utils.rs create mode 100644 base_layer/wallet/src/text_message_service/handle.rs delete mode 100644 base_layer/wallet/src/transaction_manager.rs delete mode 100644 base_layer/wallet/src/transaction_service.rs create mode 100644 base_layer/wallet/src/transaction_service/config.rs create mode 100644 base_layer/wallet/src/transaction_service/error.rs create mode 100644 base_layer/wallet/src/transaction_service/handle.rs create mode 100644 base_layer/wallet/src/transaction_service/mod.rs create mode 100644 base_layer/wallet/src/transaction_service/service.rs create mode 100644 base_layer/wallet/src/transaction_service/storage/database.rs create mode 100644 base_layer/wallet/src/transaction_service/storage/memory_db.rs create mode 100644 base_layer/wallet/src/transaction_service/storage/mod.rs create mode 100644 base_layer/wallet/src/transaction_service/storage/sqlite_db.rs create mode 100644 base_layer/wallet/src/util/emoji.rs create mode 100644 base_layer/wallet/src/util/futures.rs create mode 100644 base_layer/wallet/src/util/luhn.rs rename {applications/grpc_wallet/tests => base_layer/wallet/src/util}/mod.rs (97%) create mode 100644 base_layer/wallet/tests/contacts_service/mod.rs create mode 100644 base_layer/wallet/tests/output_manager_service/service.rs create mode 100644 base_layer/wallet/tests/output_manager_service/storage.rs create mode 100644 base_layer/wallet/tests/transaction_service/service.rs create mode 100644 base_layer/wallet/tests/transaction_service/storage.rs create mode 100644 base_layer/wallet_ffi/Cargo.toml create mode 100644 base_layer/wallet_ffi/README.md create mode 100644 base_layer/wallet_ffi/build.sample.config create mode 100644 base_layer/wallet_ffi/cbindgen.toml create mode 100644 base_layer/wallet_ffi/mobile_build.sh create mode 100644 base_layer/wallet_ffi/src/callback_handler.rs create mode 100644 base_layer/wallet_ffi/src/error.rs create mode 100644 base_layer/wallet_ffi/src/lib.rs create mode 100644 base_layer/wallet_ffi/wallet.h rename infrastructure/protobuf_build/src/lib.rs => common/src/protobuf_build.rs (96%) delete mode 100644 comms/benches/benches_main.rs delete mode 100644 comms/benches/connection/connection.rs delete mode 100644 comms/benches/connection/mod.rs delete mode 100644 comms/benches/connection/peer_connection.rs create mode 100644 comms/build.rs create mode 100644 comms/dht/Cargo.toml create mode 100644 comms/dht/README.md create mode 100644 comms/dht/build.rs create mode 100644 comms/dht/src/actor.rs create mode 100644 comms/dht/src/broadcast_strategy.rs create mode 100644 comms/dht/src/builder.rs create mode 100644 comms/dht/src/config.rs create mode 100644 comms/dht/src/consts.rs create mode 100644 comms/dht/src/crypt.rs create mode 100644 comms/dht/src/dht.rs create mode 100644 comms/dht/src/discovery/error.rs create mode 100644 comms/dht/src/discovery/mod.rs create mode 100644 comms/dht/src/discovery/requester.rs create mode 100644 comms/dht/src/discovery/service.rs create mode 100644 comms/dht/src/domain_message.rs create mode 100644 comms/dht/src/envelope.rs create mode 100644 comms/dht/src/inbound/decryption.rs create mode 100644 comms/dht/src/inbound/dedup.rs create mode 100644 comms/dht/src/inbound/deserialize.rs create mode 100644 comms/dht/src/inbound/dht_handler/layer.rs create mode 100644 comms/dht/src/inbound/dht_handler/middleware.rs rename infrastructure/broadcast_channel/benches/benches_main.rs => comms/dht/src/inbound/dht_handler/mod.rs (95%) create mode 100644 comms/dht/src/inbound/dht_handler/task.rs create mode 100644 comms/dht/src/inbound/error.rs create mode 100644 comms/dht/src/inbound/message.rs create mode 100644 comms/dht/src/inbound/mod.rs create mode 100644 comms/dht/src/inbound/validate.rs create mode 100644 comms/dht/src/lib.rs create mode 100644 comms/dht/src/macros.rs create mode 100644 comms/dht/src/outbound/broadcast.rs create mode 100644 comms/dht/src/outbound/encryption.rs rename comms/{src/outbound_message_service => dht/src/outbound}/error.rs (68%) create mode 100644 comms/dht/src/outbound/message.rs create mode 100644 comms/dht/src/outbound/message_params.rs create mode 100644 comms/dht/src/outbound/mock.rs create mode 100644 comms/dht/src/outbound/mod.rs create mode 100644 comms/dht/src/outbound/requester.rs create mode 100644 comms/dht/src/outbound/serialize.rs create mode 100644 comms/dht/src/proto/dht.proto create mode 100644 comms/dht/src/proto/envelope.proto rename base_layer/mining/src/lib.rs => comms/dht/src/proto/google.protobuf.rs (100%) create mode 100644 comms/dht/src/proto/message_header.proto create mode 100644 comms/dht/src/proto/mod.rs create mode 100644 comms/dht/src/proto/store_forward.proto create mode 100644 comms/dht/src/proto/tari.dht.envelope.rs create mode 100644 comms/dht/src/proto/tari.dht.message_header.rs create mode 100644 comms/dht/src/proto/tari.dht.rs create mode 100644 comms/dht/src/proto/tari.dht.store_forward.rs create mode 100644 comms/dht/src/store_forward/error.rs create mode 100644 comms/dht/src/store_forward/forward.rs create mode 100644 comms/dht/src/store_forward/message.rs create mode 100644 comms/dht/src/store_forward/mod.rs create mode 100644 comms/dht/src/store_forward/saf_handler/layer.rs create mode 100644 comms/dht/src/store_forward/saf_handler/middleware.rs create mode 100644 comms/dht/src/store_forward/saf_handler/mod.rs create mode 100644 comms/dht/src/store_forward/saf_handler/task.rs create mode 100644 comms/dht/src/store_forward/state.rs create mode 100644 comms/dht/src/store_forward/store.rs create mode 100644 comms/dht/src/test_utils/dht_actor_mock.rs create mode 100644 comms/dht/src/test_utils/dht_discovery_mock.rs create mode 100644 comms/dht/src/test_utils/makers.rs create mode 100644 comms/dht/src/test_utils/mod.rs create mode 100644 comms/dht/src/test_utils/service.rs create mode 100644 comms/dht/src/tower_filter/error.rs create mode 100644 comms/dht/src/tower_filter/future.rs create mode 100644 comms/dht/src/tower_filter/layer.rs create mode 100644 comms/dht/src/tower_filter/mod.rs create mode 100644 comms/dht/src/tower_filter/predicate.rs create mode 100644 comms/dht/src/tower_filter/test.rs create mode 100644 comms/dht/src/utils.rs create mode 100644 comms/dht/tests/dht.rs rename {infrastructure/tari_util => comms/dht}/tests/mod.rs (96%) create mode 100644 comms/examples/tor.rs create mode 100644 comms/src/backoff.rs create mode 100644 comms/src/bounded_executor.rs create mode 100644 comms/src/builder/consts.rs create mode 100644 comms/src/builder/placeholder.rs delete mode 100644 comms/src/builder/routes.rs create mode 100644 comms/src/builder/shutdown.rs create mode 100644 comms/src/builder/tests.rs create mode 100644 comms/src/compat.rs delete mode 100644 comms/src/connection/connection.rs delete mode 100644 comms/src/connection/dealer_proxy.rs delete mode 100644 comms/src/connection/error.rs delete mode 100644 comms/src/connection/macros.rs delete mode 100644 comms/src/connection/mod.rs delete mode 100644 comms/src/connection/monitor.rs delete mode 100644 comms/src/connection/net_address/i2p.rs delete mode 100644 comms/src/connection/net_address/ip.rs delete mode 100644 comms/src/connection/net_address/mod.rs delete mode 100644 comms/src/connection/net_address/net_addresses.rs delete mode 100644 comms/src/connection/net_address/onion.rs delete mode 100644 comms/src/connection/net_address/parser.rs delete mode 100644 comms/src/connection/peer_connection/connection.rs delete mode 100644 comms/src/connection/peer_connection/context.rs delete mode 100644 comms/src/connection/peer_connection/control.rs delete mode 100644 comms/src/connection/peer_connection/error.rs delete mode 100644 comms/src/connection/peer_connection/mod.rs delete mode 100644 comms/src/connection/peer_connection/worker.rs delete mode 100644 comms/src/connection/types.rs delete mode 100644 comms/src/connection/zmq/context.rs delete mode 100644 comms/src/connection/zmq/curve_keypair.rs delete mode 100644 comms/src/connection/zmq/endpoint.rs delete mode 100644 comms/src/connection/zmq/inproc_address.rs delete mode 100644 comms/src/connection/zmq/mod.rs create mode 100644 comms/src/connection_manager/common.rs delete mode 100644 comms/src/connection_manager/connections.rs create mode 100644 comms/src/connection_manager/dial_state.rs create mode 100644 comms/src/connection_manager/dialer.rs delete mode 100644 comms/src/connection_manager/establisher.rs create mode 100644 comms/src/connection_manager/listener.rs create mode 100644 comms/src/connection_manager/peer_connection.rs delete mode 100644 comms/src/connection_manager/protocol.rs delete mode 100644 comms/src/connection_manager/repository.rs create mode 100644 comms/src/connection_manager/requester.rs create mode 100644 comms/src/connection_manager/tests/listener_dialer.rs create mode 100644 comms/src/connection_manager/tests/manager.rs rename {base_layer/core/tests => comms/src/connection_manager}/tests/mod.rs (95%) delete mode 100644 comms/src/control_service/client.rs delete mode 100644 comms/src/control_service/error.rs delete mode 100644 comms/src/control_service/messages.rs delete mode 100644 comms/src/control_service/mod.rs delete mode 100644 comms/src/control_service/service.rs delete mode 100644 comms/src/control_service/types.rs delete mode 100644 comms/src/control_service/worker.rs delete mode 100644 comms/src/dispatcher/dispatcher.rs delete mode 100644 comms/src/dispatcher/mod.rs delete mode 100644 comms/src/domain_subscriber.rs delete mode 100644 comms/src/inbound_message_service/comms_msg_handlers.rs delete mode 100644 comms/src/inbound_message_service/error.rs delete mode 100644 comms/src/inbound_message_service/inbound_message_publisher.rs delete mode 100644 comms/src/inbound_message_service/inbound_message_service.rs delete mode 100644 comms/src/inbound_message_service/inbound_message_worker.rs delete mode 100644 comms/src/inbound_message_service/message_cache.rs delete mode 100644 comms/src/inbound_message_service/mod.rs create mode 100644 comms/src/memsocket/mod.rs rename comms/{tests/support/helpers/asserts.rs => src/message/inbound.rs} (67%) delete mode 100644 comms/src/message/message.rs delete mode 100644 comms/src/message/message_context.rs delete mode 100644 comms/src/message/message_data.rs create mode 100644 comms/src/message/outbound.rs rename infrastructure/tari_util/src/lib.rs => comms/src/message/tag.rs (77%) create mode 100644 comms/src/multiplexing/mod.rs create mode 100644 comms/src/multiplexing/yamux.rs rename comms/{tests => src/net_address}/mod.rs (90%) rename comms/src/{connection/net_address/net_address_with_stats.rs => net_address/multiaddr_with_stats.rs} (77%) create mode 100644 comms/src/net_address/mutliaddresses_with_stats.rs create mode 100644 comms/src/noise/config.rs create mode 100644 comms/src/noise/crypto_resolver.rs create mode 100644 comms/src/noise/error.rs create mode 100644 comms/src/noise/mod.rs create mode 100644 comms/src/noise/socket.rs delete mode 100644 comms/src/outbound_message_service/broadcast_strategy.rs delete mode 100644 comms/src/outbound_message_service/mod.rs delete mode 100644 comms/src/outbound_message_service/outbound_message_pool/error.rs delete mode 100644 comms/src/outbound_message_service/outbound_message_pool/mod.rs delete mode 100644 comms/src/outbound_message_service/outbound_message_pool/outbound_message.rs delete mode 100644 comms/src/outbound_message_service/outbound_message_pool/pool.rs delete mode 100644 comms/src/outbound_message_service/outbound_message_pool/retry_queue.rs delete mode 100644 comms/src/outbound_message_service/outbound_message_pool/worker.rs delete mode 100644 comms/src/outbound_message_service/outbound_message_service.rs create mode 100644 comms/src/peer_manager/async_peer_manager.rs create mode 100644 comms/src/peer_manager/connection_stats.rs create mode 100644 comms/src/peer_manager/node_features.rs create mode 100644 comms/src/peer_manager/peer_features.rs rename comms/src/peer_manager/{peer_key.rs => peer_id.rs} (88%) create mode 100644 comms/src/peer_manager/peer_query.rs create mode 100644 comms/src/pipeline/builder.rs create mode 100644 comms/src/pipeline/inbound.rs create mode 100644 comms/src/pipeline/mod.rs create mode 100644 comms/src/pipeline/outbound.rs create mode 100644 comms/src/pipeline/sink.rs create mode 100644 comms/src/proto/control_service/header.proto create mode 100644 comms/src/proto/control_service/ping.proto create mode 100644 comms/src/proto/control_service/request_connection.proto create mode 100644 comms/src/proto/envelope.proto create mode 100644 comms/src/proto/identity.proto create mode 100644 comms/src/proto/mod.rs create mode 100644 comms/src/proto/tari.comms.control_service.rs create mode 100644 comms/src/proto/tari.comms.envelope.rs create mode 100644 comms/src/proto/tari.comms.identity.rs create mode 100644 comms/src/protocol/error.rs create mode 100644 comms/src/protocol/identity.rs rename infrastructure/crypto/src/ristretto/test_common.rs => comms/src/protocol/messaging/error.rs (57%) create mode 100644 comms/src/protocol/messaging/inbound.rs create mode 100644 comms/src/protocol/messaging/messaging.rs create mode 100644 comms/src/protocol/messaging/mod.rs create mode 100644 comms/src/protocol/messaging/outbound.rs create mode 100644 comms/src/protocol/messaging/test.rs create mode 100644 comms/src/protocol/mod.rs create mode 100644 comms/src/protocol/negotiation.rs create mode 100644 comms/src/protocol/protocols.rs delete mode 100644 comms/src/pub_sub_channel.rs create mode 100644 comms/src/socks/client.rs rename {base_layer/core/src/blockchain => comms/src/socks}/error.rs (53%) create mode 100644 comms/src/socks/mod.rs create mode 100644 comms/src/test_utils/connection_manager_mock.rs rename comms/{tests/support => src/test_utils}/factories/macros.rs (100%) rename comms/{tests/support => src/test_utils}/factories/mod.rs (94%) rename comms/{tests/support => src/test_utils}/factories/net_address.rs (78%) rename comms/{tests/support => src/test_utils}/factories/node_identity.rs (70%) rename comms/{tests/support => src/test_utils}/factories/peer.rs (80%) rename comms/{tests/support => src/test_utils}/factories/peer_manager.rs (90%) create mode 100644 comms/src/test_utils/mod.rs create mode 100644 comms/src/test_utils/node_id.rs rename base_layer/core/tests/tests/block_validation.rs => comms/src/test_utils/node_identity.rs (70%) create mode 100644 comms/src/test_utils/peer_connection_mock.rs rename base_layer/p2p/src/services/service_name.rs => comms/src/test_utils/peer_manager.rs (86%) create mode 100644 comms/src/test_utils/test_node.rs create mode 100644 comms/src/test_utils/transport.rs create mode 100644 comms/src/tor/client/client.rs create mode 100644 comms/src/tor/client/commands/add_onion.rs create mode 100644 comms/src/tor/client/commands/del_onion.rs create mode 100644 comms/src/tor/client/commands/key_value.rs create mode 100644 comms/src/tor/client/commands/mod.rs create mode 100644 comms/src/tor/client/error.rs create mode 100644 comms/src/tor/client/mod.rs rename infrastructure/crypto/src/common.rs => comms/src/tor/client/parsers.rs (50%) create mode 100644 comms/src/tor/client/response.rs create mode 100644 comms/src/tor/client/test_server.rs create mode 100644 comms/src/tor/client/types.rs create mode 100644 comms/src/tor/hidden_service/builder.rs create mode 100644 comms/src/tor/hidden_service/hidden_service.rs create mode 100644 comms/src/tor/hidden_service/mod.rs create mode 100644 comms/src/tor/mod.rs create mode 100644 comms/src/transports/memory.rs create mode 100644 comms/src/transports/mod.rs create mode 100644 comms/src/transports/socks.rs create mode 100644 comms/src/transports/tcp.rs delete mode 100644 comms/src/utils.rs rename {base_layer/core/tests/support => comms/src/utils}/mod.rs (95%) create mode 100644 comms/src/utils/multiaddr.rs rename base_layer/core/src/transaction_protocol/test_common.rs => comms/src/utils/signature.rs (56%) delete mode 100644 comms/tests/connection/connection.rs delete mode 100644 comms/tests/connection/mod.rs delete mode 100644 comms/tests/connection/monitor.rs delete mode 100644 comms/tests/connection/peer_connection.rs delete mode 100644 comms/tests/connection_manager/establisher.rs delete mode 100644 comms/tests/connection_manager/manager.rs delete mode 100644 comms/tests/connection_manager/mod.rs delete mode 100644 comms/tests/control_service/client.rs delete mode 100644 comms/tests/control_service/mod.rs delete mode 100644 comms/tests/control_service/service.rs delete mode 100644 comms/tests/data/.gitkeep delete mode 100644 comms/tests/outbound_message_service/mod.rs delete mode 100644 comms/tests/outbound_message_service/outbound_message_pool.rs delete mode 100644 comms/tests/support/comms_patterns.rs delete mode 100644 comms/tests/support/factories/connection_manager.rs delete mode 100644 comms/tests/support/factories/peer_connection.rs delete mode 100644 comms/tests/support/factories/peer_connection_context.rs delete mode 100644 comms/tests/support/helpers/connection_message_counter.rs delete mode 100644 comms/tests/support/helpers/mod.rs delete mode 100644 comms/tests/support/helpers/ports.rs delete mode 100644 comms/tests/support/makers/comms_keys.rs delete mode 100644 comms/tests/support/makers/mod.rs delete mode 100644 comms/tests/support/makers/node_id.rs delete mode 100644 comms/tests/support/mod.rs delete mode 100644 digital_assets_layer/core/Cargo.toml delete mode 100644 digital_assets_layer/core/src/lib.rs delete mode 100644 doc/build.md delete mode 100644 infrastructure/broadcast_channel/.gitignore delete mode 100644 infrastructure/broadcast_channel/Cargo.toml delete mode 100644 infrastructure/broadcast_channel/README.md delete mode 100644 infrastructure/broadcast_channel/benches/channel.rs delete mode 100644 infrastructure/broadcast_channel/examples/async-simple.rs delete mode 100644 infrastructure/broadcast_channel/examples/raw-simple.rs delete mode 100644 infrastructure/broadcast_channel/src/async_channel.rs delete mode 100644 infrastructure/broadcast_channel/src/atomic_counter.rs delete mode 100644 infrastructure/broadcast_channel/src/channel.rs delete mode 100644 infrastructure/broadcast_channel/src/lib.rs delete mode 100644 infrastructure/crypto/Cargo.toml delete mode 100644 infrastructure/crypto/README.md delete mode 100644 infrastructure/crypto/benches/range_proof.rs delete mode 100644 infrastructure/crypto/benches/signatures.rs delete mode 100644 infrastructure/crypto/src/commitment.rs delete mode 100644 infrastructure/crypto/src/keys.rs delete mode 100644 infrastructure/crypto/src/lib.rs delete mode 100644 infrastructure/crypto/src/macros.rs delete mode 100644 infrastructure/crypto/src/musig.rs delete mode 100644 infrastructure/crypto/src/ristretto/constants.rs delete mode 100644 infrastructure/crypto/src/ristretto/dalek_range_proof.rs delete mode 100644 infrastructure/crypto/src/ristretto/musig.rs delete mode 100644 infrastructure/crypto/src/ristretto/pedersen.rs delete mode 100644 infrastructure/crypto/src/ristretto/ristretto_keys.rs delete mode 100644 infrastructure/crypto/src/ristretto/ristretto_sig.rs delete mode 100644 infrastructure/crypto/src/ristretto/serialize.rs delete mode 100644 infrastructure/crypto/src/signatures.rs delete mode 100644 infrastructure/crypto/test_vectors/edDSA_testvectors delete mode 100644 infrastructure/merklemountainrange/src/merklemountainrange.rs delete mode 100644 infrastructure/protobuf_build/Cargo.toml delete mode 100644 infrastructure/pubsub/Cargo.toml delete mode 100644 infrastructure/pubsub/src/lib.rs create mode 100644 infrastructure/shutdown/README.md delete mode 100644 infrastructure/tari_util/Cargo.toml delete mode 100644 infrastructure/tari_util/README.md delete mode 100644 infrastructure/tari_util/src/bit.rs delete mode 100644 infrastructure/tari_util/src/byte_array.rs delete mode 100644 infrastructure/tari_util/src/ciphers/chacha20.rs delete mode 100644 infrastructure/tari_util/src/ciphers/cipher.rs delete mode 100644 infrastructure/tari_util/src/ciphers/mod.rs delete mode 100644 infrastructure/tari_util/src/epoch_time.rs delete mode 100644 infrastructure/tari_util/src/extend_bytes.rs delete mode 100644 infrastructure/tari_util/src/fixed_set.rs delete mode 100644 infrastructure/tari_util/src/hex.rs delete mode 100644 infrastructure/tari_util/src/message_format.rs delete mode 100644 infrastructure/tari_util/src/thread_join/mod.rs delete mode 100644 infrastructure/tari_util/src/thread_join/thread_join.rs create mode 100644 infrastructure/test_utils/README.md delete mode 100755 scripts/code_coverage_crypto.sh delete mode 100755 scripts/code_coverage_merklemountainrange.sh diff --git a/.circleci/config.yml b/.circleci/config.yml index 6ccd535dd8..abaf3d723b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,7 +1,42 @@ version: 2.1 defaults: - rust_image: &rust_image quay.io/tarilabs/rust_tari-build-with-deps:nightly-2019-10-04 + rust_image: &rust_image quay.io/tarilabs/rust_tari-build-with-deps:nightly-2020-01-08 + +commands: + test: + description: Run the tests + parameters: + release: + description: Set this to true to compile in release mode. + type: boolean + default: false + steps: + - run: + name: Calculate dependencies + command: | + rustc --version >rust-version + test -e Cargo.lock || cargo generate-lockfile + - restore_cache: + keys: + - v6-cargo-cache-{{arch}}-{{checksum "rust-version"}}-<>-{{checksum "Cargo.lock"}} + - run: + name: Build the project + command: cargo build --all --all-features --jobs=3 <<#parameters.release>>--release<> + - run: + name: Cargo fmt (ignored in release mode) + command: | + TOOLCHAIN=$(cat rust-toolchain) + <<#parameters.release>>#<> rustup component add --toolchain $TOOLCHAIN rustfmt + <<#parameters.release>>#<> cargo fmt --all -- --check + - run: + name: Run tests + command: cargo test --workspace --all-features --jobs=3 <<#parameters.release>>--release<> + - save_cache: + paths: + - /usr/local/cargo/registry + - target + key: v6-cargo-cache-{{arch}}-{{checksum "rust-version"}}-<>-{{checksum "Cargo.lock"}} jobs: test-docs: @@ -9,10 +44,6 @@ jobs: - image: *rust_image steps: - checkout - - run: - command: | - git submodule update --init --recursive - name: Init git submodule - run: name: RFC documentation command: | @@ -24,15 +55,29 @@ jobs: root: . paths: book + test-tari-debug: + docker: + - image: *rust_image + resource_class: medium + steps: + - checkout + - test: + release: false + + test-tari-release: + docker: + - image: *rust_image + resource_class: medium + steps: + - checkout + - test: + release: true + deploy-docs: docker: - image: quay.io/tarilabs/git-ssh-client:0.2-alpine steps: - checkout - - run: - command: | - git submodule update --init --recursive - name: Init git submodule - attach_workspace: at: . - add_ssh_keys: @@ -70,26 +115,7 @@ jobs: echo "Published." - test-tari: - docker: - - image: *rust_image - resource_class: medium - steps: - - checkout - - run: - command: | - git submodule update --init --recursive - name: Init git submodule - - run: - name: Tari source code - command: | - TOOLCHAIN=$(cat rust-toolchain) - NUM_JOBS=2 - rustup component add --toolchain $TOOLCHAIN rustfmt - cargo build --jobs=$NUM_JOBS --all-features - cargo fmt --all -- --check - cargo test --workspace --all-features --jobs=$NUM_JOBS - cargo test --workspace --all-features --release --jobs=$NUM_JOBS + workflows: version: 2 @@ -99,14 +125,17 @@ workflows: filters: branches: ignore: gh-pages - - test-tari: - filters: - branches: - ignore: gh-pages - deploy-docs: requires: - test-docs filters: branches: only: development - + - test-tari-debug: + filters: + branches: + ignore: gh-pages + - test-tari-release: + filters: + branches: + ignore: gh-pages diff --git a/.gitignore b/.gitignore index 39f4caff1e..f47808e718 100644 --- a/.gitignore +++ b/.gitignore @@ -28,11 +28,14 @@ report # On development branch only. This should be removed for point releases Cargo.lock - *.log -# Ignore DataStore and Database files +# Ignore DataStore, Database and Log files *.mdb /data/ *.sqlite3 base_layer/wallet_ffi/build.config +/base_layer/wallet_ffi/logs/ +base_layer/wallet_ffi/.cargo/config + +keys.json \ No newline at end of file diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 5738ab0e15..0000000000 --- a/.gitmodules +++ /dev/null @@ -1,8 +0,0 @@ -[submodule "comms/rust-multiaddr"] - path = comms/rust-multiaddr - url = https://github.com/tari-project/rust-multiaddr.git - branch = tari -[submodule "comms/yamux"] - path = comms/yamux - url = https://github.com/tari-project/yamux.git - branch = futures-alpha diff --git a/Cargo.toml b/Cargo.toml index 41103647da..c04ff9aa5c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,32 +2,17 @@ members = [ "base_layer/core", - "base_layer/transactions", "base_layer/key_manager", "base_layer/mmr", - "base_layer/mining", "base_layer/p2p", "base_layer/service_framework", "base_layer/wallet", "base_layer/wallet_ffi", "comms", "comms/dht", - "comms/middleware", - # TODO: Remove this once tower filter (0.3.0-alpha.3) is released - "comms/middleware/tower-filter", - "digital_assets_layer/core", - "infrastructure/broadcast_channel", - "infrastructure/crypto", - "infrastructure/protobuf_build", - "infrastructure/pubsub", "infrastructure/shutdown", "infrastructure/storage", "infrastructure/test_utils", - #"applications/tari_testnet_miner", "applications/tari_base_node", - #Needs to be updated to make its calls using an Tokio runtime block_on function. - #"ffi" - #The wallet gRPC is based on the old Futures and not part of the Testnet scope - #"applications/grpc_wallet", -# "applications/console_text_messenger", + "applications/test_faucet", ] diff --git a/README.md b/README.md index a7d5586390..fe73d15e51 100644 --- a/README.md +++ b/README.md @@ -35,20 +35,6 @@ to generate the documentation. The generated html sits in `target/doc/`. Alterna See [RFC-0110/CodeStructure](./RFC/src/RFC-0010_CodeStructure.md) for details on the code structure and layout. -### Git submodules - -Git submodules are use temporarily until some dependent libraries are stabilized and released as crates. -When checking out code take the following steps to ensure submodules are up to date. - -```shell script -# Initialize submodules -git submodule init -# Sets `git pull` to automatically pull submodules -git config submodule.recurse true -# Checkout/update all submodules -git submodule update --recursive --remote -``` - ## Conversation channels [](https://t.me/tarilab) Non-technical discussions and gentle sparring. diff --git a/RFC/src/RFC-0152_EmojiId.md b/RFC/src/RFC-0152_EmojiId.md new file mode 100644 index 0000000000..397b62b135 --- /dev/null +++ b/RFC/src/RFC-0152_EmojiId.md @@ -0,0 +1,173 @@ +# RFC-0152/EmojiId + +## Emoji Id specification + +![Status: Draft](theme/images/status-draft.svg) + +**Maintainer(s)**:[Cayle Sharrock](https://github.com/CjS77) + +# Licence + +[ The 3-Clause BSD Licence](https://opensource.org/licenses/BSD-3-Clause). + +Copyright 2020. The Tari Development Community + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +following conditions are met: + +1. Redistributions of this document must retain the above copyright notice, this list of conditions and the following + disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided with the distribution. +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS DOCUMENT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +## Language + +The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", +"NOT RECOMMENDED", "MAY" and "OPTIONAL" in this document are to be interpreted as described in +[BCP 14](https://tools.ietf.org/html/bcp14) (covering RFC2119 and RFC8174) when, and only when, they appear in all capitals, as +shown here. + +## Disclaimer + +This document and its content are intended for information purposes only and may be subject to change or update +without notice. + +This document may include preliminary concepts that may or may not be in the process of being developed by the Tari +community. The release of this document is intended solely for review and discussion by the community regarding the +technological merits of the potential system outlined herein. + +## Goals + +This document describes the specification for Emoji Ids. Emoji Ids are encoded node ids used for humans to easily verify +peer node addresses. + +## Related Requests for Comment + +None + +## Description + +Tari [Communication Node]s are identified on the network via their [Node ID]; which in turn are derived from the node's +public key. Both the node id and public key are simple large integer numbers. + +The most common practice for human beings to copy large numbers in cryptocurrency software is to scan a QR code or copy +and paste a value from one application to another. These numbers are typically encoded using hexadecimal or Base58 +encoding. The user will then typically scan (parts) of the string by eye to ensure that the value was transferred +correctly. + +For Tari, we propose encoding values, the node ID in particular, using emoji. The advantages of this approach are: + +* Emoji are more easily identifiable; and if selected carefully, less prone to identification errors (e.g. mistaking an + O for a 0). +* The alphabet can be considerably larger than hexadecimal (16) or Base58 (58), resulting in shorter character sequences + in the encoding. + +### The specification + +#### The emoji character map +An emoji alphabet of 1,024 characters is selected. Each emoji is assigned a unique index from 0 to 1023 inclusive. This +list is the emoji map. For example, + +* 😀 => 0 +* 😘 => 1 +* ... +* 🦊 => 1023 + +The emoji SHOULD be selected such that + +* Similar looking emoji are excluded from the map. e.g. Neither 😁 or 😄 should be included. Similarly the Irish and + Côte d'Ivoirean flags look very similar, and both should be excluded. +* Modified emoji (skin tones, gender modifiers) are excluded. Only the "base" emoji is considered. + +#### Encoding + +The essential strategy in the encoding process is to map a sequence of 8-bit values onto a 10-bit alphabet. The general +encoding procedure is as follows: + +Given a large integer value, represented as a _byte array_, `S`, in little-endian format (most significant digit last). +Assume the string is addressable, i.e. `S[i]` is the `i`th byte in the array. +* Set `CURSOR` to 0, Set `L` to a multiple of 10 that is `<= len(S)`. +* Set `IDX` to `[]` (an empty array) +* While `CURSOR < L`: + * Set `L <= S[CURSOR/8 + 1]`, the current low byte; if the index would overflow, set `L` to zero. + * Set `H <= S[CURSOR/8]`, the current high byte + * Set `n <= CURSOR % 8`, the position of the cursor in the current high byte + * Set `i <= ((H as u8) << n) << 2 + (L >> (6 - n))`, where the first shift left (`H as u8 <> 2`. This can be used to set the Emoji map accordingly (and may have to be + done iteratively, since the version is encoded into the emoji string). +3. Set `CURSOR = 0`. +4. Set `B = []`, and empty byte array +5. While `CURSOR <= 11`: + 4. Set `k <= CURSOR * 2` + 5. Do a reverse lookup of the emoji`[CURSOR]` to find its index. Store this u64 value in `L`. + 6. If `k > 0`, set `H` to the reverse lookup index of emoji`[CURSOR-1]` as u8 (first 2 bits are discarded), else + `H=0`. + 7. Set `v = ((H as u8) << (8-k)) + (L >> (2+k))`. Push v onto tho `B`. + 9. Set `CURSOR <= CURSOR + 1` + +If the algorithm completes, `B` holds the node ID. + +#### Versioning + +The current emoji ID version number is 1. If the emoji alphabet changes, the version number MUST be incremented. This +will usually cause incompatible versions of the emoji ID to be detected. However, this is not fail-safe. + +The last 6 bits of the 11th emoji encodes the version; this means that the first 4 bits are part of the node ID. On a +reverse mapping, there is a chance that the reverse mapping would offer a valid, but incorrect version number if the new +mapping are not chosen carefully. + +##### Example. + +In version 1, 😘 => `0b0000_000001` = 1 in the map. Seeing 😘 as the 11th emoji in a string would result in a version +code of 1, which is consistent and expected. + +However, in unlucky version 13, if 😘 moves in the map to number 13 (`0b0000_001101`), the version decoding would also +be valid and thus we wouldn't be able to unambiguously identify the version. + + + +[Communication Node]: Glossary.md#communication-node +[Node ID]: Glossary.md#node-id diff --git a/RFC/src/SUMMARY.md b/RFC/src/SUMMARY.md index 7cc06213a1..7489fc08f4 100644 --- a/RFC/src/SUMMARY.md +++ b/RFC/src/SUMMARY.md @@ -11,6 +11,7 @@ - [RFC-0140: Sync and Seeding](RFC-0140_Syncing_and_seeding.md) - [RFC-0150: Wallets](RFC-0150_Wallets.md) - [RFC-0151: Transaction protocol](RFC-0151_TransactionProtocol.md) + - [RFC-0152: Emoji ID](RFC-0152_EmojiId.md) - [RFC-0170: Network Communication Protocol](RFC-0170_NetworkCommunicationProtocol.md) - [RFC-0171: Message Serialisation](RFC-0171_MessageSerialisation.md) - [RFC-0172: Peer to Peer Messaging Protocol](RFC-0172_PeerToPeerMessagingProtocol.md) diff --git a/applications/cli_wallet/Cargo.toml b/applications/cli_wallet/Cargo.toml deleted file mode 100644 index a5b74bcf0e..0000000000 --- a/applications/cli_wallet/Cargo.toml +++ /dev/null @@ -1,5 +0,0 @@ -[package] -name = "cli_wallet" -version = "0.0.1" - -[dependencies] diff --git a/applications/console_text_messenger/Cargo.toml b/applications/console_text_messenger/Cargo.toml deleted file mode 100644 index fde5fe6802..0000000000 --- a/applications/console_text_messenger/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "console_text_messenger" -version = "0.1.0" -authors = ["Philip Robinson "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -tari_wallet = {path = "../../base_layer/wallet", version="^0.0"} -tari_common = {path = "../../common", version= "^0.0"} -tari_utilities = { path = "../../infrastructure/tari_util", version = "^0.0"} -tari_comms = { path = "../../comms", version = "^0.0"} -tari_p2p = {path = "../../base_layer/p2p", version = "^0.0"} -tari_crypto = { path = "../../infrastructure/crypto", version = "^0.0"} -clap = "2.33.0" -serde = "1.0.90" -serde_derive = "1.0.90" -chrono = { version = "0.4.6", features = ["serde"]} -config = { version = "0.9.3" } -simple_logger = "1.2.0" -log = { version = "0.4.0", features = ["std"] } -crossbeam-channel = "0.3.8" -log4rs = {version ="0.8.3",features = ["console_appender", "file_appender", "file", "yaml_format"]} -ctrlc = "3.1.3" -pnet = "0.22.0" diff --git a/applications/console_text_messenger/src/main.rs b/applications/console_text_messenger/src/main.rs deleted file mode 100644 index 93b8c8f928..0000000000 --- a/applications/console_text_messenger/src/main.rs +++ /dev/null @@ -1,400 +0,0 @@ -// Copyright 2019. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#[macro_use] -extern crate clap; - -use clap::{App, Arg}; -use crossbeam_channel as channel; -use log::*; -use log4rs::{ - append::file::FileAppender, - config::{Appender, Config, Root}, - encode::pattern::PatternEncoder, -}; -use pnet::datalink::{self, NetworkInterface}; -use serde::{Deserialize, Serialize}; -use std::{fs, io, sync::Arc, thread, time::Duration}; -use tari_comms::{ - connection::NetAddress, - control_service::ControlServiceConfig, - peer_manager::Peer, - types::{CommsPublicKey, CommsSecretKey}, -}; -use tari_crypto::keys::PublicKey; -use tari_p2p::{initialization::CommsConfig, sync_services::ServiceError}; -use tari_utilities::{hex::Hex, message_format::MessageFormat}; -use tari_wallet::{ - text_message_service::{Contact, ReceivedTextMessage}, - wallet::WalletConfig, - Wallet, -}; - -const LOG_TARGET: &str = "applications::cli_text_messenger"; - -#[derive(Debug, Default, Deserialize)] -struct Settings { - control_port: Option, - grpc_port: Option, - secret_key: Option, - data_path: Option, - screen_name: Option, -} -#[derive(Debug, Serialize, Deserialize)] -struct ConfigPeer { - screen_name: String, - pub_key: String, - address: String, -} - -#[derive(Debug, Serialize, Deserialize)] -struct Peers { - peers: Vec, -} - -/// # A Barebones console based text messege application to help with debugging the comms stack and wallet library -/// This app uses the same command switches as the grpc_wallet server and when using the -N command to load config -/// file/Peer list pairs will load them from the grpc_wallet/sample_config folder. -/// ## Usage -/// You can provide your own config files and parameters via switches (-help will explain those) or you can use the -N -/// switch followed by an integer to load an integer label config/peer list pair -/// `e.g. cargo run --bin console_text_messenger -- -N1` will load wallet_config_node1.toml and node1_peers.json -pub fn main() { - let matches = App::new("Tari Console Text Message Application") - .version("0.1") - .arg( - Arg::with_name("node-num") - .long("node_num") - .short("N") - .help( - "An integer indicating which Node number config to load from the Tari repo root (Node config is a \ - pair of files consisting of config + peers for that node)", - ) - .takes_value(true) - .required(false), - ) - .arg( - Arg::with_name("config") - .value_name("FILE") - .long("config") - .short("c") - .help("The relative path of a wallet config.toml file") - .takes_value(true) - .required(false), - ) - .arg( - Arg::with_name("grpc-port") - .long("grpc") - .short("g") - .help("The port the gRPC server will listen on") - .takes_value(true) - .required(false), - ) - .arg( - Arg::with_name("control-port") - .long("control-port") - .short("p") - .help("The port the p2p stack will listen on") - .takes_value(true) - .required(false), - ) - .arg( - Arg::with_name("secret-key") - .long("secret") - .short("s") - .help("This nodes communication secret key") - .takes_value(true) - .required(false), - ) - .arg( - Arg::with_name("data-path") - .long("data-path") - .short("d") - .help("Path where this node's database files will be stored") - .takes_value(true) - .required(false), - ) - .arg( - Arg::with_name("peers") - .value_name("FILE") - .long("peers") - .takes_value(true) - .required(false), - ) - .get_matches(); - - let mut settings = Settings::default(); - let mut contacts = Peers { peers: Vec::new() }; - let mut database_path = "./data/text_message_service.sqlite3".to_string(); - // The node-num switch overrides the config and peers switch for quick testing from the tari repo root - if matches.is_present("node-num") { - let node_num = value_t!(matches, "node-num", u32).unwrap(); - let peer_path = format!("./applications/grpc_wallet/sample_config/node{}_peers.json", node_num); - let config_path = format!( - "./applications/grpc_wallet/sample_config/wallet_config_node{}.toml", - node_num - ); - let mut settings_file = config::Config::default(); - settings_file - .merge(config::File::with_name(config_path.as_str())) - .expect("Could not open specified config file"); - settings = settings_file.try_into().unwrap(); - let contents = fs::read_to_string(peer_path).expect("Could not open specified Peers json file"); - contacts = Peers::from_json(contents.as_str()).expect("Could not parse JSON from specified Peers json file"); - database_path = format!("./data/text_message_service_node{}.sqlite3", node_num).to_string(); - } else { - if matches.is_present("config") { - let mut settings_file = config::Config::default(); - settings_file - .merge(config::File::with_name(matches.value_of("config").unwrap())) - .expect("Could not open specified config file"); - settings = settings_file.try_into().unwrap(); - } - if let Some(f) = matches.value_of("peers") { - let contents = fs::read_to_string(f).expect("Could not open specified Peers json file"); - contacts = - Peers::from_json(contents.as_str()).expect("Could not parse JSON from specified Peers json file"); - } - } - if let Some(_c) = matches.values_of("control-port") { - if let Ok(v) = value_t!(matches, "control-port", u32) { - settings.control_port = Some(v) - } - } - if let Some(_c) = matches.values_of("grpc-port") { - if let Ok(v) = value_t!(matches, "grpc-port", u32) { - settings.grpc_port = Some(v); - } - } - if let Some(c) = matches.value_of("secret-key") { - settings.secret_key = Some(c.to_string()) - } - if let Some(p) = matches.value_of("data-path") { - settings.data_path = Some(p.to_string()) - } - - if settings.secret_key.is_none() || - settings.control_port.is_none() || - settings.grpc_port.is_none() || - settings.data_path.is_none() || - settings.screen_name.is_none() - { - error!( - target: LOG_TARGET, - "Control port, gRPC port, Data path, Screen name or Secret Key has not been provided via command line or \ - config file" - ); - std::process::exit(1); - } - - // Setup the local comms stack - let listener_address: NetAddress = format!("0.0.0.0:{}", settings.control_port.unwrap()).parse().unwrap(); - let secret_key = CommsSecretKey::from_hex(settings.secret_key.unwrap().as_str()).unwrap(); - let public_key = CommsPublicKey::from_secret_key(&secret_key); - - // get and filter interfaces - let interfaces: Vec = datalink::interfaces() - .into_iter() - .filter(|interface| { - !interface.is_loopback() && interface.is_up() && interface.ips.iter().any(|addr| addr.is_ipv4()) - }) - .collect(); - - // select first interface - if interfaces.first().is_none() { - error!( - target: LOG_TARGET, - "No available network interface with an Ipv4 Address." - ); - std::process::exit(1); - } - - // get network interface and retrieve ipv4 address - let interface = interfaces.first().unwrap().clone(); - let local_ip = interface - .ips - .iter() - .find(|addr| addr.is_ipv4()) - .unwrap() - .ip() - .to_string(); - - let local_net_address = match format!("{}:{}", local_ip, settings.control_port.unwrap()).parse() { - Ok(na) => na, - Err(_) => { - error!(target: LOG_TARGET, "Could not resolve local IP address"); - std::process::exit(1); - }, - }; - - info!(target: LOG_TARGET, "Local Net Address: {:?}", local_net_address); - - let config = WalletConfig { - comms: CommsConfig { - control_service: ControlServiceConfig { - listener_address: listener_address.clone(), - socks_proxy_address: None, - requested_connection_timeout: Duration::from_millis(5000), - }, - socks_proxy_address: None, - host: "0.0.0.0".parse().unwrap(), - public_key: public_key.clone(), - secret_key: secret_key.clone(), - public_address: local_net_address, - datastore_path: settings.data_path.unwrap(), - peer_database_name: public_key.to_hex(), - }, - public_key: public_key.clone(), - database_path, - }; - - let wallet = Wallet::new(config).unwrap(); - - // Add any provided peers to Peer Manager and Text Message Service Contacts - if !contacts.peers.is_empty() { - for p in contacts.peers.iter() { - let pk = CommsPublicKey::from_hex(p.pub_key.as_str()).expect("Error parsing pub key from Hex"); - if let Ok(na) = p.address.clone().parse::() { - let peer = Peer::from_public_key_and_address(pk.clone(), na.clone()).unwrap(); - wallet.comms_services.peer_manager().add_peer(peer).unwrap(); - // If the contacts already exist we don't mind - if let Err(e) = wallet.text_message_service.add_contact(Contact { - screen_name: p.screen_name.clone(), - pub_key: pk.clone(), - address: na.clone(), - }) { - println!("Error adding config file contacts: {:?}", e); - } - } - } - } - - // Setup the logging to a file (screen_name.log), the file will appear in the root where the binary is run from - let logfile = FileAppender::builder() - .encoder(Box::new(PatternEncoder::new( - "{d(%Y-%m-%d %H:%M:%S.%f)} [{M}#{L}] [{t}] {l:5} {m} (({T}:{I})){n}", - ))) - .build(format!("{}.log", settings.screen_name.clone().unwrap())) - .unwrap(); - - let config = Config::builder() - .appender(Appender::builder().build("logfile", Box::new(logfile))) - .build(Root::builder().appender("logfile").build(LevelFilter::Debug)) - .unwrap(); - - let _handle = log4rs::init_config(config).unwrap(); - - let contacts = wallet - .text_message_service - .get_contacts() - .expect("Could not read contacts"); - - // Print out some help messages - println!( - "┌─────────────────────────────────────┐\n│Tari Console Barebones Text \ - Messenger│\n└─────────────────────────────────────┘" - ); - // TODO Read this from the SQL database rather than the config file - println!("This node's screen name: {}", settings.screen_name.unwrap().clone()); - for (i, c) in contacts.iter().enumerate() { - println!("Contact {}: {}", i, c.screen_name.clone()); - } - println!("Active Contact is 0: {}", contacts[0].screen_name.clone()); - println!("To change active contact to send to enter an integer and input_int%(# of contacts) will be made active"); - - // Start a text input thread which sends inputted lines to the main thread via a channel - let (tx, rx) = channel::unbounded(); - let (tx_sigint, rx_sigint) = channel::unbounded(); - thread::spawn(move || { - let mut input = String::new(); - loop { - if let Ok(_l) = io::stdin().read_line(&mut input) { - tx.send(input.clone()).unwrap(); - input = "".to_string(); - } - } - }); - - // setup a handler for ctrl-c - ctrlc::set_handler(move || { - println!("Received SIGINT"); - tx_sigint.send("shutdown").unwrap(); - }) - .expect("Error setting Ctrl-C handler"); - - // keeps track of which received messages have been printed to the console - let mut msg_index = 0; - // keeps track of which contact is currently active for sending - let mut active_contact: usize = 0; - - // Main Loop - loop { - let mut rx_messages: Vec = wallet - .text_message_service - .get_text_messages() - .expect("Error retrieving text messages from TMS") - .received_messages; - - rx_messages.sort(); - - for i in msg_index..rx_messages.len() { - let contact = contacts - .iter() - .find(|c| c.pub_key == rx_messages[i].source_pub_key) - .expect("Message from unknown peer"); - println!( - "{:?} - {:?}: {:?}", - rx_messages[i].timestamp, contact.screen_name, rx_messages[i].message - ); - msg_index = i + 1; - } - - if let Ok(mut input) = rx.recv_timeout(Duration::from_millis(100)) { - input.truncate(input.len() - 1); - - if let Ok(i) = input.clone().parse::() { - active_contact = i % contacts.len(); - println!("Active Contact updated to: {}", contacts[active_contact].screen_name); - } - - wallet - .text_message_service - .send_text_message(contacts[active_contact % contacts.len()].pub_key.clone(), input) - .unwrap() - } - - // check sigint to trigger shutdown - if rx_sigint.recv_timeout(Duration::from_millis(10)).is_ok() { - wallet.service_executor.shutdown().unwrap(); - wallet - .service_executor - .join_timeout(Duration::from_millis(3000)) - .unwrap(); - let comms = Arc::try_unwrap(wallet.comms_services) - .map_err(|_| ServiceError::CommsServiceOwnershipError) - .unwrap(); - - comms.shutdown().unwrap(); - println!("Exiting"); - break; - } - } -} diff --git a/applications/grpc_wallet/Cargo.toml b/applications/grpc_wallet/Cargo.toml deleted file mode 100644 index 3d50dbf57c..0000000000 --- a/applications/grpc_wallet/Cargo.toml +++ /dev/null @@ -1,44 +0,0 @@ -[package] -name = "tari_grpc_wallet" -version = "0.1.0" -authors = ["Philip Robinson "] -edition = "2018" - -[dependencies] -tari_wallet = {path = "../../base_layer/wallet", version="^0.0"} -tari_common = {path = "../../common", version= "^0.0"} -tari_utilities = { path = "../../infrastructure/tari_util", version = "^0.0"} -tari_comms = { path = "../../comms", version = "^0.0"} -tari_p2p = {path = "../../base_layer/p2p", version = "^0.0"} -tari_crypto = { path = "../../infrastructure/crypto"} -chrono = { version = "0.4.6", features = ["serde"]} -config = { version = "0.9.3" } -crossbeam-channel = "0.3.8" -bytes = "0.4" -derive-error = "0.0.4" -futures = "0.1" -http = "0.1" -log = { version = "0.4.0", features = ["std"] } -prost = "0.5" -tokio = "0.1" -tower-request-modifier = { git = "https://github.com/tower-rs/tower-http" } -tower-hyper = "0.1" -hyper = "0.12" -tower-grpc = { git = "https://github.com/tower-rs/tower-grpc.git", features = ["tower-hyper"] } -tower-grpc-build = { git = "https://github.com/tower-rs/tower-grpc.git", features = ["tower-hyper"]} -simple_logger = "1.2.0" -clap = "2.33.0" -serde = "1.0.90" -serde_derive = "1.0.90" -pnet = "0.22.0" - - -[dev-dependencies] -tari_crypto = { path = "../../infrastructure/crypto"} -tower-util = "0.1" -tempdir = "0.3.7" -rand = "0.5" - -[build-dependencies] -tower-grpc-build = { git = "https://github.com/tower-rs/tower-grpc.git", features = ["tower-hyper"] } - diff --git a/applications/grpc_wallet/proto/wallet_rpc.proto b/applications/grpc_wallet/proto/wallet_rpc.proto deleted file mode 100644 index 8f9f621678..0000000000 --- a/applications/grpc_wallet/proto/wallet_rpc.proto +++ /dev/null @@ -1,85 +0,0 @@ -syntax = "proto3"; - -package wallet_rpc; - -// Wallet gRPC service -service WalletRpc { - // Send a Tari Text Message - rpc SendTextMessage(TextMessageToSend) returns (RpcResponse) {} - // Request all messages - // TODO implement pagination - rpc GetTextMessages(VoidParams) returns (TextMessagesResponse) {} - // Return messages that are from/to the Contact's pub_key - rpc GetTextMessagesByContact(Contact) returns (TextMessagesResponse) {} - // Get/Set for Screen Name - rpc SetScreenName(ScreenName) returns (RpcResponse) {} - rpc GetScreenName(VoidParams) returns (ScreenName) {} - // CRUD for Contacts - rpc AddContact(Contact) returns (RpcResponse) {} - rpc RemoveContact(Contact) returns (RpcResponse) {} - rpc GetContacts(VoidParams) returns (Contacts) {} - //This method will update the screen_name of the contact with Pub-key contained in the argument Contact - rpc UpdateContact(Contact) returns (RpcResponse) {} - rpc GetPublicKey(VoidParams) returns (PublicKey) {} -} - -// A generic RPC call response message to convey the result of the call -message RpcResponse { - bool success = 1; - string message = 2; -} - -// A Tari Text Message to be sent -message TextMessageToSend { - string dest_pub_key = 1; - string message = 2; -} - -// A Received Tari Text Message -message ReceivedTextMessage { - string id = 1; - string source_pub_key = 2; - string dest_pub_key = 3; - string message = 4; - string timestamp = 5; -} - -// A Sent Tari Text Message -message SentTextMessage { - string id = 1; - string source_pub_key = 2; - string dest_pub_key = 3; - string message = 4; - string timestamp = 5; - bool acknowledged = 6; -} - -// An Empty message for RPC calls with no parameters -message VoidParams{} - -// A collection of all messages -message TextMessagesResponse { - repeated ReceivedTextMessage received_messages = 1; - repeated SentTextMessage sent_messages = 2; -} - -// Current users screen name -message ScreenName { - string screen_name = 1; -} - -// A contact -message Contact { - string screen_name = 1; - string pub_key = 2; - string address = 3; //IP address with port i.e. "127.0.0.1:11123" -} - -// A list of contacts -message Contacts { - repeated Contact contacts = 1; -} -// Returns your node's communication public key -message PublicKey { - string pub_key = 1; -} \ No newline at end of file diff --git a/applications/grpc_wallet/sample_config/node1_peers.json b/applications/grpc_wallet/sample_config/node1_peers.json deleted file mode 100644 index 0b7813310d..0000000000 --- a/applications/grpc_wallet/sample_config/node1_peers.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "peers": [ - { - "screen_name": "Philip", - "pub_key": "daa67142abc315b1149b54b8f086eb16596b15ee0e70e37f0aa15294fdcbd65b", - "address": "127.0.0.1:20000" - }, - { - "screen_name": "Cayle", - "pub_key": "a091006a3c606b6dacc4d165688ef137becdcaeb00acbba61dbb15f6c2df1900", - "address": "127.0.0.1:30000" - } - ] -} diff --git a/applications/grpc_wallet/sample_config/node2_peers.json b/applications/grpc_wallet/sample_config/node2_peers.json deleted file mode 100644 index 98195602eb..0000000000 --- a/applications/grpc_wallet/sample_config/node2_peers.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "peers": [ - { - "screen_name": "Jason", - "pub_key": "e0482c31139733b954cc3e59dcfbb4a65dbec1d53c97d47b18c91f6abfd04209", - "address": "127.0.0.1:10000" - }, - { - "screen_name": "Cayle", - "pub_key": "a091006a3c606b6dacc4d165688ef137becdcaeb00acbba61dbb15f6c2df1900", - "address": "127.0.0.1:30000" - } - ] -} diff --git a/applications/grpc_wallet/sample_config/node3_peers.json b/applications/grpc_wallet/sample_config/node3_peers.json deleted file mode 100644 index e26e6ba1f3..0000000000 --- a/applications/grpc_wallet/sample_config/node3_peers.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "peers": [ - { - "screen_name": "Jason", - "pub_key": "e0482c31139733b954cc3e59dcfbb4a65dbec1d53c97d47b18c91f6abfd04209", - "address": "127.0.0.1:10000" - }, - { - "screen_name": "Philip", - "pub_key": "daa67142abc315b1149b54b8f086eb16596b15ee0e70e37f0aa15294fdcbd65b", - "address": "127.0.0.1:20000" - } - ] -} diff --git a/applications/grpc_wallet/sample_config/wallet_config_node1.toml b/applications/grpc_wallet/sample_config/wallet_config_node1.toml deleted file mode 100644 index 42b29f48e3..0000000000 --- a/applications/grpc_wallet/sample_config/wallet_config_node1.toml +++ /dev/null @@ -1,17 +0,0 @@ -######################################################################################################################## -# # -# Wallet Configuration Options # -# # -######################################################################################################################## - -# TODO Finalize this config file - -control_port = 10000 -grpc_port = 10001 -secret_key = "bafd4efafac9310b85c7afdc68d0c874597102bc2e1c824caa19d8b263afff05" -data_path = "./data" -screen_name = "Jason" - - - - diff --git a/applications/grpc_wallet/sample_config/wallet_config_node2.toml b/applications/grpc_wallet/sample_config/wallet_config_node2.toml deleted file mode 100644 index 54cf4c3cb6..0000000000 --- a/applications/grpc_wallet/sample_config/wallet_config_node2.toml +++ /dev/null @@ -1,13 +0,0 @@ -######################################################################################################################## -# # -# Wallet Configuration Options # -# # -######################################################################################################################## - -# TODO Finalize this config file - -control_port = 20000 -grpc_port = 10001 -secret_key = "7745546269487c282076ab3f54a3867c51cf71892f7f37d92323aa6f24dc8b02" -data_path = "./data" -screen_name = "Philip" \ No newline at end of file diff --git a/applications/grpc_wallet/sample_config/wallet_config_node3.toml b/applications/grpc_wallet/sample_config/wallet_config_node3.toml deleted file mode 100644 index 78a862952f..0000000000 --- a/applications/grpc_wallet/sample_config/wallet_config_node3.toml +++ /dev/null @@ -1,13 +0,0 @@ -######################################################################################################################## -# # -# Wallet Configuration Options # -# # -######################################################################################################################## - -# TODO Finalize this config file - -control_port = 30000 -grpc_port = 10001 -secret_key = "a6bbc534c6d390e9e8ab3626d01677a1195dd59e0594c13f786c29b9abb77102" -data_path = "./data" -screen_name = "Cayle" \ No newline at end of file diff --git a/applications/grpc_wallet/src/grpc_interface.rs b/applications/grpc_wallet/src/grpc_interface.rs deleted file mode 100644 index d8f3afa6f2..0000000000 --- a/applications/grpc_wallet/src/grpc_interface.rs +++ /dev/null @@ -1,395 +0,0 @@ -// Copyright 2019. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::grpc_interface::wallet_rpc::{ - server, - Contact as ContactRpc, - Contacts as ContactsRpc, - PublicKey as PublicKeyRpc, - ReceivedTextMessage as ReceivedTextMessageRpc, - RpcResponse, - ScreenName as ScreenNameRpc, - SentTextMessage as SentTextMessageRpc, - TextMessageToSend as TextMessageToSendRpc, - TextMessagesResponse as TextMessagesResponseRpc, - VoidParams, -}; - -use futures::future; -use log::*; -use std::{convert::From, sync::Arc}; -use tari_comms::{connection::NetAddress, peer_manager::Peer, types::CommsPublicKey}; -use tari_utilities::hex::Hex; -use tari_wallet::{ - text_message_service::{Contact, ReceivedTextMessage, SentTextMessage, UpdateContact}, - Wallet, -}; -use tower_grpc::{Request, Response}; - -const LOG_TARGET: &str = "applications::grpc_wallet"; - -pub mod wallet_rpc { - include!(concat!(env!("OUT_DIR"), "/wallet_rpc.rs")); -} - -impl From for ReceivedTextMessageRpc { - fn from(m: ReceivedTextMessage) -> Self { - ReceivedTextMessageRpc { - id: m.id.to_hex(), - source_pub_key: m.source_pub_key.to_hex(), - dest_pub_key: m.dest_pub_key.to_hex(), - message: m.message, - timestamp: m.timestamp.to_string(), - } - } -} - -impl From for SentTextMessageRpc { - fn from(m: SentTextMessage) -> Self { - SentTextMessageRpc { - id: m.id.to_hex(), - source_pub_key: m.source_pub_key.to_hex(), - dest_pub_key: m.dest_pub_key.to_hex(), - message: m.message, - timestamp: m.timestamp.to_string(), - acknowledged: m.acknowledged, - } - } -} - -#[derive(Clone)] -pub struct WalletRPC { - pub wallet: Arc, -} - -/// Implementation of the the gRPC service methods. -impl server::WalletRpc for WalletRPC { - type AddContactFuture = future::FutureResult, tower_grpc::Status>; - type GetContactsFuture = future::FutureResult, tower_grpc::Status>; - type GetPublicKeyFuture = future::FutureResult, tower_grpc::Status>; - type GetScreenNameFuture = future::FutureResult, tower_grpc::Status>; - type GetTextMessagesByContactFuture = future::FutureResult, tower_grpc::Status>; - type GetTextMessagesFuture = future::FutureResult, tower_grpc::Status>; - type RemoveContactFuture = future::FutureResult, tower_grpc::Status>; - type SendTextMessageFuture = future::FutureResult, tower_grpc::Status>; - type SetScreenNameFuture = future::FutureResult, tower_grpc::Status>; - type UpdateContactFuture = future::FutureResult, tower_grpc::Status>; - - fn send_text_message(&mut self, request: Request) -> Self::SendTextMessageFuture { - trace!( - target: LOG_TARGET, - "SendTextMessage gRPC Request received: {:?}", - request, - ); - - let msg = request.into_inner(); - - let response = match CommsPublicKey::from_hex(msg.dest_pub_key.as_str()) { - Ok(pk) => match self.wallet.text_message_service.send_text_message(pk, msg.message) { - Ok(()) => Response::new(RpcResponse { - success: true, - message: "Text Message Sent".to_string(), - }), - Err(e) => Response::new(RpcResponse { - success: false, - message: format!("Error sending text message: {:?}", e).to_string(), - }), - }, - - Err(e) => Response::new(RpcResponse { - success: false, - message: format!("Error sending text message: {:?}", e).to_string(), - }), - }; - - future::ok(response) - } - - fn get_text_messages(&mut self, request: Request) -> Self::GetTextMessagesFuture { - trace!( - target: LOG_TARGET, - "GetTextMessages gRPC Request received: {:?}", - request - ); - - let response_body = match self.wallet.text_message_service.get_text_messages() { - Ok(mut msgs) => TextMessagesResponseRpc { - sent_messages: msgs.sent_messages.drain(..).map(|m| m.into()).collect(), - received_messages: msgs.received_messages.drain(..).map(|m| m.into()).collect(), - }, - _ => TextMessagesResponseRpc { - sent_messages: Vec::new(), - received_messages: Vec::new(), - }, - }; - let response = Response::new(response_body); - - future::ok(response) - } - - fn get_text_messages_by_contact(&mut self, request: Request) -> Self::GetTextMessagesFuture { - trace!( - target: LOG_TARGET, - "GetTextMessages gRPC Request received: {:?}", - request - ); - - let msg = request.into_inner(); - - let pub_key = match CommsPublicKey::from_hex(msg.pub_key.as_str()) { - Ok(pk) => pk, - _ => { - return future::ok(Response::new(TextMessagesResponseRpc { - sent_messages: Vec::new(), - received_messages: Vec::new(), - })) - }, - }; - - let response_body = match self.wallet.text_message_service.get_text_messages_by_pub_key(pub_key) { - Ok(mut msgs) => TextMessagesResponseRpc { - sent_messages: msgs.sent_messages.drain(..).map(|m| m.into()).collect(), - received_messages: msgs.received_messages.drain(..).map(|m| m.into()).collect(), - }, - _ => TextMessagesResponseRpc { - sent_messages: Vec::new(), - received_messages: Vec::new(), - }, - }; - let response = Response::new(response_body); - - future::ok(response) - } - - fn set_screen_name(&mut self, request: Request) -> Self::SetScreenNameFuture { - trace!(target: LOG_TARGET, "SetScreenName gRPC Request received: {:?}", request,); - - let msg = request.into_inner(); - - let response = match self.wallet.text_message_service.set_screen_name(msg.screen_name) { - Ok(()) => Response::new(RpcResponse { - success: true, - message: "Screen Name Set".to_string(), - }), - Err(e) => Response::new(RpcResponse { - success: false, - message: format!("Error setting screen name: {:?}", e).to_string(), - }), - }; - - future::ok(response) - } - - fn get_screen_name(&mut self, request: Request) -> Self::GetScreenNameFuture { - trace!(target: LOG_TARGET, "GetScreenName gRPC Request received: {:?}", request,); - - let _msg = request.into_inner(); - - let screen_name = self - .wallet - .text_message_service - .get_screen_name() - .unwrap_or_else(|_| Some("".to_string())) // Unwrap result - .unwrap_or_else(|| "".to_string()); // Unwrap Option - - future::ok(Response::new(ScreenNameRpc { screen_name })) - } - - fn get_public_key(&mut self, request: Request) -> Self::GetPublicKeyFuture { - trace!(target: LOG_TARGET, "GetPublicKey gRPC Request received: {:?}", request,); - - let _msg = request.into_inner(); - - let public_key = self.wallet.public_key.clone().to_hex(); - - future::ok(Response::new(PublicKeyRpc { pub_key: public_key })) - } - - fn add_contact(&mut self, request: Request) -> Self::AddContactFuture { - trace!(target: LOG_TARGET, "AddContact gRPC Request received: {:?}", request,); - - let msg = request.into_inner(); - - let screen_name = msg.screen_name.clone(); - let pub_key = match CommsPublicKey::from_hex(msg.pub_key.as_str()) { - Ok(pk) => pk, - _ => { - return future::ok(Response::new(RpcResponse { - success: false, - message: "Failed to add contact, cannot serialize public key".to_string(), - })) - }, - }; - - let net_address = match msg.address.clone().parse::() { - Ok(n) => n, - Err(e) => { - return future::ok(Response::new(RpcResponse { - success: false, - message: format!("Failed to add contact, cannot parse net address: {:?}", e).to_string(), - })) - }, - }; - - let peer = match Peer::from_public_key_and_address(pub_key.clone(), net_address.clone()) { - Ok(p) => p, - Err(e) => { - return future::ok(Response::new(RpcResponse { - success: false, - message: format!("Failed to add contact, cannot create peer: {:?}", e).to_string(), - })) - }, - }; - - match self.wallet.comms_services.peer_manager().add_peer(peer) { - Err(e) => { - return future::ok(Response::new(RpcResponse { - success: false, - message: format!("Failed to add contact, cannot add peer to Peer Manager: {:?}", e).to_string(), - })) - }, - _ => (), - }; - - match self.wallet.text_message_service.add_contact(Contact { - screen_name, - pub_key, - address: net_address, - }) { - Ok(()) => (), - Err(e) => { - return future::ok(Response::new(RpcResponse { - success: false, - message: format!("Error adding contact: {:?}", e).to_string(), - })) - }, - }; - - future::ok(Response::new(RpcResponse { - success: true, - message: "Successfully added contact".to_string(), - })) - } - - fn remove_contact(&mut self, request: Request) -> Self::RemoveContactFuture { - trace!(target: LOG_TARGET, "RemoveContact gRPC Request received: {:?}", request,); - - let msg = request.into_inner(); - - let net_address = match msg.address.clone().parse::() { - Ok(n) => n, - Err(e) => { - return future::ok(Response::new(RpcResponse { - success: false, - message: format!("Failed to remove contact, cannot parse net address: {:?}", e).to_string(), - })) - }, - }; - - let screen_name = msg.screen_name.clone(); - - if let Ok(pk) = CommsPublicKey::from_hex(msg.pub_key.as_str()) { - let response = match self.wallet.text_message_service.remove_contact(Contact { - screen_name, - pub_key: pk, - address: net_address, - }) { - Ok(()) => Response::new(RpcResponse { - success: true, - message: "Successfully removed contact".to_string(), - }), - Err(e) => Response::new(RpcResponse { - success: false, - message: format!("Error removing contact: {:?}", e).to_string(), - }), - }; - - return future::ok(response); - } else { - return future::ok(Response::new(RpcResponse { - success: false, - message: "Failed to remove contact, cannot serialize public key".to_string(), - })); - } - } - - fn update_contact(&mut self, request: Request) -> Self::RemoveContactFuture { - trace!(target: LOG_TARGET, "UpdateContact gRPC Request received: {:?}", request,); - - let msg = request.into_inner(); - let net_address = match msg.address.clone().parse::() { - Ok(n) => n, - Err(e) => { - return future::ok(Response::new(RpcResponse { - success: false, - message: format!("Failed to update contact, cannot parse net address: {:?}", e).to_string(), - })) - }, - }; - let screen_name = msg.screen_name.clone(); - if let Ok(pk) = CommsPublicKey::from_hex(msg.pub_key.as_str()) { - let response = match self.wallet.text_message_service.update_contact(pk, UpdateContact { - screen_name: Some(screen_name), - address: Some(net_address), - }) { - Ok(()) => Response::new(RpcResponse { - success: true, - message: "Successfully updated contact".to_string(), - }), - Err(e) => Response::new(RpcResponse { - success: false, - message: format!("Error updating contact: {:?}", e).to_string(), - }), - }; - - return future::ok(response); - } else { - return future::ok(Response::new(RpcResponse { - success: false, - message: "Failed to update contact, cannot serialize public key".to_string(), - })); - } - } - - fn get_contacts(&mut self, request: Request) -> Self::GetContactsFuture { - trace!(target: LOG_TARGET, "GetContacts gRPC Request received: {:?}", request,); - - let mut contacts_resp: Vec = Vec::new(); - - if let Ok(contacts) = self.wallet.text_message_service.get_contacts() { - for c in contacts.iter() { - let sn = c.screen_name.clone(); - let address = format!("{}", c.address.clone()); - - contacts_resp.push(ContactRpc { - screen_name: sn, - pub_key: c.pub_key.to_hex(), - address, - }); - } - } - - future::ok(Response::new(ContactsRpc { - contacts: contacts_resp, - })) - } -} diff --git a/applications/grpc_wallet/src/main.rs b/applications/grpc_wallet/src/main.rs deleted file mode 100644 index 6024917a5d..0000000000 --- a/applications/grpc_wallet/src/main.rs +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright 2019. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#[macro_use] -extern crate clap; - -use pnet::datalink::{self, NetworkInterface}; - -use clap::{App, Arg}; -use log::*; -use serde::{Deserialize, Serialize}; -use std::{fs, sync::Arc, time::Duration}; -use tari_comms::{ - connection::NetAddress, - control_service::ControlServiceConfig, - peer_manager::Peer, - types::{CommsPublicKey, CommsSecretKey}, -}; -use tari_crypto::keys::PublicKey; -use tari_grpc_wallet::wallet_server::WalletServer; -use tari_p2p::initialization::CommsConfig; -use tari_utilities::{hex::Hex, message_format::MessageFormat}; -use tari_wallet::{text_message_service::Contact, wallet::WalletConfig, Wallet}; -const LOG_TARGET: &str = "applications::grpc_wallet"; - -#[derive(Debug, Default, Deserialize)] -struct Settings { - control_port: Option, - grpc_port: Option, - secret_key: Option, - data_path: Option, -} -#[derive(Debug, Serialize, Deserialize)] -struct ConfigPeer { - screen_name: String, - pub_key: String, - address: String, -} - -#[derive(Debug, Serialize, Deserialize)] -struct Peers { - peers: Vec, -} - -/// Entry point into the gRPC server binary -pub fn main() { - let _ = simple_logger::init_with_level(log::Level::Info); - - let matches = App::new("Tari Wallet gRPC server") - .version("0.1") - .arg( - Arg::with_name("node-num") - .long("node_num") - .short("N") - .help( - "An integer indicating which Node number config to load from the Tari repo root (Node config is a \ - pair of files consisting of config + peers for that node)", - ) - .takes_value(true) - .required(false), - ) - .arg( - Arg::with_name("config") - .value_name("FILE") - .long("config") - .short("c") - .help("The relative path of a wallet config.toml file") - .takes_value(true) - .required(false), - ) - .arg( - Arg::with_name("grpc-port") - .long("grpc") - .short("g") - .help("The port the gRPC server will listen on") - .takes_value(true) - .required(false), - ) - .arg( - Arg::with_name("control-port") - .long("control-port") - .short("p") - .help("The port the p2p stack will listen on") - .takes_value(true) - .required(false), - ) - .arg( - Arg::with_name("secret-key") - .long("secret") - .short("s") - .help("This nodes communication secret key") - .takes_value(true) - .required(false), - ) - .arg( - Arg::with_name("data-path") - .long("data-path") - .short("d") - .help("Path where this node's database files will be stored") - .takes_value(true) - .required(false), - ) - .arg( - Arg::with_name("peers") - .value_name("FILE") - .long("peers") - .takes_value(true) - .required(false), - ) - .get_matches(); - - let mut settings = Settings::default(); - let mut contacts = Peers { peers: Vec::new() }; - let mut database_path = "./data/text_message_service.sqlite3".to_string(); - // The node-num switch overrides the config and peers switch for quick testing from the tari repo root - if matches.is_present("node-num") { - let node_num = value_t!(matches, "node-num", u32).unwrap(); - let peer_path = format!("./applications/grpc_wallet/sample_config/node{}_peers.json", node_num); - let config_path = format!( - "./applications/grpc_wallet/sample_config/wallet_config_node{}.toml", - node_num - ); - let mut settings_file = config::Config::default(); - settings_file - .merge(config::File::with_name(config_path.as_str())) - .expect("Could not open specified config file"); - settings = settings_file.try_into().unwrap(); - let contents = fs::read_to_string(peer_path).expect("Could not open specified Peers json file"); - contacts = Peers::from_json(contents.as_str()).expect("Could not parse JSON from specified Peers json file"); - database_path = format!("./data/text_message_service_node{}.sqlite3", node_num).to_string(); - } else { - if matches.is_present("config") { - let mut settings_file = config::Config::default(); - settings_file - .merge(config::File::with_name(matches.value_of("config").unwrap())) - .expect("Could not open specified config file"); - settings = settings_file.try_into().unwrap(); - } - if let Some(f) = matches.value_of("peers") { - let contents = fs::read_to_string(f).expect("Could not open specified Peers json file"); - contacts = - Peers::from_json(contents.as_str()).expect("Could not parse JSON from specified Peers json file"); - } - } - if let Some(_c) = matches.values_of("control-port") { - if let Ok(v) = value_t!(matches, "control-port", u32) { - settings.control_port = Some(v) - } - } - if let Some(_c) = matches.values_of("grpc-port") { - if let Ok(v) = value_t!(matches, "grpc-port", u32) { - settings.grpc_port = Some(v); - } - } - if let Some(c) = matches.value_of("secret-key") { - settings.secret_key = Some(c.to_string()) - } - if let Some(p) = matches.value_of("data-path") { - settings.data_path = Some(p.to_string()) - } - - if settings.secret_key.is_none() || - settings.control_port.is_none() || - settings.grpc_port.is_none() || - settings.data_path.is_none() - { - error!( - target: LOG_TARGET, - "Control port, gRPC port, Data path or Secret Key has not been provided via command line or config file" - ); - std::process::exit(1); - } - - // Setup the local comms stack - let listener_address: NetAddress = format!("0.0.0.0:{}", settings.control_port.unwrap()).parse().unwrap(); - let secret_key = CommsSecretKey::from_hex(settings.secret_key.unwrap().as_str()).unwrap(); - let public_key = CommsPublicKey::from_secret_key(&secret_key); - - // get and filter interfaces - let interfaces: Vec = datalink::interfaces() - .into_iter() - .filter(|interface| { - !interface.is_loopback() && interface.is_up() && interface.ips.iter().any(|addr| addr.is_ipv4()) - }) - .collect(); - - // select first interface - if interfaces.first().is_none() { - error!( - target: LOG_TARGET, - "No available network interface with an Ipv4 Address." - ); - std::process::exit(1); - } - - // get network interface and retrieve ipv4 address - let interface = interfaces.first().unwrap().clone(); - let local_ip = interface - .ips - .iter() - .find(|addr| addr.is_ipv4()) - .unwrap() - .ip() - .to_string(); - - let local_net_address = match format!("{}:{}", local_ip, settings.control_port.unwrap()).parse() { - Ok(na) => na, - Err(_) => { - error!(target: LOG_TARGET, "Could not resolve local IP address"); - std::process::exit(1); - }, - }; - - info!(target: LOG_TARGET, "Local Net Address: {:?}", local_net_address); - - let config = WalletConfig { - comms: CommsConfig { - control_service: ControlServiceConfig { - listener_address: listener_address.clone(), - socks_proxy_address: None, - requested_connection_timeout: Duration::from_millis(5000), - }, - socks_proxy_address: None, - host: "0.0.0.0".parse().unwrap(), - public_key: public_key.clone(), - secret_key: secret_key.clone(), - public_address: local_net_address, - datastore_path: settings.data_path.unwrap(), - peer_database_name: public_key.to_hex(), - }, - public_key: public_key.clone(), - database_path, - }; - - let wallet = Wallet::new(config).unwrap(); - - // Add any provided peers to Peer Manager and Text Message Service Contacts - if !contacts.peers.is_empty() { - for p in contacts.peers.iter() { - let pk = CommsPublicKey::from_hex(p.pub_key.as_str()).expect("Error parsing pub key from Hex"); - if let Ok(na) = p.address.clone().parse::() { - let peer = Peer::from_public_key_and_address(pk.clone(), na.clone()).unwrap(); - wallet.comms_services.peer_manager().add_peer(peer).unwrap(); - if let Err(e) = wallet.text_message_service.add_contact(Contact { - screen_name: p.screen_name.clone(), - pub_key: pk.clone(), - address: na.clone(), - }) { - info!("Error adding config file contacts: {:?}", e); - } - } - } - } - - let wallet_server = WalletServer::new(settings.grpc_port.unwrap(), Arc::new(wallet)); - let _res = wallet_server.start(); -} diff --git a/applications/grpc_wallet/src/wallet_server.rs b/applications/grpc_wallet/src/wallet_server.rs deleted file mode 100644 index 9266d8a0ec..0000000000 --- a/applications/grpc_wallet/src/wallet_server.rs +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2019. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::grpc_interface::{wallet_rpc::server, WalletRPC}; -use derive_error::Error; -use futures::{future::Future, stream::Stream}; -use hyper::server::conn::Http; -use log::*; -use std::{net::AddrParseError, sync::Arc}; -use tari_utilities::message_format::MessageFormatError; -use tari_wallet::Wallet; -use tokio::net::TcpListener; -use tower_hyper::Server; - -const LOG_TARGET: &str = "applications::grpc_wallet"; - -#[derive(Debug, Error)] -pub enum WalletServerError { - AddrParseError(AddrParseError), - IoError(std::io::Error), - MessageFormatError(MessageFormatError), -} - -/// Instance of the Wallet RPC Server with a reference to the Wallet API and the config -pub struct WalletServer { - // TODO some form of authentication - port: u32, - wallet: Arc, -} - -impl WalletServer { - pub fn new(port: u32, wallet: Arc) -> WalletServer { - WalletServer { port, wallet } - } - - pub fn start(self) -> Result<(), WalletServerError> { - let new_service = server::WalletRpcServer::new(WalletRPC { - wallet: self.wallet.clone(), - }); - - let mut server = Server::new(new_service); - - let http = Http::new().http2_only(true).clone(); - let addr = format!("127.0.0.1:{}", self.port); - let bind = TcpListener::bind(&addr.clone().as_str().parse()?)?; - let serve = bind - .incoming() - .for_each(move |sock| { - if let Err(e) = sock.set_nodelay(true) { - return Err(e); - } - - let serve = server.serve_with(sock, http.clone()); - tokio::spawn(serve.map_err(|e| error!("Error starting Hyper service: {:?}", e))); - - Ok(()) - }) - .map_err(|e| error!("Error accepting request: {:?}", e)); - info!(target: LOG_TARGET, "Starting Wallet gRPC Server at {}", addr); - tokio::run(serve); - Ok(()) - } -} diff --git a/applications/grpc_wallet/tests/wallet_grpc_server/mod.rs b/applications/grpc_wallet/tests/wallet_grpc_server/mod.rs deleted file mode 100644 index 5d9444883d..0000000000 --- a/applications/grpc_wallet/tests/wallet_grpc_server/mod.rs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2019. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -pub mod wallet_grpc_server; diff --git a/applications/grpc_wallet/tests/wallet_grpc_server/wallet_grpc_server.rs b/applications/grpc_wallet/tests/wallet_grpc_server/wallet_grpc_server.rs deleted file mode 100644 index 8e4f49d268..0000000000 --- a/applications/grpc_wallet/tests/wallet_grpc_server/wallet_grpc_server.rs +++ /dev/null @@ -1,631 +0,0 @@ -// Copyright 2019. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crossbeam_channel::bounded; -use futures::future::Future; -use hyper::client::connect::{Destination, HttpConnector}; -use log::{Level, *}; -use rand::{distributions::Alphanumeric, rngs::OsRng, Rng}; -use std::{iter, path::PathBuf, sync::Arc, thread, time::Duration}; -use tari_comms::{ - connection::{net_address::NetAddressWithStats, NetAddress, NetAddressesWithStats}, - control_service::ControlServiceConfig, - peer_manager::{peer::PeerFlags, NodeId, Peer}, - types::{CommsPublicKey, CommsSecretKey}, -}; -use tari_crypto::keys::{PublicKey, SecretKey}; -use tari_grpc_wallet::{ - grpc_interface::wallet_rpc::{ - client::WalletRpc, - Contact as ContactRpc, - RpcResponse, - ScreenName as ScreenNameRpc, - TextMessageToSend as TextMessageToSendRpc, - VoidParams, - }, - wallet_server::WalletServer, -}; -use tari_p2p::initialization::CommsConfig; -use tari_utilities::hex::Hex; -use tari_wallet::{text_message_service::Contact, wallet::WalletConfig, Wallet}; -use tempdir::TempDir; -use tower_grpc::Request; -use tower_hyper::{client, util}; -use tower_util::MakeService; - -const LOG_TARGET: &str = "applications::grpc_wallet"; -const WALLET_GRPC_PORT: u32 = 26778; - -pub fn init() { - let _ = simple_logger::init_with_level(Level::Debug); -} - -fn get_path(name: Option<&str>) -> String { - let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - path.push("tests/data"); - path.push(name.unwrap_or("")); - path.to_str().unwrap().to_string() -} - -fn clean_up_sql_database(name: &str) { - if std::fs::metadata(get_path(Some(name))).is_ok() { - std::fs::remove_file(get_path(Some(name))).unwrap(); - } -} - -fn init_sql_database(name: &str) { - clean_up_sql_database(name); - let path = get_path(None); - let _ = std::fs::create_dir(&path).unwrap_or_default(); -} - -fn send_text_message_request(msg: TextMessageToSendRpc, desired_response: RpcResponse) { - let (tx, rx) = bounded(1); - - let uri: http::Uri = format!("http://127.0.0.1:{}", WALLET_GRPC_PORT).parse().unwrap(); - - let dst = Destination::try_from_uri(uri.clone()).unwrap(); - let connector = util::Connector::new(HttpConnector::new(1)); - let settings = client::Builder::new().http2_only(true).clone(); - let mut make_client = client::Connect::with_builder(connector, settings); - - let send_text_message = make_client - .make_service(dst.clone()) - .map_err(|e| panic!("connect error: {:?}", e)) - .and_then(move |conn| { - let conn = tower_request_modifier::Builder::new() - .set_origin(uri.clone()) - .build(conn) - .unwrap(); - - // Wait until the client is ready... - WalletRpc::new(conn).ready() - }) - .and_then(|mut client| client.send_text_message(Request::new(msg))) - .and_then(move |response| { - info!(target: LOG_TARGET, "SendTextMessage Response received: {:?}", response); - let inbound = response.into_inner(); - - let _ = tx.send(inbound); - - Ok(()) - }) - .map_err(|e| { - panic!("RPC Client error = {:?}", e); - }); - - tokio::run(send_text_message); - thread::sleep(Duration::from_millis(100)); - - let inbound = rx.recv().unwrap(); - - println!("{:?}", inbound); - println!("{:?}", desired_response); - assert_eq!(inbound.success, desired_response.success); - assert_eq!(inbound.message, desired_response.message); -} - -fn get_text_messages_request(sent_messages: Vec, received_messages: Vec, contact: Option) { - let mut recv_msg: Vec = Vec::new(); - let mut send_msg: Vec = Vec::new(); - - // Check for new text messages up to 40 times with 100ms wait in between = 4000ms Timeout before moving on - for _ in 0..40 { - let move_contact = contact.clone(); - let (tx, rx) = bounded(2); - let uri: http::Uri = format!("http://127.0.0.1:{}", WALLET_GRPC_PORT).parse().unwrap(); - let dst = Destination::try_from_uri(uri.clone()).unwrap(); - let connector = util::Connector::new(HttpConnector::new(1)); - let settings = client::Builder::new().http2_only(true).clone(); - - let mut make_client = client::Connect::with_builder(connector, settings.clone()); - let get_text_messages = make_client - .make_service(dst.clone()) - .map_err(|e| panic!("connect error: {:?}", e)) - .and_then(move |conn| { - let conn = tower_request_modifier::Builder::new() - .set_origin(uri.clone()) - .build(conn) - .unwrap(); - - // Wait until the client is ready... - WalletRpc::new(conn).ready() - }) - .and_then(|mut client| { - if move_contact.is_some() { - client.get_text_messages_by_contact(Request::new(move_contact.unwrap())) - } else { - client.get_text_messages(Request::new(VoidParams {})) - } - }) - .and_then(move |response| { - info!(target: LOG_TARGET, "GetTextMessages Response received: {:?}", response); - let inbound = response.into_inner(); - - let recv_msg = inbound.received_messages.iter().map(|m| m.message.clone()).collect(); - - let sent_msg = inbound.sent_messages.iter().map(|m| m.message.clone()).collect(); - - let _ = tx.send(recv_msg); - let _ = tx.send(sent_msg); - - Ok(()) - }) - .map_err(|e| { - panic!("RPC Client error = {:?}", e); - }); - - tokio::run(get_text_messages); - - recv_msg = rx.recv().unwrap(); - send_msg = rx.recv().unwrap(); - - if recv_msg.len() > 0 && send_msg.len() > 0 { - break; - } - thread::sleep(Duration::from_millis(100)); - } - - assert_eq!(recv_msg.len(), received_messages.len()); - assert_eq!(send_msg.len(), sent_messages.len()); - - recv_msg - .iter() - .for_each(|m| assert!(received_messages.iter().any(|m2| m == m2))); - send_msg - .iter() - .for_each(|m| assert!(sent_messages.iter().any(|m2| m == m2))); -} - -fn set_get_screen_name(name: String) { - let requested_name = name.clone(); - let uri: http::Uri = format!("http://127.0.0.1:{}", WALLET_GRPC_PORT).parse().unwrap(); - - let dst = Destination::try_from_uri(uri.clone()).unwrap(); - let connector = util::Connector::new(HttpConnector::new(1)); - let settings = client::Builder::new().http2_only(true).clone(); - let mut make_client = client::Connect::with_builder(connector, settings.clone()); - - let set_screen_name = make_client - .make_service(dst.clone()) - .map_err(|e| panic!("connect error: {:?}", e)) - .and_then(move |conn| { - let conn = tower_request_modifier::Builder::new() - .set_origin(uri.clone()) - .build(conn) - .unwrap(); - - // Wait until the client is ready... - WalletRpc::new(conn).ready() - }) - .and_then(|mut client| client.set_screen_name(Request::new(ScreenNameRpc { screen_name: name }))) - .and_then(move |response| { - info!(target: LOG_TARGET, "SetScreenName Response received: {:?}", response); - let inbound = response.into_inner(); - assert_eq!(inbound.success, true); - Ok(()) - }) - .map_err(|e| { - panic!("RPC Client error = {:?}", e); - }); - - tokio::run(set_screen_name); - thread::sleep(Duration::from_millis(100)); - let (tx, rx) = bounded(1); - let uri: http::Uri = format!("http://127.0.0.1:{}", WALLET_GRPC_PORT).parse().unwrap(); - let connector = util::Connector::new(HttpConnector::new(1)); - let settings = client::Builder::new().http2_only(true).clone(); - let mut make_client = client::Connect::with_builder(connector, settings); - - let get_screen_name = make_client - .make_service(dst.clone()) - .map_err(|e| panic!("connect error: {:?}", e)) - .and_then(move |conn| { - let conn = tower_request_modifier::Builder::new() - .set_origin(uri.clone()) - .build(conn) - .unwrap(); - - // Wait until the client is ready... - WalletRpc::new(conn).ready() - }) - .and_then(|mut client| client.get_screen_name(Request::new(VoidParams {}))) - .and_then(move |response| { - info!(target: LOG_TARGET, "GetScreenName Response received: {:?}", response); - - let _ = tx.send(response.into_inner()); - - Ok(()) - }) - .map_err(|e| { - panic!("RPC Client error = {:?}", e); - }); - - tokio::run(get_screen_name); - - let recv_screen_name = rx.recv().unwrap(); - assert_eq!(recv_screen_name.screen_name, requested_name); -} - -fn get_pub_key(pub_key: String) { - let (tx, rx) = bounded(1); - let uri: http::Uri = format!("http://127.0.0.1:{}", WALLET_GRPC_PORT).parse().unwrap(); - let dst = Destination::try_from_uri(uri.clone()).unwrap(); - let connector = util::Connector::new(HttpConnector::new(1)); - let settings = client::Builder::new().http2_only(true).clone(); - let mut make_client = client::Connect::with_builder(connector, settings); - - let get_pub_key = make_client - .make_service(dst.clone()) - .map_err(|e| panic!("connect error: {:?}", e)) - .and_then(move |conn| { - let conn = tower_request_modifier::Builder::new() - .set_origin(uri.clone()) - .build(conn) - .unwrap(); - - // Wait until the client is ready... - WalletRpc::new(conn).ready() - }) - .and_then(|mut client| client.get_public_key(Request::new(VoidParams {}))) - .and_then(move |response| { - info!(target: LOG_TARGET, "GetPubKey Response received: {:?}", response); - - let _ = tx.send(response.into_inner()); - - Ok(()) - }) - .map_err(|e| { - panic!("RPC Client error = {:?}", e); - }); - - tokio::run(get_pub_key); - - let recv_pub_key = rx.recv().unwrap(); - assert_eq!(recv_pub_key.pub_key, pub_key); -} - -fn add_contact(contact: ContactRpc) { - let uri: http::Uri = format!("http://127.0.0.1:{}", WALLET_GRPC_PORT).parse().unwrap(); - let dst = Destination::try_from_uri(uri.clone()).unwrap(); - let connector = util::Connector::new(HttpConnector::new(1)); - let settings = client::Builder::new().http2_only(true).clone(); - let mut make_client = client::Connect::with_builder(connector, settings.clone()); - - let add_contact = make_client - .make_service(dst.clone()) - .map_err(|e| panic!("connect error: {:?}", e)) - .and_then(move |conn| { - let conn = tower_request_modifier::Builder::new() - .set_origin(uri.clone()) - .build(conn) - .unwrap(); - - // Wait until the client is ready... - WalletRpc::new(conn).ready() - }) - .and_then(|mut client| client.add_contact(Request::new(contact))) - .and_then(move |response| { - info!(target: LOG_TARGET, "AddContact Response received: {:?}", response); - let inbound = response.into_inner(); - assert_eq!(inbound.success, true); - Ok(()) - }) - .map_err(|e| { - panic!("RPC Client error = {:?}", e); - }); - - tokio::run(add_contact); -} - -fn contacts_crud() { - let mut rng = rand::OsRng::new().unwrap(); - - let mut contacts: Vec = Vec::new(); - let screen_names = vec!["Andy".to_string(), "Bob".to_string(), "Carol".to_string()]; - for i in 0..3 { - let contact_secret_key = CommsSecretKey::random(&mut rng); - let contact_public_key = CommsPublicKey::from_secret_key(&contact_secret_key); - contacts.push(ContactRpc { - screen_name: screen_names[i].clone(), - pub_key: contact_public_key.to_hex(), - address: "127.0.0.1:37522".to_string(), - }); - } - - add_contact(contacts[0].clone()); - thread::sleep(Duration::from_millis(50)); - - add_contact(contacts[1].clone()); - thread::sleep(Duration::from_millis(50)); - - add_contact(contacts[2].clone()); - thread::sleep(Duration::from_millis(50)); - - // Remove a contact - let move_contact = contacts[1].clone(); - let uri: http::Uri = format!("http://127.0.0.1:{}", WALLET_GRPC_PORT).parse().unwrap(); - let dst = Destination::try_from_uri(uri.clone()).unwrap(); - let connector = util::Connector::new(HttpConnector::new(1)); - let settings = client::Builder::new().http2_only(true).clone(); - let mut make_client = client::Connect::with_builder(connector, settings); - - let remove_contact = make_client - .make_service(dst.clone()) - .map_err(|e| panic!("connect error: {:?}", e)) - .and_then(move |conn| { - let conn = tower_request_modifier::Builder::new() - .set_origin(uri.clone()) - .build(conn) - .unwrap(); - - // Wait until the client is ready... - WalletRpc::new(conn).ready() - }) - .and_then(|mut client| client.remove_contact(Request::new(move_contact))) - .and_then(move |response| { - info!(target: LOG_TARGET, "RemoveContact Response received: {:?}", response); - - let inbound = response.into_inner(); - assert_eq!(inbound.success, true); - - Ok(()) - }) - .map_err(|e| { - panic!("RPC Client error = {:?}", e); - }); - - tokio::run(remove_contact); - thread::sleep(Duration::from_millis(100)); - - // Update a contact - let updated_contact = ContactRpc { - screen_name: "Updated".to_string(), - pub_key: contacts[0].pub_key.clone(), - address: contacts[0].address.clone(), - }; - let uri: http::Uri = format!("http://127.0.0.1:{}", WALLET_GRPC_PORT).parse().unwrap(); - let dst = Destination::try_from_uri(uri.clone()).unwrap(); - let connector = util::Connector::new(HttpConnector::new(1)); - let settings = client::Builder::new().http2_only(true).clone(); - let mut make_client = client::Connect::with_builder(connector, settings); - - let update_contact = make_client - .make_service(dst.clone()) - .map_err(|e| panic!("connect error: {:?}", e)) - .and_then(move |conn| { - let conn = tower_request_modifier::Builder::new() - .set_origin(uri.clone()) - .build(conn) - .unwrap(); - - // Wait until the client is ready... - WalletRpc::new(conn).ready() - }) - .and_then(|mut client| client.update_contact(Request::new(updated_contact))) - .and_then(move |response| { - info!(target: LOG_TARGET, "UpdateContact Response received: {:?}", response); - - let inbound = response.into_inner(); - assert_eq!(inbound.success, true); - - Ok(()) - }) - .map_err(|e| { - panic!("RPC Client error = {:?}", e); - }); - - tokio::run(update_contact); - thread::sleep(Duration::from_millis(100)); - - // check contacts - let (tx, rx) = bounded(1); - let uri: http::Uri = format!("http://127.0.0.1:{}", WALLET_GRPC_PORT).parse().unwrap(); - let dst = Destination::try_from_uri(uri.clone()).unwrap(); - let connector = util::Connector::new(HttpConnector::new(1)); - let settings = client::Builder::new().http2_only(true).clone(); - let mut make_client = client::Connect::with_builder(connector, settings); - - let get_contacts = make_client - .make_service(dst.clone()) - .map_err(|e| panic!("connect error: {:?}", e)) - .and_then(move |conn| { - let conn = tower_request_modifier::Builder::new() - .set_origin(uri.clone()) - .build(conn) - .unwrap(); - - // Wait until the client is ready... - WalletRpc::new(conn).ready() - }) - .and_then(|mut client| client.get_contacts(Request::new(VoidParams {}))) - .and_then(move |response| { - info!(target: LOG_TARGET, "RemoveContact Response received: {:?}", response); - - let _ = tx.send(response.into_inner()); - - Ok(()) - }) - .map_err(|e| { - panic!("RPC Client error = {:?}", e); - }); - - tokio::run(get_contacts); - - let recv_contacts = rx.recv().unwrap(); - assert_eq!(recv_contacts.contacts.len(), 3); - assert_eq!(recv_contacts.contacts[1], ContactRpc { - screen_name: "Updated".to_string(), - pub_key: contacts[0].pub_key.clone(), - address: contacts[0].address.clone(), - }); - assert_eq!(recv_contacts.contacts[2], contacts[2]); -} - -fn create_peer(public_key: CommsPublicKey, net_address: NetAddress) -> Peer { - Peer::new( - public_key.clone(), - NodeId::from_key(&public_key).unwrap(), - NetAddressesWithStats::new(vec![NetAddressWithStats::new(net_address.clone())]), - PeerFlags::empty(), - ) -} - -pub fn random_string(len: usize) -> String { - let mut rng = OsRng::new().unwrap(); - iter::repeat(()).map(|_| rng.sample(Alphanumeric)).take(len).collect() -} - -#[test] -fn test_rpc_text_message_service() { - init(); - let mut rng = rand::OsRng::new().unwrap(); - let listener_address1: NetAddress = "127.0.0.1:32775".parse().unwrap(); - let secret_key1 = CommsSecretKey::random(&mut rng); - let public_key1 = CommsPublicKey::from_secret_key(&secret_key1); - - let listener_address2: NetAddress = "127.0.0.1:32776".parse().unwrap(); - let secret_key2 = CommsSecretKey::random(&mut rng); - let public_key2 = CommsPublicKey::from_secret_key(&secret_key2); - - let db_name1 = "test_rpc_text_message_service1.sqlite3"; - let db_path1 = get_path(Some(db_name1)); - init_sql_database(db_name1); - - let db_name2 = "test_rpc_text_message_service2.sqlite3"; - let db_path2 = get_path(Some(db_name2)); - init_sql_database(db_name2); - - let config1 = WalletConfig { - comms: CommsConfig { - control_service: ControlServiceConfig { - listener_address: listener_address1.clone(), - socks_proxy_address: None, - requested_connection_timeout: Duration::from_millis(5000), - }, - socks_proxy_address: None, - host: "127.0.0.1".parse().unwrap(), - public_key: public_key1.clone(), - secret_key: secret_key1, - public_address: listener_address1.clone(), - datastore_path: TempDir::new(random_string(8).as_str()) - .unwrap() - .path() - .to_str() - .unwrap() - .to_string(), - peer_database_name: random_string(8), - }, - public_key: public_key1.clone(), - database_path: db_path1, - }; - - let config2 = WalletConfig { - comms: CommsConfig { - control_service: ControlServiceConfig { - listener_address: listener_address2.clone(), - socks_proxy_address: None, - requested_connection_timeout: Duration::from_millis(5000), - }, - socks_proxy_address: None, - host: "127.0.0.1".parse().unwrap(), - public_key: public_key2.clone(), - secret_key: secret_key2, - public_address: listener_address2.clone(), - datastore_path: TempDir::new(random_string(8).as_str()) - .unwrap() - .path() - .to_str() - .unwrap() - .to_string(), - peer_database_name: random_string(8), - }, - public_key: public_key2.clone(), - database_path: db_path2, - }; - - let wallet1 = Wallet::new(config1).unwrap(); - - thread::spawn(move || { - let wallet_server = WalletServer::new(WALLET_GRPC_PORT, Arc::new(wallet1)); - let _ = wallet_server.start().unwrap(); - }); - - let screen_name = "Bob".to_string(); - let bob_contact = ContactRpc { - screen_name: screen_name.clone(), - pub_key: public_key2.to_hex(), - address: format!("{}", listener_address2.clone()), - }; - - add_contact(bob_contact.clone()); - - thread::sleep(Duration::from_millis(100)); - - let wallet2 = Wallet::new(config2).unwrap(); - - wallet2 - .comms_services - .peer_manager() - .add_peer(create_peer(public_key1.clone(), listener_address1.clone())) - .unwrap(); - let alice_contact = Contact { - screen_name: "Alice".to_string(), - pub_key: public_key1.clone(), - address: listener_address1.clone(), - }; - wallet2.text_message_service.add_contact(alice_contact).unwrap(); - - let test_msg = TextMessageToSendRpc { - dest_pub_key: public_key2.clone().to_hex(), - message: "Hey!".to_string(), - }; - - let test_msg2 = TextMessageToSendRpc { - dest_pub_key: public_key2.clone().to_hex(), - message: "Hoh!".to_string(), - }; - - let resp = RpcResponse { - success: true, - message: "Text Message Sent".to_string(), - }; - - send_text_message_request(test_msg, resp.clone()); - send_text_message_request(test_msg2, resp); - - wallet2 - .text_message_service - .send_text_message(public_key1.clone(), "Here we go!".to_string()) - .unwrap(); - - let sent_messages = vec!["Hey!".to_string(), "Hoh!".to_string()]; - let received_messages = vec!["Here we go!".to_string()]; - - get_text_messages_request(sent_messages.clone(), received_messages.clone(), None); - get_text_messages_request(sent_messages, received_messages, Some(bob_contact)); - set_get_screen_name("Alice".to_string()); - get_pub_key(public_key1.to_hex()); - contacts_crud(); - clean_up_sql_database(db_name1); - clean_up_sql_database(db_name2); -} diff --git a/applications/tari_base_node/Cargo.toml b/applications/tari_base_node/Cargo.toml new file mode 100644 index 0000000000..9cc19181d9 --- /dev/null +++ b/applications/tari_base_node/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "tari_base_node" +authors = ["The Tari Development Community"] +description = "The tari full base node implementation" +repository = "https://github.com/tari-project/tari" +license = "BSD-3-Clause" +version = "0.0.9" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tari_common = {path = "../../common", version= "^0.0"} +tari_comms = { version = "^0.0", path = "../../comms"} +tari_comms_dht = { version = "^0.0", path = "../../comms/dht"} +tari_core = {path = "../../base_layer/core", version= "^0.0"} +tari_p2p = {path = "../../base_layer/p2p", version= "^0.0"} +tari_service_framework = { version = "^0.0", path = "../../base_layer/service_framework"} +tari_shutdown = { path = "../../infrastructure/shutdown", version = "^0.0" } +tari_mmr = { path = "../../base_layer/mmr", version = "^0.0" } +tari_wallet = { path = "../../base_layer/wallet", version = "^0.0" } +tari_broadcast_channel = "^0.1" + +clap = "2.33.0" +config = { version = "0.9.3" } +dirs = "2.0.2" +futures = { version = "^0.3.1", default-features = false, features = ["alloc"]} +log = { version = "0.4.8", features = ["std"] } +log4rs = { version = "0.8.3", features = ["toml_format"] } +rand = "0.7.2" +serde_json = "1.0" +tokio = { version="0.2.10", features = ["signal"] } +rustyline = "6.0" +rustyline-derive = "0.3" +strum = "0.17.1" +strum_macros = "0.17.1" diff --git a/applications/tari_base_node/README.md b/applications/tari_base_node/README.md new file mode 100644 index 0000000000..6e50c5cd62 --- /dev/null +++ b/applications/tari_base_node/README.md @@ -0,0 +1,11 @@ +# Tari base node + +## Installation + +### Prerequisites + +### From source + + cargo install tari_base_node + +## Configuration diff --git a/applications/tari_base_node/src/builder.rs b/applications/tari_base_node/src/builder.rs new file mode 100644 index 0000000000..24463f99f4 --- /dev/null +++ b/applications/tari_base_node/src/builder.rs @@ -0,0 +1,640 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::miner; +use log::*; +use rand::rngs::OsRng; +use std::{ + fs, + path::{Path, PathBuf}, + sync::{atomic::AtomicBool, Arc}, + time::Duration, +}; +use tari_common::{CommsTransport, DatabaseType, GlobalConfig, Network, SocksAuthentication, TorControlAuthentication}; +use tari_comms::{ + multiaddr::Multiaddr, + peer_manager::{NodeId, NodeIdentity, Peer, PeerFeatures, PeerFlags}, + socks, + tor, + tor::TorIdentity, + utils::multiaddr::multiaddr_to_socketaddr, + CommsNode, +}; +use tari_comms_dht::Dht; +use tari_core::{ + base_node::{ + chain_metadata_service::{ChainMetadataHandle, ChainMetadataServiceInitializer}, + service::{BaseNodeServiceConfig, BaseNodeServiceInitializer}, + BaseNodeStateMachine, + BaseNodeStateMachineConfig, + LocalNodeCommsInterface, + OutboundNodeCommsInterface, + }, + chain_storage::{ + create_lmdb_database, + BlockchainBackend, + BlockchainDatabase, + LMDBDatabase, + MemoryDatabase, + Validators, + }, + consensus::{ConsensusManager, ConsensusManagerBuilder, Network as NetworkType}, + mempool::{Mempool, MempoolConfig, MempoolValidators}, + mining::Miner, + proof_of_work::DiffAdjManager, + tari_utilities::{hex::Hex, message_format::MessageFormat}, + transactions::{ + crypto::keys::SecretKey as SK, + types::{CryptoFactories, HashDigest, PrivateKey, PublicKey}, + }, + validation::{ + block_validators::{FullConsensusValidator, StatelessValidator}, + transaction_validators::{FullTxValidator, TxInputAndMaturityValidator}, + }, +}; +use tari_mmr::MmrCacheConfig; +use tari_p2p::{ + comms_connector::{pubsub_connector, PubsubDomainConnector, SubscriptionFactory}, + initialization::{initialize_comms, CommsConfig}, + services::{ + comms_outbound::CommsOutboundServiceInitializer, + liveness::{LivenessConfig, LivenessInitializer}, + }, + transport::{TorConfig, TransportType}, +}; +use tari_service_framework::{handles::ServiceHandles, StackBuilder}; +use tari_wallet::{ + output_manager_service::{ + config::OutputManagerServiceConfig, + handle::OutputManagerHandle, + storage::sqlite_db::OutputManagerSqliteDatabase, + OutputManagerServiceInitializer, + }, + storage::connection_manager::{run_migration_and_create_connection_pool, WalletConnection}, + transaction_service::{ + config::TransactionServiceConfig, + handle::TransactionServiceHandle, + storage::sqlite_db::TransactionServiceSqliteDatabase, + TransactionServiceInitializer, + }, +}; +use tokio::{runtime, stream::StreamExt}; + +const LOG_TARGET: &str = "base_node::initialization"; + +#[macro_export] +macro_rules! using_backend { + ($self:expr, $i: ident, $cmd: expr) => { + match $self { + NodeContainer::LMDB($i) => $cmd, + NodeContainer::Memory($i) => $cmd, + } + }; +} + +/// The type of DB is configured dynamically in the config file, but the state machine struct has static dispatch; +/// and so we have to use an enum wrapper to hold the various acceptable types. +pub enum NodeContainer { + LMDB(BaseNodeContext>), + Memory(BaseNodeContext>), +} + +impl NodeContainer { + /// Starts the node container. This entails starting the miner and wallet (if `mining_enabled` is true) and then + /// starting the base node state machine. This call consumes the NodeContainer instance. + pub async fn run(self, rt: runtime::Handle) { + using_backend!(self, ctx, NodeContainer::run_impl(ctx, rt).await) + } + + pub fn interrupt_flag(&self) -> Arc { + using_backend!(self, ctx, ctx.node.get_interrupt_flag()) + } + + /// Returns a handle to the wallet output manager service. This function panics if it has not been registered + /// with the comms service + pub fn output_manager(&self) -> OutputManagerHandle { + using_backend!(self, ctx, ctx.output_manager()) + } + + /// Returns a handle to the local node communication service. This function panics if it has not been registered + /// with the comms service + pub fn local_node(&self) -> LocalNodeCommsInterface { + using_backend!(self, ctx, ctx.local_node()) + } + + /// Returns a handle to the wallet transaction service. This function panics if it has not been registered + /// with the comms service + pub fn wallet_transaction_service(&self) -> TransactionServiceHandle { + using_backend!(self, ctx, ctx.wallet_transaction_service()) + } + + async fn run_impl(mut ctx: BaseNodeContext, rt: runtime::Handle) { + info!(target: LOG_TARGET, "Tari base node has STARTED"); + let mut wallet_output_handle = ctx.output_manager(); + // Start wallet & miner + if let Some(mut miner) = ctx.miner.take() { + let mut rx = miner.get_utxo_receiver_channel(); + rt.spawn(async move { + debug!(target: LOG_TARGET, "Mining wallet ready to receive coins."); + while let Some(utxo) = rx.next().await { + match wallet_output_handle.add_output(utxo).await { + Ok(_) => info!( + target: LOG_TARGET, + "🤑💰🤑 Newly mined coinbase output added to wallet 🤑💰🤑" + ), + Err(e) => warn!(target: LOG_TARGET, "Error adding output: {}", e), + } + } + }); + rt.spawn(async move { + debug!(target: LOG_TARGET, "Starting miner"); + miner.mine().await; + debug!(target: LOG_TARGET, "Miner has shutdown"); + }); + } + info!( + target: LOG_TARGET, + "Starting node - It will run until a fatal error occurs or until the stop flag is activated." + ); + ctx.node.run().await; + info!(target: LOG_TARGET, "Initiating communications stack shutdown"); + ctx.comms.shutdown().await + } +} + +pub struct BaseNodeContext { + pub comms: CommsNode, + pub handles: Arc, + pub node: BaseNodeStateMachine, + pub miner: Option>, +} + +impl BaseNodeContext { + pub fn output_manager(&self) -> OutputManagerHandle { + self.handles + .get_handle::() + .expect("Problem getting wallet output manager handle") + } + + pub fn local_node(&self) -> LocalNodeCommsInterface { + self.handles + .get_handle::() + .expect("Could not get local comms interface handle") + } + + pub fn wallet_transaction_service(&self) -> TransactionServiceHandle { + self.handles + .get_handle::() + .expect("Could not get wallet transaction service handle") + } +} + +/// Tries to construct a node identity by loading the secret key and other metadata from disk and calculating the +/// missing fields from that information. +pub fn load_identity(path: &Path) -> Result { + if !path.exists() { + return Err(format!("Identity file, {}, does not exist.", path.to_str().unwrap())); + } + + let id_str = std::fs::read_to_string(path).map_err(|e| { + format!( + "The node identity file, {}, could not be read. {}", + path.to_str().unwrap_or("?"), + e.to_string() + ) + })?; + let id = NodeIdentity::from_json(&id_str).map_err(|e| { + format!( + "The node identity file, {}, has an error. {}", + path.to_str().unwrap_or("?"), + e.to_string() + ) + })?; + info!( + "Node ID loaded with public key {} and Node id {}", + id.public_key().to_hex(), + id.node_id().to_hex() + ); + Ok(id) +} + +/// Create a new node id and save it to disk +pub fn create_new_base_node_identity(public_addr: Multiaddr) -> Result { + let private_key = PrivateKey::random(&mut OsRng); + let features = PeerFeatures::COMMUNICATION_NODE; + NodeIdentity::new(private_key, public_addr, features) + .map_err(|e| format!("We were unable to construct a node identity. {}", e.to_string())) +} + +pub fn load_from_json, T: MessageFormat>(path: P) -> Result { + if !path.as_ref().exists() { + return Err(format!( + "Identity file, {}, does not exist.", + path.as_ref().to_str().unwrap() + )); + } + + let contents = fs::read_to_string(path).map_err(|err| err.to_string())?; + let object = T::from_json(&contents).map_err(|err| err.to_string())?; + Ok(object) +} + +pub fn save_as_json, T: MessageFormat>(path: P, object: &T) -> Result<(), String> { + let json = object.to_json().unwrap(); + if let Some(p) = path.as_ref().parent() { + if !p.exists() { + fs::create_dir_all(p).map_err(|e| format!("Could not save json to data folder. {}", e.to_string()))?; + } + } + fs::write(path.as_ref(), json.as_bytes()).map_err(|e| { + format!( + "Error writing json file, {}. {}", + path.as_ref().to_str().unwrap_or(""), + e.to_string() + ) + })?; + + Ok(()) +} + +pub async fn configure_and_initialize_node( + config: &GlobalConfig, + node_identity: NodeIdentity, +) -> Result +{ + let network = match &config.network { + Network::MainNet => NetworkType::MainNet, + Network::Rincewind => NetworkType::Rincewind, + }; + let id = Arc::new(node_identity); + let result = match &config.db_type { + DatabaseType::Memory => { + let backend = MemoryDatabase::::default(); + let ctx = build_node_context(backend, network, id, config).await?; + NodeContainer::Memory(ctx) + }, + DatabaseType::LMDB(p) => { + let backend = create_lmdb_database(&p, MmrCacheConfig::default()).map_err(|e| e.to_string())?; + let ctx = build_node_context(backend, network, id, config).await?; + NodeContainer::LMDB(ctx) + }, + }; + Ok(result) +} + +async fn build_node_context( + backend: B, + network: NetworkType, + id: Arc, + config: &GlobalConfig, +) -> Result, String> +where + B: BlockchainBackend + 'static, +{ + let rules = ConsensusManagerBuilder::new(network).build(); + let mut db = BlockchainDatabase::new(backend, rules.clone()).map_err(|e| e.to_string())?; + let factories = CryptoFactories::default(); + let validators = Validators::new( + FullConsensusValidator::new(rules.clone(), factories.clone(), db.clone()), + StatelessValidator::new(&rules.consensus_constants()), + ); + db.set_validators(validators); + let mempool_validator = MempoolValidators::new( + FullTxValidator::new(factories.clone(), db.clone()), + TxInputAndMaturityValidator::new(db.clone()), + ); + let mempool = Mempool::new(db.clone(), MempoolConfig::default(), mempool_validator); + let diff_adj_manager = DiffAdjManager::new(db.clone(), &rules.consensus_constants()).map_err(|e| e.to_string())?; + rules.set_diff_manager(diff_adj_manager).map_err(|e| e.to_string())?; + create_peer_db_folder(&config.peer_db_path)?; + let handle = runtime::Handle::current().clone(); + let (publisher, subscription_factory) = pubsub_connector(handle, 100); + let comms_config = CommsConfig { + node_identity: id.clone(), + transport_type: setup_transport_type(&config), + datastore_path: config.peer_db_path.clone(), + peer_database_name: "peers".to_string(), + max_concurrent_inbound_tasks: 100, + outbound_buffer_size: 100, + // TODO - make this configurable + dht: Default::default(), + }; + let (comms, dht) = setup_comms_services(comms_config, publisher).await?; + // Save final node identity after comms has initialized. This is required because the public_address can be changed + // by comms during initialization when using tor. + save_as_json(&config.identity_file, &*comms.node_identity()) + .map_err(|e| format!("Failed to save node identity: {}", e))?; + if let Some(hs) = comms.hidden_service() { + save_as_json(&config.tor_identity_file, &hs.get_tor_identity()) + .map_err(|e| format!("Failed to save tor identity: {}", e))?; + } + add_peers_to_comms(&comms, assign_peers(&config.peer_seeds))?; + create_wallet_folder(&config.wallet_file)?; + let wallet_conn = run_migration_and_create_connection_pool(&config.wallet_file) + .map_err(|e| format!("Could not create wallet: {}", e))?; + debug!(target: LOG_TARGET, "Registering base node services"); + let handles = register_services( + id.clone(), + &comms, + &dht, + db.clone(), + &wallet_conn, + subscription_factory, + mempool, + rules.clone(), + factories, + ) + .await; + debug!(target: LOG_TARGET, "Base node service registration complete."); + let outbound_interface = handles + .get_handle::() + .expect("Problem getting node interface handle"); + let chain_metadata_service = handles + .get_handle::() + .expect("Problem getting chain metadata interface handle"); + debug!(target: LOG_TARGET, "Creating base node state machine."); + let node = BaseNodeStateMachine::new( + &db, + &outbound_interface, + runtime::Handle::current().clone(), + chain_metadata_service.get_event_stream(), + BaseNodeStateMachineConfig::default(), + ); + let miner = if config.enable_mining { + debug!(target: LOG_TARGET, "Configuring solo miner"); + let event_stream = node.get_state_change_event_stream(); + Some(miner::build_miner( + &handles, + node.get_interrupt_flag(), + event_stream, + rules, + )) + } else { + debug!( + target: LOG_TARGET, + "Mining is disabled in the config file. This node will not mine for Tari" + ); + None + }; + Ok(BaseNodeContext { + comms, + handles, + node, + miner, + }) +} + +fn assign_peers(seeds: &[String]) -> Vec { + info!("Adding {} peers to the peer database", seeds.len()); + let mut result = Vec::with_capacity(seeds.len()); + for s in seeds { + let parts: Vec<&str> = s.split("::").map(|s| s.trim()).collect(); + if parts.len() != 2 { + warn!(target: LOG_TARGET, "Invalid peer seed: {}", s); + continue; + } + let pub_key = match PublicKey::from_hex(parts[0]) { + Err(e) => { + warn!( + target: LOG_TARGET, + "{} is not a valid peer seed. The public key is incorrect. {}", + s, + e.to_string() + ); + continue; + }, + Ok(p) => p, + }; + let addr = match parts[1].parse::() { + Err(e) => { + warn!( + target: LOG_TARGET, + "{} is not a valid peer seed. The address is incorrect. {}", + s, + e.to_string() + ); + continue; + }, + Ok(a) => a, + }; + let node_id = match NodeId::from_key(&pub_key) { + Err(e) => { + warn!( + target: LOG_TARGET, + "{} is not a valid peer seed. A node id couldn't be derived from the public key. {}", + s, + e.to_string() + ); + continue; + }, + Ok(id) => id, + }; + let peer = Peer::new( + pub_key, + node_id, + addr.into(), + PeerFlags::default(), + PeerFeatures::COMMUNICATION_NODE, + ); + result.push(peer); + } + result +} + +fn setup_transport_type(config: &GlobalConfig) -> TransportType { + match config.comms_transport.clone() { + CommsTransport::Tcp { listener_address } => TransportType::Tcp { listener_address }, + CommsTransport::TorHiddenService { + control_server_address, + forward_address, + auth, + onion_port, + } => { + let tor_identity_path = Path::new(&config.tor_identity_file); + let identity = if tor_identity_path.exists() { + // If this fails, we can just use another address + load_from_json::<_, TorIdentity>(&tor_identity_path).ok() + } else { + None + }; + info!( + target: LOG_TARGET, + "Tor identity at path '{}' {:?}", + tor_identity_path.to_string_lossy(), + identity + .as_ref() + .map(|ident| format!("loaded for address '{}.onion'", ident.service_id)) + .or_else(|| Some("not found".to_string())) + .unwrap() + ); + + let forward_addr = multiaddr_to_socketaddr(&forward_address).expect("Invalid tor forward address"); + TransportType::Tor(TorConfig { + control_server_addr: control_server_address, + control_server_auth: { + match auth { + TorControlAuthentication::None => tor::Authentication::None, + TorControlAuthentication::Password(password) => { + tor::Authentication::HashedPassword(password.clone()) + }, + } + }, + identity: identity.map(Box::new), + port_mapping: (onion_port, forward_addr).into(), + // TODO: make configurable + socks_auth: socks::Authentication::None, + }) + }, + CommsTransport::Socks5 { + proxy_address, + listener_address, + auth, + } => TransportType::Socks { + proxy_address, + listener_address, + authentication: { + match auth { + SocksAuthentication::None => socks::Authentication::None, + SocksAuthentication::UsernamePassword(username, password) => { + socks::Authentication::Password(username, password) + }, + } + }, + }, + } +} + +fn create_wallet_folder(wallet_file: &str) -> Result<(), String> { + // sql lite for wallet, create folders for sql lite + let mut wallet_db_folder = PathBuf::from(wallet_file); + wallet_db_folder.set_extension("dat"); + let wallet_path = PathBuf::from(wallet_db_folder.parent().expect("unable to get wallet db path")); + match fs::create_dir_all(wallet_path) { + Ok(_) => { + info!( + target: LOG_TARGET, + "Wallet directory has been created in {}", wallet_file + ); + Ok(()) + }, + Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => { + info!(target: LOG_TARGET, "Wallet directory already exists in {}", wallet_file); + Ok(()) + }, + Err(e) => Err(format!("Could not create wallet directory: {}", e)), + } +} + +fn create_peer_db_folder(peer_db_path: &str) -> Result<(), String> { + match fs::create_dir_all(peer_db_path) { + Ok(_) => { + info!( + target: LOG_TARGET, + "Peer database directory has been created in {}", peer_db_path + ); + Ok(()) + }, + Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => { + info!(target: LOG_TARGET, "Peer database already exists in {}", peer_db_path); + Ok(()) + }, + Err(e) => Err(format!("could not create peer db path: {}", e)), + } +} + +async fn setup_comms_services( + config: CommsConfig, + publisher: PubsubDomainConnector, +) -> Result<(CommsNode, Dht), String> +{ + initialize_comms(config, publisher) + .await + .map_err(|e| format!("Could not create comms layer: {}", e)) +} + +fn add_peers_to_comms(comms: &CommsNode, peers: Vec) -> Result<(), String> { + for p in peers { + let peer_desc = p.to_string(); + info!(target: LOG_TARGET, "Adding seed peer [{}]", peer_desc); + comms + .peer_manager() + .add_peer(p) + .map_err(|e| format!("Could not add peer {} to comms layer: {}", peer_desc, e))?; + } + Ok(()) +} + +async fn register_services( + id: Arc, + comms: &CommsNode, + dht: &Dht, + db: BlockchainDatabase, + wallet_conn: &WalletConnection, + subscription_factory: SubscriptionFactory, + mempool: Mempool, + consensus_manager: ConsensusManager, + factories: CryptoFactories, +) -> Arc +where + B: BlockchainBackend + 'static, +{ + let node_config = BaseNodeServiceConfig::default(); // TODO - make this configurable + let subscription_factory = Arc::new(subscription_factory); + let handle = runtime::Handle::current().clone(); + StackBuilder::new(handle, comms.shutdown_signal()) + .add_initializer(CommsOutboundServiceInitializer::new(dht.outbound_requester())) + .add_initializer(BaseNodeServiceInitializer::new( + subscription_factory.clone(), + db, + mempool, + consensus_manager, + node_config, + )) + .add_initializer(OutputManagerServiceInitializer::new( + OutputManagerServiceConfig::default(), + subscription_factory.clone(), + OutputManagerSqliteDatabase::new(wallet_conn.clone()), + factories.clone(), + )) + .add_initializer(TransactionServiceInitializer::new( + TransactionServiceConfig::default(), + subscription_factory.clone(), + comms.subscribe_messaging_events(), + TransactionServiceSqliteDatabase::new(wallet_conn.clone()), + id, + factories, + )) + .add_initializer(LivenessInitializer::new( + LivenessConfig { + auto_ping_interval: Some(Duration::from_secs(5)), + enable_auto_join: true, + enable_auto_stored_message_request: true, + refresh_neighbours_interval: Duration::from_secs(3 * 60), + }, + subscription_factory, + dht.dht_requester(), + )) + .add_initializer(ChainMetadataServiceInitializer) + .finish() + .await + .expect("Service initialization failed") +} diff --git a/applications/tari_base_node/src/cli.rs b/applications/tari_base_node/src/cli.rs new file mode 100644 index 0000000000..824d10dbe9 --- /dev/null +++ b/applications/tari_base_node/src/cli.rs @@ -0,0 +1,60 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +use crate::consts; +use clap::clap_app; +use tari_common::{bootstrap_config_from_cli, ConfigBootstrap}; + +/// Prints a pretty banner on the console +pub fn print_banner() { + println!( + "\n$ Tari Base Node\n$ Copyright 2019-2020. {}\n$ Version {}\n\nPress Ctrl-C to quit..", + consts::AUTHOR, + consts::VERSION + ); +} + +/// Parsed command-line arguments +pub struct Arguments { + pub bootstrap: ConfigBootstrap, + pub create_id: bool, +} + +/// Parse the command-line args and populate the minimal bootstrap config object +pub fn parse_cli_args() -> Arguments { + let matches = clap_app!(myapp => + (version: consts::VERSION) + (author: consts::AUTHOR) + (about: "The reference Tari cryptocurrency base node implementation") + (@arg config: -c --config +takes_value "A path to the configuration file to use (config.toml)") + (@arg log_config: -l --log_config +takes_value "A path to the logfile configuration (log4rs.yml))") + (@arg init: --init "Create a default configuration file if it doesn't exist") + (@arg create_id: --create_id "Create and save new node identity if one doesn't exist ") + ) + .get_matches(); + + let bootstrap = bootstrap_config_from_cli(&matches); + let create_id = matches.is_present("create_id"); + + Arguments { bootstrap, create_id } +} diff --git a/applications/tari_base_node/src/consts.rs b/applications/tari_base_node/src/consts.rs new file mode 100644 index 0000000000..780e23b3ca --- /dev/null +++ b/applications/tari_base_node/src/consts.rs @@ -0,0 +1,25 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +pub const VERSION: &str = "0.0.5"; +pub const AUTHOR: &str = "The Tari Community"; diff --git a/applications/tari_base_node/src/main.rs b/applications/tari_base_node/src/main.rs new file mode 100644 index 0000000000..d4027d60b6 --- /dev/null +++ b/applications/tari_base_node/src/main.rs @@ -0,0 +1,223 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +/// Utilities and helpers for building the base node instance +mod builder; +/// The command line interface definition and configuration +mod cli; +/// Application-specific constants +mod consts; +/// Miner lib Todo hide behind feature flag +mod miner; +/// Parser module used to control user commands +mod parser; + +use crate::builder::{create_new_base_node_identity, load_identity}; +use log::*; +use parser::Parser; +use rustyline::{config::OutputStreamType, error::ReadlineError, CompletionType, Config, EditMode, Editor}; +use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, +}; +use tari_common::{load_configuration, GlobalConfig}; +use tokio::runtime::Runtime; + +pub const LOG_TARGET: &str = "base_node::app"; + +enum ExitCodes { + ConfigError = 101, + UnknownError = 102, +} + +fn main() { + cli::print_banner(); + match main_inner() { + Ok(_) => std::process::exit(0), + Err(exit_code) => std::process::exit(exit_code as i32), + } +} + +fn main_inner() -> Result<(), ExitCodes> { + // Create the tari data directory + if let Err(e) = tari_common::dir_utils::create_data_directory() { + println!( + "We couldn't create a default Tari data directory and have to quit now. This makes us sad :(\n {}", + e.to_string() + ); + return Err(ExitCodes::ConfigError); + } + + // Parse and validate command-line arguments + let arguments = cli::parse_cli_args(); + + // Initialise the logger + if !tari_common::initialize_logging(&arguments.bootstrap.log_config) { + return Err(ExitCodes::ConfigError); + } + + // Load and apply configuration file + let cfg = match load_configuration(&arguments.bootstrap) { + Ok(cfg) => cfg, + Err(s) => { + error!(target: LOG_TARGET, "{}", s); + return Err(ExitCodes::ConfigError); + }, + }; + + // Populate the configuration struct + let node_config = match GlobalConfig::convert_from(cfg) { + Ok(c) => c, + Err(e) => { + error!(target: LOG_TARGET, "The configuration file has an error. {}", e); + return Err(ExitCodes::ConfigError); + }, + }; + + trace!(target: LOG_TARGET, "Configuration file: {:?}", node_config); + + // Load or create the Node identity + let node_identity = match load_identity(&node_config.identity_file) { + Ok(id) => id, + Err(e) => { + if !arguments.create_id { + error!( + target: LOG_TARGET, + "Node identity information not found. {}. You can update the configuration file to point to a \ + valid node identity file, or re-run the node with the --create_id flag to create a new identity.", + e + ); + return Err(ExitCodes::ConfigError); + } + debug!(target: LOG_TARGET, "Node id not found. {}. Creating new ID", e); + match create_new_base_node_identity(node_config.public_address.clone()) { + Ok(id) => { + info!( + target: LOG_TARGET, + "New node identity [{}] with public key {} has been created.", + id.node_id(), + id.public_key() + ); + id + }, + Err(e) => { + error!(target: LOG_TARGET, "Could not create new node id. {}.", e); + return Err(ExitCodes::ConfigError); + }, + } + }, + }; + + // Set up the Tokio runtime + let mut rt = match setup_runtime(&node_config) { + Ok(rt) => rt, + Err(s) => { + error!(target: LOG_TARGET, "{}", s); + return Err(ExitCodes::UnknownError); + }, + }; + + // Build, node, build! + let ctx = rt.block_on(async { + builder::configure_and_initialize_node(&node_config, node_identity) + .await + .map_err(|err| { + error!(target: LOG_TARGET, "{}", err); + ExitCodes::UnknownError + }) + })?; + // Run, node, run! + let parser = Parser::new(rt.handle().clone(), &ctx); + let flag = ctx.interrupt_flag(); + let base_node_handle = rt.spawn(ctx.run(rt.handle().clone())); + info!( + target: LOG_TARGET, + "Node has been successfully configured and initialized. Starting CLI loop." + ); + cli_loop(parser, flag); + match rt.block_on(base_node_handle) { + Ok(_) => info!(target: LOG_TARGET, "Node shutdown successfully."), + Err(e) => error!(target: LOG_TARGET, "Node has crashed: {}", e), + } + println!("Goodbye!"); + Ok(()) +} + +fn setup_runtime(config: &GlobalConfig) -> Result { + let num_core_threads = config.core_threads; + let num_blocking_threads = config.blocking_threads; + + debug!( + target: LOG_TARGET, + "Configuring the node to run on {} core threads and {} blocking worker threads.", + num_core_threads, + num_blocking_threads + ); + tokio::runtime::Builder::new() + .threaded_scheduler() + .enable_all() + .max_threads(num_core_threads + num_blocking_threads) + .core_threads(num_core_threads) + .build() + .map_err(|e| format!("There was an error while building the node runtime. {}", e.to_string())) +} + +fn cli_loop(parser: Parser, shutdown_flag: Arc) { + let cli_config = Config::builder() + .history_ignore_space(true) + .completion_type(CompletionType::List) + .edit_mode(EditMode::Emacs) + .output_stream(OutputStreamType::Stdout) + .build(); + let mut rustyline = Editor::with_config(cli_config); + rustyline.set_helper(Some(parser)); + loop { + let readline = rustyline.readline(">> "); + match readline { + Ok(line) => { + rustyline.add_history_entry(line.as_str()); + if let Some(p) = rustyline.helper_mut().as_deref_mut() { + p.handle_command(&line) + } + }, + Err(ReadlineError::Interrupted) => { + // shutdown section. Will shutdown all interfaces when ctrl-c was pressed + println!("CTRL-C received"); + println!("Shutting down"); + info!( + target: LOG_TARGET, + "Termination signal received from user. Shutting node down." + ); + shutdown_flag.store(true, Ordering::SeqCst); + break; + }, + Err(err) => { + println!("Error: {:?}", err); + break; + }, + } + if shutdown_flag.load(Ordering::Relaxed) { + break; + }; + } +} diff --git a/applications/tari_base_node/src/miner.rs b/applications/tari_base_node/src/miner.rs new file mode 100644 index 0000000000..a7980c174f --- /dev/null +++ b/applications/tari_base_node/src/miner.rs @@ -0,0 +1,47 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +use core::sync::atomic::AtomicBool; +use std::sync::Arc; +use tari_broadcast_channel::Subscriber; +use tari_core::{ + base_node::{states::BaseNodeState, LocalNodeCommsInterface}, + chain_storage::BlockchainBackend, + consensus::ConsensusManager, + mining::Miner, +}; +use tari_service_framework::handles::ServiceHandles; + +pub fn build_miner>( + handles: H, + stop_flag: Arc, + event_stream: Subscriber, + consensus_manager: ConsensusManager, +) -> Miner +{ + let handles = handles.as_ref(); + let node_local_interface = handles.get_handle::().unwrap(); + let mut miner = Miner::new(stop_flag, consensus_manager, &node_local_interface); + miner.subscribe_to_state_change(event_stream); + miner +} diff --git a/applications/tari_base_node/src/parser.rs b/applications/tari_base_node/src/parser.rs new file mode 100644 index 0000000000..8e9e4b31f5 --- /dev/null +++ b/applications/tari_base_node/src/parser.rs @@ -0,0 +1,268 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::LOG_TARGET; +use crate::builder::NodeContainer; +use log::*; +use rustyline::{ + completion::Completer, + error::ReadlineError, + hint::{Hinter, HistoryHinter}, + line_buffer::LineBuffer, + Context, +}; +use rustyline_derive::{Helper, Highlighter, Validator}; +use std::{ + str::FromStr, + string::ToString, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, +}; +use strum::IntoEnumIterator; +use strum_macros::{Display, EnumIter, EnumString}; +use tari_comms::types::CommsPublicKey; +use tari_core::{ + base_node::LocalNodeCommsInterface, + tari_utilities::hex::Hex, + transactions::tari_amount::{uT, MicroTari}, +}; +use tari_wallet::{ + output_manager_service::handle::OutputManagerHandle, + transaction_service::handle::TransactionServiceHandle, +}; +use tokio::runtime; + +/// Enum representing commands used by the basenode +#[derive(Clone, PartialEq, Debug, Display, EnumIter, EnumString)] +#[strum(serialize_all = "snake_case")] +pub enum BaseNodeCommand { + Help, + GetBalance, + SendTari, + GetChainMetadata, + Quit, + Exit, +} + +/// This is used to parse commands from the user and execute them +#[derive(Helper, Validator, Highlighter)] +pub struct Parser { + executor: runtime::Handle, + shutdown_flag: Arc, + commands: Vec, + hinter: HistoryHinter, + wallet_output_service: OutputManagerHandle, + node_service: LocalNodeCommsInterface, + wallet_transaction_service: TransactionServiceHandle, +} + +// This will go through all instructions and look for potential matches +impl Completer for Parser { + type Candidate = String; + + fn complete(&self, line: &str, pos: usize, _ctx: &Context<'_>) -> Result<(usize, Vec), ReadlineError> { + let mut completions: Vec = Vec::new(); + for command in &self.commands { + if command.starts_with(line) { + completions.push(command.to_string()); + } + } + + Ok((pos, completions)) + } + + fn update(&self, line: &mut LineBuffer, start: usize, elected: &str) { + line.update(elected, start); + } +} + +// This allows us to make hints based on historic inputs +impl Hinter for Parser { + fn hint(&self, line: &str, pos: usize, ctx: &rustyline::Context<'_>) -> Option { + self.hinter.hint(line, pos, ctx) + } +} + +impl Parser { + /// creates a new parser struct + pub fn new(executor: runtime::Handle, ctx: &NodeContainer) -> Self { + Parser { + executor, + shutdown_flag: ctx.interrupt_flag(), + commands: BaseNodeCommand::iter().map(|x| x.to_string()).collect(), + hinter: HistoryHinter {}, + wallet_output_service: ctx.output_manager(), + node_service: ctx.local_node(), + wallet_transaction_service: ctx.wallet_transaction_service(), + } + } + + /// This will parse the provided command and execute the task + pub fn handle_command(&mut self, command_str: &str) { + let commands: Vec<&str> = command_str.split(' ').collect(); + let command = BaseNodeCommand::from_str(commands[0]); + if command.is_err() { + println!( + "Received: {}, this is not a valid command, please enter a valid command", + command_str + ); + println!("Enter help or press tab for available commands"); + return; + } + let command = command.unwrap(); + let help_command = if commands.len() == 2 { + Some(BaseNodeCommand::from_str(commands[1]).unwrap_or(BaseNodeCommand::Help)) + } else { + None + }; + if help_command != Some(BaseNodeCommand::Help) { + return self.process_command(command, commands); + } + match command { + BaseNodeCommand::Help => { + println!("Available commands are: "); + let joined = self.commands.join(", "); + println!("{}", joined); + }, + BaseNodeCommand::GetBalance => { + println!("This command gets your balance"); + }, + BaseNodeCommand::SendTari => { + println!("This command sends an amount of Tari to a address call this command via:"); + println!("send_tari [amount of tari to send] [public key to send to]"); + }, + BaseNodeCommand::GetChainMetadata => { + println!("This command gets your base node chain meta data"); + }, + BaseNodeCommand::Exit | BaseNodeCommand::Quit => { + println!("This command exits the base node"); + }, + } + } + + // Function to process commands + fn process_command(&mut self, command: BaseNodeCommand, command_arg: Vec<&str>) { + match command { + BaseNodeCommand::Help => { + println!("Available commands are: "); + let joined = self.commands.join(", "); + println!("{}", joined); + }, + BaseNodeCommand::GetBalance => { + self.process_get_balance(); + }, + BaseNodeCommand::SendTari => { + self.process_send_tari(command_arg); + }, + BaseNodeCommand::GetChainMetadata => { + self.process_get_chain_meta(); + }, + BaseNodeCommand::Exit | BaseNodeCommand::Quit => { + println!("quit received"); + println!("Shutting down"); + info!( + target: LOG_TARGET, + "Termination signal received from user. Shutting node down." + ); + self.shutdown_flag.store(true, Ordering::SeqCst); + }, + } + } + + // Function to process the get balance command + fn process_get_balance(&mut self) { + let mut handler = self.wallet_output_service.clone(); + self.executor.spawn(async move { + match handler.get_balance().await { + Err(e) => { + println!("Something went wrong"); + warn!(target: LOG_TARGET, "Error communicating with wallet: {}", e.to_string(),); + return; + }, + Ok(data) => println!("Current balance is: {}", data), + }; + }); + } + + // Function to process the get chain meta data + fn process_get_chain_meta(&mut self) { + let mut handler = self.node_service.clone(); + self.executor.spawn(async move { + match handler.get_metadata().await { + Err(e) => { + println!("Something went wrong"); + warn!( + target: LOG_TARGET, + "Error communicating with base node: {}", + e.to_string(), + ); + return; + }, + Ok(data) => println!("Current meta data is is: {}", data), + }; + }); + } + + // Function to process the send transaction function + fn process_send_tari(&mut self, command_arg: Vec<&str>) { + if command_arg.len() != 3 { + println!("Command entered wrong, please enter in the following format: "); + println!("send_tari [amount of tari to send] [public key to send to]"); + return; + } + let amount = command_arg[1].parse::(); + if amount.is_err() { + println!("please enter a valid amount of tari"); + return; + } + let amount: MicroTari = amount.unwrap().into(); + let dest_pubkey = CommsPublicKey::from_hex(command_arg[2]); + if dest_pubkey.is_err() { + println!("please enter a valid destination pub_key"); + return; + } + let dest_pubkey = dest_pubkey.unwrap(); + let fee_per_gram = 25 * uT; + let mut handler = self.wallet_transaction_service.clone(); + self.executor.spawn(async move { + match handler + .send_transaction( + dest_pubkey.clone(), + amount, + fee_per_gram, + "coinbase reward from mining".into(), + ) + .await + { + Err(e) => { + println!("Something went wrong sending funds"); + println!("{:?}", e); + warn!(target: LOG_TARGET, "Error communicating with wallet: {}", e.to_string(),); + return; + }, + Ok(_) => println!("Send {} Tari to {} ", amount, dest_pubkey), + }; + }); + } +} diff --git a/applications/tari_basenode/Cargo.toml b/applications/tari_basenode/Cargo.toml deleted file mode 100644 index 5072dc9b1d..0000000000 --- a/applications/tari_basenode/Cargo.toml +++ /dev/null @@ -1,5 +0,0 @@ -[package] -name = "tari_basenode" -version = "0.0.1" - -[dependencies] diff --git a/applications/tari_miner/Cargo.toml b/applications/tari_miner/Cargo.toml deleted file mode 100644 index 44ad247818..0000000000 --- a/applications/tari_miner/Cargo.toml +++ /dev/null @@ -1,5 +0,0 @@ -[package] -name = "tari_miner" -version = "0.0.1" - -[dependencies] diff --git a/applications/tari_pool_miner/Cargo.toml b/applications/tari_pool_miner/Cargo.toml deleted file mode 100644 index 5b3297fe56..0000000000 --- a/applications/tari_pool_miner/Cargo.toml +++ /dev/null @@ -1,5 +0,0 @@ -[package] -name = "tari_pool_miner" -version = "0.0.1" - -[dependencies] diff --git a/applications/test_faucet/Cargo.toml b/applications/test_faucet/Cargo.toml new file mode 100644 index 0000000000..d52b4b0222 --- /dev/null +++ b/applications/test_faucet/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "test_faucet" +version = "0.0.1" +authors = ["The Tari Development Community"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tari_utilities = "0.1.1" +serde = { version = "1.0.97", features = ["derive"] } +serde_json = "1.0" +rand = "0.7.2" + +[dependencies.tari_core] +version = "0.0.9" +path = "../../base_layer/core/" +default-features = false +features = ["transactions", "avx2"] + +[dependencies.tokio] +version = "^0.2.10" +default-features = false +features = ["fs", "blocking", "stream", "rt-threaded", "macros", "io-util", "sync"] diff --git a/applications/test_faucet/src/main.rs b/applications/test_faucet/src/main.rs new file mode 100644 index 0000000000..6de049ddf5 --- /dev/null +++ b/applications/test_faucet/src/main.rs @@ -0,0 +1,126 @@ +use rand::{self, Rng}; +use serde::Serialize; +use tari_core::{ + tari_utilities::hex::Hex, + transactions::{ + helpers, + tari_amount::{MicroTari, T}, + transaction::{OutputFeatures, TransactionOutput}, + types::{CryptoFactories, PrivateKey}, + }, +}; + +use std::{fs::File, io::Write}; +use tokio::{sync::mpsc, task}; + +const NUM_KEYS: usize = 10; + +#[derive(Serialize)] +struct Key { + key: String, + value: u64, + commitment: String, + proof: String, +} + +/// UTXO generation is pretty slow (esp range proofs), so we'll use async threads to speed things up. +/// We'll use blocking thread tasks to do the CPU intensive utxo generation, and then push the results +/// through a channel where a file-writer is waiting to persist the results to disk. +#[tokio::main(core_threads = 2, max_threads = 10)] +async fn main() -> Result<(), Box> { + let num_keys: usize = std::env::args() + .skip(1) + .take(1) + .fold(NUM_KEYS, |def, v| v.parse::().unwrap_or(def)); + + // Create a channel to give the file writer output as the utxos are generated + let (tx, rx) = mpsc::channel::<(TransactionOutput, PrivateKey, MicroTari)>(500); + + println!("Setting up output"); + let write_fut = task::spawn(write_keys(rx)); + + println!("Generating {} UTXOs..", num_keys); + let factories = CryptoFactories::default(); + let values = Values; + let features = UTXOFeatures; + // Use Rust's awesome Iterator trait to produce a sequence of values and output features. + for (value, feature) in values.take(num_keys).zip(features.take(num_keys)) { + let fc = factories.clone(); + let mut txc = tx.clone(); + // Notice the `spawn(.. spawn_blocking)` nested call here. If we don't do this, we're basically queuing up + // blocking tasks, `await`ing them to finish, and then queueing up the next one. In effect we're running things + // synchronously. + // What this construction says is: Queue up this task, and move on. "this task" (the spawning of the blocking + // task and awaiting its result) is not run immediately, but pushed to the scheduler to execute when it's + // ready. Now, we will use all the available threads for generating the keys (and the output should print + // "Go!" before, or right the beginning of any key generation output. + task::spawn(async move { + let result = task::spawn_blocking(move || { + let (utxo, key) = helpers::create_utxo(value, &fc, Some(feature)); + print!("."); + (utxo, key, value) + }) + .await + .expect("Could not create key"); + let _ = txc.send(result).await; + }); + } + println!("Go!"); + // Explicitly drop the tx side here, so that rx will end its input. + drop(tx); + + let _res = write_fut.await; + Ok(()) +} + +async fn write_keys(mut rx: mpsc::Receiver<(TransactionOutput, PrivateKey, MicroTari)>) { + let mut utxo_file = File::create("utxos.json").expect("Could not create utxos.json"); + let mut key_file = File::create("keys.json").expect("Could not create keys.json"); + let mut written: u64 = 0; + // The receiver channel will patiently await results until the tx is dropped. + while let Some((utxo, key, value)) = rx.recv().await { + let key = Key { + key: key.to_hex(), + value: u64::from(value), + commitment: utxo.commitment.to_hex(), + proof: utxo.proof.to_hex(), + }; + let key_str = format!("{}\n", serde_json::to_string(&key).unwrap()); + let _ = key_file.write_all(key_str.as_bytes()); + + let utxo_s = serde_json::to_string(&utxo).unwrap(); + match utxo_file.write_all(format!("{}\n", utxo_s).as_bytes()) { + Ok(_) => { + written += 1; + if written % 50 == 0 { + println!("{} outputs written", written); + } + }, + Err(e) => println!("{}", e.to_string()), + } + } + println!("Done."); +} + +struct Values; + +impl Iterator for Values { + type Item = MicroTari; + + fn next(&mut self) -> Option { + let mut rng = rand::rngs::OsRng; + let extra = rng.gen_range(0, 25) * 10_000_000; + Some(5000 * T + MicroTari(extra)) + } +} + +struct UTXOFeatures; + +impl Iterator for UTXOFeatures { + type Item = OutputFeatures; + + fn next(&mut self) -> Option { + let f = OutputFeatures::with_maturity(0); + Some(f) + } +} diff --git a/base_layer/core/Cargo.toml b/base_layer/core/Cargo.toml index 8a1d869f75..2efcc5706e 100644 --- a/base_layer/core/Cargo.toml +++ b/base_layer/core/Cargo.toml @@ -6,22 +6,38 @@ repository = "https://github.com/tari-project/tari" homepage = "https://tari.com" readme = "README.md" license = "BSD-3-Clause" -version = "0.0.5" +version = "0.0.9" edition = "2018" +[features] +default = ["croaring", "tari_mmr", "transactions", "base_node", "mempool_proto", "monero", "randomx-rs", "base_node_proto"] +transactions = [] +mempool_proto = [] +base_node = [] +base_node_proto = [] +avx2 = ["tari_crypto/avx2"] + [dependencies] -tari_utilities = { path = "../../infrastructure/tari_util", version = "^0.0", features = ["chrono_dt"]} +tari_comms = { version = "^0.0", path = "../../comms"} tari_infra_derive = { path = "../../infrastructure/derive", version = "^0.0" } -tari_crypto = { path = "../../infrastructure/crypto", version = "^0.0" } -tari_p2p = {path = "../../base_layer/p2p", version = "^0.0"} +tari_crypto = { version = "^0.3" } tari_storage = { path = "../../infrastructure/storage", version = "^0.0" } -tari_comms = { version = "^0.0", path = "../../comms"} -tari_mmr = { path = "../../base_layer/mmr", version = "^0.0" } +tari_common = {path = "../../common", version= "^0.0"} +tari_service_framework = { version = "^0.0", path = "../service_framework"} +tari_p2p = {path = "../../base_layer/p2p", version = "^0.0"} +tari_comms_dht = { version = "^0.0", path = "../../comms/dht"} +tari_broadcast_channel = "^0.1" +tari_pubsub = "^0.1" +tari_shutdown = { path = "../../infrastructure/shutdown", version = "^0.0"} +tari_mmr = { path = "../../base_layer/mmr", version = "^0.0", optional = true } + +randomx-rs = { version = "0.1.2", optional = true } +monero = { version = "0.5", features= ["serde_support"], optional = true } bitflags = "1.0.4" chrono = { version = "0.4.6", features = ["serde"]} digest = "0.8.0" derive-error = "0.0.4" -rand = "0.5.5" +rand = "0.7.2" serde = { version = "1.0.97", features = ["derive"] } rmp-serde = "0.13.7" base64 = "0.10.1" @@ -34,4 +50,26 @@ log = "0.4" blake2 = "^0.8.0" bigint = "^4.4.1" ttl_cache = "0.5.1" -croaring = "^0.4.0" +tokio = { version="^0.2", features = ["blocking", "time"] } +futures = {version = "^0.3.1", features = ["async-await"] } +lmdb-zero = "0.4.4" +tower-service = { version="0.3.0-alpha.2" } +crossbeam-channel = "0.3.8" +prost = "0.6.1" +bytes = "0.4.12" +prost-types = "0.6.1" +cfg-if = "0.1.10" +croaring = { version = "=0.3.9", optional = true } +config = { version = "0.9.3" } +strum = "0.17.1" +strum_macros = "0.17.1" + +[dev-dependencies] +tari_p2p = {path = "../../base_layer/p2p", version = "^0.0", features=["test-mocks"]} +tari_test_utils = { path = "../../infrastructure/test_utils", version = "^0.0" } +env_logger = "0.7.0" +tempdir = "0.3.7" +tokio-macros = "0.2.4" + +[build-dependencies] +tari_common = { version = "^0.0", path="../../common"} diff --git a/base_layer/core/build.rs b/base_layer/core/build.rs new file mode 100644 index 0000000000..7f8cc23601 --- /dev/null +++ b/base_layer/core/build.rs @@ -0,0 +1,33 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +fn main() { + tari_common::protobuf_build::ProtoCompiler::new() + .include_paths(&["src/transactions/proto", "src/proto"]) + .proto_paths(&[ + "src/mempool/proto", + "src/base_node/proto", + "src/transactions/transaction_protocol/proto", + ]) + .compile() + .unwrap(); +} diff --git a/base_layer/core/src/base_node/backoff.rs b/base_layer/core/src/base_node/backoff.rs new file mode 100644 index 0000000000..1948633e81 --- /dev/null +++ b/base_layer/core/src/base_node/backoff.rs @@ -0,0 +1,137 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +use std::time::Duration; +use tokio::time; + +/// A simple back-off strategy. `BackOff` is typically used in situations where you want to retry an operation a +/// number of times, with an increasing delay between attempts +/// +/// # Examples +/// +/// ```no_run +/// use std::time::Duration; +/// use tari_core::base_node::BackOff; +/// +/// fn foo(n: u64) -> Result<(), u64> { +/// if n < 3 { +/// Err(n) +/// } else { +/// Ok(()) +/// } +/// } +/// let mut backoff = BackOff::new(5, Duration::from_millis(100), 1.5); +/// async { +/// let mut attempts = 1; +/// while !backoff.is_finished() { +/// match foo(attempts) { +/// Ok(_) => backoff.stop(), +/// Err(n) => { +/// assert!(n < 3); +/// backoff.wait().await; +/// attempts += 1; +/// }, +/// } +/// } +/// }; +/// assert_eq!(backoff.attempts(), 4); +/// ``` +#[derive(Clone, Debug, PartialEq)] +pub struct BackOff { + max_attempts: usize, + current_attempts: usize, + delay: Duration, + backoff: f64, + stopped: bool, +} + +impl BackOff { + /// Create a new `BackOff` timer. + /// + /// # Parameters + /// * max_attempts: The total number of attempts to make + /// * delay: The initial duration to wait for after the first attempt + /// * factor: The factor to apply to the delay after each attempt + pub fn new(max_attempts: usize, delay: Duration, factor: f64) -> Self { + BackOff { + max_attempts, + current_attempts: 0, + delay, + backoff: factor, + stopped: false, + } + } + + pub fn attempts(&self) -> usize { + self.current_attempts + } + + pub fn max_attempts(&self) -> usize { + self.max_attempts + } + + pub fn is_finished(&self) -> bool { + self.current_attempts >= self.max_attempts || self.stopped + } + + pub fn is_stopped(&self) -> bool { + self.stopped + } + + pub fn stop(&mut self) { + self.stopped = true + } + + pub async fn wait(&mut self) { + if self.is_finished() { + return; + } + time::delay_for(self.delay).await; + self.current_attempts += 1; + self.delay = self.delay.mul_f64(self.backoff); + } +} + +#[cfg(test)] +mod test { + use crate::base_node::BackOff; + use std::time::Duration; + + #[tokio_macros::test] + async fn retry() { + let mut retry = BackOff::new(3, Duration::from_millis(100), 1.5); + assert_eq!(retry.attempts(), 0); + retry.wait().await; + assert_eq!(retry.attempts(), 1); + assert_eq!(retry.is_finished(), false); + retry.wait().await; + assert_eq!(retry.attempts(), 2); + assert_eq!(retry.is_finished(), false); + retry.wait().await; + assert_eq!(retry.attempts(), 3); + assert_eq!(retry.is_finished(), true); + retry.wait().await; + assert_eq!(retry.attempts(), 3); + assert_eq!(retry.is_finished(), true); + } +} diff --git a/base_layer/core/src/base_node/base_node.rs b/base_layer/core/src/base_node/base_node.rs index 0e855854b7..e783de9837 100644 --- a/base_layer/core/src/base_node/base_node.rs +++ b/base_layer/core/src/base_node/base_node.rs @@ -20,6 +20,159 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -/// `BaseNode` is the highest-level struct of the Tari full node implementation. `BaseNode` collects all the -/// sub-pieces of the Tari blockchain together, and exposes a unified API using a futures-based request-response model -pub struct BaseNode {} +use crate::{ + base_node::{ + chain_metadata_service::ChainMetadataEvent, + comms_interface::OutboundNodeCommsInterface, + states, + states::{BaseNodeState, BlockSyncConfig, ListeningConfig, ListeningInfo, StateEvent}, + }, + chain_storage::{BlockchainBackend, BlockchainDatabase}, +}; +// use bitflags::_core::sync::atomic::AtomicBool; +// use futures_util::sink::SinkExt; +use futures::SinkExt; +use log::*; +use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, +}; +use tari_broadcast_channel::{bounded, Publisher, Subscriber}; +use tokio::runtime; + +const LOG_TARGET: &str = "c::bn::base_node"; + +/// Configuration for the BaseNodeStateMachine. +#[derive(Clone, Copy)] +pub struct BaseNodeStateMachineConfig { + pub block_sync_config: BlockSyncConfig, + pub listening_config: ListeningConfig, +} + +impl Default for BaseNodeStateMachineConfig { + fn default() -> Self { + Self { + block_sync_config: BlockSyncConfig::default(), + listening_config: ListeningConfig::default(), + } + } +} + +/// A Tari full node, aka Base Node. +/// +/// The Base Node is essentially a finite state machine that synchronises its blockchain state with its peers and +/// then listens for new blocks to add to the blockchain. See the [SynchronizationSate] documentation for more details. +/// +/// This struct holds fields that will be used by all the various FSM state instances, including the local blockchain +/// database and hooks to the p2p network +pub struct BaseNodeStateMachine { + pub(super) db: BlockchainDatabase, + pub(super) comms: OutboundNodeCommsInterface, + pub(super) executor: runtime::Handle, + pub(super) metadata_event_stream: Subscriber, + pub(super) user_stopped: Arc, + pub(super) config: BaseNodeStateMachineConfig, + event_sender: Publisher, + event_receiver: Subscriber, +} + +impl BaseNodeStateMachine { + /// Instantiate a new Base Node. + pub fn new( + db: &BlockchainDatabase, + comms: &OutboundNodeCommsInterface, + executor: runtime::Handle, + metadata_event_stream: Subscriber, + config: BaseNodeStateMachineConfig, + ) -> Self + { + let (event_sender, event_receiver): (Publisher, Subscriber) = bounded(1); + Self { + db: db.clone(), + comms: comms.clone(), + executor, + metadata_event_stream, + user_stopped: Arc::new(AtomicBool::new(false)), + config, + event_sender, + event_receiver, + } + } + + /// Describe the Finite State Machine for the base node. This function describes _every possible_ state + /// transition for the node given its current state and an event that gets triggered. + pub fn transition(state: BaseNodeState, event: StateEvent) -> BaseNodeState { + use crate::base_node::states::{BaseNodeState::*, StateEvent::*, SyncStatus::*}; + match (state, event) { + (Starting(s), Initialized) => InitialSync(s.into()), + (InitialSync(s), MetadataSynced(Lagging(_))) => BlockSync(s.into()), + (InitialSync(_s), MetadataSynced(UpToDate)) => Listening(ListeningInfo), + (BlockSync(_s), BlocksSynchronized) => Listening(ListeningInfo), + (BlockSync(s), MaxRequestAttemptsReached) => InitialSync(s.into()), + (Listening(s), FallenBehind(Lagging(_))) => BlockSync(s.into()), + (Listening(s), NetworkSilence) => InitialSync(s.into()), + (_, FatalError(s)) => Shutdown(states::Shutdown::with_reason(s)), + (_, UserQuit) => Shutdown(states::Shutdown::with_reason("Shutdown initiated by user".to_string())), + (s, e) => { + warn!( + target: LOG_TARGET, + "No state transition occurs for event {:?} in state {}", e, s + ); + s + }, + } + } + + /// Return a copy of the `user_stopped` flag. Setting this to `true` at any time will signal the node runtime to + /// shutdown. + pub fn get_interrupt_flag(&self) -> Arc { + Arc::clone(&self.user_stopped) + } + + /// Returns `true` if the `user_stopped` flag has been set + pub fn is_stop_requested(&self) -> bool { + self.user_stopped.load(Ordering::SeqCst) + } + + /// This clones the receiver end of the channel and gives out a copy to the caller + /// This allows multiple subscribers to this channel by only keeping one channel and cloning the receiver for every + /// caller. + pub fn get_state_change_event_stream(&self) -> Subscriber { + self.event_receiver.clone() + } + + /// Start the base node runtime. + pub async fn run(self) { + use crate::base_node::states::BaseNodeState::*; + let mut state = Starting(states::Starting); + let mut shared_state = self; + loop { + if shared_state.is_stop_requested() { + break; + } + let _ = shared_state.event_sender.send(state.clone()).await; + let next_event = match &mut state { + Starting(s) => s.next_event(&mut shared_state).await, + InitialSync(s) => s.next_event(&mut shared_state).await, + BlockSync(s) => s.next_event(&mut shared_state).await, + Listening(s) => s.next_event(&mut shared_state).await, + Shutdown(_) => break, + }; + debug!( + target: LOG_TARGET, + "=== Base Node event in State [{}]: {:?}", state, next_event + ); + state = BaseNodeStateMachine::::transition(state, next_event); + } + } + + /// Checks the value of the interrupt flag and returns a `FatalError` event if the flag is true. Otherwise it + /// returns the `default` event. + pub fn check_interrupt(flag: &AtomicBool, default: StateEvent) -> StateEvent { + if flag.load(Ordering::SeqCst) { + StateEvent::FatalError("User interrupted".into()) + } else { + default + } + } +} diff --git a/base_layer/core/src/base_node/chain_metadata_service/error.rs b/base_layer/core/src/base_node/chain_metadata_service/error.rs new file mode 100644 index 0000000000..355df992fb --- /dev/null +++ b/base_layer/core/src/base_node/chain_metadata_service/error.rs @@ -0,0 +1,40 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::base_node::comms_interface::CommsInterfaceError; +use derive_error::Error; +use prost::DecodeError; +use tari_comms::message::MessageError; +use tari_p2p::services::liveness::error::LivenessError; + +#[derive(Debug, Error)] +pub enum ChainMetadataSyncError { + /// Failed to decode chain metadata + DecodeError(DecodeError), + /// Peer did not send any chain metadata + NoChainMetadata, + LivenessError(LivenessError), + CommsInterfaceError(CommsInterfaceError), + MessageError(MessageError), + /// Failed to publish `ChainMetadataEvent` + EventPublishFailed, +} diff --git a/base_layer/core/src/base_node/chain_metadata_service/handle.rs b/base_layer/core/src/base_node/chain_metadata_service/handle.rs new file mode 100644 index 0000000000..3bb498b6e0 --- /dev/null +++ b/base_layer/core/src/base_node/chain_metadata_service/handle.rs @@ -0,0 +1,49 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::chain_storage::ChainMetadata; +use futures::{stream::Fuse, StreamExt}; +use tari_broadcast_channel::Subscriber; + +#[derive(Debug)] +pub enum ChainMetadataEvent { + PeerChainMetadataReceived(Vec), +} + +#[derive(Clone)] +pub struct ChainMetadataHandle { + event_stream: Subscriber, +} + +impl ChainMetadataHandle { + pub fn new(event_stream: Subscriber) -> Self { + Self { event_stream } + } + + pub fn get_event_stream(&self) -> Subscriber { + self.event_stream.clone() + } + + pub fn get_event_stream_fused(&self) -> Fuse> { + self.get_event_stream().fuse() + } +} diff --git a/base_layer/core/src/base_node/chain_metadata_service/initializer.rs b/base_layer/core/src/base_node/chain_metadata_service/initializer.rs new file mode 100644 index 0000000000..d77a83bfa6 --- /dev/null +++ b/base_layer/core/src/base_node/chain_metadata_service/initializer.rs @@ -0,0 +1,71 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::{service::ChainMetadataService, LOG_TARGET}; +use crate::base_node::{chain_metadata_service::handle::ChainMetadataHandle, comms_interface::LocalNodeCommsInterface}; +use futures::{future, future::select, pin_mut}; +use log::*; +use std::future::Future; +use tari_broadcast_channel as broadcast_channel; +use tari_p2p::services::liveness::LivenessHandle; +use tari_service_framework::{handles::ServiceHandlesFuture, ServiceInitializationError, ServiceInitializer}; +use tari_shutdown::ShutdownSignal; +use tokio::runtime; + +const BROADCAST_EVENT_BUFFER_SIZE: usize = 10; + +pub struct ChainMetadataServiceInitializer; + +impl ServiceInitializer for ChainMetadataServiceInitializer { + type Future = impl Future>; + + fn initialize( + &mut self, + executor: runtime::Handle, + handles_fut: ServiceHandlesFuture, + shutdown: ShutdownSignal, + ) -> Self::Future + { + let (publisher, subscriber) = broadcast_channel::bounded(BROADCAST_EVENT_BUFFER_SIZE); + let handle = ChainMetadataHandle::new(subscriber); + handles_fut.register(handle); + + executor.spawn(async move { + let handles = handles_fut.await; + + let liveness = handles + .get_handle::() + .expect("Liveness service required to initialize ChainStateSyncService"); + + let base_node = handles + .get_handle::() + .expect("LocalNodeCommsInterface required to initialize ChainStateSyncService"); + + let service_run = ChainMetadataService::new(liveness, base_node, publisher).run(); + pin_mut!(service_run); + select(service_run, shutdown).await; + info!(target: LOG_TARGET, "ChainMetadataService has shut down"); + }); + + future::ready(Ok(())) + } +} diff --git a/base_layer/core/src/base_node/chain_metadata_service/mod.rs b/base_layer/core/src/base_node/chain_metadata_service/mod.rs new file mode 100644 index 0000000000..9d07180b4c --- /dev/null +++ b/base_layer/core/src/base_node/chain_metadata_service/mod.rs @@ -0,0 +1,32 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +const LOG_TARGET: &str = "c::bn::chain_state_sync_service"; + +mod error; +mod handle; +mod initializer; +mod service; + +// Public re-exports +pub use handle::{ChainMetadataEvent, ChainMetadataHandle}; +pub use initializer::ChainMetadataServiceInitializer; diff --git a/base_layer/core/src/base_node/chain_metadata_service/service.rs b/base_layer/core/src/base_node/chain_metadata_service/service.rs new file mode 100644 index 0000000000..e107d8a217 --- /dev/null +++ b/base_layer/core/src/base_node/chain_metadata_service/service.rs @@ -0,0 +1,415 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::{error::ChainMetadataSyncError, LOG_TARGET}; +use crate::{ + base_node::{ + chain_metadata_service::handle::ChainMetadataEvent, + comms_interface::{BlockEvent, LocalNodeCommsInterface}, + proto, + }, + chain_storage::{BlockAddResult, ChainMetadata}, +}; +use chrono::{NaiveDateTime, Utc}; +use futures::{stream::StreamExt, SinkExt}; +use log::*; +use prost::Message; +use tari_broadcast_channel::Publisher; +use tari_common::log_if_error; +use tari_comms::{message::MessageExt, peer_manager::NodeId}; +use tari_p2p::services::liveness::{LivenessEvent, LivenessHandle, Metadata, MetadataKey}; + +pub(super) struct ChainMetadataService { + liveness: LivenessHandle, + base_node: LocalNodeCommsInterface, + peer_chain_metadata: Vec, + last_chainstate_flushed_at: NaiveDateTime, + event_publisher: Publisher, +} + +impl ChainMetadataService { + /// Create a new ChainMetadataService + /// + /// ## Arguments + /// `liveness` - the liveness service handle + /// `base_node` - the base node service handle + pub fn new( + liveness: LivenessHandle, + base_node: LocalNodeCommsInterface, + event_publisher: Publisher, + ) -> Self + { + Self { + liveness, + base_node, + peer_chain_metadata: Vec::new(), + last_chainstate_flushed_at: Utc::now().naive_utc(), + event_publisher, + } + } + + /// Run the service + pub async fn run(mut self) { + let mut liveness_event_stream = self.liveness.get_event_stream_fused(); + let mut base_node_event_stream = self.base_node.get_block_event_stream_fused(); + + log_if_error!( + target: LOG_TARGET, + "Error when updating liveness chain metadata: '{}'", + self.update_liveness_chain_metadata().await + ); + + loop { + futures::select! { + event = base_node_event_stream.select_next_some() => { + log_if_error!( + level: debug, + target: LOG_TARGET, + "Failed to handle base node event because '{}'", + self.handle_block_event(&event).await + ); + }, + + liveness_event = liveness_event_stream.select_next_some() => { + log_if_error!( + target: LOG_TARGET, + "Failed to handle liveness event because '{}'", + self.handle_liveness_event(&liveness_event).await + ); + }, + + complete => { + info!(target: LOG_TARGET, "ChainStateSyncService is exiting because all tasks have completed"); + break; + } + } + } + } + + /// Handle BlockEvents + async fn handle_block_event(&mut self, event: &BlockEvent) -> Result<(), ChainMetadataSyncError> { + match event { + BlockEvent::Verified((_, BlockAddResult::Ok)) => { + self.update_liveness_chain_metadata().await?; + }, + BlockEvent::Verified(_) | BlockEvent::Invalid(_) => {}, + } + + Ok(()) + } + + /// Send this node's metadata to + async fn update_liveness_chain_metadata(&mut self) -> Result<(), ChainMetadataSyncError> { + let chain_metadata = self.base_node.get_metadata().await?; + let bytes = proto::ChainMetadata::from(chain_metadata).to_encoded_bytes()?; + self.liveness + .set_pong_metadata_entry(MetadataKey::ChainMetadata, bytes) + .await?; + Ok(()) + } + + async fn handle_liveness_event(&mut self, event: &LivenessEvent) -> Result<(), ChainMetadataSyncError> { + match event { + // Received a pong, check if our neighbour sent it and it contains ChainMetadata + LivenessEvent::ReceivedPong(event) => { + if event.is_neighbour { + self.collect_chain_state_from_pong(&event.node_id, &event.metadata)?; + + // All peers have responded in this round, send the chain metadata to the base node service + if self.peer_chain_metadata.len() == self.peer_chain_metadata.capacity() { + self.flush_chain_metadata_to_event_publisher().await?; + } + } else { + debug!( + target: LOG_TARGET, + "Received pong from non-neighbouring node '{}'. Pong ignored...", event.node_id + ) + } + }, + // New ping round has begun + LivenessEvent::BroadcastedNeighbourPings(num_peers) => { + // If we have chain metadata to send to the base node service, send them now + // because the next round of pings is happening. + // TODO: It's pretty easy for this service to require either a percentage of peers + // to respond or, a time limit before assuming some peers will never respond + // between rounds (even if this time limit is larger than one or more ping rounds) + // before publishing the chain metadata event. + // The following will send the chain metadata at the start of a new round if at + // least one node has responded. + if !self.peer_chain_metadata.is_empty() { + self.flush_chain_metadata_to_event_publisher().await?; + } + // Ensure that we're waiting for the correct amount of peers to respond + // and have allocated space for their replies + self.resize_chainstate_buffer(*num_peers); + }, + _ => {}, + } + + Ok(()) + } + + async fn flush_chain_metadata_to_event_publisher(&mut self) -> Result<(), ChainMetadataSyncError> { + let chain_metadata = self + .peer_chain_metadata + .drain(..) + .map(|peer_metadata| peer_metadata.chain_metadata) + .collect::>(); + + self.event_publisher + .send(ChainMetadataEvent::PeerChainMetadataReceived(chain_metadata)) + .await + .map_err(|_| ChainMetadataSyncError::EventPublishFailed)?; + + self.last_chainstate_flushed_at = Utc::now().naive_utc(); + + Ok(()) + } + + fn resize_chainstate_buffer(&mut self, n: usize) { + match self.peer_chain_metadata.capacity() { + cap if n > cap => { + let additional = n - self.peer_chain_metadata.len(); + self.peer_chain_metadata.reserve_exact(additional); + }, + cap if n < cap => { + self.peer_chain_metadata.shrink_to(cap); + }, + _ => {}, + } + } + + fn collect_chain_state_from_pong( + &mut self, + node_id: &NodeId, + metadata: &Metadata, + ) -> Result<(), ChainMetadataSyncError> + { + let chain_metadata_bytes = metadata + .get(MetadataKey::ChainMetadata) + .ok_or_else(|| ChainMetadataSyncError::NoChainMetadata)?; + + debug!(target: LOG_TARGET, "Received chain metadata from NodeId '{}'", node_id); + let chain_metadata = proto::ChainMetadata::decode(chain_metadata_bytes.as_slice())?.into(); + + if let Some(pos) = self + .peer_chain_metadata + .iter() + .position(|peer_chainstate| &peer_chainstate.node_id == node_id) + { + self.peer_chain_metadata.remove(pos); + } + + self.peer_chain_metadata + .push(PeerChainMetadata::new(node_id.clone(), chain_metadata)); + + Ok(()) + } +} + +struct PeerChainMetadata { + node_id: NodeId, + chain_metadata: ChainMetadata, +} + +impl PeerChainMetadata { + fn new(node_id: NodeId, chain_metadata: ChainMetadata) -> Self { + Self { + node_id, + chain_metadata, + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::base_node::comms_interface::{CommsInterfaceError, NodeCommsRequest, NodeCommsResponse}; + use std::convert::TryInto; + use tari_broadcast_channel as broadcast_channel; + use tari_p2p::services::liveness::{mock::create_p2p_liveness_mock, LivenessRequest, PongEvent}; + use tari_service_framework::reply_channel; + use tari_test_utils::{runtime, unpack_enum}; + + fn create_base_node_nci() -> ( + LocalNodeCommsInterface, + reply_channel::Receiver>, + ) { + let (base_node_sender, base_node_receiver) = reply_channel::unbounded(); + let (block_sender, _block_receiver) = reply_channel::unbounded(); + let (_base_node_publisher, subscriber) = broadcast_channel::bounded(1); + let base_node = LocalNodeCommsInterface::new(base_node_sender, block_sender, subscriber); + + (base_node, base_node_receiver) + } + + fn create_sample_proto_chain_metadata() -> proto::ChainMetadata { + proto::ChainMetadata { + height_of_longest_chain: Some(1), + best_block: Some(vec![]), + pruning_horizon: 64, + } + } + + #[test] + fn update_liveness_chain_metadata() { + runtime::test_async(|rt| { + let (liveness_handle, liveness_mock) = create_p2p_liveness_mock(1); + let liveness_mock_state = liveness_mock.get_mock_state(); + rt.spawn(liveness_mock.run()); + + let (base_node, mut base_node_receiver) = create_base_node_nci(); + + let (publisher, _subscriber) = broadcast_channel::bounded(1); + let mut service = ChainMetadataService::new(liveness_handle, base_node, publisher); + + let mut proto_chain_metadata = create_sample_proto_chain_metadata(); + proto_chain_metadata.height_of_longest_chain = Some(123); + let chain_metadata = proto_chain_metadata.clone().try_into().unwrap(); + + rt.spawn(async move { + let base_node_req = base_node_receiver.select_next_some().await; + let (_req, reply_tx) = base_node_req.split(); + reply_tx + .send(Ok(NodeCommsResponse::ChainMetadata(chain_metadata))) + .unwrap(); + }); + + rt.block_on(service.update_liveness_chain_metadata()).unwrap(); + + assert_eq!(liveness_mock_state.call_count(), 1); + + let last_call = liveness_mock_state.take_calls().remove(0); + unpack_enum!(LivenessRequest::SetPongMetadata(metadata_key, data) = last_call); + assert_eq!(metadata_key, MetadataKey::ChainMetadata); + let chain_metadata = proto::ChainMetadata::decode(data.as_slice()).unwrap(); + assert_eq!(chain_metadata.height_of_longest_chain, Some(123)); + }); + } + + #[tokio_macros::test] + async fn handle_liveness_event_ok() { + let (liveness_handle, _) = create_p2p_liveness_mock(1); + let mut metadata = Metadata::new(); + let proto_chain_metadata = create_sample_proto_chain_metadata(); + metadata.insert( + MetadataKey::ChainMetadata, + proto_chain_metadata.to_encoded_bytes().unwrap(), + ); + + let node_id = NodeId::new(); + let pong_event = PongEvent { + is_neighbour: true, + metadata, + node_id: node_id.clone(), + latency: None, + is_monitored: false, + }; + + let (base_node, _) = create_base_node_nci(); + + let (publisher, _subscriber) = broadcast_channel::bounded(1); + let mut service = ChainMetadataService::new(liveness_handle, base_node, publisher); + + // To prevent the chain metadata buffer being flushed after receiving a single pong event, + // extend it's capacity to 2 + service.peer_chain_metadata.reserve_exact(2); + let sample_event = LivenessEvent::ReceivedPong(Box::new(pong_event)); + service.handle_liveness_event(&sample_event).await.unwrap(); + assert_eq!(service.peer_chain_metadata.len(), 1); + let metadata = service.peer_chain_metadata.remove(0); + assert_eq!(metadata.node_id, node_id); + assert_eq!( + metadata.chain_metadata.height_of_longest_chain, + proto_chain_metadata.height_of_longest_chain + ); + } + + #[tokio_macros::test] + async fn handle_liveness_event_no_metadata() { + let (liveness_handle, _) = create_p2p_liveness_mock(1); + let metadata = Metadata::new(); + let node_id = NodeId::new(); + let pong_event = PongEvent { + is_neighbour: true, + metadata, + node_id, + latency: None, + is_monitored: false, + }; + + let (base_node, _) = create_base_node_nci(); + let (publisher, _subscriber) = broadcast_channel::bounded(1); + let mut service = ChainMetadataService::new(liveness_handle, base_node, publisher); + + let sample_event = LivenessEvent::ReceivedPong(Box::new(pong_event)); + let err = service.handle_liveness_event(&sample_event).await.unwrap_err(); + unpack_enum!(ChainMetadataSyncError::NoChainMetadata = err); + assert_eq!(service.peer_chain_metadata.len(), 0); + } + + #[tokio_macros::test] + async fn handle_liveness_event_not_neighbour() { + let (liveness_handle, _) = create_p2p_liveness_mock(1); + let metadata = Metadata::new(); + let node_id = NodeId::new(); + let pong_event = PongEvent { + is_neighbour: false, + metadata, + node_id, + latency: None, + is_monitored: false, + }; + + let (base_node, _) = create_base_node_nci(); + let (publisher, _subscriber) = broadcast_channel::bounded(1); + let mut service = ChainMetadataService::new(liveness_handle, base_node, publisher); + + let sample_event = LivenessEvent::ReceivedPong(Box::new(pong_event)); + service.handle_liveness_event(&sample_event).await.unwrap(); + assert_eq!(service.peer_chain_metadata.len(), 0); + } + + #[tokio_macros::test] + async fn handle_liveness_event_bad_metadata() { + let (liveness_handle, _) = create_p2p_liveness_mock(1); + let mut metadata = Metadata::new(); + metadata.insert(MetadataKey::ChainMetadata, b"no-good".to_vec()); + let node_id = NodeId::new(); + let pong_event = PongEvent { + is_neighbour: true, + metadata, + node_id, + latency: None, + is_monitored: false, + }; + + let (base_node, _) = create_base_node_nci(); + let (publisher, _subscriber) = broadcast_channel::bounded(1); + let mut service = ChainMetadataService::new(liveness_handle, base_node, publisher); + + let sample_event = LivenessEvent::ReceivedPong(Box::new(pong_event)); + let err = service.handle_liveness_event(&sample_event).await.unwrap_err(); + unpack_enum!(ChainMetadataSyncError::DecodeError(_err) = err); + assert_eq!(service.peer_chain_metadata.len(), 0); + } +} diff --git a/base_layer/core/src/base_node/comms_interface/comms_request.rs b/base_layer/core/src/base_node/comms_interface/comms_request.rs new file mode 100644 index 0000000000..9c6a4baf64 --- /dev/null +++ b/base_layer/core/src/base_node/comms_interface/comms_request.rs @@ -0,0 +1,78 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + blocks::NewBlockTemplate, + chain_storage::MmrTree, + proof_of_work::PowAlgorithm, + transactions::types::HashOutput, +}; +use serde::{Deserialize, Serialize}; +use std::fmt::{Display, Error, Formatter}; + +/// NodeCommsRequestType is used to specify the amount of peers that need to be queried before a request can be +/// finalized. +#[derive(Debug, Serialize, Deserialize)] +pub enum NodeCommsRequestType { + /// Send the request to a single remote base node + Single, + /// Send the request to a number of remote base nodes and accumulate all the responses. + Many, +} + +/// A container for the parameters required for a FetchMmrState request. +#[derive(Debug, Serialize, Deserialize)] +pub struct MmrStateRequest { + pub tree: MmrTree, + pub index: u64, + pub count: u64, +} + +/// API Request enum +#[derive(Debug, Serialize, Deserialize)] +pub enum NodeCommsRequest { + GetChainMetadata, + FetchKernels(Vec), + FetchHeaders(Vec), + FetchUtxos(Vec), + FetchBlocks(Vec), + FetchBlocksWithHashes(Vec), + GetNewBlockTemplate, + GetNewBlock(NewBlockTemplate), + GetTargetDifficulty(PowAlgorithm), +} + +impl Display for NodeCommsRequest { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + match self { + NodeCommsRequest::GetChainMetadata => f.write_str("GetChainMetadata"), + NodeCommsRequest::FetchKernels(v) => f.write_str(&format!("FetchKernels (n={})", v.len())), + NodeCommsRequest::FetchHeaders(v) => f.write_str(&format!("FetchHeaders (n={})", v.len())), + NodeCommsRequest::FetchUtxos(v) => f.write_str(&format!("FetchUtxos (n={})", v.len())), + NodeCommsRequest::FetchBlocks(v) => f.write_str(&format!("FetchBlocks (n={})", v.len())), + NodeCommsRequest::FetchBlocksWithHashes(v) => f.write_str(&format!("FetchBlocks (n={})", v.len())), + NodeCommsRequest::GetNewBlockTemplate => f.write_str("GetNewBlockTemplate"), + NodeCommsRequest::GetNewBlock(b) => f.write_str(&format!("GetNewBlock (Block Height={}", b.header.height)), + NodeCommsRequest::GetTargetDifficulty(algo) => f.write_str(&format!("GetTargetDifficulty ({})", algo)), + } + } +} diff --git a/base_layer/core/src/base_node/comms_interface/comms_response.rs b/base_layer/core/src/base_node/comms_interface/comms_response.rs new file mode 100644 index 0000000000..132242e13a --- /dev/null +++ b/base_layer/core/src/base_node/comms_interface/comms_response.rs @@ -0,0 +1,42 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + blocks::{blockheader::BlockHeader, Block, NewBlockTemplate}, + chain_storage::{ChainMetadata, HistoricalBlock}, + proof_of_work::Difficulty, + transactions::transaction::{TransactionKernel, TransactionOutput}, +}; +use serde::{Deserialize, Serialize}; + +/// API Response enum +#[derive(Debug, Serialize, Deserialize, Clone)] +pub enum NodeCommsResponse { + ChainMetadata(ChainMetadata), + TransactionKernels(Vec), + BlockHeaders(Vec), + TransactionOutputs(Vec), + HistoricalBlocks(Vec), + NewBlockTemplate(NewBlockTemplate), + NewBlock(Block), + TargetDifficulty(Difficulty), +} diff --git a/base_layer/core/src/base_node/comms_interface/error.rs b/base_layer/core/src/base_node/comms_interface/error.rs new file mode 100644 index 0000000000..f02be25bba --- /dev/null +++ b/base_layer/core/src/base_node/comms_interface/error.rs @@ -0,0 +1,43 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{chain_storage::ChainStorageError, consensus::ConsensusManagerError}; +use derive_error::Error; +use tari_service_framework::reply_channel::TransportChannelError; + +#[derive(Debug, Error, PartialEq, Clone)] +pub enum CommsInterfaceError { + /// Access to the underlying storage mechanism failed + UnexpectedApiResponse, + RequestTimedOut, + NoBootstrapNodesConfigured, + TransportChannelError(TransportChannelError), + ChainStorageError(ChainStorageError), + #[error(non_std, no_from)] + OutboundMessageService(String), + EventStreamError, + #[error(non_std, no_from)] + MempoolError(String), + /// Failure in broadcast DHT middleware + BroadcastFailed, + DifficultyAdjustmentManagerError(ConsensusManagerError), +} diff --git a/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs b/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs new file mode 100644 index 0000000000..3db3ac6a99 --- /dev/null +++ b/base_layer/core/src/base_node/comms_interface/inbound_handlers.rs @@ -0,0 +1,247 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + base_node::{ + comms_interface::{error::CommsInterfaceError, NodeCommsRequest, NodeCommsResponse}, + OutboundNodeCommsInterface, + }, + blocks::{blockheader::BlockHeader, Block, BlockBuilder, NewBlockTemplate}, + chain_storage::{ + async_db, + BlockAddResult, + BlockchainBackend, + BlockchainDatabase, + ChainStorageError, + HistoricalBlock, + }, + consensus::ConsensusManager, + mempool::Mempool, + transactions::transaction::{TransactionKernel, TransactionOutput}, +}; +use futures::SinkExt; +use log::*; +use strum_macros::Display; +use tari_broadcast_channel::Publisher; +use tari_comms::types::CommsPublicKey; +use tari_crypto::tari_utilities::hex::Hex; + +const LOG_TARGET: &str = "c::bn::comms_interface::inbound_handler"; + +/// Events that can be published on the Validated Block Event Stream +#[derive(Debug, Clone, Display)] +pub enum BlockEvent { + Verified((Block, BlockAddResult)), + Invalid((Block, ChainStorageError)), +} + +/// The InboundNodeCommsInterface is used to handle all received inbound requests from remote nodes. +pub struct InboundNodeCommsHandlers +where T: BlockchainBackend +{ + event_publisher: Publisher, + blockchain_db: BlockchainDatabase, + mempool: Mempool, + consensus_manager: ConsensusManager, + outbound_nci: OutboundNodeCommsInterface, +} + +impl InboundNodeCommsHandlers +where T: BlockchainBackend + 'static +{ + /// Construct a new InboundNodeCommsInterface. + pub fn new( + event_publisher: Publisher, + blockchain_db: BlockchainDatabase, + mempool: Mempool, + consensus_manager: ConsensusManager, + outbound_nci: OutboundNodeCommsInterface, + ) -> Self + { + Self { + event_publisher, + blockchain_db, + mempool, + consensus_manager, + outbound_nci, + } + } + + /// Handle inbound node comms requests from remote nodes and local services. + pub async fn handle_request(&self, request: &NodeCommsRequest) -> Result { + debug!(target: LOG_TARGET, "Handling remote request: {}", request); + match request { + NodeCommsRequest::GetChainMetadata => Ok(NodeCommsResponse::ChainMetadata( + async_db::get_metadata(self.blockchain_db.clone()).await?, + )), + NodeCommsRequest::FetchKernels(kernel_hashes) => { + let mut kernels = Vec::::new(); + for hash in kernel_hashes { + if let Ok(kernel) = async_db::fetch_kernel(self.blockchain_db.clone(), hash.clone()).await { + kernels.push(kernel); + } + } + Ok(NodeCommsResponse::TransactionKernels(kernels)) + }, + NodeCommsRequest::FetchHeaders(block_nums) => { + let mut block_headers = Vec::::new(); + for block_num in block_nums { + if let Ok(block_header) = async_db::fetch_header(self.blockchain_db.clone(), *block_num).await { + block_headers.push(block_header); + } + } + Ok(NodeCommsResponse::BlockHeaders(block_headers)) + }, + NodeCommsRequest::FetchUtxos(utxo_hashes) => { + let mut utxos = Vec::::new(); + for hash in utxo_hashes { + if let Ok(utxo) = async_db::fetch_utxo(self.blockchain_db.clone(), hash.clone()).await { + utxos.push(utxo); + } + } + Ok(NodeCommsResponse::TransactionOutputs(utxos)) + }, + NodeCommsRequest::FetchBlocks(block_nums) => { + let mut blocks = Vec::::with_capacity(block_nums.len()); + for block_num in block_nums { + debug!(target: LOG_TARGET, "A peer has requested block {}", block_num); + match async_db::fetch_block(self.blockchain_db.clone(), *block_num).await { + Ok(block) => blocks.push(block), + Err(e) => info!( + target: LOG_TARGET, + "Could not provide requested block {} to peer because: {}", + block_num, + e.to_string() + ), + } + } + Ok(NodeCommsResponse::HistoricalBlocks(blocks)) + }, + NodeCommsRequest::FetchBlocksWithHashes(block_hashes) => { + let mut blocks = Vec::::with_capacity(block_hashes.len()); + for block_hash in block_hashes { + debug!( + target: LOG_TARGET, + "A peer has requested a block with hash {}", + block_hash.to_hex() + ); + match async_db::fetch_block_with_hash(self.blockchain_db.clone(), block_hash.clone()).await { + Ok(Some(block)) => blocks.push(block), + Ok(None) => info!( + target: LOG_TARGET, + "Could not provide requested block {} to peer because not stored", + block_hash.to_hex(), + ), + Err(e) => info!( + target: LOG_TARGET, + "Could not provide requested block {} to peer because: {}", + block_hash.to_hex(), + e.to_string() + ), + } + } + Ok(NodeCommsResponse::HistoricalBlocks(blocks)) + }, + NodeCommsRequest::GetNewBlockTemplate => { + let metadata = async_db::get_metadata(self.blockchain_db.clone()).await?; + let best_block_hash = metadata + .best_block + .ok_or_else(|| CommsInterfaceError::UnexpectedApiResponse)?; + let best_block_header = + async_db::fetch_header_with_block_hash(self.blockchain_db.clone(), best_block_hash).await?; + let header = BlockHeader::from_previous(&best_block_header); + + let transactions = self + .mempool + .retrieve( + self.consensus_manager + .consensus_constants() + .get_max_block_transaction_weight(), + ) + .map_err(|e| CommsInterfaceError::MempoolError(e.to_string()))? + .iter() + .map(|tx| (**tx).clone()) + .collect(); + + let block_template = NewBlockTemplate::from( + BlockBuilder::new(&self.consensus_manager.consensus_constants()) + .with_header(header) + .with_transactions(transactions) + .build(), + ); + trace!(target: LOG_TARGET, "New block template requested {}", block_template); + Ok(NodeCommsResponse::NewBlockTemplate(block_template)) + }, + NodeCommsRequest::GetNewBlock(block_template) => { + let block = async_db::calculate_mmr_roots(self.blockchain_db.clone(), block_template.clone()).await?; + Ok(NodeCommsResponse::NewBlock(block)) + }, + NodeCommsRequest::GetTargetDifficulty(pow_algo) => Ok(NodeCommsResponse::TargetDifficulty( + self.consensus_manager.get_target_difficulty(*pow_algo)?, + )), + } + } + + /// Handle inbound blocks from remote nodes and local services. + pub async fn handle_block( + &mut self, + block: &Block, + source_peer: Option, + ) -> Result<(), CommsInterfaceError> + { + debug!( + target: LOG_TARGET, + "Block received from remote peer or local services: {:?}", source_peer + ); + trace!(target: LOG_TARGET, "Block: {}", block); + let add_block_result = self.blockchain_db.add_block(block.clone()); + // Create block event on block event stream + let block_event = match add_block_result.clone() { + Ok(block_add_result) => { + debug!(target: LOG_TARGET, "Block event created: {:?}", block_add_result); + BlockEvent::Verified((block.clone(), block_add_result)) + }, + Err(e) => { + error!(target: LOG_TARGET, "Block validation failed: {:?}", e); + BlockEvent::Invalid((block.clone(), e)) + }, + }; + self.event_publisher + .send(block_event) + .await + .map_err(|_| CommsInterfaceError::EventStreamError)?; + // Propagate verified block to remote nodes + if let Ok(add_block_result) = add_block_result { + let propagate = match add_block_result { + BlockAddResult::Ok => true, + BlockAddResult::BlockExists => false, + BlockAddResult::OrphanBlock => false, + BlockAddResult::ChainReorg(_) => true, + }; + if propagate { + let exclude_peers = source_peer.map_or_else(|| vec![], |comms_public_key| vec![comms_public_key]); + self.outbound_nci.propagate_block(block.clone(), exclude_peers).await?; + } + } + Ok(()) + } +} diff --git a/base_layer/core/src/base_node/comms_interface/local_interface.rs b/base_layer/core/src/base_node/comms_interface/local_interface.rs new file mode 100644 index 0000000000..f864c2967f --- /dev/null +++ b/base_layer/core/src/base_node/comms_interface/local_interface.rs @@ -0,0 +1,118 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + base_node::comms_interface::{error::CommsInterfaceError, BlockEvent, NodeCommsRequest, NodeCommsResponse}, + blocks::{Block, NewBlockTemplate}, + chain_storage::ChainMetadata, + proof_of_work::{Difficulty, PowAlgorithm}, +}; +use futures::{stream::Fuse, StreamExt}; +use tari_broadcast_channel::Subscriber; +use tari_service_framework::reply_channel::SenderService; +use tower_service::Service; + +/// The InboundNodeCommsInterface provides an interface to request information from the current local node by other +/// internal services. +#[derive(Clone)] +pub struct LocalNodeCommsInterface { + request_sender: SenderService>, + block_sender: SenderService>, + block_event_stream: Subscriber, +} + +impl LocalNodeCommsInterface { + /// Construct a new LocalNodeCommsInterface with the specified SenderService. + pub fn new( + request_sender: SenderService>, + block_sender: SenderService>, + block_event_stream: Subscriber, + ) -> Self + { + Self { + request_sender, + block_sender, + block_event_stream, + } + } + + pub fn get_block_event_stream(&self) -> Subscriber { + self.block_event_stream.clone() + } + + pub fn get_block_event_stream_fused(&self) -> Fuse> { + self.get_block_event_stream().fuse() + } + + /// Request metadata from the current local node. + pub async fn get_metadata(&mut self) -> Result { + match self.request_sender.call(NodeCommsRequest::GetChainMetadata).await?? { + NodeCommsResponse::ChainMetadata(metadata) => Ok(metadata), + _ => Err(CommsInterfaceError::UnexpectedApiResponse), + } + } + + /// Request the construction of a new mineable block template from the base node service. + pub async fn get_new_block_template(&mut self) -> Result { + match self + .request_sender + .call(NodeCommsRequest::GetNewBlockTemplate) + .await?? + { + NodeCommsResponse::NewBlockTemplate(new_block_template) => Ok(new_block_template), + _ => Err(CommsInterfaceError::UnexpectedApiResponse), + } + } + + /// Request from base node service the construction of a block from a block template. + pub async fn get_new_block(&mut self, block_template: NewBlockTemplate) -> Result { + match self + .request_sender + .call(NodeCommsRequest::GetNewBlock(block_template)) + .await?? + { + NodeCommsResponse::NewBlock(block) => Ok(block), + _ => Err(CommsInterfaceError::UnexpectedApiResponse), + } + } + + /// Request the PoW target difficulty for mining on the main chain from the base node service. + pub async fn get_target_difficulty( + &mut self, + pow_algorithm: PowAlgorithm, + ) -> Result + { + match self + .request_sender + .call(NodeCommsRequest::GetTargetDifficulty(pow_algorithm)) + .await?? + { + NodeCommsResponse::TargetDifficulty(difficulty) => Ok(difficulty), + _ => Err(CommsInterfaceError::UnexpectedApiResponse), + } + } + + /// Submit a block to the base node service. + pub async fn submit_block(&mut self, block: Block) -> Result<(), CommsInterfaceError> { + self.block_sender.call(block).await? + } +} diff --git a/base_layer/core/src/base_node/comms_interface/mod.rs b/base_layer/core/src/base_node/comms_interface/mod.rs new file mode 100644 index 0000000000..d2e7adb67d --- /dev/null +++ b/base_layer/core/src/base_node/comms_interface/mod.rs @@ -0,0 +1,36 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod comms_request; +mod comms_response; +mod error; +mod inbound_handlers; +mod local_interface; +mod outbound_interface; + +// Public re-exports +pub use comms_request::{MmrStateRequest, NodeCommsRequest, NodeCommsRequestType}; +pub use comms_response::NodeCommsResponse; +pub use error::CommsInterfaceError; +pub use inbound_handlers::{BlockEvent, InboundNodeCommsHandlers}; +pub use local_interface::LocalNodeCommsInterface; +pub use outbound_interface::OutboundNodeCommsInterface; diff --git a/base_layer/core/src/base_node/comms_interface/outbound_interface.rs b/base_layer/core/src/base_node/comms_interface/outbound_interface.rs new file mode 100644 index 0000000000..3ab59607b9 --- /dev/null +++ b/base_layer/core/src/base_node/comms_interface/outbound_interface.rs @@ -0,0 +1,189 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + base_node::comms_interface::{ + error::CommsInterfaceError, + NodeCommsRequest, + NodeCommsRequestType, + NodeCommsResponse, + }, + blocks::{blockheader::BlockHeader, Block}, + chain_storage::{ChainMetadata, HistoricalBlock}, + transactions::{ + transaction::{TransactionKernel, TransactionOutput}, + types::HashOutput, + }, +}; +use futures::channel::mpsc::UnboundedSender; +use log::*; +use tari_comms::types::CommsPublicKey; +use tari_service_framework::reply_channel::SenderService; +use tower_service::Service; + +pub const LOG_TARGET: &str = "c::bn::comms_interface::outbound_interface"; + +/// The OutboundNodeCommsInterface provides an interface to request information from remove nodes. +#[derive(Clone)] +pub struct OutboundNodeCommsInterface { + request_sender: + SenderService<(NodeCommsRequest, NodeCommsRequestType), Result, CommsInterfaceError>>, + block_sender: UnboundedSender<(Block, Vec)>, +} + +impl OutboundNodeCommsInterface { + /// Construct a new OutboundNodeCommsInterface with the specified SenderService. + pub fn new( + request_sender: SenderService< + (NodeCommsRequest, NodeCommsRequestType), + Result, CommsInterfaceError>, + >, + block_sender: UnboundedSender<(Block, Vec)>, + ) -> Self + { + Self { + request_sender, + block_sender, + } + } + + /// Request metadata from remote base nodes. + pub async fn get_metadata(&mut self) -> Result, CommsInterfaceError> { + let mut responses = Vec::::new(); + self.request_sender + .call((NodeCommsRequest::GetChainMetadata, NodeCommsRequestType::Many)) + .await?? + .into_iter() + .for_each(|response| { + if let NodeCommsResponse::ChainMetadata(metadata) = response { + responses.push(metadata); + } else { + trace!( + target: LOG_TARGET, + "Received unexpected response from peer when requesting metadata: {:?}", + response, + ); + // TODO: Potentially ban peer + } + }); + trace!(target: LOG_TARGET, "Remote metadata requested: {:?}", responses,); + Ok(responses) + } + + /// Fetch the transaction kernels with the provided hashes from remote base nodes. + pub async fn fetch_kernels( + &mut self, + hashes: Vec, + ) -> Result, CommsInterfaceError> + { + if let Some(NodeCommsResponse::TransactionKernels(kernels)) = self + .request_sender + .call((NodeCommsRequest::FetchKernels(hashes), NodeCommsRequestType::Single)) + .await?? + .first() + { + Ok(kernels.clone()) + } else { + Err(CommsInterfaceError::UnexpectedApiResponse) + } + } + + /// Fetch the block headers corresponding to the provided block numbers from remote base nodes. + pub async fn fetch_headers(&mut self, block_nums: Vec) -> Result, CommsInterfaceError> { + if let Some(NodeCommsResponse::BlockHeaders(headers)) = self + .request_sender + .call((NodeCommsRequest::FetchHeaders(block_nums), NodeCommsRequestType::Single)) + .await?? + .first() + { + Ok(headers.clone()) + } else { + Err(CommsInterfaceError::UnexpectedApiResponse) + } + } + + /// Fetch the UTXOs with the provided hashes from remote base nodes. + pub async fn fetch_utxos( + &mut self, + hashes: Vec, + ) -> Result, CommsInterfaceError> + { + if let Some(NodeCommsResponse::TransactionOutputs(utxos)) = self + .request_sender + .call((NodeCommsRequest::FetchUtxos(hashes), NodeCommsRequestType::Single)) + .await?? + .first() + { + Ok(utxos.clone()) + } else { + Err(CommsInterfaceError::UnexpectedApiResponse) + } + } + + /// Fetch the Historical Blocks corresponding to the provided block numbers from remote base nodes. + pub async fn fetch_blocks(&mut self, block_nums: Vec) -> Result, CommsInterfaceError> { + if let Some(NodeCommsResponse::HistoricalBlocks(blocks)) = self + .request_sender + .call((NodeCommsRequest::FetchBlocks(block_nums), NodeCommsRequestType::Single)) + .await?? + .first() + { + Ok(blocks.clone()) + } else { + Err(CommsInterfaceError::UnexpectedApiResponse) + } + } + + /// Fetch the Blocks corresponding to the provided block hashes from remote base nodes. The requested blocks could + /// be chain blocks or orphan blocks. + pub async fn fetch_blocks_with_hashes( + &mut self, + block_hashes: Vec, + ) -> Result, CommsInterfaceError> + { + if let Some(NodeCommsResponse::HistoricalBlocks(blocks)) = self + .request_sender + .call(( + NodeCommsRequest::FetchBlocksWithHashes(block_hashes), + NodeCommsRequestType::Single, + )) + .await?? + .first() + { + Ok(blocks.clone()) + } else { + Err(CommsInterfaceError::UnexpectedApiResponse) + } + } + + /// Transmit a block to remote base nodes, excluding the provided peers. + pub async fn propagate_block( + &mut self, + block: Block, + exclude_peers: Vec, + ) -> Result<(), CommsInterfaceError> + { + self.block_sender + .unbounded_send((block, exclude_peers)) + .map_err(|_| CommsInterfaceError::BroadcastFailed) + } +} diff --git a/infrastructure/crypto/benches/mod.rs b/base_layer/core/src/base_node/consts.rs similarity index 78% rename from infrastructure/crypto/benches/mod.rs rename to base_layer/core/src/base_node/consts.rs index 1210fd15c1..c778ea2d1b 100644 --- a/infrastructure/crypto/benches/mod.rs +++ b/base_layer/core/src/base_node/consts.rs @@ -1,4 +1,4 @@ -// Copyright 2019. The Tari Project +// Copyright 2020. The Tari Project // // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the // following conditions are met: @@ -19,16 +19,10 @@ // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Portions of this file were originally copyrighted (c) 2018 The Grin Developers, issued under the Apache License, -// Version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0. - -use criterion::criterion_main; - -pub mod range_proof; -pub mod signatures; -use range_proof::range_proofs; -use signatures::signatures; +use std::time::Duration; -criterion_main!(signatures, range_proofs); +/// The allocated waiting time for a request waiting for service responses from remote base nodes. +pub const BASE_NODE_SERVICE_REQUEST_TIMEOUT: Duration = Duration::from_secs(60); +/// The fraction of responses that need to be received for a corresponding service request to be finalize. +pub const BASE_NODE_SERVICE_DESIRED_RESPONSE_FRACTION: f32 = 0.6; diff --git a/base_layer/core/src/base_node/mod.rs b/base_layer/core/src/base_node/mod.rs index 4a31da4fc2..ef1512f6ca 100644 --- a/base_layer/core/src/base_node/mod.rs +++ b/base_layer/core/src/base_node/mod.rs @@ -32,10 +32,25 @@ //! More details about the implementation are presented in //! [RFC-0111](https://rfc.tari.com/RFC-0111_BaseNodeArchitecture.html). -mod base_node; -// mod block_validation_service; -// mod synchronisation_service; -// mod transaction_validation_service; +cfg_if! { + if #[cfg(feature = "base_node")] { + mod backoff; + mod base_node; -// Public re-exports -pub use base_node::BaseNode; + pub mod comms_interface; + pub mod consts; + pub mod service; + pub mod chain_metadata_service; + pub mod states; + // Public re-exports + pub use backoff::BackOff; + pub use base_node::{BaseNodeStateMachine, BaseNodeStateMachineConfig}; + pub use comms_interface::{LocalNodeCommsInterface, OutboundNodeCommsInterface}; + } +} + +cfg_if! { + if #[cfg(any(feature = "base_node", feature = "base_node_proto"))] { + pub mod proto; + } +} diff --git a/base_layer/core/src/base_node/proto/chain_metadata.proto b/base_layer/core/src/base_node/proto/chain_metadata.proto new file mode 100644 index 0000000000..6e3e15a7b7 --- /dev/null +++ b/base_layer/core/src/base_node/proto/chain_metadata.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +import "google/protobuf/wrappers.proto"; + +package tari.base_node; + +message ChainMetadata { + // The current chain height, or the block number of the longest valid chain, or `None` if there is no chain + google.protobuf.UInt64Value height_of_longest_chain = 1; + // The block hash of the current tip of the longest valid chain, or `None` for an empty chain + google.protobuf.BytesValue best_block = 2; + // The number of blocks back from the tip that this database tracks. A value of 0 indicates that all blocks are + // tracked (i.e. the database is in full archival mode). + uint64 pruning_horizon = 4; +} diff --git a/base_layer/core/src/base_node/proto/chain_metadata.rs b/base_layer/core/src/base_node/proto/chain_metadata.rs new file mode 100644 index 0000000000..4ad3914476 --- /dev/null +++ b/base_layer/core/src/base_node/proto/chain_metadata.rs @@ -0,0 +1,44 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::base_node as proto; +use crate::chain_storage::ChainMetadata; + +impl From for ChainMetadata { + fn from(metadata: proto::ChainMetadata) -> Self { + Self { + height_of_longest_chain: metadata.height_of_longest_chain, + best_block: metadata.best_block, + pruning_horizon: metadata.pruning_horizon, + } + } +} + +impl From for proto::ChainMetadata { + fn from(metadata: ChainMetadata) -> Self { + Self { + height_of_longest_chain: metadata.height_of_longest_chain, + best_block: metadata.best_block, + pruning_horizon: metadata.pruning_horizon, + } + } +} diff --git a/base_layer/core/src/base_node/proto/mmr_tree.proto b/base_layer/core/src/base_node/proto/mmr_tree.proto new file mode 100644 index 0000000000..7c92ebbc5e --- /dev/null +++ b/base_layer/core/src/base_node/proto/mmr_tree.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +import "google/protobuf/wrappers.proto"; + +package tari.base_node; + +enum MmrTree { + MmrTreeNone = 0; + MmrTreeUtxo = 1; + MmrTreeKernel = 2; + MmrTreeRangeProof = 3; +} diff --git a/base_layer/core/src/base_node/proto/mmr_tree.rs b/base_layer/core/src/base_node/proto/mmr_tree.rs new file mode 100644 index 0000000000..b8edbf9e70 --- /dev/null +++ b/base_layer/core/src/base_node/proto/mmr_tree.rs @@ -0,0 +1,50 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::base_node as proto; +use crate::chain_storage::MmrTree; +use std::convert::TryFrom; + +impl TryFrom for MmrTree { + type Error = String; + + fn try_from(tree: proto::MmrTree) -> Result { + use proto::MmrTree::*; + Ok(match tree { + None => return Err("MmrTree not provided".to_string()), + Utxo => MmrTree::Utxo, + Kernel => MmrTree::Kernel, + RangeProof => MmrTree::RangeProof, + }) + } +} + +impl From for proto::MmrTree { + fn from(tree: MmrTree) -> Self { + use MmrTree::*; + match tree { + Utxo => proto::MmrTree::Utxo, + Kernel => proto::MmrTree::Kernel, + RangeProof => proto::MmrTree::RangeProof, + } + } +} diff --git a/base_layer/core/src/base_node/proto/mod.rs b/base_layer/core/src/base_node/proto/mod.rs new file mode 100644 index 0000000000..e5c367cb03 --- /dev/null +++ b/base_layer/core/src/base_node/proto/mod.rs @@ -0,0 +1,38 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +pub mod base_node { + include!(concat!(env!("OUT_DIR"), "/", "tari.base_node.rs")); +} +use crate::proto::core; +// Required for `super::types` used in generated files +use crate::transactions::proto::types; + +cfg_if! { + if #[cfg(feature = "base_node")] { + pub mod chain_metadata; + pub mod mmr_tree; + pub mod request; + pub mod response; + pub use base_node::{BaseNodeServiceRequest, BaseNodeServiceResponse, ChainMetadata}; + } +} diff --git a/base_layer/core/src/base_node/proto/request.proto b/base_layer/core/src/base_node/proto/request.proto new file mode 100644 index 0000000000..6b491dd02a --- /dev/null +++ b/base_layer/core/src/base_node/proto/request.proto @@ -0,0 +1,38 @@ +syntax = "proto3"; + +import "block.proto"; + +package tari.base_node; + +// Request type for a received BaseNodeService request. +message BaseNodeServiceRequest { + uint64 request_key = 1; + oneof request { + // Indicates a GetChainMetadata request. The value of the bool should be ignored. + bool get_chain_metadata = 2; + // Indicates a FetchKernels request. + HashOutputs fetch_kernels = 3; + // Indicates a FetchHeaders request. + BlockHeights fetch_headers = 4; + // Indicates a FetchUtxos request. + HashOutputs fetch_utxos = 5; + // Indicates a FetchBlocks request. + BlockHeights fetch_blocks = 6; + // Indicates a FetchBlocksWithHashes request. + HashOutputs fetch_blocks_with_hashes = 7; + // Indicates a GetNewBlockTemplate request. + bool get_new_block_template = 8; + // Indicates a GetNewBlock request. + tari.core.NewBlockTemplate get_new_block = 9; + // Indicates a GetTargetDifficulty request. + uint64 get_target_difficulty = 10; + } +} + +message BlockHeights { + repeated uint64 heights = 1; +} + +message HashOutputs { + repeated bytes outputs = 1; +} diff --git a/base_layer/core/src/base_node/proto/request.rs b/base_layer/core/src/base_node/proto/request.rs new file mode 100644 index 0000000000..840d2535f7 --- /dev/null +++ b/base_layer/core/src/base_node/proto/request.rs @@ -0,0 +1,80 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::base_node::{base_node_service_request::Request as ProtoNodeCommsRequest, BlockHeights, HashOutputs}; +use crate::{base_node::comms_interface as ci, proof_of_work::PowAlgorithm, transactions::types::HashOutput}; +use std::convert::{TryFrom, TryInto}; + +//---------------------------------- BaseNodeRequest --------------------------------------------// +impl TryInto for ProtoNodeCommsRequest { + type Error = String; + + fn try_into(self) -> Result { + use ProtoNodeCommsRequest::*; + let request = match self { + // Field was not specified + GetChainMetadata(_) => ci::NodeCommsRequest::GetChainMetadata, + FetchKernels(hash_outputs) => ci::NodeCommsRequest::FetchKernels(hash_outputs.outputs), + FetchHeaders(block_heights) => ci::NodeCommsRequest::FetchHeaders(block_heights.heights), + FetchUtxos(hash_outputs) => ci::NodeCommsRequest::FetchUtxos(hash_outputs.outputs), + FetchBlocks(block_heights) => ci::NodeCommsRequest::FetchBlocks(block_heights.heights), + FetchBlocksWithHashes(block_hashes) => ci::NodeCommsRequest::FetchBlocksWithHashes(block_hashes.outputs), + GetNewBlockTemplate(_) => ci::NodeCommsRequest::GetNewBlockTemplate, + GetNewBlock(block_template) => ci::NodeCommsRequest::GetNewBlock(block_template.try_into()?), + GetTargetDifficulty(pow_algo) => { + ci::NodeCommsRequest::GetTargetDifficulty(PowAlgorithm::try_from(pow_algo)?) + }, + }; + Ok(request) + } +} + +impl From for ProtoNodeCommsRequest { + fn from(request: ci::NodeCommsRequest) -> Self { + use ci::NodeCommsRequest::*; + match request { + GetChainMetadata => ProtoNodeCommsRequest::GetChainMetadata(true), + FetchKernels(hash_outputs) => ProtoNodeCommsRequest::FetchKernels(hash_outputs.into()), + FetchHeaders(block_heights) => ProtoNodeCommsRequest::FetchHeaders(block_heights.into()), + FetchUtxos(hash_outputs) => ProtoNodeCommsRequest::FetchUtxos(hash_outputs.into()), + FetchBlocks(block_heights) => ProtoNodeCommsRequest::FetchBlocks(block_heights.into()), + FetchBlocksWithHashes(block_hashes) => ProtoNodeCommsRequest::FetchBlocksWithHashes(block_hashes.into()), + GetNewBlockTemplate => ProtoNodeCommsRequest::GetNewBlockTemplate(true), + GetNewBlock(block_template) => ProtoNodeCommsRequest::GetNewBlock(block_template.into()), + GetTargetDifficulty(pow_algo) => ProtoNodeCommsRequest::GetTargetDifficulty(pow_algo as u64), + } + } +} + +//---------------------------------- Wrappers --------------------------------------------// + +impl From> for HashOutputs { + fn from(outputs: Vec) -> Self { + Self { outputs } + } +} + +impl From> for BlockHeights { + fn from(heights: Vec) -> Self { + Self { heights } + } +} diff --git a/base_layer/core/src/base_node/proto/response.proto b/base_layer/core/src/base_node/proto/response.proto new file mode 100644 index 0000000000..dfdd3c0c16 --- /dev/null +++ b/base_layer/core/src/base_node/proto/response.proto @@ -0,0 +1,46 @@ +syntax = "proto3"; + +import "transaction.proto"; +import "block.proto"; +import "chain_metadata.proto"; + +package tari.base_node; + +// Response type for a received BaseNodeService requests +message BaseNodeServiceResponse { + uint64 request_key = 1; + oneof response { + // Indicates a ChainMetadata response. + ChainMetadata chain_metadata = 2; + // Indicates a TransactionKernels response. + TransactionKernels transaction_kernels = 3; + // Indicates a BlockHeaders response. + BlockHeaders block_headers = 4; + // Indicates a TransactionOutputs response. + TransactionOutputs transaction_outputs = 5; + // Indicates a HistoricalBlocks response. + HistoricalBlocks historical_blocks = 6; + // Indicates a NewBlockTemplate response. + tari.core.NewBlockTemplate new_block_template = 7; + // Indicates a NewBlock response. + tari.core.Block new_block = 8; + // Indicates a TargetDifficulty response. + uint64 target_difficulty = 9; + } +} + +message BlockHeaders { + repeated tari.core.BlockHeader headers = 1; +} + +message TransactionKernels { + repeated tari.types.TransactionKernel kernels = 1; +} + +message TransactionOutputs { + repeated tari.types.TransactionOutput outputs = 1; +} + +message HistoricalBlocks { + repeated tari.core.HistoricalBlock blocks = 1; +} diff --git a/base_layer/core/src/base_node/proto/response.rs b/base_layer/core/src/base_node/proto/response.rs new file mode 100644 index 0000000000..320c2e0b63 --- /dev/null +++ b/base_layer/core/src/base_node/proto/response.rs @@ -0,0 +1,135 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +pub use super::base_node::base_node_service_response::Response as ProtoNodeCommsResponse; +use super::base_node::{ + BlockHeaders as ProtoBlockHeaders, + HistoricalBlocks as ProtoHistoricalBlocks, + TransactionKernels as ProtoTransactionKernels, + TransactionOutputs as ProtoTransactionOutputs, +}; +use crate::{ + base_node::comms_interface as ci, + proof_of_work::Difficulty, + proto::core as core_proto_types, + transactions::proto::{types as transactions_proto, utils::try_convert_all}, +}; +use std::{ + convert::TryInto, + iter::{FromIterator, Iterator}, +}; + +impl TryInto for ProtoNodeCommsResponse { + type Error = String; + + fn try_into(self) -> Result { + use ProtoNodeCommsResponse::*; + let response = match self { + ChainMetadata(chain_metadata) => ci::NodeCommsResponse::ChainMetadata(chain_metadata.into()), + TransactionKernels(kernels) => { + let kernels = try_convert_all(kernels.kernels)?; + ci::NodeCommsResponse::TransactionKernels(kernels) + }, + BlockHeaders(headers) => { + let headers = try_convert_all(headers.headers)?; + ci::NodeCommsResponse::BlockHeaders(headers) + }, + TransactionOutputs(outputs) => { + let outputs = try_convert_all(outputs.outputs)?; + ci::NodeCommsResponse::TransactionOutputs(outputs) + }, + HistoricalBlocks(blocks) => { + let blocks = try_convert_all(blocks.blocks)?; + ci::NodeCommsResponse::HistoricalBlocks(blocks) + }, + NewBlockTemplate(block_template) => ci::NodeCommsResponse::NewBlockTemplate(block_template.try_into()?), + NewBlock(block) => ci::NodeCommsResponse::NewBlock(block.try_into()?), + TargetDifficulty(difficulty) => ci::NodeCommsResponse::TargetDifficulty(Difficulty::from(difficulty)), + }; + + Ok(response) + } +} + +impl From for ProtoNodeCommsResponse { + fn from(response: ci::NodeCommsResponse) -> Self { + use ci::NodeCommsResponse::*; + match response { + ChainMetadata(chain_metadata) => ProtoNodeCommsResponse::ChainMetadata(chain_metadata.into()), + TransactionKernels(kernels) => { + let kernels = kernels.into_iter().map(Into::into).collect(); + ProtoNodeCommsResponse::TransactionKernels(kernels) + }, + BlockHeaders(headers) => { + let block_headers = headers.into_iter().map(Into::into).collect(); + ProtoNodeCommsResponse::BlockHeaders(block_headers) + }, + TransactionOutputs(outputs) => { + let outputs = outputs.into_iter().map(Into::into).collect(); + ProtoNodeCommsResponse::TransactionOutputs(outputs) + }, + HistoricalBlocks(historical_blocks) => { + let historical_blocks = historical_blocks.into_iter().map(Into::into).collect(); + ProtoNodeCommsResponse::HistoricalBlocks(historical_blocks) + }, + NewBlockTemplate(block_template) => ProtoNodeCommsResponse::NewBlockTemplate(block_template.into()), + NewBlock(block) => ProtoNodeCommsResponse::NewBlock(block.into()), + TargetDifficulty(difficulty) => ProtoNodeCommsResponse::TargetDifficulty(difficulty.as_u64()), + } + } +} + +//---------------------------------- Collection impls --------------------------------------------// + +// The following allow `Iterator::collect` to collect into these repeated types + +impl FromIterator for ProtoTransactionKernels { + fn from_iter>(iter: T) -> Self { + Self { + kernels: iter.into_iter().collect(), + } + } +} + +impl FromIterator for ProtoBlockHeaders { + fn from_iter>(iter: T) -> Self { + Self { + headers: iter.into_iter().collect(), + } + } +} + +impl FromIterator for ProtoTransactionOutputs { + fn from_iter>(iter: T) -> Self { + Self { + outputs: iter.into_iter().collect(), + } + } +} + +impl FromIterator for ProtoHistoricalBlocks { + fn from_iter>(iter: T) -> Self { + Self { + blocks: iter.into_iter().collect(), + } + } +} diff --git a/base_layer/p2p/src/services/comms_outbound/error.rs b/base_layer/core/src/base_node/service/error.rs similarity index 79% rename from base_layer/p2p/src/services/comms_outbound/error.rs rename to base_layer/core/src/base_node/service/error.rs index a57b23ff6d..0376ae798a 100644 --- a/base_layer/p2p/src/services/comms_outbound/error.rs +++ b/base_layer/core/src/base_node/service/error.rs @@ -20,17 +20,16 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::base_node::comms_interface::CommsInterfaceError; use derive_error::Error; -use tari_comms::{message::MessageError, outbound_message_service::OutboundError}; -use tari_utilities::message_format::MessageFormatError; -use tokio_threadpool::BlockingError; +use tari_comms_dht::outbound::DhtOutboundError; #[derive(Debug, Error)] -pub enum CommsOutboundServiceError { - // Comms errors - OutboundError(OutboundError), - MessageSerializationError(MessageError), - MessageFormatError(MessageFormatError), - #[error(non_std)] - BlockingError(BlockingError), +pub enum BaseNodeServiceError { + CommsInterfaceError(CommsInterfaceError), + DhtOutboundError(DhtOutboundError), + #[error(msg_embedded, no_from, non_std)] + InvalidRequest(String), + #[error(msg_embedded, no_from, non_std)] + InvalidResponse(String), } diff --git a/base_layer/core/src/base_node/service/initializer.rs b/base_layer/core/src/base_node/service/initializer.rs new file mode 100644 index 0000000000..266f248166 --- /dev/null +++ b/base_layer/core/src/base_node/service/initializer.rs @@ -0,0 +1,213 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + base_node::{ + comms_interface::{InboundNodeCommsHandlers, LocalNodeCommsInterface, OutboundNodeCommsInterface}, + proto, + service::service::{BaseNodeService, BaseNodeServiceConfig, BaseNodeStreams}, + }, + blocks::Block, + chain_storage::{BlockchainBackend, BlockchainDatabase}, + consensus::ConsensusManager, + mempool::Mempool, + proto as shared_protos, +}; +use futures::{channel::mpsc::unbounded as futures_mpsc_channel_unbounded, future, Future, Stream, StreamExt}; +use log::*; +use std::{convert::TryFrom, sync::Arc}; +use tari_broadcast_channel::bounded; +use tari_comms_dht::outbound::OutboundMessageRequester; +use tari_p2p::{ + comms_connector::PeerMessage, + domain_message::DomainMessage, + services::utils::{map_decode, ok_or_skip_result}, + tari_message::TariMessageType, +}; +use tari_pubsub::TopicSubscriptionFactory; +use tari_service_framework::{ + handles::ServiceHandlesFuture, + reply_channel, + ServiceInitializationError, + ServiceInitializer, +}; +use tari_shutdown::ShutdownSignal; +use tokio::runtime; + +const LOG_TARGET: &str = "base_node::service::initializer"; + +/// Initializer for the Base Node service handle and service future. +pub struct BaseNodeServiceInitializer +where T: BlockchainBackend +{ + inbound_message_subscription_factory: Arc>>, + blockchain_db: BlockchainDatabase, + mempool: Mempool, + consensus_manager: ConsensusManager, + config: BaseNodeServiceConfig, +} + +impl BaseNodeServiceInitializer +where T: BlockchainBackend +{ + /// Create a new BaseNodeServiceInitializer from the inbound message subscriber. + pub fn new( + inbound_message_subscription_factory: Arc>>, + blockchain_db: BlockchainDatabase, + mempool: Mempool, + consensus_manager: ConsensusManager, + config: BaseNodeServiceConfig, + ) -> Self + { + Self { + inbound_message_subscription_factory, + blockchain_db, + mempool, + consensus_manager, + config, + } + } + + /// Get a stream for inbound Base Node request messages + fn inbound_request_stream(&self) -> impl Stream> { + self.inbound_message_subscription_factory + .get_subscription(TariMessageType::BaseNodeRequest) + .map(map_decode::) + .filter_map(ok_or_skip_result) + } + + /// Get a stream for inbound Base Node response messages + fn inbound_response_stream(&self) -> impl Stream> { + self.inbound_message_subscription_factory + .get_subscription(TariMessageType::BaseNodeResponse) + .map(map_decode::) + .filter_map(ok_or_skip_result) + } + + /// Create a stream of 'New Block` messages + fn inbound_block_stream(&self) -> impl Stream> { + self.inbound_message_subscription_factory + .get_subscription(TariMessageType::NewBlock) + .filter_map(extract_block) + } +} + +async fn extract_block(msg: Arc) -> Option> { + match msg.decode_message::() { + Err(e) => { + warn!( + target: LOG_TARGET, + "Could not decode inbound block message. {}", + e.to_string() + ); + None + }, + Ok(block) => { + let block = match Block::try_from(block) { + Err(e) => { + let origin = &msg.source_peer.public_key; + warn!( + target: LOG_TARGET, + "Inbound block message from {} was ill-formed. {}", origin, e + ); + return None; + }, + Ok(b) => b, + }; + Some(DomainMessage { + source_peer: msg.source_peer.clone(), + dht_header: msg.dht_header.clone(), + inner: block, + }) + }, + } +} + +impl ServiceInitializer for BaseNodeServiceInitializer +where T: BlockchainBackend + 'static +{ + type Future = impl Future>; + + fn initialize( + &mut self, + executor: runtime::Handle, + handles_fut: ServiceHandlesFuture, + shutdown: ShutdownSignal, + ) -> Self::Future + { + // Create streams for receiving Base Node requests and response messages from comms + let inbound_request_stream = self.inbound_request_stream(); + let inbound_response_stream = self.inbound_response_stream(); + let inbound_block_stream = self.inbound_block_stream(); + // Connect InboundNodeCommsInterface and OutboundNodeCommsInterface to BaseNodeService + let (outbound_request_sender_service, outbound_request_stream) = reply_channel::unbounded(); + let (outbound_block_sender_service, outbound_block_stream) = futures_mpsc_channel_unbounded(); + let (local_request_sender_service, local_request_stream) = reply_channel::unbounded(); + let (local_block_sender_service, local_block_stream) = reply_channel::unbounded(); + let outbound_nci = + OutboundNodeCommsInterface::new(outbound_request_sender_service, outbound_block_sender_service); + let (block_event_publisher, block_event_subscriber) = bounded(100); + let local_nci = LocalNodeCommsInterface::new( + local_request_sender_service, + local_block_sender_service, + block_event_subscriber, + ); + let inbound_nch = InboundNodeCommsHandlers::new( + block_event_publisher, + self.blockchain_db.clone(), + self.mempool.clone(), + self.consensus_manager.clone(), + outbound_nci.clone(), + ); + let executer_clone = executor.clone(); // Give BaseNodeService access to the executor + let config = self.config; + + // Register handle to OutboundNodeCommsInterface before waiting for handles to be ready + handles_fut.register(outbound_nci); + handles_fut.register(local_nci); + + executor.spawn(async move { + let handles = handles_fut.await; + + let outbound_message_service = handles + .get_handle::() + .expect("OutboundMessageRequester handle required for BaseNodeService"); + + let streams = BaseNodeStreams::new( + outbound_request_stream, + outbound_block_stream, + inbound_request_stream, + inbound_response_stream, + inbound_block_stream, + local_request_stream, + local_block_stream, + ); + let service = + BaseNodeService::new(executer_clone, outbound_message_service, inbound_nch, config).start(streams); + futures::pin_mut!(service); + future::select(service, shutdown).await; + info!(target: LOG_TARGET, "Base Node Service shutdown"); + }); + + future::ready(Ok(())) + } +} diff --git a/base_layer/core/src/base_node/service/mod.rs b/base_layer/core/src/base_node/service/mod.rs new file mode 100644 index 0000000000..043f3ab18d --- /dev/null +++ b/base_layer/core/src/base_node/service/mod.rs @@ -0,0 +1,33 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod error; +mod initializer; +mod service; +mod service_request; +mod service_response; + +// Public re-exports +pub use initializer::BaseNodeServiceInitializer; +pub use service::{BaseNodeService, BaseNodeServiceConfig}; +pub use service_request::{BaseNodeServiceRequest, RequestKey}; +pub use service_response::BaseNodeServiceResponse; diff --git a/base_layer/core/src/base_node/service/service.rs b/base_layer/core/src/base_node/service/service.rs new file mode 100644 index 0000000000..ca5b022c30 --- /dev/null +++ b/base_layer/core/src/base_node/service/service.rs @@ -0,0 +1,495 @@ +// Copyright 2019 The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + base_node::{ + comms_interface::{ + CommsInterfaceError, + InboundNodeCommsHandlers, + NodeCommsRequest, + NodeCommsRequestType, + NodeCommsResponse, + }, + consts::{BASE_NODE_SERVICE_DESIRED_RESPONSE_FRACTION, BASE_NODE_SERVICE_REQUEST_TIMEOUT}, + proto, + service::{ + error::BaseNodeServiceError, + service_request::{generate_request_key, RequestKey, WaitingRequest}, + }, + }, + blocks::Block, + chain_storage::BlockchainBackend, + proto::core::Block as ProtoBlock, +}; +use futures::{ + channel::{ + mpsc::{channel, Receiver, Sender, UnboundedReceiver}, + oneshot::Sender as OneshotSender, + }, + pin_mut, + stream::StreamExt, + SinkExt, + Stream, +}; +use log::*; +use rand::rngs::OsRng; +use std::{collections::HashMap, convert::TryInto, time::Duration}; +use tari_comms::types::CommsPublicKey; +use tari_comms_dht::{ + domain_message::OutboundDomainMessage, + envelope::NodeDestination, + outbound::{OutboundEncryption, OutboundMessageRequester, SendMessageParams}, +}; +use tari_p2p::{domain_message::DomainMessage, tari_message::TariMessageType}; +use tari_service_framework::RequestContext; +use tokio::runtime; + +const LOG_TARGET: &str = "c::bn::base_node_service::service"; + +/// Configuration for the BaseNodeService. +#[derive(Clone, Copy)] +pub struct BaseNodeServiceConfig { + /// The allocated waiting time for a request waiting for service responses from remote base nodes. + pub request_timeout: Duration, + /// The fraction of responses that need to be received for a corresponding service request to be finalize. + pub desired_response_fraction: f32, +} + +impl Default for BaseNodeServiceConfig { + fn default() -> Self { + Self { + request_timeout: BASE_NODE_SERVICE_REQUEST_TIMEOUT, + desired_response_fraction: BASE_NODE_SERVICE_DESIRED_RESPONSE_FRACTION, + } + } +} + +/// A convenience struct to hold all the BaseNode streams +pub struct BaseNodeStreams { + outbound_request_stream: SOutReq, + outbound_block_stream: UnboundedReceiver<(Block, Vec)>, + inbound_request_stream: SInReq, + inbound_response_stream: SInRes, + inbound_block_stream: SBlockIn, + local_request_stream: SLocalReq, + local_block_stream: SLocalBlock, +} + +impl + BaseNodeStreams +where + SOutReq: Stream< + Item = RequestContext< + (NodeCommsRequest, NodeCommsRequestType), + Result, CommsInterfaceError>, + >, + >, + SInReq: Stream>, + SInRes: Stream>, + SBlockIn: Stream>, + SLocalReq: Stream>>, + SLocalBlock: Stream>>, +{ + pub fn new( + outbound_request_stream: SOutReq, + outbound_block_stream: UnboundedReceiver<(Block, Vec)>, + inbound_request_stream: SInReq, + inbound_response_stream: SInRes, + inbound_block_stream: SBlockIn, + local_request_stream: SLocalReq, + local_block_stream: SLocalBlock, + ) -> Self + { + Self { + outbound_request_stream, + outbound_block_stream, + inbound_request_stream, + inbound_response_stream, + inbound_block_stream, + local_request_stream, + local_block_stream, + } + } +} + +/// The Base Node Service is responsible for handling inbound requests and responses and for sending new requests to +/// remote Base Node Services. +pub struct BaseNodeService { + executor: runtime::Handle, + outbound_message_service: OutboundMessageRequester, + inbound_nch: InboundNodeCommsHandlers, + waiting_requests: HashMap, + timeout_sender: Sender, + timeout_receiver_stream: Option>, + config: BaseNodeServiceConfig, +} + +impl BaseNodeService +where B: BlockchainBackend + 'static +{ + pub fn new( + executor: runtime::Handle, + outbound_message_service: OutboundMessageRequester, + inbound_nch: InboundNodeCommsHandlers, + config: BaseNodeServiceConfig, + ) -> Self + { + let (timeout_sender, timeout_receiver) = channel(100); + Self { + executor, + outbound_message_service, + inbound_nch, + waiting_requests: HashMap::new(), + timeout_sender, + timeout_receiver_stream: Some(timeout_receiver), + config, + } + } + + pub async fn start( + mut self, + streams: BaseNodeStreams, + ) -> Result<(), BaseNodeServiceError> + where + SOutReq: Stream< + Item = RequestContext< + (NodeCommsRequest, NodeCommsRequestType), + Result, CommsInterfaceError>, + >, + >, + SInReq: Stream>, + SInRes: Stream>, + SBlockIn: Stream>, + SLocalReq: Stream>>, + SLocalBlock: Stream>>, + { + let outbound_request_stream = streams.outbound_request_stream.fuse(); + pin_mut!(outbound_request_stream); + let outbound_block_stream = streams.outbound_block_stream.fuse(); + pin_mut!(outbound_block_stream); + let inbound_request_stream = streams.inbound_request_stream.fuse(); + pin_mut!(inbound_request_stream); + let inbound_response_stream = streams.inbound_response_stream.fuse(); + pin_mut!(inbound_response_stream); + let inbound_block_stream = streams.inbound_block_stream.fuse(); + pin_mut!(inbound_block_stream); + let local_request_stream = streams.local_request_stream.fuse(); + pin_mut!(local_request_stream); + let local_block_stream = streams.local_block_stream.fuse(); + pin_mut!(local_block_stream); + let timeout_receiver_stream = self + .timeout_receiver_stream + .take() + .expect("Base Node Service initialized without timeout_receiver_stream") + .fuse(); + pin_mut!(timeout_receiver_stream); + loop { + futures::select! { + // Outbound request messages from the OutboundNodeCommsInterface + outbound_request_context = outbound_request_stream.select_next_some() => { + let ((request,request_type), reply_tx) = outbound_request_context.split(); + let _ = self.handle_outbound_request(reply_tx,request,request_type).await.or_else(|err| { + error!(target: LOG_TARGET, "Failed to handle outbound request message: {:?}", err); + Err(err) + }); + }, + + // Outbound block messages from the OutboundNodeCommsInterface + outbound_block_context = outbound_block_stream.select_next_some() => { + let (block, excluded_peers) = outbound_block_context; + let _ = self.handle_outbound_block(block,excluded_peers).await.or_else(|err| { + error!(target: LOG_TARGET, "Failed to handle outbound block message {:?}",err); + Err(err) + }); + }, + + // Incoming request messages from the Comms layer + domain_msg = inbound_request_stream.select_next_some() => { + let _ = self.handle_incoming_request(domain_msg).await.or_else(|err| { + error!(target: LOG_TARGET, "Failed to handle incoming request message: {:?}", err); + Err(err) + }); + }, + + // Incoming response messages from the Comms layer + domain_msg = inbound_response_stream.select_next_some() => { + let _ = self.handle_incoming_response(domain_msg.into_inner()).await.or_else(|err| { + error!(target: LOG_TARGET, "Failed to handle incoming response message: {:?}", err); + Err(err) + }); + }, + + // Timeout events for waiting requests + timeout_request_key = timeout_receiver_stream.select_next_some() => { + let _ =self.handle_request_timeout(timeout_request_key).await.or_else(|err| { + error!(target: LOG_TARGET, "Failed to handle request timeout event: {:?}", err); + Err(err) + }); + }, + + // Incoming block messages from the Comms layer + block_msg = inbound_block_stream.select_next_some() => { + let _ = self.handle_incoming_block(block_msg).await.or_else(|err| { + error!(target: LOG_TARGET, "Failed to handle incoming block message: {:?}", err); + Err(err) + }); + } + + // Incoming local request messages from the LocalNodeCommsInterface and other local services + local_request_context = local_request_stream.select_next_some() => { + let (request, reply_tx) = local_request_context.split(); + let _ = reply_tx.send(self.inbound_nch.handle_request(&request.into()).await).or_else(|err| { + error!(target: LOG_TARGET, "BaseNodeService failed to send reply to local request {:?}",err); + Err(err) + }); + }, + + // Incoming local block messages from the LocalNodeCommsInterface and other local services + local_block_context = local_block_stream.select_next_some() => { + let (block, reply_tx) = local_block_context.split(); + let _ = reply_tx.send(self.inbound_nch.handle_block(&block.into(),None).await).or_else(|err| { + error!(target: LOG_TARGET, "BaseNodeService failed to send reply to local block submitter {:?}",err); + Err(err) + }); + }, + + complete => { + info!(target: LOG_TARGET, "Base Node service shutting down"); + break; + } + } + } + Ok(()) + } + + async fn handle_incoming_request( + &mut self, + domain_request_msg: DomainMessage, + ) -> Result<(), BaseNodeServiceError> + { + let (origin_public_key, inner_msg) = domain_request_msg.into_origin_and_inner(); + + // Convert proto::BaseNodeServiceRequest to a BaseNodeServiceRequest + let request = inner_msg + .request + .ok_or_else(|| BaseNodeServiceError::InvalidRequest("Received invalid base node request".to_string()))?; + + let response = self + .inbound_nch + .handle_request(&request.try_into().map_err(BaseNodeServiceError::InvalidRequest)?) + .await?; + + let message = proto::BaseNodeServiceResponse { + request_key: inner_msg.request_key, + response: Some(response.into()), + }; + + self.outbound_message_service + .send_direct( + origin_public_key, + OutboundEncryption::EncryptForPeer, + OutboundDomainMessage::new(TariMessageType::BaseNodeResponse, message), + ) + .await?; + + Ok(()) + } + + async fn handle_incoming_response( + &mut self, + incoming_response: proto::BaseNodeServiceResponse, + ) -> Result<(), BaseNodeServiceError> + { + let proto::BaseNodeServiceResponse { request_key, response } = incoming_response; + + let mut finalize_request = false; + match self.waiting_requests.get_mut(&request_key) { + Some(waiting_request) => { + let response = response.and_then(|r| r.try_into().ok()).ok_or_else(|| { + BaseNodeServiceError::InvalidResponse("Received an invalid base node response".to_string()) + })?; + waiting_request.received_responses.push(response); + finalize_request = waiting_request.received_responses.len() >= waiting_request.desired_resp_count; + }, + None => { + info!(target: LOG_TARGET, "Discard incoming unmatched response"); + }, + } + + if finalize_request { + if let Some(waiting_request) = self.waiting_requests.remove(&request_key) { + let WaitingRequest { + mut reply_tx, + received_responses, + .. + } = waiting_request; + if let Some(reply_tx) = reply_tx.take() { + let _ = reply_tx.send(Ok(received_responses).or_else(|resp| { + error!(target: LOG_TARGET, "Failed to send outbound request from Base Node"); + Err(resp) + })); + } + } + } + + Ok(()) + } + + async fn handle_outbound_request( + &mut self, + reply_tx: OneshotSender, CommsInterfaceError>>, + request: NodeCommsRequest, + request_type: NodeCommsRequestType, + ) -> Result<(), CommsInterfaceError> + { + let request_key = generate_request_key(&mut OsRng); + let service_request = proto::BaseNodeServiceRequest { + request_key, + request: Some(request.into()), + }; + + let mut send_msg_params = SendMessageParams::new(); + + match request_type { + NodeCommsRequestType::Single => send_msg_params.random(1), + NodeCommsRequestType::Many => send_msg_params.neighbours(Vec::new()), + }; + + let send_result = self + .outbound_message_service + .send_message( + send_msg_params.finish(), + OutboundDomainMessage::new(TariMessageType::BaseNodeRequest, service_request), + ) + .await + .map_err(|e| CommsInterfaceError::OutboundMessageService(e.to_string()))?; + + match send_result.resolve_ok().await { + Some(tags) if tags.is_empty() => { + let _ = reply_tx + .send(Err(CommsInterfaceError::NoBootstrapNodesConfigured)) + .or_else(|resp| { + error!( + target: LOG_TARGET, + "Failed to send outbound request from Base Node as no bootstrap nodes were configured" + ); + Err(resp) + }); + }, + Some(tags) => { + let dest_count = tags.len(); + // Wait for matching responses to arrive + self.waiting_requests.insert(request_key, WaitingRequest { + reply_tx: Some(reply_tx), + received_responses: Vec::new(), + desired_resp_count: (BASE_NODE_SERVICE_DESIRED_RESPONSE_FRACTION * dest_count as f32).ceil() + as usize, + }); + // Spawn timeout for waiting_request + self.spawn_request_timeout(request_key, self.config.request_timeout) + .await; + }, + None => { + let _ = reply_tx + .send(Err(CommsInterfaceError::BroadcastFailed)) + .or_else(|resp| { + error!( + target: LOG_TARGET, + "Failed to send outbound request from Base Node because DHT outbound broadcast failed" + ); + Err(resp) + }); + }, + } + Ok(()) + } + + async fn handle_outbound_block( + &mut self, + block: Block, + exclude_peers: Vec, + ) -> Result<(), CommsInterfaceError> + { + self.outbound_message_service + .propagate( + NodeDestination::Unknown, + OutboundEncryption::EncryptForPeer, + exclude_peers, + OutboundDomainMessage::new(TariMessageType::NewBlock, ProtoBlock::from(block)), + ) + .await + .map_err(|e| { + error!(target: LOG_TARGET, "Handle outbound block failed: {:?}", e); + CommsInterfaceError::OutboundMessageService(e.to_string()) + }) + .map(|_| ()) + } + + async fn handle_request_timeout(&mut self, request_key: RequestKey) -> Result<(), CommsInterfaceError> { + if let Some(mut waiting_request) = self.waiting_requests.remove(&request_key) { + if let Some(reply_tx) = waiting_request.reply_tx.take() { + let reply_msg = if !waiting_request.received_responses.is_empty() { + Ok(waiting_request.received_responses) + } else { + Err(CommsInterfaceError::RequestTimedOut) + }; + let _ = reply_tx.send(reply_msg.or_else(|resp| { + error!(target: LOG_TARGET, "Failed to send outbound request from Base Node"); + Err(resp) + })); + } + } + Ok(()) + } + + async fn spawn_request_timeout(&self, request_key: RequestKey, timeout: Duration) { + let mut timeout_sender = self.timeout_sender.clone(); + self.executor.spawn(async move { + tokio::time::delay_for(timeout).await; + let _ = timeout_sender.send(request_key).await; + }); + } + + async fn handle_incoming_block( + &mut self, + domain_block_msg: DomainMessage, + ) -> Result<(), BaseNodeServiceError> + { + let DomainMessage::<_> { source_peer, inner, .. } = domain_block_msg; + + info!("New candidate block received for height {}", inner.header.height); + let block: Block = inner.clone().into(); + trace!( + target: LOG_TARGET, + "New block: {}, from: {}", + block, + source_peer.public_key + ); + self.inbound_nch + .handle_block(&block, Some(source_peer.public_key)) + .await?; + + // TODO - retain peer info for stats and potential banning for sending invalid blocks + + Ok(()) + } +} diff --git a/comms/src/message/domain_message_context.rs b/base_layer/core/src/base_node/service/service_request.rs similarity index 59% rename from comms/src/message/domain_message_context.rs rename to base_layer/core/src/base_node/service/service_request.rs index 5d1a47f335..da3c9fd813 100644 --- a/comms/src/message/domain_message_context.rs +++ b/base_layer/core/src/base_node/service/service_request.rs @@ -20,26 +20,31 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{message::message::Message, peer_manager::PeerNodeIdentity, types::CommsPublicKey}; +use crate::base_node::comms_interface::{CommsInterfaceError, NodeCommsRequest, NodeCommsResponse}; +use futures::channel::oneshot::Sender as OneshotSender; +use rand::RngCore; use serde::{Deserialize, Serialize}; -/// The InboundMessage is the container that will be dispatched to the domain handlers. It contains the received -/// message and source identity after the comms level envelope has been removed. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct InboundMessage { - pub peer_source: PeerNodeIdentity, - pub origin_source: CommsPublicKey, - pub message: Message, +pub type RequestKey = u64; + +/// Generate a new random request key to uniquely identify a request and its corresponding responses. +pub fn generate_request_key(rng: &mut R) -> RequestKey +where R: RngCore { + rng.next_u64() +} + +/// The WaitingRequest is used to link incoming responses to the original sent request. When enough responses have been +/// received or the request timeout has been received then the received responses are returned on the reply_tx. +#[derive(Debug)] +pub struct WaitingRequest { + pub(crate) reply_tx: Option, CommsInterfaceError>>>, + pub(crate) received_responses: Vec, + pub(crate) desired_resp_count: usize, } -impl InboundMessage { - /// Construct a new InboundMessage that consist of the peer connection information and the received message - /// header and body - pub fn new(peer_source: PeerNodeIdentity, origin_source: CommsPublicKey, message: Message) -> Self { - InboundMessage { - peer_source, - origin_source, - message, - } - } +/// Request type for a received BaseNodeService request. +#[derive(Debug, Serialize, Deserialize)] +pub struct BaseNodeServiceRequest { + pub request_key: RequestKey, + pub request: NodeCommsRequest, } diff --git a/infrastructure/crypto/src/ristretto/mod.rs b/base_layer/core/src/base_node/service/service_response.rs similarity index 81% rename from infrastructure/crypto/src/ristretto/mod.rs rename to base_layer/core/src/base_node/service/service_response.rs index f83fea5ec0..724ccc66bb 100644 --- a/infrastructure/crypto/src/ristretto/mod.rs +++ b/base_layer/core/src/base_node/service/service_response.rs @@ -20,20 +20,12 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -pub mod constants; -pub mod dalek_range_proof; -pub mod musig; -pub mod pedersen; -pub mod ristretto_keys; -pub mod ristretto_sig; -pub mod serialize; +use crate::base_node::{comms_interface::NodeCommsResponse, service::service_request::RequestKey}; +use serde::{Deserialize, Serialize}; -// Re-export -pub use self::{ - ristretto_keys::{RistrettoPublicKey, RistrettoSecretKey}, - ristretto_sig::RistrettoSchnorr, -}; - -// test modules -#[cfg(test)] -mod test_common; +/// Response type for a received BaseNodeService requests +#[derive(Debug, Serialize, Deserialize)] +pub struct BaseNodeServiceResponse { + pub request_key: RequestKey, + pub response: NodeCommsResponse, +} diff --git a/base_layer/core/src/base_node/states/block_sync.rs b/base_layer/core/src/base_node/states/block_sync.rs new file mode 100644 index 0000000000..a6733f42d1 --- /dev/null +++ b/base_layer/core/src/base_node/states/block_sync.rs @@ -0,0 +1,192 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + base_node::{ + base_node::BaseNodeStateMachine, + states::{InitialSync, ListeningInfo, StateEvent}, + }, + chain_storage::{async_db, BlockchainBackend, ChainMetadata, ChainStorageError}, +}; +use log::*; +use tari_crypto::tari_utilities::{hex::Hex, Hashable}; + +const LOG_TARGET: &str = "c::bn::states::block_sync"; + +// The maximum number of retry attempts a node can perform to request a particular block from remote nodes. +const MAX_BLOCK_REQUEST_RETRY_ATTEMPTS: usize = 5; + +/// Configuration for the Block Synchronization. +#[derive(Clone, Copy)] +pub struct BlockSyncConfig { + pub max_block_request_retry_attempts: usize, +} + +impl Default for BlockSyncConfig { + fn default() -> Self { + Self { + max_block_request_retry_attempts: MAX_BLOCK_REQUEST_RETRY_ATTEMPTS, + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct BlockSyncInfo; + +impl BlockSyncInfo { + pub async fn next_event( + &mut self, + shared: &mut BaseNodeStateMachine, + ) -> StateEvent + { + info!(target: LOG_TARGET, "Synchronizing missing blocks"); + + match synchronize_blocks(shared).await { + Ok(StateEvent::BlocksSynchronized) => { + info!(target: LOG_TARGET, "Block sync state has synchronised"); + StateEvent::BlocksSynchronized + }, + Ok(StateEvent::MaxRequestAttemptsReached) => { + warn!( + target: LOG_TARGET, + "Maximum unsuccessful block request attempts reached" + ); + StateEvent::MaxRequestAttemptsReached + }, + Ok(state_event) => state_event, + Err(e) => StateEvent::FatalError(format!("Synchronizing blocks failed. {}", e)), + } + } +} + +/// State management for Listening -> BlockSync. This change happens when a node has been temporarily disconnected +/// from the network, or a reorg has occurred. +impl From for BlockSyncInfo { + fn from(_old: ListeningInfo) -> Self { + BlockSyncInfo {} + } +} + +/// State management for InitialSync -> BlockSync. This change happens when a (previously synced) node is restarted +/// after being offline for some time. +impl From for BlockSyncInfo { + fn from(_old: InitialSync) -> Self { + BlockSyncInfo {} + } +} + +async fn network_tip_metadata( + shared: &mut BaseNodeStateMachine, +) -> Result { + let metadata_list = shared.comms.get_metadata().await.map_err(|e| e.to_string())?; + // TODO: Use heuristics to weed out outliers / dishonest nodes. + Ok(metadata_list + .into_iter() + .fold(ChainMetadata::default(), |best, current| { + if current.height_of_longest_chain.unwrap_or(0) >= best.height_of_longest_chain.unwrap_or(0) { + current + } else { + best + } + })) +} + +async fn synchronize_blocks( + shared: &mut BaseNodeStateMachine, +) -> Result { + let local_metadata = shared.db.get_metadata().map_err(|e| e.to_string())?; + let network_metadata = network_tip_metadata(shared).await?; + if let Some(best_block_hash) = network_metadata.best_block { + let mut attempts = 0; + let mut sync_block_hash = best_block_hash; + while local_metadata.height_of_longest_chain.unwrap_or(0) < + network_metadata.height_of_longest_chain.unwrap_or(0) + { + // Check if sync hash is on local chain + if async_db::fetch_header_with_block_hash(shared.db.clone(), sync_block_hash.clone()) + .await + .is_ok() + { + return Ok(StateEvent::BlocksSynchronized); + } + // Check if blockchain db already has the sync hash block + if let Ok(block) = async_db::fetch_orphan(shared.db.clone(), sync_block_hash.clone()).await { + attempts = 0; + sync_block_hash = block.header.prev_hash; + continue; + } + + // Request the block from a random peer node and add to chain. + match shared + .comms + .fetch_blocks_with_hashes(vec![sync_block_hash.clone()]) + .await + { + Ok(blocks) => { + debug!(target: LOG_TARGET, "Received {} blocks from peer", blocks.len()); + if let Some(hist_block) = blocks.first() { + let block_hash = hist_block.block().hash(); + if block_hash == sync_block_hash { + let prev_block_hash = hist_block.block().header.prev_hash.clone(); + match shared.db.add_block(hist_block.block().clone()) { + Ok(_) => { + attempts = 0; + sync_block_hash = prev_block_hash; + continue; + }, + Err(ChainStorageError::InvalidBlock) => { + warn!( + target: LOG_TARGET, + "Invalid block {} received from peer. Retrying", + block_hash.to_hex(), + ); + }, + Err(ChainStorageError::ValidationError(_)) => { + warn!( + target: LOG_TARGET, + "Validation on block {} from peer failed. Retrying", + block_hash.to_hex(), + ); + }, + Err(e) => return Err(e.to_string()), + } + } + } + }, + Err(e) => { + warn!( + target: LOG_TARGET, + "Failed to fetch blocks from peer:{:?}. Retrying.", e, + ); + }, + } + + // Attempt again to retrieve the correct block + attempts += 1; + if attempts >= shared.config.block_sync_config.max_block_request_retry_attempts { + return Ok(StateEvent::MaxRequestAttemptsReached); + } + } + } + + Ok(StateEvent::BlocksSynchronized) +} diff --git a/base_layer/p2p/src/macros.rs b/base_layer/core/src/base_node/states/error.rs similarity index 80% rename from base_layer/p2p/src/macros.rs rename to base_layer/core/src/base_node/states/error.rs index 03c4ceadc2..b35a4b4cc5 100644 --- a/base_layer/p2p/src/macros.rs +++ b/base_layer/core/src/base_node/states/error.rs @@ -20,21 +20,17 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -macro_rules! acquire_lock { - ($e:expr, $m:ident) => { - match $e.$m() { - Ok(lock) => lock, - Err(poisoned) => poisoned.into_inner(), - } - }; - ($e:expr) => { - acquire_lock!($e, lock) - }; +use crate::base_node::states::StateEvent; +use derive_error::Error; + +#[derive(Clone, Debug, Error)] +pub enum BaseNodeError { + #[error(msg_embedded, non_std, no_from)] + ConfigurationError(String), } -#[cfg(test)] -macro_rules! acquire_read_lock { - ($e:expr) => { - acquire_lock!($e, read) - }; +impl BaseNodeError { + pub fn as_fatal(&self, preface: &str) -> StateEvent { + StateEvent::FatalError(format!("{} {}", preface, self.to_string())) + } } diff --git a/base_layer/core/src/base_node/states/helpers.rs b/base_layer/core/src/base_node/states/helpers.rs new file mode 100644 index 0000000000..c5136137b3 --- /dev/null +++ b/base_layer/core/src/base_node/states/helpers.rs @@ -0,0 +1,55 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{base_node::states::SyncStatus, chain_storage::ChainMetadata}; + +use log::*; + +/// Given a local and the network chain state respectively, figure out what synchronisation state we should be in. +pub fn determine_sync_mode(local: ChainMetadata, network: ChainMetadata, log_target: &str) -> SyncStatus { + use crate::base_node::states::SyncStatus::*; + match network.height_of_longest_chain { + None => { + info!( + target: log_target, + "The rest of the network doesn't appear to have any up-to-date chain data, so we're going to assume \ + we're at the tip" + ); + UpToDate + }, + Some(network_tip) => { + let local_tip = local.height_of_longest_chain.unwrap_or(0); + if local_tip < network_tip { + info!( + target: log_target, + "Our local blockchain history is a little behind that of the network. We're at block #{}, and the \ + chain tip is at #{}", + local_tip, + network_tip + ); + Lagging(network_tip) + } else { + UpToDate + } + }, + } +} diff --git a/base_layer/core/src/base_node/states/initial_sync.rs b/base_layer/core/src/base_node/states/initial_sync.rs new file mode 100644 index 0000000000..d3336703b2 --- /dev/null +++ b/base_layer/core/src/base_node/states/initial_sync.rs @@ -0,0 +1,265 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + base_node::{ + comms_interface::CommsInterfaceError, + states::{ + Starting, + StateEvent, + StateEvent::{FatalError, MetadataSynced}, + SyncStatus, + }, + BackOff, + BaseNodeStateMachine, + }, + chain_storage::{BlockchainBackend, ChainMetadata}, +}; + +use crate::base_node::states::{block_sync::BlockSyncInfo, helpers::determine_sync_mode, listening::ListeningInfo}; +use log::*; +use std::time::Duration; + +const LOG_TARGET: &str = "c::bn::states::initial_sync"; +// The number of times we'll request the chain metadata before giving up +const MAX_SYNC_ATTEMPTS: usize = 8; + +#[derive(Clone, Debug, PartialEq)] +pub struct InitialSync { + // keeps track of how many times we've tried to sync with the network + backoff: BackOff, + // Every time the metadata request fails, vote for what to do if we ultimately fail + shutdown_votes: usize, + listen_votes: usize, +} + +impl Default for InitialSync { + fn default() -> Self { + Self::new() + } +} + +impl InitialSync { + pub fn new() -> Self { + let backoff = BackOff::new(MAX_SYNC_ATTEMPTS, Duration::from_secs(30), 1.0); + InitialSync { + backoff, + shutdown_votes: 0, + listen_votes: 0, + } + } + + pub async fn next_event( + &mut self, + shared: &mut BaseNodeStateMachine, + ) -> StateEvent + { + info!(target: LOG_TARGET, "Starting blockchain metadata sync"); + self.sync_metadata(shared).await + } + + /// Fetch the blockchain metadata from our internal database and compare it to data received from peers to decide + /// on the next phase of the blockchain synchronisation. + async fn sync_metadata( + &mut self, + shared: &mut BaseNodeStateMachine, + ) -> StateEvent + { + info!(target: LOG_TARGET, "Loading local blockchain metadata."); + let ours = match shared.db.get_metadata() { + Ok(m) => m, + Err(e) => { + let msg = format!("Could not get local blockchain metadata. {}", e.to_string()); + error!(target: LOG_TARGET, "Could not get local blockchain metadata. {:?}", e); + return FatalError(msg); + }, + }; + info!( + target: LOG_TARGET, + "Current local blockchain database information:\n {}", ours + ); + // Fetch peer metadata + let mut theirs = vec![]; + while !self.backoff.is_finished() && !shared.is_stop_requested() { + match shared.comms.get_metadata().await { + Err(e) => { + self.log_error(e); + self.backoff.wait().await; + }, + Ok(data) => { + theirs = data; + self.backoff.stop(); + }, + } + } + if self.backoff.is_stopped() { + self.evaluate_data(ours, theirs) + } else { + self.failure_outcome() + } + } + + fn evaluate_data(&self, ours: ChainMetadata, theirs: Vec) -> StateEvent { + // If there are no other nodes on the network, then we're at the chain tip by definition, so we go into + // listen mode + if theirs.is_empty() { + info!( + target: LOG_TARGET, + "The rest of the network doesn't appear to have any up-to-date chain data, so we're going to assume \ + we're at the tip" + ); + return StateEvent::MetadataSynced(SyncStatus::UpToDate); + } + let network = self.summarize_network_data(theirs); + MetadataSynced(determine_sync_mode(ours, network, LOG_TARGET)) + } + + fn summarize_network_data(&self, data: Vec) -> ChainMetadata { + // TODO: Use heuristics to weed out outliers / dishonest nodes. + // Right now, we a simple strategy of returning the max height + let result = data.into_iter().fold(ChainMetadata::default(), |best, current| { + if current.height_of_longest_chain.unwrap_or(0) >= best.height_of_longest_chain.unwrap_or(0) { + current + } else { + best + } + }); + debug!("Current summarized network data is: {}", result); + result + } + + fn log_error(&mut self, e: CommsInterfaceError) { + let att = self.backoff.attempts(); + let max_att = self.backoff.max_attempts(); + let msg = format!("Attempt {} of {}.", att, max_att); + match e { + // If the request timed out, we may be the only node on the network, thus we're up to date by definition + CommsInterfaceError::RequestTimedOut => { + self.listen_votes += 1; + debug!( + target: LOG_TARGET, + "Network request for chain metadata timed out. {}", msg + ); + }, + CommsInterfaceError::TransportChannelError(e) => { + self.shutdown_votes += 1; + error!( + target: LOG_TARGET, + "The base node input channel has closed unexpectedly. The best way to resolve this issue is to \ + restart the node. {}. {}", + e, + msg + ); + }, + CommsInterfaceError::ChainStorageError(e) => { + self.shutdown_votes += 1; + error!( + target: LOG_TARGET, + "There was a problem accessing the blockchain database. {}. {}.", e, msg + ); + }, + CommsInterfaceError::UnexpectedApiResponse => { + self.listen_votes += 1; + warn!(target: LOG_TARGET, "MetadataSync got an unexpected response. {}", msg); + }, + CommsInterfaceError::NoBootstrapNodesConfigured => { + self.listen_votes += 1; + warn!( + target: LOG_TARGET, + "Cannot connect to the network; No seed nodes are configured. {}", msg + ); + }, + CommsInterfaceError::OutboundMessageService(e) => { + self.shutdown_votes += 1; + error!( + target: LOG_TARGET, + "There was a problem with the outbound message service. {}. {}.", e, msg + ); + }, + CommsInterfaceError::EventStreamError => { + self.shutdown_votes += 1; + warn!(target: LOG_TARGET, "Problem sending event on EventStream. {}", msg); + }, + CommsInterfaceError::MempoolError(e) => { + self.shutdown_votes += 1; + error!( + target: LOG_TARGET, + "There was a problem accessing the mempool. {}. {}.", e, msg + ); + }, + CommsInterfaceError::BroadcastFailed => { + self.shutdown_votes += 1; + error!( + target: LOG_TARGET, + "There was a problem sending with the DHT broadcast middleware. {}.", e, + ); + }, + CommsInterfaceError::DifficultyAdjustmentManagerError(e) => { + self.shutdown_votes += 1; + error!( + target: LOG_TARGET, + "There was a problem accessing the difficulty adjustment manager. {}. {}.", e, msg + ); + }, + } + } + + fn failure_outcome(&self) -> StateEvent { + if self.shutdown_votes > self.listen_votes { + StateEvent::FatalError( + "Could not complete the initial sync. See prior log messages for further details.".to_string(), + ) + } else { + info!( + target: LOG_TARGET, + "We could not complete the chain metadata sync, but this may because we're the only node on the \ + network, or can't connect to any peers. Is the seed list configured properly? We're flipping to the \ + listening state now and hopefully this issue will resolve itself." + ); + StateEvent::MetadataSynced(SyncStatus::UpToDate) + } + } +} + +/// State management for Starting -> InitialSync. This state change occurs every time a node is restarted. +impl From for InitialSync { + fn from(_old_state: Starting) -> Self { + InitialSync::new() + } +} + +/// State management for Listening -> InitialSync. This state change happens when a node has not received any chain +/// metadata messages from any other nodes. This could have been the result of the current node being temporarily +/// disconnected from the network. +impl From for InitialSync { + fn from(_old: ListeningInfo) -> Self { + InitialSync::new() + } +} + +/// State management for BlockSyncInfo -> InitialSync. This state change occurs when the block download requests +/// repeatedly failed. +impl From for InitialSync { + fn from(_old: BlockSyncInfo) -> Self { + InitialSync::new() + } +} diff --git a/base_layer/core/src/base_node/states/listening.rs b/base_layer/core/src/base_node/states/listening.rs new file mode 100644 index 0000000000..adb63899f7 --- /dev/null +++ b/base_layer/core/src/base_node/states/listening.rs @@ -0,0 +1,131 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + base_node::{ + chain_metadata_service::ChainMetadataEvent, + states::{helpers::determine_sync_mode, StateEvent, StateEvent::FatalError, SyncStatus}, + BaseNodeStateMachine, + }, + chain_storage::BlockchainBackend, +}; +use futures::{ + channel::mpsc::{channel, Sender}, + stream::StreamExt, + SinkExt, +}; +use log::*; +use std::time::{Duration, Instant}; +use tokio::runtime; + +const LOG_TARGET: &str = "c::bn::states::listening"; + +// The max duration that the listening state will wait between metadata events before it will start asking for metadata +// from remote nodes. +const LISTENING_SILENCE_TIMEOUT: Duration = Duration::from_secs(300); // 5 minutes + +/// Configuration for the Listening state. +#[derive(Clone, Copy, Debug)] +pub struct ListeningConfig { + pub listening_silence_timeout: Duration, +} + +impl Default for ListeningConfig { + fn default() -> Self { + Self { + listening_silence_timeout: LISTENING_SILENCE_TIMEOUT, + } + } +} + +/// This state listens for chain metadata events received from the liveness and chain metadata service. Based on the +/// received metadata, if it detects that the current node is lagging behind the network it will switch to block sync +/// state. If no metadata is received for a prolonged period of time it will transition to the initial sync state and +/// request chain metadata from remote nodes. +#[derive(Clone, Debug, PartialEq)] +pub struct ListeningInfo; + +impl ListeningInfo { + pub async fn next_event(&mut self, shared: &mut BaseNodeStateMachine) -> StateEvent { + info!(target: LOG_TARGET, "Listening for chain metadata updates"); + + let mut metadata_event_stream = shared.metadata_event_stream.clone().fuse(); + let (timeout_event_sender, timeout_event_receiver) = channel(100); + let mut timeout_event_receiver = timeout_event_receiver.fuse(); + + // Create the initial timeout event + spawn_timeout_event( + &shared.executor, + timeout_event_sender.clone(), + shared.config.listening_config.listening_silence_timeout, + ) + .await; + let mut last_event_time = Instant::now(); + + loop { + futures::select! { + metadata_event = metadata_event_stream.select_next_some() => { + if let ChainMetadataEvent::PeerChainMetadataReceived(chain_metadata_list) = &*metadata_event { + if let Some(network)=chain_metadata_list.first() { + info!(target: LOG_TARGET, "Loading local blockchain metadata."); + let local = match shared.db.get_metadata() { + Ok(m) => m, + Err(e) => { + let msg = format!("Could not get local blockchain metadata. {}", e.to_string()); + return FatalError(msg); + }, + }; + if let SyncStatus::Lagging(h)=determine_sync_mode(local,network.clone(),LOG_TARGET) { + return StateEvent::FallenBehind(SyncStatus::Lagging(h)); + } + } + last_event_time=Instant::now(); + } + }, + + timeout_event= timeout_event_receiver.select_next_some() => { + let timeout_time=Instant::now(); + let time_difference=timeout_time.duration_since(last_event_time); + if time_difference>=shared.config.listening_config.listening_silence_timeout { + return StateEvent::NetworkSilence; + } + else { // Timeout was early, spawn an updated timeout with correct delay + let timeout_delay=shared.config.listening_config.listening_silence_timeout-time_difference; + spawn_timeout_event(&shared.executor,timeout_event_sender.clone(),timeout_delay).await; + } + }, + + complete => { // Shutting down as liveness metadata and timeout streams were closed + return StateEvent::UserQuit; + } + } + } + } +} + +// Spawn a timeout event that will respond on the timeout event sender once the specified time delay has been reached. +async fn spawn_timeout_event(executor: &runtime::Handle, mut timeout_event_sender: Sender, timeout: Duration) { + executor.spawn(async move { + tokio::time::delay_for(timeout).await; + let _ = timeout_event_sender.send(true).await; + }); +} diff --git a/base_layer/core/src/base_node/states/mod.rs b/base_layer/core/src/base_node/states/mod.rs new file mode 100644 index 0000000000..c954a70780 --- /dev/null +++ b/base_layer/core/src/base_node/states/mod.rs @@ -0,0 +1,124 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::fmt::{Display, Error, Formatter}; + +/// The base node state represents the FSM of the base node synchronisation process. +/// +/// ## Starting state +/// The node is in the `Starting`` state when it's first created. After basic internal setup and configuration, it will +/// move to the `InitialSync` state. +/// +/// ## Initial Sync State +/// In this state, we need to obtain +/// i. The height of our chain tip, +/// ii. The height of the chain tip from the network. +/// +/// Once these two values are obtained, we can move to the next state: +/// If we're between the genesis block and the network chain tip, switch to `BlockSync`. +/// Otherwise switch to `Listening` +/// +/// ## BlockSync +/// +/// For each `n` from genesis block + 1 to the network chain tip, submit a request for block `n`. In this state, an +/// entire block is received, and the normal block validation and storage process is followed. The only difference +/// between `BlockSync` and `Listening` is that the former state is actively asking for blocks, while the latter is a +/// passive process. +/// +/// After we have caught up on the chain, switch to `Listening`. +/// +/// If errors occur, re-request the problematic block. +/// +/// Give up after n failures and switch back to `Listening` (if a peer gave an erroneous chain tip and cannot provide +/// the blocks it says it has, we can switch back to `Listening` and try receive blocks passively. +/// +/// Full blocks received while in this state can be stored in the orphan pool until they are needed. +/// +/// ## Listening +/// +/// Passively wait for new blocks to arrive, and process them accordingly. +/// +/// Periodically poll peers to request the chain tip height. If we are more than one block behind the network chain +/// tip, switch to `BlockSync` mode. +/// +/// ## Shutdown +/// +/// Reject all new requests with a `Shutdown` message, complete current validations / tasks, flush all state if +/// required, and then shutdown. +#[derive(Clone, Debug, PartialEq)] +pub enum BaseNodeState { + Starting(Starting), + InitialSync(InitialSync), + BlockSync(BlockSyncInfo), + Listening(ListeningInfo), + Shutdown(Shutdown), +} + +#[derive(Debug, PartialEq)] +pub enum StateEvent { + Initialized, + MetadataSynced(SyncStatus), + BlocksSynchronized, + MaxRequestAttemptsReached, + FallenBehind(SyncStatus), + NetworkSilence, + FatalError(String), + UserQuit, +} + +/// Some state transition functions must return `SyncStatus`. The sync status indicates how far behind the network's +/// blockchain the local node is. It can either be very far behind (`BehindHorizon`), in which case we will just +/// synchronise against the pruning horizon; we're somewhat behind (`Lagging`) and need to download the missing +/// blocks to catch up, or we are `UpToDate`. +#[derive(Debug, PartialEq)] +pub enum SyncStatus { + // We are behind the chain tip. The usize parameter gives the network's chain height. + Lagging(u64), + UpToDate, +} + +impl Display for BaseNodeState { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { + let s = match self { + Self::Starting(_) => "Initializing", + Self::InitialSync(_) => "Synchronizing blockchain metadata", + Self::BlockSync(_) => "Synchronizing blocks", + Self::Listening(_) => "Listening", + Self::Shutdown(_) => "Shutting down", + }; + f.write_str(s) + } +} + +mod block_sync; +mod error; +mod helpers; +mod initial_sync; +mod listening; +mod shutdown_state; +mod starting_state; + +pub use block_sync::{BlockSyncConfig, BlockSyncInfo}; +pub use initial_sync::InitialSync; +pub use listening::{ListeningConfig, ListeningInfo}; +pub use shutdown_state::Shutdown; +pub use starting_state::Starting; diff --git a/base_layer/core/src/base_node/states/shutdown_state.rs b/base_layer/core/src/base_node/states/shutdown_state.rs new file mode 100644 index 0000000000..4025f690b8 --- /dev/null +++ b/base_layer/core/src/base_node/states/shutdown_state.rs @@ -0,0 +1,36 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use log::*; + +const LOG_TARGET: &str = "c::bn::states::shutdown_state"; + +#[derive(Clone, Debug, PartialEq)] +pub struct Shutdown { + reason: String, +} + +impl Shutdown { + pub fn with_reason(reason: String) -> Self { + info!(target: LOG_TARGET, "A fatal error has occurred. {}", reason); + Self { reason } + } +} diff --git a/base_layer/core/src/base_node/states/starting_state.rs b/base_layer/core/src/base_node/states/starting_state.rs new file mode 100644 index 0000000000..2715d3603e --- /dev/null +++ b/base_layer/core/src/base_node/states/starting_state.rs @@ -0,0 +1,54 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +use crate::{ + base_node::{ + states::{error::BaseNodeError, StateEvent}, + BaseNodeStateMachine, + }, + chain_storage::BlockchainBackend, +}; +use log::*; +use std::ops::Deref; + +const LOG_TARGET: &str = "c::bn::states::starting_state"; + +// The data structure handling Base Node Startup +#[derive(Clone, Debug, PartialEq)] +pub struct Starting; + +impl Starting { + /// Apply the configuration settings for this node. + fn apply_config(&mut self) -> Result<(), BaseNodeError> { + // TODO apply configuration + Ok(()) + } + + pub async fn next_event(&mut self, shared: &BaseNodeStateMachine) -> StateEvent { + info!(target: LOG_TARGET, "Configuring node."); + if let Err(err) = self.apply_config() { + return err.as_fatal("There was an error with the base node configuration."); + } + info!(target: LOG_TARGET, "Node configuration complete."); + BaseNodeStateMachine::::check_interrupt(shared.user_stopped.deref(), StateEvent::Initialized) + } +} diff --git a/base_layer/core/src/blockchain/chain.rs b/base_layer/core/src/blockchain/chain.rs deleted file mode 100644 index 1c95d78c3a..0000000000 --- a/base_layer/core/src/blockchain/chain.rs +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2019. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -//! This file provides the structs and functions that persist the block chain state, and re-org logic. Because a re-org -//! can happen, we need to keep track of orphan blocks. The Merkle Mountain Range crate we use allows us to rewind -//! checkpoints. Internally it keeps track of what was changed between checkpoints. We use these rewind blocks in the -//! case where we need to do a re-org where a forked chain with a greater accumulated pow emerges. In the case of the -//! MMR, a checkpoint is equal to a block. -//! -//! The MMR also provides a method to save the MMR to disc. This is internally handled and we use LMDB to store the MMR. - -use crate::{ - blockchain::{ - block_chain_state::BlockchainState, - error::{ChainError, StateError}, - }, - blocks::block::Block, - consensus::ConsensusRules, -}; -use std::collections::HashMap; -use tari_utilities::Hashable; - -type BlockHash = [u8; 32]; -pub const MAX_ORPHAN_AGE: u64 = 1000; - -/// The Chain is the actual data structure to represent the blockchain -pub struct Chain { - /// This the the current UTXO set, kernels and headers - pub block_chain_state: BlockchainState, - /// This is all valid blocks which dont have a parent trace to the genesis block - orphans: HashMap, - /// The current head's total proof of work - pub current_total_pow: ProofOfWork, -} - -impl Chain { - - /// Adds a new block to the persistent state. - /// - /// This process includes: - /// * Adds the range proof hashes to the range proof MMR - /// * Marks all inputs in the block as spent - /// * Adds all outputs to the UTXO set and MMR - /// * Adds the transaction kernels to the kernel MMR - /// * Adds the block header to the header MMR - /// An error is returned if: - /// * the block has already been added (by checking whether the block header hash exists already) - /// * any of the inputs were not in the UTXO set or were marked as spent already - /// * There was a problem creating the checkpoints - pub fn add_new_block(&mut self, new_block: &Block) -> Result<(), StateError> { - if let Some(_) = self.headers.get_object(&new_block.header.hash()) { - return Err(StateError::DuplicateBlock); - } - // We add the range proofs just to strip them out again 2 lines later??? - // TODO Clearly there's an optimisation here somewhere - for output in &new_block.body.outputs { - self.range_proofs.push(output.proof().clone())?; - } - self.mark_outputs_as_spent(&new_block.body.inputs)?; - self.strip_range_proofs(&new_block.body.outputs); - // All seems valid, lets add the objects to the state - for output in &new_block.body.outputs { - self.utxos.push(output.clone().into())?; - } - self.kernels.append(new_block.body.kernels.clone())?; - self.headers.push(new_block.header.clone())?; - // Apply a new checkpoint so that we can rewind to this point in the future if necessary - self.check_point_state() - } - - - /// This function will process a newly received block - pub fn add_new_block(&mut self, new_block: Block, consensus_rules: &ConsensusRules) -> Result<(), ChainError> { - let result = match self.block_chain_state.add_new_block(&new_block) { - // block was processed fine and added to chain - Ok(_) => { - let height = new_block.header.height; - self.current_total_pow = new_block.header.pow; - self.orphans.retain(|_, b| height - b.header.height < MAX_ORPHAN_AGE); - // todo search for new orphans that we might apply - Ok(()) - }, - // block seems valid, but its orphaned - Err(StateError::OrphanBlock) => self.orphaned_block(new_block, consensus_rules), - Err(e) => Err(ChainError::StateProcessingError(e)), - }; - if result.is_err() { - self.block_chain_state.reset_chain_state()?; - } - result - } - - /// Internal helper function to do orphan logic - /// The function will store the new orphan block and check if it contains a re-org - fn orphaned_block(&mut self, new_block: Block) -> Result<(), ChainError> { - if self.orphans.contains_key(&new_block.header.hash()[..]) { - return Err(ChainError::StateProcessingError(StateError::DuplicateBlock)); - }; - let mut hash = [0; 32]; - hash.copy_from_slice(&new_block.header.hash()); - let pow = new_block.header.pow.clone(); - self.orphans.insert(hash, new_block); - let mut currently_used_orphans: Vec = Vec::new(); - if self.current_total_pow.has_more_accum_work_than(&pow) { - // we have a potential re-org here - let result = self.handle_re_org(&hash, &mut currently_used_orphans); - let result = if result.is_err() { - self.block_chain_state.reset_chain_state()?; - result - } else { - for hash in ¤tly_used_orphans { - self.orphans.remove(hash); - } - self.current_total_pow = pow; - self.block_chain_state - .save_state() - .map_err(ChainError::StateProcessingError) - }; - result - } else { - Err(ChainError::StateProcessingError(StateError::OrphanBlock)) - } - } - - /// Internal recursive function to handle re orgs - /// The function will go and search for a known parent and if found, will apply the list of orphan blocks to re-org - fn handle_re_org( - &mut self, - block_hash: &BlockHash, - mut unorphaned_blocks: &mut Vec, - ) -> Result<(), ChainError> - { - // The searched hash should always be in the orphan list - unorphaned_blocks.push(block_hash.clone()); // save all orphans we have used - let block = &self.orphans[block_hash].clone(); - let parent = self.block_chain_state.headers.get_object(&block.header.prev_hash); - if parent.is_some() { - // we know of parent so we can re-org - let h = parent.unwrap().height; - self.block_chain_state - .rewind_state((self.block_chain_state.get_tip_height() - h) as usize)?; - return self - .block_chain_state - .add_new_block(&block) - .map_err(ChainError::StateProcessingError); - }; - let prev_block = self.orphans.get(&block.header.prev_hash); - if prev_block.is_none() { - return Err(ChainError::StateProcessingError(StateError::OrphanBlock)); - }; - - let mut hash = [0; 32]; - hash.copy_from_slice(&prev_block.unwrap().header.hash()); - let result = self.handle_re_org(&hash, &mut unorphaned_blocks); - - if result.is_ok() { - return self - .block_chain_state - .add_new_block(&block) - .map_err(ChainError::StateProcessingError); - } - - result - } -} diff --git a/base_layer/core/src/blocks/block.rs b/base_layer/core/src/blocks/block.rs index 596dc3daa2..79010525be 100644 --- a/base_layer/core/src/blocks/block.rs +++ b/base_layer/core/src/blocks/block.rs @@ -23,23 +23,34 @@ // Portions of this file were originally copyrighted (c) 2018 The Grin Developers, issued under the Apache License, // Version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0. use crate::{ - blocks::{aggregated_body::AggregateBody, BlockHeader}, - consensus::ConsensusRules, - proof_of_work::PowError, - tari_amount::*, - transaction::*, - types::{Commitment, TariProofOfWork, COMMITMENT_FACTORY, PROVER}, + blocks::BlockHeader, + consensus::ConsensusConstants, + proof_of_work::ProofOfWork, + transactions::{ + aggregated_body::AggregateBody, + tari_amount::MicroTari, + transaction::{ + OutputFlags, + Transaction, + TransactionError, + TransactionInput, + TransactionKernel, + TransactionOutput, + }, + }, }; use derive_error::Error; +use log::*; use serde::{Deserialize, Serialize}; -use tari_utilities::Hashable; +use std::fmt::{Display, Formatter}; +use tari_crypto::tari_utilities::Hashable; + +pub const LOG_TARGET: &str = "c::bl::block"; #[derive(Clone, Debug, PartialEq, Error)] pub enum BlockValidationError { // A transaction in the block failed to validate TransactionError(TransactionError), - // Invalid Proof of work for the block - ProofOfWorkError(PowError), // Invalid kernel in block InvalidKernel, // Invalid input in block @@ -48,6 +59,10 @@ pub enum BlockValidationError { InputMaturity, // Invalid coinbase maturity in block or more than one coinbase InvalidCoinbase, + // Mismatched MMR roots + MismatchedMmrRoots, + // The block contains transactions that should have been cut through. + NoCutThrough, } /// A Tari block. Blocks are linked together into a blockchain. @@ -58,55 +73,39 @@ pub struct Block { } impl Block { - /// This function will check the block to ensure that all UTXO's are validly constructed and that all signatures are - /// valid. It does _not_ check that the inputs exist in the current UTXO set; - /// nor does it check that the PoW is the largest accumulated PoW value. - pub fn check_internal_consistency(&self, rules: &ConsensusRules) -> Result<(), BlockValidationError> { - let block_reward = rules.emission_schedule().block_reward(self.header.height); - let offset = &self.header.total_kernel_offset; - let total_coinbase = self.calculate_coinbase_and_fees(block_reward); - self.body - .validate_internal_consistency(&offset, total_coinbase, &PROVER, &COMMITMENT_FACTORY)?; - self.check_stxo_rules()?; - self.check_utxo_rules(rules)?; - self.check_pow() - } - - // create a total_coinbase offset containing all fees for the validation - fn calculate_coinbase_and_fees(&self, block_reward: MicroTari) -> MicroTari { - let mut coinbase = block_reward; - for kernel in &self.body.kernels { - coinbase += kernel.fee; - } - coinbase - } - - pub fn check_pow(&self) -> Result<(), BlockValidationError> { - Ok(()) + /// This function will calculate the total fees contained in a block + pub fn calculate_fees(&self) -> MicroTari { + self.body.kernels().iter().fold(0.into(), |sum, x| sum + x.fee) } /// This function will check spent kernel rules like tx lock height etc pub fn check_kernel_rules(&self) -> Result<(), BlockValidationError> { - for kernel in &self.body.kernels { + for kernel in self.body.kernels() { if kernel.lock_height > self.header.height { + debug!(target: LOG_TARGET, "Kernel lock height was not reached: {}", kernel); return Err(BlockValidationError::InvalidKernel); } } Ok(()) } - /// This function will check all new utxo to ensure that feature flags where set - pub fn check_utxo_rules(&self, current_rules: &ConsensusRules) -> Result<(), BlockValidationError> { + /// Run through the outputs of the block and check that + /// 1. There is exactly ONE coinbase output + /// 1. The output's maturity is correctly set + /// NOTE this does not check the coinbase amount + pub fn check_coinbase_output(&self, consensus_constants: &ConsensusConstants) -> Result<(), BlockValidationError> { let mut coinbase_counter = 0; // there should be exactly 1 coinbase - for utxo in &self.body.outputs { + for utxo in self.body.outputs() { if utxo.features.flags.contains(OutputFlags::COINBASE_OUTPUT) { coinbase_counter += 1; - if utxo.features.maturity < (self.header.height + current_rules.coinbase_lock_height()) { + if utxo.features.maturity < (self.header.height + consensus_constants.coinbase_lock_height()) { + debug!(target: LOG_TARGET, "Coinbase found with maturity set to low"); return Err(BlockValidationError::InvalidCoinbase); } } } if coinbase_counter != 1 { + debug!(target: LOG_TARGET, "More then one coinbase found in block"); return Err(BlockValidationError::InvalidCoinbase); } Ok(()) @@ -114,8 +113,12 @@ impl Block { /// This function will check all stxo to ensure that feature flags where followed pub fn check_stxo_rules(&self) -> Result<(), BlockValidationError> { - for input in &self.body.inputs { + for input in self.body.inputs() { if input.features.maturity > self.header.height { + debug!( + target: LOG_TARGET, + "Input found that has not yet matured to spending height: {}", input + ); return Err(BlockValidationError::InputMaturity); } } @@ -131,22 +134,34 @@ impl Block { Vec, Vec, ) { - (self.header, self.body.inputs, self.body.outputs, self.body.kernels) + let (i, o, k) = self.body.dissolve(); + (self.header, i, o, k) } } +impl Display for Block { + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { + fmt.write_str("----------------- Block -----------------\n")?; + fmt.write_str("--- Header ---\n")?; + fmt.write_str(&format!("{}\n", self.header))?; + fmt.write_str("--- Body ---\n")?; + fmt.write_str(&format!("{}\n", self.body)) + } +} + +#[derive(Default)] pub struct BlockBuilder { - pub header: BlockHeader, - pub inputs: Vec, - pub outputs: Vec, - pub kernels: Vec, - pub total_fee: MicroTari, + header: BlockHeader, + inputs: Vec, + outputs: Vec, + kernels: Vec, + total_fee: MicroTari, } impl BlockBuilder { - pub fn new() -> BlockBuilder { + pub fn new(consensus_constants: &ConsensusConstants) -> BlockBuilder { BlockBuilder { - header: BlockHeader::new(ConsensusRules::current().blockchain_version()), + header: BlockHeader::new(consensus_constants.blockchain_version()), inputs: Vec::new(), outputs: Vec::new(), kernels: Vec::new(), @@ -185,9 +200,10 @@ impl BlockBuilder { pub fn with_transactions(mut self, txs: Vec) -> Self { let iter = txs.into_iter(); for tx in iter { - self = self.add_inputs(tx.body.inputs); - self = self.add_outputs(tx.body.outputs); - self = self.add_kernels(tx.body.kernels); + let (inputs, outputs, kernels) = tx.body.dissolve(); + self = self.add_inputs(inputs); + self = self.add_outputs(outputs); + self = self.add_kernels(kernels); self.header.total_kernel_offset = self.header.total_kernel_offset + tx.offset; } self @@ -195,9 +211,10 @@ impl BlockBuilder { /// This functions add the provided transactions to the block pub fn add_transaction(mut self, tx: Transaction) -> Self { - self = self.add_inputs(tx.body.inputs); - self = self.add_outputs(tx.body.outputs); - self = self.add_kernels(tx.body.kernels); + let (inputs, outputs, kernels) = tx.body.dissolve(); + self = self.add_inputs(inputs); + self = self.add_outputs(outputs); + self = self.add_kernels(kernels); self.header.total_kernel_offset = &self.header.total_kernel_offset + &tx.offset; self } @@ -209,34 +226,29 @@ impl BlockBuilder { self } + /// Add the provided ProofOfWork metadata to the block + pub fn with_pow(mut self, pow: ProofOfWork) -> Self { + self.header.pow = pow; + self + } + /// This will finish construction of the block and create the block pub fn build(self) -> Block { let mut block = Block { header: self.header, body: AggregateBody::new(self.inputs, self.outputs, self.kernels), }; + block.body.do_cut_through(); block.body.sort(); block } - - /// Add the provided ProofOfWork to the block - pub fn with_pow(self, _pow: TariProofOfWork) -> Self { - // TODO - self - } -} - -/// This struct holds the result of calculating the sum of the kernels in a Transaction -/// and returns the summed commitments and the total fees -pub struct KernelSum { - pub sum: Commitment, - pub fees: MicroTari, } impl Hashable for Block { /// The block hash is just the header hash, since the inputs, outputs and range proofs are captured by their /// respective MMR roots in the header itself. fn hash(&self) -> Vec { + // Note. If this changes, there will be a bug in chain_database::add_block_modifying_header self.header.hash() } } diff --git a/base_layer/core/src/blocks/blockheader.rs b/base_layer/core/src/blocks/blockheader.rs index 499f859ee9..ac8e539d06 100644 --- a/base_layer/core/src/blocks/blockheader.rs +++ b/base_layer/core/src/blocks/blockheader.rs @@ -38,10 +38,12 @@ //! This hash is called the UTXO merkle root, and is used as the output_mr use crate::{ - proof_of_work::Difficulty, - types::{BlindingFactor, HashDigest, TariProofOfWork}, + blocks::NewBlockHeaderTemplate, + proof_of_work::{Difficulty, PowError, ProofOfWork}, + transactions::types::{BlindingFactor, HashDigest}, }; -use chrono::{DateTime, NaiveDate, Utc}; +use chrono::{DateTime, Utc}; +use derive_error::Error; use digest::Digest; use serde::{ de::{self, Visitor}, @@ -50,14 +52,35 @@ use serde::{ Serialize, Serializer, }; -use std::fmt; -use tari_utilities::{ByteArray, Hashable}; +use std::{ + fmt, + fmt::{Display, Error, Formatter}, +}; +use tari_crypto::tari_utilities::{epoch_time::EpochTime, hex::Hex, ByteArray, Hashable}; pub type BlockHash = Vec; +#[derive(Clone, Debug, PartialEq, Error)] +pub enum BlockHeaderValidationError { + // The Genesis block header is incorrectly chained + ChainedGenesisBlockHeader, + // Incorrect Genesis block header, + IncorrectGenesisBlockHeader, + // Header does not form a valid chain + InvalidChaining, + // Invalid timestamp received on the header + InvalidTimestamp, + // Invalid timestamp future time limit received on the header + InvalidTimestampFutureTimeLimit, + // Invalid Proof of work for the header + ProofOfWorkError(PowError), + // Mismatched MMR roots + MismatchedMmrRoots, +} + /// The BlockHeader contains all the metadata for the block, including proof of work, a link to the previous block /// and the transaction kernels. -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug, Default)] pub struct BlockHeader { /// Version of the block pub version: u16, @@ -67,7 +90,7 @@ pub struct BlockHeader { #[serde(with = "hash_serializer")] pub prev_hash: BlockHash, /// Timestamp at which the block was built. - pub timestamp: DateTime, + pub timestamp: EpochTime, /// This is the UTXO merkle root of the outputs /// This is calculated as Hash (txo MMR root || roaring bitmap hash of UTXO indices) #[serde(with = "hash_serializer")] @@ -78,31 +101,80 @@ pub struct BlockHeader { /// This is the MMR root of the kernels #[serde(with = "hash_serializer")] pub kernel_mr: BlockHash, - /// Total accumulated sum of kernel offsets since genesis block. We can derive the kernel offset sum for *this* - /// block from the total kernel offset of the previous block header. + /// Sum of kernel offsets for all kernels in this block. pub total_kernel_offset: BlindingFactor, - /// Total accumulated difficulty since genesis block - pub total_difficulty: Difficulty, /// Nonce increment used to mine this block. pub nonce: u64, /// Proof of work summary - pub pow: TariProofOfWork, + pub pow: ProofOfWork, } impl BlockHeader { + /// Create a new, default header with the given version. pub fn new(blockchain_version: u16) -> BlockHeader { BlockHeader { version: blockchain_version, height: 0, prev_hash: vec![0; 32], - timestamp: DateTime::::from_utc(NaiveDate::from_ymd(2000, 1, 1).and_hms(1, 1, 1), Utc), + timestamp: EpochTime::now(), + output_mr: vec![0; 32], + range_proof_mr: vec![0; 32], + kernel_mr: vec![0; 32], + total_kernel_offset: BlindingFactor::default(), + nonce: 0, + pow: ProofOfWork::default(), + } + } + + /// Create a new block header using relevant data from the previous block. The height is incremented by one, the + /// previous block hash is set, and the timestamp is set to the current time and the proof of work is partially + /// initialized, although the `accumulated_difficulty_` stats are updated using the previous block's proof + /// of work information. + pub fn from_previous(prev: &BlockHeader) -> BlockHeader { + let prev_hash = prev.hash(); + let mut pow = ProofOfWork::default(); + pow.add_difficulty(&prev.pow, prev.achieved_difficulty()); + BlockHeader { + version: prev.version, + height: prev.height + 1, + prev_hash, + timestamp: EpochTime::now(), output_mr: vec![0; 32], range_proof_mr: vec![0; 32], kernel_mr: vec![0; 32], total_kernel_offset: BlindingFactor::default(), - total_difficulty: Difficulty::default(), nonce: 0, - pow: TariProofOfWork::default(), + pow, + } + } + + /// Calculates and returns the achieved difficulty for this header and associated proof of work. + pub fn achieved_difficulty(&self) -> Difficulty { + ProofOfWork::achieved_difficulty(self) + } + + /// Calculates the total accumulated difficulty for the blockchain from the genesis block up until (and including) + /// this block. + pub fn total_accumulated_difficulty_inclusive(&self) -> Difficulty { + let mut prev_pow = self.pow.clone(); + prev_pow.add_difficulty(&self.pow, self.achieved_difficulty()); + prev_pow.total_accumulated_difficulty() + } +} + +impl From for BlockHeader { + fn from(header_template: NewBlockHeaderTemplate) -> Self { + Self { + version: header_template.version, + height: header_template.height, + prev_hash: header_template.prev_hash, + timestamp: EpochTime::now(), + output_mr: vec![0; 32], + range_proof_mr: vec![0; 32], + kernel_mr: vec![0; 32], + total_kernel_offset: header_template.total_kernel_offset, + nonce: 0, + pow: header_template.pow, } } } @@ -113,12 +185,13 @@ impl Hashable for BlockHeader { .chain(self.version.to_le_bytes()) .chain(self.height.to_le_bytes()) .chain(self.prev_hash.as_bytes()) - .chain(self.timestamp.timestamp().to_le_bytes()) + .chain(self.timestamp.as_u64().to_le_bytes()) .chain(self.output_mr.as_bytes()) .chain(self.range_proof_mr.as_bytes()) .chain(self.kernel_mr.as_bytes()) .chain(self.total_kernel_offset.as_bytes()) - .chain(self.pow.as_bytes()) + .chain(self.nonce.to_le_bytes()) + .chain(self.pow.to_bytes()) .result() .to_vec() } @@ -132,9 +205,36 @@ impl PartialEq for BlockHeader { impl Eq for BlockHeader {} -mod hash_serializer { +impl Display for BlockHeader { + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), Error> { + let datetime: DateTime = self.timestamp.into(); + let msg = format!( + "Version: {}\nBlock height: {}\nPrevious block hash: {}\nTimestamp: {}\n", + self.version, + self.height, + self.prev_hash.to_hex(), + datetime.to_rfc2822() + ); + fmt.write_str(&msg)?; + let msg = format!( + "Merkle roots:\nOutputs: {}\nRange proofs: {}\nKernels: {}\n", + self.output_mr.to_hex(), + self.range_proof_mr.to_hex(), + self.kernel_mr.to_hex() + ); + fmt.write_str(&msg)?; + fmt.write_str(&format!( + "Total offset: {}\nNonce: {}\nProof of work: {}", + self.total_kernel_offset.to_hex(), + self.nonce, + self.pow + )) + } +} + +pub(crate) mod hash_serializer { use super::*; - use tari_utilities::hex::Hex; + use tari_crypto::tari_utilities::hex::Hex; pub fn serialize(bytes: &BlockHash, serializer: S) -> Result where S: Serializer { @@ -170,3 +270,33 @@ mod hash_serializer { } } } + +#[cfg(test)] +mod test { + use crate::blocks::BlockHeader; + use tari_crypto::tari_utilities::Hashable; + + #[test] + fn from_previous() { + let mut h1 = crate::proof_of_work::blake_test::get_header(); + h1.nonce = 7600; // Achieved difficulty is 18,138; + assert_eq!(h1.height, 0, "Default block height"); + let hash1 = h1.hash(); + let diff1 = h1.achieved_difficulty(); + assert_eq!(diff1, 18138.into()); + let h2 = BlockHeader::from_previous(&h1); + assert_eq!(h2.height, h1.height + 1, "Incrementing block height"); + assert!(h2.timestamp > h1.timestamp, "Timestamp"); + assert_eq!(h2.prev_hash, hash1, "Previous hash"); + // default pow is blake, so monero diff should stay the same + assert_eq!( + h2.pow.accumulated_monero_difficulty, h1.pow.accumulated_monero_difficulty, + "Monero difficulty" + ); + assert_eq!( + h2.pow.accumulated_blake_difficulty, + h1.pow.accumulated_blake_difficulty + diff1, + "Blake difficulty" + ); + } +} diff --git a/base_layer/core/src/blocks/faucets/alphanet_faucet.json b/base_layer/core/src/blocks/faucets/alphanet_faucet.json new file mode 100644 index 0000000000..f3a875603c --- /dev/null +++ b/base_layer/core/src/blocks/faucets/alphanet_faucet.json @@ -0,0 +1,4000 @@ +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2af79a44d9e077139e2214f5c12949774575526860dd4abe861d7dff30fb2133","proof":"3e55cf1d4feb3fd4722ed0ba0603f4d9c94a8c1a7f0aed3932b27de4d57d090362a8e8cc51ab8b22dbceda17030442297147eec2f2a72f87316010211adc641d744c9ecbcf6cbaa008d989324c3e3dbdba507f4e878562484e4e802c492bed62c6c37fcc4614158bcfa688639f046ab419edfe9d9c09cbc34f3f62c561ff60324f7dcec4e194bdad756d8608fe842aa6fb70480ab35405cbb852174921c41c0d7ff61f4a858a7312fa4547ec9ad8bbac88bd79a86df26a99849915630ba19006b9d2c074da86cfe65f97ae53587149e9bec2c31a9afe1ed0a685938df9894901a8f6914402e4080f62865732c8b3ef05f69a4738a57a2cab151b1db25eead12a5cc033ea23f4f89ebf2505b4d27df2f7280d02f617752e171bc5c62abc828a06649ef2d99b9cf35543c24d5cdc698456f2a478d8777affb0a446590dc477831a468205fcd192f28f888b070e4ae25455cde7c79032a06bdc21d5f9b2e4d5d8755073c02376a77c20b9cd594a69ff85e566cfed8e52348cad9fc1043e96e3c55710869455e1955c187ba6386a8129ed432e4b5d77e532a61e3bbff555bab3790ff68028ad9e05f0c020380a613b13319cc5b6e0fa426752f6c1c054d8f0790c62448075aa8ebeda4cd3d658d3762d9bb44dd89f921baf18c3d4fab496040a965142be811fb650e48559081b3efe2a0d3f31b72a17741c84a0f5163787a6b3d60108e0dd3988161d40b240f182ce90ab552a1e9170cf76ce8a458bd48552f97b0aded0d17ecf3d4a6fce3020e7b177b39d9258731a827927aeb9138be74d00e01f00307aec934964600a13116e9731dedfb00cac4cc118df70bb58cc3bab5f396ee7b0ff4a8e1f62a1ffccf3723cf4d89a30914548f274962bbbbb68eaa5d4c30fa1425e0b61065b0fe432397d40462b03f8b073f91b94793774c01106eafa4e08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0e711de638d6377e3b64c5068539645a16b4988d0f09ea7190203df80311e03d","proof":"6ada5bceb2d99907e3f875a3178ee76c3dbc14e603b210abc34fd0ff6db2d93b224fae0ebd4ab5f03faefe80bfba3e44831222cdc935f64f1806b75699399739e68a8f689379b28079988f543bb8e7b828820c3ff544852a8d5d77a177f40838ea3f7625f32f8199b4aab9b8a3b16aaa73c5b52b02bdcca137b889c1caf8c66a6ae125fc8aa3d72be47d278b3fd76938150d40dd8eadc8e0e6125ebd9126c600a353f4e3f944aaa5e9a8b0d9dc5212c80b47c9cb85b2baaff79a58cd527f0f05d6e96d022fc63e79c794aae623145537a35b447c229c2836046c8927d8ceca043ab2bf3cc4abf0defff1b352ac7e62d0590e2461db1f8b07cc6010ae16ab041724f612ee6aa6c4ef7930c27d265dca0033738493eff9caab38ee1dd6318e512fdecb3a676787405ecc2e0d5fda9ca34219ae96ef8d40c2cebad2d8372b9f245e9611340dae6064600b3b9a02b91ff8970e018005dee464d751252f76a74100320426b20b069336f746474fbb61ca05932aac20f46922c981da6c1b2e1ac840416cae208fe45247e626bbc4101c22d4e6b15c68f9a1863d053edaa7c8cfb0ca5478c9672b25b722c4bf6f55ac54994e80c1f20aa3fce27c2690277228ace4b65862fc12facedd95c090cd0d94254767478f23a2accbdc7fae31d0eeb49166863d70dc25c8cdab9d8b7cfbc1763f96ca7e4cede9de711e9f8fcbb9a14408d1a967628c5751b30b61659873ac7182e68d7d2b5ec3cb78158dcc76b50c1c1b4437777ac7efc34832760d5a26438a461f65e9eb2bf73809363f05cafb505043f0767cc42dec8ef039ff74c0b2b7836d6760a09c32ae28db2aecd8e723b233ab2d080a26fb9eb6936beace87137689da8c6abd02668cf3ee4baa7b0796ebaed773e90d0723afa7bf140d8766070bcfa82b72920dd4650919a135a8d6a0384cfb89b001"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"146240b040afe6a267fa70728f7a1696d735edf9743499ee7515bca940dacd55","proof":"7025e265c4663a22e93a0c049eb85c5ae8d21cfb0277ad3e09e1a072e30e5100109de78bc96cb83c9ea3889f1ad708695c4e5c3e90e5466c4e667365ec422237d23fd4f6074b66b49e035508e69ecbe78c6fab5e1122890d40f2a418d9146203e216c06bfd95b73fd94a31d22a49c79cc2200b6dd0c93b5c6b18fffe9ae126580b84d2a261b331bddf9261786ed57e3285cbd90ad60f31409862588d2084b30ddb345f7a2618fab72393f79cf89a89de4713cc66da8918965aefa71dcd3a7c02c107ab3d88262da5e62152971d808bca935a993031fab037ed77275e61da7004c4eb4d0035bea8353325a63663db7a2c460d129b071e1122cb62e7eaa5474718182c5eb454efb975196f8584162bf4d777d411f82630965fb454bd56be083172bc47d649493e550d2c83e459fdf250412677ad9baa9c08dfbcae673cba26dc40b0a69d407cc517f4becf2008d23633051429f44a62a3c0305853ff4f8b973760bcbd5cf0fc22c81c501b4d94d03323b5df32a75b2eb91b3e893f79c4b2c0fb631cd63df2ef3ff7ac5524035e6377b0154bade0ee294311f4e2957361f0539639d6da52e3ba888b76435c230cf27dc3169d5c0efff439505561af916c47ae853070ffea183bee483282156387736c43f7ca7d7105b1fb84d32b3b91d147a621195877d6c60601e5aaf00f197c9ae5ef029416967b8515dbee3891ca12c74f3c4fde8fe1d988b1875d2bdc5141a53a3323a5d0063dcb2372a7c633c698fdce46522ca834f1d21bebbb5a435571c797367fd176023b8f4575d49be9dd505422f45d26a1bb8f1916e06d3f2829ea5b0960098e4c7736903454595f3967b95ab7c920a082016b701085a79f6edd705639898681db341a066452fdfe35c1a2e0c2cd0c2bd42846d75ac0129a6b9b914b94a4fb632b421123ee5d4d8fb52b702401cb04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1896c85db1e5bb201d13a14ae4a7f6435fe968d3ced78b0d9dee801ad32ca31b","proof":"eea704f2a82b6d788244f699646e6c3f6052cc15f04aff31622a5c9119bf9b063e9be7463337f5d709256565de336881dd6f92df531263bb0929f93cfc932e0276a1365eaadb3e420850fe2264939a049dcf17cfdcb442be2965a41462df084924a3b9865e2e6cb37df3341ca439b70c4f5e0b53cd86db2209de9981ade2fd7defe97e90f77cf89d77f945f9c5ec4ee96f85e57fb574e9ea9d12fdcb741b80000734199a0c01eeba3eade25205c8c8cdb88de086f4e5e92a87939d06fa2c5205a912f72cae345faef0749163954269f2a4586d2195ec1ceb7c3bbe9313a4d708ea653f5ee8d4c341fb9b78be85539708447b0ae65b7f54d3219684ba06150746e2a4abd423e3efea1505c8684fb755457e2b33ba101dc8c87bca7ae25c50a31cd8a14ae3945c8a04aa3ed1a72117689ca3c7c7dff9e0e3034cdbaec90d11814ac2383a45dab8245b50f59e7a0e193a50350d59b7c06ba41b349c7eebf57cf15f46bc8598fe3b0b8853305f378974865bb6a7b56287407b5a26af68f1756313276a55d06c0769cd3f251f1a511201c670cdd5418b42fc12c6f6fd94b6b632b749ca417a3c271924222cbfaf0a1e9558e1241707bf169262600201a721a273da3796943f68dc7072712e02029ca9703ba33798f2a6850229d40ed82704c3307f3352422ed8e1058abe1b32fa55d2724feceab718ef401451070a5fe3a3c49f1234a2dd4fa665d9d56c7f1cafe66e58af66709836ccb15e42b67d00f156c0bcde0be05f440fcd0a514b62e3fd2e48d3aca00a67a09d90a75f3d633fa58c0831741a80aba7ea0076c5ccc00c4ab4a7b7ab75abd6371d8ef714906c3449b121de015e3e3ac996bd774f3a67718064c0795a40d292bce71ef12ba9f65fbf5220b64707352f3e230bda0924ceb7a4aacba9d212895bd17775906bbfa9861efc6b9edc00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8ed62819e8881457c9cddadb932ec84cc551d41fb44d1d4f1e1d38550e96a50d","proof":"88dcebf413ee10c50884ed61cbd76e551a4d12df585a1305645d34293102c7447ce07feab7ebb626bf776ec9adf21bd408eb1377ff04957a9e9cf04f8113a93974164a2d668adc5bbad2a812dae9a1455b6556ee9c5303158c2d40669856d64ed40100b1f2bfd3d3e18d12e272f77b3cd0bd4d53f78874dc243e7f3063015f58ca5b241ef95ac606718649fa7b5c4e5cbb92163c39f9a5799f21af1062159e036ef2453aa286d90ff5dbde807c0a19df207015e5e59c6e04ab139393f7725d08998252c4ce17839d5a6fd375570193bd5add774bc708be1fdc8c17a5df25d40e5851a0c6bc47b0d61f30eb00bbc4743219ddb56b4df6bf9b8df421b13eb0875e3c376099d170ee1abc415241b889a69e1e946c5a7e3f1cf711fac4c20082cf25c4c58a57a86d6086204113ac602a708b6adb6c4a876f2cb290642e26819d5a08da550da548258b8c6b2eb3897be67371e233e72d716df19c02e3f519f2e3c70f221e8341db9ca7f5517f0ec0844d55b2175cbcf3b4be3c80b97883f94ef0ee2d405079542f0e97dc59e7bc3c3771ea024f1f2b888f8eb2d859def29da15af659d29aa81e5efca7622806b4e0f8edcb9241221d0722063327402dc13b3aa7f622264bf852ea0c82fec774342df768bd8f79577f792fdc8f744764b8a1c221751d3c5d73e2aef4aba06d819a7207dbfa95e955f11acc86e195a9ff96a0ab5f7c04ec929da9533107ecafe594e294efab27bb90fbeef2a2fda21c06800cbfefaa79c09ae8779dccde536a077f7f2192c193a02e883e10ee5d2ba43d35bb7ce1837d48052abfbc6a5ebfb0b479a5219e948818741ce651c222584a84b546fbf5f71f99d35b309fb634e60ce149b437189329486a18a993a38149c356f80219d9830d005b9102f552fb373262816307eb427b00538bcaeb703f794bad836f08609807"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ea81b7025e9a901bbbbd919c42a57ecee407473f197f46f94b9d65a5c212180f","proof":"0667a9bca99f618f0bb3fe13b9b0387ce171507f1644d86bb4835ba41f61bf0d50f3a0eea6cc9af93eea35353cc46a7fbe6463dfaab415e784c1a12199d189760c1994df758a2241115ef61d723f8fcb7eb5d9a9b13ea39addde82ed3cb3ad15f264d6ab3271c05b4cc96f1f872e600e074f095162f7796c3dd04143dd391979e53cecc1f570810bb03d929603a019622354d45b51142663128761389a37550ecaf58d58c21d0c32a899e383f2acd0b66f711dce3b9ed6e83ed758f02fec04008fb4e040eb314e9f22fd3635f4d62869a302e15fafbfd2ecbb25e898cf69f10c3826b69b90247e54c16717a16a55b5d2255777a71ca981c209dfd6b2f9e2d557cc217eaf8fb3c91a0b706a9a97e3775275c40cced7b2e16b0592a9862290df70c8f59e59cb533d3dd1110f55c143cca668ef42f9b055c1a1bf6fa04f131f6c30cca56d5cd935245c0cafb59cd9bddf8df888dfff57680f646f2ca41819c3756f6e2996658d0a4b21a46b0894f5aa000c2f1c884218f9df762ada4d39f57bfe033ab971ea0651e82ae6754581f3821b89dbbfad2169a2ef168cd62ff401bed276d455fabc126c6c3c4abe9661d4a6163353e0f9ed208f4822bf892191ae87c710ac21e0c660375dce78f9d4e81ea5702d33f8e8b32fcb4772ad9722ff80a66037d2df4397ec64de9270d0ab8e6bd4ad8da08e09d571005c7404dcd8938b9a927e822b842e250f7d303679a4da016c635f33b842ebc0af73b617f4538fad4e99400e654b2551c8570b71f13eac6cfd345724f50f9699a8920c0660afc651d5e8690047ff07661b3226f965958388ba3bcd6b96d8a4e942ba8aaaffbf6d9cc4a9010fc42bbd941b3eb63d3dfc1ddcff546b510937b0333a5ce1dc0994a5d1457c0d2df396d45771c45e6f852163ae6a15725c1c6c6925d6a51b9a7fe43995be8d07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"be0555a690e7a3c926f84fa62133e269e7537a2bb2225ca8f5fca77d73fa3b0b","proof":"be5d26db5734e4902231e99fc312ac519b17c7ed06cc180e4d991d8a075b344adc102b888e95d67d8bdb05b5b08bf67080ccf409211d184a736ad222cac78760d822882eb2c2fc791b25791f868f69670457337526ef0454147580465f3f607ffa690ae86d569c557f681c35d30e30562fd640d67f46eafc2aa9539c9eeb2d0ddc55fcdf768c3060a17840d1a64b12a18b993bc208b4b6ca70bed1af2fac070a9fdebf8e717a92fc7ec7860371fa0bccfd13dd9208672b050285e25e17a28500494bc53695dffc9e4ad5292728d0ec9766015dd00c371355dc295241ac87820f2e917f5c58999156b3522c6e2b874b19709c1620b9e702bd9ec0ce3609887b33b80ff9332de5f5f585e2967faaf2049ab9981a8c14fa469d1b5739afb9a9016768425ddfc57b0897e674201faf293238a256a1e13fa3fac6a051a0315e2e6456bcd4f7d319baca238141678534a0da2676b61765064f311d58284a16520c2f6368aa3719fc5df5512bc0aa37ced5f33597c8cc9e84a6382655b49fc35c674522dc0fad3670809f826de7086eba6f14ee7d2163792edaaada5f17b2f6b498836c6cfb8813be1b21c874e51fb546384db5eee2465e42efb3c4a2c097dd5e1b4f32de6064bacf03d907363593ef14cd5831a02d2b060cab3bb96cc3e848f38ea231ccdab639d354c3815fd4f663e98c855f4cc09fe450d09f98402ef718e1a6b45e961d06e9122c2891c52e2b9e5c87a90afc8e537a9520989f17a0a5245115045f185003c87a5378ed634eb727070f78e14caba39f8719ee9118de33bf15924a5a9a804a2a8848895da7e8849df8b7d6448a0e30350b96163f18677b3f80d76e1ba9eee6e82a72936f50397670703f0da566e674ae91e391a6d3c44f1cf9c8e40c37767cc1aa28a2c1c8314a4b4201ff1511814cd5c3adc9020f8483247a81f904"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a27272e740b104dc6b07f5d06d1618f5ac99c08b64f5939c24fd795437931a5b","proof":"b4082e61be5732b7b73872775c60e22dc854dc538e5450d272c3bbc4e051f43ccc162fe051c8abb1b97fef3bdbe66ab3b61d83321b9c0b3e00d1516dc1a65e2b36edf44fd9010e2a32dbcdd11f5bf1e1e3b49c386b3de6731bb5d3d153541f374a399b23166219351f7ebddaa44e8c666df4e7d7ef8da59705909cfc9f71bf2413eb63bd12907072a2587971792c7f1b252970548b45c34a31db4cbb2c31260a83b172e3ff2a86cf0bdc4cffb2518572713aa97f9032df50c3321793fabd1203e50c3054c9982b127d787e3f3f0b181ecab4cfb4616cc6fc447071c5601af9015e49f97d57b5d0cc96afb5eda2d345be4814f499e83b46597298556902c3e25e509bcf258296b26ccb8792f225d26b14317db4dd71068971324f6d9c7b2dae504ed44c0d8316e1b38714ff7b8c120edcb24f47c8a09a688adb07a8ca14d8fa7a700752cbbc70dab9d37afe41a1b9519057951191f8c7f8840c5a1c21da1340531268f44849ef67e8e04b7c8310fb1b895c5c267ac00a8cee367487ad63239f67ba3ceaf52d69eb9dd82f9a45a7a459185fe0a6fad01d0c23ad401c329f5f5a119609418ede85f5ed376b89a54187a22c5c190e2f99cd7387926bf6add1f10a1a4a5a3b36818b1c637c18e7fa4bc8df9223321a4344c1a34a7e3e82943ea6633c48c987d741a0425af0ac29b9626c4024ba3bdbdd6d50278544307e5a7a665761d88b290d9c7b26459109ea6c6882f1922bac5963e8229f68f1eefb6f04f2967d847c863e4b79fb052a87fdf501f484a31f3536c4d2b099d7a6cca6b8375a081f8ab726597d79f8096fe9fdbeb6549ad92d5c9e244e85816c4f1866fb010861640cb16edfd87ec7f86235a2a663c879d68797fcfe8e08f3edc374d54315fbcd0389e7df2757aacb515833abbbb06333a46f341ee9e38a77e058abbba9791c9d02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9ae4a4fcc98023bf6bb16be1faec91d0505031807e3aeaa54ebcab93acfac611","proof":"d4a5f49592c98e5a6a89b2a09e2b9ebd126c3cf0cc0eeede22853ef968d31e55bcc5aca7d0d29538c92e79220136a71922c6bba6d507dc2a11c437d769617e4460e80d8186a8d89f5e1ebdb3f1c30ffdda3d50c915ec4ef21b302d7c6fce3f0ce460d0a1f8b63c60051c207a78184560b21058ba9fa677365666005d80de3410fc1450cfdf98c251d177bb5b3bf775459adfaf223bba387384368aa64ee7da02cf5e3608840b1d599c18297ced10d37c1c82dd20ed719c6c038f2731e948780694de3397bcfe4484e1fc196ba28ee8da25926d8c83044c13611468d8402fe80c622942db32309528ddd05b9e18f7a797d59160fceb07e1ccdc52a4acda2fd86ec67f74e8bdaf608831e2b934f68804f8c51fba548bf71d9d494d067964723a6d5a58412abc4eb63d2048bb322d2a3f4c171c113611d7e5df8137240db0748b658ca42eac3f430a30fbe07c6924edfc2095d95ccbb84aec18d7f5a2845ac57a1d3c63f51bf670074f5f5c034780a15cc97c51531ce483613e49b387468c802204ec6518ca0abec8ec8509a1803c18a943aed92bbb4f9679c1eddad5c7a2213b2b506695e738172e276b5f366eb7fb9dc1355f949d2c4c1bceaa20ae763b143441d4425c810e634b6c4f43707732c9cdbbc2cdd0eabbcc01a99689e944c2ccc81614781bc4db96dcb2d98e0b5b68bc1291109cea60ad4a461668d486074c5f613da2dfb6bdca533dcc1e5d8ff9afc719ef7bd8ac1a77d03c685917e10d5b46e725bed8084e4b5fd06f17dcb8aec8155af471df6a120503b2a4f79472217ca0dd37064d64235afd67df7f843e5a226de1bae2c7ad5d8e6f8cfebc2d511bf1e18518ab0aa9b47d9c3354f1e7856ce2605fe6fe094b316ad0b0773159d158152f8c0819a647bb65e6f8511a491a34eca4787fb0d454f8da1fef2e3b83fb828350e40e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2863c2bc84910002dc0b5b9a907ddd1cf1bd978c482008470977cdd47ba2781e","proof":"805d90d797a5f234523cac49870ac9996f5ac97802c2f2c9982a1b519ac16702b216f3aab5903e89ba6b4daacc01b90cd61f8b507c9723092847eba33cc79261940ff201002c3774759b7a1b66222160b4ddc9d718777ff746761e1b1b0bde719a488ffece669c063b2524b2afc35128237f61b9fbc2f45c5cbf9e1858d2542a311e07eaa88accdc14228c7c640b99d0989ce5500a2805ee2f7e120073683804c69b99701e3ef3feeca8320a626421d1a498b9a42eaf10ecd3e2c6402eedcd07455998b39cadd15174b380574d7ed6c6348174e9a5c51c82b417c75866d7940f14bbe136891b11cd88e4849aca0abacaa20a5d0824f60e389cf96351b168a2757417c3c8252040f2336fda0e097b7bd92f09cbef3c88f09414a5f766a13ceb0ad619134e902d6e98ad36a35d845fcda966f2d29b972e1116c09a705e61d4274f7c4d5b104e8835440c2c968489120df403478afb68d8d2160f62c8ca3b2067025ad3d21e5f006b3998000867f0cd233deb1631ff04c42c1904ab9e79f98e7a6daa695ff91e2dea8ff17f290df4a8a51f18a0a5c25e5b8bdef00638bbbc07df3cd490766cd98403811fac49b8c664a82369bd24cda467489a8d6bccd0fb80fa0f169158704bef1c74591de0639d87e6c5e09db0a354007a7ff464f2563987e3427c6704c8e6d80fd9d1829c02ea5eea5143f7157ddc227cb6a40aa1262e04c13fa62c5ee8efd93eb66d9a7c0c6a9a8f3fb59b0ded8523d4300814fc769c02023812b5c7f0b14074867f422da83865703dda8c551b299ac11ffbbc4c12ce0f1d59f68c6fce293ff7a289e794000ad3f334941a9bc894e5d54bcb531857389d842a45bb3d847d1af0f2f96b0e413023e0114f08440ac0ea8dd0846ef464206ef807a5eb53b95b569b173334c516bfb77544f013f8186b03210da04b4f17a8fb910c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cc38adb0b0082e1a699e525255d27c52a519adf5a87a6d90857d8b6af069f203","proof":"162561efc01e6d32dff038fc992c63d5de7bceaf88cd60298311f4cd4a1944707a2c963db3c0e919a10ca1b9d06e28caedb8230a0c41f9cdfd524bbacba2642cd0553a6b7e70fd4aa10025e87493d11e33ef629b0887b34aeaf419956067ed226250f7f6e3ccafd816f4bb3e70ee01e892654724ce04f1146bc425b091db586c077d9c46a0ff59372d2a3362a4af8d2f856ff1fe754d0dcc30a24378d745ea024e4bc487a4afaf5315342efcbdb0f88bf4c8811c1a839506804ed59fa8db86045e155b117ef4c9bd8d6f3b086a29b799b649ebb31cb216fd8ea8053eecf876003af21b9804a4e80c0903cfedebdcfd9191951187eebd573866ee62fbddd20201ec2f8c3cc9bc119c032fcae1d66670016b59d252afa475df7b9092072a23df445a278350beeece40ee5b4ebfa1772c92c764149bf4aef59f23bc00a345fe0125489fdf8d2ae53b764a57ca679b317428787da1720e2c89a0e464caea9d13ec0b22721eeedf7816808dfd2b23e7a30883abdaf474857345c8d4334100d039987ec0e12ada7cdc727ba3f652339eb7816d0b3ad263ffdfe0e4342afdc47384583fb07673079e2ec27a31ad26c9197f536804847953dd0bb60ce046f3192f437b6e8499580d7af7464af788cf43032bae39fd558d15455a2d4c28e1b8d7f05deb55349e43033de8feba9835e799ef2a51682843c206813f306063e7753c15cfa9325851460ac15897666b0dca6b391889f351e97ec2c9ff55723227bb7740eec17d1e74a70fb9eedd764c1ef24e6bf585e9ddd8672fe2ded2e3357347f960ea8979c6754a90b1d0ed5c67b924eb5196adfe2c050d129f1981d5a38e0bf293d9d66d87e9cf9419fd9a2b66b120e5d4f6c1f1749d9bbde1a91d609ce356c9a4468e0ea69c57c821fddfb01913c31d19a8c3d19c12c8ed488da700c7096b23f5b93700"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"546f3ea613736aa12d398f176498b7d894bd05c2f6c5268818e8942fa21f7d4b","proof":"1c90fdca3f87ad4400227ceafb51686d5cb184d81fb4d8eef1588028a9fc6c3718c8b3ac238228c22b0801d0e2a18b0cc2c1e34eabdf511f3f21ced54580b469443233f38ecf8b633fea3db1cef503302c701df52f4f2194d5b8c388318d7d1506ee401086e69672f688a0238e4e09af675b7f46c2c007cf0b1808bc3e750a38893f10088f585987adf2a8e2588621eab1a493010c13380cb05e0b3a35a29b0d7e44968163ee9b2ad7695b52ce4cc46fbca8fa791c339171f29b695b89d5d60e80715b11a241ca3f381d6f1931be141a2b465ced4bf5d080d8c95e83773db70442c6faa83ad0bbc87dbda894d1a0499e514224efbab146cd90885f5054cd4a7728cf8bc377dac70f5b42b3ec5491fad3f09860d12aa37774e33217c9bd3aee1600a818d0c5f0d49c6270ecc31c24b37d28f753403b867a88de5a63ebab95a0125c7668a1b074f62ba2b3ba498c22957eee13b4e5c13b9f8223114d46d77e8e0f64447940aa04b3342cb617c5f0b5208a340b43f61647c56b3b22f183024af06f5a1d4e0b943888aa89ce59a8d56e5e2ba6ee25326f8845aacdcde85e4c50f8096819ed92cfbe5d5419f34248b4ab43b4caae739e5ee14942f66fdfcf3de2197a84540a6c80b6a088c9d1a81d789405aac2c78d44ad18940dac35bb6e9cfaec09766b4c06b7aac83696db77c5a500f148b714091a53d9686461b7a1b136dc0d64c8f704590aa4146491648e6e5f4aa8cf15b4c3a74a8439aa0ff32798e4d2fe51a4d6e3bdafa7c92473ff0c3a4c131ec11a477eab6cccea3dddb1a75d6db6af3bd2a01041278d665e745e0032240cdac4d88fef711a5153b36b8f72de6a92801b39c439d10987a906543eb9584788f1a1e4c0b3aa074e21a3944d8db21d79560d09bee2fcf266c22ff78c920d1bf83652fef0d34bc5d7e0efc6988fecd58ac50b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8e02d0c248891fec9eee39b67f7f07fc440ec4aec95a799dff98b7bed61f2d62","proof":"d455435138ee8b9e5fd549b2e300fc434b74bcaf1fe959cc70e9321776a81c388293de35f12bae4a5d1b61c07ab31aedb7a0de64090e51f627cdb2ecc51c9673380b3680857a5a0605bc28a098c2213821f85501c5126795c6aa8fa9e65fc86e4c11e7ccc277f2aa460bb8c8ccad349413c7ac31ea711b1b102529496bdb2c590cd02226955ffd996d1b6b32b24ef09c0573311e2890d51a6eb74d168037990413b87ea73c75adfa4b284c471c591e32e761e5920fa294151344cef33cdb460aa97ea9ee88d54682b20668c6c5f88eba0a9a6ee5e47ed22715edaeb9c363da08bc991b59946d9b0a7f7c448365ed0b6a5d4549eb57dfcc9eb6945436088b674d20450dc6b9cb76f80f788f23317e0f4f0ab99c0168831f2a2f0a0072d08c800c7cbf483e7df454a9d3916c297f6d671b3887ce0691033e294864759195fb93703863294d78a50cacee47b3696fb0fc0ef41721a8ddbb786fac42f9eac7e93227f0d3f891025d6dbba3b059c51066e9b38431f5429bdfdb0e384c74b1dce3cf0b5445cdf4a15f21db3953ddfc04e78304c4d4a1e27ec5340a939ba27430c3b53b523a9fd8cfce2bbfbc4a3e15618a5175c359722310622a170ad1d4281911ce35b01c298859d9230dd96ba8bbe57593bd6b44e36c873da75482cb87404a8eeb344e403279eba1d424147ce5a504bbb416a8900fa6ddc8c3f25f6b2d8d4ff9b430008df68d7d0d51a31af50ac401c6bf9ecbd3921cf561181027cd5940166b27381ebd185c63477f267b89302ff69f0de1b1259499f9985090ad29dd91af060e51a8255ea017c2bd991a5481ea8f2c43f94c887b98018305f64c938ff1590762416bed8b307a55c34b28b652711bae276e68d313f93002cf7225a99c9814b06b05a14a54888b495aaabf3ca6a04d3dac6356e2c5088011505d8a041bea53d07401"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1e95a7a5539ea32a5168c32e9cc4681db9fa1d19648040d2751cbf8fb7df9328","proof":"06e28182cc05738236a3c79039edd27d7f7582f4c4a183c73fd3af9e7e7f0670c298ef76fdfb3412c3af340a4ea1b676d63c768a1c2c8ab589efdb900429b1788483412bad5e46e07e0fdaf2cd4c4e1871dc5e5a2cfe510676491a6947cc231c0c62cdeb2d1b74e85c68714e92d26c4de49ff962d2f13c78ca54b11b7638652690c2fff9ec4284624b774719f5eb60d3623438d28a827400088f132b9fcaea00b52d6dfde669bc1c556985a010a27e4001977c17980208b3c93f1df433876108b645d68c1a8093d6f6cf8601df44acce257f3ca0645f8f6b32361695ca523c0c1e20a6ff96e8f29009794a4547463a39b5eebc9db36f3278534492737b32605eca1b92ed56d9ea5f154becc9c4260870b5e525a6bdd97aa4e330b6c7dd726c19ba54855f9f2eab96e72a5dc395505904957ff12d9c9b6f9d9759177d3097cc02d67a35ad33abb824c5423e5825d6dffce0058d984e76b2dae22cb6e2ac181d1a7c86c67da29456a0572a556f5bb5e6a878debc8847cb94fd52363f3fd2d33c759859443d7a588c647c1006021f6a6849387d3431263cb761618633af7cf73e30f63aba55ba1c55ee73f07e1fb612edb27f988818ffc4ca4a4d61a7dc7b475178c64102590d119e5092228950c8e485ea744239cf428f191e07ce563c7ff1334e506f4dbfbaf3070c55779a06817ee5815f77c0ee9716314b8a374444809e8e546247231d8d59eb4154aa7a4c4ee5e4c1160748f96f0dbd566960d97220c66e0efa2f24ca99b744fddec02b6234c4aa2df99dba9412f10873c96f70e1ad2c1728e43486bba62f9d2140d1f2c4340217ebe8bf679202d14d8df1a7a0578e9308593b463c4219553d3904923b7494ad9946406a4ca20df60b04ccf8b964392e67088bf9fe1aa275f69558b5a272bff6923d77bba7149ad1b412f38f1966b04d8102"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0a72e0f240a5205fe331488d8a68a0b9c163113b0048abe6072fdedafd689227","proof":"deefebf62687617d8d30b0b302e66b5b7335906e0504eeee0352b3db74f30f180a2397651978a8c1f7c37485b0ceaa4f61a9238b29c98b9fbc2fc1b81f35ea41f6b4d9e0d737fcbad79e02de106c307728daec2cd9f6a0a62ad4e2e3d329d67feaa9302fee2a7ad52e2e4807e3af49086eb2a6d36b773f5ac439812b92351b5ac5d20c402e8d82231bb832a896032a8fe42c25c532f2c21cb64b7a6486e9c10b68f4a3db79e5a4a86c8a1c1fa32f58fdacd6e4789ead7310c39c030850962f049dbf23bddf61ee734c99903d4688bb5b7c235a99dc48ea431a6de1df6538050fbc1adf2fc0b062bf75de92d60e56f50e4e0c20ef6eed5581e6c6a5f81da63c3afef2635b8c3746cd370c784fbcb7a53f8375b13c936106f763531dcbbdadc86bdc0147057fd51fe66af5904c5e064b86af1681f129e2809a6e8a24bb11401a470adca9f7e36512ef9f3f7ec10a00a3b0bb9d9da409d958552dd7142d35d52c599ef8f572b004993a43ba8d764ff08b2759657837ea7d37fe039705f36cc63b10c0acdf186ba8df2beb0916966c503298a5fbd9a60175f1bb3155c0dcbef49c54f43e34f6c1dc80f17281cf6ce2960871adf49ffbab02e059f0bee7ee21583c659ac644abd712433b8a089a0e95e8583ce179a2be01ce80137caa7794f777a310944fd9ee6e3c23d794e7e2abc19d33556a797efc9cf14bbdcff635d86dbf3f51ac5137a3107b20f0633789a2c72d35511e4d5dcfca821373896ceb6aef0e8834dc7fa0e047616e8d7e6bc9e370578f31a87d48d563b162c2140c1c23cafccb468213be669edff0f45581e061a3a3e646ac1339506b0183eae1f0fe4f9d10b8538d89d1afd4372fcfc10ed037f1ae5c92785b6b561b1da2117282699c5134b10aa7fe0973787c5713ec7afb11a186e5754e1b4aef651c8dd4968b34716d84c904"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6cac1473b0af28e3f0a3c68f3f3361ab0f16007619e8fd6d51ef79ed8cc15c67","proof":"6282c60e4cfa5f1244b07bb5f7e67a2161ecad572d352d78a71d746917fa1d167e06de2d2d4d1871ca3133b3b66884e4a9658ed93dce287f6edfa66a19f2d220dab729999eca166e77328b6cda6c433bf649ef9f9cac3f15bb7ca300f29884253268b2a06c5520850b0183f4a8bbfedddb5ba48b1a5af5d49319bd22ff1bcd3fe5833c7559c6c1594ce1519ab570c20f50f66b5b3311619bd2027e0f95c59c03c155efdef64ab7b87c213d1f3a079ca7b16e7aa3c47b7ebe7e330bebcf025b0829b09064de53c9bf8ebec02a462e95127fc2b40c4b1afb80bd55f4ddf1f49e038894ef9f13dbaea16bdec40a0e6904d6011c4a372d90d5446a6aa82749c6e0036e5ef282dcbe03c8426aeded143ee5d10221dc046beaf29be175139bf387eb531e61a5ba2705264d281df08adcc09e57584a662856902f0e0b9db6a3ebcb891b1c0b812d09d513a9642f737bccce093c54f08183db8f4a883603fd510588814916af3ba695bb2b5b357ece25abb6b1610c1c0ae7776b8bc1370bc84f6301f65d829493c839792993207255b4cf3cfdaea73479f0f490347599d0d80eda32b32da8c1adfa6da81f3752ef4c25feb2dd1ecc493bb591071fe0cfc4b47716ae6051c200539742cca7f47c81855f0194114638f8e6f0a200bee53d0ca6423e3f4d3bbcb3ad47ceae4387bcdb62e07891ecf46a5660307dcfce48b29a16c28e30941e2030d67e469b254cfa1fb0a60851fef1a2980a15d90b328ef720f32f0306eb5ae6dfe7f559ac2fd507e39d0ce4fae9d303b91872f83b454b2e2fac5b716a962ea00deba1cda8770db71d4ea2ace0d8815d8eca0dede5b5265449ba957dd157186dcd3b1fe844df0c29ed62ba1b5a552e036b102a7a5f3e5f282c6c41e9249a0c6a9bcac6095c7be72f6066741ec065ab239f7a73c2cf84c114a2c941191ae809"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"64dec738ed281993de68482c551014b584e49bdb196faf78e54f69a4da4dda52","proof":"6a05e24d9cddd833161d5cb53d0581e4b951eddd83d5d1539ea0f4bf08df096c1c5207df66e8e0ac9d3885013119d6a242d2a90b80162edfc0a8ba7d7dfbc525e298abf5d872d816668b5b0f57974d3c85cc2be80ce799d6ef5a082577d55f6bc890ce01e3096274108cad557edb9f170ba5f41774b16aac73bacca38922677e1d5ec99eb38b75c94e7cf95b2acbd0154798e8fb2093aa5c1270897c32c10d04786484e52c42c38d8c4f41512b051c36b1b18bf764585ea8e0851622a390cb000b4c4a9b7e4cc6e97152c7ec372367e2b40132be5f218984d1078db4dd9f1d0552e60883945614e57f276637ef5de5d54440641496cc6f17d48e5e72818fa46ee2bf1271870f1f7496dfc009fbf345de190609d17441fade91ce667b77bcda1fde0fb443480d0c4bcc599cdb3388c141b3d28390482a6ebfde1b32d7ca03314734ef1424d928bc358d8f80246e37115eb0f715356b4383961804df3b771c71503c9507a8590b67ec9e811fdcaaee020a17877624be158447e70f9b83ba97db659056bff47571ae60e7d7af098235dfa1a8bd9e3f76af88c6c359aa8de15aa145da0213d1a7e4b640d0dfa8a5379ce7285d78e9cf13569c0867d0a7c1f77a357d6874241f5f9eee7442ddfdc62afdcf4c668baa0fedc17a9638a91526f662d70db8d0fdb2dc05a70c86b76d25c5dc1e45d667f37ad5e1fb1e2e82de133d432b177220f8858fe518fca5a8ec67381227be9bac4bdc7f33048c1fe1cf4a38d4e1376e2f16d2539d216640a4d730cf96f621cc6b0a0f28aed4ef24963a9c5e7efe48021e931b5d41b0821d0811f4c3eda6806242cb1491d56093325096b8bb8a69565f106c14b2a7286927d8e8f8e85afb5944a99bab5ef17efcd13071a8b217f20ac5ca1450fc4a563f91454ae0cac60458fed2947fe02dafbed5f47c0592f0850f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"42df6a0ca26365947623f57c8800c41bad8408d1e3d060bfadfc0d941d0ec131","proof":"404d93d9c757d59eb3ec6c8fd5d833de27721851a4092a5c9145530d5d55952a4c97aaea8513265d65c4a16e5c7caf94ac20a17909e1408dd909a8e3326c5d5d2aea7c761e954b057cece6b889f7f04573e556a09d242634a7acaa1b532efa796083b334457d45ddc31afc4cb97d1aa7f5a14ac45844dc9407b43ced9efc412093e6983d97ca94540bf3957c45781feae8a14cc6ef1d460de8f24899bcc61a071e8f420210b57722767701d17fdbcd2253414987c801d60584c9d6d66cdcde0107f481a51b29ac0a8918fde8692db053ce3af8fce9ee406050467c195ea10e00f0a1404d6e2164ad671cbb91c3cd84b0c574b220fce053843debfe72795e64375ac260930b7e30018afbc1511adf2ca593c022f054656b5d90bdd7706516b83758652517811b1a405f1a0e725e6c12d4359e6f8789757553c627e90c73f37c2d941b98e3b44263c8157f4daa6a5556844f59fd58ce10995c0e2e0c5c0dcfd701642a00694f8feb8f06078dc1c83414f0c3f28c82aa5ac31d62e5177b70e88e5c4ad6b5f6dfe111334b3ceec8bcdb14b731ddb273b1e110e41e14d5a48b59a0515a829af7826f2940a3549a7ee9417a8cab468371e852bc7d700262116714b21e22f9414f162d359744d54ab157b62bd1fed75ca8e72943f54b2dce598727a450f4950a83cd3d58256a49dc1a0750d9afd2d8b6ab28a1f0856e74986d190cb42366a323747c0a2a6d44d0569c70c5a719147f1d0e3ae6722fdf691659b8b8c05b98c0391b6ee53f3090561a355d2148080ec4afc260b8d3abf2c59f6a486a022e727628a2b38c97a61db2ead4789625093777e45e7ddaa1dc50a21fc1c0f5fa43e1991dfca58a4dd6fff47d41ba0f3fedf8cbc9adbf68c7164f915bc38ac1d909243664eb28ddea910ff17f2fb5e56d76c9ab4a03a6823ea37ba41eda70dafb0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"94883b373e7393d6dac6ba2bf6397449499074b2b687364a3f94a52c3948b521","proof":"d65401b6fc681f5d2d288dce276af2596c81ee96f8901a1eac00774c9e644236341ec94d790ac3a2258965632b3299d857edf1b20facad9ea098eaba78e37f05246fcc15e94455424f087aebf48a91badcc3c5e96bd3323ae4119a7446891c60164bcd0e2edc83b7cb0f10964d99e7ebad114cf2447a9c54306ad04a26558c748841ac50f9a1b6dc3e03b11cdaa1388d1ee27221bea3b85353f4cb00a7e5c009f5e44b4c65233193b1f3964f6a37de0786aade7243c89c859e52e6d376f99603438e709d292abbff40c9ed2649b7c17fbfb0bd4ec3cf0e3d8eb2f6c39d05f20b6afcf20599be9bb228efb1ca07db3e43f8c43124bba00b0ae266ddeab07fe06d224cc7e9a3d2d7bdba70e8882f4572aec274cb9888d6d4028ffa6de4f7b99d51c29095abb8f4acf031ccf47cf643c2f6513a686f3385a73806bfd3c954b12b12922ccc41c929ef5a8046c64728d7e656dca5d355fbaf056ef814f9190122d33748b6b3712dc9d00cda9914b4c2ae10e39616b5deeea7ec13d85f484c4d4ff67468dd3b5dfe309dd6ef154acbc6d2a6a69d0440e50cbdd48ecb836d357f00b91f32bf0d35ab8f6cbe5589eb31b5f1df91097f8e2a9d6505649ba1de1c5f04753ea2577bc5a81c7b307516a8deab5535fcfe6cbe05a501b1773ffcb51600b1835f44dfeac2713d13a0cf4360f86cf283e0bdb86fbf275ab3a6c7deefa48c6423375c58979b44e7f95f360e0e4fbb4cdadc9f46dfb52ddb1502653eabf2f53ca96cd63bd5c31c3ae24e5f075e4519fbdc36957f7780028a04095a8c2e2a6809243d3eab25213575a5f3c316abd118ccb79df93f9575f03852867d97cb77bfe4dd044c43882b0ef95b89179d65ff958b991179d2ff296dfe26c2e8d7c72049eb140540b20c99ac4ad51c6bb30d1b03f75729db845c54fad81a8e16a2f2d49b14f102"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1e09a7a88a3367ab24f415d8f9bb8c113cdd66f9a27970099a40d5de2456905e","proof":"4c20d3b2eee40c1b44d7762f00dea5284ba6a0ecb451c248ba05773d4658071574728bac28cc0712e54b1a595144ab12f5e5456935a10d454d2898ded052212c7e0098fb9699515feb5ab157833fe805e792a1d53cb3a2cd8f5d7210aa88201b92ed0bc1b37cf80526409a891fda6bd3588aeb7199576eb42501597566c6b9496a5a2fddeb50229ce76bbc0a23229bcecd862818d8b91e7389f7db54351452000fa9ead9283b49f2812e417cec00d40b5c533bc56e948f348a00b88977315a0335f4d4b4d828adbec847fa1fef45cfb33a7ae2196fa7fbee5180a057547a5f05325879bd7ff40a34d2bf8caf4f98e4a83e30106218a30403555e2746b2c2dd29da6d52da898fe5069cbc02c5ea89f8738cab43a08b4f44a75927c781f9a67c537eb948977266178c61cf90a2aecf8dec95b1dfd59b3e28960ab25d24fbcdb05540b6dda966d0be41cdc93f5d780b4b08dbdc44785a4864dddb055d0b39cee2664e285ec7871c64e4f8695bbbc73b9be7a8a8eeb0794b9233e211f9ee0cb4d56d888e9855e8f971733a2064cd7b8893d7c482e5456e587c89de862359469d654db2be697ca5e8455144061bc5d9acdcfd950441298312c365bfb17e1ca2a663204419c6383fef1d98a71cf914ea5ce253ae0a58ed8ce2dc48008b87d66d1c1c03d426ae7f66b9781900773d8d50b31e1bcc62f830c47695b56fd267ab10e0be62042e80ccf0d399e4a0b64deb303c39404e36fba6d694af0bb9f5fddb79d85b58fe34635005fa7337499e80b0a2d338c5695ac2f9023781408c8a2501e2be5f7cbec273044336c2e15046de96642bf8360b6125f1b34adf999426fe1f470eff107331bd9dd3fe96d2856d51a19d8f9c0a1b6f26e78421772589bde08f4a2e6f0e59158c263b15f02c7eab8f5e1955624917dd23e48ecacd0bfd44b54fd9f5f902"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d88ce98f7a205692775630e418704306973e1276dc9791b47a265b53bc897641","proof":"c054dc8746abc2086d028df8240a091db80cea3757d20b8f8c1899b78b62de77e2f4550a8cd983c5e30232fe65f372633d5311bd6f3e1c144e716f1ebc4e991e48c096a75423a423f18d7fecbbced6d243538701f9319c0695cc456ee252a960e6410c04c5f58e3ea8188932b4c72e94710236e9fccda9a30e423f3d336f1869931018dc62c544aabeda1067e75c14a27ad140e0c0a1f529e7949c9325f4b408f7d15716c2ff09c23bd388be9f49124aaa0c2b848f8e432cd1b480856654880542ead6d6b75675181f8c10f544b290fd520cbce32e949b51b2814556e1c60c04a8ff48a3d994b8f0ae32905c3ff47c9ae22fb2e4d3d84d0d765d91393937a147185ed334353717b0e77adafc69bde7b70d417491c30713d53639166cb233ff44f8bd3cd9468ca5fdc2ff195325e0988f707dc96dca8c6b7d1dd26b32ad7b400532b7fb2bf87f745103cff14e1a8c7619a2f1991d44728d6f01d14a771d1147054c41312948225c4cddf82a6b9e92cc4c7b353d5af8893a1bd365cb2d7825e61f9cc9babd51fe71cf5e7bd293e46f2b49fb5aefbd168125afe110caaee6c641585a6c067fc09029d3d8ee0fc17c5a8e0c80914b5d86ae1595848cf659b07c507f4ef62376a5e69ae5e26575d3feb33207e965dca3986fe32683dc86aba2c7ba579c41e8484c82267510f3931c9eec566d80461118cb323362ba4573445656082c5872d768cf15a6bdefae2aaadc82323fd26209ab40fe6d72600fc95aa9602b753eb67397d9b77a6e16457a046e3658c239a6319661689273f8ec7d7b791873280ccf9bfb7f9a5be686b9a456929565f21f0d96832d2f48a386c86649ead4d3358bc4793de9f4124302386d4acfa44048f25ae368d7e9f4da1f920b8b8d0dd402b6dbf6b819a38eed9054e4449b1a98c00216fa8029e15c04018572b27ed42409"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"905e910dd75bdb50f3ce70774dd878fa2cbc52d9be243815a7c97d309cf10238","proof":"2a06c38bb58d1147ec089348384694055ba4066d19f77ed2194f8d6423e9fd660e085bb57e9b37aeb3c8e47c2c294a707e80d418f1846021897851abcef2ac5c6c7aea07f5cb9946ebd773e4d50280e4bdbb1a817550a121b86463b1ed1c000a56d8394602bd44ec0c2bbcded9ef88169c458b2cc79977c7b93650afd59bd60b2281db09bb6efcdf607a1aea92030cd9fa8f01545b54045a727e9f29810da302117460b2f366161395b2cbc4418f641405f5d00f7577059a9243ede73cb1c50b9499b82256fe86ed6325ca88a17915811d794ed5bd39dd67f58daf4f7369190e2efeac640250c92669beb32db9d19138700ed6b57d6741be5e0f51cc18712974ea4d69944674185e3fe0ff56a3f41eea42b1099094f9ad39dc81c164d151685fe4512400082af8930269ee80a48ced9b302455a38fe5967981c6f4f0abf4ad68d4fb69c49adf63f86ba36368bdcefe228bf6828fc6131ed1b57278e8fa97ba66fc87062fda60ae1093317e3f4c65ce3e2565954e0b660409f2029a2393906976aa3f6e36707b5d5a09298648f109c79ec2cc6e40d566f63a0a6cb7597c3cbc6ca427c45b357365851499a248653ff9e62cd7e04411a1e4832e27ea6cd1a3df6e40cf85107812b802ec4441adda1128393ee5a80522cfdcf6c2a3439d044f083ca480d4302430b356f1d2232ddf9a69c3d686c06f6fcc684169e0798f94242a3b9c016db4ee9fb0e768f0275caf5e5b1d0a3ba7b402935e89bd4bea2aa29c1e615a8eb4fc95dbe5ab28d793329048fc2d552dcc84766bb138b15ca5e767942d615abd205221081c0fc8b1e4195592e3b5526c7105f5e703c813f8a908a4c78213e384fcca417ca2a73af7197ed992f77270f1484a82b55b0463f33c47733e650e7c294760237fae199cee92fccb79313aa0d0c91166f1f68206548cccf0f7bf01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"506c73f24f4470e5d952f0ab34d0c82e7c5415f147f7b0102ad90db0155d2e70","proof":"c2a7fb5a64e0aa6091289bcdb5667d2949119b4585509dfd214301d5d7b2e048b6b736cbb1be4474ff7c3c6ac2eb11007d8e1830d836c27c445e553654ec8833d4b740c2025bf135cddb92e5757c8a04291d6c9546880608ad5d548816cc8f0e0a79adaaf08caf93bc175280c9d8876cac4aae4dca18ea70bf29f88f2d9e9546d74e4a9c9f22efd79513284984ff8eb492aefa2dc57bc7e680c647e1ae2ce1066457dc134a0c1d7a97bf86ae4d4f521b340da75ca2c3dffe1850d1ee367f4d062d0ab2c984e80ceb50eadb7a6d505d9df330bf8cdff089d3d073efe95c5cc4072e637a764f571372553bef139ffeee42cc53207425ab4e44fdacaed539f2a34f18cb7b4d061cc3b6a34d8a6a96f7d68db96894d0b6dfc823b698d138edf8a42dda6747091ca1708140855296ce895138c2e37c93530d7ff85d1ac1463eebfd1b9c2d3dd67265ea1f72c91a86ef707ae855820cb9bd744e9dcf39a0dd10f0d87a3c4f83ab64ae924b0b3242b5e8f97057939c9a1fb86f264753d37f51d4c39e5816a490a1c43c7c002ec2dd51f3c998353a373cf985ba7c651aa1d0c12637fa0e0c245f0f60294c8ee48e6809eb9a2934cba8a90dbb91b0166f425befb98bf129d67847a87fc9b262742de44bad30665a04a2742db12933710d37cc8e4ff93156e46c9cc9f508109a4d2fb0074efa3839824f0b0a1eaa634554ad49bd2450aa3adc662246d090084aac0041494ee064d8e479e7431f02f68f6f4ad30906ce094d8e6a765351367284319c87bfd13252068519f99449afb92b42ff25fe625f771c2c1cf9da99ceee2fe73976892eafc78d0fc57f748666c8e5535147e26f38f83b666039bc63ffc83dd52fabf565da0798f5d82365c745d3ed291843bb0f6c3e08ca47f353374f52fbccabc1799be5fc631898bfa8a4718daefbe9a9fef9ddf10f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d02f00f964a94fb895e22bcd219993ba0a57c616b8280cd66b656e8b40512272","proof":"c65c50249a65b785a08e4c4a859d88fe03040d79a726b80bda7cf99a17ae7e71aa2ae93ebf48c0ac0a0a38db2ffff4d98842de9f1eaafeec8f36d274edf6402a14421e993a200d2524aea89041b06fc98b54d57520bba23a065e148a5f8f5765eaa5dbe28201ac030b67ad81381d4e1b4402852a9556b24af24bc430c614cc4f97de10ed3bcffad8dfc9583d37f418691f6b45a4ae399226ca94c25bc38f980b8b28632bb7bbf7bf0b7712505fd82317ad2090ebc666964038d263f17dab8b09aa5b40d94e166c2dfc29a641200e765a96c40810ac11f132917bd66f59be0e0462619cdff40ef3c19d86b110ad590bead37e9c1f9da6e991f0fd61b869a37f0b5642de9ec7fe444d973388907a399a672ff01059e20c48f2dc4b7834c73eaf4f407aa9ed71b060010446f736b1683362a3c69a44fbc139cbcb797cce7fbc6e13eccebc163a8b98a612c7bdd33f63c616a13fd3c92bcedd08ac870734a8bffd797e2d95f1352d08b01c07d9d16b6ee7903723ff6071bcb84e60babd533006da7768beea4c5f6e7df9d56d82b9d69c75435cd9b24e682f85300f7223dbefdaee1caaa3921544d3fc24f26389e753304dcdd94625662dc5b0f7d719a579df286000ac9f0d7a1fdaed4bacbd72b81f89f2d296e243db16d63484da021f2f934ed36f6ad1661ffd0869f9779de31ae60a7bbd70647f36145c4b42e6a72c7f16c3fb0d584e3ceb883912c5b010b91eedeeef7476cbe588afb78d82bdf3519b9cb2322f7a06eeeda9d5f59c588238103903b140e8a485be25ae6aa1bb4a95e56251766c445435d6e6e0cba4c5a8f01da02246b60e142c5655bc06d56f43d06fccb381254895f2d5c1c3d03195f965fb95aeef5cee7efe197cbd1cedd7a05a30d90ab10a10542c24fba4970aefb08269af33c668c792bd061c5464f19bd7fcb30cc32001"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"66caddfd45d498d93dc571ea52a074ec78e477714d5442e246fa0043353ae740","proof":"a03d759618044656c5c7343005fc43d26faba9063a91bc0f8abc1142aabc59774c8b24def4175faddbe6bb8ac003726f0d369eca043ebc964bf04e4bd7aae6263e6741f47acb8470e17f97bfe231027af0a351f05b64b05bbd64625515294205ec6b8dd087415c5840df24d43a2705bbbb6fa96afd864ccf97306be5b012752d92efaf8a479955607b0fb311876744b7c484f8dd354a601fbbf3c917b6ac600a2b8d8ecf2a278cae6ada81554c7404c748ff72afd3aff3e8386dcd60c5f15309dc3bbd035b0321ff1d055a062524cf114109fd313b6643fdee11e7114371b803beb9615c28ccb4c17f365a101ad422f3bbd9be00a2389602fb5482068e9fc16e8a717ca848d173a648308fb30ab0389b6afa5af60e72aebf5efabba439dabd00242be82e6198837ee241cd71701d9a3b44eae9230eb9aab0c0ece53f17fb0d5c3c27f76ee6e04b8ee4a96592bc3c0cba44a7ad01a3aaa95d61fda926eb87cb729274747fa31bdcd4d09ae71a95cd12c33c8c67fcbce600bc6c1089f2af28023e6e760fd590e500002fefc907eeb6268776f7ba97c30cfeac77b5ea8a10f62950c297a22bb9e1fd8eaa79b4f657af96f2d205c4906ee24ac299a82b54accf075f5641e799e862f83b527e1141640f8dd850334c7e4f9a5b2437937d33a1ff076760275b323c6c2e167cf2d0603aa49c0b31ff88e455d14665aac46948eb32f22bf6e1a18a7eeadc166b241d465b3e8fc1b046678ca153c38a992bb4149d8136576ebe3482c3792e63aa27c8a910330ddc279cd27027e894d34916fcf2ac89721466eb2f5f2c32aa4087e4bda3e00f9fd6dcab5f45583c1e2d2e67c6fdeeafeb6e2b0c5e92592eeac944d987c5199401f604fdfb01aa15be793640e963dd63830cd0ae700b514a2896c8404230d7b3c8b5024f238c4b22aa4c8c4582a6bc76b700"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c40035931661c56b23bbacb8445fa0fd425b55205fb79fd1a152d87a34724160","proof":"3a4009f3e76a868ec146d7d095b4695d163c3c1429af722ad062aab955f5630e56deda841c5712c296c68f99e1fc6e2e8b71c66f726327d6709459c6ae63b01894fedfb35ae0c1650fbe06c46290b01e2dcac619d704273328d0efa4f76ca2344212c5bf2d628246eda72716cdd8a6dd49bfafa1f6f27a517d66a58c0c75043e95b3a748086c6db1e4701396820042fdfce4e19caddd04ffb2f2820b14fd8308cc33c4f101b69094496b8153026f9215eadea89d71fad692213a3359e4a6e60eeb19f6baf357b8c2f17cba121614ec6f200bb4b8c3e9e0a29925b18af15046064ad1b1b5a2f87a8e3c2f18983a90140fb7b2e636c633e8e4c461909fcb5ff064a82967e48bbfb3507a979e2fa97a30d4f017307d45f7f6ddaa90a0433a99d621be33ae6a41515d8f7ec8c240b4a1cc68546414dc75d4efeba53ab18aba497708ecdef060b03e11e864cdf029e1a2876bbd98908fc80bbb8a3b1f017835790f5590b4cfd18682d319498c5a9adc1f0a64f27c1cdc3d668484ed1b52988a788a04dc574a0b12627588e0e5a811cf73fe17d388f9c7418bf4fd977c30133c30c11d8290c5c26e4349dca5ebff425b01bb52c5e9894b2527185b066111ef3d2e5a3fc6c5634bd0f46f1b5afccf3ddea32e26f005db56a5cc4e153310501e64cd1653f6ec98165668ecd80bd6a681fc4bd00f6fab48acf8d03ab2dd23fe06d296da6748c00a8dd218c7f0ed3e9158210f965586faa4e62e4163a328dcdbc68bdfad685efb87eacf61a1440e657bdf0b162b7e351c088a53512fa90c9f67513f9814076ad777c21b7a6b0bb774c8524fafcb6b08e756ecf8173baa2659c9315024200eed6f8f7d8d3e6a61e501754400b1f38f6e444be12b4eb4f34f92cee9f9c9520124d36020a966497c04a67d2e9dda88fc488eb3fb9bba57aa3b6cdb5da8c1cd0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9ebc35efc48eb103f4982f4c4b78987defd63799bd74ae7ba7140e1438f8c23f","proof":"6c100085ed7a854b44866f334b3fb933ac9f0b376d2094cb963cb81a162d4e235c2dd0d913ee4ca9e2a2e69e344c22d60a6a1ccb734835c87ef78283703ee92328f3e0da4cb043abf9369f83fde503ceb533c0689bbb8837466579d8d0257b3a50e18eb297cdf5cf15b868f2d9d611f85711f6dcffb1f3869f2fa88a784b8b16fc534c5d74cec18acc7bd56cad6e4b92ef7d987bed437bad5c56ad64ab14b803c90944df70f27871b5fafb52e4586c0a8c630c3a2b60f65130d065e7102b830fff071e50120e2de1e2ed7186797fa430f818478f493b3fbf73a79dcba184ca0474333a59d80bf95bd660b2fce6057efb353779c9762495f5919a456d2f904a522677898225eb59cabffc5720d64cc3eb592ec616ca989d82d8c02e2052194a4a9eb61894f97708e41a844e72ca3ff7a4b2f9c308700f2edab963af4177c68c4cde76f48945cd5954bf9344715389b0ab8d2cee8a1f825a510ec23a88cfd591244e2d00f1385aa4072a1264845ea5c5e5b47993749406b16bd0a2eeb2e1849460b64799b49065df26f2fb1fd0e3237673e9bdb1410b087af975af4f875b2e34695ac545e1e289f4aa9d99f9304156bd44ff02f155b9def910ae386c1d1e723863466fd825e0aebcc6dd2dcf46a601015c8629267c9b8c9011211fa0642aebd17c40b6f9988194136110befc15cd2c174b7cd1fc86c3b65c4beb612416cb0c7f1e5e7f38930648bf56a6804ae65a7a2d59a68e7e72eb463da731cda912408302176013808082ce28af8e9d5edc7c7000d6f5ea48dbd4837fc8d56fbfe8588d00608e732606b6db9b64a27baf44b5a1cd3f91adc8104233de856651afd8b42c9e0639de0f07ac4af1eb67c3b48812c31fd9d714640046a024818a2cd303faeb940f685d02ca381ea78da2a78f3e4ef9492d531b03400c3d0b447ffcb5e08e150604"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"30f9a3ce9c272a8775fe86bab06886ae96d3f4cc21f5472eae5dcd58d4c31b58","proof":"d0bf79e40d04ef8484a5944c9dadd1c8e0aeaac0c26a763de08ecd22651a7d39ea5a9b19c19f922b851f95bc8653d9cbe6d729afe259463f398e96c3378b07149456ec47cf69cce699a120320564b05739ed6311a2d3fe72341b2582ae173d774e686d7bbc04c4aa5aa95608ed6afdf12a02d6150b142bdcbbeb33d243862370b0836f27d20c5a4910bf38eb557e2897db3c1acb836727ae223a045474758b029ba8c3ad7662c0cbb265a17b2867a8fd55f9eb36c05736894a8125466e14130cc904bc0564c8d87594ba332416f5597eaae5679ea5f6630ec266cdcd5e746f09f80fbe7362e6f8e02fcfec8f2e097b971d59e71f158d10ab649b3bf481ff32478e57f9373106e883281cbf00a53726a016e839ce482a43bd7595ff51b29c141c02abd2a98af50756faa1d00ea5d219f68e3c88de7ecdd4cee604fee309726c585e5312921f6ccf9ae25664eba10cb69d61f1be8d3f8ec6d25cae605a0ce7c409c6a3cd03a25b8f8f444f018dc971f5dd48e79d666fb2988e7f2a2df39b89720c9ea34ae09352e8f33c21c8ee97b79b561022caf475a9b528e078d8a363ced74212c22b898d0b53684fdc49e99bef1e0ca822ae2d1346fde9bcf014352f8e491baed0458979de6d74e43a619f55fdecef0ab8976be4ebb2abb2170a8f062a0f77f049906c957572ab7e5f43a11601cf8c8404c37a695e9a197265ea2c250d420718f4a6604eaaefbd1e657d22739aa4b5ecfef072c591b6036e050f45adcef209a8fb63aa0343e5af4a2d5d58870be82480144477325f656f3eb9e27ceda27e198a6e734d4531ed7417576d391d7500b9a4849392e48dab34d62ffd3139f90e4fc3afc123d4c44b63e0619c2a585016fbf2171ea07c4a5155d7564608f55e5107f7ccd8acb8554a44f65fee7b95744f3583f0e9b73c0e5eba9c9622df275d6b02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9a1cf246067160cb2328e9c9f7b045cf4d80c72ea06cf4640031ddb8d9f64843","proof":"0efc3a25035f94b937be5232783170ec2fe47e4a4d3231eaab51f29f6ce1a24aea9aa9802ea14a26deeed996cb3aa7e6751ae1f330f2d51ca9656f2892cb76681662c8bb206a3b48c67063eca0c9f1267ce60a8ee178d31fb7a2f64f31b2a340dac0a1336c2b6b207c3ec24649aeea814eeae27e9d9ef341d463c166a45d2f1073533529bef829a95f4cf289529474dd93b661fc5a1c2ebee53bafeb896a57090dc5c205bcc8f6be8abb1ca8ca5b585574360b08e8c2e61d486ca85df73a57012558e2dd27984aee7d4c4c538b3f8cfc06327e44a7cc011006e9190eef7af701f4fa9f0491c1209872216a4e6c6b5fa8c80ee10f66d30c63d93899a59d6d300c34b25ebe6ba36e78df0e70f117cb3dfd0ed969220108d9803b367a17d214d46ff426f17a610778aa2b7981779f1d56329332f282be8d239bbb82517c005ea664003d524a81c31b414f361635dff815dd87d6804bcd9d705f24b8048301a83e59c2c66643547b834f7b0d25bc60efabf3c406179c8307283105a5a809dc69790fcc7c2975c53115a439ff527ec7289e27473d7ea5189efa1bcd1418ec713ac434da3689d0cfbc81df71967b9ad53673f8b0ddb9c4e525bdb7974743c729edce6a50f81132eb9ae6026fc6995af6ce353e0fafdf5d09b5ca151324c27e2679312274261f6d43c802d1eafb111ebef503b095e86850c807e895dee24801150d5c47e617317395a166ceceac28a342177f2f63637e512d44b21d28b6fbcbaf61076610c5131075be6764d26facbe4eba13635c99f60c609656f73c87b7861c925c47b8fc7649a33c5c706517fe126baae0b6d3046af6e767bd041dd1f86c3bf6067b53af14b57808fa805a276f8f15d21f93ade36a3a29b276db4fa499b9e812ab04f52948c88c8673dd841ccd9360b73b5dd8afdb37a0237040242b143d7621670b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"168c99cc0e640a9f81f43ef8237acbafe20c5afb7d09daf50e181613d21f9d31","proof":"42caeefa97c5b0aae212ae15d66928399e23388d2508f54ffa640d1e70b2df7256217346e6deb28d589a0879d75734c51c665bb818d0d4caddd21cb66aa5a4114c07b5ba5ccb785497160179accf68632ad652f37c30a6d5ab6008dd77e43a1c068dc309f3daba99dd670d7afa1f6e1d2ced3d5e508d023c045d3c4b32bf795dd85e3d3e595420fb23816aa140a162d2319692120ca18d47164c52f4f0c9cd0e927bd6aa7b8866f8c8f589aff77e2d305a59931385f25540c463b97ff059860302dbf1e22d9f3fad7e917861f141c5bb398804339be500d177348ffe142063013ab9dbd47bb3b26aa37a685df56635b1e793bd26702b354ea880f0b71759d7311c536e451bfd135e891871bef46804fa8340679d19a14c0c84c48a28fdc91b3a4e2029e88dbcb3538f2fd402061377c46ce0d3ff66687e2d6676f8a356c4183f8cc8a6e09c9c27b929dc64a5646c5e1228abc8e12059d1481a491805f22a47374c532982d8ec10162f6c6f9b73a7b1a73d2d02ebccd765073b857ce17023482f0c3d492c1071166d68ccedf09d5eafe21147056c264100cc785cd0a1433fc8694ccb94a21878991ca98ce1dcf560b094af584a2d6c7124c7b26086417fba2477704963421078e2b5eaa2fcf6f39dd83090fea88746cf29513bed1cfdfcbfac1f2c171313f65b1b8c2fc1852771ee50fd5e1648d847c785c6c700b136ab3c6d4868bdc238eb20cd334e482da2205628368593fcff193ad6f1b4a3a31895d5b74b0ae831fe91767d5cf035e996535b6852aa08cd44070fb5e3e83bc2a6ab243e3ca873b97e7408d117f32005de25e65de4512d636db1d51c064562fcecc66ece64c2a8ce390a8429bebff367535f57d32b5afb844301ed63fd1190a05b970f950276c2c52c42451a62325834617a23a3448004826aeef9065165a2d0f5225d2404"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6e23fd5f4142a629eeaa76e5bdf640bc17b1275a502503658804fa1d8104a86a","proof":"964c38b2a00869041cf48646c6ce15e73be2529c250bcd4ef9b747018c22157476054b62d60d1eaa9955ccac5c01b36d34381b754061de0d86208205577037004069b47452271dc747d3d5a21189fca6bbce36ecd250a0b8a377a43c8da1493926c4fd479dd1b401709c00082cf024874fa93ebb26304e04ebe4cf074dd8d46695e65643154c34ff962f49caad529db5ae9ef8cb5bf68657008b5150a3daf20f48ac263ae4202417490da280051a6311aa05fe00081489272e44cfe82147ac0927ab56810bbaf6a5128c8a18378112e0ffbada799cf2f420ec512a86aa8d060b188fe0fa4be60b2bc0fbb40626f09f90698521366bc33a610a7b1cb79010e201a634a13e90d57e7ac87e83076ed11047ae2ad5880f648a97f3af81fdfeebc9401e7e9c170ce10003c812183539051a54f16a648ffb361b7eb998ec6ebf9dbc1a1ee2b63bb0443bdce904e7e71080addfb2b34e21fed8f9938d18d0a84c8a3f66d60b72fa7bf8ab9fecea2de4a451f6876fd997d9b61712c6aab8b58a992347232c1a68b035b55c37c70eb504f2599bd464f3e739eed24ebff5dd2c0b27250951ae28e796b8ee49b14773ca49538d1276b69772a990b1abb3496388f4ff07861978488e0787f7a50c5ed356791b383905ccc89843c81d4c7adea24b7b84b50c69a84c220c5719e245bea6c5d0fae48a1e25f7cf26eb535679643e502747ab241f3cc2a762ccfc255ca848ce5dc04436791d1e8ebe31819b2c35149b3c42103a6b183216ab8209f3a61ec915e5b8e8b3814fdd6037c2fd2b8863e8784d439679784ea35d46c48f3b5e94b713de83927ed2264228460a1d95411236fdae1d448358f89a26e606dddd77c39ea9ed2a7af8674d93398d161fa6f8195f6ae2e6cb4e0fce96c893d4d49886d0a4dfa980191e7c7ab79b0b7b4382a4edf8dde2cc94840f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"78d3a5febde0fa9f5621ba304f3d0491416dfda17bd0db59d933c3f019cee63e","proof":"c40322273cbca8ac0942f8e2ed5ce4b0c74ee4ea5e9773ccc3baed3b8e97f2417aee8b70a51cdf26afff9788fb92764d21f3a6ec21e7a3bd2466f3d2e6ac994dda7bb458ca7a7bafd9d3c7ad2cbf0dc96c6793d0c1b46bf3fe3126daad02724ec4a7f5e9a1e4097b671867e6de0b4c3805600090f722a4916be098b13da8de49f72305f3c652fe4b430315167e540472c1e36eaf8d94795d72df299be1af6a00e3dae322a208a77c3eea9d926be17d3e9c44ee47f04abf5cf719872a21f6580b30e175db223da3fea06a0ee781f18e85a289b7a1a79bf2715b7fc6a3c26923052286dfabba5f385a8009dc548385c248eb1a87021a6b741923e0493c1151d03c202c6560431f74fb5e8c7741aa0e72341cf34ddc4ec829496052b3166c6b6340f4e838ef22a72fa1c9440726e3bb27fc43983c5701bfead9810bb51667b42e5366f88a01b6fc52af36ae7f48f66ffe967b1f64ad583ba7a39f31c6c774b5e452bee1fc97a9104da46cf4e84e4f7a073bda0db6215283d4fb343b02ac00695668763572737bf336b0d4e1bb67cb107e1e455bf724590e0baf64dacbeff242c15040b8e643d060a2b7f1c5aa26a2b2c36ffe82cb0507a14238f389f94ab292663b9e2d3927ba48f9fa42d7d558d1b04014d131545255b74f68924f7162d444107ffe443fa4d08f2f8c3d11239d9334cdfc30a1e11af884469b82d81cd5271d90643a9ebc32a034d9d6a472605bd2bc185c77ab5d5856ef044fe8e1414a6d8111213a4db81df2b5fde82d9fc3f3a9b7425cb9de28bd814b7ea3ff975a729813053bbc9dd333389028449274172500e9db966b4808e386ab61a0140082fc2a39e63204156d8f7ac91ba7a3e5d6eb6881c31974b5d2344ab8b4512e8f255a285a9f08285d2d7c9f390178dc545a9609d43fb93a0700f0d9e39512e4c2f617da424208"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f618f964fd95fd643cc043fa1b8499f4c9bd7050447efe3bc6647f4eb4aa2924","proof":"1c8848f31bcbee1859526b7f68bf02919785b555b83cb2ae4ebcaf5ca8038b7b343d7e981792db8a209224a047772c45ca1b8d2bc00b778b58c4e2e17931c35496239a41d04cc82b34460505ed4c97abd297857a25b6b831e1a37bdd628593483e04aac59cca2a0619024aaa9c28a9f3886dbd629a4e0db306d3fc2536d7c06e003e07a9743895987bc5c9e0f562ce40e6aefeade9a075074eb13a6133ba210d7d639a73aba4d22b38a13e6d119ff49fef7964fc9ad442b7ba555f7c5198e40be572d8faf87ead0410ac7f9158136767a86aa1d9ad14fcc7d297ec003c1eb50604676d08cc1a7bb7c5e959a095c484609007133f1413daf62f75a35ac45e4c1d9ae94e3a8039ac71d70788d252226226ed17d54c7ef423fca57706adee520648248ed64ba81e73418d620c128b80f49fd9a1eddce72345095269feee7a081a12b4fce28bb830d075f73292a65b960dfaef77177f7328d243c6313d819ca1f05606ab19369cff9a1b88c45709620c7e8b3961f3e8a1eec608452a11e7313aae3df8a6fdf38a63d771128f83cd1221640a0d1cc8fbd7fbf76faf59f9e8b63493302c1b8d391f140f5c409d73848775c8315325b9c0ecd93de2dbd9b9475dc940528aa159a7a871e893831773920ed6620094c673f1188bde04c3aeac96d4bdde56ec6620dd0e2fc1bcf35964a14ece32f23b0ef767a83d7a09c04d00949b99ea6916cedb3eb2d9fc366a4eea7ac8036755bcd1c9b3f93ece3814363c05f7a3bb503a2705d801d877fc88098bdc11935f9394c389ed01c0c6528dbbab58ad820c112aff030ece57eb8650be7c3d5f8c877d50a54d6a896f357a670b2e2568e7de44e9974d424be1f672506004f38bff2bbf504b51b5e40e2dab8341327e0d6c720e3ac66dfd5b3d1aaaddaec8786725022e051965575f0fa6960a050ecd57d7f70c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6e5a99c58fb14de92d10582084da8bee6aabd492a626ae0e7b5d186ddf6e493f","proof":"b8f77dcd5bfa07a4643ed4e647bd22d0b230f1c36344eee5aaa58d6c7310a0009efbbcc4c8e4d4729a8b79106a1d3232a6d51f39ee2114be23f7191b2849026dae89079797ed76995cf3e672f9605cfc421c032b4e2d870113d64cc3f3aca033707af395ec5b1c3c1861a66d0324ede6759879db969c8e52b97d865030148c68bdde2fd0dbfd4c54cc506cde85a0e6ab0b6a9e19d92091236a6e1c4a63ffe40236105f52ec7caca57a60e42b0aabce1b97771ec5e873048514b0c08508f21f0877c04b319f73ddfacbcd1ceb60c85d33e4258ace1d5d4955836edc5de215be071c23f75a6e95780823ce7850f50d1eed2145b7d880d5737963d7df4506c555175c5656a42a2c21dfa60f39ff140a8e5200d4b0acf11a38ad9a9a135c0f2c2651f6bb224c9a149358f279fa35e65f52d7d372a63e9988bc5d13e10f8aa99e0757f8cf9a939bc99e102cfc819bac06f94cf527d00fd181f274696106fd80f92c3450396b2787bbd3a70e5d1c056013692ea6f6572a7d5a37a6004def6cc7517d2d9e67b09f0322bd8d357b1803f446a7417da9c5f992d12fa2baead5ee9b316b2c1cd01700c4ea9bf4780b8d9cd59bdc10e0820803c6dc1dfed51525d70c7e93635ac9572a08722b061ab2a86ff7c38876476a904a164e4480128fba0405e891294e6b42f2a0529681010c7ba16f19971a8291e777a7a69b8c34494aec45fe402f46679ba3082992b9efe05e9a6d9a5676a35ee7c5f4808cebc62f94e60aa3a314cc30f7555d6ac3f744966c7bd64d6cb3f1bd444e0d5607a4f7d4df363a0d711844631f4569efbb25a8841d75257359b154520ceb09c8546eea35ebd8d4d88837e262480ad3cba6b71ed95784b4d6e56b48bd56ef74db9a558803b0fc18338d096463975ff385c1be54439fbe3e7d9cc19061f3646ba43c24f5045088b880900c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"784d8c5f671e6a8161e0e65c0d0d4bf0f3c6a8931b1931291c8e8c6b7d133d48","proof":"d4d5198ae8450c9e75255db498915330b0339b355b6e7b8fe9e8bbebecbd9374b8ce1d73e3e9b1c2e5e4cd15aa83f0a282e192953be761018eb1c3006ca3e8620c54c3d26389409a01a6063a3e99502f37acbcd073cd6742188ff19b31ff2508d6e193aedeb652918846b6da46165cf1702313811224807b32436296bab6540a61c9fbce5145df854e1c94e10512919979b6694f5e7a39565018d9a7fbd45a01ae2b677d79eb0eacd33e162c248d17847b568411018a3a5c2c81c6187ae9ae0bfdf5595dbe42a7895e29c175ed23101e8b61f233dedd48d80fdfd92037c37904742fe026addf233a04e20aa8fe23623fac2cee4f93521bf7584f564416f4f8123ca5418072156f30f790727feeb964ae76db0f0aa7d4304d5e3412257c3d67243c65f6e1bc227a8eb0d6de62dd395de89880f9931ddd5c3659790c2855242e0f8a3c95afb5cbc255fbc463ba2b8cfdb977e5c03cf736fa808520e051105bb51f4c740959cd79783393b2e517deee7ad03aa5216e74e3948111dc05f6ac30387ad8356371741d684ee26d31f98d33745cbdd273d3ea94e7824db4dcbc8fdfb04e421c710ccc11567faf02773fe6ac0aa772309e32a47da076d369833d39112837941b06a8e600ef7b10e0dc3ad3aec43fbfdecb4af0aa437ef1537b4615c499657273b6aef32a53d987b832882d30a359afc365d7fce704419a7eff3ea5721074eab47387edee02740e0956964781d88be4cec965425b2b3a37672984bb06c471b69287a526c1b87e2696aeb68e52e958e5708da08100b3459589adac89383d22728c47ea1b04c941040e5424c24eabfe77ebff9a8e704fa7ef22c56fc1f8e0718bf0a512c05f27491deb4a0fd2b6a17035803bc1286507caa4119571b894540988f5b65c910d5b044ea15989fa787a18162dfcc24b9ef682cae37f842f05ce09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ce44dbd641db9f1a219c1b4a166648e75f90cfc1ef5ffd3809d04312c5a70615","proof":"66de96b25f37018595be0cf7f34a601fcc8a41bbacff901e29bac47539a4e50a2c60aa13fb2a11a381fe28829f058e0c925f6edbc1fd564cb55e2505a817940fa86db84d6afaa37e089c23fdd018d92866c9a67ade0a6d85dbe2f10794dcec5c20acb0fe6c4d3409f2e366b80edfe6a27de52848ebb0f1f732906a5fbd93e260d656276fcc53f4bf4ee96903cc53d6c8e7ff0f902fa1c0337417fb086f5378056d7bc1d25e05e2cbb6bbe2f6947aa3809ff18e93c041f2ca9a865348a7b18d0b99592b19387b4027a6f2f94cf9eb923183fe18d05241513229d4c642e6101f015af9a26bc73b68c49b8b63b822c215771fa8cc82f2c5281ce259db900e05cd33ee90a3ee03dd4b73d2209ce40ac1b2ca56a6ec19cfc8beb79b36caad4e9b9b4aee48142cbe41c08ad836ae6fa5dd2874c30673b4469db057752667a32f363b032c17d3aac16eb325e5537d21e0593779691cc7b5eae7087837d1f9314999bd6dda13a53653a0ccd147397bfc0c8fd6098556d9c08a0ee567eaf88e807ef9636d924069b45d77b8a5c98bccde7c2adbaf3d660dd8f128fd7a3af45309ac9bce4618288f4b5e905817bc731d67c4be1a857ff089fcfe4e660c0e4a8693b5e95b50fcf7637f2b449e743a562e0059ecf9321cd9a5c1371a21e99f58dcbc033c854b0e9246a1c598e94063020d1a7199b8569003a11c274bae5a8d28c19ff9ea423f5e4c816acdb354bbcdb792142c86b8e5bf23a21c1beeca9125921e09162f882cdace29d805b42ab8dde066c9fb2d8f44ad2250dc0080ba954cde2686a2b5674ecc99c83785bba82a4808dd9aab34025d2ba8040dc58b9a7f1556b57943667c2a381ae3b6307f22d926a5a470bd65e388bb117612239947d7000b9342d734de08e0ffcf166bd0143a9be5ef0a061336757ad95a10a41aac24906b54fce1a8390d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2aaa85fa2e77e79aab7d4a174a794a7b9fe65276e4f9ee8d867fde5cd3bf6978","proof":"82b19dd6138af6b58f6384bea72c29f155a7ca6e1ee84f06a8e5cc093cbd180cbced2d8b8b8e8ba574d95b4cf4285a6887397cd3e648ddd8e8a6cea0f80bc872d2d7bb7e53f9e78023a49f54c6da4b6a557e824cefd67fcb1e13b524b96ab70bce4cc1116d07cc1a80b9ac5540cf543d76d32ecaf6904abf475e92783fec4a7f41235b63633434e80e7b8fca295ce127352e6de51465b43111cee49f1158390335e918334da214806ce2436e2c5baa7df69ee08b4c7ffb4dc707f5f427652305ee2f80ed794109ed44017e64de79608232128805d581de5d266d7d0c0eb80905e0de0087a34339715ccd3a29eef620c957dd1c0a6b2123bb9010b27f82cfef06d4b650cf00f04b851abd5f429beb5a5d943f73b9396a617ab2dee2cac2315f711ab342e2feeacc366392eb52ada6d130adfbedf35f4b3809789599bf5e66c945083b263a01f485e4b3f7ebd373560c4b1f901a646508c08ce1ff45accbc924504cd57fd60ec7c9b52424454081c4b0175dba1600598491373829c5eb564cdd5aead623984a029d401ec482d426ff92295dc2f246bf50b9c81b7b800866c7642e8a41ff35867865c35b75c2ee7fcd5f48bf18d8710f059444747f696690e3a63b0e619452a8bb200e496ce2655bde80238a6d75b18d064ed7f8e111b4302d3d2020db1cef26ce44067a09f628232c962614fc151ac128c82f9937a882274bcd316e9bb1044c82e002b324c646250420082c9cb121a2380b3846c033ccb489433ff0d87d52761f50fabf1c06619a1a8edca29175a9375e997320e5b11b8fd98374504bdd266df04278b64fd602ee482814987a1c663c459c0f9660275e654d5d499fa8955b405920de605432d80674922087afcf93e56622cb0507ca079c00e500e4b507468cd6fdf275fe4f57b2f6cde2efd3e9e3d292465c0013b9270db9f301"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"24b7bf9f0ff79c8088ccf331305a09d55f717f3aa2a7f739044150475863e445","proof":"de12a576a03896d8bd4bb4f3429a9859eb34ea98dd6865636b3e3d9585939e6aa230c2f5ef0e0376679534b9acb3cfa238d10cd7c34851e3df237b64427d05573a165f271175952da2bc2e76c4c4328c91285c7ee45c6c315799dcfa84d2526868ecbb99e75d15a347b7aafe9a29d5c2cd9da075a6a007bbc13a183dd9c4ca2cab89f12db1b999340992d89740f617e74f747d473ff57eb9a3cccc808fbe000282287cd592e2e19af445396ce1a5fb3fa9aca79f873c9df6f7f74e80d0652801f2e1d53b1ca1d94aa5a8b121d3eac6fda6f950a1a865e020763b8dee87c9ed0a5004baf268d8ddf538bed80bead8c97276157f35eafb5f9ec91acf9e13497642fc8ab176ea01dc84f0248727fdae8682cff9c1aa6c912649027be19647fabe36dc83d253610cd3a1aa54fc64c9c9df668e10a3ebb679681afff7c67555a4b74606b3d4ca02bf390d2c3dce121efe05bff7f4b5732d655aa8877b18ac4512f1538abd77bd1b293fbd903a14b17d8d2cb621d1486c385b8228a4542bb90604c32fb43a7583edd72549a17d856eb92789760844de2f5a4a6389785500c0f31a7e2d389f81d808b76102cae34c5c82bc16a49070f9a42dd1c1dd8a26759d6fa58c28983ccb16f4fc9bf8b5f262c25ba2ed545a66a8f919e08caa20421eb8224d0e05d8bc378265f81f2d9571b69f20790ebbccf1bf9ee083ab68f4631a4ea9c381256aa379ccc2f8b166432b34ed8d94d3de07d4ecf54e0077d4ec05d4fa863dde682ce27e3d21913b56e71b34bf093bee54dd8e5fbbcfae5a9f921ae1964b29717f5683916dd54e1b9f28cbfede2af6bcd2da811aa79aa5b9711699434090076112f88631d5232d6884f5244f3d526d381e86bda439d8036e020c02d8f32ee7b50cf556dafedd6b50da80e664ae8101942ccba3f5f1b2f4109e388c9d5b500c1e02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a4e2e7f4dfa8749d94f7f77500210ba0ba38188d9cdd02482cf6375e56e48761","proof":"8c2c45a4b46801dc4381c354884d539004e9ce8a9af9a2aa4aa0faeb4152d870f88592d1be881d47e1ebaae4f387965fd6bbbf607f7159e73579ee664cad8728a8590be94aa07e4472363e8a5be14c9a8d74f713a5a95b13cf2828faab5fb27044ee3552924976e44f96bd330a81e0bdffa495ddb78dffcf14075a672a97c5381810af2b8c589ee46af3f77d534f7a21f0c7db9dcfffad877e92f8480f62a500d65f7847139eacfe4b36ea82df6433b5a568c0b5218af8b4a48844eac7c62a0d91134f05c8d5f6eddfd34528a128f02befb183648e33b53bdda61f3c2c5a070dbaed742b11db53b00b0cfb64473bdf7ae2217e7c2e81736aac9f148331ee72431cd7d6670c187f610c2b1a53c78a52bb088ca0674b63135c1c55402b649bc9443af9b5bf5358fe0bd3ecf80fea3fffcc0082fc5d2b5a38615aa9cd89eae7b4369e49b260870d0206ac14b939944bc106bf57cabfc4f43ea3dfced165128f3168cedb2fd1dd2075dbcb7d475051c07e00a370932f2d94ecb7b5deeef4e2cf7b0cca058efe704961dc1700d6b1c23b09c7ba32997eceecc8384cdab5fcce3923434c64612802aba780ad9d2b81d2b94ea1907dc35a66b670e601dd406bd37ad44deac0c3c6b7e46b689af65c6b39ec6738512e2b2be23b747e8c330edf72998e2e0aff0c7882fccf30c50d1bad2bfd24e57ee6949a7d465d71f65a4669a9fbc65f8ee2bf7cc8a497f2bbd1ac2c3d3288a96c53001d35057c3ffbcdf31c95afbf1510e386b71b1004c43160fdf0b1abeeb0bfcc42995620b70487b17b2db4342259689cf46764c434c33411b0db1f0fb9e1358e8aa9eb4c194cde4220261931aa55e946ab720ce82818074494e2d75b5eb27cb82022575089fecd8d98b1dc0b0e058ef74f522f27b2984da79a78485d198b96aaf3f1bd034b3cc6d40277185bf308"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ac97cf8676e454238ac67fbe343aff051aa78d18f6ad620701f53b48be204b0b","proof":"0c6d1a73425859d300321bbfb10644d410330b54b385a593508f19e6285a2756de4b4eb20a337e90c720be7b93cc492ff9e4468001cec839637ec9af89d226640406fd05b0f77951bca0566c38364aa40e0f648e68f8ba9ed81e4bf75400f020c43d635a6765193ccfb1bc8a1bbdd5a3336c26bf31ac305fb25d84862cdf52153ecb51c038c3423fd54d29b4dd262c46cad8a3cf2913a2524f4f0beadbd46903a37333d81431ee57ce9f68a93b7f34306de70f82857f92ef5f138a4108f2fd0cfd8cbefd0822d62a3f08461b230be4b62a9d48360c70a5a63323300c3f81360b8a271e8d3abbb77d7bed81198d37936134cd17bddb6e2af6091cd26277d55862cead3f3601ea84ab30e67e6101afc0c7100dbbd464bb417f93d21fcaf4aab82724bcce25de46f68762fcc8eb9a28c63555ee735f0f35c38facca0517df0a913c0e3b3207c633494ebfd80f5511042b50042e244993608245993cfebe3c1a4b2ed06bb5033cdaa850294217f40f780d7c7c13f33fac02f71542aa585e2a1b4b5e649d573daf0982252a82715e4dc6d548c9fb5f9d784e3052b378b528ed91da491c0c6723530a6068d5fd1c06122a4d4a6199241f80aa26aa305a89e6accd042b0432699e8136c1ca297bab375c9977b8f77a4be86d207e8a64df524d1a253b7dee86374bb02ad19a918b16c1c8280200011c4e5fab0a5bca912cfcfd36b0e329b4f9ef130d15a6e065c23bc0acc8ea0780c685403ec0090721008d3f83d98952c05ccbe3998293f6fbdba5a5c0d7154fa87e72cfd82744ab6b62afcf73b3a71ad891fdbb43cded12869e74ddad25c8230deb5b2e6b305e428db2f1271efe1e3e2c5382f207da12bdf2967d91db2fca7562ec570e441383e6f5e7775163aa2e06796e02667dabbf581af8ac33d5e279dd6bad01563f5dda0a068e6224f5dfdb07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7a466a35fa824459518cfa6ec947400c710c5a6b26304392d246cb4e5a772a3b","proof":"36470e3a8a0432c44d07f0437614270303d51f3eee722af5d119e877611463466ecc737200cbbf48a1adbd49eedc0f2c89bc201a522c98087883d7050c87ef3d7eb5e334ffd22a2d20acd69abb4b49f252b9237858467ae6d2447eb28df30a7d2e7d48a38607ebac33d72b42cd03778e659a3a049bd4ce5ac4e727c5ab25c216dbcbc1490f5ff3a878f60a901f5a0facc9ea2e7b11085b2ec8dcddad41f47901f5bb73795b383d4334a9105b9c57e8096b56f64456d5584986d9262500109f0e77be0acb3e16690c1a4da9b2de309ebf62c83dec57df6565fa9c33d61959550d821e198d185ddd1b5d77f48022acfadbbeb34bcd8d600431464f11f3964916032a242d730ee1df787e72efb8ccca265844c95157c407498121ba2af2dc561e6e9e56a5c1c094a8a411c1867d45dda084d9f989a01ab2d12538cfe15de481830dea049cbf4ca9c08bcdf6f8e6976058fe98490f5a5cf0b93cbcbecee14af29611eeeb01d20aa4f8cf00ee2b05e6416e0ce06fcedbea2d1294d30941c0b7f7451d801e02a74037f153af85f677adf95a14f2c3af2cca48894bd5d088bf0fd042426472bc6d8c876e291f1e4086ee4f08ae9f54741b8145a77e730e1eac33f6197a3813c354582c180c83a972dee63e11c1b205c7292af96cb4dcc9d31a35d3ca2d909615c8ca5cbf2dbbc631e62e52ff58826c364d86e15606cbd3b65946c9b50a325ec6c669ca9ac79a5d65bc982663857266ad5f7f680f515ddb1300ad9cf729e85091549f1ac48c903f10c211fa7b913665f190953dad992b0ff98f2a046d37c60beb011257a6d2745d933eb60f2c9ee9c5b85ad823d9827fd07896b45a520230660cd5e01cfea86ec10c899a6fcbbc4c88e5a5f1fb3377922639e3812150060d594f21afba3a81582e6c468270d30a005bbc0646c508687badaa8a55dfcd02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a06e0f9c83b685884352a0ddd188ed81b15ba89a60678b21c41021ac9cc9a26c","proof":"e88348af0e9226c49ed89c6221eba54fd08c404a791d0daa8a6cafe97069f13e5caf7e4b75028451855bae6b8859e5be6425bbf2a75d115296d973655adf5c15fed1c6ffed5621fee3a517aa58c8d5d8529f7610dcecf9372b217566d8a2083e8c2a4f24b5c7f5027aef04e1f71bbc67a42a2bb79d8f6a16fc156b35c7d29b625d030fdb19f77146b88075087d1f127b6c4300f2080ba0e7a42293139ae13a099ba0ff28638ba4132ccc418d2ac514401e2fcab1deaf553dcd8a959a1a87d5080467d85775e767fb644450850aabc458058c28e272e2053cdfd1939d41dfec01acae23d3a655579c1d6cd1956f462d5aef435cf7c98db93845025dccc8931731e0b63b1db8db15cb173538ed9b12bd3bcf6da75b3c815f5e047d990c0f5a4a6c8a8d8d88744346b8871801eb4d235ede70d779b4b88a1ac2dcd7038d48642c128a0c78185cc0155b020aebf72c049ede1f4e6f9f277654e59daf8dd18bc2c83ec83b309e7e94fb14b0bc51f136bdd2572191bfa524e8243bc71fda730f801e12b2b4a3e4148b9e5e5933a88a0d8dc91a2a6d935a254626dd8ee480f793cf251e2c3062b2c4b3464491768291efc97e4408bb237737e6d3d6dd32b87f13a4ac4f2271abda5ee2e3d482caf51d183cd6bd4f693199801e24f8cde2983f5284e903ee37ea25fe1368775eda500ab58690661a0bacf4c86526d2a1cc3a47b17af3402253214b5b5e297163c8fb085848bdcb82d223f8c417ea7d54c65abbd5f48a11d819f05015567e0608283bd74bf5f2e2e9f51ccfcde40aa0394f51d37d78d00d9804ece8428b6c5c32b5c5cd9cd31cefcdc294e80ed63a1f57aef2202123110969a0aae9c3fd375342284890709f4d93006f995d9e0ac463914022648096e2048a9824564fc5808b69971b511dce181bfbc0d48e95fb2d0c9a982d1e835d3f0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1ceea54fb7dde5cd003273119b09404bb0b6357557d597e6fa3b801d70897937","proof":"921efd4e8fd36bee541b4a01e632f2444493237e14b99ec8ed12836390de9b0ca6ed239b6499d9fc9dcb1d670445190f93325451a8884b21815c1049259f26721874319f474c49820bc65983a6c194e63144ef390c0517d42e40fe3373a20d347a9c22d0f776d80e7f6d82026e40cc22b71312d3ff0061b17a0588cbde577b6e4995ee7e9e567b8dd26fe66ddff392d401aeae77f466f76e382c2a80ac35840bdc3d1f2e22dc990d7a63f8be8aa146126cf422d4ef19484bbf6be11c2e86ce09f636a11c8630dde407eb35f139fe98343e54e9c9e1ebe3bca8acf3643369b800f842809ff5afe1a97c04cc70f2bbb39946dc5d21a75794634ec750be6e00e420e8ed51ad17f3d17951cdeb227d21b0b77946d320bde306fa77b79d61b6a3b32a62c969245d510e33498564d05aeb131455eb53615951a7c02257e1b3abfecf456c3f453b6305d07231d64ddc13ec59a2c7d061efe8f84648dde663e054b468602ee075540a79a6e93eea3aec0c94b12d1cf730ee6d9370aef615688dec78f628d215a45cecb3de304eb7234a01a92b93c468aa7b5f683d71939b47fe416ff520388ca0dd70b6ad348fd4b73b7fc3d0aadd69cfa87c92b85db440a276f2faa248d27fbcc43c4908a55a620cbaa149c0ade42366e4b4d88c911002e6b29f0df86c44c180403d88885161ecf28bbcba09f51caba8c51394af4a3dbbe6f7af7d003e3aeef82e62fb6e25a7f309cd5516bb9bdee64c265c12d46cc1c0600b55b32342be2e863c9558ed393dcb2aa9933abeb22f4e9b87e51473483773ae70461b4556d4d1e02e91bc10eaffbf8a8641ea84ed7257ae96967cb49fbe0ab7c7ab427d1577b36cbea731832173b0ab929cfe74f1cc938257b1d9ae0bcc8718fedf568509e4cebeabde68801d8048fa7e04d9b60d88c1e0c5a5cfa7636f7d4c27b40e8c08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"567d819b56dae9961796df2d44838a15c8ce7de976171f87a7f836f37b83ce43","proof":"1c5729eb5d3def86b8d505fbcec3339fb5c5672242c777c29f9ef7b6a7f4e41e260c8a737a694166e5c44822ece9dc238ff398bb1618c6d82a3d88c67d9a8f165448bd497fa8e9b8f9a401496852751e34db8b92391d46e8b36345f5b40292440061dac19ea448ed2d412178e7836b449e1496b67188cdd0b8e161579fd9c170a25d41221a9413c8c5d808af2d5b6c1b16acc287ff44442ba62590e0fd5ab2084950b34e1ce77c302a2aea42ebb54facad6229a0b81127d4383b2293a8256b0bb3405a4eb19009e0f88c1c6e4a10793b57a9ac518c010f7e98b8ddc0d4bcd80898703a7d7c4f56a88827108135a65bafb1cf92aaec366409c551bb6abdfcc563e8dbdcc2b99dfb0b58fd32b6b1ec635d0a39f4af9da5a6cd988248091e0c8e785cb0de2ded508963872d71a9f7026e7c256c684bb47690e64edcd7268ffc1d306cf03c3e3bdc8e2a44ccd3bd95362161dca9b274c81780e98c3d651201b100351a5318306d02d7fe9a7994e466a053a588e89901f89d6806583457598bd3a728ea97842be158889bafb88eefd4e28d9c863f5fa6db6ec83db6cd0ee58c131c7362764c6815a03f5f08d8a058e5a8aa240584acb915bec9633b17300a19c1cd1c102fc8fafea73317823a81063b11e9967786784389e413309a0f53e86248836f7641df68757729ddf519fffad4b16b84d1747bf2a7034c048a46a8b2e205186b5ce71d30e398d16455634abc1c6cf92f0caef845f4b1aec4cd49c170bd109e0f5694f0c4c1eb48a943f39e6daf8316939f1cca0448a2a65febd85218248cb71c32b056d3773e1edf8a02ae3aac9533534ab05484c46da088d20cc41a0e40b6197027e60ff0dc8e39d100f3b6239447f1dbafaf5530e29bd15915c8d0c7163205de88cd02565aa5ff08adad6b30eacd2d15001e16659fda012a8c773664017c04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1a84cef5382d2abf2224d8a29ecbf36f782ef9228bf91361d3e5c2d137fde46f","proof":"c29b7a0fdfd07e2556d6b09bbfd6da19f69ff18f56ce88e9322c7ba36a11d913cc624c33d7b4cfd668cdbce25d5a1adef4fc2f066b82355b6e3473850adbd87214f197934a8ec45c4c0521ec090e56011dbd1409ec8ffbafe8f9f25cdcdabd15922566188543bb392a08b06b38802f356d89755edcd8e4efbcbfd11abcc0a34f4b6dbab4d344152cbc8b330070d4c8b3a12084b93382710b8e07303b4d1aea057b314040cfd368cd919500c5a42253eb455867e4210e10ddea41e2e1558fb408e34a12e603011d41c9385f305d891473833a6f23b71f9b7fd89fc9c57c77bb0b265688ca7fb2c5b662efb066e2cc30edc62036faee4c4f4e594f3eab7fc5283fe05d0fd295e8800092f5ce9a01cbb2407b0957336fd1c9b583c1ad6861b25e10382f4f0791a15cf664039f39dbcc7e7572a652d1aef468a58723bf8ed8cb075a1057561a4c3474b5cd7c8d452f4c6399194873aeb624af69c294cacd482d6707fa77ae40d70a7530f8cf7544869140cfcb9d349ee82e9bad216bca49b12fc465e4209d1085dc3057b3dafd407031fb1ad5c31d9bc17e9098d58e8d33b59abe065430be963d99404ba98c9918ced5b5731353a29348b0e689c8268b5c4379e92d2ceb286ea0cd0ed7dcc5a741c2ceb19fec356a54ff4ade1fb000714c68a2851c2a83dca8fe553ffb61a51a63a843474a570d925141e47de3b0944a8292251f67006fd244d515d55608d2224afe485715cc881ab2b9c923a1e366f1c1bf5299634c068ed792f5ed2e97b7c3f902213f18ce9db87331cfe733a97271a08b186b5b2c56fb1950078ecf6bfcdbfec3f7abc9b333744750a1319eb9d56aaff5094d3f9213b830ab48a06f38c5786b684dfcd34bc749cd3ed67c00b23cc323af01680b39a461eeb05bd1b23de82a7f527d79aa78e8345a3aab7051d08f88c197368c0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"42e8a8c274b2d587a5fb9081d5c5a64700e62efe2e17dbfcd00be1ae0ea62d18","proof":"30644b2dbabb9d8bcef4dd525ecc9dc941469557ed3db80401b16e705e5f74686ae851b589752f66167d8393a3a2a85838997560385bec938a89e92776475001449d31e7f3ed609272b42b199aac9204c315f885a5acd14a69c110a43c59855b3a1f74b4f7ae39b93ef943ec8827e400893c7c86d9afec855a41d53b590a79575d4660d22ffb33edaae111171fa4ebe1e0f0225529ebaf17c29c4abeaf4ae0048072ff4b154e06d675a81b4dd26daf5b8a89b0605e3e95fe7b192137dc8674047ad7121aada026d8bbb32585e31763628a3f965dfc5414e373a16aad3035bd03309df51809252b7d48ddb5589df70f30dc3829694f809c527d42c1b453fe6c58f296d57f22d0a1e3c3251fc0944933c50559bbfb10330da331495eb4be3b1f72ea17ca402c0c7cd6a780e9f4336c8ffe6b5cbc45ab8205d5e3615f5f7c630b67da7b2ecf575dda95771b87ef3e32766be9e44fda1d0cfd4998497e395d0f1529527501a41fb628841fb9bb5ac57bba95a07d31b8714e18109a359abf4753910ad6866071f7d7cc459aac28c8fab9f39e91247889972bd2556746fda6a109eb52862c279c505d7d30e17f97f3f37368b5884e58f9cde1012f073dfe5faf92bb0f0a8138ecd1b717bd7e00255c1d07fc44214bced998390ebf9778c01fb0a67a48cadba1de5d1027afbeb1e64274fdf522eecdecf1cf80d05b7e1cc06d87765046306dab40e4c09a15d0e9914b030a87c1994a00b27439c608cc7ac59d12d667389a692e1f3e5133b77bb3cdd1707e4d4a8d27add54c44e9aeed67a4ffe2de3c724ebd8f97b41f7f3c89adb6cbc1bc810def13d912cd429e80da0d7d830cb32d70a67dde3596dd563f8f7b6d15d756397727f658d36b099ec86dcee5141f89cb0f851fe8e328a3354584ebe9892a31c6e86b0c695f108393b3e932ff89d4c89b09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"524c6e9aaae9752986f16dd54f3c0c757519dc98f00eaaa7b37acc1a4d3d8353","proof":"4454f83d8699ee4b24298515d16980962854a671f5cf22f576c114adc2b81b4700c1f28e8ec91ef92d7a3cfb4d59aeff011b1e8b277d5b87b698c3b6eb95346cd628783e1d20d64eee768c95780878c87558077aff74d6b89fc32f4a1d5f7d02400649447a172ff92fbf4542d216ed0c42287963446934cdde56b8897200550ac63338712441d828ff762e2c770daaabe615d267d853c2ae8129c431fee5e20a279c9bc7e5487a5afdd69ab19c12980ea192021ac553d409bcbb8173fa88bb07c00740dcca3c8f08f561d0abcd111036399b22b684f2cc0354176e9a2f794f0c1887a15c15fd73ddcb27072eed52db7b6e34179af5e9c3f5729dfe3933b8c30e186a76b9dfb2c97496ff1f282423e3f5b39f6db86742cc948152150dc1c7452dd0187f1325bf65bf5e1fededaa8e6966bf1b2ff5f4afe7a5055e3c3d78d63d10866b4ea173f09cbbe76d8903a24610cc664f315d20f832d72c9e33d51ad2e958c2702353a5d821fac2d0dae5ef3d605bd6948b636d5d2ff35160aa0e09579a0976ef493243a5df29411197e9407472b6689742d4b2fa47b7770c126ae3c3465d7ac4426d0ec059031f1bec9d792ec632110887c9fb2e4060a99ccdc48d4c6466f6f30bbc2660795ade6ab0160218148453ca1ae0d099e042110e302b9ad70223b824f26654d7da8baf8358e8025fe229c23a8f2ecc171d6613da76b66e94e011526d9505c85383b605cd39e3171e79aee114e918cc38c6962f2f8fb64eeec74f4841bed41a10f3e10fad741e2a905cf55b81c131f0aaa60f5e93fbc69285876e720901f304351b6cf2ec7d86afb7339ab8b5c2f6a68cb92754228ece147c8751dea50cc1ed47fddd49f294e46fe6469545e1ba24a856c051ab4f63c91ec59a0f715148330c82c29caeb829b928c38e19ee1e9015fa7da8877912c9d00f86a00f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2c2d0ed234d0af1ddecfdb68da09bc338a6508276da74eea1d03a0c9eed3c954","proof":"da536aab63eb88302554f04aa7b5ad4dc83815ad0d5ab5a8355255073ebae361629ac4970afcb16d4205d842c238ca0449420b2328c3924f935f8232e1fb060e68bfe0d274d8de6836bd750edada6db3c615c4f56e5f7a97736a67613f929c7af25cb931cf82d2c0456eab58727f7e6b738a0b16899c190198a7f3bf6de04515d4ccba9315071669f1e2b3e382fed5c217308fba10097277ea079d8420592407c0c41aa9a1720220de6d4dffe34140a33f2a7f380ac34a80821b750d47c44b085b2db9e84196dc5f281e07c25bf0f0682ddb4cfec5e13a72e2e4721af6f1250412c0accd8c57ed772526e733296ad75ab84b0cf7bce8ef0010e25389fa6aa60d5078f1872585a86559bc1fe6d0d7914d6b274596dfd425a0d6e05f53240f5d66e00e9d913081c42b5eee810c882e6c3d07460338cbce88d8d1fb9a400c882e585877d4f1c1688d08f08acc71a9729c33a86660d9872765d1afb0855da892d64ee4f8af4fa857bce8587375245e4f128c0884c45e9f845df0d0a52a2a3e9c2c2facaf389ead3b0e77210ab61c531efa5db405a5e311169f713679845605b5e8710ae3d0b7436822c9abf1835d96cfa505073e9b8722de961a171c97afa344ba2cc65eeb4a98dbb5478352f12b1993ebd2d4662630c21c95faefd3e050c4cb1a3ec0ed1c02aa8fd05c5d6bb2efac77c7a6eb3942c3eadd4063185de59d4592fb38cabe021a3b627021ec7a16505c4db3a67568688aa3edd28bf67470149f8d151e2852ecf152135f2b902e58941584480ceb5b1c73366edf48e3e3040ed547fc0018183758327b260497830774aaf054e9680d8defa9cda857edad6dc6fbaf3e347135c538e7d2c917e5be7f1e7c8c999cde5f94874809fa819182fcf5a00a4c05b35cb93727c076e290269c7a3d046d0ae359a67712a0c25de2af5e117a99a908"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"38daa20c1a156dc54e270eb5e614083bdbbb6dda52483cb0ee5aee324d3c195e","proof":"dcf80d2221f47f829fee87fde98080d4ca2e1a6d9813292a5ebc0b952775f1753e735818eed8083244cd785027d12ad8a82d322fd25e4a2f6642d1d156767866268d2ff169f0735a1a854d8ca83009f935d977bcef25a57d451e172dc54ec42d62f2f3c318530debe8006fd7480c6a9f3c76f0e57851b9c0627b492f17e847694efd666489cad0613d807524a189a76dbef0890eecbc41128e3551031435ca07745085c7da745882ae03cdac8a74d6b21b65dd540027aa38345117bd55c51a09a89baf8d6a34cb9faf112c6d16a39b5328aa8ff5c489a9a8f523f7f993b08d0160cad89ff0322099d455ff0cc4b3eb7b4dcafe127615de98fde255293013df3f1a569f94c46f9530e829d9865e884ac030bfdaee003d02bbd1406845bac2b46a6accb33c677610d4e507eab12da0a0c6000296bd5d1c6289e967a4e2ca6c363c289c732ed03162ed8571846d1169c8b43d2a07312e64eb7be77fe7af6ff13b32f6169267ca5a01b5bf6e841d53501b01622478be27eefde038b5737735b96b22f0441649aa1b0f38c07a1e4711e063bf06d8a5cbff06570e17b76741e3dc45424895378a7ac52da0c0fb5feb6f297e3907638ac9866be09df3c24110293c767300dab00bf58a859a81900d65d4f56919c26dd6299508b82d27994dbc72b20675a602a7f084280b39967e5c1ff889fd41399bde93470d47350416ff21fac63431d240c00a7121bed037f5b2f2597caba7ad93ccdf3fa7ed996c1cf414e40fac2eeed27e7523d2842a50c91a11306492026cebc742abf623c2b7bfa7e9802e1a7c9c8e08b97cfc6ebee3a602640e172bb701b5cb7f4656d2c9a8c9e20a53128910b91a63b2f967bf20dc9c7843df914551c14c1061f851c30a59222c9048984904582fb4e1078e8f14a5f809bf7c11d53bd07d6390f5faa4be445d9d89bb4b6f05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ec29f5da8db6b72dcce3b989d5e6ae10efb9fd2c90e3f19b48c2f4ffa7156b40","proof":"ba793da4daa244b87f3a335a81b261b0d9271bd42053fc24cdff47020718d2788c2ccde061f27c37874d2582fcc3433b22e36d56636f07831f37d837118c980e86220d67e01c4e6345b3d282c84608170b497d82d1d3d491af44b890b910c8089e8ae2a5ffcb6d8a70efe8acce06f2e23198394cbf7a7ef7a0d9254d6884284cf79ca504ede57c25f564aae653f779e163a2a48b0232184835a0e8de6d80680a33bdceaf61e95cbcbfe730f84441add63e57dadf37446c3417ccb7aed0c4e2050b2d06a2eff2929cc1de88d4ac4820b3b5e7e840c236defd7db9d3e493519109e8ddcb582aa3c61cdea04683fd44411a6e538b8dd8084e6c442d1d460e2c1660be23a64d1d677d612b9fb0fb70d6b4d5183053cf2fb4c91a7fe3e80f7f8c6c28721549571a76d208408d97725b64ee3df33c73a467949f928c40326fc5ee31013c01c09b46a27682ceb6addc58bf5631b52730aa38ab4dcf3a1a275ead60430828884df6e9c916e3f96d838b2e305b8b768ad9f0f66dc41def3c9a9c47f4672c864fa7961066f0bb8d50f897a5a72437705d8bad2ad45548f55165d625133d597856e3f89cc17cf30f90b4a2a5931554b61834727b34d71670f88bb38ff1ed48601830b1c1d4638f59be0a1be11180bf47f7de2368fd09f92fc7381bef75bb366e4dd0ac753b3d78ffb0567954c2c243b5964aafe360afe79fc5094933cc001dbc481db29d22d025978007ba2578287982b589baee0a978461be6f190d216d2042788ea753c5169027ccf212ff0bfc00f7a7ab65d829e135248aba9823726140a6f1026d2fb4b64c11e956d0a881ad9056579e210b7ad207da3e0765054e6349aa60ef29f6a58064da8d41bb33064315e09184393342240ce001c5e5c9eaf20cca06ca82dacdfbcfd2b1e1957766e0c4348a895fcce9479be2780c560d13050b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7a1bc1f8222b45f24185290bbe7f117e7c59f47931eb8b95c046325837b55e5b","proof":"d001b231cd556b98393f47aec06c8f71cebc31b15e14c4209881bb3e886c54100e86ed9b8aba925e86308bc5ac6e04a3cf85146bd338b04eb21504bcfaf8c0642a593339368bf514db09060a8469e2836980e5844c4d5774f7ba6b5a516f8144f2cca3e735397ceb7cdb94f4499d3f19ba7bb50b89dc0d01b9566a98d0a65d6c4cc9e855df73d93e972093fe5d59ac1898fb951a8d4bede6b8f1da1c8ee77c000d76cc6e25902b5328fde27f9e3871b2cdac73a404dc8d11a5db7314403a0d03b2746e244b6e93d6f79a534687d1d50747f3a7ccd8f4607c4f9d4308807dc50fb888923f6df6632d7e4b2ca478f5d7a6f28a3fba1bd331d254970a7cdd2a3f47f297072d66431f1152029a0d4993096cbf1bfb3a2ff7a9b71cd024f290d0d36db0b57996345a39adfe24cae96f32d95d6719573eb3682b08bc92bd001c33361590768664a548bdba255c5ea83890bdd2b618858a1414a18e2f6adb7372d5957d14331c8d9edfb52bde681a9b228ca5d14dd98fc0ee4ec73237a0a5466b4b57291e8196d15247ea63714c9bc4e39da88d47fd64555dac97d93908cd050a56ce02eca9f3687da19f0c13d5297976c43a7f17e2bdd9fdc35aa742e5d24d68af4f6b5485d7e11c364a734a27a874b4401db54a615a0ec7e9a8cb4e6229482f259451768728e8aa612c588e48707853826e97615adf10adb66f2178809e4b7f9150097a52aad4a27b8187c00e6d156ce5862aa52fd86671e1e3d1d141164301a7611f98101c55b97f241e68cdddde01bebb4daa2b44b087f346b674121e3ec0c3364be80fbce83ad6cde5d525373f7b682d5255b49d48cc81c9e6c9b1c51876abcb2b754dc3df9ba352d2c90b047d9addc0280cac9d4212b35881e2844f21ef6ee603d1caa03c1b7473d552866cd0d047790bc918be87f5475b6e9eb1b4fe854dd902"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"624da9a464e8a4bf03918826b436494eb268cce957bad760aefbf2e1ba25475d","proof":"40119c09ce0ecd610e2d9c574bb67cebb30f1718201318c6c12382dc47d622090cea6ab789516da1d5e81085580a92e4a379dab125a02d59104807d60a36ce63889801c4a4f984e5b7ccc389dc61440f71a93bdf71940e4dd443b5fed165146b420e2c2f25fe58a898d30c4905fca80c865ee6bf07b822a4a66b1d9b008bd507e5afb25c9997ea7285bb4d426864f9b28b04e6e5d56e6b982bd9071b926a5d053ed01bb46f25673213627f9adb0816dcf1bb8bcc069752bde952556f418ebe071b611e58b0671684491c2603af2d98a3f1f23ca9a523f8dd192c95b23e999104c4261109289df1dde9e1c61361711eb44497139fbcef905507451746098a002714d58b5bfcf2d638db8f2fde7112e2e8cb1ff36a4d94d0fe8af14399a17f1e56208e4a57879d348dd722daf11b1e8eccaa218f663bce6e5496a3d828ae690c14cee22723144bc11e538ab335903ce3f095a52ef578e0732539f35f3d9e091b6808678984765e86fe8b41e2e008aeabb393fcc2d427b2add7f616f59aafe98b7dd69496e15a28464443f9272ecda0be52a90056a2d1e44279f9ffe31521323071fe876c774f9ebb7c20e92d88a3ce0c11807fc1804bd45b81fdb1125f6b7ecf1f50611d04eec9ec837de01cc524a256988353e2cfe5e2afcf12a23ca0db809e4a86c6a7aedee0e3afbc88bd5098745c5784f264eb10de3a73197e7af79895a032824a4850bc6867c2b7d1b18cdf783a9f86cb48d338e7b47d717b1685614a1719a859ba546368528d7f9e6fe2bf4dce892b0a57f4a8d454faf625e1b18449c1337a4ab69366e7efb565450fb94e309617db4aba37ef830d19e5f060fc54684f54ccccfcc6b9cefdbd151f913e4073dec42d44d32d2bd376bc1a4fa803a724b602f7176ef07002988312eb71ffe7d698caa265e93745d0443fca69661dea124f03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d08a096a9df73bb57b449645d16433ee50473b2d5f937776c482e04650314646","proof":"04b6a1d56bce7d3111735bd2d225b1d1f9179e7c95f5ac539aa1028d6a56f90a2a664fe1c7f812aed3b8b12de0911d7174f53bb37c4aa0ddf2e7e93aedbb26665a8008769654f197ee92603479663da8732e248c639602660c3b51cad1522f36883a0474f7109b2f561660c79138ac8ff8ddf8bd1584e35771bc994d79eb932f352ac2bba034f96a97a0dede5d0f2da7dc43bb40310dbe45051d51fdcf3c560a5ab65a1d9763218609498c2da3f09e2969ac64bf161b1b8023ffecdfa71395031909a96e6371b24411ba5701fef885cc302d385a2830e2b4dba9b13e8d6e8403f212e03d5b239ff112367d11675f333d644c52c76432bd6146e459ccbdaff00dbeed6a13812a8a374a7818ee659dfbaae6e7001e97aebc8608cfae8f66dcea5f1ca6718780f9d0e7b01703f6517d912cdaf0705bdd12fc22cc08f4f573215c6428d7c9978017ed402e90a5b5a2deefdbf7dafe4c23a7507df592e823114ec9160cf320919bd03275337bfedd0ef4fd0a6c2b82c4e2fb549698d2a1eb53fe026deafe99d1863bf0a22f16bc6a029becba4c13ac8e38734edc19f0fed32d4a3663107556b335cf737f2fb343f9998c63690cb57bff84d64459b82819e640b9245b0ebbff9b13657ea04b1c5bc383e00633fce8d016e29fa23bc6f808e2d70329567429f668743560ed709567352ad4a2ab9b31e2753f2ba9a1d60f3181281b185a6aaae54e8528bb8faf56e6b721109f8c940d943bf2029e085e23f39beaa34d3dea06d0f36ddf51b7c23c7e6d77ee2ddfcb5af18c5ff8da8d71d8c7d33eb9e11b90a827ededeee0fa32a3bdfb005c0cf42c882bdce71bd7b4068820be97bc6231c84ee1f8c1a6517ed42cb01f977440763423a1f575233ffd8c622ead6e87bc08c64e960ef5ace499f514931d1f22e787833b9d67499a35a8904d00c56da0c807"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a0b99cdbbf4bfe889614a68cb2b1a00e2c3c292f9daef96db8536a14e584c153","proof":"ee8f80e3eb41f22358810b631c3b3cd34f627adf2657f9f892cd68e55cc6d0206c25092ac0d3e45942b5dc681d8a0a514ed634e55b7304f6272444e05afbe5479af31f6947d2aeeb65a836c77af549bb09d687ffd23d1c1442a6a4535be4bb6442d281a4c36890aad50b605f4ba660b37d126a46c2fe180d6256e745d21fbb055755ec420ea6c6871856be74cc87450d8c4b5db2bc7ddca2817b41d88d56a60b500cb2b5a6c2acdb3e59516a10024561ddd65d3c74fb670a82ce3e0f320af20823e8dcbc89b860b209a73b27a7ad19d7580e84975f97af35b4a1b86754abf50250c96861738eb083531413de5dd2464737af137f13097ec744a375579b9fc9254aae96cfdedf14e9526c21daee9ee853ff95cdb6fcae52c7ab0f49a5363a7a73acb9a128a553a1758fd74132a4234521e0602d8ec083eadc7f0061a5bfc9c372a2be45cdae160a7ef16f7c975f3d00a7df2ed0abc859f0371ce4db159f56f573c272ec0f8d54458c9c697dd495d017b87d140c04be0f3be27fff81db0a7edc4e068427a0fc00787343fb94d77706b5f37806a134587a690c9277ab65aba88a106ef5e5ae6598a9ad450ea6e18448aeab532963512484b658f145bb9b620f5d4af6d717787326f92e2efb85d72b4c871a74488491ac1a0240272aa91cda60695784a46eee4435864f9862db00988688faf6a95a5666a02afdea90e86e3f24cd341c368b87e2d0b06287a93f2aeeb3f0e82d0c28c631fcbb678f41495d6978413c246c34e440af1041228529c6f5623343399c338cb7c13f2d59f0f69c40890971ecfd31616d39aa9a26dd3479f9d24753050398a4657d853d23de535805e25043550e95e69ff3e59751335c6a1201bbc48345aa61ba40a6fdfb13d3b79ece7906b4adfb928b1797da941e408e9a52328861ee36a1c033972484813117e5e9e501"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c2eaeba1420f6c6cbaae6eaecbd960563d7bbc16f8902eb5e4ac0fc6c592e42d","proof":"4a91ed179eb5abc64c046de0425a06ad6f8c0093091629912e86d9fcc17dfd65bab1d20ceae21548a9d5bc80ec8999263f7e5fcda3de59efa964d7c859251f713cac18d6a8f41149270cb8b165a76b59d9b291dbf1254f34a59382aa8aa75e0094b8125a4c0c5de0660456d010719af7d9170ea8b6e6b03cd5d2d477567f54725e84d0978b268fbf113d7a209e1886eee3720b946c81cf561c49ecb9f54cc4073f5d08ade07bedffc8e1a4e162077c9dcf45b1d56a004e8403f887e751f4bf072b593804f4063b24ecf12cd022b9846deb94f4f0c495a1b8a583365b720dd90d2426b101bdaf6f0c047f3f6588395d4c85b83e0f7e94808e456348deb9f3cf347eac94fad53173f61432978e455812396ece7989443da781434b3d28411b2b7a408b032c1d376c2588e63c795994babb481d0bd18a0794c21aed659e49acf05d546ef49c1f6b8b8b3e229d358624aaf050f4252ebe3e6268860de849a41fe25afc15f095c92586478ce1994025a8c9aef08939be7db1f215dc948263a91d7e327ab9750a8588ee0046c4661cc5a19126836fcb28689757ecf546ca0a7c251146583e69a4a9b724378ce55554895981d7fbdf1648200b1bd62f456b04b17992122245ae6c3130087c375d37d67e579458e6d43d6521c75399b670ec76ad90bb57bede0a7a6343d9bec8f192710a90cdd620fb0b66656788e4e9c8c2b9fa5f302cda642194efa163ca196ee3550fc8a727596b43c1cf2c11ad5ddd8477875d0f0faad7e0ec370ff62fa0a9d4ebef2aadcc88ab4f5d67723dc2242f06c34d1f2619582b4665244f01c7a61b16dec73c491d96ce916effadcacf39d3e796a30ac2571e5ff26fbaca200c0b20ef21c3d43b2476a927dbc9cdbb4074b878061315ff0bdcf110bb5310f272c124aa455a37e682cd459382e2b2612847d047f088372c03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"acee99fceb36193fecc07669abd8e0813f8375d828f31a3913af2124af948a66","proof":"b2532c8bf6c5311981ba6a879552c9775b2700cfb7494279566cb675303f8049caa923d413480c0fd5e941b56fb1e125d116a2c0def6f00f8b1c34642691b36766a3fbf6e686444f5198523ae5bf7cf5864981afc07754884a307bf59469706c168ebf5bc552742126db53e5adb5a1de705aea4ac6506a0fa6a5fee764756b06d3ccdefbd1a99a96862b87c66fd5cdcfd84eb33ced5e52e911a95877a3a0180fe5298586dd5ee5746e358e465891ced7d76ce479a3351822513cb9124c72a9025544d83e0e4e05bc97b8fdaa0066b4848919c8593b4406cbe972b0e22cc2a10a9650214e11c1594339174fc18f84f245eb19c923e283de9c480c04ed076a1725c6e76bbfd1fab728ef6bd1bf0dfb533e5d216fe4ef5cd1a130436e67b474cf0a7a19f0d54b61dc6f33a8fd08e840a484674e78a6f80c644b64b35e17025ef02fbc780d4a01f7a6be9ca64809068c16ddf052a9107d8f5c8e8bf639663c635c7048f84cc372c2286b49a991d0cc3373bb0c0f50f370ca439645fe582973f8c54c521cabdde6cfd44d2d9a8be2ae29d7550ab33cecd2e2b6bc601e1537a149463930e583dbd4543e795998d61aa4100a56093e6b66802355f33a6a6bca1a0df03d8a0c91a4ab1818de81a46e8ce796b2c4c01dfb0d8a80d0ab0ade2a860b4a671c607cefc099f529d9b4d74a5ebc64b89c1c1740f6be0cbc6b1c7c345bb4c73b7b80c20b30513f31ca0a6c2540e518d2ecc1c31a24c590d55e23cf72a78d48415ed0e092a39796a60037e62d0c1387bc8f11924d080560859fe80d91e746ae805ad6d9d5bf77706f9f3094e480c1996f70b613031dab346eea7710f1dbf3b63e4a0dad1ea9f0d4812479beb1a7b2a8fa8b1d077854f64ec0bfdafabc020d216c0193b2e6069f72ea35f1adb18573cf7a228745eb68f1ab9127f7d0be28cdf68b0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5e711d1583e11d70f650d7950f1af070f006ae9f433966a9348ddb8018d9b420","proof":"4e145976f77f5c6cd752be95fa8068849f0732f4894151fb0c3fbf1e9d62aa1c72bd10903a1f96005f578be25723eb5cf4273d76972b905843bb5cc820e9e6606efc60becc3866534a882c0ac4f139dc68ae3163932738118d7d5acc3d298e346c6509183d3f4fab6c567989c687799196970628efa10eca3303859909c59c45f43c2bee7ac4c94720896e4602f0f6559f0228ea0bbcaa92ac56ea1090721c0d8f5d6cafc3863cbe5f3ede66d1d49099b18e10ebdf4a6255e8d16c7b34c78d0c887d99fe5a04a05d74ad87cd674988f3ac81ddac16040e673dbb48802cfa4705f8551658b406b01617fcb6d1c9721b8ef047bca99277608e2c6cd73a1504f655d4ac11047db5457a7e72a00ed1f4b0d0b7b9c45aa2c8a3e7d94ab4761df0bc2e047d03a19e959026b52974c65bb561be165870e1a610da2d848ec49fa4809902062d2a45a326f726a93ea1d9f660e38ac204417c9cff703a7cc26bb6c087ca646649b688a5ac9eff280abe860f14d4399e3842af1e72b95e63809c4954bf9f73a0e32d3f4e07c183a6b3c57d375c8c911936a24d44ee22d217aa96067fb10856bee9e75695c5b9ac13cdde62169b22ca42d3bb78779b538843591d0fa897ca1d9a57a960a3513da465299b06d839a14af77799353b88e2cecc3463c998ad586d2e424af0c2d69346b7f855fa20ec7533e2abe052c352a34946d65df49430a435fac193d30ee1c181b8315ceb3c259423920b0d3a0c003e835ad9a1bb9d3c9b25146d4d7b099ee677392043139bc9e05239f90beb35a1f6daeb5129ace35688217a2f65a38a39a698dbce7130cc5f66cb87f9904fbc89d4c467e2c5c0f84f037e0d20687f096e11a68cc7b994988a8384c8cb10cc57acce2a35bce509952b4e005796def8d9e3f57674ece0097b85a261a0b7013a673dfd831cb8bd7ffc707704"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9865133606d2d311f9b3d7951bed9122094f7bed51e1a5395b6ea14581450705","proof":"eceb45ee3ef43e396e1893dd71d4ffe43e802d408b681cb7355410c624b5ed0ef042dc7fd805365c4970ebd11d669734bdeceda8a09e7fdb9740d26896455d4baa40a898967e951b6e67af485538c710c62cdd879e55aaf3596beb7e63b02811d212d18013bb796a11320d323fc8fd310204335d0bf28e6190950cb6342fca54381c9febbc70b71529f4403a71d0f23584de2fc7e80b5feca9ec777c61885f024e7d35929449d1aded3dbb454a1188d59106cd0937419e61344beb1e6a762c05664364dbf8875769287e801b76362e832dbebaa2827f4fb81edba4383e0ee300904a98c834784766e22bb85c706c1e36cb93846c8159060a317602ffdbbc7d384c28ed75ce3a343ff776721ba79fa976ca541079c0760a0332cf35ba0c71fd71e2df4a4f23921468425fbc549cf10f654cbe4fd8a071c367a1df6f6482d2e5053ef7ef9289d000f9048c245423914b08574b0d289b463b45d86e5df2a13ea837727be3ef15257e31b267753de9ef99d787036ec9e059c35b21377e9f7806c729b69f03c76f2bcf52f2bca35f8e99431720c093f2151943dad71adae7ea64c01612030cb548a64b0c3ae5b194adfae3164c6b4754f89b8bd643165a290c9fd73760d676f51d9b0ef4f165b3aed9e7cfac25b5e53a740f68730963da088512b4634013fb22f57b253a3deaad396f67c150d0e024eca2843a23003fdadea0ef2f5934a1bb5aa77a83952ae9c54ee609b6396593052a425c29d00ad105b5cf3ee944da1e539076d53960f49395612413272e33873303aeb022d7a64a61d182be7d2182b42db99518820cc5ff96c9e9fb45e6147c37a8cdc94f5d1f6eb3d86faaf845e464d27ef815751061a04b3ae251c78bf86babc9b42ebef906c5b65c3abff10df007068e375176652ab2b4d3bbd146b768d8683653650f5c2a9bfdd57e76fc05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d296c594affc5c496ad68b9004f5fd8ef941c39f4ac69d4a83e4a39efd095b03","proof":"a6cc263eaf9cf2eb9db29bada83f92b51c9e3bc0dfaeaf598ce8281844f2a3785acbc77adb9b52dc12ef0b1ed91fd44b7e592136416b042050206464cab21c1230c8297b537bc5508a5418d572c28a38c9daa4e73af7bd7bad04280ca8956e45e807cc61f3c87d2a22e42c7d38ff18cc4a4be5af4d6e3d5e62d868e9def4592fafc5824b13bd3c12ddee846fe1d28ef801f026b583a4c87ab5b7ffd9c7afd305c459928fdd39bfb39cc2ca5ed7a1463cfb85339143b16dc621342ac4811daf0a9a109f138d5cdb3b4ed3800971e1bd4233282f185aa6e0c4015d140f8b0f5108be6d7d30e986e0146ef27dda12437fbb80e536cdbcd8eb7b6170f6458088451a56f89079f9a3fe30700a326a757daa4ac720cdd005475a369f89b43886b7e208cc7bc39ab88bbd115ac05902f720cb98f4472985f10f8f64fca86ae572c11b32741afc4270d28a6cd13ac2821d1b4018f45b4adc42ca9a348f74e11e0974c811ac97f87cbbea669125873ffae77699b12049aee9df96daebd4d65177f42028019a1ffae01ff1a7799f6fc62b098d0d8701bb862321d80c5f2650bfc340d3de6efe17cf6fbfe6d74b669f3bfeaec5833179676cc3619a591855f51b2a5f3af94bdec56122cc8b3de260fe9a9596721a0d59fb9fa4403686bd03601395b451145a6a7df8cffe1c75c7470aea955588ae6e3b6c7cc5a40b53e8753ea35717e23a7e6cba10c77ef25e9301ffa6e3e4106ddc635609a41cd636958da6cfddf77695059e8c4d20ca0e64bfffddf4814ff969aa76d0105b2b3aa6c69cc1f66abb2f6a799a32e4496a42b1dcb4403067bb4858c51f159fad10a67159ab06388930ff301d366963157e22857064714eb6457012bf34df0c18fdfb25c73e774d0d50872b024c06d7af6e4b1e8d9276ece79134b1aa87756ad5d6f5c5ab0b1ef7326b0a3c01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a4097f22ed18365629756bcc15b86caf695f6e7ee1763d0275feabb61fb37d6d","proof":"044450e1c669871eeee9ae049d1dcd947712d01142b951ef464d35894b96732d52455f28c7252c496764ea71c80de3867801265b385788323fcde3aa3a1416440a6017c720e761185fc36c40c5a4bc4cef63042dc761e5fba217862e5824e516ee450c06fd8d00ec7c58e5088192fe89f57fc2b6904ce5722833cf8094725d6976b99aaf54bc8788909a0a10f57ad7605c5e88505611fe9c465b1198808c120602e10b6ed54ceaeb1226f2eafeaa68b016a2ac7fb85999cabc95b89a79dd52032e3f2b5126d6ba8b66682c2d42c237e63f30f9dc4f72273833008db227d8f70fa22b8182caab2c8898373a1e1db47f19f2910892a9eea84b61446b8afec99b09eea4d6e5ce482c51689b0b99237c5ca3c010ab37492394e71553078471e48a6bb2980ce5c8af05d131d45da8927254a9bac7047680f2945a917db294602bb65c3462f16ca34ed683a3e23e3c9bb67169a9eee55c741271936c55d8465014025ab6ef0349faf9dab902d60f063ee4602d00f9eb3205a466558bff44c25bff3248a0e12e62cba7b6573d157004adfdb43ea9db7975afd8be5fa27190d3457713523cb03ebc40cafc54a91495457afbf504f659c4cec2973cd2f28cdf9d63cd99633c02e0fd1e12c086ea4d91869287afec34785125681cc1ad058e23f3322bcf7ef2053998656589a367b4e18546c64429e8813a3e6e7abc9d30393fb70450a77ac647a84fb636a3a51b386887195010ee348b2d2ce928553f39c172bed03b186910f766795ef8e8d11b19341927f13bd17f1ff157351154c62326e9ae37d6260b9ac86b5ce61a09c4d3e88162af2f49c0d900806e5166fdc887579a08f7e76f165ca6992d255af03c2a514fdc2091b8af4d1ce4c630842090345b45c183579607866c9ac5c969bc584b1d8c48dba4b16c1a5d79c954d1cdada4b1fc1630898e03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"324e1e79f4308b19a2385138f9aa2012afed77710063bfde12dd3f83aa2a421d","proof":"5ca10cba8f8eec80094c00b09e88a7eabda2560608f829a91f10a0ab68e28b0730d0a9af07161b7c54b7fae72940a6b22d124955f833b81e6189d38883570017aae508f6d13b57b2301b23c06e9089a4d9cfb5f67916e9d3119cc521f97e7c3d3a87cd42d5b49dc1bfabaf0bed43cd3a1d6e523fc9f967cb0635ce01686cbd039dfd13348d5f052126ea98c0528a52f49fdb3f88fa93b6134186e9c2901f940f9c1822fe8f11b4bd68f4f87058753a046a7446ea89cd87d0ecb7529fd5e103070a113b67171f3f3e62017b01cc044151633118ddbf84ab838828620e1a6fbb03d0c0420403b7e921bce57980bc42b9821fd8b657d44e1b4dd6d4b6d00bbc8812442cac7816702573d5d3e2de40b9a6efb841ab463fe1eb73a8d45ecc4a5de3514ef74b9a726cfcf221293ac57c5e0483b30a9f68e849561efdc662da1d4ed54d7428e8fb04f8f25232184cc3819a51022a8e144034515be6de6e1416ef62d0094c9ace610aba1c028da37269bb7e8303c585314388caaa666a6fab950144266cd64b31bf509c28331cb74c78904a1464231d49a32c2143ee818ae62e4cee2d4d9cb1d3fe9709d9a4cfad51825de0cf812bb37c100bcb3e6085a6d5102b3921677c00f8d49fb5ac20be28e17ecab065d92b86d81ff9e1a15581eaea527be8232cbc14a60c5d6b67c41593c35a7e538c10f67a4b010f4a1154af656ffc6f43e77412344b5c6e2a369e783ffcb0147cae3dec57886b969d4e2dc6c8f8ac3cbd2c5c14b8cdfd0c15693a73b6249158c47048d2effd5700876040e515754e1ba653221e1881f7485ae08861ce93e12c3ea10b0a6dd7fb3f998b5fd53160569a05067e3a98de6374e209c80b6203b18fc6f82ecfb20d77ce279b44e35c37523af4e505fe974aff82cce05cae5d4507ab717df34b82923b4a0e513396b134f2b27c7e0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ce3b737cd81e0d1090291f10ade0ac1ac0e5f7f65461fa88a28daf4892e98a4e","proof":"306758c0812283ae0223f62ec524a6d0d2bf4b88bd349ff856b8f0dc82ac3818165112a212f26b2775a080212dbfdde6384032d831c7207db9394504e26d83730c034e432c05183e5b7d2870940b098aa91c31eed2c73c69be8be160970cb7333a1f08a49778132510a6f1e0238b1d46ee54ae5e92587d95ad70bd496a8b1c0af29ec92f737dec417a50f31f17193d51956c601d337820045264ff792a812203161dfa032ae34887ff124206a81bce3597aab0c5126051362a863b1085f5ae0bded7baed232f7c72151596606bed642b0de27a13cfd2d6236846ac6da69389045c1edf7946d1169c3d35c224de87b820eb8754c3dff6708afd3e3319dde96b55ba06729755953e00bf265cd96ff8e7191a09f6335386eb2fc1ff19807979285866ca3484b2300e76201ec98c43c7141a148dd676d05750da2ea23b6bcc33236a3e38c7a575480faee6881d7eb2d0d47d4412910ac03b0071e1a3c2be22e3821b643ea2d33a1f3f98f89c221566b143e4ee195edc01094a6841e6bdb5cebec5438ec93e3e908a8ef5868472ae06d328704cc2cdb5b6fee4cb317c10af210c1923b478cbf1f04153056aa260ebf0ded98738b1a3f88ce431fa2cdfd60ac96e3a79a278d8aa239651440eeff581c4558ee118ddafa71e045c95686177fb3c7d145d920bea146958108ad95c74087ad784c4d196e00e5c6a71168e12a5ef2f3ac31d0e5183c0d9f9796ec93a9c919cd84603592507c26e9f63127407bfb7a307960abc1cde9545327e3132c2c0be805dd658ba718bb1fcfdd5fe0e4e7d287afe377fe46ba6bab3fb6675940da953afffb69db962e2ce4c6706db40373a20e20a117948fb718ba9f45abc1b2218d6e1104214d82c979b30a96dffb716741772992101324218c90176efb6a5a45b29a6f5cb2fbf22040787717cce43c56ba037f21e04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e6a611170daea31e4d568ab79a3bee50e379510354eba05bc3c09f19735f1b6b","proof":"6a24d37b6c69a36e73685b50c8b92ac5441992763b68db9187ab4e70aba72d4e764e429840ffba21d9b7f958b5938ef2b01ac0e161c3becad6f5e4d567e05842321d71bd44d3865d375b329bc6b261951b235af71c71d6e765051ddfd9b1fb1108b195563228686ae7ba2bfdec81d6c485fe13cd1be5214714da1455ca61676162d47e94fe39672276871b466383198693d5e17f7820d695b2bca61598531c062ca494b643fad3c83b02dad85028374ecd939a01fc07c8a5d3da445a618e1e0ba2bacdd4798c47c45ef8c10dae36088987fa51999877bea13067e0e6963e650d0a58f95e389aee91a3227d0d4e0748b01393a26b8094dbcaed4759ad621b6e61de46c0ab7251a4ecabf4bdb487272165db7d25266cb817c4986e8a0f2e79765e42238ffcfbc741f74caea5e9d7d3e924d738483d753e87293abd63d6df491e1164ec35d47cf2e04b331236a5a605addde47b56a295245814834640537e5eed5126b093b15e6bf63d64e4c014daf912c7f3d5c464952058408e25bb8151f53a6f2609dbda304f019ee7bbc6ba34ab9a943733241b00cfe6c22732e3f22c8c9721a67f7c6082cfac263c390bf24e5d97858d69d258ca7720bdeab1493bf6842542fcf9506fb3d1fbbca152bbcbd1ef19f4ee00d6a2f7aab534d8838a882f012c5d006c157dd84794022db35c570878e9235840c58a103e5dd50b35aaaffd7b9943fe7d2f1be55c50d3f3103a163cc293595ac00ce8b2b5a25491cfd0b31cd61e73fe3114ba2d11aef9a71b6e564a83cbebe85398a4da22456b2a4830425268e360349290c9e5df2ca8c5a073e38b906c0bdab675fb9bb051f4dbe7d30ac8d3bf68a53de8fe5ff27c84ee1b236e716a3c54b941829f273586ff397724f7863f3c0b38ad14e103f7d9251e85bd132edc6c1490dd970111da32211d6f55bde5326d07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"eef38cc34408cb620ed49633f09a92172351f530125b4277404df92dd473b73d","proof":"8e07d6523f5aa2f685286cf106813d56b6c944526ed5f124b58cf153d90a6a713e6185760bce091808c55a9f4c4c1d6f70dadb570bb62c1cc3f1956f0988593062cbc9cbb14844f4aef7dca6cf48ad05784cb9203bb355a7676ef8a1962c823e6e15cfafcef2ad1b0d30569a84ca1a437f97841310d561a9519f6c2886e2a60e9624cb210b538253c4dee2ef224f8a8d63f0898667c5c8ebd80acfacdc0d0d0195b987492ffd4e35fe37178619a8e072ef44efe2ae620ade2235d5a98631130e29404303b887ce82db0517cb1ab5a42bbc176d4c9e1121877be3b79e1039cb0230018c43ae981bb062d83126b19da9e3d1f56b746784f9e76a7c15b2da8f2460e41e0db6e00d5292e2ec5acb93ccfa9cdc442c5873dc3ec36c6ec795c6005c2dc0b28c8fd5abc7b649744b32e87e7ed5d1aad3cad40c2afe1afeb5c9ca12090d72337842259ae57fcf85731962709fc44eeb1fdc1f4e7b71a51db388c23dce60bee2329d96f64194d30f764b951649354df102e42ff5369027c0e6bacb7a1c566ea3ef3d87969e25f6e9effb37a117119d18a89a0ea1ccd4a06c7d4f98333e557a51504335d0eb13e772f648f68fb1c514b860b8de7ef9fde3662cbf645bc67964da636720a3c3536ba64fbcab6602e3ce04a0a66531abb0ea970e8e4ee36249a4a761e8769c328f0f5939bbe7c6e0ca146d6682c203e2cd4d809d3ae7a12410cc70ea071700e1c78fdcafa29f780918aded416747649664442d65ba8eb6a16318d418bdc88d7ebbbd9649b93a166125573ee93abe3f50b35020320ca3a17f301c88a06a3b2d27c1b5cdfe3ea88314f56df9474a0889ef2761b4a892318055729dd011cb4942f528e4eb90ecfa2aef592186a908f53edbcec42cae87fb124a0f008a72e6b7da14c46c14184f320cc873611f12f2957db1e828872426e1936b04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1ee02d5b5bfad3a8404c3fc0c5b66a52ee1fbe6d3f0b65271a5bf20572828c5f","proof":"da408d772902fcec17b9e62a4eed119f2920b1736673e007f7fb047ad3bd875e98321dd3af17d1f65510d957f0d4bd3468b3102b7d6d15ba0bdb725527ae865ca2012efa5fd031b23d81fbbcd81af412d1b91b6c9e7ae515fb2d674c523e4638949945bafa5af0045e1c15fe916bfdc27220c84233634e23c93c800201685f50816848abc7e2ff2d40f4780f03c876d6ebf5c6966758199c963e03ae0afe5c0167ebab22714758cc4cf8e9e79e246ef2623b1d3f907d8c95684e4db4084fca05b9ddfff69d995d84ce6b14d5eb5507182e53cb2f5e9812665283a1a61b2b2a087c2f80615e64d3e73f1018f6a54c8d38b509fc4f90a0000e07c8928f2d38c81ef85cca072e14b60ead75582f924a9f5343db3917aee30fdfe3ff9578a61a783cc6e8f90b4803a39ba694564a23a904028d94ea84ec692e1f618b21e16cfb830f163df6f7433ce9770ee949908744ce9de2662a2a1b85786b1ba74b9617efa415581514b6665795c5879582d29bbf19f95aadaae970ed6461b353161c7a35f11dce6c6259d64cc31a452c48c35d7f1d07c90ef391bc2ca2aa12152310e5828754903901d0449641a36ce8f18cc98621d7041569ba5970bd8209a28cac83e13130e090e1a0374731672c1d91b2cb86a8d32ebe8bab6000d672e5b5b6ebb6d3300ad8a30cf8ff82f7662760f8549bdffd02408abcad5dc78d16472aa5123ca4f42970432a93a3000fbb46e44140c13c7717778b642c832655ca562633c1812f9d530e940e7d186e01a5b8c53e1167c1cfd4e90a392e390cff4aa88c37bb6145263a2cbf000a04be2e927f2db5e5e1078b6791b830f469a9b158ca4908c883ad3a664689a5477d5c5c93ee5991695ec867339a40014262ebff078d3c82ed3be3f301eb62af3a23d5072d19955685a11b9fbb51f5813835772bb24b8fa1ccf21b3f0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3a0fc2370455d5c96ce96cb05e92a6313b3221f2e370d307f2a07f1278b1fe5f","proof":"30c7ae0526e571a69d0c9ee21d5bd45383bf87cb8a15ba76d20b2547f02c2b2e8833b96fc12d96aea60167476bdd3985e1180cde9451626a0e9e40f7d3e2f1206c7c41a32b134495457834658fe5070bb5730a4b6df937b22ae189aec6707e2b84e7af6f97133fd8ac18847705b3e4bb7270a31d80cee7461a8e4c403a12eb1c33ef03a00081a35390896b44f34cb80d05c040ba5ae7c30d2eaf1ebf123cb807817916b31073892c23b8bad9da30e80b4eecce62c124643bfb222673e1964f03fedd8f224f6ce8e56590cfc04494210245a696e7ba3c33cf136f7d5cafbf8f0e7eeb6f7d7fa28ce8338d015287ace8773688503b5f95cd7a9efdeb995e64095ca22171bfbf22a36b15efe37d49915120794250274f9fd7751c74891290be616aec2ca3f9007a9568869c96895576ae1081fa95dad9373bd246184ee20adba52b98513bbdecea14ac16dc67d349001262c62d39d52deff60261d0f253591ff45b0eca761efe5c894f43f3cf9283d5e7427d1c0a7eca10e2118500fc9f63cb724e80ad39d18a9bd0ee415fbc9f30aed6c0c45c616db5fbe0b53eb99b988a05673296b50bf89f7c7cf98aa574d99a2ea16e6650265468763bfd0c553667ca4cca7d109a26ec3c3a7f65b81663d38c7ec9ad437c0af5cd561f248ca8f910122b083a901b304d07a3e4548bca547e08214cfcf4cd92d4f6f8c19c1c6d5b85ec48600f2617500dff9e453ba4ee772f048cd5a71ffb65f39a202bd2ac75d5b649546022c2e387243155be74d8c11a3578f89370e0876b7589c8552c735556940fd6f56dba3bbe286b2bfcd0d7f7bc2d5513df85f04eb8ef6cf9ba22a534c5bd6d0ca40bcd76c69d8bbaddd6b7445f38c62f1776f91b13b485b21676633df7d99b84320ce299f1a515e63ddb892cf8b2f002f9dc7f3648ac44dcb49a75e96b0b4e1a4005"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"38399438cd0770714a736bc6a2513b893b8aa12f543f946952d148619715392a","proof":"d4147021b6f13cc9f1da29d1cc49dfc7a2b5108c760d5bba932091c8b9c21d6d9241b2b079e3b0f85173d7d8c9cb45ace597c93c36e81e356c349239dec9605b3c14a61893c405c55d08516951772b3cd2a068d0edbbae5ed134c164e8c8226ae4ae3ae033ba658c26350ffe30e819e85afcc687f7c91998409897c2e9e8b0092b24341860c5f46f7a6691f6e9f06b296e16328608edd608b100363f40ff790c443e0c3cd2f16485701f998a28f8e242712f9f38c5d5fd6371a0878768b7e0099056536003a5e62e01c4cb0b942131d88ae44ba25b7e24e5c8ffa3f5eb9a5c086ade23f5040cb5a41959be8d524b677ec6ba746305e0306315eb44c4430ce311903d0eb206285c7ed026947c1b11d3d2c1244a3de61261f11eacd154fdf0d354a64fc1170e0abd1e4c87f0ed72376e9dfc08d3f7e67e9dad55e516e1a6dd7c56800abf50d864d282556065c7914c8497c869041943e8c958279bacafe1ee965330ccb8d76aa6c0fbf2ecd63db0fa8ac2a37e3beb077b58105fe0b7bf42877220e40b398704a7e4738eafe0354b646f1e56d82083d108d9e6511a49ebdbf96607c2a4c495cf973ebe62e671fed8379207af34f7adfb22501e8a58978a19b95e1abccedfc2abc23b75dc16c924af1dea9cd3bca0289d0c9949984097786257b3756692e9c4770174d40f99ac221ae1ad8a27d9b7596f4b87229a58e2cf5a38074ad0c07b9f7e02a05189b13ce56b6c46ce1f056435ef971b66083cc0d8e0a30a57dad5cb55dae561416ea527a5c755fbc8730b1a5bd445ca17d4b3a61b82e22331baeb6f057c212a9175b01a6f19874520705ccc312fd6dd6b965a62a762f7961cf4501b9b6c9e980de46c757559daf1d2c3894f348eb771813bf24b74b7229405bdc261021cd64ef4ef85a6fb05799a6513b2aa538e7b0afc1af3a87009a08201"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2449975a185fe3904e4d78dbdb3054b6b64bc20ca99bfa2db3d7334fa02f1802","proof":"54bced43e6115a741e1a43bb4dbe01dcadadd38f1ed6e029bc84a238eae573583274ee6e60022b015fbb106ea831861f92c595edc7890b5953353e4940a058574a5449f6e806e6c516fa14df30b7ebf683eaf1d6611032e81a51ae7079660d66eeb68cc39b1b1c8a2ef7e0cdd1ec39fb97d5f7beac7a31a09d9404875ddc48481c8a07ef3a893a0aabd7db7e14da51231ec7a975fa3e84df9e5515b6346ecf04573146522bcf0550cc770f757c4fdbff204b6ec76ff2c82e697dba760edcad075decc345f5355551fb1098aea540a8a2bac2d23bf4e05b8d54929953c37c7e0f063f9b40e1f7f6f8509477acf71e49d0b769e5623322fdcce88753d4882be36c2ced0d7cca0a465441d1c4ff801f7139d8fae1a98973e459590d46de0bda920bda802bd80c17613f2c108941cecdcc4ebf86a0d4423c8001c4ec45c41293862d1820ceda73363e1cb4c7c01ca129d7fc3dfdb9d9f62c54731b6f9bf44be12d3852100c1337fd0f848ae7beb99d0edc14824a2e360250772cb102e5ad192dd8768a0958ad430b3dd554f976f88097f0b8cbfe95ff35f748d26aabf662c8002059baf09c849f1b311a62bbc7a65b1260256599bac9b8a38e352e1480e89b41e959b04bc4aa3385da1a03c50d7e3151fdc0d1cfc6e317e8fcf20cf52390d00a3e0e68f708116ff824fdc6474d8d1ab459ef642a07fe76621e6595b7e5c3758e0d4f30ad1dd61795090fa55ea93273fc55fc02475cc142a4b944529213223ac9211ac4dac7aedcc1596837dc437125d6a290ce2271e2d025758f68b820b45371f168feb2a99ac3d217434e460d099cab52d3cc73798170ab3277d7b7d83cd408731309058d469a266e9ff84cf91ecc6274d877f5783bbdb35175d684b2fa3821ae0b8042931f4aea017c5df6ca45831704371ec2dc74ab04c8e6c09144d7eb76af0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dc5eec34e85f4f391c6d25de2914a465086e2ad91dbd28b99c8fa4c8fa8eb836","proof":"e80ce16a8262edc613c6c22fd6e037d1a631ce08a19f7cafbddff0278248527c8814614522f45142b5286eb6a1d13e79767721f7b596cf26e7875787f117d24dce181f1ad869420eec6f62cff8d795eb935a72490aa35a7fcf1122e211c07f0f8ed3ce1e0dbfeda0715aca18b42efa9997ca61cf9aec69b184ed93ebe89c584ef6a89f809ecc874a1a45efb2b628b8578dbcdc68c3b0aa1d0419dd6a3bb16c0efdb1def78e9652b21cfb512f55e0791b5f1e219184b58c3e2628811b3ca68609b693c5e2d1732551e1ed09f07884c32f73bd12145a1e7a123a3e8916a73789046eda6420be1d5cd62a818bc789c4163d1304b79331dbdd1f7b55793d72009761301941c4a906fb581b0708749154e6b6654af9320833c79939a1bd7f3616980714eb3356aa3fe5afe37af76fc29d22d94facbd2730582a0185eaf6d55886403a38befe3b4e9067b76ed4e1e2be03eddc45cd3eb705646b6eae494ba3a3f1b64f62808e98fa577ddd507a6ca09f85319221a223db4ac44ce46b158be01c6ebd485831331aef7654583c2b1f5c3fd1c121ef087e360ef850dff4a196a6a13b372392ce39fe70c6fe5b90593183036e939dd17aa6bb3bdf7c87396e1836a27ec02aec547ca547e924ecc9b2ca0ba81ac4be107450d2460092ca91dce4ff1bfc0a6afc2118d92a311d763301b48bac59ad372e65fd3b7912870007aec875968b0f110ebb175be4df566135169e944fe39810b92086ca5016955e4c4f3671ed4223432a6c163e693863e5e230afe3fa98bc062acc7ac0cbd94ca53597347e4f480f63d4151fc67dd1998a5d423ff376b7ce386f5e9f580f6f11e30fa2954a2cb2b6634a35ac372e0c2942747be66a0125653c82f88165f55cd39e62997dc5d061d30a1726ff33247150c7e9897985357f2429b92b5a78933fb5ffbab8e7dedd85e600"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"325c2f954c34fea903913f8ca93862588c18f5ed7797540bb06c66164f6a6619","proof":"6edbe81bfcca903252238d2b1d90175cfce95e717216b243d0578e0579ba49204cc45f71abc1d49ec9b1aa4cab27293dec48ae4244f31783915a4fedf8da5d168409da8b117d948ae66ae68c0b9ff318bd52e83f5da8f2cc9da750d216f53469de548de7577216a590417fded34a0694e7efbab3316df5062632bd546341cf0a4a87a7a5df03798e581a288fcc6cca58fc96d9fd46b0f5ca9620dc66e4a146087ab8cba72949fee3bc68d8425c212a10274c7539e62ec8a496977bbdee3fd3089d25bf3c35d450ad1159f14bfc7e9a61e586d39848f8fe5a4385cd0889e32c0060a2868ca421fac94060ab3a230a03bc7ec088bce28d135d5fa90f3dc99d381bc495cc53a6394e2fdcb25db721eb2b388e3d0eaacb1e7406e7ccb5731b542931264b0d6ded8686c132d39cb15ac574743823e9a380b4e5af3e5e24d6a842af60f22c5d9907ee3f1834a2a99c9d35e12d2f57dbce53280cb8429e200bff70f7191454ec6a5b4b13e9b21518589e8a1df5c3cf6bc00cd227fc8e8b5a27855775481035b7a4366702afffd1a2c3462b106dc28d507feb31bc624d57beba093a04725ec7950ba2129b3e76b595d8b8124b78746661b9651866f7b1c7861cb36cea227efa21e74386c84540f7f3946b34f0f51cbfe1dda9401ad5d5b05e083bae5566805a4126fafe8437fcf922fd5b690289e807761bb8d9f6224282b8997438d16e0c19e7d2f9011cf586959af3e9f3f81df0ec17581c1ed52b587754431e5c712394fe5a28f4a5810606c709de748bcb26a1777d41e877ad2733aefbd0924ea33f6a5cc2fa860b289de306fe2ebe5b658a51c91d025c31ce299e622d7e555c5049e17425e12edd21213dd7362b3b8dfb987ff6878fdf986ce1a4a9bff627b7a80281e15428b193c024ffec0c51e699447ca39b9fa92bf5b31903bb3aa32006e404"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"08ff4bfe9cf95d89b2b5967fc8231c85f55d61a31de564612200575583e64e26","proof":"426e4371e1a6b3ddce3c46e7896db7d1efa76bb47f643d3c410254bd598dad28e607c9c4b95850cf4fe8dfc51c190e9906806251cff10ed3e4d198a936c90f0e22247e15463679034074cab1a1996bb871d091ecc3cda7e564317cd83a027e2f7e5957553654d12f09e4a2485bc7df9ce72988772384f49efbc950f241c7e604975676261e0b8bed3531bf81669ebb8dff00200c9c9decd1ecf58fbe004d6f0b9df0733c0774bdedb801a2cf9af2217c29ef6ac8a1d44a657e3539df664a4d04bd7d43af2a4b41649a5572b5c4c5dd6af9476bf2fbe3f1306e8a1b6793a2e20b90e832d650fc2dd189deb10fa1a64adbb71720e858ff681f8438c13cfacbc1119809997c61010eae04bfc8ef5d570f66547121955608452a8f69b17aacc48b7b7e478e7afab2a5e10e168b822c273abefe32b4a519623b64f89dd1676b7eb36164ab5b85ce91bec9a719c6119115db1af74d3e3b9090aefdff46a620e6b3db58e8cbe6a8072afbe870666c423257a434f13e74ee1dc8be2eb70f7ad05220311dae0e768372186ddaf0c3f323d17510feb7fa11cc0e53f6e8122db5ce737134663ce86938df199d7576f8a16d866677573b9f9f670df0a44b0e48426423fc262a70c070ece67b916b497aaf9d4277205daa2ba35c54e2eb7ad5f3721273ca695932ac0532b530c0f57b2fd6ea9c2c54dcf311b44be606bca7193bb11148d1926a747838a9547e3fc6ece3786e945040d8cf468e81732cd738380f1145308d5814742518b5068134ef43abb3d2beb377e01c14de53eb7d3bc9915207515edc7e42f206a963c4d6277fb8db4bc7f3984182c668d037724829884b6e4a475c8b967d5cb502925841d5e4d9c835cb578a27f88cc0c278cf872f582cb531bc9b56d30001003a83f84616cdfffcf2d94743d1ca00981a5ee9a51b306883f7d57b54d502"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"04fa660f9431085cd96991a907822a750955992dc4f537ad2da24d7959e8de30","proof":"90d6e3072f5917c80af6a58750b0b6d65240e7578712431b712557802827bd0024ee5c9c0291a6f9194347301513d9bf8567e3ed66d501124fcc9065fea31e6ba4404cb75ae0a5ceb57669b3e5ba0e76adeea37e722e2ab1054d61085e936f0aa01a06d32c2c31418b65793605e7065103a81bbd9aeac932d079fe61aa798418b3b301b7da6f00b93f2f63d073c277b7c1d9b1b5fa2ca155c11b28d3e7a7a80ff2024a9bdeb59e24224bb8f87bc91cc5da12b88b2d51fe2dc581011038a3b702a7b5e8f6b64fd8d1e135e27b38dd8285b52b3edc1f0a3847c7fd56ea54d5de025082f848d573588b4643b4b31eb313938615269ea2bd87d39263c832934efc66163fa1ab38f3c38213fbe6f9be8cbaa256eddbc17bef33aab97b080dcbe47c4c70a41b5358a074a5903966bc282cac815b6e3fb0ad0b27fed914589b9cb7c72c6cd916f631b5d941ab74498465aa0a7a82665e7f86ed27dd348c5475c23c1801dae020820a711349f89aef6b97149ef25db5988063f4aa3c40df0aa8ed310a135ecccfa479a94c1a8fa6cc41966738da5386afcd12c77e888c88285faa6ca50ebca496cbb5da89ee5442f52f83eeee2f0abb2588e0ec0c96fabe588601ab3641c2943b6851fdb07d089de87d4006e067b3a21c5507a4bfb05cfce837488dd6533ed3b8785ade034019885982a5a27e69394f196672a2063fc25eea83e8911d33eeecde844bf9a21ddc1e43f5e39739accbe2a38489639b9ec232ef30f4a08a38aee8d7da986b9a368080879d916de6ab6b6fd5d548770c231c86562e78f9891bca3c94219b61d458fc28a7d99bf48e7bb36fb0e261e44e7a7bcff213fb79ad10d493b620e67d2cce704eb6d91a8e843f55897c9c97db70452fcf4b6fb3a1d70d146204aa2a82d67c2f11400b46ca6422c9a3f1a3059a94f810018cd940459407"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1e4a23ac8ada4bdfd340bb001330ce6ba97d484e7df0c43066c37f272983e53d","proof":"a683c8ec5cfdd4a48e800a10309e47bce646f109d8a3a381d9733ce599c39f2e7ce9194667a1cc379b81b166cb1599fa25d9b81577e458f4e6cff4070a82a571a426ed26c8930ad47d436ec10435b6f9bc9453abb9722f771942244d8d041e3a426552aa7664dd64ff06c2f964d1190471f303c62de548e709703c791cd2631e48479f15f74968bc3bfede02283a2fb03a5a4f48ed2a736c5a457348b3576d0212a07d0e35c1c03507b129b9ef9b5e3e5c3d3cef2c47b91fe7d9eaf55be2a6079b157b5b7ed373e50d71e1e3a6b165ce594c21dd4d090e5f48648d7520ac890c76a6a9cd9e860a4042449dbb6a7eea31f23cf7f88af88e22103eb3701228907f3cc1f7955c6de87c41faeef15082386898f76e9836a12bb929bad2c552d7c216e0b7f94a93ed1bfe8f41a2cc2a77ce2d41a7888d55b9ee8cc692696e008e825268efd1770facdff9565b2c3a1810c42232aadd8c0ce7a09e014e5a72ba2a3919ca5e60ed5674fc29c690e950f1ad1a0fbb5000ee60b9e56eb07b4160d479416426b28bed0102bc0219643358e161f41c08c9b0ab2c65a81eb96c4ba4dd352f7a7e900fae82ae6c54652a4787a963e1034aa4e0b9c4d7e96ca7bee961c355075810e6dd7beefdc815cc9194a5aa552f5bb273be101c97d22310078eb89b6db310365463b401d094766ca91f358f96f82058e477c88490dd7365ced18e48217f528ad2a06da8ab7f8d9f67e10906313b8b39a60358eef3b0dd66d8740a4cb833472445490ee22f9622d071152ce5125536ebb7ced87cfecf739314e24a5df3e827202f6857b03c1ae6aff777ff2bc5398ffb05b6c3f80b6a16e5b573a14ccc1c39e875c7b98262da5752cb2bc09c1c2fd9c30953bd6ace8f1ab8b467336d617907ae4ecc264ccea32616eedafe9a5db32b57523bbf784b706cd5f953c2e40c7500"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2a0fa009c4dc76a20665d3ace5c0e67a66ae1bd12018dd8cc9699c28cb131274","proof":"4cf6dcf4d15b7de9198bb25538abb283ee33d07bc1fed6fd60143b59b923fe7bbadaacc2b58b7d1f7c90c3fc282cee16df494692c8efd62b411e8a0b325437344e035dba0c2958226902ed2e76321e4c24471ce4894a8d512b6fd2454bbf5144b28c436d4a7b78a9a13d0b83d039b7a87d84a4488554fbe21e3639156500d04c0b8bc0550b532765fadf85770c169a2035bbb98291d2ddb9c594bfff7073a70649d45ac1af90359d7ebce0269f967f703843cda7939c3a142dd7d4a58627c3018703eaf7ef1a11b3f0a0fb263ed9947deed917a28b1a43804d8e9271d10eed0b66b9bea19353bb41ce6c68a660b140de8ef79f4dde174dd2fbafc44ba7a50337f22d70c4e98d7c5d6427cc5c7af14d76f5f9a268ee0ce8fed4b534ba6a764f5482c825a9e68b76c726d80d9dad04d8bed771952eb6c6fc0b62ee293a662871589ec82d12bb837e097b12583e1a3daa76b195bf192c6d7a2eecfcb57f590cd32a5e232536d1e6add9cac674b03e19fb22682bff96db161ad542eec6bc19e9600008627ea2e4b67e1fb1346331a5be21a65428692f85d8dad91d95f9deec13c5261437eb6b2756cfed6cb1a9d9a94f35acb58ff2d8c31e0145cc04b29f90a6c36178052725528692326b9a7e62b8d9d7b313237c4b102dc6a85afc63fe75b72a515677389fe2526fffacb2d8e736150f99c91474b325772cc1e2e0681daedde6230a853d522753ef31119acda7e3ccf3a25e8da75d22786282e5720c0141a6ae77d8df3fd509f2cfbdb57b862dbd5b9f4d40e73dde47fc38a671fba63ecde20d2e922c62bd2603a7202ec2b9472e569cef92109d1ff0c59c7c2c5ccaa3c627b40387e7dab74bde8a47b708005dcfaef608527a565b7facc5bf48c7de3c04933f037f5d59553a99003dc0d525581e6d22f7d6b67896c8c86145a42dad935a2b5006"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fed521203138a024b6f24e31dfe40853ad52b4da50406e281f0e301832257a3b","proof":"6022ed44a492cbf1104a6fccfc4f4ae42df3c145ffd30885430598ec954d5a15d096a19120e64594b59b2dcbd4352f92fa48cf0b08f78a2b5035f1d9fd0211218cf0acb5020f7292fec0c9031f3840a640920fe2a372ceae2a294366b4870c1582c21c83dd4dc96b36c2a9a9c6ee0a93ef1d1f16ad9e9785549bbc42d119b354fbf5dc601444b862e361a0809ca17dc0f6b8dfe5a5c725c139350e6a9fb9260c79efe7a1550288127dfd521cad1e184de2b9d1d4a535adcb8e951934e800e2013133827e632c7f5dac04fc7a174c2ee27cf1055b570774dbcb5b91e4e915f3024c7b359c85dfadb23d545bbb3569d03570cf7ea3b3b036833a8981b1a77e974ef6949cb9da7e9b6160da11a795148338a2582cbb970ea3ec693b829b7d4ee727ca95f1ad3b8baf2f4da952639301269790e05d4a52b7d182f4bfb758a3e9ca6490a09622cbb6090f78fad64be1a1fc2ddc13a5e79ef7be81560cbd7c5660956e100a58b3ebc081c8a0dd94bf6d675a18c9599fa4975ade7100440035fd956121de9d424b418c7185af773aa0d5a3bd5307f2223fc12f7e6f0e31e278a51cc85758b1fd1097cf7f478e302694e693bc4accaccd2b4d365df5167521a3d7dafb069af57b1089f712f7590616ccb3915031a714e6cf3de148efc852b59cf33e1a6c867c23fd807f984d2d4947cf795981dcaa1f0fdc012aa549d9039fe698bf5647b24670014eea337dff5b0b0839b928e86bd9c50bfd450bd9d2f1ad67e41c617682abb1bf8a2a8317b8184399615f6231f8c2323356e55f56b2960a3581d6c54e60192951be3920e460a56036047e111863128c21426ec7c9f45e14b1e7ac8278da35761e3aa082e2a4b20512c672c7a8cbe988df294601c68c42a2da67275c0de0ec1d329b8b5f36dd8e8ad8cfccc8d8a244ea5f4b1fca26ed3fc3839eedab0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"566cd36fc06a5f50c69f29a68ea8dac8897b437f9629f8a6cd7b1f75fbd44e31","proof":"88d11c7f0622528ed57f47fed069621f5901991874d45088d75f80988c7f226be6711cc6f4a132e0a0b4de749db7749b7c8ac488a08563c09f6867ac965f297f50e1cdef9205bb7fa886d0085d0447c96c822b2f872e2321e1566102c0622746701f13428a0777349f3716f2a5aefa756a3d59c5097dd491c4bb3eb30c32a424e7cf93f4499a0197007922507149826eb8548b3e309be51a3bddfae7520cf00513b58bc4f7521f687748f7ec206d8e05cdae8a552ad11d433f9ca3f0f3325f08fa0e8650bfc2f88b0d89404585199a8c27efd10122bba232bab8dcdafbc61b07fedcd49281afca25741ca51f4f1f4fa2f37020d948518077b6fb99641e25e9240ee4bb4ab94e7ea4437a7999a2598ca29986163d87b08dc9205e7b1b4fd50f3b28fbb1617c67fc2b3d4cc177d7774ede96a5a9f85a3ed534aaf25d48aa171f4ddef7e10a1ec3b51826c2b40221ace3db008cd2f08d324e60d4efa7ea08ad892df892694aa3200c518bb0fa528b937f99c10ce915ff01cbd0d405bdea9227113310898c7caccfaac171b8aff7795ad87417bd3740bfba35fcb5038479d23fe818e26d62ab2bac155c5b0c831af15e9d62e35e12d0c9c5b4599664fe5a96297d01d2af24045caf734e3d82a08eb11a23a81132e463c01aae2bf799f0f586cc5514a86f5599921d2b85477b8a62091babac9b5987c12fd23334d1f47d9314e95e28005d0a7fde8c50c5fd976bde669fb299914a7cd949b94083897cd7bb501bfd1552c9378a06740a99793b128d54340b49cfd28f1b08817828a9cd89a24d581212580c9eb83f02eaaece38272981d39e286e9431d7d597fea058fed3d3e29e7b2aca3cb90e627bc929609a99ddfaae92ad52fa7c4b1bbedc968cc19f13a24a5b0ffe79c6ddec460fc13894613603b3ac4b33f9f8c0701e266d0983657e03cbea04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8622b9b660866cc8ae147a49b8534f156250ff8912804fb41b5394349f76ce32","proof":"72966d1b1ce0957b718b7a2374ef4b1befcc53f19079923da4de019ec530414c5417ec3b0ef02ca706ca64166afef41f3d062a4a372a72a29bb4a94853642d4f42d6e06c29d515dac1e7935137a7b33f1f3a112168224d11c6b3d9bbd56b2158c43f6091d0d411b4ea2ab4afa1233610e9a807b7a700e5850cae5e0c115aa429063aa94bbdc92f688dd6d0460908f06d8304db900d077e3caada2316cc735102133826d121fbc403a4122c741bd102e3708291345989594b635afa81418d040b653c4eb710131000166af270fae17dd80328abe7180fa04e1a0daf03f599220c7e92c8906d6599f09552f9c6087ae9c24f6ec1d781e8faa44872b80ad99f5c1a227acd6e5b1b878a5ce7e70714c331f93053e8822c8a66dc56ef5d3cf7baac13a2726912111f20bf722093c9604174d09bc01c80b5db185e872de35215ec1b5e0a7a9b1d7c0ea65c5fd2120bd3b9636626700b1ff3a344db28bac30a4d5a4f67ecaac4a1efbfb22747a0a06eed4ff954a97952710db982f210c4491a3c4e81744a13fc46126a248169a1897cf201c57b3a242fb66376b04cdd7d394fba13db43749828ae90842a4e90785081cb19734611377475d40d2f45cea90c6205f126797a762be6e1e7baa54b38ced4f61eaee191f079a50eec1e8440328709a8ec7476a2fd30579a1d9027a664ba38f074199a3169fb59f9d875e3d554ad77be26b559608896afd60731184f31c184b7bc941cf698cb90d3b57f3eb12148a078b8946e8ebe27e72a43b80b6ee94cb90e98667a9a1a1b0cfa099bc90648bbee675fe33cba06da2efa1ea8cb65d0b74b28d08d0748162c4325f3e4415bc2e01be321122675896ef89b18980b9698d93748d88dba7a6eaadf17160c8c9eb74735556aeb0deedfb83190ee2b7cbc19d2e77bddaa3dee0469886d749905d55cabe53dfdbb05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d832bb5de59fee098cf982f508ab6fd0231188456421b5c55aa96b26ede40b09","proof":"6ca1848bfa20836fcc9e207527ea1caa765bb7eaa7c51061c6b998f6f7aa59172600a6a06e3f0a93dad5022a20a0f6d01abd819512a50583b5b305b47f996a15c4f0851b759c76ec133d0af3eb406373a7ace94bbecf599fa9b739c7390c8d3296b1ea5c00fd1e128eec5182954262f956e210d6b747993dc60a5ee048f6ea185283a4b725e82e889c2e90eaa8d1c77f3fced616f060004a30af1dba7845e608d4b1bc34299e8b3fe010d8b80b1e62bbc9dc8dc6f94b7ac9c1d4ade25312d00fd128d0a1bf1988e06927e1117858ddf903c3939d1a46801935ebcd8823f2fa0214a885629bdbae478e86adcbcbe82c7cbd8d671e749e3b0642d33d44dc2ed0261ea9cb6ad7fcd5db86c809164561dc463b9f138324eb52c2b1dce50e54ecb010b66867799d3ae1f4c5d4360162baaa7ae28a093f7c92240697c78d00f0a3457a2efea92bed0c216aa3fa8a9efc184aa7c91b22395fd04f80c3ccd8b949a170340a241cc221b1729a805da92ac865987dde3940825175f7f5a6f4482f34b35e76065df45d585b83e7025e6311ce7d477b133572040ad53a45cd0046420f93e6424ae9803a4e3e5c60da6c8827aebc1eba05af4cebfe6a7cb8efd2f692fc56b91f4e4e7dc6e35e9836bbba0745dd4bd12dfd1b5aa88f27bb955934fc1f0840681c6433e6a55cfd1985315a697dab910dbfdac21231451dd5ab95450f816bfaff1e66a519807faa67dec7d5db7e7f38fbe004df5792d73f76e8291c7c029ced4821c41836b44fd8c8a5211c9726ee6acd445b22e54217e85e7325bbdb9ffd8fd006322c0afdc5033d86bace4737fd68d7fee94f4d5c7717f89657a8ce0fd75eda3a9174163343c333e0c942e49223f278fee1349bc2b9e6b9751baa3b5aff4567058269da0bcdb48f84603a616c5d2549f2fb6dea9ae79e0cb6d348babdcacf5a0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1052cedcf8ec68014da3c56a76fd6d3ee44072a9b624d848c51f603edec0cf0b","proof":"50ce691cb37596c4133544a6ead7d748eb1ce31c0063719ab8b6824c16f7584e06206a998a0e346ecc7f3eba74fe5e8defdc972e979ce653e8f2b1a0b15ac962701f2c934f30b6a7422fe3fc42896129dd6393f4bec725c293ffdc74ac77fe0db86eeff57b193581abae0e6ae9d4f7d6d07e244d07afa5a1d94ba6a5296ca96baa7f97a35fc1149e447f9b79d5da84706503a0a8d26a11fa46de38ccc0969307adbaea3a3e0c6f4f0c1a1d1d4ca7f75f18275d3e9dcf33cef3deac375473eb0d31d4ab8e42ad63efc7e10b8b4113f8b83268cf803e931d43f1dcbf44bc84850ddcbdb61ed40304d4509a701bbb34552b9ad15d06000dcf444408426cc3c80450e0411f2be798fe016bdac434c6eb986e1e64bed6bb872c09fbb6a50958ef3b7102d2537fa46f6133baa72ae3f1d197b1afaa3f1c14bd703501853d81355f151c6681b74b1f1a0d20a0c9a3b4caf7cdb357099205bd2b1cf5733f1078cbcf8362600dc911e7e41a2aeeb7dbff2025a678eab5be1b73dc8a03e63a89e9ea0c7547887da4ef2dce52c8d39f5b2a0011cc477ba60767007860c7e0b3529eaa28a250a45506dcbfdb155c7394d3ab826bdf06b5de4253a0b3cf4612ededbb5823635f6a5077cc3c49ac45f171deb849be15f357af4bd110702be7c50ad9e2fc21b27fb8ef62572802bbbb994f72493b7ebd63a11e43eee5fa3146859dbdce6b59ab4028aeade6da088e43bcb46f24497f39a8ae4d0ee6a589cf99807896c6f0acad58bcfcd8de96b0a555301d107bcc939cbf92e9da5dd7b79f563e845fd8dc31aa4c726548031c640c723f9f24eda1a7e1385baca983a5526c5f9b0301a4dd644e6dedf9ef2bc7897c43b9c5fd7905d454d29d05651a5fffb48ac3a77234598c7e05173242367d0cf99cbd5d2423f309ac29e2a9d8a677237bd9ec39256dd90aea08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f46fff69176f73838ee1d43ac93708773f9f0d008d55b8e91aef26f7cdab9f21","proof":"20a123e1f367e7ac5bed9db2962479025d381996a1c25d29aec58825c53674747a12b7e62f2fb59f58aff5b9f605ecf4a647afcdb606a3badd00e8fc6a7270624646002da84840011711cc48b3294161ae3f9bee4c3f518af616d8b596969e2c985d644640b998d06d281dfa916d1a0c343d721ee1449f3a9f3b0cd947e64d66b3af8821bd25812c594b50504e40be3617f598a821455c0c7e4ebad79c3b7305b1c27e4fe75f3b20a9993ff9fe2af9b8d2eed9390fb5355a79acaccf2c15fd0975b54dccfad3fd8bd3b9126420d79ae0fa6e9182d7912f165bf4b185c009b5034ed47c40695993e252f68a5f99cca5412571d9fc5e7199c697565a409d75cc775cc10964576ef140ac64c6ce33ba1c2c6c04d054e4cf380f7f38aa7778aaf60a18a67170143e4af339c4cf2fafd346bcd6ae25cacfc175ad33cc5bc7037f8a2a400f1afb1c5064d0f6fb17363b7ffc075ec5bf39d413e468332b0a7857a37d41162226ec0b8301cc075889b553f2eafb99c2b5a22d8ed736c5b07f4645f2175b7eb982c7fe6fa3996a50e6f5f26ede012af4b43038203800e7ec8a93d654c64a280d119e27acbcaf02e45eb3c38893e5666acdfe7e2cadad5a03dffe377a4528c615adae1f225dd2d31c497eb3d03ed3f690eaa5d61da4bb7278c246cd84112540edc877f3e35c01a7102fed72a88a41a5de21b3404fc3c5ccee0cb0e131d44c128be564db5be93ebd17504004998028dfbbab70f16fd2acb6d35ffcd6c7603534211119eef1bdca2af742cf951d664116b3450f1e972d05ca4f7a81726e4e58a8f4ffd98906db5bee341ab27c91b46e70aae14df27fc3be7df27fa6e6cc05457bd39cc5600e01a644a8cbef911ecb4d8f46fe77dd619dd05a41d157579ea405a933cb6c03a86ffb02e021c05c2b0149cda526415ab8d004b31791888549f101"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7a06b3d97d2476105cdd3456d716fed9401f3046619d9b3a0179bf922e482c36","proof":"1e922bca32606dd73af3551b2b1b4c98606172cf27da0cfe2bd3ba1bed18ce304e1e1510663452c685af72afb4c50c5c969e5f6386bea8dae46700957536de474a80ec1c90ee08323da97f6f5053d8c8ece1f0f105a425167c8927e48d4c02715c517bec20a34928135d2f78c9911a911f6d82f79bff51d1cb7d3d42ea00c6397c71a670b5d86fe6afa9ee1c94c54330fe2af4ccc801385d6b60e3132f884e090eb28b5d6963feb61071a253ae627aae40315e3be518ecdd92fef0acf31a180166ad24435e750ce89de329eec5ec4fd36d90a2c99a30ad3fe454b8aeb47b6b0480f2a316a01d497f1e765a17cad8bdf3b32e053bdc575a158764abffa9532127d6f7279dfeb7993fe528a311b25229f49fb8840ec25fb5d303e7adb84093986bea1d842ce6723a7cb6d42ef56301da9a52ab88ef60cb61d6118aaffd55546f552a78fc533504f657e7d271545aa80471d2ae36b4d0e47a0233ec353c46800d149ea5fe1498ff1041db17cc66b885e87c6ed8b70b36c676c8836e0887e47b4c73e6dc08e51dbd299048f121c7bcf5c0a2849cfcc8ba046ad7be3ecac20920485d00431bbe8746af92279f672aeda3dcd2dbb8f186b9acc9549372535aa670a743243c1105259f029151cecf78441ee71d797708b31ac6e946eb9da8100c167c2082434cc4bf836aab5b3cd7e8dd85b259a8d08539b3b9426295bad7ee2f26e2435ce5efb9ed20f01319d0ca1ec61952f163a6f5370673d10f2ed983718334f054e4a084132c32ca7a1bf6415497693114a3d64b2b8d01a1ecdc6999dd357bc41762de156ccc9b2f6754d624a5750ed83871c7a34b70650acb545b20cd3637e3756202e6fbf48e644af44b32eb45d6fbd0e5526ebf241bedc6209545946b6d0f070d2fcbb5d77a3bc23331ed8e89016012cd020fc8ac3141fc6a9977267045d805"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f42cee754eb8961859920fa75611dfef9bf05305e20964b8dc6eeed32b9ca620","proof":"c4718c2ab8058b4de1467cfc41a541ead977c6f243c9544468ecb611ad51175c2499b23a86a14fea536d0c89efa64545fffd42505eb438a052d5d0a1f424a84aa442094acef523cd5cfabd0e6b93947d28d0955e96740a8274b907bb4eae6261707184c1ac25e8162643c5ed6ed6deba4d882d04bfb91b6de18595885e4a5a58b513186441956b56e22514655a20a39b95b0875bbb5268bee0e2f63b5dc40e0f06de40bc5d01f616ce95ee219b379324dcb6e9f0dbac9743104cd81db6e2f90846d510f97441406c4b97974e2a58ce0812822c283c1526ac8583fdfa5d44a20780cf052596c8c18388c7f9ea3292b2721536022fb9ca93626a96faf8b800e87d58d98502a2647272271c9ebb3da131029625f9f1f20a9759f37c8044dabe8e2dec57c8b6481ac993e03a1e214301cb81609e0467413356b82aaf23270586aa3ac0164e22ae30f7913a44a5f3954fa9065903c2f9618be14ceefd7914ee8ce85752854f74f0e79fa76615cc57f4903873a16c2b6ba4074b40bb2fb8e9fb76662c004a6e1050f14f105c39785d12690caa5dda73a61e59ff45cc59c938105e7e686e913fd9654261da7b4dd5b92a6a616a47b2bfdcf3ae38340c11e4678784af3b8eb6e05cd7fac1d4a6316d5c2146e3c23dc2a22a488a10d4f279a787164cd64614753b203ec76ec2876bd34ef10a0c1403641adae9083d9932545ede3fa8c5483cf4317f795d5ca04a21f65c2f5453698225ed3fa89143bb4e44ccf31061505b50f9de661c457181b5d0dc17ae62547cc7b746711c362f20e906ebb1f43d7c7322d19a02a49747c29c600dee69c880311b770a6259a8a75d639acdf7ca9e9557529bc6e995d15f03fd0cae6a9054aa88c44bfad511faef47f08add61491b29044cfcd964f4e80d65ca68f0aa7bf9d792863de35a84e494e5d7872942c7c82702"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1e6112c9df6abee067704984efd024d813c2fdb28b0c49dc33deada6256c0b7a","proof":"00e3313105062e4fb78274125617ba1dc16a9d9f337a689ea7004f80d33442675685f374f0abcb1a4f69b96708b76e8c0a471198d4f3d58c2a5d55bee26ad001106d6c6063a713063c978a8b3d9be7f5f398d8956451264f4f337b7235d17b779c88c84643638e6058c936f89690099956376ae7f5dc2df9b71759dbec2f01722b8c6debf90935fbc2ef8b5ed9ecc0166883b15a28f40ae575ed5e4379325207cceb9cc437d56faeca3965e055104882aea4c255ecb3fea1431b73be4a1210049f71ca953fbfa174d7a932cf2861a6921392fd2be3cf4dacc24fd49263c4a90084025d36cf1e16b412cbe1db4dacd0ae170cc8e45698ca1e8b166879989e9e024e3686517844acac7888508d6bf750a5d6b6dd76b2a465cedba9b825dc52350e462a6ec002435ad0c6f5d8c4f9fbe2bf02b16a4d1333bdfcbfb45098b0dff41b22db790419555a3932836c5be5c00714bb940646ab65a42e2c7edf85f042773884bcde546c94ac203898043068cfc8d9028bc9e3e2d73e6974ec1789f43e2421bcab7f76c67afee1f90615703f0d2522898fc800bc2abe03c3a3b62350537447ce9285c3125658b23bcc0ca0b7fbe3d1166e0f40744cbd84705c4099a884b27d327ab18c2ff8f4b6d75597a42fc13fff5592bd52cdc519fe05b893558702cf5312908caffd2034cab833c5604c808b8e15efbe47c90998d662f90f39a3726b606ea49c720579d6f5a9151da7b5e9025433255702cd9cf1a644b830c5b428841190374de5fb7acd0d39ad41e1bfe0939f03bd3cf3a7e26b486abb33716419777c469f5916c6e04797bd53d8f3c69f33039a629fb43d94630a5f362cad1a67234ebcae3acb8d7448f1fb22e8aa49b4fff9f767736775a8a1b09b6f25b2e976c0094b8cfe44a8f85b7913ccab684a4cf354f0820dc81f9eb9e9f4b26a10a236c603"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e85d05faaff6b51f6f5fdce5da25359110faddc974b23cf2334a106a1f8c776b","proof":"dcef04fd8dbb0e57705987d91fc394ca170fe91fff45ecca225de913c08ea5400cc0540410e9356596393a18ba7bb1ba43a0b10031bc92308aa6099e43d87204288b157f56a51d20ed2f879ef255ed5fa4f715bef57f8c8fe6daf51a3bed774cfe0237709469b029fd7075636fbbd068e642d23b7d412c429bb9f26deaffe94934802969681bad85157d99aff0c342737daa8f699257eb7702576ea3e3205e009dfcc672ea33aeecc076470824944dbfbd44597b2b9239df410c1a36a6268c08a5edc33765bfad71730cc04be2086496f09bed2519884db5b1e7b15a0d9a2b0abcb0994f79b828a8fa7f90f0166b7af6f05f4eebe049856bd35855e586cee04762f2442f3ef7cc6b8a7841e611be72ce2d6256ea89e80afd37cd3a389b86f00a48721a7df86fc1558df776ade8dcdc685a52c9cb764ae86cf62239330e00d66a04da8dfd15c5087a437191c58f9de01424b4d1ff8e2b52971136f65c4c2f8c3cd0946fc4bcacaf6e32e41158756f217753464dfe13dfa2c7ac04edaa033ed7262e040c093f531c0b9803baafe5e347218281c44cce19c8cc45746159534a9c59cc138a8305b73f8a691e73fbf2346d017082646a3b50c037d4afc146c90f2d1616d766895c8d0c85b92d893c74d43af08d5221d1a7c432338c587ed9647ce4226af2a4c6cccb5b34c45428b416c4c0edb5f168e4f8b4295939267ea48378260cd680057f489ed7cf7a5c40e311bc0c248ca77c3049d8aebeee736f2926b8c87124b8415b94dab7c634caf5dd3303c3ade11b134adf76662d8e4587d256d8e410069f8acbe1cbd9017f9d9b5a48635a0d055c59d9863a800d98862a668c6a392ede41786153e2ba061be8e5a0831a97537954e5bd6218c63814a779de41eea90be5c644d5edee719807727fc2ffcce23c1074fb32f8bd91e312cfd37dac4ea900"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6a5c247320fe932ead42ff385e902aadcce5223fc1341bb7550bf7c8a836946a","proof":"a2f1a0ab99047e98bb88c32960af78b9433c500ded12c101d1e86c8d5adabe09381314d80723a2f640a1d874a519e0858b9e2dee737db3660991173358f99f696435c4e51b4ac323bf93fc6b5e11ab3007f437de61f5ce207c882ca3af36730e30d6baf2a5c6fb3e66e0b998d2c066302c922fafcb391b9491e9097cf53aeb47046aa33636fcf673ee33d27b2c565658ea3c0af23cea3517b5f97d5e23d7f30a364e5ddd1b9d365c97337283e0e68b6829b859abafb88c9fdc807a3eca052308bda1693469052594c26f3a6821651cc1be8875e36e0ce21c2a5a9e4c8811d00cb4af472b975499615abe96c4cfb400eeeada4d7042291c3b3072db117d42762668b96ebc9b34ecbe3fa20f06de7e57f16a3fb44e413c3b74abbcbf42c1d5994fbcced5e6114d88a862ab152d8b52448745b3eb4fbde90ece78c6dc9f2ca5912aaab23aaea693b2c79a86f5466d76220a258aee1ee02e06fd3f60b62bf025926ababa838e71801353c77704711f9d66f9d29d299b4b397d04c3eb02f53cd24b03d48aade659a21b295d41b83284f17c413f990465ee2354a04131a178d07f934700b68ba4a44855e792513bc052e6bf3cdce53e089d28b58a0beb5f09a1fed27e12077c2f4a15726f6681e8f35840605e1e9665e89b53e8434461473be9b87f32b081bec411f5462c949f35355b3ec19be8d079c55a2f4347849179215f0df3357caa5a56cda20e9db4675369d700c35e14750e5733672f15a270db8158f6616d0e2e5e4fb794f90616fbbafe0c20bfeb040ba14309f8c593b59b014ded196d02d0e02dbaa37e6b29b7aad9bc9c624e883e8b0120262d5deddf53dca72693756431ee76f5c22e067406685f60f32f851326b02816bd3160816874e53f891c1f032db2628cb7a4ed13ed7e16b57c2927a95b79451484da700256e00aa50f308308"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f0562c3c66070b56b1806996e81018fbe48c67e3f7fae5c5301ed05d787aad3a","proof":"9297a78d2c48b8242e6c55c62f1fab9ec71e5adb18e5f5c04aecf211375d5a1f14d2736d24998260ecc1ad1bb650150d9ee19e3468d256ffa94a21064499f847ea3d461bbac1d0da4a51f32eed1f453be378b2146e0b270343642be2a65a1a7e4eb57fd28632b971ff10bab1ce81ce59e13871b6e40353e97dc327b5d6733710a15c8226fe7fcf656c73d0302933a09ee29f4f08767bf343792f10dc004ddc0e4d713841ecc3dcd63d4b6a83a1c4d6a311a620f6c92dc8d8f708c81bb75060028365c8d671250b07e73ae7007a7af8a753a5fd09f53189d73d9a1ade0db4210792f7d9aef712ba2fa56934ebdede960a33282facbc97f90bedc4df95d4400f54e23396e6514db74d64cdc9e3608a359e66a1e5eba8f6296bc696a843e109bd445c665a076bc8898ea8eda7dd9b5244ed67ac91567ef678e14d3ea23f88a30406885406ce6304049056fcb963aeab5fa54698762626f9e45e8c7bc136d1557108ead18f6303bf5ac949ecf9ffdb8c735be5137955f62d55e6daec31909b13e227a6159ad72e2ffd29ae5eb67587ec8291486d606109abc8f76230725340abb3303aa867f8e52ab89c1ab0b5e7c3eafd1e97326a6cab51b62da2a166986976fc510ad96f02aac8bce0e8c87b9e69db4814613ea4249fc67f1d084809c71310e93dd039d873078113efa9614d0409f25ba41baacd12ab3bcfe2a34355a6c328c920fc605c64d35dfe6d4078535689d1ed2fc2f9ef23c7f795ea945cdb563f3d0013287b7de0216e6f94a1b11868bc1a2957472035c0b9fe35dedbb3085014c16619da00b6868fb1a749d11385e5cca2eccf5752ba91331160add7b948e9e321ac301c4cc54294e5034ddfa8586209521f1e07093369d661842e46b781bd12cf120ed4c851bb18350e40526d59cbda1e5bd7adebbe7a001e449cd30615b29b162702"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"180ee32be6dd84b12db87e092ca9c38ecc8b9f46123ba84ef8d283333103fc07","proof":"fc5d65c8fe4ff9ca9af765df499537a4c1828d4c4852a09f0d2477dcdb79847e2a836d36816b09467c47b1e0ca927a002f6116f42eee181349f0684371d8c45610120273409444cf7c45b74db78dcdbaf5d4bd6649ae2d81302f47b84ca2bd6a5a5c5088f7c127f64cb75fca8961867f27732aed23b3512c5ae8e9e72dfce42694e43fde50b4e0ea418b97db73851dcef62a96be78f5e73071fb9bf5a3027a0d8dcdd8c37bd8f2cd1a4562470ccfd8893081c269f65bd9d8eb882fd11d35110e1f238662c25d1e5306282e8087ec8c01303360488076c7566b645dc004ed510286f5ea8a25363d259469c8f9c5235e16c621286084acab5fd284aa6e9598c863c646688b9fb7bb87f7283ccccc7f93431605bead95a4ac1ffa649de7c4781859e05df36190efc51578fa53179e304086d2f4faf3bd3586e8a1fd149889f8486ef4814e5e5ef829cc785e07f7be9c185457e702fe87ed1d1eb02b5a03ed9f3238563735d3531f70f171d9b6682490840e1a9815fd8d945cd925d73b7276cbcb066c57de70939012e8db6d681328d13b2f7de9c835a3a5eb3205f742761e0c9d2fd0274234de96f24139fa689a679f246a937659b0da26042ea6e0541f7b552d4164ff75998ff54608bca2abbde34506e6b6b2b312ea02d038e03697e9b3de730c4e575df5386ce06474ed2af7b5c95bc8a78ab31725a9767895c8c733e581cb5d3400dfaf312a7612d20b2f10682c5a29cc7f5bd742941b65d576e0fd7d138b0ce8d359ab5a7b02754f14f56d72c178b0d076ccfbe4d86694b2892dc770f90c5d687cb5f41595daf80f5988336e3ba46b8d422a96e88ad790ddef83f5287bdd550364623280b1a64833665979a6b2e97468502814437f5bb6ca5b7e27034f0e0f91c0995b623d8a42a81b595f8f6c4bc889e4af9ba58c7b21b265c6d33cf05a09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"966bce253afe86c06db5e77cb5933a006e88157289418cb57ae04ef425586044","proof":"ee1a31df2c0ebff5c52ae313b9e3131adc07444c0da6b41b1458e970ffadbb40bafebdc34a67611651a300f03bf9f63e8b38a733f668a57163b60acd3242200c0ed5d89c38a63489712abbd969d7dde0bd88f1f8fe130508b1bcb11738d26c420ad1a4ba06fce11fb219250d6b561d358fd55f42af5144dcd7cc0fc2d7322343a82207da91709f0b9f6fc2d3819c935b7f7538949beb0b130b70a2d1080e1d02576bf78f71f23a56c5622ab397c55d4a5b46d956f9ca695045b67ee8feff980b6bf06980ba39398b97da060da560d2082b3e47cddf0d3d022ed88ce40af24d0d600e0719acb2ecc20a54ae228c57ed93ed5b766b5420477c2e6204f6fe674932529ee6bd3af679552fe729c40e09abea55a9a23bba0827d193bbee9aa567bf50668df26ddc9fd98917a75e23483aa6c3618572c91c4c8d726b826192010e4d4748441f4a5b122197dfe6a44c2ad7731c2ef56634dd5382119c466d0900157a76d4e22ea5a8daad1c85eef6d393228eb96cef62f2ff50ed025cc8534a186551253800ea34265ab3ce5ea27cbcd1eb84113ed5d81313862ac054e540855d989f36c886c959662f3dc04e57852a32fb3af11bdcec8844e0d9df80cd24e9f3fc3e0fbc82c3d84dbeb4b797f23be2762906ee0c812298c3c2d33a7d4d0878f4f186600498382a495c7040a6614eecc418f27a152a6b5237fbe8c413326fd940b94b4c5cfd74113958eed5bb90a485dc9b8ef43a9752dc530e57ab00197617609ad33dd8b6c9b5be959d495d223d881b0454cf80fc83b4cf137edb5288881103bd9658388f39826dca2b6cb90d227c0579313d0710a83b23b8a9f84633728fc86abe55a23bc2f40b9d879e7968eb2f07a042d76b2dd4825eac2cb3a1ee368d454a3c020573d1807ca1122004033ddf5f755252b48db6984a86ba0864f2462a23dd490d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ee10c9b289b2cb27498f9da49d76ceb97b5560ce789280f06517705cf9e2164e","proof":"9c7f1655c7486e9484dbc14e78d0565e7a9537dfb1263834aa7e0645ddb1e6742cbbd004193062299dd470f534e7117a4b077c998b5920d2e90d07aa68e97e504c4772206b21f8f6c75196bf653fccfbbc49a599564879aa33d778c635503005548c44b4d5f0b67800486160c806ebe30741a85cc06fd87038b56c64b1739b362c5ba9b4371fe39aa27f442494578ebb6be51e45452bd06b1921514f626e0603f2336f9c7648f41edb6e9a5fa449bc655b4a8b672972fb84c5649d9d56584a01f8ef7ea6f076a581c956435a3851aa5f35272b45c1db9168e7724e52419ae50a0ccd337d134293fd72afbb3b5eac4768e79be69536ffc9896a3bdb142785a320142b44117bb133767bf21b4e2600ff3b66fe95b60d17cd3967a6cce70ae89149faa7dc78ffb8a1d8049fe7184f4749c19915ea2c4abbe8aacff1880f2abc5b52ae7cc9524bf3e3a8d6c5c0cdb8bac11d930afd1075c0ae2998279a77192c8f4a640b7bdff01d2af8d79568a50966ba7b231a49fc744f6d1a43e50e168afbbf26461c09df8ed262c7b5ba1ef9f3bd0a7b27147fe1d82a269152aa219d78f66f27d811d5e336c2507701015f6ec478cbb451a40ec7644daec07282c1e4f33149103006a7dce243beee6f34536c256bd60450fc12db91ae4dcebd9b6fc20e6c245dba7b93690227a497dcff7e28c3995bbf1f25cd4618aa4339f7635dccb47e6d0de6f1c90e33eef619dfd8dcaaf896cc814119bb3aeb163d353e4e48c5a69d667090e0633e5ef7a4671b242f0d55fdc4738b6d1bc6270e5e545b903d2b427f25732a613c29b88735ebee5dccc83aa214689aa3b26f2a81437106078a5c078deb47351e739de6c0283b506df9d7a6d4a75192c32a147a48614f805710778ce8170561ac7fd3d4488fe5b2fc685c030dfdc098b66c1dac738efc227fc7cc7c118908"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"be051507b558c4e81d25a41e3a73d5cd348584661e2ecddf5a47b9531a801f5c","proof":"e4ab030998bf22d2f1c471307dedd41f5787819a3e5d47294061bd5418bf80514a3e3f57b36dfcf3a89edfb12fa740164000b7d7077c22ebe3528217ebb1a66c12301b328dd6d2c3775c9766e6256ad920dd62ec659c38717fd6ab9a2b5787362286019381e4c587df59a9960c226964731298b576716238f3ff3eff0e3eb421ab91fc5a0df13c885983b5c6d2756678fc9d9de071eafaff7fb5a0e0f23519085b8f8599cfdfa06dcf231110348b6466f65179349413d2a234cd6200cb57c00792391ba744aa53874081d03bb6fdc079509f3e8d83a0e732b22fea5aa1ad090178c2e65ac6997766a7d904374ff5c49dbbf674fe427f5a0131299cb69fc7263ebaa7ab81eeeb399cb0f7065f1de2a231da63ffba6748939035262fc5ac98f94ea09dada342cb1a8c98cde4f32ea460f18e5f643b7736ff93415814825061fc174a0596995cadfb95a033b89d36db9af798d5ce7f16d20e0baec1e8c0abfeb0163caf63ae04711900f85f70cb86b458358391a530c4ecfe6481b56cbe5dae7a55600a68e410eb581ef3ca4699cf8e135aedfab3750534f11bef7e04686069d46418a32ce0ab07000a95e3133f79b2f48ee1f602941f72e676cb5a67928637ff4dd867ead4ca6b2580dba9aef14a95744d6e20b11c665027a617191ad945c69a6988b28d7c180edf0f5d40e6e0f1c99b774b0449b9bc867f4528bb546827deda380ca2d75d9692baf123b493bd47d81389daf9b14fdd80c929f3fe40e496508d0bc4015caea5f9be73bd5bf86339801dc92eacea0bd0a77a50562a6d14c2359c55fcb04435b0a3273e7e28afb0277e1bca64202c2734a87f4a72b12338ec23862c695b1099bd3ba825872de6bf5414b27d48974dd192876e50c89d627f78415c03eb8cb0dda328caa9b5ec7f5978ba21d7834e8a726bd1e77f72d3d8e6f832890c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5853ac7cff78b6678fd07e5555b36dadb0ef189c55b1f7764848bbb615227f5f","proof":"cad9f7c8b4090aa765d0bc324b801a070a0f4f69691a93666f5839ccf87fcf69ac09d4d3ea7a943ada79eda03d66b03141346fa6c79af27daecb53c681aa215e9a95edc83e2f64203cd23eef4f951dbb14833eb195c778405f98c5d1319896606640d227bd2740c04f304925466284081c5b091fa6b534f02ccfffa17b2e567d67b6dca7fae2be6da541cdc0fe0347c6d12ad5d4b885681ceb68e4ebd53f6001ed3183946c18bbce719374ce0fa3daa0ea7f27a5b315c470ed36132082ec130fbd0d7cb474000e1bae758b45c9c00cfa0933d91920c24dcea3aac55705e1fd060e8dc08c7f036f5e8bd866b2237b87e89f0853c1c6b12ce8470d53b9a93c6d314273534df09107ef7fe850b1e661187e0a06aa6fc8c6021bad3e09a253a52218b0f0d4959c312e854de16e757b9fa8410e74a8f797e5d0574464017e30ed6f32862dfa3b761eb8b25c4609322360fd832294730c81bcb85a8a71a78ef15cc755be6eaf312d90410d158c399813f7555f89599b8eca2726f655ac8938a43a2e06c6cc23e17f0193e53757e92e529f315f8e45623b88a1f0a79a0e9f79ac1f9e791adab55a5649a0e43a8c922962ff056135aba2fe136c249f444bf891f786b7573eb0d39b7d0271bdf5490f3878148129cabe61bf2408616bbe2cb0609bb54f19c8b78c434d527ced8534b58dab67aed0dc4b48b75ae640ddc53f7d5cf061104856146f19be9c622f1140a6adfa24a943ace84f888c50516330bc44d41b6e6008c88ee29ea3b0c6f4ba65f96bf9f656b33772a0d186ff20ae03ac2c566dcbd5717a3f0d31f01cd012dde787a4c9a14fbca9edbf699f7f931fd92caae1e89d796653cede90d65021acdaf2fe381a7b71dcbef67ea3c04e3ce80473a95ee100b5075c5e0de03efe06d86ced6c545eea49f086cc59f8fe0d860fcf52aff10acd7d0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"547018023b127bf87fa0d0b31d0e172260dcae613319d423ec68045cc8ca6d07","proof":"5853a590a18a27089d5c10fa5909eae1f129eee4462bc8ee02bce9af5568ef0d9846bbee30bda49c8e7bd697dd4e3bb254ca5a6db50548124a0882e73c42e41f12f52f6fe19d3052862a0989f0e98d6aac45f9a14f6015c3f7cbf0bd78c38428e0e71b592572ab3a44918b3b4f17f83a04327cba67bf1ee4d3918ed0d2132e1adac97452d4f514c1cc6e9492160679c7cd54a518d22f18dbea3661e7cf23960122f3492297584a60c30a859d5d0d46dac28c2f522bd45dcf8268b8489edd8001865b1392406e439d1521ee6ca4b30707fb95a5f32eb38e03153f0f793f87da0210fd67e0d2990f5c1c6352c01b7ea03a0117b354b76799c8fc652425b089974266913a2f10cbc5c0c7ef34074ba56b41a63e65b85e2da704ac1510f4e195fc1578010c8abc06ce365e2f03a8acecb899a30482e2db496c869d2804ec391b973d26818a58e5a48469cbd6ba2c3a5af690b0a616b8cf3ace60a1861c4fe9968d38a6477cf1c1bdbec6ea4322ff81cc74d678ac5d1870b0d700a4fdc0898ca06e18fe8dad55adde05283de83b9256385b4674636a6ee1ccb42d97ca15b1a4441a76f2d422b24f1255fa44f3f7586b4094fd09e74e0bf423abab637fc3d8f92df90c18d27adae026b4bed89b3c94dbec2f0381c932fb3aadb688d370e268946b77582426411390dde2f1bfd3f3954de589d602a3c6d1317b5ac1dd07f63c41e97d3a8852ee3ecd58885af035e5d556fb91f5ea6482d687922757a7ec02056f494d38e849bd69aca9825882b876a3d936ef6baffb5f7e9b88fe346fb0a513694200502cb593a398540ed0c1d9d9507ea2ff18a2011bdb23befbcace0095fef5aa1c2e70ccc9a7daa6d086ec53edb03e761a8651a53ec48cf5cb53a9ea315cce0cf60e79c5c50e9d19c83538f7914dda39d0cc8484ea3045dc6c4a5f4f99d35965cc07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"88534742217ddcd3a142ddb74e0b7678449749b7532c8c28551666554a7e8831","proof":"707685f65f86bc6637f74e6f22d87b5c65730156c7174dfceea1006f38661b4004386093de301776e32face817fa59ade4b5f0b40c27d6326c6b6791a96f116ee6e61fd46bc92fc4bf692088cc200d37b6b5e6503ae081a08020cfb3477a1c6bfc9c57d53f81fcb290afa6bebadb2c0e644cf5ae47d4846e13e6c6735e6e6326238825f7d105ea28ec9577d7fec22bd2f1e96d4a25b0946a979f0b1afe5af6024a5c86d06b35fd47143df21b3bcac9d7d54d7993de120b48ab736b6426fa3e04f436190e8dc3ddda0f65e381ca1b54f93f48b38fb497e3097b10c1bf88142505fc05731f525262c6ab4a74c5b885f2470f435f10d742dd19c6eb89286a542f653c2783c825cbf7d23ca4f19e927b0c1266d579ff61c1066366b3098966b11047746bf927a1868936b03e49be3536490d5bfcc7f5f526ff910e8c71695f93d43dd840788839ba02faaf5b1c7e952b7de717aad86e829a4caec762e9add24c9d10ea26a0378dd01acc07576ab2fa19934b4f4aeef76b798f9ce34a4058f9b2901288b3df5af5eff7ecda927479d615e1670887a14c12f1cc28918e671693deb35cd4a64fd1c2f08b20cd8ad1159199ef3cbfbf35bb084ff8509fb3a1c7d5e16f1484d63e04c244ecf4445dcdd692b9a06d1779268b3831e4a049fe3830df672e17681b5f7bc7174be6c1ee46db84270951cebc8de64894156aed90eef0f6a05477ac07c7020dc356ffc108533e4e489e8304fccc7b317be6a3f70f0b21ae0bf4301863ac70c1798a44fcbc3435f02017005c85f86fa940f5a1f52cf2aa51fec46c3646fe2110bce2a5c61bd2154a09bfb9e233c16372faad083f281ccd6c5dd62ef4ca32e51bd17638d04b5872895ea2f01675cdfd2f777e61e67ffcd269bb0a0d2ea50da38bfe00f239e82dd10d10a1f12b78a703d1236f1fca2ee0aca8f39806"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"542d0215b3f28468f5e6c53fd5b191d83472e1c2cf407516a776c00421c9205a","proof":"300e66d99f418886658686bd7c56396ed9030202aba2dd2f41fcef1eec293126046fb0c1d7debc72558db14973da5ae4e0f6c1ed558c6f928c1e01fe64014347b896f4e39415112796952aa3a37b4a0f25625455953a69af9ec4db21faf63d65b02396df39a4288152b13f309bc70fb8d6295483c1d6117bdb1aab021a944c0d80c505824274963efa3830cf8adbda0f5576ce698d17fd61ad8ab4fbe2629b082d014d48702acd8d108e3a4dedd8e2f53735e26909090108d8972f902129530f4f04bd4f5496145f731e0b07d4f65ff13700ce0798ec68f101eb6df700d7ed06e45525b4e9335893420e38082de42f28005cc4ad77e65a6afafcd480a4ae393d6c10bceeb0d57d86873d05db93dea8ec68d919c623c93759955029c3b5217371dedfc31671ef5254eeeb426ae67b90fb1dd1ddfdcdbab18a80dc7cc6678d0455028090bbd154e8e3da9e8028a556394152f569ebe3f6e054292170917213e808c226cf4a1397f3d790d32b997dbf715cca16ec31fb36b076f39bf53858656c51f605c0754a4140aab0d2829815be452522a873002ceda1f4b6b5f189f7f67c6c32e06ca24dc2fecd58cb589e011417f56663bd5c8c474ebfcd3ce51c4d670d4b3a18d8f107f9c87af6ef4488dd8f12ba4fb4b126985274d44e0ef098ab8c7f06963b09a5b88b508086de700e014346a9ea49162eb913d95c17d45c58ec4fc6263c36bbc2196ffa208339b892b3d3c817a74d4ffa70083a4544eb14ef8a1fc302204998589b4cd0c359b14a1d3a8223199bca58b5b6de257d096ba9b4c188e14f2ce1debd2fce95c2522674c456d8756d320be62fd6804f4e9d9188c9e956354749c72efa14f5a0088c08788dbab7f839c2bd11e8f374ba99ed9fb36096007f07db32de93870e8d81bd5324d394318be86ed54d7fd4aa175f8c2611db36ae4305"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6cc8db241a0ed5417bf2e2487672bfa2aec4ed6f3c9464f42e80157da959f300","proof":"30a46d7b3ad48fa66487a4edddf4f86dd6128abd48a250df087ae3400f1c3d1ffe8663a6235e3d0e6abb74a2878a3a310169d389aefd465f22ae0f22f191f72e3e2eb411ec43aad7104f68196e1f27f6405cf0f67aeb7824ee0c4188bd490a748038306bfcd7363df1e80ed16301a4b485a4b0ffe072c816bbce2819c2047c18f47397bc8d44d80de06bde91a53eb7e455b3405f67dfc12277e370087dd77000b9bc4df1579cdbfe0414e3c4428d692f9756a0ee4e22a16cfc8813d3ecf3aa0682e465fdc26df97d80e172e4a5e20ea01fdd56b2b3e1ce8768d3b15952713c0b68f9e9d73d9b2e129b9614e116dd736c20e590b661b6539b8a8bcc5cab0c451ac868af67625bd543410f0cf2d7c413c8e64445b8f3d16dba870437ec1693b125146c8d2afcc6ec34362f620cd005da2d449dddb1a514cca4196422db4f07f553bcfbc887f85af9ebfe30379739dde539e532e58f971be5ffa60ed2d1ef23f63db8c28932aab60d97a2d5719722fcd2f30e194007dac0356a23da77bb4bcfda5af093e23d6c44c267d0897bdc1ead5675b1567fb9b52942cbc22c6a9271c5d36df4fd2a2087636b7264020eee132414bbd2a2ce5b31b5f581ce1dfeb50b81312c00e09e3e0cba9c0bddfcfcab8da2672039d64e6ffcfca9cf9dc1c0a219497f7c881bbe8650a49ea3a42fe978f0930e01dc03778bd57509abe77d50c27f00e066a2174ec3120fad2a184cbc4ac65bcc4550122ba48c98d6d7f680c471096b1037ce2980e4ad19d3faaf2ccac697375f74a984a324893a6ada6dbc638248b25a7564dffe132130289c2d260f2c6c8d023c14974860420204a7d90bf230c0fd0a3ca5551a6667c81b26c169812fab6e6610a60c5af73999e2b771912d7004d73209fc6e9d8f976f8cd59bb0747ae51d8ba5f4b98463f5f8f4dcb64f56763243f00d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e0e1d8bc70e2b5621010e8f0be8b24577ff50d1db034883677aa5c7afc05ad4c","proof":"68c4c3abefc0d553698e137f3e68047b768ebbbc33c6dbfa6c1e009f9ce1ef1248cf0151acfde322efae6355a50e820e8b32d58088acb663f3bf534caf17a316f4b331277689192fa4f995035bfda96ce887758cec8c1dcb27d9f3c753bb9751486fe4db69961e0176e096d257c2c93c6f48d93d3cd39a15bf7eff07339490018c2c98ab4340aaf0238db8a7c8c36c1b449ac7e71399aa6be06a4cb9eff0bf05207fc7512fd16566db76bb171b853f31e612b12e508af883997e7e50f5964e0f0aa6109ca1696e910da1e3f5704780ac427ba1eeac7a24995ea5851c5f75e90722e48e7ac613674a65a1d911af8c1c6620c160cd1e0ad8a1965bd90c11836e2884f41d0e1416e966f1638af57ec5cf0a223f379bd00f633a69f5d091d6a4b47948ca929ac1ccfff03a83d91a5d8c7481825a0487355dc3c1cb86f9dd367bf854262f6d71e6356e87e6d3b9ec2a261020e7b750218d32ae843dfe83f8760d72199e619da174d215a1c00fc9b7f68b97df3251b92bc267b0f72a655717084df36b0aeab91a81d6eb6bb0c4b79b8f19d904b337766a0c95f2176c67af709ac0863d8223a6f1925fe78e538b6e576ec0919153ef9b114a288b7ae9e373d7a1784a47d43656207c4576432ea8f350456ab54e7691d88a07856a24ee56306798f8b8505494b5a763b0d0f2829cd6110c4e8f99f9c6c02ce2c464158b07f32968302d6354032170891f2e1888a465f489e388f98418207d625cc64106b763051caa210030dd3aff58c215799d276195cf7402bc0dd77633f25d1558a2f74f004b21561a36232408012c7bdcb400e497bbf3a5f0e9cfa1824088c122b0ac071e66a1ca66f74638149662121f12089762015d8e4de59aada23d4a26f8530abae991418c052e26a0f5510ac0dc6f3bea194da4b1a9ffcb610421cfb92473efb0548863ab05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9c4c8c8aae8b45063bbe0e515d0df605b6b907c7b1c2d034f4f3c1dc2448b458","proof":"c4f3359a4859ced894405cc9f123d8b6e0b14bbf2667b1d7d0b619a6cb17a501ec72c4d993f93783ba1b69484f3b2fda15389918e56cd211399ae79d7e8fd9763253118e06dc414478327c1d60cd896eb569ec5328d9e76f27732cdcee002b351293cf1e786f14dd77bf433a8df150172281749c64337b996bfc63ee86f77445b8e753b46cc5b2b0a19e1124dfd67c24c90ac723a7e169cb10cd8a94520dd40bff3734e20df9fea57cd8b721b009c9eab4cb2512cdc09f1f42c533fff3f97006414c6d4275c8d69f0ee24b0e8739029852bd90dce9c1ace05613e63580db610a9e31aa26679191d3a9a7e6d22afb1ade9568a7ae4db185b62af50c5c0b095c73a85359db4b5469223017ebcde65107cf2438a4d92ea8dd6748d8bc3a177c07450a50eec87df1406b57b4d7837c443955972d2eeca5209827deaf11b869dce042cab050ab9571b9235dec9642114889811400bda3f714ed1902cb2400aba70828663c37616121aedfa082fc0bfde3009bd8ff295d19c552b7f84a1d780376b75102236743a604c9f9c044b115b6798d31fd1f0c1b92bdedfb877506488b8e204108dd690043282cd2d669be18eea8ec537e8c5568b8da2d4c1e0e30d64402ac79dc94996f4d7fb862c0b18bf0bb2a7cb0a8c3f6d4eef2d2d29ab57bee7d72b23c28954735c6ef4994cf7520d0b99d1a00aa74a1afec3250c3e995de8a2342046104d3207856af9b23b2d0d01a5f8c9d5526ebd851b6d67c1095c7003b34c3aa6e94b59de3f36a055bdecbeca32a52167f2f7676aa85f5e957ab4290df87a8935b7c2ff12003f4e985e855100d10d5e58a125803995cc4a9ae7ff0de9146727c7c69b4500c91d56c60babdaf3ae4a45901d0a645a77f928964232abade3b323d0fbdb053379c8f7d2aef5ca331c8c40181bf2e9bf9c8541666003cb9924df2aa09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e8a629685a94e7bdb879168eb8aea4605f93c6f5356a44f860bee61de1a8311d","proof":"a4c5df1a62518645ebd34ccf94751c1b2a49a50eaf4230e9ecc2c91b70ded6426211c691c31a0d97e14e31efc5ebb09f04f25223138d54feec245cb547ad3d4848655060ac80c7cdbc270fc5f8a534bd3dad5e0d96c384aa6c97e17a8660cb3ef4b2cb860b3e092bf84497671b9750b51f1f955caa93bb05d5d4fe426606320f841b1d5818287796dd0e8a1721c2495916145afba87d8c73d27a1c846d1a92075a2e15ed4b9fe5c50b5cc9715a8077e0176cb26d476d54c45479d4b35156a80b512af6a59da10d3d41a98677b9126b77464f6e894d5fef10946b4ac83dbaea014a11f32ba9f72efdbdb59e7d8b07c5cd5f052b22d39671b1ddf1be9f3299d366e23d0c17e81f03d6b47ae1911c50ac73becd0dbed6edc0e48e2b1ef04447c370aa2635f4012de4a2c953041b688485b577df56dcb9ad6b7411a5ae391c1e114b7a07632ee361667096516f2eb3fa0ba184831161871bda0ddb9c96480c6dad02188c25ffc33251c427cbef1d0137ff0e7b63a1cc299bf2aeeb0fea2e1e2fe06e5476d0b3cb03b088e08e7a594bd6012eb326b1771c0abe94aec8e12edc0d3c3d00565a40ae20a246b52696ee0459a44c75b5b860076700464515c09c7b07814966484e33d09a966e45c13a55f0740db4536ce474b1435f6c70880640eee1ac56544a295a3dbd267e5141fa92d03b3533e46faecc6363c0ac94748ae9e023020b18c2e21ef47d74ed2b3bc792fb7617bc24d82a386cf8a2c2441622ead47f79687af3cd8ecfca71f5d83425e2818b4ab433970bbd068f99b3815c92f443fe540740da7fac1a865bddb31437f5543404019f42b502008d67832d353cec7044907cf12fbaf357d239285a9db8d9bb2f9dc34a5aeccd0893382b587c06af9e55a502017fc2bce68af1a00ea5ba483c9230071838b54a681dfe9b707d76ba5d776b0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c8179928c4ed1f4c72762e0f8eb4c26181dbb87c4e62f2ccc028bf50522da070","proof":"b484a667df029c160992d8f12b1a7c2d59d7a060d360d4979d53c4a0a2a59c36cc1c4c6c5c93094b1cb538608711fe8881f44e253501e9a29eb8ad6512ad6c3a94dcf3e65217bf79988b6d4f9fcf5016883c74c7aa94411183ffda6d4c40021e165febedc0921ac33e5faac39eec5c7440f8672c3d5ad78aa3f3fa8975336e1a423464a271676cc3ede55258c4a31a39367c4eb5060c9e0eabaeb819d40bbf0722c58861668ec09677e05d4f4597c97bf04e2f520ad5aca051c94c887b08760f89eff52399c40cefbcc5e2d727a17986c7a88dc4a749ba624681aba99691110fb4c8c65e15738a118e0e1d9f869280d4de2cd1458fb11f8729c7fc050c0db80fc4492b6688c8f610bed5615951e13f9bdcaf5e509e6a574821b3bcf88258e526f4c5552fae063276ece3e2529c1172c9389b0a84310ba79c969b6408400bc75040dd640c72b4b1ac3eb4aa1e7d4aed33a50898d917f53178694b6c4cc20ccf237a5a32b00cf679dd59d378b0e1081779e48c2c276b86551bd77290e97207924522761fc9893f8ae96d59bcd691be09bc25754e34d3a3c3281b18b2600064584c40cde7e6eb2b82fa310d82bf364e6fccd9be8a65e9ed6a55e6709eecd4949975b2fef4b5ed83dd12a057b2714d7f12d235a89741e89dfa3cb8518293dbe3dc422cf42fae115d332992528690c79552276f9351db79ad3295ba344d3e54d6526100ddb24a7d1b3ff77118edac08ac1a26738f494f3270260fcf07d93bba2837541412cf4c9d3c70a80a4c6d7c58efdc058f96a52db52aaa7b7be3d4769adf19046aca98d4a0e0970564d21ad02893a5cf86e66e349699436128f2dd607630f55a3beb1e89d5f90fedd4a3c729a9b7d15f4a10f4dec6911412f8ca5a2254b7c309ef260a8cbf6caa7439560a893c106b7885470f1f90bd3937ad729a635b3f0a0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8440109172b097f8dd348dc11f629395d7cec6ceebb5f368cbbe92a1b273bb19","proof":"dae48cfb88beb6c5f3619b1f38b5c728993a3a2bf0fcdd6f3ad429755c614561f42d4562c7b32e90376262f805141f571b37c5372bfd1733e88e086f8a61a62b1a93754fd5ed53f8cef8b70d7ca72f1f665db4ffa820ea53844e0741f5eede3340f2df9916c9c56c5ce10019c5a9f1ea9b6d98b74abed6f43e7614317e53436a37ee2d19ec56f1ae84a986751dd6ed5b95165e0f44c4b6ae6ff4ff540bb15e0295cdc94a986453ef472035b5838d334c7d3b16f527cedeabe14f43184f63e80a3b1d355a70dbe8715d095f4a2edc781184348337ec85821084c5e9818e1320039a1c6886eece81810640859ab93a47fa8402921ce3a41775e7b8fc5ea031ad7a041ce0ce3563d38045f4cf1aa44b711592147607511d0b2ab6200ac4d2c40644b8bd8e7ed5240bbcdcdce02791cceff1d5ceb7bec462a1be3083368f3ff14139f8c29f06c39cda515c20187800bebf3353d0f297c6e6fdfc81d97f023f4770665a43cdfbd50fb64a533487c168bf5af63d94735257a7a2e852a0dafb37805310385dd054034c3418d151e527ab99ee3d4c0d8cfe1267464a5efebbaa489d8139f2b25bd46cebffc8737d546d25f72bec9c986ce6405ba0cf84550c0923c03007fa7fe01a55abadc25319c73fa376de237784f9861786838a392468aacdf95235707e4fb2dc11f3715a8f323e56ab40da41a9f54d3a9bf4fa9fea8236283df615a825f7d0814840aec2d6dbb1ba1ab17c1fc4bc87ba626b8376243751c383d4430a104da7b19ce008b53a67f9a7ce7705ddc53b28545a4c238a4e6cd37206e502c895b4627778d5bd81479e7801887e71ced8a7ebacf3d05a094574468e15802c2f813c4d329cd75196c2f732d478d0608859419b3963c03d63b16102590d260f1fd125f8b9a050c3e4daece2b2ee4cbca173f4765a9bbb22f2c6414b649c330f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"680e662b405d7e2370629d3021db18d5de5574905b665ee53ee441a966cbfe28","proof":"fa0d8a6468bd0323d32d4fa868b68ca4419453d29160479e04ad23d3249580545ef18830039eb73a43b3a4ae877081e984dc79308f423066bf4a0a4e42f2434900a8360e3583953a61436cde3c6ae6a6d6e4fa9d07af0be6a9dc2ca62ce49d5632990d69c359c8ab99ad6320781fa1b1712d7ebfed18263aaf6d40fc03445d6a7755a074638721fd3522bfe477bb0fa212dcef6acbfbb173325c90192e796f024a40e80ab302025ef25e970a85c1247d5153113f83f41d5b5a4241faf0629f0961084edbf2af7e031700c122d696f79a0f9306209d869779e8359ba660306c0766fe948144f185790fe91351420eb1363d9c99f8aebd35644e14f0202fdabe5160eff0c981d3c38df7b456c933f7965bf14c9250eb4477879938fd021c3fb9182036a0f51becc30bc90981901d02f769a8e5d454d92f4d81fe4c4afb27aef90c7e0a48fb3c78d0037fd7fe1641ae1785a9a05310aea9546e4f893f1b12c32642a447f853973c6d858fdde23c2090816c39e506e74953476ed4dd3f16e5571a3aee583a75a4b2829ca6f6ee1539b880867c7a395ee65f69beba016017bc8b9d7da8706af3f45764e145162337963932f77f6fe017a0275b49f150d1b89d94f75cf895a8d1d756c848dac8de768d758819097b7bc1875f4db3d7ddd2e1ccffb96128c334a3c23aa8ff3b0bfd666b9cd8cd6f0ddabace3526186a73a239e0dae54b420e7c9e00410be11d6cf03bbbae69cd6dac1f60a8549951ad3dfe9dc26e3a57eea2d8808cfab3e6933a0893910dc37c95b0d942ab9c47dedc4e28d2a6c7ed1dd4438915ebc31853ea16bdc750720bbe357247ce9453513e3010efc5894ecf15780ff1a45b8d04d6fdbab5740944ecc4756c0a3cab8283aa6ece21037763270923ab5dd609b97fecab652b9eb4d2d1a360a83c5a631b88bea84bc14b48609c0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8c9274af38524fd9ca5e87b2c1b1af1a7fa61e24152daa903970f4278ceb6852","proof":"ccf073d322b67441e243096572ca5fa0f35e2115c624915ee6606a58b1ba4813586bc7d7183708fe2936fb82fe191f38ff47cb4a88be5c10a969c4815ad2b40d202d4bda09213c93af4c8142f07cd43c005ce15c31bf09046323dee363bab46bc63b6149435a3d9b2e7b5fbc69c314473c79b1a569785053d21e20bb44171f21200fcf4d3bc01edf925620d709a107fda0cb463f73f0c916eda21c87dc2d97018a12948503f1cd6543d5406533540545b2a2fea7a625fb0d427333ae723b8e0fb86a2afd5d10fb17f9a675883b0800fe8d1e98ee2f055d38e19f5c92699066004ab08e4fa5ad177952bd2c605a1ad4acff9caac655e9104149b31e1c60eb126c02b851558e862d7fb551c81860480b669e77958432765a98773aac50468d3869c408bff04d76e2612ae4f6b16d05bf3656357bf35fcc34eebadee6ab7c9f5a0b9e4a57a2e8d650dd58de3f3dd28d2ee581b6236c73011c71d994c72cf77635594c9ceea7bf120b4ff5161a55b6f336e461915db24fdc8fadc730914c6f0af277f8160a823cd432ed6d6d0f0176879be3d5251856b55828189f3d168b2f608e512240d0cc435049d4a8cf60680c0de32a06860098b111f3cbaad357ecc2d1f85b98edd280897b53ad6c9a4529a6d301ef0cdbcdb5b8cfa9d5a60296c78dc7ec507aa062bd14473510a7ab16d2c0f49aec27d5c5a0519bc53d2d97c1ba64155c6cec8e60581e0f901b84642bb1558de3d733f193f9fa15675d1b8379907eaeea7814dcf269454a778d46472dc48235e62b87e0e028dc74c54a6c390c0f63a3cb581888ada96e86246f8261683573a60928e3b21cbce2baf9b72b7945a1b9a9e83ee0b21c3266dd554df349f9198db40d93148dbfb702989a211c4a9d00b6eb840df05d74214fe3a7221bb5759c588d06ad522c0ed86f3a4183dfa62944b4f1c205"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b84bb4d1458181c4c9e384aaa1bbd0398ac1c82e7e73cf085d50009b64a8d871","proof":"5a2fedc6430581ca03bc99fe7a382a21cef9f15208c09ddd205528c2cbf63200a0743ad6f943e9d3553ea4500493af9b770038955170cef9b71000e11c5ab0391a9a66e8b84996512d721c7e9ad45eb2b4e8ff4709239a472397e1bb0e5afd0350c0f276057b97d2c9edc1767948e24336b6dec82e522b154364b72367b6b265ba7ac97d790d697b46899659c06a6c025db87d3c379135a73c2137854018df095f4c4710514bd0b8d51bb04f00ec9b897d4518efc546da5520c10261b338fb04dd85ab8115bdbdbd8631becd47f999f9ce77b18545be590a517b03a3f698a902b213b4a6657b2568d38a2cd87f7da5cf8634f41423f1f9bf71a1aa7f2fa7716ad6fcb49ab495aec4930be41bc59bce97e7c3ee62d8da51b03a1a6cf72df76f618a99a9557e48ab558ef85b8ef8d35d83220802ef7eec8f1b6f6de942451f5770388c380af49d25803b6400c1d6726b749bf25a30c1658efcfc882b01c7368475dc00a45fa8b63de15dce5aff728bea3e846e01133c6389e0f390f18f34321723ea8bf7fb54e035a9b6c5b328d84b1e6342943c6f59679bef746f5774f007772cda94fb27a0e5c17aa16b2422c3a724f5a7fea3837de83580c59ba73bab04e41c88aa258a8045382c782141ca68eac68f050d34a2bff2ffaedae7ca8084a1d45d341fd010002baf1c0f99bf96a9b687a2f1df03ca4f8440598aead02b16ff1569c29bd498f30d8785b3003b586a1cee2b4163bddf620cb53a5f5c7259b8c27e3942ec38f20600866b3f60bb6f4f34ffa2e504d1d30e99ed49722ffbc6718e7d7e306fc2127b7269ab9b74327f1d095cd58488345f797a77d492baa34bef89be6c06e02d2e5e293698daeaa532cdd527f4b404bf8a1d1755af7378f5be07aa650f02492880b7fe3360c2f785e88a1a38355e99225519c545a5989b220c6092570a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c200035c71fe703101874c5045b834222dcaf54e9eefde04af94f8f31d63a53b","proof":"6a58cee62cffe3e4fc08a44887ad6323d85170114b82a42a506eafe9f38b3956b65ef5a6e28db33ec5e19967bbe1b5521189616cc9dad9e8ff81d7088b08fb254e3a60214acacf16f2e5eb0b524e1ce692c6ce4642f26ecf6fc79d6a5950801bb6bc8aff6470d02f3bc5be9dc0eae311414c405ccd13b4983f16059f573ca9770af458b9f1be1035b60cddd4616f8243dc0cbd2d5b47d8ab91ef9e7f94ee24071ac9d6cb3355cdfe923312a3d40bb56432a25549eb26bf07862c4bcac3eca00801d6032d9981a0fe9d32420947b76409a341205dc2e6de0c49b978184d9bef067ae1d383b4dd50d5d30d0c75b8461f965bbd0d2d8003491d56e900f6b904ad09be194af5c43a8d06f5a666d10bcaed35ac7e513100e18e017c3e0217faeb8a67007e02a37ed8f6d20e2c1b2d4901d3822b1ce3b2c0e48699cfa1639083ae4339dab9d826c120840b60ae88cc88ea4d70799ec8132803094f7884d16e768a9c3bf220ba5e9f0a647a51955c85fae7cfab4798244fc187beee64af63062945ef269033507f3b5c6088c31e361db856a0c9ef1474972db5f5a246a3f9dd2203983c3c13e2b1469a75476d0ed897c0b25503ca7a28bc3ef7d7c043599d62c1702f40802fbfa1ceecb2d085d7a88e584a7b18ca131c127227a4630055383b9f3e092d025ca0ed72a4336fc26e6e40274a4d83125bd5977b1bf9b094da1faf09937668b29f141cabcd46c25f6c12e25319a667bed76ca648c06d0f8e2f492e6db1f8004416df6155b390eef26bcbbad4653b3959a4d27eef591828ba19182e567be94ca08dd79ca671173b9de04ddc4485d862a63d6889c83437be5265890ef644ba07b389b72bc4032d0e5eb64984cb5ce66a5fcd1c0d9ce68ab5be154f14a4d2c00caff39e95bcaa8f96876986548a4c63e3b8f766b7c84c34b24153e93485a15508"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d2901cdb3bd99902cf47a1e55b2076d73af42eb5dbec647195e88f4bd7055435","proof":"282e5bbe80a05429b8d4e619f40f26305878c39dd9b58bc9548ae96db6bb2d0e3ef2648bc96095f78bddc22511d4d06eac57e2f269e0b15781c3fdf32a32c855d65bc5866dbeedce21a4daa66814e86469282277001f633ef7be7fb0838e881db69fdb3de76b1297b7d84dcee82c9e6a035409b392f531a1f91aadcdb9a34a20ab8565a4721a8443aa2fbfe35216d50f07a568abae31cc127dffe19603e88a07e3dc61585b38772fd1645e388ed90d7e539ad0674513986ac2df5283bfe066092d70274be012ae518cd4bfb0918434db639799fa29899fd8d345c9815ab17400deff35c9bb4108b89165b359c13cc306e713f4c89f61011ad0a2892fc407673dc854264fd6aadde67950ef8ca9a9d711b740beb8dd75d2c2e8f3faca5c9f0645b8fc9eea9f0b59a20659325c73a652b77335b7ca0f6ad37658f5aff1b9427d366e08bdfb84a8230a60f334e24b4b0ad51af407be37241e5d36846845b6506d6eb21b55af90cc7607303242200f8321681e7fea0781194d6701a8089d0059383408a88a2ed0a30d2dcd9c8398883f9450b1dd8c15c154feb9c9536354f069925d0cd5e332ce422fee3796d6d712314bb6d75d3f179991c9b8e881eff700b76344706f341481021f6a7a22c87a59cbb8978e3bcef3c3cc25f2dab5fa8d082552049efff0c4095e3e468c858cb2b3a7a0c92fb49b2f241a6f22fbbeb65ae295a46c5e6796afab855518becf80f5d5d025253ea76848214f3ce33a4ec14a95ca5927b250f930c831df27ddaca7c47350d127282dda37bb1c1dae3cd3722fe7678f5ba2ed9c3686211a3c741e6e1e744c08ab486d375ee48c1ad97d4798fc04b5a56803dd8491ab9d517a369b7328a07c38376e4008502eaa18cab673d612c4a27302f0bb139d2d20f5bd8377f2901976efefc0e340baa892fdf49de8e12b9c5e9e0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"08d82808a8b8d8349fb4bd7ea24188205ad092fe814ecb7499b100f245dca152","proof":"3404d4c3d7a15680809676372a7761e8d638504768cc9fc7423a2a2c8e49190dd096ebe27ae629c7107b63a5a191cf31bdb83fa8d32835dd639a25f24fa555021270e0eb69d7e19295553dd21404eb5cfdc074a111c6293142beb9cab0fc7d380eb779165959b35a204674fc1d5babec7281e5af5317c953bb0ce182536ae673a82846070f5c9562d2fa4c27671b6b300595165301f586aac24000f7b6988b0466f4b580caad2d59ce6e89617a5f335e86e5aa51591fc70aea2248f73a03bc08e176ac0a2badd5eacab303667682fad284fd4606bb41fcf776edb582b2d7f200bed5079a84192755fdbf9a9f00e61202a91aedd66c88d83d3c93715f3e954a0b44c8d045e5f3d0ae2e7cf6b813772ec2fadd4b33a3afcb66396615e86294cc29847837a14deda1585169ab27a5331f83d3420d3ebd0cd89605a0d3e096930043c692542108571d1d32a67dcba38fe981473f482b9392d9fc95e31142190fc447fe5af164b5ef3b56113e25531de710518a9da219b784968c6cf9095d1399a254eea4ccc06bc14db6e564a2775198be96fb460a39d656a2cff525e7a15256fc115e38464f0745ec87b84bdfbd6ea0d2ec5b87882b6cd5ccbafd6649c1919b2035c0435c289b14c05298f68cf1066ee142339083e3945db6c49ed0081a002c9476aa5df39bca53b3f29dc072663eb936e8893cade5a02af268a52f25ca2dac2b7bdac89bd14d52949c19e7e97735d67fc3a045542ddd8072239349d69c3a05ad574eb7328c1b340c315468d916738584e2a630827532b8c73777332dad3ff6ac3b543fd46328c1225556e3baca69e8cf2c317fbe53040c2af4f3890516e43a3f1584aca132fa3e292cca99e28caff0d9c48b906256d9de3a2252bc65bd1b99bc0a388b59d32d4d275f7e78459def470e5c633337b8a34d2475ffae3eadca91a304"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c441dec58c420d5bdb4fe516205bbb2670efc48a77dfca22caa09ce3efcb7262","proof":"6c4c70af106a320d472aaa638f636781e4d6a8568d7247bc6db1b9db1892334d0461ec45ce8872342a3340326e3e212831cceda773733f92d279e378b8c53c276422514eb40dd62d627533435dae3f6887898d855ead96e56541253e4b40b31e2ef7aa9689b5b1e561ece1ea83db839acebd12640d4e7c15a7bd2dbde8a4767fa080ade105109466ca4e64520469325bbfa48cb0835d704e365713ec0cd083012d9acd56c32e5ff39a790a15585e28392d4520193801c8bf8a75796db8c3250006fb33f949f157ff8ffaf4198be81e9454314f473479cf08e479d945c9f220089ca10e5f60078142989328d157e74508111e471e8655cf1791c0bcf8fc79313ab624197d4beb4dde611fe844daa32a542549a4a94b8602fd05958fe60208c04cae80ce8eea3952f4ea23a76345c6624228fa4c53b8d0c6b36a0000940c7f0b0da4b3f9ce6893ca8ecb501908e6237f4fd3c68e36b40d65310d7e4c569a2c2629cc4fc147a8f6b19435fb606ea6ea628316e23fea6dc21681bc5518e175fa1922d6c93a5d201421f204ae11c22f1ebeff65d400c50c7f9395d6d0ca9fbecf6a372a01af751af1bfb853bd81bd32ad15250dd791ff215996bbe1a092183f1708166eb350cf94a801ff85c1800ca0ce2e63652542abe6da23380aac402bc33a9372c00271ac6e17561af93a95c0f3cb679a26d723cec964f48c4b534b683d55bd6d2abb02dc787e8f8b4876830cc19b2a093861b11b9a4855dbeb91f5ad24bcf451166b60a23a338c0e477846963c2b3f7d38dbc3d091f7bb0b2ba6b8017e5f311dc8f9305e526b6c5cae20ce27756adb791a4e6a517b0e40485e19939dad8012374d78fd78a57c63729af38bcce5fbbac1088fda728b7e786433bc6ac3c16af404b56c49144f6bb2804321704572f790b46359c594bb2c1599562b36faec68fb09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"90fc635c99d6787bc1373ff0f140cc94bbfaebb546ecfea7503026ae2b7a353e","proof":"0e5a48f9235a7c577ff69a361a2d48127560c81ee8179a3145a9fd5aecbeba2fac939bb03b71ad195fe37dd81a1d29aa40c84c03ad1a7634c398de44a0aa221e7ee649ea41d99ff26a9245f78d4f9523d208c031dea353c5b999fa0fc5a2e7063663628863ebb739e7bb3edecb6d15de853c581ee905ec0d3691f85f72ca943a6c7ad519f68da74a7cf8ca7b9704287bf78c9d5b89a289f2d604daf95b5e8e0480085b6c87fa35f36d5a0df54a7bed1635bd2cb62bf5e6b6cecaa2dc09ae870b040e5bcdb75ff12ba331bff29ba5b4f98d379da03da375808871a7da6c448403bc57d6473e6f8eecc5d3375613c117d3d0cdc9068e13caeb38f12b1dba277b5838818c3661013adb3968356c3d253adbfd7948e4da5a5c47ff3afef1d4f4790fc6594511ccdbab8566cd8363fe43d7faf231a31af637fbc837134673c26c3d7b0a7b5cd1fa3e0ae5e2ae85698db7e4d8db6559faff84090bd690cc2fe6ae79485aca9f8124ea497e9eb28fa8a0aeca888aa6167863f576d972f154f55bbfa260ba287bdf75e981b37c2f52030f0e6bc162c77d441e0ea1e9120bf18c51533d7e0816274b261d336c0424bf1b429f7e1b2dc0cc5061c6596c51d33d3cc9d4343dc223b7f769f12b7e2ef3b1b547dd8c25071afe02014c796688a43e54daf54f17a0451a1d55cdb9b47df26e9519b9a3e37c66789a9df94c2df9fdf39e9fee507b8419a266b0421f56ebd371e5d3d7295848d6813237b1364ac4a76a32c080703bcadf01664ca5ba06616062926ba07925d0bec624079e34684df10f30863efd114e77c25dc89c0eab58342e88a65f9eaaa9ed4fbe978df55708ab3e5e4c2d150dd89e3e7ed890ffc388b5b2f9f7a0eee3ad6ab41a10697685b6dcc54cc7948d0c13112239fcdabf71496ea0f1985b595f075b603a297a9ae635826f6e6c5de709"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5c84ea9a52502d3458012fd5e6711fb7f20d64f861722e3ec7494cdabb783507","proof":"0aa3bedd1f6e090ad673c67199696d73d30cd171df5c7a205d52312bc425a910f6b3e91ba7ea36c42e2c1410d9bf46c03f7be3f0ab21fecc285dc28f5a95c254ecbe80635fd693be4b63854fc7582e75cf0362584d19f2b550f6134051c8a0318ce99ce73493774b77b155abda2f30da791a1711d23d60f0872003ae6bba74684015bc057b6d5514e6bb4fb6c2ad20393cb277f06fdfe0b122a4b41cb8b3e70a6f4859a03cf93f85b4811f5f48dc11f495bf159dde6781b95cf3b7b21ac38e04539d9c958291a572df81b2ed2ed736cc5984a44443ad11a519336ea8a0a9700f04339d39c78635229375d4fe2f867a4bd455b6a19c71fd45ace9f50272d34d7ab408083f1d7c227ec9ff2680294ec65f714a75c8541e50358947740c3099911264025e78259afab45ac6a27239dc4e530d0df91d4f79f818ee750109c0089a11bc2655559380340cb72b5432572092348f0d1a9de23cb819d3ffecde2105834bf6051e3a552ca541dab9e954f5c7369aa5a4fbe205aef802902645d743a350144c43cde7100ceaeaed23472722e48f01a2fb3112c66050d4739b8aeef850c7682c70285ce7b6a2e387d71efd3de10a84b69d44591b923565830f3cf032b0e21bdc691b3bf82718029f91bb7c05857fe91b5fd4e99d120ca8a5f9bd68bd62f27d00519319c55636a375e30a15e1f92f985aeb5d504097d57fc6f0245c8545885eaaef849cfff3aec27f38b9811d1d7a402ba0a44aa40207bba02ed2d1610e3504e8fae8bb01ca550096462417978f3aa9f5e9dd02125b977f2319768d2d89793680292d0d6aceda6e1ed400c1abe34e1da8987f0bcc70473869339c3da98f9013777603a1ee4d968d5d64c4df8cbbef79d11443d99b15dd49de19e17070df08038ede58bc0fc3b5e86c9e6b19b3c693fbf0bb142a5b47e0b4039ca1f05010510c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dc547cd184d79109aff065dfc05150630c9549075407b46430d8667683fe5617","proof":"70c47fb37d769aea246b5abae31d44db5cc4017c1436ce7682f68aa7c0f14f3ab252acc6d8594c94d967288cadc85e031954dda25a98a2907a0d202c091c346976089d03f1bc116a219b217617359d7f0d058b161eda5a5d0e7f924481ebbb5430f08128617b758a9573f50791be3d7d75901ce18c018487ce058e868f188142192bbcd68840e2066d7e75cc98c7177aeba312fee763cab702711f94f1cd4501700584af8eb072f5225d832dc621f4c466cea2a2cd86e14774b0d2a3fa3f540f56d7e7dc53827c59b65aee7c8ac918d1fd12d78973c67a19cad02c3218af920a1ce4495ea0e34005efedbe98726ece9c6f0357ec14fdbacec4d362ed82444d6f60f8d19daf562bb7f464579fb8be623b2b1cbacc575363100a28c5b97964d43e9677db7808677a0cb61570ab18925f47857b50b391817a80ee5db37f6e484970143d29f5a83abee83af7aed04e7de4197f22ce015c3a20ebd59f4960ffe4342ff6690461a3c6e32e44518bb2482019d2f79dd024603d85347ad5cbe2ca586f2ed23ebb5dd37a804a8159631ce7a09a18d0a0b06facb8402d0d722b8222a3f25fdecdc4af90b295e6210c1ddb649908633df6d7a667a0de3d9139508b3ad42851ec45096055944ca58e4ed9c47636acf1e7dab405fd8f12eb90f39e5241c121359ac39147f3e060ebed00660601f6ded38c95d4510de2cd5866a97233854bf87e7ccd5ef0609f0b05c5e3d499d732e7cd1ed81ef1247fb38208c926b48007b86f5803e6d9df88929c150593591c67ff2821e4960548120f4f9efd0244d5a3084de496bc514f6e9832fbf154a510fb2df5045dde837757946a747571f7b4328b421ebe7deb380f3e0a0a7ad2fdb849b1df91e4d83c0dc43e8ad9693bb0ea194d0ff8be3a3d61f82480b78a39fa13f6d698a34f102746afac431f2f6420850aa608"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2a1afa499a10f238afd5508ad3a1fc30d065c4fa7d80ab3802205eae2f713625","proof":"f03ba3a8d0353b1b94606d80e072fcb8c28a287b3f9b79fdb5c47e2d74c1fd042ed6e8dd89e35bca501213dfab8362ddd17931c4993712a2cc983a1d9b3476318469a7658de16ba61f02c6c5ed464882dc44c0c2f20469ec036ae43fdfe38e1aacff66e5d5a3e978957a2993c1dcdd73e2dfb5554334b6d2144d72d873d873699400dc14e1884f2e940c0fa9faa74263deee6e3bfa8543b350aab28104f2080ef2242477ee669fb11ac8ef138cbffb05a60e8ce37cb282fa1258adc4e5fd270af391b95053a80f882d8d03978433a785385a5bba875e8eafda5a3b9ac390660d8433b768206bef2761cab6f6991ff52326cb6b16c7b3d02693887e4d28e29d78da7e8f2da0c177f936192f5b59d1aefb0019177d6328520a879c8587354d9d3dd649014b8085bfe09efc773d1f587fbe1988184c1233663c9c47aba7522da20a147a3da9209d520f4cdbc70723c4e023b1da17d25640fdedbe89d38364454f0d5037222de7ae34240d530ed4a17b012bd3c8b9ae3bb806d2a2d440296cb709781e8d90c1afe1f05a5c39b2496a70f284fcb330849eb2b317aadc6b7b2a50fe2a68012fcd69b289a5d4fd8fa82f3dad14d5a199285c7306bf291f1477c1c33174e2fc42230438c319383da1564630e59a22564e438077ba927f81a3451d83bf729addbf5d6601e2ae6ccfb787a908a068a03f2b7af15c4420572e7ba1a4a0d4038089bc44a36dc1a2bec842ff547d0ff372d18423f3f23c24a1c6424078ed01715ec51cc0eb5cdf0139394158da0f1293458f402e09e68b7c27f56e6136663074142db76e678a8d5f591ae2dbc674ff33ea2069bccf1cd8c352606cbfdf530d602523f3e9446fbf9aec674c1c3034d19640037340d743dddd778f6c2e9804d103a3f2817354c2552e0f7ed18ebe43622a3732fd90b4227903d3cc4a5b87bc8603"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1e0d8fe05444a7c1a85ffff251a8cee820f1dd4922a77bf77bb66fa362b58d7c","proof":"081cd1b39efcf1237a890cfc5f5d56e3c3b05ab973300ab3f5045cd6101ce60ffa908ab21f6e2967b4d8e1ade0f7c39a6e2cb71e8a18bc69d97781560ce7fe6132ea6b205bb7fbc0610c199b795ef1031061f6167f6aefe81d33a14c339e165874a163fce85d63ba16266c2d419aa4b3fbc2528d9757bb01fdbca983f8a57f49512e765f93f0039ec6d7ebefabd4c77b4533a67abd2eb24ffe70e8afdd8cbe0a97f84fc4cf1e81dff51237c1412180fc1169009eed2eb4401363b97289aefc042469de94e0bd6f268aed0e67ee2c5ade86176bbd98f938532c8e785760b5d3077ce8012ad3344031ec257c8684b724d3d979d229765906182d48274864a7bb174ca456cd462bafdb6c7d61a24566a631737814a84afee40deeae297ef1322813febaab4e10a37182910dc120f8e9bbafe56ba2322888c04b4485475194cc057ea604c2d65289b36eac8322d69f89d80b3ba0399753c586142529df14f713ec2ff0806a364eef9f0afa6b6a004672d3625ff83f65f60c486c1d5634c840356d1ec07fc85c5e89f5fb00542c462ecfcf3b7e65deefefb3c8bfbb8531e1f3064f5e86224c8389f60ca28ddd542ee3be8e2bbc1011f619d0e71adbdff4141082111a5ee84a651264f38f3dc19fd0bfa607653f3c64e0f014d80d25bce6ee98c40e48926f347d6f36faafe4feef6495533ae164e82e94c16f6869c5102a31e3fee25bde5c5f7cf42dae750abd82c96880ed9442dfaee4e47650639a81c85eed56ee72f2dee6426fbd0b2df9fe60f91d421ae69ee636c7c26d7160d408df268b474a3a12f467a00766d69c4fe6186ecbd92506acf361894408dc9b4ba8f9e1659e3936b3645d34da5265cd6aa73b8520daf5d20ac79a18deeade8ef710b27b41cbf30d14fa4f6df70290b7e0f0fa30291bfa0f32a1d5adf0aa8c7c01deb6105f9be40c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"26393269e65751d587441161607aabd3bb7e1bf7f319a7b1a2c91a257b64a620","proof":"7e9e9b097a6c575ac02d2aaebd38e474d2a40465f8bd7e3a9a410a2c3931c61d742693236265de001b78c64c6e4e3346216cc18765504a1a85c120c2e7d0f553dc1946216eefeb87c7b9052712f27cba830c69f6af3db45785eb71f82f73105bc8a7fa8760c2fd957987475cf29f85a60d1c9395f2a6688f3984e31fb7f9c67869ad8fc5c6d7364b4838eb7c925d95c4760608b862882335faa2bdd71712f904d4841a297c28032cb4b805082e2bb561f10e7287b87595077f459b6d86fa5a06a1b126d4d876b177b7a301cabf08ffa383a4f693e9795d1c60429f0ffff48e07563cc5bb981b4bd866c7ee8ac167335664fb9a4aaeb1b293efb4806ae6de780d4c797c99da8033c5f2f0f77527e8c592829fe353239deb2e608d20050416d105a259527bcfdc6b295587e6f30f27caa996cbfbb2ed0478689ef8ccb6c0b188410e48fe464ac6cf8b3df0337fc6c706b51f3346041e655b176d9986225db930034086de87e4601090c0a1bb0bff2bac1dd643420dc34abd2cf2eec9a7c31482171267acaa85f5bd35031cf6ee6f2faeb8354f7cccca7e1c27adeaccc6a259c245b8816366e5ae1ecde8c03ded59bdd9914cb908e9586721d30735e149d798b05370bae23746d318a9944cd998622ee6a9379089de28f093387179179fb32444410812bd57037dd61ce968d8dc4c9c77aba4769939f2320d3642f7f8a3d065681a3a7e6f41c41eaa1f4f327a04b94f3a7829c837be6d01b81287317e2b2fbee16de4fbb560a5205e5ec3e1b8df23f519852bb8b33917c7570c45ee38932d4c040d6881093de38f033e252c21f0b79f8abdf9b6368ecd98d8d53a41d4079dc2c0218563c2de6e97ce23c117ef6f9d65518ccc0415061594f107f2cf5407e90bc70b9c66fe4ff109f9e65c4e660c5936e4e5b6a0942cccadbd2282dd37dd208d4902"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9ec6c27812448b7070f2ce1ef1d5b71471b3017b689b685df46e07666445a17b","proof":"10a05c8be8faf14d2c68dec9b9017fb1711a32f60b9177dd4225804548e8c239841d9c9987461e735c5be2065511be9a2115f929e5a31ea0dbcce1db66f02a6c74ab841b8f2e1698e230a310358c8a730994038ca4d877ded780319d2342d9383a4a7c72eb496405aa04f88f35d35ca1cdbc59fe20f3e3544d9d0b2b416d427e12b1f01076d9f998cbf4378ad322975db00feff32f5412e368440a795933370cc8e76ea9ce355831b48eb75b25ab01072034a13e6bd188a4c37a37a8cac9e207d6932d3bec716ae51637d3927448eae565ed3f1a1a05a30239104b27342717088205b169657e2aae9c2fcad165e9cd9df8280230e2947b13dd8e384e92373f44e0792dd1abab31de2f2390bb5f9b4764f8d6947fa3c93d7e1e086bd8568bb11db4916fe48c923afbf854829c8da6dc48d2463b5bcee2ec0ed574a5993d65a7248c08ce40c4aecc14bf421a28e1b468123cf6bc36a65c22c874b908882c9d696444e1759c329830e5828e034f6866c5fa37b101bb19c0b1f8a9cef02fa2f34a5de80324ca211431f43c64ceb356b42413a03a4666727180a3d41e280cf07b2925c641d46163330ac8acfe5035fd64e3413b9c765012c6323d54acd92a21a257560afc6d5bfe90c0b41ef975ba6c29c06319e8affe26d795011c54df9592edcd6916f4f89aed7ea314e14848d73e5af7422308e485d0004c1b99b58343543470506eb84ff440f8483b4ea7393a621a81363a51a9ca9f6cb026cfae2e75af90ea5a7216d5009fd57514358c2b7cfeae1c23b2c201b3476826eb38131ea3a6c61f007eeafde6988331c5beafd2e95bb7b64459461ce1ea953594a2ab8fadc9f9831bf28cb56d6ce69977a4312c9ee77ce386611bf0abd7e46bd0490452b2d18b1c0d5e883fdbce67207b86bc9530850785236e6296d31d9fe0617fe5942e5c3abb00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"528eda2927ffde4921c7af1fa975b59e5fa9c09fa7a51111ce97e20a7e0c2a65","proof":"de36a72adf6e1498a794e800cf2fb138e707cb377f90857b5caa0ef888cdd828b2e908b29df0294b12910842e54ea46bc2f11aba63ebd93836bd8756884bf0132e369124260c0bdb391c16548fad8cdd00f678511f3bc46ac06999089bd1ce559006a0421a36e6f03f18293d97029d6d2280df565d65798962e30e7a3b215e0b096b396928a3914cbe37c5ab368a6310cc6190b60e9d891f19d442effc1bed045d509b18186bd6ebf7dfb09c1814c8ef68419e6165b823355fe1db3d79436a05df16bbd37d517898c17cc5baf5856338f269a7e618c41561de0e99240105310ac89871867c922905d96c24477f45eeab4cd38d00121c40c509416ed017a1a541723f554ae1bd0c13b95ea8ffad6f3e1764a945329afbb7590bb48dc8ea48bf56f82874842549fd61cf2ead629d424eb5587782553db3aa19a667cf0cb6d65a357867f1d997b508e33291431048b567127dbd793a67ea885418d70e43d8a2576282adc154890cd4916b5e1b42b0c5253df6d84bd9ac4a503f3e178eeed9b29920b6c6d5367129c782b094104d291b994d7a21d52336e349592eacea44c79b5b708e632c9f286dc86690f03879ed0a397fe4e7e3c3711d2e7937b0ccf44ee22667bed4c26ef63de3ff67cbac7b104bac2adf2be6157c6aded530644d5f0eeda422fc0b499f6768734bdf39dfd1eb097cb41342b62f11e6348ead563504f0e0d4688466663d404732fe17889c8fc52a04d613dad9192661bc7f67396cebbc0a8906ba25406e57b133250a99980d2102a2258ada020ead059f3aed341baeba335b347499e9291c08cd79e8ad2f7e222535d37b247afb008ce25c1b81c13f10925a773f2959b1e9241d8015e0f3f717b43f0e966b738f00800cf2f32f0bb0c492c6067b7f97cb50c707d71dc96168fbe15e55a1008e2784b4cf256cf764c47e79530f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"56c0f4fd462092dc24a3f85d90730d47451b382dec459477ccca517a40df2077","proof":"f069f0a689ecf47b7f554d3a0ca957c2bfac5db73dba253706f639dfe0f24e3d5c7904c41f7de26f0b548098a8fb22e82b47db599b7ed58e4dcac212f1f34318b85d783353207995081f7fb38b8ae17df8aeb37cafc0c47bf3e32c0d6690b115e6f58313460b15f3284f2218b69f22eee6f8788df91f0ecc598b021492b36a39e7f29dadd2faedd84c125ba509056731b0ad0328ef9285e8dcd49637d100700bcd1d8390cf5a15a91c47c32782a1a47254405282fd514b9ab9c126278b05c1043cc7e2dfd4c960f7ac55f3add6aabb83bc11aac1f7117d53a3d331080c6b3908ca5a4d4c732c7b488b98517fd81a419c38374ba49dbac4e6882b7780ef662466562c94bfccf835ee4b2c6573cab9d1653abd369f816c13c91fa2466e72615b130489c6fe110695a01709d748ad34b87a21f276b590028ee8459dc4298c717967ae2fad49d1306efd8fbd06d08d5896a54cc6c7aeef7705cfead166e8b3b0af318889450c2789e868d0906e0a51dfd1266b39050cd5ff59f01c614f8477adaf30b075487e6049c8c94d2f35c41e629ac1bc463efefd112d4291353b1677134d3538e36053f9a2a66a5f15150ad300b69ac6b5d418c87a38d95594bc110efe8648d4ef207a69a1100df34eb80856b38b1b6b7421ced77318fecb1026915b4c4e3ab4cf7d76fcb829c91b068e275f25d7539c2a237696c7f76b54d5cffcb1760a487e8f0c8574618f7b0d95d012ad48c111a74f841ebb94162ad05928a98136ea0bb673421f001a33adc73196be97cbe78db16052fb86c684985c0ca3d32be2544d2a476889c02dc0c9cde8078459585602a9d891a9859d3a9e5d9143a6aace0e3ebcc2226d40bc535be35455356f9f938655705c53013d636ae5f7b027308fa207fdaea6127d42ec0140f4f93f727e5b594953ff4e8a9245e30ed0842d808ab107"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e86c5f0ddc7c1a49c7ffef1d1b296cc42dd5c33f99e88bbd02f25b42307e4347","proof":"440bdf53a590c7ef9e561d6ffd8a54a0cea8c59ebc7ef7dc8c571453733ae92b12c19ba0d7364bc3564c6351c9d5d98f74c716101b8b5e64a925511bfe7c5715c8706b2b616bad220cc806994a71fa06c9ac6608f8d39700cf76281d6f4c6f6ad4205f4c3dfa772cff81bd65f91917221f4c256ef829e0e40fcc8371e4e9ff2b60fc266850dfccb679c7a08bf34eda20a5415cd1826a292d24f05dd73dce11055d7a281bfaa8aa022dd0f0309fc2ff7aaa269a694a02b49e2df2225ebf4c3e098b67832cd80df387f121a972bb95c754f7478db700fe7db8c4a8114e60780907ec88557addfe13278e3d3b7b5284f082d50910cbada9aca6aa969afb9476f571f6ea8bd02fb40fe4ae752943e25d143069c6a6b8cc5ee1d55f68d58e4e22ce0f90397650972a2e8a45afd464e0afa6d6071d4b93007553801ab763db8c69634810c5e96d05e22d28da6cd859203993050cfdc2583cec06a3d65a0d9702d0f10c3e6cf99eeabcad5a9f143cc27a31461e08a59a0eb5278aedf38d612a3fae146108c7247cd5f81e5fe54e38b3b75c5b56d5d46b2795169306cf9deedaa28b06646c0634027e7d8e9f5ce86e2ac60ce6a4d6f5ebec6451b8fbb021fcd21313f15a44d8adb7cd7a18d3572ae067d152fd5798c1f37c27c2052f15b166257532f15a7a24945bfbd7ca58e43179582a979f5db8693167b9b5c94f263bf12d19081c2aaa2d403588748c7ef5883e339f084979ed09193d1c93b3fc4d6d97727b8e8479e860e73afdafbeea61eca21b1e197e1a4713bcb5c449b8640ba3d8c17cfcf57e688160553408c6d1fb869b8e4c4dff37087ccb18db2598d7dd2ff42f9c7b576cace5af40eab6929d4ef459755ebdf645b0973fb5e36f122805fa6115dc00cf0a40ed275a35840cd61bd954b23ebbd6765f64b7e31c04c68e624cd47acbe1eb01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"98e0f4026fb0e6b563a278e1bb4e55c219191745ad02ae0b7ff718e44efafb36","proof":"204cb146387873f28bf4fc63d68a0617ebdaed464376abd72542f0c2f5415a0b288fa9140e6791d64e37cfb6133ee036775c1b98eb633075075277f13a75cd19f2a81a535ee7d7146f5f50ac239f4ea4cdfe26d5b5aed97b3af6add5bbcdc5194ad38ff019bd68f93a5f7cf5d71db236690e94e34df5d5c8185bf1bba4efd46bf9dfb11d837844110b1a50f57e343fd0e518d9cdb1ee951555f425df6496130d777d828c632b38abfcd53925dda583edd2a5ea7d2ef1ce3521ff205ab27182044e387cd04492de4f486eef6fe49858ad17b486b53be3ac2de271e1ce6b495c0f40af903105d695b557b53b3b1af85397b165cb8bb54b2a2de17845449fdaae63eec5a3936ee12f89eaacce0279929a8d2adbcb3733d2985ad64a6d7556350770224435185c7a49f667bc96adc3cce9e0bcf16ef4b107d65b20c44eb3e29cba395215f23c3dfb5b2a4c9803b75dea106c22555b60b63d032dca2aade10b82ec0ca2c3817b2cf74920391894826e6ac7160ec1e5a48cd043492824bec5d507d76e9e41353bba7fe1cd360d372e2536126d85b1506de36939e797412819d3f8dd3dae6a7da181141af0275a999c22b06a6c6dd00b5c955ee8690cb3500f5d886c325ad676a38f7060922579670bf3987df4f406356c045b885a4d7700dea3ff2a7c8247f4c9166fff28b0a0d74772c356a881dc781c97eb1b4c627b90523fe5036576faee7db60cc8e50fc736da1ddc8ae49d4fe573295ff42d0b0eacd1e777fd7d5c90c2649f7254f13d787de9c7079f37863883d89e10503bc6be6eda5514232714cd492b5c29a1464fa5ef4e9fcc8bb3639cc3df1ce31b6b1096e0ae8caf4027e45a18797bf3de8f29f7f966a7451b4d58b34ef1376e71492a18a582cba96a0a81a31e82dae3392ca08b781bff07a7ba49520557f8db2234a8119314b6f87907"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c878a620fdb704d7234bd1d356ee2ba57cb9f75d9133323f9bbcdaf5accaa811","proof":"5428df3bc90994ce34b0918086fb40b53cb7d22569d5e2a1f868678a151e2f213ceafa28df7a3da1ab46483c2cd8fb58bd1b91a3d39e5ca5a33669ce7e550259ba060f7e3000a83c75c03ab522e664b0cc0d44cdf0af892067e6ce92bbd2253c0e0e0b023efb020e5c51b0c1edf7cfd3d144e5207adf63d4c5e554e64038c92e0e3b1494457f142e831a12c72a821c98664f8e0fbcb1d0ae4af35d5a20543700fc1882830b8b1989e2a51daf1ccf6a3994dc3d7812cc96fbeda32877781add0d143559cc59f5168ccc54a38577d32b9862578b778a473f1943d792236d397708c4767ea4adeae07d88ec25c40c6fb006027352debe5b8aa279d446a5eadbab7f4aced652655c1f3f85a209490d1c87c31e0884d0cd2b58ae36336643cfd1e65b86174a3e000b8bb8bb900f54b27fd206b51b32aaf7438c96f685da5199bed678a460dbf17c4b4c012d26d8fe6b0c5b860dcf20f85a8739800f94563eac699f64005806eee63721f7c20c33f56b13f98e80ccffea6874620b5d583f37659b4d4044ec7e6462b2591579d75f57d75d40bbf03010ba4927bea3f44f6397d765940306c0b936238d984cd984549ebfde2c36f12aab60ce31d70f113f3b32cf2a281b5641223747aca480f1c86f369a988933043f520ce660086697902a90bbacd70bc880e65a986f1985b94c55c9e9b0626448a81fd003e054d2d2ceb889153d5e72123b26128edf02e9e1d4d17b95b4bb4b799c02d3989e98af49dc55ad8ba8ac78c8586c4dbca69fc2ff11c924b51ab0860d3d75e40395ebd6241df70b845da139d420dcea76aca1cc359e39a9dae8eb8d76718a08c0d1c84222ad5f1eb0ae8d4fd54afe2c376acb3eb0452bf2b5f0b4b25e7aec46c73daf647a0afed91a1d6504bdb24a3b5eb68bd7b8c3eb95462d7a014ef28dfd0483378a625aee7f7dd86309"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b461e42cbc83126960dd4695bb0fc42f1c3b712787125d57fd997e42bce19441","proof":"62ab4d5c344ea2bf098672547fe59f2f6bdbd5882b1c2bc0362bfed8c260a01faa5f103efefa786c2c46079f7084b23523c9abdb308c5e21be6287e491dad813007a4a9ebe399bf81d20aef0a8764ab723168a9a5cf87562f0a6010d656c57753817a8425778db6d494d3d7bafb780e018111289c927ae7c06802daf8f2bc45a25416f62b80865baf36db09bea0b12f3fa267d041b085a5c1f6f06406e901c0327b19563ec97b975eb60d051b88a47422cf14588545341d72df324d9c380650e58a9eb2c2d5cbcee64536b03663344787e2285a9d1e83d37723f091ea8c36f08fad53f892c95990ecd339c6e50aa8374cff2f579dc62ed09e7fc585df51b9519dc5fb209ccd9e7547b4afd4918ca466d7edcc0e3d56b3774d7fd6263fda21c5e387c8107033e606a69a6e115468f424dd0fee84c88124cedcf7074173cb76621a0be8206306871f83e99397fdcfced6ac50447940e0071e47c1260aa55aeb969b4b81b1cdf7f637572f9c648c82a238fb89baeb67a3570f6fce8b9e7145cb60b563e2afe183fd249d287ead856ee70c22f5ac8ba8bf9746fc6e32653dbc9c62ce4cbbbc00ca10023720c9aea40c60cd040292f0a453210911cea91a3651f3e666648cf6a901f8a3e4ccdbec247c70c9752ba2898edc84aed25e2f881afd570587cbc9ff7896392583f2db034652c6fa946270f34d1e2d07b9d94f420f441d019b414b0352df9e5091918fce37feadf4b2d118257dbcf77bfcc4370e1a704b4688024113c3b1ba28c7b8d77cf367a598cc6d7fb43529080733134817df221492d6e93379dfb77a8086572eec98cd1c6db4291ac0281543dae62b2dcc1f22a1d31d2f2cee56c42e8aa2209521ad46c18811b67cfe429ba517f621d600b0ae7590860dce93722906a5b0d174482f19ef53222ebeffb8a20858a450c2582b4923c02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"deebbd7baf5ea23ca1be71a5d32294d1ae3bc495e07bcb79a84c75966741aa46","proof":"b60d1e3c287c5254d1384d57e89f2bbb7bba9ddc04a57d0fed0da4b976d2216e6e2d7231ea4e0ad220db9deefdf344b5816239d01725123c41843b1ea179cd3a08d2689919f10c3394ad2812b50e73148ad75ab369d4bd737f5066290d6e8e04845f86d3a9c8979347a690a1c7828f472c4d48c901ea1c5825faf344cc981e63d7842e0acd31ce6ded7da380c3af57996abc80020c53ba84d42560eaaf1635015ec032c98faf665301e196a91252227be6ec1b7f8786e643743f6795a84a890960bf4575840ff64ea28e8f524ea4f0a884430ab5630507a4c4022b26f37ffe0d6ed8dce9f8398baf1f57fbf9af3a0db5c3f952fc76362f5137dcd9e796fc700e24a2a12a7d8aabb333e6cbe8d620d310680e05a81cd945a5b343206b751243415e24f4702b5308487f9b01af76dc4b6942d213f3949e48a0ff0df5e9b61e777d92dd028251eb228abfde887cb3a8cbd567d69a9676dbde1439024148c94802369aabb060472ee368b6d5f6410eaf88278018ee54c62c72ceaf8653fbd2922a5168ab6360b94646994e9b44bc8d1621859d84c7517b032920803dae7112cccf7490492abbe900822733921311402361067a5d81762586b68ab842a2c943dad6040cd6faacd11663ea10b385c9dc0db32d9bd3973f50b8f5004b3de0c171faee65ba111799c52037ea931804512b5330413fa34603f58bcec31d16edc9cb5174144a9ae7bc2c5b98bf33da26f32c43e858f8e4901faaa8f33335845d8aad1e8b546005b4448ba90399bf7d85a06af4a4093107d792e44f06aa284a4d16fbf19172b27b2c2d30c03e16c9b16ed6452c45543c8dda669d6997ec73244ad42e413c0320394fe877369fe175ff62beeff977aab1f20105863aaaf69ae8e5461850a9050d6dc0553ab12b930d7855ce7acf4f279bf60633285b568d1dc89bc88e42da0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"182c9561d905d3e91afe1d5e7b36bf4647aa6b78cc5f221f848166a7e141e607","proof":"1889719ddaa9a54fdb66536d0459f5a395b93cfb68d598ba0ddd8ebd292544021aaedfbf9253cb6d173bb431917595c6d1b7cf735cd06ef93e1ec02396dfaa0dbc12027f5cc8516d314e6fc152206b394cbbb743414be151e43c163fca9ad7462c4c38b92be2bf0f24c4b997ac9b366b7a516d0174298bc83b44b73b5c0392575e8a7cf1dbe139074960f8c1dfe80a3887b2000e5b46b07da65b81bdbdaef503b7f27fc4fe235e3a54057dbfbd6ce7f57ac37fde8ce1bf9bb5e45a8324f0530eccb0c9fe42dad0fb792cf905279077a124af7e6f15b68155ea126afbad3822024862ad06afe714e1b6c29138e912387f6909f768d2fe12172a70400b63410e720a798011d42c519f5a284a095688fe03a92e0eb7a338eae765168dd21de01c2330388e6c90c9e3c915c1d5281f3709c6425a56da82e79fae8e5f43a9cb19251982ee2dacd34790e496468a6b71070fbb4740b42c0d813dd7f08a8787e13cf13736aadfec475b9b19c26dde94f01ba0fc49f49c229ded31570ea869506a2ff662e8a58ea2912cbf2b8ef86778d7a40f5a19eb85fd7e8bb73f18395c754c3a0658e6ccd10d339f44ac828e356bf52343ca6e8c724858d6af49179be54bf216dc00040d66407f47cfda9640a853ce4f5b447c5ed9f5182f40631769a1c4f1b36f5e6cd5510ca7d29d087592bed2c427d0981263efab98b135f466fb155f830eef2a182b9b4b3f52851ba1e3f435ba3d3695c2be94e5cdd170f0838c605495349d79947b2b84681cbf98abc44cd64fdeabcb7a89e2cc12d8df71aae5c8e81499326dae23cf7d96da7f51658453a966d0ec27b777011bfafabf4623365670142d352de8705a3b5b786b68680c64be68101e2c96bb00be1b25c26970d42ed6c4eb3601523f9c7a034e0eaafee83227d360a13ff5bb173bdefe6607b280560a81155b0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"507f155a5be2bf6c9e2c1b80d01123d6b8fce5d4a2562ae662429241c383b677","proof":"747845b2989c35d53b3d75ddb183078ac0842fd1d4f3553af6c171efa42cf354d028ce0e3a48f097f81b8f58f111a96d549c8b07022ae2c3b45c3c7959377d454c1d09f002bbd7f1c4baa595e1e4509737597795a9e8e4a6305bc2f8d0bae57f86dbf55183f61213f622398fb3bfcc99e4553f802ce3feb6358d09fb012acd21a2d12b7157be000b13e85f68d04db86d84d2da3d81e9595ae4ced2ac86ac080fed7ddeb78957178bc84eeca81af751eabf210bf0cc76766d9ca23c41b4425906fea841d187d669d6254a3ac7bf5366dbe686f5f40defb582cfae5622f5628d09bc7b55d3b506e61b4957efb85d613322c943ab9746f07512d812c170d7189e026858ba58cfd372aa0c8b49138ad9c7ab4f6e0805369797470bfb43afd69a4f2f3004887f1175aefb3f279c379c4e5233b8590e99c6f11b40a6c185c4a36fa37112e5b8423351cf4a060192ab9e0a47fb10e21dbf2b0677eb087584910e57a44e26e77fc84cf6f1de0d1dcbd058fc391a5e7fce01740b08125c61c9dd220dd873480facd4cb53fd2c5ccf498fb0d9e9c0ffae70c0766ce3a7ef6e2b64267fb100b076f79d3b847ea100c47c0b930719e8a1e284e865526c1a8a1ec765e13043116ae220ed04bfb2c88ec10b70f64a21243f61759d902c9c68f57dd60f5e86af433c2ec7f1ff7195d5c3c8185794b18549bb87313d2edfe8979cdf09600cdfc42a28b1f51f6aaa3e6aed0cb3182e749114150b08de5ddada4947cd0620860b3124021e1b0ea2b08f79d1e64ec178bd73b8af465b27084618d8f9b8f5375d4e0e49ba48d9c83ba3d99a35a9a7a7848497f16ef5c832855c03eba43c42fa512599039ab469da34d67062c098b399c42f07780fcfc19535875ec9e8389d194d3a9008b768e50e662adddad77e0730ebf888c8c83aab18fd98b517dd3be49e9895920c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7ab3de8656180ed7aeeeeb7f0c6b341f35ca6bcddcf8eea6f18618a0de256a12","proof":"a2e4cecb5d3a550075ef8d758a21957cc4ec9041903241bb8ba903b8d104fd300413b7f0e1377b2839dabdc04057a3e654686e51a8cd32681c466db589e0f93818c32b70e8a2f583866c50c6a2e9bead97e9075a698522cfc64c9c372cb4cb3b3ecd44f1deda1ec152bd506b8f5488c2f96bf7ddd915841ef0f4190e9599c159c2f88ebcdb182648f18d7bf5637e169525220ec5ac940f165e9f36811292300333fe9a502e06118c3a461e5c1c6dbf752c657c0c7078d452fa849dc1444ced0882582a5775cf6a7511b8c870ea3948d7fb0aa7657047d98420485e5e461d5f0c4ad84366f991b23428d16bc550455f34e24d94b23b335910cb6ee460e835967d8a250ff4439ff4762c5cbc7293f9f903f40d0ab3e0139c90c40e3840364c77568418b4f23db9860987b2a9bb15fe92db8aa5a56ffe4f9a3125a13f7f5d38355d98e13c24193c2ab2ceb0f25eb5f8d0a57c6b1662be98eca86eed48bdafdc3e483a2ee2b4a0cbe51904007f014fcdcdcf83cd026e44eaace7e166b0f59a491275e01a939b1e48896052d77623797e8278c4707a2a969287212edffb168f6dbc2328f3b21bad3aa3fa321c375fc1b7d591f374a7e74e1e1ec7a89ac699f53a0e59e2f3a60055f56306e158cefb7a22abda3541dd34c2ea70af2592c661cd49f95e66e9e49e763f5d690870de7274d47304c2ad038deaf7fa28cbe2846cd9c0666aa401729b1e590905e0575119650b2889067fb0ae4751a48d2aa26b1f2ea9326b1e3011ec58c4735027c3b3630b149060024e3527f75e661e44eed30bbfc6d46e34efe4d9af3b2d897d9616e231fba3ca38ff64fe6fef1ff608e7464f2755d31ab52dfe384051a34fd51c2d6b7fc93fa3c6a426424f1bdcaf20d1c284f1224e0966cc881c04a1742aa63282606281c9d5587e1382e926c7c71ce0b6760f76460f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9485e41bf5bc3b934f0eff4fa8e51f2216ceb7cf2aac887a6c48059bbcaa8c15","proof":"3264d0a6bf052520d200aa0e77e1b50f970dba5dcc5c7a2b59cace3e5d6f3d01d0606425e171f374913747d01cef3de428fa125b976ba6a1d46a6b8a1f638907e64d9e9f43e5722240584bd87e5d0a1d0ce911f159ac6f2d8134d7f1b4239f26f0c2ef1891b98c25e481ad4027c8a60c88a59d0315024abf75c6d39895ce3b5cce9ab74fe66a9ad382ff36d01cf6084968efd6ccbb51f1a4472b4d1ffdcf680a6cf034f08b25b86d64fd7c98f813e0bab6a3366c472c6a7bc694651fd37a0e05482072b13d696a5affee4e01d6df9e6319ee57c70462e525c590c3eccd07b503e822e7b40055a0ccedfe8c043cf2a0741ac8875a244379f0114bdcc838603d2820e7a5fc55baadf5b221a12803eaa423e15e7aa5d02474df57b7c5e76500d245a6f080e9859c7ab932893149c798a8fa3fe5205fbcb1c80fbc92eecfe35faf281cc5d1b0d48c1d67f4bd716622c562ea0d273874f97e9a67e2895a86e272a500666f1016bc281e3e27ae17038773f7d56a0a7960dc5c903fb827232837875c14ce127416b47b59bb548ed2c24062c01f66e8ae5e740c57e4f93e1dcc3922d26ae8d3432cb5c92c4d4e5ffca103eea7cb3c18b47bc71d040d0afbef24e25c8816c27d9f8a1fd962c83705788f4c9c45a58302d04fedad235730f7db0dc24f777460a8823f748404925c65c788e956810d977892ad50c87eb8796b069cf120ed237e2e38c5e9c5ea200e2ae120092c73db6863139fe4213dbdc41b366d59a2e55834253d61fcb81664043a6498006b82f5d40b0bdbe9193b4bf5791ac9813ec50f7846616f1e8098bd91761be0b212cd956440dd5b1df4472ac632c18872a7d633e10d88478976a7befdafc3fe58d44732c02895219df25e9fc59f0dde8cd3df01d4006272fb0e21b9c18a05883f2350b1848623b254cd66d48053db49a51c010f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"70d26e7c5829dc2a5e706b7f3621c9c5a922649723c9f1c47c99a5badfe83918","proof":"548e58e0da1051a1bf2b8714c671db52d44682a28092f0130a70ed9c8035b87c368a3424b98311ef1857ca61e0066267c12788e7ed7bc3511de8259276b848459acb2b000a4dc99d9f98200ae086179280cdb2afd07e6de3e76c808135e792424c01891efce35dd75a58e988d9abd3abf56d197d32f7b6291eb77468aceaa63b05dd5dfd07a05f021b29daa7631bb650e2b6b569c936c8cbdd946d247ad36304d1db7c6e80f6851b268a863fa70495c5272eec071443744c94076b2ec1d875070b0249523911f221893c8fefed5610e7fc9c2d87f675dd9f3eb1fff3e99fbd0e56bef4c6d5a1394220a381f456ef2365ceacd18178d712a9b67984e01a3bf42e86aed8245706e142aa461a4e239cbc57522f036cb324122cc10fa7ac01ac1274d2fcc734ba1ca6f765a143439d3a3583b615f8ecdad0db655b06fa54ccbe7f15a2e5d13fee56e47f3e25a872c2103e89c0ab6389f092638443d0cc8a15e7287cd24c6d1c7571a18bc38f64df1cecc0fc402da522e456e814155d6c6d3358c908e2518d9a620e474cbd3f2c126b88e7624bb41ab6bf0b8135b0586fec7710fd01befa1e74ef171564febd17eb510bf2277b15a92dd52ff27196aba19e60afcc706e352a170906f2b77ba7cdf2013e477c4158acd331cb81459d143186810cba26fe28832de8d6bba18ddd486b6af7a564c8fdb9b486cd0ad1e9c18f88fce42a11ccfbe4486b78c0f866de34d1e15200990966c4f96953c5dfef3cec3791d7b6790e9e003a4b8e76b82724df63c202719902acee63f281df5b5c1c7d287cee1d1ca6b619d3de7950c823b6741801ce86d67d3e644016566af81fa6d73534123b76253c54f2c06fae78b157dbaa1066b2de6a139d28fbe8ab20076e16800429bc035156222463e2f405d11228521835b7de2e3c3372ecb6f083c24ac0dbee1bbe05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6aea0a27b733b9684a3cb1eb96d7bfaa1fb700c77480751ba62a501ea4958c61","proof":"92c12ca10c9c6022267de44f4a28c387f4286364d12fbbcb30ee5ebae2b2c908b6da968beed641bc453ff033b027ebc6cd91d0f4577821ef0104fbb2a4738a0da48b80517a937902767df0cba15eb8cc1ff502878852693244b914826b8f1f7b520ba95fbaf0e93d6b4ba610c48279fc94af10b9a7113a75a9695250953fa33952ec1b8845610483472380577d48e0bef5bb35f86cfbe641d3e35b51c44ee90f214dbc47d79290644c1b352c32a0b861367ccf1963994a620ca566413ce30f0046218c387723178e53b9d104ae602055af04a850121a224d879489bc452f5103fe1d57b6d82dfbaa467b2cee77b40fdb0f2ad8548e3f9951ac33e3dff608ab22ee209be50f259ce0fd386b8515f06f9056262db27313fd63e3873459b54e0d55fcf1822362f4dc59ff9a6d2d4683b21644ee43b3b24447bf6d610a4a38bf86684a6c2a54b6fbfec4263ded2ec9ef7b64a9e7ff7eba95be7a088b6c8109e6e6756cb63408147e3325c5e3ae563aeea8f3c8c348a8235466ac8ee0d96ffbb1a60e72501dd15dbc9ed1b1274e13e99283a1d987bf082496d0c91cb22b8108f4f208d8767ed15e7e358236bf91b51c44bb68ea1a4d441fdf39b73e774ed3af380f5452891536b3be8d89626f474e6cb3dbb3e089be20446b5ac1334352b94885a538b068174de7f08cb021518651c1dc3bb7399fe7add3b9d1ad593bb91cbfa96e742049f588a43b4efc1ca903ebfb3d0d1b47f21480cf7e6da951845c55e840fc64bac488a23d96c2f7fda10670a03f7699e96f3c87f8819f777ff8e6fcb2a2bc05185d05db0ef6a51e2825699db8b7a9dc240d2cca551d7dd7e40c8f1088cf3e009544ef10b77908ea690a2649ab5c548c77b9e09abee6883a86bed3109e2a9f0bc373767332dc2e1d7f5e20593c95dadd7fa3cb660fa14135e830c852f54fbf01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0a6317f2e497c18897c8d87711a45bf6925438310c05a9e80fd87f70aae80110","proof":"f6b1bd91b2a9dab4e4ea150662137ae62e3d084bc25a6a38af2a2df930f6464d205b92f972a9c0523068de263ed6a49e14f08dca40071dbb1d759024aeac67417a53d477544227190635d4036c8dafd05332ba96fd8367d218b54d2b27bbf1353679e6385d42ae14ef46b3fa98318322885202a9d079c283dcd3b6b18f46d302d09b045cbd5372c684663d36e34c7b58de965e2ce8fa468bb934fd9b96d5470fcdced6c68d43c225e2d495057b0fd80f998fcb4c07ac11e76ee573a8fa7e84068051a88397658e25654f93b1a23197c6c5eaeac40cf90129068196712f1997040e6db050c3f3fbe539e811bb558acdd94803053ba7c31310dc4522713e3ccd20b0c863128e0ccb645e7feef0cfdb825ae9973b9dede92f523bccc8b298a3241d8c3f34bf822e1286982b730dd9ce57464105317b4435ab9a319e44fe98f23943f02fcde6ad234c377813d1ada798b8f57cf9b1653a557bec146aa3ffffc43e23da6f2fd440a11b718262af8baf583f42612e1637925a35a2fb2e196dc867853ca642f61ad49b3881a98fd474d2382ed68c61e198cb0f5bbc355d63465af58045247ddd9419ca04de444be694326a710c0ca1648ea57f6ef7bde9c12127cf2b57345f27db23f48ec6d6ddf3844bdd32ad1c7fab2eba56d2299ce5f35bf130991df6b347731298ad23fc40ff5c5b7c87b479361704de025ed7779a27a3c719b460945aa7f0e6e8610817df12602e8226b68b25ad1bcdbb591a5e95bbef6151f07daec3e498a8c09cb048b306edcc763b28d46bd7c55a6ed4e8476aa8b17a9d4963700b1c1e4fa6b009465b187cc28d7d4e4020d2ce870449a111566df779aab93723e59023c0b677059e501066ba457f17119b888412b957bab86ddef127b684007190b9f0760a12bb6ea41777bae4e1081dac06e8e6c5824f96a9106fb1875a09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2ad0c0baa8d37caf16a3c07dd7d0acf0448d27ab64c2f85bffe27066607b367e","proof":"e66a9ef727919212ed8e3e8d35fa0220106a535b78f16eff888fad65475b2449f02000be0f526fcda9204c491c0989823a57cfca47a3d9f8a30c6b3d223305791c951cbcc1d795045316b42a07e36ba0bd99e5b8e128b94d227bf540f07f9260ac861c201f4fc4450ef36ee875672ffbc1967825e9cf2bed9d54d260b80de32adc8f8eb5139ca9fc74df195df1bb1621aa0c4d71b6109df95b704a8a73055601e68edb46af22e2933979e1be82bd64f76e1b36e2516b772e38d28db19e6ff003311c9b686b12a38a3bc53b38d545c82e9bc56a29c9e24773e2f642c35e4d55010c32387da2476fd346409d25866d03ef0e4a714ac1b918400d8086066199dd087862c71fa843a61ed6d870ddc56420478ed30f0176e0d40e0750a15f7503ab1bc4b35d71b1e0d08ea0489198bb51b78e22e41fdf01de60a9980854949200500f7c1b9bb4949f51a79bd32aabc8c1ad189dba8d77e68e0cce3b46ddeab16ec928ea7ff4a999bab638f175b8f2c029443a3b78c6f9e41c827af8e0f6e22201b540a4781fe396c2ff6d19ddb8f3c406e58021ab7310f11dbc6ea593b3cc15c4df234053ff1c3b07bdf43ee36d3bb4362f3c1888208437b5eeebe1e0c77153b38676980d29ed7125f52a3a3cebefaf640574adf8d038537dce7614b3723bdb121a48508d886f9ec53c3c1d124811d10827219db982b2be7e1caca150f6c2c75b7d027eefaf339422281f725ad438d1b8e1502d153894d85b8ecddd3e489154b54a53feef1d30ad1bab19dfdb8264f0cbfc696fdaf085bab7cc53da8988cf0d84e51a76169de71a8abc52f783e87f32525767b6e9e76a3f1415f924d16842a23cf826de1accb646ebfffac45ec28c5be9a71e934af7fdb2d346aa9d8d2fa8e7d4be01493b8fd9c3900a4297531db539e18a0f9bc9fd7b5b84f3272dc1f263715a4901"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2228dfc068451ca062aba7d9282f9f774ee90f8ae1aee849ab2a8b20324ef207","proof":"22a7ce3a9236ca06a0e7d9c70a7ff00ad1ff6488b6a89ccf86ec9fd560d0613cf251cf42b6101da622ef2bdc490643ace01bbe81a7d89d236d0c0d39249b843dc06aeeaa412ae7fd9539c34e354c9fb10ceb137a406784a3bebaa506e5a2295e605eeb27e122b5ee0746dfced2a48b20bc0874f1460ca432937d0d3f464be76d3fd863ebbe02788312183bb8ab75a4508bc69dc556acd85de4c43200f71424076e5a5da5126572acff23813336ce21f860710f8831ad9b79ba041f5bacb9520693942370821fe6e0396de1d75480ec0d8ba98d007be1b25d1417dc9f471bfb0a18f50573de517e4cbcfca0ebe7251101034cd9c5eeb996e6b1914131b8183e094c03baecf5d252275e33ee182e76da5a572c71aabb093f92476f8ffc9aeba214e8a407c80276e9caaab2e03a1736b41f7d8b70084479dd7c54d7108d779b713f2e32347a957ddde9da118e6d5879ecd7b9a14d8e8c8169c4d31ed3d0025599651adbfecb7867bea2292bf329264f624ea2b6aa14800ab1b0711498d1ea871e30f0b4065e9d2dea1bea286a1c5a967fefb087626b2eea965ba5b771125f3efb5f6845f3ed4710a7123eaf630eeadb21f9df1440d6514709e7136ce690715b075678e002a714699992426d22acb3995d5ec54c686cb5dd63f62e66bad4a9fdb5166a23e58004809fd1f8c93d296c53224666fd2fed63f8106bb8be08ceac666c71b60b036acac8dc62676feb868aaa7ad337a6551cfbe14daf0fadcc04bc7c6d6994e279432233245b58911f1b5a2ccf82a7c8c83eae821845f7a371857c840844482b0b345efc36359f8ae8189df6f09ce78bc2cc8b74fe5577f50988d3e8002cd76edda846efe1668b56ad0f1c5a519c1b81cbfbe2d82aed780d8473d850f30f1a70883db47e3c679ba3e69ea7369723f680efef29eb9eb3fde93e1d68170e0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2c49ce68b61babe31ae41e15fce6d184ae2afd310406ee04d96186913dbf615f","proof":"7a5075eace6f2b3e041eb70636fd346c988317107f9a1b74b2e971f754c7f010c2186fee1f2623e10424f7fb908e957a0970ede11c136626335ac90d39ad962c1a961184a290791c72ae23a49c58a2084e00c60f88a1a037f574e214d817a11db016ef67256752a25c141ebe25d96477e11cc50064c91861f6a528ee1d881c7ecae196197854d76377a8687fd8a319430d7e944e076f9483007faf832bba510f4bdf9c6ede1b07ca98ad80c9ab9ae3d526f41af91e84a8da2932b430cf79a10bfbc0184ee9a066d3ba38be25392bd828658539d817831082e1ff64e3ed2f600f3e5877aaedb87c9f88111a47624801088300526effc6c970e8ccb1fdb5097234564a5f1d1fbccce64697365e70cdd102a66cb9470fa765e17d07cd1d6d02154b8e65d3d85c6874ff854091ba7790dcb0a965308e1f016d2a46a9a86ed49f571e0aae409ee2fa4cf03e947516e026fffbceca16a6e978c8a22d79f01bef53ad1bb291c813a1c8538eccc5725d8b60e55d269c0926a2d690783c4ce90a5a9008185a4cb67f852e675655e308e88db02513ce3b098e6ca3b8276825f48eff1fc56da2b578b220438e4f2f9b3ea5fa4bd00e382edb44aa389204b72a2b3d0cd1711bae6dae0e4d531d3ac88e4d26195cd2e071f0234c371c90e57f009ef581472c23e413fbdd60f0d4c5a47180e9a772af3f5c4f112aeb74b066c9ca993cc01f6130ecac3d9fc14d3e93dd194ce45844fdafb9d276c862581706ba02a68c9afc314d2cbb69bcd4a5680a713f0b1fef2f696d33b129bc6a25ff6948db61153dda2f38b60732dbdbd49c6416b1a7136ee3c88d29c117a8645b584181c32f90f45a2571ab2f0af9d1ce85700a8a7c0d59e013016ce945f5af5647bcd01b2cd7095736067a040cbf26c4d9824343080efd36adc07c703e8f398dafd7cd076873934ad70f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"66b8c3ecc322eff5acb156e8845327dfeec9b38832599b5f3531b95994290d5b","proof":"22f6d8bdbf465eff6edfe7cd713ee4a935ce9b9f825e8880bec69f1103b9152aca6a9bd870b12a52cee32ac1b076fe2b31b4b94718f2eeb3e9026c5d5104fb5670be899ff2d04e162cfacac896f349e150fe4d911134bb093624a1e3deffcb26f837867f7a0397f7f87e3b785945d8c22da8b6696f2b5a04df5dcc77b307a81b2913fb4c77beccc5aa055420191d6c4934a25d3c349e659f32e4551df5ba3906e0f21ad24af6467108c2f34164258f60197ba8562e53c29a8ec41eae4f19e70bd76f68c52b9b3b92233d4a19d1620dca3855e3f9754a96032216a4404837f105de88668f18e0c878b60a57501efea1a58e15f8d84ea93b471b9bbc00f091486d6832ccc3fffc32487e7a04e7b26c65786a658b6ff9574c1c387371a7bd6ac50e8c3015cb121dfbeeb4a3764fba331719b2b12cd40248ce8a0b51dd031e9bd015b26345cee6b576ee646a3e533ef6aa4e8f5bebe0902e4c7df2a47e5f92112b276e4d1e02ca62675743b0fccff28313463b855fefc7d5d84584cc36f320ab84302e2cd33111c0c57adb45ca4d430b603b267497754dc63ed0db2fa8c32377f845d8bcf89ba2e541afdd6728a3fde14b03fb932d3eb93108be63ba50b7489e3367b2c2b348a160c726c72fbebd8c70585e8d60ef9e206a50d8f74ac59847898c3b54222f921dcfcfa24cbf897cb1a1360757a28d72447d12c9769aab080163ed5b8a3edcfa159d8b3374befb2970cc8c032ea0042fffe9af3622d700a7b7bbe234b203664849b68f8b10213881e91484bcfd3659cd913cc5052e6b25f67a742b1ee02c5285ec5073fcff14203abbfb2c2b723106eac2daddde0e493f61dc0a76446ca28ae7cb4f28892749f93676f9349d8233b404b27d96753f146d771781fd073ed9b24f5905736b8b6f19bd707a5c074f0a2a62c466eac853feedf2c4ee9e05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a8eee0526f910bfce7bbaa91c16d67f9785bf7a2dee31f68e941672f1447f603","proof":"c2cd301a9715ac7e1d573c79c5be0becec238f37e3efb6ecf836a06b0f108c62e612b2fba3f0871c6800c740d055c72a2b1300cfd2d48b17a51266125d75505568d79fe66b98751c3721a76f3dd964a212bac16ea62db5b8c6d5329781fdca409ce19e08e285a5c2ad8e9ab81c0c271ddade76b540c9bc6efe0ca4d929960a382a921dc05e0922790955b6000cd60295ef39baf795f5e8ea2e4d56c7f44a67042277dbf681ab8071f438d3477415b6d19302b43003eed5bc9b75b5286e2d670ee391546316a7478a0b0d7dac60cad0ff31bad84318b446b860e78357fb862b0b126fb955d56cbdaabaf980c6c29314c571734badaaf58e00e287a40778fa8c49e2fe4dacec5de9ee47a82436380b1b1f01b7a1d52e7d618d0cc2eda7dc936c5b40f07f1d6a2e816f2c472bb1ee6e79dccd3b81af9743e70d3b5aa23176b9723714c44316e449b2239fb351f651a6617c059169e7c1de1c13c6772d4ba44b976b1ae8b7d2610181e7bd47cb58c887006fe1ab854e124fa68677bd906d12ba4c4f0af6103b74e4419e733014100d45be8950aff9ab9b1b25e8b87b857a12b9565da28206f3b8babf8ed491f4b30a5a36120a7a5efb8c56098965510a86c3542329586ff1a61d8f140df7f5bbf2eb7a390e7f3fa473ebcd9131188db37114d91e46debd4a688c50cb0941a8bdc6a666dfd85f65ca452788bd865b13abc07d1e315f9e5df081ebc1cf301c5725184d8b897bbf497daf75f419e8d4d7de525ac5146bdac0e79b81e7a00cdf786876e1a301737bd73cddf33e2c095c0d31b9d04a910f4afdbc20930d0cd128477c671171c61a4c2cf02bebc3d25cce18195863583813fa1d2452a5d794a9338b66d3b3aeec26028e300eaed2d59c46dceaa59ce1eb0551ffa6c51e3b481b3043942f030fad3a0d7a033c3914bded26277122af90ef0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4ccfa7d7d4d72833cf537b027d6dc8d196ff78943f92c563353cab27dcb2b819","proof":"9a33d465128fa575079a9acd37fe84011ba24e3c0fb7af07d37dcc986418c4776e3737fe3d54aaed493b1a0a490afb748cce2d9f0bf10e445ef4685754b7bc7e8cc4ff7ab75ed34808a9697fd8ecc5ced89c790fc8aca3c9bf4df182023ad42f2eee4f9813c4cbf2f86268f4fd873837d777c4d073e0f1ccb4047bcb6fe7f65c2797e0343be86d31e314804ee366098da901b774872b06f0eea10b7c5f37f70a51e5a59068c77bdd9e12c0ea58a4df65496f4380fc02591387ee08f59fc40e03c1c006c6a6843dd97cd8b282ed6bd397b2273a9b4ea07337da3b43fa8ae2cc04788dc908841f3c95d181169009e8e3bb2329e2084db5f866c0bcdc439069205406baa90b8b7b5fea39f1396eaecab1282c5401eaa75f064eb4eb1f9b242bc513e2df76097f8b55e225d60cb31bfeebe85a50bc8f8b875ad9553761b98a79011f8445478e707a311ba5514bab187334ffd347b3f0b68c6612841d6980efd4e15d52f211b0d12a820b13f4c38f327862a6a88c3c47daa33cf338f3bafd1e00a66e9a633ed4d45898a5a720e2cba6291d6ea24df0e92bef1042c60f7fdad10fdc3900fed7b3c3be0bd5ab28f9727f5edb7e5b7ad15018cf4b5e84f95d5c52e1be026cd96155ddec43e245572b4915d4326497584a34f44b216905034b983e44336f424247ad0da81d31b5a35d9733e20ac4d06c9107247d7f5f4a63d3113a4b074aa6abf6a2c5f5cab021ff01b926535fbe85ede819159717caec22a3a7053b576d92e83e09cae24efa2c80d9f7ea4d09cfb98f11ad7903bbbb73dc04b009ec0c1654e51802ca3f544d542b3f9fcf328471342c446ad145e260330fc8759639382553785785b9f14a5e557bc0f9a01673680cab0d0babbdd19943aa8595b48d180b17e790499c6f58de03c036adf3557435f1631d6551ad7f82efd7dc47b4bc9904"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"38694b1d4a32f0d472ba43041023322940d496b10c9805941b2b42fd1c476d05","proof":"8684031e2576d64eac988b1b84379327d3161dbe02d82eea1bd41a2db3b01e47427a0f37559c8e56822a97bd9f65df3ff773fb21362fc3861b49f9c0bfe5e535bcf3e43fcf07524ab8fccdbd0fa4c11d1475eae07c7d4c652ac441fa284e757c8432c104bab73c51796180d7a68505a07610cbbd572edcc8e2fe2779b42b101979ec6d7a7dc23a3a2262404c098ee5f12535c5d383e861aa28ed618f2d3813087460764422da20fd680fd6a41da457f7b79db2ef0605935a92099b8e6cbecf070342cd491494735bbdf5be5884ccb2eb302cbe31ba79403928af47f8bbe4dd0f602edad201e0af080c27f9f69095c85739b422acf5b8b3caba1b94212596da34fa8f2f48ce07068e568101b8546c0fb560eae3c0d23f148a6d555646e380a5616e4224a689ab0a9daed57eb1cae5b9ce3809bf2cfacbf4039f6fa3bfe7f00c47d6def309a89d55549a183577c4a7dcc0351fa2a1e179c440b102e09661a91003628f96566609a6e2628a7cc1e58f19d741284792a1934fe4679430b291e17957d41c35f96aaa981898118dc7017fc6ead05ec24968a00cdf3c19dd89a7a1602d48c7778dff85b2754bf5eac1499ce6b90752e458bcf59515cc20427027623319842b34743124809db72dbd80c8d043509985485d94787fc542c07e9587902b5f96a90f04bf81e1e97e56ce70b2a5a8f3b506e1aa8df19ba881fa968802a81d38449bf3748708a63bc5b4316cd1565b652f365214c0eb184f59b9fef878d7751f8e777b50c793d2554f44012839a574cdfddabd4d10c356a015e6391a87e219626001c98f281aae230f19e1c4e71381b3d9516f3482b388943b6cb1c4bf9f9429b43c6536f71e0c496c951a704f667f456943916404ccc4a1574c6dbf0e33f00eebd297539661fe68f08dd2dd3e4df33294cb9cf8694ef4cd07d93f3af7cff106"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"88ad2fbac331effe407f8f6ef5a00580569ebbe8386a2f762e5a45493ab45464","proof":"74a8d3f2b81f844ceacbdedfc1e5693b217191adaa5113974fc02f5d2af0fa77448e90b5e965b9030e4992dde07d6049afa6924d29fdb2fdc5d9f76534d8981ff8bd73c6c9b82ff316c1b75658d4f7d632fe45976fc31df71804403d944995059a0d8691998df23eaef15c64333df7d7bc5532f7bf279e5c12aea49cd136b17d69e1ed5c9a3d46dc4a95bcbf619a1773ae655506e5cc3169cb98248655806d057d5d54162893be87184bee05d3117371114fb6838e2d9cded9266b0f952c6a0a5ba002b9320bb5f3018b65f32d4d3e788d8c5fe410b115822e00b959066eb70b329a528d0634760c2b32c7d31515284a53f8a7b5b94a6836fcc75c49e060cf26ee63169c1ef0c65a44c5cd7fb10820868c9b3347cf383c33c3138ffa6f16dd7b503cdf5939199278038b27dc3ba37667ac6d3bf21b5b4aed559b9cc57d5ff03e9eb39bee5bb6732285d2aecf12d68561f397bb767e6c021cb1fa94a5fe08c3524a8ee28a465310650b05340bd153623597546f650b96ef4419bef4d888022d3ef29436a1158148b1aec6953dc6bcda81509251789231584097cceaf1cada89024047ae8168f180d71ccb14d32f12255ff7020a9ded71e990cf028e25fe66f41e068735acd1ea0f86943724bcea4fe36fb92dc26b76cbb692515be1d265f4cc2f2438e7feb6f35225a4d536cfb30796e2f9fed80baf1cb748c130c2a0221bf85e90e1757ca5985c79228a0624ad362e4bb9888102abf381eeeb0983f2c857034d9cc604626a77f5a04fead72e18eeef28832b26b1daf02edd423189c34332652ee49a63a899204a6e0027b454ebc3b34f565074fe8f78aa353d9c7cd26e2f244fd0740b91bf2137ec9f60d236eb7a0d8cf3cdd7d49d063ca5f295afd446f35e07ad2107d655e5507876e03561c8deb1c10088f65fe63f3c20db2afc5e5ed8ea0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"34ff51cbd204b15da08d336e626d09b31869c6d6ec94632dd32c426189988577","proof":"f86b1a5c231bb1f3e0344fd7a678c14ef070b370b063943363d0c2c7bae02e7f821f494dbbc4df7038c31fa915f0951ce35c93f95c6ae809f9e37b5b506bbc35523d7c36fe414e36b24edadba0c7ed4924d016d7948ffec65d147aeb544e222d583130b595d465f8d7f3b4fb51163fe3571d4d993fef85567e4ee9ee486f5f2151675e82d6b60a1158d2466e2272bfc5941e467f272350c6d6b345b0b637130cad636951656880757256704ca30d9b83bc2d8860b19e2b8b99875a2e15ed1503b5443649925d75de4a7b29ec092aef1353d6f886c4ebb16242833fefe5938107ba4a1f7a543058b30564c6e7f69718d45cdad7f3d24ee51d2ab695c07fc1620a0c8cfcf3df98a5dedff8c0be6a2ad95ac9835f2b9c9197f87d391ee4f707663d46f9a2cee6b2670b5dd072e0330f466ac17930469c958147bb6b2a040077444da4ffa173a3f3f70971345d6b877a55acdb3e98010182d5ac861dd3078efc856b76a966ead48101bb7521560dc657041afa1f88d5c715a744c415b68b4ca43612a6ab8eddaced0467193cf255fca8e391bcb817ba9e600e5672bcd34c295a6d43c055b55f4d9fd475fd9c841d80815ef046886039e1f40001996986821ee4681d3a3266101af821af5372ab0bb56938dcca9a23abf37ffe4f61ff3dc0df5c9f32106776f74324a9255473e369c8d0c165c844e1b642d35188a80dc6e9cadbd26ad0be80d2a267427cebc35b1ff1a3951095e277d878836d585832c549537607209eb37cd7b9a047ec40ff1f7bf28804e63963b2b818f57e05f88c0de9122a99409c1811696d9b4baf2202ede8a13cfdee9150d096a1c0d90ec5a48c35b7189a2a4ad4e1f45dcb8402c9ea8606673e3d9bbba89a6f3cee629fcd2a66743c237e077f8a972ac13a1aca29ba81f8e7f8cbeaa296216aff303825d23fbc9c8bc0890c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"54f50bee52d8c725589e1ba605851a51554ccbecff2f1552b306672ceb1a437b","proof":"5c82009162f857449e7e0d8d46e8313dac568fe34a751ee2ea6db0f58634483950d18959b0c98052cb659671d376132aa5fd8ecbaebd88cd2d461aff3d234309ead64b8e2141265a01dbaf49580d08eaf67c9c5b98d20efe880b34fb8dbc2258e68544f8d488c7d6919fc97d2211a4f87f300d10b617699e790d4048bd5e170f4bc9da5ddb59ab26dda93a95c729bc9f311817896aa2bb98ded8663d43b0e507ab059e26cc6fe9d50d50eab18f25d9645c5d79e0b933875df0c89516bfa5d50bd2a648e81bc82ad1f1449a79fe05b271e8a35c6903f5a18d8bd8660d3d015807ec82fced0308ca418a06104fbf51bafe6aa17b6af415671ab54b4d6b941d0f712cb1130747957194be80ba3deaa82b21faec7106803e374511791e4d7c66b56a68fce12d7b2f7db511717533523fc837d6089d06f121beb33f36d6d9bdeeb734aecbf7af5787155399fc5ef797b3c0dee99c5762bb6ad864b8a72c702a672147eaddc59feb9f1112344531524d4328432446d15ae310f9cf9b73512de26c860cb0b9ea039661ee1c6b814def9dfd42f9de1ceef7f534106c6418c82430ce50528efcb7b25c98c640d45e2591db15d159e22d8c66f83d920ac90db6c9afb2d550a269ddf2c7050fca9de8f001d55f86357d3d9181e5fc25ff066db9cfa20fbd193c3921e9d0131286ac250cc8c34d3a3a294ea63f688dd9ebb1386ba7c22a94042a724956f3157c00b820b3c5fa5acfa89ab3c611ce4d52750f43b06626ff0444765fbc479571af72c2a1593fec799ad2df624d648ffc9d37300630e3f2ee8f60d873e47696efe6f280d85788028077936ba06de130bd0ff3ae071d05aa07105ea14d16984b7abfe7bf1b298435c7c0296895a63832db2145e48595c8e1ac160311772446ab2f965c6656a5c8fa13b4dbd0e379f1f68a2b3ccadec802057e9c08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"90131d77dc71133cff85bfe025275dd99a9c5f1eebfce6e7271bdd394de26b3f","proof":"70e3a348952cc50e42cd2c98eaee71ec41c237f0a590e2ae20dd6e29ddb2f46b0a62d78674af7ece179a98575edf672f6814d48e2141578070422a98e9242522e417dae40674efd90837145bcfcac53e55079d36ab48b5d5778bc800255b6a696a20b3b6c2bde34298788fd9e11c17634eaf7aa3969d622d5945912a0cf33313d5ba6c5636b5e63428541236f0ff7bb63f69ffaa3aea2438105341fd149175098d286221a5a70ad32ba59a719dcfd418a632e0cd9490d9a03fa5ad81af6e5907e0d2c3fa39ad3ee6c095443d43daf2f89775505ec62fb4864d385fa45665f50cc842baade0b6f887d27d1652030b9222fe0af1a3adb863d3078c39db0912fd319abaa3924578e902465ddb1c43a8323c11ce47f3e4eb79f297975600d141d455ca1422ae3a74dd52c8d6eedec5bbeeb1c118db6b04a223944509c7b3972391207c02fcd6aa0b4bd9e76a0278b2bcd1b20211408a62b4e6aa8e6a1a1b7f9ba50f9e3b2e33f7fb60d3693791b88c547656afbf4384da0a082998a195e22f04a07cce50ce468573b0d4af90ed7ce7c9989108f2b3b0a1bce7922df4e3567b6617276443bf69d1843592db984eba301211b877a5be55fa0b15a339e25f0b05687d18500ca59e5f9594fbc64bb9b9b8cff727906dbb40dd62a3d010ed6da5e1512913e63233a2c4b126e258c833d33bbbe372784d315252d237540f282b2ceb88d941c44c3429f1719dc785c64b81d1ce0e143aaa2d7e7e2c4b3f431366414d0a4a7be08429acb48374386dd5fd55525ba9a9e87ca99fc2181d894fdeb49febceb4635e44b83dc05fd05b43b7d80f846fadc91ab4f106f23cc67f7aaddfa2099249355cff13d1e9ff25e42633da2241cb033903ec8d2e6b3d834ce1e998604df210039d52686e59d74d36390ceebf4820de8241a5cdf2943618eb3e2808abc0d6f40d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"846247c38e3020adf7a3a085b6bea42875d52bf635c6db899b03b453fe6fe178","proof":"94bf15cfa237e730e927f1aaedce77b93ad3e70199ccc968b08b55ff7b6fcb76acd5a77d9d9bb529a15ac20d3db3653e743d31285e6e94b287188c64d2b8c977607fb50e4b187dd4099d38bad4de8bc04ad2aa11cfd892507db05ac68989815114f7d83f1bed11d06060f0198397625e2a9b7df14b770edc3b953a6476fb5d7d93d0c1b021dd124f578b89c81ab7fc28106e457da30bad9657290f78c898290fc82abb4e194b79689f1cd9bf39ce8f6d0b0d0e915ad0460bcf0abdb975c7f800cd1ca3f93b1b9f539632aa501cd3973298378fb81371474de3c485b6752ac2056e8ff3c38a6b6c5a9d6ac1336b9e1a74e023fdd3e44929a27b7b37f3f1b7c1103c10d980c930a69150a24863ccee7b7bfe2d7eb37e85acde699dc0113588683e64b148ec80ed37dd8767092068da126f74e91b563a86077174c51d305a61684114518803d237f05a8e06b584012532423f9ef27aab8cebdcd85badc5af7a783fc8f4f128d6e918e41837287d1388fa1fb07fab733387270193e97ff6917b97105029c379022682015fd9a51f5ad696ccd50706fc5b25f0bcaf492d5c48e0686506904ff06e5595d430a9ce0e1c2ec59fa7aabd393993eabac304bd5bb990da1068f2f460abbc481b5eeaf5d701459a2d9e5400283118046ac9d586b65fe25c050a087f9c09a30406d5122e31f4f6da0f477c2f42fd7510ff9caaff01df78424c4077036ffef6a1ce453b7ccd11ca5bd965d3504163e317eb41f8c4870d24b45aa4611a6ff6ef2fcd48fb4bedadb219285f14afe0ddee3f5ee9c3b24d182c762a689e22eac932d599e7da67305f7f8fece0f9f904bcb493ba576d5628a8ee1d00f2e210b598bb5ffd09e00bf9bf7a169c4342c96b780c73fb574b8c3b6d0c06089369c54a30d2aa72c99e2ac36872ad9ee588bbc8f7bbbd90d55b2aeec160f308"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8cd37d2a160d02ff7f0af78ada4294270a124268ffb9414a5720f1475c74bb78","proof":"98f1c0550b41c08e9012ae66e315742b1374bd49fdb9ab89e0a6f9d81bd1343704918130b2a3bb4e36650ff4223c421c0979987562ccda275a54795fe1b81871c636471b35a80391bd61ec3aa7eb4ad1d64f286ad1a1ed1236560d8839f86a0454a60ffb36460d9aad1ecd973479659dcbb2b065445965f9751c073404bd4d6bfeae5328b910caea9aeed1fca24e91a8a67c409ebe3a9c14d4cb25f8fcd6ba0ef4d5698c7efa10a2ea91bb0ee9dc5fe8204dec843a03c3f56042c9977c40c3050fa3f7b5c14194ffbf7ec3d28641b45f22c513be30389c0d8c47cb24141c7d0f589b80321c49cc3fc4eedf09ec3ac583aebcb7e5bdbce60d8871dc915c7b6e43c4b36c0fabdbeee19b9cf0cea5502aaa2fe48ada29be7a3c19b3476bada6e704264d90e880888618f5562eda16557ec008e7de82550f1b04dd46fb567944b637aa8255b5098ed8cf9bee76cbe3a56f8173dab7af7623b6bc30f2b45972b3ab6e2234cf36cf2ebbc12fb45c8cbe98269c9ca93af703d88cb31d861a0178a4635e5893e7eaad01080caac5c3ff5090c1d7064244d5d8255a8d56d681b6007a2255defe5da3487cf90d395279dc3b95dd61d6b4df22812c2c8af7c7c90dd15ecd3630f7546636ade62b7f0b88cf2b5ded2e643821477765ddc4ae066e6bbe72b568243a5cfeec2f664f3bfddc2383794aa4069b4a3b5ec1e2f1243b8ea9fd0b8d40e2afbab77b813fdfe159b85f113655e1a13306b8f5a37d0dfd1f03d428bfe3777e9bb370da4b7bd7947f1788e013420c9bd330e4d10df8b975481b825e526e5d20108e9006903fc68451ec23bd0043a9f3ba2185cb1d6e2e4676076072280577010ddaa0b46f2f7867ce4898dc1a14d2174d20f23885d77f0acc49cf78a5260cae90c2971818e407499cc457db041b03fac75c70f0528b900d4b87af8591ad0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"06c4b37e6e4ae95884d84017699289baff2f0fbc485d023f3fb9c9d850b6b907","proof":"a088e2acb8ba2a0e5b4b08313679a680d82884679c3e88129f18c3cda1615b435ac26d0d3a852545cfcf10a7c59f273c7c9af67ce66e5d9259f5e6df184ed10316546522ba137b13cc2a0934dfb8f18d778a500d85a7a11e08e31b55d1259d518c8c4900e2fbf7e8e15e408ea5e91a567040f3ab7eb8ca5e3cb4f57dc60bb6434c0127078b498b35237654471e3245cfea23246dd11982b8fada3da295263b0df5b5d06fc7465fbcd0b83f25750c8fc1918d6c09bfe1530eff8c6c107cf7210a26560f177049b196d928500cbec952a620ebf41308e3cfe678cde7eb4512d50b2e3dd8e6b0ff940dd780edc470e165ee3086914baa186043851a89b68bcc2019106f04eb846f15dc2b458713373aa71d91c2b5b599d06034067b5910bd1c6e4132b0e2e35b71d1a052294ee218e3456fbfdc20bcf67158ca2664015474b34c07740e869e12e3b217eaea9d0e8e3fe9134b61d3f9ddcf7421f57fcdccc0d52e2e366e146c9920d8d944818410434a2fb7b8f5fd0af0b52230bd6c7c34edf8b17c960ce3f34984d61b51082e2b3f838a24c2f64cfae01d7cacb79abcfdb4e1ee2accdbff596c584ba34cb4272219ed66e5ab499ba818864627ede3481bad99e9140cd7fbc114e99e4369db5b159db39a2119a232274a2a89497c7ad907ddc2a467da025440808589fe7293d8e782860bac199d65a2bebb53c12f3c6b7d79043b1da2e572d13b684387f662c16d4e2a03c6f1e377bf8d2072f17bdadd0a6ac2da6f6a3417e89c090ce27fd0e48d15d9b6acb4e26098a192d22b115d5259c3f2921bd6bf4cc53d3de052d6a9a458af4e73b03b6087d33e15fff3dc5c517e8c3214620b7b6eb33e62f472a8ca2e30bacc91fc15a775390da2457a1a27bfd29bf98e08bddf47ebbbfe85cbf7323f72ff5527cccb2f2cb5351e2db95a24b6355697b306"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8677327c7853a6b3dd03022f0fe773193f3934ee7aa019a3c10990745bca335d","proof":"0a5c0667bb250e440d271f83a551ce995bb0a79704996a7ec3e7b352292d6c25aab88718eaa8ce055c3e187e996fc530bc717ece7fcec36b2af2305ddf4d3d19fc9516810430c833846f9b3032b07f29fa52f748837923040ca404ff721a754c56192ec29bb748c9e5bfc98aaf6f27c085b8a5fe3de13e28588194e97cacb640ea2bb523f568429ca0386db11f3dc7ebef60dbdcc7c697d6af1c98e104eb2a08a95b46e006fdc335fd3d41a6e092a1befe60b3466a3ee1a5ea250c30ebfc690a4480882dc65bc06c3ed30c3f1b79e8feb84b244d15c48b5e9db25792ac4e9d0584a8822a70a9047628299d06463d614a44ab7634d4c0a773bfaad421f89ef93d940b5396e6ce84d9e4dc37d5d596e0340e33aeb0a12b438d68e21a3157fc5d422a9dd074a04942fce0ccb4e1edfe6cc331049b9f6759261a7b6dd849365e4974c234ad20c43bfc4d3b910af1e45fe148aabe067f5aae4e990c3299af8d441b1754c1701ed0fc7693acac1af473ccac905b0c6d98c58e862cbe7d40b2ab04bb125e7686fc3f0883f6bb2321b92395ede75f8ea823b3f0dd4fe4420fc40d9e821188ea12d5884fcf0644ecf306a0ca5432dfab4aa90ddb80779bbae7db2db99b27d80dae13a9aeca73ab8428a0c932a7feeb7245f07b316d7fbec9b94d9bdddb2ade5bb38e8baf9efb8879761df1861850444382e523f83ec7282f1bb1b4898c465e97bd1f1966c89a6c36f73c0ec21263b4415f1d5de40fa763c56d81bdcc172708058f7dfb75a301f9d0b6ac94eee9b5ff527ec64a9671ee1d0c2f85ba9d731fdc2fad483aebf64785167dc8e9333ebaf52b44433fc65ac581134a3eb0004d4c5bc1fd1588cb0e918142e580a90ad916fb20957dc585936fbf93f76a92f4750d88ddf3fe053587d8837159f7d564630132fc2f1eff2e7fefd5003f56350bb908"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bce896d70a196535d30921f3adeb3fe4147703d78dcdd74d9aa61d167e4c3777","proof":"f2c0464aa486f08bc7ca1daa33a11b106b27504f12254a04c02d6d20d0966722081ffe252680902683454f82f49e8970e91f4f6f5093c9ecbd0615666c509161daf9b6dfc817cf5ff1009f05940694c0130245654832c7143d8946728d873010faaf42afbf041fda33a63d3ff333115ba3d5b7328bb6623f4358fd5c936781113290e54717471151483a4b539b5143ff1a00b3694f2462be9e865288471a100851a363a6936f656e96c1e27c15e2b616c56820dac12da3b4b0f6524eccff5509b29d862a1360261fe0eb7fec3285717d44a15ac4f4c4a6e1c24d467b8a037c09ace2dae11aec30aab4d38e36d48eafe55a7f87cc0468a8c96484c16a246b300604512d83326e6166752aa8de8a24be0ba68060b941d7418f72e7074a7a4dd31c9c58a2bf2825d12b9d260e536274c4f3399c6d36ed23c92d3bcf5c4b2948b71686621a302e8e6cc613af9e8c108001d63bae8043ea0b0e62607fe4cdd9d7466b764eb41d6e4a6b16497b62ae52c1ad6bb5c8715aa709615c6f4698bb4e4b9f432840947905f3f9f69f918ab8f72db373b7441fd3bb10ee41097fbada21dc753ff0a9783b1c590e51ae5318b4fbe98b7e677d82aed56bebadd89d1e7d34fc834132f8eb54cd7991d6ef1ea4fa70e79b6ce1da5b2688f23861353b01c25c1c621d7caadfc6ccdb27523170bd543dce20fd14dcae189187521d8dfa5a9b436f0e0df01bff555422ee17b3e106c761f2b44a5690fbcf76dbd76d33ed1f1d61920c78849bf5406204382003de2492fcb6c9136d2bcf334fbc4da482cbdb9218c9362fd2e9067681c4cfbaa01a9124e9340c06d3828509760de74b4d02ce1281ef9124fce72dddad6c930b33def3660fffb5f0e79c2e30355a2495b5d4248f6ad1de0fb7cfd0d070c72a696b830021ac6fddf5eef247caa76ecae9d58c053c3519f507"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"18aeb9df4e11cdf193e3ac12479c13a485d466e3ef9fb9bc471adb5fefabcd0e","proof":"94573a3c4f25607702f3a89cd3b8ddaa7986e78eb2a46582a2a6186307ba536c78220829f8de33e4e6a141170cf29725b6d0ad8ab9091648541571f6aa90cd4674f8d02acbd6eda990513b2ccf2c467488316ac44b2a7f2f8a1c9177fbde351284121d2352291d503f7fe8c673423117ac3b24ba3fbc7d43864dd3e1cfcf940a736e111e95a49915a56619f63ba8af89204f1d0bfac54465a8aea3083fc8650a55f5a84b89acc26514a631a31044a87729c2a17a7341ca972b1e036870d5bb09d45613358faae87eeffad51c513568220d8d7deeca739c34004845c54e28eb034ed135a80da7e43b9aa216ab73a096cf562b9f59e9645c6a525d2c77940eee1d58e0d50e5f49b359c783abed7cf6c229cbb763fd53b337c22ce66086b22813410243b0da6d2b1f509d11bb7abf816222ce07db70a2bdc8a04ba56e896164113b1019a47be55f3a1ffc2edc6069b255a0f019719b4653d0b135c4d45e4d95a2096a6564d546bb7116f5e9f9d612994c4c019967863cf90c995869fc9bee2a690334f9b1b70e13c356e67929bed02b2b8c5f00c7b3745039cf149f1756e0e66b0530971c83e0edd517b63d2995fe226190b3216964e00097c1f4dc3b3bc23c1317584e336c4f841d4e384359bae066ee52223629f6e6d40666f0b7264b3a051a2d2620c2279ead4a5e759385f5e180e2f6934b7c172279c807b6f6d90b8468d0482890e444a9ee74757655abf905a8c175df5a703e0c4c539717e3c10a7228a83de87f14a32210f87d4543bef71a999101696880730990e93cded65a1c689ed350f432bed0382b83c54299fe5d409c8e27cda71b9013ca5a9abe2467468540755703d08c21530feb74c4c6bf7b2425681aaad479367669ef11f3ce978d365e6c0fbc6ee17ee962ef7db79f2aa6ff2bf8fe99bf9db37d3385cb150fce49a899d70c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0a845a5047f1ff4625e6fb2f3a77042454810b44b875fb4112881a5832b3eb18","proof":"5caf6ff1401070986b8e2e81d62d7436ff684fb53b38338be5b66784993a085c1aaa92aae065a19f05c9efb8a09b95f32de2f930c872eb440f6a14f1c59d5c56e01ee80d53c323252919c66123090291210512e8747da58f4823ac81af60174cc0704dfa195710e0dfd1bb246b2345022dc8772789e83f3904410e87540043403a7b00be21c75d1a3a075ac5332b77f85337f4716dbdc4d19186edac77a1260a309f86e5e52a1ce159081e29fb011841f80950492f2ee5571d398aa471f759018a5dfc214ce3d715b892259d2d883e026f4ef6fd381fac615ba439e59d31d006dc2fa8f8522a69f2404cae0b4f2f79bc58b1c9620450803cb5fbc09a8ebacb59ca8deef001844018c8a03460400a93030c8d2ddb7de83fe3f37b49962869271f565b8d58a42183cdd94ff22dbaff69f2a6605fd30cb9306637612de6de19a46da46292594c43b42e96e3e7c9965bd160413eb0400db1fce8010e6ac978ab413ae605cc250358e1474cde3bf74c0474ead265be673b31aa493be85865983e5f183872b4efa735f2d3bb35dbfa8ddf6cd3aa99974558ac72d23aa79e8dc278a47f424560040ba2162674f3181e29ed60989df5f309ee9c8a715cae59f6f3853033bebcb2fd20795910c91b7e40c4ba9b74f7059e12fb61869a077e9b275b83c165e44778087e4852b95bce15badd903993132bf8fdd4f4c0590eae162a4f5a64357ec009396a42366c8f4780e9c2a167328075cd4d3f56270fede9803d6c062c714e80adc43860c6af13700377472d6026c0f48806d2ec9571252b33625e6a5f44a89faaf79b5507abb3436b8b10c88eaa74e251dc8483ce450f9ade587aae79061acbf603cacb23a9a327689a5b4eb8591cfe26002b3a06f11504dc7604620708e083cc215be671e8f3d4d4ab7557d31796be9b4cb79cabc0d68d7f68b945e205"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9ae1c73726d144ea0e1d1cdce4a268836a047aac051d3668f5fbf18514868140","proof":"aeff779cdb8a16db6025214f7824d4f8ec733138b5f1f06ab6f71500c40f6a51a6befe8fdc86e76beb3a3eead33f10ede7176ebdabb925b41b2fedd7fc57e706aed769a6d01071d25da3f28fd2aee83b189957df8e10d636f5241185326bfe25fa31eaa283faef922a234a169fb5807d86254748489fa96d8203ca4b1186ba4588365b90c0fdd9002c177b3ad5d0527ef9e5b525bfb05587a934fda074309d095e3b8e0cbd9fb9007b4282cabe4b7f82bcb4c1a1e19d6bc0d15f501426186b07c6e62acc8cafb2dfd5694226b4af75bf04bf8eb91e91baebefc4e4bc2e7445047eca102aa63427b2505a9ec8eeaffdca32c2eeb8852d0972d93137356da9e26dfea76b7eca26d5b8e8cd2e35331dc5aa4b2a0ec96e35c5d35bb2f1dcf208134024a9f81032bc8a059db3431e6e302ca6712286c971c713ea1775f6cf93d71b4c3c316e6cbaec3441bed579790c34ae79bcd1a60ad6a92be3dbae972433fe4f297c4051b861ffd6c8f175fbf84692711824fe67774500dcdfa91ce24954c5bf5c3aa26047d3b23ee4daed1d991e5fe7449d9095e659423ca0fd23c74a43624458ec11c7951502714aa52587a4928cb9476f31f23388e56423c6ef5d0a534955358c8053b16dee74d7160b326d9475dc7d8015a6337e98c467c8edfd52c9b0a61ba251fa090921528d4a08a1933595f2207f8aae6dcc0d3cf6c99011ee51b1f359b67f7e7079a5434da7626617518e324495a85231083f4fd8880546b3632f28639ceb41e283f6e61f4cefababd93a64f36df6027cd1fd39ea7ff3ca5f0003b51fdc7f29f323b44ddf9c7ab7a647674b3dded35f4c5098a22cb68358c8d1792911d71b489d39cbd4debb0a204289a7200a7d73768015bf721c3a6b6d2dc628970c71cd2911cef0bb8756a185d7a0c2e9668a89052353e74d3a4951715abac56507"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"74533b2d9e855bf729a85e90b36a7636a267f003a769513e3bdb349312433e21","proof":"4adc59d84161c5542866574621ea17427c38724b36deaac962d912d0d59c364bded3e635584dda298283745ab57ff8f28394f4df345c16ebac3b1b0bac3eb86a9c3ec753a7b028a8ef3928dba25e10d6795a5e9069cbe080b193c2c6d43fec72b0646bc8e10c600f77fe1b71c4c8c37ee263cfa7a5a6e70e9f6505ee0937080baf1f2e9a10a58c89c2a39a50c44e213816a7849358b739890dafaff68c5d2209d4afadd91e58383f53c2059c6301033fc3d9f43d62c1bd393cc56eeea88b06009e7799c9f72d8356e981e85ba42641f6ad1ff9d4fd38799a86e6be71fe454e07f8a2177f8bce002a76fb8a877e3be6997c597ad153d6781848155b14f8578707561a2e6c03c7323069f98ce4003e6b56bd827831a64f768003a1837ab3e5b40636f7e06f9116ce99407948eb9459c21efb8ffc66e438076ab58267483c4427482e447989522ab968209d4e539d4309133947dbd928c9867f4c5fecf45c76ea67529112870503d48e145de94f4fbb53798d410a8f5d116ee789089a3062804d0bba56b50c59183048c3d05527832f16f691a1cea651b4d28c430e01be71ecfd63363f8eb21eef8cb55254fbba1e31d339407322ea9a7269e7a883a4335822f52d562c46d28816b85b2df473c0f66cd12478d4f00f554987017fca653a87b3ab4858a3aac73b8e3b7cdb941ffdc4659c912ac3aecea6b18ae28fc2c23b4f213e3e286556fb0c77744f92f7532c25b0314ece1d774f38b8d8cffd3ed8dea3be7235fa18aea1a208e63681b11dd6c0621fb905207f721f75bdf7fc31686a84b49b37bc6ca71d48803f4d88883cfd49e7d27303346fed6955fdce9b467cdfc5956b5835d081d820e96faeae9951e42aefd491b73559f5fb75f3bf8eeb8810cfe89309447bcf6123307213f248e716b3e21fc878c32e970c419173546eb065f206360c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"48bab5f37189483755c3af91d92bcf3daf773d0352689f75711106101edec01f","proof":"c6273a69476f9c69d31cd3ec1c7af79be394fc1f544305d77be8f08566c33614d450ff5d331eb8f223120a592f88adc7d93c1b5e99becb758e707b2e274cc94828aefa5caa7f281afdc7fa6a952231b160b0a54799066bd6bb2bfe1613d98268b8400d27a27cfc05a3f1b48101f963cb8a7b1a7607bc9257479c20a046e06642670144a549e453f602c3daef802e11edf36a5ff38bfa386e8c317efb20937f01c25adab01545b92cbde25f6135a23231febfcdd082d5abb36a378b50b0b49908705b8a0ff5dbfc953825ffa9e758f56d796dd7b0a9cfe655f9eae028180b7f002ecb0c61c436eb766366285d7765f574e1bcf738909378e898864ef54c63dc507ed19e66a62cd7330767a2a2d29d07589db691b69a5d57c39a5b7e84327a1f5582dd1df2798a00d568b13887906f38efdb3a988e86769e293261918bde9ba918b04cbc7889740976c67a8970de56b53c906b14bddf536517155bc8c821daa363f623d2e95fba3167655592d02e4e6a3c81f0bb8ba1cab1231b17b2a81f0a483dee42c0a25d5213783ad2d94c05c8ae0cfe8f9af7c92c4040a3e87b40a2c03d7ba21f1e60c8d5fcb2ad442cc4da932b0317d5095fca6649b2b26e977d578d5343d01f3390355f0d882dbe7bde0f019439ce9766c3616114bc0e6e0422ac5dab543a51e3904832e0c55a18e7d86250497814fa1ffbf842d938315240330fe5971486b863eed36b054032c3c6b3790ba7df38ce8e84ad55d4a6196cc5c4db0bae57d0bfba5a8884d390d7c6fbe78036a464127c61f2cfbc803451f71be0e66bf54988b27e68e6a09030685d1930f9a1cf1793be31cd57dd3c27af69ddb22afed026ba96acc74e55c4f9cbac6dc360b5638f90dc2560ed656ad62191dc05a5915407bf3161f5ca18a940d2bb9d7061a942f7165760463352936935e3bef4dcda570d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3a43d691e3223eef35859936750fdae904b490e0a5df01ebeb49b3e6133ce02e","proof":"f089f2e556ca507462cd0c3b18f7ff8ea057bd33a85dcce1bbd8deae84ba3f7150368acd0fae290f200537f94cc235132b8ca49a281ed06129f17334f91e8b2aa8c8081dedcd92c9c84529ff6a1e4e33efd43292e20bd6ed8e74cd956c17ab39d44a7ab119e34d68aacf0da56651b25bebb9a252072f10bf2afa25fbc76eb86349e6ade4354377d1b350777f0db3f97e4f1c7631d41078acfe47c8ba9866320ec2551a064e48d100c175e6da43c5d5a562d8e2470a647ad5985a8ba00b318200cf1c0b596f1928a45fc41e3a1a021eaa1e155e49eabd77f2af978479c1b2b20cfe2937176ff46db8379772b829082955814fef6375fdcd7028563f8b6151614daad7a5482129ec49327ce17e57d36047ff5258e021869700347eebe73387d66e42e55b0d08a6965703c750d5a56158d15e49766d19c14bd308e77decf5107c12d81ad8b0be4c70c2267f95d4fddc69fddcc5c134b2b5e9e3ba250750027eba6852968235a8b74157ae45b1456fe87d93bd3303b1fc6a2c00883affae15f4dd54de7e67f20c371054879a2c6847e75d6857d78bfe04134e1f895704c247065d05087949842db3d1f9f2de8f5bcff98f9ee0c18712dc0217b8a1cd65bcfe230a4610fa6e6252c24b08b5da2aa0dc02960dfa97e75522ab659b7ad24dea4f66227dba9085bd2c602acb51b6834de1d5982e069100e691492f9301bcf00a2a60d666d4f8b213ab7a140b73114845431962cce0b09477b3143a5f493a1dac24f4d52e7c411e2ba6fb1d16fef50137edd35cb444ce57437f2ceb83af69600fddeaab57ec25d0b4ca8201202f7395436c2a7a1b2d35e5fe0c610c7f632b138d3bc59b70171a687cc7e932807208d85953070abdcaf831fbd25a22c8b13b2604573ece031e183ce605e10b2dca14343ba5385d3a5037174c746a165a1a2bcfd11d87b40e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ec846f32b0970ce6f5270f2189397bb262f518d389ea706ef19c2d31dc6cf70d","proof":"902938049a030c8fe472ac93b9abb9f77a9b2137a399dabb7cc194369a599e403698fd29ef99311aa19d8794b52f9954c6a86bf3c84c651b78774fae725e2c450457fa5fe49d38810aa5000960fb42726b8ef1cc029316130dfcc5a082652d2b76fa149380981350e28ca41f8acaf06f43bccaee4ba5f0b8bdf4fa5bf720e37dea939782f2998ea7a69ea6fd9540d110a603710d8a596d6b7f1b6af7940fc60e3ea0d75648c181df25b801efee6bc35fecb766857fb68059bfca2c8a270dc20ec1ad25ee0891fec0092a414333b7eb2c7fd942881efa402dcc2299e185bfea0a066be7e2a2430252338128e5c3335f72a7a14ebb2b51e096af2572d716846231e85b40514bd8d27abd039cb7c631560ce6638a2b7f2edb7a73625a32013be947a468f31432b73a19a00e9da356aa021d12722582f166d0b2318100a58963313d3c076209cbe36b2bbfbca3d80a5ecdb91fd497147f0642919dbc63f09e501a178a228a9f1dc9260236c844005ed66a11836c1f902ef3f67710561e5d6d4fca12a271afd80eb068fa5c7554d19701a4fd2f1586dbd25baf5718ad73c63c6dad4a2a9614d9d5f3b44426edf9aead242871b6d241b71a616f4ab2b2e6d996a1c2506e80f9d9847fc85afd826611087c52d9ddd97428ada3c804cac65f523004a57d725864687a2131e51163ff387d344d713f7df7be396fc5ad25b487878bf64639d8ff797b4c5e150d87a7e0c98ccde375e41718e0c6cc8f0dd68b4a0ed476084ba2f4a462358e7a1568764dfe5db5b37a1e10852b4801bfb2b9d6a03be88b4e105c636629fcd332be223c7f05fc225a3dca5f1f898c545c376992c22d47d76237450220e8ca5294242299b9d8d066fa8e53a8a6cf2594ca6060b4b3d54e5b5d075ee50bd7f3996aca447946d1c96300c788573ed8e925c314d88ad01c6f82460c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"58ded1c20102a05ee90288efe0b4a35754630e28fe5cf53f68a85bcceea14547","proof":"dab9f495a6db0970e3c4f65d1965b10fe59b0dd0ca6d14e67c2160eca6bbbe6df212143cbd6dcdd06cd1503a3f3ac3b62e6b7b34279a1af34de71c52592d4a5bec475446d83ba309c7de09e7635b23602c7f6fd153a40d55c1c3f4b29ae061243cac43372d306ce1a19d9fa432f0e864fd55eb65d7b2c2d06e36eb334ebed54f58338981183a6f6c3e5fea34d3da89df0d855e7fe73b6701fe37724d2f30740be4a6abe13cce47993e1340c348449d0e8df43e355397a9ada22088ba4e91db021b7a7645db1ba4143533f1f3d27df082ed4f0148d87ccb7ea6027d246cc28c05249a5b30bb2d1a489af8737ca0032b10a403ab9b9946efc116ba92e6a35761709a58512ceeb64b3990dd1ab5ebf3e68bae25a2be9a5005aec5482c88ace4d05036fa37ed2432857f74a2da8e170399df3f3daa191e1faa349c3b4c87829281099cf7fb0519d8f7d0b0a54f9e09e4c2be6bfaf59af0e52dade056faaef15d6813424e59421f92ec203b7de057f2c4fb4a81292fec5f3bbb682a11016f64056c538661f45f6ea6579d02f772827d65d04d6a70c1b1cbd13edb02a86019daf8ca782238d44de0a091d42f61d4f877535f21a2092286afc27b091ed4aeb7cf33a6290220955db4d98e9dc7c7fc1587276ce6addac579e645bc44185a45157010f10c6c18342be7926d752e64cfa12f189659d5943e06989500ffd22b2189a65254317ed945a67f02899e4834cdcad9882587b66326cff09faa00c0a24ed3c5c1eb4668c3e62cbcaa45a840317b5615e5ba8ace32adcb6ebb6ecc9f45ab7f83e9f25e302a7093a94f447dc77cbac265f5b5abd75433be36c9b30330706f008bbafb1a26267afdfc6bf9f40260ed179348c74007c34b94ffbdd830a3b5f2fe552ae3052aeafd39bb401036225d0037c8cd494567ad165d223c116032d22ccb40c3bf0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a608743b28c94c3026d66f339fd56930782087de6043133764cf8f7733881e4c","proof":"d2128d4008bcf3effc7c7e14d687c58211c3911fc674c4ce5e3ffbd93212cb2a4a46b48b54853221e0dae1e0cebafa0b12d555f046da167a1f489effdf438f7bc846ec0a6df5fe36e08edfcf330cccd52c1904b8d7661988e241129c43d08f42d895a83b9fd3f063cbc1e47be0a94cade406d6acc9d80cf335d6ef666f4f4b7fea8c883d157b95356bd850cadf86b13aef28a9946ca7bfe0068fe3ea2ab77f0c5d553960c3848b6e941ac1cb3089d0ece9c4be5d3218a9953c388aff99099f0cc5862f2aabcd1a9ef6828ead315d9702e88603a705e851580ffaabf53551870f0ce5038f2af9d05506fd41ba2514e4f6b69443f09c3f4583104bdca5c19a4f6b66202bb4d74cf49b0e2a1608e30262c422231a07057faa7ac61188c63d49d85c9258e2a631914e5e2f101b62c855791d663aceca72fae98782d8f4e4cb64275b34fce0818d6c57e4afad955aa80bde74c2a5a2506181d2904ffbd551b6e4a22206509405715e8cff954ff68b816feae92ca8df6111057f8e7d1aaa3a851fe51906b44e2b8b53f721ae1812cb2024e218bd92bb2ef2c986167982b8fa7fbe955254336315168c251d2ba07b75dd1456bc3c166f04224ac2bf925cb1150062921472f34d95c7e4d50d518ce97fcb816c4b1519a5c2c2f7ed9d0f6192fc5b1210554cfa22a64ee208618f374e76b3d1f73c684e29854ac13e9ab2b5577ab11e9414e404ac01e020d6afb029dfa7152034f5224204137aea05f972c875b75999ae2b509191504e2367b38691242b09e1112cd5f3fdb7a0c0c4e7a46e2f612ac2e96f52b4be05f5969948076f91f063e2469813f44ccae0934c83e1955a4b3941301dd0d112abacb0542b32cd16a19c0131d72f6c09300e5afd8f16c21117fbe3b30ed64549dc557bc0e5c5839567b6bbd6348374bdc10d415fa88206f6148088d406"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6c8f280a867824f956e1d7e287778724a5cc97f0b31652e3f6d09a45aebb9f70","proof":"e46ba7eb1a7fadf41ee04c14c0bf19fc959412448f773ebfbdbcf4b7faa5b426fe9cb05d02d31e1df6211fc2ca8fe8f1783d0d6d75f7353c2bd24385172418159ceac5a7457a6a31eecc247c06a0a7201198254ef9427c433c90166b2918c508f03a545fc976cac798a1729e9f19c7dacf6ab4e0f6ecbd6e25b1f3bfb3999a1d7d9b628e33d81b5354c4e8980e4dbcd3c13277b4e9a9be50aa9ff82c67abbe0f964b9f020d3b95bd1e17416c0ef4c601988db42d9c22c273b65936ba449a5207ce05760994dd2875ab1031f6a88d26dca5f3f26eead21d33aa880bfa5af182026a9fcb7521f3482d7b0eb381a9914609ff18ccdebe2249ee813f63943c9c00591a064094095876733e73ae3d24d0058201aa2bc12fe007a1c6b4c375fe99237a9cb49cc3d75f73ff95fe39c5d1682662647896f09636fb38a846e28d7284cd361484f823cff9bc150ea00fc8236d69da76e26ba2c1610eec8b492862ee5331527e8173fda5767e5c263ef8c6713793e4608d0951015a17b0aaf2457fdcd4dc70ce6db991583e698e73aee69ac1bda26ab0acfc32fe65356b3bbb4c6c5d77862c0ac1a97cc1677ef9a3675295d200d85dbad9fd903ac4490ed191660ceee3e014d815263ef32f5b9fbe3bd6cf43d92e105d797401513b30a45fd9da0a10ce424f945be2c9ee3ee23467e54ca0bd6f4ed0f635f12a08af66b714b1f39654729307d8614415196b54d37a33f1e5c0f990055cf322f47d97d552ae4086852285b90922150d8bd38feb8d378fc3b866a7d2c5c5d4d00a05cab2d5a599d927961a0c50e605ecde011b19c83f5a0e3a304dec377e5fecb2b79faa40989310bf7394de3bd651b5fb968de14deb16ff27c262406eefe3b594927ce8004af2609695c4b802f12ffb2d6bea3b758b04f2b7af1c81301c79a16851312265633007563eaa4d07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"16bc6241daee918fff9079934f6f9894812d0b7be41784df5b7e35ff21ee6742","proof":"02115c1013eb12b530307ce58ab54fa4b50c5f2ac5cf04613dffe45a8d16a275f073dfc1096b961270eae612c07306710676207ab56c8e1563a00644c1707f73463ee0c391042b0432b981c51876e05d41da9c621fd50aca9f1abf1d9e4cc0713c5a32a5d60aa7be86c817201829c6d249e3e84af73755dfe463c674656c4e18f2d71d3a2c0f852e7a259ef83e1250d8d3e4b3159929a96d05abccc9ddb4a90c1a0b470d4ded7f7cf2a769fcd2bafbfae34104c408d3179983e21a401ef1f101d117f083bb5d41ac4aa30a8ad03593ca149e7992c7235725bbf444338135b80f66a8708828437abd44c72606b31db33ccc3c3f2ff3f977a39b771ba5598bea5fb66c4caae27b4813496bedb9fb70c85ade716f56605749f49f47e6e7be21834a7465f566a6401150c26f188d0a84ea089e28523fcbe0a03b3e0e16a524d28f4ba64d4adae00f3314260042bb01d293055b5d13f1952f7df4f394b4ecbdda2b5db42c28a091128eea48d7945e26c04139584965a0a7234987f308f5fffc9b9a49801c1745c623ab6d30ada954a34187c0b7228b55738830311d4bb29c23ed622a7464b2e166fd09a58cb1abcb5d5f5cd2d872234b96e589f3987b9c5eca55da282e43f76bc19d5ef319f9cff0513120b8f7604827bd6d97b1e8d9cd4bac1dcf72327a7b784fab36a49a32d78ac009764e72e5be1b1eabc6847cadb81918172a3efa1faa00dd9a35b49ef444ebb36e6bfcc121cf64bc53d9a3826791ea83a9bd12ba4b54aa1cece9280660d409ec464972a50d4fde39f4182bc3c58d628c36a54e54837871d6b27be3f3893c6125fee1ae33c6dc6d00e89e9e0f0262cfcf7853775a4e66a968985dc43cbff73fc063e1b67cf4656ea816b129d6b592b70b6f110053eb328081c9887302bcfff8081453e89c75d8cefa281ae811f0fc5fcfeb8500"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5a5dd8ec6c9510442d516002c6c44b486e2dab7ed1c2e0a51539741fccb3c203","proof":"7610832e215501b293c29436420a4c09ce331c849570bf1c9cc91bde21b092613453b68cc52fabece8112488bf9ab40fd799c249acf14a8c79220f7205e8d32b22ed8ae2e2e647db2ecf9e88765463a71cbff63fa523dc05bc39e6afb1c2f05114ef346b74b82b8b8b07a9e5d291f48c06e5a683a03271ffe39ca863af03fb67869f2cc6f889347b8b9f36bed3678308bb08a01ad1926d16cbd911247efbb60277319f48d40fc6dca4c9a1c0c3aed5279021a5e4e8dcc81934ffb7c26d673d03bba4f750fc2a2332a8349254ca4a8be7f23453338761b043c72f0acbd7a86f07fc25663d3e52521ce838e9ab3774768e17a56c141121492e986ebff3a48b1b675272cb2d354eba9cfc3b563d489d9f058fe7c41400b34a5b6075b5ec4332874f2c56cb0c680102722acd8cf5eda05532b69011a42337c052895bbe2710a4136f52d7e43cc4312052d52391b108ff4aa529d05e6916c8b4113415d59f4f8bcf0e16acd45d49cb9c5078dec4a35ab925f2b6accd5b2ef1471f88278ee06a660d2a98e7a0763433206d785f3fe691849204904128148d7d4f27349b23c7b8050072b890c865cbb5be3f9c9c2b54555f6a6ca71ea179f1b3e4ca4af2f7ef99d3ed14ccc73acc8d8fb2ed87dc8eb0c79bbd52415b3fe252afb6e1fa1646f125e9162d7cfb8b17a8b360594ff0033fbd958c98d0ad29275d2775d3932e090742b4a77f8caa1529931665cf2049252b43790b0588cb16cfafc872ed90f6b6e3acd34e7a1a45de9fc4ee743f973610dd223c689996f40e17f6139a68c496875042f68c2356525aedaf838b761ddfd3a9dd51ebc3f6a35df036fcf36614830558e298b374dad277554748e564f40dabc0a1831dd7ec1c53df6ec30e94955a3b8452b3cc05e2f3cff8174884b5ddc7add61da5364763ad943be6cc41cc2229ec1c6135dd02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2625361ce7e62ace30bb7fd9b13c05a2f83829ff4f079766c4320e51dd76c30c","proof":"5474f289a52d183469a518ee8e140952250ed571d762f9b4d640d1d6771328637e49a7c041249af981f5001cdff17832f8b6d07a3c07e8e4505ff06044eca965383388cfe7dbefcf3ed6e10293af549379db6843fbc468dd782f59460f22dc33ac2cfd44bca11aec559362e9860e81af74f5481c1fc5a6ee60f795a7c03547426c31ac322a7ca7783e918d05da019975056c887c9042f34c0a30b75f14acfb024a1f515c4be4769676a59796d407f74888129a5213e005c1ecd76da1160f9e0b356e5567c99489e10b5a9b2ae25db5deec342962ecdbba021b36d81d78f91700181b5f344865a864b97e1a3cd59a6c511348f6601e260ad8248d771fd36dd96366d0871e53e74ee9ccca21cf6525b5f250415d892145cf22c2a8549654d49b310aa5d5af60e706b3a101623b4b578f5b97ce963003ff0d2c73a92bb63c623345d45b39cfaab127fa64e6776b9d5386de1c608b54385b81271a7e1adb769eb85666bb95f1174a4f6642e01ce6f4d24d06c8e0214f0e31e4b79b79cc9b377b422e6a7ac26f243450ab21bc3135002cb7a4c0485303a474b4b9c318a0e03c78024076b37ace21ec73021effeb40733abbbd0996c74c37e5422fd27d7713747543709626603f6b653ce9c7d0024fb333f2f863dc20a82bd8a4a6479eb872de76e15a88f6bb03a73a01959c91c289a6e5c4d084f41b3739b649f098ac9ac90de2af57e2ff874970d6ae076c4a84b169131287d449e4c5fcf4773eb060814f730a7318baa1dc496b9a01965ba50b1d14b326ce125c307260e79ff321e0dfc70293076274ef6727317ed8bf1f1294b886e9125bbc3d96dbf09be8a826b2f2424fa1fe2dcab3d15d6431e5b85f613e16ef9190a2efb1587b24f48ca09b8a8bb6398f900e7c4276bd05395421cd83c5c2388b847842275890ca4e8958193a6f7a612f1d04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bc95a12519e0fbd8c0e8e04580b43fb29661bf51b986716a51df34ed35847a31","proof":"22840484c0062f419422fb1f2d40198755d8909ed2c530d6878338b1d5093a71d4633e1e9b917160e6ea7781921fcf9bb3f1d593bfc730522c43ca29ec42ac5dfab5511ac425c2b61684e99404d8400ca0ae820c36995ac27a7df9195d64640b425b3d4f8ba19f593a35f74613c24e558ce9e85d329a23d6a9b2671ccfb0a40c49151b876740382771fb40b48cedc69442631e874a8ecd263451c8fc6a9d0703f1a1416975e1b9cb2eba4c1f3d03c77c9399a5d815400c232e0235f5dc341206a63cad9a1336d426b6ced57dd88f5bf4a88be5de41cdc6ab87ecf7a2c900ff0e7eabf6d11d3bada84c6c0249071b69289e105337af15671f1da5424b3d6ad517fac8bbaff8c0576b28db866d5fa11454d782a81f2ddcad0986d0f832f6b7e5445eefcb4a2f8b7c2b296af9059ac5a44a0a611eca5486430db57b8ed3ca011753b0b692c92bf69e2a0434e3bfa084a7698710d1c4c4e4b611301ddc69bef0440372b00298ddbab3ee7824627f8d82d38e73e162d6684417a03dcf895994b660104a28bd7a640ea8439739c0a30afd2ab220c8c49b4d2269f73861d68facadf631ee6914a2b1af78d51dcadbf8e5e6a7e6d797ce010e8fcd9fee48a5d366441f7214f2927b49b2da79e89150a64eb8c6b38fdf6b9ad04f4eb239c6567b20b7ec6a36f09d7b2a74c04d424a59de1229c644414e4d45826bfaf4d97dd400ab0afd1a2237ca9de49246df8cf658e7cd8e93b599db075e272b8c001bca71492bf9d2080c9d85f672b59e4858055e1d7b2f1948c9e2f386b2d9ab6fe7e1afb5bf73ca5f0a1d29a3539177dc166faf02de3f3411ce0c30e2302997acadcac8b3e2b2334544fd913e4a72237c26e9297de5b48696f86fffc21cd320521d60a0a04b15f70b70f6e22047265cc725641f27f303ddd895aa13c506252f3feb13cbdc9ebada02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"10d0d3bb95099f0b7614b56171642795877b611d686e438c83026d04c9307939","proof":"1c5593bbb60bc3bac6e5ce7a1b7811f5859881433cf67db3f6442f3ceb5941057817b28d666dd3fe09fcf474c4d9115af08db58713cb1196d3553206f65f9234684d7a205b18fdbd26c71d0bdb333890f248ea332f5da78d3a0d69ee3834e9573060a91e30b37fa7b1723523471d2c67f028b7d2991cc03ca17405cc90bae90aa0e2549ad810641fa46682c6bfb669ed327d034a9ead85e98c4ab96370dc610de6caeb77819c74759fdf8421470aa24cd786d2d483f25b674e22c497227aa50ef4fa4535f93371ba43b1644203eb1a834117fd1e8895a1e43e117624df55d80a24c79eef3df4fef2ad6d4342ebf22da437ca6a02d05c9f6602e53d2930e1e97bda0b9c5a595f6207c46597eeb1b0c0657664bbdf7c1e920c92da8e48cdd5414f44962acf344ca3c94756a74a81a863b175d0e269eb8a5e209101674d8ade1d3b1403bc60cc1119aa54c22983eeea1d90d1e2b00a6a1193af9f5acf615bb84d37baab40149d06e9d07dd6675a3e384e91bd21d12104652577929c4c987df7b36eae22d044985135ba4ade1a96ccfff8930a6f31d9ad71a106e55f936b5201f178b4c71c887bf4337a6df2de4c26f4ae8567f96ac8f9757a8d0c1deeb880b1e70b3abdf6d586cfac9631ce355840639ac83b2285f5ddd0ec3c41a939ccc145e4609e3b1f11c9715c2d7bb100b1b557257bf14fb48769ee23d28d6c934397aa8c3da8935a8251402a1f8da603180581e73d65a4a8cc7043c474664fc9db72eec470349e902e581d3b79df23f0e43a029e6efdaa5e581c54e0cfbbb1a2da3c4f586b9cb2c13559898b992324f5c3129134f77e6996b9e1dd3af693e698f334fc604e1fb5ee451be280e4beca1bea4a83c2f620161bde15f2462aa9986fc8e7493d0fe1c1100a40248b20367156a6de6016b4ba9f1902528a97efe1da1efcc40ece00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"241d544ed8cab568fb4dc6a6bd088df0aa86dc3080f3ce9d2d766d643d1ca53d","proof":"36d4a0b263e0c11685e5df5ae46d6ca42bb18ad5e97399a324265c5a04d7554b5ed0273749c180957a34801cb5fa9b029f0b81e0460eb4456ab0db843946644a1e00a0f15359d2348b97e5a7a65e2cf91db8df4076e4884a3e8f5b6eb43d33231cecf7726cce3db52f119add9e156a710bd28c94e8d0acf3830d458ef02a440d79bb27043e41137cbe8f51e6accd5fd155262206c1f1820f8cc9245afa4281034aba3ed1f6a02d0fe48b6fdee1d008723e9654f9c234d6161de8dbb4314cfd0a6b05259395c38d0fd4b5a0e240f2d4a943b4bc935635236c2b3db4338d4cfe08c020c5028dfb8e9ac616c46f9296e061452b0ef3cc8b1d595dfa1160715bac6ad8d42129ddef56c25f048722d439b7062241a292f92dc1c1cfd7596f6ba7bb13f4c1a96d1e67b48c9a455f611e16402fcbced4e77c2a0bdb12b57cac262ae93080650a90232eb283304bc8534a30855ecda1d1a77763ebd9893bccec72efea7afc038c8a0cdb567b80057d21b766bececaae206a36fe5a1b6f27eef260f02359567301bfadb8b7e01af83987f151cb0ca7fe9f2b028e326d273609cda732b83f3eb30734624a401f97ee268b1293fd0b681ea9db926d14dc4133e2b9a81c997f7423c6c82950d7cfd11e7e7721b193273593dde8b53ab01b601d03a9a3d3ff18a05b947bc83d5ff1cb4045e0c9f5f1229e57fe15d700cba8277c850b05ce4a2dec70254646b80b5896e618a23cc9c7d223d369bc7fbd0cf37c4a569eb0d6d8307a083d93b2d7fdc5b2bfdc650084b4f6f491cb0921245d495b0938d6dfc2dd19d497a820b05580ee3e89bd7a3bed21460dcf3180901e228c351956b860c61306550b7083944da800ad1d53dc518ac1a94ae7a2311441a2847ddfbea7220e5503ec6e531a9df08f1bba65831f3aa393094dec188ab4afb959b54d28479262f104"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d411415bae4b74244cd92dc71cc79e4014086460df29063bf1f619e488f88504","proof":"3ce957e9e4aac159b135c971a24263e5de8d78a5153f900b22dd987185b98a762e550c6f5aa6abf32414057261b9649e53dcaf77350a9492c1ca7ddba6045a7cf04057578bcba17c50ec5ddab862fa19ef9c0310fe8e03377dd8dcf78842e6428a796d34332a4e57b999d555606bbb7d87954926eb14e03ccf8c50ca759ddb541b03ac8f93925f0605dd423c3ed5c509482b7bbd76ac5582377687b678509a0eaacb34417f73c1dce9c22289f28fc1ed3a43c0e6e24894661be59d77c267c2040735d76a20ae896ddcbeb2cdf7b541f5020bfbf663a05fd7ced62ac25ee60c0b423014b60e30f0e5fc51a4468a89272bc12a3115b2f04b60d3d090a11a8f32324c52f5ebc55ebe054d65a77a14649380306b71d0ffce505ec114b660bf6d84658ec6dce8e169bcfd35c6dbee0a9d05ff728e28ac8d4ef1d1cfd5b647d9098031c49efa5ba0b31cc25c5353bd3c42cfd2e11c98d9dad38b0e21459739d5a24966babc0d1646c57eaa9c5f4520a6fcf9c2e1b67a3d07127b77ebfe92df59478238ec7b8f53fd937de294284a69f134d12aa100d0bb25c064eca752de6e5a3a7604aa132415f65ca3434fc270ef2e28362e8334b2ef90d8aa86e5551263f5ef0b1d92a2cedf649766efb1a57eba8746b9e1016ecb68c62cfb96140fe71404ac59336ac98af4f8ab1065d238e05236badd67c46c5961842261575f860b9487b2c50a4cb62dbd8946434b5134a99fbeeb8507095319b1996b23b6d8059af9749edf782adfdcb79fcd033b12dedfbaae2b7562acb298d25087da94df6e6fd916130559705701bef6957f06474b3d9b4a821ea6786f939bf9c0171c3d394afcad1f371194fbf8508d23b3c57be95187429a93c2bd306c77de2ddfecebdc7a4a464ece089fdac370dc12e9e0ef3eb4d0ee505702307f915066797e803987bc965dc1420d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1822575c38d972499929898add200b511eb9c8ba9679b72b3995b9146c4b0d10","proof":"502ff806b747dd91cd46da58c58dba54e3744b03c7ef0f20351c1c470f52a674340b32a765f1b2ad96cfaf028411e92682adb47b56e2b913773a0e828fbc6b20621a49ae51a63bc453afcaaa88694f13023635e171c2ddb3875cdbc8d5926850d6381a3ed38417bdbf75d10ee355adbd145517bc6fef76bb27578819b82d0a4d6a4edbd888c30e3d7bafb5680f5d5261ae73ff71e23ca12701a511035310b3003b851d57a12c6943ea25c56942ec9eb7345eed82be467fa972c3105bd996b50923994edc81a707be40aa274142a47adb595e8db7a1fdb54996ef30061078c90532001f11456c96b4f2978dd6d386ed2deea94130b6ed67dabe5a1dfa5543ca1168ccf919142e32553a49e2be4612d60b4e4ef6701268f169d3c100d61866f16d2e9949140a943f57f953b69cdf387f2a45d6b4fd77ec5d349b5492ae9374d44c806a1f46612e8d36ea6843992e8caab795a63fb39b73b7646955c5a60f59372a0cf1f87b793f99646f734e87a0498785d80088df380795db5df9c4c7ab8d6e5892b1f563586a4ee9fcb795936d8c1695b2f271951fb229d56d7ab4fe9d02ae502e3fa46646e669e597b44ac44f15082ee27a65cf9046774667c2ee08dc46892a024b361fa125fbeff08148505ffd04535aaa06d006475ae82fb8691f627efa615c8922584a288f72965dd3db31bde9f9fe939479c5ccf297bced4372e88f1a2c5eeb02b8bd70a9590e76cc97f6a242e77a5e10241ac60d43f2a260ed08a10d1876951ac85421ffa76b4833933bce1ccb7e6471074f4bad174385ed236d71ee42204159b0e4cb850ad384f7485331789bf289aa8bb7940dabbf01f1206fa3f51707b228d1c513ba8dd2f9d58607a91c38cf472d968a451d622e2e9e0e124f2c03a39300ffd8147b37f6d0322d56c9cb3670fcc23aee9ebfb34a1d3e8f405cd907"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b4e0962ab12ecff88bcee98c736eaef5bcedfad7a0963e32adc26c623e948d0e","proof":"84f35d6c9fbe16739d85c66ed856cd92bae560cc0e5c4ecabb60485dc09ce22d100794804dddecde6f721a6322711fb7a603cdf39d94b0bbf5a3f6e8344a573aba623316c4394920821aaa77ad7c9fb00c24d392ef0dada8b13ea90755f1793adc4ee182d88981eb62d0aacecfd4c4caf77081084df6e7ccbf51b61d33cdc03907ac8e1e5549b3d2dd666f82249bd2055c962f74518e7cc8ce6784207e592f0db4f07d0f23728ec48dccc8b2cc51ccd747482d87445acc9c8ae9a45cf3917e0b84ae070eb4e24fac2bc1e260dd78c0b78554e31f2abaeb45e46cd53f3e49890618fd8fda4bfd9d9c5984a860d0b8dd12a5459ce20d6ddab4b92a61c0876e0e75009ce8dba67d0bd3648ae75006a791c8a00dbb67e9cf9b37772b4296a7d0ae4d28aa4bca9c0e765e29a5e2b78c942514cb88e410c4397404cd206ca6655ee375f8672ff76bad92cc26245f5ace32806578f23600b9fd236ad608a2c74b55dd77eade30b797d4c2c58d4b1e78b9a5a360133aacd6bd1742941861460e3200e85ee0cc907e400dff3a0ff6d06b86b4067c38198d809303872c2b15bdd9319be4376c217e88c613190eb3fd5fc4ffd10e0add9e7b0c71ae35516edf68322b497a715228b9440fecd0620c9761285f22c170c1e68fbb307fbb436c922b09ebc95d79860c151f56e125934f1985d3c4969d71daa5afb65a81c07f96e3e0c0d702936d1823c958c50f321eab0aaac6023683c56bffd2e1a5ba238ac68cace96573292320b115cf9f4818820df5b10bed505d0fcef82cd2d9a2dc69fadf07417b2c6a6ac672d46d652c8763508e846d79dbc827da99ecc3cafc2d10cff96392075d256a595b893438ad50341e391a948d44aa8971deb81f68cdb89588749e9501474401a493e50c02d876506bf1b81ef181c7001affa46600bccda9a566feeea20c4103"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"50fc600a036a62fdbcb79569a8e32ad55475cb5fcf360056ef776a7601544b6a","proof":"9c8bb31001a086de62282aa9afff2a24424fb3646d982a20a1216a1f40e5136edc85cee557913183513c3a4d625c4f5b49de4471a1bc8d3d447c95be44744547cea147334a4daafad7bed7ac11407d054907067c74338cb751ac00d7c19f970ee8cbc092f6c589e7bb0e94a0d6d5dbbb4b3c15ac30ca3bbdd5f714482d8a3f1c774141be7531305d02c8abe232e29683f9e5c20d0b9d56dde27b49de66600804c491e5f269a9b2421698fe9164e7e600dd2275d1ddc047bccdfaa40cc0c768022332dcc18ea66980f24412fea824e3937f844a76dee9339c3c4697c018338401da96b59dc93d46339b3d2e1016af5c31ef513934f11a1b2ac5122450916fcc490c2e9904b1455033811e6a677ccd8d797c700941c2621e9a5a313867f1785250f20c05d399c6801be76a9c7d9f20461d544823f5e27581e4513561b765b59c058404a7f52cfd119e4f07e4153d0a31389860e18e5d86b0354e26ecf451436d10c606a7e3a4a74644bef674ac0d7e4f4c59df08be60e13129c6c5e915c2b4f510001d105632fb4d30adbf198424560d0c986ec690d626ecb923063025c5fc893e668aeeebd006ef49d1158c526e55b49fbfa501f0dd380373237235a9546aa06d44f6e38762494a8cd526fcf7616aa9f02de2dc8c3ec7fa2ffce8b9a16184944c0c17d985587dd5ca2cc3b16f721ce70cbf65cd8008b0bc982b01f1298116eb26f83588426dd02189aedb3f360a295dfede328ef686305a95dad612021a3fae6e40ce7312f810427dff60af6a1762968e6abaf0d743e905786e4da61e1acf080df86e9c0e2abd2c245f4a31d974d8092f109dba9652e7947dab39612c55a1ae7ed5eca5b04c651c7a4515ccdafef7d717b43091ee38cf1959471e924838045c070d0d8d74c613217d03862d98d0ce2ddfee4e89a2e38f9f4a3217cefc576e6d08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2038eb5aaab2d506f2a57b9940724eb8b45579f4d69375c6391aa4ba4b971650","proof":"0c41c981807f07f9c667df12d9b18dbd40b042cff7f08ddf3c0bb581c738d1111c155b88aaeb394b15514f9272d238a685b2d6395c005637089452d13fee657e60740436dd83b8c5c68100c8a7c0b93ea2191c855e4927d9668622a37f360077a8187a2857c79c5975930d0be567ec60d7947aa313715c23612e3307e1b9d66af105fb3862a17c64e4e2ef514230e516811c88934d99edc2cd31fd571ec2aa026de5a2b7acf2143c9b162c0fbe5617ffe6893dd42ba06c49d057776716096f02e8d7500c8ff2b5411b20a86a95a3fb2015e421422ad5c2043209192bef0ae90376bf0bde30daa5ba0dcf53b07c88e73f002e8f1d5bd85dedcb4f67a01bf4800c4eff068838b7681154604c5651efc273b3103608921d32a0a52353bd6e5f28718e2f2d4809c524f548c79fc02a10f5320000ce55308dbc387fc06a4dd6de963632fac3fabf2bdd25c9366b43675b487a8a59012283bcc9fea7b49f0e0031720ac275a26175c0038ae5320378d02de9adc5eec94d200e61f925409e9f7025ec36a8d6a5ae90b8d2e7931d925423cd9dda545442cf26aadf8974fb1c236e0d4109dc902c2e558dd7dc95adc8545aeb9936629e578873e03494430f60e60d36e475f6f037ea418bbf7dae7da0b70b435e1220d3ecdf3a73af7495021b52c82fe2112a21c977709719518a24624fa36c7a9697a5afccab871c0a914f01d3c751a72794b5038a9ae46d5e7a261358ad850549dc0badde248afe25889fda85b3333006a4d8d388143d46427c6167003b19de75c5127c6348df67550ac50cc61d4ed23aa44845fedadfd3f0c31d4b6850a88f792623efa2a65ff3d71b86d4c4ba90d347820b842f0c0f7cf4fbe9f9db094735586d80d76b38fa547d1942ce2f7fd57905a9ac8d09ac3b32804f5dc687084a79e9d9f516d20833420ddc2f10c95d28230a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7a514046e5d9404625ae2cea1b9eee0ca525bf8a8c4c326a5372a1717001c23a","proof":"1074dc9303232d784542340d8ad1964ff6fc8d0f7e1b312d6b47bd5dbadc677b58077b6bc907901064a1676c6571611277afd81500510d039321a8a44cace06b7cbe97d5b35768e30918e50435f524cc7e8ff6dc7bc3cb3303224e087f6e213ffca67fb524705cd2439e0be64d5194633c9ff986d224b441a5b4779d92f9e5725e263f9899a1b4f3d12297e52ab5ee2361826e8a4be4d967745136332659cd01a4f8cd126b337459e3f25b3f52565c381ae0947dcdce72fe9464152e4c7e67010a7b4883a69846b075d1084759e2e0f5d4ee4825cfb9cacbac0d61273c233a0fda02f984d858bf49d1bf72041e8367af71458ea91ae41480e5032bf0525a54688c907037712539abeaf22a12950d792d93d6978d302f11af2ae4ab251984545498a4d4d2e0749d0974216cc0a1a88c26ab29fc51d8c6479fd9557f6701518a29942d0052e01addf968088e7f511036169063a704b2b18d9bf8eb26492a59bc6df2cf21d082bcaa30939dedf1796e06e19b6120292fdff11255bb3ba90be0cb4d567fcae5a628119b2d3565b5cc6fb2e0f3787800be1f8a4994562b2a9adcb826040100fe625463266537c3138dddb8ba74d2ad27a963dc5f11c70a0b79aaa51baed5ee61de6b1ff01739b671992299ffbd8219704cb8d712f5c5965dd0cf49738449d6706bbd7f62d9c4486e4d962d3fd405be6812a7b801a93c159b0dd67a7a1ea67e650b8a314676c4bf5e1cbcdb11b5c75aa30f2b5350402d139bcfa4e4249cfa3c77131c4a0d4aeacd7142ae1c1d313ebf41bafffa3b670439667bc945329491909108ef22d4c4bfa8fb700037af6bc2ff489b3f0d490a165512e860ab61af18a24eb89b5a514e5c2dbaa89a0872e9f1df43356b9f55007751fcc777c908a844c6e100c83077021241d277573b054d6640246fc293e19b7dbcb0b7505806"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b8d4bff952cb1f26b0f7d732217c295f920b114347d6bc3fee048b34e0739a0e","proof":"f81cee6ef17eb43cbe8d6bcef0049f41c9b7578f347338ec226c4962e25af5139e32f37ba2204369c953df862993f5716e48c76aba0000ce8539516cc4d6cb3d66ed380c25f1f68da846994233423204f539f95231384589374af2ba4d04d12c1e50deb77ada13416cb2f38b8056b2e8405cf3706c9bd5674e5ae3b6895e7f792a6ef00645271ebbd6f6ffccb04c3ed57b1a2866d5718c744fe3a21ad3e1330b3a6be3f2f61602e2847a1ea4a7c79db2bdfbba07d5fb1ad0b2b38ed350553308f44aea32fb6d7bebbd943d71303d5eb52ac34d1b2e8df0bafeb06e1893c5e70000c78565e8ca15dbd8418d86fc8102395176ececdd5ba854885d1ff5ee360a661c77524b310d748cc347b29559525f66b8bc6a1a3d2e8456cdbccb5ad0b5482bac0b9c4c43d2e96221719222e8da2258423b26d4a0ad8012d15fd1a56a0aa4354c26a71f5e8228f03c39aeb739faca2fb20c6c1cec5225cefc206f03a30b8d48fa982d4ac8466ea272a7dfd8e4dfe7d05bfa2d3f8f478070f7a9a9a2c6cdcb75ea77e097b06c9bb21a1f95da64c10ff56cb7dc694069ec773fa0ee8eef73fc469ee91f82422b084ab768a9317ec8ac3f8ee8ec9a0d83f023327777f387f9cb7850f97f003e1ba356abdb8bfa5a75dfda8d47298a11cde8beab141cdb43e82a1fc296ce6a3e0f7119285f79937f4f8fc2ad4da5780ac1891c33f67082f498997c723a44ee7d163363bd522ea71604849442c86a4f159dc4d6d137289f938d50635e95036f91e69c8f18ee611ddda1074b8390c4b81cd45e05ce7b73305367b26f4eddce729ffd16684febbef738d6dad8caaee8fa29b3035c5f3185e061958f3fc4a6a71d1b87507e5bca1581288077b288dc325eca8cfc9dc326b83a9b12a006f30a220d35635363d29a339cabd7e19225a87a57d7f1ba3a5da80b8ede2b280f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"062bcac00b6f5539769fc276cf652802bc47b65bd30bc7bd1b79656609027c34","proof":"9453a1365397bca9322674c46343aabe244cd03287716c78f55451995e26b116a236150b597744986d4a04a0b533102ceef804d24821ee8ee128dd5e64c92153c86e8cca2bac04bbcf131f78ec7c3e694a3170056568b89b9da1dbda17ee81650e7b960a0f9c6c3bf5bd7f9d22db16b74bf67ab23de02bab9c5c351ce97d1750d4f758e3225722cd291fd7c55cdbb8ed56afe14c9cba497d7970afd0b8a70a04483d7f6033afdd16113679111c58707adf057b6f6954e564831f9f40f883a301c3189611ee20d9e30d814bb1ec7ae688cc76ef92cf922de2c85bd682dd6ba30360176e7a71e443ffa8ee91e4e6319dbd8673c30e268f435d3a53df61c840f30d424c3ac3f9ffa1eb4269b6485fd77d01718a43bd7a4e98eeabd0d0916483c65ee25d06a262e2d6d77569c5d3a3ec2dd8a4168d085489e4f044882a44e0217e1730fe43a399034541a774f75be58f487ed26f7aa55162455ec832e65cc358bf1eb2f1f8ffa79996403c4e8e6595237ca71b5f3fda910950070ee0cd07ebf98d115af63776adda2b3a28ce2e5275c04687849e04d98876a0e88a54b3b8f216ab49d0001c28c75e2d6a2d0c9fa6e861e57b08efb644f3155f6a7d9f302bf9b8be58ae1e19367bd05120db6c47f54f84e454871bd317a5272391879a8f1ae704c8634205daed525cca6cb20f8722090752e5248160cd338763387ea344bfef5b924a62ee9e5893a1065cced36bc4887d768b10a9df396cd7d463dab8d2d388961e307c5e4e248c8f53a1173b2af688cbc99d7d737944005ead1955af4427f2c96c0f303ff868fc18e688b7033fc9ebce1d157919dbb483a81f1de6e5816701f3182363caca378279f921b209575030c4511d726ab9ef96b052e714fd0b9c9e5b3a077d6f5a0bf4e6eb0dd78f52a61925ffd77355389feefa9fbb36f65cea4809740a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7a2534fa2cad6fd5309de9cb792c94942bf546456d6bf3f72b415b28d80a0702","proof":"3eb136d8ac58893f3eef326913d99596a3531d77bfeb50471ae00b0beab256259adda376371be11f008bce03fe63eea10e7759fe39db6993fbc6163d99e82c20fa5b00d25e4bd4c134900da8f120e879aac6685521e3afd2b1a04ae3c6943660e647a04eaa1c35a4eba86e6c49adc8b2d2315f6551e30e8575b11a902446d8017ac389f93ee03dc48c17a1d9c22df97e7b68d95020de78b4f380456cdf176f0eebedb83856b0c7da7b8bcf17c165db454be74b2b6a66322cfa9d8a3315c71607f08f6d2c6cd5808a021915de472646201d915da8a3436372a33bf9fab129350ba0e09cbd3a3e1eee0b304ec41780296d9530d1f3bca2241212533745a085171cfef09cab26ec0d895e094256b0f16f224e2df2285d8da400bbe37e4f65f1d72e5ef7b3d8bff5292f7e2a8ea4f462e23be5a4367ba9b0da55fa4c4b2ed4d52c1b244248dbabef9bb6c027783e1c583d47a8cc0ce26b408c79422e37c0e8305444d8466aba954dd07e37f6174eabb16d494b41edca523e96aeff09c49216b4ef36d09a582586badb44db59d5b285cf4ed1a11b9556ef7ab62071702f27a5618e36ecc27d28c80fcabaa56ce7ec0d74f76b9799384c0f6b80346576f94bdd3112199a38dc89154a947db64bbcac011058a15700ec0468a7c7168cc68979cee25f657835b74b209a4692cb738dab60419ada84ae1230245a2b1ee8857fd65cfdad48902972c5085b6b92b10d5da90d7e840adba8765a47fb8e90d2e98e6e4d7439077e8d8e99080e2f9272314f981c8c3ac859507989a0c31c9b9714e0e9cd6e846dc80e062d7a6ee8693368e0d478dcc1e1aa5b0514c7b832ff2e3b08da2c7799704a54480bd462abfb6cfe56777c5549577adb5c32e34047c7a727725174b39b0154234942badcef2092671cb60ecfc568d0edb29b9eb45a274d42e84a7d863002"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d453742d1b765487a6493e5c39e4699684d6b51784da1983c462c83269e8c470","proof":"f822645c795a8e3a8faabd07e3f4e7366f492d2a56f0c0d14f8dd0dabe21f05af6da6625489a9c3feed6e6e143dd7126e02f3dea21fbca20c494c12014d8f84704ead029146b4ce50051d9fcc49b9e071baf3d7fb74163278552acedb9df903be8ebca58d97c43ba4dca2c2c56471f247d81a84afe35ad7805dcb1ee0d2a6327b9d96bf4ff38a2a6d7b64532c451c017a2179efe5f9e66bc3da446a5e3a031097da1df60c1f7ceacf21443468cfcd8e38e7a8a749a25835cf8f00c65b587b101711009dc9659dd9da9e2615718d478476c522f70a81a257c432cc1fbe639bd04fc56c1d0c97c3129c1e44387dd1fc09aa7b7c10d416826774caccb859a9c8d754acb04b1e0682d0062fad90149394d4ee7cb9605d23dee96e485f4f85eb8de4bd0060db271d1b4a98481f8cdecb04cc7c45960d32ec6de365d4d7b55c37bda440458deb3dd9d239922fa5f389dee33c7d4b082ef83fbef9725905515a626c77a544c54f33ab50e401d3c54dbb5f8713606753e273133418246bd9e43682f54289e776e902e7a76185b09667cdee49e3f0b7a43f9e175db0125dc30928f58650050c494167ba824db98c1234f3823eece27aa8d88fef7d482bad79b13f3d1af105ca57d75a4190a0065e93e95a36269dd6d36f829d881f08091249f8ad5a2ef7b60fd58279951627de7b106ad6f96f9fbd508d069cf4ace6f6e366d1ed3531c2cc2d1ae8d4849c2cdb77d1a1e7e37cb5259e2e7f0317be64553514df037f04326aa7cd3450ad5e4294caaaf860c753da8b06bd0e91845cb94a2ee559ee6e05b6fc2b3182e3d8c0d537b5f1fdebdc20b0d2c52f4b6f41cc55f91b50f5d1387597a97f878b111f6f13af4b29c1d224f367cd3638a579a5d233b2785050bd67fcb08f5bb55a7cc4132ded8037e4d7bdab8c7e93a2a4250730e50b06a9da56c23e103"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8c59cd4d0753eda6314b0b1a2f2e984fc2e9b43de1846bc8bb816672e85fdf2d","proof":"224cf84e6cc34ba4c6a41c9686ff7fd5237d917d4f1dc518ef0d8f909fdf0315da14e57855ee6740afba9801950ad09f397fac7a560a931aff08471f77e467266e15287422c2d62ab1b898f2beb878e22d9e4284ef515ae9ffc4c7737618623fd21a4dd2daf7c0da04120e20bfa4352b7f9d548498cb87a1abeeeeb015e79e0f083b427f81d3a16965b7be408af2069642756229033b4c6f9e441acef98beb076a64d11024773cf4c411801f0ea3414533f82a216b57c2aae93afa4b03da9f09fd50e36042f1631232d21c72be542b0f33e24161afc5aaca618020a6c12c0900928855ae5afb61388ff6f29eae63973079105255f8dfbb6510632e47db504d56e8a99edbf348d002d843e38fec5c1a1ced071d2eccf0f87ec6131648b36a34786e4a40ebcd0bafbb41d2dc348423536dc727174376dc851ebf135995733d7e21e6bec9779b637d39466a8d25ac6ed737d6906aa42aadc712305208d6c23e3a5eac6ffa29d0eac4feabc066e24a3cf80ddada1c6cafd19842d8e0fe5d9c0b8242e6b233e0fbecbef97467f66bbdd5a3dfcf4d2030c8a2ae30e73ec5ebdeaa1e2d5e3d608dc0e1c52c823cbcb4ac62566911e4f9dfce4c3cf119e29146d882960efc4129fd483c7fac2d1f78770a42e3ab923a494bbb52f4ee493b39dae6259a106ca516afbc98e3705cd2f4b85cfa1c4a61dc9518e08e3957c664d293ebffab17d4605b4ea03e8bbd7d8c01859556051351d486916919d5175545a07532dd753b840ec70b3bff9b18584fafcc897f1beb0dea64a1d256d53f80394c3cf1006d423e0620dd4bc8bce1335d2146f96621386ee6f1a99ade6d64a918bfbfb64ce705394b8c6d0a0768ccff0d1e283670ad3f11a4042f2cc3b7867c2ea990c215c90ca0edac24254224bedcec05ffecd7f374563756f8ef2358dfaa0e69cb021b3203"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8abc13f33429b7393c923a93d7c6017c1ae2ab4209e057ab8ad9ce8262dfcf47","proof":"22d40527402fedbc8d38112ed80f239d195e84d7530e3bea032d9450d6b794177695c971758fb33c339c41a52214c2f301b279de8bf345650bc80bc2f2e9d863022eda9592523ce2470c8df24ae71a4675985503d299868a02fde1f3c449676a901de25de8ae7b86151d21625b5b44debda97f546e5aa93f524480fa64fa9846a30e7040c006b9efaf946478dd0d672b63fe773665f74d5e08599dcdb98d990063f86fdae9c00dc1f59976ccedfd689ddae562165ed9232f04fa48d41fb8df0b44ef40abc69915638867fa5088fac1fa67261ce99db357c69586bb307445ac09885b86cbbff13ff75cc7195e9e0456267a9733209dec99fd5ef41647730ffb50f63044f5ee20aed2271ffc37e31550dae40d1b74770fed5fe398e7e09f40b07a0237352f15c6988af0f237f3361f37cec6af22c4ba7dad60c63fa598dafeaa3f9a98111e53ddd7e4899d32c631cdac45acd77781253c8ecf13140a6f530afc3f86dec3c8ef5079ad8198d0ada21094003bc3a97afc53bd75349c4c1382c37b658c23dc98e36af78fecc3db55d46a2fae728af98f40a3bcb87407c69347c6512d64cbc72f733e9ec0cf13670f405d9a4966b1261b8518c259dc8ee1c3b485cd79009581c19588bc96a914a8a66b0f3448e6264f2fe43b504d75f165d88d20fb18e62bd6a42c22df2f5f038928645dc6caeefd9210bc9b77eab710c6cfec8a427e8ae42a8b9c079b5b0b4b24bdad6a832944a171a53a01253570591b92cefcf442329d518fb3ba8a7443cd3f82158fcdf1ebb85f65ba0fa93b405c86c30ad7433cc2364574e7cb548137f4c9725be56a9293776bcffb291b6acd1114922bbf011165d91c615973ce39d97a3ff2136ad5fed1c4a209217cc442c235769169e5ed0ca317c05a1664d5d471aefc3bbc0845f7ba2edbbafc2366d5109fbe9098f69d09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"62c47b9785f4c8ee0554457b82688cd3419cc33f5c11952579d31690976e030a","proof":"cc93773070e4c54ba3d1898f1979a3488ea860ddfe17b57150cb2a1b69b53d5bfae0f13f7e4657876eb2bd229d90d4975ee91400b56fb94bac23174274f3705b8238371ea7ca550f265df24537bfdc6e5d7bd5db642ef015f62a6e7a0b14042bce1f5f7062fae33901e70742759a3aff7ace2451e63595776d6a1581804b3d1bdef6aa9b6795deafe588bfe3d3bb4266e76fb8470e83ceb71ddf6a3dffb0380a5b3256eccd15d98d6634c7fce16798704cfe865c0c7e61958b8c047311bd9c0ee5600ab2554f649464f9c6bdf5b454c428383cc1ff20cf5c2fc69024a6a37107220127d987b0da8dd0cac97b5546f0f53b4a413e311dbfd1d65dd602fc2db3435293a3616cb847cffc75803ea240b905403ce4701498447263c656cd7f6f1f380a2c64fb8e40501d21d30e20c014a8e5541d699e96bba04f32f1c66c7e027354586310230f07f31c0ccbed744e71b1c858fa794086463c5b9a08a0563bf8060fc06fe52ad373f714eef13ecdfd6d9e13b94547b8c403150fdd0a3df49a69cc48486386111ee7a7b4176da350b006b9adaeeaf81dc046459c84883a9553cd0e3e20059300c4f90a8f2596feae326e93122bd94b29cbb11306f6d9973a3b56602160a92dbcbf6ab0d7f9291fda8ae2cb33de59ae54a8207ba4b08b17839f3bf563b065ec4ef23ef60e7c9e78ed6a5e4685e0e7b0d04ba3ba71a6e2a55d3162486a740d86ee245d91d5313304f50a4a54094c71e623c185feaa7ffcdc6df89362658ec5228bd7d3bafd301729ddcafb07108948bd2a42c02c6bb4ec99fb2a930f56c277fd8c54929a75649e0142d52fae3f9218658a54aac1b7069e2d1fc192d750a9a164c2cd2aa9df3a494be54372ad2d7d653dfbc80a43cc4342c070763bc309d85e0e2eab0bffefee9e80f4a14a670d3d69828014f2cb4e57ff918980ea0005"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"34357c776f90b387e02d2430ba29accf9706112f09041b79691d68653cd1f828","proof":"c43ac090335fcc4884f9ea89790d97eed371fd16b2d7dcd0367e54847d6de3185e1a7b555c21b48fc50ad979b4ebc5751a0d4ad47a2438ef9093cd9b47d5e72688ad31f818c344a67daebf5c647ceac5cb78cf8e5470b263bca02142cd181e24761b1b610b5b8dbd0efa72853d01b45cb345c4cf008d71f013254faaf4a71156e101c5768ec3523a5b451347d3686af63fc57033a5c69f5ff188e491f7c4ec0cc9d0a2484718bb5aeb0ab7c6661cea419267d77994d085e90134b1b5c557f205afc6a26e121b51c66551eb11a1301c8ec95c612911b8459e5f97c083cc631106a86f0b423379b1920ba8a5a6e3731552a0cd930b486164b89bba50bd10703c7f820c2ffac40d45444d28d5be73d3e3d6d2522790dbd510c267dc911aedc06355a02e9d976f5da46303120d8086116d9b3f84cc6cfe64d8ee3370646eb2351a42481743c907db10c9f8982580e23f1d6a2a1315e255c9a0e7b71a92a5fbe1a43780ec9e578776c1b02b2e5bf62fe5c1076ee353cf442b8aa7ea3d071878520d6492d32a8de90759b16fe1d9415f3344f0ed3d959fead35446282be510a3241a4d9833b027eaf4967bb13f7a0a44969c3c033b0bc31450e1a803d6b874b7460a77f62f92c8d841ff72e18cde5756b052f9ffc087bcca0dc01ea1d8bc62fb773736b4c5c6480b39c0a4feb7fb5634bbe5267271351ab0890ac165f5ae0168719c508621a1aafd81c5805886f0dcd6081e45708a67079b085457859abd75f229e06b964affdb2e4f554bf82b68f77fe54dbc9ed663d5c49da8f7b472e59a0760130d36070a5e2c702cf29509d7878189173cce36cecf7e190f273c51f94ecd09e353cc613b51dad2c56fcf2d004ada54007613f250503252d14ec32925c73ef35301c564e79bc24202210b398612320099d1aaa634dd10ddc8940f9f3cb5af781d04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4ef29ec0a3bab789e8313031a1f0925b7e05e181a7ad9d47da164336dcb63304","proof":"527d4200e9d6c58f1be7719c492e47cdd8c1a5261ec06c8a6eb003b64e849c7798e2551456187566f19f8c4041159df2dda5996d41de3309079480d2b02d9a718c91ed3e745304e7fa2d70ea62f7ba251ede092f402ae1547eb12c2526e9a1201af4725f7ac3f1f48ff7d6800768023f858f843cc6432ab80f72a9506945d67205ee24efc0bdca65540ae5fd00063192e352a912607b5f093bd7f151bdbae90cca1452bc5efd4c4729053dfa94f4780f75cbdab3e0e7faeb50ea36efaed80305eb132d18c55633e3fb46d6a2e9e23f81c90538c1bc8ab5a4e347f4edeaecbe069a468a4fe16cdc5cb423214949503d3eb6f009840c0c30529e46e442020b5d171afcc08526c73a59b93eeca45ee9b3702ee043696c3603f06a1715568fd0b92334b66c41309b01060fe2e411de15c6e4ec685e2ecc08151c16dba03848afea5b8cbfabc8568b945cdd08891c2722b0919bf0572dfdb5b0cb9cc524886fcefb1902fdc100a05fe826c3b78b207485e697c7820ec30a242f5f927f54fd8dee872f224511f48ca6ab9a103f520e16652d20c88e5c71b75094794a92434e01a72f2a4eb035a1c41fe2902926fbec515424f8fe9311e49fe1963a957be91cbffff71d863ad5f63a4898cb27c0d6b5e38931746c76e38451daa7e3108c43d00058931768f170e3b24e4a40e9138a794d82f6931f3972ac9039aa8c61beebad06d6101a7407b2db02664ab2eb3186106c48649c4dfb49d18f007e7220ece9ee1b462461c0cbf79f37e1aaa66ebbfdab652b51994c1abe4866378624374c25be76750f7e2833fa4f422e3ab6b61f177c3b0946ae4d374b3d17eabf9ac7bf73b4a9158628cedeb3007d2e9a0c6a18b57479c0935ecf7d0d9884067e1a2f497316388f6c025cbd640b42b163495d3001e1431b731a1d471bf5365350b18d9df36802ccfb0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d833510ef9653d7c59da8dbbb597acdb7da023262fc8f2e506d89f7ddd165637","proof":"a083794caaefbd693590614239434a424873dd03ab12235b400d9280e69a08461457ac129d871ba145c57244a30a78e0589ea3828c81e77088cc5cb6aeaf0f453808b091218080e41bd6b8e902cb1e57f091a02e8b011b5a4613beb78806ca2d9a2472b1c26ba30149c1172d6d64209ae9141aa16a5f158e911c7c42a6e4766237e794b1b791aa08cbd003123c31a1a31e45a14f7f26cb66da1e70470d3f6306512ae747a20d5cb4ed21f01dfbdbee27b7dad16397d622d6c214d7ac56148b02121959d5b15d786d96183f929d3a19b6b64ff90708d80196010cc6da87d0f10bd288de7296ae5b78f9536130a7e079d9404c079ae50e45e95561a588cd3fe748b42592ede6abad824a0e0527de8f11ac3ffc2902e44bc5a3c56c7195ace6ad6c78a556e17f14145232e0b9f6fee95220b987bfc45f01124dad4779c6d47f8122940b29f62c80ee755e3ec982164f85085487c17fbdf68db37ff1579bd2f0746a044cb76cf14b3e9060aa355a39bc2e45570bc83d4575043af021315a4e79111e4086acad5c13a3f1d6e57379d5314ebf90c69fe8b3be593dd279fb160c91a12ad42d14d91fb6075ed341675ed881fb1e30dfa78bd70e976c2e5ee3cdb09a4c2cbe1ee34ac83bda2ebd28eb8234b35d7e465a35ef85ae649ac6a1baae034d6b1314e6903d5b88642b7cdf693bcd63e924716e062ed921b8e0e2e93da0a94e0150fa52068681a197dc52b632f783e5f44b0a81cfc79b84f0039d84670088542c5062083c26bf4e5afa5270236c980f52bc9ded7b741fac638a5ab39f161635b8126c77e02d72f308d30bf815d3372b6aafcbba78be624b14d313e2dcc90933b86b6788aafe7d77f2b6cf422b01843644571baf656bb3ced61021081c0f87348a099b7e10079f1db1e56f2444e7c85562934d99beeca032ae4181849ff955f24909"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"907613fa2603942dec5473d9bcb14c15d9a58528633ecee8cf64b6b1621d4134","proof":"0c5ff89aee1cbc76f4c0d71b007b63a83f2ac105cc41cc3679878d50906fad5ce8b2c4c86ad6361318a0e89e2d62891e0a83fbf5fce99411f022f5f537b46c3a9e4d9fe35cf527bf47c9ba9e484e5492fc3108a4fb45fb5874909f37455f43234870d1bdbcf62e395067796e9fe1023b7d40769d314b19b443066e7cbc64840436dbcbbc59dc85ff65957c1917a235eab2d67b36cdf84a490ba34e2656cf180d2654c2be6969b3ba77159cc7ec11b8de98274c2e787a1b7dfd4c8d0e0a79e40e54436531def31073355d78daa9417fec1ba697ef5ca152b3dc365844398fa106b409118a2c8f7504f4805d503130646e5753970bbd3c4f48084e722f6f9561370a0a6e485eabb50a3a25f1286cde720eaf099e2ecfaee8e12653035e8d9df561a6ed41b42b93e78cb0f2e47c8f851c7b255fa7f71377137ed9bc313791e3330f0c5cdf5afb5a3a7b9ca317dc2a6e624afdf19ae3faa06241aed1c3ef47cfb42418c2b665f390bc1300c84da787bee645cd85ed4afce611b93f6b49ecf339510a6e5f0083e2ccd642c645de3f70657cf6599d2e909bbde8df308836ab5251021c9e1ee37dc4bfffaec81b0cc5c998201e3d7e41881ca184d8ad2a3c5efafb2f6e063687ed99c9b2046fda0574e4c3cafd8eff370ae5ee846f9916dfc6bbe59544969fba06ccd661b6ee68347206fe59154f20d9a4231a33710f9a6c7b64f3654c9876d9ebfccaf13b06a797af94136f78863dac9f719881711d01b1cff7c4681aa4fc5c7e6a6915428c1197b164b8d3fd547ee2a79daacc315530ef9d970c385be60a6eef6d2b4052912728c108feb9353a0e093185ff1039f34790728c672e599c1a638d55ca9e6e805f3bc3de5d7ac7dcadfa113e2afbbdc5fc23810d3ff9060516548973d3ff540f3b4bc51e1849acd40881124d34f8c9559110cb0e7bda06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"108283c4e40e9c577f2347d9cc96dcef0f06e8c9505af7e6ad0bea5d2f1eb24c","proof":"703729093c87ea53213fc525750d74bf6b8ad8a7f89c1905f76fdc3c25ef673f302c78b2d17f43c5a0a514b7480878d14633759f58c6b19999aaa97ed2164b0c2ad762e165f51980582fa32b19c467aa18234fab31a91c062d921e920ff536531416d5d7065ced3c7f5ed61550bf3489d2818305237d5ae3717cbbe2540bf52bb1ba24570f16b6640895315a7e9c5c8714cb7b6ce9bcc0a5adf70814b967d20ceb74bb648f3f3fe9e2823be2080f671c2005a7524cd41cf02e1503d41467c70bc2bd2c472b5839c4751677d4e4e30feb9b7e34a9e4306909fb340e69e1fdf000b8f67a6ea6f80f39c11a2865beabb95c0256e2be7ba4865b795df2a43749266078d0439d3923c91a01b28f18f568165a6f235f72c4ad41a0cfa886e857e8da3738fa901a10e81446367bbde430e285fafea70b8c0099ffed692373627a12ab15d26eec773582045b5cd725c4fd2a08c909c60a8239796ab4140c897c2f478729da06ef9cc3aec28bd17e4ad978947a1554ecf16e49e443c787c14f106186da4da851f41dee9479cdddbbcdaa8d9965b89bb13d1eb166b360e85529d9fc501a31a003d98c4bb8066a83863da58c448773bc35b1d2e8bdb4f91345a6b7c693905622df1520da0055b9bf5562e984fae3928d90a384e3d201f577dab279562ba843ccb0341f33d3a7131ab70a639e637a6e503c00f1a5af731845c553a54c9d2d349ce809fc02f29c2057e87a4bd43c7ef974e8fd3b4d3f0f2d1c42ac692e00722dea3f733be78a3d58078dc47dd9231529902e509a287875af97d9ed53a8aec8556af3be81bfdd2b3ed09aaffa57b87dc8c04f724bf5755df847dbeded75198c1dcb876be719838bb65be9bf4b4b6a75b3264a528e41a0450853e2d0577dfa5b03c1bddc93a3ca0c854b89cf5fbefbb96c7bb4b86fb67a2f3003b2283fc79bce0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6ee317391a9572e96ba668bb438e44775714e0f48f5d9e74b00a87b123f80810","proof":"a49492ee3b54b0d3ee8636aa006e04f478b424ac99244cdd0137b4c735fff876d4615db328b7b0802291acd9945b547305e440181b52d799b102341e9012be1582176fdd758839fd0e511f629a3368d36825cd5dfaf7572330ed52649f435a369e299f24134b8a052bdb3c5ab98f03b3abc7c1a890c22e93c6bd7f5109efb7689d7b956571e7163c370aa646a067fbe140a3eef43348486934dd0debf82d3e0fe2ab3b5f793c76b4888b4f45489cf918401480374cb832b16e6ead01cb03bc0cfe874e3db93a02c3b2d74bbd6bf59da3726af311504bf3f351301b45b71a7c0fdc63189f5d1b57a018730426cbc951619b13e6f4b63c8d6ba6396f30a357c452c059368d269723553cb0f293d3b6e3a484bda3209231369334264f06474abb3b7243c48f8b66fa46f4bf40710db0d9f5f08d9d8c9acba8a5e94b32c6286c1654765d826b4474846ce91db28c3c61b5f67b65419eae887ab42465cdf76204ac0bc056db9130521552abbdbeb827808f09346b0d79d9ff43ae54fab869f706b349dcaf655f97290ee5e1ded102e82b97bffc6da72442bf40607cf97e8a9180bc539e88ed53ffcb7c49e98c8bc07b0f8769efd5d2ba51ea542f6cc483afa2f0ad5ab4b48bdb0718d843d852f2aaeac798feafa4537e9eaee1cf4b15a9f19b3b366dea5f91d247d5cf521938eb4168d3fa918f88e0c0f6be6489da711e048e1dfd4eaa0d8a9e6ba2372da5696634bcbb5ce7e5d228cac06435d6776fe3080597ce0f3243e108f022998550899dc22cfaf8d1d0ad07243a3e3488545daba6f95f68068485d07f20f93fc19176f9925d669beb4d5a39737289afc5f5de3894bebb5802ce4c7664b886ca01388f2fe1e4e17a327baaf4f3b2eb7089fecca97029f4ff073ce831cfc3b939d859413ba18f46cc1f7600be98212591319332892813813806"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9211f3120f132848294ad8692c2b3fc4813ce80db10cab8be22a8f5ed8778823","proof":"30c98eff651d59379c14f02c3ff710982f740c87b5fc3c53ce88cbb57b724417d6cf6d35a25beb8fdbd215c8fcfa34a825055b18cb8c90b84836903a644f46755c4c0db3bb9d4853322a3c72bb674863453d13581683292e9f45735f030156484aef57b1a462ef0ff51f517025ac0032dc012fb8ae1dea24a7bd313253719e241391c7c96ec29f86191ae99966dbeba5f56e456d3740bb9338747af42d1b38043a8f52dfa34f9c2afc3400696e06036cf3245ee9e8fecb08f031d42e46a7d5043b16018e2a9df8dbc7630a64e583289efbfa58d54e6d7345b5f8cd3476c3e80014243a1c9f784c67a6b5caaf9b2a1cff09127b7803037e36c1ccb7264554b940f675ef55f5e1e4c3805e2a3a664a95b241ed5796ddf6c1a46886591f79c7fd71f69a5a636ec50d024f392f19d194cc7a2a75158e3cfbcf09f0e9667cf412e504ea3684c2ed8141b26b1453cde92d5fc8f3798e1e9a1c312049712870d9fa2809d40542dc515af7ebae288264371be97a6655ef9fc2695cdf5c5fba1c41dd831a78a7db53e0b1658c83ca2c9407ef2d647d4915380c2601d01157a2820e8fe8488ab59e37b0dbc358869e404a7398c688e36fa8bf2b552f3cc7b1d00081b4ab7670f1a6c451e8cdcf7b6ddb9d6864d64b898f91b7cb5879d4581bddf02796f1038c58070164bee90151d1aa687a13e9bb750b2664a21b1acb3476ce4838a3be6f6eb44f62d0339cb9d109fa744453c3ecc3b11990e154a909858dec78b304c53eba420a1b88f5b634784c1abbf15bd99cca6bf790da4de58830a41ae478f55d3ea27e7d4f17c64e8cadd227132b2df97b199ccd28354b0b5886998adabb31b85676f79c04f6716232dfdc8ca7123c3256d5f9df5b53a13098ecb59943414cbd0d33d774587fd065bc6e2d813e3406603dd6c2cb23a0802ecf7537cd94c73eb80c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"040b326672e23e900485600872371eefc8189b2c7a98e241210307bb7c1a3f11","proof":"86fb40db1c636e762b8044ba0a056f99981feb3eacfc995ab4536873981940490eeeb197d7d1186b2e53cdf81438dc9dccc59297f5bfa9e5cf97e0eaeffa7003c811d218ac7dff13abdcebefecfa4a1688a3faf522d885437e206437e6d5f45a12647dcc3e108a1e072f86da5a81be14ecc0435d79504c7a8318165e4c09243da0fc441e25282fcc7cf3c96dda37c4172bd9f7e3ceec81a25d9f4740a0aa8e05ea092c1ab5d2bee94f04c7de6e8405a634c834892df970e4a5eee52dd055b70b026e6709d0b19f6a9b1244cc39f32bb7b68447baa3b37d7091814307076a4c035294a2c630b83cdf3c7bd5c9a62767de4cb74cb2837f1502ec7e1386b627b402f06d0f15f5491fe629026f949c7f34714611ef4764017a447da1107ee47665281a4ebecba29da66c0111817b715bfc33208f1c93e927e4f18b45664724f65f188866616c517e7ddf7c4027594f79963b4489900ed99daef657ae887512fc053dec9413b344a8e6a7349178156745d179a3706d59b8916a76dce5c4a23e4eec28d460c068139a166e7005ddb78f6bc1d014225b93f7e557604002325866449c7f342b981418dd3934e7039bf3c716e5338005e4495ad30b6a29ab9c07ce448364e26cc250d61af8cec88497f7d2faad2329cbeefd373b68e95f1fd2f3a4817559065c5faf321d690a191fff9914b3e86ef1b49f936e795c2c058260f0ea924d74ace7e3e45c974c99a20f2a2a7fe2f0630e608a8968a11452d606fb5de4491379964664724a64a5041e9dd44c540c83526129b5592645d0a9e0173cfd291d055cec2ba3117c22ad1604bb7da194263a126421f117b4a1b5a3283433f82b42a93df6964d5a9275ad98e668f28b184617261fc94a71faa1ea0d41ef415b6f74a40496dddc622d68be9d92a42d94ae424d28f3c49f5c78a90a75dabc9f468394f605"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"845e9751e53019bf18b626fb62600fb295daf4552a832ac74d7225abf61cf928","proof":"50b24df94f96e7757e120bbec5311e3bd53d2cb0d516107630b71d14d36b741432bb2cec5303f1d8db9fbdba4e0f55397d4a3878209a88536170062fd65f070d66f4a884a588f218664e0b54d5d61cb2420d280f96e6ece23dc1e4afedbb23108a1ce279e5fdbab1d0da29cbee6f7fbfd62595de1a493b14ca01f90b5af5d418cf181e7e8824dc496dcf16b4a61d5ee71f9556c7efc04a7943d9559012fc6002afa099baa395f5eabfb1d9b5a9e2fc68211327cd2f9d44b8ce3a26f21ef51a04129a19b046af79c545bc13c74a4cf31e6e06093a1b2d56a1617cf88483f9240f702109846d10aff6a79e7b0a82b2c9a9f8bc11d8d23f978e4345510003cef36fa81114bbd2aef7065e0bf5cdc90a99b37acd01ae657938812b35394995948c718002243303d729d03b96d2fedcaa507a697bb4608e9187b64b6ca622c6285f2136b2fc2cd8d59a2e9553aa73cad47f84ff7faf45352550b6d906ae641db2027234abf7ccd2ce4fb0d60d8f21a2dfedea3768c13192df41523a742bbaff4b5a332e61c0920b65c279134dfb4c024045d75db3df39b5f1668ea64b99ac570ce354246394e4c8c389f7a17698f3c6b0aae88de67fc7228e18af7cbfacd98e52833a20193cd40214e5ed32e27f2ca92602c3dc1e94e6727f3d031ba2f95f26b71554cc7c276cf3c109f98918168bd88c6dfeae75d811c5502927915bc966b33406385e352d17c4657092d42719b97573dfb1a4f2d20335558ccc2557fea95bb04d419a5cb0168e1be0a83071abfca31adade2dae4b5955c324e97bdce50ac844940c5a27109a18b68f49a0c473022dd09d2285e8b0fd8964a54593b045b4898e3d7ce7e17d3e4a5638556fade04ae934a2a3b4a27a2a5ed7dcfb71d78662ba98c8007e5f745ee6819f46a68357e40423f439da8c1a9253585890d2b5a2988890b301"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"408b52457900920555d367fcc440254d89eb8f8047adc5c7fd182eccf067af04","proof":"58a32edd26253196576fa42a947edb5a6c72d99bdf02a8dcfacf1adadcf3b970dc550d320b95b26d843c0b02a15fea787acfb6addd9b69aeee1fba9cc3154017424eff6e2e76ee3794ea9c3eeb55b7f011fe22c5ca02f95312654bf20b64dd3920f96f14ed494cb3c2b4960864227253dfc6bc72b80efe7e76688ec2b82a611a1f04a7576fd53acf370f8d98dc86223733de42d393b10dd6cce9134f4548ef0c7df167937497955f856ccf93424b0f8021e9b89a77522285ef33f8fd8ce4ef090f6a8d68966717be9f0985e8f81aacf4466651aaafbbcf2c7067e39a31af6700406d3de768686f7abdcece6b5b540354f745e1aae8035afb88c66fef08badb51b63aa06640cab83dc82407f3d7ea08414f9cdc6d88a13d671dae4fd65b9ff1331ee45fc4460398e1cdab7605b95d26befa98b87137dc9525da1dfcbca76d9c416e8127c56896d6923a5b9c8f8b2d3a15251ddf0fdf062a5423b7e27551770d1aa4ad93d92ea184baafab739c95b4fb989d8cb8d032512991c1fd1aea5ca7104b6cc8ada465682a69ac65fa299596c8be72a8637734062ec7665706cc38d466737ca90104463cfbf31d253136eeea7a4fd6c9a34432299f5c25acec2c7ff6e74ef4b684bc2a8eabcf601ebe79d591f0017994f0e65c687ab2b8c645bb9e88c90daa20b37651c67b29675d6ba8cb0d1750a00fb1f74a1412acb8a39b54a040a9235ec7710fad558b08f76281089f1ccf1272c6c8c1eed1a74a6f7e4a9e524c6c430e87a572b80aa7c19f80166c6535d2ad4ae6bc8c2d1d08fa1b9f48401d37883172bda329c93c880e7ffa31a80c678625650e5e015796ade313aa4f970b594f249be301d9a43cab04c1fc762a94c8780af1f5f0118f83524f981f50e371a1880df404671b8e17b63b3ff1f8050124d511ad39e483652d3ee6fc8c473dda065606"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"541abaa5915bc759393b4cea25e2cd645a6cb58e099483360433148524334f7e","proof":"2a51e85cf9c5b5d48725ee961c4e044f56edba080801f8ed8948dfe5a826a778648d50601a3c526b03ae2e4bd805da782546d4657fddc1d6d05512f2e9ea2c042e16ada73a2398fc8ba41b508895511876b28daa0cdbcc853aed89dc3227297b54978aee07fef9ab33d296cc50866c56edcb805eddde14dc499e04a4986acd687782ba14e4620c2fb64049cca03059620e9932af00f800f763fada563b1f6c0dcd04d1c6b0ffc6585bff6b3b0ae613f80dd561aa2768f1ad7e3b1c6b5132f00356f9dcc108a66e0423192ee567b38774aaee79b2412844da22f21864b779d10c5ec50372db91256cad012dd5b57e80e5583fd42cef0286ae454b4c67d3525e3ff80d38af7507d4d4ed9a71cee51c44615ea3973c8b1a63ed64f000169509520b843b6347f35dbcee6faa98253f1d63a44e01e852a7d8762a1937c2b5a693e5382e3a4eb3e730b3916ec41d26d3bf70754ff397fa35078204ffb8361b72c79f76ccc13608b15690ca4815faa611d6a92079c9323f2438600cfc795bc4b84d002d68d90348a13627dcad509289fd3248589927d202efd658926934f35c2dba377a40e1aa0e255d2612e4b4488f8d8d24fba66be7748e991cf13924d9ede8c0f44cd05fdc95953b96b5955c7c293b03a84a434bb732e9732ad16ff3c6b8bc5c175c08bcfb0809a67fcf5ad876a47a5efa12062aa595b2a0629acd2432ce507b3552fe87f5f8c350c3bca7a7bb8077063c5fbf075095ec36b3e83c49a9b5d29d6f610cf299f54b6af954b412913aba24dab473fa6d8566a8821c77937ebdcd9b642306bc48158fc036fa4e3f8465fe1d1c1f4e70e6dc9e7725ce521d93783c61070b0110371cfce91f399103e2964ed7e8a82021d042e3e19fc330275af69754160784e597eb29bcda3afc2ea23cb15cd7077b486001b1af917fd7521af6f6e41801"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"98956261b14bed10e1e0ebea1adf88075525166effed3be12119907c2568b84f","proof":"1a59eae9b2666960637012cfa686563205d015dd2aed0c19440a0a087b720571d4b5eb0e981047ec7e32739c24bb321f14db6639980272b1b35534aac6de2f20e06d9b17eda87101d4562e6f6dfd63a4873d6f76f7fd58a5dbfecb4e1c7c251d5cd27689633a2f25f628dd3f4a8905148a72decd1c11f799f1fd2e1b5fbcee36293cf62d4f799802d850b6b36bd70c49e38ff5d451c08d61df7eb5fd6a0768053bce592e2fb7357d31a8d706bf7e60181e0fb00f9458260f8ac3eb61d62d2d014b1ffa605c99b71a83fefd5f87c2ab06bf29213d6040dce07b3bb9e123765503ea6877e8e8cdc05f2cd378f5f79e0ceee54010b970ac758cc6a274fd851d636868161bc4dd9ccd9990aca54962e5f5c714c575c9c9cd201b1140553699ca515f20c44589b6e0c0c0a7932ec78f148ebe75983e8a93773483bc947a140de30c57a0d026bae605edd7574b6ca350842189f5cf6aaa8567d8b2716b08fddd0c8e6a8e7aad065785fd8995da39f1358e5dff7e6562445de09810744da0e3138a1b6eaacf8cba5fc46635839c550f24f0b64fe5ec3089e3e841fd67451ec83ebceb0ee842bed0d869739c14e826606c67d29e86e1c9fdfe22e416328d5d9299d73910ccc1a1956b81319cca06b3b3c280bac626ef9225b1bc0afe723ba61f282b022d6ad16b234bfd07f9988829b536e154e972d9ef7487bc3851da682ca6b50b6015fab0d3c87bda843fbb3d2894b6a7caf26ce42a796a46d8372fd457b4af4d0728b012ee03dbb0ace48cabcd65cdabafb92fc1ce07ea2d1d9a2b08bf1eab5ccd2210f4472a2d9f36208eae585da03d7de3de4ea71f081513708486071d8263381bc341cda16121974d8ed5f9340dc117f494d4faaea5d7a4ac850376f9eb190f0e70d72783f967edc2a719c04e9f7f194467ccc05ea435b9b8edf1994ab5b28500"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"50c872a40097074047bb21b1bc463b894ba43a55354637250352e1e21315f364","proof":"c0379d13bc81c6f1a06fa5e137dfc4df994640cda52d8e5271023e969fdbbc1924a7c6852dc481b85f62130d1ef1885eab1588ce7da308b98234932b51b32836048fe6cbf09cbce0deda0caf7491bdc20deb19fbb1904144d3b8fa2de47629153ce4aea66d19669b66b37495c0587068c1628ecf41661c0b96deaeab9b2af81551af6048ae361267ad8b2fcf3bcb90723ea355e74a10af7ad8cf660e88b65a02a98b0cbde73c9f0da136ed680a02c30618bc39b36f18be3a9dcb090c667a1501a0ba8809d32ea71689c1d70188d991f0e38a1e3c532e5acb30e69a46238feb01d4314b7a8a47e88308caf9a19ad4ca6ba3b7e02f94b5c24005ec346dddb3a430c2a68fdb6ece263d6a57a16fcc8f93e60e1b332d894ca72ed1f1875114dd2670c8d93f98b77e62b1caefe5dd4d98741f1f8f87f4e9b90790c6daf766863061471c152530697139c14eed43efb7326511d6e3ec6559192325818ac9eab44ea264300c31a892e10361a030438ffe7a0be814cf8de9a5825e78599d4d8da9414b6cf6112d8b27c2896aee5a43fd94262f69aab85be92f1d80935a308c3d6ce24a71fe31afb9811d8307251721b9b8bd77bb8c7b9314fbc4ff22b258fd235b81505be485bd2caf4dac745b3e4ddd05388676283aa86b0084a352985281135fb61a3f9e4e3e3a5ba7a6fd3be14e6e439acf1475d3674333839118d31cec5c8ada1a523c712d208dd3d5398268ce991bfd8a6cd52b4d7894916378ed13b3564b6ed91574eb6ea170a5b17c5c367dc4e3b1c6c3c4b87fc890ad3b0bc1f940994c5e156b9ee26385a4fd476a1492b8feb7f3250a3d32ad0bbf91906b55c24cc099a7045a182a2721db266607fa691a27309a255924e56d8c7535bf02d4e59154d9db8c04aa79758683dd01ed3dfc221ab4d2d9302ea4fce20db57c76a7e04cf08a82c006"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a44d1409997896cbfca4e5161e5a7bacf5abe6391bfcce3f1db41b88f6e77d23","proof":"5a8ca881c4df36384a8b1c13a15f93f277bab0d9e4344808000b36b09da110020a195baa72621ba0c8836e8096f15723ac32dd34c666b1292c488314fa7eb2494087e3c80c646295425ff6e8f3ea61627b993cdc562aef4baac9c70c3cc4dc657a31a5f0e6d710e9df23aaa71a5a4d7a4b8e18819d2a7f1a3473f291a3ad4c72f85fd896367aeec483ef5720216148989061cac1c8ab74ded0e84f31299d1a06b231a2ae01dae1c8fd6695f9327b4f4a790ae4f9d6627c5a14df1d3181b2e001093772d46307210cdc1dc1664b000938c16bdc791a765cfe65a78b3dc5fb410a624fdc2fb64d934775f341c14889444debe7fcb01d45dd4703db6bda196b6e090499ee62fe3d2f6e7fab1badc5698d54b1e5b817c041817181ccca4f3e805c0b0e2a450d8b4f5e1eaa2a4e1592b13a4c1579f21ef8bf334f96cee104cc3aa639f4871bbf28cfcbb91b52b73ffa6936a594c11f63c51f6ebf7a1de797f2563d6a82c43017476451fe556ff098b97dac314fdfcec07dd77fd65b5518af3f49b710d2a8639b7e62146dd20009390c35fc1b2baf25146d6bdb52a33de2c14f1f145d727c6a29c966a6bbd744941dd1170a2d0e7b60e7c613e0ae3f8b6319eb7aa5775ae1612173786cc88b75b760851a0e742d2fb3fb37a787dcbdc01e1d5497143b9e17b866eb58968b9531dff1edbf61e97f50985e2e6cb5764753918b56123a2d70a2ebd2858fc4d833149e72533f9945ed5c592aba833bb14546ee63955dfd7e863e9abfade86caa9dc08e4c1dac9d6554c25f99869e3308d670ecb1515f3816d89b373bc9d5b290a89920beec656feb486aa51f56e3f60a791337b8585f5e0b8466398e4aeb5e83d4acd69263892c2ee3f1fb468c636312fa0b2e7b06445701b3e741bfa3009685b97407872fd4e1e88cf521297a46217d52325ace61fdc70a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aa82ace0d2d1667d946acfd0f3270b82cb739c380374c7718faeb8ca6dea5d3b","proof":"081ea3e1846341d72fb86b80a5338b078c0826eae703d46a6a8c9d24d31ab1063a2444672a3190564feb4389443faaf757c8708f240b21f1d30d8cb156daf637e6d94ce1fc336cc6384d50aabc5bd1e662303a856441ff9df137b508334c9a1df454b07c7a95b994d026cfbdd979e2170d88dd02aace14fdf3bc918bd295a80851eacc8f35bc7c5b81f092ec529635eeed29fdd3e3d2b4fc0be0b5210a10050753bbf000cb7f2caab2aa9ab6b55afcef41e6796cf0f7715a553559d8152666032a0fe1148f38908fcb8c6247107a19d431cefbd82d96deea08c8fc4de71f6b09be4f9c79a2b3eb15417de9f1a6c1e9cb419149c576cf9e252b53fb2284d99e77dc90ab8089eb27f37069b5431b86d5debc3107bfc26e89b162eed2a2a5214f273e513adc2bddf96bf976b6fe4d36737843ccfde24cf4dcc9c9723df383d98f397200b644278b6b5512c94206d687198891084a64022eadb8b5774bc4c02b2c51fc4f7cbb10e954b48ad1e52688e1c1f3bb9e638aa7f5a5d9bdad81b9ac983a675ab6866b33bd2226960b23cc7364a85b8c6af936344bd0dc7230d282f0ee13231c533503f02825c9898654108a9d1aa2825d270d9ada75497ddc9bd8e9a224348831deb3b90f18e1b378c6034d31c9f68e89c4ceb09bdd46ae09095a8557d44daaf50c5e57a5120a593f325bd66bab19faf1a8471ef1485f18b86ee6fd9dcf104a02c20b94e6c45e4747bbf96b856fce03bf7d7cc196b7a0f44bd788fcf3222c60339f8f6eb2857a3d0bd022545ffd8b923f8b44d160a0f9ffc9942d51b8a724e689717738ce7942ecc8810068042a5800d63e605c9d5a0b1aa797d26055b607f719fafd3c5a8280d3b1fb685eb1932a23bc243375291701995f3f675bfeac08fbb1073876d065e8bd2aae86207822f59c14e7cf36d39e53871c9225b5adff04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6850192a131232beba94c24d401771a2ba69ff28a0e0f96443f81190de435133","proof":"96684d4b91a9dfddc745a395ca00b5bf15bd5ab73abbf43adf0b47ca2658131c26099a7d320bc06cb9a827e9322a6cb1bcab22a00186beceea4344f9d3eab94bd689ada0465369345f225786669dbf42e867dac6302e718595eac81f550ea410e0f7c01851c5328e97a5ca0bc654c6c9b0bb3659c9b34fd32f2cdd52518435200b1cdd5f0ba4604014f15f70d115ac8430b5bc598bd9eef05ffaedd70ecac700f98cef031faa5baf9932babcdaa269f37e8c9dfa93d10d91c7674622bd11dd0dbedf2f116fd13eec7356d9e23cf6ea107bc4d6a1513fe12b20b9c6bf6fa41f0db0987b34720681b87e66fdcea4b31a8ceef2214db33d969a9ca8d93d48fe1f0e0294d72214cb9f46d4d266007acc83a419752af8c273fecf1ebfa46a9075b3548663ad324c5f3e071de8244148593994673bbc4d00f0a3a9c82d69235a512f76482b5304927b6c632e5dcb91e4c5db6684537a773413b9016df3b5f0f64aa0287857c7ad7a06728a82995de044ade366dd43c0d6ca5e4aa399c7c0303ed77f2ab4d3e7ee71289bd2aff96655441647ed7505814082b2046b8a437932b19f090f9e8e67feebdea41fb588202e9b715459334b8f9ee82d4709a38fa8abb597f37154df552b06892df464d4311e8d8fa2e48578dcee1c70a8a654cdae3a87d9ca05186ca94f86479e3400a9110e195a163323cb6874f58708f53d39c6a818177d79544f1313c5adeb9494532e2987c678a3b96ca46e15b1b21acf4e55498fde79441480e833fdb74441afecc74cb378541829f8dc8017a8d0de23853c07cd5cb27eb495fa4a88071d08427ac6bfb5da1f29d9cbfec3649ac1715ca2879b9e01d42f6b1271f47304a7fa82baf42afd030fcfa91ebdf60f4dfeffc35b05c30c28a40b318ba0ccac24f9c6750574068dc0732effb6d6f0677d15cc2b6b2509fed5fd05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"424154931cba7a3b6c0faafbb07f00a7cbb6abc393579ca88c4915881ae0543f","proof":"2c967245f5de255eb5233f993b8cc09064a7a9291a432772b4c19e567b540260e23d5194cd22c81d812e99f9501e6e90156440bc97ebfa485497ab262e6ec7685e13a60cc66ce0cdf6993a7cd9eb36f1686738b17222e7d937cd8f48deebc04ca237b407ba4dc65ed99ce409660d442404141e19d14c0b8e0ec02e4b487ad44f28b2a783c6869804a86dbceddd7497fdb5bac8375148220e77e57f839cd9930acb5e91eaad822c09367a1a1a0736c9912a85b0b9fb89a902787194a31a03810507ace02984cf8ffc088f62965432b5d158a089f459ccd5aa36be51a5eb9b0f02763aa168c84bd63200beee1a077298ec6af4bd6101574ca4f2673080468bd96ebca62b6aef03972753074e8f8fc7c9659c39342da1004041e858e8d8c3b6b104aadf7e25c2e34bff04b79abb69c37eeaa04d2adad1bab47170ca3825e0ed30016e9c99707edc5642600eb846e52ed21e020520dd10746ad4cb43554180a6df6faeb741e8a19286569cbcca089dc1b35b59e15acbbc2c486d4d8af40ea234e430d45d4ab19df646bc489e9896c859a2e00fb34ea34e6475b2e452df706dcb50105e9d030c6a927255235acddd7f23a0c81eb8a92d52d0789f2669ee3e6ddd1c03fcbb9d0464f4ac0cc06a7c8864341711162fd4c418310b625be84eb539784e7f0c65e23a6179c86fbf4cf12ea2060e9f3fe385448beb64697957233642a4274b36d9bdab3e272068ec2811d0c5ce632048c304b1cb4550ac86789ac0bb4fc3150a54958eea0239f7bd419dce3c6b74658bb5c6da0068ed36e13ee6c63de7081d60d9898dcdd8074722d592eea007f5cc5a99c89ace5daca7757bb8dba0022121b2b4206a66a685d5d491bdf535213a9bd9cf4ed18ecb83b0a4fecd94ecde410b73557472e72dd5bd193c2cb68cd70f0c968ccf0791ff139922aa6876e49b510c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"263bfb7b282572e25c441895b6aa31d0d521c3cd5f5099d562efceb6758ced53","proof":"66b6cc30adb502a3f4d92130f013bef1b662ac470b65d3ecce371b716539d050420ce92f66e91438597a9790c52bc77d9229ba9cea89d94f034f99d59a787c7a645ae243c3f57a3c208e47247b22fce7a346c3f437aedaf44f37dbb647d2f236e8deab82fbb5c70884d82ca5d067ee3e89ab243b095a046d6459d5106964222614f9e38a598c5e658c8c53e4f6a8210931c2fa6aebd348b52de304c9eff3790d99e8eaf0a52cd461191c269bf5821cfe4629a9af59c021b25e51528d5f72dd09115c9cae287b5d7db104c984b88873cb2177d1fd902c33f83a42e14fa64a8308e0d0c61be2286d5c7113da6ef1730b2e1806043700f1563657fdd0bad2e19a1f2456e4c28db539babe5b3afd50231a7ecca7ba4c50b2fc8bf9f4f695c0e75261b46e14385c5c8fda336c039b795c55cf453d3e8090883f6dad9d05ac59d2f87da2de6eb6fd518f945e2f4d93c60d0d9914fec092c070c5520bb0acf70f16e762a83e0c171bb575e9f8c9500f6ab09886e37dafbd7eeb54c85329489e888d971dde1eed0c2dea021634a7c8e6b6499e0912708eb4ffd5cb7e8385e1a859f93867dcea4ed2f51cfd9e2e8126054e901455e9cc79c9d4ed37cbeb6713e02fdcd475b248b06889750c3201be8162228b7ecc4c4289ad44af79593aa87355a0014d2a947980f8a42ade958a796d58bcaaf90ced9ecefecee1335748b511ade1db4f5a2237bd2af1c6b1abc058ba9c20c93ce62fe3dc93298813af9ee0f4f59ffec87770f4b5338c9791e3348a3d5a043454dfb64d19945f576214e9fb573e87d7210d3c9b3a3ddbc50a9d1302f3d873d6dec7b9faeb80791e2a5db094ba68a2402c1b80971d7371ff69ade89c9d59ce91386f66324757e02b1a7a1462a9e26a0e2e08b735de2dbddc3717fdc7030023810188174253eb78a12b9fc01a57ef95facf0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fc47184a23aba214c25620739114f341528549bc0da58f125b2b0ecdac11337e","proof":"aea1798e666276e835cbaf17039bdfd0b885ea03d4bce3275ad10ab84000393ce43e69dc2b825f73721d987af92ecaec692108e7e3bbdd4b00e6f57e386a2a470cc98504a0bdd2c696d628ecbc54a668e55e4c76ab551e319fe220ef1cda4d272a1698f8251bc1cd33a614dfee7669ff3e8780dafed18b6b81a815a9ed3cff38242146a145b76a67bf61b931bbe955508e1ea28988bd6c5a1f8ef8920c62ad09d749caf7cab973573d532bad3ed7e83acb7e8a49ca2bd59eae0f9ea85aab45081bca4cc671d6d735987734c47034cb0d7065ee0cadc66bd93f05d817d2ec6806aab69f5938cef76429cf9017467e1bc1750b56ab5491f29bfecc2cc8464cd1681c75b4231e66f5c8dacc2f72f03d1f15b2930afa16ef1a90b990c1f06f4633094098763fcb470552b40872448f684d4ed3bd4f3e90d330c88b8f05ef7c367d3366dbcdb254220fe25036e4fa13bb7a1f5a5be88912ceb7eedf3f5e21c840c47a220c4415d09a74298022ad61973020ff32514aa39bef84d040a3f8cbe7cd0317e0b0e8a5c5c47d1ff19c7691961d8fc324d7127acb1065a1a8eeb1bc8ce6757cfc3b5a6a0fd43e4bc41e49c0bfae4e6ae78e178d14cc1ec3e63b3643aec0723a0898645b7744f036eef4574ec7d588abadcbdccf026f2a4a0164af70e62adb4236f7171a80916bb4aa028d2348f290aa3539f6285a0dc1b63791e44e4d0b1f562c1d9dce29798fd00bb371912db8f258f779974beca99cebf8edc66735f65c46f6a8f44caba0870ae50bc0d2478f8e991a9080029d3b36133c19610c89d6535ae2bc9d24a533f3e30f92269a9cebd370073f3caf2fb5bf776a555cf2e482ca6b4b80c5e3960e07ddee4b223d3586ab78588ef814cec919cd1b46e5dfdd910b0f8731750f18e11cedbe8dc8d0437f25205931672d5ce1708619f08a4ddde7ab0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"428422970773e1df45538797537b69b6e28e64472e645e080ec4a59802488976","proof":"d2866388dec5e6189421184cdd0892258379a97fc234b60e042b381bdf90b14a449b3757881886a0c47f95e8b72ec6f3e3a8997ca186f03b41b53246b0fc830b5435ed3f01e3c0e778c2f72c56205872765234964465389b607fd219a6315b6d0049f1fbc8a25452efb31511655e2090ebf6f0da23244eb4a99c07279644302dbf6fd2e577c7b6ae6a9c257e05fa49d24deeb27dce772f8f0463556e8f91be010fb9763bb096f40c80b9ae1e1f4fa85c6c7ab87275aded48354a87f876e00d0d7b01b24c9f981d70ccbbb15e134589b8c3d8f6350f59d1980379f8ea0c95ea03f645e6fd3430d232e8cee31c1f0ab0d235c907c30691066ee20e1181f736ba5ed8a80517bc151b4d6947a51377b669772c86866a4469f9f2df75bc8d55296950c40b6e50e089da7d9bd0222c44799de387b7332a204dbe2899da8654b0f2b673acc8f61e48b2a644edf8a6b651c83a7620edc943729d132bfa1dedb1b6d35b178a174e9f1f905e39452f5b0492eab5ad5d5cf58131304cafe1a623bb939b4c519e84136302f0eed5aa2bf99da0b5c9b69ebbffc2e8dc4ba95928310f55ef295f3afd95d6256908b93c12665bf7ca82c69405585bce39057abd0bcdd0c862f83dd08d71942a199df955c42cc4267b96593e1d9f52183b8c4710e987487397784c6eaecda2be5b52ddc67b7dcc2a135b0a2d347558f3b1c37a4978c0624499304ce48fa709a6a42d39fd6900bf66254b0ebf0749681fe6610af2f9d0bd99b41f7f4864616f01cd56addb0d43d5dae9ff0cd0821f14bfdc1af6edbeaec251212c3c5c00d4fa2eb19e049215fca46eba4ea4393d77550e7ded7692cc300675c73d1697c911549a54e418f96bfca3cc55c418f49c63e5d2e0fe67ad01d42628f9ee00819eff57d047b70f10f631596f9228b28ba10af6779723f30c7921019cf66704"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3c49cfbb7715b43fc68a4c43113eda103a81aae3c5bdda514f3e15b29f7bf018","proof":"fe64a1d536ac3d33e0f3c63510ca574141236d4a19acc0dbd520adff17066a4df4a9fe458f3f0bd5aa6dc56659bb4c07b2809a872e62eff80d70e69c86fba22c1841103d02f586943b0a9d4dbf1b1fdd7e49e7f8516a109865103f85304f73001cb37e11233f1d80da54e5515efeb13aab8b57670e87bed35d6f6e263141cf4af1c7d234c9b4c254e43320cb3e494713a0024d98918f3790c3d56262602fb004c4426b3624f2498a728ecca20f71e7b332158cc80833c8e9056addeb68600901402b4e370eaca454302cc0572a2c45e23f639cf5d38a6cc11f221832f6c18303dc72ea6e5a59625d519f55337ce8f9c05f741e75e146b3605af9283df9b11b60d46d3f17ba5c9ab4585a92b1951995a6aa55f9bb746d0d1e6c21a40ee44fd1786ae9932cb4e6b75de024a50b95b5aea18f01efb438b18677d3187402f77de039f49eb5fbc3f6ee884a572eaeb795c529d6b32a946807ec665e94b5731285e914dce68c240b2e1d7b4d3ce1ccb2d413d76af47703f016bdb072bc2377d115e232a26870d6d18121377a794abfd446e1531dd7237e0458bff947fc0b996690e70ee062b509cd413028b620ab8975d2e2a4582e3b39d50c375af726febc90356c28c2815563c0231085d961744d16c44f6093c729f1d7b71bb0f1bd1e02542f863610c5e72f1896ac4330b9df3fad6cc16dba523290edc047125ecac7531a388d1474cdc5e0fbe7965c74ed5e5df5e1a920d6526aeec87805bea1920f4b538c8958aac10795f373f921da54b1f39a0c3a3b02ca9a688c0692150f2b6dbc4851a94bc836811d81a516084d95b22dfcf8ee2061693a3bdfe013be43e62201edf7ae1f5106d8160b5ebc3fa2d2d2cac1246ed0d7a80694f237cda61dc9840fb7bf5102eae0eb545d459dadc657456c7f73a5c770c61349e7ad39e8057cc1942c728b0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"065cd2d0a9c49e349e02f6d749dea1851470262501d5e4c52931570270238b03","proof":"84e363e166f3794d521f714789c5a5f14b7c2832302953377f15d9e391da597d08179b32fd6b25facd348e9ee458a6625142827101020dfc70b5502a9360d5016248c07a566f5ff9e65b78b780307c1aa3aead93e0a5d48ae61c9f1158d81f7692df50735c89f6b9c7d8f11191cf80adb1d7b1ba60d463d552ea6fb5704add3db994e7e2301d073ebad24428f0b723099a2e2899d2cfd6c14044a1380d7cd70d05e274e126fdbfd5717a4808a579f7d69197b57f2cab6d1c2a8cc123ea6a370503e859795e4455904e8324f4a7134d410670bc2945a8ee744959127e075aed0992f83de67bbcea2b6f1c323bc9c6a251ebe818184b7f4af8b49784fc8114e84e0427f905701fd472317b28e3ad67354e287fc5b0011b26bdae3df672216e7c2c80aaee182098a4535478d2a5ce0d53867526532d8fc9aabc96262a909218051aca42c67006d97a20d249492a3be3ddcb0134a29e13e8c1b1f428981dd9105c2c38135c3c1fff516d2ed909285954d6c7a620043ce15963f37e48d3329a0a0d04100313b81ec002c8be2f8d9bedd54e8269bd919b51283e98523cbbc001534920f42ed4b38486108b401af66f60b6e0b3984ba72aff50605b1c27202ef3b4d8257adb291e4085d271705dea5d5886f339ac29dde7ff8af70c0c5eb0cd26efe8584cead3791f4b26660ae718a117d85f50d4c95498026c2c379850b74f24ca852cb4a4d86ecb70d69b471dfc5b7f0614cc73c252805c0c52703bc64c7b932ad3123c873d856da950a75a4353f55e876fa86b53c27ad5916f71c7d97a7ae58f8e35bebb9bae85d781695bc166a33e687e9e81ddbe7354455641f66b28e36ccb632970c6133deda73d17fc49df35e7714b06f25d99a02fe52cb71f23e5081be91809bd8b4b121f3b3dce2ce82198f9c53742ea33b4dbbc6e33a45fd7d7ceaecb8502"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5239863b8d514e65ea380afd04862f29b30e74c1506d0a47fb793e6d61a7884e","proof":"66451fb2ffe28e2c336a7e39f50ef6db55c5760e8eedfd978aef5f95b90b803350f524049028e1134d6b0fe27d022787d7a9c16fe7daa0dccee935fd8951ca1d62f97f82bb9ebc9ae614c4b410bc126860ffa9bca957f3eeff47a76ad21fc3064e2a8f955e9d3acbbaa9f74b297268c8665e54f693349858c6b12d7543af5a19acd167a8db99b2da1f8b04f125befca3737c42a3797f86d094761cc3e6f127034347d6f60fd876d0908539f218654b7baf854b93e411be076ce850a74740740ff1f29500ef8e12beed0bf855d32975ff276da72bb478b112091707927e4fd201f0649019fb9472bd1985eff2266b5dc1107bc74532fb3a787e187e4bea069342d0e9ff5585fee0967cf4972ca71872fd597adb6cd523fbbf5c36b42e89154259909b4d213c7956c0c9146be81bb246577cd15327cf82817cccb494dcbec1d928780d0de65fc40cf9c56991168bf28f66a111cd35be3159982ab9322a3e15df3ad43a7e5320d6eb4bb1a246db90aa502cce23d69d38053f489f09bc775f00d50e76b2c258cf3905ce39e434b16d18d8cc1dd588af20af61a4dfe67a71e4b6ae10c0ed0dc5ca9e112a9c55b72551567dfb453c9e0f54fbec7363d2a72a41a4dd5ea0c7871eea6d524e70d666e1264d3917e5cd0633ffeacc6a86eaf0a6a96a40044e6afdbdd311210aaa35f6513a0225875a36821e5e98cb9230b229944291753728df1d013ab77b1d95263b3eae42972f98776aa6b2a7ad521f7b41734c194c3dd089c23c58ad869ba5a701773f3886aec1a32c1dbca8c6f97994b4528508923a10c38fbf84705f6ef6a4396708bab3b5daa5102d8f5c96b6175a784ff22c427009ab5b3f70a72aebf3914f1248bf02248b2ff95d3a7c54923ef1b9af69f6a800497c35a2e0b195c9c2d35768c7936cb786006c1edaac60d1ddc690b299534607"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ee1eceb84fa858ad6c71895cc59fb379826fa0f89ea67878ed055cdcd666e90a","proof":"50973ff2ed79d56f9a61ed1426ff6d1d9981fd2cf82b6ef8c792930338bb182fb092580bb01bc74abcd07054eaacff117422545e7520d3cd32bae357cedb364a9e4c78d572aabcfbc39de9dfc088a1c778f10721d9d734fa0be930b8f096a17f72b88c883470ed2850c69ef4415c62ac4612c849807e4842dca16bcea6df7b6531f0bf24dcc1fddcd8314b18255b7abb562d84c6dee86b683f112536c8554d005db3e7a509ccd395e2a4f0a45dfcbd380921728c830c810d67bb65e2c081560d6e133fb0310bbf0665e234321fed7affbc5959e575ed85e52e60631aaf64fb099a331f7f9d211115d440c6c81ca3cc5735c0812da5ff3e85760c9b02432fce1abce61127892d335223328e8636d693a9347df3627936fd21f650e5a9cf43eb44b4c15b18f82829adc292a5b2d17f74c7261e5ce577429ac283ecc6a418f72666d0c4c0e3ace0d397b37c2454cc4b0fab900a011f9c75883d5454edf11c165764843d5175021f353ee4c9ff3e4e866f66e2b2434ba6fc92a30d1ceacb54fd0c65409ecdfc12c02f4b39c8269bc2bafc31e743f2725148b785fba269a947817945101ac745826ff0a581764640ccb1cc2eb6d675ac4813b70447b6697a89a6156d8e037e88f69b5654d6a44bd802f66a66f1e8f8347504c235a84763182a74e74e682f65caf33490ab6521f7949c67fe50910a588c05cdf8e53da51a847e09050db6a3d35fea09c7637b9d73b52e14532d90d753f28f64afda5210962724c171014e5cb85c2066a912cd913742f905acfa9de3cff114d4e04d5036264033bc793e94373a887fa514035f468e1e714e3ab7d04bed4a1ec70c00f951b1c9cd9acc54472ae8c6e488a34024def8f9c1894ff1a271a35f490355162792143aa8b982041358ecb514ab3b3868b9613a720ea297ad8602d0021dec0cea9bb1b6910caa02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dae12f170eaf1a00e4b4417f2aff3d5050f0cd48b2edb7e4f0929847868ba415","proof":"323d3de15dab7b0e260763e6965ee1a6700ff106fc5af878c5dbc26aa950dd74dedb2a22f4b9012a1ef3c4fb15261ea97d76c219cd3e39efa0c61eda6c38cf7866bcfa0971fc818661448670df33c7dace59516b3387b763a525a5112240fc118648d0b45d7221dbf8c5a2f1534b345f3e4d11e536572bd843cce64bc582c23c20a4bcb5809d704d23b13d860fb3e605e1157ccc1633c64830d6dd80ec3b9f0ffa97096a204bfa42bdd83d54a9a9d2209bdec4e3195b7e3410b87c43c29dca00315300f1aec455d8107565956c090a5fcf9d708865ec6945308ce875ca31530d5012788f10d5c98c8ad438d4d9f1194849ea6e5c466f3d2f3f29175543bf6d2fd6aa913c71872cb1ef9ab374f058611000ac0dfdeef39fa8b62e6426423b4837647083e7ea0c4ed0a5205972b38bf5d13b09756106ec94cddc71624185470878e6cd4063a6a624452790fd84c76519e666df6740510e8d058336e0aa3948c664463165618322f65647d56fa1a6f15b22db1ad5f3add78a97c597fadd6821fb5dcc267a43130603fe7e10443289dcffbecf39cab4ad5385f16be1ac696531d8653034cd2f34a214ac350ecca581dc931e7f9571e21ee78300989df26bea08063ae4ff5c74d1f65d322fcfb0372e225772fc61a80cd265a855aa908dc3223dca101e9e4cb1707ede4a8385c9ebb789f24487100f9ea5c6043996c6d47aa5e26225de98d531dd5a48a814d1611c436624e815c451a41c97d37586c71a77a8f0b21a42d24988de024ea30551566528436ddd6f0bdb11d74794e6fa1ad8bf50334824d68e77d8eb1190546b3bc85d69120a81a8f38e9b7b67cded2f851383a7fc5d66270331d72a434f7fd2bb61b96409113cc91651ebfc3cd6afd46b981d51b62807d67a19ec4ae8b74f2f4a82fde86a881c7c3189ffaf50134c28e7a185f5677605"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1c35f0f324359b53d7fcbb4052cee4bf87c98fd69d5acc9d7fe9d965c00f7d03","proof":"dadde9f7d1ae595fdd98dfe1cf4546527809ac74da477e146032784c560de13802f5f6f50bd48031bcdc91819ecb3f4085b0028200e82c8fc55bf845d11d0868a29924d9756d516971e47ebbdbea4a50e26b3f9748f91a0d7c840bd48036e35d4275d0aeb59bc0f969e17df6b1625700f422b0d6ebab9acc9dd737a392c51c60abe1d6129215bfd014960f1ecb30614be5d93da2076067c2b9b10a8a08db950447a9a7bd96ffdd55516c62cf3d2a24a05cba83e804452ac09a1e12ad36f79a0cc18b1d8d46b740c9acc71ae3cf7f7fa2caec7b54d5c44518653de110d8bd720de8d6d562f90337b752dcef7bc117cbdc0cdc43385599162677185bedb617977ee67fa9e994b8a5634176cbde857e9e2c7821c420d6d99be5bdeef6f924dceb4616d691ae03027e2f5a8497a7cf9bdfd8317d68697ffb8f11ed9da8d01c16553af23f07e9fe6909481f5dc2cca95442c8ebfb275f516f49c4ab08240dc3b6fe0d82193f8a0e99f9b675177342490ce8dc1ab4026cb65b9c94715b9692729fda32d2544750ebf21638d1f186d1b838f9c395c207039898b9ae3dd461719c3f7530767d7a83edf6a77ad565c12fadf91fdc66fcaa32449f4b3b8ad4b4ad6e131918d6f5a5ec09e6ae54201659f9dfad6e0cc38e9bd6d59ebc8b2d37f674c63fe7171abcccdfc1b94810d6238ab1b32e657d2dd49149e4c79c8df1f2f87659db512b6a2bec6ce534e7960cbcb7615424483ff5c498cbd15dcfdb53195e473b33de3f404eb570f6ae4600ecadef18b31a7303c988e6c3c64b8421ed1e4e5a601934580e1f9cd25daedab453697c6179d4ef7cb0ad333b70440fe1419e8a30fae7231cca7e3c143f03613c817410dfb0819d0d77a0cfa9bcc1bd1af0880b3e5312bb04a31a4c8f5a4c1c60a6235dafb5d082580f76c3de9cb495fd249374d00108eb08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c2886f9165ac41b6fe35b72e98b58c7bfb6914dd84a2d6f39614db506ea4c371","proof":"b81f440fd93e3ad178033d7d1167ff42113ee46bd6ace2c8009ea03656d6fd7502da5159c1e64a331b86943b1a874f19aaaaf1c5cf1a2f0e0187be31745dc8406cd2fa7c98c2f35a33a7675b139dba15af65055636297f41c5232b47dfa168656a5dcb794f03f4dc784981aeffef7f4a15291e2884297257c68589f93abd8a64378f86bb7c3d9d4c34353a8539c534b32024450b281bd83a1e8e24a8838c8b03fb76be1290f1a3aa37b116c21a15fb6cc82242e5c30a0df2e9789f406a2739042812be7ff62f420fc1b53140acadb9ce2bd7a040cf4c908b59b2cf453d85d201eab1923e80eeb244e5e4948249a42f29f3dc0f98121bba76cc8048fbc95f763a6cc63e7b0271027deb4b6a8cdcf18fef4014ee6cd4c195d6185c28a3d3f2a5724657766aec2a7519c30b421c8051a0eb9eb353313c99331f46699f2b59a8fb7f52ab032496036b3a72cfe5ebbaa5ba1e2a4ed71a1de09c0e09aa11d08dfa935f24ddd80a869c20d61490eec795f54a387b14a3718e25bda73c51db3512e6cf77a2295bfc85cb3dd0132355309bfc279d7c3bb6f4da54cf6003a4088e44b285468649b1195f56c0f830cc04cf78de8ae2252b0018a1eeeda39de9dce7d4683b2fbc1915b62f547679d1de9f19065f1c0ed89c19f538c04698367cac015aa156462a2fb515e8de626c2e311f110ed9ba3a2819475c2424e93db6c2e6c8508d2a0a6c725535cdab97e9608894c412453c4f5bd191d61f6312bb5d5459e717b67a66eaf13089f83cf2f5cbdd000d86b9464d99a0e6c4fcee7aa944e6a3db9c9ade2200783cedadbd3e1c1fe14e4250a231b80fe93ad440fd784648a88c762c0f546f60dcc5deba67a3d06eba5ea421c510c9dc2f20467bd388b61313196780237c0b777256879a5ecdb3a53700bf4e59ed29cf2b5ba9f66bb5eb19c22aeab34a7504"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"baff583fed71a2c9801c3334b9c74f476d46755757ed64aa44beec3c1dd5f306","proof":"c8645d202e3baa43d57f73158f5fe67d9149a2df86d635ecdd56115c0ed313766c90d968dfb4f3be68e958fd471678f143317ee507f7ff64bc61bba25c72501c98a4861f0abf76a30e65c216db3714b3c0a6ab723242447f26ad1739f75052050c9b8bdca01a778bfa5fc2c693bc4a55f7604df9c9bd90a3dcb2bfabad0a2834c60d0158d7029eee458f9639b525e5145afe790247ac8310949d09b61ebeb60d100779c2fee68389239fd904273b5ae17e0d5c83c9c774c133707ee94834430f525cc753924a038594ad4b9e8d57da0a8f459a5cc452f88bcfd2ba399ad7d801003ca6f87700fbbbf2283a3523499e3b8353ed1a403da2fd8bf41491d838b35f86b9585b9c80c36f6a90cd5023e560b98e4d8300fb1464ddaf57e7fe17df5e3cce60dbcb1d73bbdacf72c80587ec96758ad609d3d24a24d25054595372bc423faa8281ef47f2500efccbe226df931bcf0008d4738d00ce7ac621dfdec626572fde448bdc2ba43518ffdb757069e87d6886f12b9ceed065d3479fbd081da76d5f2eb4e3f7dd261d67228f1b300b1f35046547820272a5b1f847bd3a15edfa921f0053de11a4a86b9b5ffcb5d151def104b775eeaad1db8bac09bf967d678510654c140b454b95a1110244af895be96cc6aaea801015fff8d3e0ae2e7b9788bc43609f3ae7d0ecb15d1a4272ab78a84391e5b89b5c38c4647d276510ba7000655b22229eabff6269d4581d04c3eed2bfc4a360dc516098fce58822fa2125e23c3d02d520822acf7a8486de6d5144691e7aeec22b64caa1e75231ce93eb67594a4a60e1abd7986726aabcda294f88f89b8451545eb34b0b38796bb9b7734633f31067504df19b5983fb6df4c477fd3239301477bc9f21d15b368a72e3a2788b200407bc6bd215932adf9178c67f028de4388dd22c95988f1f41a4145ea47d6b6d07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9457ed7596b5a9ec560e6129d7cde4f06c85f6d7931a3f3b356c284584826064","proof":"6a036e0a99e4a7a9f249a222b9bfd82d9c20a842fd2a38fcc60e1c569b69c64db802ace9bff988d3e39310b00b1182dc3eb6081cd4af9627bcd9cb30eb2f8079a41a38cc267435bbca5dcf8fca4be47cda420facfa3d94a6a098aab021819f669cbe34d090c7c35a5601d3313d8a44c28e9e111d0f894fe48f7bb7328c945e1e054a55b557d26be4c4bc514b427f66bb1e3ad6ec295a4f973307f6d04153b0023ef4101b3e5a2188750e331d96a1030ce0160130b15e1b04df02f36443d59b05e37314d286f41c9166bafd5e1dd4ae3708e31cd7bc55c96a1f460148aa29770daa0ccfc131418264417f5786afe24623051195a7a753d1f77ef7de0be4014f6c2215252f517394f13a861b41ac7b74f64370bb970d04c326c677e398fb60041c4c1a805074e80bb897c40fca7f40ae4ccf64cd6eb6196fc9ee7075b98274ec5896a8bec9dd65e0dff51d44fbe9d4678f2e0d53f8c1e162b423c9e0b136cf354e869fd1db4f5958d6fad857704dbd22813e861d8e668671c40954a90fe551646806cd99fc99a60f8002dc89c73726e390031778e2283a215b64cd5c0dfe8e1f2c845f3fb792ffeb94658d725cc491e88ae9b1320f73c1dcb34d50eafd6b770559307c22512021e3f0bc156b2921c437b25edbb98cfc187a98663927840eda6f6c845200ff7aebc44d6f9a9a70b3cddb77fe9869673d3efe80f31c10bea9bb4435ae9ba0465c533d5714fac45da47359550ac3c95dd43a060228cd10ce73f2c130848c31d5b9c504256944461ea0e81dffc8d68777ee46196c89293f43a655834c589b69a1fde2e04265e18e777407bbbcb3f298403873f95a407cbc3622d6ca380df0a6265faaa11538be5178d9272689709145cffda68bacd2362bb3df287c0d2e46b3172632bd060c7412ab7121a8beff4168cf36b548cf749f746ed11b6506"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2430fb60df2f49b1eeb6a2cb5f7ed12743c94f1ebc764f873d957c1ffde06013","proof":"78db12b2274ed142e12cc54b3a027a71082ec8c0040dc5eccbe0eb648e7f8951ec50eaa000e29861a62d2cbbde51ba11a3dc1136d4515d647a59e119ccbd863ca494ccd3cb10ce8188a3bce3f914115de4d1071fd5f0143685c0742c6df63a6d6c7ce7d0500e673b4b1bd6fdd4636a222e0260a28e2849b4e4d26caca29c594dc7afc6fe7917def52600f4ea8a2e30fc573e696f42164222d455fd87e528d603125f8a1120320b47e26604feacc6a398a0d70fddadbd29589889936e914613096c7eb3d9a69db8d41b929f83314347c24e3f36225c85ad7e3a955b7a41d4e3072e0f9b27b0f82344c0ac1c3befda48ea089d8c7b6c6690b154ab01787ef3d872401c162f5630aafce6e0b86ead90769c77665f12679c5f3d8ff10b3d54e7984e02dbb61fa67f85c4f765cff3aa9418c058ea2c7ed6924c19c62a469033af7730eae9648601b88225868960aa94f76d7ef81de0cf4a83053c1650df6960e72b401ebb7f8a4b05f9e2cb52952355c1f441f9e9f728c59c2a1cd8a81bce4199d140d2ac038df05cce3af6384e677cd7f0eed1c9d7903f44ed5b13e4e9253450195868530cc580fe92849af731b10995507ec772b110c017d1b4dae231e02abed930e8ea6967fbba373cd875a6f17e8b2262a8a5395c6daff21304ea4d699c02472c6ef71468aef5cf01c537d251d7347c0b1e092f96a579919d39999761e4c3c1665e309e94b44e6a1677ada639bd1a8a71c70805433784d863b50e11446217bb70fee55e2fd1a7045f2975a9d0506a780d942f59197d16a51712abf76bc9667e4696e9cd30ab21a45cf18595bab8a9582484c4961d80d367ee31a5f54ddf7f9604f3b0841ade4c82dd7bbed083cd5fc5b345bcccb3d7e2be480e07ca3053af3208d020c1bee7a1a69894041a134b5264176c1da89c9d1183cb7b1ae71b1a91e909"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5c549abc6897be5b2aba0e9507475abe111b59fff3fd93e6fa623c69fe822931","proof":"e2a8411d15d35a0915a71922c0d2f0efd77b299158050999e32d629eb1035d6a9a270102cd3c63a4c2037d6de37e9bbd4807e97b9f7a4fe5a19b88f01643c602b6fbae734f0cfe952d414643f4ded6b1f097d491eef26c77c2ff8e886eb6026e1c102261b927e005a2c50f2ebd2ca4b4367d227b1fd7b70da1895432a63000756475d668a83a4ec5ae8a8609e2c416ec35cbe84ce9357bdb56c040e355039a02283eaf75324f17b8268935ade6a0c021fbe4815709b98c30b83823387ec0ab08dbfb83bab24f0dfcb888743f7d9c5e81b3b1a85de7417f74dbc607137f6eac0cf69c41c9bb0480e0bfbc8577fdbce95a252c376baa45e44538e758ab0e06c74eaa08c02533d0c751d3a698d77a4cc9ed48a2cae048c5be688272800a90017b3d9c70ef7b533669cd639fa6dcfc66927b44e3d5e04f91fa1e718d083f14ba9a2dc2e5c5fa7ae0d45cb45219fa5a231936c826e844ac8ff0c6c2f3f82ad0a90a57a235207a4f4bab5f9ec3c20feb5a1e85e2d0d061df488c8d4a180ad4d281fb6aa27593ff95fe38005f279aba1383030f22fa8d54c2bcded4ffa1812cb7a3bd5304a855fd830a021768ec020f4aad5f5cedc37239dcfe4669554e2d92ced6c643aea891abd5575234d98db636d29f0c160f8d10c857d8e8c09ae6e505ae16864eb4fa0a56ac99bc07fd7751d1ba3dc852ebed09aa01d947160eb1fe4d277f6954aeac6cbad1c158cce82464a5d0befae556b0c8af5b4c416f13c1ee44b019ba5214ed16501b86d5c566c99686d130cae994d34e2546f81d0c1e8cb154d11b0c1e34bd5845b39838a2a5dcd9b51ccfec7840232f2dddac88fb9a35f098cf156632b784118a0fd9b4e7109f00924387e5c0897680452ede04ddfb6841981b4d3c0eb7aa3828d86bc2c4e6b45390e493278c4c99f7997ad562bed46b81d38f76fb01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b640f44e2f6a24054c41c7c664c02a88f8c42c62a8bd70e23058321ef373a860","proof":"5cb7d8036bd17e63c07de7ab5c17596054b812a0de014659a4e6dfd18c2bc477becbcf07eadb31830ca3206d32c361dd777bf88391126742e5254d6b513b175690f07f27a4ecaaa49871bfb8a2fa5d61d50cb47722b17595fa24b7d4adc16252261bbffb2b30a117156dfb77dfddcb44be8727f5e54abe2a3d1195d4f0ff0550eb657739ac288d509ba02c02ca9296cf359ce58ead3e229c45ffb83d5905dc0716f101b5411457efff8aba66654f22136c25714f3cce4653de00dff5c6c2610e4adef21b5098f26e434d75bd77c82b81e8566d312f77325655a556dabfa4960f78edeba66759fd8d9a1035d2edc512d85b7dc8791950c890af94243519b84d7630e9dd2a793a5d6d91a6f2fd0739a61f1eff5b88587fcbd8ead5979e8542031c62bb1e08fbf92827b598a06eeedb61b175e8c54495d46453a3192324dca8b213e434c953b1d319daa768b35cda024be6f4220a8a7d6e437dc5f38be291756479889b66dab67893cfb4c4eec6ed0ae49a58635d9ed6c4c05dd09ce09af3701b2fbed958fc7f4fda606dd3ba087b229845777ded71bbca608c654dbe4cda714d61ca3d3f1a111c3e9a908e7099870add31ff7554c64a6a385051604b215c265f0d4831cd98e5ead7d4ae0467120cde41cc8ecbae177a87d026a18e717ae5f0f134821aa95e6ff7ca6dfd828b576887970597093a6d48c828647cc27d61a172145b6ad18b3cc8f0f1692c0ec5bdc7728c4527d8b5bf7aff0d7a5ff0b70bb3d98610821285c7ef788bca9f80478ccc2d600b5bbe794a4e58d0eea74e078cdf68d02ad01ef20702d840754aac5711b3ff2fe7329cf42cc63086f8f7387ac7bf4ba7118266ca4b7fd6be28f907ff4d44ddbd5731ad9ed4ee02619f17b82ce687b3150836c0985b772339cf9db620d5224fd3e21bfb55b66dd9ef594152c7c4a020290b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d40d8e9e756f23c3d55386b953e8d4307993e67078589721432bb90398a2bc08","proof":"24849bce78e9af9a1bca59d04ef72a6b83de7e0a266a64758759ad744764277810f95834a85756a86c70e9bcaf3fd506e0c9928f6c4543ac55a5222f2c9b7727e8369a27a9e1897a6af281aeb72a91c7a21aaeb5a2ca8615a257027bf04c38763e004e5b31cc110db9d0abc444bfa74aac10c82ec2d5e6fa56d1c6fd5df9d96b6e1d9255742e824c903aaa4381268f306d14c230c22edec844ab4122365ff30defdcb85df1728d3241332641a16a08c21167bfa3298d4c8e5ffcef8f9459530d1345c10ceb1d18aa40c376bf399fbd97eda1cfe00d4e9a97b15b2b20be97d603346560eb6b4bc81d514e8a334040ecab8c647a756b2379f0ccc75eccc525244c164fb7e8a4ca7cd6d0d80831e7c53a472534bcf8ea58fd67cfc98fd2115c5c4e3e072db9f4c742cbaf2e216c5a992c23f1c10bcd7b6231a71670df00ae18ac560234957b6d3341e07f4d4bd5d41b138f5be9135c30b1114654a47f35d428d561aa8f6f819a0cf5b79deec0b4e4268c301053f93f74251e88aac70b625c976d543ee37ecb900b2aa7465c6f6fd8be9a7f30c7111bf512eac4447d2d2fce13621494d1933f9207715e7c908b83ef350f87fad421e323fd558c3ea1d8729c37754678fb37ede003983f6d4373d4dcf97e5fb0f9bbdf47abb178b508e1cf958d7f5e269cb5d49a8622ccd73c2d3ed574ae2ee590e5b0fa76cebe7e0c16998b85a739c0a1b53ce74dae0c4a21572e42b6fa9af11c3a3d03641fad0b072b1b4237531d8e77cc73b8387a97e3f10c95abfb4a805dc65750bc0d9276da9553775be7ed6f92262e148a5db27808367345e3a08d741461eac412f78661f255b547d50fbd72c9fa7cf869e292374ee33536d657bf1e82b6b7bd15aeb5f929e7921c5e027b0286809e2d2d10af92a55b1350e6401b0bf7417edcc71d2815d1e591505884b807"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7651fe2e563b8c273a4a3c2b6cf6aea4728105bf04c8cdb8b321133613df8430","proof":"e277ca3b36c20be2fd8d885f532b7e4d8abfcff85e1320fd072a471de88e5d2396b9ffebcfba2e49685fdebe5981f73b92661e84d7a73e9e2e238c663ec0226202fd0c88a48e5100deeed723f5c5ed918ad625da2724ccb9e9db5720b0b8a15912941350d0652e1f889278f35a795f38b1a94f32b02785fe1f2d47b284dde152cd977bd12a2b5b90e02ccb0d9b0b6b6a075933c6978470b1656d3c0265a47e06c14aa101c9ff045cd18a87216c69de854a1a7e64b51138dabceef09611d0b2036ee23a3a843fb78a6cfa323ac494a27be836e2c83b6c117d7d109e0955b4ff0a0407b11f9af48ef178d189f4de11ea99a830eabb5b41289b1e927b850565781df66001ef166774cd020233e85c32c1d9e3ecdd7a708bbe107bb136f8409cba5d701f5d072ce070e1911be7e867810815a52f091ab3baf2b91adb1c099159d0698cf992223dfe68ebfd2cb8109c0b7abaa0230a261feb2339ae810323bc576a6224368ad7dc4e041d2e503cbbbe7775bf8f1b0cd2c29c856d2d74a5eb6ebcce498ef38bea771b5a33bb47712b7cf1db70c83eae4ef30825ff2986edfef78c5a5936aa568cf9eccdadd38f25a34ac769d180ad911e482f6c6db1348f9e6bc9de1eb2a39ce46e8c3262dff227c53d74d38ccbf22942018f69f369562d0e24eef04dd8c14be4672ae2296710d65b562101d80c1f8c93e7c4f7aa012ccc1aa67c787f625b17689005bbc22eafad208118d30a952b67040ed9b2577f3283daa77c91295aa24140006564d352ecf08b1c8c65e3afd6766bf3837ce37d0ddd9e0911a356c651b2f0b181e0525cab069d9582678675a6c5a6ab6df68b6d19cebd0ed3c91f0e16fdf7554a91717785e5d9fe561f9a83642ce33f7a4be3c3b948425f00900c06c2818af7fcb5f67ed28d5e39367e91be74d57c96677a99759813969099de0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fa746850722ce6c50276cb33ee323865c060ba07f286ab5d45112908ab235b16","proof":"c2f80381dba88937bee202a5a39a07a457af475ebefbe18afe7ae281efdcba7f584db33c111ba3f012ec0826e6117c9c93ffd289deb88773543f445686bf1b05987f8d462a1e10bd265f2d6035aa874d767a3fd4b117def427acf407b3f8e60f00521371515ebc5605baf60c9095a16d907c95bf696671efcb71364e1789ab4c5501f72d71372c19a666e20229ba7ac43459787c3282eb76ffafdf2a5898500128402cff6056e15fb9411b7f33a827a2f167a52a54dc191fb65e507ac208220f17c5043edeed431ba3f9a98d31006761889a5c30f6167808b06dd94dc12dd50ac28641177939c47ddbf4c2310e2c34f94d8bf497469537b7ad3d4d07beb34776ac4f762cd10364dfb96c5c4c0b628e365d84004920ff10123155acf5b4f5aa6c4411d6be67419ab610226cd806dbe6ac18e3ac026747e978cba7da87e47dc5276cb06257a16e2152a8a3486aae66e1bb30b800a38647a09406c76c94b3fa947af4bc5f6428506d9ff4c61dc92d5409f1db9d85e26d6e1eafac8103617be8e45512bf8601ea01ea63bf37129d97ba22736189395c36c89076cda4465a38ab2f34dcd7414955f3d7be93e9388190b27fb431bef5c9ce81e28f78e27f6c36d9fc4a22a5f36e5a42cec755cd7fa0850b297dcfb93774aa655743b763ce0fec57f62bf8b6e9a6794b6e619f8cf71b7ea38dfe67d2c2b348f4dd65b8c7a99601b1af656c95332a665fba53265c7479346935756e5455e3a74338c5926f1ebf10206e775c0528196e597bea696fb566b14b134ba7b7830fb0d6fc6b75657b0d479fab6044743efdc51dbf2bcfddf91c298b226ca0e33f5feaeba49e86a6896c3449a821df92393a59f5c1b8b0a59e5e88841792f9fc45da0c8ec7b561a661c6b9f0be0c1d9ddb2eb295351bef335cd789b0906fa23da72f3994962100701cf8bc4a6c02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0c7db8699c5178c2a1e442364f69264692f36221fdf8d2e1874c2b69fc002a4b","proof":"90e35a348e51e09d567474f9aafcd34c2d0321a3c0cb179d400475bfe0cacf685678739f89519fab91d30586a03a2949419a734eee9061ee4a5ac84fe9c8ca056464de6e28cbc144ebd0dfda4c35b070318969b4015175a41b8ac0f471a79f4408675aa82b76f4c385d0b284427f6c0d2b74e0cdda6a86a02aca5dd65fb60b20c12f552c396c01d1b6c0228d872ddec4d85f03e72fffd08027de7425e235190c14d47bc207b08fbaae890598a663d07b69f32c0e23213f64ae03432a0c0124036c69c22d83d0f1ca270f3815a2368331ae586b91127bcbc95af3bfd39b5aa80746623f4c0b0b6afbaf8b4078e358119d4a6151d83f00e109025a1db4baa90f7f0ea39ef9423c4455ea6e29559167a8f4ac203fd661d1266040ec9206630fbb2e607bb27181b8226b4c1d0bacdc52f2256f645f1c6140a2a7a26bd4594c12e837fa19644e385795dae646075538a71df11b1e73628da8ad83c68994b2db06421016ffeccf3de75a54be99e84841d167aeea1201b2e4368856f0c40739b0d5eb6734c0dd961a5277305b7576534eb90d2f8e7b608bbb282bc5a80131e0ea3dcb728e8b8a7b67c37399610a21363807a2f1de15194450760691b89da58e2ac59068107d3d2c9ebdf88132858f5dfb60af02f41895d9d75ef6e3923fdeed79cab32c461590d6f49e3f450ae50f81990db81754d17f6f428c5bc4bdf2177d87ea510bf66f43e7bef42cd5626df8b0279e51cb2e59ddca6c9c6453cfc2f7c241aa7c1bbaef0f4554e47685c4847b53058711540a54d593f246a5b2169eb3b6992c5e0a70b858a224d762ecfbd246c107ac3d003e288fd83c6506b49c3341f0ec6c3a49edfb1e0d48445e15769a0a785c691ffd9877a9c3e2bccc16b6ca0052f368bb037a8d5ca65901afcf9f4773b861d8c07756d8ba91a4ac3bddbc51d4c37589d700"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"58edd3255d61176976a5f11fed863177fa2046c407044f5c52fa2defdc625664","proof":"e851850b86bd6654e057ece1d48bbaa7fff7e9dae5b71e48e5870ab9fcfd800a74b30f43e689c519c0df6c90e632a848b167e273e13077ce8c7722126ef7f50972e17be3f82008581e027f8b573b2d5128015cc680aa1fb20575101de31ee50d0e14df35d690988a07353b16b36560a191100f7d380624534ad2718e89526b350d69eda0c5efd8f274583077fb60578b9cbb7564899b2fca9dae6f867ddf8b0c3fa70a5614a7620775ab48b78d65d9b1e2debcd6aa505422c62700c656a6f000bd655f8342aa357fa01babd9d71944b16ecf52474f3527cc1d3eec405081410020513ad00d789e9758de75c31ba0e0ca2f535ce09dd94cc871fdaa1c257d2427c25ce0567da9b903e814810cea7933b909955393e685f6b3b5064b37487bfb7b6a4311c8a3e39d0a984a67a1830791bd1f132d1db7a9e5dcd87617ba3e1bc81d10824614cb3b8ffe1215619b00e5110d74ff4034450c63a3a1174b7073431e6844e88ad61a31704c3f64e4635d962d3f3f38742f35d44615a7df95059d63bd071cc83faa3ecd7bcb3750c57430170b0b1e2daf909828ca945a8ce2794b4e051d52e03fe978a6091f7647e0ea7296a205f4517cd9371d321665086cf5b727c86f9c5e9d5feee0a1e8f802850f981dd8f15dd9fcf1d316c5aada6a2d3ef4dc797fce86ab6a481c2deaa1ebd45dfe05499f835fbff84ce9ef51ed0f9943c4518c5914ee65ace43ddb5b79a323bb8f70f7dacf0ed18879ac5a7e885b868c4a016d52bc6d990a9afa5736bd3e49579a492c941a20e00faf236cefe6e01090a67bdc04b402b38ed5bca0c00f26b2b7490d4ceb77d7bad4f7451130f450fcb1c8c519583e70a9b24f93865dadc5892911bc626cc10e367cb2ff68053ea521011626e80eb486e8ff7b4610b21c817fe63cb06ff5cb06eee53b48bc7c243e0c8e3eb37007"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7e1c00df202656dc67e9a40e10c2033d5cb5c1fd3bbafe9ab064084350049a71","proof":"ca36eadacc4148e61f5163bab3ad087ecba35b2f5391c221e162c19066110c2178356c99db13b5ecd3ad3af9c4c11a7813d91d0e7f066ce49471bd4913b7af0c1edcdf99351db6408380ddca30831fc225632b7542813930b104028c77c5f555c4fd0ccfcf4a3a45fb2c506e52efc7431b7805b86795d4505689e420c92b4337112fd6c20390bdfe38e95ba04c01798fc7796cc803b68935b018500426c371083d2c00e0cd4b6f720bf84b235eb5eda8a15f8562f6db19ec51626699125a010bbcc8652610b697de715f545e48ee0e2e9d23e7a68430f718e45597ad82607f09f08d3c257f30d21538bd93930716114bd64b645cea5bf803468e99c8b0b8c126da1db6c7457aeca1586bd79e2a38948cb93f4471fee533b1368f3a054b89280c9078724f97477c854b029842447708a3c18b2a9d061d23ad2e2e0019961df568e4d5f29bd8bfdfb78bc51ee30d8ca3562024690e21135d5f1163ed58e2294621e67abdcc28f9fabad8251a4cbcef400f49f072f4b7c47a897107e247b07da411c4013f4d278ead80fcc92a4bae27be25666e19a0808189ba027e03d2162d0b618c29663b7a2bfd62fdcf6269bdbc4d65826fb7427b00a15d365ddcabc31a23279ab363eb1dfa3c61d5e94a436ed7f82cce149bbdf6b39cd14264292cdc84b9069eb9ca7aa5d80d2ff2a8a7ec402e2f603fb63f2524a80813d00e685c5fe867316c2e2c13cd8bbff9dd426d31b6df27d9ba436e3cd2c660554e9a7f0a276c45465c26bf757927140f86b818823e20480312ee1996780bf779b85e74615365b00ab6b2438e7877068c987549c1a38b538bf502b5e281005592306f3b3526496f6fe8b6ba1c1246d972f4df60a016bd09aa86d333f095b711211615117212af4b0f78b685a5f6a518f7fb2e72898e79c2f43157e967c1f9d8d955cbed311d2d7b06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2669b2fe4bd51bd837d2ea1b56c1841493c049bf29059356147aad1721d4dd72","proof":"2a8a523dd93a217b733b130a01ed59c9a22e76d1e3c815fd8a3036b99d288509c079f73977909a361bec310a317ba14f967edf609376c5c52a3d2f4e2208361ed2656d2ab81e04e875cd527be47745b29b9c03f59fb7e57b9413b15686e16a2d7a1144877fe1973f2fe2a71463165acb881e924b78ec3e7f1ba5b71a251ab74be1e37d145ffbd384f6c4564906c0f716b633b75d73d57c4a0cce0c3f2913950266f263ab61b7392cc79e31125a7f3ebb981ea0a8ae790d69aa182e059f41d907f9b63e0469cbbe7f2b10a1ac246d0ab5568e18e55b04b3740f588613ce76f402cc13dd7edfe0d222cc7e8e5fc2c52231eaf3e632d085af8a3769570c16a2b8332a07b4849de675439be6d278815a0e8d949fa49e21bb66c1f7a4d91274fe466f140eed9b4b53d75066693069c8d201d75eb8cc7a20d27541852714ff7730057c5ca40f30c5c4e238c47a9a066b09419f937a759b10cf9a287e6363f87dc4d44802396638b74708d8bc77a686bb72b39041ec0bf5a6e0c78ef42f86b68a63f52aacc76a4104ae198c69407ae1a3de7632162c5f48384ed6f9fc84b3d4f297ae2fc2949736f16a4a0553d63f0375396acb4f0df689f49b81891a2747534fa5871692ce614c3725700f8cf1500d2bb4558d4476449b66336b0efa9c256e0deab62cf8f162c24d190dc008cf2c11b73f2684236d2318c4723a5a4d4bfe29b5ed7417b60a1677d04861382b25893c775695cf3d9102fb48ba43a7937fe7fd5725476422d200822cf95d7868e8369d4f25e423cfc9a0de8ab0631f8b8dc4d5fa25951beec0e5a558cc342e7abc430b08ad2c6b78771c21b1bbb9517c2f99d15bc0f768caea6831b231409d268d8bb6ac24c7a0a3d8a2f972d800bc56c2f9e471499a0a298e9d8745ec8b032d165f66c4cfb082cc1fe02afb56ed9887d2517458508b01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3c9de59905ead04482d8267a39a96785834c6e875744c998c0b78a61146a037d","proof":"9cbb910ff7ed434e8cda13603c9ffdd419da202960cc108c1cb5109cbfa2c16e2a49f26ef07c54e041ba44c9e00833fed7d6402fd5324d58a8e2c95bd4ff257c4af4ab007473854bfedc6c4b41c0e2ba904271bb545499dba4b5394f6a56f830c0fc9824cfdcf627c3144ebcc0bb2124bdd11df9470261a6d2dbe6d09979b71b16765a17ff0f5e91ce1a93ed9f96c30f994cc9d51590a21aaae580b49c593205c1c5924da8917b7194099587269ef15fbe74f1bfc09b007bdf3eba070e6e0701ae50851df4e202599423766d56a35113666613778da48f06cbf05b9d3311e502449138524311c618e0f1519ef46d5db6d56217aea0645b25930484745c40a557622bd7f3da4deb8f44b7d078be32bd3096d33b8d5e711800f584dfe5102ea837fa14b357f7ff66a1eecc0488992f144176d472d437d87639c8c7a058139a4a5728780c553443cd362c1006f96b223b6f33f4184001c7e550e295538f72fdc97550ed90118fd78368715ed42766f260e8aa1070d55baf55183da8e154263e2f01ca2504dbad5644d0defc895e8958637936648d22dfc95ce3809deaa9dac208249057a9e319ab7e084e5a908eab869085d3fa5a466d9a25e00c740ce9924b5b3134ada7482f0baf5b6c14a2d5a9a42038efc5094362bd7b0dc7da6083d3760925b6776e0eeb79bfc4360a2405cff01ca574825acaff091554046ce23fbb3a9776328a0a40a31ac34bbf53c93049491234b0966fcb7e0a9bce1236febca287bb1954a7c3a4491e80caee676c65590453b688345d53caff4c74f4ce7ba7a8bcbe4fb2a0a979cd5a72f5fe5b918dce7b0c666abb261193ba828965e75586358b8f5ee97a58c9752e04642385c8462996529d26945d2af46f60f2de2581f0f469a4027e040144aaf8f5fd9991d7a3338c0a42c8236dea3420a7c1e85a1a0c4a329e0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5c80faa9b9e624d749fa6099b265abc7e0863fa92667d122e80433ce9dc40148","proof":"9ee8a1b9ca60573fd714fe527f894d0b279c52452fdd83a3092399c74f3f821cf8e1e4d03b27790094407e2c5c321316a2de495807244e3b23c2e3c2e49e3c2ad4e5ea2527e290f830df5e8404973bdf3fa76fad77141c43bb7a6584ddab142688d19ef8b7ae95c5233f258bf2a269a25f7dc3bb78df9a0650f9fd9b958e9d2a687b2711bc7a1d1fc02a85c16c086251feec5acc7d5a45c8fe663f8d2e04490cf3f6e040325b2920ef4b597afa3433bfebd5fdaa89513381f741246a60d1a706311dde5989dc9a02514fcaa01dd04fce9da4aefb9c5f493724ed2c1df5abe10cdcd57ebaac1e4299508c749fb7cc4ee0e21bd0493e20b7b2a676166b415f1518b81d88e13c7d11dab0ad83c1650896a7d622b8226e6e0905375b4160e42cd34a10a722b3ac519a0e137350441561bf61cad2594a6f6b44199662153a10372a6b60a7b4ad6791a8a6589095c1d80515c587103c6ffb2e8c6cc49874bd06bf286a581815e2d8c48f2f8327fb818bcd83fa96ca384ca10fba80e46ad07277101771d6ff2cc5f9a53caa691e83412ec379b5908e7484da2c105d7f9d86fdc7f4f04b1e62063d6ed6abadda51c5e47ec6bfbf6d0e4be5cd9ba3c20e818b12d7d1d43bb471f554f57dcda900619f34589351c470b8f8c13e5d07825d4ae51783f2e52d14836c78c16e9599a3f8468771c67700df78de2e88581e4082cf11a7c77fc747d2534c3a2b39befe389e2638a8905f93c6ccaf390609eca311d166368aac1f0fd86bf551df90f7e67c84c087d0ef55e28186b99ec491cdfacbac02193ac91a6d9cf704d0addff98b912e69018599062e2af2213b44747382e9587ed869ecd5402370b74e623b7e5a7d81ef30b92e6022e8a1d5838aa75a7e7c2f6a31fc199406febe2ce8722af8ca0ac55b1683646143d672de2d034395419a72057e32f95f02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"865947f7edd4dee4d266151a211c20ced4a83aa988b95f9926614080b457f765","proof":"2ad752c5f2926a4afecacf6e6b2ccb97f51cbb7adaebf814a61e86df95968e494a2b0ac9202847d7b7244ac1782298b0c0520f984ac3f392997988b82a5f21277a0d4f061cf61793f4071634dd9e86afd942a58655d1c0441fe7f89e31795460388fd0974d4771897fc63308f489db17d5bd5201fffbaf15372c89805dd2536ebea0529b127f19ed0100512ac5e97e08cf0aa33733b86de2008edd103909c100a250c431a4c7dbe7de214bb0daeedd44e89c80b17ffc9475acb8d0ef1dd5710d9499604cbbc7975c354b0046f90172197c69ee9c8f225f803482ad9f139b130a80c0e0e1b9478833b80a52a7b6639b507121791befced9c8f176d352e6b23b0aa81de7e01a91be121b2651f1d0fc64af18037f2adcac3de9521560b1f406dc766c1f14980dc640ad4da0e1e3bdfc6344a31e57821beb52db7b77f6a3e953127150c4c4ce4bd5d03e6b756ec49cf55e6649d503f9ada4d6ab260396b66452fc0ae447beaf081bc366d0917bbd3650bc6606ebee23eb7a9e905d2e3a8ee247a343aa3d50bd5f25545d3ffa7d4b447a8c92dab66e9b72da5690f082f3268da1257d02e8e6f4ae9097414d113b9730198539d9c3be987e21d091ccc7333e3123933df6ccb8228be5110836a9d171245a313ed22013e95259859cc82dab85a52e0c6c00d809c8a17aadd2f5572a019c97f500946cb0dd3cad86618e037b9c5c701d309e0888ddba4450970c32db0714073b35c66991877f86c7bdee11eab75f1bd43acab96bfb4e8ad0a395f6fd38916b6f4edd1a38c26d1b08fc0914cb72ea05e774b427084523706063f55387dab31b2d8a32d1c9d924e63ec06a9f24208f63df3447f8a3dbd4311d4a386decc60fb477b211b78b0ade70c125d5e8515de7d8fd042a1ba004bd88af01ca11538cf39445a096cece227c00d3fda9d850173f2b2505"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c844961cfc08aa6890739fb03089ff255dc2c40c25fcc3cfe9fd7e2ac66d8a18","proof":"d46dd41c5badb1db0a53e2bb8769e69782d8d5dca0742336ae28b072d02d9e24b8d6ba491b20e4f4f4021d579bcef9ae63493847d4df33a97d48f1de7ca0e459fc166246ab42060cd9037aa6e733069d0e5aae11554116bf79ac013d4ee65c31b8cce8ae54a1a0f5ce027b8c87aee00de0105fbc687f7d88020992c9760ca135a4233eb817460e89d5ee9f4e482542b36969a2ddc69130f3222fefcc9a1fc907fb069ce6aaf2759290f506557911780e2385edac2f985baac9d5a153eafe5e0fcd5b86483965bf28bfe96d4995679726059ebbe639ae2ef089ebcdd6ce13610b5e69c651c93cd2cca86e547d5ba0bb45c43569511bfc5e2ae1e773ac3e2c900780e4b8cbb88208151c53f6feb43bea4ef5a817d8d75eee2da848dd5b64fcb21bca78bcd9ac2ea4d84151c4dc1fe0f1905a15a689becd2453f4408d47edbbb93a84365592e545101f11291e90784b83796d7c6efbf5b19e3b4d5ad68b7f4293201a33be748e3766f6eda0ff7abdd4210a92186732ea941e72807a2fb94668d241e8a752d987d0850e2b77b677941b83ea23297f483d124e3ae6b374860e34e56296ebe0a7bcfc3e43ac0e36c48bce58082119652d661657cee30f4315f6404c1c92653b0662ff1dfd28f5d056016ed6807e23dfc85bc8953dad642793038f891ba0bb585bbe0891918d2be0f09e268be8cc892a412ddf9320c8d97b98690d6b0f067cc17e468087a496f4c9fe69d43ec8aa7c2c18b4a8680ba8ac4e35585d7226620c657aaf8c33cfc394331a0e1ba215373761a78b6b55e854ea4911fee3d23c9ca53f7541ad520aa1eb607bb38c6bfc21c376803949e69acc64d82b94639055fd2828846c3c98f16038f50139c069b019e5c3ca2ee7b56bfaaed2ddd015b00c50c8d0f1112ca3043440f47885fcade78119c2fa0aef12bd3a128d877c79ea01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"44d2d4c23a2c1427ac47452c909f0bd86334f9397f45fbc09ffcc853e0d07874","proof":"98fc704cd8c236237b28fc2df653a7b9bd01e12ab3f2f9332693153a7ec4c74f16cd045e5ceb91dd4d480addbefc1d36afa6e90859da707bf00265bd307721214e23a7a5bbae419072c4c3ed4a086dcaf384af89862775346aeaf0a2fabda209ccdbfe78844a867e5063196ca23cd71640eb5f26112457dd2887b8ebe66ae8213865207b713253b5477e41445573b9928b813a781e00813280ac7a24c033670597f8de0f0df4f653ef65c83dd52a3b65dde3b60773a8db9c8ccdf8a83109310268b5f6e5294cf92d338fd37a87f29941aa6a3343dad2d1c62a2791e231ce1b0af0e17ceb9bf704f5ba32b3928b33ed3b149bd2402f33ec023d53d95fc0002e2112ddf0ae0c6f80e7a387b435e9731956e08d53bd648131ca8f634c922bce977cf291be397d0920293b291aa4a710a5140f7f2a7bae9d971326b504057bae3247fcc3480a5e72d6b6f9cb1fd2682f7561bc3f8e6cf1e15b25a697bdf74306166d2623ea25332021cf19e4e981e38edebfb4f8b6c790b3a266805a23b1ef6f7e6a3ed28fecf7c475f13ef314325a382b2481925ff33ad793f962b9073a054e4e525690e2f6594bb70f13c0ff166d93e337317f6ffb97c1079f584db05e2e44cd7ce8ea83d6070b57c56b1da7233b953e68999188bcd1c3e5a32909af8204f21d622a1a71db7083944b4791d799820bd8da16136d0a6367acf0b8a81adb51d61a04c46c488f789a678e9c02503f3c56c24e4d4cd38941f9bf7ee79a098d0dcfdc6e1214be4cb82fc09c39901359c0c26ae76307a6da4f9124791ac46ee2bde14f66982698b8b7829845d9622711930c4a078faba96f871b62badd1d51c4199a2f7ea7a7765a4c43490efef35e618659eecd4fdaee141bf2312c77aff6ff8383d9053858d65aae48d16f651c54b57eeb327a9b90b08d75ccb47b3515659cb1e9d80f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1434bdea2736d1f59c2cf395355cfd60e6292cb4ead063fa159c0e8411e0e975","proof":"626eb1d71610eebcac22ce5c6657ee23fa8572d7bc8bac931bb2bf6f4ce46f588235e13cae9f2da2dc9be30d55200e4a46f97e3755ceba18a90a088d38ce653fd2ce98f2f76c44ce0d218529047aa3f40de4c7132e360729c5f92bce1b2dbd247e2ec6e9aaa9c736c5dc53abef3211687c0dfde28a32ba87345b093f4f7c9f252d583a7c5f01149df81d0042f29c5bcbc2df95a1ffde24f46a21da2753397c067bd1254063d85868f9eb30080559c115db16f9d37e6f6fa395f509df4432460d40cf2dcf3d3d07bce95aa90f99e0c7c73c2d82982ef9f540a589de5c8bdbcb0f1af4d8ce0e991ca682f95cfa6cbc3d9c0448f4ae21bcbeb1a7125fa08849380b96b728b0c8b77805c8086385e3e8284116d990a04f03e4bc92a5ba21dd1ef573f0d07f68fd7a0fee45c3bf801b35035af6fd54f60149af1cfb374cf3a8703a4c56643e7c5bf14665913f4b341a1e893d49b4b73a2d2d8d8dce2007b7fdbb2167dcd7f565c9a1fc1332cd204ffcf2c105e6f879a9b3ec3abd3a5d8ed4baa95153dc09cdb8e7a2886ccbcfdd45b5c362834ab12f173a7000d89793326a0948df55e4b743124493d71a43bed0fc098fcfa2692e1a59ff8c239d7ffb57dd7bbda50d8a326553a94d0a92184776f4ef0c159a1da02dc727c5fc6f428aed920c1a834b125f1e61f843be82ec25ebe605f59d9a982934deceb645e617523b2e5d21787d88582d66ba0fa887ec6996a1e04ae3e6a471f4406ff453a2e72a87109e48396f0c60ecb3a529cb7599fa5fc61bf6e90883ec5eb6e666de54b35e6a7e293783596091aa3766e39a0b967128b928f37a856301dbf50c90940977f6a03ed489a11666d7862b20b3c967dad27cef2b43908a9e2048bceea5a666699b0faf30b378046d62d7d5d7e13cb41d390ffcbdf8968c822741a78e69419da3f8a41e55c73a05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a4abae3a2a19a3fee1b6467863498475395d411fd2722c6cbbd96338e093cb79","proof":"ac4b6e220ddced4975482838fb0c96eedc1ffc284e746750ad0dbc7d8fdb1173060b43ae826e45faa266228a0dac23e7d4f21c779bdb25ba2a91ac53df3dc277d0ad09cee322eb815fdcfed9865e256bbe48d92cd7088e9612308ec5141aa72be8742728eca96f5ac29ae0ec8b58d4fe39006804558a1e22c8ca84b7325d48187b6c4519abd536bf1e16a6190fa315b419ce7ce0e3c75e1b142c7e022382af074f76919809e59b6d25073f2ef321d1a10dbffbb40602e13ea2c8a2fdec1f300e5ccf5c28b63ad1b69eab8a2a6698dd812200377d6413fd4dd2f2678f95f97f01d87db106ed783cdb5109c9b54541fa8cde931df5c967a23e137c26524f5c2c74144fc19d328321e17b2144ac16b208d008af662e0c366920fd776172476c6307cee5caa47a255fbad83e1dc1b40b057822138abe122efb8f721debb675e782161067574e11dde8ca27de147f7b7e6a01d447f83ec7b10561d624150dfc4d3048d21db77786d88dcda5e9ca2ee4ca8aa9f1082e279edb04eb6b37220d9e589040ccc23f4b4578f95356f9f3e1b4dcbeddb3902b912ffbada38505e3a652871d61fa2757dad128f52a35e5842db81f772b5bd44464505b630e245cf0f173136e1deafe253035cb45b9f8cb1fe1423b0c00f9ca3329d7b9af54edbe240f38b04300924c3ff214df8adba86c40920547fe245984050cd3e4f351fcee17a7e93b8d435085da6c505b63c30e06a2546b89e56b1a26f4f3ebcb0cff0931b4a4c3f53b3ab4636138664505530e120f1229709b0bfc83e5a50fffa916daba05ead5fb0d4296b46d12b404e9bfe907b72ba5e75bf14abc0ee9cb8c32dc7b3c8439cd6d9f46bf8be3eb3568e44f3f5f574770d8a515aeb95dc401b9fcf3412fe7455d62130659ebc2183fd7d5b38aae4c861ce3792f0643b9ed279628e7bd62be1da37f8b01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0c04702ae93f0f4dc33670c7d83e174f7c345716c1d5a0a47a11fa5f43f6e21b","proof":"5e84448c4561c488884713e8e833ce46f7f3b310aebdf729436fe72fd773e034daf19a33f5e91f3915a03c95269fe0a1a8bdf3185b324466305676934b6def7a04714cc0abef81d50d3c1e2fc6fb1a4ef1c67aa80bf0f9bec1cfbc5a1fc0255c04f95ec8fa7bab7e566a39d4acb2ee9b3cc3c73df14cbb50130d139b90eabc0658d0c5265d37c232aa28ef5e6f804af3a204dbb6aa533607e9c0888d3e64cf07e32f1c9307267d87cceac368b7b8302d586da90c41ed97eb9a64e701ea226f0c936cf40e764be24c83ed9c4800c0c54e84efa1cc2863b098c7809cce89e20a019ca39704e2b43342d94ac1047c20feebf9527d445716f79b4085ae7933d90975c230d64a5afe752c1626120b7dcf5e6111e45b51c120b59c9d2a14fd9b57c73dccbfedcc325ddb8527d3b968ebb68ee8bdb1c1101b09ac38cf4894430891bc5ae4bedf0ffc15cc33c8fada8b677df6e4e8561e0b61009a0e7901425d38dbee59263d6598f6f32c0ebe3bf4c7bfa8d2b942f59a47a0c809c334960b20c7e6606a868fb35eb97c8fdc4e6795aa64f00e2d3edd8e64def8a29bb4a90523816bd9643001e6fc466e5390130787d5187e90e53232df723de33ae8eaf487da330e1d045402ccf62782fdfaa7ed26d64e06c96f98ffe73c30cc11b893f20d1c6878a9028887e708898113e599376392fa371e2cd25acfdb41cea4d55e44f0474fce84422009ea291391d88e16136e37b396f6ea22b767385c81d08f5d18edb0d3fa474194d4d5f20c6d371498426e42100df3d9a5b6b7d0895579ac0677057dd3c70b12bc32593c40e50db3b612ce21d888ce8ea2e4b4ffe07d801c5d7ab2195cae5c3da57e58e6fb5876543b54a07ca19ab615f486cc553cd4f25a2219da669d464a09455e959c2741729e6b38f8da02f701cef03c938f604da00ec5941b5625c7020e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3437eb0a63de8f22b1af556ba18ef512125e2a8b3dcb21b6f943c0744c24b607","proof":"d8e9ff35689e948e2626f4307da6dfdaca04371f03a78f3d00d07ef0dc2aac2c70dd2ba5feb37ff69025d2153aaab72bfae9ecaaa43a93f4607620b50d49d941aa0b025d8db7abd04d883880f6b05c2c32f72d14cb27bf8731903090032caa72ce869dc814c73dd8792d53b55298ef31a9ce86340c6ae0b10a0249d27f8a013f635eb9565f9e6740cc63f86aaec583aab41905bdfc840aca511336860963370b7fa30a4d7a827f7f86f3b84d8bc344d3690fa714f9f10069c0806de07a850e05213532a98e1dadab15e947bf822147fef5eee946ab29966fe9b788d6a599730bd45ca532c3f39201b1c512f3873db00e3a88bd9386ef36f421ec9c663fa82053743c780a35919805a9f5f610379ef57e6b99f588327004071be099ba4cbc9e1bd05e5a04269f4b024044194fd29719e097fc6cb96f3d6b32b773630420b2653a3a671c8511bcb74249adee8d261cc072f44e8d230b83f1ffb6370ea967268c34a048dfee84a837e05985d620e90b08430446c29423f5e227c0774e109105317e8cef7bada88e683ba4d221c79dbddaa20a1c627acd9fd5d69b1738f491b9066d9a3ce43201491b064c2c624312fcfdc7dd37d2dd7156fbce34a03df3cf6d882434515fb923e0582db9e2f9275307c42f41220bef50b67f3290a996d6e8ebe90548859411be1446c8569853ec431ed2a87e3dbfbf95761863d637843ad99e693ddab7fb6bd75e35537c8dc4541f9e9453c1e123550cc1c5ce8215fe715f2e660b50c6fa79bf1ca1e7259bfc0b92af5b5a960462a05ce68a7244c4ec638eae942698cb7848e9684e8727ea3030c9d8c9f97e76f252327f813de96f2b96efce3c29f7c4e63b06fa3939979d8177f62522a4e447251709618edb6dbfd3644ab4ec0e07548af15bc4dc5f3e62a088f47c7502c7bbb9e83411bcba2333c1d581bcfe01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aa2841b1549db1ac6cd4e027ff46e7e38cf71c921598c72a863db37a4754dc68","proof":"68979cd3fc50c90947be6855d1878892b067e3c2a6ed628209746b234950200a74c1395625abdf2a0010d538b9c5cfe40d027b6006f4e227e837c1be724c363cf43fb24d2c6c57045eba1983c6c0ce13276e1f64577f6a40029d22cba8e947728620768fff66647da4f9edb21ffff4d9cb5d40097498447cf0595b9363a068241c9a4f8cb050e36e0f2fca297f1d99c558cf0e95268fdc1ccae8148edca5ad0c1f60158aca34f1999807425082ab715a0372b3acde6925aa00b69d9d00480002e43aeaf3400dbcb690cd9079be4736f5de4d88a6fede5bb16592228d2e35aa03262a296599f50cbe515c56c788fdac5227ef84a9f1d4429c7b075f02aa498d2b58152f9bf067a2fe3a00bf3ae69ac8aad8b26ae3679fd3b4036a97929e99454c381a194cb6b558e5a3e95eb92d3ee33fbecb154b7c7268d5b9216b0ac693422ba4cc88636073f963c02969f05327ac98cd295e7d6b1f366c51defcb5382dc300eecde4d2d2f29548001e914c4262a319a9c44e13140c7b76728c33df0d0fc63bd0cb4f2775550d72fe2abe30310b0be164d01743ef144ff7e520796a8023df10a0d7f7895325d934c0556c6b0f37396e7f7f55dbd7cc5aed30419cc12c9eff51aaa93aa80c2fae7fc68568c75310bfd8812bb22c6aa345f82f21e20ef3f1703f32e4cefcc25004bfa763a20bba063d3a0cdef1dd185dad8db30a439a0184163ea06a060ed864fd172e8d5da1216b779df18efbfa17eb4bbfb8e227eec82e1e222a6669e9d0a6701d0433f41295b95e094508f510045d4558eba679d1d583e76d76aba451ea720c7357ad339f3703d74ceea69de75ccc30ac15ebaaefc1f55638a6b9cc81d31f81ab296b7991c3190294fef2acaf87801dfc783ae38074355d03909d9cb603eff389b968d2d92db31c51995d6624db2f07c86ff810b3b75abf0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"324ae264d5e80b0aea8d05eb9a68a4f0fe367ee234ba16aaace024da601ed55c","proof":"f630033de6a688a8c00e81acfa871567adae53528af2960d99f91eaf3ff3fb525acb261ae66a5ea0a6dbd267e663c8ad0824439fc269a45ac578f4586fae7105041981caa53083842aed9f2cea55900fff8acb19e12001b8551dcd61f4256801e4722b5bcc72bf8a77d831ed77c03ecc5014566335eaec9c37c7b8ae1e526974b20acfa0f6ce64af4f3cbd099ae52531ec0ee9882f0fd76beb70867a1cbf1b0220cb8978c68f9a565e0cab05cafa5224bc313e0ea5f5da19d6fd47a5c48bac0bb8974e134e5dcc96dd94b7c413efd1d846c695c3e9eaf657acdc1703301482040c2cfee49965aa0b8afcabc29ed2e27c477be092350802bb85c9acb225ed58393c366f3a5e31f94767d8cd2e7db4f4d1c4940bd7a08bc97a9d731549e7be7d3e72c9cf4bfdc4aef967bcef844207d28da8b92b1785e59e45ee4bee5c36e2ad26922770243047d7f23eb6fa8aed205dff6d4f08d515cde099830fd29fa61610199859f308b5ae84b5401a4e0e37e6c74cebbf91e99325d9842c796a3a29cd9817a2e585bc29c2dd9bf386e6cba55a3fc13c6971bd76e8c4564e78901a1e9a24046aaea96268c78fed116143d1d3f99d05c6d025e99cf878e940db35007e04391d04b7efa483b9a185177c017c9dfcd13dc27fd61c09e8ba67287ff288818ed1320e63fc137744295ef763be8a9955c77e36506bcaa069375000b153e72ebe1450640053d890a4b9556c590c19d833143cc65ff1293534df8b924a75cf8e76a04f46e9968dc985094d2139c5af7a14f1592abc4cbd274cdb012e7430412a152d68babcf61757fc7e684ea83fba7f35671a1e7b33392eafeb8a4a74a7c4bdc742153cf36728187d5bb95bc9e0feb1fce2a8018bee826cded18061eeccfc0920b602b4184c778d8c84df1d6ffab8db93925178c704505f9245256af95f3ed74b3405"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ce668694a9997c51d687363fca547d5d3f3fcdada8004808a267a8a45f942f65","proof":"60f1c2ded6c5674aca1bb7745e493eaa27c10a536cf708b5662eec3c25d17b5c3e337daf22f6c2dd49ffb11680180f07a67d8f89cdc0a74525fb0593aab3ac575e46de7a4069487e1827cc453422ec6eb0b39b8ddaf0a80e2d17f2897509ad6d42b7b98b56ed8c9bc4e52252a2b036b1cd9c82a613ab53f880ff362e2d0e311b575f0da57701b2297732660471bc8886e0dd95859e2714965f48f8524e724709b3de523c1e0cadf191ed99199155e7df0339e24094075696d3d57a374dfc0f0642f0d2687ca76f8f4fbde70fddc7395980de6d06cb787f8082ec967cc8aea30054449af3c4e1ff1dfdd751fc88f7af7d34b951b55f3b95b4023eb252988e493e9a8f416b907c6e962c483ad9de06bfbe060f676f9ca9c82173ceb5f2295bf40aa21c246a4d151b3cf4ba7f17002fb107a86968d1adbf4263764294c8187f1d768cda39b4a999b438f72a834cec2afd8338c55fc63712b4c11410b80d409a4502d66545b86ef52af6e23133c370a9f15d9ded76710f1f9155320c171320e9e370a26c1790dcc5185ddc8bac781cfa5a0321045b69942a0870fc4ea4bcf4773944668c9fcdec246b5c4fd205bea61b33677659bb54745d8cfca0f67beac8c05b0f2e933f84fa531c66a232fbb0905476874be5e0a1fd61f2ff441bbbd84a3528695caa9ceae3a683d4eb01353dd4528c2f9a0e191c0a79bd7b175c40b28419077a7851ad9035e4df62a034c3fb7d6d7cdc056a7cb96739a510c9b9fddce5ae6e33b872624e0406b67490762622318613cf2a40c3cdb812b1df927c20aef437407cf41a7259d24d3a6c9113ce54ac306265ca1748d5887c7f3d195f37740c70081dfc70458fd3d8428a1c99d96c38ab5786707e11768647380ded091499c32a520101e7a95bb4d3ad2524a024f416bf68ceb08a54193225ce11882e607c90234506"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6a640cfaaa87560cc8230a53ae5a638411e30353584c50fbe61110ab56381101","proof":"4e80a031aa2fa7b40d20f6499c22447fea791976923e1c8195d17c4c1e499f09b41e431d58bc40f2b6cbc4708b88e17b66f0c206d98b1d00e1139f71ad0b225e50b7e4ede3bbd54f24e42622528230fc4c733e1dbbc85630e8f9ba639ae71548aa085ce0f72902fc67a76570a899a4602072e3bd113aa9bc67d3b17ca36bcc1f7db24685773281b054f375de78aea78dadf25cecd20d48a90f88076074e8e60437d0ff8aafda4d1d3fd37ad583f7a5f8d3bb0aa997ea9c1e1ac6f233b2ae86000069b29f9f279df66b16380a39ed9ae0562cafd2eff897bdadc3440a75b8a8006edc89480b09581b23f45c7f4715ff2fc5b7ab4e57ffc00a49ac3294e5935e2c46b58aa41dbcf0adc71f7724157db95e475f62d7e5b7868943bf44020c879f68924177d04485f7d7d3581c4f29433e8d2a6a36f691e4ff6ac27694c97e380f399cd3250f5ee4123b038e6a0f5fac0a06e8c36f13f03244070e9638698ec6d30d7e5daf35b25475555c97da86cb49f50222c0f20ef965b8ad7ea478270a30d7655051aa83d49cb36caa650bf38789f8c01ee0ae4e9112bf2f4a66e35a2839e27b50b291b6365756c540de4894107ffd59dc176660622c4661c13016cbda17f56f88fb100d1b2a7c8c049f501e938b0c36a8893ebd64b1839c8af4e159d943736586072be3a200927b39c9ac4fa1f55b0311610b1d79a5ed6dcb689110a9fbbc0446323c2fc249137433757155add7b2d57da722ac53bd4a12e38d23099384cc3094faf527e468215cc856ecf1ab19f7e2fb577a34dbed7e0182b38cfef20c9869fc7fee68827f7849a7729808c36757d00f6816ac7f3fafa64bd9ab93a3e1b97fe254a7e71f2208d578c01ef7bbd6ddac507503b679855a1a7d3b418e07af51050912a728227cc807ad8738f91867418e718dae8b7e2d5e242482abd6b33d9c0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6464b4c74e801f3c84bf228f6fcc9518b79cd50d97cbf090abfc076c6f1bed44","proof":"10c3d4bd8ccec5c7780d7b03fb2ce7dc32e7bfeff948b5f2fb543d9ae9865d55aa5ce8503b92600ddaea7f9ef8ca918288fb2ad1c023644a586112afa84c3c70ec8252403802298b999535995e203b7c03cb131e4d7c68d3852e4e339e61ec747e63da1be6b1d36a8eef56da2fb98181a63ba25b81e83c1930159b64a5cf92351e48eed07f57cf8734c4e61b759970a1980a538276e5a13f7cf5435e508a5d0a04475589c0f772b7daf50f5d3f19913569cd7409dee178dacbeffef7a1f39c0bd7fe43d42384446493c6a0a09f39932d2e6314695893fdfd65c12211e51cfe074ea0d0ec9dff7c573d0253a7fc5e87e35efa7edc922c5e0a45fc8a8a6736a47a50b1a79275abd9917bbbedbfdb775623001ba69841199abc47c20ec5a8a75c294e36946667bd9081c944578124265438f19afd7cabebf60898bb00e06c13e437802cf6ed75532849cf658bf455b0e9c75821b0c99f2beea8775ab5296c3df7406eca99d15c3bf39ed2dfdee0f0fdeea89e62b3b6822807bcd493a8a2cee4d948b645d00e0647afee7b8841c32fe1593b19447edd3369da0e01aa2879a2d32a54888e5e6adc3d0097143bbf67f5d8f693395883f73f94839a4e22e5845586fb41f6f63f84c4b0b85db8ac489d801e4c9fee7e40e177c531b74df6065d0866d667e4879a59c0b5f370d129548c0f6ef6707deb1f037338e13790e6681bcee5636306467e0dbdd8e8729cec59f0616debc4d484bcbe0fffd0f11e6ef670e6d5613e82248ef1842108c580d0a0bc6ed616e8e2a9de1eb0ee8497fa0461200360085d743415df46ba6747d3b151e5867f575c8c1849e4c4f040114ec740c4a7832a1ae3bd4d7ebf9edf725450e2e421760b231788ae22f2ac5b9ccd031b6dcf5a9200936be5260e5c14948f7bbd8ec5c288621335efdda0458c3392202944f9666f00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6c659e8dd1c45b5d2e25211f0a036498125c323020a43f5eccc3bda972e9e00c","proof":"d0fa521242263d749a594bb825489bbd999dee1b12dde2edc4a9a37e78e1925d9413470f891aad32bc975e0441b3ecc1c5aa6a6c241a80e5e650eca0411e860c68100aa34283def185925c2eeb055131557720f103015d03a16578570873ae580ab9b64d27192387c7e0a9f6c2f450c073e6735e2f94199e0f74460a4774006d0b4f7b7ddcdbdc0376fac466421ea9ab27c766f3fe840f2f0adc2dd37eb6bd059613b15e0223868019e479158c4f8775d4e769aeb4edaf87b382ef272c8b580aba5dff6d76ace98bda418e3628629cd7bf750d283dc5eefa457aaa2436ad5b0028a82c489fd5f77e93985bbe609e7fc8f6a70962fe5553878ed5f462cc350f2e369d7eeba2307bb7855316dd9b0a41cba6be7857f96e4781361732fb2ccfe45588c58b310a660e935cdffc279016d97b5e6d3439106f3243b2630ae138969a30ca281a7cf33e57ce799e5358be40e280301d28b60a1e93fe52e25a46d4174917f4f1020763e7600f85e8918a44db8810782582ad01c144b916f4cc0971afd63df84567f5e1edd6e8daf3cf79630deeecb3ac17384d00993d5219a7983615217b42f48fa76447951c9dba645a5fb1e49a38fff2e6bb57fe7d0e00fe5a9e6e506768ee5ae37b2b671fa7d233ffee47c6171f748381abff4e4878959c7ae2b3a66bac70008ee74409c09d83448fdaa5ed84cc6e19be7210a46ab10832ddae33dc314eb7c23eaff25f1ae735b31f761b4638d129dd48f76caefd2907fb129754847c501c0733ce6edb85b8b86034b1e89af439b12f0b5547bc1da15a7ee7b42c742208bd6198966acbae9b66f5eb728d80486b69c8b484cea06f51124ce843228f10afed2555ad4b27f5d925b0d6641398253e76565a345a8d66c443dc88a75892034ebb5b3107687ff9cf621e8019d17e217f39074c0e025c21af0cf53b68a4620e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"822849b9d13cbd8969c1f3e6d35780c9d82d73ab068f7ddc5ddd5bf19f2d8f6c","proof":"acbc975e4c9d8565085904085eefc8617a6988821e4c703ba4e5c313a2ca747f2681d67b694cf353f802f11a16dbfb74fea0c749ad250a005537fc0fb97de9581a280141359d6d3ae5f9e7b9b3f6503ce4c4f986aa563396b2723c009672310596b825a6c8cf5eeb1cb48a2cf130ae91b044dbae7f56408a9cee0cb779ee9e6672e79ce11dc549b4d01d187b4617fc59adb95437e7a2c07dbf953c5e14331e0ba5df62a2e5889f8cbb4e5bba5e91629d300a19a28857d3a4e60744799fd255033c40df36abb7d5980e5480b6dab2d27aaf78ef76a8e0600296be2d4521a1ef028867c51ad180bac6dd2d65cceaa9cbb4d13c2631568b5fe451d2df4fbf30e56bc2fe034476dfbb3831401ed68fe2543e61152e9cbe45dfe3f3b69cd53a4fc60ffe8dc0d26593c015e88f2524ef313d6c3cdb68e98bb04fe1268abe237fe905133a8e2ab4264a017ffa6dbf3cecd76f6bc975f8ce64a001782750939081935529008194d1dc9aa23d1d3a3183c6c9afd3f0c888d6102fbdcefcb54c884ef54609d04ed3679d0e2fedd611bfe2da5eda6ef6c67e2e0cd72997bd621fdd258a0b5a8e8dc8c5226dbf42ebbc0e6b629c58796dcb33bbe17d16d85f9f14f811888a7ed276acac92b26dc79acfd5cff0fdff3ccc1fc7d0a1d9899fd8868c3686717c44a0d12589756fb0dc473fee5846a469aa9eac933b82857e8730c72006c33dd60e1ae162c7000d950a55c31943793ea2f1701b55ff330a35dbbc5d325d922aab6dc41a4a6def74d215e0691ecae9cb0bef5d6efd8a38bc98ff4dbad0726391ce3bfeb5566f9d6ae2a04ca1881383ed6b1574e2f9524e9f15badfa896ed115da55ebaaeb16d7b254ab768430ce078e8b8559c53c14633377badb87d1d5668f00202aff5f82e663bf0ea7876b54a79cfc9acbb1664299a9f3cced68a52b171fdd600"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aaeeb013d536e25e59ac967aa4ed9b0d567d5ae779601766626b4ea236a9c70e","proof":"020ca8a7a0d1a4130ad7cbe8eb190581eec7815c11cae6d3ee19a22c800e3137ac7ebd8e08928a4f5f1f6822e7978b34ed2a5dcf120711c6b448f4116915db560863533596812cd4c4a871ef48a3ffaa28ace9bfb33dac431a8241db724bf86e4a0f3c0c3f0d7113e79f16b0c619ee57a2db072533c6f670a4e4eff630810e13ccc120361c9a79473917897972008a4f44637f707eebaa6fde51bdff184a0f01e11c4169b78e5efce0b2692be4ea3df4bd04965fb28fbd7fbc869ca5e338d30279e83636f392d66eda2d2db8e9496a22e22483ac454836c8d6d8e047845c690b2ab31fc1554dfc8e953c2117f5ba874961e644d3019b0fcc2616b0c25466f338b2e49bf2dfa6281e04e85441b4de94c6fd2cccc8f5825115c28a58f15d44993cc20723de82c3239471377fe8292a7cb949cdd6b9b871110c6df74b20e3a3a23d16d0ee2e8353dfb48c2b7449e82d93f73975d8c460619752e848f9180f6f3e366cb32f9c6ba14acee843bdeaefa6e476f161850c3079361bdcdc120e86e1375ff6111072b41eba02c8a0f94ec3edf3351c04990f0880ba84925cf3218f8ca4078213bb5aae93cd7cf5b212d268497772ef183ce2909783ecba9027c8f537d7500a951256c7984871ac3762454e984cac8d5fef4f77a7c09bcdcde67288d0927fe857887a795ee3daa9d1c2f0a6fc5a2b85798f433d1336dc48e130668cf4b051d65d8014f364b51801fd35cb8c28eeec2db06af8f55776b743db2655e9e0a624e62fd2e9948e05664a736664ec11ce8642b1f16f82977169b89fdbd12a1a570a3a9f0357d30105bfec66669d0c91fe7c9fb80a8b128b6d82a8064fa62ff4b928cb7edd575b949bf5f6ff195c6c50c7e361c334eccb4f9cebd44600308709020fb3c450a5dec35c34aa8667cbd8fdca5c3bf3d945a73120daf817a3c735a09801"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"624212339830982f548f8ccbf56d2322836b30d9abbaa88162fb6d8a3267d25c","proof":"ae56fead5fea7d68cd76ab60e031e5a5ee9154693db963846ffeea4e5b65700e80ff627cf23c4e4e496f74dbc444f0678ef031252516395cecc209340f55274b7a51bb25d1ae796bc5f30a03e7f8d31b2e013826386f9b665a19584c58ea7532aecad1c37f4ab55b5524a37ff30883f1767e9920286a0a10a2397890b57a37053e7b021302948df024cbd2f8802ffc7ceb730badec34cd8239fdab895de8eb0e51c6d692a3ded315d78a8781831fbb0ebd4d1e241ba5f79d504c1de80721140ca7df4897ab3ba77ff62a6d7a645e93714f7885bf49720773d67c0ba34c1c000212d0406603592e351b6031439de73dd1f111fc42fee95c06aa581e58c2daa4044c3695dc28c16eb387cafdec984b00471a2dff7d710accf46b27d6d4c26fe0370a694464963aec8841b937d59fb17192736a78b7d5cdf53ef5115a70c8eb752f2831f6fa5a1674c6396a8bf7e0d75c3b505bf8987ba53a1b01773556f33638722e27b3cba54f5849151034025977fdd78869bec33ea5681f0e12ad6d2430ba76d44c92cbeec187c4ee7380726ed2816cbe6f3844f332f410ff1add31b60f8a1f82528ae7b85ddbd37b17a83599378022a05d1e3f0b1d8569be66e6be39c95d12228727834f67a165cfbdf1ae5ae96ae707ffb20e60c4f7086606f3df3f9b6c17769611aa367bb1e3ed7ea8de8c8a6753b27994b4d2483d90eb62c8f11d28dc2280527b899bd75d8e572db030919935aaa19e4ceb2e741b537b4e7d747bd9513a562bbd3bb83e724deff0bae7f611e7293c9ebea8b0199191e4deb4d0570ec106c84499b07db2951f7dd0a8209047772629e1ba7d27e01f8cf4964d218688ca155a119e8800833a65462b39837c7e08995887c04f9a5fda1f123e6ed056f0f7095ffa6dacfdd6e7c56e562ab681adb6fc8b20e234f99d47b0607d687c2250b104"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dab18850b919b3b1f219970b14fdc271a67796681bcfa11ad45ec0bd5e278a6d","proof":"902de4a0a9814710b4feee280a08cc6907f407f5f897412977f728de04d65c2882320a1238ab321657513d84f1e3d2ece8acade4a6eb59780680e07654885e07f0c8a266a98c84ae59128c1a16cac70e441c089ae6dfc0dd9d7db11264b5b54f3c0a5956533ecde0e34b44f6716bdce944604bced39f9e62866857782a3b146e2af40a7934bfce5b0821d9036b2b5732713216fbcbd8f00e880cfecdfa7f050c9f44e62107ed567cfb119b14f001b5f5ba8492fce3a33030769ea7bda8888807a14cdf5af4b24bf58211273c0a5733ae87c5f4ba70d59af2140793294878c504120036d7499ebc46e2fd9853348624aeb3d95ab56cc567d53b0ae7d564be0745040d6170076f9ff794e1b1917ed439b524e729a5175149afc0f79f8e9f2ce900bc0c0902d1a2fba67fa2da067e8bdf5980af4cd5041e48a787a214b7ff91e8470eebb7e8a5802a196d2cb9153c6a153ca7a53b77e0eb92d3461c11b92a97e275aa8c56ddf4e279a916cf284e116be461ef647c820eb8b44069e3f0ef45a07233287cd85c73ff9998b7ce7050934ed514acd300ba6e00fbe2fbfd73363c34cb3bf498fdc7a85e67dab8e28d0109a096c26b9e6e7d5d96d89d6899cd8515711e323c28b240b5567bf9cf068063dbdfcbabc0f605994952a71c87cccd24619fbc225e438e0b4fae3d560c82fc9bc60f2b5267947b24399d1cd19e0d77da5ac71e54e409087ca33cd67761bc1b63d432aefd6fb19b482603c74d335ee33966736a6da6e8fb0ec4111dc0089c0d39414cfebe2cdbd57b0b6c4e820fc5dabee72f0c6e2664d60ad14c2fb90e6294af328307c73baaaf9eed3ae601e58d5a8102f4f222f9e12d84794ca5346d3b3fe040a61137317dfdfa58915b477d0f3ec849d642021b9fc8222df9eef9cdac27023c658c2c7af9c5bfdb1cdcfc1ecc54a16a29b30f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ae3efb02f6e3c336bca391ec191a25b6088db939f9db70ec21779fe45695775f","proof":"8c386726f92ec1145cd7a60f5453393d59526c0bcd50bca8ba80601e88a935145a775b16137389aa341367624a1cb4a4eb945a19db9d369508a12537ac947c729e02a4178407b5cc29fa4554c62c2cb6d52186925627847e050fe85503f6df5548f295b1992472870662fc5ad36fba7a7a3fb15c9d5a13c325fc4fee298197514e43d0446389de275ce8ffa00b42cf60cdb25087cba15a9535bc2f213e738f0e4373f75a9a240f2e773f7879fb5a12d9af3042fe914a726a623daa624c1fc60b7baf1e432d3cc0f5639c7c138b1b2b607e0ac32780a61c70d51d9ffbe8641d021227cc29c11f402fac1598b598a830364d9412c6436faa74fa1ad6372e60d85192f438938270650ef177c00b52380e6684343f18b5c9f734ec532c5fa9968f727c510416f9333ad2528beb87f33d246363fb6e34a936073f0af080e3f572204734d2cb865693ce38552f2cb445c4157eac2d9d616914447f481e87636d39834124d4c9b3336236bf74f96ba10ae9624e779dee5c9df68c4d98a66628a751d062f8d5ac15b4cd7d05532fa8eed1467286789c32cee0febe701eb0b049214f4907a43b235ff7653a45f0625222f3b8aaa67bdb9672c0d6489fc0e24eaec834cc33744724932bbd93007f47e7ca56e2836b802b711de94d53c0150378347882514ff6809c0e356d717de6113ec4c6373c9e059b0bc713dd14132fb7815c00582f4b7e273427763dcb00669a4f54cf4290065484a4bf3871a3d9cb7478056f71e74088d202d7e95cc38dc8ed358fca2c9763e9af1f1a493c4795e79b5dd0e2dd826342fe43c81bb95206c1192732a0556e1b15f8928423bbcd152eba03cdabdeec02107926dbe95794849baf3f0e8e5b3ae635d742e060ff70d6883112dcadecc708aa35ee469e3709f76e6f0aaf13339e3b8b5bab365574a5a2b2d15e043ace7201"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c2bd26ede5d2e44d6c3937c687023ee592a018e0fe12cb6e146860dd22b7d539","proof":"6221a4ca65dd7a1d581bdbe06e79fae9d384575c406a806576778d01c36753736eec0a37066dc5212fc87f405607421d95edd39eb218d46c29cbb72c0f629334425bb01dd0a321f9d733068dd6604c0ad38b6fae44f1045cf6d5eb8b98fb324df69f9bb0491b7997b2319d2cb949a14d1064e4bfe3e005b9eb271dbcda29aa1b05e609425f070147dce0bea915abeeec5eda8e3f2b13a684888f6c388624150a535b0c74b93feb78bb54494eb2c6345f46a6bd0f419a4a7e7a22b8ff146c6b0afff7854c89b9b2bdb649114c4229cfbfe8ce9b912f29d771b8362cf32f177c0d5410393be6083d9e68943c93cac99c0a8e010f9a9c0e4192f6b7187c0ebd4c43644369e5b441c498da26684f8bc0a0a36efab26bd3897fc824c16cdcced9303d06e2b1db9f4f4e131560f6aee3b60a72ef541f4d6f8afce5ba5f4cc70d477f52c2cae6131ffe7b0c4a1512c2f14fd9e3d86540c885479dfe37f4f860bb89383d7285d4c8e847132d60c75ba30abd7b89449e92b4a7550b71130f4e83b679c86446ede0c458133a88acd91ca4a04b2406ace76a14bddfd87b43061cacd0fc8a5514333368d4048154bc6d48b4818c26556d3099cdc2a55d66d1cf978ef508bc0cf20911f8cbf061230942e131efbf229909fd5bbc28b04d5b50527b9845a62626dc4fa3a6c2a95dc52007b41b8bf10e39a473b8ae3388f8292b4544257bb7a25a041db29802618d161ee70a34ca92d2c367644f91798fa6f755516399664e44131c0afbe96d0cefa30b07cde790837a47db43f12fda7a34edf53645a95a20f50378817951fadd29dbc927f385073a2d70295e3dfd241f52dfad325cecfcb33a70d67a2574d7036d4218e5db9b0ba60c78fd67ce007411debc69de99f729b82b05e7798251632154c750647ccbe9b30b3c4bdcf3358fabbe7e82028db98fed7d0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ba50347189953cce8e7ffe64453013161f98300192dfa94d1fd550d21ddeea01","proof":"b2bf4fe251192febc6fd195bc8e92b7a8d3fc00252906b646dee0a5aae1bb774c03eddfa98c985925bfa6fb09c5d73026ae38d9e5eb8b3c1658c431867b69c0dfcd6fdcc90b97bd974f63fb37c83770f82ace28a1dc28b888a02e089b5ba1c6e2647ec0dbb1eb87f8835ac439628535b6e1d0669b4ab51556e356a510144fd6ffac75e62e456e37c1f2438d443ac997f7879d608bb12e4d94d53a2dcc2a645024595d0f66d65a1a366d9122872b95984cb980f9e8bcec0b6752ea783937a390a12f57be765f3e1c7b00c8a60a36fd12d4ae217525bd844af16d96e92130f560e6a2b057e0b27bfc915de668c89246118e9362c5223c9588cb6352a4b2d15191df050ad2c77ef8b9a23a49747d11f742be1864390685e18d42a247410513d4b6b9493b808c92ba29a4b64325af2f53073873f7c2a3fc22c4f30b54a83ef7f4073526eb16a1fb50a2219c29664ee31077dabefffbe33f8a1373fef68f44951b67fd472e3398f77476adb2290f70ecf72fb1e4cc7158c92c375efa6c01e76eb7e26ec55070bc9265210d69f798f5c4c37c0d5abf2e25665b31b92e897dc56060f770a88690fe6510303d85a3208cb9a7ca637a3ef2bf35b15c5c32eb44b4984972af466b319f1b4428893bece9c651f15d73b671d72c4c37f68c9ce93c2ed219872bcaa8253ce06174b66885b72761e236106d1ba72e385a9994acf2aefb71511190e6e34a9eeb26cb0a91432f2808d66c148c9aab10c9e1b8508a4400d9521d94b90c1926eba2fbd7767a4710386a02ddab9c2627737c9402169b27bb43cad41758e9b20a61f8201480767697f1bc3d5675dcb18437f7920ec2707af1990ef3c59fd353c13c73ded32076bbff616b9d512c1a54a3b5683b771469847c54a356f0e0e1b0c4f198c3a91be087d75ffbdb10212a9088288a00a6756964c0e2d36cb0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c876ed9455624fc037fb41f2a8a22600c73f658c85f5c0231b50ae37070bf16c","proof":"9a694e0e045c308accbd57bd9df1c0c8e8a1818d73a3db5ac41f198bab54cf13e635881564a16911c0245b164034004418b29858194577da300d8c91aefd64107c12b9cb41f33f8544c5618627000793913caed24e5fb03c551d0470fb48fa3878273dccf5e96bdb455dd5109c55c680e9248b22afbe7470bd23cf5cec9d774dfad071d292c9700ea9699ea27d5b1c72ae3182c876f9395b993ae40605b91402ece5a23716359d050fb274ff1f629f720827bcbbfa1cf08636beafada8bf0801c0aef361354fd27c037766d05e9778dc096bc0c31df4f940cba54c81e570290248a7d263b96d89eb8faa228bbcad2323813a9e730b2d5daedf519af0fcd906001238d43f72ad02be92e9a6880d669d5b3dc7795054773147ab7720a72a965c540e4a804e95ecfa480a5726c70e3eb7c0c37126b83fe743e4d1c853adc167be2c7cfa78b5597ce085e9e948c6f6b60c53afcec759a00497cc90705a5d1801d525d094521654bb9cc247cac3d2bcd234bcd98c7a1a75fe64c7cf2c78b4defb7e3554cea13d69563c30792e84fde80143f20464b639b21f0d2c8d85228a7e91f66ae0ab3b64fa9c6f1f37745b163ce9908c5050036044b7aa7fd82ada4a0f869826268622ae4a7c33ee9e52c420dd8a28ff05df5d6acd027f76e9496da886535221a4d2496b4614b7cc6386ba714c9223858fbf985658397ba92b95c521fa30ff38fccc28046a430d628b6ae583fc6050c4ac2784e4e3e6c82ef5608faa23a81e7c569163eb8bd0f7f38c117eb9c9ac8a4bbe1edb934bc765aa63a1e84f7675bd0f84058e16dc3846f0e29a38a5369ee836ed40a63bf8eb50034780ff1ed4373b201f7c0996fd5245b242e0da2bd4d03486fac33b0e4d19fc1b1fe67a9490f7250daca7f8d11c5e0b18c681db604910a4ebb9bae121770610bb2fc10df83617380e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f0af0e686cfd0a306b32ca622430ea943c1e0c54affc34a89e7a41db8d6c9a7d","proof":"2a6f46ad5f3c760523ffc5bed81590c2d998bedfe456165f3f135d2f86219e5fbc7f1d414c6f0c07e766a1b1037fa227229c5f1061d101d3be52a5903935ae0f8cd2c9c65ef2957540e6a2b925cf61bdaa257c6aa74b094efaeffb1670e1cc4004c981f1489986d24df118ff35d07c3887f10d985ba45825dc4fca5fe25e514f1e6949b8b8df9509aba4a2a359f3737b7e8829ea22297991d43ce2499de6fb0bc27fce6ae1d065e07802b1ef189abcc0399ed7c6b668e69489e5dd05fedd1705a4c8a35e6f87c0e41f131257370b5854ede7bfcf1b25c967486bc13156558908fa1d59f2a1029f6af14ce7fcfcaa4f66f947aba1965e3c776695e36085420d679a731466c1c9c00958b8c6cee64473ab06fd30a0bef8bdb6a47d7bc5dbf8f4167ab95b95de69e1bbd9cbfc1e3553e5adc4b01a3d2859dcc97f494fe26467e807661cd77e02455b3fa45dbc89d41374443f7f1e50595c7889ba9b22ba9e2c8c0aae411518254a44208189592e924bac0f43a83c911ede908b03e2a71d1d8b0e1958526686a31dba77d52510387d8b76d1f955af951e02f8efdc2e09848fbac71462c533358c3aadc2b8a338d9cc0a591bf4aebc85aecbd5243e7843a23acd592de241814f26ce1060859e2fa04524514b96a9f90ac04e69cfd0147606bebd8c5f2c00b31c8ac7fe54df9719b2ba4866d719da3e20c02cdfb3e88c550c40f66c707a1a82fc03b19428c5a8bcb911d1d24faeebca73777207f73be8486e03a13615482a765fa2251e4fd3476cccca3d6505f3018112366bbedabb6fb3dfdb5c4d1ae84bd005060e151a60db5d159d698d77fcc954408a510ba6ee04517a11c46939f3e978ab6ce48ff2d192a5c0b743ebb57e2dcd8072fd6deed6c3a85323e21a077acf165a32014023af29e48dfe21fc4774d33517ceb753bb6a7f3ce0e5072608"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5697a069672410f8debd975297d9fdfec958a36f877c89c8f013513e5724886d","proof":"6847ab0762140b60c2bb245064c577152bb6b2c4141f71110ca0d3fa72f9e01afa1ea2e04d63b16d65c6c20e393ab29d275479c02629614506d5af66ce8f601234269c066bdc90a50052f7ab30de7cbb982ba57b643f071eaf33d3c7bd6dee0d26fff70a7a268ae6858f385700dcc21b551368809b9657510574d53cdb20887b4b49cf346f2cc5bfc9ac378a808a5092660052c47d52c2b901d892319aab6807c4301dbee57d17ee1eb20b07af36fae99d9fc8a86c70f5334ba212e9bbe7cd0b220cd5838dd534baf3b4d83fdc4173f611d79dc3dd8ec77608bb68bebe51df0c3827040fb70b1fedc9e6760d3451edf2ccf175628d1f95f5c1fe0a548f9c1b6f508cf2e3c8b0d2b5b1d3fb5e63e2fc5a0fe3db20415148c3189e30caccebfe6684e69844efa19eccbf9ff544deb65cfc5fefaed037a7e9b03920b9a835683367907278c547beb8b2c5a575b8e8f5854731c404c0177e866890b9e3f2861cee486421a15a1df1871750bdea227973e41eb6287640d94bfec4780fee63d6841d39787d01a57e7a7acd5f9f9404b86ee703bd0664dc0b1dc2c7c89435c570b9992fa6c4266aaa510d8238dedbaec8d3ec1d6ea36ac68c1e4a5bcdbc792c8850fe3c2a3997e8f08f9288482464a667dbc4f1f57bbb3a8a1e75169a7519314d612046f42619f21aa74cfb0507a81921509473e25e2d2538ad8670a665bd464d4d4336a698afb97421453c8b9ffc1a283fcd2191dd3cea54bbfa5573cdd7b78000954934c9994c9ca7f746c1de7f2143bc54e3b4d2e5b87608ed7849ca64b299bf0666ba91836cc395ec122db4a6e04848ee917317e1fb153306e20ad539070dd852638709d9ba95c029a5df22437ea622d9d76f1d3c32149b3c05d2350d31d0b01602172c9b651b0c6d62d8fbc212b23950634ddefa7b08f30cd9191f91b4c2336e06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2e928c18e8dd61d049b112c0146d76088d635609360804c55669a7d0cf2f527a","proof":"78943ff3c932a2ec873e098eb7d73b804d23f535d51f9c927e065ef3922e720ad6924a92f2b99578241ae9b95a69dbac911b4374abbf4b1028d570deceb9d813b441f7a6011497942ca2813f8fda24b4a0afeb8ad990fa16295bb863ecb60a2534ac2686e9858e61184eb160b772d0189df58e3b6c25d3220e96bf49f05f4d3c003b3967a784a74a82f882b10f07b0f7a25a8a845aa57c0a9f309cac636e93045d96136b79899be15b5003539458e00fc16523d01540a62fce09be36fb19cf05c5f6834adc511febfc34576552a7659e7573905953ff88e6b78597dbe796c00c5af17616cb288569a8f2c135b5708b26921520019bdd423dad91f549ba02cc3842aa88b90e33445dcfeed0bf80bf336b64b3f9b06a0b4dfb46c41af60d74326eec1cbcac5a943f78e89c6036168acac4b55a81cd2c24d247ce02ede5072a9e524edfeea5499265617c160221fed7a1a771e6ac9c928bda13c8206af5eafdea28fcfc0ce0ffdcdb378e3ee114da01d8b31039618178c7086468a9e6c4663bdb1f7e4d08834493353229508d49d0e76f78665b2091f86414723c16ec3463c8e25292858971b673f7e29f2214782f29acdf6c4e420f4d38c493f54b16c4a097463ea6a6de71b076bf8a5ca72fc30d96d94632205b50b530fc206fbac80082ddfe3b96cbc4468f326adc3d82d772d7666e6ffae845798d40a6d0f1c0f14e1b275e3ff29d762dd3abbd71ada93c741f68bc3908abf5df018218506cce10f9d504aa5d9e62d532b420d079b97d76f70c406842c60680a7db20de34f222073a79a80e6016f3ec18b7681efaedba126d28a135f90321504b1d0d133684f9deae4d45287ae627997b18c03f7a56bfbd69cf42cc9430bf4960ac9d754f78a7ed2ffbc5ea0b37588d0efe2691191438f65bfa2eb874a83cb0ea1cc669b782b769b534c77b06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3c8da79306c15c7b348dd8c89d2cc401843baf8a75c6e521a81b78be1ad3a40e","proof":"fea474f3e1ecef563f669ccfa9c9cef2a3fea71cb952b4d178ad4e33b9b6a37e34c1167436258b47f7909b6d91da7505b8dcc33ffa16060202a00132649deb73d2658005e8ae6cd16ee3c6b2917b5b5773c450a0e335440a4de99681064c5704ecc6972b2661d6b7dff43e4220a96be28f9716ec9b86e2b612cf00b0c7c0881480120341187372a7592b227957656b5e4998a45695c729b114558445ad412708146bb04a5d2e3a6e543a82a7df64ca1086ae21378e3f633120f141cd1a0e3f0a3351c8ab2351afaeb4e288f59ce281178dc049bab805597c8092723ede31cb00f4c164f95b3a86409939e9a751f00d58e656af96c8c6d9f580c92eda661ef75f72c3eb07163db3a351523e8a94e1814c411a167dfbb2350dca4e4f6ad24d0e5bbc2ffaa8bfbd096b782edeab71fb1e72bb3018049deb9f82b16ed8b5ed90e952d2d669f322b8504655174e3bfb2478ed5ada85804fe11c78d82798d4536b6e79f6dc009bc916143612d88be1aa9560cdfb144f0981e5a2555ce3f81c02dd52724cbd4f4a01ef2112d41aa952c34b701d3218210ba35305ef677b349f0dcbb67fb6cf585c76895e9881d78ba9241559da23036c2b95857a162e94efcee2458f131e00076263b49f40382636357d74dbf4ac75f148ddfcc6ea78d6d66f2977377b3e33293cde6813002c718c6241e3e8efd56d230d642bbe7cbea0c1a9316b54766e9a3de70b26d0918eb57d6c7787385958590542e4ab43bf2a431d1207fdd127f60ca83c5861a4d9ad6f8bc06080618ebc3acbe01ccbb9224d066efd9b8dfc73ea10c646662999253d96765c4607fc3cb59a395592df2aa6bbfcb207ac7d3a23a5f8ff4881f70dfa181055124216f704ad6c9bdeb4de9ac8d98a80d6b665c90f9a8a4d6be3324b137c42e02cf79ae15779fa8bd1cbb8ed32e33fe47ef077e00b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0c7faddacc1b0ad8e0e56295fbd5f5dce46fd8059e0630e9bce87c6e37587929","proof":"eacece0fabb4425bdc724d8cc1cc65c1345950b55a35e347755cbc4616d15e6b6640d7f7b6296d3678776495df1bffadf21b9917bdf707b948a4cac6e50ac300341563ebb389028f079055580f707e16da2dfb4f1ea386cf468f63ba49a1b73864cf51b8b53b82bd2511781662071fba52aff6e682048b6821bbf63678e5ce3b11c4daf63080f2aeed7dad8fbce2f1ef92617c747d0b1ad13d3daf05b14a8705ff516138729cb4da19285efcd49fd9c4866e9bcbad2cb442a19c269a829fae0b1a767af6641c9c23f387da05a4bc728bbb92f1ff564fedb5626bf80a6aba1e097890a20e2389e5e7d97626559775b5221a31fcca88f2da8a5075444ce280dc01a459d49241e51a7bebeb3e9c5025b183d4e20f044a3ff6a1b499e9a3bc7d245c16b07f2a48c296e84c286b45bfb56bf7214257eb897d73c5ee3819d06bf52737f0eb78d04ec1484378f9f29c2156653031dcd09c26a2bb3916a024ed7624311d4c0a57f04c62dd6737e308bad769ef39fe5d76edd584920bd7851a082ca83d5e485dff1255ef706a20000ba024c3cf6664e313612a60886c86fa748aea5a7702a065ba122ab3995d76b7609ff179b42c514676affba62c4d289531d559650803f0db16fece6551833ea1ba79a13284789d2b637f9cdf86b527e8a1d41ff6a66a1a5ba4f839cb6c6ea6bd85afd0f6f6c4a3deb041dcc3a0acedd8257eee02bb2b0636ff31abd44a27da306d6632378cec5e682302ee1a3945d58d8e9b8b50bf7c483ad1ecb9de1f6266c91e741d142b2e189b4e610ef66411f4f5f0b0ab2ddd11bc85b59616a1fa944fd882cb247ff4ce12674af936e6955d0718db8edc8e623e3d213ab687df79fd5196c426757d22cf47371317733be677c0342c816a48f702a71b3c663906887d18afdf74e673254649ec27068b5d5af579ea434359b63e08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5266dfe010bd721e3e60c8a4c6337f6ea698197c7f00b72c70296b177f150925","proof":"40e827ce60183924a5946199c4632d55c40962ebaed7fbb1d0cd969d7ae6055c6c89533c893fcf97a1f2c5ce71b3254efbac3edf61eec878418533910a3f586d485c9a8940b48f4a82d9a3ac30f795dcf0c8f1fffe987e65f0c711d9bfdc801282bfbeedcb63785429690e134f398d0396c16ab51b483879f31fb90158d23749812adda34f350cc8fe62fccf05f301992257745133044cd633213ea7a08fc7064b6ad68ba9dcdbcc260450dc8fe6b8da0c0142d51d9b6a52ca9c9b7c97cfbe0dc8f86c9fd89557a4a65673e971c6adc79a377b6f2ecd836623bcf078fefadf042ab8f43a88207cc899e44ec2151c2a4c27c81c9549e15ce861f8a415d7daa32ae013deed15ae520d8c419440d620d4bb275967313bfd9e3d0808768e742d3c6b343057f832f8018200b294a79b1b6b96aa854d76cb1f8e96e0b368674ad0e322387e1cf7eba8b3c806753d1e3161aeba867266125fdd6a0d37e3e1f3a0031a0574b782eafb398edda5c7b613b2948b613b80e778b0515c26b55146cbffedde2b7e0a54642ee7a96f5ea2dd168da80b76381101527b34e80c99dbdded0ffb3850ccc729391b5c02800e077c94f007ef2d6a5999304776a7b2aebd04470ef77d2eaa88406a2e5df4ef3ffe61626939fd3a0b10479c2f3499a2365133f129deba02981c11e77660f25dd8b34bd1cf12fbace2e453bae23ac109108a047f55f7032d1af91e63b2967b0962c0c9ac347cdcd72deec572992ab2534b763eb5756fc324c6fe2e1aca8756fc882a7b39100e6e043e7cfe45acdb0d515b8f5e836665362e6ebe9dc0da5b87c7de0d03dbda2d40e02b12cfe699ad9d3b551739db4292da6a6111d8ab1d0956997e7feec9371b5a5398945013e298874be38e6b904af65407b15ab98af3865f208688775a03fdecf0b665cdb5d252eb0076ce063be8539e03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"429588be6b2fec15c5be6a98aedfaf1ef51df261aacdeaee748588f3a1637812","proof":"8afab84ce5412162c33a2f1717634716177ebc561d07f968a7aac4a6fbfa050b5e6420ef9493a1211f427cd6d8002c1892e2b2f875f9fd61036fdff4fa8714243e74c2322e3c62e46eb862be6bc161fa469363f7054581eaf04e5354440534307aaa9886062fc8fa556cd0d20741d133e5f8aedacc57983358a09afa358dfe25bcfff9d091557c637abde7f040de1e19832fd8e8c81bf302991f84e405ac0f0ca34484bfa4993c4b05b94c8ccea55f75eb77f6d4dd0384a0a3a3f7abd942920b90924abb8334b65409c5e5ac38518b54a797b3531c68cac3c9b116e597f6430edc8321385167b3daa971dfcf61b3b7d7e989d862218237dc94247a09cd5655566efc3a42d6a95aba214e5622a00686d678df8149611bdc8d469b47c1ef987e7d7281dd0298a02f1e0d53d563d2683e5269feb5a46d9ebd624fb0ef931133ca29185398dfd93fca93e061159cf87431e3c3c0a4a46f03fd36d60cf55f8769e6344ae4f48d4900b18d18e72425f151c75694e98edb1c9bbdcd3f840dc411747361eedea7b16bde74f4ec98926ca9ee403a3fef075cfa6b7c93ead5121436152a47be78e3c31c01beff2632eff584fa549e0706e697704db6e34f5c9c6ed27d6a4cb69fb8554c43665c8043e39a4cfd9d0949ac2eab539716736c1ca0ce1837a10184dd7d7d179dc5fedc700c0cd22751999adb934fc9c7fde54db8d476e4a9421a98f38b08da3e1e19904e0b93ac60fb651c1d8fe33b96748699f3c7d85125127feecbecd9a76205557750a4995122f16f0941e392906671f706d44989b340786228854ed120e5a4da593846c60192b41eb3f0e50baf444a44869a6581c972f0096ba2e95e4e006d9356c7d4808e269acf6cad19148d91628a480580b39273890c0cd924493abc000a964bd8839fc33b0a390a9497d11fbce383ac7e09f57c6003"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5265bd3f4beff7267332110a92c89d5376c19b2557a6e9032b20b1f6e14d293e","proof":"7a4e3ad2a504b5c43feff7736bf1d819775d6fe05a73f33d1235f4dd7fee206cf8911ad9a06293a180fe4d2ad9afb20e6831e8f4de6d5df98ee62877146a4939be3b4e4ba24598d3a4ac4c992ac2f35c8eb195ff8e9a29170ee9f15a1112a661107501de2a3e3e4f82f9bbbbb5d1aa36651711a30a6267e0054ea9ba0037ef21237170523959e7ce6412b0e187dcec5bf0d418ab37c158dcaa9f40314e582c005c3b0d51be75b9fca2a28b329c182e897a34357576048f51c52db49184d1060371b21473ad630f847d430867af574e4a9acdf5fd4bf89a1b4d4d1efd6d79810374b4985132fab27d49f2fe277250097d292e63ae57a18ddc73d7b9168f2a3055cc26f33c7f1c8ad52969f798cb811c5291924e26bf6dd4e3020f7f41708a747ee036b83df8148d05d304210fffd89d1033892b94ff3a566379b1f2420c1e6c058c7086b8e82986c2b7f65c529b15c3db6d17ba6e42c210304ca816650db61b0f94594546e8da04c7390eb9b6572e5d27101339ebb5b3527b2f27abdb9abe9b144eab3c7d9f80dcb8f90ccc55b66a47470525d4ee09497275f3eef36086e5b3675021a16a69a43a9fb9300766820cc3e0c1a505ac9c4351f47c5ab0ee5eb69a23befd76190e66545ad2c15fec1eb19366a11035a8a00d80a47734299dcd152175820733bff315914dff3fc43b4521b9af705d87caa683812418be9a5bcd243374f6d53c79aa56f8c127566a7c15aed65476a763d370a36246de64b1648df74324103724dae49eabf543e9cabb3f8093f0f855fc7340c9ed601c4a4b399d70bd430ca7d0c86e1caea8e0a2847c252f4e696db5f0a162205c88db0cc2333e4a30329fc7974a88581dcf2bd963778845c207fa62c3e7906578cb1ba1a2717b27e40df7d9ae80b680f133e786134e134ac0e55f72a500a0b680af0e701aca1c510d00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"84893d52548769ffabd3a0c2042432f5f4df92b37e77e0e53fc3b5f6e0375b6b","proof":"acf7db32d50956e576f13a5bb5d74cc2eedcffa5d284696856de078aa9b34609ecf9c40ce21684ad0e0b8b985aae8be573c33364a8cfe8b70b9009362f2c286d14c332b5e8533bb887f36a336b83e4562299066ed221f5c0ac90490d7b4ea639e2a83a08bc79659c4839f63a8acd5b0bfc4be6ce0494bc976fcb486a6b472670b40cbde634a220c173bb66b0fa547d0f2ac361973424b8c1b6add87501b93f00a1ecd8244c641c1df411db41229ef67d861a2334a277245d95f9daadf365090110f00b942aa5a9df4e7e7871845c864b8ce488be85211aa42e68e93969c0630568bc3cd7d9ca213e026096fead512f031687b01a3e9e99687a6a507fc7ba3c451c86e0805213eafab8f0ae6baf59614933852dec09e1dd82db6e06a10f286d05f8aa5668e088bb0612d3b0da394aef935ececc73e73302d3cf3c552bcd953b7224e8dd00f8210ae34888b3bd84403b25f7153822f0cba17b32f0774157b6a16e96745d4e5c050aaa99b1443a275e36e88f0341bab125269c67aa8808b7ee4e3a2a5f5ad307423fe5a457540705e29ef8bdc2de7104f02a39f1343f14df57904c3255b0dc8cd8fbf16edde9e166a7ded307658b551927a06177fdbc3982cd336196e75be9beaf4ab3fded8c6b30c9becfd69c8d0797a1996a2e70cd738d435b7d86f6e680e797a1833a40c250c73bb48e6f5acbfd7443531156cf7fd5793f736eda780a08498a4ca8f0fe14e1c6717a7c7f0cf4fafe7196609ca4bd7c05e61e1c723eb6e094c46d328912852fa9400bb815eba531848d5b28bcd91c0ecd008f7360e48b9b4fb863a51cfb32ad3b355d22c4a7c6bfe82f5df32728fe17c9caba45e97817de61d4a9e9d6357cd2f4764c538716614c4ab32072d6102f5f76af510ec272ae4efa45ab4ffabc66f75cf8ae1228a38c53d3fbb0a0c5ea751040371304"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9ae3259da69065a4fcdbf7c65ad8ce45c9838c26194aa4596bb0a2d36bcdd206","proof":"6072c7987f022d89489f52077440d8aed0e6144fcb7e4782e469e039422ad32b087487d9c7b7046b94a79533d04f0b59d0c30dfab38af729efe0f33710f93a3916adec20e01b1c5f1312efcab91b7853030dadbdbb35c521a7a65d3c86bcd13c92e0964a9e276361f3c8a9c5fc42c5855dec0ed3a3585efcf7b18700c918e97623ea6d82e907f73a2dc7fbc220a68faba10b86d9312f62f63b46f86de39e8b0bc363a45ef9a2e70a46ecc7e300c84dd8ac204a735eed1075a44f32253b9d530ecf84a22c0ed872708baa98e785197d9e610e9e6bab796830325ceb4d5b7cbc09a89ab3189f78641fbfee4ea5b5423dca4f5781a418ed6a1b9e2455554502e647dc6cffd3cfa3ae798a41f0c6359f06b2359fc058b9d690b63ac3765fb46e7a1b8afc102d55d49ae813cb652ba2a21457a823fa1838d26d356f7a2792aa898573babb5313cc1858b73cfad10b39946ae1c2bd2a7ddcd214f9f681280f3e4f145de6fb4fa7bc2df38d542d80faaae2007665adc734ffa5cc26c1cebe6f80eeea5e9a979817d041fd729e18268bece131c037e6e948d0a131b5c88df6c932c8f2177a201c62f656467c28089e3c945498bb3f463c9fd23308fa343b835d98d83d583e3496d81ea4b389f7c8aff8581ed099eff1abc8ea38f2bd21ac71a54c983e614a080382a7fc398644d9bebb6a2519706a923459790f1263ca929edcf7c37826ea1ac7769a53b457b579ba27626ad12d9a538676e18950c2ec54ea43f641332cf81fcdae1734671cbfe0ce13b5a5aec8e5bac0914bdd6fb0cb647907e79ab243382ebdb074a96ad8dc6feae3422264392b4689f1805dbbbec01ec0ffcfc9fc14fa564acfc85142c431faabc8f74a5dc1f84ecf7a658dba72f628f0527ae8ea0f8d4515ef824104478f341f27c8a37362de69abcebc0030c15184c85bd5e33503"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c64d16507e5bb0fd746c9387644b3087f6accfa18c8cff411274c4785689c472","proof":"c892c06c43fe1c0a68aea5b51e040f57525592cee884d1b716ea768e37b29463927e66bd54bd63dd54560e18ed8aa292ef72a1d33f6fa5f29d89118b69e1c21c5040f3930d6092b47c32b0e5ce1d7620f65f2a6bdf1640a65dc5c2a3ea3f291f18420e9c698ef01cda690a4768fcae81ddf3dff54ee914e57509c7f1ef00cc58bff18734a468b8cba59c5cac3203a5bb702b1ab7ede8ebc5aad851e87d4afa01d4a69c6d296ca7052ea749fef8c0abca4c92b89ab7d1649c8a966826b4520a08e4d6accbe9f7e59495410f847d367926ee483d6b853d9c1dd9a36e3f316c21072857634af4c9c7e882e75ec8c0b8369b605bbb61cfc4b301a9c79f73efb068788a5c719026b0ab004bf904ddbb3b11c01c4c2a49e9a163e8baa08e661bdf937042336a5589764e779780fdaa6383018c4c2f8dc1c0e20b8b99e437e499344c2554b5df5b3ee270092abb3c95b70a754a295a51f737d04ce9ebc6537ffa50e91b442250bd36222506e381d4dde7787c50194f16ce57cd35166dce5032288df730de9991d27003d9ae5dfc76e969e664ea09bc4469e283f5e7fbfdfe88ed971e2122afc24d79282643264110e101fbf321cc153b250fc0e6ed49fd1086bb4f5a78588a3f269df862c874c9b7928267da9d738ce8d4905c2f14aa0569b4a1758c16eaa4706ba2f3daeafb3d492db720815f7a5c3bd996f72020ca6a6e2e4f824706164273c2b3c3671a01ba1b2a5edea58c13f0cd21c26a2a5d1def25357de2b204863b9a6dbd2cf450df9666fe6193b18618b0b12c8ed115b809fb17edd4b0624a28ce02f5d4a8a229c39e7155b5a64ec4d8bf45bb5f86ea0c9db443958648df27a8f86bd6eb37764eb9719906260797a2805cd0335fa94dc78d82fd3f4dd7380206aee5b7a254df3aa82651a5ae0fe0a72c058c2b2b4fbed44484caaf9ed59206"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7282df36a0a07d44164f656d16d837e6bd7dcb1f14d429fecb8bb9b1e91d1229","proof":"1e8326f1eaf3dcb299335a9d7405023d6132a5d2e69bea5f722ec7d8d78dc865fc995891a3846f2dd02c160305504aa504f615b5d2a904fd85a77826ef388723621d25450081b03519371b3b5c15038cbd86fc67e2aa2ac47b88606cc63f5c76f20b5651f3e07441b6d2769e94204275c9db6e540ceff81dd7f41e511c679668441c0b1ff1c44b1164751dffc353cdb7cb5b40d01262bb306798dcb79dcd490f189c0770c1853c5be6d981132f093f7f10a2e5ed04cb703a3a1c6fc5fd943403d3b29e6e6ecbe31b18745f9e8c44473e0a4a40468ec6a98df5b7a13624787b071e247b55002468ff256b95bfe8057e17b2a208d6e1d5b69b587629fd49e689387a00ebb66c3bb047ad06c7db0917bbe4b975a8509b89a770a7cbd927b224cc289a4a67fda1c5cdc486fc5675547a2ba47ff271ddd22596e8813ce1307a981963a27f93b65d1593eb0891bfa51e7f5136a8e71e4769d9a01b03bf9c35cfe75d08d254016c106552bdfda95483fa4fa759959929d52a29141a7d87457fdd95e7256ecebf543b6cd1af1bb3f8512b45878a1135aeebe3ab1d3bdc848e71e5eec66da026a22f30e27fee3e46e9e48a8600f7b32d855048b048512af6ddb941966e43d42025a18d6a579f5ac5228ff587d0e9388ce1a8aae45335efae2a2e9c5f7b2d4ceb5a3bf53d2806a95599e8d396eb926d5a4e4bd8c8bf633d667af7cf33263996999ce974cd53073d59e8ad5cbdcb8f398ece68ae324f40cca70a6304dbc84dca45d9174a5b9d81924b12dec0ae77491fb8e736e733e795ebba660cdb8a4e136e911cc6ffb1e5aec0250f4ecbe98776b73a81638ccee4b5da0a126fa2f42f6a6629e7d12d46e27af656141a752d8143dca85b2e9b7922c82eb0654c4b1a580a702b53fb5e750bc50354f20fb8dfa1944625c75b12a9d4840fe9037de5889803"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e8ea4b289d22a0aada82ca6f36f0cabed3aaa790aa57edcebf424027122ee26d","proof":"38d44c9fdabb9634ed0c45c8ef47a7fc20a689638ae1f477cbe958d6debcfc29e09186fc90aeea5c2c458445a5b245641226fa39a1fc58e72cdad6b12cb6fa10a8e181b9426660bdd0a0b26bf1d8f4baba11ef6f17cd80925fc47fbe3f74f5449ae887b69f6197e6c819ae02c8e615e86169b593073d90c89d1423cc0d5a623e9f03f4f77e9d8c6a59471d1cf9a0b03544ba089fa4796ce4f9998e6f4e84ec09618e5a735f8c3b2ac51ec31106b433462ec57ad03b2bfb924eee314fcb6df30c1e651b8d564ce81d5438924d318b89efd839ed2d1724616355744ed57d9812008e7bf7ec7f0cb7cbac370b68adcd6944a93fe8d5dcfebeb6d0b5cf1e36b59b14e2738c0b29ba64ce9989a3e26d1e6c47aab7abcc3092067424f277bbbdc2ff52b007acc337b969409eac431c0ec5d7d599923501e113726ffd266c3c51912465fe1023e1b741abf2cc69aaa2abde4c253bc3dac0d533c6f30d732162a38dc333045f7277716a60d3ce055d691111d4193cb6c08ce5c14f57b7617707689bea2ec608d96a5b8db4d7aeea32bf9e570b2557aa6760ae5dcb36fe83aa52257e225b3cd8e0ebb60494e0088be1ce53baf3ef4f04b011fd9db4be9b517ccc5199a66fd8eb1213198da3f1e8fd42115e6a17683f108e0ce1b371a072cd239b7c1b8f0d2a9ba5380d87ad2c90091b66019aa4752aaf661473320ecd2a4a6694e800822d4c6752eedfb9ec09d46835f6801f4ea1f64a420075380cdd581e1edd40a680124664c72acf48d3e61bb886e0df58a9a7f2e44381fbb2ff090df52a2e56388254a648e5d2e90ae5181c0b9fc754dbf37b3598458e4df2e4123abd2db9af4c4e3758fee886dcc10b5e1c8310bb681d217cd52e7525a6addd44f71df7e9b4bae40112fe61ea98006c4879a5f518aa43bdce1831fdb190dd71adf7acf537f7938100"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e218992292526276b494b439f07b611dc6d92d4efbe38a82f3791f9e66bfc213","proof":"4cbfbbd8e3c62754a961cb21d9d5531fcadb9197c4cd0fc305b0417e50df986190339e070edf258754195bcc33f7d83ec8a9a17f835db477b3c5d74782520770425cd1206cf97804f58db83fc638c9ccefcd1ca0585766842f5fda3d86f15957b0e4cee6d19fc375cd75be63cb32eb657569bdfa60f151c6c2fbcd48ed83642a1312f8242b22959ff9fa8f6568730f70dbb1f304ba25b34b0d25d68370ba2b0a72f2b35a81cd1c19f8ec4f9b23d5e28756db47ae7eb5fe267653ce96c3d97502523f5a2cc58ff541cb7daaf90d9a25e65e91e9a7644f2cb4158dd11afdc3690e56e2466a9e99a9458ee0037ca7329aee543c12d5f2e75e515e5270e81c6a6d3bf084bd1ab50e6bffdf86140f53e1d1872aee5e7661c90b05669052e95544a46e7675c424c876256dd0e746e5975a8b9afe8400a49cee8bea812df9cecfd5851a02ea02216fd20349c001b5ff367337740c66b3944f2cc8d9ec666cc22e5e5d5604c4a5d6552ea33be04e3215289d8976bf13d89e6e0e47084d62fcf0ea1c09572249eab3a24e3e8f309ff8a1542579448cccccfffa66562abe2c733e23670f639a598a6b4f16cbad94b9e6e3d0fa3be607e44063b2b482c675cbba2e3bb07a41cce21dfb5d976289179743a5493a3ea96e17d9b69615405082d19cc7222aec21eec9abeb9ca1e44c003687b9019e07ce6485132f81e190b02d8ba09cb856e80d7c6909e759c61d706c767e873b8138b001add5b6f79be83bc4ac6a7f3d0ed06e4c56922acb20a45a01df8a24e956b0143087ad175aa566cf45bdff9d189d2f4f28f5ddccc5f1aa964ed6f11d16227822ece601a0ff37036269be16a2a485314646ab4538ea619bd44fc3dd4f718dcd84224886e15a808561173df28fc9d4370a904c2f22c0fe2d80441865d48bd7d024651e65f4d96ff000744f77a619298507"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0cf631446d92a2552bdc9e90c6bcdb039eb6ec28bf108a8d1676e9aa799e184b","proof":"80a7113775031a7551f7ad23cd2ea9043ee88c246908ed546605bf19d51dfc79ea4825c35304c3db98dd3fbe71b1f424fe950272c5d25592577127090aedb30bb47dd3840f6871bd8acf8873d6a2b3e634aba6e2e3ae9126a7143431a7812a650015fe623bdb3443ab42b9cac2b57a3adc9e285ddf566854a282dee343a38a45fec068f0f910b5c82d917d3e01b4c07adc364481c6117b62d8d00566b4c9cb0d81dfeff68babd287ca597337c37fdba940b0cdb147e29155eae5d6adc3eb2f0573d6b357bfaea15407f11436bf3bfaabe1c5722bd18b880e1551e17af5158703364a4acf7fc2e2586f1b345b288620564f49299451f3e0a25ea466137f0e3045fadb198712e19618a2ede5076805c922ce34093fa5d7f7c4120ea918665de37ed8336fab94d2e4e281f3b7235d2775d8e52c7926ef6439e5765cc764d247fe1992864899c36fd6273d8a428e963c2e7e3ca07da3c715aa2808ec1f401e25a1558afc24ad3807fd58505711623a241627519c38dc6270a74ba49274b50c08d668b22569bcd59c945b1ddd5577854ea96f993eb123ea319ede6f6cbbd949e08979ac652f13c951463e9aa62edd27144a74dfd798c2c5258b9700ce8b2ced54b269148e529c6fa6cb60070f1213e74892531b19c58f47f2bf19056580e694aa9109c888351ab9c5477c8467bfc525256481dd6939780e4773c18029040e8a0f896adeb544e36c7116870bc684f9d95fc1b584ff945f987ee32e4722dd0c2d63cf3a2ef98d65b9fc1d8566b05285039540033e12e96d326e7c0c1a5e98dead191652e04ca1c6b864582006556bf36ec9d2b2fd1ab0e61c0f61b02ab2e8cf0997d1710c6a4fc3221cba0e0fb8399d764f2238c1ea0719c1b5675890388317ee153b0b5afac706f23dd2b721863c6a2322ceb7d089489ffdc3551ea3affe12789db407"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2c7cb27c1e469090888b903454158b8c48893c1b06ec05c2e9da6621acc27f05","proof":"d86a76437f1f1a944f558fc107dc2fe97ab6f52b1ee8800edef12122560d595f0cde4298b86c1d66ed18658d87ce39e69bb79a6d740f5947be081686689ebc63341dde74c2c91ba2507ffa3760260fa7b8a531853c2a1cfc45d2fb8abe85563688bf52d4a3652ac1c4ed95189cc4cc9bfdbdead483c2f9f3b4045bb4cd5bc770739a4897e703e34a3015e6cf110d5552fc5a93d1e3f819ab3f9a2c08c993d80ccd4a543bd3cf3940c63eda00babf74444430d2667230bf85134cf119abddd902e57876ece8086de2bc7ccc269287eccd68badd004fa63f59747875e1f142a40b401811c55e469e7f0506694ea70af10634e8e2b67b873a8d313373d79605bd31cc379688a3b6f3e84ade47e5ddcaa28f69598ca5cfc94f3210a55b9863e72f3ffca7f368b3531bbf0290efb44f790b875da65defca7f0d397cb3ccc1600ee3715a1a6bd51242259a578b2e1381d00722569cad9b416e06a20b678a3f92cac2035cb7c45fa8018eb1d10d93f77e27be271b4a70f3dda78920628826909971fa13787e56ff8c285a1a381043e96f2e6928aa52bc37f8a35afe0b675a6005a2af055e36a6819b48d7cc2e3dd36281f8f6f39910a5ed67089bddebe02c13bb5ad34daa499e15a31fde490be8e303465ccd8a326778be4b09915b46d796010012a30ce2d421de3ac0ca0178914d42db28900fd50b8d529f41fe5cd159df4fc2293d09867b9ed900b16197208757587682d690fea6027c709843e95542705d1ba29718b0e7afc10a07d2e3858bcd396c9f58c9524c0dd92ca1bba8a5383fd8bd1e2f68847b7011ad8107ef59a8f7e31ca385a6d04a563d5c8a6e8469d957efcab13b03d3cdc062a48c2e325c6dc3f58500126fe787f09a21e43b9988a0411e86056409fcbd37442981374ac325094bb33d010e152fc06aaba881794edf2897dca68d0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a2b5185829e86f1441a3c716da46a6055ba4dd6b989ade9096551fd5a48fa50e","proof":"aa4a2f804d935da5f0bfb1c03c2a79c3374d8e0ded35ef21a4178fe008b4ea1580ad65edbd733159ad721c3831151ece0a4a8206241b8c4235d03e0d3808ad2e08ead9db796942f368257a66819f43c33b72b0f3250ed142e92a51406182e61b0459e4e9de2e230475f297aef6f9c2bb852f6559ee9d9d10deac41e4598bf25304cf9305e3048a2515013396f5995ef0ec76ad9f379546ba6a8bd025b9303e07bfd579a3fd5666aad2af569e02758e39d3123e288a8f7ebc92541a6138566000f25f2b7208786dd39e58ec4b49c7140d1a812089997d82a903dc417d2fb02d0e361c3d76d4985889e16a47dc39e66bbd1388bbcc18b4d695c367232fbcea150d80d9a1f0fd3384040ae1a6d941f0e88a11035306f2307e675015137cea416c74fad9b578723204ec1030768374a1a083a0dda53670ca0d0c1c5930a716e047709c980fc317d50fbef3ee72e0a53304664687962430a43e54d762ab5db6f24e2eccbf4a562ed724d4d88f00833fbc2f6c296b25a791c4c1a7ee2bdabebb3f5419426af31d4430cf7a8ea676af8fe812cbffc7d50860c048ff130a6d6be91d5d5806458ebaaf2a474ff2c486a308c0486b4a617f04302ca264a954f693543f1303bc14f865faa86abb273ae11136f514cf215e1e17ab246497f4bed38419398f352af3776ae0dd0cc03bdc54e779da46f23d019e8f2625fd3a1a36a55cc6208413e48aee3452888c3f560d77bef91f284b6a2c10e29f1e33335405e61412597b0656ecb47d6abc855cc9531e8fd1a091e13b8dca4b6e528879efde54ffce82f111d853ed804e4ac3fd4b56b6071051ff5afcc0e01e79558d9138d47eb3bfc35d69a402eb43e0e190c51a8a2e006322c2147f6755ab9c075ef8c4a5cf3d102506061101dd7d1d0bb6585e2cdf350465a326b3988d17dc3f4176e5b4334158ec6c0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ce1d3f776a8034d6b40a61254715f6762e6a25f7bd03e08a519d77391dd6b66b","proof":"601a47740f6719791f7641bf59f208f4840c5cbd464a9446b01ab6373ea2ea0f747da2d6f736c260fe27171addc1674d0f077ebc80c77dd1dfbc12075703287750e43f70627f22a43399ef3256c1e6334e9eefa10681d370e36fb9227ab1882ec274ef23b5f041ec837a5aa0be4b6336d92385d9eacad3ba8020ab4dd806a028957dcae4ed3894c4e344dd3d1aaa28257bb570778a1caf12db4cd46b43f7ce0f44dd194910eb6383eca8ecae64d9d460e04e9e7382d85d2aea4a15e47d083505910f4ca37e30b6af1bb818da9ecc3663002b382feb00556f1ef55fbfb212bf0a2cc99a866f9cf15c313aeeddff35119bc2002647e600e06ccbaed3462cf0ad3b78ebb11d96c7dc2b6ee8f7cb5d12f4a1bcaf50afb0644f4254f83e19e5e42273e451da46923630cf255ff8f949216728defce5b03068690b5a09c5433991423686e9ae1129b3b9a3f9bbc19fc6c7bac3647864abfa54170c71d519a74a43a606aa3cbcd8d5b9f95d76a063dc77a3fcfb3ec7e0ced5fe8b3febe987dab001652ba85820823dd7b30d103af2cc0376634bbedbc505d963c24b58b006ffd0a37535a4ee9d753e3206c540d02079cfefa3298f36eda92843c5bcebfc70545c7ed629283f8f46f7db40cd97fb655ba261d636207b89590aeea4e29b67f4ce162cce18b8a7cbfc7ce19df10cf0eaefbb9001afdcf97598d0f96607b1732748f042fc704edd328594b91eaf512ca87771ff666eede7378ce39beb8a189772e19bf5630f903794dcd250ae9764e951ed5e6bf878cd046a5cd9f3dbbe5f7635e62af9a36b5e520e611cf09f5f47dfba56db9a883c2d1e207e0f9db7a9b18dae5af63d9073478a6d6eb7e15ea9e8c528259e5c17beedb7dc574e98136fabee94a0103f9b0fc095ea84baa94c680234acd928dc3bd110a5a18b13f5414b0ed0b97456a36800"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a22b43accf2c7be438fadc1e4a35e7358146493b90478cfefc3b21129642831e","proof":"00c848754c9bcbfb8fd8bcf085170f2ea348d79a0a6969fae3e8dc41df100b7ab0cc732d0df83de289774538c9ae1df008a44cc5da77f9129628e6d194d52718dc0d8a88000a9a294cbfa8de09aa27d47ec9833c158bb50ed3d26c73ec54361248abdb669b0db6dc7c3007a94422169a6927988c8ee4ec17abcce68bb565fa08d80c2d0b2a72286779f4e8ec96bbb656f373d03d100761992aaca39fae8a140c61ac4644885e181b7b9ecb2e6c664cd070c5946a44355fe4a82899b55b302a052a82cfd09136c94df9d552a1a1b37c9cde8178314dd0efcf8336daeb80ece201d88f9fa63a3d508a3423429dbced14e023714b9b40fad5771c2da46cda70295618d181b589c9c00a6f130a7f4a4d93f6baad9b9e3da4d9e85d480e00a79a9715a66de533fde8d88a34f3604e88e7d56b8ba8769567935dff981c15c14238da7fd8322e82837c3d69bbc327d705565032f0a0441d01fdd47b8449462393b0181e089ed05afd8eb22a85ed2b8f9654b5c9e79028d4b51945b01211e0ed11c49f7262f33dded3d247eafc9a3d0814770b1a9cc9a965c9b6a764b637507aa9cb4c14e63d19d86291a13e24d4c345294d89a61710580177adb9db7cad8fa276e02b6d9c22a146aa1ccb7023ee262b2ea23b639e408c90f2c670a207b874cff40774376a6d2099f665a2236152137ea23fccdeb911a651a8a748256e3631a4ce34a647e2f777d4e57ef8e652f805b06c41df1830bfecf264505b1534077423376e8f077cfb6481884462d5bc07b0992fa178704aef9e7edb2cc41c3ee6bfe223ebdd02fcc8253f899f6cf6e3ad085adca938b690ed8578e0778ea679acdbaa266e5c07c1ed0d2958514140a2dac3efd269cba5935be57d04978fdfbf2939435e3f910fc7c992130efa297c06ebbe1a07baf97352eb575a173992aaa11a3ded0471fe0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"00b9682febcdec63ab3978d27fff3d10b3004e5c4d6a4d43b5e6f1db95aa0a69","proof":"18a5e2481c2a889a3ff9fa6b0cea01d2c6074e19e6945fb57a2e646f23d25e0da05a69e6391953ced8f16e9acf924c302a0c403deb823d82c8bdc61d168d083e92db9c40b06b1192678f45f4399adf9720e04e937a290cc1dc203d7a1950a22ebcee66fd68d97e72c848cb2cbb7334bacca057b61adf099d013356d9a3af792d633d3b32009513fcd9146a265dc3e5c644ccc65807a5efd6c9673afdf2507d0b7850dad9966d9bcf8b521b71ed306381c70c022e116970b61e714ca0faa27d0e10a8be1ec093f90fd30062ae5e2b40c2dc696ce4f647ba656c1447eb4e7d8205b457f993c9d009cd1fbe003348b5936234cca2adf526e521913a896bc589e55946dabade9a34fd77c86a13cd11b4f4b5591a4f423f01d4d7a34f98aca55b3133a8df19d2dca5f37a9f9d876a1312d8b0794ba445fe9d15656b5d607de1819e2c7ca43e5f6433d287c2f9d00e2aa44bd550a488e701d5cc7179b8695b9b07fb78b2a2ddb0a7395c26d7e53e3cde62915a7756f632d42fbab80fccb16102c8be4a18f7bb50d7d00260fa2de1ec698386617afbb3919ae28913d9966505597c5116045eac17dba5474e53b8feec335a751a04efc42884282bb7b92bde8073ad965cacbf4202e23e286003aadc8bbea8f5a5e848271d2a0bc0d686ba231042595e1e1ec01d1f1037d5ed349734f3d0000d58c7f0564ad86fa32acffbcce1e9daed136af7e85daa1446da5794593cad959dbe3ad1a4a2dab9c7a8017a476c9d7dab1a84559b2ea3821bf5f11b2ea2f11d04a8ab1ef2b9aaff3af4ddbeec9deb616c2632f4d21d612c18f86fc95154b4759dfe4b8344b51c79b5413e117e14516b110cff8d980580475df1c168e07e44d658d7157546201d7e179893f58673640e3d0f8373d60a692c5ef88297f043f0803506c36e7bcfa95832e6b2dfffac48761305"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1037c3fc8d9c39955cd1637cb95f780c6e8c33f88dbc870ba13f2d3902690f64","proof":"6e57ed14f228143728e791f1821fba8a145359b90050488466f5ecd9b88e322eaec1aa035ac0c1667bb48ada88ecc1567e1a653133bf9255641a4f0040e236121c3eba5440cacb37f3b9a5d3c8c2f3577b6e7235387bff31974b7b593fdc674c5a9884cb816dde49b4783f968a53d95f12cbc3563114cc13606ea4de35982446274fac8353323ecc6e1eb4b838695dbcaed9f45f97543598e85470bfc955e105dea9b8d442bd4a8e2d46882ddd1c7ab921228b7da3145ce677a2efba68d20e02b8fdbb9189c386ed00f1ca91f8180d9dd1b3c31b3d8d1cebbfba03aeaa6c410ad070381e22f5043229dfef499ab03b117577214bdcb7c6d58200344f6ea45b1d38d9e04b623ff6ba42a6f350b7f39861042661cb86242f9332ff1288014e05003ab4423034fc06c0d9a04f9301dcdc542bef85abb326d02198bf27c7a6500f47ac37471b30c716dbaf1eea66b0dd272a63d8c8a90642e77783714154c75dd83a7c249eed89237fb45c5b0d0e6cb61f6fcc8d1651467aca04fb5c303549a8ee711e3f3699e1e3935045a77b136687cc7d02f9b3fb6671f13eb7c6a5161bba704ed6f348c02035f90e65041fafb149da52decc84dd8aa2d44fb14d3c8c76c2ae453c01cb990909f13116b8d86898253536c6c3d5723831ae324913701974071d42380ed1fd5bb4c02ff25d40a7f8c4336a7ff3333d87fc8974949d716b2655eb4e6c5cb4070071c2dd9b23370ff19c1b61941d124e88ad368ebd80bbaa74c5fd59bcb352967fbab6ff7e338e32b885d8731b12abe4bf31f5e059b63493dfe59c6046c16f4466d9588b472d5a80b9fb943ed6e6848893f399caf99918cea43998714becf850115957cc144cf4736f373c066b25672227feaaab7ca64a8f157ef901eb1bf706de822a57442dcdcced39db34646e471eb6a6cf94650aaf38d2811606"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"10ad47490c303c5a5eef8c8b9329d93a2bcb22be841ccb42c0502e4f7a1aeb17","proof":"d0aed55ab0c0174ff14e483041d5db35008d69bf1b1bf404b3bc6f3dc8247b268a84d56faddfa411b7f5394ddab0bdc67f84acd3c55830a6c646c9d6daf24243e89db2750c52846aaed84c41ae40ee656d467e46185b3f2cc4dc722a69eebb243eaeb0a3ed7d1184b1b4515e5b31fa04852045cbb6595f1835cdf9952267844112c598ba2c758b3bdf6dcb21b1abc271e02f19831941ab69c839f0981e0abd0f0783c5b11ef3eb6f1f09050b32487bdfb7c9e11b47ee2d01b67efdb2af7044097b2629c1be390814c54c2ff2d0612d10a22d4f5475576c6942b1c59442bcc801e83b9bb6762d471847c559a63a5387c9e3a38c63aa35bb165269ff9ba77d2242c6bb8bee1c657345cb48b6dddeb7ad149d3d196e9590576efc1c1b6ec586a26092c30f806382f79b2f191137ac4c246d2b81966fc98c725361ca91aba390827ca8d4ab362d104aabe9d5d763a12f3d64c6f1fe3955e340c2f54f3994378be84a18c5d2c56fdd8254526611ddefc066116bde7523f9a8565ab51b4ca9a2eef4215e302484a70ddc04022387cde9d9c5618355cb306ef3c9138a063b3f19021d3426d0c6ff9f32aa78c92e357e412edafc97f133983ad027926ab93c2015bd4e093ec6b8c1f7509be6042936da6630dbd53a0a756a154e6b6d37f87ef97fa15d1f489d4552d1759b795732807c365ec0ab887d2e8cbbfefe4782a5429b1acfa5604a3c36cb7cd3070446b38f16c30900abe2f9b1ebe49e0351d6fc8bf1d93f694e6066e634d3a65a88c2d58e9a0e21475d971eb9fefa69c7a02a387ba8d6e0dc11be0bb838e8f0e6320d6563ccb172eaf6d30b0eac4df3677c9f63dfe4a84d363c2ed691de4ff304d664ea03b29b137ac382327120459c41739c6f0d98eb6c47066d8c7046a759929fc87d123a3ace5855e578b180ddcaa10b25d79bf443862b0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"90a3d799d213dc54ae5e9ca5602ba2fa6df08bc097014c03d3fbb53e380b7a1f","proof":"185fa0eb414e9c47f00094000bdd5083f49391f53db6abc98049b77ecea62c1e4252d78002f88168f9ec64afd4702681ff7d739cc7a5a8f69f4215cf58192e0d92f5fe1836b321b1f1bfe73de38fdfd012b11934c3623fc7bd66baec3a855c40644e1827f9b59b5f3165ec0cd1c73714861787a940624eeed808dc0544dddd5bb48adcabe8097e9f4cf17f4dd6f4ed66d9fb3f89701449fd3b5c2525797c0b0432ca21b0ee0fe8808ce1a032f120525ec124755ed3fcd9287e7a3327dfeeb60b872e01176bc2b58a08e1c0fb5d93e83523e8746863d8a39bb008b269f1efc70bccb732e680e2e49f02e731dbbf5b99830b820cb5eb0620009b1763f8fb60745d44865ecad9346fad07a70f4abace0c4b55c09f72d4571d3ccc2e3dd29dc3c43cacf25572a3e2ac6a52680313aa7faf0911c53e4233834aebad6b291acac3555bc6222a4e559bf552e0c5034579b47e321a2746aca3df890f2f304283c605827766c442ffb08d0a61a97baf8473d832ecc09f3b11d528c194de6ff9dc43dc43067ac34f7778534908ce9b0833c35861f0fc5c63fbeb7e6c7879b6281f0c895258aa64af0a69ebcd2da1fddd77b8e6dd4d4821bcc93da7ef8c7dc649dc6b5566788c44e4b14cb82c355a7abbb20af03b024e61bb6ec36e051dff5ff4d5012b334f388d2dc0cfd7f3ee1c257128cccba5860c5ce5cb21a66d758a81ae64bbba5325a0c22c6762826211ce18ea459332cde4f4dfe951929eba81b5e68b010bff1f071ed8133e41f57763d4e4f620f68d35793e17ce4221bcc9a717d6eae066fda212f473423102c815faa21b992e6869071ffc7f68d77b95f0e055f2c24456aee13edd55ca94159838f21fc707563136a19077b2a7518f9ea6429725864c0f7c7f09262568649c094750bbc78e568daaea79e033f7f70d27bbf55ee00383ba027201"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"489eaa3386a1296e845d62f53143074b60a7e468b080c094d000a80ae91bbf74","proof":"0241a221b95ad1aada22220ab942f0f0417ef545b1a56ae7a4a0022fab24c4311c5d80d62c3bf59d9eeee65365e350712117d89299a1d6819c314d5d74b34d1aca8e8cac6950fc263f132a03189952559541e9037f7f8d0599310f39f0d8276424946f7b8f69c30929b802e9e355668b8fc8368459431479086b0eab833f6a20c2d43bee2877da6b407b6782955eb4d141b02355c1c29fd0389df7626a15c10b2ae6bc95649840854e982c44381e2c9b96c9e5512c4755873eaaf1ef05146501d5db4f5441da95b1d3bff9e9f5e68545be37dc67189b52baf485bf53321834013421414d90c6df14eb9784ff9d28df638072d0e3a232c3357b4aa7de7029336e4ed0b44caf73f4a78671fb06d6d5fce9dceeb092e8b914c79c75c1bf0ae95938404725ffb35ba643b0577bd96d29fbf90ac9034e5e77325d872975ba2477c05176a1d8de9781acb5c7807eb735b5d4707c2d7dde2a46cc37d0ea0cda7607bd65c2ff8c6ceeba2b961a69af9a9f7c1cc40816f47dff90604bae69136402796d625e194cfd4fa322dbf33269437ee5ebecb56ec7feee91a2dc7526a706768b694e1c5fde944514fa0d2f32f3ae8bece959be16fcf7fb4e8cc7cf01e0767119ae321e9fc39ffc227bc2642c7431b1ba75fbd8cdb3ac87a5d0471d37eb49b11f7003860a06d5d840e7b0b38a461eafc2686b5e8b22049e830afd33e7447c2df8b657e6c4efa0f0c2a87e628485abef3bdf76d174157dc228a663240262ee95aac151c4df8a3d9cbde366dc45a1ed63d9c0f6eeaed7178ec66bc9fc2a813bcb8b226664896940ce7e5d21bd8b24fff230722cf39a7c3e41eb7134f02281a33d44d1381cf2a925b127701f991e8d3cd118a7282e9062e762bc0763fd882bf04db942005729430f83d41c1f6deb777fe33beb091d1c061074e9c721e6db55cb86d9ab0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a08ca2a363dabe15bf968f44513937447e577c45d96a80cfaeade35cee556549","proof":"343354962e88a232480578c28b83e4b5453b47aa390e5bc3a48cb66f827db914b229b0ade677a2ada076de8b0dbe78a96fc929453f956117915ec146ab881f591e6dc3683c61ade44535419a26add343435cfe2978e542e5816fcba396a6060f50dbfe334da81f9cbd879a71603572790f14b9601b94a1f8952a1ac1ec30656b8cddeca12fbfddfdd98d12b4fe7fd5a3d204520d48faa8f95703edbf7e729902dfa0a8d4f3241a1e4edf0802845ae6a1fe9e968cf47e57dd7cff1524e056ce06cce57bf59bb95b1a56f74204dfa6f14a9ede782ecec89920a508263f32f85101acd9e1f1cd2767d6b166ec81828d89326977708402b02310b445e0c686ca240b6416e2c084c2076c9524c569eaed8d1cd19a454b2b18a62e38de51594cda4a233c1eb6915f87b8375ed31704258c47518b007cced277ccfb9c483606c4c1123072df1516f92832ba39450fdaec667af6be412da24152ea849ba29cc1c6bd4d56fc718d14038e65d7b9791997e1e5d4258b538e8bd6d7482e035507372ed90e196e7bc935b35803aa02f0ab9ddb82054f8d66e135ee695defede3bf2a3b463a21f2bc3d59a8ecb33c1f4e1edbf30499bed958016dd99dd3877e3e9c38d8a3562858ca08ae470d3ef8eb3aa58204baaa77e7371b82f3b48445d206d79524c7b71808ba41ae4a3806edecd9b279a7531d47f15f79c06c046415d139d72ed82b6d3b10f7cf0bf3d3a62081d3656ee0f272985ad75caa6127682dfdb110584ec43f1e7ed1e0bf9588344f5ff14500782e4433b060ea5d8d2c0834dd0a9eda406c873ef494857b0c2abb7a1f36c877f893c5a5837bc0daa861da7a85320e16a7fb894b34d67f737321e55fe264d0da631129fb5192c4aed8ea512a8dc8d5dced12e70f88d71d25874c3aade990c15b694f7384075bb466dd965deaffa758a695b2470c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"763c4890f04b5c94a5a1df2752c59edb304d4cbc17f046c31cf94cc2612aa263","proof":"2a13bce2cf827a680f844a6884a13eb647a9bc54cb09ea8f3d5931787c912e52aadd6be9e099b26c6c46c951f6255295741351036c437e8d39fb22de5897d021d4f12d58086f6f727b318a99326f234fbaa75030b526fa5f48525d4af7a08801bcd1dda92594c1fc5668d98e22e995321d7c3e3d15688c64547ff743d34422773f819053360671eeba93c20d245227ffe5714eb96e9a85e1a4c70c7ba1c7260313c144755598caca32b9d764403efb04ff8344a985b0604e684e3f5db2c75f064a6492b91dbf840221d86c777bba058e78162efefe81b76787f88034e4d0450a2e429f2b91ca23e419e0ecf2d077764f33eadc48747aca0815973ab39af42f65284818f9320cf8b8b1d510c6595de5ba65548256888e092ea3a4b73f9d5eb33d4cd8e9cc3dda75f786fdd1831409d53db3d3d88ac354d95db57a357524fdbb2f36b171035495e4efd18134d17a2e43cbd2f6f6692b08e2f2dc3cd604657a4e7d06099a688a2ca544a98edc91b20c78d542528bf645b7ff94710709557d5d89141ab3db11c4752f84daddd96f18df504a58db1a599bfd6a9c8903f1a71769a20236734353283923c1b0dfdf9cac5c08f1c6c78a4c98ab1c2a03403e051326006e06ef357cd2c1bd3c86c0848f3c8aca5ed9a4844801b17da625cc0a9fabe2d661debed83d01506e5ac015b554c8110c4136f7271e5bd1d5bc013477e19f3cf42154c76e13cea756c4780a9bd4b49675eadb20a9c15a9164a2b93d87c5e46d7b1f00a571c643b899cf6ef9348ae0640bb2fa5c702c02155be091a1f2fda1afed6e88edd28e122404bc56987ef574176a05a82ab5c6f259a3d0f120a72ffa13e70fadb2ae55b089f9effcb7969f7c3db307b768a083aa2d92f3d070f52a76526606b4a002e1dcbac4cc67f8a934191e64c373bf8b6421959290b8a78b3282f3cb01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"282d971759ecc218beb3ca8ce9d4d23b1184c3fc05a6da943ae3949b1ee25d22","proof":"1618b367852402ae873c42f15f1f80df63d427b62d98054fe11b346d21188a645e5e1115da342cc904b5696059d39959c1f8c0484fcbc08fd1ca6c8c9633de3890b8ad437557287adb49d71069a98af09c0b3b926488b38109b91fd2997da65a86d546b2ebd99dd2616b61481bd5f1435deaee85a79baec6c9ef7154ee7a476384f681d512354c6dbb50f27b990ffcafa2b7b6c4a376120a7374e89545c98b0c404b4254ff5a5611595df78ae0f2ca032f1f9d27b6bf0d5d63c0680ec65f7d075cb57aa04938abd152a834d0b79b1a91f95778f4d46223088e7657044dac970504178bdd403c9b8292a01e4c3285c322800e99fcc34de436b453db967b732355c635287801c3a65bf8a1258b4f901673b0581e69cca8f69c7eb7a006e6611b3cbaecfb02c66fded394185472a4d1733d1026df7f7db3c3ab6eecf360da5216328cf4aa1b10c91ca3ec8fa527638cc7bb0e6278210454f5d92ef87d018b503968f851277660dd1073c173c9d4a3537522ffc299e09e127f3c3f8bd9bba135464fea29397d29f815a628e7db935f7c9532637c06df3bede52e0b29b4f4e24a3b2eccce6d4c56653bfa58b65cee581a1ea2e897f6c6ff8d44a54df2b477c6fc996f6cf02e8c9a7fdd751253e0518a160d5fc2fedcc0761f02ac2206181cb891eb774e19910d55083ad7f8f5dac9b25406c21b7e3fd9bcac947d7cd027625c87ad5eaa9884ee04355ebfd3d077a24602ba55da90db169ea0dc63bd769b0124a64c31b874ffba892c1d25cadf5b800a19b026f48e7c61b307fc049f358e53b263527c06874ac6bce71f0dde7518d3efc87cacec88cc9610f70faf5b48406e1eb9056225e317b093a059e441a9f224ee1ff77e554d520ffe8241fcec3387ce2496370135267110438a5e211e591c55ceff8f9d5191e092ff40b7a6a77e17116ab37b0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8c7721b10d6c1a31a7a3e1c3e3e79ccc38e85f9fd32df9b765cdbf226d811c29","proof":"92d11568fd49c796e3f40a864e339cd6f8ba9c003d87872e5e23c97c32ac592046b1130ec4263e49e813543b6a541dccb70a6cd9c44ea9e8181d063c4582da17c215f6ad04453373a1d43fd2fc7b7e9277ca5f96d2a7b2fb2c3253c1b87ff026d45a25dffe306ac873b365c5db5ac5f8c2911bdc7a999d6f088a8030b02f0b4a8d4ab4ad50643ee6a85ae69bdc3dd2a7e0c2c221055a8e9c501f1c6665ad100e9c74057c79f49886b782316288a40cf8c383c3402b9c1ebb46faae5294b2cd09cc398eedccde0f9e90f1c0c84bd44f4faaa08c81b17ba9743144aacb7e1f6a0bca75eb741dc5d3712430d53c495c1afaee737f11109d19d4391499821926287dd806e41723386478b91b256a9739ca40cbcc56dbfb5e2cc3c2b41cc43c3112060461f203f11214600e87bf3ecdbaa57f4e362b6e1d316a3af44777c295ac8f47ba22225c10815fe35ee4db3621edd0acff0e7f5dd3509332aa8e47da80fe2b5eee07d242a3754fe9efdad533473bdfcccc047fa595b7888ea68de313eb972459c660111a67b50de40a6428e2fe14ca17ada9efebe98fa3d007c47e3046ffbb7044d17e6f123a4293bc40c4c9c043943e6518daaad80d28b9835de435ea82450f688ba37bb8bdc122a6346a24c38f0c65d9176117740943c0253deece27eda83ff0f710a9709dad5caf6dce8d999011d57097ca2eee30dc2315e9e007061b1c7e280421be59502bc645679918557f741b8284a75e1559c05ef521e4d1b57ff226d2417b7a1d62b3828db7268824dcbddc119ddefeb00c56774be5b47159f5d47ada35c2abe2444722c20612d9cee6b64cbdcb93f187f31c533d2e3688f49433670cc3a3ccb186e71fffdc44aaf59bd65740385858ecdd39b0a8813a5961ddb406c8f0583b1e5de56f90b88954fd732c707b331ceebc8b4b2cbbd3c821d9136202"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"025ec0edf1d9f77b2ffe7d093f3f71a6d32492492a9945f888a80a25f2f1cb79","proof":"ea04c27a269ea306f2070770478fb10627dad83652814b29d5b22669247715797c312492a96e82f696ef3297e7c5191dae41eee3fe947100de0a36f9dc7e4c62ce30aa764a27344fade7ef6e0dd825314ca10277f70f6e727f3df642c4ad6168325140126df5faa46146ca4535fc10291e2129227086cf70dfb1c14b79c62b35a0f2400b9117ca58f3e55f50db4622f544f4668a030d50a107b82a90c58748076e73d7ed86819ad35ddccae1337444276338eead26197efaf7637f4257a79608ee326748c5b37e7ded88eb00d328accb129bb6167a764f77a4149b73aba66505d2027a5ea7f895f85d0ecfd9c2035c6e999b0ed72b47f470ad193806261b1e36e0877b132129c0f47db185e057b3a56ceadc95f25d80d843d015bb4d3861f8530406ce12dc059c2799c83e7755e975523d62bbd4965e044a44242a9b2915a669a093d85c66b7eb4d82fd4d1d621d4bb5eb3d1daec5c0af3d80ea4ee9fd9eb4176077d47a301608ee90bb61009924fae3ffaec8e23d4f397260d5e3e1305c967092b08526321e488ed8999a0b295498aef43c21caf1de2ecd10875c8239cb5e24e8298c2a02723ce09a2ad66c38f1a1a2fbecfbdf0bbdef7b91feda67f96d3a686ce140cbc521f5a3fe9db61fffd46f07d67987519be7a60d6e0d01b48bd9f64e9e21d960606ab58b9e8cf94395143fc3a686d3287fbcd3c8f674b938575bdc4e60c0d0bb04f7d984b9efeeafdb8238fe3b37e933e47720ed1194b9bb610c6536d28c83d81f6c5cd7c4cf432a606d5d64c022bf160fc188eed868cd81a9d39a112800b8811b33febc6cd7fee32306b8b7e0ae483b4048050be004423e9b47c37f8d6b7788d784d045cdd7d6089b17022e7cb226877dd33b650cbef63615d4bd0bc1dcfe7884eca3efeba5d25b5f36e0256eeb7856623cda46fa0473195283a80a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9258ae303c4dff356c40c314ca151471d325c5b6ce3123e50bd6f147684bcd06","proof":"b8826c212428ff978ae4f60d3833c4c2e279dd99ea07eac0a59a9b00ea70b44a5c1364068ab42febc433c7483d899b4693801b3e399544c73c87807e215e71264276dfc6036f077ccbf4fd56756b9519515c9c3cbbf1a85f529a966c47a05127ce15890d698ceceedd265a9c9cccba91576cee2149e85aa5796bc68aaceb812aaa5966d3ea1398d3c2567b8709e5a46fa95c825deb5456c2603b682552541f095a4f7de56cd46b320244227ff180d76f3153eef95db6636b4f59bf60dadcc00a8b70a287d6a975516dea196a274ac4beeaeac423268ca34fa3e475fa1df3fa038ebc9cc3cc26d4fd12f3073edf0bb2387ba97e91f6a5f0c8a451b63707fc2c68cec54060bc158bbff9512c5da8f8c039db06485b00800a52c2140971f3d2de45a2874ae24e718b1ff183da1e47c2c7570d3f49db6b7507160d3a83bdcfc7135f0e5634f811c1e52e7cc5bf308089ce355224b503dba8aae0f73ab2ce24172b172658649ee4611a33e25dfe95b5ca8912416ab87586c285749b6584b85a301a728c03a65ff89b117df1d7b8dc6950be1bd6524c7d2a498acf32ff78b6be46b8553e0e0d9b1e1256b09f4125db9764096f10dcb655c5e89026156a9e333d13ff48e0fc6953704b97c138fb00932d7b2c847c5256632e37e7f7b161424d92df1c667e668b5f8400291c8f8b033c579da3db973deb8bcb33d7593daef8ce5a9560371abbd8083adc1706a7212ab92a99d6eace1f0003bc1cdb9d53ac431235a04d49b0065b2f3980b1b441a344fb60a9ff40e7c179e991ddd69bdb93adecdf512b28644b8e8afa9db20bf48d2d2fa82ed4ab7bac39f343e43412957eb849f903502d85fa5bc7a46cacd9f0bde8f9792920febf9d54e963e8fe9f7afea9c8b07775058fcbdfd88e0e9ab3081d40162b3d9055069d5ce1e4f439c5f566ee73b2253f03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9cf41582239aa54d18f06c16cdc8c42a9fbfddc6300bf90520601af407a83e40","proof":"a8d909e1c08a3d92905b4a29a1a60bafe40e2bf52683b374e035d2a976963445d862a2fb8c252ecbcd72e166b9344f9e9f6f52967f6ed3c55bd250940bd8ca2e04ae4a26635853b234b288ba3d3624aafdf0d41d07bd1c8b6dc36cb12b9f010c283ca7340e7090f4b4c0e0bd5b4207567910ecc8a589d54fe192f36cd51e096f8c22d451122a95fd34956c59bd92e5b10afbbce0e7592c337b5d948a0cec9006702a92f516f76deb9d4f2752783ff95508ac258edcc65ce6eef7bcb57450c403db3f1bb6e4aedc36b8d705ff350bf83ba20b0ea335b9b5bdaddea0ab40c26f0baad909faea05950e225bd10421bc503cdc39041846d31a4b0083699423e86027504faa81577bd3466c1d241b693d23043c2ea4c5d72ea9e422a936d46e2e332ef46c20491c84dd1d7bc1c1d92644904d8f5ec4a0daa7b04de9492ffc9a1c4625865dd8737e1b571514e9877397c5112030c6ad743f03a3106b57e7828aecc1204a57366c6bb258106b36e4c1d57ea524dc9af3a262fe129bf9fea6a10c83d06cfa9a8da5439d3a8d6b491afc63b2fd7c551de6699eb84cdc56eae37d0b185a09f4a2df40bc1cd2615f4d397237e035b16e8390626f7abd2a1344a84fa24f06343a617f261bee55c3055197b186a84f0a391bb140770f0579fe8329a92b4d06380c36d2b051744a8da37cd1b4ded4419f14a9bb6e1f960590304a5d37468db337f25cacf924bae708a6dccfdc8fd5a87b5c53dd40c4825feb3e8c169ec5750122da955dc8214063ce062355af854a29fa2969cd90d63ace13248ff32ea76012352a502e9d7a9b75c4bf5feeb224d55201d59c3e103c660583c636d5a9c8edee4351483661e457f94a2c0ac5cdd259cee41be65dee026415715d2a53dec88c400738228c4e84d90fe876667b570392b6beb4ac3913a079a0f2d0f2f95dcf210e0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"96f30abefe0ca7d685d2c538d4810a5168715c49189d8540ddfb72f55747f070","proof":"0ae6eaa351402d53a1653d94fe4c0f0cd554577e86447b74080cc8606dfd705e4e755c3bc6be68db2c53bba924a0849683d7200278ffb5553c372654cc5a105ceaaa4131966fea2852881d0aac3eee63e8cc8ca3314c74fe638e5872831ba161925a80db17888cdd97d0d2c1474056dfac58fc64267e173723e2cdf615e9c2196322b89ea8849572f090c804b36486983c83b632f17e92a944de45fe34821403b8a57c37fd6b23675e16d61166f4cc0229329858729351cc42734c8bc5e1b000f0e18353a0146f71be16f0eef686efccc36bd575e1b7d6575ab00264431db90dd244d293ec7820725ba9184eec36a760eec6053f9f217cd8b3effc08e0662f2aca96962801c41de9549b9f1e7d8aaea2d0bcdd2c06e1a4756abff66889b35d2d7eb54288cc373b1b19ff6dbf43ab57d249d1ebdd864c4d1a6cd77205bed8b01d44e433bffdbf8cea830487c4cb83d61d1e249c573df0bd6600194d825dfa724832a0be631f6ca8a62de419b6bd7847e08ffd36112ef89eee7c5364f29a6eff17d4fc7c95269c4bd6e5bbdff5ab0cb36aa06ef7285cad03668f5c0a565a1ec64bdca1e878c742141886d073dba00d9c5e036f7b663cc3fb5f8a8aea7c41119400a83f723beea1404cb114a2dd99082e8b45f9e4471af27692a1163b39ad32a91312fb458ddd26808fe59b1e501f7e870e5b3469554032cee0912d08d21aca2d17b4873378004fbec3e60f1801d676b27cd8f4202963daac828cf35e5e82f2a94482b1a5108217510280593d42e95e7fefc73610c0e0f859c957ee61582f46272e50b11a2d5bc89bf78ae6f17a11f5c95eaf3ccfded71a7d1af89a4072ab1349071e381ffd74f0f5906ca7b51f3c3494b2afa1891c85d87c4b81f0c94ca196a90a5c21c66467a20b8bcf139a691f4e5d956288742672bbc8ea87797a43ca3f7b0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d25ddfe2f31a843221a61268f7bbe0ca511169e34bc6beaa4a8557685c12d45e","proof":"cce5b70fbda3bfd62743a7a2e0904c98e5ffe6cb80b76909c36c8d982ed32d5f02303bf40bd469ad5598132b56e6ac0dca2c1df3903733b8bd7d1044dd143849168988b0faab156b1fcac79ddc3fe14221a235650074495251d6e8462344552c6a9507a5e4ee0397c9be6826027aeb9d7049636375ba409e2a4d72472c2fb515a287e317b766577c956c2ebc861403c82904d8689406eaad1ee8aa462e892207c2000d10b01b22bedd35746243388587a2cad97bcc39f15da130ba02a725a903805f82c5960235fa272f4b72493c136ce4bff2925297ae26e9d7462a6174730b4cc0b5846c56371a5d65cf8c886f59f50b9412919efae1275700d7f70cb2d616b23283ff8f9aa88f20ebc8892bd231a1043f7dd9333464570e36f53742ad29717061732e1e62660756a0036ceb183a952265a7157af80fc377b44fe7525c74258c9df3086001748038d65c8b397f6373a44d68ea2682d08c5ce1c667c7dc084b88b69d31902356eeda2eeda44bfd529ebc1f08d5cec026da8d71f93d91e60d20d4e9977eaccc16d878b87a4ae729977d68c5b2160aa49485e0575257d6a0fc2a86e36d57c232cf3b77dd2dd2ee5f607421ca03d2aa2724991c15103826b165028cc6e788edba87b991bda1649837a2b43090503861880d4c2ab9c5156627ce4cce85db4f4aef5ab6c79f968fcfd4b5dc044a277468c66c5dd66d935af84e457fba395f46bdcfe6a8eb1ab8f428fab73f337c0e320b75efa928f0aa1d14006c2560ff1df6b0a493085d350eb0589ecab6e408ac4a6700343009c8348b46e1a8385025a1b36fd32875caebb003cebc5c2d110530f30fdeb509ea5f5a7bb2af125b8564ddda82744e4724edde7af3876d9b1f39e77b5236e51cceaac6bd4f8ceb001a8978aea1c064c2dec962b594e2974a05ca944dd4838ce52ece90d4755f4c06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f4c0f26f1cd53508fe462023a9b7efa49ccf0b2315b269e1201fc6aef5c14f11","proof":"3214cf7822f6d1fa5f73257f5bfdcdb064e6722bdd41ff0d6e095cc472c3b20484702ae7b47d7f0a62f6135a83f7afddd181c97da3686889fe6e42b6cad5e8735831a65636bbe3a514b9b748f9e32273769f79519c1e8ca2a927ae1e7bbfb05be493217a2fc26a2fc49621c88d05763b46243962e65115bda208faa1936f8e744839300af7bddceaa10af8c3f317101d28d8b81ff5ed0afdca117a211245990b4b732060a6cf1e383359c8dcce4fa3a2668750116c48e32880095e9300a27d07ffb7b84f29b37d8dd5f6b2c6aeb7bfc1ed0a1a6025ee536fed91a7fa4f50af06767dd6c74ce817b0312a82bdfe27c438fb67e65b4034ebb994a7f810f30249530a9102f2c525a1b213f1bf4054acd0350e57ac6260ef5f310cc8fba8d9cc002ee83612c8402888dc92bcf5b1662b203c260a240b00680e3a5c06c1a2a4f73409140010fcf6db29196874282372fa1707c9a8f9d3a41517ee9f27cb555546f344560e62d5626ab9d49db682be1397ba195aad0251421a1700812b43c63e457f094e3827d10a840803c1b814e34a062ab3f5951454035beaceae5c6efae9898f78200f57d2ea7aa970fbda191438ec10e5e4e54e379955a9af1c88583bf5544e0a28fd69e041db53f039e31694e27a0aecb871226fcc55412eab17f6b360cc080a7e4fbfcb3418cedae5acaa0c767cd27ab9d8e35fdbc022be2c07dc8314bf8d7b5805d11a2e6922f30d991de3f8b6958820ac97115cf0404ac7aca8debad13e612005dbc020251b4e9c8bd77f1cb7655fc0a91cfcdc3ec4bc52e91e721a611d4a8a9c791dc9886fa71ec78bd3f918bd75b78ef791a3ba297d6ef651fe8d3d787cd1c254f88d95026f7b0ceacc8b55d2de2e3336fc3ab297f2de6be661561b6c023c37f11467cc4d1cdf056445cc525c03c2fa6458dd8ed3a3589bee9673674e02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fcdc517ea1cbd9b376f8f724b211af21eaadc2d4b57e79ca2fb10f974bbdeb0c","proof":"32ec6149677be1b16da92d30531b5bbf49fd3d1b9b84eeda54783de0bc693b54decd8dbb4be0f090392b901dae3674ee5f35bce42d157819e63f9ee2f649df338c1bdfa3ddf46deb6438228ffa89111d03a3793a2dc5cc9d0494d6c8f1f5622f8e2efdc1e90ba00847ed3742cad03f533a05a39fbd6ed6021270a4a70aa4964e4ef9f54dad21ee1a17e5282f6b6c3f3550807ebd49bd0bdf6a9194b77f8d5c06be53d4e560280afa7dd9728265f7a28df36689a24af413325cdbb91753b38001bd366615204ca55a5f21d1ee9a1e0680389a0fb1b3378664c494e2f772f53605fcbaf030295e254a841cd52084a17dc7ca9a17cc303d7a4a0af1d57c81f6bc4610acaa47b225555eeacd21d9924fd9e806910baff278a13a684fa1783b3309706ee1e1aea837288442452f080bfbd623e149709f7c49c282541d8cfa05c81a076237716bd7da53504523f92c223d267248df23ebbe38350044e2a375407ac85d1ceb970f10d952b92f8fed3abc1224ecba552cb26e940db0721c43e10c5fee3f6e6095eeffcdba28d0c6f320541a189d7600ecd6ac9c9afed4ff5fe89c00d901d68c2ee237251601f70325c05d386af8e3ef981d382431e95a011d9086fa0b3f165f39b2a787f31f59fa956cf424b6174d29f6624f7c150021c0837b7899642b3863cfa1a4bcc9e2196412f54e3db839f9e99821db710f04a4bfdb7e2f1a5666e69aa3b4b3c37c084086bfd5cfd2262e99b4c2349721e0ae7dc5784af4625612fe8ae59e177e2c6765f96b44151fa754f3cc3cfc22a796e4b6013b2034054e0c2225e3a147ee8b64db996c0190d0f1ef258134e0f6fcb4f558d1cd42ea45e6258d96cb4247c0605021c704342c513c187fea4751f310be696e0def491dd6560ab3c13c74285c30cbea3c017500b7a70ec987c715f1b8b665efa0d45dff518d0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7e090817f4f577a52e4ece816700c18a58ed4c10114f563b80109d9d4cd1267b","proof":"a8c8ab8a191dc3c617f754c37f63cb36fe48778247c4004ec65e57cded6d28428e252626208d6b611ed31d9623bda4e60ff38d2c480dd3d282971cb847d4b16220a0dc1900599be28170e0d1d28ea6f4961d76434958ed59641548d02cf1cf6080755da4e9ccceee240bdb8f1f8e299cb0a64104f5fcf9ac9187fa66c0a29b25139a716305014756a0f819b15434f3424b5e2db178a45960c5f6988398e3c6004838b79f447eeeaf54755b1c604fc907c1be0850dd1d38866c1ae1f4b671380a18e34ccd6fa319448b45247b30d170323d29102f9d21a55678315dc3e6e75f08900f24d991abd08bd6ba20a888c161eb7fd2563611a605164ccfa5915c9b7c26ba18607aaf165de779c81bbf295202f6b81501cc77b65745b2d953cb91009a063a3eefbf94dea3da30da233c7f12462b1042f8b3cdb0d5b3b13dd667fc218a05a67549a3b2989bab6b5af28c918b6a7ae606128afaef7708bc3ea48362386b01587812d59961e3ffddfdb3a8e6c28d7e6a610620df59c1107784c4aa979c980574f02ef8d299eec980a224fb526b21b72952b998f4335a709a9f0b728792a701002ca2ca965fee28a334f071adb9783ba73c58f04f1262e8866bdedf4ae29540ac4842f398308e4e1f12a2afbb019dbd9b50328ec3360eeae696df30d7fdee29fc410d8f4052eb6ea0e4a715b0404cb61af2899aaa2bc4fe830e6282c4c0bc79d64ac1556c7b8fb0302f0ac46a5e0a384e173e469d987a57f336f8eb36ee6024c4ac215a198736eafcf8bb40e96456963556ea1747f8c6f5bf456e12d0c7f70e38e94e92c237c7812f8e1375195d1395ee571ea9f7e7d3d4c639e490ad4b90515fcc68434392c7ee4a51350a5e355167d4a352312ad8aa08fac263c771a56d07e5dbdd4c2a90786df1bf09fef4fbd731fec91bfffcbb1ea9714e8c7882efb904"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"460eaec3108a7af627a85b0082395ee89bc59ad8ee295afaa91d2af9c5dff25c","proof":"087900d75825fdc10f3471454e328f7bdb86615e6a80ecca8bdab2f8aefece3dac1988e01e8f959a195fc5d385b087f6b03f42096d1fd3f40eac3c66656e17519824a65c5277a10375d54533245c71bf2e5331c90c19fea5e92a82e004646a788075cd7108007c85a2a1d2eefb2d591d2fe92e7dbf1b0c32bdd6d6742b812d634f025628b44beb0760b58833619b6a86123286e5e57556edd92e71310b6d9d0727c09be64c2ea6d492eb476f7c219906be0a262c2cfde04eac71d04a83e69c01118a7cb40c53d3b899eea86810b3409d4e70bbf74dcb5bbf64c95d2c698a83058299d437f8744fdb96e89d28446ced8ead2464833547915890935704860506342e59a77961696f9e4bd7bf99bb4903806c9ec5c73ddd6a1174192c4915e8086968bf987f5ed7f0bd4c2d631ff8689671f377fd47dbc8bb5f17b1103b6ea4a8212cf9caf96626731b1bf07da4f25103cd16e3d568c4e98fd32c91a6e98c77d908eeced919b91f723f2cc3e386e567fde2b1e06ab38cf6d70c3e471c0d23c3e15aaa782a47e4d1765607ca2f3cd27cee52fd60944ebf87d2669d469cc8ee1c2d726a6551e245a87a8fcac7168f07ea1b725530c07b6567ae3305e5e0a3bb57973e08ff3032dd2d4a2fd213b590a1e75391fa32ac3081e656c512d99dbd6279964720282f12e48d98a4925f53ed2932bec9b43c89b95f3e0ac891f4164639f57e4f2495ab69f0510aacf587be81af0616251e816699a12139c75d62d79d65c45a7096dcd1f1a0e2abcd4e24908c091c18761d1018b6557648c287f2c6933b93c82892fdd9ea26e7ce9a11fbf19293656ca140695dda7af29242f7b69f1ce8f8440819a92100fe987340fbc1be004746ea01b941704f153fa87b209976717f5a290989eeef42ce564fe70b45c2fd7f38997cbea8f4d8819369a894cdd2102021e309"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d81b4d695f42581bfa8d9c65975f8859f857df44bac269ab490247acb4820f5b","proof":"c6449f76f92ab399389cee32c7b14f4fe8206582b8584aa327404df70e7997722e75392ebfd4c5971f0ad18882032d31c7a57b72b699ecc83bff1b931be0717b2ea16dd8291b3e9319daa3e0f46cca69a55b93abbe83104db2012af9abaefa47d43b85d9613dc14ba744643d141892362899831e4dc4e2c63b18b4287011a3336b5600ae1b5c796519d0ca545c95bf4b8254733680d5804f01152e134a3f6c0c68020897d6603ec1bf5f5a90e0597b7079fc5e35305d7d9a200b1d5040299b0f45a3115fea225d6c2ddad6e8c9b0f3721c9aed8d494fc74c758446a09eb5c10a60107f9ba4cec6e70bd9ed8e459dcf3b822b58148881f7c4e1e10c01529282271810f6ea5f2c4a8a0f3cb6c8f9c166cd1a50848bde4ddb01e668eefdfd33603b6cf662888624e6b8ae9476dc713d4531e6042bb32db998f1c6f3e63cd6f4d10e86045912ef6fa2073a03c65681f2d7ce19683cf60046b373e1e072d6366d9a6d92193de56ba6549616e893c9178821f52b80e9d6abe3b54cdf45e7b5d216a8518eb90dd1066a0212a7957c4c1c27ac18ba95538c88fd6db254279ba707748f585e2ec13baee8252f5d83ea6ef6dae4df206e20ecd29caeede063880338732e127054fed5ee8bdeef7e2c5f6b6124f7642044c5b0f58a7eda81ab376cbc557b1b9ec4749a91538c1d0e0d31ac40c53603c8ed38fe55bf8e1bff914e8201e250347a3ba1cf321d1dea82cfa5256c8e3c38b7393ccf2a9c90db70131d0d59b83b4c646cc41dc7fd270e1425a0b04d86fdc04852eb3796a8036e18ca780c8275ec78e659810af7b5cfaf9d03bfc79d5c7a93167d9534240015e6c57381f4b28e237844a8649db2b17cafce67b4438737f9a204e9ba310f739871f05bd0bca7070c009710839e9ed1952ea02d54bd60383811c0f87b94758c5cb0afff24b3a185e903"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"72b5037baffa25cef16e7214d21cb36a47f08d815ff7b85b2413fcc98d03f247","proof":"54847507866b0889d2681f41076d1446c6f5b3a4b6b838959bb2e3b4af74f72c865bf2f932101ba75a5ffb154982accb45c20c816312448b90981978d606343ef0dbaf7af7ad34be899afc49c49d58fd735cda989f31915f98506918be4ccb2bea8f6abe71e5c98c1ac7c6ff2dc9f3838f2702fe71382efcac082be44ecaf76ab34be9296d9e84b407e9f5f3a0d8ca29556f8dda8800393a6541dc62541b0d0d8c02a8973c7d583f1b5a53e49d2ac3b5e47e5f09706e45ac3c8731675a443e0c65f394384e213539bab8674f809f6ad1f7f5adc100b240073c28ab3c2688830c9cf9ec46ce896128dc04755a1fa98b5d1c5763ca76165564bf13b055d0971d2a5ca9be16695289c30c26d6f9e27f43503cf3b7663215c7c2ff612f58d7e6e15b500ffeb693899dc8fdbf3fb3805b8ff62eb3cf3e636d3125d2c4cf079caa095fdeabdd90270bb23b8031dd34ccdb4887399111caf8821c889c81a2a051e54f5000b78332fd34f773005d187b5c7eeb5b1148463fe435790be826881ac0e2ab50bafaaeb1ed39403b9ae6db58b3ca20b9828686b9fc89dab82da1348819764a6e1ca1c2bfb29c4995e311a9695f1a1c49971755795c32c8061fff2986ec52355c521c789225fc9172c2d3a6aecf0310c35b9aeaf255395c9640edb24a79173f3878258b083bd80b371c1d9a1af2b421342dc7ae378a930e174b160ab7701d346c6eb157ffd02f1b44e9e1ee4e3ce9ae00b262bcbec4467b8376ce06831239257cb25f1487ece89b418113f40862af38def9e58a1d91a4afdb3334ef6aafc4e87560897adb6f310b9bef27a9100acc986f3576274f30517309a52e0b54042674640490203d3a46664302642d768fcfbdf05cca137566ab40ac353ea55f3ad67c00cc78a10e7dd772883edc2c921977c37620f013686b3ef6aa221a80fddb93e806"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f2a68c873dbd760f4657cd9e643d0f07a49f9af9f9895fff0055b7dd29fd7378","proof":"5a3da8bd14212fb7d1903c3be06b61ab5ca17e6e218c1e8b86c1053462e461665c3fe8f64c9f50686c75d74796c55d8df04fc67557de152403747566b9bbd273727613b4500719ae72ea5e2ea2b3b16d290be577df3dba54fffa552d647fc827844d29bb3643371e2e0eb27ec7013234fd5883b2f5fe2fbe344162dce3ea685053748e5af7e2548e8830d025536c6adbf47951ac439774aa581d46d94c9c1b065b6d6f56e0fb361e1a07b6993977dcdd1b532abde0c5b31499cd369022590d07e9a186938480528f31bfe6dfad98a10f42e0e525ae18102a4d74724e8832f9046af782f5bef9bce47de88dbc0c053935b76098c8907b58246b3f38a25559d43cf083a63f77245da7adeb81dd3b72d3e4a808a6f27cbb56a5539f966cec50de1e3cc56379f21cf89b0eec731ebfbe0b986a8c3c455fdb2a10269ab3d67cd1951e861ab7992e219546d5fbc4c42b60ea0d3e8ba7d86bf47f848663e70f93ebcd733082f9c2eeae35f057313be2a69ab75caff16d104bbb9040e8eee3d1564ecd78b6fd70f61edf6838a862036e25e7df62d30808ef95030a7ae466a9acdb46567242fb2b1de2c98a185c827c97c5d0e2aab77d2ed587b7a1b1c42da3775e349e2c720bbf1dbaf4f839b924ca9d36e503e794624be66b893a5baf2e6e839fd3c7435405aa1e442cd9304c3e31a941eb3336153afc450e8d01ba527778b3edad53716068964e751547fd8f71852a73e95070adff772aaa5e3eb551650c194291596836d39bcdc4869ae5710162c569400a79493a29b5bfe1f7bbbd96c2a42f51cd7b6a34d6dc7ffb8c8c0d53515e5e65e79cfb3150fd279acba39be576fedd46156681160629343f381caa571354ffb8507b91c7a9a8939affe8c3586e33a47dab09449197cddf85e3acea9a7281973ef04a2ee831ba1e20561baeae813623410e0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"88042e7283a9a5493b04ec145d195013bfc0bbeac42861c576912a3b41a28471","proof":"548c4029fb122c93a71d9017bc85ec5baaa4f1fe895f97baaa18b126b98fd95e766323f5d164c590a4506ac1d1180b2d6faca0bf76d177718d8d23057cc147115a1d93947a73f3f294d645464b10dbe366b8011b3e5c0a3b20bbf344768c037e6ccbb23a67e7a827ad5fde03b44c5d7e01fa23ed52def22c34717e656f264e0bee282cd2d0b1f99f1236e13161021d58678652e526012c52fbf3091b952a770b2fa9c78b4bd9532b6ff345ceb7c348f5a77b9931ca0ebfab442a65e0a547cd087d3239eb1f5fdc168af9a63f11a51282cae1fb26536dc7e5e436b01660687d0e1c8250fb2291a10a14825566602eb4f89b3fe13a4c300be469dad70a0ac524015293b3a25c54aa49846708463247845e690dc062b65ba195811f64b921bfee010a3de666f87560c6e6062d6997a8c8af43ed70aaf3e59a2de78d57a9f7a3933efcd5814e1484f7817bfc1d0c8ece36f1192b1b3c876ff5cc81430eb161d2cb32f475e13d0eaadb481186ed2316b3abd8499601e1d0dd04463751c8161dc3273f627278f05917b362ed355e9a4face7fa5a29533518ab4bc23b8a62c37bd21e7a0e1975cbba99ba52e77ec46e13645b0977307b7ecf754f7399e874b4fc923b5be0a4f1ec385cc5bc57d5e3c6230e8f39dd527558e3e034e40a32a7f4eda3151f204e32051df238d4c63ad5e0d8243338066aa679f96092b7a7ad19774171d732e654f5c4a0f91afd9cedf82acf2b32e5546125862d824a11bd80575db55fd3000e54c889b1b3e1bedd4ff6957e71c85e7874fe6d909eac059abaef62391a9f1d8e616487e585e12fdfdf324dc48a9d3d7a641fcae8dfc68ee1d1de6b1128694328ad7d9aa82ab459e5495ce51f56bd6f7afa19e71893f00b0c19c56163159a03d3d052caf948d4662a2942e875e3800fc3c83f54f1766bf7cbdae8644a5ac008"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fef60f83d3cf311fd9070107ff8ef59e599768262e01c87498f2d913a2af734a","proof":"6ce8ac2462f391d9631c168fd506ff681c71684830140424080fc53d4f1a547c008ca5e45b3a4a47216e0849953ef669fb0d134da1465d0a9cebddc4b2bf486706da8b51763b414884e636c1b189ec78ae00b50d3b76d822c420239c0ceb4e77b49d8617472fb7a268e7a1ffa7f2fc2064ebcf3637eb2f5db242d1c60d82df41b3984c4acf77b969119aa29bd4af1c4f879f36d37266eb04069739543e0ff30824226258103227eafff5b4a9fd03cdaecaf7ad3cd8239126f48bf434ab51070adf9c5af3d7d65ba6025de0758153ad87e9c6ce2c44746fed34760b725cdecd0338797606718d95305da07cb29b6c24035713c11e6c9c71a30d48d1c6153b916f88f2e651fbc652a89ac42b3fe4506028f5e8a3b8341904d1a7267210f2237d4a2c3decc727e258585805cad97b74a8f9f542c68d56cfdcab0a34f39bbad2035b2235f6f95344d89a34676edd420b1139d2dbdc51fa81ed9cf8f607f41478e01764a2969d234f80eeb2050444ddfb5221dd30e83130b57b047dc964ef516f6c33a605831091468146dbdf8b2e2e33cdd29b008dd3ca45e921d3b8ad1dbce99c4be65272b51f058061ae9d67ffdc5de68229905e2fa8da9a76d81b451953e8774dd2ba7a4d21b7727e4cdfbb47efb5926f8e03321475b073a7322c5909c7a27c4e3e7e264212b3635e1b7d1ec081d30be0de3396801136d79af6a38e337532b91a18b1c60dbcb7ea8e4330360ee8b003f4606ca50d617b54fefcdd3b6115c6f1622cb8e528135c84d5acda71bd5b55af27ac14dfee4576fd850c4cd9f54218ad6bc083695c626d99f391380a9117c5221f82dbddde6cc15ca38dd87f2976e1f7127ae5f7120d30f8a8f6124654d5189b2a5c47be0fca1f0c6aad9b3fc1d33d8e0ef5cf201c4dfaae8e28e6dc262ad488fbf89cf8b3cd0f0f60997008a169003804"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2ca60307acfa18e50bd48649c98feee1b38bfe5cbbb32b59e2ff49188f28980e","proof":"5c789e9d36ac9ac1c250b8298f94e74f3d527521bb7066fe25f542c1609b7679a68c6409ed52294cf2c49a72fb8985fafc91a60e47c4f52ea163bb91be8d651a5694eea6f98d6e521fa3cc597f5f961f24f7652e5583495ef6d62bcc6894a4793e8d975454a5237a5d9f2dea0843f4ae5d9739daed33e9974cdb3bb04bd48f165beec9c9d6f0bd4cf3cc1c29c02b2f33529f1d1f1e79b0b08f6cfed4b8fadd06b76bda6cc80f1661485fc4d992c29c906521b507100ae2d014bcbaecfdea0d0f617f70c6cfcb4c38769f9a7556dfb25d380d453ad1ee9f2dcd2da0186d8183006cddcdd92636c8033fdaba2b78baba912b0ff25dd8da557220ba38d9025ee701e8ba9e73005fefa046ab41508d94711d4eb063deaef0080e3164dd89ed875852e20783f887068d364586734358caf6c44063abc3e3bc9f79d314382a7797c050568e183f8fe18cee67311e5fa5fec26217978610f09fff21e023bdd8c0b66f380224b01e93108a79c9739089e2011b3b315ffbaba14e54cdfc940ae9be8bac15d80c6361d811cbc300651127cea73d0b13453892a191757e45af2ac16f91752b7e807f1ed9104286d143c1598785140e4f9dc358de945e666fae206e50ccea19ac4d72fc1562d22016c211816d02e90a8ad68dc5303eea32520c649f010c0f451cd792a6b4122b485748fa4f84cd2bafdb4e24360fa1af1c332a292bf1ff95439eabe3c7936eebe4d353add43807aa28708fe1a530eef0b1db6dbc2ee2dd654f86e9b2d609ff1d46faa2840fa3fb3c8a3b67e84956a3dbd014890bb0062473283c3efb34a9cbb687fa5d50f6062bbec9428bd8c50e67f510bde6501696978813dba7e2ad2fabed07decd3845c906fa6c3120d3495f1d20854b2d2baaafb55a005397c30addf0d74e181e6c770059e82da63f744ce9d69efc3b810df9f2a4210b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b6d649f1d53ab6625fd5b9d9426d0969fcbd7b07425de47496b8a977ef0dac40","proof":"fa3576d6e9171a241235452a66632faa7e0bea9bc0348c010c97880101c8164976779e59528b787f5ef0d448476976edc84c7f63550bf18387fc647679f28e64be151a0dceca7451186f8bb843c46f70585676bdd895d5257a888658a0edf955aed9e9e86f22f32a155ad70e6c3bab21832da45c9ef67199bd536964e2345e3399aa78ff3fac0e93de4c30a19ca26c99f4c1845e240fa798dc907d5f64544701524da2bd9c0bd65094e6db66ae99ad95decf2946e65e8a476437178bdcbf3b02c733296f42861bf817e0b9865f492db86903f80b181ded58023a6e269d5b390da23f769290d4797657013dbaf42c1999592277678be7c327c962bfa9ee6a125f348c0e89c47e51fd488fe73f6aa39b64fa13c2d2461fa0cfa49c9ed322f4da438aa7efb827f20bddfa03144b64f6c3023f3d20e6069c68a6fb01ab70910de15e1e732fd342f5bda4c79f00fb8299b01749cf68529fb6f8a70f263049f1998404b8fe5fd0b8d79c0e45eb5801fd8faada5420916da27b4935ff4f45338c247f07de3684107ba16f5a333d90826c0e78cabc7ec0ea71a13cdba8af29c0e32e213c1a9e220ce35e0989e6ca9fd6b7a0d2651e343d0f51a180cf0ae85481b1a6d844506ad815a3caba4f56120ddd3f1f769eabc926bb8b1ce57003c5f1b2255945680cd50b5be914af195c22f77c22e8be1d5cd54c6330cd2d7e8734ef252018bd4cfc339f453fd2500f414393baa1dfe040e35f1f5eac53451b5765926c49054348de2fabe91251578f871464874dfa0010dda799ce814aa01944edb46fcafee61ae88af67240c13fe7c159d7d116a8e0f416a283195310d1740e1e25827be7d047efb7f7a572a67aee65d5050fbcc3b1c811a69b252abe95ea79a17cb87c2d1f0b950a4039d0c93978d3b07c0ad9037bfe8070dc24e67e801ab0a0d2aed9ba5b0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ee85f17e62c6ab84f5207e85c566fa34e44edf8ccb7c3536674f92e5905c1677","proof":"e031b6825cbb0f22a77a25913ddd61961507af0310c735abc21db2f84c90047dc2d76515a36387a1325eb6222efd5ed349a7071b77f5749cfbc8608820e71c327ec64dac27d4459d96edcd9153fc99979f88fab052df10d08713faac72e99520c639de119efb2332caa362a4347f50174ca6a2c70401e949d226482193048e6dc9bcc9e5bd247b44ce7048ba56487d005213e2d99131753f5a293cf5193bac09745f3fa7f27aca9f0fa42833904994e9ee8087d3b52aed74f3e09fd9d9bc550c775d574233def6f8d611f2bb8cd302d33baa85a03fe568bade5da69c53e3010e66faf30e2605bb5ff0034f1918f52731d8052b208dc8ba7401d740739b55d865defedb706a53369025b32ede52c34134a2298df339a9cbabb2a46e6367d2b333a0118f5ad5ed61abf18354f04b703bd612d1eb2a75f59d45efd01a4e47c26a5f20ead8343cddc12d39aae0094880a1999abd8eaa4c0d77375e528cd75ab95c15a8ea906830d78ded2a3f3cc58cd78164a8a3b57e5744916f7b1e4e63fb215541e4644d6e8688eaa67bb178e8b67dc36ee71b9578b10ea0fd2cd102c202f9f91888371143fef9a119789bf72886e35b1d4508ac0bf7d2c2fb6a3a81983d363338da366bd30e3c173d65884df7182ddcc3236015dab9d5a5414f41941681c35f2324bca8762894ac1360c788232732b42e52b646100714d9e2a2a4523fbde46530707b8f8fc4878f89a316001f9a6ffbc0af0b3c037124aa4ada7af2baad37c0506a1e2ba0904428d2e6841247cd215bb0d6cc1a1fcdc90dce01de6883268b7c3082eed3145e45d2c29663af72c272e9976757aa7652fa297fafdaa3f6fd695d0607ec89ae22cdd6d8f2b482de9ee4cbf67ac44bf7233118479c41fb56b32e2901d6dc0eae9eabe924c4fb2bcf7a738b17209a0c00451c24d4caaf158f052b6708"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"845f586d3ab8fe41fc909de8b41b2009e84da994e20844e9d2b7d33608bee922","proof":"8026e8b95a8346525a0f67cfc019752e0aa5e016f8bf6f3f1adcc03e25d7b77bbcc69c8f2d84d45e9881844b9b85ed8721b74644bc740c45ec2278c8332b0609be956d699d372beba225f4898f169b66891439a4861548f39968be089581d108aec38533d4269fca66ce62b555a0564620012f407e00c830bc0f45cc1048ed754ca1e9c503d79ebc932a43a19ba6bfe4042c973d8ae24cf45010df559c855f0fdd46e75c04080e6ccdfb8b158c2d6f385ac9ab4f0975fa493385cf2d41a78603294a79fe759968b3bf0ae7f0d6aab12b642f5b66d43b37dfead67134635f800684f0e5fcacc9db07e0482fac0bd4bbf32b050d507665cad1d7a5fd03ee85534b266ca4b1de118b4d9efb0548b52e985f3956c008d8fca3d06a6f1c391437b43736857ffa5cccd208be3f88f1e34543a8b55633e01045a9f826a06192fc581117764d9e6c8435cee75cdf1e6a2a9ff3f4aa7f7b6783a6c281b91f2b06000a5e093cdd80cb55eea563b97fe2f4caa5204067ba13d2ba45e80139900e70e1c16250cad1656d93287e8467eafabdb36702ee9e075d2802a7570567b030e588b45c7d9c8be0794043b161094bcb2340ee5e8ecc8eaddcb285a2137e0fc95820e9db47803ca641a70cccd19197bd66087712b08b3b449bacf96ea99ad9ba0ecffe171fc8c44e26ebaadc4d4fd1f425769e3ea477c203ff131c67dbbafaab7bd2a6403f809b949f7a7a46e696281b8c5cdbd358c0a2cf0a7743ce4db317d0bd7f37a00decc62fed9e40be292cb99361abd041c63c789795f63ad61f1fc6db055881c408784d4f08f870f6136a2071e61047e8027cc32d6bf82183791f8b74d16f4ee1356951c49009b06b426ec0db4575c36bb069609577f33637c17e37fc53eaba2b0c2f415c3867c94d98f3982c27988b651f9aa6857235a800d2f7d8a683b1f6cd07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"be9e86fe7efc502d04ba533ca6138205a1e7e2c78e2fc960701ff704d5bfe55e","proof":"e2719232ed57f184821b5bac6cce0a6f71da19afcfae5d3d5cc5603be34e18634a6a4db54485503c50c06e4254728e0f1c9e8fde49a4b61699205433f5e0972f4efe2c1f155ce5fca41198a9ce553b98376c39321250b2c9a0a0b7592ad7686f74ba89175e6c75aff49eea1d0238bcf391bf39143dc6ee5927ce33fd74bbe4746eab382f5e301f931d955111b919115a9499231a9d4f1f7dd9cdb9d2f4247608529e5bf6c0de476283d65555c916133e2f388518fc926261d4c0b0a5bb8fd601e602da29daaa008e5df65dfa16350968756254668407e769b581d28cae4f020a226bf726f78d547b5ccf4929f5f2b42d201495243b6d816f1d8cb3794323315408016e7c3a9fc8dc652162c758ed33fe0bf18293812b8e7a265033c5bd27252dc850002b199c248d8e12af0816d478e84c125258a35dc32f7ea591c4e3101a64f6d8ad92c3204d90d98369a713b518943524f50465ffe83d47d1474cc3a08427b6062dbf74085fb158575d17afd5b6cccacb582aa65e3151815d336a577e5247de8b8bae40541a94abf5448f273e57dee34ef4b944abdd2104b558f87842296478b73f373332f2e1375020138dcb932cd65ec1908a43505a9ec59f82ea47b51e1809e6edcc3634448e9612490f9efaaaf20b08363719d1301160b1286f0fdb651ac539e2bc1438764f8fe27e538ccd8408d1c5a2adcff36441fd1dc0d9ac912f6aee0a7e6f79749a177ba143c2c65a6e5c27e18f5491fbd0f4116c821aed4a21641d21a85fe45d155fba070d9b2825f948a22631e0318a85cc203582ed916a2638b23a6e33cccf2c1e6ca512f94c9d22e0956ec786ead898379e2ca44e4fe86f827eab26ca9fb230f64b04fccbf821f3de184d60963794daa8fca499c5b05d04a969c4304b290cd9c8b83b7f5874ade7ffe5540c879b97ee1248b30de9282407"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9affb6aad56385c382fc2f27b92ee878b69af109e41a50a87b554659b800da4f","proof":"ce076f726cff629b61693d16235aa100f4cb4e8abac85d6b32ab8fd0ce26927a96aae4f2ac04b4ade55b37a2d71d0b03c43521b3f494b7fcba798381396f91791a11af73686b7d9bc35e1170e74da0fee7f5b61119a3f92389d50c46bc911418be29d1026943a386a7df5d7d5d07dcbe228bc9f85222948c7995303f85f5f85c9f09f1f7b6cb4ba796db3db7839482ac497809b1a9c0ca23901cd0e10a11f20eda8af2e2d6169e74dde31f17c2bdfae5bb067e21e4614f336707ce95daf11c0f0a017999f139dc1ea70edecb3477aff39ed3ed81b2122bb720adbcd66230ff0d8e5f0f35541b1f015c85a67576189bd857746ee0fe790dea308260e1eae88047786681090729515c585240a36fbf023ffe919c79945df00db8ec49f59fa7713ec47eba60c08dedf9c3d3c8793ac4bea705d11e041d5bded2c56847a4a4cfd56102e674851dd3ef62f2df2ebab00db8013eb310bc29c34436cd9433fbdeb8b74e7a0d62809affa053d5ec8150eef70c956d97529f2ae05b3511a411542aed373752cfa667923a87e84d5b02326ad935a2b6367d68ab8f52c83f0b02796e533651a63f151704b97596aaf245def68eb801314e362455de2964e6cd470a1795d726685ae71756e629dbcffd756766b38b6436d7c3b484d61b7a507662f73374d01f4a2aa952b0c1814cc3f0d04c805cf1591b87c2061772741c676612f9959bc30dec0ebc59098b13e313abfec808ab09f77c82f52990618546364d7b04924d9a22c29e99fd6305cbcd8041b35a6b95f90f04e18b8916db5223a4c84945fd550a6b82f4eb7f10c60cc95ab11f5470b16c921f586407ba198b1b3b092ed2d90ea7299bf77e3c1fa4a8e86c870c1a887a51f43b157200a7487b3182d93d4af9ea6c0e3b2c5e174df2995e73f83c676919f0c872782938389b442f10dc0b677323150d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1ea7d6aa6593353dac28cee2118a00cce19afc95401e1ea3c0e2e1899150762b","proof":"7ca6952dcddbba584612cce8b37c6db5f351d33072d574e36db795c56f58396a1043e1caa38de67638cf6fd40c31c29d5a8316b77938cdc770f4f993c609db049ec5f61856e2862a99b3e36d5db74b4a4a8f73a5855b105abf523a77e3aa051a403eb46d66ae49bfc8a5b14960a683140afe14cdebd5b96728e039df227e5a1d2b832d0c4e0036c6f7f5b1d155eca4ebe7fa71f74f91e49c6f5c23a1fdc5820bd257bcd959dd60da69b0994345e6480ebf78997579ffc92c3bc5c350c7939b0271fe40c0ee5343deccd325a982977ae7d9e1fccb9c756741e7d1c9fb37c9720ee6f462dd40fe3cd1163fd11e9b450c4a6aae644355a3736bfa70557c3a4d536c60f356babafd300881806b1bc61a2b3222c6f4dd500402061c8d486fd8a0b261dae2834e94348af05f746286b3bd35caee9d48b8520fef1786a3cbe8f588022b142a76f8fe5f02ebc030ce2e15266055226cda58100dea3ab77822e681e92e32c04b02352dccee4201868b2d3ac1c1006684d1c09e72e8ba792ac0540fcb9556189448a5c3b1df261330b1a823a50c21383f5e031a7f17fa47f1068046e0d35a9e9e2ea5b2422e5dab5d496784f87a55f97a751b9a235b5f70ea62d6737dc80a66cf34256fc557e0daf7006c69ce0e8a77df74affab89590e5a341c06ae6d7300c0811235161fc7613e5b6881e312d304d990064e193c391cafbdb5b494c3a00ec38dd54b2383cda0def432e90a77d300e38dfe6eca68256ec4bb4665497d235f40cfc99cbf66a179fe7a565762a793fbcd051cd65e7f1ce72f8ac330cf8285be42425b592c358c35bb7f7782a92aa8db10e0c2f0e18c1c954519a031f35d62cd234aaa481bd0199bc071391b8e53f51c03113c6fb2696ef6d84151bd4ed22073bf4c5295e2cf332a6e1060dd21cb2bab749c570385064851e3ca0771d207805"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0810145f43b1c7fe0b06c93ca600211cc7b89541c3299c68642b43147b572969","proof":"b2d5a5e1c73d4b45f31409e4919bb5332902cf5e1b8c04b3ced0440b35309e712ae1e3844aaf1ab5baaad783d43de05e87a4c312da307d996c74575cfb42f22b0e32aaeaea49c31c46bbfd7900463b16ad2d3eb930b704ce9ce08f499ad74650e479d530a3ff9b5d8e5aa09b41f473fed7677c10c36aa21f9c74ddc97d78581f3a00f94b9e7cfc5794c36a528ca5cc2fdf376e98de34bca0ea274503290c480e7e009aaeb97f871728c7f1ab6ff19cdf80018aa136756d6fe325340e4c104f0ea63219ed9b512cfe112206c1c6bb578324b3de5566bef57368533c08eea3d70fc2175a4a27e8fe5eddec4be051ecd93b80463da0dad0df6322413c64c99cb05ea0b7986ac6671b3b61fb6334f1af80561b42a880c225ca92c56b1a5f2b8f0a5b28ff02bcf9744ea384b45a9473d443972ca9a4b21ece5c95aa68fc8cf963e06376cd5e66ed6bde5bcb956939447f0eb149322203809584e87473c00071375117bee351d507e3a297f79815c22761ee2bb4dc2f4e37c104ff5eff81e33dc989795e7f1a10ca20f71af19a4213c58c39a2f2f4b37d0ec3aa647644afc5f59cdb14f818436d483346b245d1e7c2b797d43295afc97a85360c12eba8872f0db90646168ba5ae332745aea4a97b6841051c0e0e14cec998fc62bd0c64abdeb459ee4dcc1f02bd4e07ef0664a43108db78dc109345dc81ff27815128c153dc625899082ced062a76f98053471a82d6697fb9945c5fd7698141695906aeee10530b0b4e48cdcb304bab0ab21429684178c8c5c1872f56fee2abdd4891ed28a332d4a337fa08ca3f3edd2b5c81228cf6f948bb098f02388b17a619c385be3e114dfdee1a7128218f1ae77c69c4ec9c5dbb9ed77a8f02268715a339d2638cc137cdb5310ed6aa9247d9184e304546947ad62ceaa474b00d62c5e92b82254c6bc83baad305"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"005bdf7658c3289901cbb6060bdee66c76ac65909a64a50beef6f166d9f46e7d","proof":"08a8c9c14b5006c7254c654ee102db77fd7df07f817d45233b4373a7bf14974ba091b53aa96c3a22589e0570faff780be3f23128e516e7cb147de6b86702f216a03f9433cf46a367c858aaadcac47bc3438ce1cb9b8f718a9727844394d0bd720a2019ff8a9fbf0b5b5789b0034fd2e5574e707d06a2fa768826f0fe1695ea694f3ca11fea6a04d5021ec4c50c0097e0035a3b6422c96359a4046f5d7a34f4051a85f6c64383666b6d919ad95ade0f0c3cf5f44c6a3b3700a66d15cae799a6091a69f1193fb71e42b303075020a543cd4242435a1786838aa359c897936df8049c9c45b428c04c131d75cff3676e175fd43f0164943d8c752b1ced4ebae4a721b233f51fac7b6cc26515406d6f7b3e2dff7dfad1ab648811248116ef18921b02c02e12804a940aeb446d680b2558cb09a33cd59f7700777ca272c86d6f0af31d780ab19a7347997803c42bc6a3d2d53d8e749bd665b3de7cd414df2768bd3540d60a9d4e00f5ce94185b6a47e5782d071c987e9351751e808afb4d3c3eaecb2c66ed5ca6a4a2bd4efb02911d39572e31a68004dccbbe038a0a01f7580478b65f2c7936d163313c30c860bf8d6da6ef8d21630ba58940fb2d709cacc32a8d70731cd1835901fbb058d2d4e6253a4e254c486ad1a80d88a1ec0db6439c9b6f8e428c25e29f5407dde7f98fb1c9d433ec1b757487afc71ce16fa650554351ea7757c018bd824e6c556757340c511ae66a1c9bd55ae935c7cee067e0422659af1a54b8d61647235c3a885eb04c4fd5189139e95bc23caecfeac9c4199b953f4f3430ea0a62edb619c571999e9807df1be066af5b6e646088b95380cfea6268bea87ebe396ac11182472ff87bdedd3fe96d9918e28e474f8dddd11e3d1d9779c24a08babed033e0f140cb7c54edab632b593fc1147e7852a2df1b27d39c1153da8b01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1867071e5fbb2d66da859568c252c9f2c03fc94ff97ba1bc2ba317165c648d22","proof":"26d0c97221e17ad883dc0a628c0b5aed83df8be3fa85c29ae1df4266cb414c6d5cbe032a81f66976dbe79eb697b4a57f176e2dd97be5d42aca0dd4cba9ee9d5d90f59b8f3673c89b51047e6a43f1f9943ab6b6071bf961d2f6971d31457d6a5858a9ba465cb09b9fbed440d888cacd1c24b9f0e1f0f7968a61dd2d800262fe05c9d332124af388dcdb411d17dcfdc447bb9558f420f0f6c5fdc2ae38db5e730df742216b1685d806c6b969f43e2685e913013cfa523121fdf699c1fba486a805045875c39bd6ff3d8d84923182b0784cf154ef2b8deac1ae9dd582b269fc74006ed9bb47b02c7aae604789294ef7704f0a4a7b99f1f525c690e602d6c31df45812cebc436be9cdc8496f63b5799a5d20e93d877123f6dd2dcc161df68d8e9f0a56d2ab8ec3047ddf7bb71274610c63d70b7658b234e0d0c3d32a55f6c2dd3528f69acc3e1d88d9709a98f45fdb4c4d7d9c9ad63b508dbb03fae775189009072cf6d4829b2f27861c2d75ccce7598aa9eca7e61c2ad4867f059d5f0819df836249c3452866a7af41fecc1ef0f3f20b5be02e11141afacf6c2be565fe0dc0dfd01de2f2a534b150f0101783394a13002cbc8b8d703387aa1590060a91c9dbaeb036843e8af47bd67c23f45f1f2458f14fe8e14958c4d9e09f38b86a214f3e7b30f843ff819ebfcc57ab3b89b7a47fda0cf6611158d7fc0154ab6cfb07833fbc23c3238ec27f3e744b0a62e149ba2ba38b2bcd2a12963fc36b7cd6141486cdb692d6efb8f29ca61ba9e72282d551b3cccbbd38e49a18a0ed2f04545fe298a3ba745ca4be43a89f1b35c8a245fd6de82411314cb4b3c650d77f8e2437cd9a0127516785fd7d3c7ca3cfad30db02728ccd71c0e62efb47015ba3ed26ca78c8882a502ca0632c3e705167ead39913ee2da57777e5c0efb1b5f37ca8b87ed7d17fb570e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"34db4d37ce7ec0db1554daf722e86887588bc9ce3d6ee045df56d6609224cf3f","proof":"861e2d89666459594fbadc42ceb4af3988a223459e151bcb0c4323baaa013f73263d6d6255b48f00ea8cd6b7cd97e4b6070ca489765658ec145b6333c71e3562507482f4b142a3255c0e4b67c972a490404e505cf0b7dd72886d5213015b8f6a468cfd9fa787340ff4364e4e4d70cb4bb45d29559cae4a80f3bda50c4a9df26dc458468d20a691738072a92926162d719ef74e5eea913698197a5c6b4ab5a501bf831abcbabf89c8520f15764a200b1dcd6873c52c2a7bc01128e8b3af483e0959cfd4cfdb4b7219df99d6d70cdd4423fe87eab4ef3638cf21016a3376a7c00ccaf793b67968a6bf36e71d9836a05afed762bf1bf93ec9470d2e433eb879ab6706f47ba125a6abc8b8ac6456d02a48c62ab43d06054e1c387da4ea0e2af4326d602094cb122664a0657527aff198605b59fb33a678604110e37877350650f079e66912caf5dd90cee97d997de0e7de826883bf4623dc2a5a578369681bd0811c1ca3de072b63c67b0883fea47011b37699497f5a16aacaabf4b91a1f6eaac208e45aae15858c8bc23cd6145f837330fcc6b639b6bc8b0feacfa744c36a12636d8e205640c4ad3af29e5db08d7bf180adadab4a2b34078b55e4ca5439114d0e56d05132ea4eb195039f5e3cb25345f50df58446f89fc116dc7e02a5aff06f2041a8549c760b95a0804bf7c455a2dba332e993e81c1423faff5fc14ac4792a1c7f22f359f1ac019d2b08cd09250309087af9d93585ca244237c1f0935469ab700462eab43f7d7dc82613783e08a05f44ff79236116b8cfb08fbb2a1c6887c1e70090742f7bdd161920337d3d59f94dfbde13dbac7f8ebf2385a42cd822406a20070ff73a00f3adb4631962dea472810689500e29304bd8ad1e5d1e74178aceee0e4bbd94952a21646a93dd22b7d975e50a46163c7e9aceb3903ebdf9103c9c2004"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"884d211db695961fe5f22f34e3a3d84399e12c8d826af186e5e37c31f26bfa10","proof":"7a2ff496b246c91ba9456515ffe9368e65dd967dcb69bc79f27c8e0e420dfb2f902ced7139d6ad237322444915ea2c1b7eac1c84e93eb6fd5adeb00e36f7ee42320446bf35dffeec877c513b77535bdaed63da7399b56dbfc9ee32995e33214b2e8245c9c3d70ca685eb0bc8640fa721602b9cee82cd0658cfd83b3ca5f36822ff4642cfa3341a89b6cb58dbb3f1aea337109e3285fddfd811d8a9c7745e0d0f1ad99186db1495045f575ee5826065d9a9d151176bbac12c354626da6d0b770b5e4faa7c0a0b37d008611d5258629a2c7c8f03b19bd803e11bb253c723b9c90c38d4e1ebdfbda2d60552009067d23306ab111a91197e652aca296f8693f4c372b6c25d7c65763c1f71c1e8b6978c04767a15e36757b2350138292204df881f03ec227e479abba383d6a011df0e8636942d43d45caf3c6d239de2f4ce62ffbb165a37dca79a34572c9fa8771243b9ace43229a2b1c90d3aaa32ce006305bcb721ca879f3f65f8fa5d5143e713e898c4cc55aa37a22cb6a1cc1ff637f682cb583e66fe97c93905ff586556689e4f1175f0cdfda988610f0e6ca019653dea622465e23ead045efb1912342b34f103990db821272067cb0bd20662e6180f0fddbe44649a6bf6957420e43f7214c53492006c5af129e55737cc4e0f752ff94df64a73d20d7cb32da9f3110e368d36ed9740bc744b8aec0bbdadf224b7eb07c8a0460a1aa0e1857a6eed504051fc5e53a578ae43fa69b137e430b573378891e110c162503de2936f0387ec3b86209afa2190d89626e663f9d3d638740982c045a6f82a3edff720a9ac4d08f3db211d13f454be0f542c0889c6b3505dfbff3c1d0612549b12cc6e575baadcef56dfe671d17490f83c53a78ad329ec0e8e259d564b580064b0e7f4262cfc086337e45a3760b0cb3dc05b0560b0b0454169c9565ce83102"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"665540c977a7f827e45122b320c64fccca506d3b66dbbed142e19c7bc9b48c14","proof":"acb46bee2d5eaf225c74b1faf510f47f67138bad389615951e7ece4567b4e46aa6a43834e815e8e5bb150c124eaa658e33196eadf3b3c0def5df4b1bce50ae1b9aae18c07258fe9f0c83189eed01891b9e404d9ba8d2293012a323ab4e71ef489ed65549a8998c81eb8f9c55da2fb65b89d3b7897b5e84d0fee373d578e4bc1299ffdc90c247c924519af7a6705f26633d1079e690e5929d489f8e8ea2faf5071717b7625b6d256363810cb2c05ee5e46d27f0823a620edd8117ba79a0bcd2036c7734cc5b44f235d0d0ef7cc9dba09caa656b3c1ff4f84a5629e4527905350f24c1f9259d40eb23e866d4b83581ba23499b500d536a77f2147ea3cb6a5d3b4fa0505d58151d68484bcdc86ed09309381ee264bd248443ef45e9d1da444eda777e3d1323bf83827f9c9ce1e311bf967b7e4686636357ae10eb7617abbddee323086496518a8557f473b66dd973e23ad908e8a5b5f96c55cd3e5db7098abe886a9eb301f0e0936b4edc59ecd8242118ae269e6ba5743bc27953752869d4fdba07e415255785a3c2ae7fdb242b847c34df3c213f45114106db2b0ffc39cd36f37ca21d0e5774b0626ccc8502f0be85b4d4a829f1d4c51104db4e895fb627528f31ee368942eeb9aa6a55dd1d4ea2d1c54fd1674dee29253f700b1f510e86929372187b6d9113843a2b398f32d51de7c26bfbc8ee7fefdcff6d6aa10b6ca146805b56c67dfc864c045666412eb45b186a8b397fc79842632d95a448ba7f2f1cbb219ed7fb45a8b33634829b36a7c90c1e3608f3f1472808ddec5a6e6c354c046766b260e7ad7a0c7a4a05d207895b5acf7a1a94a4fed9c39e94194b185ae000036f5a4272a33a3652018dfe5bac19733688887c0d49716a0035efc0b96d803b39088b8fd23d60b65f2ee7412bb3d0d2ec87c4433e40d1e4f62cc5baa9cedab85a0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7c9418f41f5c91e5d63d090a9bd3ab0780708580709f64fbd45158970b959044","proof":"fab4f5aaf81823371ba7424b959aaf27cf69454513f51458dbfd30402fdef56184c3feb902f4109c8ff98d3a511df7a892e589ecec277beb8a1e8e3feddfa1231c4367156ceeceebbca64f2028736e0f58a6d91b9341beaaf6b5bea9c05c50481a7c8d75565b89b42a0ef3c910edbd00c7e804d1f53bed094032c349ef599e0c3a91b9818387c1aa41324bbc551a14d51a83934678f90e1e59b1f1554f8c6f0396ba1fe6ab5d770cd40d9e24a43e52cbb2b15bbcc3aa446846a2e747919b96062824a56f9d8a2036931d2021b9afa7f4d8b283721cfe49b1ce603a47b62c8b0758fe25a25100d639630047d5199d1ae61a374b7d74a6b183b93fa0f1523b5a51745a5961bc80e2dd57b11eaf103d61699970bb59c0ebb9b7aebe60122826715d9a4464973fa2c97bb49a6cc9f1b8196524ac24aa1b3231af7243bdb792b53420ca77191c6e6f57082c0e3ff9f2a562e12ff472577a5f2939ec911b9019e2764db8f757e075782d83967f85f9429c6a465bbc4bfdf47012a4dfbdcaf7ac41da5c1eaa3c0d33a565f774b89a16377e84b80dd76aa6a9839572908e5399c69b9e0c00c62a2f7ed30bf1f7f901bf40b27a32fb01282e93f97a8241a8acbe9f26ca0f82514263d70610520ec957fcec2fd7cf8519392a1fccbfc4cf648d83acbcaf0f6498d3c148cc47382dd06a7765376210ce20ae14342c3b4218b1a29612f183653a6b7667a273afc1112c381be4b87880c2b38583f235c382977b5e5a731a2b518c0d2bb9ae2b91ba14e29eb61f01916d98c377b35e01c53dc69cc471b0ffc47b1674634f4ae6a7755fe20d10ee6fdc5eaa0109b28501f3911b16550ad8f5d50bc642c9023ad982bc002e826b4118722ccec59b971728fb0d7ee3f899639c170a16112b33db310592c95db2b02c73f9e1a5bd270ea41f60b0d16282e776ad1e08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"82771f57932913fc43c1046aa17d5932abf47318a041ee0b21980d38a5cb3b57","proof":"26386012b689c54492202cc451a7384779821c7ad73513dfb28799ace99437175c2ad781bc07d996bcc643480118ecf4279e904f5da2124eedaef5a9ef578458248346a589680b435e8b3c3f043de7e7815d06a1dadcc4fd536dac39fee90e08aa71d513600932acef5381f565a1821a1339885c164d23d064351326a96bce465b0df3ef70b997f5358893477a8792155ee365568e4439d34c9cfcdc06f96b0e7cdc54d00faee825a697d3952d1125a36737022bb440e037719cb658dc8eaf0aa3444408a9cfecf7510aeb10a44d9450451a95c622e6cb9a9eeb73d43309570c684c449542e8f41c36fce81f88616b525a2e58c7850edc90d7698e9ef1de397c22756047f784bf869ac34b5d016c13aa3c090f826b770d3c7fce49326d19467dbcc8e762f69a190c56f99ee10b9cdb1397960ed2975623706b19f08fa395030fd2150de4621c07a2ce55bb6a5ded39a2710ae8c879caa169065efa1ad9b37d55eaaf2949c5c55378ea4fd3c8c2d215dd9f204232353c97756467462247ddba571c5bb824fbf79b943ffb08117cb63e3332d4dceb289a2be8f0e9e3fc6205186560ccab326fb91c9fea36b27f62aff742a97b149f73fbc1fa17252da72ec54c19b8dfa20a724c832fa68538a015cfded4523b591659cf618e0e80a9ba884c377b06af8c7a3e7d12fc5ad8ac72eb7f9a0cc5cd19ba52c9c30325a6e3b659ec053e5e8cb69e51dce7171792f2598784661cda5c32d8cda584013e463876db704f1aded9921d3ea795514658c6621fb5aa2565d01ff0810fb1c7a0f0f5b2a5c81a43702741551528c164722eb041b2e9e8eb4f991174c477712fe1088a2139d66f7cbd6dca3e9b8ff0ba33685940e7173b86c896dddd54890e586400d87cb3ef7e0c4b3ed174af2208aef6f73f4a15736dec7bbd85b0dbdec799679e84bc95f13a07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7ccf61bc86fbd71815b5b095ecd0ace0f3afc562e7489c1a6d2b47547e96a628","proof":"3284445ff357d6ad460d5206d9700dd869765d99558a57fafe413069b2806b50cc37226c1992213c8dd4f593623096337870499116d3801550a8c1d2d529552a6031c23106a428976b261a6bd560d7a87d1fa56b702c5d6cd1de6af96c76aa7f828756bcb9c5ffc97cae458161d589452cc35aa4e1646316659edb1554183337636313a45a241b790bf45e7133092ee29be973f1a6edc90e8073f034587a320d1aa2893f497a8ed100890b1f77bf16549dc606b24dc1683e265238690501c700fed26af96ee300e43e585ebc8232c12a41a3a9bf5c85ffb8b222b2e4325b65051667d5b8c05de3c834c5105c59f3b71e7996aa5975906a53e25aeefc7f27855bc80470c48ee582d05fa3eec3eb3d995489f0202f19df097938636800148e713a08f9c6029ebe044fb908e2ee0ffc93ecebebec0a6c4c582a73fd0b68cc9e303d14532750731ae56deb8a6c1a11b625fded9eabf1035ab1c74b8d59b4457c086288a9204b42f746973b784bb086b1f2f3905a4869a44e5d5f814d3cb175f54118ee66422592ee8e5ad121a2e441a692fb96af52f2f6c18763eddd96d122743431786f28cb72326318634804394ecd4daed96133b326840c01eb3cb09de35c17582acc9ce01432fed5b0ca6069b551fd7fe2dc7d178cf8ac7fd07ed0c76fadca1f94c19a284dfc05c754b6ca7fe3c022acc9b64f640b520cf1229db9c93d48b10228c957a9f23008d905268071ad98b90ec1771613a7ea65089c06401f7b0f350454e9966fdf6da7dbe8ce765213ddea48e1eafaaf59924f5193627bf2fdac630e66d06633a03ed4f844a8bdf766d8a43ec629b8267ede67cd676459ff70f1ff7240c220424159c57358939529ff923d4b41cef5b94375e8d07bc475cad73bfa0fc6d3926bc5ecdceb83221a4d89009253ed25d9fd0e5da4b8bbb48e3174a02101"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e68f37032da311d8048d75af174105c53df49ad0448405cb892bf5f0b85a0775","proof":"ec8db38794088735c5bbdcef251ec854977bce47ce3a920fb5ac143bf91bce2a886dbfddd7dedecfcdd38c0703a433682ca0f1614ebbc38e591412075f18f428e2caf9138ebf732461e3826d51160517a7699be23177381fdeac3dc062f69c25d0d8f1adb7c0022e2bef34f3018335fc00524521f02c008ae801dcb55c5a4f21826f6b9628bf29200492a741926ba6d85f5d41eb665836d66ae767f8fec62c09e26140d69779aa8e8cc3cc0f903eb6e768a84664280087c64b0b526ff3be2f0fe508cf6f5b89eea1b240149ad17d267ff76e688bdfc12ed43456614958246c099870683a84c025003247fadb7c38c46f8da3c979a5774324e2d2f2b4b62b09005627ffa7396e6df1bff42c2d2213157300863666d1c981ae40d76e2130acc2175a70081fcde7b5909035f8ac607eb9814b93ca6c073a946cea09fd5a304b39312a033a453e1607f4a04e287a5c10df62ccaaf01dae388328e8bef3365ead3b5e48e6d4a5f842f4cac77f60bfeca414d8be413d4affd18de5b6f57e8934874d175a4b65720bd555ff531cf343e4dd140cd989d667f359276f439408b45928de6ea8ce697d35bebf1e51ca76bc23448edff6100fff308f01fcd21ae96fdd25470d1c0bb1f62aaff84ec75e75303dabb953f239eb1e0b308f8b50b86837fadd3013ae1531ec965fa06c5012b99422d1805298e12f7e0257a343e137dd4252dd4f2e1245c4966c1e00a8fb2c20b2b14cecbbe3bc0eff50e3010204d0e7329470c2445c8444f4f150cb244e3cecb1086d26302a7766d54c3c8ff24c59b30409c9c43468f229cb356ae324c01aeaa99021f48685550cee729fa61f20c1a1f6f6d9896dd045f172536f8444543b9f9551840896821574ecc9880e13b8da19add8809c0aaae38044df40bc6e63db8e98d9aa778470ceec2a8aa915a4360a6ea42fe56e00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f69c6c7874703a216ce166e5bcaa9844baedb26f679d3b2e3d94d55e8ad06f13","proof":"facd50f01ccde604a032dff08cdcd12037262741ba71c2165fe5847bb297fd6a3a3f91f084d616c25426a6a7499b28ab680ce4aaeaa4fb0d85d4611cfaf55f368850ff41a7fa84f3a51b0221ff2e10872a9a573bad77b381a9c915729bf15f767e640f57632eae775f4daa1399e50c9079b1208bbe0b8fd97e44f78c512ed043e734d86c3be2ff92ee511acf549c2271f39a1d94df3284af6a9bb61cd78b7b05d23617dc1e7271a82dd6a114661f440a455680c028feffe6a46c95e817ee2104dc33379387bdcae0f352804b4bd16c93b3892ffc3c79c94859d31aaf75afbc0b503534ae545aa13ff3ca04dcef1dbc78a51b4983bcb9cad20e98652440a81a19fe520b12d5d0bc2d8301ba55a43eed5e462b202597b13ce721e7a8aa90cd0a3f4850712c61ec69449d8b85dfd288bd3bb463eb8ab597aefb5fb036d73fcace4384cd60244ca261644ffeed3b5337ab6f531e655965b5c3acf9b6282ea4ae6667d68df6facc906a3968aece964e2d7674717f4c27112f3562cc873e6f8ea9d46760ae20913e9196d7cfcc4354db1d6ae735bf44ef72e513f1e4ae20edc5d9944ec2a9d05e09fd97dc406980afcc7ef48006ef180585de07be505e7b59cd09cb3dc0791643658ccee15c807a442871cbc861e2925b9d8307b81ee82be7c7bada3b70604cc668ecff35872b84d705e7434baf86194aaac8aa51bb641514df7fe8512058ed08e3c90c74804d3504667bd6cc663a7ede250d8c571e728113f7a9d62c7c18636cf27b5390381462bcc9f5e482935158286139736cfa134a168b639d68a45c8905001807a04d94a2fa1845fa6ee47dd8a18c555a8be5d02088220bd76cb22d1e1d85e2abbb9bbfbb360912fb95c8d6b1e41e53383699074a3e47e25e05a28570887a5e445e4c5e1a668556b634dc6781860aa4f37d7d1afface5421a0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a466709afbfc5d4560c3ecd36cbb2af17445d2e92d3f9d79108bede69ab20176","proof":"a0b772e5d5fe143fc913c48f60b7f804e0c6871e3cd139b8bc703dcde28aec77c02e37bb0592f86cd037346c52a43291163483f796d07ba1f84fb28d11baa67f2c296dd4b9a661b09e89dd4bcda302fd615d84e71eb4781cccc6767bbccd5054cad3f18a644dfa4c75b060d852e44e9ccea663dfc7856f638088dd67750c0b00249b9275c947e1656273e7085576b77af204d465d68a3621dd44cc94eea0580391355edfcb06e9ddbae707dbf9f88b19e298f459aafe4061608808ea59013502ac66a21bcd7e2913bbc4bb88a51b1707d3e6251cd2de30447078aa2198831e0280ec701fb52d40e05b32db8bdd001f8e3a0078a3a3718ca4159f157aa724c0113e9ac0699a97e6c2767f506c1fddfcc86c8e7d246990401b25ee75bdd6a06d7b848716a537ed3295fa242917360c205bc6f7a6894b0d28a36bf485c8d7b51370c48a16379c28dcc39d7bc75886958a487c70d5d417c710bf7805d101eb6aa305d8f344dea4026539065f72edfc4406007dcfd802d95f7722f7080c1cb2837a559e724658a03d42a9304af6623211c5648b8380657fdfbac56f06b31bfc41be2c70690c910f580dc5182ffb62d005fbe87a17126959d5a260529dfd937880a7672e44365f5a4e48c2c9101ab62618913a50cd6981543b0a6447bef754afffb021e6f48315bc47238e7bacaa7264eb517176a35cc49debd61ea4199f5b90144958ae3ffe9ca57d673fed50b0765cfd58eeb14cddb4465cb65b9842dbd60f56dc49b29602b786eedc21a3eca6929b1aace1e4c61a87d79bda4b9fe9e690e41b872cd413fab0e7c7cbdb3ae09376b1badce9488eed4705da062fc1572dfcb0d285412f3ad12f05e6d90f2e10721ac0784cac86f3b94acea8226da50e0fd4be614600a9fd7ceba692810fcab84b0b0f962b92d2cb70582337937b5bb496b34d451f05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a04b43f6b6b1f6340b8a54be7ba5d1223c3c020279cedc6d52734c402e3b411a","proof":"a46e460393cdbde47bead99e02b21762dbf3ecffdd238ec165fd579120ad74420086d52c36033f367fdaafc5c114a67a3b8a5a24c891f806813b24520c950257eeb54531a22900a2ea3ba20543001cf37d3428cc195002cca43d94967513b52904180a7e7c8ddd06e0391252ba736aaad5055f3ecb415cbaf7d269b09deb78085122494b8120d711bf58547957905c1e905a3dda1c23305062680603e30dcd0751a5686820f3baec525e0e4b5fba73fae320591c1cd2378ff2a801f703274f069befba1d7233c108e67b1d8de9e46ec6f049ed84bf985f9bfa09f0c5549f39096c6760875ad9b14ebfb0ead2df2825ec68ee8171b5f92ca94e9e4b30e3f28c4e5a9bf0178a633bb00d56fe1ac51d40e613416f58b3cf0a95bf4d4f5f1bef260dcee63bb170538a1f0fb4a0d63152764626a75947480d521732cb84deb274602f4c29530f4b5b478aff9763df094d28e96101349df6cecca8f8f7cbdf8c8e427ae2a2896e2b5f556f54d2af6f497dcf2dc766a008d571c54756a131f8ea91303e30f360b117fbbc98960a6d9837403e08ed206252da932db82c40ee6a5ff0df6a9a306da0479157658a80d2b7ac8ecc8b2354a98236df87e0115a5e8b85475768c2ff587317c9f2c923368971d84f44bcfefe5cfc5ae5bb514fcef93f58057479246de1cd2edf9ee021104fd3553389ea283c70c0da10272445bb91813d0aef5dd873be44015e1091233c5507f48dc1a4c242d5c1ed751c73df9e2274fa611059e8b9ee52650336db0abc5c4e457e5a00b18afc53bfe7860b01b83f943e53121fb4efd3830b1793472706289ffba33758beed9bbd3fe9adfcd90a967fab318c6f73e6ec154c1252366efc039974a481762b5cb589c03b8e5506addb4da227b2057f8e39c30b03238eab48bb0b913dc2cc0853d89c99ccd03dc70a8cd0c468d40f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6c1b2a86ab489760cd3d62e71df069805c23fe87a2b92c8185ef114345794968","proof":"743398dd67aec03539d5dc2658a7b69269fb9a85e5ee99f8bb6e886503ffbb00fc68dd7bca7b660238710f1bb4a2a25ee74652a54cdb140901b3715c09ff022ed6e81ca46206bd0886523c96a6184169e05ab0e0e6a23dd67c24658988036337523689c23a0e00196b1227120540178af06e4e7c48bb6c30859a3033b552857ead8f295d4a22ead521dd128143a80207ddc8c0e0d1e3adf6521371def17e4303d54851dce7e1c00e93a874e1a4c6560b288d9d3ad3ef2e1954540bf95a020008d62c0af080154b8ec453b44d9c1720964022ee8ab5569d4371145b7dc82a4500e4c7ec82c2273c514a574152c542ec57d690d2950442a85eef29f14ad219bb17dcbf99f532cbf651de802f9cc5e452d946c59913e12e6eddd2d642b6a635787eba331709db5c8e2ad2cdf13b30235ac1a20271f4f0f2110d40f23ca8d9956916ca669af69587316c6d6292b531e2a9ead2b12c8f2dd494d26c14dbf945e7356fee2437ddc43bf1062cd28500f5f99bfce048c1ef79bc030c6c9ec2ad08cb9d1d1eeaa3d7c27ff88ef82f109867c4c5380fc74afd58ab5491ff29767ba8831e476e06a2f5284e38395fc5cd6832a692e81a6a9bcdc662624e5078f5666761a0121aa57ce010eb7fe46f3765871da050369d2da536c5d472ae8012ae90a372b67c063745b2cc7aaef639512b3f542985de59fb58086456c9e1f06584e3e9129060fe8da40771f662c76ba6e03b505c18c11c3325f677f1e8649623c5807b54691732f4cd2e33abfbf119136e13ab11c39c8a7f33e8a45e234d50831415444ad870a06ca2fd3735eff9fbab3aa78081fa5dc51baedecfe6479a9fdaa4940d54f8709ace6dcd6a8b0a57102f5e3dd4561acd5722c8f408ef0a37a6efc845b90f990795cac9272a8d2482626bf576e03550dbc3969ca25524303787cc65b8fb5cab08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2690afca3a01bda48ec3d340d27738c1fb302da66d05fe1e1688c70ebb157375","proof":"acd2e693cfaac97abd825231537fe2556a1fe33898ac65ca637f1c1442eb1b48404268de4e5895ae80ccc35b1b17849c250608f6becae907ebbacd6195a2465d96884bf24606ad15ae595c507adba17864139e432a62f412d1e0338b1acc7963681a6075bca8cdad468b86769b81ee4f95dd7e7a73118236cd485490c7e40d383a4e3e8ce62f9ce5e871ea305399772fd8938a6e5d4717a8411da5fadb3b110a2ceb8f2a7489257d3a8e716762ffc43a6e2866e3638a2deb226a2e6f5028d904b5a47a890c2f3c60c944eedc33e9ce186707472bc3a10bf62eea6e58c5cae209800328263af2cc8eb83bf64fc3ea8cfa1a3eb10c972b3952d53ffe2eeacc8669c2e3015e126a1978e425d5f8db7589185f799d62bea615f4ccbeea9efb1eb9506a736f935dccb1b775618c1b3a40d2d20f9d0f6aad0b7563dd89bc2d50d10525843ff75289e538af344304be7b364c356650848946c3c03aadf0bfff2eabd1151873e0ee2002697f900158ae768eaa4d03b76e4a4cf95b8cbc48cb3aaac45d69083c0f25080c97efb47ca371520ec8837a9274d35f55f09cf213c471916b6e44baa533ff7f41032d65cd18751bd9eb6a5a89b9035d8fa9cd61819eac4e9aef6c9c879bc83d646a702e7a11c5849dcbb08a9f14061e39cade9d762ea42230a67dd41bfe34c6bbee28e7e3de8a855fe5d1d86f0f1f38b88211ac0880a0a2ee4f32e89445070671c744daadbec04f44ce731da9aec1e1f2f3f01922daeef9564739b293b4537e7c21c99e22928eb30c9193976a3b0b19d448e97759769515e65d3dca90150d0b8223f0e84c2cbbc95227630b9447b4f9f5795294ea3db5ec8e911a237ddbe0c7def9db338616df48c549db10ffadf0b540cf9a10633479af21a80ae0a3f8f1a8612773693f1ce15821cdecb3f2ac622e627475fbf9f0c984ab770f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4ac67fbe3e02010a208fccc34627eaed4b5ab16bc57d28261da956c917b00317","proof":"344025832ef4b0618a7ffbf3b427a714e819f90fed90f9de1f859df703a8a16fa0c539b30aa25c36ed315c857fb30c7a7d6095aa6957298e25048492c2beac71fe6658554b7eb42340ff9e1005c45b8cec8dcd5939daaa55b215355acec98843c8bae36e2b9f96ad0a9abc597edf94b7b930bd18fc927868ed289d50a60f6631665dc159e05285627b3e50812262f18a9d0602fa618736c302db741e764fca009926452ce8a0751a3cbe300b07f869b5bdc19356589e3da694e263c85b5a34086b4b8ddaeca57b6fcb7f2482e90295cf20c162a780bceb7d833c3b6a3d21600636c400f5a9ae8073ed9a1a63fcebc8b13be704f26e99b2d73c88fc01ad680719b6ca5ae006383c2036ce0a9ef0e7243ad56c6e84d0b064660ed16581ad7fcf3e02ab093f4e99d2b2fc9b55d4267af44fafa77235bca35033809f338b6bf6a819bc1ecb35f863b6fbc3e029a4c29142ad5c37a458b3643affa0710f322b00b4602c7d903b8e60392c2a6436f834b29353d6a64ec9b6d6a93f6e48535c5119d642643a75d2fc91929a872b57de9d6f028314b4ca91c50cfb30d31f08dd19e1d305a4f9eacbdf706bda90d191799cf9ca6240a9737b42f2b0db736c5cc3a9f71619e6430358e0e985fdf24f7f5eacd97328eae99a5893c592dc4c6e17994056a11cbebf089611ce915877c35dd4f02134abaf049dcf85f94d63ba08f373e605153d62e97f95caeec1096645882e116d17807cd7dcd492111b2ce7d405e5ace46c491473d9554239292d6c728602dfe370b6b20137738fe0855047c4968dd5dd7151b088a7a0d0355f1c434d13db19dfc376ace29febea4d1d4ea6881372718acf329a145e2a37d8835ae2300e7263390affcda41bd63873ef324bd5d1721011920740e6dd1223374084f23dac282608d0576a35ad92f5916def63a7dd7ccfca9909"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"56805be7a57fbdcc4acb481793056505a37cf4e8ed1482645520bfbdcfc5d225","proof":"62cf8f17df290b2672c2fde692de21ec602595e15f6475f66aff1f58e88a3d384ca9277befc92e06b90d6bc06fab835b9408315756e26e25728e7b3caeb555629a7c6eb0dfdd5237da6f49415b6d2c834c950c7c1175aa7e11a9b3fe8d737454a0213561ecd7b6c8ef172eaa5a162712f3fba8d4ebc59ce33b0e1c92aa3dd701c4108d5f7788961884b565f16b0264def5639477c8ac8eae3a49b6cfa463220790ea2ed826343118a3643bd3d1ae209724e8101dc03f8aae64f0e48e0651db0bb4213c7b7aeb753ae5ab3e31eef4e0680c2e02f4f42dd7af6bbd89c8cbd7ba0624b35496c009d9bebd3e2dee20e938e135554ce60173d9709d6c0a125119f04156736cb4a10eb2b206103bfca39064d3cc907ef89d0972a59d92926141e739414eb905378c5398a26cb4290dab61faade49d8c6360cecb1e25d82b526958983aa030e0f2fdbd02a4d544908af70c1ce1d85ab5cd90a0daf437a11c230c793719b2b8be87fdcb61a10e9e7ffa443aa8a038437159530a2e1de143053318ac627862fadf029c959804cc2056103ae20a0e23e8b02803975cb5439731291c527a074655e9413d69f5c08f4e21892510e05c36db191f1d0b7ddbda65a4134efb25773ab473d1ff2e69ec0f631d5b6c4f6f0a7da902e83b528a17e7ff9e4d53f7cb3a2a948b1fb5c2882f38a97e66bc2c1677feb457dcc147b015d40259267a493c7cce70bf2271295d71660abb097d0a67de77981bb7ba29b01c288e11a92e4e234e303cbb7b4de4d1b82552cc7bc81e0cd92db788c2758581e9c5e6ac62d724f20298e2fb4738c5815c9d0f250084f2c5eb993e2b1b79cea82ef76f1e6733a31f5c22e218922c125a3b1449904bdd209a035206c16238028a1cea370857f413c80d7b51ce3d4e17d4d0db138d546c7abafe8e19cc64fc6724bed6c62cb658732e0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7677dca1c3602da6b48cb63d51974803580146066df4ee827c9251b55de0a920","proof":"dc1b62d649339a500f93b501a8cb39eb45d6afa2b05a43d57c94d6a4363c1b1a382182bb1b64ae672e5d4d70ed5cc598fbd95de26bd85f4ee58a4ebcb269317f4cf9e2e9d8c49a7e52f22bf7f7d9efc0d0a42c1e0cbc586fcce605af2431473e3a2df816c54c33e42c7e7fac09f7e544f9efbc2a73329c666e9e436635d02847f992c655bfd95cf9ecc26b82059aa0f61ea6fe463566a7464048750e2c79260d069d9364d56f3c35f22130ff51ba35a36acaa0a3432ba7b94215eccd21147f05af6c6de8cd11351afc5483b6601f82379f629bd96b84a39f3f119d9e9003f70a6614d3a50967c003f8c034907f94b9eb48788f60e18bbdce1b2b133bbf5b5840c8fd6e256659c3ac4e31329357d62569ee65816b9af35d9b7c2afaf933ddab1de4263d880ba38591851422309affa2d4e31734d158a412f621697c00acfc6c23f6d32961232f8c9f306354a71d3fe908a33d6c6c7bff8b2284d4e4f8f350735bc2c23bee82f119c7cec41e7027236f6e963ea6ec6acd9d2eb6f163db237d0574ca1f0df55af600d1b22c586479e1a7399fe3c79d1da770fb3e5958069497337d403e1874ef486133918353c05eb32df6914e3a6a87173d2ad93075d6baf43128b67d239cf8b4393a93d56edf534b0d557e863fe0f249a9c1e2f5b9597014b1770e19d8874ad372d41f854dbf5f132056d35730480eab24dde10c401d133e952896ec958c922f02c6903df7e398898cce7fd6d3f079b381d84bce42ddc05806646cea92ae2fcda4727619c69f48167a267e21d5ccf4ee8acd51ef2f57566b392a8cd5207d46a8065c48a8a296173ea97af90675cbad32d86b5ef1672c80a6c06b60f8b2580332cebaf7ac3fdc448400e721c36d95595c7a3bacbb676c75853f01e1a01a406e701ac3991b13cbb3a3ab7538319fc530f4b21131700f634fbe3a06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a0e085a728dafd563fb31f6433f293d96bd5cc304fcd3e320a84985848e9292e","proof":"7882b9e6fac89ac389a0b45e6f94b29737480af041300e1a2be3496e11db8d451ee46a840d8f3b45908850effbbbec53a63de2e5acac836da5e5beb003802928be28835ec69d7fc0830531af24007d5b4b89e66bb63cef65f8af2eab24077620dcc289dd26e505504fb8c34282a3efb6c766803f9f4e203a6799090c46b5567f680f4063c4596b65f55ea9e19023791f5188aaa7c33b4c85b216f9337021670d744be2d3c292e1f1fafc6863c144d7790cd5d9f8f924ae03c8215dd64cbb6f0bd30a073d99d281c19a709c83d23c3c2b4bcadf4b91f464daa6ab9a9ca2eca409d433a8183c3d787cb12801b079e2db793515c761c85bc7d0b4621afec5467a0ef4190369737a42905fb4e574842cc61aede7dcf5af3253df31518ded0b6d782cecfa04a545acc028b0b1b48544ea44932eb8169e102c85a99c6283cedac72f43e254c8696a163f0196e0d9b325e2b1b588b8329d779e89c6e533a4711d29e914d85d4ff5b54883316e49ca83b83453ca57883bd0cbe91c2698b292b1f8591f32760206a32049770ce88b21e7a2ff12fadbd5bdf9ab34f5541a076b1fa4059f7912b2f32a9d4aa5b5c43db831e84e3f7f639489db0ad29059c5d4134123ddbb372ac725625e005e3288e9334923ce237b945578111a678882c970faf32e04f74a60d842aeb6e08216edb23c8547d69a3fbf9f60940975c2426d449f0a1ff6c05d863a9daf198aee31fc69e85c391b5b14f9972cfa5cdf5d082b528f87d1e71f3b405d5be94c6c8fa91ea26d07bb37368a8ad1954acefd14d33a7e07fe46fe7c7578a444c36de2ff70853671f205318cc7e6e92b8c9dc0a027b842fc50dbd47052ef98c3acdff14b2d3e8e9ef515c574355515cbabc9c5419d04f76c3c90dd470e21844faca456c31840ceca956db291cca4132baaa4da62b58e072d847c637d0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bc9c2442b330174afd0b5870a4faec9adbe2e482211634581a63e3c361641f1d","proof":"56a1035cda9ba3e89af855733322dfd0209d5e53d56e534b05828ca7f66a60358c3b475a6541bfc12d7d0c92d24d368409d40b9056b756029f87fa3ea93cdf3ec6675d9f7c50a1b26a1281fad9d235f110f7b3e846854feb68bdee0ce74b5b27607ea6ac9e3ea725016f95ea8f75db7b26e8a6bc2cee68dd774ed28e2652fe55e09565da3036e78f1644c7c3900db525dbff7b5296f2ea213edbe5508a1de90d27d74dddecb28dc4899822244707fa4862ab9c20686b87d3079f6988491d7405ca2752ac9583521f2b744a842372ea66f47573f9e331503fe30cd5fea974d90c9026a06f0000cc5b67d058f898a6558c18a784167cd039aa35fe23611fe1624a8a00b3be12f0b21520f41eba148bf5535627d7aee57070985ab56b6b8263e35a2c0931c9515b5cbafc789a85d0aa2c930a635567843731165163ad03078eef5dfe991821f775eb52febc8fb47409cfa73f9e24aeeb84d95b9d5eda993d760b6e2cb23bf4e194c74dfdb16c67bda5a2e96a10f1a485c7a9cbafc789e3075c4f591cea7994de244437869bb7ab0c842dfab70e2b9af9de1964b3c26ed447f9fe0836115c22b8252956d40da695fed16222be047b0855540d247eb6d1d0e327c866c0b70b5202290d91e603ff4113fb439f4b47e7e3ef0bff3150ca125666a789730a94d68b2b78ae91def42155f54e93a637338090d6086413715a9abc226d904b8615c53b7c0850e0f8ff6315ec47f766c857b9c918395a1ba3571a092a0e302740de7d8550f0e9e3b0221c6561475c09942b04c4d180581015feb0c7c3fa2c6f9cdaa0a39eb32389ce79bd8d509647b6892661d74acdbdebe8081bd056184079681c18ea804977cb19dfb6e1b37aa2f05e7f9f25affd0561437b086f670acb0879b7eac4d4477f5556acce2baea61f8c1bc447cd7940d6a4a9391b552aff1d00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d6aeffc65b443688390b5e01c58bfe08adf8bd967eaffa96058d032a37d6c01f","proof":"5690f98ec15c23370d04d5c9463048354dd1f6b77400728848cb446e9a56a364900a318aba1ad82f41344d7b40508d00dd794d830496989f2592f00216efc272de2d039750c5b2ff8af1768575ff86e731dcff21d5afd43cb71611111472c445988ff80bdfbddb1e039f04159429acb9eddbe5cc0663480c1fbab66f98865d3544427e341295bbdd957ba5c6fa755a76ff41c1fe2b7f550ecac373a236d77505065560805f4722f276a11a3ffa8f7f019c57553b0a17a831af1907ebcaf07f06f336fe9a5720e317e27d09b24b2bc0624a40fb89bfb2009cb354be857623e504fe69b92da080c8c71d3a97925979d789bd9bcea071dfe10360c37436cd0646288a41037a0ddee2c771ad7ee14e9a6a3c50324f358cda25c2b5b73f8813a274069c585d095d5d3f98278a2bc1c71ea3f708c9dc1a55369ece50abc925d34d491b949c71f44102af89ecee7a4a6e0297f6e07b5b04d3cbacbcee29532726d88006ca1a242d6fd9c587607faf809ff15cc69f834ba2a922fe5f53946d5e2b1f5d1d788de6af28fec50c37a2de4bb385b4410d36496858038cefa2cb6198c15de3716ccb4e9c91dc40268b618709d8cf2c36897354516916d771e3a2bcab8ccc9352522b42c6419449be695d13c0d023f5ee41d16228b269590caea6954f3af7be5bc292bf8c21925c01c6a5e24886ffe24feed03ac866224986cb4ec91c55d55c2302163cd76d72813a335f617e9f0f2cc66399b8c85d8acf4ad2c76a6e3012c201246123bd4e269e336bfe0f799f2eb18d67a913bd0ab18387e2e54460015d727a30d5b4b26d51b930600db8a05be289e99004d1fc31c7661da4b2f0fcf359925d82a31c7b6916d3e4ec7f532de2e954f764ff1750b4e4a264d28d700ecd8f4809238b6b14ccb3a674982e0f65b380f050b5aed757bb164cb3c4ad48af6b724208"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"861d076b0608ff1832980ee3cbbae3b7c883ba03544d50539f5a162bbdebb459","proof":"1ee75771b07a6280c1fae1488865373eab19f9443318542ef0631bfeade7807622b602dfedcab9501b579aaa339f49143f2feaf64e346cfaf2b80b154a883818d09205573b2fe2bf26da0bddf6c1674ec617bcda11976a580b737f9967206b41d4c8b06ba83e243125a6c53745187e05c10bbb013a59d6604f5391d28cd102392c5e69c6636a52a493659f5b0d4a870b5a317d50f35837ae1973b7da12025b06ea7a2fadbb3c46040ab2a517131830cbb53a3e191f3b0daee366aa77491a770e18af200ebd62d51a05570c0dd4ccdb155e087d8278698b63f9bbc3092c095403a8eb7baafca1c6f9dd1d2dae9d891fd9552bf19d6f59d01d18d47be133ed315a609e23c59c2bda417d9fee5f65e0d71de7fb27b25e4ffad25d9c8f84739cee2d5297d126d8ae724b47a757d42c05aef55dad50a62a688d466f137ec7182cf44fb2d3257efff6c5b19c1126c9c9ba57603e17d9cd5caf2f1c8dd0730e35af927ca80f5065fab23a90e7d6d98c336ad5202b3a37b727caf3e640a792f9de8cc7609c0665fbdc2a8896ab91833c2afe5bbe8921f8fc501d707b7b2d0c77441a7f60e4a12573ad96719a142d2cb435c31c23d9b2926b87d3ea1f25eab19135e67d1d421f0e8ee3abd6257e717b51544eda0353c274ab048fa7933a56fc6ed2be54732e22a1e4ccdc4650de6ed7557fb42b1beea63041937a1d07c837e2e21ced762c9c5788f67f3b2dd2c63ba066c033b5f4fb5a0fbb920e9d10d7a35fa0b9a8b67366a9e876685ae7cf3d10d42aa577d6f9b4f41e723a1bd1fec35a0c153920621d5499ee1f8b4f7600325b563133eed6c47fd26319e91dae0794e835ca0d66186998c4e02abbb1f2459eed0cb4b29d4cd8ff2e6cd782438a25fab386e3afd8990443e4761500c0f9b7d9a029f026d87ec7ae5124f6f8d9b942647cd3a5c3d9be07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"645a83999bcf227a66602900e5c9d572e157a113ba40d14aaf47ad4b4d250b1a","proof":"c06279133f859eca8cb3870815ae723f2b8694cebc92545f71a9ff1c30b22c7468719090d6d27c531f605167c9102175c39452bd482786474a1e6057cbe12271a8829c20adbe415f096731eb91ea8e07e8a9c6477f602b1a3f71147166300874f857c22bf88f249a5792bef41352d38c8b64ce96e73b25ec792190bd8a990f63d71b81d738381575b41081904b64d6a0dfc6cc7d2a1ff8e6a68325baf3fb300295738429932e0fea47d44ceec8bba60587677e4cb140b369af99bbc60ca9290034cd4ef2ec34167e267ae4d96fce79f4315fca15520da1871f43d292cc19170a30e40ba572403a9ad4cf5acb28bc8f71d499b3aadb500621e38534a1701c4a17c68c9022ef66af5f4d5fae31d64e773c9dffe1d95e564f3cc83412ceb90ce30ee29489e000e7e903d6c36a57e6624c9dd40dbbb59d3dba16e3f7fdcafdd98d1e6097efd2df26844b918ef978c9bc4d2b4246eaf5214dbf632e1ad6fdfdca49243ef2b81f9abe9f302983036bd2c2d06bc5516468d19f0bf32768977c56c9c46f92c1686f0e468dbc984b044bb6f2f87e10829bc57c0f48a9ba4221a2d12c332b22651ea9c1206b62366985d9b5e73eb2e542a59e8e111404fdd58849d98a3d18cacbef78932b425e038301d12ec9bd2f45f95cc912fb8c0a96e2cef9430cf27b3078dbc50ca40160765d7f99ebbf50949816f242c30f11ae417efa2a0f04d1471ec09e428f1049f774e531d91cd6e5d48013284727d7c7b1dac520a066e3946b5e20582fdb22a6dcb3b3828aa81dde08a432319e81b25281d128b749a53bf41304b8bab0e2d85bc745f0f71f2adfcce2b3208e9b9ae8d6ef6f9bd3054cea6479c1fbf3ecbd119b860f9e62222373053b5975bb7a5b4b6540e3b5cd73fde2e300cc9ac43084bf8e194ace795eac453603e0ed606ecacf2d271c820f751f4fd601"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d8bf7acc7cda3e44fd901a046701aeddb76d7a3d53951eac41fbcadbabdcdb6d","proof":"f41f4817672f01b0631816dd02c3b70043bd2079b2794b9d7619c21f3ca3855f0e215f80da4823e65467343ccff97ae6d6f6c72f3282077c93e7d3108816c60b6ccfa1f547f839b8080bd8f7f606a0f1dc567c4d98998c9c6c9ac2929a2f0c121a772f9a378c9a60de486f33922c503a46afdd03bd788a63ff76fc71cdccdb565e1886e5ff0c2de4a1ce8d3755b98c6d8a902567cd2f53e6fdef9d0bc91b7100435cb9f45f38b4bc73a8f4cca561f20d0a790815e49edd52cde75025c332bb06640ba14c9ad032743e2987a80c48c7e710efa25a6b597453f22938269a50ea01a8df6626f0249330450343286ae592f406c9df58d8a332d15365ffc0b3a89a333eabe1c21090ffd0f7f8b6ef08ab684a0d837f93d85d307f2ed46a92decefd2cc6e2b8f9e23e8abbc067f1e80ce683f5da0ef790ae2430a8aeb5b9c2aab29d01867e7c9a51315e6e5a9bf84d274ca6a8d15be7f4a64789df4f43aed4226a0428e01a1d464711fece68c9f0a3d417dc986a8f79056fe6e12c7db652d65a4c2074fcd6f6c1bac5e3224d0b74c19d3a13eac82eaf1bbfafa3c2e9708f154a2d48797488d22f71f81654e9efd158bb6407f1db7c2a7108f59b1e77208c3f2159154d8a3f610cc11d2727a80a26603874a15567b84222c1e0ee7b38a2f964b6c3116d30aac5699da9d0ef868caf9c4b3f7c67740ab809b2c039bec8217dbf776f5f1420643b638a86d0a1c3755e3c3b995247a993c2637ff897b14d14f62784c30159a40b70120f8fafcfac590ada566546329767e00a297f91a4d78247909a4471792ee78346b44e9a1e0303c6f66768b94b919889d72c83513d700d9c541f01dc78348f06af4796ce47579e43513ee91a4859ddb888f42e0e181e1cfe6064173d063309985e324455b601acea460af6e4b0377b2d6b2d8b01233a3226e127672203"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"36577d3cc3ba3ac00425ffd2b09e682f94dc96bc9e434f659114d3108732061c","proof":"f2e9b81c0d6b406f9b9b8b077e9e72db631b67152a8e52c8e25d24a7f789325998fb0bd04359180b8c023d0a3daedf0ef4617624aa3a7efc8c6a9b65766b0c298a22e79cc4e027d6e9d4ff4cff44c0e2ce4f7f01e1146e73da5adabe9b80af45e2311f71004f639f096b2b4b1a848f52dfb2df78a6712f5ba6e531e0e52baa3dbd6e5a369b37c8a11f9a0d6c519c715e55f472e9b6a87f763058bbbbc5559100d5e87f71de29e7c3f17d90dcc14f35ded230ffe05b92d9bca6384b0a76e7f6018bbb20a6f6ae206fedb13bc368a876a60572b1de4170f0e128075786f8346e0a0e5cc58cedb162222fb16befebc2910dd4079206420b5e8ded10c870f621ee794c69c79708f1f1b8c2a3fbff883f0bfbb41426e471ed3a95a39bd1bcce3fa07ba810f699d186de3396c145125420242b2c17eae712ef32d152c3792de7388e79d63fee6fc084876b585f3df36dd27d5414270f1b6b5b003cf2a2fde534f3866960d15d8017605798dc28e4ce378e1d37d9714c69f3275d6bca8acb76249a48123432d4ced11a0bf4536fb30f57169b42343cdbf4bad73675acd3f34e5c9a3239f63a858aa3313be2ec83a01573c3ac151e2bacde214f9a389fc624adec774837dcd0c3f3496376d3aeb580dfff741bfd803ba011c0cc4911d43c5589e28c1a15b09c2adb327f327982997e7e392acfb980dd5c43cb16366bdc9106805fccb2785e984c56a28f4f38bd35fa20ce67a96087b614d10ef94b0aab9a8c59adc2c07bc4c16517cf55b60b4ef3fcd024ff0b1244031736593bb9f62472122de0503d38a6a362712447cca68f36cfbef628005f8afbcd83274be25e4515f6d78da5da17bb6ab635365af591ab427644846c75f5cfb7a0e63220d2144c2d6167443ccc0e7c869dc23cf674bff198f2ef7f58ddd503671ed2a8ae61fbf7538f715dbacb0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6021d481055d49e8486ce66703108739fe8c58f364fb78b7e9e7da1fa33b1d39","proof":"0c647d10cd00e7bf2ab2ca12130ff1370ad0e9c349959736311edc9d620d295460a465f0d06c5f8418fb557085b5ade2466fe867e17ab39d62526972e6a93d02b4fa7eeaf069b650267b31f085d57477bac9102ec04a445ad958d345e8bbe5482a6d69e3596a7f55118f32aaecabfb5c74b5d17b49dcfa079e431765cb34997e16ce65ee95ea0924974003b3b1535e469c1c3230725c3febd032b0bb59d9670915c18da03bfb5b6d33dd526c931f0fc056e51ea1fd1e82e9d8f7aa2c60e2120041e12e66bd4bb8e3078388e3927139d26109bb090c9a79da88995c74c843260c6c0810ab1cd8f50e2e9c51e2f5d88a4282040a09d32fdb895231d4e1c364bd3bdeb84e20a0d2d65ebb8bcf7e09e870f83bd9d5b908d6970353aa295dbb0a4f658cc9eea8d48622fbf4e8a0b78c5f421ab00c2058d0271f83965f62e36e10967916249b0deb3bdd56f504dd2d2ae00b0b6058434dec7b8b2ffa2e5cb840e4e362f491a57c099e50df6444918abb9953ceb8c71c50f506f9ebfcbefd3adc6adf258a795e60e09d164ec70fd638ae2110179a5bd0cc2a80ae69de0600be66aa034ea4549ddbb43100da393ba62a79e2214e6b9f6aa5675f22b9917c2b5e05387a7e24bd3d790814ed97b5c70ac6ac45544a4f9fb26a33cb0ff4c31a2b3ca8abe82558f7628f0235cd779e67842514870f20c308539febaf9a83295a02da81a8fe7c140dcce33e761a26fe6f84b406c21346e847361ac8d521d757f6867fdda1ee0e1ae4b45e41c0eb7cc4717c495b0d4267a03b8d2b18a156ad03d157671283960f8882069f69d50ab88b2ed87fe578014d336847fc02a610f41024b5f32e2109075a9e5770db5f2cbc208b815b6700c969902f07bced1c4ea7cd4d749c76a5ec094fc47147c57e12a45c5545502b0565987c6a38f442553a6c0e37e27654cf8a06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"880c51eea5de3350065d74bd563ad473aeae2f2f3f162516a5c343f7f0c49d18","proof":"7290a679dc78512718d576bd3fdfa3fce1564e4747d6313c13471fa8af0a9b49fac4d4a18c4c55ded4b567874e0cc34fad64ad4415eef759b1c54abb1086ec449269ca89b82223e4e1982de098b988f479acafdc8c183a5eb4ec62d3224cfd785c0222b59b24ef6f38ca4f6a747ed67b72a9680d7fa5b60fa126987703eb2362b611310653fa10c0a8d9acef4441ec1887cd09d5c734c06772df9049a22089020ba49abca084a63088b8156c8c2a6cbace42396638ee8aa28a8c2bdf505419052ef17a1fdafead28993adc806424d4cba22df51067a7ccb768e116ce07157c038ea8b73f1729120d159e27cd9502a9d2d4466de374d3fe355206f51415bd313ef643eee613e457e5d4515c709c67ba66854522bc4f69f15913d7869dce6e9a450a36b59962a881c15f6c512b8ffb4df88596585d9626cd347ba080e58d1ee3572c1c5975a33bf214b15ef6fec55357ec0b535a8d9277929187db8a8a7c431a4b867b17ac1a5ad6834f67f95429b543239e956c8c071ac79465bb979417a86e247cf5dc347d567f82814a7c268a18bfb080901afcb80e932bb7714b7921a6e6751244158335ae078ce22f8d780f9c2ef82b2dc4b9605af77c853a9306220b2046da63fcc34ebc6cb32f587b6fa2ed4e77a4ac6267197bef09491c756372cd29449c9de2099b8e0a605c156cff6ef7c57349e657fa0beb653394a09147a3b41939be9cd4542fc5fe12aeae4009037ad5ee569106645299db7947babf6e32798d1b486bfcb53c2d00a6509374aecfe0f57672458dd3207b529ee1c4a598b2904173fc56ff75bb56d401812e05b095c962cb7d889c99f1839d307772e0102fb37d6c06b1b7ae9432a4163137c4897bd670d7596383d626ac8eaed408147b7e79450a1c6a0bfc4029d39ff4571d44e22ecf5722f0447b299ac2ff3f9a3705c31b4c08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bcc1f701bf85db85e65786c31c384a1251c0a21c7df701da6202f47ef621be65","proof":"0ec92520eae4dd073a5a55daf5afc729a9ec8eb86cd13cc7c76c036ef9bfb62c9a421a7e6d7657627b3bda4d8c6ad222c23037d5954bdcc192d3745101e3c357dee70ac968146c2d1f2a6ef15f397b0c1da304ad24d3c70bac9215cc89e5733a66e65a8f0ea29455405b84a1fcdb3c29bd3f877e5d5ff634c622519d33723e07a7187671a633ed06324112d5479ecf6429ebdbbc6a3d6ab6a34d00e4c351670f6da86522d6e9251d30b0b72c8d8105f305b345cbb999d6b4b75e6b47f4b2ea0f8df09273d5d934d468e7bc9926fc50499a1cbfbcf95858b7c93c13fe11ed2107c064d9660328373d32582203196ea92a86a4aafeb49857b5d274ee1bfe118b1a8286923582db3c6650d06d0c5995c38da910a01fe867bbc40bafdaaf02575452123dc7b06acd2610c4b1274a90f1ff2be8639aa519c5fa4f642aaac882dee164c8bbc3bd16f1fd7a39052931c2d5b851541f388d9cb2bea04c44caef1408375e0e6dca29a55394605650830b728413a345d3860a549f3239275782fffdf8185e809c64e0448d6bb080bf703ab85285fc2f41795cfbb652c38823802e55b7481facccbfa0ebac665b830d7cf4074c15b3ec0ea8bcd0063c1b6348ebd1f61de03a26c3cb29be5a1165260c0d3e95bad69fe7eb4bb08c18856028d4da08965cff357a322a483addf8236c32858c09bc76034f1defe2c121cca7e8f5f57cb7a50f45e62d9c5f75be71502f83eb00d720bcde6d95b09eedf8ace74512509deb68a70c04d3f9237d69e46385a905257ce6f0c75a53685f5d88326262f9e03b7acce9131809ef8256fc8f2f09f76a9d12a7dc6c54e764df6a6afe555e88c96d632fac1fba06382e0ef6c5191ca1940e51b5159d5b6e1bebe011d71dbd6bc2cea6d14106b98f73224c61aadf8201a4c450084871bbc0b5c6fd5f8ce1fcb07c18aa81000c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a2433ac1cf411a9744e0647f11fde436f4e8ddb49df91d59130692f5865aa508","proof":"4ad446d55531e48292e6a057000e97bdd0bc62dbf508a754137325a2cf0a5826009d34c676727e00db37abbb08a3eca4b15a14f98ac75f9a8a7ed58fac1ae43a52469666de42c816ea28e258c1280cb4e75e8b6e287c0fd5690daa5a633ccc1e28bdd6c034e4f15886d7a0a8c1afe619d6cf7c529e3710933d69299c2641292ac09a36c1c32b03286260411fb2151a8c49aa02acab116f2d34ca0863e2e366081577a03deac193d7855bacf0f8448cdefdfcaa48d25febfc80077876c76597001e68b39066799c88faadefe7aa967916b4eb8d87139d4c320ffb22d2f141df05d8b06663f5823412c4020d715ba1074a08838e462001a631795d7227f913c03c18fa8d2997c864617fe3bcca5e0b08f8b08b1ae590af44e5dcaa8b808fcb897628459b483cede38aa64ee1e2ad4ac633fe8fae98e30f834460cfc3fef41fef742029a2d2305cc7c6314e08ee1036a64c54d2606684c1afbab11c119a45733b6b66053a9268b970ca6ac119c8b9c64cf3473ee9f0148904cf029aaca72d924c00f4cb4ab422683b5110fd23666b69b92ea75457daf5c3581b80d2459932ca04775aff7c796fdcf536a23ba83147e07b53cc1ea48782695f47aa416e3e4ff41043fc732bee24776113966df93c4b2f0a91ec5b821de4400e3f72daa3339620234112ff2e120926449cccd681473495460feb5b2baadec5799c1a03384b7666fc55982eecec689fff0569f577017af47e2ebb41a1911691592640c7308b5c1f9b59ce8d407ad14185ec0241fbbeccfa70ea34adcb4ec725e780d992c2435bb508597897c70c6fa1436b16e8b2980e18402f337ed9ef87995b150f0eefc4b77129571cb4218eb0f2988a108c076dd0977d325fd8a97de84754e734c67297a5a7150087b44da787c7d419d93f102b53c02bbb9e8a78003e3e69053b2894293639e70d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f21acd116b0714232dd727d2acbcf8162641691730b4bdca56fe717bb9632e42","proof":"24cd53622ef71b43edc94cdce77d9582bf139bde7f5d96d9bdce476f1323e83638fcd8dc808024823ff3708c54951955110eb5f2ca2eed9cd49278ab1a6463260c45f9641ff88ece90326f5f20b05d53146df7185d3035466e89fe4acdda244822286daab17e9d29e92bbc02321d750f6bd55cd23ea7c0953e96d86ea1e1c0105a611a220d741991b746d195665081750b0d2be0d09885b5d6e3b99c29479d0521a6edcc7b19e402c45efa30c7f86ca34437d8efc17ea7b776b91448634c5302bccb0ca81afe4f3baf7e64e3bc3b5645d7c519b204ff621a742aa913ea604c0f3e26b71063a35d186362c024c04b1a84923b295434ef99342a00a003e23a333a4c45060cd9a7b546ec694acc8d7814a977687442b0d7661bc3ca9391c40b2406f6d806386344e5ba9afc54288d80403a2124b3541a49c644028b5d7d07057f234c63e6d7aa2b0214ceffaeced4ad979c0c1bbd775757d738b1f9bdc49417d372881aff0341349340a136628ec412fa6472125aef73f10dae6fed79b568918d60a0a9cb9904d19a2a892715847204b8b097fddcbd5337fcaa95811d5b9aa06446eaf5a6fe4e33f44b533062ac807f0166d47a5ef7d1564ff4e4cb44c01843b6726a6b3fc0aad72f9f2dc60f088bd132fb911cc1c7c217379925f79c8bcf1ab9305077d45b5315da74d676389ef7bda1266b7905879161c90621f6e1b14158ea1a48877c93966ae25340af01b73edc54f91120448d6399a3d2ee607328371e76127c2ff9475be57598a6eb62bfb5123a567a9b64f161e6eab4c79239526553417836bc42fb9068fd3ddc3238659c038d98398d0a1ddd8327e1f0bdc5e2682fe80cb4d15fd87e0c420860a134e8bfbdf60fcfa4f0dac7833eb8b49a167ee94c3008948557f8e5f88e543413f5e04bffbc1210274ab0a39fa8e44cb14a3d95f68501"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d2c1df1de796c47850351c00a13db61b1a1dbbb4e469dc259ee9057bc6f2d025","proof":"b8e860b40d969c839cb5e116f07fd8f515e5c17e937c734b2f5cfa751d2d4805e6668de253e168aa2985768c807005af80eec5174499c9d5ca53a2f1e7598e2ac2a67e4b9990f4084ca6586085258eb735c48316d0e82cb18707c6e104ed8a28d6afd69b203a5b39e4e1048bb398a53e2aa45ccfc370e29a2b08be629c328d537593b18030965ea3a252058a43ae566028d6a36aaa042fdda9d0c552f2f81f05ac33ec7c96481e50b2b1ceda81e256b7417f4b7ce68071ad432d82753222f10aba20af5967f1fa68f8a13cb590686b76fd23e1deac61cf845a60f16a53bd950e3cf0435b9732e19472a7028a3e586f64f32a0e0416f8651e9a163dec40d68f43c81a5e55886cad4e47014144a3f4bccc70f058a72e013986bd06e72cadebd030ec2807117ce141c19186e559eedd717fabe33e9cad7431795f92f7656e68a3505acd6a1173f02e147ba2d951f72e45e1836256e4a1a8a06f442d7a92e6860b0d181cee05c9da78287de0c01970ee2642bc79a62451fc9927fe60034b80dfcf0ede7e3993b04c469ef861c1493ccaebb0993d5c24174cae10f7b01b2ab35a261c98e08214073fc8837e4742edeba7810ce7b33395f97cf0c5748fcd1515493e1e70b36b6729f084ac0c79eeb85036988521a3f53ce3c88445bd290124032381353c9468b21d716147332497529a6374c7737d93252a31383c37504e61241bda0b1eabe4cf178046060f73ed30a40a9db044efb6a20ab928e22ce7bb70ea52c01cfec8518093edafeca7e145e26fe914b5b55c8bb40cbb0fbec4c429bbb510776f24bb1e97656a640a738e4523fb6f92210ef1de7237ac6198ede473a600dcd61b1acb65207bc199a89e8cf9bdd2740ff4e9f907a8446afffdd4e2cd5418c25e0fc7b852db7017caeeefb104edd6a87b5db2d976de035115f7538d9e6608f66e0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f66e8f80b9880f58fdb628e845f24ca217bd9aa0d798c024226a03ed3b8ad07e","proof":"f244cf9b0ea50639e8a06ae124222ab27b741cb4760ddd53b21cff085b93df05604e3e6ada798326e58b24070cfefb321acb4e64e260cbacc321aee6291f1d1ca46fe94801c44171e69d5e0640c97beb86a729449745826cfa35e6f79b187b3b5c85486b07c074934d5638bc1e02b7a4c6c13e3c01ab8a5ae3e2a975548e1a1105bd52409df03d70a848ae331be852af303d67d2814c3735eba813df3a7dc804a585cb0f76346e866c8b529917fea514fcd3aee42f78f76ba04ed4f769412b0305feadc62411490ceb49af9972fc86f12ba37e18c7baf32f4dcb0c99468238023067ddc901985a5804f45ca37c2729a5136a6818799605b60a962b0a35cc8b5d38050b0c726f75fde428480ec45f7cfe7c4b8b9083603c233e9c80af4420ef111eade6d92563673999cdb50755c7bb8625b18756dc63639d6ba51d84c309bc6624c1bdfef34e1664b00ea80b7fdecf44dc265bc0ca3e7b32f29d6f1648472726b8ddce65eac475ad15f68c3cf22e00e7c385cc1eb197f15233f3788168bee46cc8b448f77f9500bb7cb8c050b164bb8a319b796725b222a28b341facab9f49311edbb5aa9efe63c95faa71d235f6d1439f78371c81d0128544246da8fbfb9a7d48f82bd1583335a4419a976387247d4c067b189585a189be844dbf257d8df664baa14484738b38c440fdba61438b936420c74314a89dec20f9d45e5ede57934958004746f5db1d826cd7e04d369d09a39424da9d588ece2b8077d469acbffa3a00cf2542e004b6a0a8ce906a4afe4fde7631d1f7fd5b67bd56b262d04e8fab74b2e52a8bd58c8e144376d74c4eb67498e575c7493c6f95009d5c9f9c90975a613ffdb55affa3bda40aa25e9a43d205ef1292a6210786db4fecd2c176bef44f0208ab158ca50913f0155b72ef05472c80011edb2111fec209802417cb8e450c02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e22b7bbd7637b254b9bfe6bfd38293ffa0e4ad8c36296975ffef2f472da4df5a","proof":"de3e0ebcd7f21345b92d21c6f02afb4e1fbe5e275c7d81588058b6d80cef8c357452827b1f97de95764d9c4a871909cf53f4a5a468968209818e84fe70ca04177868a495711d3949bd101c489ee343ed4f176a848328c8f781b1fc2a1cf3113930e84f6be2ed6a7e445037abb581472743c5bc9dc4e9a07cb65e68b8aa74381dcba0fed5b1aae24217f71d81e74f6e3a5c37f88b9f70021dede43a2af029b301834a3c632c5376740d59ebcebec5fdddda7cf697417c84c646646300d2b97b077376a7e580a3492892861dc57b6b17085efd199713cfff7dec52ad1cdbfe580b743bb9121824d91df43f0f55e2018a5c3e2d76ab6a22fc7ebaaccb1752521868540c56271dc18deaccd25dacaf92d0f9a5bd073e754829af8d940a6ffd1c935b7ece6601424880a12f9d6bc7e702a48da2238dd62477d223d8567b508219475320236b33e30f685b7a965bf17d4d9b0a8a28bbc8c99cd1f7d77d8d84dff8be66982593d2e90c3583d9987d89d566423857166698804a4d386f918924dd7e2b0e96ad75fb597acd4119575fc21bc847fcfec168718d1d99d87b96989b7d1270188ca94d5240f70c65b37a6e182eaada01d13abac65d961c7ec994ab3987fe705ca486d56537724a55c499cb587eb213b3e84d7537c112da46820ee760b8f92925ceaea1f221323d5ffe82fd3e38ae0608f880bafcf044db4a88f17e27d6b6de0896f91b92ac03bf4d7cb567fccb5abb1e0d431c70a2167b25d8bd4fbac16dbf39ce71f8bc0a6bb9d514e6aa9ab0445b45edd33f34c1fc209e4e1f51af1f95297ba05a7510f1090b097f42d020ce3c065072b1450c66c7b0ca6dbd2164cbf267581cecf6300335033c69fdb0ea2f276ca482b513dd63dbeb33a9fd3b2b9016cf0f626d934b9737327c1aac9e7c4501b060643443684cf8fad623a8a12ee4b6140f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"14aa6cc76d165a0815673f8fcd7b69db1394b521d193678c326862c2490b5d46","proof":"406d69bb5d25edbea9e66f703232118429f8a1c9568c1a83b006e1a49e516d4e443d9e2af02707faa0b588609793984187066937097579cea7fcc01fdbfb73050846295f0ed8a15d3cb2d3761048b215259d1b0ac1d8a165ce7186de4a81897bd223ac4b7905a3b251e47a5290f3d614a467548a3ee182fc448593553b654d529f2391b16ec5d3c545ded36a9d4e619c9e8ef7fc3375374811139e52fea3780746e2c467afd1dc51a561a21fa788b7a60a6bc9fc2ebc131dfd2c3106933e9702534fb595f82f1b1408d2b9345a2cc8bf1adc95a2e8845f9571c5330f4d5e5808d4b0231d01cb0dab9785ff0639174bc4a36944c385d338da0819e9a178ab04280ac1620d868c0b9e601b999c803af9957b4a19a2b1e4725cb4447bcdf5364364c888393b90528936427a4cf03158e93c423f275ace4aa942b2d3ab83e9789e2b14413fa527e339c0aa0279b148d08505a75897e2684ba5250b6912c2df922d42825b46b0cadcc0268e1d391c440721e8d0a80f09acc22f9a341ec11abf80ca35a8ac3164c4c071f9398860dee4795fb0adfe45c3a502cdbdfd43195be5d1216266fbc98980c13cd8476220ccf6d56a6750434db93c9987e7c98f6b86ffe5f715c299eae961ed90242e4e2e4b7a22fbe19832e97c1544c8b1a76ce9866e6ae04434ef243ee8acf2e18d8557b9380d69427e1001dd8f73a11d422869463f1cae19fee0dd57b2f95ad017d7a93c30fcf232df6543ca4b004ad494ad6651b7159e2476a666355dd68a2b266f877f3dc038fab814a50c204f0e9e55b061dab4e8775e52f7f47830f3efa4f239f4301eaecc02df702b8643fa187e71be07e007fec616bd26293384f3dca810189cefa92fc2d8dc24b85c45313c6d09c3393880b8b10d26b2e3194ef13ef43e74da6b01d19d4086bc4d7c549b13e8c4306835399c4f0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4072b8ab43a0fb967c2c0a4c60e991987f737188b6496686440c9104cd9e990e","proof":"48186fe3d0b2e38b85fedb2e1486a8d2dbcaf8c05ce7bf564ecde7a50aefea308456a116306256d4ab07cf18c2c649e4b852c0652f485103cafd188885ea0b46c619ed4e40cbabeb750416cd53a6eafa5506c34164eaf77025e7f466ba942d6b8464440f0dd533b7969bf930f9038e06bc41387456c136e1b79e4442f18dc675e28b948e449fd110b093b9da8e100fad19b7ddebdae05737ec7e307aeb86040fec02acb4151add40a14679083f7aa5b636913d97a511d797ed813ea895cd1307c6c83b69b5db8c6b1db50df75c3175c2115bf7a29e870d4fcba414ebb502ea0bfaad28623c141de2ed1c75fb676bd6a365ef59b76aaa931aac7ca9764997ee645e8ca0674e31f58be901607ea96b9166953a7dfdff8d65cf2a96eaf79a743549969dbe49cc3037ce7a19e68622d23ace46c8ca999c7e44ae96bcde5fdac92904b20e791ed1c6df93d405431ca3b671312d712c7b783c81e717f940aef0d231433ef57cb32507bffb6f98064698e661a63e36088445495ad8ef4f2e24233b0e1e14ac0bfa1c1b882785a6c98426e303a1d8a75c05f71d11d9b5c6262699e3b25d7e7210e862e3aac49a8dfa1ee01f90f5d17a343defbda01f2d8cf22b3604e120dcfc9539c4ac15a38994fe8723e77b0facb40fa78c5faab6148fbca04c1bf13dd00b8257316fc48e73789dfc4d0c8a9ee7d98e50722f5bd0ac0b1f0ca242ab269c6340606c47b0eab1034d304679f4c7e9c655569665f462962c0e1a4abc4a7ad44cf18d8be35338cb42e971b6a406caf7b4bbaeb0ef03d6b86be2b53631e11eee14d9089b1a5967594f45345adc3c2780d57af5d6dbc4cc3514b184b10d55305a7314e0a183d42f7dc1186fdea9c3ff14fdcf31ecfd7a5fba920f4908b1c00e8c0b356a67d984d93b9c73d9f04863e0d96e451077a16b44c234d78383a20a0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"86368d008356b6eca917ffb31b87431def432c5e16a21fcbfe7cc4ab671b2c55","proof":"c246251ec8315345f8995ee7d7f636b3494293105f9939e282fc3b810239c90494bb0ca673c520444f4944d422a93d217fdfb50d4f0e7443350cdfe985b25c78223de29b3509a6e8bd16512fbe534d032e6b30eb0f27d5961cd67bd4afac7d2ecafc7f35537317aa3b03b4318bdfdc2a88fc7d55850b5a665032eaa1154df773b9efb43e5f4fb86fb2d0bebfe6cfbecf206cf7550fde0dabea947db3050b21015aa327a000af810c25cebdca918663cf3b74c3128a9d5a963020d51a86583e09ce5ece11a0b90dbffdf6cf53a823853ea6360dc2a5b7457af9687fbd6043a009d8dab0addf08a28d71f9d502186047408291d5c1c85134dc0fc90d5bf8b50b06429f042e249f70536d9060a8b237cdce00fb302288be73882749779e96278c67a010aa32ac4ca80e8c0efade27cd0ec176330f69fc37f9177594d4f22c667762aaa8109c52f10a196f918af8877e975f21a49d9b2a7c586b045c27b37a51445ade7ca1c31a5e3fd9a2bf6875ce3575e8ef1c49e8f24c4431cdab734da2129104dac13bf5b5cc4c6c1e06f359658a12b04858d7723bf4c3cf7d9aab327c282147dcb90fc81c9b34cd32522a03b36f4b052a768bd8bccf4b5d400fd1fef312494b16602566703cbbe415a3498672baf8cc1176411bdf51f446eb66a521c7c83910a45a16d46c993f9a8c40598160ba88237c721b98eaef67807f1a49fee1c7d35c409af517521a377e5261c0b7388e8dad13373d0c518a887f4457867f3a320649a004318ceec81cec6fa7c4b763531bd077013bc23c79c09a972b031d49258a38145ad8966f02b7863e5ab8a84fdb2325354cae24a3aefd22955f84d3b44067467d205a5311a93dedb1b81ca2894d857b8e4f23696d41e0952847de30d99c8f0ddcdd4d7846e8b2b73506c7fc62c4b1d867ef18d08e2a35c2a08337da24261806"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9421f0dbc851673d11adcafbdadfb3b6028edab4dd28a1f154aa323bea3ad91f","proof":"a6e9b43932cf5b036aa61b50bef4d698eb3dcde16aeb4b288f32c13fb0cb02324e5f77194be4d94c411f185fe68f8c4f3cfc301839e8370a38a597288aa0df7918cda03145b9c39f2af822ae804af3d37d9fa93e0a6013bb2fa415aaf3e16549ba55ec4a0c0e4d5768bfdc06cf5d108e4cf703a6ea884fd95e0358b612843641d666e9d2c08d5e4c8b53669971ec4e61a99ec0f2e549cc9ecb0d99a7e3f68604a11249f58304e80f22ffeb2a910ae0e33587aa04ef16ce7ef7ca8dffa105740e40cf933aac372195ff0e63349fec6e701c34e4c4cbc0262e9ee65fa63c4bd90d3cfcb766dfa6db68a0b4b939444b4813f21f65f653078e5d474dd1113a239363ea538c7d14e3a279eba03da1051978efc6bcea90882098b6f3459570c656a02652d2d30ccb102ed83e2b178bd3ecfa8025eb46ce310bdc6f086f02cd42b761481e78d140e6fd148744b7ffc05b5274178d00a5659296237df62e5bb6ca4fc915fef90f28bd81f03ef1fe4610841c85b0d31743755198ebe067f3a646e0192200a8a3fe6f0db4b62a9ed6ea0da139fc07c47dbda4685b2a308d9f6560ec9ce2026075f2a1e8f752e37b8890a7fcabc108c6f42308a2bcd943ef90bcc45d84f378eab0d097e60d2a38922e330520ea828b9039500569fb7cfd69365d0ce8303517c439efddec37468c4ec0c71a903e2118887e93663d1ba660061c46ad43290d1b5cb70470a6000b9a5b38ae1a461f69c4825ee49a93c76298f8eb603fbebf465974d5e1f071b37945d73d226bc08a3bd99e024a581364f82e1904cb6ca244c54c70ea41369355adb2065f32fcb3062171134bb73f094be5ca9f6c7be00c338d1e832d6d4c02e31316eed167f1b812e8b4b54d95c4e6b61acbd41dfd1b42545d08d86abb97a51fd1a4a287e6c6fd8fb7fc53a214eba967b5f387d240f33dab2b0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"de369dd743a2a4c86b49047e3d984c7cae7e1c6721d3e23a4f385cdf88acd34b","proof":"485c6126f2ca04fa64ba1266a2ce28c388fa3766ab5da1290657cbb4f2af0d54c228f34ed5b9458c34f37f5c23151224ef7019eec48cb31cfa0a156699323272b2609cd15c798ed7c0dc889a460957fe6aae8540f22323dfc886ce5cc397bf43cefb6dc2d80a70763e23e381c1bfddaf1c61ce1570f849fb45008425887dd851081fb5c2a457e91fed4cb91efd274cb65895e75c61460f68414a6e265fda4a0bad1ac19128aa43115f94c0bc915b3b0eda6fcdd3b28cd947025a7c97303776099c1958995204d3b0a2c3590e8e1e424d5b35f12dce90fb297a5bd5076bcc5d0856e93ee1893f77bc2a19fe202485316d583e8b878fef30f497151639fbc9c17138f9e595db9b9583d873201a7b77d9c14b56946a0784d75f685d772dbaa4b1770cba62391bf13c1819a4c7a61b7339919eb319c33a7d660ef73fb0fd003e993ee2a413eb85e169c19df8623db471fe98f5ca514138becba4a864ba82ba5031515c5b6663eb86cdc94dccee36d24a6621fdc5d1d21a4ff77e1daea4e0456cd068a223a2ca787bad3a68ecacc78faaac1e530af2394a5001f070f6d03a0ceb4f36d626739819c70530962d6b16efb931f7c3e43701beea469466a2cd1938b0184a1e24fcd1d7933be6a0478e6b5ae07fcc97b55e1820e3174c523c0d9b76b1e94b94bddc3da59944dce1ac31f6f1c8e6846a7a05b430aaabf5ff135d48f41bb32acc6de057572bfc9a93412d61dae771623f19f154511b1fed67ca4f430100b31bd0182cabd41c75aa299fbed452726738e708d1e89f82d85e362b2f910fb2bb0c98a5454f692093ea722e3e72724b0690e3e5c94fa5b32db7b558abd9eefb0336fc2b56b7bc569dc692c654b39279f0d5da6bb7ddc70ba288d3879ad706733b059b9473d618242305434b4980df9c354aed8b0cda06f50b0f590cdc2de5283002"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f61eb658f06d37ca3979ef8a03cb3108bff8779eb7a5c835f24d7f0734d6281d","proof":"e64b73d9b8563351a443c65eddebcc7b2671180685e426d3c10aa0f348db4f710610c76b60068807947949355990b9699d600c257c1a9115fcd93fabeca1825cbc31f9663b1afa4e8331428e1b22f79eacfb250aaaac9ec5859a589ec98a12009242acf9a361d6636fde2f1d4c78af63d3ca9d91619c93a1bb4c0c01e764e37cc05a819b2bc30fed4b6faa7efdded0300d121f67997318eb2bc2de0d0558c003c62d8bb670652e5ad55d9d58cb652d72691fd78368e467aa823451c9ff3b7a0a3931076d331cac984edd994a6e912810170b83afc59e791898feda3ec9748e056860b214a1e23b426810ef4a28f59c962f6111b4f59f9e148e9d253d7e1af538c0ba89d5d98abd6ab59af018e4e69d26aeb34be820fd01ae10ed5530a63cbd7d48b3a5e538b450e1d9998216ced885dc62dc3f7c529af461959fcd69db89c849f2fa46e76eb5045df2386be25bd0ea6b130adeb7af349ba09310241c45d8e9520c4b6076a9effd9195b21a89961b8322bfb13bbddbe878da3554fbe4699c2d5c94974bf70d8697079f4d1ba17d514617c11c1688b211f14d2d76456faa2cf14b4097c5b0a937f4fb10418a2039e8d3d2c8ae66e2c154cacfc393a848a28cec65d2f772d4a13e433758bf7326187bc2736b72fb6eddd05f271e7bc76af702055998462b6476d75a38b397afff80eb9d7c2f1199ff6910840ed0167c2165382f416cf40b3723071be12e89c9961ce0cf5b0b9d8aa0f779a47c28147388c63e8f56ae0ceac7c64b5c797b67596945799b11ed0bc9497b4dd87cd9b9e7c2d463706a3810c7c643abd646e6f0673df8d08e3fe6e15a2e547bf988672a98aa2d23247994ad206ca9655bab8f0c2168deddd691ce5a085f466deca6da5a603c12da5e05737a5023a1d53db66289731bbc4d7bc364c74934da9c02e1eb470a8c6493c304"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a29507e7abf55d1d98b6aa817db977af1163bc5034b6d8b826d1b236de51b451","proof":"9026fa64d3057b0e1f7b2c5eafafae04d9503d0782c9f9e74c02efa7116a2a1616f1465c1c8c481ba75571664e763907a6d11bc84d0ab771dc34ce617d8ce32c36466117dc99bb1500dcb6f3a41e3b779a7df422095fd3293f801691b69c2e5e6e9983017148aaaf56372234122a427e06e9498d375d95b89b64ff9a66c33b1a3706ff395cdff60d1b4dec13ba59dc275bc771eb6aaed924a61a53493cc6eb0f6853867839061df812f89635f5ae6dd316b267f72bd63cab01d17ff40f2c910dc57f933ebdc06072d79dc0e59d2dd4d6457d62aad45299ef25be29575cf27e0da64889dd1c6db61aad9a163554ef35fe0e73dc9fceb65516e07bc32ebc70866b88479c464b7393c945d8baa0f2c782101d94588a4487df374da6fb974db7a56602630090a2e0d8c7fda54ab72be21ea117de1ce666108d137d61688f4614bf4b92c00f43e39032f25cfe49aa299071044355fb4eb0f3640da78f9026aa86142a3e554cff55e1eccb9e5bf158274bf5dca9cf8fdfadc18359212f44606c345a1ce0f630375dbac2be5aed4103c712ca9571ea83f25d30c25733939ff1a08797007c8106f31f6bf5c34b59979b2727abeae3682912d5ceb17132679175180dbc05fce26067d63dec7abcc8943d3af83ce6936cdafaf800f92ac7984adb3d95c1115069896cf66efb58335122111addf759c1b4e1f42f9a5c69544b73f66a29d34d30329557cfd162ba33b24d8b66963a2c35ff7e04fea3aecad4e3d6f0b30d6c1cd0f7fa893c55db1f119fc5a79e3f54a4314a12c536efc59076a9823ef791932ea69d5fede3730346821de7550ad8be6330ff053ac8ad98633323f2c39b9e303579badcb27966fde4598b496496dd4ccbe999cbebd15743e27bf0238ba014810ecb47bb0f03bff4034406e1c30eb290532cd41eae5bd2dff3360a3f1a7ed04306"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"28a9611ff6e8c6746c6be68be2d88809c6b29ee30f799cce7df5a9a3def34f5b","proof":"a2375dce42a5251cfdd6e04c12a28456cc0086d5d60accd9f8d5107ebea7802f10c0909666c0d79ec22ad291bf5acccf09ef5a9bb0892d8e0ca8e1b82deb3806e4980a9af29c837bd297240b4d73976f7b072de9401d39d9d373182241763f67e233a06b980a570567f42064600deb475e1712e98f19788c1f9b0ce73363ae16b63d2b1c69202866ffabd48df626a2db9c0c1b927e59aac98fb5d6d0418c5a07e94a2d52720142fa4cf24e4e50947d703bca0c45e665f2cf4ac8d5f6424aab0e92aecd1ab6103903c2f056c555aca331e80d851e69d35819dc8c48b7d2fe33057ad38ecd41c01a1bbdeced8193b9c3fa4d0ccca6560552121e395d55af629a452ab57370d589554544fbe50902a16cdf9ed7f9aa7d59c43700eb9b82165e570b3a1a42971ab62b894da1169327d4990f6bf7a366c7d18a4c64eb1e136105053ea22daf8872686425dd3a316d32d186d1c50d43d9550c9e97deae7be0f150586d36b8599ce4f8f53894fcdea6acf72e51093a6887b9558757e24241f424675368c67b325a5d0cc7caf01a28ad71b138847f3fe25451684b22cd830d5fd915e36eba8e2d41abaa9ffc7cd5e446882dff3cd257935c1b77b95f7dd5c4b3d4519466e692b8b46f0c45ccce7b9f7531d31539580894c8d00f761c19d5f209faabf40508547b82c8ebbe4474e6eb3986131f944ce8113d30ed8667a6bbf2f19423b565aa773a3497f49a0ff01d9e7ffb6c95e658194d68dcad3ac5ead0405584a61c6a50dab9e62416fdb27e5879128b254e0be4b6b9e7ba1a0e1b4e1bf11b6e735a794cdc6d7de6dde4adafd625b3dcbe2ac54ff56052cef0b4ce6145f6525e40881c4644be74de4ec40e686522980daca310ff914cd7bd34498fba58057539e4940d548f83c7c95ae764d35feb3e88ca1a9130c998a1f794f2471f532114c53a0706"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c8727b897dc60f5d7800768a5ad0b6348d14630b0704bc329cc4281a4f50ab26","proof":"ccccf452139cf7fc0253f78dd1835bdf03c530b3a80ac6756fa8e9146c077b45605c13e2e523bba074588f0a86f1ef8bd3ab24d05e9ad6948014ec06d06f2b52aef21b4cfea04fe23aa84719aab827f20597ec905ede4e9d4dd9a9a921bfd76aca4dad48894fd9a9d5536fe297696dee02708523603b112531ce2396a5ec4c48789d881d56468fb37979f9023da99904ea10c40fc9cc948eb52351da9250440e1eb7c61c4e730f7396fa24c04e7a6b855eb1f2cf14c72a3ea9b1917fc2692f0dbf4482622e0777ba8ffa66642905bfe9c324c194455d784bba8960f8ae1c0602ca4d2f868f6673ce55a881b334c81223ba54eda75e4f5afbd5a88bc29a9d883b64651e66d953ebcfdddc4f9aa9243e4bccb773753e8a71b83292f9881b60d4177014e49a0d009a186777a7a7f4e52962e8d0336a932e77cb28b677ab6512dd0ac89eafd627da4ffce6d4c5ccb011ddf64a52b1f8396c857ae590c3590c88ab24cce5272200deb1cf4061e14e2294e33ef1e877c5330106895d7e78563fecb31426cb269539816ea0b591241c5ce38f2b674331afa57fbaaa8c30a24a13e5fd39660146e2414ea5e8ac002123a30990c97ef116bd69c863a0ede4bffda415ed2a9e1d47e7a81aec8d0ebd8afd2c0d36084be5a54b724b15b61ca67470f37f616f0c71a90e59d3ac387b229957fa7fd0ed1e10ec251e167fd3ca9dd2b5b59db935525c990e6713726d3f4a466880ce35ee1c557aa002c3ab7be3697db6f5abf30f2e1bbdee82fa87bc302c695ae8119aa185039fac8c47ab8fe8ade54bcc412060bc35acdabf627b5302140c9c3958eb3b62c6f0fd62db304b5693a4980145a8176868ffd797fb248b2ba5660a572b2b2a53b47682e70f94fd6867a76f339f840fda319838d241b1adff38d9e351d036e9dce5c3416bed242034015493e2a7b103"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7e5903a14357c67d38e920a769688e5db3588f9fbdab864993d7d919c69ff00d","proof":"14dfd61ad2daec34bbcc400ae725b708497c8ba3b0c022fc48150dc3cad131354aca4d477ebeb47c2e78773c27f9da3a8e1d01dc8ee56a3cf1ea5ee01749c22516d658353aaf06800657d5f0fb8df4be57a8e34c3a3e699c9982fce41ee3ca0a348b10b5f2627d6d9ff13ce70a30798ad3da1c928526152d576aec86efe8787c39e59bf9475ab93543f63e475617cc03c0c345ae1895d69c2a344635a932bc029c702d9e3d0b531feb30e391eed8f0f7eb7e974dc056bce4d7c3670ec44b160d728a52a52423c34044010ee7c027c31d5621f47583dc3ca89c3c03390c20720e863b2a25283cde61d2f0e599dc4e6e8c2dfd7561c35810aee07ec0ee2166005e9e879d62b44f03172456a23cd33a0ba5f3a7a841c9d42aae39bcdd712a67cb63aaccfe3ad01b073afed3b38c20a160678c316b28d61d70497213ea49882e3d4826b42788a58afe6afddd1656699e1178527c8b757b85f5d6caffd5ebc09e087ab624d625fbbf6ecb33f2936d7cb9bb3a8359563b9d756014633aef249ab9c7756406c15a90cfe27fd9848b65c79c776cc79a029b6a914ce5950173787af3812d604978ebc43f6f830fad260e7947b933e2142fa506adace850da15531cdebc69c495faa682f5ebccd2cdc806165eb22f285e6de3aab444d9b9660fa1be318938aa9755ed765a67d555d12008edc3bb346d48488d99879d2b8e4596654d0a8576f2f63936873e09eac16fc4a4e31f4229b2bbdee411b206372e695e82138a04171c8f870799541678bb6cd41f5311fbb2bf4cd8627162c2da80fb7e0f921b6d7ca606edf036ceaf3fb437a9b0fb3872c556e572128cdf552b6601612c49aeeb21cb62e263925ebcacf54973251fdd6ace3c09ae5de8b9c3f26f865a164fda0e0c053f1bc29cc37c18c0b10e2582e54dde700a0525232dde85b66e988c43589d0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"962621ca155e358eefabdb0d1b7339b49ac756caa406fec6b006aba7dc648b46","proof":"fe12d78c38ce5a90ad8c9771a623827bcf69351426ad65d0f4583e25b031154a8c97b20cff8497edff8d2f4a7271264039a51a0ddd9be08207a23a240cda8d6d6c17cd8b665d838efc61924ad8bfa9081c564ef75575be5e63eee02ef1198556c2433055de688bf7f6f8afdfde6b36da7ca416d0f39182ec048cfdad5c72f97a70bc00f364b988601838aab468de0232ca6c92570e5c0689f68e28c4f533eb03ec54be0bb48e6b67120b0c77de98154f75ce00876a99fc79af5b7b9316b25c0adf7fb525446bf6f967a82b884759bf701a7288a2e7207129141dd696d43c2b0040982c44edad6e5fc8429cc20915e9f038daf081cd854190b3e02d73fea3b569c017787823e24a06dd44bc59b4f89b6e1b9bae88cab642e4f04f188cf7cd624efab21050bdbb15963c529035d00f0e00ca15820ce45bdaef860e5757f7e58e082ef6c3b649965e6a838c0ebd98f2641a9577a8f7873df96c4e425505e920fe3d3a7ac84dbcba207ff555cf55613eab91eb72738066b37c16659ea7da29aa1c17982b373d8bf83e23569741e2cf22fcc55ef77e8b2a72e04c889720d104bd805df83e82339236f341968346978767f1467f18aa5d56ff91c58f4c0f8dd2f5e35078abe9d55af346e00f12c3c6a90978f1e59db92347375d09a0fd697f6b65305e042c96f76a8a6c2c6bceb52442621d9f85baf09f86af462484cfbbb158af104c444be83b8b8b00911c4417b4472474b1659069ad0c7656e2b21c1ca59cfe3b2176fcc49a45dc21ae72a3365f6f2fd7ec6b12022d115de537c75648cd51f56a369ef81dd5003de829dd4e561b076527a35b91cdc13a5df45d2f5a798f84161a5b44214096473af905cc6403343a6e630be13e9673260b9c55fded0c1c04512a0e5e6d2c840d1eaa8f10879ce57404d6a202dd7dee6729cd8cf4ab7bc5762c0a0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a89a84825d6d08166d71de29ee33549f0ec8fa8340189a3b3079ff9acc413e3a","proof":"6a05edca5d48d1794f0dee68999e13eb6103e775581099e7bc136f5e8a21846c9834da5cbebef11eb93f2a3ea5bffe5113bec1091a5f91a873811540e6dc5b0feeac5f49423c971e534fda8c8573040e359b2b1bb7671125c01eb35ae02ea443e49bafc84e88ca6827278fe8480092fc6168975e1f9ecba49f3374d18d38e8502a2e104b4a5e235d8b5c9f90bf56c231451fa071547de2c7569957bda0fc140e82dbfa7162e0a032d802275f21d3acc0c6cf4454e7f099179935872107a07a0a204840a351871d1ba89a8ac8672db9cd3b201d3ff2cecaaf36cb096c9df4b40f84cc243ea4b7a45c20c718d39f1250ad49f87e219e781427295043a0126ad8222261a91a9e663203dc57d6e07368a3baaad7bf4b92f57123ab406a9da51f16423c3d8c059da2109047acf4fdea1bc038a295b9931255ab1d9363f0476d3c0e407ea7216c125dd0dba8c724a260b4d6e1b46b6a50575e11b0c8ce82fe474c2459d8e1a9688fd40ebf55e40c4298df17f44b0889b860ddc3fd1ab19416d70460572423fae4864f681d0f41eae9535868da07d178275adc101dd572a71feee0d056762c0b904a8d90e5ace028f55ea51d0092bc939591b04bce2dded3ad6bfad910d26d86dd0370c2eb056563f3db3a6ac6b06357f00f665be7624b699ee75b87042a8c90f41f505484ae5042c16aea66b05fa172408fb3c42623b407827445043982a4782cc89c3df33f747bbdec48994a167afadffe7ec07a5fde9c8ba9de9008308b3080bbb2f18632244b1029e58d4523af0ef1488e901f9ac553f52e012a53768b667269eda727768e11dd093c274acd73ecf5a391105171530f86eab0554539f1c721322a3b2f5387636283171cdac6e64646e156c88826466f17ce39c7021d9abd9af421eb2d2236b73c6c8b20a0fb517d91ab31568472fc6421cdb2c300"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"72c5bc7cf1156d9d5fbbc9dc14f659a554a2116b644732a53b7d59d685a26a56","proof":"cc80d5c4bcb4b7c3428d67b25d96dbb22d6cd48609a48ee71914e0f4415a8a6c20a152334e77b2f3ef692be0a1acbb1c58f5f1a67d93a06bc56cb4f9bde0de14b60d7ee86f48c5534d3c30fe3f51a9c2014fc238e78a66f1f686f38088098c056a28cc79d62d90f133547aa785f11afdffd404d27ea145c59b284ad2041f692a85cd2f43bf9fcfd82d4f49d3591fe0f8cefbe1d8907f8b429c4c6d74f04da107df63d30403d6b1ea9ed47f967271fc9c774b5bcaddb696956f903dc3882cc30ef000074936c7febca1706fb91e968da1975381d648cd7b3be5394a291c686b0de043d459ad79e3ef3441fffcdcf0e503bb59de8bee019b1d63e7c104bd994e57363cc1c373ce2af79f25b4083aa378422bf4d9e3b636cc2c20798f02fcf05667529f332cc981ffcd04dda3a1953b554f222973c33dc16ae489768723d06f0e7fe43c656d3f4e50611dbd49a9b2195a2116b079b516cc4328c92e0e203f8a3772c8ff24fafdea374bc60de4b35382d87d395a07a66ec524a520d80680835d8b1708a715547f2bd82cced5cfdb43e2d97d6af9d4571f71204813ecf78c8ccbac5fcca99e58f053cbdee4f2d731d8116d11a720b8e38295c4af47d031721bb85b41f0fb052ec69080690ed7379403383bb095f9da15ae7d25697c84ed614edcbd2ea22bfdc618d2be57fbda84ab317eb4d7854c378eb696f93459eec2bad51e6c51b080bde7a9ee15f90701dd69912f4c7f8c5136cc1e9ab9db6f9d99e20edf663f185d1f1578c1d12c7011823f0c324ada3054daad9e6269094eaed2ea1118257674b8de687edd703f8b86b87d811c3248eef0de60bfda018ceb9866529d92a750044dae6e2d4ee0eed3c94b4eed566cf7286ce85e437c2ac5fb48184068e8380cf0bb1e4f5214cddf0c22ade497bf17ca90621bd51362a16733b0e4d2136f130f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3e9b63e7065a1c352f2cc50f274759a06f87ccba511a5bae618a1145b70d8c52","proof":"7afef4cae10c3e9e8746cf1cc2f56f720838ba11f1df04ad7e04f5330a8ad9048a3fbf1e5083755faa3cdd97519bc0d51fb91d350db656b280d2ffc2f05c580d3402568c5575950d132edabdbfe259ae378884638d0b3ef6f6fac409e3d4a332fc0c85879f91ed26b7f4ec003e9be2e179a5a12dc87c507c9661fbf24309ac5a81e1b1e08e251b70cfa173d76031ad074df72207321f8af2081db06bdc56a00ca3ccffa729476990449fa2995eb28239d970129d21004e28d697163ff96b870de86b9e51efeab524360aa5cdfa2efe7cf3b633f69d02245f6e6c25ccfc356b0a2caa3c079d53f919a25764cc2d32972e43bce30e0fdb809617cab231295fe825b29871ffa91f3a656b8e8fb69470b029dcd6c11c557a86f5333938794553042010570992977ffeee5a2700857d6cc35a953bfdcf6bb4788b11def0fd868f997b3681587bcf5ce0e9ccac5e4e085aa13b80a4c9ce5235e85cadb3a65f5752161e40af4faedf2dc9b9bd98f0e43d5706c4cde6b6bf76ce18506c562eee36a4ae6270e2ba65716020f877ac6cd6e5aefc7aced58b9df4a5fe9f9722c7193857f67be89e0b8c1aa081fba19dfd39bfe8508c25797bbadb0eaa2fac3068774b850f5088c305d36152b49160644677204b5c2ba891d3055ce6ed980b292711966d675638929603fe6f2f3c48b24cf7038bfb6143b6a4ea2a0890ee4745a30a18c6b344caca789da94560d6a3029869e4bb3bd2e08dd35903a52170e2e94938e747261110668c92be5a9a5464073989fd9e196f78153d77b95e92814d10c3c9e98adb1e464b9f983853cf0a9ebc52ffd94b5f9dc7b2ee0298eef8e4b38fae4f09f5475675872603ddc6955fd494482e5d97c6472699215960ef2082dc80500228730001016800a049f5773c7453c1ac6600129b24f5b31a6ef281598d2619db8da8a70c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"088d7ca573b13cf0917bec6ddac512dc44169296975ccb14232d1d7c63f5876d","proof":"686f26cb3337089c9a0f5ccd10cb09c209a5aaa04f9fa3b4a22fa544957463280ab8f02925491ef9ecc7a203c1501307adedea7920526aebd37b4bca2a7e443e16c303995d0fe8392faf61ad736932e34e596e6f28889c15c7feba47fa432d14a63b62f011f26d0098ac8e15fca80226ab67102901a1baacae4238bf2044b122897a750cb5cfc93cc67dd8d233932d58d6f15291e388def826f9e55f4bc4d80a3f142ea8fd9fbc4855c575e82ee1ccf4976590ba5737c609f6e3fb3482ceab0d1e60ff7b0a0281fa76784771ff4dbb0e423e610b85d935ef38e1d7504bdfd001a0494262b23b3f23dbae4974311f94a36e11f78144ac5c3606280d83eddf4f7a22bca35628a7ecc66deca61cc884176f3385e798a161b407e0671111c8b6615a3e60304539dd4fbc3efe2dbba0b8934d88e3258cf15afffb029b32567ef7cc5d8cadfd69cfd62d193123092324497ad9e4e07922d66b1b4b1ff3dbe0a535e0666e2d32fd0b2adb2f2af16146b09a2f048f5308f5806d4cf53ae6627170c4e119e20441cb2bc66b84f0bd20b02c8dee5418ad27acff911f3884736d2c3fb9844ac0df7c42e99feca5b1b1cd20d4a220de3883a7105e3bc566e3f5798f01a4627da6a2f207286f879db9ccee47bc5cd8824465a8a7833210b23cf8ed5367cfc82618b5a5f062117cb85d494b8e2906d7a3984c3f128318ab13c67cb52663b93301688f38cf69934f58cf243641ce180b9525ed957f701ebb2d7c602fa5f82ff24b7a1474b1e7f3ec1aa7b9bec19291d54c53a9b59c2ab5c1d1e529aae395349a654e0fa966597a2617aa57965c23b73017df88926946ea9adf4efccae59085c51099b61cd430d6da1579d565d5dd2503dab8c71c5c13a175881ecf53000fdcb00f3b5273e2d7f0457869241c9609b66bc852081277bf4b6f5456f5594ed34ec00f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2eb4191b0f8752570b5284053a84041ea995165088cceb0d8ead72721c9b9e20","proof":"9a1ddac740795c29338bce4772a2e777e1ba188b52171c2be3f37f0b753d3826a0657bc1c1bcfaafb6c3eadd24e5e311629fe97ad43af57ff0cb8e91455e6248bcfdfb70f16062186b2185264e88bfa609d1366a3917dd0754960e5f840a787fee011e4bc577df02b276c3b6384349aeef682b1ae39995281d1ab9d439b39b171df584f78c8e995ebd470efe9a21840a662369e3ff9672acec5667545dfdad034b50668cdbb4085d4ebe13960f7fcbc0b0915e0d18c5c13ead17ea6e3eafbc0e3dfecd56c7990d1eaf9450de7ddde8611e1a46ece0395ff8fd4cf5820323680382528d400a8fc22a02180fc9537fe32eb7a8e2277a99f9a8384ddabfb52b7c1386a1b27bceb5ab7a127673053f38f3443267bcbb484f36aa7e83a7049f919c434410083d6f585b3cf6fcf630b0640a1bbf93a14682c2f005a17e21462b196b28b81b98107bba452ac78a2491fdf27f97852ac7ac93a5c2dacdfa9427cbc0843334ce00ed04e74e2d229c088194131dfd8c4c30485d51e3c779eb1a3224858d4db2c752c9002edf3cac90f2c03a4144fd40df862958765b4e3b1bbeadab81fd6d383c4d20ad04eba39220955fff91fd9795f022ac65f94ed9afe6cdcaf8df43440ccf9fd822b1c64646497df62ef23220a1867741dd908f8800932d6bc594b03df4cd520e6f5bc729de1b74cfeb22872ac8cbbe1a606b59a0926152007fa9067d48c953a6a2cde1492def5431a98efc852adba67340e6d5987e995e59c3595825ee4bcb10f2f8422f98771f1409983c70a2f92e0686d42f15e26711aeb3a60f73740cf79ba395541abdf4b27476318d30ce6fa37b8c6f4202dc2dfc1894b708205306e75f8190efe7e96f7c5af523edbf620fbcaad2cdcefbb8b8e57bf516c905b73cb5472de91413be77b703aeb7ac756db56f765279fb1eae5c5918683a4d04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b442dafb1d0b664114377bc73f2be652f13d4745661ab172ce8ff3c9704ddc4d","proof":"10978a69bb884df48b1b346cd4ff1e7c88999d4183e7d275cb9a4810b334820b8a3fafd85df5ca1748d828a5c0f7c90ef5d6eb60c7f6c8774399d3e4f73fdd1b524d3a6079b68f1ee8dc9eaeefd74d034bd96012c8588027a5d8bf4451ab3e1b70614aa413ba922f7814c9a7f1cf8e03f59a6c956636d4fe6119e22a3aa0bc4532c6b0e179d281cc6bc01dd3c0f434688f895c411e53013e251ec3c7c25d1c037790507252ccc85bc043a6d4f2fcfad5aa8b6c299d4683ca869dfe19b3215e021d956ec307c2f2da02f43a5af48445ca1a8ae8ae7d8b879d4654db92a3c0480bd45fb4d5da7a31ccde47e12dbe4493288d6d1ca386cff1a95e878e987492d24c7e54cf5b436ce347669bc23d0e8f0ef2b1570b6f1abda2a1ba8285e25640a061c0e5d573b3833a08de15acdd509f9847e10bc3995426599c91abf869347ce77846b79315a0a4ad099b8c2d16a9fe812e7b584090b6aaa7e0fad302e1e254713c56caff3a1a6e37eda6cf38cc273ce1c5e41657a190df12f55ee0296f6cee3c389cfad1b0130f77166433b07b9b7cbe8b3bc23d8bbc4065806b0927dcc27f1311f05b1fb395a4e5807978171830417ce5e31e966acc836582cf9d799d7c23c615881c1164e591254d70c6ccf23cdd7806fa4c78286731708a99c8e36d8d16372c1604875c40373233fcece2bb91f56ee914d2b5246c34861eee08cf80617f5322ca11333bc98fc970ab73129eb8a1e617c39d0b7a78081f6b7098774f21e6fb7a404e64942fb7d7c259e311ca14c65ae38e5cd6169f38b83af7a532c1c5756c2a369f2f68eed32d77d3ca8ed2fe84a4d98e2cc2c07d6bcb3d19111b4c684992264f2a8e44475d56d5dd1ee74979b0dcfc4867ad8d1f305ba14553a26a9f03c50580ff9123c2d276df4a3d315fda70021333eec6395bcd2cb685a1a59ab811a70c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1e4ddd86d83fb385492bc910f5bc4fd7da8ed405b75b1a08e143c55efd1e5144","proof":"ac3848cdf5f76843c6b7833c0199a0343b74ea3173f61880d86c1f1a9428f75c1cb2eed9f3e6693ebdc7b5eb48b5f17a352bfac8c02902d74ac1c088513c136a7c76adaabe21275185f87cd62b777b4d3af3dd0383f114c8d8bb6f8d60153a3c5eea049ac896627afaa8c11778417d120636dd34f74bbf5cb3255a595093e04dcb08fedad35e4b0db23e4a84fab09c03ade3bd8f69bfc00dc58682043c824802938e006162eeb0e7e99274195a0d22c4e422b84eff99b129ef8df7dbe20135034478a109e750e2aea3f6b456bdaaae7636b45e25a45423f0dbdee6f9c5775105827e3d2f8ae21d6bec0fdeb282d91de1b6c9975c30cec59ebc885e585715e32f94f90dd269255f2377cabe1b13cab26c09fe133df9cd9569a44249c75300cd348645b7003a102fdbdf1821440cdcb6ee78f3cdd8727eb43e0df930dbe18b1268aebfff1d7ca5dd4843c68e6520e14d86afa1be139681206e9f42bb698a920505b844f44766ad2508076f0c3ace7b47554e0386740fac8897f8d957d3b04f9805e4595964183578b221b53f4fe99970be5dc3c8134994165d211eb224ea99a325fac31eba2ee4fe5e9d66077b760e24bc9a0130a408c38f0e64d1c208584953184ebd6eacec50f57fe360eec4b8d8b52f77bc6818c6488922942ab45748b3892c7237d2368289ad0cd10b39e5adf358ba7fba00d8a2ad54f7a5b4065dcd230b40f8b4ec79ede8c9656a6f29938cf9869b88c7d1139ecafea024d93e2e55baf707faa5714839ac2748bdbc80003b5c1148e39df69c00b7a69d30104425fa6fce6f4c28be1aeaa7301f939f4dffc2af7862b3bcd0354aeaeb0da45230a6d860d17d5c726a8f7b50fe071a859e3ff33cbed664522ca976540661f9ce7869631c400eff495dd94a5c0faec9ceab7307e6f6af78228ad22f269d5d94fcb204bbbc1305"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a8659c8eee6a469a77a0684a5a29bed3fe6901f12aeed2a822a22d3692f2ea07","proof":"32af8a623dbbbd557c31d1b08a7e2632f751100eac0b47654a45bd2164d24e720aee5d338eec5bb5c7227c019ee8b8194361b24c089b5fa0a06929ad965a16290cce5fd987ddd9279ad94fbfe733bcfaf635e89690f1a1707a828995e2d9685fe2dc2c8b0db08ebbf870c28c9aa579f5d1eaacb4f20eace42a037b9107ba123a9749c940be955a2b1aae29f6adf1ce3225226976b51b87c26145beb035983301601ff24a88d3de13fcb8ac54d19526d595f0b40e1d1213832708deed939c440e4c6795e0fd0eb944c5c1ac0dc7a71b581fd821a48d0390a0aa89461628eb000f7e9a1d7a664fb320be007fae35c0143f99bfb44b94e02aa3c96808644b2ab1665075307c900c1f321468a4dc67e493494492067d7cd8bfa174672ce76d2eaa55a6cd30510797d8778af12e834e01914c8783832ae8a5517d399b784094cc970b9081aefda64546e2266ed5370c876b384c72bad1e2d4b4d51406778dcebd8538dcd9981618f145397034e637acab40d0077393cc243e5bdd75fc79d378864f19481141463a39d319e118cb40355b0ebf851799bf26022734aa870eedf6b7f16eaa2d93ec66be6a08144f2d5b97997f38c2be8e224689a7290e654c1d871aae79b26d3ab27b99ce679d0565d1dd30f19fd534a87d8dc88808095940e81473c15d5e711f1edc037ae16268f52e9a220111095a78088ed6f70bc9f027780515ed1cfc517588add1321997ebe78d58933023005f9910f963775f315401565ad41a0168c0211568965418f556a3e5b53935ac2a69bb9fa2e28caed1563ea9d8058d4270a6f10b7ceea19d73b2e7498ff9e781dae31b4d043ba9105c8f1b757349d71ef9fb6972bc5cdb1d4164ac578319a8508fad4e945eda1627e9036d2d8fef5c060bf74c9551bed7fc5ff174f4bd6eb03f73e9695bee0c54352498bbdc9f239b0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e4e71f611c236554a9f52ade1ef14a18cc782360570e9447f894ff7bdb1fea33","proof":"28287e3735ec3a27bfc4afd425f6d857214fc5a6c1a7299c82262875e477a248b670ef3771a8ff342c2fb5efac1f724ef76573c405a50df888a61527e8c8ce58d2c73a9896941e6729ff21b5145db6cd956d6b14a26a5ada7a1e44f6ba4f5f295c5d8b85db6a5561bc553b3c456c928c0441cd0d483f0d54555e2efba07ba746da7e0944c8ef07d7412d1d1d223b3b697895820ffc82adfc2cc2cba9e4ae2308033ca2f94607246468d77467298293fbbc7fc61a1416b1c9a1e5930ab0619e004fd666c5062c375558b7cc9077e9d5015d4879eb5c9290f81024494402cd50019a2fc988586149e8106f723b90d6a8f0fa1bd20b204f0702913a6f1969f3121f0a88a7fa436126c6fdec60215718e975cd9e05d8f5238c7233c7d8724260de098cf8064f20b5c0732ed576134122c7fb774f8caf20139c8e35b810e0ac376310e04c230dcb4bc4aa57a7e32422d41d994ed8b3902fb2e9e6153d7f13b4e6ab60fecd4ace8be3058f9417f564be5bbf9114966a329a8a66641a42ed9902e771019c764d189cef22c7d44a28fd884885efb6253f9f02d9614d666d14fa558aae1df2d3470e96fa32587b16ac22fe5522ef10a1ef0d66929de7b16530c5049b222b8adab448a0d5f74f65a1c4a7afd8f91d8576126a9fd22dfe27b41d65e0c7b728fef64d0204e67abdda8be30958348823a783cd656bbe64fa947024d2a17d807ba662a78d02dbff2c48a79bc4a6cb44225e3f9f8cf07ac3a794f752b35156850f84cf77ef82bf6b6a18b1e0180d61a12dae888b65ed5dde5d02d67ab22284586e0422c2a6e37bc9525cf0a12b107991eaaecb42ac1b66202cfd52a10f9337c32d32febbecfb3ca0b7541046804cedc5ffb9a5e2202719d5ea9796220a2f921303047c9991e44716a9e1f636bd813ec8b1548ddee5ec8f4b592a37e24fac449c0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"70d074614c504a1d121487556373e582c1c487c47ac823d38ec528fa1eab3446","proof":"5a9536e19598e491b4f5ca04f19f6d7544c5df0ec41216bebbb9fd092f471050a6aeb6e4496fde56168829967d5537a44dc57b960acdf881c326ecd90c245476ecc2979ce19edc287fb1cac8894ca83e94acfe8f15c89f5ef632c99a60281c1a3e774e58941caaec23898a54073a586e3773ea519c0e93957c26dd8dd4f49051ac36deb317c6edd2c56c35f7e819a8ac7df2694f3367203b5d99010db1c3be0675e9b06921b013dfa3a72f073c74d2a340e40c9a521a8b6b072027f8ceb7c60bf7acc74fc37590c44266aa7916c0f8ba9480ffaa56b5323668b27ed59fdeb20892943ef3cf7300d0c7680d2183e9d5b5fa50406915a61de1e345d18c91c5114fa08664061f028fc90013ec8f3a60cd5780dd4d199bf5b2a23cf1ae4bb3c0807cc498dab55ce3676e9de1bb283595b12dc09e7dc0962b8c71cb0e0fe5df24d276f4e36420a5154a39c33308988774d77fabf0bcb520f99fb99ca180679815924d84e5170d11861204acb77135a71f65b408084880d0b8db7845a364e314b22e3a4cb7904f22e1eb0592e6e5f90a3f6d4482d80d8ab4dc82d4c2a4c8a10cd7785a24b9ebd2f0af7fe378a675a150c0d34d49e9ab63d7566a9ebbcf32cd25edd0035e8ed3eb0ac7e1b0fec24b6b315ac39a1a1142509e433d6ae21b954ff63cf93be4479faf5f58c8b100cd29a5f4ad7afb7624d3f5a3e8cc8d1c140994e16ed90dd09ff804505f6e931a50b3bec9f4bd542453fe360b66e7a67efad1e763d4c0109865b11f636de2e5ef7a6034fb4425efe95486342295c4401a41bacd3bdb704548b9ea1214d0d32ffb1a652e2ed38a3aa8a1660855fb26b02ec2bb614d5ac323f41563498fcaf1531e0b80b1ecbb794b1d59746dff173a1ef76365fec002920c93b05686aaa0bf14709308111783e113833edc9946069ee97d624b5a54389608"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"de32f95a98836d82a39aebcf309a4f224703d5266d1672db180c3b9ddb5d7377","proof":"807c8df254afd59405328719cbdbafd608073d99b4e0ee0069d3ed9d9823eb10dece9bb95037527c933cd780948f08c011c4c9800ec780c31dd4b65a2206f94b326a8356eb45bcdc0d43db3be5404f090ebf40b7e77d2682c0d7de63ab53150ca2c9be4fab35121595fb79be6fb969b855c5dcd052f9d1f3a2d73dff458e9f50a7e107950c468c47acfdf750873b33384442d06e1d982340cb70ae1d753415084eec7524af550b320c2aa0ed9e02c36ea0ce75f09e330ea6efa14e859a789e0f6d821500983b8de11500216de05e703740fc180c8516a2fdfd3718622ce87f05b88014f6e0ac864fc1595015b87ca63ca5c7809383323d5d5a344bfe7f7d5d48a070184425f721220651ebaa53befa10c428543abdad4713477c0acae9abf36e2666c734ddcc66ea6a60c39dee854d8084ec70cad3d17da9ed67ede59d4e1f6550d941475c523fe21be6ec8c5cd6c9eb707c42cf1530b37798cfdffa55d23e64a25f615bbd5a84a4ac68032430183543972704b11834c30e5864a0bc577bee12b021534fa76dae3374738ab2f2bc43ec0135f118c1b71700c6d25a88746307462261d9b108abd599557bc52646d6a0042f7653605445a83c7c63346e5fabb83d28b7405de78f27579b92c9e69856690c1f58dc252e6d6142ffb44a3fe1b5490c0e9ec80e2f795432aef215add3989f75fc5fdd673e00c0287e70d2eef4b3e62a7efa1c333c8c4ad42489f697b2955af62cf1a42338f7f3790de015b774c87d6f10d29aa9a13a55c7dc31c6361d3d87276f343784f2ea878964d36804540d204566a4855bb318933fd570c72d85a6a5689bcbaf1442cce6bfa355683e25e4c30991aa2c903ed542a6c9155200d39f6b316d13e2e12ebe7a1790c41e33bd3c0c02daa0a84fc22993477a95eaeda8ac46c5a80b5c62beb67baeef0d9d9e07ed990f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c04dc958142fc3ceebc1329a7440248fb0c3520650c5e3b44ed2d2407b82c369","proof":"26660bdc65b1399cc36239bdf41e157d1e9621815571349a051a032f2063252d20743e5455643c82a85495ffec42529583934f88a44f3e5abc48f531f6995d6db07bff77b17945199f35de566dda3b3b42992cf8dd07657c90266dc334f009716ab318f8bb555295d78821a0c535be61c4d50b55ddb418e602721089c66ab801ff9779dcaf24690a6cfd8e6cb75b2f89ac0c7993f64a9307f1378b0eaa76d407dd51c5a6ec16c060b2f6c87cd808cb28beb4d4cdc5d21fa3446209266722730e21910eec1fe30d75b7e87e27c8a974ee51c12ca91f7ef18f2a605e267f5d080be02f83dcae3bd3bea17ad0f56d5b62373cd5858cb33b6f774d18d76a1b690808229e2bd8df549b4a46257869aa0bf4a4e4f841677c28ef74240ede296fec3646a043bc43fd67a64f5c04f54c9f464bf082a0624117b21f759e3c1681b5bcf138709f785e06d18d0493cdfda31a022f86ded783941752df91f8bb95b9fcaae43a4c32531e5d5808867c032414694b305ab294612b921b0c20fb8e4bd53041b97f3096f52aebae91014c2f0d3ef3e6c15f1ac5cd900a4ac87693a622a173a1140b9ebad696b13ade2bca84e000981add51ed4fdc53745ce53c7d70468428f7070008479635acd9c6b88e5987716fddcee71e226772a91a0dfa299838687a8fcb6d108d143da5df1a7d0323c0f8cee1550273c9f482bf9c1e9be0f4facd116a065fe8e9881e3590728163d34b345ef4aa843591355588c00df78fd5485e00dd3479ae4dbb3e89ef60003f91b44922bb457662e8dadf1abe1d2d5038f73ff0b25b70283d4883e931df077004821eda8b3432cab11eae4c2cb96f7050a9aed1cd005a2bf68afd12e242710f91bfbe6338f873c810cd152e075c3f9f771cd68679690676ae98fc35ae61a78e67036141a366ee047b0af937eaf3f0a655742596417b0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3ebac706d23f08183b25997ead56e8020ac97330084138bde8c9027043ae0c1f","proof":"62bf28db3d71e450bb3237716bccf39de67f26ba3f75244a8158ac7f0863626dfc407d78c6df6c78db5e9e66263c34ee8962b5fbada1b8cf9b05846db8814f1220da7c61380f5a232b1cb9303c44456d79af8e4bacdf629d277bfa3767569a0b12f3bc62711ea42b7e8aa312048db593c8a5f78f7e9cdebc9553d24e00e1c90b72cff71b8f53da10ff4065a8b1b623e69ac84ad4fa946cd95ac5ded44fd5b808dc9f6d98deab9ba62d776980d9b43070f3d402280b691b4c13f5e389663fea029bc44870bb65abfd69229e2fba88a6223c575182f209df9579bbbe269582ba066aa7e989c9ff731c7c55893264def3ad52c379a32c8cf83fdeefa5fc68ba1e6cfc0cf4940046f6a7ec5e01f8100a819b6598ddb4399f32c48a4837042d55a847a6b78b4a27e3418b0f16f14e6b25abf1847ff81b1d5945055bde2b43fd6b54068afcbad4e2e7775eb0388378bce5cce4df4517da9213e0316b6a1b11d928f14b6e29f3fa54bbdff0608609112e80d2fc6bcee5c8ca51c13c86ad81eaf5f1576214229ffa046a44ddcc234f708732d6739a15a42855fda0542aa780c8bfcdeb25609fbaea50532f861933fe506c2dcaead4c5643f67474b5bf3d13fd3f2c5685dda830652851faa077a5e8b4de75cf99157d18e3fb93c00423863aa30a5f9470a62de35fe0e7a39c06ab3f2f57ae5a0f210ee19fe0368d089575373b78dda88426c210c693ada33b65007d486237c43de2561a57b187516888bde6d3abccdb44048be91986fa7ca3077b0488c336b675f37423571853b3353be3467a894ff494f3a88f00e7ef3e0482eb04046d7b48e6ee9c825151cd3efbf388388db5c3e12121f2c6626aad10dadcb57cfdbbf192da844293286084a852c144f85ca3e32c00180e0bf568ccfcce6032c488f036ddeb58cef8d082421e0e7adcb2e0e0b82f60a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"384c15108edf30b133830e6063b457d96248a7097abd3e217a6314c9f6b3e47f","proof":"fc589e5f9b6f6f56e3f7be4d4d8f69e6ed669b23b14c54bc944fae31dc16f77452e9f6eb3aabc9e5d962819a0cdedaae6e788f4a0f7d5bd483dafcaef9876b43c2cff34bc19eb498518dcd0d83147ce1e0bb27a034ecd816ddc6dcfca5eec16b3aefb4cd7a203712b984e502d98171a89a3488fb7a3cd2cf2907629d96a9fa41319728431d86475e2edc064942ad2d11e75339d6b94e22783f55996264d30609ed16c7a2aaf6650ce0f8b61a3bf5f768d467c27118baff36251661620e7d7d03b7aa2fd5652a162ee03842423d4d5cbbe626a1c1cd931ecec8e02a5f9fdd3f0e70d0889901a765ba7547b69e5bc18a4d6fe53dfdc46df382e28cfcd281101e517ef3eade4d840ea655a91b1d9d1a1cf7a4fdc7d546b184b05e478dfcc86bd9646a4cd44da8d0ea1bfb173e48729a21eb2fe975ee8300284be8924c378685fc2db29b8a557213a08dbb4016856a107d576d0a29ac6a1ca27b79c8aa4590281c6fc26cb602ab14aa61fc4e763393f88c84b87522aa911d1bccd0cfd30864acba58629443b27ee0161fef37453c58d48596bd21472ebf6623dfceba1bf732f5e83082412cd8aef4f3922c8974eaa91f5456981059592d1ff46317d127036a10962810614bcc7c6f38557fe3c49cd5d7b69f0ffabfe19ff5627d70b726dcde1ac30ac61f8dc144858b09496bdd75296b1fabdefb8e7832d96a6fee709d7c7ce8107742693d7fd5b6de27552549c8fa8b4a8049d9fb36802b24ff081243b21770d20e92ecc16bb69ba4d1404d61f2ac896c31b5e8162d084e3272bf185c8991edfa16cab439d6df45d128f585d76a4c8d5cc0beb6f172203fef25dae38406f399ec1ed248e7d68a1b9d407cc1315a491acdf920a1fcb7b2ee951a86c2f28d5105760854b40f361aed80e30d198b3bfdaac7327db2b5777651d0a89f4d6bde44650508"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"361e87534373e9f895ddb0e3a4d8ddf2d02e9c599a886caec9e642f6b42f9e55","proof":"28eb6dab556a2392ab3a5d9f374375541a4993647732cb2a32e479b2977e4255ce525b1c97d600474c348f7782a79e4fc0e5048a90791f7be620335a262bbd39c23a299058aaad66ab0f1a237a5eb876582733d1453714fa5336c2978a358376d62badce425f7ae034b4827fcf4a5800cbc0af8361e9ba6041ff19ebdf030c2a372e40ccefd77ac3920f2f33e3876a948bbf1ab3fcd08d17fe8e4ef2a001fc0759084391791a50b14cba9268f1ce947c37568ada65ecf7a681e4a4ca06c34807ac2ef990c5e3987ff10bbdf438894e6b2c8a60b88bd2e1e7c1260c8e52976c06fc056e5298fc0acbd80948a204f73705022e80fe1c22c3eeb40dd49d47204249240be89fc66b4c7f1049ea5855bb2ad7a0ea00d33081be9c011b349ce42ba6479a4487715223c164c8bead81340ed2c1dbf7491ad2be3740664aece6dab332012037f98af6b31bcb00a57b34a6b8ee4c33937e0a89cb64918d3aaa0628204e7036d0011b7d9dab3a2a165c9ab5d7c09925caa7f6790bd51c20fcac1dd741711754d9fcd374bc169de4d3d3ca9c1275b905ed84aa94fd879bf8d2448ac8859962a8a54d08d581e15b0ec04bbac7270d6eb5df557e2b673595e225d60e5e1e9b35e4892b17167ddd850ad3f788271256b35f1875e2cd0e53567a430e3363919e3c46b36850f3c8a550f5ed64d98c713e023f5de7e3274e51ea0609d771af45165b34ed0ac8ca4cb90ee8dbf4c52665c5be8de448a1a6749157a935e374edbae251eecce12396f3005ac63c04ef6b0e6e4cbb1c807c6399b9e0a51cf3747082de14aa4f7d382f96b00ec8fbc0795e240e475f50202f2451e13f9f4625ecd4750b464c390fd1272a06920b0688bbb8e4ecccee09c3ca9175cfa80aa3d31983e88e01a1e38525110bc6fa4645778bebb0f13570696166088bc155cb2488423dece905"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a0495b278fe8d20a58c2dc9cea958c70a850b29338c21218da3b5fb5256da809","proof":"9ecd289bc40925133190a499e05b7696946ac836c149bdf2eea5c7b905a7784a2ee982ab259d983e6c7c0ff126ded450d1d7dbcd3b8aacef4461c4cd941a8c7d4a2c6238fff6feb6d4f02e8192a6fa70980f9e4418dc307a22b311d6a59474774eb3cb09dbfd7ab491b1e48526c5ce3286981496e90a96220bc96d6115e60b10902002e7fb1283333a9cbd6331daca4f5f36bf3afcc17b257e421c5202cbec0d52338b37d1952b79cdc07efab55314314db1f3c82980cd079ddbb4953c03dd01946d89a7a3097f8e627126122fa798aac1592c5ab8ceac3e11dcc1fb11a2e4073436037d7ba657943973d682f132103679bbe6dbd3dbab0e5be4240ebcbdbe0c2e7906ebd207e9fbef5ec4087bab97320d083099ef796a84a6d3f6ce39daeb5ea0ea4df6f28b807328efdbba7ca6425227ac80a4690839014cf5aaeabb73c853764167ad078a5a6b4fc388a6628e091242f0c6c37a4116d883a889041129b8068688446e2979f9d0e1892239bdb03603de0307fc4f98b28935670c4e9df9f703da67121205fe7277a0f850efe65d9ae2d4dff5b3ec683815b253097ee9147370a63959adb6f1809c92f0d000563bcb9f45794d4eb6b2d0c7861e6614348d5509a2be4d5977e94a2f3d56bbfd0aff8996c3fe96f9941823030f1e6b89d2959f1b12483de24fbd7c0faea9cd2390d13aab99cbfc3cd98c559479e9772804bff15f3e54c91baafe00d32a494b33b1bd0796027f63fe7a31421968d74e48b68fe456eeb63306cec5915c7b43eaa923d974d4339b8e5a21c58ae981fe177504c7602a08a54fa4917abda4e44d77550f2e6c64a18b4af7a6dc0b8eb47e645c70cd5a05afdd413be3fb0b369925f254e1ea715e083762b3af9ef4f996252bcf2e3040038fbc13e40dc26944b9708cd296eb21b44129dab94bc1f1c53202b2c2e39cea03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fc1db0457448e79d644e3c15417c4d3a07702bfa48a94e0034dfbc35fa064970","proof":"58cfae0d049ac5fdbfbd2386ddcb324b105fea9bdd360fb98bef39878806070ba053067fc9f92b66f5672f6b10480d419733c03e41b1ddeb78aa87f0c2719b7b085f87005699080f0a2eda620888e181375e0d5f682cb55c9b96cc25e6305e04dcbf5f20226b214399f0d8114b66a6a68668c717f6eb610b225ea07e581b53238fb1e4ec8d527443b8ddc02f83937607cb72c8fc48aa06ba49b1ad042a0ce70fab34b73ffbb20d591779bf031a5420a3ca824070616af2a9b8431886bd20360a7816a67b42b42a3b9f9e4eea46c6948eb268d45d70d5d90479d533b84967ab0ad238bf5f3d84ac6e6604acfd79bd839fb62a0b921c39d7e00aa14445632d6d1bacb816ec1bf9df72ead0ef29ad537fe302027569e3f5de3c7328b135ebcf053a44ed94634114563701c1bd95ef026f529807a08015016b6e0a1dff101111916ca25ad66372fc4aa6f7e28cbee6830a2281913fc0efc0e18990e8c4d0adcc9d65c2cc7a807ebfa01b067ca302c1ed5d53297af41920cc4ddbb9152718c52491214244b421ddda14478f2308970cee3aac550bb9e470bf93e85ffdd71d401a6f6cc4acd7d08e7c1e07a8f6f71ae85b5d7c825830322a0c6d23303bf11e5e2e4e5d341bc0df3f7df3f6201c051e296b4aea0f6840fc761b5291c31a4e88cb599751cac63ece956eb222044c452f1ebca2ee98b09d63b5bfd12a9561ba82a6f3eb47bec9f4d7d9bd8627219811a643dbccfde7bd1afb8eac8b001d919830a9b7351084e0fdad776b03db0176485ccbeb653648812f16815ca43a897b3680021e506774d50370d780a569e2b43150111c6ab330d480527f631ad9fa22193e6c8aa142da9631cbc2cd9f765810c682bce7fef178254475d904e4b72e19ff300a48b7023084790372a9826ef3dd9e9ee77ffdc1b6f1174f794703cc3d09f6663fc08102"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"80d73c3759f9df8bbb43d703dfa33eaea6a701cfbc4c6bca6e65ebb0d6b82760","proof":"cccddd5d7857756cae977920c9eeb3c7697a0a42fca05234e02cb9ec1b32ad5c22d50ce65969005530b976663d23d1c7e93baf7a553a283c33f7749256c8225b800a73fe3309c5725e427c593e7dc60046aed3bf62c39eca60b8f06d29035950ce49a19da3d2cb680bff4c702d26589f3c1132ead67cb1e01d49fd8e7a8e2003f9b1bb07166ba499c75665e31a97ec8189e729ab49cc2d405baa438fa1b0000e6b10096960813117b330e7019ebacb37ab5497605d23163766d25ce23e758e028cc81158c3ebc650751ca4af879f12888d8d562f1019738c6844e36a62662a0a9c0b8cf7b02a9ca49f370811be3ae90cdaac0b8bdcb0d8a5e4c4c045149c7d1c380518117f4cda420a6ba5692afe50ad9bb49fe94b7288bc2b4504aa8c175a06a2daec84d5cd03f781cef1634e11f0f7d7e0bede938c6ac40fb1c697abf72c76109e08706c00c7b0514d219422b97c743b97534959ed456c5034b3cd6fe8e604fe68dd03421a2e6f5f494a5e4f8781b04c2068014975c6c0de91e8d08ff65805bcb2e460c3a693668f1db3236aa59dc6a4d4785d39ea12f2ed5e109c337c051f6c0cd06b5c43d72c551055eb6d8fa39a708e2fa1f5d8f81b74ebe5537e592167a4e23810062b841bbcb3fa7e425c7a52b4438729a7c0798dab3cc2b6b7d91429a6b34c93608a44129425ac95f427dbcbd94dd534a7f31d94f63af3d18c962a72ec208d4f852e67ac78a32ede76fa1e6ea8a8909e190cf296a6a444ed19f7597a00ae8735751da7cd7ca4548f1500a31b57184fd7d1494eaa3c42400b7fcbcb6ec6066715a9a13d717ba6c9e72c217778b558e52a25aeb74d1dba5b1a836c652b61587bb7a43268e2a28cad330723df2b72ac493ac5830ac46bef4d787573a904ac65f5ea82aebb8fbf0b54abdb3c1badcba705eda7d8c1e43ff5b0de156a1d00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4a9a230d30c125a96b360c1fca2f63c0267c1e6388069c2d89287e8c802bb37f","proof":"86e244c6d430792addb8491e038d5abf4bf78d1ba8d5601ea49514932dc8a813e264807d1e8be053e7288af46546bff92cc3edbe274795c6c5643d68c2cf6b736c7bbdbedf68de1b09401235d12588a93cfa9e2c6084a0a36b5f6e236bba75026610229228825f1a98154414717f53064e4c2d740f1093555cf56417a38174140d49794997ae00458eedb0ab16128eee294d4160a28f0b1534ca3c82f053810c51c8f34de2cd087ead9739ffaeba083c41acec548988460e78e355548c8f840c93db0f724c61257e131e788e8f2dacdea7488cf7e26792d3eb1683125004d80d26e06327f9ff8aba43c2a16b450276ec594b912362b33ff2c3e46ba5e1daab5752ae0cb8a931f5c365de7671d818bcb7f73472ac0ddecb38afde958ee7abc6767465dcdb07d4ba6552b0ce5cfabd33b1a169effd57ee8e626f557e1b89e8c823d23137c4a33d90b7fcf8e71ff7bb934cf2c613c97af24560fe29b6e439da13583c9c9aac3e2af97a1d7f2354aed1bbc96ca71dc45afe575f9e407ecbccccd65ce49edc01295ed5c58c77d8924e9597f712a59628f6bd3060a1b24c86d6b0fd21e680ede433d852bb6ab2ef0521e589238041b82e88f1c0030192439f82294f26ba8bdd965d1be5e5634318b6dc45bfdd8a154dbdf06dc3530872fe4072dcc27214922205d36d9da1363b93f3bf8281991c4f217116980e4b2c0410363a3c7b398a7e1bfec90af4c972e52fcc8e860b86e0f00021a4ab96f8f20f59f76b173a034efe9bd8b1ebefc6209fbe6889b6856ef3ee0c3d8d928c229f0557556f12b35dea19fcaa89144a04b23ab8e90b4d47b57c4ccd8c5fa2ab53932c0b1f5e83654da20ccd73357d7741119ca12680db4724a7d88ac045077a613a8e4bda44481b0bcc8750706b5454d3d64bf4a7eb9dff9586b0cc7568729751877ef8667571c709"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0c66a08179db0c3d64036950b368db93dfaf869feb3bf40bfd7078499f8e9e7d","proof":"fe1ad197cb9fd38637ff55b4d22599896dc815dba5f57ddf3d0b0b390c6e714fbe07ef485dc5a2d49dba41f63aab5f1f5841798090eb9998ce7e6bb36ec1112eb0733b7c218f0cd5c3a1347a33b8e881cd35c877778d48c12359cbc091d38d57b03f33186dbdc697e998bdac2e3a3f8769c89c1d9726ea4d0f22362f97188c3a9ee2a51ff396035efb6c835f26d7fa0fac807fc585b2ef9d6ff109cbb82dff0463c2b4e3074d6883154a9595b357cd1e7de8c838f4801f9127faa739ce69b908963d2f70c3342694245bfaed9f1caa2c6dfa6d019c79ad2940c3df2b27b4e607fcebd3acf4194a9d71ad183568492801b2889de5e09b51798bd65a7775d6856efee25873019b260107b3d39b9997cd64480e571e1b520c370c10b8492bd0867c7aa1737a9e9641be90fa8cfb2ca89047cfbac3434e3fe6e3a1aecbb169db371aae8c571e3e99e486e786f068a1b1d4bc74923b2a707c9ef6608c192402f6574ae2a73272a180e3582c12052093976544528030462ce8b3dc72e0bba5811c8335c04ee7b04de05adb97f01387d216806fb3ea5cf5c423579e1f1f4fac13711e40be973ef8224e4e3153243a4f916d2d734d9fd342e5cdcad29176c056738e8315341a8299faa4d778e53ac490a9525471e02de4c5694d557d41b79333c1a4ca639a3425e90e128b8107c56da267067c09057f8241d8766dd9899f46fbaa889d205a832a5765480ef0b63e05161cfba43a0aee718bb24a499d2592f68809b35f1abecfa309bb31311412cfc93589fb754dbc742dac8361e2f159fe9ae71967ce7396f885b9eb45799f951ffa12616ef894d2fcbc8636a2f0939efc086885474d3db221db8a3c8c7345fc6d6cd30a6f38169a6786f50ab0006fd1c91d84ce23c70d541f4a675a54debd08c485cd24f1b66661e9c5656c1dcf6671bc3ab429d1e801"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"723201d9bb2f39137b8434b11b37c1ce41d1eff3fe918b20f77c16a918033033","proof":"fad7cd2046f6723aa66e447737eddbac9f08d283af50c5937ecd0b6531c2c7351452853ebb15969d6cd42098b7201b10f74204fa3743e23e0fa51660fc40f52cfae006892fbaa30205635774f1e3627b4291d5549a778c7d33c7d8d2cafd492c94191dc934a58aa0752e61d150cceca2989b60eeaade37d739f6e61c7edb19433dbf0490bd6310df4b5b129c0f6461b5bbfbe6e5fc2e3097436d0af20e4bea0d249df314af251249e3d2c80c82d3c617058484c2819cc8d369fa089f06ed1a083ec1dc441cc29762263211696f39c2c007c5f69b354c760cd00a16c514dc290bb2ecce662ccf80d1d9a37b1d4d45b13de027dc82427c46ce62c4080616495225b2ae8cc0f07f8430872a63c474600a61bb5ff20f0b19e4d3fbfa343fa4ca801c40bb25d690ec5d06816e8675cdd78b088b2ae834ed4f5162f11e1d4115b02c3f5a9620e80679f723c41f65864bb0f91947b21044ac2118077e5974774a991b6e28497d911456fc4006cd9e57a07badaea099c46aa184bec8a9d7d0e5e978f10074f89c71790d6114d23fad81db9a171e511caf6428f65539255def979ac1fc3176085d2e4a2e2874e4a7608c703dfd357800934c9ccb0bd6a5a194501706f4238c0298d10d3293728be3be7871921bf139e1c89c6ff7ec61abf3052819ffa975fafe46da46ee49d9bd33e4f06e79688d4fa7f36f4cbd9612ddcf35d287a4ba193e9920c6b28f2d4a0d8c2d3e707cef533c1ae451ccf8b3a29be71d903f11631c8c63f20cda03850d4d59a4576a70e039009ac7a4687b88a84e2a1cf4cab2060b3a4400429a15bba936de5eb8a7441c4a1635e63fbbdef1ef105179c9632c2a0a111d123a21a3b848c21e38b06fd3904c97453428f071416616d5bc14ce548b0a46c4218b2fa26319dd468ca4285d00ab4349aba658d84a7fc6d1f8e9026a440d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f64d818d5a9cf3e9cea77f464acb03efde763c207d2f8c3dd08d15acf84a7b25","proof":"92c48ca0d479dee2bc617982654363b82f64f94209f239467c8354d445d9525dd642b29c7547c50c54781b02d9326ff86b7e6f13a185a77b9226137a6ecb4663587ce90be16280309a8e543fbe8c5764fc80d55ac69a232c76b3a0e58cf6a7392a789668facf18268b91a70182767b89e8ef28a694461a36325cfb9729f16b7a35455ed05c1195e0f9c873f935456b003e61d3017cbf53d7f3c1244320b93d0ae88ed8ab4e5d6a1b184525c86c7130e4ab5a7ffe4f2c7285c87481f85aacfd09afe2014ddcaa1fdefa98dedb4149a68fa7cd5075c8afd4d92b2b25edea0d640468e31d91ce75bf5f252812b5177aef322b1c9e0a10815c325ba1c411eb0d622db8d4f6de2c017622879ece33131202f8053288dbc2a32b1d9583b24037b71d6f3e9872fcac0776b784fa96bf5272bf3c86e1030a53d344392100c82de8ad58046c7c08f7e0e1e9be9e25cebc04ee55433a31b77db719350fd46ecd2945bc4834caa106680a273aa43a74134b2bab9658f7bc5277cd31de9c14102659be24a8381c6c0b032a491b242a0a111fef103d148648cfa4c1fe1fb81ff94fab7fedcd3716751de4a76062270f8a4e69acc67b0868e99ebe2bf48d24b41eae4625aaac58e42fbf0d3c503445bab763746452d1d2275f086a649e6d7f18bd41887abef95dee9e55e47a98416d068899abbe5b2dcdff854965a62529aac9cfdacca22f526338bf5d15cd6d6e345330ed87dd5b44e6ac03d48874176c3a2aafdb3ab4097c7bb89e13f03675fe5ce579d3a3b0bc540c40b05dfc985a2b589eed929814fe922f88489ee74c2f8b05ccbd75b40565b34e2928d08191beba49e730b46cea6d36139a4ff0674e5e83678c9f1b32a984556d6dca6d7b939a5f54b40b1a6c289ef3085d32e814cee00d2f1242e6437db0cb6bf2312e4d42ae61224a5805d4ce56f609"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f224481eb0feb0c883d83551e3950d3f422a5db49dd7cab619bdd2ffef66d825","proof":"f2f4b4aee8bd6616f2ff12b77207a3c5d5167188783cc93bdc497cef3909ab257cb79c322fc8effc4c7160a6ed2d06112580e1e20a0c1ab26b85923582c0d7713206821198c3a35e114e981c1b7c675f7a352034dbd5f30fea67e6d7b29b016f56246b685502f908029127f9bc79d6e4094aa5711cae62206caa89f00dcacb763fab5afc8b751b7000ce330c07755f9a348df7386429fe87fd324019a1989005564cccba09505426a8fef0511936c868c8512eb766f76fb08007bfe88d7f340e1cd73554ae44aafdc620fc0d3626611d8cd924d6032d8669718c67861386180182f3ed5ff75a497897fcf50062b814179f1d70556d055d6c936c8ab40c23850fbae9f98fd4b76efdfad71b087eec8f455da1df7557965c031d0b2b9028b9dc1ef2e4d78f98cc221640e8c25b63f5e6e0a96e9f54f72daa2cb6c0721ff204464ea8cf66f506f8e2ad6d6583afde9b6df310ac611f3650e0bffb5fadee35d88618aecb28ec98df375436acad67dc4f4c6385dcb51bd16c8d9ad49ed968393c5703949d0f3f70575485a76b5b28d5ae40905603f0acb3363516eb9aeb71a06107479898f70e2f3cb5a3ce99f018e04f37dcb28cb6cbfb2cdc8f368af7b6763c96460cf47b77de141042db36a841b88c0dff5e35a294c91c2814d2548bf8c9ba693acef19d1bf79e74119e359a6b0152bfb8c8cb1565decb0d27cecc6c4b4b56d54eb6794f0c5ed8cb5463e1d07dce39c243ebc8a6eb5ee316a2657ee2102a31f07800b68c471cd2d4ab09f7f10f08859123efe214630f4d64393c33df0a23010a15aa928739b52beaecb2c369a2d7fc066854169dace0d9a0c4a684437aff5a0f1decfa65b89ebf854da68794648731da0350195feb3652095d9f984cde25e811036c4be15b80c537836cadb4f6be4cdcdc1ce5490fb53482f8b8854ddcda5fdc06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e631275de0f9b5536eec1834e4f496a8fce2e662c74f238f18a7e3120c246424","proof":"a65d8d2af1c4cb9fd37a8475fe5d176e08a8a694863f6ee323b805f7b25053001c944f9126b455aec9b31688098d9fe1468dd6f5206afa311c8c6c5ab1b1d80d0e87d51619eaa4c8760567a07d1121145510d668c5d9ccb78a0fd839c27edb1e6a77ce42d76da0d02ec7e84caa77bf98e6a1b3aa953839594f54cf4dfd6b4b0d8e3ce9910699ab7c726096de3c79428f241a26c621a7af60d6e8dbcfeba85e0a0eea7419f5deba0d323368f6bd0ff0df791870eb915a66eaf36255717db106001b82718400886fd37ca9fb3f16b5257dc682c4e8e050633682b7c500b9606a014cc411a37fe3334b6fa54c2f088ecaaebf21373163ab346932f688a2fd415209f65c7b781c763d9092dbcfb10f7edc77f89544c89fc51252147c56ddf9012667acb539b9806389b7381c9fa8f5fc8ed2c4ff69c90c53a0e7677a57e39d97aa22e61ad06e5728c5f547c719c37f40df502a86f83d79b718746b5f5a17238abe4f988e1c77045d6951c587c99ca3a3feb06c28dc5e60e3e60a954bf2a703942264c802f92170287af6077690040710ac544fb9ba1b62630b2111d9c2e521fd8419a6bdf429541c48f0fc480e76ef72b35352aa079670e84a9029c68619d35965581eeab429f232f2078d1fc17a23159b6d1a192915c399da10b763edb0b59c6b04b68a05ab5ec5ea91fe71184cc19353cce48a3d75d335f766c9b4f7bdfab05f217023f6f188a3554ba9b355b11315fe32302ef12664f3a7995e15fe90aad67d2190eae17d46352bc29744c593717d9a03366e9cd627db0df4f911fc45e2a5242c6a94ff6c392b222bd08af7d3dfcd5bebad212d96ca4e2356c65e6fcd21be1a262a7d2717a8f3e1a8d158aa5b10a87cd930c8851869020b0f46037cb3085df205def2eef3c9e7c9424fa47ae34786cdcf25bf9ef6113b96951581259ced704a0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0a2fe15064a166b6638a312b3d07ceb12a01465e25bf3230d1c787fe2300af71","proof":"e0d992dd6884f2d99866e428324970329535c7b3ad0ec1656a1f9e10079bf825b01ed4e7c7f0688233c85c4f529f2d7203bdba95fe708d0281a7bdc4bdaec243ccdac066713f947741a01efe9dfcb1f9219c206b0bc6d2ab8ed8d0a262d7b6479870d2e4d756a85e2ac6bd7269b754b656354fd2a3e15d51334d72237333ab5f9b060f5a91d96b1a4871838be9dec95eb984701e78025c5cf853b962163e060d1c6ef364683f70b675ac586d2d7600e1c7bd648751db64ee9827609418ae240cd9b1fd62f21059a8689adc5fa4892f300b1e2f0b204428011ea07e5504f1ee0c249ba9199ab320da59c872e2459d5485436b909992bc0876cf5abe62db8fc872fe7af156279a4e0593b56057dfcb6ee8213e793b18fc170d988f576b826bf77654245da0d083af3d1cadd9d32223164aa7c97a0a4143dc3ec71d30079a722942142286625c0a8a97bac8e037d0641d186e60ce369eeab9e26c5d843ae9a70e04dc8bce4c7c28176b43df6b327dd86b197eef556bde34d1d37e2872630abb535dd64b3f5d05522cb077de12c8e1478654a4efe2af8d6ce7102d2a488251a8ff0f905ae1c3c594280e6880e7379486e78dcfc17946d90fd21c86cc8eede4e93c7b7253d455bcb1825602c180f8ec9a4170dab963524d0923ebe29b23f5cbf2b95c2a133e3ff3c20145fe0997e21e4afbf314d5cad4ea72c9a12540c75f86c1a163c08f630043f6284ce8c047c50d7e39cf9f1757274b4ecf3c8e338e76421d1a3db63cb1b2fe6098d084859e55fedecfe992ca0d756c40d2b39fd08d164d61e70362c90c56dc51450ca9b2c75a27f7ecd2e8cdf4358f4223a606a98b1e335080531a01e9f20f41eb0c12f6559e4b5b6c4513465cea8cf3825a1b4009ca3f9b9f0f6bbe18ca0d2ce3a4031df0f8183a472102e81e781e8ce110f43acc298638a406"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3e268a7a68def5d11c741d760e3e8b358e8d299b6831d74b8f73e9051470eb17","proof":"846fed96e7eea11d1ee698257d09daa6fb353cfdd0bdec33b305284fabe7342614576b86878b9d5f6b5ff96e81394ae2aa6e6e01effd9c2f175a802f8f817d094288b4d592baf5ec5aca4cae93b55edf2829a6e7ca5b3b49b9cdbb0b1be4642ab480f7db4585fd75a4c17ee3f2bf463b8385f955a39972cde8249e1d28a7f606a7cd44b144f41a5849aa2ae243e2fff3835c035c46328330dc136699c9d78009fd724874286e1b844cb071c25aba96657b14641b559f06b810574ec0e28a0206bbcdb4f4a220a2721306bbedc0b22bcf5b1cc5f8551c3f76f88722b78119c20cbcf31185b4d4708aeb72ad5447b1511125ae4c7595524a06672804f7b0dadb699432887c2439bc5d68b85708fded9d5a37256788a4fd3d48400f60d6e7150919a266ea85a6408c143d6c45e458272024f764325cb7291b3e477b575546faee4e74bf2ffe76ef912ea52d89177148f8638a161e8ad1d85c1d5e2c78e294285f4a7860fa3d3b42f6e9a4c89f2ce86357bee303c7ed4349d55ff48359ab99f6c24d647fec84997fc6c61ab6f04e8af75cc452c1ce2fca1b9870ee8649309b295e193eb9a9a76faad79abf7073d95eaefb042d60539f0e702a6b9a60e4547677cf455298e953e027355098ca709ce26edfc1b33f74d15c4afab872735c099a1e3271dc95b17bb500efc7b14cf90c800b46a3a67514deadd5782ce22904c5467b926b72c7f30bc5e8cfb17ac5e89adb899b601d68eb887ec6b4c25aeb059859e08105a2b09b0090b272252c894ec696a3ce9d3d3370fde37e3366520d5658c36ce54940d314b61e1bb62e6264c3eafe021317fbf69c8b747ba7612a44113336bad3560a70ef965af852cf525d03f9123bd785bbcfba550e18382f5d177a2292971f07dda389887547130c344dfc54fdaa8ea0d3c52748ed95c74ad4cbe3e9a66d4b03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0223bed85e862f03f16e3fc0b8913433bef90147d23e579428cb816d99dc7544","proof":"fc8e6666507e92ca4c419607d74db34ee06c22a30c4ef57aa92e1e8977331570d8fe36d6019726bedffb5b65141a28cfb0a8411170da673ea00c3824a4564628fc5fc8826b10a98345a4bd18d67b749797b20b9c0a7ad3f609a4e270c6446e01b4c8e9edef87fec6fe33559ba1a6345da40500c37bc6677b5286e74c06fec259cb9ce09479f4e76134e280e88164e44bdd07afff0fdf8631c612dc0ac329c4055890757e4e27f0aa481193aa9e9f7aedbe6fc87bfcd3cdc4d61b8cc10edd6d0a20901192c7a4863860b789dd709c1c9b123ecad83f13d8a19b3717e70e2ee00c4a84dc2077329135f7bba4f07864d4f44cbcc8ea6f619708339cd339133a8568b669b6dc190687af1118f86c3194770c197e93f343664919ab28a9c20c44db6096ff9e2a2e2721e1fc28283b1e4f7ced208b56024a2b52a8b8b3de2915dcfb7c0621c400f6ad528df581db1cb3e85b9268753d02292f5095b0496d640c49c32120fac38eb0470be44cefb2535175e8cb58304ff885f0ba63f7c863ab2f0f2815fa5febcb81e2bfff66058beb81cb731fcb29fef98f3e2c3b6fe87f35c94fb971aedbc6ad1e6ddcc49d18974b7e98db4499d2647ba31670fbf075addaf5aa755d06edb822695fdc5ecc4febb3a6c287f769e4458d4ac8e7f9ca2db3b58fd87863b632ab7df0e3e726178bda5222c61170464c03a6923e34e1f824c72a2ca8af3304180a443be8dbf7398f53645265f63e31b2939024b8ffc2530b9869912320282e7e5dab8ac98a1874cdd0e55ab3d6b9aa1677a8809b32d9bb3ec040b5ce436a5c1a1be1ccf0cd53dc9485d8ed2cdb84a5036feb92c62035557ac35fd2cedc09ef5b47d96867f4efc4029a849d8ad29289d70385e8c703fbf91904ba2e661b0871e7db1bb71193f5e34510497cef8c66ae7af51786bb78650356594c0fb73305"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3493c3c357502697a3f09e4c482d4aa1a5beef992b1529a72f564a83b9403916","proof":"eac9bd2e7c463ad5c11cc2463ad33abb056e916deeb773fbf3153b63356ea33eba6aa1ea2d2e6018721b45db8a9e156ccd211afb8972b8a7324d5ddf14f695335a9c7c447084589a893959c16e15eb346a60f67157eb9f5ac9842f8842dd9e6d1607a9d0825618fba29e691bc3eba7c1d1033e2afa8477430e242329c394a449c377a9bfcd0e92832b4b4f4e3470b3b38732dd69a337240e7d94eac32f390706574dcf99539c329d9bcf29722cea5b86ae4784a9a6cfe8917843bbbcd687c90410f22e95a3a496f0496d6319e0056ac76ff6cc17819dc3d254b70d117c791b0b5613f0b7e70567730ad2643083e2c9c19015f34918ff8aa30fb4c6acd0860277e298d30cf6f89694549c56bfb1ba2e48bcba2ff3d9e878cdc812577c55f37e79f086b7f2439cd06add3c49ed4ade921c32d6c5dadb8325e9b480df1498b39e48ce73c6d51506d781cb703e9a78d3b801d3dde8e47b45aa6ab06ff777f5fbc72ed6347526674ca11687931d3254e449e4ab5d3e14c563b12138e14c3bfcdbab1480f168d866c46c4d78ec731555d6c19432253bded43c43c5b22d4026fa9ccd45906969374682a480d7e57098f76162b3c928aa3a61c4f5d9c137f221e0c9c441ec2c75b5def2487f7137775760e1f968b88ad6161d4bc43eb17a8f2f0bb8fd22d01d9821895d56e4efde7f2a70a5ac438c842d996c71dbb95e737699852a22418affeac0ffac1f3ada2b09d772896afef343501d69e32cbfc47ddb9902fdc15d8ae4753e5995079c5805e57ca2e779f05d01a358c0234e575bd157bffa056e79da75ae9a8d2f88170c9fe6a772cd85b162f9535a6cf37f4d4ff99748e60e394130080f4207fe294bf0857f098cfed1f308e2f4ea107bb6b786c71260fa5fa105408bc1b07ad2fbf3e2468e5dbaf2067794d5456f20ef21e5f79f49e672e0690f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"527536bab280aa1fecdf88f2e686864b2bf361ff09231d4af6c4cd081b5d8a06","proof":"3053ebf534493f6189bdb56b95c28af027e64bab0b8f0a794b55e5aa72c65838885482d34da85838fc8b7b10cdcb607656040b6417f68cbfb30f870fa0b0b05ba8ce1ed2602496ff4386c6a2bdbc51b5d7c94ee77a58e288d10eb1b9d39337150c20c305252d2807e42fa7fd8538718646107aa8d63913aebc01b7e1edd0d048ef1061a0185225d1a156a36b7e764933d57a4707a8cb1ec9732c324d5bed5208036f4f266229fdcec0a35427daab7aef45dee29c949735dabb2ecf9dd23f160fc6847d1eb36044ca5a19279ca3edb8892ecb3ca3a7988fbe500bbee1f4e10901e027eccb31b7fe85480eaaa4d0ee731a4083c3fdb00879cfdeb5843d70fac512ea1433d80cef249a307340b040aa60db71e3b0e5592a060fdb55a442555ae72fdec88b3d6ef7af780e9c45297ebfa170531140230951a164eea4b80393196c4ef4fa62341b63d1d32dc2ab16ee537941c8cc84d86412371ce007cfa05e08891f8841d842c06efd3cd859e0ff0b5ea53d944afcd4f4ba0605332158d11a83dc7214dc3fdd19b9511d9ffd5b92e0512540da982c91b768cbda484056ecc2d5724a5c202a1f0bddd0229c41175b9d4851a818615b6e65206dac4c0c86cd3ba97221085a9a4d1eb2c928f461833556fefdcd07a062b1d45b2ea4c5d769c79304802f50e75208eff8136b15673e2ee6cf7dd6a43d7fc434e4a543be0af994fff7d12ffc19fa56452ff3091607db2e28ae31a11fe087bbf03015e5c14eb98702b81c30fec7d4d0caaf2147f733a6da9e4b673276ffad765490e8c13374b3eb80dc0c6b00b425a8a0ece3fb5d5a5435b1a88e8b34c026f451bfd8baf6a657fcce750c4e9569c345ba61e28d33ef22a8ad78bb9c2ad12ddab34fbea2b2e9c3933e807504ea32c31910e276a70bdacfe38ffda4c4772dfd1847daaa45dcc4510cbd0c3101"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"42cbf062daf48d81f6fc18f257095979ad01a6b0ed6f62d05ebbd9762739e245","proof":"7cd2763cd88d7c626068cc3793cc6035b6a89f24f82351a504c69cbb6f4d027ad0d979bf56288547328ffe82105fcc4715d5ac089268befb1f822ef3bc0a944ccc04b6e88be5fab6cace771eb3de927d87f0dd36c96f45cba5a4e0520877ee4cf4b4753ea4e320049ded14475b6f3f3e63b6f34ad024fc2112322c7b505e79710faf44956378a2ba35d25626924b098938d6b7db09b5dff5f803c3e2ce561108dbc9bec32274a12a5945d46d4be027b8e56e24d4f08bb2394fa9d43971750d00d425746f161f26664df31e7fbc8be0ff0f92a1f16e4b833a1d7ff894962dba07c8292914ad8866fe3f13893b27b7130d3ff7d7f280dc7f81bb10fbc7358c5c70feebe0a1d97c96eb04e3a250960a39c53e7864b044b610c41217439605be5e7c9c9d3713474c8576ed04a932274835f999575cad6a0c059b61b054b1a6f6ae328e5c2000c3f8f8a45de0e16c8e6bbb2e51251f39daeef62fd68231bbe1350a29804381b0b08cc2b7a6f057ee6f774916d2c36ee584ec3ebb1a570c5ce4c87c45024ce3024f8a75d17e1faa228b2fe85c0d87027422fe89d97c56938e368d1d61a065e2eb5c751cbde825340e35ef4a31adf3c3037ac8e16fd38a252aa80de02ace79315d47ce1bceb74492ad8ff72fbded4e987b92564d5906687a1c7cf591266899512e7a3ebb7ca93aa34ab98c239b82dd1a40baa08a508ba974b8e5a6e851a02e58f8185bc6eb649ad79e16e3401e0552843d0e15c72f0f33f4905cdc5c0ffe02f27bc7494c406f2e98df026b61776d45a6165c2443a94d88963bbf32b4523cdda4138833bf30a4a446b8e0c5c4bae12ffd9e84d0b1c724dfdbfbbfb52b333851bca271e16e9be974cb739d45dd2bb446da11b735bff80d0cbdeb917a8405f58d43af3936b9fe7b3cadcb2b9fda4c27ea551c233bf5e244120ae3adb7fc03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3ebb1ca82d13b0b53dd1e2adee132afbb4c701339dcb02b4784b7d4ab9e44912","proof":"62713e8d76e89979175ad7474fadc5c6b03bbfdbeefb89285b83f31629f16b3c92b599d27ac5e0c861d577a6f3cf052a990e5db27f2597cbfcf339db6d545a46940f47bc0f88b935ae860f6be1a82668b309baac17e9ba71b1e3e6e650eaba11b0c9c776549d7472a22659fd128816a3217ef58c32151d85528c45ec85b8f53052f1f7e006f91dd858b57beb02f46421048c200c18ccbda569f64a1665ecb10cd922ca84ac63156de1f33b0bc1663fe2bd32a41a2357a59598241844c5a9d90e2b9e9b36b1d51e55f3a973e3da3ded5fb59129d23c482b8b4c90a265c006560a98898eb569ea3d62659f33aaeb2701bf0f9a12326557a20e49539a428514d61214341cec43530dd10bb32ec30c3d6b03d89e8814052d90296464d92575c26243e67c9536bca5ef1118a9309fac9bb29fe44e70d09e86e9d672fbbd63a8fc9b2ea6c1e0cb0eda9bf7aba888eb6aef894ce536e1d4cdea7c127382da1c1314cd303c484e5fb14cb4cf1c35f9e7d71190c0035053fe0722b826ccbff6df2e067a39acd1181a17a166fe9938ad6db6f7e9692d7ac372e8c0ecf2e3c99c19bc767a10b07c51833f08a905f7123209b0f66aca6125b45794da3d0aff8165f31465f931c2498bdb3dc12418877e411341e3d2d8230a166c065c750662c1eda7ac952373c05cc59673244962758f5222f0a1931f829bd0300115cb6e217a82fae2de6a4b30d06a75426f3209b7709dd7ddadb3dfadc1cb4c6b73c1fd6cade012976d6237fadb793a3d5ea8d8b01915b04532e89e8c161d74c4fa3b4eee9cb0b63224200b3c19ffd5ce655f7460f185d4827eabb5fcf208ea859d00f79e2279993e240b3460e5ed053cb44121755d77fd7bb5026db181f3e86dcca9c7f88b516899f5ee041f3160fe0b2ccdb97ca11cb49f6412b97a9995c9f993af2733c611fb4469ba04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aa5e056af5967af07e9821bf25b64b5601813a7d776558a11e8f8e781a8e3456","proof":"52597aa7cd27db17f558ab05f043b4fab44bbf813fc1d82fbd91ba3ac371bc5390c1de19432989285f4afd5a84675f00220d0633c01ef5b4191a916f662bf36008063f11c7814d783d86c323af0ac3b87dc665d6914ad889c9c254f3f7fd9b681894a6ced61cc3877b49767d725a08b9b6e094dc2f05d9fbb0f424499b76b66d3c3790442b9c65c88075eaed281b061574cd8cae6052df4651f7b3b432268506d0e2d972fbaeadbcc1ee1499e49c4940b7b5c9dc5ed345c82a8ab3d29a4a4001a4e94ce34ca75cd59f9273a048c2b91f4288c09fe7d1ab922c8868d98795ee0cf4fb5df889f995c69bff911573035794926466e9c30a3985447b11b8c376fe20242a2ac7333a08f24a0591ec125760db1ccef4bbc3456d7b2c9ef4249e43f858306607572ea19eb51866d7abafc521410d0db463c3269d6e2bf23fc26ab4581a529058cbdd017b954b3344946bbe59cf8e0f96604c714592ae5262c918788a4662c95fcc9f47cddbe0355686747fd27984880fb688ab0f355f00e15412832e5528dce9d064c95b728e1d655df8956a9be2d0147b4b198dedb9889da1eda15a493ae8375cf20256868b027cce5c4962bef07f8dbeaecf29ddc2273cedeb5a57409685b220bbb7f0b4ebae8cbe0b6a8f2574c95216d389e6007b7d20e384329d2f2888e5ec4885f65dcf846ed0e5b55e15fecdae66c3494e228f136608d8a05242de71d32b893194e6aa00c9cb32de1216fb88ac97e7bc25eeebd480fc14fb2e1dccbc405ee13717e16031daee78450926a498f455abb428572e2164496b97e02bdae209a1d3cbee526d0939ebf52f11ba4752295a022570d6351f54baff55c661f0f5b19f3a3b4ba9d7b94e3949163d34b1fdadec5f25d8831768ff4ad709da0d8fd0e3f9d4643652f18bcc2b9f4fbeaa3375ad4a7049c33eba70ad67bc06c50f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dcc6cc4ea3a7bd39a255a7b035f936f4b488c44e0e5e7614e923d37eb720b52b","proof":"8ee188e35f5ea46c2668a26a28fd13bcf4a5f745e6ec48824e4f390bdf42fd194a29c46dd5ba4ae5e427a3c75ffc2f0bb335e5b42134d6a204b4cef2db564c7aae36ca03658c6cf13fea12d324d3c2929695d8f5585cba68db651267224b3924e61203dd251b3ade7fb11d1f14ca59848db06e225a6f3c1a0fd02a032dba456ab79e846275f3ac6f41c5aa7fd8648f54d78915d8f755a663a131ddee430f780800a821184e8c23d6b18d39d28e75d0f89d5824b1810c7837e6fdbcbc8338070a1e9819767bacc33833ee439eb41c4b056758ab22e315a27d4dde70c372e61202dcb907faa5eb5b3df10f37d854959c13ca5d032291747eb6962d9c0e41a6fc53b2bfcd10e90d6390af297e109c25ac5ca7e2f0ff8aa6b41a0503d8fff16c7e5e5e0f485770dbf027e149b077cf5ba5a7ab219026800de200fc7538312f2c89444cbd05b44922ca46ade936dac179a7fbdf830e56fb32fc28e4c0d8609b6f107bd2dfd2694c8634b5bdf61cb739376dffe6b8175127417d5a82cb5fa5ee247a2848e8b84b36ba61b0497ef58c8378c67516c478781aa3ee3e9b0656b6f8a3486ace2495c0abd4ea4cecd4f515250e063b8050933233bf5a4c6348a2d38aafaa3246c5a8f401f9db8010c12cba5190ceacdbdc6c1fe1441f4052b7a1c7f24e0a44aca317067a292850a61fe6c3ad7c3bdb5001b5b0235508b545baaef4aace885a32646e06a738d46ae11aa059a844c0ae578e735274282c16225cb94929ab844788513ec210341b022be920b7c9ff67b21629a19f4b3e0e15410453c972dba50e003695fa2781dfa31b77e4067de55d6224952194ded8bd9211560abec96d5f36c0e3ca1c7716655654e49d7687145211f47daf74342010fbc75db9c9de363a0fb7c2139c30a26c29d842d7d7a8fc7359e5ee822ddda35ae7de7cda98851f6c08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"80c6816f9bcb1d010fcbe5e463cb41bc6d00753acc94e5aa7032a001d92a5b30","proof":"28a42495db12079996faacdca40ed860fd20bdaefa9cd8b46bf98ffdb15a983a82a4382d34d8a8712d7ccbd831502d2c50d7609afc8eef4223215059f2bda729cefb7c7c46135dacb2d0772656e018240543ee67672ff1eabe17b26b8202617f1a1e2e672cf0d6e3db7d54325a643750d824cc94da3b99f56799f0e34153eb6c30c256fba9639b607196122dca9790f91159f0415984a1987e686d12f0793404028eb31bb1f1dc36543289c641f9dc7b7b56ac252ffec7f9ff60466672f8d601ed407e1e354f6e834876204df9748723f3472870f48ef86ac78815530da0ea0fea1c4172cf5d2a9cd1b340e382c8d892684989d57fe7ada1ef47072eec48e038ccb2eb0b347e169e748f891a81f42c5c464bd33beb84a0e301cb4bab91c86874ca31494f188d2ec8977595eb067410aa32433e0b1f9bafc437bd0d0de1f7574ec4053f419d269be7e67eb823140ae98b1877c39a40b81cfb75177f3ddbf8cc18ec73a4102b439788e72e615983c30ddb42c18f6887c19a72461730c1ac7431098eec29cb1aedf54912a8dfc540f1c0a19aa04a7a5e9dcec5b428c20de6fb607a0cce88c04be75888ac494abceabcf2efe052b09094a7cb145b4c286fda021c3c4062c80812d99eeb68627cbdfed1d2e746e9b4b38d392e3616eac4c796a9fd407e0c08f2eedfaaccdc2a1a21df306b86e462b3acff9c4741857e72b40a4fda37f4e9bcd0020655d8406d79c07702af0bff9e71142321b156cddb39d11bd1ea57b0d354892b1d05b2bf260edeb26126250b735080782de0ee4dd62fb42e173e0d4a1188673a0224c8e0093e36bad70dd16a8d3997760fe151cb1adfc70f78d279ea81225d274afa4e0126ce29c1244512315c1e1a59292ea6780facfa8e45e800565f35ccf619644f7e165f0fe64c980ac363989cec58860a22d7f1afb2968306"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8013c98391b15e44b6a6bb8d686037548469b22e258352961911702d2673653d","proof":"2c3f0fc9a6c1f06867e53151faee68f01a127281ea003b28af1ac0546dacc51facbef1dfbf30d1b5e08da3660e8631360717b6217c1d37eae3591520cf08c3011823a79b44c736e3089cd96c9d3c1962f86d9c0aba53cbef30d5d11cacba4c540c75bd3d4537ff457d43dc8fd96a9d20d41863e31959fbe343ae49484ca7ae2881d2effa420b3bc1c56a732b6fdc644eec6023f98139a6b2b6da56a34e7c080cbd2769aec6ad63662c92ee218fad481db30fb4434703f90e0ae449e00d538c0d38ee82d2ef824df51c6ebfa1dce3036e04dbac96fe3e4fb34d2a10cc29ced1034e914a30771b811e0307465a17b1e8218536ebcfc542ac62354a11375b3ece4c2297cd63d019455044d1baa0264513f63283452a54716985dc26e8d6728fa40bc4b49126b981dec3d6c46999d01d3d0fbc9e6b291a26aff6b53bc77f38987c5608dfbe918546b84973897f2cd930fd7c262f9b928296793c6af672bdd163c66ca6cd8e055d106989efb362458116a32270133672aa6b55917d8eb95c784ca81dd0d3a63db8e20b47cf053793d6c6a249adb572d49beaa3b366e713c4a6936d6a984a3cc2864aa6c705fab0c854b2cdf4ad5259103d355eabf5f1a787cf9bc90ece43e282e8daddee1e25cb7087f4cc1f99de147cb35aa84dac5b4447870d0009f47ada0987672cc75ff560c6f42f15f7a2b2d718490b7adb542a5306d7db2670b8ca0ff133f436c959d73dcbce7e7f1a286c769b198d8891418bb01c6b68054ace47bd0ec1acf37bbb42e77f6a20753fe3598d92bf07d90870a646dcbe687257e249e148ee55d6968741696faabd6e2abc0b985808be5b63e77f7364931e7d17d543cea6d2b7e571b66384ef263354adc7aa049f87778ff57c4ecba5fd3c3e0e65ab3ce5bbb5f983207a2b459af68601a64a8292a50f9e87ef7bc9afd951a90f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5e9b405b876a0ec86c89ab89a0295de61b2dcdbf958d0d99552ce4cd17e47110","proof":"0a3fee2be5f5dd65abce6403a099b0ecf179f76bc2f37d626801e01e73f5687096e71a6ead91d2b523f6457018f62786356c2d3a16488d5703c5a9911cbf517368399136e6c7f1a7764d9b0aa277af94dabca40014dea90b45efdcf09abe4975b48edbdb116435c1913ac8ba9d9405000a51476e642809a7c6410571730fb53fbe2274be22ab7203ed40176467541c2ac56f903a4469fce6985d2b5c2d09a402a4b188991f715abeaf850859247f6141d718f25c4c7f56f77b8c04d0f8b0940314edb56ebf9f7df73bc6c4ae99490c980a2a506b91a0398c8c706ba5502ef60f464a0f6ef11acc13a05bb00e8fd3380bc3a3b1cdd2c5883b4d76678abee93c03ea7f69b8e4443db23bdae5e05a99d778e203333d2932c08ea0b0d7e1e3f5cf2cb8b360ee7c7d91c64df597a49c3b60a48c21b71c4d31dc0579e87959f98643250040eae20f8984aa930b12a759f19278b5a94584edc5f6ceebba4564924f1462c4a3372c4e451e8dfe42d416cefded7eae81c3ef2fd4741ed06fcc5d6398d85d46c740cb5be57eb29eef97ebffd23eed62fb0091a53aced19f0016c9cd73dd0aec53e48728213b3559ec9a1d45ee18ce95798e11d849a9aed9b17ec250a10c1f488082678f658c8faad8971cbda3b6f3d01b3fa125ef839ecd6881f27e322263cc30479ef0785f8a5bdde11334804a147b5a350fb90b6616e4201dace57ce26ff2f44dab1312740ce5929a0af4f59f9cb2645942a4bc480d7625ab322d4d0b04a6987b4b5e1b528da89756fae87a169b6a3334967d419e8723a082d40fa04c495aceed74e10af10bb250c9b97e5fd1db44a950b7a2188e88fd470e70e5c885515955d6a57fb6736e95172be8d7cabd352071bc53cfe7980898fef218979d7d003796b3a6f85c472f7e56c48a4edad82b8c6e14a62e5f984c03008a7201983e04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"24970a6a5e810a7d6e31502b989fe94c24c0c03c0206257b95db747f3cfe8261","proof":"3e4677fb7c955d81087ced62a4cc998c3b3d2bb499cdf1b103e70608d671c7570004a0c5471eec86f0a1d7911e02683eb04b4eb14e8326615d2150388a733413ece2891981057c60fa4f221014b2f39ae9a239822d66dc79e3ca00769c3f0b30f8f3ef21dc9c4ec6e06edd104d1dfe1ad38004fad4ab8ff4136520100ce5b64e3957b856ee458c6d0cb23bed208207327c4f983810609a961f1288a45df5d10f914795ad198d90acf08147193ec5da26ecc55a591e36abb72f7f0f858ce8ed09fcc0fabfd0b2ca018ad51c743e6c2fe7de7514142a3d91b3f854d45a1d48bb029a8e2824ab0b209136727ed90524d791370082c7430de02c68f5d468ec29a81684bb1abb92554bc5c204b422577cf8a3a888a449c00eb708da45ef8f179e8a45ca91988beb1cd46a90593ba911d4d8f26542a5ad6a46fdc4c81648dfeec6ce40688fbd40efe880fe985add7b670fa0035eaf2fa3af62e4fd0d060ad12306f9297a8baf033d4b2ba5fdb5cdd2ff826656987c4555931db1e49d6c1e88186e3832b8105ab7e4f8e5a1fc306ab954824ef6e1b7a0970f6b6d3de9083d02f39cee637638f5a6d368b73232dbce1419d89a54526a673f09046e85c56586426983fc6ccc6758b3b8ecfee414d0844869958845bdbfe508e12cb796348e035307f5f862166dd4873eaa83b03c95433e8ed8a9ebe0d881ec82eec20263bbbcf0791e5276ee67ddeb429b7ab71d27170cd41dc7794f0602b93fbd8d7e2bad26d3e0bc1c0f4e9f9b9bf2a2293f4373997a396e76cd0a2f55445c6357110aa73de2bdfdea0556a948ebc0f42c8ddd45f3a5c96fbf92160b23b8f1324c18e8c5ddbe89077059d92df3263b0bb4c3b67c0d48568a5f8fe9ac11a6f170e903ff814f408d84c103f1903c05c7b4d1dea744c0a29cf36124302762c25a08f5255934437837f1aa09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dc139fbba2e935bbd99bb46cdbe3d93743e4acb387cbd36108c4a79b05fd0d31","proof":"7a1c079b66c5a54033b042c798139d0a41a3c9b301e751a65caf8a26f4f258319e3008f92c901530e002d7b235c39b9e9b457eff00636ab9df9a82229b8fb472b21c0fc85430de520b42df2d2fd5f79ace32ceee83aa39c24154be66b0eaaa021ae593676ac8e4bd8408a4ef30e5a0cce28648cd7ca69c8554b426e226e4a022ae408d2bdb230949798ebcc72c7e0108f265c6dde1625b1433a53a7910d3680af261df8afc5b4892fb87833b79a61610c0371c32cd491dab1736610f2561ff0e9284b2551d9cd7f518ce95ef22539cfed65512197dd609e585a907648d663307e0c1ac3a8e02193e45293fd975e1c7e3571133536c8947053c4b33592c62bd0a6c829639c8d005cf1baf572272ac39b76ef46cf652682d26d7db6d1a9d0b463a8664525dfeb93924707dfc11d6cfc465f733ff6eaee38e73641009298a96d308388cd825e7cb3c52a346d4ce93a76ec501ec25c05dedf907c66d1baaa7e6e3516e09c2015403efef09b02894967088c0c477592a1d4caabcb933247bdb9db05a0c87736e542521c51ca7c09b96fb98ca634b0e4427e389f68c9a6eeaa97da475d01ab3e344b442568bf0db44e1d601fb1e7a25dd4e4d363e214f4e9b40f1501780a078dcaabcddbe4fd918bc36fb737e5c653295b9e52a7975cf61b3b441a115bc60871c61fd7b22bfbc6f302a9a84d7b1a50cb09b95fdbb67f5c2c0c613f00434a1ea40c9821091c7bb5acd9960ec134b5f8c8fefeec02206a05bdb8f5c821cdc8f67b183f114690e55854ed9b999acb5db4d3373902b19db01b114eab3f36dde2b099f24ec868e7e5db89c4b42958dc20d4021f3190e6278af5dd99bf53b0535f2c5ff8e78aba3a04aedc0f095c93e16397313a000db5cfe5f7ee31d7d1402fb23506644a61adaf48c9e8ecd63e98f8dcc64383d4891199e45110cab9e1007"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e4b987e7fe63a99d7999a56aaf32c5b96edb34e9a7ada44e833746c9752f6f1d","proof":"52ed924d00da94e3d17ecc73abe644c3410e330dd5566cffa102a803889dab1ddef32adc4e2f050e3671b69adaadcf5ad1f1e22d098b9733a90cbc564238fc1c9417dfd358e30bb5490e3e798def0f8501af3d2792d1e3479c466144940268739664ad714846fef2123a8d0f44587010182c1e763b3fd2d70da1246e8fc5a44541d2afc2dec8d54018913e5355b11a10962d897936eb942bc94408a659987502cab2ac87265326336918cc25263c206da84ea042a55cd848e0a7c604246926036854e4d5e86e7be7e9c0342326f9142165bfcdb9e06016d739a0a6774c32f2054828a1cb40ab10bc97a269300d6828c330ceef2bffb10c19ab20cdb81a64d0550ad1c55260cdac118536ba87907099fe408bffa005554a594fde851c084b0508b85cb54a00dd52dee67073835e02067425ed41a3ac9b83bf7ce5089c6770d34a6e0f69e5a7e5ccd7c2de26ee771f29a2b0316ade01e5773ed881b40edba2ff6ca2d08bff5be37700b5f61017e83204a1585d4ad7d3d1f3a2512ed14cb918e305ca82b329235f932dda88e4ec4d5590c41cf8ebf65c9a9fb28d6a52c2cf6bc32706db039b0736f8512f25f0e9dc43b749ad7dad6ca3f5d38fb9f714f9a6bd9a22820d7060c07ede93ee7b724651cf2839b4bbf1e0519b7bb38294510f1399c04cb6a8b377d0bf7dacd1cae0f48f000ca212e9cae9dc2b5fa5763552f8f3d6d91e3ce747f993d80696ef0d0092e91a2597369fd7b11b943b98257acc11b1f5d403ce24f1b89b8e9a63182f676e2f7453a42688521c08a9f9af478fec068674fb4aaaf183568cdc76728a2f5659c2eaf50c05d0d301110862453e4dc57db675d96f496dbb744b6b802800e3b67492b6c8ef55e565b20d053cbf6f520fc49311c10a97167467ba93c6b539b6a6b8033971600c8c8503ffdb4d4a50f62515f72bf805"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5adf2fadcea591c6030c6063b158fd3227ebdc037a9aa35ad496338fc5a3531a","proof":"a824ae4325fd7678ceeccd903d9074bfe4119ab28a3906d1d41049c609f2a27a446fe3aefe65ab9500f38e658a63eeda25f837665ca7e0d826c953c1f84f34579e964073ca49850be996adcca412a7e41c3c231a94dbf5e1b0d1070cf5ea4a459623c3d9633275450a4826b64c98315e9f09378edce3d32ffac8b7db6705631c863c9007c62bae81d12c5c008c090e802bbec68ebd558addc4fe5d6b28b84002231f1cdc6d9154f7db1fecd40599f0afe944bfaef9277eafabeec99e149e280d637b74db8b4650e1590f7139e791c3459ca8b45a04fdcf6e464eded2998d320824532bb17a14d8d86d36e86ee098356098062c47d26e9b568f13509690925201c6d3bb7278f3c94b662268e714d440a4ec0d9085ee806ec2b64b9089c2dbee717c3e6cd5d133201744e362124959f86703a3742a9aed0b258778a46445ccdf28f8722af03e5fb0d8c1882485d7cc89685a586c56dbf7fe43a6798186d88b73324662b27a7597a6a146d290b25d4d628b4b4a2db2371083eff904a04bbae8332a4000649887fe686a8fe883c2624df7086aaab5cfc496d134f19f39528b81f64f003c9ae5a5c4769a09225dd446f103d9f7935844acaf4ff2ff1f3d7754b988776017ee32a00efbeb8d1b8b34fb38edc4a15eef7421889ab354ca100353b0c770b8d509371d0367947a5f0a2c29cae3163bf7b58875f66c5ed293aeddd883e540c4ef80bd492a5070f4f518c657dfcb5942236e64294f67bf097ec354be44285e56454aaf95ecca74a12339ab9e9dfe9f0a55a60fda52df6467569d92a8049218feaa4c10dce47f7f0c3eb83771578a599adb6934e486c0bca158c42755a66f6cdd185c6e1c57ba25f59d0f6c234e8d5229df45138af1c2a1e9423961d30b660bd3f3c1b8168cfa160de4013acaf6fc9e10ff66d1fd3d470c1e97a5282890210d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7e5eb815ad5b41b24e1fbdc99607ddfa525a4610f0f609745a616d3fd2307b56","proof":"6a244b607a638d947116be45857643527d907b116e3c44957bfc290485bffc204eac63929c3bbec0f65bcaa09d33be4ab57391fb305dd03c6d8fb7822c1ae37146ff279984cbef995a771a2a787f74aceae99b9c353c21c3433b125a9c2d734b68ac603d2783d9acb0c4865fb7c374788e03d2b9049900ecd98531591aac242e4e19341505acfe82d82b68ae633d4e69c3353cfdb3788d8fdb469162d90873028f04316e69b2fa5c753e0b48939730081a3123aa9768eab870476b605b2c920a4c1d8775b99d268eecf5f380f446226657ccd7317f93e1350f851f7467e6940294d5b94b487c29b912e45c00403056ee9fd0aa963239ca1cf20aaa6d8f4cc61ef81f63cdfe82c680b0ebb6d2358982275449077b15988b60ee8606882746fc31dc66d0892e0e8a405fb2e37e861cbae748c3d2c78cb33ecff7199f4ef8d7e40aaee949e1902e806c2a436b1566291da0ec0401130eb344437840d249704c703868521e95974acca28e13dfe1b04650e0913f043cbcc8d55ee439e47cdac4c4454a6cb204c6b2f656ac2ded271d6e3015ccfc71dbaeda0e542db782faa21d3235428ac1243d469e2ac5d25fc84c463edfd9d90f8b258aa748ca03bbf8e17af050da570343900c45a1a5ebc678f7218c21f787125ca73c1d7836d42e8daaa7a7356855ad9afd7d2868aa55b28150f2d798a2753b4a7506c7fd4ba1a08ead62d2537ce982fcec54b8fec1eea3aa066b10621a7964ea0915a6ce580edb01353ac979b2bbe617b51a4beb5a11e12cb6788f0e5059300ebdf80754bc6f107d36feea04cae2b462c09a6cd4ba1bdef14e7de3bd969e034e5453cdfc154dc73c3a34f3194e6fa8bb81444770e5e2d9a3e6619ab2e64c35ecc1a7ae5e05c8a256de0a5f0c719481dc3fa86d31db86a361e3e011c44904cd9db93fcc3967500143906e650c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"baadc6e2adffcaff018afd91a7a60c69a8f78555a2d746a03e3f1d06d81ce441","proof":"d40ab1badd6f13d3f979664dda042debf6fcb041abb46662f4cb9b469541c622e63f67c9deef823f5f6991bdf6918e6b5a45b299a59e3e612165b5cd1bde6d352afb2719edd16ee51d1def575cfb75bda565cb63cb5f108c4fb139ea4caf5b077ecf935d1e8f39a7f8e030d828f5ec11a96fc313248207d727cff3b37b6cfb286614057d8a26d4f812f1a8a7fe2878d3e89b5e445a46c522d106c91748b71e05110de2ad6f61cdf4e833cd37d994c7ab847d6d5affec3f62bcc3ab32a98bfb06141d5673a8a1bc31da26258b6c6aba00f128c4ba729e3de8fc6623f3c9038503d6dd394169ff8b697ede636e44dca56cafe69edf928757bd4b5db5102c69f60e44f35f5ab49cf8a8f1821afe9b4c1adabc604bccb893d0157cce425be56dff2ddc83bf0c8a32aebfbe80bc95dc24606f939250e0de8ed2740351ab2294d9ef555caaa821d47211ded564b2e7eee30a4f0fa347e63e416fa232453fb8dd3ad6576a2974a69496186c09142f286165630f515fe896dc6394c13afff2d17177a254fcbd00a284422d42646084bb34ad48836b59352136ae6122bc80b0275a7afb5c0e99bf9cdbd72a651bc846e6573d448320c246f721e6a84e4c48f7aeb16ac8050e51272d76ec431c5fa39a128510869d8ad94160539e9f34666fb38aa438d079d212dd24f1576f8de54bc4e036a8a163170caa2a9ab20552c72a653ed4d46a35426a99d1ca7ee017208c8076da39bb686f3c9ff46e0feb07881a17fa4b1f7858369839081d2a4ba81034ad78aa2cb513e81dc5cd4c7fc6c4fb2043d8c6e8112af63e69ece070384b9bd6c9c20082797dbb89450f5f2a521e8c203ea438942d7476e7a46c3807ea32c3c71bfcee77d4325627ecffb13bfb0f332a16035427ad0a9d07e86ffa74c93d83e3e8a0bac03ba32c64ede550909bb6cc8edd3ecec4ee08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4276510f32ec8d9c8145aba65a569790c25831642313dc0d72dc76c1e835df69","proof":"f840fc9ecdbcd88eb15ac79079be150fafe79784af408fef8c24dd96135c5a75545991d29f0882c51935dee158e22061784dbe53104abd899a3ee6e6396bca05ac3673cda77e3450ae090fea1d1c9fdb47df957c867262641132c9e7a125725050aa312761abc7d9e70bc7aa8350eb07a15103b38fb3eccabdac4649d7c1cb3e1c333b5962fc0d0bb0713ce631bdd2b40c1b982b8f635f20901e993d644e4e03f3fa1db6a4c39a569bf404d1635092553ad16de966473a8c3509153b7a2af60e05aef7dc6be8178f992fe0626470eb53fea47f87aa4801664c77c3e8f6ba760f4802a735afc1fe25dc49251dd5653cc37b1d4f27f836b11f6af315a515d0a4266823e82fd93a3fd57f3785db936aa71762a09c0f82ea590d42142565c647d0595e9b2877d91f2ff60dc8aa0eee81dd0b6d472b4bdc38151f6ab0e4e7db4385765c16f2795e3db247111d702fbc04d2400fe08ad8d3d4f60ba0bf41e752dad1699a38b6cd33ba41765ff90976282118752ed64d5d64cb1bc14931296fc53cbe1a527cc09f68f557daf87cfe61055ce92a468a5208432677836fc27c0fec6569644cc9cd9e374765ac904f06abf15e97809d08ab163ea96878c44be2ea950ab47e52873156e1a74dbd13f75aac5baf84c15f506be97d09595f8ae15cc8c8d1a155c08b271c95a1ac4c6d5e876d3f0ca215496183fd09105844412cfc91cedcdc22fa47d35cb3dc6eafa24e0513ee4b5e25a81895a7c3930c0c40116464a5413d7ef2943a6dd148070ae01bb1f18b7e1c082343eff28152316a1bd34d537baa4642d20b49591613f04dd7a3c0f841e6d5748c015c6b36b3b36cf61388050acdc2144bd3086a1eb0d09bdd27bc3a80ca34bd8b326a7eb6fbd26b531214ff48ee5f037c1eeae20b712cbc4ea49fa45e08e3ff99c6341e2374ed963b1838e168eb1208"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5297d0a32fda763c9d9da5c51b52adead8cb251191045cbbf706f58890ff1a00","proof":"d6e4cc2f4132aac876af945dd36026822dfbdaa1767ab45fe4e5d95e2e292179ec40c912e200383de7e116a9a484c1173a237999ef49d7766fd164051fa7331522e7f43535f00f47f5ea5076d14bee5ed3a5726cbda0b5745a5b3ebd24c6e84b948afb164fe6d3e5f15a87599981ec6ca6717b548520aba97e237a645839ae6daacd29eef2ec2aa13926d0dfff4a49b035a0624d4542fafb0fd30872e00f2e0d378ae6cf70d5e3a0e13b144216ea52fc1fc853639522d0ed830ac23791316004db79764196f001e788bc05295f249096bcdf7b71ac5c4240dd00990602c44701d0bced8c4b0e135fd98502509b88a583e15d939287d11f6aec6bccb8e8ee4d1fc66c93dfe4762b03b3939bd5e5b071ae21e43e55799b79b6d6f09a41cdf3c00f14d05c824ab5b4237d3bbb86679270eeebd900eb48db32bfec0923495afb9032ac0b33972402b5cc52155cbce17f272775c65b9be362d502bdf236dcac9b9b08a4c1179ae900f82ef57e6dfc30f8f6492ffb6516f186a45a8937cd6d903d23381890ca0005ee1c2e67fb4617955baf683adac376bd10634a9922f37c73169118ae5bf35a0eab8fa83c3cf37f28bda4d3a2423e34dc4157cfb75ac57b849bed32347c3309fefa732ed2ae08e4dd8ad28084d332dd6e5ef245d75e45d17dce6d35183531da87c84324b088a91c8282545e7710031219f32d2229db4d849ca39c27282ab8ae0172ee0ab3a61305281d8fd007414fe71a5c018c19f8b7b058a3577dc650af6c0de30b22619a385cba0cbc6acb1c25b9e329acbfad1d4a12aa8edd4cac280970b26859e445be0d3bbe701998f6443700ce16200334d147c34a6a8254591a3e159eafd551a36f758c769ea59c45db7bdd7876adfa93df93fcd412810df25d6e5abe11f05b95cf2897c6cd4b87f4265ffb9c0ec6f50a6279156ad1ac03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a67f087743ae6750af27251d910c7bd4e543b23cab52200116519d27240f5c6e","proof":"4ac75f1f05beea8cff0a819c359ec4b303f0d7584b376922e7d8d355baad5043040e7d8adedd7da4651bb734c980e59ac0cb2a0181b7560137bacd7a68e0504eb68ffde89a027e81995c09016dc1d0fb215a451152bf6bbf3f637d0de128c924c2ffbaa1f36b20dd056fc6a67f278557ae5fbc2c3469f935802a87bc3e816f0017b22b25cb1ed1236c4352ce8fe15f9da1c14ff049d1ee7c2e1dc20d172099088b39e776ec6299dcbfdcd13a11c0e1317cc195cb603698ac612af794058bbf0807c03612dfda2ecbda7376462f7d3a11bc4c5b4b0351bff04fc659662393730fc0c38ac06d4a404f0ee67c1772c83ea6dc259d20367e41ce01fdd8d35542361f7c57e2889714d0cd0fee1768ba66c15bd92f2385ad77ae9890c86f71c40a0d2c946b18de41f2cdd01c143f31d6ab99e77903bc768e2ed1298ad1a3a3af03d1334c7861edd7a533c00a2e64033266dc0a65af17ca2d09d6da347da1afb1c092362a49d88943c8361797ee0c3aafd657eeb3311c156c6f1110ea39afebccdd9c21664811c4d6f4349f90a05b61ca827053cba1e3ebbf358d0ce756a49bc53192611e8ac56a53035a03999214df52d6b50deb79e77572a02c4e703f4da2ca647f18dcc27569626c3e2a06f43eb1d061ab5f3ff675c3c039e3f53c73d83e0268694620ecd6611f8d18fe47ed049828154df7035d1c270f3010370cd7afbd2af917399cf858415192c1d6c9d2614b2375faa70916b8ae29186dd7a987b42441afdf6f7087ee2aed2666f6fd0b3bccadc4379107067ca9fef3e912f89ac7abe68ca437f8eb778e7493b322e5d0ae9b89069fb31344be160ace428fbf209388edf1343a629ad5f867c159b65cd4d9e58c0a99b57c2fce8f59260ab43149d08d9c6ffe0e22d83a491d195a8af51ff4c88ebf026dbf06703d65c6e4adbe4527d7a9b26f05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ac521dff13cb3aa11c7436870cad3d5ba966b4c4a5cb379b358912d67bfa113a","proof":"96fef15893852500c9f221f8ad01777be0fd6871ccceca2d9702b2af3e091b1fae3235c8646f10221d627ab8cf7cfa2ceda6a00519afcb4a6438402f8bf06727841cf1b51ea5ede93c1294950f1eb857212b58c95ae1e21c6c2c3711bd081e0e48b8b40474ce2f1b49a46d17e39a532f3b12376c9ff82d171077ce29e6f0652e82e0ed577649562bddf4f990917769b8298c1e8bbe7cec3314285a9c834bfc063207a1c292aafc2c4088b053e3085b4f6bf781b136eba7ecaaeba712517fad01a6072edce6a1dac7d8c1196c163bde5f6f7b141c9823ffce34bb7fcf79083f031c6f8139e641cd657c8756f8f71c0a160df953b15826474ed1f07342c122561c9a02e9895c2fb2a67008e5b5cdc6bb9afb7ae25c237a35af5f41b4ad0b39655f7c6fbc3d2ed4d4d05b69992484ba126fbcb684d067c4fd75dd18efa38cc871606c6c645a69b5167f974ce88e44842d0fb1058c88244cd316ddbdfc8fd499be264c53440af9edd08d3dd3fd1a9fb9b497118ca2bfaf7e8ee283c6fa634cb5824b849bd799a743125051d9ba4f7180f961437a1f058ecac5a810c7677ca58b034f3aa1c760d389e34b88b592420b932caf3d72838996c0a527ff4821bc983f1c0aa4d03f34c1c95a3f20b13f0db77405df067c4dbf2c82c1cbcbc7ae8bbbaa197364932ba16e24a4d61ac9d03f953161cb4646945c48ec43a351120f9346480165e8d9789b4538fbba9efc8f75eee399e2ed15d1fb62d3ef284e315979981c193bb47c83d2f51f1a5af47631786ad99abcf3d799076666186a2a72e63c5bc6824be00115b2de8e33594613daab9802a4e1fcfa1eac4335baf681a0966cd7eee337c3968a0069f37c388a5b34dc4b66c07ff563cbbc8753e57595c9184840282102f2aff8ff33c5bf662a28c9a2612acadfacc4a5f610208365c9c1ee96f997a10c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"94cbfffe103ac8235828d8c263327efd372fd7142ea4c947bb03a4218c115b63","proof":"34de03f52d131e69cddfe8e41fe22c535232e96971a9f2a8fce97c3631942026ba15c3d4b4aca8c7cad3dcb9988f6c28339ed365beacf1d9733caa2b5dfa3358d4037b1c445f92e7dfdb29f6e26f1f8a63b84c5388a96c7a1f68ea1c2b06000132874c252ff14b25d75026e9b95db5c5f6dd1dafef8ffed1b92e49b3a2d7cc3cbf5f0b673ada64f76b3a07b9ece47ea96a9a642a2215820bfce8c9a2aec7fc0dc2ea228c6744b886cca71e6e5ee035bd08d9d191c637c09b61b5d7a63b672008a5b19be0cd6c9ff422672ae8ee12abf6e4451daf89204a6b3d815b4564ad9b045297aab957f4e1166dd0fd91538ac37cae915725e1b901ed7535a4529c41f725f86e69723af58fb11267819a76f01bf681904b43c15218c23bdcf1a5bc1aee3ce0ac77a572ddcd6040759393d5cd3c9afa59e126ac2127ad4b82047542a1e1617a494454239bcd9f402fd56945f34da8b60776fc1031e156e71f217dc1df6d65f8dfd973744260528392fa6d71fd73c92ca8c4119c884872008470b3401baa5148ae03828fea004f59e8e773c9c068dc4350b98f8bea421f1e2bfc9cfbb1b657043f6ac0bfc505535ca94de91c67cc10025552b4d32a915ee0b48abbc48f40051aba363f17b306d2cd98b55ef304744fd2ab6906ad666b3b7508f0989641fe6678c3865dca9d252ba7662e192c2972ba4c8d7395a1802d40a6771e5bea4507756c60bb916b105d49ff61926b5fbc28a4cf8da9e9702ffaa34dc350cfa3a6d57fb872b78b15ebc8422124ccf872fa15b44c6630e01be290076dea526dbff8f514ec1059ddefbcd7d9f09594e966206f6925f3bc71a41bae95151d497d84b7280a3a7f648a0b0b06cfa4d0ab158d5ec6bd2b74c3e10bfd9160528306754108d1066c11c287848f87c87b446aa37b82660e9767c5ac59b9d9b8ad6a6a02c2469f07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1e8c5809151381b2f1e841a4757725cd0a10a372db63455ca077124474090161","proof":"381f365ba11d1cf7e6c1a8b73cedec1a9089f6668c93ec7a12cb79d21bc499736c4b356c1499e59062df91f0c85fb6eab40cf40fdb982af2053ddfc0c3eef707f4d78d9cd7ef9f4e86f0f45b946f72aa17320f0c3792da1bf6f4de6ae251b8605cde736b67993c3f0c62ea3b44258bed5bb2171c0699ca2d591bb2e737213867b04e8f6673194d801075abca13a017c2ab2643c0e6ee28a24fd5bc4e33c85b074aee6ddd3def1e8cd9bdc914ce86d1d04a000fc1a0a32e55c07dbce465ec4e07c60d259453f16d706c83718810dbda5b671c4a0f47f82a732fc4fe94abdb7d0ddc5545fcb7a855b035f779bc270e5d2e751e87cd9ef4a7845e0b6c5f3d48a9424ac85a26174a57a3f35640c4e00c13865c720fce9b6ff9f9435ccffbc4af33090c1f60c0619de04db1239ef032c60e1a907483537d36cc0130cae25bb41cf172822999c51ad3b17de2a67a973865a31efb9cc6e6baf46a008cb12e720545f25ad29202c963cd1c137480dc77eebf60e17c2a96f130443baa8cf6340cf7513b6b1e1ac2070b67b6bac801155b1d258471830be2700c1cbb64a89780a655896c1c9a1c2ba657db89ed996b074557c325a4fc7d9d9e3eb2e638316e189422dfe652be8dda7dff80f0efe4b312e3421927856e5e710a839cef35ed793ab84ea2332aea5efa4279dc44c22350161b534389866ceeee73345160b0959e6c4f94f7f500f21f4f28b70663b4910a40cc1da49bbc6b93460d4bb746468ae708b5d541b02666f43f124759dfd07e8205550b3938885b52669207cd9466f0b2a07a1bcb5a6afa45a8dcc0385579fa1de3c398f481c03f67e1681f551057ca294ea5f919d757182016186aebb0bd21c00184971f9ef04bd75801b4f84a56109ba62e8e37360839fd2834a614ae57cdc7fd14a0815c7edd293c39bc6a03f8a3778ff7b906fc05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f89264b65925329e918f12e23490b840045855c3f0cf25c449643deadda7af66","proof":"1efd6f797d2b32bbc2d421e181de5d6465b51879c2e1cbfef8ad5f909a0df408a6356a564164f876171920a7e9110a515e33b5ad72cda271a4050a3860830d7ae63a02d9db380960269a3520cecd64fc4af3937d00eb1971fa4fec8a24684c727042d38b2305ed7e563289a9576167f93be6de124f0e6344cd518379d12780113c293ffed8981a938a5c39dff01df544b9958db7123480660e80fe7a685355080bf756fa3c0d43ea23ac882302dd3d47c8515ce2879324912fb3ee8b0e4bab09cf3dc01b222dc3caefc26cdeee1ed44abb4f16031b1eb35b6551a958ed4a8c0832550fdd473ff287d4c264989a12f6effebcac6204db633991b1dc7c8f49bb37c65ed4d451ad8a145a33a5c571c9e9e5f0ae4e425a744bf0cd3945c18e7466732eadb89b146e27d6bfec01881e1b7d82a3eafa1b0bbf1fc4d6e37b2b2e8f615578d3af9715ee37c0abc957ab539c2e26336dd1c150fda9c321c97fc580da88405a67fe6530ad562e83305a70af2662517e8074b9ca99912e7cd46f2e14ec4b62822ed3210e5e1f4f7d2b3ea2608d9c0609711ab2657eea7dca7442b74f301b004205e31c8522e052abca1cf2c7010eb8f5208bf1d72ec08eb33d9b0c4eb47a5a96a00efed86c8d15f53f7fb5b48c266169d4820d6833cb313d0d43613d9572665823282671304248e0f306c61f48d4cfd41be7a4015383cf61fdd28fd5abde6cdcdbfd65a911e689b3349393a8acc0b4f90c779485ab33ed306d5ec0c8ca550b6c78d5a8a96c162a1e89ece25ea379f76876d1087a92e12d24dfa5e66d79d960a6966cd2000f8b9e0f62b726ff1790978c8d648ceb49c86e080b055d1675824e6cd039b367ee9a7080425d13211428bd425b6f1ab5354c84ff919925635f6807b061af5ac9e165c68128f5d61d914dcdd91aba99bc4509f667f94c37a51c2805"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ec253363e57782d8260f7bcf2d654dd46d27f0d1beb7b43d539128fafe02000b","proof":"d61954d78c6ad6ac49c7ae0e7bf6823c17205cbb114da8e8ea663ab4078fa50efeb7851a4c2a9175c1d3ab7c818b29f683a566031d674bec7c293f0dd16f6b73c4d21162899ec5260449df9d92a56414146cb2588d7ecb820d68c80f9795ec6c382c5a100ab2831a9a9cb6a0c290fb2c38f073813c419cd8070c32ac44371d28c3ecb861ca015a3717083264fd9bbff414a6427a58329075a31084ee94e1d50a195882bea39aa57c5f0d90b600a49dbc96ecc9e6ef2a9cf1f761489ac623680e6bb444b95781b97c5603b542dd0ab3940bae1a7b4c4374a7bf229ae2475a6b0b9239294b409e1212fe8acb18f2d071164d059f737123b88c4864986c6a547c28ce3dd9497d6c308d37b8e02beb0fe9538a3e9daa0b8ed747311592bbc56b00009a02f0bd650b7c0704cdb4d50f59721ba824f0a945c95cee8d4a3674cd3ca3371a26e457c171428c54c43ffb717132675a66d4281e96653745f92231543e2467e6feb6f159d4893b0d580d4b415b90b4f6d5ffe8832922714a3e273b3793a634ecefdd84bf3f3e172a13e32b39b5df67fd1ad96cfa58a426ae551cc8acd80449c84851029793482e658128b2a54d9dfde6db5f7adc69e3430e5093c81032ed17ae2ab18dcd5f4801a14d4d138f985ab630aaea12ed5839a8e778a217e5b07b3ddc1fd55354dd9d63076101e6056e442d3fd0d9020119a8c6ba05d162573b3350d0c901046798eed422737d496935baf839ef8360812360f563530474df894b3eb05fa61e206478c3a3fc3a940332dccde340d709367d992f44e3c000f5a9030e98fa64da664b4edd18b1dab9e55fccc1c0d6ea4a21abf3b90d4d1193475b140993570c916ecd72447855e90595d63c94222615f78464835cba75afaf0f1bee050acd7e15a3a94e8619cab0c429d97b543489094165e3daebbe9b51a33318d502"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9a5d10802b7c69882df98213ffe582e6bbcdb8a0d7178c40757769a715dd0162","proof":"b4de05b3e9dea38186d210b4d8f4c65041791fce0f8d75fede348ff48252f04a3c619532e059c701e46e3307f79f05c246f470f38bc300364e346b9935706443488438a1cbe3858eac57d1aa42ee6c393e08429d6045d56129db571e1e2664563657470dc2c6521369cd11382d1727bb966362a6813f11f5ef4dde04e2481e4cdba264f507d9818d9d45a71f454d96fa7d134027c444cc4653166c3413c37104e05ae6824bdfe28abbdbfbc71a2acc3bfd5935f46e85e2e5aa4b999125f6ab07b15d3e651623e6647ac62e18c774e149496d8d522044452b25efd2f19e6fca07b22de4a4e04b1722fb276b52e27aa42fa377d513d38949ac00544a9db5122f4550b0b7b728a1ee5a659fb43d03f370c5075b02581018486f7c0d05487ecd0c647c45c7b0c708a3fe391f8bcf74eb9b5ea11062d7c3bc100e78c982604da4d26960904ae5ecd5197142d69129756385b16babcb233933b4175f11baf344e3fc35fe480131b31bb57e28859d473817dd8f64c80074240dfd555cdaad068805de7542065a8eafea68d2733bff76357b62a99b4d2a866aae1f45ac303574fb8b264c32025c654d972b28e5b085b2cc8c78ae69a175752b26d9bab0cdd63e7695f35d64cce095b35401883e4a135f497f1d960432ed11ba71c23f89f0144d980e663a00bbe86001e4f18670d80f7e84a47fd19983c4a634684dc62c692400ebbd815ff02aaa79d9f6974cf244ae0876f90a8639d9038fea16406e237c6e1eddc73d5d9674f5c42828759919fd66a3947605397dab99014f905c0483e824ed09da073fc85f5a11314a62fe43c64615ac7af901167c7f17168e3119ff31d56129e1ca56b43ab013dcd728c3e812ee7ded0a54263cb16cfc45236329a737687e9dbdeb0c33f23c8963c8331795b20362320769d60d6b533c42d557fac35a8148a8a9cb0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e0702115a689b48a0c26be584619ffd5c3951af09648d47fd9fc0e7bb5d75c27","proof":"4837f352604dd0b3919bb969d0ab55e9748b39da3a6abfec545bcf9ced5b397230626ef92745785c2c3fb2ff1887963d3ecbd3ffe89c6ad7711c4e9fba10575c7649a51a4dddbe2b70286be1badcf08782eb705f083f64d8508e18326d321e68246301a0841171a0e824a7f21a59212373a129aaa1458289e094de948a9b5f2d039f77d8f0c363a22ae7336b9c95b0aef54ca4b00b16d6b2f3f7865d3b3b360a058632ce91e0fcf12d3e2b4e8d8b52cf5f60c31607b20f51ae0e96b20e1e1e0c4e911025339623cfa58200bc25769c7e1a7e6dcf2a7ba0bebcc948f5eaa0fc07901cd98941fd2dc22e348adbd5c20c8535fc463395ce30499232531639e0897574260aa46e58e54c83a187e4846af1713818640f4aeb086a9febf37ed7983c6924d56e22cce9443ff6cbf316a5e2e871182185abd9ef4a5c04ea51a1e98b1c03bcb00e8dde2bc109a6305f1f68c0865671740d58682de12777dc7edb771d78470854363a917454a7af4f67615105d0e492fd14c2b9e46c901433c12332a0e312903dbd2ba6dd40125181be16d7d3576fec3b2ae01ba1b429ab78a46b9989ee4516e2ce8de60d859c4e28425fce37374e5054a5e9125805efe58849d51b715218f8e56a160db3c2bb088b5724bfdf12fd94dc1db7f664d1944f51ee5305f5305d1e32ba1c0d81f1ac8440bae555367edb4a96bb0570173e22b3c6b9cc70b87d6dcca7de15511e71b9e8d3cc48c9b4d26bb48956c9bdc5788323e68b1fd212ac68748d012f935b8001c56b3cffd3cc654e80371fc49d460c4d623f7f5d70faf134ecafa30f4d0f9435919bf4b367f31b3c5580caf80ee6dc469ff2a604b8df29354086b74e76936881996456166b74c2efe99adfa35c4797a605a3203e4df07e099f8c4a61df853979375ba65c4233931219376aaf72cfbc23ba62fc1edb43fa0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cedc5ad71ef02003ebe7ea41b47e803fabdd5eb91d5a5730e21fb5a6aa7c8b01","proof":"6cf0928e5d8b2a1c8a16f38fa215c909f12ccbd237a2c9dae30d68e5c9b3c7132aba464d73ccfad677d842dc364a1abf2451f21f58c404849551815f935bdd44aef4a75aaa1b456d3884defafca77f108838943dc2e7824751bab3832d69d00b9086e14ecc252db300a1bc4b05f68229faed9b8d4e2f0eabefec956b7124ca63c26592c8c60d05fecacd0397403c4fd6842a57718dd741d31524a8e9aa76cb0193aa08c942cbc0cb0e6c6b649826482e1ed25d4392f8c641614abedd1977e30f0f30e19c98822764c557437c538984e6fc52751971a1ecac8809b96c8a260a02a0a241ea12983465ecf093809f5f260fe5292c1e03bf2d1d6e831778a65a234dac3cf804e8112a909165974ff9f087f4ca0b8506b16996f6fde8e7ebb8124c28e6d72d6563232dd2e2e1f9fc2786a7aa5fed2fbf57b6e29f9b8407582d154e276c521ea129a0559d278abf397fa5f4d6107cacd7fdc4a7720f580efc8143071dcca5ee6aef0107901d25852362becd2dc47f05e66e00d04c99b12896c21b7e15f6f319e153fd4b42a4fb4391317ce8d58b64a8aff79c3dee2f175c3ce6acf849c0c63689682301e7be2ddec81f0a568a5814c7349983824f94ec863825080a6088ff800b8636aa74824fe738c84620cd2cee38d0bd85a6e482c66653cf499d1dd815bff4659339475d0620cbc3b510d165d3b06fd25508bd7a865d5ba7b9f1786cfa848a0d046e2666b80fda1dd0bedc017a10521dcb5fb926e751536cbd8d5652ed234f7c5936d9457ab36cf5f41337e2dca4d29045ddd60931676fe61b924edad0ffe32e07f77b5791cd99b2b7f45b6a2ce4ec1f89550477d04f2525782e75c498c28bc9323fd339668880b202c27e3d67cf5083eda5b1a3435730db7e8c0bacd0c234d05f9f05c2ba7d6d9cc35df3601e40f187c260b3cfa4baf4d3962903"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ae066326677ab97d79293ed2f5849fae6abe873c1c37fe1c46d92bc88196b819","proof":"ac184fb509b13a71ca718764faef5cead08b3c4580900a95fa13a16003c16a603c6b92524299c8ded619a425f9a0b3dd4cf224a1400b1791578c511114bcca5f8c2f97ff1245ca22bfceb304a9f6747e89ac2f171082ae60c793af551952ae31f4cf851954ed88ecfe985ff3ff1e443cfdf47aeb730f41d5f85f5a4b5966d71dad0703b8c480db453c3e829f27da9a9fb7aeb14d0d760446c6c9a7fbd673c10e597018c7e6a842bc4ae06fd2735e37937f5d29b464e2c08ee3dfdc999ec6f603d505f75bc96f8acaa2d0fecb8d8bb5ef1cdde497ef5bf02fcb2dd6fde396b80d1e2e74e7f9178a11c3d06d771d8ab24c9c6f97586938f489c094e2adb56c1f07046216f14fc2b0a160d6bc77f40dea7e712f77746a296ba45812675d4e034f39fe0d070b24bc50afd9bf5ec2f2cfa60f00f47278741d21a7ff94869801000c4ee89f2b588eb60d47152fc2d8ba67ca27a403dd0e68b5dbff617b2108e1970415324ffddb2cd0349ce125e6ea9d7b3dcc5752d60d2ec6595f4372ec4de92d6522bef83d5445c58c988185e4bbb32cccdaa24bdc4220e2029715bb64da22f6912f9081364d3f3a71e1d013ee119f1188b786fbcce7c167481eb66aed0c7cc8e63778d2efacf58f2ae35a17433520a7120860db8fe43cda93eed4943c5893c077589e8191852c4487cdc55b02a38f1764ba23a7e151a58c8d58883418322685ad4802174956ac4735d656fc976d42be58dd133e20b5d8dc5541aad948c36baca962eaa624017e5f20d1c3354e922ef85128631d05e543cff88ab88c99cade430b29165e772ab1a6847a3a864659eb36899f956d0c7989a80d8ee8430b16e41d6d1e2f2dd8450d876fdd97a90bc5f2abb117d3fc0b28fef547e62b4116324961350f5f3a5bf79a711a4ed168952a2940327b6a7d0e72e00f420f278d56b654150804"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7aea2362dc899d870ec87f61dd328c9484310c5827be5b093e0b0c7e63840f1a","proof":"0ad8a36d24f831b86ee11981ec9141f84e93146823405d6bef923d204cd7e5508a03a9c247c502eda1d64e68ab9e0704886830aaa8e5d963d64d5b5e9065956c180bba015a123578cdaa78c2197b73229ab1be41a91c117f0d3bdf83c2f25d2d52464025cc93a28e8792e01975a45a423021f98b8b46d119ad54ebf07c93bf6818b8793a65d6c3ba554121ea8cfb5536de331f4e7a40fe60f7e284e1547b020d8203c1e7e18a92a6ea8c0e531eb44a035cefe7ab19969b0d94d153f0b20e970bf4f6b890e8ddb07f44d48f1f2f90b2b9f06c98c42fde2052ff5030af40bb7a07f0ce7ab7dcedf4173d77c2907f5a676f341a871dfcd3a0dc48f7f3ef2ac10c5886550958ecbe4a3751408c7b02aad944e325d0df1ad5964d98d056cdd17635579466225907948199bf9cd8112d889d7661860b53790430782748d0481d23fe3fb41b2572ee445649780e080f8459e0b180b8fa8d74b20e699782d948fb8fa7252c778f59030a75c00f8631af2e5828d4a8cf255170230ba0d96ceec422025d4ee2ace3028d6db4945cf7cf4e1b2e7ba114eb471f4356c2949c478ea8688bfc0f2cde84e328b83eff07939e0bb6c1e44af08d84aa219cb514bbcab59457ea2943ecd343c3200ad87de10111057a0741d70974af5cd0989fd4ad919cf7fd1f43323e8323cddf94ed0e44e729625f5b041ff8377c1d39261dec47edbf7285bdf20cc2dd060446e9c44c6a81c4b15d7b3591ffe8e21fb788ad9649f16f5fbc4c453b0a5fa6cc05474e226793258a1ef89c68ca09db8b787197787ca0d2634c6a6e5a84d82b7f5900002b0e6577fd19c7500e27b523d65f89b1f02b690fa61a6937561b5c11e686c9ca90a8c232e0f9bc094ed0433dec99687e593aec02545151110f67e200653fb574a64493cb15bc7c765a89402b104ad07830f03351d647fa990d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2826fdf43636d4e75557322e13456cad739fc36fc5957f36b041001f75b3b628","proof":"6c58580db07bf22698ee068e58c0b90b5b3a03a163d9f5f12f47b042a500a353521b145d7be19ddb1add529eae34ff34826dfd8027132c8b8fe1108c1328a7443a580677fb9650503e31fc847a2a73a7c2c20b6f1c72898599bc2bb2bedcab1c4add448892c168eab34f827a9652ce778da7060bec76a5df30991314ac3e005db351492100b12f2dc1f8ba15aab61cb0fb5db6a8929f5ca8f9f2335e2ac5c70ec41ce65a544755418598bdaa5b4fbd2303b4f4430dfa162bf6668cd023b93109954227b42287833a4c6433b6a6a92f23c52fc315873f62b52aa404aedb549b02da163bd45e349d844f46b8817f956cdf851decce419a84badb7a41f1c1f6e74d3e460899ec724ee080d22e0d75518dc26ebfdbf1f940e09f31d9f93ec7eede399cb068e316fdb88f4afb151a2b8bb9709157244a41d9bf2d425f9a26c0177e209c97fd5489153af6610ea539fb7e17550abcc1fe58d389b412aeb22e764dfd34920ec6a6704cb6af47514a6b66541767cbae6f9a0db8f4fb7ef1ed259f0e4d6dd6ad5b82f5e913cefe77287443575aab374c9f26d036c12e9d36965ff7052e446ab7ec696e9d0fb89d527b68f17fe794cedfc54bd1afbf9a86039b2c92a66576b0ba5ddb0a6902a30ed77054e476c36af1544bf46c8c61e35e9c67a202cc435c928dcbae611991d3b941ef85be8df56817e48c870d90f11c7f932f1a8bd5c05e6a1283354319632f830c871684193fdd0905fcf705b80f6cddf6fc56ddaf8f3a1aa6ded24a5e75e58ace57f00f1dfb0ce2bafd3b7d7363f34dd0de21a9288c4ee03d1b66b7a84f25774dc2c8ff0432fdc3652f40ff650c38cdfc7dbee1b40e1d4a023d01a43b91c672d7c0f4a7f27feea1c1c9a70f34982716e6ee6b9990920a0bba5af0edea81a79073f7672a4d468dff916c61da34cf8c8770e47223c0f207"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6a5a6e18a04680ff16d91e77c756c7fecf3f83dba7c641960f3b368efb07504c","proof":"8cb0d7640b491b6aaff47562d038daeb535829ecd48bec62032620607d62543f7076ff81bc1440217afa997408a3fd1489ea8779750f2fd4c1c8a2ccb9e5a1468e0c2897ef5ba6fd6612203451a159ad5018b7ad37d6446f2f10520588388422880db7ef4addd48502727fe3268a380257e2d34391a7d4079234596cec4d4a77db0cccde5626da6a020c02bd7ac77e679d1d5d2fdb99033857c8b4fad8c1ec04023e865ffc12987b90d69df96a37c943dc2437cf815af210be7d0663d66cf10db9e0bdad6f54a47224c1e442cc9b6d56251ba0c0541874fd3c870c6df002330c3250b76aece5fb447ac294d71ef54b43a7d3a5f1c64d834604697963d0b36526b47c09cada2e7662911e63790148c9021952ef4e30c7c20f374db0ddcefc9856d837d7da96c68f56cc1c0ff4fa53bdc2cbe0696d139570df6a97f928f68a607dc0d32f320ef333e2a8f641396b2cb132dff83064d58fae18eb914c150da915740245ea68087f35c1262c88f6d08839554d5dcae4abc2ab6d6cd9d461529d170dc6510a1e704cd05a81eba9fef23d3dbfbae060b7130081266f2bc6fa616a010e0eb8e31ef929feb6f83deea2c37119308be237706000bfb413b250f936f30a6672706ddd7e34b51fb3447ee9ef1c6e1350608a33c9cd062d95772216370fba77ba938e7689b24beadad53316ba8f9ef9f382933900afc79a0c2d814afc2d5b48aeaf864104613eb136de0c91e55c42676350a74501a52ba6766f67c0c9a8766f38282b8d5f4334dbeeab8105197048e845ed9708baa6b89efe798746c2912143a655957e4b7d0c1db8c102fe128c4a481142b4b3188ba35a53189bc27e513100515431d06f2fa43de4125369aa501ee52d9452e9e42e1ddf6a069f2f8b13650639743d62758a12f9d2cfa33091697b1ebbef900ed7de7725d31b4181672a870d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3405fd22e325b099f039ddaf3e3421961ae83692122d4e829e089d51808db522","proof":"3212b6e2a4d9dce4ddb6894b24d270f795592f3c368334e5dafcb5b30b6bff410634b756658d89e118e8259fdfcf08eefcbfd10596b867e0120c922753b2e2174cfbf6c1daa735592e6942f7c63483a9916b16317d405d43bf9a27b324813b660652fff55f473333a640863c461c9586b95e3801b53ff2701c11dcf340fc471305dbd047667bb3ee8ff1d8af766a4fa9ca0854d69d3ca4a7b1b35701c15db80ff06a2b8924ec8d25803a1bc2c37bfbb97b4f82835c485dc4d3e47be11e3038077fc74287cec94e1c6948c9f083666f66c891b73c609a3b25e326537c5213fd059eb9128e3f24a2f5880c9a62f63feef288e00b7974a7ba24eb5b07870b58074d38530a531a36721f7cc7fc89387f313b1471b2807f6ee2f950c9d14df6592f4c5ce7af3c3ad315a263a54751c7acfbb8a52fa6db00e967a368a64396130d3255f082940030366183cd1ec541bf31dedf342e803a4deae7017e53bfd2f563f31f12fa29b7a066dfa0df145921cba78205c975c0addccf83d2581abcc840ad611cdcf8b2b6ef49328ca6c734c6feb5b91072e4d65db5ce0c3a56aafa1401aabe180223547ac41ee24c5eb0dcbee14da8256914fdee2f2bafea9f246b6fbeacad0f4692899c9688b482428e8ab301f57251cd08dbc5190b25cefa1df7c8969fc110f0f41765aa980655fd5953804f007be77db3b7b40f1fc15cdb7f89ed85d29a1796542355d90834613f7838b2cbd11dbf3362853971d237143e0eca775894353ce67452b8e9d09404d290097087db69f1cec1e43a9e09566b06fcab9bb177d32c68f423f13955b43272bdaae9e7c1438c87a950a77737d8d5fb84054727a9801c0f856f9feeb7027fee753a026b958693fde141ed40387b890e3c13477dedec0fd3e3afce07a0afe0a09bdd5f9cbf816a442ea6baf769f9bae178cd476ae04003"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9af6b9488ddd8d1196a418ccae9ee8dcb03265d956949b9a0a0ad9b9c76db227","proof":"1033a7ee89088e8efd23c3b845c85c617802d324507ca6a97b7e29d7bb0c0651c468bd35aa15c9c93e43567fb2f7254b72a6ab6c0ddc0328445753461db812570a549b8ad01e53ff2b5655d637195689028716be63d065a88da161be6abfa40fe47b83eca76b520452d5d131285f120c46fdd818b1c9d2dfa67dd5d10958b160b06aa0d30fab5e1a5e99cb583312d7b730b7531da4685bbc966be159b822050e765df0b0b667b0f0c180026cbbec63d812994632b6caf2e2016ec0843c48c40c0e8d279cf26862657a67b8c4203fbb6f0ccffa1d46c83ca1f03a827b9efe8f09def166da516b22ee8e7ba5518797ca37b0ceb50d771fa69d50e4a6ffe3cf3e54e07622017a68479e604ddddc6ff581d95a09776750b51bbc8e4c53d1e42a106276e0a46aa1eabb27d8b1f6d26fa8890e2b2ac407b9c4fd9960db0655fe5bb97644e70c1f65edceb3114d152a998035ca1a44e3a60d33c9949c865112160c8620540c66314ebd3531d38e4da72cf8db8b70e60433899bd36833b12bd209444c1e32a4b19afb9b2f823840297cb58f2f13d2a915e89d38e637487712e8f5e38d3b1a92c921d0ed6c23ecea3f1ed48f19cea8241c864d1e6ceaff6867da4b8bff34b204ee467d5497703728008326fff1bd9158b1d69bc46359ff97b2a2095a860d82ec64d21dfc7e4b19666f5ce6b541b9d91364cb0c96fba1b17941211f980423dcff62163c330d5de807ca602cbd764bcfbcc3cde9863829b6163cec8ad25923d6ed8c1e897cbd30c7e11a94b84327e190ec558c32f969b31992c83584657d3d8295b8ffe8b305cf18bb26160cb73e4d2ea7821d41d231d627a103595d20b85b92213784b63f138b048e7db46522c70e0a7b24a137cb2d5cb10522df9e84b1099f5166d8d5d47acefc2d634cd188e45ecefe8510bf9490093c080c6cdd5e7805"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"240171cc4b67c9f24e0e3c02e8ac5f6881bc05eb2ebfca4634e6718054365d3b","proof":"7272cbf68d19f723a9b5d15556aa59bc474c385a33bd84e1f465ecccfab43f47b84063c9a0ac6836e0894a6ff47987411e845362609f0c6a3b40214ee181b935389d06295a1ffd7ca77ab9297856a0a106b349b0cf381c7170eba46615be8e2a0416008b8ddb75afdc0f6733d26674eacb138dff1807aca0c05409ffc2e1872041555d7a1a5993c10794e87cd5e5a5d8f533b6a82f0e1efa51f33fb12ce74500d303b8d04b0022ba88134ed8bee77895b3d230d58b4014cdef414effe06fef0d34b852ab759ac7192105fd5f691aab91f351792e063498090316e5b22297a703da78cc028a92a7146fcf89d3991d15274db73b615fde001d09133f8f9d58c201e0e253d66544ebbb834874e3707f9bad3d5aabdb1022d9dc048326c6d43e9c78226d20c08b48c769c0753d858041dea3ae9487bf964e4563476452910e29b759a8bd9a2783201aec4c3d4c6689dd60037585bf5da3e953b1a74f46e2f9e3e101d215cd2e02661bf4566437c3b4d24c6eddec89a48b699caad8392c599186a35e5c120678c72d51238292230bda20e03df24919174f984c6109387872ce6da2131835857b36e8523850f847fc7892c6ba39c7f96ca117bbbc77c62564f217264ff63255117f8abca63839a917ad187f12fcf5a96f18d1fc661efeadb379ce8f39dc22f5d14bf3162a3953c394071fdb8ced62ea28e946059f39058df3bafa5f63bcfae0c866d2de4fdbc51e6d001a1facb682d34717585cddbf8f6f985e9e0654284f473c8542a15ff968e88ec7bf4a16e6a41483090cf503ae862aea80922d651cfc3508c31009ad2962e332a5f24b5905410d6495ef4014bacdc2de3670fc6e691170e5be9c11e52985f8bac9fa2e251a1dd666353d7234d47d7a3a8219d1056f69061b0c9c976953acbe2f334971cf4b32df0de5e6b129b5b71db4bc753b05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"820c31c300283db7ee4668993eb0bf0785d79e8a713e072832959313c284413b","proof":"7c92f1e7e101ea06b040913e0a02713e74d1216f0aca950c43c5a07e28fe6b0ebc00ad7a882283e721a75c4b8f9d4fc496351e2ab9d334025973da65e603c3394ae27ec907cacc8ae5b492c189927924cc9fd9ae5425902f9433e6ab6ee31f2b6c184960b4cfd4957315c1d88d95cbdc69d2c5df70380754d0f804c713dfc50d6c74e797e378d100d2f0838fcb8c1850b53869f58edd46fafc81916f66fa450b726d2916b004143228836fafb2ad30ba1765406199e3b13a5ddad317c73e430df042810d3f8232ce68c76bbd0f8d061b567188233a9c764b05fe8126f070740670fa4e750bd1bec61017ec7ae9d0ecf80a0321a57ec07d554371aa75a244d303129d139de534f846703101c842b67ee58fd31fe0ceea7be4b9ecabfa74be202a16177be5f7f73abb192a3697a7ba72022f7a1d7ab927e7fd26fe9f542c3ab804368964403961250d57a9fadf739cb15d3076fb917bc5a87a6611ba6d735ada3f16a3f400c794f23356ea14e556bb43602676360154ebe529b805566c05fd2d4898309c75a985f4ce1efd0fddaa2cbfcf7e4aeebc0704a27994aa0a68fd31ad0b681323739857aea6edb7f47bbfc2d9bdcdea3a793b0b5ba5f631691f6d7be77ec01e79dd331867f3f9a342c457563e157a02cb819ae51d2a1b0207dd5d605e0e14a1833d7ffed664718533e4c55264466f83e4cdd787c848be37a4c198c20075b037ad1cd6dbba252f40437ad5bf1aecc010126dbab0fbacf5fcddd66818db3b2447a11c6d23a6d1f9039dfc6ba373ebf078a9c00090af18f11ddf67c58ca2710275cf610f622f14c0a47aee0eb6bb82ea24ef051e0f9eb3e8aaedf43eb96c62fe73a4755fecbb7fcaadfda0af4c91761ad2f7ce4eef30ec5b9362d2a7da2b0a9181c55378db9a625eb26273cbcc3091ee090a5e9be5363afbddf5a04bbcf601"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1017206f55babc28a513af00489607b3f59dba4b5311f6e2aad3b0d715a58b0c","proof":"a23d5b590946eca6eb5a80fef47a7840384d3bd4fb584c956a696df45b22512d72ca5a00d3cffa294b977c301ab91d7793556cec65839905c0db67d28f3e7e6ee8bdd080e95815d8bb6dcdc72d57309e49536e3b9aae1cd56d8300328291a450daf778d3839a2cfd04db4922d5872aa7f6b4fbf7e3cdd6f7129433933533082b977f64c5ee8c262f7cafe5c031b075ccbc9e62507855eecdbce8ccec5df1f406361d813a08da069115f191849b0a645f18f74929177166078bf99183e0158a0146193f377ac61a8abd3183ddb429ba54712ec008eda9b5254629212a7ae3d90b8883e01bab258d576ee226b6cca7b414614dd98f985d2cd543498efbb7b0895dea32d5db244471a4a8ab707cb2e494bc2ebf6cf990cf40df2b66c47dbc70d7751c6289af1ddfdb77fb314b4ad836caf6efcbde90b6fcefdb93f8383ce92dc9301a9175b02b3fc9d3772df6e2506a9200e0d2ea1688ee00e351cef26d0cb97a3d5ce625d37fd544595d987343f75bb1d35496c33312db4ba41a57053a325cdc301c691df64843576da09740a6dfdbdc157b7571a9245675eed765bca8e5b36761789b215cbbcc69d522732f67335593625cfe702e9e0aed45f91c481159301f6e166a6162fd31e0511de397abaf5db0f00a5ed3a1c547f9d28bc8ab48b80bdf2506b97e13fdd7351e0bb38c2475c5d7f0a85081b3c18061dda79c9aa38df39172567041e4c1164472f4b2f9c2e9d3b50767405477fdba92f70c92013235fe803b609e21bd5f09283d00682d9ff1fd3644b4bf8bb27b6b6d15f5142985faa9914630bd1d0d291ed98aafe216e8954f709590f41dc056ae8e96acdd8f131b83a22ec593ab4172287ad2f3a9071037c447ac64bba178fecb804059276185f3cb790cc56b39512376b484d3d64974f93c328f198821136e9673bf96d388c9a7e6f905"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"902dadbc2e8ede4bb2afb524062597cbad0384de6615319e1d57560df0096139","proof":"baf9f771ceba397ce43fdc65029613994aa21c5b0bddb67eb1e6d0c0158c656ae48f40eb6009e12b918ab2db429a7df810a83d27acf20f7f62b6b6da62e27306ea6fb24c6b213bf16d9b674791f243a030bc4025a2c290afd75bb7a16ec46f665a175d6f779da82a3d3366c94f429d59528f3493358db097507cff82ebd4a712aa9bebe27183041958ff2ce62a756b84bf4c33d4cf47dfe8b86cfecba42095066f7d08d2dd2ee433467ae835c12921b283bf0c551bfab7724140581452bf8103d89feb80a798a3edff4fbef36b299bff3b17016acb0a9973ef9a88cf7048420db0f00b0435232cc207d27988471176b9f51371df5628a7ad89dd49b429f476134c4759967166317c50f8e0fe4ed904ca8e4bf92966a06b2fcd6cfa9d550d81018a10cdd843e2e989757e195926be62f75147a9c394a5d63d6e7c6ce20dbc9539fc89f42b4a902e4fd4acab14a2c241116098c2c16a1e81520c376a8d4ab80f6dd266586b3c8fd9de380ccc87689926f651f46311b0709ad11204ff872059772d02701482f2b44f4686ed7eaf1a89087653885f98f3fead27a205d98d3eb54d65b496ba1e2f24304dadf98769450a9b5bd7f6c44463ccb3c05b3cb07cdb5cdd34389862cd9211c4f16a9c813b994f5c91f78a0a97d4d6d5cabe622f1618da647922b83b4d5bbad138320b9b0a711fd6c8004763b28730368edb4e23ffe2691d5596be926947bf2957e70c2adeccb93d9095557ea5de561371990bfd225b275573e85905b245b3a90002d21c0dc793c96fa4accba74e33beccf08a0580c2d21e7e9e399ade70987755fd9f34291276d60a791e04b3665252422c2701a4552f7a5d55ea5db4ac6f4f467c2868204ef330d2c30819d66096ac059c32079e9414b50a9dc0b52905c0576ad2f10bae9b0d8f78ea39e91c22674ad062692d991ff82c06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e20a0d9b8d783f5b69cc9ff63702312db718259cd730a24e2fd49d0386ce8277","proof":"287e74fe2d655fe2fe2e01edd114c9e69daba3218482799fd7d7f690fadc7d4c28d70205c2b1d9502586648996d3748db0ffe84bbd01f2825584d712d54d6403504785aa4a5d02fe8cd0fb2885cda4b532409295b448311d06026cee5bcf89438ef33a181ccb5af66ff7365a03f997820c35f33377ee5d9f9fb67be27d8df15631d98f4143bd26190a8d9266face32a2d98f554599043eeeabf53bed96cc6d07137aa700697374fa0455be4ec72e1de5c9a43f28fbb49c1efa5ff3ca390c80061279d3467cba8e20e94eca77ef38ac3443b334d2893fc61e899a1100cc273a0dd08b9f1bf0a66426714ef7fc7fc51aed75e8a5b4cfb4339b473556acf3411734a8350071517e6a6b7e11e693bae9faaf164b19676ad436466279ba6c12402d1154e4fca454b83697dc5cb5c00e88a63214600e09c2ce0d1bc4a46f7bf05cf949ee43b9d8bdbc604b826bad4b5ac527f22300f9eaa42ffc665c7337c0a89563471ac55f971e5a577b7c2752e537e1fee1e5309cd8f6fe7778bced92952db813238a08975267fd05e1f9e60bcdf4ea9e88d2bc68c5f39a80df5cf74ce9900a9152c25839c07e4ff45af94aeb70d5130b81892c541d98ebbdef9d46cbab1a39513110a8bd2c3298a091fdc0cb74c8e05d0fbc1dccd3fba36d4e52e3250251fc57005a1a490d13778ba7be77792b4c62d6f7588cbaa90fccf17be30e88622db9866b96534ef5fb0d8516fdbb1b1417ccb0a89e2ce688d056820bd172b68122d0fd0f0ec242619836d8f79ef1f5f6a878faf75d2916d33730146b1be604e84cad1670967e9fbdec9e7f6f2d6ce5c8170ad3601453dd451dc50ac6a9eb0d67d7546e593022cde3c2b609fb1fcda588e6c354c6731f1ac3d717bb1200a80d7b9f5244076fec323a0b84e22713c173c719d87d5d943b48704db295679358cbe76457f904"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a4c8d75f3823b4625b6f515f98a4ab3f0808bfc67dccf81f75822fa653996d32","proof":"8ad9a87b5a574b787d3b36299772c4b2b3664c06a267b914aa3a48e6b5f4da5ecc171766c718c8050a6c6e6a0f7c1e0ae6b783ab61180dcbd26af86df308573e1cf71a6ee9e93c9af11382527893141f6868fbab9567d9c3f605f8a28abddd6c8c321d63f5babb1f333baf098b4c9ab434ca2569c9eadc1567f08ac44a04471448b69ce8a3797451a41d92299281ae2ddc0a9dfe32df5a384c81118a46fc16079d9f41ee06c53c9e0b9770eb43499339966953efc2243fe56660013fc1806a0a456390a52801689fe3dc87c6526f7e509ac8d5cbe66408f6a4dbdbf658c99f0f109b4f88f18edaf4d9fa1e117eb9a8fbec73b3d273ae6476a193385e7b92d412c66a1103f4e3006906bcfac020e8a017bbe2e3bbef3064c22266d291b8e5a01cf4797e1b3bc3978e33edec5a8c9fb4263dbdc907e29d5a2366dd80d188cb5f671aadb38eaa4488064c626c47edda8dd1715e14c6866db5d5bcfd55050a9b43509470802e35586a03afc7f50a335efd7e6631b7f6fa7c36b9d1e2f399f305412c4c8438882218c68aa3b2cb84c9891b15f0f6228b6990fb0617a7a5347a32377bfa1def9b5768ab8239fda0e618073dea07961f08cb4047468d6e5f6759009e480694bc8f508b27a919ab2049c1d9872947e73a0e75db068a8c3d8eca5a334a7f1c7937925d9644f2ae6d39633527933bf27c36d49fa2b8e85426668ad033fc186e4ea3808265030fd2353212909acdf75a62e0bdc9020b0e6ff9cd97c0980f0358fd80d78a7e3324413a41cff017452efee3d00d7c731a50addc74357de37d7a9ad7ea60612e648db37701c705ed064efbd790be6023c9b7e11007d52f71213f04f0bd1b82cbd8d04a40316b3782fe605ba04fe740348edcf824f129ac1987050cc7083580cd423b25da4038358f0517d56efb4088f6cedb9cb3443118109c0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6482bb843302b7003b80e5c5294d7d899cbf4e201c6531262a8f856e23dc617d","proof":"e2f37f1d1f4be23f3dae35522b085d7bb262363f5168e6379b74920c77a1b54c8e3a9f5399ad6e96b09c5dcd6781276a29dd7622b7c805f564c2775d04068e1554b24cf61123d7862ad4cc78a37a099128f6d7ed63e8cb898da5d7ca42bbd1781aa9356614667a78aaf926d7ab88b53edf4c93caa87a3e04ad7f0d6c053ca15012c2940fc7b7b7d1d311851a6ee831cbd06743e283900f038c21e4d43443d609eb0989d28e813c116b3f4f7ac5d22d9a2f08c2c60ca6f2ee9692b4af0181330876db4fe21972209b1ae4c728ecf2710ad8ed853591f26ec1fccfce67c1866c0388785a397e8dad1566244f55bbf433ea82072abb07a632d4b0bb8f95ae624328b472e51f7700020267ee614cfa4a15ae625cd0c54f036a0591fc2b5a68796f6ed84002a8e1434c4b920c8b6dd1ee65b2e916074293e13059f9c518384e7cbc461a1ff2f0bf01d130a86033a1f4d65cdde3890473d3f184671ccb80340e8b9654b2acc5766e8314fe1fd607e86292f79c505cad550939739999772bb6f39f193426d3d03357b427c220d5c5ba79ae93e63f5ac4cfa77f6206518cd245b399e17418572326c69c83f6507dcfdc00a18f4f04e5d170ec81e1adcd3f48706411893c24ffd628de4daa899f31aba7dd214dc281b4976acf95ee80ecd1da9907a77070f464c36b7cba21bb372d2026d0e6c9a6758428b211e73bd268d6cd7eca55c03a84ddef528f42c974d8170461e73607886614c10a3a2b98e3c0362b5fdd5ee07458fc702b8e15bcdf802f3e6bf5a487387d3721fe7cd010654cbf7bcbb23c994334c3e858b22340b1390edbda5a67a3c4078575b238620c7c66c4c9548dfe8a69b3b7737a78619c16050996be2913d1f413f9920ed637f93844b37e6fc876e90568393227333fa3dea7e0f16eb00d86897455b0e668d8545af5a76c9c92bb5b03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4c0d1af18f5ba04c526a725af27281f48c4667c515785db536d1efba60f44f19","proof":"8e6d55f098e6dd574d5ef4dbd48f08718233e67f9308919eba61f80aa2b10a039605000beca4bc6bb0b085cbcd8af681ad5e5b5603cfaa5e928f3f8b8d91e14f6ed2300f6daad59007aff6dfd1221b759f05648354bfaa45b92de27a6a519f07ae2eb1e85baef0f82d3f8b9e936666920498fb3242fdec018b765142c40d1f471be1a64cc179fc583e24f5a9aa4c8a04c06040b66ab05718fa922330ae5554015a98aca5bebffdc2d83d8e72481ae68c8ff43b39e70a4ea870dc462d3c7d7b0c2cd5e70388a43d94344051a54498384028aadcaa7d1e74202ae77ad20a5efd00f84388c4625686d95c4bc044f4be28674f6f42f2b8a35998de317e88c5f50a607098bf5ce19050ba63bb92468d2d7371848aca046646ea88092094b7dc822e69d27a0f1181bd0bb6a8de405b9298d50964559b41d1bbf62aa05e506a9fab69652c22c96cb928ece8f4a3b4715f30f5bc40b366b666d5190dfacb270451c2614d689959a5d0abcdb718b5b52758edd5c43d9825486ccc54fd758798a3eb9cef2db664b683030de87645cb891c0c1447e3b41df7bcb253e7fe6269fa1027297e60f2781b8d5e6e81e68eb0f7b6b53bfaa920e2028c30141a897bc52b8e73a2ba1534974e3c688d75ada356ce941c0b7cf61937acb0751350b7014398d90047fa3348a42f1eeaaf6c0f93d4351e5fb393911281b016741cba62c22f2595c0019e39186feb2ddfcf2dd890d6ef9fc32438635cdb1ffedf0c026b84aae5c225e31e37724f2fe47b88ee73f005b2afb77995f4d4da59b52355ab9296dfb76f08cc8867fac405791767de3847485671cad17da7f149c829f13f42806c540686a7e335769c3403e7e5a428f2524fa2085f5ee6ed0f795d07a33a94e28427eaf6fd74b203ee3839b4c4be3dad7fda47f6f0c5422909f6dec68ea3d75d7c4b2819cd4ac70d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c629faa33d417a85451c29cd0ef87a52cfdd55d5ad041d75e23d66f2a85a337c","proof":"921a17b251d2000f29c26ab137f24cb1a086158ec3b82dbe15297193a5cb2471c0a428d43e1aac2e808ef0d7812f24d74e85b451f27743d4dbba86e19c5e263ef075eb6795db353906142899610d77b7a466b2267f5a2a80bdddd6882350e01c80bccd1d530356ff34fab7e6787df29d6b211e7e4ad34c584b0299dacb0675408bbdc34694cbcbbaef3836f5f0b6966e6c78726f4acc35c3093d650d0838c9048dcad00d4f8040032f5dd66904af5be9ac1df9d19cc87b6a8470df5624e63300a1928c32a443b72679b9b166f6b49b1b8cc2d5734fbd778214d79c2a11be7701de4bda19d95b014e683c848defc86ecdc88d9ee90fec8174782a1fdedbb79b545873ff5bb7c1dbcea35523b6ed82f1e5d9ce953ade3bc883c0b9b1acc1b40d20b4d9ed7409aa776503181e5221af6277286da966168bb9c80e6ee9970e5395480a42d7bcee1dfa0d84d1206eeb032bd6a420500f7d02627c820c8a73d645eb17c458e9ba4db9527129a4ce9502e8d1a62dc89e7c80c07eff24ae7b1c0cc1c006c69d6503f7706b15420560506c3c72e4cbedf28d45a930fd34e5642cb509184d2a443ee4c79261d7e2dbcce44224e309eb59fbb0c7c809020dc3789c29a9bb2d4e370cfad46b47b366080e7ec9cc58b0aa24763ad1eadb263e43cf5710d02935b4ddfa2c2c68e9bae438f7d4ed0d5cbd58f61ea3f11ef3f4191a40a4c5341e525c75e3147df9185f90baf7fda8ecbaa29ec62cbd6ee701554c435e72ca2706453e63eefe172a909eca894e86bd96934d97d8f1361807ce31791dec19bd3a5e0f5ed0ee2b4603ea0625ad77c6122b19cb64bd30eef2d23483128c462b5332c04848e80c622be57137ab2c6c58284f867712864306c6a597055291a58b89f327008ec5c20efcc0d34e2f44e7364ced6ca7c5a9346801003ee6190a58321cfeff08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7edc9ff7c3687532c5e070c16d1ac5aa8a00eb100cbed0897debd349c9e43f1c","proof":"d890c3f9a09b8493f4681b9d2281f955eaa3bc4f3b96049e3f93b9d71c24e75e88634eff3779bb2efde6eb3fa26d8aa87e03c473f6d64863170d758aa09f095a48dc6b5f985094f81d3b971e2def88c881b2e435e54b6a38d161559d0639296d3673aab739f8ee358db2ea82f1fdeedccb82f2c8debbfddee82f40f67d7fa604bf013e93677aa53329e53abd09b3374be41c4b0c07a79942aa2eccacaf77d30651a95e8ac65c259e9c5ab1f8097cc48dc2741e62e44724121653fb7df63c480110acd1caf15cb1b6b15f7a9b1e09203ffbd49b9c81f8999d12b0a6080224dd0c20e4423adecd955604aceac7f69650a17d5bab25bed118564d1c646d16f0ea45fa8fe296fce274243dca589d256c177f0c6b736f57acc4cd513a127109f0b24876a334219792c3504846f29d542928512cfd4a727b59314d00f849e8d990684152de5712dc22758f4afbd91efe93ad77dacd8a52508a2027a68908c8d5abea20a04b65d3d03eb8e663c346c96b659a63b5e8be0114f541c690501af2ff5d0939def87cbd0f632f62af6eb13af0feb8417442071860266c0528d9e9c7fcd9326a7444e42bbab9926c293d61ff78a9ec63b9fa95d5f1ad662b48087eed7e938836d4e0d409e7a7d246fa16fd6a773c353c4aa6ccc6daf3648e8f5f19a28b866f216a2a1a9192d1c666a1842a069782febeff25c50a7ca039878d7518803d1a7e597215b8fdbc9f5346170cce4e4f2389a7c9fe6860be91d6d9064df476fbf81377187d7b2f081df0fd91ece203ecf318a7686badfcdbbce2000af8a2adf399a049083ef95dcf475cda46bf8e8cea9cc80cc40fa82c1ca167c8e82864c3a912ef5adf374be2da184a6fbd6c2a7699f63276ef2aa3b8baf1295af3dac98051ce1e0e1228384c4624c05b7c9d95f4d2a1920dce81a9661bf28f98794da23a76f7c608"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bafd67c148303d30badbd8ef4272de41b071877a1a73a2928dec5f0521d9ac4b","proof":"c8c2842df852429c8dca4099dc32407bbce60404dd4f9f19da8d1ccf4d618e717e9f9b5d8109fa5457ce23996a9c4823c75e6ad93d0016bd8b08924a7cf33b79a0ce2b23878ceb86cbff6abbb4e392acde94b74f578644040c761039d0176813b2eba760d2b069259c4ba4de6b6d42bd7c37bb78fc499ee66dbab5fdca7aa47475280437d2fbb1b4eccf3127dd5114025d169dbd434219955d4daecd430c4d04791dc951c3a83e537826ab440c6d3fe2b378bb253a4c0e2185065996ca1c9607a242fd9e6334292d93b35d9f46e2d01e02870309a7a7325cc6f5348a037c200866accac9dec5d78e4e6b507057a129a9cf983f80d1e482b1cda6a4b11b2089782c9335d1fe84546ac9ccecf6f1088b1d151a1c4c65d8c93f0ae864e267b6915e181e7ca1a965cba87620a1729b5e4f644e9013028c5287585798140509bbd211104924a307ee941f084e891cce29149b7f65da570d76a8759e503bc5b7278d0a725dd88813526c2599b178fa4528d9cdbca26a150eb2fac42c34bbed5a3178465082b21926d6356c77469d58683cdf568b61fc1ff7f96542c2c2b7f9692356615c53241088a39bde05edc573eb741196f4e09ce72362466005890834099ca05dc490a3296802815ad0d040efe07a3164ffe7a4a90c43548756a8d21487599128babd8c7ede062caea9b62692249120d71d83c25bc2c10cdcff825ae58e4a215564645af1139e1305064e614a1e093f40d48cfd310ad17e2b8af97077cbbe1a1d30fbd8ec88d39670f6540dc3e60c0f8110965a7885eab50c111ee64b9569171f5216c1345497da53ef386ba401274ed7aec7e6d9e21bd47cd7b41f4b48657f2206457a1eaac35ba2873d05c4528b0ba8c0891a958107d840bfe511e4171bc309c48456aa27ee799867910af60590eeb91ed926bafad4cfa9a2b38dc5ba1d1606"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"20886aee188db687cc38b52fbc4256b8d4ad3ae130814ac117f7df7c6efc1b15","proof":"f8c5ee26fcf7388a496bd5115b537f2c33bf7b6eb4828122cc348d58fb292a2eb0870ba36c6b9e5224f6bbf70ddffb3d36e7a016ddaea9cb84fe67b9a032a83cc8f50132b88a6f6001db6b439b6142f4d01d33a9dd36d6308642358293abcd7f264738b880a89c12abacac1995e38950607ff6176ed00540ddfb44751bb65264884bad284b8150300b46efbe8f8feb990a12ab4fcd624f550d55e92e4a644500e290f4c9d407f6d27ee05561ca6380e26aff9690a69d982f279f717697c09108a6ca8c88eba6abf5faf816061e54fb5a4718e395dcae564de14d0498c7d52a09bcca79a51a9c95510636f806bb90daa1b4b63c2f64afb607a87ee8ac850d240b742c5ee88733c0d80bd6f0869857c2873976d314ba47c406f0ebf7f7e3985d630aedc911977c08ff365bc69fd6ef928f3b976a6739728ba2c97f24e3941d9148460f92dc291a985854c26b1fd128a7c34140fe39a15a86579e2d94a57ce079622cf20e257f10c154c1de72dbf81edf1e07e00c4a1bd5fad2f8407b690725ec3b0a074eac0ed97b2da9560a977c80cb6c0aa557cedf219ae92528faa371490179662c9f987d51ae48ba0f76bbc26399b28d7a039085767c3eac0b337e14419f378207d942c5697d6d357a2e0dc0e83dc57ec88bc6c15f465c0ebfd50ebd067e0206aa7df23cc92d7226dd5a769729d983f4f36d7d313ee30f58facb80da976c1d54911596d46e56323c271b7ad3c2455a94dad25566d9b53d4ae0c4969036933766ed49f1e9a186a0cb15dd2e84baaa0f18bdd844d1d314f4f8a83579e6b84463b64435b4ba01080563a7ee059481b9f3e1fa291b48c923cf6684acc63bc75b6e64b4d10ede9cf7e50963da9a32f81fc7671aa8d0516f168a22426e48f4be230d56cbeef65745587b650145803219f344006275d064133fb83a7c864c1381a70a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"70de2d00b136fcc72c81dba45c3b03ecb1a044875116554d8a587a71e0861f38","proof":"24291a52d9184be38401e0518af79b6c417583391763a6de511cea3afd87d814b4eda973feba95e399bc5025c2b8edfe5aad678198a1f7a522e7bd2ebcff7010cad99b9658fd9932372b42fa342d3d53fd718567e5b9da39379a53f5482cfd2e4e903a4f5b8b048fb0d7fa8c0dc4f2711e4a5da6586ba4a9ab50b9bd7e74ed0a7ca7b581ed14432c8bbab269a701a238598c716a21fa4d4bf0cfbf67cb085106d0ebd6cce18afab20bbcc22fa8465570e16f5d2229b70861d8e16ee766306100c6dfd63302a7011bfac46b2216b03395a414c9ed1c53bb821130a017770f9b056a61174f429b71e9abcc7e8c32708d10df9a53b1202b6181d7f4d573725d031cf6d726d85f54e784c789f17f0dd9b1c87de4c62d086ea82c1fe5ae89b7de9259b65bbe47d827acc7b4213289f03eeb24113bd184850d8763ee6dbff2a7aa2f4348951d48183ad3e9fd49fdab56baa8e1cdbb735f9c43889d161b48d67c07dc698226e10df2248e446eb06bbd15ca8412ca53ff19ed05dfa7a5d0faec48cd656ecaf7ec3353211b0cb8db6bcb0b04dba1cabc74ee7883cfdc52c6b84df4a8ef045ea1f52b419e6e76e91ffaca32563a5a1df10e211e37a47a51727dab4104c1531a53110c0a975d720bb42d1ec03be532c2c59f0017e212ba6726989c92e6154f56bf7aab8dd682692e382f4b82607a65ed486991c054e91a0697b8723cb9ed7d72b411726c96596e70a7965dea89d55f1a907f8aca7b0c9df692bf2ed58ae3474a89a9f41cc3f3af30e3ec74e69af3f6297adc91789a7e85d6c32e1497d9dc3aae101d5795ca959c23216d2095647ed4077715b0713a6c84314096932c116413a78703aec7625d372b9f383277462f1222d4536e227e170f129f1ec8336a400bda30f8455b3a1a9b02412c08301462fd84bc05f9344abc782e6dfdb421bf8306"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"de029c8ab43d92b915c5407d9cab1b524bc84faffcc6d2da8426a080762d8876","proof":"1a5b76d7500efe1415cc5c12ca58d9a71482df0314602223a82d4c8abc50227646c4d821207d32b911a6cb8757c38584b1ec1826e7aff9c97ed06d43776b02117a186d36947d8b393545b9dd8a1c28cfad5ed5d752be1c3a4c8552e62e134e720afff3f3d156c8f58df3d8a221947322ab01f06db339d327f456a3d8efd3bc79ef91421c18e5decb8f13599f3c526be1a99deda85127468c9adeffc575855e0ef267bcda4e2f2bc9d67e5f12460b7feee41fabf4fb7edcff8b8f7299fe98430200bd9637a10278df5f28cb2b81fed7995ac534f04c256ed9b7c30b1e915e89041637e554847260622d54864412dfe44782fccbb1ced3a1375601b60c9fc90b4d0855d03ef82c98636d72acc05ec1aa26e1e27c156ac0bb9288147b0973f4663ba4411db11b27737b054eb2d51c133037e98e9b9208b6b2bcb8cee1e1a4b2da7d642b56cfba5c6fbb420bbeda415579444aa1a2fb9442f998e134512e52fa0b226e5c49665994e7b638b6162bcf5f23047dd2b2bb4195e9750435a56d0f83022dde411a8b7417da1b4bd51f84c8b27cbac8d567b7c04a30a930b278a08626b0499cd0e6e84a2d1a6d700b0c2148a52df55395908a897947850ddc1340a90c686b9245b397a644e13f38f26b2f1d02b9d04a8245d616c7464958c9aaac6cbcf06fc653bf9e055406305de7c16f3f1c03af7e63519fc866c17969ee36b5d0c21f130ab697942fd67ffbb715c7c7aecdc47f883b1845cac36e320a345adcd849dc7ddaabdeb1403d6dc47215a3132f88cbe8d991accc31dc0a5ad5b21496583da24810a0351d8a499f1daae6cc84d145f3d9b0d2547bd305b46a5d5f7779ff72aa51c88189019cd6036a940efbbff0a89cd9138570eaee3644400d187717ed2ade0014de2c6a1d9413f3db94894efa32d0787ee6c4ad2599df3e421edd91b03b9e03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6a00b27f4cd1000ed5ed369ff85dc0172c9f33d4aea86009a1abba4b6bb29069","proof":"36903e0be14fd6b457266a69ffbddda310cad7c1c1c15f2d7fa62c3fb0b17f1174bdd93f069eb451db5bb095174b636f950a4d4847cc79c6e9a9eea61d590d0748d68e5e7b4111c1771d2afbc6d31538a52637a20f34b924bc87ae1b67b6ba32360a42982e326d88e97f651a165d8f0819d35148e4ba3471ac12440c800452664aa17bfbbcdd94e275a59c918d81ba36585d76d6d41b7214dc482e6f53629b04fb5875855fe8d30e9bf564e67cdf101aa713fe51b1df1a945f729d1fd9ec070c77ae1bfb3c4767cfa3e8a19452787d8415865deebed97e59e3ccf6586a1dbb0c42732373372779d3ffe7e4fbaffc2ff9a142499eee70e1aa56be2f48ce31e238e0f9bac389658646e09e2c6a69721b15fa7a0e91b60911aba4a97c8531d71a522c2a5e4bc68b6c549ab7e2af67a1e30b8667fe3ef3d77b7ba9e5f36407207b0078f1f09d584c2da9b62e433c3eb9921f778c386eb3d3b97035fccde7831e99610e3e7a455fb800864aa26f49be88b16dce72c31c5b896c4074c3b6e4635b1a3628a5a6ffa73566e764ded7e8531925f29f3746e63dcce3aa36734c22fb96571802ca724bd3c255cd2034b0f028ca172e94be46ac4cecd4eaf5d9b78b58b61d75b4e04354509b5df35be828fc40e226d3514ed9ff9627c0b1841f10ac682d15515c6c16e54eb86d95c00f1b5f11c09a50eece482b4abf68f8929b5dbf02462f525ae6a03889749243da5de006f33f27c48f277c95974c8325433bbf4d632b883a0c613b6c04088bee64ec98f9d85e4032751cbbb63001820dc37c8b4d495c9a1356099bd8efd271ff4031702a00856e5fd65c08af3fe5534157b216d5a9abb031e5e1195bc0837c8f4244c8158c816554782918294fd63ca8f256ece1ea2639045427140679446639a61fb10ceb17aaa1a6980e7daa0b9cb8fd34a9588ae8df0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"02b76adec5d591c5a7bb7850ddcffcbb973c310065538173d642246689644e57","proof":"f8189ddfd7c37118db115ec4fb7ea3292f0e884309731ac512b214ddad5f2c6c101ceb6125a65460d44232c8215c0fc647cf4eb77253f742b52eced0b2628f70ec4825e823f1e77fa49b4684e90b1cee0aca35644032478803f6c7f7e8040a5592f2bca378d9a9cd9c50c16e141d0359d18a174a7da70f817dba1e50cb01534df39ef877aa07dd9572fa1baa3534679c7023d2e94a63b91e5f99312b01a2370231733cf73e69871a9afd4e40d5e82e9c0ae278d5776d4e4be61625d14b395b0d79c67a624d81d58185e7eb7ed3d4a51f70e11b6dbb7ca810310358a769c4850990f8b029fe6207b70d7a237576d0bf4e2c80a729ec520427af74bc99f0a95d15d6153a0a343d8eaaeb87baaf3921b91b6d8c014a5f80ce894ad25684c756624f24d56c8c312bb72a8345dad46a3daa0a54926a76bfca8a699653cf72cf95d84e8413076eb1c8a24144af74f02c9622195f71265f954de1e2ba2f8efdbb348b042c6eb36ff53d9b8d929c5b123180150180300bd5d50bb048001da9a972736e040ea3f3775566a9cc36b23418039faadabadda95979c5fcfe73eb49a479ef0166d02824c665ffabaca759133118aba67e1f8549de219f48b2012d9a23a108b26498434dec88f2d08ee0c49079205bd86861913d7d60a30a0ad21aa46065840a19ea854f82ef7b0f918bfd3b950bba8b7f19beecbef57d329a0030996078df6f72781de88aadad06abe7b56e39f9542b45042e5fb50d51ec3cda4b4f329de56f52260cd304ce5d748de7494511db379de3321aadad6af10519864cd64a34a582627e834d12f6844e680da22a3ae52ab61db2c21c91861500cac76a5e475019e15bfec68033c8b56935909319f9596ac0b33d82a41469fc4a510b8ac3620242ed07e955e952a4634af55a4535d23a17c108d310d5c1a2edab176fe74b8613623e0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"12585c61eb66c6c0893a8777f94c8b1fcfde2c164d91b673d1532d76a590113f","proof":"18cb5b192816a3cd3cc8eca03117bb4cbb37b45c6a62f72f11332a19f87f2207ca7fbce5009fa98e229f5ec45fb49784fe12176286963964857e5150128e5d0e9690a2b203d659b2af2ff74345096ae43294c7a1caeb9338ea2964187b8d2b1a5c6668bfba5727a1211bce039832e0301b413db5b0769b0d910ce7e0f612c57446e9f6f6a8b904b64be0f5277ff2daf0ec88fd320e57977c19ea3bcee3f3cb0c437f68e806f647b5c3fee01ca884080023b696d5c1337717869a5de7a1db21095c998c79c7ba59480cc60d7a4c255a63d9093df422ef8fcdf451c425a54cd704e24251d2ab1907d204ba946c0a6c88af6f6e14802ef085661c42e83d96d4457ff8baf24b580901135463de1de813213dd7d73365970e34e347e78638771d18020a34fd65a76d1981b28ea2072c6711d78afe76505f6dd4fa27cb6dff9db3104aa654fd7a6ec97c2a520bab88629dd7ac380319bfc34126a5f521297b9fa052341c73e9d820bdd61ea00c374bd92c99ec16d9a3c4f6ca0357c4de0972ea6901065e49d2caff50a7f15d79bf22ba0189a8563095d6b8f273365b4b84f60343a326ca0a631b2d25aad235b248ae416564f37630e632dc646f304d37fc985623292164615f87384956970b0f63c04d9fa1feb8df460e3b969f935321a3966a03247cb2132f1df4c27e775476f1310cd78b02918ecb8f2eee6fa1d125ff6c90713a4d72d457e2c82e8cda60d0a919d7d51329b766a615c003682ff63d02f908a0377c3463f8c49e455c1a85fb789fc673c79734d8a670c9e12ce8957907df4f7e79534e83a79adab14509065bcbade91581f9b5d04af8dbccede6acfdbe42d1dad263cb56a49d1ab63a08d105676c8b837f0d9da4da0c2c5263a39b35beb10b46540ec8b682291f6647a6896c4d9d376198e7e3070412551b0fa6c400904ac7fc9602"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"663b70dd09206360d342940345347d74522e805d3f76728468d4f8bf0fe83f77","proof":"cc729ddc81addff52203afe3408c51bbc1bb4d0fd3a6631dc5b76a76f5246136869b3ca176aba2b10c79a96ec4c7aa80395cdcb2bc992ebbfbab355ed924954752be8c05306ccf695d5508b03bc9a94759ff2cad9ce9295bf1594e2b97518641961b6f23d6002b82e3fd8c0c793e8245e7c96314ec87c9764cc8b38529a01b50aed090e5649fa6c5506ecbff7a52eaa8618528193bc8a8daf2b9419b8c94960954627aacf9bdbc484d3a6c57d200d396cb0aa2037ac9a993f019111237035d0f4a1a219d53af468f3e73c705d5f78a75c84f1d4e45a66b852bb41f94d489cf05329c443194e68679af5f110161019a4c980e4c858bdf02f75cd90cad46e95917ba8996dc3c52e4d9887fd26350ff6738a79aab588439bd5f22ab712fe0de3c24bc9855123d7762e5134a01bc9f58f0934be4776b900585376726a8a8bfe7a81c264b8221769f4cb79438f92eacf06a25d92d70c4bc0866588c653a8d81e0a94746b283dcfcb3099ed6a2ffb0a54271ac5750829cd394b7c06c58b5e52536347a3e15b89d5c5bde341447ecc117efae790951d4aea8d42694c5d341d49fcd6b6bd2051e80153787c2c5bfd211e6095df62d16abc88af2395811c55bd1251045022432eeec9c4c39b5c84bdfe39f12f4d0ee35a9fa615bf3579babe1c101dc545efeca50535dc603f48372489e01653fb5bddec19593273ccb7d814fea2c552946e6f160d85de10e51137883f1ffbe7d64b38f7fdbf248849577ac0d9c834f6c4808852285cab902ff8c3396937e89d7164f7dae8dffae01503b11ead82249a24e36bec674de0559dbf180e20126a143fe56f7b883b6adaa9ab3a68f950e70064dd1f7ed743637a57d3abfb3ce6e246cae01448b4dba41a43de5888fce8f8e280979331caf59191749ee778cfa12386ea659e29f74c64c818f60415599c2322700"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b2757d9febafd961b6d7dbbc32497a113c9ee766bdacba3114832e28b30b2424","proof":"9cb7450e6113576f94e5e76ade48c872fc0c42c7285c8cc3342fad8b0fb5e7403c70c562c9e87ff87566c8dbda3bd73d1a73e2ddff89d32eb0a3f982e3d6044fb622a70755a8e4d2e6e3268d269ce644a9ece601519a8967c944d6052bcc0b4d543d1d81ae6ded6271fcbd34506f23f56b406a3b0cd4e2ced00f79638c79b86ac72ad54845a381465a46caa52bb1463789a839eb9a8867637d41cad15900a80af58b9ff152dae64984d4f20e08333c84f1fb6fc2f9e5f52bec47c48dc90fd20d1893b7cded15ea61b597012d542ab0365d21085c24eb678aab0075a794e8dd0a0056ae4d704113127e8689052e4ea1b40a4ae01cefbcf5a530ed93e38d2c563bf6d402342c053f8882a52d65c34714cd6d0a6c5a523b568f0e0a1ea241a1212e84bf8af1820564f8f6c7774f44f875b9fd90fa6744328c22026ee924df1ba21b72f73fc33ad538f15c957bc9e37dd65499c6a5cf8eb051aee6ad661712105428faaae696c764ae3929d5a09108cceeefe28fee2290a6e571072f5adf7ca84771e6873bee94fa125f7db0716577c78e229d328f1929e8df12c8d71071f4c2f95c72a9cadf71ca23c77949b27f8318a97f25a3f214081f9588cb4f7baf7db0d0096051456e9a16cde1d6237e1d60f00eabbb2814d96e470014ab0c472bcfc0b25db25d4d67595b99d376fc5fc1b5af64cb41e355b43e031e4e47a13edac8feb76eaa6a53a9f8968936e74a8c50fdd6b86383b45ffda5d67cf9a214c00cf2a26b0d1079e1ffad74ea862152fdea93235da05db396dd5ba29e641114c2997ebe1a19a2c18ca50df8f70583743dc3fc081cdd17610e13e540c90cfbce0a466afc695fdfef419ea850581c60d62b7e5bb990dfba371c7a000327e589dbc8f7abf1bd0ff849cf5fff213596d17c277b99731a0ccc7eb75fc1d5259688f4679c177bdb01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a03db7f7ca8c33d25d64daeec423038a419f2788d1e9294e4730b9a4e2587957","proof":"f6d7f92db4a27436dfd1c0731d9cafe71fad5496dfcb41634e1a9532d9819025dcf0e927ab28798abb8e915eb72c7ff23d1f10186320ba01c0575685ca437a0742bdcd8e5d47f1885b41b20d12d8b85a00e9e92de4b8b6a181de91197240370102d247f90eea4a0d12485f5e68349a5a51c9483facc3e0e9298ac0253f609f7707fb0cfb06c2ee0a22889f2d32f3124c7198594fc862fe8b358ec464d95af1016c934d5a44f2240a50423c774a8120595e5613f31f15a81a71e6593a871ef207600db497ff743639aeff9538a6bbd0c25b1ef267efad79895faa2bcffe642205baa8430af8e6bc080265bb21e6ef8245f283f96092319c40ae3c57a62922fb5f082623d6a5450498b23d5bb6a154eac0b5735717c08c8eb12b55739658bc1606c4105e36ded03ac40046164f6ceadbd80db65c822787d91be5d558ec9bf8746140eddffdbd7a32c035d0b59d45cf99cb53839edb1eef928426f94e935ade8d17724c79469db4ce05a66fa8dc766a1792de04a28487e96c09c6dacffcd194bc62febe78f2eae08aab40af5b9b9bb007c098b9763b70721889a0e351a462db734faa5234ae75d07d0df856b8951d24a238f5e6b12e6d1ebe97f0289b88de5dc30a18e5ced0b3f4ea90a990965e8354db4f11f3feb10b710fd3fb54e6154f7a2b241cf911f7926c9a8a959fb981c24eff300fefd6f0e02e632a693c5af43ce120333e8a9167705ed1fc3fd9cfaebcb173981acd8c7bf6840a1e247bcb7da82f5627040a491e9913b571b331922a2e9882cae64011eab163cbeb607a148b7c189f538af42cf893a2dc59a29e1b1656cdffdef15e12d9db7f86262f3e15bf00a9ca72271f7c1c09894cc7eb9a2ad0c6fb3b20c291b277d9d6265ca320ae18b9178d028afcba630d5cc795ce3cc872e452f147bf494c7285752472cf3c2dee91d67a00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"802b2a75a119bc0ad3489b49dfbfffff7f8b1d7bf8137840d7098a032b0d527f","proof":"922325c970ae6f22b92f81fbfb03c60b0cbe6d8c228500277f056f3bf4b47c7afe78e556d1a9c1f26d8dffb6dc9e77431a119a174fc64d1a38d62778b2f6900016d2f7483eb7f692aca03e88c70284facd54c737b594496e10395d483b42d311c086b7f9130c76ec53b3e103dcc5a6df4d52f404a981e419f2d6029252e8306d554fa11c9d2bf61ff51b0c78c4a7541ab39f662f416fc4539b9118f309e705043f7755bb498e378635e19f2d00c9ff6e9aa5b59e9e5da227e073a9e365a6b20aa2eacd21c73b32fd68f9611ac1d792d06bd6b8d75ecfbe3e67c9991a0ebd560e3ec7304ad476099542aca59a20fdfa4bb0c9bd13b861aed96e041a4b3624834e24127780e8020c28780901fde42671f40d865e19103bc4a3026c9abd3afc045f840600849252cfc41d1f7e7bd456b6e0defa2e9539f265b155a061e3f2c3614f769977a3eb1db57a8bbb8b1ddb3cf49b57bbd11adb02d93508d7cbc3c8780411c84614af9ca41d172953d7b381ef0a81a0952cd3e960e1f28514ad89eea7e865b019e84c0fd6626e3d1a1c840cd6f742aed492a5f52c72683cd7cbcd7ea2e97cba98fcad49fa29e7e14aa6792025430eddfe91aee6600533a56971ed3d104e28844a0fd5b2b6f533873ee7b3b519ea3f7f69728789db001d68dbf32b3f8584168e747ed19f5e2f89691ccddcd271d8679f35e9c29e48b4e8538245102aa58f531a089ceec21df5f0ac70d51e55d6210ad0486614f34ba2faa401e5856045952156d19dfc4190e57d5dce9d4bc191cc663d5e4f1160e2f12209d972f66657ea51f84d42e2cacb7ea028c0db45722222347b325eccc0b69ce864e463eedd73147f8e308233e432b8c116a5fc0120bbbe0e6336e4eda0679ec427bc024ffe841a0af38335023f4f525003f6b56dbc8d77a5dd4caa36f26067bf5184738fe3195c03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4e097fd11bb09944bbb1fee367851681abf285cb58e4deb8083b4ce6fd752b5a","proof":"e2e199ee9efe75ecb44ff5a9b1d3ddb168609f76cb99aa9ce49b7d93a6063465523571f83a107e9b828e1d28d31a6725446b16729937f8fd1bfc44a93d785e487495ee3a6027eab7e11ec53fbc445d0c9e6b1cf6221d967c1f1805569ad6844036dd4af638293fef2ecb255d42c2c9b580e925446a88da871d9f3f2a0ac4f4779b59f0eab855b9313eb22203f6cf6f5e01832d808307eb8a270050cb54a4310f4f8ab365694b4ee9c3995975143ae383e4c484834cdd5ab8d57be967d82a8c079c18f970ddf13a445fc78e101ec9d1a4989d3d0ea55c19f28abd50efcaf0420860f5721a2c3bd80b59c84937ccd51f87516e773ed8326858292f28e884d8f82bb8d3ed5bf90103f8905296d3813f2c79b3afd25f7a1b9909e65104e213ab7e2dd0d280ed7a9781ec143c5c1a23d3f462250b635299c3dae8cb6d472064f2e8181e814a1614d91550d316937d06cb734cb4405092c87e0790e04950cef49a1f05205c2ad22869d8c24c850023192e3de5a89df0fe923f77dbb27dca2c4994da19e6d1dad64d57f4ae63652d34cbe0f80da5a3d4084e6dd9705fd756468cc06045b4e58d80126e34950c917833cfb0416009bd2758a5c39ec58409c602647c9858d03e5c06dc5db3fe792e2a36f5310a360ee4ec7d702667e77c2f75e31ee3ac7db46ab4611342913a4ec6649323f1e060e1807b97788c0ed64c2f78fcf2d08240d651c80159b223c5e2a7ab0c4c6980699be02bc8c486754043915ce1e314b1370cf5d6ee6a8c8c220e0d45c2553e4c0678252d7b7230f0199ad31dd9e9278a2caea91dcd0693e87603e4bca44a7749a18fbe78a7f195d95e549a2d55e64dd75b37d2fb9f5e93765810d15a7f0e1e749cdc4b8563bef97b21f390c7c1fb22c30b6aabd45e0891a5cb2dceb64fbbcf2e9c55ab4173e86ba239413a1dbcfec1eb0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8445af885c86275aa5cda23a5317601927cea58e18b4943241bbdb8801fd1a6c","proof":"88fb3eeecc647cb228c85c10005ff81e4a6520ee62e7aedfd824385d7fc90929ded53d1ae6a27a55839b12e0c1aa720d96c1afbef967d21e92811211452bf471fa741118de6193fa1ff611a6154cdfdbfbede8af9ebf74082374b813f3a4196e3c2967a31df7acb68893b2791acc21b47d9979867d2befb166e742c9f264547a17e938c01897d8c0be0f06ef8bf708b7c35d637ceb04aa0204fd8f0061c37d0a12f672099adfc446e815937952ad6b22635c838aab731a65d15692515faba10f466fbce4b5087d7e69027e106a34c2650746b797d273e6e4dc695967badcce0ee81c0c3609885d69e6ff4d405681440941fa6a70112b98c94c26e1e9206b89525a993c26866fcdaee25544a800b0e4eee29ac0c555e7544264319afa333dc2729e003e5b270b9ee4a09da4bc5c0567e22f7bee4d5a5a4b5cb6851d3c9760784ebaebfbaa638547742fe5adf0b88ae9387c5b565483de225cf0365dca1f522a758af20ece15fe9e511638df56b1b3b5df8faa18d78e802cfcd3e13fe7aa32d90102d490c853f0885b07fd2ce427610e577f0095e93a0198754490c081505c1778c09ab3b1531a654b0e4351267da57f4c68b7f10d02a484f840e88497bd7c8d6d1e43c5e129958266b6a87b99244b934cf3fed6101ec0b3c0bcd733a1836f5d05f8775c1dee8ae694df9222029f7aab8f87939afa1293122571467d1041435f7eb88adf0c7caeeabcf8cce72632b9da67c0473dc853793f892ea7e662d58720179c90e70aa39fc125068cafd130d3b2601cb54decec809e3166cde82ebe4b506f28e5a41337cbcfc6080b868c1117a701cea379dab126fae891bd403a3071033cb451c960788701eddd14581d2a196c19ad9ef9003a6e5951f2d985dc5bff65040218ba11d4d8adaec0052d617a5e7deb7c8fcaf925974a1a4b1ffd02e1b1c60b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b66fca46b82a65aa4bb13fed0a101478e474995cd24b6a06f5a6ad09ea6d7f13","proof":"d684284a34033078bdf7939a16934d0ccffc53b5f8a883c609834ad8df97d167f4900711cf2f3ae09785f9129e52d41a7f50c6e513519f51cb0f6ef788701a309245bb98bd756969385bde14c5447210bada6e00efd25f7debfa338f56b8567e785a9ade4cda035967dfada2174361a5fd5f7ffb8009f6224afd1927e3c02545d9e5a32562b9408fe9cb2a7f74bdbc44542b781d65fdd1c990f086393b82c5059bb59a76f145734ddfe80cd371e9fba6d9b2d8e3813a48ba772de52b6985ba01f0c9450f52d5067c78087195a89ec331aa915ff10545e851e70ca5042cba820b7c361f06752de076d97fc63b6ecfa975dd70bf5c71ffca2d053f753ef8fffc547e610dc22d88c7f22bf3a0fa6160ec09bd7a7ae15cb65c6d01a160e680b0a4637815b77ab19c869beece9260c677c694cad4a003c4f9dfc4490205fb45f8847dd077e1070fadf1534cde57311a9476d0247fc02fd323ae774844eadee803f14cbcd52320ef363f9eceeebaca6d93cb173e63d872d60955688aa3cd5763983115d6957c595c9adff150fd416785fb55b4b1cd971990936244d1e71ea77325093280676a7cd1343ee0d93544b23c88a7eff3ae08f6d50d576913728dd7d6e7a0182cb8e97ed8eee452e27469a1a270a11ed5538eba0a08635f95dcffb210d4573476ab15e93d0f69e45ede4b9bf4be56f923a3f5643852928818bdda45f9979c2ce695df04750936a2b2f7d49ef84de87ad9c9048aaa0cf52f2365a2309d8af313d656b8dfe1e3e937c7a7617257e1e8deb75c03a18af0856d860f55ecb78ac42fa0f0e1bc322ef94e3b1879837fc26f62b1f4c639dfed836e1954afc3c66f1c1174479a2606212c6476723da80be00e7834e05d568c09fd90334da5ca7b48350c2e1674a71d50457d3b5276628e6b9454348494ea18f343d6ae6a087d704d9c0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fcad654edb79ef8a4d0cb8bbeca6b5c9f135c0df3e4738b913d96db07d3bab2b","proof":"26e26f694a12fa010b67bf3b9ec588138af37218fc413f411d8aaa4589c925665ec6ec9b793a2bf00c41dd63238756aa57f4490725b115f99564f4cacf817b199a30fabdd4a086b19589e4baee88916edb7d9f0224815116fd1a4d5826f2e05fea2bf61e45d021d4583b70dcd9b94c1764a2781e1b611053c81cf818fc06a066a4b9ba0b731693136003e7385e162abcd15aaba35e9fd0e31994a47714315806450519ef95ee2e55bc4d746b457add51dd04613b972f6e3315eecc05a1d6370d8663571f125d7fa0c2d5cf3560c87603df432d980087e206d3b7358532275f043adb4bc9c00da0dfedd4390895fc649bf37b9982a623e6dd30e0fad5d7594441da38c5f4fe9314e4661b3661d865f8a4e5e7dfa64127f68b2b0a45eb0bcd964ed2337f05a94a464c60ac9624464df2bcb2967a05ed107806e3d51e32f0155d522cb2fb75b762bc0a9b3553c50dd5257a4e3dc2e3a072c00730d0e9362177b543d2cb1ed7f82d4ffa73b9a62d451041b86c09ece8679e0e2edd977f7993039434967466e4871414b5bfb966e6ea88488d9aa0649736c96142d895553e7947d74c5485e25c24782cb8813b18184beaa4f51ade950200aaf5ce66caec8e0a3b3204acb9c07ea54a7f5e5e68ac2107695faf5a4ece459778f8e88019a6be6a710c492e424006391e9404414256c0cf279fbc61cb2c45014c47ec3ec7779e42b4c50b3890113a65cd5f8061a468afd80a08ce3dfa28fb799f19611281e8d154bd522aa4afe7f5d1a7b112fed9b4d75a15ca40f261c0578ab57bb48e45e1e85bf1cb7c740e6f201e66e6c699fee275f279f6a2227c7c0fb644c6d0e66d74651b36a16a8f6d6668aaf211d3ac60db50c1632cb8d5d05af1900780d110a66230af3e0b0de8ef578b6d772bdfc3f1d3d6cc174117a15e5e8822b73237fc0a4492de17a302"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8e2a053479f11a59973c6b74988d925ef70ee70466e82e0d452da3eeaf7de95a","proof":"740468dfbfabacabd7e7685089400e2a570b22684e652dbc4a9a72c60473586c80224315eda7211619b999e31f6f4c3500ab4d88dc48d120c29eedf0b8c49402581e3cd1f68cffeae9a30e05fdd5280274000ddd4b75bc8bea1ed472c512e83432ef897e0acb842e48c614717e33a5832bb28c7346043bd1016c13991200f11fe0782135f47b793d7e4d5bc035c081e665ed4e4c8ca4645a90d97175dffb0009c92e3c3065b68be3b975277a6ba4df23809272477acdc4e0c6a451b32abb15061adbacf93dcf447de06d791939c41f05badf91383406e894edb896e5ba836d09c02e1ac78ad5962244f81d563eb21c61a0fa40fd4789a8b78950943a61b4da533844999b8a5dbc5193a691fbd18363aa39f2faaadd280dad146d4628eecea04f7483948219758dd1d856aa8132f1255f90ef1c1bc03fb045acee86d5a210b7289a2e865f07e76b5d150dd2b8e849ab4f04286a40f154c153b52177d1af816e0c40d6709347f6fc62de8d58d0f2dc11fb7e1551115762be0d41ba3c4d7b0959313c2eb3b1eb9d8e21a8bb2e4912310886d578df059af97155e3c208a6b9b5c735fe6179f0d4ea6e6875617244982bcd17dd1f2807048bf1d9e9f6bc7d1292846788769aec8801abdf7e40499ba7b7df4b18d8e3d240ca4561064d22a5b9ba514a4aacdaef8aed1fcfb47cdbb62780cf48168b5d9d0141413bcd5c60d1a1a86369c0e5b0fd543c7dce7d5eeaf28dbfeb3d1fd5e6727b130711f54f77e7aebd4d0f78af363a1149ef5017906e687ba5a14aee089448ec108fcc5792b66e1267cc239443462190411cabb56852c098e09f3eb5cd5d3fa0a1750fad1bb04993d7c95ee58c5bdb8af936c32b32c738f202e4505f769c52f8141a7c7914ec7173c71200406cbb3107666fd9817c4a0755d141d538efc3784d97a72e71799c272abe3e0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7c13dcb875c35bb1e9dff6de4050ba4cefc51cd2ce33d451fb1b37aab3d80121","proof":"f4b067f4f4dd685c27f5e10892a1edffe933090adeb4d86a1e016ba222ce855e2edad1f609e50c72baea9c9611c4f9609f02b6ab8d5f84379204a01e4e4ab14df683ec36d0e94fc0903ab75b4feb3fd5f80f3d8cc4c4ed65733b10547e6c9462a01147406c9271b016b84a25d0e001a8e58349885b759e936f7809b698b2ba67f04701ec123240d27bd902c0bd444ac5791fbdaea453189d2e360d14c84a3c0d0d218e56a967a151a799f99dc8b5e1349d9261d8c3976ee2413813a197ddc7065d7fe636f6c4a976b580cee56cd976ff3f40c1a8aec2e119596e1ce1bdda1b05d8087e1edbc48ff1d6fb9e457ac44c6384531ab78c7dfa6bb5dc80ae2ded9055b62f2e98a6b6275ccd4626c860cc95907a7cefd79aa8aba2d8cbc33f0ee01f43e46946e17ec657cbdefac99c4e8272cbb18a541018f27d1686074fd8d41c4c4e0e98c582025d8c33b386cdb698724773999838d9bbaffc0f8a82d7b27158c2769ebb0c38a9c9280cc5690e2000f3d35b8e3c8f692fa3c4650a0da9f2e582bf555c4c054c2414694de93e06331e9d1580964abf7e57d82ba8013770f1e9e8305f7ccf07fb5874ab1e68f9fa8afaa7b365fb05d4af0dfc852990ba2a486df1334e3ec4ad4f13cd74dfdf25a4fcb5cd5bc9069b274872406937b60669584bd8731cca43443673505f598aee6464a7070df745cfa7e8d2f6bb35b747b2c203f4dc03f63ee6a5befb35b32aa2b212c22b58837659b7c0ceced906ed91ed7fc5789b34b453857767514cfddeda1cd224241527a44beea987794e0c98a60be4e903265f08710df4edbc125f966856258fe9b1ca081f9c7a8ffc372b63bd573ab805c729cc33e12e6fdf3eaa29249cbd2d1702b201fe84a6ecc53125d505243faa8e39086ea3f4201571c8a008dbe2f00ba465827e5fd65e2cca6328107b2815b495f806"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1a7a1e99ec91bf928afb5e00104a1599f49d0a84b4aa537a697ac96f7bc2164e","proof":"622555d4ad369a81eaedd744a24a130f6200ee0c9c28078f652b433622ba9040e2e5acc2dec0d2bfc07c084a796402a6428cb3c6c0531173cf9516003ab44e0070f54299446a350039adbee765cfb184aa05b04e83ba42fafee7fb509bc34d2e70883ff422561839c0ec53b6a43efbe1344d786dc606bbb9d088b58fce969f2aa30a50995fa160435bd71a1a7aa300bcebc9368b5f25fe54d0e5c526fc348208c52053f5f3a755996a5bfc18ee08160131220acb5e560fc20ea5601f5a73d9062cacb52e865c0b3796607d5aa0a98e73540a86fa1f5ea00a257d3deb94834203d631f974d7451efdc94e565aba830d2be0305ee42f4ddb31daeb09019a4330216034665b8534945dae80ef63b5e6d2e89b231b7fc33e74a6b217628a0082e8351a396e93578cd3cefbbc69552ba2d40f1a100b68c38455f194adfa2d4b36cb1b7861adf8da7b794f80c40f4fc5d49b2532a1b37b813a07bbb804d3e3545b2b4f0006ba0795cf00309e2c5efa65dfc108a342a28a8c31227d595430eb3e94993d860b873ace5223135a03a85172cd5ae9195797a812c00910cfafb927d9679a2b6c4c002db6d083e452f7c01ff3437dd11ad202fc90758ffb1adef0e05726fc27908178cadbe83de06035f2ff46ae98a89671960ba1b329ca07c329a9ad2ed46f3ae537bfddddab43e585697a9874451b8ddea51cfb270ad551ad2ad4bbd4424a5adf9b393b5ec604c956f43ad07235047ea6ea9e89b08f3255aaf7a45ce27b3322934ff4d905ef1b445c230e1e52f4d14577750e564734052d46241ab653a15644fc03ceb2c789b59bc260f7a15ffc70f300f4074c10aec104a01ffc2105fb59b471a2b454692edd1ee5b8e608a9f5c67a32375f22d268032203dd54af733601b586852dd2b6763d6311122a72e6216a0b6f4c4db53aae3111d4b05ea255e90f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e0cccf69fc2ab0f8165c447f773b92a4dbe79fbcd051cc9370c97724ea87754a","proof":"3cdeecb60aa5aa3022888ea9604fdc036239348e249fae22d6e12fddbd0b44422eebf5bd0296371a3e5e160ea1edec91069398090ee88d401b9b29b6d1b1ee2738bcfdd9c17e8dbbdeeec5c07b2cc5c840240ca6095137b67f6351be870a4e5e6c18ca6dbfccd516e558b0b5662ddee790eb7ef548e4aa9dbd40b13b9a313d52b150f7b7585cdd79323c1128e1d47adf513eeb3d60727ae495eb8e1efb5212026a0a63dbafd9f597d39c7e0d81c17a1bcfb81ad5b4532d322fb2118c535dc106bf6553f3e4468ebf550fdfe01aa1bb3996939f19690a4bc3fff531e78276020228ce498bf2643d73e5ef3e96c7ac9c4f8a5c5a01f59eed16e75d052ead91257ea04805d959ca4d8cb58b5c663b515954b1f6c33d61f2ac19228eac3e208b094de67ee3b4084effa4ec7d2c6f7e182ece138f40ddf0dceceee5126e1485b00150b48dfe4ce25dcb33cf02f4a64aa0ed7e10ed878157f72dad79894f9223c14b5da2d6f26b27e114938595c93432c64705c7fc03b67386c6dcb7ce00b21a6e620462a387cc31bc834b3962351b8c21b29844a8ee17b48b35397f5a95dbc0b20d76d0016552436e2023501accb08cac17662e6079e5ffe4e1862e07b5341cbb33787005f19a586ec799e42f3f2220627e04dc956f44daa809a271443b9d72583c7e1ed7c2e64704d561a2c2e85c93f4ba8c87c025b7e93efcd73dd992c058f3172df822b3219e1a801c740d88041fbaffe73198dc9ae0afcc20ac4438a734aaa4294a1beea658ceaff19cfcd22e2e94c2e7a5289ed98cf1c848d7477eafad270d724ef6caa26e227ef5ae06aa1ae7ee15cb4faf86bea1d9f8f0379344c56dfa2b7fd6ec15715047b647f4a3173d4ef4da3f6ca8807857d9908f5c0fc2742035930e111c759eb97d9301abd886cf02424c3df152827fd3de5143c612c0f3dae3ff0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e09f761c7007ca2380057ca8781ec32376ee4ddb5981542dc6c1386234938c3b","proof":"526e2c1cdf986314ff657228a370b7821c4f5cbc1bd63d424612541bd9b24b3612dfdccfed9ff690bc0dbd7ff0bd69d1231cfff077f4889b521bcc25dcecd4417a8f5374f821c7973bb0bfece91d4f65ae1610e6cf1b1b3d63ecc2c48adb7544fedca7e7e653eb805ea93a053ffa4fe89436f97b61c13fb51e3352aba4e1b37f39974f0d64caf522cf2409253cb3255345fe90e2de6a035cd3aa0d821ad6ca054259b663018733736f8711edef0d085415b42ff342e44840e10055d58aab8209fbd5c283852ef58c7f70181c17d863593505f22c2fdc897de2098970c54ef80bde3b7d1c696f1ab9a349f41c2454b6488582aa48923b7076494facf6d22a6333fe71ceb5a67f370615f025efc290aa7f5834b8fba632d8c3c61419885783ab32ec593e7e2d4a72f0fe8eb1c9721be4275f6074ddf441d95f0238f16a45e6893848bebdbcbeb5ed4f66b874311ac852c935ad580541deb3cb2372a7872023610ea6b90a3b43ccef65cf53d29b37c4ad15d4bd40324d91e55d99749e2e84567a046ab639e9030d62e43762a2813cd2951ac6f5713e05c2d28730f4b18ba2980e48aaf94ed5b1baa29fc719ab90962389bf0784ac1375438589a582127610362a41a2da013e490493a6337e86f01328b23ae684497d562c339e88ec7687ddcf3143f8469277c2a01fe3c180b11e877dcfb0b776de68942f2b97a387224e63e09c6d6855fd5f0b5e7853639e0475b9a74a1aeb00278325c15b022562773d03c2c810800a203a4e55a29893a046465dcf07ac9af411d55dbe727671f11fcd171c9c60d22cf9041eb6fb1c207042eeaaf552717f4a4ac129a04394b1e2e7564877172e0509bf712b5c45b50cb01238bd4fa5058780809c6ba95caf1902a10fed005b08d49112d38fa31123a688880b26f208f14d8f8a262ccbdea0869836757ac12008"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8436762f25ffebd6cdf9097263ff7648017f837c62c1d3be43f5265d496dd54c","proof":"f2fa3d2c17567a133dcd03b26d3b23338e732b79d6ff93e46fdca093d18a3d04d4312b4e768e4886f4509377ee981649ebcf064fccff5b44aa3019f9cf24197f0a940920d5ebe71089c65a4a34b7c41465b7250e6307a2cf51e5758f928df056589928f8143b4322d156096fb7e98b9fa9bcc98867161bc48ab245b96ab0a7441abdb50b727446b2c90c420151459e0207ab0bf9b58e67f67409a89958e88c0a8afaa9b2b40f31bb6b862bc85ca312ea40f2516ba8c83f30118e740052b44b0c5c311a2ec4368b20df1e885e3646691955b896a2f6c9a1c95c707fa52de7f3043015ba10c6adbd66ccd833ff669713eb32fd0e90718f26f326fef0d3d9e82c1ef04b7d1661c3703674aad6c11145a34ac181e41071e1a384fec7f2b378b14f32ce5652241875080f309fd611fc8fe8e170e283dbb32ba488d3a86706b610544948f2a4e42edb77d2c431e6a6feb500529880558e5437c0a344561b87f2b8630f6ce651f1debe634b81f246180edcf7350dbf0aa0bf00d4bdf1710bea4e0594387aae7db3e0ff5b4c780d6be1d60579b113da8df02c78c7017b271fd6148f0452debd2c6fa822438759fea4569e74bb7231b22fc6f21d3687912ad4bec550c50fb6f5a3be18278da5f039d158da6245940ae48174c2af292cb6f45c99abe443279a1593ff6ba7f8dda0b41a96f5df103073a1ecc1476fb54165d4c5689731211994d6eb0dd1121ac7a1127b130cc075ab193478fe66098169841875eb2687f477c2cd2d9ff4a6307c34a90be15e73ad953353305f128a8ad8e3fcf89ffe145d22584e6938645603208d458d1b235411e13cebb866286159e4d3eaf52a18c09e4f5da743af997f5514bca1c5c8d7a506b0e70aebed74308437f4b3e78a990c8a040cd12efd1ab8dd68cb90bf9092c379c1aa9cc82cc11576196ba64f813d588a03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8c63d9c0fe599f3734a25028c04a07f0dab4477f0c8a7cbdebc334e8b5185927","proof":"d0b897b45e5350d558c048f86c69a6223efe968ae1466870aacf4551c3feae6fa8306fc8efc285616f15505ddd79c3a693700c74adc35413f3843ee51b89267d2ebe85370bbf42fd907dbfa613352028aa74b6a51204b49a88edd387c62de94d24166dc3ec264eb8a81e45f16c2a4b09b49549210a17195ab3205fae4769c82cdcf287383918ac36eb0621141f614863e76999d648f8791480fc6515c913a10f3e4e4f2d5a93f40eaeb70f4e86f4e004716733f3f194f76f3fb2f32c053d0a03f425bc7f3dd0ea2ecc20b2133e0f6c3b66eb4475e41037b9afa0572c52223607f66d23bd7435938138c4873e2cb9c0cfbc2baab11b80c60434b0336e3e04360cf61911a64f45db7c3f4a53a76eac5639d2f0218394493f8151bbe9639d99bd6a684710ab831efb5b6e1a6d03e62a772d5e9c0d049af37e98355028d13eea17142000044bc28a9e321852e7ac39e71842d5d1881c142247f8bc32553bb27dce00907d284d6be74c767ce1254929688fe69f9621101c0e6df881de003c73d0f413f2d40089394aa43580a42a364862cb4ece18b66bd8cf1de6fde092f95888a103bef982987ab891e39935365e7121c77a974ef55142e089a8a1b3527c5a025300643d399cdc1b094ec7d652bd780c296c6d0f8814fa574e90c6741a5632719c501e2648c6ad3ffd01fb77b6f27da99be9b93bd45c85a56044c873477e9f0fc53deab2ae6d1371e555d7427311aa4374e1c1f8273d06fae9601d6d570166acc175ea0d3b369c941295e2da2c7e75215a8f58caf5ef764de50519833087a14fdb70e0a73dd22ea177b7e9a2393a0ed045113cbdb4481f8e8ac1085aebb24bfb9f68dbe9d4972e9a7c005145200c264cba29f29d8f2d53f504907985def6caeafb0120c39074aadb4d274f244091227df6da542fe37fae0ccd772a6559453e886b0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aa58ecf1aac2527a948d803b03bdba1e66b9312a7a4898b6c1bbf307c634a00d","proof":"7494a4ad4a341cb4941feda85f9b54b2c20d0f19b36d083d4372cbba0043026f06132420192f45aec5babfd57d9b54f603bd03d2b7bc03ed39d2bdab2ead8b3e1a000d10318d7a564663d79fa5faafdb01fa3ebc9351e72acddbf2af534b2a1bf26eca082bd13a484f26e757c2c43a398a625d78d78469b76431945f5488d00233de0b260f41385f8e8ec988445b4e5372a599b478ea2866d558f179991b2c0c6d31764b15581304092d8257523f65a5c1063ef25c878cb8b9438c022911cd030dc6657dd530bc063a6fa8dfaa2de14d5d04fd77d8d4df3972b181582771970018a8a1192064687b1eac20570b457f044fed21cc30cb3183061f164b5026cf35a24c5b17bc66611f0faa0c1753b9f20dba3a39b6527195eb12ca5e0f58e18b492e2a3dc85067da26e379744f6c3165fe9369234b301523527bae12e107424e0eb4ea3c406eaf2dba1e9c876001a84335ebffc5c1ba2e06acd0332408e5098e0c6adaf6d45330dcfe9ac9c3c8238bf4de0062ae357923c40f9ea61585e3f3944416f4905a20739054df9058af7e402f70138d28b5529ca94014cab7300230e45486370306fd43366223d5264c10a5bb3f66d2bcd58d0e74364f1424af8b0c0873ba8d6ef71e9ddbf62d1c048030a55851ba6c19edb7d1d2b58c930a39ce43ff4d1e58eceb0e0dd54d1d2a3fe4ab8efd4f7f0dc0c31ff69ab958922c118472740f00fe1209b645b895202bcc8a964e5d79e47de8c327620cece3bfbddaccca3e04b4628ba4c32b93d6459881a29c433906347d7570fe2ee63310d20bbd70bfd01ca8f992eebd2ce926e34e6af7aae847ae0de47d99f8a4ef04ccb2eb29bac86f2ca041aa6d0fd17c89bab4d774f31d0fd2abc8abffbe92bb1a4452f44c6a114a0a18bee992a2480d42fa0c98fed98a310a15540e7547231c5fb278906b78f92e07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b4efcb832fbe6f433a2ed6fd180b4d591bd0ff73a3d47d4f40fda59d1ad78164","proof":"8800d36cc0d183042a038a334426d79a9e5b6c776e7455911a681984f8e8e027d44649f9d2c8962b1fe175e76152e7e9f40014b500ce16d3801f56f2f8de3b1ae69215d7e2ff84a977fa5b03eb378897a26f263b76aec300c7ed789e6c06cb1594f92aa12af67e64e0a97ba1e408b8bf70c6f0c4ce5661154609809e7d47200498ad92cda510057d820e084e7f41975182bc1d31994c682794777e5bf1f2850bb208f1a1abc2b6b17ea45bc772aec9bd96d963d4dc1c65aca990df032e2435081c48f389862f722a10c49c71c89adf87a0ee7c22c1df02c7e50c9c42f993030746e85f1c660ac671b3bc1510dc9c6dceb6975b1e00f0cc97ba6ffa4f65cec0112614814d4f544c2ba17a9db29f46d8d1cf55700036fe01528a2f3065bb939463f2d7c425261f5d315f5ae1d2effd1ddd1365532be408264f3aac689aca83fa44b427a339086addd824742060f643e7576cbc54101eddf6ffb29420bb4cab795ffe6499bab1b593932750da53751656fd8135164f3215fa32f88dda73e44cf569c8b6b5e1118e6f37aa1408722a1855bb1270038f199df92a7a32768ec0c204410033e52af514e872dc54be1d90e5da77202cc2006bbe6b2b9cf96859f0f4c61874d446b956aa4086025d441bf4a05474cea062cdfa1935be953493102e5ffc18e0cb1c476e81d9557d025c289ef9456902721225b00101a9353de7ed82898463da4030bd026324ab3a4ea9c99a2a138ef2fb42743311779c03eaee384e249a67c4778f6c9cb03e6cad4100394b6531593547dc54bb06c81a9e36b8a4b835f740c6779a417ac016126452389997a39727d0f065fdb312a9049b6f8efe2ccc7700c441504209cfdc8795e1165f008707363a3123996c14d8ab294d7ee2559b050511be5f9ce2df4a572a36225c819a3608a71072d1492b2c821efc94108c65580f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ae2af14805ee5d07acecca3e42ae536e44a70c85007413d5b6944519f46ec21f","proof":"f8182cf006cf41e1253cf19eac9eefd7c8b0a888ad05459b0dce9ee71a2e024e045ca0b43290fd5d8d7b43e16c0d104b03192e21e35fc5866adac98f4c843b1576d1fd1e200c9c7a15bab96a3274b128409d34bbdc23c8f4950b689e70a83435386c31d6060813a49b1999454c1874f8477e85e1aed5661d9c141fe37bed98117a82dae870839f3a69be02e6f93d7c22cfde50fcc0199d3ca7b313c3fe30bb0f27a99072b1999df4a3735b19bc1130729665e4a991a31dab00a2f4b472df1c0c490b76eb303e0ad189200b09c52176336dc07c05bce8642fb147ffcd0dc6d3081ab97a7dc3f2d6d10665d770e408455fb4cb9595f7d7b5652a5c8c53b76d7550d0c10ef77be4766151a006f81770625b21517d32930e3384a5ccbd9e6b29512bace9679db320da4faf03e41bfeabd8bb3c34f0a51c050449588adac3997003534eeff70e9290a39a0d8b2c1cfd2680114fbad1303d024dab8a63250dc8b5b93c58f57fadde5aff5fb0f6e2f263c4e73c54112fb7067b53306589baf3694a07677c32a83d6257bf14214dd99487f534471fdfbe24b71fcca6e07a5c466930702804a75ef67a16f307b5b08bd5b33cb7c41384c4d8f61aa9bb8790d611866a724aaae7205c9ecb22145f092086a24dfc89d256351d435d2e0b14eab2049fb713625c7ec4ce8f46534ab9875d37215888925fde616fdb53df2fe565c11280bb723a300a07a0ae519a66f5d46125b63db330e090218398dee9c7bdf872a795be4312b83975e22450973340d133f089abb1a714c170991ce976110413c502795756129c9995bb2541b91bee755367345bac95159eb4bb676ce6d3f4101410dd9f8f59b4b6c3d83c39dd0b9b766d2dd2ef3e62a4bcbad2e2f94a7a032e9cd2ac0bda064374c8967d83965dc7ed4db37f78477439120b5924be26ab001d08d9e50b7108"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3a3eed4ce8e48931578208cd3855b6325c6b742fd089d03e38bc4b73fe449d54","proof":"4809dc0b93c988c2eff9655e0ffc2fd77f0f08a5ccc085e8c6f4d5c9ae90dc1d6820fc1fffb658ed67dcd47a74c2925e9f6f825b4daa89de1fb8004eebee023f28637cea712799f9bb20f89f0f06c398cbc7e207d4739067262ab297018e5a4c762bbbc6b39a8d937711920aedb5dc26fb94d886966ad096a8b36c9331001a43fa17178c5adfca60ee7469ed849aa1f6671795bfcb62240d847546472a92950a72622ccd0b3a927a36bb9f195173ed601a55a7b2d0fda8b05ea3c4d07f126804737b91ce61eb15cd4a0e346afdc354bdca67443564736d598030545fd73e910c68630d2251adcec6753e693657c252029f4b08e66300cd5365f0389eb23c926a282808fd592793069f2458243f12fccb62984bd8d3c55a9604e393849e2a076a4ea578b06484d73ac62b54f8ca004ecc30a143a38b5d9b8421607e8e53ec0058ea719aa48f83aa8899564a2c26a508d0da23bc10c51ed7471f6d5bb9359f2315784475064d61ff70349ec146c3c0dc4d5a4cfdc3b59dbce59a01b8941c17ba3226caa7f830d1c78a705f4b7067bfb163522aeeeb2a8dfb4839b129a4c523e278a8629237a25c6a631df06b7905365df4898ad74adc7942375a4bf7e829cab96c4afad1c5f1095eed6de5f0505babcb29f8b340d8d71d89927e1753f4c8c650394cd98b13513eebc73e19c73a99c8aef0a26ac0b3786fafba345618f74b1d17762efa835988bc999e309b93513491b269c8eabeabaecd5905b5b32335e55d25365e7ff7c847d210c651362b5e3fc3965271711ea25ac984be07b70687b33f601e2ed45b6f7d8ae39558affdb888d1dcde7cc3a88fd2e724144e615dec4f14b6644995415bb0973120604f12717fb04a5c7611364be91c9282bed64b6bfba088061073e2c1323f1f15ed743974a77c1db3528cb16021efe424cbb32f3bd0e84406"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"126ca883764e14d8ddd678acfd5c7272d28b8e369b4d0a68025a6d275f2e7b0c","proof":"52ca556a4074844fe8cbf07d41f842085cd8849f6d7aa6346c45c82e5ddc85014e1a7d79ab5b262a838470aaa83a734c439aff0193909076e7a2244e59592360368fec2552a28726d839abf4b0b27bd2971c8befc983f0ea1e1cd6156c221974fc077bf9a719acd302cc9f8048e4777e80082b50f4e12b135c53b7e65f6c967b77fd936e528998d728061c762727ea295a5123d44d159d9708e00bdffe78d601075b158627ccd437bf53980c845d0cf21dfc09833417eae473252be454036d046109943608c5a06e3cc95e4d154072d80d71893e0e8db0f8c520a23c4732e00ad282714116c21ca4e3c4ae50029b855006799f574b9cd89b73856344585dba7402a595b9b7440407d82348fb14e91140c1246e70bf5f437751b6b398a5b4a57130fe993122ec95a8348a76f39edea3932bb537f6fa1b5635d2b64c6386803f7e645fb5b5005022cc253832e51ae419ce4eea200a6c881ce8da8eb42948a60c55fabd2f85c95441e48b2e5bf3f3107402e432707dcde4b1b01e3e726c84f44663e6a3496996f8cd91be464b7fb1723dd7435ba36c13b7dd1ea00fc4c7b3a110204ad187dd58e27decf4d99ac3b5dab1c286b0eb5dcf09a23717c8d884b86d3b16c88100ba33d4c62c4c50f4b87f0848008eda4f39593e100c0d18d251ff2fd174589b8d7dbd86e31553505f114a43451bcd112d2dead3fc1b754a67fcbb11405b54fc13be6cc660e28c28e5e74ad3858f3153017a0e078fd23d4cca6faf3e6f27962c16e3112b6ec05b3bbde53f712750830ade91f64a07d44f9dce67e0e87632e64e02ab493ea00edeb46e072a2565b28c078097c02b479f0f22288e18de5d68ef9bb846443d0b4f446ee703b1b8a9d8961f455783a017b0a0d7923addf87901607bbe918e0aadfdb7de5f99f1dde98398ab8d48fbe66d4317fb3f53fe121408"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9a4ec502780b5ac89f7a5ac830765521bd8233911e53d35d713f684991be7c2f","proof":"ec7ecd161d9c8875cbae1b447fac9c865642c51abd99e5658f43be9c7bdfb3215467145088b2f4fd0be80369b23a66cdae51a7ddb4226c1a188527cd5ed39b74745a56609b2e7d7cd6599a6cd7bac54c774b7198118290eaac565ac250208426546b1850d663294be82a54b9f3b91ce1b8dc13d301a3e3e8c82827527d16436dee749561981748df58b32bd8b5e258117ba8ab2333fbf65c36fc8cd850c3dd04282e05d74c46fc679a6998e02fa1736a63e681c01a7a6ea942ed9bcb5a122206fd86b3a758a62b7b44252e2b4e24bbe77d83b5a0ea776e5b8d8c1c73ab70350dbafdd8d2d17c8d56e49bedc6347524d99812586a7a3d39afca3d2194619b5e08daaafd40f21b79d03bb2dad27341764632bd0ef6f1cfe4e786b5389981c0144f7a2d2fdd9cdeef3fa37b3fc531061c763598a955efd5c4c33eeb507381ecb83412471679ae10ae846657c7d56b58956d9af949f6eeb89198d3a4cff0e9f5386a5657bdaf6151e8f17fe01e968794715e92aa7013ad678680a2f086f8cba27a38f2f82c9960008b66643c49a5c39de494206f8f4808a05c68c720d44f8b30017ba83be4e9eb0f0706f7420344b88f13dd70826a931ab90519f1c597c29fbda638f0bea9b4a5ce02bd6e26deed998fb19fb21f0605b58fae7466fccf3b2822451d8235a1234e9cabd171691f2901309c98a22b1a84d9a935289bc0d8a7d2f9d07240839e444d14f809a816df829560972af138bbfe02bc0e3f9c7e1cff98edf37d7a771abb1e82f70e367ba64e416bd219f3feb5fd1ccd6e0b64d90833b3114b034c47c07526fbf3361638e68df4b282421adc98bb8de642561c25586b3b3a97735b520b2449ec913c8a6faa6e718b7d0b9b91b2f7171d32d42ff202c356ef8d0ca2f5d5646ac41ef6a978c993a75c90ed38a58e2be6be31de03ce5962d78c5f07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9c97488e7f17ef710b04e43713e42e1896571cc45dc796ba7ca2859b8ef70a43","proof":"d23123175e18f34955742e92c618bc57778bb9080423965a1f4a26168d13177e7a76d1f572236fd8d58cd1e454a48eb9cc1a11aa286f01895bf5511e52d40a3cc0d24dae6e3336514aec84c999de3b756f07b974bd3f501ea38fbdef28634d39e0d3a64957f2942ce69c6d037df93210d6a70e27cd781df2db5068fcbd68ef1eca8d6be96bb814f72ce44ed01c5e9e2255621907b92ffc42f5ebe126e7a9120b531a09ec0a4343f7ac01fc76f14a7be48a2471f8ce4b52e79d3c926a4ccc6200f596bddcb48cb8a7d62940d75ef88ec57193d71a2cba770c491c99a748af3f031cc67af2d50cf9ed9f80e0760596b5951160443dd6ab0968a02e985dcbf97d2ea42911add8ed43043d89e5486fece4f7836ea7ac7c62b4b8e82e41fe453ab12c3a2d5fd628c057c719ccc0ee8edd472c6c4ffbac716f5840728ee263691aac7f70bec01d5e4428e022b1b53cbf1177ef489ae35a817a7715e9bd79e27408e21cd0cc38ee4a0ade5b1d5b2df41e0781adfef6df98547cba140b3564a4ade86041ee90a568b3d263bdb613ea534b79fb48f659a3df3d0876430e44d274cf9e6016629872ae1ffaf45ccc98e29b223aeaa5e063ede9a6a4914b387d6dd455e9ff539c48065094c9c127f24263291b824175157d81bcc30c6711fedc05f1408839415e60d7d516a3da73538aa6fbcb23129727e67bcf075223dd2db8d78c01a33b14e4b68876c0aec2604280fd8c30ad15295b9f716239f4c8cd91f1ada8bd8af27d6cae281ac3ff6964535d443e031047e3972991133491a19da8e24ef97aec244490d5b90266d002e4f95980922b80e7a0f0bddc1b781d4128e48b54c225ea2b3876be89f365f020fd2005ff7a985341007e7403aa9aa5e436436c5e51357f7c0adedc876c4da272e5bc4625b6e497caf88791cd6f2ab67e222ed3a43a2061e90f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"90d02ddaafa9e105a4a9f02a601dd08ca4f78a819aff5e27067e29722d933943","proof":"5c11fafe759868d82da47329551df758f84be15e9982460405cd2d2f3d2d8e302206020945ec4f8640e91ac1863eb6a325a5a66513a78b8a9468b66b3e6ac42752b59dd3c4f52641d5319f6f256e066f52e42192a5666fee4b2354c8a18bdf6b60618018b7ce46bb2e2b2fef59ab8327e3035d78a4c981c8714c8501f2d1bc7d6bc1362816fc8aa6d7cb0cac7d2d43754426baee6227c2d23d524ce7748fe00388ef8b0bb0bdcfa145ee0c33d1021c693c6141993fbb57c91e5739c232d4c4088ad35bfda49020abc3ad647b197d01a32b7f51a25d50642c2906e9d801b49507004f1d99c63132edff7106848473aa99f0664aad45d445844d6738f687e09e3b50548050a3b6eda436f109e86cfa46110a1cb2eee675f9421c04106651e4d51e9226165cafa31f2e2fa6b57ad1e7fd333ab0c1cbf70dfe6f64a11f08f481190c52e6a8881cc210357acd205561afd90f0583a094c64397436f94a15e77cdea0942a6f872c08d19b5f238b5b9c345a514d8eac2acebfca86ad628b55734a63749a8087ed48dbcab79ade93de5009008e39bd70ce1dbf74b980059999812308d52fa386b91054e9a91ee02f701c57cee75fa34846c3c551300e5eb4774fb4022776c35c95a47cbd96f2cb0f2439638b8da0e8a95b27ce36b875f07225996ed0d2a3a30efa537de4ef78415514e348a70ae5c11db3b9cdb3b9599ca00d15405c12506eead302912a2ea4b5cf9248ac8b8bdf910724e5e7ae4dfb748a46ecb1a4f6e961b2796c9e55af769a86c28387d2f3a289cea61f74255bef1b84005c6a6d61e72a53ab00612c4837b213445f9fde098b0b7d07c66f10aea9a947a5dd0ba8f55338fc7d41ec102f2532d7c711fb02405e715ca3440f696bbc12adde951763008b6f85f7c9888ac2cf0e463cc201a581ddf7494e9e7381001c60cb2a2b64f5b07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"98dc0dd826e3476183fcc4f43967a294cfc43d9a14ea64140576d60eafeb2218","proof":"d2d04c3ed5599ece7cdb5f334909b370af89103cb4cc3837fadaeca4b8d6ed0f80658dfc947976ea0c5669163e23833c975ebe4d7c98859ff6d5b4b52eb2e567f23812e862b7ec1c597c34798c702c1fc4334b87edbee9a9b3017314a3dfd90cdc207726b8403d4be5c0222d12d8f3762f196ab276924cb2438b384419f4433d5e513cd475980cc797b4d6e9e4a2c64eeb674da6968ed06c417d34a23e928d0ba3d25e48c8b299f23da2c93f8b52d18bb0f19fa5bb57a2b983d0fb175dcad50016f0cd3715ce95ec5f3393d967bee5b00b92c16ad7a44ee018595e193ca7840e908cc3cbc4512d77f106e16cdc2e5e62909da6802996b04e452e15b1efc84c578460b93aae1f5965d52e3acdf0e1f01f10e771d440aea18836b019ef2afc8637d2ce25268b13af08b3878a2a5c8267f93fb4b9ebdaccc605009c15287bf0e6726cfaa55a9943a4f84405fc9e49e7d282fc1a96cee49dacafd36c4a1d5d98657ebadc5d827ad63aa7a8e1ab4ba8a29a8e953e5aed0378b7330f4e989a42f3790a2a4a97324240491a0c97c9509616e44c1e5c5edd27f699924cb17b26b347b9743c75791f69fa5499d7c6b07cffb20b5ed62d8c10275d5affa02e4fb58da73069d091b3ef67c93a66cb3d8fdeb8fcf0dd0ada582c6f461839d8cf6af84eb5ab690a657881cefb53f731eb3d6244cca5786ad9378837ac4ad896eb8aff03eaf362e6cf01ef50b49c91814937138892d416afc6b8d39c0156a1387de606bc96af337ea7e7b9974a220693fa74f20e918ffa39802c43e5fa46bcaaeebd86e9a13d359af9df38023cb7f0029c34dc772e95e27baed1c4b27acdc8e1304234995ad579d99bb98d6c1024df5892f4b0d06593272564bf2cd66042f0da09757a1a75c404536e6f2fda1696729fcaebfd03e2ad179780e17aab26527bc9be4a0f40a65c02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b01c45dc294f25f3508a073681fc5692d29c499fb1b9ecb07a312c5bb2f28f1f","proof":"74ef0a907ef2022ee2383e614230086164f47a21a6d4846f3f55600b4115d06ad0b8d3e23e0b03e092155772574b0c46374e372f56a3301f362db93f7f36992902ab631a7c06b8ef60698ea638c5d308d40b234c2dca1334438b01d183f52757ca46013d79181aa4570ae794b56aae7bacd7a160dee6e0992de382fc827f0149853a1ab50fc8d2413086c839b739f092fb68972627ee833940d6bda39462030979ed99b436656e95b1f48be0e446b5ade3325c73296a11c5f94675ad472c6f06f97512b73f1b0f88ed95765bfd7e6236c79b7b7375bd164f362aca06758a8d0e64aebc0a67070ea0b354b88b6c59b31a0634adbe1f5090e2e5952171b2f5d2290e5337aa5a3d781175ef8e5598ee77b7949f8d2bd4bc12f67dfddff8e315756cdc78c297e69eb163e6adb655f348ece63334f1d4ced8a037404efdeb5f453267a6376e8777e2e2cff49b6d2f31e84b645f93839a91c8c64c57847386a1385d2a809019f0e7476c5a2730490ac8669be7a20213630c8064c8d03d69f3c928fe766c3c4baa67cc70af7351d95288897fa7374c14b00a324f35bb54e3ba4bf0cd4d4c760da047d4cf757cd62e496492e44d4b4895f27c988f58f98487636059401eb0bedc026765d8b634a4c670a4b749f0af66bf2e68410722f670980f534e144b7ecd91a64c3c7beb8a2dc597d64bd2478a3372a735e3d01c97431d7c6911492a9cca66bed406a616672fef30a28ca06dafaa73066ffc41adc9f4ccc0b1cd5164fe4e5b7dccb16c84052573c3b8ca17ab148273b3475670a1d63b817757197229da51a8e1f32696f3c02852e07fd26a8ec46b0aacc7c62df2d531192a380ec635a58ff2125af3096e77617e54d5b311fe995dae22300046bb501741fac9707a0ca34ec1e0de2af9a9f8bedb0123ca17bffec48bc27d317ec7bd18d8eeb3b70907"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"60f5b755f09dbf4ae44d06885d015bd639fe371c88cc7ce8a17c4ff10cdb9632","proof":"02f79606146908110d66b3612408a45e645bac45add19f4067da0f10d19b5d4c6e7254df7e2cc5b312955c70e48e7a4df29c3e2b1ab470933e0e37ada5c7962a34e0bd92cccb54adfae216bce07f0ec57e3142055d2b2426492816cbf49e9332049f45c60e9464c8356b233a79c9b903666e817ad8b7d73ea1803af38fe0567bbe6bb1fc98afe8893d57c1706ade4875d9b6a83e5a95555d4530d7817a4f840b85c4d211bd27c9b4a21568a871f00bf64fa90f6f41c5c1065537d39d73b9e509983f441afbd74f3497dd3f75f10d10b100963911edcdd2942d1ea245d8b3c30bfc4e2d3918cf554321e77d6f1502eba1e56e3e7ab05dd53687a1a6f039271574fe957b3921eca32bf50005480f97cf6c7e8d120a678a159f411a547e67fd18648ef6bf54f94a1c56646954f9042ad21192f630a35cd01c7c1a37134af53430440e2fe60c1c2837068731660a120d2498ffbda5fb78f294ff1aaa6f93bead5730522ba2a0bf73d0f06a7f4c67ff3ac66dc385df68707e73baead0e9070e8bff47e86b922fc610defe2b47c65484dd3ca406051e4cbb39739ed1a223e2ce075e2c589d6195a9cb9fb3655a786eb6f2da04042ea2ff0c4c8f2315b422be61c6fe11c0810c8f17f62ce828094b9b0a62308fa47b1fe62e40a563048af5307e96d32bf6ca494b7384cb83e822972e57c43f2ae753f77f14bcccb35a6282c89ac8b4146e6c959433ebadf3cbaa996a4e802d8c07d370e1eea8b61d69c8217c75585978f8e879fbdd6029770a68b55c697bc67e7e277f552d05d2143bfb3615cb260b55e629f8247fc5759656b69c428760a9a8a68ac6c65c0231c6291389f5951b301d8918ea18447dc04534a12a01f5930dab16210a4277db929283f4d8e326a2a10fbeb8ec8b3b914964f5d8f6a14ba7319cfe90515feee0054f082f272038b80702"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0ed8193f17aacfc844ae1922f771cc35b48c506b7939b6b64fbf5d667d7d9503","proof":"4c2379863608c1da0c8cfa0fbcf7aeb53bc9ed4ab789c7c247b6def21cd1de315459a8443dc3e2bd87d61ba6d10d67fb4fc683169ce959d48bfb13dc8f40237b628b3fef25ee3842046cbdf641547707f236e69985e151f86e7732e00b1a935ae0bcbd3a635ea57cac30123b8e53301a61fe921daa0afca4d2071a2dc499540e63a71f57e2bbf364e2152eb1018ea9d81e11c9fe695eed3fe4efe87ebc4c370a647520431bd11664f79446916cb1947087126f19f0a2fffffac775361854680eca0a7f337ddbd2678f3595c6fa84942229bfda032bb60999843ec4f8ff83620ee45a49e1c96b046d159e236398e77c8acd10b2b6cbdcd57f5905a6e4d06af955dad42d3472d7f7cea70c50a9115490303528dc334edf2a3526e828ebfcb6574f920855e9a46ab523abd3727092dd770b29bbffb406c39614064242d1f9dbc65f12f7bc16c12d3e445d94e1d5eb12f6b37d81aa2914eb61be498a9e4c5ff85c020e35747ab59f2396eff9b1014cc3daef6395cc3d96ba69a8d7e7a3fb4f96fb6aee9626e348c0e871aa3c081b03e8068ca486ccd96d82f6e35c9619bbb797e80d5ca07089edb87a331dfaebed5c102e1693503444a2c8ac4c2ce2762c3d24be34d896eb577eb8edbab45f2f339f05e783bee019fe68e9dfe640d9c8a2bc53b05b708f2988e803580739ce7e2f5a642a3bc07537c6e0a390991df790db74057f7f64b7bd4eeea9ee5b5981ec7bfad8e3f0ea94b78924f089841bed49886d30166cac98a096890a3c5a147417e3d5c8195f247d850b6761660a42bf6fb15b791e2c64263a84b86a8c0a50cba5c0086c1d3e217699c575a7b8f593e40a9ae408367f30397d815b7c364db741b1597e7d0c404dfc4d5752275b4cee3440b3d4f6e20eb12f7b3e6816f42e58e564aa2f0b67c9b8dd0f815c1a4bf37991a9dff9faea0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d4725b21d5636eec5d9aaa8db8c5ca9e1f91186d8e00666588f6216e8b663311","proof":"3a4c0767c425bcfa9478ae520f89ecbbc0517f8a8d347bbefa74f37ce535374ad8abdfc32f224c99b177b1c31713b66833aa1eacb5114d66ffa016cd6c6f4821a050e0c3f21c55dcfe418411f7e370ecaf65573c41895142a43e24e19efab049868cc603da9a008b1c97a8b8c721e2b0c090ccfdeacbfb65b7247973425315758214a207f871c27e0d4fea211fdf8f827f6b9191f19c2e0959d820d9c7dcc00cc7931a57564c638b28392707b822358e7e86a015d9b0cdea563af65dad78f60345293d0bd5aea6cbb40880289a51f23ef3df0afe6c7cc90001114af22e71b80b189072a64ce9078981ceaf6ab211ad328d8987884ff3d91d023b6301ec6a7d596424d65e837f47600110d302c4ca77e0c15b1e39aeb7ec2e9f0d7673fab6e23adc8b95604d612b1954f551a7334c8aac1fb8bcb9dcdbee97e45a9ba06edcff3bb68a16e14040d48357fc890671f33bd14a9ed2cbfd19c37d845d2ca6a968426912fd290fa6cb56b6a994b7c12e163ed5c5a4e3b4e923853b16ffad07d1b0be3a9cf2c52461f99f3d16fc688932e172142e619c19f2c4e9c5b048d2b60545ba55fce088e7405205e69c4e1fc52ea8bf8311a2fab103daea6ea7faad06a9a45564e0a39f8c14263acd450adf0f265f38b0e07d6537537b82574cac2d488b7a6d129c457747646b14d729ded600c4c530a4b72e237568e54ec659b53af4b1977165c23165494d1ab6b9808be2c97ebcdaca3b0b3749e38f60f4d48c61346d113018d06fed134dbd7027c81902d8bfb7603b1a53c0dd5e3165ce61e59734f98a830fd2a70500659e64b3d19e68c6156210ae14901789b6870fbec7337877d8873173b4dab52b2ab88eee3319e37b385fe02ff713311aefd2251d0318e78ac862c400e775e59a4f001fbd9a0520209ba510b3a41ba779139799d754b76fe1d6157a0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"069c696b2168db0d38f8d855304b44dd99a1bca78717481248ace69cd0f40702","proof":"36c377eced7d5ecb095b4391e6f3434b9c18a4ca2ae2e591f74b85e7aab0b23a62038e9b54cb9c9ce5f5a092e7dbd1324b905b44819e660ce23435c139facd002027be0a46842e58493946227cd2b8de10e64944a06f5d379a511da6a1c1da243438db6e28020db851dc0144472537ca3296b90dd89602336d12f25f0438c80020680e04d9b94b63ed10981d591cc1e524caa2a98a86606771b85ab9c866aa0ee6831cd04df360995e3e2afd7d10b15e643a78000ffeac010666432d57cc2e0534ccb22b19db89f9814a10404f4d5a64f149f25740b59dc964777703c9f33f0074c6a7aedf88f7c5ed378e602013b5e9c9ad00f8734fb64069494451cb72a32038f963fcb08250cdbf3e2dd86214bf3f484e29f99abdd643d4bd3c3d957ecb3c0cadb4af05fc78847176a866a81912b60b07af54239f924b4498a6185727e560cc3d8275071174812b6f8d9cd6410bcecc8305a409bcecb847b32246c49a8d7b96e1cd5591353d2168c21a0ae82d6a315b3b18037f3645c6c38841680481c33cec3e485e2dd47fa7bad3a11761e9afc66589b4bc7f599b0ec6d5d28cd12aef77a0015a1ddef0b4c920b721f3972598876abfd05e0a154a2832eb39b458d8522ee212f2bd41cc298aaff45f0c8561d5bba8fe648fdf9a1cf72a4a820282af341aaeab82a19a41ff1afe19dbf3aa5e067d545966842e922ddc22ee721fc415e9721c15eeaae2682e57393cf43436f8e457c89d13ba037dbd65201f0ddaec4e87709861775e4851b2a9834c69803f68d2d263fa7d197bf97a8175d7a3bf9250823c942d216ff1bdfbd0fac09ecf8a1b31b32ede9c7502262d4981af0634caba3b6e7607c65b52d932cadd4481057471761a25287e58dc020aae5cab4c634db16303d763f5df88ffe3b4d5141c53196be0a962d1d848a7bc5bee760a6bdf7d58b209"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"22d0f67355b76335229219dbb764cbff12fe9480ccf914816a8901c78d971349","proof":"08d8b02c1a6cdd20c7ebb2018b8c0deaab171b4f150a86090b33b171b307b3318a16c162b8beac081e9c06efff80a8cfc818794239f27d86b3363d6a3a6c1f519cf9785f4f4d206cf129fb8340b51694c213e791977d3eae6066333d30061c621e25ec983351b5550df168c92a8a9317065d53d5164426ec106b134a9d31a319480c88c5824adcafa2e75dc72526ad978a9edeef553d71e6caf6d186bb00380c3239045950d18e3daf258e647828b00414a0dc070b60e6985f4338766d2a4f02c473f92b691b4738833d3d592c14e09b3e1b78b1cbb1655a381a2e8d395ab5014e729d7d97c2cf0e4cda5db16b7a8f259e232766280d1fd5a0b631bea6ea6102f6b6df4b37e0326d06b68f0559ccf966e7e32532a55ad3fd61065b651e44fd61165be49295655e32be1a515657a10b3313d602a5bdf0af0272eb8073b9fbfb34508af3c55eb3c82aae0a895d96da1ac45c9e0fa7765cf32c5b2266f3493f2c1a82348d3388d2eb197ce0de51bf3fe76cd3834bcc86aaa017c2b49975cff62f281458b7c54db9ff805ddf3ff4c402c147ebfe7b4c31fad6b7e660a2c8f1357559b4a75ecb84a2818106cad768b8771b4d252240c06b7f577a1307599827ba726caa6fabcbb1bde1fe4d5ce3c3ab892b759d65a714fc8f1613829647c2ad4f5e042ecdef8d10738332aa727d706f3ff6061c88de212564740f782f4fca7b8aa242e2d6b89a8efe7bbc8cdbf01d94924635123a226c976d6cda29ebf4f8a4768c6feab0683102e710bae8190fb47d328df30b7f3f1645a7db3a30a8788fcfe8e86d56ff989bc86d276ef8cdb1391a0c570729bb295977bf4f45435ad017024a446f11c24d0283ee002d590eaeb1242194bf682c88e32562d638d03623c3e9a60d0616bfc4a597d155f87fe6eaefbb6270a3dc14a90fcc3a4a4b3a8b7cf4759e150f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cee9272ce44c139aa4e30e929d16d99afd60f02802c942471ee9e50fe44c304c","proof":"9676dc073caae56e7351e8ff95390ff68ce442624bc4e857f15f56a60839ea23cad7133cc61836ac5109444caa4259d2dea1d56e177974487ffd3d3a795bb52556eb66f1b706534c6002c6512c27c3aafb6c77da0639a5249b8533217de4877c96e3024f7e5f536db9d0721ed6690cf3a33704ceda1ff48bd1063e9646f91936098473649ba12fe5460f4fd6b0894bbcb49b0ead3ee3d8aa571ec229451b220651d1c78663a7534c49328c17ea7503eb26ecd41049bf1125e73065bdc089b90499f94b01aba73479678395d480d9e03e07b3c334b239e09f28db0992b48c850e642ef97e9247ff43691e63df610c05343a338e5c0bf53463b0aa01e873abb07d260a323425c367d66cf1ae5d7ebba70bfbd554eda5e839d5e9d44c47c4bd2369b8b2a73f1c7aa18efad7bdb1eaee98b73127db2cc9c302045a8854654b4f5d1bf247b7400f60846cebc0f378153dedd348cac73dca8e4d9edea55242e3c453233217a2cbd762d6ef1c9a3acaccc93c8a69fd649d3f356071f9715f8e20709b1c8e082fcd3a5f013e44fe1c4381c98e51ee9dc4c5d6a41c2a73d3402afd6d277a48efc3747f4e4c2b198b67f6ab66d25c75f98a6d0ff60c7db832d1d2ef0ecb254a649b797af4489a3599a3468208671ed6dca1877b7c5541481c2b8f96bd7a5834bbbb111b2a74455f7ae62acea5a79ad122f8b6f6e55e7d11c28f97710b7f5342a53d595376cbaa502abf5e9a353f6131f2fa6f3ed31e4617e7ad9890d7030fece01901dbbf46252dc883479928f385251dbb7b22ca71efe37a0ba1189eb4214a59e680b48c59d02438de7db0dcaf23325344eda4d6e90c42ca77c00565423ab306fbe092a34e1841d52c8b0c5121347cd9a11cf1a53799c45afa7804a9c70a7057067f685dcb7121dece4e6a2fd5fb4421489faeb9f067155d30e9c3f8140d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"84759e3dd53446a87e9a14c9005d333545c0115d16397a8b350454cb1841e74d","proof":"fa612b3ab7908dcfb95a2622786c5e39bcb813bd00ecbdd15581d1726d51d862be86a47716d0d841b47d0b444e21b492e96e2ee379f6c6648291cae101106a4b4e583def1528732850bc3002b9a2e3ce6d721ad1e986dc0064de863e9fe9361bbab7ee3814abc88cb382d46be0bb709feba4eb23ecc3f58be61c1011d70b0e552216bb120bf50909cd67d7c0dd15c30dec99d17a2867604bbf5fcfa338d8a90a1267ca6c291a769a55185566f8cffb54e9ef4bcd7f4af0dbdf4873b321376b09ab9d4b67ffe44cf44e748a935327ec74b148a33895f9165ea13a0fbb252e630a8299e9f65ba3150f28e078c0f967a397c5efeeb3f8215e098136fedb6a358b1d2a5911280a1c3e2cb8edbdfe21f5838c19b9288fa2b19ae87e96b4aebfb16206f4f7e9ed7c0dec9ec022a270c205ba7afc366be921098dad7417850ed56dbb6c6e9e3d7218afe178a8b4e23e9806cbadc441a16f5bfd12a6a9c7e8fe50f60574f64b2e609d312fefadb43cf3134c6227793af2d0fc6dc3e4db04958b09c9301566201ff98e2cbaeab75463515520db392e957986936bf78fa5512134245e34478ca42a9868c6da822b3e914bbdd7584ec95b411f3e3c6e9ec584e31919a8716c9026f6bd70d46b021ffed9f94e16f3f4e7815ff1678f24d8e9dc925c4ddf92500ed3eeb52894db0dedc15e942b3c4b48f9aa988a0fccbd53dcd2475c0974404ee4c44f7363c30c16d9b17d556c34928df2bdef736d844c749aff718dfdfb8a038afc51e53c3c4e818a7e3a046c026a577cebae8211890d49fbde504a06fc513aa4c745885b4315124454bb0e198e0fb12e46f2833b31940119907261df271a1a1acec8c62ef8b1ddcb2e840b3644cb6953d929a34a0f9d6bfac2526b85d9f10431cd9c679f75cf86470618090989f3d63ec1bb1bdba1e45ec7ec3382b6b99801"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"185dd757d532d5bfbc5b4ed81d6e2441806a8fd2a27ee9c09db51328993d7839","proof":"7eed69eb87c4e1f9ee38e0f9e8fd1749b9fbbcfc04089341ef1b194623372368200c7e2f055b874cb233009ec198497a78fc59364a6c02afaba8c5048ec81f524ab83fc5c562275b86575d23edcc1fdda3e2a67760ca9b04bf04d61ea3faf40ca8776c2c606faca99d309e60bebddd34a5511546e3d1c0e36baaad3c2b77b52568431359ec8c327e86060a41ddbbf75b07cba02aff8332f925d26ccfc00c1d084069655fcaaee2084215cc7def9b75d78f26d59aeb20c018e3e46d48f9d85501aa86b51c39034a7cfe5ca5ec5e5483c367603e1d737482da26c44dce710bbc0b087f24abbb1455a9f90a8f8d7c71aeee43d6978700668ed7f662d98fe30da428f6f70a9c47947ff3ec1e0f5fef172b42b3575eb0b4ad0c1fd8f5c31a7cbe5e5a6c631b065a331b8f43135f1d516a9f356a7c27d8d2705760b27ef03ee393b10232e6e5dff45991835c170058dde72546b9cf30384a4fca656d038da8029f4a1ad0078044c8b398527c534780c1105d3ca2011cb0eabd9652bf8a4a537c17a62eca84167b700c357491be49a4bb5598e6a391895c506dc3d99b15138c1dc7091e04c2bf2be5f636e47e035d597de4c834edfe170bac85a758aba1b45ea1828567a85165b27a16456ec70bf6ace5dd66f7dcb9ddfb5c4a53dd26bc31e20187f221d822276c0d424b3699c3cbf8dd914fc37c80999024949a95e1b071879e479276c4736d095ae74a3f8b33c47cf130a6122a2f22d838260a519c36289e66b3bd3a800dbb42174cdc3bbedfaebca84390b2bd6eb892b513622460cd5ba754b18d6128e6bec1714cf8e0492cf4dc35d5673e00b9ad4e7b8dc554e5d4d5736cb076339c03baa24dd6bfc1cc0d8b415a9b07dcbd2d67120b9968c3666ea9d79b949f0b151060d9c9b0729056712085e34801588887addf6a95fa9fa7eb68a0f975a60e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e0238a52950c376d7f1d8bf800e84e6ea9eab1b43bae3fee7bffc37fce018f37","proof":"148551783ad45126404c12ef3ca4a2bdebd8de533c93186dd215a81397d8d3565a24aa0e601db34bed925fcbca80307e60801035a4e7296e4f8596ab6c74dd6342e13f81801d7749d108f2d4ea08a38ef33bab2e3046e5a538d648e578661b3774c01616bfbcab295165b34395939bcc41e9302fd4a799bc6f5b10f5b770e012559b598e561612518f215d602f573511602e20683ddb79246ddfb222533c9801aa4559b21263028593ea5ca601aa1088dbfd0bd0968a8507bbd494922e74360dfc1f160aadb2898a9cc65349d80f2d03fa7faca1312acaac9499a65ba653890b6ed6ee7fbafea8097be1e37c6bb9ebe5f47b68e0c308e4380816ee962764563d761d922784c862d76697eb3e026998d8a9b8646c63666755a999995cc030124814b5c86a56d766410d09961177df68336326fc4230dd121e42ddc4602537e55c12f68dc2c4bb8d9a2358f852f66a4885f85f93fe3bd2fad17a3b80589300c234c06266e36316d7986621f15d27ff08f67e4a5d3fa280a38a462b1cb03996d4702e436a8718ae96db706c66e1f58a6f0bb5ba87a4a506b3bf853046f252d1e03abad954f3ee90161b69a7d6bfc280bd105fa41d21619ec95bc9557dde08ff7b5a28b09fc1b185ef0b042044e26eda10579d770f537079004324d86773e2a69c5d60441ecb75148892c3fa947de468381d83dc379735c6e266450456042923831b76e9f03a5156fbf6e15500c1d173c86371e92b40c741f4ebdf23f32df5006d4f84bdca75a00c60afe066e492f4ddec34a2b632662d9c4289cf042a725dc72978b42a5ddf844d7701fc10ee4bc76533924f07d08864f8d8dd6b6744f5d0541d586e6171d7431de0a3eab9b0cd5a7d4a7bbf86f2521ce5066bc58bbfcf8c10420fce8d38a2e76c42454edde4efe8c32e33ec5d12fbc503705883fb8dc097a31f04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4e661830a65c9f499a340738051a0886215708940834917557d330bbc1aae036","proof":"d4970aad04744bc482decf319da9014323781719b38753f8a065a17c916f9728607d98a955bf5b010fe17d904ea66a699a801d9d72bcc596c3277a7a8ad4c57ce45419697af47c6f0a7eca271a34d718a671e55aef715e0dec31d01cf14bae1024225016465fac2ca0e8f892f04de049f962578a1c310187a148054c3af06641ceba8bc42c3dbfc6805862ebeb06475c56b02f0aa21e304754a2133fe7a1a20a53788f53a9b0f4881069393fe87efc51b40fcd961e1c3c6ab5d2bcd792b3ef0b254c531284dcd5bf592228ab477f8d28b3257d667d92fd4d8e34574c5865e00fd6027e15de0d5fa8cb1dbcb6537bf72e7d87c79604ece69f6b3697b62b59b40d22c0a0aa19d7de9e6cd790e109e9fdc9813c2e9c8cf9e584ed6d729ca29ef126703afd35f1ce0fd54f63f6796d2de2123c8a9d20f77b6299db5309a958cd3551c0b289c203f35cc00e09cb30bc688efdb0274817c2476c02d73ac593228cd57936d1bc502521c60620102679fbf67bf7d684c74d8ae01f0bcbc35c7a2d101a1d7002c7d136cbba0c608f9262e32537f1f4eee07141102aa9ed266d09edb8817ac01e71f41a270df6274a1a1aac4efce3405a06869efbfdbe4cfff2043baf29196e722545825b453cc4e8660c248c90bed6798a00b9daad5139b76e72a67e793ba4be36c0b162a368473bd7d17c681527527565be2a18e5996ba0bc3beb2a356bda8935ac0041181ea224fb02e4f4edf8f77412ef032fbb3d9a8cd7fe7c4c96749c2c451141ad94d6e30cc97d850b72dcb4e2c8fece043c5e6bcfb8dbff970a18a656f1332ee32c197ba9821189dbb42d92222170e19d738046245a2b56f5d71e2ab91be3d66b6152371f3de16b0ca8de2d6127d35ac065c175d5d63de446020fe43eaa3d292adc535ff131dd60dd6c64fecaf82f0271b0e03ba3b4390945c907"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"52b5a36fb1f7a8c285fd329f90b7acfe2979795e41179551c29470d6f9db8e60","proof":"f493491b38c4fd07adebe0b7923a07ccb07a6edd0d44beeaa7af8378933e877986a5f03b49e0f4a6ff6cd4e824a63bbb56f9b199336a61b6429df9d78e95db36067e50bbb35496cd734f34d45c3317e42aec96cbc40317b006083887745b555012c10753edeecdf6e05c1550778275e1e743aa484916aae106995d9e52931056b55c4fc294eb8bed9962a64b1d1d1bdc302d038799618484bf4ba4dc30de7707403446d0c3a8c65ac874a490df39d3e8ebe9d7bb14d8bf0e17a8888c89dbf20d00123011d8dcfffbf917eae5f88daa7e606647dada9a639a1d0beb6a478ddc0ca29b595a020196b67371993f44d3dd240202ef8f3c4e4e21a55192b7235c5e3ceca535c921652a0351a9ef25ad6c06fdc9316aa6e2f8dbc3a7d13b63c8aa8b3c000156c6abda7c1b9346505b65ddbf9bccbcade619d5d8ad188fa131db983b07eeec64e6ca7d91b390d01eebdd9785ded6d1a349b458405aac688ed30ac8aa7af612a47fc16ac301da94f7dba824d02ad096607a885fcee10f5fe75132dba1293219a59a255c480b2a43c7c0e1b3fc2a06a4f31ad921d37ca53398e6509018127e0eb5898b05520ab318a2ba9b11d35b9d78c62c6276c414f1f5d478da61583ae66520bef725c7e5d18ec22914d89fdbb8c1acbc02bd32fcb022305adfc753677046be161040cee38226870c6029a2225f10e0a6c3cc121b34c5a6fcf597a92eee9e4797893c8131322fd5a7095193990575c929950991b3e16a99c87ef88347a69771cbe6dcf9f6652fd0cbf2fa04a73939d46497a67181883e02f57d493b0c9097073f0995d18032532ba0a6ddb3cd65bfc3c2f6c53aa5b195c5f4e45e7a110d8cf886a6cc011caf33e050e692e0b7d3015d2e2a314d5490e7ba0f8740310f86d6f572f57173a35b090f20a0b7c188298f2be9a4cc219602c23c562ee4ee04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"eeea64ce3044ff94a59d75a8ca9257cc3d641ac5727ec53ae419a9896c910030","proof":"38d39e9bb0b7cabb3249f5ccfb3e26b2a9976526eea5099fd2c4916867df5949301a1cfeece47303bb315b603ae534a3d0f8b1ac56faa174f8c5a4809ba232407c2a2746c924ce1a9d59c7d474760ee5ebccd6aae1d186887e959c59852da85242ea1aad68d3c30bb42472e93840025eca2cb99be841888cb80211f5f5888d17f10e5c5ada12da8690a37250100743ed3fcaa6cf81ecea45539c3e6a7e9be50f5f343240375d7deccea09f8f33df5b9f419cfedcc2dd2a1a74d23b7d5560290f55f30b0741409c3fe56ae336caa2b7ecb55ff18582dd02d985aeee1dea5219070404b63b54c7ed34e70417ab1368454c90360b57755b6f89e7b3dc4a08292067d8b8126a0e87d0d9ed35c986abba41da42bb154d60983181061fe2a7fc841e531882b29849b954eae1707e0374ce1664dec0e73fb90410c01f7087115dbb6434367c3730660c7f66a0a118f3f117b4963c3594e4ff0e366d6cf750261efa5b509e4219bf4392ffca4933c4340660e0984c18d2f113ce8223524624f7c7ae11490aec5d1455fe7c183779bea37626164b3f32a4e35b8ec4ed844af99263a39273be27ff4c990190e8160d1bad7e20a15269643025422f7d4cbfd9ddb814790801f2f0ed21971f7632defc5f31ca1a7c5dc21b0d8dc95d51795ce642488a07f8441c45e7d237a15cd6f7c6ea53dce9c7f2516a57541028c1e39c06021f0a646074ae8d5689184c373446d54089b540b83661305dd3587b192a0cd99fbd5319125226ccc3359149caa086df8e374b3911f3eb4e06c492787706321447eb87705d277852a9406d0cd6b1bebb45009412387fb4d572ec7ffeeb2f5db3eebcabb8d0101b8fbf5c065c442b04ce777fc7324a0a19e19f29a2059b20d917fc9e6c600b005b4b7fdba91e94699bb588fe87e31ec70c0279b7bd3309e22991201b66c9f905"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8a3156f0ea64da3eff532ec4659a9d6a3a9a389e77667440ace5674a5f9e6563","proof":"3a4ca57f93b60485e6ee1abe90887db033ad81df4dae8e4485ae6c834cece11f8a15efc28e605431eb026174572934b7a800e1946202c221523dbbf09eae07306841fb872bf9c543d724570123bff7a69ccb630dfb90aa4175dec48e914a4a47ecb694f2df66a35fb8f1d72fb0a9d6b0476cd31d4c668c8e7c40d17454f12e008f585d93faca47b45e3199b213aca602e8f1a890e3c58f4ec89baa2d2ae6f508cb59bb2ae0c6b4171348bc4244ec74a806eb872dea821cd39a4fe522d5423d0dcfe915e25ccf24081dd40754c88b11f5987362b21abec229db979327f5b765024ce518e294d1210c8f0879c2cbb91d05d0329ea10d30ca9bb4b902065b89662c125876013046d2feea7add53fe3b45d29da5a30a136219ef9c6052dd7a2e9404b6fcfb8dadba8caeaa86573615a2b963b946d87dc2d5c3ecf599799ba91c7858fedf6c439905237173a4dc53e8eeb157bed5bf830194d08002dfaf6a79aec22f3479243959772630d01bfddeb2527a929e8f0f79fc89612935c5ce96853f1931825ac006a20e8d894785703b4d92dcadc76450882fa917c10f8b71a10673ad230088ce8062acc5ba4136b84b00877ed37491ea56c32204259bdd1f85f9ec5f3278a08e94fdd8c86da1f5b0ee9ff947b351e0b74732d75b5b83cdea89768a0308423b4b9dd6e228fdd961e355899a98f42983eb65e2312ef768362e1e3358ab6a76b99319b72f145b35b0d34e08e41f7903fff050c02b02fa8d5a1d60ef3e8a4fdee7f2870253ff0fdfcb883e6978dda12bfa6cab8a9820efe621082207774a362ad28d2d6f8e21c8631856c31cdb6729b37d49c8b8ca76a9eb4844b0d4231c2e64f7c70779344b0d98f5e93a9299500fec6f422880b8c2c27f6d29a93530570b543e0f40665d3240913cb5f4406cc4e3411142782a3382ffb872117da8876704"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8067c462a833c87b514c559ac280e5de6193e33945e2b8b7ed5d590f64f7207b","proof":"92c254b4d4150b18f409add9c5039cbbd3ab508e28acf9ce6e53a1da0c7b08301e0f05ee3fd6cc025fdb8fc5f8691fcdf9a5bcd67bb7f5f2d277eb763334d93804e0d57d041f16c360d75018f014a62b2abbe0d2e979639f947aba81e9eb0b0d20789608da4dbaeb0739c8d72f4fa3feece91c957022ba9d0d23d77549cfc17d4f6f23a699cad3fea5985e611f9148be83e8ae01f2fe23bc32fa0c0c2c93180145e068c8ee44e83b65b717a72a596f8e79c01b4093b5c093c9b6f63f0d846909f1d6308326d345ed86faf7acc91d320a47a801eac22e415db0a4f5d4593f3e05343a17f6fed6aa439347f0457209f91ddec06807801e14d5c9deb2d4a33edd42b891908e103bf9b9c2d628934451774d8093da492105bb8f79ed0080fb26cb576c729c98834b14aec4d65a057eabb14f57c426bc0429ea43cad976cb396cbe0b4205c314442a947645b12e93cea4395c6cd224c5c09fb75a2969dc1d732fd32fb81fec39199c727336239274ee5798c3dd0f9d03da9862e6dda7a46dde6de679ae4c03702e317d954adac6101ecfeedfef82a149bb0bf2878061462dcfd91065626f00f8b42dd46679aaa867f5c730c9e56cb98d4c92c6d17fffd8cdeb5cac1476f0735357be9f44cd2b6168a9fb90b68c1a32ca6f2bd6242238e9cd26fb127c505b9611e625a404d727605809d302a4ea92e23c7c581b65e6aa91cecfdb0e0c4ae166f17d7825452af86f284dcce9316a67e52de0a25d5605507318740c350308d5369e88248ee6888062f740acfac4eb79db40e469c4880f9abfd06cd68140120f7bb5d807777228b05f867482f327d9b482fb77e8988f4331c750a1967a75dee585bccef034e91a9d906f8b5845dd9a1c6c6859172770b13d1992db9fbb0964faad7894b56000dd0b72f925be12aacda526800fe94d89fe6817fead131402"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8842d714cadc6c8e942d56b67d9e3f63c37b67b14a19f7390115d3cd4b88f008","proof":"d49d057906b206d450d86e620d99a5ec0e34fcdc235cd011b7936658bb526f05c07eb5a621344440cd7dd13dc63d3df6566c94f2f282b6660c9fb6c413e1c828183b4dbe10eb2d27c497af9853fd1d9cb77681f66845d83b5579d4b5cd3f5124820bc59ee009dbcf503875251b6b5a7c2f47ae3ed3980916c53fbd816ee62a1600189f5a6d6e93427b44ab11ef74cde9b0d7d46703a5ef5a8657410ecca787048508f5deb08e626f97f64263b268944fa21be09cc7e1888553c73ee116a24901970ae9c0a5d0e58e992d7e58867c3bab4b027c0705e352d3584240e64d62a40d2a915466f0272570e53816333d71c1e6820b3b7d023ffecd667accb34e340758a89997ca1c027ef8a894f570126e1772917e352e6695517e9f0caa3ceba2cf08c0bec8039ef7acd2ec9ede04d0243975d6f36d269d675c117165404149944e0840e017aafc2963fc191e1c1431bf4c32f99a84ecd52b4edfab8e55153f102b04b8872b8cb3a9fe33dba5c6dd38e6f4be5f7214277900ba5f824d99239ceae86bce4388d76a29b89d1273ed3928dbf595a1b7d21fd83fad7b3a4c7dfd6a3eb95f3a2d50c2951073a745ff3c75e7bf7db81f5ccf44581714c9c713d4335dd62f5762161a73a648274a684c11188fb0634b5a2c54a0d47594204e8a77434a11925e22e868c43c287ac65f41e57aa531be99fdba1edb1983c1b10b395429ccdd9b37becd1d471ecd597d1889770c0f88531365af4059a85c6b11aab752801204dd1ce42011d475e34177b7d8e929e386b2af4266f816824ea3c2fc13414d97da653b6268179f6b41afa482f825d28dd2b93164f092e582ba035e674fc1488de2bf43c8c736e59f934cd5fd4bcd29fa47f71c297e9fc118041c8485ba095d7a54bd0cf0cbb7c0d9e66ceb54bdc5087ff502eed7961343884ffcbbb5e49114adc49504"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6c14c2be7a845e209e3c86e9ad76e7ecd4a59cf0cc35b61058baf60c35353a52","proof":"6898ba8be64ec25ec279ddf0965565a4916116d3c1b981ce77a83a4243c2010258eb3b255e8866c3f3967d5fc8a162b616ec46af7b7e82fe90dbc997aaedf35690a645e347cf86f2cb6a04cfb6f6ca4ef1ab993de73dc22545a73fc7a5294d758c025bf3231f91f80dd49c0c9b600dec54809002cef52ac1f4b9d56b3962e235c9361b8d2a2ca8d82b98e76baa99675774a6e35d2d1f08184b6e19eb19a33b0789486c596cc48bca7820bafe2c978f2ee1bb0f62f9511404415910a00eca000ecba8c70cb4369799908b743217aaa44fbd627445dfea20a9c13f617b2af11a0b40b5f27f9482697b3c12ac1c212ae38e7edc377ef903f6e4deae285b42462a3b3a76821c44044db7ff5e3ca3e87649f4ada56cd7ce9acd86b9d793fe7458b457da8aa9d27e05223b9bff0d58750489d81cf949c8af5af8af8a70b1ddf04920694c8e474c1758b3706b873695c1f556fc8f3d9c1ac75905f9456e2132b7cf2c040421dc733cf8c1d177543dc06c57424290cf0c664477b906522fb09cf892214dd01210682cedc86f65086c568146738d06e05e720f172451e36e015c7c080b0d660a6c60145b8dfe2935b46c20d66f2d3efc65efcaa82870f65adf81e4a7463b060d9029cecaa7d1c5492adea45b39627b8baed85eb18f6cfc75e18a40419a3484c3dded1a1984f94c1f6c1556c5f150d59eb098d8fece44677a349419a4284c4ae14fc0db05cbdae628d95e08561eb0398b871430397dc6fd2d4453eab78d5a643938cced9e7dfdcdd80b84c954cf629395aef2c92c4a891a63d2c37a8b4378fe08406adfd077312bcfe209caa8464aba595ca005561ec09aae83632718ab50218035da335f23a4302cdece1357cd80555d964967900af376badf16e9c7a303ae22c2f41080b66564bacba9b649f04ce577da1fb052200099740cc617e2fc0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f8ae4d3b4e58c410155a684869fb538a061aa03df09abf5cdbbc22176af6fb75","proof":"764a8339fb15ad3ab4e807397048dbf207a312544dd778f8013f115bcbb1e26ddc2489b84dbcef0340017993d7c787c7d0e44c2ce5a7b4a2c14d2a60d974d36ad8b127fc0ca100f5a74a0eb6e342d0c5b24be895b87576ba1f4f6eca8aef1444ca3a194cff3bef1147815201af259c5ffefde8b38d0e933f01f1d82dbea1177a92cb0f81ac4f5f0d31da68bb72a7af8aaeb9d9229bedff2918e5db4a90d0cc03f5638b83de19b607d99bf0daf153ed9cff3267717ee8d35534a6bc9c20aab80d4148e8403b7a736a16ad633091f0616741590c32229b23c1c5a1424bceb8e80b6485611e739d78c65d5363e570402135b9f9d9248d36351d4a7a6ad53fb2940b4645d03b703d95a52ea5da650fb401739db566234033963faee3120f2d494922f68262d23e1d89c2d4b8ae14e2cf24674d104048ff08a4db839e5846b2ffef1a641da3582596ba6821a7e720f87e5f85f4b117237e7c6ed094293a2187df04057c1f0029653e0627cbbca158e9d002352a4ac517413141c325022da75206d20b4257ab35cd4dc73ce5dcf591cc5e736f5476fb915833967c776f0a7b1b3cb2454a62756c10e533e3596efa7e5299fa2a21d0b2cdd0464c127abab1b4b2e82b100ad4a7b52ce8e57bab55d75963321487cabe873892f63b0ac4258e869978fa1928ffd7b5dff9260f0b2bd12ec16583a04a7b7af18f22f423f10910acd7b6437fe411632d4e55ed53c0ac0dbf72254b0818beb99f913936acf4f5355e13d9c95088093f764c941a826e86fb1a578c039cd0a46a08ba40f06bbe263223d2a4a8528048d7db5577f1e417b1ebf4be27214b08d3a4e36a827085b1c807a4918f551ee49208e575ab91ee592f42140e11ba015b6bf7009092124bcebddfdef6ad5d0a4bc5b9d114ff67fa78f259d4034ac1566d1f7d551455a040a9e5c5666f1fc30e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"200be74ec78b75eb400d29c2216db46d5c95571a1d619796e9d6594b83c6fa32","proof":"a045512f12359cad6252b36a71172b39e152869f6cf5a83de9d54444131cee79d280f47a20dcdc2248dc0daab34c09da4cfccaabfd49f03f24d6a710ef1a1c0f96e5a934ab7c358a98326e3009e6a957a498ff5beac7908b55f155db9b954f5b50442e2a5bcaf18a647bdd11af5bf278e637e2adb705367ca11e78ae84fb0040e0a4be6ad5e08ba8c720bb88d554545b752f7a68bcb7ce68d393a846b7db110bc77c74ffbba16e5bfb9080af3fc58ba5e10725a6cd378e43ac668776bbc9700b6869708980d45a95731b633fe0df433e7ebddeffe5685c0672a10db8875f0d0f66d8b038cc5e156dfbf38a1b468396264d8d1b1ccff91f67425752cc18cfd467ba39347993629693efe5b381934cb3c809abadaf0ccbc08b6b3d2ca4f5964359fcecd7340b5b800aa87b1d88873664a95165e970f099a504d52195bc43dbf91412c078da4068f8d4cd56ace9fa06e1713d96b42c205838ecaeabc102f513e51c8497e1295b711c368473640d8331a744bcfe24040f5772be0b997e8d9e77813e3ed326cd1a1321cea2d81d5e87d0c79a4d1ee4047d63b725d2332b60b107342ffa5e1b1cc0b3f0f23d7214d3f5185e58e73f3c0369000604aa2f1025407ef524d852c0f06ea02212803875212dd666e80b0a4331e4c7dad266dac32de9cd4158d28777fb7164550e790077fb011ba3e5308964ac998e4be2a11f5b41092f4b3de417bc34d8dde82d0d65a53ff0cb061e8ae318f2b36b9b833c77668806735c40425cd05b33f5d986c0888e291fa719c36b9720aec753bc82d279088649abe97bbcec6e2f9725cd72ec6baca595f56b614ed3058f9015a6ccacb1d698ca78b8502baad6239e3d864288422e49b0072c28d0cf122e2c25459462314c2f85dc0404e478b7a8f9a999a2f9519008d125cbf9e269a3f30ba0a781a5f8ebb76c20ec05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"84c0391dff19bafd12f390530d0b4a8ce36207542dff932b1ea8367eef63fd23","proof":"e2c2fe1ae53a854daea9ccf42245eca966efb3b9989850d7f59f24b8056625356464cf4446b8f0c051a614bf5e018915e93953b8a7486950371777b8e54b1b7258c57d2df2cd1ce7f507019fc599446a989de68eed51299c35dfef822379dd5eb2cbf390012c0c679adb4626ac23011c97abc4fab5036cecc9489ec5e7fdb3442cb5b9d3148b1222f20869786d0af0e12991552b664d8b0ca516d2788fed000b210eae85158227f9e31d32eb0b2cbd86bea8f6fe7fe8c66c11a903f242ebb90f80eedb397a8690fe3d7a623471ff7c1663ff86932ae204eea2263d8a14d6c40deee9be461d5f208d083e99bf734a6340782c2b675f507ca3dee8f2f723389a761ed0b1e724c0fa32a78994c7d3608b519fadecc2cc21dcb1479a80f245d983758076ee02c88959eb0ef6e02e148c468c7f2c601fff1158c8532e7fbcd5be4a29cc83c76ba5c8467241ae7baf7114ee749767df0d9ab4eb0b678796c25f7ee107ce64891609f6ebacfcc2153bd8240c599d40bf2fb60df14e748fde8ff48af630e85ea0ced4c95d92c21db863c3ba6bf5daf6c81fcc9b53b394374cf969655a3a925b3cb24f1a048a85bca1691ca55b0af26785e192a3cadb387c9839d229a17f244c23d0d4fe23d149484f96d97ced8a42d07c86369542f2e7e754ad2ed876386a7074e1ee4389d15902de470574c88083c9e82ae05e01a9013d3aeefc7c5f013e7d044c8e201c267edc984bd1686bf2a5d0223b0709e354989ccd278d92fc3282a0e1b9569a59fabe80b49a5ea3dd2b3271f0f0dffa9bfd8e78165237f1867cda7e3c6e01d0aba2aeaed33e6576f22745606f2c1a2a2ca2feb24b0997f0621b47a4036f4c4b73f9b790ccdbae60cc3177cbc36d624dc12e05e3a2dd08c9b300eef924804622b6e891b57c9dc1b67c89e9feabfb42f26c1ae4aea1c2ae537201"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a0ec0bc89d4ceb482080e3a99ca1e39194acd8089673eb510e1434480604a254","proof":"069dea3d8bd225b09a912ba790b2dafe1ad79a3b0f767a219e7656c64a017b1f2467fb04099bb9960ab54522ccc86f30ebeaf7f98070d8f3c4ca29d6dd88df76d42eda3111be72b83bd240f182fd488dd6d7c25c6a5ca64c149cd1caf95ef83b7e26d1502f5d4002630eb6ef9470c50931328052e221f3e3d1c1887eee929c40057521c20f2ee04f99e5d7a9c0c3d238b3e493e0a1367f3eff483f4e27fa380258a0b1033eb7bfa11b5536189fb7f9d0688ea3620115c2fec8dfd2dc6662b00c3408ec8a9d2b27b834cb47daaf7878913b54934a99d4c7e2909e7d12db35b506cac2af473f9d06782f98255589adbd9870cc0dda7f28d746b01eabbab5594d2d688d3b5e7b0c29de5f32cd7d478b17c031cd8d65007a7144768e6188076eb75d1ac73274404d6cbbebeb2209afe7331679f92fde83d376508978cfc59324f13438c90fb6567704e2265ce453e43ec1e6445437f4019335ca9e3ca399d8c37e6b26c1ea1aee766f98d41bb8eb1185b62b7b5e73d04c3ce55567523aacad694b68349387f83ffe0d2ca61bd6a58d2170c501c0e268825841de99784ad13a553a525038ed289d3675d477d07500188ab41f4ccca8df06f6d70f2dbafd0e5a763c6a34137df8007d737417374c569d621b1152eb14bab5861c615763ab44deeea9073a8be0319e8371a6e4ae20cf8f83eb7d33668c9e4ed7359fa7fc4acf733a2f038ce43893faad4d0f86dc15baca6b628c63cba6478688ab34e4aa2fd4f6d8c4102442fb88e0b186876fd57bcb99d4c4c4ce51552f8c33a24d118ed9adac7b76475665a114f998fb5b8d1c9ca235b60728dc56b6c501bd9a2af4667325adafa04fefff32b917176fe7dfcf7808eeec6d9e4cab4c464fbc20c16ae3b1e93d6f4a0d72f065a7e745e0ec5a11b8f9e3404696f92a560cae5ae6e152f1be719170e50f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2aeed8048c4e4c968e1db66d7a2bc5a44fa4aa2fb18758faccf8e2eccd7e4e7b","proof":"cad6b0276607ed19f8fc64ac0ff72d618c57b6c52cf4e13c4c90fcb5c84dfc7f94a4614950a1b3bcf63c090768bcec8a115108a3421a63be659701ebd8d9cd61e80a0ccdd5213da368c0328d9bf6d2136408cbf27089e18ed109775ffeec8733281ee37007c7fd8eddc1334d804630ce28221c31463aca0dba43036b0c69217dbdde1f5696503efd685312175c7b2e7a7f09f4cfc166aabccc1b83c5e34c6c0c30d1327a16c5907454a3cc99377f3c74ff0d810439abadc9fcd512af220d420c54b636be9173ee9ab9bdba08ebe2554d8420a46c13612e2028bcbc9556e29d0bc4c49dcea05b4ca97a075dc8ba2cfd4a5cd8ab4eaa3925ace293604e1fef8e4bc09f035cc2b844073aada2db40506d58e6add2e2d8fefe0317dd5bf900194c3c8e61872e41343f5738b6a398edcfd0ac69a62cafe2b67911603fd1616ae0be4598ff5b3f12cf1fc7252af9e02a0d9500735fda5a2e4e452b83cec5c7c104132ce6f27cf545cd56745c0df78783093809d5af6b442bc359565f83833456a8116716d8ff810e4550455c80537d707611969c1a691bc9a46c0224039d7217481f52a66de4f68421eab0e9dfa44673e8a61d9210ec9a823971c080e999d9327734388646a1ed0d97648589c1b638c482bdf8276aa035d1c6fbb174c58c02b93f7c6b3a506f77c3f2bc12bdc058f628971e76a4c4bbce4f17ace05fd3071488014a051ad61e313c8af7b584f9c3567d568684893ce003e448d5209c3754445c07ed6846406717b61edf7ab736a0219ab67261aeb07f8d9d1afff650a5498b75b6d97fe623a1a10773144af1807e408bc54e88459182f3c9a9b803213c4a9da68a683365cee851e37152f1e3a7ddbc4aab5b76d7951f0244ebdcc6a7820cf05172b2054378c58aa7ed00534bebbaa9ef922df036a54652a5a0b629ec60194330fd1505"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"48930151ba1fd7e3186be221dea540f64b9d0dc2c16b8439034ccb9063957926","proof":"94b8cd48f29fd63979181e0163f0d6afbfa53ccbf1c77c17fee88e06cfffbe389a52f62c896212a13dce55a8656f4f5e8cb1027ce2d75b6e59fa20b57794e53598b187dcc37cc418e715f7cc1b44807ca0edddc09a05d421b57c419b294cd77ceaa015f34fdf02931fd2d106be725b868c9882db4877b5f06adfcda6b28004507d58acdcba4c86f569347df38f69d110fd4907b50ad77d08fc10b91ed14852069c320a02f08cb64a83fffbcdd2a62b316c1cbb434f321da7820b1a96facd78061e354bda3ac067d1e2fd44c3e622c4b5de45dd3371968ceb430bf32d4415720d881d36ad748599b8b30d73ee46e0d438cd36cee1f260d0224adfde6c4c9df36310cf555dd5b7d20b5bff7918ea1b2e5b05d2ff01b6eb8b25ae603d1e0601bf45cca35ca52667daf3d7229a47277e59e4181a8367862ce73d67924894e980852f74672dff44d77cd36c85237de4b001cf07337c7ff0b7a0dc9c17a2054d7dcc5ad0e861ebdc40d3bf104cf93cb529741272a56325187f5a0e29a38a8de2212d3edc738a5e0ba65ed3d4a7b0a96d8fa3de331de4ec3f5720e025c71c11b103355cd2ad4cdcaaa4b47e80b07a29238f10329b217f1cb0629e7332b7f484524ad97522fef3d42aa5eb528694ed4124162659f39a48b1575e713a33b1a67187f4ed1966f1fe3759e0fef94b50f318d0bfb3d87ad02a5af22e4efe28d7756cee7df07b6e65e8c4b51101e48aa5e21e2620c88c12891a77a5ad52d273ccb45ca663d02b8236c40587f248dd6dce663cb500e55ebb9d22451d5c3b8cd964a6b25cca862c08381ab7fd6acff8caa657fde8ba150102473d5aaa05a6938a2763e1983f7471e94d0127232e3b78a4d59cbd4c7c894270637b6c033d022dd1f8dbd47838130d6142eeb769c224d584d576c526996ac76b931e15bb987598773db2282ab80804"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3cc6dc9befa2923939f284c9490f302aa72f82b904a8bfe077aa0bade385cc12","proof":"783176cdf6b2ba0a5564317e8e125935af95492ef7f0ebcdece14e243fadd246b00fea5dc3c7aee05cd4c43ca06fa00aa017bc03b86cf5f12099ead22653fd0ffafaf7131a28b018555f7cb99d965d42f31f8fa70b6d243a686d640f86d0425b5c5988f3480946308d0e1f3e25c3f18f33b1a5c41c8746ecc25cf8e9b9c2e46094512ba85d1358f00d737ebe412c321a279586cbdda2c9e82c86eb27d4822c0b5183292db94cec3acb0c47db5730ded39cbb798fcb23ebb885f5b4e95b6a8a056a2044644e45006c2f7ee1c2a5107505620cd847085d900f74fe82d726e6d70aa2754b9fdae04f4a36f34077a1c14c8583a646d2f5c77e71989f525d9b2939020ee5a1e860311b13e8cc3e103f4a4047740e9342e062622a5358164351f9a73470726f1854e7570c54475c3dc50d7b4fbad283654be9c0cc23b8e0a675b0f414d4bbe95c01c0d07ad278c9113a56e6019a389616cfd799aa9fd1856096772f05fea7755051bbef4a06ce2770fa969490ccb4aedbf31d4860920bfc46c2974e22a899b7cc50b0bc3d666cd185b54b5d0f8cf0ff98a911bcf958997eafa22d2f2210b9d7a4fcc4ad9ac9d4acedf025ea5ee6a576aa5e260a0f8ebd73c3badd0c4d6673bf186717766ec2bed6dae88126fa6e0af0ddb48d67b525764b3afcaea15a34b53f755e502352e73a56143525b8c6f3d40f2c8d8814e7568777cbd3d0323e0a5e02d9f715319a830699c20860bd3515e30bb9f1a2923c6e15cc2c49a1ec5e487014347de2ca6b589ca0d6d8858a25458bca45c861da9a26d6bd89462f462f248efd2aaadfb14921b0543b931b0f5dd5df2e8f6625d0c146904e5b5a1bfa670fe062a0d9c3956676a27cc96f923e38c7d73a68ac127d8206a8a44a38eda905a1ebc7cc9ead6c36090d96399cacdd8dd86e0bcff48ac0eb747883b91bb70d02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"748ec0647fc567a111ee2df1db0d0138aeaff89869179403a464c2c1efff803c","proof":"3aa005b1729dbfe964c443e4f06634adc05a8d3aa0e47bc270dd2cb58d8b453f9467867a9eb0b3fe44e92bd0682b9383d6f83ef726324c1454b099b91b704665ac766f319492d375e42af4303aac3dddb947bc237fa06f1435128d1d4a318e1cba0f4773f57668af13a1230800fc09755552190dd247c19c6997f1255ac06306ef66ec96391508a1aa7385ba24a04e24f861c2b598f9a054e6cbb7457d3b950ee17f05c1b78550064763a8f5c1a8b3f178ec88d1f1e7d24d1c94f849901ce10e9bc329f138d248b4051ce54acda41031976771c324bfcbca9317d56ab2c585011a4d4f4e587dfbd0cfdce8e9007529d594ab329e407423b8256a6e2a9283450674a9f8dc6c0990174a664f2a604bdc4115adf89d695e0c77ea6b170396ab0a1d9e2f93cddcb33b0239e3bbaaede08956d8b2a0f7086461bb4ca02506d969bf16188be38eb67671c326df1ce66e15bd0c8938d12784dd84d9a63cd096e0faf0515e47169cb0bc46dfd1854dd3097e5d7944536c843a252d23bdcab55b5ec3e91d3847a1f198cfd181de07110bc2d9e89e866cee59e70324232687e9d8edf7b95daa851bf69cbd0c45a68087279a83eaaff1318101a915442c567864871c0b0d0bfe0d7df77bce7583b2f15684210984a11ba45608158736881aadf7f91340eb03c208554574cdd3b2d30f975f9dc3aa38e82a5f9b385452e507026525b8ef120b6819c96d73e3937d01ab6312820df6e8e8c9bbe8f6b96ce9588bf59faf6cbc2ed462df50cedd067582258694aa7e9252e77fd281ce345efe1f340f2bf9845e0c42a2ae98232db309ea032c66723b03ba2661d1bee7c7426e53ee5733b8ea86070e9f9a2ce0e07851775fec593a299c05e5c2a1a9553686b390784409d40afc067920c85fea446c74148b3a2d712a04b9c1a14895224752660f661ba0bf730004"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bad51def170a3c03c5110d037d624982c5131f29b1ebf0a26a3f45c2182e0341","proof":"ce6ade64cf7e0125280ec3f9152a2bb3383e1ffcadbb13023271b8a711e5215818cf8975c8a23db24f4f2ed2b4468c5f139592cbc823d956bce1168c1c2f400fbe8d00a43d8dd0405529e965d1449e7bf3fb6b01de5599d978b6ff8afdc66d4114e5e5770dddc3c4800f743fbcabc0ba65052c971e76b6cbe567bcb0bddeba6026fe18b51de4a7b94fa71d6529e17d05f7fef6b03699c38b6b05b72ae84eee0ff7b93860b903b0f0267c8b591a3cfa09f42bb88685d7f39f8c174d82be3307043889c4835b0ff9d0e99ff11ef348d74f102bbf64f4a7a82d4325f47c3dff3509ac1fe699b80c20f9c8706d73257b2092e7cec902398c45768bfa86907fc6b820e2655c7a25212a9a57706d8e4c057c0dcf1929c7e8cc7e6aeb0fc3484c368b525e1396c40647a47c53c5506e3e6dda0f623f886aad2b4e84107b53d0efb7c17006e5f7fa4bdd2b21d1c52bafe42a5b620141306c5b5b865630fbe55c0648bc09e226af890630f4aa444359b43ab44d5f5251debd543cb0fcb79cdd63b395d55ccecd81e4906ce55fe478d16ecffb22359694bd0c67256d5f28df9d287305414b46b62bb2422d101edbe0e5b78445e8d971a47c76f19e7680fa4c43520609c9551ab5c471bc5e033ea183d98824de1ace92d12029f6ab35165d9468ee4c34d0489a8d2242177876439d639c4c7e70407731882e79d85b14b712168cd429ff867a786e0796ca9bf5bc58afe7aba33803a5d33babf269c524fde70af601ae1eb967bea7afb34578df6cc10d3fb068a91117862d6c52f47e197376ba5719eb8c307700f27928ee7100e9232f38405d0efd014d6a09986f60d69f8e98ff371ae3017478787463a02ecf3d24007f840aabeb9d9c883c933eb7ca658925495f1a6f710c1ef99cf228a0ac1727bfc0b74e28fa0ad8f045a72c834ae8883d4576a1426a04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a2cb89d1b9c5b36c1e521d359c332e2195322b0020e05f2ca436c19078768a45","proof":"ea5b6518d50549ab03290226db1b902a72810556e2726d4174132dd8446499175ab8eac59f812293816c9b43a0156899d7d85f0586ddf602ce015347e7400e59e8e5f82fbcd9897bcb2158f83d77d44e59adebe14030234bbc9965ecc2cbf94fe44239b724498f47c2f20aab66a9363b03881613f45e8e5924e488ce6ab331479d9bbdaa585fd5f53059087e011e892de623594684bda8da0a2c9d69bef64e0d04b588a62f6207422be444a5cb0a1067742a0b2e9e254bd6a52f1efe3e3ce70b1f21e93a31953e195a047294b700539666eb07eef6a23be593bb7b40a3be5501269334dfaf3a63dbf7df8d2bdd8e87e2550f0653a67df4509e5f2d9081333e14c6d312167fd4c2d82f19080a66d79c4f66b0c8026375498c9fc94d84f3ed2a773a80f26bf1259b2054a48163519feedb43673b4fcb273f2e54448603a12d5b181ed1cd54acc7b5bf35a597be9083d50634d53335995f4cd1804039d309da9b4a46fbbeeacb6d97e9ce5064847597e41ff559eae59712a2abc6dabbebf0cb490c8c615ff830804beee19bcb9c624f2cc49c81a7b12eb00f1c584688de18e56e0b04e746bfd65d3480b6423bba8f1f491d6c8086346d7b267df7e8869109c3593f804f0e79b43f929ad2d38dc827cb9b749fc0249b59e10d8925fe2f43fa339b7d9eeef13fb3782cecf47dd68f6a59b5bebdcd1fa8dc6f2748868b7d29f566c6427c5dcc0fbfddd5089b221cd0bcb87c1905f668abff179e900ba05ff97e04494ad296412331a6a804e6e60e8607f335db874e2da795010afbc4bdaa5985b92e4626fbd4fee1754596d1c0fc0597ccb3678a7ea257944a6b283415cce44b5c0c1ced7f41a1e28aeb9c202aea398239dc78912c397281e9a20355ce9eb19f6820078ffe10c376f306ff233a5e7ae9fabdc5525941e3638a6cdb9d506b671d66730c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"76375f5a6a2e404ac08e92144c33146071fb1daedc9a635b7552a21b35f35612","proof":"42650fc7cd66e1ff5d2c86f8eb712d867c903ec8102d4971d8bff3825131ee410c99325e20f4cd0e0be6652f1dd8fd61d5d019e616478a8d1ed79725931fe53a54eefae23d6386fbeb3bb74cc166abd5344858d145f9a19a6b7f0b51bc4d1f281cfa280fe80449bcb65157f2778535c80036ab16a9507c6186035e8bd1370e005cb11041910447093b51ac3fd4df2749085a4cbe09a0d3310e21e0217c497103f449d822093224a6c39531906cdc7d709b6997d9ed14cf60c95bfdf108121604f6f951fa33e3c6b3219cd90d1d192a383d9db871de6c3e473cecd3b4aab5aa048a84ea900269e30b3297a1865dbfa521de39026fd88add08af1c4efbd569196efce4adbf53ba032adc4bfd1d4cd3384a12e3168908cddb245f6963961ccb1b5a8c7c5678395de9275b6d4828be9e4eb466bc5bfbb7737452ebc1bbc689818241b055e67b36b92da998a02609432430cb731d719030e28d1b8098633962d837125ee531af64c9b805bfdbd5d2490430630d015c88f46a0234c3b9908b8eca8e12e042f50ae5edd8753142979f617c8a69f0ee068d72d9090b0a574f45e2c30f2e32118a73392675b3033313202663bcd7c911042446efb3dc3ca9fbc3a5ea581e929d2431ed0b67b6d3211ef7835d877e69782c61de0bcfa7872644a9f48b9b4cfe300b68eef87da0eac2e1ac817e6ec4dcc1237648882a8d2f3863daff8afc67e4ae1ead3b8b02b03ac206af68f0dd6a3f4c07fa82b2f2498e26dfd0a5a9112c4256519d4fda88ab54898c2897eb7e7bbb001e1393341db20a2db9b2824f2c4d0e03a2236b3cecd89a67df537373df1c502a71f4d9a9adda0946438931f4ee7d270ec0b4ab42bdb9b9b0e044de66b90385196dbd8255b38cc6754c6d1df58d0460a22a6946bb2311284556f8abec62218dcc3b59c4fdea07d0c0d443ab49cd05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5cfd4efff5a7d23e0c8cf34cf621c0a9ee40efd94cb2854cf51e5a431346960c","proof":"2c496bddcb25bcef5943874b526c926bd0b3390df16cfb909e469b933e6e8c67ec3703479df9c64c4a511e1fda039b72dee1d0115485f8e7ce33ad891c6f9446584ab6705abd7a7572e2774cfde27707ce28f37c123bcb06d26426586d0ae72646b1be9c4d903f3ca6068ae970bcf8abac52f9e93b7b83819d39b23251ad1978d6554f8cafbd5a3ad069d7d46aebea8abdaf7de7f56a53ea17fd9684ba260a0bc79d5b890081b0f4de0b2566f36628f6a52bd16f881e07991f400710eda3d505f6403bc466ceb0827f5f610c02a2dc8be569817e28207446cfef2818dfe9bb06aa2f07403d111d77feba20e44f6ac9a20ea31f0b240aa8b8f99c8e2f1bab711e204ae4cdb401c7febb09171123657c65ebc516ce70eab9b1f0bc8032218d946024ba56d5329b8f3d103408ab094db46f8ed995a51994d8507fff75777da25e60b86075a4bc38e96ba49dd7a91364104da48d36c280ee1aa27d4eff635b2bb4053ab8b3aff7e6426c711c46ccec01203dd43d01213877d13778db2932f614db64880132d012075cf02b5f9736a3494417875391da91ce6b123dba6acff7af96092424eb0ebb15ccbe60315ca3dcc5bb6abc112717488470878bb0f15ad55bcf3ad219d846983517af82bf5ef0a14ef6d833d3e2c71e69346e8c7a5d3740e4be3a4c0fc30d795843c6b13fb790ba0b5fa51ffe7293063a31625611061561fcb8445207945445b178aabdaee9a2574b0b2ba1a0caaef3053881c654115a55ff8d4636ec4e17784c48afc104b95b1bdd9fe0b7daca04f03c66f6a0325e12c949a707984c033afd6b143265184ee6c0b24b290ea73e6c3ae1c78541eb7873844b4b34f13ea3e66f37bd9b6a559a8a85df6e7bcccc465ce2b738ad90c2b7be5d459e033b72398690a6e13bbad8a51e28361c5a1c0c31128031192cd2def5b6db087b01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b0c790cf6f19ae88dcce47d59440d9f1b79bb8fd506e2a447956c46d6941756f","proof":"8e3638ee97e9a74a0a6c74145100383a9e721dd490344100ea49bc881899ca50644175cafb826b45e7507b63366d79391b9ddd506ac30bcaceb206370c2855489ebb1cc1acef39ec7692b0a117b450324231453e18f9953c18fb1d908c40d07ceee4542d60dea76cb807a333538482604f98db40696c570e76d394d2e0d5c12454e400f01b30c9cea4edb737bda0e50cbe57e1ff19edf1e3ff43a850660b970602ba508cab44166fdca3c57c447ad8ca78864ca304b8f800a17d9134c789d50166461b87d3f3a271af80f53ae39ce9f1204dafdd7cd1a9526105609c745a7005fed55d7bc5af18474ac01fe605bc83a552f999a32c8270dd00113327eca9c6086ced7340d6aca1f69d355f8f179cce22ea7ada76abc34b14bc3474d2cd8f26679cb5a31ceee798607ec38d8947dca9b4a6ae88aaeccd3de46de955c6771b8f749ed6f34dba7d625f4bd9c44ce62dfdde83c067dce1a7826a22b671d8d3f0bb569c4ca9616b4fe0cf8390471fa145a7cd30144299e42118a73833b21d0debb2609adf846b1f7ce9a5cbe381432f00de3bb01014df7814e03393605b3ba08e775c88db4b091e88d3cdd290479e7e05660980ebfc8d28c45fdcf9479970fd3b622e643d5b935c85d7bd83e4e45e26d7b19fe5d574c416c2abb6d4b34cfa88fb026b40aa60d9d325f12ab7c050bd7d111bbad13ba6f06779874b352d322cba114d167cf1b77185e8bee1c7349340bc49f2313faca7eba245edbfa5c475df5fd2b305e2ffbd2d84f3ffde89703bfb13b3048a3a39ae18e87f982aae8c89732297b327365f70977756b8e06b50eef1875bb427dc3d48247ccb226408277d0a0e65cf24c9e47349fc1987ccb740f2f84849f9256ec0d784b6739c2d3295f505acc82c0e41a94f32dae339d0f9517eb298c40202cc37b017d67c9986e5fa899f963a5f07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4af1fe91b011b1488f2ba276b8462494d720c266f5a38a5014b6af3adc0b9e5e","proof":"6ebfceff1cf77d1cb642343c0e6563eb9a70a97afee394b0884f00ed270cbc25f4f3f132a0d88df8e9aa6d254f753ee6ac3245bbb5a5884da2c66956c6d1525cb0d717c7ddd89c09e1d24b2fed3ac8ce87a9472ac6f093f945dc0fe80089903ed418f3ef4b64b39e7cb7a602bcdc56fc4262887a68690f468e17a8fa6554f4399545a09b5a9659092974e8e7fb0c1c81a016b66949f011a4fbd0b0e7837a100ce082529dc6ef4e142663920e34f56067e0bcb0ba11e85d504b0574f638bca100866f2aadc02001b97d5271eaf4f916f35493a7762d06f459a48b1e49b61131019c82439bc462910c1737a7cf06075ae77f24912892afcf0d4402d461333450291a6681daa4a3a16cfddad5ed98af74554ba2c6b1471e591d636320cc29172d5344f9db9f7071050f8a3b33ebd8be07d535743833499ffc25c18229b3d343a00404aac327ded929c65a5f15f484680931f8da10404ded2e31bbf4cbb1aaf79d6decbd21bfd760f2122da4a18a27435d675321c5075f1571af78e74f10b132343f508fdeee80a6e03f2f1164d7dbe9dc9366d5eb881e439154081f8ad69d04cf24c247bf5e2411645940e13471ad6eb31a54c141503cbeaeea3a75d298aad0156c32de0335144d3bac9458de18f1170828c46f65a91bff2e9d42119238b30ee37f3478a0f3a3ae9f5838a6f03c9fa74155b818c55d8f2a464b26f509226c13f72f74b454592bf283d4e962a29cc3de71001e3b2969cdf236dc0fd5330d7bdf0b6434a13c88b70e880f03931556a20a2c07af77dd9c7fad5d3d66fff313c849916dc29c4176cd3de971386f4be87c80a2e1a1715c0ca9163c09ee33b274b4a4232fa69aabde1119727c967a211173292e14c3d1c10281e8b5106911ecbf456a540ff79ba55b14d500acf9ecafacec8f3836f1b8668c1e8779224e33c4602ab3e80a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6ab1e570406223abf97d2366c929fa4dc38a506204d6ac6674863daffb7ee06e","proof":"38c95fb0c7ffef02faff029e3d4321f3a935a19840ff8b8ce970506e1e067e1cec40fb31967ae001a33a1e727ecf323749e4407f00f89615f1f41c001afa0e7958d227af77f55965fc6d5d2f9fab8e562696fc00d04e99165c5854c394b8f905904d16d5fe59fcd191220f19646da11ba388b9714e50c0a355a4de3f9837b62f9abec3de2115f33f446ce84fcc30e3a4d0a8c62965a959eba688141597818706dae77d93a9c3a6b8a9c20ec2933bb1fc4ff772812a6d5e17950e57a35fbe670b848c067702e5e153803f2117ffd44dfe570582f624aa6dc1ef7dba460e681a038a8c7fb49523d44b054285d146b5f4b84606e3b31d6b72b047846a64d608c70668f06e41c7556412192b9bb82c806b9843b0d0fb7bc26a1456410363051bfb1f7a821dba4b1d318b5fd19cae901f001a106edc99259ce370b4be440699e44d2f0c0e64be6ab36e20b5b49cba945828574abf8b4f8149b72512b9c0350d47cd6a86df266a16f5f90e236d3fae0e4c989f1b33c167568a360f12d5b60f9e098d7092a0cc816e7772efc7c44bc444192d76f9010f548895815931cd8f7f8419e74836d4428b19075f7dde25fb5d75995f74bfbe532d12185465c1d168116b1d91672c807708e5deed42bb64cd7e150cc427a9604a915a45bb5ab57a0f6ff36b2273ae9dcad57a950638f2037d7fe3e76a799358c3041150edf8ee0d47bd1f978277e0356e15cef5f914a7dd1aaddb433849c929d02b4ebf0581fe001c555e72312b3edca395ffd823b928bea2d26deeb4e0b49a900eb0b412c0a77592f7c8a2a952bc78a4b81ad387727378cf97d2ce6572f1644832a9d14aee2b6779bd7af9793ccacd8cf365cbfb3321e563686d978477b2f45cd8556865b99bc346febf110b04e5810cdb13d9be124182b00cc908dd82e462283d5dbbdb36b9ca418ee9bf950f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4a8cdf89469bf278f6473a760bd487b5be99df47acb16411764a71e1aa61163c","proof":"32215fa4ad2346bdd1b8cc8147c233646ead0b9c97f59d0b922b84235303590ae2b89a0fa6bdc9e862eeb10008f28a6e3cd870282d5656b2c38059f80312b42bca73533ed6eae728c458e1e7de8434e3b5d228f106b5a4e5247a5c32f94c43029ecdf321b21df41636d7d8e06ff53ea865534c24923183fb9c0f8ec6eacf1f122763d70d81d8a418246b364bc93eff6f38488eaa40800f1eda83b59b657c240aaa6eecd6c88e9006bd460df7470aa38240978077903ee819c4f3791319ba5b08ceb63603d3645f4717e038632fddc55f0247049696d228ac0eb66c86c7845101f24300960196ca1b368668de11042f6fb804e8d350d1c70a11e364c68f3dee2452fd8cf8da50e9f84132623fca336988185a69877177d3c543c89f9936d11a795451bcadd19c9d6f55085b597608654aa4796e5d38812a403853b3fdb658d917c6ae0f276c6fbbf3119c563471f9756c20e2448eb886cb8caddcbaf2bca3351dd0791cc2f23e060f20bc9b34533eb044b2af3a50bdccf6f9f0db4f3c84782d726a1f6a5006570ee2ff4d65260a0eff1733f0dde185be8fe2e06aa3cda9576e4292b0b1566e0ad448289d50981f33d5a20a0b35b238e3fa511d76305e99c329045a098abaa9cfc0cf3e606e93e850665749085d7d2740b825b6c68f24da1161531e53e060d59bfb7357453b9a4354b2cbe8d9667ea76f39a4740b7d0d44c0317fec63d0e9ad57e985e920c294fba505b8f90ddf3b01257bee001da3615d701468f0eadb88f7282920ed4da3eb4efa4cc8bcd595fed2113431aa9c158208ccb65c7873c4074d688eafdcdc9e955da44a01893d32d7310d9fc6714c9d33228c8869c85a7a25382fa8c857d1a7d4a4614f20da6c52d081fa3bde85be9ee506945f0cc97feba034467087ca993b7d10b3375d005534d282655197b9faf1517786a006"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ecc373ae84f0b5015c6131b481d6e71798b665770e5e95fa8caf8a4ecef06f10","proof":"e0af716320147dc85fe06130bf23cb263255eea7375150711a54c331d912d87f6c82b93d040101a7eb8df96466f066cc52a7fd4e1c42aae65f30ea44a52f0f0a32546be3952fa8ef948635156ff41c482e5e597f675ccf610d75e531a1b1630ed2b190ae071ad7ce2a6ba658b2558b156f0a38c87c19504392f48c9d6e24e9784c14732d4a193d60cc39743a880629e78f094c35bcb36456cdf16ecb8b6cdc0744edfd3fa5cf11bf28258341c9052a6bdb0fe74830c56ee33e29b47da469d50d269dfaf681edcbce8aafb6efa9adb20a6c12239f68122450e6a91d9633ab590f6cd55431983a04bb8607a14109e4e4bb9ee3bcba4b5b8c87eaf02c6d1e933850aabcf7e79e1d6575c0ef854e3e9d0892c191edf9b831ca5dfb7036cd469a7d187a3a4abef9650775f937b2b626a843f4f4e21057b6de2750dd589f8900953336947855096544e42960b315b53f79222fc75b9d34dfc6a55a15256459b5501e1daa7126ba1846104143a1d97977feb93ce7e1918e6e15611ba81c316c6cb1c72b444ffc5b10bde520ba7996a822fd55411e5e623bf53f67fb9b90b21add549e05b8c60a0123609cd07a7acf6edd572fff8214b093d52cc189d3c04184946636252c816f8103394fea5f997b8f87de3411c70f48a0016de60b1ceccbaa36f2043884c1967aaae4a2d5b392d17e1dbc26afeb0472552e1d1d9d7c80150c0c2f0b64ba6a211b8a2ff31d3d6f447b6904e91ebb080602fa0da042316ec97a4bd16c71e28ff94e84fbc5e35d98d950c2bbeabdcbb5a322076ee91db281aff80a84e21c82b77868424efc8907f998a51b64683c3ca9abbe81fc029f257b280e8d0f05211957c3f564375d7fa0f590abca9083a6d919af1c754e7a2a8f53b2427057d80635f02505ac4b1819636b6b310c0e5ce056fd1c6037259c7ab91e941935c61b04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fc6552a5177ef98e932da39c16b5dcc0261275b9a3404995ed88ec07aa7e417f","proof":"ee5dbd84ab56073a4f67b1f38e94ed7f718d6591e3b0c00220b723c002ccae46b477262322277a0b1dd9e7dda872fc7f5dee08542a8738233121e2864c488858c8827172002105a7a4bfad918ecea1a6b1949ec1159843396345fb5254fcb955ccaf5009474e46adc03948f0f6800e43a91464b79b9ed6c67dbcd02789727143e7e63a2e0b9269b990a1dc6e5bd2023691f889e6f635ac7223a412002d222b03eeb590c9f1d77285b62faa75b7da0b93fb44c92bee25859db51178dac5fa730b5325809cc79daa055b113121e70fe261cc88381c5d9712f587ada3dafa9f9b0fb62a7f43c3849592bc801acfe632cd8b21c1e3af4b90ae9408698c8b792ab5195270fea5c762ea07708b7b85887759c9b9b75264768000b8ac5795155ab15a29f408213c52aa0df7d97bf8c07a4a40e6788f2f4784c04eb066b84b981b693f7608ab6ca64dc78e84ee38a591a2e1301c7ef9ca56b964dd5a13c21486ac7bc65dbeebe9cceb4500ec510d510c0953f3bb78a4a2bef29ec968c3e3c3725b217670ea561246ad44c7d003ecf229c0f6f37e0320dacbd7aa02a886eaaf0cc8e2b004ae7f758486daa40e56b2c83a4f2b54c41d1640ee2f973dce38002f461efe327d14b7ef40977123b99b4866583a8690c31a7c08df21dbe5983437fa70554787602402e2f4a08edd6facb59c3c3c648286a3bf85dbeb53b641249920806919cf4acad46961ee2a477ba9276f2ff14ae78993304017d8c0ad840b44ef3aeb96271d6272e2585e2a16db0ec00b5a499d12f870e6b36dc0a9086c6ed4a49477aed1276c481e49478da914cc00bbad6953f2e3bbc205f1b235217181bb10eb35f09028867ece2e0741b00753ef96991bb256e0381289c2a6ba6240008d15aac3d4de05d4f2c99e2173b5eecb1f38a7908eb342fa037b19750894368925e8550d09fa07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3a13210abc2c5839a8ce81ef13199ecd9429dea77c317f74489b05465a4b9d64","proof":"ce04318634998483cfce41c84ceeed4281af8f8bc4dfffb20a83ce2c68a81b62b80e4f34cf0076759172fbbcf029565219c8eb0a242674771e3c40ecc1f3db54a0e820eb53dfba2ac83143e51901d93cb8cf21ec63d10e5e2a570c7d7ed23455a68d5b92b5baf77c488965f48259fdfe5c1239a7cdb45e990583db5b291b5305613c839b8261e3f9571c304c10d6eaedd24c7716c8aa779d0d27c0890536ca0270ef2e284aec24a5dc76b6b55e48eb44c6e5e275a107a76bef1dc0f15f9bca007c866adc48c31246601dc392af7eaeeae61e245a1af3cadcde9864d1efc5d50306af41ffd562ef9cef3ee2fc65a11a7076f5053acb8f5d3b0337a8d16db5673aa22a127040badf7ecf2e2211043d1e7584955a4566dbca8dfe3e52a70b2e4b59824c1a59dcc4694ff79005c296b5799880dacc1add3dd72a7039fbf379db454fd6bbd2edd87959f77287758dc32217805ebdf731810d555b7a144304116342577cb685e08ee2749efb7c0048dce61125beec6ad203517aa9f0b5b9ab946dc3542e2fecd1e9f8bfbcf33d0662fe5ad9ff8422e6b7ae2a658b8aff2cf23bc7c416cad511352e89f91a6d052b637e46732fc567b6cd6b26b918703db0a0fc4a566bfebed0b15ceaf34651f1aab0cdde618f528a0fd291239f545a06cd225345761b5602f81aa86504e0830b59dab879c579dbb1ce92d91f8688aeb048b46a74253bcace952ee6c5d69988dd7648e2fedadd7506b7d610654b185a1c7fd548c2312d7019a02738ea6b700fe48cd6d8609c265bdd139336990528b851e172f3cc16002c5f97e156aab1829f628a02a2f2ddb1e1d5bf1eb6f460c8479eef20e137cc210c10ceadc37a43f836e0a73575204cd552a35cdb4824475ce4797096bd555c00c5c1ea7fcdc2458824205983299f432aa89597925e9290fdfc6dc3e0d0059608"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5c282133cb6acbfe94fb089fdff3cc982e31e386cfb7d2fac40bc7c637bd567f","proof":"d41017469934359cfb316fcea1f0db6dd4d1da264502418d8235005906de204aa688a540669bdab123da8f163e9967bedc2fbf06dbb2a8f6e8c5662462cef81b08491567725d2af2beaa5a3a2997387084c0a9afc5c69296c36953f993e64a36c62a22f30fba675d1c12bf4def5d462783e6161fd4c6b56e429e79e3e99fa45aadc9bfc5ed01e5fc840258dc012235582132699613279ae4bbf106186468310d6569e45750d4619d9969658dd2ee6a81da9a01f86a21d3eef31cf36b00e9520b095a499b76e53e41e591f069cb2204747de985ba8b6f2f1f8bd279ec5a95d4078eb587ecbaea9fa75f00fadced1de4895371d28d7011f02ef200813f450e2b16b4a8fe0fc08fc1f1c6dfb57836f502c210280ced0fd62d696f1fcb9734877f50282cedc466b6421407d541765f2accf1887d9064b900525f27c971679fb8fc5bb454523514d51a87e0178fb468d2fed6f579e78e8c507132f73e930db5c3c6497ee5a23ff756ad0a6c45cfbf1d75644831da452c3d45f5e4dc04a4160e20cc3eb0feb5a76aacdaf682f98982dede97ba6d8c1fb0c523a3b5bfe426407dc0c82afe5a851ba7580827e4e0e47e67540120546c851674f92fd6eb371ca633f01c5f2c1095fb59885d518aa1664766aa77e94cbef01fa0966abc212cd8c57f841b3c60f7a951ce089a611a38911ab2f425320ab87e3970b8295e75a4a554d5bc397dd21a65853b0be58947c5cef13d54fade9d1325f2fd8572d7af293ea3e52ce3702c4e3bcd1812c59cd59a91e47548d3a1d6a8b12e0809e516d3d2fb21423f0e50ea28c669ff92144e8aa2f263bc67fb30de1ccf3328b5383bef8a3b7d105bfb799f1dbc9d5813ff7d594d7f9053984b8c9d36ca93c22253386dd986056911cc0d0116e15d9770bd290b578a13e06c9c1a1d5839e99ef21abfcd57297a07548a0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aefdfdc0fb69eb0932894ff88319e9d98648ef5f0d7883ea1165cd41c0453c7a","proof":"4210b5606ce3dbd69b0ad5eb9270cd8c8410527b615932b831a8c0ac51555a321061f7523e3e4227e49b458bec709fe7bc890a88a06af0225e6fb383b41b9516eead218e7ba2773ce228a953fd5a6e2b41e1c44c808c5bbe18d7aaa8a9239854c4e3c8abeb11dba86e9e8a0449e53eeda9c2ed8ab2174acec236ccbe48cb71757471a0b0d5542c0d3e86271e5a65a8d31c1c7ff16c70e438c6027224efa3f1098c7dc13c59512037a93d8f1b48de25a51efb00995df239561deb9fb8149508030915f73feb406b15d20dc0a967bd856d109e726243122f7c3fd61d65f0b7850da85ee6ad9c2bc3784e0be1a4e276437636c701bd4e865112ca3a97577bed5071e01d40909921e8b70f424479d5e744dfc7040a97c1b3ab608257a1a81099a571acfd4f5d333cce8bfc0f50942d98735c31e5e717260450936c9a8141aa8cab263e5c0f087e64304deab83bfb16637f29b22657ed522422f72e1b25664bc9a1056ce2bac1ac1ebc8a78d1ef82da450ff37180312a3ba5e3005ea850624eacd32e86731d8f14e1820b24103e57f008ba86b115bf6c45deacbdfce54cdccca5fb51de56e260815d76bc3490a9456225764bf16ae8b2da671e1e968379418d09a93f92762246b50ef4d26d965b7f960df2bd0b75723f27ade07a73887076a4ae9063564a633ed4b3eeae1a683c0f9e1c181ce31b528fbcb85e15d4c51ab46cc1ec725c41fd728997d48ac0a6ec79f429b2aa436b7858dd08ab57dff026040912fb424c07ada08d124023d0c837c5585da3c9105b73419a5b2d970131ebfad10d9668b4e2250e3202771822728c2306e720878212f4fd26332fb0d2e061f3cc9ac30b6f175a8be63f3ef8677bfd35b201bf99371e0036f5a55275a562fe0cb00ace0fde6f41076b5b924675b331f9af5e14add4ed6e6b8ab2ccc117a2446c96fb0305"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"429a9ba60f7cccd9eed671182891aa112359de0a62997a35a8b96395e7b3d246","proof":"4271cf10b6f9ce395a9e9e8e1db88f7a0955ce1bd4e62f8f4d7369835216503cfe99e693cc1022b5ef0397c896fef61e842a31f233043199da748d0a593ce93966e029f6969f796d79acaedd4ab478f4dcb8090ede23821038516b3b9380fd5698e914e96ed051484fc9b9cf818a69755f9d2300a7268c7610392e48ca40a70d829d547d005b2a71106cf972c69757f47b5b5e063df4c73b94f44075fa8b7403ae89c58cf82d5994efe6f031a9f9bb1c352892a62db1c83c550d60ae03cb8407b7d2c381c6be32fa9f9b8e8600b06b5c55dcbd71314c0283c431728c4c5ee40a4ec3e0da18c7226e15e762ea26e72aba178e680aa7908a8eede02c51d798fe7f442c193a9626710b65c7bb64ed017909507ebbbd015f85c54ab3cb606f2dc75c466e17e81d5f43cbb4996b6aa9f257ba8104bf9761e746bfab1e555aee3c9c106c0aaa941bfd9d5be9ae3d6d978430faac00a876ceb17ceab9267d4167a072128832d1a9931766da8ad651d9d65a2c492e5add194532f1a30e8581b28faf262720aae107b2d4b5c0c66b6afeebbb713e0acb5e47024408256479a398f970e76e926212acaeee4830637df592cee87b9267aa8a00b7039a459b1fc91a8c20a7046e7c3811842b10c7a013f1bb5acd4a59be473fa613eb295734da799ece9fe673b6c52b035bf82b5e132b5af5703117f7d7fd2baac3272a2aa2d1d9fa3457cd7cf4c2ac2b48070bfbe035361a398960462a73aa6f0014b0ed5d1d2b4741db1608864c6e9768a1e5a37279fef482ba8ad8a7073687026d73afad606bb56816e50ab478d9deb87be11dae1e485e7361f317c41dd2d8f7b150c9cec818bfd1f1730da2cc5c3069a818745691d5bf67c588fb45ddef85db0322f2e1653af4b73d7100b2d1a149b114374b0a0a89e39f10de39d407a3a24dbe6af3aa42bbe74a838f0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"669195354314f111bb6a46603261fa9d2b9ea464f745738b174b38462aa1ef70","proof":"8c15c790d3915dbb83963068a5bb943ab588305d31de947c356c4c2b47bc9b754c253be6ecf91df0038821ce7cca41bf13859a008cf65f6432367c93efb0aa61fc43843fdb7fc7ab6d7812c362f95881e09889f6d332b66f74c05af8d734d15f6e2b8f17ea0e812f9cc8ed8973b550aa6ce16a6f9ba15324b48606d300783c5117cbfba20df12849ae6b30e563fcf5a417841b2f5136db7c60cf194d44e3cd0a60619c10c0fea5443edbd288b9d56eb4410eaaac7a96216346a1ba8cf036240ad8b83a676a4763990af091898f5c89babf1437949196003bfda899f1e9a06100506403071a224ac2b976b2a9bf6cdf8dd18ef36f4269b0f484b349db066fd42ad0870f9c6a5159ca8cf80c9fb2364992d9f082501ecfdc1726bec083a53823502aca6e60d4e3316d80825a3e3697de79fe746ac4d5dd333ec601f9ed6e248f17c20a4cf1f0ce7671556b8cc72f2ad0babc24e6dbc4451e9b67a6b1c91679bb5f2a981295c9bf287d4f005f4c70e14d3b07f2f82c0aa83b1927d55a38e5e11c46740c53a35fb5dfaceb5b4c9c8dcadea89f510dee39ea36200ce26cdd8bbdfd1d90b2087976d908483a744f43309a5023d98ce62f116cb3e323220d8cbc9a183180ca915b877f77b046b9ce241c7ff5126189515f5761008e76ad6bfd037f1e6bec4acfb55d54246b13a77ddd33651c3009efd93232da0f26c4b97c21be0ade21d8048facef55069775845068592d101c54566fc2c61a423c2d5bb66eddc9935dfce9a223b8e401d209269ea032528e28be74b56214b9b83a7661de59c8b5ba2eaa599f28b9c3a3a7eef40e4d2a8ffb9ff03a6e6b4338c713bca82ab0de8bec1c5ff87e35ca602c391335cd8cc2928cb4ae5b9338b740e26162e0c4d8716ad8088fbc140e84c56e0501b0ecefd6dc0386d469a0bf5108d663936b4ad4961ad40b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c4ece87e987e2d8c8b67a710db9841ec23f68aa2b2af88348e1647e7e1141c6e","proof":"76359187fe2be8a75c5a76a86a222170fd67deb7b3231fb3d02b3129bc6b2335ca4334ae5b3b4014fd8c1ada74734ad0d0710d6b63cc3651274e94b55ed59766cc5ab170dfe51e3d73a3b6cfe06b78e2580a7b5ab15112f7a7f4a7d91828c463be848d410f0d2cf8df9c900debed63a0891f4dfc711a311d0aec2d870b7fe934cbb0f69c2d7991f3ade6eca5ffbeb653b8448067ee751babce60c5099ded7806e5231dec5908035b91abdd13c3042628b730627dd3b49e524c17d99a3358a8090037e01c9717255ec533072cf1e1bd0900dfd0d46f9da9f5382a6a8222c59c000ef1ce5a719132e8d80f5829082aea39539537e1eec6e156ba26fabe50c68429fa40509fbb47b7013752f2d42d5148a647da8e4263a2bd8e9bbfaf22f17431483c9548390b9019e0f083c8144344b8e273f64340fbfff840e66e2b35db5be710c6d4e8a1d72d831ee223a2f31c810690e75eae101d2a3c0d3de8c67509ba490326cb5e10c7783363193de87af67a26c286e007a47f6751f738cab58a5968955672e8df03e508bb346763c2875d025ef2ffb0c8e269a2a48d4d18a152a68fe00772991b4328db016b02f2394bd40ae95ed3fe0c8cf7188588e107778a046ed256c28666fbe300225401381225708e96e6cac484efa5695429fb21d6bfbf81ad0310b8580c786f8e284e33a4d9f894c492a37c50bbdb4746c12c2dacc8ca4b761c8a003732f656e9107a7c46d1c33db19a0107a30d428dfd6055ead095743b2044eabab4337995067a0fcaa36fe610587a68e79b59b3381e6de27e9a01a3fc244d3a8825387a5ec9c7bb2ed2d1c2c920235aeecf72308214630acdf88fd211bf1c465bea59dd24b820358002ddfa49fa776fd28170677f94dae4b6d80dd76be108615ed744b3a532d4304ed62cf7489e08628da3753828077d5855239f6762be07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3cc3c3fff0bf1c7da420c3517109f8051f6e68d3dfd09a6a5186acc6cfb62a07","proof":"7add057daee3268087fb9fcdc8f80c46dc4ee6447e64d7d8b63f67f482684255a0a43a22ed1a083b545b716414c79163132032ee0febca00374ea192dc10924674ca62287a98cd417a91ae7254d0e80bc6e5858caaaf0df97148723cd56b06336631da1117b4ba31107643ead6fcd0eb11b743ce503fdfef30171cdf280dfc18f40555de46286cfa238158bc5d3b06ec7ee034aacf4524661432ab7749c947097351421ceb6e9ff43a2c05dd596ce4a7eddc644a1926746fca0184b371a28804ff44936857581286ad31df7e5bd706348b167132c07bdddebeabe7dd56bf3a01badcf92ac61a96fb6ef08de3a9c2a5576dbe3e28d2c44cce95da88d7e9027a43648816ed8d2cd727ce98191e0861d0ab64d171110ff2ae4314bd3b6ff044893e50a0a1e567fbc9f5bba8997ff96785e830ad5115c308d838667058312e4e756c6027bd81508de15f126e3156db0a50592957962aba699f485230b973057cf447264b2059ab006a091b0f1acf9c53d2945cd49170eaa88d15f6b9ba1c2ec9da03e4c1f8459016b764895b5adebd07e4cf54a9b17ca0138d83b54a9f99d84ff222743029d100abb7895071080996f95966aa91cbcfee8cba7130a36aa09d66ea3332bfe05492253848f971dc7d9482ca812185bc7d8111cf0559b18243b8834d19e4dd9af347a06debbb99256a80c91760feb1e292e544ebd719073a04db539470527234d50fbda13c6455d90c56a574a28ed31e33820ced590a9dc31a2927ed570cdb4483c8b21c2d246431eb2968dd627de4da8e285fca7773a291c72a6a90036e348b2cca35f0af5e91c87fef3029ba1c0fb58387d07f60614c79a7bdbfef5c4f958e9403057c869bd3259ab1fd4939f049de943888c031b01d3bf48e00af0fb04c1fa52ede0d33aea883bdc0b7fcb57b310248288f9c93466f25cd6731740a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"da3089d3e58458c8c84dd72c87a8c9d408af0d2de1a680c2acf3e9ce391ac46b","proof":"50ab448b60e4164a0b73cb223e03d43b5017549bc3fbc5c63131f99a5d9f014fe8a827ce962463b68b2039fcf2bf688d713feb2c2f23ff6a179e746ae1675b354c70389ec63506c19011d936a25dc386d77446ae9314f0c8026507e655cd1753c8823988285f4dc53c24cbfdd4e500f70de5dd3260343b1d72a9d486ab6171652ccc85ff78c71134682c41c48ea9b2db8c192de53232b70f6dad095238f0c702e246f96462a35aba308416e8ab76cf3f178c9a28961e2771821a45bc0be49306918830d6b36eadc3125fc5112fb0cbbb22153bc71192e3b232571851e01d070b38a20e664399ba5f89c7acf506aea7881730fb4003fd353d7e0bc2054986624230cf4517f292986eda056b02c57c93b6b3e96dc9abb01ec7959f7dff0e7c0413323f304a603e70fb1994e2241d50738f967e7b312b6fa0ad6db978e76a2de346de7c157104868e3e50766f3231b6f761fbe6fd2cedc385650dd56a833103543e6e8e39089da27a3697132679321abb723aba54db94d943afb761390924f02c41d85e588abda525219949b55b1a072c4a34c76f8aec9ca597e6b5e862e5cf7b43449e8bb5ff992855f4ddf367014066f2a685075f8d4b38d06dddd3fedc169a7cf0ea52c1ee198727b6f328aa298e310aaa724729f415564c26f4fd2c61068e0616837cef7a0f7f9ca0d023619a304e76bd9d7ac70f3f0eef7c281cd09aee1672fa49419d847ab876e2b327239e020df09bb0905e6c09ee3d50cb756ac61f0b2138b1220c541eb907885210bc8aeeeaf54ee9637c6056bc8164588b5163fe4301420f17f32cce80976e7a977f0af1378ddce0f6399925c052b50bf76b3626b414c2a451e5da7307af0bb9ac47908c20526088862d03e5a580d4d1562722467909cc218279bdd41200037f243757bc06ba13cada3b5fda8368d44ab1c74bd3ea05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e0f815e7c0218623daa8d61cecdf854cf0ee4988c633037d3d229d52253f1e00","proof":"e6aed8a7c0bbd7b9ab06f045ba176870aa7a7e46252e889de41df1692e6c0f370691728804c1a2bfad374299eb0285e5770e1b393ad8e20c21858a5b00fbb419100bcdb8ce6fe39e170fc60337da328dd9eebede517ba89586c23c22bbf968085e724a07829220abeab935744078d8b4825128b49faf7a0ba878dccc874b3b52446175b040b76a160b0473dc2aaaa8a9348147b26cbf1ef1dc07981a33ce4d0526da9e6fb1c8105c57f012c80de0cdec5911db999266766a4afb7a18a104a708f9a583d669fab4cfe6eb6d9c5e08ddbabe483b9bef466f3e7c341a4f8f717e01ea8528b073bb5e39c1ef7816282a2bb78b54656de2b46c3b797cc7bfdb9a924f5edb48537e0be6be95985bb20cd73c2c92cf6bdb93b129c7dfb084a83cb2ed5b541b0f2b6b7671f04ac8d476816b99ef94799ff7ac980afea809e30da6435a0d3270e70639ff3df70cc341526a79f7bb1c2d4f4665574a98a7474157d4bc7537801f7a3ae49f0d588b7f74dfd958849f0f4bbc739e619d06f43d1e103b31a01a1e93b2c601538e59d437673eca02cb134e584e792be2cf9893733906ebb31c32e6f17122508f583fb8abb39cbc7d0d6ed1fa182741aa4184c874ebbbd705f70b3814a393cbaadccf435b7124383fdc75470392ab5ac49b2ecfb740a5f35cb95ee49dd94c21e7b93157cb750c59fd1fa1e6d093e127c05805e65bcaa54ecdf1364c8c693885db7cf90bab88266a0e284e8ebe908a9ebc4ebad7e456bfeea1bb7630dc4d0c0ff20efc8d74131210bdcbad218b3be068f6b4be275248880af2dd0f0e80a029db41dd8f9fc57e1452393c34903a74b4d502307cc4cc2f081358de456741ba052f30eeb47f8464ab21e3350d7a08467369c20a31057c2a0c223ced01c746b30cbb4bfe7bf51333443eb28ba4ab447f62b97def4ff094a029ee18830a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dc7f385f887b86d5aa0870d01af186c3e3ef49dd9cfdb5ff236e76b53e700900","proof":"6e7b8a334933da6030ee74ee21693ca1cc66807ccf3751f87d6fef48fac9b60fdafefe075701dec77b3075036720a4bdd38e9bae85bd15ebc6e92f12ca2e5724baef3e192c852a8f89532b04b8de8c61a1ac7c200ccaacfb21383a90b55f960bb0583811fa794f387fa0b0fc0fd24d75673739404b9b58f84ffe2939c1ccee13b57fc79a326f2229654d3b772653aef55e2c98f43da08db8d471a69f85fbe8017123f8a77fac917c6f289b691cd1fb48e2222eb4b58bd2afc33bae051663510d35113ec2d8d8144e46537b9eed28fc92bf7385df7388ce2f311b0b8f9898900e2a97d98b23e9399e50be740590749cc069f79cc257b42b67710e5fc94a5ef66932a7a452f31b34b5f4afff893618e2728812d70dcc6e5ecb1fae7889a5d44c52d49527661c84829460e07e12b96240ac1f6b2fbe8d56126297643ae438ae8f5e0cbf17a8a2cc8275c133ca940785e9a1803a8a17073cbf2a1610854ee06d0945a0a5a9e14b798192ebe4bab5ba6c6f39aeb78105b6e2e80dc036187e1c65b84d38bc3996ae19751a7a2be2cd5f5308721d52427c0163ed23be1e32a826d7c53e24e6132257b2b92ea2583629ad03a48f0f09da08c40260aee1fb417fd241071dd28a74b64e379081200b03d5b758e82f999df0cb1814346172940f9537c07d1cc81c893e4535b2893b76a147cba7c5318987ec6f1c34235c27c929ad13176d5ee6b54f845df7c13b934612876311932f5d6287d435a5a74b546c8dd1e5136d38b40b17416a1bcc63ef45658a038c4b95d09dd2b6f131849c45bd24850718a63b0e8683461b566c0a8dd1f9ccaeeb83c8f5484de88ca117d797243fcc2f4f5a33d617bcd3d6462c6207aab3b107d256240ba5f894bffd1e48e12b1ee40790870e37a892cac2444db4f1fd1a4b38fae438edc5863fda7920e05a63ac0a4d634f0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"64e3fcdfdba4fd346a2f78e748b4faabd0a688951c728988019a979b6475442c","proof":"3814a5851260dec6dc32793f42670b1bdd0491561d75d07139be25c19a4d916fdaba69056bcf3fc11ec69f0fb2f9f320dee037c9ff0f0c073d41b0527ba2c470fcffaaeffb368c4e84527b5bb7df3a1436673deffed3e12216a400385ff93c6b9ec7ce1a69a4bff4a5f6539686330569b3b7e29c3545dfb0d227052bb19d981bf01b97b3bab6ac404d6df8ae095f83a24381f9d53b124d65b5b5722f15bb3f00473235dd329a21603d9a753fd023f14a0067dc042f3bcbfadb6de09fba05b4063a06ccf8c5786848e7e91d4522ee29b23e8cede735f0bcf265606432fa96e9035808d03e5292013f4acc935563e43657634f8c3dccf988fdc1292916d7853d3c5ea33db0a13de4e47b831a95f1c8a0190d80fd4c7289fa5ec9a87433abc85b4048ef1a042ea6e52b1efd29667466b7b3680d70a56e9e5c41f46453e52b61711766b44d448d6bb556d7be9ee5f19f0d42291627ca86825fd803242161d962051c62c2f2bbcde1c1140e563abe1a33264e9625fb22e5451b72daf18e8d9d03e970f04b794dcda3c19e1e4aca278b72e4e7f1c7ad8b81c6f1266190f56a1ff5be2b6ecad93f762a1941c6cf78cdfbd3078285af3d47c387cbc6ae07891b59956c375c49c9130423ef668f56c5dfe4557e7f02509dab756d23d4248d04c5c8e1b234064b9e97b74cae218098ed5e2038b151110f8e971db0189196eb13a0663cbb0786f7a34c60cbb0ec65591aade1626d3ed8b68d83f31b531b5660d4d6b7a5da3c0e8f70a59dc30c9a6f9ad6c253de789899daaba9b57abb1203bfc0449d20620c4e184ecf1805c39c1e4a8ee86605339929675f836396c2f22a912145d1a430314471b80f9d6ce5ae49e1ea1805e4c334e1b571f5a9f34c24d0ca3b33753dbd03d2ed2e65d2d903b241aa949e1b27108741b960bb6d018880cf3f459004dc2700"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ba52f1db934c1e8829b47f0fc897b7504eeec22a0ae6426191273eef68eb535e","proof":"58879087ba1fbacd7ccee99c45dd200a892824d549ab1002330c70ac9f4c1577f26ab63136679abc74db0cda2259b593673326c11bf4e4b8c5065cab1152c948d08fad40f88d23d9553eec084d89a8b5ff411f8aaca09f48423646470fa33537de1dafed38002994b8c102afb6f3720d6f9fb0a0cfad94ecd01b36d842cd8465e35de7e71cd7172e8380136d63bc53cec5550c72786b3908336699ca4c49390a9a5e7bd07a0d9b802f7db3af26710a90031e47a1e5aaca1351bf942375f354016553b98e15fa26974f2204e25da9148d6af12f8136a9896d007191e64416c10aca533a2611a2cbf8617439fc42c46a1d2a4418b9ce20c06ff4f40b2aa49b2d4ffc2ca7e25721444cfdbf8b7cb213b813c317d90d2e6a6729d3f75e9be9b07e073247dcb6b6a926015bc0cd5aafa5f9e8e276951da4e6c182895482976516c57caeeb0d53042e3a2a6e6f05d6c272abfe7db7c62135d056a8ba49b444cdb9380044dcbd14fe1b2e2392b02eed5dbc27e06de1ed4ae474653ebfa6ed133048995f6004b129c3608cfd32aa454e23616c23227e9e9627a330dd932a74236324265d38c739b20807b2c1d7d5293ab77c0dc081053dd4187508dd7bbb64515dbfd9623a4c72c1c8b24b5356845fcb2b83513a55de59ffd3328c34e49e32505cf9f777728e06c18bfc123b24a9e946d7c540f0e98d84bb81b815f1b66a392eb6897e7cf64fe9a2443a271376393087a081244ea3df51f66093a33ef80d06c029e40a39461a00b29013a49793069f86432722ef7eebbb956b5bde9e50f055e02d5a8173d818c2d2ece137bb4d13dcbffd48cfe1b47d69415963ca6e96fa40b4cd5e3648e3e0f84a0917199d98f46790b7c2aa15a0eda483608881782ce5279f2a23430c63e64348047b84d98be228da69d5d0522324eac30dbb4ff49b8682cfb7a1330d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"94a515fa313d79ec284859049b0955ed180e51ef7887979981ca5a9e4d022c4b","proof":"4ebf1babe0f6224ebe931a322c4b39609d67e69b1447163353d6473d8726813510af60f0dbc5f0602c15570ab0088a3da5c23cfc8e8d42c38ff429be52082307b4ce1de9df93c32ca83bcca3e664d14c6df1266352efe6e126ca3c45f1ef4817ae588f0b6a377c6cb51d60081a58f5b04c080ee04db20ebc221b72f7f977ba24df6dbd3563c32d05803d9c9a3ec58e3215ce8381bda45937e692039d1f431d08f5bd56dd1001d0bd00798c58c92f8b24f8b076031dffa796450ff918cd5e480fe5ccf342c6d29299014d24d1bc0bd69f1e00f12da966d83c598442dafaa3a7096a0c9d165fc840b33b36a77beb9b330e3f4bceb41e5d501f16922d546190326d3a26db11d3ce2322b01f78f828df82691d16de4763c2594d291a6471a528807b2a8d6c4bb68eadd700b3f4d2505767774725b9ed3751681c0fdc3de7db21d26eb4d00af32e3edd225f8a83e6e4a238df43603714537908affc7d1dea3edb9a0062f4327e31183ad1a4aa4d133beea93c9d112f5d2df28600ea9c78956a646563ac6db00592b5ac060a32e6eb6a3ba79acf20cc43f6c4388307a32053290a9d4018ec3babac78c723b7205af70ab0356d0790002375a7a9d1caef44766216de31d265e068ad1f68897b137b8597e571b66be6e77f81e8f389ecfab0268d5b630d0aa0f185dfab525440b0a3de4f4db43b13486c8070b1ed89d17edad797a5d157dac5a8feb9afb96d01513e961fa5d3e7141ecd2c735419845004ebfe4438d21f9a5ed643c1105bd4c127d5c8dd08cb84ab3aa60b8b39bd6e9712c07822ede944e4feda524310daeb472e8e5fccc9c39929765c5727c450a4ceba0fbb2e8a546b291cff27c013ed8284ddc87425e12044f96e06c86cf13e193aef5b7dd95fe903e8f61f333e29263316364c9ce9f953950f23fa4dde44dcd3508d02df1f1db00d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e4a51f4c55504c40fb87b9ee82a099dfc5e38107cb6633b253f3ae51a9ff4263","proof":"9c2d7d9eaab2d9dbf7a11dce1fe972ac2830faecc199d3866612c852a29729350c2fafa21e9cc1a5d6399f8ebc7545f2b6aec12adb4f2d0ca67c83db948f0b735a8b5f31291429217e877e1d9b6e3d920fe589ed04434c53afe868ca1bab241cc06687ddee62deb42863ed398b956ef1d12128184f7b53b0e5efec3d0734da4f0a7c9248c16ddb04b762d638ae7025e637121ebedff52e90be367bcdeaec3a09dcd474155401cd33efc8c68403cd0c19f43aee06eba0735519570111ceee1f083bb2695eced2bacf27ccdd81a818d6ae20a2cee3eb75a9af27d29acb813b0c08b87afb9212c8db2053b6d2eb6c689d70d66a6c19a2d2e5ff2cb3e61109ad134aa45a0306479d0fa4982526263d44d611f6067afa661d903f26e89e3681fd090bf2228c4cc6b5de96291c11dea44708646c4206abfdd2502fa2c2f80cf1eb467b2ef1886025602e723a4dd4dade6e5aa23d6efca9e1cd46f4e8c0f463ab5ae26348b22a70c46d98020ec49d734c89a42b3f973f6464ecb23deabd30ef06125e7c6840b9689fdd75f579b81a022f112c736deae1a5ca0e545e49ef9aee3b338d225e15a8c8be8f31fde9e8e231442c58e4bb2c754ee2dfec70cd095f968dac8065e4383a40c7100ddacf19eb630e242860f72f8fe8d4b38297d38be2f50d457361f4b0aab90fcb1bd876f0024686e4d0815aa264f101a786e7e17790ae51df1164924199abc77fc86ae6055510cbe72c827859872690646cb46709f6bd6d63c05fd252c3db1cbe7d7752d115f7046f1c8ccc8270707803f057e0328e5d2dab651f94b665fa02e11e46b147c85cccee9bb897075e83a6c81da7dc23a72c18b6303925eaac4c21c4a88c6126def9d451b1b644a004568288b4b2c2c73d21b448e4016948a8ea5a5764a7beb42fa5946d7a1deae27c7151274ab76ec2b51f528a2f0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1ea26cba52b30f3b17a5bc01e0e3e34ff9e18bd3efc76c8e3aafbe11f0e8ca42","proof":"828f1381985f502f504009bcd58c16ea4573baa5c954d3759ead8c467e174348089e7e7267bf1ae9d513062f3f6313d0c4f9c94388dbb1bbf7ff6e2cfb3fba7742162eab696b1436d3d4d326e2d44efa72d99f62d6ba3c817894fdf204427f318a30093c2dcaee26c17eb91d2f03697cff09add3f25e07473dec2e9c3415674d2979c8bb48ce84903ba0cbd704e9d1e690305944924cd331e4ad0ace2cee8102af9cbcee7949af05c8b71ab03f02852da6dd6c84638fdf9bb89b3ba2004f5a0ef1e1f579754426df37aeaaf3e5526bc522e80add2033db05333843351f3f4b08eecddef71274d7a7afd67cab2c71ecb256aa37100229f2bfd0fb4639adbd481f967da4aaf3dc545d2dc55658e62be5d06759bab638af5898f813dd7baa00992a10f46786b85b269c13d09fcac47e7f1eb372947770a747f44bbf774949b06b462c05443a474d7d687bf5f7fe8700c3d6eaf602d480cd76cdd0c2ecb985774c1e487f8537b0463a3f9753d161bf88ac435a5dac879d8fb0a7423e464bfa86490488bbb23a6caf3a85f70bf1d0b7b89d5dd716822e6be1fc7422fd2fc671e08a236e97be7de200a259d832a66411d73f27948d80154a878236bbb287e3ed960816c2765010ef00f65fe837b24296659e08854751a6660fdadf6fa96a68376e7f3092ae341868a3f2ec0959f2be133ce429f0037ded43fc707d0998f4b353513a41725a552e49e347b77e6849cf11e33827ab25e6f646d5387e1f6fc5807ca5a72d5a2ce6b737fb93abeef230d25661e8385bae091bcd398aba3be708b6319d70747a150f0818f18020d61f2ff7cf65807ab67aa2d39f043bdd2327fc2dc958144c9aa089694de04968a87f2f3b8479fe64b9456e7dc545701a2487095de8af880e50b8fa8ab66e6f7a25445b9aba2692c17e66f1e376cdfd7a5070b320d5aa230b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"94855ff72901f10714a8d77148826cde5f27316fee73ae79094938d28df68370","proof":"c2c6e91f57e39a98fab71a9262c546386d5c32a583a704fe695cb92124b27e085c22436af199c0780cf4930668404028ab4a8e7ae758111c2cf7b617d44c14027a8c13bd4069799249bdfd8ef39f6b48ad67a5bcab7ff86006626520d24ac6785a82745b5607eb5085d5903e627da87a98d633002da8cc8a7982ef85502e68527f884a92822d3df49a3bddb262e56fc615e6f3aa547c0e98f62f8afb22919f0941066b358a2c803b0534ffc459a736cbf286b237c55926d64328fd52236a9c0f79ad726bd4217c6e23b0fcbb62203ac4fe82274c55211b6be28e6d1482ffec0a225c488ac3220492ec9e8a74949113ebbe2639a447f4155898216cfb0aa9b35498927e7a262e0b5f05d9099e7fb42a2ab2b95aca6301ad569542f4bcf7171100cc129b4353632c61985d43867322db009c24cc903af00bd83d4939c81b03b563b486fc13d852ba0bc03c5c2376954bde5e007e1785c1473946c984b59ae39161ec3d8401907f9bce22eff45645c4ed3a17bb0902186ef2a29b3ac72a5ac9535d30fe94557acc15ba297b2b24e5ec158a1bb71fdfe6a3ec905240cb5200297c0d18a9246ef208c92775a4a56114e4f222aa8cbb17971a43626af12ec1c640af69ced2e8a20a6c6128d3aa994066ca655f338b1355bf874674bdd75e78e083fb0d70af6dc0414ca3300430ed9d619c176d4a975798bea3f426009950389320e2630a2dfc3ed2faccd26a3becd16ac457778d337d40f688999caa719e03fcee944338c2d90c885838d0f976352b7adaf7c996c729be913ce04f8e7e27912c3edd449efe045fce7b7e8f9ca5cd22948b2b9c6bd2f1513479d81492144e30bf1b38324e216ab8270c5d1fcd184685ea847445de0c76cb6ca6d857516710ebfbcb380bc4802dc27ae28440d18f127b145f64adf89e18558686a5ee3f504c1bd43c350f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"94df9ab294b319f3f09ac0293fadeb22b5f68442aa693e0b9988c9132afb370f","proof":"da1d879a3eaef215a75dc629b2869194e95dd927028563cdf4e5f44a5ada243b402d83bf154055e25d534cc1b75e5e1f19e5d96872b17f0d0587a8c90e704869cea78221ec7b4762d462f018dd9d1e8f719a870421642768f18f878fb99f861ba6abe214954ce5b611041bc1a1085c8c9e6956083cf868d4b4dec5508ea7e93418cc2a6d01649a70fb027fe11a63b4b97c8ea306dedab8db9bb81c8a2a63fc0b0e5ef83d835c7b8cf829afa6eb6e48f26cbd52c4234d26170ecdd4789abd2a0f12d64ec78416553e211709db62baacff250f512055bb2072a3f1c17dac72b2014e1f39584853eb06c7497c1a6b57f15df90debfc606acca7d07c08f5ae707b1d0669adc25b5574db613ec44f64423d54ff4859baa7b731224bb79ab555067e0f6422f6f20d828ef492ae40c0a326d1e6eb0c9d383bf4a4494c2e68019061aa49f258b92abb8568052b4b5075742ca6463fb82169ed4ff5001cbb907194195843fcc47d0516ae639d65920172bac6748cf868cb9775bc03fd287d060097d90e55a68173a692b106f9ee54ba7351a521a30ed737c02c357dda330e5634cc1f7234220c522ea868503ed0313315fd96e573d8eb4f3265c7799a9453fdcf834ad54458e6f946b3497417039a93448aab6495189f2468a48c01fb92bb099da4b5f71840b4ba901e0dfa9fa4968d8d7189d39bc94e11c97c5befa4c5237c1751acaf4954154d9e3a3d80ea1b9837428a9c4e08a15edf2b445bc04197f5907e71770128401d0068346067deb394bbdf7162e596350e3b4239ce7eb3697d3b676919e3104cce635b4ca5b484a2edafcd4131d02b260a7c8e2f0aeb70f940d6b6ea3eac4d7279afea07164b90732da7a49e7d4df27287c3ef9d76e6dc6fd52884989025064370b5e78cc8c2937b1f3f26f54b9d1fadf0dd7d72b8dcdcbbcb74fa6de0070c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fe0a61f278d832b0d6483dc49c8e290a7d58439d5275e9551beefc13a15fb469","proof":"667ced284b99b9a7354459a6a1549a24d31ee1af5d4e9f73d836d637843a4d15e044d753b74fcb73ead90c05b609a0e358b6033035ca8bf461c19f0523ed742550fe3092155554317e256e4e06cdad5c06b098393161d5faef243f9538f4f84b7ccabc787117a554047273d9d7f8d09e6ac02e49bed74501958be612c4a7a91ea82899705dc209e5bd0bbed9349bd0c5cd8223e34d1f145343b6dde3c6a20807e0b8594c06d277ca76422a5d68db2899fa4c42a9f2e72cfd4827e3c4ed6bcc0ed4df4ce052e169d5d27dc75586c3f678c35faaa6b365ed3d283940183afd5a0b549389328c083c8a3e401094c19ef65157c7b291586074f48a24210bcef1867bf6be0b46532cc1be33de95751327fe2264ddd2dc7757160248ca25facc8674451268f92331c72c747313210cddec268aa421a7ae16ef7e6d4964ede5c44607522cd4470721b0703a12504b362e0523fc2a2e2f2cb683055ac3d1e4c369c9734c169e60aad8dc8470ca50d02f881d3a2c4c53eb536295927dc50e314b4300f31e2e39cd169151a3a5ec47490fc62714d51d71d49d6dad6810cfe21721cc4abd696e020b4b54dbff93334ebc95fcc8839f401b570d17d63d49f3e098b4c74b9f3e668c950fd0b07478c823d1cee05e2ad5c05168e84858e825567b003904a76802442b3fb4dc7d57111c1ea70ad6c9a157f43ad18d6bb6cc6065dcdf550ef24c05ced8b64742eaa9e6926116214f2c649b80dc308739c6ea4a0e94ccb2b638400cde289d1152123963d08d08ba941e7dd0f1bc342919bc4211ab6134245e2d8c39467f69d4f357465cc3f998d0b48c805ab1775f785595485e500789645490547529ca8ce25a9e2e079fbd871db6c928268d5674ac94e4665f8e8f94311cd65d0e00c80a030d91848d10b46a533bf3e5fcb441a139849c0f7d489bdcbf91c30905"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"42def1343458e0a826ba5825c91527edd71986c4266793124c2b3b6163cc7165","proof":"d6798f4f6dc7985b76059dea9db267487bce6c793b4e2383c4ea216a21647276a68675f2373e96736999944f5b58fa308f54b8118277553f13bbc8ac192d3d7e6a1ffa3b55ec0e8c34de7be43c5bea52dfa846ac82ddf673107042117237113bfe3c34231d7398ec86811df1fb70235ea77b849728c2015a1cdb1abc88a4a41b683aa37d7f6455f1cf09a486f48825463b0cd9d0d5e8570641d42e31c04ded06970eb253899ab99de57efad0f14e94d30f4923c70b5ab2e60c18eef8c4f88402e2e47faf206223149dabf789b5012b8c81e31335bb013d705c2be0d7a14a20091e4288db7c2f8d58f33e57b11036411ca63ec93cb142bd870ffaeac87766441ce8dd74958eb3cd771307ba6343f502fefb87228f97c0c8d6376adcd92f88dd0a0c52e5408a1f59b219e7685e414da6726dcabe30839f1f19bb670c88bf322103b0e503cea56aec04527c2fb1bd94ccf203bbe8c03677b688fc021cd7b75d923780d12e09ec84436e2a7ddfc79b86fb09fde17dfd884a946a28f647a46f7a3c64b624162be80cfc7c6d76b86c84726a27875992bcc0c82b094e529175fe50f62e48a69fb1cffe19611e43d3c3ed52fb941515fca398661c6aa123c8c0bb925b4fec1bc6e79ebe5c7ad306bd102e572c75779e6563f8ab4d22334836f02fa58f7eee4a0cbcfabd2230c71ea5678fadfcd37295bb767ff083fe6bc8b1735c21f17cbac3534b7f1a1d8c18d276cf29acbb201efaa9b579f2ceff63c9db61d258a63b5690f739256d6454befb568604ad3a2e3fecce1bd17a0a8bdf6c14992c54096a50e6bdefacdc6ffb14b700c86db38c374d7eba2f8334bfea7e4b264f4d6c2b245765e480612e152ebf41d8f94d689e852e63efa32aad772db04a42be99699f0cbe63f3d6c5db17e440ca06a0261040b462e3ffd336041acd1a885905c202540a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a2e121d609dc7b384d66a963a039b6aa0857bfe8d4a754d0647fc8f97e6d4d2c","proof":"34a536823c15e3a4d27ac5643e767926c941c62f25ecc63e9f16df50b8de402efa91ca8585bc0ae27f79287ec8a0ef1fbfd40f18a891639f5e2f1b9506cb4b27be7d49ad9c02ed05a1261c38b0e8adb368c8bfdc7ed66f20e11e809732101b2a267d4e9cdfc7e169fb7f8cdf8746fba748baf8c2db3949df0df9d258d214fd2612542fcdf3ff2c8c68c7c05193fe204ef8cb6647b2807a16cd09e8fd7ef2350eec582652335ec578b5ca1f1deef45910bc056fef3a3a5e4e3321e9be4794a30d6c5d6b49045822aa86fc06e50c2b6badf54a8759a85d8ad209c7f68ce85dae0eacf31e0388be01fc4f53aeea61276be918ed63316f046ce1d2fdab8a27270e0024afb64d3d8b48e4861deb4628bc0967b5d785a0fc64e0fc24ef01d4e9c8880f62fa21774d3b72088290c3b7bf7585452e5037270b5b3db1a3beef262823c3686031a0172ebf61084c8be78a100a8b84ea15b0c0ee06a66ca966624234519e6bb2eb608f2b84fa9e6edc443a1714fd02e57d10244740f7f3f32e80bcde63607c62f0e8bb6644b69d87284443660e3c01511b7ebbddf7a17d48b496bd33dafc16fac7a712b0c26fdbae3f4d74846e058519442e9d70c2c5a5b6fc8aaa81f4410f7c87e48777cbd33374b4ab49278828c96dbfa88c62ecc656f0a8cc7e7b383d5626cc07be9c644ea60440e0c3ef6eeec57b84c6049f3f704327e56fd3a89b8953c8087cc862163d2e750b5e6acb8d5ce825bc3fedcb48d8913628bf315088b3521c4bcfed6f75603b8f0eae499e8d3c3e1aa946296fe878941db4a2fbba7c8c5ad27deeb48ea8bbab8063fe191544d8218aed5998249b64067b87ba60ceda3e06cb75bfc4db81f71a03183974519d41ceee2e13c416a5dfd4a89bc9cf19057f0a4bc7fe499778685a9291f1c75ff9dc2d7f7982252ff0eb7748c8037bab282a03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a4363e2f2433f900d8165b5bff85daa6191f3328b5d3091801049e18c89f6f26","proof":"e4378fe117a3be4001fdf902ad34cecb1ff8a7a8c8305a2618d361235f95bc72783923e0f41557f6ba4e6925771526bc153ed2cbe8248a469fcd4d9fe973d220beba66988d3c6580b6fa5d9f15bf47bb8219e675844c306b94ebcaca38dd881a6ee150c87adc5c5401dc97c7b4724f0eb703d02da32d4f7d5361d344da01cd270e6f89e81b7e923883038a8d4b3e0c86883ec1608d411dfe6e80e6ff2e051c0620c39384370fa09c9884432d7a1c188cc5d82ac0daf2cba67eb95148fff70204cf62cfcb61c6fa7f25803f5375d88c1cad6fb11610758786515cae5b8616540922dd7681f1e9a105144d9addf80ba6a948a836e69cbbddc46dfaa85cd5c8e351f2195a5d15e1a045480ee2db83fc7077032b0198b8d1e37ff3c50ff8626bfc64c4cdafbb05d75663baaf835bc7cf7d055d9e61b5c9b3edb5db1ac3c801bf22691e61f22c7304839237c0d3f9feff430dfd88315eb06a25aaa7c1e8734fd3482700b73589600290d4d1db32e5561fbd8f00f6ea684ba75c37b80f3bfa03bdf7472ef2c6a0a0039c2fc1da4cc1cfc7dd834c482ce9ecb7f5d143649ad15bd96527c4bfc59dc1e83c3e1f0c4057fbe3757442cee8d8548158887ce68e28f2926c0e0a72b2b277e3e92e689c94ed0c0f97ac4f0e799eb411fd7dc11f69872bf235631cbab2b67d9cc6eee7eaf147f45964526481542ef82086937ea2a58beb80532ae4cd879f6fe00bdb045575568e9a4b237b68dcb3cc025cd96fa443dfc9ae871a9c3110e0ec5a28c7d4cb2a37476d46ae6a226c259331a8801d712c683c4be1119ca442193f786150ec3fa17b668e8a6004ae81ef447eff7bf7b26e2351c452225ffc89b0a935ba4de434b9ebd01be1f5c8acf5bcf7c8fc38d6e3c1da7ece830f0db71208b31fbdd08cf0d277824302aca2a4ffeaf51463508be240405c35330a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d22d49cf4778a6f692b25ef5f16108410a4628b5d39ddcece33365a51fb5e37c","proof":"b6ffb1dab1b3287916f1120cca8c1b3291292b32b8012dd69e13205b197d062d52985c60dd26cb91c71ce9deac90b0b654aaf9a7e3cb460ce890174a72b49540aada203dfc932cdaf3cb074417e1f12623f4901d4c7389db3d9d2163fc411052f6ede1642b76f6a4911bc0a4a610f2ca1654da12e62ac0ad7823296fcde7d852c1d3959daf9ea61bfbe97db1a75de238b298ff35c08c7fef2cdf4d377825020a37c8362c57f078d5f94d70e145d7fa42cbc182b7ba150b11adc32513e44b2207dce6c0a8473bec1b9d2eae488ab24fa6d6341aae0f4d91e02182c8c4ca1c01007e2d6a5d0345a9b21a53a413b154cb4e444c2577387a470d1dbb24947e84470774d6d8fd3d2cdc7a05a4d3bfa471486acb24f50577cc1a588e9164a91ee40b788465680eb27ed2e980869dba1009fe0bba9690d7835fcaa735231749e7954a2fe697111d67216a9e60afc26f5920bf90c4dc123f431432fec0200774a677233a10d354d117f4fc02e4c26acd23d6c66066c6d03101da4c180e7364013ced06126e9b58983f5880823716da2c3be0c0223272f5b42c6ed94d903c5452348ebc103228edc878e353f4b0d06108cf7c5040bfc2ff0b370ec75aa1ca32ef72203844488436cad3a1a4bcc65533875437b09d906b6078ac4e99d1f6653bc0fb4c495450715ab90a0beadd9fc682cc22071b8a7a7f0ff421c19ba6587cdf28f511615b5842b50e23ecca0410e604a48877e1e7c40d14859eb6b73626d73cd02c97665fc857addd3ed020f9c9b5c331bd85512d7ba3e66ab9669d61ff514b9a19597a74ea5de37638b04f9c7ae6806afcdcbc1507dd4eabefdb06de50933ebf2bb32c5b32608e32f9417b5a190ed7c1b512d96b4e02a774b84d472ea61f16de7dc57a008dcb9f62b87f8d5f70b63c6de3753a66969ea0b54416fe4091e13c194bc3c20c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f2f4608aa6d077989abcb5f26da2a7d35c3727e96f75c736ce5ffedc9ac2b76d","proof":"6e377684c6ec22ca5ca9bf111ee2002d809afc4ec23375bd301def41963dca59585d387bc82cad7c45e8d578f060a3e4f4bf08b5a4dbbb09f6fb7a31a931e162ae5487b81a0cb98f066e8321a8a6f6cc7b79303c5b1a8f37171d5fb82e0c3c78627a75fcac330f9cecd6b2286c071abf2d87d00dab232dfaff0572526ce32f77bf3b9de4434f27c46038c6f4974d35e9bed0c3493eb1e5a174c0b794346ccb0d0a5cc8ec9bc47d73e55433ceed896d24ffbab72e94b8f5b9cc1b77bb094cb304e9043379c58038382914ffc55c5f81769a2ca45934e654e36846f2b8d81ce8092c4a7d3c00d70dd1fd9981fa0bab180837535f6a30e8a928bf1d082d28114f7d9e46c60837bc043fcda3b6c400e636ab2df5d7deb101e5acd4d5542b2cadc251464203bdaaf9c685ef488a22500bab6646dc3606cecae7558531c6fd4abcff3b2665882df0a35dd72eedf62d1b6ab7e6f73224d6387f2483b0f9d516413bd81be0c6ada2d59d702a27420a1de2a9680ddcbf001b2d026b2ba209517305b5cf6422d1003f3a1b8be9957572a7e92cca0f5493424ad1c7c108da17cebcf452201058078175e3c57f58f61e8012086ab3d7fe1d991cf94ddf76db026fc63123fd1d86aba3f8da14711438b029b34e509164d1bf1dc6cd18c74843bf04efb1bef876c07ac8a0cd0dc6ecc0a94c0c7d215331985f6559c835e30381c0e3a7d74e2561a623d03fc743e0487f432676dbbbe4e4599f88bfcbd373290e4a907952745862ac252c648be0290f7508680292154468ce7882017fa062a51bb2cc84a1e3f21b926a8dffcc694ef9e5335add546cb9b93c2da4fede6617eabcfd2b0ff0bbaf40fd2cb1eb5aeb571efa172077862962e2333dea43545ce94eb1778132d1275706d7861470079224649b84b8b6a3b59835d78654bbb66d15a54207f8298a8de501"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5665a66fc61cc012f88493d289e83d68c684510b4b019f72c2f7d8ce5a56105c","proof":"bc816a5b1c0bdeaf16c39036b0cb779908279f46e1b529a3967bd7834577145084b52d40b875adadd3be146e58f3812ad41a234aaf5b2cf71a0934e5b8174426246e2401df18dd4b0cf021592f28d40d7815d992000c2ba893761b77d5e5a1330a43d8a7aa926b5fd386a2e516518b7a0e2335a703aea683f59b44d19dd98c188d001889836dbf657a0d9784b14a2d686bf409d414fadf8b7f894cc3fda2760c2efd34a3f6d84732ecb2b6301bb3ff815295e87e32a573cecdd9fef44223180f331a12eb5d2a98fff4de14b513b3b9cb7edf2fb6e9f230c732e9a7820fdef5015c71df21a8bcfe9ac4b43221dae5def512fb5b69b0ec9a4b27db0aaf86451003b818dda4b34d50aad0cbd1205461f53abf43067a1c42fc3ecb6cb9aee74e7264340e533205ca945f2aabbd4f87ab67198ab632dae3f1fd2b8ec61b68ea71a417543e2b98045f7fbfafaa33e970c54da8293c0d20d92de76b67d55170e37c8b0d08ff39fb58d635a4d84a57ab9dd7e98a949077a8d4749e0ba68c5de13af7bf591457d351109c438e0426edaf9a88e3a79de9e19d159cd30a35d4f0521630c35512d1e88450ddc79b3c3d45877ba28230cddbf867aa5d111c052d3aa09d5a5c2d7abcb0a482073b67387e54ff03e8069c4c97659057e0c9a7ae140772f503106bea06bd70889df8e913685be1cd20f0a23d8104ff6a1523844595f48f15cfa1270a7d2321ce1c8f11121ef17372629f20d596b4004494370043435721b45d9c75744f3637bce8f46e582540c0289647c12f50de1abe9ac2ecb94aa84ad8620e40fa7bec8c04f44b26f5f92bff0fff65d16a0aaefa6f833d3f01b5ec60d60a4e34483a09aadfa3dbac91a5eba63f5a71723ebcd3730347d4bab0316010be47610e03096570ff983caf19cba0204761acd0a8707d0d27255db8e8d302222310d708"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0a3a292254dd1410446446311d75da35f4f3fc0de651b04ff78048ee5c273648","proof":"2cac6fdec872c5069b91d4fc4743478d5f44e6489bea41f579a71617834d3a2dae6bec2652fa5c96c603d20eaf99c7e6da2ec261ecfb244d8bbbf08c3c99b30ecce73235fefa677af225d8ca094750bda3cc91651066a929bd3c314eb0d89857642d2aee5ac903823f53cab5742391a9102d9284153a2ed07384159d63f0c35476ac3be4cb99f20683200a272e394f4b0201b266d6a0d08d67262d6718723a0194345ed082d56fc3ef33f52b9fd918e0fd8556203d770dc4b3cd12db00312b011b7655aa64b362d14af19ae603344c00e6aced84489ba7745f571cf4729008005c5d5cb368be2f37eabdd5779be5f4fa96bff1e2b1e05cf055886ab5dd231204b497d8c5f27469b1613c072aa7a8a51417a214813d3084e2104b7294e3cbc57ba2f50801ea771caa8c541c74498ba5e037ae70225e6fbaec11b49cfbb1d8c102063e0048c0f0e6f4fa0ef3fc07b47fbe64e67f1d41b99bad66d564c381b11512b4ef2ad8152ef2bb40a8d9d36a9f3eb5d04930c1e2a2888aa417f3c6b454bc79cc8930adb18c1939e80fa0238c7a0f07889f15a926515831c9276518c32eb27d2459eea31b5a40538d9e74fb85f2cb7bdb5002bbe0d55138f458d644e51ba043fc1358596ff1d66409c3a970fd928e2024d40ccf10ff562d010f117e7a15727656d5de195e1ba9d018af5ab08ddf4f7c12164371a514b609c3130c0d0fc2912216457a6561bc4288ea19f80c7f6d62bc134cc8783b8ba58c5d85022d2575231ac6b47fa74b90271c6f339592af0d8dd726de1dca17afc3c7abd68e0e74796070acdab48f16f4e337183f761abacf4da1d521f18dee05a5b2a5b5701659d36915fb54c4772c7c41d75940ca18a0dae3deecddd8f06ebe4472fb92ccc424ead50b84c6329c2955a362625204841c86078428f421345f62c2c518d44d383d9e0c05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a0de48243c7e8ed8538158a5081de9f90fff3f6481ed1ba576364132a8e9c10a","proof":"a244306fd6e48b99850978607f12a885b04542a4c9e2b1e1e3f0683a558bf54dc051fb286240a5fa152b8e04070cbdeeaddc7091d5e1223be48091b708fb1c796e9ef4fac72b83d40a806cc3146469ccb123367bfac7a0d8b16852d628293c0978ed7fe616b522dacdf1705ed2dc58e92f216763b41f941fb33ca306dedb5026a4cbae5eb27b83bcb01511669188aabd5070c1d4d00a6403fb7662d008ff8d0c9395d0ccc6eb89209e5c0620342729d4906d9cfa87b7ff61d4e7ad301de23d051375f25f592bd12edd110887fb3139107aa90dad94f0973df9742d83db4bec04e2802ee5aa9948a38e1b25925c23b7838c1f8fe078a4175bfb3245faa6e662323ee5285b623414eb112d8bbcec9f7f3b331110a9ddb6f18b41f07a19fa8c570aced0954348fc201968ca50177b01f8cf1c22789a288a9521fcadd727a80f7e356e456ebc4d23b5454fa093e79abafc8a23a3f2674ea9b005309a9bd89ef60935faaf0dea4489273eeb53afdfb6c9a25cc0dcd88530865aab6f7beb6b794b45226a4cead1cc4181c7e567c7b2f1cea20f5391614d378c9017e7d6647f91f21320820aacf3e04d9b5a55663ca488cdb672ef9c5bd1430da6a7ca42da1bf30d9437dae53d907994e01c44380c4888e2beb7bb642690c2c73f0365b49b244a6156667ef09cfc459114653a8f56bf7f11028ac69521b9984faabd6bb72f558fbc0a1ab843aa0e8b75b778860aede0a31347d813d979ebf3dd049caa286bb2577b900274734a121b9f817367e6b6c50cc8864ca499c66a73d619493e16e603ee38d505863c210fdc1de7ce4aaeaf805b92c57e44a8dcafa9583b8ef35528b00aa2334dfd8dddeea4e23763b281e739d61eb4250e859c16387f3b5cd94ffbdcfe6d2d04b1aced81d1c71c8bc79d5db755d3358f834406031690eb930b8f75b7fa8c7a02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"28fc553269bc0e11f53731a37b856a707a0cbfca523d04aa574a075dfc875642","proof":"cad8004d589df64f97f6b06bb3d02d35156d2efe33bfa509179e1c21274d5b6b964976abf2c443fdaafdd6ef4092d1778f1e84a927fcfc448fcf26bb4ba483455834d9727c2c59dabb71d7e3b2666574bc9c5bcbb2fe3ceaef0e1f79c0eca25b68827729cd3673cebb6009e3d36167a0f80adbcc92c2805e26941f30aa16f50f310250ca5f5881948e5b0f84ed4839ff52b47c0c3b5ea04f93a0e2de04acf00b6eaa7eec6344258a890c38043b9db0b7db9f59c84743d4b6d99203a565c726030b011b67c3b41a7126810f9274a2f2b5ae3933a93574c0c631a39417b6524b05703886e72772a9a20fa6daf1ff4bdc92c472533931fe744a8706b246c569527d763a9efcf2f3347923cae9bff48476203d6578ac30c59d823a890ca5e4024041dc2b42431d09b88729ac079903cd452ac6ff720b03986ff1cbb7d6a0f0bfba2eb07fa400871af43ce18545f7404e769b7541aa5dc4b206a7fd50a00876f2ae3664943f1825600c7d9ffde05fa5f3ac7b36b7894befdcfce1af26302bc20ade72e048e2d62e21207fe2ecce48f563408a1be4e91f4b78be80bbd9a662c9f33117eaf3eba5c44701692d4069167ebe70c1bc0e6546186ceb489f9bf1c2518040347cb591da2748ae52333be5ffdd4c97d13b0e3949c4bc526fabb10ccdd6e2926628b9c75960412980b54a462ad13f69def63871f899f09b7f2fcbbbdcd159e4288a16b0af15aa6a06e83faec2c92eff6b97acb7e37aff0f25321a5d6866ca991d4c2901720cc55c35bbb1121d6489e3a6d8081eb720c655069924b6bfd8a2ca360a56ab5a3fc8f5e844ea950f33084188e23055222b012b74ab6d23e05b22e27e3affc722a7eb8533b7ff8683031ae96d1df875da254d26f15795416b25b3020d51d61e7f807f6e562d78c713e6f1f36fe4662f72295f7158315d4ef974d24e0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"527a794458ca78dc9f9b282c55e75d50d35da86aaa66cd3fb3c54d2f71d98758","proof":"e075b04f735acb9f2d2a04814609aa2efd37317ee3c53c6b75a5eeccc927f76ee0209299c114566cfcabe3069852706b0fabe53e1eeaa7e2cdf8ccb8a368001caa1c3671099928e7a9cc1eee9dbd33f36c5e21c915d60b64f8933925f5f1247f4af5574c65a372b280c672bd788393cb5a879b2d3d761b5125c3bb276398f70dd70750153fc4d0b9cffa2ab79fd8d1ef0c3a818d1010139c1047e8bd313e5c02cf4ebf683cd9a379d35dd73365f335e06367e872e448cf01434b0a3fdf8f210d8a52f4bcc5c207958ea914228caf254b028a8777fb8d9da0e8be0da9c24f53038e8b4c1ec100ed65634bb43f6ef446f82d27f7043013a0f0a36cd2a3da6a1400c63e80dccbb9288b718fdda1eb9e6f4d825dd8b1391d0bd425acf5bd0d42202fc4ca70050bcc54c8e0d46c6fd46ebe747c5ee8236eaf87021e65d5ef00ab8440b022a747925509994642ed4c9c4edc84befdc6420088c4eb355502a7b854eb1f6a9d98f7d3c0a60d84575965e1bcb16092fb99ec77db7d0d307ddbd252e93f1ae2e9b2fd079cec8ee4d3fab5801b47b152fbdbf17fcf18f182cf65899d488b66046a6f154e0bd45d6188262bc0690fffa91cece0d8772c5bd2dc9ec65b36f07dcc98072589099e17997373659dbfb6d9c125511756e6a6c2da32d666a0192167ca19a544f45251e579d4287f4ac2b2fdf6aecc5060967d6f0070efc5435274283a60f69d0a242f0568b7db9a35715f6a1df06ab85dae00d303e230ffa41f8640c80eaedc9aeb3fb7941069c2608a7cf9797a27ae9f9d35d6e72f4fee9f03644a943d5f01bf342e707c063a7fc318ff80f696fcd107708783e402e2be3116b36d0696a587c43b9c704584968237d8ed2a745664ca58800dfdd52de59da8092002eb9595e30f06aff6f9de1ac6f047f6f88bcf2d1c2d8be591f80265be2698a508"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9ec0a6e7547faca44d3dc8d413efdca17a51eda5403de69daa790ce7dbf89668","proof":"b8084992a49f9134b0cc0412c3a10e37563376596b6889d2b3f7022dbb3c852938ebb38f654bada4e0d9a032c550178f4af87b1abdf7ea3fd19afb36cb7bc73e7223819dbeaa8ddb68c87bfd59d409b4c7bc131023d2c022e6997db24c9a3233360a08325a470bb8ab3ed65f768fb6440e1c496281c9b3c8ad499cfb1d05bf49482f2c805ee3be132103cd3a3d00ee38c72ef28e0d699c87671cce765aeab400259260844480e99efb5802966f613b186ba6877abea8d4d94e36747a927abe0db57dd1df4f3209cfc079b2ec0fec0ceaa334980714708d351f46651d0c2b070e141c4cd5e5714e1dcd21ca0ea720dbcb60d71999d21984b1abf1d59fb9b3dd0e88809ba73796aebfab29fbf47757a238eef88d20510a8029155d8c8a1bec3d3c76f6b83768d5b764d4882845388d429d824de63afaa9a419f41c486890bd03264e79a0756e148670c0a4053c0a186ad9006b89d14632c4155ed9ddfa56e6d5224a28d52a1a581c6786be6d6ace8c4ce9c020b2429e062bc29d00282649a9522f06503ace7b87a416496e2a7a944e0d43e06bbeb7684a986f22287b4bca4f6f44d24c7ceea0462fc69805fe0a522fe7f7dc2ab8384f7c64d54bdb660187fa86142070612e056a1ed741e5d283bdd50515285fca13153956cd009744eaca058407d0eeaf220aa466173e004cd4440dccd73d210260db5bc2eca89329be1a955851da11d6f06f1cd97e95b92d16bfbb67548f33bfde176164277552733ff9f88c0ed2ead99ccbd8ed8dffcb8defcc190a936eace959dcc9423133da08bc07906e04ea0120e0051e8b02204bd137dd6f9fb93cdc3a4892ef462c4b3c950f5df23441c4fd6a9ff51be49ea1d9f1a8c8fb9433980bd8e874334584319dcb02eded8c0b7d629e2a907e9bf84d3b9a0b49e77ac75f944611fd6327ee2ab788dfc3c3db0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bec59c52d2269b411e44f3dfd24cafb5a33b0fa3d02cd43e677df0f19c214b62","proof":"e8752ab43efe249c4520f673705446e7769983cd760b32fd4603afa1aef94b53e45031cda1456b35b5a06d0af0acba1916a878a20878704a1ad0a0798cd8fd11826facdc6072eb9988fcced46620892484f077c981b3066f188fea61c0f6625d7a150063602f53c19f7dea9baabc1efdfa985a574b581a1158f30f1043d42a35fbcd31b9d844de6b98d21d5d0b1e1895155720556f44480590df323a1c79cf06ed1260b83ff48dd2a3f67859cbd2e3dfc3e99aa92358e85b9d17cfd0eeb7a2023b1b0351482bc70fb5d7a33ced1f2e5969bbf7e183d8085ae2a157e8b05fec009e9c202f8ae28fe13caf95a14e51c93a41369d395e66ef9417437c1db23bf27356ce45ab5d9edddb2d23e35a5a34c1528ede7d8ee5cc504d84d103eb6e845d1084dfa577309a161751de4b5c0b597aee459054afd7e88e1b154d0283bc1d131e266988c972886825882ac7eb01c1055025676caf6222c2fd5566253d85cf9701be5ede51bb4181e986eda9713b75b51b540393a8b02a93cbc7c4fbd5f539fc0e0ec926fc288596d22c9a4f557690a5568dfb5d2279ba71ab1e76904caf66902efe7b652aa0db295a5367486f4f1936117594ca1bfdc65af7550ebbabc6203847580218d6fbba0a9c017ddd73af9b0f9128be9e152a85d1852359ac850712bb18ec2e591f85423b3513b51a50d6e83c813b09e6c2af98c550da13e38e0288203180a07ca1ace1e834d0b1ead5b27eb28adb2fb28cf0d597d2c4ca52de0c014971e494c1f2199f14c38aa52e505f7fa878338597f9623198447bc3fd166ad0965d0afa200e73f06edcd3c19359df35d447bdc66dab83ce9b130eb45299b03b10614c16440faf2b5c4d0e68080944022da651f33d503bcc605846191d01a81e960a4e98aa8b798095723f4ae51e8764b4334983135dd0d5d3932a230b262c7ff807"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c2f770998aa567ab2fd2f458018f91450798623ea6883c975437d0c67cad2043","proof":"82fc1350695f47bc4a8784d4db6e7160ec53bcdb03c4438672cf54c89b15b67cb08ed8437c4670e59d084656bf91b6e2f8f99dd9574911e42bb877975c41d2174e033b9b74f9f8dd082bef7a890ca6767a7a17adfa3a78db2e9c1ce8ac8086280650289145929f176c9f5b7d0f13a0f8fa588f01bc6d6c49bea3fbdd8bef3b647eefd8e7055ee97a4741f58d210a314911e2061e609c47fbf733fdc7ef404f058536c90a5fd2ba1a4a1766cbdd6e728876288291f42ab4a9090dcc0fd45ed90144dc2b80b8e609a54878385624ffa82f2f7b403b65ced864865b48ef5f835406aea35a7e6a3e3082ed65dd70839b9c8d5e53948c2bf4e8df8a6f06142a4d7a121a23e35e192f380261d78e96c610181a00fa7dd23e972757958fbc3b57880c2ba2d323d113cdc1a8d3897e843d9c2456f5576e6ac94bbf049f1d768a86a53d1970eea32deede449c7c5de6b256aed39430ef9c56f0292e2e94bc636a4b677a7866494c15ad7219aeeb53cdb651241dd79a8a4ef34b47ab041f785b54dbe49514b03ead4241eccebaddeb068dd027cfda2e29776b1d567bd55c34f0005d6fee6eb2a2e15f2f143e6c98081e45378910593d5a55a85b74a72811a7cfcae7685169d0c7cb9343461d690564db47a10819b01b89f050e84f99da64807caa0e26a242eaf5ebbdc572cbc9c72dd1bad99f3e32dc694eb5ce9b74ab708edad7ef4cc058f8ab67c8fc3faa5647a7eee988ca31af496bf71afeb6cb2639536bdaa9dab774be259b3d883c800451e2ac561530669cb023ec4f245fabca44f6ba5d77ceba02b8513181fb173052b6b05ea89339a5741764dfa99d611d051920df722edde325644da3068eb604382dc67e705d73db01fb16563eaf2923e7ca8adf237a2a320497115012e86f1c4577524b5daa3f285d6b05e00216496a117544b7be694ab000"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"acaba6b082db45b06e9cadfda32d412aa04771bb5a50af5c7daab268ff9d232e","proof":"40f70a5e32fcc4e8cc04eddf8b00137b6c5ae8ba20000a7d9bd2c8b15e753d0d4caf283e6ecfef72b9d9c20f320af95f2e87a185c33ba94adb0f29cb8f7f3f4a3adb5a7a170264b9724e73f32529606f48b5347d39d676f56145d7e8cddc693a70a9ed23f5ddcfa52b4425655f4539f34a35e21744270cbaf936c92c6e60f025359909ac33fec05a74a25efa572cce65985b20fb41edff61ab0a771fe63b1203a002f68aaf02196a9b59976c71314141c70fd8c05e41a9e981c368935dd9d00be7d35d1521edcdcd37fe23f0e377603fef644e853f7a226ff00ede756781f10846b09b7ee13e4093c90bc549e386185545a3a2ef3763a80c23067a53928709630e2a34b9a314f03ec9c7d81a11fc18677d7413855fdf580611aac2e07e7c4b3de0333743d4ebfb51ac8f02a0757483f2cc2c6bdaa5ad572d3aa1c5edf554c678544d691673bf942c174a13850e6163761934d13b1ab32fcabc6462c926b2437954ada1eeb42052ce9524a51a4af501a7e7c1a53a393db636682e55b1e3a3d61b4a33132bcbde1502f2287a823d3c13da80eea490803b3745ab28ad4d5756c518388e678b7509fe33edfb6d337f0ea22d5797f55153e3ad98b4c432247ad177073a6760c98dc29ea20fb8dd1acfabca6f934e56cc89bdd9758e50b2c57449165622107f3fa6e99ed0b6adb26df1795ba1281bdecaf65871296f5c86c0fed20e52946a63f03a5a9bcc0e52e9a7813d0db434827a2d21cca174618fd1f76efea119f690a3c5645969155820cf1ab0b77784926ae87e8c11abd1cda1268f3b3ad841a88bea391d97179489f86771123e265157909c11528a53912d6cf0131d245d493b043db8fa62b5cac616568968ad3354a9c9c9dedbd935bf757bf22bf31ab907e2673aaf3e18f2c62603a9b3f264721d4053a096757747e527307ee5d4e9c603"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8ec3196f12b36b339c4efe6a9a9ee1a533c20f0c3324835b886623d31afba460","proof":"eae43a6be1b6bf92a9f5a34ad3af5f1fe3fdac8c2b6f350540f3cb2eb04333632ecdda59f025732de2585efa0c04c9d876b28d6bb96c269841092f0d8973f413f0c993282e744b5e880cb15f42000919e64966e221ca56c9dbb879e91c528d1052d335ea68b1df21ac9892f6d71cc0161fc13197cd36a0b62b75b5e149eb5115b3a3d3da7a80fa90e6181f28f4028d3477a7949a484f44cfe86a7f545a786b084599bd4975df4d9ddb9e720fad8a5deb950edcd9cf75bc7c3f4cbdc34f03b60323204587ab656dcaafc9a7d98bed4ac497213c5c2ee3febbeef9b9ce9b4206089e8ebb5575e201500b74804913c9bc9079e54a5451ef948a40786dc11e0d8d40e037f3c5dc54f396b03d1f9975bc6098bd826b0ea84497444011c32f7172da2d041e3a634f98e1505cdb3533e946b962c28952b351719060e2ad1778ef65b50db0fcc677d87dd59d4c82975debb7444c825687b69781d668343a1b09ea6c9f5188c07a30f548817d7bd0efdbf19b7bfe16d087c249c7d648278d5cb72051ee05fe4cebda8c4c2368fe917ec5188c602fb15a1488b5011d5e91d931ffe729487c90f12ebc8d1323916cb944a7f2c7f33accfc40345f8b513f5567e175b70a4e43ccf14e93b804d97b2723dbcff8a0fe594d16f30ee71dd5504ec20e121788625b7edea2bb5955a5ac54f560e6cb352a600f4870591212a13b381568e8dbb8fb23d88b13ec333471e852802c3cb8558cedd22ee92b8b0570d396fb40739cbd967b30d6d4be80356ae0f4c373f63fbd0bb1b385292d16d832fbdccb0591e53477062ae3a8cad59be17cf1b59a9370bf50a9f27a2362870b077a522883e0f52ad17aaf00a25a36e786e68a17d6603590d3372a3f55e3a48732915a49a20534bb1e0800123200b3ca98dd067dd67aa9db59fbe133290132ce34fe118c2f0fd6155e0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e2f16eb7cfa642f3e599de79b9489c5ef3850757a2d19c0abfc53adba7e93a16","proof":"86efad1cfbf3d7e6b298a1af93f01d3674eaf4b6d3bb35e2d0895fb70ba1445cf8a94a0a4bf6e22524f9ef6aaeeabd2dcb474b671b0e87ca276aeb1fecb29546de27463458343bbf65ca5de80b4b8d55d574ba47b0491516d4252d3115e0f6279c9d68aa32664aab0fe0c097da1b0e5a79afb8a2488bbd0aec33f3461b12e654de1dfc4008695e89662cc501c773ecea0ef7c0deff4d3185942cec5c0f90c005512d743f9633a5da20c8e400c4ea738b1833829581018c0f8db6b4a0f6bb8901a2eb889ab2f0ffe19b3c4884edddf21be54e6007b5b555f42ddc8e818f043000c65757ed334292d506fafeb7bb152a7d42635f3703a89714fc52ef30283cc53a34d7d490a4f764be3dc0065cc409f5e643f1dc22be91d25a6c2a7a1e1c807028ec68ae411f65e131587e76ccc7c0b6b1fb61dcd4ab3c1100e393d81caf62cf2336720c97cc3cce3d04e2bca4a9168dee567eab17c83a812e228c23c64bc48a251ee735fc4b55701e37a56109e417938b4ed7536383e8e4a430031783bbcf294690ae15e74d7143bd981a30ca24a7cf399149f1002a32c1c3d3462afca0843a384455b64ffc62f8782f6d3543c04ba2ea3be3366d8fe5e1c678d33fe4e90611536089fd87aef78d4170044725e21358ed4fc92643b559a6fcfe43fd2bd126970aa289c3c4fa78548179a7a62e3f5398c744499e3df6ade11abe33dca90fe2d23d1cf7b431c4099655877fcf8d31adfb24f25a034aa21bcc4d02c163eabf435e079c1d7483c4de4349d7ef927526bbb77265ca0ab696c5c68de926608d91f259237e045e42a71ecbae2e897a225845d3e91ebf1cb1dbbedfdca7c2a0f87ad33c009789173ad337293c03d8a1286ecc2ac0f1248b444e1051dfc8087c89c5e9920090f2525a5315061fa07262f18d847dcd4b04ee8ecd6c4330b0b306c762d50f03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7c562f86ebf7c50e5b289f9fae9a1eca199df8ecbe6a4ce61d3710c03b11a173","proof":"b008fbdefb1193158e6e1dfe17f9d4e6a4bbcea69487ecd97386f09c763c485fc8fc67c7fc61731bb1bf5de3c53c760851ce640df21c199f35a94fa805bf113b2e1a3d27a52dab6fe2c9aad0985bfdebf8f0d4098ac9dc8fb533ab2e8a9f2f7f323350f727cbb4261c3d4915817d50f184b4792921ff3e6df5277590a1503f4576fa32297640aace31e39e07fb26ea38c698b9708544c1064582fce1f549d10f357d6b7c14b2ea8e72eb0abb04e423cf832c21f6752e338766cc74daddcaad05484b33a221f4b10415329a674531ae9a1413de4cfaea05ed6f0e29f5c0315d0d60700943d3953251c75a309c3408b79b7bec13ef30b8413f127485846c1d1011ce85615eb91ebd62f40dd6fc33005c870299407b43a2284657931fb3690886709618bcb9982ab96afc5617a3e40a12874229a3d99653427574cf1f37458fc86d14b59e4b943a3db154c45af9055e3fcfd7840439ff8a129d0bb6137db73e195acaddcf8152f6c59f6b446b5bc9efea69c731f9e525a1c30e5ce311741f9d247974ddcd73bab80ec84d905489a2b835ba39b50a2d1edbb3a024242a43f83feb6a0c05562d69f5a0e06b4b44143080d943aa5836c5ba27bf740ecbc0addb09df014019a6ab33d6e9526a0e9a0e7f56b39eea28e797fb6ee2717b3bd5a66927ae7e38899f16e10742262b6477d66b7187dec788021f9fa09c09c80bfe9d8501b167fa8bb32d5de56174e2a75f2c1e0bc496d8f76917a5f6bd211c2e3290cb30be2bdcf197cbc15b9af6627e5d94b0a7366963af1e0171650499321560f587c7ae095e60f6e4ab4c9b77c51fe54d52817ac9deba6c1b3b2c58920716f048bbe1080405cd72359f8c8d5e50e7e680eb4d58f3a5b05c669ce75e4dd73fc3f3eee95709df0d1d58fa94ff8dad153f387558bf1f7ae38f1f21751feab362ebd8dd4f9309"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"56504be5bdb66425fdbd2b676c752b302bea5d797512b5ef5ec6709464017f66","proof":"96120c86d849d2bd3914b4545b9385df792c04a32ed74138ccb35d907b29696966c20fae05b317b9907f9e8e5c0765c36139c930c25568ea5330bb0cea92c059eaddb8d1fc66f8b85449c992f3d52969747f1f12890bff2aa35a601657a4a034e2a5a6f972d2076900b6ac3b8e31c406b1e98c02537c8cc43425ed71c2aaac47c1ef9b5f752fcac946756c891090dbcaf4573f02cdee5106a0063453280c7f0d721ac014e3f1300a56aab6e8b48a7fff9ca1005590423c2112e78170a6b863095cbd815293068564db070981b8b669d98f37b963a1df156ce4f0a3ce2802470b8c822ba9e640603922657f7ac3c3a2bab4bfffaabe2dfc65180c78f4475a210ad25b1af69d24a7641e03b1224135a4ecf789ff5a3078745852798db00b3e13176c0b4603b41d257eda6178c0d6cdf26dfe1bf1752b6627a0e75b67b667b6b668782438f48d7cb1903bf87acedbeba275ce534ea43851a99275db82581316f848861a353c509b2b9828d62deff2f7c09c5ea908b0b4a31d5217f3a53a85c00a5a940af7b3c9488ca661e2ebd3cc49674fa7fc7e3604267fa117f6f5eb590cbd5dc8abf13bdb005742d90cc8d909017c7dfa24f4bcedab2fdcd94eb4859a036c4026998c825c5c35e317859f32bd95757358e4b6fdd33e63da2641cce93e18c975f88f1462b2e119d31a9578714a9e62298561671f3bbf0fcdffdb4db3e3753c4eb27c5e3a5312777c9d1b5a27688ebc214a73e2db11499931a74f1e115df4b344c40aab94e0b65a0cad64c3ff1eafd6db41d32b00cae7dcad6f1d0d89e5f813159243bc3338d4b568c8dcbc9857079496095d01cce5e411555619eca25757d214ea7a68b3772f49c54a888e1133ea655e840d2bc8742b6fe1f7b31ef8efbea60ee43731992af7a5e7d7303c8765ed6d78952ef0d831d6695a1d168e035084c906"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"faafbefa1e9e9fcce5291da2e8425d21769b27815e38432d8452d9ca26846b14","proof":"c241eea9e7ca41e1c2b51dd8c65dd3d132431604e067e440f7a669a2d44f2b6c60b01663c7d59c1d1fe6025380c0bdabad88d35524dbfd6d3e72f70bcd6f302ac273c388cda2348ef5b191b9b9652a62d26b86a9752b8b8d6b86ea9b21190c5e7c8917c195b78d797c37935392221a3be0a11d8d9c04c2804c3c2715b9fe8a6ae7af32cb4537b35a58a71f6b99ec0ca2e22df0cdabf2b3be2494ddbebe07e102915d1ddd4ffcc0dc43a74c8e4726e3e224b982b9745e2e8cbe147ac26aa0b50b1973c585f5972c8c6071b1d206cd895e919891cc029ee72ff2425a9dad633500ec85f6aae8cb5e1c6be6150a2b47f07249b94f92e091a3ca6c39ff74f39fc476c2ad6f933acd1098f62cfbd3e86ab6f56738cc4289c510dd22f3970039d3e51ea66f14eaa86672ac2c0d7785f7a7f03dc5b1fe489b4316d97bc542ae48cf33469a0a142a71d84b070a4f724b0878c836986d4aff53fd6c5587d74c56770e8c4e90b9158857971b3d69853e8fe171e82ef5580e119ccd1d4617e211459cb3b93b8cfb3ab97b61b74ba2449edda279834f3e31ca43290ee8210e9287b0e9497b33a259077aac44d4c6bb92f03123dd05f99158a112978338e480327b2c3ed564572e3f65342e110f0e8191c218f3408a5c6bbd8aec8ccb954c315bb020db714363d06e356bba06ed7f01baead5e96e438b8d04ed0fd7f4732fefccba4a2a77961918d9c2cc7673f11f4229eedc13c8e30a468a9a647057146f94b6e973cbd7b16a646ba25cabe956b17cea32092cf8ab205cac90bf87f67ddbffb12beff9a1736a7016799f88e9d6f0a2dfe973ad0a911a42b32bb36390a3914eab20b0c577cc34bd1df92ce79a0896624f207f586d66143c1b3e4e73be80a09b4b20a18bf12606ddacedbe3cd3e4a8e5687e2a409bbea2d091a61a0b5d112a9111dac85ea04706"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"98d4f05d01ee9ba18956240c6bcd136cd9de9ef2657dc7a9b84112c9198cf86e","proof":"c2ffc7cfe94370cefb2f1e424ebaec7fd4e1593010ea1dbfe3c1bd561905015f5af796ea9128f9d406e9c99dbc4d55cfca14b593d31360eee0da0eb340f55105486dc8200db76b0fb654b5df51f6909767e4b19331c8fa20eb3e2f4762dc5b3e88da66078c25b664ebc4ebb9a06ec4d046bb238f41de4454a15b9025cb8c9c24c809f621643f263e757a64c4d1d34f54ce847b763b8b24f66b373657eb835c0aecad09f14ec81c8bcd22af9bde5aa862c589e1b8481d451786ea8fda42a4e605eba4310f8d0818da06d3dfd836b788131b02655182bbc31859f39b1dfc74bf014849c707e826606761ab218ea11980cfe0fae6578c6ef6c77f80fe9bf26f713e468fff6d0220cbbb70697e41528dd5ba43c04ce39726b67fd60b6e544db9b3650061527f8551fcf6cecdf878dfdff613d15a992148045bd83dd164c522af670a163aa66c6fdf61c00f1a36af92d0c92c609d0296d5ebc1feaa392c1213179c2a7ab04d4ad689f4e1755f261d0eee89a9fd97229d8e58bd2190de7ee9aa8a397c3249d1195add35ff5a8570fab8c2e73d7c561931a13648748d259c8c922dee10280862867d078fb7db46dfb68469d28b391eaf77ac343dfef58e6e95874f7a726ecd4cb202bab94810c999b05607e9adc38980fb858a4389e60c2e523b6c376530e5a7c8fda00e78ceed3e470687bd981cd95b519a02a29f24ace49dbc03075e8e786cd88571210e72b9f9f3faf38b91324f18b58d2648a4587762c901bed55c600bb8a6d8e73a8be46f16f5ddc7d00715bca87639f8165a855161abebdaf32a626dcffb7bf6f5e5447bfb64cc1ad5d9bf8ffaa6e880b320e5f1d003858dca613afd651038775f9f8292080dac61b29a386ac78c18de0f03e8db33f4c52f410267e155c37e1eb746f2365c2083c79df5de6d46af94b3d62baf8ef45c9c1e3e03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e687e5301914be4954f267edae4ce779567ec3e14c8641a3f7f28f9d36731172","proof":"4624e33f3443e62509e60ca6123a82bb5146947d06501c6077bf9a6cf8f12f36de732ce50d82ea054be7608caeb456241d1fe952d44cf6b323bd04df6b97e7498844414460eba9b2d04e4fa1df5d43346794c33dfe7b3963de2572a58088220896ad989e15a4ea6c62c90f9bb0c24b7a8aa9ad69a1a421c61109c7103db51f7111996dd77b2beabf4974cd409a27cc849f9286bc06ac1776a5027d2ed1c3b5070118f529a36e7edf0008aa277973079b662075f22907f3c84db685531265b80a6a17d8a96133b64ef320e9da3a532f62a955757378ef77be19899d1793a4a20fd4fd25ab69395e87015dc4ad56175285eadc3835672c9208fe291af3d6348f74d8dc57adecfab16db040edbfddc0639fe9b920bbad474f4a1ebf202d75eb277f10d776878f7273c1f89ac32a5ddd27fe07747b70b2b5fb48071299750b06007216a1818b2e746d30da730588aaf496d5dab7d1003f7b6cad52f5fb4c0066cc7f742aa0fd4c3c47cad5fd23abf3a8358f17e7fc01548512ea516a89ec462a5c068ad3a1439fe3dbe4efe50ec98d24ca73231ba0b37a9c50c10a05bd2ea30938003e0959f8f86456737604778e971a95f06005288c733fe8e0990e2534f6c3023c5c4557c6522898c770cafb3b55d583195e7bd6da7c0109842b94a69834675650f20b745d582b021e9f89b5c8e35a8bcaaeacd3c9c3965be8caee18166b887b02b46f7c779a707dd6414fb23cf2f7c37f3a8137dc984fc88dc48797d26450630c68210b553ce1661daeb0d3f1c2e7998001077cd6c76d7ef7c8766bbbe16bcf714626d659c1d3a686e3433821a7d3d48165572203c233cc86d7a0157f3cbdaf622eb3a6f6010998a34c2ad4434128be4279ea8a2fdae5a9a4de08fe40cf5add0cf9ca8ee2565b7fd0fdcbbd8dcb82a43aa920c8ad1caf7491e5651955a8923b0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"181534c13a71ba3d1f3a64d24b4f985cc79321d451cb73a48ba561cb9e68e369","proof":"30621b3da2917f5582e23f7ed9bd11476394544708a9a5e3ecbcae547070f92e9862e638bdc4c369147cece1d40f8bdb31633e95f79ec96bc18e775d16437043ba1221cfee6091cd9a4979dd03897f7bd5b04e997d6c99f5e7471139dd995124aa10587f77966d29d5a58b2379d2fb30ada1636fc523f922a01ea34aced7267f7156fffac03b2724e1e200e4ebda4ee3cd27a8a10315a62a2c623739bd7a01067169cbfe3c816a77f1c7c3c15675c6a326cdc3d585a8e859d88fd7fa889c2302f093f1847da70f300f58c7b83b6c83ebbc8e5c5361e122ce852b9a6372329602ca63a482ab01790b9094104fc182afae84b8f9284a299f638e0dc8f0476ba51e74dc07c33f1c6d4f106bcaa210ec100f36f2af503d8a7d1808466fd4ac6a7964fc7a1786d66c99c3db98ba95c8239f46514a49ecc6152e1c8562e00f3319af07f03ec67cf29c3ca3832d27fd18679da9c3c9972eea86dccf03111cd6882eda6192e82aeeb1ca12d8fc1eb8e3113fe0d56803a0553908c09e994020136847171e0a8c5f5f92004129e8739e8ae30678b7c8e4edb995f1b3804c0087890457002b1225782fdc007f10fbaf976a43038a6f56941f3211aeaef3863ae3e65ae6bf5bd8d3fab535b71e3860e5871d47ba1772a855ea89efd43fea64155f6e084d542a42e0b741b048e2f36d811fbc3835e3f46af4df9563c79e15c642259a2c398b370c11882f1d3018aa7645a32825c5f26e8b70caaf4582bb4a332a1ef4c7074322fa41a3cbab8f0550ae16af679970d6db01d329bfb156cdf470309f4e11d4ae21a0c194f50a557fcf68b0f09251233a2c8e560f0bfaa0a41af3df1812aa30dd56b41feb98057d21134eb38a2527ebcb13572326b002141a1ea3483ecc4f32a900eb001170cf5f5d32fa39b5ad11a9408a439174aa620ab42f201a5c52986caa01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fa14fa52a77455a39bb68891167141fa9ccb3582e37c66c565b0a24f076d294e","proof":"1ae81d128dc9e7fcdbd43b0316ca01dd4ea5858ef61dfefc48b89be01a74ab56b2580cf70dbc97357efdccf901f7eab9492146056df12d2b120c95905386b962c066bcd2c2218838e8a68797889986e5d8ee26fcec77fce1f666ec89fc44f275186b4e16685c38aa7bdc6a16f1d90356d0a9b4c3372fe176344dcd534b04850e9d7914e1fc5d0f5d1208f972a7e2f68ee042276ca02bd87057980b5c64bf510f0898c89bfb2d65a0d37b5a89b624d1a07e8edea5b348d3f79910b6df71abb6089cfc6b008ae3cfc0ef702121cd92d050aa92b385a6687b65c633c16d220999099c3764e5bfc214df2304e11c32f816975edf8be698a8608c8f4b7738c6fae7461a786c9a77a975a1a628a6f5edd5a6474f08e9693cf9bccb1f0feb9203d33007249f677a9b73fad8d266508589463011eda53abad198e5f3b8c6ff7d941e6c20700db3ab5d07868529cd34e47cf0ec852c61f5f26d0478441bc8972d2a210f607c3260946393db7cc8c989d7b3871cadb39cace4a942c70cfc3dbad223a7030f34656135a8ffe234a244b7e85f99cc5c59de570e0b29b64b1d2ea12cb467442862839a3086a6b2e65737b3949b45c7bfb17f863629670ed52f45908b3daf4b309c4ae85768497d0b9ebbe51245b49cd4706ad4e39a0b86d8826851e5b14155111cb0049814046550511e0e0c4c8da18876be213d74d7e339ec1a71f137fde33310a786cf4fca2dff581c6e5335bfb1262d540e27c5b94ce30768923ab159a25bfc890eb239cfce0a173da448ceabaf54f5e31af887c2e063d5427f4dca904a47340363ee84e14972b761cd70b5551b8c2cce9728e0244d5414475e29e866501b9a0686475f7f8fb7d54a4c0dc1a0d84978401f5cb8f58f6a25b65472c066070722bdcf3c7e5f0b668c56fd3e4c8b56a61cd72d8ac0419693ee4041252ca7200a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"886f6afce77fe35d859f862fe5d0a8f46605d14b40afad1b31a107f1ad848a6e","proof":"e2d74f2cac3788c63f471ab71db08571c4b18572328d674ecf4947c79828ef2f2039192a0c650aa1139b87b50440da9711ab7944fc39cfc576b3f01420b0f337d0b33525ab5a8accd7f1f8b885eb8338ba64c86d28180a2a79df946d90bfc137185392c950c58c1ed1e0296ddaf9fb4e75133bd4db6cd4741ae89214633b2f729a35ec86086ed7582ad45072b5662aa21acae9e0b58d8632afc4d3d9f6e9300cf0aec153908c3a4530838b38ff7c68c4669a94145542b0d97570f04e0cf0aa00f276a4902cf28a0d0f3c626f6802b9cfc2c96aa63e1f7420df3f98bbf44b8902549b69bcf6ba1db05faeea01bca286cf58930512b80201e98a76407d2062154f2ef0a4857a1e26c3bbed7bb005126e6f5e17e02ad5c2c9283f4aa7df300b9f275eee32401f7c0c09cb6b0dcc9d83d52969ef51d3f524244d09ee544521dc0676f8f4e2e7427373473d016f0073b261cdad2b2b240d807d8d510b4d1bf6bdcd60142e0c67ce45ad8a7d3978a74734d1cb1fd03ea50ff7bd973e8e8a64f0b0da459a74834999b411caed094f23a066aa93f9c4381ca7a52469e0986f0703afdd468a109cd3b87b42e381de18f9b6ad87db623f32efbed17e363a651824c09fbc4556a69296c1676f9f77430c815debd0b1d4ccde48e634df0cd4cf261558f5833a0ec480ead81e192669ab61204b067ca14c9a71e216bdbabe7a63e6265e3ed52bee34dfc4bfc6c254d8b49f75158a51573e3bca73561ab45b7aef16899bd4e94db43d1e14f659cf560cc6b77c7d97671ab0d00d36f9b1c75b4b7326c5b2d6060f248646ce1a89045f26e8ae52da829fe79b76b985dc116fe2ac5474c337c1b279aa7974a1d733f0efd3d5101d72fe6b59dbfda635d5d5631958272b4d7076f00dabe920088e0235ea19cdacedd959b678118d7099ddb35eeb01697dcf6912a40f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"30d6e162c6c46753f642fae4320cb5e42c29eb3d1fabbf70da5ddd7118053e71","proof":"540a363b6a63feb832e7035e63db512a74428c4b5a55f81dd1e4305ebe11427d5e62047d9ebaa8b67fba29f610f44792f6f4f5a322db870091b28036e341500f1e0af416b86d04800a3a10ffe2a5e013016e4fe2daadd10ecddd7779f9aca419aab909b137d6531b810d29b5f44871374c079a75aeeafc40c714a7516c7e213b4faeb99a4ef61899a0f672a9d055edf3308113a47805c771832eb6c88947f20ac6cfc6d85b3f1021da82a4181ba15885c95dfb68327d1c1bfd5168e61d9ba40d776779a5223b585571223d7db0bcfaac1e918a3fd6f83167df879e1142896702366c3e0df80793eb8c883102f14cd7a60c2bdcaebfb36499af67829d61ab7f5b10c877a7e9f33829a7b9f27b692772f4b42b495f6a4fc63903a7ddfc6196de7fc43ccb9ce5af18a66e5840b41d1d5da96611a2b6778d18672d6758e91ef32f63d805ffedc98caa1ef4607e06206a82f49f530d727590b32639f237a86493ae340e44f511c845152f5d2149855c1336942493194e5bb7d2ed307db431dd5b7b29e47cc529132698407fe3a77ec81556c58c0b1cd458dfc34e32b388df8796d47e3a3e59aee0baf74bc9084196605faa391dbeb06cc9f609627a6a48ba3fee665f64f256e16aa752f2a0565fbf14c0d9d2092783bda3397d9b1f839da06765730feeb539c41756e64efe6e8e63820a212eb11f24f10d5bbaf78cae6faf9238467c126861334eabe9291de1e1c2ebd98ae6d0b050f434c7fa2e506eb079b976427efed015938659acffda7d5f8b32ce3ddc5c5efa4b6f980edb5b7797dd21075a35e248d2cb63c1367343726befba5b4ddc2c7d2057b79e3af97e22d1be58429903927bba9e4f2eaeef6a60d3661783fb3e524c1d43b6907c4f8cbe829d0a3a5106550ad29567e91ff4afa56453eaac64e8636da6db584329aeeba8fc58ceafb806"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b2c3b4a26a104d4fb99619f672d9fa263b6a6e1562a1484270880d68299b2e23","proof":"fc21d5784810f6e821e776c369c82137f88d97019108afa1799ad2034023c712c4d3b709bb6afdbc09c8cb7825d6134c2ff18b923e3e02ac3b8fda29a4de1d362c33bf3e2453ce9be915427f3ba1daefff48d78c6bcc09a23a791c62cbe09b1cfa93c3ca4522e4a1622ed49034c202a745f9c4d4e1b94a2b3aec33104850bf72d537b54b80ac85761653dc9ab0a3de7ccdec1874fcea7cb94a43ede78547230165ff8a9c7448263f59dcb7217d9c86ad3214e7cea98a17fb0f7c28236076ee0f085cb66a330bbde9db915f4b00b39b91f2c736a6c40f21064bd3d5aeab220f08de0b03a9893bfc43a16215369e11b2c096adf7c29580f468b7d41cf325b9742c6c37c9ac64e8409be3d749c96653df0e123381f0af99fc0fb7fd98e076a6df151c26db205b00d891680f8cdfcf7f5a02ebc8292c53e5b0671477d01e31b8c11cae033c6ad7d0d9622cd1ff25223ff32aea6582d85774aa652720eb497c204e6d10d70ba85fb424b83af42f1098a58fc28ce32e015247fa92fd4f3f4773e3f529986b0a8fa8eda59a3acc986f2a2e17326dee08a47efcf1765db13fd39c2ff10a46aa9cd7067ef0b58ef7155dbe5d67f69c45a6df6cc4ad6de11082959d87d34402bf04c5b78b6ffad1fbfb1b5f1d02a832f2bd91cde77f8db3e9f5e6ae46f544567ef41d20fa83793b48966b1302f7d14a0caf116664419d0004d56e9c2f2b45a8397b7e6a6a608aa42cfd5d521cbdbd0a2e2e88452120e057e89794a66e8c17e2b7baf2259cbdfe64f56715c35937262bdb31913054217fd2457e3c1be1856e8822946f371c71e4f252cc44ba9eb5a8ddac7b1dafdd10c4fe1fc0a49069ee553854f60756918b769b2c8ad5ef6e8482fa21ee18a21a63ff0eaf48e1639f2d0804f2102e7ad5fb47970e36eb28f718449a01993009a5bf73abf849cbce302105"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"509af60067f10ca793b70ef9b8e2b056e1c477c9894eed9116389a58b1d3890b","proof":"6077d61114ea0e203f182fd5257979165e036f427a93135d164bb4e2a7f1fc69be2a03f33a6d2cb9d8329cd731fdbd2e2d672b39b07a161e8c91277b80faf25a0a54771972a742c00ea7a34bba8db5aa36cd532b7f432df8bc27dfe3a824d209485ad4370c5f539c8aa0204a25aab78dce4aaa4600d2366a1617fcd8047e9a653e3ca23637ecfa214deeadb344b6bd20d892a295c2b4c48e55bee90498a40a05ce24f291b6a6b8ee231adbab80f02fc94432fd7c3a64ee69bffdd7e077c3f20cbfb41956c3b6ba42d0d93fb30e0634a82262e47112c647fa1c807655db700f0fe8a9e77b7b717c6241a86551839f8ad9e0b580a7a5b56863c12831d2ab55d15924136bd9720dd15fa47b892d653a6f4583114616c06266b426bd371924bffd120cfadca82b6b012c790f08cda0003bc433bc460d5264e70dfcf0b9c1445dd300ee8b13a016cd128cffad987a2f2014433d34be11671d3e1eae05b4e784897871783c2f75f7c134cd842c9b34032fed7ffe88960b119b9c4225492d5695b21f2dfab860f720cf8fd7bb5520f80d56dec3a166ca6ed1fbd047f54e51d6de9bb65e02ec1411ce9894db9aca6dac51dce576516dd71a1af13d3e0bb6d8d13715e87bb2bc2955bfd7c9c089412e25100e1585ec1ad4ac39c0002ec627085b3f988a11f6a0a6895f593c3c6deccff32f71610d8b52fe2198aefb95b23feba721d1394042cf0e41746cf35def9b089dbf7365c845b2424e2243d161e12327238b5b3c7d1cf81307a42c83b86d53a4635a6ef41f17a9a4b47ba5ace2eecc1a49eecfd408405df52672d0c402cbfb697b30f3d22b8f5039d4c16eb2f22e2c6d2dda1ab73814b6dfbf265dbcc96723e36ed9da6cf2b632b205ebf33e86b571216f48bdd80eab568c26f37401dae963ea4af31b99be41027c1dc8abf27c82fb9c2c15409807"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"101a0bd4e635142b945baa160c385023b9d280342cd9bbec3cd1471921836b5a","proof":"36f31116dd07bb7455128c3cd56b76c51cfbe1907b9398818a309641d7e9f636acb6abcc469e883249ca7e5dcda1300321c68cdea22f8b4d1ce410bfbb20de44b441acdc375eb97ee3f9fa04cd240f7589c4b6b6a9b0355734f1ad8bf959594a26dda250fe1b1f890a66dc7992eef1d936b9ac57ef1016e6abea43c0d88ae16d7858716d4c070fefc82268de6f32c52ab8571ec2e1824770175a8cebafb2840886313dcfef7a1e76446824b9f141c0d6120a72d57e682386ee1724105881130858d04071eee1452d85f50e1e4209db04c37dc646fe1e962f28305600f967e00cd46b171304127bce11ef00a08ac2dfa125ae14504e719aba0afa650a35b3866b086234c6071199e1b096ea9dfbf2d1a360e837ae6035e0c6c6ee96ee0f0d692af2617325d53874a97461e5f52752c4ba9a7df54a584c91870995a0d124a45d0de681a866709d484a719d2aa5b955d103a30e19f2b62eb564d6cdcc18d6b9a951dc1087e72135093805998b812179d561ea138137423e9c072891c2e1e95b297162d22707fed2d829cc9ea1405e4e9a9de00e1bedfdf38fe341414dfad4723d65a286f7ac6abd25fde44a1c371e47a70c92302f959a6c01c69a406a1a5867cb5cbc06c2ee1e893712b97f430dcf7cf58eabd62c4f7913fd0fcf6418d0b48546249ac2f59f13b598ab1fa0273d854c5d3ebba8cf1f1e21d93d9846f56fbd151154c27c9e7cb34cee1a58d3b2727e6414fdee5b6c68fe25d6e9090ca6799a018d79c0d0e956e90b21639b549e7c51e20b9ebd440d2dc30d90cb64b95ad70021a20cac6b4efad9de37717fd240827752910c29e9a9499a4a0ec9ded3aa310785e4404c6532029a0f77089c8a790a8aa13f5452d8937456ad1c726fab55bd821a8e03028e6cd18cf14e6ca2c1c658c0d97f3b6bd8e3ed89fed9f5c510d1f2314d2d0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cea0bf21dc75ee2e3575316371c3e58e64c177104abce602e5f0c4b441243370","proof":"2e10e12afab9e0e5d2df97a688c1a7cf0a60e9cb749653e39fde70758c710f6570cbcd34f08819f0f7d18baf9df19b57cf9d89b8278e1ab978b7dca1e389c576805f5f8e457ea1d06ac52c9a79d7017d91deefdeb26cee6dc12a96436561a7080c6ca69626d01d87cda4011b666e9f7997cd1a2d674a0460b65ccf698e02db1ecfe11c3e0c73a7d0d2eaf5e52991c70240b20b4e6f91bacc9c26c5fb9262950b6df9e2fa09a22bf72aed9ec85fad6c773e902eee0590990d8f39a4d74e21020f6d6a88e109f24862e04ee21a49a65a405160616d953d44b537a117abe43d1d0066f37cdd66578b33b9ab7f4bb5c274469f590cdcb72e68957233402d856d310b30d484f3ec83f5ee56b19a9fb2c1725ce698d9887cd884a7e54829683653ba0f8281b8df14ef0957e2c290e5c8a9505b20b214a6884f76e208f80638851aaa3dc8da642d09b0b76fe4d82857b2638c92b5047def56ae9958690be460fdad557d8e4ff3713634f352298be1b01347f0c280d45543d44ddea164307070c0ec302e881b5cb494f2982e52b342dd0186d79ac373ff4c77533c9a7c1246189e53e97f120974590ceedb09084c7b0d87c9891b21e497e809980ada94de0ed199c340291abca0609743d0aa0165367f2366037aa4ef914640fddb55640a1c52c3bd8150ae3124fea3b455f349f13100967f18becb41a1245425f2c4b33009230b2e08221292410d81886520f567c35ec6ef1dba82dd6e9fce4888ccf67fc7177817be63304016701e3fab3bcb39a3197ca96eb4260b2ba455be65a5827bceb35caf23022cc3397ecab862da81409c172981720ab2b2ee2af21ac4d9b292e54aea819a50bbdc5c0c476637271214e30eec76615960670d349c43b769163720f746e37502dae1393e709fa18e74c5d089b39c9fa9b8832e19b759b1d2f468032924f5a40f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9452a45da4b68bb553e553d7047e10d66db5f4cdad93fddce593e93debd0aa12","proof":"bc81611a543c004d7c5888b714a4c4f1acbddc04aa238d417fc367bfee811d3fb467545afb5b7a8c449d070efc5f9efa2d74c0c0162843898c379f1b8c2cbb376840367783b6ae65b0330f6275577a0c213402cab2179cd8551812b56dbab007b428a7c043bfe22e090b48b27463273d479f75b0a61cd6fa4fe4f17bfffad757b5f2f2d5f2a3a63091356075f8d3a8323ce746885c84dbfaf65e3c6d6aa7370732513898be94f1d5532c2355b2012daf891abb4daf2c7f848d777613cbc10007cfffa71b8934ca74d258b8470a14728570cbf092f9b92229737cddbbdee7f00aaa0db844a0c085f3c67bca7882b1ace03fb019040ec10f70db8442c55f168e0cc26a292502935673b31913f56db84a1060295d89bf1c7733023dd531a3199c53c4c0079c286623463e57a5dbe15beeea0da9ef5accfd239a72f33c58f080ee427874ee650fa3ae51eddbbd6f72d99c20001b9957cb61168d4663c80570a5950060c4f79106aa649e95064e796672808f1b9db2ace2ac40d1c6d43b742b4e1975d8ebd9c169eef8625076bcd3874984b12190632a9944b984adaff0848a361135b2b13913d92600a95f332cbc275e0e0ad01a7c67e91d10030ab20a0c143c1a22a4c9508a95176e96560b4ff2e8069ef04258613853c1ca3eb69cf1e685006955ecd4f5659f641b58fcd5c5c3b6202662daffc78301cb6ac8f6386915811a411404bc6b8369fff892ba6b1979dbfb1659704f28793ab1cb9cb0b3cc3d004ba54d96e457ebaae5e1d5489e9c4c1b8d3e2872febc019d3340bc539a5dae71e6b22f9252604596101744e79cc761d9c27d195205ab85b2c335ae90d7fc7e6573ba402645782397f89409aa8609fa6212e707a61f4b4dc273260d9ea2e4a09979100722e8d873b64b9e2f62cb281f39cd0f86c351549ac25e700fb619b6503f8fee08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fe407b6a8c85cb08a46edaa85b3b187def7a440c1ddc8c9c7082d16cd84a4154","proof":"7c18d04d2b46d759bedbadc3c5a62895cf12b507c2bfa55e2773392770e19b27f23430889bdade03426846da2f89732fa84b1dab7692c66c6f27e220997bc92f60f71d8b22a3445b15bf04a13c01d149a4af0c42f2793e11f0e0ba438c143173d64fb3230135d00c7b58884000009dd330670182c851d0ad4da4be2b9c52620086ac0930d538156d811699521a98c4ff68536a47ccb31cc5f589cecb7e3849011665559ddbccad2865bb6549b219d229879b331a49b2348f0629ca4604aac70ae6080910a7830d44b9789764241d26480de88281be5c9aa9dfc108dc510241046e24d4dc6176ab4e6fe0cf15c1d609ce49a40986766048e456e8996108282d685a8f830058b005e04d04697821eb1fdb746d817faa7e7bd2694f89168c947b2b165c86d99b2ed3f9a0124de161c10a3cb7a0e8435dc65606b0b448ea7006e35358ba8123acba3cb0bffa90ff6d8192dd4f8b223c98330c2891dd4c7545e94271ec8db723d83964a0cd5f18301b32b0bac9272811ec48fcad4bb7ec04406bdc03e058447b4f4b7585b53796d01f8c027ae7ce3497dad2a26724c4c61f05d3b0003299092cef36b5a12c7329384ef313d0d89bb2976b596dfce788c66b6837961dfcf1acbf1910b99ec794cd9b331b73643810d7af53cb0f4edfd4c452ff9659390eaef88b4bed11847de7257b2e82795483dbe2c4514cc29c2091a4b83e17634ef6fd420c552ab58b35b4b3bddd0ff749b213b68fafdafd946622ee494c3c1a6feecf8a1ca5a12d95b8430812b33090e320ed1e2845325e9caeaa07a0c1a9787f646c50bb5091e7a70871183c10e06a2e077c78a334621e9d93585e6868a4983728602b1e162564444068781cc35400f0e379e69c5edfcfb74ccadeb0c1acc409ca211f3dc54ef883a59114adbecf050d59f176f9adef148b2f7b49514d24e30a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5273389d707d35bf57e8284f7b984fff2c756cb8da826462856a55695bce0912","proof":"4a5b685d1ff4306711c5bfdc2bb739d5441843b712978712e95a9c206be78d57e2a2f8d663462cbefc8607ba9dfe0ecef71fca315992bc337cd722f6af01202d62c0cc89dd5cdbe5fedbb5d1a8512d327a08594a894f6f8fd1d1778bc039df49c21c85e54b5f7dac6c357489246f179512e402b475cecb56cdb5034228b7716801657b2084d9c0de8a0e5bbd0fc41a27ecd873f9179d22842c86c700303cdd0da41c1254cf39ac5a227be43725c854c2b59447ea630599b3a4c154951ee9a606a302a0403403d8a2df03873dcac4f9bb798091235b57f116e3cf8135e8430f009e80ded19fd09b00ac4050545286d044565d4f3b5c573b1022d7c821b82fbe14c257914e9ff7ff2e65a3859827563dd9ce51f3da53e257a3e0db6c71e319cd6442e0f17e53eaa341bb890c1694271c9df4bc44c2a2e3243350ffbda1a749f1028c78858e9c73375a14684709dc0d4c6baec18daa951f45cfecbe1f76c9e80a10740642a2cb4b302d539bd4b5648e04c2d90eac12712c125647908d61a6c5e149fcd6ab2c612ce9d9c93bb5ab3c35ff1c4d0f17ad9f0910a470ab88d6ae23875f2e697358b360d9d23809ad09918d77fe6ffc34634f7c760ab60594c0f209a1592073cd1f77aa36ee180d7c601acbc6808ca44ac98907d57f65c5a0bec6686b7b42803ef2b51946d57a1d0fcbea449f2e8f2329e6cd41f3842282949456f1bd09226187b5047d015fb6ffa5cf0a1bda5234669bac836c9091e20febb5c45c38377a9768b980b69e1dbc936bf7fc5559eede783fb1f47369ad8647ce2a6d34f40f34fd102c0d43d0e8215d7ff2750f91d26a8af317cc0da7ce1d2aaf45b7b05254e0433aec5beead37a091d6cc1e08d294293d7bd2b3a17966604f7607788e380867d32c1ac037761c6b352963488b195b6ed1e9f5b97ee51c4d86ce972a17f70f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"18133b525888aeeb0a154951f23fa5f32391db7ca59bb209e0f9dc5d9392474e","proof":"3ed741d293ee99cde59f97538b89cfa238c22f83c811a21c867eb9e7201a5a4c6e69f5da17478bcc4397fc8ee2ea2b2ce8c4b4f426db94b38a305b1331f4897c887f855d9f4369665a56c50e2b94db40e98668a2aabfbffa33ceb7ea20f7c74ebac18a70c4ca7f53c4a802c5d29532a7a9bf83d2ab6023d5aaf5cd4031c4034e83d35240dcabfa8c8782f71e8309ab0aca44eb27e9ce4e422e0a24c30e6fe80727129a0a51ebfd3d27cbe7bbf073aa9d2d141617782b1e8b59a9e882835ae60a384727c3279bce6c7a6ed52549d728efa8b203669b66ab6ffbbf050e571b640b5418b1b63e9abb01bdb83948d013603057902a3535f5118148ce47c37d973f3e663af59b96e832069acd4f4f98973c11cd342ef45a79f951da96718077950c36da194fa533bed5deb1bba0a7f5fe3abceaad92757df6f5eb45b5fd8399c61a113ea4117aedafedd4b492b7cddf8eae175db1422d29806b73c1c4d7d82d6dbf34f624bee7abe1b16e2dd4d7fe68285940165422cae85a79aa4e19b7dba6626760e6ce6c19e630315a0555bf851b7dcfb7e6675c6851c9d61900a60782cb7c8913c816abd2d99ef729cbf11a7ec5a0adede276fe946eb6a9a509c5c75d58f1b15f36d5a86bbef2a4104b5165fb826a5029b8ec66ae5372746b29c428e100e5bc79b81372226abac91cedea8588a2a57b002fbe90fac3f1c5a47abfdb9f6fc8a60d06fe3c9782631cd758413678b892614fb1c43ec30a32e921d645fafa27da1d380e2dc899aaaa56300f553b6bd7d3510260947c8784e644e5c0148423c783d068488331777d963f9a8b0f11320138a156f9a7d1450191827d3c5abaf3e8d5da74fd9d88c3e56ea4df85b640cb137e3c475fdbc200600cecea92ea948736b93e07457e7680c044fa14a840f90f8fe0bf54a9f9c500769bf82ebb26327bc4a9a80f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e03a6d24efbbc2c0a3f4fbe98e842bf089c6282d62ce142b7c15a3273f2d2435","proof":"50a78e2230bfde974d02dad18c1eae8bee45341dd99cfb11e0e0d036d3c0123a7a6262410e8d310e548271382c6ed025cabbf9dcf28a910db801b57a7601941f0c08e40e126ab19c056142cb678d556f55ad17bd94d224d99f4b59705514805d74ab951eaa510db4925d2d68f2508f03cbdd61bbde99cd7101ad18c31212591b81fddd8dca5f6d6deb996c70a80525c647fdbb64c97949e2fad1fc977a22f90c41f96ea1e9c9aa30da6d3d13459a9d0287140f3c84094d3328941ae0e2c73b07c6633f2c9997e869c2b419a33d10fa62f1ae38e32f363379a873a104fc96e40f7efbda8906468a624233978e6842bbd56da7b0907b4a4a7e0c429abda8fd674ee0db0688e53515b63dc123c7af87a0cd629400f7a8a77674648386bae3959e1c9a0a9d0818abbbdbeabcc55dc11d6f8196e95482421b5c62e5a77754fe650f41fe907ccecccd5425c9ce6ce5902a307ab7c4e033a39ab0676a48309f5734f14e262a38a0171e5c734b5a0cce33464eeea3fd8d1a1d0818216a5afdb3fd87d9104e0eefb2fd5aab78805746e888d6ba98dbfc223e54ed3d3e5393d602ea04445d16991f6bb46214072a3f05567e92e5c83b51bcc3eed08bda7a283b3e488ddc4a022cdd5a8e8047206fc4376c6a3968ead25c2c923bbfac7b722fda2f2e2d2005507140bc9b3c9744fb5f54f3e7924553b61baa369d19b1f1a2ce190bd0faaf0aaa0e3c7f72d8ae9d8991e25e51e752378c827419c3a97ad6877d2307a22b3148ac67012d0d14c35bdd1cacd35dfcb63d1d47c3d3bfd2e7b40e581fe799aa5134001de21bf112a5386d9d58efd87ff80532112314f794f14c2122c304d662031307398b31e00f3da145fc71b643e2356aeb028ed52b893f92d16763eff79c7e04f33c9135375582e88f871a13c0a5ed0d09cd71e43483e7f6cda7d4b59f6dd80b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"52fb487633013efbf0dd6f092e138f5475e11da4c90ec6989c255652edd04262","proof":"2ea3d48ebc1503e61fffa266ee811de2d46ffc57589de3aa5ab63143e365ea236e5b78c70d142a53de500bcaa9115b63f14a99087af6ff680531cbabdfb3a85b64666b4f01f500f12dbab3dce0db9150e5694a350012ab270866871845760405020b736a37717c0f0d014492b0bbd7c2a9406a3e2bea73abf27ca900a3bfcb13746c704eafec1ee94ca413d076c96a1a0e1b590d2b13b3f4014c2c896cfc620804dd7e2fe615c71577c9628fb375cb569df1de4c067934d71d84f26c6682660f46daf3a9b5e84b6d06db0617459387f3c31ed888f53d4dce56ebdab01c9a6108c4816b1f5ccbeac7c2761de7ceac6c383a40f28a043fcafd7d6af1386758e310e634ea0ed353586974e598a09c3831274324915eb4987e2487bdf8115ec21e62443629bce231a383bc56c8f7f15712498c94936c22cff0b67f17fa84f46f9a3a64bcd37b346372b35d29932a2cf518470f3c92e4c7b66f093e7e7a2409bf076938904d600d36f9ce7378ee8bd6e54a68fc20f045f9033aa55669a023a1c055285c3fb979bda1f10dfa7b69db4e490c9ae1a2abfb0f6e7dcb14c42cde2b716f2a6c74855d87a719d2f0f891bbf4e95d89793822660935cc5bf401cc937cd5522e5afa508578b4abf730aff7287493515df23b365c44a06e8d44aaffc56ca7452b3c85988620172cfb6a59d11b4e3c9f06003ccca590dc2cef335b3838b3314d48d427a00319c1174f354b93286ea9053347834ab1bfac3b1878498e1fd9833c2b94b860b07cb705dcfa21fc54be13d72eb5d2b4cdae6a0435f335a6ee97b62e63588649c2bdfa531c22db21b9f98699952f86471227a09aad237d253aa224a13dc1c1d081711f1e4970851338567520a0f89bd40574bdbad4cc777ff72234790ddb8f3828709671605e60c1bba131829a372ed10c15ef9612211486955db01209"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1a67f2c056cc6c2b0bec80706161c0e11216fc17b647ded1d7199e94ff62b104","proof":"98c1685e812129b81ca0e035578a4daed06bc8597c26a970114295a54098144bbc208d6ab997252a14b3809d38ad90c779946c2bbab33d820900a22c65f3a348f6c2e04177f70b2dbc5bcde3159816f1b745d3f9ba7649cb2ff99cfaa05d6a7b3ed36cf7ed773905777337cf2d5f59b3eda616d802057ff169c0977910ac5a771511b05ef67c8f431581250e887d76ec87754c9f99173f74d6cf9530b3102c03a0cee4852f85bf4365d935a4fddffa965af6a1ea77c9a7fafb8e706d4315b0023f6035011713c45fecd9411adc27b0fa849bc4b1d73628be281abc495750610e107891f0e27fe03ef2f53503065048879f0288c1f257a43a390c0f0492e08b16de9b38609d76d2e59e715f0bf428a400266b9d07c50561e06213cd9bd2e4eb0fd2eb33a45de2b7ea7d83888a3a9b546962f685e5e6dcfb1287496a146d53ec7052cf681444ae57f296be673628a13785885cc517d695caff8a9b11d30f416554f84e60b3cc20d06d420e114724322b42514cbc1c0654053f442d774f2e69820ec85ffc729ae2ff2d29144a4a36542b689332ba36479d99347ab34285eed4637522d7b87bc7f3f84f03dfd0ffad2164ef433edb4eedbffa09b2f21bb1e153ac019c54e93766ad0626dd809e0165f2db4570e72f4a92039d67c8b56bfd9884b52f48607deecd195728f3fc95cfcc5678997400f2a5544a10e1474ae419d6fd277df4b6ec8c1db1d4013d51c24146125c3a61646d4ebcd303196c116c057256a81fb646222233ca0bb2a7587da089c51cbf59fa0c78e8e3f632fc013457c7116834181a44a9e86a8b49e8f9a1649ed43b8ce6e423d71cd1da8ad623350de391a6233aa8869a30f0dde1da42480e17b4acc1f7c524ed37ce098b3a7bdb04563182057b81750ae1ee62b844da8879baed3ad2a96448528259d24f1cc736065d4c2704"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"eafd3addbb8a6678b22b2414dfb89956b54f06f2e04deab2906bd55ca74cc075","proof":"e091a64b7f2fe8ccfd2d75af3b6840fecba16e4f2482a25abbfb9516747c7a2334af467c245acb062c2d78bad87f5ab43601d6d04637c07264c6ea569e834412609fd039a3f74177c77ae02ac3b86ae78503900d5eccb60df82d659c1ac3f443aa6176c8ede23a83c56c1db3d93c1831e5f01ce048885473f23acb851d01902741b313f193bd3f26bc3313e4b8710744bc7f511f865734851f43d6d8d2fb7805b3a7a43723f19cfc7ce09fd0f89cfb4d956a1fd6a3d5af2cece2e74769aa020c4e5c4ebc0d1c778ca33e0c2c8b3a942a0f64898c851d2a0fba44391925e36705c02223f7a20d519bddbe15db63eb616e776904f62419dee2b6658b51fad3e30a2a779000734ce579132049afae4421348611bf7a128ae8fe87198d38a2c35f07423fad0e8dad2d05843ffe6e4864397304b00e24c93bdcf6f27eb1ed43348043368ad1b49f67d7ce6ccc4ad1ca1a11229feeda215bfa73ec958622a99a7e88079a90fbe0d34a6013520800e9b7918d4b9c3d3e6c883eaaad2a07b413a2408d2d003b3e29660ab5cf52c8ba0202b786d2b71af51129302db71358447fa8c52f48c84ea892596dc5fe0deef345b96b910312f0b9d06ab20971961efc8b24aa1e4ec6feb8e1fec0a7291ee2bbebf0b67f83d6522ac683139095a0953f3a45a9473b8e87e8316c24a55a51c9ed44646457543e1d1c734044c5f40e67bc8e464b54286622aa73fcd5f37737ce5a16d94d1fce5a284e6f9bef4e6dd59d9d761d7d772c4cad28797540297dbc90302286a5331a4a475b5d98b5af4c8a65fc6814a0cd2ca0f3065efb23f5fa0144a204185d6d2d63490b5edca43bed7180b0fdb31b5c1469b79f87de1ad07c942bcf79bdb627af85c9700fc3739da633b562e1aea8b106d0f9cba2fe07ba4656722ac22d143bcb6e2e445d3701145c2db3b5d442de9a0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1ae422586ee34ef07aa939f853f451be0dd1d87181a70e07890356be75f66d19","proof":"f0806a673e3ae96f1ec1f0f7ba2c38da2786af2efe9bcee98bd59f65696fda60bc0397bc3bc70ba607d537942491a3c30737c8ce719efb41f288f9ea6797a52f322999308f19a64ce7989c0be0a9c5618114b67a6f5768159a775f133b6aaa1b20ccef22da7ff6ab89c0af926cf16b2f114bb188941d054496550181462b81486307a20cd08a6a4343914a0aa3425c090c305d4fbe8edcdce5d182a5cdbcb20f27ea7c93297ac0cb310017021e8d1da63bb5adc01cde8fd25d0a3d178397730b68f9fbace41386ac951c33223883757556d7e8c9bc39a9a250e7c5f278c56e08e69ca890abc2276725d25d08dca5a577652ba590ef88b47a89ae4c5fe7312a4e4ebbebab59ae436918efe487d422078198c0c0ab6889c221def3094d9a0b7961841de1f6dc450d15d20de03d3d7a8f8a28320287a32520c78ae43acef204fc05f0e6bb68c43d20d51375bb827d67eea9085c740bfc7973c222a8800ed7d793362eda60325675c31fe2a4b9a6b82f988c2db217af9950491584a98e02c5609e23f4605162d3437526e8e8d4fe428e29c04db572be2a8f5da53d05e17f62d1322024f2e6ee2008290dfdef1d9ab01500734c8d5b36c53abaf1a80af3b33d159524d4d4c36653a6ccfc124ba00c81af9914b08259f09b8a0a5d78d21a41134b6e5b541a2ecce088f85de2f58b31bfdeba53d16db98a71786f59d72868b4467d416db02f6a060d78e04b6e431c69926ae224404b68a690f22f5469ca23978f61087842bfefb6b0148895853c1aa9d7fd58fe8f9bba6c24e634e5d3093cd8f92d7b5fea1076ee55eb8096568f554a8ac46ffa7b80275354c2bca8c75bd842e5ef4d18912a948c502cc05269279bd7237acb9de21035904314c63b71700b3ef4bcf4033cc5a491dd7644253ae4173e602d0ce6293949b30f5ca12268361143f01c3e0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"38c2f41b2709b9d92e84083a175b25a93bafa156ea236bbea3e4f953b75d4254","proof":"9894b74911cebb434002e2a725c91498b1f5eff692e399da26c55ae8a240db4d8c7fc3d6e35dd2ce2903e408a080025252421f2f22fac8168ba52ccba943620f94a8a972ddda88c60a32fb6515647c3724372c1a66e001d38fdc6b1b1a7da76da07a1e3d722085445f867012ac9694f57e1a60acd803079405a25ab517c7133f2fb66d76bb9546d8bb64c1b8fd4cdeaef7d53b9a84d99846ccfe3d810bda970103847e0b0929c594064f95c57882c993457d054ccb94cd1d11c297d9d421280ffe045c18b05fc19cd40af26045e3cb4340d4dd316359e1eec9ae61cac5e71f0f382686695471cfc4ba8ecbd2989e62c6b0a62b15303aff0a68a77f26ac705e09389fb5bebdebc9b0559caf79e9e1e370d814b64c2cd5057d2f9c179efb3c5e4ed44fdca49c0e65f6e5dffec11a56067c9084a521111753ceb6816bea10fd9169b2be79c4d641e6deb85234beebc87a8276afc4f1120ac0136e6cdd7308c8ad21e20ca8eafbf4d62187a5864e9a00ddd735d542d4a32e91a25ec649f748f87c42a458b7f955f03a6003835e4a18cac398fcc8c4abdc38e3e916865948b81eb66f5e48559edede908b0b70194d4dbf2c17f42f6c063d87e6054cc5fd1c51b41e6032c09c5733f76254ffcc10283b8357ba27c7818971ab1088bc8f21b014c1db18de36236b4f9fd2cacdc306ce41697d657980708437acdfc0519ccff33cee293396a1aa8b062ecc2281ac37728c649962f9001fc2c370d8ee6df4ef49091a9042103948069637b44a5751645005ff14164094ba97ad548cf7f957f384c772250fe0839f2630d4b2332ce096e7ff867caee954f328b057db386af381cbb2b7d17ced27812f7657535d87c6dfc1072eeca8c456369cc43d01970499cb72296ddd0a9969135c1e994aa198e6737aa05b2f814a05bdcfd516e89b14e9c67517196b06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"78035d68784f7163730e3b20d2fa35b0f57da269d25c6f9b4bee4f3b15593130","proof":"2e2602288eaff59f0dd378129dae25f70c7fe1a0ba25327e0b53dc9b6483607664cd57f3f51c747d345e146e8101c8ad4a115cc094481b87b560c8d94cbd7515444abb03d4dcc36293f6356aa0b8ba52bf17c1a06a93a2886a16d988ba750e3c2cc8d0375474a5b7319ee8c1d553b890354e46db54ddf8b5065460de54e4b902698c1bddf14ed9d75a881fc2acb723faaf6236d77732c017408a53ad310f3702993e37666fb8256a3514330b6c9d82a7516892c34f04e922119619ace150fd08beb3a4ae7fbc95016cc8cafdc1bb28a3d27f7c0305524ee39b68e2c775818d0a125cdfd8148083add2a499816abda853c2c29ab61d899507ec153becea0d7d1b281d7465219e9afdf7a5cb57ba905c1f86afe52015ef973e4a93ddf37eec0c2998b4de1d621cc6c5dccd5e637d13be0e2a4573111d6a3b0079d738d7830d810db22331d82f822d9347c0ee4e569c0564e348199ec366eb38ebbd693e60238a4b3c392a2791bedd13d71a8cd98d9497d9a71a93d586f5e95aa0fcb461a3842c5888d69442127d944ed544d38e23a4f65de4b3ed23c718a34ad2c622945d7b0c79c48e6b6261479e99cdc3ab3c2eb6c304e3ab48d8effcf2a17a43eb7cd40c3e0ada5d5a1437d4c08d8cc819c100afafb5dd9aa28132d621bde1a5ef2788f5c04b820a26cc6b5db79e1757e334e358d7d4fb60e5981f5f96fe92a8848705b3205572cbe829d6dc50a62027def75f59c00dfdb0bdd19f8643cf0fa92668188edc486219a0280aff6dda9648fb4049a34cfebbad610f27f9519165e9da341cc6a738d0c1b244b631cc9108f326321b7f3b8552f750d5bad340705e703035380afc000318cd377cef4e0a1346b54578b319eee315cf79dd81825db157ae0cb6055e01a0e032beb029bc3674ef016be7515ba8c12172c54e61bc68909844a6ee239d07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4632ed5aca8732d8a68cfd9569e1401c9e7b6e5d4a7bb310f2fa0070f3f34475","proof":"528e187dd4d24d92c4c52355eafd1b278273df857497e6b1f40717b6974971008054e8cd8a7edee027acbddb4e2c7621fcb7d9289b185c55418759c48447c36be6bcb8d485525030a94e51fa7802a8a62a07505fa271ece66a3f6248f9f57c46405bf777268f3b1aab4bfb63a72c641b03bfa08f725f3cc2d2197e635ed8fb6fbef1a962b97c4158538789e8b07ed1c69f909fc0dbe1404b962cfbb61855110b5bd96b08dfe380bd36b0b664a9b453c1f1aadb667546ba6e8233e93bf9cc27010022ef21ad3ab6d4e22e22868cad0b65b23894979de8bc31eb75148822743704c0a9d8a72a0c417eea7b0bef9e19596ceff710a5fc02498061f117aba232bd3b08345f187c8f68120bc68bf0b21656afe81b82f947c14a6ce3823f101b2c7f3fd2200438d7147c7e7465219369900546548b098cfbfdeef4356434edb59e3f62b6d8d1e45323eab460b931aa1f14af6ceb703515eb3cee14fcb7101a75762754eed7f79548ed1b4e6c57029970368c43aa57fd658254c871ee1311244d1238755aac9cc8da2c54d2ab52eb960a77b18d7d693f743281d0cd31d620ef2a3aed33743b3db21d095d58a8fd9e24c1ee0d5e2292c7e63c724097833ef62cf42aa92cc4883bf82fb577172c5e15d295a06a50d0b3b058fb62db883f7490b283572f0170fcadaf8aa7e505862dee933b580674c37550048e9ba8ac5b6eab0f739ac4132cbeb7d66adf750fd94b32acf83f55318eaa0607f68a408ecf596165461983687a17399521516ffc70796066a39ec0292cc5fd9ee51004239f9593af2fa59a79761b35a3801d16129abe273bca017598c45ae2657eace01651d1394993c3c72912f080e26f7e8127fc8e83cb980647c24593e258bdfba1cf587e5bc8c2dde20125bffc77222990ce756fef78e5d332fbea48f724a00bb508d1575ffbbfce9707"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"70c35328114257a9ecca326a34bf91c35806a2452a427a53cb6bb86408e5e876","proof":"1456e7bd818a001d72f27c527615cc4618bdd86c1e1ae9252b8dfefd0d2bb91eba352728ba933b8c713ecc48a696f4b9450d11144d57ea272a7b05c4edce8144cce35e04181b4ac64ffb9cbae144857cdc5771fdd20a6753b1a1acc13ea3f348a6d1ce9947e508dce68c5a060e0f04eda68fc0d1e3500c5a5409072271b5ae25aca1c49e3022d4af83d74087fc8b96a9ec1b42864ee307e556fbb513ca4edf05feacf4eca0b221cb8195170bdcfb54474bac9ad23cc2e0491be49ac56be294049ee1a1c67a5d061e444d7cc1c3ca0aba3104c53b698cf555f3653d75a1b0d30108e850cd274e86de206792c798b890f3bbd140499840ede58767300abacde64fb28e891db55506fa3e4c01e92ee9f692174d46319442e85912fe0681367e047a603fdccbd7b6efd1c03dee3917688e8786788ee08ce8f36cca56a84ee8bcb67254c82824c636cbd3683debb9a9fd5015ea74ab681e0c5f13218c457b2645bc5d28394e7948fbf809e89ed12c2e8a64b37eeb96ee274dd8b26a1a169887381664baa9d9e1db9f670137fc09389647eea52d182ca89c56b4dad3f62dec0b3b2c6dc8fcd160b7d4ad2412c7fe77b7dadaae415b35fea277f6bf1369c593ea6100361214f68c745c95ff6bdbf6c4997bc48a345a3288e1dc292e6389e816e3cbc205a80d8df5ca01580745c92707de1dcb26985de46478280b4f267c6eac7c563020e887619f51b2808ba24432368546640c5f98e5264d1c3b5a5668bcd757a4c851f498237618d4c66ff3c704f8f3aacd4bc3843ecb259c90068f3d83a2a031aa70e2157b24535bb0aa1fd61fa87987256c3f0918496ae271754b590a80b47ce554df6db04f13ad865edd0a6b9d0375b8e822092de60b5ae8dcf69a9cb27f54c00bdc457c5931bc77c1560979568c65f2261a48363044c627045218c59b22833b06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"261cdebb13d9e19c968a77697bde0fb894d6562448c45eb054cc747ca295af23","proof":"42dce61583a017d434c70ab90854cbdae2d677e3404b1e046435c27677600e22f66c5d2507c802f83091de9ce08ae9e7762518fc62b0dcad6d3d739f6cbfce0258f0e7d4a851bf7b054c3b8df14951a386df388a5f0dea1d6bc3407f6a97eb72948086dbbda1c4c8e4ce50c69f4aac6952854c99d554f8394fb4cd09af8e8729e961654532965bb128635c4aeeada620afa7ab0d7d0ec76ab33ce34508f5ed0fd986f2f192faf0126ae938e5361185f2be3db2974b23cabef97194e59ce6f60088a7f7e8cf73f5222ce0200bcb8b8bc7547fa1bb4f0f7245cb5e5461bd4d19084ada005ebc34348c4fe43ec27cd42e09a07adf70ab863ea603d4f1209cbdd82032f75b499c2db1ca4a14f06a3e95ba06dbe634c25e7625228151417748bda73fe22346a1a1ac3e9bdb4077f8397a4f16ae7a0689a265180699f5ca651abbcc7b787667113e33d8afec9ec2e6b8996c8a2fad06fa1850caa5ad351d88ed94981ce4dc08c371cb0659c16fc922747f647fc5f37132e59465f6164871116141b41b44f146d326bf44b8b3967b62971a9a3f7a34fe6fb4c6b2c1e521d33ba46498544a33ab2ff469ad6febb23cc7b4abfb0f51fd9f1337dd47da7e690ead5fc39b3da8db22f75af870a6e55676ef1862247b50201e8c0500d65c06ad6969363dc10f4e4cc27e121de9e442f788b1b8794cb9029b7ec6b0bad20ea6d29aec1224901f70db68edd88f560077c096cc5c725807b401d7f3ea6a911889c2b25a7c95e80fd4b53808ab5470a30c7f739446372d40cf639a6cdeae388934ba8d757e038037a88d4501a72ba2f37ad84069fa9568906e1b2cee5ca228343967abe42d6a9e5ba7fb3f428dc1fdfcd455b7dcef1fb359b4fceb8e5302123ce8d38bd73422640db1415bcb8ea6b661b47eefbf64d5646a9246283120b69d5db7ab543e44fbe20a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0acdd30b63b90065ca00ac378fad42ce0e59c6adf241de87d01d54ed1533934a","proof":"40e06dedb45c6928605372b17854d05a61d7cfbfbe04ee4af36f8db4b4f0b06af6340a4e2df579fdca47f0a432fd6b962dbee7de7ddef9da4bb5e99dc931ca630c8a5217243e7776dbe138908892e619a517046c1c4aaf6a7104e6db2e1d594f52cb621b8780d3f827610256b5715fd6e8cccb5542eeea7072e507c2a9b68a5357e9bd764790e286b7e11fd660b84feb01e9125add62b255f09ccd20b5a09d07545b12ad2720e010c575c352aa28e488f69d03df7333c214418d91e7e0472e0a729254996f84b85ed0d985bdc3df33a04fc07102f1df1f92d8ea4f64ad99c1032ec42d5258fa028ec5794951378718a661d192a629cf4dcf2645c01637cc0d2be641476fd47ba19ea7dec3e4feb36350007b42e13422e53acab39f115bd28d4da0045a566f703fd97fb59f0f77fc3b2414a1eecf689e4ca62c89b16a01ac3b1c0a034c8f1be2f3491dd5c9ee2366dc14cc6363824d0a1f1d1ba1370dfa2fb75e107257f78797e0b6932baf510a045bff8e2e3d254cda0a3acb5142397a2d0d4baad8df8904a2b454af236de8e30313665642d3542e716eaa2a948fef8c95f574e2464d425e104cf633c088631743af364f8a9c27da2b19f6d9b6dd156741ab18d8c1532d5edc5ef654988f4f53f037a8c9ff89ee739866e966b3dd51d727fa2b749ddc9c99550a7a9dd1832a3341f6d6f0e6aa9cffeb10a2d7d63d7faf40ce668041f17e7371bf5fea6bf037f1393ec75dc30e83aa010badcb229bd41e741008b66a2c09474b6ef4f7497a7ac326c1a3693f968c8aad321beef5bd32c79a6160789d2695054d849a918a3cbf24e7612f6595e5482a2bbbd2b7bc91643d93c44079ad5b771a4db2464feb14977be1e64262c7dc5cf3038ca98c277c50fadb7f078228e24cb8555a524f457515fb9f8c43e36b81be26fa9098833f4d2e9c6c3e00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b2999cea4722d5adf8939e804bb8e6847cfdbb4cddd4d59998e394ced17fd03d","proof":"ae1b65d6a47301f702ea655af2feba7fa3a9ee22460af3c9d1238cdbd3b5652dc4901c31c8b3226712d46d8ec7c7fa5aab25e68438fcab312c0a46035407494edc00571d77a81915eea805faad72440416e7462fd5d3eee4277d57fa44ef1a4ecaddf2b3510119befcd54cad8caa1134d915b773448e33f585b1706c75aa1c070031d0c645ad51c6efedfeb27689b31078edbdc10b8a0809b4199e01fd2217085ef547997ba674d2e18a9d364b4600cf54c629e0b728733fc1a76c0afdce4405259adb81b30463acd3076033c57fbfa3f151c08e0def3be5f66b22ed1c13b70142ea9a9e3c1c8db28846ef5b66a082b613456b3eaa40c23d25938f56bf1ea12d8a87daa57df861285441d9da841694db72710e56f0c1c3f4430078392a87ff3ed2803bc878c46656e87fc13c75696d48079b76a44b29e0f7caeaa3374d472376329f1949be464c043c92b7be0872361362b5fca2c101f6dee0b7c3623a65e675f04fcc27540bdf497f3152bc757f584e006e08cc52e88894018cfe7fbef2dd1c8a6d236931aad7bd81b05e26525c07567ccfb5aefdcffbdfdef91f840216254c8004aa2916ab3702785ed274f303232e459b5c974f16685bcd1321f4598a2c5942f6d2319e6e9ca14d8f76711f892afd783ef2d3560ce2a9f91d41b34aa98f367e1b62b6f572b7cccdb8a504fd008669b3bc3d4d5c6e4c2e7c74c8e130336a4b50381fe230a2f718daaa1c4c0401634a07a9ec74893c9680d54ffd576a63f07f9077a4896c04590593549ed06d0cba1691178a75c65cee032e853a80d257c24802d36ee1561eebd66f5b0bfd1cbd374c6aff1f8a28a83d1b49790f1e2f3a297eb0da884eba363d10e156e72c040177c3cf3487a6f8bbab25d9719ad5160a800a06a8ea5e50a097e1c4fed9f4092a76a1ce9598c6ecde7753cd82c35d013d920d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"20487165f82160433c741ae1a9a316518c225e021603c7b79d42c40533dd8a52","proof":"1aa988a29aab5c84cbdbb925bb2f10a529a8bc3cc0c3cd137f62786b79ce300aae8affae0d971d338c04675c5812975b7d7b5d5f1b4ba56542969caa16a948449efd6d34b04d4e372e70a30e4338b8a53ea40f64de027201ecd053b13af8f036420b327fd7c3be4fcccec0309cd61c9c39b9f9389fd8add00121b784c758c031871e1f1aa74d53b72526b40dfada0f9a7b8e11de24b1f0b45d925aa4e94ba70772a194244677647ee366a76ed4153daa92625a165a68da285f4f02ae5b78d20bff2d7e0e9b2b21f3952e24c741ea5c846cf22b3cee196b74c21db5489d837c0dda258887a131380b4de7224482744c6b52afa5a7b34f2b0cb8c56b7b17026239768f3a9df40dce6948e28186ff753a31a1887e215b256e2130f4e7ad87ac165892fdb556788876e79b75925b272ce94e48b917d56b307d50f07cab93f26f5a14e002aad8dc59420740da3a2efc7d94ed2fb478b9966f3786d4537d5b1c77e47fe455757d8f830423579a1ee848835e812be7a57609698e7d541404ee0335ba137e1fe6d4794148b8f907864784ea3d8faa955daa1c4a0ae2e3807ccae44a046412362bc893eb7f9bd87b6d01d07534b111b79f13168d8e4ec56325224a1b176186aa9797a7c3878e60c8bfe454995d522419d5e4d32dc8143594f5c9fd0d23298c317a2d57c5affd52c7be41b676af5fb6d8ada0f21c201ef85f33fd279b7a258659992e7509d1383e806fa77fff91ff957bb440cb3aaaab5a3d28e2ee1be20d38b414775ecdd60ee2e1de75a5fc618d3e68c24bea849786e3513288742d7014d4240e38322232acd0707e720db7ca6003b16672171e427504631a4d64eb403c4a34acf69434abfc85510d39a72645920ddd15657414afa608386dee6bdf68014d827270506753df6ece19e42d04f3f65b61cf4a1f18d3986f501e71cf8c5901"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"08b77a6f0d85169d49800662d57855ab69f84939ad16cd26496c1a7ef1e5d402","proof":"ae95625078f31532ed7540a5b0c46e202f835863f1d81b401720a842571c9374c61955203e9c68da99e712447d56eb05da83177024d3f4a455428caa5bd1c8058a2f7871802ff44d12444e5527d0d3d983aaf9801a2b85adb3da88cdea0e3b7882bd0918adc078fe4d91dd0d217d0db33ce034eb0ccf9804a96f1cf4ee2e7b2142eeb03c07725db5efe25ff07adb8d62eaa438408c941d1bf25ee09c7e366c0793308a17adfd65dfc60e6cec44d09cb19daea392cfe4c8edea7b1c5245aa9c0a396e01bff0e12c7ea63ae6e3b65f537e8b713466112e1a71586cac1ded0d1c0a6e86d6bef5ae76884fe3bd1a6ea71668436fbeed729cb70179ce033339b7c10ede7f87557ec60ceee6f89f0faa490a341ddb16b6fb7106555b474f56ccabde5f9880fee7d62d1f30bb132f09209a5f0ddb28859791c67c45314d9ea769eaf10e1eacd35207bd971e020100633205f86ece77501c8b4663d9409bed3176b5ec0ec2f0d87f46a7b31595694e6df7b6df08a92b69ad7964b096b4dd3d54426fa9157ae81519af3bbd6d641c6700ad45518afafcf4eda849fa571a761e6bf34e774cfa4c38bb929559c1744d150023bab255090891a44a35e9b022263f9b9739076e4eb85b822204274f578101d5e5f30de8482ff102f09d607b03eec7dc992f3c24f02c53d18c710571ad85b354db96400188e085fa0403aa82552b3b50ddcbec4a440731958187fc957954219aef9315c28a6747052d64ee6d6748f1e2fef43278762e38decbcbb0bb07772c6ac3c90551fd5f7d005cb33a317cacd8f9f0e0b937acd6942e7ca6f6f45dea5f0e4b1c7b61823aff679747873810b4e9db3ec9886d65f6d80ad223adca2b2fba7a601bae9e7f4e82eec234d590b80804e44f1ca802290dc160e78fe50dfdc66229fb418dc8d81b698704de2e7861f233eb24537505"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fe28e49865b169afdf6140f8e66099c53746362a72166c038901d74e474db909","proof":"a227917adc70de8027505d14257aafe21f5147200fb2625ff91804fe9b8d18070c344c0d474b0f28ef14288503085015d4d7c1484b03fbea95f1be5902ddc03d46a51b23e86f2ce6fe3cb0c5ce72d7be244e45621c4a98cd8926379c9ae9773ec22197c4153619ce17b7ac325a11c4491e508376fde1273124a1a15e2952ab39f09883418d5222ce3903fc5f738495e1e19c0ec4304d19a058c31ab3582f8c099664cf15d92ab0ebc3f051adebe89101c9f127be70fce85c018094c863351003ab2581fdc865dc6b330b12c271c9d02fadeb8a7ef3d6c14a7b9ced7ae346cd0e481e8371f9e55be9404db1c6eb27f4bb08c8bd9dbf934343dbdb8f510bc5543dd8d4710c670a925c9acba60ba2d844a5bca39186b23dc428ef31f018ae174a1a820c010d0b2b46d0571b2f41942d15f7c32c90211a617e4ff8456e159a966756307f954faec03a6d117111fb90f865f683c495009ef7a9d603f35841bc3e642a8e89d16799641149118ddd27eb89e027d380e7084c144c4847f90f1a2fd38a38265b4db98115afd5e8873fad855afacecd156fc0e7f6b4e7bfbf9c26d3f2f6635ac5fb52c08667f4c33ff1754650db17ab6630e0e137b6e1d48447e339fc5d6eb47c7ce19ac8043245d281e29ffa96c38b648e5ae1af1df3fc84d21a7ce93566965030c4d2bd6894da60e4da267bef1047b0c1426b65ad4e6f871a026953d04c386dec32c470afb447d17ab1af760b51837c81331730ac2c8be0a07ba19e9650b67f3df90b9f52679e1a5d1cbdada752b871d60810aaa6f706fdbdbae9f2da3344f706e8c618715f49574cae0fbef218b623eb828e0a44faabec27db1b13d450434aeee0176e850c3e01568e32bcfb3de5f93279e25574df38165fa7128d320917d2c92f216766c01d88ffff5ec466380d299fbe20e53dc06b8bff228aae760a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f452fd2a339f2b7dbed43a43c6cbafc3e02eeb446927bb90e0d41de03f87be0e","proof":"e875c71e249f3e19a9415b60a1ac3fc3c04ac4507bc017a43f82e18884e0763ce4b5e479f4d8d9020f51f347052f459f7dbd77741bbecf18754486a3af5a5247beeb15098f80464fe9a003ed223ba01dc39defd50f390a63a31750be45ee9627b0f53068803a42dcbf739ea084bc2ab1526f369d2243deb702ebfaee87cbe23d641ae7bab8a4836bdb7c2ecc5dd52feff9058a5ef7f1329c0060e3432ab99800642df10b7955097c95dc52d99045183e30f427c308b655b1ef11c62e509cad02715566fe9ccebca5a81320a54524c809569e2744285fd47e46d98480218d7700b69ec6bbc5e7eb7e18f436f223be13842ea62ac39687b90cf06fa30705b1c01e66709820eb9250b0842fe5b2840fecc49b69cf8da9c372c5498f6087b36afb1e5ed5af84ecfe205cb496ff06a595114f441ccb534f32a5957b10725a8978f6749c34c560ef64c1fdbdfe51e6d1b47ec4dca04d54baba85662abc0667286926536ab6667199766ed363fa157f59f13cfe2ddd11ed23e7fb54b3e89b828c7a224cd84a4d0a9c9eb1ae3bcc9fab6f3f8127356f26f7f4ca03200afd649440383c0b8ee4f48abef89fa30359484889ebb0b617186986666bf116ccd84b4e085d11613224cbbcbd5f93e705f8a1e8d18a245d2d44521a25240e046d2d8a52a95c5d63b2270d7632a446578b9baa679decfda0ef2f8519e5ded0b9cfc22754c88c0d2944ebc0029f2900dd6217db87e2fb0ed4505649973a8a4e011e7ad35baba1f1459e0b914b992599ddd8dcf2eb3873d6bed528a23623e73575a538f9e29f09015c2802bedea6e50329f1911ef4ce2302f0602ba638b9b42350adc5b82d00b88b589f83d7cc2a930140198469341a19fe4fed7845833124ff01d2e8b4d4c9442d0825a94f111deb582cd648a648dcb9e27038a555c562c8b8183d1971b5ac6c260f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4ab27160087c19ed4c469b4571f2a83fcebf399bda80fe7651ac684bf8452d01","proof":"5890514c4f8f62be3b5cfaf8d4f035778d0777f88cd8f14036639c486a13ab2878a5d25035ff4162e8293ce2c96e0cfd8e76da3fc229071021d59c0063e83a0336e9000683fc376a6620f07c0698e25d80b2fdc1673ef5d1959b7f4cf57a6b54622ff4d2f326ad9ed5ef34866557c111efc509944b4111dbbb7ae3dde8bcfa582964f8a543f1fa8b9521e58d7517236f7fc630a85631548e408764dd4499ed09c92cd26b216bccdf66c2fc660a4be9bb5a99019692821d578fe730005205aa08a70f5511ec0cd6431dd5b302526559e86b0249adaee702cf789cbea1243ae50cda42908b9fee2e2f0f14e101094e09d6be4c6a247be9de37b9e6f236a1b00f57ea3b44ca12545ea34e380aa862e65f6800ed3cb9e976be2acb89f303d642624e4c11e1ddd67c708f9bca63bfd1902a940c55fa1e93ac8e2c96c765cb9877f153c02861dba930d80d789c8b5d86a8be42e64e492809e06d58d0e84bac2900196940165207b34a3e047c2c610b413fac8b5068a367fd4902f97ab8c66fb8f68b277e4735d7feff373d35a28d6502ff360d364f5d216a5ca35f49ea880e77f6ff59c221629e15c1042877c4a6c6a227378c6e2a0b0bb48e1510d5b917e4e9eaa779a226dd13791445693be678c9efbd0521e1772347f064b03ae0257dddf8e3583dd802c7c8616825884afc3d8d5c2008f139889ee5977cb09cf5cc71a48e63be12e8c70e2fdce9ddc1f87b63457ea2f8d499ae4de7067d5e39feb48e3eab1c2c23a6c188f5faf7483634c588dded287fd95522ebb7917fad77101aefa1a356766bb8575279a3e26657671fb34e590edec04cebac67fa188949677023e6deb4896ebd7c63d0afec634246c5e9c09ca8633660f0b763e215d103bb15dec74d27d0078b64529c43fbe8fbc8d8f0ef3e929796266e0e3a3c8a53a98a8633fedb8ad60f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"86813c901c82e82b497a6b7d2a0c3caa5fa41e50d61d7b42c4d3dc422fad2173","proof":"4c7b58d078520010a502853d31e287cc90bb3811e4a8f2cdd3c73fc61dbc9a704a98463f9fd771d5ab8764158450c6a196cdbf30dbd0ded4970e09ef76f33e7ca6229b6ddd3bf3ba782bcf00c999a07c767c4a7353a3619137a304e57c91e05eb2ff627414f8ede74581446c3848e8e7790b0890b85b0aed1923ddc66ebd824de2ea621c3284614c0e7ee08ea1db50c3b0b06e507df9a51bec842116a304fd0b1361261004d01da429c4e795b20b2f3ebb34c5e07c01efec53f656c77b4fbe0e970f8bc3221f04eea85d30e6ff0483b85bae64d1904d70bfb4ffba2b65667001c6288b4c4cd1467c4aa2cda74b5a6de448faa9a2356820e1b5a676a3b7f9092d526ef6884daee2f40d93b06c89163a65275e546015213afd394d568e9fecc253d8a32c7701a30271e113fa3100e7d637d3e7cc1e2a2b84426d3e882e9d1ef750489d2ee59aea1accfdee3e9f952ebb9501d66f73396d33134c60fea4bdf5c719d8ed7d1082f4f436d8b2619d6506e1d65aae1b2a77887e6959f12bff64343b50a0f6c897b9e11d997c9aab8e9a38eae34b78df29589908b6d7ef4c8d9a2ea515a44cf4a10e49e15b27e220d702f2e46aa69a1994969779d9b0627bba217df13a6ae9bd4aba7dee6ccd49eb800d94d242dbb737c82fb13ad3154f1831d756d11a1ee526be91b81ffeb26ce09b229bec3c8b9b7981b35525649261ce464f727d08daf62d8906fe89f84b6584ad69a3445e7589b9e7af15c4549dfa2b9e0c33595a2229a7bc12c72eedcddfeeebd3e0306e9bc74993f5826960e912630161864e6f36b9e6dfc8d4826f5520a0656ff58a2e88616ab322596d568b08f80d465d6051e49436665091f70fa2acb9bdcaac3ef21ffce0bb21cdbdb5365b6f9ce9a9db09b5149967b532506e2fe3bc2a93ede6ad46502db99a1162163f0ee811a7a44108"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1a37e137aa926f681a9f45a4e6ef498acda70c303c44b57f8a89a152db84031a","proof":"68357f819d6dfade803e88827fa7e5ed7985769bd301e5970daba61fcad7c62accb963e70f197a7b5c7665583b054bc1e2f70343d016a51b100df048ca7bf559caf61efca64e4be1048c1c3fcf4fc7e5aa68b3619c0f326a4b246bd578ee583430728a848963b95ec6b172a02e20a6d83f6880b798c33af8e739d1b18a87e61d4122505552a8de88a23c19fb32153675c62614467dbd4765d63440fc9af5280d566cfa05c881a3772aa45a32b5f5b30085bc02a27df63bd843782c97ee446901e0f90d23c5395b606518fd5651d53ba8402a92d293abfef1651ee6a9aad21308c8cefe1aac924710e856443a0cb36a0c0e0cfac2e00c88e2f69a0b010b2e3a39945efea518797323ad27fc7a1c8875de43242d41c88f869330bd8bda7fad771e7e13c0dcf09caf5b094657b930b77e58b7a381524368ea90067f744d38940965968d77b96d6df68f8e698402d0bed73e09f23557d4645f2ce4f1432bd72f195ee67697eb02c0fe26a22762068ef78672221f6d60c6129d6033a921aa162ce750804d5ca3ee10ff250914de6848482d26e327034b395766fe58927591b754c22546c9aeb3c08c51e74faf5ff15eaa8bd3bd5c630fd76d2627820a76d735f5df7054cfe8761e07a0f2920dfaa94d054cec5b2cd860e3ca79e9daa65a04d01b1f5c0c0abe6c8af7b4b2b6d7a706ecf042ffd4eae5ca1b67b2e8fe4ee4a20eaade2c620d89f6ecac47fd628253548fddc19eb4fcaee7a31a10831ea745ec5be3cb68d6ea6b9b60e064a0cc255ef57f8822e7e31af1f5b7e59e8d95554a3ed4594c44f0b7afc313749fd11c1bad21bae71b88823237ad46dff732db4414c74158374a8d91e77fa6b952500934726642c7be9bfbff5a3a5df416abbe4b99a0aacc4f0d54e5e364ffb21dfd1757e4db72badb2ce22ce6a24699de9e1aadc24cc2eaf901"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"806b86904189a79d776a51b07cada69c283b523016acfd7119c13ed0c37f597b","proof":"80cb7886693d733784213ad6bcb0b0de1d7978c2b43e50a2b13e96d74984181eb4d60a81b95a805fbbdeb225b0d20e581d3e199a02b873a576d93bc617b37e6506f19c6b6caabbfdbba0dbd141d478f2a8f56bcfd600423060ea36cf9100970a66e2dd6164631b2a0e57f33cdd90a2c9b97916a247c87fb91addb8d7b2bf91369ff7387a0a647c929b6b218415f42317a214dca247cadc89ed8de1dbacca110491fc186469672beaf9bcd03e6dde6171a916b11d374bae23e7c6d9ec5c175602a2fbf07b68220017ee088793fc9f48641f25d009844baf8dc5d4c5e73eac2a03eecf6c990301405dd6b8b5756161ea18452e6c04e6788a75e675501443c89c682ae5d259bec07aaa607d728120c7d0c126fdea2c243dda1761a01aa545397d5fec22594a436482926127bb7c8bb6e0b5f31bebb5013c68a3814f60c80c984d3454d317666f5be1751af5ec837692a423b631325faa82dd3fb5c105e3d8314f556aa166cd915c2b14548c5ac47a6bab1314413f19158d0c9917a985407b837711fe543f0e4f5a8482170e6941c45c353569ddff93373e449439d3c45f1053cc5780cebd79b6cceeee1399d4093ed0fc338d02bb07f41cb1e6409dedd5d04353692411a2bf7f8c53958c8cb5d68220fd05a5757e5348cc77866a94a812202a6863160bd776f97185034c7216bf543d11cccf5fc212d9df56092bac57fe0afc6311eac591586dcf919e422b87cd5e575c568b1f4d46745626492c8c722f49226224dcc36f20381332128901f3e241038fb0684fc4501b8cf5d27572b4b448178852d422f9232d8f5cf144f6bfd01eb27a3c1124454690bb0040f347c705796840173102b4468387ac918b6b4e0bf764b25573e7f51acd68b8dc56036e0e12f7920032ca13d853e6ad25ca82edb82999d8a69e4fb55bf9b0ea7e0af2b7464b09e308"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"32eaaef925141f9548a0814bffa4f8743bb4d25c59532f7d2e8db8f05242ba3b","proof":"b22834160a4eabc8adec21ee3c26368c2e13932d45c2904a113e197ddd3712432a4f2875b894791c80a911603c824e10c659097785e26127a5b0882bf1f3e814de4029bfc7645143334c4f423e1f4fd89ec65d41b7d9ccfc5268cde29b44660364c722340d3eacd4057c0529efbd2f1868cdf0982cb3ef6c41af46f44cf57e60adde11634b81eaaf9ba15db25f16917227e6f32609afff02b7f56a2c95a1e904fc202a09112d15aa033fabefea56cb255fb7abdbdc247e6787cff116a4e29b09b26116dc8a9eb20b5c4f5671020834bb6b879c694acb62c467b1548c59e6cd088453d7a0ae5d940207a10ac03e1bdbb4de56af303088fdf00cb72a49fcdafc6fba1f0036573f023bbb175b49801f46e3a70cd04cfcdb0e308759232c666a50697a51f1055c79bcd92d50b27f58559991f9811bd033d05c662289a0079ca6380732cccc36e0b99e6885e8ba9f200afaca26a740ef14a2717d3950c3b9a81ce455f8b7727064c8888f59c9ad4b790172a93062259a6045f3a7a71ab736703c874f0448ccd91c39116c17153ab1a21a8794b216867cb0287726a026215154a12b23307dfe142a00b7aca1b3331ec444dfe1b8eacecec5cbe80eabf780bb2d5ace034ac2d8beabc424bdc014137538bcd53bb0f5496f33bc5ad41c094f362b41bf6d460d096d94c7c921f6e1fcc1be3b81d6b95cb8048dcea64ccf04028bf453ed0c20d63568fc72018ed4e0b76e26bceaa5a558b1f969f0ff4251190e44fa621b5d1083cbf2703d5091037283c4d6293dc9b1ce7be5ffdda95832edc70a5e78c77a98211da1fdfb60a97262b7e54752d9f78105335b572c72ac21720a340961e456f69f36ce5f9c0a812bc21f98267548154e71b9f684a3f91a5cbda857e78fbd04cf474c835ff724e7e17bf2e1ece428cac09602f16fddf4064159b78e0a88960d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3a42535da508472e8245273da188b9882db6c91473d9c40247254509dc13ad09","proof":"c86df50235bc93475f9d797080affdb3db7b3b365af0cba330e8201c91a4207052e56e840347ff4d3694f16095fe6ec365930498876ddfd8e87002a5c2297234d81a4a7ebc697f92eb06d77957dff5b5de4903c097ab415f1c5c90337bc3245bd8a5bd357438fee4577c904645555fe166d4ed4979933378d4a621dba5fa8704d669b67955a0737a68dae00b60a78b5c5dc990d7b4652a1448936aa482ed4103f9f1e8ef309476f944ab36ef78e9afcf32c2210c6761e5c9b27532ab0cb64f0ee712388d001bebf5f516ef718735da3f930e273f40141e1ae8563f473f57270ac0d58489e3eff46e24fa42853d7bd4336e82cd252d236ae53d79dae45f69f76dcc9dbff7319d8cf2d30f775ec4aba8b85d3ce3e268a3b7dbfe3a4c0fe2dd425db8e4409cdaff256456f97c097d6ff11f5572a90e84197f1f88b7da94ddbb8809be40088cd822c2ebf090166d8f5604dd1a5225a725287cb29fb48c50ccbe83178c39c50202b05bff268652116ba3cff582c45c015c02a7c916d76b52ee792a56b2858d395f60725d2652b4b83346afa870dc9f648b93bd1a7495f01639e2fd5cc875a6210e6de30bb382be899ce0ed1c82a42fa89c15ac26d02982114c107d0e44e27611c92bd4d74abfb209b454431365e96dd8b5022faf18ed5110c85fc616b0e92cf7ec138e85b2bb6817cf6992b0595769afc233a327d26c157c289381539ac5736d8f1c9eead19b711f897d515979dbae476dd0e449bca4e35863f9407ea485976a386d2baec87278d309beefcbfa2a9fd679ee011c5f8c2bf6c9fbc80128c9fbbbe88c80e0176561cd5bc0a9bc53b88a8af6ef4a6a2ac82d1c725f7b1a90e2216af4fa1f6c14e6439dd3be44da49a379818c369269ded4a05da1d0c80e7147c557f2a949ea5a408f3281f1645d4e896690063238f65da091a53fbdfc0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7646085815e20c343fee73c505a93ae2500402b4be71414ebc33d0441db35820","proof":"e831e8d688e12d1216edb344abeb2087f177ea1d0ffbdcd0310283dd968ad26b301cefa3f3b24013d8ad2a7a2da9b49b5dd7fa865ba8aacd1494140aa76a855adcfe2efed3e5ba4b5e7e4941a8b19faa6a75ca5bdfc8bdf599a7fdb0cf4d3120b63da6164bad324054aad5f4161c7861181e8ac6793d3f23dc4ba824102ea542a3654845a71c24df4206efe2f301a7a22c44288d67deb624b9a99e919f60730a31f42220cb16fcfd6c4f7c83e28cd342ed99700c15b850312480a7e330b6ca0545044e57fcc784f76b83f36abf7158bc14590c6d825a49175efeccc65b22d3033c943d67cd147947bed33663814f3f65d18ce3e29bac66cbd371678b056b0e5db68006adc11023af093bcc5fc0614e03be5f23b19ae50601745333b61fc562079e58f7450368298ed75a95859d832bae88e93ae526833cc0bfd5d668f2ed051c1a835737c1f3c7eec4a0acfc9613fc5deaa5d1e196c5cca861cfcdb1746a5444568696c743487cefe15ab15818f96cc08fdf89d7b983f7f442980014289712748ad2ea7cae7291488820882d0a02a2a2c65772b9ae75cfecd773fdd3ebd85c06e6bd91afb5726160a47268e0ab3152090452857d1076a8ad50daa3d087eb196328a31232c81fdd30231aa6dbef99a14fcbcc671007960b802b52b19ad50b6425bc3f2eccc96a3ff02480e38fed24235e6050350b42722a6d94a3aa8d86ffaf467c2b8ec0a0569a2116822420b6511659dfa6d8e79625d2825ec44701a4f59c5cbcc48dc1108e3939377069c46f87cf6f4a39f2c01a10f9cafdf9a661a52f014ba4b1fcee7a8ebdf78acecc1c20444a435fb11aac06016efb9ea42518a7a7f70f4a7b145b5881ae05fc79d04c8268451b9579dcdcc8ce67fc118f8787e42a480c2bfae97eb4f111a290c088ab8f756f5f60c27c94897ff4c8d2b326f2ce854603"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"821c5307b553b37d1b3d2b5747c17031203786888b513c21cf40d77e62d44251","proof":"1489127c0bf5424322fc94589844c3bedbbd815df66264770e70dc65f6cc2125b0e291fe5490a15990f47c236cf2eb2d5663f3be6f95e6687e64560f9bf4e021f2334131d492ca9ab7179c5c9c33a2379b672cdcf1b93178d0b5f74c955acc40e63fe9f3e26ff2bfe5c4231299828b4d90e3167e8a63d5484326095f3448943a7b7d63d05d204b1d6816e5f0c65bd0ad48b87fd87d49b37d708b36290f0f8f05e8424e9a677179072d27d6c6914e8eb990fe746c6fa817187f63d05aec9774023d3b38590de4bb8e4550bdd45edc60c76dad099260239c7672887d8f168e4105f2b49853d1f65637d04671b7abff27dd94132df522644ce0918691ca4dddfa33bcbe46732dfe2b9dc41d4b9ca7f703f88b117479b8281ab9e48742ac5c53054654c15607446f5f3838d6d43a99066b7e1eb90750579192d33101baec7770740ace2496070d9b6f051995ae1f6cdf4ae18e115b3d2f623c9e6edd3d29ef56902688e55fdba0490c2887683fb7b389d6fe5d56eab6c5e4591df7697063562abd04d6bd23b6e03c44f6d58c379becef25c4880215e19f2051e814f64ba05255724910b21ae3be0a4828de0fae8da75abc0fd5fabc2684ca396c660269dd476506143c38b6bfbe31202982fdc94993a50924568c2eef075e986e2788b1f44981d435d2bb78064b99d7a8bfcf0cba7f49d5d83abada92653a4678e19226e641d3446362aa462f057f6a5464414139d4df3994272bfa69a2d675c7940a51a82c82437b589d5df29618f659a5f6d3ec70438274f11ae1aa3fcc963891ff6e5bcd42cd4910d2a04c21621e9db9dac3f74c8b1a38dcb3596f8b63393ba11183a4ba364e2e19047bd1281061c8d0cda51a1e02af30cec1f93a282b782458d4877083a42d0b04d37952a13a73465c05d2259a12992075ecf9406f6aabab4aa226e018e9f80d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c0549c0fadbda4c072bbbab0899f21df4761ecb7bbf5fa096df07cc4929ad00e","proof":"7cfa12fc1e7826680af02d86888923b6f60cf357d6834de7144a62bfe17e27521cffb342d88c1ae59e4d71667290b693ff757bb64fd59c4833d51790f16f3c3c1ad07bdd3ee0d5f44cf273651a1d35ca0ed6558fbe608f3004dd916bec8bd902b6d2eb1e97308d129749d4a1e531a98f93b46a860c55d168bfed21178463f7569812820eb5fbd452690f1d1082502b71f3350effbbb1895f07dbac0e126c7d06d4cff611d2bf3db2694c5eb07cd284a8222dbbcfef9550bfc6efdca19b1e920068c12fae94c162862398567f91275f854008503046760eb2a6db5e4e3cc16b08c2139e010ffd34e5f6c40cdab12e2d1f214ef373b230b0c76c3b7a347a110969a2bd88e103340d8660f1ca7db72bc4b8179280b45adede8da0e4b43ea1ed213b9c7c9b0c48c2acc18ac6a30508737dcf531d80b65afaa590d69fa199e4e3e035225637d7b3764938b2b04a1bb5b0b1e322c7b936e865e92b2115662839ed605c86b7aa9b141ffca06211995d830f685e9f487f3d3d1d821f3cdc8372e3bc0a7d98b7bac03c686902735723dc01ccfd887b3bf261c9ec44a9dc7d2cfcd851bb080a1bea8c775d0b8693ea001b70139366b7dfa84d86cccfdc5849364a012c2348cc6e75173aa111b230084a169f0115622ba4bbbfb53ef6e0fb1d5428dad2e41332bb787f13bd7cc146e4d180d93204951861990dba4d0414c246e28d9d5ff15306993784c6588b41570a5afbdefab6ba7fbbd63044f811a861f8480ceea7b45f5880d3338f439579492b102bd45d9103903da2afba9de8c1b6b27f30a55aec72ced35ff0f31e5408935e6b60246ad2947e4132490c2d8bf61ff65f0c7a9d51625c56679102a203d97e92debdf4f15d9f0cea9d07d60828280d63398c1d5e73003cc1c34046616055f1d7d77f9b5d62430740a832c9612927b3bb6ece7eb0ee08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"58e9f6f5a03beebf35410affe2b29eddf6246044486f8d45f8089299ad886437","proof":"8c65846bcd49f9e8f1b5a11f258d09de7d6f9c0503972751fc33448aaaa0360d68505f178914eeef47679afba19bfdb6125baab587b4419ff8b9effba51241411607865ed72918f684ea305b911c8616d11d2efecf2a7fbe00ab162487980a34da2ee579d33db4d430ec60183e3ce99c9ac8c97f91e1ce01521fa2056f6ec833882ecc25bcf47729e7135beec3c86534ca549d2ec9c711f6f0c57f4842bde3040f65c84f2632d0d063f122a76ae250012b5edb35c7ea18b421c8b9e4e0a5450f3481d2a26cd4195fd937724a8af21d778b87f76236737bcadbe8b23d9ea3d6016cef5ee0e627ac42d280cffe1fbc25d86e92ff5e7df8ffa30ac3df910e0d49490cc52d58e9064c83cd0348ad56302611f77e2050831a2484f769001b26791531f4a378196a36b69553d5e781e81ab3445f96a1ca2c1a21e88a2192d2b77a541f42eed248f26145a96bd981315ee82854cb4fa6c32b2e070e4db7deea7ce4dd1f4c3cc3edb4d8c855e04f5c838d7371c7ba60bed8e3e2230bad64b6898ac3e3214c8dfc62fd31dc36f5a358f80997ad645af257593e7d8255c06c51c85bd4de1756303fe02a2afc0967f4db40a8216f8a2161f8eaa4cb4775fed9707380904c0fdab0cb3c153c17a9c7d01055bf538fe9bd1441c0020105a5f0c7707befb2e85f26e946f68154f82f26d34ff7b45c4786570ea399b1001831c687a2fdc9ac5a79028f35eae9a241f47099483e2a90f791d8891b960bd904ac64caedc8d4a379370a419b01d0fe2acb93aba69278bc304e75021d333f5b995e564aa9a4703c703390300eba54840b698931546b4c7138085a2ae359dd104c7b53d1d523b74e743a711e429d68da25be1f6cfbde3e04654d2cd157dc0dfec3e72fa805efb6f59b03645cb34a80434ce8ac081cb023638ea95495c16be72c92e864c08cf487583f0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3e96d05fab903e5af14cbc3a202064e0227f814dee56b1490ed0280b96cfaa21","proof":"0cd4a11de9f980460647ecfefe3383113a577f75a12affc6b1b1dff470b0d25406d0717926fe008e2e0d17e6ed986a522fb09c76c25a2a264fc56db87aa1db13de3bab27e78b5e9977ab37192c2f2af8d5a2b3445e9537f37b45c68c1092905dd05c6ab398a3b3f52ef89ceb0dc18169e2e8bed8568a07e81264eb96ae7afc705690997c60b225a02a59cd34a67818d9672c4a9fb30b2098b0d7ca0fc3d7d802bf5d615eab50f04d6c384950ec73efd8f7d0f4ff1e1159a3236b2b339e6aeb0c0174691ed4087836d06193fd20a06f4d57d0b37ddd581ba18ee59781d34ab3010e0b2a129b675deb7004c0dbc6ee8e315c23087e030f5156230c0ee051c4f921241aad98ea709fbe7e061aceceab88b19bb21f178e8744eaf401a95dad1bfc531677579bfcdc2a9ab64d5f90482e8a17c04287581611d0325b4da5061964e11814363da7b02e166f40c9b198214ef5697ade1a3214fe508a581e49ca3a8ef55936b18e2530edbfc380ca0cfbedcee26ec4bdedc2fa7177b502dd84d37152a92fbc16724a8c9cd5a7c61d7d63e3adf18560738ff051f76cab9ac5c7c68952b21600e9fa12e4ce09d67bdc6f5060df8a0381a2c547875365f2fe96ed60bbe2d1484a3c22e497582af3e922647deed5e5ac8c48975ab7bf06672e8fe9e37539d10994ec7a1193aa2b89d091dfd5e7aeac92325481f747c07800e535b6e3fe26da02620fb0d87d51871a3e52610d3089bc53834e805a391f4810ea95de1dea46423c7a8a7093e0aa7f7bb3662c63066ac125feb68b05bf1b0f3bc913306f07d1f901a43c28d2dee1ca87a731353cea05c77ee6c8b1aafa745c2a903134f3c07eb93d573d544826c98161070b29f582b93c34d279509d45dc211402175c7364b3dd00932794171e3ecb19a848fba1e4e4357206b8c4fc142b7f48778c0819dd0f6005"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"08ee3f4989a83e850e827c873d3b87cadf02cb349c4f4d7f6a70f37c5ea26237","proof":"709b9faae9f3d51c7e53bed27bdefe461ed5f2c78bdf5ce423b0388d4c85ac723eb7317eb4940e1fb79ea4cd5933da97fbab4c44e356d395b4ffd509e653a701860584cddd2e7cbbea3340746d6be528db4f1ffa662610040de8613142891e62d062925b29c69197c73f06e0c3b7648b5dd6e97b3f113755854519aa8c1a8c557963b47e53c840a8eaae9f6de4f7ff6def03e980a7429c798fbb495e9f25850848ab130c0bbd556f66272fe15cd192beae79067faa33c7e0ca25fe5c437dee04da4a463e1690e1d7120641a55fdeefe8a7c39a574c3facd5f7e2b08f2ce20205cea172ba34a99d80331ce20d2960a4f4b14abe4cb7be7b76ed73e04489d91405343fccb21fbbfd972243c194be11c2c528ffd63f56585f112db34c3f5a852e6a34b557303172ca3a6f51680e6d17774666bc8d214344f5591c408f8af660703ca000e6cc30998b30b83da525565c575e18b664846c64004797959c79ff1dfb6946fbf62786f1d067a9d2f5c89011495ed3b0c53bd9d50ff5993a01edaf5d1b331a67ce3c7af3a03ff2da5ff494cff5f52bc03ea26b961346bf98983b2257fd4168338e173917fcaa0290b06680326ffba9998b88471d5bd0a06714ce8b09e046ccb039e777d03681534902b2b515714e84c6985cd2578e53092639a9f452d30f563873eb2b8410ce991cea2c46550156526a3a89e61c3fa57be43e9c6bf172602ce2b46ac57eb014699d6f6b4b771a37824867e69b39d674619679326182864d62d4a5b3b00a59de34fdd1d63214f7d6c877843d5c02b8e0154b96450379f9752e5de7c441ac748920a107d258174c075bf85d9a6001761260275f58bcfad30bf9c44a46010afdbfff2d852a10b10abd6c36667900efa2b9c151a1e1663acd0625c118b862ee922ddaaf255f0b917674ef36f5cf5016c014ff91047d5d5f880d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6ccc30d9d8c553a33a7f9b4b9975fac299d4a0cf02caee5d7860db4ec00a171a","proof":"1eef4b700b8830c2ace384030401020950eb6653f9da40e19afac3cd92b3d07e2a15d7fd4360aea45df290d59b9aaffce3ed709f6e1a49f90494fc72a08ceb024ab57f19e6d3283e7fa09d3a7750ef407c4a29ac99866235e7746b9e02801a35a0825ef77e2922858087d0f45bb3f18d3cf9d4a6acc5dc1a5b5a558d68e96106aa8901d7326278e440bdae7edbaa5a35fa78e87b3cdb54d9f5607d1f41a368044ebd31c8583b6e6293604e10066df22ec49fe0b0c335570cb9895dfb6c05be03e0e1c358b50f34f7a40cca92ba29517e721e6ab4830c8a7f6671b6af630ce501808bff37dda5961ae7ac09684c9f219b1f125c2b3468a797f823111b51ddad231281b3617533da3a9ea823a34e4f8fd3200c0ec274f14218c29ed52acac8a55f429cac3e316b39141513a74beb23da58b17b721ce289aab27e063b364514a67e0a769f93345711fc224410ad4c44e9a15065afcb850d87f8bcb67665facb511d5004d9e3ab653ca93166f9fe05105e41f8cf24c62a099b85e8db0e71b3508f4ed862f0e43cdcba053fad8f3cd31b3c204057a913b3f5f011f9a07b8fb15d167d0c1ce0d4081f573ed6c6a713fa86688f2cd8e2d94bfa3260d1d38901fa769f2980b3136bba7ca9411d881e16115de101da2300645629c6e1acca17f55adff52f660f324cadf4aa9bdb511e492d669a2dd07da1a1af4712ebbb20868e53d96b3d881b84c59851c9d6bd0dc0543e6f260a77e149479f267fcd1e7ccc94a304a770ec96118a828941757b665423062c6b2178350a13c4ee8860edf53441116a596c063e46a24fdb10bd0dc046ec6e096bf763c2216ce9d58a81b788ca645788a048fe873e548b5d4af1f0c2d0397fd81e08753f7def2342f4431a245c91f9f9860c845d3ea088effd6add7439412d288442e69cf721c14e78ab4b219207401f2d04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ecc67130b72234adf91b579d779fd7139edcf5f15eb66c293414e4dae1ca6d5f","proof":"3e38261bb2a06602be5f6244dea3821a0b5e632d8ad79383baa47d110e215642aa25ea67816601394daac697d088f425e09043349d4290c24df189824c6a911a4a3ca457a2d161efaf69fd93dabeab112239b5e28d01ae4ceeccdd5d12eb2d3b60ef4e8b11c9d64ff99077ca49641a94e4197ee23fd4d6343922bc92e082221682500e831b58bb4c71eea7ac61a6bc713adcaf5a5d7865856dc4a23b90c9bf088ae814e30a217f1bfbcc86d7eeb94e002e2f50feb4bd0ae2fdc52637e786730155ccdcbedf97565f6ebcf8195b8b5b08fe61218a5aab1c7e8c3baae8b91e68071424b30e0cec2ba6a1deb60b227cf24326c88db23d4df07dc0af94eca493dc34f4c6e2fd01d5489abb32fc0dce44e656181b584729311522e441322000c5896d94b7ccf4959bbdc37c32334d9d42fd3abeee9608626003c9922781f9a9e4b15266da737629748218a58bc16d32d3fe480b22a083e37d756bd67cd0f905719a140296ed1ab07039a23500c7f74e2c81b3e286cd111f2523950320c26b3cc1a40c02901e668ef05d2cc236cf4f23588e530bcb244bc2ee97de48b92f9d8720ed2b38c03380b55a2d80d94e3efab39fd8e7e8b9b6b32ca2f5095db17f1e1796310c60bc771febe38fd1f58423c65fc18c9c0e4d5e2ad5f880c425c423b0b9654c5da6c10df13f44d159da80343d2e12fca09ea3372b15c4e6862b46997c52149c4bb01fe12929549b84625ea04725ea3429bde49b495ed9f4d82f4da3b0fa8eec6d4ca22fa3c82a3d8c4ac8ba23631ba699d1110049dfbe14f8a034e94471cc5507d00339af88bc9325f270373f3b11c86e7cca823118f81c5f17f04cd4b349e87c1b697936b35ad275b20251e0bd114fee88c2eb730a5a78a0b3a30f4a2e380b07ec627195e3d2c9496e165ac98f1714d6cf42886993cb436eff0e49436be97d01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d40323649e9b62d507d9316342c6b951d7c0307785da677c5ef111c15c847706","proof":"e03eb54873be7919254fbca70ecbeff92e1799ff312db903f463ddc6dc8e19473acb69b853b35604153faee05ade0d5d7103ad1d998c1230a1b12690a9506429aab0d062685f17b0f0ad368272a0f6d5a702be0cc0b1f685013f9b12ec1f8563c03cb283a4f8349c6f3caf2f4e23629b3cb7b662464bf6f5429fcbf741321e4d5420a5e81f2926fce12276146cb954759a22632281fbddd505b9608fcbc30103828f19607bb2376a731091c302cd5fb211e6d39df83d2c3acc63be2fa81fcb0487bc5c8581f94aa97df8339cfc5721a7875c309df177681a93d90ca3f160d907b68303aef706c8472f594d74bf6aebe189735aaabacfb12374b9868b42b4511d00029acdb8f50bfd94c39937c10a696e10aa4b5ddb92c0eed02666f9d375096154124cf1ecd6ca9ed149372f66dc1bbf56e8f39b00ecbce80ac22bfd2fed73281679d712b26ededdb8160c3b9212ff71c782cd64b457ecf4d7d1f991dfd9e33f2cc3c2bea88ecbd5056b3d2a927b4bd0c85e11da92f551c23cfc698a31ad5b25ceaf6580abb5e0628d0de33f3f9f51071be014fd15da80b62cba0fa77ce7906ad89b3be21d7a3df9b656158e04f4a455c390a92214680eed3b7377f6d8e6a2371cc3ec82ce28b4719e30b6474a04918c873d85cb8cc789ada0a88dfab6c2c37b9617fa5c6a8937053d2b0118f459ac9d4a6942f8ff30b4d0cc415ab581c947438a6100cfb565d4c9cbdf55920a54636573d14def93b415ef18d1494a93b02f3fd4547a38244f95c0b62b92c89bc8501a857f14d77e6e06ee2292e49d6b6c5c268a1920a78caf8ca2a6bcdba826b158e6477ba29fba25b13f9bc482bc420a2a079000fb2b15c6572dbcb3134b51877f9fd58304cd2730bf6bfa2b9442b111ac017cd869760258e1e96b6c886fcf3208b98d912dea88250868c3c0a14000398207"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d6c058c643292f7d94749d1ea364a4e5fcc41b9a6e2c29e52b3b736c3c668757","proof":"90e8aacc447ee6f3f101503f18d9a98d96a9a79487b231411ec1436e4793a123e2ed957284df7c7ac41b7e8c6fa343c8f87a709cb5fbb30d5ee120067d966830fe979b174d2b55ea03710c6d6970cd14be2ca17f40b98c5f2b77b673699d877b2638e2d1ad3402e878b7f850d3a46a1a39a387ff8abbc801d3062326e9ac55296e1d77735ca3b9e677f448464d04cc90a217bb9732539013de8a53c4d2e2680119be2272780deed89512dd475d3fa437010c0b9a665102a3bd52804ea7b0600f214e07d40ffdb36b310474680cf3c1850db608671dea608705bb8a93823cf002a0da953c135b84ff615b59cc63b14af326e1173fd03717959431fd9d4eb393292cb9b5dee4db2c4ea2f3914611de01ee8ae9581db31c4cf1c87f0e12cd2d4b64de0ac199d72960d59557dd903b76fff6688f47e290bbcf36c3189deff7bd114aac795fd00633143933dbebf09891e887ee39ba293c516ce8c280d6dd5bc69634a8fe8e3cfa0f93c4d7f8b47f0138373f3cde21c6e003e7ab57f891dd9d45153c1c1e31770e5a6b1138ac60ad1d037e673396d2f3b7cef721ff40e32b3b0b2f624c196e9f2ff548950537d24796292936f7be5956bbf821a56877ec2776e4895732a89e70298f3ecd24372a65c79428c92b6d7a9a57ba877716582524ba35e545866b0e9ae29cf7ce711d3c8f976abcbd07108cd647d7c770c371e27afa88e85d823609a5cb16b133050039cc252171cdf5f6ea9fd2f0432226b4de833dc04b48260e65450772de97c05a80835415c7f3b5ff140ac36cfd97a857f1d4a73545339e2fce98ae2bc1b87e6300e42a86d751badb76bc87e534e7ca97ce283e2d21318215255e36163d786d8923dbd42b01bc39026f9ee0d0b43664a6cc9c63219108e34913cee3b62722c53109a379f187ce2487e205067331399645ad00290ccc08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"10717321af2ad2c10b2bbce803968b2a15d2b451146c0c4810c1ad5f38071667","proof":"52f144222483e2c87cfbd005d966b26d164f7b93db34ece7b422d77be2184e046c48281477dffb9a146391f5418f16729da34ef08e4084f83720fd90f415cb147cf872697e0446ec92c3cbede259dfe87274ada93fc9dbd765f025801c10153eb622b8c49e79fd5334d3cf86127eb853cba8700aa6798843cba26a5a08ac977b1a08b1621ad218aee0fa30c9aabcef356c260f062b5072f5afaa9b127c39e306eb832dac5eda2bf14497d9c1e47e56e3f9bd5daef429edb03147064b3a82910684f81667cf6be6c13f2018e2c85bf3d2332334160c96659a63e663a9316f94033ea40927ec32e81789c055da35fda8f80789b810cec91a0ed4c66dcf034fff3058ad0d832afa038343c2a5658d3752a33a01664cfe893a208270e9108be267697692a9dd631685fdb8207149cd8725a8c4dfb4107a96f4265ebded215a2dff034e2d6db77285750c45d2c7cf46ef08bb3f521e71954fb1355048388492bcc35b144b9e97bcde07ea55c00d17293b26637ac0b61d99416fe87529b88de082441218eb5564f93998e7a6f33ebc36ff548bda9d329460d113ea7988c66e9a091e4a160d46282599c1ffde6f49925eed9db46264102a457eb9a2c2e9a527c730471d460aaf6fc998e31a301e0ec63e75dbb05b482e39220a331ab1c0795d7b2c6726b2baea6a2abc171ca571bf3aebf3a50500ca541efce7a99aaa19078c16b1466d0023c643121b4a3e1efa3a2b0649cc18f9c00e273ff1eacfedc57b362eed923de8aed2320c4ba3d400e788a694d450b305749a19a61b266d51810fa2fee79c780871d1c5f43fbab39a26604a92f2415374e162a1219b68fd7125335e4e7345409dd893382f1a0a2114a4c3ccac8cd12fac6170907ce4490da068444134ca74020f6bf6f8630cf980fb2bfb3c5855f4b4ba40e7dce6ebd9ef131e5dffa9780f00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bc83d3ac8e8b280256a72167d0ed71c74e2ed6ab7a10bbea6c68756a4d9c292a","proof":"5c453ae734510ffbe1dcfcfee0654668ad37fcaeea7b89a0278a6f0f281c610258596dca17966a88869e603f123277f3354d8e36ea22adcf3b686781fa204a2b8c0d0df8ff6909c4ef79ef71c0c80acf30f41de81ca619dc3929800b153be346e4511765b1a005e0d9729b9e512a293736eb0399a48782932d24683a65c7c7564ec37fe850420a127820ce52a2a0a87964b6554fbdc74a6ba27ec879db651209f8219017809826e26fede420f4d48e1f2ea1fc0557d768c98f70d0693e149e0977c2b53fa39339005f7d326be2e7ac41ebf887f43af65f8ef9806e32de47f50742abfde85f54c8ab925b6b96ddf66ef7fb3901c34a639d2c26980eea1cc6ef30fa57449c0abecc2f0066bbfdcbb16ac94d3f4a1a157a52d5790718540d41d0076e874d5d73fcb1666568532e01dcd605ba570caa312c53af300c6a7858d1056d42062d658810bce54fb37441df49a1426dd71e8818af4023e7722eddc8e2c971eaa3c5aefd6ad9ab185e799df92267e5151bf39c536bdc07c81cb2423918e16134759614c22148d0115af046e6c5f3cecc3d09ca12d443aff565416d2a5d762f6c08b0e43ea0df4ac84c4bfe3900c681e508ee62b837679fe87f0a60f4e0d73bc6674288241ad6833beb56f8dc4cea6f6d5980e292ed227fc1b1303b2df23875d8eac283c1aeb3b4de00e3c4e2e02825473b2edf12ba7394acf277d0e821e112486872ada7b249836e95d7765c572c85db780e521c07d6ae803da819ba70a160e068977508343a730c76f1bbdd1bb5c16e1842f0469b4e206dbb312e30532165f817cedc5433b4920082abbc5dc9246e345a356c4ed839e994ba2ade09e8cc457fa901fccfe6b13ffc658923e828b2a4f46745ad636842c430f214374dcb91071f1c66aece358f68fe61af1b7072d2236a0ff839c3f5e9f26d4213e0703cbc00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d8fd3eafe5f99605a8700c8978b7db5692c32096b55ee7a959903eef6128eb08","proof":"a260576a937578dcb43c38daccf166dd20ce700188182f051960bf6bfc03073102c5c16119368bb834aceab435d446449c063bc37baf1e09f63bab3d1c51e46ae0d9b2b621a36d284c305d70a655b2a00ede188070a32e94a2429881ccaf183f4e288ded5506f88ae547f2779e0e740b7c65c60fb5fff010408c8cbca1bac52a47ecba15ff459c5411f5556467ecba579acb2dfb6b7bb8afee0e61be961f510118764e065d64f703ce117eb49fdc79717812ccab4d8ee13ef04c458b05ada30f8be217a1b5ca0e8f8ce5a76ee279f9722bde4063a6bdf7d7beb0e5316271c008b648205de42a5f1ae38053d138d201442519553f7026bf9d169861a423cdb658661ea0d61a3dfb9544b685fc47ae2e1da1e2fe656bf10eaff7cd428904329c53b49e98ab10c3f3d542158a74a938827a05b0adbb491dbe0e45ad517280b86168a8454b98d52147148cf316e4add7d762e67decb2fd6ad77c0e8613c15205755ab43db85b063a15c25eb6858d770fc51a8fba11c9995e53c308d3781224d3a04f82e97c26736a767e3491c85b279e799cd383828b832727638954d9a5e9b40e4e0245eaee830ecc2384841070638c0ef95d62de940a0fcb4106a167e1ef314d214a38615e076606996cc4ee4a609937334f8b956627d6147073f5d511bf89d61426a837640872d7f7570b76e7ccec7a93bfd18b50b0e44ee8780ead75de2784688a2bddfdda549e65268aed149f967b70f04d89772dc3be269b318b3b84ea77521ac803ebd0d82d7bf8b3c3365a48b489b458364f0f22a106bc0abf45b3d8e27a9a844ec90e02732da4cf42d666f620ccf24f859470ec23636207828cc4b1ea1622d695f444739b22143be7405b4599a02c511909297ecc74eebfef07aa43300c20c798157a7e698ac346c6eb79511f150f54c61210b96847f691b3d15144b706"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b03ed21329d01cf14ad556c0f49e07fd47d62ba4ffc301b96d457964441baf07","proof":"329d8e562ab94371e5a54f27d726518d4e11fd8a5bbe6b5d749d77e0e4334f27fa165d433eb760f781606a4b94f8ab056706b8ca37301d22e35af9c94e84e017c00271f0b7ab2662d87bdc8f72f8b9dd44bcd1032a18daba0664481dc1066d148aa88301857420282a9f494356dd64f5ddc2af616c83e8ea491911335f743c0e34be0eca1a3c0c70ce1c94663fb77f5e93b51f5ffbb416547b011a781d88990eef8c2615692798a9a601ca9b2f518edf207083d39f2a10e350540145e0fd460cb8d0e85b4b51c9c2f335c3dab44fd8e288fe047ee026267ade15eb0d6cd3990576ce92ca45ca7436023de909ee7979496100000cc7b003a3e2678971287e2c380a367e75cdef33e016b22d27b1c4ef431acecdbd954470b0d4f781d1c9af06157aae454fadcfc84fc5c75a7e37303b0d3be31360183f7e5704aa05151492dd5b3a9d2ce88308195c0f40ee3dcb0747d48a9cd9da0c0e4d91cbb43b5773e5e47afc16e19c5643857b4b43c288056bd054787fb7e13e6f9fe86cb6c47864d2f2159635aa847d230f62d5c629a672349cf0ac924d97ecff82cd17121e16fdfdfa24ae12074d5db77c8ba4062e80e123dbc2b408a1541290fa40cc4596c0d002e97df4fb01b5f9156f70b1fda83a2c67cc8ba7e850ba714f5bafdee0ed25daaaf837fae088a12274c0edd214dbff64e32f6c45b91f15c030b33c885d34e0a4eb5035046894fad76b94217d54a8b88def8f77749a98015de543b9d1ddc4182b6c5d279a1ffcbb368945d0d3ffe40267dfa1eb688156281759fae096fe19c5302c22228a99aa25abdc0f96751ea632b42f83585688126243f1b98ad6b4a59044a7bb7c986802b6397e158d6d59a873fbc1923aa49ae9faae83247dddace88b9d27380dfebd53935bc9fa707c5777117a34255ed871d6132cd0494b353d8c691e801602"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5292f34d71b9ce232d9a82627c6e40a7bb07e8e33130836faf984145e3017d0c","proof":"fa484e0a3f2b5e4a5ed82a5f05b36241d2f12ebb015b34206b34a0effe188c0a2617ac2af2f5f2a185c114cf03d90bbb6b311eb3329e44ca60421f02b3200142341911678a990d45ed87919d40badabe27dbe3f325798b86c5bf4a9f426bad10604d5c8dbe7e9954d648026738198a25f94d2a29e7c5c0c6bdb155d63c09352b68558331318b96423fa9e05ca006ddec1ebe29600f8206cde54502464da8570a91b997fb292c5ab41cf705e0bcaea5328fd71c2dc5561c5e7dba27f8b10b1c0929f2bea90dd5040ceb23d569f0db6b75d87180199f185d1f3f9752263b19770e3056794b97c1b204f0eb9b9b0a82da5840d4920cbceac71e4288e1c169f4db52eea922b40bcd1baf62b550749b597671491f63d4820b30984896276d202362760a77e2c577c884d9d86e1e5bfd1189ac1227377c0a91d562665ac89ecda8215e92b5e16ca642e1dbea42c385643b2ca553b97cb139877d39dbb0f9d3ae9633564ac230b4ca1e374e1abed3774c951a995cdfd8a6ad4be9067f9006dd5653e95378120ca937e1558a1309c176cbc66c5616331545c5da5b5dbcb59ef56542355722b3268b4a4dee8a2878910b5f40c8624bcfa282a0ba7fcec2f6359b25cd1b40822f3eb424c682d13cf762952e9b844c5ec743e05bf351523e23981ea94e6e216e8316b2ebeb1734e58572be2d524002a7164ffaf71400b3bf623cef3115b06666d4d749cfd780c59c87c15f1e114f2027e7f955c522dcd0336de0eb0676c6103cc0f20b23807bc857fa17f5602449aec50555214e45d253c0a1de797b46103eca3d5216f76b16eb1e99133e97ce1e6f6d393996a7e577c7cc0b853a96934b6d0fa3bb65e5a7f0e6f5922562876fdc7e04a41eb595bea0d47b84ec64cdc4190ea15e0c5b694d60e171000f9f44242933cbd4677bfc0932938b8d4c0017c75e0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dad94fe7e13f5172e6d655bca89c8a84a90d2f7f35b04289de1947d94614ce13","proof":"c6e58678960d4a958290e69a658d73a7f2c2f2fa4114763058175cc67dfa70467864f4e1753c1ed1d186d9a7da3dc23d2e59b96897f52471335d70a881336f119246527ba4290260c77a2b0913720fb8b8384904d643388db1e83be557f77f41421d4a33620c89bab555d32d95201d341b21f3e00bdb3545781de9aa033c9d2754d4800ae4d0e6b17121aec75158970a903a189ad746349a5fde1ec7e3f1de0b3d5a3a630382b13f693946e9bc2c7e170c63645f5ca813a395fe9b9451d7ca04c6ea3d6d7a122b244ae7c8f9d07e6a5df7ee594de3bc162da861c70a4c5bfb0a14af44c6c5d193eaeb4db1334fc4300adae03421abb9127078656acbc5716a4ee42c82e146ac12e9ffba5d94177d611d6dfc3d00e63f51e99ebba70ce2ed226afa0f6586e4ca2321f4e22e927c50482a33267a4c708a26abf41485d6cbbd2b558caddfe84224a77d0bea896e162ae8b2e11da688841886dbc054c70fcd84af4be471fa6e6746625972f14fb2cf9e15c4c5e6ad5bf0ad77b1288578026933e212de297c7ffcebe89561a94182bcf9fe2647001b5b32ae64fa885ff051554167746a347eb1115c8eca5c4ae68834937939b738949e4140b5875e26a4b17f91cd6b6893fcd40bb06158aa654fffe1503ad8ae8d2c97d03bccc981e572bc6f0bf9506222615600d1079aa352fcff9d1968e64cb43259ea0b621d1fe898ede5ee411e242f81335b7257f5b450ef9f3af0b78c456baff1ab531c196f3542e1b38afc34543ff3c454f19d244b3dc66a63de28686900ee89309e16becab5b2ad5c2edc618c92aabe23cac9e1d9123a45b7a1202a2725582f86972fb9f5b6ffb75f77e451661abc5e2bc09afb76828f4aeefc2186cca78605f715a90204357232d719a30fe16e048697b74d59e2150a3748bcfc303ea8899ff5feb4ad2cccdcaaf96a370b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d4e5ce088f35ecec920bb93b2dd9b33b433f7d952bf2df585114d397d3f90b71","proof":"da59b08b4ced1d36376d1b44ac7de712cc0e0c812a231ff58c2ba16c79bc0007a2b5da8235619f109235fd02f664ba24fe2971523fbb5878746b25877dacae6d96e56b57955210d58b68c3000b81f1401cad0099856aa1f230ef02a38b65a36d88bd91d7cf8d344ffbb0c40e12fb00999d1e5962945877f59e006623cd027b0c0cae5da64b6ccd920f7606822020ef1d25d1342e407027192db9ecde6bc24a0cc00b28bbca74c81c5e2b5c9a7eb5c5b32c048f636c4a07ac86aedc6a2421b5038539ce8a037bb575c3baca69afe7597cb3c8066f45d2e53f39078b5bdf82c60c8ac28dceb932c2d03d5cfa0f5341d2aa4bbf6b242273bd32acf43f503de76a6ad632c9d4ccf91e432681c9174ffcb0137dfff2c1f1b4d2e684e113feebec9479121468df456936bf5be968f38a93b75bfe33dd36debfbbd21452d149a1f3220e04ebc12f120ca50b35e07a5ce0e288356b643de360c4fc998d2fd005379b071558746afa0f9c9db3a9d27d70f6115dec6560349ddb391f1042a87865a1d2484ca48fe5bb8a73393dcbbf6e8f55412f32bbdf435e51bf09abe65b45996a1a5d7ad817c08e4924cad56cc6ddbd4f518ccc589765e300cac5cb485146d2c86fec3eaeea4c8ce0a6418464b1eed0b8dd9b8d7765633d7ea4e620afa82b5a0773962baa4a747d9278d0b8a33b84fca981661ccd82b773c68975b6f03523516db36e08c830a3f24ea783640c6c9172238e3825c18f7cc7368db1dec5603d078b6a2b1b825ef1f2e0b91a75ee552c5d31cdb4c252b332093f073f10eb2a0afb5301a33e96193ec082b739ec26016fe9f188bc31bc3f44407f505b7a1d2fd29fb5f900402f319938bf0b6606e0c2e7f81832c0fcee90afd039f8b75c7fe97680b115b8032df364917ae6faaedb3f352868ee1aeecf81fce2087bd5102e2520a4e9c8d309"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1a6d59a92879b7c59ab845098ea020ee72106076609d63337bdd3b8bf386cf5c","proof":"0cf157486f857ca504d6ad602de8c523234bf59e7d245b71fae1e51cae41600214b9e50a46017661f2525244a454de355b525498086360e5b16225b42138e134a2cfe858ac1f56c3659bed0c6f4f30d6f034ed2689eb4f73d736165cc1be0009b20c3c98661123da70525af9590a25fc907a30041a4093bc25e8d7c72d46cb6ec52e92ccc35f3fa6a479dbeaf71c2eda9c8613e373a6e6264c27f44e44dacf04e4d64972df9ea447a15cc34ebe53377e1c89103ba0d6df066442bd187633f001ec0ac583f4495f111413605afb6f282729f6f05138fa666cfa9cb21a75e58e0fb62b2846ea3e1373a72960b673a633049f7f01c9c37d097870b2082856e9de2c00c62b0a750518246f341235b1ea35b92c97590072c9ed104b11f5d65270fb590ccf47af7ef318984e548eda131858e5d65b69efa42bf106d7c693b5e1cd7729cc2dd1e6a2eabe5d6d96b6a70ca696286d91b1b739214070d10710b090163a70e2d3e6df2fcc5d91071afa0731f32094e6179d6ba86f6f259df00788dda6874bfc6a0162627a85a3295e6e0eddb5d3a85fb3afbb200dc9583f9449ff37ca15063c0858a6c9cc52d63eef511c9fd1d4bf8341e6ee3c7750b7674d9da5a0e524185ccc267c063c2de2d2b19609a04e33a4626ad15a9991a6e54eafe9357de23e66c0212e9a69ebf49a1e2afaace8e8de722c35d8c22496f9362c534f794d540a1f5668f4fecc5fa4390872d906ef5292763e07973c6ed018d104ae03f9ae27e8708e8b2ff4448cd0e4cbb95379a01ae9cfa3167205681a95b0ce8e19681cfe515794c68637b106e7da0e68c9bd82ce839d9f1a0f02e3c2d36e87b027ea0bd40d4a21e05f95d69bdfe4c6588c618c13872cd51d3b6f63faebeec03d53a8ff4dd7030241ee4d773683edc8bdf41bedbd291233a17995c4b6896a07afad78102a4109"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"584651439dd3dc7d174a7e24210d4f6e64d05a9ba45c1648e5ad24873e7aa93f","proof":"88a516bd1ee7caae21b5507d1b2cd4521ff89e0402e4e4821dc1dc8d73b41664fcb66365b749945e4ba3107849728e26c5208c00cbd76cc6af269b542324874cc69022986e33f04c7b77632e68d9732f048e77e3beb9692f30be80dbaaa3552f447e869d6da527b270a1cf282632af946a24fab6ebcde75419736833707f8b4d5de7134bf7e53b509239ecedd9f1de4173da204adc792cbe1b5a5848d6da2807a3f1692e8d46e4d5a333732b6c25a2f114d503c7761edd0b9de5249371f0be0f1c7be7e11c1dfbe23c4f34077d3ed4d857f13ef1e90a40e627d816a149ff7204a6896f69202e792a89c068b4fb0d273e2cb2f7cd3f88344264f36365b0e711356814895dd1a8e7add3b54ad5cdfede10cac8cf47c624b12cdca245fb3874830e74f462370351bd1448fd99fc7a50825c2cb2eaacd694527450cb920928059a21a44157e4c0954e599d6ca9a8638b07dbbe3a496a402ed2831153893f631cad5c0a5dc4f6d08af2332a65a00ebd699cff0ad7c1f365f4305c7af1270cab4a7e406271237f59b4b17e6cc629469819f696624b97375fe10c7faa2474f35feece1d901779984e15876cddd9aa3915cf46a4fe88fb430a3ecbe58bc16bc33a50a722a2b7454596d7ee6b77106c4226b8725b12fb189748f1cddd08d6273492779754c835a0c9eab5365a4350e3fd93509a3301730c8b50679fd9ddb1e8dab870d15ab01e5f0fe3b1242a3a7d1750c4fe1ec4b29a6b971ad7412ee17c62abddccfa325edca533af1f7d6b8beae1f9ddefc07b786182ff9bd3a7038f35b065a986c73e406af856cf8715ad186e9a311a709af90dd8a0a61f69a6efab1d27056918eb29f54168dc7fd69e0ddee709768154c57cc97047df17523d521ca4095a54eb170dcfc60be7aac8361a9d39463e44d5d2bd8084c353263d9a8ca5be791408139c02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b48069023ef95055abf9f6dd8861bab9819623a55a392ed5218098b27aaef011","proof":"22b90d3cbd19d6530c529ee8b471c476c6f772991ddaec6aa9a78ad0f9ebdb04f082b0517428bf144fb0da76d40aecede7a88c8f77821f67a5bc9ef6c394ac7574df7934ddba99f0da72f82a23e4375769b1b7ba0bf37028cffa6aee1ae408788e20cabcf97162bf3fadacbcc3f46ef7414776ea4bcc5ca480a766c2edfbc25ba9ab223652fada4142ea4ec7b8b42ac770fc6878ea99d6b70f2250dcf320dd053ab941fdd7ec7de535ee72ae645bae7d2456ae89a582d6d300708b10a205080ea52c496c7354167f63caedbcb9ae3a74f0bed42cd1f3625cc5d87f7a02614000240e7199ccd7d716ba3005da46fb7c74ea0c6a9f4ffbeb968b32da236cba05450eef70c780844bbea214daa76a7315581a34903bfd9f8677a318cae36dcfa30e7a2e30e6ce3605e20e7b6ffca37c36d41f3c2103b2fe63a98cc2cb1aa4ef7f3b4ee456740911dc0fad3c905877a3299e1f2ed1ee21e33d3495d214e279136034f85d4d233b6b94ab89eeaabed2440c860b3cfb4c8a090eef37208164b92b5a5c94f1fbec44abbf3beb47d9bc7b2860f3e1959f5e8dce3bb03bf2f541fbe0f27914c95704c7ae1bd75e4e3520761b5b4a2871401b9196011e1c3f385a4600a454d0631a576d84ec6daf11b7e6538a54096b6905955df3dffa2d18d0a1ccd05a4580547b7af730a939fbbb28dcd1819cfff55ad8d4d82ad31e7365f2f9e1749c4fa8884ed8897a97477c2dff7644a72d3d368f62c46f8f623db70f13f7d1d42e3d12796f1f6ec5824323b39bf3598eae42660321de79b645d814041b927588323c78f17d6ff8e49cf50f00b2e2c7e82ec87f36eb57f3993d029c3a7f16059fab3c8370fd259b6e6ac4a4d22fd7af9bbf7b6e4abbb182c07bc601e540b7b4c4290beecceb484f6cb6cf83bdb782b000958c5b93f479d59b609f2b4af52aff73d30f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"229effc52794d13ae88977353630faab2b1d9c7194979bbcdbe2d002f2616b2c","proof":"fecb22d92b5b651de38e0c420e4122af7a42d1e96f6761bb719122500771ed08680c2e06ec04827f1dbeb3d8d6eff1f18bfad10888f219ab9d134e8ca3fc1e4edac1c476cff019b9f1b0aa1c9d836ee7fa4846eef5961d75bbb541e1b4b59c5bc45ccfdb8b2cf323912cf245c4b80707ef3fdbb8dbf0afa42d6611162af97601027f7048c0c82ec59d3e569e03f0d53cf175badc9ef26a721b6e9ea271449604df281fb322065a1475fabfcd61404429b2728ae059f702383e968ea134e3fa0a25cf15a615a4c36983b97303e8126c18d1c6b61f9f130ed7e440be457c367e0baa909ddf6130b73e7964f49aad4f42f510bb75193dc99fa614b44f05c28c0f29067c9ab0634a414c0a73768459e06c62c51f7c881e59619918281bfe1863e9515ef5b82fe2ed645c197785ff11481c7c9c45528a7559117adffd58eaf4333c386633db6e2d88e0d14092773912c56838768ca34208214d61bcdab72903785611caa721f17653d6102be2c951732eeeec820afa5241979c815aa3ce560ddd863d689aab3b78fdb1491ccd0e07e324178ae02dd0a0bcce57f5f88aed8ac6daf5233e5a9ac0c0a386c0b19f02b167525f40192ea82ce3dda8bf859f5e5dd9afbd464e125c726000342dbe3dbcb3ad204b5ae0d6ef07e858bd75a58713f774af420be8139ae7d23ca359abe526e5b2701d954ef1481947dc41f226ac49668002e70d0cd808dabc84456da6f02b8e3aa01b770bff81ae014f0b8f508e1bacfd918d2a8695b81ff57ebc6801c786d1328db6cd8deb429dc15fbc2b1f26ead77f018471aec22c2e5a9863e1e2038976d681aa596eece3143fed82eb4a31c99b27c5b64a52d06bd08dd9634257670c2d769b35c8b0e6aded83789bd266d841dd31a61d0f8150388f4a6a6cd6ad83a66ddf9ab9b8c130d3beaed3718a87995c29055ed80b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"40a7b9e0cbb3ecf2efad768b9a28799171a2ea9e4cc29425c7cd2672d8651903","proof":"c825643b952c098d28bcb9fe525fd947eafefb09270beb02bd18ecb10fb83025f4462a37a56cdfc4a1b95191d06873ec151195e915b3a0fe28fa5b6b59e27a5e34bbab3c9d77ce9b707415eb47b47e24d554746f7ddc9de4a26859d64003290560683c7a72152fd77415b7201c51371eee0e15439a0914161e1340dfb30a9b41ad6d62d1cfa0636f31be6d60105431cac84935cc7f294ea452256d3bb2f31b096e0f071c48ce31a257d620f310e530f4918dc42785f259ade70cdf978584ea07a9ebdabbe6db2d332d1300bad85fed96e4059b23f49e9e927fa8729a1a19f9005cf5c7f7f9ada6d5e42d17554a95e48bafc12ad62e29d667b9492683a67d6e63267f46f65b80a18edd27efb67915be94d93bbe6eb72ac63503945ae54fdb3a35c2f39e15db28a58e8ad9746ca6228be5b9f048cbd019d344d5b7ebf915851e76d0df25356c820214411617ce45971eba2b0c8633525fbb0bd2b298c7ba88b37b94eed0c978f1ae455f245fa038274062dec0ede624ebed85f36963b99cf2960064fed42590aef390abf09cab9968b91c930ab6d2449ea24e2ef799867b3dee583a6e8bc253f668db7f310549d722e004465eb675cdf3f6ee5a7e895a03e63e511ce7019beae16c1c4cf6c5ca1387145ba63409dd90cb59f5ac42b96eda06420a568d2060e1ad23460749afb4186025e42bf6fe0a5726061bdef1dec602a76779e0cdd0a6feb4e9e7bc9c3ac5d12b7011f35997fdb618a7c36ecd6542d2285f30dad6e840814c68b236373b41f200177b16da510e02359c6c0a62b43a8a3cfb6626ae77938860e7b25d92c16bee01313b0b290969db59adceccfd5657bdd6d8243fe2559d94fac516779dafad2ca7792b8eb0cc53e2ac5deb29bdc6701ddb460f628194e06d263c9517304830cca938ee4fc93efe53b25043e47af633a97c6606"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"204c16004906a92c8e539562614bfae9d664e359a30374654f27762d0d717764","proof":"981746bab379ad047b646b517578072837610d18ea3a0ca74e2edd18733b170882f10ab32a835d051d6912f998bfcedcbd2bc87472889f4ed042a0c8aef32062be5b61d9f99c9e4222b23ec33009711e06a44ef9d4aa2271d40daa60ce129f2af2230e7ddfa6372060ab2763cca2235c4a4b899c90016b2641fef73ed1fa6d21f8c6b3d551e3d2e73f9eb894fd1b1fbfadeb6aeca6c11c2db66401351dedc7027e4538945c92ab0cd6508772b2acd9a07478acced1c447f91173898c23d9e6052e4c3b6717471e058b4070b6c81b8a7607584cc2371457e21bf7855df2b0ba0c8cf0fd00aa6bdaf764bef1040a9265e846d055e6e68a8ecfe7aa3ea25ed7e938e06f1d54124fdad15d49d41a47022cf0cc32bc7dbf7b95cdc7ca5731ae4b982f96d1bf5fda4eceb92ffd4f39798bc78fa7d15e778865689025e869411499e11a1cfbc05cb34c1cb7a7578b88b91e15b3fafe087be4a5f704163626ca34d6e95f2e9a15332e7c7de0791e1af0de02556a7e8cca7bf6c5327ad4ba8e0af27f846b5aef8556c4f03458a3cde3af0da9450bf61e72a295b540460637dd731095b65eeab873eba3d3ddc7262c682e927b4457dc8d51532e921a4819833d8cca8cfc3b1e0368f6afacd943a5448a799915d599ed3589ec1956f6238ec0378d7dca88647a442be8180d7469604548662cbc67a8d478bb1989c6c96ea9708ad2eda3e27fecb02c5d833dadf773d3a12a6675de70cf2a27c2f610d95f1eeb53e7c29f223580ad8eef5a5ba998c44dff99df1914390eecf04bad7e476935274b2822fc6513e82b6bbcf4b822781b8dec1a545ce0afd9267a6f2d31daf2d45bcbc582d8a1296d6cd8ec06b2934efb4923fd6f2edd232590be5aece1210fc46aadfff04b9504ee87b7c67a6e488717a47b149a6e0289d3593fe9685afe38ebccbeeb39b70400"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4c2c8b4788c862b3ec161b3f180f88e7fbf8ad99ad81a5aab38972b3727c6018","proof":"36c92c2857477d4dae7593478bb33794bf7f27c3fb4efa29ded3971862312568bca2c6dc002ba9849833ab2fdbd16e8155d3dbc57d1f4b0f1b06063dd71e1b3be40aede2593b5e9c27f5048a79c6241e7cfa012355e55653ccc43f5301972f6b1a1dda400810b66919b1703b1311afc4f2200c54d71806e9db8277b27fefc77c8181afd09926f28e5310c08d9e7eb13677749de7cd41185fe50ad11e97f8d4072481387504f3b1866daad0d41a8800982f0cdf908ec068575fde9c5f757ec30da71617d95d815b0eedab33ffda18c7c7078878a37ea411efb212fc518a79e60dac53e7c0be590bdc6be9191d2d091bcdec920b27c786639f70cae09fea9c0767c44ac9721e4211cd9aed287adb429f4eb157d7836a8a8552cd0428ad33472a3e08f507e3f708e4c12e9af31a65ca1a07786bc31d7a674e7dbb473d96ec9a9a53621550c848ee1dbbad472b09eb64a297c0edd0e9c4979aa95e0dde9d7a60063ede2e07911a09dbd63d5e732bd3921bc36eb679c69426859ddecb28b69bb24d21f2c3bbf064f60966739bb6e302b732c32054baf7e6c24fffc8612d80528d5012de754b890a9c740f98f9068067a43a8362cdbc953dbd4da49ba186b419ad717e260c064c16e5690247ced26674edc928a002e39ac606fbd74cd3f7485c5802425280bdc60259cb2e6604857dec4424b01fa402324d0f6a62f4f8e63531dd0944aedf37c088f9f8663edb47b6e5a0747ecf0e16c991a759b533c63456afa4757166e8660079c19806302551f0d10b27302f9aeab07bdaf3d15798320639ce912fc8441229e15eb82f882b1bdcd4d3aff4ff0ba5b1710e43e5cbe0adaa62635b2f990ce01ebc43d743588d10b04e50b04315d03e7b580267badc0f81d3529ef90cec273d01334119ef04b3e5c54902ed06ec8b90594084748e3e08178899c6450a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6e276fb5089574d30fc75b8e344cce4f5a37fda38b6d4c2ce09b822047bd9750","proof":"3c912326d2248063358a4d338da84882ef7682713a76b5862c5069f5951d925f9a0720dadc7e02abc121a52afff360f131d8c45f687854567647d0c0a45a302154101b9b6b3286f006987b64a00f2842bc2ba3176b356250fba156c9c300c96c964f13c4b45d211ee6727ee391919874b2719a4fba1026255e5f09493c80501bddc4284c8adb62bf9c7ed7c9568fecbee8bcd54bf3fb4450ee79cecdc3202904ce635233a6aad619591039653345394ed4e4fd612e726b7d7f2f55193be7b10d1ffe231c9c0ca009d79391e81912ca717b7b7ddc641e53ad09a6ee11cbdce60036c61f621370f8b6530c956957d459cfea7b975a517df1f771802fa4646c18610609e60e92dc16a04f6bec6d37f1ffcb5829ac455bb555aa3b1c33db4adec356dc0f07ea99420f333cbd133bd044032ce6f3edc51002d336c0202e79f1ca3d10a09648d983a5db63ce642c0d7f8948ecf002937d74a9be90657c068bf3289144b0ec2c7352dfa2ae9520ba8356a11f9371e7287956f37348c223985930a73f16ee391bff02807dd1fba78ef0955e3f19a6763c97e0660a1fd6a9bd50e4a367334a24845bd59669e966137f105ead959451f1983642e3bcb3a4a531b2c94c8f165451012563b36fdb21f7419284786ceedbe4533b0bec7d3c2c8fd07314d59f584814736c5a1332e2bec87095eeb82d95bc4d56a6058ed1fbd26139d377ebba303c8fda75e47f66da33448821e19c4df489aea4b985d95f758d4606168248ac308c642d0a33b60ad81b680fa2aa34e6602b0e2106be42b94947666a48393dfb745ee1c4cc6dcaa20806c4d38ace80c48afd04cb55eef8c79502f42ab4014f5a632b2e414a4f3e306d8182f93a0903179b8860f6831fc6b505ec5a82bbb3aac80f6a13bdc1965987d0dea5c007e07ac7dc742828e5ba1d337c312c4dc50f184d01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ac06bcf77364a83d4489f51197aa886e9e0b92cb6533785646ccb8cb0a24e461","proof":"b410d471e694b49bd5171ef526f715228b3faec2fc40bd078915ace79271ea44d6872691923bae600c7f047a0a028638f1f8e7eaa0f62a9431a7f7a43351897de6ec579ef14bde3d49c213114d85fe10a223078061d32ae45bc32309f6eef26acef2f3841895aca761c36acc493883596731e641277f1ade1761008e64f8db71910992528cc40472fd89e5b6b803f1c51d8ab09c345909164fce8506720f700fe8c0c7b37fcb8d5c2628857b64c88def55f37fd27eb3bdfd5ac56caec06d910e9993436c6da0df68a5b1d3cbcb5a19c26032949354b07934c1f85c90f1de8e06f290aacf63f1e098c441c857e149199f0d5eeca0b0f3a7a299561e92e0754d4b2a1a182ee1ceff69b1696c55002b2053451d0c46e67b6b4b41a8996ae855d3613ef5fd4011940e5046343735596f58f955d46108d2a9c0b3e446d0d0ba2ded72185116de054332b944517a90a5d033ecd310d713f48ae716812151dfecbd4c60307b191a5617c23e11954fca6e6c34eaaaf65aa9838dc916da09435165b97470c03b006217cf21be695ec33216946d93f13be53bf0db71d430eca79cc1822e360e89d3d4ba22575d6d5f0ab2fd8afcd5de03c903c044b6d264cf57ba03956d4be0fe5e82bd4b9ec530330d2817ab70fc3330fbadc6904e6eb1b667fc753585281ac943076dc7db7af0e6a7a620cd3cca4a9181dca45b0e45dc0ea33f800e761b803a51d853903325a7b4ede16a9d04fc4f6c1102ddd58a81534ca677e66d5f5a30da2a5fa5512621e7f6119153e38580429edd15cde418a0e242247c9f22715b28715d80dffcf524d16841d7ba286aba199847dd54075340efd662b5a6ac666b565137a9e19019f182649ef9c0555afcdf3293f8671010a382b5cc8ea180f203e42b08b779f9449161166a01a5ce3e91256bc2269a0a51690e4cd60251cf300b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2ace61c68d0449ba4c628306704e8dabd9915930421be5dcb1e09e19b4381e6f","proof":"68c227f8574714d4aef7d2c3c6f63ce2f6be45205502d4c2483e5e60acecba4074ce9f70cc351d0e78eff5cb2bf2f19c9164307cab2655e62fbf6261d4fc3b591ade0b60ef18b06764765f1a8a21f5445ce4cb35ba7aaa9fe2e276a29fac5627e09eea64bb3e539696490ad0c43b1efcf8b9b729e0b8bfff112270526f067431d4b03d9ad16b3eede31cd81888672d8d05037f788be1dec94a6d3e7f1f37c3071c824498ffc5da2593cea9bb19ca8a61ad3b72b9de0bbf7f1c7e9e8944969e02620c2c0001fbd3dd0a5ea1c59bd16bd93f8daea31ffc17fe9083f063602d8609426a006a168774c995f9574451056c0f46497fffa7ee1d39ef8a8f068d8af92d7868a8bf6e549713f637ed1bc09b185fdc37be9b643a19055ad955588cd63c7ccc0153d006f05d20765ba6161216dbe15dcb4fbd2e1fe96f95b97576dc8d094d18bac5c060fb9cb73a5f1b6692fb268bf284d088c9154fa3cc58a1bb53bccf0e887dd0cc726f6ada4ac76d6100cf3651e6e07417f1e32d54760628c40d0f3812ce9127024856f7e54344211adf5e024a12b9eaecc4e4f9a3a9c0a62aeb16260b10421e00ccbefe7cfc9d80fe7fdd4b71f70af605133562e55e826e1b01319b00e8d37aead9eb0b0ebdb768c46749035d1270bff85ed19858a98c9e6fdb28b243a246336401dc01285b371663f8180d306effee6307c8b1c3de5d134f9e612e5aaa87338c163b6b09a11e416cc58615eff842a54f3d082beab7d3d1adc55e025a56515e130a90eac0952b25cc0e22b1199c16fa42eeab1031f2694313df29b47742e2a97b282c6218ddd3ba03c7b356a59917147973da41905b3fd67e0eae5857d01e893490a3dc64263d32303c753814656d93ada86c946a7a4890a335bb770f1d101278ac7c69941ed76eb7cb647b29b439e79784c2a65c3ae15694182a2201"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"eec84a436d4cbb8c2055af54bf43f586b9d7789a0d971d2167ae59bed92ecf3f","proof":"74077bc1c44e18481c4e6420ae6904f9ab92499a55bc2da7b294c7572739d3587edc44b6bd00000276d0bcbc3e46e651616dff3d45d1c176ca17febca9969203427e8c4c1ae66706d48c57216b60eb536b0b8315eac9ad23a799232eb9490b3718cba9c5651768da7f49b9a491b664eca49376539f35694355e4903afc7c57475433a80b10e3b636ebf0b5cfb18bf77071a0f9b482f4b57194d8f0e6d1a67c0591fec7b15a1d7e04a466cb8c051b7f2f15ae957f5da5ae6c5f877ebd9b9ed90bd1a2e021da028ea97dc7e8a96a874e174a1548859ad5c01f86babe63041b520bd4a2c74aa03142c8b98918f6680adad151d1c1d92c708adc5b2c69d9f300f866bc4f521a779283208020a2540ce87e04cf0dd1afa12f6889ada134ca22d8f03768244d2f0d8956f0c150db1a16511966079b922dceacd234f19ed2190591355fc4907be9d294838fe5c6edf19ca5c2dfd9f18eea2ddd95643f3f071f2df89c291a0a75aaabaf5289277cafdb905fb78cadd7eceee3df5bb32f0e3d018a82bf04a650972752bb7bee32589ecc4bbf772d7d26c7fad031600b8e8a9a1076702a75ecf7c50f78e2ea4a6a249cc85a006bf971c0bc7939f361270710f5d20a946c2bfa31ae16e274d13a5730ce6c16a3dea86ac09ae34cf302b4b0a71bfb77396f1cfc484f8b49463869ce3083768e4e53d1d6dcfc92d1918e4658eb1f48da2e892dfcd5d0b32b230a2e3e8bb1b39947c9f36acc83b468c7b2389ce511a936d6a73fbad323cdf3cf22bddda11e052d2ef4779a67a5dcb939736f6b0561042a454b1fccff9f35d08267873a5339fcc8999cf22d98961f11507eed0de57507a9f92b136e1cd204186bcdb7aa2b9f9069329ca51ec844c6e735378a1c63143aa4a9d50dd00fc1502225976a8c3edb0f4f53953b1fb09fb79dbbf8276a5791ec8883300a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7e30da42372a2bf0f2d917a9449d2526e621f90c4064c9e97a0c68e180ef1e07","proof":"a21b42c04ae48d189157573caad7ac0432779d54ed76a9c55eaa590688b6046b4421e130f3da06208bd892529798cb8386cc3f1009b3fc67c25aa3ba0acaab0d6088b2fef7989bbf1c34a4052991363dd2de350b32a951203080eeec42df2139dc4c55d6c1a3dc23793d75be03d6cd9862e919b35037c7ce010c82e149270604fcdbbc4eeb57a4aea43a92699b144c5fb717ffc7e1f10f7590e4faddfe733806bfa9d41f10e5a96b7761eb8ca40b7393c2b8d1b4514ed640fee5bb0b8508000738a9a2988a9b1b29fc86694cb0ca93545fcf6f8e08463f410e99aadcb8350d001411137f097a2904c78e30ba5de9e2f061053783859e3cad62d7f5b443a15c6a4e3142c1d1cd6b7d4134f2dff2be13fdd6f1b6ee7f862e32032f12658b9b4618083295a6efe482c444c13e2174b80260642a09a4f1fb0aa90f7f8711478cca54b2e5c14e502a76f0413f89522f74e31104ab57b75f8068c7e90c8253ac528e4bf2d834410a80b300e2cc1370b8ab0ce9ff39fea5d4c3277f24c4fc8198060342e00210c2b6a538e0e014359842db33949b8ac526121873978fd79b400608c203d41c9addd3304ca7eb0923a265eeb1fa7170f3972ac25b843eb4f9cb2cd86e63b24e742c23c511746c14be176543f4874d55012f927a049d1225013f6f44072f66e25aa3df961a629e8f39e5108fe7e5db0a263d40dec352b4b3276f728cab2b9c47b2717102d69ec1164c98ffd153c17bbed1902b696005b0c00ba2daefe41890c7b000cce4c1c8c8982841faaab4ac9cb5254928c23cb8c0a004be8a7ded6014f82fe047b696f92ca1f94172794cbd803004cb510ef49a37e549a4a772866e0ee778e7689063fadd67fb80e0b954710544705124f6950afafe50aed7226b0e848e529d9bb8e23d96a2af310a32d12a1d7dba2ff662d6d7bbe700b57cbb1e0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f2a5c3f57d73c808f7f6b3ff4564decbce33e05a0baf07763ee3d88bae492e61","proof":"943656cd38b9254663177a580f8de9b9379f20e97dcf31ce12837bdd55d2261ab040bd796ea1794f4e7d97a0a0b3c7da147c7ca2cfc3e07a6f4ea1276a909c0dd865ed55e55077f82ca0814288a15e58e9e5daf5f4551a90ca9a3c26d6ed1c26e47a97a2cba78c605ee9eb298ce35d8a171210463746e29d13b25d8a9994016c5f386d1e812324fcad8362f1cde47fac946f094e51faa82e8a2c5aed554c50088803defea606d8090db11e2d36ad17d7c42cb9b7e2a33cf083ba370dcaa8430cd504c3a3cdf90d28ac3681c9f55c49e5d61dffc44746db2f0cffb1467d3aee0780477c1a29c3f3281fc34dd867d061d632ab4a7c087a0f6944ba87929d1fef619cc07fd2607d9f50a23d0beaf2d081da9e789420683a1b398b92cd90a989b54c36697f67c79910ea802485a22e38d5d9e5eb4e23f80d330ea290230d72692a5480f482c1037d6f483108605b0998d7b1ce17700400ea7c9d7e3fc497cac3ce407e2407e8e6f3d9240c2323e309533a13498d9a30032df2759602934c597e9424c4393ef82d9525b2f8a6ffcdb3f5b17ea8f9096aede4179098c9a59413dc0752e607fd4a6470a0c338f50573be19017886481b4c33714166e4b4fcbf4ebcdd295c9c02355586c14599f1df36a82bdaa2c0e1b86a71ef82d410994cad6812d2095624c8c16d00148e53f6b85742657abb6e7fa941b12af74168c3509517b49043f85388684c49e8d554296850439167aee44b4a36068daefe22e50bf60ff7b2418ed325685c362c1692d400a1bce2a8f330e12103df025828e9e3251c25faa15d646745115bc04e5850a20f46d619b29d7384a7d5ccb693f2c3ea6138b6111b0af74b59392bde6970222dac9149512f49b7ad0b9dfb2ae021bae01627d5d7180d9a4221e77edd1665ba47b5e0fe7f72ffa7ed8d760ba4b6d58ded10c4c37a4407"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"482ecc494df15c2c2aa39456bd40026e63ea5aaed309c5db3227a227698a9127","proof":"2e18cb7d0e7116f7f7190419c31692f12891135643b76c6fd8abbead83a0673546e23221b21ff5d183e7cffa8303b3ad865f05b5acb2c3bbfb67422b61fb610ac80e36918c48ab0ce62872f02204efde4bc267bd229208eda4dabd7e68f0db30f2e0c6487ff996cee98f6f7523623d84538f0be33334581910af2790d211da6c560dd6e4e0ac6eb73522dc96db20c7e70e8262cfbca3dc9f640e0cb5e54fa7016bc345aab115fbcbd268a405f8c70f3c76295ff2e54b842717bef56588c61005eae810737cfc329e3fb6e5974621b10375dbd9c19be82044502d7277324bdb070abeab653a3084591541af530dc957632f895bb0d4b874822d67bfdd785fd52f2834ef6ee0c7b05824efa5b6132896595138dd1becb182d1724fd828c1a1597ec42a08bb69819b65c3446d94f9346c0c12d39d8285857d28a7b091b3061d1441de83c9abd15122fe5e8790481ac740af7b9f4771e5cd2aeb317c7d9dafac8a099a07352298dd025123ac2b7d4d1021688d6d1cf6369db39912e04a0406f21453e00806af119f2bfeb4e3f35459de0c525674e3f94e61f0a00f525a6b9a26c13090d962c19123dee09dc0c84789b3496e866aa502824c3a9349325ea4c47a4c554e151d64d58d81d93a908d23691f73f2f2d981c36592c1e2551922425765a844fecebd46eb4f4b3282eec86c3af665d7bdc082afdd7035b404d77cf48224cc5190a1393b7db7a55617e8e336686a2a6ab50fbc7a75a16b4ab308dd94d14fd03d8850698b8eafe979f67f59b9508cee58ff6458c08a6f5b47eb124f89cd142437d0959f553a6b5e5dec4c4d16febe72fc4166a037d18071b3f68e8e92bc5efe717b457b2e7d01cd62fc6d2070d1701794017d3625117a4aca3a1d504aecc5ba069f510c5da3f04d2cd64794ce68f7e455a9fb5969f13878a73fa35ac1b3371d0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"eafd766b9514cbe34dd9d888f97008a53f1e23e79dac3cf892c78ae42166960d","proof":"74bf88dcca4bb7b65a963bd43cef468bf80f03ac69a32837dd1cad79671ed93daed8bc8ab31b8837b6e4cc7e360a61e976e03fbede9fc8f7999f2adbf26f92355258a865879201069c59d047db48c1584f8674b315c9f5035f1c4f3c445aef13660a4e43f06b0e58e5e380321c8b3c52bfbd1d034577d80f30b84726bb9b8a56fa32fe9dff54ce7408648383b85a283a07ea25bb37c189e676e852c6c3a0330762992615ad7d0ebc14d872a8490b8a409e89e63a512c7d55967246281af4040aceac3a42274627f8a913bf7feaa02f655ac0f61bffc0c2751e0f26f4be707a0a0c3f862774f082a4bf621d7926218326f2123033d417d71d4e90763db639246546f4b01f893c784d712fd4c02683cabe35cb9d88fbc78eeb8bf99c5b50877e1da8af75ebe71f129128eac1b9ca8a3f2fe7901b5de4dd60bfd0a21f90b4546704e05f9edef25dbcbf2f6ece2914635acd429da5ef31ecb81483a583ce23dcd47beea4b6d9deda7d605e68d0b6d5d4310dc8d80b513a31973588ac62a7c1ba986460ba225db5f0956c146ea438b0f2b1c24e3f974d4d33a2d8f42cafffc1e9512d5c4cc3cb5242860da78fc47494bc43b5ccb60f919639b7dba320f8ac013f3d1890f063fdf586442d9dd33642dba4c835fa62bf5d1e1ade338e6cc8218e0b6f642a7aa23270aa68e3a4886896e367dfa590b63e6e295c86f1f97547912509a5200ca145c7accf1922b12ad68cd157c9ba0f4ce98c4dd6bcf1bafea5181fc69a309a044d054eda0854a570c55c2d67ddcb6c4dc340c920be9a6e748848031de57f3a077abb988e069ceffadef04dc461be43aab54e94bc65bcebd81f0c9243e343955808c0ab46b3ba2c2750084fc2b19b3200ef08e48ebd34f7b8d371d05c710b91976cc6a885c550f6d90e60c451eb36983cf80748a4bf313eb367404be50604"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e8778f7c73afe7d8fd332f7ef13a493bac8f7a980ccc3ce2bc666308832bcc61","proof":"3eea43f69428c26d9ceda87043fd10f364e15176b9bb94c8e518907b79c558050ed9946c08c4f42171953b854eac47ee2fea19ad528c4a27aff9e81c26bd6344980b7f8e6b2d48e7debed229af1fc4d009fdfa0a353747dd83f9199257e5060462f3534f07b10a9027ecbab48b6e9f16bc29e78b024138d1d87b17f85234816c9f3053879bbe1e6cb9df971eb2bb971ddb72c96f45341f2ddd8b3c185a1b0505188e367c9a105ccc85704d9c460b7b25de17b347ebecf9441bfea0b84e9ae906b0a1573e8888d7ab9a2745a0de0df235e933ab35bdbccdaaf5f18809824f3805e2d540aea253144a8d67991169997d9e5367d6bfd6ca17a32d3bd04106693920084c9ca69fa0dd1b5304d1d7521bb05bfdf269a57873722aae3dde0aed033d687a2f3ac0bd23804f571e8655a3d4ec67b29c3646f8e76545207912ac7be22f4ca2fd53959a74209575d67c2ecb3f95b4718842e5faba6233365c8be73d9cac0c8e08507b61c4f3373c40b55a2f87586c41f95ae25cd7aaf9e442d01d59fbc04b1a208ec08c38fd66a4f393d5982570c67f33af701f32c7a82763ef0f8a8aa479c8cff1c15361105072ae273a2f9af4302c5d1b97b33db45ed1a13eb214ba54407e645a41630a3731e0e4567bbbfc3cf8005f6ba5eb75b9316ee34ffdf1a51e08fae970ce6aeab1e4a7e89cfe91118d107df5cb3da1a1d64ac38c50d89409d05b303fe933ecb2e7d75934c98f4899f12ab74e5d03c9f6455bd549489140730c38408c50ad75240ca9f927c7bd8898249fa10bf08ebfd3793e5908e92dca2d75173ace7e7e9e4788d306ab153ac9e4c0b0e9f9d0aab4235bc0dbb184a8e72aa449bb9ce9844f85ae8da96ec7cd947c37e64be28dcd79555e42a7fb91f53e6ba30b4d3096f7dc318d354b9f952ed667b2975443eb38f026aa6eb1a94ef266f76405"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4c0c7f475053b736cb76a5c57313aab1944e002487e12c0c991a8424748f3b59","proof":"8ce3d86d69cf25a963fb16a07b74eabca21180faf1d8bf1e8ea45b50ca47d14be0d00568dc9d65b26808296d840b471e79a5c9fa1fcc4d047d9d84d490bf553ee6fb1b2a2221a83b023b1ec4d3055721608514de99c61a8cf2b55bf64901b9793a5672160f8bccf751de609438fc4a76bdff23b68830c48f5be6a1edb565351d11759285e09b66bb8321b4aa1cbee6f996ce25a5d2b98743fb751dc862a097021b3af96c97d9030f6cc43128e6bfce2e61491e447f0e42923114639cf6711a096a9b004d12252a1f4041bfd578ea51283505cfc91552ee830b9d83999f20ad0a9c0dc3b3313fe9cd2d9fb91fcc5e69b3918f6ae75285007517a88d103bec0e0a2acc1d794c8ec89ad5ec1927744adf526b21e2dc0c49908396ead740f8f9ab4cc88a396735080327a612b81ccb76f90dc3ef152b6742941dc5b894e488916511e606e28b5bbef4dcb0f65a8f7551c131b9de47f68b03d0eaccc6aed62205f956b038237cac0213eb21f5ed7d4a8313d91c00808c41080ce111a6bc0d4474475bba0955053072c7c0919cd65b20637b22bfe8244546dd5473664fc447f1f9ca4618e87dc321e3eea549b30253569d9de50185600c7dd8c88d9967b9471c53382886d9421de5da21399b162cc2ff834798c995ba615adf63ddae832662580a6630c6696b2a9a175988a6a1c9e8598ace1d58b50e585945494fbd297c93ea2c6371a07ee16dd2422960f2be13de25eddfa33dd3d1ef9498f80ba6e92f4acdae5d56784727628e6f25e6a56366606355dad751a7370549d44a0f4eff1d645e26f8724cfe008e8e469cd0c4196d89ff12860428c1f76a1a7a44c8c6340e4fbe68624506351fa0fcf4fdbeaf88a928adfd160ca0384664b6d5d46d2dda29df302d1403d0dee2647e08321f47aa352fbb5b74cbd864552af0522285f305a0026c0c5001"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8218622b1564c65a4689c6495c206cff552266b7042092c749f28a44d3fe0e66","proof":"4447dde9d8e277d2fb1c9df15fb6a843febef642f8ddcdaddbedfafd3d925b2c3afa7f429e823baff9c0c629b21ca3bffb1e867b1049298887a69a3702933e41e25ea5fadef70a83d8972d68dac08a4300b9579e01a464d2d2c976c7d251340f1e737bacf122f4efbe09b0941d6f41f787c97eb4b97cb017937a0288882bf04349aa4805b58c53172619e24af0c1303cd5c1ae08c7fa8c29e3bcec86cb098c0d742b4d5f1c4d640231821b66931b28a26ee8f1fb20cd19a4ff2ffeddaeb13a0c1493e2021187f07aec4fc5e72d29ecfd8b6107fc567b384c4d153d1e7d45a00fd6fefb2d7d57879c339481ecb8aeb7c599f280e0cf2b797bf63debd9a678931ab4e2daebf601dd3050bab0d834f5ae259e68154f3d2d170c2b362efe9d4cdd3b562fc5860f1d74999523969cfee274388cae31b41ee9346421e36f5ca52a94414e5b5aede4ca71f7d378bb8745eaefb74a4ecadbe428f30de39c84d090960f470e80344db788767fec3b7f3c8f7756b4b35942d13ce7d89407f1a2c8e616d875ee326999adfd8847bc0515265717cba28ef4e8d359384fb4ea35e71f9b2f8b614c453fa534ffec0fd2552a7a2051790746a8bc207745635d23d1e539e7f5a366ceff76470b09d23daabd7ca227f8da1a8ef4f77bad036c80532a985e2397b12a9028b72b138ac935a795c4d787456a9b999c1c6523cf21d8b77edbe79816a3290272bc66e44fcf72c4d8b0ed8cdb2988077e61d081c72dbbfaeb56b7aae8123f36697864a7ea75e3a29f60447d3f4928f0b5675943eb7e54deb92115308fc76ec8a1ca6831c2cc5c5c51e4bc98d8f5adb27f24e14be95c782bda244358da06167ea7c21a7f3bbc78e593b0b89363b165546e38c439ae4078b55602c49cdeb40154d94665ee1a56ebfd5cc99348ac54ceaeee55d29d0356f9ce56fd8c922e8c09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"54249ca8e4f68727592ebcb3922788cf40e67117235b3cc5bc7e5c8c498f0d4e","proof":"e492dae2a86e97f0ab55280bd757e81159adba671232a15c71e61973989043103cbb63f16e8512410f78936b4a509ea08e4be44642f9b0c1350ea3c5951ba06002b991d2ab244997b16c03f93492f203bca14188b45f3fc6106de3fafcd8a45fe606fde91957288f99c42b186e594539cedfd3b8a6beca514a7d50a3d4ebed4f8f62c25d8ceef0cc715603f89c6bf6dfc6af73e21ef2f5a720057bd250c6b90da7c4ad077972ec347355d9a68c5ce3dbb03e54baa5df22c62cd0b1ccf57da505f863dc8e941c8c3692a72ecb5d107b06d8049cf195e301f310dff502bf997f018e3458af2fb01a451275d231e914cea2f8773526760c5bc76b562200ab0dfd385af7261283b95f903b85e4fe818078acb9dffebcfdf6f5bc40ea5b53282dcf42d4f8a17fa38a151c7b85d740936182ef8efec79755d7f180aab0d54e61009c1626501203c76fae1af321f9e45c3093e00a999f4b6297823282d105ef32bea0706835ecf81cab94e36c845217d070b304152fdf45d3c8af124e48d33b52b4c346f873233fd81328fccbbef6137b376c86e1ce62ce2066dff71dee401e3a54842e6c052567645b4ed19a30214d3445b13234e4188f4a9f8d97452b78e894549c0cd62e114d34538a78fe0591342de806e6a6cb7e93b000ee570d0d9b4b576fe5354eba345b5e720c37ca2a38bf123ed7a165696958311523e619cd6a91019b68028ae127d216289ac0062fc346913362cbe2b265429111a76aeb0c75468254a84a601caadac3e5ee8acf9ddb080d4ebe2d47c679d58c22fb50806e559b1794261f4a1a88315f2a5a9f8320ee5ac291a120e69524755eb42dd144d809fd5fc0b62d2372b3980253d7435a6fb803fc0c30898235ed6ab8fe77189207cfd90ebe340efc9bc7ca55d651c3875b321914608abb23d931fd8e3666d9739161315225050b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e06ca197defb413633d9c9706c2e74fb5fd3747ba86ed867f4783a05da70bc1b","proof":"70350d738495c2dfcf3cf6be96aeebfd6340708474deb3e0517992d49c530068c256c82fe1938bb7e992aebb25ef842b1edf14d52e2c199ac45a29064c590b09c2b76e799b5f680dd6aa8571423bf12a5301e73e7436be557ae850ca4eb37d4b0edab11ff74d9416012100c4c279b10e4392fac25cdc9f72ef152770ee3aaf6d8e259a8b84d08ccada5d2b782f44959b291769e66cf5f24d4d6608cd57c5a10d15a5356a2a567092357d48690292b8399e9d63a3dc79d8af5d09015cd239090351f36fcbff98a9d5c4eb45761fcecdc52c9f80702b9e1db5bfcdd2496555fc03d08ffdf9359b62d61e0fe20adce452e59beef78fe4f5f5399abb2086a76bd07c54498639a3d73460d3d1898613052548210ee61085b45430a048748ad01e625260c92145538649a50355bf5ec658faebfad6c5906120996b8960924dfeccb60984db82644730fe029a07b8c821afff2929003bb042c4545a028015ba21e8534a36de806848d9a63ec354006d5dadceca4729a475ed8227a4551d44d645b4610264a7f634cb9ce41c6e081015565747f6742fd1941a2149c981fccd8ae0d85b0c0020605e5ebdf6bdbd362302743c8c8b723d2a6f512cd9a0f7b0476b1f998c3142333b90b166cb9b0db9c6295395d2c2a27b07273039860bbc43cfa5c4686633a82249eb050890ad477f5ab33296803214b452f67f21ca572e6c57d62afe2d6f2620bf3c033ddd10eba26b37f293c5d4ee649eefcfdbc0be906c5fa4f324d416d8cd4370b9b8943fbbfe2c4bb0e6feb15d7e4a90e162d7c3af50bcf40c3ce62f8221916e0546ee063b9c0172a0ab629db26644f821943b6ed6a2b656657ff73f88e6df10b6c5fa83353c06723a855bc43a9026d0b27fffe4e770ca3813920104c0f358427db07101711cebb108e49c1ace87658442f3ce7219282d336beeb702"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8c1727075d7fab106c8526671c189d585dc36cc0cf46371e9cad1307b2b14136","proof":"dcb26d2a3d6ef14bfeeab1cde3eb8b8ad740a37f6c25e1d297178fc8f9063c0f8c133a2cc8770ebfd552ad9c28494987006263a0eabc9f0243f3eed68ed658610ed65229597ce7afc8eda21179e31215fa26f22fb12b4a431acd7c035243ba243c67b6ac45267fbd796c95da688de779becd0a539e6f228a665225a2d792360431051b2afd74e86db0cdb8444f51b1d10688eb4a4ba91c3ec426bf5585f9a70abbffd450af3a63737d3038dd907dc7684d0d09b3e9c3674cee51269626983f05f467f9a674a22e006b152901845fdc2395143a7ff4a267d925043eaa7994750ef6bdc5d02bdd97458611988efa946825dffb75c23adb7f492851b36fce88991d1416ab273fbfa2c268d207d784ea25fbc9f299e39bbfec7c23db138a5f7c7a1a9031c26be76f56c1279738bf4efd0730af225e043f8a41917970d43de999ba283ecec083a1b34254e04460a3f6dd6ebb91de55ae1ba5f5dded23ae4900df284b689e8abae026530228d5627c414465d423f713d58385ed87dce59a0625b69853da95e9a80fcc783a26bf0eb366759f9c55779d0252a61c65a53ec9aa8c93db5baa3dc4a9fcc2d7ce2b4f477eb460f2482ee8d7463e3899aa745bd852d9c7ba4e7048b618388cd73e2a3a17eada5de83ab5ac20c6591f2e11b494379b9da02c51b8df3e7b7857c809916eb76da48f77b92d492c7962ad19df2c7cda61b7dc7852a67a496f2ab7b9b276d96678c39d5cde7ded86357e45eb22f31e390b11aa11221eef9aa8008a333c27e216ce200baa91e367f01ed5e4e08a17cd3d91ab3d831c482f435a54cc86125b8964c1acf024727aeaac6fe279f1929ce58c326c0aff53bbb0d11e980f37faf4499cb63123919fc44593abb049f6f9eb39fc8bb9574e031f9ea645f93fde22216e0dab5b53a17527e79b854022ffaedbf037012b796209"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d6a7590de1df1e23f8ba6bd6a00a67bd21b2dac0e3830bce4999da565c1dfc63","proof":"56bb3a2d5cd92da0f2b4aea6974cdd7052dfc54f3b3a1ef3032704148610e5738c0e7096e3be34e6c7dcafacb0f6ea9f99c2b57768fc074072848a2af8897c18fafc7ab8a7f5d7c73abac592dbfba2dd2b99274182ad98116925f1ea7cbe051aaee33531fb4bd916ec76fbfd93f549d624eea3d2dd465a9ae0cfe2dffb19e03f84ec2acf8b7d9788e406e63c682c0d3c91c1dfd82e0b4f1934a896eea821b50a46aa0ca3089731b2f261f8f708cb2bfebc4b949d61a451d639b2e4d2fe0db007d8be267004644b3037e580e463d535c0ec1d34b53352c0f477ccd0ff38b3b005a866169f1d8638dcbed82223697285a9d8e00ea02eeec302342ee3e652e4721cde9dedf3dd667d2f17cb990f2617a91c14705e508df0b94b006e47afe352b571fa7ba510ead4d98425f50afe260423133680f1555b173201481b35b0b5d0f37126a5669df80e57ea7e8b04d48f85e1b45c7cfe4f4b411489feae7129bd458f6ea8b35572207ef38b1beb57b7f1c2121f87cb7dc175eb9ee2c38ba88a0268c50f0e0ebbe2e625fa0bdbb9d7fdddd4bc3995524c3a4425e7829ef56b3fb2571223facf950d0a03592e2385671bdc5eb0c412cea344df9a69b748ccc1eba734ad5900d8bf83b252575253060567e0b7940d63f27acca9b211de68790342b533941f94da49afc4573b3f32e69b8a7a92638199c7abc7e018ab671db54717e71a6717e01602c6f72a9b0fbb2399241059f490f2d697959588b3780ff43fdfd5a87264a4093778ff350fe2a07643322c9c13f51be22c02865f122d83f34b57c332ba28b2da65cd54151d73ef0dc3cbb17ee7333ff0fc94107aa232ea75fe36b1b22e747d9d7fb12e492582a69a9cc74cc77bf268e28f022a62984fa86cd0e1019a9903ca0c1ef6670fa651a693edca1f23ed33f964b237936d323a952e35b952156507"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a2eeca3f6ee755b3b79fd2511815bf856304865e8faae5f986561c574a27dc50","proof":"fc722feabbcca9f02375da697ee935ebc470e52276e07ca44a6ce843c6cca67810589cce8f39d6df9dd6c11ba3185d8b044f7c548e202e717361c5a4f116865b04129253729279233c45fe756b21c8b37f03d62de349415f73117f40ecd78b540cd9d7e7f340697f9961c2ea268a48ccc846f0ca043651fb82ee500ce49be8357a7c11cef5cbd4ab5aa5680f09c05c1fd466d07d5c5437ef1ab55516bb891c02222866517e1e73f6014f8318501228670bb125a82941d9d427e571a2c1b0320d8bbe72d1d0109d66900bf03065a3b5bc825b9cda70c0594e033894fd10bd8c043e4c496587a0cdb2ed75b955c0bd3a627f712c09275fd10de6ef4b37484c851a6437a9f81c3439d20408c6589ea08d83ecd98747bc0b0b95e24f464d4c48f34e885ceef6c78cf3056bde1f717d4f73c7d60286e8deabeaa733d0dfc1d6ef6b2ea0d3a9b6f1be2596b7b31fc265faf77e6597fdd406397a08a694d8e52f5e6654e629264ad12e5a335538cba05f105af548cb4a9dc38317a06886a4afb350c24da8c84d76759c9d4eae64d0717b650fe0092ae39c1eba48216b7e3ca82c6f5850e85a6c049ac75860d718776eb2fa0522f6714bd6c2abee4d255fec9d70bcd558f8d4027cac5ae64f228df73402bcaebb17f50af244e4342d3f423d1943fc224a8e4915528120b5f417e3c64c7c482fcadaf8e41ba3dd8519eb5833856970c02a78b7c12e81947fd39e7d6cf4a3ddd1761f6502c2a26aaf6276679c772bc5c024cc61b8386fc70719ebc22381f9776628fca1a920b388a10aee9178eebb54fe74e23df3366b73db4068b22936211ea448a4b57510e08edf231a0b7e5ee491a15bfc4a1294d819d2fb30807591c31f5cc66707133aa0719940dbc1de566f00cf02028b57d0deed19430408c7014ea117d8758fcd74dee4c7a50952674202e2d10c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"12ba55ed40bcfd6d7e679d01f9fa053b08f666d908b7ff882a02752448e68368","proof":"e80652861a3007788682f47b0554aa26271fa06d6ba7dd0ea2d2104521a9367c0403f1f42d8259009ef994bc6ea92d571f1557cae2a73c9f0553fe53358f1c09d28f0f7580eae8ff58e10e4666495364ac9de6d36031b30e6d3119a8f106a578e0dc44532f30244b2245d8f543b43a4e31971c2b6fb38c0f330f0a93472116751831a129ccbd6c92853ff663351f46d39a46dc5907808a5b06c96317baaa2203f620fb285f652aca76cfe66a0c8d3c0389b1f58d0e08e03ace1a0d8bc496930a107a135f6340becd6ebb11f222bcbad485cb8a8aa39d2a1cbbc0850dbe38a103dcdbee5fde0699c9dfbe8e6158bf2276dbf690088c18a83f0494c588f97a473fd80736a9dfc7aec4837d7e21235682488b29961c691e3835f0cb85c2aa24164b2ee714c6cb3829f578eeac1decf7be847feaa48bd51bb8bf318377dbf6fac03a70d499d329cc595f7be74c28f126a17d2e1611a21d08376d947e85f4a20d5e6d6051c81c38353a5e4638255d9b8cb62d0dfe18e08bc831e9d0870098b2033b442a1f6349222c176233d36df26a658ab5101b7c800635c9e1f73254f91d99cd7ada85bb9d23eedddcf70d4e217fe26497d7a768b46e180c64656f3140e33df74b0a309111de0414b1fd6c39a8f3f95c2e9cfd72fb9340d9f038e91af5cef25a2f68429d52e3b91ed59a01f27ae3c0f6c1a5f3dd4597848d9a92770cdfd57804548e0010c3dba10084b622bcba8abfb4dbc11ff68c84115711f4f47e07aba3c20820e396721146712e146b6518930b19f49f21878041f26cb2f8165e05eed60b572e97df13987eed71f02955979f3ab5c732218d872c538e775e64e71585c3eb4b78d63f492bf12851c69ca3a0e9f4154cda973d1081f7d650290726c2bd2b58011e960dff70eb260d5fe3a98f7edf8924ddef31c8a56a49109473444a5e19180d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8889a228bc67fc893a085e1c0e6540bc435644d548bd2756bcb93df58ffc4e25","proof":"d087da888f6f5e173ae307fb3eef9cc9caa387e85c5603774fb28054ba4cf05006e028f95267d975dfe4cd7448c095f3c8a45c5b01dc00fcdeda27da0db23a5a2efa2b6f9b48ecc86c80ae1b980b2463b428a3b69d826799762cef0afd9a6a7cee135dccd1ab5647f4739ca4f7363dc95f481793c5ef0c8870fa62c10afdcc7e3fece6ca39a15ea430cf004a4155001ca664557966e1024901a249a5258f72058dac8b0b3869e5bc7917d260e999a61bdecbb9caf58d3666000c04861aad540481a1075a3d15c9e84d0b0ea5f9fa72336d4c142541445f0be7ea6f40ff9b770cecea130005f7986d1c8962522748d82bb980bed0d77403ffa82a800ad60b9f1e688bf319f03d6aa85fa1af573ddc6b576f3b6dc98815b8dd1ec0336ac97824224ca5d241258cd5cac78090eee64277a1657807e4e3edd45016893cefb521e102e0ca888746fcf6daef9791717fcd33ff5b395918913b05aaaabedfaa934db669f00319c26574047de77e3df7e1692b49ca22fef33c4a696395424241bc67f056ee4caa44b9b14c460c5341d2db9c7eead6e87f5eed239d7579bc26d43ac6ea13ac9f771d841cc9fc04d2512dcb863517f389c13f141567a8792769d75050611fba491395fbd313e247663c44801afa739c9b528a07a6783f82d4fbfd34ce5435f824acaffa6cdfbc6c79f8cbf55ff3fc8330878a77104657e89c2cecfa0431564ecc1d5eaa4c68b84f1d7dd3a7633b75f8b5d0dfb020cf007309095c34e43230706ed41d367bce5cbe4bf1fef897ae3b893688dfbc855d2ac6d5f6fc1247b540f0727b071b58438c709dc4bd384a1d3527c241ae86b16ebe65e4df5d50de877e7af1fd89d844b373d19f9126a7615dbd7f987867b8e698ed0c7a943008138e02ed713a93f12e2407c4c8913d915f3dff5ac2704430845e1621256f5589c8da07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"22525113329ec9efb7ef86eb038cdde1d4216c77be75225b5cdc43183eab4a1a","proof":"88d8938ec1519e26bd35c865eb781bb44d96c429281223f0339770f2d4e720359e401c0c39e16be4f49b2c323def2bdf4b72d0d8797037dfd7e52dea21527800a699a2d4af71c713a2edd979c5c2cc0a1fed99982bd52099ca6b125c04759f1fdc15ced5e653921a4efac51c2cfce4d9b733df89609f8caf8976b30d6d64802a128e658891a92901c340bbc22e0b09f47df0757c0f52160cf2dcc983d4220f02917a3482261a415d9d7c4cb14daba0d971bd4829db2e76bef3f55d0dc8ec3304fa225444851f9c6d38d1573497accbd8cbebfd52ed950935499fad772b41b9097471f57297cdb335f4e293aa6525e1b26665e0d004e7883bf4f10db63dbdc00794c7f554311ea834636bd86ca5feb37748317283c34970d93d357a0fae6cef0cb48b15317a78db80be5fc5246eec0beefaef6841216b7a22edc6c8868981fa11e6b83e3543ca3af5cf07f87d69cc82107ca84196ea9d0f40bfd9bf4f2f873e14b876653808146777ae18f07531f47d31822ad4e33ec728dae0f2446c9aed160e086f8079eeba1fc4ef349c4cb6c069697b304e46dc398750bc7b72adf4edaf3c820772c6e765b37eb52f8e49a7dedeed927251b11d1474ac51833d4c7e5f9203bec3a59007f3cc21382ca6ea61ced1f78921a61f2de69fe0d5b63c8ab0880475b434bdc52a13f77b7d038dfa9d305324e0b1690807d068fb73fb7f3d3c17b90986121aba56244b4cc70232a7004a8f9e926440b3ab3ca3360e45f167de80126c9ec368f1f77f4e2bc6306ad296af2999b6853ed1a90116ec861ab926372e277bc63787c5c9fedf18ed325e2f3848b777077b40a7bc131a531935cd6972dc1867f9e57cda63a176cf1f121e93b56fa8166144638a4803bc01c3ab6ccbbef071096684de4d9a06a74a8488c8c3922049b04014111f063e625ce5e39664504fea0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dc99be8e82a2f21a42cbe16cc72c426b44c60b54753122a07f203165b2055a65","proof":"9e52733d4de8e22a315e70b42a1c0e3ccc7153bfa72fd4d52635a36c5c8c567b56be9ad6b97e371ce688e4d56ae0fc51a020fa3fd1c88b7de727a963bb0933585665a8059c3fd3530e08070619fb4ee2e387116882beada862a00ac3a942677c5cc63e4f24fdeb89141650346af342837d9cd7bb84cb3c6251861c2920974263313b817fada53bba81b1a24478b81d7c9dff9de087b6012515b766207d27d705ad4be4e22976e3aa5bf691f8ec1bd64f72980f09f70daa0d800e2773d00d2a0df69107ab290d16a28f89a4404678c5fbc389b79961b93b4f549ead4a1c88180c70cf625626834de8815d6dce4ebbdd51f4b4ccfcf56899dc15f482675f5b6f003acccd7ef522ddf72d8072de4307dc696304448cb5d440ffcfcd4de93356c24a74fc721c3fb4d3ee67c5e3aba5a17be992d851c0b833c90ae78c85422e034c7e9617d65081a6ecfc363c5c7ae5f41d589e34370338a4f494a2d4bc97b5dce20c5c0da88bc9b649658a26d999ac4a45b922a9d5b3f97bf19cf73d90abc3f7804a8a301ff5b1e9f67db11b26fa5eeb6d6ad89284552be47d1127c7b2d3bc92e712fe7b44179e2fc890fe594fcb0bd9a9161ea63c9ea5306741c8f1816edadb293e844153d4952383e7b0f7041eac19b87298c215eb646035bd54f6c73454e8c47f680c30ab4c7b97f678d9fa9dd62dbf6a4f8d6032b233cdd95596793afa31374086d907929b1f3247dd7939b4c20c71f05e81ff0b2303452ae3d9634f48828003804e1c2449503e0baa96dba900c5f732375c495f5704b6c1b5036833919975782223d923fa976f4a097f0d3aa8d205a950cd677e7209ca793b7ab80e4c1c67533ed4ddb86441fa3c8a260fac875e6c7f09886c86d2988222c4877fa84536490dec1e78aa4a8364c25148da38cea991b277e0f82d00bfe5c5147264f4f8a7c602"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d2078f993c8a347fe7fcaf9273afaa578c9b4c6e87f0740fdee43d0c55aea50d","proof":"ac832837e945a6976c57235a86acf506e2c500b96fca3f6936443709da18770482b0d23c25ca592573b2f1e3eedaf33c5470661e6a0944bc4128fb4d4d902c2876d04c35463353b7f74b449399708f7caa07f029fa316f435fa6d293b351ff31e8186df0cc998e7c77a5f103df5497d22cb901b347b31defb011dac2ebebcb1359db415d643210a2f99fa41a7e668895155e26104b05841388ba528c492c1b02ca75904a6ed2abff61bc64888fd62c0b8d4637af303fd4a3e41c4b4988e8d00462987a11882fe7b930fae0ed0c856215dcebe4cb44124de745b725d6eadc50038efebf198747031915daf1a14c2126776836a60ac1f2891b276d348ad5e43b566477f4abae498fe8d2f72e5757b54f5c516c06def95154349ae4c84984dece6fee6fddbb1d21e80a68f9a2826f681798dd78693a6a2c06e0fd978801e7532206e0b370ea41f508c46cc30cb04ea98c823c8ebd2c94739304c86f6889503fe659da96e450c5aa6e593a9845d4e36c675fa8d3af99fa29630927207ada5e389763ca4d699e284a674f3fc857d22539b805a19844ce61f49b6e51993e0402cfcb12b2b7a58412a4c10653dfc3dff97cd8fe9604b41d281f5a456abf621161ca3733b636a6220c7e1fa99b75199442640f7dff4e335ce84071f2c20121f3864d3474247faffd36ee75362e173607ef53b74e37f42d0ad68bdfab3b128b488938536a48c5841d4ef438c990e22a3027019b7436418a5046e7d7c97662653a05758e5364803026f6c4205bad2c1d0f7c7ca1542732cd76ad87e9d97439df2c34b49d21aa74ed026fc5e53c9cbbd5af4646bbbfc8ff445eda996d5ac78c2efefbd2bd2c6db854b76d2f3d57a9c89a9029b0a0b06eddf1e5238d1ba61c3fb362bbe25e0cd99e443270a514db909f8f3ef66884b3e199a3270ec4119fb30a9c1b739bf607"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"10f393c8b9fff36c7f62821dd06bda5bc921d8dd5d222abc0ffac93fd6512140","proof":"768c22aa0df67a99d6c7eb87700fc1eb0c962db4062e25da03d5f9964ed75645de06a3692b4be2b05ea898a1acf2386e549268178080803ba243de012b5f9b20bef37422b9210b455ea3af536c7a0bd03738ff62812a4dd1e9212c2874dffe041813ef9a4987c0622149e307958ae8a538f7f84bbfd9c0922a6d7d251f45ab30827a81476a538ac634549eff52dc98a15ac858a1deb75ab18bdd7d01aa5c530a4da4d9f9d90001dc47e6b56a444a37ccae4661d2ce6da7a2e1e2ceb919b19d0ca7711cf50d33bc9e9e0f7e46d49419b73e94ecf1346458da4b7bb6860dd47404669c228cb390ad57d6b9022f66b9cd13244995ceb9c0eb12793c310b2bb68c765abbc9460b752581bc08797a1a4b804eca10a3fea47cf1d8cf3f71c43255d07ed024d32b2d959a1f1151cbd76fe01c4487b1e67355f7de9728c3cda9cf16803ad41a284b32d2abd3e66c4044b50c502f917467830b2b980281c84a64202272749431471a258fae42ed9ef4c5453fb39456b40d5655bbe0c57f12ca882eb58a54ce88ff5185193799cce96371e3364d1f343a99f3f48f0979627158bfa4f0bb087831ab1e58a867c2f3d5ea81f5e8c1c95ec81c212820aee00f69af3625e15522f4ecb2290ccd1f981a2fff55a8e07eafb075267130826fe8b1162febee91f516a0d698b32daf7b34dfa3b60ff297cb9714cd4e55de4e4e0d9039c8eee1d5db3702d45ad1eb99af6136121320a3cf0c7ffbc97ec9c86da99f562b350f1746fa1fca80343bbe5ed646cb1aff7e25eec9431edb8dca04e1ea5318d759f50e54647046b37dd30966db481419261da25f05001c062f5fa95f7b216eab322b6fb15051c264b502d0cc1682df554bd4a3094471fb8a3379d1bd978654ae7540c4d1e800edcf057bca5a3efcf511aa42d39527621f2645ac9a6df747567a3c3bce90870e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a6afd4e1d0ed5278b5eff863722cda965f72d759b404379d284edbe5783f6b04","proof":"38a0ce58f3eed84cc45a34dda11c750cd040224ce890e20f8a3478b8370a212020080423447bb5497321dcb8cf7800db4830433471adeffda1818b06779a5146180d5d8dd222f663ba0949258a0a68ca82a674c5470ca0a0acebc5b302efb237aa390d2f46651e3b54e20dbfb6ef0bce5d985fedaaa71a3c4cb6b48049649c01611eee014ea27719d637d288669e5a1bcf883b06decd6acff8a108c408ff9e01c2fcdb084472dea1282d2f1921736e16c382572d5283e1184276eb6706b6590c22fe264bc435fb1cee0a4f819e1d8418525f1dc32d1070a280b7bd403654b90594e841fd822c0c4da43f2082a64a03cc883b5f7a1350be3272e3b5a92119755ab6fa0cec5680741848cfc3a6061b9b469cf5534c6dcda9fe831dcdbee8c2f56d5c63fcc23691571ec24139e488fa98215c00f38e1013e980bd1aa39d20a86b4e84d138225289b615a87ffe4b47ac09cb012e6624f1ee2488eeee64ae6f49d76376d46eb2f29e73f2a90e8903a0f4a0162a419bb1bcbf558791035b11f49d3b63e8eae62f069b790172051716478a2ceaa2f1b748cd265354072ba276a623e80e721b5fd3575232e9562f6cad196aa3861fc18e9eae299d781ce243d5e7a8466abaa9e3ea100e66fe9243496093a03183b3a3b9d903322689d2e5c0b4b0e8710a9c9f38c0cb80b19116171fd0560662103c2c23908671f5da559851ad74ca3c7872d28d8f1372b240508fa262d6683bae57e3c313f305940f626455f84ee9741b249952102401149255eea1e59a69d49c7de0c85e2ae18494b24fb0a9f433ed3458d6e6928bf9849845f1ca385afd80259a47568249043f2befd01cda2b82af309e5ccec43c4ecdce53ded424e2ddfad52af2181361a8c86b088be428f4933405f5633cd3f90e09d4628825fb418bc128c71413f170719f96f5eedf16d55d3f01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"70f388ff6e0c9b38322226f05e78f421418a567cc1127ef2f9bb0e9f1191644c","proof":"d63bafbcf81900dc037fd8360e44e17bfbf074565fcd483d4499182453a6ed721c411027b1cbcf503dc14856ee5caecfc7c6296f35cd41f83974540bc2c53915dc430c68654e57483090db63732653170961a303a2e56eb1e754fe5eef01ff5b00226a8eb1e4275efcf05afe8abc8580bc8d69d79ccbda7ebd6d76292a2659506198c43ca9f14396d1fd3e1794a28e69a32fb10b74e977b930337419513948006fb03effa252afac1c14f8156a008913ed8316ee38d66b9e23835e371277430d9296d82f14138ec0a396e7d2ddf9802d2f8d8e5231e6c2711c8c8b7263978701baab651b213db96b79457cd64720352fb44283441f7f0890613b2ba348b4881dba8ec45db164216af37a046bb52f31f56c79e369e948730887901fbc6955597de89d639d403e4cf230e70051205972a0406ab27f2a4dc2a130850aaf221c8915b4673d0e278259f1c7a1eb496dc6a4c1470dfb74440ee1f2dc42543e1fcfc2602ee62a8744df5ef31dee4f7229361338c8fc005f2eaf7f56d3a6de51e1c633131e9b7ebc856c53874e58cdbfb95cfaa39299d885b14ca1aaeb1dc8f1837d12711a37d8057c627c3ac9b94aceb3958f7d76ff6bf0dc0fc2435f61eb64f551f355d28fb152dd3a4fdbd34daca2cae9f37352462f28a22a4e797ec1a9cf58f781672827e24c242e5d3b289475e97beb261b9a093d9fbcf143f1e9cc3f6b1c5a2d7d1eaf728fb38fdbf4ace87cd2392a177d6ae5cc655cb1e2f5adcae638571f722ba21bd9c7090100a7d6d27e460ae801ca4c3d3e61447fe724bccd17a3fd5b50218218c07ab0a6d1b7911d758db29fdf840266cbb6f0d1238b370950bdd127390345e8edd669d9012c4cc8adaf34fce14c74013ce3a5366d976eb6eef506b0670c3dc48f4f490e378ad4f51166e324f81d27df692b1a5961a992ff6b30ea7dc809"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"543df76ede549a1065f57ae3e48884907cc08d25d9b6a2046b19d5f98ce77101","proof":"aa3a34084dd94f38db601dcbe5e08d6dc6cba43e3c44eb9b06a15a3132411d46d603d615612de1bd209a54f00566a0f1a69fa0af8d43a0bd89e9b8bbf9984c3fb4e3abf566ab07b18b3238bc874f125f599654df6e2aac265cbe39062e7b6619e20a1ed85b908c43fa545763d148a7c29f9e82cafa24c6232c0a3c3c1840b225417b23090abf8a07ea0acf7bffcee0cd05f2405c6845bd92cb31227ec9f3ef0ee9e343d643d3c27fbcf22f32cd6c79e55cb65c8e81617bfd34d6b3ccd671130197092dd3daad262bd2a4d7e3241a896598c406a010587e22a2a00f247def32031a07232d8c7709fe96cd625024eb0d197860d0d99be7ed0e52ae07f9199dd71c56ee4bdab19ff10e3e6b79a5747be5661061bb2d2a9ba60e99e3a617f93bfd4144284734185b6e64a25952d3b8cfc71cb18a9464178d35734793db542e6aa94898657ccfa65f595c4686e5ba7756e10e76ecc9aa97defef1a5fbc7f06eb57e28de4c969b5e660dec1f0667b70091aa3f7b620459e814209f6a0f20390a3cde22a6fc4dcc92de956aacd218afbcc4f2e500aec57dfd52d941b3f6371046f480608e4dd6cc52cae5c0d7819fcb36aea03777f463fbdb1cef8842e2617f74835f47f279611fd8c3075e8515069f0a913734c3651d896e51acbb30ddcfee62d7941c32071062678c38d9af37182740db4c941c5f4d18520ae02ad9120b67329a8c7062fecd476c34c7c6251d527104c91d2beb47b58b3d2c7e2920483a1ff0ea450362255c58c2ec7472b974ff2550b29fd955993b4757a00133b42e0b5f9b99702e863dcdff58a1bb71f7406994ce72b931540f57d7cd260db462f6ca6b2e7ba12c064ccd730cfac3246140a9c7b32910051a369033808e527e0d0e6bad86cdaf06f5762f5a2a2f78e790279a0a0a2d14938a646d97f7d56de252e40ddbf89a2801"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"46dbb085fcd576e24b6a0e756180d88a11c684f6f7070cdefe8b67ec52d49a57","proof":"0a01d304e12277ec25ae6e9481ceaed0fff7e033f8929a0e0ef29190f70bb40b12238c5857fb46dd74b0608ddf7fe97f48d434ff62c53812aed83df9a177fa2d44c4ee38f173930fde39cd465ce2714f9e870d0d1ef2bb73948ecc44ffca006cd0e0dae95d519800542f912c5f670f8b4283ad9f67c450afbf756546b658287d5629a5d29c3318c29047332c25b1a9a9e77b972ed46a00750ac4d14f7eb68101fdd0ecd4f65487adb9cfe684eab34c710fa2f0cb2cd6ce6271002e4a857aa206b783357c0e741bc32d329d47a198eec47360742ad0976274ffb4f7d88c25f5009260612f1c714cefab0337869b22a55dc967269d7765cac5726a580469487b768ad01a537b12e54647992d9291dee21ca097a95910a2f8c2892f6f95b2126b480e957d772916d2102a27387cc62a26362f66cd34c09befc4d031e7dd16173e68da9d5228bc5de6dd1c02aa188c4d55d00b8f96ee965014264e44e3eaa5fc3949d49c9d6ebd91cac3a5ceb7271c5af177eaa5e915cb366e7e75fec62a5fe3bc103429e42107b5b4913fecd1f2ec6cf700e46b1123df1614a020547463d3b5b45c00ebb5cbbc1468a53738ae75c581d95cfada3b2d7b2750d515dc4153bf1e363c64fa85469f786879f504eff30abfb0d2e6131d6151510fc158f5a173a167e102901737d8080c955fec796d5476b4c6e0ee3942e0f01472be48024693592268386e51b139123d5f11bda91eeca1c09bafaeac83aa4307d6bb106377731bcfce11c201e2818e8e4e2948f657f1558fa9e30d427d7b7db86b856c098b55ec36917850473cb82617199bb027da40c6f6091bf5b04c3d0bb4e23dc90898bf0442db58ffa4cced37565cf932f7de56a17ae8dc98ca96e0809ad03409b8c6503e61d600cce4adbe7ada276ac8c208934c28f527320d226f3a083c9762e16084ba7a7200"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"52befe87e15226ec0cdd29e25f218784203adebbff2198cb3ae3556cf108824a","proof":"68aa5bf802996d819bd7f22415c51de54fc645bf9ace3532cd5f62ebf4ebd74206eff3ea09d389674434ab776ebd84a1c12dc9efa783ad2a7b077090b9cc06771658f9bbe6937166744519a5aa37ed30a1ee8356d541f55531797e1bd8eb231aa0e1a983a2b0b6a0482854a1dd4fa421ed2b424deafb6cc7ae94bf4f8946fa634f9b42d08c67c9626747bcd99a3d858686fbfcb2d9cf60140b4882ae5ad7c00c84453cf13b5a58be4bf6f757453939121d588c62e7ab86a84cf17d232facf60f8fc0564a489ed407ef46552b128a6447ada03b23ff8578fb1124fd29f9cbe103b41ab95e8583df640ea24f0b91009e85d55d1819c86b8a6009eeb392bb757248f2f1c00aca801c4ae6e4f5cfd53902ed56bc44a0e48bee07134e3607eb82bf7306005eb7122a4391e2d54ac30614ca8ac96f85c04477c888e3daea09d3522728e829d73c82954a655a0ff1a89a19dc10a59e457c25b78a8b295c2c543387865c08c29d40ff248397b9ac4262d8a30a7f16fcf4bb2483ca663834410db89634247cede390b664735ec0acd8608159af08be3c32d79818114acbc3b0b7dd539045302f5ab02a532b46f8ad23adc0dd597b36f6c14e0540f8e41a334d6f84f7d41bfcd9521a4b0fdc730c82342d4852021a25ab33ee2a053eba5679629010aae53430f9d5b479d4c449a887f61dffb66fc7f81f7f5533cc1fccb54ae9bac3b21255a2ef3e6b70ca7db535e25e02f4a8f874d05811b482a712f29b06d800ed18b675caeea1293cf476d21dc94af9dc488b94a7da5fa6c28574689bb5fba7d078b472b452ec441ca04092399d43aea292f994ea2e85ddf4d3fbfae9391d570141fa168e78e119ef5911953bdcd35676a94e73852a3ea234b66f9f4410ceacf228530a7f062c01d9e5f02803a590f32a4743b4c028acc06ebe92b5ca16562e180fb807"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5c607253f4b04f7f8f11a4c263e6f90bd140a4afcd3e8fb1dcdb593f39f75179","proof":"0628b8b116406ecf5443c30c4a801f6a630f3703bf5b64c33e752975de743a656a9ee5825982d3a33b1a3fa5d5c24cd916725172e980caa6f3c07b058f86c17e6096f353df910e9a1ac2d93f088ce218a56416f745fb4bc487126ded7341a174c83f8644157a00111960b676ed55c96b5571d6a17d472f782bb62c819a65f5033c1590c2aef32bf514729b8aa47c01818a0cb3e87789058a463875d272e2fd04a3c7b0b219f1dc1200d988911e32d63b5125a304857d01e85de9f541e40b64099a0572babe2ad4420b8a83be67e3e594ce24c26cc30cf5c95a5820d699271408a63e90d74fe88da2212e087a10fcaeca8f7b1eb746220a8b613060639d9a69294ee81a6edcb6c7d5e3fd6d35630ed5bfe060e61075459ad8b9e8ba24b755006c46b23682213ff8f05847630ed05cad6dbc73225feea6b151780eb229e561a06392fba499e4a26874b8b6e78ae82cd40f3efed4473b23924972abed8888ff760400aca4353dd354a4e845c90e3a570fb4f6fa34c5d357a81c46428c4c4b87bb51ac7d20b49b8ccbebab7b936b7569b30caedf367dfb8da0dd2c985858536d5312e8e1c23a539222a38ced459dc7f944f4e4f3e0157f256e6b4917411cbdb35b1506b3582d8f5d8e582ccda54bc57d9efc85678b07d4fbb6f3e39239b192e6ce0076b1adc2b2baf16e5f397115b39c8f811bd49ffb91cda5444e6d24bfb8794939569aa1af667b88cca3d4a4217d37b47aa4b6a27d0587ebae61e9b180af247c1830261e0779810513eccf3705444944c19b616d9dbc16017c3c131fbb024d7325080d683abf7b7159110a96d8a040d39db61d36e338361902b1b2d8f02943657ce8a34776c992e9a8e1bdd7a31f99ff74d98230342b6ff635969a95d5576e93038a7d33b0d652a0299744aeee4b953eb6e5f1ec7755c9d8f9a8ca0488169b450a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ee8b07517eaf7943b2aff787a89cfd81116c64b4c21fb438a7243df35edd826b","proof":"12aee04a22031a59d545a2d76b3d1125ec57849d65712805b92761b3ef66e53964932f3ae4179035e69c18652974536c915279109ff58d721d072268c6d06f6396fd59bf1fcf67cf68ee604727ef6af68ead93621bd00c406c44895ef87e674344071ee1ea79ed85e32c00311cbab35bffd6674e008b6e21c467893c6d58044467e6d8583f9345bbfde487f59c6300da91f6bdfeb38824f02a83c7c988d491015177e5fe48ba286f03344cdd3c71069597f52c2ff1e7e092e6b02d547423f60dc094dd1bb7cde25e14f8fe459d839af6f5119284e158e863b9ce80ec90413a0cfa9989ac75cbb13705ffd7c1fab153abe7904a6a4f6548f450ffa9d9cf5a1c7c16e429593ac1e70ba348b615b30b523a49bdad82bb710d92ac9bb4a06fc96a1a747b297b575eb1789cc1e0b2997f05a50e1a8e46aa54ae3a77474e20aba196169217f15ce3550e154b87099e757ce538250103fb0c4bc5e387994273487786533473f4e9663185e48bc5dd5c27b71e728462650c4c08423e15069284af70ad382cde9767051d8aae9d6148c5b85f8ab56f42fbcdfa7b960d59967c6fbf87710f10ee3e35603b5de8eb9c05c51c54c4583dce06f7ee9410c9dfa2f4cf427aa407142269d11ec2af661d5fd7510e0efd17e5e39080b1f142fc3006b03b6ee0e62572619bf24b1c5534ca06fa4e548194fe776b1eec7e178fe9b38367e6273be722ea952257d66fade2aa797fb6034c6a313b83a074560709b32b36be8724223e48b8d656b412c970cb3d4c9c29fb50f6aa1f06e3857a350bfff0774d643889d03bf8bd72e8755b13587dd97735fbf3d816b695c9fbfb4a76cb1afc06857b9fa42b8a30a07fcd5f8e8a002d0fd7aceef9d61839398040ab5775610aea781693690285f9ce9db5179339a78d4ea75aca80a25f51a63041ab4b956206480d3b0fbd0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"84f24e064c325b8e35064d89248458bd2f51bb86321d0e87c127429dd23dfd7f","proof":"96dff4d82401cd1ba2988ae1686ed4027d2be82ac50f7a1825620bff6ac8193d841f4b31da365ae24c6f440f9e0af6093d11bcf18221c95546180327ea4564750058a306ca203c7baae89fddabb5793b2cb22d6bd69be96f7430c6bee7097b6c008d12f786acce88881b749f8459825039b79e9bdabfbe47823c32798ef523105baaa7e11aff5d73d6746ee2e7d0a733e3f253697b9f860d53171a4641a2f201bdb5d660f70b3914f816c5e52328c02aa1a94c5440d14d2d82a5a76d1bf79700ffe02366cd795f2260c887cda9ce4d05d1b03bd52b06c189a6822f8a5943740402b8832b68ed5bf0260c82fe035dc4b128012624afefdc0f27930d1ef929031e4c689ea81a28040e8d52c93151f687a4123d9e12dd704be9ce0f91b475da2c3c648ac2e35872fee31893d96f3fc7ce4e66d3588580742876990ddc0ee5134a7d403245f6d756006a03005971f9ea5bef857e04ab358b4a73d137cfe70e68b32a5ea3daf71cc7ce0d7d3307c63bad4ba80a9bffa5c35824d2bf9b4ff65fae0e525cfc6caeecb32cc9e72eb8cbd587ad25f65e18f0f5254c04a4f389468cd9262e2cbc32aa2720bf1c6319a387367460524b1150f655c9191702ee9222f464b55ec23f36623b0ce428e29c7956e38e483b0e1c177dc156a27b4efa7acb577b6335965ed08fae68458b6e712355b44d387107fad7135859bb52b9aa7728cc1c9c2f1c55fb8ad8792997a8cfae6b89fc3f69244121e2e27df8ab97aa72ad28f05b233c30f434ce9439bd5e0453f483339883fc09c8f1eb7b3a65d82c4e8dce4b9c6448d72069786fb53bff34a05ef2c6b0e1f396fae9e284e1bf5cdb26ae002768714b6b2d35a4ab580babbacdaa52703afe83645d39f95417267b6028e9bb656404f94beafdd4794d488031060bfdc9e705c370cb6d77e4f9b02e44c82955951e07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4894f66694659e08337fa5ebb3295e40707e758fba6188f403aba84ba8f73224","proof":"46efd7550f502a64022c4f418aa821b09265f7843f123af419cecab2b89b044e1e9e9ed2f8c4f3172ab7c9c839c975e74a4cb6878b45b27691a23c8ce9419f06d601f2a677f32e2e176fa282c0efcebde6c507fe4de26ffd7b2c71902f9db54794d6979b46fcc15d70f787b09b9f349709f6388d91136793a8a205793d9ce636b380d04c62c9e82c988b901a96c6f308098a195371371d551c2bb05528b3330ff0d53b7b6f7af605b903925d6256bbe34372e2c067b281a7e30cab762a937d097e76ea8ef7927a38360b01b2c8fd0a44dc9369e4a583c17b74b8c4c89d15e2067e3805fd04165c3163ef623ec5de577e8def52323250f77497c91fbd6552c544aa47bfbe29f947213ce17e9d732b0217899e73f83c311ad90eea5fef1a0a48418259d965c241a796c4162f336957605f51b22d659b621099327e5937c5f94b7f7a35536dd53d21f62f32b118603e1ed76e76dfeb3e9be6745b8b4b58cec396767e78841e7cb2d4f6b61fda0be33d178ac8e338a66a658b825b5ef90c726c9d15ea4f823dcddbf2a422b6141f889b70b0fd2c2270cc29d59e6df232c822215a443a54d243e70de4881c6ee5cf48ac657d0f23a0cc82b48d54f6a17d32f0ee7173b8e3a11031e9a19b298588366ff7c7f1d246b580e5b2e82579a3c2e27f81683ef82d8d7eacf73726567de79f135987b7d34b0323650fc3e16092533a2d05496dfcf462d1ca80017f95ea4c4f2dc4848c7843fc6f68747fd540a8e61607b2491fb606828b0c593df34e509f04c8c522318f2d9257e49763b3bf118b45904876559400eeb83458cf7bb9023f7ab5a9c0a4e2470df69d515675b14f82f080c23c19b4c3f88aab4a1d88d4a7ddea59ae6cccc1fee75037adaffa03056b6d3cc51a0ade3665cbd8cbfb2789c8117a320b919293625bf7e746f91fc2956b65d83c8706"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3e2ef4a8f64b1c521b5a073bd74203500c218b52480ce31748d848dae6e0300f","proof":"7247b50965666b987b241da85d125395f6b723181c7b3836b2bb7beb55a90f4d2ca755b5e668cae815a7a9a7e7dacfe90965be1abc69bb646acf5405d4801b02fa3ba27f987738c30b03a5cca2f04d71e2aa8bc29254c6c9fbeaf98726621a75f29891378bdcf4433902eaae6970b3183027d9d3167ab84a365c31dba2668f06f3cf13ebd8f0a2ab6ca4f27f695f969d0cedb67f3874ba97a8dd39e3920762028229218f2164710318f6c4d84a1f7297adc9f81cfed9c58fddab6b9d17efe50ddf17d6b6497a2e99246b98e0dfed7d6119881cfc84555688c972987e1e25a20e8cc548e8c6f274eb56dd5979c274f3d8776fb9f8b2fd31d650f0b85dc31b4f78985516527f5e34f7b821d8b6b571afd1bbfb1bad4ade41c371e2c6eae68f9b2db42ce309088452f1deaff8417ec502ad74e4b7fcaf171be598992419ad493b5350b6b1d018ce30003b59eb6b028622e55478d1da0053df5597a87fa59450767bd0b372bb72bfcdfaddce1e6be61ed05ea578d619ec5a55b96ee4ef09bbefc73da03607508c48d01c9c793c974873ae84c150f765f2132b539313196edda5de789cc5d7d3fe651f7d9b6b8dca4f7f07aebaf702b8cc6f1fb3dfc0a7b4d9681f31743d9c58ccf5882cc4f97c591acba7dc4812d646cee55899550f35fb0713e84f54ebceddc607b823c6656234add1d116e145f1614c06a5e3fe10e44ce103aa43ae6c188478499c2674fb5745b4def3b539ef6f0f23638f1005894552fa46af20ba6b3d8a9777b51c2ffaa550fe6ef406937954677a853240e42407adaaa18e4b7293b6dc8f987ae56d91e4222772110b19bed96b5f5e5323d7876e6bb738737a6b5972c70b32f67436912422fdef0ab1fc4126c256da3a0d1c4a57d34e3a62028e9e5262cf80e2f0123a18f2d7399bfadba511c65158e8122ba612bdcdcc260d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"de83515c6530fd8fabcf294282689d47229c3b53f151ada2c20d36c2ffa93e44","proof":"b638611fdfc78f949b4ecc88004384126ffb4a52bcf9ade9ec82e4a0f653a00ab4ca0d2232348182ce277309b74f2bb44763e819fbef86a095525f3e687f182fca610fc533cbb3a10b918e894db5070b4f74e47b1505e5c09f53f10a31ab131b82ceb000d5939b1d1464f2a2126d852f506b4fc6aa2a8a074218146cd468196ee43d4bbf9d34dfa8cc486a42d665c159f2e9c8a04094001f4d127c8104c4e20133bae0eeda38f85fbf40cac84713ce44c104ab529a7cd1f8e0868603f48b560d50cf36a0724afefc612b04451b56846820b36e9f1d8a5eea71dc581c656f2d0b1c36481b8241d7f42a8c957beb229a3d32dd7d575d34d72a59195e64baf59c57842b5faca475b3ccdbf0dbce0dca5140286bdf0f9dfea2265a0311dd62c7194cc68ebe1ea84898fa202b3665cb536e7e5536112e103770f03f0b8d37e5004e3da86750f8ebce53f177fa8fa82feffc543fa9cf99ab771dc4875ed17f54610962de176fa7dc377e5d1827abe81692076c2acf34761d44e50cb2da9b02abb6225bae9e880238c5da595c4cf9ee44afa6fa6e72031d8c8827dc963be5e94b3ba3366a46e383899b1e482a31de9470284bc401fed9d213b357092ab84d78a44ea7560acf360645c43db8968e47aa122b90a88fac816c18e19e04d1a1351c356b5a17fe46f19629273d366442a41718530359b35debf436763c1b8ac3e2ca4325fc4f7284f4d6d4aa09ca445394ee0b2332a351d12fc9c139b39d7f015e55267ffb472668052cb2b7649a96614dca3b4a3e9c220d4710b28f90c1d5bcc01b1b21040f5c2e23d4ebf1096c4d5ca2710daa8615366ee75bcff4516c9c2e4c3b8c6ca92478fee4ab846206cc15fd92062711352f76bb226286430c44de200eaef3b5f70e2e00b63e5d3e60f57d4ee65b203cd5694015827f51d5e8ca9114efc869921b08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f20ebed8c5f3b45e3e79cb4f6de539fc0bfade272ac223fe1cb5028ee50f8b55","proof":"601f05632b001786069ee60226c12205646b69de04f3b1960cc594122325d109f84635406686a38eb606b2a147f5c48c42835199a06c3ba2db1413e5cfd15e74be7d90fd94f9903a269f30587663a5eb3b033717329a8d17bd994c34c139a05058a333b131313bac5d868b9dc662ec24f786a7573a3d1f3ed956f651d3b4196c64a9fad64d15c8774b83ceb418fd7f20dd8c9a5c5de0ea233a62ff2cbbb12a01e4c5b0ddb2a783d8db3f5326bad221bfd911fbdf1760a749b1de068f303b1905af2c067f5eb9a8b92d7109adb775cd9fd183854199c2f70fcdbe2d60a73210028ce02425a471b84c264d87b4ed69c26941691d0908a9ef28bcdc469d6798237e70dff4a57613b5834a7ad0efcb39bb569f59808b4934a73bf97bcd5de863b82b2a2654295ba5caf35d0b5e9a25da7867f23d6727017ad08af017e749e26ce46c1e611bbe905a4e9c48c64197a654af374caf1b26bf784cdf619dc73be3cb9932467f73c1623072176960f67067ccf6737ce0189b9d61b0bf756dafa8126ee659ca37f8126e45fd2dde1288a013e4691559ba60c5a771ec4906dafd1aa4e8a57016dce91a1d10d1d19a5e50faebb0f9495f1ffba2d2b7b3ff5ec2d9be2c0b075a2621d34c4908791b321bb2d1727ba7dcb03e098429b6890670da533133f49749cc031e390c6f2485fe60bd33eec6a0b3d5eab6dc0257a7e3a77594dd553aae67d4f52a8e55526ec0f307896b4e595a2ee4fb456a41fdeda23a9ed5a8b0fe6e5f9a27cf9707291cb51f8e3fd497d42bccd2b99907e4f2b412fe9bac1b88ecb22f623613643e32f862e9554d2b315104b0d25899f4ea6e9bab5d3b17c43d1ed31468abc18ab8b602529905caab5e4db01b17c511c190dc80f690bd33921272c7045912f561424a75de410d7723786dff0df11010969c3fe313fea9b381e4964105"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"96af169a3e6822b57d05ef477afb33b13fff9a899e2619aa02e985528d6d9f39","proof":"bc905bddb7a60dd955221511acbf8fce7e2a54ad3c001e8b35b9e93cbf81a92000cd40830284a4d6acc3186d270b292bdc66d0fbaba6b7eaa843fc60ab4618583ec55282dbe97ffb5f0269df84909c659238d4b1528db3add9953b974e3de01b941cf590ad0b3aa651ea08f1f5fb8005f5ac1a2943d768bfdd1667724269492682126e36a0325825a1cd3ed42c21191dd4a24dc7094cc339aef7ff758569850750e762581eb6b0d4486232b1d36d8d288bfa94cf60f845ffd4f58a4a6507580f00f6f43d4cd5852ddf1217b3caba6ae3ca20852445bd3c47c865daf3b20e1302848138b7d9af80f3e5a43eab3ccc8a304753f44b493c83e69609446725b9601b9eaede6c07a099e187eff070594f8b213b0417a333bd165836e4d06bdb3c8b564046e7bbd32aa494d3fe894c51f368eab3d97dea1241a500f8fe9f16f70afc783ad8c04f60b420f37d4fc5502ff8a8cc9814b4974e301424e83dd3ca61da2167f48ffecdfa80cc19f1f90aef6f099a68d101279be480cde237d939a35436552840556cc57d2b56f7fd4887f79462b0b8baf91b1fe561f40e092705ff065e270c6a31090f7c9078450ef8dbb44b26d9fe7a958a9903a696219ef83c748ba3f776b22a73bfccdca8e32ffa9cb59f04ccf70901003443faf0567c18cb51433ba16f7e15663b1d4ed63114d2f288c862f8cd1ca2901781d28f1be86b207824e55a28faa16e6dcc47b64de9f3023a497a7218e2856a807bae8f0964d057ccfa000779c4082ad165432d9540a8ec0046fff4cf4763f1356c0fc517dda957e8e3732b68503dbb3dc6e03199b46fc00c683e4d54e156013f2c34d4a32999c369148d811959d93b4d25bc29257e502d22c149a76dea406125657854678c6a9155b4ff77096182e293ba3c8edfa630a9a5a425d6ca2932ee5dc96581bd6246577205ebec03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a42074a372f58aac440be127407f1c2ccf46354a8435e7c60b521fa6404be14e","proof":"ca1ce0c3eb7ba4026fbf94b03e07db8d6bb2aa7b668c8709d793bec43881d95358f71dc3e0d303269420539dea262a016b7bd7e04240dc5c2c9637cf2ff496268ae9bf3de32d78d94114baad2c4e749d5c25b3001c3ddb57ca38f131387b705c8244ca6c2a68a0b4c1fef93843e104c78e344b116f1478dfdeaa1b07fa77c0118aea07447b1cbd41ce9da894cd27670e8307779834e9b76ef4bf1116e4e1be034743f2f7cc3adc4151ed04daaed763db4725aeb685a5367f3550567192d01a0686f79da0140aac5311cef2cfdd7950b083698a6254dce43e4f945f915e540b0f08643e4d520ae3741937b037b3fbd9a749ec2cdc37dc8a92f638241f148236431091b7e40c7638118fab8313e0cb2c44b6d0a1db0475c5ecfc561df07a61110e80cb5fde33cfecff1c79f6d54df1278b9182f3937ede5dc9ce5d5fc2ede3a2699c185e7dc58c6a5e5be0d22e0209b1938366181aac80167b0a30f2e699955b189214b4d9587b2bebb64a266c8d1259b0e0e556dcaf5c0976007cbc4de56f9d17d00a7a50945ec9e21d690bcd2f33193f26c4f8eb1b9e6e824dcdf73f74a13d7cd6713482587eb4770273fbf683591e036300a8c72af583881edfc6bd4e18994a84e7f90c86f1c2f6c959d15537cb6ac89e064391f485e1cd2657dee68153874f7ceeecac7e9fe6a4b76338f8d8b0097aad3b994d51fe6d99aa5cb7f6cb020276984505ffdd576bc8873952ce92077adb2cef40debd26fa9e7d572f110deffe574260683a7ec700f4f3b390a02ee0430e855a6a278c243aefbb88bec37731707cda61ea80cabad8fef957c35375b470176c6c5ae947cf71c68c2ff62f50bb7a294bbd1dfd50e29b0d61b6cd0422889647cf0cf5f68895457ed3126723a859d70650bf6e30f624a97ccfeda59df0bbe9a8fb97dd6ef9e381d27bee22275a1f8404"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a6b1b9cfa89b3dac572c6c8c89f71d90969bcb112e2b975a42341652a2a31970","proof":"34781965fda7df30898cb2d0bb56455622fa6c565d8bdc9a600c6e7638e5ac3eb28a39c63594553b7357fb4e99ac04896a84e6ec717bbc293ea10b68a0640a18feabdb1b8ab93e84c7d2eb1b81fb3027352d7130948b5c80222f4f33ac2bb86bc0054b9d7d03d57cd3d1c34ab8413d89f148197aab77bc1cecb0725b6a30483f732eda9c777fed29afd14e52c3891e655d218ba7eda065edff3e2a3e8d8a1a02b6a0b50696951b0e09ae3fcbc090275502ee5fd525b2ccac3a7ca4c1ca3fb30a4f9f1d4cf307f8e76f7c38f3d28ad3f8c7bc501f10771b8dc01dd70068049700d24bf54d80300d211e6201463fc71b6fdb0744d8221b7a709ddf6f33fdd1ca22f29d91799649e9acbf13935dc7d8c9ace3368317caa536fb08dc5db8e7923630bc00b75bd3c449321d6adc7417c1d6848f72012b569e90a778fe6d94a56eff2b367ce38f2874362dbb51451c4326bb36f1614fd8ecc939a4df09089ba68c34148cbdb09a509f8f8e16ea82b61cff0604007cdf81d0a0bfa487df4c98125db931de9cff6fb10800e6b03bf24f62731e33d6770aad60a63f615321a5a385f8b04a24a58c107917f4932bd012f2e5af5e788e0314c858319018d46930ec65e2ae72d8e6118c5d3c7161324ca96d3381a3a6769f3394356cbad604807c107ca6756b1c4369a11d6e392ba7dbe62bcdbf7d4ef3152670ce6e3f6707a8f59a426c4864d6eae21d42bd14934c4482317bbc56f846f79847013eadb853dc8317bdffc9619c07911d96e9215da0e4517fb9302533ea6e50eea975b94a03260797b459a334dafd7e2292399ad124cd046dc59ec95a546f810531ea57cd702ba2b0395cb24d9a9749ddbcb4d8c3681f041ae30cbc82bfd6ac83bea0846eb75592206009a40572571f1961a4839fc6d3f3bc9b1bc1ffaeccc90da18c95c0d0c36353f037b80f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d43c1dc4e85a3e42bb96eb1ad95fe3ac00ea5acd81753f032f7fa1f4f0be1d24","proof":"4041eda14166d759df13ae4bd5c22248d151afca7e0ed4756fb4bdab6fde2e77ce71abb59652e8c9e9c0f689f06de0aa000089b11b7650ff475e100399c93a659eaac7b7119f7709934e25b362c8954070c9e7e0662f125858b6ffd6d44bea29f4c0ab6e91702647baf22891f7f353b771307eed25eff95b4543670865730b444f551407cd50498583b6e38a970ebf2ff1a76347505a7ecef99a12cd7b9ba203592e9bddc187ca9213f1c40e937fcb5a3498d8d746c4ae39c959a176658fce09641dccf26b79a9598e70c25f9a92b4c12d4707f7b6671f0728eb8e299bbbbc0d9a5383b7d827c15d4b8855a7156410332a49c31f24747f80d0430f6d6a3ea80a3ebcb3bb24fe94d465350131e4ee352f41b1051fab212795d23e16ea199fdc45d8562c33b49c86ac28fdbe99042f7dc2472b7123b01a54f3bc3e5729041cf30a2e2d1a1c33927dc3727a87e991bf97037fce6fbf0f66348eedc6c5d9a9486b4a5e508d7b31702fdba999a03e7661de6fc4056b92eae25a61ee3e0d2565dc351a168041a0963d2d1e4a15c32a88b9765cf4de74bab964d7844d9eaabf1ceef110289a2199a1bfc731ec0cd412f55bd42fc418d110c764b22fa0aceeb784d3384f2277f72fb922cc55c153c1711218ea8e6e8ad547ed2ec9c5a0af912fd865f918108e1bb192680bc1b609ffddfa34eb052262b812adbfb0b623b5ac603b6d993bb0a2455e5c711b27ffef0130b97a9fb7419698445143dd3c89028c0511abd25e9a741ac7998fb29070dfeaba0ab1cd1c9ef3e766bd02bf14d42acaa896220f28e87b2b6a4e3b7ad3b8d76524d770d7cc3a4553e1af0da61e313a02414e6a867abf70b96d536268b4fb583dfd2b3497f23a042f5fa8cbfbb0b1d28317367747070974ba837299ac4d0825bd206b3a4149e3af293ea74de82b468dc1b9f7492b02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"02118d913e7d200e8db6f1ec3bd48dae4e04731b396288585c18894ccf7bce7f","proof":"c282e6b3fcaab82f0b86fb9d49758a1275651701a150029ce2116ba2330f923758aa2a467acafb772ccc17a217c176e18cbd394c70d0c0fa92f1d2e51166c0262c58d9ca10b967cef434b5ff4dc67fdecca9e5d48384c01a23adc370a8f469226886b83042ef4cdb1cb64c57f7e8463fa0ef91ca8f546080cc872c4fb8a8ad23fa9afbefdc575bdf9e746e9890d72013e31c05e3e49f3612e1929633a8b80c0a0174adb3ebf2cb43fa9d845546bea48be8d36ef3c890a2ca7621b02472254708a50d6c7a092b8486c35adb5f1b0093c31b30d38413e8131d20bb15007609fb0ee0bba972f0bbc44db358d17b81f4080e01bf8c6cd7288767bc07f59811c6756ef2bb4b83396723743c1bb41400b591b91f8b5581cfb36114c4f0131f4da06c12ae8e05328963d6fe083c1fc53b4a0249269774bcbf98e75b84f1f032f0e5520daa819c268fa1a1a107a41020f7a092574c0ddd4842a44dd70f50897b67229676b6d8871ab067678c040127235c92014a2997391d58de41624306a7b0dbdb560594396491133d7135ace1fe367d4f13e38f1e1343ee980136067624fe432e482c5c23cde5b4dcc428633f2390355fe9a2bbd44b47df2b9ddd1d17d16118a3640756543c309d79631d39525a6ad90ac941a8aa89305c4e879afeb1cd7363ee2f239c133c8dced04520796e71e00b045039f530d75ce7fc33cdb76747d7f050c8042c9b193aba827a09d93de70b96c80868684936b4fb30458bee69a12b01552d30544f99af5fdb2bd89b95d2d7c9c3c9779b5529a0f00426297d6865117e96c71bd2d50cbc265423a3e1a5c4e40d9d4bf78fa48fd95ddc1e3116f437f0c6375046c6b0218e080c06eb98ac8c9bcc7f090212981f2e26a76e0870265810d3a2590e0d40e55c992434236d31d9acc2fbd02a0e7883fa75577124aa444f0b1789f30f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"20097abccf337a03e5bf19556d4d7d2d8349e59ef18dcff8b59e62abe331c441","proof":"b40f9e4c0e7d3bb1c81f84c5c3ed1c7b91b12875deb1e779cae76fce2c76e276282990166faca8f9fa2f74abe654fa93916000a065e0ffebd4a2517403b7ec0d805f77c2165024926f04646042b67c3bebeb2f36cabe5535654ccce4edf1d929c81b66df9150e97b2e22a7a1387188cebf2397f854bc966718d1e147081aed7ed604d9b92322869c8577c9115179a1d3f9e369fac6a363b516a5d2817c2c5705000b7422be62d3fd762ccc7301654c3c34dd2a0e71b394b767753a9c2a0d1709fd0ed4c6fc0141bb66d83b836d4bc645c7f019503c285d6c58aff106463aab0b56ba4b80495b2d81a028dc5f2e47abb038212677684a3d7a976f0b4a9c905b43d089c93a2d3343d317dff65af454315977dd10e0a5fbd4f53efc035d7affd80ceccb7d7f0072aa14b93971de19dce4811154da514170d38bfa452accb7fa6e3aa02c80dc15f37a6dc7951096e778117b9ca8ad91ee0e9a235a2174dccf73342b28e91feae5bf06895466076cb634b2ed7bf029767ff8cea1092c00c71edfe4753a03e2c3f1d03bef8971297d3ed5dbd36ec674be885b228e44caf09dd477fc05fe0d3e4976d3074d235119b62467bea709457d19f5eb3bfe3ff678c0cea1d42c683ce7b8ec4f73ca012741e6da632d8e4e542f2cd654153e323052fbff525c747455cd3b8c87d785c95baa7ac749b632a5fc551d72921a272650d04822504447cab6c2c36be1da4360ab08112df06d5503a22df221a616b766e8b340e51d830d48228955d0e5719ad366847d17039aa7e03ea60dea0c78c18bc5f56f95dcb460b4f9001a3ec1f78c5be009335d86189fda6c2e0bc05956556049897fd7ec844f2605cb662263b4860c3a98a9fcbadcb2a024deec5d91f1fdb5be90ab09dad10c897d8171af8ca85536befe97e8d0d7376dee4468fbdb8200678f9ed5f24ce30d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cec238f4b9d7d33884698b93e901124701faffb2ef026e6f8ead9c3d31bcab34","proof":"b669a5f11266f42c01eebbbe2a5639bfb3e6cc860e2911b9a77f3af94bef4546289ee419cbf2da7335192d15854a23f390bf026aa2caffce6ae734920d43bf6d06c1b0c027af30619896743449712581402645c7b83ab304665099715278eb4d34ff392034623d2785e1d6bfe91cdde130bd02ec6283beb3fd1318898430c82eb97887cd60e7e34dfb6f0e94c0adec5c69e3079a84c63ecc32b4a1ccfe3465048d37154a7911e7d46cfcf75abcf143a9ea7a37010ccd6d368d36ce778d4d720523b1fe449e518d653cb25e82b9eb155cd1b5b1bd8ef8df4539bd6137dff2500b6401c005e0852e99241eaa02d36f0e12f5c0442b78c017255350a6348505aa496c69eec95501f57b8ed5c5f195dd70317594b1cea1fbcc4ade0537ea05daa15dd4dbda5ab536283164fd64a08689c7194603cdbbc14daac6201be38ca4fa6a094ce5e3e69eb194fdacc0cc4700477eb8a86bdfd77fb3b0165fef86a59c9d2c4e2a51859e55f44550b6fd9de55f97d33bf47d1ee9b23200dfec395848b47d330b767f25f445645f7f869f0a91100c6701499ee093f86b7843c3dc8b42e864967ea6142463c214c1cef56a313660db59c8538e8f8065191a0330f6b91da6798e3ba2ad358d10b0ed7418fc41f1d5f45077f9f93b262e11a90c8d1eda20ecd07706d6495b50e3f63cbad8aec97ca2a5719be054620e4d1e36419f8c2ca01bac8a72dc4715233ddca4a58cc0f2c4798272b74196c1673249636de2e0e64f490d0911ba0bf550ceeb2e9355b7d86f6cbaa14d83f9a8d9efae00baa71307c13ed30e7cd0832b04cc2d137353261c64ecdaafb90dc30c7ea42a7e64fc2d3ccc4991941673269188aadcb1921a15baffe76853b04dbec4099303287534362f7b3ca55b04fc0b047d15abfdfb01efe5e8ee3403f353a5f4ef4ea997dc24fa4efc193ce004"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"42e7a8ea9ee9b88f1f17082aea9acff5749f154713a24ec2c739b993ce1fbe7c","proof":"6c10f7de6cc2396a8625f6b848200800f046049c3c3f641f942720427e530f4edee21e7ecd4cc5da259f43080f48c8b0cd8fb0550b146768876c749d2da8d5048699d2c3df0ddb063aa118866ed8912f3e4dc63965cc6f40d88f97237e768a5aba58fec942dc6f73c580ca43db930a951a6a8f99ee6824da0bd564a6e5d12946d3a60de7dc47fd2b88dc24fe3eea911a664b6572478fcb431f5d09f3cc9a6002d9be95f9354dcd82e8484a61f6aa6ecc565e51eae1e5c9b325f851acd8da9e0855e6fa66675206bd365e036968c743a1984d78f52a8d685c1c6c1abf1fa11f08863a6a0c5621b14736911fc98df9e832f392188573c1d0dbd2f752b30a76671abc215e6d9607052ceeab4666c3e47011273b45533c1c95652b0a360f76e26c1cda3f10a47b8c2fd91b441dd7b8cbde06ed4be30cc3efcb1e987961141057a764585c33404b48ef4b14fdfce034d19811366dae17e91dabaab49300bc38b1cf7662e3a0bc4e6d756184850574a102dae9a072825fa89a4f8b796952fa2541fd6cecd0c99f02de81d676a644ad011c0e20acf33c608c500ae6629557939522727b80a2cfc3d8e27869ef32e04bfd428baa3f51bc78f738707bc2d7f7ed15c37b3cd450f390c59d37f4541b88d8db3dddf1e51b27f99d64ad0250f4d5195ac07c384e9c3f88a0cce041ee160876ab820a5aa1ad2e9df7f031000367a456d693ce59564e47237ecf4d4b4a9e445c52cffae03eefd3f3f29405d3ce98b58b2d05435f2029b0cec8afb9a23e47e87c032ccb0989e2037bb6b57aaca644bc985e11fa35d8900c5c5a7e9c3f222cdc7bb83f0aaecfead58c1ff88069d5767518f994683694e922d8dfe5d20a02bfddf2966f2c3a8f3849679da38c89af6724027ead980b7a1d69ff748018f05ef16ec05977097f69356394f55d4772fba78975338d0104"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"02ad8b4707fac07ad3a0bbfb7b06468e35d3408cb72c91ad8eb0c6467f589b77","proof":"be7db12e3e3f907abe078f9c44d304d7cb95b50fdf3756ebd82991d4038d61693e47275f6a13d1f777515a493221be08161c4bfba34c32d96f847a0d5a83434f064ab7b25389fd677df4cd2e403259e4b71e13b830f7ad385f03de9814b129095808c48ecc4e42382a36266f691ab8f090c596a31ea734c7a2649b071f2d841fa50fb77775ad7ab30e3a094e1e522db6dc6f7968c89643bef35e8dba31ea7f01bfd62c2b01a51e58fd24015390704169f89876cc2b27ecd7ea9bd53be4a11404d50a9a729be2dc8800b8720935c0fdea86b03c0d7b79ce34fb0ccd8293f0a001d0bcc5d576e90c9fffd57b139c48ab085b36052e7d0c6bf04c870ad57b4cde35fe05f7e47e911715422e7be52beeb74a8856af9f3846536ed6094eab94f39e120a41285602111ab8b31bab7efa28f9babc16704260799ddd0991bf40b78d0c3808ac5e63b99779dd2346e691c82d94d85d7fd662eb4febd18a077ca3ad77b855fcb51600cc478e1f141ff718a2399ce2548fbf433e310fb7e263158ed3b1760c9091f6dd0781a310e5e130203e93867b161993b60aabe8f6a20637b6c9c1f352e499796ef492c88038f00491e606760790719256ffa7ef13bcfeec04fa66ee0954fe6e266ccba7c4169023f6f867faa176bc8bfbe1364438ab0317cc25ce18644ab9ef6caed7c968520b18686086d388986b1563d55d20f9eec14b651eed862a7a08229c2fa2aa14ba5d4398ecca3a20a7f549e3f06b80760bcf228eda7048766e1f36c780f142d55f0a44d0aaec94131e100486d6910f4b9977426260b1d509105985a2b120c4a76d8646ea9723557bec3f570697e1f9ad0b3b56ad096973124decf1ac72b3ef409036ae744f90a4039601ac8a2346a75f15452c82ab8f4c0700d5181601902856477c8402445582e513cf9723c337e636c744ea55bf99ed0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7408046236baf734eaa34b4c41c0da8d4e9a5514d086d4886eaccc6490f5c87b","proof":"e23ea0c6512d48cd1d0e27904030bd9f1feb46a8ba1315216d5c4b4469e3d9678a1a0813c01485debb73de7f30e108ea7895935ee4a5194772c74a17976b54126806580acd6f578802db3c259588a630f524483d6cd96ea50d6fae240f3dcc27f2ae3223d41478dac230ab4884eaf3e04229bf58c720cc17b7468bb93832b53b0c0d81c2ad750124dd401bb9b50d21317a4470e7cebddf3f562325e02a316403bd6bebbfbe72801cd1b849c1c67faefa56252d3a9b1140d970151182f1e0cf08046ad1cb2bca1ea26c66bdeb695ca5f33f062c39e84cb8a25b8e8a6902f07f0d5224ef6f2af55fd03daccb50c0e9c2d30e889bee64dddae10ce7cc0255eba455aaed9d9057a438c2bb73ea0703f52509e0911c95cf8ea4916c539ab7b85938047cae01cf65b22aaa14c758f3bc0678fbeae170a1e3bf67f5ff4a33f42a26e14d3236a5d9d2f25b0be615c865639df7d15937ff37a716e3691f6ad6ad64a68061325290c1b8ac9a9ae8ab3c610e21d03d0953df92ce4109bda0601eaf490ba93286b568c02550347b58e913ff6f75656dccc091fa377675ead1889764179ac901803898544139d6c14bbd7df5a02db6507239e2fd5b33adea3549a8785805071df274228579977a58a88549caef6327a2dbe82af8bc4b8969e7d9dec2a91a324a08593ddeb995cfca76831ee54e8768c676d7665f9a58a0f81b2bc26ad874dc26344571697a53039ed27038114c7ab9da8cff8c7d41819b7aa8300694292a586b2e80ae46a25c7f5d180df42604560e136c5af03e518bc83b4f3d5a0f55f90b6364e3e032b74d8a1b2a39389dd7c63208f11dc93715b5e09d756bfe04efe2ab727bbe85d65706693d4479a98838095ce6a068869bffd4fa064bf977f159c6e70193f902403d43a27f3f471aabaccd6aff89b3fd4c0a391fb6b8a8eeefbd643d02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"12a4dc64d09231c177b778a037440588aa0cf8e3b1ff9a64932c912c8eecc94d","proof":"946b0bfe1581bbdb3049401c3a8dfe38cc1b5c80a0dd17eb10af38d3a15c7742def0c26e4456aaf3a5d30dfdacc00a35faae80b92e103d0d9dece19a0ee7fc5a7637a1eefa3669552203f5f459164de8fc8a771b019616287cde5f03fa733f139e33fdbc77f3f3fbfb4eb64fc5626d8fd49afc4b5b0a335f29945132c38ed859fed22abd2ccad8227cb847f93f6477ff1f09c80af3a70cda0a301b49bd660a0ff28049083827514eb8dcc172bc5da7947a1b620e7a2cf5d51ca38aacda5dad0362a243abe74c4a277f150b3f608d10db8f7f022e380cef2b07dd022ab8507608fcca743488e6c86c936e915b1bb2e9823a8c3cde2adec4989753dff22028a462e01ad606508fde42a2f4436fe5e2da73cdc069107e2de6d29b4a17d9b53dec03e007f9d0599cbb5f8ca1ca75ed5cbd892a396b8c59af2179450c7e809f2ff43268daba10e4a97564b4e31fe1328be11850f9841b0aa78a60aad544cbdf2a2e67f4176f642c0c663e54a01b54351039d4f18cc8e8c4bfc9b4a78b0c02e3751d52f078f780fef261ff787efbd5133e52635b789f7c8e2a5177fd57262b8142a32892d45afb151e715302f38e4c721ed29daed0e2f7e086c8d1c75e13c68fac18693c4b09e8ed579bac5b836d597a112634482d9c70530a7e065507e5c680cc837e4674031fa4edf756d2ff1e568a0c2adbe81df2c9ba1201da1dfd478c40f639523c07f7c4a1d4b16a1bb28829a39db77f110a31bb67de64b8c5c46c322a26b8273a5586cc59a5df9155abc639bd14a26ce33496b45bc59b58d0ba9a2061dec93b5484bab9a6d79f1cbb33e0527a2b7aae2c1b0796bc482f0de1d6bd791c484905289b472e6519f018d19f3dd6bd574baabf417736b139026dcbfcdbaad23a2f08b0cb94447c872f5b9cf59d50183fc761541ee027277af36063c741745ba69a0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2edd45bcfb8f9a58a9ac14db8436696541d2ed0a179c26a2633816c6f8a85546","proof":"ce60c11ba621306f71159b06d5c16c0d07ead760f381fd6220578c8e6645ab0e8e5d537fd9acc366abec68f6c9a30207c5139d5e998dede15419e069e0445f28fe956d90594d0a3afe4af525fdb2738775b964ac341f0e5a61da9e37fd7bee6516e9a4238a07830969f67c712bbdf0bda6c782a4769cfd4e7339a1564406193dcbc7c2799c255dbef615683c8ca5fd6b5b889115de4a32c60db35e43f29f96043094c93b72c64c0f8f8f586dfadea476b2a166cd69643390dcd41c89d856090816c011d06cd6e8dcd783da2ee86a7bdbaad7601ddc3c74af50d8946473bb500cb4d2d331d2cf21103fed19d1adc6131a178d683db34309db40010f98cad81324fc05b9a440f17256cc6d37d858f9889da408c0b0c2c54c241db673e402634b7d78ec5ca7ee6baffc1083971086b77b234cd5c21be8c670dd2b928f16f8d020236e076d5228faea037ba6e4dcceb4c7896f97f12bb6e85c2e0b9f7fff6513823e90809957294977046affc4c47dabc27c2220496290b998af315c1dc8bd2df3658eb1d653bcc3aa63fa4b4e9a02f1d682634ac6f64cf6b62f6586a80fde8f0400ccb73470c51dfb8965030f9edd92669980578d5e002fadb60e9f53c174d4a21decd515c698a83e0ab8ac178c6f2e7545d513637850766bdaeafe2c7f6330d73332185a380cfabcc773a2e7e0b296afbd792ebea2db42a2c0c78e9450ad9d6a21bc3d6ca8ebf0732d1b034f3a80b878fbc7411e01c6cd8f34f311301706bd015cc0bef645b7ccbdd30d28b8116c2dc8137a8a74f9022b8fc82db49356022b931ebe71e92472d64d3c490eb4d7b5575d675f6257f6e129c692c293a7543482cb698cb2ef7aa2da53b113afffab17374ace84d5bd3f8f643f19b652fbbe31691d0f259db631c2f7296cbaa27baa5a60c14253dacc2cda931b4a94a91cd550c3ce03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7a49b05936294ed3eb85a2e4d1f5abfd15b2b51a3f5a031c94baf7c5696c5e3d","proof":"4af40d9f96fea35a2740bbc563776d8b10121c43bd004cd09716eabfcc628f1d0287af24ae89b4d2c00e23cb8b21c98a06bb72d3cd1f42b2c7e3ddf147c7f131dc66902b9d6f1edf3867a4cbb1226c95bd4a3332908af6794f214e7b08ce85154ac448017e7898ea20f32cb8f0a20928e02d5c42ee7f8bc5357db99921f4e15b9aa26b24c3baa9ed34cdd5e0d786b90ff18d6136d42e484e4877b73b92068d04099202e24c715fcf69636afd76eb16742e247ce3ea29f4c967288d1842deb004fd8fdb2ba040dac71b04fd070e0694cd78ad9f48b5497f4c190d475165b22200a2e1474f0b54380eaf6f3f20477039bba68810b73c233b0502ab9095b4c79c2326a12eacc5735bde831f461757a5da607a79d62eb2e1ca2c06a1e9dbd5fd34757efbb75e03df6b9656fe3e3820812f3472bd013f36a91e5746baf4e7b91e092a5a883a6ad85ca79b8504be1134cb06af41eb77337e5055aa9f5996270f4bc038f0a2204d21ecb4d82c055ccca537ad5e8cc67b107a301bad0a751155a37b192088796b2b40dd3d0f00c33ff868a3d24950cb3f56b67818ff5adfa3f913bf0c628230eea1e56239c059a8a260a58361cb699a47e223c2ea74fba76fb9eccf3d529c79db32598903eeebd65c2ee45d9ee186c3cd7eb921c4df46c7d245fb6bec4eae2251df6ecc693b198ba96d5b4b3330b2883027079f2c96db04c4d5b0bbf810b0eb46af05da706beb0eb3ba061c48d0fe6b901a22e874555749e5d1e0bab157709af84294c5545d45319f4e1268faa7b38f2e93a236bb8b2702e36f2b3649437a457b2fa6a88a4b59c3d011170f3a237687f9d58565958312565b4ef78240674a7714521c1db1a8aa7458d1b2ddd527cd4a157d2ce7f8237785a4065d50170bf0ae45de81d22e1165a52551909a93e5ab3eedcc3256cc668d5820708e00060b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5c33620d754c852287a4a7619b410420ce024d5f52991231bd78c62bf2952c06","proof":"a89716e0220f3f08b759b8d8843b6e5671881bd492144f6f15d22d2a2ff08860c262c6c0804096fb6c4410a23956690222276ff625e3c6d2fbb0d806896ee87ba05d2a6824564f8e761fc363a8319c2bbbdbafed4f0a32710c87dc7966d98103f820f9c29ff07f9cbd1bea6c6411adc9f21a5a5cdc81d49f144d9d490f4f9e59e400809725a4a3d387560fea8c71ebd6eb73cb1dfb6be324b8c445f705c4b10ce27aa588b2a512a0478bab3891ae1c2ab3c59385735c2b0395328a6fead8d503e372b56d85b2f72262541d09e8d50b25f70728dce0d4a7d1a842c965523e3f07a29dcb0e986549443c138cebc2ebb75cd8783f50eccbb943115642824db8b44308be86ec6d1fee136994eb918877b6e3869ed3e7886e94bfc7e7108f7c366c15d2983f3d812641ba7093f838c23cf95cf8ca31ef2432014159bea56f5333b811a2919c0970f86bbf2a41b7cfb906e4d8a2469d43b6c876c5472e3bc5952a9d1cdef3804e667c76941a516c6eb45a66e932cfff60fefa8c349d3ea55da72be83c525b33d9332473a2237ebb8d9f8686544c9c9f40b574c4ceb389f7214840d519f8cba708406b1b3e2db47acd3c6d1add8435e15b514931261b9e43fca572a61472cfbb60d06bdd21e943fa52f90c484b16435a2f96a5dbdbb571d4c0ba170c04e4a78792f0af59647e89506be1db4a2b88a5cbfb372d5ffec6d2209b31dffa11a86d47a45dceb78926ac421b1a63d080eaf9b4db1bc93fd9af4182879c97037a0667eedcb153f7d13ac498eeb9385471c1fc4d7c1e43ed1c475be0dae41fa22d2831bd181427730f961ba1190b3469589a9e6bbde61b0d28bd4563c840390273053a7b229b225ccd80ed1f95e2c4b76dd3b916cfb38012323d757c0a92229605372c2b07989ed4981d73f37cf5085ee304282fbb30c3f70d85f5d2716215980c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6cc64c114a1f91949c25ff5a92e681974f84bfadc61d944e328af1959d117274","proof":"2ee63035b35855c0fd95760c1249b99d6f7b83910853067a2496168697275755e6d94ad86354d1955b2c1f142232af3f1de77ad1c76fc52175399fec9ddb7c4434b5d1e074d07bfb22851a149e7ce1521db013b93b55475b2afc820e30a5a2274091c52e6251ef4927202eecfe07a68416ef3188362644ac61cf59a551f2703d86ff9d5380f2957600a6f2fc50b67e3dbcf264e2578068db2e22f33b0f15060b6ec893f1a249a7bdcaaab72a68a2c8884cf02f81d7f9714d14ed8afd7868080f03ff907505660a892107864225e93a545353881101a4b1f48292e9cdfcc95a0f8a4b899f022888459ed050c6c7df090c1d75b01ca899a83537f97c47798e590786ce93f77a58a8de613d8392bb21f2f991d0a4c8dcf49d4654ae6a83fc6bee0e24978fae954ef0683ea9897b104d47f34db1bc0d8533ed473ddf8be696ed3752686bef4d6c8140127e78f38e251d8be43e49ed5fb59c6d68edebc23e26d0e535eccbede6ee6bce4d8e3bd360c0ffb618076345ca595aec6587011eb849c265005c1d4ca78cd6fe71d073d52e1017f97ec8ed1417ec861e6cc7468b85055123725e60b4589ddfd33d8f11a5848029b83921f6973b3097f1e07e36d8402856580ca62071aa58104682726a6ad130ec8af057ce98045c45ef5fe498ac3cff0e6522748253d7fb67c3d114407be429857f2d5db1adc0390614627c4cd026c049f83710a7e2b71488e4bd9ea72420d72408288683bba6848765e4c5ae035450568f4e78312cb7e674d95e2e66e3f28ecf2bd791cd84d21b6aa017238d1773cc34e91480e96498a8db9e6b79a37d2adce3bc6206c49e6e97343066d1c3a67db36f5d4218e8d68c29f3920931b7f243b24ab57408963623422111d78d232c7b09cf700fa1b32a642c0012c151598b8a0bba2a03aa213ca33e1edb932df84b6f58675203"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ec4efa1debf8bb98c8d7e3f24a4f7158b63042ee31aed021a32572d043918821","proof":"0ccd6d54cdeee4ac2ecea1b74a765e7e402e09fb85997cfcef1de56712359270e80d41cb299c70ecca73e2fbb8dcce4393114b7c0a8bd2b47408057ce060e7639a2b13daab04e038f04d5ce6bce6139b57038101898fa6bee1768a1e63823c1dccb4ba519ec01d8389aa5df1a060edb5f4e39d670079e2e7fed0389431e44709e117a4a3bfaede7469ddc92b2ab0fd9f0daa8db38d79f415f384868c35764b0998910dac730de326d5d129a51516d8a7392e92b899d7f0e3402b55a9ac528c050967bbabc30e1bf0afd1e7a524d8aa545aff446d53efa02fdf8004e0d8de3e0c423dbadde376d50244da137037ca32e84b40540636b896d34ab93836f9a45e080ebd83064d8583b90d05203c318e652c15b6692d5ce2af1397e4b3f57eea342b90d9d55127d383127cc40b18799b448f7cb7f41ae85dbb9a5d5020e457a5b355bec0c93d72b281479467b0ae465ee738aab149ed18ca7499b9df8f9a4659e0533cbcb4aa3239a9121000cbff6f9dd73fd94b0cf21fe962b15aaa582a905c6267389f764b0044ff71aee9cfb6ed90beb5d047fc3b5f8ad099fa9af09d1849ae03b64e9d15301304600e47cb95ceea0b59282a43e6f36f3a736a6d6f6fcaf5bc6b7ca64ffd1e69d43885a02e1e58dca67a7d492f7d0860ef6502fc69e301524d662cc95c7aa7424ce05c19223a63310035686a8ffa159135a2c42d46a0e422ca1420a5c519e7f14287db29172dea0d428cf60c22d002f650321ab521f11172b0075c4f5213dd7aaa5df841906ec1d86164aa62578a7fed420357106175a8b3ce523e5f36c0586f5a0689c7b516414b288306fba891f93a92de5821c08818d6664c8606670ef06fe3ebd0f39ce180c9bdb80ae68e1beb3b3b10bcee8da0810f3f006d3ef7fb70c3bb3fb42d7786c922c0cfa65d7e79c0fa56b51c4432ee1148080f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"045f33efc2f17454f9f4a70e357c1f1b9ea0602c79f506f05cc24934c4297208","proof":"be00ef5aa8e838ec5dcc7779237b3928486ed4172b47346b2275917902930f58d2186087fd07d173647e1f5005f8daadcdefaed72e9522d0b4a9d2bcfa58855b485c0ede87e698c3a7dc55464f5179ca005ed35aa6ec6dc9066cccb854404a2562ebc5ecb8475381a0df0bad2e2ec714c4234162c59a2ecf9b98d313a984fd2d2587f857f5f7ac01db4b918ff8438aad41f89389a717c235e5f9e1a1b98dd401da35f2abaaa3f9918d2dd4239ca5ab05a9e569f5bd2ae6001cded1542cc6350881956a49d4d7ba0f1e92356e3e1b888de8f27c55f7e6b76241ad3d69f369f504c211a3fb5a173be76f40ac4c72ab5142c2d0799ebc1d58902ef1d59c7454e20a4273430afda39ae4c77face3431b7e0e45b8efa47e8c7be1806de20efa0caf3ed45a1da157838a39014e6f7f742dbd302ed3e2c3b314f3915871646dcf4f214d50ec5dde8318ef2a767cbdad0244b061006e4e26edf12f787cf1cbe8fbe5af5e767b8366c389da6c09c28719bfd8f45a922a00310439aeea4ec9d133e9d75007fc5e43a352361a51d294b42920ef65239cd7c68bf28a0eac511262b954157945c26a47c3d1b6f2b621d6c2d164ed77c0b6c0b813d130fa107ae690b28d92471a96066c924c6c8bcf83a456e6dd6417d597a8fc1e35bd2dddae25e141096512272e02b04740bc51d096f86751fc24bc6e55abacf6ba86d04f5293fc508cb1690a22ccf54e6a3f56b4d73900395e7a1f4947de84e0c5a15d3ee002ec46c2d7db587037b0acfcb592d8499638a52fe8a6eecdc12e3b13acc460f8d7add45cb70e1d10b489943a87348727b1215e713bcef37101b65dfbb007e1a6bea73c9cc3ec1bdf51fd07fccbb5e161b4e1c42922bd680c963e05005eddef44f5e37d2700fc0aa101fb92f28f53dcd12bf87e93ea4e36db38bfafd6660c5df2ba99c44d01c608"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b876e1daec1185bb688facc380db90e3669cbc3d83d9d9ba4c71fe9e9634f631","proof":"9e1d62b23bae5997ca26ba93a60983455817052db398979abc9061c757c8d2772c9f4b9cc435bef3c90cd6ed4f47464a24f2998ab301e7259d3ebd740c78ac4762cd8885adbe38670abd022fbf18e9f48fa2c6797d075b427d7c42aec88c0e01a206452b0f7b52add0740ead8a9b94ed86dfde82d18d6ba2a3957b83c39fad1109cbe60c8651b355cfe2e7bc3859de7ab624a9b38926b6c0eee72e324cd24e09002c4a18e784bd50e121f29c35d5d0bace94e35aab5cb427ded54a2a1da0a803f4c140929abafa4361afc66eeb3cd6d090013414ad69c806c93cd7ea497983093c3bf38f79886d203210df6f6626184f847e534555a9464681601e898fc1a40b76e42bf4ba6531fe26296dfbc3ea066edeff8262e632667d639b5ee7bdcf7860c8368c33abe789da8771672b71704cbd2e8327c44f4c88ca3a4496baee9abd0232613c7d8d4505240f0aa30f0a17124867547d5e97b9be24bb83baca1604c125e0eeb19dee43433d3286653720400e247e740e2ed7604be54f70ec75a363e7484801cea0c8e68c7ef51774ecc5bab405c476cc39dbfbd6715e885834e8674a4ec6c63a0291a1f9bb3471a2521db4540e36b6de015d07022b301fd0dd9e6be7009ad250ac5997ca43ad0c643a87f917951a269c87019cea79f15d30c3ededc200ee5a622f91382feae37cee19fdc09bcd758d9382ce5d58600d6d1b5660136b33e2fe2717cfe24ce933ab767469fd26cae89334e916c60b5292feb30dcf3b2f427acde723d9d0e1b0a6cf75da03c7c37acda638981b624cce8fd68929d80df80b025c4d6ad8d9d6d7418b6cbc38a4f3b4586d0a4b59d365e0d77a55cef3ac8f054879623f49dbe25b6ada7b78daa8aed36c97473f196ee070c78eb76a2083bd08756d831954b2a1b299ccc4d124ca12b23001be0acad5b5ca634dbe2b4009d40f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1237bd91cabe7453bed42784f348f84b3a1d8519eaba11d4bac49760cc52e336","proof":"267cf279d7d552d8cd17c2a6a80ec59e482502c490039a9af8c24ff5bbdae5215a1dc1c4e05c2ff9a1bf81d220a03d3323a3814d27cdf4db8a6ba199e7120464e622420a7118795b187bc94ecf406f6769ed6e46a4bc2d958f6b3f9b1e3a3d46500398b9be784ef6a5579013a7b6650916a8d3323cb39c6579ccf1a6a73d7a1480b041cedf290e398fad84d46e3a4c8b776c35ce031592c997946b560904700ec30d6ded02acd1b5220c3477df8ede4ea8212b6b48c648624109b61dfe94f002037a036766787ee18c56074c1115844415edaab8a9648d042ea31e19ddaa0d09c4c4df3d4c5c0c990daf352fa0b45f8b8475c4f90cb94eab6b94739b11c196593e0fbcf3090c427478bbb8ce6e34840ff7115484602fce752e5761e432ce8a78521f7f9c6ccc0d6f235ab6d98e0c4615edd84d7b784d315ab2baa66ac4272d3ece7dd882b867f2ab16e99b7693bb26d64230fe45180255392dc3db8dc298bb3fd22bc393041ac6b139323f5c18c15949824e03f353d9e35b96ecd704faadc94cce1d4d4c40bc66dea022cbe1fc122cf67477732c16c65c16351ba5e64d7fa84daeb6272853b7e299f05ea22fb2a8cd56afcba86a94d3520f202ca043adda6460801b7021bc5d2d80cc558d714f17a20cc60c9ae5db58f9df535ce568c52496106c0ceebf64ab7dbcf0b4a980f64e11492ff55362b62a76655ed4a52a823f1a2862615b331079140d86f5fccdc2233b2c62544ab1f11072ec957a2e7a457f6c34ca81eaa43f75d5e3b6c2324e700e51e5f79bd495c19ae8ccb4d4e791b78f6965b08a6e3a970d54ba38edd5ba1fc12240850a0cae3c042725dd012865ef9f3401ce3da7c92bc8449732e0392a4473c7351fe4d88fa4f6f2f8c051a3f74bf84109cea485ff57ed361abd45a7acde24179e5884e7372feb848964a5b7beb21ef00a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"80da26690bfee6654d55068741fe2e8f443bc46d5651922f619fdf188b7b0e01","proof":"c6b4a5432ecdede4ad4d2ab070c13b65b2be85c57c1540b19b3aba62e3a930672e6d80d67e95a5faf2bb0eb749acfdf6d5b9d43ff6353ba6dee826bb6067a4024a15004e9bc6a5b384833fa905d2c3d7152db7392d80984aaa36302161a86656d08a86e15a558f3c60378c8c0fe7b4b63d49b4cfce152c59103b6d5c57786b45347e895f987ea687fb32ff933a71bb1ee53c5a5399d4f7c45903bed664ab650427803e62b77659c38c2b0f529fe0c08494029fb3863f342eed806a4ea7ff950d7c3fc53f35ceabd763a5b78b890a8a8ba72ae92a8ec96515f16240e0177962024a20d40b910b0fdbd6055ff5bced96c516c09270267a7909137eb5905d6072722a2c2f00844f70f275d1be2f27068c2c6433e2704b34ae53fec157885691fa46580a4dd46a6f63a793968ed9f0a03eea244d5d2c6ffbb3c8c2bc2cccbf0c8942fe11b68bc73de6da2e231e38af71e48d4849973ddddc43bbddcea2b0765a9f1a30f3e5d7ae574066f932d585080e0c0029dd5375cc04de0eab433b7eaa68a924c029d8db4f489c5a508ee49c30c4471f385bfce7bd98175d4e8dd2d54e5f3073a40710ddb0e8f729da22258a302229ce65e8d34303222b4ae3edf8d807cc4a6eccd43bbe1e93f7ef16fcf954feead524c020d10f4ae43995f514c95336883d26260b2c215ac228209e3d8d32ac9be6f3c77dba425807712dc4e45f645309d877825cb8cbd5bd9ba87e7bd77876c13b8f61659b59aba64d3c8caed5575e1fd4229edb2b16103781354ac79bcefc2d934f5716803dbccb52eb30e694a29dfd657a2c5470b8daa6ac353074fa50363fd7ef11445c837bfecc891f808d97764f5f21644db7188c11576f4132b40cc6ac8dbf1f586b5a7cf20242947b4ba53cedec071df6ecb232e4896043ccc60664ad7f0107463ed51ccba5d8f5b4105e3c059501"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"366ed8ab6b3ab6cbd4ba2d0dd24d40f80615007f1e4faa3bcad19261648ea62d","proof":"e86df64d931639aebeb2c3608f8f87b4ba93fb22774da57478212f603a1ea5494e4dbaeab4d5d9129256ec68654cca648f28dbaf8612bd3558c3b927ac3f54426819e03b328216e54a04bcc4daea043bae56f9837185d63e8bce421c45c3733858de14aaf40322e973ab3b8243f65aef59f3078760ee9c21bc9cedb2ad6c4436797e198b5d2026b02a2ad8d38548b3630ecfe6805e591e7a814706c6749c05095a8a039442313824b131b7edd1b975551406579ded64ef3c96d8075532e1fa0aab3ac9a65bccd51801a03b13763524979bd2e892b16a2d68fd752d3707478c006eae52b5ae4d71262d5b8027fa2bd08f3152b779ff67b945378ad66167e784517e018c0006e029dc75b9e0a41f37e034da4c707f532a19272da9145bfb6d603e62cf8e743026f9cbe01be5b7f829cc057788a242ab77d84ccf22905557e6de09e2ec6efd7335a9b8da5601fd1765b9689dcb4dd25c376f9c141377a2d0aad4761e1757dbe0cf1094cc552c797821d1cd53f9693104c36995e4878a4a815730266ec553ec3beb2287eb858272ecece84df18b64c2c2df9318c6d1f575a5dc3d3b44fd60844f5d85b8887f8cb5c78d5f2ddf0f0e1302c938d5dbc4afc88eb25a0fa090f1331f71515ddd87e9b1ecea9206fc13e6eb63d7430b32faa4ed0b26845c5c3c76d3488323a6d54b27f99437c7340d3d3cd4427631bbdd1b4af7f62620103c1ef4d5079ae641f80ee5a13a7bd7cccafe981b8e2bc8c98727e50b9d36921ea832da714e78ad2e2fc71ef2f7245bb4a84140f978f02942120f3050677c1460d63df813b670b65c6632dbbcba4b1fb51e16c7e9a5c5d2a2960815654ac1c6228c4057643aacdb5748ce83e857905018d72ac46cff5f70889ca5ad8383a3110600c18a38e8b99a831134bae11db1a4edfb67df3251268d763ac01aec34176f0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"de930312cfb532a7f0938d9ca2d6b959b3d013b2bc74c5a6047353ba2484d943","proof":"5c2c6369aac536550f5c2573517c4adcd152dc86bd48f87058769af349d2a376964577759d57288b53f9badbe288a904c477754d0ed85284834cf6c46a3db62204c05900c515a6e0ecc384d6fd862891a78e475474b4c8f8a3a6e06d4b1050433ad131936b27800eebeaedcd328f0715034f24f19039f7f0ae2bbc8cd141c546ef0f761151ee431431a71cf75338d6180cff3f4d94bcd3b414076fed1cd7c70d6286ae2085c92ce7bc531e987f47f10896838bd293ce86991b2f69214c9ee603c4a83e4619fb436a21028a08041a52278f0356d02b6c431729e0dc780af42e03c46b90e1cbbb30153a969be4c0b579c10d5e61348756167fe01f5f52b0d135641ea85ad6583deba650897d167f1c4e80b561db202d3e4af89955da3ca471d7218ad7963f0e11e3462fb686f30d1ae069af625798b67178b17e24b1c6bdb310724258559454ae48db02c7b42c4ac6e9c48e44ac4e57116e3ecd4282008a2b4e492801d0aa4f2bdc2801e6bbe763a78d0386dd70b83d079c42b152254b9335ac1442fcc10e6de868b7d18c221dacce4eb5f76607e8ff01cb54aa807bf0d72167769452cbfc30129251abc3e604fb33d19093e94e26cb0a2f5302a322331f2dd4762ae699854f3dadb016960531ecdd853fefb7d0085eddd136b91cdab69bdb6835e003b54a1a8195e12993f2861853e9cd4c82f09c76acaa9ecb27846235dc6c265e1045346c58ccf97feea4e1402d01797423876537ddf139f3f4fba3e7c60e6770489104b1092cddc085acf39d59bcfab898a168da2e82e4e74451149bbf5d53e22c4f27a3747101aefb44b7c7982e294fa91bcc76537e619568d65f59aed8433a1c5674a56be8f8543093138607a4bad9a4af6df719b8a7eae0a8a6f017990d659e620e9344842197a6210f42023e0d4a96d33baf7e95c00ee6b7b3ce318600"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4a86c1b31e65bf8a65f397476d85c2ae4f8072786418920cf91957409deea743","proof":"a2a5ba97c36be2ff8269405f8fd30aeb8ce15008178bd5e036549e5be63a6822ec9ed31a65f38cd62d20f9e0d75b1321509eb1d86fb1d3dd8e4abeb77eb17f05cc6739a5ef64efcc83067a5c231ade321850067549cc1b9d1033a6bebcaa9d1466928bcc6cf0f6b16e7adf2936c50f8b4a4997adefd2256cdb6def767c67074916dba0d27ed4a605969d7d9629d7a8875a223097ee3eb771fee115550cf5260ee49225fb2e74f5b331be9484ac234d5db9b8b696ab0b9e157f86044b5ce820058591b2418878eb7611158797cb46442353d89a398cd8c2a2010303c9045e440b7ea86577ebbdc8e372edeb37173ca77308a56dd66a201389dbcdce42eab35164c846e3305cc1eeedb8b5d5a7eb785d7cc41c54ddfb1a3f96280da54d1c7be3432c27bc52e962846dc3e42abc5e2b506deb25ae8270b27c3a9f0cb8ca91e475715628434ac10bf15f34622a6c64c3398a9adb590edc177e33b43d87c46c663c58bae7f003b2fd962b40b2c227e56242cc667bc5330d669f048618df9c928ee259c6c9f8dd2f0b8a880f31dfc10c93bb68059b238a32a54b2b2d72379239649679f2f71f0a7d7393429dd023090638dfced5a7fd2f3e1ce557a918e85bf628ed7a30b56398af4ef628307f654aa801d5284931f855e92f13d6bbf9d8550b05f40792089355c39b554a3c8e07ca83a5e986442fa394739d48a70f8364274ba8753ee67f4178efe21f67ba6a25d32f29e3df4385077443630b71f0144828274f29675058a0ded816eb2a625c4f553f112d1f35527f7496b312bf6dfa1291b640cf45dc5d7da2ffc532aa1741ee9f6d0895bd3d0fe544fb075e2a8144de31f3620a07e2e6cb607b1a177897d7fb363f14ee32bee6ba14207da05985315f693578d808ac34930c7bba664429c6f961a4c528fd9ae170f14dacb046cbf386274af21506"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"365ddc4e5395901cfd99864f4d1ede50dc17b7125da5feddaa4825c2950bbd02","proof":"be3c14386923677dc7b6a427c8387f7af54652140c88f6121ecd0785ba65132d74801481ae78b91a07af4b2c597c1e82ed786378d3f90c35d8f98be521b4663be84dbe7c3fe278ab0bc3e31f7b11dacdd6a4a3cef205d6b9c8f5089082968b521a1f36d76890d46e1e39345d582550d0f5b6cca3d01f8173dda69be8ef3efa62bff317f9c172105c27d1fd7dc922a9f0923f4199a376c3c2f92d7a5982b7190edcee5fa3e7967bdb2a3293611cba9720d0ce0752fc17bdca53f4caf135aaca04d3c8ba44413037c165950afb5932fbfd20b2ae7f3ff559b73f19a71ec1af6c0a2a97fea146e77efdd3ca3f33be2329e280cb3fa04c113df2dfafca60c335c3552adf127aeaab04ac08c1b92f7fc9adea5748b8c7c7215c2f2719e2d17816311420ef28712825acde85cd443bc1fe9d6b31eae3bfd9b9901550725890b058a70a5096e3775e5298c2c8b30156608584f8c27ce19dc055d028342dabd77df6fd053c7c24f8b68eb8f5a4fc53a393c786e33bdf8d76d222d2c19e4a950cfc1f566e08b68bfd21f0730db6091e5267ffee1c1a1eb776794bb74f0b5e44e226f8f90202f64d1ff93be321d7d7ff2044a042846188c1de24a2ce2de94aa727f694b121aa6fe4207b34be49c3942899411196ff4f4973e87e2acc2e38d9ee8378eaee7dbaa920e28fff3c4caabb218bafb780b65fcdc0e0bf283de98f00603741f9215f6a30bcdd8de70d5c94fc65b862e08e9972149fd95ff1ac7a06f98e7763aab44baca5b485cd0b0a09085f449658560b185d7e3908f028b79ae2bcf28a041ed53d8897738d2c7aa224f3359bd5f4464d4bb401290d37c2b4e7f4086add7e9fcd444978f9dd698a27d91d0c25ad88209eff8d0c1ad366af3e90da1d7c9c885a360bdaed11d1f082186f7f3015f3ee21bbeebd05c99b104ad863c17e81ac0567140b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4ee4f3cd1d8f932bee33f6ab84855ede03132fb65a95bf421fdd896f214fdb2f","proof":"c4c6a014147508b09cfd95ec2275337effc30c14374d50ec648ce24a91944d6dd2e73e660d26bbb01d19838784778eb0e6977e5ac60082ed8ec09e16974f565ab0ad71d7e8f115b46f6e070660d1eee2ae8b44512dbfad9c3a126a336b24a06646fe96e4569907ebfe960235e02d8c1bd32a4ce466f00b5a1c341455af8eb91d51b0e10ac0b188584fef4a1369a1c53b86873bf266631f4c7efa2b1473e1b90d85bd9e40d03faa8478f604847681b7677fb10abe38ecbf0a3e35af97e49c14096736923dbb6dd3828584dfa8adf0afa82c1c802992c6a5011983f06d4a492e04c82c7b6d06547ccfba4cf521c4aabea1ae557f0cd37e7c20538f61f5d548b0739e0a800428da6c7af3af9792062d2218488c1e752eafe537234c0f567dac751560bb564700b24be1713a4894e336ee3282226fb09b904dd10977ffdb17c5fa39bc9bff4264aa75c9ff2b01b7d7c35aaf1081e8472dd64048474397785ea0f73a3ef2e08a7df29f805d1a9104aebcb1650800e7ae029c27b3e41e8b1446b7211b004f89226de0152fa804fe8a78124fc75e097a392448d1476c5d3718d8421d61100a9e36b9f3154e77ec4d82ba177a1bfee85584f4a6571080f080dd22bc107a76c800e87e9f75207be39e373131b3965bb4ed80982e200a9b688fa1e6f07941209571c3f5833f4865e4334f871d3b687d86b5b9ce5c935f91a282f758a1c47b423877dd826c5cd748734f31ba0b31bfda463bcb546e50b518b2280fe229d422848457b698b624bdf99dc24f6253803fb14d590fb0b1fecba335e0affb307c5024c5b89fc76ac2e81a0d28b2432f767bcd8479b3b620c7e461d84f0a874f74008966b3cc3a1928fe4cc95aeb8d8492eedce6519f4300f86c221d004964decc09ca3e32cff6b1ff4ba2012186bdae3ccdf8750c718933842f829150677a806c0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e83b129105dbc54db703b22714ec8dddc72a24b855369aec1e574e3b5b959737","proof":"e241ed013aa9574f5d230a32ce0d9d219a22ea444db3d2cbd31cba0a865e677f727cdd6ed06548950bcb890469d75a8fce455f743f9008cff7341a36bc90503198c5b0083063a9d3b14ea8b7587b090c2abb00d96936791e3cbb7b45fb88cf35227696268635226f8be01c5738b82d1cd612422a4a58440e953d7b93d1717519c648bb13a41392486a9b61183acdd84be71beaa4b17344f0113aa2244dc20c03f956cbd641195ab813e603bdc47c2643081e3ee308ac0a8c12537e5269d8070c298163dce1a6c291671287479b1fc3c95d609b7048d0d8e2e1a9444cb595a8079655851441749300a3ffdc9fa368edf7d9e4f221cce78e61a76b71788b08d2390c5c30790d93190402d964f051fd7d47b227dd0547b83c98089f024265a64144b40013e9d213f9b2c8067a225454433a3aba417ab40ef9fbbc6ecf2c2c409f4e84b286d212a309c138ca4d10ecc93f5ca2ccafaf4e927d59a5bcfa2ffab0fb12720c13ee1c8cbabdba20050cb0db882146823fd5aa398e27df62e50c88c85c1b6ea6a1e00a54d16e03186cf62bb57135ef0a898ed20d615c8c3c2a330d574e584e4420c3113a4361e2f6143ce00e9b54c5232b20f8eef2c251e6f409090a655c5abeaa16de21e277897108d7333a4215b41fa77f87d5bdbc8b2db0ef67a1033f767c9fac10593563803d1cbc4b1eb77ed07bd66a72c1b066aa32dff7c045f73f92db400a559bb8ed5b4550468989c3c6532579b9b466bbb67e5bcd3e47000728ac655ec38d443e9b8bc79014fd9cbc694f618c35c6950fe050b3c2187de9314dc8b85de8f4af04cfb82d7fce556fd4cbece752a3c5cd850b0c4cb26ef2b6e451252a8fe37242fef154baeb9da673ebd2592cbf623595be799f17a353d5bedf0987da9219d1db8a1742d955cca33ecba14711bf12de4ee45b88c26a6873cf8f01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"06ba338de8b41e8e71291e3612b50df0287a93702eaf594ebcb710884bcdab0e","proof":"568634a2439710be85a22bfcf9a4ac93a40bb20c6f77a681d47bd040fef6560d58e209f9cbabfbde599b32a8e670669af9854dc269fde3d641e5aa275e8f4755f246d10ccef2c6e3ab463be3972749ea62a10976236d57dae1464e1cda103e3132b384dba69d10ad7546ae97c8f3fcbc41bd11623b6da007de75ced1890f060f9ebc00c24e837b0a10b586de0275dfefe6bc8e253abe40aa0e0c154e480b540c5ec8f57f37eedb69501a76634b713c460c8b3733bc74b58f038664f2a1a5b90e879451d565726317104c8342412e272ed87715e8295563e74a70a87882c9b60feadbac17ed5a7a201c4cf93eff48c50bca08d9c72f819767bf169855eb9ac5534e1c033a8985de071d0cb756b192b5c0ed0396bf97316ca9b954b5ce1b74015d943b9e9054f8d351627bd38155e0953cad6457db92b152f115f66ffeac7b891de811561eea414ec7b3f8a7dbb47cc7e27b7f77eb7c93e4aca3fa7627a9019e46b21054eb98b520855268c601f3cc4243b2ae5c93fb4ca09dd65f793646c5f434884b5f9a4737e08a589e3231703b744f17254dd937b5503ebfb2e02e5651ca0d4e697471c020588f92d6700d5fc46774d1dae0fd064eef3b38082817bc8515360494c9edfcd9f62dc93fe49429e8207953c5f65f9100dd2bbeea3c81225e1c497a7c2e6f6b790d3629577db9362be43bbbeef7d88256b9e855b111f82f971e3a2c04871002dcce9e9c0a6de7758f2f231b3bb23ebc532ad12b42a63723365e395ab9c5bed04c03e372c473dcd76bdea26a34bce466ad025dcab29c81688ac5013a8c8aea73fa4631892c4109431fa8c6aac175120bb3da8e9f206d80da4e5070ebd6ec84188a16e9829ec09f31886a038f9f312416d31605e6016f847c72e10416147e763cf47d385104b689c08e8a95b7843d6a54c146827dee3f9508daf405"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4ce3e1a68af0fc3cd64fd974ea815029dbb5aa60f0780887de8e799e4fedfa10","proof":"2ceb91779023ec2175ad0f2b3abd842ea2ae7ade2761a07340b17c1c149000065e57e399df9216b4a8affba912c004c97be3d094b8ca11ce79502bf73cda716c1a318cb89b72e79306f36ba8f69c8c7ac4bfb6aebe2be358e81c64cfd4281c2860f5fa7329d4298585d55e1ec74cabcf3662fdcbf3334a0c51ebcf2b6ab0a776cb6b6155274e2ff4735585eb879bc9d2bd052bd73274297333db94713cb2940542052907770a6d9e732df310722fac86c54c247d0a0e0f98e26e7f5dfd12900cd827a26f64bf208b423d8d853703ab345d2e3bf9dec82e482f6ae3291a804506e6cff8b99470b2b934bc8f0f9c5e1606b76de0562244687aebc35ea482430332120137299d84c956eb19124dccad36667f7e1a9e54d56ac746de0756b71c853252f9af91959eb840c3a4b82f0231ef20aab18beee69551e34282b3811b69cb7fd08ba181a19eef62af5a2a63ef052a83b3228724e90a328554759e9fbf4b9d6d9aea8a14cb0b7685c6b818284affdd6cc618b1201e1d8e0a2429e10dca3eac7858efa9b969861beb2a06beb5b94e445817d0cb27dad6748ceb70696ee591af44427739fa44de3154a502b8e67e86c7b5fc192d02e396c52965fd7eab4e59962af0032b0128b7c4276d2854cbffbec5fa5238fc1ccdbd52442b8ba14620aa7b20e470c45873d68a8775838f349ab2a48b6d980682b8f8efadab6eb8c208159d11d623b911e45946ed517651b16a7ec0dacc4efad04f947eb62393757004e097581c5161dea9b1271c44e181d2768c6e97943632e27733bfac4b35896019ac8275a00ff11f3dd7f4d0e82368caaf476d71592a97f6e4d84b025abd7e7cbbabe76df15b53eb49aa4d145cdbeef597b33834e436b19ece9ff9c6a3c92322a5e77801b903eb84f651bf514300e6eaa88fb462e9a5e781f0705dcb67f113b1e7627205"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9c0a7cae20a8c6e287cba7b3fb2cac5c98ed2ed513b0210e1ae27650bb5ce139","proof":"3a0d4b4306488a2b6feb1ce147bc5621334c8dabc5f2ac422c65fb01e9e8de2db4561e4360c33af2aa802235716fafb615fad060a91ff878cfbd7ee8ea72250a74ece7de727cae5f61cf96eae615d8346b82fe3724473ca539fb4c6642c3b7554603d61df190dcef5823a4168b6118cbf865462078908817ca89e412831eff4e3808fa6cef727f0daf2936479e9a43e77f85c50ba6a4cb7096dec4501cefce0cfc55f4e88bf3a3c8a8ce576646632ae4a81cec7e153ae266338396f7d910ba0c1722bf200266f30c6c42395d20e528606526c32bd75e12e2091a0c5b2a1295014a75119e9baac170e2477afb4dd943cac71d6d69946ace7ab4c21f1ec72c375afc8520e46c4f18dbe612ed4a895ccba783e0dd5deaef1cd6a373956f41485e46a67d036280eeefaca37fdf166cee5b3fc30d170890265d01700921e5c19b2261604bd75ba4782e5054a3212eedbefe3acbd8d1cd3df895c51102861c5875636930cbb98d19159ea86ad760e2aaae8113e0912157d604752875a2bd36fe359074d69e4ad9cdbd25c254594b459cce0a206b0bb409086a5eb57a4da9784ddc584cba343844e1fe03dd830a5b3ab60ecf48ab0f84552d33477ded7f42d69564fd544cf6506ac75e6c1feb7dec1e391dbd7c4852b7ac2f4f3bfba256a7fa5017d017d8d2e6c94e253895f085699e662183cb7ecafe33b21d8ee2526172a32b8fce0fcef181dac7f75db4e6309086fbce810e85db6df4afe5ec8b27e853126903a84060d9288521f9d87af74fe13d74fde93b1a666cbfe2d5f835258fb4be8b2ab66088b7687cd71192e6b1f55119387dec7d313fc36ec75f0c64afda806f79ba797ab652e3fae51efb0eba2e547e29dcc03017cf95b3bb5764a5809acc6841106b05e69d3e4cf171b6a247f579d50b84f158cb112e87e1f6e6bdef7d57ac70aadd05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b672bdd84500deae64d09f4917aa80b1ce76fd41ee7796c7eb77e0f5d3725115","proof":"70142cd805a80e1e09c17df6c97581cf17bb1071db4b89ce114c4fafdbecdd7e4e3b670001b19cc9d52c8189742e78d14e7bdb180490c448d417e404268c6236308fc2c6d174b977a98f89f04723c2fe90457bbe67d352705b26a24a50a092648840029a47b2e48a0e4e99a5e00e9905c50bde51275f1bc462b62303a07e83005e3055c2b3682f95e621e3b28cb495240ed9ea809f2ce53d0dacd084c88c720f099af02b3a36d9694c3d02f01969580f51e9616803787e9fbbe7caf3eb27ae0dfbd1616eb26f23631fa2a141d44245d99a68c819ffe24d1e50ff3966625e26003832179e185ffef05afa4d1075c9c1f86f866a09a5a5b49b756523f9284f5c7788a2d404ef7bb11e815690e727a69b9db6c6ee4b4530a769d1a4f5d3fe9b2557ee5e50e7a84e7901d8019b71c2844508ed66f2400c86fb9e8c852dade0f8e926be8ab862e7f17f00c8cd543c0d64ea5ad89a551b22955202a89ed7838984fc6144d974a4947282539c2704ae5f3960f483a1b65c79f1a59947620028e8dd854956e3f44581d62e623d133405403a02f5b5527cb0647f4028355980f7576005203e90a18600a36214d63eecdf9e30484f57ff862b47087582e2becdf271b3ef22f60028d942b60af03e9eb6d70df37958dacc7dfa82475fe24fd6f1609220630c42eede3975ce7d06d48c371761be57cb649159be77ae1f1e464941472205590ed49756800e7f62c02fc2229643fd1f1c71bc9dc0727217d73b6f5d837778462d54536818e7122087c51eebfa9d577f5ccebf1223538af4b78500b3b23e8e0217726b25361b098b5557426d0d5cdc18a2bc1c316fb3db2a376d2b0def759f7615df0ae87a7b0596cc301e8d4eec1ceb5a59aa48f72345112ad6a51491fcc68b08643ca965784306ba09f48a82a7051545d9ac11068f593f1e325aa575960a0409"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"841f7f7ef7318f6ff5215ca41e5d29c89d334d9f2326e18043e728a4e504822d","proof":"c8adf3dd2aee9b9d21d853e1ef1133b10519ab8e6e97afc882c0158fa0723e101c0fde6c7a129a6dfcabff021fa6fa50272520e68397c5abc378d7ebdfbdf3075ab5150cf580b96c6e8d14f6a87662ebe222c40e85066f205f7faca9eeb27735a827b8fbe0a98e365826b279af6a117d0de0b5c6ed23154f3d9cfd4664c1223b4c85d6d6bab732f188e7a6a0fe5f5d887c50f272c741ccca37eeace7281a4c01c45331d342618ed78c5abb84a173f82ff7b95fece9dc616bd8dc2a90b565c408ab9dc3a29c4f2d900b13f6955d3e59f8bd19e634ebaaa4ce1933a0d580168906a0fbecf7e8207da214742d59084061ec48322140e9588958c31ef795b1b4781d6c9e583d1ae9911606e435f5309c0d5e14f8865eb5bf24d446a47a406c1c93735a29207b49c806325c6a9aa8e9b1681e1fb05978fb848ec875494804b363865c5ed3ba100d4dbb0b968054bb23e1c28f12e149ba0bd96712a57e6ff16f937e586e1ddcc5669979dacf1017906377eed59f36dfbfe8a3526121b00397b74ecd395c37584394471ec1a44879f17674e8293294dd58132a8624073f1bf56c74406dd45a0a58f52eac24249370108e6916122cb9624d6a1ea844e92b9fe67d00cd382cb999bd379b95a883d7bb751dacae70e94a4c65b3b6d490c5c6ddbfe68d132146b4a2b472cb00a36451600817f51a8895be7e6ec0ea261f9431f2f955fb130470f083b51245fb0595fc66ed47502bd63f62b06194aca5e21d453b32e0659d7d424d3c41b36a2f2e769fad64393ba06a49aaf7e7b22b7c24d547d5af2ee1d56b6218503f13a84bde69540643fa615a60337691f28a6fad6a74bdea035ee15438093a822330dc749f2a7fd72ce17fce7cf6fe84dbf5eba48b4649bbfc88d287009049fec7942b027950a9800ef4893273dbfdaf4770eca1729d928368624c6a09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"04f8a33a3c6067b1a5a75e2e6a5361dc0a70b22134c6e0954396ac0b7cbd5e7b","proof":"0282fe4c15d698428cbcd0eebaf6743f4efdee79aab767d86ea0db9b24c18f5aa20f1e491d4932b1ddc97dc6d8c497202704afdd5d333eadc608345db26dd1305c63bd2e2e346e1ce6349cbe6ce1f0bbb3c654088f5c0283aa8ef1124c8bdf491c0911903e99917518bde62e80cb637968ea1b0c1bd5650e27940b6c89902101056ebae0aab90f76f7e05106ba2141c4f6a350874f82e873b6ae653b3a567302ddf2c3326085dfc9abf2ada8af05316210a255d141c31172ae009959b4a7be0a0a38c4c01dfdbdd52c359bd27017e1f461a967d0e96b449c0a4261a58d53fb0f54e4d426a389cbbc865caa824c6e99449051f65a362e0d855f7331dc6a897404faff703e0e9910df8507dbd1c3ab9935d7b46e66f0afd201a165b56104eeb37d8842de1e6e7a800b8f82a1953256e4443215620acc5d187c82923cdca1bd7800b0c3551407ff1a756e7b807b811399ae6f31c955df0f6f2b96455a7ca444c17480e39c03ce684ba8038aa8989e7028b47f082c070d4d50af42a26cd4c3b2be1dba508ac865358502e67f9a058f018974b4fee97aceb4c8554d4b5c1acfbad14a709c1a73cc4416ae430641a77664a2bd2247e09da672c6ccfa7aeb715b0f48294ae1f500fc401e37a3762a15d18da7a81d2b083e91a596e6fcca50c59e6a145b0c195ce44ec2b7efba4715ce65fff3ff403dcb92528a4d554156608a5c2423617892a6b3c5dfe6fe93f05fb5879c37ef382decca86e8b934323dc648cd4972108a159273627a94039f377be744b34d58f17bf74488a1d2fb5d8fe1c078b19d625cf77d008a7642741fad0ad2d58f121bdfedc14875b4b45a0ad96983b769d668a799ca6de920d481d7877e3ea82c0041950c6e1c0af2d2df7ac908f07c5c9204adfaba975d0d7b7184267159a83902904281dd0a662424ecc6e7d65dfe897f03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e6ebbd039eb7aee963c382dcb924dee4653eaf5b9d9a8b2265caff51b31fad75","proof":"c4654f6b21f7892aa27e0e9ec3b14f1a0c9b92e4a77844fc22c8ade58ffe3c75c46e96d08fd1087be557c5fe0eca21109b1453e80779883e399fbcbe51ea4607728f41d3c215094f9955cd0702b4a9371d5bddede74d8f5859f5c02c1c36356440454039e3065a82236c0e0ff3be045845084967206f61e1336377d9dbbe686057f207fa15a94d57a6e642e7b3e76440fa2f3deae36aec3b5fd5c2cab98c190832a7d5ad9025e8bbd61f74f3da3b66c46dab70d59d4679e1e7c76e520420ae0e9bfd1e02370b264c3c68897ed53ebf509d78003d04d3e7dcda3fb12a5656210c38f1de2221ff79d8e0e82001ab5ab9966ee4c0b4968650b355463cba7734600ed8c37fa561aff6f8cacbdbfc502669fd5bfe5b9a77bfe93f88b00269c2d1104104aad7b797f400c3460f6271c68bbe4d6490cf17a20c9767cfb473a2115c54056a202db7b78e74df607688370a6729c34bc69d21262451c1f583bc84809c2f520e8a7fcf17f694905edfd087b3681c095553c11d110faa33b6768068769d090eeee1b1353095b93fe23128529fa24f8713153815e57e834b6a7d42e82662f70a1eeee843a1e52d21852e693b811241acff9840c52115b6224a4a1440834a6f6e6cb5508045c6463a5f8a1a4b9a306b806125f339ae72d7d1f6795b488dd06e6e10b3687f59b924782e059d1149a9843a262fb1c0dde851b5776e5eecc93ea86ea68e7ee08c787e507fbdf4c59b47f817ffc91fe3f491cc4f3d8755e1ac5e260612e53f4b5c85047737176448c34a9976dc4e2b0d652d8ff2549dafe0aa8ee0663a559176234dbb2c421c42d21916ff3bf7d3a649afeb671c7eee495e382f22704b751f9c8bda2724ed5cd1ec2997a9868617c00ad4f02c325a20a229472a4509db62b0f710fe313c316d8eb1cbac0c367dfa6fbc5740c988747ad2fbbd774704"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3845327214220d4cb2a9ae5bc7547ad0576c9cb2ba1958b08198d9432e787856","proof":"ce0ed16c1d718fcc9aef36f39eee1a3e8200b6d4a0c3d8f6854720f4ddaccb2b8c09bc41c0a34e77a4a796d0fce15d7ed46dedadb6505d2d3e971b991ccbf8471ccf39ea335e3c48b93016ead3cf2b84823d7e3266530210594f0e75af215e528ca1dc0124b968293461022620b70b73ba07ed0a53da37630d030db9a83873334d486a18c7599b8eb58f68c8d2b3460353350f8c70623703f0773e481a15bc0bd0e8a37c8280770ead84cd377c227b9851b92a3b56d68506669f5f3693c7de036f5b0541da74ee3c16b7e6433328e2284f0fb8555453ea7440f6797da26dfb0762c40f45c51eac530bd0be3506a27afbf5c2c464f3fdf494cebfd2b274393a3f9c744a0d865fb55609935003a39d0290aa9e791023e1859ae8fc600ae0db331dba6601efabca73f0906ec6f8400e696bef1151a08db80e8f327185a785906217beaf792dea4d8c03faf0d7f7677ee0c2ac9c00a62ce01b47632932b98ddc3d266000d1d10a56cb99fa8b9a6ce4f4775e44d7e64cc64e5d00507ae191e5348367244c75f32ea1f548e279ba6332191686936a66baf4ab276e3e976a539e0a9b3ab8d83fd7b61ecd71e3100d8dfb9561dd91e57f804be20800de3a929cc5cfaa2c5a38b20f50e9f60f45f43f0f3c7ac061d21d37fb045990d8e1f90d7b3a1792242022ed7217256769dfc7fd1d2391ac7ef9791470a734fd2ce357451e7916df3884627253c4f6c4bc242c7f5725e59a3f33c7137220c854b6724aa56bc4c7644358a0490f45fb1398a13aa8bebbc4e0aa0d42595e5d6d4b7440989a12c4cf6943a287d1fae0613136c14777ae7225ca647ab137096ed3d49b050d91bf660192452a4bf295687d8bd67c3ca05980e57857b816e8fbc8fd0653d8fa871a318a620d57a4998e9031d69dd0449290035ebec72b82c8c4568ade7b522ceb2904ae1602"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7436249e8bcbc9d3a46c05ce7ac7074ae9087de3810e9fe52064094be98a0346","proof":"6e2de7aeafe9e81ee16d10f63176229ea29c5616502e1c4dfd4a4306f16cae067c597f8c8da2002dfe9c9031645997a0d156be4bb9274753280d2f66a6f38e58ac448b07ca142ea99524a5a8db52b8359f250f9b8ef22d9cb0947f464c69c61a5ca826d9699eaf10b8b9e4257e1c432d6ffe76b646668a39e75fa8bec4d5812123ed99f2862580131b0d4d3727394d3b7bdf1fd908ff10df716b10b1a15b0c0cba68e9289e2dde9aa39e2f881e466993f4d5ff1a43499d61cfe3ba61e0a3f9049afb2611d5362679d60cf15cba2e63ccfabdaef0194b3fca32e2b027bdf01a08a859daafba93eba7f6506429009f498eccc092d2b67144b3033a997a83d52c5d92ecb442e7157ece04de22fb95b809145c450f82dca5411e300b021d8b334e4ade8c1b473cb299d4de32a698faae086288ab085db064d15db41e3378b8b7db4dee53688d350c0ed8f2449e6b542857bdf3ed944287bdec0003d26b02dd871e762ed478d0769af47d5663706db4bcccbe970fc82668d3f235eedd2bc78a37134a5600334ed1301944320e3b1629e7f55750fbc629212d1ae324448e41ec1dea0afec04f30dae8ca091254b66778fe6b05342d12e4f26b663901061fb6a84ecc784a6df58436716a8920b48448b27b4d410aa756efae6df4fb8f7bcaf3e3f36e0b6aa514ae8266d857829043430b831e1c85ae824d73921ec60c1f6b2c42fca83ae034a3f671225f12bac7bccbc1e14f1ab723f880d886c73e23efb81f6713f1523cd5201046050b755595ad0bbdffe7090be7a8c54deb26b779ec66972dea652992a7c3ba057e29aca7b1e0b8a828c996cdf8ec55d8adfd7f984c93ba200d3e1302710717e50b2ca227abcc3b84633c9ee073a81580eaff5ef9c936ed16b2ae0945c074d4dab3f7d8ade7144b93876518719034317dc9f3b4b070d658285caa0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"406ce62dcbff51e4f7f2544e6a8e1b5e9157a2678d7c65e872a94308060f0464","proof":"12592dcfdb2b931bf658c8b5c49f4431a5db69cfa3948589cb6b40730ba35d036c8bc6071c2dec51cf99dc772ee6ceadef17177db6226557c89985650583035c9493a54e4aa3178c4bb973606b6b6de3f0031b40f879c179895e3cc8df15b9712ce5813014af13dbb25b10841ca957133f9b13ca8076c2a491d0cadd9a7d22122b90ee0e1d665fe8515deca0032a417c92da36448d0a3bc423dfdb5385e15c036649a2ebd426975a911275c5c8ef9383ffcc4d58d26a669543283701032376045d2d78432256c6f2987d342ddd3d906bdd62f280c1e4e2d14dc3ea063ce4ea08343df2786c0e61defbab46bfc84e027d1779a612f258ce2f9f0e64d0774afd10f891257baa0a1ada9d1dfeb6fa5aeefe64836c3eebe6a1d8dace892b1e34ff24a800b4e20f1206ddc8ae75aee9d574841ccda684c19c89d21f426118fdd10e7b84c36eef8c98a8bfdc483c5155a9ea9926b1940af799fb5a03533ff976fd0a5b8c0befcad54fb684cb7220a273cc287bfb95f2abb446c1d70172023c09b3ab56b8d5b55d41e2baa99ac644470b3c7cb683becc942089654f592ae1aa986eb770c490aa5f9313338dc62f51f5a7f24b960335bc41dda4d755f79216ee5495d94c78b7868e45df66188d7684924e77662c7558647cbe78e66f03c47c5b0d46706a920c7242b9ca41fb38418fb18be351bb9ca76c83c815409dd60951c7c8f6aa4548fcc5ad4a1ddb4e4324cf95368e86b775c7ecdd9fb32ba80499be331f33a03fb834697c39635d2749dd5e5fe943379920fe4db3dd3ab797559ff5ee5794b810163ea8b8958b8f94b903c6941cae0921f14cf5736cc98b3757a8981085e47845b4850c9e935c02b925ba6e1396b1fda2002958ae434e7554a534e98453a8fb098ccda693c6fac9ca2bc64f31e681b4e1fd19b8dd8b9fdcf2b7df232e77c3cb0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d6714514a7af225c03bf77a763115030789c30b3e5faffc43446acd062a22678","proof":"e40262e444091a587f27340d0b339926caf8d332aee4cd7bc2c0e881a02c9122f6332bc395733660187ee30034b1f4a88149b3792a61d0131d06ff02508de641d4179fc99640366ff72a3268b09c5fdf05920db0fb065750ab06632f1a49bc0dee547a7479d2f5b9033f68c050f2386cf3a7272abc68a6deced3da3b933f570cbe3ab705cb682208654d55e72123068ae893dcfb1676e16571eee937b0566b00a3eacc6cb9554fd6111abd8a084012493eeee2faad4a5cd16b9ece265634d305701ab507f9c2131a43b5b6cce891191efe168bf36a2a18ba741b4cd29f25bc0d662be08438b3512a3f135b8322063bbb524edcbaa8aaea6fd6cb3366b54b890f3669a30c67fb625211715d5a7040942194231a3558ec718c16ec0b469b27431b4edf4cae294934898065589cd27c20028c6c8b415498ed4210ea0b2224c71d4ec22be00cc10777fc2d76f611c11f83d0bf73d47fe4f8ef3b6029d940c9176f65d6f16b9a6344c557d81ce1059d177d4ccc20cc1fd2f364ab2f284eb05b4f565346f09608d9c2732683176737c4e47e3d5621428abbc75ec84236fd4837df75685a14a2f5900f63347a4c8b5700ecb64488de288f2b562260445dbcccb808da468c2f368cf3126c120413a7fabfd7631ddaf15ea336d8ab51b080b0545121295c40a3ce200c4865ad9fa179bd471a98a5a16ec61dde3fd30748ea573ac6dbae0e58cf7d6a55de45463230b8de82c3a9fc9baf110aae57629a2febfef20ed95d45a43e13bd032b3914274c349ede8f6eb20c489454bede0620b2aff3d80b73716e9ce67689fa122838419d88b02b3b7fb3dede522d56e4e605c9d49672babf911b0f16553a269eae816ecf515d4d9b5a42139392ddcf8385852b81bf751bcf58026eb8fee32d1b019c9af50522bc1b2dbab73c859adf083b86fe78c8b33ff91102"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"509511e504b0479e727aae74058d0315934e0f129a400784e6ea2928a6921800","proof":"ccafb5b615d8f26b587ff7b9181925abe1b1efa9a84b6ed69fda0ccd2e5cb647b2272eda0d8e765f42af0f3ea8cfb3494125a8ad7895b2f6f8a188adf6dc456514bba106e36c57a6ec6d3d460010c8c6db85541ae4f2ff92e27b6bb7b6159c77a0ddde9ab9629ed10c573c55b32afecc0e9d492a46e58d61487578c098569e7469929b1a2232a62d09ad24c45b752ab02c42c3478a7534799ca7fbf8bba8b60dc9b88f7c2bbefa17f620b5b131c76ea3db3f61a224a82820eb4a19aa86b5460c9ccf6f1bcdc0a5c3c763f9d51982bc834d49348dde21acbe5a9cc6fcd64cfe00b8a0bda598d39d766cdc05c584027225a4693ba92ef2b06d507b56951923d1466863b4e81ba037210d6d09cbeefd6f69acd826872fff8f7861663bd6631c6b37e47edbdab86dccffb8a0b35299923093e83e1b3e62f6877ed3b49b7d1e28d520f0ac3b640aa154aad8c4ae108bdb0b958e4a1a671235d8d7f130a5cf0ff77954ba02506ac25a20ca948d61692da40279dbd3a418176f0886777b633cf14972636e85b38fc10bc00f436a7d0bb0919a29d44612afbcfe59ec59ec0b9a16fa9c7330f785ce13951399b918ec227de244bb54fe23baead71b1e1caa90da71f36007c6890b36f244f45fe0c459b1da49310cef7ab48ba6304f21b49d73b18eddd4212eea34177d21023df351b585c5e4408acf17dd5bf9ff5178ca80381d36537b52b8a87c67117a74e1951c8b104c439c5b31d81e69b4911c06a40af1cf2c860573caf70b9c2658bc25423a64fe6f54d6b58bdbe8e84831310ea39157efa03eb93e6c540c1f52cea83969e6ae6d5e4fab1b002dbcece2f81733048da66ae7d5284c36d23f1a671b57537b6f9e38b13087bdc31331273762b36e6c0b2948d460a8033fc00b131da9cdf91d7612a4f64c4e4554efbb25093cdeca9bc384e2355f1a0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b84a128a2e12425202433ed1f1011fa3402d41a0b5f2504241ddfa9f224c0511","proof":"508bbebb471785be15bf839fa68f723288eef8cfab759b11632d21f760a12740e0a06549d57e36687b383bd5ec8bdb46d9921d4579e7732429168beaf8519157fe95283556acf8d93831f3b514af07cf0f26c3c142fdc5ff385fcaac3cf179665e4e4d7df5d7a72998dadb89b5f54ace56d0b14deae0c15341593bba93e7c35ee08c5a956f932e5bc8aa79b2d5110e63adb46b54aeb566ed08b99682ee99f806a9c60d57c66158628e3f33c82c3908739b2ebea39a30631ed67480c455156d099cc97d20c45c30dece5738b691c26bff38e5183f4821c95a9ddc6f13c188b809f6acd097a515768c2c6ec2cf889c853331349e0a6e80ce5034cf7f122f412e441a40216390fba3eddbfdd53fb75eddeb23f2de6a788f802dfa687b189cca240bf6f6214b1b2a97a3aa3ee492bf9efbec4279c5830b73c31836d2d9def853bd1ce81f48c2f249ea0bec363c69e2c0550b34dbc0628b0648447bf3ee94b921b970b05f9e55af99f5c3150137ef9b29521ca608ed1301088843ec1495d719657753440b0715cb162213c335e7f900f3dbc79db0c232fae7720721277b4155a5e6282ed104283367237141cf26460b28f9a55a20adb9b2cce371e4cbff00e6abee7566d54545134f5592bc067ac1a91c478ba79e4bab7e4794a5d10f4cccf54fe976cce95eade74b9effb087066e9fa65033f55a5777a8714ccbfaea24209b5173012efb601b9952c91b34f7c04eb0348f68776a62701ce8b5a8cce8590b793b8c042ee1971b2d2a8664e9b50baf973820200f31df665db3cfb294f77f694815fc63f4661613795108cc27618dac9cd72042005b256ec826b82b81054ff4557fe032c79684bcdcd65a32bdaa7093c3499aaa58214d09116f754e0c75c6745b6ef70c3d01b775ce3bc16fd13133308780e9f11bfb2628c8d4ed72ea238139e0fb1d05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6208e0f09a76545e5ef60d14c49aef0a8a46fe543a6e9fed70fdc1e2861b0207","proof":"d617909f9d82577b21941757d41f71577810cfdeccd5f90960022734fc3c2f1a0073206f22e66cabb17f273fdcee4add7d8fa2813271a30860060f6e19a448485c3d969a64a388308d5a0d6d29e405f93d6474924f4c9083c68f24f18a68d84c8264b09518a0b4b6c9fd10fea1f76b6a9af2aa84be8cd145add58d48ae064d36493048baab9790646ab7a3fbd3df581d8f804295ddc53ac89ba2745b349ae00ccc3e969cc01557213080d6eed6f188e04d2db70c4e31f9a09ddcca8bb3de110da96f519c78d51130abc7ae1dec5b4ea349204194abfdf5e954cd4dbf9bb3e804ba79d9d9c803f7c9bfec8e136925beb7964b704c3d7dbc349b9b21aac1be2a725660a4d6e629ec00818fc0eb439c2611fc704eca8ff178230164ddad58677130a4f2e14cc7eb2e1dd250e20ad873384c5f1ba5b90ce2a8b07897b096b237a671426d7667c1cb50a59041f99a70f0e16eabfdb04906aad4aec564ff34accda3385c8259c7f3997b6d515b2efa153067fd86da7192fed696ceaa71e62647844f44005e762ea9dc2e74ddd2131b671a42f7ece3b2fd44e6271a5a2209f620023a1d588d4a591ac7e6f856be48fb1d84c18a3b6e6c8168a701dc00d74b3d8f5f6c0aee12af4ed594775d343907c69c6194d36d876b1dae205c73092e8393bda9bc744ae5457a03beeb87b2e655b17b60acd5d941f5de2eb7e7d5675a4ac140ecc56c784b4dc2a099ec8ddfd83ec332a9fa3edc601285487c740dd09c93d243ca6d7c8a42823d8394e03590c5e904fefe6175720f9a6be187c430b5f3cf052ec9be45d4016b996f13be307b096dc4d162a4140387619da4de60be0c194a2045a1b2604192147b29983439e5054bfa34c3bf6019f9d0ca34b1123393f48a1d7da66f0b629e9dbce3ab6c247f1402304b2ecf4ecf28189e80c984270988314e7e0d3702"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"66dd76d915e5c7dcf8dd8291cc9efec9135c9f7d36563c583bed0402d4604875","proof":"94409ab00e98e81db2b47da46278365660640a7d0e5ab606ed808341d1fc8a13762dfcbbb167dba4210a2dd9e2ef8df0b1c7cd05a188b0255f256317cb679850e07bdbeccef88f380ea632d0c3be6263316c929b5f24edad36e8d8395141fe4fb039e300ae5c54d6b0f7cb725b0161d5ddf5a44e89aa468d9ab93657a0dec433b3f78075c8960db904906669c23ff88f7c92259928f701e83435a02f6e653d0220303a409b642baca61f6f1d20c9d1763c89766a7f456c46f7d5c2fa6d791a0c44cccf77a57969877d15363b3c8f0ca952fe84a6126e88dedae3ba3089b2c40c7ca9057cabaec693fcffdf11b4f80ab9d73dad2be98a23a2c6fbc200d399a477f2263204830353e84ca15a3f071e89df23b4ee91d016fd3b77b083f00a5a326062c4d7307eeda90ba3a0abcdccb5b9d66e187bce17a81511aa762eafc15e3470e6822b2524c947ac72ca2ec85b55e28fe56086b6d146f5a924166119386cbf1c8209b688e8dff434267b00a30595f4c28cc97c59c74a843a4de5425b2ce5a252389515de744388a3fbcb17ba6716a7440ca3028f1ec01bc3fab024f592d24b6f1af75c22f55e7b9a1c6f040f1af7101786d845009382a4770b58c3604d27521f3c756934fcc95e18a175ef8ae1b16b1d70c5aba732cef5cdd14a54045790416f7c7b417d8702122c2f6ed88873b303dfc8d77a8c88449a5232fba239f3340d4cc40dcc66bb86e16b587c857cbd403ace4e178d8d48d1d88f13b01250d17ac3232a1d0079e00456ace1a28fc8bb3a6891d96d1b235c82b1e6c6c26f2fd8d412603a4d4a4e5e88097cbac0ef0d9f55ad90604d7f1e5ea2e6db4f7d8c9dd4281551bf367bb115bc1fee198b79e2e1bb154bdc0c3ca0757fe9a8424a7ffd531e490d63999096ac3435e24ff76ba09d277e549dbb764c3f7d57c3d6036a00d648dd0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dc8676f0d5c075a1c0d81b448eb028006efbdaf1effaf09c3245964713063704","proof":"2047540b43d079afefa96f258150c13545b3f4ad004aec1062b0c38331af6f7ff04f9a0ef7f5e36b84d4af0048c8afc5236e7169f10392fc950652605432af38f2d2e8d50c6e274d772449888644191c9ea1ffb8d78d87cfaa88ede1b825970d8ed2d7c77edcf263a8b8b056af800499bc17ad8ec5f5449cad5089351610b074621fd9002ec55cc070d8c2337050e4456a1d6def26c9e0bfc216a55207e4e001228e50e5e37ad3397d9eb059f3d91dee8cd7506b3903f294d002205f69f731051ce781a4f0755991e37ea05e39b36d6deb5eff18421e6f2e7b3c7a3938728a08a6b528e12f23ff48ff99795d7fb5b57d141d0381b7ead2b97d8a2a7db9d62d4b9eb229faaa95705ea4183b808c60b7869dcb719f40771906821e4bc50d392a75124b1b817437ce48330036ede9c1c96a524dc86e3d6beb44993c334a30b25f239c289f52b7539769dcd28622bafc22c7dee75af25841555fdf7748e4a77ddb0780b475d0671ba901dc1c4f0fec757212642c758cbfbb68347e8f43863f453b5392dc1d53cea62977184fbf3c5d3a3c85ea2620d89cd34eafa06ebf883e3c4c092ce7398e15986a2315c180a813a76dd7b967a1a86b3947a8c675cbe8160ada5ee87266a652c9a80fa3e6a9a890a8706782ac21e07c99788352baf66c7ed4e11fba7fef817145d35f081410503f6293d3b4112c00c9536c04b95d2828c94503091a6986690d43dcc4bef1cde5236569299e67b07050a4ad95d9fb61cc52738916204303a7a1d4b1b77c6566da85dec4945630f0a98c4abfd94dd82046b1876c2e687ca72931df41802391dab6ecd1dcf7af6a1587865fa51befc83bb9020065402159f14d61a600937f19d7588c922cffe3da899591cbac63fd33e50cb4b04808ffc751cf0b706609d38cd6214a8db1e8cfbe8d0b7ea059b8c7a88bd78e855d03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"181c8c23d06423bfba3d8df46e6b6fcc3560bdaa51655a9e27e745cea4c5492c","proof":"1894da5838e00913b93710ff6c659c25310a286c8cd66adb1c08c07ec3e9b528c2b43a3989a28dd34bdb55f999f6d810c254bef16843c86bfc744f6a487cc0535ac86de003d0247b0ddd7c6f530ced288c1e52097ff2ceb800393e195352d95872ee4180bdf3d9e5b66e3b82694311fe001807afde3a307bd08ea19b3427ae165fa0e9ebe4f83038b07954f7ed33b6b32065e9b50d0d4fe2111c9364e2e3df05e1846ae72abf8df73a7fd2ec403126adc589416ff5888f68a3501e4e3bbd4d0dbe483688dc78fa51b030dc9c821606c5879f282fecabe6a1f09783372c81580a7224c45030331958dcdb6a613a0781a669b8d1551285328ca79d1894d1007f168425288a21b9472392093c9fd9e01b57596d2b020590ce61a85dd150572e7e6634ba8bb56cdbcbfef834b739645520c22ad4fd414883b679e70da01683e9e33026fe62c6ec01705c2d1bab55eda0891ccff5f62bbda27aa11663ea3a75660668f665f6b5c69f29d29ef28a607961bbffe62dd13df159dc4f33532577696bbc43f64c0e913f78b7549542fc5035fa4651538cfc4472ce17891df73fcec38aa97e70d8cf72d874d45b4536542a3444891baf77ab51b4cc5b30fb8d045e8c02d460b2889ced0b0e011a28c780c7d02b21e8a9f0517431e2b52bf757db5ede5aad60baf95ffc817cda08c623c37757e2c49fc33f40e2d610f3738bcf08a42bd8400a3416da96010792b3a9832a58355b0a7658710bcb6ddca25bc58ab258430b4a0e42ebfff2345398fb7373774bd114645131c95916ae6a752949318566c363eb7e78db9a7ae4d12c08691c13ac1da78a47583384e853d0d6884c574a116c525a32ebdfac9a70e51bf2aa13df09f112abd43bb3929ddbcd0620ad946a9bf579c00b4f469ec9d0aff7cb723c5b0b8c47eddcf2f731cd71a67254d8433acdc389db0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"76944a6efdf06cb4542237b91268ba55e6ceccca40f70a9c5e822966d364bd11","proof":"9a4022369f2fefcc5c77820760a267c63e7b93723769f4beac3eca976b6520167c1d282e4cc2a232fe4ec8be70d469c91ea65e864fdd9e11dff8ad8544190a75fecb1072e4681938b5384746188174227c631c64d1e84a99fd8ddc195cc6db49d0c6e369e9a9a431f8651514780b1f73963c1132b2d9342ffd58fee76fd23d743b00a338c3c787b297321adfb8e23f8211d7df554a020000db13d0aa7eae050e0063c63679ae2f9e41cfe2fd1707b28fbf23d391c10f18e48340ef62d9fa0508bda6f3b4a30f4c9aaf2796cc7110364d391dfda190f09d1c8c3e101ffee2fb0016534fd95feb118432bb6c0aee843583222609ad1b0f1b248d86995fa33dfb5e58584898e1baeecc5698150a3f7bf4d78917ffe6c4697868862949f6cccfe6518a8b72c1becc7568f833d916605bc3ec054d5cfd06f669a725431a0cf1e436151c0b722c3de426ab7d0cec771e24a4d79ed186c8c4f6f468e949eb214e063b6b24d7bcd8c2a9489d8962d0aa70fe140307ce39ab91646226a7a85c5e0f221315aab9d68f35a3756e8a1787abdfcb5a145cad61f719174e826a3028d116b0303d145220925ae30730efddb05606dbfbc7515fba5c72b1e0b798482363d65074594ec226d926f8316f351e05179decec10db7936df9ffd3210a1af355311502d766e88465755cedba9f7a9716ad7de07b845da0cdadda1d2595b84b2e2eaf1b0180834a2f0bf8b450daadd935830d5925b8b9e3a25cc95c4ba25753de56577d92556ba98e014a7b171da14118a2f4691944e14b7324067ccb06dc4c455e4c03b4a3e8ea3658859468f01bf39fd3a8e857bd7ff6bcf319f6eb6f453d6f05532d774844232144d8861ba207ae5a3f9618102994258811e1ea35e0765cb90ee27910f19d453f2ae348fb4232a8b377f1dcb1f6cfde5093c8eb945e2526e4c8c8f9201"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0c987e33119c8d512a1c77e49fc12e79a03af501abd79711246df0f8d5223b7f","proof":"6a6496ec2ef82315292096bc1ea083b778a63292f8d83cfbdc7434d0fb697536648cf90b66807ef74035a94e6bd4ce9bae711d3cd6b1dfb1947cdf6f7a6c4a5f2ef9fc70c28d10adf900b79b6e1efe5b558d5125c9ae5820c458381d4401242a7e5e6a7ed0747e2a814ea7cde32abb0d4baaecc0023dfa651e8855aa77f3e76aa3ca89a79f55e3239ad4105a79cc59d9feb0574dff3a6d6ea17d344651b7020870672f09ea6adf4852462a2a13fbacd4269fd1ce63e9ac9e5326acc9bb0d950dbe613e33d501fb23d809aa9d34c9eed13e0a3e08fdae0db3503e02fe98590e09da4776304d161d26ba3517e72a2bca5a71cef6265c495be564949a5913cc3e41b669a196853459af07eaf784e547324c7fe141f36f8a40fcf6ff10493090d9027003925150345e86aa387999936ff011ec54b64a96af329ce749f215029edc651af6594c2906767589dd3efe525f4bc4ceb9a936449c2edc68e85c1dfbdf531a642ee3426617e87001c78e4bc8e7836ae7c5a3f034772ef06606d5d9d62d8b6120324ae0ddc5711f1cad922a65fd3c93264205809c4ec1548399b3ac96d7972650fa40e2d0c7d987fde443cef0f9c1d7b801cf0764583e08eb6eba946183fb3aa6319affb0aea08d1a93994bc15d112b5a0dacdea295bcd5fb6499a36d7c48265ad5a0a04ed8f4c63c3414e738f3bd96a5ff501a68fe4e69532bf7513374a956b041ad220742794a254ee19ee2aa120bc3f658b592c30a1e9460f6cc7e6a172336b427b490ff7dc9df97b1d73c9e37b4abec152e4d9bea67a968e131902e3f0428b77ec7fe33caa64f2e4d35562c940ba76e8bb52dcf9f956ce5fa42df40f019620dbc2e28e28d2d2f6c00d55bdcb02154054642b74acfc3f3516fb3086c1c09c5e116c08bf251d74c96be6408f15d0d91de313c192cb59359671769b51e9209"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3e1208001a4724cf355fac0d16db5b1318077409d9d6247315b83a0010f58d11","proof":"7497e02f38f14e561ead3100c30520e0467913610831a6470a48e15390ba4c7e2edbbafdc2fc429f792cd1294a068d484d6130e3b5f74d50d881f3f80d1b242a8cfbb02e083b8024f8124df2dd4b4d2eb3addc829f2b4e94ba69e2743fbcce3ddef9113f1bbbabd7a4fd8227907648b3355686915f9ceb87bdb85ee1fe48d24abc719a7a9feb872efd676ef2af5a820d4b94e97c593bef36504528d211e4e70c0cb96957e023b028ca49ab855a37be897c2b2e9b6a655e4f32d4073cbac2b0031070f9946f1550b170478d878aa9b6ce3a8170e51a3d457e0741d7f022507b0adeb7d65319526ed831c699fd5b748e192e0486b840def14ced217c45dc705b5b1442b78b72e6aab771886086da01c6812df64ec9764936d51142f260e023a908825f2657d8c3e74128263c530b987a707a0ea640e920d477e698b686ce375769b4904a93880bf0e682667a4fa92178cdaac36291269c6665d9e4f74b2a025575d2a27cda2cc22e153d7fbf7b52f693658a765c33f9dff1f83898307273ca112140174ca637b4fb44eb08ab08fb8533c26c8fe718dd15bf2aebf8c73fee94480c90a770e5ab5fa7ca16ac65c4aa725100b765ace0868304bded3f709dfbd8e02df68715ca1059ccdda2505fec529da232939bc3e97c3a804a78cdb3a82b675510769b9e0e3c1028cf1ff929f178929a1c84fb43904a27e55456cc5a1b3eda615814ebca9fbdcd457fd2310d72da95fcc1937a7446675e6e3fa9e3d359017a063a2adee26c9b8add5ac4d82d6c6fab8b8a69d9827ca24cd738c0d9bf02709d4d423ec70edfe3fad8e64a07df450ba56990db507f9c377e8b2c0ddcc43447311f1c777bfbc35bb762dd2c83c49c5a5ec2264bb472d9d47db730a3b7d8fefda785016bd0bd05d1699ee022856f79d9cc8f22cbe1b2500e2a104d6c1078d33fbc9602"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c6e4002c50c15f31c3caada6165237c6015df5d92890786ea9ab48a11b3d272f","proof":"fa732c7b2a49ae571d98abf8f69d73dd11d306933f7c6a1be6ca20a62287b60f1a258969f790618454377942155e38ce4dc62232e20a4e2460c78acf4345f064f8715a70b6f506f48a5d6ed4f695522f42ab9a2c5f1b286acdb49d9427513d7244831b4a2f4a0ba2b997204fb2f0b43d15e31bd770950c96d51ddea5f03d1b58d70b10b8fa508065c7f97900baed7b1e626446146c228c30a2e12cf69fa9d00a8a7d675a95a46b90faf93eddb91a16eb278168668c98cfa6492e91cd34a5850eec5d1672dcba4d6905263047840b95c4cb96dd585a2c62a9ff2a4d6f34a5d6002400de51261a616386f380d7d93f5a45982f4f1d320ac3ff4622be6267fe9705f2df04e85ef520bdc5b6eacb7f3627726093896f624091d31de7d4b7296f835cc6c3605fdedb85f3988a3adda2c615df263659777535aaa1d42a0a0280456b20ac779d36ad96d7c220fbc540e4dc8462bdf0d836961feaf96d38e817c6f2315b8e17557ebf2240497f67735e5077686cfd6b9e27dd13bdc57eadbb438536ba545e61ffac48f8e46109e80e21c478171f75b9d14e49ebc6f57225b6259956d16e42cb0b59861e6cd84dd57ed0a727f84a0649900470a95c53086c15c9ea14103298f874484ddc5f76767ff4a2683c773762d6210d3d87653d9d395e20b0668949aedbdf551ce5d80bc9782aaa44570af673126079a240f2c788a35b7c74a2bd245051de1334a6534e2a9826d08368b30232c3fff58b6b29c0f97b222f5d30da2cd4fa92bf889a39a74335bdf1fdb66ac6929d4d914b9328291906e7e4458d777b12b8c5a8d1dd661edecdaf0e1abf6c5162af7438d3a47b6144d05cd020d128364e642f710ef904c35322695cad7e999ab2156aa3f5326dde2429b3d8826f71031393a08ea5f175ad06e598766cc242db3307843e446c74a64b71a13c94a95c05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"527e57cb31a4e3e505a4cbc8861ec3fadbcee207dc7b8994dea28d78ef7ecd07","proof":"c2d6c93eebd1bede11b424bbfa935452124c1b80f3ab0c6e6622e1fe1a1aa0662e2182dff05e7be8ae2b94b4ea76233a9a5fb6acdeca39c938a9522af9b316175c5b3939cbd9fda1739a6f2046b6ecc6c3715d3156d7995ee8c8361ec7dd53790e47114717cf4452b42ac7c9ee4cc50c5233977cdde875aebc113668c250a30f8a8c7ac2f1175179398c1c270e7bace3ed6ae39a456779dd6c9cbe55733e5d0fac98e3e397d56636c2c70e65a882abe30e1d5ba23962b52bd2834dc2c14d8604ff92005210ed62a22a3485d417d8d506520536408bdfab02e2e3544ebc37320ad00aafc2bd934ab01344c095111c9b864c1100f71a973a6adfdc6db807abc526a69711acdc2bac5db58ffb4189f9848cd3a313dc27278ea10ec6e0a045a1956c66f3532a20f2995a7afa6ccb3b8e00f909692057ed2147bb08ae526263052d56b617d9a5d76c516119c82926dead4ae96ddf8ea69a151febb86b26160322a952181c19c8ec7f6b081296e9386dcb1284b190e6aa3be95ed84fb82994512fb81f70f86a1ed78dfd794bce39ce6dc293d745f040e68475e26ad8a964b4c28d2c1f82b8b9bcafbea340cea9dbcefb914f491efa02b161da286450d8715bdb75d93c42e92c7b9e352299af8e60c2b0f3b91055fdc2976916ce93057e6ab36783d52588c0d454cdfab997a82bf35e05b8f9460253fabf019d5e8d3f023f738e6ddf3114d02a3075af22644df9681e74aa51d5c467adf334e562465c36614f74c04a14a8dc4410c570bce1eb905aabbe1df4f0a67311770acee72fe2730b82e646d4225665d83cec332a2b79101d07a1677a22335bf86ce8d00d7a17112138abdc90437aaeac1750f99703f369dacc4720477112a35277f51e25bd5ee1268a8f9f850702e689f9d8c746fc4a6a1cbe8007919b6079f1b23ea7598e4ef87e2749d51a03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e65929795690c7dbda33774016ccb0d7aa4f989a12e9c8a360e0e180c2d58928","proof":"60f25d00a6c6226770a46513dc9b2238072bb76f749dd0fd17971f1eb30ccf2cdea8b0f657ff937001232c574df1001f118a5710a0fd3e19ffe6c6780ad2d9723ef9dc423207de7f028318598c8f7914b6513e3158386030a4c19479fda6531a4eed156687a1c00587f012b6ac171ecd44d85ac73fa0a2a3c0e4d89fa7089646b36ad76c6ad168e42f1b9a5ef57a20b610c5baba430e5a0c1d5bc58b816bac0a50cddc14e01ee3fe24f0b010e429afadbb5785ad190176389bbea49a5b4ac30fa9fe5b62aeba0721ee17b71f47a72e41d17e6568de7d9209e63b9930bfb2b100ecea9895d4c0992c83ef0e695de32e5b9e2366d6c6ce502cd6d9ffac64b9c407f67b235effbc637447660197b0ef9cfd2a6ded1e47913f56bde2d5bff365d352ec82483224c0cddb4623572cf401469fd021912e68ee12e922f0c9c80688fc417ca97ff4ec39e9a5123eb57be34b89456680b93318c4ff5b1baafa8fda3d970a021bc6af0327641ddd022a637971e2224a205519a489e2730a84989281e9fb133608f30cb88e7a5e2a63cf98e9643ad044408111d146092fe69a5640ba44cb3a0206b3199b6fe5d618d8bda0aac0afcf0e4785d990b240399e2b995b409c57395ed27948291ae040530f593e7864489c9e86d11f9d2b3416fc05683682e6454db6394fee1320f1f021b90d8271e078afe5fbcc5f5c313af8ce7052b2000b42365692df35abeb2a7d656ecad5282f3e1d27f5c806789764f93a853f534e4e9f1474234d8ecd5f28dcdca51bbc2b51281fd81f99bf8986f6b7e5638453475ec14424f42ace8213ca0c7f22633d2bb4977483eccfc1f0fc1fb0d8bd8909da6a0d29d2a852ed78dd22188259396c7396cad650e1c6a1ebe6502ea9276f5ae439c602974438af52f055c8e22a97193f1fc3126222d12aaa2d4ff6095577ab68867700"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"042513294e72bddee33f5b24272b7277ab2df66b07dd5b5f8ce69de865096f0b","proof":"fecbd69326ddec58992caf263c9f42d1a507ad7b0b0abf6e6251bd7def797261b2bea30a7a916f1cbea6117ff6c8005076e592969150eeec5269aa8c2d12cc6a681dce113807136a3818137e64447526f9593d1549da6d4bbad35de24ad8ee27dae4ea2b9030ce9a507df5010102e6bdc000c8240d3122e09f65daa5b171862b30c782b0301e4dea8752ee120547916df9bd8b42dd58512b9d92d562d7bf1609a082efa8467c03fac36a598ca372d0d1b6f70bafb2b75535330597b75ccc1100cc730e3bc7dad0e47b7ced9bbc004ab0b98968435a6198dddf85fe33e55f7a0b4a5909e35914656584cdb632261b46a45c0c8351c08f7890186fee794ce97555b8ee79320d35e62f93c323621bfba14082766c33d036a89e52305b496c892a71de86a5316d12f45dc75347682ceeac081c58b7fda032c45fdf537ec65e07480ef8978e38b631e3ce07a70b559e1089caf8fa73af9dfadc16e44bb2f9d72a695ec8702b1469083025e52e90aade3f3263cc2dad463de5940ef668078e4147e45964ae4b84ede08e845618bb92e499b7412df9524b4c22c050931e0c71a4b4640ec8f187dd5c9fb95646633c525736300f5c263a45cde2f785b6ba9961e4b9e537babe6dbfdabff5a531dd77ad201ee88e5d12340e07f2f9fdb442fdcba8141a6b0803120c8813120327dd4f8bc2dc6230341a9d042be5a5e77c83140588bf4f5fce5c7c2850025a8ab42e8f453f6268471761d0f1a03f7291eec098e8069c6826e081319b3686cf9224c908b32489dfd157a9ac6ea1c4fdcc51f59b76f8d9be10f2406b3bd0986bdad5d6e5a78def0182fdd514a94eb5b3fdef7f125331945312013b865c0981a2c0ad2c3bf57ef073c1934e1852107a80165cb87626b7545e0e13fdf748a1960cb86734a95268e277ac6aa283bd9196afbeb61e2a4041632d0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b03720fcbbc271be0255d5b0d0aee339122b1bcf9684486a88049f8dd15dc328","proof":"7c8ed376666b01d5cd9849113188098d7d35d33f59e69af9485e9952af2fc60be0ba35ce2b215e0dffd95d79d84c2a49469243a5a19ac12e031f801790595c52321489ab8cf7afdbd549313033bd5390d466d0d56a8a6a9d670d38519e313b4024b5201980d0b043c353013af20df8ea28f33cc23e582dc41e6b83960acc6a202986db96de76e07b4856d039311ce30b3e6c3849502390feddaae0c804dd350499ca5a7bbf589f032f0f01352b03ee8fdb9c0abd86264234e6f10d0d93533c083ef9b1764668dd5f3801e543e121d716875b4d4cdd8119f5de44c0cc11e26c0f8ebfd9df00936f80cae0d0222b1f69b82529418c4c8d8ac2b2aac475274d6168526160f72bc6f4e469ae724b25ac7fdc4445e2e2a0adba194f94b28cc9ef8319689e719acfd65dcd13d266eba69910024a08ad983129f4531bca4ce1976e672460c64be086a19bdcf038393e22f7acf36b8dae52efe4c73dbcc2e697cc108275601193e4e26ef77fbb8a73601a600c2e5983cd67089c319400ee23ae512709402aa8a651216388bf964ecf8d94380f6cb10df6e0cba369b4e8c4f6db9cb5233336ab9e27312ace43774b949545bdd861b6f9933d19882c1ba09405d420101a398cad59451d0728d2efcdf092ca47b068be9769ee9ea63ddf3b1ef1e091a5d7495ec7951af143c2120da891c539dba92b87dabea51ebd4256a1bcb39c17f5ea669c45da9b22538f8cf33bd5cdb60010fa0c90989cfb12d96a72ec5c38aaf64c5d040adcc6e9bea9774c372cdd6dc94318b91d707bee394d7da7c915f1c62b3d683e980256b6f2b18e29efa053c9268859b1d453d08ab55203d2fe15e0f79fc94b15eea996dbb6e60bf561e511b1aad5d528ed8679abdb6bec3198cb1689c0ab0faf59fb7e587c9814e2571ac12cc2b2ab8c24f39162de5a6b5d5aeec58d9fcc08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2cc5116845fa03628bfc79f8027875451458d048218140bebd2d117f8921a361","proof":"823d5cf589a108d54484fcafad447eb2df748bf3f680ce597f92fd87433b487ff019d26980803c74a8e9ee75bf587367f9588c8f44ef34ad012e95499220cb0b8ca55dffcbdd4debaa0ac355e78b65766fec743f8ab800e353dbb655d0a3456bda29e6078365a9551295e84405dd438b3ad45f594a48e99635091188f712992f909860a08867fe6712cbef74fd84238f9f1b9d4c72744f39fb2b5c9540942c0bade83175a85d7539b947f4c812b208c8a97e7e489fb580189c83f9fa3d7f0600273dee28fcfc599251faa4e839f135ea850bc1377bf2c590e1ff80c8d3bf7007328d511a5bc6bed5d0a125ecc05127b6b48361d575ede0c0bd891505ad7db4726c0b87f9d04380ad225f6ac153a2206df1b4f6f3d72797a860346eabc4da3f35429e7f6134624bbfe7fc27b7c0badb4f377785a6d3dd1b1b914263879cdb13113a1cbda49be780289b199818dcdf5afdbf67bc6e2c3e2f402a3d0956ed238647c213a731c5dda17f52e600726a8ce61a147e32091f6bfae3f2a290c937278c7e5cd149bdb0939470aaa212d44f30896ec944e4ea47b1e84164e2c34a88becb08e8e0e1bd10502515aa82f7d92e96e78b49ed7a93c937ae898a78d7495590454f0ede7797fce9e28d2914a22e8ba1a08375c38b7c6a9f9fabe09be880d3cdcb669c0ca9ee3c1641b585995c06ff1b2f74c1120fdb65316af2f0d82069bb1b872c5ce78f73d61b2ae89142be916a59e8a152610544902e23051711ef3d41c47941c61e6be940cb488a5d2807e154674f2844e59c82837bd3e42a1210068cc6d505223354c9a7b779f6f3208edaf9e6e82077e947207eefc2031b321c30fa4eb96b377017ce515500d6e3eccf9b7abddd8ffd4638ab9e7b28e5ff9d8dacdd9d0b06c31a5c5c3efd0030abdd0f0ade46badb1f8894119226aa6224ea491016c13f07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d48c8acdfdcc709710bec55c738f81f5d8c9d4208b0fec50736a29b3074c9658","proof":"50ff7abf49c2d027a4b427baa5f702476d58ae38bc3bff1e15df906c1194c6140819f318dc64aa0b9a6e2367135b5c3a6aa36c55507f62f3f95d0a6d3172c83e64b4cf85709fe040f30a31085ef053545d49ab450fd0922c0f2d3a9e287f8942be1776159e169765260bbf9b3fff50db370a29e5e320d9c544a472ad1071b04744abffa5f2ce39c7aa9ebb0fdba2e484a6a512207c08f8231a25371f784c7d07a1b721a289cd19c985d4396325b1c51bbe5efca79c97f2a355058be722db2701c4b8eec4cef3d3c33bcf12d279eaf6c9a77c2a82a80fbd0efd16ef4cd2c0e805feae509dad2f44997e0e02700299936f8642d0626ccc9ad14bfa05b10a37fd6f809879b3ea8d13416dd870ad2e5362264e6ca9d0b9d08f40e292ed6f1df5fd5d32ea1167888eaebb79491bbc4f747939eeb09e311b8ce5035bc2ec886552f777f6172927572b773910c68507eef6df6f811179df3ef3165b0a286cd28e37f362aca464f3320e3e6facaa3dc05f11749e99f9e0206e97ff1c60c7e7f92b3f9d5aa2a0b65c5ef857f9b4dde5a3c01424d6ba270c510de7689e12cf27d67bac37522e92ccf25a174ff70a624aa96b1b750c5c8a643347add08e260455c21a130420087e252428cb38045b834f407cea3e6d3cce628dfd7d1d2e4cb99d5d0620bf296ef4a49324688566cd28bc1771c08d7de54f25e8fa2e977de7c854ecdcdacb113a08f0e27352fe3e456618ed1e569e3f0573aaac74376db02628b28aafcb432de667cfe9a0a3a95d058ba5c2365b97d7947ab95dfaf1be4d0ba5b9c1983037139eae5d0186ad6810020ccac7433f6f1b0a18c60eea4919ea8a17fc192ca27e27afcd01e37ca57b56406a271b7d816a3b40dc5add2ce9f93410542a37e0e5c207bd83ef20311c64bbad6c7506cfb2269ada8b75a0685db65aa2022c6c2e77ec0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9030a1cc26556ccc54dd0dc1cd34c3c5293ecc233b6e90eee9d0bd070c02e916","proof":"5c9d5029c84671540e8421f8ddbe8b7780d0c7505c85a290f44fdb6932761b38b8b0035752b5ba7df878aeced0b9a01e0dd0b7781d3d4c53c7fe9d4221e8bf7724cd9a6353aaea7c278ee8509f27dee3c185ccda6d4326e862507f23e9e4783d103d35fbcd68577cee2f61065750070b21fe2f33e2d9aadb035a771456b56649c63a15219f59c45545361083d615b657222c3652855ab1af5efebb73bdaf46075e793526151da001ba191eb8c9bae2f85cb233c8ff6b1cb2eeef9890e700e70fe81588284a459c372f06b350faf2ef257aa910599a62c7944263d002257a6706d8c745e0ef89decb17b7d0f410e06f0fa55ffd151d29855d69c522b74073fb65169a2932634128008982dc6c0ace13ce22d4a60bad76f5a3bb28b167b3ba1b293cac16b2919cbbfdfa74fded45e9c83f93ff27c06cdf1347fb54884efd71ff72ac82a5041c78cc964e54b47f25e96fc4e7f55b583f1307d40cf5c87208cd474722e5ce846081b2ffb60f201f22d4e778f655764bbe3494133e0a774fc4189546c649f945daf14780e79fe1b067e34843c88d6ff8f7ce15625d8c6d02d07ae34ef2b8e66ac69e10261a8ee947e8258c646e73dbf28b4c25418160a48616aaec1dc453cba5ebb31d8c831f2421a1fd1cbe34d15dec674f2d36f3c9b88374ce1c28daa36b4c17b0c974f72435ddfdd1761cd3cc0a0f00f4fa28916f885f97dc042ec2ad1c5b34822a37bf36cefd088ba25e3e1ae202f3338188e1998a5077e42d4b74268e28ae7108af3228c386216c90f894e6e185e8f44ae57e3e5562daaf673fa821a4ba218de8e16dfaef88c282b5fd976b3baafde56c2fef81afe4e1a0105a02e965eaf737fe625282ab1d37036d8ea6bb7ef7e0737af913c175044645cc07a799146a09225a80e2652028960d57c0b1e0aeca6e63539667901f4ff7d6640f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"545b610f977382c7c7b835ca25e4b033bba02df09de8954899e291cd4bc0ff56","proof":"9e936548c785357f93afcf1220606a1d36bfadea1afe4eab6316112336ddc865d665f792f92b4acd05a96f03ab4c6241e1f9502db39caf9b38802f9000fb1822ae4cc90558ce6135549700e79d2f03f291301dd3bc9426324d8d9b040afe6874c84d734cfa41301b052c00e3a7e198b1f27a6a8ed810449b7ae50f5345e760326b3906a11de7c95fba38b6c7bc9294ca43b82a516a4f2852167857f550e4c606ca938b22c7712a420f9a51bb01371de6ae07b3eaa4a47cbacebef95d146ae8075e55cc0812f317bce4e24afd288eba3d13e9c8f5b8fa36b6253e9a2d6c3c4a0a7a11ba34191607e0507ff9f1dba182384c63dc0664da0bbfd206a8b0aa6f376acc61b86f511e62c9efad276b0cbe237083e27b56ef53e2c1b6a704dbe81c7934304049556aa22e35b6b1e18ed1587413cfc35bff0f76f182622f27cb66856a32a055c3cb1d6e7bbec50c579f09c5065268754b8b88def8c8348cb93564268851767709adc248a1375972f44abe3b06bb7e491f3580af48dbc671f3e19d828c4318adf62bfc231c0b39036dcabf31177266cacd341831d0446c6c749266dd146b162901037195bc3c48b95f76cfa882195751ec7eae55a9eb37892b7549abd67028597477998232036bf7480bf91bd43b8a8504e2caeb83f6939e5eb4502c3b58105a7f181b65ed553a1523b0ef133c22376eb8ceffaef3ef97becb2290b0353a4a3cce37b4e0bd2fd6711643bdfb1f70c4e2a99b653188e914b154186e7dd403023ae64b89575fbd80b6061fbcf18d2c668bb4c51cd0ac2984f289deb7a40e4e70818390b33548268ef55128ef604f0f093d350cb5a66688ad24edffcf56047aacf7f1b854e8c5c28b31d10da4e08ae67350d2de55e9f59b4557f8e77099b80b9cfa7427467f5441d826b34e3c27c162756b21d65c2fcad93b0f9670aaf23402"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4250d5aebcd0a40057207e436c31154786f91a53590998ebc0bcb8f5f7b91244","proof":"0c99e118c62741f6dd049874dafdce85a01684d25b74aa971ecca591cb535d60c249f049a0e79a96f7c019dfbbb004d8887534dc3be57bf636a635762c0b0422662484918f39da5e063179f7cc573a7ddcd90104eca1f9af3123037952372f6b14aa0578b761bc2f7ae782772d1374fed9186dee3572bfb9ef2d4387c158f176caaa58956769737a26c7585fe602351f64ca99871eb35a609ef674ab82c0e00bc0376bacdba1489ccfe6a03b319225d7501e2b1779cbbfc131a699d381387e0286c503ac5f9f7acfe7603b739286dc0a79ef09e19c8a900bf741b9c760a9ee0c7097d30fe6fc79929e9cb64a1428e384e16c449a6d52bf293ab845e16be6366e6c65b8ea5d3f3f7176350f42ac3a861cdca436281f10ab21fec71f63d1e2896960114ce828a481d491011d0cc0687bc3c1a0e53610e63222e60f39214708026a78799a0524955bedbc153ba8c57c49211d222fbb9cd5a17cee28eaf69e132f1388d3e01dd4898a5a4bb5b6ab8e4fc4e2eb2176782dcc7d9f5f458b291a9ccf5830749639f881ee86738a444b7448f29b8c79a6e76a3dd2c3f6a19f469a2bff5d8cfb41dee174c328a665a85260bc5cd39cf6c9fd442c62990a35d157b2e70d11387aeef4ef9476c04b4f5c74682f56aee4afe4b8ddcabb42d4e5bc37107a222276aa8e2fb18b6ad5960bb12ae0cff73aeafc01bba849ee32983aa0fa51135d51bc3201942ffeb4b42c0b120b8bd286a499ae88c194e84471687032418236a15c04115d0f08e8a01b05780b35a41444eee239b1d47ed04ef8a98bdff72809ff02f63036025f456f3e4c8bee018ef23f24d26173107ab616b0693621cab5c88313c5e78eccc769bf688bb20f8e9ab61c547e63d34c8cc4446480c830f38ddfcf0c55051c0c1316ffb9d308bb6a38f2ea8cf6044460d41fbe306996a008fb665e07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"989ce39a04cf804ee6382f1f2dd2799143cd8de5746ab36cee7fd55f52677740","proof":"5239a866653b3d5e37a6321496a1ecceace202e1447343dc6dac53a5eb2156389ed30ab8c2f20a2fbed5581aea09517c53028b0fe3a4c29eb4a8557397f3f7192cc613355d26da705fb762367c131b5e52815e826b714bbdf450c2ed8a1c5114b6274ca7f8ed5fe26a5a6939dc546d7de25a2355263397a7b59c235502eb1052082c918677fe39f060937b4d8b6c5ea2309b7f54a7001e325918e28cd80f9a0480c7295ea2fb7cbce2a3f45f96269e9101ddc7645c748838788763a2e1a4d70610f20141bde59cf5d1ec71019d3d0143199c7c3322b00410b1f6255c3db1f5016412954be38959a77edb0c61e458acb11e4e9f1b9cd6500668e9665355b2816368f60d2c7dc0041b43c008200df2cff315071e788f954726817e8abd0c9668289254723ae7906bcc38c6fe9f8df47e64e4e63b31c92c7f21cc6afdd4a18f013444b616b11fcb62ae80c581e913c12c6c70678206a2706edfed2a0375aa86190928377cf2c9b26e9fa8870fbc1ce4cfad08015d1d5ad6f60eaeb7ce319666b823e8d9276304f70814d0187ef877fb33eaa620bfe79f9a719df94904d6efc1b060a4e00c883e4c5c7297ee6ded60a9e554e4ee65a9f4b00341f93f2b42619a440620395253768717949ae8eec6d7a5825ae4e901b5d4c1065b4575706fdee62d13cce9d56b4ff4efe032f9ba0704e61759c2e875246d7ea0feb7d10d34d4a92743400c5df9709375e3354dfeb491c3281431ee0fd6fb08bcc7eec18f713a60421fba93bc477f97f0104cff718623eae44c9400aa5d51cf8cc91c97787e0372f41d4892bdb0458e9e008e95ff060b0762ef3ce40df76488d4c8068da82a7a01710a1b7943a042d689d7049160140b4fce2931fa0f3c045d1199ff2efec8c87c1a00e92a52c673b2731e45c3ae481d914e50b1982f12db385068645dcdb5a15f580c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2448c044a5bff55c6e5fc23fefd2329f3a195e6cda2ea6117b7a78008b53fe61","proof":"10337e8c8bae85e085a828dade5a2f12652faa679564295e1db339e575a10106764713aa85c8803650a03ccab2f754c635f13bb4aca7283efdb24ae47481781b6c862d86d83a849e6d27a04e2c40732c2a7f33969219ea4a7dd7261affa82f504cfff9b0f3b8ed641734d6c0d5d4b8e58edee030f8d11fb31136d750d16aea33284345829e21b4b6260e4ab3c1f1bb6eb992cef73e9b688f1b2c0558ffd1b4001f21394a40ee7ea356035c490337684135a58643e979ae42cbbe7ffaf7432b073591f0edfa573ea5f45374e433dd18d0043bf4cfaabeef203f007e1e7f12c70d80496be4c9161177a6500972c5bca2171f4ca4dbc2564aff339f4f3974dad43e1ce07a22c7c0bf98adb72f4da6f6e5d459f6124f5efd20c4ff69afb8f92c574dd2479f944473af710cc3783f6205cb27b4470f818c2722c477a74591abb2c06fde7f5f82cab2ef2053a0066f87ed601d293b6df79d94f25b639bea4d115d352f429c29f1baa61953c2e3b1f875d682024c7a35ca565d17dc2beaccdb9cbc3c0916fa841e8f6eccfbd57310d83d57685fb299af7cc166d5b50d1bb215c704e925f867d63de3372d01d00d563e44d3f4474c70804296f62c565904d603e3f00d2430bb0605b5b7967bc0f7f1569dbc4c28ffd3fd6591bd9a4dbe42c77858dfce30fe545a24ad5e3727b102fd16d7b841ed64bd32f7bd612f86eaa4131eb1a4be162456ca71d18ee3a7cb84d669cbc1fb1c7723f787eebb48559f7a162637ed5950168e9c03cae7223c5ec790ae7f80f90f15d832bbc1f13ba5731c96fdf2c1cf3c0ac75d3030aaea5db065ab55761caafa3255123aa5d265ebcac140c84c598233f7fb4232e51555fa9dbcbcd5bec664746d3ed206d6636a84a826f1cfcc422e0628f0ac18be663f50aafd4020f6aa9950e848924e9ec3cea8c840d82400a2fd0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cc5cefac5aa21a270070efdebe8afec5b056bf2d878c40709116db793539905f","proof":"42d62f1250288bb6faa8e44d091a7cd56bf4a3b198582c3c59909e2fac312d3f9c00810fd9f47429982c8b305a6c3ee5f6198237aef68bce59d1e92140ccc300de788193c097be043979172ff364831224628d1fb7166c665a665146e34c867790f83c48222dcd72b3b1178d13dbcedec24bd4a7073b285670c94d9944876a7c0218ad2ccac0d5b2daa9e2ceede7b4e064c2e8d1c97849ec48406846542a620c8760c2c4c9a334c65ded591237a94eac7ecf6e1600f1ca38c52208df7e3038067309c580fb89072f88a20c043799867a67f8097490068688ce9e1aa617f2bd048adcbba836aa1cf7c3e627e7bde5dc114dbe11ebf14975f94d38794969309f02a296a79a9fe91a5e348df645c82d2d81035edd35a61468778868ebc70855422dba6974b4d34ead71beb2e4d4d4a4fe0a78968d930560899617c1b4a4a7541f55acfab9fca9025e7b54e258f9657e942b0b68bac007b4a72651b00f4242db2858b4dd450c61b5d82e62cbc877d25787493fa8ada4a698d1aad780e3dd23ee754540609a69f04fbd763b29067d2a5d7ff8963aaf5e1282b5857545da93a683153b90c86c942ecc3cf1d0b9b6a2155b14c6b53700fa0bff5a17cb677d0386ef5335ded4190089c934124b8e3770462653309bc8be0b5284e508328e2c0d43c6bb3226fc38c906e7330c81846ebc95239bf2d1345befe68344799c24cddddbbac956eedfc6b6735ed53959595c6fa7dad5ec3310e451327fb572a1fd924b1320352956b55b99f77c7e631454160e9888a8e75ae665aaad83f96d7323fe494bf456784ac50976821cd0c50f5e0253128129f4d6623921d219fcd6b56e40e1f521d415ec808d351f4764a3dcc8ed1e5634f74d19ea333cb97572f93bce9d81f879df0a0a19ee80fbf7578238c958a960afd763c1d4de423a6f44f317f8fbffccde6e03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c6e4fc883b029dd0678cb1cc9e130ae3226a304a6aaceabacad51f876de4687b","proof":"4e46f06e33868d5edfc48f5723bc8be7324d75fb91e6645da85d015778108636b64ee06425c3086f80080100cdf2f4f803997a2c98a44ec3e498efe114ff63656ef5ed0455966b1d94f3218c0f0cb0259cd18d1d028d881dc3e46d0647d4710b0e9145dc46378370cb5745a59f47f5cb6b31cc9d94b574cbe4125877d81f9358fac0c5dfe7f0f67aafba8d33762a60e353a24f98665faf05c6ed37bfeec2c401e2f6d23b1f62ec863b033dffc21d7c0c94bc18ae0ad2c1d86838fb46e1ee620483f94b5ce96ab2483848adbf9d735fe5e38c2500550309b9a35b0b56c3b38503a4e45bae183415d855ccd9740e04dca9402a7df4bd594555e8b7af801bdad60ea473847af7793f29eb40b55ba188b68967e7597cdd172c6f6e1d973c0c48cb4abe5178156c4e1227f4c803cfdb8c64c03fb3350ed717798a0ad7650e3715411b1a07ca133e4ea82e8a26b96de2bcd492e98538cd00ae391e0d8419207b34eb18dce74362242dfbc2517c46f46f2e751022786219cc78bc8c4e6dad44225eb93f3ea4ab1e810903aa813e84d1e4a72abc10ee8a4a10edc583d11e0b8289fb382010b718300977610df3344933c473879183f853e4cfe526ccf16f9982b96d123dc04d3494e5d0cdc5d33f9dbbeaf5c7529c8550cc1449975fb174df5e4d55f81fac62142119716e719724f3ed11bdebaa8c9b845e61ca307c59ee6b7fed9ad52700290fb0b2bc0173077ee86bee2798aca0c92ec5157eb7b093149148949320543c30419eba3886906bc85168376648ba48f29c78d95566c8370edd0c5d50eb1564b043ab374c3fe2cc3a3d234035e2da826d698f1a753a06889bfca01b5a1b597e38512bc58c9c29d264698440b443d4e95bf45a4726556845a259b0f101fc0f447cc2d66849359991adb82d86085267e0dc3bf99428a1214cdb6a1ccaff5a02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aa0ae3fceeba29c755babda8b3d729cb2b3ed72a55c255f093e0f778c5dae134","proof":"fa5b422e37d8bce42446cfd74e5a0bc7e364937ad59290952e65cee4d39f1b0b2a948a1f6d3f895cd8ef7636e1426e4cfd57de96cb2019c0a2f8edfcf5898530b6d51445ddfd5e4e8251261baabf12c3e47637f430da2754f0d0b3fabf882b141ca39f1f19a35a99c46a0582bd29015a38c5e2692b3cbd90199ffb1d10a5a4726dd076d10265b43c542f9818bd69c465e1c8ca0187712a299d6b45e632dea108d9fc14f493a9c96378e123eb5acfd522285c1591e204fdb14e3f6dfc3131ea04b5ea2948f2b662f4ef953f663a866693e41883407f5c7ff43aa045db76b3d80132fbbe410a1dbbaf969b98c9fb8913e914d8544f781f5344656a8cd1ec4814019075a07c588e23d72d09e63e0f4bee113eb12f410298436896663aaf6ae88072809fe348de9b7a5c49ad9ebbc0460b67948100384bf1d8fd48ff4ca0acf63143c4bf64dffe22f8cacff9325cb4c2a34090b73d47ad83943037d1e867f2f1f97ac01580d1ed29e74f1685dbefe57a928af54c0f1fb5a68073670ce92f9f13c8038845498f2ebaf0281e3950037284c8f5f96dcfebdcce24ea879c6281063c541ba0ab769082c8cb626804110a873e8c2388a6b14be9f701e7b692f2c000557e6e0c1639da1d0b9657e3a29ae19b50c78ed4d2ac1e8b9f6d8f1e17b105b6d3c245badb614a92a2644105c962ad34944cb01c8cc15a7982015e4bf66f3b7ef59f1dbc9865625fa77237abb61441b0dd4919fa3822bc6092589c532e1e4d4f71663172af8e71b84401071521fa201701a3d1cd612acd6c23af7ccedb238eef192942be62d83981ad903274049057930e64fec6c702b6aaf9589bde642921a555c459a55a132dbb714ae77684eed7613edc4f29fb4c45009d281900322d2179bca3091266e5192d6e6aeb05a74cb79a3da0b1f80cb14690804be84bfec3d596132a08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"20e2600b9592f776bff5adef99c559f1ad4d82486e4498630b090822cad76f0c","proof":"407cfb4cfc79753f3f8a685bdb7135d9397ce698f48888cd502b883289c5c939d4d04d9e7a953071965d0a761f49a8abf7009111bbf2533f9d4269b1530dd25c0abe790fc6495bc854c002755e74e9c9987626d8afda7cacdaa4c1f3b9f1b95d2423e613bb3ebd179ab0c6cbe9981b8087411d1d39749c95ef46ca5c3904575d8ef053c30f48e2a65a32780a4ef7d48d4a0460bfb840f145b48d815bc67c450703d9c44dc48dad068d6e7b4be999aa1ff607bb3a92dd255772da895acc5e0a07d94aca487656229617257d77a0bd31c0e578816d00d333e643cfc1632166500796a8a37514bc0a6f581d78bec4d36dc1c46c64828283aad661f4da479adefb02f6e310743a7931ef4e6a76ce299c507be6a1792e1b3f8159bb87fe81569574605c1ef43bbc4aa4f283e072a258461feeed83e0b0c34864194a9df71975541412ec281f36ba342f58fe9dbf921c6bd90bf0c433ad58c21083ab1c1fb0216a9e36d23f7e52587372c909c9dcb6214616b9af7ea5525e437413b1b6cb8ecdb809785201130c1afa5c3a5a9d76d793348ba4bf34312ec4cee38d86fa61f5c230d73fee9fb5590d19bba4b672bcdb162e376803015a89a345392890a410c15705100b623ca5a97dc3bf42e4b27d0aefe221c707a0faabd89c7cc025edf4c6b4686d203ecffa0c653677e14a1e641b60a8ab1a31797b3cfd21e352adf732f299b7925c14767cc510b638128ea273b433f6ca2c8d9206233209784176ea237604151f25c676b93214d9e54428037e17b718a1bacafaa132932202f9f2f0638f84bfd21faa46ac37e348dab98072d2d0bcb5442d8c92614b3ab8f7a1ab08a691b5327a23414faf13fad39fad3c6287f895b65295614d60aca8841d6252dbf9b6e26a66067d1855467640fa34fbf64826e82aed80c8ae4a1e5e17abca760c9d5501409408"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ca82db3fb151cade302dd1d947c4a52bcd9c015ee9bbb3a4058f8afeb017c024","proof":"2c6469b763c1c9fd97cb89191a67bd51b0825c50f29f94602767e2715dd04e7d74dffe627603f21c04c4b79fadf9910fcbb9649f4c3e9313b20e467db402705288bc7dddfcdbaed4dbdb8dff7462da3dfad44af6a4cf181333cb71258dbc94053239adc668798972ca1c53081c8195904d638ec5d626d468f7390c90c984fa5e01608874c5214639862e92dd02d83e6f65ec099dbbca994d290cc68b4fd63e0094afadd07b08060823b96e3ae0d2c330b2e14bdbe2f2ee5cc3ac4a8a2ed20706bc30ecd4a262ae3019d67c0b2af586a929afcd04629955599c276dd299f4f10998c4d270c86da146610a42e25e83bf5f1393ee8457c939974cd0ae6315e298691a344339b22e48ad33638108db521bbbb5e963dcf538b800c6681aefb5ee080cee554bab339d063760b00e268d4c00131cd965dc6219e04072a847704296211168eff2eb5f8985656be0711babb4c5d11a243a802c39e731500b8b59a3a74425c4ac1b93d9210f73c6c1dcddc626348009b11c58cedae5588604a7e86084466d5695c49fb5bc97e6e107550f0223ce932814a2cc6dfcc89c75d80bb514ac515da04901fd29326b470b798fa525d2d037ce66845d995b9b3ad71e0bd669843a577207b1b0ebafd9e1ba533879c14b48fc7c45f276c694320b06af2f0198de084e2c2d7ab451b262c281906d205de8a46b305526b030a8ded26d11a3896198b33528e5d11de2ecbd3e3a01094b98c66e8c94f7d3c959cd9dbfe761cbf52112827470b5985cb9ee7d213cce84dceade5d0e73bb51d6b322fc89828fe3e6df97d1680440ba9df80869b3008adc2b43aa4f8fd2eef70b20c3f4f605212cf022ecfa4f1c32cfc11f315f7dbab98bd21ecd08d71c5ae4459660731df12c6ca4692b5d0a8028d75d176ecbe3d530f57751a0ab8edc49ebadbdb193714db990c1b8f9380e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"38ac08afd7c4b802de3eeea83cf2666be3c2663e1fa059ec93aae51dca0ddc14","proof":"84c5820e1422f476f11d398c629ffc1fb2fee2c1d3ad5fb655d5a1975714926bcaf8ad55599b7c6a779f2048b3ca75305ec89e0714eb36f83431325b46ac271f669a4b473cb32fe7d9d3c7217c5726d5028264cc2535ea2ae4bf90acda6fb170703e53f9393303206e156f40fdd27a087cad8ff972919c71276331d26d59967a5354c789afdae35fd3f11e0c99739d1306b19f6630ae18ee4696eaac68659507dddbdf3b32da571dc881b88fdb00b9361cd08913e23fbf97faace88ed318a60fa203a6ea026b00300a23afd0961f246a5da0597057f69eeb37c2eee35cc6980c90efd079e0c81e02d5b88a2e85773e284ff1069c70dab31bad6b5c38758a5272ca3b5f0a5bf1d9cb4658134629083d1eeec9beb9542f869369ba7b27fc1f8b3c285414636514b5e1a28403388a1f0c7be3c18bfda6fccbf39a9ccbece55e917bccb384689da0908a0a40d4617b9206c6429dee40507b622d04bab0a41ad52738a8c257b389caae52088480cd150cba5ad25c90beee3d021063b4ca46ad780e145ee8bef543f96697e068aab7df21a62c948e318ffe832c74e083624e688fe0411ac72081c1623a6dfe419d1afc0a4a375fbeb8bef9d844b4fed2121f6e5287119cefed07705b8b10198d5aa1eaa158d319be6867da91903da4a47f2eb355f322c4343808e299181ad1d56d99036df84b40c1ef0bad8b0af530689dfe527d322884fc035c9215f87e636b6a4a63a0623e094173d48a52b0a06cdb847136c4b86e7e9f5dfde9e6b3c0f62e90a160435854bfe2a7dbc6b62134d1e9d5062acf11653a563d57f316a5f7f17eef55c3b06710f051c9c9ab1e8f7fe9efd11c6f9e785b9a34bef3484f2ffc1b1001fb9de75eadde5ae78aee86dece7294d80b0dabd609cba883171b455315f2a3efefe0ce5815a8762fb02012ff27a23a5daf88f23004"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fa266ab359c55515cf9f78106528f737d53c40fc3c506c4f9c45b8fdb660c40b","proof":"42d6d11457a02dc0c8c56037c3c21d54257cc5de87777cadd43feeb689f64e7c1cd61174d0ea8646962a19c6f08e681484e4fb021a608cadb6f528bb0acd5451780473d4add775ab8e525ad9d180e6d8b96150870032f54a92b761b2a76dd4023252673626e97f921d197e68f5c9063e4cb5ae4f478e7376ef261e3ed0ed5a2e64e371436cf458092439ade2ded5241ef3998eb2dc0cc37cbdf376f87a335505bf697bbffd47b21d474eb5dcf2884353d1e8ac208d764bc57a91cc1b6643b20c4f88b4ede2cd50c1dff8850f8ab105df01b3ee2d566f02fc81ac8d601a9b330354f68199a90bf1ab87cc70b0c0b98438435d07bb35a341cce353e496dc891c13b8445924cbd26b3821fb40107aa10becbd79ef5097a39ebcea4033d4459a603bea374de5a937bd70674f520e6a24c8fb247b6ab2fd116ba0ee7b22fcb794cf22821df6ddb3ff426bfc9a735927e85561ca6be5f5b6a29e351e3fe841917b82244055b3412eb7557c0f34cf7d3a987643741fc5ba836b47816903af49f434b04e7af28610842c46d4b5f40519bdba7132d7395d5174b644d6521805184549b9188aaee86a82b8eabe38d263ef50a02d57f73c6076b690884c9fc4f8cadb0ac323ecab1c573cf9fb2b20c5dfc23634a700af173347cb129b690be7b02735c99b1b8c73ca1b234b5f6f8083e7777fd1ac90dd15f907d6cd2b710791e9368ed8f90186c82dcd30a3976d7df5b8ace3314995d923dd5fb8ae06c334209110cdc50011f8773d1ecadb023a8a61f0001a22634d1b174e1dcdaffa47c5e590f407641217beaf532820c724dc193e41617a01db2d6d5e4c511710b4be2dc0606c77adb20897f0af4be20deb8c8253581218e2000f9001cfe2d25e49ed0b2dfe51fe8a7b043c53b995164ca33aab841e4d7a259f7d0b9f8ab1b182e750d4858c10d95e6404"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2cc306cfcbcfe3f89ae3e767e15835b3fbf1e9b699109e96fc9f3d2285517a22","proof":"ea5320314fba98b23bd4ff7141228c86418845456c0e615a98b51d83871e7e34a2b0e169a7c669c2f58c17872ec494424de04c4c1b382aea4f26b7244dd0665324019f8c4865786f2eec7bdd1f93c35c52562790fd6a1c8b09ebd6f557854c513a0b4a2662a1e0ef2afa856da0136f8f2263924689fbf01edafcf5879dce60152b6c3a21c4ed5c3c337405ae42d690ade823b779ca1571e3e01121c6d2291a0909075c1350119ce9cb4f89307328ec5cba00a559513c16d485288eb96e422d0a9ba768686711de636a3e39303d1bcf621e9b16b9abce01a9434c2c2ddcbcca0ba455805802cb955d1c03215ea578282de39980cd69e3d148886ea83f520f7a7ec00f62d3192c9bbcce673f69213688958fc61fc1c1160f88d5c45ceb10a2aa540241850f3c3c55c939532f6609343050dc6b19ea5a7a0dd66c6540528bea9f4f30a26b82e41c4c8519723cd48028ccec0cfe9233f6337b8fef9fedb66cc77c29e0f7ce72f413310588d64c7f22453ec9d8d7d938a3c33722767736cf4ed794543e761c1fd373630e1a3519583f2ea597b19af741eab42ef385a295e90786e37ea8fe77d728053b78e98c40c2867a09cfcee5a3a593d63ea3fb8eafc44af6e40390c7aa3df1c474c509e1a11373349942967f0168831a1c77743cde2b8e39d632b449ac21ed96f1d339f6de45ed8adcc57b00c018885fd04b4fb5884e423b0a3d6837fb4fdd41dc3d0d3c3b9b4a3bf2f947e934a1adb4a4d9d3a3ac13296ad5684aaf6fe2e6a4245f3250053c49441cacf919ab56fda452ec141c2da30a3c84748cc1c566d977d6f127b831c3074afc5bf2f9b79c50b2be1e86bbf9d5419bf839bd6db68d96cdea20e21668e8f1c50137689589f23402f1952db543cae7713d0dceb4b8dfc6b1a1620b869769eacc6461fe8bf248cdb4189bf919250e719c2e04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a6d3069a949881477937d80991cc5e5b252d342525000c18c7fd17bbec9fe412","proof":"86111826d03b3e17fc23fb8eaf50317d8b5be90186679f37d0b632c64fad5f49507409475d573ff8de2105f745d4c3905b24a9fbc45567ee1cd0f63418c82c4dfcff808cce7678a32c10fbb4b300cf0ceb8d675ea010364ab28c77cfd6ce202930fe9d1c9da5fc8765d03624d07ca649cd5febfa1f0475ec18e39ded27b7c9023954d1d96a3c98583765a471748be2b00f22826e19500c1a5bda135380159d093e6285326de53c07d69541ffc5ef02445985eb83c7054799527c64d62b6a4d0b6428d1cfab9d31548e8056ed058dc28bdfd06c2d9775196c9184ca46d515780798796bf22b786b18a74ee4ae4afcfaa13eae3096c5cca6380ebfa5372afc99450418630dff3a90af10e43962c9255928caa7b4f4f59171fc0e3ac954b43f790e96250c14de8f52713680e6e55fb0d4d07fe858d22aa6d7e2f87b964d558bdc168243486e781d875f932a95daaa3784da0ab1529fb8b6d61dbd2a3498a3d9db2dca5206e338421ffb836ca9f21c174bfbf6e346bb9f9333fa8ab1fb3519bf3839901f10b63513893dfcd9b218b2cc3c3315ffdbba23c410a6f414ef44a227c248be4813a7f0e07e4497d93f1fb72867fa2b1a10585907e62e526df939dcfeb678b6df178dfb0d8c660bf5a7297e163bd0724f302dabac92cc6d7b57c695374b1006b9a9efb063551785e64e025604e5f16a5f386b72263ec2dcd5cb26a138e75fa04e605817237cb333a8a7e1ae6b01922481ca0c657a6e44dcf8801e2d00f32f6c82764d58cc07585dbb11c2f00f3aeea8f5f1bb71ac7cf11a64a2f7e7264b60d0224b0f9779b552d4b4a8da2bf5434d375e3f4d7e6acfb85046be0da61c2c6ae0f3c689e962a04e02c3e805a84eda1dcea035bd2ab85e6ec0aacb19ababb206b2c637b91120ee4df8f870b32eb57eb6e196939041618c38ba78852d7f71ff03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3abd9934750d2ab4c8ea3e878d3bbdcb9756ce9ed544667ee40654ca81c86912","proof":"5408f016586e2a17154d6a781f38742cedbd97d04e15ff9a4412e95abc3830774a2c3a760b778f71dc077ada4b4334c06a5a68f23136a88fdb32d8ff5265f06d20d7c5ce78399279b87cab0a143b595bb7646c6742393ed9ccf014c0c3c87b0c881ab806c38bf16c37da6c3dfd99e73d2c35d9d903bcf2517877106e0079e1008b9dbaf3b54f508c0e92773d2be27b8851d917889d2cb34e88477eb3a897d802e3e314926a84f307657e4b79892870a5f680947e5a52409c0cb0f5d785da630cafe40c5dd978652f4f850ca2065afe73c34e4bbc63012d682559e4a6ea22dc096a41b25cde3a633f81951ea67a17c0fc3668cec6d7cfcce1650d8c0417f5293cce1f53f19673917c4cb74b0d61d139eec0f60c40134ea7a8e4bb064fd906a34702716a3f2206a39e2bd5025b81a20bf46502df6356495328f6a9b2b76f303c2b981668d3e96e6b00e06e719f201b32f06be48ca44d3794b87cdbfd3acb74a8499cc1a8aef5d389bb85bef90925da9fcd466626d4504dd5b51da83b21415ee6575ab7693744268ff9784219d6934f3d9f31db24010429d53e316af4dd6ec043518235a74dd9f5106a47bcbeaa5fc13c5645f5bb7524f69198437d9672892b05645604265c53531634e821e690292c81be42fb0c4ba8ade7fe217ba609fd873d75e4a791cdfb02b4c78144f8f8726cfdd0a0d72ff2022872ecd2a1163153db3e40fc27bb3b4824041b5e407e68d3d498353607403b69396ae5635371ccfe23ac1e54de112ba65dbbccb60790ff82cf47f9b67483e2cc77d8d1a5a50c88bbba634d1eb8aeb9aa5c01da3c9e083c6877df70fb6bf770d20b23dc856dc04887a29b26b971d397e432397d87f9692e71dd3f30962c2950505736a2f5ab53c31dc0ee08beac2ab224d9dbe1d40a5c40ba97a4d3deb0807315dba035c965c0fb85cb7e05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"585427e1a387509d879fcabca97160c6723e92652fab3378722a1a4b67f73015","proof":"bc8eecb30620beb2060d2b279c226c8975f1d751c9db0ad046c8128874447415ac23b682c32a59e4e7ed991d4d512200000019eda8d12b9abb0ac04a69721a73aa0cccc4df9d1618cbfcbab9b9ac3fe03f5fba3cfca719c94bb741bbd422df51e2c370d0d5f87910f4a977966da6706344e76c63736b4c83f054b1eda28bc90c87e8235397e3f1508cdc4bc517c50677b7a6f493880f71b97f0bd0fe83290305839356c748cacb35702d5c2a5210f6b68e57e27c18841e647841ffa7b8da0000a5c9926abcf0f91fde5686f1d2c24ac55894db39074c7178f4c87b885fab32025a56d9c15fef0e1dd9311abcec721340ad492f402ab86f6931a69bef02abc94740e7191ec8cd06916fa496ea48d41adb265c6742a77b7422bf94037d0b987d784c7a838650eddd9335f6a8168553fab68c343eec1ec06d8d97282163cf71ac6074c392bbbf3bab8e1c60c11a56e563966c74e08aaa7187c11a615cc6dd0154443234534fcfd58ea20fd779fa9e579cf38b91e56b8d65fee8296a76631928a861b8e5b2e3e4ac0fe00a8f9c70d8ed65e195f50fcc8a3883510abd68ecaf28b361ce18bdac052f00cee17eb0365451fd045378196226c60e952c82c9785f0df50dfe7d489619e90e9c1b3773e7abc7903b2a89262e5af2a3928c6ff52ed891877e94aa28cd9063842565e39b0d48de017c54742e77156c8876d89db3e7e72b59599e093bdf2deacbbf7f08a8a65b6f33d11b2f6ed37949bbabd66029d16108aa49dafac7122a67592b35de2e7934267ae3b13ff5088900865082685e777e5ef57a480179f82f77918d05261d6d6d32f7f71ce44c742c7ce4bc37378f4d14b8aa74222dcb704615a5e7fe41acc78bf77551ce33d578378a15d2d6e547be9af2da046970eef7b0403f51200a0e5c2122ada9174fa2d545f79183d06873fd4d81c207"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"74f78b1a3c4cb058154e21393ba29fc5cdbc0404b2380e530a788cb1ed581f02","proof":"308ba38caa9b2e429b6185b64f650051cc865ad6405a7196878d27978ebba25328d7a47c06237a9f03a982bcebad0fb246def409e7ef4f7899495c07a4932527988a276099e892a7aa7aa4fecb07656ed88a0a0142b6108cc8aabebffaac113712f071cb6abb9ce9125932fdae4f5501022776f3de345706ce38ee2c486ec35e3e54a77683dc4ba3761b86e9e703f7f00965b90109969ebe618c549916c4460d2e514b7a0dc047bfc8828eadb9391263a3f11a4377dc79b000cda4069ad52d09a5f01b791aafae3ab2a7359868e1e52133253c622763c2570eebb7723583690d24a79e60d632639b921ff9a693d92db066e9bb2e71f923dd11e30af4f818e20c6ae7ce332caf173e04a1a670b01998388d62d18d5b853a41e4ec2580cbf0890a6c26f6a40311c3be5eb3670108be1acacb7e4a99de7db942b5b5b4d0e55f24516ae2b000df229b5e87e5f94b7b2f22fda1486983c526632f857125432a45755a0ea6bf59aec499034c91c4bb65c630706929cd18e2caf841ba3d93920272b271765df9a0367e90f5b670a09e5a295abc9cd5c3b114f0b400e0f97c8d774c1b71d4143b29e8f4632201d26f63d3dca8131f57408fae91c176a44149f7bc1aae36766a45085e9c1b2188243bbf2c01825965299c1d121e191bf9f877a965fda665f8b43f07a971c2e2250007f0f4e21d151a7c37b61d7d134f18c9a5fe75fc6a5d3e6053a45f0f12c3e2b30c84804b21ba0d91d573a488d7ae03271c2a677a60384866d5a43cb917a8c528e8bc7f6fd4ae865d0fae7622cde01adfb108b707fa3c76215a4839843eb5887ec8408f69fc5fd4ea9da3d97e9eb4b3386119c72f2954c115ca148a3476ecb53e8e00232a41543ab1c0b45da7845c8c5f09072eebb90e5234ad41ff379c083833e4e5c11b3ed6655684953c81177f2c4432a8f143f705"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5ef14c613d719afdd71f30b28edda234297a7eecc466bcc60a003c6214f5586d","proof":"24f19bd1d62cbf1487c91b59413a6fa6904be1b354e448dc3f6f8a7cd5dd740684b7d0a170fc2754544ed87c924213d0e072a3c0ed05e11dc9f9642bedd26a16043bcf55666269a4df2265004a438bf6da50291bd9b379165c0f4dc9739f69070a085c39670b68a6d5a3530d26f0af326f502313b8a37bfdc50d1b45979a786df2888d8c1b9cbf0846d61a1302ad318622d1b29b63a731c17bfc72ee036e1d0dee37ac18fa8915c4c055efa60a9da074220299a2deaa6c4e8fbbb7df041ac609711136e442172fbf90682f154b6562697dcc85ba975db6c75d13d33d275b290832ff749de5d4878b135d8730c01523a2c4ef1564366feadbc15d919eef067419308e4da12b70f17a87ffe0198ad89d5844bd2459e1f3141d57728f3e05167d21cc5287de8cc55a0352fc0d76ce3d07ff66fd59221d6c153b0e76d13ad1e66c08be9715e4776bd03c9f944305758f738ca6573033881665a3475aaeee94e24213fc1f507581f3a3ad3878711706c7a5afffaa897c29739da7c03efba6139610570a1586835d5816d33db7fa9fc5e01b4490f973115f609769e473a09f48de0350885cddafcf6390b5376c56cdf9ff3c9480d65c1fd2dc3a9c6d5e3b40e9e39f533acbd9bd0067fa06f39fd996f89a67d6011c069d1b6b2c84973c46bff1e0ed0fc6b7c09c7ca209a73aa82d54f64bb2833d26943eb9bd59315160fcd9bff07a50f8518b8c343d6d1b0059ba1985b95871fbf183c5c500e11f63cdcc7656ba73355c64359f0089ec2aa26905f9e5a0919a653963c07c81728e854a9b07d5d9212e94ba3d037c4186d72922f7f5cdd493ebb940d858a7948184d6e4f925bcdf5562f1f571ecd425ee90b15a34daa85fcd15995c32b41308a451770347b6670af0035c753677fe0a22100e3bdf77eed2c799fd97428fa2734d4696023cac62a22605"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f432c3dd3faec707e347a214de2e6c26b571f7be2b2a329a88e517996e746208","proof":"403b384b4cd119f979059aa26858c9e767e0277b981f5a715619860e6d03cc024227ad96fc3b148c08121b724642c87e98a33b1cba154406c698ea0a421f23152ad2cc1191ec03e19dcc5013aa0eb21202154a9fa588d77364bd3de7bd3c7d02fca508283f314b97ad7578b343d21e267caf9dab42d387bdb1cb0c9af158342462c6db74affc636509f2f6ff1d8bc569aec1878a08bb8d4740f5be21862e0603aab880a52a42748b486867ed1585c3d600bb293bceffb96785ff619473dfa003adb1a4bc650fc4b77796dc9be9b0b436d9b114c9a8059ffb57c3221f05d28201c0c09c60196aa5643791204b4343ffa0cec764fab20b7437067fb539e642e06d0a36bb431a09be756de810e473fe54fd7573cc3066fb391c95736888d7c6546c746ae0c1afd0675569c312951ea1f1dba4534797bdff005338450fd3e8eb0c133a2aa7b015bbb5d0ffc7c690339c9f29ec35f9ff7d11204e1a52e82cc6be6041a6807c18beef359bb808ef4004490ba6e692e14ebab4fa513931fb8200b78921daf38f83ad095f19baf87282996cced85dab13e90a5c139ed5977d0933723557f8fff419360d2c4ffed59aeeda3d133e31a3c18423fa2d62d18df05efab89f2bd4ba34697e2d8c1815c50183cf278831fcc33c282cfbb53df95a1592810a2b7a76bc660c9f2631aa8dd89da4c15c39639568a6085d0bfbde659ef4172b71f36e40389afe615cd28816cd631ce497797023c6e75177b9a478e25f4a3cef6f354d9a00d28270c839773e4570930c8e4650df715c1472df90d00231295c7f21fd211e3f9ab79141b47b6703b14a091d17c80f75ea9e5cffc7f6bd08f2ab9130ca2a2736bb7f3b851e4e2d817458ce6d1ff90fd74be5e77737be23af95d7c9f5f608390453244c33d2f39cc525bc1d7caec3a2c8bae2f26c7f9d3ae52a721c6fa305"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0a65d145ab68de8b2af132a807845582cd27ad2ca47c8c601d4f52fe2554e83f","proof":"6202dfe0929bf9cb3afec35e3966c4e846219309aebd2e43ff7aef592493ce6ebe70ebf8064cc1585a50bc0eaf8dc90d3b17f00c50c72371ce9159cf69853771e6deb5b0974ef1efeb41535e40ef7c9f6e0b3dedc8d6aa98e64d82789cfcdc12522969826fb9572753aad2a4decfee93222da756d785fee5744a7ac7c9e9ee3b39036a9681e46a6e15f45701aad9bc9855a4912643531f3736e5ccf903fdcb0466515e91c6e0f05b086e13f0dce5838c2f8c0f10ab3588286b423c0fcb2be0028b0376afac8e9e4fffd3e8624b30868a973ec4fd3204caf8501a0e1b1316aa0cc8015c2b7719d50b8cfba826c47db7f37875fef3cc49d2bc571c64af7967ec2fc64bced7af74fc869cf1c85dfd52a00d16350fff17df4593c3e6f9934145892cb05fa06a28fe1cf0e46582cacedcd5faf844f0aa26e3323475d3cfb573a5590e025e774342f30eb6b734ca4bb61337bb85b49b2eb716795f4f0329fff369607f9a8335a9875ba3be9432e666f150a17541073c585f581c0dc3de87dec8b1e60c2ecf7bf39740bd4cbe5102aee727add69d4ada12a7caaa6df4d3aa3306898d02421a81cc3a0c4560b790a0e5b70f9791c0650769ebc542612b004257377ac16048b75146668973f980904223d9f5c6f1892e52989bb9386b9224bd73abeb7f1bb8abc643ae5c2de2b5630a7d9a628d364974182cf04205706887adb1711c971ec6d3eccbd935b7d0ee10b01b315bd549724863a04d0620af8172c6bcac8f750848aefd94792359360b7c63fb0e270a2e06a0c708891cd8119828eff3b18840028cc627011fbd1528af054574ef3feb191ac3003d5712ed4be137cdfe732d04441fbce8932650d062c2b0f45a8647b9a68730528ad53de2dd9ab66dd5b9a12a0a742cac81e6e7fdf8e3be3f97c2b63cca6563661b078b3df5392e889a3696780c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e6e47beae3c7c8b92413874aca32f635b8b8ea81b3243fb6ba851cd2fc76ed15","proof":"1aa3f6784efa21e710601f78a9267ee0c2574a82d207d7734aa9793aad3646035431fede2257a1efcce21294c93827da91669cab6a147c8dbf657d49888d99566625b444638a85507f4457c42242ed62bc4a9e3d1c4768b4dac53ce235dcd4173afa55371abdcdac3bd5078848f1b765990b9b081bca660096e875b567c012010ca26045fdaf910e8280745576816dd7117211960405096b8bff3ed3c2efa907d6ad55e9f251f236e55abac9a25e90cc325895079a716f25bb764f6b1cba8204415d8da6ebe0505381f661855927030be5bc4d14e64fa6828cf1acb710aff004a23b618e59603494966059e2d10189f403f5a751788524d2cf7e44ec4a07d373d08ba55174d83b09a6f4ea003deed166afe06e418a5724cb01a2c218b92c0e6d78a01816da25496c4cc6d255abd61201a7308764e68c3b0406306d048aa8d45154b78afd3f71a254a53bdcde8e458443330f3a88e72d745cd480c9874226056dd80b2528689ff6d63072b5d1adb467c34b4817144f2c31d6c14166792bce267c921ba559f61412b710b69a82bc8c905d2947cbd602f535ef9d4929d53cf9560d26a6d8d569d32debc494b6cb0f52c9dd0481631a4113deb74ac65ed17b0a4d3d746f729ae0297dfefdcea22bbf98ab3331b63732d74851286b50fed408eb082b46253fb2a7c1809f6becb0621953f2ada20b9474619fec679eb2fba5baa2115970f74755595b6d71e2e2dec25f0e81916d1c4038d808520ec7dd2eb84d4e60425815870e5fbf76117b41054e56e348a2161721b2068bdb95503ac294a99be77aa2731406219a5272b0defa8273dc3d795656b0a7d4b78d632b76dd7b83593526afa3bf5b03e0b1df0e863d20a17f8a37c60abb0d7175522815b5b808db916507071110a8089e5de650c619a1ad6a6c3bb9d76f26e0051fc012a47b1d92a10f0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9a7617682100e735d411bebf4ec96a667e5ee9ea743e0ad5702731c5acc62879","proof":"125e47bd9d77fa03d1541418dcb7ed098c246d318beb59f79b144d2a0f0aef794c5e40a77968e26105f5942c782251d934fd39940e6fca78ba451c06d50bfa38782e6c9371e630c79dc927a5e2e2d3f51e8476aee990ab7c75faa2da11b63109707771253181394dbe1f8facda74022784544e07b5bb509748717a2778d0374ad51bea829597df0a547e7baed2f9e589d48ddab3a8a993c3e7aa6939cf076e0e61f4b90ccd888115a125c965466427dcaefbe52d40484f7f42f137be97f10406ec1f2e50c28ef2356a84684b8f067044a5cb4162692b83e31f3fea15f03bff0b685ab092186de3a3b0e6ddd20c3db7c534d37b921e73386a8977072231557d0e980d4e126131e8b80959f7e128e007829c3aa320cce8358691230640f965db54bcd7b9101e14a59f24e0061bc9a6b0a6b34ecef015692aa83c20f5186a4f4239382e19284771f1e91876326566a9d944998947b899ca86c6b6d25abca626c11e7c84f227ebe8e9f8eb2f9d85efe622c79389283d08dd699aa0d7377774a3845ffac16d745baefb2cb94dcc96846a3d8574c21c572252d4610885be83ca8bdc058c31136c6a4bb9e9735e6dcd80f56d9d5f8904ed1b42869587a893106051c556f2bdf61945d88aaae4d28a0914d22b42ecdbabd1901159da29a28c2e8c798f10962dfa5a47c01e8b85fe44f4f7943c320a0b33d1ed8a12156978b47e4cc8f51860fc65dae9bfd174b0564afcd4b9308a180f2d75b0a904f9acc8c5c52b750747ac216885e0d31ccc53a7f8878ea5f023f331e6623f4a04d48251d8d0e52fc16ac269e4f7113f25c47ef22cd8cbb5fd8cf7967e028768c9996c8dc66d31f5264d4ae72764c22e5d657335e102203763d2110a1d92854206a577c66eee06e9c404b76b47bed51a887736aa84e01e8e933333e8ea3684e1d2a1ef95b60841eb0a05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"220a45b3b9647ddca57b82a69c21d6f9a4e4f98191b6679001686472d312b70c","proof":"0efef7913471b243f2f1fd53ff2886de8dd34e7edc6f9873cd3223ef1302ea5c821ea7e49324195b958924bc3b1480939bd4cba88de14896a806bd1dc358a37e065383e97930082b579ae2244baad68f86ad7642008f5877bb498dbf8551c03460f3144272942fdb042496be1c1ab1ed80247ee7e0733adb30f98691b1ca3a58797965595be98f0ac4af28961baa936caad657919953b7bd4b49f2c694d2540253c1a098013ce71075a316145af11fbb13e8ca7eead27b0d4cfb215cd25ad2021f30853d376e15f54d06edee76e58e49633f5062ff54d33ac8ec565c876f9009665dc03160cf9ed2baa09b17020a0b638b4e5446080f260d2ec6d2529b341b58f6e2066e3bcd2ae7f2132be5b5f30e271ed7033aa2bc5363902f15311eca594a32d7dc18a043a7dfba58150b9806091d2a38318aed3e661c6d04bb4f10a2ac16fe85a8d9f26dfb43a567612341f5ddeb5e5d1380c363e1d48b7a7c803b602908f2ab7c0316918b5d3993423c86009aeb5e0503d6a0a954b41d8d1b6866689420b26d0663ed953df8cbf07a7ea074baddbf0777a4cce2814a1417788e4a3c1d2618c80da15c133d5a0b512b581dfeb48e84089d95593c1e8000e5f2ba8508886558f35374e464388edad934b11db6f85105f1b0ff307a595a99b54caa18b5087f3ee032ce6e93d778a5a38a96b970c5e5a56282d684a093bd3c01ab50658217673ef6cedba6b21e5c01937e34628927b987b5e8bb7084c69a759d40494c782070b4fe48e06d3d4d4960c29598a2fb86a7489e44ebcef28462234fc23d1aeed5113ad4c7de6fa4a184b3fed5fd08db3ebb5519347542e3094d5a803f1ed01714754f569b6f78804d04e1d7ca2e33f76b418768e1aec6de38f02175ce20cfd9d30c172ffe9470909e45dc2222cc973b4f630c7a3c6eaf15e3a7fa1cec6c14702f03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fe1eae76b6890e1e68dbc7d4599df14289e5b1eeb42ad2f10efe571a94f3011b","proof":"4cf34b10bd3d81fb34f3b76ae632c8b42b216431f39be3f605e2154a1286d645d8ab344baadaa0ac19f03d3469ff8e35efffb7a0ecb40177ebf7db456f4045578ce737544097a4abe985778de9afb0b300ae366f8c51d951f24287e29a2e796f7c6d8e75c22d2cca68fead2125dbb58da9f15d366d178577e94c6ceeaf316a3ac00523e026fc5c4187817da3ad71db597420fbd8823021afb497980392e0cf016323238e9509caa36b9c6c04827b39e10c210636510a5c3754973fb60ed696048e9f959268690f1805ceb4e0e8b3352b5ff2040bd418dc21a2cec992ab945b07f218c21b4ecf5a3686d744f473a26dd89de817bd2a3e610107073ecfab75213d6e67a07ecd6d0a92392b8da37ccdffcfea05b2c5697080cb44b0ddc9f480e37b3cfb6052f2962af83c4af89eab7d1682bef68af469c2530ec292ff060972650ede2d702ce12d84ff94a3b5e7bcee3755d3020c4e4e083209dcac8b40e687386a4622f4ec1ccf49740df2d0be48992a494d38533fb699ff6b52ee80093e65de509483233a4b150d8b3d1de1b16e672723a5a20965a5bddc38eb5d5fd9d705a455e8ddf1cccb2a529713653ce4adb3650415ae13d8e2d0798dc2417b43a933b076308f36b06838b9bb48893b1bc24b379984285011c36c24c45834d4e225500064100c67199a1ce429857d3c024677ad24d7587e820badc33e0088f7cfa291ba53be9652779ffca39beb4945d6fd093f65a38337e07f7cc003e1ed22d75d270b2eeab638f56a2785fd30dba3a4889921e4dbdbae09d5e7bcd310a62c614909b727a42f0b488952184a3e1a1fb29b6b8658b0c5e0ed1655188efdaec6f3c3326235dd673630295a9842b072d3b6ae5d4193793bd16a3aa101872073a36f9ba67b00284cd1860fa839f4b8dcca0ad57630c38922392dbb68a88f9dc02bbc923bcf09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"68bbce404252f0bf45b7e8d3e9d5cadc79e4891ac1b5024b753b64c985c9cf5e","proof":"f6c94f801abea2a17f8add3abfd281ecaac74720f255d019f560148b99d4384f063fc8e56cff0bb533634de0a2906b0bcc50762c49442463963d8afafa15233d92dc552fbb30870004ae87f122163f7ab082fbdd481b9e05e71903c6720adf4088a4b445a67e89116ce9079d499d3ac805adf5603849ae6f0c246aa74648ae18dbc8cb72f3eb16d40e639ba6f656c74c101fe2343f719c7c2b4df80613cbe10e0ead4f106ed6e3c5b636f5ce96f5e421dbf34b8c575a896ddb5ef88b926e66079471f7fa8709778289368425e5e74fefe85d899659aaaa5ef466710471f7ac06506e1a9453dffe250288738c1304153540a1f959eef154345b1b03947bb2281d4caa7d4f72c9f51f8416436a780b9da512da007295750b5be7ce24cdf8fe6c25fe770190aa98a87484038fcebdf98baca68ab16be8e3cc6b2def88b844a20648542d408ebec9c1bc5465eee249ef3261c84c1a72232280f32c1cc5204ff4fd49a2a4f75c596659d0ba3a78d6230ba7b971782a3253f36b5ef766e8ccbfb7a74cfecea4c694482da9ccde58091f806a7ea4849215b0888df2d89aebfe6fe4132bb88793d669cf8a79dd700cdd158ec1989d63ad6d45f32adba08bcfa80777c8043228e06e17400f86d41fc03fe232ebb30c0f981ea0a5b3da4cb9c78cb37e5c5ba626520a6fd4b1cb660f4eda6695eed0ab96674273c085324dc93ae0de5251356af6005f31bafe78a97b27ce40a2b37d40974c120d846214ef18c09ad874de408a0a21a4c1bd889dfd1629c61a947fc0a52ac4c14800c68dc5d7d0826ae85b58f4e84d899f7c183f08547b1d7a7c4e07cf386c7da33c19af7ce7988a7d5e6c1c7c7ab4169797eca78b3ef6bcdf6d6f6e3c5a356c7095908735fc97e95bfa7900343058d29796ac4880f55ff29504f8fa519523e0aec06a36e94079c42ad7e708"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d06830abffe218a1122c7f2b258448a47bfe38b027b7dada9ec6b8026a26885d","proof":"e0cc2dafba48877fdc86f268aeb9143546aeb47be73b8934c54e1e20ef048a3a1e9bcd3c85e9d66f68a2819ba0b7406be6a480b50f4171e5b92bf17ea47f946c520bc329d86709151905f2a905911494200fc0d6ec548c5b26637851e895eb421c2f9aa9bb37662c486cb88da2e95c918a7ee8867aebb676de95eb49f77ad14a41034254f75e92d1dda4bb1d3b17cefeebd9c45dd66fc79474d1af4d75af600c72fe6ac3bcfeab84daf1d46899d63a3a774e6c69dcbf59d33c9057ffed76ef03d0e586d4207ac6a05ca24dfd0b011a39da5f277ecfbe0b0206a1aad2dbdd850634395fce0d264773e481e92295f4e9cc6b068a9e8fb7de6d79f63053e5edbb3dd026f9dd84c90ae7850175dcf995016c9817cdd1735bf97a208497b8c9ad105996fb5827e8b273a47aca1176d1637503e7eacc81a19290bf8e9f8d71acd7ab785cbfe854b63d7561587965ce19b37f874c5f6dfc3ba7dd8760ab28a7dfd59e50244d82e889d792d43d819f67b7edad71f43a8e2c2ce8e4acfecbdfac9e378c420c761e3ff670ea47eef8c405bef0d4a0faf45f642e4634cec81da825116fea17dc6df07f81ef5c0bea0c519825a8d6ac6de99418b06f6959fedc8706d534150dba98d58c0d11f91664cffa80faf2fa0eef1e7b8240285086ed33ab508e4e4a41644558704e6549cb3bebbb98f8fa1c61b1b932767c73a6410cec14ce524c847baa01de870d845976714242ca123d2e9aa7ad9e78342e11351437792dca126f6e6cd505e9098ae64c13380c87a7645809d4f46daac98306f7f15badb4f0956b48f0fe8f5f7af9f1f43322816af9c83bb50acc20e3e6f4aff82f5480bfe23d976f3f8e622183754890ab58f16061c40766a93a1581533a78f2e3d22550d2599a0c1a626ee24b50bfebfb3bea9d40a559b5b1c0e241c368fe0aa05114990835130c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f6d29126510659abd99c03a26d9f199d072e835c0ca7940c3cead4e8dab14b3b","proof":"5681c17717e0e8def817174502cad263be32544066808e696b4dad0c4e204f24b6a2a216c236650fd6081bb99982a42d226799a3b2e54e0f1e5fee7ebf384d28a0c664363726bd9c35eda427b722e88c4066247f38bcbcc1895b6979a79c0d3276ae03ee092a550c2262191a4cf1592f2df94a0290bc53472c60d8d8eb5281183a6ea3bca89e67e603e3c9a7807a334a2935188fa806c731d062b7121e4ec709b5b72b102b0d8c6ca4d84b8da3fb06208f0344a5c4c85f438b6bd9356e35ff04cb7c188db4c4975b85d6f6aefcc9bce2cc8a296d9826dde8e2b0eb619908e5044239179c511266898afde34680bf3fc44beb7dfc640536111a133e6e7096a703de7af4a48f9650f92c8ca1809cfbfdd84ac342ffc4a7d0476178cf579fc41448fc7717cc20b4187541eb167e42d49d1ea28d1dff8fb94c0a951d92cedfb6b3305423f097964f828b5fb31f7ca62feae1b036ebf0f25d1b1ab2fa7468f0739e56bc153c84f2690113f9e952bfa88ca3f7cdc50e18531d7560da684a44bd033f43285822f428f6de4ee82de115e7f47ab5d8dc19f40f0eceb24933a784c8c69c53e486eacd6c40e69e08a9c2b17ef524918160ac422af9699394ce02c26fb7b03a06ec3ff4f7b22feffdecdde1b04df100818799e86eb38db22fc1671fe163b66cbac90eaffb61f9ccd503dc1bb2b3fc46f532342b37c8c3ffa734a916a034d012a6d1e9c391c0fba5370b27387c2d01444398fc575f68930893ec86f29e91445c2adee12e75834f4f5527314fedbf34f1d7ab7c1a6d592f482698ad20ee10df555e3da501e5c3cbef67576658d2466bec86e68b06a8ad40a1de732da3136d2523c5618e446eb18bdee46ae84fd068f6b8bf80dd339d8e8befc33ee00a7609fe06a9060dde5709636c487a7d2538daacce5761daabe387357d0ec19999245af90d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"341f6cdc0946c9e389a30de7d2ea5cc90fda046ca306fcfa414eb840fa1db737","proof":"0077d6db233dc96d03452d7930e71e45d2b840e2b8634a5c950cc8cdd4ca132ea23ee81f9f692d8609f5e047732bc0eea16fa3ae078689ed2f33fc1fe753023ec8163310ffcf8f3985f2ddf626cf7188d7407a08a75c7d817eb6c628a0381c5b6e6e2b3f6ae7ef6463a1fb49ead1fc122174c6c4f0c1cdacc1bc7c552fdc1653a85f7fcb828db984df03ac76f4314132d1becb7f4af6aaa02dd653ed93fce20aa7166bc0acc2e3487e105bde807b4f121b017c2e338cd9ce5d21ecdb660dfa01a3df6ed204c47eb37bc441dd7297d12fee846c9f906006952407cf2282ba210c8a18bcc0f2cb1b934577a98072dde2ffbd2c0acc2df29ddcfb8ed582a249d64570a98dba3cfdeabe6c40796416495a1f10100072a2132f3a9d8e761ac5c4477e724a1e03598a28b7df09c13f5bcf8ee5124749fa402862e72c44e015e5129e240adb28d7049c2c96c30dd074a6fd0b4dabc9ed40f844d2d9cb295af5c3e1dd321698ba8a50a3de04fce85eb72879216b3021eef7ab81431f7d7b3a85eee0417b9aaf49089ac3f2cd047641d4be37134c9550c345af6301975996fa701c661922920bf1cff2a1c6e89496c2a3ca6862e831adb3000f3d24871ae0025af83bb1407ec50e74907c733eff3f9388540a193f891e22fa03e56cb1a13f9467ce9c682ad034a40295763a3724c16d63f7dc6a1bd42bc9dd28aa17f1bb5f36f6c56ee3095244fdbeac9dbaf7c52c1a85efd14a06c0ffa046b433cd6af9f63a8063b69e6dc8cd4e3b09432533a07dbaa35f5bd0954047c86aa3be7dcd16dfc083c50291648a411e65feae2041f9a4f816c06602a90a2c124ed7cb3f2668afe296a04328353e1e744b1156e68968a4ae4bcdece91a453db096e2708b73f928f072fca8ad0d3f7b9e4990f79febe5356868c1531c24cdda5190c62fc2c898a9cac26410ee0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4867d984447260750685532a80bbc094ea16b4a1c513617a1893c3751ea7b242","proof":"0c639dd7fcab3fd8d9815b8fc21cf88eec4f519bff76ef31a551c23bf2e9376b581697220768f656eebadcd91424c8f5740a8c4fe491140e329bc4bb6fc56b252cc2739b4b087d703fdc3db7371a9de7f90b4aaee0e0e5c8f858b90df0b4dc679ade649f9eb7ccdbfe92b1af3c45ea1c2416585bfeb37e4db8ef340a9aeb3016bd4c5c9c671f4a869ce93f325387fcf1706942e345fe7a3089780b14aef35f055c273aeb78e18662db7ad78da874ee48ffea2604d388a061102c74026abbbc00027166ab7a6b5a31967320ff0c1375ed10e6c15c786747a28aaf6a332163bd0dbad16a35f1032927ea14aaf564d05658ec45971e405b20f3fca7ef5a05ff4654d4a8194fb0f452d8dad9555b328d07b64c24dc10c1ff182bd1f71ef06bf71e41e680fcfa8e91be9969eff10d47c30e79403f7c137b0dc0dd426f0b8dfcfa7824c82cb2a77a33eb36fd7aa034afd410e48c7780c70f5ac30780eac0edfa58804c9af43cb950b61ddfc81a72067f9c054df74a0bdc820df5a0eb6b20406f22cd6f4cbc645799cb6925ef28ac8a1e13695112e4f5d5e710f00adc49522b8313bd6024fb6974e2f93b431766250a9df8a9d048e7dccd3088fb5c8ecabbff114b917a804f7ffdc1724729aa48c93b02d289c9c0b0816fa0aea41a79839d5f30b1400cb03f0f2892ca87c63ac0bccd86ca65acd22cfce109aa58bf6d39dcf548d4d549fe7a5b2919596144c130ec527c44411b3415005a514525a4377848b60449ba20dea9afb899df58b5d089415ec31aa08bf6088d2801e5869a86628d20defd0513b25a6155bd95cf38093e52604a92e96296fca2e5e48bcdb6aced9e8b8f4d267fbc316c75f0b75bc7aea2a621acc41385060ad3513147a94c647508617bdd7201bdeba481a4d867848592666cdb02e8a370feccf5c1b062f0f499b69bf261e80e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7c149e7a0aca3b4738ee851a9f8728b0856d36a3e1e3e48fbff9c2cbc3966a64","proof":"7c904036b5a87b005313bfacb5cf4522cce6a692ce263c5fefe07dcee6e36f4e004f0c47a1416e7a2a31a172cdb5f1e58baaee6d847dbe28f6e5e5f86325366c20b2563f6b8b26df622bf028c0c5e5804d922c6ad493ae4c76f007c063686177262fbe23da4a17b3f017c07005188ab71970778f66f278961dbc64f4178d7d3921da368ff95f68cc0cac2149a962002c2e3db4d1e6bf76a85ae92c520eab130bb7f9d9feeaf5a6401c249e4e2ffa0ec5a9040700aca1fd3a85f22f2a920a2e08a72fbaf5c5128c10d9dc351f9de6b9022dda0eb59f3bc9de681524853eb3fd0d64fb75ac85d61db569d50c091b3e9ac5f04bcfb0575e64f0a6fd1334e365f44a4eb621b7f9d2785e118a5c5607bfeb3ab91c7b021d19785ef5cb2fe9ee76db6eeab79f19a2a16f814d259ffef74c17e2bea8c88fb1fc6e8d26530a94ddbda8561a10895ad3381e044d3b985d7f8f706ad1b1ec13ca1736332c2ad42eef682e70d06744fd4716fe97b8887c496587cb9f7029ec33fe5f5deed4bc7da8643d7850186fb73e7272c8ddbc06bef03918fbff07cb8607f8fe039ced23e5862ca2ca3618ee25fe575f24d0c0355937cef2396e1f346f2b1f39322b5fa04ca1cf8c2035d0b1c5a6006cb17548078e740aa8132f722abf3274b9365f44df355d9fad6c160eda3b86bc99cd44b965cacad1f36d11d12b4496c3ce86b29d3832ac9a1fed7aa634ce7e50ded268fc49a5632247608fe3d0650caccd5ab5687317e1c0dbe71e98b9521e3fffee98df7fdbc0977b90eab7a16f519c8527945b33b2e49f1b63231cd580e95727442307c137096804fc82fbc5768a2b86d4b56d687e39ea2fa7655291ef59f722b9d96640713aeaec14c5d4db7dc14bd8526a492d0707a8c2a102a77e4842e13130a2d639c59bc81765dfb3b89a9f56b37d8ed003b82849708a0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"90a25dc85a14df28ea7dc948305e19d3adf2fb58b83845d19ef30c1130b88408","proof":"ee20ecfc2d8a4eb1ac8967119f692500e82c5877a91a45627d3a3dc2bfaeaf3582b6654e9ad1e8445daee51ec09f98063265c5779ff3ca92d24d717871481631ecab1ccbd0e2ed021876d421d44a6a1ae959307f82f74ec6d5eafc75483c11350a2937e69d08b00aa1d1bbe1d920143ae22fcb90ae37dae3208fc52b93291c68a4e53e35b8de8a9cc56447a1f588aec42191a770615031e63db75d4a1a06260be347fc68ef4fa709d293a7fbfecfec04bc9487c628f9dc1ffbaab25487a9520f98d04de8691a0c383607563c579940d384161cb8e015a5e491516f3bbeb6330e38b719d05a9c1308ca94e7cc53ea3fa3751848cc63b5880204bc62aac9c3b2409c56f025fc394bc8a6e738fda09aaf364c77a894732dd2b2ccfb471f424fd1409af07db2185aac6a5370c698acc7615540347fa53b6c2843af1c84ac90e30625e47ad7b0aa4876e10a700e0d46e5047de589e82aaecb5527b75ee6a7572e3864e8334d8a3c8036beb1627bf4a4e13ea453bd7de32954268fdf9acbf1b70dee2b9275d34f4aacdc319281d12ecb2757728bff2b8ceccc3c673e0a482668e16b7086987834d0f52a02a9fc64f0a38a3953b1160e238cd7dd77d7e45ba24fd7f36422c24fa8ed38379e2ff5bda58f87d7fe0261310f26d11c4a15ba16c344b34952b46bac88df8002c4a9feda1493a62ea538a50e6ea2ad924670162be1f525fd517a52c285c8b4f8b51cc267098b0453943ee6cff908ec506a0e9adfc7f61dd91f5c875c8eefe4c98c67c7b3a413f8c7044437624652fca988e7b27bd54c07113836f00a03c4716333c1144facac5f43612d7d5a0c9ab780bfe039379209f3c14ad88bfb9a26f56fab5402d303249b984524c028d30ddeb3510f85a0adea5f6e0f2a32e3e8bed173dc36ceb63112202a8349f8ce7305991e4ee9b1fe86f201d700"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ca810d5732ff0afa3cad8262a5a446fa7bca2b2a6c15771229dff222a9fd5e0c","proof":"26d0ea93ef3c12b3f54696558545079b306b4c828f95cc98431676bbc5606406e6be3a2f5810f4cd7219a983c46f278b6035a03bcb48cbaf295d9fe1755fa9603838a34c0f971ef9c80c9ac95231caf57113cc39f4eb11c2dac7ee99b85dcf23387d676132b547fff199fe9ddc005721b843061c0c42acb4c20d22f99985515147238529f6a11c5d6a4b05e9c730cb97b379ceb49ae955003797a903e26942013ac930f61f766d2fbd72d550e97a5b103644d697eeb101dd42414e96194b8207e3395049ac9ed447d2be24ed2bae17bd657778ca7349ae91e8906aa4ca6d4d02d0fa14a30bfa72d93fe27a412027ee88ebaf84783890d0f53692a84ea824622524203d91a0822ca2881aafe1a26e6899e28375818ba01f15a62ad5c6d749c960123f45aa65d1a30160973e079a37802f994cd346a14214a81f1e7dcaec2eab45661c04201e8498ccdee31637452a9a3feeea5e4907bf0aa41c04021b8e9c6b59f2821b7caaf2963519294f9009cae86f1aefcfa8ff07e1a46a100f85bfcb1b4ed2ac1867bab5987350e7451d64b38eddd261158334111580040f534c9e248f7db60eca245a93f3cd5d2e579f7c9dab270663875921f7ebd9eb28750b3b3a2f2c447ff9ca7fbaf3c883243d537acd8c3cc4aa4f874f6fc2cc861d3342bc27701c942f82eaa9f5b76ea8b0427ac27509bb37b31b880a5bbac2f8bd254c9a7f8f7a48c1d600c955afbbd4536d060f1a15a99dc9154a1934df816de78ea674c8d50d16423484ea0e3fdec42f975a62ec3ba9dc521d3ae7cbd86c382b5a181a29a23266030d41665099a3da7671afad5e6dd6cf37ecdd0cf3ee2c0cebdcf92b777c10445a527f5785cc8d0d0ef7b7eb5e1f4f49de54540e06c573c3dbf1019346570459cec33e5178118e1d47fae1031797b343592d30ba5b6182408691f1ad17f206"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"94d1e9fc56ed81c426e20a10f45e8371fcf0a3edf3c8eca8f5709c07f117896e","proof":"fad2b5d99844ec40c1507d27bb3b647c7cb3dffb2dd2437b8c18dcb161645c6b44d0b4c635c2c7dfee04f681fc60a3f14c9ba0ab7468991c81e5b701f314ab3b3eccefbc4ebcfaff7c120b2a245e49a3b855b5dc426afea7bd87f880a059121402752d7e8ad6d133b461eb15baed80cd15eb987b360b2e64a8c367299bb8d73510abaa354359065f62433b0179b4945c12add04937090feb3870422d98918a0a5ce227f65602ed8c3643b58678caff01d1045ec80a9386477e9b578b503316065fdd00a40d7a1f09bdca3d7d4a5a09b9b430db010c0156a77e6b14bb6b023d039ed3c6dee1b9d253d1a3edc03153a682067b2327e79eaf6769d913407cbb2a416cbde9eb0df00a23a07116b946065963b205ba717f17ed2dcaa39862c95b4b063acb08cd275e4fd40c739a3598b039dfecfa236f12a28e0dff0cd0bcc233f46ca0d89eb0aa45e4b186c2be3e4b6a67377f6daa356470acf930709a9525b2e71788df704564f2286d82e4c3dbff96985229d711adfc95eeeed16e4dd13ff40510908f9d25ce5915c3f0e2fe4a641e453e9ffc74aed40db082d98308ef1ee36570045239a48a36c8ee7545ab0231ee81ed78ea17d4068079ca7984e89372ac3e47084f1ce649b707541765a9e5cdf029ecf03d483b3d0faeed687afe1d56279f12b298dc4122a3ed3afeab7a80ea431cdfd93672d83204c6496a0acbdce584ef7230c897671d532f05c053f2b411c67bf16f3904eb0156dba10e069e3cfdfadf7a84266eac24cae42e320d056428dbd3cd48cb4883bb740f3d57c6124eb8a2ff6d0a397fe36698a33f60d2735cd3fcb3b8523b7b4883c39f15c7a33cdc0b68725ef08368db6042ca18d680c3358c51aded9bd764dce081b8a60f244350e07bb90203ed74724ec338545f6551d47f79d169d5caf1395bdef8193f9c53e48b4e7a0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8ad512f378464232e8b81cbb6c3da7aa8fa221a4aefca2fcf0ca307fa7a71d57","proof":"509f7d31da6eed1cd0eca7e64e203edef7902c248b08e375dde678f1016f10536a7299dd6af97f130af39806c2dc35e84f25e34287544c8ea81a60dd85b2bb42b8d38d469fceadcb208768592af58a48453a9273cdcd28c69cd3c314417bed0fc2ebeaee938fba137315cff4b61876af30796e5b60894be4777db201bb46e526a182f013659e1f5a18c73ab5ebbf5a32fb4e38c0bc9f43a339edb68986aff902853f5a4261cf350442b876bf9c49baf66e5f9148bde689dff15e2a96d03d430e3f08fda6e6a0cb3cc650ce06db90387c25e1bdb8c9ab7d433268608ca149cd0c9e6563856b8c151748ed08649a93fd81dd47c323ee67b79339e67b152e593558a8d2716f70367aa1a11dd662526165e1eb68463465b4f7d85d032bd28c9be52d62342693dea2b1994d62b99543a24ba4ed8fe465eaa27cc9ab0958f1e05eff116eb562db82b5d16bf4c804848540525e212b406f8b935902fac0299baa5bdd151ed49bbc02bd4bc639e1cd05d5d4dcbd2f5425dbe6e88395dc86b871bbadaf73801322ca6ca9b2a4ee84ef8146bb44d4ec708844dff056e74007cf589e7857682e5caa2529084e27c28c9fb4bac674021f83bda47351eab526e21dcf4b9d120db8782332e78e8e74693c0b7c407b53e3256821fcc83ce78a071cfda16accf76248990bc16b964d2735c02674d33b10c026036385c28838c965339848d938de45e8f9430f8ffac4532efcbed1987f78c0d2552f2999613d4b03194a4df4784f665a7ad14c25ab0aca0561a57940724b52b4a4c6987c7c340bfe64536b97e7302e3a8754d11625c04a5cfc659d7e26796aa284d0c7cc063c15c231ed6987c23259e0967011216f9afc29ff0300e3da768be02abb140b19287fa40adc6ae188e2019d575b23e9367fd39d05a261e51c27e6747d1d4928d3be2185c616280c988e0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"88240d659f6b444d665ef8ceca06c81bc29cbfe8a2e727645b327d6da49e8a57","proof":"5e420c5d739a05bfe4e2356e56c8c5352fbe81a122dd71cd6722257feb551150c2ae607ac78e2bb50a6b39b0fbab8d7975c342b6bc494d0b90a8674dfae48d39a0544e1efcc9b3d2ca32bd79819b884f32f7740d98be2bd8eee403cfd41ae30152ae8fa9038857338a51b4ed452495e8518f2924c590adaf4ea3fd3c6a918c643758f0ebd2bbda41de3b99c45cf9e5e2730bf68117a322d7660776e058d4a60fde86c4dd6c287aa58921e58216aa15b0ea5753ad625578122c90073de7398905e0344796edba145bcdd6e8b2aa600fb8ea8a53a65392f205836b8aa0c748520b103ffd77d7b135a31e192bed3c938f15aa43ab4f2dc990ab6ab95306ca043366cac99b9bbacf457dede924ed1be9ad347f46ddd6c7a8687db07967c272549044f60c1a504eef6c88fdbda0cf90d2d46e3d33ec0ecdd6a33c01d1bd7eafae7976f83960dfb9eb98169c1c9803896b35d763c7fc889db22fcbe283f981e03b3f6c287ff80476fe273f2418ee346b55831db6a06e9098f0eadf55ad7bbfe32a712d56eb2aab0b78160d5695fec679a37cf4cac663c99f110ac4dc209bb66911317278cbb2336fc70717fe4615d5e7ca19902bab5fee71e2e3f6bd4013c062d6fe20ba6039b043c045751a05e122ee399975cecb6161208c06a107ac5de35ce4f5753a31aae8796c8cf3202a6e776b787249f7c9e57b856ac28f3bf72e0673f17b345e61df11c575e71b0123ff5c3efe06272bc1b9ecdb912c70502d59917c2b0324b8e71e4fec717108b5c418d4b2cc0d7613ff1373daa0a2dddae871fc6008ed7c2aee639fa0eaf35986def6a367344907f7a0050cc66ca2d876dfec9c2e3b6b4a1eb8658828bac5e0cddc0c6866bcf4131b9719f9d99f679b519831e9441213004d14d31d95877998f41477b6fad9eb2efe33ac22ad4726f2e8f3459c2887c006"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2077c45c40e887973b905e0ad1a95fddaeae730f2b1e4662c4a7889178dc766c","proof":"b6ee2a6ead3cad4a777ae17f45a2f1f90ac6640d17db9a5b2ee9028ca818cd12469034491111490ef66b2e9edb1c38ee27b9965f359815c845837794a91fe96b1a9a48e537807344c40bd8013b679ce0e25c50b96c69f1c1f8c89bf28be08b2670336cacf2546baea171e5f43131f813ddf2fff6175ff4099c2f4a95a26e4824fd7b9f0222c928a93667c8521d987c043b73bd9d949f4fc8740b1c67b2367c040c884a56754b9864122e6ac435a5bb276469628406df58f520ebb1685968ea0671a2c55a84cde4353ccdfb3b544391a33cd0ac47d6020bf7788c3db1b904c00e0eed40a00b9c11486505ac6caa0b56cbee689e840c1c7116e0342d385a0566331e37fa2e85fd4c8cf0d021f14645e98238a9a7fc583181eedd5de2fa5ae4cd402a2ab3d544ae3d6faf049a6f2caf44416b68c530616ecc8f598c64eaa43e7f1e76404a31cb1babb01bf4210b121c649dcd89a7aa805f93feeebdc8affbea291b6a78e6e75ba61ef812a307794c8319c0008429c740023b8ca7e9fab52e17773c0ee25cab8c08006f6ee8e770c888ea7286f3cd139bf3701553a4a2c424f1f92e486fc5a0c2bf42f7d985ab7c8980cc19779546d0cb810f21bcdf11c46101455128ad5539e12d3eda43c697f8213a5d9f3115375a5ba497cf30852677fb19003108f95567e9ab73c8b903c8f3aec8ef4d52918043534a0b3f44d9ee04b52c544efe43fe78586a953e50e71049f7a6d68d841b4d3acfc6a5ce9c2ec7349cdd89395e6b8727bd57a3de5ddc5ed7c58afadfe7e89034f84104a56a46343462993418847c4f7d6939d09db0cffa20b3eaf37e18ceb04ee1bae7c96202cc37888ce944c91afea91215639c79038b5d66127284481f8cdfe3a5b2e240cfa3fdcfabe50dc505153ec907cdfbb3b735bb235a1d716ff8991b3c8678d007ffac9347fdc407"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4840fc4f062b48a212247af15fcc0b066c7b9c09acdd45dfd5eca62cfa3abc33","proof":"5cc11d5d9473c7d2bc8ee41b516f530bf2bd865d19ee298dbcc6881737684121d2cfc38aee677bb35de23ffb89dd9ba1023a105b30cf63867af2aa487724546a40a71b1a35fcd2ce92b55b0cac97d836ccb16885d654ea99f64800be4d48dd0dfc93019d59831854679bed11907dc01e561582911b1de67903192b6c6cb22213f3bb4a00c8826d7ab79db56ba21f0b00e19ca9b96f9fce845e92b88783ad370a4f5995681102a52752c443f8381306816e6d7a41f86410af7cfe31cce482370111b74ee3f5085c68aa0ce5567350cd1546e1f6b75e1ef7d0d61bcdb2215e210596d6b1bf30b6f288f1bc0de0fbaa00057b3f5a139f9e623ece860e4d2d0cd343c25a22f125f7e4bb872ed6206ed9a64236f6f9374cff516b268521779ffed234144d6ab75165f01f9771b9ab525ec45f8a532c8f300a0d6b7f095f10b31fcc26d2eee485d560035566c5996021ea73793f60b11cd2fd749a72a460c6b6004f4a14b24f05a0b9ef449938e4b415f60de38d90e958bf67fc5ea4e76d46e9190541d05d2eee3c29122cfe5a68e4b4a4e86f1cca32de36e83d91368c0e0ad7a14c104a32d11e138b8c1e919e1d417c5b3a92bece526404bd1fd08184659456237f019ce3388a531b0d1985a4c3e5fd05e1a059c52a8444b826b03eb3de40caab947e0496fc51b95dbb06d20ea1046c5fc39dd418f554295b403c1bbefd3c6c193b34fe3ca20001041e4410cbeee9daa6e9deb83ec0885b64f6cfcb7c01c1bc9b642f16b85b654a10ddce7d2b7a1b43260b2878b03b2247b0d1484e8650df4ba22848cc45f2f45ec811417f333af603f9e2f9647d35990da127195ff634fed374377c59ff6a91db5d06b27b73ee03fed3a07a00a8efbcef840f0c0725ad8065d82c0a944efc12a652814d70045165dd88b3dca527587e451a87a2df55f8125f29740c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8c5a9a22e774ac6067439fdf006791146c0ed901cbdb38f40f32ad395bdd7518","proof":"9e5ba0723b2a1e5c1048ba0bfffaf1f8c64d80e656a5444dc8deaf884f0adf33961e5355dfad5c80f55d69eeb5f26877f18626b6bc263eca0a94e284a31b874f4ebfac311907d49f5a093ca7889175e421c9787e24592d097a2903cc74e85c468c31d79ea6bfae4ef40f47b145bdcea5055ae06c2548140abb857cc3e090701c9ddb52ad69657976598e0e3804295b9115b5e2433d6c8c32b9fc70bc2ad9ff0cb6c1fe2d707087819d89fed8451a92b4a968d495c3374d0621857047ba415e08f5a205112b6592fe7481f3803e3d25559ee62e01d32dae3026bd56b05e24f8067c8d5c74955b4469639805ceb90bc0c9d0f49bdba07da1d323cf22e596a31307dc3d971d07d40f71510c6e274f5c2b465465dfbf19d11ed33d678653a8db3b4bd056c3309737ba3ef001f6ab57aaf4a8ea4912b0e9acb584a5dc7cb01e97a6423862fe5b4aa48ebb6eb1bc56ebea09ca032983a55c0749173327a79000dc9450dedc9dc0c9d440e98c42c717ace481c98955c27d3fc23150dcceacf75c2f9806d4e62468eeec65a91cec737c3a1fc724018da63345103f5a5cb44ea3715f3e4af89de8a91c33bc83ffb2d215cad2e593dd6d2ad0d2001b0f02eacf752ee36b1c148b613e4a00d03978e31ef3196e026e5364c95009288fa61b0d2c86362a2701fa03ca4e3d50713290feccda9c4f84087b02a8b1903c8715c5148878575182226ee9b24de4d8689c14b333f25ca73291c10f8c9bd858e399421c66d6b4e9a00b4cb9619b1ac5d65be4189f8449086c3964adbe71bc68934eab5fd96751a2314f7cb9df02a9036bf00775054738094e31dcb70aa1e550790673ccf71841b23c4d07038c4d55683a188b531587c15079f41edd9ddbdb8c502a41fc2dbf52e9780bf6c2d7242b2fb9244d4731ab10913b57cbcac935d985dc68df8242d33cdb6202"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"54ca24bd522332d19acc387cd27d7b6200c3e8f5583e4a0fa4d1be2bb715771b","proof":"6e55c067002b69efdb0beaaccee200c9edb28e241bcd2be8e84db2412fed9f2c9ae1cabf9506aa3704a41723764fd521c7b413b49f0a5b7429e78a667111aa194c1217412e9233b616ce8f2706fc91627d3417460c01d0e72b7bd44c43c3be04b447c5a59b04d17b5137245dc4c3d150121910ee72d769b835d4f1dc3511b60d77fcd553740b6ca889633c462f6bf6a7c958fb7f7427f1645b8f02887ef40b0b7a66fabd4215bb778ceee4b49b2b32e6afcec8d3de5f26f4d8fe67090c7a3c05df12742f28e72611d02a47a7ade0c3250657bf6ff4b72e96a020936195a2000bf659d6b2a7eab64756fe3f8c6cd64157f26bd2ca560e669dd53c0bc635015f4d3a1a34f67d3190b3079184b4f3a744e83c70ff0bd89bf41d1812ad060054e23e50e907d87f7ffb6132983999989fdbdbbc1aa9cba8a6db78d52d9b6f47ba375afeb81816254686424309123362293ce46315a1c871eb3b94132ac9101ce84a5a7648db12f6d26348317579d4cb182c8eb99957f3dc4fcfec64f064f73e6b3323aa1f4e0569cc64a527c53e70a99c98b1ec89501df67ab944a0d5138c38bf7945d2f8bf63a101a57fcc95c98d5b5aa9db75be1f2bf7466f1914dc3f6ee9c3e812a00ad7c946858bced1b046a01cfc82676b5e9d01e8abd91140a6f09f32cd1463ca4d21f651ddabf10660b6a9762dff13764c1fe4cf705195020ce682478b4630e87c33cd7cacc7637bd891094cf708f1c2e00a1830c9b044219fc2d9e45f045828712dba08f9f907704ccfda4944af3e6c50b06e3ae95f2347a9e7507881b12bba27a501005ea978e2e3bed6ae7ae082cb3e4016e241f51b62bbca880129351cfffd484238668ac4dce020b791adefb58fb2937037dfa5a4387f53e6bfa21709e53661864f123bf8e7167a3b2582b3ce206eb666057bf287855b8c615408df0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f6ddaab4ba8259ff3d7aaa59ce86409bb0f5c22765533b3a81d1c975ff3e4253","proof":"7ec5cae4cc03837e8ab64c7553e1961cb2b9ef4819e5385810a6b034feaebc42d4b526b25b44214fa96fdf49f4d7d1a296268d37ff97052bf079d6e4a095530bac56558aad0fe57c4bf0666b480ed601efccf44074244b6f92423c44ec1d02435e2d999d1c207af92949bd453011589a95eeafe1bf656866092083d9d150b76bea3b0c84bb5ee81070b857d6a8d76d02ddfae10f1ee69d8c4fd4dc966373290b870a678fa0be2456b6483bd25213ed0bb614cca588ce5899129e2193c8733c0cbebcf14d8b1b2075a4d7d60d1b3bc751375ba9ed3c05b678c4d0d7ae4bf4b70a9eba3f044fe917b8f351d32742e77d59a94ad5cf8ac51e237f0d8ab887e2326c6aa039d544bcd89e69ed8bbd2c802b89759dcc000ab346d5d255b68cbaac403c4ca1d2cbbd25cc7236e811c45f3d643db10b1e9cc45cf5f2177104a7754ee023e0f17d1c4adff57823f25976b11d0a2def7e33fc6295b107f2a2b29c94eabc028ed07191c3985cdb4efc2dc1c70787e84ef956b858807c906035ba980ea9cf5c8e5aefe28d1b8e1c496d430f9e668eac540f93b3010cd2f67602648b2ee01936b25f92fd15484ef4dcfdfa5884cfd9cd11a37fdf764de1d844ea69fb26b0fa693a64e9e33f2645d1e086f93050b8f386336ea00a2b9f2c38ca72f598312cf037002ba00920bad88e2b147540e681ff2e125084a16451e00a741b6e052cfefc4e082e463dd440abae6a9cbde13b1222acebf911fb6d649e2f0e3df8c40a66213fb248396c1936ff5336c0079f9a66b9c4f860a74f62503c570b3fae44207d9274b2ebcf304d8572181bf92bf60a3b1b97753b772e980993dd84f0ea704068c5520f1bfd73813c46a7bacd8d017326fffae86bfad9b8b656e7bb753dcd000000038052529e7abb0fa009c3635752c3a453698dd638c8b55e6e36240c9130123c0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"30de5d5570d909e4b187328e4a4bdebe47ad86321b1e5814dce549b96a85ed09","proof":"7ad786e065646d132084e5baac057d1abb41d949c9fa5a5c73642b38d4e4cd65ee19aac57b26fdb46d624727d908f6e006d321713affe08b1b0c0a918f61544354b1a3cf0a07ece98cb306ec357fc0083e1ade3323b9e7d8462f16390ffcd655d6400af70cf78a6caebfde67ca0af3c1461177d11173afb97a4b1d8dc333794543a34f421e3d703b3ad365ef6c50df75c55d08694a7c803fe4221adb9887000b8a3d568299935467b120ac3dea76a76ee204e70d183d660e032b714a655e590b2930b1f738a8ca28fdc1e5236dbc0b2fedd9089272eccbb3486bd6da9fda4c0ac89ed427b3e22693350d09d44b3fcc5e727d3ec40283aa6a473c35021cd2991014a83f547cac2bb2c51d57638fb727b474c3df45e01ad79101f68899e9937a5cf2559908c757be592dc97407604a296961f3900ef4881305080e16b9e599cf487c4aa569c59e501b423911debb59bae6d6746bd783b65ecb4b6001b48d11a4355e1d496a0835bcad681c4250104e74e4b63596b896729f52753cc437e73efe00e2aea16d8a165313efa1811b01789d2895f57653de160419b736dfd8a5da093f387b69c166918c0dac494e7f97513f97c33244349ebe20fa0e36b3191afb575466ae1f14b75c9cbe2d7b3c0951bca2bccb03f6a09672eca0cd0de63bd4cf8222b60c8088eac8dbc1b2ba8c0093ebd9a28f84e34f89c3a6f3fccfa75e2be72b376aa06a58fc044113ead02a01e2225f1f43a0c39df9c40a447ad0a5647a93573ce62e6784d262f4b09d1591a9f3ccdbeee705a176f9331a622f3a7af5abca072b7e58d7042718ee40a6f91d466271fc09edcdf274aee5cf910665d8aeb612f270038673c1664537dbb018822d8795ba2b0449a622c79adad5165baeec599a4703ec7151e948e1ee1da45e06f031206ef1141e3575064d7231616b308b808f950b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"32c4a8dc8895e82703f45cb32e8fcb4774a6ce0889f9122ef22a96f027a1ac63","proof":"b01e49e6752951f0dc0d36ab0beeed48120abc86fadb404ccdadf97bbf78492256dce8ec0b1e116ce9073fee377f5a23fa26fd805f113094f8aa40b81483d02c60937de0fbe99fac76e73197d4bbb5eb4ccef47adc0da9da33438bc663d9ac759c7354e590fed10c030b17a34572a6cf8c2469ea0055b8c2ea91279301d1dc7d93ddc50ecbf755e8505e8b70df48fdf228ee97f58de618fb32a79da6ae7ea101cf5b862e0e2606471370c83860357538c611c4343f162de0ba6cac0c61a59a0e416e19fac7ce18c1e26792a2f1e695076f2971ee02c725d7d57f3aee1588e20c4c79cc5ed085d05c19dae59adc68c4c56d6a1e3697ed2c34d224385226d49845b0148eb4b2ec7e3348d964892c3d2789767519f995d4da4496e91e99feac4710cc314ae24625e695553fee9737ad9bf20c6179403f1fb66b150217e73a06cf005252a2a1c2af9e1ace29dd2d43a03d74bfa79915da4c3bf6130b328d6ba3b754acdaa667bd12c83618e6505e96d05e07ffa94f6384f244ce1e2fa5b776c6a5476c5fa886cc0eabeccb5ec27d8b2bc7908567f9fc5ae8cfca4f84a710900e174aceb24311c2f8f38b7fc5a166ab5b651705c026e831dfe0602555037d79b0ae7c80392dc51c62fcdb4d6d672c2abc7fc0a131a48f325c58dc61981813a73b890072bfccc89b27ac4ce1979d020a086f5da2f75d438e3db69f677a35faf328271bbca6f03399bc144f6a22c1231658dd5ea2d8f9553ca1af16c5d3ec9b6d49535a64ab3d3b11f073f46bab6dc55aa62c23ac76eb44f85117cd0fa95d17259d891394c249da38ae9152c1d3fb2026c00f8cb402d95fa080697b6e5ad8d5917ba97c12f2d4cf1d3a15011b34686dcfd5b72f3129bac25b794821277387935481bf01d3efc6bc72efe366158e9e3642ccd4c515a99f9db09c4fdb86e4547371204f0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f0b444744ce87a11ed2808801864c66ea148997b428f763d7adb2dcffcd0985e","proof":"0ca3c486ed8bab35107de097ab2316614cebd6e7024c64d24d60ec791217ff77a26a15f69c75d64e911935bafdfcf5f6f6a7b12b15bf5f8e7c45c9f5cfd2240e6a89161166bad1df87b7f6c5028ba58f4880d0ec449131f9f384e20f5afb340648a58615c3ca2e1336863cb7abe764a6bd1f898aaec54cfd65094f32045a6f4029e9a5ac4896f4989af4dca2266bb6c10dafe40999d4a5cdcf5482d7bec10007c9550c122c39a18856f19f2e34fa89accd86ad2165a80cca05a5cb7f1658d809597e6589dc8bb7a3b240888b79424186f2ac1179ed6ede30182fba39b309d80b0a69fbeb906f00cd737ae4ec59b39c312a6f699f02483018b258914c0633010c3a54446f2c670d7e6d8138d665665365361c894043e671475346265312749167beee512401b7c77263e9d58eda433bbc08cbab77ceb0fe401a20da3fdd02844130d6370cb8d315a15ed3ca0cf34027a554f8c1e4a2cf4191eaff86c0c0afba6a3eefae5d8cf411a5a968aec8a44146bdb6551a87ea31fc70a8ab62f3affe347e702ec72f6dfd6cbaf5b23f77c288669261ee0e9b825a03b6bfedaaad17378946caea7ffd700e82e34246780611fd5029d7a267f677639d4a633a7caad6d24427e8cb2a53cb6fb95589dfa6e5fc867dedce407108239c4c2044f60427aa2aa7071aa7a8bee274becf14d59849dbbac9d49df751fd7278746eed961542b744375aea9144d4a1ebaf728cf8b2bfd594b4b27dbb39e20a4427eabaae73cf1dd0fe129c60bbdf4b5c1f7491c9db45753c592f1cc238fb527054beb47ae21d2a8bf173fe2bca25e24faabdd6d4e33fe3e26a176bf339ffbf6ff7b2951e7943bb50bf192605d470723fd73229b8b4722e76316c1f8086dd9b2463f44e0b27fc8783760c275df4a8f92f3def9ca37b66b485fdc2a47466d2faefe97d390d0b2c52b08e0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9ae52829b88b72ffe9722ba0fa5775f7ef99c173212a645a358231156332b775","proof":"9ea5efbec7e4c8dc9444cf432b11e5c5035d98877b01e545b4c95418e1742319329ee41c48a417c089e42a60e71356c1357b4bc818f43264350d8e41838144743a49ddffa7d47b7609db48e748a58e147312eb269481f1fc38f97e58d7b2814046608e3af0132e7276dde8ef7c0f2dbbc68ade549c8445a5c3e8eaedb43ce04b07daa4e26b932aa2384dc5ac22ae871b5619ab1d052c5d195e0cd100de874d0f7b4f59605dfe4ba4cc7d22b1a8b43d6145da82c125097e9125134ccae1521007a81a027f3d303ace59b7e9357f015ee30607ff2c13c0fd7656f30d0c3ed45504ea46e6c83796b4f39e20579bf663c2b15642e297650d7eef47997cc72d3b0b3682878d94fa34240b82f09737f3ebc874f7f6756a2f34449bb8e176058ecb77186cfb9235ddee4f42014241406c0cae5b6466dcd35155156f343843f8fa808c505a0d88b0f6a37a3a7f9a91a9dd1fa843b43b933d44e32940b757225d710a6d0e200b1aa65d81731f1f59f10d701fd75fdff90b4c9c3b40c6791b5cadd88a3e50ecab9bc40030212434afaaffe4b8af59e3248c563cdffdab239669ca9286a84bcc655b6f7bd02435dc2dab7af4bcb35fdef1d64d3417849060ccef30c7671d4f86aa4960cea2325c8f02ce411eca1e022491ebdc838c20ee71d9c54de94dba054085bea1326ac70a03466f886210b1ad62cd7356b7d98631fc6e83e1e935c60f688dea0125d68530678a7218176136797ff7a222c5f42cd2437a0b1d0d17fe5b9c9b29dd9b8181df5abcfdcb488eed6ff7c86aa61a7780b4457dcc0b4e78f44bae9677c2b7096211f870328760cedaafc0ce00e94af6b003613600c46c19104f547125460bbe349e11d02af03a9e6d8a03e07db40e3fd01081971121d0a00f0053cf8293d109a4abb1ed60533da12d482f0926894239186e04b251309b144f0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"66830747ffe0d2c00045ec5fc9fa667715c21ed51c58fb26a759cf011dd27539","proof":"1803de325dc2e6de8b2be14addf350e7a774fdec227845446dbf6373463dee511a71211204f8f073604b8b3e60c67551ce50e095b9e02c46a250586fbf104228761b3c5042ea1f21556ca2d7eae9c7d42679379ef04ac8f6a96edf551c2c9965aaa08d5d7d30dfc5ae737613477020569e47f2ba97fa883915f85d977174937ee2a1b7fab58d3803970ee5e1328c6bec474f230c558d0f0be5ff95c5bce4d903a2c533b352eae7a005b94403c68d05ee45e5aec1fb494dcfa1222bb45479a20bf0c2ee6ccd61b434eca02c96a64cddc1180eea6f9f27324748b57f6d07f5890e5edf5515c501e5a52b06907567f58aff0d975a255b62c1044956e36201e5f42db6d663bdbcb7fc65c4c759276b969804f3f49b318abd0551b49b822c4f646171802c0821615bdf221f60df19687974b9a4e735c74f6eaf8ee761ead84ea7d15400e7ec9b308bb8a1689b3a84ca40659a36a9b63b3c47de791edf9cfc294ca012e228234a9e9a1140f49d572a0f26959e87e5ecf20a40cb9065f0363289317424c084b0d806d11f7a86ff4d6c9a0223334ab670618eaecb1cb856d8e380242739b473eaf9d37e53d6a83e243f1d689c1e7005d32fd0d98519987802eaa9c23b053e2712d9fcd87cbe37eb556a458c5d1b51be221fc8d62513b4b791e672b2607370a438a2176faddc56b3cf5cfd3ad33b61840803f1c07400c2d98724d52b612d883e642bee5bbbfd6fe0c273537a586174fa5382cece9aff357c45627cbbd452441b83e2e20c83bc9ccae4ac5194723dd7161f15992639b4c1003443949a172d0c054021a3476f5f6e73046295501b0e394733ddf6c4dc51d94d8dbb751cb8680281d217d03d1117cbab4e4343fce313b802c499ad9a8e884438ce8432670b0d79fc8b28e2634f0c21ae7e03f68a502984f87b30d9faf31b09623510d98b2604"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a8386f2cc0da7f6b1244ff6d245ea4b2e2732f7223544d5ab833b1d8facc3356","proof":"68d22c2fbf332466a552fee4788741bcd2903bc352ed1c2d9fd5a26066d1c16c46a3f977c740add9f44ed03ca64cacac7f2726d9bcc6b8d8aa9ac3bc5741c06836397ee0970b46582c404fa8f17201522e98d5df8011bb96ad34efcc01fb943a80718a3e71f8b736f43966d210ff5639f6291b673424604dadc684b3c7b9ba3d2060d5dbe4e3a94d76844fceb0150757cae9a435728ee01428ec982b19d0cd0eabbaca9275ecbf9bf3c4801176ac9cf2e464ea316d62771ffa2234262c36540594ca0195033c5c61d13dfe8c8239942443257456a7f955e7bc2f04bb471adc0606b3c9333712509fa4ba7f7e65a5854f358e5600f2d800171bc2acc493e0181744d849a502849cfec9c04d501993443490653d9a07f82032fd9511554462d71768e5acbe075a62a178781b39a35194a2d9e979ce75cfaaa9414a021396f87c2dc0946ab0daed4736b7f2166228f1a2007448570bbbc66aa1a56827e3f2cc987d3aa576cc42c992c22d6f3c6df64539a32ff3072e7e124bf098670780cf39cb175ed75026acf6faf2e92e063e348d6a922fc917dab5bb0591a0f86490d1424e3ef84307d8bd9b91803336fd99bd1525ed71e364e3244b76734126f1b5a2ba3e47cca492e8dc66d42c07fca82cbdbc062d729c221048d210e0d07e4d53c6487e0cba9be4de8887b606e188aaa56c7643b224412962f52c37776683db1fca3c7c301e78bb1b773c687f214910f78bd3c8fde8950263eda52a9d4de1ef06e4be7a230859590ee5d2d96f2179389291b1b0d338c3bf0444f27fb5818c91eb909ded702a60812cd27c27c56d61cafb0ecb27e15a553fd016170a0b7661bf21d1ef4e1ae7900ad29b27e1b99e71a82504ee9f49a1bb8c69aaef5c14593eed866042670ca2aeb54d6ef931e3f4d5ec6ce3150dfac9ac0cc073dfc57253dfe9f189595f09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"885480e86d5f2146ce9ccae31b0ba7bc37552e5cb64f3f4c578c74f2e1e5845b","proof":"b0e08828b454c3f21f67c14f012e3a7f556e133053b8e74ab49f63074d592958f6047115f4eb3fc72975a1a2c270ec892c686e68a992a90daeacb2318743aa75604641d9fb11a99ed45ed3459fc7fd7c3791af0fbbb6825598e36457a3a5e943cc621da1fbedf0164ccc193a67f52c00f9c60e5edd3096bb951937fb07603e76b32deab94467585660e7f93fb74437df8d35dfaafaa0bb920ad323c7b68e090eccb226d19dfe696384de7af63c933cac99456891b851c53292fee9c71036df049485fa37fa442cb5136620f284edce9f2e14648b977c4443c9a9ea2c3b5e78075810dd2cbc0b0697683fbb4f0a5486318f8b0f3639d04ab9e36087eb8c0a7d67ca9562f56ed3ff6d6887c528ac4ecd405b93c9c86c3e1a076e8b08497f63bb6b282927683157cc129896e872d11edd813714d4baf4ed1bbf4fa5edca8971dd1022eeeffac66dbcfe185ad90e5f76b1369abdf7c3baff18f9c82d4c60ed49f27d125260c894297d087cf53f8da1125e083d4e5110987a465c79f06c88acd1e601ea3d10e49053a3abd3fd6192c99014dde06f826b3d7918e9e057123627cadb7b4e0350e29ca111475b10bf853387df2dc6e07cc3f59204f208526327f3cb7a2838b60780d5de3dd0842a9e4d715b7778d652f78e6534d3a32e2e4555511242359085494bd82f23524d3e423d8da09abc8df0b53f892c68b77880fdbe8cddf8479aaed150087b30653af4c87235466fa37a38b18a271e4ddfcd377a399560e1234cc388d6147524b93efd25b781746cd6445788e3c5667a6204ae1b14580db7128c8ba02a136f35ec8031d95ac5a93736571e790d640f2772c40dbe9b9136690a01760ade0c4357df3daba80fa42aec4b7f10c93e13fbdb71b0b2bc27943fcc05f515e39cc296d265a7f55b51b7c7e010413f13a24cca65bbf1acfa8c43128604"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"deb2e02231e233a603ce9d69413ea776ca76dd37b9864ba0cffeed9f01627b12","proof":"e6af1b250e1cd4ef61d68154414cab5b85c100b77418ce9a7ec98d8a8b56e5089254920da3696f890cd5af3dc5e40283b943259707555b605e0bbaedf7aacb76e82924cf8bd258f2151a9199d8dd145a25d5087e00d01140bdbf41566b1aae114a7e9b2c7df3fa9f1d12e6654262619ca82c2a4e81fedd731d8d99d364220c683c4e333bc4861a8dcdc8ca95c9619ec19b276a5e440308273c037f20f916df056067fb11a06c84e8823d2656cdeb7dc2712b16f5ff5df3a01b41224be826680edc967256260e7570cd88f3a2ff4b2725b4ec80b87012aaa3963272710313c909e42abdd4fc4af8e1a74ad9bab5a18af5b5797f866252775ef263d621adca0a10b2e30fb9cc24b419e6364625144eadd4dbd9c14a9bddf50b6710128cfc2f31501042d2594bfa58ce6d407274cfd46018bdc6bb1fb08eb88b67adf9cc2803b00e8ea2569ddfa6d12714edb1885acc02e8d4194d401501cb902e384bccfb54d42c26391f2af3d61131892ee780f5a6fbfa8b7698d222ebd3023860a5813da971659e147e9ba0dc9e2d19806b9c06c7ac7de80755201258ef43b1e0e0e85911632f0ceed7826b9f7cb6953bfa9bcff0a6e8fc56f96ac8b230ceb1485fb95f69d4582481f66ba67fe342bddc770bfdce64b5833cc090a7fe61a9c672c078c847f832d26247e66ede431914216365e14724040017af08b991dff6612108db3ca06b76003c6def192b19b73a4c3f55ef60aee45469f67253fddf608985a01c5c69ff4372618f6d88af38a542f017309ce374fd68ddda188eeca9b54914c5f18bdb394012d1062ad7186caf655aa22fee94c77deefd06201216d3b20261bb68aab198023423056425e4ace10cc7a0530a31a9f165f573e573f18f0b13e83d1a0f62900f9f24ee0f6b276328bd461ef2728122b40183746c113ad12437ff877de294340f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ec4071e20de91340551bb5f3d9e0d023dd3f314f2c0543e420fab378ecb1cb2b","proof":"aa9f153ca7f739e30b8ed3923cbaf7cc1e8739f462b0088ac9b96aa28072bf6de4c9219cc853123f6a2b2a748ed074168a25f5ae894cd9ccd7295265137e9c0712c18f7b6795de359a593836dd9622792551c800e4e513369a1802628b303f5fca0435d55b02e1f8a582f9358ed06ef962d75273ee14eedce789f061e9886432bcf651aded04740750188ae0d50d934467f87139b50aa9ffdb3ee6fc5ec466012ad474f64e5f7fb83a5e67c35ba3005390a4165434d1e19eae7aafeb1824500f469ff9e2e71214e408377a26c7e2be5a39a6ac9cf0750184f50519ed436b4003c63b204ce54ac6b4e7469223fee3c53d4d1db0705c777e22fd8a1f852676921e3c2214d290538329ccb5970419bf05b0f6cad76477a3786531d2c6480c0a581436557f79e7803f9c7d3c74beb3f54105a30a56c9b0546e948bab59ce81eb9c3a9ac61f25948d716110dbc80bd5e2f3822aaefaa7615da8210a8dda4aa0d6db2558a416c1a7384e04a3d9b5a0bd60a37845d02f0bdd23fb4bde4c5273bd739a54a6e2649bf17f3f55a90e9ea9a9da6570eb31b3885f72ce50a76ebb48fddfb72c3ca556092d066abd3701c75d08c611b2a80b53085b08a7d3c69824ec67c19d3f0acc531bb4e70d970c004a13ff79278e55ccd8d82974c5e95ba20ba8618f3e4cbea4bcaba3ba39e6771daf34db65bde373d5de464ffc3601bb931b7a53bc5d78864087e9c6f7a977d947baea1fa6d33b242a4f3c7b18e4a112a51f44faf97c45e412a8aa9e8d03f4af422b220933a1c88ddaf5f6f527a8119641816501077524b2bf5edadabdee462db14eb88f9a12b46af0aa0c2a174b4b790345c1d3ad53090c4ab351c9cc3469aa16eeac0de1e1dcff89c09ec8bbd56c9f924181c266190c3e1a48f448bed7d9f862d1a4b2a9b9e87116671f48290b1572804497ed526901"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"60c0d172293feb695218f18b24661993dd49d75b93dd95edf38c97f96cd20051","proof":"2c85b769b41916986fb44b7cde291c98561822be13ca2f22ef327764eea0220f507026a1234c6d812c68e9b8f42fcea37a0a05e75030b5f50e5a0e11db6f704738db0f4093f0b045a82ad3a021b23a06edd37f61ce83acdb0ce8682d11343c1ca68baa25fe1e8df7760872bac3fd870e8ebcb5f757ee0cdd976cd0e393556113ab73f46113750a6a17dbc97ac63f369265538ed390f1d983d16a5932cfac660e2fcbfa4f0e632505ee0f574ca601de0e66cea0a998f717ac871bd47c722ea700fa901fa8794ed906b76e617f58eedc713582e20ab879152f8ab18004cf1efb0ffc6880cc873740719458710ebfda4200799dc9599b1dd736679d1d21e3f66847e080defd76facd6e639db001f18a871bd8c638d1114ab89c67f742c3bebd220592ce9461456dfd558684550423069b8a82bc941795b968d04e20f744305c1659e45192c39e9cc9fc49ac9d38bb8f6d0aeb8f31bbd9b77bc2592f1656c495693580edda1167e7fe4024e543503f08c56bf1280197a52c68b9b2cc2c7e23697d68d48bca1a6fa644d2d970b0e0915cceb876170dcae4bf44bd81979845b2d83e75a618ac7c884a0157423226fce5dbd22b590d03b05f133591ef09e4c17eeede678430146472620ea0db2e19665c1d0c86a3eac456e1387acd5f31e9fa31267032d60a37d6a25c506f23384b540cb56ea0ae668d641466a95f1c5893818b50186a1c0ad7e75df62034d2f0feed9d3cefe6372e7b82dc77262b8ad637d2f30ca17ae8012dcce1058e6145046ac74b4840fa904baf9b5509f4f406d84c1232bd5e31ae0025a7d8fb3d83f9238a43be632cd4993edf2345dff65b86ca9808aaf44a0c99cb67b39c482bcc9194a119ced4faf405ab0a36921ea9baa15bfb84b4e01808e89d885d5c01fec0af4acb93b2265754647d86670b75e7c2bdda1120b93bb80f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e08626089752d82537f0f00b6b7f4177fc52004dde0b7352c509d8ee5f551153","proof":"e68b7bf40d7e7c495409ba290c20a147a688b9ec7428c38a06a8fbeafd220768ae5b574283ea2d92e57b5d2f60a5de4692f22a5f9770175aa2f8eaf838a0c82d7c43be928948f8282e991d3d5c2c2bdcd6aace7a443dd2bdae2365d77a430a473e3f7a20a3424a451d7aadcb95525ad33e5716eca702482df73f02bcdf8de34a716baa31f9a6246621801df2c0f0117601c6abfc4fe5f975fbee7f8fbef4ea092fcdc7997640f9e9c9b517da0d91bf5554c146f86ae085122e92257b5d22770a5535185cb168cc88df5a2b14c86b13fcb76de4ea0882ab933f7be7946da2a2069ac028a98ca07f64cb7cf73623522260b345c6f80814836ad77fc5c78073c859b66096c8c19d6a620eb19663d3c3b90ef0939a6114738b521fe99de934d483661cc5a0e76dcb95e62d20650b5c167deb9253f30c18de90e4a860fe9aa9733155f6927322d263adca9323719f6c966fbe5281c56eaa238791b867a5fb7aca3d1a6815f5453e9fde2d3511d4d4115db2fbda01d9f9c16e2827569540f1459ff83b68b49a21061c53d058fe7be22259b542040deacf734410e6fd0920776f338161fee8bcebe10ee01a8c8ef2e6c7672128950e624ec7f38d43f90a034d68d38f5e3c1f3957cccad7c3543f9915f315b0477ef4134caae66cc2bc005c8450af376ac0c1eeacbf4b52c303ed449eb1429ca1290ec68ab706e82014780522e3973c2c6230c6ec6d36d3c7e37a142fb4ea0106fa7d80aadfe573d07315f8fc30272833da997b9913e94a25a868e1e2f81018e336f3fd1670b2591be95b7b0ef031986cb65d0f52fa4611f09e4639b72f4dcc4a4dba7e773e872ee978c1aec4fbb0747e238edbc0d6146676f8e9c5d5444439b9df917b5e38ded5f2e09d86c664116206a2d85e4c6c415bb0912d0c2c06bff9978edaa15f669ae1d08c134d3ffb450a08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d6fe7d7c31007a69e973ff98b50fe7c1b4a52801eb855bcf36a299d4b6367844","proof":"78af958117810a400577a41d9327290b4e243fc3b00dee2cbec78947e8b3d85ac6781a44212c54a309846fadc2e061d26c5cd75d70c28b49c96945eab0aaa25f945a6a93edcb04fd2636b44d0570505fa035c17566a322d7a1dc0f2d0ea18f3a7e008aafbaea48f0919bccd61b0110d8e182056302d007b350df84fa0c68f018bdb35ef1887ad6bb3f79a06136d41112d0e4431e0ca7200937ed8184777f610d56132436dd8146b4950cacc8ec2b7b2169098a1f6c907a0f4fb3a051f82f99091559bd9df1d3d7dee5dff9fcca635a5490c621fc30f7bb227877b59e391b1f0e1cd21a9486b4d00cbba9555acf8e280dab976d4fa7b4c0f4077eef8988766670fcaea3aa7c5f81c957596643eb4bce03227492742fd0d94f4ee4d43f7b08a83d7eb89cce184711acf5c1eb5f2927b313053fdd3772af908f790a931ced42fb698021efe889fb608c2c65aa8b59efa7c7744e14eed0344e1fea45e2b925317225b2ff4cdf71a005b183bfbcb6ea157b25a94e5ca807dee8d8a23749ba1898330e647ce360182f2889275c7b48277eed39bac5c69653aeb26b855ff54808a30a7528634d52cbf01150bbdff58768ce44c2eefa8d4b78126c16aec90cada67a901cac9ded17c29740034c02b3e4542f9fa0e5174e503ed6a72e680d0079d1d58935428cbcb664521851630b622647485e8447baff91dd706ad349dd75f74624cb22c03d42d9845ec48c2fbcfdf2c43213d4ef5f5a9b162bacc2935ddfb66cc827295acff46022e106e72db197982cb77bce731a53d9f89dd87aa2199fdac2a1ba5e5c09a1178f1c56a74a51f8a1d39ae226cb3482d47b1fa2920251874f5cc60b5b9093cc9e908eb101e571442fddce14f514ca15285071f5981838b456f5683909c8e5b013651cede3ad2d29dd3278ffe4f2199d64872f24141ec88d34018b0101"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9e9d6917e13bb5f906dbf9a1d2b26242769637958e416d0aeafadf7a75a06313","proof":"ac246b88a68b43a5a14886e5da22b6d10ba635b1e1d18853a10d75be98705e1cb4d31efebcf876e34484d6bdcee43a57cb0768d04b6fc65c5f3d9065851ece6be6d9c80616a53f34910f0f6e57fcf4add9197721f719542321b060ca2abcc6024823836ea527e6ad624f4275370884417c8bc4ed1bc89da0423a49dcefa7946fc5e8c8a5822ee7a5653b9c25f728087ddddc368a4b477dcf0a892cd0acc8050ca0f9f4adcf745f0f5fd784005c7d5bc72f3b6c7b91931b92bcbd15694462c204133f3290081b742ab59d50222ed8941178b2047e87f04e9e02ebc0e9b3a0420828fb7779e887e98bb3fb344f932fe32095ad7468cde1d3e651f80c8422a36d33fcde2f90365112dc1053c412ca5253a5adab1b2571d9fcbebe7148c0bed3542908ac6043c27b04d4eb3341dec03789eeb4175b26bd3b39c3e38a5ec07243a641fcfc05af9dcd5bab70f51d975fc76d6d89245cc971b66eb436d19c0ad3a8ff140e19200f083e049fe57662d9e8c8079bdd1203ee689fc1cf903bfdad1a001e111ad41143664a394496fa1e29c19b1ed75cbfe03dce16f1287ff0dfbc58197d49bc988f47c062ad3cd7e164f8ea5ccdf6ab7a1497c06e19bd5946e8ae8cb6fe33560aef755ae4d35354e314b67f304c8369d72b3056e80639709ee086cbf1684b985e545046cc1e0e45b6dbd7c25c7c7bd97f2d4a69d79ddf350956ac3dde3a433808dbec89399dcfa7427ea9b12e9350afc541005109b8f5552764405a98fc32787e2c3d2afb5ecaee79f406de6496c037311de1b46c66703a119d2f23897d2de25c781b409ed0b018d5a94840812a9a1264f7a30b75d3e85292101213646623afc36bcf25b1e568b7282a61ed64dedafffe6b7cb62da89497c6206988eb0702c3eee71200290cc590c63335bad32ca91622f754e4eb4e4906ac2e01482de10d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"685025ac95defe3b9f163ffa24248c76866c0508bb481c12531e8872c3130962","proof":"9cf01224b8a6bfe8cc448f9f20bde4955f8da1c6ba215ee333a7228af11ca1145013424c827bbdb3634514427d547f7894ad9ce4f4eeec93a28be1b5f6b60f0b4c3669a27c7e476f1945df430bb025d52ebadf87788eb17d1e0474efb3ffa50252573b6d33af3abf0d9335a891e863998d4cdc923c8a967fa43e50569d86520976c343461e7194ea1280e2e6ef2493741744e02ef5be05b44f5e89dd41c27e003f0a2bdb51bae511abcbab6474f4cb81101fe1571d3cc10fdae4bf54e126c40268eb6b90ae7a9ebaf8c0cee30a8f5d7770389189d4502acd77f9807868e2d10b125a839697ce174cd29e4d00935deac1d484cee479b61fce9c04fa2193acff79ca8654bfe61b10356984ef11889110e451fdbae3a5e1ad9201afb21ea6c5040d004b6a76fe854f5514d0f98aed1bbaed63e01a8105be09309cb7ff9a41a32617f2d4fdb8d289e0b4383ad4336192da7474f67a2253a46ef1d81f7216ee44d262b80197167171f115de8c734062e1c97dbeed63da2fb1b077531c1701c811167e1c873ff5d1b314629be883abbfcc009875919f3d31e17d3f03f5c9057517b9009a24180c923ae2566b99ed9aa85a7547577e0cf8f46e8b60d9aab8f4125497632a38b09ef697acf995040ccd4ca4119283fbd22e5b9ed0f8c8f7598f280a17577c6ded2582c85b8b006f392df27326d0406014ee48439509e8ca91fe32b5771c94f958b4324a1d862ad4834da7ca9ae42c42729ee6ca402edfaba220cc7a4e6d448ddc4291b23f4b5c83c72586d16075a6717ee115ecd352442310d937b2632fe04e14331d378720c222e1448c0bdfab42e9be42353fdf9dcf382ced67d8f0237db16ac4424a2b3733d216e01938d918996436d2a53f2a78268df2b11a6c8407b0bcc2d7c386505102d9e1bdbfd536818634cefe6edaf7d60cee73d1dcd5fa0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5a1a026dbd7b0c5992b46b238b08c79b3621b5f08f37331179a0c74eba13c969","proof":"c253ca9f7ed832a105348a12ec4240ba16274fe406447901396d06587f04a919d8a29f33e5815084cb5760a857d584edcaf65a73336eb0be2367af2f898b7d7342ee0b2ebd59afbf35d59ce63d41690aeef6472ac720697f0d27f759c3cda24f8090ae8af31d6f68207887ba2ec2e17e0a91eb8f2ade9e3216e9be240908fb66173fce7777807459d8611500bc623180d000a3d3d80f50cf2e4439099e346008942fb87273daccdfdd96ed9c1117fa6f2782382ce538bda50469fdf203a1210ceeaf5836ed122736e451846dccf62343a81bf0073c79587a583800ab5120f307501c097a4bb0162680eb929eccf9c0891e8eca90f84eba98361a87bb44b6bc3f4c21f51061728cd866db50a9d27f6cb7c01d044fbaa988b0c61a4c1a061488409c3649ae147b77669eed189abc4dbf550352130746a76bc888e3bf6df8542c2b768f54a37205aef92e87963ea97388e6f59c308ebfb485f4c4b0313bf68cf27c4ea4de1f2fce3c46d052936332bea0cf12c29bf6e31d5a120319764c69091a7ee2c852cbbbcf3768bfb68c30ed1c66eb2a1a0f9000311c610ce28afc5dbbb07c563c95038ac686c0bad87cc4a8977d0b476186da0de295e15da2f4a23a441e0e86028e578de0f255a26e930f8525f2fde6d43610bd8729a414e119f430a347318e0a07972557b5cdb59235f8cbf94169e9f5b26cc08339ddbc5aa832b9efd934a0017930ebfa83bd97582aa971cee858d6f60f68307e175d0bf423b603aa2919d6c8f1a10e630e79a21986ddac9e20ff5452dfdebe690b919255c4f6f6b0df17dcd89d7f643434591210efeee3926cb9ae1082676703409017588b3a73729350738d4ce6a74a6c02c9565af657f7c41db8c966960b915d80e7e0f81f97e54508b1d2d3f65c94681ad18bc3d7b3f400562f690631092d4debffa0de11ae347401"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c62433e19f05cbf8f5b28b478e6adf14486581c08327db4d58772eef6b96c05b","proof":"1859a283347f5df5a163cf26cc8be7d60af8b928c75ddfac341e139b2daad04fa4d9a6ca48a2650782a2f3316ada9f119cc4248727913982603dfe066ddef16b3803ff560a7068f5b2b4338b01b2be53011ba3f9691553bbc30ba436fef94719f63bccac62ce610028c82cb4d2d5a6716eff59f99e6d066ccf0a224122bdef2c2ce763358fed1391fcf3c8262aae3301886484cdf42c5d10323d3c5fc45e1c05b816edd00e704ef071cf2452e776dd3a6736e255af018747463c080e38fac705dda6806a8a85001d47c49ee872681fb635b7017432834b52b6c1e1e14667840270262b2003d50679e6f2f87c7a81752108fa388ea561d985111095209e19a6149c2fa8a8fb4cd4d09211a93a90e1d476df7bc8314115bf2116cc045d657b867d868ae7063859304f58b5500de4db2eaf928cc5ce0b3a406857b73db9fc06b460e2451e6889ca3830270538e69f567b08d2160ea9c13245a0a5b9074524c0612cbec7c25dec225f583f0c57e2be6f18f48c00536fc3b864c3e161c6932a9e741d905f929dda72abce0f2e6420b8332bc1b61bcb188e16ca217ba19e2fc77f361748d735b7c1d3a877161b9bc934e2d3c50501c4d97d573a56b4c39d8192ed6b6d663b5fea95969e8e49295b6c93e73d3113d50fa150ab0e627add8c95e7103f6446d1c17a74b18ade549c682cee73b332da1f2d61b10a5ed6189a3f505a254c1e76e1c21d74ce423943e8d0b2748e7d87e61b6bed977a29df5d0b5fa8bef5fa7f2e56f1e8eae71a0b9baee08ade7be5165137419c6602a5839fec6a7d7c1e0b229610d3753f849d965d5f0e147838baefb2c077a2bcbdf3baf58f69e1fc19770e178336be99218d6d057d19004912f8baab3dda4c1c7c781f92246ca7acb7f60e9d6ece3c7dff1f397dce6c2f50ff2f8a8ba620f7657f82718e282afed745c906"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e23acb3ebdcb12c2e0f1cb7e6dfda6e792bfccccbf1999aaa25f83812c50c317","proof":"3489be8bb155a340da0fccca69df773d9e998cc309a64465a63a899816aeb205cecb8faaa33044077afc66d96cb5e4ce41577b9bb33fcdcfc28a1a23c18bad59baded4e570a96c0a9055d4b9e891fdfc1105a755c03f28fef3d2a834801cf81ba05fd85d488b5db8a0f2282ba58b416221679c3b75bce3d70747d7f7a554d139f1ef97601998bb320dba57e0d0d1f5ea8d726c70228eadcb6c84a47cb437d309debf8132a6af489d607925079da834126c9e6bf61c281acbe0777797e4273f0234d14cf527ad0b6c138c19e11e09f1ded5365dec9bb33a78c263cd1ab39eaf0b3a8424259f5d18e215b2eb24dbdd98f41a7bbb719282c1a19475a4f7574135526a06bc0ee91310a0d8ae2ecb48695a05fa108e3602a27af9feff5341e7102f723a68adf8da0e04486a65dc465cbec9f8a6620d71a58dca9bb3f02485cc26030c96e6c8478e04c69fd583af3f24609725cc83842281d958bb0f4561f3cf9c897b06368b24d1fe4a1df2f5b5078ab799236fe758dfe1e8da417c6767caeee14d7e621b62ade010d055205a9cd5852e49e9f087ccda26d636f1c19e3c236a6ebc132288ded3905119eb6a705753be225f1f39120e6ca3a794fa14ee787c485bc70d5ced426b1985a0d00b53e1f1246134bb373217dde68e103ebc2d66b853566c57203a2cb42e1b8411b43688e24de2c26300fc1a7cbb8ecb81a9ff9577cc0b034200c4bb9042e6b24e15b8a02e0080af808fe415e53e7f8d44d224e9bcb97e7c6a188949de168936467f2f512d65f555b87b61d05869762312931749fca8d91b7c4ee303b7e8be0ac40046acb0496127316071278ac7b3864f1e22215cda4d231df3b31d047bf74b9bb6c334e095297731d986ec071567a90d1dd652f2d7a3d707065d7ac300ceeb2118274bf09c481c564254e33495b14b155e2f9978ca07f70a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"daf5d161954e81df7c7bccbab819c89feb8ec181322b83fa6c30522801524157","proof":"1ce677d7aace409bfb097b408f2c4a45f32b44d2285bba6a8f3ba886c9119e3c3c1c4105f6c30bcc93fa071905bea673d7393328e5000bfcda2a04377ad0930596d813ce3b7cdefa5bbcb27f7649928d8e59dd13a2a6bcd711ec2ddf863cac77fe75b233ec227f6ac79f3a05b1730b42786e9687e812d21c5e40a569cfb5cb62254a227fe2fdf837d3728ecd1e8a460933133d8d6fd5c01a3c333ad768807d02a7aa5afa4a41e6ac743477e1a83cae31670251c553254e8847fe3a298b77100dc9cd5ca5f7240a65e7fac45b542bb1df6af39db8ec30070d11598f0c3469d30682d1daea44a785580e58b62d5c68a11c78d473ff6b4e5ef1f07f49e05fc94e0f22bf1a08333e068b0328ca988f0bbd878c780f02c1d792a2bbc41a6a94ac8f414c5e0c4df5a629cb7bc2ca5ac6127884950999e912830f29c776929f72802a29b2e1450409d28abe40f19c8bb5b1227f03849eee64cc884f756b499ac5617e32c41266e467f337927d5a4a1316987192e6c8b082fb19d559ec11f9fd307e7825e249f320a1fcf69336ce285a8631867ed496dd5c1dfd19fec95d6ccced5c987fb287a27b347d3815a32972e5c85f5361f279cf0988cf7d8748fc82bc1ac1015f222c4f09e80bac520c719488c56c4c3703eacb3d6663963381945a51a9eb3e3728c6bf10e3bab01fe9653f3009f7b49434d566660f83985fd8241cdd0b999457eabfbe53a65003fbd8ce95350e8d94ceb4ecf53a20975ce0fde826a4dd671d5c5eb34c2b37fefd8bf6351130f46c2b035c9f6e0d33468bd84ab7efddda5ed13e8858f0aa05992f9042af0e4e5188ea4a719ef7d0439153806b202eab566df003d49373dd3bc17303bdfb0c89bd5637f799c93b99a25db4062ddf05f01245a803434e673b5a41be587649a37cb01222c6a46e346a770ddbb87786128cfedf6f02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7adb0d247f383bf52305f968b7ae35612818238770ddda54b17a4733d33a0a7c","proof":"9cce741abccb365740d471d8ffec3a13c42804b701a72889f681f3929e51dc11301756db5df856b39da1f6b96481dc1e0a3e59bd17e048806702517c897a7d5a049de14bec3d8de222a1e1334ae88c1a478c25b0c4f1300a8418f0da82a7a70a10d9e94fc4e63ad0a63c4fcaab3174e59b563470f585c35b60f0dd5d25c72015044f5484d0e33bfcb0f20ac5192f0edb53558bf01d527da3610d926c4b74f306fca3589c14047f73caf70f1d3f9c92a1998060626c5765fe1a6fcf88ea7ef903fcab8221da699eb2124dff044d0366f689d4943fbc21bc6c0cf5ccb6ef968604fec646cd924ba7e3de4137bdaa4d37d380d491c825363cf5bae5eeda91d92c51f6f43b7ce3a441209c3219f777e821032fc9334c7b464bf7d1f4ece3e4e60b281e8aa57baf45d67d456f70ebea6a7b69f656d7abdc4c22aaaedd77171b59d9226e8a047af9df66885e5afda7d34b0f0af877906794843cd5c44d72bea36b1b7e1e1390164f69760f08fe1a2f18f1f593dcb508323acef20b9db3c1b0d2dd7f3ebe3e194559d92c67754a86d07d326f0602fbb5f19f874cc98b8054c7ad17f823cafc1e02f76a98d356c45f52aca7527bf9084abc21e7c8613387c7f125aa64335e4c1b7f1b89f36cc63f3a82d75d4be129812dbfe84d3ec2ff5934e82137504fda6d8851555591d2c6abd9857c6f3a000af2cf6b8e9ab19bdebe5e286fc21c20f4f0b9da31303190f5739831fdd042a2a67cb0237ce4b66fc745099d0183444fca446a02173a23fbc6b561fb264e98b4ea3a7b55339dd56a6cff1b93ce16bb43f81d8eff8215449d466a9e71868d6739c386432c046f05fe0785fd6d0d97ef79be146cfe2fbabfe05b6b2f9e4410a4cf6bda1c1da54ec213879a4c3056e3160d98a1bf8f5052a5aa8f976abe783431b148303a40ccaa421c15875c976e7f5800"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5a8f871d8394aa62b95f1dcc42c497b53544f2f68795f2900d13455ff90e8379","proof":"7c401f525c64b9f3bf273297771ed612161e9f31590edebc2946fc1d18d7fb23482bcac479009e8432a5da110306039dbfbc57ba03a1959f288367eb318f9d77cac2c43d989e7124b6331fcafcadc9a2490df55d624c606b8515f213293ea9513c1cc106c8a486e30c536508051ebe993af4dfa5cf6b4e5254d53a887732300bad4e95c7f397a6ebb7fdba5e27e5331a673918e142ccbd05987add9c41ab5802bb57f155591be53d586310842669144d01c2d70ce1be9dbbc15b6e1a380891086d13620d86627b4ad8be8ed29def84ef49b1feadccbd029ac449f9628137e509a674b0ef79bdc3058af2240a8859c17033d4f7cc1e2b03748eaf18b78f802958069e2c90867d8283fa2961f28e75e5d1a8f0835075fc94ba63b0d6c2a5f533691014af3af990b24b32845550ff013ca55d6f058ea6da0e333016190c45761d72f235ca8ec1897d9b7cf0534bfbcab67c570280bef3ce9d1d21e5b219097d1445f224a386478a265e4b114efe13172fb8dd103db5188c1e8008ba322c276b381f141a8b9198abca73a361173f6fb70b2de73460d61d2126d6c0377af1ac7bbc5e945155969f3455587029b40adf28599650c1a3a545463d44fb648cb8437b306ed6dea888742f3230f8fa9c908745c86660552dec99530c127f97b7141f326436a07a605c84bd6d8eda11b4833e8387acd64295617129e70d143d70e18b14b3170058e5f5a1cc9976e13ecfce3cab453d0e48d2d5dadce752053140fd5abcdc1cf0112dda159be8a41a967e33598fd5e2cc66ad37e1c6eaa0138728d66d7c7d2190ab726dbaf54fab5ca9d69586dd31bcc6c9cf3408e9ab2f1286a3a9b425ae3216874b4812c0bd4c63bdecb420ca7eba2aa4b58f4d5b3d95ff0628793d83f5015f94d57505c1b47e37b7516464c69731c0ba85e0e50ab149fd081e3fa33e5804"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8acadde3a7ee15e124e266680134142230e1085f479ac02c0251a087ef2e4062","proof":"76e73d419e56e5a982afccdfce514db27b4657e500662abf9bad447ca53d5e44104ed6250a66d77729c407a5de760b274962c7191b14c5c33694a104a0c4b921aae6742d9e139ca009a887fafb16fb0ef45e49b7b539884d1673a4f6a9376035be4bc32a137bf87c22c40a8c18f7f5383d4e35685a45a1c9375920763775fe0cdf0f6614327c8cdbffda9cced0a0b5fe009e7e904c9e54254ef03cfbc026ff059ebf4efd661126320e3bec43384ed8a6c390a5f349855f2462cb580535bc060f0838fa09a595559547fd6b861219d5dca0efd15b511f6c2212c856248db5050d8ab6e6efc56b8ef7035ffb857d66f24967de316328015ce59b3b7f2c7dddce35dce846e4967bca52d41fafe469f6394a30e330c2557048ff4d2ea5adca7b4805367dcafabc8fac138bd9aea80cd12abd695618cf8fec4c22b06b5d894ae1523a2e7769e2f0f249fb545c3335f6a4441f76279ba94e0288768c43feb456cf2b63aed50a4248557d80b75955fa339fd798322c160133d4c27981ba01cbc33a3507ca713f31a3500855ae5e582c929cd4dc46205d604ab3ea536f1992ac1d1aa01f2a088d53fc7fb76f223dd1435f42fc44d0aae71044dcfe1e7b84a1696b4d7d6dd6ba8d91891589453d13b4b86b0509884216dfbe35ebe87fd135a57c3df25d67e271a1094e74cfa76f56b200b5f6a3c632e4d968037273f4e93e978a46a48f2c2a72456b2f207aac6ae6c4593c87ba50c15117fd848d61b0edbf173dbe2a6d7b44f5eee4195613e9f06ca42ee4da99b326b37998e4abd7d4afa4e5280e2ae31656dc0f8118ef1dcfa69d6100e248d3be5bccbe37f1f0e1224dabd3ba2fcad74eb0a0e78f1994b0357995b2393d094b2f0fad3d0a2c0404e3acd84fc76c47980cff737d38b1be744968d616d7476ac9349bc65573bbcf29f1764ec5bf6bc3aa0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"62e62469ddf6cb4ecc56c6ae4df21b65b1a1a816fe6fdd48747c164b5de8d56f","proof":"7622bb684abe10f86c57ba96496be4075a0bbdc4487dec2bf6fea117b62c796a6e97867f0cf613614cc084fbc4649781381000994b1fae4593e1f42c19290f5db00354856a7011c99a94cc89a99f0a9b8211099d0432db6834ce617075651351d4eb675d8656bb6a13ecb54b0372d489b14131b317a5885179ff0b81f203451df8cd60ee769c48a3953143003e1abe30ee2a5c5db7475d6668d5c0b8363ead004a1189c87a5ef2e12b8c709bb7ee5f95dc4448a51f93ef5feb3174b52eba6a0382b49ad400c32ccc3f20db3cdc4ef409761830fe6f181beaef85170db8c822040621bf49e8eb8babbd11a680b014370210825e3cdbd4f413d84cfdace03efc07a850cf49c92e6d094851417575b024031ebba5b9059e7faa40e29bd95eafdd6e98ee9783653377b6be0cef00155d4b17c16a7b38619917617500ce4b94268832307f477df96f48dec1de573e975c53b329cdae3ef0032d2422921042917d5737d2f60203276d4d12ef7ba307cd16bd5f488cd2261140cfae1a119a912faa01373ccf995191e5229426725b8a0628c83795251021de278fca0fd02a31e9177e74f613c6eb11d2de2e95dcd0377c6a9274a762aa66b77c3763aa13103c1597644cc068060531acb74d8a2f758314e4f0189d1c9166e3a8a5f631f6dd0d597fc177a206afd5ece1e088efce95e6dd3a8b8e51fc5d5cc372a12cc674be26055bb87ec0e7d86f99c7bb22288ad616706254ebf28f311568392e4e2809a64f4f8f5612a2f91e3f7081fa0d4c1e098c17733dc9eb1ebd3c9712dc4ac8651139b9bc297da659c0dedefdd52d24b81a3eba82037733705fe1dd73316ab75daafdca1d9a00f619d3f66360ec53cfe74964f6ed4e3ff09c8e65d9f69a4877e0eed6d83ce408836bcced7d06969cacc5d81da9c6ca74151151867329afd4513f00d1b77c0905"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"50707f714d0b204251f7501e02a81943f403e03e72838a16331c29726242be7d","proof":"4e852c30a9bc657bf4929519b9665dc8c4676d8f11f2b8bd2fb632dace8d6763846cc6fe4f474c2261b552400899f34a7aa61935303138fd55ba0728175ce3344675acc754ae57ff4567155f3c4de81b982b98157287fb7fc2c76bc3bf4f3c2f6c6793d098266494cec4466276e9e9a54a2ef3de92c8a6898f05ee547596ed6d83f2c1e87a014ad7ebcbc2536253c425d6801fc98e08ca415d34756acc08c1079973604eab671d44f0959ae8c3309d340649edd3858ab7d80e2bc5820cea480fe245b755b508e0a8d147bd3597638c61d9b9e5d7ced14f995c974d193982e8074c5a92a0fbfb8297a7f9e1ec5d0e7c8a87ea04c71871f575b2e03c5864e0550fc2f2f3351e61edd23f3b3870a2980c6a3576a354d0e2a8465b7a4a6ebdfdef30d45cdc3ccd7122a40d555b6730523a0514a1bab1839f2ab7969b0f5d664d9e59f22abd05ac6f6e646591c66242a943d789f67f37a3be447722d494d0ed0abf4bf8736dca3240295ec879ef271f23c8bd40c038fa19f2375c0a7903a0bdf58a4a003740c45df501e2fd74021a82bf2e93cd43e2d2bd8265a92507bfe5fcdbaf6226c505ec2cb9485832809a401e3f775f2c78c32b7891c6dd634b55b66088900aa63d67bc723e847ad9c130a65aba197b672035acce9ecd0f9928c2e7f85e3341e690369165a2cbc3f69e1e91c76bf27580bed3c9a99b53c63071447e07f12b037844c8ef6537c64d5997dd1d7fd0b91931248b35a958dd1367b6f5b6cda4946bfa2e57cf36937ddd3d208cdffb723e42fe65e9da9bb468e4ac85ff7671e2444cb4adc3e36ec494cc56024a22640a3be59d27d359022826b717e0c501d789f868a684f24025fda6a806a42458c287def0331b8f055c3ecff18eda5ca8de703604a4ff24a38ae90d8a16bc57acdf8a18fab21f8bc37fb5d24cc858ed103a3df50a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ee7013dbdc45868dc24739c2e642fd86cb5e1c327ec66cd6cb4753b84e0db869","proof":"265d7d427569ae516e0983b00589c159d5be1982b44519340834462b1f7f50124a42e90c83f672ed2e31992879b6f92fc259b535bb3af83a49450dc4f1b18a06768d85354d256c93e786e55f99b63aadb922454449054fafe1f0ca023e0bd753184eeca76e1aeaa1e23d47fce2c3262eb072035de161a7c6fb055be974acc8185695b0300e3f94ccdcd35be84feb06f0a39d325cb20b2aed921072d058a88008c8ca2de7fe37855d3c5a0f2a11341ca4cb01c9cae30116090c2324c086e3a707785783c9f38252471d36f50f54ced484a3e1604e4e5d5048b4e07e00e5c2f807d6637ae96c28abb9f1a2a4ed53269d7afb95ef3cb0611f00aaed7cd37fbbb877c8fa3a8e14ce24624d21499343b2cbdf5caa3bc285b978c3c2a1bd5af556c352400181857b646bcf5abe5be6e08986d4f666908e2ad5d3872ccff4f751d80627feb4698505189a980c3a1c056f81563fea579130ceaad06172c2e0b1318959691c12d6028a8ce0e2421886bf9c187b049b3a8f2ea286e24f1fd06c30dffd2f5806f0433b44d4a07833c741359c64b73a1cb1ba68ec6c583f775e82bdf2680e2c7c604557a09a23c475a143bd25bb4bfc0d3e55938a27feb8b5b617c826ef8b249e7be3fa8e03cd19f59c6d5009608390a63315c752006e5d17b3f96fb6f6893e00552cd1cc37a44e5809ecfc7bc5f5981ea877ecea377c9e4a6196fcaeda1521603ceaa2eac114b84ae372010082d539f0ea7d44f1abec09a1d2f494a1539f752abc40ea5757cdd31339777d996d67d8c8fb952b1b9f224ca1b63fa82bab845c98e8a0341cf1298994de2b48badebf0ad344af9dfb66d547916a56cb321b9f03a46ea0ad1c51a7e7dc1becd7a6bfbd8c856cd9df1859453ba60764fc46982b0d050e5ae2ec7b15fb00d485b3d8858c220cfb09033986a5393b3e82853325e30d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f495c19bf21b089cab6a517a00b187ce6d909ffae80832ef3fd71b8a7f617d72","proof":"625eb30d33515ae758c151ff32c53d03ebbbe25a25dc75a1a58b47d1ec63f25182f06c7b7b163b2f610f220c82e83c7ecc50b0bcd0bc1fd8f3ff7457468f503500dddeaa339b539800bd9202efb8c167b08b8725c564daf685c2ba8d99c25b288208e89259934b3b9a43a78e71b6f80de9bd1365527459812ed897613701b372fe43d4d3a159747e941446fec63640436b042d3867491366c62b05678aae17042c5f0f5d3d58929af028752d47bd9222a0ea3a2ff1a1001da585b892593c9f0ad702a8d95b28726e81a50b5a4df45b7820f9783b288977dd8dbe85e4e5ca33096852c38326c049ca58d916d2503fff0026501941fcbb42efe51526e81812df2050eb5f5c19ec150009a1016bb0fd6fe73b1dc72c73838c68687538c2cb4590360415284dfd224ece773f64115fd1447c1b401beba78c3271ee82aae29a495a075412ae1d405f69b14ad3d9d4744ccde591f8524c31823986e57dc3216fafa67c5015ebfc279d9f5191cb2669b9e11094e085f7089b2820fcd0eff74449c98804768baa4ba5a07a4b1094a05ee208f1dc2491d9be421dee22e100ad628849da2ba401819a15ad60a345ed2217e5371f32cf3e419b0116265060fc4ed203f02519d2997c29c266887db1e8a1397e1db61d63ee13dc1c1d15a80373c9134cd7e92508bd3ac144af66aa1a22d205010e5e9a4dfb6dd39804fe553da2ea4342f8b442a43d20764b892e85cdfe8eb488b8d454f634a1afb5c192b756e423c76fe1b038fe3243b6724bb61aa007819d0af4e50df3998733b7a2ef62b8566b078686132a06a13a32db6ede949b6d036abcce575e5eba5755e1ba465b8bf3131f862a816c482023bf4cc601c4d675a624502430b31d2e80ecc02768d18f14828d3543260f8d374e6d70f50c37e8742165665d2fc2560da34d813a073d710f45fc2adc9c0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8cda34cc263917c834d90d8417f90653b56d176d25514d8692e4a25a190ae530","proof":"7eb620a33b9b24424e4726abe87ffafe8c7034bc01b3403abca8297328d102158cd715a55dccd18cfd103282079567028475185c39757e222708070c96f13e51e41bf1b565dc83ad49ee80e3007bb2e62b414a68c03ad225bbaaa686b7001c43d81d9b5a5e36aac3cd4870c582ebd61733f750d26b0851f478a176dfb0696328a0020c968e8d3d7dd7f8e4114b8cce767e229fe815b4c728410faa6d0a90fc07120fe03ebf36e5260cb9ca02f40768092d2eb1bb926be747e74c9fbdc317c003ac4fe7ddce04523db7c7717573100dc351c15c0b103f6c52c5a0c5a7d457a102f097b58643ad29b9f3c9bd0403137b8f7c3f3af1105e86bdcf8ee538b660a41a08aa823845574d9a13f4429a95a59e94b5af6b288a8a380b0e2025d82c17f93604580c8b74414466f00caf4e6de11f02480db1382954ae7d11643028a91a311acac519e2b2954e01a68c6b51a54e2fe181d5c8753ff498f3c4abf3c24ff5406684aab3b1faf21cbec793c409c9d7f2111d1ca88c471d7b2592fcfefc7102374d36d778c1fcec418bd993e3422cf5785fe10bf303f2139aec187b40f39bd41612da14a09942346351fa0258bb602f8e750a4f3dbe9e8c7f2962ce025c0ec9a02c4cf19865ef2252fa6326bdf9e1fecb644d492733f83efa1f0e655b4c2febb8141656609166ddf877949506d1cf01480fea05727f79dc0c7384b0e05160b6ce3de8a04ac904e5913da10ee1c579407a1c37b7244285de7cdb3a9178cab51d2e556ca541d617c4408b3f18c0a42938043774a47ee88dc466b4bc806574bc3fdb38820aade7061aec7bd5fa6e17d3ef072f705f3bc88b91381df1f73d31435dbb432195a27aba425d8616f812ec7c2a23c168ee6d42ef789d672e2f502185ce330ac06742044f7cf5aeb11737ada827867d32219e3a936831c82f4e73f663a76706"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4e8dc572546fccbfc148dd4a8e09e9c1a440638401b5655bb356ef283ebb3e5e","proof":"c0446449cbe0f650103b93c5c2669b57c53c5a2b2f909f78ce66fc3dfafe500fd2aa5497d73eae742ca7e5794f4a4aef618c3158e517d33a529ec6ccce42df2d88928b2122e794e889680dce6646318f82b5a5c37ec8b6c23990c08765ba8740360aadb7e24f97ca07c94159fd097bfe7d187881cb43b59c5dfc0b6f4b634d36cf21e6d966cc72a68fe8e7968442f47ad25c75cb56146c75c028761b22641207f2a23ba987bd0f6ec7013e9cd9bfe7ae11b325be7d0634d3b349e43bd4dce5020c82a56815ee10d1ea6a54340795807ac3f8f3fc48df0232712ae4645daa9f0eec9378190d0fe0cc588641e5c4669cb1c33ae60484ec78fb7c7179a349a67c3cc07fc3435201bed6f1c080eb055854d1ccb860b92021e1e16e34eb177fc5d508c0da4cfa315b8ef1f23bbcfe95412bd2a5a4e7425b96e970eadbee155971d5511857af8f9af388140c49bf178045098a0d17981864db006476f5509ca182307c12134359a1f956036c908c3facc82f8267300ac978ccb40fd4bc72ffa523050c2e91c0c9c61b27ccd2b729ff409e06abd63c107b8fe250bc35e4dba32ddc95342eaa488ebab179019f8c45b390bf63a7a11bef1f8e56c8c9ec2acfc389a1732d4e2e8999e2b559ce73f8cdbf8f9b37e14e67061b1609fdd581cc5ace5f96cb60b89f922b9d3d7eabf9c47b9b67329a9e386445e41cf1c60409269dbdea6baa308853658cb944abd987ea3c38628d69bf9f39f1399055a6b22031f70a7d06c27520229feb9e012e2700c6c9912c08ad43ca4b7849b7b51b74db557555258ae855727b46188260f11f629ace4b3dff8905bcc3dbd847acaa4321954b1584f87b31d30c8f707f2f33d03f9a9354be70ba6ea79a8f76e0dc6b64e8219ee58b60c204b0e10dfb2756a29e40cf080254e1504172b6d62bc63ddf1db8b20fc38d89a602"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e8793ffefb7832d469c4ffdb402bd6c301645a5b3497c45d6628692d4ae10d20","proof":"1e7a9468f27f1cc8fb81faaef35155744ae3fba81d0346a606fab88ef7773f46f8b94021c4339681fdcf60e41fe7ea2ce56c915d3355bbf84068dd54fc716509129eea80214cb1056af318a4799e9e4e7a93646cc8c1595df8e1bd0abc802a7cb2fe538e3a07c380de270493cd2a556dab4ed6b2d7e3507ecb521dc7cf2ddf73a8cf3f144bbea47c745860dd2b41d3b063a1ff26b2ce66c6775e9875b246b106b940845b3f1b1b943450baf7fe99145d05c199e24827d5582f9843a9a92d980b5f608c47c4e6a8cb9b03c6cfc5e557d532910e2be6052e2b4b551976bfc2bc0f98da5a911e9204d99949d5807eeee3de90f2f75438b40c943063caaac22d3063b834987eb1db8f9a9eb78f34f265ba1616841818ffc701f57c473c75afc2dd0d3a800fd9a0a67769e979eda72028804a7b695f38aa7d450e770020bc1658e01072eca8b97f5195aa3de65c3717ba7a8d51013f0ce2651033c17e2cd218621b6abadc0094789e66be59dbd9e8b33bde7f5bc6acf4d027dae2f653b627d5de293ea0bccf168379390ec77ef8368770c79ccbe31e09f0588f776abcf813a528cb6a669e367a79404786cd90f9aa837477e9eee969abcf4585645301a5c14be4f80e8812af824de4a983559b3fc7897b20ce31c80076869baeeca2421290b3f7750baef65c2369ea155866aa49823acf334512c796f7415af997c9867eec3d912a58dcbf1d518ed89d00c6739890f52dc72af614a75b3d9322e12ef1f71387b73518fcdab843a98021e69d2e9de55862476ca17a518cec3b85f116e10e60dda69b5edc149545afddb591805281ab8bd892947f2d36acd81e1899fe31a3b7c63f2723da537bedad1fba155dc1e1458819342ccef0e9ff67150eeaacdfb950c2aed504308a1345a1d4d263041594ddeaf014a0ef9f4e251db6089fe690cd5771f2bb0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b8152dd7aa6694fbb034b96ce1fafd8b0bb911297f7b2022d31599da9ece9f70","proof":"8a1ebea336fda1860e66afd58d0db755ec72f1b755d8eda24c7c19a73a20b01b121811a9b335370e8137211664c6dcab838cd2846c58062c8ba407122a1c015a4eca609a4438ff476214965e6e25f123300d8e9686dcb23e039a43781ba0b82fac968f6db67021b6ae69039d06648fdca31a643712a19ef2bf70caca1727cf1e2fa33abf46201581077dbeb6513a7f255c9ddc4263f7a60411ef04388dc05f0695cbfdacaee5fc633f5f8eff8dc382b0e5740050052c498b44883e5a0eecbc0e9237f31c3f88c28b2f9a6c48a3a8e3d8f748cfe69fbad833c3f738ff93a2010cf25b7f01232514ae369529de79c233a3c5f6e22b7cf06d4a04f19bccf7ab312f0cdd814f26becd6935e8087a6e6ce2c46616b3667014c0cd442dbeaf3259427550a6d2c21778757601f5c3a56d9834fba23533e681aa5b93853c401b4f6d8a69526e77a90589f0cd286cb8bd33e590c5dddf953fb43b452130a642faf904af707625f8938859d55e5d306bc4af441141e823fc1a057cff5d35d7e9ad6195a973ae855c4478c191eeee777332ead31ab1f5884d0821221223f0fb99e01f3af24e0ae58c207179c07c7f2082fce643a91b2f56fa69dfd645a073b517f6282ebd61d64e36e3fdf82caa025d377b64964db25bbd78d23c13d2b3abac5472305d4b5f1edaa1f6b90f0fdbb3d54461c7ff345dd7f0a79e9d68977f34d609a1a036955654e623885cbfbac85ffb5f750c6223cb308250c2d6085cc77989199fc391996a3e993b38a55c246c473221189eb8f8da470e037a16e8a5d9075540bd353ca633ae60ddf9ddee0c43a3ad99983a9714815c04fd791f247630631f2cd99bd5682c21422d6391423cac42ca58d096ffa9a32af5602bd2f7a6e545ca0ed5ff3f7109aaebf32ac90866c35e6ad104dc7e480ca766032f1e5190e6837865e75b5cd805"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"569f4dc4f8bb88811746df09421d1694085b0fe81443add1bce108c119782b38","proof":"bcc0d4982baf7fe832d3c8f467948b63696251c4c9e4801e797b1df0a7c69b49ecd0c9907bccfe2ed0506f69a9243e42f1c133409d9728a78309f02019505d70582d2b8f22f7efbc1ca5c7b51adc288de67bb927c4c347082b3ad011fb03200a32dfe66b246d7cab3d0f31665a37163a78ffe90fbf3ed09fabfd131ddc8ea169aae3692e7a5ab710eb6b8675b98eb235bdfce39ef3a7b8ef65b093f0367a7a0501cb8597521d6a63fb02d6f4eaa01c38db1853a79783f25651263f3205ff6b09770a207eb74f868faeb2d813bc01edbf4ec72a62b4be32e29df82eb452ca64037074065a81914fe01403e5154b84c4c52be399b0fd7a2223aee548e49d391e4b02b0f38edf1b1af8e9bfa1a59e6203de54feb74fd18a5d9573050518ceb74b46508dfcc0791c3cdd76cc6fe41a813a214c881635b33c8c79bafc0c7a4f46405b6a6d354b61ea11faa701a4f7edd84dc59c1841533052172d998b23aa0d5571028e6674e457c98ab9ec6cce7ad671d071742a3bc74899dac5d3df45da3898cc7e5828b927b59cb6999fb3e84d367e3f21d7404a8bcbeca512f90029284d919e2a3c9599b6ee40019c649fb30053642e59f000124f8d1643143b6fbe3adf832a4e220bca419a5e283bab14c915797b9022319a940e485f36f4714b8794e5a76d0c7841c2754028e2d07e152390665fa8ae524e46de0f31b54286a7886fef2d686f5c7ca52412f63f79e5663a3af030b26dd6cf95b43862ea52a0bc97d9e2feda3446f4cef7ba48da320716be3af0e89e2a475200f51a65c98edfe98d408286d42944657368102eb1f2040d6e4d58937b58bdd2d00f78dff1bcdcafdb0a87a9847f523e35108b425b2d0132975e665b770bf7038acb26c9e9604d29bec02f50640ea4850797d06854407e72c4f9451c6625ab44d03fe69a37777227ee92495ca009"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e449238a4827db7eeaf3c526ece1adcd6d7edbb51125f3adc7b4c27f4b6ff942","proof":"de4c86c67dd24ad00b640a844b07c8bb1a630430da16801f34a8ee31fd550b70828e42e2fab16f8073b6373c122c2c1fe913fba025954ae232a94f8ed24cdb0e502ecb2e75bb502d00247bce99432357bb96ab224ea049ff62529e336355b1579cc9c0a0499d22932c7fbd3f1a90d2e9750941447ac4d7ea3478699213f428149d2f0c3aa8bb3c7caf55f31f1a5306c4e0c4e0e43e9fc2d3c8e223c214c85f0ec6f65575724e27c2d20a429fc7e773e3dd194962c80fd3bedd33e03eee10680a34cbb6d98ee48d341463daba43c75779d70ed5c33532f23dffb8699e2c3ced04fabf03f166ac002889f914474651aad186f58c6ec9dda675d65f962b891c2e7c64d8399adeb2a221b5b62ca67090bc60438a7db73ee5bd266f1493b9c2b8e62b50419534293cb32f53c623ec319bc181afc0db24cfd8629bcf3a4be4cb1e890ce4e6d611e8cfff3fcc2d1fa30a4508b7e54a90b2e44ad4aad8bf4c907e35fa0f84f27fe89f4f09fd50599f968f461b23f8e9b48f581a38182ca5aa1f0825786886df240309be02e7cc8d0de6b5c4cee3f8220bbc411d1022c04744872dcdbc64da2129d6b88697992482c2442d184b5ac2d72933352d84677fc3014ea3cffb5440058f71a060b24e3b1c4c116a775b4ec6e3a3a507f5dd7f7b9e0f2b74e836244800ab535620300e4d0b38f1f6a1c324094a60659a7fd7e36c790eb91d23c00a42aa1b1ee1aed4e25467c36ce323646133a043d520ae6a1ecc3626802e022539ee69e2b2f7c10c17d698531f4c3951be942baac88d2368161734ea8f7ba0666130fbbd3408765d3a943ecfdcdb05e9ec3d88ed7c1abc552759f89eb3fd0862538427bf6bf5f5a83417decd3019452aef32b6bc8009ad37f1b4f794685718e90012ff97a9e480464eb51a2476360e9a64861e95af9d1c4e6008f3877e8ff4ae0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b6f492c778c1f628d0df959f17af2843aefcc2888f2da9b08b4fa78da6d3ee09","proof":"c4ef4575edf202bf589690ee820fded3cdd596e847b8b30a00c2d7ac4a6b2b2ad8ac9528aec14f390e1d25717c1d5ab6a478e2cc5b087506bdfbfb801ebe353face270c747e959b31d8b3e0d8dc82295a720fe1feedc16c45d4ea1da8502b3416041d746d34b08c797218431f2f00cb4e041e6e7d70f49241ac983127a60db24d5cccc4a7ecae5ce2d9044bdec52a7e80710ceaf81c106cb4923af9dbc81bb081e83cab616cebea9bec58949a4d4433e51d6582e2ae9c89825bc0ad4fb64a206e3d89c43b499fed5fd1ddaf7bb3dcf8dd8dce946074c15c106984f4dba91400e34f2d7f8879a319dc4ad62337092f0181f5256e170372aa1e75975e5fc2c0867fe83494d2dae535c1ccf9abdf42f61e161da3fc1b3eeb4a1b29b76b69d8dde4b287fe78c793a7b83a7f8b166ceb02afca5ffed717cb9f9332f78dfd7cec7e11b205b6d7a137109a0f525350e4cb3f06b2ddb7abc6a5dcbfa034d9ec173eb7a37785e4f477fd0debc4a605d0389126d9096e01fbf29f4b5565cc35a8bab8c6c5750106a695c5c51bc4a59bfc2d1f976d2729d59b88862d4d345e1a16b949b1233728ecdd437377b97e94e3e732aad4190d81d1fcf8da2e3b6aa1f21582ff7323af8a8d7ba3fc44e038389b6caffc8077e77ff88f39edb7a17dc091f68feee693f200e14e3f09d9f25d032679a73e6099c7fe5d3a91b71fa16647605e6320e5c5eda276a0ce32792688d10ae5ee03dba10e5529ae286c08068f794cde3592c07097823eb29a960d37617aa972ed10c01aefad11274be68d4a60e0e8962834b6c7984d34d8d890df7b0a4d2853e8308c3aabcf3e8ce018d04fa279294ecd6917c4cbdcbd3bbc98fd497312d8c89bc33c9f227b60d3d9d1bb816c7bfaae56ee2e007a3a41b8ffde772fccc67994f3de59dfeddcca093781d39826da04ecf08d2d20e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9e64838ff1a4c686ef946ff532a6c33f76dd9515b741a544874d87fc3743663c","proof":"6e87d3561f46fd19c51c5a35aac19ee484b950db08e35e558459f3f2cad43571589ef5a4e780a271f121c5bcc5c78334a0338a90be7727f44752c2e23a98101becc75b2c4860a00c717c56d0825653b8cef4095d0b6dabc04bbee6c33e46fd742a2594794c70392a48cfab5b3f0fad17e13cc07a96c28c2a16614bdf51614a2a129d78cedf4eb67af1dbd22b6d54163a7512439349f41f5466301fbfbe4e290e972d4dcdf03d83eb946c7edb1f44f7d1d57e965507bf8f0fd151fce78a3b410118c578e6f94f81a2c4100d37f76262b8ee3f9abae7ad36bac5401bdd00af34015adecb2b4cac440a8b8b2a1ade4f89f729625799c753db5266f8802a49077a6d48ddeabde667d1840c155fabd9d9da80a73613889820f18e9b7f61e807762d3c2aa2ded4610c63713cd37b8da0a2b471e2dafaf17a98dd623e2bc39cfc919000cc9a70e707984fa1131775091a4ff688b2b11a293a41b33376aeb2abf2a83250e2f0252d81124870d48f97e22bfbba3d669d48f069036a0aa9ee2fdc9d114f535e2040fc888727087970bf557481b8416779581977741664d09256a3602be359fee8722d6b082cbd38a741ca327fa46c783a6dc44e19920be8e2e88d39899d007a81794fd46b4f1d7acb89f47196d5b7776d920c7cd35efc9f75a28f66bb0a797ae662331b474fb527f2ee26a43f9e2fc4e840db8f0eaf0a710b7e1e63cb2665d03bf9e0ad19d5004fadffec750c393d118cb19c8b02f9000a36c092b110276b3caafbb84ca31331aece45fe47b0c56b3c368c8b779d5bd71ad7ea0973fc370d7ec772638b47a6b2acdc6621a224f45af7c0dc4d5527ca1efe906678cca2ba08990499d89d4dcb32df408a5ca1f62e154f40f40a693b91556749342d0d2df60a251cbc2b66f4b7d6f4203336bb5ec5db8302029b835a6000b49e38a4cbfb4b0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aa729ab00b658d4a4f959d0a382dd23c86b565697514e68edb32ed88d056c54b","proof":"3aa02fa562480e7bd97bb6f4239e339bf2a82e6ed5f988b46b30190a65b6e942d6bd05f2d15e5979e084dc6f844505ebfe13f5bafeacd1652ea7cd94a083181c684549270e83d2996a421d20ba7ccae1fc711214db87803364e665fd2ca7361778682520e26baabf32c8d283216231280534a97f3c7bad72054fea576062a62bca3b113a85a156212f723ee2c142ac01ddfd6e05925b3a4d8e89a8ddafceea09cfcfbac3dbcf105a127a5ef62aeb6187e5f06830d21eee27c8f5a94cc65f4e02f4509a3853bf5411563d0cdec85a71bca200fda1bcb04575003dbf7e49b7080ac23c9132a496db2639e59f99ea7829c951142788719cc4d261e903b4e789383e340bb5bf956604fddcf3d2d3b5c74af1663e142e796b5802d8f93c85d82eaa124a2228ec377362b81a94a3c2bea27351eb648873b5796eb75a6cc87f07539b3abc1fc0136dc01a2dda669b14c922730be61b901c83e3cc5d90799d0a64ac37075c05797ed11114a6bd31bfb6e8976f8edb3300a704d68a2556fc62d17deb7c3ecc40687fcea34162f68b0bf6c52663cd722dc9794917364c11fa4610a30f207aaab6d5586f64db9aa34a20602dbeb79aef5420a6b7dc3c6be72f96b9ac7fd15d9a80b3ad343c2f44ad9a6755c7637aa196cb5af28994d4f11091cd70627273667ab10bb2228eee3fabd81cf12eff866ec8a3fa1b65d5210f1dc798e6a309746dc036cb265d6f79f1dce886b7f712e2ab871048a74435ca281ddf1c12069a20120cb08613700e84b8fc38d8a1f0aecf5023dd7d54b3922f24395fc2b8c1560060c8defa3741ae3dbb15201e58b8543c9c2d270980d3732e93edc78b434b250f69c1cf1cc1fb255eeaf9a62d8271c1c2c681ea24644d4ab8f00b35e24d8b843c017154dbd0693a7fa946a1e6c7983724d68c85f069b8dda1445ea14214945d4406"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2c1d1994a4e8f4545c04d02a99615aa6b2a79943df9a9c92ecd45a74dee14e0c","proof":"7854a1f1a354cda06d43ef56585621c85dbbd4b5bb88c57f35ac0d314dd8c25c1aa6d2e4fee8e053a475e169c11a65f4bdbdaf13b16a14133878a7d42f9dd77c0ca13a3560b0874c5ae4eedb74a1e2dc080429ede10122b1d650bb5cd6464525be19ab10bcd5c541987179b58fa424d2e9fbc91dd507769d9493a7376c4cd354c341f72b3a344ef5eb6140fd74f2d733323fb494baf21f4b58661360f869bc0aee0c7bf1b00162a0c08ebe5136dcf0fb307aac7758fee402f235ba75c7b6b90cbef1f35f286ed50e3b77e7c9b264e9b0602c6c38fa2192fa4fdc93a6d13c0c001ee0362e5454c1c6c386cc6e13cb8a22ea87084c816facea812d4a0e4440702f1ac232efa875864b9c2cc6796b8abbf56216ede004eaa5901d3f65372a5c5a2176d14ad26438c60f2a64454a3ad9990898a5d7fb48bc2b08d0956a8956c982407e8fbd5877f199369ed10b4d0a605087ff0e31526e78a801e03f6dd4e0f3be79a84d07a9424311ce2e84ba3bb1709baf701f46a4ed27a6046144cdc7d10ea7783eb03d2970af3df20843d7df772b41e67d39a0c5a8589a1aaaee7f4221ac7370bc1955265bc95d171eb5e52da2a5e0c5dc4d10ece5e32d74a308913a86110e6f7441fe2000722c978bb845d309a87f6c9491523fb5939206e809ceca4502fc655ee03f2eba3d1fc974e52a8db33064effd4a3902dda02768d323ce7fb01d9a58f84fb08ee8aa480308e88c424ab1e17ff4c2ca009f6d0c738b2e0481232c5b2c10384556c741fc12deeebe49136daa970051abc0bfca951196f0e46acc0d947e96140082d61cc332cf68658f3565d5f8a7ce25f58d021414ef632955e4f3382ce124d991321ddaeaa968589d1a0ed020b6da6ad974524cb9a45e52767bedb50198286ede8876b3a12023f16a57193724a6edd21144539f44da7504793f79ff00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"62d2942e8d5439269c4a00425b53c4aebfaaa5f1b4273925bbdbc95ecedd0c2e","proof":"26bd13af393fe7e0c5d78765f6d1ed84b54a09d31a4486030395cce811553d05e214cd263ba9788a68d054958ba8ed98c983b319941ccc5e622758f583318d4772e83a7ef85001680f8a030ef7fc57b9d1677cb3f748fd46b603a0710fe9391006a6afbe274f48a48b619745a785f0538e01ea0aaa732ddf5da905ac881f7a3ded70abc092b3ea2ce27a24a521b56af71cd34987970cb67292457635dece3c0c3f635df686b37d0db59d1d5fe9c9b1464482e90f22d1a195f0a90d6bc04b830ab8233d89e6ce37899afc350bae4be9c07f86a4f72effeba5f216b6310b2275087eadb46abfceee7e3a8c8d584d8f19584ca3a8afa0f0e3538e33f55f05b62e6a68daeca0c40d833fbc09a636f167ee517eb103d16496448246925c6605142c237e137c3fcd3bc4e47d7a124fc5aa037a4c222b030cac9a91e2f194c43fd3245a7e0d01a466fd98349ea4733a6df72ef352244899dacc21e7a2a9ae5f6b7c166a7ab658cd8da4b11ea91673359ed3827803873772ae97d2728eb905f9378d1c79d69fd31e4451e18b5db767d1b991a03ea9d51505f5e4710aee2ab8a19a53265c0450e6408ba8ea81c0c14a55948724ba37bd4ce1605694809b3347041f4d1e7b0e78be59b1dd0f1cb611b3300fcda12f55f0a3c486be66bc2d272758474c486a22359e01972c4ac2b7f5dc2a5295f22cde798526b015ef90dd8527493a1df9065ca33b05b9c6b7d4ef2f594be958b0fc0e868d5ca33626e9e9bd0fb851f91f2a805818f419813e8d0d45a3b29082e9330dd3a7b59b886709836e49cae454b127a618abac8d5119e891c08155a2a1cc4589ca1071366a1299ec50804583c6bf27251eb4d6edf1df8c19a9acd2b4267a5b73ea17ecbe6c0078034f00670fa2910e062c00db6fc9585b9b359dd7210917bf583f1ac12458b42cc542a073dc18d60b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"981ef0e8a6a1039ed5cbb578af44bf278e9e910b5a049b05d362dd18ade7d17b","proof":"6aaa93c6597a49ae044d5a20c1e47fdcf8905614a770365b706e2b2d9ebd483338325bf01dee93222a5495ead1e09482478919471d929008d81897377daebf40b274eae35ae8d606435dc291eddc957e64a3b7ae4c7e4c0b918bb4392cba051c80b85daf3030c20d8d9649f245f2cc6e6a9c2d60f4ae42a84228573cb5c8ce5c7375cfc40945797ea737d5c362c48cce91d9294bf6e0c4a8e372fbc0e17d3402a7b5584217a698706872f2b6dcc055c195ad97f96daeecdc86abcb891de9910497f060d5b2b908c87dce2c6fae5122d95f270e09d7941c92173127057b36620e9ae67cec36883c7548765b0015bbafc8b19a8bab45c05814c4ea927a57c4855f00ca0500fb9c9cdeadc8497cd725851c91607aab51e5a83a37747acad9b53462f2c120d9fd4370385e391ed3b91c6d9fecf7080c744c03f9626cc56ca2e99f2162b8188f7e4377fd51849c39e881b338f871dcfcf13d31b69ff936eb9a067479be0d9e09160f83d910f65aeba406c334213814c12f48ef3501b5cb497f71c177d2f47091f5fbdcabf22356c9749513ae5e2ad4df6fd4ffb060f4ada55ac27430a220d5e515cf4ac7b6e7acf34e6d5c9a415f78a31f781e4fa74face4a473733888a06633bd81c72f5c6b62ee0d59df65f5cbcaff14991f78e8370a9877960641b8cc355ceddc1a2f2e8bd641284734cf5b3997932cf0dce80aedc5f30b4a946c06214cb8ea8899c0349f89360e8e17c3b635d2b5443f332490a07310127f8d2a1885494e2cbc091927d90493fc41b16ef3ae68f0174927e612aa029368df7948a8b9cf184d3b903d6ad4338de9558642c6c8aa2812509c5fdbb6a17d60f40d6464227642d9868c4965e0f6447cc83e110f8169f976364d2291e9b05c117673086937da36c582c7cfd74feedd9fada8713a4898152b28df7d3afb95c9643b960e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6009d7b72c85310ce93f352ae65377baf89590f843813dce06f414b8f5042d21","proof":"f45380d4f4b7a98e2cdc2fdf37e1a255eb597eb3b40792cfb6f789ae51d49657ec005c1827d01cd47fcafb36fa5f7cc0e57f048aa64f993d3aa6afc1c67d701f4c6271324129814a0bf117741f967e8bf69f376a73085b727f225ab2f2e976400661d87c452a5ff118a8970b9ebc6c295fa793eff791a4cbab0d771010063e3bb9734ade8cce304ae884f9e007eca4c10e85974cc5da52b09eb4c1de81932c03abda0f34ef438144b0012460b4114243dfc0153581a5ec1daed553d5dd449704e35205ef375fbd8b4b47f58018d12bf45f54a785ab6b1b7f5e665aa09bada90c125b7c6082651841cc55de7f133a99e50372d806f338487cd937db9c9b0b904cc000678cbec8bcdd664f8a3f081ee8f06cb172f5330e41d4a8010798406ca30e14d0fceb295c4152674b4e1edf84c623eee6eddda62da73b6ca6e24d9a6450159c3cd9a4100814acbd5e6888ddb425ade59bfe1b3efffbafb2fb54a14c0727202e6d45cf4ad4de8ccdd973493d995a567950555e2a29c4c45e00165b185ac315d2583ba086c669a998a6e89567a9a99b69acc9d85d2dd0331aa626eef59b5d080840955091c4013399384d9b7ee4a25ecc1874f0d671285ac1c395d2504a7d677a2532ed226b56f69428f539d657375f5d7699171f7ff9d744070348ab29e13ba8df6388c7d440f315c1db1936b33f59c2ab6afe728e2d22d1956589d2dbed151a178110accf676b93316bbf5e9ce03ceef6bbcdd3f33e08977bac049926a51162a61b8c223799f111391396904597528086697d86ad744e28d06d6a2a4acf08c893ffe8f45e5ace2a558ec0a3bad94bd1fac9e1e7cdf0b302996a305c8f5a45aba9ca382b81767560605d90bf733845b42c989d7eea12400ab5f33f59eaf40865a3d9a5c0a79dd4cbbf0e006b03adf623d00af9bd15aa14d4552bad1f4eec00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0c90352f8f8b3bbd3937f13f083fc575acdc854b21269a5cad6f0c24652b2571","proof":"cafdf585c22bc2db599ef11c72a1eb5eae73f49b33a0c20bfa176d3560fc8177624fdc4b3dd178b5dee4e0d4f58b4032f90afa241ef9e8ea1cdd6ab4ef5b120e6a0f622f736fea344513fd22f9a6ed1acc91a0e6a9c660b9464bb2170787e75fbc695870e2c213cd470002f4305c32f9f5d520cb6c43a2c9308d3e348a722c12ae699c4e0122df033585d96f519a7225afbc9649fe032a23a46834e3927e150517686cf2b0fb13bffec9a3fe19d2680fb6c81bffa9f5c53ecc41b21d0421340d4ef8849795afae5bd58f6756c274dd75e88aec6d0f4ce53c48cfa3afa9326b0b8c6c2ef3f7dfbd3f0d164dc11a623b38edee9619a119b9a63600a1907d246a3f2424cf5d8f40103a84d414180a28d7cd81dfd45685dfaefe2d9427a2db407257d6e8e2a6496b7495b5a193d6bf0aaecf3f760eda49b7ce3036fbfade55e142566a7b11369bacec22ace53c9e08dab1ea9c70069ee19b4b304c196914cfc3631c907d7ccaf4394dfb8d3ec7b7f30c61525784fe2482c3adc476a8dbf9e19b9d7d700f06bc9612e5511511178bed109468bb9951117fcb8b421dff909a06030c7066e3c4474cb1da68b2226ad6759b1d3e8e8766d0d6d704cef58654c9caa2962ac49671fb0243670bbefe6d3b56bfa662cedd517137947bbf1c0f545ea7d0b0635af70052f77defcc37fb62b546e8e5a1aa1c631e3e8ea0ea6dd11311feccb342e89e2d7fe7d43bf1392f276751282b5a903264ba7a6cd906bd6e63d2ba15777fa8d02290eccde5a43a3604b7af485d644e5ae2808811ce17cb5aac07177005747a36b9039d234683e724ee047758d50df739d49d1db9e839089717fe779de91f57b5961e2ad37998608ccf8736bea33064b85120fbeb7d81b1b1f9a071a26807ba5d743ba154364eee5055d5518ef59cd61859fb4c04b3e42fbed0cfbcfa3809"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b40df2b76704ccc42109280168bcd5ebb4064ad6b74db7d904f6e77d14aa2638","proof":"3ef9f4391a64ba85c5f7df8a10d2ca74352526f4a73b9e71333ed8837e19957a4e4de45f6a1a54580976290a06f66315f44ff6e1673c5bd9c730adb527caa75d7e87cdcb68c015e24fe612e3dedc66a4a3ec6d2ca03e35d27e168486df3fb50f20aa29eda76980ac0feae26ba0296b0a1fa443ef2a77f7f62e1e3f1bd782577adf80c7e66051872956449cdce36ed7c168ab2545524480f4cb280a75f1ada605458c461ae658254b6ac5679bff2bec732cd487359a27fb144617350d9525e50a9842638384f21a15000b85811f9e6364b2c94852c13d67d229f6f5a53df2ce0ada42233f8a3538ac1a865fd9911eec8ddebe9065b0e2c0c04d704e1e0e3bfd15c05fdd901620e9078a8e7e5cc55dfd014ebfbe16ca67092a6c55445395dc843158be6c25611030bf5f057d0469194c834880197e920d0648046f244be664d244a40c08d9544e69c26e875e818804f92b9e044c62677a7658412f580b1b2b5730c0165ae08178ced61afbb5717a823ca76f8b55e39c30157021ba8cfcebde1d0e5cff4300d454bc19d9f4296f140196e6d4eeba57a8a302bb2e06b06357aac672c0ee8460e04143297186df52ca403d41dc46e4d686304e7081d25c8548f48e2e4e843b23e46ea4dba54d14d2b010c2f4198df193757ba227958f38b6a06c793bdc7ee55bad163e49ac36ea3e333ed5afbb31b98a5d297b6e797b85156ed58657cafc6e1aaabe484f4e1ba197a96eafbd27d85e9d32625cf5fdef8d79b586f175d080cc737d25f05d820130e8c998a1b7934d6a51270e7d52934478f772a2a178f4d07d723ac56b4a52d26fc54f360e011b0bc9352f802de476683f3cac6dd3320cd206ca69f5b1f57054bdd7f244a2578c09736a30ebe072067e6721e59a9f0332bab43162561aad006497f92e9d929c36da20d508937693d5c3f8a044309b0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"eaa5339bb2a91aacfb99c2b9bd400e8b1bf85bf3cbb1f097836f368e640c4832","proof":"38538cb27463b0e829fb7aad66764d67fde4dc25b60f8a3cabbba4f39a50ab5fc046f7617cd74da73ef20797c469482b754ab17a36c6e9e8e34659ea9ac2c62f4a3cac14490496a2455eca3d8005212c581aa3b154afb5577a7abff96ed612088e3156330a82e2e2bc8f6aa1fa062d965378c93074bf7773fae17f465034cd518c5fed406b1e97dd08c7a5aeefbcd14c333b7d36ed2ceda39f410399b3d8660fe8e37150e57496241a07963bfcb6ad9524add4cb7f6d66f0d10ea8b48cdc72045f5325b30038556e75db08371dce10a26ee6194062638b8c6f24796b5defeb075cdbae9d2098b9a2b4042dda8e24f4b8b222591e26e229949511daed0dfc1d439284a71604d70b22e5dd5b2d09e1f3d3ca9a2921ae5e29b4c9010b75a09b65625485153e3f9c8f3ddbdd12b7a3654277326d662a73d1a634d1ef075af022884f72baea7eeba44e40a0d6214875a0f635f0ffc7bca4ffceca97c8022bf6e43e4c60c3bedbcb58312312d82d4b0f12c0e31230618415cc54659533e1b3c2ef8c270aa70c695d78b81d853776f94559e3e82fe66ec8ecdf6387507195a5be800367f4e586c3e5ccf69d72f945d8a50629694fef96b7c7c4186c5736118909e41b1e1cd369a9d1275fa2b0a500e0a84efdd88de5df1f3b56199143b282232019874d0c898dfadd7c084598ed82479587ce390807a2a9e1403fb6cba6f219a4f3595e64ab8915396556e81844210d092cc4b97d855e5e0e975dae74abb4b4d10bc651a2c34d5d4e51992f27e65291d2f7291362799c286b31e46dfe88e8bf1ce01b77bab3ead9c4da5d27e209820d13674ad3e9bb9648759912363d5236b2fa16b235c63fb9ed5953782d74fb4a63ae23eeaa647158814b7fcac990f5c1a5e30ddc074c4ede8b960c979d193f87f7a928cccb82b42016d298f60a1476aee3101a8001"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"961bed04746cad3488d12c455a44d86314a638176f92bdd724c2399b5e32ca15","proof":"ec5a09c85b691d3fd90685134ac9fd0083a65e9a99def2f4f4f85437982ab07f34b5f2b51c4adcc7867c4cfd681376bff7717487fedd84c1194cadf24a11c230009f62c89cfb3cb6b107049cc3a4df6651bbbf0cc76da251a7fd3473c4a3da03f48137095b51d0af766fbdbab374a190cdb6e66e8978518609e0975ac95d4c347c05d87cb162135c9421441c81585bbf631b0c0b0b8cc20401cef42afeba10050f8d5a3345e2b117c6a1db2cceb6c3fba1fbacbc68d6df99ff6d92d79e116e0593c1e3fb20025f9fdb73e7604680a894713cefdd57cf3a4b41e33f7d5f5d4605caa0264d1b56792fa86279d844f4697fed3d752d585797496b352657505c115e06b42d0d29083edf45a7ecce70ddaaaed0380e6ab6df359dfa90a13d1a9133272c7bb2df5c66d26f40cebd718d69d450ca6ea4d49e5a7812bed85659388162029ee3ff33c85b59fad59b85d3961d741425efa161a16d0311ed0e9fc2af851e581a23144997cd1e52090d1629d625cb5d97b7ab4674edb9c83622f41cb52ae06924e63b1305c3f7701f1a87d11e7f7e32146cbfdc91f995307d6f177a17bf402dc0b6234a4d1ebf097e937122a36a35c92c27de9e1ed034ac45678d50efd6072154ca50c4d102deba77d94ddae2f5dcd301a7e8b32b4ff9825d07645039e8af43ccbd05093e8f24507e66300ecb1bdbd739b6ede46ae28f148faa80df10d6862f3e3231e48c4353d7c736a5b3998ac8d3bb5c99beb69579633d3ef708440ec65ac83b4468f6767e6571c2c1de4e1be440a3c8cbb49eb5fb3fbe762796179146005a2541a3f87d046759197875b12b286b6408450c3297f0aa2ab181b30964e17a7ebc803af979903cb7870702a574d1385f9590478edd32eb4c0f59934d71e704819e9377be00b63a370ab9bf54c4b72c1ae01c73d8bc8ac465a3832f3d643906"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9072c0513993503433ff89f954d5d3e15e35e45f2a0bfabf2ab32162368cbf37","proof":"4cb45a8ec90a8f610e03e3ec88396932844965be9647a5a941df585f5bb42040d27c1fecc6d59f1e6d78c55c1eb6abbde1bf63a74f6107c1dbd5f701ba9e231762f549c5cc38261474314815e910d9bb105e8ce45942216f3d9f9c9308411f200ee5cf762ade56370c281f13be73fb23d7ab149674f064afd7aba5a29e06d67d9f89ff5024f8e05de57958a8c99159d0fc4b13fc5a7ac8412c1de40a2f3db60a63799726fa941b0256730b4b5d1a2bef27fa7f3b486f543489468fb7200b6005a75b973dcfdd6554f4ea07ac53006b0594fd522108838592506f8dc11f88ae0178fd4027b3bf47076cc24f3ff545e500d91c623a116363d1af4aa66c598a3b3e307ac3a418b43c007cce6f770e61d61f077c8fb348e587dbf8e9cc965ec9e928c06a4e776bebe9945f64ec2520866846e587277d9178a8ec3c3acca674e5d767fe76639ebb907d1079fa2b1592afc4e0336edcbb5a95f13a54d754e59fbee274429e594d943fcf0a66075af18ab1e15b799413274d8660c266883a1222e54e0e5e2c5bb8249c698ce2b159d654c31126c63dfd8386a0e00d8758f98177c82615a463a11fdbb68c39d8df3bc3da3d830879a73237264598424a3efaaf01c8960690a908310f6f9fbbeb85231310f4392904028855c6bbb7f65f1179474bc157430687629b63536b3cb81579707596e37157d2900eae2823baa39ec6b297867c4ab26ff50ba2da2382a85cd69f7cf3c712b27e633bddcd4cbbfbae08133ca2fb09c8d3a996d872c9eab3916d92dcc6d8acb8862a37bd7d78f0d72b7464cc69883c6c94d97304854e006293dee4dd54a32f6f4182dfe0bb56e79a7674c94b369d4d4404e046b112523fc953c7c8152cd7597322c33ffa0299b5515dad1cbf460f0915e9d6cc676c803ad03e38255612dc08589ff65425d9959de71b78c85a09d00f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"de79ed70c14f06ce92bba47b43d83a01aaefa3f336d8936fdeee9352b3799d3c","proof":"06637040fc45c6ec9e9440812d1fbb26f40c05c255ac8a1edf529672304b07656c7e917c350dbb198cd63d55e2addf44eb4fbefdb12f982ad9c557a905b52118fc9ce1d5b1c18e651987ce9a9f8cfd55cd81d8273785f699893f8477f8dd714bb4290c4f868ec3548a6822e7d80f92a1bbc99e6c3c9d2bea5574d2989e7fa56d4988d4b88bc8bec73b8571588e328ac665ca906cf35e0e25e4a7be7f06442003e6ee2c169c5f62108178554430283013fc5498b69dc5090fca7c2ae2e6bedb0ce3c0709b69210bb1f7006c75c049377214c8f07345e8abd8b52aa50800dbda0dae6de67ac0a036cdfdd0712922ed242ec06bfb02a8f1edfab69046758b7fbe5e3eb44fe1f341d13668032cfe0b7bf0a565ef4e9e9df2a38c1f345c8647a9c3482024c1b48e003de4ba4f744b2a7ed4b29c5cfab539185889311a8871565a3d10c8d942cc6d2e01c564da02e8fcca5a3eb0d3bd433f5b9789be8441c1fd728a1ea09b780fdb2ad6db0f478d50fc1060a413830f5afb05dbbc72749fa7dc92a23726ba661ff439853b10f9616f57935896c4223818efc269a0e18fbd48a625c128f2c37e356c18a50846a6cb3e9dceff63e57c9cd5bb2ce57b4377520f5b83bb2c507889e8926affd8f456f0e748126cb9ed85daf17f730000d52ed9f742d70d3a52af24040bc2344ae353842820c8d0e59b207395ee51ec0a5cd72c36ffb59a3d5eb00a1317c2e0a3e12351fb6d073b9dc1b88d08dcfae4b8e9865e4c19c3e37efccc69282d0a1f3519606cb0981aaeda63c109812bb4ab571d773f95cbe934638ae377aaef188c26d838b885f97d238ef28e6323b1cfc654d521c5c99a1af828bb739de9cf0b4eb951a99316fb208103cd04eee1dc806461b2b4cf47ef72b90f19d009636d4dd9506532dad2a371fe320f4e69bbb68c6324a3ebf57e2a630b04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c267bcbd877c82271604e235f83aca87809abe491cb155e55ae0373c4b1d6c14","proof":"f013d8200d56ba24c2a5a135cd3f07014570ae92dbb1c567fca8038b4bf0753dd470e6fcf27fb7745e5507f58597b451b2c347cc1af0f8f1128ee6d90e0541189e423d1a598872b97b58ad33187a803256ca88fa666dfe18e03879bed44ee138b6c068b273e99a6039cab62a1ab74ce0d0ab1faa5d73a08a00531729c66534167e60f8d30da0aaa9a95993c885b4d42ec50ca3f70c664d9eaa89df1ab71f5809d3c71134bdaa5a52e0214d821830bc3b2b6bfe10d9e50096421d7ea5b427190b549028fdbf0160b4175e852ea060d0d9f4d04b3343a1b43d62e0217df2661902d8945ada31c05053f0085de7e3923756585f7f02145289afda8f5a86f4073e738ab134ea925d9e8c48cbefad9e74bfdda530569045e981b4202404ba31c2e478101dce3d40aa7f03d9ab68c5132140581d0bf14176bb4176cb00423905918f572e424ec1d8ab223f75808e94da463374277d7e612ec1519f4942f56af4467e44f6944c584bc13d2ca84591481ebcf82ecd237637c21dd89d61f5f11a60b34f408826376f779da959105e845f3b35bbc6c691dd6f38a1ad2e26da7fa7c1ee73377c49b162f68973e9a8c4d47619d50728e059451382fd8facffd5645a18e8160c8c5ecd5cd387aaad97f11a5024611ae1fb36403d3a899ddb66f9f3a0af445249ea93785cbb51f2356f8cfdca17249de1854d7a55caf60892a0c692cf487f444cf4ce0775e8fd0c1ace03c9f592c60e105c88f5b708a6d53a18e9f0feb31aaf1db4ff5c6d4e18a5140c4c3385c01f5b44da6f8b2f494fa98bfb42a9a803f1f049267a2af4c4dff7b54da5872d148056a99e768c29d54c102b06cc91a6b20df5218b9d36f52531782f210fb0e2fe35215e7f71e62c26af5a6617831661388ea70296d3dac6f309fa145b64ffce737dd18e446495c2f7714fa8bbddfdf685ac6900"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1eb3076fe6729b585310d91efe2e4e425db6f8ff289f2f4b984df611a51b8516","proof":"40deb90e2b7400dfdc7309978a0c31bb0fd7c1bad6b11a687588c46d3ca36706f686a20beb16df9e4c3d21f02f4625c0ac63f2a543556727ce4bdc08b5e535193c1aa4abf1019642f48c5df6ec8494859a3af721d7b3cbea294b7e416a73144d443849ff84825dde559c0863a646ee497f149d3d6ec88e1469e100e28312d37e26ff2fd761882df206fa429ed42a4f3dbe94f89f696763365367802523715c0264c54505e5786bc98ae6f6c87572d7c13aa6f7f45f2c4157d2c6a487f22ad306ce5031077ba324b16d6a8169ea226ab55f9fa29a990ae4d3ef860d651127bd01fc4703793ed9fd3f6606d6360a7f2f45185bfd724996b2c12bf4514f0b89b106209d1120d88906214ae10bc42f357bcb63e47a9b873008570f0fdd5ef3d1cc0ab2aa849533b10cc6960b7fc86a66059de9d0e129b294972bb9022471941a3f668a614239dca9a6c862c8f1629304b02188d9b4b8b635ec26bab5f22f7e3b3b180c3bbe67373e20f5aec1cf483221917aca3fbcb9c55317fa076fd06dcbf21e29b216c2dfe0c4ed5fc919f04c050c0baa685c6c09984841f6e5ae2d708f9c426b181a7d29fe10dc97e5da21cba3393842676801e63eb510a45ac4640415f66c6402bfbcb8a8053dba9a6e562ee0f1f9b5f5765192fad601cf8bc77fe5a1aa6a1fac1a3f109baee4161daf1965bd7ef1153eb4facdb57f8fb648f4c98af54b63017831308efc6366a7dbce8a7b7c33b0948510d6c1a982a6123763368eae3c017ff4feb1f3a35f10aae0b88dd562f03347ab03d33243a9f7bc1aa3a6cae2d8a7432a9cddf898881d7f9494facbb624b0f8d623ea1562b563236f96f5acf2f8c61591bab19cca7d493815789d0baa021aebfed38230f9b458b345ba47d548a2cd0d85bdfe76628ff0f250720b0e27ef08edacd507a04f3bbbadaa951081645a0703"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2ef76390d11acf9c0e16fa7447c7c719cb1f0afeebe35097e03a0676bd4f4f19","proof":"3480e69339980c6346ce376ea3d54ea8c3b6ade74d003f49b0d6f99ab2466a429cc84602047e46ff8161c72080692881e184fe4c914a01da9215606e5a50d8744886308cf64498520c1d9388158b68ad4b02f58ac89a125cb8b4dbcd14798d5e9eab6d67ff0f23f2baafa0fad4f3ce92abe3f3b57bb42cdeec5da991ac65ab75bf8b14aec9763afb4e46743c67e00c122143065cc19d1c0c45a1e657e0ca8f08e712528dc7933494e200e5ee566c1333c24732cda49de1ff2d7b7d726438470ae045f0a6662ddf094d0a875ddb34b6bcf5670a35965b17b3b6f8079d9fba090eb0b2267dde362c8be1e4947b27b4a4188cdb2b6a91e6cf7f9802ad52cefdf252e03ae13c018bde133366a4548fba8fc8e209902b9e7e460084445136e7e6b06210b726d1e7de9a1275b701300a164d60820ff2970411f75d3c4a0d40dc9d1a093ec80450d2b8b8321ea6cbf9d157018b975ebf4a7e1e4a9fc28bf6d6b56fae15da94e61556c494d51040dc13d4f017fa346db16d52a8a91556fa0a3c0826af61aeab3ce54962f873360fd8e59cf64f13f493a904c39769cfba8416d99e7b296008d64abf6d984812adf673afc64f146b12db473260f54d552a3a54b29eb1ef09dc3c587d3c2fd6a12085e5cc1961317aba98965c6567863ac57b9cc1375d257c0a8186244e627f4dbf744af1ab5efb70574dcf5dc226eeaef8ea76f69aeaed28e2668c7c442d1e70923aab6c5e4fdb5de04031d5fe48889de10801189e63582280dfc519187d685b6b226d51048497bef43f15a57f315038c7d117f4a6f9ac73da2c216bb4c1e7a1ba1d388b5867c415affc60102035a9d68f289a7ce1467f27bea97595c3f9fdb93c39bbd1efc5ba0c574fc5d7af83812d64e9069a58ee4901b01ede04371581448b27e4b2b3ff2a283d01f546d7443034e83108245ca1820b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6c70971987756b0ccb37578c32cdc418d9140e6f9132295d4e473d4c9a8a6a1d","proof":"2812cb2fd5794bbc3f423009e256f7fbafaa84241329bafe1c992c50d38f1546e2cf41b8c497307b0f4c27a0bc7f2836870a1d58f0f461d30522a1b2ec7c0545bc8a42613f179cca13dfb1dd4f42985a5699f94961c0360ddaf556814cca2830d46a41bbe0945c4f408dae1c385a6a782d9f0f687be41a5fac638589c53cf828107dea294bcc0d2049edb71ee36ab4fc64c122886a290e9006342ce93244b60fb627d3808926928c386836d7da584e41b99879d123f616d56912104273033203d28b802ecef12a42101a6327f6ce5c06bd5392d8906cdcf30834fe8f16f74f03f406b27b8d4fa64c2f9d6b8f9f7b0cb1e6f69e8a91ad9b9dca25c3db99e6746612842c41f076cceeec3661c93235631c9cd932543394849e542eb12d69ef0b0f40981ce4585da102a955b064f9269aec97de727d5d7fb5ac159634a088e9af5f40894179dae235e8171eb70b4d62824713421a7734e206cff74d9b9db3f5d3219258def5d3b6fc3fb8759a967db909c972868b94c02fbba11ada0ae5c75b9833fa53ada60b3ffc0e5a9f50d36a1f05f056b843d4364567934a2b1bfd952c6111aa8b894e93623bd1d03eb6b12f21b5e9982588e92d1964b3031fd0c493ec86703ce814e5462652dd3ec82667bae84ebc7b56819d07dfce86a7d2f1b24084471806f2f709739dc901ed2d7b725f38c761818c5cff8946b659ea5c8bd0d7d74c1102d94d6d40f21ed1206ece11cf44bb688a1fcd44a515f66b32ca76679f8308203ad9292b9b2af07bf137e38e01d387e2e2c0ed95eea40167c9fb74251bf47f0a18dd8fd9aff103c9f194e5036e474c509b8e360158467636ea2b8183978f165079dab0708511fed9def2cc55e4b9c2ee05d28ae38998b53d8c61e78e77d803012bf01c3b4cc9ddfc7c8ce4a8d2ee2ed1ab2fb2efcf0d571356d042ed0444ee0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8ecc8142b0c4f84c7004c7a817c5d431567dd522b53dd704a71226ee1b5f2a43","proof":"20b3836f2782d10b6975ab2b4b593d1b0236436a0b6c50c322b17ea21a12f20364a12a593db79a86d4113c89cb8724d8a3c976c8e3f623361f649f9f1bed376eced8a0e80694d1481ddb7576c1b0c23a803ea5b7e868a43b58e3f5e33bfd651092f3decd765c14dc85c6347c4d6e6f0b71a1131716548ee9cd95e7526537875b5008a0ee08d1eebcdc4a0f9a9cccf7e62cce2e821a6e77e019f9cd92dbf84f0cf9e99864dfece97dca0eee086f03027a66cc47d532e25de5a0822da432b0a4003595c09c6d45afa83a2e5872a537abf1fb2691372f70f3749881e178191b940bc6eefa75d032a0c0ba584a2d95ff2bff03ca348ee3e08e688a16f3ee1184334b9c858a42068d9dcdc85cd0c22fbd042345b6372539822931060e8fb20d97d45ae87cf3600e859cebe1cbcf19c5ecf18b2b1771c3bf94bbe353c6506d931355387e8cbab91c976fbf4ab0a37a65a16a3740700e3ab93597d7825df4464604c302e0bfeec63634b28a1baae0645ddd84895d7dbea2d4c18f4fd748907e9f68480b74493385304557ca7d6fb6b9269e0067f3968af886836804c11eb3ef6e22405f28e6ffd9938799b73d8f26b45b12daf3b67808bbaf1312b737ed92eb9875985db09c62d1f9f00fa133e00b2c95e16e5201762503e5300a1dc3e800d8b43c497b84c1ad514660347db1931055c389805c00256c91f6a57501f89b653176551d48f8c87053b3dc059791998f155ecadcec9af144e61360246eeb80bba02976885c10f521dfb0a724684b1e64c13ce30d8f6caf5161c4b153e23c55deb70fa19b51dc164f1d1dc653f6d78711691cce0a10032d4014618de744f2fe8182128e7d6a579ff7970933e24670fac05e7b3b8869b7eda3378eee9a42f7734ab7a6c097001173b346b82597a53baae46fab7edeac37047f5cf91183f997c90900b1fa2309"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"86d54d38692c486aa408b2257e51fcb610d26f6c647aaf2cf5e9be94a260ad1c","proof":"2a7490e2710b13ba66df72b81d0fbedb67f8dcffa72539d5a16cd5768fba2e5ba4d68a007420cca21bcd821fe3ede18f6689fbb9f011a49dbba5a7767025063d1018b8a9d641a2972519305aeaa4f242d0d182fcf6505ae85f169c1ce7e82512904c04089b3edc4a4d62bdb0901a30303d286ce87505f0b5eb00758e0dc37f739a24d6a10fc38927a69afd4b08aa6c96c0e506edf5b3b705533523903320c50c72e43c9543d8f2de4ae58d5c74dfc50a7d60c900559ba0d620d3db00bdeb5f031cb657d0648ce0c27b7733df0c5fe07b0a1fe304b48c7cd36bd3024c7c1090013c43809aa935d59de4faf48953caae7985af6582ae100f1df0f97b9f4aed953100837f9c87012cc78a0929cbab95eb0ce1c3cdb07dee12fe0549279f53a2c9638cc01a01c2b2d6597292da7e4dc775577780212dccf9bcc4c4f476be6b304b76a2fb09ee24e9a4a87dd73d414ea54996689e4c96c4ad80e87c69a2e5dcb96e7c78a118c259293de51a8bf417365fbbc4944f1e4ca23014b55fab31fa4150b9116ce474af4387dfc97d91022e83ab40f8648c2b2a01e3f84a82090e198dccf45d723d16753a91d917d1a104ee9e71087b4bdaf6e94fe25bf22f1275f5155ca56f38a2ea0767c560c7fea060d5323bbfb1a3d9a66dd022e77f95c342caae82513618ac729be06202c71c378b22b035ca7fa0da020f0d66cf760f672a66cedccf7404acdccc60ee4253620e6f3b91bb2ba197a280e52d7ff79edd42f7f4877269567a0a36200325ec224a3474ee9e0733d7a20c908b877de7b17983527bb25a7d29faeae05d3c24c4fdd126912608e585062e4f5e50f0b5587baa85bb2595aa1a656715703de8e3088adde3ad6e16971246e64f670583803a42a5227ae41125aa0c25fa5f5739ef04f9d1f6856165b9110c13d4892ab041fd0ee900baab9300c10e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"781d6281f14a7371dfc764e9188065a9cac7301781687c40c64f5f17c42b4376","proof":"9604c6de20a11275ec2adcce1303f42a1d9153ddd5635b7438bc06557d6490630e8a1dcd3bbfde3f6246b334199e1407d2fb1126c1c32781db492c9b16da6747c45cf5859f7df66d25d9086021b2c33003a6c3d184cfcb55a4e5e65dc017b609c8a0a82d72a0aaa1498d54fac049a2ce9345aed483ae8fa66e2c7654e446000d177fa9feb895a8a4b656bb7654c7143c4a3eb8c36368b402cb2f86621dc7560da96eded0ca54e00bcb7f7702a29389408a0fce517ad5dd4f171fc732d1715e078ee2e68cf29542e91eeb061847d52037ee803b2b79ccf4bd36486e126623070fd4ec88eb779eceecf101a5e8563f2a2a6f24a7a54ce287be07e666f3a90f2f47c808435cdc87b9e8a5ca9bce013cce12146357aa1bfcfef1b4f3bf62c9c3486826e8792922e7c07ec2c5b25e103805dd42bf57c78691540c629cf949a77c6841945995f025e5ba636ae10ce6fd20756c9f6bc80e3d28e83445ff32cab4a8950e64e02b110c0d0eda8dfd8422d7a5669fea53267f312d314f50fb2ea3f8ce2e296e192df61c33686d2dc8e8cb2e55b107b59194c33239891f09fdb3a1280b5802e6d2a8d7846e40a7189e0a58e463f7f0c02b0a241c63fd4d0534c4164c09c37dda46b3a2ac4dc54c4984fdb5a23f01bcd1bb3b4a761494cef7a56225f102ee6160f231710f5c8044f3aa5816e43a35202079be50e8ca429f2a415d075f75eb6f2427843cc2050fb23ca54ab668f900946abde4d035452ca746a32b05d87de52f92c47fdcbf764238ec5a9eeef8e20907e61a45e0c1a3eeb1c03043091729631536a04bad6d9e56e894230921f3fc6beb666afbacd56a015da26b562192fd970eb6a592c60e387c7aa7f04a5c376f0f97e7bcf0101451ca92a9fda0802c40650a6eb43f49fd3b74c586e08ee896568bfbd93456d729de382419be639762fdd60c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"92724fa5ca3f608a2c5121d50027d2349479e9c35e784137432fcc2b4dbc567a","proof":"1413d39da049c7b32cea564e439e579086f13d634f0aad924cecb3c1585fa748845050fd17e33b1d8a31fe73030c91b91652d8adeee378fe7d61a23f26ece66d24ff54b1aa2f0485f0a63a451e8986054c96a1553a45e42a09444f0e83491841707d193f0b3a82b4dd4d34ffada10a074d7504638d8f7859efdad4d44235dd6a607055e820d745f30a0c7b68b25c7657f7720fd7052a73435aedb0c50eba4600d20515adf97268db23e2cd3671edbc7bfac8305b904bbeb9580e35dbf30ba208bb4406a184897d3259d096b840cde4755495ec2e6b1150fe3c5d8b5bfd533807767747b2606faa4ced15bd32136db275df37b5701c7138e4107c3d6feeb8c73c5011dfcaeeb390159cc80eacac8b17672e23a313c5ba429316a3bbf9f936081632b0be47a4f7d9d1a19d1429d7a8609c5f28e401059751211e870d076df7096f4294ae079ca21aaed48182ab6d70413d27eb2c0ca90f95a143d6821396342722340460c7370401985dec53fe4792d0113a21e25ec4ce2c4bf3097778cd739f2d7efe87acb6e262100f58bc105d0bf99034c546b92f7dd2e400b0615b3f2b8925c20c550a439210dd14ebad7abd87238c8789d2b897f31a7dfa309b487660f91ca6181c7843668aacc595d40b32a8740a0370519a0174258f2e1563b4500d2e26ac5d743a20244d8530518aea2501462c9ec164625265e9512623080e19b6f9584a200e1aabb459ff41ef28ac530a24d327ecf4019855136f2251e66fc57edb3a0c4e7a2a1873898bf3df6f856a5a993619fa6579b26ad8b3b4c9fdd02c167547801f8c1b570fbfed08228dc6ae29adae2bda45b1ed8eb529c95c4d93024c65249374d88348cfa4c520e40dcc6728f53175d763453e8a5050dbd2554817ec18009c6d8e1a0cc97930f362c8f0d0f54740c550c9878c10f82789db4c5e30b61600"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5a99ad1cfb7fef539609285b2748a24e94443e31f28e695b42fb8b43c25f0c7e","proof":"fc235417fa93bfdf64b14812b58cb760b31589634b6c1da81bf408fda12b68053029905fda9f68030efe0d907bbec79c488329d40739911dfbfcf0d0571d9e6d780e2ce3a8ce3d592f1099adea74767b5a65b21b5ffe717c8a63d7c6537f257c047c08498a9bb971d272199973a2cc136c5d4811c177963e4bc8c96a2c47946a6bd82d85f62b915aed7e168a0645fd76854ce30f8497bde0080c046d9c75df0e4b7937ee0c136060326e95edec05f78f417ed5f7cc2db492a4e1c59911e2920a4ab2a67a2a7a7a16248feee61888c1cd1b6d0d2c6f2672a9e6016778f1138e0352789f71949c46c91b36f99e110a80828cf990694c84b356894ecb7748bfab5a7e76675380a18f0a12510705c0da37cd9f8cb522e49ff9db120ab3336d47e67b60c865391d3865f7349da8466d76c52045746cdc0805f881950c86a5ee050513cc01a3a0c555f87f930347d69a4a5867746cfe37391ec866b36201524d8cc7407068d66389ea5474ceb6f5b02ebf924bb40f5e5947f04fde569a4358f0e5ba2c4cdd7ee5cd45cb6ae662857958454a2d8205c8d91eb7de294d5b0def1bfdcc2026d946e579c588e44d9e6a2b66cc9fc6899218f03f76da68d38741faf54c7b2ace663e477be2325411a1207692916838fdb3b5c26bb8220fc0f3bf05c51a7b1522a3370d023fd51e04902bbca03c1cf6fab23b997fadd5795b8b8c47e8208f1210451c8b12bb762b28a2a2032d3ae1915a31b3351b44ab71603c13eb9dd4ed7ed2b20726d75a84e52b4812b89f25f65389dc4956d089e9f7fe64db619d22870422f1750b2f9a7ebdb22606d87a6f28b2734ce95ddd302030eee72aeb4cd97b5bf5c7325df3617fc85e3ee179b1193b6183e95fe01031351db27a886a5b8b130413b09c2dba9db8d3cbae3d8453cc44b4fa2cedac923b18ebbb5e96a213469808"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7619246a2fb6ba7222c8f3accfb7175d9adba41e2d685722a17acaa0f51e9358","proof":"c4c7331bdb72f19d236b6c1ac55c329c91e9da0c710da3e1ccf3f25391a4c02dd2f9ed5e8ccda1e619c020712b56f3678e2c69423b0afb85be2c109cb44126704a3af4b68c611cea33f41cd19e3c8653bb010fa66b6fa4b9fa77fa0edc70091aa8c1696fefee69d795a901a06544c63575691224751c4770a6366c610ab0560a20d3fad1ae3f78c18666a4725c3a958b83bb9f434756e4a48b37e5bde44d2d0fcfc31f6f74c2f97e6aaca4867aec45ff4d7ed2f086e681dc1b9d9359e0f5440da55d1550f9beffd27e343875f940c197f5aefd8248d079b8a5a3ce5b7140c5099464da307b7e0b885e0f90453d2e0450acadbcd87ca499b704f4dfdcb0768a5192ae4845bd9ab1af6ce09646f9db530d06dc63853693a2e771cad85f87730078563e4fc74a17e74d3c01ad5e49ccbea342145dfde6aab397a3ef3ef7764c8f207877752cafee02ad5a407cbb510f05360599070c320e817a5b18e73bc884df1cfeaafe74b4aa0497ea3bc6a2e2331538a0aae984f18031a773b97e7e843550582cbc57ecced2c366aac8642121f5a23930789582e8fb1a03f66bbd8c0c8de414365a0d38d6f84a34fd0f78216bf5f98b05562ba4f53f2acef8c841dd988acb5f304840214978b9b4388f5db38ba44c23769d98ddf8b1c6081b71bc46f00fb7395477a6ff67c3dcea971c1cce461ef3b332f0ca95df65fd7de945a5a0fb08026bb66e64d0b1b7781dc309867e718f281f90435b6e861a4ab405f1005a0b7f7263ba22e4b0e008aac09bca1498d957daa3470ffff49a1c689bca841870bf7112348a9e5eab53c8a14f8796567953abb206ef1dfa48712f2e563cee22893d46b05222dec83ea78e607dd89ee5ce62e1deed5198a1a78a574d17f97656d398f50e0bdee4ab6cbe5a0a108dfd158505665e1f448fb3340fd8a092ecc506bd9bc5f60c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f435ddc31d2abab0f58f83b34adaf24b038a9602f96eccef43ddcaedd62d4807","proof":"24b91b4325310996839c930cc42cf9bcefab390328cb602d6ca6ce8d7856d26ca2eb74b79fb0bf2e6c61dcfc7272713b31fddd834fc021aa61671783abcbac2e024b49a91206e2b9738616b0b9b6e8a87d698e7abcd0357cd40bf22c033772161830e9cf383715683e4a3d44de4c7ef18667573822f0eac2d3b90bc64123e358d3108a2e5623a699a7814cc802d51afc11ec9e929489515ce4a58e60af05a70b13b3c86bd5dc67550b672ffa372b32385dc195d9281ea529e1d78c0c1f5c0a03426a479351445095f3d0bc87b6178bb7c51741096c94df5e1d82d10cd80bae02bc44aa6ca4045e087d21ca09838f605c8502e051f727393a117bda2926f9a30ffc4344d28b11c30886a32c0671d74ff1f586cf911304ae190f0639d0ac918b5c86d434d5ed59dc628ab898f8e98e5e13a26363c6a2ba00557ec52676e64f654d8c52d91f4ae34560b49711b739262c33a8e75d78e0d78ed4cefc59436d91c07a44fc0c46aee7e4d8692749d27c1978ee4355bfdc4152963c782c824e57fcdd4158ff949d6832bc6af9477c01b53792f29477b5be5f83cab1058f3373ecb80121004b177330ffad2498c08dd28a520919349857b9ce8efacb5a8abf3d24de3b111a090d4c2ed419e35f71acaa5a07ba63df4d586a5301a379fceba389cfc3a5510057626e39d6f3b9b2b71ce3c8ec714bdab1797ce63f43d18c33234276277e45fe0e0e2f3300132d3b23466244f912e9d78be89c3031b901d5b829ba7ccb0508b2e48398328429df3912b66acaf6785a76639871e1a2e432cf54639835b8d0236675754537417719a61593fc63d03935885a6b21ea0abbee8b41b320ac43743f7e73edc0d8e1ce821c099806419bc13d2d939607496fc4afe6750142c2da4d0d0e65587e59e8ddc68f539e5fe0f44e8ddeadb488f8de3d52ef33233b4d3bad05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cc6ba1cbdd29b81326801801c1d7b1621bd793b1ce7e54c9f60fc0109e955702","proof":"520776f5fae061a0c676818d962a83e0ad6078d9dfb5854a980bb0bf8bbe861f10bdbd96e360a7b0f1aabc55cdb9d53054420bc0e77e90c9b605d49dfbe7a733d6cf82f9e82ac34a71adbf9a09f033cb7ee3230d5cb164e7672e702672af690df87787c5fbf047dfd3394cadcd3d940affdb59acd280d95fae775ab52ac34231dbc35776c292ff4a53eb855d20c5d6ade9d2f57c2868e5df924bede84a7e0a04916bd0f807f2776efde91a90678ff4b7ea9cf319865022ff81fb9ab16b5b46046417feacda6df0a5f9e52d8772883fb485e3c483b7f257dd004ede3ab0799c0a54cddac5e492c31222d0361f01f660947bd470df23f719812e1db3261ce9a112482f9a16f212486ac6389d6e89c6f0076fed7d03dacd345d913e192047ef144a86d80b519d25b95c39293df97a825b871872d0c43e6ce1915f33cb83bef30f7ae685318537d78fc10b54942b6c41f5dc0d1feb1868c12b7cc9123f250e4e3769b8d889cbfa8f519d12ff39b1047f5e5523ab937467982a7a3eabceda86b8675e6ef4582b1d89d85ff2d2b5839e2ee3abe84494b1b8cbfff348122af0255f6d1344917d57d256e6dcca252dd08200e6b0e613ddc8bc241a5f2de7488a45d0db22a85eb0ce5eccd4b315358ea72d27fef73cb2084e087bdddc99442c942b9f486c84180e3bb4422f1659e2f3f19019e9de3415a5d777227846dd459f0e6a3679224e783058b262c0eb3fc07d13610b2c1e898b08b5c0b11a4416daa2722c44970558f85d9e555da46fbbac776039202ef146383e7227cdefbaf153e4454e08030c86afc3000372cefab1fab98c77e2b9f73f150d628c5c8e2d255d779b782598289bd8c91ea7799dcfa2ca2dabf933318923f27a07a8848c4c4f140071d579c70e6856b2e7c45deca845cc6c575815099aab1249eedac71ba3b55ea2bbbbefe10b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c8d156dea484f9e2326075e81c693ebd1653c495f825eb007c78a71296393e3c","proof":"ac76a4a66f9e8e8c100ae22d038bfb322a6d91640023fabf8e2a51ec50e1f611ee9a26833221d94d7cf5d6971c4aed7584fc63e83f5e456696603a68e404af2c9077134b1491eb1a57bca0eb75235bb41a4a23635b31fb16eb21a83eb34fcc2d247061a24ea73df5677166f9572999ba4fc53a2a00d9b7234960e5dd9a89e46d013532ced8889a110a62098e5da8f9abdaeccd3661a13963a5ccbaefa840fd0dcae00489899ebde54372f15febe8788249279d44dbbb12b948c385ca1b1cbf0ce2d4022a90f0e239d09006ceb100d1855d51cc9b96793ec955fb00b62df8210164e5cc07eb2dfe3aa08585bb8151c09c14f5d6ede515b028b3308ac0392afb2974ab4bdd32ce57e87e609cff1ca4d52f2e6da3fadb86460f81b232b5c475594b164886d006de48e2580470fc0dd65d7c04b0cf09d8f4ffa8d1bb0f16da5d951bace8fd60b6d40465e7e5366b1d249890d17a02d6e469aba17bfa4e6d52f3e648a2c33a4744bd8632b9a19c165cc3c2db7db25388cefdb7ebb917d41376df2304245dc9b67acd5221182109bac7159ae77504156f0c53c218a00bafb47e387353ccc0381f3e77a5b8c2502db84a1e7e9118a14fcb00752bcffa19d6d5914141278ee73525f03b383594b58ea1d9197e741c57572d6e89db707623c0ea3ed890684cf124f92a02f9022d23a1fb8020234768d30d90f1f8de00c60185bd0286162442c00b0f9b98d7fc1d676dbe589360324abcdebc2b6cd847459a389c414fb11128d605edeb501b83a55eedfd63023d4b8930f1416b82ecdcf985571e8d702461183519c7c52b694e22cfdd9a13c86a123e1c5468ed34ceae37bd162ac097d353218099b8bb84fbd93583895e7f45cc68aa7a92385b065ff48bdaed76be86510434f88342adf2ce7fb619fb43bc6e36affa1a2ffb8590b94089a5a1921e9e550d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"68e630ee1e96afbab3c7ce8eb89aea93a43b657c96fac052f15631ede2c69368","proof":"92c1c2bf83b10586c2e81f04e9c832255cc18d29f54bf73a5a10da38905222710e5248e1e110f0bb650846583583102d4b6915ce91c4d501904bcdcfe15b4236d80bff40df8bf2fa91a7e7e258a468385c25336c15a396735699f16666bf7b5e7844eced69ad814f60182bed40425dc347339fc2bb353230fb91b1be504ba24e83af7f11d348bcc1b463f0bb99369bc64640af880507cd735087157e70596c0470d7ab14a423bcff7213412250d05e6e7209ad3c511dc0ff164562ea47545d00dfa172cd18bb350b53d87e8bb23fbc0a1eb7f0d5dca4fbaa25ee6c9e4a64b40ffa0a61c31f9983f6310f8d84f9235b221b27799d3a3e51a9428d86d7830b6f4d12165bfd5d4e0babceb4c212d7e1f2e24c0e0233b36d01aa319e0b6593d4b9387c17e1af7d965d38fe450d15d38281f818d354b37f463522d0ee7f6fe0872744a8216828ea1ab7e53e64e1a3bb254bf81628c2bda399caddd302b2f8e018584e12ea1d7bdf619b92634783acc692b7842ddad947de3ef7bfd8eaa405ada2c018dc8a61072f074031d134eea0f7170809bfc856fcd8b5478ed1b2116707dc4e61a8bf7f776d43723e801c61850fe2abc93c09e842deb8062e8db41708e249eb4d4cec0af3b3d89a672a4fb6efbf3395c9f87fb469f0140340cdd48df1a78e7e2708bfad4aa586ca8ab93650e2df7081c9e3c9bb5f8f6d9fbefd4e5c8db1a6b60f261a89435c47b575504e6df9a7883f7a14ba4a0f76eb14b03b452f03b2240b2818d48b5f21eadd66af1e023bd8e07393a6b4fd25a1b0b10946540408756b8e5d7ae3adfa1d71e637ecdc4db944ec9e49927c1ee9eca077c8e0db3e3e950c994b35712e45b758cc1b24b3cb53b4db3674c5d20980d6672202510a1b7b3e26ad0075664b3d722f790523b37ffd3c0c2d00f954e0134449f617cd4172ebee26dd0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"261631102801aaff88e3a9b566d19a9779b52e77f9380b3c05c5d9ee34202b0a","proof":"ced20eaacc20280fcdb35c7e63034135c9f7a4f27f20cf0a8453ab1012fbd81b12e7dfdac5e11d772575aa8e6da2c14ef58fdeee545ade7a1004492709e3ee4a8cd76f8921456a055c170bad5ea5901d372fa2468eede2354c7d1fb84615e42c4c2a8eb6bcdf6a59cc138b7ffcce88644d62320c67f5c71f74bc146e4b479a4369c1a1656f03e7d75b175936c68cad13e734d2e33398dba31fe161accaad82009210d7fd005aa2d3f96f7a8245a7094dcaf46dd380e000d8837850a149d4ce0986590e4ec0e0df49096fd7cb8920ec8e0b7558aa4fe45719026daeb348daa809146ac2290d299a25e704a909e9836a62b292e05b673949db29515cfb3f8af37bd6fda530846501bbb036fa6e761f01544ec8af841a661c9d3e902b5fc690c5749e756ac9d3c3199734b3de979e77e86e655026374239c4e5178031e081dd5355646a90f3256b6a4a93728d959ab78a999cbc8a99a5bef1dee714bfc61196fc297c145866a74d32de05615eb3aaa320deb006b819ac825ba66f5fc418dcfd4f41081f051496928535017bfead7572a7b64705c7b9d499368829c8e5405dd3bc759c5e3a8bbe19f383001f88007de56e756eb94c8d11a94a2f3408270e55ebb7192c492d0dd09a028b1f1e2b75cb637e72a0e878884a47a7cb35fcfe22fe6d5a098cccdcd47cfe31f9c2f061b3634993c569b984c26b3209782d51ed4c2f2d406654639a8b76ebfe9fe0d413d3f2f7f77bfe043f27f37723f4965c1308ca8f5805e808d202dbe5ef0029338898e8be8766a7ea51bc7ae97806576d125e93023e589cf341ebd27e2d5d3d7684e4bb8fc30026e8057f2b94119f753942302f6b0605be5a1379f48b8c7749192cde1be58c92ed2ce93e39265d7523dd44fd8233a3003d8b32f3487454060b353ff21efa94c55a1afef090f37e3c6ea8d6c4f98c1007"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e4386e602b7133203e19928a59ca0c53954c1043afac8e085d32ff894b3f3f2c","proof":"dcc17a131afd7d9ef11cad64c3955d68e596b3b1aca978a4cb8eaa2bd3e135075e41c4ce061b0da2f15d5aaf713d9b78f03cde7a1972112c66685478ca6c000f5c8c11aaab2274417af752d038e708a89bb3901eb682eb912716ccd9d006862d18bb2b1e7d23f377e747d75554af5279572a41b49e4189ed81ec4140d3cd0d16dfdaa41425cb25d193eca10ba2b60f90e8a3610fa33645bd1c94c70afee7f006e0df867e8e783f3a57519573f0cdb66a2090a8acbb3c487dc661378afe4334067eb5d1a8eada18990db825c34b12a9d79e4e8eaf333fe3359e2adbd31de6540468c0f42110c3cb99bfd3718b24a665870c39fbdf9e00674f792aa3cfb0a354220688bdda628d951cdea66f76c30f4f89476637bdf66fed7153084104c1f60901ec7d6ff0c73efc2ad75cd64ad56410189906b67dd360980cde4b8bfa96f98f6cccc1c8a7320eebc70be184c5118f8b64c1f90faa4e5570f3699604d039b2e126ac4a30fc52be4190b8bc00bd36080748602273e25efdb02c171733d0c2822618fcfdc78787e67f67c81f7b137ab248f277550d2f8a2cf67bbf1c6bd2b0a96d1214351a5cb0ad8e76881f10598d8913cedcf1dec8cd82bc5cd03fc93a199ce372907e3a0635a6734e9d4acade2c88eec71a2aaec2bec597f3d636670364d4bf65a2d91345c372d2477082f27dc8fc9c8dfd7f4500658822208544432ef67636787002b8d81fcba74edb21fc56e381dd758e76a184fd6ca71f7213467676e912314a3800a8ac87343bb97d83553e2efbdc0b6c8ec8dbef51ed570faa28ef25ec2c161ebbed807d76b8df551df527398967400003b9ce3d325b459ae5e8bfea4c4888c86b647e75dff19f87f42b469d7286b664069b4c0ecd21813bb3bde3b59c0fce404d1e50b0a557beaf05a2774226f97f14497f9b4627ed7560568af9546d01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b2c6b003cb257db18f7dca617f4519beed4f8dd93c55eb954c74b2de6c5c7f61","proof":"e437268b4ca2ebe74a95ef2aa35cca0964cddb23d01ff0e8defdc92975e4e16b468a4778e32381f779644823b0776ed53b8bd774dd5db5c408a741acdeed8b388c99792b416f54f0d6550d2385645b4e61072258625963478a88c51e6b1c2249e42ee1a306c2565970c5c3493caddc368e6718b4d9d10aba65ff8327854b3d5eb603189696c439d1ee4d5231f9d460be6f961f8bd53506c51ce16690294f690e0337fa6f7a98708368234e0364c8a8f0f7bec8748b2a5e5e074c0b04af75220dd605be832f4f99e6d3f66fa91b2323dc6edea9cdea34ff75785647d1c854c00dc4ec4e02fc2f2f9c5bf37c3131908bde37659ed3cc1aef141ea645c22b3bd369582f8bc56c59e9ac0a443d0176b19ec7e25bbf4099b9148b6c62ce4c94d0362bee3c899895d502a323761b2fdb7ef7fcb96ae2fe8242a2ca4e3047dc8ff0ba4968aaab69e4709076922e3aa822c04d4b10deb0b8d180ecbb92ed8bf5aa0c7f2f42cfe0114dabf62802bd3f8d29beb0330d1b8192271add5f5c527591c3bc575b16d20e84201d8da1f0f7fd67c9d7c9ef0e64b6f3b6d8034e6087225c8c527b0d78bb5a629685e11f371883aa817c2d5491a89e258cfe8a5ea6a98ec9b181ec6160286714b9d54d7ca312405946cf93eb0808802d3dd4729709ae42c75560636b469502217b4aa5bbc455c936e856bfd7f770cd183f6ab3d64d8ef2c22c2ed827fe8f3a202f1d8a102e186afb50c9bda3e71eb56969966cac5bc240148874877fca8ae80a564e6dc3db6090fb2b0259dcfcbd99ca47cb27939ee997c0c780f85d1850f6ed70fe13fdb854ff8ff93d8bd9356023fc2bb408d11f46d48c5acad44f12745fe2403989101382cda52ee6e033a44a3860f734c092bdd95142bb9b3c0d3e17103f9ef14830ac248bbc871f0a4b4a015cbaf1ba1d37a79e9e2e2ce5d30c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c86a36c85bf8ca44a85da4d9a9389e3175d0b634b4341bd92594b5804f3d0b41","proof":"fcddfb15484dc30e32684158186f42a5dbc1000281c604cd7e3ce23809b2912a583296dac97e3d7ba89206089617646c0c1291d88c22691bdda1ef04bf059774e8e70c557d32536ab6892d63c53a62b4b019ef1e0f31bcc2abbd352b4354504d0ad9282dfa561af25e536c52314a6a5a6568abf1c1176ad18a2d4d91793a7c3aba169e54da2c004223f22cb123504dcf9d4b6d2d4b48d70fe3adc1f6a5f6fa055d3f5c6260296d2e60d35c3b3a53fa7a0e533e692211c7c664c945c4365461029a26675bd956ba6813e708b6072b28596260bcc781045e4743e4d98c3e62b10ab41098442fbf22586b220c5b6ac422053288aefae01e48b3d509ac80af5e492a02768a62de42ab8e1cf38ee405316756177b246c2013ef3e61bd68a80838ba47c29a5893dfcb25205155a056b6be17cf29f2e9847b41c5ff3198abcbdb89cf6a34b0f427f06ed682da6be74e8edde79be4d638f56c4502534e11f545cbd3be64aa37b57d7b0d267c4d1f78acdf8efa7446e502f360d9f51b05b43a3c6d8f70356448eb89b52beb7903bc5793f3b33694ffa31635497f0fc377467de52826f6488072aeac1d1cd964a73ecb6533f00b544351470ce8c924defa26085df6d3d1273002f658f7208aa09fbeca887ce8a7b4a88607b32a735eabd542629e341d2e11a8f53aa90004785a04b6c710966f72b36e12ad4da78e8bf3763f02bbbc00ad2a64b059a9ce7a98faa80a6ba622ed6c3c0d55284995eec6f2f44923c43bfdec39c2b3a86d6ee0141949a36699b4b07213b8521f2dfdd782c058d5a5d3a504634a3cd358785d851b620b5fbb634151a8f4502c53ac327fce19331d4ed505ca8b036fec124e5d13737bd789f4f3b30396868dc4ead15ab0fecf0161b45f5a7c930a0dcd57c28fb9fb701722ad427cf85d8f2020d1b025b9e17634415f3f6f4d5102"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4aa80b3615b92fa57922e2cc7aafea82f774caeac08ba1935996c9cfb0b4c67b","proof":"ceaa073edb3ee4dd22a00e75c29195dbd6327cb18e8a65a10caa3e4a491a3642ec9a8706343d42949aeb54da66a2164960941c73de82fa4ec18930e325442a28a83f397a3a27764fc584c0e6d172594d09043241038d836a014ff0342a28dd441033a8b1e0698bf3b84447de3d7375c08608e63402cb767189bb348fac57f764b27492a72d2a7e556d4e4228cea60fb7acfb8cedaf47c380fc4558f14043ec0edce3727df3e0631f0fc6aa13847f4da2b515ba2db4627242d6115b199415ad0502810feebd943cafa54424be0659062030032f3e18db7d8a2b4b5ac20b7df609daa4e1abba40c17248a39a598befc15f621d750b9541886b5ad980b75060b314f8545f793ab812701806724458db861279dab68dfd808d1e2736454277ebff7c7a2e948385ff4bae0456b266c510bc1359ecdadb9ff063a269d2ed5f68dfdc6ea870bfb8475e48fb8ee81ec838deba5e8a07a59ba7362a6853611fa8fe64c308da8f847803dbb3e62020061990ca705038468f14b037dd2db32530dfcc1f80105815461911b70d61cdfad3eabf8fb73e3712ff816498b4445030442df0ed5d1e62e2a2e639fd3b04d636117b7f7accfd58c506a504ec535ce67ffda134fc2e7c5610d9bb59ed81bb463941566c102e3ddeb4c40ebf598573134f6cc432004053d25254dfbaf95e4167ab554535a8da71160a0ff4603c9231aefa0604bee47b308c0c7c92b41c13780f13f316c456872c7c7989b9ae6de1f8c36ae9f9d60e3a101a70e1a096d8f90cae054bd4ea85321efdc1d7ef2cfddbceff767f86beb0c7305cb020bea408db31f2031e19f0f31305d296bb14bf916a87332641803a9494707d647df2e57aec6c762a1dd9c3c68b879f4ed74de440750cc50057af34b1f10eeae115b0ef0593a674c0893e64367003c2012aaef2d2d9ae26c0535448bebd0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6e5243a0aaf925fe4fb06dcdb33e4f8cf689c4c63098a962065ac37ea03f0979","proof":"e281cdd7d346375d836e11b72dbb06d1d4a156e8ba8586ed9b1e82420340287e26c5ba3c6378902a50e5fa3642c1e527cd0b24855f3aa2ae75b4b1d74eb66743127ba1ab3b7c83fc559d5c2c0fc9be716c1f3ac8ca40b98788fe74f0f0007a56560a6667733d07865f4e1ba2d7a8a00172160187bbaeb44162bc244baa4ff440a07d764cf8bd70f8743f0b0d1ad3805fa7119d52a41bc3589f5ee72222f33d087ffeb3261a5841830c7f839913fce81c096edad01402b127d23e734144756007b5c2e0c1467ac2ceff917a018c9fc0b84546b17c361691cf2cb280fc83240b0804ced85c42623ebc1fa8f9595dd430b5b8b8d5ffd382b95530e056bbec27827e28e54b75286b63528eb33c42cf56d07f4662d975bfcd41bc9094040355eaee2b7a934096a6cd6556bb3ae50a613b1f25b4a35524d2a13531a56f92ae3fe9983f16ba085f32b91b7028d70ab5a2d498f3bd46cc49db08fb0c47c5281131fa0b348a0fd81664475e7cff5d95504b8ee38315c6fcd138aedc02c3c20cd63795e93bbe9c5806fabe4478aefcd8fc9f2127be43dccd6b74cb1228093c66a051a3226aa899872ad8d51aeb6efd652b4ed6f646509f19e178e0cd4f60bb70761a8b4553d083c40dd2a47151b07b62d3a1f5dc0b9245fc4b339074d3a547fc34145cbf6b94fccc3a295c41e7fc1d532a99e16fdf45f593db253b3b4bcc35e17bebc436048ebce69f77182b48f85c3f9ec39b18f12c93f51158230c04c11c0295a78cb8357cfe2f761849130c30d97957749914ea7273348f03d796e54f47517ef6f5e40cfef459568e0c84e811c73222b374a68ecf7be8e3233b4c2f37bce5a2d355f51d494753e99c5efa74d96c394e75a27231d5ac0f7cd257b49fe7de6480c182fb00dd832f6fbd1e9106deaf42d8e40a121d580ea36aff1c3290d207f3545df61602"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d4c4d6e47f295fe98d8ffe877aca5bccb552c31dfa682510530f8fa142af2427","proof":"2e49866b9c74199e39bad2cd8803a8b487bf171daa0c1b9dee67db88ee1ac757361a4f0b92c102d5a20a933fa9a5b5aa447d73a82010b5b40ab220c77d22f213d0aa7211f4010fc177c9c7cdd43ebd90db1800373b8098891954a0279dcbd16664cda2ec765a4bbfea984ff4dcc9d401c020f7ddf381d71a5f0ae96db10f8a0c95fcf5dd78c8ed6c6912a94eb9bb8887ca42ff5c41ef5c18d5855b05cd883602a0074aedabbf12d94ce687fe84e367493762506616e91abb2b378f038884660bb3ac530c45fc13d414ef18313c4db87447183306d30977efe674dd421a69b609a28ae7e6525bac5413791c5c0f2b138bc57d39dfc810fe5e245cf71552c27d58d65c48de3ffc7c226f422d0cf1ed619a1e56c08e0a3ed5fe753c1703b2d1cd0ac0b73abcc200fde8228307f6ed0b05b98f6b8a663a7158c8863bdb7675608569061cb02fabe2426e7c67f26bcfa7d16c654fcfc6bbb0baadc71e80800191662cdc78709f1f0427e6d052a691f34e9aefe74692b683bca264fe30c3bcba1ac80aee4f6da2fed60c44b6bdde7c1a8b7e02ff65fe5950232231a11d856d62e8a37a8642c21a28763ce1a8cf647d6fdee094cfb226af6be6dba4829ddf792af35d583c22756dfda8c715aac64c0472d4cdf1d960a2567f65290be35c48de61bd6e0392f423a6e424be443357549f2a640751daa94e47e0df42534b5c4702ee41106da029fe187bb16de456ab357f5eade61a630ebfcbe95495b6d59becd6b591114d6a0e7544cc28e4c112dce0680799df076427a57c820a65070abe76a421cd0d29305a5a1d25ea7401f5a23fd590fa109c4922b31193082783c759f60da88f3b58dff1a9a1908c4ce1577aba638e57e845692e43b8d856dd99685866ff3778ca025ba9c629a1cd5516cde4426136a2233882cca25006cc6036531e04f6b6d61303"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f2798c2a7dc35c1badefd3b4789c9a2c841899ac930bc90dcc63170e930e4122","proof":"14a7adca40850eee24a16a83281c86f15f30e71482f47e4b0a957e4a1fd29f0918504bf9af6408b8eeb04c3b574c1d9b883fae14b48e197b960fb5a10fc5ba54b8716c923c76cf8d38aff84dd13df7affdcc3865c2f695dc5307089d4e3f90743847f6839adebe2d38e0b34c78c4a2d85cc0e3cbca1341c8d5596db8cdad040a037f50372d6860c9dadedc4eedf9eed37b3f5922ed0d1533ad9eea9187f5a20712238f0862212999dbf431646ae399dfc2e39efacfb149596ca79254bec4d3014b02a9bdfa02fa1fd1cfc27b77ab1c0f8ffc752cfc0ecf95f823ff1c5663580038aaeafbcbe03018552e40afe327dd12d87bed13797c5f577aea87d8b7c1565fe0527f06200a412cbe9084ea4f9d0b28b5d4dc45b961513ec20096ddd6d09d676afe4caf3bae997e70516b01a0d61a708f656c7eea9353d45e10f1fec3ad0a7d34bc5c38e027386f92194164f7e7756cae0acd2c0d097371ea51ddcbf9262341b2b1c19b94dc0fb5b0618638707569c4570ea2d91b7e5f8f81eec8f3aefd8f5654fbc80cc5cf5885e0175d70ef24e54bd2fe761834f4497244123a252b4e1668d0964f000449a1f83f6ab13012225b0aa393586fafd9917009950fd946b05f7b265a419cd2b4fefa111a3c68f9d4920ed3dd9e8d22700304ffd692efacfff929d8ae93ba354398677204f828b6fd0d7e897a0a71e0bc2138234f523520533b4428c2ed59699894ccaa7d92cbc8c82a9fffb1c4d77d62deb82cbc71445a7fbd177a730a396429b322e362560a3f9e953fc809677916f5ada56d99088702df0a58d8c4933d7c968f3835f8fbd48b0ca7127aeb22ea6f97e11c59d01c570ad3072956b75fd5eb96a3087c97b76bb0d08de578b49604da2d18a1bf636c3162f9230d2ef7e5eeb0ff56c974f541ecea7778b7b08cc041da8142de6739d6aae4540d03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0e5650d37c020578536609915288bc6d590c16e928347d8d27439c700ab16636","proof":"f2acafa9933dbe6e27433015b2d59d933d92f65f9aabf68c486770b5d2960101c801f9fbff5b58ee43b04b014f93d0175731cbfacf1f3ae2ee086778ada5387ebe89baa38e1c62a5bd183c4d3bea3ec0286b230c3f8a935787390763cc05646c6e9e227a50b4ec5480fd4b4b08c636653f0a78252a497f903aff617f543dc7685aaeb78c153c536813adf738f41348587458fe9b4211618042ac8863824f160dc46522221b3af425c0c0203da863aa911b2248656c9e4bccf8abd9a8d61b5f02846aa59f744c0c5ac4a68938b39a1ee7b4c19bdc606ddd766d9b81feba1e410484abaecb9923e075ee28ccb2887dfb923e2a36b9f462cd42102d629ceb76036522f69f694f6b0269e5343f7c9e82df532648f88fd73d30894f0ec546bd201e61603b50f749b21265664b7c85eca53b20619c83014d9c743fceb4ac13aebf195968e1d9a0c448b608fd1e669c1a4952e890916407cd3e4ad3312cd86834f52d340c776424c1cda0f44fa9134c7518f49175a7f7b778da95bc862de6edefd07b2b7c1d14e9ab13d4820b2ee361bcdb26088cec95691122f9b54abfee84182960658cf287081cd41a64732ba3d9479249570a295ea0943cab73f5b4b5bbf9ec7b6c8e3d9c3b9e5539a09530133877df6686027717602480b15ba593cedb4ac88f2b26c3e18b97ab8873e4af21377bdc851bada983b23bf735fbddd014a19d06cd0d16d9de64c7506d65b2787a8263d20fa2ba9877db7c7d743bcf41389d22ebf207e4588a790d5f1efdf002e6fe6ed585b538e96a8d954ef8567a693471f30cfc4ed8689a87ee90f47d36d9f424f50bf1fdea90de028d22becc8c94f718e56d841a47eabe94f331f5caf0b46d61bcf80a4c9a856b7fb3ee68952b60c2a3efae5f09a7c3de04657dcc2657c12a1d95de1f5014a8fb667635edff2c3986fd6f78bd05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6870f9dc282d7599020a93348a5b5a9754d4efee0118757183043a6ac90fae0f","proof":"b4545a15ef45cf4c6cd6a99aeb02236bd58d60105768b1363fff1bf0ef47c43cc207205bcc8e7b2ac90ae4a0e17ea6e664369274d685ff2c49852adcd9d25a5ac29bbc59e0a82cac804cb043c9f653843e1e4478367ea87d2f3c66c7a84c66031cccd7802a2f511b0be3a580ced3ed58e1be6762d508e601926a4643806f4d382a1fe6996d7275c5a5d4653e9a1e32936fad58f998a43926175e9a70dd70780da2c0a5ca0cb8b276fd965735268636b96bbfb19177a418de17079193c9cbb30c019704275b60421460ebb4e7881f3840b453d05833c5f034773da64bee5cae0410bbd78ca26e61e9a8a3fcf73c127e8e006b88acfe403286ef28dc83dcc0ea747090daec4ccf2b6734556ab70d9d304ad0894bce2865bf68bb5e68f8a5bb91505069026a0d702c2057663cace3e11f2af5081dca4d35142c0b1b9ef9087cd512d49116529fcd564d5630107173b4f8195eac8ed851214392fd4bd879d588d8524a1fc600347f81d666c38758545fd9cf23ec4dba095f514ba426ff713210df0e3cafda20dfda2f370191d929bd59f5de034111334c79be33e606bcdea5d2af7d5484351c6bbc50a8ca7074e455845b91ceb36c59e2910445c3e5625da4094902580a76e3690275a2dc0e55e51287cc728bfb2bf2d37c18f35c899b271b93fd7778165223c96407a7bc09d47c526738c2bc190ad3f547d02676b094c9ce416204eeef5e397cca3a3138262f342a4ed12d781deffa7850cb691f879b5201faa16d649d9a9f1507ae4825f2db863c15941c276b001e2705327adf4df59c0c85892102fd411fa040fb997a2d8b657715f142454d1c4afa864224deb6725ac1b2e93daa326263b311de985633055a9f7f338ab966f859abc566e8cdfd78a12d94c80db8a3bd88f62cbee151f2491de032ea0255f87c43de8ec23e76037c3c97487208"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f8879bc611202f55721473bd8259f6161ee7cdd22f36a8147df69b9fb8eb5e2c","proof":"8c26450f620fefd300af45005a2129b38cbda74b405339807439e07c62ba263e12fbb627df44593160a04f19ed98cadb58eb3fc4aa40dc0d90ae92091806822de818081a443af55d6bf1a8d867a509fc53f66c78542d80ccd4a72ea0807b51421e7512d979842fdacb0f7c6efb0e582e20cd6d8f86cf66c2a26a589f640d8c5c417286e45a30bb056f55a36ca47d2228b8c131b483874cdde5e628c707ba2d0e181b2171fdd35859377433aac93c4e91b95d8c379044ef68e530ffde2185c50ee1553483affaf39ffc13ce8e7ae02ea01532c00125aa17411fa8e21b8bb0450764f66ba20dcfff4b676dc16cb3b846013b1721a558f4e4c3e49e92ede535062f46afcebb09b5faee95ac37037a32dd7884f09640d25a0e57184e64c665c8825d127bb47e3c5fd88de9e3a051e8237fb1cb4d36e3f91c0d84b359dd98cdc92747dc9b8cc89e2cd0e53cd14fef33e6cc50b8fcb67b5e558e9429d71a7c12bb94597a1532aa774358ed08aa906e71b8c2ae001280264db79ceb78cca13e9f8c8739f2dab9a91360e097715c3258c11a8559d80d9f2e15255194435bfdf587cd827c3a7d190dfc87e7e21194a50ad26a009fd1111600289f57001203b1364a3d6e763409825febfede8566b816bc0ff088fa4e57d0cc73372da461d894bc472ea7498499e03475c8793aa5c217c9a9d045036d2ff3520ee33d4c071a38c8fcf555004aa992ef0d8dd6063a897e0dac9ed7766f4aa4feae6c62c12420d53840bede1a043c195b15e58f86447704c8cd50c4a2c2a5ea0885bec79f108fa75d9fe8c435a0e1ca2ebcda96bfa0e74a3cb5ec9de098985e1e3f0da8204ac75601b5992c43a2d9492c071920a902d82b32c524f3e94f86cc7a3b9496015d150d1e780d550462afca67f8dcd5166ce384bd5034fb51ba2d91faf891e1f0a3f2a74f85115f03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b2cbb046ac4758418af875fb2218319f8627602b08c6ced57e5e82855ed2c06b","proof":"f074f64259f9e9fb8ef42d63d96ff1fc07617c2591b921bf1c67721304a28e3076c5e05e3b87f080b66906a92688a46d702dfde829dc49ade156ab49ed6989594aa9103fa644308282a130b406443f14847bdef372b73f9d5a2e7defd526530eb023b8adc5135e6d45ac1bb3c128f67a9616514efd0a3ba186c0415128d2a07175c602d600e8c786b5996a6158b825a0e2c311aa00a00636311c7cf0b7ed2e043b20996afcf3e865178fcadd4f7d23f1ed5db887b2ea2acc9b0f69ca3cd657012de8503b8ea71cffad8df754bca39914bf8ddd0703858aa488ebc1cf7db901078069da6729b45906a7ea5c4dbf65061fb11de049f595735b82987f5947904a1338f74053f36b1a5aa5b31868e7169dbb18a74ed1a8c97fb61b82e6d786c53549beced4b3e9c6fe6bf9a871b54ec9010ba2b604dfe314f8d455b3e3050c959235e647ff0a77c64cdbe085ca73ff94de544269964a04c11d0f1d4538fbfa6c600c64aea1a6ade79d36ad92bd215dde903d3938e2983e31d7006ee680c466bfeb68301e9f3772b3e97e30dc171672981b5315246b1da22730b984e356268f6e3575f4ba815b1623c83d91c561d4485eede4f4ca945aea49c6bcb70f2deef638ac1566136593a7821a57f1d67fe653bac405af221d2447ceffc920bfcfe6f974890e009a3161ce75c113315f68454878ed2c4ee4827af94c4190575876ef6f4ee906beb2d104492e358cf4efb9331e27540f086623467800686c07114718650dc243e49b667c5ac2ae2c870a898f8a31092b9a6e311a6b5ce563f6243d27c15e482e6470fd5b66722be9e2c704765c597837c69a36853363d8d2c67b7c30a7d480237ddbec6b6d10089a372794ff307a36f378f476be9abea21c176b1e262bc20e06fbc07b79bc4974bfc2ba68f0a2e3947e2ec1f12837351024a3668b8dd65d3c02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d06aa949d7245559b414cf6ba870ce5c0f9e25e3f05a26493fed592b665cc048","proof":"d4bd4211a4b1e39bce50fd4000c82455433a6ceef420a03a4a26b01e4becf51bd2151098234650d7662f73d3e783c65c86ca5b5ee4de7e6e5ced6338bec36339728bc814bb0767157d0036bbc9b2856c5a4a0e58502d5e39ab3cda16708ce765009fe2a0855d58a72ee327daa8ee1d2e9b2f9a2e6ee98f23efa58342a3a47a715cf252e12c95048fcf21d73da102b19aff4fc114dad1553789de1bc2cb93e40d16594a2e435ac6192ce7af375549b82ab1c42b2316eb6101d25320790b6f30013b13547ea38272e27829c387eb2ea6864b540f9327b77845bdd9fa91a6b11404ccc36161e63602506128e2d729acb073661f709173bbd6255013aa5729a65f7326fd54fda53b92f4f63001af19c9c018f4e96a469e9ecda08f8fd28d1c3c973dae18cfe76380036b065b269b0cb71192d00fdafa0661f19af644386e2c51292ea0180fe8ff875ac47b99593f80029e3df58fc5b58722e9250fb2d152a562b15dfcf5d0be208be4ea8a432c7ee3988bf316d5954cee139da2b82e2d9cf9e1615cc8107886bcac0abad0ac329d015c8512908be0ee7b4ac613ee50a16c6b0a3f71f6b84aa590044302a527747209366218817969cf5e977c1117f32bd7821a11565ac5b16d2caf7c6c21fac91831f499e575295a8794857949db3b225346cd437bcc3ae558e4df4f253461631daa7e359c6c467b74d74467752f54058e36cd081a7c46e9836a0b60d4a66fd7e0b8b9a636fd4e62d8e02e0d5407b3d970e72ffa5106963129a34279a881487dea3c9c3dae56008ef354c41e728cf6b048fd2e650de4df5cbe1487129829d653e2c2f8cc62718a8406b84838b5981ff94b7481377db04dce5b0120d6e35f4c8f28f5a9d394a043a6ba117062a6fc51ff39e59b740fceb273e907f127106893f62860416dc57149cb3203821fdaf4fd978564c62102"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"16ef650b8b768046a8922dc3984ef65bd6bd18ac5e545877a694cebd79d1c121","proof":"824d51cb77fb32c14afdeb1defe667e8d0f26d29eca62dc9c8661372be5c1f1a8e847a9a2b26dea04c772df876ff8abe9941b1a57325cba82ea337976dc8ac24c4ce9ed5b044d12364246f364f8f020ff8b8808de471df93a50bd2b6f24e806e308a220240f32774714dddb1789c37f83155e1fe40eceb224b3def852d533c3400450a5dda3d5df5f9c3e1a4bd2a8065619418030c2d84bce9e6754f98d4870542ca971e1035e7f756ce4e0d8b4d41a2f3a1af59c7b3e85a83efc2b715e9eb0f05781b2dac25484884ea0e9e5c98aad5fab4389311a07481e8a6ab04632c800c72ae73cff63f61cc066ce471ed9f9a18d1ead3623c16981e1cfbd734d13325138a5c5a7ea3330eb6db552677d98cf2570d19a1319b1c1889c2891bec79578e546679fd5fac6cd4f125c2630709e9f701cee93195580abea63291098bbfd89a7de2cc89d0bc0cbbde2b88e4071c4f452f5dd8fc0e911326431da143540fa7d16a788703ccbf9b0c74d4777247fac3b17f341cd37b8053d40bc3e732e771373a39fa093a93aed7845c324a58f5bad09362735d85a60e4506fe0a02639f2b3e6d6afe70255ede71ea33ab0113ae49834c46c3d04e527e6ce9558c28f0ee0537dc129295f09c6c93f5dade83587b2e6d97a8ff26b36c30335b8553109b4433916432f296b3e94941a683ef40999a147ef874e7abac2d4aeeed76b058c4e2c26bdc07dc23ad3d0cb6793b1c8bd5afdef8cc9ce188b287d298468d44632389f689b8345aa942b37cb2583ddcc171a888c0f376354354f0bf9a5f373417f4726b9e88035a1545d8d9b170b8fd6012b951a6779ac78fc2b41e9c5410d432e36769343235bd81c00c7dde9078a875a1b01fadf91bcc123e23ffe282e39e54a429fc2e3e01426acdd57b3fd46409715b1f959601d96cd875b7e77046ab252a9ca8362c9a08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"984b8f74361229af0177208151abe94eb07ad7253001e8369e9c0ebc1c717d24","proof":"ecf3e70cbcc4b25101f99c36a2bdfe797142b732b8e9f76ddfbcdf77f981e8382439fe8d1e364d81db8b269119ece78eac03fb94dc154ec00fda5aa9f292321a066a791cf3ce1f31ce82032e543381df3e14a2308781efc738f8377cdd37cb5f3421d06eb50a90f2b3c6612f5d3ba744b710b98eb6219aeefb0ab89f137cd8126c04bdb60fbc51b328eeec006f4aefca847f356185feba71fd8f69c0a90b940a024d592922206ea2aee9f5c21cc809675b3fc19560ada538284f42eab1c0e00d687964e6823de75b325067b7266f91e2897de868931c437719d4e7ccdb29a208f2d2bcafa701027ab7a28463211b06b76bb1edd5a6bf082a257a6a1f9d1adc61ead72b4bd09fe9653cd3503217517353ae2558eb159246b3646ea990d6c2d132a42693d7dfab096254160c574b6e496c3b91abeac09c6c401e9cec7335e94f4b16c7a078db64acd51362dc9da1a28c04f7c8cdbf35a169981291629b92d4b6796e12b68c4bd56a74bad00cd5ff03a6800b29a63a69419f6b7e98d5282bbef04b684ea1f553820448bcd73a599211ae10791dd79dbc55a720c060dabbfb1fe475de775c6da97222abd824b949c150f543fb8c0c602588886f5d227ae7a8818b24106ff10a240e14ffad67323f668c3ae8a38dfc4074955e2d6c00d7595bed1e0c8e70332fa3e62d872e0470e406fdfe62f62419ca51a6d9e9a8fb1a602a7ea11614b5471c3ee26520d1cb6d9ccebeef9aaa850bfb8bedd217f0846dc04320952ed4617d2eff80302ccaea4f0f5c8f545d08b6ce0d8039f0ddff19afc534bb34327670202efacbb33d28a16a58e21ee535765489b91c5b5287f8bbdb3167b0e4499bfe8e9af7be54c861b800309fd916b810fceb40a9219dbc4adde129c4e1d90f0f52b664aec30654428e0a15272f749fbbc594f247599b282ae8d001d45c7f08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"88cc2b3c2ace5b2f0dc68a276a3d612f430501a8cc09aad0b5b39c69d6f4fa7e","proof":"164f98520fa28f0b251765c843606ad55bae6b4d3ce32d47531f3aebfb684e097e8ddd63098620fc8422f0d9f60ca3f32cd1833130ca46f674e5795f119f8d619c283800bf94fbbdcf4c1e3ba6bd4ce55e7b9e654507a9f2b3e065f61a4dcb42d000b2b283f59f4d7d0ef28d77c7f71d941332685469bd2401d84108c4411802e89616d1b76d83396326ab8931f34706ee6a972cf781042f8dd1521dc361ae08c725bb92579acedfd0c2a72219dc5fb89824caa6ea719d8dcac5f65a076e430b212c5294ec7e28891d5274bfe80fe7e15cfcc96af426b9d1ef0198259e230203b231163365981c280f8be5b2eb0e5706279fdda6ae613ce886fe36cf31075c36ca927025c9744d0c2c85fe51db0e37b6966433884847c4c9bb1a8cdb237851468404323630e95430a1876f1528c1efddc2ea1fd3a8fc5e4acbfd64cee736b65d5a38552ca6047fe6d0455d7cdb6d3b1229fc1ff17a7a4031610eb20d0f6b2f3f2843a89ced3149a9329b0e998c070ed57aefb7b856e7b50cb746d9a41265fa5246b4a5b385e58307a29b4dd87f7f6b92ee90200af208db722c844b15be1a6009fae31d4c03b479bcc7bf1032c09d76dc1e73a52494cd9d5dcc885ad1cc778519ccd2f6e7576e527a23c6389d6ab4dde24844efef178777137b4845b60d5fba6590ecfbfea115880089f791be79ca0599bb025940ccb6a8c8a67aa09165b85826cc6912effba18409393a2e3fd3bc80f078c3565f2494b698e69bc95b2d000e5a9a8fb6cfd6628d2c636442ce2f7b7f398dfca14e5200b23b3aaa728e56f69a2ec84bafbc0490c005e78a18e84dc5eff29ab533e5701c19948174c2ee451172537e8501729d96b7abb090a1b37eb802f1e033ca978cb4c66f2afa52ba7ce40903a91bc23c05f7ad43f228fd04009b4abe77ccbafa8982ff8a3db0a7ab065a1a0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"160c4323a69df3b88031f40a8ff9c5243560fa45eabec1af3c5ac7b54e6aed5a","proof":"fa91edbd5c4bd16e67451711b4c60099341ef692a8d06e36029d573d2a83487422df3d7077c027c457d76b9f818e2a45dc8cd57ec625492cd93caa2660f2bd5986d49953b3ca42b76d6e607027ca35af1e7d50d93ed493bbeedbc668c930a773624e9188370478ceff92015d491c1c8a7ed303a1d346058a5e2cbe124992d32272522020f8184677a3bb3d91b6dcc471ea176521aa238b008ce21dd0dbe8a3005f63c1d72157ee38e5a9ea46ea228b2bb6787dff85c9409865e727c3671d40064871a790edb4cfddf6598e2cf65d1b984cb400013297bb781246f7fef2bbb40052ee96a9151a952025b73ada145afdb2a5a66492f35d878681e1552b0f4f5e529aa1939f27d53640fff9408acfbf5c1cddfce0e480c2167ebb9217f86c2d5e40d86643b6333fd8201405eea6c2884e9fa99f9c329b3147241abb476214c9bd58f2b046265018286a3e8e71a541a02364c77bcf85733f4a5194047181fa36ef1638fc7330f3660efe398598af6f25509eb6b48ee071f4b6794179b9ed41571f426e4cb3f5ecf3c7f3beb83475cd4c819882a6da5dba9d9d6c82f45eb039b5332ea82e9836e2552304d400a60f66aec017b992204d2f7ba458df4990d41cbe92247c7ce8ef5d4bfb8a6005a518125c2a64ccdca7c5a5ed9e7a53b04ea3d3aa2e6fa2630e1f71e59dd46186a7b122d5bf98935c66d6adccf6d2b82e6b7529d3be0c9ca876eb40bf3658ec05262113d6966e39d09eadb818b20c931cce1a35af6b014a4ff79ecbea0d13a766fa95589c6637e9a77ef9b549ebb199008e1fd9072b0f2c044318bf3d075657411ea368c400621b5fd6173686f2021f849b076a7c7142bc222013f567c13baf583f6ddf2856b1babfbc7d11d60ad9522c9ae9c76efd002c8913f325c2dcc8628a49022f1178497df589bec5d0ab23cf8f8a07e85c790f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8c40f46d1b3c916079aa76fa96cfe7b9966b91b6efefb1ff24a9a71131c10330","proof":"f2311c244fffae6a4d0ab7e89328f4abcd1ec63a6c66403f6a5c645986b9ff75cce47a080db5fe5c79c1497a9151ca70bac29fd32b593aaaf0f3663bdaa2712eca3f1c2d7000929f9728120bd5e1aed6a147970c660e0f66ae352b07f5d941484e283b4bdeaacedb2fdc0df7e1ccb721fa348b8e8b8c9727d3606314d909e4718e24a2dc7355609df66d8b675db52456490065767f7405dbb9a9a81b8f46e80d61e18c35585d488f48e33cc83da1ce401b1c3afd752ce43df74af5f8809196079f3dd2e3c889369f04e4be007c8831dfae794d40eca71977d23f6ae26e90a40d26faac2f39d1fb30a6b729f73264f36e3b18cfa47e917120f994715921d4cb2a3c5493b97e863618747197c2c0ced80a8c1a82203da6ce923a50b592c5a8f579d0befdd95d0e6435cd5a3d06b290f94b01da6ea6e3dbcc786e08522cea42da4d4ea280513c9c0acdd175b7eca77f2fcad624b9b74c031fe3b7c89f432e570b5988cb8c86d5c5f9bd8bf95449b7dd2f5e8b5412d572330b468df1855d1ceadc16bc3bb4f78ec9ac11d4242f7869afe0849473d7327e0dad7407a989794b04f85c28a08288a3ededc169dcf3a8fc3fc9f97c3b96b7b8e356fe50fb1f5585cb347876b07b7e0bb9931cae19511f1b4f2f19a9be521dd471104ee180bfa33ffa4f2220ba638627883061f6ce335d7f2694d429e2e7b99664362413d7191fd8cb8a2a62fc2d3891e3767d9de68eed81b774a0b917aa23df6bed9af2f176cd3f0c6d4e9e65176ffcb432da018178f5ff2ded2c68ba260c64dda081ad3badba3d6d541b828630f8b9e205a84958994e99d95d965e72f53e2f3a5bf2dd6c7a5fd18c7f0e71f20b1008bcecd0de367e0787f26a0dfd5458f59ca225ef2283f4a5dc401f0f186bda6b15e77016ff478caf709e381d0663c4c1eaf97ed8326acc9fc6321607"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c4782b0c798ecde0fb8c60c43a2fb0d79740e00960a76f141c9a9e89a0fbcd5d","proof":"e4b4c7f597fe588d1237f60effdf4a5dd9e62e5c6e1b9114fe0b17ef151e635ae657c806f107311d9145598d03593be3765c4407cc7dd201ffb4b3ccac953f2a642d2a7bba005732ca8707da15c063bc57355b18aa86e4e0956a075474de6c5ca29b46c03240b44d220bcd793ea6592088dea0a5faf5fe45ebd20a6c9637547cab8e048ce0ca7b64afca4d417349723c5f2bd892faeaf9bbc4f6b106d4d7350e5892d1a3dfd3bd88d349c3a8694cbc97d3a549067454d6f29fcf98eae0cf590cb9a0893d3a47403beef43de8a76d6459c7b397a079fde40dccfe611dab2b5f015c9545553b7679549e65211e49dfbb3b6e3824c3928aee9918a117dd5da9b874b854bfbeb748b3c9965f1402fc69425df67d23ce3df950ef255110430d3555066401c007cc67b1a81dc0100b22a87706a714d94140c91c7374c720018f3cc1623e02eba05619b5d01212cec1c62b01dc4b2fc33de5a3304c28db5cfae0acb3042ebf6fd60ae278a742879ba952a698b79407ebc13a8a99b767d7ce738fdad978441c46b8bca1a55ef51abe3070860cbf02cfe854452bc49c640e33e9b4448b68eed09a1113c250c220622c6a2fa95f761caf9c7d9df4efd6b59413bbec0d900c9a08079c9cbeb600c9b37dc8bccce91e9db5be6be4814b60a20f50841c26135250c9f81260bd67ce0918f29eb43a6bf23b4c8e945d38250eec500e9d328315744c0435bcd766a735fee78ddea5cbbcbdae55088374152ae991bd8027b63ec859d69a513f76f82fca6887395dfa197cca107e2c8e01efd37d940d875ce452f2656a164acb1f859a1de908c4ec864bb0b755a5b33dba2d1385a7dccaff0c90b904729a1fbbdf737589e6cd56fd1204c8f543e388ffd79cd28fe55dbdbae2106b0264b12b40fe91800fada689357eec911f2e7bd2a9ac0023e5c69758ddd07f3c07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4a19f9220fdd144fcbbd27087b82bb13e25bfc94832dff46ca775a2fbbcd6a6c","proof":"88b023c742f01699e42dda75d027324765cf42e45b90dd514341dbb10c8d5b2a3ed88a5dc60f205a1f13bb2a8d155ae1698a3961d5c0300ce05f4350a323177b264a83fb2c009d8b1762621d82501ac8a942b183d84fcc0c47ea2b5969ad0f3f6c7c5ad4e442f862f91e619cee7c1d9faac867ac633e09572b94179cd69b5a4483ff82b50f831610e06c0704cea0e5100284a761402afce1e4704557f77eed00e6f476b5ea6f118b2952e89b3e10a8c5c523af4b74aa4b0c41d63f020199cb026399a9b0409db0a0e20bbd25dd129b01ef64643eb90408167dec0c1c2a2899029aded6bfd3c73398311ddc0034f143fb08aa8f489e289a28223a1c5f34711a43d4f7fd047fb9822ac33b778a83cee08daec555d820f07f743c3cf62eb78f3a14c8fa5bd4e5b8a0b2cb2fbca1a1ab71f3ccf2bd93b0e9b8a0a792093f7bedfe1a0e3d80941eebd31e7b31026d10e7bc2816814a8e994d17624dc32562a6c3ea280cb6a6497ecceaab0409140b6f3a43e5e3e9083fe5ec06b43dfc6b317968e0483cf323ffacb780bdcbe7d2e0994f546e44a7521336c504ef50edcfce20757a67425e3ae018fa0ea833baa3066836fe43df24aeb49bf517c3b469582701364c3ba690bcbe144a8e6393b76bbf092f1b340142bd032be2e8cd2a0c7cc67de7ac05f4453d4061a7e9b4473acb0426fa8c89628efa8aeb92cceacd8da25014fed91626d2a6ff4bc3915c5dfa5fa8e55a74fa26a06b9f98a85e2a5ccf2afffe6e8b488ede542d4902d4764e7748e14c90e968772c91c4a8b8d62acd745ece356bdc2f382a7bae916170402e4d9005e6b4dd2c7e5e9833597583fe4ff3a3e4a53cc7340d9e7a74c01ce5011deb1103656af8237937b415427d76d79f5f13491ebb7808083aeaa68f97757a93a580adc5e3a93e52fe8402e380d23fe0291d8b179af009"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e49616ff84471349bddd6b43498410ff28d7fcb1b9630a05671538d9167c7e70","proof":"f855faec80ca9ef06e21c82058e7e7c1d707a48931518efae6dc3ac268876c2312d8b1a720aa8cd79328efc6f1ca2b1a9aee2d96f5ca782bf46d89f112422f637a8a2a525270b0b7b8d2a4e3a548f38912b95a8bf7cc9a49598169bacf252946fe794004b43330c2075ebaf543680f50f10f9aefdba4a3833af8c8799c40101b55f0cf59d01483fb8c38a729366b1d80d0f6ff68f82e29d685c57a1baf9cf60dda73d0b9605ebc5618f4d99e21bb9a376fbfeacac4a01c860918ed107f86a101f8fd30cc8b6c270b9bc6d4cdffcbdcd9a3e9e9421ac22aae56f7c9a1e5e2cd0aa6bed17b6cef0df0e699095763dfe02f09d1936f4932bfc13059d993f0ec0d2db009ff1e0b1a98ed6bd48d969de2e50825625a3b633c0a181998e68b20d7a63a54a87309319e9e05e518d46651390acf098fdc6fc9228b57da7ab96e75e0a63a0a682280d67e73442546dcf4330fdd4fb5b908d771afc60861e6668effd546539e19c40975c71ad0541e2b4930b81d6a17d2deec8a130220c8043d6835d8d470a8c1d0044ad184a87b587028be8a80d73d193787f1e1d37cc170c5e4b69e55285cdf72b32dbe03d2e8a4f8c6d3c199d422d027a42ee817506b5f123c4421f2401e0ba8d60af57bde8fbe9ed79e939e5bbf0fe5ca981a3f4769853ffd447367046e4bd220faf749f0c0b57a7577ccde6eb9ff99be7e5891436a900e17db725b0d082c86c3caeedbb42e0cc1507eded51f0e51c1fea097dfc76c57b6e2061c554db0e59b674f2e0ca1aa51b5deb4b476a2e89dfdab536e5729707d2d2b3b86547aa05c8abf39c6edbbf2f84e3bbfaaf812ecd975288ef2d008044ca127c3e5df23ea9350f64f051032d5d59f6825926d43093194df61af101e055285369712b40e5ad312f8d1d6c6e6f16d68e91d54730a48656376d43e126bfc5cafc4d3a76805"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b8d62a7f3492a189f73a9ec3d150a58bab046f109b0248c5ac555ac1e4f29928","proof":"5088701db7b27770802860c3da81d8cdfda83d87294cecb46c4892f7db500961be1f7d313fed30255b7fc4cde4f5e0403237d290f2721c40815b90637b6e3f70c09588606464b721ec80f87581bcf9af973c246d63d8c82e8bc3fb7f5f3db9552e01c7dbc710b4ac78109084c9a2e0a3534b88e6e3da5ccff0cc103632a879402e01f596ff2a418bf613643f61acb62a3a34c0f03bbd71fde8c5d2621496850fca54e74317034b17eac0c6898f9c2f7828da89e8166705d9ef9fa1d2e04dc008dc346242a2c176aa30cd17617dea43f8783491c389e36d0d08952729d581e5078c6a2bf73fed55c6c14359ac0104298eb7a22ef2e03b4db59a9710804a67a70870f4c91530c52c1211b93433775bc3296a54edf6686df2ecbb451f768eba974508b2600680dae0d8bbdcd26af94303ba18b043e08c512d12740da0140a1d2a5c3caadaca77d8b85e2f9d8f50170dbbcb13205ba1985e736f37b563110abc5b1db2f2373146088c1875e5c519c0600de20c1a26add4cbb51ea6879c174e2fab607c58dd6d09f45a876178a1e76099872d945aa8678194de1beca86c527d0cee13ec8f6abaf23f8add33d5668dc633036fd20977ea39d60c9935e62e0bd787ce3eb2f66ea3a8980a72b612738e2d81f4a8223750f9fde6fd37f35a73e0d24aa71f92ec79bde761ec7d1148810146f9c15be714cf81f794548512e5da5dd3ec9d7036f3c26efaf491dab7d138d1df98ba8c9453702a3888d6710d796e8ae2f4352d2606b9058ec6f5bcfbbcfdcbc6913cdfd4be23d66a04dc1d06a9b4b7e39faa1336c779246fb8ac297c3999c18f0ce0007a9beb23a58f3824eee65f125644060d5b79ddc033f0b962f057e8719c41c928fe42a51df442d9b50baf4b31badeba062c467668b39ea35aa145895bceabbc2febfd177a2ab10e5ca6c3c51752787000"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ccc20c1caab5a81c53b4556279f313bc0fc817bf17247221e774e4d18c0e701d","proof":"5833cc98ec089d79bebdca1670670b20edcb620c392368862aa0b536995aae0f56e4ccf9ea10cb882c921166b9a26916d4e5f064f453b47b403c3926d802fd241a6f6429147480f6278b851199bd6e436cf8ece8c2874299a780231319288349d09f26e6e8f569e4bb52342023991eb87d2ea142a6ab1c83326bd8dc12590743d62c29d11d787e68b1abe220f6b02a205b95b14c78538f113cb968d1abfaa003650665e7396084350cef67ffcb38088cb42a040ce7c5806953e899402d087a0882f18b36517f545a2931e78165cc043c3a0c203edbd97df8bf1b9aa3126f20007c1e148dc29cd4e24fde46a8d8e189d5ae280e625c5020813633469ff6ed572ac242aad76b562aefd9d4ee36fa21b56336296d2fdf99ca0702bf3ea436875535be683e0dbdef6ba37ef3f0cb63d3425fb7f81a6c1eee44a34bc043026ddb6a3ade16beeb95b690be720a4b53aa2c7b1ad5fd291079e469dbf178cbc418fbeb4aa86af23a1c62f28ab4dec9bf3a79e256a139011a50887fe54acce4d38e11a84f7e3c4ea2b865a250c9b2599a44a47af96393816628031485994ae78fd4039c5086a37d76be53ce2246a16dec3dace423cfdf450f39ef133f2f4abdffe6563717d8499303d2aa4648cd201430783dc180a8955e897a590c94763d3d3adb9a5d2046b44cd707b0382eab4163e436216121a54dccc4060a621d9d4bef2ab924b87ad6216e22dd1d1a2fa1f19ca1b1fa3a3e61f462a78932b9fa0c6a6a6d4300307fd84ff1cece8fd8552c151a66d4ba777ed78e34986cd419d63caf937cb9a3055f660378d04e48cbe4e3cff5440707c7e2385fc4afa8c28e03a8e3a08a4abc224ac1c69d74f609b6b25beb92a7c3c419af97a09c3ad1d8cac92944019fa506660349af6f0652b25b0051e4f2731fa946215f9e0b50ba327b9d1e9a61871222a300"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"be16382a6ac1d84466190579b7d8dc77e9eabef7ef733ab515c55663879ebc7e","proof":"2e581c6abced3fff6bfd1c4f7725d2c5d27e98a710346f754174263ada034f47ec9e33390dbc55540aaae83db75f9629cc92ddad3c3d8644f6a9bbc34d32e36a9004535db64644b8bdf7931a6dfd2c371de4384506a1b7623eef37cf585406647a9453eeb3cddf69d45623c8a6751539edbcbbfbd324d527d8bddafa8fdcfd1743b2878c740f71cd4fe03a01cd564b74711d2f45feb0c7e6e049f7e34077e40afcdd5124edcd2d10d2f03cd00d630ef36dff64c52295e663248a2272b7225f049693c6621bd2c3816a4d2ba2d8233114bc52d43af37ae05ad9303c62e68b40018640853272ec2dc0d0af3e5637f20503cca569509be8b8596c909d49eadd5b6c8c617a85a28cec05f972eee4d4e91864e71d5c7b8c88314e48593441f112a526e874dc4ba314f5ffb16c8f1a191451e4d82e5926e6906df4594673f1e9c12a228e75b741f478795a3e44620547ed0bc9e610308ee91c6d59170f310c7e873c3e3256802082da2f01ae3ea75b48db89a1d820aa0a4cf2921771036beccaaff80d5cf1990922b5e911dd9dc78db57d17159446aff2360dd5d31babdb23bb4f0d3b46540ccf5e1bb6ac22a6028ea8a9e6bcc1b00a27b722582230a4e61698fa332b6c5ac53e986cd810162f0d3ef290c6d2cf59b7713749cca11688ef051de29c04f85a541e828786a3ecc9df3cda2041f36b3462fea9ef6304634bb7d41d710a17521548b74ab7591c2dacdbfe443ffe7190b66905357fc6f6c85198baa623dc59f88acd8af5e26115cda532896b7cca3837698cca6160121c218682c9a1226634369a9e0354c48aad401888577f4e1554a5583371f768601d9f5d585b1680615bc720f93820c369f0aea26009163b0904c601b9f746e74bc49cd02898e62de106d0c58799de5cc13b46e5dcd456af137c90a0cfd080697c3d7f64e53a0265a00b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f24c21f72051bc327f9cf18e4de7a36f5929caed80d48c189acc162441acc326","proof":"5ec434eb7ca650b23fe4f6ec9d152532213e29c3a070fbf9afc72e64b240cd5aac1816a4a05dc856cb501fd5aafa0373d6ceda7bdc7fc4b602718235924e852f782b6a6369b6e7bc52e58de36370a31016dcea7463e12ba60f2d7d21679b957c1c21f99b1b0fb0f4917642f9efd93461f2c125f011085a803c36bd4bf7b1c00d40b56f1444e291693611c097d362c05022bae8379c2cb351a4d45ace56d5e00f9bf9f2415f4200d93101ba7fb789e67f30f6fdec11fc0e05e9ade37983e22a0d2f764b041aca767be45198eb9642a3e70028f49422eb7c90c70bcd2255816e05ceb87eb0d7f605e9eb5a2d2a457db12642781a34b985cceb5937c486712c6b0a6862684cb94ba2acdbd508b141e622517b825c6e1b619b5a653c58f9ac2b561d0826834aa7621257e7ff99c30a474e54afc571e400a16c25a3408d24e36eaf257e79874a157832c1a553a558c534cab5b0e2db4e0b2594aca7b4fa6fd26b1638900b439ee7130b45063cbac5044c10a2bd1cff0871a9863896724abb21b5f8037048d93c188a9444239e471d3663b15866065c6436b0338b92e8cf9322c6a66c3028a6fa1dbc5bdfe65a249d9e32c121c4a94b4aa0fb8db757999e3af76da36ef6fed17fb8621051d89c8def520698405d47d92c497dc3fca838d0964098d20c5e9687d6a43b8f065a8342e6209956e9c1b5e5c84f66368a72e4975ebc220600ecd7a687596126bbd89f54ec03599a1aaafef5f8b1cb459d22de94b4b187875b828d622c9dbb5ab7bdc6747fab5acfda3b95ad168a09aad6953ad73a3a77517fba55fdee05652ce23702655574afb67c960f361ebb915505220d0c1e6acb83568c584e9437b54ba4ec466f9d3ff9e5dfc42954a9b5dd3b5c5bfacc65ca67b1019e086236b7af6816229b06b18b3fea648f939b9541c8c7e1fb0d6e693a23150e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4c651de244d42531fb15dea91f1d0249a687b6de204f34fc29264290a2472a2b","proof":"4298ee041d347a4e0f34bbc854fb789cb4b557c006dbf606889b6d0a04d4177ef8691d893fae0fc0c8d22c4af9f88f05d18f16dc8f4a205164c461cc4199b51068e82569e17207e0e891b204b320ab4ebdb3524d5e75426b8a647930b008ca31b2c248d148b75b66c3335ce04501970362f68d709785a7a5092929dfe603194bd74b11b413d43ebafee8551d6860e1f0df34b0d691ed2b5de1aa2fdb6866ca03f7a59b6c43ca60f10929bac00d4bad70a5c65b2d3261ef0ac634ecdff2f6a50c6c5a177441de7b8ef7f87662b13b81e17d860cdf2db26cf289356f452b40620b7c5c839391a082c4ac3c590bf2c95feb456b8955820a3f41913251d27271a925167558ce219ad829e44b045e96846eec86179327b9effb6092a86f95b939d344d8481e8a8a2875480b7016586f9ad772b2ee157b92ef2754f149d03f7760d112ce2ebb3de15c2d95d43d37a1c478d50d78cfc9c3fc0b887a73580f9144ec254b2c415829d7780fd82b0303a079b2efa8dfeb1079217ea5522bfb8781bd0c9c542ee658b408f4902731b978d7e30d90ba56b1bbb4d003e9d2d2eecd9096cae95c846e08049f8c4f86591e60ba20a78b88b34c7531c1f9d40c88aa592c60e1b130581a6bb620554f9b52d4317463fddfd45a64d915fb1c8d4ac5c1509fe6ab6e503e4342d561b8b147ab48b6f0c77db48f7269b7f3d70bd0f0bfa6f2cbec68c00d505aeb0e2003dce1c2489563d717aca826834cb28e7c43c82a8a9ee554c31075028802cfd86bfed9df3f202f04446e69d5cecc6e8c106e084e27b6712578c32b2ec26ea2761a0959d1f11cedb64eee40b3f6ac98cf18e9e3c068cd8ba0cd51304996cd3ee0bb50cd191384ac0c0eed7ac76a35a387937ccb5bc869b96595bd03ac4c0779e66963b645eede977af3f428cb57cbe98117f0d236937cda9584a80e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"520c60bada38d816cd274bdc71931b30ca37d9f854af49015e9c6b0a0b495d78","proof":"90a00802cd23d01a3a03c87a49a0e9acc7720354abf7b5c0118ce85347ad28273a7368544f34d3b37d6e1c0f09d45fc94e2f823ca2a19048f91781788585e827e453d0b5fe998fa7dabddeb9f9f9e92f8b4c80cc85a7410f595db8fbb8f21215dcbd622667a7597d328269fc81f1c02ccd0dfca7c9dd2be8ecb3834cf8fb405238623417401005b84423d74441c6f1e1ebabca09b019fa94af457545db3b7406bb7b2712d4cdfe1c93dc80a9ac38b442a4484ab27d20513ba206c030df60bc0ed99877eb4dc125175a86c3eeb702decdbe32913ee19019dee36fcab27c7aa80aec61183e2086bbb81103570fe9dfa241aa098db911889008d82283c56eaff015723c607b535a93bf57c5a6989be4eea8b14ee244b13fbce592a961ba3771a2473e54f63c567f28d5329ed5bfb09e45dbfbb9e84e83a2b435cf68d16e33057c1268fb2921447325325a63c2fc7fb23ea7dc44fd6b2cb464780de179896f73fb47c2435c5e702018e036988cdf7270ab58cc0e7f78e88e917dc586062cd930d32d7e7c48567938e7459e5a87f212936d1d12aedd093f8917c99619c9cf02e3921d665a6de0a6a8c1675d627a73cc37b85294c1b571b3b1c1082852fc4070c37a6c1226d3fe6a28b97bd7857db36e15765d78616f48e8b8dcb930701d73b2f0255c22059fce2709b04f9130673c55d96019cfcf0ac9277b83c8031bc2217a86511d5c9ccdb96accb217a49c553a7dada4697fa589a64090f91874eb5050a5761c113a74b5c84ebe47b0d68c84fce115aada1f4bfbcd329286afa7475145357ed80ab4d48a64dcfeb1b5f5514fd8b7c977e2e2b1983ad5979fdfa2f100e7dea8fc05a006c04c39074d43210d7b6f10c44267b570a536cd0770ecc35aee0266d8b102e3b5ef183515adfa745de789d6d376dd7d30bad4b71d44386e92ba7f8727960a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"16180165534237bded8b0f2889e964365b52c40b31bdf518a619105d2fcf5f77","proof":"c2037261f0c28f9939c06b0af4de169fe803bfd3f88a121ee1d335a8bcbedb32d46ea60e14dd0a83c105b5d0203aa1182fc8285002c858fc79379fbe1f3eba1dbc3abb5e3a22e72da0f4e8039db418e25bbd9a3ce545e51e6c47d7258f736c575a762d973f9c42baaf0664f9ef90250ebecb2291b3519a34653101fba8eaaa10e02bb970db6caf93f7195f5739bce2b2616b251f4abf0e9c09613bb49e91990dd848a97b1e64398edc1160ca5d8e96f3871a1f2ce9619c033479eee6784c980db88abfa64239fed3f1607bd6793c5716bf35aea89ace4f50927ca1bd9e07d406260cf694bed1bbc210802bd2183c87ceb6895631c6594346f81dad90d309c3414e3155d96efb046da70a35f6b3788dc19b8088492231a84933e6e615594c3b008882764dc08edd677ba3159e8f344249ba6f77c90672712fc6c6be02cef6323936c5c700e40516a4c9fe70ba4da5d664248321abe6918b8a9d2508d87f92e325525bbffad58ec45099bbd17e8aafde2f400994f1f48f20f62c48ba366af5996e6ef8f51494acade42da19467ac6211f8fa0a79874e07b94c92c8c7a7189d666436d3f947b95bec7e1c502ad266fb9b4bf246c303bbfb0caed543e6c5e7076c2fb416a91decdf100af9191eee92fec6db05ad26faabfbebb6612666897701dc55387f72272d9da67d210294e500cb0539c5aede2684aa6e54a6b8e07ca71917275ce9f9322ec7c1c271f5b6b1cf0ef5b562cab1cf8d84b429eaf7465ba344dc40c24f3d03ca4fa6fd37def26a0b6bc90eb400e20f82e448a589ae28e3f2c33224dc3d26ba43f93bbc4d771371ab7e869be8b5ddce9803dccc62a2576211c6b475a44310ce042d2a9d074c39f00ee6ba9b551df594cc447deae44244319c5ce701c8dd54e6ca5312eea9533160555948b73c6b991122c14511a226c433d32e3305"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b2204799ae891ff262a707673ab83a6b7e844617c133c569dc92456df3d94757","proof":"96a13a75f61872dd3b433a4d8ae0133526095d187a4e238e83d29de9a6bc0f1a7a2f3feb601cd3acbb843be7f7df0fc07adc5f2e8b6e02791658d0e9d1c3b13f0484ff9362872331292283af51806005cb1cdfbc0e41b7beb4c01896ebef621626f6f62d6365091b0b1dac008720d3ee22de85e6191fc4c8819b312b69a9084d6c80840f977c052aadada0e808775ad75fa1a3a6694ca67b164f81773cb78d037e80906f974e80ca9924abff7d2c32fb0a18d57507fb80ae723809d7ea3606031d81c9a9d35e8127ff4e62e87415bc51e9defff9c704bba357becbdea9771c0906bd2526e592786399b748effc949370e974c8a1e265d8bcaab1acdf4ff35f49e2d6c53c06b33f5be0fd07e22d0e7810af510bd3fe65380e8dbb450a7593725d7ef20a41cb96af1450a3ca1b6c1a6e8acb18ef3bcfb53fac09b759d9daff697db0bbc1f4f58202023fa354e37dad78020463511b85eaccb0229f15c1ef6875296c47e045f0c414b31dca04fa5991a8b8204a25c412e65f582f5c00f06b39586c20130d5360180a8c2b96d2f9528be30939c67d7ef7c29f94b74a1617b16f35214e3fab6d858d0e203cdeda40f4b4db94c089022faaf5891f742627f24d0992698227eaf0d4913c12551b6dc8c27cd4ea96c38d87b5266580b73c8d1c4805d246be51e929881517bdc85f88b3cc4fa155b9b28565899e7f9f76a2586278aa5422fceee56efb5a385c84ba0325aa3c4296e27a46422ce2fb4227fd541821885d4cc8734cdadde00a33123ef06c64c4b062ffa94f6ae39434d9aab7fb909b4c475ec85b269c90d4480740a244a938c5342e10b1860b3c702f844467b7b83cbe1c78ade93db27679dc7728792394a649cf141f26f78454f0d20820de0ed6a2e0420ba49e986cd0592c2817eee2874583d405bdb7b9123f63f6c835e598dd0329980b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6c369345422567f88d4225325a1eb24ffdc343b4b227621e038ae1ba53564b5d","proof":"04edf0b8ade6b32a896e3685c5f05109928fcf01f1d2251440304c6f028a37220abdeb97145351ebfa944574865f5f464da19b721948bb168ad0372a80d4e22c52774a2440118b745cec329b5f4d55f3f7bc35d9295e2734652a1347f8a0cb075cd1937a77031eb31820a739866110fc09d78b72fe2d7699fdacd975f3d55862fbebcc24cf9903c7e4969f098400c05b11a25fbaa3cbd6212629c24c364b1e02c4e48c86d3c4f899949cde7d8112e289699fe3d93703f152b963112555ef000f8b2b3232f5138fb685ba97527bdbb6052556911246ec549a285666d5c501f502ec5fb830b38b38d320a5a91548694894b415fb41acfd76abce79dfd5eee70323c25b80384078a382f53c8b3f0443905a1137ac467c3a4fe0f6fcdcc9d2382f0caebd0079c688b17857a982b6b94ee1a3af7881e11eac330962d26706cbdb9550f8a055274cb2199a67f830e81d161fc23493e393e1e0757b17f42d3450b6043fc0a76da4900d654fd9de98700aff1ab82928b083f538c28f57f171d4bfd5a87eb82ee6bedca5d538057b92c64c18d0a3913c96c9c066ca12f25728330761a242026070151fadb4fcde073787dbe9f140f6d220b8d2242ebde08b924898092a339e32368467e8da0808e0af17818336c7c0227c65aa81e455e6243788c5eee16f664c2480d158b726845dfe17e034c9065acb1fe1952533fa0272a9ea6f228c48bc18ce5cc4cef7d551becf2d27bb5fad3c0be4a75c40a6c4c203a6d5b3251b001c6c505dcf04f421e36484ff109c1eb7ab9edd216353aeba2a69459589c3d254640dd2536940837f2db3b4d9095e03f561ff218f9066a1b8a155e508841b4002fa74017b4fe3b9a47d69dc40e21e25e11cb60319e47eb6c8953be7a90435600010aab05d6b9559b209ee7f18906350162955b5ace4a030b5e065573a6512ac07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a4f71e8d9e7b6c7ae939a42bca22a02ff243d7430e9b6ff7b6cfb9e8c729d245","proof":"e043d235752ec264e158c4f5755278fc846cecc0997d726afd65e1a64f87021800b7caa4d63feb421fe27b655fbf90c5f126168f936ea9de06dc1958c9bf9b79a648f5fd31e29691df83a5c99cabff606ecce87f46359b92a6d786ffb34e000c2a036e07b58cb486db8b042b60e408f8a884a1f306145ff667d4d79c5cafbf52275ee001a7e6f822c424bea0ea26eaed30bb46fabfe081bb0a93b955f9badb051d77e359fe6fb3f3dd7b13fcfb9f12e20f8de80bf73d22debf3366c1dd56a80934fc39ea69710928407c21c815ae0724bdfdab18e2c4f9ce2021a15aa87de900bcc7436abbaf69324d5894ac78fc482a4e0db4dea7b438a688e0fe3c583f7d4614343786c5b1ff45a8722c4cd0c120a0077ac9fa6574745788d62fa0df47b15d0403cf63c7246485de0b032c192d4b174eeb5a754ccd9911ccef36edb6e24923ba18e8cc4a3fd09a0da1f6a12b6e5589a3b66ca74f127f6fb62dfe8b0739a52620dfd49d384dc9da90e892817f39b5ed24ec836a5545a3500663cb3915a9062030ffb8415217aadcaa369a20ff7b14a1106a864e68f4396eaff535e6525fc5006cdc258f10869d3d4cb062f19d609c8e00a2b58b534f7f2b653cc0fbbf115a454609fc51c14d7174e0b5739fa6314c08efc5d7fffd3994f5085de84d5240007666c33795de05a5ae1060f5de2f733958ad4944dd47ac37d4b09b3540798b2c068cf92e39d5bd950d0bc6ebc6f01b78ec162d8173eb522fc81a7ee5ad8b268f7d68ec5932e3be77b747aaa1fdb5da11d25feb44b23b2f7ffdcb613d8415583345f01dc9bee414726b18ed7e938dee23592d7dd7f77bb64902133841a2bf3a8006f02e18e2994daf06d952afd1a2941cffd50ded68f568a514a2cc27c9b0b2050b0eb309c1ca74dc34db08ee20f8ddc21b7de6775ec9baec5b6c9cea83ff212906"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f29eb5df76ea68155c75869c6bd48b15cf730ad981f9d9f1533d924fe82c2731","proof":"3e4178242f0e62004afc93c57375d6c3450d2f02a7296a6c0907ea0058cb0d44448c9d1398aadd6cd5ea6dca840b909b241b25a84b64d4f93a4c0085bc4bc9106214e00933f62382f9e9519184287688b8012306c43ae0bda6a839e0ac94512e806e5ccfaf913646e2da81856f7ec942a532973a1e331c9b612218a31f7f221c807ce33ea85c14065c6501a3c8faf3593cb2f017ab108e4d0056bd0e6d45160b76ec32e291d09019a207acdad04ea6349ecfdf3a34e793df6388747b9145c10fea9bbce36d0f0473f32df613158ea900fef867fa97798f388132997140c9010c200841575d6313c554579eaa086442b997202810aaf5f22b9a79bef90da78869fa517d5e8ba5326953e78eb233510e32c2aee657db3eb09a8c4d9469164e874a34471f1780fe4731796210457373caffcc52d8749a1574be719eda2250ff3d5eae27857f939664078642402096b941bfd56dad185130c3bfbe760e686c8c2c3c6a78f24aa126d76c778ee36d2bc62374e86cba7ab30539ddb463e2c7ecc5641b3839b8e41d113ea5494b8fdefb95a21e90c949d8756a6832699e229891029a3b1e74da80eaa09fa58415d5ecf16134627f1c8d9b31fecfdce62948d183ab977580b9412dcc5283e93e970a927b999aebac5b85113205e394411d1c74327e005014120a3075916153d257420e4e47510090c5cdd94370fa5d57150d3524b7623afccf49cb6d05de927bea4013425ea31f0058f111ef7168bc2aed7f8cf26e3e03ca60a146c8ea252207884f3348dab5882cad8628074c33194e0f21bd117bc35690a4b4a35966457ec677da7cc39daa3575e0615ba3f38d990ac5ef060c371433e6e4682e0aa5edabed7a0dcd6ce65349c14efbd74d11cf3a998c1bb7aac966021279cbf80d79c1a473411bb96f7d1232d588fdc783f543d6b683631d5633e507"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ba8f7c3118de2ca3c63710f16d00f7c0a8273ad14715a935dd5caf08f596087b","proof":"126555eebf5f95d52973a94c5fbd1bec075f4d201a7b3ed610284442f912b07aa035356301e0a201a472dc86b937a162a929dd7eda3559fe7a5fd6fc68f9d93bde63e8a58a20afff4ccec671a973a6ad1c47b0e622ec765e372dfbcc604065771689ab23454d421f6d549f13a8716ad507a9862725607bbcabe1b68fd6653a7b49ecdbde0af26a82b884f4a84955103b4b0b90dba305af3d17426edc8839af00afa6423409199b06e25af9e32ed804f86c075c3de930296436a35c733951f702d15cb7d77834c077015b20905456ef11ace5ce1a659b6a3dff2f037beaee59011618296cd80e5c883d5a67652b9d211744d7589d16d03b429793eee1a454e60eb6150daf6fb3c007edf6e19be99781299cc4ebf140088a52c23590055ea6db36689352ea191671ab5624e50d91e64ca4010d98d52576b01d3e5e5e8b5985e57bda611e3b5bd4f876937261f8fe54e19ae39db1dd71f934ebab1166eaee85f968560d523b01542efb7c61eddf67961fa4208823119c7e695b04ff516ba3d9904cd875cc7167939dca9dd05c7f30eb21286d16ecaa4021ac4434b89499904f664a0c5a2fe0b26111b1e3b2faa5d953e130a30e81d910b396b1cda137e23ce26c109006ed911d6eb75b6dede22d890d9a109db2e4569ca707b8b3847de49fb8f164e0a573d177abd35764e2c2bcd0cb6235a1b67effbc73bb768322bfa35a05302bfe43077c35bb71e513757bfb8e15423a4df7841469b7d6cd16ed3185b2f1b5441af5be6fdf92609e5537bb009979b6dda2e5e42978a36ac4718b8e17d2a58a2d8c8d66eb252424e6cec85f4c8e65ed4e202d3ecfe27e409f80fd445b1ddbac552db16ece953151b5130e980cce1ebe5cab6b70bd7f349fedfbe38a7467e2ce0a2612798e89bc437efed55b2370ed4a03054c3a76f14f2cdc4faa3c7fa36fe906"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a4a2c05aae5fd1daa2f033c0a171aa16cf7b2b39d9895dc56a575a3a2ea8f714","proof":"00fee546c23a53ff154a14d1a8365c412aca995d9a6eb120201154e64d15a86abe895e08accac92bc7989883b2795f97ee0550d236c333b020977e98b364692a4af44ff5d564b694c801290042628e401a5b7942a8ba3e538161350ebaf7c20ef22030a53ffaf45c43616d8479aac12ec54fa93aead11246f5d874a2861e7b6d64cd011380f335d8471ecad0a7613487e80d4ebf431b6c768419b9c72e0ef10a6b9e48ccebc1858a685dd4f14937ed386d4bbe229b3f5b2a98af5c006559790ed732724fae79411599b35a264629d1a2e02f522d578907c21ddaecf63a7c4103ae491a1831441a06dc9c485fa844262966007414331a01556cc9ea69032bf42854dd8e29c2e146627641fd07d63995f5636036353c3a4621dfb73c1806a06626b0fef0d9c3788d5f40fce463a3e3c58936a6f3afdb040d3fd85f0938e483a173e247f76dc2ffcca1910fa1580369adc3312cb7c9a28e41267293fff9be7f12248e830b9967ad6dd64ba5de0b266dd547d940cfb6ddb8901a919ed207530d29648aa7ebc6853bf8377fbe5987ed742bf9864a67a781cb52cdc9542e79f773704c52d3b172e1289c434fb15667034ff58f501e8575763e7f353f5cbbd74fb1cf22b4b66dee1236e89c27547da4083418ad4434519de5a436cb1f62a9862b02952a0a1ea893c967472eaa47434191717f0fca982efca789fd4a8c8233fcb84ffe1264c543b3956dd38de4467c933744657e41cb73ef228d293f363b52fc03343b750a41bffc911076ec7478e65317ef204b82764e7b8ac8b7d6d10f0a508af24464a6828c263cf82fe65e68695beacb5326d116bb9c4205dadcf8abe40b87a5e32545d62c11da267b623536e859533ebeec2cfac4830cf2ce08d214817556182305b7a9ba3969fb9f9134ac097327aaab036730621609ae85a760533101ed9aae0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"32665c2a6fd4eb297828ecc9a7d1558214e81aba6549edc428c9fd10d427ed6f","proof":"286099cc5c6c756911db1477de62a3a804b1784607c7fdc550ad2afb6e32e72eea442db26cfc56cf571406382cbb5640970bfa6a87e79acf50b35690b132ba10e6563fa53e3a0949699fa1c2dbdf67db37b0448bf93273e7676df9e4e2b09227dec0bc4c29626604c618e455341a5700367fa32f3f6d3b8fab5e9d5054622b40c13c6fb3a4012defd28df24b181c7d23f09134c1233543850d9a808ffe51f00e0f8bf3ea290825e4db3580258cf649dedfa85330b68265f5b5f8990258d8e404d109f1701aa06ebbf67f1d041a09ad8c5973ff5fde12b368447fc08e462c7d079601f97379f88f24af8964aa0eafe11b6815f71fbe11ff0f998c2d53ec8c931a00ee9d98f308a64a77fbeb08b53ed6169c95a92379bb5d16b352deb8171e3e3f1c9792f6dfbf4654e59714517f6fecd0b4a5e8c2c1626157e819c4f53e7a3067b04b3a13b86f188afb37b20e7626013776fb4b469e5f0c3220dd13f9dc3e5045de0ea9f1b4e9b85e3850e8dc617d62064bde0a68d8f25ffb28972562e1b35c785411a8c3622b337fb17740df1b6adc1c5c72cafcccbc768299d25e68e246885c8c9c06958e174b33405a74df9d50836a2f34b3a6946eabb124e4204eab56372bb08695c5bf61a8bb11aa69dc217a21d4d4587ef53c42b027b23af82af4531702369570330182d68bdc06c61d3fa84c9255a985ce9d30f16570458dc0050efe63b6587a5a7ce7c75a3a3698aaf2a0cc8caaf7b9d6a612a27a5f281c35344271611469a495a2483c84a14c4e31e7dce9b9fba72b80f51837ab74430d1728dbc95e6a2432005aa20c33f2e39b2d33017ed07f0b65959ebe6ef93228c3ea9cf92153d2d4b78c6d8c567b663c58317c1280f89ac3320b65f16c337130546eee9a630f30ab7a5435187ee889538b1891a1c503f888fc5e2839735da7e0129956fb9703"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"26bf13fc03f5660b1cc70ef5abf09a25d146a58916a6ffd1ffc777d61f50f612","proof":"de69a69169d71f2ce93276ad3c4c97c4f47c5a02b4b8ddc804ef3b8e7e504c5bb4e69815b8516a3a6cee401f6314ba6999130842ddbcaf54ca2f3db5f3d7dc31127b1d3cb56c6b8f26089c6b3aa056e25213a59d7b0141124a02156f43a8d75ebc4724e77f668be21cf440bbaf6fa7073e5d95847fa8ee1737a384c2d13ab230b21cbaac374ec465d962c1261b4eb0216f103fc6dac5860c16545612ccbc6e09226b8f7a5084ba57e741009518052fedeead8ab4651e19fdc5aa7243a67b2b03496d4d5a7d281bd43f8992b27b5dc291a860ec42f7a8ca7e4da6ed51da04750f4276884b6c449c1fced34874394507d61551f358ec75dd659a80172b9656ca02b46796284a769b7d1a86b84c2c430d7b24e7f7a6b29986ca9a5d7a04a10b4c7b2cc263ccc236f7f897f5ed5a48000193748eaf6f2e610725a9a5fcc4dcdbc56f46a0bca791dd17fd79ba93441123b38c46ba56cb2b4f92f13fd360e5f9edf37e28bc2b5481758705bf9827a48abeaed0f96f35950cd3ccf88932a011937857180a8efe83f20f8503da4a9f54da4ad245c2bbea559e352620374b71edd90dbf6c4ed39ed1f38fdb387e289627491db73669aa87a2d1c115a3077f089c9c5af5730e9ba8526c4b68e3f171fa9b63229ad121ea5a6d61ba96391d8b645c0f7e5063a6fa468d0e6ba4c1bcad55696787f15499f5a65786d46ece249926fd3e37976a8006273aa8056c3dcab32e84c83e33fd3ecc0805e3aedcf937a8190160caa47a7ca320a002dbbdb6259f1707e6cab8cebeb1055adbd7a2edf4a7ceba09d70d02fa721e10e36af60fc79e7ac4de4a94087a140aecf825e2f2c1fb1c9564abb772f4d4ede10fc7b55788b6fe614c38376be819259256c742005d6f8c273c6ff909ffc6d41088c0e640a6d47d4f214927a3d5bcbbe42f383785db63a7af8ee7180f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4a3732de98776dbe77128eb1b6fe127492109d0caffc5e232702fafac51af276","proof":"2e5d61aff3a10d0b39f938d1323578d24dc86f99cb2dc509683d0c7eb39f1e39e60396aedc715a25eed96bf81b59cd0537a5423cfbf7c7f052b8e6f4fc24807eece013452afd2c9940cdaa9a32e8191b1f750537fe53cc1baf3b781bd07a7b1e104a4c14b30016f9f6987fda8f14f96d5991a42259f7bcd5e02b3fc91fa1815f38f985c45d9bea14a62148a8684452e9befeff9b53fd32d26f8498beb3f8b802cebf7a2550d38d1131ca51d50b6c3f9fe849d6c58743ad4cd56e70af90160c046da5cfa8886784dedebd0d2a266f0672bf8e98c8fe2edc0f08f14aee263993072eed8f78e743882369cd766f4a5cc472937a8cc12b8e48dbe5043496e2c397651a71c5027b22057f50ffc8cd9b65c35d8820e4adddc50333719ac3a56d26e75998a0ad1ddf25ef0eff148b5a767e2dcb5421a5c883f747100eb03b3a97c85f5f5219bc4c94252f884296081c208dcddb4c94234a3913691c1ca0fbe52f2820084446ccdf5b6507051742ef2a6befec1654109033143123a85cb0eba0373e8b0a646443dd9fdbce4767c12476577b47b48abfa9ab1ee70f5d607a3b2a0f910644889e4abbf994e580a041da0dab84e4d4e3ada21ef3399701a68e5de189e3b0660225c26adff0d5d4a1bf067ffb0dd0db8bf939e6466ddf871421f577643b262ed6ee952550cecca4db82fa269b3ed7cf35d7b93778225a1b9472ba119c1a1d037e2d1acfabbf5c20df0d77fc6cff7499bfb3997ef705a257d6798ea8205d2b2c62a06c7a91e289b06ffd62a06b44a3161f332a77834cda1e38ce85a6007c9943bce101b4ca0fb462a10509f516dd87fb55f3f15f68a117c66f0573cf0a91a5712a145594b26c0a6904ea7785cb30b6c5307d8f3ddde6262850a453dace0abe09194477f70597521ed22cfa6afd255042bc97efbac875244d78f657dc66dd0300"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c8de7f2ea852d375a9f064669169bacb3ec6223f66c6b383e72c217682ccf173","proof":"ae8e5feff351f06a641db9dc08ddf1f311f03a0f4d6a3ea6f37da15ed140b87f3a889b6954b78e1f2e1dc7a7336d1e586658754c2b2858abd9d3e53fb4090d4ed47161cd497a30e8560cdb1e5a4bfdc66bdfc448f108bd87e0d61b1992d4515e0edf588b30931a2f2e264f442057d8f3e4d02fcce9931bd3ea8b1b04f86bbd1175d901bd5474e472c04566fa1f395da83e4d764dcad558b4a0041fba60efa805a3920f239325dcc66aff7e52e406d0d75397fd9a0a177c2db6677ebfe850ef00d4c37dff17b075c5c7e6f842ae07b35c181c6b9bdf5875b9f4d35ff0bbbdd30f621c69be8b5a05bc3016730468d4ad5eaee343552dd7cdcd1020bbe43aaee830ce381d9a7f010e8b689a68a4b725862cecdde434ba72d433bc7e993358f5003c1cc6a398c453dd032181d1d4bf5bdd30a9d8f233d5037b28b2acac80d85069308e9bd66cec803691f46dd7dc499cb71128b973308d1288e32a196336633beb5dec9a489b6c6b0d7c9c26509b81eddca03ed3c5865d374aa0a378d25d47b97f5bc22367bdd9dfae0fd09d41a55a9bf5b7fadafe1d87274d08e95c19b00601151d7e21939ce03f6af9fb4758fd10a9651be19916f5ed74c473bd939151d01b79202c1c0e58dcfa967618bfeb3d7d4338d0b2d3c26ca624dc1fcf245b802c71d4367a78f128d641ed9217023b83ff39aae6b82ba26c6ac44e8842e993646150ab09127161e6e99f4703daa87c67c44c762503e399ff0bad351ebd1c140186619f46b896600f87c228ed4d1df64ff83a1e71b16dfbb96b6e3540a9787a4eeb4ac23e507f88d1e62579b9df12cab35d68ce00a3f40c4884b5aa9b5b4adf70d633ad44b07363cb49f66495cea383d9b70c05e3bd734d7782321f274f8be9c288a530015fec301619c0d875ee5dfa50f2dabe267234d9c630c90f816811ad6b4af84801"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"247d9423832b7500765b80465399d6cd8f6683d02404716ecfcad5f04689cd5b","proof":"2813e676e4191711f8a9b7dd89fdc4d2a94736500bac8be968e6b7b8664f3c293a95e2e563345bba17aeee2564038bf5de09e69a70ffd3e67ca4cdb17254a91baad47f64757315072d8fd88e6eb94ffd62f53eed33030482d33f6a56cd875d3ca2a9281140093aff5a58e61b6c4c9453222f3f821f73cc163b20f3d1ef1897580c252d7cd86540165316bfa9be9a244e85ce18e4972a4a987724b1c98ade560263f059d3c6c53ae58a37e685d08d8a9ee8ea27a08b68310ba9bd0981b6ce190e481bbb05fc06a6a96ba79d71924c661bc27159bb6728ecabab7c26411a57790c4caa01bb9db549ece13bcad3853aea92fcce421cceba8f7fb14107a530a1b24d064dddb40908abc29d3dcb9676ee977ecb4257da9e6174e8cc05036d4865bd72c07f6a25721dd2e6170b72a83ce6f5d003c59256a33609913ad1f50844cb147602087b15076c4f1a81985d7a33cf72b88742ea7c365848024ab098b4a0fc46577a408a26e8aa702a66399d842072ed60f9cd3728ee443769014020d14d94e405fec604fcca31e8c596d67b5d76cdd332d66b4c2d16b9d2a8733749913d3db519065d14669b03faf20044dbd73972251111df9c0e298c9827cc03dc307619a72598e9bba5a3408e3c2f88465a930131141e9dfa495cd8deda6d65f45d2d68042402815652d62cf75259b799e339f941cf7d08cf039e20e395499df3be6e5dce42f2c40db8f65cbb4c2ae46d17dc30caf1924cbbd21e6b3d62370954e5ae589851144ceb674ce4f7b2eacf515111d63d3c037bc2df8dcc5b31ae4c6f9b4c741b5472c8bb1867a9080fdf73251b35871705a7502c87790232eaae8147ede26f666b24592d769d62c92c69914f31959f75dd1d57419eaed7ae070cfbc85876cfc303122c3b2bad3c49908c964d240dcd7e656913e3b7f49a808af03b9e5b49716d0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0634b0e9a5ee2c358bc3ddb945e42e38d7935eefc2051f7b7d671b18d0d2d019","proof":"5c7e2ce04a30315398af307720970af604b1ada8e1a69719301e0bf059e9e16f9611e373bd28b4a3bb6dc88913d75534e5a0811c7fc292494d9bbbd1a714716734d9fc441fd393c1dc62e92a3b927bdb10abce2fa98a4c37663127aef37bd22914f219a708279824177cf8136fc61e27d87f9412fced77684810aa5391704b7693b85a062b135cdf63375e6711666cbf381f9df91c43c6746f005311a0ff38021e9c1f8776ae0dbd3f7ad85d1d38fab5d270d123fdea2a0b858b57bb5e297a00d7085c3a8f84c48e0addde205f8c0bdb3e357bf316db72430279d78807fe3b0aac594a0ffe1c363090a5aecfa77b27a27c1ca9fe47c8a528ec4e9ef4eafb283830e81a128763b22353c9134dd69482cc5c525a879a0bdbf93c7ae970bd576e606851a140e290e49d0960e6ce4aaad6ef91e99bfa14fa8a7aea846dc8ce73f003bebea7b0553e8067deef7f7b395cfd1bf3db01630603e087c26a8f6571b50e5bba97726ce11e2277fa94ce69ea1e528e5f0f855553d08ba82506f6b45c805f642eed62de41243c6fa4a5230441bad96bdbf350e04935b2259b195199ce5f765e72029163acec48da3bb5623fe598b604f493bbf90a4469a7505cb8127a243e5d9ef331fc6d2a2a2c26db6e4edb856f5a09f9353b1ae265908936e7286fe49c1c2a92cf60b07270dcfcb48729ac95d79ef7c16f0cce4f9e3c4b42ca38764f821494574621782bd8517577072b9e0122acefbc8d1c48ff7684b3fbe39a9595fe1ddc362c681e77551e388c8464ad0380d88b8a70de5e442303b6c1462acad52121e85bef38be5845d034c0cd5895d2c48e9fdeac63ee7d7580e697cf3b42b3a3517d0ba58f47d88ae498ce86ef1a46e11f9a8ba01c084add65afe790ad751cbb0a0fe0562f9ac00a9a001f7ddf222e19131656573f5b4ca501e57db075b461d801"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aab4bc959a93c5fa4a29404a294ffa02a67442c11a0ab915ffda93f084aa604a","proof":"3ed9f21ac4cdf2c4af22a383a5c96935a8402163884cd852b2cbbf9bfbfd370f82493295177f568b1bbd3fa9c49127584716f432b24fc0ad92915c333c8fec54ca7663eee1d9bc0693119eb63534a3f51c242917ec93da309217d0e0e7b8012a4e351805e8d72357a91e547651b76aad42c29d7c3f464c7dd1904b2652144217e17d820679a5e148005bd5098a362cc3b0282ecefebf004285684d8b0bda150250781c95b2676f4d7b81ed5775cadfd295fafee38a9c60c2fadb4fce68e3170a60366e2387cd1eec80ea7a345f80218f5856c28a3caaaca894ed4d496180d10074dd937650e68d907db43d490e0e87c94b6922d2ea9537a016521fa7a118b32eb440cfe8d878889f8237474ff2d20668eb1163946f08835b61a9875197d867146c6a9c7fd12c77046c20e7488044f597a69a92b22d3a3ddd8c6f7d919852fd7a562078a94f5ad3ad0269cd9fab0d904d1086ef16bce7e1ba0b66d73230f67751e0c6375bbae4ce2f5bef7ac915043635830a244eefcac11d139693b2a4fb57593c714748cf8d7d499496a4afb15ab5f30860f3b8b1cb7607adb6183f4718f66438eb75ed833c7041ccfc7375c64bc96340bf5ed462d69cfe88b715b484a5b8120a01cb70ea0d4661a139788395cb42fa51aabc23f37f8390f82e7d9a0e00e6529aad10086c4dd12fce7559f9ae2e45a81b6fc8fcedcadde7b6d6965c4f7da94822e6cf7756652a690285f065795625e02c6e37d55ce1127e4b52cfb1d68cc44a8e79d6bfcc894c9efd8e923155a23654b59e836d9d79420a8c6d6f0af5f2933d4ed90a18522266e44a39358d431c6bd261d5d3e7ba651d417f5f46bca6d3844d51f08147541ce9e9bbf57377306fe1554e68e8e6ed2912a18f64cfe3c501ed0669a3f4b1f6409281183eb5ad0b50c9b10c22a205250dfec95e116bace736930a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7072c1a041d790a314cb30496d8cbcc3da0d3c1cf216de1c7e563ddc70311c5d","proof":"0a18d9f4835e89fbf2e4f64906342e54ad57ebac6265faa084c89d9e5698e46da244aef763804eb583e0e3ebe7c113da67f322628252f65afdb4e4d086434338ece9c265fc3a2937a3f3b81ae085595cca230ef648b1a8119d0b62296b0fe668925024cb984d28c66d657a569e7615e97322a18613cabfc65c8b338cd287441b9edeca75b34a796bcc132a29f311fe57682cfdb74b495292e01e7de4f0d1630f9e40eb0e8dd1473c77d777be415564c015f4c0e22302a1aec74a716edfd6bf07cb2f039fe682c3a622f8010a3cb656acbe695f38947a4a4add722861a45cc7075a845b6c8416b19b26b3491f710d80ef45c98094adf88c4e7b42e8f2d363f651b40faf81dba61778d7ed8e10d3c3e087656c2fdab01b56a6a19531bee60f50517413de7a9e124416a7d2f0a82c8dab57aacf488491fe4ddacdca4821fd4d706b76cd3e3cfc4b818207edb20c7c2dfb87e2985137aaff6f3804e12db5b254552b262261b22f05cc8730477397c12b386fdd84d3ad1538242c3db389b60f00a2015e86d0783f70597da7e7913c83b7c9caf0882024c588835742e06f6675afd23ed8eafa2c2525bd1aeae73d577046e0c36667f620359eff2d85d9cfbbf214493ad01b5a1e730cdea6e3ec54c4dde1bf30c13041a92677d6eb1445522c80393f27b29d0f2ed944909ae9bbbc9a67424cd1788d0a0f97f0c6c535f828ad80333c40b0bb2f0a950205154179ec66d4211fff42700866e1fd3ffe8a28fafb36d13e0e82a8c34c5640cda06de5110e5c8d1dde9702d05824f077a8d4ae772dc0af90671eaa7b0d81709368d503d1d0d8966018ef4fd2e6a09d229edef3d7a41ae1514dc111cc3069d4fc719d277433f0b014b54f9c77bcc2fa739580a434038bae99073af33397f121dbef7e99ca1bf5af97a4341ae51ca5e06d936fcce60acc7db203"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4c5bfb02670901b3520dbb42236531973999f53c1026126ac3fb772e7e788b3a","proof":"9eb32d9a4eeb9113a047aceccb4c39e4ed93ef906fe7f16696a72a24ab60b542e69bf26846e6961170bdfa0342956aad4e2f766fe533ccfbcab4acc9650cb55bbeb28c74dda21f750493343f1f60333ca976e1c636b0c0b9754e46d886b41734c08c51659cb31bb698b8bf12c7d96c133876b4eb431396610b22d4474caf08630d804985a6da16ef69b143479a04e69c213c098f91185e348f81d80df73a9d0fd16c396c3ef8e70880a9d5b1d0582fcfad13552376dd47a39c583eb2e6749e02841a488edc6a091d8d2d7761f2da9b31d751c790e617155fed2c157d2ae475045212c69fa4e1d8964acce1131a1ccc5e0ac20b5dcfc1c579d55624ee75bf464a889179362360f4c500382f1d2f819645a1be39fd44a71e7a0b576ea36a5027733a92e1e1cc29451f2a01063241de8f0a51ea81b071f9ff7ea0b50c67868d4b0f8edd1c2756972ed209a6da91fa1846530e73f870c3c5280b0c5b36bab37cb451584628b3b25c428f7325b7b83c0ce6c499066bb5d48402a51a4070f75fbd0a1708d17475cfed17b8c09c3bfac5f41e7f19c015224b498e16809244d60d761f00703705e9c009afd7b91c4a3764eb23d2d7ca674c4ac4a6dcd63383d791c6840e8c930d4d2f993655512ebaf7421458661d0b9ce300f4b63a08d94c6753a46c2b50e6b1b963651c3b682bc77182798311338056d70e0adf8612b9314dffd1991a5ecffcaaebd3c9c2feb6d5dc4f2e8b381f4967307c6c0d2db3466fac8226826e52e870c6dfd39805c963e7e55fc09ea7ed8457079784d328e7dea438741f353910381368fdf04cc95ec0faf3bbd0f0f455c3503c121c9ed6c677fe1aaea2bd6981ba46017403e38e10f436b28b2abc5aa9be2e0ce61fbf2dbd0df3ebd364c504ee2969d1ffaea04d5d5463f46078566fac7ec306684242d1f606fe694314c107"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"50175773af1e1ca0ac2a70418813e3ef2908608250295e20cba6e449e368774b","proof":"b218fc85ad423ebbb567738e118348d408416fab3649bc08f96b27eba8bddd3dac21c3d140990324ccb8954ef7eb0a16b20d9988a229cfed824ae75f0f8f496cfaee44034b9f2f0d58bc6f73a144f6fbf02babdff8e98abe5b9117cc233cf31ea65e52c8737d0fbbe18b1a2888939a9be0a2760b9bb17b2997901fbdec4659264bdb140bfda40e2f25f62562bc5a3deead679d0249379f655f33939ce79cd0078b176fd5ba6cdf6645a01dc0199bcb5d7bf30d2f84abe36451bdbbaf6e14c0047a14ae1cb4e7a8cb28dfc7524072e0d1b8c996b057ff2a263fae1bb97f63c305c89abb012fc0ba8424a1e9ea9c3ceca6addd55acc8603d80b60a1547e0d29400cc2bd59daabdfcb950dfcb096dcb513cd07797433e2687ce6d1088378af27f3f3026dc1b117cca84cdc0902a9f0b06de26a2a48868ed8855556355a0b351fc6f9cb4e2872afaa9a779017f580c9f1ce88833c2abdac718ec86062b99475da1519c7ce0feedbd078b4631b71e7108bd00896ac63cf48f67cc9e993040066fa930507de0d20a7a734dce21e00e97e17b3d557f8c9103b445ca5e4ef352be2be833ea8c9dc535d6070d6d747cc76d40cdb9191ac2f7d75f6bb93777f86051f43e6c3e9821a5ed35e99c5f6145d09a5e03bd2c85f0538f044fbe492113b45705e1373e93e95946d30a47b1509f1249761b86b445387acb40e5323d1595cd3f5bda239c276bbfcbda31c33ce1506f44b66eb33bff48cf2e8d1953e020b32a32223f6cc8d496b6a8859ea541140377446268c62ec195703b9f79c16c499f8e7564882066852a6a7abb5c854a85b6c616f3db784672e7d44589d86a30333371819e9f11e6c94105cc633ae57c4c2b90deedb095960db6729f7427089cbe7b3fd7a4b40e2a9d804e6d54558a653fda6d9e941b547af681e1c73eb01438620e26a5850c00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6e80a68d4fecebc1178d57392f3c82b03f3cc8cd16f7576a86cfa7a42fb86536","proof":"746f0ca33d9f745a84623b2c206092b61101baa20ae953df81901c52ada9cf678668381519ea977f5d88f83fbf2bb5673072be78794e5302bebfb00f358c031e2613c14f26aa6af0e0eb8a391dd231e99409636c0c426a36622462ef379361601e7e8747b1412791dc5965cf33bf879ff77511968b20eeebe82341adcc44e54a0c122684b323719fa6b067fc3710bcab74bd8a7dc7ca5e7681b718ddc8ec5d06b326fd26fa5d11f524db046cfa1f77a1d440e61df3266b08d958ed27dd338801365d3ba67950b22ca2ab4fc6c6e1180673dae6a53655c2290e9427850293a5063c795683c114a761f548416e0e620e64367803519a4905b9cd5235db68fed72e9cdf03a3bb106201eabf9db2a1fb255c18b460653056d05309bcc4dea347d762ec006d4749d0b6eea1298d08b2cadad94a4739ec9d73d40fcd794e38b1a4f823fe46c166550a48ae0d7327514de6a029b53c63fe6aa02c9d418154695b686b3b285a9ae3c0f54ed904b8c50c0498f155aa9cee2ef0cca8fb5c9232e93f31352dce6fa64bbe98c97f32d98de1364dd01a58406d1c6b7bea43f4b9fd6d1c7317251695772f9e44802bbe5110545215bc8e665573757a1b704660665390cac9dc6ece06f653a71c9ae79fed6051290dac79aec54a02288e85efc7bbbf4136a86d272ee6f80df76b8e3ae23aa051009750fec3a133b621e0dacc7a7e454a825fe25d58da5464ffcbc63e0f2b901105854fd3b71f63affebc9c159987a13816ec78744cb80fd000fdc8609136cf946340094daeb6c0b33560fd7d549db45a53e0ba7bd604fb0c3a22a3fa6572d87fc969a867e0ddc632af4c09f04f953b88421c9e131b0287c52e0e81d08c4f0640bc771ab26e37ef6861cb1fc6c37d98210e01e1055d1fe0e343fe34c2ad33e87a897b742e2719cd7fe69df93174468a167928b90f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"78cfbdf572a1a67878715fd6eeaa7b571c4177b083a253b202dfa6c6fbc7e220","proof":"cc9b3d980c03cf735edc4d41319672b4aaa228609c5861eae9e5699712abec5094f6e0a3ca6dca18af86117930f40ede34645a2c527baa8d227d9ff67bb5db0fa0ac3b0070fed289a24aacf678b22d7479834dc224a18e92d74d76ff33c69a7e2abf31b2d801b926fcb01af47e3e28c0197f2361111e308d24d1494261fae934ecb65d2e2830513f5f8ef428099e63e50848d0d60e4b425c2b7a2e7199a356001d651a9907c5bebc6ef4eeaf97406bec0c5c88c03be48ac19d36d88c9eb54d0af1b525b101dbb08804b089363a20d4a898332b25255a4a36fbcb4fa3d45f2f04a8acc267bbe1bdf2499217efc0a5b5b60262d42fd7d9f55d1934617e5fc2653ea4c672ae18576b01a339f7547592507e11c2e478488eb8c350845fc9e450a344b8865e0c469556d338b2089be883b8ae274ec19886ebbae49f6afddda9d6a7134e19e9dfa930f507f45f7e4956faf6deb8d0a93d497eac3da98256f5defd5f6f2661e2c50449f0388508a64fdb1641dcd62239cb5ce32a240a4ce6c777ab3134d6c7b0c7cc1e90221a09dd370af00992153510fc6d04159091f9e3a8c8749b5e64ad79d55366d38a4e086b599fa872c11bee3473ba6e22e382a271f1454947535ae43038d8f268c3f950dfab231429214a3431c519e7562a9ca6a25409c9651026306e9a492ca7cb0c0b09fd3302ff9f9d05c2cedca90fd6bb030bf0c168a163ee6ed47885771196bd81f0880548c639044bfa41637ad2523053f2cb6ed66f5cd04c2f8534ed0c92484dd1db857f6bc82e36ef88f2ff5225b75e9ad985d50a18eeea1d512c0919a6ffbeeb34f06661d48233831464e6712198a927290d91cd22562a9e2002ed1a6c2e2a2635c3b07e1045862daa3043db7b14362b663b656d0c070dc546dcf8a05f25805343a66a675492c7bacf70de97c5632b7305459ac908"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"74d7f5cb8401e3c4bbb735c1c921eb9febee1f15388f7420cc064db34848d733","proof":"1430510aa3efff11794a56db163d3b5474c92eedfa8fe28ec1f3256bb682890b84c8e974070b3953f33cc8f147741e4dc90a7018f3dc90b6c6ce71e3b2074a1ba0675ea3f11559712ffc60c9cdd01f7df70b3997a0825a644a3c45144b923470f28f38a78f9dd87752faf4eb576bf762a4bc6c4ec83b5a1fb489dc26a7e6ee2a52773c5efa3aa5ff5a6478b1aa31fbaa5eb0e50295c5821b94a4bbbd2e2e5d077a0f2c2c29b5f6f0b972ed83a1f861447bacb0dee3cafcfeedb66897ea94dc013f5c7163478b05aea21a2f16341aa8b069d88bd9ea617d8eafe26c1e6f57200abcc65d892fd0d3ca83e7735e1007b8aff63067649cf135e7113d692734e710090e46fdf685edff8a3602606b62564f8185008f51c0f4983ede44a0c25fd1a611523cb2588369b7b6c3e602600be87d06b709889c0b239afaf112f91e37d89e73aecf3f7d1f29c671470ce760b333c7ee8b966f2cb6f7996c5734629331a7224f9a4b9988f3db54efc71a2b0f2a49154fbbd79acb3ed0f50770dc464750c7e328de97edece6d209166413d88ca8d3ee7be4e101bb89ffde80c6baf4c85b514e046e9432250d91716c22fe7ef96ffffc6e569fbdde6e5bc70b687943254d8d04207e284e55d34e96c6a4edd5cc6afc6b951f58f3dac8c82fac9730a9bf2ed3c1029052cc73ca77afb523f8485e0cbfa0174c366911bb833cfe4091a16857fa7117ba86038be6ae35b234066aec929157495c2b3b479446ba9ed1e7327a2b4e3d1c10f1942464cd5b4f69c91ff04124e8978997502d7c50c1473ada7aaafb17052850bf2bbc86f09f7f4f4d09c6b14413d37b4199cbf7f95b285165f283df37c06d2aabf69669196582e7a9230cc6ad6080c5b9babe659b3b7ad8505534a0e3440d5b8e4fe2c3ab2e629d25649192affcc54cb5ebcbe0bfdf75e56c9e6337bc840c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"14b38498cbf3026eb7494e36d2c1169afda8a7761c390760f5cdd88c2b371b3c","proof":"7e7de7b49cfe1285434d8f1f190ea8cc9aecfdfdcfc433b83b961d75223ea4431a80ce3b84d01b12bcd43ff6c28aeae9357da05e5a8e91685649f8fafaaea32f947be30ef0c86b846c24415a6087e5539495df8c9a96b3338a16d13d1f795a326c334e934fb7e22b44a7629565a9c14264e3c0f75a4833444d60a38e7bfc217099cf3530eddc0ae78619d8a175947fe4c7c0a2b45a9a33310dc2ae1997357e0b3192d0df9280ed506571f484fde4d420ccda89f3cc46d86f59a2de6486e7cf00b2f5d1e7c3d0f620b7c0c85e2782168a10c7c8a44dee6e63796f935d693242070a3bf302aacbfc06975f9a188d3d976b9265a0801d2b543a13b28b1b0035f762d2475537dd567e8c5a320162dd199f5bffdf8e65d1e7200e33da7a13ba4c066a86eb5a0d762b4b351efb40cfbe5f83dfa7ef80f6571ae9c72f9890f669c489550a395c63c6f2ad03a9ecff75b0a3ef5a7dceba9971449325677c9c9a9b6efa75e048fac23775a4423ccad8c9b93dc0f585f85dbb49ea8e38f3c52149156ddf2cf6b51075a436fa74276df04aa1f93f8f10bc1e1de96c4454fc07c7ef210ce471464a7f51d31a56247c27b6974c0cbcd6a33201d87431166af032aaf35ea3945cf612732119384970de7c8e0bcb3bf8aec06374851de6bc8effef21a80393ae431ab56a3c701af6047a721b8a585895a97059759d0bf5e642d31c97fd3d50d751fca1ce960ed5df410f96a59a2e054a43680ffa728e00adc1b95666b54fd9252360e5f0cc0f3d958d3c9adccf3d1ea1c666db143e99bc30be2d6aaa2b66f03916be1fdf8d99ea84150bed3fbaf4808edb3b92c39c13fa1bce4d95da1c55f11d50bf8384f141b9d6d722ba7111de7ac11f7b7a8ea05cc1a62a06dbd18c09f98e070704a5c58309b144005c5c6f861a381b4a80d3fd0b19c0d3a1d45a276a7bfe03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2e1d9030b5266856e91288beda5a9741432d74748755a793da0cb05a6c70c223","proof":"fe50e706e684a93281a2ee6ebeda6218e327fe027d3aecba910df189306ba8244ee6ae3b34208a7d0867660474da62568c36422e82ca26f4459278eabd87416046bf1ae231e1d658cb3d3612301b2a089b46f1305666e235fc099ce6b85abf6896ba7868a51dadd0331bdd265be79ec204ebc49c3b435729e84f1f1cd07bb156b72207dd48bec5e42202a944a0c68abe5c2b1f14045aaaa81845a4ba1752590c8af661879013e9365f3174069947b118bb3438e2df0b99f8687313fa67e9b6005d15f956111937bd664b57bcb20ce4fbd98fb46d5a9132c83c1b21703347b90bc0e873d98503bfb7b830eb4338034d728a8c4957527c57a88ef081ddeb30aa47c8a15fc86d3c7907c0cfde1557e088ccce4e483aa444b71cd8c9cdf1be5b0750b0cf90623fb5d0fc88ee98ab1a7add0c1ce6e619d4dd31bc38571a2bc0b60b37d425f58e30e00bd2f7fcd876231205cbd52e52251484644e85b07fd9128fe25748da69c4af4f59a4f33f8a6be2a5fc23cda75837ba8e52b17d4f0611e0758a4c146f1cb4f3eac75d3481b188a2e10d62487d8834525294be6cadd8594f92da402c0d86a6f247892f04c20d8aa66c9995cccc47e84f6656f37ee4df35eec5907b8c5c4de43cd42888015c53ce8ce66e2559bf00145db97e389051fc4cc4632201a039602358c489c559a599222a848e0027e1a36e362160899fccdd328a99f2768691c02e2773d38119e8149af853c8371c0ce3ad7aa1bb5972a7eb80c7b30234f487f76d81f888b2d80462fbd0361358e676148029d992955e420a6079e35b00189a290d4bf3610547e9b4ed36d1376e135ce186d4fcc4a418b2fe066a0f4c3e37d827d5a618613bd8f8629a8f252c00fbcb497b8f01ff78ceb322b696151a0df4244f5faf73bd4f840570341eeb0d85a68fcd76546863acfcc44c7bbee44502"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d080e88070b334c3dc0e282c9b83e2f953df0e59ee4d04a6082cc26e03378c60","proof":"2236cd1a06f8586cbd8e3b50203bd5840a2f620e05d20e815ba91563f79b820c2c47f17da13f570eb230e58a4127b08f6429c544f2ee50c472762e96b9c0b062748273f8152d8713c23f1d7dd21e050c9453c827e1e5ddb8e0858b7afbcf2754bcb040a5fadd9120da9ca3b949cf4cfa5f95e978d60e1c339f36c5a4556528041050a4a564932dfd2843ce2affd07e5c6da05ac53c44b3786c67d7fd781ac3012d1c6a3a58efceb0ff64f8265affdb2fc1db07a440ed9b1a8bdc9bea27a4fe025f20f99a39799268f0f582e6f00038a8d634a39d49af0da086c0ff7d485ca701a809c368cae3cc8ad3f623f419617468a327c740ae600319bb7416c9ce7c804c54782538a8fb2561bd4d1ca1fb9cd335a056940e292b992ed99b4fa00ee73a2b466069a6582da82642c07cf3544751c4317ac411ed7d600c571cb2d44fd3247024d1171d1dd63b5bc148f3e7e01e8e64ef4713c37257a79650623728b138cd0928b9f38700c0b0d004b4acadfe5c9f19499522a51ca8d1da175b6c587eb7d37af0c8af6879ca40d8abab7cd108c50cb75444ef66f0705a3331fcd01150713d3e4ac0bedaa4f77f16b949d540410a4349b41f71455c7888ef1d16f3366996ea696444603cabdef0cbfc2cca56b71f58ef2d88a2c676c118fdf277f62eef80ae78b85237318779353644c414f6ea263b2b6ad72336cd8ea7eadd110f33479b7257cc9ea261cd07ba4fa5c44e7561d2dcea44099d9f3607e2c10879230f599f8801428f7c5420ad6e856c1039e14cdb809f1ba231306a75273d964f1d2b61209f0d54cf2c4fa0da8c96077ef26c444aef2b85d623e14bbcad50aca214220cc32a2af500bf993887e535589414610e0f7878763aeedb2c4fb36535cf533b43e56209b1bc36a1a9ea29233bfb756056b78169e616210129e693db989147a4a7a25302"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"547f07ff4634feb5065e3c1bb55d7de0bba3cb7f26226fde7497331e7a31a305","proof":"da1122df5e31bd62955f48cf5ac003b6279cdff82288659f1122966583c69330723cd374a86677d17a673bca8f9319b115aedde23df5e354938868f7eb8dea2ede52f2179f3a6a883a0f5fabbe0df05dce084f9a22e010391fc44aa133f6f824b621cfbb5c0be1590dcb8947b3e26795a53ea50f1eb566c459f172ab1aa7d130d1d5eef4a841075238d5fd30d23f2b285695a96537fa486026d9a54882cb3d0bb84a6a498be88cc1093fb77918de3f817296139397f28cb21a8a7604c579c8067cfbf74f37184cc8f8b3237d22dac4d7a5188a5fd356150a3017770abbe47f04d89c1a5fbe29dc82397a7f6f790230bbb147c319dbda534914bfc0229699952a3826b08432f3b17741a9f8f1b96188df4cc12e7dc66ad625673a99de9122ae2588bbe56086277548c5cb07fa2948bb1f0bfea24e4ac7b6b87d3fa0bf7e197f528654d38f7e97307072cc209c8d902fa168bbcda5cc7b213aaa1f5b0bb871315b5234c52d8d1c11b5bfdf0a13dec59a4653564374edfa6cb515de9dffde76372998a15e2abfc43ea6c126c2759701539447f37f87f866f21b88e47281826930199e1d60fc887ded7bf312d8c5a31a5cead0a73797d0ff15b47c10446af443c2389894f806f2ac198a8c56c5a3090c14c162ed37f7509b91cdff99f706c6e3105dbcd74f819c0ffee67adb29cae906d8c1fbbe5790290be5180ed8cb469a9a7d15b8c506162b9bcce2bef85a38ac87bee69756a23bf928342f0203d0a1c656967982d3fb0867d25abc01ef655e58084689d258e1448c54b0d7a35e952415c4ed6fe0d1e02b923e04b2b2421b28c3c3c990361f8faedd099d852cd2a9359868db1e5c8fc99e35987a36d329daf6c0d86b2775180f3d175406fd8db1227dcbddef0ee4ee5b35e532997e7d8844679fc844c432678552f4c0cd845893918cad773d06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e47eec9002dde5bc03838e7497e729c359a75bdb8bff3271a55793624cad3967","proof":"ec6354a80ec297dcb11969a4cedda7dd47718030277fd9aea3fce84eee4e9c410e33bc93cf60a90d0cdab88168436f05305893ba2dc035f992de21f2a6072a10a221c94dd4301a00e80eb57909c2bea22b4d3c9b8d380b69c5e98ef331a6130d1eeb4e5040ad598001e4a617e77685fd578dfe3b41690ba455226f47108f9b3a2599ada346c9a38fd54fa3a4585fc6a6de28ef1e45b391717a5d9db260de22079c56b89cc5f52efc0914db245d97203f5eea47fd55ddcd415f65ce5fa722bb0fee4e6d5a1b3462d8f290c2fc06dbc4120cecadf02f53ccb8f008605aa089d4055e7173424aeca71160f1443fb37b0fda492d4c47988ed4123fdcbfafed741654e0bafeee5aa47bdf81a4dce6c8a3c5c6cee8cdb42bc31c034ea9ad8fc943115fd83f45b4caa626a8788a106c1aea4ac4b0779cb7c777992ea33fe17a4638651ef4dea1b2ddbda8da5e85787273df96938670ec6b41b430cf295315e05fd57e27969dea87424738c59c697715efd7a412d692f9e947ce019ced7e56cdd1556b1de4f8169a15304e956f907778153de17f6d1607d50f1cc0c2e940d7d3fb59b030220c1e6d1de02822a445af5191f8fc9b1e262c759663b3ffd2c029eba6ce22276c3d18702d6abb91e86f28dbaec3d2829cfe2859168974f00370ef586645963638811db62092fb4f0a8ab7c25a42a45b277f1a49f64cd11a4618c7580fe6e971448184efbb5ca2678d9ff320232d591f8112db9f0711bb894beda2b50608a32918bda6eccf74138e19a5c1dab19125a71114df33cc6b2a780421bd0c534b07016cc6d4c0c24c02c09569a45a7881c014219ff56eeb1f0c0d97acfff527b0416b0ab8c65a2739ace46ccfec875e12a9665f4c7105318379f75d8965eb3ce47301a768d1dab83395a0ddc842f9ef4813d86a78b7758f6dc38539aa778a0564440b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d432e16636ef9cee4f0d3e2a1cc4092932ac957d5293394da9f8bc9b79833a3f","proof":"4ef8afe381af0d3efc15769e6091e8de97e55b077ca322aa9a98ff12ce904c05f2095bee8bac5d606f57562a605dc5f614ba08b786c0ba38b93e506c2e57881636179a1e9a85c0544045abc89dfd6d644d68c523590f3c80b27eec775ed0db16ec0aa48e7cd02100d627792fd4a381f74425332e30330df642b4bb735e9eca2e0cceab30f298e9a681c2619dbe4604bfd0dd574d0f331d9514b05679cc5e020d6075cd70e90daa8780f0796ef87b8dcd5081bfe79cf63571c66bdeda93bb960f8ef25e476651981dd0c79d01fbd4fefaa141bab9f09a158e849e91f761b9ca074cddabf7a30d40387c6dbbd776125d1e2e2ec0a44aff80327473c1b821e43a5588842e2b89fcef1626b28fd2ccb78be070a1130aa43158d5624d22d9dcdf4156b035c4625c9b0043fda73bba586558331c4d3b533d8f31e6d8f321a90c6a5e68bc7f77a0e18e2c13a8bb43ff4fe3e1b05eec36942f390f9c4b297bf3a367e01bcc0bcae8ca63c581827c7c47352a8f17b73f722ce5474c5de9b0ac79d431776e2e780ca07395442a933dbcad07cfbb1233fcbb4022f75105015ff3fea928be74aaacfaaf943222ef95d89b344ce0e25e554f2894cee0995421981d18640d906ba6e738e95253fde5636a1766d04f27562912ea3c3c748dfdd20f8bde2ab82e46a86b44caa2a9f9f6fad6f88fe7085776d8ee27bf58ccad7bcf8486e9401e21640e24cf13d6c347815dc28686e739076719ee70acea90b8926779d9fed219d0781013f8d15e9895dc9cc616740da38751933b94ea23f1d028cd707683a8bfee3ba2ee263adf557001c0e5fb5352c67bbbf64f8f0488cce4041ec656df952c7c36f5449a64d5b1be5a4799ca78ff4802b1589b235a3f84fa13fb9e768df9fa2e0d0a548584b9c24e5e2fa5cb61e2c32cefbc26d58ebf7dcbd00138d7d9d670600e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d877cc209555e9a50528000a6e60f998c828de39e7ecc04a7e8570ca75da6f6e","proof":"96d712b97366f8f1e16cce3bf010292a33dc1467af1867d92b763ea2d1dac86e985a72bf2e0d3ab6c10ba160310226cbd35ede67405508855fb054717ce8334f48e097cd050ca428e9c422c39bae3f21f31f8d3c29ee6bf1643fd31a1301c1777a33eacac2f21ef03765a472d1f91c278b4f57ca62526efc76b26c30a3a44b680fbcd5f53802c0be4469f2f8ebd7f555ad4a6bcf34ef3e69694eef77341bdd07ab3994e9c27fce22db5e123d9664c3e83ebcf0ebd2befcd66fa117451419f10ed5c1258101531d38482e72aad41e6938cc8f531f19c8e3b384479723a35a5a0f82381bd78b70395551fc2cc9b739a7692ef19234e6fc95ff172e6d0925b3cd54bece9bb7e4a5833d3c41477f6a86a870bdda5378f88aacdb99127fa83bd7f4601426f59b4df7326acd1ee6ac1f2d46511e86806a9c2b80fec8fc5e509f5cc8259aa88782e707890073e6919273ece82c7837d2cc45bab7bc9749463fa0cfdb311ad6f3bdea8fc8cdd0eece00b00942ba1932e94f693ec7001aff328ad59dc53b267e0f1158c34245c949c2801741b7d4111f6de1319b24aa69729dfd7e9c9751d6147e375e640659ee986cc2287e2050b70b3b075ff276ead6f1535e5bd8893a06fae042004acadea319746a9e4338a379aa66b78c788e4d5275d6e909e097178cb88e2d4e37978e5b335790714b00c80a13fdb30ec38af9e343424beb95c37ab8f2a020398072e49fd69939c3c32421ad8025713f7ca31dd6999c8587d07f1ba659b5f0969bc1087ffd2df3971436fa11a14e998e91757c80e1208fc1fa525da0d904b2d47c239f68b1966e29f0b047acc317573fbe21b35d2225fb5b695e5de68a495d05fa730490fae425d28f560a2ba150184758202b21bc232bea022406d9e29d78ce9d017e51d32efeb19114788d0004b8d2237e17f63bf5e89669bb0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5e3ac8e6c32784678b4e127216fce2a73522aee860062fabe44f9953266dba70","proof":"50bc69a3cf44c76029b3e7df241886ed34769816d692ce8c242009942a2f570a247fc2cd18fe1f111ca822646c1fa5113283c6752317b9c421b04036d9959e689a25e66c49a1ddfcc1c3282ae059e0d7c4e52c119f858667294945e51287101d2891b5a8189e33c0d6d4fbfc821085ab447d533c15c7a0a93fb7fabb2ce40508f3c5e96cd522a195ee5df55ee7a63ebad0bed3ecc27b4fb05d1281972ad3410af0c65fc35373b2a99d156007405f046fcf67df5abd9e650bd4f9c0f191724205ed30cd30825eeda0d02cadec83852cc0d7b3a0f86a4905b6b0a9aa53ab1ae50ebe43100d316f7f43975a39381629efbf877001939493c325936aa2b0b64bf43c5ee91ce2ace9a23cfaae35d4746b1d597dc30058d19c32e78c4d7833d98bfa3d2e846ba6f4bbdc5731339042ed755f0e657150df0907ebda045e3c581e506042d8f6edf2fce71c254474b82aaaa1986439fe3569780469aa728b28cbc5d61f77ee83ae5e8a3fb3ff8316b1ddba54f14c3c6bcd1a350b23e139996bfa9207bb249e11de144a42a8a3835fae57d31e5737f6b17aae8dc0ba7d4f57ad979339cb51ba17200e901fc6f9b12ea727bf07d3ed46a5f517257304430792f9ad79bafe377cfbf343e4646db844acd0ecd59abb3c23af9800484837995552d28f26db5a66ee14a361d55bdd9ecb34775e00db5d29c83651fafe7ea2c07b44c8a83c238f39ce2153498ecbc03f6bcb39b05364610b594bcda5bffece81caf3b7b47075401d56fa6cdb997911850bbade9fdf6e6b53e31a495554bb76535c9376785c59082c0ecdab821327d13d3becadbc5ea73cb062cc57844b705642edbb434e991e806a3f4f5b535d7c4f117a1d56b62c55fb412b48d978a26cd4d69370db125eb759006e2729de72cf0fef57dd57551b6f655c1e5dc698445f468dd2b379ddbf4e9f06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"285f8ef48393912f74ce7aee287587c5b6d2544a14d4bc9cb1536bb1507d4d1f","proof":"52d6d31f36178a3c2dcb701906dfc44b2bd79a9b53fea07736b3e99fcb62a76334e3444612d35f9a1d8600d43f2602409d3dbbdc347bebae816eab7f75d81e557c075c486f87af3b7bb434d0237e1732893e1b3dcea46af486cd006144fa723e74f1c502921f7d5b3ea021e325f7c7c77a9fba3f05ae2601be0c391a6e62277f83218a39c914126119f88f24faa846e2b510703d0fc93d97edb48f490314b506b06b227d570066b428d97e146347ef25b4431f97fcd92bc183a088ad48a3040f89f20ecdaec92e54c00b768d4e7f6592428a3cc150c63cd649b58a66b262a70d7a448fbff8ea2e7b6a0ceaa16bc950414520307f70192b39aad5b56b5010d6763e36a2a41d2dce200ec3b1c93a0540b9559686d2c82a6f1c2e650d457f03335ea6f4b4efa2fd37fdbe0286ab44770b171790624ad31802c0812ad6c4cceadb26726545b4b1d39941ef1eec530182da43710392158b2d8105def956d04ebead32fa620407e8417c3e1e60c6484a87d15f85cfade41e443cf4d8877de1ed5e2438c083c36d9249a82cec5fe4698764d2c83800a187aeda9d66baf2f9d75b759d7bb4846b75a88e265be1cbe460c5fca6c6f76796f11301fd855a4b5f445b2ea276f434f9ba5df861857a331e5e05a0f88933b18b0be0b700036c668a31a94c8f72ecf0d4cf6fce03aecc1cc24ee78d42ecb80c417300eda5fac18afa1bbd7b0a0c90f7f19eab60f90093457b139fb5ca4e9f3d2e990fcb41d389173a29fa8179162cbc39b06d19b2503fbc54aa876799c90c1a0a986433bb6554aab103ffb7c7758496ac5334c0db0ad79952c0193a9f83333b70b3be7b4297a44ff1d89d102672e7f73b3633bdb67afd5369a6c3517454a0807be29dda33740f0d5def0b7d3e0d1ac6e0141d381af1a1252f9c95e824516511ffcd7c0dbaa23d4805e34ebef70a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d62e9b2036a097d04f992b140f98629d4b5cb783349c5eb4fa66096a2279096c","proof":"9e1568ec281ffe0871b609ec86f18e75c4f79fc032905416a9caa572e4ffec564c75bb5ee89d1a8b2f14864cce98ae7555488b377ebcd8eb9cfbfcbe49e5087d82605a17c87e2c563325bc0e8da23aeac906efc3dc3c518fd0ae2616c6a18b73088daec26e0c82174710f7761e86e697391fe9981394ae733bcecdcfe1807e18f687775f2dae46d35b7b1ff5b70103c405ddd9082e2318b9dc97344d579c2b0f2a9c2575e25cdb03ae2914b06c75da6deffe2b70acf589ab371b21d80bfa3a014b4a72823bace7d327fe0e6edfd5dee3e4453eca0b3466df57cd715ac1b3130a96542a999a07bdded1737b4c63989d6a4e5198ca558e40e5e13030b4d2d9e10c60fc79864e98c49aab0db5209e8904d1dd9777f6d5f924e7848c498b7f9b0100ceaff860814d71ef9a679698143abe454de23c4a53fa4bba3a7feab6d8c621086073b3dec245bc6dc3cad1866df60ab10a09bbd2e900d23e3c626b87f542cc0a2e9c2750188adb07c2a67d27aaf7b6bf9e235a6199c070ef3171104cc0afc478ca9e511ecd277b1060afa03a8991572ad103ec3529959721a07a3b4a801a9a23169618f00eb78c3312290fc55e859e12b063932e81c5c892a262bd3c74e9753668eb4d9cc1f2b325743d3f451b69b71a5deaaa7abdf914526e1dfc3a2eec5a292874d20559f802eddfb3bc70989eb3376fc867078ca327242d8a6e859269fc0eaedfd0095121df769e009b8e56718e07d8cfdadd92f86e1d478a592c580dc423de09ece072410f6c45d004e118c5e592b03c81aeea599bb7bd986ac08a477b76b48390b2b25f0295f7d80f5e0fda088c2868a0c0c4209f02af7ae748a0369766c723a9652323ffab58b3a98e7d292fef6b50148a72f3c99933a49998eeeb5601d4a0e561b1a86142fdecf64bf798f5d198aaa21e62f65bf180eebb5db201f801"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2ad098bd8be4b872076e7ca8b32d0504c89bde711ab4b1eee1f0384550190d57","proof":"34145e795bdb5e267c00d302b183343642c41a29ff4e4696a781c0f43895544c22b5386a453f70f1317bb0fcc513c6bfa4ce520ec99b49b1b7b32935d1565d71f63c1b5c587f8db199666feb15507f38adb88b4cf302c4cdecdbfc482d76b85c54fa7eb2eca8f83f9a21e0f7edf5a416541f761a1526dc2557f76fe70ebfdf2638231d0024a731356d331264dcaa900a679eea584780f4ead29c11534a9dc80db200056f1d32492d7944b1fae9464731bff2a7bc2250455f1b2a9e647d3874058405b3caa374d850501b7647624dad25ea57f9b7d8464ca6e0b501a2dc51a00fe682e74beb0648326e59c25bdf3276fc680982872bba884bea05b04aa1a8b51e80c7df4604187b85ded7d74fdacf844f35047029959376c0366d47f1fe747c7654c1eabef3494bb126256206ad12f66b91cd25b898877e22a36e05d5a394e339d4b72406c76fbb0d3fb52182a37e9de351977881233ac7ca7d95e97c8d03dd5e368b090d75d2598c1ddbd32300877e5870aa2554a39e16bbef9706026b1db81bcebb7ac658d6818a938d71579a7b9c467c03ce4e544f924fe4e32dd28c14b54a26fdbfd50386fcfb0ce882eb215fe1ecf3d6243ad15a3bb60eefe17d89fdd72bcc3d57ada77ce0e77a71b7312b33519a22158358c3219432f3a77348a0c41834c48d73b2e93ee9c857d2212a369ec7f230652e942d0aedbe8287a379571035124cfd5a727794333f7edb1a2dc63648e77543517fc8ec93e52fd9e666375f8d40688ee56401c8af68200b26e7c489c1c3bc113ab304744ea066b3172d09a83d3c10886f2e8d919c02da3f129f5865ab7187eaa5c3e0bfae88d5f7984756fb851871014dbb184d7a09739afc060cd5da85e61552e632c37fb685e806a33c81410af29f75bd6f427a2215ef2edafe441dafc322a8b1ecb2bbdfaaae8f504df97200"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"220d59afa6aa5092e73f59ecc2ba9af65bece7fa70ffd58e40bbfe1cc7de4423","proof":"588c4dca60ee007f79d642d4414b47a46f0ced9792011116ba113227dcb8d61c88a19f3db0118a27d61c39eeb626ef86fa938f50f794274dc793721adf809116902196b45988a1ea880462983e4aa7a9714c0c67a3ea8055b01e8f78da753b037a22fd8a569719e5028424fcaa031c9519011b8d4b4e0f86d363741b1f54ac79df08ff2427e34a1c3a5a41ec5528d46561c968f5d356c9fb773bd7d62f495f0b5f311eed97c6d0a5c9ef095eef31890fd87866f926bd5078fe79f414c6e3e70724664823cfa5330a94c02fbdfe00af84951e182aec7fb0f994110370546c0d0312e829c538f4e521fa69b300979b834ae7ed71f4f1f168093a71cbb109fb8e4328725dd71845dd1e9ea252c541b71f5b65ccc7274fa06838d49fd0f354d630281c30797cb67726edafc49045196c29792f6bf072edb5541383742d662eb244607ede1ead8fff3d99b77f0455e008f6c476cc6ccca63568957177677de3cf34303632c17adc6eb42f32d915ec47d24561c811508df332c617963b6d8c88f96c2afa4fb9bf5523e829c98210cc689472aa65b8eb13b5d168fbf99bec89c763cd70607694fee00c11d1305ec6a6f8d090c41b5a056112590765292912b8885ace28a2e75c8fd1f353198444c6d67b6fdbe223813d4e6fb5e09d9e9c687a56b88972aac6f8734ec3e37ea9550cf41ea6d66539b77e5fe2bd0297c7366679626c144ea630fb8871ca7d166268aedf318891b05b3c7d5d68f525ce80872fc82e091526aa6faa96d936c9e70f343afa210e850d990aa1cc71a68b0ce4e3e1327e4f1538f67f8e8af816695aae70e1796162b6abd5b0acfab00588672cc0827046cf425668d48bc4f4205075ba81b7fa35cf3bfff5186207f3a2dee97b650536e36ee70da081b01efdce09bccceda4cbc6f09e7250434784110e4cd2ec2d25a95544c200"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2e1ebe0959e7b5cc09d39f0435ff00499d795bc2200f62021aad3f3256cfd04e","proof":"440b43c320cdc820d427d799e8de30e4e5d6aafc783343787258e748f7286a79dc31b8c507287ccd782261e7e2e6d01070e6eb68fe5f4575c3b6e4311742bc6534f22458c03f13133ec7fdaaa89fe68006f02e501983e77c165029b47dcf710f580136a3ee08157ee37fd8b479ce9e8676f86c9dab69f212b6415cf7415c9e39dd7542b894faec7152c7551c659d17041a5b9de4680b3a09efbeec87e62ac60dd2956550836903e259e7c84ac983b3f907158ca2999fc339bd7fcf652c1fa104ccb1fed4b1809ef03e4af32ec9552886d68ddf3fd20b66707535cea72614ab050423e28664c8d82f8ab29f642b2cba7f23a68aa7dff57a9117fb8cef746d656ee6b13acb6d6174c948f73d03a755cc4c9428570d36c2694e9afc570bc59d1f798c0f787c9d8c5adb2570bf433445e43dc7a559d21bd5d2cd586c1e7d3dc66d4650322d7da63823b5b247a4822778123f23fe0f51e3bd6cc70d682bf82b834802eee9b09e2ccb7eb520dd55963c37137595af5e0ad6ae3084795a2ebf0e7c572b34e960154f7d2a107101d07af6b2e1393a379f239979e85f888b31ef33782e295eafa0827ec06bf8b29c584f8001d43ba56db9703894609fc3a44b87a035056f6cb5456088a7ffcf4fb440882d364dc89a0b7b21c3066bd164bf480e3e05a47ea40d49a58900d65562b310f2e9798feb34ada7d347fb8007de6bcfeb39fb905bce86331ba60936eacebc675f4f8316d996bc00c3d9ea3670ca55d799f8abd717acf17aae91883c22d42381c25dcb32efd9a2214e2017cefdb1d56f5ebbf5532d940c0d8656a3ecbd27b230570af128f3db7f2115682ba978370ae390d58d2355cd795f892b3661d2c78d1afd66ad4816235e183a703f481f78a55ad8ac46f903fd7c3b676827314c6288f185ff866529917e9c5501cd14132806c75823f8cb0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"58454dc4cc07882853167c71a97ae621485b18c425dabd968f94fbe63a5db67c","proof":"0eebd0681ee9cda4ef741e0424cbc6f3f670aecbccd6d3ca69c65a1f9737d671fed38ca0792adcb759f9e62e7fe7e8c5bdc940425ab914f834d52a1eaa5f737f264a6a81f27cdf95467ba553d9e7ea7c7daab5e8749c4e9cb413944cc264130bd434bc6925c0ac34e1b0654962dfaf2a7f995d9050f41f1cf770486b7976f139b9eeef03bf9e1607cfe02aef315983c0af696ae123808b08bf9e0dcc35327a0ef475336993645e83e2332a6980ec867ef39564b2d7caeebd6553cb3ae27668048bbf3386cd412bcc09ea06d383f374cc9cc50f40f5802819751a79b7796e510f74b895662f95faaddc36927ec201f9656f3f69a1ca35624c4757c840eab9b0048275964b2d42081e51d39b68864ddd67c39f01ccc071d87c07e491442e155f5eda5c9f9b11a03b6b88b9960c2cec0f1751e12ffc10a1b008af33bb4d72d5b24f369301384ba9ccb2fe833f1494f53f508d9de66c5d63bfa08389fc44ce7937252ed097ff9b00e55a7330d85ea94989a21cce7bfa761ea51c0b702a56d8c403244efd386844a9841bf19c9a5da680ac47a5d2dc47b78c5a982d523c140666ab1d20db3ed6f4f1e5d252a7307bf60eb181db5db0bf0d06d118825a2660e16b987a4a1ac5d2b316319e8f90e7e58e445dfaea382e8361ebc2c10d59301f6a8cfe4bacaf19799ce82151dc25683f0cb5d8682f411810b4b37a775fedc9b378b86640243e43fea7952e1999eff7ce1b78e159f1cdb07fee94b33e1a0bb47bc2df7a0b969953968f98e46bf7949a5d6e9cb0a4c77b71965419a0d6b31b59571252343c3c12a5b443bdf73cd7081878ffb7881b2e04822b2f5ed25947921bdefcc40e65726860d4b1f0d21ade7830fe3f77cf99d7ebd47b99b1859d6f56c57c9fe1c3027e7b6c53da113d36de5f59efc112d5551cd97ab479c1ccc27a2092a22907500d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d08d4446f8a7f068556d786766014bf572bc333d25232f0b76382221fd436c00","proof":"1aafe6de8b7a07fd9745cc3b466aa1da4907d037a786827ac111ff34d7694e4bb4d37ace87bad9d542faadc0c5e64d66fa66cc2416137e50d68620df0c2f30574407427b096dc7595a7a4090a6e6bb13a16e5309dc727c803dd6c667b224aa275c684b36eb28ad1ec7f7d059ae31da1643994c436c94085ace9b8af54644343bd9fbff8339461e93de20ed9bcd90da9bb10f5068685e92ca63c9b7073d3ef30f208ff8334f67ad18541a4fd0786b37f494fdec78b073b4e9bb18c30ca877c706b372d3813d2b7645dfee46b0d464f26dc0dc35413805960ac22ea3172007830832d6b3783d6befd3fad5b435eeac754a6dc412af0b11b314d31359e5763f19130227af2a53599f87858df19c66336ec4d873472a1ce226a358a8b47352ff5e3c462bb94303b05210ed5ebbaf6632a730081e756d7e96ccc31e1b056f193304361c4399034e31d9331613b351f73d3391582a75f51309f901e02d006f7db1802a62c49614b5786d741c717a97cb955b52474f634b68f83969efc936a802d99a45603dc652932640c0423abdff2a782dea132f100c7e42e8f0964a8d1b11d5e36b68bb14a92f6f5e32e2e93146fb12dd6e5754b19e4b61f8c81f6096df719a7437409d67fb7d922cba055634456412d45e151f5d71abc6ba4c753b5990e638e620d666d28c2a6c995776467af5983370e49d345e1f5a5aa2fb9cdd67a23261f821d0214163bab227678d0d7ad8abff1189d08ff6c99c1fbed2328d6798ab42e054565e6f21a7ceae10ecee99bfb79b2b5d46460c420b4ed35efb160fd8b33e0175fe3e396ad746bb4f611e9d104ccaa138b752aacf696e7da01c0ccef8d033bf1f57f656ba09cf9d7932b94d1e41bcd3e672ce43977ef9c4a7e106d8a8ba96bb07c2317d6d297098ed596801985ab8894889d79dde340b35da466e63fe7fc29707"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"665aeec9f18e3022b6b7cf30fe19e2112bd667ea57c32374ebd46c8b70eb2f3c","proof":"c690b6f3bd3a859f83e30ee733d162095d8cfb03c8df42a1b5f7547253348a14f457065983c20739dfd90aa8cf616a0222013f20664d5efa90c332a2c095b5162c7732718ca36df5a3982f8849ddb8e5105d884dec46e58dce7b97d93c3e09405adacf85a428c58c78ce4ea7322ddf48e748af65b59f302c0045eac35c71cb1c18c9daf3f65ea6cf1c7f7262027a4a3d1ebb4a07ee7fe52f6150f4b7b5cc990d8fe26ba0fcc9c48c4d453e0c528f0d91d9f7249b3a18d3358766916281777902f3a46b0888e3123075932566022c418218160259acc65d780953c38ac555f100540b32fa4cea484a5eed2876084fdc2fbee3b6f70c68bae212b33e682f70ec430c6ea025da6177f6beb0132340f667c843276fd67ff48fa2e8c6acc3cfd8a919d684f85f74418b1c107cf0a1e2affb0f83b28c668b7800d91a57b430181ad72bf818f0a39ebe9e630a3bf2058784f2f7c1d1298174afafc85ccd9aa83202fc4f0c4604af2351b37626f9b5ac39f9ddee0a16fa1a883bd2cf16580cbbc0244c54e205af8c7425740862e7c9ff898b438d57fa948f098358a1197143609787fc464ef720c1fe461d307ce4cb5b5834edb45710ab89a6c4e1acfb6b4d5873761569f02a84a5b9614675bff86388ac7b9de9d2cec3ff0eaede6657f5d07b9846a409581fd8e721398acb3de636ce279d0a2a64730b6ca842a27379082f13e826e42ba46de2bc1eaef0c970cc5adcf9cac0fb835620868df0d82eecf2147bd1ddc40eda8d209e3afd97faa09e5cb819815eb15557f8d376696e38e0083f1ae5783703020081001a0e8df7731146238720b4fd300debfe6d421bf2d0017ee5e2a0e758d4eb1b15c2b60a0d12bc77373b1e0114310a31c3c25a3addfc33f5599e52b4034ecf46cc49efb852c4652e62bf3c94c06b06e18be0619b58bf7cf631da797f09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f0a264f0e423f0db4c5f790e48b6bd51a05771c75fe98e8c50bfd7588feebc44","proof":"060eafd197c7700a01d930df86340608cc175a57b364df521081424c95a4983e40fdbfd98cdc4c796d5728e6ee33659a74647ff461f01757236fb0b33f4a8c746cbb2d0f5e9812f640dafbc04566a7b68f67e1c2710e090668e35437c1934f4ba6de6e6ceb71b21a119b7a5052507bea45d4b8e5c4c16b82dcedb14dec09fd080f21ed4b50aef069d66f9807b7170ebe13a58b29678b8828cde5bb293c9ed3063828eff3620202cb71822847b5e7c04ddee76ddf01ec667555552bc5e3cdd40db21d1b6b8377174ea56de073a0d0ca6f1a8ce36d4d434e33b44aa2c3cf66180d885419527b7ab73f36aab1167fc942878b8396e604ca89cd5d6cc3d6c248212cbc9da7ab737b34be98c8109ccd96fe1b695f2f050a6c3aa4b34328a2e0fa003a18b656ffc2d2a7ade7fff540f19a855ad65d5e79b9894fe0489a0f0613584c16346ff82b9fbae22b3d35827b6f0ce7619b4544d713d744ec7ebe9e2db92fb33a0ea74281ddad90fdbc0b343945c0dfbeb4e4186b12a3946685300ebad4db0c1d36d13dcc9e5a2cea304018b0f2369353838f0ec80fe1f613f6ec665e4e495a754a9c52477814ecc025c5d84900d8c4e72a1e1aa1d6304e7e8fcbd215953bd80d7eb5e7f0d0668e5039d092778facfdfd4f1ac29ea226dd9cbeda25e5cc592638aa0b27b3891aaf6748506f6bc140074f84c56a16c7d2214bfe12a240e9c5cb58ea6651c78d022abc35c30a96f6a7e3ef3ea582bd3e6fc4ccc92af8c368a8765ea4d203c52bbf420d661cd3c3d984140c79ce389cf142dcee4454ac41b50eeb3b30a0f4a301cf136da7b0505a7954469e0ffe7487dfa46a062f4276e1f84ade08ad1d446fbf9012959210c82776cf0ab27456f076b71b6a69eb716deb01a0d806b928b3efa0d92923e7284e97b9a934b0c355883067577c1dda6b771eccda6308"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8299dee572cf7613f1a57338c6e1dce53ee65bf1d3f7afd8a9b492a7f0a7036e","proof":"3628c0ade9bf05cdb62c97af22cd51d07bceeb3d9fb7c6a7859b35365ec0890470929a22d52229c834b94b6400a20166d0169b25afe87b2b1275a8c2a59756290eb7ce15e043fb9303ec568266bb86ea9b8c261689ef791f2aa543e85bc89c60e03609555e7383a8871636b974720954a889c3be5c166d2c30f7f9e7935d1e6557e8683a250f49a0c504dec31488f3e29f2811d1306d75e9980747e60c260806c654bbabb85110959169030ffecfd4cd86a83f65e24116a6ab2e9fc310f12401928f48c9af1aabfcb0f0662c0857b8bdb2a7a4af44045e33e2d08bce98770f032cbfc38980db280996a26519a99eac3db9897fd1f13f85ca857c1795ec6e922d2e2839060bb6bd4670f9bb2942bbb9ec57734d9a68b7da4b095bca31a454353a5a7cc96de0ce43779abc966fc56703b653e404894e99ce86eb62416dece3b42686cabb0eefbfa4b560c2ecec469b30f63a63e293d0ce7f14b1625ff751cd903400d59c165dbb1679b20773f42ccd3fda7b2ea0d737334399920c7cc229cf77689a16ad944917a06a21d86d70057e4bcdb70d291c15951f3d1bf492dc0c22fa7680e2a25886ffe5728e23b93af65e4b30f0cc04dbc5755ed08403c91495936931ca448883b43e4e49388756f1d91bf9c629235172a90db10c1f8fd72f4cfad06152d06ac7fb067cf1fbf63152ea4378b4854291a03b9c4a079d7257f1a0a26e5c522f0a1a1cf3bbdea44bc139157c31d4087c952d667120934bc733285b59f01a30e98b765ac62e94c0f43698d44fae13cfe0f1c628c3b0fa89b5d4c65207c70cb024128251989830b69a206bf6281f22cfd500a1161d646520da8c91d0f7845f8c9107c834eaa96ed08d2edcb3b812b97d6e5317cf2442c7f8d1d6079ce76e03341ccbf93903a48b6964b05af65941ffcc4c52dbdedeb29da93492856e4f9102"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ee28828ad4e559b33d64b896679e4c6279e662ea16b3b02baa300f87e0d6237b","proof":"74854fa7319d0f73f7d10bbbb568a23e5309b0ef96ee7d02ad45af52a803d340a6ec677d95d85ae4ac89f6ec8b9604c67c407b2255cd6776e45efe8d9e7c805b2816c696b0e500e74bc545fde1bafb84ccc43c151defca432a24ec4f914c4c7644c72a7b160aebe24ff6316c2a3e5eefbffa8e8051c94ae7186d2e027b7ab05f60e99c742e77379c1cb8fc06a3ff283b0b56ba0714bee2a50b7ab134687f070a238a6c7f21ab8cf065985532221f31939c8002444f02b7d0f6c9b00c5b1bf1058c3738bd4d3f339126d479ad347dd1c7bafeda654c85563935ca19214859a00ab2dc9ad883804e107a37ca9535335911a137707fa069e40e57f04ae3dbe846215025a33091a06a5ccd69958c4ece386a09093d348da575ca98a5ca1b7d283875fc136a2c381100e6951b228da03f267da622d24c7540d022f4e35521de532026ec42c6a696a841c578bdaa79cce857132419719db7823a601f977147a12645391ce08ae5551a024c53fb1c5f66d348cca271dce80e18dddf5b278815f899896a86bc1a61ced8bc99a3a2cbbbe5968f3af1a478fe4f048f2be9adabd50a30364afe77d9b737fce8892177123ff00aeb75ead1b8428dc54e387495e01cd8223344227a7a47fbf8a7a521777d06de0ba51d0f612b498fda1a7285ade5c1c085aa2a4a9cf175b5220e2923b4bcdd2b4bbea00b5efc84bf491eb7fcae88bcdd952546248456eeba97354a2723ee0457b365fc548d3a73ee7321d23753398eca53191400eb4431e34db6df1a8d00d1de40ee5c12194ec8c87c2f47dea6b60f4533806050dcfb6f224c6c3f30eaa3eea64187db4d3ab045b5bddd27e6f1ba332024be6655d1060b485fa82bb3fde05251e948a71c2bd41aa7270436deb789392a428a01276687b9e26a79ea19be23a545d96843d396c5597808211a7d193d7222eae00c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"48ee9873ddbece87dc3fce26f07cada1444ee0dce7117aa3754ac7c68d6a6b5d","proof":"8cc709e69a5937e383f0f3bc82233ee094d252e475a2446ae79a638a5f64a84dc0eb8feabcb83168775e009912407cc64bfb55ab11454a3c932033800ccc3c4f662fe27a8cb6239b1324499dda7ab6f07cfea17eaa25f8ada3e552e879d2453612862a0beb338691040792ad5cdf2d7e1d577305f93989b793d9f06a2cf4e5529481e2c256e67c7af053c22ff8e2d50af2e8473dbec804209893e7d7123f1c0c4aa9957b31274883a9053f300995c666c9d06d3a1f51c50de8dc00d02bc9490ec070f68874f07fadafa3cab2a7a52dc26f41f45e78e5a35fd7520db34d138b060e56fc89052db8abe7281cd275b58b961d2bf785f4f0765db4dd21bde2afec6f68383d2c6d020c5980bc3852fb646b4560a8011edc902b5975eb5c968ecc613b8897a971552599b55302d7159bc8041e34bd43711a7770749536fa2da7287e76224fe3aebb2c7f6774315afc8bfa0aea697e658a337de65f4586d6fa19f8e52846ffa4ef63dd0886481d2cd07207462b630aeb31bb9a4efa75849e834b380d690afb0267eca90ade656aaecf916e3642184d8fb8c1ff414d0cdcfcecc38a2a36367b73117ad3cb265b56026c28f9eada7c18fc480be6c37aa67285258a4a404ddeb8d8836cc18dc14f0cbfed7a1e4d764f302121d32b4e42bad51c776795da3df8d88833ae660bb69adc7d225bbb2f6b5f52f1bcdd37aac13ca7c18e11d6227ee8f97eaaae10d6dd49510e687a332ce1f959220dd9a389ce85a18750ada7bb07a69ae4f0d57c718e81ecd589df71c564efe0918afd925b3b639b0e3d520d242f584116f163a495b98b2a8c1ebfb194f8065caa3202ded54ad9cc1d2f862c0b7acef30157064685e000411ae34945150d6419882d6c45c7c8326fee95f9ce260dcc25b2f89b1137c44dee1c7c7b29231c89ab5403c11f3cb62142e2c4febc7801"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f44b7a1d3e4900b2833be5201d292c3c1930859fd4ec839acb5212952a35e103","proof":"4454ea72c5ed122f0697ea13e0eed07dc2568c3fc9ddf9460007f0cd09154a6a2e5532b0c9106a5d3823a4c5fe58bdecc0b680d58f827b14d8d945e531efef5f3086f374cba1c3c2cdaf1c00cbbb6fb2cfae8a4d0c2f12d2d9c851be9977903eb61e5ed2fa312c8d1a5d5ea58f44257b53ffd08144881e186671037ebdfa22477bb65c0d1a7f2f36a0615d81ad332a01a3708a3f07ba1e4673f7cf256be2270412ee21544584316f35a8b2a903e1ac42cf364471548d6f7c63a5609942ccd208308f37eeb74c825d73efe879bce5634db4d86fa860ea0200dc03f08987b4fa0b26e80dc2d0eb25c7974b9895d7967b510cd0dcb2b41e7977f612b0cd5aee5b73947a33cf6c47225feee52d5561c3caa17e1fcd0c99a58e467950025ef90aab41feeb4167073f64f7f0edadd3c5f8c6a4933e7e23143febb5a8e22c07179e5f1168bede862fac1ea6d6d9dbcabcb81020c244bff7809f90d2861bed76bf01bd604650a9d722e97d4a9c18a2a46696883cf3dfe64b17279ab329fded98c46e397a0414f54ef96e8a410f896e7706a6fe123237e4983b7288a1a114d8141fc43150c0cbe90ad974831792252a5971c7bea8f5975417f4bb02efed93e6a38b378a688864767ca0b4419d4cf724f76db9f3c2f705f15ad1700024ffa1d2e4b5a5265e0cf611cdee4356503e8e2d9127770ead82c6955a17a16a9fc9d8cc0da36de631608ef234b65bb46b4214a8e19e48f537e439933e8d4cf063d244377b7c8b6b10d6242953abe6f3fff4808e39a6f0d2fee8431004280b68eae5db41978025e65be26a8e9d317f47bc5b00b748913fadf65d5c3f69700dd9cd4bd1d58541546a183842905ae8cf6b34754437d3b21b8ff0f6673028335b769889b40fb237c6f1040fea97aaae901d21bc29ee2561060d49fb5746aa6cd88d06246016059767a006"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4ae8642bf9647d51acfd2ee61becd0ff683ac495b23fd0138f7719d0f22b6b63","proof":"166c623ae1a83be9b858d1b4f3b05fdf692026815deebab88ba2da0ea28287275a245c338d2f7fa5846762213bc2af97fd1bfaf6639a2250c416aea93fd3b320da7bbe89d23e890cbc081343fc16cd992d52a4e676d74e2e8bf6f5fb8e4aa16cee59ad95091d771d4e256e553078b3fd3d6b8714563ead8c342e12f24b239e60e3dd14d158f514a50199d2a5bdcffceda5c7f224ac694dba9fddb265fbdc5105a0d51c2f0b8012abc851e6362cd15cf940269bc673b7be8e1acbb7799bce4f03a9a5dbfd9c9ddfb177fbc050ca77a79d522da789159cb1564d9e65b1e0abce03285d9e2d9d6670fa3ca5d48451d1b18fff57a28f7743458637b70893d3916918fad341613474b7ba15198fe7642a5919b7115d06ed0c2dd7061a4a9dcce5e3190a6dc1dea0248be938d921e75a98f5a36a36b8e9150aeeaf766491c1754ccd6a54df0e67bc19fe71f94622fcc939432cde01d969c634f34c8755156d7bb0a65f86c7ba43c7e27d4a3bfb14bd16e034ddac745e0aaa0c4ffe84f3c3f5084e122c582781f7f42913749d7b3d7af30e30a5d0438c9534142b9f1ecfe9dbf8cfb953c45d7c4d4539958d082ffc6ba18005d0d3808593b2e9b08e84225625ef3ab413c491b81ea6b49a2ade3382c3680bec05baf0c9d3c8814739754ac6fbbb757876ecbccaed107fb7d6e013a223b7353eb8386025f201c3d80260ab850371e1e4445a676e94fb61204ec63b67734af9399e9a640db0450cb5301453232e2330c917ceaf54be88dd3e00661fa4a647d90593958f5d58524f88c425ee2f383f076768bee5edec61ab1ac6158552d1bb61471c08643ab6b3d3457a17d064301f2833307df7e3a457f8321485a372dcd1cb00870bf91bbb02acf31f60e0e6df418f070ac6ca60b4efbb6d622f5669071482e596671b05e4670af85c8db4d67d760d2307"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a82fa33e21de6ce7a9abb5a8d4b2e96c2b558e3158514e0a4414e488c1702a25","proof":"e4838889f8d7b2ec176350100bfedd6c0ca1467116b6ce2e03c3fafe7cfd844982cbd29d94c8e1f9fe38335117db0786af0e028991cfb0e6cf77fc5716529f6efe1d8c2aee040a1d9cc959d97b95d9ab4c79f26200b902f3bb710e8838816972dc2c10f1155375a08f33b6b6880d88a1414b8f4389db54ec5293af48d9958c588ff694e63164ee2a2c5fe75a3e7da256726ebd5558f24fa8e0dd5f89ee570404c0297eb6d59d4c21fc6135b73889042423ab979bf36c3cfd4af0f3e13e927c064a087bbbf6b73089f5b8a4bb94955c88b1bcf1595532843dc8795efceb9ba70eac3f3b64acc7e70921c66ba4e2575c844a3e0ad6f8215dc007dedd7a908ebc1ff658f62da599bdb5b4c8ebcf4c0528156e04802882ae2ec64dbf568b6f31f05860dfde54e302554f9a2059b3a2fee6be2da51e91a6e6367af8d727c839aa307f7e42837a05073c7236a6e26d8d8396fc81ebdf71c72b17c78b15c29dfe9be026eaa055019e34a73fda5b7642eeb734c653c633dc5b56a798b332d8f91bf3e4356aec1a1f588f7b7dd5ff18ea8ffa998982640fdf56b45f3f933c149e4da30e69c8892790dbb7fcda2799099699c4573a6fa9c047f96a24e039f7b5fa864886249a69ae4d3827d9cf57175dbecc963f89eccfacd7f5149da04ca107393ba621672a6d37acfcb780d35d53249704b5e86b30c3399c85d15391ba70d0ae425c306c14415a44eba2ec4ab867760dfc576ed665d07decf937b29aa736053c2c413d4134c9350e1f5c5755424c5edae93eadd03c39b26735ba110c62b685910c4a9d1e10610eb32ebb5039c211cb929cf277e858ca424d579da4c4bd5d85fc6a754a10620e0f2ff26d0870d2e3a5f65868921060fd536f89e09341de095d45a98a920abe5aeba4db4b61fde5715b5a1900e6635fd61fc7942663f826138e678f1fde0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4688b0aa4cd6322021d0f6857366c6db504fac6787c64a659ed4125fb1728a6f","proof":"ca2587fdd28796379aba359c8c61fc719904b59b29102d3d4eedb726da94243f00302c9116004f2adaf831d6eef9f3cc4f95db081ee46cba9b388014eca1ab376cebcfdd5234b7cd5504ddc9b5e7e42ad1663879d9c5076ce49c7e0318f593481ce0a67293341e484569a4dfb42716308fbb7888a86b579736d14d8be82be05ca9033bd0fc054d73a64de301096018a27779aab7ca9b07401d5fb4a6579b4b0926cd7262dc8fb82ea6452df87848c52f0efcbe10aeff6995d98040c5724a890859dca477eaf6095a581d445bd9d89b8fa0f4cbb05399f9135dbcfbbca1c33304a0a6e7e41cd8dd31a467ee489871543d98c3a5be2b1b14fd32f6e82734c939284654ae4394aa8f7d1f88ced6795911a01a16f91ce118dab9f34fbad267584f5fa4846e4c06babad77fbabf9b73a80e601886b0f0a9a26704b77e615d0c27cb4028704cd02de778b0b436fe171c794d467a59aa3505ec8184ad1ec73a7d92516274e5de7f7a2596db5cf3f26c55a0f805a211447c994fdd6106be40b93466173e9034eb9e8c3c819e5f1c1160f0dc534d55548db1f51cd3eca2524ed6be8a793b2290dec0c4606870f62e5f9c9f60d231d756c1db098f94b4687428395f4bcf11907af88acd9febcd9f3b40c039cffdae91786e66866b02a16e76a7cc8da39d575e7fb2abd7b82995647e76b1455d57f0283854c97040a29e701e40e2ef0e12033cb9bf8ca631d20c7d31b740f85cae8bb671350bc2124811ef00e78f70e84405a8547e9702a6ea5c83c6e13c97745b8e93f131749260b8b5e200feb678c8bc693c418e46fe76014cbb4cbe19b829d86e08fb6aa59ac5eaadbff486124d1d670d7364fa5d0bf7437d809a2bf56efec41729ec09a18aaa55ed726cc61727c888080cf2837435ccf22b84314ff1e556350ad3c409a6a854bd749cd4c7433549310e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ae1d0b837d72e66c13badcec15b4d27037449ab56ce4a150487179e1f072371c","proof":"726893ba694ad9c642b25c5a5961a052fdc3cb1ee1e70dcdbdfa133b9d5caf25bc8883158988166561b1fc70c3d35ac22e8279883b8c28d950eda0648a60752238f8ec655b4f09110bf69219809b5395c70e0865dde7c7259c41acbcd8d22d3afc01903e3b2ff15825126bbf837e81837e1a3b05e74a578f32bfc037528bda37d6f4df49e824b156263055b386f034c63a28559d0161cdb7de887260ab29d509a55168dcf220d1896fbb9559936a8b150ac76f6b6b408fac8119327132d36908a0bedd5f3521f10d811a2282749bcf501a0bb37feee95bdd03f5d1b2f3bd9a0e02ff796e27631643d0d0508baf662ef20a169836d41962890374bb4d342981326c2e6d68f26d1c1a1b59e684b5c41c1dd8faf04e09c807d43abeb9ea377f4e5ec6fd65765d3ca08ee97ee3fdc059b07c3ad213c14080f19bd8e7d5ce54de78695086a393aa685068fa3fae24d547497347358ee189df2632521b012ce725b12d1cf2da756c09d9bff35d27119d54ad5df4651ce99c43d7b61f260ea5f9dec378da99329503a2cc3612ba8dcc13055ea5580a006aa8ce2636e025c27578d18c0eb6a88a79b9e81fd816b70455d4683d8beef1f837ee2dc810f8630e8bd692573682fed22c50e0e89d0a1cdbc4a6c8c9a79d39730832fba731c441733a1aed1a070a500d11f860ed53b0d3579c1f04f254a3bc558ca0c028e53e35efe9527f521e0a3015a6bf01dd7193049d599779a062a583f06ef7de6a0ab1ce4e2a6d471c50fade94131c1b6f8ac704e9a7b25c2952c06735707c877c169320afe22961c81172734283720724ef937361e9af17e01596afa539b3a30b3fc6c1f130c83d7436c941eb64752f7533ed0f51179fb0cd7667a013b9e1fb57b4850862fa5317a50e77a097367d10d8934ff1bdd358fdd62a24b2d109f36ac82b5e35b5555a73a409"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e09304368393ef980961a210914287567fef1153910c9d6e9897f704d6402043","proof":"62967865931f26f3c543db8a3fcbe43bf9c99b11881828c50ce6115691017b26128178f2aab25770648fbc611f4cdea334c5581bddfd3e876ec98792b546392fc4134e172bfb86ecd67faec70b2dd20b815e8bdaeb0c51ce4d415cf24587202c06b02e2cad1b23038d7fb2b13136fd410940f64de513763b75ba1e6ba9c7ef375a9b739faa11da29ab20d36ca8f8b11a11bfe9d6aab9255648c41a45867cf00bfc348e9aeca456080a8fc53445059401293249d3195ebef4ef0583654f535e0e3273a1466fe61b884b0d2d1468deb5326d35183e3f75b05c3ecdea18bf8f5f075006c83d3534c7346bf106b56ad9bb7949762d031015c5c0807f4842e098b65f8cb40e2266b355708febedb9e6c78dfda10d376d539c4988c109364c0dbe390062983d7727dce2297f4ae5935356bfc65e2334beaff97d71c8f21a07ca549f06525b19ebee1de30b22d41916a57c8f9a54b102dab2cad380a6257ee713ba4f479c9ce05a817b6f2fa1bc3e2ef9683cc6f85c4fb0ca388c055d606527d314c25bdc3ee017f5cec18f83c373a204068a487b9a602896f3260af10e85c1c057dc3e5ec4b1834a86dc1e2ead66f85a47a4c23c9361600aa4ff7aa6ae2c383c8b9953dc981950e6b74bae22a25521857d378c1f05fbb0da66981df3c516388c823e7e505fc0def1571e2e64797f3160261af8296d49ef521c01332c472cacea3ca26594a44d0c024fd0528e25299b62fe629228d8d321e975cf8a3d70c6620d97170a2854b66a73658acd411fe5b15cfdd6236b1ef8723f344eeae15229042233a633b299e5e84daea5e23aa468b837e8355453876c1918e6e663a65420ce955d01442af15c96a6667957a12d5e7bf96da4fd7498c41e613fca0197f6be075bbb790da77208ebc672328b9566307797c2d7a5d296d373bc769824967493a2f1b48c04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ccf150f04c1ca70533fcda64fdd03e73bef454dc9a01aabd8f8f388b25b39616","proof":"0ef38d71d0188b8f614bd089ff7968c5a65b3173213bbddee668cd0aba079e2b16d4dddd3f208c1a5ac3d98a522824a0e91d546435c04706156da41922600079f601ab11051e5e9dd4f4ba5fe984eb9e3dd33c98885db991efa8fc653b72156e861d96fdce72d90a5af845593394e89b6a5c5cef81154207cb4321c92e408c334c6a7ad04b2d59978bf50546c116051c41866c58796e58e6358a7bbdb1be3302497591432bd4c7c1eb4e441647ec588caae939aa80f7541d4f15db67055b700c3caeaca5dfd207b92bf48bc4756a7db9617c856cf72161e3a499098696224e055cfaebd5c6e222829915a30534a31977d1ed5fc81e988ce6125c3805d165634f70f957b2fed45b7b617ad879e595bcc64bca7d542d0454c58975ea351092b503e40c3adf5a0a7b58247a283ec528302e36beb567fd9b66524a6af7b6d646d21d2a3d069f4f5232342171666f7777fb216584f6728706bced05a04518cf0cf076f0010156a5e92e9ccab038a8ce83ec67863446acf7ae95af625b3bd6f64b686d4acb81c4643a4ad69f121433855c7c2279d8d84c582ea2361a5435ba282b0e337ec2307136379af3b93b873c1b99a46ab0e7d89f38e64af3524b5fdbab971875843845e08755118f9b3cd70cb31e16665fe2f02c52b59d9897cebc4f28d3df6eae14db2c172c6a6d89394e8eda6d5043d9523dcd579043ca0f134c9116dd2e527441b88cfefece2eee8a795ec90059c6285d970a51a8c075ef4bb84b18f9a05ec4e5d257c2b2c68bb509971d83cd44c3ab459c96abb480600b39322c4d1dd37c1072afb9ea0ccfdb64335b4d0f657d81363795b594640dc5401fd7be30a5da13d6928011bfee499e1df157df8503cf5542e9172730c2a6aa8478ee01ae22e20478f3fd8411492a20ecef0ff90c88dc689c9d133f9157442bc56c42c712eef901"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"be2e20a0d72f2f7e0b6945b395137a8a46fb5372c560ae35d9fba6aaec769631","proof":"6a048aca2e06740813ca6ef6ed113d9bb2c16306a6631e6d69306cad55d3d062b855d93a8c898ce4f2ff144d8d5afffcb2cafe36d1ded011d4953176e912d56804aaaf585cd50f75cd7abddf758b904e2d641f3b12f4bb470d16ba39c5e5735aeec7a06e8f6ed6721243d98d7c8325eb2e3da98b052cae0b8e1d1246d71c9605ac48be20e69a44091abb56b9d64c507583136d6e7492ccc08f6b953b4e8d6503c457ae13767651d19d1bb6c9db196e85dfa167f14d28f3c064f3ec13ffdecf0a266d6004288c1a97b1840323b63a6ea27d8f6228f83fa1d4fae432ffcb0e030c2071bdff66dc39754a9a689994e96df0d1a60c0c11fd075bd77b8e130cd37415f6996841bfc57395591732d71b318bac7dbdc3ac4a78edcbc1917202ddb25b06d292d40ee576f82dd5968527f17ab8924615eed5c79a2b15ca9e38fd707c8b5e309f06251eb5a2a313632c9156d54ca1f2aaa2325835b4eb63345b6c53613111f25cc7d01e486440a51cf7e4d2a277e89917ddfa40d44a5521960daf6424ec04c6cfc0ad77485cc3519e444590d4e1e37834506826323b3147cec2ee0bd1db719062c5109cc734fe464be0a2c08df9521e5e08a2e06dc709c4da587f72ca1a65ee04a0b278817737bd4397637c91328ff6161e474b3c9a876455520998497f60f26fc95f58691069c7bb6c2340e33281abc8ed9807ab291da5896d9c2cceb70d923fede7d4219fec293614b21f4de8d0d413981edac2f279dd6a642c281019387a1f7d46b3e3f457e47478316115e2f127c287e94dc7aff8466b33c21702687152de49ee51a11ef62d5b829484d2ded0c45de195a17f231f575b31ed49eeb361aedcedc323cba96b954a5f30ef3bde5687c3090a979739b6a80c0e13ef7c78088b478eb538e766650b681ae1d939d12aa039d860d8bbf405cec4fb801567e906"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"32ddeb87a6427ab91ac64c1f595fa50e63c11eff6b8fceb43a251c2365a37a04","proof":"de8785186d34c6985eb85db1e6e4178eecf6842121e87c4a2b89c47cf34f5109167a8affa8aacc5a5a4929e6614b947f3199531bb2b3fb8d4868e153b81fab0588ab3ff31cba23b927114459583a018b324d5d1c2e0b03a23591ea71be90d62428a71471423deeff7ce96453a8ae62b608223aa62eec17a3828c8dcf478fd2419255a1110071034b60dbf6e2ab8de4ba752b64efeb4ad33e14e5a5a4bc1a330fd7dae00a875120f3ef7cd7a3c8852c52b7ee54fbc9653dfadd4bf7babb56e60499f73c3433d34b1eb498ac4995d6412e7903c9ca0e91fcecf93ac6933abbc7099c48598253d1357cc76e07b78dc1ea0a2d834894ed6b6c52656b18ae7b412c4fbe3fbdcc499b7bc041942ef7df35f7199012c48e59664810780c15846a1308524ae084e9be4434b8ce4190317068458cf756ef34ecf47fc605db6aec6f8e974b5a6e404290cf2f4dcd3df0568b3d602e6ed3734091ae3057300cf831795635600616086a9a371163dff5ebe9da8423e982a0f8e32a74ccfad8c5f251bac89009a4c1a66e9374bd7e780673aeec740fdce5acf5f3e76044963984be851e5ce125f85a55d32b6876774348de43ac341dfb57aeceb4f553f65972778c4a798d640f32471371e2bc569320719205c8991d8f5319d2802ade028b0962c6a382bb847d948d7ec696b56156c59337210fc90a88f9ffaba77d7a8fc124c3be36e397a90524a9ad3e00808543dbf72e176049d499d75d02a0a9bfc43febfa3dcddf3faa48c4fb2bccb55fbec3d12f033affbeb97ea1ed1ced75325aa778dc2cac60141446280dfaa8aa3b41f1f0ebc70b6a2b5cc12a0b55f57726f8d46f777a8f484cea02a916519b64033ff095bff48c541e7a1e75d8b281561a58990e5391aa72fdc00f3eb7dfd68e717dc70f657511a94556c61644e2bea8331f84a3e34b2649848702"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c40fa273346d17990d194d36b170c09f819649e8e705a8114b51b4bd90065927","proof":"0cf1dd3a74e659e912e7b29424b79a773203a90a25ce2d3e89a707aba1d6a90a16bc7e774387e428486c854630611f7c2c929d30d86e53f188b48130426f7e5f8460797978c7d1861daf2eaac0a0042548035d8ab5cc3da4fe18e20539a698271a2eaf3fb830ba947915958fbef58b4f467ef4b0f86f74d40b1be002b500dd394ea0238b5d3a83950616c81f036b64f10c5b15dbc6ffc34ed8d68ab4c94f1608d4a3fb4ca4c78398f324d74a588857f99437919a38aae08f71b925feec179d078d952907ae8fdd55924cda2414b169ad535c93f2ac2cda61ce672a85debb530c303a73271b6974b2b68fcab195e7df5229b7156e1f8e6b6428083cd44f7910607a00d96236ecdbf4d5b0a755e758a8cae20fb5be4c467a3d54bb1b99532df025dc241afc9cb51030cc1e06469ad1220a17ba40a88a679c879bec0af211a72239384e08785828b6ff1db61c1d075c2b05de3dadf3a5ef065486a4a4b308361a43f4938e7701d41ec63f0fbbf5e1c249338bcd92023ce756418bf27bc6bf364718a4eb5f607cb2cce18f2e47d5019435c51fb051ca8c3f9e53074e686f2b8925363003aede6a2315e187144ee3d57fd21466399115200a0ad90d5e1739a45c7907ca08bbb70fffc854a8c174275bd6a411d3749ee752a56b49cba31ffea0b5e37c2cfc0c8b3c75f785d78e22ee216aef079732eae91e9a622e2af13522864fb55fb2abacc0fd8949952e026ff101206a1b4e60bc10e1883752fa5fa5b253f84e6e0e4add84bd37f7d41386028d62aa117b45e0ea73f0397fbf0c8134e9ef2b1e15bab26fba35d0b7fb508c70b25e9914e72828a4de4ec13f649bdd2578dd56ed228337ee975a01106d666e8bef13dcb2855ba60c096f6f22776ce037b4e6a7120d6d2b1de5085b055e5a8af991fe0b96b51b3ea5621a590cef2fff40ff83d9c000"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"28127d173f279438d2ab6360628f9c9d899d928a10da6e6479e6ab806624c804","proof":"94d5d8243fa533f98846940f799a422b999aebbcb38bb7449cd7890bf2b8546f20a0707584679c14d0c592bd75223796333ee37bafc90ae27e2845f3d058fa19469973d00d9d49999e3959afa31aef204dee9646b0193adfafde7ababfdded77f4b4d82d5f0ef9fe01af909a728417e23ce78641919b5360bc773bb0ebf60219e4797aa458a6e41ff65d5983eb7325d60d5915101ed4afdd9fd43f6556de5e0bdeaeb7fb789d16e0b8068ccb30f6f015f1a436f152e1f5391c824e58e9e4ef00eb61ac434cad2738e32e9224229d498257b4b16a15532be96bfb5ee8840db6032213a3f075b750257bc85dd6be786ec356a1bab5f63e94f695c082a108f07c3d80a66e496434629d4bf536301746d75782de4bca746fd615b30bd888ee04cf0dca2014c31b1f82be86ce94a4ba58b48500ad3b23bb49c6bc62980d76d26bd31f86c2a10decbcd81c679684209bb5a0b70d9f96bdafc8be30ee48f9a4353df4458807a6ed129270d613564f0f53c4f1b24de13844c5ce800c333491ab75176a79a80b60dc887b3cc1012c7ea99dd602f0e919ce0954fe424c17c85169fa1fa97942400777527884a1ed75b7437fca2dc07bff7695dded8f70e7e385c8f28d7f0d6217b9acb029d133b2926b1dad30e439e833691ed1408d62e0eb97c359579f6130f002d1b3fcb53554e2548ee415e5a69b92492cc6fb7c582c751ba65a83485ee6e8d509e99537405c319f6f91069450b3eb1f19aae8d7c9dc7d5aaf65d8ac5004477765e6a1d553bcd4d0975432a51c6bfa341dd5dfd73acbf560807820d1508a31877cd04eba7d3a991633eeff742d67f214cf7db7c5ec10c8052f30281556fc5e923e1c7932acff20b1a6f90489b34e0583a7a2b72f3dcc95d705eb6f310753c4d679ba0661202e1055360718afcf8bb5ad7e603e15d51f3daf4d17c31f07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"72d47aed466efe918a8728d87637fa385119d12d124a26fe512b61baf8acd86f","proof":"00ea1e577bae85b48d95fb5211a5d0a1fbb5f0341fc070e5f00cedd6e34d78235687b26a8315ed930da1ed709f78755b48996df262881d536cb612eaa84a8462c001834994bb68ca853db73080da149e856f9df22f93e54c1169292ad2497749e23cd732876ec22f5fc797230d1543bdfba758e6f808c8e5c8a9c462dd48d73f170573ba3d538966bf50ebb8811040a949b46e099c871b62ec846c6234b40508e019688c348516e083e5652eac45934edff96b051d1de3ec74148f0dbe0d620229bca77dbbb30e9abaa0f4e66a1cef11a393a22750e1953a62b59276bfe240053e22c2ae05f57c8fde2b3d87af1f0397d34d7827a10ff2cea3408655d7e88759c275ef0d79d0f6c51639ca6c6b2aa383d35a58c9af760cb8daeeab9ff205d959c833fece6a74363a52e4b4846b9282b3e7a9dadce5869c237287f00f598b901d524e0b40d4149e0bae762a0ad6f44b2f2e06d3a4255e2b0db8f50d62d6f90a2dce3b85fc454a95032f0369458f29a174244d566c86d5b2277abd6141165e2f45c48ddcc6c19241958eea71053ad4f53f54db77604a82051dc1b5242621ad5a3e0aaa1824f390f25d1128a14621bfe95be38e7da2e1c3a5f7cb983d56cb680355ecd3b1308d1747d0a4a8b9958f959b047976af8938b8777c277374763b067476aced29c1244c0f40fff111e2eff53c80e44a99625733bfc4435b99329690b43ce4c581236e3e5835da7c0bdeb213c72777ba3ef7550dca377a1c70494447967e5eb9595de4edeb3749c9f1187a5ffe2cd8c2fd1176028a878181901e999f0e5fdc87d0e4faf0840efeb521bc2e463ee90e79cd25c2d2035a4ba532656d255e5be5b90658788dfc32c744dc029a461666cc71f16e0d56f245caca3571522fac0e8cd008d25b79c345112bd428cafca7ec7fc241ebcb09f082b291670a8c047c0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7090e15a27453a5cab0c021b79e0aeaaa097433308ba5e3bdf9509404dd66359","proof":"04bf8e0780e828f0c484da5457372acdb36d0c3182c044330bade0050f85753416e60be6d621ba5f9ac8b4a052be56406bc3a7af1fec91aa69ec34ab9e00e96df0729361d743b172ba3771a48790fd8f7466751a4818574c880f327f27a6f8485c0eb8dd77cef9c2ef9237ec46eae5804bbee92857ab61df5a8a6de5442b0e65d857f17556351c49fb84f2aba5127a974eb7be1789ba69408f2ee427e713470a83fea269557ac18f6e71b0115a8129cd5d3169ce990d32db72108ab6acf6190eabfcd779f00c61df4792f6a20ddfdcc24242566fc234b581940f338e13f9e704b473a2820a3f13038348abfeb586b6f4b35af062fb279844f01291ba81d5892fbaa655ebe621c6d5ad18477e20e445121fd3d2a48854ade1a2b3c76830a99b1166ee26da7f2480d41bfbc7a18db855a1861a8c855bc352de5fe69beb2d74623b508fe9af65578b0b4befd987eb5a97fc1f7db89c4b1aee641388f1006fffb455de73cab0df10ee9298b1cc10bfef4240e7c92bff1ddc200e4f7c3eeb8228b449801c390c33af6969ebabd752143088f0710cb83a044c4f0105ba7c67c880766da68e798de9d633504b57fde674be5a94abcd946381d095432be222e1161742346039fdb10486d0687aee30763f5d1041abd8bf8cf4e2ba88701b59aa89c411002494f94d22e4dcd8c76b54c49a9d9d50b1c12d1c299130a7b7b62d9de1f3334fec6f8bde1dc932bee032638d454dd042e15aa37af36591fa6f798aab3b69646aaa51dbddd79967f116803ea0ab909f3f6a6edda9164871ccd6253f4f2f36ce10648ad0ef638c8507f2ef37223542e9851dc016a51afc6f7b5ab6f5e6cc6f9a728a02cbf8d09a8006ef993c8c7fb5cee3d7b819988e5e7f7250077f6086f7bc08efe8f771f65d68a84907ad38c108da1af695386814f8bbe3764a6885102a730a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"84084bdb40fa764a36130f4586dc327bcc3c821afd37bebde5ac05182a3cdd79","proof":"8a7b609e6f6dba6792ff4d1a29d777430088e5f624517d459bc4f77806ce8875c24d126a473331d63d9ee085538a1d87333a27e18dae9f8e4d970b7851c3112ad48cb7e5a51c7b60c409fa20b28f545d2af1bf6a0153235c963d25f25cb8b66ea288f0994e0c5c215c1e32d77d96f28ac5fa22245bbc3dcfaa7900b0fb1c9147484646d7d70eeed8f5d87f66163504f0375a6a81853998cc3b37916fbcd7ce083d5e766fb0c91134c23591e34a02b876af8738146cab7193677cbd2bf413ac044d0aeca917f69157516db2054930a303ddea2f81674aa2cf81e6fd62201c4f0f1e281ce00232658861829642465332985d8edb45bac16518528ba6b53606ad00440a4c8ec737ee532cd9095d83c8e6fa95d5a374f3e3a558467148fed77b8b3db2a41488a6b5db0cddfb0df7f383e14ecf018ca4fc11a5a0a594af525dcdcf6a2ab74f3a376e16970fcb7b14d41027b22846568dd840feadf5eb21826cf4c93982f71b8284241fed563cc36f5bdd0bd5c4c59a64527f0a536be69c5c632f2f41b4eea257a3968f86cc02d181f9302868dda05a4e14973d88ed5f965a1b519a4462219cba5bae6f680522d1effa17af315a65939056e26550246d31c139462f197a71c1506a68784d306ce5f4d6d9549522ff5af6a303a3857b26a312ab961b15528890c6dc2173caff989bfd30b898344ccf8034e02fdce17d66b3ef145ddc2ab03d693b0c55c12e238b32e63ba7fdfdc2a7c70ea16426fc8bbc0683ab81ce5c8074236133d3dba3a2d166463d78759c364bd5d0aded14c92dfb3997bf1a8e107697564a16062760ada58981c6c6294a264120921ab95dafc9ae8afee4ab626bbcc1cb439a40e01a9c0008ad2aa548333c9fb5f069199794408ceb8847c0d70a2785eb856ffd5b43e768540251129fbdeae61ee7500452080d6b48f38bcf7b0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"348e609b4194a25482990e12ed5e67fc59c711bc8d631c588c70070cf6628605","proof":"c255425a6cd2d0b9fcd5c0c0f46df0f18ecdeec55668ba50af2610960afff3388c5f816e8fd63054d9bca4d8d6f9a95b30c15a289ca99d12dd1331b91e1dba6a6eaac4fc3e0dd0f89705cc1ed1785d6e6974a5fd4a8e9cd7592d44c8ee22883c24f2e63fef78d7d8fc3a167a21e17099adef3d44227a6cd6822f0cd596649142b76332e022d78b50d3c8b83c47b1a0cacec5e22ebcd500bbbebaed7a8c0f980a8b12299d70a7e2cb58571b83d8a5aa14962213809b777cb13bc9e185f9a3350915fb594c9147d3039e33d3cad81f9b75b9dfd849126e22da08dfc85c4b7af30072a9af6e25bb9f82f68d3fbe96ad353f46c626c0b31d42803e8b234dc73ec73d2093065daf38209e49445c4dc106c056aa75618696b47426fb2f8a111aa36e79cce9714afe1dbf993f2b2a4fcaf8c39fc3b4ca3cb7b6083d8131603757a1724be298570a1e389e2e90c24a2eaf824a85a78f9a2bbc973cf715911fd72a0d3d66b480b8e1edcc3834a269a3f1a7db453d046dc6b7718012fe81800c0d6a4e527c180a9ffc48f905d64e79241d5c30bec7e35b7eaf976af5aa511e93143c7fc455725c15171640c8ee9a62023aca5bbf4e4cd5c6eb7d8837f4be2e2f51daa4ad79529ba1f9b3054a12c990aa5592d62b9cb209aa041ccf6cb152843136fec4837a0a7bc9f9a657c5013e58a48560d6d837b1dc19cc7945222e3c19c466d5b2d05fd670dda4947c10b3b5115ed3317c9e2b4efd137f3adbded81a0afe9707b9120dcc328a5563b74c7d07840f466431fb64f51baabace02040a7817e83326c5e04ea275093a4fb51aa24ceaeeb5393da90e8d3fa4696d4e24217a5ee96d69f8170092f012f5a672de164c8d53850c399e77e261e2150f81fe467eea771e1700350d0a5122da63b495fbbafb74b3735deadfd44e5cfa91632b1cfa0be34b0d264e0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ac10d715e252dc171649ad565bb8b6bb09a732a94fb95d89dcd42144ba5e547a","proof":"aad8429650c84dbd91ff81d16e032ed7336daacb7a2a5f1231e93ca784c3e2039a4a7834fa94411caed252194d8ffc229a11bacb4fc97c40aba5259c6830bc12f6919d20f08852ce9bfc2589db1e4c684d531c2a9322bf1363652c25e3e81115fa070de229772159bb4a87e2c1ef73a4c537d38be872990d6e460aac3a8448718cca1f79170f449d3b613643c5c406b850f655ff721e68e2c34085e82388b00be7bf8c15e833026b5f6ca0a7d60955bd4faadf1e955c1cc34874759f25ea2f019d114f56f54ee2e7b74759f6b1c4d5ed04820e5c588e108866a1c0ea261e2508904a53725953b49995453da096e676451b18f46b3092fff369221ef090bf652ac8a7bfbe03030594c064443a13c5e34b8d8bab8b0b8bd7eae2f40496a70d4e1eaa04efcb99e85d722478078fe0665b6a088a546977cc8f5d5c1bdf3e064668753acef65afad52e2ef92e2906e6f8304d85b01b0bb2c10a016a5f66e3df855c74906538058170de372af7d05c58b39c1d26d564326e65ddff67cd1a2922691202f43707e73e6d5be7bdc29160dd6f58fe741e2f5e3ff5e3e03ca7f09089f10821d4ca2339f8f34bd88f9a07b731904cfa0d15adf47975df7e5e57fac92b5f6416ac88140826b93882c6828db1a53782e791de18063228ef49be28505a33873c6836aeef701d059fd33f17da9503b63545012575b034271b98590837c89d82382890f9e6a893696f6d7de96685179b67feee2a4144a71007f8deec5baffb6b9c7f527d5fbc26b29dd869d2d92d6ce6830f61a4c178ecdf98019f60cd06fc9cbd79f03e43ad041e1b1f9aab315318cc1e2dab189f27c3e8515fdb7a592c8f04d400be641f9ae0a161108e258449c9a95c930758405528d34472972296fc1bc7b100c7b0134f742b59f7c3149be33df1e308f5a42b7ac3bf1165feb23b0051a1420f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9853f2fee1ea2d0005aaeaf7ccd271b34aecda36c0ebd094aa24c89d5652e75d","proof":"5c2a26257c8f46ee6dabd7dd216e40a34f7bed1a4380c19b066b136437d5da49180651666c2e3d32f660317a5c8ab4794fad0044c6eeb17ba1a2689fed4b3c1b062ed884f8880135d58549787cc29664d323bffaca0a9e6a225c99785d1fa47ff6ce50c12cdc6a4968908bb2b735d99789a6e20e22933cabafa4041c7d1b28481703866e1a17021d2a3c64e90f450918a5f8dda6f82c61e9589c585b4ed2880c2617d7baff3f7e2b82e369585aa412658ad720a23af2d2a747818feece03f00f67ec2ebe31fbc3a3947df4c815b8453fa9c5f73e5ad1a46b029bcdc860a46605ac535a9365ca3231c7cdff42505c8053b97a5bb9da5616fef530ba93f04cf800e6a11976e3b778f92d0fced8ac12638d98c7794e75031698adc6cb680fa7f618308506d454fcf6926c7bfebd3f754428d35a375f47ea88023bddb112d111a4411235e41728aa36e01f6ed8246b5eda1b2b3db881d9db801fd3384e24f8f1653936dde9a5a049953efc9b8ae9e63f0c95c53c893575dd724037f3c03f6192102d86f2840b2544d3de72386ba01507e9a03b838115be7dd0a9f9680a4779a94b23c64c5dc4af5838499c2334dbabb9f9e323706232675360cd989bde44602a87645e563bf27c51de34026255ff2c5f15d6cd9ce0059ec4781bb7343c5988bb3966521c7c704172b938dfa806919e1d51539a49a125021110d2424dfd113c1c4a2b142315fbf812c7dafc84d332904ad823b4fe998fcfcf3d0d6580d3b39d899f2b5e44523cf9a63c3fcb368a60e057a6ba0df6e4951d9204ff91bfee92072b73400cf59e54de1bdeda594733cfc4b9379b29d16c550f122b5a29200198e3b84d5b16a53be60b0055a2868f35feeb95d9f16f80c461d5a68409a71a441b027b930e184643bf8aa236d33c12b5792485be56b459b9ce55361f01a93039911c2dd602"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5c4e16afdf801c95be32ec303ae10bc68a1362446b921eca7bc13cb16fb75c0f","proof":"06009ae6dacd0e0ccfd506d391c7e92979c05d819bec791d52cb82b014c7661aaea88f97e4a4605451e9d704981c2eef1cbbf18e10f0726490f8dcd092109c4bb2b5efdbc75db77f979b5a3322449dc8329036eb61034f0db517388064342d3ffc87cc0af1c78a297c4c3c60fb2f9fda40630669eec44544b9feb82f95f9d62642f464b2f6cc4cbae4fbeed172940f0831519d8b1a5666a518385848a289e20f7c4f6b25ae5acb2ae156066d1dac0a6a264edad0402b3bcecbd10183cfca5a0886a5e2795b24419ef2858591bdf09992c414cf51e544c4484c27c00f77e7cf0d32494f093018fb26cc8a885d81d293cbbb26f1173527d02a9b786d1b05ad3d1ccc57847edc06404645477461bcfcbce5ea83cc974a8bd5a951f5e6cd2556a75516a199ddf5c34ef307dac2ff57b1fd9e5760f016d5f3a0905e6a1af15f1a4b5f387779eaed81a9c00e54c19bb4f760cd5284c7696f227799c1396114fd2e18190ac85c9b15012181936f26739df1b77bcca2d63b64bba819ee00ee43f5184d5d903bb2d6849e2e380436ca356f1fa7c233b111e4a7e7eec2b600d704200062010af11b687fea6c7970afd9f0d822c0197262144652389d9a7bf913e82c9756645027d3dd7fafd06709e2a5bb0ec66524ac17bb5e4147b541077518a2c6392150463b463e2aa15e22f46a8a4b7a7a3df072991a9a520ad5f1e6e63588d68ef55da6ff954add4ae18ca2b301f97c288bbed354dfa6a2c615b78b5e69d7329bd46eb21a0b7d14f0ddcca4fad6b1620947e148434772e37625a44a861c6927e0cb646eac18cb7c75e739231e3b4978774c4dd0faaa9519bfa4acfd8df9fb8808317c939983e1ea068bd5dd9c9ccc0371d2117926219ca8ec60b64211414359489e0cd09765efa01da81f6bc25822f58d7472c426d0f9f19366ad92b3ff256f966504"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"84f51230adfe3625986385bc6f649a8e78186c76b57b54868341230b3ca20e63","proof":"628880a5eb6299e3766868a5430ebdf67caf2aa4623e85ed413cc0f6103a084206b84399f680b3e0a057ec08adb9297c718ff9577d3f78a7c9acc292f17bde77346fd6beb42fc5ca5b8fd261b9cd8ddb81eaa19f91152dec4b621e4d4156972c104b02f41de9fd082547b7fe77af9942acb321354df8b54ff0c9dc8b61ebb319cabf6bf80590eff11bf1a82db3250ca44d2064309b4242581b541c48ca05fc0a82fc5d176a37366d4d6110d5a6a02b6f278e1f7b1dae5bd6a0420e37fba9bf021d9b2f7a00ca7dc5dbca8fdf08b5aeb26e3e6ab16f1c6b4dcfd10e23af5ced08e820e93f8e4cd3c2b673e421b7e9543aa59912e294b19139753406fee926045e9ca7a36550bc095f4bd2b3aad2d59e39400388cf7e054ab63188f9f71bc08554aede2b0356591f6e98eba266844818d2854be5d0bae8c93f80244526a2c0d771823b64496d7624195141bc976253339a99bdbf1500ad87c8dc6deecaa3d1e23920aa1e18d723c6e892bec8a5e189d9049cfbbf6ad4048adde627c0af7b0610755e6a86f67e563f431b8773beca097e68b1938c459cddeab46bc6448745ce3d7d68bcd3eaaffd1397f15221762141c52fc9dbe4ac370b8a79294d928473f92b5b92da6d90aa3884457f3f7b68d309bc99ad28d734e7f107341c0a0b753a1a8a22aec8b543866594615c73a5830c5418ecd250ae7c1fa8e7202b70338b9b4f5d236677d9dc208fe213552d9fda2fcd30631b1573ebb27f277ea18d38150a910115aa3419624f5f3aaa8340733d63cd022be3d4baa40cd35a182ce091058208001254f097d9942f7841a9d73f14aacd18e88fa6f76df8d08373d143f71388b6606b4c748e30713433d21f126403b5f00c3f7e9896019109300bb903a54e236f230107e2a9acf4a7765cefcc9ea84f527e2e515967db8e89133a32280a7de2cf080b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8626a5ddaf5ad2f8e1cba67c37b67d146d315a734fa68be677e8cf01ae7b7b63","proof":"5ed4644d0418a50bafd109420d50e8320922a978167b98ce12e9d77439ecc824c84b6befa8a27001ed961bb3bca85644b3d0d907783841441b849638ae13721a2cb53d2ba61a8e45c2b9b1d91f374bf57e2128d8c9f5481a5a7335f0aa4d887a523bba9d473a849d868c7f46d13e4d90b5fe001a8150810d50776654fa5c643357e55947f93e89fa774e47bc2710aa84d4f28275623525466258317401c859062dcc19250ccb64208d250aabe6b8d20ee041e8c079873821709f6baf878b6b0a33ef8a140ef177eb85a7676ba8b4e79e76d415164d7ca70ea54dda4f25d87206bcc3d25dd0b68c32ffaff55f78ee5d0ab89a649df499ebf11c78c28ced98c77d484cdc4623215fae83b47dc1107871afc2d653b6aa807d105b268df19905b476e89c26a4e348ef676f441fbc3b82f13e2d36a486efc231ab7feb14b8ea249308cc74207469dc46d344adfb2da63b390540df40a918f7cbbc276a6095997c44239e6257ca684670d3cdc2cffbdd1ae4166305739a7d834ce3ee4e84511dd98213e2d8d4d3c21ac9247129d701c519508ecb967fe3542a0faca226bc0518dccd66e6c4e9ff3680372572049cbf57f2ddeaef0352e868185fa5cb68fae7b0571d6070e31b0fe47017e68391ed437dc639a9d1f2ddb24293db546466f8b4c0d8dd04ccb5e314d783002a0438972ce76fd007fc1c67b650b92ef93211d5c6ea2835150278a5007bf967e37a02908f4cf9cab898dedbde9d1264b161e4ce87abcbbd3e6cdad17d6891d9b247d2132b2c0299b4071904fb679099ad9aba703a18088e0a90eef23669ebdff8e48c92b4e96c74383e55e2787a3c68f6cd36f4d200598e5b2810df3d34bbbfc5e87064b9712c8fc7af3d84e0c82524c44ba00aa96c5f660b819e06916e37dc42fee46ade549a52e5488bd759bfa1eae3ba567432d18bc706"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e0bfd181b1104392d55e0b411596f8b0db2c1eeae39a86e341bdd0e0eb9b1a65","proof":"608a7cb6f5c3b8303e3955445d0692da512aa34e1f677a14189e5fbbd6425509ec931aedcb70b200f2292615a9286c2710e02dc9492f0faf0d218e83f17ea40ef60d3bd9c6d26e720f6ad38baadc2318c4487b9f716b74d65a9ffa5863542e7f1ad8199672387057e93b88fa535d9ce264289dac4e81cda9a964470f150cb438a365be080453e578e01c542a9ae5bbbaa6e25ad072c56fdc20ac725f2d1ef30cd3faff5b3ab45a6d94411612be63e658f2da427cec0f082f362dd31f5d7e4e020aa39bb2828dfe2e544793475e4663cdf6950e8098ab9a9b890229ffa24e0c0904e445fbbf3bd73b6251f12d2b2b3d5aad8e833fff4d66b19877f57dac0edc00b20683673bd8f52102a9ae5c921edf097bc8223eb7b78ed18af161b2b6c5a72f6c0f99956e15d62b29842486c8bcd6191846048929932f530d7910895df8bd39c806cc9d48792f1027e531f8b10c27a5fab766c6e09a36f4e36fe3c68694681970927da536dae9ace82d90722a1a2b05ef3a6e47e4c97e585af9b72fc1414b56fe13ef633ddb7af7d3cf386e783b8296272415c99d35902cdbb2e00f7020c549ee8aaea673c8f125f0bffb84f4be385853fc319f29fe607b9e8952af7116ab230ca5e47a0b857da2b73569e29bc5ec000ab7451f13983356ea87dfb91749762bfc8c194390f0fefa5bd36c569aa58149ef54a5b7bb92f87d52e11b13d07c2118ae02bf850bdbb1c2adb4f22ece53556e64b4c3df0fb3137049d6f2456c83df0e82cd981a0fdfb80e3e11e3bf16f9d0ba8040359d09cf4e6522597d2aaadee216a0b32ef40523d8345314168244623f35ee5089df57e21f2e5e21709b09056a7b7c7030cf6a8505ef604c516e53efc386f27b80856fdd9bc5527727e853366a0071c17edc2241c461e389cb4804fd8f0b95efb0aed5b8ef4308527986cb7fc80c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"845e2b590d99b609409bb48f5338658d2833ce0f2f06eb9c532eeeb97816dc11","proof":"1aef8a01b989d5d874bcdde9d44c77b55a9e6578590c27e673e0eb9de09b173b28df507d703a1b730e343601a1fdf19b0500c0400da222bafc4b436410aaa074aa193b4c15a6870f56ddf6d5169455a740edad1c45cacd76517b643e6843fb789aa3cb2d564369fa66d8f8126b32170cc62b0574827b9f1a55da5dff3893ab29d547f06d237532ffa2085da8b7bb70ffb0e5c96f04bc3671ac710d23398968034330c300771e833fcc06829bb1a8385e704478528e9cfd98370fa8c9249be60b7d2a5280bccb4ab76f4ff3d34850f648f0f4e73a237a1d217d22b4ed8cff7a0f624b947800ec2f127704b6d1a2442fcaaf5a3c5d550a0154924a389ae447736ce0249f953e3bf68a7a8724dd4a38d2a81b2182458ddba98d59896ba34ad25227b2124f0c7d6804f27f4b52d2ccce6d33162ff523241c1223b9e4db155b1c06742ecdbabdc505398f23357f58b41339df792f87b2e2057a41673dcbe5cc476e2a5e60381e6f558ace1156927c92b9c6c56a6d986cc50d5a72aea02cee3e984b5efef578a7b92fefabfcc41d65d6d1e645aa49dea6a067eb57bbabf436db0ebe42105a3efeb33effbe1852fedcfe61eb1d99fef8bb34de9e53f6c95af00bacc03f1c488bd9e430c7968da780af92be65d51275a12f12be251407fd6da5aa7fd15a26cca4eb2b94ad4e4b91d84dad6dbb1ec6cc03d9a6aef2d6083d48064e6f0d24f66cc92676af31dc88cba8041598228cb212bf17a928993083cbbd0e782d75035ae005408566191220949b78007cd46d3d4ae44600de620dcf6ce8f264439a416403c451bd2fdb2d41bec6a4f18e334bcdbaee5122329185a5c37987591f8d05fffcef361c47106511e8c8ded0b3e9e9a0ff5804420249e7de7e853d2100c8074bb7367f200e33c93485bdc03ba6dbff9e55750d76220c78db37c7538118220a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a86a04a44c00c5d4110f3583c85909d7629d24985bd6eab1d456e07eb9bfd448","proof":"3407b3e4e9d34c316d9bd7096b72c084e0725ff627f064a91b190c323e222d36002ebb05662ead504cd5c1f89a323d8aecc94a9cd64c6bbd7b76892d11115026d2ca0d791fed4e6b376804c6c37d8123d4c41b0fb3a140625c98547eb49c0601a8f3faf2073f1b80cbb2df6d9045503dc6bba01e5fc26a17ae0a91f32aef477695fb1453b08b05fcc3e96802b6cc005c459559cb643aeb859c1ef5e08ed73704d8a13116e5ce60538f133b8080b272a72522be3b98d733610e5a0785fa050200877e94598b950fa9454d8d6df16594b011d3216cc04d6e4ec5d8df9e334bb205ae2af88d5a62ccf633a7eed07d370aac43bbf8e9089f4e64eea0e874f52c7e777c22346423bbe1e3f9c0c7258355774168f5bb85cc61eb714d2fad2e41f0d70018aa06b6689a3d68facee076969828496955845943bc4c912c3af040ce68b02804aa6da97a982b6717e9eb0b5f907c74e8ceb6dd9c8c9fa16dd2e0ca39de9c62ba73ddabaaad0c32d27f1ae3d2ef2fcd012ca4a668214c4d8b098156bfa3b730b6fd04ba73bb4f464f8c0eb30c1654138c719122b2114c02a39903ad89628b72d218d10029a89ac2bc5fb4df20bad29f34b1214acc0ca163b5a51a3777e75761aca78535c19454c254bb9fc9a56a6bda07496a136729fac4359d9c63f5394d360a6e85de42e1a93d5326e0bbb1297cba107dc26e27047100d177d9c4c3e1132a06621ffc6fbb7fa790ff691c1a73a1c50da9c5aa72894db022cbb6c37fa2131f04b88dedc3edde1dfe19de149c2b1e243920e918a9f521cba98b95ca22c13170e2aa4453450366b86f5d39b45cc4e533ba60e19e159b7d087aade9738e7daf210d484f6b803c1c421155e654dbf3632ddf393fb4197fc85b7e3d4a188639a1076d7ca568e76b40412b31083087af79a47fa338f7b0505f57565fcdf31c938c0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"720d21b361ab0ee281c0c2ce35fdb89bf0c457aca940de5c88e00aa9032f9c49","proof":"780131c824e679fec12348bf956036f329c8f414735e38c50e38daed41098d741aa5ec7edd977c5a02f9a483181b802c8e1ffdfbde1ffa3fcb4ee0680a88693b5ede27901c1b0a67eabc5976d539f76cbb6cf45ece8dafb1657a3f40020c942a6ac05d00b557739a19d13680d978aa978bdafa164be534a87b318afd9067d367f1fa4d5c5ff08684f20f124826ce922b8276f7f16bd2e8b6c8cd960cbfd8b4031c801df131d46614799e58dfb137d09dafe962fc58b63821c401ef7169ae230ff4240af89913700989db7de3d90729f55814d4921874a73a935ca1386d9f470dd0cdf32aa743639b621cf6a6aa8b781154777b550e782844929b2a38877b6d7400821eb6de0045cf2cb6ddabb026faed100db71e81c42d75fa8fa2f23042a2565c5cd638c6039fea9f9a4981e18520270fc0840c6ec40452156ccb9a8153b8121a6bf2059943f365f9367f4ce7102a3fbc43e3a47085ca0fed91a97d87ac5f45c2fe0ae4efc58c1cec84a9c81f104ddf7c5e9026bc2723ba033abccaec07221ac2bb16e3de2afa4bd05c542ade9c98d043d524c958f11cb93067c768d0bb2d09b8dcc3251932259dd99dc82ef8b09c3bbf4c04f6ec26a002435e1bf84e92911596c9ac71de868a62fa08378f2daefdd5cbf11685eee2f0ffdc24c2df5602944d4eae0c1f5fbf5cd286d7d36c0e6bf256909ddb071bcca600bfd0bcd891ff2e43861f9ff8a4fb9c09fbe992d5f9aa56db6a514e14f78311c9d555c0b3b45af25eda19b8187ac58dc59f6de086a4bc7bba3956c78500a725c854680535c3fbc7527e05e82caf78af8044c8a7eeb71e7cc61351273ddc46e360ce7732870a1a8e6cd6fb2290d3ffc7f4d7ba302ff68016b9d4a92dcc8e7bbbb1e7a1d0c4c3d41101e4c9bf24805e63d574855cf856ae054cd94c0961c0b3eb0bae6251cee246600d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fa01929b549d5791f539e5d4f6b510421a123eb080f862e2c1edd826b8a6ae60","proof":"0cfd22a65898bb9c99110fae69061010368de9a72b450b18f586c4ce02061d7d84ecbaac7952f7b62d786dced8a756fd3c443ea2e3a5cdcdc0c52147eee9501ca8d2ef58e520021762185a6abeebee78d3483267780795e99e8052ef9749a54060905070152786c340d11b8512968178f837f56f251b9b5dca9281f7417b826cfa3ffd4758e6678a9f802b59ae2b03e554d295f1b27fe39fc515cbf0b3601a086df5eac141bb402bc2c87c9ea4faa3f33264235cdf63e587d6be122ad75d8d0f504d4757d4867357f9a4346ef96b126c8f09eac1d277aa0a68200ce837ad8a034464eb962dc11699662095fe568cdeaa9185dfbf1947874adbd751e156ef741f7c820e9bb274ba8a0e7306fa0fd6296dc43c5f0393ce37ae2c7d292fba6a30536ac6d4b8179d5f90c8e806b9e56f7accf681f129b75158371ca5e38b6a0aab53106184b4535cd14816927e4fc8ae49e5ba4a50bff5301b2075876c5fab21856376b02f3a0a7405567e995cbf8af6d1327fea6997841b4ba2efb1f27f1713ef72b622054a030e4c34c4e353beb5c5aedfe1c039adf7ff63b0d205952593846b7078ea2b03efba02cb7935533da3826b571a51d609ed39a4c46615de4bb51d241eb4a029bb6bf8324dd1afb4744382b1e9b6f748229e5dd7aaf221504952428e0d58969f3cdaade05504c215ee5e24ed52f29ba3a7c31aefd52a398995da1a57711892faedb9b85602e6c53debd8717d98a98b9a7ab8932cd566e2c1d8e6dd90124e094237c9e8bbfb8839c43037fd5e3f30cb91100ab08bd588d618a7688928266240783d4fa2aa914177a700691bb21513c1e664a84fabc10f1c47df07f9b0432b08dce4318ef668f3069b95ddce98feb960d724605ec776ea5e4b2ca321730de0db09f7ba2f1b0d9638bf8560c32c812d30f05f0b5189a74742c516532f9e0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fea42141526b3a3287c7112edb8f64cb5b079b108fe03fc236c5f55e0e8f2e09","proof":"b68d5b533edd931777574b496192250a70978d218a880cf87283e0228b0ec4091287d59c515252ac0ab7eec60d7ad00e7af7c3a7ddbcb8cddc1b8f69ee9efa042042a0ab5045eb1be6c5939a1070b632d73e102e1dd5acd19c81a7fcdcc4b3186c4b6bcf30928de0209a8991aa122fce4e93502d8a03395ca31e9ca48b7e1f693d2f0cb5861c6dc19e50bc65f28a32e8bb33867f2358444355435c58f5a0bb06837747661772973e24bf2e517f9f0f6234c5740506d93e68d20ef033caac6d05ac7725967d602162eab8c5956715b1ce61e8b7ed4e84464395f59675a737870e80df48cc2bfbff56c201dc1bb2d2a1e35d7deca5f218f55b86df8d89984e3036341a08b3c1326277ee6aa977b421ea844805139b9e4d38237ad35546d042cc690090081dca97cfe61380d77f302902be74022e6fb6e1691c7d23f1f6c8096478de64ab29e1be67f5def4cea3453f8641ac5ea6bee1b3b12cbfdea402c036a67b7286f9a8aace4f0b2e65f6b4eda40bc21362f7a76332333eec0bed7a0238773beaec8e9a1350a703e5d2c0d3fe0a9bb0e8e548d016d278f407725d4dd80f8b3046ff7803e7d78ec961412327a6f7df40b79d5e79c38f9428438813355cab1b0b70bba92811ea740b9155ac38e8fd164e460164bf20ad59c43769b9d855c3972d06bddaf191b4ef7c355217947242dad9aca1e3893111d0c88cb472da2a4a8c63bc1f6efb89325b1660c1f6c5717eeda62edba5fcba962254bbc1475d42d675583acd63b32a95c0e25e2770f67770d1ca125a0edbabf6fa634ffceb63bc86c42dec084991009aeed04fd3da910e6f255676f6ab02697ada676f9d8a641e7d7b337a6bdebf430958d948ab8a8e90af5e874c30f38acd408ef8271d3ece066bbe0a4c8d2e08049db22aa0ed897d7f60fb95cf54c4fbd75ff142ce076bce51b6ca00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"781dc1706161f70a3d3f23470ae7fb41d860d91c5558d91bbfcf7399ca01424c","proof":"5c78dda865ac72494d3ec0592b02f9f07085fdf5cd59e96ba2ff0ff7b71c0d4af4f054f73d8da76dcbc49ddbab799df5134e1c5bbcb721bab73a700063cd5436642b2cff9da4b103923d0437c5fff096d8f5d81bcdc739c7d510d026a064d5386a510437d9ec5ff98988d4747a050a1e4cdb26f723959bdeb112a6b38add852fe0b47bf70530d4aa638ef6998877b11fc31550d8241eeb9a6931a0c5ff810b0895ccc6c3398f155d88e5b3e5074f3fa070375f7ab7b88409c0abd80c4966bd0cc7fc7f34717d6f9703156e988b0be893b42694251ff65ccadedc3f68c39fd001c808ed60c14fc969f17be48d2313382919681e9a67be72fa148a0e8a0f3ffd4ce21ed38a04cf6bdf11492b271039f7750b85ab1bd5df7767ad0c078e2df45a36d04aae98c73ebcc0275302289ef0c8b0e3a51bfd2cc12aeb0dee08c129e5c7206cd4d89de409916300a212baffb104c3d8977ededaad0047fd8a6efc8d8332618c4e10e6e7006ddf63ad5cb433c346ea64c139139b1f5f0babc6902d90a66003e8058a60c9a0a3f880f5f91fb1df1583a45a8dbccb1d7f09d78234fb9c44663dba386a432b809c681738313d6368dd5041755a2ee741c666bc65a9ac26fd6252780154954044df232263c47034e9254e7515d38a3375ac1b7e783ae586d17c56ce84cb5845854ab94dbd9c48af3a669859322edad76d383c9764e380c137e1019aacc7061901e33a8dc6b4818e664876c245de48484918dd6bd78d1bcea1896d261bbf77056279a8ef61aba01fa8ce9c134ecf0acdde410db013eb96967129332249010f0d802a939de1f278ebff3d920e2c02ff7442f4d0b42cd441d231474d6970b5fa8949d05547b39c40b777d75d92ea3107518bd1cbe84467b0df6ffa0f10684217dfed52efb24b302568a39d84ed99a3f6bc26a8dbcc6eeaaa24719a04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7a6c0fe05d821445a08f43a9168e6b421262d15a590e3e2d4e3d5da754845e2c","proof":"727d53748b847beddfc5c5b71a1fde8b97d4c5d472b7387ae8fe393f1183263e3a660a5087d06fe81151ca2bbeae1b79a9125357380a715b4e3274fbe006f814b47706dd7723d97216492f2d9c8f28511e778cb09aed7bb2b19a3bb430ad1d3b16810e7ce8a4347d73d5130fa91a4c04f2dbf14257da6a7c9ac12e0927e03a6738b24ba0203ce0b568141685e73aa040e99ad6e101a54f4e9f6b1f70dd087f0fbee4b7fdd2429ca4ff120e6fb7beee58c864b582beb719b73a33394a3a5d8b028863db63ba91ef7986dfa0abcfad3bbc95fad715e895604483ee5a7778bc3d09805506d10a59d3b432aa0bcf78560fc89dafadab50db6ed47d4623ee4ec45766e6786aef832806cf92cbebf64ab7a332df347f80e1471bc628dbd0816450e11b24e238919b01f0a4c4ba70037248b25e82dd7b46fd81b4c91c5efe0ea0426b000448710bb5dcf2141029c835ad5c3f669bf0409465d1c4892700785b335a63094e1cacbe99f325832d66df0a79adb6dbaf18bd9854078768b04120b7a982b06a12d61352ad5e8e2972b0af1440c8799200afe0b95cac7c62ed25e388ef1a0b31c848ee719b700083e3edf96aa1a2fbae763b026ee4de11c8e3cddb2089c5eb60a61808d5e221c658e1db69ed9f9f1be7a464aafe3929406e661013660f93535336ec52743cad87452b0ccf1fb262cfd0e9281f6a2aba0e5a58f9995f94bc3f374815e92a05c7e0f3e06d8319f305f78b4b64201affe9a314eee65599507d6f24f8bae446de49d6b695fbcca6b77723dc75cf8e8e31789809de39a2b4cf98be48a6a1d92a66a9f13b16c221f706769d13e3554f4a8ff1d2a5cb37d39dccbc523b6a092505b3abc19d606831e61de7298d0694162750b9703246231c085fa60c0a1ec487df74d272717c3b5ed9e125da752529f23ac44faa3ce9745c49873d970b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9029cd2777dba59943d078a530d4cab66245964ef70414d4bd2a32a839868877","proof":"7483d87824bec6754ec2c9860a33e28c80444938356fa2b6c9c94b8b264145316a47c4ec076938eaa5e1c4f927acafd9e128c5622df1f17f5698521871c016506af050b5dab3f695c8ab185bfa5903e5fd564d3a01b23bf24caf28ff8c361e5fb481e99ac8e34400d9fe1bae06c04d332ffff577cb20f01ae206e653d0d59b400aa5be408eb99f238583fddfbe94d90241d4804fad10a92a6292423d0501fb03fcb980536df3640d53ea255856be08449fc884a5c3b161c7532852603968d206de5abbf026d99c76d5cdd00decd0678ac111c49f650fb25099c39897d66ec40e08767d3950a797f164839f4ecc07392ae1909051bf1b85c5e432733b6a068057822085356b361a8afe55293dd3f666fbd8543e8bbf233ecbb784055db46abd05b63e9dab7ee5bc1e2769aff5091b6071032079aa416282352e397bb2221457666260cc6416f568810e443ad387f12feecc9f20f089962781c46b596734f8e942480f653acdf01b9a6418a26dae803a8cdd855d42db27dd250c5bec8085b1126e30daf216fe3682ae24dedf1ee1fe5202716265152e2a69676e11df2477580c3c8ee4258f96396f2cb4144f560af0fdb3dc464c42546d7004d13a24cdb8f5900de0d44694fe9f74eefbb35ee9be0f9ca19d69d2470ce2371a1cd90a37e319b42cc432007aa0309b0260aa0f0bcd5980f081066a4cbb25828ba9b1cc260bfd817fee101244d3fdd976bee39b77be6f0558ff9f739ebecb0cc8492ac4afc8527c05942902bc0788fd84dd65b1797b49619eae0c2e6919b48f16f57d128c32ff0f0c1ce471dffecde2c5453e2e76e785921d2520aca44df513059b2b9da9f56df648dd06e10493b7c35368a31a8460d4e81924dd1c1c1e02653682ef31548c6f850c11063098b5bcce980f50f11373a21dd992c552315afd754976af34a7d2cb5d06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"94ac5807af359691594632cd0df2d29bdb40e0fab4aca8a203c674decda8e376","proof":"085fdb0467a69c5c8c5a7bc772e91f9330e7b786a89dc4241947e713df91a005f0111265007c96ef327d2ff711ce87e4440277b1133ac238f1c7c1640012275728a93f4c09d678b537a0c165ecab7ca175ca34b4f3d609f23c7f5b7117f54727ee2e60b9b1b8904750edb1b0fffaf894e709f96ac6738e7964e91017914b7d3c30c4cac0da54682690c5980daaea2a592826d102f9bb55f60996e5b3beabf906f14c32c3731f5e3f2038dd54d0a62d0bc1538323d24fdbaab63e35aa16c254047283619ff4a988b38b6f1a712bf582d89daff182ff54a9c876291b938d917b094067272101eb57427040fbf6b6f93f2d11f6b1044ca50e6a4e8600b38806ce616c9d96030d783bf984209326c0d13bf8bd4dc8c13d1367ac3370eb6b39fef836fc7af559f573967d104ec6555fb602e666567f213142051c82f3dd946cba865f947ca57f9feec539b5445628168b347f72eecb8421a9dc1ed888108e0c926c67b8a9b706b56b5025de7a1fa694ddb63cd4fa4bb31b4bbe13367d93ca677db24c4e644cff6dd3fb1e2797960169565a946d48b832d2afb67255716920946b854f3a29043aaa03c5a3d25a53da9a7de5b83741e471a808f21a45381d185635aa67aa116de42ce8b766116e0a3e21d73e584f748c7fe40de3ad35c333491b8ed471e0c2e120a66492d30c353ed5573b94fe1498299cec1000ea4d7d78d467b0330736c48a168b38d22c47856a8e6d610fbcb16034cf40bfb0410a92c338f68b1867ae9f5e97f3ed7e5ca1e299147106c260e477a7a60518b678685be51be9ed5c4ba8fa480bd1228cb96115b0600ddadeba3fabac25e34bb539ee6cda32c3ff431022994e21b58aa44a931dcea188c03579e7690bdefb071099ff24dd56ae50d00afdeaeba5395f1ef9c44790af0aafc267f1831db9146310b0bbb95c6bef876109"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5e2a18508860e87590a89afedb90f7d786305e8683a4f850f5400f9e00c0f152","proof":"c0a0e8a946e7e4eccd978d79f1bd3345ff4b187645f7c4611477e95999bfa479da9755fd30505abeb4a41729ef496f211e720379acfaaee20f0c8706f1ff717afc673374a796a7cfa704e443b716c0a97a1335f23de1c48f0ae89b44ba152a3e32e8c4ac2d0a418c77346f02794c4e4092ce82a45043cd081b2dd617d723d633dfa22fd6ed9ab46203e7b3eb2ac352ab39259192d0560367d9d6e1094b82a602073c502cbee3f316bd33b819ed3eb156f791341a75bb7c9a7e28ca7d88a41703608f9c81b023c6b5c69609e28003b6f823078eb66105afbb86ac38247038b4099c03f29d1db440cb3f5aa5ddd56f53d9c42f4ce2c4a9e218e80dd755a8205a30b8a7e528ac2c1c8f1dc5caf5a816da4b9a195aab99025a9e04e0c93b797698487ccf6947346b052d832241289c795ba7e39978827fbdd8e1b099a34ccc69362004c7f416acf89e5ec8c2718d59ba69e039ca5130e940a3b866e3425b57deb1447022c2916bea4bca97a1b43f4e86f5cd768266099c61ba84cd7972db592aba61a81cbf605dd5cff80df3dbf3514e4f7b57a25de17f1972eb0513a0f3f221ea0dba657940ac6f5c8faef2704340658a1e546a4d7fbd6b060035cf2d2a58005b175062a0fa65107bf529f7a660b2e240d6c10fa7d854c819e9355f152ba547e84ae41af6c92cac1d15478d2b1e0545ba6072c5d58e2f49aa5349b949d7cb7cce09065f58cb4e3cb4e78586ab082c970c00d385dc53f75b9a30cf9c306595103e0f56f9d3d0beb5729d63b6b8276dd8124bbce185d4e50fdcd68506afa8429f052388b784fb777430d18f02c2807d12a38e4ea8059878b52f2bf86ef98540d7831b5171926639e178c619e37eb7e9471e57402c843cf8fc1ef1c0490e3c615ae40e39216c95cea511634d7d79376446bee924678a5489038d5aeede30d40cde1b04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"66665be27a7508ebe25f3f4459c1e36512112015a594a84489df33d42cc46516","proof":"daa851b541ff49c5b7a28e19cb5fed71d3d8ad775e07c44cb109b70652329a66c4417bc829ee5fcd443296e0d7dac06d8a1aacd404ed091744c47fd3baaaed1c40be4a85f7b5dd2491238306fa5f0d234ed8d805f56c11c7d66fec4312217b37c86c83e43cb7ebd1bf7637d22bdff6446f2e684240f51c8564fba5da4815ca426f610007b1d0adf2e389a404daa3ac89606414fa653fe83e1105eedd28d9f402fab84af5166c99db82aea9dbb3d312382ee90c11ffb3d2b431b2e49dac87f704962795d9353e1786bb0b6e77db6fb504f60cac14a7c2e686d5d3ff0fec6bed0c1c383faf192801d6a31a937bd866218bc82c30871a973106cf58d71accb9df43f66d78eb71b48e11b272201890187fec197c7276e982222d966a97d449ab4a2c8e36e578219ca3a60cc8201d45b7a8103815cfc9c1df5ce19c04646bdf32f916f48b9861e908b10aa82e6f67ed53bf8e45ef956dcaea248b5fc10b7e62ccc06c08e80bfc712d8bf540b1c2bd57bc4b294d92e56092a1750882ae15a45f07c260a2b82ac4830271b6a16988f112412289b1e2e50d388ffd00e94f02e3ea035a6674da135b3d4e39e5b8c4b510266737e72457ff6c29fd003c20872b9068611408be722a23aab0496fdc35de818a084b106e756fd52bc4d3c97db36182efa97d6e6a95fd059161bfc2e6a3b0c12230b4c017545e93dfb89865ef3598edcc54ff05f8e430f6b8f0198de7abf07b12ed209c140d37fcb36891866018d5b933689c37f42e911a060cae7551a3316486e5769dcc07c5f341e3df7f40d31af20a5aa56b8c3b943ab1e542d52ecc3e6f4ea8e7ccc08c2078a81602aff0c08dcfe67a7f2dff2cea781b68b5faad4ea44c30bba6ce054040c79899f6e58111e8e085aad001149952bec46a90f667c0c253229a22a3a3db64ce57ebebe7be21bce569e08909"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2e3bd866b81aa92958c142cd11944b32894159f26544ada3d2704c301f36ef6a","proof":"d6aa8822a21b7db22a4069d4f4d3adbb23a30137f010a746fe630ee7a17677786a6a869c1f04cf632a962777bdd8d544c7c3f897bb536fe1c464ab064513ef7c2c8d4c5b3eb2268184fb5a8b0d5909f462242d3ce3d8d734b5e63d399fbee57642984e2f75c88a8f1ae4f8528f82cc7765df6de4475cca407d51bca258ea295373b8632c62895a16d54080a78650915e8a8b6dd1d0047da4ad1b2b4ef10073066076e5cb5403224d831bd8d92d886a0c4ee3953f158581227faa31bf4d190a0b4976d254f74e474b2473f8c0cec3401030ca552664631f3d612f3741d494ae077e85852b1cfbab3f3a2566e6221f838bccc460302c71600e7588043ab4fc73592e102bf59d31af8194d521269e633329886d9e1532d2c9f1a8e79d88acb2851a7678c4d0536a0c0a7451d051e581a4f7d02aec2a816aceaeda4a2a811389eb6bd8d4135f98868eac379b2287b3c596175e2d6f3e05d64b39a76d1c70edbded73e6ae7358b62c0cfc6cedd777de8ee72b338eea437bda0cfcbccd594fd61746482c7fe3228c5f7db8d9b7642f1dc6e87d6ebc02a90ea0e2785915a3c3956ee772122e2baf60139a267955e3a4c8ff40fcf1c19c1de00b4e4c0ef14c8b277bb87f140d6beb0cb7b11a9ed2d0a6b82907af2f413daa7cf98d160807f05ff097706be4dcc1fc470af1f25b7e37cc03a37300a8d08080924dc5296bffe106261613298c12677e035c467c85fdcd9ce75fbe0408597ba6b46f034f3fa9231936627627cee12035eb49f4c4f9a4dd8ad4bd29e3efa1b7f0977bdb1939b0eb99eefbad12f646e7c00efd92cafd338b31fdd426f26c544be897f83cdb7e63800a185eab44374b168824153b753b8a8bd5b410b8f3c4038cc4fdefb90625cdac4a340f3e07e18c3a0315f9911aa30143c888df04f6377f7dbf1b7533d3599c716aca3c7d0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"440e14d23f8f7d0613c1150637e1547af6156e2614b0c28b77c143152f92220d","proof":"e2005f68e2d7df605d64aa8506a26c5dc510f2dc340145378763103d3fa9367eaa196c7f1a7027cc1dfd660d6b6a6a474e1db6f5e63fe53cf41a0b2653295b059c59959464cc0269d8ead948aa73eaaf891df029f056e352a0705e4dbf882f0e3e2fed4014004fa4447738ab9be0e08ce88cf18c41aa491c047bbe370e690143aa48dfabdee1b3718f1ea86e5460eab45af171f571dc668f454f8a981c8d050abd5a85b26eec0855b0449fd8940bc96e9426eba3ac138fc1a2f994e3c9711a0eedcb639bd7ae8c153d683f7570839b14d6c6bcab81840a01e844bde9c1570c01f67724c9ee954e7de22a6a593b86f35cd6c4393cca3a5c63f324991e2b344a6fd0185111d7d3b7cb875c8d23b802d97d2adb382f2eb73c80fcc3fef6a3f5e27aa2c15a0e065acb6e68c7e50b04af5fe8aa1f3fd86fb9671f38dfdc0aaeafad66e29360cf0542182160be2e80542aec024feccd5c20ef0967440384a732babf6b1416c7a94df1827897f37f5971e2447acc78cc686ac7400f9ec85301438f6d63aa359de72e756828baa78e36d16915ab27903373cf12a89760538204d3c4fc5a8a0d61851d2b00fea24b483c986a271325e5bd5a76bddf116458872f94ea2c65faebc6b53e37797fd9ff56a600558cbd0ad5cd5870340c3b6421a08ad186f23b1a9443062dd0cbdf0a39bd2c9df38c44b54b3ebaec7ab10ecd5894c315814a7d66326d08a39f909167d47387c7e1cb87cfdf33e366be12cdcdd6ebaddf16f1089adf4361d1bb446b6542c58d32bdf56f63ba4b1429a7f695304bec724f4a841e52570450988f1689886268d7b77453f65137f74896375ee0c8621e33e32c8d1ef79454edebb27d196fc8695e8ac57e8a7fb637b44285f050fde582550bf6070b4ae6f621a54f9e77c2a54cb8133e6d9901ac9494cdfbce2a7d993d1023d19407"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1e9f0e0658e96bae5df1f1d0b00d784239e887a5ebfdba9735e9e61d8e386308","proof":"da718be6f5a28c34529275496794f6479757c946cb71e7066f71982066343c6982567b90632902aad307e778d01967fc4949c58ba43ca463c25f160f3800cf692e86d696e34afcd0feafdf0bc05a8100f2ab049e9661afcafa9bd3e48f5c8d3a841e0535d19eeb6efd3d09930cd7148344f5a0def5a198191cd17ae3882ee2569069e32f5b6f9e1bc19ffa59c41c21b5f35a60847fa514fd62c5a3fd5da49f0706794e23129a48386b571fd9bf25337518e23d4bfe0d794caa01935b0184ad015515659f0531190eaba64853b9adcfd334e2e2664cb97a5c2853c7a011f9df0f564352fa919279c51b3aa8dc21c65921139b941d4ca71418a7ecc26e9ca1ed07e88b29c248b9c75f84d9d7fd661c9930b91590c71c5360fd45ec0a071a7b0473e8897212bc45af09b23dc599d77a78149bf58f6d880f1de1672b631a3557c90058b06049bae8a142cc9ae726ab45f97a4597d1ae3a65cea211943a0368c6195bd2048ad57faaef9e441eb2fcced2d7baa42a12d4d664f72b389876906ce27724ee52dcd529d78c919024434efa1e28ea0595edf9d00c15a28c1bdf0140e1a454ba00aa4c4fa10ea5b5118ce8fa9d70df768da426aa9376f89ed67627ee142a3efe5772b9a43d5fdea45b1832525c0aea8d522d76cecd21203db026078a4cc702c62d3a1491753e6dace604a1c5421e1772691b38f9f15b0cfaf3f12d9ba78653683629084cf63ae876cb8ed1e905c2f67b353096377d1645dd827ce4dba86660c804d309b05992cf016b66c2be746a77fb613c1a1a14c5505cfe7b4ea99ade6bf0f66fe4e8d81d61a7deac78346edf21b065924d4e76de4301eb7244950df97fee3f797560f511218eed1acbcb0874aeb0eebc3487d172bef2325fb14289110581c968168b787b7f648b7ac2d5a5149199adbc572fecad6b07a6c5997efb180f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f6ab08825c9a141883ceadd8738da3c04a140af678ce6fd6d6ed847c2df95c2a","proof":"3e754459b4a086a2910ace1a5da39ea2909e262d4f334c91764eead113023c33b8e242c5cec40b19ab91cf04d6cbade6cf0a6228557f0003261d869676f9ec1fbee459e0b2cce84dbc6a9b35062f7a770da2f5b7e9f1884b538c6fe278d4ae3f763bf49e16f9d7551d0263ca8f254b532821560fbec1efe6b3480ec8e0e45257a7052b2b2cc236032da7fd8636418ea6f68f060e1a116a2a99b54ea8644ba404c87225c9011c29a69295eb3c701338d2a16c5e93a2fb10cfe43baacd4c5fd405c723ed20d18f966022637e39a97fae2614c1ec2d0a35261b76fd07a98e3fac06fe193ebbc4c81542e017fdfad952cf1f011df493b7b6ab1d750d01f10f16e66ad000e1474cfb8bb99a95dbd886ac85a736406b2d1260f27e23463f69ca86a338de2400d18ee15f7a9d57ce83dc0be3f3b71a18d1ebab96cfeab9fcec83b578475a69a9585898b19014e2815a90f7521ba9659f0a0297464ea15105752d1da51bee47fe3fe6c074e74b5e5658b5adfa4d7e5794a14c399a2a955a8d6770535436947144df027ebe653d2630ef107347dd49173cc7df11ea5d5a708f8e55a2b10750b999727ad5b5455fc5dc9c7bda1d012a43a800741c9e243494509535fdbe57e2c2666d1865ffb151bb4b419f14fd0efbdd098cd2372109fbbd10cb538e1410f6cbf39373626f28351a9ea2688f04ebfef23dfd1a430ea64f4625ec90b4ef1dd62c817dd44a2bbeb105a4a2cf885c2249f55c3765cb2a0c71a52e7f5573a23252c8a5c6887dea2311337cbbb24910ad90ca88a295673d08555d66e03e0bb37038ea43aae9763ebb3484ababfd7623e98ea83ca9e8ee5625a71a837be85d3933bc9cc93017427aa7432220fcd7b936850ca0bb0200f7b34dae63161e3e2b72037f427b15f59a2d3d92748efe7eb0b55261ef969b9f7bc3a864b530a63eb38b02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"da67b02a189734f30147c26eb34f145cef98752d53614be6297f42bc00e8e969","proof":"34c313c69d3e08683301755fc4bf82d6025966a2ab808b16cacb1afccf5c9b7018408fae54d3d79aba4715ec2c8f2113e45ec1c521061136ab873ad5423fb8107cebfd61bc24fce8345a4242228bd67d5b413982dfb7a340de8543201996a670f248bbc7b947a3c37b1bb30f7d83f071d9413df99d590c933df4434828a0d86b4c47fafb18fad08b1444203853e3c9732f2232cd522630cc366ca2749e165e0180115223a92f0801061f2af1ac23d1a36265b467994cf1bc54712635fe9d1904082b677e21cbecb1d72def733a571daefa914e34b3211cdcc3817bb907e53c013686fdce6040db0368049c1b8e7f3f29571be105aad73df5e8dd03fb9163ec1c7ab781c2548f02bb696986b2765f05d723aa07935bb90f645146eded7a8d4d48f67ee0f814c51978b642c96116b66e4ceea9d42419554920e55efd895d39b039be2d986b8044ec9c2e5ba3acba6f629e3c369e3fad964e445985690f24d0af6d64713280c484793a061130cdb3879729955c16a1422684de3db44af67e6c854244820d0816ea8b6d8c0d0df6799336f240348991aba38de4337177352eb4026b663f7e8ade640efc861aee9360208391f98ef3235a31ca004c4a104ee3ca641d663640b13543a3ec1e1fe66343cc66c9ecf3cfdcc58e76c7c3234f6b0f87411dde3a4a694c83b9e8f582e8ddaf5d32a2e5420a0e679220b6a799b73be156285a3254c69b4b85f319551265578f1a971a9e183cead3635e9ef07f3ba3977bbe4958d4d7b54a52e99ad0ea8b6ce35dae65cf473c49f8f16ff2390ad48a29e22d3030581ebd83d1e0cf035ad7d6eb886985fb8bb39d4f4b98353bf76333ce39dd1c31c05efd3edb8e42208182e3aac5a6764f467d6ae59a96654e2a9c1d6492590a20606886dc9da2f007901aac9632db2f2845fe0233eef44530a7c3cd63c7920f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aace4e91d7aad9577f176ef9c0bd15e4d176213a1078a5d35a14b89c01a7617b","proof":"b2620f559c11bdc63fcad47dd96b917535e60d6eb65a6149af8ea4ea7ebcd13d4eb57b1a1d05c1d0b91b10c547e83f6312ac6b70eab3edc9651817d6138dbe1ffef0d744720b474e962e7166c7267d2a4dc23d714a1c03d954a291cd1bd3d1489e3d370527803e23c31b198e0b98a81508618fcb7b4293f120d4a79b0814096acc7f4562c3df47e2d6bd8779fc2dba29c5d439d5f234470118d34944d741c605b807ea4168b1833aea7bd3c05db256ca630dd5951484045f469dc9286217200ba7cda7df53dc3292c5839900de5425a9848269e3cdfd627df473c89d5f718306f01ffd7f0eaee60df56a60ef4ef3a147e17e36fe23cf4fa52141e16a4b78bc4dbcf36950e870f11a01b8bd7b1abcc495883af203c4edafe15f68138d3747c20f7019f0c03b1906b360db106fc4bf570dcbaf02dee149108e96e62bd539754957bcec2071c5617260d9214abe6a0be030a204c5d38ff74d52c58b56d68388b437f6de56b32185aa397c202b29439805deb3aa87f86253aabf56d5d42094427a7c261c7d83de04c5fe478ebe3d504b524adb0f2f917122da893d538b1e8e9cec1c04d80e9cb6b934eb80ac5af58dc938c59097378346587d98d752d622b9d59c46b2533d1a35417478b63044511049937233c716e097f115d70ff85535607dc70662451a61a8ab1640685f67a02b70a0beadc59ff6f155a4a9da7ab94acb874a0b447efb666e7018682738045d605bc50f721d1408bd8fe2fee728e1de4f3b6e533a5a34c2e1b27d7abe8536e0e795d2c207050782ee0e0418d4059c8b8d83652c7e38276739b055ad9b7492fc488660701b31dfdc16a78324376fe91adc2b4516f79e61a37c957e3d1d0f106c54f7366f47c2843ac8406a8423e4b69099a2ac0d0998c2df227485757b41dd4f0f6551c8d080501c3a2c9250a8e018fe93303b0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f4313d2f82b87d3bc77a045d0bdae37f11a97c213da72422486d9a5dad9e3a77","proof":"6e4f2eb46c9d6c90112205b6032a27ad09e9dad56eeaabf3587d64d5abd68303cc1879e662f88e31dd8eb77c967c665ba732041f772b4351ba2d8bff1bab6d2d66f8073cd4f0583caa176d78443955b2cd49686320fccf4cadbbcab96255594b128089ad037cf2a868a5e72e499016798ae0bec4e4a2ebcb03fc6871f87dbc079a6063f73ab55e719a07fc36844f1b531c5c7a9bd718153afc37c48a551277066b03eb5451f8ea025d16fc3f1a7f15033e1a27e826b6af532cf80447c2f99a0f83b7535740760ff4403fbad6afcae9c58e8c9022e90c4cad071a2e92b8257f0be8aff43855175ae1df819f3cbaf1df36055beca1c65a24833e6e2ac5193ab343144b27516dc771306fa8e89ee155b31762ab4b8b201f616e0b09ee530b76560212b44ccb6bb7eb57f8b35a65520ee8537fdedb8defbd61bbe297d9ec96485a45a2f93a7f206d085ead4aa25f15232a7601c093b109b9f3ab8cd7c972cfea7741f4d615d89a92eb53592b7dd3f2a40f846dc511dbbfffc5c6eff02b5cfa985d54145ae33d9d6f2b3f77c9c3a0bf1bea399177b3d92a643a039127ffa9b25ceb203604ae2bfe099fba9393494655dea62e404b863a504c27c5bc2fd87d307dd86c3c0e446cfa39b1bd4af53c331310e69ff719bb57bbb8a805298509ef4f210223eeda41465089554771dc72f10ca8a6ff1befd53d7be7ed404f7873155dc5121ab412171c537695390b53a9a7f78487e17104ae258fe947184db416eecf217e0686e330a82e60900451dfd90eeaea49834336cad2c19dff59d22a9bfcbce12464a8c2a578c0212f2eb4367d4addcb239b641fb19a558b892da8b949dd4b649914039a58df094843b1fd664cbd9391d30747aa02d92446743893015cd25ea3eb02040ad0f5089b452755ab883b3cf839e99d4844e02c1d63dcafd4a0478efe9f0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7e3fb60d3519e7fdc31335f640fff9a7d0b05428e1786a299bbc4692a6ba1815","proof":"34ef95861a938c0ca930dda9e1dc5ea160db4464631498363f87eb058de1f702e8d0b9a489061e37f339b78c6369450cb4f8705a268797c0c8922cff9c360368e6d9defdf31c90e56ee6b8e51f6330f272764655eff6dffae5334ba3a7724f7c94a6cf29e91bd7bc4a90072589493fc29b3d9cb06aba7b2aaa6a38a035bf0a4b37e559d71ad55570de077321a7a8e873bf276763eb7c23beed489a01abbd51048d3174f819bba0c49cfc0e9337f8460f36ad99b990154d5e94977cc7c772b1044bc134bb71b6c58f4f27d5e8b3ca205fd674947928b7d2df5c95ca23a08f0b0c12c944b625fd52bc45c206f7ec75bca0a6591f9558360635447616f5986f5406927e0c74356645fbbf73c7acacfabddd59e19f5ce0b30b71cade6c3259e7de3d0233a713380b4eda3c473d701129f6872a7e033439544fae44b3b2654f45b970e2d162014a6153ec6e47ac393353a83e8575a018befd1aa184adbe47dc0d420528e24206b20eb478d263b8d1da9b2f543b358c68c8519ed79f262cc867855012e0fef39238fbba1d80f48e57c3f02687ec8515b9e2955089d908ce7276356c1f6898bd23288de3fde954d82e08f0d661ae65700fbaf149a4ef4107b43aa95668ec3e571aff8b215f2e94c46e8eaafec11cc60e23d8049d27b671e77afe0bff2c44af4f66c7c0e23c021feb20fac1876cc6f3da2e04489dccd16b0384ff1f16697e3c809375bdf97779b09dcda931dbdb6fa631d35f2d9e8a263ba959a70fae02f839847d8838cf895572a1694ec5bc4d3360bf5a4fd5049594f5edd96aa2275fbc34908ee51b957d7b86c6162c1bbeebface52832b81c8514ec5562ca34fc805ce80af12dc75a59c530fb0a463273461fc67f623285e97ae59b74c7256da350dc8652257855fecc54981a70d01bd3ac539a0fa20481b5a9a8b70460fe5b37f0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0875b24ae5e09cb4d6b828d658e3ccfe1eb0891d6b38eaded971037c070b1f00","proof":"509d2b2464fbc4bf2c2441a3998faa59fa20165ffd663b588fad4958bf841566242fd58e81962658ea9e2f18fe15594e3d5e01ad97ae11810a320bc7bce023024418667e859cb896e99ad29b47eebaf60f2116a89fe1264d144ade0df57aff789abf7759e8223a697427527ceb2b3af1ff4f0b33b16f8d77ea9653aea3b94d2a12bd89184da035c1e94149c86d2fb6c8d5172c3f3bb6929ef2a77296366bed04e4e489c077b5ab996d8914e935f909d4f2a5af0cbcd1e2033966c2e0fc0e3a0305dc0b7bad08cdfd2c7f4406df040c60222338dd6ef807e6ec463755571bd20042dd85b4e9e0555fb941943178bbb5e526ec4dd6d7f3c0fed09ab0296d73d70580fd702b8d151a81344b695928243b0a6c06d533f1141d4501ecc90f6793577e24cdaf42ceba40ea91374743181625a9f013be87a8867ee46b9f92c04e3c1034804b2453411d002861ec14de761ea5b8a74d2b600e21cfca8ee905e6d6e983374ac19c0504ec4bf88d0cd40680e402e555a6bafe8b22f12ddf48cfe1714aac48e8f591f57ee1511401c29d237c68ce5d3982d2b58b3046ec997a4a37df008a02b0d3170498fd21a3274b25dcee4bc179ccfe2507bb058357556d1cc1d3bcf60c166df621a18505144499fa283805a68c1818f615eea8086585c5fadd2b2b580de856794c6eee7fccda3014a50a2c91c3b27014c08156e42b8313323ff42d21637ad1724c83afb80a32675c209b9d0bda54b05e1adb75ae536746c1f87b58bc149c9b6f8a185ac39f0ae6c63531e230cd94c02b9af62a8be53bc4e94ed0a7bb39cc4e750c3da1ceebe17209d0c447c9a5b26e80d42cc67d6e1b11980242c27368bbeb53d2c36b38a5c8d4ce60e5c5f811702c09c2c38ad6874b5f2fbeabfd0804260aeac6b6c64ed74b9d1c17abaab00c7b5162c984e94ebfead5e9b442619602"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"86fa2e1f6cfdf640e0e235c278644380cfef01dc4bd12ba3b38a9d49a2ac2862","proof":"14d0e271181ee8e02089103e36f9028f22d3c74d63a89ab2e9d99b1c78744f3bce678fcd926e798d0f7179168a59f4dfaa0da2c58ea43429db68f29fb903f853b29eae8624d8870de0aca7089ab5ade5e43f916b18a0f208071aebf04ade3e6ae0b08ea02908eefce4e815cb0d926c6f60cd8d5fc20ad3dead5031692b3b8d5017102cdb470cdb5a787f2f7dab96439eec171dbda3ee189a4bee5d84998dac0b72b95927b2e37a732e408f5b79a3e76af7c9b250e40650d0e96232dfdf20a80cdd99e1ed3c3f40f143ae931beb5e7bc9a3362b097e07bd6e2c8fed4a38853b0efc77c829a6507367358aadc6cdb1c5785121383ee307c6eb59282854ff26fb3782986f4bbbad3d28c9b3cc99c2477839123f9febf10f347a18d5270c3b3fb478fc96646c92e3bbe8c7f1741a595632d8162d7f69c77cdc0299a6d3ce9917016f28ae84a79cba2e6e071f74bb9c9074180ede16073f77045350d3413a6b65e922705bfdfd9b76593318271412e5c9c7f4c373fbc4959c93b08a64393a249a4a0d02e9bcacd1bedfc2e4a1013d7c341886dc33b39008b7e27e695e5f27c2281121746d813b6671c5dcfd9a85ddaa81a7911c6e7ac95780f87e2c3dd42720c6b34384561fc9b3ba0095199ed845bba3a90d565b943a4afa8e4bfb902edbd1e65805a095b0053eb82800e56d9201dcab4fc64f6e60fb606a7dc72025e603a1126649fc616aa28af08c25cc24f218756be6c55e74c579ab558b2b30e207a53e5ebc06e835f934073bdf3dd34bdf40a1859ed1ac56cbb27258d0ca1d8e10b803506d6a1cce428d2626200f9adadf338bf1211d7ef44d1a252df5d037eff17ba4d63434577cf848b1f81008fac432780b75a82a81cb82a182f736c0bc142ee807eb6502e23293c612128dadf5a96c541222d66828a95a6b5a0a6a90711c6a84de15880f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"24bb40862c36df86446c022f17bf420b0298b3ab045f1f0ce1712d7cd0adfd0f","proof":"869ab406629f70fad64806e8cfb983f5ffcb86dc33a5b6fd9285bc6ceb4d3769def4f7880477d8048cb795a5dbb19f8b458478a89fe5916185bf861c54603b06e2d991cab7cb0de122938fa5dd11d054fc45fed67828f91fde5e100d4409bc1bc4ef851089235b01144db2ed2e56dcbd384e5358f04ccca42c82ff56831ae525ec1be189a1350275e3ab5ebe61b1ed202668e5d1d0f6d5ae861495f509feab0a4b5520aef469dbfe603a3047ada10607d22bbe62f01c83fb679af30b55b72c01c7151c5ce832ddcab8efd03f9ca580408ac3b91feba82238e37b957495d22901d85a6ba14085da6ff3205d2bbb15c254062b91657e6b581337b2ab15a72a297c4c34ee4da15264c07bc00d07f7d372cf66c44eb63cb8238f08290c404347074ad64f0db4dcf94fe4ffecbe20e35574d3bc6fe523657efba46a864339bd8f635b5cc89de3dac631eb85c89f6b489b2403e19554e71ec4d1236dcacbe0ab4d6565660ba26e8a3c16c7ecae1820b3446e5fd1b37d3cfb0c12df3c4d796865e8f40d8e2ad3ef62e160632b71070fe70f76a3cfad05e8b01f3a8545ee2e6c7a9c790ffae29c10f03ac1ce071c0dccae582d9e3ec7900e4fa6368aa2e84826d2323413380cb0083b0fa578e99dd7454c2d33c680301e6f2353aea65b128741974ca13a52b3c52c8ccaefcf9e150b4fd6d647a2294baaaa4e82e3d9517d10800e4d92781ce83b3d4ea75224676075a629cc7d3ad176fba0a40d806eb2948d7aadd1842c00fb8641030cef4f5c496cec6b6e5d4d1d408795a06d508421550d88800c297e0c8b9d078aa1b266ff85c9bd12f264939f0478f728a647999fad9fc051ad5b6beb84c3e5553226934c4635a4d5ab12b145910fc7a925bec200bfb763c00d060ba1b023c4e0c3526bbfd1aee865fa6ed7f0327182803daf0015a6a61dc7962b03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c4a1afbac5f07c1463844d32bf4fd4569c03cfc12711246eb3a600180c1de305","proof":"b0ab74c7a0a35985372bf906c2db50daf1a3fd55aeca08b50a483cda0cda493172cfeffef93533e0fd25b7f73b6acc463b03ac000ffbb6164d85655812b915557a5c0c3b5efd877d49b6090ece010e38cc2bc04489a164dab4c54ff642c92c0142f0756090db67bb761d985a31e0a55f7fa1fc00121799ec90514535452a8839d7782faf5e2c1636339cc01b463238aa6e4a6859143d9510141092216d5619000c68c5884c5d5f863ff643ea0b31c25a75f3f3e9c1ca4a6e229b79b1597db709718e09250a2c27192e66b1e409ac48460ad75fd21318276b30f502cc512d500d529408a06483ccf13e1a1160d06c02510bc0d457051fd6f077dd117c9ab00e6ac68d70ca98f659767057989778adfd328caa2fdc2e35c951e9f9b9d1fed53f469889e9be4d4283eb000f5a41e673ea8089b1e9635ac7d7bc7acb89d6c28a642028e28853ed9fb312e2096b74c797807da13763d0353a2ab4a2c4440ba4431a683cc8f8e4f0dd48b690bc8e2bedfbf4ea80c39961e90d00fd0b4b889647d34c44361a2b38a54021eb65157eede8c5106fee7725e1b5847ea87eb33a257f80c8734415a17c6967556085877d9a55cb2d2dba7db40dddc42fb3a0a9fca2a7835a676c2abf97c92dec3c3b4c2defc4264b55c42491c19a280c45f1e38510fe44966e8caf98bf85033b2a64872a59880554abb6ca139e5c4390b9e16d7e50723f74718063820b542fe7fc8b854d6801404aa62c88cef873733df752290624bb6c184fea9eb5f633c66e6047b36e27c9673859b82852e2e7d9710b5bd86393f1f8e96334204250f74842c646a385c42f9c665ba9cca29c9854102bfb4db09473e5f861823323624d6dacf9938cfacb78286ba86345d0c1218b691c6d7dac879433ca024edda78f5dae338ac1c440168cae40437f07ccdcf73add42b765914466d41e09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d221d6b5602e4ef1edb391a2b4099759375f7cb75f2052bb642099d7ec7e9469","proof":"501b607f0c1bff7e57ac9f5a4652df58a50f3dc4ca6bb22c44f0615637a7c220f666121fe37762fcf68cd0aad54a97455ee36cdc8b046b4a4ac9433f040fbd1e027c97ea68803ffc354ac48486bf5f2f9cac2fe590ac358062b45f5c0d91a4558620d14371ead1e0ba623467b5b6e3c2c3dafabc3b3993106ec04f25d31f983cd9a2356d9b6656111387dcb35186a498f128da0769d62ecfa3cd0f29554491021161a2ce69c3f91d87f7a433e8abf4e108eef786fef2fa73e3d65ebc3021010c63cfb99eeec354e9ecfa7f68326e00bd9bf8cbade390329200152d46290e0d02bc6d8e8917369f457908c8a4067bfbb4d2f28b71f0fba73a31745ce20f0c9829ee4eeeabbbc0a89042a341c81e62ec0005cf5881f1972c2154c50d6b785b0d52a69aae02a193150d0e9e6b6428929207027469b7211b05a100c2e0c94af4cf790002051fa042b4b9f80b9ef2cc2af3db328f163133c4637e90c2be8110698353663d325e18981cbdf86fe17dd06c5a9d6d02774ce88258db0669d59363714c7d9a685784af2299c212b03bc9d6fede7df764124a75ae5a933dea9a6db726002de299cced9db7e4b2c8601708558ab755ea40b415eab205ab140e18b0684d2922546030b67b7f84b9a9c18cef56937825170753fa8d8e3a19d21919b0f19faa33085dea09ab2c517dac0fb62cf8f12d8a116164b563d8927287c9f5b5c5c9d45c1086a9992a3a3c289cc0411b86024f318967054c6b01ebf84031b9ac9e145c77388ca2935d3f40c42b06b8387b044d80e3b94fa9e6ca9b4c4f939f23f3f2dd2244b3ccf95394fe95f1accbb63feb5022977aa64cbe7ed8727918be731a99622d33211d0bb12c67ff03e241ec9016fafb2f02c64cd02ecb187bf0f25f4ea39c0ca074b1a58bcaa8537d28aab6d5d927409b838739512bcad2800ccd551910a401"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f2038057bae2ec19e3ce8a632bdf7a179e0447ce5584998950b1561212b07178","proof":"9afcdd27b541f19276c075a95bfb1c61c5742585de74e9607afd4b6bb0fd5f7a26229b09e6aef181fff67f2036cb6d1f772798c0c9f7996460d2fa9c25656360ecfe3951ea4d88b7d03e6b1156934ae3eb9f7b4a5dc3c7855b57ff89109352170a69950818033d1b7d9673e0fea419411174844cfba274efaa6382079d305f1a514587e5953798b4b6bcba1d4fd844b7d4c76dd20d2f2f496bd7a763e25f750be0172f7c1f42cb7d0a7261b2c868c198c752087b1d8c267d3eb00d5b1ab3960af26e215014ddd729768c6f9c738b93500ab8cf847082230db42e0b4baefcfa02387c5b1684c926ad4cf98e1810684a39cbcfb550b3940f6ce37c9db375f5ab4608be5cb4d8748fffe728ca21a8dd2f3ba35a58ac685dcc5402b6f5e876624f2fae2207905c4f81557393f404f9f3dc42178c008ca9589fa688d18f6d15c11260247de1cbc3cd5f99952fcdcd2081868d14f69e4b5cb487a31330ed63a0a5473348b1fe272272174f391ebf699735a999544422761ec8122d03bdb600f8d93e32c0da8ad05ed9e513378d7f879e1281c49f803b3a7cf331273da9efd3ba44dc324ef68619a3846379db2fc902a9ee7b31c0b257faa2e9a3a03960d818e4c8f57ca21ee5792e1afe72dd77531941b4e6a1fdd2291a1b50c8c08be80b3695d78652547598b23f7b3b57d8cb112e9c2d37f8d6733d6cc1114bc59e9db08481d97e27a0fe6f55a1abf3fea75605edb8897b18c8bdb66a4c7ac60310b718c89777fa4b287b72e5d5f1884ffc2e3cb60853b24f07a0eeef0eab628e9965f3ae9e6df9204a3ab33f2c39fd8233dde048e3127789d052ae87eca8b54e184685171c65372dd55a6fe2d34d4d6a0670bd992a273a03562b6332c78eeda376f694069da9ef0c0f51f70dda593160a12f27e6cd8bd5e1bc3a3bc99a8083396ffbab697879270c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"826d59d1113883a25074281f1ec1470ee2206f8f5678aa3ff179928687d7046e","proof":"803f8aaecbbfdab3c33cad0e9484981adddbbddbe2b2cb0780267fb65ff57d6acc3debd315cb23b051e792d655d723baca0a8f1a56cdfe05d6670ad76deab96a4063d70418568a5787f67a3d4e0bbf916c9eb7fc9e1c5472b6ecc7b47676f202ba718082d69140e4f4ef6438f969580073100b2899ee53930c1596bb41dbee73b55d8dccdec6eb3888504eddd4b50e288287604cf7f5cbeefa1b361cc9beb30d120e10f4c16ce6bc0466b9185f38debc01a4b0db8e20642bf242f2efee33510e73fbb77c324a541c6c1e17e024b4f9710b8d2259544a3a9201401358cf76a30d8e392d297dfb7e462cd62f3dcc4b034d8876b93d16f4734ae4417c12a587ca2ab6189ce864a520473acf39c83a8276087edf374730149067799f5e182c64cc1c2adb59cced9b510dec8772f1c4125fa0d14195f23496e2a3ccc20e7b12eb832eb816caecc7c3308d259d10f4d38dee03b45e5f544365cb7bf7a84be7e893a87302b632c0c255062729b4d055f6a92140b0e10cd81215bc27224ba7face381d29e8639065884f3070066b65a4aa7bd19c95a8d2f12c509ec391005742cfb5001eb879c8bb250a90bf9461ae431bf7e8aea9ba1a33110b848723839064d5c22251a64b44a08d8d4bc25ba0e53146fdf68a43ff6070fb716b349fa34ba00b80f7357a2af792596f9dbd02d446189c682e7981df1175d0eb57977153975d9fdb647d8a12cffc1cc533c556918488d67017391e509137d0de78fdff10c538173def46e091ca6aca95e469db33742bc75b194ead559c5e9cd304b634e642bb226c85481acfdf2a48971132e0ee26fbe931ca9bbbf47574aada62a13949842d9b56b206cd263f2473eb610b792025af9b97c1b10d2a7e7127773039072ef5dec785b10bcf2ed3ee443add632a032b0090a6f3399be0e26fd566d2caa42b59253cbb4103"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4ec26400860e5f900df74cee96f4d88a8728e4291175b84b12017ea9a3a44530","proof":"88b125f79852445b279756ed24b584161747225aa7b62c0b15b8ab3049325e31548d36138108c510264328e4d6e72d51a581e31a14ea2722fc80d0b4e6f1391688c115f28e1f52d20a1a79edd85b0e3a23edf4cea061abcfafaf22575eb84108a6b3819afa9b0ef4340c9dbb6dd0ff9b78a612f4d1542548102e33d9547dc54ee50789d32aefc2c9df9b96da6f2062621c5250791b1654f00bc8c3aab12f20092211dfe5ea80f06903da34515141f4fe764be6e7c38d44134c0520648f2c700ba40e43ef1da942738380eaae8bd4b41f5838bf6feb9031fa620afcd563504708867b7eb7f7bfcc95e1d202316ced5a5156fa6186a38772c6bfe9dbce808f7e4f98614bee28b066a2e850e10272ae59264124ec926caebaca8274990f152086366c4e2f3f16178f503e9fc9ace87e5389c564d21168c965cf421b90ef556d4d53122121b3bea56979c788bba42714b810f6b4ae49fee19bb845e80edc4a1d674ca0acb284d12041e523fe747b3620a8ba5ffb6d7c5a6dd39f0109ff2fa12ef67a1233ef99010fea65cfa34f6b8d6b35415e535e7f30a4b7f9a1386fa824fff6195091ff67e02cb2c65bb1ec2972e2a8d9ba4cc07abb0eb48e2cf3532f577b3139540bb36972c19f0d100a7d532a7e5c80e92798fc784cc2ff061b0af1aac9333956f4b184520986326f7fa20daebbcd7d5c6ae2317a2e377d851cf4782e81e706f6d438fdb24a01c8ec2ae56bf0b46b0122a1145b01181ca7f0d8dbd2c61f965bfa26c2b7b28bec7f6373e4851902f8e4da002ec40db4f15be089dbb4620684642ab7066ecacc54bc7875561ababbda628a6f3b17d7f970ff9f028fe4fde9e211c5511bfb873ce5499283347271610b15f16d12a39f264fed3b292c76fd27fd0d77d066d2d3cee2a68e602c85201b54bdd12ecc679b343bb7259fba70bfedcc03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"62818b5a92526858e838c38d9f735e0b15d5329dfec9f3db5538a82b281c1704","proof":"de89ef8aadcccb0c21f33a656ebdb66c77b79468ab758ea8717e59607a1a9c37a695663a1f5fc6aa1443fe92a63e2f0a841e4bde10c69d6dd621c4854cee196c68d4805fd071bc81b0c2b723bd6a8807c54e0039d216742674fc05c42a091b54482fcc8f32c155696c5efe319fc3b7f191cc535b87648919d477f067862ac367543840c89a2872c5194750f4c8e1c275e06eb21e4be17f8cbf15b6694470500a3f979c1d38f9e65aa0c6213077d947e98b3b628e7f49cb2a060e1d631edcf809a1672d07f54e97c8904287ca9c64c40fabc4914d6ef72679cee7fe32fc0b3a0b2614f99cee241eacfd1bf80091737cc31f895b65ceaad4424d81147517ff91066cdc86c0ab407ff82547e9082fad38202b59b12e32499880d2da54682399d9214c6e1f8f09c7d8e2b630e7bd2c023dad1aa1e26f53c3f73125aa7a0e80e9cd682e5b864a7d3875ffbfd9dd5d950482671811fc9d93d3405a861f632b5574093dce33c5745acb23d426bc57d79913747d27b921da665d8c403ecc65d2aaf4bb480e2ee359da5647f13e91e76ca438b953ffd6bf77b0fc8f0abcdb31201bad5d57387a6532a1bf67c00a79d8f596ff039d5b87dc8fbcab17a20925e2c0434258043a434deae41da273a9b3492f15811a25466b794ad09e26532df60ec049ae3b51f2eb6948bdb61093aed46cc6325832ebd90b63e5c3c5c67174fc7a0808d67046b29ccf73e1b759b341fd70b1955ed235568eaa6f4c5742692dcfba099693c7793083af573c50e75b50e7138c453bc8e06042ee97e2c1ef62bc16f8152c91345f72e20d86eff4b2b3b1f429a3283c78c07ca57f61e8b640c32e0d017c4823e024c531ad503f140a42f42406f991f7ce344d0e293a37f75a294f4a94831776720f7b6a5f12dc86bc9a86b1a4ea53a600cb764d5d989fb88b96adff145ec5793f0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"64064905e769e5caf313fe0adbef6f21e9847e0f3d392310ed64776f36630965","proof":"3aeac3b003d024c595f89302155e6af7fc09d8650612386172f8dddde839b65400d3529aec4761615339890de1e75043b84109c9040efa56e1e9ab33cdd69b766c3b89740a8bc2b7e8944f36121620adb2d6bc16ae06008ca4ba05aabccbdf264eb35096aee079bbd618cd8214dbca4a9409e81af7dc0c09def1ab705debd922f4e3ea14a886cac8dae2b23d9c4b8591988c0e837efb88e43e4e463325a8fb031408a10a4c02361ec7d5e26df1e152db563835742f04fb4ac2a3e70e98d0b70bd552b8957053d2f345309efc16624650e9d97ddd4f066def0eac732eec228801a8e9cf2ece8be1e6e463808214beac593197b9a71909e02291cc12960f16ac779cba52e97f7ac5930cb3a432e2f473a78c89599da97d119abe615cf096f8f705b8d06d420d90cb118fc454cc4deb572a4338c3fe2a4885ba1c1d8e0928ee9f73782e275ed3ca95aa48bed4f35cd08c225f4e44a77add2eb97b491bad1306b354aa8e4527685e5d308fe9f1754d80f2d27338985cbbe863aee07327d6274b8f1844751c3de149af163b433898db17c3c485607826a38662f148921e39cbfff242f68fbb77fc7b48e4909bb86fd2f5cc2f314e0b05e7f7e9c4becabfc2c49570057664c224525dc0304d0cf805d6ecbe843e5091caab2f7958e9fc742c30b75a03c8249978a019b823c734ec5f02cdf6ec2d17b691c9ed6f3521b998accf570a6d80652cadb440695258c0fb24310592115151c7b52ead8d99b286a8feda063412361344a3cf044a3108cbf340590fba2dce3648a774cacf6ab0b869e64892c465dcbdaf5a0049673325eb041f9ef27ea62d4c958387e1addb7194a0ed4797050ce3ef6a4305a5b3782ccc491660adede5dcc9c9f8c985463b96c8d463bcba3c0754f943506c1c147ef5988a768fd838fe57ba0922c40a848c48341126edbcb70d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"82dbb860a8b257293567ac81d9b2057dd5bc8d6a73850b760aee2158d7a5dc60","proof":"1ee69edc1bb967c5dbc77d2adf30648c8c1a23b0b64547d6be6881340e47bb6040cd841ddd0c3600062cfaf70d802f7d580d38b5130d0bed27b9997e16f9c502181fedca678b109a73c98489b45812701a5e9d7a047f224bbdeeb7d230c0c100aca65c38e509d5e343a5070137fba458259ca064559a8b78dc5e360dc0e35842d8a59fb464c75c7bce045106d3261157ee25df960fe4a25a9747f166541b15004e39a057c556f929bac370e39d837877fd6196458031bc9226cd1dde87b23e0d6961b2c5778eda09db70101b334f24b8b7950d82425a396e9419edee38e2060d5820e4511313c5e753dcf7ccb8fa503eca853c6c1f531a7bd8df81d4e349cb6eac68400df20bde888d75f2099b1c8e25c64c951f20cd6437bfcd18abdf38394cdea0f65b732a53ad94edeb0d76a87edf51825b9ae0787fe4ea49d1e43d5d9f2aba331a91fdcd7649dd51f176e098f7420af297faf1ec06eb01e8342b673e7c5098718e3ffc056aaf02f9e6080c86e1cdbd83a382bb30d13df14d01664bde307d184c759d5f72de492b06f9fee44aaea48e5a97ac46a1f1caa8797f4e49d5133e5e9e97fd5d459aac2275270a375b6c11ac317b7d5564d6c94faf6957e7288739e02ae85f857e06c0346832288ce0d5d2963b4c2fd09e3d9c586d12ceb2125002ac34b41ebfb1409ced92b005d6a3fbf298ff847efc490d6012f65c3a689f5056803865506ae096c462e71a70706d0a5384c35ae926b490c81e3b5489a8f412592e7c7c38c5adb5cb329c301473699bdbeb73ac77b75811ed7141ada3eba9d245a218cd499213a3a47f5c172a42aa0a20bccca9a245784a4819fe9a7a0e48e2207dda2a3819d39b196ed1b0ad701f03e22901751d027064a9ca02b4140dadd90e325b4d3ca1eab498e3ebc76a6a4ec04d2b47962b88b08e6d48a4a613984f2b0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a0977c6e2dbc70cbfec8655dd410d8a4ba3e2461c93a7cec058af6f3cc413c49","proof":"7c93ce14bfe4d20e4e52e174575267d7052c040c0bcaf2b99164607ee4997b7ab6a5429dc80f7dd65ae28138f32aaa65f955e0448a1025b231e8713e0257f56b5c618eb69acf6005694db504754a5b53f8788723f9b5ab57fea2331ba77ad4004288a63baf7117c57e755e43cd7d57ce97c38aeedf093b30c383ba60a0e88d5c4759c5a1c2686489a8e434a68f626d093102104bfc24288d686046ab5b9bf301179e7cdd1f0532ea53f81d7252c4ecdf68efdadfb40b72a392d9c4e90e69a60b1a7e48c4994805fb607f354f34f1543650625c83b288cc52fdc9fc1e67e6d00796e9e67e847743c006919104c036857c9fb4adf7d913b3bddba9b75de214a1384a428b73c033d886756b86298358c6ee9171e24e75dfc4ab9c659bea91f8762e7a240d93c10247b87bdcc2db0710b7e7d860dd1a5829c3fdbbf8951474db98546a14cb49750516315d0b2816c77bfeb2e7ef4232c17bff9dd469b49e10b9351b0293fb47f18715e54cb7b82679a556a00ef2c18e7c34fa4b4bdd18f82b2ed55c4ea62a99326588e8687931aad7ddf05a8bfc556bd07f4a21bd6b4f0781daa032b04d19aea95bff6061e9a187c8d7f3cca9560baf2deaad0869a540f01232091b36f3b7993dc2f819947662d96a195741801249d0af079119995eeebbe5946f50fad96b8635c0c697b0f039375c83b71b46cdc44857c0d0c8f67dd420223200301c3b9794e69c98123aebe285378d81f6f9d9cce8b84ca45d23da80ad974c59059e3f3bae284b73731f4811234edce7aac7045f490562e24995a0baa05f5c6b4f5a8383b9b5b3dc6fee821c6fd8b72b9a68cd9b06d3eddb638aeb30f83e5eb75f65d754392b29bc2937187f3fce122fbae577bd510ffcb01eaea6e25a7c110403cbeb252b2b4824bebef706fa0fcfc4460077ffc08d9e347f30a52b48c9c1c20f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"eccc2a3cfa0dd1a7f56c98d27a8dc3eadf2fc12aaac2bd8af6c7e7c3048a1d01","proof":"4cf14f66584239a6eae7141ace65940c93751670608831040b4b8bda45f116767e9fed5acff5fdf78e179b46670ad8f377eb68755499801f1f4f247038ea5214667f4f9cef09149a461cc332b534e3c145c625131d51de7e2fd8446ad818b115d01f49dc6c2b47bea32dfd8e57b7be5775073d8757b2ef5877cc5e715dc9fe77ca4f334f441315073c15c7182bc26a0a718490393c7722cd7edb83726e63ee066655decadabaa579b71956c0cc876712f850d8a8199df62a23e33c821b8cf30400bbe6a68918c1672c8eab7ecc748e15f13f8b001530909c6ff5befe43ac5e04def1cf838d838f13445a7843122749f1bb8636fb5f07b05310eeb84a6264b47e2c1de13cef02c7ab34839928a8962696a0b37f9264c57ae24216f7992009f41bd0905f8bef1e46114a9586a3c57690fbf5e3a9427a88ff60037b2bd2ba759658369210fe4d0d253edc57cb4d47791f875e92c159a74457a2355b0812054d0a450ece78bf46a26e8a4c524b2449c5a620743612e8768a577285a1ad5563d2942bf8ef9b6ea5ff4cd821ea3e7d443f2275a54bd46af27c941b797668ff228f7c48a2975370edb74ed62ffe2233df156f31c8f2d4e0cc358ec8c36bff4b1a39854b168cacd2e5eee71673d9660d2fd26051a2f10c0a07ba7549d325b0adbf5dc609e0aea86cbcb30d15a3cce816417aaa180dd6e19f578499ffce9089622a38107a12ff60fdd5e26c14f5f356b6f8729b339ff23bee613c2f51c806a62713352b4e5c1302b8e2bc50831131fb0877cfc9cd2f08b1e81839b94630ad8c0784b486726ecd5f37852c12b71d19a128d402af1ef9a40a17ff5734ccdb0a1464ca4be55874c7f39bea01195ad2769328847280626197ba331b1545a714a6ecdaa5fc72022cb164cfe98ec5166a9253d6a67ce88572ebecbc71d2cd712710d6e42d34370e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4051c12525b2272e80168ac82490bbafd065641b9d0d82c7eeb3f8cb3d356106","proof":"eaa059570977e0e7752100f9db3c1400bbfb6289849fa2893c39f686a5c7b5508061f0660389ac9af3442a730d418b286e9e00749effa82581b05cc6cf1f9f25145e90f58b5057ba098727d007ec78eb76d34a28213b3623f99de02ca647485fce17be703ada14b132076a9d82cbf88b16a1eabf1e7efda9b10b62276ed8701ecf5b7c066de999ec0fb255766c817c9e6bf09ad6551a2ddf819187f31bda160530e53d867e33041272d1ccb7f65f53d005b6c255f3da247fd5508d163ab46c013648ffdc679e15bb90c3c303be698cd2d63012ed44c69f7b359bc8c6b0dddc038e2b9909a826b2a123f4561ca19962efa6e7c763a7d1da7e5707e81ace601c73c41faaf3fa407608679c758dc843481093daa05c7701daa6f865c61ee716cd3a30e98095c28eea1c48805424ca0ec61d71ea71b6db674bb5512cd430b2eb34651888dfcd49ec16c9758a4bc52e7b8aea1c040cc52b9916bcb8a4537d564e1b1fea8bb5e5ba2c886c716deb045a405307fcef520cbc7e4a102a88705e61f0167140c447c0e495144f33e406102f625a744a480f38edc7fa9f0ab9fd77c88d954d9a0830c9ec3187c172d0e7b78914d10522edb9738e8762203aa671bce6b21859f607a57274e88bee056e19a50c691e0385203eeb85106c666eaf3f1beef64b6d46dbbe326489077e5ab5eebf35bc7fc5d7868385ef6886a5fcc65ee0f600291a0069c63aa3b1da2a941b96a5d3d0e47a90c6ed329c6183f0d50c570849e820423a92b554669241791fbfa20fd2eeb9b89d92ed28ed113734726e5ff424cf484a2cd3d836ac59094583468c49927d99c37c16ab6ef64daad7aae382023c9ba67cf9c83952a8ce2f9b39f45d9c495d2bc888208518205606410fd0001ce5d9a406ecb33d1f90468b83457e3d804c0c8508b0f2ad144c7b231d07d4fc24b997b505"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6e5ae163fbce19220d33f2944ce17f9a4ea9bd33c0b21c07b3c7a051e311e213","proof":"2681a204fcaa0aefefc04086d7307fc17a2cc0f5da47b5b350839213737ba40422715202fb6fca6b4ccb713ccadee4b2da459688bfce4d5c26bdfb1e4ab9ea7f824702d1695af840a75e6432673f3b9984ede50ef83d0708a51864701019743d361f2153e4599e77339d4e122686fa85480b3d2de47dd103b03648377f2f644aec88a4843a68f8c7e871c0a5d4d9532c16d95c6cad468cd4ebf266a0833e12071ccaed7aa569cf4878f3d592d04965e3c0021c78ba224c79583e385d6eb55e012fac64503c0897e28a834a6b777daca97e005df740933d2521de3d2b3179e3093cc0ee999bf15cf90298bcb3e397133408ec44a4fd23ee13f2e0ce192629760ca8b1ab29cbcad0fd86a5aafd7306f214d2e98c7b16532b861afd5ca2c4deca2226b473a9981ae227a988c059bd76c39207ec90680a9c3ea7723e365e56379555f24a3a68affe24d6cb62210084c4d4c6805a8e825fcbfff2156a0bc46f2fc61102fc30a20d4af8537317f024ba929e70d746d63c0f2a8538694e3a659a219523d812e4a4d84c35a65b0c66edc11d4ef57898b27526b0acd504b13eafb4d204785aeb3d0d2dddbb7f1371d1fea38c60e49b1f8a814998bed9f61829e0f6718f447c74e6b149f915d66ed0d1190aebab4fde4e04f80815a98f18331adc96663759ec975ec4d1d5aea8d216424b86d421ac0e2aece0e78b79c37709931c0bbb7f2ff2faaaa00da5d23e7a9437ce9f455f9b9cb78b950bf13947e44d7949c2ed7a33da5a64e6dc44062565d3927704a177e3da44d2e42144aa3c5e1e009a0cdab67a6cdf0e1a50c5f9291d5ba3822f8649119cc927b4b349cbc91bb736ddd202b4615e2e86db109fe86f5c3688fb7188a4369890cf9c9dfd6a6c51d6daf59c63a30b43fc604e142eb1d07ee5cb9c898998550098370e0a48554ce9057df44fae1a02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aa5f9d65e203f1eb6826b67565a4a60b1156880d51e24cdcc7a72cca03ae7073","proof":"b03c0f3151e428d46507207c51b4d2f7d794bbc082666c08426d7287af028f4462b1cdf58e7a0a0de53b6694a7fd1190c2f168ca4454e0c53f2a9c3f09e27407a459f2d60e7205d3550b85443fbb5e6aa3d209c6f49875af6d4c0e4a1fd39d10bcb9ed97d8b8ecfec4018089877b1b9502189ea558148b9e12b0f0e46a4178465e2fcecda45020a3b301be9b5a83f7f75e7192ff6799d29112babbc98565ea0967e29f137b3e02ad6640cb587b8b02bddcb4c3b28c576c876a8e4b2f5f57940420539a05e362a69ecebd88e15b13a4fcb087563158e4b26ca373c07de276b00e76aea6e4a8f0cbb51002266085e1aa99812725ac2df364915decec49bd1c852c1e4c9acca96eeace2127dffa8a39bbf5eeb90e470a60814663d1867bd58ef329c62483ffd458bbed97ac6b68e8cc9cf27e5e2f7abd64794177a688e9e5ff2f3a7a3ba8a5bd10b4c6bfa51e4708f789f837048e57bbf102caf4a9295f43ac1664d6687e3035c9f417b61c4d135e8818e6856257f1d388546f836d6c89b8fcb0065eea51ab8b9f621011efb5f5358d0d8bb533154f8089dca089752868832f44445c2849ea05b8dac0804a767699236abb56e09d9e806cbe3379a13b1583dfe94208317688410f4e6fc5b7dbaa371120887ecc54a223864cdc4099bc4345982e41f0c6134fb929e8bf3ae503199a1af9f59912d3e7f0ffddd4c3ce699e67c07f747e1d79e0008916dedae86ef9d2d3e237ca5a0c8481255ab59164cc7c4de9c1230421579c27fadf358935ae6f48e16ab4779e3f79f78f1bf1296f81171a310a4f6ee27f221bd23f0f279f3996197539d0307048a28512760051f8da9f54797228ff7bd7587ff99478ce38459dc4df67be97a20674d45a571537d7a562274f740f9a4f24d34eb138629c74e2ef50d5a4ccef2c5266280b91715d28f9a233843d03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1455f2795ff1e7a031bd26332d0495c3ace75796132796fcf6c81b87bdac8605","proof":"0242373edb4e882edad2dc4369ef41bbce6ca5ba3da45e75bf164fefc61d5b0a5cd32c47c3faf9849fcb9b5f6b31fa142520aeb68acf258c8953acc2c4f0a83a76f1f8519d59abb5e74cade17cbf7b9869a723c1b60b20da222df36d3bdd6937b6a1a75eb193349b762819f7bad79dda9a7852e29d6dfc77ae7db7e067071d63c32ee8071c12826ef504d61cde7cc3ceaf74237522ea75faf47fff4565f0d2009840c137183ee02855f190d2f40b19b27134f7e8a30781788f3d61fb4e0891069637c2b13507c079539fbf4827c1c98f05ef9f397823e1b777c59a27a2e11d0638f4232eb51a3a79e37d16dd0444d4ec6487289dc406416c83cb895cbc07354a7a419c8312ef81b6497aa17c4a707176daaaadd8ca93765415015586d2fc0a7bde3099fcdd71e724dcef3d7b7aff4981d5525cb79f39811663fc8b09df1af027eefa4bf7bdc9030209726da37090f62676d97856b018606c62fcd5a6567b434a28cefcc5484592c5c449fd44a14707b10e0e0ba780a34a6b4700edc1ce650950560c002a3e75f400cb15554790c1f29dac27c8903c42b7157dcc7f6561967c2edc44e768d2c25f282837698c72a19237d22e2d1af940479b4738648fa3dddb2b40e5586ab758fce10708cbd00ddeb8c5893d07754efa45e7615654e72863843872ec775053b0a7bee94d1d947dca95e62018f81b0108045205fc6dd9d70aac79b2d3ef49aab4cd8915c360659b76e942f8cf50910296f81c1695662ca3912a34004685f8b0452320157ab7f5e544ad4b23bbea939837bc32b6797e6abf0a9371a4896f873e94f01645c9f104d9ca9e04b4b0e4611d91390199de1b0229f37148517ab4584c4669bfadf4d85c3bed5e0c0f1aa500c4dfd064c31feb1e756ff701758974f4af29b9a78e1e73aaebb9dbb8375cc6cbcafb1e6069599ef6acc0ed0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"de8079328f535d7a060f8d41b5ef03940d96c7617a329a8c86a730ed2cc5a558","proof":"3695bf08000c1bf68445adf1a81cb08ab4d1ea20e6e6de8d5c120cb0c46f6461769038b3ee4c2df2a3697ac4c7523b53e9bd49f11e29411665dba9ce2a36c62e6409a6503dbfa1f7f169d61d65c65ccf8d275aa3a54033ff33b1dc58f2a4e570a25268308224d667979a11f5aee8de9bedd3dabb2d63f95ff5b30d902c003433f179fce4a830d6f5e32d026badce54cea875b0a89b2c2c260b2275754bf4910a7f2378c462b4f17cf315ebf50b4165e4bedb834dc710ea8da9250da1d8eeb3090ec11f22499f16a76b0afb40356ad848f4f213e7a841630ba38a6e6401f3ab096a2c36794c0bba3e7e1b951018554098a54c87cd3176b69d8facec77d8ca855df686c17df55178d1b12f19dbf8ae5cd5f16f2a25926016982ce31014789240518006532a052c99640dff255042fa8cf47f895a80843cc348d635db43d272e41346bffbbe9e146c86de299c471149f69cc9fc56458089cb8a86bffe384083b04eda6e09e82e600623053b51f332e5227b1af9b6a270a4015928dae5a40dad0d588868703bc759cb31696b3c610637b24b9531ec00b126249676d5a589a3c16714a0aed1419842fd20118f2d0da84d82dc7cf5127c7533eb4baa8e9b0f8125a3186c8f8689003712c0ceca52d448f6d81a65423d4ae55f2328bec98b40ce0e942e303e42363c2af0dcf1ea1aaf2306772e55c970d2edb56f9847b62158232f2862f27837e88cf82a03eb6750ee194a487ab7ca787a67f2dfc3459acbe8b1a9a14d7a5abeefaa4814a333fa1dfb80483ae9e1ed852ef78682e28dc8f2a1d618150a6e574f625310d70a9d5eb1e20606b32629e3b9e75f5c341c88bbb21fba1a5f5df02ec2d65028800b734d4b08014ea5b11195a93eb47a2f8be8185c24b01b12096fbf031dbfa0c3fd49eaf201f1f045d17fe554e2275e310335651f0c0fc6210f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"48484526b197cbd0efed55ee3f0538306607b61e564814d6a92cda5086768c3c","proof":"644155bb2e728739e355fe45f686916ce84b52e50ef04948675aa6c09c7b3d34d2d3982b7d847bd51eea72eb9924da812f056607f79e17d6db4fab97dae6880bc0d54fcb67d52847a8b427521107e88da0c3e01e4fcf6d1824f5485111f0f25a8a895447018e159a2d21935e7d3a576d4fc9d152ffe0eb53fd04c67863db583fbd364119c89ebeb408163b325cef687793d2d2fcc4c7de7d5c6612da83a19303da8af749a03d79664c171e8c33b855300267213a10313f198bd7df1055cf3e09402f11513952e5e21488693f694cf18528f017186727ea57e08dec59bbab7f057eb6ebaa5270f8f937c5978319ecc3829f4b7115e26d8aed4a9aa66de270f9407850b5e40414ef00d301c992934627d8052f15241a727ed7179300d064e8515e4c2c94053d6c44a8dc115944e5144ac70d37768d486512b7f40994726e589e475eefbc993be13ed2b1b2422f3d84242937b2d79fe47901683b373639d34153758a798eac75fc3a754862f0f81468ea5963924e575d4aa0db65c0ff4fcf88b932b4517c431cb3d3574f2f4f0fe83695b4656b6cdc042e03e49e0cdc2cb4ce874d38119b120a704805ee325a3eaddb0978426e1231bfd0058c5ffb2f452c763876f05bab6a9d2afc54539ed4ad9463bf46e472da7a35c052eef826b76bc3ed45112a7f6cd8a6c599b058e085629f104f50103a37919f07e132dac86bf0b02d065100b0c6213e479d14292c604cc1f89ae35faafbc87becda7b6cf576f5a6b11d7e882e73e204260193485576a341ed9fd470f002011c3756554e2318fa61ed0e260adfcc38fce193c22023c3de7a22c21a5d001cf43f35be9517afbf98779d5d2aa43927ab3d044f7eb2c3a458a72c72ebfeae2e1b0780563f95f3127277b4f9075ef737625c0d9fcdb350c4c79abc87c80069a6f36eae892436ec9ae4f214a40f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f442ffd753fe8cce1516b5b7258eeb507abb7a2e80e838df23a7c84f24c52218","proof":"90e0ddb8bce89800139bb8cc0e917c67b3e8deaff9f3193398b1cf10182fee0fec815037c48643f45c1b160b1f45be811c22d323eddd88805873e25ab488f93688e248c2080c1a74c2bca19e37dbf9a311ff78d22c39d6c5dfadbb0be5b3891b9696b075e0c33dac4808a8246726ef6e83cb0f433f2ed241e4ab340e178e8b3ebf3a53f29cb397d9256d4d0c0b4faa5c65f05f7bdccbcd90479cb6020f41490db3a3847f0d4dcb9c8c034539cb10fcfe52f802152c9e6484c2cd8a50bdb87604ef3f27547e61c29bcb5def3d17181b908d3f3e90f2afc6c81c53f0efdc60bf08fafe8b92222279981301c696c145647147e660ea77eda6590b643cc5703c315302537f48f4e1d0690addd9a62a8df5e30b0ea1dab92f954c4f0991a63962a634e895152cfc028f917b7e11c754548fc13086d37bc3f331cb04217afce39e6366641383a7b65b5adccf5e2fb5d08ece83a82c305e28f58a46bee3099caeaeff593ef3e126e586414de73c544b7a47e8e222743ee4f75bdde86af162cb096fd8026016debd3e83205d4f55d2bf6fdead48ac0229d6c58a85428878a008c6f0b933265b714d4ea6b9929107a22c11b2b7311893aca760aa7a9d184d8a9c3f208d3946a22fac080a8ef84b1fee351fcddcb575199f04fef0d064288d3473537eb1077a34f1a3fe6c5678f348c455d648127067ccc1ecb097e4cf060ae39168c0445ec40e84e9012437918cfd8a69520a2b63258e7648ea805cc9c30f557232efd77e76588480fcace34430c9986c1547f254e0e924783a4cf327895e07888efac15218e12e5bf25dc5777b930c25a60c0203913f5722ec71dbad0414b0b3c0c34f68ff764abd1c64633e937133aabe95cbb7938961f77b7cc2164d67139cb7a8090a35a516188292d98c5d4f737186509096e2f3bf74ab36c133ae9c0b7173f4980a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"10e3f5b6dece2aabc142de62be59c33672b68c63e6b6858ed72d64365ccdc70e","proof":"567b61bdb7b073938e85ac0dd19cc13242c39ce69cd7c634e95f074c984e201c743b398e52b1e0cb1e6838405cd0cf0317d7bdc14ab0223ae9e684da48e2da0b1c16e8fb8bf6881732a79839b2af5fdc2f79cb144f1a67dbd7f03431db5b445e32df7b52d75a0c4c08e21adf4294b0cde1a16827adbcc3c9a1eaf561b01a441d29353d977c2cfa6a9d1a3a635262ff1caf2371c6f162a953e2a5013b42fa1d07b80f5b72de3202f93cb3d6880228a927d2377365009cc4248f7ceb01d0455d0b2437f704b1c4eaadf6370229428a5946b469c841902c47e4162b85b116b5f907120680a442600bb0f81f66c5d3c30b24337bf3ee8fbe7a70eeb0b2b3cbf32526bc7f5d23f4ed4567387d323962c40956a946b752a49dcf51ab097cae07dcdc4730bcaaa37ef58747b2ae346f087e5d4efd02613e7341cc0cb88446d3008bef6b282d7f94817381629e5dacf2ecd1b3dbb016900082f823c1f7f82f06f4c38954e4398c5c7df2a3a6ff946fbc00e86252e7a89024954bf9d1fad0445c4acd77597ae75aaf03f3c9f5d3789ba5d32d69958d1b3fb63c3c22e48210f0d087be420096782f7ddff1c2d05670fd6568394d82f3216d042ab98d26e615b97c987f9d3656d237b7b927b42d70ab5a552bcbe3b34e8c21a15fa0d7a044344ff8ba9d9348347f6e3567d6aba0dd6c642dd86de23dab52dd580a0c0866db6b7c3be28258101aeb01eb1b721effad69c075c06afa6491b8d7271db5e1b739e80622acd65a714860249f3f19140f9f6d5c11326bb7b3987406e833a7e6786dc580c0e1bd47251a700f64c5bab7c630c76644ec51f2a93b30ad8fbe4b4dd38ae15d3f2384b67590a04764021efe286ca939daf30a42ac0ab3bc1e9426e33b7fd8c451fa7cb40359aedefbce14248856412e1522d036bcc626e90692454340f807ce41a43ad20d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"200ab377922bf11424daf85d9f0b71ce03b114db98f30fca6a036fcd95bd5c1b","proof":"d4af92d74890bb0f83a0773df2fd34b0f6a354f6e35a87b1c8eaaaa699c7ac1098cec0cbcad54d727f15c77d62602aaa7148ad6bb08a45e4d172d85495adf57c06734222a2aab6bbafb6a32b90145ac2f3a7f9b8563a32810bae1c96b4290f35606306e0d0f0c71b91a0270c729e9f85263596d7168a79d21bbeca638a2d73764ef6620c52146170fa583cf95d4dacd36ab99210d5fc820c4d3b18ddafc288057ebfd736161df154f7a39028684fd6294653bc59f265c50303e7c1ef36480e03265ac0e73acf976c53d0ac0e6efd1d402c41caae7b17502c9118892b66600f09ca93152de1f15ef8b9b89d716669c0d7242ea381ee6908c623fee93dea81953e46dfad92d828d1afd8e5b22988fd76ce8defae50aa5f2ddde265ea028716d817f89bea4af36693dcbc8e1046f1a336baaf1f13ba0798ae164ed662a1c67376475e6bec04f1829b308092111a667674e47849b85d2eebe9c185b4ff5e3e5ac364a8ccb5714bd1ded829b8fa9a1b7024bda3832bb99c8db5471cd7175c28bbb84ec6cd43ea5cb4cbe99f18587a58ec2bd938b2b8b862d7772168b295a661a9893b600174cecfcb035be4526d5128e00ae8ace1985c98272c72de7fa42d5726bc6b5637f448097dacd0949b8d2fb6b2688d88c6f60156f9a81cc514b42e0d6a517c8e54d8e945300561fb35aee4f5187b24e6bc063ab7d79cfb4da1a3c9e4ad971382b1014df957c13270934aa8a099e07f397fad9515ee1d7af0712ab9bf12541baa168040e2abe7713a56f344fe524f588b2aabdde1764d37781be25d814ba77d9ea476e5045020c130037b8e05be1c54d20fcf78ba612e2ef5e57ca69b4f087afdc0d8101b5955522d21603cacaaa385f7035c04e2190694a599851ba110fb056788d8864a77903e075b7ac02a74ac1e2e23ee231766d9467c2916b2ff9fa90f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4295b8763929d52140ac32062e34010ff23daff5c93132c46cd73355ab8e0616","proof":"503696c109c9d0a99002c3a0ba990f507086ce53297301029375b5c5e910254eb2c0ce5de9a35180381e6b568dc2e46e6000d3f8c74c3e6a871e65b6476c0142588eec03af4f2e25fa636e185994d4a0294c0c428f87b46081016bafcf32413a969b7635713ebd9c6da32142eeb0199c0cffdcc9dbe00b1b1e3d08ea67e2e2200e9e605f34aa681d87a8597cfcd758a23f3a5e7002e04647f14609e9697bb804e2245f8b028a41af2d074f1ee2b83cca5139dbbadbe4be3794a678efb9360e03bbb5ec3cf62f5ba2db9536aea90f88e9f4dbbea0d8f8cd7366584282bcd4250d642344a742b6c1407936008b4e9a2811a0567c157f918c4544c0b834f84b8e2bbc04435a516ad9084b816ca97c9c44c3bb7dd0619cbed913fb099109536cc86f82cb63204e83a84490dd4fdecc0055f0b9eeff4918700631cf414707b8365051e2f199ec594af97517f03bd5511017570518446d33a1c14b1d54317e82c21905d6b7751d5e26da14af8698033a727f7f780eaeaf28a133d7e15a30ff6ab1c235e2c2b3341c93833cdf57fbc1768b3fa81474475a1f744df8677362bd5c4b7d50b0bfc97a968b34ed6734c38e4fe9e8ffad78894dc3df537d7f75a3dc15dc55124ca3d31931aea27eedc4c3a5dae37fc7ce3eeebb1da690e49b195d5c4b2f1f7e526042689ecfe7033f8f95d7d21acce66b76db112d0c7a9692e7d8d3ec3dce39c2f7db3aacdd5ad81851bdef69369ca2d3937ab01d7aa3672c708fe39872f935ee5f0f859faec74192fc57f02b7bc16b9c4f717784905ae1fd8d54a7e36a000fcebbf4f3a9207cc691691e341c861e7add289cba8548da88447dd7864e8ca71c147970f8a82b883f3ae6a3fc5540188d0bcdf341fb19cc69639fc7564932ed08cf6699df71a6baf1b72f5b3ed78135a1a27e4510e8fded512d988144931b210f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9ebd1380c99c18a40151c28a0a5af9a188882930a275e8e599d23a9bffe76a58","proof":"1e971c979c8584b277a38ba03132165aeeb5e41e69efb34c0b86a4c18a7f7251489b139f3da15b347b89e0953ae4aa9c298e4fd2bd290ad9d0eba2a920a8a64556af4c4e281882af5d7d2df7b11c5d63d0d3d1049c299339f98d8451664cc10a86e64ce70833f66d1eeca2161729ba957cc62c5ba8a8625a1b015b8ce395e57587b35bfab0dabcf7a2407f8ba503a8e9b9ab019efe0cc1730dc52f97b2ef040c0aa271f758ed2121ac7349f421387ba696c3649cd5208e1c4af64975b1d73808caea13dbacf458839875067e0e035de97a05e728cf44293c9cabcd2f2c7dc600cc4b72b3e770fd9992d2d902782129d1fa84b648859d78015312e04332aa87225c52ea4f78e22cf04381ea55e390239df09ad87d0757e3dbd936a2b1ba7d215a4af6e3fc298000f18cffbc3911309ddc1277c9966a1b25723eac559c45695e78c0a5d2524d91b8a8f2bf26882d368db7a2e6ad93317851684711169ab807e1581e0135068ab15b91b61c362370ac05455260b0fa04ec93e09477f6d04b047f3d80e3569289f5f7ed322a435333ac62213f6526891bf8f87d12b5bb21cc27006fcc3be6fdf7ab4860011f8fa33207ec65a71e7ce48f9686376d5663eec708980dd6cc97b78c81a2d1e7ff373afbb3f18cd016302533f1e779891cb6f3c24fa9013c44c0763f4ed64522317bcc0b7a24ba407da3f5ba50b6f58e28b70bb1232212a64e33baa9d5a0f42d8eeca814d1e9a537078a644fe0b4a1bdd066af065f81634c32d271a9aa2cf300a4a48e23d4adb320cda6e1b43b03938da299ae28ae132070a67d87a134c5971cff509bc3b7879078c40f6458a157a2ffd3afe0812c1352c8c31f89745ee6d5083acb5822b3e03cf0a22e5560146e4a21747fd86a2cce06dbfc27ec49d4001012437960bea9620e035787f15fc774a2f58b08d9c3d85e0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c892c759af2b94bbc218c6afc7106ae52c179be297d31718206a67f41d910d57","proof":"868726eab4c8f4a0d957b82395a6090672ec3b22f83073ba34b3aaf193dfa06a76504fa6b509c87a904e5b338c77bb8182bc61e6ee6e57f88f00619415faa254189b89837f769e93f3199308d8473f6b1394e3b8dd235d2b93dcc3017959bd703ebed807fc19a8cc86deff566903264c444d51f2655742fb1f0dbdcaf9689b26319d9447410c02f1ec0c60e935fa5b956a56cb42b66daad39448ab278e238800dedbc12f901a4d01e3eb008664a72425c1540fb4d652f42116dcd97e6fb8ed005710b5c0a1556e42995c2c37c826b1e99cace70abb23981cdfb512a0bb03a50de49102e9cf03e5e09043e7c6add61c3a4b618093d25507370dea20fc0f8ceb11fcb41e7fb206bebf1b13ba490168f6fae49eb0b31cf403b64ce0fb6baf982b374ea8c7c3ccba4a552624624ad6803d082768ae9af72c73c20e33a666e549e977d09209d04b9fab95965b247d15f8a2793c7801d8e6023165c3cfbceddf64055bce8feafb55a11779626e51ad3a0d82a85c3b910caec4cd8d50945a1f325ea618f62a887ba5505ce915dd94c70088260f495b3a1f084d45ccafc80efd5c2a0c3ab2b28a61e74add9e8624088f8d20a5466f33098286e565b2f8b161982287691f4e5c88df5ebd1103527156dacd43e40f57f5ce3cfb87dbbef6328cfec7ea564040453587ebeb4643d7f2fb9c833ed862619d0dd078fcb74235bf9269768a952e38c7b1d52b05a0f08070e70f2d89ab2c8268e306d027a68227245481ca11165ec6271cde2688f855225c8b4045da86395c2004c36de9ae03a7b8fa7c15efc2701c3d1b2d5d006ab8c340492d62ca8162efde5433d66f18d68311283ebec933347e1e7c50b74f5a970c595f6c2104fc10a8cc0e3ad866c4acbf2acf46b8b2f8080e648ea91cea023779e5bec66302df83bf2571d9658fc9a71d6302107e3c1109"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c255dcb25fda26d5d1d5f808284556eefa118af59c6dc38e507be5801a5e724a","proof":"90358073e7371b938ee61462b59ff1378b9cff1b060f91cbd7cb6fa104cd0070b495ce86ce17f1807c42150213e61e9e77c35013cfe80a133afe9ac913d4701cd6846d2ce111221d453f06f209c823a0dbc9caaf9b7da14561907373d32b4570e6ea3f5411ab90db7f78114f8dded7c666849441e92f3c9e69ef12dd26e5527f5cad6f601e873719faf66e617c2b1f583c2bfe44012c0862cbdd1e91cc651409f55cb299e110fd58291ca6ecc0ad13e8f7f4363e7546c87e7b50bcc0475b43018ebbc8b76c2a2d172481a7acc82c1eef4b69197a0975a34907708c2279e1680ecaa96c4f77097ac8a562371a8f92978cec51d0111f6b99f2bbbe19b78dfcea4ffe737dad6719d62ec3d1e05487f874774fcf589c90fbc18076054601c245d760bead0a0291d4e5cc2d5920f23616166b6ec0144e962867ba61d0016b08003a7f1af6e986a91d710676081947ef868e5d7867ea1707a54577f172e93fc42be9621a14181efa409403c25dc0304e76145bf5378ea7d47d37779232c2a1e7d746606efd9139cd39c4d6be343893a0fcc78ee9333d7d8cd092f0bf06ce5b206eae77926aef5f98c21acc57339ff310a32304533fb9fa754e8d496332a17dcd9496689c6d1df72d62ead944d01f4988294aac1839ce86901363bf4f222afc00015e2c721889f9dd5e8202cbd837d2eb97d7d2539b7c472411f5c3079d2eea2b34434c0628b7f6b995c0eb97be1da3aac17dd0620ebdb7cc6457cb6866f80fb24ad6255e4b1119ca8b748184d541848f89a7f4d016a1e132216e1b717785f32b60ee05b009f9eef73b7d190daae8b14e07ec660afda1dd63c2447d6d5af1d09858727ed6a39575af52f1145003b4e5cc49959847afdb8b1169518517520dca9b94c306ef818c1caf7cc7e753554d2fb34ea9cb76dec548b5cf885c829490622d15ba0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f88d36db69b1609166dc6e916abc432fb6f27d98d1ad06c9aa982a9f7479f464","proof":"20d3fe356ebf14fcdd57e3356279e10ad4c8d73311828bd8e8c1ecafac90170aa0e0f4b9da2c4796c4ee1fd09a6695284467b96c855d3f42ac22f7e81e34081f1c77bdb482d7d9c1722ec4dae65c5c299af3548d1d23b48476d388902042e376e29496a83260d276ced3834fb4ce97eb4187dc4c4d2f90cbd56e7cb0e2140e53d413a54d315b6c78ac7de1c10743e3ba376bfc7125a365d46628032a2aa5ad0d3a01c3057820920da3d087da24b823cc95393b7832c2d4d93a78fcb7464abf005cbb7f9bfd6683ede58e9ce2d5bffd350bf484ce5654bb635b06d2fb0a65090a0cdb30f4213e9acd2641d16ec405fba9e975f2a15b321c8a340ba186e958ae6d5ed5eea9deba61f0f4ac85e3ecf10ba125c8a5f03577124a8e1238cac312c728ca77162512a5ad99b484a6eb31e62502139b8d7c433acd298ff0b2a88487eb4a3858b4247c06cc19ccd845a22737626b6262db7c8d3b28b4fa19a548bfe6ec2af09e3a864a32d6eace850f72f278c3450d6f929d527a225a6fefacbf112a4f63e4f671d12a84b54cd2d293c23c26c8875dd5db0d95db8bdfb0b5d11c27bfa269e82f7f5f9b2ee796d4d5bc9e5d16d20b0a61de6a19bc6807e28ac0b21cfbf804bc2ea5471943e5f48cc2f2b5f2a4a48f00a9007c57024bc9621e61de1db24142ce356aa7a4c2e222dd05a0344728337b5204c50439f9fadea472b9fab00ad631261478b16128ba34a1213131b4edc27d042cd7480350c22aff532c425b48b0599c7e421a8ffeeb1a835c66993663faeff756423b361428ff0110a6948e22f8771c67f9f250cb9446c796caa8e39a05ef4f779f8c9ed36888a15346e213cb8c25909e07c63d363b029c62b3faf469c2fe13635c1550fd8335a2cb50844887ce0a916b46c9503adaf7f89a559f349fd60b6db1897f857c741e23aaaf7fd082160d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8a5e06cab9c9a69d4bccdbb8b8ca71487d221b7fb0098875e883526350180769","proof":"4c858cdad610ea15f77f7e1929d040e5603dcf77767d55f316a0dd7537f1b113ba9032d4d97df0bbee8bef41c8970e9a41e250ff3d63cc9991d86a06ea37134f2c1153505bfb3755f6c3f98f09883cb46e24cc6290e401a0616db1ffcc305826363eecbce74284c043f33c30ce39610b02c296ea5298bd58a0bf1fefed1f4916f43d6c2e6e51a306b0a248c9d334a725382bcc129519c144bb42938eb1f2200a0a053335070fb5b22fc3f92fad788527ea8748c9712d0ed88fa118145d8c8d01addfb5913ba3ac4476a11fcad53f83ad4b1236b917bf3235fb2b5131b28ced051667f2a80e573009e2ab23fa3d8d3e7e415459d7185d0fab8bca02a8f6a1713e2633d8687825e5fbfdc0b2198eefbdf481aa5ae69c1fa9ff64a36566b68f4f1e8651854a8a48a38b2e0e23fb755df2f54a463c750db2c04a267829d7d103a95c90873f994ab5def956509c1375f0cfc2a1d481180307e8a9c4934db618d7b152dc53e3920e85af092ac41fabb7c054b807c91ac9949b8e43bf8a4df06106f86ed663e191efa799b593f6c9ef5727fbf00fe17ef589f8f5e28eed3ed72da4c8119ac6352ddc0758d38ac05c250d03839fddad0b80feb5e804d2becf552fcde515722f387af0bad02f892727561c4b10f22959b7eeeebe4260dc2601eb994d5829c424fcce84119f2f90e7b39077b399f3a51fc70f1b43c77e805b4ec5e8ee6f4e4a4402fa9071105e80bd28a92fdc4bd7b9ca2a86f0f3d702a9ba9c5c93e0281d3c014d424cefb1033d021781f8656dd2359c12fd632e01ee4f90456a0a10e374d0750a39bf5f123f40d891037757cace447fed151bfe0bd20d735c4ccf6c315c23c71f9ff756e24b51536c6790fffb828c120fbccd2b75e3143de76109e7e608c160f6ca428c903b6d8e11930304ff68a1fb6b01e17e16ab191a23fb13e4f006"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"965edbdfe27a54c195d773c8b2450f793fd67fd69cee1fa69160a9b78fbb6d62","proof":"061fa5668399e1798e6a0761464aa540b9daff1e56499d8ad8760ff34b621526180d855bf0d88ff5ee86e1eaedb7c0066be773605baa0e4ce897d58c0dd0681f88111df3601b75eb3371d0359c6ea75a723b43e05b2427ad4359569bfa0df320740f61ff663e7f60e90258cd74f006b747cbc022e732f0be01d785b988c419761b68f90502d9dae627953d78d0351fcf55ae5e26ad76ae6aff55f5a49c908e0fb5c9571b94b1899a3dac4eb7beda4e1d43c8a2d954bc369029bcec4009952e0dcc19e6c7b2c1149f66fa17a966d1500b38a09319c7bb82cd90af82595b8d5700ac75a295d01579a69867a8685dd85ddf461d211b08ac6afbe13f4997417fce7dfa62c2f18b1907c62410044fb01e3877d472d0d60e13a0898f91eff80105bb4c92f0ca024e2799dd6ab6f2efc18727162047998356c7b2bbfa1132699c63bf2cb6e40947499134e6dc835095a091c8e38839bc8402bfa8996240eea1455c015802d1221d0c842b4a68c8158b41a41e5934d78a2745da1ad75083a955cd88a4068ae6c1e98604da24ec1583e45c17224ce26fd3a538884b99dcc05f43eae86e5b4ae2402bd6651095fb19eebb50a696141b687fc4fbca5534ef0b0b9af06f2b2fc84e99ecc94f9c3ae7cf401491c8ce999dd9789d3463990e6833d8cbaa869e4a8e3627fa3edd6883428355752c03b1004e4b0b6d3bd55c08802ae026b803a437e4d8cd049fb1f7a08706d05f067641b0e0100316ccacdfef741db0702479c62c7e71601430c43f24f1fd3d90bcba1c5c0042eba6f79e3e91173bf9d1f15e843e5a38ee726fbef30d2d97d3c8cbf9762cd66863cfff1c75d19783fe1cc4956662a964f5202b2206200f166e48bbbf5a94a3097cbd913a4fdbb3ded33710bf42012ae8b9ff8598d1316f0267adf5b1f462b594416b25c01e7551cdd3df02735e02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1c8ba0dce3a42ed3a8e26f4fd992189f38347eec3ce44ece120a6c331a46f353","proof":"0033f358deb9df5d53f8e577ffef249e0a54b4131a5c8727b6514e44725d04560e83ab66fdb430065d4b960950c49b3f8a8de769d8e5f9e871dee7b0efda8845acdb74ef86526d0a3ee1c7c1305dc2d2a99cc4063a4a10ab3f7f500ff2154548f6b100c8910ed8548f3d36fd657c1b024e81886f0c599f070473d1aa03aad91e4983ca0fcce5a3546f74ea6a5289e4c1bd8bd218d1b8282a49d7a8e228ed050da7f8d421067e55b5c6d95c6e4824936c6f5c6f4596d4494c04ddbdc0d100870feee161593191bae3d858b919c94a6fd0cb24446a06a73a25a4fe6859bf03b507a4501fc160f9a8a60a0ed0cb10b2cc9198a0793727bf5da6484bf6dcd87d6635d6450c72b68903b7908b8c46e73f157a76bbb221166f862168a4aca9a078e00622c1bde0b464b5f8ec624c55c172a1ad4aedd0b8422a00c1352c12c83e27b37ce6e1bcdb0b61c3450264459864fd00e638b0d9e9e393420a25a69948108a5d45963b0f1c7e2b749ed536b2945d5e479cf8f7110acb8838c6c6b87bf775a0786f6427783a4e81b26195a77cd33be4f22cb1dd5549061359c4631fee1879d10e4b44eb3ed71611ad8ff455eb6b4ede79885f3b62003e399914005273586b6d054b8c25fa680baeaabc6610b7cbf118d40bf678d2deb84de936fcf2a7fc8db166484c2e1216e548ca39959a96b3bc3a35b9ab9f3be6d2d6e973e3998f0757d3221ad02cd0078cf088223ae9f95745dd52d0fee6230862363923b2e1a56cba8a146726c5baa092f7a25baabe9fc88b95b63a00e9733577d27608391176f93e6ad5679874b2ae24b15b013a03d7386abfe6c47ec59b6b82cb03e3a7dc8b61799b9d6145971021a2e72d3a8ed0699b61abaeee748dbac7edc6355dcdc0cb8fdff9fb0ec9cfcaed2e40fef487b03c61dc79c4b8d4dc30b78fe3c70d0a4564d5415e1100"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cee8ae92558d25b285759021e4a6bad24b119580f25d6ba970ee7ace6489045c","proof":"7664aa21210ea742e69dd272a39b5d79853bb6106b3cac3073d53cf5dd630a5d68b550d3bf5bbcac0dde6b543c9126d02fcffe9866ab2e3bd2b9763ab283500fd094024433f27372088662ac4bd2e4c55effedd514b253c2067515d411e7f11ddef30051e15547c8df922ee056799491cf4f3a9f1781f1c791f4d490df06f62e6ce118c181cf4258e651f844f0b0b6e0d8235566d36ef76a22a86182e5fa130c88609e14366785cccd06753bf5349fb854243286943d4a8b0536545f7b2cff035ffbcdf5d5bb6a73d1eae4334b1ffdebe7096961fae69e6298ec93c86f8c41043e16f47f5a04995ae16f790091af74621334e7fed49bd407c22f9b3a811a64125e00bb2bdd5755f633b6473c73a94dc7119b085c49bbc282c45fbc4b501d2056ca1f2a7257be957fb88832aec898e94450b1dcbba90f8ff2a5618b7ff6866231fce681085be1974cc203d682c373dc18ee3b4e69709e47b2b2bac1a40d56172efa0d37d4ed26e02e332850c892c493719bf0bd9e8be58de0d3ce15f0cb0bb7037e2f55afbc5a9eac9caff6c38fa7e26ec81f5bcaaa3625c6813715443a7db043cadb0c5b7f75d43fa00245728422fa299680113a32d843115f0389f9da89567a2866044f8fc755669677d78743d064f0bd73645b7bfbf36fff4d65ba7c332b1e9c74581c9faf9bb65a2f4a92a71fe0f3fdfccd6253c01f42efd86f74d61be141e42096a4cee969408412e78f43ea1b957c174441753ddda9aed0c1d02b02de1bb02b4d20df0bd75d646da66116f760df21eacd20a7b75c0c08492a67ff12cd50ba022f64a73a855e43ab24a3f8734e6f594bcb496b316df9c7fbf11cac7f977dd838799d31fbccd2a8ad7a3de90c067aa4b3a47fb0921954c9a2ad12f1fc350272300ce0ca587fb9f6b3ed204ed8a007c5476b02eb1b1cb6a62f3934d8c20406"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"521f0e679af87f3e4b53fe52cc67cdd53ad40e331adb5dd71771040ecaf7971e","proof":"7a5e525f2752277e9997ea03627482856a48a941f75668df7f209a1a56884740404c08d28170f6b6e24edf9a9ac39fc45fcfd112300cdfee1d7518eeb11ad65dac49ae9b5287fb4a89520ad73aefef5c21dd89373df76016f806ea1729d33c0aae8d2c50481048ca5d723c594fa00d0c86b478337c0da7ee2c75b15d13960c516b46fe9013355f4761b0807c12fc73f95936ed3dcfb45a42bcccb90af97e5207bb85db16be924d3df147e56156e69789841e4fff87fdaf43a3e24a65e9b27e0188a18a44a75d2e1a25ebea4b976dbe894eb5f092725739904248aea88e7f850b08b6d506770c0ce4777b41fcc6f915dafb1f5c57f7da54bb2c05caab4079bb7daa823bcf4f6011dd1cdb21be4cf5dc51cada6b493ff5bcd21de285116150d119b23938536dd3528e264237d7de8da4d3894a1fb8e0b29256d81f9f2583ee323612610cbc2486c62ee4abce44b6a9156f9fedcb36b5660d574e4455a15b16e437b602a3b058504a562bd0f26608863488e65909007e405d4f4435c48b8e3332480a8d7695cf879a581432b33203217301b2f698d63f03b8b8616f2772d19245410e90d81f64468fbbfd4414b5fc2da95a9ec5b36980f7a7361a62f9da6755850dc6a3abe01119e13526fd1941cf34c02937fb0da97fff038752bfa9faf599cc324a788cda75c6c65e9827e807de90388692c55bdffb75c98cb0a75070e02a020b4abe9116f406dc8ffcc8aea66d06e7854da45c5d1156e09e20d0162475d1a673d4939488bc7c7f882b55c53b118892a2728f6b9ee83788a03332eebb6f2358762455965bb1162b0a3d80fec24ab06841ac9f9901c3143062616ef752aefb01198bf5fbbef91d98115d01103b69a2f02f09ee684c9f55c4b977bf0a212e106e0ed4337aa1f3548d3ba8223f6aadd4ed3a1d15814a54a8ba18593270ae88cf1e01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"30bd6349c983632f8c5fddc7ed8821a211ade64cf82e48f5ce1920fbcd6c343a","proof":"26469fe6f25c9642b97e672fb2cfa40a642e22ab82a9e5c912566dbc5140655ba227587611ce8cfc92e703ee4ba112230994db51dc9aff407a9af57f9686961d4e7222ccaba39b05719877d5711e773247071bc86105e6905d7ca1d6c4b6ea657037f62048acc3b9f916144a0ab58de6f4bc4e077ef00e72f924b759d085274b26425ce6152b37c6ad76f6c5bbf1173eb29eb8df9f39265f87ea2da310cb2d01e409ab9afc6082a3075430ca88a22086feaeee98bcb76901510e3f03d79d3f054f27d7f4b3741925c996d3bb35dd3fa38b7b845822f8dc02f5a2e5c5712535020661398809a7447cdce3a00e40cb3103620a178f29b7e482ce54fe0f2643544c94eefcf268f9569fed174cf24748bab16835aeed7181b73d112d3509f429ee18c0a2b0c8177da5fda8aa2a120b6eb9f49e850073963db14d8f82d6a0e70b0569825b7251c23c937ccbdc3327bdf58df986dfd02a3a8bfb0e2b3fb2d975766c23d6c2c10221885d50c2b7f4fc6d8ff14c285b2a446cec92fe4c116735e8a2036d324dfb860dfcb845785ba3d06ae4dd0af7f79b170fa41a54b3519d6c20755e4b7c0efd52482c72edb4ee80925bad67eea2df252d6784f574c75abbbda38485730e06d24c1e02e6fbc79787163ba6956018a8ee0c28c941c53c8a38a2a5daf379881f9ec4e930707cac1dc2900f9a7708dc67d79cfe685c4d198108af2d4103629a090a22346da49a7305f23462ff7bf6632211fc601998ff4d42ceb27381715f4c17e9a4ff71c68c656eca4430c80da29fe8c65d9667633946d2f8eeec50ef2916fa8850c7810dd5d14c104dc76b5d3ac28fd01fcb23226d892329574ae14a5b31769033d5b3261be86d12621bc1d6b8db54da17d1b4152cf55a676206a9660acb381e03311d6e933d68a80def8ac8a4a9e77ef6b696e03e6b99f52db7766604"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bc477c08f6143b23dc01fde84efcb5b601208f0e8f78227d414b3b460be4321f","proof":"e657409814a48d0968bcda543f0f56b88d36769f57e7f0961d31e5353ad430204ebfff93bf5827983617d9d7262525db440bc99dae862fd7024c951a14d42809f68a84eada831e32c254145af087a965e47abfb78e3b9d3dcdb4a345f8436a7010090a0bf1edaa2d724124185ab477d0b706eaa79690e45879013c8de6c3fa1d9efcaeeaeb79d5933f58c92372413c35c85477095376e89eb99c6f1395d9740fe697fb7f9e9cb1606cb48a311a605e4830e894da1e93f7329934d23f88b2700acab4d18d3d4718af88066314eb8017c0254bfd826995c5cc07d8dbdd1e3f5505c8d0a4cf1699b822589b343fb4f1cf87467c98a0450281d136bd80d75c18542c34ddf371644bf48e80062d925c52c399b3ebf560cea76dce7189d1472093e15eec9a84b36e98de4c77b464a0c6f8710894c4f3a28a4701a5f5e797440b543d4f26a8e6a1bb85f615b3a1e05885f6f554be0a9093142fc81f4653f37b2066052370b9782170a6d9db0a5a1ef455eb7147379c0cb4070a57e8f82396304d58ee35c2c4fabbae0784af4fb87eac89e3784ded35b87ecae42a14b1b8a52a63f5db0a107ae361ee4359f7cc08f230dac7ee4ecbd2a8df1d6786077a6918a310f5bc66c2b24e3fcaa0907931effca0d10f65e82b6a797c59fff8a5b00d22929eb434458abdb6f4a458e48f9acd9472f1068c74dfe0c2cb25c9b7ed92a650d3b8e28464c6c6c158691e46058c3cc8dd63e0c76c679af1317f262a7a697eeac95480e8063ee3fbe72ad5c76d5a32cd229cc4be92eef1ad025ff6400d7333ca7c8aeaec35da4ce66bcfb4530111a35f4c7c2d9c4b81d81ddd23798adfce3297cc202cf95d9c48e71e6cf2daa12e444d522a5e83345f6cf1dc18de2b9b3512f2ec6188f107890ef23261dfefbd39f4ba93892ad12a20bdc6e04e878ecd17842af4fd3ec303"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"de1c5dbaa590c18c054ea802cf1c5f866c43fd625f43c8df87e4b3cbd0c66448","proof":"d6f31d14ac80adc98ed044aec9bc24a920a24406680e70ba7400a60937393739c4c1ba3ad628707ec887bf20a5ccbc52a2cadbc93cf9ba9d32193cb4d1fa8718d6ce5241971df0633a4acb9b593e90637cd23421552ee823be980815af33fb0bdc85ecdf4c89a3759674038d25ce3aa9f52ee3c0aeb161c8d50eeb9bc25fd750354d5cea03a3222c5e5bf1393e4c9ebea7aaab164031ea3e31aa4e0ad07018086ec58d20a91372c37d289d9b59ae3e741e9a9375eb612ce015f2db0ae43a6a0b5f6cc4b5dc4fa3f99e490488fca4cb4881dc39210eb12b5e023b5c7c1cb62e022a23842f62b35944cd32f92c8d76d450514ac9e0c769f50be73ecfa32ebcce7a186051e4c7dad7633000893c60615a8aa77c95eac8ddacde5e58181ee518146e8cba0e9951e8082b550a65427516b0fcd8457ffdf9e432f6d4d15f95d8376b1da698f3b45bf6432f9224385f3e83007c38dab46a6b3a076346d1b92a6243d71e3c66d549dbf4ea5d87c47c8c6682ac9d09b5ef90bc2f83bce2812bfac662cb4a286735ac4f720bb6bdb87b21a13ce07e0b7b46b9f9d5b9ec32dbdcae07f73925a21f9a83c77e6251d1201314540c584b7d89a754d7e9d10ba7328b79e6f04f3d18eeccb7af0518e43ef988d24951d3ca7dcc60e7531f25ba384040c33f621566820b9d02be5efc949d32a238ad1f64cac5eecb022696fe421764edfc3d47d140bc48b17b9c91fd9a30cee42d32d363def98833e72b389fced5beca9e8cbcdd7c7a9e44351e91d259c683c9b47ac8adab8f7bc2a82377b5dd2c69f76483e2ce3e38bba4c7ef73119e7f3e65e69445616a36da2173dcfc5cb84bc87a295ec75468b4b49158c207ae3c09d53c397d9599ee0e5008181def44b19429e7a8a4604e0a2e48b2ebce2ee2d817e12a86c691e90124f0e8241785e335082fc7f2fb70a20b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"30fc972359bf66ab3ca0d90a0b44077404b5302acf0cc6ef646a7ea4fae6e767","proof":"2c1564a94694b85d9088507427536dfc52a35fac609501d7d0edda5059bbd101d840b5b9b461783360bdb48a114388a9c367ba76e8905d51b004912b871d78279eedbdc7fb69baf63ff54398c0de20e0b9743544c919dd69ee954312520de35de4026c094ceeb429a1dadeb79aafb2b142e794aeb785b417314f9fa7b0b1a648d247c181bfa62669784634ea111653d3d093208e33d41541caae849ae0996503fc6fc7c6c3addb0b92242a22c9ce62bd9bb362865f025f5f8e09c364a523e00cf861787f7926e63ce3e4bc8406003d7a8eb1d077dade110fcb82c0ceb338ce0f248c8e785d2edd9570eff47f74b1762c3448aba109809478a5f5cf79b1ca2a60a4306a89890cf7a56a13b93323a4c1ed05f0767c164df2ffe634fe8bece7a05f80219bbe46d30a525b5dd25f474e3c444bfd8667db39d90db91cd41ba2ce7a39be156f46cf75ad7dfa8cf658dc3030cbb08f4572cc5e578a8402a3003a29df17becaf31c8a46ee016c805f5d2366a09cf808258fe0132013243d21a818f9aa50f8b887f4548b9aa18b8b10bddda1f16372b21c449e299b35fcc5952ea102653b965fdb3e03114d4b0bff1da061ac0acd045ffa06514fb8213723cb9f6ce1a71024308011e974044335d564e659665c051762f54209028b6bf9c8b6d80bdb7849369bf7b41efec1ec0ebb210bf4227162f092641fc1ac69e5e38ec577b89c0430122ef9fea2d8ec5c4c95da9c41243cba28d7377539cbb319ff29a40fc38a862f94179252ddda73a6a5f70c50803dcee0d69cbcf074a74ecdc65c3f8d6afde37ff0599adea915f921537e678c0e33d5abc0034d5df2a3038c7e36a131b2e8164d53e629a917075094fee7c6e3c4892f8c552d092c3fe05bf1fb622a070d464203fa1be790ec3ccd3e69c6fef41fa9f9f88690e6c24521e98871d2182df586ef05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"64311c55e17d98191129bb3e1270d5a14d1c5273bb119842c87151c27a1cd70e","proof":"40db938f23a64b23dee25d3eb255ea248c2aa7e14d2ed212e3a15ff43e4ded7cd8803eaaab0b6732cecdcbc13c24f3f25c05d9a9a5681bd93d57cceb0f382c1b98079714dbcb701e52efb7bd51132f33ada35b996daf3bce7b4762805911253b6ecc8db3eb2ef2db1ab27dfb25a47f09bdad5937d8497e4fcba93688b722ad664c3b7819260750d34b2d37626f3140be71ef701f2cddd9e5917ebfca2303810942c770a9772ef89291f4e3d2b5be12f1bb04b9d3a1489e58ddc9dfc40beff105f51348ef529a481e731e3a27d65264f95d774ce80501ddb94a3e9c7fe1a652055eb0d4cb1b4a0bf1b3d73be10e2e09a626ffb2c30d24aee3b6e917edd1607f0324fc84cb88f777523b2489d81825c94cea85bb0efba07d6b3e53a3159851be6b0a2a142023f9a22db23d5837497188dd9e2bfbedf4935d9bfb1c20f9cc87561d825584671079c5049dae9b701cf5d4ad7604f563e22284dc4a6d09a515b66f13c899227d3134f011fe47658162558e1496ba81727fb918bb5d8ac7419b25a03f6814d145d3eb92cefc1122781ee9e437d2062eec8dcbd2a14ad86b78af445e30b2e663e646772764675ae6fc49324fe7d2dac7b3fd542917166d0c4327417d163c0db7131d2cb2abe881bd0c83dad9561febcba991b7a04cf4d67e8db75e995a3ed4b4efa1b680f5e6aff6db3329344d1ccdddda1565b2370b9a2f8a26f7110b76a08d73887ebfff69056d8636112a8c5b14abf67a069a8861b7b06a12aa450230a13202ec69a7928fb450a8e4bb273967653aa3affea67ecf1a82bd4537ea74261262d2ee3b866486d5f07988dc1a5dc61aa0af3d6e35c69788180db6dbe438c1cf287c5cdb4da111e2a2d748fdd21edd7e9a161d716ea5689884b6f35ce005eea8bffc9e1ab9963da5142bcd4db7b98fd056930ff76673201f345166879603"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"52d3c2a6227a23c05cef0c7121582b6a312c9b89986a246558aefad7bb257634","proof":"a46464f25094306c915f3931c11a27e7f7549f968834f3685797d69c3b59a347486e28e2ea4814a69eb665934fd9b5898a7db401214426707cf84dd6437c4f61f674a0de071d00afd96e39307d20c5b87bcde5225dc91df2b3e978e66db0db0ca65ba5dc7f903cf428a2056ec3abfc6c217a1b39c474a6d14429bd40ff4622325525897eb542e7cf3ca7fde493f16d201aaf67eca501dbe08af98254f327b9000aae63737ecb44f8141449a627ba8d9b997179c9ff93caef9fe8fde15640490f33a0b3f9fe08e3fa6f8aacb8c06aeca5f891abefcc4da047c4ba492ae7dcd503fc7bf827f28ff89d1e7a795d772a8e4a5ac937bbc5b1ec17e30637e3fd9fb44240a5b385788ce7a2577380e0ccd500e9e278442ed2a74f12edad5e98124ea8588c3a0b7ee2ed2a95d99d97794855d70b7ff0eca405adcb74f15416cfdedcb6681e3fed5dbd330e7dc72b86117ae6a90e56a2e4f1985e86a2872a50691a9f9c3b5c2ae5a1d36e867d45aad73069a8c53fc3a4125945cec11cf9179381bfd8077fe2999f5509dca34dda261991edcbb64697fe93231d66b6c83925c69e603ffc6d0c9043c423b93b3dacf05a9ef3ea0f48309c95e3be8ec07fcabad792bdf55b5f44478852c9e61e9a3e34b14e4a13eab113b843b281bc901768b31deb56a57a25b824d2a82cfc19755e9ef7e53c3628189f7be0ac7d948a66ca9b4facbb71427464eac7f617e9ba6079cd61d487cb1cb55b22f3266d777a0dd89ed9dfc46ce66932de2ad85e3dd564e1065b4ec165791637687eaa32df6a43156ca20c7ee1f95aaa8ea72e95f0aef8991b16d0ab5c2560f94be1b296f0626050668bd9a64a657e112e53f1e7030c5a22d5eb9fe67c387bcf6eebd4f9a357c0332d6e244c92b30c5d61ed20fd71553fd73b2474ea5bf601e4cb31372669d803ecacf3abe097df06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"149e5d4ab138aab6d19fa98db0e0823682f684a0c38bc2c13ee9c6013d34fe4b","proof":"5abed2f3d1f0090f01760ec0d2e8eaaf3eb4a3c90301a66e4e899bb007b3233862722748cf75977e7188798f45986fa8e27d8f28e7f9340951cf9f2fc986e113927e874c8d9923417ef223513f6a4a1c0bf44f0a0f124e300ded0ec6fe7c3610e4a7df8c3b569b368a22b0dcb0d688a8198f6c591e381bf5291faf51b151d319f3e7f8b8786d8b53ae4a70bb4a2607639d0ef459f3f4bb13a0bcbe0264bc720f6fa004d615516418f96b3685cdfbee60a241279f41db268e06536b0904e9250fa0253c7dcd4e8e221b78e936ae5e6d088443125d20bbc2c8ee091222fb202700929b350f928ccfb480ce309399e3aa0064a9420d822fc3b4c918125dd7bf571e96aa11bb707750f523224187ebb12fba87aed4badd52d6b3fda51bcf07278a7302c2fada57fa87855b78afef7c9314b7bd03e017e4f9f58586f65a213475cc56566519aefc7988364aaea5078f5a60c88188be124e4226e6516d22c419be7627dc13bf4ded92a3c2034b8fe6f42d63a595b92fe81f1df984ad1b7051a1b9040e9c0f20997c1c17e0aba8eb87de69b3be89996c229931b5d1715617cd58052d5aec3ac0e338a978c2e9ccb273daacd353b4e916cbc8b1dd2010352d826f39cb429c889537b8646ca11d8a4a5372a694de425d352633dde5dadaf29076c2d5a015a21d90d936a6eb985d58621fa79bcbab880dc613dffac627ce5c2d538762ad0b647cc81e6db4da682225276054cad0e2aa2ff5beb3773c17fbf99e0e13e698735e87e8484f5e73fb7deb3049edd27eea6c61d01fe5b82e97f4e58c0f092069724c51e2d707af4ae135edd31f5bcf1fddd2fc763f1807f16058b900c63f82bc1a0c54a2b5caf847425cc06136139c3c89a7d5d0725f231105fdd2faacc9a76b0b5d19a59ba1a6ecf52afd1b92a0429f38ebcc37732a9f5873022a0df9e59ac000"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e24c853c55a4b608278d737f96a4fc399eeba765cf6dba669cbd947eb865f809","proof":"90d055a000b388f5ebb887428e269b451e7e3f6c44b157d8a4088f512797312ce81815d5be2ed8fb127004fb0155239cce6a0115501708ddbb3fafc0f084e23dc0efed8b89cb32c50517bce1aa9efa8eac372ba5a78226024858fc9abf59b671dcb3f97f90d555b1d20155599656173f94b5cef54c57250d87748dc6c3f50d27169257938ea579e141a52102816650e714e7771dcb1680171136805934770609104d63a3cea32156efdba30a710f8e809db668b36e652d143e09801356dd3005c90017cb98777f48c476c5322650c764af8887b2a89861c71e21cdc8129326054cc6312e138a515b15ba6a32b0957b9f1024c7d3d06de42bee8a7ddf79cd71194a316b10d6b2fbf7c3c264c5b40c6e81c225f9f3485bbe45480381907a27b576040b2559151c0d9db9cdb3023cc783fc76449114a0315a64d76d6570ae560b5f3c91bc59ca7708c1cba8c1a216f84b1c582ba079b99b37f080bab6f6abbfc8262832bb3fe9a5781f61526176a698d5cbb430eb81512b28ecc1e32fb81eb66107a07053798d7b041ca28e3b8c55feb72a37baa14b4040542663891b950f3d95725aff010ba22bc8f35e7cc2682d42f2f8f462bd5b58a47b1eedcc0e5f367c3500b8c789ac2aa090dd2e1f778db4d5c97f6d01f39e542d1953769df9d569a93534349537a03f6789384cc3772e85020fbe44557a23c593543e4f3df70f7c48da30f426dd501e34b829fb8d065c80fef7850b90cc3d0c88c0725339d940c07c5c344e5f8c7e912644e92b54edb11fdcb5f27bdd40f9aa05ad1fffe16c4cd5c6ff2522a368d7dbf9f61cc88cc7f7ae8f945c0f1b1d83affdfc2e59ffc124a24c486f48a422251c2ab8e6d83432a22daa600c55ce8b525fa4ae45c3632d26e160a60556af875852de1cbde91731e3954cad943492c735fd461227597244480f9f9206"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"441e6459ff0d92a43e448cc4e2e2c28766c64d063eb2a3a7720f86c28356e11d","proof":"ca45756aca99e1b05d46e076782eec37a5ea840eaf5be2c42232de4f82355a66a29cae18d73c04df28d5d2c391e4a9dff454dcb6db857df0d4fdd4ea81384f04aca1a817b421c8da9d63b78c0d15097c8fc3c5ca620e6f969f2fe028a9628710e40a32a644f68e1942770e9464d395183dee14e97f867b3c4971dc4f76cf3b0eeeaf4fafd9bd2e09f5124a899d0dbcbad44d0ae0c7855ef3336c04d98524c00f1f9a6c9775c9f38308ec1f2c583ee0bd2832e1fdcbb57147dc9fed761b804b061a835f3aca8a4806ceed5a564db6f4db90813f61d868065f65359ba44807f80fa81de07503cf1c6a64e17823307bfb54bd777e16fe185171a24f00e46f5c5c2b44fa8061cedfd130630a2de2050e36653b1fe733b3eda6dacc39d6ad49824216ca11cab4def9b777652a6617d96085212e71ff8c154ceb679d2eab90be7f4a3618814449acc6ee244dd5536f0c0bce6606c7b92140b5602536445614e5fca637fec2b596f29642a86aed81338b64f65efc3cf903d3c3b57780e6701efeb72d63268bc8564eefcda35044d41f709c1a2d5017fbb2cd329d8ddfa0e9b8d65a2e1c5699259f3214eb278d1370d4bcac8adcce6b413b0f597c5ad541b13ad0e8694e948f8f06cf2a0979e76afd13de045592f27d9feb01d6b827ac3bbccf1d8c5431805d175b19ea4f324c3bb474301159e859dcb31235e30acb72903c3b3855260920920cc55efd8ebc35e31816d599f3ef08838744f1363cce60836f45f4e06878d4d6a23d602e88fafd0803556bd7f9deb6e1c3a98a38de2b3efa57d2dc789c5dc0504bbc9cb2550c7da84d9d93d2024d901d409193903d1c6f57c3823da8595e87e5a0004f1e388ecd0ae56b1176df18769b4253f2c47b6f09b5eb5ffaff510ec8003c39fe86958bf5855eeb1f9d1eea2a113c069090027e818d97519c4dca05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aec6b4504ac294e3a7a50251dff177cc2424ca92ba797bf02bf438de149beb30","proof":"621fe681e267df8c09d6de8fe10ee5868116c620c446535de04cab87b980403a66c964569c35f924b61455c10d7a9fd2f7957eaddf4b57c7c6600831d9c47f036eab44faa2a1834099699c254f39356cc73bd497951f10c781d9cad2f2c8a914460405a7d2390c800b054a03673db2dd5a5183466f40c8e51640b7e23c9ce120672ecb234178a349fa1081def26be170b35a930135261ad705a061f588847b08fcb9314e7d9f4a5c7e23b826940139d777fdd46d5e55255e128f49d0c6dad502c007c3b00dc136946b8749b74dd9d11bd95b4be13fa6a55ea6a68645eec85b0e1e6926e25cdf65ebb4ead77040659b265df859f4c6c6d177a3c3dcf117956d41e0c4339439424a17a6e422a1b53f34b684848ee44038525123fa6961e6376846306167352e922d5fd236ca1a5d3e4b07cd7c0c052c0e3370b89c2fe20c0c941f2ee2bd8681040d024d76f9f549f0bae14d0c9a714a7616ec8a43f499f9cbac53f231b41ed168f18802175e77708e65804aaafa6f6b5bb22a37c32971b410101936494f085781541d2b601786f50fbfe1cd83e3bce35daaf5f069967df198ac39bae13578b9c7ab69db27f7294f75203c7b49cc7db21415c91c1b3d107564650b46ca7f991901036fc0559ff55128e540fe2f24f6de865b7fdc29b539b5cebc7c9811db9a259a78d66edf9f3c423b342a930fb03443f4f71fb22302d1c75ea40c82d120d5e7b28305566323918c5f6be3681887efc8a1e33ad64f9a0bdfc24d2aa8bac7ef21f507d7492aa842f3ab319bfc66c1f1e6e4cda31005d56cc166f96df63cbc49632ded474808d283575a1d5ecb0933f7d77ab5382ad0631d5b2f061cb1fcc3c8b54cf9d9f3dfaa4ddd57bc57c5220142eb744055c90700fdf3e1940916e1056cc7fc62e24e3c203cab1e7a32457e9afb34d82f97056baf6b69971c0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bc46bf0a168dc855835c3ca5100e2b60958106bc1eb0b949d6a2ca8d6747ad74","proof":"e2e62943be92b9c5bd4ff5020c5f329df2e36c227033cffb6f02662bfb88f451d248b55245ba2e1049ab275eb0d186669b0322262a9282c5ef28ccd1558c8355be0ba54c566761d6bdd526bae99e53a6b0a9250340b8aca6a2ddad404596875fc611a717948b78e886650c8b621a51cead425a55436cf7460cf26e645679d70b0af7c86a1a0bd6fbc02818aeb9de66eef1e6cbcaa4b43288b321450326ff60058abd7aafcd935f7807648f34135f0a022af82f5a23a3cf930f755f7aa7a0b108f74ec3030bdd17f0aeef6130cad832600f09a7fe14c458a4cb5a1c27d0f176016cedd92a12bde9980f1bd26cf59ee151d661d5e94e3d75583eb0b6b5233178508cb4d4caf8bba6b4790268c2e79859f511c314921ffd91707720b87ddfbbe457e25017198a3eabd7fff438390d8387d49527b8099568abfa63976a6538d6e37a94ad48c7e7d7eea03c2686e43a1faff9b74a194a215c1589e9e16dc9521e267712e981834103fc9265c3223dea0766376942beecf1b550fe71ea4c1eaee56f762ae062ac26d8c50a8040bfcd202a16fe6590c49d0d2c958093d145efa076d6178ee78e6d32b8d426fe0685f1fbb81a162ba05b46b98cfd3d2a51aba69272ce1c684d0d37fdd6295f326039abfb8741dfb4f8fa09a68885d61f156541f137835b32acf62824d931208b33f1bc468138ea357d20daa63460dfc3fec33c4908572912b6599a57d27dfaa31fefee824c9f999d8154333eec57cbf26d443b8602cd35a03c6dd1f147078322d105f3c932c5c87ae773fe1e6782ef6d5e711ef8eb4a55623cc8747464a6f70b437f1ccdfb21ee8b5ad9585cecedcb687dac48b4e1801b282a05aea27a5ceec5e0b43d0d35c7022d0c5358582ab5f362c6450a46b2960f29d9bdb6d633ef2fe6e9a594225bbdc957de36f0fe46f4d4d7570bea87f01c02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"84d3397472199676554d0bf25c63de73a6e61d418807b388dfc47920c63b2138","proof":"7c8c0c0f617a14802b7cfa35e66a707fc321fbbd865f01bb25b4c750087f6751461b7cf8e85d70db05299f6e98d56d091121d7b8d5b35c9139f03963c875023418b6430737a5e0832b003c61c5a0693ff0fa6db2293c9a3ea9258900d171e754588e8cb025e1b14f36b6479fa3ea4cb27350bbae6431de08bfde295aef0871016af3b4b90745e1a47fb2278e5cc5e3275f646bf56b41eca77947f1c8a943c00af12acc52e48c97292c6b76361edffe5bf2abc9c91fbf1e7cb82b439b4c7c370698352b6ca6b1fbaceebf87597a180ff9580b6d05fa813e7eedbe8a56a4bc4603bc9e940e5f3220e28a6ef4efa5d4492808089c4eae1f9ad158af027122d6d040b2357deba1ea8d50a95f2238ea6cb5602a86ca3da50951d7ab32ae8d2bef1a2e26cfe78e21676d84b7de0e7d1171618d9c1027a62a9c4f7506352f51872078249c6d19ac2d5658765129a7f63934fed01bd1305008976b8adb139a7502ea3c30daf48436f15a84a715b2865bf725b86d26af9292071f643c0a3ee1e74de3504c083f63a5712ac4554039e5a62816ef2ee82f0f01a38cbcb8d0d9731fb3f2771734b0f35b1e46bcc6ee1ec1770bddb879a38ee54f629bc6a6e6cad02908bb8469966ecd831484bc15a139d0f8f6fda74d68c4ac579f066fe6417e2871fc687e786e8980741d26ddd72323195fcf31cb0886e0f51c9d9daa272e8c448b7b608d265c3eae18a2f893e9251d2f675ca01d212481ee2a257773ab2451f2462a6204458cb19b362312c199761692de4d145edd3a69a49d61072b2cf01cdf462b754b42c8c9b6b552d918b9960f5123ff596c91ee8079a27208de87b06ed345383c784b9bb9c9ea5cc2d5af0104e44b84360fb1a555781ba00b72d3ccd3abf73c99850bc720a51bf9516505102e5487624e71fd20bc10d34b4be754ca0d934823b06605"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"26fa00190365cfb32507ee8c2eb361a07684bbd19073e67e577fc65e4935f002","proof":"0015fe482f455f68e3a24b84a28956453c07b3d03d921849762e00b97e7524236c95e74c7eda49f93e0190036ef39c1da63b8a5469de3cda0749b0e01b528e78e4e1d039631c0477ec291a3e4f07847e9841600f76f4afe5242e9803e2f2162062b65bc42d8d0214695f6ef17029ee5e222a9d243a8a5046a9074bf976d4a86e684f4a91613327b7c952171c654cda68a35ce44a5d65b98f1ec24899ec66d80881d01821e935120e12c982880b6c1ff182b6347aa97621fae780de5ac8be8b03b67a176b91504960b566f878d1e807ea4bf0db9ffa9539502490ceba0d4aa8040022eb083d75ee17500f803509e5d86c048e38a843ebf6dbc527abd9b9598b299a54ba0b04f4fb0878177551450be879c247f092ece76faf696c79025037c96d78ab700f46b56d04a16fb438b069030d4cbd5eb8828c16a9bd053cd78a6d4d4308f4abb9e7c4c87e8b06290257072bd2ba5d33f7ba6ed9a43c39043b1d89f400ce55c0da1fd3eaeee4bd2c26e729c8b01ee2d4c3be9241550aa9aa9a18d4bd64a0c75caa451371b2d920fd352f14cee0b6eadc67733644204ba4ac83ec2d6050fa3ed4a51cfbc046aced2d567d5085a72b3d4cdf0bda8f507f893b681a78e100a08b6f0891bdfa8c98a614774cda49ed253cedfd5b4a5d9c88f068f34b4de55e76c3da00c182c8173e09a61a86ea24fed71b1cff02c7f97ccb2d947735a34c79584b7860d537fa50eb5657d203236c81a182b0c6c26895a8436a7f3a2150e3227accfd91b4e56236f7709a46889c36831c42c98f86ef5403e78827db888eb4361eaf7f32d68bf2270a5a4b226929dad4a7740651beb7db691e3d6fba25b38226950d4078addff941057e05dfcd879cdf5591e77790ed7991b7c4610d3731950a35418b21b8736db6853e7a32e08d577fad216ee911671a6ff207cefed3333509"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aab8a029266748de8fe1f54d53eca425e09a5319c0016a8b0b870c13595d2627","proof":"8222069a95496bb4c424182eccdae87ad2e913c6567c2a63edb39307e2c5af562291a72a2858019be0adc5c7ea22de60b10f36bec7cae736b13f5a56c6a3396f92addbf70ac07a198b17068d9c75667b5634bfc7dd0ff345c836355994ef8601e817fe4195b98236822b981654b178486ef1980e89bbc00191a34e8488195419b1efab3890a720bfd83a53d01f55f7e9f6050a4fd69cca603302f2b77e559504db2fe7bcf9c110b3c7c9831e0dd46d272218298fa7be7c1eabdb0c3826b521012892fd49ecc719e384d252e3ad98a62d94ef987776e4908e81046700e4d3780efe94650fb20c9dcd3bfec9414c39d3f664fc6c5950d6931eacd0e9857a6ba21666a71ad107246b095a7f2aa7dda0a5c7be3db523165f3ca4fa1de2a86fc97e6166fd4c83cdcf94fae928aa60a6d4cd2a51b9db02654635e24ffd0e39283dc30cbe133b9ac137cb3958f9dc64fa605b938467f66da129200952db417bfc12c26baa334d8018fa0f6572d20decd3411ad737aee4815993c061ee85a9e3e848bf3602209534bcddf2f9f58280413ae86897cee5d378657e9289e4c05111207127370add3f0ade8d1f3f7be4adcb8abaa05bcf29f0f95401120e2f4539dd837325690619a16813006d65331a7963f1b8f1c8445fd46cdea8ef0e51118bb98bcc0d18c06ad0da9a284425152a2aeccedb6056d68a33ba02598b2aaa2220117f5719392683594073448d7df6518d6c803048e80a48fbf300113da70198e823e1a0d5435ecd32fc495acb02f82e4018c103cb2c320dd11ab0a2a74c1a61f87277eb5639ae373e9314a6ffb5db4066e7935f2607bb2652f70ca51013926ab02bc6918069189cb0de4b633613dfe9b39e58bf0433e93a2028d370ba628283f0ed3ec5d6010fcc3dc06c2ea040033e763f472124e375a4374cb4076955f557c6bfc8372605"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7859a4e317998f2767370f0b226693240e78b1f0a15ddf066e97eb790915ca4c","proof":"7601a7d292d93f1bbfe06e99fda0a53a00e0c288ffd605722dea41a5d53cec2710f6e528240142aa04680d73ad282bab0ff645574344b9d9246b5fae121ee45cda242bded6f75f8f431efe3965c4d6a974eabf527a16773c9c75ad55fa42a01a52a495f1fb65f3be53a027a3b5df8707e11fea324f631e5d812fe794c2f19517771f6c6608605697df87a56effbc1e34cf2baa5084723ffd518fc5d8f3ab3b0188e38a09daeac03b2ebae69b9fbc8c3331e0ac56d4b4eb882fccafa3aa60250778c4ba03d6b42cfe0e935b194ff53e07e95d7a37f6e42f519f20344ce2bf750d542987145e45047f04607eca5c4a9f60906d4456e8d06d33a4c7df68680fab734c3089b9e4ac5b4cb6b2ff7058154c2a485a4e9635d25baac4305a8b65ce7f3984eb520f60053bccfdc4cad86dddb9c4bd2d43303c92be11a059554f81cee46766bb4eef614a571599e9bad1d4252c3869e61a9effd6d51f0e7e4b14d89009238c1d23ec3cb7ba140143e83948f299bdc757a0b290ce7d13fbd890820d9a16562acc41160c25245cb005cde064f3b8c40a8026c9a42fb302cf5127f339d6b165745f39e8fe0883ef4c1a636d61a4aa2d7c874c348bc631bf417186028447d74bde7eddb7bee5f09de044d91023246a89670635608dc3e13be54df2c53e03c610e248969c961eb49085fee703062e318df4b2ab116e68ae2b18972c94b0e3f304fcb5a6a15c0eaeeca9b2d22a40830c528d63cbbe0025bb234d1d9ce13380d962fcdd7a2868b08579249e07126c17729d60153d31707572587694efccf557dc067260cfcbc698dbe5abd55134a13782a0ad96c0b278b636181d642bceff49ea0f3f9f837cb2a676e120850aa40e3b457989d744bd2ce3387669fc8e6682fdc90e1b8b44bc3acbb9ce6bde282697de850cdb0ca86c7e92ccfb739fb58444acc106"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"76c06963688aea2d5aa13c1cfa464497d43ab2c620fefbb5c987bf2f0793191d","proof":"0cf0f9b597f36ac164a4c7d59ea4c59f45847f320de3c757252f08a51544d654fa2a443190a7c744d9adb994ddc97ecdbda893b79798491da38c70a4975e890c8a47ce4802e6e7835749a00cff3c914b6677bf865f4c607f7ca050dd8b6f555cd23d665feeee2840a86d5632a02a0610bdcf25d32dfb7a02705fa2e219738661ece3110f23f0948fee639298401e31bc1b5e7d4b7167af9bf8feafd09e57780ef7d124c402cef2e91f6161cac18aa152705484a58016e75c09086d14dc3e8403ea1be3a2d49e5c8d41316d9bafc7c2bad1db496810bf1a2c97bc4e63a9dc3f0c70b18bc1ad54a106403dfdeb76e8c6763fec7044b0e4b9e586a7591e805ebf625c9e9010f097ec722b9c25b32ee68f6e342fda8121f998330c2c8e4e66ca0a0616777bb81306c5f6da5d5b275f3c6a3dc5a05af55326e36fb2a6f34948c43401fe771c2787d13658fa066bbe01985a396aadb665794bbffae367998305bd5028709b16de41dea52078cd90bf42ce16841163ec7c1db1512e04394131e8793e7b76acf6021f93e18cae2dd6fa28c9f9ef505eb030fc3bd0c5c81b582c2e10aa21346bbfe8abff0e141cea65b44ae23aa10c70b0a74f97e227ad80694216057d31b2642a74299c9ceea5ecd4ea80e7d69bbb9baee3873e2fca25eec00feebd7a0eb4aa995d2bf019efd8c3dea0747f49df708adca54d61e4f0454cd6d496b99762acc3036a7e1c2f6f6f7b31e68e3940e86f0b444808f1c896c6d189a7c12c3f7d927398fb8f708ffd9cbb661524460bd774fe47de68d1ca273ed00f4e775dfc5764c8f918fa5af4693963da0ffdfb251c3c2366b1e80b2e8b67f54e75d2391b535061f44821db037de4d383d67ef3a8b30348eff3416b350a95f6f9c08e37d00dd098e2ffb41f1e3bcaea54c2fc6b089361a35af42ad2b459789f08d39c0e1804"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a0e035f6413f4d126fcea4f5ec0cc716aa78278d24d4a8ef33be7b204ee29a71","proof":"6a2423fcaa6db3e2668c269ac75d0e303f0b1d0f82ef18aa014b0f8ba0d9f51dde056e8b085d6779dd258c4e062e36e0495ec394eab86f4bb7cc017fe75cf43e966dd560793742abc75e3be832328e7ff06a398b528944675b3ba77e8630821216509b061d77bb1deeafcd8bb97737ca9de6ba083d9801715272b92fcd911540f8b57f2eb727dbc817067690c4b9db8839a84c3430ee52dd397e8ff607e99c0f65f2091e25e4e27e227dec37bc881333a75f5723c80bc9b99df69d345520c70f49b3a6289b618c2d8179492ffbaacb57e5236193f7428129bcdec41697458a03c8019c74907bb1e5df7df79cb6e587fbcc80bf3cecaf122035276cbbf8c6c2096407fd5430b229fd7aaf9148b7a174fa7a4b8e6d4cf876a84e1bd10d90d1241a1450f14af6a7f12cf867b65ac117b1df8cca28b2297c81abf850a4d9f7234e175c72352ae219258f25283d4d9d7a47a2d41c478027ddea844a41fbe9b891b13bc21214776dd5080b15d59eac26464885ee9cce88bd9e7233acc9271d6fdb2218aa2bd9f4ed67ad90547b9df116d50b3ca7b169956ca6d420ddb9f0baecf32129689d639dafdfec5a124088ceb55f987f3a825cbd7ec9ea935ba612cf56259621325557a05d53beee4a9ee9a6d755dab791c2d02b290a588b726fcee8860a67409edc1134764feeeabccd763ad612456cbb8b6f6e636f6cc82345c7afa3083c5e2cd196b475145476976374f7df2d8e7ed187987ca5dc61169dadb6a78748092f50b00c2ae3fed2dbf54693dfc8879d050396d0d9efd877d8720e927546cfdb600092754d4e8943671d28ccf325ff7a350de9e75b4787dc97d07c4382cdfbc67fe92e913903187ad756aee5d8e426b987ddf66c24ada8261307168e1335ab7a0f0a751b5134fadd71f54ce72da141b2abf6926a7ccd00065dd1b022914db5d70e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a2438202731a8f69815c1639ffaf81cbc5bdc74b0ce5460dbcdfeac614281e7c","proof":"082699303e66761accc4e773c83e0fe5cb3541a202a2440c30772335bde0f92d585f70151575a57f48c9c6f5a39737edcb31cb16d3fe24db0d20d3d602803a4d82f4749abd6bda9099b178a2622ef83ac3e463a42cbdf07d4915adf958db3a09dc8f630913bb003f7a1bd8c5a062a61efabd936e52f74858f2f7776a5baf4c0c49f918e0aa80672bbe35651a05964a95b731add285ce435612c00781fca10800d6651cc01a199785a4b24f4ed0e7a6a23a64616893ab345c843549c0fdd9730e8ea9403546cbd911810cea4462bceec2e436bcd8db3652ca378bce69ce0d0c0e2800731c86b933b8387294a2d4a6d60e32d502d320c0b18a09a8f095c40fc55c5409d294aadc32961f490006d7cc9383564c76484ffb7ae98bfcc0577529ff034471e0b3afe9b79fc72599a73a5df8f6c787ef4f539b08d4a9ece3d55927cf223eaa7e130ca409c1f5e427f883c04ba0e4fa0a84423d414fa871a3df4816451baacaa30e7c48f9e0af09e9c5fe14d0ae442934a0fe7fa112a014d1c12f49764942e6c4c6bea2d1c3fce398991656145f37c88c692d82e47f66ad6c91aa45a03e4efb104ad10ad94e48ff9a4f907dfb245032f0159a4fc4155034c1c4e7e44d2550201ed54840b50755b22cae940e4aeb2ef38ac2c8e6f7a5a88ffc601d459277580159b4c110f481ad055ba162446c1300819f8a34e243156e024a37299a2a587abba6832144779dd964425e11cb38de03a4bd7258d622d455bafc935c47fb7b2c588ea1ccd93a88681c8248daf58e51954b65a6da8ba693f60a516c66e2bc42d65948fbe64072de8cadeac38fd0dd7e46c887a0973d26e9e946055c000860314953a39578f5f265cf123227ab3df2a3ac500dfac56e0a422b502f13c8beb400b15a6a705cbcc947affab7babefc140c1512d83730abfb4a0471ec9955999f0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6601c2845fa8ef588162dd56f8e92f9ce819d418f7a7cd793fcf42577715a213","proof":"f22c7ef98dfa2ffd46deb4d371fa04292586493b411dac5bb4bd571005ab506d44d0e96be4d3fd13e147e42d081bdeff34be178d1b4cf631d7af9a65605006240206510f042d8a5e2c9524e4faab7d465f756d2b30429d80ffd6aa99fcd60046aa039b43b07a245aaf659653c54c8b463e285bdffd1318a62dcc7ac15d4029128f90aa91346e883f3e8d4e580a4d6ca61ca7f459c21ecdf045498ade7199060cc9ea0c018e1b50579a694a7f2237909d6672e876160a7d4b726a35ce4b3df30c2a61d407cb90440c54c023f7403d4195e89f34179ad6f18610943da1647b1f0e2447f9c2b0e232f9f61d92bbba0ae1e9eb03551ab0ee49d65070775f15e7f17cf686d9032a0ef83e96a7de5269ca65eee34a29465843dc637ae7ae8c786f434a3623f2751ca1d2d27c60a576bd9ce3a70aa94a26690b58577097a173d2aabf56341bf7525d9028de8c8522743bcdc032dffd9da06ebf1ad7b882da6e7e653c67d0d73a538bda82d92045c0cad9da21ca2319f6cbaf6b11d267fc18b6b2517639aee39bca0d28574ef7f6a9ff09a64c28f6d9986086448f949333c1d3ad5e1f3db8f552aec0100895f0645a85dd0ac2933880430fb0154ce10cb2205e6921c14a16e839176237c473d937a8ba69d4e414cfeb5ce1b0d8c4e8513240a0ae99652adac0d3fbb4734b69713f76694de97b6964b186ec4c843af2e8406aa0f0df9e0ebcb1789dd4c310dbfb0897af982d7851e3d0c7d0da797038ceb035fd0a77e57324c9b79ecec55e626d3e41c745fbfe999cde531f43f1963910096a7226092126029d5375aab516a7df07004ae875256569afa997463df930a70791575a20463d862950e29c1951d8cd5cb674527ac17095dd1fdc774f90653db0aba52e059a0b4801cc57590f705ba089fac9a8831dccb773bb60564a028174eba76c458d290e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0c5e9897990f00b01975161b2787112763e20f70cba31e682d8ecc15faacca3d","proof":"407f5260ba905da96d23673569cb75ab9f52bae5170b59e3ff7f5fedfb093454d6ed17d05d5afb56f26321dca14484da741c656d3e06db1c1a8d9de5deed0c176eb8376ed787e2de891caa8eab808d9c859f44c6837da6765c6b1908e9b6f53d94052f52c1eb02f8f77d22e3f9a167792522ab42ba67d1bc988c9045e2c06f12a4cc1ac06a13d33a32738e0451574f07d1bdf8e1363d86ade7a4a5f2d8d669031758a34945ad11eb3fc007dd3e8a308f0e14816fcf2f5627d1d6781dd59378043ce2f7cae542d5db0a2dd179a20ba199bcf2151ef19b7666fc432e42b3356302ec012ecbf1fce0c18d3aaae7234d487ee36a60c5299de8d25b1b495a81814703e42174a76498c695f0c608f74dfc27df9a4ae18867dc18c4d901c8de4dde091b8cc8ceff645c8bb85b4c519310b6f0fb90d382c50c7a5097aba073fd693a01504cd9d5c3f5fd80ac898356d85c72ce47d7dce9ca2e3f54ff1cd61bad9e6a16667e8c05a70b96a73af0892935d13cdc0d852d6df291e21c15675831638aaa61400e109e090fa0a0c8fdb1ae1b85e2cb23a7b75dd8f5880bb36e23462fb90a024f8417ee10b9457e3d8506f855b36342cb8dba5311c76aaaa79b783d889bebf979280d8f521e272dd08b7afd9142793eff795cf9e4c730fb2380026071a993b529f8efcf3ad5acd8c13ee73f8e93b47813a36b8219d410c8bc952055ff1fd02542969517aca32d9a36e8efd7f892a92e6361e48c10c5cc097ad2ae0ca4f99d3449f802817a5978f864a12301a8926a9acc3a53f103d463315e0763941c312ddd1640a016d111c5528fb526d4c2844bce1191f254973eeca73ff4396d0b60624c65f59952620bae73a0c761f18981190c0ff27432b994578d0f8ec5f668d285da0e8285e096d16ca7c7f2964345a619ddbe4b50bccd3ee57ceda44f2f1792b35002"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d23106e4fec3ffcc93ba5c31aa0440ac19ae5f6bff6e24c010a07028c656016a","proof":"a62b3d2740f32ebe7cf65c369a2269eaa20a72821f147d9643c4f1262f20a17640268d16e8cc7634d3d2240f421767f37997b29cc661657a0f86af9a00255028645f2e80dae13846d28a17b8e6606ebe2b41b1824d46f064194b8d951938340222e5822ebd5def021f00f949080512486add95c130de925148f24736bd8a9d0dad63b0c2cbe2d401e84a1dcfe4adc55ba401d968487af5005b152276d7bbaf0ce16565b8b52d2666321b084f89563bcd7e2f5be86ae6e57bd64d66c51dda2303944adce6a69025475ab3699c51c1ae14959e3024b0cb82edbf1b383f47e00a06b2c6397edc1899a89515f2b1bd57add4667275388a5a54d9147e2fd782b49515842484ff9644b54d9b1b93cfda0339ae9ff3a03f711732274aceec661471b348e87f9a2ec5fed13c88f453e8a3686992b344411f4af9acf87b4c94b6dbcc4c42368b37c91ef0b35d0cb4c8b5dd723b3bf78b818e119d91285dd92f56625a2f5e1628683c3a9a1c09467cace4b5139de1d9fa51c1c4c008cf71fb2210a3c7244aa6b96f1917692403370c1f368ac6eedcdddec637f8b68ba3d0aaec689b019056349714e83537b53a496befd057f166478cb61b250eaee9ea28bcff9f7658234b2ad389a44831edc86c3ba2ec5844b31b869ced0217cefe80e327a264471308697ac674f042c6309abd9c2b97ecc4235c5ab8ca06f50566638722804ff1df2d25845d562ee0ff96aa455031675348cccf13f1aa938f2273cf0e43e64365f26f2978c20c45ae16255a50191086c6d07b9a6ba85052c010cd7886b3b532176cbf74fc97ee50fb6b033ef0e0bf2c979480d26c5d27d27a9a5819832392140573556f4015bbb0d098ca797d315bc44ef92ae05322046e4c8237f1abe5840691136704fe30ad2d03ae78f72a0eb57ac1d9d8d802754f387129874be9c09c133036ee04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3acf3e66f49ac396fe7f286192b33d75742b1d74294761f89a55f2b7129fab09","proof":"2abdc1e8fcf0d95bc921fef0286fb09ed03680873dca69909d6e867e6e43884c16521427313de73b239e6bd780947984d04736ff5990c3725069047666cedc6254e6ed0d3e5bd6ff9fa9148f19b108e7999385bb0cc0b5e663f13790b7580e42e4abb769442106d37efedea3d25dec4e79186261c867112e918e7a3a86b9024e06480bf768e551917338729815ecfdafabf0c5f0c83e9f0278256b0bbbfef80b888e303a8d251d2fe140b76a3de1b4b94dfe917ca1b911db0b2a26ebb1648f0d213126afe09307ca5b048023bf2df41efb376fcb792cdc13334851ab55b6c101561de922a992a940467da11de1366e6f084d992e2e60ec82e2973c91e97a791200481e6a5ca2ccc216f47ccd3c73db6bca6c314810ac4650e1177e507f14f015446a35b356a74b70566cb8a8d4e306ff490e0d507db2ebaeaa55a6675c03f174ea2d855ea1508d62703621d33cb1551f128a16d4be9fbb1692bbf154ec050d04aaef7fc82f1751423bbff23df00978cf0606dd72181144b981ca963cae8a8464b6dbcb9f5927244e6fe1aa07352cc5a99c2fdef476c390c90c70fff27264df1fac7488690b5a632c3839a4e8a81b71bdc246314e24f80cc20a81f4d379a67d0a08b25fef55d71edf5a13625db0879570e0c6ad526a5e81347eeb1ddab7f483620cc6ca03e40862ef6de2eaaf39d92caaecb59685e84cf2dd69ff585cdc1f565d50e3523a94fe24dd5f958b279ed8c80d215fae18a3276891f8a16cda3601f6389a27064a07b68bc7d5565f8982f0f5b443dc761989bb43adb4830236ee984c651244e42ed1687a9396f167c5701b621ee2f677922a6307ac4d7dab230f7ab741bb915300c845360c4a2e451c5c387b59a82397bc8eeeea2825b0aa82cd94cd0e50bac26bf7839ddb738d2b6c979d13a68f62f2bd4ae6e060676c09380c0afd0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7ae45e8a3fa3244c1250853c12e44dda9ed38639cc76bdcfdc7b48e22f6ee06c","proof":"166930dc481f6282e90dec14768887d3e75e77fa10a3a2046c01b03fd54e696decf267132e63b75a71523db01f01981647a99a74c3c864654224ec34458a9d196a505496e1643e8e37686ee44e0eaa48719842792800e2fbbff2dfaf8bd701683656dc57fc058a88a2677c0bd629e6766593797bfc7d1bdeb97ef8e78acf105008040f43ccaea10d3d04553053dee749f9452513d8ba57c60acbd7efcd4e86033135ab177eb145330ff76aefd53429691b8eacd3bd2717f93e08e55956c2e6022f8e0db784d30e49f1979e97cd15a797fdc0e2ee2bc24c334440dfab90d17f00f2cd65fb981b3d4613fb13b1ec8dac9be03bc31627cabde711cc4c9bc72d6733122feb600f720d978871c9bb11151ff4299ed791bacfc2c942cf5b734e31117510e3b82aaddb8e0131000e398b83c39b65e5f2e68e0b34aaf32218b3f0812c385e5863ea7943ab9b3e98833e1ea28407e0bfd17e938165af767f0987cea72a49027efd37e1bec51008c14a1c100f1f79e490d13addc6fbbc21bf093bee8c8c70beca5ed26aca2c6395ca3cbde9fd21621e774e8fa3130e4e5611a24d5cb44f030c85ff27df7acd937f3a164384911d6276b6c1ee0724eb483cb67f5b54ccf135f84ee6152b7d42d95760af8eee88d1832417492ae73cfb261b65fe13ce18fd3174b1ae8c11082c9ceea41b8f5d8d63c38ef9bd7cc99a5ade4738d384a020a11e86f67e22e6840724cabaf14dbcd4b6d4220d5a780d3dd46a9497728b4d2e6c2430486b264b027f9628977992639c1b64d961334aabf9f54bba67be980fb64f6de48cceef590c65fa0f1539a2f3956a14cb09f0d3428a2637d1218005f8a6a65b6fa0f794925a780b656b028f06b5ca51874fecbe99388e0a2ed2ede4c9cabc01c92812d757bd9ab18722ab1c135126746288cf32f1a9c5d04d078a38f38a3103"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b692328c88d2600106100f223e0db7a0307774a2fd8591364c39b5d6181a8477","proof":"0418c71537ed5db2417b1d5151f35b2857eaf3e32e3e496547820e6d44a4993b4a9941e756a8a90163bc3f830b4f83a01182089ce95de0a0cd010eb9127ffa10a0ef81a3fb56413b61e9eb14c986d934dd25c3f8f377947d08bcb1f684a07a0b5485a231eafd93641dc342daa4ab6633f73d29823d6aff16bda9e495db6ec35a18b48d1614b8902c6d87af26018318434742a4f018ae7127c13c3e9c96d9f90a9dbb09ffe7bef8e3b95fd400ccc2629339b452285ba0163a79419df10ace4b0ccc16b1c8e3c9c42d48f21a29bcb73328c301c8e30117caf9ca55a52d77bb6403607d44bd064bb0c84356f8673019c1d5f4013665fa49ceebfc39f88e5b3a73623a3c6300fd5f7d4d698185c50689d08345e721e76cc22622f71919dbe57b637eec37145834924c025b37bf36f10667b980a5651ff50d6d02e858def4ccabdb3cd028cb31ea7ce1ee66432c992e6b8d03237aaf521b9fc33818193860edec0759d26096cc79ff506d3c92d409f3af57e24cdd4af30496f5c31016085f6ef4f11a5474bb076e9e0cfdea7483e3e628411108968946f02ef4f531364af2c2b89752c4d13e9e8117a7e8ef4db10941e48f22f17597079b81ff6d1ee4980d1fa6130f7e763290dfba5a74472966a863a91bff83275a17efd9f7ccba3d5a8f15d0ed09beab52d19cdc8b9d25e4ae9d15c57f1e849079cfee745a55da29fc5f23b4146c40f16350fee1d34af209c6a21e3b5476b67d2acf728115e8bc470ba01e88bc0940d81d36531fba9b75fe097890f9e6f81738c043d50d122ce8a2b665e1a6a9197ab6aec3d275045aa5609653b85e61c15131e9b95d6533f4b1a69bc0b9e63775810a296bc2b9de4798625d027ab294c0025ad2cc4bcf471f33e4a7c69ab5790a33eebe5169f3ba5188ae314c04d30c0028c4edec31d4afa59f8a8d2be7770f0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dc38ca89bce8902987630913eae1fad6755e879b58a0d74b4c59239ad5698d74","proof":"c68bd45d57e4fe3c2b331648814bc537e7b9ffc6ecb76fe39fcda8a9eda70a65b2ac11ec6bfcb6540ad10156ff67a66a4e3a33ef9cb398b2b677bc2d729c596fbc9993f0e3d8f3c9792f48327a1b913206c1bf714b66fdf51859c7b9cc75f5545e1d87eb3f45aab75b2bbdca0e38981f03591a82e948309889e041fc9050a80ab76615c0195234b1db5a0c5735cadc7a8ceecc001da9742f8df06434d02855042e34fd89b78409f42494fedbf74b36ec26c0fffc647895feb51e74d0de696f08e33c5e5cfb3a9ab915fb359219363ccb1937b7ef4a4f3e143a173b6674223b0502b93ce3ac8e072f0ed01e8cfc5edbdc4786adf6278cb54786b2bd9a9129d820ecbafca4da25548da5448d75abec7ca54492f8619dcc22ba6cc2d7c72a650e601c3e1362a3c0176daedc940ec2f27ed00b7096965d18e4dba2fece3252f03802e2c8781bb172925ad05a7d24f30eaf1782e1a2325d90f1280732a57e0642d745f4e6584095eaca4465908e2b2065b485a50fe227d662935bb464860293d09e62ee35bd12c09bda6da5d9ddfc11a76b9cd04248340ce407822f54849e44a04c3d7ebe31c95b6f03f110904cffb1ff62353ffe4cfd001a3b6d2ffac5010556071f3a8f2d5e1bb58f016ab3b2a652114fe54cb59d7b612e0ba6ad2c530d56a28a15f09eb2e33460c4b031d81f320cddfe6aa421551c13163f84e2f8c6b540a07458c836bf2ad5d1f2a2fc29c870895fdbcf531d6db1ffd742832d32ca10322f531c0aef16bb31afbd3fd1e93286f6602c9751e02ae43ebd2e421219034cad7d993ac4fcea8a0be837356b6aea71737e15f788ffe97cd63d1e93996a0eb26be0b23a81a898e6bdfb71cc0fef4b26191e9bedf920f7ff87879b254058d375ef565304bfc8fd83ab6c54d5594888c12e4eb9df459251dcd8d178c818d63b82bc80d30f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2ebcbb3c2d46200ef2553cbe7227071e891530e85fa6c8b9b44dc91158d4f007","proof":"9e08c31e108a6d256cc55e5f30415422eccccba7b9281186409a1805cacfc6764a199874f0659d394de25b36e6879428bc5b42cac3bcbc7d9577580e0da5dc54423b6e8e12ab642ed231eced0cf1d8bc5708d749893e3faab19d75ee626dd4208e821a39ab5a0658d6120c30a0508cfe1a1adb60327f1922034263b69d9fc216d747dcfe201af676e1678fb5ca607612cc307b88e7c63630def2b2a94f2504067bf5749a0e4b2272f5f8938839725d42d3eaefd59d765448ac85a87062de1101014fd3353a36ef5dd92722c2c90b579c401273f1b01e8a0a6d56046ef714ed0f3c0dcd9b84f7321fb61e998e4ac8a36d18f2a595e470c8f1631d97fcebf062610409b6d09e3c680f9f7004799d34926c0b8686b414f7a040bcabf7305e1fcd45128a28211ea362921b1129638e8f097b6951694f33ab422ae077e416f4379f0124e5584a16ba4b24b139dd6d11752a26156c83896198ab10a520930ae77fc10ff4807e6f7411fcc3f76451f5f303e33c8853f439f177070f26b52ad54d00380baaf00f358ea45e452019b98ca18a8e42f1c085b403d23e0c1edbe73983eeea76c6ad2a01eb81b708b28901531b0e90a6b210d769e30cf118a2943c7159e69343de5b7763b8a8f06dc99cdd2f75c989b891adbd744b3a9592ec03eeed6ed2d43e300213f58d6fcc2176bbcdb51dee0274aa02f93f997efc790ee4ad0ca864e059f65c0420e1230021d0412c7cadd41cabd2ecc401245b5608b15082c65994eb1a0e79d64b2c68bd2ab4f826e8741e4433c3645b571913f00dadb86b856a93986be0daf144844b1063a2bcb88b353a964d23450f7a4a85b73bfbee95ee268b0868deffd744fdebfe37ca8878ee5f97db36d599313fee77a5d4f74da016b4bef70855d8cf1a1ec27307b8bad5ccdfd59c6357c36487bbf0a6878febd913e0f7a603"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5e4408c111c3ca0f582711104604f9cae7cff2d66de11d262901109c7685eb48","proof":"0659fe115a23341a87825a0951fd577bb658f488d28975bfb9b8b01f0fe51e0840449e8137aabd2f5a949e9189ae0c87f507bc66f4130c03156225911dbc4030a641dbe18c7abca6ebe70fac2578455b2c071b026e3e95eb00b865aee1258c6ae0bd0de52ac106a02c28014bab32e0b7860d22312ed5a02eadf0775c55a0ed768235f15e9f4dcff81946c37da4d2418a81bc76c0cfff45be90eaf461c2566c042a80bc5fee67bbaf201cad59777052a980c9cce1740aa565c431ecb1d5b94d070e2597fdd45540729d3317de3d358af259cc0eaa79320cd5c3c1049845cdcb033465cf976a5d5678d49efb5d89f4dad8cf8d2ac965f9d26279eacd6f9272ff18a2cfaddc857f8b726517d426dd7a553520d0b1be62f0f0c068dd72f985f1dd08345b5bf329626023f7371619afa3d715243488aa03ca8a1099c9fae78b0d456b4a1273840f6b5cab6809103a0f78f064a1814bec1b1e972a53364c66b7288b7b7633bdf56fb2322e70e5904cd74024a2eea091f1bbb7e818063428620b06c866066503f3c2742b21d056192594071257dfcd009592179a390a14581de7a0f75e96f8d572680c94771ae62bda01234ec71d9a1d9289bdf9d7d1438baff90d1c3c300502a210ccdc7442401c77e0ca2a38ec4aeeb4035bacf8b4e81cf695db6d5174693e12d62730678b38db6963144f184f4d1f2c5f9a970e567d354d3230cc6decfdb01f95641f851ee49d9a45d4a1058dba2f55bfd6c642a35d3adb34e4757e7cc8a07f3307976818f43a97ce2b0d208f9a1b07f7544829f142fd72bddfd1549c1fec55367cc7c09d65b32cade21b6de681fb2304390440d190283c8895966546b49ab04184c93cda741581da2f550362926d40a4904edc55d323a94c21c602e2492c843c6ece689f757681c5a0f13e1641ac25cb88c81526316825dcbf7909"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b817f0121845901ec272533d6169f4337142205df3470670c0f11b5c7327996a","proof":"2a1703774f1b6e668c7ad10ae5ee96ac36bc5afae9baca26baf6bbebbb22ae06c253c49fcd82af1c91054097d6aad496e4ab61e1876e38e9823ecc1c259cc57adaa2ba283f9f0b4ae19c5e36322473559048fe0afba4d3f87967d28ca7662546f4decea43429b4fb506b928078aa8f06585519b60b628979837e7bafbd3b040fb2e5c519ffdaf4b4f5721054c52c01aacfb836da2ddc144f8336cd5053609706486df18214601de868cd3a709d4ed12497d2e63de86d1d152b271e18584ef600d91a50d2d3159b4ed8f801750342f9a5f3a6eaf82e82e3be0ecf37dbd97ea7032a57f1a55b29152ccf6e8fcb7fe63612cb9f1c89e4b8d30efd62fd15a16f9a13fcf9b3b295b0f21ddf4d545334c93e0e94018d2d3661bdce71677c7a283bac3a405880c0eaf2fbbb9d5d445329b152c8fcae9909c9ba99039dad43161635531140055579f60d6dd1bda862b616ad5d4387d564dc1b0df81d54ad25ecf9098f737abdf2450fb5ebb49a998fd998f17c35f19a1db2a34803eedcc340fbb5077b5a9e76be3149c56fa88f572a803bda555f8b06b57e64c3b49351b677e87e416a6eecb3af5aa3a781eba58dc0fae6a6019f780debad903462033438519f09aaa95bc667fcc021fd24aa5ee30a7bdcef5576443ee6a2321ee48534c4ea59640626611e70ef3498c152816ee0ff7c6a42fbdab1b4a4d893da3f099ecb15be4f5a573122fee2422db06520da3c900e890e70d8b1f5d5c9948ac97cb09d940255f62a634e403a046a7ebbd4838e39f012a879a942e7c47bc75efbe5304321e1aa56e633e426c89b80194d17158793985224a1de3459e340973a0073b986f6167d85ae3220aa5ebbcc4ce8a5b58447b8732a6764f7778fc08484d15fa0e12133d119500c6b155ad6a7ad5ce8ba659019558dd68b73e7ba4d14b977a5959c43f909bf2402"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"10b2a8fbbc1d7cb0a258ca855c167209025f8d2bae2e7002acd8f72e71f1ba67","proof":"368dc1fff83c10cd7f2abce97ea4378554457432974bb0f645330b165f653331d8f70e27254859ccb569cbf7a7a535accf4d6fb0d3a4470c70c6b7f5e366bd54f610122fb82a08f9d01821bfeb92678c608772e359943b651271e108f51bf5717c68e3cb9e8e0ce6b0c244981eabd27bc152f77a9cb9e39805be13fd6ee35a5419be2eb3351a0c91c06f3bf6a515d99c5a9b61ceb092f52d4304b2ffa2715903a903ef821ca99553a672359c3b3e9917c18bad85bbc62872dd8dc15ca1aafe062b200a8c550e79a0aa6fe56da7cd3f217d81a7758f2de47ed55e2b3862e17b0eb8e605417d20e1625bf048b1b8ff830f2fd0128843e0c74b18278a04d94eb65f42f8fe7ab426c4ba31dd25cd3c32a75bd711e92838ab305a91d4662621a10163f6a46fa0cea9bcf3ce8dca188f1d913bebc32937e2189495d3ee89c8132765728604fd2ca52ddeea54a629626f63a7fa5d427acd48e260ac272684dfc428a03b86bc2e5aa40e14edf4caa6da3216c28f4f0079d8f17f2a0a872f70e39b4e0f517c72d9ab3ac0c10ff8b85cd3fc7e7380bcaa3d366992bf23cb7facf30ecae953b63dffb586307ad51540de2785a7954d60f94d3da5953149e763c19aadacbf57b2d60beb935e2896cb39cc9f98a09566e658b48c1156bd91295a05fa2fa7db022440ef39c4ad2da877997d77b59fc3941c4375988585be7a66aada3a68edfd7dbe9144f72570100ed5bbc75974d0e41eb8dc23e106da4209261218f7fda2e65e06888225132ac1f1ff3cc403f1617f6dd10b438b02b249f82090ae9c1b282115442e98b5e671a457264a4e41a3e0a66a7522d672355625749db44dd48fd48711ff600987e50050eff91fd093321bbc8157ff969eedeaf8088ce20987fef07d0a706b986bf4ce5ade7bf00994c9bb4cae70e944d3fd5fcf7ca1eb1d8a02a66e0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"907699b28e658c1e21418a1305dbdb7a524f3d0fdb6b1d29bf826d0cf8fb9234","proof":"0017b0b433c81c0ac75da226802bd8dff42cc07f606fc4ea18fd9b35063aec6c7a28b0a44ac1cf9347ca158bee8448c9861c71145ce4de35176022ae655c090ef84d7fb604d2c66fc4a2e26e7e5aeba062c1c51a4ee6fb15576b602c42cd3711fedef352edf24d5a15fe79d454a0cccb157a5c7611739b39857252cb84e0ca6a27f58529337dd9d8d60a50d0b357580c8d0a548f3a22ed5e80241df03c3ba106431b41baafd5ebc5db8e26ca916db142ef52a12b9efe06fc70eada9a69625e0ba72264b0a07798efa00d94fd7fdd367fe2594470752cba184994f611a86f0c0eaa7634e5684543491782bcce0680e89bba38aea7e25e41b8b12e44cd9120416e98ad984142e6c4e73debf9d89e0709018b0f77bebede91612e6eb4000438cd6c0c3c8f1a440842b896be852af7b6bca410e6f2a382c4a78f3f071b8e4a29e326a0d6fb2a7424ebfd2b29fd13d9f566c3bf5816956f7c15631f7100af3616155b4af1dbf470341535bc0d2c75d235a361a919fa4b8833b516fd2feeb17822be392c8bec20df1a0cb8b189542a4af6f8166a72a90700dd6cfeb788ee8f6515b35d2293bd3658ef03a4f13eaf5d0b9996398a06ad1d70a4099306f60688a607da41c21b260b509a122851ce4bafe439827afdd4366491f6db60f4a785c94d06ec5510b65ae52622e1e7a651381731f66e1bf328d4d3fcd8fc25b7c0d45368705a306e2d4d5488b5bb459cde978337761c324760a5b901f7be281db955cf316e28671428d57c05b78aa20f072eb711c5b860b1556b9a984211f767a9f203b3fbb91ffe774cfe1ff903dd31ab0e50bded62eead9aed8d4e971fc13b69aede4fc57c2dc37d768204ef0e7376f1b371211ac784aae4915ed5d6ea25f634984c55dc8802e92895d8b876c6fa42a839cc542412d708adcfb583a3d9e1a1e9bad432bb120c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0a799dd8457b527e7890950bfa0c6acc12dcd200962acdea7c6f5733d7850634","proof":"48fea7e2bd7a15434bd1b185820d11a5b9cd60a6d312b05cf5096475246f7b75f4600f0df4b41dcefecc6211f2dd1b06bcd647e875a04cfb71ed4eee016d5a67fee690f96540c8fc5d6f49a6c2deb1e562ef3a99452a068dc7666d4ba947b70a6cd6071d1c8531f88098217dabf370f886ebdade4d55d997f7afbe8c39012f703287f66c173bbfb773f3aec5375f381f0ef2e8edd5acb39c806a246c569b1c086a241a137fad75a6893cae5d081061113b9deffdeb09067d7e53de2601b0230fdae7a0f20aa9ed30ca40f0826eb173ab91a3ec7daf1d0c038e907a7f66bc64015e454e9c005529e1b50afe9481d1f285f97e0c22bee480f56c18f5720209a426a67b781ce4325082cc5ae2bfcd0372c9b5f248ca65601dd457b0ae49f86c307fa87b1c0d0dc684f58510d8d0fe24de538933da7f46c33feceb5a96c32bb86d503018fc0048493608aca00f8b377265da9ccb0313372c6fb80aa7dde47c2a6527b0c490c945beeca16b96cdfd11971eeb0647fcb25c5615f8945ac93fcb40905796e99069e2ab375bdbb4af022712b367f80a43d2dd3d5dfcc4f9a09685108c5ab01a49488dfc0df93b29e86457472ec8abc7b510fe2c11591b75013ac625d50eae148b3e0a529531eb52d293e4e1251472abff1e231b314f35042f9f47d3ac4604ac2c8e86a4bd3a7fc3f4a1e24196bcb51494738f3c38b3ee036f7e311a6347903b4c7797f688b302856d2cd58e1e8d6c703a094363e0bfe56aadbb4f1f5774a84995d00c03e66837268d3b5d74910316a6ed11a6090e2849e13480167c3e2a3e81b80b134c4b2e0e9b726fef10d5bbfce4e3cc007d93d52196b3322caf2a43dd109a307a49ce90c2816ace29893c6f3cc47f38268fbd9d2af4eb8fc6c71b0cc2e8d049134a9bf4095c2a1abc5c205983ab40118860528913fcb72e23cdda0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ce75a9cc4380e4e9a2cdc3fec27f39ce2a773f8aa46cd419257519511974a00c","proof":"88417d48e1585abe0bf202fa48f6de39178fb4a30b59a5c3777a6a49a79685543253356c84a89f6a20df6f8286de4d40f0f4a87d8ef28956ed8177fa43e2ab0c8081e35918c6a8f2084e636442ddf622ebc6ea677773b8dbb4a417fc076476052e9bd224f0ed3bb95a6ecb324ae3c2a23d762fe023f3448d0bfcfc3d16cca113de172a876a94266804e42498c23f9711586b1dbb6f7e52eb07a35153c41c5c0a0a74b42ecfde2d83a27543a729ce1278cce8fe02b7316276267c1c95016e0b0206ba69fe0f187e152f542daa8764241e359157c8f17aa4da565404202dffc60bfa8fa4a70f715d0bd875aa362a1ec820d6610a070c4212eab7f9e0d999443d5636a88af33f1c9f5fd43f1cecf12d2c26650a3e5f4bd46a8355071e09907f3a40eaf25280149efd9c255ac786b01aa7b0f59006ecb1385e66f137c106f7196d1a5c3f34073da7494abfc7296ed8ab09c6da859d8cfbb2f1cf7e4ffc2fdaabb33fa2b651ee188ee22d4d26cb8e50f9a0d5780241b7326b585374cc5d22fa29ba157e6fc408cfa13c1f02270aa583d5fbcf833e6d21e322f718324738cbabf82d022ae38de27c1d115ca9f3558dfb9b4ac8de660a34d8e319e81c7905b4d7f4643db8bc257b5c60578afa01b48910af83b975863b0b69e19dfcde770ccd53453d7e5eca56fd388e8bfe602e2eb4d302cc0b2bbd167a5f075eae5f2eac7987e21713c25981c4ef7b981a36d6f87e415294e2656eb1550eda81e1fbd311f1d2a45530168bc3c56b43b24a594df036bd4c9b948dddccd35dd575f4c24c2d60483dc2680ce7d3427dbdb80a867cfddb097fe194465d8b9d08f3b8d6f95c755a366d0919d67414af1ec134ef103299a59d1d0a37e4fcb727178fdc809108fb6c0017620bc9d31a62d7b398371355ca3e79da6a49028a39eab8b27cf8e105b8c6e3c16e04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8a212d922809a5350edf35d18e1ff3af64b7b61a3ec31664562b630ca336302f","proof":"d2b1b01b6f13572faba38690af8fa388ce404d35a444bb879b31b591e7d6374384ef784104a10372e0d056e3146d1a34ef285c48bb0d25b4f310569a6384820292ec584c2a6fd80252a5188dcfda9fbe1e9677801fc51439235c547a094a6c5fb066bda721361252c2ffae6b4fda52465c25662794feb4f0d3acfe09765590752638b5c0a02bb827fbef4a2e3d575030e36c613ab46777c9690c5abfe0725003550b7c1221ec86ac6b0b37d7548b95d611c0d28e03ad2b2f8ee925d6aaf5ed0d80b783fa1336bf74a83ae4b3c9ca0c008d9b9f0470d1612e7afefdbdcef6090eae0215cba42320ccdd04ebdf7ea7cf698b30a7e107c815f601d2ad99009ea01ad266650ee9b158dec8de4811f87e85f36b81ec8dacaa482fa25eaaa806169353a61206db3445d43520ec9cde3c3d4527d188f8157ebbc8bfda02bfc11a0e411d306230a6173872a0b08998163358a4ac0fafa6e224fbcbbb3af5416f7f680e5a266f1bfcceb800bfd65be8635589782b8ea14a2e65425c767ace3b4b58e3e516e6f3a1c49cb8dd6e98dc6a12d6392a2d967f74d50a6b01a620ba99e2598afb0d18ad1e9f1ecd91144f0c5344504bebfd34795c636d262b8cb21bf4d7446fff719a272680832653aeaf198c1e061d68e4d5ac42cf3c9d72002d576ef0b0efb30d645295e413becbb0ca86756af1aa94f2ea8f75a2168a1ca48831b9b312a9e161d2e5a5fa49da2d8e9906632f4f51fb2f67d8568839b0e52cce4829ad40774e0fc8309699bbefda3977c4270e20a8efc857d72c1601d75705e99499e8e33daf22007a3ce6832f1b79aa349576ba5a1e6ce1b865a89304b3f5b4cd01cad0cb425cbeabd8208dae3e63513dc4ae31cceb1f78f8ae3fe4dcca00c324709b4ba6b6062208053819edd7e57210bb597499adf7300e9fecfea8d0ca4744438cfc83a306"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"04e3ccca7d4e183fa54edc33693520175339be527985880822ef3cc10a361d02","proof":"24ba1f7f8bc8eaa6da9c20817dd3be2191f490d49e33f6e25585ddf064717d329af49a25cd8b3cd7ab0860f77a5ff1f8bfe438a016bb3dc723810ae21968953a602bdea5168e88f9b8e303f5cfdd7fc3087f9e636f86d19464d6e7f41392c121c8dcede9a793c10a9879b6c8c0d046f3b51064ed62e669f9c3e9c9ff1376493954daf1b5cb02c463a86e2df8a7ae0b4b67214b0dda8f1473cef7d78bc2b7520d1c5a2e6d2814ca1809ca788008e9dd7c71a52eee74fcfa309a7e5dab91150c0172b51321fd2187cdadc83540efcd2006235fe9ecaf81722382fcc4a9dd98200c6c7723aeb23fd0b8a68f1d8201253a4a9bd1eb6438c0f012e4d578933f4f3d1ef43757671d122e961b8230c7d501aeaf69bba230af7fcc32edf9dd3e369030305233d619e2b6a7b9bad67d378b6e7f9545b593f0652bff3c4fbe6d0547f24a35f636e9b4145043f68611017240d9ad1e7315a137239b086bf4780fe0c302024fca0a0820aec2c0f0559eae6c5732ca3b986af59e8318eaa52da443495953e333ae9cdac73a9a737358affca584437f4f23e527aef5b6c0a132b5434cc7148670caa6138f3d23e250dbe03f30f80c2c1540d7805f4b4564ff663e805c0f3c5379ce2a16cf1aa6eb427662e385bf523e5ec2241a7b5751b0d74d8b79b0e0a34a152e90f57af6a1d245a95144360d885fd0c2047cfabad07d37b70db4ae5394eb3ad2159e9c716373adf77e7a5b4e61b5669d31c4c21b2e66c5bb6b2f60ed2f0b6f4c6707da45eaba8e9ac97a5e4f36022f0e90c437f551ffcec16f23095ca07507c621cce80ba0ecb345a0a127a6c5f80f54989d4b6d891701b9e809ff5c90593fba52505c11386d6c926b40187393ee8ef0ee813aba0d1fb7a990cb0e6cee5f08d8c95ae6dc06eb725de92735a9ba40d3921c2d8979b48fc6c983ca6a6e7f3108"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1a3b74867f9a8685621d9225ade739a4b406c7ed7edb9da8efe693b58e63c908","proof":"0e6884d1ea94268e8c84bebed7c530626fb03acb952f1687ae0bb571831920049c3365baf68f68e2c0c28df26cf1deb961768012587da8ae31a95ec80a2dce6b5ca8bebe8b8f2216c8c86c6e953d8d2c1e4b64e661338e006c43234c0e73f87696d71c22f02be71de2644d1a30d85bd4e232368904eab02a4d0ada5babe81a700c9170413dec24da0f1565a88b21c4e56d3d5f9f54e00a34c1f888936747dc03820c516767f8afd6917c7ed6fd514b174f057d2677f3709be43856bb5a9efe032d3d50da8e544f03a2934c9a7d27c9515159fd9a04b83a0e41098cf6543b9d0f7ec565549cf1556684e0e36e37f21678dbbeaabe6c69c82f36fa833ecbfb087e4cf6d8d8cdff06ded47a9f1b4fca289a47aa68007b799b9edc358ac8fb771d7974411983b6c55e4d245614e5228c5662a7ca24404e794792d07f5674d165d37b8a8a8ab80d43a4182b061b6980497db94693f927bc34628d4f5955baaa3f162858bfeaa5f6dbe6a3d9c0d509878a89a1639c267679c359ae629f8eac5601454708e8974cfae020b3c96812b3dc0a1ce324f872fcea8669f3b73cec2fe3d44945d259653f29870697ea920e9751a553c70f4c0879777dff06076bf55bcc95b42bb01ee50ae1c971f92a885ad0316699532fda4d87efcc8bb9d7ba2a98189eb37d96e246af89a2aa6826f33f8457e20c1551eb4a3d49161f0ef407ad7b73ec4b752aed512fa2545d86ac8f4e35836059aba8954cc1d0df6b1fa7f1ce946f1655625657412073cf55ab62273f93de99242a196724572a29b7bf4d6fa20f4679b03f8a3df551a5441a213c765791f59868162dca7dff91d640a80a3132f36b8604705c76e4b55242e1b9ced490da36f6c4a1ecbf6849dfc8c349dc97e8467146da0866509de017661303a64471915a443e59b9b60418143e51390406b95efb8c1f00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1001246c1d7c99054bb71de6c516526c772efe62738b90d49a844af130072f1c","proof":"a0ff61779c48429cb5be20598474603b877e345d53525522057d16f1d26e6616c0e1e6650c036d055ba8c82ec796808e8bdbe426a75a3f55ae833582a6489c6b92e7734eadaf505daf7bdf1928494d9a9d313c2e244c65c8370d9951b9128800806f20f8ff27d8c116b333671d612abcdc420fc63534d4bd28184e7711fe7116854b32752a74c8c078294c91750afce752ed48cdcb034079b5014c3b2d89e000b75b4645d3b7a7d5f8bb3276417fc8d95813fbb94eba1200b107d82b5218ad0117c3a20d7ac3695be3af3e2d5ded9315a22fa02ace6844cd53d48fef8340610fc4eb9e6d9864b5d60cc1a44c9eef1d5896a000bb47833d645ea31c3dddbf204aeafadea94082d069783542cfa46f58fbc31cb16e9475d05b63afd5c359b26f59aa7678dd8fda3f79e2f4cf6dd05e7a307d8c0f2ff231039e10e41c8322ea271aea426dfe840f358eb982df2073c83d82389475016e318cb6a059aecdb6a2cb09d81762485ff0e2235e7ed3f5b641b50865ff49d2f4071f4182fd294d2335e353d26abbef08823e9f6a21e83610f568bb3f3ae9fc646939ab3259917a36fddb34d876f6274af49d61ebec8379615cd6a3db9fabca65f3f652c45414ce1612d708dcb6bc318dc0224210c1a8546e04f311f00fe0f7c375c7665e352880cea7aa299ea67f74d279f0222c506ad96272bbd752e2c0e8152b200a5d03c94e9f3ab01e8e7bbdc4f006c1208e2756fb05517e5375d63d365725c0949208ec1c7e03510b1225399cd755f8e7b862abd12f58860eb2a95e7eb8ce477696623e7e70a2ed68c2aa403d4ce4d724c658d7cea8841999d730a952993b4866274c636d3dd4f066008da85881c49583494dd257fb21230f37fd6683650a467e66937907713b720d0ea8dba043963f9d8899b10cd7db06f7b8506bd3fc33eb3e0d41ad22d81e4a0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"08642bc0538dfc025e85f0a69aefff3a2db399c0e3c5873f3b26c18b661ed11d","proof":"6c536c0597e19f7f8bc76c0418074eef8622a3dc20bcf79a340a0d9c53bc72186e66ed28ca77b0505c4547d9dacccd62ce5f3ba50f494b1dd44d1a201b6eea2f9c79c0141e3184272f9ad42295e827cda7bc98c7704b036274fc2f57ba292b4e80f908d1325ecb1030f82238f48d72ff6bd469decd9b8dca2338e29ebb442428479226608945d1ec02d274e86887f996d483351bd4919938342751a486c6cb0dbf710eb07a207855a9f3127eec79f71f4a2de9595231372ddf1892dd6ff18d003acf6dc9058b3755713c46e43faaa5d4981ef48fdea00bddef511870258bb80354170bfef668e2f9f01c4d3a8f134e75a26c669e2f6a79b981f9af3980f00118d4d7c7e3d071dfdf39fdae6d6e9bcb02975c3e4b6a7b6a7c6b1f3f4a2bf52d196a65bfb1d5c0831897061f2a05e352a9eb7c2d9eff9543246f736e682673d853f6ac0db7c7e2d8dbf715969b234878a4946105d8e96c3a83c85943d4b20b9c4d76fd2247aff209a994184d615193cd236e44402bcf771219a1e7658f712cb259a00f9919138fb6fbe18af1ae47c345a1deffef41adcadc707143d783baf16d66b29f61758f6ed31aee579b582e889763564f35e596a239ecd9a6e0a517e4d44ff2af36316d3fff4af8b21ca583d8dd56bea68965d156d40d09966296a66ad060ea21a2df20f33ebb91f930eccc7c1fb77bef228bd62e214e83c6abad5970f16d7e2086e7887db5cb69db3503739b4ead95978c57c6af189dddf25b214bcbfd61e63a6bb6abef6acc51641021ddef4f8a75b0bfeafe771b4bdbb115f3d35c882632272e3dbdc4c69ae894085c07bdd846d70be59ad38727b8d1b812c9ab37862262bf4329693e370a90e1692e23204e7d6727ad3d6bb2fc0b70f121e7bc49fd0410f96c1d4f25ba52f085d48f36f36ccdb72c1c4948d5bf78e8d1bf8a2f4f080d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aeaea10c02dbd95649353d2977a7d548f3d187d6baeeee5c207e1859b3c3343a","proof":"409988733ca8bb4219768d9fd2e2fbe63d2143ed92cf18ae2d7fd8c7f362ef48de32f06320a556d593440defcb529d5abd7a7c2431d9968a4e1a116d7ce6f020504833407899c741fddff9293423ce6404b1cdac7c3cf3308b2b7f50615c6864cab08b3e5083385cfd6fe4905ea0733b5a184df3593c0fa86e59e58a7eb9b6228d69c02a3ff3a0336853c0ba9e67bf87df3fd3dffa7dfcb4433c82765d84ab01682a7885336a71ff27cbf67f895d39a0764642fcad19e4597632edf27dc6ef0a06a39f629e8a9c043e8921928ed428d04acee1fa77aedfaa2932bdf2cfd18c09fc5227125222091d5fe0973e3d14ddf1750fb9c4a1f29c007343b539d2e09222d88d44c2ff5c8a0c85091398da7918e97ec5d72524c1a3d7b14d3c2c88f967561ee8b68d81a654ad19f3c22a5bd15490095eb8c5b6b0e041f29e96769551b90768fc9123825b267da3937da124660de8f95cf74d571b6b2080b1bf340e43bc2d881d5d7614813145fa4edc71ec98f06ad1025b413b7c6b43b044f2f60de9f468e4b0953e25e193d723f6c66b9bdd211987e8f201a71f47aa476278487b1a09588caaa8f77b82443b7035f108c007881435ad0a9bdf502fd23aa5bb0b6c75b05f68c566861b598538dcaff41af1d5ecb1f15862a5d08a477bb4e51d29f3508a5e8a63f2f727232f85ba8df07b7ee6d1358abbdba9a7768c79dbc02c20adfc7c357ef3d93cf8886b6e32c057c587f3f84ab269db90c5c0f304fd49b4237a520b535eb740fd0be6b66fb6d595102e61e286d2ba5b29698ee5748e2cb802120449608ced07763e62ac45bc2f083f616127105a2af961eb36176fb43752db5553421efcf91916185908a3719d687256c08b4af048579a7eb648a1ef7e8282fa40ae03e92a2216d3d8492e1169436be4acd3c9b3e31845769bbeac4eaa093b7685ba01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"842c5a04c6f0a4db28aa82251b1eeefa6a7e72d02afac3b1b6293685deb6ec3c","proof":"42c24f9b9ec76d800957b3dae79b560f37dc7c242ef87a294511cc3400c28c53b8cb281b77188b6cfe73a8f62c7e3d6e0e180812cbebae22003a1742503de755e0576140bc39ff8e89972027b681b72daf7bc3777f6a4f159230bddcf014c500f695f3e9d00f3e0174670f893cf24979c999a92012d5b6c45058f820798b2b4e8712bc468028c86c498d655704ff7868912309f24a5cbe33e609d7d3654d950f1927caa1b4d075542d0d362457d044ad6d7ad996a2d9060db6b83fd5a6cdc8012845014aec845cfb5a0f7fddd507e1e1301521648d223a14dfac06dd2546a5023298277d2d5b120c717390b3c5deb686d1da010cee97c1af73d30e62e597d52346a181eff7352de7b4d159f211f1974d4fea91841f7a7733aef881c0e5e2f8745c2167576885d6fddce8a797854851f3051e2c4f93f3c8515e8c105cd3258f549e560ed9ea4345334cf95994e2996d28a1f790f3a28069861bf0175b6393f24912ee24bdc465009330d0bf8e36d7afcf5f246a00c3bf8c3372076bb35d73f30ea435bc89ce2eaa1946beea573c77f0990873fdf0b3d81c4337943c2963ba974b1c06fa8f230893c9a125adf4f99e2aa3695e9c9608d94ac1c0e98d2695543a27eaa47df43aeb5ccc1a507645c972120eded44f6d197f77ef138e4c0776fcae38e0eadd952e7e413dd7aeb99536012a926cdf857a6c3660533233070a08febb0fa6981ce23a4b24349e1327f8eaf2019fb41d85b0c7518a6e53f8cad2810ffb0980d674ca8d3e886ef62c21d081c972a8e105d3fcb9ba0f8399d3f6ab3468e1714a82b9e0243cfd8a49272ddc4cc6cbdca0272ba53a5872bfc6d92820e97b81092e494d92004b925737fc7b786c90bf719018de682575180f78f677a6d4619b0412bd2ef52547e3c4bfb047aeca94e0fcd4f4be6b0ab1ff3e8a49b24fdda6c30e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4a51cea0a6fafa2ac81d1e4e5c4c83bf5d8883ece7c136515d180b69bfea9c7d","proof":"ceced802c036bcef54265fcd5238ea9fcf5ac81c83de14c9c0e4fd8c3679f72cd80ed820986a74edd4d38f3b90721f043504d03733a7924d0aafe17d591202127203e99819a81bcc4e4600353dc6051afd7834dbb87c205a63a3600039c9340338a35e9331bb354dcdf3ba4759e1535e9e14f8cf7da1c22187ccdeaa8f4a7a0b51d3bfc041a2bfbd97df0c4acb2a5df8c998b4c149fdaf4cfe5974e4aa188c0f4754c4a22c31834dac92d97df8fa302769dda42e402b929128a06e71d98e620fedad4c49667c127993e665aabe4781df28dc401c2e25151b9ab9f5ed2e8cb50ef0899a18b86b995e9a6c6122953c734ffd85df423eb8d93c7a13cc2bdfa74053680ac1b937cb701cbf34856c2988354d6d0d11f7525c101d00a75aaf7069c3090afa760f309aa56185b5201ddc65234ca690ce6a934ee22ffdeb5b4f6b99de19b68729bfb114cb57e4d871642eaba8f1f1dd12f766156935f8246d99bec32244403d9c40a4931dc32bd7e47c3104e491818ffd9422a9c9a12fe45d47a916af2a1ac1f64630b095102570e66be72bb36aac4d100b22a6e914f9ef0fdae37dfa77d097e5a88a27e1c5809002b0b17383d8ae385810d6dd7ce05fa45522944d90255aa24e183535ce404a2aec0bdc3ca65d271f10dd9aa148c901240cc49c25701c8800f045172a6879d9f6f6b337b8d2ad7c53687627f69213c498a6451157291502d1709ab2c7950e9b8b55d956a12abae560691ac4b83f5add6980659aa8b74e88bec82f0671f2450283410b60b531aed983acac71e121b29bd832639922e323aa6a5ccbedb0ddaf36f6410a5951af4f664d3be03613568492b3c64d46be7f68f518e484026e810f7343e824e7f159dc5e3886152873ecbf86e352666b66680b9846616ce5813fe54756bcc7db3c14453068e1572d4c318d82a992bda3ce2f0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"60387befa3544b601a1011627a74219e57cf0345c94be05938ed4ca10cb5285f","proof":"0a8f39900d9ff6b909d73f18f25af7353ef435e39e75aaf4a1af0287887e870582f0b6a76f1f0744aad501abbcb90f2b8b69f84877ee531f407f77e84adf961e92a24e0e55fcdb699ca01d42a74cccba2b91e5bdd7f3aaaf48a5db6463828b0c4e891a991959b81c84425ca651e6ff3798c2ff31680028d9275b677049bdf5769f26ca18cadbb5b9f4ca2e3a7053f56d2922dbc80a84e230cb3568bcf0d0e408514cff0e754769d490c316eac516b253ed79f9e1aaec33e428d8e795822711097ab0ff8d0b20296e172df9fd274d6e3a2ba9a16eda0b742d9d4a75fda50af6072e917957977ac840fc8b0db41998ac4b41dff7d56b365655481779071653ec37582831a698471132bc229f5c0fc4eb5205c624a86e2022f0b2cc7e1b7fbce6559259cb78146b5a8f4d7f80bd0893b7a3c4979909010aa29dcdca5ec9d0e1d244e6f019ba15cc6225e69e0c8e93353b3981d8c155d68aab02fbf75bf1a4f6ec77be3d308ecb868c9dbca61513c8fc1e7b6ca3f5d2534e22660239d54c3d33c7744ede942a8d5a572c7493c38f3c6e2e5be8e7423b20e5199b7afc96e6a28f933592b3ecd5866ddf9a08a6b19d0f44a2b669a852d15eda432a73fbc08be5cdb94176477230b048cb8ccddd2672825acfb82e5dc9347445d33d7d3aa4793b3d90544c2792ce6814dc79ec6a60def99285f8c795669637dd1bd083ed7e9400244c17268e412b6322865149a05b9ebe7dde9360417be536d49cb1c5bd5ac55d52a3006669ae1dce200cb516ee5d4a49ef5876c12de1d25f5837581e3f934a2ab1d14d66357b67dddd9f1a8dcb7b80aa96fa52aaea46168c58d24b3d0883569183c5727952bd1a1e3a77e353dc94c469d7c3d33e6c03f97f926ae5fcee9a0cd3cfec0c258cbe1b0060ba5cd6a7774b185ea0f8f968f47b8d7e49e0d55c22a6e7b33a00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a6dec3f27df7f0d50180b2bce8cf8c9e3f42002258494940fe478a1c3fdafe2f","proof":"e289dfc3b26a84d7ce6f83cc2ffbc5cb807d6e0e26bd6d073a891ad143ddb10ae657e65e94f0ecec2c2bf62154a5328f4276043bd33db1d95a03585e7dfeda7df08cb0e69abfc2bdb9d4951edc87942155a92d4ff584cb85fc1ccb8129597c3dc6002c37708acd3e3a4541321a0c3c9a27b12d950bfb6f21ee25166e2a50990e2a61dbe69aae1c2eab905062366d3fa035ce6714d15916407fd771f321507404ddba2acbc98ebf32b04e19512f7db02cbc29b9f42c8974ba44a9ddc0da7ca203f8d9fda1075b1e5d907a91d7341efd5c5b73a34ec1012a9ba431f3c74030340a38d31932aa795f871fd753332670bcaae6d2f408d2a734ab1919b9688a3fb30b06953964b740ab8281fd63598a80fa4ef591dfe83892acd21de6541cce8c0c0f3066324329d28ea870d18818a96012eb89adbdf034cd277f00b122dd73c6072b66fbbd190beac276c4ebebb1c92a02456f483a116167e4e898d9bb87fddf50474813a7c4ba049218fbc2eeb7f12224173ce7f1ec10e93be8518b943040adc366c644d052cbd1ad80c9db2b4661707f1d0f29ca51f66324feb46feb06f42ff55eaed8922db4fb22906abd37438c8d779ca3b8fb7e113dc50cf64ad1df12f976330c4500c86574056a445590f2b74518c40ebbaae08f6622ced32d294ead40e63b086fb27bb99ad8c81f1eae32d1c1ddb660bb20c46a2c7a9584d32adcd63ab546689b7e26081b3fa22dc4ccbe5cc52a7b095c50dcb5c50d6ca4a91ad420c5ea6a7c37935af86be75212db929cb35e1c1880f8122ac59a92be0cd46cfc054b161188fa525563f9a0a9477e14e97bed650035117aa8efa4970c9f9318e46ffd793e4ebef2b6c803d90d99fe5a5cf487985fb68c068026e33a8c66eae78c50b92b07315c393f9de76929a05de8aa7c619d5abecff5303feec7223a2a96969b20b806"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3edeccf83339215b64556f13f15d9313cd6ec98966922980864d1f63ceee437b","proof":"8ce774def1544e55ed08b0efbbce9a5811b781d50f8b0eb05a50416220f2a2071a14b0adea4223374cc05d0f80070e365cd086c975e6dfd3f60207e30230ee07c855c5b51027ee0f8586adbd7af728717adb42ad0efb75a9493b68269239fb19b8c84f75f441385f8fce2eb4cd43a9331d14ef754568a4947614d30cd3635a5dd4ae3aa423adb44428d63101831ea257426a197c4b75283ee0392a05a2ff88030a915d23a48fcc0d2ebdf174581e371c91f31b1bbe3656934368c4b53c946502681b4242a3b3deeb85d8c8bc35a03d250261636be532cafdf5e4551fd2c75a098618748963575533444375d8b5b01d8f29b12c8b5418a77acd3635be446985285e111d5f392d04ece6c2155e647b490931dbc6ba76393087263b926f4db3c02b904af6883b9534089963a7663dda34361d8fa6b2393c91c54390a036857b564452a2d901d9738a8760063aeb9d758dee303571901842ed1eec67dd6526fdc33b8afcbbe98725c8df1c219f674ccdb3a965f6bbb86ec44c17fe1ea8365c8e236e8a4247f4a34636a87fca119b9ef2d878745bdd10d90d07c97acaa1e136e196209c369c51b9c3cdbe9b99ec1df386ea14ca66e857b370ecdfd68223bcfb56ec39268c016b632a54bc94a9ca2341ec75c2820830d47da8feba4dfbf42a66bf5c5ed88b72abec30cb0b5e3bebd99b0748b04b77f40d848c5e0c01dd34552d0eed6f78abc34fc8e2e9583ffeed3066d13909f64df4ce2c8eed23741af86f44c9522d4261eb3cb38df8c0d38f6f1061aab6321d7be845527c18506dbddd14c5811b191e741e044d8e9d38e22a9e9c4411152643006c204327e35c5ceb830c4617d2427d58eb142ad853fc03fdf9275a4162f6d31918e82e246448c0abf8d9009e3f00ac27f92c6dbeb1c1ed0f2dba3b93e640591eed940aed1e1296280bf611ef9906"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d40d8c482da4c668746032d8c0de4b56818b58766596e7b094cc946af96e962c","proof":"1c8cd2f90bb8555468aa06dc2486d26b38ee09907af433ca7a0785492ba2486070e89bf4066c8122d5b5a1c7c5f192f1d2d646ce9a78ee2075c67a8a614ed153182232c2d442ee52f30f7287c9af13f53e8831f54be8f6e6a41659841977e2538496ee8163f154f8685aae521f999f84e074812a3fdae377ef211a508b403865f94c53710a289c328648594d5e6a8d09aa6beaf11bf1634283cf24bae48e1b07d7e01ccd82b2d3da6874644b6385ed497b9c47a8fa4e5c85c985afea5073ec022045fe588c5d8256fa075c569d32a9a4e805e2ec04a752b99f843b4c1b8510001aaff38b960b796eb5c1aa113cefb296ab69a09b4047c65be61b6bd07ba4cd7bf0aac24c18e4a62ddfc1d8e56985b73f2bdd8b0719ffedf22a5b3956d96e986d2878ae5059b3cf0dab1fe300f9f031346dd320533212524de376abc1088b403adcffa253154e3704b7fb0a33bdf309b0025a8190af75f3b8173355cc246732471e61efcf123c62fc170dc9d8cf9f912775043b0733942b870f18ee8b4c1fa0189e756e84993b29a14b7077caccfa926bdbc57c72d6282625c5db68444a94ca74fe6e4824a812b063256d61a871f7570b026021912299fa8593f787de83c89c56a84e84fc2064064b271545ffc8482dac18f8a8ea11328377d1dc67fa8a1e287730abf1c7beac6751c48f88d7d7d7f13937fba224bbd4f702ced6f73077747e45e409f5a875d69c080983da6cfaf595e419b8fe70839d812198f2e0e13f60265ffab42d57a8d99a20269bb9525edc491afb793c960a70b1aa373a9576d37de7120c366a82bd904c8adda7e8c9f1e87c149b352be82ad9073815af24356acfea3cacb735910930caedb5a5244d76b5ba99d58c74a5d7620c941a3524a2f59500099eef1f9475a91a6bfd235d802cd3209fc257120e72b4fa853305103411ad2c00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"16dcef1f3cb02c39e12b661183243ad0eaba166eb2b0aae005ad8766836cac0e","proof":"74dbd9073663745696d6846b31ecc1dd00ad0865548e91add471c53676cb0a730ef887faf291aa1885fc443707b2cdd1d634a32191b3d7c6a7e81396117edd39c2e7ddf05990f905f624db2757d479603b6c1a746bc5d04f5739a272db1e0010f22ff3a4bdfb2d880cdd48cd4477cfc782fb861735ed8f8fed6dfc6bf2c55769c63de9414cf67cab69d19f393a289e0a8eca72f25059300a22e590fc53e55d0f70ba3ab1df1b65fc2440350adb735a9e249807ebfb1626675b9a037a9d3790076fc41d59a9cdd79dab2c3934cf3c5fe00cceca9094b553100d209c8fe03eba09bcce5812dda54f8210bc755168720eeae2594a05039c45b75c48176a55d15d203ca816c5966d9f38b68318eee18c5d3b0598ec7b985e76518cec233726201647a6757538620f0dd3cb5bdc965b379306f9cccc70b4776378d80a204acaac837f065a49fa01e030841b7b0ae2450d87a98830bdc9950cfb25a597b99c0fd1614e68425feab8192154a0a833c8ee97a8d408d2ab2ce2484570d20fdd1581c6162790919b2f0b5736be84aec139e38fc12f351e76fc6f8de5f701537ce6ab26793f3e553b292ae6aa11ef2ed16dcc9f3ad555ae3a70b31f5590d37808b08e07357a7e3e7f0fbf5a8d71d74c1df86650d662cecf47930e08d58c125a2a0e2a941276eaf6b207ac066206092d84e65f8cfc0a28745108ed170b673791edc811b1ab0006daa4de33f9480652dbeb5857cc8765d1675ccdb908b08b70ea09258440845f0acd3eed771f9949460ec90ebfa5134d5de58b5183e3886bdd39494fdfffdf661073a40c0c08df0839513e7b1ac10265852f146fc89cda38a7e37434c320c12a59f2a8102a5ed4ab9a23d62e5a7550ed26d90d5175aade07857ed400501a5c0df054109415217fd9b20e051f5e6c1e1c49252f4a5ae569f14fc274290af3f008"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9a6824c93d311388fc6ff52c974c61905fc15a9680bd234df54d396a08e64a56","proof":"86db81da85ace08259d9d325732461b2c2d63571c7adc9b7ba3dd12f4ec73768102216f1d37b1b6591f0fd6ce2009e5be3d0f2b354bbb39f312dba81b3ff9c7efeed3c71b0fa76185f835027742158ec7ead46f94c6e5c5a915dc3b89733dc37fc64dc495e48897674eeb2d8d12c03c8b5219a43230c4a2735dccbc952844f71a5daf38a67f1a732f169b8ca28e3717847edf9571d92803e8a297aad61890d06f8f1dcfb46e2a1a0ba18c7c829265877d79129aef130df9764ceeb59d5161b04c05d705a360f842ba611f70a11f64656fcaba0ff92634e6cec3cf8898b9f5d04c625d9171188d1a8423688681d7cd6184aa2ccbb39d9fbc2c7a323505d504c7af055e9faae1dba4b68861f5117806582108edc0b942981e7b94a569c769f3614d888a9132cfb5cdfdfeae82f356eeaee8258faf7e5986f231157bda1d2f5e83a96bc1a9fe7c8d09eacf1a322588daae9c32351d907c231c8cb4c2c087ada1666909b4edb813fe977e727085ac13b82f52bd3b9f227333fb52ae78ebcaf6ffd328458b8e447befb97e9f7ac56a94ca526f2fe7924c7396e54043bae9d8d50222fc83b3c559a085298797d937775dd49162918b0dbab5828cccfe6f9f7feb08a501e4206d7837c85a7b8372fe5f8fb8f6a1c2de852f996cc097e98b4aef8172a2f22729441451a1dc5533c7005ff1d3ccdf6f6a3bf78f43a2dcee1aaa0c0fa7e14d65c8fd8e4ccc3e91e841c2ae22bb976eff67f702ad664117d52d349e6f8185d2c535d1ddb0d8612fa5495fc771a0bafa5ed57a64a456b40da7dbf1536ff8055400776d96b800b0b176b4af5b3f0c7c85d2ca1545e5aac827d6fd4205098140358fee3c39427925db394e8fb8d206f49320c4b8255f372cb78d2b04d958957095e138c718ca70f8eef8b03e3fb129b582b11128d5b69e9519c0f4385f223d10c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"906f2f2e8de3c21bdcf1e95062f362cfb9258691876bc455cedfe0dcdead3b65","proof":"e0084872ebade391815068355227f6285c6099a3dcf9b4854c9d5efe5d1276788a8434a05d7af51b35cf30aac7e64ab9bf1be4b0854022403b49bc0bc5e32e7da0b70ae80bf851984bc9560de89e2efb5251af774a9fee2ca1f7cdb0af26f1145c3851ff788b1c3a549ed3d01d40811389f522548fe4a85060a77d6136afac6e0ac1c8832e9fe720820c51057962f43cc5adb76485f059daab04e4941ecfbb04c832cf666edd56aa3d734df0bc82cfb44793a3179973dfd341f6b3a32cf90a0a8321f8509af281ead14c3268c32e865f6e758cbd2fb2abde44ac7685d9610e0b4291edcb5e8ee8fde77b52efb287eda52bc00d10a787589801389e2984fc03061ce7ace554448218dc687b0d538356137d4b01eb6e909d600d6c1e80cece870e1666f45c0b42b3223a17f4396864504965457c4bde36f19f6520f1cd9882a223beb56b9469659eae341bdc626661b047deda21d91b142aef7c9ddb1fc57a653fec990b38ce95471b1703ae0147d41b9a192f4c233c39acbff28a769f80e0d2343ef9b555c87022b72754f60ce7ec26fe139c2ac9e4d7533e6f377b8de20f930ab4abe997e9f6bdd6b815d2661ff4c235640b4d5118c652dca42510d2fd54493f767a6a622ddd5f6f5af3546e7d77acab11b140ff3dcbecc2446c9275ae82630d26a46154a497edb8a1dd1de5d98a390b8a63f907fc39d6018f817f431a18061ec08690d65a841989ca257c24d4c6be532a35423c17c10bc24ee39b0af002d07dd2f3b32a51c719b9859fa3299a75c3b7eaa4da6fab5fa8c40eff8dec04278a4d98e905bab37151e2b736b7914fade6a93d2a15c01ddf5454f1a8ccdc8c5bd74f45337d597e95d6e749e18555f2493428de7732f8b6de360f8766930563c3e002205e5c2e498c4e3a11f2fa7760c6f22979333e23d7624a8891690c7a84c8f805"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f679efbef6ea4ecdd71176d45501eb2e06259988f1ebcf7f3134e9784686ef66","proof":"9a03827080521609b9f91dfa8f8136f28559574cdb32932f77de0260c321d10f8ed69c5ec91961ab06e54de655eba1dee902289a64cbfb0d066b4f60e88c8923f4a69acc19ff29f75002931bace6bdac8ab0546cc9b6d2037aaaf0df25d5c314aa6bc7fdad5128f842d4f0f46d58f6304ea1ffbbbeba204c620a260c056bb76027a04e9c79f1609849dab87a66069dea6fb9293aeaf37e0f08bd372c7dbfc9062efd43c4bc88ffa630b3c0721e922d3a02223f33cb0b712936b2947b2f983602aab0d47ca778a03b1106fff34a2a563e7164e2469fa366a29cb12481738174027c4b9c9e897dd9f90e119093a4c92a1655f0d0ae33bd018cea6585b5893cf46438c94219a930583e1d15d9f09e9be760f28913575d4227a7742bf731c7d82f4eec5d8050b8aff1ad34d49345e03c6179ebdd3ce4541d6f401740fb1e4073f94176c7fa5bb1c49af9e0f7444e9a39ebabf9bea9fc550b3eb8a4f99e616ff86335dc573942588e54e941ec728289e14f5f97cc4085027d1fb360b04ce28833983f4a50facfbd187d0a0dfb9b7f1e2f98a3b49af829f7d7745314c10eaec5fa3039a03a3b546a62b90b4e0a4a8c993dae11b602242bdc623aad90fabd4aad84b80af2d15994d996b2ddbff43df436e83e60ab4956d953e431951ef2711783ea6e2bce7de0fc5f25e4a8edd011559f5d8be529837c04dd659aed36f7459ac126de2d2e845e764018c82fdfa8dda7ed9986cec73e2090f80693d68f40de06a9a0515464c3a83721e203e54c9cd5eb748d52c56f45ba02b66ff060ddb50d4588a614573e5a20e318e0e6db0568a9df97e182db55702fbb2efcff77132860efb378747f048516033891eec6366c86761ad6468ce3f2cce7d67ecaed7d52cf83d02a9e0b8e237430924d694c5d0fa0889bf316b070060395511c12bdb53c491f34bbba09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"32b3029f48ccc2967ca19ba9c619c2ca32fdfaae4cf9fa8e9a1704a29dc5e032","proof":"96b00399e20013cc0ad41d7ee564269151e661fcf8c9b7afd0c30f481a08637f9ca53348bcc01071c0041306775a36797b146e69654b72ed082902c86598713dd095f6eac7273980b1a9066bccb4775921250707e2b81b95116b82c66988f87ab49e9b8b2d797787cba4f15dbed638adedda1ba0c7753c3313aad45debf3b51dc991caa72d8d82562d83fea67aa2e73886eb46877a0c86c507521d8d4219e602411d5cd718ec534fd796cec5fa7054cfa31fb77a4269f34f0d86ba7acbbd640ac172a4dcaa5c1e41c47adbc989d9fd93cda5ab6d7d974626c9b39fa176609803a4c7cf2cab198415eee50d1e6e17d276cdf21afb9127045087875b3c3959220a0e9ab95c17530573962b1674727ec07b9cc524b39a678cbbf7b5a484a693366d72cafc330dade09acc95a965158174692b1bbf94b2d0969c8ae7a42cbf443763369cc5757c595f8553733ed72307608cd76e6df6e7b24a5c3b3976696e42f24a3619eccadfa594b41f10baf7305be48b9c132be358eda908a67b882ccac4583116a2236ee583c639f4419b48b54e0315e074709ce148712e2a65dca55f755e0a8286826bb06021a521ad4dc4250453047892c7dd50c839618d22a576d43d4d5eca26cb3ebf0f5a4493e488890089e3c581554be51644634c54be90eb1e637752fe8b87a71c2bfde1dd3e2f84e245ce43d0208d1fb45ef22aa6657b41bed82a765e2f38b00cb19f00b61d4878c2a07b2a1d6878eab4e347506a5a50d638a89e69b68fd9fad337708cd613ea6e3554f996ffa16711ed7570c664ae3e757a9c2b107c3033e5f54c8bfcd2ca5824053b07f361c7e86b4281aecc7a08277896305458234a5b0300e3ce49cf035d1232ace2e06f0d7121d4444886f2ee98ac972ace080adc0ec010d2e5bec0f2372667accdcb95f30b071e6cfc7fd078277c15fafb01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"58aeb1282c8b15c82cd16dac067d856801596fd6c0be4d388edc0c64d9ce5031","proof":"2c66d759593132f78062e1e0b6b07778a7a90eab4b505ee245d3c3e0f5dacc56be04f1a47f225b9440b34990c6fab7780fbe92fff5003e9944c932988c6e4c57446da5285eeab29f6bb183006dd51911be762b46df93a464d449930084ff6133183efad1dbc1653e8bb762104a24098693feee8aaada81bea5fb7a5e965b1c38d94de80ccb7a324358cfdcc2b6229f87ec8c23a7bd421931a69969c43aa4a00435f731777f5f0ed15530e3aac27d941697811b5f21c73966c103f121a4f7dd07837d5d42eea401f0594647ab32528b8dd77facc217e38d6a1523226031bbb406f62a8333dbb8c4fc6e69c9ad81f0c3f618ea5d3f20049ddf933b06616daa5d040273a34df7e2151746c2a3412b3a6843c965dfa55b718f2d6c3be8049e562f6758e5f2ef7b88d7554626fc3c270dacdd692460babbb5b8e332ca05fbdcc7c11728ed1a7ff03e7b777f3aa386c847c53f41f4cd15cf5b3e9e425870eadd20563aba49af3eed70a52e3caaeea6d106faed795b51387a0793655c69840bf78bdb1982c16eb3600598c639a3c940346c172d6774e207e93e4bbaf6aac8ecf96f555c047cf3eaa77f6d49b33b2cf7e0c649e23cf2c5a9ad8a77e7cc4d941efbb96a23b452343bbca8d83bb3a8ddc643ad863e45623686fe1f2c53478d3ce93e951f605ed2fe9471d7b897f5e4790166133150f8a10250f6820202f9b561c672818366c2734fa1b7f41beb9a7539e12ea2d54d5753b215acc633f0e0d17acbeffed9659c227c9d50d3e532915ab609c38806c7d4574b817c56d5e7e724f02596f4de496862b6d0b49bbce1df294ade7768bdc7a8e0270ca40ca213def11498b1e8d82a79d03237cb5dec081af424915acb91d469013bc9785b247524af591a90b56c05d4c8dc42d956d224cc60af639911747d694be4f605eff00268fd7a91ca4e7a0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"92f8e9dfe1f44488cdaedc5b9961571e36fdaf95467900fe91b2682c39b9297e","proof":"cc86b3c02075cdb03df65eac89b5d410106082d7e79c200244eb74d565a4f817847281363b12e216e1cc051b5998250d3136c6eac93b13f897b52721e9fccb66221878259610b10f80d1e9b01a7de9b8a5a32c838716cb1337944ac52f368655849ac7c3b946c0030e7a57b9b48c83d9d32b23e6a5ba36199b12139ce8077b1ca4d6ad57e08bb548a6978f44606f90d19c4e16b86aa491c3842fbadb28adf002adf00799b4f690aa4ca9dd101227dae5811d72b9880cf13b06ae0d925c6ffb087044add0fcc06268f1903216cad24a68d04534ed1438ef4fb78072a02f29220de812f1a9373aedf489617eafeab4658d21bdc7877b3b7caa280ea7ca70567a60e846c590cb0c9b970bd300b6dc1adb08d1e0ec569939a34d398fe2d5b61d421720f746265a0c845fc3027744b82eada8ef38792709f3dd7ff4452283b50dac04ecb46c8f03b64dfc5cf333ea98dde3e1ea3a0c8d7d886df5efdb30b01cc9db1c50d759fcf7c1c2e081e729889da54fc658e38443b7daaca430649bad309e545594fa787d0135c2a0c973829235a8019a6ba515d7bf6fdd24e8a0cc03643998475eedc0a72279a1d454882cc17c2fa0228ce9bd91b5a664243b6f491a56056301526606e6199db437b20a1561107d0bed0c5509e5bf6cac1b8e4a9e9b94d4b764ce07c1bb3a0e2504d18ab1b6e41e9d3eef6c43dcff31f70be1b25cc78bdae4185a7654a680101ea97f21157c4d03a5b73ba3c586bc216b9d57bb54e262272269b4e2bdf7adbfc22f025c778263c28bb2e2070806f23dd69567efd0b72372d26156b64a62d93099b23fd111cb0ea1305a1a0859403daacbd9936b34b833871933344405451fa5f377542fe7c700e1ae55434f1781f66be39ed49904114252ad018496b65c29088a75d19816ef0a911e03533423de481bfb13038319bb533f8c07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f0befa10fcc66732e7d939600fe907cf7793f220168e52147777ffa6e2c5c178","proof":"3687ea202c31c789a554d230df97757cf91bf9b10b7e3b1f7bb5011fa7dd4e6e82cd6f2a6ab14902c24fd08900b6882c32de825de8b99e0ecf7b445d54243a563e8a6de7a7bfb22509766caece86f3e1202444f28b534ea830393aff0b75af19c041c4d396267f2a4a468975b48830b2ebff148feaa4341f7e7e7467bb405157c3412f8bc445e533c72ee52c3d0d8de96d4da5bf4cb745b26f62ed6936e772082822ed4981bb769f40fc5c1ef71585e38db33f48cda5e091536de36fc0d26506ef199a7a8fca9a531ecaf2e858f787c857ec0276024245b9db0acd02574c670160c7c3a13b64adb85925df88c8b7d7f66ec7e294da7c79e3aed7be7320b2b97712e064fafc62e7d68a998c7ae226514c5c73a49d88e0577e8ed9f77ea0941519ac6d8968930561ece875fae2132f682c6d023537c4ab9f7e32fe85b9d9a6952f28af892f6c0dbf3cde561cbccfffc1f29b6b70c044daa7387c3e5289a12863179277c642df6fdfb008d7f6da772c9c3c485fbdcd225869dda108a05c10285946f4f4b7b5c51bd316c15cae6dee69c9908b7a369be83cf17e404596a9e603cb505c593ef03a3085b3015ffc49888e234d3adef27412365537ace7a779bcb3977ad6ee6b940790d45c906fdc08a23b6b3d04dea79d6f18d59c6cec567a77c82c74fa6cd3bb8be69444453c5ca4ebee199b219bda7236902bc0b8530d2730268179a4fc57f0190df6d62765d1bd209994d6561ae7e82e7476f3afa2ecd9ae45614fb8c012ae637b6b6a5accb56c5086340e2b55ad5f69cc692441c7f1f4f65b5f78ccb822ab4d8a4e3ad46db61afc44385b7878c3f98c2bd2350dc5a89d3d3aaa0a37a5de0bd7e94e58b065a2b2a525d0fba4b224714479027438c576b0e22b090c647e40c2e4a164c4c1bd974eef03da4fc0b99f66c60685c1516c7b99143ff402"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"60dc66535b1b95675dcf7e10ec7f90713868bc1e4a3ff97d1186608cb6234627","proof":"ee856f746a950049ddd76e734972a54a076879ddabd4279bb0f412b0e925da53d06ebc2dbc2309ea2bfffb2054ab3ed0fc750bf0e0342eb37d8a2bc99b2f2c740648863cbd378eb7ea2a9274ff55b29fe70e6dac160c4a22e27429737d063403eeab40337bea70e60109178c10ab1270b1fe22f7bd2f7ce4fff7669053e03b54b20266db24f41c67af3429088db003fd919010bffecca21d39b076f2a078af077a1e57096ffbf227d5fa2aa618c5911c35cc5c91b228599cdb460410be08040dff598058a2301ad829abff3f0f8229e1cbdc777be8d29f2417fda732ff5acd057a00b4af1a2752f1f477e628d54b22085224f844abd3479d20f0bd690455724ad40a1fc96a6affc68678b51a726fea5e847c757e9426a7dcb019178fe12b5f24ae37ba2c485745465e3ca937dcce88db310ae54d24ab1c1c7e72cf9c00cd1978e2cbf6effb7d8e835f2e6d4e57d2af566e026b0eea66922121a0100761084b419e1ab882732ad4d69f3d48ee501929ff65c5ed0c49a030f51819d0a36aded70b882d90872df927be27c8207d508e97addd202b02e003363b1f4fa027697320583a35d265fe146518829c1700b3b1a4d0a851393b9929ea1bb725f68b7d4b804976d00616837431587db5ac84cad4d56e4b23d2723bff2aefb2ff66ab244b111190a8561a1d0cb4968a757487c7fe8116c8719acb4dbf9919870072a49f1ca84f4a2f31841c71c612a651a7274154cff7c04301c69bb373a8432bf96875307057ded8d65bf2cd0f88ed011993dc9a12c30df7d863f93ef713319084ff3d22b6384816917d11a91c1ef911d0f1f9a244b5fc76be15f368de5f0cea5fde49cdd111effbd2cb48cb4b071eda926e5894df5284802b46329ea19bcc3c6e31fcb74c0396f257a0c9ba182434f5583d095cb48b649275d17d34c219b5ad8d5fd182250e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c22020b6e080688ca5df284be5dd009ce69bbd0773ceda8b15a0af05cf1cb73b","proof":"343a3a5e40d7628f5c412eae325d5c0ce6536114ee2f910b1b7a38ec906af03b14a92bba02bdefca2ee1df2e986109392a03810997675e771f83fa619a48671d5e126522f5d72ecedf49bb3eae579bfb0ff4e8b1de8cad536b39161001d89147c68d72cafb57e83746ec89e6e140c1bb3b3f048e40b586df2c7a93dc71493a0937b5cc0fe1ea2d8d4d839db9b700fede6ffde05b4e4800b2f5ec8e5b8038580a070621fd8c40d829e4755d18a10a0fedf2ac24191e724bf5669e104404d4ce077c7c93c8771a5923365cea7fc4416975f542dc57c2c4283b4a01669416c6e8008e029c811c25d228c1bb52388e9a8a90c4c1fc453b546607477b1d6419120437ee5ff1b494c0b3a344139bc09dad44bef6f792d5e7e3ece775e416b6e200d3582a01500850e1f861c1e03b31126c8dd59d4fc4232692cbdd424f66bc90301c418a5b17cf7cd171c49467a923c21f2fb8e136df025a0044d1dbb1c1558265cd39d28fe8f06949f557322e53926e529cd265b456bdf6f89712049faea2616b42280a5cbea0a09f5fa3e993e4df44ebd8d571e3e8ab602f8303a4666286587bdf54366cfcb202a0d9fee09e6b6ef77360d03af16faa114d67e6abd0dbe698bae67cdebd869b0aa49745dc32ed9859559e1a96cee763927f9db831b14ac86349781982ff53fc4300e0003a3e5062aec584ec757acaae9b9aa4d2acd2ac92f3e1674cfa31660660c347a41c95b668c86631b6660648a06759a5dc2a69b59cdb8edc219ccdf5d2204ed8baa7a298aac8fadf70eebd9612d9b57d912746f15e3e71c73db8400a6c2112bfa1a5066ad016e299be58096e46f3ceafc68b16037501161b5c35993a2af80101f39e36f9daa9b82f3eb9f69e919741e2cb4902dd7e00d3b90b7c5bcbaab142967c59456bed9314d5fcd617384efb90d823c5b576da51dcda08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d6614f38ab67ac5ebb0d3cba6ddc2f37329d4f4dfe065ef76c5e19928152ee21","proof":"aef6de7140a2d4123e1879c4548145cc3c35a5cd89c20e50f03b8d29dec070372e3463edb4c398b5c49788af1e3c33a06899d6ca6233e89c67c711f06c45b630ba85dd5d3b757c376792ef27ee89a2e80e7d18cd931f701b875b4a184f50934d80278117e2246a61e8e442a070e90d877b22c5ba6e6282e342ddadb4d0143766e6816a93b963521e87599f70a4b31b19446f4e63d0dae4ba7b654121f9f7a60e7992877847dc74208e5bd4028bd4d05d27b5cd3d4a5d9b9353c61994e1e0fe0897db372282c3d0d0f15e9aac3ced8758871e31b8971e674e15f3d4b5f0a89d032c29998cbbdc368827c5dee6ad75b9259c6d34309da137a7da3b5c10ecb3ba3e5222aef17cb1d0024d39baceda472c322f1877de4d0b287e0a70c4759342714626c38526c4e99003e959c80bbcfbca0b7e4b2f6075712b930b5fdbfa0e2deb7476db62b91cfdd709e764fa2d9b2a8081bb747eea2cea5776ee4ccafc07e61f735accec22feb3781908a67cad06b5fcf1195dea42d04fd126093922d1ea431d5918d18277534808dbb073f63b0ebc63fc53deb3c20f99a6a511578f15412bed78a6632c2f8d5216f2e65968897201c0f8e038fbfc34a2558c44b222c42813fc2e4a1d4d18adedb691910d5016eaca289c44a5b53dd30516d0cfff9cf81feae404309b37d642e7fe8e01c0bab788e7c2508f3db8a5b0c69073327167cf79be7240d6d41447c183f3d9e76f5b1d8c60a23af3d723983119264739b4358173a43a121cc6b2db074f7cbc51c645ea02268bca2264bf22d7b3693423d16516e0b0da0406c8b6414fd66310f78503e93563e9d66d4aa61d61ec2770d14b61b83585b05d87bc9e33eefe37a655517e77fa8debd05f47865a5fda367850e29d51eb4f4206419fe5aa6b453a0fe2a70a29405c6fe75ae81baa9e5459296176281485787d02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1c6019e5721fe82440a45dd7454b988a51a2337ada4b39ee0e50906f62a03545","proof":"3861a1cc62fb1aa4b466d9532d93ab4910b9b451e8fd35c356ca3bf372ad9724ec92b0ce081b09c27ddcdea3035c645980016ac1b2e64f5de15c01c4b7646e79e43d47d8c2ae5989a5e8273f33bacf8ef6f21dfd77db9a126bfbccba3fc09d05a681b5afe99dc357094184255a812fe5938bca1e46f2fb25daa499db1cb6be5f41da1809359128df1c6409dd7fefcd9be7fd3442c37ab40fa829d8b7ea3075019ccbffb11407042d3c5ba06fa657c7f3f69d5ff6ef323804a549abeef367b70774bf5f1cdf68ff71b542a2e2b632324815027adda65f9aa436664da6f5a2c60d96ca3ead09bd4c83eaa9548cd0994486d1cc316483d53015f7c6ae697f63770012924f20e44e0c12d7a5d2b3d5849edf856a52abc9e1bef21cc56b6b1659d96a7e81781bea335d6e99a500617e4139d501bfd293fa35fd3521bbef120bbefb2692f36d94df124157d2d398615105261d1741ee524a76869e58d68de5e14bcf57ea46adf6322ea5d51d7325f6320a3f0faa53fb6492e93d8b08d277e353374f159681db2c9a2696fb9bc883d5629fe9e715d2e278c1033fc008bc2870fd5d84634ea296396302448e1de6e15748a9ed2ac63d7203e2ed00be919301403339eb51409d976eeefef75f4a4bd5686f3e5876969691b6a472b21e911a2ccb4fd1964ab2324db506ed78215506d938d623b4480041b5b9c860ec5372c44df63b74d451f098b4690dafa46dceb5a9323d4a6025b5ee3e640f87bff9752aca94fef7fd3c08b48bd5da42e0736d2f3577ba806341248d2290243ac3d788524c2b0a77f46a8898bb28b27996b8e082a2c5dff6a89a6f0388c9e18447f1cbf7d335b5888c204e3707828ad4cf0777117dfdc4a3b2aa8eb79cdd81ca92c1ea48741e45564f0e9a5f0bfb8dd7429aa66539f1182837f69b8d6916d4147b4dba67b6cf22cbc20e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1ad150c7c4cf2e186d306121e3867076035a47534c8bb362a6ad16d903414810","proof":"98e9984c18283e3e0f3b339e8a43286fba7a582758140f163a122a1d0072b42df0198d4f98c72356b462c5cb114975ff123e00c228576d1499e24dbdffa5c753b21339389a44c17ef031ffa787876e06f7c763cc002dd4d68ee27aea1240a757ac3c98a13ce490023a14b2072470fac3762f6183a9ce48384cd810ccf6139b4f83756f8930873f2ccd36551c5bf4ec21d6b301ff141cb8af00fa689763a31e0fd4021236469596364d551544b1a70af71b47b9779fca519083ab703615f5d80d2cb7fdfce4f1e3a972ab28e67fbf39e6077bfc3d79a977b7bc5ea86b5d17d50e9064c9e754562cd9e4758fbb055fb6277ea6c893d0a344d4905cc9da58c5a53f54f6c0f1aa575f2c13e0d542b322b3f6ad4cd75369804dc906bdf43ecca7e74d18c902d6b3b874026fdeb64760483850e4d8fbed5df772baf32336cb98b2db1eca8a61239beb40cf6674e8810e699660f5ca1bbbc858afc1cb56ecde7d591c499ab7bfd6c32665f59c32073d465b6e7d2b3c91f9d8a6996356d00d2ba3a2cc35261e9b21c00df0dc6db7d57ef62efb2e90f22c1f326b18b90aa59e205b9b85594a8e07f251d34f5cd43949670b26818a90bbc9047b7f007db69b9813b0532e4264960fc79b5a94553ed623b43cf57b490dcf4f94e39a2a46348b0d8626aa690c303df31d7843d3756bdad2ed7a08f7782f7b5e4a2d5ea8a8362a7d433df9f111bc53c698cecdfab4de6fd96aacc047dc6b41d5a9222728c1885a460bc79d4b773acb27d42823a91b05af07598f200e3cb7317124d86b4b2cf875539eea000956908a514157a9e419d648b739488c59f129e525656e367047880f46807583db6621d109e63934e990cd4ffd60f6cd0159c775cb689fd687ca10a8a85bceeba408bbe374b709a58ad8c9abcb7563c9ccba9db736b54da337d1cb42c4642620fe0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3cc3ee2773095f742e45599961498044187f03be8f08013c3e10960b62872c27","proof":"2e9440503622d0e4a62de6a3462a87a1373e5e9ace2277ca5a7a5e14c9869d3e3c6c76fd469fdbf3ed559fb4cac8c7686a55af9a366904d2e410c1e5bbe9065144bcb58f2085e5e181ada9aac3ecb3f3d44acdbdc8aa677d0caf387c67c9a85bc8cdb0a493cbc52c5be658689f837cace61b174a7ab8c8bdd61cbbe7593e84535ae26cb21ce14e183dabba45a62757a7894e2c0220ba7bd16aeaea833103d50582511252d3c33b977977e41f20be1312ac9da5cf74d92678d97f20a09543630ebe3fa886431fa5cae50907e2287b4451b068fd10fd8c976ef9a9dabcbe51f9026c26cad625a3c9947f88a86dfed17f9c930ec5dc39c79604b59d60cb5af7c632d2aaaed64879bd51bc658a5865cc26bdea7964fb563d53daaa660e2cead61f3d28a7d45e7fbad5033f950b6682ff49956a45bf7facfe22445046665a3a7e5e0b6cbfc84d7f7c59e7864ea026da4c6004772e864addf915c55ec4ac55215a4b2b08c730ca5a1700fff140cebef4e78c70e950989c4b9c005e064284cab2a2282522af3e4a7c6922fa09ba05bb4f20fe1ea75752516c8618ad3bf6a7fdb00c2740e8fb49f27b38d4469d636545950eecb0d6a182dbcdc5f91fc74d7124af6e607d405097bb451a11aa81312d3cd99876b97da857b9518f611c8ee4c929ffc037110a85bbf427c726ee6a375c0bc86313a9b5a8af043f6fff1202c1f0e6842d574a22ddccb52922b284d04382daee9fda7a5437cfe111ada0bfdb2efde27c19cb471e0cbf6676095ae6e127060ccdd7eb32ba480c2f84e75377cf2e41a355d9af145acb4f27df350d17ea460eb9291efd8feccb68ced9b90e6d796190b2df1ab33e92797f2a3cb0cca6e1de1337c312a13fdcd54b0c1a865d791c884e43ecbb600d4aba97c305d5130c71682685fac595da77208762f8d2a549829e57ebba361803"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a058765546af8d941b0eb0edd74d00fc20d5d3c691c7789ad6c1197378b70168","proof":"ac4eaf1d723aab33e5d2b65545169e503e14306746e6930562a72ac9bb1d092ad0af860500eb4f042fe7948a71aaa7a196e4dd6bd0f933cd93a2b9810682e43c8abcfabf077a8b7d0437a137d4637d3f87151da94c3f16d666bffd1460cedc7d02833b47913132bc9e98f021319e8f13ea9bbfadc0acbd019979c1687bc7e26e27a10025bcd9c1b3aaacce403c6d9ed28d110e900aaa5f911085b55e5f9f3402195093c440a344b08136082bd247d79bd6a56ac0e678736186a8dcdf41192508bbeca6b3565393baa3c6f8ec869a5f233b6bad93d4b3fa899df2455b5e9d280108e85bc550108ccaf04dff1947e8bb28669e885b4cfe05526bd64b4d975ee52e0663abb65350d4f549cf7011feac9014b45c2eb2a60e959542d61d29856ce301180e0f86e293da2bc41905eb99538fba71a4b792811857c106b49132255cd1700054b660295acd6e93bd66b18f4c1cf06d9b7bc875d82c576b4273a112c66919d884c3c627182c13093d4fdb4457f21caabd34fa27528dd5704992369dd51c0f5ceb7e74da20353049649c0fd58ec255640180024ac7fd05d6281fc2da946f6f80d6daa3ebb48f131fe2bee0f998db3a879eeef1fb844bc3f0a3fe49563ff3717603e0a54c0f263c46f2057d4d1290a95ff4f8c987755aa4530d0316507deb69cc558c784c789c839c62b46fe0f6f8f8e347ad6374969abb3497d4ffad3fbd4af0cc4aa258030598c5ffc26d8a6f4ef0a2a579d671216b0c7e0a9b5480d81f4beabbb66ca7857b9605be481b314e2c6e832e370bccdfa61e40f5f7957a6ed15276e9dc0b881acf03df0e8b72dc2d72961a3028ed732e6d7f998887592d1c3c27eaf205d9472242f21049aaf06c9082c3f1edf823dd11834e27109f51f87da2052f8ea6aa17336969c7cfaa7e3fa22721dbc1bfa385f3d1c03377ec92c6861303"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b01b81a44e9d31fbb01bcbb11fe7b59499531e8a5be107b13a4fc55d2c3b7875","proof":"02f2b9fa1acb3dfd8e39471da9d9945fef98b1ba101836b4e49c92fe42379642b624ec0913ed156b62a7c871a1aeb5b7289d9c117c879591996bf5c1a94a32742e60817fc20b888d8037d9cd6ff6fae43ebd3de31b4658b1af40c001b585553158dd9fc19de769a9fc035e59a7bc6d7a16704a87afd5f92cf33935e13322c32414ecb12b40825c0078af3f8b311e18d0572a41559d900590ebb89523c2336009e88777f1b90e2af0ac41d27289d653f7295a1013ff4e51c27407bd4dd287b80880ece18f228fa24994c7592886e46fd99d89eaec5a93d30663b7313f4599960092d27df43433844d31831577cb020d77186a52dcd02e3ae6b22171544c364a115a2c2e934ddd6f8e50923a4a96691add52f1c09cc2247de9c0a61a5c4f43825c6aa9c1cd9aaf9cfb065dcb51c9f58e49fb3901e7b12d315e47b9374d1d9ed334e8a65f5c481dc6dba5ab19920f56e53532eeaa33ca3aa59850c3f3233c0bb90246dc23de0a2eb8cf863fa3cdec34d2f3733241f0b8b6bd09a525303fd4eb9568c23de36d26fdf414f9ff8b71d0e6a24bb33671db40f9eaea764a44873b23e13ed23603a565e7e77d703c855218eec2ea331686f2b397cd8fd5850519ffa4963a0655e110f7dc92e31214e3cc28a1fedfabfc9867a1bb5b7f7ed4c95a37516a4d823df3a8d2d6d3388951356601c9faaac86d9942c8f54e828325ae3b453ab5184c3f11fdd7b11ccaef4faf3a21d4b9603c304bc710b0bab3d71f5b07d011386cdecf05b97d6aeae93493b6bb99505d85450265ad25e21f19641ec9cb66e4a44d400f8528c38c4fe89f20b51bfb2e1466e56877b07d784c17f93f4c1cd85e941cc05cf76e558b20d073b2525da53cb7b0ceb50bbd635984c897b06bb70fd59706d7117ca2e0a6803cac4d011bc6babd4f705f9200cb192ad3b14e36ad39c75604"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2a50410d9c5733b8e65f14bcb2bd151748dd53a3fe9e1bed21bc4fc3fa9d986d","proof":"5000504253050045c194f0aa96e906ba56c74a006dfd528eea1fafe4a2449f4b1c60aa6d9cc9bc76c739eda0e8c3f78fc0b8aff51701e6b8b6003bc6fa074054f0eb5300c7b215ded12640e3df241223ce787402caaa703e390870238d3529549e0b251a94a5d9b8d13ff0de08efe530ebb2940fc1b5eccc4abef47858db365d9ec8fad1499fbd43f52e54b034ac288961f45654d83e5ef5bc28c3d5979ae50ed228f4e943df242a230460a8557549d05b817069234f689a2e8f7aedeef7b3060831b3431aabf0c6967752a5da6866e221b14941bc2e1cf4c9813de167fddb0fb2cd32393461d38af14440d143de367b10cf0c598a0d3a94ac3df47f4ff5d2415623b0f82672a50ce9a17676a45d567fc71f22194cceea571a696713619cb64890647b5e4d14e4ce39fe56f4b3f5320bc15acbf6cf0b6c86745542ff4f626d1ccafe6f2e0e6eac997191b7500fe90b59c7508867924df950d6235778e04a29319e25bd908892f95bbdf734febd65593939b9acb5f6ce65545bf22c3f3ab18224f40720939d663d2be622f785d6281c2287e789136ab721eddd64a55bbd497a48f23739bc5153cee9d0f693a6eab0dca2ef0a659e48bc70d4bf818b3b87a39c7014d33ae0fce03d3a6a9f1cd96fc06502dff7571914bca7a8ac7ef3c52f349c1e34066c02fd30ef2853a24a9c73f13d22f8dcf0c681a209fdcfa47a969940875b90c051b6d6d5eb41c2f8788fbcb0d8c599b5ab3e28ce7d9b0eaae502ba75cb1ef8fd7a6d44703594c7195f896e43a2223b13a14f6f72520ebe6af1a34c9c0f72ee272d6175af377ecbe4214a1b6edd93374941d11bdf332937a7dd845f4c3b0232046178e181379ea782b3a65b452881251026fbf70cf103fd8d9efe1187f00481769d287b3b978175dd8042df1de02f1d8a3787a92602a3e86cd6d36fea3009"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f6d2ea1f7ab033333abccbb9ddf89ba8a19b4f7f3dc4f75e73527c2720df9b66","proof":"d62b57740375bf3f8f238342918a3cbfd044033459e2d703b189d933293b377b8aa351f71bb69603c5e7042149006e53331c8039a319742a2656d7c8c9fb355082f4af9d5754b7a6f0ae1f26a47c93ddbde728ba01a3414f0dbadb96e57e2247a2c11e2a29138ddc60ef2a15045e0d8cb60b195445cd21f1559fdc25b616b96674fb54c083134437b1036a2c650bfac3f1c9f6bb9af6acaa1504484132e91b0162f44ad2ca68c4d09e9ae44b55af942aa4c204c0c8bdcb44b9479c05a70f0e04235c76a14ad9c45a6a497e80b4e88ce47b559aea438ba245d1ad8aa4c22f3900387eca4cc11dde7e42acb74b2781c513cd3419f61b86ee039fc743cf0e277d4be687dda1230e351ec22a16f4f546979b0366d31c9014ffeeb5b8aee0fa2f2408aa2bd30e864287f96e7e0ef7238bad382e437d6fd569535c2f2eab69b0eb526852bd1c5c0690441de577489b9aac950a156f6aed5c636f86003c2d4cd9531b6610fef3c643e082adb8b861ea2cb743fee92b745eef8eb8fda44541143207a719d0af0fa1fd7fc169da83b18ec2ebe72e6fe31a636a6c9aca59e436c20627766744d5335c997ec0ae3a644a3d3daca0902ac2241984d319818d96f3f703851f0a4c470fe23c6bb8f96dfedd33b5244db2a331357841965ce11a978334ae41af62a0e72bf099eb6d1ecfddf3ab395564b1620be1b436f328e2e31d85dfbe34913b765d66ceec08006aa133ea5c6a41544845e82b9f3d1c23452b26cbf19749f01338c6f7b59b0edc5fa203cc501c6b0cb5f7fbb0bf272e4fad6285f8352b21b13f72c8a3966e23c02a7281e6c249076250509dd55d664824d662fe13e7aff23a17daf73765668c391fd4741ee60250506e65ee2e5f6a181bae71e911c10e18080895042e01e62d39b159f452efef44cd66db96742524be1e077d12c58e30134200"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"128fcc58d05158dcd9d18c1135bb16255df1430d4ced41f9dc8d1581d6b4b33b","proof":"0a77191270cade85bd1d46c7ad431796df442336fc4a40fe20e5a0058d98f71f3eb0472aca08dfe592a98980d1352809003d4ad28a48a4d68be2038e92ba181c42b6865451bd9af57b8e1dfe7595b8c593661ede2b7784e305f63a299cc9f97aae22f404801ece4a8bb56c7319a5a77a349aac5f199feccab7d5adce9f241e2920082b3877ccc05a9213de5b2ff55d5dc567a63e803105b48f5cbbee65acd30451bbc2fd69774abbfb843555f0c452e2cc3607306e5d3159b3e11eb2679b360945e800e0d9d724f1cd6497dd4f922ea76d10b2e3f55254a47c364cca3afcfa0f9e4f0910bc1fae296298b092fecf600e068f9cb83cc6b458973f250cb485012f7495e1f08b3a4ff739fb154e3e982db983cddddff3f918d87cd6c36ed66c002960654266bc62898b9bcc2e7496b955e97718e9014a3287bd9c07a744fb6959485a837073c5763747d260ee38ae266028e55ffbf2caf7b8f61d2dd5c97e32096bfaf1d8f76b77841532c4051ed56e7f414c8d846b307897b2c0b05912afa12469aec9c692e11fd1fee5c69f225deccbdf2f6eba8bda1afac2cf440b59a6cbac1cfe426bdd4836a500e66a61358df8ccbf5169f4de1a5c73d9cbd2018f749412411c86501a58ac8e0ff66dab690f7487e53ae26d07c60d446138659251ee1de40c2860316a499e1c0695081bdb1aa80f426a8e14c029a3bdce4aa3124304a83533a2d139ff515e6ab4b17254294ff852d693f70a678c222adcfac1640a3cc3147fa48b719a9c1ab5f5e5a02e2cce45ad4a1336571d07bab119e24f8543486ecb40c4da72ae4d8a5afc22cc64e0d4c8d4dccf5cf12c117214664892a7dc35025178da7d1cfa1ecd620a53ddbb7dc7aabcdab2c2985a79594b06c4c603bef852420da102149dca8f50793589148193d16c27c51eb7f61fae2aa6d6279dba46c67e04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"784abe3296eff0ceee7e6d1b8e9393d77802741b4f769fffdeb5e8c539841719","proof":"6604a2b11f0ce0f044f14dba0be27fc8e26e6a92d9416be8c728a8c173ec661e5214c685eca5ac99ce85d2096cd605087f986e59964a8f24b130840dbdf6f70246b3be64590ef2dae30b5f1024a1aa437d020de97eedcdd9fcbbb96aa2bf2f36be9741a29069ad26d08c04abc7ca1d4b08f230fc9a6a06fed1e547d5fc782f0c6ec38b506532c5e503c82d302f9976657595f9a354556f11d0b11e67f388c901de4bea0e6c0c54a2dda60341bfd1a3be6a53e3181ec9845d944dc9cf36165b091df0cbf0b512a7f2f49889992d817a3f9d8bd3d7ef544e417e5d4bade73f6100caa7a4682c20b0bb14d8662848382206218c590135968bd06b5b777c65f7f34386bc77ea1ec6ef9513f91cc73e7bbb7f15df39ecc7799baae36b1125d1c3ce0632dbc92c8801a01b6a37d83cacfc3579fcb407c0a897dbc40338562024d7be771459f62998b7c0eaf7c54d7bc9c140b628f3e3467e4153f42c3f09c58ffee03d3e1db57bdc6d785956575744360e6372341995f68e533bfbf9afb664d4369e04b02b4a326603a5b547127ca1027403d5e7b7c3579b7cc173fb6ab592a0f362209a699fc5f667851aa7edad2155b3d12946fea44f7ef73866cc4ed6f100a79b1c468b04a8b03abfb555283559f508fe16acc3ee477be6a9b9f916948564b3ab26026421f730f1269adce2fe1576861f25034c9699471ddfc5fad99e941b090d2b54a340295e2087bb0e2ba18886c3297fbebadf49f00d053551ea81884d3bfb73328f1ce6399b8839bca0592a6d9a512d1044b9b10d033f2a2db8892a60165a7a80846d2758a8e45f37a3d22ec20f96d77355750484a146eb9ccf2fb19e591d764529c2fccb6e0fbc140b9d74757ddd589fca569b29036e73d9e615977a6e7d0c3396045738542975f50ab898eece224d460cfbfd0a09752f427ee7660e53750d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a0029cff9d74872fd59c963377a6d09cafec985532f144bdf9535107bd76a578","proof":"321738318f0aba4a327459475f238e5571adec415a2f6e42db71ab1c541c205e24e3920800f070bf7cbce3c9428617ae170a89a9759ce0b2aa3d8211cb59e44516482769494669f8d14fae97138b12be46e7e74cb442b45a3d3e5bdf489df1602c402eedd0e4221c27ef401d467ecf58b19e6c9399882ff04390e3bf60b88f20392cde07926eab3b92397adfdcdb6e6be300581408c592932539f567c1d1630fbba55e61202e67ee700eaef464ce3820659633c1d36af2779c4f302674fa66004479517c2efb921fea7de5dc572aeabe30f4030c2aa4cb1a50226902bc0aec0c50c62ab9e801b99f9b4fe958274a8c9d5e47c146610a2902055700745c1dcc71d0cab0c533fa1ff3a24a5574633c6668d2ddfaafe6ac69be9d8934052309c01460498cb8a823b9c439cc503b14625c59d2cfb6de7e355ae12a5b519dbce3b0084e4dda807309d3c8654a53fa5ce0f44f4997710d5ea4974097a1a5aecce06c3f682fb8069b79eff3a1fa537085b2b8b209bfedcaa0cd273c1e2701ade31d861ea6cc07c4bd851e7a9e2b54330d998793d3687d0a2270bb808872e049facb3b4674aea3fa4b7af6a2971e645f3a8d8a3a076c31adb7e89e4eea1612ab23da3a5fa0f3c2fba804ffe887780b4b491a2cb1f5d77ea78ef3c18cdb0b1ca4ab8a74583a6afa60720bec885bcce25f63d06c0968b587be9025213c15e2ec69ba61e46fe0557c4dfe0d45dd3bfc752b92c82c5923b5bef5213f465f75c5c4e535227b63b8e274b085fd8d89e70a497b82190d2669e041d2fdfa1e4b2b09777eac4e4d743c6abd1e5fd8feacbc740cdb2c8a8376f3c751a094e5e505a414ffe2bc2df02f7817959d8e6256e6cdb5fbc25d3f280defb210aae659828eee251195ef7c6303336fc434080de0f6d30e75e6520cd31111e793d31f58f508a765fd42ee2b270a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a0085b99111f0512fd2f232e8289323d2fe91f515c74b11487809e8450740e30","proof":"cc3153b215aeed4f155dbe4e8f07820c91d1247fd42cc770ff3d6d3ff211fa465ed99a2b68f1f3e31827370f55880b6486d520effdec33362b7910b37ff5ec7c56fbc11cb60313f3c61f4db349277f19c7fd28c001e53e06807f3604242a502fdcf624177a1e3c0a1e55260f0f815c3b8b98fd5650d2560128fdb9e3d51a4c5d5b1de7ee05b2055ecad3fd976d5f6f1db094598f806a55f99836b69f46f8b304fc95af1c49254a5ae098703c47e5ac4c72d6bf7af5149a75b9201ed63dd56504ab2e36feb5c134713e857d658b60ee497ec1b68c7a3864f1df2408a65425aa045c14035a367f2b78b24e95ac52b86cb3c178b0198f04fb178465be8a75675f4cc68f1d40af57b8a03448deb303af8ebd4c981b079de67ac42ab6ec7ac5b4cb28e6afe6891ae4253c36dbd7952b00ba442ea5b4a145d035a4d4eae48a88796f0422db591805b8a32d42f4a797de65f6a968f5504bda3f0bf2bd699833fe4f00722e7ee725c526fd5168da34d1fe7f6087a3204c1bd8e319ed64b6521ea3db1315c65ccdca27fbcb74082829bfd33b5785f501f1405b58ef01cc9231b2b4d2b91da4329d9799a882e2ea97e2012ba7ef11d1edbc536bfae4519adf48be55ee15525a94be02e734425283ac7257b7e1ea299e684d49dbc474e17af5a51ab9f1ab035ae011844453ca004e95b302db0d335bbac7d04e95635bb0c96844293990d92402f127ccddc46e9931d69e84e0f81934b022894e171cff1714b9be1398d006682efbfbe15e5161b652db5b4672dc0405eadbb6f071dfd0d7eb7ab669a0189662d61dfe839262523090ced39f1842055d8db46cdc7d75ec1f024452b1fcd0c62065330555e232a97c9b46b71e384c1ed9cc8fdece942625e1ed485298e2b2300f9c3e6cbd81ccb246632c5d9304129b29da5891794906b04f26c7f25ef318e203"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"16df8497562e8f8492eeea6f5eb4737c50a19e3b68837d90e1e57f62c1c08b69","proof":"8e615e26721f271873c5274a185c4869fc3e1f8b926484ebecbd87655e2b02367af9630143aee7dc8ec700f949cd8fe18f3222d7b098a2aac1e4889f1dc2212f2287cd2cd7650cfbdc3ea41bbd1614d9847794a4057465e56c8830c535bf067a606fa0412c409d1845a2eb72658d4ffac5109fe951b783cfb69d36351a663a21503524c2042b997b513d73f71752c902cc48180da75cd1b5525dfd8688453106afe69705677a0c968fad776c2303adb0ebec0832a57575170374f1c61e3e9800c7b1279ff4ff27ccc0e0709c00ba5d148d3c682545a9bf29f14e84e2b93607029cd837ad63a3a15ea378bcf367a20bf35da19420045417bd3b5b2432be12c425f457f740a4af71e467b614e8af9abeaba50af8401f5049b416f9ef0e8512e151c21f5e46847a207cebf30e53f0b895b0297cd3e8517160fd743d1f06fcbe504cd61b115f34cad4a818e7af0e92e033ca0ca34c616b352a819aec6932c804b02b5806f52c6ea367eb33941e8084b2124d70b266d298048caf8d1e50683694884338ed84098df7e2e02c5051e016c8c100c1ba26ca6a4943cac3d986c7d2d49f7b58a6ad1a6d198e7e8ca2937e8e76e8b384a06e4fcfaf01deb64385f510a4e621ba7d6aab5b06742100fbefafb5f059565299040d70efaa3b6aaad302006f2e4958bcb60e948a908c74add9be06b2574bc746f5de7032d5d101430564b9196610dc5bcf2f24bbc5d3212cd5fd45387532e55dab0b09bcd9ea0e6411747f165970fe8c5619a23f3bf9830b62eec42601e7bc4a38b382c16ce41e274de0db4d8c25e8f7d82ac186892856703f31761e37c4454a5d975fb5265490b3873a55a04e1a989af930f777f9fe094959099eef863cc033ece87b5db587414dddce7b0ac2013a71079a3b6a2edc652a0cf767c1bcc97192d45f69c58c6518772161706f8a04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"362f925e5c1f03a4b91b36cfd9998de4f4028efd749c9ed0804ce764c8864855","proof":"0e0e91c7ac0e509a48d04d40fcd2ca58d3c3b97b579a832eb64045a61123025312b44c4e82d2b89a5422e288df643d2e9482e89a46f4417feb45dcfbc726a00414fb21f447c7b5ff094b573e420b9372b6b7b38c870e1bc235616fde05b4af0f80bbb92f97a54332045db4c831d3a1de938debeabbda4c987e975e1371c17650c41b2b502204a91aa47acfd97dde655fd3fda629982889bdaea70ea2c42fe903be87ebe04a95cef68b1751c2877c5b3a8f9556e56632676002bff0c9ab856d0416020d156073155914708801056783ed13da0182cf40b96045aeb20a67b51f01c072b2a559cca01db240cbdb3e14de13f689ed3a87216514682a4eb180551828be91d0a4cbe5634a131ec4a235936c9980d9b10a49c60b3984d000fee8be0a50da955d4f7af5d4a44a9662e77d60010fe6f1582aff98566f8398c99227d7856af65805d3e7b747d4deb7751b5f52f816c9f6d03423aa5a03400aee2d965dfb4376693dc7161b768859ed2ec7af6020d11b8621f85b8a62d81b9420ece7d6082f2add95c0f34fca1d2d292f1616534ddd76b36a41c792752d2245481c71b5971276ea3451ecb4c1fed307b3d144abc3fb42716b7a683358874524f60403b271020efc68af0ba22df78b6016f8982d25fde6ca20406338c6a12e020edf343b2f639eee287aa444c96cd399031529ac3e039b0d7fbaa1d4f7fd7831ab7ad9e8bd2208e86fc2c8f2b90580d3aa83f67606060dc1d968aa340ee7cbedae0dbc66a404e87ca1e529d3d37c8f78e8ce71e8557775b308f7591ee9ea63dbdf29ae929307beee73a0436df5f6b93dc0723b464820ca6ba188b5b8a4d16f4f85e91a7e0302acc3357df2edd3ab586f412b3e7ca92fb67a190308bf2879cd2c51bfe00648045a51866829eb1d122056891f3657794ee5d9a5a2efd696917921843f3e45a80e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"16e8c7733da4509523a24f221465df4ce84718c5592190fb0e730dc8abcc2d43","proof":"a6481b97e2a07a2733949563518f1501a18622c30ab6a47e14c5941a24698e4e8c5e7b33332d18d91437fd6bc2bab95140bb65d03392e21a5ab41afbbe37ca004c94a4748691e92d3e18fa977fb202b394893c65ee956b6e27462d697021ab578c07713840e85b9abbb12185c6c306d39b3990cdd472c883dfa3f875be893312a17788a04592fc9fa2c3ae85671e2302c90774824d9ae1c090c49b8c47eab3033a820e5a3072dbd165eb6319c24f8f112782e6573ad3ec8d544cd05c38a36006fdab2507ada43c418227f7416d4719d867dd890a285bf7801c4ab88c129db50f76f7ad6052ab61d69089c3940188c2a8fe3694d4dac76b0ceaee441999b8b9796c7cbac11e1d6184cd5eddd8be76488b5e84a7d80097a6c6b6b4a54f6f50fe1912f746d0e4f11c2af47954f704168c7bdd401c8fcd05aa847dedb4ad19bcb160b2582e9b51f7b712bd2844b7d44ab038597f96bee07de34ece9503dbe79cf809124a7847d49e055a82e2f8017a31a4abcb77e13f87a343ed177683863bd1e173766fb3885f4fa4ea2a082d7f309e35f171adff518d2309ae8ba5d4b7a1fffa0df892c5299c690bb86440e05f31211ce165f2fa75245ceabe55cc16980d404b03f8ed64af3c50d732e9285b3b4794bb14b2551fe1f6152d0d151f71fe51e6b9673e5503dc8f482dd7cb07d24f4766c47ca067a2021b66571e0d34a9f638a431549415145bad4afc9bb18feb19bdf36f057ac56b0a952262a5c333fa6dc34cfd619e616cd13d95c751b4fed44f77a329a1e44ee7621208dacf998f0e15e569a62d3ea064a43a5bd849ab6580fbed09936707b84564d31b60fb73b934784cff195e38db7d1ace1953fd7c7719d3e8963fed0a7f67f78240da55e44f3366d3ea1b003ab6847120597db155cd0cb254b890dc58c1150745670529ad27f61816ec380d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c62d9e555803af06ada9ecea2b355482d47b6401dd323d32140e6c07e8a46c5b","proof":"9c4a74be2be52bc016ceba427357bce71876a3ea7bf60c0152006f69b297dd3eda596c44a7a418b7fcb9e75fa8e80bb3edd9b4cf91be4e6ace2f41a6cb6a6639386527d21d9eed25d9c7b78aa2fc17736e13adcf9a3226a7a04caf270762c459303d69461054ce3cec8686787125e020400ea29086f9329c63ea09c803a67c4ab1863fa7cbc18cb90a576d334e24494855c9ff760cdc706f76bbea7bc3993d01e5ee293b7980325c79f65664d28916d66c2981808d5390686aad93ede197d301d34bbd47451682d2433870027c3ab146351be0d8077bdd9f34e5add89e324601ecd814f4d881566f98ee2af8d8ad430e6e7985529b44eb31251e433a9972a96c2e9a64a03ae08d224e9cd5ad2087601693b582a54d0abfb498a1e458140e0a610a66a7e09fea9dad2471347d9f881c0757b0e3c688891ae522aa5c4f5cec981050b9e99a8499a753bc9f0a2e7f9f01d9802a0df5ba859d6222037306451a6430765d88bbf8ba10cc5c804a611a554aa24192a77624b55da00962316600d3e057e06c23807df5e600b39cab907102c540da62a543187796e0ba26e7d83b53bb1cc0d750e733330ee410915a4a6bce6d502737ce5e35d2e8df14a29024feaed8544082d67ff47bd5c4e48411674afcff1d8c9a7800c3a1b78c350ed17349ba6057d0250d9e275064ece0bf49d95eb4b7bcce4582d061c7fabe4fc437c7e1cf934c5e3a8f2739698c36fa419e9f5d9212736453e9ed5472807238547f51add3662d40e534fbdd053fc80b90900737a85ab2c6f11f435bf70c69d5b569d56d2d1c3f1e946111705bf2a5048059068bce73e5ac023e0b9fb8128863174bbc68477901c5b9794f23c0c99270b6f235eb4a9e7caa4b6c4aa4d7fd0a4d18160166c83b0e02fc6074fb45cd51689485d8507ebe8749407aa262c86f5a6795d976a859bb05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c4dd771c6d59c4f9bc77709a9f23330b487a0f9620816bb4f39a3505ab3e9571","proof":"e28ca6fbc68b30c14044f49497d4055cf518d1f2e6b91f2bd013f37fae28c6628c95bbbfa58f73636ae65df8f443ad74840c7a5045f31f566c7cbd39607f81192e33219d1203b866daea5bcbcb15e3f41f9ef046a14748a6d415f019fb6f036a1ecd22a41c14a59de2e4156580ac15914b380340dce6228b51c40045f293625f7d6ae95797ee85a21cc8d1986a5ff32322bcde827d695a42eace0c620747f50f84bfc13dd4d526177838bb863e4bd910982496fca8719988a32203323513a30abbb11100123b70d9475f0a71976b96b8f4bc081f710812c4e721fe3c15e939086c098339e6dca80dc93e9e3e6e06678afa2a77c462083580add1000fe20c605d683d0388b5680940632d1fb8ace3e7d39ec33fab864288610db92999846330728c3231b7cf159df355b9a4b7aacf18cbf59fb846f6b2a83109a1d1ef349a39448a93ddecccc63e58423bd98d4814631dc6c164d1a0b115693f90a6e557bfd4351e3b21a4eb74540042f6b9a49ec09b3791eb23071565546b1e3a423765b96d7128a227e659807a508996a7ad162d3124cf4b8588632e0a98757181c4b7ebc65b20d57f6402d3f0c6bea166b521cfe49f1472c6d6f838e8adbf2f7da4575ab32850f1356f3b7249f2dfa86b1833b6fc21c1afb3f88b0040ef7fc6e0abacb48410ec1df379b7c9580f101008aeaeaa28ed52cb92a3d2b1ec9be5173cf79293e00262ec84e1c878a7df5a5ccbe641851918b5995ac2c6607fb813952302fac2c34d4047652c471e73d17e27c3e50b32a281ecad8ccdbb2d7373c3b9e4eed55d5568ea3ab4bfd74b7d26aee86621dae63640a3a542ec4bde996da69e17b5e023ab02482bab6cb85620d7c214d0285cda861ac7de162c14c9e52daef6b3688e44e60c3c247ece5da2d23bfe3351133ca2e8cd04b63575838a77e327af3fa0afb8ac0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9aaef724b525de726918d399b82277a4a4da03682b17f4c20b35ce41dc439a1d","proof":"143a8cacaa696d5dfa5b938bea73ba700837e3637a982dd9f20d7a6ac6755847a0cb1cfea2c438324a161e9de10e3271d1aff1c8d068fb05552809a20b082204c0d12662bb47a1d996255eada0c06b6bedae94d470285b0408df018fbd2efb1b06a5a6e651b7bdf04bead20943fb9d6bb14fc71f642fd0642f50f732781e7e40623a2bd41ee95812760ded20988119e8940dad137b71294e74cd1bc57947130e188621bd618fec54886a423e38486839706c89198504ca24f4ebb6491425bd002a8f4b777bd1df80d08f88291d94f813d4f0766da133d956b9b771450283ac049c8d2de04c4423c400fd9134254eea4b955007251996f8659dfbc31b4196865962f0ee84c645097d01ba40e5cca5ce04b8cb6f686d4c1dd2d5f2fa0caaca674fd0d44f94534824bef8c7cc51629aa7b804ed9158a99ab1f30a9573217878a35beeb50599172157b22e639742fd5eac64a427db759ab93c028cf749bee22cb17da82ac95dafc448a2832d03f752d280d8a67de20d9d302296ffc38db017bf5645ae96f83913cd4b32086909f50ed851ea546dd23837d8a7ea9a9930a50d03db6f42f301ae12effa08e278160881ebd940d22e6f60ded8e57ad2a43c4a6399254fb481c9aad444942b58e6f4b638ce0f4246b07a8d363879157b2bbfd3f78917183857fe442a58188e56a3a0770091961b9610440e8eb9a0db9f780c5e796c2255c6d723e99ac7c9612fb4f554528b86c6362429f6f78c9786d4686b84b5d30d772e0227bcf5949eecc74f34d893eb08a2d5354596126199dc0e304b0382346919820ca79d2ce7a823081023cd56cc98c477a7aa710985b68bd73d2e34cfe01c5de0852429dd248110e91b476c87a94481cb89b2f2f4e9dd8d857cb7437612750be716e40273e7df0ab3ee4eb79f972e0080931369bcf1ec217b8ab422dafdf406"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"24683a6591eb8146a278c308c38117b6b6040d94a9fcde6ab97c24cc35bfba46","proof":"b42157a0e37868f88bc29c31934ff444aed4dc806058f2769048aead323ca25dd01ccf03c4436186b9282315371587ba3a5ca21c793037d59ab40b14bad4f15f5217acb3ecf429381d1029e0bae1606215c1c5e7b92f9273775823c08414ea5cc253a5a54e36af66570c08ae8370431102bd6c85f7df0b5dd26828cb3e2ee40cf3e426fb0715ad16a035b4d5d6aabb148e452a655787f013240b9ec092f9130eef6ea0f222ef0d0cb2cb57592e27c1543d214d6980a9963cf29fc380c5fddd0089a04aa0706aec402fe90d85c7fe5d6508992c03e264f37861b0a97119d9f001827614436b601b6278c6663977aa244c8e1c073b3d627745e6c222cb3f2d996ca807ef929153ac20f66ba6850ab37bfbceb259aa7a3525d791cacb8d3580907a78a24195ca3e8d6f281fb1d199376bb2054be33ce2d759df499f0aebc7d2fa29e40dbec2d5f1948f49004c4cc065399c8b96edbec09eafb7fbd9b1cc5f421b712cf92c524f45dc63fa5b903688f01f451223da62d023c566fd925ed6f5f6080d9aaef9f3660e0cf2fc5222f66c96aeddb6f217569c5062389099431d28241e716066432f05a50d3a1810dc94e55c961fa6e6d2f682bdafc2fac5f61255a7db77407c1dce90802526a349a9fd2350ffc758b5181de573e64f63607d313d2c0020768fba49d0bc09bb6577dd00eb520c81479575440fa3cbee115119441aab17604ca87be5272beafb56122a032e0c9b698cd350a6dcf1491edeaa1ca6fbdec13f501f128f2421eb73665628b525e9cbc51477bab5e306f4655363166228d4f23cd06545f7eb40a974bbfcb4652d0d651f7d26002cc96ba3e27f97d4de74f0d005883400ebbd7861f2bd12f732ba14076aee13b93b018585bbb936b1970e12950fb31db610cc31168ccc149395768bfa31ec8dad376d2e39ce5a0265aac07df10e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"56307b53be9a65889f0d7c3bf8dc837460a44a3f76d06da824105390d122645c","proof":"083afa4b3e9c664fe6d2e31aafdcebfff5d7aa3c60891899eb1a1feb864805563aa6f37d64f30aebd5c2abee0e5eac725213e1cec091c08ddfa95e37b3ff070046084d92553cddefad55bb088db8f17770ffb38f7003dcc1dcd18d3381e0d156ba58707e3bfbd51d2d0895f300bfa14e834d96d10470536750084eb9f0663d2be8d60afeb1da4640b5e7caf2954bb52b724a45277b80be4e39a2b4e977f1df0b1cd6bdd5e2fcd1f84de4a8ad64096e52d7214586f4852e828511ebdeb9f86d04f27552ae874ff6284da503f39b54725e57644d8a11cf983c192f097fcbd5cc0cd29445cf76cb1c8cf9baed31b4e98dda0a5dcc40d4aff6c01f8aabc5f9196b52dcb1da974a5ba867ea5ef2f6d002bc85fdf779c4280dd1425d54731b6b66aa322a7c41ffc8aa6b203e906ed30caf7ded4437f2606e29ce339f0a9bdb66df9329a8e20fbf6cdff847a94977273b64d12b145f51c5ba74c2367e6fc3713202e04f302fd92b6a580e398787e2d0bd5ae39a2fcfa2981e2073824a7761dc2951e517f67a6aa7e20181307daa145fa38738478b1ddf846d8a17fddb1f09ab1745686a34dc6f83689e31b348ae2097ad0e770453915c003abca82545dd14e939d93a1634dca6db74bd9bbe7825c29f50662884e4ed3c453bb8a46bbe8016d9c5b78f65009bc6eab83827ba2cf129b06fdc7295e384848b183a359a4b77ff7869c8704feec33375f76a59796cc496778ce39a4844374e87ad7cb94d152c0e4b5c2cc559bcf2885a4c186225c982b2d310ee7b3eb497cccc198459cea28f5a2dc4f85174dc6c44c406f44554fd14c1459d74619b2a7f218d067750f1e84afd0850c9c44c01997429ea4756a75c1d53d1102e096b6b096632e01fdbb4b61b35f0d35cf5084e99a6826e58974171ee5420741493edad0c0f4a5bd79621958f38597f99cf04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"720c55343fc05f82b725cd8f2246779365aeecd018121ced847ec7a0d388b042","proof":"904f4671b6f0cf8720b14cbb849957b7c6055e9432c6f06695df9107d380352f3c424b76695c456e6b9f23b36c651bc07bf1bfc66e49b185529feee631dcae68de8a933e042b5d2bac350784c0a1240571eebb6db232828ea9d584e394a152234613e703e5394d770d6cc7846dab43f219cf88bf5748bdaeb2a29f3464d3851ebd26a0dd4b86b5e8bc3724179c31da2db1ac428101fca798b588dace346c100d8936e05c5a03997049d123e81d96673955fa9ad702ae44bad4b358d24f71370c3a88b5cecb6d91ff0dee232a696c5dc19689952017d711975465dcd8f08f40068e664598f2ad0b9c09cb817d1527f424e3d237d566402c64848d4743e2dcac1720a8a59d3570fbe03ccb84f5b69f1625faf0e0575e5a9f68a001c10b6e2c6810a0877e51da2ec384f7f23b92923b08b4eabda1951e04e568bb3f6b43243d8727c0e4e7992317bdafd9b6bcac12d4c7962481ac16dfb9c67409325bf0d08ba57ee89dcc97a186c14503d2b56ed20229c6bac8115a28e5138bc567faf6cc1edf5c006154a32ca2fbee9377b720af8c3148a6fa83bfd77411686ed4aff575906a4352a3bc088b12d0f6ad74fe4f137a80797a6f3893a42d0354d6ad670d9faf3d000ca0028d7e3e1d8abc6886abafec3a81a121a0dec372cbc25e3884435434d547c04b75127031fa95370593e091806e93e07b3ca59c8589aedf36e3e68682cf56b4c059891dbafd17156898bd47787862c3cc548a27e632d8e0b0f7016c1d5b7fecd3bdeb0fea2ca03770737080199ac829be2681d6d6498d4a21ce913a7918626a0ba881871210c4ab73d34469be6424ab36989198cbe8e2dfde07559783023d06d6fad9a2c87bff6cf2e5c4658d3e9b88cf696229b4ca718a60c9d00049db05c44e937b59a0b66830e5ebcb770f740f1ccd66601d4eb77596816d06d7c0c70a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"569c3710aa5b03bba1ec49cb971bc2f8a6a33e54dd5c7f295c9cb1a1a93b8819","proof":"3ea91ff41260bf265ef5426ca691bcf6882be6d91893dd103f69b32ee84aa657ca2564c091f66d39d775110dfb1468181a4f6f36d7ffc75fb209c478fddd674c20d9b913effc5403014973a0eb584d6d9eb4a5877e9e31eb1a52f4882435310dbafe983771d26bc420996868d414c3d6afcf792118885008891c92302f026829d45d5ebc4806764b8498cd5c046f84473a16ec10bd44cd5daa0dc8e85d6c450018acd072f4dfc73d2cf60680442f73b384d69d09f635d94e9db9fc831f4454038c5e7eb69890e8fe13310a469bc6f378b7f6bff532d1c5c3432ca647e498510b589c44e38f6eb976ad6be4b9ed7c691a6bc56383c641b0f6bb8c56084c1a983cca7bbc4c2589b18b427147f822a5ceb36d627f35fdc592a32383cb306ec395766e2182e52d0354ac78a6a0e47013b8c37047817d567e976c32b7003f93662141be4d38c5163246585e308bceaef0ff56dfb886525aad59e0d96b1203acd10e055051b79c4af5f85a9148cbaad016119189d6ff88714cc8bf196ec0a61403c349e2505ecd2154128c1c0ebfcd3d5ae4010857708878f3b665d1bc459f35f09736b4ea63b0ef6b81d765b2d966922c1db2e41c1c532eb0f24bcaa5da016767f83ec2f277535adc58125afaf2cbd1bd0b73f5422dbef3db7f737bf6f5cbdc146551d6fe745c9c3e9a49d30c808bf83eddfb4270ebdbc74743158b3ad1cb646f2210a2cd656fc35a41f1f702b8c9bb65e65e58ad0f7a66a3673e6cda065ada4ffa787060870d56bbdade8ff51b34a8394b03a63bde403787a2e924364ba3486a590d0c2a2bbff2690f0e7d50931b24fe26fddd4e8e6e0664060dc559292c5dc84f7d6d5dca4d099af22fad6fd55336eafe7a057b87ec4f107aefc3a7f07e5328280b8a3dcc16267ad8718222379c2b761a6fa0ac588058108d2f4999b0c43b52bf01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6229b69f0d607f5fd3c261b30d0ecf62747cf86ebc67913e93d9640db97e1b50","proof":"161556be486e6771f639cb390c303826801f2f529314eb682edb93a336f0e50b58e9f351dbad44b40c360618c0c1fe2a6a809a6f69bd9a2330b26858f6de2668c005ebe48d7c1dbf6414066ef5e9ae55997106d92691510dedbabf08fafaf6426cf3d45714825990e5d409bf4cf17e59b093d3e8ced5f5cb9cda8ef65687a1395f6ff3ab28052a3c5add628c0b725436ec191ba9d50b653730f0eaf813f4b20440c33267e3548a2cf98231ff6d8177510aee8109b5ef8ea8547c3906eb9e9c06c1cdfd121f2332a880e90cf13587c6ea297d58c16773e2d725e65392dda297089a9330f5175b6f6c22a36cd1f5e7f079fcd18de10c2780b081b9e250e9012c0a80685e87d4dd8483611778cb43c41a9c4d2ff5595fb390d6329e7c11c1269a7330e4109cd82c0be10b062735547dd0128e6a24f002be576c4d182394c4332708402fd88a9a84e1bf93bf9d677d306076cae00297d086b7c4d05430eca770c3680ab8b93c3d7813d6b48062957c280c3c594951d84674d495c077b35d60ab07314c0ca977ef75ca8aa39df682726eecd41e22ccc1703fab03d49e125806447f662e618a6f6c7c9310420b39c4317df867b2f7001d8d0a881badc570c8552ecb7b281982b1ee6f71f8a948a48305f99b93d9867895a9eb843fc8a274a3851bc5543c59d2c9190bee326eb3b2548bea51f57b027b9bf5c13c74d5f7eb04ee2ef41b0ebf6190857321beaa6450158622a739ca8f3bfbde20588cdd2fba0358eeed2864bb332023d79cb79e04ad4bba6d00075904808ebae5c8d2b677a5eb40eeed3c78fef928bb1a80b99ee2483011d6617ef2dc05a2ac9f603ea85cb6959312be737a18bbd9b5ab0be549a7ce03ef59cb35a6f047bc4372ff7df92db2d753947b03c4784506bdd324d5c7758df6e010669d54b1cbe85990fc699a8eda977abac50b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"360565878491105022282ab10991b7200142e41f0e946a2cb2ecce89c73edd36","proof":"ce31d2ddee3cf7d441918157e4f841db45e9e89a5751436cbc4b0b3cf9b4e70f1612a7c25d74a0f28ce93f28531ed196d0705ca894279d8d2cb9fbc69231a97340c5c05a45eab0d3e7cc2ec56fb50448eed1250d537ae1565e68e817a65f463422fbac5663359c4f1e3ab5e6bc986dcc36e7627b83bb6194eaeb4456e61df36ac2a33f9878d60292f52765d3311d3b8afd98c21e6a88a1c6525d3479c852b30e2f558ae24836a56107d0f7aadf3ab6b62b8b2a098ed6b18d560e32a619f2210f0c0d4222fcb5a23eed26807df0936d9adc84b849dd51bbed49669e446c0864011eb3769f9664a16450406021c6d35bfb3f02e0c35ad7a99433fc09a6c96bfc1c54a7853447cddd0aed95d028cf331f972d4c5f64647583d82a2dab874d868f43a8ee3a933c8f59b133ef0ae22083c1ab6d5ee9888782ed0ff50135061e213e60ead4102b4f73b44bd32ba3a0975e1078f22e1b12ad426783ef35aef6c7ee47494465a7579fbeb20ae1bfe362f16f96612e816fcb4f7171288e6db209881b573c8a093e7713122eed59c527bee210b36baa516b23da286d02e994c8f0b84ec30bc460647d4c7230b2310f5451a13109d2e5abc4814e78abf68ae48c87268e692b5251476fd1ff705a103e6a0e91787cb91b8be7710a3e890645c96d507f70ac366adac8c1c32cb96e57610aebd1fcfc59901443370fd778a611178dbcdc7de219be1518d01f6b851c456128cef9b9feccded5dbf0898d71663e42f76facd3b8345610ad64afb98095dae6f283190467f6ea3e2a0742a329bf26603c6b1dba9e51d8c92d7d5277108bd85c6e5da467b9d31cfffdf0ac8bc07ae414edfb44f49b7a48582cc9891626624ddc4fcd2eda4e1785298c44d5571d6ed818f20c63c02a00b98d1befc8d27cdb134fba6a266b190eba4d8f55fb21697345b48ff5af5bd804"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"22073754993c17caad7376169c164030146d621ff50bb0c6861e82380a304d36","proof":"30dbd3238fc92310f716c2a99ebae4699aa807d72a1dc141c055e1f937013a60e26289452f03fb3d05e878a2f6ca154cf7ae263aa2bd7f940a127d591b6b1e463e212fa5c3b81f0ccf1e752eb06056b35ff064a3e78e0159802aac76c3d180221454997f30ff2edd4a198c535f837c0c51e77a7fd38f5a716f21dd0dbc25cd3859946be59b12d606d0817131087530faecd60b45d1b93d0c126cc6497dbc05094d0efe2e5bb46bd34525936d11e71221d2a04c77e2fe6ed4320d5a26cb9ec007f603c1dcc261887103de7e6c794f5b2ae72dbe58ed1abcd74c70d6e439cb4d07fc0ffe606176fc3c28e8d42f8643de893a65885799168d35713385ed9170e80ad4b570eb7ebd0a435930a7ecc14d15b70e02948c305b136f1cdf98b26f7a59259cd5a783d65d8c3e3545daf09f3b8a13573c724f1cbca93a068aaa3df3d3eb1dc6c6e9982b28acddc2427733b9c37952af35b9c91dff51cab83383294e020d5fb6e186e37185911cbc5e18a963b9bb78c46d3273a0eab8cc04348f29d6a8a52828f6fd74474222c3e52fa556328b4ab002285731dca4b0fbd789565c6d69d74e427ae7e540691bb758e194dcb02a238ee4895ecd93dc0f779275ff7f756e45050069a3af949794d1493568cc965fa1da3449c0d2b237e34f2a8aad6eb0999119a681e97fc21156c77ba427273cf8ad0e5f3f7a74064ae64ae527ea92cefa6f05a86cf26724553954d5710098b078dc7d98124d1c4f8a3d7bd7a7208d41c8e215667564523253acf3dd89886c6f23adb85f1d2d45d19964d44ff034bc62bc6129e2d642f3c6ad097c4dfc75e2bd01c958fbe7892fa017ffe07cde05baa26cd1768fc918a17775d3830edfb3ebbdb47abb312cfbff95fde64a20f191f35bee8b07345569160bc9383a00fedcca8ee9732660a45f65e8cf9dddda8158af5b4f490c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1afb23583c36fd3d3e26acc7b6f0c346b97ccb1779087dc907415bcf90841332","proof":"084e2a9098af608a6386daaef9c307a6cd97457fc3634fb09005aabeb88aa935a278b3f4ec3f3cf52ba8133919f951f441642917eb329ece1c1f66fa8d01c22b7878a136f02f59e644db552ac5623e89975a67b444d0cabac87a845ae291dc3a748774ec4273c113e2bff267b46323e7a7328da9e42d905dcabecc061f8b434c522b517e5f03799323a3afaa639e6ec1d0f0ac059266cfbf1d90440fc18e4902d4efe75378ffd31e14e17fcbf697b59e8a67e012a7667c89e66b024b302f0803512839a7ab406980aa5104b18536cf4a3913a1ce1c3b9136bfaa89873eae5f0c5a9687dce257f9de71a963ded4e07e05d8901c742c2125dc4a5396682ad32d2606fbb9a500c21ef475f6dc4256e4d994cd4a33badc1d25dc15b12f8e4916922e122ab856ec3ffc071e7923ac5c58f528f2a42966060231af6929222bb746ae708293554e90398f02ca11ae3f7769d06cbd20d64c1a1e71a8e1cbd77219e3ae00aea8e5f2e0cc5ebcc8c407588bb39a5314e81be9da0a2d8215fe372ab8341c2a04fb27fa96712439350d5d8cd88dfaaee386f493acb8df7d9e6e6f4aba150044346bd0956f9ebd3a845427f4b7cd8f81d7b27aa0ddb8fedecd01070673a36063e8845f191d14194e540c13ba824a702775b7e10752d83aa35ae3a1602c192f0926d3e31892e4b1bd4cd7145b2a05b35fc75212c446f41d2a9501e231163c7653861e9c7fce217ced6f31fb1761516d7f8ab85e914cd06571be8349681c9b02165a288af3c4336b9a12b06a48ae9b26298ec8b51622c54db3712a9f522490df000c98eafc7c69f99298144afbd7b60ee161ff6e72fb3b077a1be21e7c6160e0241a8af89f97aebf52ec9f343b3458906aef2f016867db0e7dee3389c68901e7099cf7745b1f398858bb664218d4cc89f9cc5961580f966dcad55d9953df4c780c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"56902b0e65155cee9c8d5c3cdffdba5b6810b42fbdb9a6f3a2d56be990aca211","proof":"ac18aac345dd4ccd36dcb9c1a19cce1fc51b78dc7a160b403f41139cde95332b9869d497e11d47fec77c469da7d379b5d111c88b7e2ec450710752df2dd496413a6ba0b65e7378e36b7f15fda01e8ba59a34444a1058e95b80560812a6b49e55dad0bb252717bd9f92c87a7e6fd7477670998f6bbe205aa67db232af5ffd321ffee701a2afc5d79817b3595c4a631b68a13de61aa2106e16021903af9d8ebc072970b083959c831d7730c9cd87c94ee6f346595c30c5a69db8ed0f5eca38f100eb6fb5727b2ea8c7f15edc3d0b5ccc4f8a9ab8f6e16eaf9ceae863f89b06eb0aa67bdd1b2851a3c8caac23d13a8e3896ebd9ba3b3b551267cf5617f927021c6f2e492796891903c10b1793076cf5b2b00ef5c786513ea405bc47aa5ef7f75a1ac49272253b4e63789b2d758bc0f827abf4e2ba3b6130d6209b19243f625a2e3344f7c6e88dccc0a6df8248d62f141cd2f233e5ced8b2133ab09f3e8719126b2516381fb4b310a18d205f7575ecd222982236e2def84f090bba648bccb72cfd0372f70e9f587c94dd67501bc579cc2f3a59800244cd08ce045254b77de6c4951914b6856878536bb88109a0863630481e6b9e07f8e53e420fa9e959ab0a940d19fe4570cb852b6c8d6a4c12d2a7c3c41c16ed46c2215350ec2b2062f1f033b278d0ea38d7821663d8b9c7565df6c6705c64bdf31d9bd2a65f2dce7ea6e748312c903a9420e5e7f3c741bdc7068fb7ca6dc279df2fe57128b65523d0fbce989e0166e2a3038d132ac9b6005ffc419af48de2084098cdeb5a15fafdfdd7585b7332c84f5d1e69bb3fb2cba16d1886383077b15ad2ace4edd7d6d1fc0989f6f26f157d59c001a449d03c5f2c8110b4bd01bdb215143bb0d2ebcc23c4e04021b0350ab30ee2a25fdf9261e7b2f8808b0274c7bbf2f05bf33194b8f1bc438fd81fa001"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f84aebec70b5e9ff31efec9b09980d361e776926504407cde9a461fa6339a86a","proof":"a2db4204b63041d42b5ddf0d4f4c02279f7d802114a6dd5355c3bb12633d413d38c1f2960ce422ed9d3b27fb13ab22f37055dc63ceb13648e76f60e59597b01fdaf9cd73040798c2275a47b8448bfb7d2fef983e1d45afd37fc03ad3bfa6b460902217eb3085e8c06819089924ad61df1c4899a7989dba2679e0ba5d8dd6cb19f2367b09e2da9aa6cc32108ec32409d5818036a98d3f1288a0421f66e914030372d1afc93b30b3995df1973b4428f451333e9fee55f2581972a632ebe9ac6b01c3397409ea8fe2b256b289a7bb19d378a75d56533d6f4274ab624b57d710a509fe3529c5a5f6bfbc33f2f855d8551b398fa7e98e1d02f4627f9b428aa75c710296f66a6dad2e1bdf018aba668b3fb54d762f0cbb962c6463e8602a937da396754aac4bc690bb9efb44fc747da10602b761eb9219e0187cc8df1ba7f0e739bf5860014a8953c500be778bfa0cf5404e429ae2255eea0b6908ae9b910ffbeaf462ac90c32e163360d4b9b884c0dd553ed8fa43a9ae48ff746afc8b2c68399f503b68338695d5ada90ee952b8b168a5abf7005ca51bd838c7c711925f506546fe58fe771df52b25bd92869f8a27dd4ca176a79db5a1d26c819b0f14c913fa9b535e300ea09605a11abbb0075dd11b8945861099e751b7a0fc24f82fec8bdf7ec133f6d91d214dd0a3cfc9d8de4987120abe0cce4ae2ff7532eaa495445bf6e20e057ab6213ce0f4009931080913d9f6e79b1db751722d17608bf643d24e44e5f840aede55b68bfda08ad3ff62c35df030fb2146ce846076e3586e7ad3177813d660a64ea98c04c80090c88a221ac7475228a87c206ca02b947561b99fbfc40787696f21de85b30c1aa713e5d080d55e641e3d9161ee326ed17ceec7af7230aff60098a359bd7b30c2c9c47ba6ce4c7121c45c3ac36a028f0134dca99c678207b70d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bc6093a6bde52655124c2aca2beb3c68b4b919d5189af881b0088e46b6c01e69","proof":"08eaceba93279aa78776ef90f72dfea7c9f02552230ed7e0acb0132205e6550c4a12a8833b824030c0f016229e95fbb59005aaf1df7790a84e5606c79b3895693cd15549c0f418e45f16ecb5106b767d0e2543da130e75e5c5bf14e7005c096db83a1cf6f1f56356bd28e3f4691dc56157ce9f0682c989285749ce1026c2b7101ad1d0836f0b796e11347239d210d5b2631db89f53d3805462ea3f9482aa0a0cb466c38a85319e9015bf06953daf2a65b9c0323fefab1b01142a6e83f15917056bdb7a461453378ac32aba0bfaa0417ba3cea63f5012e26de7149ca5be273d033c90ea380fbf3a4b35ccb658b6968e732d90926aa00092dc0e8a2d7160fd1b1f5a022b38a7fc18ab90f65648820a1464f68e23abef381244281426babf58e6403ec5faaf197d8ab6fb2f1663d3ab799f9341342401eb379e6ea351c47f0fb213202a9e35aba8a7e0589fc95791a148e4b4452c9c5b1fc5ee46d86cf0d4c40e41029650dfef15c83c98c1f8d24e2f26b12a6adc2f0913002cc2f33f3969406a265c1dbb12aeddb5947f4061c64e4ecfd36ccf9071c803f4e48ab5347ac670a316d04337906dbefee795a3b185a30e5980677c0f7f53a3cfe5cfa63cda5b0d5f09360ef084b29a3a5ec5e5b412cfc34756f394a2484940b9e61d5346fb37c6975f564906ece61693c9703782872003a8d6f47840961c9ab51c76071a9d4f5c725d5ad543b95c65a150bee8621a9a29503793635b6277118664b9a203ed1524d3589c0df1ff017cfc6c2723d0da2d43eaf13dd3506d4978a89c85eee78fd303f164fe0cb53268ff4a7cfbb4176dbbfe7beb93d414d3527d51b8a283371a4ce7204d8ee0d0cd29723f32386d22892718bfd2a115a2c0393ab4791876bd9f78214a0d60e29591810ac2eca2d1905ba90fc57edd123401f3987fd744e579fd68c6fb09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bea505918cecb4e5b69e8f7d716c218b9c30fc64cab088ddeb36528fcbc26601","proof":"1cbe126ab0d5a88211dd6fd4aaa1420e2232cfd2e8e05604e21c7c3dc8c797414a2cd691250cd114a091bee2ea6ebc445d9a68e03975427396b45bc3d0d1cd33746498ba89640df7d2b682f20670af155022dfef88926599a9e4521581eff019e6d4751bb3ed53f6a492e506651f0959416e2a0a44201626d9dba4ce411d966da99296affbe348926963cc2965e69d5b35df6d5b9fca8e625388d180a12b000edf43dcfbd1b1d19de2db205156dc59df23f7f5eb5f9032d1ff5e54b80dbb820af2e0a939b5a931a2fb6016d864ccaaeb858b7fe1284f233b36098332c9e38c020e2ecbfaea82a8c3cc68b7c86054a15dfa0c38e7c39fa29dd399a45811bdc76382d9551da014098687d3baf6bc41eaeff74993bb2063a347c6d524bd447120230eb439fe79a479787121eb1b48b7024a1184a1a1cb962ac8f8a7174f0f7cd746c433251b8cff0219be8df69e64b82d915a2c4eca33a8a0a5dbe709b297388d69aeb5f88386b720e88456f3bbf46e954a9ff1d38107d6d9a5695d760d1f243e294af88574673dd061269859b233299e282ed3022577d618dc03ee821028f4a2155ca6bbd5bab3276f7f37c88ced616a2400dce4187b04918c8ecd3dc8cec1e26feca0fa24d800b96c3208d5314c5cbc290273a94261247c2ccd1e97e7cb3e667e58c860ea05e2336b3ddb79cefdf6af563eeeb46e6423238d2697d22a7ea1837b9aa75f63294322b3c386d92c87101eeadb1be269ab6f099e103975620309e16c721f60757fbf92468de8e76ef37f3b68a26f5977272838d6548c0aaa8ba32b01e2782a13c8ffb6e68a405c4bf97439940db6ff92b301ad2a1b5c010d3cd8624b83a923d4ed9d18e20e04f4409bc94cf4d4794e5a0ccd30185784fdb711b6e30aaf4cf9fb68e1c893d7b7ee3c04ed5e053114d0b0f5aa6d8d80163b080a43e409"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"60faa20b0b7b48fb4234f74f644c1d2f22d9dd97230cc3f39e1c6e3885ec2b7b","proof":"b488e93fcc86e361ffaecb8d46def110f7222834a40af87d30bcf2cc7da8937490438c42670bc36c3ae183268ba8b5fabbb82d00a707cf12d3222fab2c4f870724a11d0605c199d28bb84c1de39b6fa56e1bc1a58a9bb7a566df6d861e6ff3425e199f809846db005d44ba2cec8ea4fc4a714e1d855dd117db9faee7aa9c64200630ff20c342d6131362235643226f833ce639e58d0d5029b842de8b1c9d1500d9ad5a496e6a4b8ce02a5b66255a4ee0ad86211b0bb589558878e022b5429601ae38b30182bd96e81d8e5d6b80efca95a2a232f39a3552e3b27abaef453c620028fe1bcb3a8a454de8628ab25c515e73634a6db6b6ad76d6cfca53dbe15fba35e872324242e7af28ac1ecc19bd20c148deb21672414b6237be98f6b7e1796e1752a4bfdb51b9e78288ab955592c7c448c8da1392ec5d209c77c9333b8743bf16ec9d75d130058c46a393fb972d6753da36cc9cef022474b9c91d4bf90acc6c365c385910cae7ec3f666854f3113f9ed0a10d8b81bd3c27351b947887c78fd84134a3ce6d7cebd42887b0e113fcb8946d6a596f3a88a3240fda05a1a09bae76392296c53db2f5b7813a9cb8dd7d6e22ba477489ccfcbad5711e72588aa73e6d2872fc3af8e57c4d3d413347c6a52293f3c0699e833c2b8c9ebf0514a16364bf6a42397684ae150d2ff5605b5dbbbfc3f20853a6aae121ede7514dcfa31185f876f8159e4e2a912688759546b06f2cac875f1b55a1f30110a1d38ea9c795eeab4dae804ecfbf9a1976a871fa90df40cee5d94c0b8bb6069daa1c35037741c5f60642db364537af5afda4410269d2090e6f1652abd4fcb729ed1093bc621a632f73a499726028883a9b18fac94dbb8264a2d450bb74770a9357aa477a1d7e6d4b079306d29a493927e2ee9f54b527494c186ba6ee21d111571840cc9b77aaf3e207"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9c30b9dc9599f09e031cc2ddc4e1da01b23a8f487633d70e6aba3119d9f45b3e","proof":"2c2b4124274a737f8e7fd7eb85ca23ff89ea172ca8a78e38669bcdce13b1547fc6ee9a836722cf52e2695bc7baba1be9bc99cfeadbaf781d4072cdb19912d667da8c99385189b31ccc02946faa5e2b1d0d90dbea6bb7cd89ed232b158f21f776e2c4cbeeac4d98ef017352bf0147bb0a2390663d131f0669cd5a0aac9ada415aa1f0e80228c2d4f76b07baba8d543a91ab302d23a23776c05bcae8d76527e40f12cd6bdba72baec545675259cfb276400b068ec8f764fa3b70e489467368010554acd83e9b1b28608f3e1aff98e7fa805d3893afdf6eef34adb9b693dcf9fd0c68f0e79c3e3a2f55fa16e13868ba825afacb03a18d7ca6fb05bd3aa70572d87056d04d0547793e7a1dcfbb7bace5d2bcbde510d204326c4fca3a217ef4f41d5a821266e51c3eda05c1484fac7ead38a0f639dbef0ee918a58da6fae2f3f2a076e458bf002929cc11f1fea232ba789af802f90c7a439cd1da75ddd53139909b2ef0e31ccc61510123b244e953cb755c158c4de0d2f2ce7c3416f8612e754b33742e482344c89ffae605cd1614f56d34fcf9b0b96a77b22902c477f25701db681818b00cf1dae77c67bc5006cffa82853adba46e05beeba55609542fec8eb85061d63e80ad542e61566d10056a6fa95b7ec1cf98a6273791ff07d1964e8dbe48448233425e5cc75a32c97b50ee9b2852ede692cf479e03389b074a7c8d64904b5294a8c6d80e316ae7b8e5876648bc3752791bc69c05e3fb997dd4a13ff385327446de3793c8b91cf3f637bf233bdc2b2841607a361e8375f694d25655b2223a4c5a1552d91669ec45bbbe509e479b462a38d6c4101ff2e808e1c0a7eda6efd81e03d3a8a58c5ba421c5dfbfbc890c863f2c5c6b87df4552c9d8440be663e15406d7f145af77b9de2f0e1aa38681f15285d8335457a3c42231b1972917a03dfd0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9a60c7e10da4583105596abee65489a7631c2815520cb384b81e7c6f4cf7797c","proof":"10eeb1fed86e522f643d0df1ec01fcc28ae899cee65c1f22c22cc36e90aece0d7200793ebd937aa77421d147524032c7354fac7e2aa62e7dd40dda93ec2f980dc4216acd023975826648fa4289852c9a42ba26fb01480220255a236e6e4c807cde62bc3ffa52b0b9d8a1488e03ffa37d3c5831fe15c4a6e191a621f901a20648fd34714835610bf3e49b0c5bd5e10300ef2835e8505578183e5285eeeb979b0045c275d3b79b99e8d03bcddda5a2318292f02bf610b701fb0ea943cd5cce9a0ee309f71c72c63a0a788337696a6cddeb27ae67fc1296f6b10f1a82583cf64c0e426617a4c1f6bb2d80b76e4431f7fe4375796c2d928fe777d5b40ea1eea87a6acc3ac8494ea6446b8a99354ce8098339a6c2608e8104b1da8a36c6ec8b17105d34c694ee7999aa433cbe5c18b2f6bd022d363455ae541228f2439c8e0f6f2e6a7e5f270db7aa914bce869a1fa62e9b4201b2b67a04805464ced0dfc9bab1412af0c6dac4a810f2ef375a361ae47bb89a24c4ef64fcf5f043487324e699a64d6264febbd3eca744be22a4e2be3ec94ad8e9c8c9bf0a211985427dfe128f00b66b9ab29bb8b05ce2bb020a368a5160ed58838c824a3e60a01c3d03ac0d183c2c68a6e75efd62051f8ecb56abd7e04b967b874d90d4fb88f1d2681187f197b6e66e3cac8e2ddcae962c7df2452b2735221f9a70519b01431eb1dc20f83d2abd995a186fc1ec8e2b755f8c34c6307fedf9c2c773cd5e4d7da240683626b84daf62685e1de9f093a5a9dd46e47b2d6d98b476328968b38da293e667b7152de7b33b2dd61be643c06fb869c65491bf4e72f9e135f34839f534fd100d0be1fd16b95e3c86043e777723e0f70b69127b576a6b51a674ecc03a4cb9bc0aaf18fa6dc28101ea7dd7cbfa6c2fbb44f7822aff58e02b318d09492fb1a75664bf31ead069bb05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e0e2a0fa69b68a1a964bf7f0d50309725502daa1184963e6b7d47701c95db86a","proof":"14779524f3e1204e3de3a9af062023f24d27073eab4ef63c62c5f89793b20c4b72782e793ea844a6af213fa4f15dae607db399ba125c23352b3459ce93a21b6e029cc4ecbcfc0737f56868bed94df3f77654e1e61387256c175dc0e3007ffa4a1051f6df19014a76b71584efdeeaf3de09d38d19fe6c697566cea5943e8f8629b6a55481e5ca005417fe7f89dffe914f66c9108f0154e68f93377857afbfea024f8789d2dffbbd4e3fde59efd18f792a9edc2273f08af1f7b2aab9fb6f08d90166718708e73bd2550b4b37e11e849498ed8ae47aa1e6b1599ea7c8e6f532ca0b6ef395a3dae0fe719e186850f6bf02c5c2f0029e6a6f4fffbfceb02c58a2b94efcf6bc7eaf71379754ea8e16cf6f8b6921ec1dedc4d345de1094f7ed45644b6954340e82e3880cea6ea0befec7ca7de120f48ced08dab5396eae35e4b25d902076c152db5450407d2a5698bbdffd583acd7ce94e4e3fe58d420030f55a61913bba14a796953c3580998f95b56174b0deb22c6549804b33e1c858c6a871c8672d866193cf31e7933abb959ce5e2864ea4f5f77840c52f2b88f6fe86ee26be6a06aae26a5dc63bc1a3956c3b14aa9b2e5532320c5d18ef8d82b1b0960d3e16a72306df45d01303446d0d740fd710b841c95cfefca2f33fb49170b0c0096ba2b418505880e0c6165613c443f2c8119c6ccc61df5d86421b7fd8ed5aa626c1aefa16c478855fb565791b76bec4ddfa4e497d92bfb6ae275dd93b619c9af47ac2684ba04dc1f8bf7ff2020c0014b870b813d87badb510f31c9e9afb0647a08b56310be68c666dcadeecca53760f30b7781079578e891ce5c102f2e43aa6d24e2f70666ece8014e5220f94cc2a848615f1b84884459dfbf1c76a2a2532f338678441049776716bf545f1a67f8b28dc4a7170f98c3028f0e6fdde912c62dbb3c94f3a02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"022036eb45b65a854011c580829b4169758457680826ea9e527acf0af6020a06","proof":"e843011bb540e2348ff0d6242d984226b91da84ad9760ffbaafd1703355c9d42e6d67bdb295d1af92f0cf579305f71a881163a8145f95816f513923aa431ff76cc045c0ea5630ab05fe9b4e837c898ca5bcd898527dcc9dd52e8cb00700e3444e67de85b485e83970327787613948c174f0995eb012859e05d067ee6678c6c773ca70ffb43a50009379425ddb51a222a512cd03eecf2c97dae63ae9c8a101d013ddd232703d082dcddb01d9fd4786bfb599bfea00e6c2c46cf34a49dbc036d001f6e84d961f48c7b7fdf8cb31607c7f372a356574b4f46452e0e741d72939c05c8945fd752716f20ea58b9dc3f0cddb4e3f467287378493b66dbf098dce8e065ba354d6b9201954c375c3746d2f87a46648e066a30e8c54530bc3555ad57822686fbc25b2cca97e3fde167ec41df20bcc8b1e936be4aa5a45fda6f8e04df65704c9f9e57f563fda4fb142b4e475b3bab9e6e4ede74ce0888e338828f882a583898be9e12d790d0d162d13eb2d3f4c6cad19d2e179faf752d87a1f1a2ccd1f37a96a1c5c5362dfd6553e752c1c6376cfbbe46985ada3c9d71e08fda7dd7b0fc782ebb5887dd7216e37f89ffc0737245aec4ccd53e907d5bebb99cef305438d61934f605f76c31031aa8f5b6aef3f6f8ac57a635d6ecd8732164274aa3d5c25e094c64ebe2c793e0d0ab8523b46c6a611683dc97de18f18f26e5b76867ef723e00465e8a9f178b3b61db0c1026e1699f4e89855c4591b2c26c8f17f61b667a30449ef85957e21521b8dfd139023101bfb6209abc16eb69e591a0b554dde478a57522c3849088d8c77c19104f6d46fccc02e4fab002d412f7cd0b17e9e46bec5f7e717ca456178ab4169c5a8ea46e6b8971738e03195af6e52d57bfa5b32c82be05dcf84308369eff4b7962f9f48f2ddba55441f0e4d7df17dbd8aeb35910ce2407"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"44e43a9e679193f0ae72e349760b0f69546c3fe949902dee39fa7baeeacd5419","proof":"422a05b8f4152edb3f8cba3f69ca8971c60d76cf4d2cca95203191d04c8b9f2f4816508b7989b01038246d3d86573e291174356efa4942986f16619c8174c0799413a07f3f9fba4034731faa825695a3801778b5eb8a4f89335ce0fc3877110072e8ba0c6e1e9d3c40574c66b896bf495c2261a96c3113a50b883397fd7a03315753fb3488b37f280827ae6ca7d60ed4064771175c6ec77586952631e66b8207ab0e48cc1c4364885fe260dca34dc29314496d67d58ebb3050e662856456a90b2da576ecf6e5e687a88eeec09b082fd43d2345d64b0e31bf7f4a6d0bb15b79036e23f8e471ccaa8604a868136c88e11c7c52eb93fd66298da350bfb9d06e2509cabbe2e116643ac7ad5afd4cdb4152b2e7c49189a0051d5afe05caf4887053789a75a3dd346873850aa308ac2263d58de27a1d855feb93b9b8b1fc384000354fd0f702048b97a02a7c9519e9ea38bacef24afbfaed4f484e6bb071844b02c62b0cf41826d96264371f0486fe4be949e6e2f6a906c36c93e30da0e6c75e61981bca6c4b4eeac89f04bc3e97272484e9e89f00c4ba25f527d9deb0d641575bc7380c0839e331844ab48f9be5658bd89b4e1431c148941d05a11326abcbf4890676d86d9de4ac31bc0d038b0e596efbfb0825ca48b3571d89627829cade6ac863356090d1b63786ab0d54ee3fba3364a31d3bf101c61d8f56b091baaae88552f66a280f98ac70fac5bc44ab7e1046a04c224966b62c8204d969b7b4e48ec5cf30753eddd4b55defa3a8558300db9f583e9ab67faf3fffe0e2458e1731da3c375d2a368544a75a60b9058282532d3349404aa230d1d2536ec307afd1b0a4bd24ee203cdda48c170e73a0114f9a4ae6cf6ce61564c40cfc27693f7ed539199f83e70ca6804f61312690321fb79e0a64ca0c9b9be71e001c523d5795da1b052290eb06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2eb3e7ffd4c606ec9115491bee4c774832f19a2bacf7d85c02dabe10b8e18c4b","proof":"501d4f193854ff9bbd36337f8698c014887d9ca86a88571793825dbac3ae1861720696cf21c71a498fcd29d23bbfd65f392f7b3ce392820c230216aafd46da37527811bf351f7186c85fc6b3bc8bdce9681d68d176edd43469b958fda644a13cd27cfadcea6927834d015af2dbeb7e7483856b65fc57a9873d821e440b04d844e15c21620f1de4e7e140259de35bb3d9cce8a4ebc09f943343dbd12c3afedd088914a54f971d6e7ef07479577cd69f5c779720617ad2336461fcffe6afcc3b0957d5a8b66ceda7536ba93814a4468965c965b237a8c2b103c2b10ad02876980608570887ba2454258445887e680ca6b142af8b38c3ffae0c642eb74ca3ad2e2126d4066e58ef7521f8c17e41c60c022b6cf37bd7fd0baa4b2320088853375c5eac52c265cfc5e32c708a09d07b83bca9ef0304f976d7755c84dd9830f1d9df21d2c98cc44283e3b3e0ceb0f2510a34cdf499a979b83878735ef89bae4647dd1ff6c0b44b94f8710a937b9946612d432ea04dc6811c4252399e861109b946ab6d1cca8d25afae18cfa404f7dad715465af48ed040caa0f504538c4843fe669a74ecf9fa86a7cb7eedd06851634d5f43f52d54369f4439360722ccac30da5b4140404251e530f9351b792a294972d5c761253be5cf91ca236fe16c13b020b1405e9817633f91772de04c49b0c07fb98da1919ee6325ba109c5c0f68b6b300f581cda15019d6db0f1a1134c42cb1f1eae8aeab74cb6be391ccad0b4269f58e0ce17c81a82d4bc256656703c0a986ea1846c2326eb598ce124e125e4e2be8d94000e18be337ef2ba2bcc8eda4b2c25c9f36e409e8d43caae9ac52e4530f87542352cc4721d690f8d44a2c09cee27c01d86e653127d9f681e25fb2b2cf7575c44ee06beb83ea5e48aa14dc63f61dc3abf67541f6774e2dea6b84f60d8eb23b29a5003"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ecab236549c4a26669a31cf3d4e41ee31ffbd949e47731e33edd54de91908151","proof":"10dfae37c02ee5fa115fcb85a529f18f6ce68ca1e63b98335e1c289d103ad578787af666c3b1cf283200869f8cee0dd96460b630292c5c849b4b7eb64377de0ba8b3e568cfa4c79625fdd62e430ed381053b8db0da14dc01131265491c16611e5c24fae0ac14c337c0e5d05f78fcfa4dd9433a0e91c540a1ce29f5d19ec6c110bdcc698d910995e5f66a0720529b86fc3393c139c7adf5f5e23d6a0c79f41a04d63ef62e18bc6c29454fe41fb2bc222fc5c064983d862d5ec4b0bfc0e2daf10850c61d7bbf93efe8dea93d3f8ef27b1b303cb4d8c361527f53876d0644a99e082048691c4b9804533b0be872f700ad40bc45ac7ca5019264ad2951b48809d163647d9bc3fc4d8fa9908c337df69f144283374acd162db764c902d345cb62e31c16d28f069ac3d0f7f5ade4b65a0321bf30e19cd1edd284c67b6d4220874410152e6615a85475b053499990de9818d0c9de98a077afc9fc27220a485156fe3444f61daa81c21a45fad11b4c209f56b9b91a2ede629eabf7ae6e128b139f515e4836dfa1cbe9e9f704ea105a8e434eb98ec85299a2aff5a7be7f225ae0b97910064eda80a88da0087ac75f3f8e0b611723e52cf17c7ff61d1c2ac6880f7a8ccf777cbe228ef1e573abf1862c0799fa3e1c2d7ff76589e88bcb49a9908df3461d6db271d6bb003238cd8d81aaa749d14a708e165987bc01f69fb99ac518d137aa0b5ea50849d4038e98aca96fdef11d5a3ace1c257d8ad3cfc95186ce921121d530acc4e2e5221c83e8cc90a5dffd2c75cef10b51292849144ed1cc02ec0e5d8c43a038779ae5aefbce557001e5bbef1a0bc97dfe243fccec3de68378ad1d92626d442aae181ba07d8509f60c43dfa4abd80fd10f39c6a61a1d5dfea2fa1609dd03337a39697962dc4b8bb40dd6094e02ebd821d14d30de0e3683df70efe715a707"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"42ae630db5fe304d0cce3cf9dc627fa259ddd8640becfc145320c25fc2746f61","proof":"f280b977f83a849102399e864083734d11cb507beaf754ff9703ef54fc5b1168b878fe55e288faa6772d5d52569337a059ed9f48f4c84835887b697900a2c6030a6ca9c92d52ac68e9fd2db329c6676f50b611bac6c071538e736da990265226502f89634c53e742a18f5904ff392e4cb79aa5f74544abf75e4b33f56b0a93067cff3bb34b1b1824d79751011e8c0806ff951d69c42c6b4fd30c57f349ab4a0f45c3b1fd2af8eafdf56281333f4be4980b0aabd9f351d952b55c1ad04a44450cfa4e7850309f237ca67dcbdc68ecc65462aff5dfb6f0e487673ac32d3a27fe09d6de37095bb9958ef89170405587cb6a2b84373920b23a425cdc0acd29e0276dfe73e4944fea2e5c2b7b607f8ab92adf67a43db4163fa4245b75782b7689247158725a76baf7e17284780607444131d2a999c65aecc83df4815a736db2e1d5598e102d4f0c09bdea477d1626d87cda65e47e22060318773044640bb3fd96800f1461fc8fe5942a27aa8912ca73b83027e91abb1860aaa84ff525c569298bbd7dec41bcd1e61b288ebd9593c1182da4b0e76b33d6d9bdd0c5f462e156572acc71586b9b367e44b7919f7d541b91c9bf5089efb328f81cecbd131640b77d098563dcb2cd33a0d8e35e4e5e160962589385784eeec84af08f924e99cfb7faafc84e74164a3c39bfdeda4e47a6be129a4e0337ca6d611d6d907b6445d962e55f2922ca1c0ac32a0482bcf4fb8103df479dc11253fd5a2ce2bff556e4896ef9d2057024b0c800b240aedec392deb339f415802e6a8d22785a4ef8d1e098fde8186424d4d1aec299635aa14c086df8e10de915e5b196e1c72e1ee2d42cca290446bf21031097ca92de4daa8b55ac76032994af8f841af4b71399586a94924ba825fb0561956bed2fe3246fde5ecb01483a084660aa3509ec0d0ecdd8d69ae01c8e040a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"040712f32ff115226d0fb9336cde9e1244053bd7773f931f0845170118db3013","proof":"8c65607eee72e9434f0561c1c67668f40dbb1867dd9b4d9792dd13ac7088512a2a3340c583c98623305e274aff25ebfec974568589147241f74d2d673e9dfd664ea789d8533f632d8d5d9218b0257049c2abbb24a7afc2bbe63e38e20d8b8b6846ed3b7be62c6c03fff9b0b94d9f05da5e679e1d58a4cff0d5bceb30b4393d265a4ce24410fba109e538319f2b1f336a130bb20643eb2032c819d5a5d654c707b53176bd673f26f1f8206bbd2fbe2e5de091df61d66bae30556f823418886d03060d0902549f0d93efc58ba3c72203549f111eb5231f19e5b0f33a10d6d37d09700418006147a4479f39d646d315a8e3a732cc609ab636fbec280fc348a80b3964e5bdefd95fdcbb6a2afb4aba6bd47ca861b5b0fe1d3ce07ae88a60c38fcc74a2e5b152092088fa130a63434d844868d87d7e3bcf33aa17f52f82f8e465ac47ba569d041cae1f80fde5501bf691b5e49f1796c2b2764fab87fe9faf7b8c0c3e322683c59be2b13eee4643f858df1aee863678ec5cb044f527391750f63a0c32e82e7054b89f7311984b8212fd1fc9daeb15609f05ea91981902a1d0b150636424ce1044b97ac965d5b9f07bf111bde183aa8c7f53bb2c4c04f533e0edcffe4be6a34caa3bec253997f26d75878d7b158dca50f4e97a1fb452acced8e8f3f06a5e36a285ca34f267c93fe72ed92282c762ba1f9789c82d6bf927dbb14dbe3c231861a8393ef3ea769e9468b3f87b5201bebd3976a2b29d62d8ceca455b64bb1c80beb10a2f4f72db310b34572693dd111367d4fd855f96e6e7101c6b7ad2d345048d57345c3732070a6a62f53a9335a3acadec3bf870f98454dc9550632f3070595487728a59209cf2c99a921a5c301f323a2b921b1869a7f44d5128442da20a39d2ce65bc20c38f3415406c82c3e60608b519e0913e02ebbd7ed23557a87e03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"de5876d7a640d582d57d66b17e04cacf89d9ad20fcf94d89dae91dedbf31aa55","proof":"1ca77b2b64d07e53ac20fc5cdfea4d381fe825949f1b979832b369bf2c50361694eab9c1077741c8571e950d6d8a412a6a9c6821f9990d1085f829638f29d532246d8158c1a8f33ce03a9af56a7ce1effa5d9b6d382bfd3060b80c02177f7824d2841463a78069f0dcb75435a5738c35e8af8cd806177b2d6228b50f1b2f91711bd7e348e4d0d7e7b5395b76178c071d57c0d5b443f38191b499b826f021e00d242bdd6ef66a6da0a79892b36f28b24e5a29133d5bcd86f9874c414eb9ee000e0aab99268c8fb58741dec9b4d9f300e074a83dbe93c048de4b47556da1ba4a06d081b453197dd21c61bfd1fd566789bcb67a8753f448c6a929fa529d5518800f7c32b1cf1e5200616ab7e3b32e87f055887961e357c6be58c151ec0de307b02f3a65aead219bdbab3d05e327a732dbdaf2348369a93f44211108d3289ad32b738e4795b81770e06cac8247bd6784b2ff8a817788acdea199f9e52d9460ab04165cc016115d1b8f5463d19bfcfa85bb08d92c6a2fd80a37ac83a5469cf1ce7f3d30a8c36d73de8bf6efdeb4388ed7fecd5fc80ef272f6c5082a4a24025cbebb20fa316b8b35ea8d0b668010edda6d7fca38af1b0fd2df8465ce06fa147020fc48ca8851baabcffdc1eeaa6d476971e51a79c7532f74b319e001f661619ae8426a3024051c87d29a9908fb49d7a940ed3a85e95cd62a371dc3095c5247b542990e4e6133fd45ecaa775f4c3ad625b76cc7254bae0e94ce903bca94458735651c0b8092d143f33795b379e244e7ccb8480bfd18feece4684d339c1c32c0d8fc954a68949e420deef95d11f530bfc878068d389f48057103cbb38a85162bbd8c9b0d53cbde142c9dc84514aa3fae996d325ae487d79ac28a0ec380533975c5269c023cb8a3712349232c3826bd2da33a87c53a97b32acc55d4d5156b16ad4f644a05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bc42145c9ce4818fb30c4618872ee1716e10d5fd3458367fbf0d38ce719dd31c","proof":"aa2c03ecc93e2b7b4dce13fc9644c056bfd3c4c1e9da20ec8f9390cd8a82e50ca21560ba65164caacf88318de64e98923740a1dc425aedc69d0ef5150ad77a3474c1c2b6d8b82d9fb58ecaeb3fd02f49e39a3e4521d8a8ab37af634e234797759a440d256a7e580ad99d29b343949582b7e4e1b7c8b1ef8fd4b0770c88606f2b953e983f23b66b6dc519e22769fea7f3e43c40efb6691d28ec57628fc5301206742e98bc9d86935c7fb231139a394dd70611175faf35b3e5f88e567b6050b804ca6cc2ed6ce8510bf0fd0619d007129d0270b686dbbea627812f20fc0b88f30fde4ec6f40083056afb3ab5b1f0c0aba8ee664f1105186de93776efce81ff303056efb31d9801ab911a33079b144d45297791b3e72b4f7bc81a293c411835aa1886c37159cf2c104282ee0199776bbf158ba6ae9c2ccfcda67b481f7ae576ad03723dadfa62f8571b835c2a92f13b474149f4ecc352d47150614ee1ad8b03b14fca5da2297dbfd4db408d1791c816a3632136b45e8fcd172dd4c2b32f071db03126833313659eb9283a02dc9bb3ea20cffa2ab5f3fe3831a62b940a319615ae234600f6a4f86b0ff9f9fc5d5c1ff24392ab30ff6ab105ac096de0d56bef13bd2088a6bd25d3f6a5b8ee776e2dddb3da2bc3c26155c62f246ac2a2c57498abd23e1e17756f49ce30fac88f536b12b239115ccdc92d6e765f49e638fa97426cf00af67101b00e38b98e2567e9ab38189f0dbf6128d8289a3fb5ab345391c0029b7fb012d50e010bdf290eed09c559652d6fa6defa91674453c09dbce2fd28d8d71b8437fdfe4ecabfabb203025305f2e5f83582246d13ef48ab3c18bfd570ea92316ec7b158db220c6e2f9dac273bb099c06168fbc9761d8f69c242f36fa21a220af54184df69ef988380032febc7c105da2943f33eea94051c5b69988c96491308"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"36204cd1e04b88f653700aec26e69c14b46ea923ccead6e292b2638e5bd32e2a","proof":"fc5eb6a25aeb2545d9e67fcea5af6124edd6dfa5a5554f65567928d38d98765c841e5e12a526f45f17bcc5a25d241dfa24e530e91a27e8fa8c9dc6fee0beed0e702ea927ba5ff5ff24669d1530624aaa015c31c8594f22b1741a1e9d0c52c44bf8379a8c89d40465a824435f4341e92b2886fd8109471321723c6be715f1390538c3c0f678a3dab4ab7c3bc90e143d4e9802dfdfa9a2ef0aef12e776632b070f2a44c1e37d484581a5de58ca68ed5e8273451b81781fe82b3c44c49a37da8304568aebe03f956900a1dcec3ef0548a97e9c28bf79dcb54438d5a86491e314408caec12dab8e173fc3f9c8e05e8c0d3531a7153d270633dd8a4ad85f9aa78b817c45a5ecf2de7dd961529441d75b359e4f043a0ded7c8a9b8771a3862007b9f2770a8c1027c32969ee20fc60e4907f12037302643ae80de99d37ccc9fe649b329849fbb909e4d4b120e0ae3eb7e87c83d658c398047ba74cab6e760dbc14d9048da483dbf88ba2dc11fe54f5b008969477a36c595decc2a5f567f67a82a40ef51160f2c88e0c249a1c743ab5d634b088a70ca338e818f43b448926e8ea33b3f42ec6d02f6dd27332adf8073abbb4c1e17f9f9d19fe3eee9c9cad07f18c8d9f651868e2d24cbb12218499179c4022a3d9584578a9ce467b3de17bec4b44b8bf427b2f5a20b4aa6af4c02ee17287507c18238defdf46b7a6305fbf1a5b34a072112967ca16bcc55adad49a1373458719ab623a68e930c3fc07893e3b303fbfc5218a8b7fb40ce26c6a23cc2f559b338db5b7034472c76313e3bade35df95f79e717947d9a0884881f79a3bbf597241925feb61ecdd977d135680a51709ddcc45d559f3e158faa77dad4483dccdd93fd2827fa419e706dd49deba432c7862745e60a9aa2005200203d44e6b02679da50ced82a775f723a19293633805dbe622e9c0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ae3644be8ecdac5bb3b59f8744503a9137c072984d2551e1058034f24dea9b78","proof":"1c3a19295666a24f70a8b31d09b970919b01b8766ec7e3783bea81c217a575491ca30f78d986e69146c419aaea6f21cc89d0d1ca60d186fc0c3aea04ba98265838580b352946ecf585a60848dd82a0b6be163d4c67eb01f70c11bdf6549c327d326967bfae8fae3c9d812c89182637314b43615e53a125f83e566afed0d3fc03e1776e9ebbfeffa6309209b0cd5ac205d8440d2a660a46c56259004e7d88ae0cf661746d6d047c010e45b2ae11710f79a72183a2aee008ac540db7f91a74bb0e7aa56f06c4f0af90415a11a71fa45115ec49cdd21246c2781f4c23f5ca85a00c449d0f29c277f88776c3d6db5472b0e0e19758f012e2fba1bd999721dfb6ae2584cfd6810bab1297e9e223a05a15afff31163e662a152b6dbcf61de76d94c801b68450f5b64feec9c4acb166c51a33df33b15b1606d340aa53a6b11dbb70725eba7148b4828ed82bd4703bdf471d63a6b645c20e9233f18e312e36dd706581596415086c544232a7fdc2950756e8197b8fc7788a3aa38d78b5bee62d9699fe01a6544e41236e0af1694117f8ce13f50931e905f7690772b7eff22f48fbab471056c40f797f9a586e607007d6fc3772b9269d6221203d4464702b1e440918d7733e846ee2710560808c1eb6e20b1412d2caa2abb068d6be9ed4a05773a989744922d17b72c8cf1828a606dc5e109b936c4e7789a1fbffff6c7e6b48a2b572e4337a22c1a316a7a89d42b953f1dac57d3d517cc3e8568e474ec32a370b61993642b0f6e2b99033fd85fc415459a3c7233ae26a2d28301a3b7281ef8af53404a52b2418aefbf69fe7aeb2f82cec0098f54d5350b8542925d63d6981a45bb4a08234b0acf7d5f680e63ffea38973cd63109170f99c368712da0f998b06bd1a6b0c09bf5adeaf2adea22b3468fbe08ef87c358f25252544bf52811198624603f0f601"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fc0fc3af3d43a6f3b710ecfc09c25b624e5753b92ff521b60d0c909cb6d46e0b","proof":"ba0fc29af71f05fd8147d71fe1d4c5be7396da862a3b21d3c4d02ca09e28011ff4c5fbe29d2f9223f79d6bfdd20616d741c1a7e4e6e58a413ee3aa4bc8459a417e697d0d8de5b804bfd149cfedfaa09f2c50be4471cb51305b82c519ab32f56c5e0883cbfb9485d4516bd06523bb26391da2b5d22f7e64ffa69d03a34db55869183179c1b8b0ad3416dbf3428dad3cc41e7d52b67ae8d9d678ea0560a6d44605c632e0584cf271e79e01c0ee0a69c82e22ceeb4a64577ac83410f53fdcc8270d05a200cb1b829669e4de3dc631ea78c08296b4a095a18cba058809a8fa59ad0890bae8cdbf1dc395e03877f7a5b702cfd7984290e66553a8385546f23ffafe51cc2c82a90ac0d612801c60cea12896a0abcb105bd4870802b955cc3efcad75563edd39dc77a0dcf844ab5c02b013857029809a3b16b080c3b108d7ddb2245444a08fa12ad1f7069d1e4a8c443d6a8aa8b070fa4c90d156c68d3831addf1817415808c839f80a6ae4de7c87961659666d19b8ee478fe974eae9d56113ec49f113ca3117c57e05d3a1394f1a7cd22532a0612355fce949d0529f34243c21dc0833ced9cba441d3aa535d0b9da54bc3877067daee122fd8f332bb7831bc57e32960cc321c5a39851687500b94076e26f934e4f7ca9fe77726adc9d2394009c21f535c38b772c57215091f6b96fd8c3eea2a98e426c21671f45364e06dc853f4a307f848099c0c8328cd1adf09bda8236298399a5989f73d9044785d9c9537e60a5a5e4045e571c2226bbfb61856e9f1d559773385091295de568b4463757d379b57fc00c4e5e1ab1f2ef921365291114ea123e74567bab61eac1e1ed06b4411862cbc05e999a1c2b644ef8d2ec700fab5fedd62ee792b817cefb863a041f3d24f0aac337d59c32672b172c1467b1d054aafb9a27c130ddc7d14daebd6266499e605"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ba312ab551f9ca753be4eb791e15d1bf57f26da96f060c4c6acda82de572610e","proof":"f8c9ec84358b3df5823fdaa59538ad0932b7312ec5163fd1d7464c27207c4430ec4867eb0a1748929e1c90683779e3894b0308101db15707e4ed0324b44dfa4c1016c29d3a0ecb3ddd76d6669bf9737dba27c1cee7522f592e206101d418cc3ed64c519a1e6a260d35dba20360cac4955831b9bb9bf26930b78dcdaa050e34623473e641e4a176ba9ccb4e48889569420754262a32106fad5604bc7ae3d60705a02e9cc0e177e00288953e3c100b7c6339581a61d300fa0a279a888bd507c10af86ab4f5c7f19941b1e565e3a8648afb6f69f1ea38dd3e16321e7a09a918e20356bb338482a52587aee4f9706510367c11d9a97c1054b04ef04530917db3fd36eed3ab75cdb80d8a9cdf00491ba3aed80da539f6e474674511111c6ec3ce047d6454839a579821dd5bf015c96f19243c42636d60f997dc1fc0683c7b49f9134194ce8953d2ee00c30345bfbcfdec7b37eba217897b8b3971a538524caacb3f2a18649932422c6e4e3a450b8cbf293d1019c2fc373e75d015bb7d809800abfc2e64e2d775a73b952e69e61a7ee1dd133afc764234239327b3898207d41595983a009319232bdb3dbc3c2cf8186b3edf0c28a497f94ae03830649c4611145807146e49ddbc0dd3504980600a6786c7cddb44393ac92c73c280fbd5eb13fe806a43f8ca30c8911de8f90a899556cfa4ab5b3924a3b89238dce8fdc5b5fc89683c5356091d4f6dfa260530121d3aa32c1822b754119a4202331d61fcb4af39f9b15a5e7c9224f6dfc6d29c824d8d56cf2b694fef9ee7ba507120fcb2b0a45eac1a749ce5ddb9aa180047fe98367205420d3c4c0041de2a61304f714942c34cce1d2abace42ef86b6f4852642914c32008880c8b7aba491afe9ee6e6dea0383ec880b4d9eae5d21e10dd8d58d5f1a88c21706a576c4d465beb5b2ece82c6dd095af09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ead3db93fffd0b1810863590a2a5dcde97cb92720eb67a326fb6079d1d875f7b","proof":"44e0e13ae9bb82ad2da52306f6fc6bac5472aefc267bfcbb0d8aa31f4f218769d0a56aa694299635f373b6818bc3a2ed2c55cbeddc281358ed6864d0e91fd9268e24957bf0e27f3f6216c185724a5f8a83f0c5af5886a0f8010f2ff3e373cd0bcaa925c8771aebfe16e55df5ce885357982e8621390f85d7ddb0fc5b78d27b637f15ba1640735abc1ca417569be2e301a58f42ba99a947baa8333c3bc1c0d306afb9376a9df1a405cf909cc62c77337d55e7c1529fd6ef8bb04e7ccfd6837809e781c83c4347e5db8191bff8d781ed0e8891ffa9d46011a0126a931c427dbc0b5458fd36b123289c07c87ef4aab94e6ea51b6ec6350857a175529d800ed24e06188660917c8b68e4281ff77f7ecc98c104957a87326c1374e446020f8f53035b481724bf4f16caa19f354a8cdc2b9b812a413edd3980aede3671c731c186d569de490358087977a5ab65539b72f6d9beead8dac2f95dc19e6f0fd970e9a9056de28556e201295980d6bb08da1db3c2dcfe2c3c6544c064eb48e6dea68674770c0e2fca15149818fcd069a41a007c965caa908085790d72228f6d4bcb1309117daab515bd1beba2fd200551878008ff5d1a5a565be7a1d049ca5a05efbb45a477aefa3aa49e4c3e7536ec3f78eef9dcc1ad126ba7265736d7afc8c370b495765036dfab4492b0ac8dadebaf7ecc46ffddf95b19241fe781653d1838f8f7dbe271cae8a30d2b75b2cc500829b3d5c2e426b59c027fad7f0812df4650e698d37c7162e462167e0c011858637f615fa507c61c540d05b413015db5f94dc322c91f40c62b5ad32ded8ff707260ea6b644742261f3a37cc8a0924cb92acc04aa9622651ca24f98b48d0e6870dfe2a8c1d74c48333a9ac71d02c07ffd36b06f38e6270e6ac0db2160a8610321cbdccc79a2b85f2562a8aa657604e9e2b917d04d5a7303"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"123e9aa81368373edc1c3ab40ec0dd510a211f124ab0373703c95b5ea905006d","proof":"48fa6615a85e7865d98bef75f86c540fee276822d8d0483c3251eab0dca3502a7a1e79c07502fc32052519cdca4ebc9b61d3a1e6323a07223326e7ceb0d46b28aa280e84074a2738c1f776cf07e272a6344b7a0b5b4f7a26b1853c4729986971f2c959a39d16f05474cb8b05aad718c2267b177dee2ff07be42f1293fc152b504cf51c48747d3d197fc621c7054cfda7b2c8635ae2b6f271e948db404929c309028181f285655377d57b720af303c802533a4c575564695f69bdeac579495309cd2eccaa924d051a428232be5388a3a483b6081d2cc2b48e8687e4d9bf871d089c991232fe8e9226f8a99f42355c111df1bc823c51a4bc6c471ec5877ddba4119e6cfd98ab44ecae9f689837174745ca1aa74063fb042f88c16d6752bf19ba26f64bf45d31ce768339cd8fb981e9c68bc0ef7f5dcadea1084b0da1c2b5c59f06f44e3785f94bf85c6855c5b5424aff80bc6b0926d6341a6c156f96ed4040585bba69d842fa253f3fee94279f32a95a035eaaaca87022ab432498a452917f806314fab7f7a6022f4af095133c26282faa367bfda7f1e093a359ccd54f1395783da86ef08987ae9dd1579730ddb0179ea87182ebb6812d3a2b191081f90ca2e567583482de5cf1eb85297ff16193da09b023d3b5348c81edc2bbd3e9aa7b689847103c7faf00cd93d8daa886ffb3990ed47963c87504e3455de3988536b175486c141cb512f4259e064c405225fa66ce1f5c04da90632c4446d903d0be1ebea850c87f1f6e300dee66b2b247a0802cc6ea49052425f86a16cd53971ff2855e66664c216a2e3d68c0b44b71db00c498b9ba9f3e4315364532819f09d38de514c801cf7becb96034df5e2972730bb24325105358b3d2920e57c9598fa4f8d4d46b077b2d96613116191048471b854dab70adca2afe91e359abae779526a0ef57760f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c47cb56ad9d8b5595a820e341eb88ac0b15459e90dc3bd76fde66aed71acad0b","proof":"a822d7b50c3d5782f148dff46368096c729e844bc40e9d8ced1554caff15ab7dd25422de30f8a05cb4c7e4219d7113cd90705b6571fdb61bc6857d0bd761ae1da064b3578a5ecaa086db48a3f125239cca81e487b3ab2a14f17ea3cd926b692b303e45972452e7166892d1b219c8f63a228dcf8514766b17f327a6d9bcf9821e7d6a40cc654f1fbf79122208bb8abbef0a9a0a211f80cd2787b5b3f2b212680a6b261d780bbfe6d9f39a3e6dbf3698b17c1ecfc30dd0ba4347202bacfaaeaa07ff678a0baa13795f2c60b16be22013581586d2d4ee5942677543f5e4713788068a346c6b7853795c1112cc5138bee6c8d3b7c533e32e3857e07eb22f77f8c77c58bf3bb746f238e04800853b5f0f67aef34c0921d4f07830303f55a235a26e030608e08ef1f1228c3ec6ce2711a23563ed7f469f770016f6f7575103cd2f0927b4c3f89b056ecfce39468da917fc0d12fe5a305fc3c32edb754f2b2c904f261e94599c01b9fb356a15d6ceb972bd819ee2559e2f3e84e64e2696305a5c00b722308016e5f663466a3e28f7c8db5f090cfec1a3b7bbbaa952a7d519ca544f2c015291544692eca8517d6c79f6c4f95823e5ddbc397a0b78a89a5007ce0d7fb92d6896e524258b20f9bb98bac985c071390477aedd60952dfbe46d0eda852db053ac898d70fe602c62952eba98bc410a9b291729922e1b60ee6f5774d1d1f6af1c122155d0c95691af92a163a9a51d77c4c37865c3838c79aa54124a70c341a661dc2b2265bba320b1c1882c6c38104359fbca56be0d824c0d56841a8091b91366b65f7cc71b8480c92c4cfe79743771149734fef025d2a2099091a7d009510d54a900824593244a81931a8f9bf980198256f268d007b55a1b09d5c7abd0b13a067930fb9a24ce2bd3e692fbc6e2936dfe6333211e72d7790bd36641b0e64d8c0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2277bdae2201bf211739bb2a7aefd32a42b830a7d48eb46eb0020433459cdf1c","proof":"44a514a5937e66a687031a2dd986b1186092638e0f0f9f126225444786311c60849b1afda9aad578e7da39cc184fb9e2b0475f13a34a6ab68c53743ca92bfd0cea01bab43986a4831884240f36ae90b792a6a110310275d5346a97762556a359488551c95689ff0c132b15103fd878892f536e670234ceb8fe68ee5c09593f24db841cf22d408f207d731c9c854cbedfe161d212d8bf54ebf06a1f8c68bb97066519d7090584affc7673df229c93ff512a38e6be1f27347c958d17d910a30a02731a814be6e82ed3f392b316fd2c72ecb27cfc89a74485054778dea46c584603c83cdbd4bc89e217e0f116cb8ad3acb855567fa1a976c5dbad0bbfd192cc4c31ae212c6a7fcd4fd20bba86f3836ce6836cdd99dbfce4016c39b856d8070e115038c869a77ebe033dae0acf2e51a0917e865f59dbfee0b9320793ca547e50037f667471583bdd018e30270c0814b8cfbb91059d408b8d30981187bb5859adf96f9c4b53a0f014b96951d5b10447f90b0a0807dab3598d5b63dad9eb493fc8b9319c319cb5c54949b506446405aaf9a8fd6a6217b528d1029104e21c77d5c5e011267e73626606c8629fd137742971200c29e61837d0f0338d908863d9d061ac4fe02a77e2347ed08604c2fda8ca1d865da98316efd2ac9ccac03470ff382ac57fdc929aaa0af96293fb9f01cb491110a80f646da7e2ed16c8d4e3bdc6d6d5aa243e026a6331040e06e7792b0503984465da382d6048e62fd1b101ddbc13af980f880b2373e0e9505bdc28955c788f167193d67f0a530f8918a8fc7c5c4f96f448148002a61487e982e71943164d4e0106146f5306ead4db6f74fa20763f5de56a78ec52e10bbe58f99c90339c44257cb784f202e0b77e3dfb2e61409717d89702a55f5a0888aa6134fd0b9cb29c94c5de62e029fa2d103857984f139362f68409"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"88b30c06d35107770115254cc3378f81646b49f14e3e5a9e5e3d230c642b3457","proof":"da6b9fd453f95f8080db7d38eb4273d3c996c544e29af844c8d517beb66e586baa7ff1468dd25621529c2a30513af474fe122048f7afe2d0967e75ec02618951123d932ee10f2dca9fa72954508158e022e9d9f200b611a05f71b9098476aa552021b97bbc3a668afe08a92f0154e1362c1aee527444adfd615a37f4ae64115460868cea4867dce1248b3b2c64be33aa452694ab60aa45a162d7c2a4a0f6e60a2814eccbd8dcb9b1b102d438a0981125be00b193a9dd5a8dcb073efc99a0300a9f9eb1abeec1a702a1ec92f4811dc8fef41319b1ac3f50c3c9c97e0022c0f001fa74cad6029ab5a6ffd155338e992a8846de983bc3acc90427000114d0c8f00ebce678d7d4efdbf98d53fe33c7e59602bf5848fc9c83fed7458156be0cfa9433fc4183e83ab58bf26483378455932212ba2f8a0e750050c55112b34c29e64b00a47c1b6e4328b50742d3d46a82cf15bc5badc26e832af5292785a92ac60c3c6e80e03101439a6541d7764c8baea3d316c3355be1d246b927d32cfc10dcafb522624404f471e249e679ffc26babdb3992b152a0e0a2645889890611834cb750402ec2fc0fefaa0c0bae2ce3f901c5a8bfd78f382730f2ab126c50470b3884513c4e960c443e3476364c9fb5c59de2fd0711da4eace09beda0e396efa7d5c7f1567af97e1beca35dfe0c0b01d0617a37578e62417dbb78ad78e000f57576aa726a327831b9cf69b68ed8edcf8d59495dbbd943f0056cfc49a17e381d15085ded39989fa930929f423d5ebc3f24c6442311281834035d14895021950976644d0c2f04b308b96b694099481f318b04b3d32da78207a24d846a117c235bb95528ae5f1b9bfa2ad05780d2ecdbd7d696163f4b4f305d6053929fc2b155aac92eb38c0bf8ee2653f0d699bc422bd32f2edbaac76a6e73517f15f2161682cde84ddea604"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"787ae1b17806de3a09ee05378c1345ef2af6b50505740081b558b44240487078","proof":"f2e00aa04812975a1af47063723c21bc636bb5f1924d8aa83c2b12ef7bf20a14daccf8cee6904395df234cbe179e962dd7fb6889732ce8641c0bd9222369ce30b4d860287245d10afea0a4b2da93fa6700110f7b251f98e8258c42266d4b913ca07a5a43470e9e06ec541b8d900e79252abaf40bc37ddb794ce2c4508350804a3b6997052075b2bfc305de38320be105bb30bd7774da5e74b0523d06b285f8070d67f6bb79e984d66c80703ae3d923dcafbe4eccd6243f2dbdd5d3aee9363b0e5f0e1a5016e05f03d7160248ba49ab0b41040ab54db3262773cbd6e8f2b28702f6b9113869b2d6b27890e0d91de971da1126d5533a616976609827a788d8c32e0e6b9a89610df8b6e0689c4137408d44fa6b5673caec3848838e04f691bcba1fc2776edab6c163163155cbf9e6e35c0dfdaee2cac381b4b3e7de3518977c303bd494f6123743a7cf8a383baa380921cf834acf7bffa1ef9a983c8f039047d0205a108f902c0c5b163d67b8deb49c146303d19ccd21f3e0b8c4dd26d646c21513b0fabd85531f978cf393fbcc0747d540fddd4f159ae98de2ac7d19abbfd5b452841a6c12b89eb86cbc499a6619c85d2db3fc16ea6329521033250c880b7f595576e2aff7591f0f3621c3756324f7b51206edd1a3f27a73d2ecc74d37fd8fb90ff6430c96d15a53ec9af423844e8ededa4bb26076e8eed77595614928b3e91344241ec5b4b1a7a453f45b5ed9727bcd7b000de20b727c2ed469ebcd34e81aa65c6e83624adf50cf22c3ba95b77676073bb8230d1c86bbb08a9910509f704f393646487d9eda8239bbd4ea65bdbd257b057e9509fe489d15acf19d9f59dea7c23466aa734329be1bd24bdb7b67e0e3f923cd26fdb9ece28747b544e2633ea8d40ff2f542f6e1b63d75b2874b9a03f9c80a473f564b7b50500aac03a86a8d00a809"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"509de290fab799a04b17ee6aa101f12fb655887d35db438039693838f9fd9576","proof":"3c29a60d7b72cafaa59119049968395784d52a9ccdfab98bd6da72c3a088001a368d05eb0bcdab73ea0c85efc8f3d0c8410b544ff49c2436e7f1b15bd8c0e5288c4639f91aee19808edca824ae122e09bde527d9a68912910c2c8ef18a26d363aa6eaeb0a135260670cd3b2b4cd49cd238171a1654aa8148cb34d29522694d105a758fd28d906c4f75c98bb7aa9eebcf34c8cf4bbbb48aaf485504a2a0c7920d3bc1753618cd093daa5d24b444432198e09b0193df33c95c631a4df48c8ee307bbe25b6e53570f9e2deb1282f4f0c501359265c7e2509910dfc998acc4c895094420c4619a89cae1618543f978c2ba5362bbf6b1ca39c72f1a013d9b5d0d4943fefabdbb9e06e1ba2b0be4007acc029909b5d6f56e18db31aa27df4af182d51e521977961829e73b80ac95b9a12bcad1f8b01018e2d87ecd67c0a2557423200fa6db91409a90eab6c893a4bdcbbb99874a29bb42f857a1a7c308ce286292624d9c8a956f44af02afe54481757e15050dd518afc8db7241d3ce05fdf2e9dff6325e69cfdf949dce47000f420edef561bd3ea09d8a8ba7ff11c47c15e11fa0c155aca0e4af509541bfc41ae5d42ac475c341b959c6a20fc057104b5a61ab1c5b7c525db25fad834fe48dd13fa7728f4152c58b4a81e36bd32fbce85cfc0991d47ac2fb9bbb45ec34e9fdeeee0e91126346234ba23556a8ed4dc1651821e5968c00208cd2d52fe9d03bb1e71b5df3a124b4b1e7d5adb424b301182fe6e448433d0ceecd3c2967337dd99fce56eb517e404ae7ec5ee34ddfd181a1403bafec719303a4fbbbd11f9f6e3cc9a99cf125be089f8591657c81571da7a549453ce02c611e6a08eb4ea3f3bba83c8c98b4fe9546f401ec1f819672d454801e8ec93b1f6600984f44a27957f49c94ebf9e3b83aed7a27f376e7633d2756b3f0e85e4668b302"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"78953bc811f98ed606ad7009524bcc87a152576b34534355fa3572272ca02729","proof":"76be1bd6abaee596b863517ef5c227271054bfbdecaf9e3a835e472ec3840e2ef200684b9a9d89ef89c1d28db6dead9195fe75886d9637bd4346366de146ba1570b8626bbf609f0e9dbc5b380da7618a0338390084b83deb3c52f9e6220a97255e9fe8cb476c445ba925e43981c5c31f068a3835fcb45490459f8608415fec7908e80a44364c2e85a5cfadb03fb43b23e5eeae3764c2ec54efd07e82e1065d0100495c849aa460cf6e1c58b5d469586bd5d07b23b361d0834af2d70868637f0b16965521f35d46161ed21820f8b5d754c90da9a9434929f2682174185d13640c6e10f99a058eb32694ddff6b5c6293581f51e6e5a6417a6796ec6c208dca7c14a0bfd22bb63814d4c9cf7b2da3e5a477c107050539e088f18a0a8d790075bb1b408c9fcaeb944d7ca3b82abfbaca593ea94fb97485be5ba9968593a31a5e5d33ec1c98dd5874c97171aa8e938924374a6277d2558db71e6c1ae722b341e8c31028e17bac46a75fed5a4b10db61d0da55306b0f34ede0dab69d534c0f395e895ada74b13f805874706c1466c752575808770658bc2ba1c0a95204482a0bf2660d66341067b1c4e21adea8eb0a772753ade4000473c854103080cb5bf9446c3d740c5f0ec4bf03c799b186cee323713e8ee19db851c2528d143df719234f15dc7246fdc897b1d7b8ec0c596099617297cf0a44e52de71682366ef427c71e3a340280f1a1c7070a6bd2aada9af566bc0ebaeedf58ebfb38e4974b878ef2441fdb176e119941624622bb4d5de84c525d87f652e0bb82165cd86134d85e4646112c0b400a80540d3887c786a467fb1d621d0fdfd6c4619fd20fbfa785b6c55a06037bec095bd81050c76279fb9039f46df2eaa8c5524e1ca54193b0b8f8b14462520a3d4343113b4b968f833eca902a039a927198026d4df8251db66e837bf302090a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"be9028a7409feb5416b51b12b3fcf2f0c7a4d2879fdf7d6ed18db979ecdc896a","proof":"ca1dbd3f036a8622e995c39106171d09faa0301ef612d2cbf8654bb45e394a2d8620066c4c5b87b36f85fdfc38c540e5af85011b03576306313d70fa49910d7daa21fb7b46812948c9607866c47cec754f1e9d34d4fb89a3d1612ca14668a302447b3c3eaebf236aae1c49debb4ad3a6511d513ff68b258a3b89bd2b2e5abf0b756360ed1d31cc203f4eefb043fb2b1e94c7f2a78fd279fcd3b32e1f259ba60f56756ad8aee742ec0f989183c82af5dc7c3a52d67fe6c69b91d1fb133dd29509ecc9cf7374ce043f016b5c48c2c7ea863562b7701d68f960bc297cd9f0cef80b4e8a5de8106f7012b8bd4eefdcf6ecc5f27336983f248a64a172eae40d00f46f9805afb70e1ae348115e2289c465dea70fe747ddb62fb3371da8811c74850f12fc9ed4d4b62e8b9136963e03c49ed77190e5dbd94c8a8b14daf3da707802ec2de244cd7e2e0bc97171e3a6e5511730c6dab30d7b39f1122c51bea47ef54121315c0583a0af3254b28d37442251099b7a5934935d4b1af3027d899de486bc7f070c7e17bc5c24aff4196cc826219785fae90f2f0fbdefdfdf151c8d1d94eaf87aa8c40afe7a8062fd3f8132c7060ceb19a986b2616fe74ba50d5e6dba5c64a205624deb033e7d10e7ede25231790302aa0cab343fd18d21c7fffa9b2b3b392d62f0fb787da863aff1bac3502149f1d0c8ca5c798149487300b7e187f0c8ef0717d6a83dc62b45bfdfff1c534eaa1fa932f9b31c405a0545f1b182f9fda514c3448a105a953902a49bcd5c3cd800c6fa035e1e1e21f7d9187c124e15389515580d626ac6426aa867b7d3b9d3fb5555a7fdf809b73124504cb9ae76244fd5fa6b2bc045f498184b7da0d1e87e3794d6999bd43b8f8e7b63aecfa77ac194bfccba067c6c6411cd8346862dbb9f078d9de86cdc8c94f1938aefdaa3ce23744ae70508"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6cf0b9224c7c88fb89562c206567da8ad094ba0eaa606fb4a5ccb0d7db683351","proof":"9867c2d7212ff346cf69da247009598907e03e9c2d1937628ff84a558894e007244a34f3124c76700c97fde2b3a4e81cae41abdfc806c0532b5c37999311d060305ccbe84d6d16da7bdc29cdcdf14b811f39af2fe002fcbc4fa2a424ae8d6d12a2717414ecd5852f364b27879452c934efcf1fe784312457e2c8c6288088b0254148347995bb20901208f0869d8289e4686e061ad0ce7dd1044de36c354b660db3efa2d9a3563a70450df4f3db34b3d0eafc52f09b632a0eb1150c4b35fa3608233dce0e86f1dc5a54720b45b87240cc9a7366d1024e708d9e37e9f26e31600816453c57eafe5eae0b12b095306951704af7ff8ac49b1ba5a81bde47c316ea07929b05c7c985a1b24cf200e63e54eae55f3a8644eb1d6892441c7e39582f2365e8fcb3ce8b3b82faab6cea309eed93081e76d89390196ad7a772e0083e5a9b358ab9795adb8428bf34ff1e6915de3a3be473ed9422b0511c657136afd4113f6aa021beb214e7bf814b0901d366713fb85ae6c115cbba3255e50e1a2be5078f4f5c9e6c619d94406fcbceb672225c975e37c6d1fe7d3b4fb95820440ec077a51b1c61ce2111db06bf5c3a375da4391d82898ce49e80b378584216227c4248655bea24368cf6433c5d59dacc30321b7ddf590c85b7da1af20473c1763bbfe0d279544682a3c8bc846ba56909645a7fd34f68985577ac0527b90a0954dc4a07ea7ae83a22a7cd5fec67710322f100a2fd2251677bd6790b66784e8b9217b603e24b1232ef9e59e4dd36e992e9cfc3fbaa9fd0fb6b9db6fe81f3a52ce3c4c52d753a10db7ab88ba0a177e12c315bd484aeb1673a16af34be1315353b835d1416e92c177a236fdea7b73a6268d28d2a2d92e3955c4f150a3ef715bffb8f2a21a5b20b1e92d2da3d9728d9243e96ee85de1b394d8ced8ca7547ec6f3af481dc844570a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f0a92192b021635f2813e4f84f18309a0a68719bba094a23644031ce1a230f35","proof":"bcdf03122152de3eade46c9b84627e50eb8d1cf1d53521050899acabdce59f7f6413f9d6d2a90867beecf9e56e37bd9052275e0026980e080a8a7ab1e1409447a61a080a3105b7b9df9ac8198002e27040514f33281bfec5cfaf4e93bda1991ac64e9d07ff154d631730495245fe300c023228d3e4104879d95173fb5b46f7671df437b17aeeaca564b0ea5e78cf9edb98f744c6a9efed9338991a0d69745d09620ea0662b86782d909b381caf3a4e23dff48012845742e8847c4b2f3ba6220b58a66c6cac4450d21f9cacfdb172949607a281313928ecccc9db85aa9f5c770f1c3676023cd06dcfaeb7f09d82ca670a0d6400089a7fe7ed8d3320455bb0cc4d22e5f3ddaea5e37f59468b29ddddda2065f31da774ca4115f335a2cca83ef147420eb6c35e4ce1e822af9290019c1e92a0908dc8b6660e7dc33ab93939de022018634de47444b901b019b689e90b6a7e024641a0a2b910a79a8c2b21e574b0520880d7335d44fdbeffc770c58715437a51c5c270d534b89208f4c47ee219aa520c83c164799106d2ef72dc5eaae9a3a45dc99386e88504a25192d8e10fab351e5a0086a5d674e0a619bd59b0ab38ff7cbaca9881cec0475453d1c26a543e70445242681fff80c4cf73a2f4ae791a722680173ddcdfd154906d6fadd4c390a6248487112c5565adb5c7857554ba66f92fa9ca5ab359ba4c383f596898e52f9d065a147f86985fc40dc2f3b17517577bfbc0431cb8d86dcb3d10ebd92f111ecd34507795481c93c1c91ef7fa9f93b7494d7f6ee7b41fd0ff44e9d63b9755b83c2b487c3db08dd8137cb9518089f20fde97db18c1d52a1b816ce3a21e37365c38566fd069287b455477a0dd3ddee6b73d392780c6ac1b2c999c9bb0d29e6313080d270d648c8803303437936fb5ce31f907d21a05b49ee4a9766a239ef142f8150a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fc350456b0ad07525deb6e4d4391b94b53edcda8d87b64b68413fd11c4fba75b","proof":"4c809aa3dd6d467ef5819437becddc085afa2e4c37445d3e2a103aff25bf83022079f4a60c3ccf2d52169c86f787911f33b0ee43e08f4b59644ef6053ce474237c4e89c9bd234b68763932c7334b15902975b13a6707f31b6d9d6c382a71eb44b2bd10561e8e041061b9134c13652f9d0b30977c92ddfe27baa42670e1b44d74398ecbc0490865f7a1dd4554a465a51b5214be62f605700bcc8bc6f06a689f0c401e946960396be2e7c5873059685285d4ab1757dd99d7e5c8d7dadf00f82800f3ff2306f667f91277366a7659d5e16d252ddb23f16de5dee9625b3ffbf2df038c6f5659dc188ef3737f19779589eeb5a3f1d28cf4e8240260664b969bdaba27ded071fdb7b44af026e908f68d2d4f91268b251737f06b3be8940b51c4b61c74780d5daa95d772ae0b8931fbf347f2ffe4dd33bbd0947cac11413da1e5411075d480882477f90fa555a4e38ec121275ec851159a9ee6ca561db066dd1275e543864ec3a862d056871606f35cf3432077c7bc6cbcd97ede3f01dcfa69cb5a925d48168fcce624198d33e4d577e0de83c9043bd394368557e00570d37e1ff9dd41d8e0a45c5af72d96539fe1931c69e8edd07ff15424f1cba649a5c479145824535c8fe26294334a7a01310da06a43ff2b62a2e77b9e1da0d9a1fb0cecf87c732d00989c3f5720d9b53ca7a1c3a3913fd896feb2431ad1cc3928454f38cd423564b6a1b0acddd10be59ca3ac0c67edb8468867b07513c3657e47aa4c634cc2332242de34d64ef143504c69a1a31b9e4bf1856ad6c7b8440f4bd38c69d8b912a6131e85005668cbc49ba11cbca1e46dcfa754efbfa503254d0d3673c6381af2246c275900205a02fb8fe15a9bc9fc1b890ca072c7c5734eef7d23b6f24ceb39510cfaa874cda111cbfe0e77af492bba5befb8f8fa184a85b41fa9941839ca83d305"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"96c8e240337f1dd4713d9ffda144472f519d89842876a6a218e7431f1004664a","proof":"8c1cb0c520823c77ec1d987110ecdf195354abb357ce36c4c6437fe3871072015014c816bce291612f5d658a5d08d22cc34d98cc60ea43677da74f092860090c983d32cbd9b2f169965a4ff2cf15bfa73ba06b051254cf88c19b28941f6cb77f4e087367594c9a492dd75bb248427237829a25689cdf3e0ce35849218c82706ab006ddf5a5ed70e34944b90a6b5fc1b18417bc4ff2635f8465eeba94e4eafd0a3b8b85cf07cf0b045ecd0ebf39acacd8ca46cc254af14b6519ddf283dcc2d6011a1881a8140b23a78a60f17d01f65b3df8cd0b83487efa4173f6c1417c50d305408e2fb83378e5f413cd91fb94ce2e3c2466b717c0e8fd2737f5221483f1e71b90cfb2ad79e71ce6bc21a0bcd6e6e9e26404c8d4d06bf9edf5a071643678a91b6e7d031e17c474f389493bded16187f4d7b865e85fafc3988b1f46a0f3ee13173ace730e034f96a2fcd5694bdd94f8eb4f88b234bf8be12ac8d99ee319d62112027f0813b1fef7e9809890f9ea15c6b04fd8760ae91e840a1d6a2e9320cfdf37f629e4db7ff81356605321a98deea393f3812dff33c1a3b7337247e7e4108b0e8e02c88b1f93cf71d4a56124d8a44119bd2c9a850819af1969eb441c4e5d866102d6729e7441c513fb6ee576873a9a764128d9de50caa46eb56fe6503caf77006c22a62a8de67275b492be5c2b31a26e222b925a359307d2e9a7637618b47f297ebf2bc8c60ef196b6a0f1e77505c2783014c283412b38770dccf692c821bd5e4cd89ddf7c0851f473a1b978d12f92ba960df079cabae3f5493679bde0e39b3bfccc6941048fdf00b51cda45fd7f861324101a6680eb4e38fa48cc639c1de6503393bd2cff01fc3e1b4a2a16b2ab3bc57ad7358439eaa70b049bcd6b191d2d01229198be68cc9d6c017ab69c42e577d156df184a5605010396e78347d5ed7b06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8a4c30440ceebcf8097def8fa8d0c5ffc56b5387e71bd5670ab15753e5fcc716","proof":"6aa869b20191e01d01604f5332a7ddc19e88c82b79bc646427d88a7325022823c614d78bf8fb4779ae7316cf7ea5e9e533b0014b08f7500d76004e5d6535b509b8050ca34ef2e4ea7a6c44407694e14533f37a0a20d6aaf295f3ae0318ee42185ecd1b910dcffc465ccafb2d20be4d7bfcf625fcf5f6a639298a9f3d1c9088170c1d771010116fe498a4eed16bd66e34967418a50b9ecbf7154483350a2f140270043f34d51b798e9af498bd7587b03cd36db46e3bea13defc28f27566de300a5932d8e6c29fc49385457a8b7ef9c724bfd7b7ee9b6d48f984dbe434044a3104b0320e90e2eabedc8fb48c6abc7533971f072380b0aacd974c1c1621bbb55621ba87e21dbc192466deda39edfc2b8049a77c59694fa96ea538f6c9cdf737b344b27ab6f556ce90ed6d6391c4d4e2729fd245da1b56ad28a26ce2f252ec783d4bdeed3e7536083e223c9c38469c931b4bbdb7b3973f8a841b579076ed8e211579c6d54f68fffde08f6cecbaff2768b095569f76892221c3ed3603d0f078a0536d4046253b678a57df943476f293cebe4ca113c9726a57c69b30409345de37220a64a9a5a4596f8aeb238456c5ef692bf785f8be73c8a8b6714f9fc0d3bd65be7cc01f1bb62983f62e47212229a066bf76d2f283476148f9ab2c45e13fbf5d5b5da6dfb6217c07c3a2a6c3b629c892e497f8119b0043ac225a20607dd1f8260d427eb6f0171ea5121afaf95d6f62abf976e81fa74fdef24641e6ceb999eb364c42544c736510e53b6e0515f7bfda2dd449737c7143989c2b4093cef7bb7abc6154acf6e7e608838a5c8b37ea2328d8ec3dd88430bd770600f4fae012ce78cb621b174f106fcbd34a03e4b59fc61c9c51311fd0269640732e98e03a414ac51f290cfa941f4a81bed8d3ec11c4f9ade0f0a15e7aa5a460f4a194faebcffb52cb920e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"74bf69cfb14920b67f23308a658fe1fb6d345a6483e6a0287f6885044570bd28","proof":"9a523de7ad6b64c015584a7140c7ec13433b9db5fc9a6f0652d755da22d38538f2d47df16006d3b9e96174569866781b8c5f164cc8dcf1f7897a0f86bb95e60cf46acceb7ce1f387eb4483e0014c91162adf73df61031bde4ba0efb700e4832910fee2cca8fcd5d012f272db741873801ae4179f16b2639918ddadc25dac4919b6a505212adea678e4cc8098cdd712b7ce66335b5110a0aaec11ee05fc85490bc9f22e86179137aacbcc0af7a77ed57f6858d6b7bd18904e03e1650820acff0d15e7fedf92973a20066cfb109881ba2f0d3569d5196ccb05316be1c80c35ae0202fe2077c46b83fdaa8939255b3ff6d0db08ba1de5f78c733ed8692e55a2d90e38031b74bb890be8b8f54650382b650a46a2faafb7d24f54931836d88d9c947e6cfa7a0bd0d8938b99a40335da6dc7a00ba6226229a09f1a52d7135d905ad9394eeb2aea488c681fa749b4bfe381cc3eb4425b34379fcfb11ad12f5adae71830d85741ea4613ba50d8b55e04446fe2699ce74361b651fbf6d5c2da09b4dd5b243e788ca600424d8c23b0286cfeba0b2f3abcba7729673f15bc48c91fd45f740b929fce3c6ba0e9640bbfb637339de6941a4bd8f5f63829b3eb31a2eaa59d6b10165a68fda28c302a70e4b9e8b564dc123ed69a18dc06d90811adf2e0e57dc2714439e8d178fa445404d31627cb9e24c7d6d3c6c1cea47f53d3e5598bc10d766928236fe33d730081be2d19564ba757f8c38cc45762eb83c3e9d4c1fde28a791af66259d7d9a665d5a7e7278be3d53336c17644422e56cde7b88d65a6222e053cb83c96931868da85b99d08d808827ce10024cdd04275e97fbcd76032ccbc9528fbf5084a16c846da1d6f2f3cdf862b2b2dccb2087c5a93b7e326ccd347ca9e02692d5fb4fa26697cec139df303e6d48c0e7b77836a2df34cc44d0d7816b40f0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1ec7dd76abd19a7ee45b1a139a2d1dbedc7d95a3373fec6c1fee0bb962bbc06a","proof":"7e1aa878da029ba6c9832054b2e4ac2a5f2d3c03d8c7d1f80b930c2c797dac018689969fa6a870a189f0a145b7e7a0bbfbcba0a195ef06b0aa010940378d830e98faeacd7743c6dbd8dc0e888134e214b9ab250ab09f1edec917c26261845a7a7e3225603d91c6d192478965453a2a206714ad4b170b4c1002b0364257fd624d54288199e43cd96e6c0a6e7df2ebae28d0ce464476f2c4222b45974fae0fdf00fd7a2cff1cb7750361b4a582951c009f5b164a14ad654a35454716a1f7c1130387a7c4d2478db94f4d16cb20141e548ba3681a996c6e2e0da8519969d8a8ee0166756cae6d81198edeb64d2a3738605b4b4737d95241d7dc428ebd178bbf971f560c8512fb43217ef363ae8bbe87383db15b124a1e66ab00a4f72994690753169cd16eb320412c61ee1dffbf33fee4faf20d45b835858c4c8ff2faa1efe7f148cc7baccde0fc695e55c09f81adc4fd93bce07aeb7e0fa681111aa1cba5d0c52a007387ef48e16f42618bb76a922ff3b4ffc2fb5d3515e84c5b01b879fb4bc81dfc56eca4ed10a2da82f519ee270ff851e38b6b4c08e79a05e106baa72b4287109808477a6c02714e2908483303048ebf717c9a8718fc541f6450aee97b3df57526bf5b53370921cba2441cfc01d3140974c8bb0ba3b2003646637b5a1934f62a90381dc674182f49bc123e2fc47da9dde48708db54611d1655362ac4e2040d0660f68b2739459d1536c7b3f6cee58aa7a1047f81ba91e2c51f91b50a2609d767dc94bb1124c66f865fbe55828c037a262a446aa3758c5d91709c1e6b0649bd2ef01633de3febf8ca674dd93c4fcaac7983ea48f954364968e86fa8fb1c2e405984ee98b0349565b8f10782e1653c8f9f2e979c3fd1c3adbf20b877c8ed39e10f0a8fbfb28113223f3e09236475c7dad14e41cd929e2fe0af7e3b91e7d553250a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c2e1a2f0f141fe5466022d7b2e0ada4b531e558a3284acf9807bbc71f0a1896e","proof":"c243930e982e32f181187593dec562562ab961069ade3a47ae7290c21c76ef7ac83ae6ecef6562bd80a30cd199cf47719b9546bd3a97d6afc46d93aea64de22d74e9c09aa4d88d3f3b258f56a1f8adcaf7e3466463746432fd92318523263f4e62309ff57aa5684f4e690a9c9814696f224df97a2d4cd41ac8a7a3f3c981070e770f7a17b35f0c817686f4b17c9d0b8a86180d9a79d5bb90b6e37b37b1a53a0c9087acac07f5bd2c4b405b3be2814ebc993fdb8fc5d8969bce28c4ceb66e4d09af7bae3a1d88d6b07b85bdd721ba430ef5dd2abd9fff9ea9ab59f10730727306887384d76a248fdc6433e446324e1d664f38ea25bdc2f483ec7c962d10de633488a6f4271a5e99a983e1e5cfe87cc4a5b4c7fc6998c6461e1a4b9d645f9d9045f63f2384de1d9c6b3a1fa15aae47107a5c5ac3741ccd0a4abdd6cb59e69d77308ec584296c45642169cc87dee8a776daf48938b294e1d0fcfdde4eb0f756431546f0f9fa35183fb12cbe60fdf2934464f347b6ed0e77674d203aef195a43f4666ed542502c51d77c3e7d9be2ec2e0b6c6a75c30b1296840cfe375d645bb88069ec57c7da838b4aacf9a4a25631cf7d47baa324bb3cb393aca23944d68bdadc1f5805c3acb8c559d456837cac5f07074fb09794d3ae5acbb75904a3106b97d74c2e25e3e5cb792c3acd0c5b20c2a247b7ad4534ba72e263f73f365305163fc9613e8606707409d61bdf345a1ddcbb17f9d9b9a37f6a162bd95c7bf1ba14bfba325e4f8e873df78ec3f153d6aeaae31b5740ce7de841fc8938b42b49aca4ee471c885010fdd6ced1c15034bcda64cd5aa5dfcc7656c46a0c8a079eb1dafc432f05657106549b813155078a7ffed3c319de9c16e84cb06e39b7b195a27c6717c902b81a8c3b9e524798eec22340ae154e607abefe112865de2e0bfa7f8405d62903"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f853ee327f398798cd0ab7e6d5e71badbecc53aaaf4eccc1b056d1d50958915f","proof":"ae8377bdbe1c30fa1088c637d18c54f50f5a8e3a6f43778d7987fc5579416c4608c2d862560f984dab3e716fb3dc9e165d29f725eeec5f37253d6bfc589ba07a5c77893ef3c8d3d5c68cae9f25e87fe63466d0d59d471fd24d86595b8f93170088263bb98235fbbc876ca3e86bf15072ae21867e6ca3500e114548db17b0dd65cab7f1a4771e83907b1c128bc368ff2034c102efe71390a84b704f73de561d0c029078e3519e1e60a056b75a8ba615ff0fd3d49d56b901cf1322939ebd126d0904eca0a51ee820661680fdd4750fe94b9d26b6e49f9f76283dfb524e0426280b38fe6442747ef24984dd60494f6a8d012a1ef17a020755de188da4f6f05e7d197cd0cf2ec72f4fbd2e4204670527c0cf6c41ac86ee00333ddb2246e95593c1660c45f89ae5335e5d19518cc91dcdbc0c3347452833897e1e2074de4406181d432e23d8e5264f1844dca5b5981c64121d050386846edf080e100a555756f40f134673f5f39e7b7d8761a5913709671406414b4ac15592d0334112b53367f84f252c4d9a4d47f6dabd540c78888c94b4906835eed0b9dcd0d1d7663586208eba47d42809dece15e541adfae751081e24cb5760ca2fdfa9a9a62faf9d0ad06de76e969d3999d5f67dd79eacd7ec0ed34f97ad1afc7a74824d67790eb4dac3d71f7ad0ab82e897393dbb38db5c40e8460b406ca04673130f52fa26be74730c7b4e18d2ba8872ad6928fd8b456f41525496706a8d0a7e6607feeb5709070c1bdb11074424a8aaf957f12d631e9bb26cac5a660c1d569b009641087ef749f1d946e27338df93d6b2547d1e377bef9413cbb9f93a383c93ef55ef4e2e7d536e41f6532b7cdc5150409de6a69c87c9af7c9bc282db56a6ab65a77de781c9ab067865960a0acba1336ec3ed42a96c116eb415b9cc5175c899529d8e87ea809d85d1392d09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"86c5df4b1ed03677165874944596351c6fde157406b8a3d6641ee344b0d4b912","proof":"10c7dfa735f849b6473c215da0825be92324743349fbda853607ca5ee4f8e4769a3276684b14c82f09a51ab662e9b824dc702e4e6b46219179babd6c8ae6fb00f0fc5338437da4ec87ce02f99dd3b927d88f6d32b6ec40fdc6581f544ae9517692b75d2daf422f3c2e2d515b7796ac0d042eeef94b3baa1b86f48ab46fb9181634942e39f736f5b764279235609334dc2bf89354b0d0afeb0aa40d239b8e8c0a37247a460cbac2420419e7af137b1ca2b4536f2e105f14225c41ba752865dd089c6d6310aa6b806a4adc304449187f76e9d36ece0b795252e56ab5953528030970befd37c88ddbdc8e683b2801cc95cf00c9f240cd95441aad542909fdd7f67c980c6c3fc5d641bfbea847ed46fabaafbe2013a9c4712c3426341375c5c8835f4646855ca8b1142e9e31db8e530ec38b13ff997ccfdf6b2d3cd2654bc4740a5cf6b30c2a4cc0b7f17784894c3babd284db8b9284ceb7a3ceba52af6d5f20647b4ea48f7d250a1b5d53c6a0148231500fd8d6f49cb22b1f670b0d06b7861be142ea8eb8e6963ea9a368ed7805090aa08237dffd3629ea5997e59597b961bea942b8a69327ec056c43e6fb58e9957486e46b8f92f191334a7b7ad9556d80438706e0f7ddea5c4d5cc3f4aa21e9f3d711aa8c346c9ea422c20cc76a55ea0857f505d6d5b3b596970ee40dabe163a61c78012694ccf0ce7eb0b5481ee3dd0e777e4ee68b7b6ed90ce7dad77bdd4bb94044831f2f16b743fa570e68a6d7eef7a7e90a8e50c7fb9b4512640559cdd9cfcae4c87e72e1da23c92bcdf414b4900868362cc4d1b2b8a758fc9d17bcabb902df409412b52e57c3459b4899cd1d608dc56769f193244b719ff25ec99652d1fcac256bb9132e1738316715e309644da6b5a903f051f4dfef2f2d157a872ba30bde02105f280df7d7d10fba22c4795281a2e30a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2c65f9415815b10936eb34c744cc4c8d11f7b7ae0b7c193a617366a5c0c5bf7d","proof":"4a94419858394c69d437d13327634a0365927dbee25c7cb2c151d7d16d4f69125014ac038587786be6d235440b3a1188d67a1759512379ae6793eae4d6a8604bd8fd8b0be4c10d8482b2e1c3349a144a8ba6bb43711c653da153ea2c41863c7d6ab1e28b8286e0c7397d274f1d8b964bef60174e1b41e386ac470ffcb4c8d86c0b033fb87b48369e8713a3b2450674b2fe2dbdf12f42e40b9692d61c006fd70d06590d39e3462caee600a2a59602379e10f458087825494ea885146db3ed780059bbacaaf1643d4c8788b4311c54618c333a4ef3eabd7e453bd99290f7a2b800f2ac8ae8263a262c9191ca11b6f4f4689fb63165ea531c4eed4d0b7b047f760fda8441c8611f9a3d42d941c47549d3e6d2c7ccc8a307325178ee7e0ae5121b632cd4bc4f55a848482bb7d74b22298f754a6e7209a278bd4657013bf163a84a7f489e27b30e695afdf5249a9b9e6eacbf597ea164e19ff2cedc48cf27150ce6168648f064b8de55082b9b94e964d2ea46a16c191ab2dc9e3bf207aa6a7c9095192e5c5e18c645c7f94f309d3dfe44b84eb2622cd877a109d57f74046a12f95735c0d70e636f4fab3f28458b750bd0b7f020625e4f0d991f9e3d904e416619b629dcf150963aabc64069a50ff396ec1a62ac113b98f10867db37efb10787228679364cb42b2ea101c1824f95e3ae77323a09974e41f1c47994794949c6bc7ca50456c0b8c54f6f0e9770c28ec475a38b1da1c83e4229aaa0e4509a4cc940e3a73cb0bb0c1b364f792719c2613a1579733e2d6226f7d654887601903329dd25b729f6558f854ab27dbb245591c7439a3c89c2037f9029a7ca774fecedf90f6dbd52f7ba186d64a31ce287d9197f1cd740e47f0accdf3bbdf4a30dca126fe63baa0266b4fb76fb897052885503b1cf17ede1fbd26e5c5e75ca6ec0dd64c09b405406"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"16e96de2d6709f754a2b9d18f4b903abac078e24b261e90278dacd8a7c41b968","proof":"f2f5252194b6f6cc690e8e5e3fa4a6b37f63bb541126a15c3818587787a1667e98d2f47d3a57d9a693dadf8290ba5c5e283c4f72d506dbeaac5561dda0d20950ec2abad19d7ff037a5cd2a7be88de63b0cf09f8ef7e4cd1991e61a48c6f41d01c86fee19e02b6d4e2725a5811b1d015d493d244bffb2d9e335ae1ce26aaebd72d597d1826a3d1dd64b8095dee0bdd01d7a0126e0fb1b59f5015f16233a0fd8044792a5ca1035efa908f5d893797298d479bf1c0f0d444b310fc301bbb6245f0c1d5195227ac15a01d3069991cddf906c0653bb76da6b11f8d2b15d8624d8a30e6605c7a76b2b1e7f9e0c235db679cbc055722196c7a4a9f91a9514f0e05f253e14e5083cf4bf86147c5c8060e8e4940a0c96478c8f77a1594b77077362e5de086efa74a9330a97097a06c386e1193fff21fc50c2382e6178c191f22032e9157d583186c7fdaf99bc6088a4885fc806a85f5c6116f7aad757c10bc10fe762544e001307209f1f30ace8c712d705ae1b33113089b8b9b733a5704fa294a7674f6afcedd910c3d18bd35601a59383026549a68218f173202777d876b5f89d05354d82c075172738ff4810ae4ef3f5880ad02dc791f59db04bac54dabbddbe2942253c3e00eaff9fd4deb25d64f13c5f1b486d324745c878680cb232c84b71ebb2639600055ea32d5972152bb02d518676d1916096638f94362bb66152cb73ec0f439c0f197519fa16517969f70a5c634b21ef72b68bcb852a52d61152a50e7b125550d371bf1d128183d0b20737341da55c33f1256ee14b0ada41ab838d1e5b314bf064d30fe4b73a75eb059a89991724abcb25b864ec3ef3c3bd988397be78b65ccc94d66a8c0c053d6399e8da0c62474247d127d5edb4b5e7bccd19e4e6b28409de5abe050932064fe59d04689775a9e356227ea1a95d61d1bbb7828736fe4305"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ac6768f4567d4e3d1de40d5b46f017c7e6b96c5d3fcdc1977f029a959ec7ae39","proof":"0213c0a802b87bfe51b0603a2481ceeaf8ca199cfdc52ab50fc2ba83a071064a543ed9acd797f8ec9f1f05a17186000021397380ae432a4e1ce207d4ecb4e53378c21bc9ce13d2757cde92e27a4575da20f63783eb4bbf6c94cb1ce20656d235c6b016b823a21a1745f32ad06353cae0a175c6afeb84baca85b45f9f53c10e129d14b33677e8a6b148a0c42e8d43be871088c33d3d85189de03dbbaf47bf9c0f92ae3a863cabe80ee30d6c4ac91a6323c8f1b59c7b07755e84beed5c8d0ae00251a40a883131700c4a75011b426d4f65f79480176a2f621040659303b2ec8b075a0046506985e3934ce8694266346946ad7449526c35fabb7f114bcb9261b00620c2be513949f813d7c480399abf9723087b017ca60f8b0fab3168d50a018618f432c0068b92d7a0fdd01c05a19d8f3f8fd1a1e0af42d3cbeae3740249fbf35702e83961a1a091ce3ec527f9b16076d1d7452cb889640b0d1201daca9e2588178ab46dceb3308f8b5f231c8ec2f58f4605c42f35e720a91d058ce857a751817fe83afa93c81144cce577d3f869279a3e9402e1606414e7af9a4c159ff237657a524c762bcfe1a901ca56f91c53d3aaad6af97a73ae7d96748e1d19ade2ab91464419115c7fdbf250eefb741870fa5debff3efd6747334201e812a8936d1fdc4e1eb5a44e88aa158ed9227f3b1a75e1c82a81fc58cc5df66a06d3b7290873301a447198b441daca9f172feef5af472ae5b8fe17777e5f745a3ab05c057e6be61b12e2e8b2f24a4682faae8b6092526a8e0ac227d08ea3ca87a8b2e620ed1514396ec8a2670c3e1a0408428b4e423da07e788b9eb9a2e73e765d3466340c36b96174a08fdfd3791849f19612b11f0b4ba02cd231e97c8cb3e5c3feaff0026e9506433efeda490e5c8558b525fb825309c0444035bb027b9516e23f53d10c32190b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"88943ed84210a3caf09ac7a3d5bcabed0e419eac4017a797c52ca89cb9a82a61","proof":"f243ea407119a29093133e91ec25a68102c38f10b32f86306d9d711e2eb0286e92a2fbdcc98dc6df5c727319fae8af0c89731649c20ae555dfc1c66552f6ef162af69977109ec74cf0f77c49d3f49c9f325b557ed46ff199cc3b787ef81e1a3e6c03e31aa4d0b793a07127a0bf9e78a771bbcc7d86da839dd4d93703f3d8fd5ac54eae2bfe2792b8d92e789f002499a8e004823052ed6c04c60bf31c9f71f502339d4fa6947622978b8d942fa40093d7fc78e5ae3c0cdb69b8e407381249330e497a55ae9515a17f9fe38b975a0204605ff56dbe89c983f73c5ed4791f45870d06514cd74ce0551ade3a9d7c8d1c5551f49b70665c0aadf1d6c582ed4d336d11563873eb6532b608a173e95f8fe3ea9784977ac865fccf356182c41777a8061a96a70397660f91f1838977ba6a97801fdc3b20a3e8c32b364b6a8d44c167785d1a42a2a8931b57ac26c9ea051576ba44d25331cfbd4fc97a8f9d90baebf9172a3a647fb78b977304f0796fec08112e9139d41272f62acb99a21f31e5e0fb4971b6d6a9eb9e96813b01baabc012d0ce0e25ee55965e072b20d16353da22caa83a4c9b502ea05c53771f5406ed77851544f693d6cc9c5ecea3a9aa1c8559f4e8609c6ce3f66d0ff416c35e979c35841439a3f42a4ba7497ffe3ecc2e2df8ff23318a45c6364527c167a20611a3d873e4a258bae5a04ba9c8a89e41b01f66e3b74686f8f966dde9b5b1df18842e4a710e7439c02926c106bcee5276e7a4d1523b1080d40b345944ba8b3eaf7727fa8d0c51195e9eb3b84044e9c3466b7eeed5277f8239540741e28c5be1e1f393afcf87667f4e7004c181c0b0f3af2492cf6f590320f65e28ddfa9da2487276bbafac18c8f0e87711fc34ddd3af0a88a5cc9de10da815cd061cbefb36261eab0153feea1472d9ab164ebed4a900cbb2c6c58c8106"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d4bd7ae5430807e9c5dc5c1add227dcd13ced8aa4dba33814a8f2f85f79eb545","proof":"e25c12b6ef6705388f6dfb99f1b2e53d37a99598139b5ad15117ffa128b2ff1c4e616874b1d1169a90bcae8edf7181fe33d75a5f4cd71bf68a218865be6dd76ffa7f87a72e0a224c2f1db17d3219667cf92262e07a5adf12a7b3c7f6dd34a344e08553701a51f5af3f7eaa087c4aa42bb6e3f2403c8920f7e14287d9b6f39a682e78ac622b07cfc4f7e186b839fd2d6854de5b769e6a020651960024626d210056a49c1304ef5ce2a1c2039976e0213a676c3a1b016b709fee83ebbe5bac040c46e8d3a84917236ab5f3ed77e6d3e8f703e68bf63cad0d2b4e3b8dc7edf74f00ec3a89f1ec5adb9dce395895588b412e1ea16ebc758f337976fd4098741e7a41c80c7762914289f637539509e90fc280b4670aebbefb7a85e269ba7906411940b6532c0b0d0a09fea436d85f2c8d3f25e8e406ce41b2585379d2a207a2b4c01276a16e52f57a6fe13dc204a0185b1894ab35f973ef30aca7ba4208ea9db05073623a0d4128c4ee91cde6159294d753c8272ae33af7b0d4f7f2af8b91ae1df035906a2902084e7f50e863cfc8afb6cb228048c801aa3775bae6a751ce59ddd700e0302deccd861154bb775f04f307084c92186b3d24b44ee2a33b84d08edb8e2da4fc39a577634bc930e241dca706013fee8e4afa155fc5151714355a35763373c68cb9b194ad2ee3e9324dcfc5447e4a0f1ec0602add27904598f555ac256b3220b092211dabef524d91bb1bdcbaf6acf1de6e57d1e8a524a0bbd0f9e402ed1b8af878c00e3716553106a1c9b64f6da44bc97b87af3a157c6a0b38f05101cd11dacd90ce42b712155cf0e7f037c6bd8e9481b783f61d03df6745bedb6d815d379ff99c1914b0a5320f2dc4cfc1c74d60397187850f88d69b2e3e8dbabae2880309a8b453af215265a8369fb5f620487ee3ffa0c5cdf85c58153c6109198fcc0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aa6bcca90fd2c47719422818a6111236fad45a464103dc7224c8625e9edbd01d","proof":"5e7ce1f3236c520a370c5dd9487a87f8e772467aff52d079496fccfac2908e13b6a91d71181ec7e133c3819d6c763a22b2193aac99250f4c2608bb4617f0df25e28b860b7ebfcb15f000f104ac9c6fc65ecf7efefd28d2bbeb5089884bf3922eca20b4b8439bda9f2c7969fdbd4349060648e0a91a4c2454489e76a193ad60545497f1a31612c6a0c2b1165e027290447c9da484b061d303f80d557e3cebf50038340a0b23f4f591dd853559285d03b3481cdae54014812bcfdbe675d861070a87f43bd6cb2eb8274bc4e54323af19cd4b82054a82e568c9f8b7d9fd398bfa07b0afef7c04e08228c4a5e9ddf16553a3feab8a25e2d416c78adee4197f98b513bca95028dd29890a6f41829705f352398f2030bbd74b8c26e9451f792ce83b61b4e5d4bd6062c8770cc769c92a3df9846bcaef2a331396226eeb21bc94c62035ea4c90d0950fc5a0feffc6966abc7d1000c5dbee3682f096a08e39218a060000660950844e07330ce10c5683b8ab49ffb7fe35660de668321bdc3a3f2086b5458a468fb77354262c024e06f9fd0d563cccf9c5316fa43bf886c70ebdda7822569c812f3cb3076cd6f781805bb70962687661b55627723b77ae83e72e2dadfc7cd616d6a7208ac2907edffce8581273961ebeb780d4a6340dea6f45dbf3d50e404c933c672203c4825de5ba1e44f871e1cb3e8f18171b4f00d39a3a7dc942143a18b6510f8603061431f9d30cd6679d4e709e4d20c5baa8a378a91a29427d01678e7f02f993b0ff823737ab497aac20432df0df9b1d26eb24f5c342b007541544c8e355fec2989680a3278ef97239513e3e45a6237572891e4f057613200a1830d182679dbad00965920ec119f8f651812371052732a19dbd9efe4f4efa5ee7048aa74ce4651a7c55cc4443a7fbf01586954e0db0dbad77da71bd12b67846f20e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5639bd1bc6fee53d077b89399012266dd1bd2b653582ed29ddea208190870f21","proof":"1e5857f8bdae0f6a30e6632990c82b9bd63dd8709f610640b4a086d87d825d67d62176d3505b75767c84a149fa710a7434b1511d3d519d2464dc43a3202cb15c9a07f3f9dd7c09f10125181949ef13a46a4a2a7b5b2c212c91dab4d49087575f0ecd17396c7edb00a8bd7c2700732895885ceb8b83732cdef7d04da219318356821d0126e1d638b9f6f128767c24e79d11596934d602b4b67389ea6ef4b49d0e38d7a0f05a1d0dc17a685e77e1d3b7d860b12092800956107008609141a40b0f68c2d4bcadd53cfc2ce09b25b8f766a77c73dc920991e7f2e948e3bfacd2f9062490874e23d2e589d143b7afaf784a6c33624dd0b940a1a7a8194e117ba7c96780f35d388535ba72233086de623c06fcf625047330e19208adfbebdad20b1f06ca2da7d2b1c26c02c04550e3877ca8dee82f6a4b58c8be445b661c4dbd138d7a2615a11e4d55b5886870d325bd4b4d6676cf3ea4d37deec798ea3ed1552fb30ca65f24bf2d51f1f6774798345cdeb587720adf39fd7385e1e19e740a4561450d20b3106b07fbbd99fac9b1dc4ab4d0c036451918d2a6c8a76364ef0b11efe53092e6927b1460081ec427f76821b79fecfce7dede5aba6096a951168da675f70676573cabf8b0f23031b85a8df95b087f0cdf19e5fd015c7d17d1a1223c2b4a4b924676f73d50f9c0040a137df4a8aa902bcddf1cb2d39e052072c1533c7fb52f8adf7780e8bd6034d2ac8f69f62c4645e96494ccfdd613264e0525845f320e145e71a33cf669314bb977bd12cc79da1d5cdf347276883cde2f48a5921bf08b4afc8e08ba7c27985b9f446080e07d8188991cc87ed4c4485ff92babdfccc3093631a2112b8fca8544bf13ec9c117a14c7954fa6b37837571c6ccbea8ab753b503ff640fc52d48faabbc55a30173aedd5de5191f8ab655583eee9cb48752f60504"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"487035fd9eaf8cfeb8ce597ce24ed88098a752ceb89eba0da3f265b4a4d1da24","proof":"66f9f627d5e9368b9e3dde1d68330961c46f3174d6ebc0c1c58d829a28e9246874845b78a5f640f37b29a2255ba59a274a1e74b901e3617dd1d35ffa4cd3b942fce866fa16dbd52ebcd729a46a391dd3e961a498d83e29fed758c73b8d6fca4a02d348818f5c86b4192b068e7cce3016e3a93ef19db538c8f66ce533127a1f43f2d62fe9fb7c3cb5c3afd0d488c14c699478891356f650422944479e49aae70b002a06ba5dbf194191ac9a5b896614bb0cbfcc2e9751ce40f84baa9727645a037495ea813a1adb28b2aae3533eed78e2022ee2cf5e08a8935b4ddf9ca7ee9e09f8b62654e4598cd87bf08f45ea29d7b96fe515ef66e37ffbfbcb8ae5d3ec945a949bb0631ff590de16b7d1c6c67ffc7b38ac7e2f739cfab9ec3412972f2d852742fa5d72177f21374eed71646b301ccdba3e68b51771041780e66fbb26a82871c66c91831c955ca3789a60a4286912ae6207a4109ef11c8195c75ffc29db9557f2ac8bf1fbd9831f3781765f9ab9f1d56e628b0f6690f767e49c2b4d297d974974bf0cbe12a20da1d57335df84b2aeda7889a5b44ebff65d65fd004ef3ccfa667425b16fd985ab790bdf8e0c0208bb73afc01b0a8fb33f74be60158caabbc248d615d3cdd69e1ead9448256f8a37e1158d9c72bbd2ac9c20f51622a621b75e6420077df469a663615a05c6d8bbc47bee367576a462cf5e0490f3e2009a8ed075fe3a0ab416600def09306f06d2b074ca005998d4afb2840124bbbc36be3c15537cdd4fc2613512993cf16ac96af6186f691990bbcbd1701e922496314198e8484ead101bf162e979bcdc92b0720f849b55441087476e0997011950086174d932cb13db10bc221472a6d66e1839cc35bb5d974e4e5a9a95d288ec7ad52986b608d8a37f6f1284fc9bd5d67f96cba2082f2c71e82b093b35cb6d3e025e16bc250d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a4283618f9b4228bbc616b09dc17ff31162027fb71a7a0dc9b5f4a0b8f093b32","proof":"b45819a6f3a9e6e9d319fa8b5504b55cb6963553f4b9222df77aadaecbf2904fd450c57c9ed818a420a139c69fc4a9141b9170243295bb7a59d74fef2e076e7ab2faea2de0222324fa314ca091a8871842406553dc551c9bc2b4a57cb429532f829c691e6a61950cd9b11c048059f0101f925fd78dfbc3958f28cbed94474e7c397bd87fb2557c22e88acb3d5874875ee9257804719b313c7b5ef0d0adf4c00a0aca94c06d41d7a0ad672838385f51865f820e1331bbe41d4c2d1e849c7231047a336b246739b4dee278f387ab7b53ab47430346cd7ec474a948bbf3ad26b0044e3524539d061526ac4edb0f3467ca9d5170a4d355a2fe7de48ee12189e0cf4cfca76714f855a5e16237bea2afdf2f211135fd9bb5a975f5d1c9c189cdf0274e686bd79db8195bd103e30e6daa131ecf9084fc9b57cd6f63f830a580b5bca2672ea7412bbc1563ee9edd826ed3b3d68b93c5e92c26c742e6dd42783a210d8619dc61e66c595b4daf8f768562eac9bd31d56300e3e23ef35b3c888bc16337af6e1e8a7e2db6ff938d54655f2adb87a3c01be36a029e8987f5feb83a29bef56e5b729f1e97f119e9c07a12139e141b8850391ee3f13ddca4f8ca828e9200e2112572c1b3cae7501e1ab1ba45b24ea5250d7e51fa39986fa315bc53619dbe201e78f838c5e27b090fbf70e30df853cbb290657a20f2fa534916e60552294cdc7c7fa0a6ad7c45dfd6f8a0d82564e201b30e9e3d8a9998291b206398efb131aea6132cf488d4de7b72f5fb20cce304798470c1cc6d96a2889d82f2b667ec74393d177c8050d1623988e83e786a10c4a70627931d9520f454679d7f5068cde428675886e6d0f9d3731956733b7b6911fa1de81f398d51110a433f6326b477f0c47a0ce7bb25511cd7ad8eb38494273c9498d835e0c19867a972e8373d56052872e400"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2e27b80a80e500492d769c63181a0f2c1eff57929af535eee28b9d0f50f1816e","proof":"c65fde819f8bdebbbf56a2c0025b6453f5d02f47b3e56076919a46dade8b9401c43ea2e2f62a7bb85f8b9f0dffc21a7f6aadf34eca2ed3ebc82e755f94f10c1730dd037a2d8a61f000bcbcc679a269a2ac5f4e1c2e19c5f2b66b1c7b7a12b215081b25062b1b908ed037b5a2e4eae838f36cbd90895d18c1ecf68665b8ca7540190e5cda1d1bd843328e07270fde15f50764403d8629172db3d95cd450a0890ff002fa4c81513c9503f9b2200fd6270797d7ef3ce5e0c43e5c5598faf22ee80a3f9b7e4ef7fc370132e33ad032c47ac522a3d23b155b3fef2e2b460a890cbf0a00dd04db20a501a7c91cdcd4236b2b47b93247d416e8ddb83d0e725e6138f20ee81fa2ca408ee4728168911cc1aab60b44004f0faa574e671cfc3bd051260f4ad2af2959611fb9fc469c1802e25c3816bc2ca3590a753963453e78387f34d670b2629c6934c517c34b779fde1ae065644d08aa442b91988546cd036ed57d5f67fe34784f0c17609e4b36176db7a2ee38c383effb30adf19fdb2b051825b84d5f50a763a587489d9233b64c13be0bc95b1bb42a058343e029ac2e215b9034f661d00af1b7361ea2be4c8c8032946390aa14af4f25272d24fe9777343cbee84947ce36108a28a5def0f91c079be12ac04e4ffc51341309dfb08ed9374a424abf342a196c0aa62e4fbd157eaaddecc08012243905b21a4beaa8ce1b9f0cd0f62a359ac61c2656dc296dc6382c8c9ca98bdf67a851e89645d33947ef4c9006147f25ba997c51ca514b13c8fc29adc547488e59f6c1ced7e40526b9340ec4afa3e965d2650d241d7ab3fbe6bd73c4f427961e018b0734a0b48dd013ae81d600e6615b6d4ef9bf9ce8d6f6b223befe0c11cffaa13e028c0dd6274abd968b19f0c7740a5e324e7408cb4abc2b87f6c422c46f5dbaa84b32de6dc7b6a71bcca2c1e8830d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7a122d3653a47f191dc16967acce20d97a824b7190d0b7eb77a4e5b6cef26919","proof":"e67bf03372d2d1b4b265e2a3066d20e52479b31a5f764ea289ef5ab62a47295492ae7701ae061229e496755427703328fddb482ed960c2378b56119dc43d1b44306cd489516304623d04d94437b3b68344bbd3d3190f5cf78754ef6c04a3ec6348c72220fb107a279e99077d86d5a58e8afa5ed820ede1b4aee45a036ec6a933309a4decdecebafcb44a4e18420393a484c9d00326a1ff82a5e3eea0f07be00fd389c6749e717a719a946e150f38e417234b2758a36b3e5e7e39244a7f8607068667b678ec324a868786d190b3ec3e4b1b35ede70be95c9afc343580ff6d970e0c7cf14ba665f875ecd2f5921a81a15601a466522b55a51854cbc9f76de16143340aade18f88c074063e9f64f270bd2201b1c8005c21726c76ccb33bbddc8f05e60c2131b6eab50d03ea12a4699b383a76a26b9ca978632cf3a19e6bf036ae18a87271865d3fc4f4bf9aaaf02e700c0c8ff13641ab3a8a2b2b9ed161dd8fb93d3ef4951e8999947b0a16e2e9f5c6ff2e3fea7adf34580bc8d9ca20063b8828645c7be2f218c6afb52fde73eea1ce635791a7d4d87202539ac11137715f5f3b364289d689849fb89c5655aa5e89688f243e64959ab1a4d7e391f94972f445ff0820b9d02730c171126c5b721f820636c8a26e8c4adcdc5e52101a8c790f277f70f4c181d83ce3bc2a32b4ffe0c04725e0623aca1a3b98bc32b32cf434fd3d834a96dda31b33d6f393b84cff2abf957cf2a08b9a9883381d8903212c4fe613ac093ac3968e64030c38c15424d47593e0e7ca689fecfc9eb32472e516b74bcc9570169abcf50d233c52cf84b16526ad9f68f09e0635d6bcd8c1fe5352807367912990125ec21da22612b7bb4af4d64b874c9753bd98b45e848dbd95e724a26ce60c2cad16781684dd478aa229c000e6c1c8e55b67dd3ea02923fe809a000ca2d00f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3ce6fd19ab155523bfe4fed6851777e9ce82f2746f3722fabe387c009b9e4924","proof":"1a8c8552a377efc563de3183c2732d01874c8c9f0b5a8e8c4f1beeb413d8e63bd8d1e0385f618718696f728610dceb9140657652e1e2a487cf28b1ecfd857c0b0edcc6e7e2bf84b4d066737cba59b2fc7f2200b3b117cad200fd6e03fc0c6a3e8819324ce37cea017f1db9daefe18a17397b3bbce6c684ff7fee4f6b89388319b4cd467b081d4cbff299e5e4e89c9bb56f90ec2946f46af28b847f88739dc50a39a62590ade6e32d304389abb0bab5282f0b1d002206bf25d2fda44f71de070c4cb4122dbbbb9ac1cd9899541618aba97c4b109f25e7c837da178abf2a39010ea220ff839c2362a431ad241792109e5a03ec07d5b87c82881b0972f8216e773dbccc8409cede2c093f06a7e7d805974e1e3a0c3e42572e146522e9eca4958509da57a6cd0267e0ff88d08d2625db20a4da229476c4ebf4fe7aed83dc4fc82e78fca5940837ffdbbd635ddbd6c9b36f844bb3f363c38c95666faa11c65addd109d48b0ea408300e6d914a77242c82d02de66310f4cc5d38a3b3392b9648da1253ee7279abd58031c44e0f44652a10fdb0dcc3bce807d260b3477da9742c38eb3294202dd1651a253ae2fd1187b636e0bfa954d433e4f6701a25d57934ebd8ed658eacdbda70a381219ccb5ec51c74adcecf43d8040667ba11aa3180d43c924046922dced4282718a368dc724bc57ff30bbf9d735ad8e800febf4ab38497282c01f85c04db7fe62b500d5cc745c77f0687030ddff7167761096e59ba57b3aa24323e04bd39af94dacda8bb0d6c6862339f168331bd6bfa73e0cec7205dcd35d9243e894d3aa834f36f05e5799b6b46d32bc0680de08da6bf74d38c58f7f1c036090c7fa672cd9a02ff3022945a4642fab5691bc00bf2e2f58a5403714e74ca8506502cd9f6e1a7f6364b116799c14c71012d426aef1c89a6be6433a6055b360902"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"204c363d09e787a8febba35a1542151b160cb87ba40a02e95c6325beb38a334f","proof":"0ad2ee537e09d24db263e030b02471dbef84c9fbaf80c406d5e9a4ea3ea87168f4f1df03b06cd404f4dbf8a80da56b9c0886b0ff194b7bfde67de5c58cc2d963c01b44ed4489f35f29d77abaace23b8e730dea641555ca107c70b18b9df9560922fd115bbd4ba2e4eb778ac9fb736abfeea8f462c8183b5e5819f7a5e4482f734aca09fa069278f3c28cdfd1ce0c679c379ee081ab4097f1c4986ab7d723b8042994c5b493815a95b6ac82e56808eefb25c0b4fe0f64f4c4c2d2256d0d0bec060e4c968f3294d7cdfedb23179b376a7675567814674bea93b102203e91696f0c4a90fd5f226bfd7aa3601832306b8af9221b9c5a420593ef91d3194036a83e567656531ec82142c073ab9f82048ea0f7c59def5fb7573c443d5e07ae972e101a4a0303dbce3203a441e8be705741479a1d9c558ff6ef0eb38cf7e44b89a07d45c45c2be8116787e2709cfef37cf24d0383957585923afdabb110af67b7bcb17a1aff6ad79234c1fc57a8992ad9021300252ec0de484d8c481e4072e5a2278851c69a502cbca17a1abae19186166e10874f10f51ec48a1ebb5ccae1fd53269549e0b916932ac993cb5af8385433088580bfb0cf13018dc0be8d9fdc27660e3f46d20c7acc91cf553bd5b9e1bc1a9fcda51ca56a06d718433ba80d41ab83cd35250ae3ff09f090ff8a7b31539cf5ee4f12c42f41ea21763894d0e16ab5f6dcee6d8a2940d431ea31d1df33d4c883392f3338333b85bac9c67bde306e262f9fb86a5c37c9c93c6cfd1c7a41a8ca0a5f2e37b7af3cc5e41394c9af7eacc0e4780353ae7a2482ab5acb0b1d7cf81bd70de84a6d6c84918bb38c8dbbf88a0481045465fdf98a3185868f25f852f1008ae73ccc229112e90a2a03fc1f23753c8e06a601e2d797e10e85256be7da8ea4a3a61a509c1108bd7e7949418d274cd3f49f0c02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"226f95a2a678718a44bc7fddcc625247d78910423d0227f9a32542c2dbdbfa47","proof":"60c3254b72ee7316f99b129395ad51568f4b3e6ad2f3890c966def0315921a08c485ce9606b3e97e2f5fa6a9691f318085591b429c572bc762c5c1c8214d6b4edebf6a09f6d4f85159a4eadf657b9c1e02c73cd8c31cec34a60d907889fc2e305c1034c7a1dd554dd92aab469e45ce18e279ed34328e2c5ae103da8cafa0194e163035ecf5faaaf58857a8a27d8b696be9e353d57724e72c0be40488d2d9ec0566b0eea29a71072afd57207566c7fa35fbeac9d0f1b4d22a59f9c4c89cee090e56bc9168e1f2135b517634699413d5fd06c8fa15a11b7abee28e7601bbecbf00ec67775eeef63a720b0421f8888a7f9a6ec6b7950822919c74102cfb64a9b761a6732c9c6ea5878c5ceca754608753496947022188643b7bda45bda5299c3762d29b29fbfd07548da675d3e7b3c34935178ecc1e80dfbf04ecaabbcb3c4fb10d4a236298f773e30f339223f0066c43280ae31f6d8d39d7c8ab36a2451dc0125e4c62e69dfa7d37abf8c6de01777dcdf1577206ac923bfbf9bf501f2732509648129fb33ed65dbc0b030f6d669a46e300b9c41709c34f6f151d8d6c2fc881ad0d2cb6e2593c174fc2622ce8284dec9dfc7faa2c4dfa288f1d75eb5a5f881fac369a8af83d958a91ff7d627bab6c194df03a240604740b5a0fec90dd3130a1034da20609d5cb64e5a60f788f0437528cdd87a2c04341482676e65c2b8860a37463e61d4eb1e3b2573daef3136b89f703227883b64273474ce4bb0442f5d0099b18c2e77023ae658dd6c55f058419deaf4ea55504b2039b4db0b4874e47bf8c844a8298c87f2d7d488922ce2c0dd7c79c7aad58701759b22f3feeeeecb59370f1418a37a17046abf1068de5c4e115b8cfe2fe05293b05d3d7ad82cce608afffb805ba9be25f6d0e433efb2412e599e2dc3450ac960ca2907105059c5e869de37a0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"18e3c5092430414ba2c7b81bf66571894e285820c3b004655c0d0a6865305900","proof":"004c3a1d6376c4dd566c10eef4850a8089b3b0e3fc5c42d7d305ca22e04f303c74d2e4937e16c77d28f759c6beb5d4ead150663d5d1fc2a0c665e7312ea0564b1c89a0b07bc259b713b99d3decb2949abf4297eeca5ce85d4a5f4acbe993e52106b154fd2c00707062f16735e1050ead0b14f56c755e042a964043542be068176c954562020e296cd823bc11daeb4f0e79499fa1fc6a0b49e9445168cbaf7500442eea80ad0c1d1c4c6f3ab3eb4362857ed4c37fe9111998a4fdb41b8406cd0a5bd483a27644b9c7aead9e6ebe514c3126edcd88d0cb30af1c950d3ae823f708a41079a2b2e09a653b28710b32efde5adfd65282a9beb7b33a741de3d34c1c5b04832c0f38adf014ec530623ed45077890b7ca8b5e6f2b94f0d1a9610bb70e3d564cbf99fdec2a46036b29ec006e5643ecc6d09a2c0d6903bedd47c93c64fc7d366fdba9493b85ede1af6fc5acaf31d14fbcca5787e3a1ac2f37744b2ab4d85dac7972f58e42b3ef7cda2d2af39a17b0c755ee0725ab2d1334bc8ec4d1e89d415801c70554974c3293374c2b2ce3c11a727e6889f8e65b4a1e40272b132bbb2a3ad2d1d0f9e36e7306842cb4b8e001204a66ab7ce47c9ae653e72e6d03440e5be8fb6c555b98d93fd36cf466e0330fd756de07c9f195a616a98b84f0f3b8db48a242a9df2beca5e017893c872d3b72ad763d64e952bb9eeb285cc026bc876c2e44578b8ddb5e19d5f183ea6d0c538e201f95ce03a1312be9b80d2b195b4851149a35b7ca9529639f58916a9218fa17a9e9887a9cf84454fced9ca3455ddbec0ec046a358a7546bf7800a73d21ba284e4ee978a5c2aef325c5b2dc8b8e736975e938ceff59550882eaac403626e0cc80ef064a3272f370aee2777ee974bdae804c21670f52079d64b24eb079a8751d9ed794e9541a7d94d31c4bee30cdfb10300"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"36d4beb2f2393ef6704897d3edb545ff24a4a18bc03151423ca11c3325990b32","proof":"6618cfa84ff5807faa53e219b05f16398f999509567e1a65c2fb5286fc460e06ee142f4d3d256f4d0db8521cbb6ab2a5e7cf90dbf7459982e4d35995e9e97d0d90fc5d5c845652ea211fed8c9293fb7093e0f4f9242c88236daf955f24ad262b1410c598d91165605669f4071919636a87e134d175adb790fbae233eb27ca926158e1ffe72620a88284be1b2f25bb5d780325a220e31febe9a09dbe20b46aa0f65b6f3c835137085eeb40cf280d29bd997cb6f7858585ef3d738d266aab0f802d5b2ea887a9e9a4b439e30e948cdb187db6c85d11887dd91f2bcee8ef85cb8077a83c59f19f48e6e74e683d12631230c763e61eb86c2ef35e82f31c399b3a54214151e6ce2f1f65c2f1a698d923022c9205b4ccb56e9d6b40e5949879bc2543630e3a08470983372f2595d63b96a737999425686eab5be6b6cd9716ba339362846b187ede70d9d2096775da0558215a7e77a5561d3ff1af7521f2a5dbf9b1f26608df1afbbc027a71a3c05199913bcadcfe1c71e679937e77e95fc503ef6c01eb825666f2b37d77fbc949f98bc536b2c7d73952468c07b431739bfcd89191e70a844eaf96ed4261ba64249b7fd0ef720a478fd88f1778afcaf83102a5c56e0485295c12d234d07e5bd2cbb02d4be4faf1d9acb4abaa6d1f6e94d0e80a2e40f31d2f4df563e19f313caef5eaf75620721e806b064b437aa92b3fb34173b4d5c2a6a26d16f8dbc18fabddd336ebe0b4b3cdfce46f3d65ec75c97a9f4693011392022fc2ae6a6827270da82aae845cc715fad94d03828a374a83cd763ebb3ecfd6d6ce5a9f815d58a75972984ecab71dca5848c3cbfb17325f8fbd7be4be59f1d6f8d691b5b9085d203659948dc7473fd8c591d3f50ac3d5709913c879f31c8f40c4b154a3f7584f85e94be01a66f67a8cbceb3044f3fff9a93fc7ab7a342659a03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b8b28549f72f0e8fa283c060f280bf6c03600461ed61ee76921326846842fe4c","proof":"3666e86b1fe7456e0a8bc3ae4cce3b161e1afe18a147500c2aef51b717ba3f5e6eec61eb55356fd4d51188e9c4bae10ac7e5edf89c796a8da0c0ce839e22e775fce76ca9c7ae4e25fcc36616e63fc63eac339f0dab6069b93f7b8b178e3c654c7636fde03e1a2e7ede45c716a66e0ce07be5b967e92dd8c0812777943b80654f046efb60addf3789647ff427d9c3c56b6b0ef715b21d2bebc60df7818dfefe070fce66c6c62df96b38ec3f001f74c2be19965776bd92e1af9b3e31b9edca8f0730860fe594383489f325a8c718f4b13daba2400f6eb4a294be07bd668cda75036e5761a03924212cea41f91360c09094713f371ba006b353c92f919b95409a6d56740c601620a085e36e49dad266b7848c1b60355ed1c280c42a99786d2d5867d23426f705d73a221d0252f204a8d291f9e6cb818c66a2d8848d5964cc4f7e26fce7b7c12802cc2ca0a64851795c6c5fc04a665e72915b3c4cc9c3a61c15f47d626f4b8a7fd2484533f37f50f3bcfe7ae8f3489b84f82488301ef9c28a506810a466b914074f0347057c41eb6630017c88d35c16cbb2ef4efa3f4b3c52250a23922f4b3b72065d7e278c3787fa50aee660e16da145648a0f561165e078215e4b9e820405d1e2fe0e56744ecd92ea6d2e93f2efc09718e379e7badbaed2cf2b4e58a21ab57dd8cafad3a1a3ab1833b4643c31dcfe23c6a54ac8490481113a2631c6c79e56cb804d1d42bf3e720f5cbc813ff64fd678f578c0a60ccc568d205054ca9a368c864ded12e193a745d4ed0168a1ee9303a814a183c69d3382863e7a0c647d24a151d79f3bfdeaeb2ac1fc96f5feed535b12d9389d66bd2bed13817b20002190f4dffd511bb7a5ed20e8764612ac5e6d281ade02cfd4275e95983a3f001ff8f41ad3647cd6482a857efc251267de6b431e1abd414ed61a5c7c92828a00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fc4a70e54162706d112299cbad73835b43390d23e1de26f4dc01c7752396dc5f","proof":"864a4af2504a1da88ff42d2fb223cfde49c7d081f74f82381b6bdf3c74fbd37a40409564157988c0f71f641d741c33bf90dca4fd97cb2943e84a991a74020b44ba769347be92e3ea5ca1d8889abb1544ada119c895c31f75baa9e1fb908a622d00079dbf033651850da47da04ad6b8a302c1fa196b871417a213cf095b57bf7c3cb6022d5ed89084731dd4f8a4b96de934e8e575c2db9c09b1a2db1a9c7dae01d165c6e34bc45cba0ca7b1a1a6c4d5699eb6e42e551caff3e85cd5a9f00a8d0467d3559d3f5aa206235f0975d405677cec7747ae2de9917de99a170fcdbfcc0e9c8913d62f6ffb1322b6dbc1f59447a5ff7c4779dd1410bb178182cf342481390a6cba7076ecdfebd5c274a5eabcc183305af162c8f431ac6ac95e36a0976b025013fa3124050659c89bef1bb9eba05577c7bda1a75e5ce2c074291b1fa77321dccf9af8d4b1546b1afe2ff3e7847493bff69664f6cffe2bb91bae24fa86d33e2841a7e7fd914e2f8c04f15ce86b2443553b8fbddae5265529a502e48a37d9748c7dc313c1bd7c1ea382cd3686c9e61286f90c167b32f2944d1bac046ffe312870fe6921e6d232f065a424b42339a364de425313ed4981e15c974ac2d2f9d5578cfdb64746ccf13e18e8f893ae46e5f5d17c08c5a1444bd0155a1f74b50f5f1952447e4acb489350bbbd9c80fd1fb6ead574edabde8826019e07483855e6e9719efc093ae718fea61f0485e8382b547ef9f6cd5a5329a40061185190d15c4c19e66d31649b06aa885ab59017555540e740a6ee3f9ea3a58b71ba04d6462503539007431134d9f4d8ee34bc62bf44285e03baa8d2fe85ff23dd11ab8150ef72768323708b897e7dc356dbce7c6bc578c2b99776a8a8da945b4dda73fe8aa59c04bac17b546336e1a4ccd347c091c2e1518c212577edcbafb792554c68a78d9508"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"14182b7e71b35cb106b7de81e09e1ba3db1d8c6b9066f30f95170e17791e2b2d","proof":"567bdd7af6396d58cfd4d2fb87445447fe63c4e93ca2bee417061606a1cc8668c65a2a455c326774186e2c8ee0bb885f0520ebf1a1ddb1ef015395a0865d143f92e6fd8821584d4b6127d194cabe8478bd3b8b3dfbe08d22702030aa37c4eb10c09a23f9ccf6fef14c3ac2c7bb9fd721814f8091911c7664797a2eae3594087ea82b68f28571fc988b0b39c21be61bd47e17875e206cfdb6bbbe012d25bd7d02a8b66ad1bf2c9abee698497174c180f77e62c424711294f48a05b3bc85218d084188fb125c7dad89bb5f87f51c33ab0138cadda897aa5a49a17a1a2e29ba2407c0c0e31f9e34a8ef17b6121306a40161ba6995157d1b605d0eb10127bf08ce20c8689442caa87e635ed4604e798f180d4f57a0fa007db1f6cd45ff056b9a372cf8cdb13ee2d2a2039308009d739702acf65c0d2f9ef1a0fada5961a855dc5738b80d502317af12f25c172910f8df1ecf6b598fcc3e845c0dfcad509b48215b0cfea9c4c199b8496b72f65ce67bb444008224dd06b970345875c942d5b73a4b4e54cd768cfee5fafb4eb8100e9b55941842e7a37fea8126e2b14ad4d4a7c5074bdef64ed88fe47702f606840d455e3251df3f6a34877b2bb50acbeb8f710e5a6666998ff84b1721140548d55922ea90ac62732f089dd627cd2edd75f6a58ea31ec860a067ea5036bbb54c1ddfc2c524c7043f1153c14a6d1ad7a5eaec75b6544aea691069fc4dfab10153a8d3279c587662ae2cab4f6acf7b3b5acd28df83c209f06971d8dcdb2e2720c59dac244ed4b36e383afc85292e3a8146b876221d7f79c252c5f58b585ce00f945a6d0a41ba8ed9c504e2828a33611fcb894552786a66f87003836104402ba0b33ae8503a8c0cd8e5b97289dbf2ccce5d117d18545100f9eff53ff681c6cc123e9faf4089b5fbad2440abfd9c98c10cb5b0f52bd63b03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b6ea27c196f8efbddb9918f53aba22d324efdc9a0c3d2bbb7dcb64823ddbb425","proof":"6c06d35724779f07aa284deaa410a29916425ed70510243e90058612cc31390a4a258f38a163d82e419c1449995b777f6f1d1cd23e53355ff1e520b71ade60450819f33d450e05418541730230b689662f1952a62237c53328833a5cb621b05a967920434126aeeaf6995f42860b8dd71b8f6a2193d556cba218eecde0bdbb6672015a1c433c0a028669bb0febc99815074fa6d65d33c6874b9f993d67322903a1885ff83688a1a0fd9dae401ea5a4aa8cddef844e4b3f8c72d2c715faf1320bf9ce8810e5853b4e9b92f195273a66ea68a986456777b3391693fc40013d080ce234a79c6760786f46ed79eab976f6e33c6c1bc4a936adaf5d4575065c25a933d48cf807c8e952d6ba06027f5f978044b8048e1c6d9074d115486f8a7c057d73c2e3ac377cde15732b13f510a10dc35efc345bc2ee6b6a4bfe6799f63a03e93b2e3ed036ef1988178a540d8d4e9c23bdffd0e5a90d4d916b568c828c1b72576730824e48f9a4882ba6b9773de1e3d219205b2f8b88eccb075232724bf1eb5b70922203582226de466c7d509cc5e4fb4bc09266affbde1e19620a114f874b9232ee7a6589f41077d6e72ffe8ef3936d58ebac6394b40dab6f571c1d484af9ba2fcaad5ce2077e3288d7d0fe852cb518782319204d4ff7bd6e81f1aa92ee8f412fd8e75073dba673e070fa36db0fcc48df2bf629a264197490f5f31ded17fa711a607df89b40275884c0a8c51a539da87dae727af7b19a19833295781525dfb16ef49ae1e66d36a560d804ab9568a7106b65768c3f1c67f15f802180efe83a1a1576d21849e988f1457aa029ec6590ccb728330c3e6d33286b15db9b45b31e2558401df27c84a1a62133a997e02a50c2852015a69327e5e1bb6632dfaeb4173c00cd5fbac1a19eb5267b4b138269cd2ea101a35ef1e865714e9d4ac756f4cda800"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ce49168f140e5f7fea993913510eb9ea46fdff59ad698b36601dd4d0cb4fa066","proof":"faacb3b606bade626f4cb00a0c9be079dadad8df368f3ff44369082a6fd77520ae21e0280a779c47e4268a6137f058e386df9193927659416aae4ca30537640bd07245e9dd0d797ac89200ef28de97072c68847417015162d5575cdaa091ef03b63ef4b84d66c4dd80c6ad18301753dceeaef607c8714a73f16145bf07791f5a6419d78e3b60b6c90277d17f40cc2b91db450ca8ee0c1e0703b9a6c47b7e3908e191fa4db45eda74592cc2e58aa2ee2217406ccb54dbf2fa0c2b67ad7bca95074776cbe616427bd6950c50e525365168db6ce82bd42e697b9156bcaccc5fb708b6cfa87da0aa9a31bfc0171cbbfe21817b4247f440882d206f9160f4f78e8f44a8f35caa58a91f46e48c19af238869c6c5eb0c6c44149e0eecfa757dd5de6b76920f523b52577094db517330cb4c8cf6bbcf148da6456bee6a020f3d55f36228124ce585152cdebb45c310b44a755dc1dc8bfbe64d7c136741893d40839e827380491db93b805eb7b32a2e58a933029408fd31c6ea46ad1d3729ec594c8d1e4238fd1c137b488f93a00e2b163f6adb51a2ff4c93f6afc9726a0464a4ea5cdf30cccc8467bb498c6f4c40b1b08c009cd26d6cfd600e47cb250610122fc56bcb52da98869e55646d047ca677c571eaa9130851be4ccd09677dd89ab2f58633da66a095e86fa19e067465170bcf9ce97ffb3bfe451f49afcb26b6e5194f59877b542007ead0e8f2d01a6110221658cb3148908f56c5cd7013cd0217801795a412663efda01c8f90b1e54bda981305b2843d93ace6739976ce0862efa87bbe708a4296b20c4675a19eba6005507209a9fc45ad1ef1e6c5ca7d1637f6ff7820a82d7698673a86e8be0443ea2ec6d9fd116923fc57230d3e95a15125b4dcace39c510d4c82e708099bba63bd3367ab7fad8c48e9d4e3ca0e11e20137e6b22755c5410d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9cc45850a8093c45ed519b71236939baa29644c344a81337450cd3e8b80baa7a","proof":"faab47f812851367003e7fe2b40437e3964f75a9bf5122f853fcd0729009a524d44172ecf5e52c4262ff3e8f197aea5e9baa4f4c7d71a5316d07c8b8fa722c181cb616433670ec1a188432676323ef8949a9670b8dda234137ca9e056815363a7a475ec021f6977ec1c265620d7a13e87a5e72ba2434e48e656375ff13a97f51bb3836be657243257f6703f1649c13b9cc6a431e40d1611b384ffbd8d561780f0b060a5852fbfc350f3d3e04d959ccae1a0e7211ac9ccbfed5632bf0d8809e076e16010519f35cd16fddcc0152a3b32359279b03a680df3a7ed05286fd6a210614ff6915b2aa4eac95382d2e38801add71f7f5e967715efec0d36370182b7670225ef158af056bb3a8124d1a83a85b5af90fd5d16742b3d70a010dbf963ffb38ec5f6c43e45ae5663204ff93275166f3a73df3973a6c87a28a219d0baabebf37786c0209b72590841104409a66c43b8473d2df1c9824f3e2d9101aafdbc5795750bc17171f107989e99662038f9a02124974c8c0e37001432ef254b53613b6184e0632b206104e7978811ceaec130d1ea911bdf32cc9ccc397cdf29425b28639505f5e2332132092f50428ba333c1b32cdd51af11824bf2e3a7b36ac2758c379425e9946988d4bf696bb0cc1cf0f5cf105fba31f324cdf6f2f7132fa864d111ab2d221c76b289347f628f65e6b442112a2f63442c56423435136318818cdaa709a0401e0cbdd69a10dcdedce548ee54568b3e49e54d5bc6326bcc96a50f9d655724bbec045ff9462035c9384143e973e87cb48b069efe3d3e24b392bb0e3b536d41de6fbb550b5b479833a7da5626c471aa170925fff2ecb3e7ec4b89b4ff62e2265c52fa8ddbd1b6fcbd3969cde0485d2ac6dae305c6f54fd259e5eba3561008d0ccb5a63ad96ecc33088ddf4e3607293f3e15cf636b0fe469143d6200a3c04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2817d4b0b28b22c4ae88192dc759a6991685c5f04ddbf32aede01b1b6a43e359","proof":"2271b0254a5ebcf6420f2521c7ba697dcc13d62670b1340b2deb810fade17279b238bce96e01b9834efdfb7507bb6fa32f34e45643e4c47e4c9fc7079035386f480269ad6bea712224747888f8a2b1f48bcb34960f4ee48317edb5c930edfc7a0e83d4da241c07df1eb850978b4367d2e5b4ed42606f29c27d96f468594f9500055fc2a2334fc291d02952dae5223c5ea6e6745a1e32d8ba54764765bd0d020e6eba64353b6f09102720878c5e21a255c5ee78adc0916ebe8a6a31b75e00fb02dac2be2727a026e8fb620486a027739980b49acc7d359577b138c33888710c01ec5e52138dc41977efa5655def1c9de79f35d70972bacded519ac37548cb9036c4647734da083425263e756cd42303e43f82a590464d3b19404821e0089113493281b0e45ec59440340312f35a7793c01f500de0ec5c0d0bb882d0bcdcffdd22c4e9cccabbc6fc971eb7d9d3b79e8b952f2d67274f8fcf5d8e7bf71a99066f2e54c10240e59603409978d3108e947dd033b1a1eba2e72845ae59eda96ef10360ea35b90cc730b27dc8dfa06370fae7c356fd10eb6124dcfbe05127c101d5b31cd4f2f09194e30b16ee33d09c2003109773a207174f9472dccf27ec09302a230aaabcb1fbac0afadef8010e721dcc295bda9070012c667540ade589ed07e8ce660a64fce25e0d9f1c61081faf361c0265654771473cfd202c66f973d7e0bf537e8e0dd8bf9f54554049c4e911a1fa8b3b69be5aaf3ec924b68d1da2974ed08073364281b3a6f259a5819812502716fa1093598c069e88fc17278e9cdd526e673cd0a84d0be943a5453bac07d7398b97541429335319efaac8c83a5f45c187b9481e12d1cace320252bf73e47beb971f2db3d0700f3fbccb594860679ec079fe0d3ff40c8ba17a7477e6da31121ed7456c0e395b1b4833e9de449188e55ced780f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4e98113463f6968142d0d174a758619509723824cf2f0b46c0d8bb2dce7bf315","proof":"469e5104f4b110b0e13e88124127c3b5cbf885b059d8192dbee238adff560f5064af08ca51ee64e07b7db0666523644cf0bbdc233c42c828583a940e3b859805e23be03c7ae7920e1a36226f01cd8eb24f016c2fb61e49741b6c5b74e436c17238baff614443e38d0fedcff72041c4c506b8ca2a0799a8c9678736a5c864fc612d33085c835b6ff17bfdc6e3dc1fcffed7d442c08711e3a5268f811f108b39036a6b678dd043d62933e261f93030c03736a74308e172fd897a278017d9fd2808a27ae38dac210237a1a869cee899c3ec33525887495b1aa6c02c8216c80a260fea90253d1e1748e320cddb4d20200e4c00e494c8135a414bc85959c215a6092522166884ee8b4c238cdd4997be09f3d2cadee7b07f99f45cf09d4efcffc1c676e4459388e5b02c6396b924ba3f65f0cf3e66df8ca6f09d9f265c9f2bc686bc78c6a9cbf0af6aafb96d4c9f699614c075bcba91ba9809096eda03ab4979e9a5792ece89caee3b84fa8181d84808cc65be7a54ccb77e633099bf0392ef9c271e0c58096e8e8029ee220ad6b367d314444b75237120364cecd752e537d939d4de7604e208ff165a34a61abc94527375c9efe2fd5eaad0aebb2df5a9d823f5f9ef00d2c39248e05c7c3ad3f3555dbd3442e6440cf8d170c6926a50f13875204eb40cc2c5bbd69975328f4cf6496e22cc89aafa327fe7752e893324728e629fbeea1b4e57dac3eec83f31c8607195766db975e739857e5241e3a1ab835b6bc8921934d047b06ef37d898df428f49e5428688fd57c884dda331673dbcb67a221b98329d03772e675c12112012e780108c5c52db79cf310eb1f5a65444976868a44de79cecb0a71c80a3ac0959a41625a5ecbdbe9d53ca4b41f58ddf9be9567f9d58607199dc4b79ed45a191b1030d82116a6b14f131a1f6cd1e95e2f1cc46d4e8d4502"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b2b1aadc7fb6fdf661f7b0a58c58df9110322bd0508c106df776347b5169ef20","proof":"0289012647037b84c3385c8d4469055b51bf8a3ace74ebb524a8ce6c9958940d965d00cdbabd269e419dfe0a094e0b763b6ddedee51d5c9a8a57add7b55afd59faa1537fe34b4bb127000b95d14d663292f763a785381c335dd1c7112e5a921482fd2a7de6163c03a5f9461bc344c3d11f2b3ae1012684d04c5b890877d76150abecc1d51975cb4b852fb2e7152a6502b92767c48c8b8a384a1edc9b5999c10bb0fd3e188480d8026226fe0003a2657d3973db1fea15eb7282e631f68d3e5b0606bb6f2340981af7c99061da97960cce0710a9f4e6ff40b5f38eae151c0f770068da876e36f4fe1c4692b8bc95afc8f5a920433262f3b80843ca9fec6fc640728e01429354488462d8248dbe24ac161a5d075fdaa5c956c6aabc222ae81dc75602d93f1c83c3249a15bea36af66d5ef677298bb7c0f3a9a41d90a1cf997e707ea6ba4d8aab4ad336cb37a014fc238ace47f45fa67a2bbc1cfbf96ae9d480cd4a26baed6c79ae821db5670cad692e5e54966dc83294218eb167f5046cd1be32263ef969ecba9f7f3c8d7127c64c9a7e436567c70433bc1d5ca2fecb67172e9d2e4ed801591f1fce61b79a19a137daa01ef1e848364776a06728b9a56b7a892700981aa214197a5311057358ba264786ed2d8170bc6b8aee9bef6faeccdb1c82452eb267f13aabee4bde32d2668b42e3d7f92ec70178808d9c10101651e23f2400908a2604c924e56562fb2f7f2214973d912065547a94ee24a999fb8495a6be13ee295b4d81ee728ebd06ad6dc0661a2e413c337a70b18b67f9249150b5ba5f4d087718661bee82cb96911b766489a16f7ccf6a5a882f7af6de9b0406f6b7267b71efa434378119d34d1962c3d54d8a363b27e025fa29f1ae81096de68470100c968cbe60eaf4b54d5165ae1f63e48ac08ec6c3241cab411f2d41bc38ce891f0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fcaf347d7d3334b5c6d318baee74d2e08a2d41d466d8a94e11e31684afa98047","proof":"f23b5233061ece802aa9d9f4213297d12b3e6c4e323b26499bea4548db9a476e5603880763ce74b661aaa323b0fd9a1657908932c3d4141fb574b5d032148c447208f90ee4c8b0be37925b704656e3e1624338b454a5733dd7a92e975e901122c653545954b3e5e323cf86e0bd8f3b679a90b478eb716ab16b35b08d9bc5e45b0b1e930449aa803d5eff3ff58d984857f1286ed85e4be93c5939153d7444540fbdc5c927fe3699f2ab1fe1b62a9dda06fa983e8b6d3d51954541e3a7635caf079357c04d82036e34919b11d0df494c165832fd7992d0f77bf81550c3903724020e30ef6b8575ce07b90f13043cc41c5f3828ce61392e3efdd58a2ba395dd4e2834235dbaeefbb57328e2c877c6918836f15b74369505b5a84dfb68f07d43845848034556e3f815cf007dfc8672326eeb4673a8dd433d464907216d256fbedc245405c5bfc9c51adb37d88864cc5134b5112051d5c6f76dfa172e94659b796345802c0daea9101cde94e213cf204ba298d7cea99e5d1c9ad081a87f86f1c0b122f41db4d15cf750aa1e30f68ebfd26d24083fa27ca845200de186dfda5b16d91fe46d20886eac42bcd90b86f0764179899222045a5da080449d7d82fe3f2b2e1cbeabcf8dcbcb01cd4d6286a9bb46abb1f07237a51187b158c07fcc4dcaa32b25fe458c0a7f352854a77ebffe517f4519ccd128082b5aa76a3f59c08d2cbe6820bab373dc612b496087fcf75de58071e79db382b588a77e4bb87a43ac96d0771e86c5ebe81e31a62f6d2318fb3fae6b63e08b5f7227dc1322e0b7981e8b7beb273eb3c88e7d9eeaa4b668f7a1dad9f87758a8f39a432b91b6c176d3156504710a2f2bb124e76540a919f375f746dbb0071794ef4f50c1e32054cc1f613eb7c40656c7fb5e6fcc95dcdef42b4e4469f230660d26be6bf92076c5d863488f1f570c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"46b42915ed55ec62456ae13b1ed30be42dd3a7ba841f25ebc36b6d09444a8453","proof":"6497b54ee2027e1c577f3762874d916dc33c748457b80f2f7187ab8622a24973f670353039d29fc1643c574f61d335f68b21ff59bcc2f8c9ecc3b829f3fc172574467f9ab619173f3672d34816ccc17989837c2b881b04ecd612e4b97fece606349459720a8d037870062c3f302235e6ce51da7b7f54ffd9fe5b72619aec1e2f37e35f0e6f3361527d3a7ba5e9944532a5d45d1387c7b37350604c142a471c0cf6387ae9dfcd87ca718fec883d14d556d9bc4844aaa5c0dff37a7f2e9c0c66058f612d0fb6982939df157557fa57fae17ed31a2fb84650fc4c1998c72596e90826ac6a6275fa5f56b1796a8a79af96e8368c56c15bdc337611498452d20cba39aaf123d66769677450834c98460d6db987543eabfc192b09d4f6626d64b38400a2b3180cf46a191e38a551a96c166a2a0f9d6b5415bb0f956b0b4eca53c4303a4285b7c54565af9cc5adbb78acd16ab3cd06d6b1b0216f3d1d2ae199aa1c2d3d0060ffded865d305d499f856b8d713cb41fca48d846ba95371cdd43bee6d2e72789425f8855e1ccf10c579620b571eb0aa2012e07ba658af9d9febaa676711133c2e0a7c4debd8c23fbf6547ed885e853fb43dc22f79a12aaee744d0d609562400b3b67622e3efdff80f64f3dc907a894a9a8d719669aec7d692bac9d2a62c538c6fc9a3a9653d6daa3b6f572368e8efb26357d8c8c35c0fc71e726863d61b79360928c83a909e6cfe141b6909d48e681cca8d293a6ebb439c4005f9e3e69d2e3cd2da5e7ccb85d0697804519d88edffcf566962c41122f65b45dc498449af36ae1bce64b4d8b375438a56965a9195ad8fc3c41a18c65912a82a160783f3be40a4b72852209ab369cf2fafaa97570252b9f0b344c63c56f513f1d97fe3ca7a05732476b03b897b0ca1562515705977987fcb0dae14294d224e8e8065efeadc00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"107ae3962985bd0b1b2cd22fc5c8422a0f1ad95c118e76fd183c86fec1830217","proof":"f0e02499e5f232132f4f17ea9ffddef82ff10efb1910656ac82f87a6af3d8a5e0255ba2e910c0c3fb5e2c0f119dc50256729ae852ae0e8c1f2fd936255cd8d2606f4c9303d823156ba21bab3e1552ede5832948aae1a64964a7ec0d9a93d04048e0523c829416fd378d0027996baf1e43804c358b7b092cff034b110b0e8325189c0e3d15aa26e2caad19480ee854e3c26a4679c2c1219224f46aefd0965c90fa15faa7abe912ee45bd688747144e9738e1abb1f7172a59a9647e1d67fe1260efafcdb14cc257f093dbcc072f718aaf1929cea213bcd1adfbfad91483fbd91068817283d881ebb8c83c85c3caeabdff3f4940f14440f6413f21f58765a16f5044890a018a7b05f9ed54d6da8b9cf1dd7df7d4a09c19502ae07a247c0bf477e65b8b0df57f04446edfaed0723bc85a56318de56cb4c8d505fa10a32fdbe72b04f7aafeba00c025384e6ed3ed1439f9b9f21cf799f2a828e34bd39b05dc77bda0242ad5470c786fb4f08f43e59b8fcc23d24fd7072dc9554f2fb0af000acc78761ae421fd6d4cd23c5548c7a5cb8ede6dd668bb0e71cf60c050d6325b94c4b8d3b525356a0c9fb425640a8cd853b3ac9573f08a6a2147964fab5f241973c50865a8af6a20e97c1e899e570f4247a70552763841283bbe9c6dfea99d8d1a9d0d20958fa5d181f18e922280eb68978ff396c9260890d6bbcd5377f5f4615df6d9b4de09d9fb4d445844f4aad15d55341b41a7486eaa4b01ed91a0ca515203f0eee36522a7bcef5d3d88f76fe6b6c5aaa09dea6a6f7edd5c6544b556e620253786d626441ee7647038803d3db4cd4c700ef6fc83cb30875f66026dd7121c8b637a0162f29c006cbf5387aaeeddded125ca6bf460710edf2c80656cda8e183e7e5b00ba87599bfd7fc16e6d8e81ad2c2e08b050fe2de8c079eaeee02f5b8f188dad709"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c08fdd8f3fce0e2f1726feed230969971839cfee8728335b60956805362c780c","proof":"187abd1543339bd45e098781efa20ebf9a8b126d8f49ca5707e88bd031a9827088288eb5ddafbf11d9758b138e5d3e79bcee01516c413a80e59b5529af90a67b940c53607be356c4aac5e207d3a9a96e3a46a7470124c286c9b5a9c2cf98585cbac05540bde433071f76976fecc7d235677ad842cfc4c1bc9f9b26007eacf47ea2e78b470211f615276826ee27cbf97c65c338cf82c530d01fb34f90f2da49039e6c3d286c6c52cea256b8a9ad1ffccc1dc57d4e5c7cd637a01132954a79e900ee00d0bae59e70d10e4cc096dbebd53c3aea77442d0ad1393d706f20ef7739018a3cf90030c953c359e1d9f8a3b74b0f22e89cd7bf1c5e432dd8ed02fcf109278697ae51838ce8cd1376b7b0c34c4b051f38ad15f75ac5c579ef13d5fcd91004e0201a8a641e2ba3bbd767b1e9688e3b141241ffdc60d1588a56f9f6171a1c633c3ff36ae0404e1ea6b76edfd5eab15cc885433ffe466e4d059f0c1059d081048ac8bcbfb28eda42274b625ce7483f1099c5bfa3fda0a43f0c4680d9700d43089683a9abeaaf19540703ae5970fb98e17f965fddb07332757423e7f4fd9e4a4e3cce491beb38cfba23a54e9a15de44e41df1d22504f974fdf5d3ff610a1d0c3ba2acfded12fa07930c582bb375c90883ce3b61b6bcb730b3ac9961a4aaddcf593e4b757638bc1ec2b4c466d369a727772f3de746790be87f8d4250be43311e4354d94b28a80efba836fa502cff739c433c8f81dec81b4a9569fe2dafea83e0353c146368394db3f9fbbee21e52aa44e2b0008cbd40efbfa1621ee3b69ab82d00d48b39f296fc17f5558cf9ae31d6e804114fd29c0aac2926ba856176d9f6713c0eeeec29e2cfac93cafe896c957e935825e8f3816840a1a6e7534d5b49c5b80d8355d66a436cda152d89d7306e537a0bc7261e5a082420b1f3558f822ee36806"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"00f43f4ca785f087accaf6c38b1eeb12dc0a5ece569a386fad3237485a1ecd2b","proof":"2655601999af048f26fd7f71d47fdfd3a3e4f57172d5797280383c244da39e6de4fd85649eaac0c09d9989a92bbc434a5a016a4b530c7c1c4275f7cd3c2a007e48c1a2b6e46db46dfca820462b92d50affffa58ac61adbe938308aa9cf6e0669ec3f5334344e15e55c4cd93de5e31683127b2adcf3897df03d734428c4d9645030fb808878a2bec75731a7be8b60bf691609826735dffb991fc3a1fc808de5047992f05850d65decb9b67467d60c35e2acb90dcc61f63a91e0be2e1799aa8c0108127d0b9d66519d6bde7abd7e207ce3bcb60a56b3fc8a0456ee9a7704794005acbb5d4961fd0209d49a0ea42dae4356089564c152c6d22753c9d45f50693a169a55e235637fed1e9dab5c767ea78d9e025c490f7231d69f8c59a0942677ca5a1c9f6780beefc758acfbaf524fa2aa1c764a4212e3875744bfd83126952e5c57baaeae82ad29c2397f5c57f0b1fc41eeb2769d7b68939ba8b1ddd01d27e7b62664fa030b1970861cd503c10b5bf73cdb37fbb91c573a3e2bcd43d748cc40d74ab4b38a59b6facc8267a4770b7ac56ed13b4ab7a4277568e784fb471b4db8db30943248aaf4bf559f3ff3672f066b039fa142769e2041005adaece8b0f2210f2e98151b541d6746c99a76fe46f4d96c289bb3b7b8ccd05f73025528639177a20df43f85766bcd7b9f6ac4626233a9e05420e620620b38848371f4dfc637a41176b0e5acbd34c50a817b32ca7d4cf6d4b1bf5b64a287384eb66f1af30968f6270bae9aceec43f75d37ab2519f62410e5394c15688fb2aef51cdbb53f203ff3952c1eb64e6ae90ce7718331d46dca65500bcc1d70404412c403e1a30bc4155cf34b6baf8972f50825a018c5bfd785ad7069c2b11d4747bf34a1d77d02ca8d1a5808c081aa18c3243463ffcbe3132948d43f45eeac95b02c4c82a7f2abf7c3b29908"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0a490f4464d085c3046a420879a360fa5045164ce1e105bd786a05d7a2c43771","proof":"a2f964bea6ef846bb7dd95a6316a88721d3397491cd4ee2c6944c691897e6f3e9afe0c9303127b499f75b945c903a1165d461e4f1092f9774215d66bc45efa1f8622416911719b7aafe1181db39fd2b1f35219637c6baf07a107173b105e5172483628c5fb6fb8207f5c0a15c3b820d293226028029498b37d13c4d2da7d7c56cba978183628f5b796b79cbfd460d5995b6ebcca339a874671f836713726f10a7007b845564a7515c6d17641c8a678d9e6a578030e11325831888e89c8a3630254494cfac331ce7a952606b3761e9908aec1a4afa2b72cf818bcd4cdce781805de6b4c0382d88ddc9e8fc796b0df49fd8c6de7bf5cad4aeb40fc3a631f8f841a0cdcd1ce45668c62a393fe7d520edc97de88346f1ba31a27cd71e98da48e8237d01c2ea7f88a32973c0917dc435ead08bc3667580ae08d70ce3039649a47ea7654df386c3faaa0f841a1db0873251e6cf5f316ab5f06bf909a5b0602ebf70f7c8a5dfa05225e4e9984cece63db0c9128c3b71afcd078668a4d3d52999544c6330ee7862dcab150eb21aab276d1c45ae76f95e58adf575e52f8abf986edd35e131038422c34899cd7c2efe7b8dda557f09cfe1bfceac810a90d77e18a431252155aa53d67361d57384bb13698c360d6578eab73279443b24e429bc3832be68577d266013c7cf5e442804045dde025474d65168fa7924d7040f5e4eeccc3f55c6ee08d921c9382d5a8d4b1831a9769c03757507cfd790e553ca1780cc32751bd58605ae3daa14761fa04841b8c70a78814484063abad9df8c2649b895f658009553810f45865d8d05ebe5ba7e4f63ced62a8d51326d2d025f37b67b3fe239f9e6b949118282cc166926c7e42ad696a759389ebf10004603512c756762721f033056f212d2b4fd19fccee71d938612db5c476439a10540515a05abfee0a6aaa2404"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9cd4df596151af0fdc482e147d6a19fbfb60e7b43c5d9c2247d55fccacc05604","proof":"ea43a64565872a7e645e4a823786e54965656900c1ccc6455b771bca4fcc7412c026af22e201a1b6a09a6a04f0515fea8c8519fe0d3cc88e7898b9d24113f62cbc36bc7a19035b63953322d3543deba26c27201ee4cd701e4901771077d10c65ee4f144b030129af9951cf11a9b4bf070f9ccc049f6b311e4635763d2be48b4e5093e8077930eb385fac786e642f8b644395a2669473324406f00901d3142e09a2fd9cd87ae42bd8a4b8233c8e8dd5e035b0bf4b58e8d04809098030811cad05a6af5c0ec23ec5c69bd34bcb73f7e3212a061d835bbc7e161d761dc25a4e9009403cfcf127d27bf6c13d39c16d05cc49c98c382dcd4a1b3e58a15349d4de977e9c7bb2b56caad78ea8d155905bfb39d5240f3933df843522b29fcc97f01b711fd21fc15890973adb8c1f024ec8fb776a15540ae5bed12a9cc755a09945f1880d66bdd83d1d9a0b2dcaf4b30905d3d9358895be2de8fcc9cd9b6de47fd2cf5c545cf64bd4a64b1d3584d67eeb9fd436234e4382c6a032dc745163dc5a1db5426fd67ec33984b9053c7375ceb80415725f9274717ed47af8aa26978563fb473a50ae144e445aef7fd3479149e4576b440ce49940b0f7e9ee0ab8169165460726591298f1907f3b5f580d8b50c08991cf9b9189130363b6e93d90a4e9c2ccbe176f023ea3b3ec4ed320f8c9e905e13faac540db74ac30f1b76a98b19a7c5aabe4325eb42f364cbd757e36791118350dc624df9d625281b35b0e39ed802ec2cda470dcc5b2975c875571794fcba92fb382d07ed790395bdd05bcf57e0c2d5c9d044d30401a088480fff52bcc0bec4f590616a0ea4917db4ca78620855b360c9e720bb364b09b549b2f788d7ca6fa94fc3552b116152ec4a90c92d5a9d5fad8beb801fdf10c17285c7f8fc958856f68c0626f885b50a378055aa46eab5393d1f08e06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"709666f091e157473e4979c9edfafc7363df0b6040eb3a46c833ca51a7a7ad7b","proof":"a0c8c5517bac52501536ffe36beddc9c8e4941f41b356c61ce097c07eae9ce7958ffd0da4312085b2df0f536cc3c61c9dd0659fee6acf20bc5a5042e4660064a9833799ac78795671252a5ab888ac203e2e9d3c3429a3937f6b27457708a0525bee0993587daed46a91f55a7eb622f2c848e501803d9d4fc772623f2ca1ea905faea28e2089329c9d01879217d49c1e15a8990c2c7e073569ca727669052b40f61689af886f07330f03aa94b5721d6de02210e9c50f7a1f53b5279357b19df013b3ba89e0c858b6454b42785876bf261b607d8bfbc36ec425ddc041891a71604b25fefa95bdcfc9d22271f78dbf08e0c01b62e25d781d43b9d2956d6841b7071d68e5a6218b74dc8be0c5de04f6d8cacf7bb09fcefdaa9ddad09ccce750bc47ad6ae9203b62655aca856ab4b9ebe35cec347b75d5660bf3a439860db53078728daed9d4d6c2e0e97ce1069bec1304c689cf803ef294df040b6a815006dc691748a46a7ea79414fb2f65a1b0c18294410b8f423710694f0d3425b8d3d2c8c0336dcb3e0bc175c83afd61ca6ea3bc73c2b6a74096e6b430ec0488935932e805279c4c9aefcdf11aedb41e87dbbe0b09ad2fe42fe536cee421a163affb1c84b2d2ec44286af8807fcdd082469f329e01fcb506e44015a130c92fb9943b2d236a8652cd0f1c9d07fde365d35a5172c07af08d84d004db3cdc74817d40d0b0ad1744f3eb3e5b98b8460ef36457e04e2a1a1efc3163fb327b60fb7ced967999c23614398ce86de0e3de6acf2a5b3b674ee84fdb467bcf5cad154ecc8f7eecfe984f82e6e8f53864b219b3a2bd959c230f8f0f5744fdf0d5dc756478e0470864c22e460e3dfbcd6521e77d3970e3cbcfd689640d2d33b70756c2388fd15ce79d8e72e0243ab9640a52df8cc61785ad84a8fa101ff696d45007b9ca4bd2a44b2eeed6303"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0c2ae4bef46c9a2e1107ae581ab03ca103501fade28c6aecc80a0192a76d2c08","proof":"12c5f148111ef4a3086e5fa194124f76520657b739b1ff34aaca7ec37a6b0e41f413dd04b700c366e508f24402b45291afd95663630750ebc3f7c63447155853883fe84f8f8a113553c902015cdbe1cd2486be7227d3d2cd0d5c98491759927f10182b0aed6cf0aa80cea93f7df28b8ea6c753cd8cc1bcec11de33b4d486770f92173fb43d63032a11bd169397709aaeb1927fa901bd5cb15b72a3aad5a48009e475355dffcf7f928277575148a0a7bd98c86da2fbe8f3be34498d0489e983074da6908d6ba38e4701e554ae589e1cd5395cc5e9c261b116d9532ef1f621d90c00b24daad9f9a6aa810be1d327462cc3b78c055e9f8ce66608f015232e12e31c3cd0299152b134dfb5a52096a217c46dd6907c684edf63942e438a6e70e83a29389c31062815c14226c7db902dd5ed7dd773c41e977f9f8d9cc5fe6252352747706f035653141dbd443ed90e8ccb4f24390dd3b1c0fb1a5f69084c06e49fc7706aee6ac5391edeeec55e0340b6f7b70d7a6695b132fc22c0d703c6976656e75cd44f609c1ec1e8cfedf63ee9bafa7a51acb1ff930de6915475fc6a50abd1f96ec4d109ec957de9da4c70890e8d728a821e47288ae965cd62c68f3032334eda555e351bb3630930f446e10c74fe41b4463d1fba2277f774dd994e3c1ca1901d0582dc0d57cb0b4d35567b563948b9a45908b1ddb3d4abf9aed2f4577c11dfc11c8c35bfa36654203f5edc32a5d2225577deb28dca73e0cec727fc653f44200f0a1a06a8627e73291a2df5cf7e41b56d1ceb44cdfc10ed40aed58734175e120c6c32ecfe2293fcedea463167d7f9ee05d58f9928b32b01844f87205254aa08b974b16d7cc499f005d3cb4a79012b4c63cbae8d991d96270cd14defc352fb48f40bbea0f4ee59ace037d71b9d013296402a6f5b1a626b745340aa17123602821a05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"922d9802eb96531909c330a45530c64da48a901a1d807095adcf620377869800","proof":"2052397bf7bc3d185315101c7c20aee6de80ef3ec2baf5224dd7e3cab29ba6396ce597d8661752b34dd6b8615d13d3d8f638fcad80f284b88c2962727681bd286a6f4044314036269b96972afd07df6a4cd68596e72a16bdc39ddd113cda4148ca578f7f3593fb8c5f51ac43880cf9a40eb3cefac05ba55c4abf46f91622a90fc91155965e687c7f6c24bbe31d3f10ae1062b140c35599bfd599c0e755b30901fcf3727cda8292eb8b7d7f9574d2ea83e1989256ef17d5404185984e3e645505a57c351ae112bf3d2cd0fe0571027033f28213cd5b794f2889b591ebbf50970a523b0931ccec0f1c2705cbd74b7875d8d838876629acfee20e32adbc585864126a3727e6513fb83e52fbbb2a714475e2b14ab22eb445042daf10df6f7719e5016e0b3badaf1a83f586d163155b79940a8daeebc7929f2b816ee039f94b12d142d84d3e7659775fe9706424a7b86acc634f4f052e1a083207e272a85644a5a4247220a754acc3791452e03bbb541b6737daeec13cbcf18c416fab558ba2a3b47c6a5eabec9ac70412533aa59da9e203b1f4a1d0135dc06b75c00675b746f9044a449ffa0dc0ae89fa8b17b1c19a27db775bd0d91b597e554b6f86f7d9a617524276d7896e648d19032b6de7bc41ff697cee643cb75171051bd29bcc7f316abf0eec22cfc2e901da6744134bfea10c052e0ac61e2f02b23dcb143b8fddaee7da2bbe361dfefe8c71b110343f82e00bc7156d9bfa164ae6d02049ae31fd3f80e413181f0345b5c73d7c6264ce8bfb2012563e0c3203627acb2d9c73d740b61bee1c763fb1eefabd331755ccba6aa109720700217887a4fff5c5c73ed65bd71abd2cdc8415d49dac45747dda367f40b1822d86687d0e73038014f55ed236dd75a5004b40c47c36eacebcbb3af5d9281ae5433eddc326707e1c4b9f0fec416124240a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b288697fa8fd652f8fc6164ace747e7740a4556e444964a7a41a547f75bc2778","proof":"821ba71c3da10f71f7dc2a5b8f62ffde09ca879fae9eb8fcdd9ae3f97bedff1cdc505673cfd1473bd99efd5d61afce0bea8b1d54c12c18f177a554f46c6cef0d2aa7198ae9b4679b889a6f4ec7aff54040def215e3d6a661f0d6a57d865be91c32864913dc79a6dc24e5fe4b5be8c3b54361e1a2df11125409274d5d97834816108e65f25019e3f2eeca464f3b2c7c2cd4e2a0f2fc13499df9586612effba404eff86db1a5a5ca1b5e62d7d17cafd2d6186380de5c18c731c7f0137ed8a5d00d04cca93ebe382fd494a875749deaaf387c162619fba683a459d50f36d589b203f661f67c4e96474fbe6aaaddcbea2a0f260be946f0afef87aeb4cfe4bcad6777ec657d8792aaef91bf769169f88917f8b8d41b21fef97dde4ca92ebae665c614f26b51bb44e7ea912088cefbe1545fede34c0e90fa812066bfe8dfae694fdc66984514a09e4270323e8f9bb30c33163ce3d947df1f0c0d54d27d55421ea4e901a45d504372499744a18d2dafcc15e7ee875a4aed51db17dde1fdece7c0d09c7c0459cc780959950ea7acd8d8ad324be824b7e93907b7bdfcc72665222c26ec7b42f734d952021c773fca94609e766583f8d03cbca9ef1153d71d5743c4dff243f030eac2c2d7c75f0d908a5ad64413e1a7be16cc221c72d5d09231f9ef9bd035767243ec334839a44cb77968f5a2d51d4fdfcd336c57ae5d4c077a0d7e4da73cc8229ae6d30a5abbd76acf3c6b35350655e0520542264cb98c4dce3ca6e01504a66598a494a5eb4d1ff90273dd39b136c152c1f13569c6531f4a37aab588572cf0f1c23a6fc36262e627674c92c93ba2f229ed055f1bbd270d7b7cd65f7b232bdf7ed6cff642b28747b234fa614df24b21daf6d3620566bdb1d323d8d1326e0fae4eaf4e346e4f777c334d261d93c323b6f84ab23eac5b28e141c57adaf6350d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9062bcadbfbc4594e72b1bfb3f26f37d83db9e494d899a1bc85f817ebbba6b3f","proof":"721402a4e9f9864ac428e8d3c3a82af0e83d95b127fa4f592c3cf25ebb5ab20aece384824b5255757b1ed028f394e2f2d78c008efd908bc163d16042e086f865fa95a27fdc682be2fa0d5387c4436e58044dd15ef8f6ea2cfc91f11d534d3c3c0ca4625f0142ef36b56a0280fb1a85552d739931f57d17753c7f7ec83541012a30d5d17c7f2be55b73981e0825245b2d8b2b713a30ff16e609a0e911f174fc05bc134f2766d030395025c06fb75c3fb29e2c6940d7f8b4658aaaed21550ea6068e0955532356ca2237c9e25f4d06a5f949887c2c99646bf27df66cd30ad7370f62a393ea4322d39cf4cd6dd893b281073b9a9e9feecf45c240355c8de5b1de7e188e41dbb0da42b4908b1668d6832b2ad7cafd8cd0680530931485826421c259aa67405d7d8ac01efb4d4bf3bb854a531702be333f493d136a8277f0a396c37c00f66e68b8a6a00356abb6208807893114ff374f0586809daedb8952098acb16c4431497390c5cb6c16445a94ec184fbc814aa62790e90d312d48fad3ac1692e6a67e756b4d16e19de0e335ec811c08bcd6f03c5f4165229afe1edccd5a11257520f6296c81de37e074461408898039bb45b6229b7d792edf041333eba4fd221b04758af74b108fd109cd885d9dcbb602abd5e3c4b1db891b605829cd621f241deac8306fe78e4de9385a86eb45d62b22166c92df92667689ea79f1faa36f455566547749be2ebafd9b8d211ac7f68ecc1115863a25005fa37d755d641fe261f5ee7d95283a86dcf40ac5923010d49e490d4e6e1466363f0fe7ad8e3ef24e40a2e6dd943b2f598c9d9ef11d26c2210313caef8e533a5579d08083d2bc41ece45c08c5fab0d46461327b5f314f496edfe7311eebe562147e5fb531d3a4abcc40ab06022a76abc4cc2290f08b00a6d389ef8c6276888e6a67c10bf9214b0816d07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ac8d5a17b1420dba5e517b8d48ecd279087e3bb20f7a2bed5b8226af54e10076","proof":"9ec3c220cdc08bd17689e7d2e6b056c80ef83882cdc75ef23ff94f6c2f7f4d4eb4bbb6e8d35711419d9e26bd3218244e524b2c31dfec627dad2cb8c1c2c2a8283a9b7ce27c4ebfd6e068df2ce38f7085ffa28ce84f6630acf1884cf786a51e70ead672a16fc12a0ddbbd6c04075deb79ed138a531225579c09a644bc19b4a76ca17448b6c51fd56ddb15cdd9492e885bdd32c6bff0b3cf6228bcd6437b716a08a44c144be513fedb884bb249d31e211fabb599d627a5542cade88b755940be05dd50708f5628b31c4d19cde2aa3119543841695d1e6a9a95f20802fb8985140768c980a3bb7f4874a7d8fac9394751a759b251faa1fbe08581a825d8618fc7385eb4045e442b57d35849e41fdb5423b84d74b75f36f57e58f16e798f12a33527e231a184902220c339b41f8acd4794565851f7d65b65afbc284a1a741d92cd6248f727698a43c9b4658b2305cf666ff63d47076605dc98953715b2253b676a39fe57bbd5d67df8d54cee44802d93e81de80d71952b12a9330754fed654d03f32d6afe805aca0585e95e2af76080050e2052474e5a27ebbf121028c6ef7152f75ea779d8e1e20fb6f8913ca4edb71759fa5c288e6dd4c1f3dc3edd6a832f19033c2ff49f3b05a0c6e35f861b92b3f5e7179c558e9ce192390d9daa103f582634110404c1b928d4464b1dae88fada45c40ff322f49052b57789d345b925f252665b8c1e6bc8a2dff1c043383aeb2788a65574d2873b97b5486081f1e9d3e3c463edc01d16c10412d98b9366b8a3ac631b63c952c7a8c36ae329feafa98067e8a2a14cfdc60c8b82948d2c1d325b67ef1d6361714ff4959a7d17826b7182aa78128f15099cd310ea79c2bc77796c0b17a7c35ec98b911a791e9d78196149379c00b9ab09a89eb94eb2b4e9347f64f6c37cf2ac2c659e637c1f23885bef5c4f82f06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"146a7c262a3e5e76f1a393f0fde4c0cdd6a683e0f96f61db61026674022e7156","proof":"b8c4bc7272e2459dfde25b49d89183541406417d2ade4a66bf7cdcc21e7ed255cc737d3d3634d400841675fdba66f3712eeb6c5653c12a63c2f02b0fa811603170f9a37165b105ba79e92d2639db7603554ec067c4792328e635b77758e1e039bc2e928421afacdd92f57d4dfbf85145a21081ca1c4c7a262cbf338b9fe9eb00a976d8225cb502d4d6a262e556e9f506d176ff5081fa80e2a8ecb0af05dcf607b02c760d6e12da48e43400ee9bad706ba7823f4577297309fe3247fba21f73054c7969a6e1716d7b793bdea06b82d212f5556fc9e4a8bec65535f451bdebb70d489de6b3580c92bf767c6cfc5deb3ead40338e9a0149d1ce1a4babf09f8afa05140efd223c5fd3cee7422a4bd967b459b86d7839e97302ee22dd0c3297823b222202addab4dfd30617fb9f848e3d7ccd6bc7fe60d21a9b97eacd9a3b66ca341dd015c3d00de95393b7fde3d694b67cbd87b61af8a75e90bf486df949e36e0362706893891f01cfe57d8c12d649b30f8fe0e1c49b478f1df39525788fcac1e25e12c9eb9c0590ca259aab6cc090e19585f755042e1c433b376cff34d747f4a13dd61c7921875403080f209f7d5b6a736e99fc1ce51399a4d75ee2c3b836e07d4ca406e4926e360a1d4f7867ea5f352c3bed5b0489e8deba254a13d87f130af9695272c62ac34e1e0aeee3141a68bf16d31f8dc9663a260cf4143104a6b799e05af49b22b08428cdf4043eef4ed304fd064421cf189737963f9c18ef624d69d8193e6e3219a5e9c129a0673ad5c098023a4059f7cc217d8fcf280dc741f205566b5c5e87670a4eefc1dba04a06ddc989b4bae9272d27d0406140716749c3a9b65d9b2ca212ce162c32f4d34b7d508c4a34f531e6248e678e78a2b0f445e6ebbd0eee4fc85718577a685c5ab6de888f33c39ca82de892df7a86ee3f066b58e63107"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"744bdeedb8594984ea3ddbe5971ea954ce543a823b82024bb92a02ff64588939","proof":"22dc76d58acc4947f03d5647417bbea7c0ea81c5ad0cddd46fcf97e6ee22503dfc739d7e4ef042785993779d4f0929ae6b5da1dc30b99ffd9adebf9d90ab3b2e609d9bfbc888090d0dae8123a5da9ea3c9d5b485f2cdff7aed7addc476f29c6700dec6ebc303b88d0617b807245bc1d3d99193a6e7bc08fbd3c455d669659b12b47633e236292633659eefc1f61d6c240571f7af373b5c30b9f9e0d07acdef0f7dceac72ea1f5f54b2460025a4b3696a4dc78e9ad195c69901e04ebcce9d5e08c8a7e5cb1653fae06e3170f06223097c223fa8365748d11143e98a186167320ba86d42b9ca17a92c18ae59184bd220659667f2a8389b25ca891113471d619e3b5c2c93765bd1698cf8b0490bdf51a66819c02e5a0c9027d63f77de303c4fcf517e513e5a28b071f6beb6f8bf1fb81b2c6a52b201a3a4213fb2935834b6882a1c9cf99a32fffc278d49324f939c09cb7de0b1f1c3a8ad2b813f2c91fa4bcc8c5ec2fd69144e88ca91cf90165cef25a9e685a0e94f1994aaca3cb7fba687e27d40b2a4d530088740b6c3d15794d9870e50e509234fd16e2aee56ebbde7da4ba32b4c967cdc884624658614dc0b54f3c40d4c174ab0aee56848aa595e7100c0380d92a6dcf981529430776c403f1bd43134f0a3f1b79a3dc65673bdc5be7dd43c1102a5c258ad428b7300465fd99a8d5700b15639b0b5bf60c1c9571c956f5c9e5b72f9ced14ba68ac7238b61722a1563e691584a885ce3735ea9e453bd5ba34b56c21e7ff620e4f3692887f105d7d03bc4a7c86b0b5d1b51132904fa84c2eaef098ced3d7eaadb60ace2bbdc6c60c29de9b81bd4e6403dd3b657f3101321d06a1ac1794cdb3179f1743c0edb00229ebbad9ef62ad886d8ac1a60718e7487dd06001dea6f401633cee8a7bc2fe7174295a10c22a46e1671fb84e921c48947332403"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f073bec8116d2e8d092bc5d4b4a30c6aa0c2521034003bc2e9bb1c9140e5d454","proof":"e0879858f0d06e9ad303b97bdc3f6e99b06cc5e4daf882f67bdd8a30b62cc27a3e8dc4fa803e6348bfab2251ff7e745a3ae939c6fe44ce9bdf6062fbffef716f0c326c482f67c97c0d1f4df9c28170c1d15e1839aaf5f64e72876e0c8f3aa92d3c434b37fd04b1585799ace93e01319c7adfef0eb8bdcefa89d399b14336fb13d669456e2c5d6e94bbf8690618ac473d874124b1ac2c74b76666b376faed600addcb6c13ae8561319c36319a38c8a6cee0a54cf141b932a899c84495b27f6705b49502ad0e9fbb3ac20d5aee28162f0c3046406daf6ed6e2bfa2f29db7881b0452887f8d4a24faa3bddb271589899d01c1f650ad762412cc84556ee32acba3085cb814b85eb1c0e3932f12ff8aa725eb76c4c37736edb094479a4e2300bce6595e221f1d685f7a92cb452d074646a0fc6fa3a89479a950b5cf8a9e512d91533b4e1ecf6b606eeee0e6a8dd2cabb9d301246f15bc9d33424d268685b539353e6332cd252e6cf083ee4da21c33144b93401768170057ddd2d5d1aa2854463e2450c65f9063edddfd0fe9225abca6051a4d4d2c1e318d55d25af4d48c3085fea30aaed406d95511481f3f1909e0e422c9edc6313d12be1b81c07aaba29272336b7d803cb962d7d54c3672dbfc851a69c51b59e3c94ce0d9f7d63a1f2116fb601c07749cbb9a2592b30f4ee35bbd315449ff6c1ca93866956f9e5e456e263f930c591e04d73fdad79c40ebb51e5ed973206a07019380a970f41937fc202452305b79eeafce243875806502de6903f03a0741ffb580c9e74ca93c647742682be328187ae0b26d7bc9c8972595a873f08efd7df19aa39ab3bf97fd969ba683ecd55a63b75a1a7c848d5c20aa42d949d7cf87685ee90e757fcfca5915200a5f90a911006d41d92fbd4a58f9e9c62677d9844193260f0889f53e675cdd3a20a7b7d0180f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a2b1f89d6eb8ec6f378d4550c3dd3fa1f55cfaba2b53ec436e6db990fc113830","proof":"6891727faaae35085e617acc2c08e4887c8caaced88c05a93b933ca5319aad17ce4ebe73527b403c456b6104bd9fca76636a677bda3c81b90b87772b377e3642e0c9c221bc127cd0037eee9dd2337148863fd2a3d1f0178a8f94b664a0d4630e90989d0b8c41fc0dfd1758432658849dc7e14d88abf73bd53461e56f58ddc25566346c3662ce47da50f56f87b7052251225fcb1036a7e80b1b4647fea1c47f05b1361b6c1efa41e0d33223d560cd146bc65a8c451131262c85703386dabbba0e1e2fb07b70e67fa202c71140b62d9a5016509aaaa67ff75278634125c70e2c0ff4eae60a3a9dac749a07e3d4a788206979fa47e5be175576ebb8cd0665bfc124a87a044b73591f55508549bd835f589b2beca6f28e3b1287c0920bc72842703534c5ff3cd9e08fe90a6b5193865c71471debd1d28fc14eb3ce4e241c6af2595daadf1b2ea00261094f7d8dea81deb7f44b69d5bcef487b73caea9b71744b80138a846e59ccdee1ded6259c94b23a9324deda2ff3703a80c77caeb4da1818e428cad46879e78820efd3e8dadb3f452350f49ca0fe1e1024db343de2ee5b43251e78271e17db7ab8f2e99a08e5fe32a6926077b38fc651dfa2adf3888f25dde154ec0fea3bf99da78f2d43f0ecba448d09b69da08ecf5e4cce03f76dfc2758dd0464a1bc008ac0b1f3f7d9097b024f6b3f51d9bc26d6b1c98a938e1306cc861d17aa75acc56f36779a016dc02a02d8292b2c92302bc66594c31f595a90d1bf33701a726464468e5244e80e1f94dea71bf0416dc3df04a10df52a364759d792333c187178e99a8b3dfd7371d24bf5c84bb9da3d00a44fcaabd5d218d7ab303e8b41bde93c236f5fdd685fc8aba9583d0f31544f511339d578c52d60257df4258c091bf92979749bab5a3323a63c6d652600257cafc63c41a6c9d3c261c5d8022b09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"382669b560fec21843a788ee7cfe69627a20985ed575f021820a80dcce1fba16","proof":"9600778c0504e363da54348f608cd21c6d399f0205e3bccc8f7f599334936e7c566e17f6008db2982bbcae1f0676895fbf34644b12afc392f8392e40485cbc3b5611c3671acdf603240768d3db8dd3c97794d3d4c1987ee5da296ce0c42844335c9e402f7fe6673c281dce3a21b3e273f3a6a6b48651e826d9a48a00290bce0843ac77f944176618e2b4a1d3a5e0b62d4f9a7335c513837557f285861b5c78054e16e2736edbc86d507558618b14fad28df35640cd2c004d2bdef7252db6e60c3b803b96c0130a215a068ce99106be71510f5d3566b13bce174ab311c0e67201fa8fbeff1c4dc166aab66a97ef16e294b1c088fd1bcf42c584ce9e517f8bcf02d6deb82b437064dba368031dd905f6e429da11de62d346e58700a4e0cc66e93e7a2abbb5e86fbcac29000dc8d18a5f57266453de4d3d17c8c565646e7ab2b82f381437282c5d0a7143c35d65cf72128381d2717868c5269f284749b155d8e07d2ab83f6419b53df03edc054bc297c1c2df165b2381f592516e12dd4d2918af114c96ac269ab69cde53b3ff3073d1f838099f47c1bf2413b6872b86df66b8cd70480b2959bc64efb7014574fa04e025765469c3efa7b36f95b122b3d559c73f0048b5ac0e51695888cb7f5cb919b48ee9f5cadef05bb61db93c014cf13215d9324c7e5b53a1e383a551f358de0ee4e29e9bcbe912b513a10aac9da795211fd334c0e6f784584be2c69f037b7ae8a7e6be3d30c790d66767221ac119d6b48cc54fe0f3257201af00f40b9c46eae99436913c7f8bac15fc07b132668d64ab200423c43d2f4e23f3d07be38b0efa30c4d2e4fa13aceae9c73905ad870257f083d05b0e770616434396027b4b4f1fc657959016233870fefdb67ade96a89ae132dd065c0ffc4d693928f2019ec21dfe089c0105b4804edf09afebe2ccbd6f789c2b06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e6eb63d4526a7ec656cb25b19a09f0237e1b2e331999bcc0e587d034578db819","proof":"f612f7579e04591a79d00ad122ea77adf4b648a5060d5bfcb21cd9c034eac83d5a3c61b10163ea60609c25afa6caae13987e4aeef440ca903526dca86caa272c3a39163ed8392bc798614faf6d06e4169efab7b375fa48cf128f95a26e40df31ac765fda8a5395cbf36c0b53b1a5e5f7ddc5d0e38647e6a67d06fc539c9ebe6284415bde9d7be7239989f377afb809d1681a9a76819f3473569b9d2cb9687e07bfcffe45de246a8fe48130c4c576a94ef850fa9eaf2008bcac67de8ec61fae03796799969eb156ec4d5243d6ae3429a50de770d53ffb260a2d6f9084db51ba0b4053b83cdbe3f558ec99eba5648652851d3b1995ec61e60e20ad7771b1d6cc32caec03b71556f71f91b8377643e82fc65c680ba734b57637380cd7e7cec52264c0f2782167115842adde201355d0c752c46890e118d3cfb701d9e1a65199f43c364cf9e0d8413a0b4637f3452a00f96f0e766a4ed0629992b26e6e9db21fff4e826a363245d713ae941e5fea054aae875263beb60fa10afdff9b6f88838526472a4a7aec943c163529d791a65c7bc670d5f6b5fc1dd81dcdc457eb2bcf5da40408f811b4bbe29c1ff9c65f48efdac89ee7991fbe8256642da7a5bb63dc33f73ebcc08a5d7f72134955a733084e09f76cb7c24e88d1ac7c8ad0f0a06fa9a98e63a46c18d913fe511ad2e6d9bf3dac9f79a830f955716e07dcd2430588c84cf35cac60d6248514561e0084f47cbe796840be48e00942112ca882d8b37e9e9edb211a0cf7d86ee387854352b8bfb47cd9b75b1188b4e276d86fcb61b81d1ded5315aec4d8bc07199f552de49b1adacbdc8fc3288cd9af81723942f1d4d30c94e31a79899e02a1db7e448f997b498aa13d463acf90521049905c63b64cd50dfeb60c9061feaadf87bf528bdeb6fdb9b0bcad09279b2f56dae19b9f599cc95f856504"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"96c27936d72789b2242ffc65785e18cc6d879950ec7e16c9a6b807b8e7a2bd5f","proof":"dc189cae68a855a428f18cc223865101a356abcdd6295104b9fde562e9c12810f65ce810fcd38eeac930b100821a3a09ea4ac6607ccd311229c5e862bf33ba7dd230c4a9a476eac6348da8f77e8ec1326a851b2ad12f9049d0c35586e96c1416547d46c1cb8a182836522f87e3438cb8dc0c3b9cfe80620febdbcdaf57eb8823f0bf53cde0047dd275c68e02577136561b9db1831e874f8f3ede8a351c34ff0de52c4cdeab3c9de7637e0cef0af7cc0ef67e04f7f040e72be1a424d4cb301c0f9b88b7ebd986d44b82f59f1f30da4737710df69ff181be4008f9c5c4ba8adb0824ab0d6aad3ded3dd52766468a26c22d5f80d31ec875db67e4423d63e1ae800eda948fa938a36018ba88dc8dd63561c0f2f2073adc033c4f7c038b4d81322433a00f1e13e8ede8d43d645ee26af34faf84bd6fcbb7d2ce67bb0501ae35c2465fb49167bbd079a011ad63581a5bc069164bebad1cfa07d825f67de6ec5e979b35f8bd4b1684b350fbff06be6d1081ccea2d272f8d392678d8d3a7a75b248c1a503eeef3b085a0f629829c977d3f65f69ee397ccf3b29dec6bc2e3b84277f9e93de8d8023d0671fcec75b043bbad01ac4d60aee7f60e6398126461375c9a01ca66a869a8cea09ba4b06d4bf881c0ba4718677704f0eb9c6f65455ef92cc102fe6ec64bd3458217a98d27338493a166994d290137261464c4e2a40b4b1b42588f3ec8787d6c1c8230b6bcd05ae4e4cd04ab9ab43a6fae65c8bc2905fd80fae3a22f46667690b68406f1c13d4949015a6a31b5137afa75d472d201465c20719991633c3edb4391c43dd8c502bf11c525a7831b0778f54b83d2841fc6e374d2985235cfdc851f55949aec22fbe9ef7f81e763d9e5b201ae033589fc8097d897700f03016d7c2445ea03ae18d10758c5caf6d897c3b4fa1f81296c9deb4206c0a9820c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9c0f41608ee7eb5c3ff15a700a04ecc563021f9318b2d69bc78830d641f3d813","proof":"cc581e53a6bb9f87bf304769ce94c829170866aca2cb9875de6742acfaed38166cb508ee86e92359f33a3cb3b2d63bdd3f3c51c6929ebc65d0108c38ec42b669d85eed5b3672f2cb56d2f3ffce35038d559a87f2113619ff56b9c93836f7493af6c75c5009a3f18b77fe08a4c2e7d4b53f27d42e082e2d286eb363c2b5404a44570578b5dd7ec7f3a5ae88f4ace9982b8ea94e81ca2051f1c9c856f46c1a270144e375303c6b9a500e3e4f277e8adfaee3d674bfb6f748e538a3078a562fe606c7271b9321ae9321e7337649243412a9012da4d5708f47a8c3ba15df2fad2c0f8842d06f2c1fee4754e3eae7ec4005c04047752aacabbeb1c5acc5173b25ef2c2a01e54b9a5c7fb841c14f1f5c1901052cd06872c286250cd244307c8130722a0618be6f57633fa5f775aef2d9d69dd59216f98d5603c17f9173c4c5db9e2f1c66f49b0017867328e3226d9fdcb7264396ab0bcad020c08948956edf0cd2920e6aed7a331655c495b936274eba0d38dbb82524afe6ffd0857aff166486d2cf6db488311c7d04477783abdc71185d0cb9b6987bdb6045f3f2f0f90ccef6e8537e9a509a16da1134d11a89e901412df38b985065a49379f15cfa306769a7c3842126ebd6c1da62cc40a5f7421ab10a0fcc212d15e9b003512e07378d37554b4463c6dec479cb8aafc77e8f485b6cc1cb969b95583fe3655ee13492664f3215706d109249cc87c4dc7189221cb444ed0d25158d83130ae72745184f6e3ec3c29a6374daa349410e3402e05c6e6697a915442731e67df51f0d7ae1290097ffd99241e8a3fb2f03f62c2ecbcce826d999d5767962cf1ba871b9918887d8c7369acf263dad6bb2457f273848f1308dc4f7349ab99df793455fe2177b58365c0c88670730e36326ed2a8df93804f72ff71d94c536bc4a2daddae8276ab8cdd45098c603"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"18d7af24860d83822d1633d00923ff886d28a9a6b336b0185e2b9c980d7e907d","proof":"76064e6f3b2d9095a5bd6686617df7a0fd0eeaa3154bc3458391bbea5dfc970d7a51c39b194865b215d438c68791c88b201e7d06734dcef24c90e17c58f7c943e67bdc27b62804a7818db942b4dd839f591a0dacd6df756e1cffa139a1ade3719e42d3dc2da0cd7fbbcb040505cdb320aebdf2a67480b209cbfacf53a2ab4b11026612b1baf75a6c72dbe178c5bdcef302dc8f8ed8e77b8cf3e50b7a7116fa0621404d0779792ff64daa3503f87b29a265ef5f1f33a7ebbfb44cc22ae485ab053c9299e7e6f8dc90982895b132cb0d8833375459c99829656baf4211be031d090ea0c6d4209b0a550163c9218bb040f364e50cca3cb1145148e72e52f43ecf38aced1b29036e4a1b5ffabf7f755d7513c96f05fea0029234d7b45b85defdcb7620071f585a0bd4a59d288009b6918b8e188de209ac7da5b11ac58cf80f13de762a104efd912082ea7aab42b12a2e654006221ca048fa8d5a1a8cafdae520e8494ab9a18627dd274f2907b369c1739fff5d211de690bd0c1604ae10c5e8b28f544e2f6083316e48a8dd369ee8f20ab27941cb766639a7e716f9efa60d3f81254e889b10e8d7ab9bfa23e45ffcbcb236326b60759b8d01ad5a4e515a4d85a8175920fe4ab758041675b29e44e39965e6e88f135262414bdd145623fa9bef7eba45f045e8995182c071f0f64c7ce2a8fa1733c8a85fb783db4f032b37151e5e303c14bc8ea0ce20e7fb328ae3625780fe766ba22c5ad7394227733bec9dba09cb7cb4c2340ccd14d6f0b0e264a203d52cbd8bcf0c513c5cf12715188cf8e0d61909bc2b39932032ae5eca0bb6a8542172e0d72ed20064a4a3fa9a9c608a173d4b54e3508ff406cb9d931805bce0dc69dc2b32200345e16b8457983ebdbae28a0c0ad419b48ac426c2fdad0aaf56019a049e2ac26b355f1173f64ecad655c7f9880b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6a204bda27be4fc60a410a08c3e5da2173d3f8cd4344fc76a67ac41c0a7e8133","proof":"c68d3fd515825bd67f2448a8deb7fb867f3eecab2fd2279fb7816e122ef47b54ca2e84ff7c010df1319ad34365d99626ce35339c03e474e5806746abbbbbd82144a34ff7feaed11520dfff73bbc0ae4a266cb6430d8e2a3de313a782f69278258a106e2640c633c59168e40bb755bcaedaca0c97465fdd15bd7fca7dd9203c20100b0a5d9e552387af49601a3477f22f63df6ab64f2935b6228500230eacc10ca51c58d572c198863fdad651180ad121e1c47ce44c551391f0571ef6e9e5d9034be0e797cc39b0f7d1b46a0eafccb03572ca98aee65d54d7386ceb092d45720364df1c259344b82881c41bd37d3e6c2c59f6e95d5880a22db9e217cac2638d6ca0a0fe4670928807d0e4e3a9a6645864da4e00e770111701450c4dbc2c89491a9cc50697bea5ec24a75cd70cebb77cdbebae4a3cb999096336bd6c27a1eb262650c66be7fe7aa917732e9f59c954f4d138d2b722797b308874e81efb10ad9005fc503f175c3063159b8040eba0c774effa73a5446ce4c5b27127130d6a9f145d762a2790ba688792635bc358eebcd56dd9059f09e72497cd5c3605569ec6886c965683f425033fc1bbe2d064e68b58e31cd3882ccf0a235510312ef45865a81efc257cf461df260e5858e782499be248393fd23ac78f896215f32c4ce16f91733a480b7a08791e39cbc3d4fc33da99ab066065e810aba392c59d6ae2b74cf152aa58ac33d9af37c6e592b250f5dfbf0dd5d673480a9886f5115e622518b13d784c307bd15a71af5e262f43bcd4e0fd524dd60b851ba0482742cf085419f6ab58b05197b8a9ec77bc2906abf079fc97e253695eadabd6f437df0ddeea93cbd4664788d2affd214f1d0e986eb4cd27f08e3ab8ee268d3778ac6e3b3fefc74b03004da73c9888abfc9807570a25b7a415d563870a3030bb164a2fc06d796c780801"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"20f9328f7fd97024b9937aadb52892d98aeea7db7bb5cf8da7202c5ae6d2f734","proof":"b4166b1580474b8a0367bd864cd1f60618fda2072c4eeda332f1cf791d2fca3b4a260bebe6814be6e942b30158f14442969cd7277f1f1d6d34f1854e6ac677266e5e08a9fda103105c4c0a2f02b6ae1db8dabe579cc277b39c56a15a7de2f35d909bda2515096763fc8f1440052c994c789822e93c4fa5cf23eadacc69c0eb5030e48e024a1fa2598397276fd48cde1207c2b31690e7b1c2832bb0e73ff92400e5cf5a3dd249caad301519d2ba24647629c1b8363873203c6de4ceb7dfc499044ace1cd4e7f317b354ea828ae32dd16fa2fe9683883b47593f7d59670409710bf8eacb5e2d4f1888201cd430aed39eb4f8116cf6243a22577b731340446edc5482823633f0252a9ad6bf6c49b13acfe87fccb3f87cf50a18f5b95cd99c78c059d0f937421d092e302c303314fe9b86bdca11b7f82ab02d462ce17db9689cea139203e203b505571b9392a2c4039c54ced185c5da6e41d6de4c6555ff74b50501aa5287cec9ace9d45d59d52e87d3314cb60389484aa3fb53f6fc0844780a3530dcb363ee85b00f96d75570bbbf8ed5b08a1379136269d69e1de7e4130f38216f34df0ef406a3e04082acca6c0ec74933e86cafb24f9452771b8520f35cfa1320ae74adebf077a0f76f90b7cd5b43f9ec3ac6b63a837410539c9146b0b59e867dd4bb6b05a542056bce4d0ad42f428218ceed7ed2c569f4099bfe6f73c4c0ea10c4b015238523501b2626beba1fb2d98e74e7afcc3def49a4eec575b856ca8c2a9a801067d1dedbbd7f4b5b8af3cb72e352b1f89d76533e0f1faeb8640d7c1f4a4e9dda3747a746697a12c1a9989663fdd5bce779d60966dfc7e517ad9baf7b576c5cf806700ce8fa8991da358fda2c8a50518ade6df8d7c93694cbb61509a707917b149f8547a40facf009a86faac26b867972b213c56ab6ffd3cff9fdf3140c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"628d0ed21d65361e1911095808778f7e33aac37690df7727199c3c1e8d2f9222","proof":"00369b7d2f22165dbfa623aa351badda88c19bbce1334a2086670356262b3c3ffe72e2f1448ec7e84ac6b412710444a41fea1d9c2c744b756ac1600b2e93a870a28c823576203747714dfbefe33e447350d62520f436cc471a30daf42824d71b4cf90dadb6043b4b5e3cd53ca77d9adc4ebb7a6647d370d9465c0249d75157393dfbc6447b5c6db555efe95972e36091524ec243cbd73d8c8aae92551206ac0891f30dd365ed1f98afc37e639cbe3bc43e83ac598b67fbeafb98527663c92705b9c8abb77a2bad06e3a1cdcc4e9d5dd16ddd32e793222e2dcea33dfefefb9d07aaeb735d2a14873d946d243913e08655f9937cf4713c46dfe583845b78aceb40dcd2380a66614abce81f036c6353544d47acf68fb497ba19982b06cf8d231a7bf6b780a693c3dd4431ec58515bfc88a64eb3cd7fc8186c58b8b4a13d6388175f5c38351439f2e0020bc38c4f9b2811d5ef7ddb89ff1caba8518593ec701a1c20201b5fa174656504f2294a970b286cd3ba82f6ab1e014fd00c242fc24238e216a4213f0cae13961d15f5a04159ed379e49a8307b059804821fb60cd122f32e66e6ffd1cfd35d65d4e6f3c887e584926ab6e0502320a6786a71310a3898ed2f480022a9e73b882030bf7d6019f68ebf461a0825e5417ca6a78637741175ff1c51cee516b598cad02859f95d5a7059dd19596e6863a25937a0f854b363208e0d6f78e24c47a1ea7c8ec9af4772f039108a51451190e4ea3d65aae37198ce40443f3c91f7dd4fa3f5afca9be1aeda7a3364b7cd32eff1fcd40e687214e4a2811e1c36f1a25917f4ff561b631bc70784d3f503e91c083c9a8231b15ac2b54c905a4b30ff938c273023f7485e9ad561265eb262b1f4ad4355db4b31fdc17aeace4208b29f66765414f62607e511ef336d38a782e681d653ce064b010a7c6846efdb09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"38644e7f425954caba8559e4d2ee20a740a04742c0878354a712c3f5da525234","proof":"344ec5dc28a9fc0718bbe3aec78c53cd77d1fb2e25d2ae2862ede5882af88f3b961b1ee6318caf107c7d2482763e083156698380a19b5f457155f250acfe5136aeb6eaafa27915c4c9b9855717304e3abb44a05f96c5e35b1aae640107c2330eb8dd114be4c0a300267f2a83c53a7d5a1654f970d0377bbd10fa45c3f6cf2621d741b7ab28e6401f8c775b4035afe85e5030d95b8cfda24aa2ab057b138f4902e16fddf69650874d20bb6fe1fed07ac5102122b12aa35b1ba85ea2710de1ad0dc0b3a3a72a48d2dbedc2d019114c540a35dd89141abddde8c4748d5d018111014afb301204c2891e538cc499835ea7f84e99f0da59fa37c05f4bdc60bc719e2f32959f2ca9895d447cf7d964c5aa857dabffbfb8ff6d04a97b6655f6823da807d20d1f030c473e13c6b57213acf2438c1bb820c5ad718aa09e7b644e9dde610b6eb5f95da090f8d546418950fa17eba065f7d9fb4f4408a2813f0b5702b9d8529ac9023255dd262fcb650fd4af4dd676c4442271c2433b8f9d9802e765f00c4096e6a85eba902e54bd19f80bfaabdcc692f601f82962135e1d9d975bad2a9625e8c82889e5863f72475fa9530dfb968bc20c752d7857234702a01547a84f272cfa45bde5c520dd6f0e9c54beece6c928a4a084cebb1c695d4499b262d59145453870cb65f953c4fbdb84a1cf0fca69ed9e08aac8e41a41d23c580c6007156e265c7f56d79141f742c91e51863b78b785a38c70d3f95a2592adc32990802b945c5a2efcab317e22711fce34a32f56697cba84db038f82737f806f87d7a98036723c23da6834a3bbc5101878c6799b8b5923cf81cb1e70727b1d7bcb487f788b68a536a142198506ca5d49b63ad5282b6b9cad7ae7cd134b8e6175286030cde40bcd4e77ac84a1a22d95c671f8812eea5b0c1bc451be48a30498f8edcd3ae1180b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6c390e6879c824f5ec222a96dbdc26d97ea2b8b3b023419b2940dac87e19a60e","proof":"f89de2acaaf1f0d9a3d368c25c4d4cc6c31ef35b698532bbcc5791a7311eca019694865e6054ea88c5274c03c73623caee76e44edba3f607e0a0e1db954a9c368cb71771709b2e75b27b83e68c27da4f7064689c8426540d862c04f43dd05d149cf0c17cb11e7f3fc93b57d605245b10269a9de0a1433cf1c57506ab9c20c5257812d3a7eb082ad2a7c0555440b5165885d0b1523db46dbcaf9dd6ae3ac5b70c1ccdd0c79a8caf1918ecef865d04f66aeb5099317de524a1b0a0830bf326970a28a37c2e5abaa209a54347916ef9a92067be2b54a9026a6b0d021430a56d1808acd76587c260ca93c3bec74806bf785eae191f5b6758709230650b7526f8d718fa9470a9e5d1ca2beb51eb9e0fec76b8324966370858f285a80e0f7cf5dd0854d87e77e749c12c0b58e3b99ee5eaba3c3927f833675d6c4cb9b51ed55c5ce108e2754ca2c1d5fc24789e9d97676e9032a6ade8f159526ab2cc1d15764366bb47c00308ef062f078601680d17d010323c08a2b310e2ff6c8206a13818467a1a52f078c054b990d6a9a89f8ef4de554bfde8e20aa022499daba245c97998bd760d2e9264ea899874067300f7edf211c6a0356f4f204a02af0fb184876425d0620e70ffbc6a32230c0df01dc87a21b4b74d28f459dd8c98efbe0e7323d1bfcc2a795c6d8251e8565b73e2b6a58c521f239fa8a61f869c30dca63cc1adb637d3dd40880316d107b10848b9967290ea4ddb7fe2d374679d87337e3bc1008436c1b86cb0b50697acf5c3fd8b0e644a3d2a28de8d53792b9e817e633ca25901b150f35c280fbb57786650faa52babd22a2ff5b5c1f17df3efefef0bbd155a8041d65100f0aaad412302a19b8dcc2a3a50a92b061feb200ad72086a3672323e6c11f4a042daa0d6e0ce75c72de81cb264e401f89c0f3f0178cc12f99df29755f363cbe0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4c319ca7bbfb713df5b87e9244b32b63e0f4e2ca4cb0cf94014186fe6b150e6f","proof":"0a4fa9a3fae2ec3c5392a3e85fdc2fa958cb210e47d0e8554e007ca5c800c069b8dcaf0711f3eac0aa68e6ff8f7c22a79053df82a6e4e6ce27196b6efb5597317aa6218d06b638008483670429ac795591d6adf348b3becfc1232c68f9a3b857d4c9a68a7fb5320110e58bf9ca87a7db7308e0ed1566b332fe3bca620dda505404076831a9ba04d2a25512848fc27aa933181f98efda7c31946ae72692be0f0cc9cded6889606dc1020e83b4a87f3eb12bcfa7ba366b5fbbf5b99ab2a1a7930a41e2bd05be7570203428e5147b88480a042c7d0ec89a78b07ae45a0487a3f70120254a300f34a888e93375c1660ce1452a6b9269a832fca6a0c7a71d6ba07333969c85e095337ab390a4aedd0db53d72ca9e4a7c45f61213c9cb8c9fc4b95c6bb4f1fb638ccffc2a0a985a3afcc2e36bb315b8ade184c5af54dbcdfe6b4c2f0a2ecdff2d30beb26bba782d646f991bc2dbdee8d6a25d9f821bec879ba263c84ed672c838865e08ec7abd2efc6d6f0587cdeede8c47ffdbe4f3f0d7d8840ceb75202af036f5f3a761fb5d9b6d305af850f2c876963927d121238bc65be09adc4ef674627f2e45f9e4cc084b8aec9f1b9eb56a433caf10d82b1f7206e455a587255a77e2ea8ac67c83228702b4bf5547a987eddf650702cb58abe927725fb77d134a50823496c63a706956ad372cd00317771e3a5f1402a80a97cddc9edd51ee4cf8f7e53c3c9822db37faa64804060267f6c9233048fcaed674fd53b40e134328d4c12a821011eb442ae9fe925ff58e2920555b5c7fc74870177f021bb0630006962daf6a8689810715e24d0066361a32cd42fb0dae825b180dd17535b05c997450623b16dd5b28750698fd483d387fab4e7ec6cef4ad9e9ae5cde9839cb8210e1021a673b89224e73f52b69ccb82aca64b70085f90c53557517dcef5bfbf6f06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3ab5d833b1e86c225f4bb0c2abc7d418c785b4981fbcb549c89f79d74e42f23a","proof":"266df48e55557599f79ddd5d08f3702ee52c84cb23540f5c39704fa8994eed18a291449c4b81aeec8570816c4b8646acd6055698eeb934794fdf9221326d3827d8155488b6612a03bf95eff446bd3a907d3171f64d979b287782e68d8b8ff134428a18b938637cfc817c8f406fe47f481e9f4f736291ca77f2342905d9c3f529d00b2aac5b6926277399d7ee425916fcef8b0482d607116fa6858ed00b6b3508f25929ee6c532973b5235f0fc1785b6658724ff30bb8fcb78bbab0fe48331f0a469cb17065d4770b3bfdc86bbb58327f671b6768f689480f218ae9a07c0759034691f21eaa5e448e3db01398d44a5b6c18e4c3fe1f0d346f4579ac0d6487e166902d1ee351ed2fdf98e9f82e232997c7c1af8faaa9259222e794f5bed319b558fefd3804ddc1fc996ffcea5effac91cf77c2e3d9963564e4ccee2f9bf92a12135effe03d39846a3cc8ee8da69788f23680b20b67991be0b49e0c02c814bf6c526a7865296d89a05f7233bb6c547214cdea10f30e49b4753099055123b5b302509206086cfdd8d2623222288f593915c5c8dc418fdcc31f7f6f0ab1b48ce4692594d59289243b1e4d0e4179246d3b72e47ddda800fba7abbd4e85eb953c43ba19600fb9a00c5984ad562b949632b053bd5b0b1cfc5550caf68b0917f47e157b559cbec632049230ac831ffbcbcb8eb7fbc0c9775494d412b65ab74a7f9b41cc598c2bb76bd196d3ab168ec3cf60d9bb726d145481a05c62a7536366f04dceac5ed80518ef6bc67f38a3e8061568020d3179f8da5593c7049ff0ba69c07a238918dc9a1095bb703b90a3e86d24b39a1ba913e81a09b29da0dae523904f7239dc1a0effad95ea28f66a3e884cf4f33d376149238cc9635da38c0717a30b72ab950877256e6aed8bc85496c48635b3c4dbabbb7537bd8c3cb375b12e7738e280470c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a64bd0ceffb3da72d23cb2149c98eb6b63cdd87d7b6b14fb553d14f761ac897e","proof":"8aea218666ab923c4fcc77ff987ff834302b53770011f23bca2a6bd124b66b4508916806adc12d20419e04679b5e5a6fe387249ce31449c438a9faaa8084ca44a653d746656719cf1aea4e0adad45f36da12a9adab3c71c5210462224dd54c54c8e606677cdd08f1e9ba5577fc45581156b0f9aae950cd57f71760ca0092536b02ae186a1bd1b1eaa44d558ec0ae839917939a42e62ba11cbc2b9971c9bdf50e1862fbb6efcd68dc88781a39d353ef8b2cd5fbba4854e6dfae0dd39bba188507a8c0296e65627ad0345bc215d7d22026bc92425caed819cf2fcea5ef4deb7f0a5a9ca02858b256d057bd831a2cef40dcd9c99ecd91a55b9018d154f6e42bd52ee63837b147c6aa078a35fd48ab5e16597c22d6da607480a59e024e8da0684e66fe0fd82ea53816391eeeeaf7d2c0339c7d217b75aadf82224b5a4ecefa99627f7c9f52739c091fef6dac590856471c4955ee332cea14320dbbe49c4413cbaa4222f854685a51f2562e8d02b7461dab3cc177ff721886633f737912a7836c05652e5f910f48483541d04e773f2e14409b0ccaba98fd3a375b0ae818276de2dd6d2c77cfb7f3e99925f68f457e0af34050519a8050af0b18b5ea692fae631ea85646f8851c5e45b2e3eab4c0b10a8d84d6b21547f24d090f9e27f9bf140447323fd4fb10e04bccd6e5628711635c2fdded4fb9aa29e4fa42fdeca657397e2fdf30348c24c18a7d07c6fe40d6d9b04cd5dd8c041b39618acfaf6ee6d58c60e10339ae46cb3b2315e4b5100d6b575c154619692e22ad0bc5dce670f6e3c816159e473eb12e54828f145bde8ff2b2d12d89ec9d6ae442d3b3888e18224c7f2c39231fea638189aa7b20467a77e1eb2a180d2ce9d0065db2f3b733f65b81e94148fd0e2d97a3d8eda0ccca098e0cf067c63c00ebaba874e959aa5614ea6247b961c30c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9e5646670a7ad683a031134208704e33895c33c9f40e6551c869b31ba1ca685c","proof":"a0a40474ad7eb493a28d42fca3545d2799c53c6fc97c895a03ab23ab52ba5477ee25abac7a2c3a436ad0c5e15abcfa9d91eed3e9cbf235d49c55cd7044e9073aa835145e02e5c365282e9c790549d0fe9bddffa198d62f53c068dd9cd434ef28724663a4ebf342c41ab7c6a243280c00b4e7d21264741d1a34cbc4019c5a3213968daae5390396621d9ecc8e891a36cf18c338a71f3dbff6bb16432e6a609c0feedb36bf5aead4e78e5e5219167581e59f4076a82614cc1a775adb2bd132b90f8ad362d3770a959e7c375b7865b16c0b9cf934eebe5400152ea1fd4a03bd540a5ca416a78936905da1df2f74b86d66d5ab5a3f9e3dbcf35226df7e5633b3e442083c1959585077489a03c4fdafbd05e8993f9c3fcea596690f87dcecf2d80e710c0980d008b85b756c742ed4487b89352651e72d3bc730c4f693ec0274243803d220ea10be0f2235179fd3204163c44a2aba6c3929e339ba0428ae099c743b52461a1a0b52db889554fb95e86bc6854a46a351a2d736e7df95ff511e628f3517eca7c6572e334b61c2496746cec13e76fe4d5141d5b714ec0eacc8504a90d839429be22c6584a1b72807058eb376d0a9af6a4245f6e54a4b5f1ad6001c03f1629e28c925bcbc4d8fa7b510d2c7aca3e469b4b8300ae544bf42b601499dd4a86deafaed9da5c4976576437f61090cedb590d454a037d927e0f54af36418ac0d51ba5961f2238697504457711a5ebc72a4f849d86019910438d08b823888d64c689c99da44f8621a3d7e3e0ad63344d2cbc5bd7904db883b3eace17cd8bf01107b5010697ac3f43dbe1ad69863f86ed0fafe5cfb4a4791351d8e6b9d0f3688357a5192140f22e98d0d5bc5d6f82fb1fba0eb6eb6a26f2e39f6036970419cdac104c846a09252d6fa9be645d3fee1be60d59506f917a2d81f8ba1747f1f1b8f4a0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3c4896c3fb50def89b89c13eea04544ea82ce33afae61b3a4097236bdb9ab152","proof":"0ce4d3b83fb5824c810ddebef7b2accbaa41cf2277e1bd0faccd5f1701a9e266d44517743cd8cadd5ff871a5e3a5e44c53aaf5168e5ba9dac75171fe27249c77dcccc6ee001f55ca074ea54b8c8861e20314d215220c4e7f30c3e1b7ea29f14936ffe5c03932bd48efb90e421f37c690890adcac9aacb434e82dbed262276209077bb15b209333391f5bcab477b64d9aafdd839be045c14b2b49c6912d1c2404615e7a3678497a02bfd5ffc2c9d9009f8b66be9a863286a23b1834837d0ad60fb6c30688a5affcbb00cb93da975f7c2b37b378559f841f2e04ef0f1d363a530e40cea7385c6f21d4a0aced10b21de864bf264c35838fdba6b2f714573223295d0830379e90a2b31f7ed58eec41cfde07e43c8d5e115d1a64d438b7f208b0232b5271f1226ecc019dca11515c74750bd716ce7e73caf5486fd98d3a6850752370c0937d6d206ab98d092d6f9bcbd350cd53deda1cc8610ecd6c64b8a4d9b42725140bf1f7e1319ec98c238e6c8d983f135f498d4926851916d16c514b016f61162c6193f35a1d17a622af0c83686626d237d48161177fce509c6220d3dbcf7d6e92c2083032889804829e682eaf8e6d62ee6a4dcc501ebd39608f5df35d625006bedce28fb10c69c69a9d66fa41afbd4aedb05198969035a7cc0ed922d52d3503a47708f34b7ccc264a5062ed51b61c5497672aea836fe9aca6a004006e7af93d90378eb29d13035a082f0cecf96a7fc4141c7f789c16ead98489a02fdb77973422cbbe937ce7029c6fe923557407412720fe687d5d2d17fd9823862e0babdf686ad48784518874f1ec9d295c68660fe73c6333ac1407af13024ca48a7814a7441f96dcb1a436236ef2280898dad964e5dcc796c02f043b40cf16a222532e40062237c2973f995d69a729333ea0f547e32c62a91a4f87c2a65f57ccaf5aae5401"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"80058693e10841ff0f81f218ebb5630f81f8160e6fb740b9e712581059b78217","proof":"72797af97d951cd6ff072710cf21295ad274661182dd7b8eed286a08ba039f43de5055e10fac24f8e83dbc2e0eab1ce733e512a7ed44097cc8ed6b1f685c23682239ef77f21579fcd018a1b2ba32e8c639d35e01e132154a87d94d93a1f984798e59c361ddc5b968af9f658c371ce3c51afad42383d763e03d8c09684841924c75891ecaddbfa1d08d6ab071530deb69c32d5b6447a0e7b7223beaf3c3963204b6bed616fbb5cd6cafc20d8898d60a878e9370031f7db5625104e84531d95609eea5427253fb3e1e35f3489f8892cbe8dbf0c68b4d88fb4b55ae9ad0972cbc03ea942e216a7f769adbc12297e1959da89d7cbd8eeedb08496930d887c2efb31538576694f1f96deb55ac216ab63057694438450138ab04ffd2487963d2177e5976cad13fbd88131600def156238b3babe2e0bb597f399c4c6a3273e0c55fcc60ae97e79cbd4830e34d066602f813ea714342ddc1f4365902b68c7ba7f9b3a06ae6ecef118ccc05011fec32faf437f3cb61f7182dfc9a6175e092fee937fbe25aa21ddcb292f64f3a1debc86cfc7525cca3465ed0baab03c6d2a7984a108350505ce39cfd15fe15e1a48e381b82db3a414ba66adb94e46203883844d9139e0a4acc93d6f37ab89d0c4c315031b9f33ed02bff5edf931e1adceca317d490b1f02424cb4480d86bb32da98612d0d0edd473cf19e02d9d0c7763878d04935cb00009a26f243c4f2cd5fdab44ee96406a87c31bfe057c4ad3a506f3f0c3d46e0b2612303bc0d0e0ba90db60934d651b28ed2e424a6aba76f979a54c98ee88d62e1350a43e4326722d66b71260ad76a727bae10a91a37db63824e3025e6201c1c6ce461a272cf59833b09e48c93f45d09e022b082e596eee11c039b5a2b65a4cf7f80b90d58723585c4e88a3b946b0fa76c2eb685d514f5d840baaa31d0d85670e1104"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c6e18df0395789ed0d3bbe5dec8391c8f397386fd512a04f130c21a09c0a6809","proof":"bcd1c7435bde6736574fb0dea2acffcb2a69aaf2619d4aa2d79e963044bae94fc68befb760ce63c6c3367da987a1f4c796b66a42510d7037a9ec187155dfab1f6622a511a5c839486962d5c3f99f230f6e8f9f8665a8df0d45d4020727ab034a7af3a10804c893260113ab05fda8205793fbe07561da4a00cf1c607418d0993b38da939df2d3258d297f2932c1e5e7afc335bfb3996cf37210cb2262a90e1307ae6c4958f55620fc02128d032b906a6722468380535264581a941ad4cd6e3e02135223d4d52958bd9b916c8bbd285dd9605c758ca965f412d956183cee73700bcc799ffbbfea6391a3aeb620c39be12706c4b73c8a3348c656321396989a9952de76aa7553a9d3c6ff558fffa514b425582e1376b5092e6a2188284d5ca8c14c423ff34c5d863398fcced3bfdf9e03d3b491400ec1d073ae4751f3a939a0d927c06cb783bfa89b2daffbf1e8048614b425e247ad75af14b33849048746675750680e4dad9abc0df02b4ad6292b34817025418641a4a4aab7a5729650fc3dd44210a47e04b9c70bb8d053173501be2d5b4d410f9ea1b3f255bd944cc67d7b5e1814d9a43d3b0b1d0c96a2351c78d0cf924f5b731f93b4dd4c3c31d42cf7c7ac7ac2a93b89d29f953d7d7bd3266da119d0e81004cff9631215d6b33bec1fb8037ce4d7a66b10ae83132e647761575cbe4a639598215dea92d2498531be813fb74326ce936ca9bab5289231ff677a39be0313327d2621c0c661df776e5edd086e6aa405b41220b17f741babd80c503063884a58b5748254dabe85b3a2187a09aa5f34c32b9ec835176c4f7f0733cb8023bf3746ec55bb6866a4486f0585d1bcce2bdf2c8503e65c1b72fc18a195efb13a19c95c34863840a955bcccde59fa2c290bcc0a3256d1b0622361e649cf6ba26f74ab81bfd7110388236b73bddd8ebd3205"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"94b8f7ab4f34086908cc21a3714cff575000866f588a163d03dff2529ad4bd68","proof":"ba61d30d64a21b59c9afa92339e9bc7ac125818938b8f546ddefedc04eda885510d5a5b6ee22b87cccf48be30372ee5982f0235d2d8d1117f27d31cd0b1358150823ca39680c112834653820edfc81f32b34b0073ea8817eab892ee954d4513f0cb1a9d77da0544ec5c2fef83d68f5ed7a8ed6cdc33f2ee8f9052adf401fd15cb4a3120016e7a886d862b76ff8fb56d8bed94e8a37910b0f6574d20639c38f0482e491d9d53096f345522699c0dda73948c761937aa74d384a36e472caadcb09c6448ac0546ee5e20b5fad0efad661ebfac0b8298ab203c00fc22a93f580a20d2cf66b4483da833d534dfdfd20a1246bd1bcf6206d7bd7a36ece5499dcfc085880cc1b594b57f5941e361af7705c285456cba2bf4e9da7698c0d0a16b740702dfc9d177fab1be01a739f88c92c321f5e79988281630485881bf39aa421e15d529cc13e3dd42ff737f48ef1d11879ea5a9609a84ff66b27bd8cffc42f0664091920b2cdeca9ee54c5f3d5f44e3d96c51caeac86049dbc6eb792b711e7f9ee1a0ae4cf3b7c5c4fa70595628ac8fde6453305b589ddbbce2f9293b1b93442f2442ce63854c2c278f91a4e317f861b653d6a30ac5095abd9dba78050ad98d056254d1237936b90eebb4e3a95098a160a631988231a8c3eca222353c6420e257bed0318991bd1c377252e15f4c703c8866025c3261d47d8856d9a97a2480a4cf4c14af24b19eb293d48a20ba8381a2dc30ef41568dab556d7db6d1becbd8c5ecd071c6c2a818e3af70495885c7a45ce2b24a074ca4bee67ff12d39d49b2b64be0377a2633a15457e2f300f57a4ed36244288e0a2f56d08a8046f337dd9a825a6266044e53435e11d654d2094d5328713cc6ae14004822ce0494666f610b7ac0d747034df6714707055b812435f19702ad1caad39a5d654c7124e3123642789d36a10b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"54a8d784444bee7aa3426ba2a9b1454228f51f1ea5d13c113a2c7c58277ac756","proof":"7a359ba83e9d0bbeba9cf89f1c77a28631ff94b67feb956e1bf8fcc0071abc4206898223550f20a58dfb27f966dca6b73552affe043ac4fccfcb7b793646451296d56b507f06b10d95ed9c1220db207538be0c0e62b20151d4728072e3878e7f94bdb2045c1c96249496298cc1593aa458cc76af7c468ebe446350ab7854930026cb587522c118cba98fe702f0da09621eede3ce8c26cbc68767de759a52a504050870f36572b36676d8e5b49ef0265b8ec4b454af58541f3ca49052809cbc035fa73307acdda5295db74edabb8c2172e0eb56c794cef69c09bd7f6de0733b0948a5ad436044fee3380e584376e441438e27c403a31dea0e7542f1b55db6580ed25662ab96a87efb907ae802ec20a28120faac02c171b46c44cdb0486538f34be8c6bb71ccd9778c224039ddf05c92845b59f91714d5a57b091092709460ca3552788f28c24945d2b2c3cc85fd12d516e84edea8ad77660b09c4d7ec1542227aeadbf2e0c1edfa939ff40d3fca7b009c84b760252bc56afd30b62f400757ce68f8a5e73cadc43a79c59e1083442dcc9e77bbfb9493c82a0236dbe7258a54b6575a22766c07eba37f17f739c7500b69dd391fe9674b14752c97cb6a88f4bdbf4f185a4f09c4f90dc7633fe3d8051b7bcf0049a96e1af665eebc2fbf7ac9dcd759569fc675dd6c88b43b2a1eb99cfe445cd21412de27f23a95252d04a13e24cc6004263c262191eb648fd3d0d51e06050adf68c8e64917abede134a30f6dacd04b9cbfd059d6505c3918cb73632f008640cdfffacd728f90b82d18516174961c549a642cd3787d6c83c5dd327c7f20763c4685621a2eab01a829dfc30a9c5b6010c1247b10d21ee5826bdd10d651f86f255dbd1dd0464c4e203d75358a0e009b051319bcee3bca9dbf8e263cbad7f398e555fa8821c2bac0131f9bf207edccba02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9080ad20a1f950c7d572a3a7eeb54d2dc54a615b904dfd74b0c6b8675167da72","proof":"446081f8e84bc81dbef17cc44f2b8e045b8d81d890698d0aff19538a05d76f769cfde2af3a19f0b30416c3996f1d35c1dd4493ff984af8218ca553352c7be4068235a36d5d79f3205957a07aadcfc57318239768cfb48386df46db6eed77236ae48f36dc8f892bc12e5cae06e8c74ea5d5486547db8644bb5f8eb785de5c3910f5c42e605b093910824d8c093ce76be03f434dacf6cce8027c797bfc78f04c028199929bddbaa23e7440ef983567eb3b91d1790b038e7e75984bed97f5956404ebe58ca3467326a2141202ab2de172d7c96fb383c1391da3591150a33c406305e01ae8bca2426762d7ac3af3197dfc91c29f6746eb8d64be401c9b3cfb9b531656236a84fa467cf5c280e8c7dd56839b3feba3fdb2a8a7984ad95f3551344c1a489435c3a09335c01c2c75ea0592f879243cbebd62c749940371379e51aee555380662ec9395fe9286622e7be2f2b631db897452ab0ca3c7d477299bb433ba075a4de84e09ee17ea3ebf47e29013c3294e83ea01d0c53275ff14bec6e86fac315c4d20234c36d1f61797190507c72e718b1dc2e68e434588c16a40a6baf4154378a609d8095b6bb3ad50282b34aea12393758677a785ab1270f3f97d7da7bc6332f6b537a7e26e18473fb64930f423b17c936835757b3087634269cd9d495b7194a1fe1562edbbbc3ebdace50a0cfd53237b476ec8e73457db049d83eb743f3c0e4de946d6b3ce33ed09ec0e05c9e21f822b355619c0493aa8c060c7d7c81e4c58441a6327375d6b1b38b8b0ae4945318453d2281d68350cd519bd0798f2f76380aa09061ff5bdc059b38e2a2130ad7071c62cdf391c5e02406ee78b3f320f6e03bb793a2599f262fa1ca56fff225c981f4a29288eb4a232a93386a5d520370d1978f08388a575b3618db030b768211f8836b35e56d4ee92d162a432f36dc007"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e6c07ac8b367cb03d384fe2e361496634437aa24ce428e856b61fe6a7d16f76a","proof":"6ccc785bd4b583ef3f0d4940a3c1e71953e89a41cc30b79941307d3339916c267ad745868d79d6ecd67ebbe6d47c7638ab6677684fd9c6a1f7a245f3198f21519c71ffb2f0fdb21f64b2f10620029e24c179ded02bf25f35b66f9873385cb977ecde852ab4314cac36991d20c4a25ddef910c57805632e78929e10bf6ba3527d5f297af73b2b0391960357c982f9668004a386c0ede6aa07c59a2bf8e60f5f0d2b8aaee102c61fbe8f80a779bd0caf28a8953e489ec86434efb7276d983eec025e1a69ae6d5774544fb2d467c3af869b20cc95846c4a6d1254d95cb599755c08a8303ef0507d665238b5a56b347e10793f0a2d477ac2bca28e7d2e9946a5f96164bbe48423e3000d27d8002e2d59b094d28c5f5034cc654dd32f86e949c6eb288437b7b48f14dc1446f03766d8fb4dcd140708e4e74380e8990810846bcb610e3021ddb3508ce7c7f0f34ec41f2107e1bf0b477f9ce591f38266ae2d1b8e9a1bb052bccb27f833785508fb272bb22eebfb08aa0413ff485b23a52f864888f34d42b0b3e0e009230be36a633960c4d35784e0af3ec53eb15b82d57cd9d8004922d834b99e4cbcc273df50d5400972b0e6b70dbdba42989814f6174ea0db2cd91f44191066bc1c6880605118aeddcf33fd095a951ea00aba51663c5fe605b5fb0d6e45d9f292d5ae1229458e46dcb6992139bc9815cd40946276fff02891b20c32801cc16e2f1da22038307a586bffe966b0c15039f8cf340ccfa08a4576934951942f70e8b4a8e0e53d45a3e6bdf69b5de1038705410029e36a9b564427998c598854c8a5cff441a386fa78ac2f6d99582cb86f74a3f4138b9426f0ec5307930f16c4296e3f7a783a02b250fceca1f3e20b2cbc1377050de9ffb1db7509841809e2063d23b740c9fd98192513ffd60152af563c877bab21902e95f3bbd97d890b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9e641d9507c764f99c3b06019bcb073a68d0d20c4a3b8b17367f7ef866697109","proof":"ea3d5429218225862b4991decc9b67b057a1a00011eda98f02cd8ac14b70e0543c51f6387e6816376f8e30f784f58287d9507a94d6c25b9e5fb499d382ba883ee22e532d560d25de524baa49bcbb3e4f5140ee7ff3d6d2d8311bf65632d8f2103cb2c9688b4bfabbfc655c4936ae727e64700c453052ea51bcdd2d729ac195420d8fa9e6bc98ff412a49e1d9c6af8f0e77f09cea2362d2808ac63302033cb906e99183931cb9320bb810abcdd74e26eec5fb738e247770cadd6a9a97b59c1e055a8c6b333f3451a4f45d4b3eb47ebeee9ee3958e32cb6bd1c55cf7de50f1820fe8e945dc4c16cbe690808559f4468b4caa9e99120acb43dc31c1976a66e3a316887ef553b610d23f51db8b740160a3d4fb1d90dd24cc80435f07e944cbc3ba2a3a970bffc51729f6a80428fe3b0c0212e509200c79367736d8674d71462da315e899963b02a4b3982fbcb3c0f4de94c2f264c2cc47081261c8b9fc6ff1f8d32cb8ee316e26f20a433cca4902a25e34f7ca30b8b166c33f05a3240fbc641ea15dfe4bae5da8960ba92f290270c964bc3ff4194a16fc86074ecb16f86900911a339e2a5c4d6d4820f6eee496dcd6a747ad3980ccf298466e692c2f11e3aa60ea7ce6887fee646ec72f0558f78be6853bcec395adc4f07d75959b74577f8692991dd6c3b965b0400f9d84e3529de88f55a809004eb1969addbfe5a50f73eac4c5299ed6fd625daaa3bfad9dffab685ebd75a06a579b6fa9d9f1e750ac78ec3ad70814a15d8e6e47da3e0d9a88d39b211c8b37cdaa234441142d6eb7c60072ced27294cfbffb62df428830177a7b3131405406f50125b3081bacb2581521d9bab4065d08595296d3eef6a8dee5b525b1ec85387262e18c808f1c7b9304d326e0ef0ea861887119221ce971851d5423469b03db1ab18a8d636590de00439dfc17d700"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0aeb7ed86868e767058c9c00922ab4cfbdc0344f92e34fc1081bd808f07c820e","proof":"160b7c4b9a4015cbbc37bb4215e70e74723bff557d9a74cb35ffb8403f39720af83a896197602c2fc61389eeec6a36b5b7111911c0452c80d039400817a5a945cc0f7efb3ae35b12a4aa06011168deb9d2626196bbffa6310b0346fac625bd6956557a619984327476ce45065ad8382aa581cac1629620c5df03848784301a2ae0001ca86816633afc03b29399649bfd36290f44df4dd61f828b920bc43bd10a57632ae2d8b1cc656d9205bd35496e372ebf9797a44092985ccbc8dc23c459098bd66d3fc2691d3d8655197cf55719e3006d5a76a02c7fe0b0f0a0456183ca0430ce025135d251449cefe9571dbc1fac58b7b09aa757e3a25426669cfe889a593c2687707d4fc5fc1f17b8bf0022d12687e1e026edc5953fdfbd54ecbd80824502aa39a96468fbc443f03b1bd83d7fd014be1adee9e06be653b82a7358f1fe744621a6cf737f3a0c1028461d7dff20e92c99525770503b63ea2f1755cfbccb52feb003a3f885cc272915bff2692ddc0a55e8b416cbffd174a08f343ca0e1606dd47f70ad6f923c81cc050c9cb8a53fdc7b93fd9deb7ab2448c542ec58ece7f40505b1c7966c9f7ebc543fd9fbd7333d12eb50eaa819e8f70fdb18758e467643f9cade7b1440d7813fc97b46b0e7d435815b7106a6155a226b576cd5c8a498c0b4c6e51696a60d2f671a54c55cfde7a22de6bb0fad9aa5548a0948e7c9db47236d24a1bb0d1edb36f1add1c8bf7000f64b2103161db7f4c7520e3999fd8f1ee73c8aee5642d9a314a006b909fdcd30d5fc0b4a1671e0b1132b4a2dae04bc24a529626334b52d0de6a7420b85117b2dd1c5a2c2cfc13f1ffbba04b8a17e8cd4a22faaa3bfe3ad0a55258511dcf089a3c8322030eaea39bf3093e3d0939a2263c053e2c182144f8cfb87c2cbce493535d9637316b3b9fe20d6ea6229b2e9f51ac07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aab4aaeaf9161a37005e5ca4baaa2be083f02a3ea68326b152b5ba1e68b30952","proof":"4a197a69b6323e609ff0c6f8813413f87187f207a2c908eb2f4db76149b9c2564268f79ff05f04600698642d03f63cf73f4372f849d8e6c1a29091f30a47670be429ccc8e5e38d3f6778f7fd74e043e7524dfb8419735b57f53fe4187ff0f570dcb79b58bd10c90158cbaf8d46fe12099d444509068c6aecd12d6053f493c64860da9e7e30519d711a813ed89bf8d9eda9d0ed8f207e161244bdb994c0d026056e5469ef139f7957e7e04e85ad9624796f2083ba4150e1bfd4cf1c802e9c4e04d0c02411d666e17f2e0a1091f1b5c808000bccbf9681ac00e894f775e367320520b5cef751332d08976eced0c1285eb0c549843a75984beeda8c049153445a357c6a26cb4fd7ea08e6df5fbdd0510ed750a62d05377c49b35e93ef00dc5451129089f41a53a233663914291bc8994acec0cf118f3f6c79ccc966d1aa79539908ac2a62fc535693a90b78cbbd2a1fa445218196b51f0162f03a89b088a9c11268ae6bd3ace0ff97e540bddce2e721da677a89405dac1bafd2e4459dfb6926d4180ca097fc608f47bb99ee81fdedd37663f5e47f83ce8f3d6b6cf988db86cd6c7edaf3e9ee158402cc35eb82480bf403ac925cdc4fb5c37ca707d555f9d850a31e3ce7f2d9514d20ba09b2400678a88a421b68b2315e64ff43c40be7c26748112dccb5bac037c65e1464d755ae20d81975568bf335f9183abf0b6b64a22b6fda037a5689ecfea75eb9a0b7ca5ef0bb5f6c963a304148e206d71e6fbbf49562367feea46a9a68ba2db979bafe136b97301a442a40a7c5e5b61dec68d5e96f5bce5e2a6416fb7d8909d3a95c207e7f86605f1da80982d615e1aaedc73ca0b4c28224ea813e4b9d28778c37b16f7c568280e99f4f38245f7b9cb0d2dc051e837dbf0ba7e24bacc01fa72058e7685dc9e156f98f3ec1917957fc6d92ff74a89c9beb03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"54bde9d428df78646c6afce2fe312193d5801c7025b01c18d3441091ea1fb926","proof":"ae9a7449495a01bfa04612c2917408356559d84d26ecb4645abb0cd15fa5422be4a36f2c7fb69fcaaf8a919ee50a4e12d6d8ed2951d62c47dd89f7451c8a9b2142dc8c83b8d25cdb5c5b2707c29d4a4eb25a7210929252161e2abd16c63e3c58b89d9b8781d34fd649f46109b6e067d19c5fba4bb8bd12ecb7a7394d8bfda11f49ead70b623323f812a607e14b020b42251eab4ad7b416f958960f3d63f7be0c49a37f966d577032537733466bf0d568df76da21f939dc4da534da833f3cd701ec93b1b40367c3322e70e0c142db3d5d3e2a5e336359ee71a5cec0bd3fcd400a3a5303085de5fd4108e23062ae25fb17da3b453db020b73df4f5a40533acbc185c732f140ec9e706f4350a5cb52c276a161ee1de0b2ec7afde71ea48a04b376f2ea2ca2cc1f786733786164580f2ed62cb11769af69c7d9b7594e4564031ec39048c63f11638df25d8ef6f1958f7f5aab8ba5536d38a83a185389afd0a06b04b14f3f359230b20ef5448495a96f099e590f12203dbe1774dcc14b19f1c88587236f75d5b237a01bcce9de0afbc28eca9754cd0bee44f7ed7e6d0c75b951c6a778a42314b4f76085bbf88a64df37f63fa18be460c362b1effd6b3188a5a2b74794ea138e8cb668a854f0d7c6e64434770af77da64742cdaacfb58460149ab4115304fc11d369946d7bfe84699365bed3046f8e89607e473bbbea37e6ac989a37dfcc982599bcf3ad269b801d76f7e1447d41c72b82b1555a4b1aad0a63a3b964626fe4e84ca25aad815ad67077bf820e5c45adf3a9ab6a0545cbaafb473bfd00b98a9b446b889585eeb28763fc442dd72f5efb74b635dc7e01782af3cd2f6117aeb75bf0ee778391616d9e694646a8078389d5e2ca0cb7b2b7c0455010a704e09cc685892079277e00640990db3902bf88581e20e683781272a69323a08fa7f0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f443ca95600a0999709998bd31eddda03ff049849feb9de7a477b5a94754dc09","proof":"9ea668cdb4de405d14f87833f78824723430e439cb8af9cfe0b469a79d89567e1ca42df2f286735f1396f31b72e9c930b9217e0a5718114309d5466f54e7377c78e57d1c8d0b81cb5ea9985f0ee9d09a9ec06d1f164d78ae6d59675125695d1f94c9e99baada2592d932019bc8b6e4722de293fa96b6b81fabdbf46bf728b9592154f8891fede6331c3915ff19b0c64090a3a1dad01df40858da621b8a81780d81b8170417fb37b17602e8f53f06a9707099666ce5f0008e1a71ca24d53fc10de1bd00c0c8cb64fa58b83504d39250183ec349a27c86c9c6b17bd2eaa8970507c4f2ce4b8cefc81f0525a2eed0a63bbea0acb809d76161924dca42aa0e07ef151cdd2514e2c4c9a5a0b0dcf87ec837fffe522ecc0a8e143c00e6b22d482a1413ec1d7fddbc601530de4b0eb7ffee5dd7dc4ab3cfe2f031d53b8be6d59c5bed228695c55e3850076d510de4cfe256fccae453b0bb379df6df6b95e47e234f974faaa6123059027941335d4e01844a75ff80fb9f7616d78cdb768e6e9afa9e4b183c90c9ca0575834fcab4bb0059dcf7c59139681b75d86658f2fe818bf4640811be974c5fc825edb28a63dbb42c4507f6449430bfb8115c02dcdffb04009f4803ce04301bcfef25686c22e2b20d1ac470c94172f10268633b43b01825d85a9266ee1a885c88a740b03dac9d52ba0d2e64ba58c4f449a93d4a90373d6182e31144ac92b4eb7baa5dbffcf71c30cedc5b16daf7db3f4d0a76b5ed9b3d09e7750d357cdd6756409c2fa78d7cd0a7f61e03ec484766e84869dd6fa57278895eaabc00ae94c974ad22c3ee70992a19a948fb2043aa3aed0defb4b6584c1784cb16533aec811420d9312289ee3e7dbb0b4af5220dc46a7856c3d6a764ababe34889e80c513b4223744d915d7d0fa4bbd8842ac2a8531df3f39112bfc793169ff9977408"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e84168c88d3f6690d6d04b342de778d4ec008985357592ac37344855719c2779","proof":"cc7f0da3a740ed0ca5cf25196353f90d26ed37b6c2d448cfec18123e4750b101e889167d304d08a094b591481235d2bdd7f7640c69863a53008ae3c49659f44d8e82ceefc214931fd9a143d6c16373f1f86e6c03bfc6fa5844881bdb0d261634b8e8aa39b2c99e28a49500bf2f103f0e9b32b07eaf85774f14567b29099e206c9c5ed950b61ddd8c79a857f2330ae979c633bbc79c52ee27aac4056d6814c905189f793753ecfd8788bee3147319b959cd2916371e5eb4b2938d1b2b5db8870990e4425923c231db66a4b54ad288ea77c57ffc5fb7f0b4add987b7dd52d2a4010a5368c99611fcca650c1fc83e6e4b129d74a8223f8d4c735b1444c4b8a3996c54c03021ee4d83fa5aba13d5bf5b0071424e84d027bec4478cea66af3a49e74bbe6e1966adcb1a26a14d8f733717bda76ebab33e48c8548aca31060fb79c6d3a4416a5cc6d2d039bb4121b42f166c7fd338acadff7f0ffafd4d514c4d033c65cd86481cdc6d08e7928891e473bd901151a3eab19f175bd29732f981be0944d2050354b60606e07c7d3ddcffedf11d4712fab0a664cd7b5f2449c548d28884f78f899d9414355882c3feb82fd087c402801d7685fc742f3b5a10339fae15641444a61d9c39dda2166b446ddda12dbd5f3923fe79ec50659bd5c2c92f3be0cfa64f6002d0a266304e5a55d494cdccf74d79854e96a65c3b9e16dc603bfb8678e1ed427c2998a9d9c3d1b0ee61c7b9cadc1dbb46cf1acc81fcf60703223c1ec137db2c6633e27728decf7794224d144f39688cdc5b6bf20740cd140bf00ddf1cb4bbcd45555d2ea889893cd37f09eac42544529242bf447e329b62f2a4389bcb57891dc7836845cba2e94974e404d18044632f92ea69f28c97062014116fb37a302a77e89cc64fdc62c92ef26b0c3fea0f6fbe507eed151050edcafd4a4f5ead20d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bc08a9ce21795e4d69221d22f9e7df84d3b5382f66cbe1f73e62da64d297f353","proof":"044d6775ad83508ae223b92b6aef71230bb5743ab744b39df353ad14418a331588dbc6ecedd141b43f56180186af68a4c0d4a604e0c65213ac97ec60d0dc250fb22c6d1c45324f391d2008b241e1fc8038f7d4696b0aa4b4ec02155eeab5bf23544b3dae57f3ab9b0fea87e95e9f57e007f1e5cd1f2cc936be6994a9e454dd2d8da912f601d62ffe75ea74dc08fdf153e957f52f58a36d5696cee063a4a8ff06941f783e14a00645ff87f2b137daddaed39c8dbe514a356310253e950e3fb00d4225fd784921f58adab8befb8e4fb4630ac7e2861d1a9f23bac42f89c2f6da06ba63a7e7230c8cf6abbb88e08fab53a0566c669a6cd7c23314cf261a21350b180ea0544eb27fc99cb2dfd96457c3225512dfbdf1822620dfee40296601bba27336ef54e1a9729c6275a2b7f3124519009d17d6cbc1c532d3f0b8c30742001b317001dd08c0e46e8ff9bf7e8a5d6fa7a7dd97cab13dbab1e223e71b0b5574b72cc8b8cf74fc32374fb51f2c469a8b2b6342d92021a07ff4dcdbe6c88a1d325e3920361dad2561c1b3587156146db59541d698aeb800134e1e77a401c5e9cecd551ed1e8b5d3adef79b2e94b6ab088ea405b868835c0c0e00e0844b064168be402e8a128538b42d9f32cb3ff83bcd6a7cc5db928804238136945f4c2b20402d927a42b5d46e6569827646eb42888f748f7de12402b744ee95a5b83624426379159eec9e0f21625eaefaac21ed120efc0542a6ab22ddd313ef6ddfe9951e205b6651e8b3bd868624a96e351dd532938dc210c98b1ee5dea850e2031744ccef92c030aba85b90e94d5995cf789990509491dbd2beb5f00147a41db970a9ccd6716598f69d0c6111e8752279497ace18d5a9520db9be1304d4cca38003e07b52e220245c8bec31fb5f511582e79222a561feae2a74d35925356bb8fa5f85db7853f0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7e945de83763e7ef5c9840d61960a86a9fe3fd6c81c8a979c8d0f7667de1cd65","proof":"a2a4466fd93c5611365f5b9d35a6bdd0c271a1b3ce6998da17bd821779afc64fb263e3295d4f39d4b46668ea9242fe35c9482b5a44b1323466306372e03a8e483c885fa1294ea96511f6a33e64222f2d5c67b0559d41126b3988f5a7e55c16331ccb86d9d372a0fa4b5cf6257a5eb04740461337bba7bf50fb067eb5fba876784dfb6c4bd0bbe16cde34ef85161283bb5dd10571b9a0577d65d1aed1eea46b08dfd9a87dc9ecc5b75a4f28e77f762b9adbe14b676a8af34d5e153bb03c56c60b1e3930e0fc1e8c1c52ea08e8d55468020dea4f96891e86542c80bd58298c5709960d3d11681d7ef1d2d92789d86cf519a8e1024ccafecc59346847e01091751d9628d2dec6a0f9071ca0895cea6cc6fdc1b369b21f528d199ed5849c24ba777afe9c6334eb06b99933c33fabd7c0f84b7d0d5b15f948a56035eac579ac944663fecbd4f68a49313a437c58ba04f9257bc498be55aedfa2013978947fba1afa465ce50b98298878c6051a1deeb31310bb7d84f20046b2f2b9ac6dba23be17d33c9655a5ed37a540876f473f10d5cdfed4a5cff47d859857be185b54f4e291a34920c04e0d1fa3a1dd68f486bbcc59982184a2bb61eeb98153646d8481d35a4e5bb4d6fb3fea98b903a948522108b77265876f060e2084ce361505b78d5843b40ae6e98f83c60ecf55adb9ecfbe53b5396a5bd609fc639c831544581f0a88e034f80e14708d0c551b24484dd5776826cb7bdac59cb46e4aac5df693cc51f2a9644c2a4f390dfc79f08919fd7a364785ddfabb20849987b5703dc6aae45394b3b090aa67d315f1601cc680368c6c5aa3a5b7107d453f41a558978531bd9a9bb1020d495ad300936eb92663688be6700e8443d405acc4e2516978ae99f928192af05ad0850ba7c46efee7f412af8daf1350b84be8d1bc23284d2f0c5f509d24d1400"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0ec5abc2dc4911c31566b35d9d80e288de50ea610c22957e6e689fa37dd99e6f","proof":"78ad89c2cea3301ba803653dd2cd453be706d9ad47c9efbc5088a60a8836e26e8411e4b7aa61b640b9ffe1545f9d737d6cc5dfe5e9706889c814b724bc69bc150019ee1ccec5f6870bcf9f5b5fb66090ef8307efa7b6cb55b75bcdae13e33f68e872f80730d15d917b97855476f660272f0f1bf13d5dfd2893b3a1f2266d1b2cb6c92215debbc99f6636dda01dccf67e26dde85348eec6a09004ce0e3a21b2039f19c75ca5a561e331cf5816e2f053f5af0c8411cb748d901794fb00026ec60d8f9608cd591346a2c60afc2e7bcfc87020e86178b13afe49c85f8dd64fc1d707581e732350342d37197c3b4982e19544476445e39aeb3691937bc9c794f3e04b986850388979a2bce662b98270642a574a27fe7e119b0c44a4f3370a113f2374dc903a1719069bdc4313dd06204c3f9a4edb45a4aeaa791ab68f08e31594852a80b9bbf47d9063b336aa10911335d915d516aa355b1ca8b7e62dac61bb957f1a8830b6b133b4f2772913cea6f01b57b15a236cff7fe9c3386108855fadd7d94c9e8a5a5c92a1f0c3cc1b10564ee8c600383afd653771563d66bdb0913b2113621c93119b799c43cd1a62bb33cc33d1051d3f814db4bc9cd9ee448360884ffb465cf646db1dce5c8d9ad4339948fe1b2e024f4c4e8a298ef8136d8e0105d84a57408a5e2a79f4ab8a1fef85169cb401d930db044cc23bfc8934a272e6bcb5201f90babc4ccf62c64ca87dfb0cf8b9a5399578f50fe9689de0925ebcd8ffbac0571e5ec7f8d03663426bf8570931f3bdd4e28545d55d6701d3337061603e983529be7b93af14f70091bd887de1302f0aa9af9a6415b2ec4cc3a6f5d34c4262aa53be91b450ef3381f29fec9449e9c767bdba80fcc86b476e13c09866ea42256d05980154d51d8903455354f1a311411c5c760ffac474481a9e47938e1fb9eb4e0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c8b7d91a7b1af3594a2160c53e6269114c6dcb304567a790ee01478c0801d633","proof":"8a6a2766c3720d96502a801c7f3bb64cfdb9a37e5c70e0694aa60315d205a128ae705e6084382605c4ab42b4fb116ab68a8b1fe64883e514ba6cd9a87386a468341e73f52391ca77932243e244395385ca1f9e69a622bb0108e5c87e6da8de50ac4c04f0e482280e65e0ec56d5a7349a59879c3cb247910cf0c27a73f1074f7e7267d8b3f522f0f55aadf046069c0b9c710fc6d84e4ed4ecf842c7f2584e0304031b7d2ff005b626e221f62d5665c75a8a197d113004fc08dda7c5444e9fce08654a7eed817e590040a695c83689091f1f3ac7b050c0c4fd69982444ce4f9707d45113ea369bba092d97a5a87fd64773ca70c0afadc882a32e6675af94a4cf0f9c25b815d04301c60a755e949e9ddc94dc936d10d49eaf8c07d999870d01c96ec084feb4716a3739918120e3b8dca9d5b38d4f8f227abb81c33a86dd4fc32453d2693be27da01ee149e7e910a1444253fc0cabfa3981ee880458f98b7083252a2085bf284c60d8f4c66b8c200180ffde15718931a4c6bb4fe90647b689497c46de2bccb60984f14be31602cc096d9e128ac03a4722a5b7fae2b100e9d30dcf637ca22450982b8c4178ce79318b68e55460ef507df3caa791e615a094cead282a9671c5022b3421e51dc2bf7ff0e7da98d05fcafa52961bed6497f495db0b627bec4e54ba1e685a108b62caef79f274edc6ddb4697dd49dc680ab9e0055f86039b212c254f2ecb0c6df9d07f61862be675013f1e25015e29c3b653753bcd59a1ed4fd5bd5d2714987e217b5609dc69bb521787da8c241cfcdedd7ebde0425a769347fe8496d6c14c8d05fa8faae38a4af53e99fd4343a451c3c4014d7d5a5bb3a3ad2114fce7641af709024920bc0aa5d79d9635ed703565c4e81d19528fa76006b91e14f4b1d2df1166baa905995204bb4396c4d964c111043d9376ffd57f70d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5069222c189954906e5e1690df7ac2afc7f7ec0867cd5a18b5a85e12d76c4929","proof":"66a697acc124dc24c22ddbcbb31c7d1d2a9d227dcf2e5de4b09f66e4d1c6b4297e01b325ff8e1c2bc714eb970bf797adca99e986d2bdd47dc7a2d3b5a4141a5a328ade472e68e14cc909fd8bae21a1c618f50a459f26175f02a2b94a222521707ea0fbcece725df366056676787e6d976e507cef38938824c04b27374b42466f3c285d23f00ad0e40b5474aaa0867339e9b0811cdb98b21c8498b6f6b833fb02d30c48c57a71fd3cdffcc91a8c6f60374cdee06956fb0c4e4383803d5ef72306d08a17d3100a61412d69ec6a52fa44bced0af163d001aa04ff127b68c03c8c0e361a0685ff0533788949396bc3db30fa76570dfdf8d466828ec26f1b3cf4083e34e006987a98e6baaf68d4f31fcaa99b1f96ab065fac39d552e5953566c40c0d407fcc31f7ae3064b85c235b257ee6f78120c436e36085a75f0c2c8be8ba24789425fb93ef31e986204f7ed6aea36f128b8884f86efb962aa595570571aab57730428aff5a146aa4da0255f3ccef45eb6455ec3bbf6f5916c05fba26a2e5b71908c8c886b5cb44e4b48c8c966b427c0eb288c16d97b460ac41d8924dd1708b33a6de70b554a82fd01edee46110fc4b400cd0911c031b4ed3d405677982da2c201ced972a04fc6ae947ab095ea7cb8648c6585c94525391ecd69b2bca9a5afd35841cde9b35ee97877612c0bbfb08672a2ebaf71a6b61dbc6d7560ca1e516ae59121c27a3e09e7923ed506d88a98fccb334fad2d449da5c3dbf0b4dc068316918661c0fe50e23a6f1964c7eba38acf6899ca6695edf908c4f8bf608ffb096847900e961eb1439e9fcc78a9e4f29336fb2098b29f6744d2ab609c9b1ad13c54c4f21ac594961f86a4e71aeb0ad063f5957f866c4631526a9eed17cc44ee12d0207a60ffeb43cb5b7e889f143907b6e4b4cfdbda045d9a006a1f135be286808d307"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d84847fa153bcd9590b9e5eaa52491106eae8253cca8978554c6efc5c30b5e72","proof":"defe7c15909e38b5e161270ada8bbbe33ac6f614fb5880922230490bc3869e33b2258c2876826d6ad7464576aadfa8d4243c6aa1bf740c0ef87f35dc0d96460d60d897413ed933202dc10b043c1e20f5950f186df0da07ff298369c20a825c7b7ce6e91e2c5e9ee846de1b2e62717e49731e91cf704fca6459f2514d15eb522ab963b84073455a5ab323b1af13df4683f0e39ad7451292a8a7f286e3263d660b7325e7272017ee6d0ac7f14bed1eda744c73656b7db3ca9f45806fa044f61508d3ec9428586ef357bca9e966b81a42414bc072f2e2123b957cb292d609bd6304604ceed8886d57f6969aaf6c259381fa19e86b571a7df8fde740c89f94bd6840f6780eabbf7e9b15cf591f5310bb66619ad488b841afb4ad3c1d2810e61e054214878cee18be4ccb39b2b1112e957aca7a122dd67a78a4b2d9def6031b90406ba65557b90daecac5d95bd684c28f3835efe3ed29aac97f2ce949b30708e0d245ee902ecc850f5812d98a1c492e8e6360daa0a7029703692b6a65201d8925fa3accf97238801d4d1bfc3bee553f832665d6cd3a0d214013a6aa31e32d217fe613a0419f7c247517fad860ad7df360dc37d7f3595f923e5cdb79ccc95ad69e8d38e4f9e47de6623123458232354ff508ace23218921b90c18f00fc87b5b145713f6812c0f58c0a1f7b214e97229a0f6d43605597ddc155ebca975b15b3233fbe3c12a38aa84fa4954241741086faffcf161301c172ba8ef42ecb424558aa51491c289c802e8c6ad61e7ae04d8a222c5983b68669c6dddc6f90a193b40b7a8062174a8a25130e47ca80359617bdd21340c4479ad7b5891def1f2c62b315082bd5346ae98cbed3ac1edd7e6e562f3c812d17528f97dca9a9b323d430f4ab77d246007ad1f6a67ad558b18f5ff7c88b96ba686df687c1c40847a751f6eb4e02025c02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8eca4927f66bb506782f7f2a4301596255502bb537d09c649dcd93330c8a5466","proof":"688c47c7ac12da09dde0624cd380532208e15b77c27f98fbf2be9751d9914035deb84cac036263193d0dec484a440f36c8f487d79a813e8e95a868af6730997d5043d05307ed2b0dd0b4669664840c7f568f4c8927ceb5a2716f113f867c1a3a3ccad4e96e95b11d52a8c2c13d024ddc8d6a0ba7c432953bf0c62e0fd7b0d031bdbe6c0f81993f26dcea712e1960b8c965ef9323016967aa5c9d344e0e380604a369e90f82f98b36e9788e1eff7c5ea998ec8441dfc2e1cbcdfc530addc16201f9304a2999ce8b0608afa7901681c7d74e7ad67ebf2ef2180c4d46dc48f21a04bcab4deb4a0bd7c6efbbaab6042c015ad99fb85442404dfec765128f998c29557ef7f395bc186c9bae3c561cbc1207562adb5ecf04a3c7fe3be28a19f847396e5a37f5554a32d734a06294fa75eb3c61fc998757cf84c018ad79f5f1197e040bfc5a0af7a015215e9ece3c23ac12dcf7f143bc2ebfd2e1615ced74cd53f24c0cb8a96ec50d122a2ffcffc6b251e7064b45cfa8eb3381d18e80c655fd63c3540752a322abbe388e98867a8d4127908896a9a9630c765555a6bd17669cde4c43719cffb4d87da015e8f62680474fb1355e1eb37a609434ea2175316fbd47154f7650f9db0b38ea8f56470c8de83ccddba142550bcd3076387036cbffe66ab899568291119d3e6f880efafa20d93f663f8b8b847fedca59fffe0b05fa07f4c4015e283d4af4baaa77514f2d98fbaee7be0566829787209b5ccfcfebce7f7326882250db39360bdc0f14d3ecc1575d9c17e082e94b87f8a2153ec4c3b6bd37f4ec324a01d91a26044f06f761f194a01656653326dd9c5d6797e7c499607adfeb816c8aa141f031c05c304cd500e036396fa96097b9fbafd84e96e1569ff03571b800cd48d3ff69bd80b7b2ed145da5514253bd8cdda80e5bd806a93ef4639804190a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d4524201531270b707a338da9677ce9f2e7c1a816281778a95da349f731e795b","proof":"1e49467ae480b47409b216e695e53ad86b2978bacdbbc68b99621210d3c43c60267b00a6ae07d9ff3a3a0350f8c83f5f0ed678be7501e2aa8855637ba15044187c6fc78673dc60d2c360b6d2296b004a88852f7aec7e786ca1ec8338cb8e91288691f38218b6cea6734d433468e722bd3918fe0942c9ff41ebba905fded2bb29612ea2b658a9e6fe17f7724aa242ee6170f786ce2fd2e7ea488bd1937bf92d0f3934f740fb00b9f37341663918478b78637db4a8d1b269e949bbb231eee46601534d650a2ebb26e2f2dd1e9bb9a49d0d9e64c2642037461f22dd1983a0f54c0db8d96fd3787180e9aa5548f6ffeb4c7a53f67b9feee3496a5a71c6f315c3c105221988d48c3668f165dd521fd4b15cd659041db266688bfed65de8324748ba0068dc8995b2168b3770eb5c113bb49ce1d2fba982feafa956d4892047a772747a5c1bfcf07827d9609fc8735bb424ba3839ccd97d0c359af984c59ebed6da7d09e861563d00d685091678dc6b0f30935d64620968e1fa1a5334947710cae7853eb853dd729bdcd4e25ad55fb8ef3eea6017973a7671c0af0e3aa10d009c22df39643e4565341fe62680ae89833de93b7c1faa9290b1509065bf82c3cd17c5786edcbc4b9e1c4f8bef7244262413ea1af9a9d31fdcbbb6444c211a84608a6418307e9b3faa257995f3bcf80fa3808fbf4431dfdcac537eed67f447370ed54a93672e3082b15ec351934490dd1b2d49a64d1c4d5fd5ea0cea0f17460ce516dcf90304ce7d814cacba3e2f37be0a61ff9725b2d6b47f590f3e59587b6f5d61de6810aa8ed4d77a4d7293fb00f6345ddf626e80db4c175bf565cb78e4b98017d4ab7e22bd4540023f000cb573a0232783810e51c4fd572b6b4ad38843bed260c34205b6486e1ff1f443a24122e2bc2df5d28b07c2ccfbb305b0b96a8756530acff803"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"06ffa9a7dc7f771f8b91b0667842bd3fb670a48d21811770441c64b117f7952a","proof":"2066d26187d353b336406f54fe1a0c181d0e0bb4f0410045085e95470ac42619c6373e576eca31330a9cc1869febe462dd560d3dd8f60e662c394ab73b5053367aaa1960da3312c466164e12df2d977ab75b06d5c71e141925169d795af22064d64e1dbcefa24f9a52873ef72124e294913cc39b1bcea7f5135694732673377bfad615c2c8c8880b8a2d262deb999fbecebfbb69f67fceb15dc949ee7cd235055d3f795dd7bf333419ba631e752c2ed5cee6372ecc77d00947ee1d06b694a307a956296f8eed3abf0a19d0725b3454aac91c01a440d36ebeae4b1d7c0c20700bd879e7d7917f12b8e5baa5793219ee4dc4dd8576b7a20396c87afe63242a1c74be7a8c13408352c46f9d924a361f59350075a98a216de5ddc52548dd52f52415e0adb4510a891c2e1c2415c45bbbdb514dc7e7e7395863b47a9fcab7eb559b27e4ad7f070c97e4689e8b6a8a061db57df661761b89adfa40c3ae1b485c2a282b62988b6d821215db5894b8f981bb0c73e681e837f28f8d1d8e92f0708c23ef334a62e1f7e2d9e4cb10c2edd978f1d50e223582a58a4609f80a47f68304d9264954ee0fb073681b77510fa0f790fdac606cc59c0df5ccd77a01881efab7cbbb722ab80398d85fe8376f90e642960dc030cdb69ca64ed649846c4196ef158e6a029ef4feece91d60dd8ceaebf34b58dfe011d71b953779e830c2df3c272c254c2202c98512f2490615fcb15f16f5c5aee0bfeb489ccba7dee690f00056a4e5db547c025fc24c4cc1d0b1bcc74b4c4ec4a6574ccd3291b5f9e7fbf7ebf93f975f671a81c071bf4f08cb70c51a39f1d97667adb33bc046594d0838fb03c54801f924cdd04943268d324e3112ee9432de8ef73cef24aa0dc0497a9e8f8db5d5e02a07621b173a63fee4eadb94a544b0a3ac13a9d0a89d70d7e5745cec461b484dd205"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"46324a416456e098e532ad0b1f842d49ecbf823b7c383c5665d0e4afdea6c347","proof":"d43c4af355efce9d478cfb87d09f757317df70c6771cf789310bf616ec5246091e4a9d6de02f17ec208715e1f84d7ff741c40e8735e6d0447b66c5a57221cb2b142114ec5b08b3d48f7964a43e103b7cce28e8830875b6f6399727624ee5a2210c38f86067e32bebfc4a02d39179dc061ddf9f446d83b437a0cbc67bdda6a5031fd17a866a22bf10316d70d337d0561e77bcd8125ac98a74c8a3c033f7cccd04e7fbfed7caff965530297569feb08fa9dcf9f98f77637a42123b1f77bdadb60b15d0f2f258f8256d5d7aed1ff83df569fa1688a57abc6504a5bb64781da2090d30adeb1a9cf298097fee258660ca31aaa4c61aa8feed8b8dc37830cd9558b52edec909bbb73c3f8688daa11158f2cc7cfa5e061a48f58f00b4bcfcd4933d4361360225d8b984c9d79eede23928e4975a4dbb20f254d6d452b3e6eb903656c5203eabe774b0fcc881ac1a57566eace238cc08cf4b89e176d34eb2142f50f4626a083f8b6b4ddd805a46f7aacf563fd8a7ed51ea3b9140236c9edfec0b11634b193c14559a7e481d5917fed5e3dfea82dd58b79b662664384bcef3b5bd34581a2aa26abc8b22193386d35097128825a2dba5490cae318ca3aa7ad5b5e29bd38b7e3684924d8f71013a62cb7093e5f6ea04af1df38b6ea69aef463899c90e3e5f624a0066deab51bff7f783cbc97c70c2ac2a69f2033e502f3430b63ec52c178c2b58a0d820d45224d558b229f9ad3d6be19861164b8d1a617c5e0ab08ec54cb66842ef8bebf43eea7726fa3fa6103a10e9737ecb2a3050d602233a10ec24ed6845822da0b0017614541447a675de19f420c959d2c7730791c3f913d3e70051a850f34dcd753f16a71447bf5fe27cc7b206d035b576a1e2018e8b8dc59bb0729c02a819fc9336823861f8aca573a284ccd598c67bdd78631c25080e425fd0418f0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"70d594027a2f14773c7cd11aecef56687a1d56364012a0c1a34e3554062de747","proof":"503312a2f75dd15ec7cec79d8b262e9bc5eaeca209de05972b85bdf6fd61d25ace0a6eb3d363eb678b973e5e55aee96ff916f4e302605ed4ec1a203a7a168c70a04c7197185a18877e1cab841e26b16802712ed3d4d6262f29cc670b4a82525c10bc2ea3757db59fff6e720d05d21367af4d14ba9f94c8aea336cfc3644d400699f7f586514836ef720ea32cc00ffccf14ff106e97c01e8bef418bf3fa58cb05a2be2fdc93d740d084179968dc084963f0d809698c9b711afed8937a32a5400f58b3c26444fae335619af56e0d1f89eb7969bc17bb241262da63aab1dec7ac038643b439dca5481bdff5979fc336a980578595545c0163faa533509411c89e21f8f007a0843062b478005e85b168ac49b9f081acc3cc177738b292be1f4f4d6644e42991338feae15c108bc95ed8a67c8bfbf7f4ad218d59315d8bbc476f381392712617d2912774d75e0f3ef4d0983dd84432ce31a1bba2d82fa423c730ab03e036d6c166ca382c56ab9cecd063690b92a5827ad622907a1641d951cf8a96181cc2ebd5687b9bbf5119929d4398f77362c1d05ee07cf2e60071d94a4f53331784c3a079b2b51059c41a7d79588f5fb3e3a5705d346e82be452caf5856ca521372f89b208281e868e4e1c7460132b1b080be45a0a39e7adf60e75b75b0ffbc4bdc74e9fa72a982f2cfcc1995b7826dbdf3d21a562cfe931c2c8d298c51fd1e06d054cb619c5341eaa8f09054454c090b900797dfb04143ec1aeb057fef890d3c1cd435722604faa5fb4301afc8f22f8d4ff0f0cc4b5547590a49498377367b13b4bb63e54f48f3d3177433d493366cf2662610f2bcc70003646d75a232a7eb5c27c55e76c8ec7049f16da36101103c0dcde7c571cc44ecbb9cf86c25c792680cd383ba12c09301a2f02114030791410029c323fe2f801ad5dd462846d14a3703"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a0529333264ff21a715ccb4e8626cd83b4faa061c5b06b23471a5c6ffb5a9543","proof":"2c4eac1c89c398a567c40a96509293cbefc80d74cfe21ad44d903cfeed35522b28ce4031f082878d147fe2cf28b9daabd6802c441649d388b6e78d3a1c9e3406f2211f5c89260166239a02c7469c950b6a0b873172434a509e5440a9aac30e16ae7f82e360f615a81454b94517608967cc6457270fe4c4082b5a89dd51fc996fc8298aa74fc7729a1a5b0113b43d6a63406dcc5d6b3d86c245df0e6547877502364c7ebb66a11aa171cb081e1c9f8f65b4fc9e12efc1d2cb9cf30f3f8a66b30e55add3a535bb66c6c65fb481a34dd042eb812af1bd02380b4de9050ac04119033a126f7a1dc07284a940a9fea4e115c757e1d44abd2631b8a7c5fa1d8a8bcc708221648fed0f136fe7d3a54e84c82636cbc2f188b2eb72f63c59d526bb86d67bce9b45aa32bf874a5217aac022fd454930781f3ff24893d3c3231b04b4b10f1d240598d22c4464f5e72c8c063bc0d144afe26c13eb7b47ff146a7752f39be90fd2e1ee8a19dd97c2214d59552261d157c41b099d437ccd218fdbafdedb696b31665fda538fd6abf92287b0dd710b965bc5afea72e040c0aa399320833a70116a3e71f450427040bcec33ea66ce36cab2ccb033711d8cd709733f844d8947cc1ede3a627be586cd57aced38ce47a32792cf2b196fa87a6d36441ae91803ab2924fef1ff22a44c83859adfd5ed836ba8b3e892e32cfc555557b8ad6e16674eb17132069b527a3044d2a5537f9cd9142461a26f4b4f4a439a3035cbf4187438246a44c86921d2146febf6712edbeb434f7128bf93f19af4acb2ce1c9b526bccf852e09b3f15aa68e36ad145795fff5511cc56257fa18a888ec5d469c547b27feb1020cb43590169102349b70d04f1c698550d3653da7aef95574487579f6234900fe7dfe318071ea5b0a142d75842ed5d87b38fcf679fe4c6ca4cb3b40ab3897002"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d4b9221fa7ef321f4c7cfa7faf54f78c5590ed476a6d0dcd42c4b4b09f86861a","proof":"8eb6ee4b100eb25ffdc1e20a56f237b06cede138f6e628418f58af321756d00b2e1eee746b61d8a1e44d796d9274b12737ec0755ed1e3e6f08e780cd5352aa003c7b93087bb525ce4730fab05e36afaa47570ec17adce7d9afaa09562363770de87096fb0e1be9404aebcbc781df23fe05e12802642b0e0991aa4c70a2d0bf7af737c64dcec97cd9e9aed98bf33c40e7eaa09c22a4c0e7fa9400b479603c2b0f63a77d072fdfdb0cc9c00256d0433e430880bebc02a24582af2aaa0c47deab0c85cd8a21cc631f4ba469a6492f744aaacba243d01437e50d76b57ed01a154205e48dc9831610536de71336cb19b50c5c1a1854a9592184d83a4c9bbc736b7e6296d7d9c51d62d8f588965d6ab82c7431633359a231abaa47c8ce3e1794dfde636a8806dacf02b2e0d7c8a43a81b380f18aae864efa2d44980d78c4cc635e840c242ea6c8df3686a9afb2dfc39c6705287a712a27e78977f1158baed8fd1f9450ee73404cfa5a862aa10b29f187360d5494d86d4a1df437c6ea536f2c3b5e3461fa7a955720157297053ecaac34607ea52dad67517b0ef3be09c4dfe280e0f61cf4b6a2590848bb9d0834a7568ed07eafd010268226654a63bc6d793055d85718aa8f20bad798ddaaa9f0ac5ac9d41c709d4274ad269fd2df8850bf404c616611e85fa37f4685f5b6f37249b8291808f2b856e700f7c221617bc958732bd18345c4cbd2a09a82a5bf2dec6b2343df5b77f5d7b3d8e88b2a4a5ac258be70741b0844337c647f8f22284b3f63ac86ca90946fd3a2c42dac88b762fab37a7c239d412e8c59185547a1d3c0fd8eb881151d0a4895f0573e0b8fec774bd9437742dc5f59614d55c1654b37d49c766eaea623988b902ad184b9a5abdbe7e4de2fcaf201c5a0477bd3b300343b9ebf271941648d2f3c21bc5e1ea6b96dca01094539eb04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ec8686c975edc1586747e4beea77c9bfbbe82a25ea70194c42bfab3cd6af8d16","proof":"28e942671189e75d1503ca89e65d9b9ea01b1adb4a3132c1bcaa60e2d6a63056241a915c8d5aec7bd457643a8947817a9481c208fd6e61332dcc18bcb6c2cb4a0a213b1efe440831f6facfb10bc5a9078ba88aae957f23f8473f2d41db25f02ad22a5ccc4c527ee36cd14cc77a4c64104830cc16f8a6b3d56d0c55e9c512235671008d71f60b287f3334a06d776b83e4e4910cc390b95808f73a56610d694e0fe44a3454a40f171d83a3a04c904b4fe7171766f83a4aa5d652668f4899303c04182996718b2557b21c7d8a788fc0e37aa47b4ce481c59225352e1bb33748a302ce89fab75ba840276908dcf11d8e79ff5f453c7eedb506bd21a18fa82509d266e66d7d6d85f69d1043ee99975fa697d03d1c471be32fe1a35aa1d1fc895cda624ece37a6aac5785438bd7bf65b6d810f664f16b11b719b43476b25e3a37b2b05b42960962dcc2e82ce38686eee56b4a00be7fd849e023316b4df21a1440b0e591c4e163b1c795edbee9ddb711b60c5de8336480b7bbf55daf94f3148efa2bd2b0e729146e5c16b393cfa00d5f1d241f2d41a34129d732fdb43914dea4957b93726a747e3ac5c846b0b96ea0f3e6734dda6b502c5254932129f04b4f067e6434d0451e2b5669090cc90d8ddf5e777024bcc3a98f112e6a05505fc4737a83a395ab031c28bb2251a16e8b6cf84ce83dbafd1d0b702a8ad3d82137b0b9d26db832af2235d293d07eab80fd3c6cf3a38ea71510168a92ffda6d056a5330565ac556a4cc39bc7d812194f147c24ead57df8f82e1579c2633928884904a1085a9c355c388745b1542dc912ee650f96ebe83b13deef465fffa2fa997ec3e021d56d7422ab41b912fb632d3aeb870885736ebeb361e495288dbeb69069a340116bd4f70f72f5dd89243a54b00d6d815628aad1cd15dca82cb954e90ee27fdd196332950d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4aee6fa6d92ddf6b5b2ba6b9157118f6edc2861714d61cc620915907c570aa0f","proof":"e85b17fc04f53515eff841e7cb1b0ef1771d725178205d005ff7c059568e8213b409387a052bf4df85ae5fe1e81ed0f3def80b02a219ae4881111ff212c25a43a420ff2e38c4c4a5040aac9689763d74093ec7cc630095c815cfea3840c1132588f5c849d7e8650a89da78341d80cf9937b2ce6db4aac7fa2b469b313a7da07f5460213c373899c05bcc126a8ff3ff78924885a5796aa3bbc6639f103813b400fe7366dcca19fc2d24aa586d43714d461f7a84c3af799bdb517126ecebe89a07eaa477e97ab3bf458227dba806f4cc5480d83868c0f22a187edd75c6b9125502e46ce1a7876387328e0d938cfdfd6b0cb6c1f578ffce9569ea83ef2e8dd545153e73c808873c641994834792031bae4ff052c2726b35d149747e1b09c377e970c6385cf94e5c7ab7cb5426beadb077d48a1c399b5b2d8eacfcff909142b53657945183ff5e87207d232f9adc1a3dead7db111363d5deda9a3f13ce39788ee160d677d183e68729b650cba81355711da6290c90f450ac8ed7a6323ca86a4997491825d96caa8bdeee40890af7dcf5889022749077932073d81bb2d0ac7b99a04b5c852d602d9d8c2154c1462c3db0564c2fb625d2453edb8ac66175715d0d0d2ab0550c2327f4a1911af726c21a10981d0523b480ded8ae1e82720e453ff156731e90f1002e3e3f14b663646fa7b73cd26e0df688d2c07714edbd9c977c3b5353422af62e98497ba28145ef1e0c77ca74b9b99c4ac8cd45562ea6c81513c7f1331a7bda1d9e384b3f99bd5095d2f1ca8538ff8e02fca2557ee7855a324fd2207cbadaa55e3c2f0e098256432e5e35acfdf96ae69c3f0fa430bc40e7e1def1d37888d2aecad675c65bca44eda10d29d6d14dbf87977fa459c815574ec73ca3fd089100a867927d8422e3c8b23c8ade9343f9444e8ffe2d5ee6aaf6b2deff7f320b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b43c69131c012d7f1fcdbfadd19a78da157274da987603529c8547b3396d177a","proof":"6c373cf3f744a1226f16077553161f4aba6dfbd487ad7b3cef9f611c17318e1dc644794116330a3d2a4caaa53d47ccea9f39fd21ed3f5754f2be8f45195f964aac6e589dfeab3d48c66ac3258580bd63bec0136cac497ba21f3a182c1f11ba43dcf07e519068d83b63cb4944c44f9e8e72a6c3b88c0b210442e11d6ff0085c3a999c7582551f59a69e8de49de6e56262af03ad06fe0a941865643f843bef6005b270a0ba2a58f1a64859f56b923ad7ac7d431012dc2f6a864cb82ef4d28ec9057dd314fb01027dfcb3ed09b4bd3e760c5590f3754f8d24d9090e3d609ddbb80a10059617079e0186a909bfdd3d07ad4b57157421892fdbc6553652e989725763e44141da644616866bafda279022f1bd5938b508080f19fe8dd027ec7045fa3d207b79bc7996293c30ac9910b884d70edef31a901e99b4d16c78fdce6563a204de1b187f018b78b3ad6ea3ff2fccfd9f346e69406f15102d3af23e9c1430782660687dd226b73383269a6eb86036123ef1bf6d643d21ddee196457c63a98f135fecba43d85b1258b9217763e1e604ad16178466e0b3959187f52bb463d20e052a80146c5be94a48b42ffee4c31bdf8d4dfe079185b699a1d4196b8ef13a3e93992eea48ccb4c8b8b81e4d26cee9b4237a64a3b6a465bfd1938b4b69c41af702e82b18c85e9ffc96487dda1e6a497f8a3c30615c03c383faa1a3b2b12edc87533f00d840849de06e346fda1c2984ada27cfc6388a85892107c91d69d0512ee278b00f7940bb74d4a7b2072980ee4239c492214cef89325e6200046326d80f5166a419f34cf2068e139b9b9b5f45e1574ee3961a2e24f6582e5f41f55ff586210d08122b6461f7e7b6053347856e307466866f3ed7d5c2c0b4c268ac099b63df0c15ea44e0e61cf827f4f85b5228ad609ba903301d85f9958e6dcf78d1bfc37a0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d6fec540e111c28c527f774a76121078956d4b128a07cffdae1a491662bf787a","proof":"3681b7fd30303f7bd9164a9e38bd7931a4b396125674c27ee2e825f589c1887a82f4a9abd10ebfa057d6d00f24990d9e8ed8925c32711f2e58c52b592cdb7e74f6eff42e8fa8d23c72f669f08dfb1d4d822f66458ca93e597b2c115ea704844432a22d78234b46572283659d5e933af4f1780fe2afc2aa4caee86b5ca82cc459f1dafaddd152b8686affcc7fca49de339c950f3a80f925e6711a80a6f9457d09dff0861c49985b6539e0ecb6f46fcd7b1ab0bd36204c6ed44f626247e2e0e5081604ff6f47f4dac5ef0cca31a7169d03abb76f657e94f53a902a44c5baad9007f8128de4491ccb9bfc5d915d2a5dc1e550849c38fd5bcb1adc764f4ca1d8ae0cfc0ef7e0d99dbc0198e103b432fd3c57f7f87f1b72aeb3c969a695e9d06a45160a831c5c8d9cf302ac8e57bf44352f6813c9be52e33be3704eaecb4a083f161d5e317aabd4f92b37b2d03cb5bed5b0d0c6b2a0d05ec3eab2d38fcee60100951fbc810e75b5d750e1469be55eb8fe13925450f46027c3bc8d7b61b324ae2d334feccf098ebc7d51f6e981976ad6042688601de5007a19fdba8f3a98600e28e06cec4c7a12bce7f3c5eb2cf6744136b4836912dbe6dd949679639310a1b8dfb75d70fefc88509decf0fc83413a496071247f45dde88f2a2d747365ad8db8ff4e02ec5a6c29a161f2bdcd53bdf70ed8870b46f78c05f15a8867e124a4706473a62b3aa5b4199d9e1b8fd8782f1a69bc46fef0bb0320d09e4fdb907fb7d6a37a992ee0b5a7deb81d153a9b93c6c552359777461082106e4568fab216c6f4afed9c6d7490d82a6502d7f8a371f98ad850f40d57bfcf5595dd2e7eb0a87e2e6417c251226352f42e4697094463ae12ae0f630647d57924bd422fd873e19c233345200c1555e41bf4abf794440784bc1de1ca544df5d4d0c2ebc920ca2c705eb713dd0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6e1195416c00e6f829bf1ac9471d43cc76d1b0f6cc8fda0b1aa35f544b1c7336","proof":"76d0da9c717ba1ea1ecd2af01276f835469346f89ddf2b11d71cbbbe21eff835da36a25cb6a5e75da1c8596dac0ebacf5435a33dd6ca7e93ea665bc570aafc696227287f5423e52bc1746d70107a9215f536a46f79bcac483580ae4e2affc91220d81cae603f77431ee852c21007964b94dcadb40e893ce0f018a6bc4674a33312f4201f5058d2f344dc0163fa9a6c184389058055a6d7c848d48081d8969a0bf41815add581d388221d91b2f42a327bda805a460d7af3586218abb9a8cfb2078503ec3b6ed11ca707fbfb38f206a38016502980857248cc1b7d4c8e7dbd4f0e06af2e9bb6dccf48708721aa1cd49339540f6485aa36a7687f906a4d44b70b342cf7cacc871225820da010f06b01e6cf039eaebeca5c4282e913edbda52ef53c10158ea36a48615f0573c386be96a8647d3c3f9bea2a8a549c3003ce3c001348c6e4724e131726bd0f03d80bb632706ac8d810f409baa83e9ecda1da7ec4c9170c3c0af8c92a1a0ca704824f02537b40301864cfc82ed062534d59dddc8a2b0c369efe07a9e931ec0b6ca7af6eef12512072cb164bcc5c6e2ac1d76ff21fbe5f92132d7f8f71c16e09b2ef0c84a4d9be38fe1dc49cb59ba21c326bdd1d8e8c566212cf89302a5b9ae487b7c58e2ff42a9d307d35f34d24f69e2831ae7f251946a4fe09ea77586adb31e84e3ba8f09a8d0f950b7ea3a5216db983386ac538971f5eba58ac0ab3740d05db7c0afd19d395ba4ebacb4745c3f42d388842397cd0687c5715c49f29dd75480e581e025b9d673680d5cd04d02a4b3815d776ae75a14e32a20efc03959faad4750738959bb74c5d8a9b49f1440bab1989fae7363b590930a393e0486f44c59e271b3a0af8bf678f92863e4743f426f07a1ba3acd38c05d12829f71eb12f426f7ee7b110abed83a70c58f1187ab5e7236c98f23801fd0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6cf2203fe96a0195911ee680837bf3337ed8a445cea6f57ee73c400378673342","proof":"1e256317a3ac08e32c5621165b040eca312de57ea838d8af85f449b6f040437ea0786fb7cbe0d7e078b825fc8c02f6003bc5f6bc95ba64aeffc0548acd637254b85d8d01990ec781b52d543493d2894cf9919e7b20be679a407e4db61ab3d52804fa64dfe21065721f5c9c3c9c369ad2c8c71c10ff03b5a88d7a61528f687d3abb498c6b33ef8efe0a1e1bf7dbf882ef739bd46a88285dcc0169011f2cb19905baa690673215b2b41f93fc67bb5f3b4c45699f7a73f930bf234013c823fd5c01abbb88dd5ae54106105f2b93d233acc174fb899e71314941f47023b91391190f50fdf623fff4225611b3ff498f1500a92426972db2323310a6942400d98a4135ec5794c7749aa36bf95f1235991bdfa843f2f1614583282282fcf9a722447321f6130c0330886e80dbc2e999c47fcef95b4f82e7e59665a858e3445035344f3b42ab2c5c9d8ec8a0ad145353ce08756a5c09f7d47994eade927cb991666e86716eb417e45b18a19d9ac8ca80b7c69e1c3804a50dd469e83e1a13b5a15ba4bb567ace869c39e6425d1063dc1549fb595e022c09452e1e4101fa4c0894e2be5f19fc289a599ad1108853403dbb9dbe3839862d37ade7f2644c6a18ee63b7c5246f2c8c21d842794edc9262df07db0cedabe61d4c5edb454518d0f0b93b7c6e642ee010d5dab62ee36142b549ed2a6777b1e68e3734ffbaedb9e1967a9c1935ab619445e95ca57307be34913842b426786072ad9e90834a123cfdffc94aefeca50834d5d79d056b6cd6faba4ac9dd8eeb6ad2713637b39c76933698762645570115a49818163d02330fcdfcd4ce4ab2c3a77b53cb7d8421b3fcc4382c661c8af9283bebea8ad3e02b054678bbafd6c44dc8117e5a3e40fd985815929d95d17cab0123b937921e823427e693c5fd095e8ea05f9441005584042b97d10681161b4c07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"64c03d714dc0110c226d5713cdbe66fc68695fe9e325f04773db2e734d6c1a1e","proof":"b2fb8309a39cb134104a9af353442f15e758f1363cd6c9305a9289c68ea0ab0d9a17e16602b34d7f5b4c78960f51deef02842ba7fe9e681c9cc171783c2c523a9e41b982495873a1461f5a8779f219c07ee254ecd95ea7fb80be2717b46db2545864cdab8fb86ca2189abaeaaeb7399f374ee2403a57b05d09e3cfa29a7b443051fcf2d2e7f705c924cac0253c05a7a852cf69d281aa4fa385d35f7b8709790d929e860efd9f7cf64aecdc37bfbb1665a3abf44f77d14810a1ea4c2db5d0c70eb2d88c86ddb859002b5d9d9e27f9dfe34d604adecd50937534d6efa0e6041702bafdc46765c7c3af3a17679ed678ba80b6741367ded56d1017d9f5158c7d75172c52091e68be7a2050b6fe31db93aa4e2db113246fe6da80674f037cdf34c437968d2c8a11b30f8dfb976e243b3c9e0cc6d5fcb08bb6e890e044b53a9f857054ecf54ceb2af956a5c3cca66ea6069544d619de3c4f34ed42e38c9e215846f133541e5e428611a0a6ccbbfd571d825ced539296574d7830aa28d222286586351baa837a35f7e9debdd06bf305d3268cf63b81a6fecffedc13b0b4128b3304fb197effb11d7e45423162ff56c44e143c5ce16e2bb8da864672635d6cad53f350419a2f685b7d0965d13a58e829ba482db4c4a13253cb650a57c864a3bebd20d704563a88f0e3c807320bc50c4fff2a77c75f592acdafc3e4626a2e777d20f04470964eccabd3ad9d63d0021fd61d830195d3603ca0106552f8a22e4b941b60916ad8b18581b536f8bed146d0cc5178523bed3d344c7fd63d93fb9181531052922548792e4320418d29d6ce6ebe03d75f188c06364b0b8bd9d16b2f7958a8bd91272075be23da6d66d87b4fcfde816be3b15a53316a69478613894fbf2dd4a5160f533db050a6402fb21459e4c6f1f9cda3f1f0e80672bf856411376972b460c503"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"622605a4f40599bc80a2414c4a3a6ad25f29047185bd703b1c5705851b348a4e","proof":"544d08b596f5804f42f2b6a8e60665542b6a98970063086e2737b56dad76b14b442cf334c53de38c5c560769e26c1e9f9c4a84a58458a78550249e7d5d4e9f67eeeae78578992f91fa852c8912b9b3b63fc9f499ba6879cbc25d7c9f189c1b3554253cacf17b628b0ed3fc9a765b301058ce78462a09883ff5b1a421ff14dd2153a9e04c8ef17ce027c5480a3e3e50473fd8af579e640bbb007a11fec7c2d60ab34c4a475d6bedffeaff58bc5e041586ba631d39a1a27a6a0d0ec9dd6d6bed0e478c4cdf348c783015b8502b3435c977ff0e5ae5d4baf444d5d5c7c6fa6ead0556e9ce8ca1e26039739fde54be077483d80e712df3a960abbfd8d07d9b8ed07b348ba2b905ce86290503f8315877fc96563636a514f6a9123908580412989455ba3e2a4c416bee40337117c986783189290619e518652ba31c56f831874a130274804a92240e1d6e616bd4a2ed3b77754cd393d5559fff137530c4620ae48c3eac063ae82fbc2ca43af4ae877361df4cbdcc6306543c2ecd3138be8725138956f2d0394a5fca77b4a4e207623f24cb37c35e4e2714fe1f70d7c40ce16e7f152368a116e60f3d8752a505bc1d383035c851fb6e8f840dc1ab2bbec61530107d244018d8a43be07f140f50bfa3819bb4600c51caf63d1925962b0eb1eb3de1f50bb4b5850b05b4bdee5a71db27ddc97e3b8bfeafad66a972f4f7bab1d321d3436e94ebb27200cf61fcb7983e522c6bfed72883e872c701a2db6e209739fb7ecc29c83bba683e87c68712e6252d4ab0a2f80b811cdcf2cf0e796d13460369569215c6a7aa34ff0aa28cca5712be66ef0b3e7d95aec5b62a9bc001c412d24ad7f44d556405fd23ddd23e6df298f520ead6d13e37ece6f326c3f32323f3fbb2b88306bbc1f9d51f85c18c8550829b9c89035ca9597d6ce83ceb089bf6e27a04825f0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cead9a48fe3a2cffbbf69dc58dcdc37e860bfcb7f651b282f564a0a8a1d4546b","proof":"50e5a82a724463c1959d0255ce57d4a4e7017e6ecce0439afb641768c350b661749bb55a04385f5dcfbb5b0ccfaced611655a7e8a11d5747eacbd16de9c78567e660a9cc938e90cf4454324a4723030da9bf537dbb8adca5800a272e87b0e068165d3e0f9589519cf47188852cf3bf7f48a81eec633c18c3e74d01874ee3415b9d42d8e2f60019a2eec8528367b47ab0edea94a0a8df494a9103bd13f3f5aa0e40124e4b54f76cf9facaf440a3735b7d2e2cc90a4fb04df7127a3dfcd1598d0d02f94cc42e69d3936633916e839fb198e02960e5dcc904b4eeec6f995684f90b16702e13edc2d0f38bbd5d30cfa607aec6257b23467418f6ef6aade317689c31461c2d8141240aa83379d5213bba8e28300cd2cf1c602960bee52c178a3ce710ceba5d0860df7824119f5d8752a8e9d1581ebf99b9dc8e35358dfdbf74c11826f66255601b2b2cebc3e9d7e6979cc06829ef4f2c4cf0523c8607160afee60d46a0e1782f9a06061ffd932dcc13a9ccff5f111455a056fc9903ff796c8e0ecb28ea2b4fa739e7111005b8e027a8af651433c02a095543331b61de0c55e8c1a355ca0d6f6f88c790b8e39747596d48588f3f7072a7df08f26c12ef29873960e55f327edb4eec9ad524e2ced6d7ac3fd46212f35d4a74911fb5934ac9e4d0c4e61d3c91494c42c03a2339fbac3101914ca19675ac04d96facba8e0ab08336aa937c380ef36527c3ac0ddc53f921f4060676a04bd464a552a03c612f60ded9540a671cac1062093a42c19096248a1497b314016ad45f8e9e339e286c122a12d565408a0603de9f6534034ae7ad250eb26b236ef7526c8a14adaae74e58389e5144734c0995c8e43bca4246ed6e2777c4db0076ea5fc3d385fd3d6f04591ac6ab1203ba314a33ee081f59d4a75e610a488932b3dec029c7eeee70a41961d9f4090d0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b2085f281fdc0097988a54985432f7f124f8cb157800972e47e458fe91d61b7d","proof":"4ebdfa63018f903824e226bb99f34d8398419f4b7762b465254171f03d466e4e706a066014c5baf6ec4930371a2a5043a5bfc2df8d7e5fcc7734edaf7620b26210747752b90feb5dfd7fd1f60bfaf993eb593bb16a66cd4f45c211db44d38030883538992624efe374733290e591d0366ac78df0f34e1e16f36801f7f41fe52ed18c95a0d3b64fa82989eb32d7b53685eaf0a80c26c5d4c8c65e5b90a4523b028ff08844462c67e9180f64893187dfd158fea57f09731c0c27306e881074800a09da0a4fd0f53b17f7236a0c63e2d198631a2c7ff68d416d25e44cbbe7ec450c08241e29a73f5ae3a8bd3da3b4ddc8ec5a05e388ed3f559e2b2c58585f037c4c3aa5c82346de88e3cb035af97878e3b421f29674beab2d67bc4877cce20ff10aa44dd65a750978fab405a9199111ebe19cebced2034d6eab3bf6c9d5701c3578f630415549f1b50ada2150634ada75a926dc40844919ffd2e23050fa947320348008d37bbb14a881747cc1155a3abd583756fd4caaf6f4492938918e9e18605178aab77deec4d5e93c3e90c210eb6e99e08e8726710e0499a672717fb5e3ac78c23ee10ae50d8ac8fda90dadb32724d0eac49eda62ff32ff5a1c3fdd3dcd093a9c0ef780419e1a0f7ac55a306abdaa157861b1f52b6d88a1352918578fe0be3d507538b056716909c13d4f8209f8a6d1cac5219ccbd6981e9c1de0f198a66a07b82516338c3d5b39f2e5b705fb22ead113856e332e8c225d984c8f1bb075942c469c1e59f6863c690497683049141e4ee9510141745d4b4b8b631b4b5b56c907f4ad660f5111f0cd5b74f0923e0cd7e2c222754bbbc0eac1bba98f32591c88186c9272ece5b9469e5bdba036732285a15be1ee522436896e8a408e5aa023a40ac619855d6107ef18fcb4f3ca832c8e95db36e8f9104c0ff20d93e04e3712b60b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dea67b438e79f1eb7f5258144156ea59dd7cc3684a251b9012780ce861e05e78","proof":"94a6e3f391d3bc0d6f3267114da966cd4719b82aa2b5a22cca9e5a37501a790f9a61bbc0fb1099feabc327a870f93cf41bb06b3bf4f6130ec4f37c95ae13af1adc915f6e0fa3ff134a3af8f211b2d4e010799b957f70ae7dc402acd9e7bda61b762132e6c3c519107506fb6bba9d6150bb678c6b820ed796594e61a60889f93127d3ff91f32767284f3af9eb4bd854a341277961fbc1e1f39cb91debfdfed709f1fa38a45e673723a94b256415053f7a5cbdec3b5aa32b568864c9677bf1d005c4adaf11dac419f238a9ba199d248af7531f8558de62bd8927f22b22031807040cd7f390c6b70ac3655acf3cb21270bd5b324e63daef89724edc6acdbc8b446e808d72245700e5c5d873b886f84179baedd5ee7a34da76cbecc9810c030c3c631022440125f860207a69e46e9300f72ed6418872ed7c60f649b320c586605253a882a632536da873c20e1e9fed7f54f5a9b3382804a960f9b1ecece2d2802e03aa1b5dac8b79bdbc61b67f3cf6d36ca352d127952aa806f3bd17f7b68185ca0160460b7f79404c6494c2510d72fffafa4b36041d045290c52d147a0eed2bdc1dca44c2a2d2f27af8ca040f772ba31fe3532dcb8a1d968409924683572d7c926c326ffbf377ff587a95a35808cf58f778e78ca535f77ee8e2dd848833573f4347c6908f76778a7904a1bdeb2be390ab75250111b94fae54284396619e36dec424862a2b9717f184c70b821a1f25a0549abb65c8885ee24300bb1368d6617a226afa0cf4e943540907be84b2918f9fe4e3cad895195be2a258b70f819b35ff73514ac6d93931c491ca9ed36697c35c290e3a2b8fa879e72d7d33f3ab7c9170f85becd3bae5ab508746fbb2ebfe84ac72c7adf99f009aa8388dc08c1bda8c8bf203d94af05e5ed61123147c4d7fa820ea338804666169918ac2045790ddf1ddec00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f66ba1ea780ca73ffd0579d238233c5878fc99f59b4b0584869fffafa9b78041","proof":"b4a4d34a7912325d78abd582897ba2d9ab689554ab356a0b3038dee2e419c70c460af3383bec94941fcfa8e11e3c34f286d8b00912e84fb215914105e77c6625c434019ab29d560dc487a0d32e23cc842e9dadaa790e6d1414bcc9cfa089184e3ac6296750ec4b384a3edeaa2d68d4fa8e6d0794fb032cf502493a9982037417f9ea23911102f5b51cc6e040cb4cd6d6ec7a7b8bf95fb61fd89470191337cb0b199c5f5e0f0305439e355259b55def13556d2041198397ca053f2c2a5685b800e9520249c7921d068646c97d6b0c0a71c15530edb6a2b591cecd38a5f1488a0becf2101a88a2f18c32a1e594fca1bbdd440d1ca85926239e7cdbc39b1fc9d92f0844e919c964c5c1951d8f7e6932754de0eae87e696671a35b95a41559b0c8312c0c054ddf5d1feea1465717559fb94bc79eea1e35017e0595acaecac73b9134beb025e26704eaa37e5fc2e9b57b61563aa29b8905a677fdfc2949da3ca0473132beb64626838c79bba2451ba0b0907903952152c0878db5d2ef307b59370f456c07964a685cadb14db86e321ef991f32f53ee3cc10074039b4495270b4bb1568cae0b260f1c507ec9feb08213b316730ca405e46e97658135699715fa456733be781504841cc0afd9735c7f137d15aed41d81bc2a775c93d45b4dbced85a74e4c8bac681ccccb4d246384602d73b2a751e5d32ed451fde23e652da56c9e0a43984143c183010e878fc660bc4f1dff69921b761cc0345c4e66b00c7cb8c9a67d9619c2294b28552f115c5f036a046f0d9800263e070510f8cee15d1694c38171e8d726245e0b022c901b51644927471215b19f4d05a722a5ef329401bde37304865b1112c2736d4eaf5f1147dcc5a65aa1bad6371e46e7ecd0db69d02738050104d96bb78943bc44f1ee2a8b5232ad2f9b5be4ac69abf9ba810ed7158e9fdf0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e47b9ada4e29d9b1b65bbb43283289e9b2a660868af75911265aa6dc5c7e2c12","proof":"1c0718222f93d9ff53403ed01e1d88b50d577123fb4aeb764138d71b46273b12b2c9d31245fafd4e440199df27c202073e7d868e58e882de2f965b87c12a701d0a467cfdd0bfcfae76edaf3b3227bd01b5954c6012ac119e5af3b12e0e3d2649c8d32bb0605a48c2673a3d041d9d8fceeaa0b5fd0bf95afa07d3d7f9b40c5d40742d54d7662f13a8dab3233efe87e5a5ff34c99b9f2bb451f5a9a1fe2a29fe0a3ba48fdfb197fc91768107951ee9d850bf1131c91331ba7edbf74cd1198cc6069a26cc1d3934bb3ca5fabd1844fc28bb0d9d35287cfdd13206fb3f835963980c500ea0adf78ab93c1e2a3fc9eadff6021d18ba820f986c981369d8124b0c5e2ca8952eaec9dde75ab8e7062d30e1fb0c4955567dfc9abecf123070fdc30bfa585e8ed6df9eb89b498cbad0521d120b2fc48309eb21c1b05acf4297fc66daef5048cbd4c23a6641b285f33b1d085f65560b92c991369c481fd7ab8df2809e96081ecd96cc76a72d6cbcb7693f1fbc05070f7c845c8ef888a8ed79a7c639426b1b5e4c3322d9bb86407f275f02ba3b993b05d0f9b3cf1ccc045e0710e5e5af4524a8e93ebf6499e1590bb35da3f4caaa0c3cd28270f1dee9757733f97c41584f172c45c3599565b4b55e4a88bb34f769885d280d1359ab54b747afb464228eb6486017a543ae87bb7c05a2a074c88055c398821f2a507d2a377047a2722419536e02b60803f8ee2b778c4791c4335be54fc1094b654ccbfca97ac7fb034cb7ca4076f51c16b0308fe48f9d2ebdc371689e880125c9fcd2942e50738f46da86cc6c8072bef563135d6a23ddae070cabf0bd7f058bbf5d3638f31d241ab3184e2f31c1b4c0c3d5222e78e6c8aaede20c18d98005efa144e098fe7f577e0cfcc5410339edb5a8cc34ce2ffbc9d10a0c6bb019bc5ecdeae859fe31cff03a43a8c1ba01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2a505d43bc9712b00eceb79401531e1f648261e9bdadc011f1ec3dd1acc20874","proof":"aed396af9102f7f551e2d40be0fe3fd07cdf35aa5fe904463d8b0df9ccb0b364ba684b11f1140f1b97db76119a22658e9cb5ea84ddbd333cc70c1998e6a9797e064ad7e0d5289620c7159fbb1903115c3ba8ea3f1af0738a9ebd57c5cb6868499a65accf5c8d692326c647b8795dd4a050920cc8c9cdce0db1e8adc74ea84e0d01d9cd6f7c7e3097977775fa9f85a95e5f734e125cf884bad3f5b8e0c25c2108d8d9041598f4c5a8de124fdd300dfdc68f974a82a1a9a2f4a50dbc26bdd4230549eb7f40785c5a26f1e63a6d16085ef705805f851fb1aec95a8d8f77465eea0f92541d8a731053737744fbc1e5c843ea46947bff14bc41209ebaf4064ad30742641c3b17b21bb7f6ef24b34ed792042025bebf3f9e48a9b964df3aabcc43cc35e28364d79b800f8b6c1fac804cf6f6f084e3b798d3dc82bd3d97029ba4fcad3482afdf0eddce6c400e42a9019aa1643ecb4313270ea50cd0e858dfebc1ec81160e0e5c9a02aa64bb514112bda28c01dc7d5199a305b77f240a0a4270e4e5e10d8afada038b0b2d59c12f425075e1208721e379f98d8d42328ae27ba24215260458123316645a024702d5e722bbee6b29c87c5217d225c03e13466a9ff8ba9e4bf4226b6767dc466e78f8c3c3115fb2363438cb2f7afe313842d41355c5edb9647c572c45f27c48bff1eb236c1b5acb10062165ede942959e425df313ac8468712a577a853c1eb568a8a47b2f8022ebc0110a84301230f5d7803d39615caad9139e2f9cf8101917e7e42c888ba092c62bc694c16073d5ec3c37e3245f59e5720b8a613ab9eeb259e76dce196ed855a7523ed9979575d465ab3cea69dad864c947e82591164ce6d4d3882b7d3b3303a5e051ba104e3034dcc90c9d334e36770605851d0820c31381bf4e509ccc3f3af71fe0e75bb795f3d762ce2ec33ac99dde05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bef3bd554221d06bd7c5daab6f3ce59ee18bddcace52ed7ad8c6e7b09075f853","proof":"2e7c8ecb4f60d74b39f964e575cb75e7a249c3bb05fab863d06babe04c20a25f4e43883870e6182e053b24771c0b62e37eeef9c7e5a3051c6cbf3ed9a9932975e2d85f32ee1ed1660947e161fa196641304cc11a7d4fb19ab3950b78cc9ee27818c8851a182fc69be5fb08764e89a1041611316e00d75146ddd0056e4c84427d3a5bc24d5fb8e496afb232a4e2bd6fd5582e14c6fe4749d0e216a89433277f092b4e37aee497142a7ceaf0389b8184e273f787ed9604fd601ca10d4669423a0adece4d85720e127f995c3a48db813434b0ccd8d6e4c8840a457d11652b6ab7023ab901aa088f757319b3dacadac1c1097d80799243a811460880bb77989a0527fa0bd06c8d5da2c475ce9870bb6194f9b3ca66f8fcc3b2ed186650d3b27feb575ad9cae39fa3cfdeb19b2f8c8cf23005d496b096c8e45d62392c85603421dc119247bde00541066845da71a5536ec8b34e20b3ae1ff3c5a8a68949577b1fe600a4d3aa8c061f5c70387d407c67522e9c7d5abb7f06260330165eb749180f3c224cf142b31c6ad3057458f4d66fa5852eb7fff9033858ec71139d17b5adb557331cfda4b69ce2a9f5559466bd832ed33e1817da120d2ac3085d32be400486ca65d0dbd5c8e89a6f96b1aec951952059bdcecb59af04b1207813988ed3474ca93a489b789f9e1ce12d9801551932118c492a13e8d8c834e27c589694f7deeaad1d222bd1741d43f793d602489642573c8a8f81cf266b4d421b9f0b5870e417303df23001c6b9a40ef8222ee980c4b40b62356e6ff0127dbb83eb48e5ab045e893e4e4410075180aa686beb853a88be4d40c39c6a276ee8df69cdd6cc3b1608f86c8dd784599849095f3c821ac1f067bea9f147819058ca9a5a5c918568b6da84031f4375f902f5d772590db5d2d9bc2cce8e22cb9bc72c156cbf955bbcef411f06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7c9ac65ec4fba536c7d0c9e280c0cab6e0c31f9d606d74d05e1cc5a8c296f543","proof":"0aa58a9abf7d0893a33b77bc713089d3b343bd0727d61fbc9f2eadb853c5c3648644c079039567cec4151541e8c796284eff721909ff136224243db0d6c67c5dccc5d18ef3a0fc91052d47ff7ba0b640a10475fd6eb6db59cdcdf9279dfcfe0284ba4f4b066543ec07bfa0f85014d49d0bb2c56a49af3c7f701b59758f87b543c9362c1ffaa26c43323eebecb484dc53dff3223c87e5dee82df93a665f5476017242a7bc5046a003b06f7f1559a080306cb749068ab7546d0c36df9e23aabe0d529e3cb9b12e34fa216d553d9edf54eec3bb372838b3ed92e63d231da5b4cc0deea1ae5ea32249163f30326f185b702130ef116f7d289f2956f37488bf356d24a847ad3914575eb20b055bb90646fb33e7c3f669f9b0f1a6e7c5cdfe7258a8769ce9a8feb5c74d58137eb022bd06822846276940a3311a217bcbe6cc5591f96a4a428c0a741c51495b1f352fd665c79741d2af163f08b788d3adacb749edf321c8697e8efa41d8a6b49115af1906a7fffdf5d7043772d84c7dd95cf79725ce6ce84cda999996b433ab6e22e40890a92023a3359bb02a5ddfc463bcf7fad9e6627c89525fe4b0bf48e9b2fca85fd90c314ee0e7a80be1722f78a1e4f1d873476de83e5796f9dc79425533e924257a5fdf219cc248b0bac80df6ea251ecab079167cf31c17680088c237f4be0e7cd20d347a8b76f30994668b32e6fc4499b31264dac4dde4cf5b21fcf32f060bbb94e7e0a33ad47a540248fac01b2ba97fb1342a16cdac105b1ac1d5da829ed01931f3757cbf4d59cf65a49585d2ca7eec29133422e94d33954f15fa633a81e2c7c5539160fdeca3e2f33a6d33564931a47e0a62868eabfe342aeb5ebb6b354efc57573924d332b1d37023c7dc3f3a2327f0360687a800cdfe2a9bf4bee927ea9202f7315159f744cc9306e8f1639ba3759d8507"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"36dc39991000600b56224374a00d445cf2905690660291bdd0e54a471fe1642b","proof":"428712898a5e17afe350db0b316a97855936e703601ae233b1cfad9cacc9d577b2e8a79dda3c86fd6a2b9f9c333118ef92ba7cd7f44366af263e776429b257313e75eca74fb3532055f23d2ba5b1f7c26d22e4c27fb9dd0bff2f6a9f8865703540ead74bb521cdc3d039badc351398e7abd32efca462bd8d2cae94a9c5b0a800fc203607d3b7fc0cb9c5ea49f0bad7ab25cd4879c56b890ec3fcf29acea9050c101536b234a2c36640926c779dcb5ce050cf7be59b029b787b1ab5c58ad4620ad179988f19689bd74352f33fbc97bc70ebd9de803e7440e7688833305fc9910690e3ae1df97e086a770e7657ca560cf0c9cba506d615d01347a4cc6c660f9c3bda5fa7230ae86cf561f2bbd58e93b47e182ee6728215a21954911e21c8f11e6786767622eab1b73d54d4eb51afcdcd7239e50650ff4d353bb19342a28b9f2072c68b372234edd7ad9d25b3399839508e83018180d53b2faf9fe215b21649800a34f53cb4ec6eb0aae47461b42efc9a6de3e62c74522e65474115f738ae9ff356ea7d9bed59ed51ba661b2cad9a7f305ea34f08443b9317f1d266020c79c031682a43aa2c118e27d41831c9d36d4e200c35f7b95b347541b4fa68339a8cfdbe32848b05092b20a9538c8e6027784b375ddfab646bb8c4205c89f78913482e536c4efe01f255b5bcb0926957d4b66aff755c50963317a83f6803a7cb3ac85ac10122bc68d0d9ca32bc3f6b34dfc124075bd2f220b075aca1f4b1ce8ade527e477cb817f483fa5353e28065cc684c8211ef34c24fbe3e08e67bc8fe062a1e26f3194873ec993429f150f6559c17a186e14ad490ab210298d2384438d130cc4f067062fd1f63e88de2e0c3cc07df7401987ef96f5f942fcf50c67587b9215372420593e77b149becb1de0b32a467d6057496ca7975c9a393e2a7efe5be0382fd620a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b63688b58df935839225beaa7724814fcc8df371d84db0bef1042112b09c2c12","proof":"6c1266a5fb592688943abc41865a132ab5f9081698566cd39dd5d88cca8b4f5be29732d528dde2d49661dead6a6f8179aa0721e2e73f3d776b675b970911797600726be14599fe3b39dbe82068cbd39174870c7f1a6312bfc84bda37fc9a767b0e9a7f3ff9bccaad0c49a792ac8bd2bc09c4af8e9f75a844948f40b484a186153e4d4b72f2bc4cb6a1c7dfce3983546d99040fa40a7c0d194713d1adb67bbc0c535ec0e9114d3470fbe9f727d77e002521ffc87eb5f1fe85db030ca1b868180cdd938fdff1bf53c04791446ea49b484d990b3d015aef8724e6a4bb5b46c4c0021e54448fae460b4e7e993765853fd30846f2c31e01a4677e53bd1bc3d075b358881b44f6bcf0933e9c012780424318f9733088e5bf98142c26d5a78529c6f5531e726247025d59801188bb5173ba0b99f79a51dc57d3a5d6f190a3dc2a64a52d30e40ae6807e52c71f5c4eb0ce51021158f31d73d04ca1056876228992870d426af87de51c76a163458751547bc1de73b084a6adfc600c4b6015bb5adec3ad73649ebc29f7b3a4bec410e946e401dbe0d50bec38b734f2d0f2c0d6cce448e61e34a9931a418a08fad6bc84d4a7d4b9b7b96da938ed852782386d54c447952f342e9487e35f5bb48cbed09dca6d80931b703961a4584c203d1bc4a76dd58fbc1f5a933982a8cff2e14a3504c1ff46702fb71297e678ff1eae6c72d6855e558311f0e67f88b2493611325afa994e27cdc327907bc8e1cbe8f51dff49df9aa1b0305c0efdea4e2efafccc817700a767c50679c0dbe70e8a67fb56c20d54df579205aa065cffc3b88b4adb511b3d5806640d9e9b9381d830e8e52b5802157d24df44e2afe97b50f53538937be5e14e191c73e81157b6e65a5138aa876486134b220cf63316a528da6f11c4ab537efc8a970818c4725cb9c637d802fc262456265706"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ca03f9df4ca496ea58e5e904bfc56d7e00c15d8daeb63fc03329322764d50c5c","proof":"50f5f7c91e1d367a1b0c1e2e814eb1472031b7b434960fe3c7f1186ed536f60bfc81cc2c0d048f08ae0c6196b828777f2f3dc484b5c4cec887ddd9ed1d0600690ee806e6f16ed60861ed6684e6e4ef06bca8b3242af28edd48c27b1025f55d1c2c757b6494a63777aac63a0eceb192188cfe31552a19142a512c2c38da9f176876c2c42fbf0cc7c81dc7b9b924afd32eb4be151f6d22eeb22dd0a29d2a9e38059a8fbfa2748708e9549e9290d51143ffe28f356202d82587b92cea8226b4b400df15d7d4800753017e3afda6f8a9322f9bab78cd652da1eaf5352245816ff60f8aea13bca2d576f0053855616eeea424ec7a279075c6cdeb91d6f564458fa0470aa05ac3eebedcee1837f01491ce802e87a32c7b92124946bcd0c159be1f0c634876f4343f8083f015e80b1dd6549755515968ed79ca88bcdc48b4727798c64410a672196e6f12dfa6a4938e9de7b7b3f5765c664bf7ea206b4562ca7a7db93f648b0b48e365c3b6e50902e4e820f62c847f85b9b0ab9abdfddac2a08e33b707e0f989eb19c0040759a0eeba0f1ef3431f1cc9f0824defa29a61e71019ab967fbe863cbb8b692256b8774088e7fb0ef277495c0decd6630c13d031eba5337954cea14c8e9b2502283b1d71c975d5f89f9aafbdfd7afb0e893ff28dfce7816d70343c1202ed46b217c5cb8599f7a40945aa777a2d019cfa13afef27d030d23e2df629731e12c6c4bade229b85164a8ad98bf40cddb2d755d3d2a595052f50c15e083f98b7a6f360edd77054b574dc31faad3fe39734a185a5aa2bbb3809b8bf1ff27b4b5b6c5bbc8af99ac86da97d9429d9acf0c5a22ac8f63736c178310c0000dd94171888be57a170a6cbf46f651bcbad623f20b5f1600a519c1c4f60ad6f08d81e7b7cb7fff964b674f328c9050b65814c693af2aebbb72dfdaa7aaad7e509"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"203001221d7fe72acbf78d1efa24e69ed78185a37230e5157694d1bba976713c","proof":"e60476074a0c9f701e0a9a5afa277137e4a2ffe65a568169821d023391d2af72c8123f532718078d86f6a28776c742e44de335b5d35ac995fdad5a2cd80b63683445aab9d797ae9a117a0018a1a08cbd6fd5733154ee5f919ebd0fd41f122219dc4c15966dbea053493b0fb76f9eeb814d8d97a29aebefecbefbea0f322621525bcb99e24bb099cb706c7a9de502c95816dc37f4f64292803c9d43e87450f5088ecd2069a82bdc05859288e6974247a21a2ff1f315c71b02973fbcd0af1b00086949100d8c96233c92a21b9de66368813828bac34ff2cee7991e21726f475200ca47afa726ac09bc9d635c5ec0f8f701fc1c49d46154f5461790af918572c36c360dfdb8538f1f496abaa3637bbcf87e34ac11bd7110526a9edfecc614d86416b04456549798a402f54c48fa61579a2cc950fafa4ea48d26e86c96e03bef7c409afee182eaaffcd83c02d240278bae38bdec25d8493ff5a0e55f7de8597296642c3fefa224289d8f9a3c1b934a8c64f9640ce8ac168a3a66e391bae6105c656986170f64b2500c99a281e7a2056e3220e7f74ca784b80156130aeadee4f42a47d2a9f4eb254270198e48121dbe720d5754c608383a6225e3ecb0f61fb7e5f94e885f3cd8a6c4e2d37e68ca6ec79920a0d0036703296118130385d0398ea4e70622b259572f7919fc836e4a43f1e33ea0579e7cb467bdcaa70624888c2acd68714cdbe614a8239360e8f0cd40f355b5a611002b478a5dd7743c309bd2d164c7435e52d51d0e7a74067f8a148ff8ce59354c25b8816588062341ffa2a0ca26740b9ad0ae0012da54fbe4cd99df46f659770b2464f9c617d7f6f795fa51a276b53554c5661b58798e104677767770f523956537730f8b264f6c5ecad00cd5cddf0efe75a59018153e01d1e3334dee030aa725ce87d7024a02fbbb9f39cb8ad1b801"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cc46fbe5278f6dfa1766931cba9adb750ebf4267ca54916ab06e1aa76a03982a","proof":"84fa0492508271be8b4f22d0ed0aaf227a43afc8942a925df1a45072ad2c7427a0700499824c61ceb8522d81f2427f70cc986acf173cf053ac1e59d80504fa672c0268e4ab10dca80099a96ace89f03cef74a62ab3f708227c33d8457a5a283d1c1ca73decd1ec1678ecff5d1bf8c6ae235eb8c5273082eac7c35c92fa82c72b2d43b9db46fb545171966e7a910b2a0f957ac18b96fd49cb3330246ebb10a906f3625071d629216556ce2368784858d772699be110c389261be682e470804103b52937613a6a9b583eafbf111d2782a9e2ebf6f90c2765d53a45f5014d90a80c68c6947158bcf6d74385c34c1650d3d1e6b6241768127b038440058fa73e7c5d9cec68db8a55393bd5def4210726d80d72bfec95dd4a96beec0f079b55b3643eca58b0f0282cf6e9dffae3c1dc2b104454936a16254c745779c0e0933a8d264786de446ec40ce8cd35235997492509e79f1a0bff67d3e0bd3e4f0b1cb8b504692ad2f0b5db4ca5abc683cdd8dcba8a72e19aefe68adc86638f359e1d7f557c49d8d1b15c432466c33fc287236e4753ff75ee1a9d42623d2b754215a8c67c88085a775140d67e86c30c6afd8264244d1880d977253e8de7fca0480224d134d2479e51474de713a8d271a56aa48e87d556676c205b961ad9e9e46a6d139b575e16b88a001e178a3112e7ff028c1197ec4f1294c8fd30b965f500e7e256a260c70828f86d90994d72a4d35432c896c829d6af8ad47f21ca487443246548c393ae0a0a3df00edd0731ce50ecdf4afb5690ba05f158ea933964be246badad910277136e7e4c03bfb3741dfd777a800924755517ec948bde07b2db4ff50c22f9bee92ab2c867d35a64a0266ae6c6877a748bbe5a845461d1ba938715d659d7a375eb0960e5c0683f523787aa6e238a132b32e86f4438bc418e43a9a0c170782780890b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d00e3c96a81f59826dd5dc2f11a5b94a44067925f46efcf1e9dc2b784cdfe609","proof":"20338ea2b3c52d85a368a8bcfb701a632c0e8446d4065d6427289ec7bdece96a4c9015744d38e4e3c6fc1aaf5a623eee796e8747a2f0db2c5f6776aedf41ca7cf0406624a38908a3a876f6d03fa1a722b3828b0e3eac8d69f1b96e04eb74f61edc6606a7dac4dc1fe9d0c1a8bda80d7be66cbb739dcab746cfab971c24e09a1a236023ed6e29240d8303432ae1cd89c5c1f294531314aedbf0f2d2913d62a20463819576ac114dd135d6cbbc3af92875ffb56084e5843b5b21fd3b9e3dbca103e4ec5211d60ecb9d7d948a70b0943bc6195d4f8cba8c9fc54c4cb66b7caf4e0d7685780445a393401202c256f40fc9a22cf30b841e276e7b653621969f29ad64e8ded6b42ac72436899f263783a4dc40c98ac5f23276bb33cde079e3d1653864f0947aa7f27557ea80d10b89215dd6c13257d4a1f48ef5e2352aeb9b7838231f629c52ce1987d41d767e34b98d2439e54beaf9cee006a2a01fdc1583c6fa990c128e6679c494308f2ccd71e66adce1411f3236900d5b185a9b5788c5352e5340c29e01e4e1867aeede7daccd38dacf64fd51c998f5134e6236ee0b5125d20a78d001381bfb867fddd870c39d53fd436851c3db34f5f2f7f5a88341715cd7636cca4126c2d0383b6704762ae8595cf19d1074ff04329057f3f86d06dea3fddc77ea73423110ad04abe3a11d8141ae4306c56bd363a07b32c685bd97ecc7e97a10a01ac164049b541d372cb4703a3024d37cd850373f4db7cc294aacd0983b5f77dcdb23e7c7077db025491ed63557612aa47add8be85c27177b8b799592dc6738e4ad76eb332c0024562a92784c002dcac9f6e00c5e2356bd89a50d38fb7c482d4d976b58929c14c548d592ad1f408d2459a79a932e48185e54f457452364d3037974e707a3c8cb144a00e80dd3ca2c24c7faf684047bd19d0c3517242548c40e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b4d2bf241dcc778dbcd8c3f24471db13112025e7903fe67d7c9147ccc8fc1e73","proof":"48407af10e91e451c852e1038919e65657dff678bb85fabf52e3159cdcf3556996e98daa5a96a9c167b152da380c4f3423fda70241802624c35dc4988f25104fc0c1c73e6efc71ff4f2b626d4e5720dc4f5623fc69e6d787dc25e6f65be90f126e73736ef1d6ecb0556e9fa2a7364370e88f6f8264b3fe46b5ef01e1a8073d3676c09d2960143367b36b3a836411ae2981dd68b5e24dce94f7adb6fde56c120bc147cb897272d0245e944180bba4c1b75e9d35201a0d080094b64f8aff25ba0dfe3fdc6060dcf2cbbc86c5745a900dc443d8966a10aac9bb7496a45f098c500b4e55aad6fd093d9d58cbf64c51e1b43062a92bdc27ac142b400717ff2efb40327c3329becea6e9c8a551664c7ae5b83d6c96fdd77d581d0059eaff3d95873a175030eb80d8237e50367036aa72e80470997bb6b7a0eb6c343f856de2cfd90504349ba700907b1e8588e6814ac00662522b635675e2a198d4eb20d59caac8663b58bce5fd196b5eb113e543bf236707e4baa2f9a2cb0914af3899cb1f1da678160c64c44d7aba6ec0016d511181edff256b27fcf94cbc7fcc8b858851d3bb2b7ceab6ca2e511b449264c4a8c81445554e705542d47741c6179942d426fa6b967c46c639969e2d297afc049867eb281d904031af535839d4dd4ca93cefc39a4e20fc824877dc2f96c3144db24a8bf03055ce0a597d28b94d1e99f6ec7d6d1aa859d2739770b2dbded10f668fa1f68bd88e3a1919befdc661deca72ac2818a25743b8131c92769029e9eda8fe8e88ef82e775a0208f0c046253ee970a5fbcdd533764fdb731bb3c1509b68238816ba1148fa13b490fb8829d26a9beb5a298923814ab448a6df80805f174314794eade44f8f78aa36b3c6461cd2a0d835fdda0320f939c40c6bce13200b1c7533a1e01d087b3378bf02b55bc745c3b1cb27b98190d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"904f78f623b3193485ab8ea566335e9cdc6c32967b6c302d1e4474302210c010","proof":"b4e1be46c89ff058c02cf92162a273106303e20f011efc7ef7f619071ae1294eba33438c46b05752e2167958bf84ee1bcd485467aa4ef7bc88bd959375c87236e4a691dc4054c2c59e5a801fb92af58b8129bc7ba62d9080884e4a59cf4dd278165014118d3d04fe9fae31f701ffb6e9cd581d1bfcae663aa258ddce9aae91400132ef2814a00c405af776761071aafaf2900897af6510d37f2a7b5c7a709a0d0aeeec1eda0c53d0959c23c1848d5a1a03d01e5b10ccea1a23083cdb67f92e002bc996976670b596c7dfa0791aff0d1142aba05c35f385a7c678f1218a623305905b342f22fa3f0da1b0ae9a78a5efd99d5f49691aefae22db766f70dfddeb2316d22a215ddcbccb3a91b4d4b74b3f99993ef936dea43cc2dca43f41a45f79292a7ddb08c5a8271d1f66d9a3c4a55acf650a485042ebef98a902291789394276726cc318b1f9a21b5beba416d95292992ec11ac926fed0655ab6c19c2cbaa3636a01e3231515d489b719a9f9f419b138a7aa77bf6290952f973d2ded2511985218166f0b4ce1004195b0f49f397e75418524d80cac500f2668139e24f3f8ca552294e419a43b0e3290778c3c0508933c39816679652aa519e3f6627dc1a6122b6acf4f5ca6d731024ac6a9a2d029f8d2e72fdc089c33a3038181f81eeece4b59aab95f7c2a028abab2bf2c2a893cad92719685dfbc3d8bd7e1a8f754e75f900a4a88cc5d091256a7a42391ee6d9e1bf0d79bb5d4d2f5e1fc9c2875830bc5c42428f3bbdb5c94b88ed800d0366e7365856f75625028e455ee89b0ff7f93a0fb1536228010bb39a828eb163fca1d7439cb82f5d9d939f2b285c0604dbc81f38f2b488d4e24cee47b0933bd3312a1e26097cdbfc6798e27095baf9fc1d70b2b9107437d509458373239c7ff295dd88778a3450fbc8a4ce05672cfde9c8e304ca604"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dc28f7e59093da80c72b6d8d0c1f297da68b277cdd98cc15bf4af4eb5203344d","proof":"5c11ba594d47b3463c524fb61f00c0c6a95364b5e529ae67d0640dfc2975025b5ca2b5a3b185774b496454587cc7ca62ac36e67ee569888862cf674e0abd0b7426e4923f9d34c68f8ca0367224fdc95f286141ea08ec5556dec6b3cae7689729c4c89af7c9a79606d7a7d83d71e15ab37fc058897682f7713ff08f5845cbee74edc3c276ab07d7d0d850341b22430e9b15ffeb80a59cffb29e19e211d72c65000672571839a335f35e8c3ccbdba6582806050ff931cbb7360ba07eff8207810a83e78c5269aa685c5c4a3e44c539ace98d9a992f731dfbefad709162041f390142d0159fe579ae938da9c38ad40197038b91453c31c42b1370c614a1d2a7cb7a3aa6ab39a4331cda72cfb6a0b6a5bd639f26c8e9afc0115f788d7c39fae7c45fca34a0545e476b4d596dc49900451dc10af209043a33ed7b8f7ccc12de05d85e549c113ad8cc355e7afbf0170866c7f7e72f9a546bfba2fd2b9d947807fe3e589a9a5e08edd0e092e5797cfdab8e288e0651c24f53d5b25bf78fdbf199be82411a2c2a630f93674d676ded9bb2ad86373ab8aa7ae51069b9a7b2b9fcdab2fe6c84caaf273538f45483e73997b08bd09adec58173294cccb86ee1f77f3e96535eec867413c3d16d17938c2d7225067ff5999ba8088f3577dbc5b5bccb3cd98c3a3c39c63f4df3bde68eb195b9022a275b24d3e2cf80b0dd8408103b3dddd9a4776407bd3216103deea6195866a2567ed84eef8ae6d8a0ffa064efcf292bc3501f665d398e60bebde17978e2f112957c663c99249995354124e6295a6d8f568a1b9696bdd51429165d2ce96a2e23d5159d6861ccd8aa1e9f350ffe33b3b79e9b386979527c27bffcd03760324835d4e94b4dd6a03d1c2a5a98e135c09d5e31150a72e7a0d4c7b2553d98cef6e137034559d363607b2390a4eda2af641d5cbc4d09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0e2d44cca59b8f202c33bbc0eeab75364bda9a0a96a1e3b7aeb16b83a52a4a2d","proof":"0e5acd2fd1ccad401c3239c43b6ef7e84427ab4343f43b40c3f70c80320e2226fca525b6b2dc345fdfbdb66814f22daed5efe33031df58dadb448249af87515f5a6dab1b8c714c09d70c270ae7331259b1c1b609d9766955d95410c7f29d6b1172c65f7133d0f0e6e7b2e1d593738ad46edc73852554c4c13b9b9469578fe2722e91e80b18c48f5b6d5942d8758e949dbb8bdf9b56f37f69541226b7be0703031d26f1b7864e1a2eb444cd1de31bcdd44377f2b524e5720e180104821e06050f0231dddbe958d354bdd3a310ee4579d4ef64f52611bdae64419e43cc91366f0c6408aa223e96baf59b0b7aa423a53df0e715c638b1b68d255edf77083566b9767a03a7a81e58a693f534fa98d2baaae6d258c18bca055cbe48ff3d5965e24f4af27c185b3d4c582f61570ba26a3207145202304f150988510a254930bede683f001323234387d589367023240428591057982a385692c3d1635197661a8dcb2e22ae5271406891c72981c5e5593edc9de5e60e8330a7c7628f6bf580592d3e7c40b490b132d85b8c6404e188a5df97c35f86823cf70474e5576b4f4e1c62d072d4e2288f9a3b02de6f84572dacfa59c4d039cd4a48d914ec673600cba338355f4875d072989f1d34450435d2b966274d02344490454f8e92f9d523e4c41d7a368eab0b7bbfbd2a9559e65da55244bc55c612665115146525269bd703f3bada1c0ede6eac59851c4cec1e9050f4a06521e5d20c388e99fdf46eb201adcfd0c731ba1bce7e7db10c1c900ab6668b2e8e822a99919c23b90995d4f2ad6898bd8e301a7e83b49abfa179ed782bd2150932a6bd6dac423aeb67e5ad9c2f327b39d6096a60045f582f9c783b2c69b7a7c3e537370a45d0cb36daf955d4cf2fb4e33d0f9eba725911f16d53d1fe3a063a41c99584e2f418a9181ad655107271accc3f02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"86e9d23c22b22440e49cbc975ca7678bce3c6baa711e56bc30faf9b28feec462","proof":"0a5d71de218c245fcddc93ef82bbbcff25517597b6b7bbf73a569fca3f8ecf55085f1e90e1a3ce1c7c7ce4f398b765c241b8d42ddd558faa985028dfe42c1655bc0793521a630eb81c52a813d3a04b2042407b2659cf41bb8fd4c9aa8d804878701189852baabbf8fccddf9c3dd13f4f960bfed89b9d7df3cb06c535c453fa05a06e3f1defcc396b05f6d09be1e5e07547dbc0c238ed2261ac4972b52dc87605cd1dcc82c73096c85a1bfe3e295a054ccbdb154ac421731d4d8d3448e7faa20d7881262e8b0be3b847f92fe554430ec9cd0439582b458d02f20a2e757ab725028456eb6075f4c84adad8906270ce876e5ab63e50b0ff19b216b688da27e7e940fcb097e9dbcb6821afa4fb5baa883260dc78abcf075ce3049350c888fc496d7482183b168cfdab8533785cff303b6e3dedb28c2004388be73d9467698ff6d1079ee0e58273f078a3ba1190842c303ead99bdaebe373473343121423322b40a414abb01df062180269ec80bf5e11649c29718e189e2f9dac62897bd852947df463a632d3059d43213dd1fb94ab458f103fb2cb131cb85c525e3df6dc2e5497a0e2c09cb04cec104edc230f19ff0c9c47ade14ccc49409fdb7406deb7401e4da08c47defd056110a6367060b9b8b48aa07bfc97cec8f655d9f39f40ae8c65c5e1d26f6ca71805b596d88b7b06d5581c940c06833d5db76ba950c34c1646919677bd2d73577e489c121f1ed4080ff3f808e792df708d149ccd1ab2359e299198f76aa942db7a0535dab5e1e60829291b00633e57c32f202f0d834d99cf12cb9672448a34077b9f7cd16e90623c38fdff642b5256fe1848ed89f549a3a2ebbe9d562514cb21493ff07fd91aa3d775b091f70a272d54110ca75d9efa57a03e51fcf05fac247c4ba24f642b4390cabb11c828d8f3f06184584010bae8b2358760ff005"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"126f30f6005c2767e1d8ca2c362aa88d3fd721b14afcbc0b8460b00511be7d7e","proof":"6efa705df00717dc89b032e5836e8fe5c6dc51490311c95b0032cbfb5330a778161852a24924eba056a6fb6c7fe7d91658ef8d6ac55ab8a4ea8ce257e7ce4a08b836cfe1eaac04cb12b2942d24d7a49829947cdcfe92bc15711aa7c3a5227b79ee86aa851317ba97eef088e1c3673f866267e0c51adfce38076e29619da15442bd2a8be7737e7332289e85081ad79ae4abf64d1512b76e97e69d35d0446573054b7d8ba13f48727a2e698680b1d430948f515927cdb6af74cfc6ebb91b852006b9244e974a72a131c542235477276662f1d1edee4e2b94d4f0c3c92685dbcc0540986b621277145d41fe8245946e1bdccc1f8d4a2eca107c16c933ddad6f1714dab6a0260882322b0295cbdb1303935891617200ebd6d40e94e8071e6809d708167447970d7c28e3f0f3d138ca1287028e7a6229abfd04ea37040d8442d13a1b8290ab8db4deb07dc2370058e2b9a75608cd9cd62569a76ef5b7f9c6ed6656300e6be00ea6da10bb9492f14dd1151e9cf00cdd912fef2997151672dd4aac5d4fb4cba68eb49e5557761ea7f708958e6945a9f2d904f783a45bba7d18f5bbe30694223a70478da0b3bec8b907ea504aa1cd9b0109865001b95f98469024f02a1642ccb549f41a685e1533a3e44c2350f25daf6ccba33db72fa6f8877cff25754d30c96043ee659df8167b6efe2c1e7e7ffff3978d58b7e5350c918407c32f793d3a53dbfe2cae0d0eff587247b12265ff3a822c70aee5a0151bffcd9bccb6bb1380e1528a0fc36e5bc3023094f80232de5a34eef0731097dd3fdfb1ecf5073475d095826f3a6e5cb13af18af7d3e038aec7ca563747bce6f93c3ebc67ffe7a268df2e42cb63f2c2d46940dd49eb3818e07586a5a3869fc909cc96b51339570b0ba2a865435a7fc17615f58cad766246d389a2fe36d97c3e62135559777842120a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"98899a0c8515df39816d40a582853c0cdff3a7696eae0116ca448876723cdd56","proof":"ae60edfc806de7a7718c6f5af8e63c27a879ec67ffaf36d9768f19f92c2fe64716ea5790176fb8b022ba0274b7679ac8f43e496a2c9abb07a33632a47d9d025650ad1b4bb90af9d4fc5cf95694a510ef8db37f95b8572700b3f38632b8b7dc1a8cc9469cbc7832c45b3e80e1dbcb510e37c742d70c3b95556993c7917f318d44195206dfa614ba22d9ff27e5bceb190d7895b511a51bd6232589e57c4464b306dc6b1360b79f82cc2c3c628e9f8347707c7ab2118140adaacb94f0deb8af5e0eefd737059a6b0f3af7f79e4319c54d860eae5c9afbc382aa99498aea7085570ae815a4fecc65c9e1ba5949ecc7de20bfc6719d346d1c1f127a245788664447443405c39cfc818ac05495031dd91399b086148f776af197d408252193e1e17f18061da07eee3e0e30395a0e6015f86b79a0fbbd6bb082e6367623c0c39b773a1a88fe1a0c0b2383563231c9f9e74e93c2b1441e8f0bfc6b5e735eee2a5ea816353c4cbf7f5af391b0daff6be23e9450c132cc3247dd9eb1d09b4d6f081168e968b8e487c573e20c665b33728e1811648436162fa2b68ece486feb49916dc92b2154b05999a0c88c98f5fe0448b6b038973add0ee5cb63c376b370ad6928654117323e13eccac3ec84a8d7d527e2cf619ef8d1961b2e1d3eb84da3b8b36f9f760e547bf5c363a50d22b740c6d13b0608e4d8545a2c2dc2e4aebaff141715eb717d3aad918317a10f8577fbae525292317af728ce1f93d19ed4c4ebd51a13cc34444241b6eee6c840a4f8ecdf86be40b6da59c51935a900629b586c79cc99e5f50a884b46457a9714aca61d42bb7c771459152276998a670d669d7c2c25a05e421de098eb2729462964840b515bedff3f1658a24f410a75b6045c30c94769981209aaa216c58c8ed8c657e6a67cee1c71b65e6895ea370dd17f4e1dc7a8ef14fc07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"568649f841eeaa479b08446ffa64ef1300055d020724ae3e5daa80c65ec58578","proof":"a6aafe70bdda1e7aa25ad02c9f874617c6f9b614c9faf4a90987f7b237f58b7174b82127e2bf0eca163a354913d3330780999d88dd6cff994ba5f7a7c0048943aedf6ba92aa734f17ddef1ee732f358d9a0dda2fa5af775519283b1f487e0d2042b464a613ebb8bf6dba266b82d8ba5d93f9eb1323979558d68b311479dffa3cb421cead059b05a49790aafaea123bf3fe15d55b1e196db821f7a3a59f71820a9a7a071accbd65f64825c71c31ec5bf20d19c7c57dc2671613c15f4472d41e05ce759ac5c4deeca80a8655d3560bf8298d52cfcdec59abb57797d73eca69980a7ed7e95ecb71c3ec9101ecc0bc379436dd331475b6b38aa81e3b9721f09a4f49fe414d4b9ab627aed135714c03b00180aafdc7a62f58274c37779c97f549b22dec25c024a333183280cf2ff7d7da09df52d06689616675ed48aa4ef1b07a4f0d5ac6f7ee82c4eb291d766f1862efffa8188cf395833ebed43f06cc702c265902c66eb67a761b66e929acf1122f489dd96d3f126f1af4cc5b3bf0476704d3635e9c752288a8cfe47d034c651a49a76f0a1035b415bc8584f91f3efd4ed244bc17425edd232e6388c847a11f2dab0088d8d3879bd03b28de43e60f3162ced158740eb573e2aed08afb62d78bdafdb5e9d3176deab7604d8e66d3fcd96767f17413c23a140a470d56630a16bd65b4f7efa62b1b3a0b5d4984a3f6643fa018ed03688e7df7a4bb6ee15327c943bbbd669e1f45daf93e9408de583680862b77e55b0d347e8c369beeb9ee885cb53db63dd6164e7c3f93f98e8a99d9bbfa204ced4900402f872c126eabdb1ce2bbdab703ab9df824cb980a8d2042181285c6fa9be502f45ace995e12d71b506581cea8495cb3a6feed742aa1a79771c6abfde1a4140beaffa650ef1c2832d1670104f061d042db9bf71891f31850a9d4bf0449f56a0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"84ab4dd7f617bd2329b4b8a8cc979a1e6c46c43a572d1a230eca301ad02dcf21","proof":"e8e7947c222acfd00c4a4c1d77f0578c1380c41e9a0a42d7e078f8a1cdb9dc15f8e95da875ea194c691df2e3a75b98307c86367502f1dd74d133cef1fa61f96166ed60ec54abf374ad3fd704acc72d9bd1e51fef44ea2a7c96af88dc7d67e626fa997769fadd9610195d1860963290cb8fd61ae9eadea59ccfbaf18d1fcde84b6a985084c86f717ffd638d54ea122a2d536f42f11cf716380bbe7d76a5946b006e82f0d85a6fd6448f0ffee718955f7b25689878c82452305fcaa7f52ae02a07d0c742b3dd218add345b855c157e31409d0ef8d28048c67b8bf9df51027f9e01deb2cf756949f12a373e3b0d14584c038e5478ee7e7abf918c4f4f6e47a6a14f7666a9d60216d93eb506144c6b084eb44d669673590b747aa79527a13583af2ad43ad2c82ce100dc3cbe1689d80a821772b32245ac2b9901b6fb7da184ca9606086b2120a3c558d9d04af4a48c3ace762f40d1276f14739b1cd9fbcb4180a443b46ee2863f212d00e355a0caf36257decd4edb57aa2e08a69ea0b2c0cdc6b80a8a83910ed948c61090fe777d79d5e65e38a9e2d1297121ce92f17012393f8f7e48588a792189ecb1e64e419d07721e3b42b0b98b6029e6389ee6c02b459cc814f0f751ce5b568090d4194c84072170f72e3727344513cac48ad902d6a3dc6156f6c9d12428e9fb80da0c3098993a8cc78a78dd20c9e4fb012726b299be1dd538c84efe547549f2d199def347992533d86ec323a7b1a1c1164471a09f560eb40e0a42a1b8770e99d8e6f477621dad7defa632ebed65a1bc7f5baf06b31d99c74e82f91a5b4ce6f3a7622fdf305f75792db847064bd659eb1cf0b35ee8bed67c13b8211fae04fe4f887f8b9f59b746c5e85fa40d59b9d2b4ea7c700c5dd64a090687043739163549cb1a0d2df194c602be8cfbb69de9bbdc1875487155f5885b0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"20fbe5552e64b055e5003f07f971470818a5793d9872dba5f325cc890909f061","proof":"3c3da3b6dfd6ca229beda44d8820d3a7a1fdd5f6b113d7352edbbf7cf5e077701aee94df44308ce6267a55f20fac7f325a60c9c53fd407b07cbc5d049b0fb16aecfee1cf602709c7ca95387c2a80f3c1fe5c5840e02df4ba05f36b4a7d475f0982466e6cdb22b6da3d1c935a4133aca8b2c05b1f67ea9d86bd5d5261464d5f195652aaa4f7f3d25677d1a8ef3d1ee000419961ca5f349b8d6f39cda14bb3200713d2696d3fe9ca0a57f3d51173cf61c730fc6ed2d0685de4f004b7d5603900003910fed309012161014cc71d6864c7ae4d3359b08e75d79a35ec65bd20931b07e642419f317485a74a6710238b4dd8269accc498c41d260b7e5a2f220e69840072c17c240125cb376e6c95d6b8dd1695b5866ce2e767a6656bcec3edc479ff2d98aca52604f364d577f7b64bd66fa62dbda946cf14fb5ab9775d56c10fa090333c863fb7a60e299fc2de50264e74780c8897e7438e4692b39493f0f0f1c54f72f6e6c3de7e6ec384480bf2601e05422b6372e84c42c808d1928e6050c2fdec05d64cd70ca22394c977b5387102618ecade0f63e7978240308ab256a55e29580b14ba343943e8a853541ec390c7a2a31337f3252c02f04822efc92ac869de164cd2cddc28992e6ced07315418fe98a3152a7116a2c76b8b1ba818a5c87328650bdaa33c0e3b705c1850243898a1d8a1fcfddab760c833f0aafe318b20db512819eeea8d57b324e9d311542fc6632bc624abfc0eac405b1151dfb2a96ab0df993e3a1ffe89d864d6f336f6db02fcd6f6eb6debc46d6116f2bd06db8f57a479d4518cdc984a0e196bbac1d25b53895a5b10af83ca3b2ee5a3d91809a671bbb1e86912911a33ca011eee0a9aa5d7f5235c1d2822325e3e45518e8a99d25f0ad1dc0b1107ae418dabff4baa5e3dd6ea54f7b8d217e0e48ef94d2c52ff99fa936e850d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c806a559919e795325ee8cae819a0c03f289bcb3b7c2d60510b8a72c7709b379","proof":"6c7378b502a956fe1f6934e1c13a0ad8e53787e67589df00ca1cd9a45e0db26398c3344c54215640a250f172aba3fabc1963bb900ff943f155b1eb503c2562071e2180eb2594aa8c4cb01cb8752ab37fc09d75b93a94c6029c8d1b887b5e24447856c43ebf669ca29f99962221ad227125685b9afb9d0f6b9d205ea09746780ccfae5291b70fd6f63b399af21f08d629b55dd6c1fa64d04cc7af4586783bbf04f17cb285274af22d30ed8f15f2a210939f78f303bb4255bc5478fce45a318c0d9fff3ea6648e4039e6d460d4e103724c4584d0e9af6f81428f4c9e19e695fa0bd45fc02507afb2c39e81df1bb4e18181b3c9267b58cb24fb3f05c05cfed4cf0926faeef51aa3ba7420456f86b109a29c337c5e05b25de0dd28f94d55c2001e6c90cb2bdf85b0c21e4b8a11e4a197c9fa02642f8189b689b83b3695267008a83fd81f97ca8ede64f0a5e1268c39e5dca2129aed3b479b72d5c10daa9f2f083e342aa3c6c60cd081d87e81db139cb24ae398da384ea09cf950e999d8a037d7d82f2eb3e43beb273736f3a34b4df8783d9c4c25400e1d424c9a9652b107ed4d775216e532ed137ab48330685e4492c946f2810b5e06591934049d808bcdda699822a8b6b22ae3ae4b782ef8aa60189b53819ac6b07c616ca99187032c8852eaae51e04cd3b17bfb6a50494d14914e904624e69a7340f2f5d10f9aafd6a45977563b2abdb9b7df766cd219ae742e233975db1f237c93d7f1ab8b52b52fc83bd6811292b94d21af6337b669991b288026bc91796d70c4c4da7efead42c1d4e34fe42eacccf3473d3e253e849e27e7dda9ba203c2795b3524fcbed3474d521b1250d28c90a2750d8ccfb72b7025378c3a5e1afd05f9ad9600281ba913831342618090b081089bd99378e8261710e69ed6bc8feffbfe378a40957079a89004a83b37e07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d2b61ddde93464c38e1127001e2a08a1fccd557aea6ef70aaa45d1ea5336ae65","proof":"381d765f8ca5f3a7d8c3ee1b2ce83e8642824332b6a5aafebf1b648732f48948366225ec5f99fa72d03bfc7a0d28a07c3ab8086ee078cc0972e51694432c701536c027cf34e30769844208536c4ef50c692037b5210f661c36886bffccf8e427c225d2dfc77335f6bba2c33c3dc4d9d16818e066cfefdcde41e8714d19ab5d773dd75b9896953b7aafad9edbaf0756323a3c014b1e87a78ca8ffa15a5d0157008c1457a96a22a935e9469d3f934b547a9b576ffc84b7844062f71dee60020602d1f12ff671d3da13e2a36246ddb59d0c5748a09e93c27b8c8560b098a7b5610d180c1fdcabaee7046f53bd33f7c25f2fc5daf2480424748b9d1cb30806a46e03767a2e1ac3a089c7bea5e182ca1f7b99108239befb079b8b11a140b685880c686a9162606e7785b89e12407e2633bb18e706b602ac75e2f4ff9c55d1a55ace4b6e529468568026fd154cdbdca2306196837bfc7b2db797307b6cfb9060dd37745efd1ef55163c15e4dc4fcf0a75d720c0714c60450b56ffe2e1b8953b748b4323c0233e4d269c9ca0bebb0680e5ee1b5affedb414133b2ceed6a979321c3bb22babcca6846984dde46831c625b0965f0533b1460844aaf5d233b30355ade021aea7bc53885dd4f44e7f66006df059a55958ce5954c7e996766b445799632652a545f25addc10aef8c3a18fe59ade837ce1b3e5417e49abd88361a2ca620e020d44b1bd16487acd8a79ef2ea87b40eddd80ebc135f623edd3a9fb2f4daec5fb41d08a1af0f8ff2abc1a168720c3464ba80f946a692e31e4fe77fb6ba8352eec7e7c0861bb0ad2117f8ecd7a135bd797aec7ea710e2195075d66b99aa3b289500e9fe6504e7b44347714a4faf905173cca8f33b251488f1fc5a49f7dadf3affd03c61d92f7aab06bd03c8cc23aa967714919cbfc8e7563f52eb644ef0deb8caa02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"eee7b00dfd9928456632d9315d414be328d70a049193c696c852b5401a896872","proof":"d08f7d354d9527a369cac42cbf0bf3d73c661e3afa73adf0b731ef366797e954567f333eb42f39e1e497154d36602236a5a1ec72fd943df256f2cd13fc925b3c04ab14a350d51020f9be4e59619337f8cd3ced7c357f0cf35074e1156ff4510ab2b28679884f0a2f880c3e07008d746a0eee521d1cf044c46788e755fe98e14efc677dffefe3d603c92927eeef64009123200aa037f895c17a6deb57720d23019f657fb870663002efb6b3e0684faa681c10158adf1e6ee595bc3a42ceb6510540d2515ff10cecd75fedd1741f163885df58e2b31845d6cbd5bb22fbf800cf0a24cc78e57a42651f48d985b87d3d80e9f4f029f24702ff7aa61000ab97daeb4fae4e292e21bf310e6e985a359f10a9d61b1493a3de616852a4cf9e222105c85e1491fb138f52a2b677c33c6752fc31082eeceec37290199b700f281b3c74862ff27bd8e5480290db67a642ba0474e44e329956ad7fe4d574ad5a05c00261013b6031f500520590ef5f4963a5231eff95b990b416db450337eb7775ba857c3d63524de53a6654c85df25e224e06fbc4fa85752e7b76c66ec0b45e2975273b042008606adfab01a546310100673dcf7277294ceab54ba1d149732f8452fbb546525a817716b05052fa64c841875d6242192c8a31ad1ac4f571722803cdbe23d86a84ef0bb2a031891bc13754c038952ffb466b0ecc2953ee6f2cf3414e4c149b677ad753d155b65bd0e870a1699ea2217f866019c498e70ef8e40ee9b643683d7d4602aa9950d2b30819d12b532844d1650652edf059ed0398c240223f93477154d88bccd673b736b96d4b749a7f524487c7aa544704b27d020274ff07cdf2f93d8eda3ea090301c496a123bb92120995eb300d55e050a7381cf0960d72aa3160fa06473101375c4992a7c46ee3ec9444c119b04691a43d8b46ed46699eaada10f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1caf0ed07ed5e58c4d886eb70614fab9859f93e7403950d7c146a50cdbe9e40e","proof":"1a3a49223023da88e4d6ff2dcce1974836bccd3015fe32d896c3ec9bd8c46d5a5442192f1f50c3182b0dcb6758b085891ac7a03515f6d4dcd3472fe3e427466024c034249ca51e272e1c64768103bac8758cbd4290cb06a1e61601b5c6832f7a56d01d6440a636c0dc91faf8a664e5730c2eb615eded8c57e96bc32be4cd833c6b9448d43306b4eac97a3c092d91b15a2b8f7d1ce532ec45ac6274dba89e4403a6047e5f3b9e849433f373d454d14f8771c0e1e6b474e2d62e6a9660b05f670abd79aacef817872e3046b1c0e2bab92c378975f60283cdeac61b68c8b0a6a707a2a99e3edca5c5b84caf4ff334945d1c0e25fcd65837f1657cc2456c96236062fa0cf91b7f79634c7153b6b9c9b96ea33055eacb6a56bbccffb990e7a32e04552ea6633c8592054382fcc073d0e6d5fd0919918788c67dfcbd2839b97696c802907be19f80816d9e6589e99e747307244af0f0a81ea819b8e4e3916d203610311acc5946893fe1f9613d0d0ad57d80a29a06ad4be4a882c7a45e9252ccb8743752024abc6ee47b91c319beab8687ba12ec4b537fc24bfb4069c5095fd7c4a840d202d1cad0909fe1f9fdd41c02ede7246da3c7b4cdf9b60643aa3ae1a75c25594660e00b9d8d42c81352cde6895593bf26f0864ce52cc28c860046d9b04b281066343724b546ca0fdb954c7fea1b149b9b86b75f8ff332a38c579ca1f29078543cd1e0db55ad3196abbe0a7cf89bdbce8c4caaa619edb88b0e9451902596627af2753b4b4ca886cef0ebdb59365c194913748159db07376c50bc87ad06d74a0c28b7f7fff705caf055ef4c09390be4a0e652c4e5d46f52edb479f4b1a149cf3848372a8b15905a76961f5e171a56af781efd063503c5ab7c79dccfdb1e5d06064f2629d16c2968c993860a396de426caf1d0f7a4da470322e3574890e42a4004"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"be82b5023efe230a27311929d110b5ba61b096f750d4844b6f9c7570b5df2615","proof":"b6627d61f10c3d06afd8f4a26c5204765d0493d8ef357ffc957e69f7a3137c5bd001e660e79ebb4c34795ca8c9d0a9136612c33fcb3f21c320ac5899dee49071642a05c3e9ac42ec5fa3971ce27f95c896640da48a894de134e9342f407e0b18b2098258c82f6897a40d8008b69825ad77600b01bfd805d1058d4015ca4c5931ed51d7640fc1d28f06db3725f93a949ca066d9abf0f7242f0b34b28e4fbe48099035af96f72361b9dba587d107b51dfa8d9c48fb61dff992fa69bd7347f5520c91b7082c2a26f263edd2691f9be4ec4081feddc5418c897f2518fe85cbd4bd0f4861fac27e71aee00073a9587e3f4dd3295463704908390724dbd70da9a07e2ba8519c7ed894039a7934aedfc3e8e2d3bfa5c70fad5784e2304a9c59fcf0e92c089a34836f66ea0aa0f4f53b79e55aa0850b630832d1f9847ddcbe0c0b569a09900493c2d8ee3d086aaf20ec12a353505882874f897ca391ab88749e4a7a7723fedcd3aa6740c2cba99503d157a6d31576a08ee46c57884766730816554b407340e33d8bd349c4634392acf82e7d796286550d38e642a26455816f242fbabe66ce6613c8943c29ccc5fee59a57e0a71e3177be6efa36f55deee8ff3ef9d9331a0cc30c3e45d4ac03db3b18b9fcb5a20d8ffd746aeff169b69b7094dbb1e8be39b8e06757d787db9e6c0bced2283ddb975de1f5b31aa039b2e941d66e3013284504019bc8baa2345fb30f705bc0b03937b7d7e546d80bd2f567ef9f6d8865e045ac06090f1bbd4dc85a94b5e7212befc26b87e4125a890146a00e71e9f44165208251a2711c40b7509d3989facdfdd51e80bcacc86b991053b7554c207b42ff2e43c2fd23781cb0cd6379c6873a124c75fcefe3195e6e1fae09b98afce41d3407ca25e59d7108eaf2695429845f47d337b7d63b1877a05caa63c3c1dc19e49609"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9429922e66b4b821fcaa99fb4885b724812632557f10eb399efe4f19417cd93e","proof":"d801f563e329342cc7fafde8f180bb3bd33ec3fda9352387df065e28c771795132c39919cf7b19a37aa3920d041296c739a73e647e5420dcbb138097477c894e5ecde32b64eab1f0929d7f689af028e44d5bd27d0b95bc8ed057a7ffe0734375ba6f6eb8127fc60dc8cfc364034505886f7e072eec8518c72ad590f8327370460fd95a3e9e22200db512a6f4699a1e6de14ac4584023844f8031c4f62bfc67011abbe9448334afb5cea15fb9221417411d64535ede743ded8498d91e70186e00c33b4b17584deba9d7e6e6febc42e1d2a7ecedb032300c52d72456fcc689820d50ae34a7c20f61478896dface9bb6c4db399f589bf4954f496856ebbcdbc7b0140f4a6ff80ae2db4ceb2ac140e752c313ef2da8ff4585d71af4e95bc6f13ee71a433c08dc189f41e678f5223b6d91a81943826074b5297a1c12b262bdcbc587c7a378ab180b2a19092cac83050a639ecabcd2f81efd855c37c20c7d70f5678388e48bea60f8c0d83b4186c5c993784ef0b2d79452c2078c3a2fa29687793c93d3090146a1bffcf104c5a95a81c8b1f8b3bc18c66f9299711d1b670d19ebc6d2e9a3f752ee269ce14c1f5fb48a669c709dcbfb60572e7c1aa286a3c5cc8773175ea91e5fb527d53652117fe7da8273e9124b62699c6ae61cf155ccc0b42f2ed045afb476bdd775cf840a86be8b21b380420ff4cdddd13b27ddee63cc3f81cfe0ba8715a82cafd402b63867f8ef3c8476a7290cc830bf15e743567d471a5fbaa4c9c6dfa84653619538411de54d46a5206f7a96e3640d288e8a11081eb34014409563882c81d426f79a0c47ccfb7e80b2eff2ab9f63336215a036bafe9d765132513d3f6cb97e90f89ae9e8c8c66211dcc52f776ef136e4f82d19dd03b69d14408a1b84b5e636500b7c7d39222e74fb5a55b2e1ce55a524ab5034effa83fd9330f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"82d14c434b152e4f40f1fc22d416db2dc00d1faaf5e175d22f79be6cb0907207","proof":"3e33be666e831d603f2df682e7051e6690982cd0654d65be8ed833af9819885c9e66f9ae600909f53801f9b89b02b51df7c1af47fd98cd01e642d9fca1668d46e6c77e3d6df87dc63de02bc7dae434c78d61d50a5a37b99608e28b930f27563ca6a314190b8292de52e284a6e801ec81573822e1428c6b29de2433be450da3446548784ac3be6a6a741c4a45530d87c1e64f6e6b05df7e8be4682f65c58b1e004bf6f14b38f4d24510b9525e9d1d061856e56f1b3197ca4148b48a8c543c7d02f7983c52b39b264888d95c3710822c68ce2a47f6d3e9610cb29a0a27df29f10d928c217ba56b1385a8cb8fa134cb048dab7717bf2eb6d6e269508f38f954426f36fc3ad28250a48c885055ec841e39bc554b8074b7b53dbc232572920e5d4c2c52529a60ed67272940c171509e52732a78d9de2309c6b1c0aaed21a9b1e54a79d45b728c82d5bb3363a9acb306a39f3476d6c53cf8dae1379294cb542f527373d606661e67c9622586127163f74d8802b1e73e05ab0833f1fd53ba863e95a253b0662ab55c0813793ce1d123c17991698e7a13cd3e00847ed9a12ac4ddbb521052834980a46c9f3e40ba3627c2af026bd4cf132dc8a4e3a93e648ee2181571793ac3cd1f1dfe51a2f2c6ee6f580bb8ef8f3d6af8a38953194538400574e38534ea20b894bb7fbeb39390385d5d46713c0b125e8b22d723140845e94d7a5c4621f640e33026bb300a38a5a6bea09c3be7648bac67371475c14fad49ab8b12955edaaf975d33f14041576500344d4ea39413e2ae6fed5e8c3aca86bc7eb702a06b2092f8a7a88788d62cdf9686dfa26bd24009642d38b3cdc2a06ff36078016e631c4f4442464047b1a4ae024889f76c94b66f8be93550efd99cc4df64f5679e014301f59bca59313f9391222d9ee81a3ba2d91be4d9b5f0ea5b2c43c4e83ccd06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c2084fb6cc3121ddcaec1a6e5ceb393299b703437eb8c1a95d8342c8b5360e7b","proof":"0cfa22555acaf7e8039c96d0eafc77ec1f7d1f6209214d2417e2ab37175bbb19eccbe5f9d3081bdff461bec561ed92b4393461517204001e5fbb5d5510f5e96f5411c34ccc4769f5e97ab1ca9b30f3faa4ec8162df07589122a86568af72d50f56c83241527fee4fb8649a9415cc3060e616af003401d16cebc8ab8441f8091f2c134db382a8b6b440fb4f96cadca88980caac97dcbb6b983ae42ddec960a40f50df8e685c36293dcd8c598467ee25189c48419ee6a44215b80ca7d9f7b63c0abd1ff57865b6c4e2b6ebbe690eba6721f05aa75eacf8aaed56b772b6fb5566042ccbeb239a88728f6ee23c69351ad12c8177ab0709711d08e3931f710eb9905e989a8de7903ea8279874577f5d3fa115f61b4aa35867f7c7bd62c6503bff971a6aa008819e6b9d8cfd21c48b13219ca551aede878db1b371b12b24be3400da667081da0ac2e5d75bbb6409340aa46816ff6036ef3bee30cb292a5588a28829672cd95945ad3b9d804db88fff5ef1cf045ce54557de0ca9302191f26157aa3266c01a09f96d9017ea64b1514c5b6ec12f59710d49e841869f1c5cf9cb3d075235cae46dfb81053cab67a4de7f2a2492206c9ed61a3d7ef83b3ff66d01738e8e2e4c97b60e06684cf1243a6d06c0d38341278fbbd3f8cccbdcdf2443e0b76a86708ea7f66998136a5e384f52fb8a50c341eff49e19e89b246b132a7691fa28532bcac138e974744d1e94d9ce26d6be38c1b10d549a127116a670fe32b6f5b30d269248cfff35f8570215f6e0a8fcd2f986839675f12577b6de0e10aa461dcbd01ec066279ae2f35230bae963776ce411c9ff5c5bcdd2d28e7266a96304004b0044953630309db09a022ad4e35b815b1f1d3562f631a6859454fffef3f11714c60310ebc7274fc0857b9b15f5c42c1524e834b088e60613840990fd00aceb01360b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d4871ae9936712aa8ea16e5fbf2ed8f0d4d645e30a5e4c54bf46b0ffc26b8b02","proof":"f85975190c8e5e8b69670f5ac27235643b19c02d41e4f2dda1a4a1a3dbf07e336027f72b3d27467b849759a6673b11b3c5aec1269918074b3d140285b2986e266252cf9e7320486ef90f5a9b094fd6d5c4326a3d6943f103928d9457b925ea022c8d304ff1661b3c6fa1a011d58fa3175892d6576683e1a710c811a9b58cf60c379ebe049b2d439fa84ced9054f2a1e3fd29a472dce7553219891ffc1963bb0c42a2ad62534c8cd45382809bedf3ac2873a1da4fe592b3a837655170f08b590740f2bc57445e126d4fcb6c479a1800798085cbc3ec1e9aa38c65753a4a5c7e087e6e72be516272b425557b1e7b63fbb99f1e0a02e1790db2867c3275215c1c02ce7524e9938a017b182c8b2d5ca9313cea3b2a49376c89918190a2b1c592434de841b353ae580fa497b0cbdb342592ab031b9ffcb9f46f274a8f792fdd1103551a14cce201e97d881745726457143c28e113c90433e1c725d0abd4af648d984b7039b1d6a264bd3a686063b866803782a47cf7ce47d590c1dc26e2fe8995f569bad88e4d8817bb55d1f3f8438b972146460c3caceff0f0afb130f45d306a5228cc7d5396c063b31a4654e18625c83439077bfed4955645be669b31aaa2d8e633428d748463c22bc622eb0e59c139c42b1313eca9e05466051f822347ee9fe91a6ac02d0efbd0c767384f7844c85318ef9de504fa9c09499a3b3fe011d2bfb5399af2afe4ec95860b569206e39c7806a39dc6728fe12bc0dbc534fa0864b5bc74bc6ce19767057e005ce44e7d667ea9916ef4f9cf2c79b4fbb1393646a7e00f3894cfdc5c12d6a32f6ab830bc97740d3894400772f968e47f5b13f01f558a3f48a2f0240042f33bf77c1f5c48f89b3779650ee1a8c96a7537f2278bad3c74430a80ab0356f602f77f28881f834d74fcf839ee97eec9f19765c02ecb6abeeb610c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9267f1094cc31ad02f3e4c5e70f699755b99a396235fce577196eb37e9a2875a","proof":"d43f123b796b28b7ebcfe68abc63d33ef6fa40c9d3f0005290f0badf4adf392cea80448fc6a16ac8524f5ecd485fee9cd38ee072f5de5e3eec457b6dda0b1233e43cbce961945c6a1ad3519b71fde747de3ceeda3904b7a8dc0a9e4c5e354c7994e84d4de309fd21e323034c4949acfe417bb1e5454bcfb5afb1b743d2a89c4ee56ebbd99c85f7ff895736c898bcc71f2c2cfdc32732caddf6b31e54c044e508f93ff1d112d2f7787a60ab285466811424baac3831a91297a7494d53cf2aad08b392c168a3c6308fda00dcaa57064e046c7904e8f77ff91301fffc7c44f08204663f94bd27288a025428defe33d26892ecabce6c84cec9f284562165bf3a8b7b00195073f4fb7009cbfe91608f807ecd95702788303fedd97b25b7dcfcfa483a14bb273ef81cadb9dfd0bbea3d1bf684706d623bf7e302e03c423ab912ebe1649e471014854c0d99531ac0fc2cc612b07c7c58fb247f519a7f264573599deb76a46814c0735e83fcbc6c79d23015cef12abbf9770ed5d41ec67c984739874a6d7e2aedc670ed8cb49596e783084dd2c3bd486bd6f644e65bb7fc146cb26f7a44866b403c153f1620745690a6bcf5a1aa087d395b246135c3dac885f7c0486f0274681fdb61ac8facfdaf0102cf18ee66bc75aea6adbee436fadabfbce0b0f93738915597b11c99c44da860aa5512975bba1e3667f6950b2148ea30a4eb90857d6887d0252c0cdf768cbe72e79be83c243433ed4ff596abb8509de50fbc51702be47cd0046010bff5ac6904fbf0168a402d25ffe926a4d163ab649a918eb0a70b30c4178740ecf76867bb893071cbaed6ad91fbeb82f73cb8a78505559c548740c0d31cfff164adf8d2796ce2a5fe72d92836bf74046d7aef5c00141e05d44a044b1324700d58595c77c04a688ae89ef5dd39c0d59f66649a1c4c61a9721bb20b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"48b3962254927ad182a74b2e6b7fdbd0f586529800b73c8c200073d5c0891769","proof":"fe5dfd52c2d8b07fbac043442d4e4bf62aae2675ebbb8c703257de51bc483f5728155dd172e3ace154552fe6217d5f69651a4893267654410f83db9ad4ddf2256cd86fbea389efb86d626ec6ad542b784d09d90cf1b021b7800210514933a75e6678c647f130b262783360e8d6d139025d01725a7cfb16fa21532f46a9c92343392cca0c6d4d3667a34f6f001b81b3c3c167cf88d998512ccc2865fd39d30d0980f96179d80b1f9e3aa7cdcdff1feb1640014d93d9f3cbae0f1d1c45fc08e30e587a4f88ab77a1bb045c15d612a4495803608fb8eba249d318a36fe154843100fae06986d80ccd9aa0299b032d87e56719bb88ea0d7849a339c061c676d5c5402070f67104cbaca5871c4a3a480f5778a06588ddaf62adf57cdfadfcc809c04e24ba037224af3a2ab078aeee2971697fe4cd4677f868878c4c03a230b9554c59563b17c395b49fa1e471977b5d4811fa9cda8606929de93ade8be67597d26156a6da899bc2ee64a250c7f993bbf4a0cbdc8b8ccda60c5a6dd2b33ac9c283eb3b5a308ddf2ff8d33d7274fe7f551a51387d7db9892b424e33ff649c5732772d1e843246a546a20b65a7b4c796d284702a10afffb887f4d57d5e4d7e792fa3877c885364efb34b475c55f23270ca6f4cee92f0f63668ebea466ba59571fecf5c68d8ad8238c4cc7b1e62bd8713ba518d56cc293b2cb6b42e45350e9004b454d22220039abbd1966bd1b396c384fdf1904314f8814c62328a5aab2be85caa18261f5c13fdab0c6c28fd6355ea0dd0d5c4c0d26dad575f429b7fee3d03943074f43252ff20f5591c8e81601da5b6587025121ba32c6f4a6420de8467feacec53fc3b1a27c5d0132580e7555ffbe066d56ae269295cf63a9eea5e7a2a1e1f33b26f0287286ec4ada90f74b1a8c39e9f0974100b37ddacf0525e676b1f7c0525c9530b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fa69992a2bba60727d607997e433dfd8c3191902b98f952207d692bb0b4abf2d","proof":"628750f46175ce5c86910b0a03828233bd1903cf2d4e2901c3821ea6601e7a4dec8276307a99ece1a551fb1fad7ba2cddbf921975cd35632f873f82d1c4882562c56a782bb07af3422b0d74db69d5f14a920fdc3a7325c9556e37f772eb8dc0642e14cca80f6e4eb0a7041036c4cfc138440d7b4dfe4e9a843c745991824b02779e622a866af7681381311cec5bec2c7cd175c3e565e413b60c3d3cc27f6a90f3ee3b5e79fc5b7e93d8fcbd882dbf32c36d6ecf5d71decbf900c53ec9139a506e6d04beb391cb411deda1797a04ae9c98c89268f087e0a5b3d56ee3b9b310007421390fd7758e5aadcb389b405d66c4392cec7cd941dd2c4b50884409e9fad43a45a8d09281b02f976c4f1d65676adaa9c4ebfab35bdb5e265499c3d7c410f53f80b4c34da7696fcc0058caa77392e6a15381553667f5ddb1b822769147269352086cdbf6df6ce92998853b3b363ff322a0b4b63e3c451ad5fc76f7f965f9354a8adb5eb5cc7c8924673ba03a1bd90fdbd6a968a4a4e47c3975cd3ede7c13143506200f142c83140cd83182017ed539346afba752a05db90185fdf8a5f555b6994e727e6f7fea01c9eac255c64893abe376069aa629325bb946c95a4d88f3d05d4003688bfe0e4585ed924934416df57c9a73d9223aee4f0d75f65c069ddc075d422eb5667e762b3ff297652cb9b1510c0f666db6d378644d596327a0ce45f46e0ae1281e01070c849ae0e405a000d8f0dbc9a68370f919450d94c136744a408e6caa257f401cbc1d8c7afe05ebd9a9cf4d1d0c34521f434cc193dc658df0d2df22af4867280f1c9d732291edcd8eb22a07dc4fe2b478a70a64af61eacdf4843b8a5ac3332fcb5597a85b86b91cfa8f473a55d4a681ffe802d2d7395adaf2e085961fe2706bd5dd1f972ee1e63383bfa7ad90aac91fc19e734f8c89490a3980f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a0fec87089eb5225e1f98671adff5bf96502924fe837f12bf2c0c541de04ca56","proof":"38316ddd554ee50a0797d4d321868872bacb71f93e870eb8616a7533bbf8c543ac777776dfcd5ea27a44cdaad875be134cda9af7134f8127aa34af80cfc58051407c771f351143da420ae0ab29fd206443f965ffcc50ebab92756e088343764a94cd11b492b2ba16c9d5323f1e514e1dd790ecba095df527a3bc18e3a2f5a37e0e27f1e220e69be1b29e134c285e468a1c2ce02adb1ecb05ab7df61773668c01eca782f56fdb2801ba854482fae1345611dd724be2a3e26d7eb2facea922a10acbd86bf982cec2647f45aa0f3fbf05d1e0d5ea2d01268058346fbb2e2a368f0ee2028fcde3089ad2671c313d69140363b98375b4f1728b6cee68348bfe330d267c4602b582d41cb549278b5c4f842a302d86227e842f597abcb668af963974021cf6e21ff8a839d0091469f9340c46e3d467e6fa64843572c1ac1f413d141c6a3ed4986068524814d5eb3fc86fac702f61c2a06eb8ce4d2fa5f5c25103250407c46b53ed87a0bfef8be104352d4ae2481c0b7d2787ed018e4d4545be9ed35d0c5e70a3c5431ac48b4d4889ca25031f6d96376d62b341f14a882d35423f65d531aac1cf1b06615c2e5b0dfe2b5b251ef31eac0c07d13df6e15fbedbf4e9a3c737fa4249c9e549004e14eb2c67ae46f369df77f5feb799242709f576338dd0ae48d207cba33ba72398955770b2f936b977d8aab64239345dce73bd1cb9d831206f0ec5390152da8898a0be340482639fed808f62636e85b1123f6bc0ea873b781124b1fec87fa96f3d830fac7b3db93a7a57e11d668fab541537526b1377821867c6328ebcb4f22822ddf61d9d7c0dcf5d9904d2e7dc359dfdabf841250c688465df2ff41f8e298be2e8839e06238af230a5c3e24e281ebd810fdc9bdf00c4e70cee82e1986a5e9414d8f451d6751f897901d2a90fb698597d50394ec57280fb08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"88452d6ba8ec627604cf61ea72a6c39bac7448144a935825273aa1940a4fa638","proof":"3cb3725cdd1b56413f864de21df8d26a7eabbd6fb9271611222c334335ee86721a98c0bda609ee2d5d3f55da7f2dd2f9810f58d6c09f4fdbaf69e8173fb9e2321c47a4d74c0c8534c6e2e0a3dcdfe213d56abafdd18143d2e8633a655720ad4d442501322d3700c529c8927ecb7b7e5353cb9c980be4e2753c312f0ec889666d357b50fa81b60e022d96527d0e2533213a34c803e0113ab528e86ded59a71202eeee165f3d944e572f259549508598b0f4770c82c5feeb68501f6ed70b9c310d3e563dd08d16fa5cf89e48bd17917ea16dcf143d86bb9b9f5a6aee3bb1e3e5002466f59d71be536bda5985351612dcbeecaaafa884b9089c325ce93cbf488e3a3414b8d8ed28bfd35bd4c0e731488c5b3772757e7b7cc2edb2309e8526cc15171887b4a4e7f11a9545cd8f3c4dae9aeac16c6d9ec6dcc695eeca497d930de364e00f97173a51c1e93058bab4250dcc6cf99394d15e6fd23740dab66768b36761f673af7d66f508bb0f30b7bd49f38e04f3746dc6dfd2a000bd26ecb0d8745b50ca84521fe9d371eee4ab8ed7672434b8b7d82084ea86bc367aee95ea4dc88d5772876ea362df6c891381edd4cb4ea7a5d11ab86afdf482d6fa6f3a4d1313365f3234550cd5c6ae501f2b2ea8a3988fa169dc64b3772159f719de40cff4df6443a2ce91ca18285569179550d1c2f4195b97230d75355d967009a851e89f0bd54cf466b4d698c515daa7f5a787bf93a2dec3957eb52d53e318b0b848ece22a6129524d55be2c4dbb2c2cc1be0c464f601630edd327742c6e78199fa5f8e4d77c30d418dd0b882fbd16af70767c924018cf51a516f646858f45ccd4c0f8f1ff737839c2c9f4660c4f22bb15acfc7731b86cb79e290267ab997d7521e49b01617109d8926e0fd7fadd76f6fba400fc65f896d383810d7873efb0aa0f409af37a8d03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"343a053a56f30524fa3fd5691bd31122b869ab2537bad85fb10196fdaa8b5c5c","proof":"b4d15787af1ad65f2b687a5dcfe272a4b00c16182c57c34339d0d96ad914282526513235240980a67553fd3a435d8390e07b33f95e6c5be550bd8f4f2fd3ae73220517a3a4fd1c1cc6d97999367a6bce24f4e777f22ac28324825b9522c4fe32804f6590b129c88c27339f6b5e9d7e36f3a4c27854e5d00345bae99f4b976d0d367e5cfb31172f21726c12e975d1b544db858b90b0b317d4c455f590802fcf0203729e703e6531993790c836f305f095705ce2be5d09b56432bd5aac714624004002f43f2519cf4ed7ebdeaebb8470b9a1b6306ba581831c30bfa574b4ce020642c6d760783715da797ae4697fbb94aee8bf61fc831f9b6bad69727994819543ae6379c81704af7f6dbc3a6483f551b3b85b9f22b847a9c1578415f9bf364d59c82dc656157d997b895bbee237e446eca472dee73bacb045c3fc8f18dc6938701c6bf033b40cefdfeb3a049c5b72b75d07011e087c3b53633ca06299d0027e22929876d715c4340a7918a56e1bd537256bb8fd56974aba6d57200c11e0bc8a2494ccfe2c04e408013d7e27b082cc471ff57af497e0d65d50ebbb9568cf419a3ebec8746c4f69db25da848a7a738fb0d99165e22b79d14231ed2fed725f9dd40ab63362d4465f9d8df77a6af1e723fcfdfe16401295aeb8b52b46b13ea157ca12e26ddd2830d6775f798a28f581a525a291974eeb54eca1fa99f2ac062cc0de1c2441323b4d8553067342d50f9ae72fe2ef2ee5d335c0b2312924c6629c39222638a249a51b1e1c53d97cc7cfad1c5e35c13cbf7e69f01fe33406c9d3e0f1004a6490d9908eb514bb16b7b2e8ba2bcc6fad87929139807e1fdd50a221f0ff361ff3ea69147107702fa7d921ffe7e0dead4eeed1dde1f8fd617b687af08d11cd022671bea755ec6328dc688c0241440f4afd8f5f8f5ee34f03d3505029e2fabe04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1ec36cc354009c6c2d840d2379d6a7d090565dd6bb5e8527cd4ac9d940883344","proof":"80807940af4200d9f93fd04a356c54120fbafab789fec04c9d59ff230d12f617e067014b81fd99238fff9d2597a7d2e91e12ecd3f636a91bb45d9225d6b1c83010356986e929cfcba3f57ade11a87e186eefd97e85949de60af98266d59ad823b0888d27dda9da9ca0751fd1e890f3c8cb6ed23216be0506a462b28f29946d30645a9bd0e0152f58fbd9a27720795a5fd97fd22e46838989eb772c39a86d3d056cb86ae8f1934b224d9ebb380d5eeb343067e637de5269aba5ea8418242e3004ec98998dc023a34fe6e54cf2f46e79cf1e1be82ac4c863434a3d00945eec6e0100af0e0990ab827e4b09186c6c9e087306924b6024bc98c18c589d5b492d281716807e452cc24e2256929dc588062abcd27b040d7bbed1b399ef7fa5e2f117760452de770c4bf5a679c3ea9aee2def62070aea12d2b0f071de44f2762e9e745c3c101a49460ca4b8fa506cfc2515d75093007abfcfe38ea2a1d848fc97a9d12e428875acb9770cdee729d34b179baa6b8d49efb4a642ce32910ab7af8b080071d0194c57bd7b2d3192c8f5e01f8d8a47ac45cc8b3a0cc618142dc285b563106a40a1362e30b023a73a731cec430fa4a50e4113d174dfe3c6a75c01e63adde900c09a4242ed3fbdffe532422d872c8e0db23aa03ecf5767edd457016c336ee41ac2331cbba7d73074d422f62bdb594bafe3e838a6203ec4fa16467e8f80743349f4612080b21530bac54dfaadfda92e0a44c317977896c487242fbb9bc7f461488c142e79a23ce583b99d1b502171be531ac789c38a6f23082e60d1932995156532acf6835d50eb39e513f034c47c0ca4a012f599de499f2018a0876c572dc272e7c760c407298c688f79a7799cabe9f2c12c96a1498bda6a02087229e96e06076606a1f801d82213d37c5b3af33de42b07905e81a4e02d38ce34d4bf0806a106"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"54c67a907d823fa9c4073a1858410352fd748f7d5130a6cc1bb2dfee473b7a6b","proof":"96e16e7ed912ec026508ed2802b43ba005cdc2c9668ddbbfb1aecd3cd9ab9f69264fc5c7c6d2034ecf961a9b19c64f868d2fe9d7742953c91a9ed3ad5e27a201903eb6caa659b4e73030e90fd2f522ee803b09eed94b087d60659ac6438a3f4874429d3c927dd5bbd3fab0bd64e4541d61751dede33d43263a84cd97d39e81792e4058420b49d8100024a7f287fcd32a4abd082efa93d16fcecaced731b89f0de91f5888e968679779d8922c714604100bd1f2ee107de0d282132dd05702c70c5ee1b6cb677c74ad79041fa9cb0ba4c22ac6656d1550a429031b5e54c5b8bf0d7210846fe3f8f3ca706768d2dcc52a9df24648999d3972507344bad83f836e6ce820101019843c3ff86e4e789f93752bf2040aef99109b413983640ac6a03e22883d8c769bfa80a4f2c89d7584beccedf846906e4a3b8658ac9cec234ec859228adef235eeee61e0774b362a2b78321fc1b583f7bea56151ed4cc403bc6b91674223a940c204ff94df0191af9de820985214c7397a4695d85fa80ad36fbd9845a4c1b68ddb1c659dcf35539a29bfcec8e3ce092e27d239c9cf09c6d47a918d22ec577cf63ee256c8f79a1360f204cdfb1111abbcb5d141550efc6c82c213017c1a276d66bb61165c4b212fdcf4f13bea59fbb14ed35881887482c2180269d04c986a7ed157287296318558337c7157223332c1a372ea21d5efd08057cc89cd24b6b75dde8420e70ee0e5a861f50280ef429cb4e0315a8bbc115770d2901808655a6a9c5922989e67126ef29f1ee882253f3880d19f99377ab5561b559623141642a7fb4601fcce08bc91c8bafef1f7142a7599cc24de0171db017816daca1f0c50ca19d6589e3130d944c467a5eef9db1686138ff29a3eadaefdb8bf5e9a0f067fe5ecbc73fce28ffa13dea0670845ba6362299114653ee637d6ae615da85600"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3c27cf4d59eafb62c96c79786703434435dcc6ff4aa107fcdf483bfc16bcd454","proof":"10999965a85be6808d63f8c5a90f1e5c55690433c47e747c4299aa74793747543c0be5d225d904db179fa6ab8e343f474433fcd00356c56bf32357acca175e614e202cf5fa3c976cdb4bb91dcfdd27dd4d860b3fc70001da19234898161a2c1a78a21c9a1ffef0107aebc3fd55ccbe20ad641a53222c87c7e7cbfb2522bbd2578e9f30df87f10ed0781a4f960c3b4595a085581677fd9b1839cdb41837b9b60a39ff0e1dd45b94fe4c226b22dacf860a93977c92804f31eac5fb6370c194190cb873a4f7fed0fefc429ef521cfac183a5618ca1f96b4f15a6bff7051fed3650c8aff8c79f2f9f847af309e2f4c4c47a2a44488592d210f8e0c7999e9e5798b40fe75aaf5888e53b8c074347f2ab8da9f6318b21991f68b16eecd11a3988ee914e0bab3f8cf6ae7aac1b73925c019bcf30b1f2816945db924496e5d4a2736e518d27e0ccd791ebd7f9e17085768c97d20eb01afea198ab91900bf0a35eef18f14b45213ea4b3a4006b734c7122239fbb57a041df56c3e5f6db4c3aaf336e36a649e1f9ec3ae4407f8501448b01a20df1776ee1c6337ff6588e6cccaba2c5c4169eef96788b146c66ef9c8bc9ab33748aad48343dbc2c89e51710efc6a85650f19a0a3bfde4a4b4764824a66125667627413484b010a370b19e9e8d33ba40c612990e8ed2678e01641f48e701ce97a0485f0974f1b3d56a75437bd57498f34742f22647913ee7e4935fcc58e284c2d572857fb839cdf41de1263f68c3bc3d71f669a6375f433a1bc4326b1fa8e8569682049534323b5b2af224e19c54327f59e6d561bf4ef1b5d7649e298b17bf66bec2a270c33a87f94c1968320bd6af95ce14006fb4dbbb8d223d76e8c8b12ac196ff80b42dc47f72914794204ba2d91bb890f661df93b13acae05083a6714db6f6b178f1a28fd9e2a0427fcd63497fe9af803"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c8e67951ee52e36e4e1026fde72db0af36f8307002b3a1578c3a195624ff4343","proof":"a69af5bf460d44a18df0186c1bd5fa5cc60e959d59817728463aa6b9bb79f45c240bbd144f53f736f6c9a61ef3af85ec3ef67e52a49914c6874d2ed2dc1b3a77840345fa7b5980e4f72229f88ce5c4a59d782198ed6c446490570980969b485948237f89dd8f460144b94ecf5272fb3fcd69064937a6dce2ce68f1d9d82a2445c5007ea098a5572cfa0c80a8a145858bd0168ef57544a63931f26969c466090c28b3c7b805b5086cf43dbd12e275f164e3e775371109e3e2ca67f10f0c663a02e3dc4fc6a6d591fca53a08dc106c09d9c83a54c79090ca9a96434c4baa43470bb6ddcb624ac75377e475496d19132e118170419439a3a76893fcd1defe47771bac3159da41ddfb980f22fe412623b5c6556bbd8e040e3bd35cee3dc862a1335f0e8fed5c1c096f0cb29471b6e5f2ca05c48565c6b501b494c4608184a9fdcf4766ab4a61fd3a00f12b7c228d49c1313660772a1364f5c5f7260710c5fa8c2708c224ffacb49cc47b48124b5f742a50562f198bd0cb0f5f1c7e941f3edb9a335b30fe65a42f9dd6e0c70e6d439e843f253fd1b4ef7cacf5a2896e40ee0bf7247dfc22e09556b28d711c85fea5ba78871b61290b1d5215b2526976ff134a2f7515eef7288a5797d4e57cdaad40586fd3ea55aed89e903bf3a7e815375a66deed5eb4b0172d6817b55149d61f61bdcd8cbe46fcef41f30aecb3a5ca9fdad9779d74c0b1f580ec909c1c116c535ac8f05d1f3c7fba51f6f6cc7911069c0294dc71371a50abf00b903b16658910adc9d9b4cff132166951498aa764bbc287d5c250293251348a6510cfc0d69a35977f52b156accbea30b81148220f16dea986b10a2beddfd263524049fa546657d4da0a4312e10159c94969fe43f5236e76ef318305657322178c6df40a3b570cbbab75be0b87af5139220542a52c67257a14c0df09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d4e9ada8ab612b8607325a4c80151fbb14f36d91daa64220e44f0eaf682edf3b","proof":"8e3eb3c8cbe1918919b7d52be66d984cf1fe417a0ed6c6738bb15cff77386b515293c0cb9b6f9e4095e69be43dc700b3334e4b4fd7ae2fc88a3da6a8cb777053fc5d838e5329c831005060df264abb122f8efa81c0b39795a4350df0073c6e6d5637fa84d7e835768d77cb3bb096d6beea56b2367418a6dbcd29eb42cf84f3264f78c0af85ed856970bf234aef08f159f088561cd6858484ac00bd868b9bb200703a75771d58063e5dc169d707175ba98febfa3b78173ef87040766e6aed4203532fa63ba4ef9d0943141e5a38b46106f17140cc62a1c20017f27cc53f6cd302f61b697b3ffcb435140d17c65595e25190e70cabecb3c6cae2e04b690178db6120473f3757da5fa186a768027b6e875c7dcafe5cd7e819f667110d833078f27460776535ded011035a24c6ac8b7edef9f2ceaee1afa15c5fad897262aff8616afe3d64546689efdfb469f8d62c2e2c6eb2a34f884a29f5c5a8133fdc5a9cad0ede1439c0b4eea7b50773090ed7523ea1d3a8e60fb72869102c1ad3aa9735a16b1ef42871efb574d478365e50518f9570a33829da1134b0f825d60313abc785779e7eee7a42f694a61a729dfe7fad7a6166b1f499d46d6b07a9531d753265f04056e1b3de59b19e3c37315a3bb99e5f79adfd04f9199d94c4d7e4181116cb361eca8ed128dd4927d1b12349b958ab49ef27e05a5eebab33c3e4355ceb44aa783c003b0642d7f54d814348d2b858102fefd7b8a4b12aab002972090ae4155fb250ce80a0993420d85d75a01f1af9c79c895cd0fafcdee6e1044afcdae47d05bb186ceb5d44eab58df46a2b4af60183c8699818e2d959b01f21cabcb255f2f2d253c2533f013252037e14a0745c62a67bc7ddc5975998269260fd2968de0241cf0ab3ca3495376df460e1b9ab2af7433ae249954a6cf6da2ab83864ddd71a7e8f0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cccf92d42663a29d15c49b9340249f4de7c80dedcd2b7a7617707fd169798b16","proof":"30e2d477743148d51e32e6b93250f74de995dccd77e68f87782ff9bbcb553a3d64b7696fe9c2b734b5202ac05caa0db518866672d6e7f87ae8d491deba4e550f4e933bbfa919a909ecae30f251a69fa98f9422aca8d611fb4c8c82af81e6c9491ef1edddaba3c5a2a58e9fab3db50ef34f90da519c44ca64cd86e96ab84dcc4e206d9e02aec736cc5cc5ff2bb9d2ca11d14c85f4621c5b80fe7e082a52d0e8068969856d3822568f030822a4de386266d22e88bf198ce39378a7d5bc939d180e46ad5a146aaa9adbd1c58ebc0a428def73c0d9f6fdd51c92c1cd559ad4025101084098ce86521524765d3afd040f2e98c45be4d25c900d729d6f5842f3f22f050695b3d40174d3a869975a9d82ce12f1a10291c7d6f591b1c8f124778e6eed25923e48eeac9e6bd8c0e56e4f63cc2e25865dea208a94fafe5a18bdd0ce75cc090e33148db49dc098ef1be9b17a628c20e9165aa45ad614c04cb158aac1446a20300c950ea6072ff00c993307f0297cddc4e8837a6d1aff17dff671cc62890407a83ae811fe0e007078189754a4131b5472124ecaafda5b0e92a6abd0a71d686398cb165113e47bc124bd07620cee966dcc01f053a8f2e17ab5f8d8b3a3d7104590c8a9c007da61b1c79652e7d8a3adf8e9e394b13c3a89861ab12029eb7cab5ed8cfb1cda5b95a2296bd5ec5b676e5eb146a2c80201cc5b7f6168af7e71ec868ec6332eb7e38d399eb3f875f5286ad728dadb7934294f6d7e68710513b1603460297dc88c7fdacc36339f6cf25eb80f474dffe3d6b17d19cc7aede353a510b6c603b2acd3338071f419403cc8efd5a30e611c9023193aac60d1e40f511922466bbdb6b48c7353a5d6025eabb7e438b6a599636abe2cfdbf24f3bf220171eb0099d49d33631e34e7f750293dc9c8eea709b093b5f23ab262644d329623777ba02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"445de7aaf3f0d2e7ba2e18a46c7f66805ec9b2e9faadfc22ec7ce6b11977e71d","proof":"041f49552a00aa345b27ff59ee2b3f25424330a81b075cad94934008716e26431205f7491ca81fe659f35c43c238091d54707e25124361c168fee195d4d9c81ea668b47f288b59f96b72f9ae2ac245f0f04b7c3285c579a4af23daf4ddd46876700a4ff3ce37b7c13950aa27619f5f9e3f5026beea56447bc75874eed0d6950d839cf7290237c71ad9feb0c5c007d0c84a00bee5b5a2e58fe5c5c76d5025c0044f72bd7e973b21c01719a4b15415525137ec6e02a023ad3930623d939ab11f0186e838f38540099094a9bdb2a7a8e0e8ebe353dba6e007c57c003e430c9d3306743095561641a46d78c56eb1d00afd02f14fe021b31bde8a9c89f6b083959c65c437dc0aab78453252c866294f539bfd0f0d005c391e7439d04fd8831eb199327651875ee1877470b7affc1c82af67ed75d4e7e407b37eb2622898859f77f6259ef377e468bc4a1d9fbf1ffab9840cfcc52776aa391a4e24ea9365b6ea03201790484ecf68e43e9c94029b1c2b629ed7cc410e9706324e476714ba047d57fc4d70177f770d112463b664b37d476a4931d8c4fce1fbb920e6dda9da4a15a9997610aec3324d76a60f4051a8c6572e930c9096d3f5729aca1f3fd94ad3287a2b4f929e2259a4c428c5c45c7dd3a2384feb11151420a7480bfc8f74522fde486f7eccc8b87f2fb0c60a65bcee51fab98070a8107d779d3fe433993ee39f219d0b239a1611707dbe67fb50504c0ecbdda08b609f28ddca482ec58f25eeb321b0b524747f6dc54cdba41200f90d33d8a18f27efdcfb6a0a64da671a838e9fbce9c649f4db421d1889ba5bed93a444edff4c91f1e44aa9fdeda6ed13aed8fdd8ee811b3fcc7b1047f788df9700e947d410d04e3ae035bb78e26b8bd51fc315ca54a6050c7039b4f224cd45b1ba56eb425ca1065ecf5c9098395a091d61563a2c19f600"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a40f46d0d746c0bc71dada5316cb3cac1304c39c0a16d34db0a98639a269543f","proof":"d6d736453793dd58839cc269ba9613ad9953e042c1c80d45f672468584545232fa4fb513a8fc520a5d829d17f6ce0a2e48bd3633cf47c6d35729b68a9fdb6162c6fec7463bb37b15162dc911212674b6c600e9cdd32a614ad72885b65151de07e4e3b2a094e0f49516cf19d345229e55345241297893c65ed5fcff2982d872392c7ffedd7654890e1c975a0f943ca4df55bf79247a55c2bc5aa5f3078baee5071623215ddff7fdf0bac5e384a1896f1f15f9bbfaa67b6cc6cc0c37a2fd89b4042a88db82a3d10f03f23ac737652616c59e5d3418b4ca27ee81c5b067203cd8079ee59b7ff70cd8f3704ef8110633cad0a34da03b4e1ade65480086aefa26ec6f26ceb9d33a416f3a716cb16370db804133f1c56329efd60cf014226f1a77156f8a3ef2a8e46c135bd7b2a29a5283b22273d624e2e9a6c1e197532049c8a9ca626a78fb565766017003500eddaa8401a14706da1beb1842610905dc8f66eb5658e0ab69999b35e1b09b62ea730e13f390373bd16151578014aa043b9c25492465c4227dfb98f9a0ce66e613b62e83eb0ab60691056e96df1fce3331ed7e57bf1c06e1559b8fce578a46afe07ff2eea87bed48d87d2dd2ee29c496f17ce3047b7d567ce12e29f738ed14ba03b50ad6fe87582bc96abc34c11a79e9f2bc73700767a25ca778cb2f0f4ee14dbe36b5ff3470e5a3ed8d2045a4efec493739b0745d102cf139ff996001e879a7f73d1548825cc6f29beb1bdf5e4840be559c57b4d23d444bd3192046b27b31aa531f0a8fc4979e631eddcec73cbec193b1b492e5b115f021c4ea169edd360534dd58fa56d6a6bfe28d140a3d691fe1194224dd8ae86ef393f31c59c8e713783040aee69950e9b3d17bf481d802ea041898e262efa704e05ee15d71814b11ef79a8f7b6f08ea742a6dfe1d82240f8dca8213a8a242600"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d4e3383d6ec4570841b8dc28536f87dec7cea10836a0ae5e056712ae3a117348","proof":"9c7c6ed110a70c66018c1fa8732198d8e28bbeb35d03a487c01a94c748e1bd388e6f10d057ccd78044536cbacd4e2fa7ffd737fc79027a842dea0926734c34701a6e84e539c9e1a591909f7d1f30df653febbf183db0d9e0e240ac400b304104368817a0260144d82609610b859642a8a51c53c2f8a9687147c0ba5ad9e90c2465115b9150e8b64fd1f2e7b08e202229e1b1c72b3c1da2ab144f80fd49323f08ecbe8c685d863c527c8e19a29ff3a051180d8cc9335bebdd240b41840cb2cf026b628ffd0c8326489451c90e5a8254bc80ec3884f9d355b6bedb650bec0eef0618c856401a43a2cb1dfe9728ff19eb3848072a64c8a34286ca9ebaf85c87563f4664cc9d58c3e54b1538e2009d191b686ad3eb1ab08622dbdbdd2e193e414a7d88d2a5a64ed786ae877e377a435d1731909d9cd9d3954e09062147cd29ae2264e41a90d43369732c73031bfabe41237a4c72af89ae15f7d55e973a0d4c1d00664677a23ba85962a80044263c28b2f500d0e03be944ba4ed883bfa5f49a94563fcc63fda7dc16bdb6feb8e7ba6b12cfb6c0b86b171cc4a44a8be8bb636a7ec9272eeb5d53b95dabf5d5869c033b3fecda7873b460dd55543dd782d037982e792b0675d488c54fb0e50f684efca4624e989a259455e7d416a441f8095f0eed7c51aea7c9c5ce5c2902ba90962fb0131911bd43b09106e9d60e6cd1ca8a7045806e7aeafff8d1a96b692003e99b1a3a8dfac410ef2e02765a319cd85fa3649ec92066f52b483bf0004fdcb6aba6397d776e66df300f6ce28f568c1a3ecb97f4ec5238b53fa59b9d2abee24ad8e602952f50115f61218a1c843b9e9cda7a88fe64045d52f5fbaa15cc246d3e38f897c328598fa8996bc54e6f9a7399dbf8ade6520c50f141ea30001c71a1bdb93cec5e99ecae8b755125107493628833e567dda104"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"349a5ddeb2ed4a8e58f3cfb678133ec0912cce4c9599a7a691ebed7ed81e607c","proof":"b8f5ce95351888762e51ed9c364a7a98ff323b6851f71bf0667d6e13667a045f5249fbe66b2129d03aa713290cf614fa2732d99d88187c5b141a62066c6cb723dc88840be3e29574c8aee5dba61b50c0d351e756a60cff90ff27edc6b2082d2f9ccf2a1ce96180e87266281117afa25c20dda7983f540b6854c7c1424157ff360c41885be858a72e02c04a3dabe667a258da9a1cac7b21ceeb98ba00de0d3c05be48744e0df3a0c11b705b11727104d50edd00178ef96f8845dea8274b0b680a77bba25b33cddc6215e7cf636bd4c71f1e283062fef0ca82b27a77d923c3e10bac83be5a63aec82870e7846258cd6ecc2924ba13630582f60a46761faa4fce294ab927f713400c9bff3e86191b842e8ddafe9383d50544d0b4db33da8751b5385ed98afa2a363ace1482cf63c962ba0715da3d1e15c5ed0613c94acfdca77f65f4acec148b1283796b993a6d6ad9e5d24ad0fde0f172cabb84098eeeeb195b60a246ff3df75ae1507d5808aa89d617a8e525bbf163c0822a38ffb5f540f02755e88786d9edd4c85e5a3f8564b7da971c58e6f21ed97877e486ae77cbd8aafb680c2d1965e178efdb002b19c138b0d7215bbc52eebf51b39720ffbbc3fdd7231788852cc9a35120f5e3c58482c18d7bb95f4dc781ccb594fff5e79170ebf5272816750aaddcbfe08fccaae3a0a17767206393bc0a15456090e8cbbd09ed7ed95d964091d4c34c588e8de5b8af2ff8b71f1f4a79730a4f048e5c767a4baffc5065fa78189499b7f0c99bd0f3c12c53931517a166c77ea8dcccf383da86b3acd229381ce4d3285afaa6fca9da66029729dc651d386fbf326cfad735ba9fa8be8c557c8471ea2af1923c5a97189a14bfedd88af571f39d55f9a01d58abd681cb980929ae6f361fdf70455e3d1caeb2adecf73674bfac44ea6322fa39bd0e99089c0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"60526ee45d0c27b48fc551eb67a9de551dacac6c27d2aca8d2ed4c4a3892d123","proof":"32b4c1eb83a27b1127a30ce0d34b68c32c30be82e665e0dbf78e9bcd9e992c6d826ce8d9a937af536009ccae9c30c912d6bdf69e02e28e9c26fc680357fcb420e064235a1b3dc020622be4e0039cf5206c874ae7580425f68e68ab77f6fa4d1c001d6f272dc7279b12afb67f577586e13a4f982bcbaa2943ed95bf4150993d0b8053ca6b62525f7f451f98bd8b9a3de2c473d4781a80c28c77f5d1c93a97260c604e47091a2591c9b9a717b8e9fa643321dfe17d1fa70cb4d095402fb1d0e208e87eda2c970818c8b08af33718e3b7f7d18cd535e020d33458dc98dffa8a9c006c87358cfddfd3844b8e82348a6b02c7e8253f4889b2dc489176a4a4bf9d766da641f70675e4b67a32a01f7c66904012626a84f771a85557aa0c01a36aed8126cca2bdc3b861b8fd4d0793fd3d85ca78128336f08d77601c0573c79182365f6c14c94901b363e5f06d5c54a09130ca6a9018a5fcd94f8900beb24f9639f76c68e89612d9d19d853df9b15a83a5b09ff3a4f55101bd0a9b980cb2243a408dbf309c1e55f1876bc09b412475268162bc3310411f060bfbaa788b4c9afefb25a90ee4c859aff769b81241d443f9f25381e7533c5c5028e83be0c03cfea73b9c62279cb517be2ee7e36fa354522c29701819d75a33c5df6a0de54a369665a032cd4b7a03fb447b0e331886c08b3fbdf1c8d4ba8c97cb9d08fd1f9d6ec5266b6b9b7d525b14ff6236fb817c98e11da42d37c0541991362b795c58194534d989406f68a27c906c61d10b621c6857c506274c4608668db16aa59b9961a57d36f76b0e37a4383f38a23ac18ca48158261cc0261ea45a0cdec5f4cd204f081ee1d56c9770c7e8111009bf2a4da061c06d11df60fdebbe852bf32c7354d0a4fb10e13e64021abd540c21353c6469c4ac7a87145cfdce6b6530ccc9e1bc1d9ea592d8b6570a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bc759a230b735060dd875add7b6ae5752935ef86d89ae92372f4c1fe90e5381f","proof":"2ee98714b8c0a732b39ccce4d550cf5d0ca48ad87aa9d6012b63df1715f9c37ee47f9544e9729e6bcf6c82e8b37011112c0a3a1bfaf0d19027a9d17aa1c5d1634a5d1fb639a226f5945b1ec58aea6470c70b225564dd138a1c2cc5e4aca98a4024e2458a8f437f397ee4d99cabf42339b9a93cd8dd27989a4c0fdd0c722b7a41ce32bf71d0337ab2842fcc191a1be8fc5595bf40cacd6f9c3446892ff7e5440c52aa20141146cbf75fb4db84e54f57d3130ef61f12c64c38032b82fb78171d009383de29bf697dba14ae251b8529bd7e28abe90317b4affe661b1d8a72ed5806126193f9259b8554dd4c48e2e83f9cfc838f1731927b4c08e7205d994c8c33088e38d7f8928c39f5affa8670a3308de02fef3932b0661e19103e4b50ccd2e955d4c7f488c66c1c9a263cc25d9d3a02e2aeb2ad36e12dbe9e5d49a53ad38ff2386436097fa98424677bcbb53ac2cd4a36beee81293b3565572bf35bca24613a23d8bbba87b4931c0f9dabd13a2b6e5dd7bd22dac9eea7c6ae2e5176a47bd2a662a0f3073572342121c1afe2ccd43d067dd9b72bca1da2531bad942f1ff6bcb0581cd6960d8a99b74c3315808faf4f982b84117f4f1405243641f1527992dff07c0e9300b491558eb6ff311361e91d8d2d0b61d7ce1ef018c0f75c419f4a574e384c4f247d5788c00f565c40da5c6f83eacbd7acef5be320c79441ffdd8f787d55f66badcf2938f8bfcc3d28d5a1809a059588feb46a603816e9a34ed8082e6b71e2cd38e874fb74a96cf46b6cc0632bd9a9a69860f9e58ac1bff0bfd8ac0eed4e30563637d200f42d470331ece3ba548430238f9ec8f4a4fe7b5b4b7799b7c1565004aef149e50547beb067e51d27316900253212b78271141a3a933b36f5d20ab8a4199cc08c65353aa122c7fc47d7eef414d4fed3b04f1f75731646a8a3730d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8c5b7780f9aa670a0ba0ec74d9a946063d87476b32583cfa32b6fa94041c8519","proof":"54fee2d97cbc9551a5916381c48020089b2b10f19a61d7bb06bc1e84ac00dd0f225efd7a70fe75d074100a7755ab8099dfc7a23edb297dbc94052d46af40f1289a89ebb62479cc9b59ae8b4ce6e00064ca102567c1b0de4f722dea8860428015e2f03a56dea4e9f613e796a35af675f1be9f6a58ac6527aa79b520b694bfd457eed9e73e8f1e7830528d2dd3b8793666b4af1d7807e311c53296f5601b97c300f746070aefde6e0c50173cc0de2768047769d9fdbb2b367891c945763753f808361759345de4ef0f9d8bb7432a68962e485e027239476d7f84a1d64f0ffd730f7019def2e4a07571a95700da3a6e4a68b2e4c2a67c48744b21e43ada89e1ef257c76762ca5348150a160a55c88b50dcd98e9406c5d978ed0d832658bb4e722515ccea40ace5045ab3fa98f79a1fbbb0947f95d0373f2dc3440d267cfc0a62b177a8f0d4a8088a40c02a57fcdb8a429340ce7454994e69b17ce23cc770e3a241b8071a42f003c82c8f1218cd2a18e596def8acbdcf92d11b8384668bc3951b85d946539692101467ac1ff0387bb509093d10259b44cfab4850e9538f352d1f05bde91e0bb9814f55a2a9c3e6ba0aa22eb79d1d7aa162ab468dcfca4c57f0565126e26996b29361f6c674a39c4dbb145514ac5780b33a71ac2c99f4cb60658656fd2c52195ff5fb08d302105fa74c90a0d2dba1de1f0a3add72260021f4ec97e3abafe3de8082d5cabffb15c5f0b9f5d9c22820f84de11ebe1fe505b36a50bde22cac9e64ddf5d7750fbb98ee85bd4988cee073e82ce3efcc343896fbe7ca0735fd8d5ff5dcd2fa2e94db964b6e01dc4f52591c97c6af1c2a51dc2fd47b9583d47c282a3e024e5df8e46306de287d3bcab49a0799de950721502b28c88d210380de94a1803a648ab147862786c812c330930becc0536d7b3f39211735ea161ed03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2a14db15b57a55dc1bf3bd67c454f60505b7ff6f2b6107ce796e156840ef2771","proof":"746777da7666ab541f114ad6308554fb2dbfc2270ffb3a8830c1516436fdfd6e90218a87ba6587d6418bf1b3c6e64bd1ba5ab6d5a1675b388ae83ca23e9c785364de9258e58e361ecc3d853b5efd317f5b68a41665a0a689c3e0f83d6bfbd23d78b3dbe62143bc0ef51c190748f5c27d615db342aaeb391f759a07ca6bde4e5e8376d995bef22c5ffa5bf4198540d78143d2d5195bf7459770ab391d5430e5067f88e52189fd7770f7a32e84f16749814875567ce78a48ba6650a4c23746b80a546f1df2d7a405e12052218e3fd34ab735cb4d909b7bc923c42e74703ae059042848fda3b9c9fea53bbd4c51022263c2b474d5be96f303bc7eddf2dae8ee542250d45d16df40695db90fddf1a980fc11fa570d2af7710f425f28fd510b56ae0ea0cf463b229923805ab2fb392fe748b22a64aaeb96f2f2f062ae24746636f75c1ee909765b92b249e702b44e0061d7c4d24800bea3fb2149b006279c3efccf76fadfccd62558b31477f910be2788f2a31e0a73540f5fbd0294db50b75ea4dc45facc82a1c2873e380cbe38ba4d65991612dbf2f7bd4ede99bb6c9095584275217af28d03b9a98a82adc9bb8be804d6b78bd31a708398d684c5123eae98f131668e21e175a7c27f7568d02acbe52a7d107a4d131c92223204036e75f4f4ee6301f22ade2add0ddd3ec65f92e3ab08d8785dc9d6b07a22cc4ca805754ac81ead64d8facdb2030a1f7742bf4e09fbdd0ee50bad702dbb4155768c60955afb4eba5dc85637ae6aad4b8326aa5910f33e2e79983d4bf273563ce217bd750a5214074fde587fea96af3e4e88f4821468e51533ca8739bda3a5208bed0a799c3730143f0f0bd04c0a65a8119e5f7445a69240c837df6f781e5a7e1b75b7326a1adb480e8760a09f1cf2d15e3fc8b03606b4e03119468c23392f7bfcb04e63543602b102"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f2687b29e4bc6e08c344488e6b13eb7259c69d14b4cf6d586d48ccba45de8d7f","proof":"7816a1b68474ef613993f508600e4735c0dc892c77924a5d4c5ae4ab73c25f5cf4e08e28c0828795b3bf6fd7bcf782b6231932159c6dd9a8219cae333a37c108c8ab7031a270239f75d740f19c6e22f1ad6ad3889dea3fd166d8e6be4685a474aaa1db343ea1d4ad3aa3e1d06b85423b12076e21e47c93bc0d321c0b3a19c11a23fd8fc15ae87ea3f82b6d96f32cb5cc268d4a7f387b6801424caa92cc5df00157aa0c305cb667ecbcc4838a5d58dcfb0b996af30d5871d701c094be34b49a04fd17948d72566ed7c78385aed49a4d6d44fbf7bf0a36a72237c89653d4700c070a3a24d2eb3fdbb34e1338d7e21c54b0a6c9aeeeac15d3b140afe8275548027848dc2e68eaaf277b9dc4f48f3376f035aa39860d62b6b46fc940aa79766a0966e8be9eec0a5fb0ab3f57e234994531b14bd120cb2600fb0bd55d9d00706f2535c400dd6df6a3bb37d2fe9fe29ddf5291d3b4e72080cc722fa6d090481d01bc283ae33891d43f64138379628a4587ba577d19d8ba95bef1b198c51507a670260dd2aac0edb16dbe6bb31b5cccef6aa208f645a144aa585bec1756c1f6c349c55e20f88b704f673812b69c8f468ab21619083158f089c68a469e4363b05095185d864bfa91143da28bfa276f356a3fac233fb14dc859365403e5f4dab9a1f0a347668806149758ad91c0567af4c4d35aac78401a27b62d9b1f14b8a1b736551372285fe1b1c82d421822e140a57a08781f4dae3a2e211038c00775eb001812cb42ce9f9ee3481a6d404e83de19f0b76109811efd0989b0ee5d69cef1ff52bce50ade9daa0666100e4c6a368dfb39a5134a6c4c435f0ed7363ea47d9fa9dbef543476469e3650bde4ef300ffe0f9757cf50820d53705292d04b981dd806c8055303cd2b47e5d5926e5db04b2ed37a0036e280160a1b74cc09534c88f88bfbabf90f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"76c5f474c6174363ad0224229d9d2aa4c19b3ec7e5d924964ca0d47399afd366","proof":"280e217c2af8befb7d6a117faec705489c4bda6f13f5084202fc7f59ab0b6c52a4c9b5e01fb47281271c40108237a28ece9ba2dacbd972dea04a5cc1b5744a06927b1195a86421d89c741ef02510825c37fe735c1e33beeab46a21d06d9ed40402935828511ebd721449658de4c5b3412528688f2f83ab8ad55840601285ba75d67d24cfc398d72ff902089d9cb476874bb7148c7cc9d8edfcfdab1f4be80307349c1a1c64a57dfffc3ccbffe59fff0d2d8b2407c116c977cd3fead0ed89060352e2c4b851bfeb76dc7ae102e3bd416648cc3f5497b50a2a22b3434b6db6eb047eda166a7c7e52ade0c434e1ae0967f47672e7d8f62c593dce6f8b2a82a2bf03a05d73500ad5ff46cf43e719b3f3c01862d1add4acb31f75bc6feab686078d739e2f119f12dc88169d53535916d5f53f3efa314d456e1d40676e699206b2c67fe6bb20cb61fcb81bc1842996da521098b007bc86cb9c2e410c5887cc1fb5b179f808479907caaab52d347b9bd2e18c431981a7945468ef43195fc257117f630ace234d813954102164d8b52d868438eb8d66ecb0fc04043b31a4a15c62689204de477e996814c8ab086327730a015fd49ed75ca0197949236827c0a15d28d66308b53220058242c29cb3e8e6d7e4f785f4a542855695b403825e7e0979081037386c10d32dadfd7951eb4319837b00f29ca9e1174216338c5abf92235d3aa6760eec59e205c893062b199ebb9a43367cc64a1369b794c24cebd5f3726002c22646c364ba0398e64b6d7ee3daf345e51db32d9527cf924b927a3dedc4f8226c2a74213e4ce96cfdd3af52a0eab23c47e2dad8f6f37d9cba4874e46afddd1b9f196434aa5997b0f2a35cfc9010a8dd462ff10c71f9a8ad1f0cdea417645907b609b46c9831866039d75d7fdfe0b41b9c9876b9e989871476fdc10da89bb9610901"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f6564d16d0fbdde3ce22cf50fda13cc7621d96235df63863a7104f2252067320","proof":"72245a85cb0d5e5cc5392b0e3a38ad061c5eb6d475de1c8b216fd9b5134a441d84bb73424a45c24ac0821d5d909c925a2eb22b2ae05c0927b2b1088f1d18ce3afed9b5bd30523226f66f24d0b9a455e6d338cb57f54fa735f596fce7b35fa852b6192ab3c16d780eba1e0e6f3da4824652e320e43076b1222f22553b522fa92ffc21616a678bbfc9ae55ebbe2f1f72e68802bb04103a27daee9952c50d88c60558f6b3ea972651063a650f54343fb6e1e6e4c047e34cddb00bdde11b9806c70334768c1c80786a23b449777545284bd3567ba3a39bb1f18ae515161038fb40080a8b48757b676c22756ad5b6752bbf93137481dff146e22482aee50ecb63be1a60031e829124b3e77da52acaa9e93ac5e6382fb5eabe77c5e8be703aa32ea206c2eaddc92abee8fd11d9139fbffd51a98106ce21737e2d08881a7277c812226ea89514b807681e179d710d5bdcf7a4970086aa528693037788d66304cc8d1774b264b0fdcddc82cf8117121e3ef9d9fc2c5c8b7cc22972156282eab5dad0973e0edd9305fabe3275a576a82dc1ba9fc9f9dfe9ac5e234725a5171e8c0816de70b82d478808c940cf1a8178251d8e25390a02e2be9e96b50d6f51736c2afa8b4394cab72d6d9810b328b156581e4d09f6d0c02072e1f3ca3452ffc8bb2b216e4ab4276b6cfa8f6f6c3a6fee3cbc71f2fb75a30f704d7b767236716e6d3cc8027feac39c05f75782444fce4acc0dafbb662b8f1658bf54dadc813d5a3b9e84ae181ee15189aaffc146ca2bb3e23596f2ffdcf749b48b5bfb8211f60d0ed0002b019688a115a0e1fd3cc7e5368161b403fa5ec83383b64c5366daabc8c490fb1d59fc3a69702b3c721b06337f8d7a4f3d2f009dcd8d2eefaef9b4ea428efed3a209d9e75ec94bfa8034bf02a1b6f6e2f090e5edbb4b9bce9c8c06ac635f83379a09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"48c86082cc8a255d0f423b666dff488c5736c98fe1cef25ecd71eb6548a22355","proof":"a0fbc19e3806a463b29963d6e9ec7a47709fb8b993defa206e588fe2db646b292e01afbe5da2f8a408936ced218a5f9a58ef21d0019ed69848fc5efc6520c7150edd85be24f4a2baf09201669d51c70d10c4e27a5038b49a6a67a431a64ca81f98f95834eaadfa3c2f6f286da32b046937e3628307b568f0f3c30c38fa5e3233cc845f1b221a4552b1ec4044419d34485d185a8d005d59eaa3d44844f2e44b01b75b292fad2e2a7a8302214292f5f314cc238a482f72aec824e92cad728760077ca35827729d24e59641c616d372fb3c35b889b102685131109a261b25d982082e8344650b65383e6133b5f657cb326f22f3176d356a74aab14d7b98857c19087430a85cfda75798f65825c0f55f70216f0889404cf83cafbf4eb3df49adab06ae3bf76ea18600c5defa8c347e9b2724c2cb741814a6e31099a714083354ce2e8257c1ad57d4cebc7f7d918635498534bf5dfa2bde3116a6518b78ab64a2823e4e3177bd10e1b11c5cdefc1d9caad10c108782e07b0a64772ab8f4449a1a413b9e84d0609c7eb88be7834f2908e7ecf66465415a968d276db1e63431a7314d719089bde99ed670de4691a7e5b250e2ce0fbd3f04e4fac5da76dfe93edc698276063f41a512aafb07fa36423f0cab2cfab845115edb8276a2fa2d7ef1c37dc704c498b833897f309500195def0142039f064b63d5110664a1988f787fd53d623df85c2717668376f1ae26f88fd4e9303b4be166840386c60ab9297b5a08d4be75c61e8c06c2d23b95958c493578dedcff769e7baaad88ce2461a4bd75b918255bd489790b6439f2b5b8ebdfd0b741cd11795b3cdc0a4e9383df18eda99172196cc5e7d6d45ec8e4dd8eb2d67e145286985bf060a62b40a6c542ad194989b1b8012b848591de3f3ebcc841b0c86f2d1e46d3b8acc375e9aa4c25f21b0da2e1f200"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a0fd51c8fb21c6d18ff881a6abfb76bd38d92a402824792b4b26518ba0f1e56e","proof":"eacc89926e30079799a33c91e1cda622c79e41f6c48aa7a815ffbe285e5ce525042f9d3c22a75d096c10ae09a624f36b84e4aff5ddb033db974938923b531c187a65f89c8bab305ab185072db0cb7ba9bc0b7d693f6aa35448ac3dd42220f536a8f68b9d066f9be7f651f427949434a5e0b7f2a3a47facd2bc4c8521868571259cb2a105f6deed3652e4534576e357c0f606a741274b72bc9015cd8ede9cce06ed380a6a70d1266ea6f4f38e7f56d98dc4b3792bc7a7715e599e71168dc12602bb361fcf989c24722a17050c9d516ef6376ad6a02b62c9a37f51cd9835410e06a623e4e6472c019ed00c56fc296ba3eaef0921be8bea54768e885a5d493e7e52121cab02a27811cbdd973169242f2f02d9651bdfe7e9a80bb0933715db01b908d830bac4e7d1c16ff501a788efdbcc5f5daab75500606087b6b37fb4f51f0b0c2cfffe6af1cf0507a172ad580b4500d56b3648a2f5265bcbf705f1b3b443d8730cfc0456d2ab42cb9773107afb45408302971bfe7623b19cca0e1adcda130c749ac3d8712f2fb0593cf6866a144babb1b039c036d0d17bfc91d0d55027622d4cbc6fa1cedbb4b73bde46cbae70af9f59eeadced6835bdba7b6bb9701e147990e56eb2faf8812b68d6ef52c78d0dde2063cad0d13f2f59a88fdcf4a782b7db47fcc898f6ea4367f03570ff37cbd05b652290cb6c9239d21f7f9a61a77c1c42d1eb2887ef53940b2c922b4b7c0c2e37d022b336494c65fe9c9835bea1f754fc543fc29f41df2031a4408383965fabe2e4dd785df7eaa12e74e73008dd0a5fbfb4c6677fe8b0fb3a0bba5c1d36d3d26b10eebf906de37b52a8531c91d93b27f1727dfd77faa9bfcff2f967f839b2632d7cfe935489fc1f6a2e02fba5464811db601e8c74d177423c1fc030a403143fa8d3fbbdd8f945d32431fac5b1ec2ae1f1304"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a01eb22433e07032dd91841b51cc4d892ff0d407a89b5a14bea7ed699315584b","proof":"98d376dad7350ee8bfb9ed1e78f2a44436279e1f3c31bacbb70cf3cbc6b551677ab4c866c24bd78bd69092aab42305ad586e13a45024e6696cc8337e2c29a82b700616d31b5974fda08bb05e8b8d8b7694f269d00029de5fbb4afbf9db871547e4244839ed507b255e61e56f7793978876078b4a88c5698c84cf9341ad014150342bd4e839cafdc3ed06feb7366917247cd1d0f691d07e2a9edcc16f376e8f07744ccfb8126603d9756921e1e306634ce34526b9ddabe59f97e52e022accf107d8803641be1bd4612d76fa8b61a47f49d711231f7fa8d469545812c71650f102ce650fe226bb8e690b01ccde2f760663559cfea6fa5bf5cc95d292e6aa7ada3298eeea3abecbd8fdf0c5bfac89d654059437a485143db493cec62623a9287f5fee391c575253fcafd6b6cd6ffa29a51c86ef1127a1ca9e3e31ec1d0f2551773700c12c10ae12f002ee1ba612c757887064967a28532073c8736fcbbd70a72a61767ea9c61ba45b3eac716b2d005e5df03f6edd83d82e1d9e654dd958354876088a60a6888c37323ea2ebe51d3b8aef26fe1706fdf847a4cb4f5b1477239584646c534bafa590dd92cd1d4c534aca6030541d0527f09269039d4f132db44d11779c210e0a8a5d3aeb902c17c37ab7ead2345cfd941da79d5b494ca5d21518687e4c2015fa9813aa90489917d2258e27f67e9291042de58df595391d7d3718543bbe07f54c846eb889ed724344eb72908d0ef3c33933fc4dc026f99b2bb947c875d8d073db2ed37da45f864f092056aab7debf764cd96793a1307ea403f57611040e7dc486766172a5551518a5a0804130fabb69eb468d1dc82513fa6e065b146f74796da4be422c71060d8383004af902efd01cebe337463c26667a244e145a0caf074bcf06ae66cb334dcd67a1b367408781f7e72dcb58e0614a8f1a55437a0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f45cf4d15dd3bc4e20215e18d00e48e4122b73fad3d34b171b2763f05c974312","proof":"e80e0d5ebab7122d229f7901fb21232c25b6eaffd9e75a2683a523fd64e3c20b8e5bc3f0961ddd1f6ca95b5fc854b6808fe5d1859edab44e226ffd498ee414242e968805a188303cbd41d3fb41d79223d8cb9cd561e4ad78b5c5a042cbf0e14ba6fd49e2ba801192d8e4d02fd5c5ae466b01c9e91bee945b53b669ee5996e92ef3f8b1d22f137e3b821008599e6b6e2a2ad530cf24f306783853eb5ed7d942005e5d45c53903f2a3cec573bc3311d5bbf346c429c8dd15c6ead3c0f8f4b7ef08ec8e67d4a1616ca4ea6f15865d655139103566a0278ab9b678b26f25eae0e302a253ba233b41c30e9a7c6006d6a6fb49d975223cc398b2478b36e4984b9a7666c8cdd32437888705818eb5e60041cc31795f0573f508ac03ea11fdc047bcf85f427d97d077878326400c29e66021d29a3a3e60e7444f00bab6c49dee42f1a9242e60138d27bcda38eac2b324ec14840a5b1cad300583c1e67e50c6355501e20ba28544ae8ab9ad9a60e84aeb23132d977d73a7fcaa662529409c78c9c075c9389ef9a2d8e1df21bc90b32e37ee7209a22c5c8301a330ae79e4b890ac0922b651bcfc30d2b9bf1e496c73a2cb8c9cd860e02057cc34a958641aa3359c5f32605d8ec22b171551ffa0f49597901ee4d34c09a209434d0acbebd80a0930b46444222038d6799df6b89fca36b491f03e6a8425b9e121b3c115486f4df0adebc9b912e80b70f4b228588d6c59ed2e0555f4c8ff8ad34e2c08592791ccff47e9b0154a2afca2b323eafb4129ee537c4f5b20d66c3cbed2a5513be4ccaaac84308450751454ddbed72b5416165eb5ba49c4e65f195131084538aad24f5ca0b6568810793c47df1dfe291ef3dc15f1802eaaddc7ee98cef5d543be92415523ab49f19000b3831a01529c19780e47b3b74be835c7998b57ecd9d9660940322a0b344db50f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"22e9397e2c585caa94a9661734bbb24dec346811bd453f1cc98e0430212bf07c","proof":"8425db8f29a8160584b90fa0c3b54ad1e9aab604c2f3e198ebde0d91e464a97f0e2ce979c79b1e541da17014ee3789dfebcc7875f3dff7db87eeeceb36004f65285b6d22337d49dbfe0b974c04e937439aac8d941aa2d371b911bf678bf2405f78b7db04fee6a50aca6b56b24be744ba7ff3c19458a9bfac3cbf6032c6f708219590733655ada0d89d586c0c43b942235577868344a4fc0023fda172dc7e6801c67d1265cd0cbd56161b39288eebc031cf80cf0b462543bf2f8e0bb24bd2e70018ae6d3a9a8f3f7c8838b6020161c6b6e474850a97bb086b545bc30f5488de0060cc399dd4b949ff5eb373d7cb8c1cdaab0596d537f17f02680628cc6a0a2a1d2a62cddf7c6fdc3f41318a1604963415cce17d9ce22ab854e57c11d13a563729ceed56275485b448a601a30d764ab53b0aa42ffd53bddca2533a0d1ba009bf232662b584af4c6cdc2b56d5a90f866e7d39e20679adaae42dce1bcca2e467cf4ac6efbe166def37ea231b537ad41aaaf3ef4a0ab5e0e41b3461579760d74993094e5a2498568f46fad9e848d684d478eefbc3aec87c607a2f13fd263b3c97685238fd7bfb33ed9f1c2f62c12342d36cfb5b321667ea1a1010e3793ede071ce879520fe4753ae20d3b0c7a7c0de278daf33e258e5ac4f89f8417b79ce890b54d7a0ece6c280c5875652ddbe347739dcbc79fc6c96ba9ed545b08357c8ac5b2a03d0487265edc2fd367a667f4680ecc17c195eeb1ddcf4f8459f1ad739bfa781645e20b168a76f583e6022afa4a1a2411f020fbdc030f72ef8f93afbe803e19621454d4e3d1a55b57826ef7e1813e126e04ba7fcb9e14b104ac5805d0d62c53925373e52f1570eb4f882a116f94ccbae3733581ef6613e4656be97f000701c6de018dcf4672fb5f0ef70a1233a1228ab25330c9fbd25eedfe06fb1b238e6c25a80d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1a334ca58244dd1b4754254ba7e6e58ab59657862d884d6a600a6c0f500cbe72","proof":"6cb4ccbcd39ddc77dd15c9987582d77abd502f3e73c6910da38a018835bc0f78709a7683bde2cc12d3308f597422ee5ba3cab212ee6c5fa7c4c81822f2a8cd6e389d0004b35edff5e71eb8169827ad558024c94ce8e79e88a367331b5355352940edb236545d2149efe8b1c2c601b0c41ede4caa0a8f26bc10919f27228d7c3cbc5057661191580f53801df4f3361534d66a66d94148c4a74b393f5c1c5974079524dcf5f90b5f09af860b56e93d9f4b5d3422cabd919bc8dce63352c9d8780735efd9dce6595585362892863d13c894d12abc1fac73f06a9523244df821950d5696cef1d8c4e89ed265557e87d06ce27bed6d9163941be18e0595cee5040c2782b336a65da5f3964bea886ce70c71dda92bd4d61f0d9f019a713cbfd306947a72ce474830aaf113581e1b22216cb49dcbb400dd66b78540bed08d9e65f93108f67b6dd802308611639bad2c06d97ebd95ccdbbb4d48cb610e9fe9c34899fb534abe0e1c6a69a387a020e9a2a6b6c4bf6cda294e16510a9b800c3dfad0608f684edd1c149a33493432ec8b2d2909fe78bcc0b52a3c39738201cac9baf9920b4ac68832e60b09cbc261524031022bb8b79b867eee2b6ebb45d8aa241bb9446567c61dc67342fc50e933acde4fe630863271103d0f4471e44ce258b9f009e2421e723344e7067d73b53dadf09b2ab866621c784cd3593cafc07546e7393e50505920374563cb962409a9f5f71ff854cd254477f4e828b7bd27fbeb7be72161ee21cc9af91449ddbc63656406028ef9c02c830088bbcf7731f9bd7f0ef517953a7ed243dde71fc98e9468b9b68b4a25ae4b694d46e3610d6e954d50f06a1071c279a75cf4b75ad34481667850ca7e2305cf9ee3a80705767d7228c963e6b09a850ca2794527320f4bd4a6c7429c4c7ab462d13ba6165ef3bc64e136418ccd222c02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f678edb3ccaab28acaf44da46fd19f4aabcb3edaf68518e8269aeaf6d1819a3e","proof":"fe22038bbdf57d99c00b1d003b614936f912cf1235be1ad0fb51f2a6bfcaa53f6601f5ba14a47cbe8ff594d23e1d1e0820bcf35636f75a356cb493d9eff11f151a487ec6e0db674c587c434e2db2efdf370720795d32a64e248c29e24feff24936ac0d46cb93319d3ca80a26ac68f20d7cef71f5330171d33d7c23e483ac471ba746a6dbe6e37d5ad04ff288b5e4bf3dc96dd28af8f745c6404072345f0d210ec544f009a9a15d374146cd531b14a12e32dc022ad3c3ff39093dcc92feff9f0235ff7bf7a2ce205b9683a48d1a3d8d0390332bc36884b54ee59aa229684b3805a4115fbbcbd5c6f9cbc096ce42537f045f7288f9e461350ab8c55e16f337075b50da8a102d51ae05b95698a6fd49f9b9c6acd1f2b6143a2f610f0521329eb51aeedc565cdcebd7dd67ffb28f86e274e46eaab683e0c8a3a771a276ac212b0c339ad6f93c63548451cdab0585167d886fd6bad63bc71b86abf19d912295d46420eea468990014638cd99c4a65e3d88b15e3bb5bc398f37604e8eafa1c4ead2536347bd64402b01e5bb9621bc681dcf62ae7340179444cd28203da32dd1679496fd49706ecfdcc025d4d6312a23bbcfe610849a3806432107c25f242a72cc5201fb2bce7e055d7c28f3b15d281bb58523ddbf299208f707241272cb97992ea7c79f09c594d0519659528909b54b252dbfd150551ba34af6dfe647e9fb7152244448a9e20431daca30196f1a881e7f66650deb71fec97276cd68b936ccda9ddbd487e72b57c3aaa8c538e2369e8a370495feb9abd4e42896b5985a4ecb6dfbe305e7861b25962dda25160ca0d465423954a009169c9480478c374da9156b2384e798664115bf557883cd223495beca0f2954153a7149e16690d2518794b99a75203ba1f8cf3cd1a0eff0e89dcd97fcdbf74b0f46aad3e4164bc0a81376ac9842608"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a24e2e4fe515dcec02c5203720e5348fea5e7b724e7ddb1c10c138b28056523a","proof":"9ef513f8659a3fb89cf76406681dafbf884bb22deb25b7ddd9bcb30c31d964476a7434cbd3f601a3565071c23f2528fdd2a0c3b8d59b77c7f78e5f80e82ae2612a77d536aaaedee51325ce52672db2759cc6e5da08a59dc8e1a612a1862fc1177e50523f13bc9bd4f058b6cb84770648b174437cf7c52d08dada39073f85304d5abadf01a6be46845937c7fe93e8d2c0b201e22aa1a7bdcde61f995e34a3290913f1a73104078bac8f42eb70b1c6a8750458613831bd6b9b080d26de9050c90639e155b614613f8ecb0949ef33f5ed93ba5b88b841ab51c04b6e921f62c1270bf03c66dc5cd32114d64e3410346a3d338199d20a40b1fa6c3778e2fef3455e7eaa8bc2e925ed0c132f8e292f87c66fe2318c3ca0fa05639ba7028bda71927d2fc078fe0cfecb866e6a4c15b8e549f4d1f7953ad7f52a82ece9be093959dea57aea5294a55d3a794d36f17a4dc34c14676bdcefac8728d5d80e6c3ce722a2c84df66f4d7f448839e623ffc8619d7c89e4c914e335d36188eee5bc2cebedc5791c9c1eefbb8029716e0435baa07b602e14d2ccd230770c3625046255394f82823112549fe5dcdff448239c7917991ee69cb6894e4a372ca55cb89e955d06ca805bc80e11bd41f998c9727ff1abdd730dc635faf22397b4e5e978d994dfe4f73a5a022714a087c48f2d19e3414c5a8c57e7f7d20b6cd60790807cb1bfec08faff3538e35e46c16ab53f50283aea85ba22be3a5e6585e491f8f54ac7ff2ed2879a18128303c06acf7df557327bf74a48bf2fb7901d3b39ffde51a02cc8162efa026472f86126dfe1303e0e58e732f6f7e01371d473cf95d0984c56606b8e654e8a5edb61e9a300e0013c69d8d0f204cb36006782513743873355631cbc06cf1f7e041d51f466e275f2817c29d49b7d26225c6e94180e576e36098f70de3c843cb50a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4ebd926a2a6ad4312f158fafebab9c2ad7c618a286d0e295f4a221f08810e41a","proof":"d4d96b0e5083a7d91f80dd033ef7d47a41a9ef9aab3ba5884e895efdc5f20266f08ea800b8453c372f36e893acc12e3e98b3f7cb103b08ea5354e0661135f622482dc196ef04b22b27e19e6e232b14e83d63ca82284a4cd6ecb38ba303ffeb3e2621f89ba79c5ad68cbb68537d05bf6138bafa955260f0459b10c4bad9bc490740eb35866984c2acfdd74dd908bd9ababf3b334850787db666bc3ddcd57b9a031ded18963dfbb3011dfe21269d17634648a06c967dba434ed06278163723da05ee708810554e5f6525cafdc9122577a9e60b864732a857a6a2efbba5f0ade90ed836d41a12739cc6b89f5cd2fdb5a80ca828b65e5f8cec28a582382ae59331436e1d8c04434b306a4f672f83ec7672f0b5d828dfa4099fe7c1359e37a57dc663d250b9cfc9a0d23b761e810b4ddc07a49ac6e8d69cf6d4f55cd2635b3010e42f063ce803c6d88046dc636cf9d554085f11bfe41fcdce89b5fcaacc67c4fb0d10a024d3a9e78cb425c972fa5ee2f0e5fc656a4407cfd378bdb570d09cb395ab13a462e395958b9460c2b3d37b1c235cf56d89e9ddddd9f33009c555d9a9b53c0a5c74daed7a803deb18908f87b725553a08ddbebcc7960af3b64f4dc20b97d471602868848b262c95c239276e8e285f323e9293885362be72b181aa8f6ffc6333da4065808ea2c21bf0eac23eb4ef0e1ea0985c500f08587fa7d7cad87c75151914b4309f6d5b5538585826d07ca68be07c808af74f798a117be6b451077dce6a50880fed163dd9d5c5f7fa7d9a776791940bce33b6248fb63e3d4f7850857d4f8abe132feb02e9eac179a4b2e233ed1ac0dfad8bf87038c3293997544dba7163635bc477553735e98e7682a0cde1df51415fad5a0e120bf3a413e296b972440dfca700fedb27c1f2f7876b461f8652508b8de9dd7cd5cd9cca91adc6de778008"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"78b667067413135b4f6873b2afbfa5ef5a2a32b1e53e50c75bb5ea0119ed8b3c","proof":"b0f87e90ffc7bf80b33bf6d7e3781c026008a4a4cf74067787ad3559745fc13f76511b5a6d8f3adc120b6da006b5ba7ea0d3e5ccd7e38757df15e351a13f434528a760ce010cf35a617a33d65f238317c0767e03407aac4efb693602b041eb1e26eb34097f7672c4dc0863873d0ebe224d2bd363391a9c9c48753bc09bca5f455bf30ce19e7077fe7bc0341f884d2e92fdc0af659db69b5a7ced28134ca4e807fd875c727a03f0c67db4c4106b3b06f02c0dc2a13ee15d577c82368c2cabfd001b903f0e119b9859bf53295fa801fe57e7c36ae76abc91a329779a354b36c90f22f51515b4810ded6a1f90c823b224f45f21a58452742e461ba1edf526718b7138df211986ae1809fd281f5de0eb64c3730fc86c26f6a85954b98abed7cbd838fa5e7507357652334e87cb32f6c90dd6e124731c324af574ecf947850cc1f31242b9a8b50522f781b7224e1c6287d2746b7eb681c03018ea2a85e4732c0fcc75961b56390c01e86d6b230b7765f5e69455b231f267fa72766083c19649a2e3351092fc2535fc106c97ba94618a8f8d56bff0b929b4e66654c75caa2becefd028a022ebeb0e6cb2d2852d0b80a6db930888d3877c258e7295fe2d3c467e1ca15c0eaaa4d11c5062c68c04c32923d50a92772dc0361bb952071cc16501a4c4b92c7662af7439327de15bf395618e59ca9be06e825482bf3d65a6cddb5960158760b460ef88ecb3e42977114e3947bc6d656d8c4a31b7a48d2192794505823c87367e4c5b6661edf614a640da67f6b5776bc40629a098f027b7ddfb1ec587b3c0210e84aa3d60bdbeec9e91fc92b98ca1e30be35b6507b32dbe9a0302990a96f8305f6204f3b453765157dee0f5f466bc69779fe0a1e9fa8550d2ea65d20d9c5b024d8ba5608f7591f15e51539e49b62bf2624ddd6eefa79456319977cd3b897d0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3a9d74fc3f6818eedcbf7c4ce67edfe7c72996485299434a5d08c1fe29b1610c","proof":"fa7b908336476aadc28bc8dc8918e207a78dd7cd609468db34866899027c493a041a20f9e9eaa29eebe95d0a321672b868adb1ecc4496d8a5e0772998a18d52cbef1ba1cac3054c1b0117cef18617d1245f02191f55d77d9e8a82bfb029b572c4852d0514b9edb352a613ea06bf79dec2ffcae7251ae1096f92f1d91ad7587559fcb06927144a1e7bc0ac61bc1c399e6e940876f45ae2941523b7790029e6406ba890f49c6eeb9146b8e549fd6493de648ed535d56f250f0b911ff6867507a06e0032b30cde93629c045ddea07ad7317d98cebbc7c9ac4155c3425a1a801160a4cc44f2a5620fa01e4991a0220fe231ba843ceba0f3c24e8659253aa311a0936c0dcf8ab3adaa20c613b543335d050ce269499b52179a894386a6c1e2459ce659c36c102cb2221061bca68d734f815cfbb6a41c36c6de0e32faa39923f3835286cf1a03ccb7e915e9d38236811928b8a1404cfc2f6042d26d1e826ab829a8224769bbe952dfc58671d5e4877de6d0aabd2b3ef5feae42f4784e4349307b7e31656f5aee73585aabd0f7066ab22f2fd36dd2377407c0e8076c72858b5825ac61f10753e4c31be399d362cb421a7918117d8d1f8324261f189c60963f75f664369c2b9338670ba72c4fd119cbea900d218899318af3683fce7b5fd8e6d4962f110a2b7f5b9c628ff87575ef8eff1e405e933754b6c12799b72a848a9b76e96e562de7fe6d3df8abc132e10be5ae97bd844683882c9b2f092ecfb9d2bd14a5701270c93c06d9cf61253217bb308408aa7d7bc978876cdd9606984ec8ed98abd980092960fccaafcd225f945bd44e4c6ddcb6ba35c88798733aaa86a72f431ecb43d8053450b8e6a00f593037c3e6e984122b0c478e3a79502d81da43376c3e1240e89b560bad91d212291e5d9f118bd681b91d3c682d326419b8d2d7a9669da6c08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d2ebef022fc78fc747125941ce801dc62445a33c013806d1932e696b7901b829","proof":"64cdbe73bbcf1046e78efaa23fffd1eec03752d7799001d28b93e4dced7fb327c63f5853319144fb43c8568be370b6bae486e3c3daef5ba2fbf0b82e2219bd6ecc9935e1a20fd309156f2d3e829aff2c5f307cf6a572accf5e5e667d495e0673921b4ec6b3c6ad024b4802241dac73c2c292263788eccf9c92683aef496187652dbd67b2e75ba25d3a5c77af39ea6220266bf790f7287f08b1ae26228e92e80f34841cc2115e5dcc3d7d95132bb258d67a2217fd64c01718777dc8c3c49560072e645aef0c288b73c23163b30994ed7602d00c985d8a8b50a26408b67b75b001f287446017d1110644d04ace4ad70d1d032c1a265eacb8cae4fa9c164b078032f42bf46573da433723f10746d23fb40958b6e435d0592c0048f843dad8e9ee2cc880ce291e179bb15f9767fe8c0af53a5de0e48244f34a35c9c3bb94cfbaad631e8852659d8df7d789c31a2866e71b6b5497bbe37e88d82c5cc0f56458d6b760e85acc4364bea1eca379d0b873ceb9ffbd316b06918b33978120243c3a5e5016642c2952ab384e75fa6b87412664c9ff9551596852aa71ab7af1650e83b7547f82e58c8914894e013388d336411b102a0fe3ec3654e4c33e4577c91c14071368f8b034cb5361789cdd4908804e7781a946615d5758b16bad58f0fc5f48d6965a18fb725fffab9668ce2dccbc24c34c8c32473b8cdb99af90e6778ea932e07d7962fd0bdd503d631485afd12750acb700c1ad4f49e3fdaa176b7fae5834168101f429d0af253307c1414a9dad3e4e4c1ef5ff7c384cd5875a1ea6f0b125c6cf263a59a9575776474a1b3b56b0843e82a754374a34fca07b7f3f59981b6afcd21dceea8ad326be35dd1edbf182a11417f482543238784a6e419dafbba9296c5900bda47b65ba5bd4568402fb9ccdada5ce1bd0704363b809fd251353d33ff1220d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7c926ab3e3c6bde78753272511a646e7d3acf4ca97532e3e043555bb9bdb4838","proof":"a82d3474ee41ad0400f87ad4c97f635667a6a57558a724dc21d4cf140e89bd01025677b1ec636ad6f862e2e72ab838d944faeab714faa985c170085d53cacf2ec22a098c6f002ed2a57574963635a8310ab4240bc1c4890010ee9147fb903320f89acbeab0a8eadbfb0935828fe60a819161ab03023eee3ea24716a11c3ad7003b9047978d0b6d1b70521ec91a2fec8baf214dd3cd573191804d05054c97cd043c8f4c904c4239469aadd4c11dfab7dbb0a8fccd2645efcb2c2917396ad732056fc50164e4b81d83469e9efdf35986e2d6dbec222df579300248768247ac57069c9a5a81a3db144269432ca0d9c67a820c8ad2808573f461ecae2d6f0c04043874ccf7796b632a7375de5aac9403d170a332bd74f05151a12b459fc8a8fd0f18c4344eb15da549fa49d11f7bf55a78604831b278afe6615c03cd0ea203687976bebd7d73adb87fe96bc7f9c72a8b3d9781a0245df518341db65578fe7821c352de3cc9882efe6074b425782c72710a762d9651ed6c2e63345dd52f6bf2d1f61aaa881bece1281ec4c89ed89930dca96f1ac5d876662b14cdc2aff5d71c05777ceafe876b5c628248278a5c9c69c2985e298697c134213bfde27de69c26389239f608ccc601ef8aef98a4eb307d2193418f9589a39cfaad7b0315dd7730cc9e2406724f9550678c355993c722ceacdcd1556ad8f7d11b055234d9a81bb69579775cedf8e85c4497032ec9e7ac06f882f706295a980f1bc6b6ea976507190310553ec9ddcb1a027a6b468185efff7b044335813261f754b632b1550ef0d68a125d7aa02c0d14dbfe2467e90099344be35d981da97ad6e577ce9634714361c26a5f0b346e11ed15ee73e3c386293b5f89bec12fdeafe65a0cb0c3cf9e5580c0ad09bd729ba57589dfd9c3fb3e9739b743d8596eedebadd410600ffb8308de5e9b0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a606b9b14125bf72a25f3b9ee84ca1635e24055f49e45576f6cbd47213d9496e","proof":"1c1f548ec57a24f42da5f0ae7ae4d9fcad59f2aad846b22c2568361a3659242f84d1caad4e9ad13eddd641e67aca03123c2bde0cb0d4f28b04ff5af413a4b530ae95fbd433dde5ee80d3f139365c7997eb821f998f959904a92b3bf3c429d945803dd260d65f09b365c8bbc31058836594ec6a9c6f307c405415c3ad64f7936d7130325952f0d91891be2efe8b1ac3b3c0c507c977785274fbb7f820083a3b0dab213657b3e6ffa2c100e21306999e4a92460d2b9166bfadb6ab5a1b7c0c320f1ec10962bdd86d30d25fbf24de88af7236c97c567ea4ba2dbacb3eb79b5f4005784a8d6a5b39ab74d595525a5695df4591b789c71b03d039f1953431b13a965208944bd82e1a38901dad9cc8c9fcb2c173f0f5b8cbea2e951b6100bbfe0128097eacafbb787c6974d2266b56e46947bc75c6cb9aa5e534c1b83716e1c96d7368ea35a3d77e68aef2e96c5387b33ac68e21fe07ef46c73ad42f7bbee61d5470772ee4cf63f6de7339f79ec298bf5dc0ffac108c9ca8cfb677afc286eda82bfd064e2badf8c5f94badd4584fc5917482b36fa3babeb7adec299bb3e9f2fd26a2154623c20e5dcc92457511b0345cead35a7fe318c0dcabd8d6647a060537070731767a946bb8b1c51bc7d57bfa190832d227e71593bcb78317766d37c6620b6d3e000a669657d75ee2a5b5e0e21d48ad81cc1f1975ce5bc88cade335c262fdda5e02e9449f193b2dc1b90c4fb960945e445ce642d88280faae39971098a812e931b4acf32cee1101cd8b4061d705a4798b50d86f89ab07e71bc0dc76a13a7ba57c2aed17aa764a60589153abc5e21c50a5b09c634582f3945eb3575fa07f3ad77a15b419aaf9b5f4a6ba835e1ca0bae49c605d7dce0687019042f8a1f57eef3e0ac441268a2b94d110761b7764fcd9505adb5907968e23503fe3d5ab2d9eeed80a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4c9318bef595fa70860d61aec3962f6fdea90a2999f44b78c5051cce92e8561d","proof":"56b06dc9cdc31a5e447d86efa07687e0078a61972d9a4440a11cefbe86162c09be50b62bee5b39d8d5c3251c5e3a6072770dea3ef326c8d62696c011947e517b7ac8d25b565cd8554db323981035f15880cbde4640c2917c048ecdad2658d82d2870dd9d4973e53a96a4773cbc1264d846d41c6917e42afc474a3e9abd73ba06761b6d9e9f68f5a372fc53ccddd55f2132d2c0286e6926a186f68986e4f78107ae68a95af9ade6f00d23fe3a720b1a7fd4478f60cd3975cc21ee15ab287eb00b78e4bd984bc77d6cfa25123de1f0c613ec3686bbb49660935c7f0fd3c764560daa4c257d48e950aaf513057db6032101ee14b8651ff2b9faa568555fabaae27b44e02d98b1bb8ef41424c878bde7b610876be016f30d431259371818effe0364de10dbcb0b8aad4b845a20686867d83ffb72f88bfcf36f3fbc3d1fea02f29709aaf02b2fedfc5dbd9714cbaa436e3e275f90b1165515c5b4d87f3c24ef792f660c6ee0fcb913f312470f0890cda3b738b81b59e17f22d19c00d94c5d26745e48fa369a630eb975ea9971600baf18355adad9400a4a83e8007b9df4fa6581b537e2370f411a8b04fddc61d81dca5346b2799020d91d3e4df459a8ccef68decb49c67ff6ecb61f50df55b504b47da5415f25cbc1eea7ecbd034cc85fc38ab15151d486b5c19fde3517d95443a21fe34065ab8170e6987b16f57ad5e90e55c96912b6a9a57975c311644d07fdefa00673f28b0fadd60b5e4a183213c81455798918486392614954e2acc5014c352140080f2243267173dd2620d7280736beb418235407639724384afd41cc8d29d2a2226c32627f2bbfa36ff5010769aa2ce1a576fa5a430516111d360837caa5c18c2d63f132c88d14f06cb5e7ed9268c2692607d3b3f76eeb30eb4b28516857f2fbd7664eaafc5062885e7cf9dead9a3948300e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7438afa632c8f22f2f3d6eb6c1d07edf321a28305e5631d6da132fc78524a833","proof":"76fca3a967dfdbfdf3af469fdfe6e614ed465a620bf3ac9eb2838591c3ef965484b82acabbe18e0136a6b979437b5483b072a12e97e90c84c4bd3023c03d2d055017894808dda0c6c9d6cf484b80304385ceb82449c6672a08407f11e52bb16144a789725044cb403b840547e2f2246e672e81a512e626aeb0a356e756f88f4d828f436da0f8bc3f6b4f5b69641b63bcd1b47c4e8d5e70331b2fea2276e6830fbc6bc0643d9db70ca1d4247b3f884d43c0ef02c45cfbba32a2be6dd69a089f09ae5dd3e313db2b8a70d9e68d8823ebe2966bc0565a3016bef7e59c21a152f00cfc7083ddf0a604cad5bdc19721a3e6ac6f6cc207323e84cbe0cef035cdf7831712b9684aaf574429d1e6e352d4748cad9dff8776049ab4de2305825f8a6fb006a889af7a0236193d45bbf73e62fe4080541ca178453a15596faccaebb44d804c148e33d23d06da60e79aae8404767ed3fcd8bd9865ac914f32483f576de86f63c647b1c1bd3e8aecc1b786e559ee2af44034a988ee6503588056ce46265166167268d66faedb41aa01292dd690fff7d619143e80434ba7d9e2f9460f0b7e507776c307666f8b1e8732f406ae3d53a4c83dc80beb25b45dbd3c637ede350df771cc17e7fa970f581b1754e32a107f069b1df9b1cd6440308066241560a0088e7c40b4475e9aa26dfbeb5f895e3d20756d3453fc5594669e98634025584de8f00120d0da1b152ef86fe7378a07b2a6e48c4019041a10d1d02b7a406db1a5383a0b8875a52cd6cbc5eebd233f4a6cd1f2a7c1ab859df70de50e92af1c97fbbbf054de39c64229d04341fe0c40581901cf746014b5c13151c217938d4a3a1c1a1f4d57e6ca9a606ca21ceab1d32fafc0bd2decd10d703d8da841ca00ac1a2b86d70b06e4b93947f33b2981696c169faa927437c1bce125a9fd1c0d605fe70ef97a0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d2d1a1cf4c358f84553d1695fa2919725bf22d00e300e9248ca9621726417323","proof":"3adea7c73a6d821bc2e6dcb73f470b594d50438b8e62cd2b8f18e7df4067dc0112abc373fc5d2a530f563a8f8a5cb072b35aa47a4b89d225bf88e67d8e197357f83751bde45f43a9d42c19a9c8171ad05f46d01a90ad0190c6d37afd632d6c590087ccdfa46caf9409596a7674c4b49a6a3331ef3f13f8fbef09433d127dc1267f3a42e314070a3c951381abb76b4833258e90bc0e80e3977d48102b53b6440ff96e0e8224c5499e276e0dd2d97fd81b9d884c753e3d58b44ab04e95fa85640f100415616d1c309c19687b52fd740bc1d903019696c61f4933fcc7f2ec1c4109524176cd1b8a94d5c477de36906c567879c5df98c6a2a099ec590efbfdb82e6a48ebf9ef4f16bc8a1cba99b47a40096b4bec4fedde8e781fdcdbfda1a83a5b3a1473630b961309ffcd90ecd70fae1e142d8b923e994ee6fc0e2d3cfc215b694fb2c0637658c84f8ace67c8f1ef3e0476cd918e316fdb25c826fce69f1c2cf927588c8f129ab961bf17a67480d3e7906db910538f0ca549e8c1a80d8e2f7056239c775c51ec385da77d3b4f48d4db8ab20973d8a13b1121ae703b0035b34e2c5af633980216f0f26367cd19298ae8af32f99ed14eb1d1e25f3e715cbd0dda207d84a299020082e6015a1193af75b1de9b564c13283658e7ba76fd9601e0ffa41b2e02e157eaad581d66aa4ab52caa6d479cfc047314438447ef7a74603efd565514f21c756301ca812813684f4c84ad2c702767c38f90de84f0679d2820b6d237fc0f2bf7ff04d5eaf6031667cde6e1023493d97e6ba140242addec7565ba496ca2fb44de133f8904f1b1d1b12088f3a32743acddc7b0e841b86b670b5edf6d7bd6061c5c8da70c5383ce93d4f715ff32ccf00a6fa29e01791f999912206df306694830b373c63cc8e701f2975eaa02543a5aeee8cb45d983c44185f1c9c1650c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6270d2bc1a1b9b678b2dcb79b7ed86e34b8fe92b43ef731c0cb0a1e9379b2134","proof":"a8b148ab808a00a44a4871969e25fdd6a6fd67c242aa89a756e26e5516b0b00a802031912088167b16a01d376133ab633aa7d829640b1e37bca0a41664ecbe5ad2f5bbd9c4e18cc5793df783b91538ffc948b3b357e02442b8ce1fe8fa843a2952c46128d86339b13432c08809d6176c668a0f4a6d4c94a4a6d68057733b1c03ffa79b841e1255be64cbd5968eb524382ee8d43344d1e4239930d87ad0600206ea28d3da74e4771b3509cc8fa3cebc2d4a89efb57d72b1712e9be1966d8c940b88b4d5ff8315f8950c937c58543696a94766a0e0b9f733d8746bba1cd4c7fd0c5e0f4dc1b239499d80641a3cfd0a6324399e17e63b3acfd11e78aeb79175c84264eb3f21e1ca8d9304a851709e0e78a68b2d64045bcd316787e1cb85346bed5b5cd9a191783360ebb5e8efd23097aeda80c728966d1988757703a01542cd09249e1c1c98a8a6c07893e9dedbcee2a33a4f24cf768a235e60ef04e7c5e9163b7180bcf569d788ab9d63efd6e21038821e6947640c83e033c7a79c640fcd2e234baa187519c4815cd398df87fd9819d10cf4d1b29482dac10f526aee29adcb2707260065410be1f1c1ebea41abc6489d91e00da6a9660ca64771c60e08eb5b1f6ef2a41a17b8130d8a608a4413361677201b656f66ddf38458500a160c4fb5f44562d8ccf791e1388a6dd931e5b3b1adadac453929d94db78f739e2de2aa8ea958360ae13f44d95d09eeecc560f5935a169a6dcf0a3303d5c9522cbbea71bc963870a093d699d6a87c77983adee0042a78dbf6fa9b12b7a96a59678e7926aa3f09661fedd128d8238bdc968b9c27e62ab610052527e54d7d93ca2f3c474474e018a22c153105fb8bd4b070d56531ca65a8e1ee61608915dcdecf7a4873fc11fd0062351e18f4f81f9fa6f3a37a2b113d55d819652c68346cbed85407cb9529f004"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9e309ad0e39be4922c1b67e43c78aec2f0a93634d1f6e489bd8ef38fbe608e0c","proof":"2022967a0d83d8f7610e2e713c8ed21e39ceab813525f87ab4b7fa3789aea33e3ecbab048086bd318e5f992ef7f119f3c2b20d6b2891cb54ffa642f318b1103b300c93a71229b31886e729f7189977d940404dfc91b085c2613429aeabfe9a1f12bcc0c5608255f37d6b4f7f0e5ad8594a5143ca880d7749759f9a6cd4db24518630b38d0ac260905d84e056a39e4a4256dfd197bd68ff28479bdf03d31e0c0b99a0abace4b9f958589248ccb88ecca0c0f6a677169641689fe62af0d887130abbca4257538a7aaaa78346f9f5c8ae0592c4dec571e2e4f847fb83c5cf56cd0a5c20df0a38f68d808743513bc9db86fd0fce8582cc0f91895de645a6ba9c2d61e4c288f0cdd80b17a56d1834dea454b7f7900eba9500455e69893ba57d836b2b7cfa54e9b760c3cfd62367af3a8d1fd6e87b87ddc7eb9e1336451af84dd15076562fe2a9267f7ff73fa1e8419348a9372793216a2358de2bcaff70acdc41800800b0f3e82c198ea3b2038619c7a6a52bbfc7b57ac7fd5d61193994a4f45e0d6f3c929de8f043b400ec9c418338d76f23aee785f09cd64d2bd2da3f5e7bd94c2f9a0e462148560b4b6f85c23421a433dab5db69b3f27635f2525c0f4cd1dfbc2a24221f864274f4689b930bef2132ebd3f82744aa79d3c8588969e61d084fab2162b38aee6eb530d1858a99b2478c50e1dd67dd34509d1f3236f4a8c6cc72904d06fa249f9e16f4daaf510e1c3823f56178479cde0f68608c2e4ba267b969940bb6c05dbdd9be59e25ebb8f93d6c2ec6f64193134caf34a30ff46de1d7b7ff73ba0ef31436f3c65d02a68236100e0c4fd19b64e5141b26a22043b4be14fac286913665fe737bd8dbb23aa7aada6f66b22c9eb030c356aa091e28c4bb19697690b514debfd1152360650a342e8ba5a09865ff677ca69c671b203dfd412d605d70a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0ac3cfbf0aca12a574b7f0b9dc6fc0c2630c38bc858c6765c045d445d4191146","proof":"42dff805d32e83711907a732b67a9ec7031ede9f7027f54738492d1c4bacce684c58f889f8d95ccf3a276162caccc2b5774a4f4f99f90191c8e2da53bb70b317922c90bd1881b637de8adf3f1c09f417ca8eb4bc880931f9c96bc00f1d495e16fc156584689348f8c6a73f2c168b989c7788f1fe5a58dd254791df8c5ad3885a823816f71f382924bf3ef59fcda35819d061b7745e0efd192875baaf7fc82f03a476a910eab9568832ba1002c84f713ce1e3e5d7f723dcb9cb081ff3a7700f07cb55e82701dac9cb852decea8d77c961756676ecfae736a951fb449094cf7509ca6f6419096cd76bb648fcb15595621fa319506c65b0ea0dbb7ab307d9168649c8b57c554405e36f219361df5bcdbb4c59f1bdad6bbdda61acdaa553e192506d4eba6a84c5a2be969e5cd12b3b772c1cf10e51a3d30e5fb593f27c9234817c685ce4ab564e0b0a657e44ab656551ad59432236388101ad53e9754111aa7e5477b08ab6beb1d3b22ef6bae9eedca9b41e3dbd681d702d38788592c09c1654b379f45007d8824b1e4fba2b29d72642bef525456c45d7ccec995a6eed16e3554351b8e9629c1ead6fc27043a48720ea12eaa809866f954bc2ce99e7ab4aba5d7955402a3fdf70bf14bb6b5adf5d6dbd3fca7ace0be30071ca46483525566f7277597a24ed7e3c2d6fbc173f384af352b9f1d7120d18ff387c324a2f1e3220467a2fa89b049b3ac4231c7a6b3676a0bb35331b4cc5233a9b64528b49658a1ff5994e1e83927a79447135de64bef78a19b7ac0366b6498bb6cc36db2fc05a9fbceb230cb44db793d3aee6de0bdbad757b81ec5c791ae1faae5298000aada73d1d9352a8148d5b518f19991dc04f130b3f501352c6647206b419ad8b0404e81baeea0cb80d9824578941eb43bf356b5c110c87918756a193d12eff9165717eb3969d00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"de30f220ed7a2f024cb8a7b798f073c94d8fdd92cde64861f3be809234d3cf24","proof":"4872cf5935c384364d19dfc75d1b6606bc05dfbffcc551fe0172503285e81935c861cc6111f5b1a09b40bdaf4fb242cb132d9fafe63f616bcadc506659e11f643282e5a9fd997d9df8da6a11cf875c7b0bd12dfccec5a0350d589f5be946b40c0cb5e945849d510c13072ed3fbd7c69c946b85b450bc764cdab0fd3de80bb717be7c79fdd0efc634decb9b82984149ab04ca7939d07708fc5d6b3a8adda974072e80985e8880edd10651d2f0e4f4a1c3c5c705bb56d51e7604c7a61924157c0bca39e2aab46db261cddd419f5a18ded91cd8e2040e5d4f948a2f2d5323c137060e9872c40ea8213a62949272535223d30eab4eb7924e6007e1f79998ad394b63e2c3399d76cc70c66afa49bcf1cc57491bd45ef1f507560fa104c4bdb9157c0a92408a72ff32a957a556723fe4d18fe03e8443d86fd173261d537e719a826d5ffcd6d682c0d5176649e3147784d5195b5ded6caa347a5f339c7825f54771ab335682b00d08fd7eac31698f55631be1d8a0ad672f8fcbd0e1cb4686c158c5ad7964c981cf1bbb1310cc8355dd81606aff42c92198f574f8402f312f40077f7a22949d62a98a6be52c48008ae178c7278b793ee865a9d5e47d0c74882d06ee0c537c6bb572e69849ac0da54064d34157ee7f6bb888eb61517da3f3bb6d875ebf100c8f6744e9bba0b5ed290cbac980a35aa142cd0a26815afe209464f3ab70625bbe2ef1b719828fbff7fbb001d4600781e6edfc2f7a918fe9b3095a86abc44454cae95f40b4f036aa801efa246e1855142ff9df880ba8ec6b2387a99f85efb95084589a6ba07549c4e3e2e7e6783154ee8a0b763da816297cb65ce4006bb5ab6c9a310f22c9055e6c430c88b7f27c99a5efc351e656f0eb203b68dc4baa3f7a09a6a31d1f24381a4e7f40d1275225292c816b2beb08af6e7219b4d49c0310a909"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e4ce06cb011f14bea27ef36d80efd4e6357c47c1a82c31e9bffa2ced80d2720a","proof":"eccb8ce505c25081b137ac64675bbb6d734bc850c25aeb8002cf429ad3535855a85c12f1709a86525eb095c4ba7b6f5368589c8f9811b213e1e3ba0aef5ca63ce2563d22028aeab9922e2c83d7e94c3bb15cb6412bbb552b53eda50ef2d6df7b520a491a4454508da211b4faf488aeef34abb411668632dd969ea3820b319c6ab80a3a9463fd34444da998b45fde210cc0ec59d775bafae1de348b33319f5d0c435f6a25920816642f69606d0ca04e1392da0b9f56a8b6ab676c9b9a657fb90301b0b23d8c55f09446a29e6c1faa072e2505b79fdd36318eb68349ea6af8b409289fd3553cca7efc4b6ae5adf5df89d7b8db14d97f3819e9697aae9a7bfc476b26d180af081ea99c0f339c9365d9dea1871e2c71df76499c9a6c612976e5eb4242fdcbafa05ccddd44f902cad6ce76f8c30636d7ae56e4f2fda9d89327ae396fdc4fec66a2fde7b1ef54bb96c9f00d336fcc573719a108d9091bc7f257073c7090d9b5462d3ae7aaf7fe1e3924b10c8a6c96e75a592ed517c6cfeb5dcddd9f60bcf2dc11d71cee1d2237160ae2ec8ad107da888f48c66ce3f9231d4a60ff9c47668b3556aafc30ead07b213083a1c9313409a2aedaa4d270e2ab42ae5a773c70fca824b4120109452f5d9796248165ee628ed2d826dc83128d14b712fd8bb913acff1b61a5fa48d03fe4c84c306e08c8d996708440fcbf360753a7ae6cf53e4cccda5a763f63d614c7887523f65ea00604d6cc215afcaec8410710216d7d4f6b4867f131f91e6389d0bedad5b130872c9cbfbb3a56ff56dc61550d59af854a1f98bcd4d75a761b229ebba06ad4d527246317a0f690c2c6de6100b8b078045278bdb6335eefea48ea1a41a1a1c8b2dc2d62d3ecac9b7845133a626c0ede75410c1fcfd0e0fa11840169ad2762a6022415659ecaa2b2ee89add6d7c530e9d89804"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"241bc597290100e6a78c27c84d39d2ef8dc619f1db685b9d05432cfc28500d0e","proof":"28bb5355bfef6495c2dd2c2cccb39ff89ed7eddee160da4673ad6f5a5a575e3a1492b92a04417049b8a46605d374489e711c011fca732597a4efc95ff56d0b4eaeff1fb40a854ef876eae9df78b4ea32c6f439f59e8ea439ddc6cd90fb82645c08eb10bbe1b91b57a0223de63ab915d1740f53f3d7a49442e0711f32d2863c5c4e3fe55e07de8523be2f19c89a02249b49570f221c97c2741c8e01d5c26a3d09d34d281f1a0dfea3baa3cad206f8fe18266295fb9d0ca2f1605d84a4b1a1bd081216c641f9a56c009d2d410eb67f729187b4f58c30e0e982bf6d52897f57b5065251596350c92aadb52534c9c24503244f7080fad21f98122731a0eac9647445a837b25264b4fdc781fb8acafdae9dcf1bc707a4a1e69b792bf35cb4c7866b45a22df376aae8944c8f4bb4d4ff6a69f634082044960f7e92bafa07a9be7fdb59142cfadaf5cdd947ebc90e1185e62ac6f4aa9530c90373a75c512ef4386ff94fe05336d01760e2e5b6c233aa6ad1548446baef0b086240f2f89c8671b2315c16fc2529aee2dba3b80d1c9d5dbcfeb1e7f767676f2a56a3e612a1343b71a08338d8b4f6e9adf90adc37c0cdbbdcb0355d0481adf8a76ad252b237063af36c7b359052ca8f4e18a08a25105003a0eb61efc63ffc9be886952da24c62e134dacc6e8e867fbdb73dad45bb078322f2d025eeb62d185dc2f42920f0faf069154a7160de7574baa916b1a3276093db62ed60943900a93262aa118338d2b20b532c9c5d1051ddd743b14d1d9b9d08e21e6b7b5356884c06a9b92f21a18a6950de696e1590ced7cca9cbce396167ce461582f643dee736e71f28a3aa835c704b60211d3e1d9af67abde21d95787a330ad2c584b0a6ce558d2d7229c325944951c6e1b203f33c6e1dc1b1d427c020c2480014e9757cd4c32df64f2f6d38ab41629e8d9b01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1a966c032081275e2e462acea71b88318ab7aa8e2cab8861796681b303aba247","proof":"ea4d742c32dc6ad1ed878f48c000ebc8a57172131e22630ace3c5f33d9245d034613fdb03c3239de606043121622134e3c979aec2515180d67411e14394c8d0dde16c4dfdd3e562f89983337bd40ff33456c73bf6729c4bdc481cd02792927640013dd963da726792e30d04f792a2609b77efb2f27234a712d86d6b2d8105e0b6003f746d2a70b22e549fcfdb22c3b82ea0bd1fd0bf68459b4dadd7c44d39f078f8a5615c076f595a8088439a68f39cc9d48107680c971bc8cecfb294d42250c84ddf3cb3281b51148cfab1d905c0c28e39fe8409c35f97236028ad1a1a5400c02ee696a27c4403ff93807c2d4b9be758f10f2aa3ace454088aa28fcab81563ba83ffc7240a641f882d6ad876fe9e0cbd72239e60f3f3dc710135cff9b5b5d580a63159592d81048fcbe52b15380c939fc34a89ad151280ed7629931ad78a177f20b16c627563840f2c7c6367beff4d2ab23f116d6d9f7cfa1f6e032a4d851365c5a0201f18e51c6229a0d1dc2c023225fe97cf7e8cddf6f0515434d3544db4d7657fb3e17b508e923fc765a5ad43577c4a85dae172e761663c053ad2b8c5b0c1cf8fcb6392e49f683e1d4d277fdeeab3644e1eb65bbfa7719c02156e637d859a68ece2320eaaac836cb123ea1b475530b49edd4bf516016ac71b6eaddece8330ea6376a32e2d815133489a0b2ac2f10f4c22e055279c297d41d60ac27d37b7c5c0597479370f197b5423894add847850d3afd10ed77d345bb37be999ed205032c8ede2db32bc6194b9129208c2e07c50c2aee3769061973ef31421cdb50082c766a7fd6fdbf36a5ec5e1c95f2150f5361f6196264019227924a39c76b2e74308baa56cfab0d7e6f34c1f3e6061f770d58171acbedaf29829509f16c18cf1109e1cc5fdbf3a54d55d502ea5a7a493bdb224f7d523a30c118af64f1540957fb0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fe9ac556cb21a216ad458699d301a7131affe1556b487b14ba3174fa9f2a5435","proof":"20f5535ee3ba95ab137d784d730df60a2f822263132112d5751559e052cc5920c0f3ec2de1d10ae0e0136b0edd7668ee665bf41388b4f7f741b13376a7f89d5e2eea922dbd0ce7248a33ae3a89c5026850a28e581e86eecc0ff1ca16ac3e857b80d67b193015086c31c82644a8d18578b17e7ef591191aec0a08898b462874784a22167b4f210fb797ae58db2205cdc0d3f15cdcc627b753e13886017808f80b5e1b647d8f1113872a48e70b43b9d8e3bec17ec9ec1ea3aa85651a8f50b2d20d20db980ee974bd40c6fc630571a808e5fd0bfb1c3c72e3fd8ad5622f981f03082add2f8288111d2cb8213e91ea61aedff8638012925b7c96b0edca9ca52eaf5ac4cc813c24c504ccd8821cd68395f8470030d7a9c99df813a25cfa63f477d42b5cb95b3ca879d9d37edd415b194bb47bcf681381298a4207665af4dddbb3952ab2f4f3955ba97b66d3edb3dfe9d085ef4d7fb9d593c0f7370218f65cb5737c0362f18c6cd3281dfb0f6d7d94d70b99ee010c5f9eedfc885e5d3b7e1627ae122bf84504228f3e02619ef0182d3196b356081975a7de3d649cba7087fcddf60c180408a043de1fd1f4acff176f3928bf85b4af2c6e13fd19a74ba81ba514b27c5e2c71c3d2737222c67fcc078242548eb30c71a36d94743007ddc8070695b09678ced6c4c239f195aab71d17667a33524304e4fe6427e55960c81ce4385c6df9517ad4d332ac8cf5f9088f4bb54923ea0380b70195acb479df33825ee759ab20552094e6e6d25b03061e0aced710ec20be453125b3d8d07af5f7388545cd7cd94444372a21d8119fa64b36919940e32c37aa9c2ea919b63a57c9079206d7e1bd65042ddd11e7b40ced7cad8b9a53d71f08fdb5d3e54d3428c5d70087808edd790453ce509e6bd826a0cf343c740c1764302d00dc95b8c1099f0e3ae19cfacb620f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dac5a79fe78654616e3038d021c0fce264dbb2f3bff0e18f526f418be144f863","proof":"343f0905cd54d058c87159da1c2a5f7c162b3416d841d276eb03b1c388954f6c0041f84ae39721c8d212efa0021f725fa23cc33cb45dd2e75d479f3aac75357700e7b77f8e4d6442495ca6dced3ef42f957d4a74112c958fd39e13fb39c32405e85bf00af19bd64778ad9fb7a66b109f5b3a135c987d7208071f2ad6ee47ac266926a81a54cdc3abd986ef708f89feab1a32d0ebf18f3dee8c136492247a0400dace95421e01c48201c32d3feba2c506dada6e1d61e2e913c4eb19b8eb9bb5084052d624e2d480263841f90508d0ad5325b0001561dff35f162d9a70e8688309907f48d03cba2f870c287bba3cc9ee93bde0ab4871ed4ea3802281e5c2bb5702aa1d011084369035b8128d48cc24359fb1b8c93509a8fa771da44818f7443b129e16fd51b8dae9e66842846b0fa7ecdeb0cb356f7bd3d7a79e1be28545a1bc04007a533669545e227c75ef5ad072995707a8c1e3a6bbb14dd429512f24435c4a188c1030684fe528798cba9cb4368b1b2977970395117b2bfac5a270fef46a2f10d8b715a5c4f0e632a8aa9eb541715a5dfbe82003255c192ea5fea63619da7e42ba4129a142c0e9de3f306b2574f3005fae59f8cad7782437ae07af60d1b948000b2ae1fc61cea422e37689801371623db6e23219a906d60562fb78b46cdb09bc3b6885b0848889cdce1a001c97a2939174a64bf2432244516a50dfbd9b8d2c16b74cf254ffa37db85b3de590e62b4c686f8773bc337d4747493f5c3910e66b0cc417a0dd986551ae6cfb14186a15c82bfa6b8bddd30e2ca4dba5753a77a03b18ae90679883fcd40376e82cfa591edbbc7eb12b423170c1eb45199bf2979952d07913e51c4d1aa185993555a1bb0adbc2b648da5e7d4a3462e39dd73be339044d4f0b0b90c286e8f5d219a59e6e7de04952780d2ddf64ba2e758c7ce511e50c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3a49519b9eb6accd903c30ab3f013417477f763a8e4a49d153069ac9452e930d","proof":"76b4c358b2a8aba3611dda944c4f304d554139d2a0df1cab5e6e47b86563bc6c5e1a6b2376fd7f7f9310b53885feec1d1a3ee92993f3fcfa1d5273769f717441124f09e9213eebc1c6dd453d58329748864961f95a9fd86a68cdda4dc4bbbd042cbb3a22bf99f0f1e1cd6d77edc11c8ca5fc584750699049f2fa5f223888516768326d834b9b731857b3aec1ccbb316b4b0d24763f03b22789478309d2b46f06d8a99ad43be5c67f0abad8f98660e32a74ec2e762a3f2829e573641045422500ec78f8c5f72a492ce70adda1068038fa3411f83cc1930ada1ff87f805d77da0612535198ed610cc16bdb09e1c56c9975f98cd33b1a22b4a220e7e9d3fba348613486a15e4374686d62fd8ea14e051fb35d1126b9c72d1a39c2187c64b83fef0962622bfff8581514ea60bf1850e3ada5a030e79097c7d5eeee5a849b0553a70490698d0b0e52f716582e2dc06e7fadf0f1f68eb51cadf2902b41fae88d5d6c6ff0c728e6da82af158aacdec2b9da7d9e1cb2d529516630fea9677658bef93c01d668b8802156c36dc4f2abf39ba6f78685aeaa372e801886ead6f80ef5afa40cde343ec97874ca4bc69d449af4cb08aedcdbe90a147faf4ad400b96ce4bd222c4a159f0fc019313b7347ed594d72a52f586db7cfa3b28575bb6b85fcbc4d203d06009ab9e4fe9d744bca3fb2e0a21b615b8670d037e01c1caddd1d196eb1fa19a282f54ef675b00d076973aa8d710768bdf7cd3ff85a4a757ce188601a7c083082c057155f15c304ba61701ad98d869627623d3499bad117d3700217a678ce44f4b258dd41e8ff560425969cec25d31d07fe311a0820eb84aadfc4ce89cc572b2c387fca80709a39c8537d26d654e856b404dc483a1f568fe85e11600c001b0c66f6e61c92842399a2b3ce0ccf8562c6ee8a96e674b8bef7d47e335abafed600"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9cc2aafdc3ac7d681aa36212e34c2d53d6dac5fa560e79e5d9ec13c36d352603","proof":"6a3a7533cbeb5a6894ff3544f2478742da20930fa6a3b46ea3a43da16f7d0c11120a44c3a208af88148c975fcd0b2119868ceb098d7f493b38144edcb2ae934800112a9b8705229f0097389fd0534d1cc62d510204306c4c749b71095154e44802219b09031ab75f11f89ca72da3305306105aeafd53f7629f06b1316900f61da2bab7c53df06a0e05bd196c922afd4f335a4598d107fa3fd3ae837b6a857e0060c9c3ea5f0dba2402ed5b0846899de7606762c269fae2a87fe0f075bdff6c0c2fae2489b2e123ecb113f5292714bf1c46dfd6b197973ec2f68b1e423b773901d6f0cdd207c791600517adf65e708549857d45179c640a69f8d0fa1eead1cc59be14e8567ebc7457402c421163307c3710752d11e6c486fa4753cadc48f1725fa6f362df0dc5630345fbb8b4cc33c4382ed42cdcd91ff9b919e46f16ddfcb1482a2d7e8adac6a927c56ab2ddf41c8199de16b5df586380b0cf93dd501a6a4f1b6882fa004586c17e4012968ea643f302a47e62a28b7c8b503d0acabe6e1e1378287bf8d73fc6753bc292d9806676615377afcb391bf1961c1cece528ec041341d02b69c714174fcea286be46b6b31d049375a8f76167536905eb886ce70e6a16dc5cf1415935cec4be3515760bed9fa90aadbfe228718a6a854a3f77b70d906dfe9b39fe8490baebb826bcaf0195c0db84570f8a2128799b32e0811e854ee67fe65804724d6317b55e358da785baa8449d02c7e04ccc5ad0ea60ca6ae121490b2c0b72f4d80fb27355a76f37235660b24c4ecd34a92ca6fd1c2eab315c41cf6c2a1ef794f4bfda4676497b5e6b9a87bfb5e773962bf0c32c82254321418962341f5c6b43e22a97b0d4224a305f9793eca9f36b80869957479da42c2868f49b05c61a5910222830fe0ac5d8ed4aec8de151fea005e0b86661c3b685c3c20da000"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"923013bba531b83b73546c3223b19fa9ef213ee93d2487157825f636a3313f5f","proof":"82b14b51405e98a1fc5d13596ae424bce3f1f597569759de4eb973dd66a51e6bd86bfe36ae242d363e0184e7134d60e88a4264a4502ff4b2cdb59afb08f85c10d41a2a2d3f8f29ef4282c2a364da7794b700e738a2b17843eeedde2b31d29d64e2745e32077de50b4b2811b1af986e068752a6680b9ac772c4c688574f5f947870cd536bb5afa69865828cc8e855a7c9b643954804c4204c207dad92de499a09eed244ac1e607ff3c37c8928a0aa0f44ebde5b440b547c3e81a16fac5ba5ab049bffbde83372af93f64b9f7a0fef568b4466c08692388f08f55fee13e9e87207c8025539b234992e4be8c21554c2313828ec1c143a1fe4a989d9a58d4df1a64ac8e68acaba6e06928355b8bf465b9997e866046bf56ca848acb3bed432e22329c0aa9bcd552db3952bc5027004e995d6a69b2536a282f16e91e928dcb1d35c4754f821eae1370a94c9280764f94c3ea7e1fae97937bb80df398404afe5b4834dfe7c36df0ff1e2bee40668c4460aa07e852af6d636032a4911283f3967cbc9019ae56a16c9f197be6ebccfe86ece43be72fdfa3a96438dff3136ba3e175a8f7dece10eb86d5a1bdd87a33238bb5e7e8278a9a5b3d78c391697eea7ae3389de40105e72fa109c569333f73f806b46823d3e5b6e683a51d92cb1a3011866da4b6f6071cdca655d01eb111e0f08cb22db7b297bcbedc4c2cadb189e62b024d6a63c48b9c1cba1a6ea2b67115ea0fac24f2a288bc4a27be142f6f5147c8a53dca369aa76455e6aad1b68f9e4dfb9f2c589f914b2028ab2a895a4f4c5767b184e9d431696b5bdaee6fe0a44c36bc1178411c1aaedb3ce605e87a5979706af40c7bf180782e5b91c75bab795e13eaeb098319408dcfbfc01af7e9dd6b1145175530709843f07dc6dd1d2af4bb9785c138b4e95a7f0a95627626288bee046a2fa812209"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1451cf4e6fffd13e17d93b8b302f719d4f29c4be1d4a3da8e4ca6e505e5dff21","proof":"82afdcea449cb45d2497fc05276912e6473c275fca34feced8de12a22452211d7874a9200ed132107b5d572368fa10e5acca299cf032b420aae9ea52b5abaf4f9cf467fd88ac738638ddd643c8211a5c1843a7bbd4646ad2f18f9ad5b0e0de0c527b931a298309a938aa6859c56aab95523429c87d787a5ab84246022621397d26c22c69a4a81b7fa9f3bcae563130e8876c9ceaba77243f2f78b46cda0690027ee31a3cd469d405a4ad8baa44f7f6e121589c1ba6e5579c3ff23f47072e560a9106f96c561fad4953bf576130f9efaaa9c3a9686cf271cfec292fe809a7cf07d0271c3fe798add4fe0bbf8451941dd8d3532ed5f7c109042fc5c78d7470c55c88057a72cacbedc3b491bb66b1307891f30501517b72cebd46d99d9984230c49028d8960a21785ffdfa41b0c3c5ab944339a6605b8854e9f5f845a3b93439e4fd83ec7ff3bd6c14b3a6ff9d814fcd45c135eb6262426bd6808c6d5e34efcfe0d062ec69291a90c2b14ca8e7f874610f43e5518e8d31ba64fefa9c72ef7c9bb6c2afdce31962916f8a6d2d1a3ac06c8568b85723438421b5dc7071e22a2b1f317c0e59b7c5b87ae752d6c6f9d0046eb93ba4a64787694ad9609882c64944e5b1c58725e7272d7a1502e69e7298127ab1ea44f35bae786d05019ffe1114029ea3f28c20ff79d52fb313e831bdc42443e9214b758157e7cd15a492df9b05a43cb7eca0cbaf8e4e1736752d85628103fb94c797d46447e8e277b0f0af24b00a7da5b62be78315e51bf626935bb7165d6bd1af8df14d1fbc529773c7986bee202213e063ec4f88cdfa52c46b28a7ff6159dd5d3af02a5290ce411add94c3038ac7d0812894efd2c21d3345554ac6473bee3cd57ed5d0030518d84eaf37b566f6b9504401dfdc75eb28c6ab9f77cb143ab0c2f37aa50939ac3828c8c1cb0028cebeb00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"30819d15d0a7a396976140e92dacecb1a032fd26d5ebddf0797519b7a781404d","proof":"6cb7d7b91520a382a16ccece45cdd14fcb5696d0a42fe50886a2fb6d1c032e7340d9ed232e1c7fa37736ddd15c5bde82943e0c6d1bb0f48d07d7fd9b968498069ece0c896fd243072d80758e8614f88c6a1437cb32cf24dbd54f820df4648571040321d4098b30dcc1db01d7232fc00fcfe2833f7ff6c7a7c5c757ae03c1b97aacae0429998241b0a04b621eefd0ce2e64a02c6f582b45d0b22cd1dad729360ab59804b95653f985172d5841301a66b7e4deae2a110918663c00e271247d6b05e92c8062f2d4de49241c7b619bb85e86a458a81e3ef8beb1334bfd123d64570d500f8665e915b95a5fdafe7e1dfcfd722df5e302db733cac756824d3d8197a0cde86dd2de5ef74e7d4bd0618ede50644cb0a4f29b416e371867a678292fd1774fc658cd8004f29955e3443611ba2e5419b86b93fbd07942f259ada7a64f8c50a1c632560b8f52a2af837382f0b6676a435cc80c1184fa4451f47cb4a149b7b763e48f3e7defa6360b5408e24333ddaea0ba7205411570f4902341a4609b377051a5f49704b3ae18095c66e700076dd2b7a97a6da31eaccc2341d5ef57be16b2a7e0633c55f489f5705cab3cda2eeb3a0a0a537aae9f0a74501aba2773baa412652169de03594003f8186a905236ea552b310f97c12f89d1b085b52f6b6c1555134b1a08cef0755d8036f43a1fcf3a11ce27918f9a216838102153251a0b722077e172c6385ca1d31c23385de2340c34abf37163cc81f3c0815a0deecc3a8367894eeb1b76c400e49ccfdde1057824a28dea0a649ef3c5fabda98dd1ad7fdd56e24fba6836bf3121a1192ab970845262bd0edcfd466f7d69887e875b56662c27ba254d12dd9bdc3575b843f1faf3572c38ce31424697ade801636c5b0beca3e00abaeb72da57425ce6929908421ac2aaa676f83a832843d2a0dd1b643ec6d2002"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2409edaa2eb8760a02707ae09c7b1f37def71c7b0a4a62d303caeabcf5b5ab34","proof":"fa6b74660cbb7a16e5c4d5569ae4ee5d5124f734b3e51a74237683110d94021b5206db63332e64ba99eb526ebf23dbf50130b4e0065c04a560b33c7076d9296b6ee7eb040230fc584baf65ecba43c593e3b688d01bc4ebb7a9e5197c8b3759023893e46005a52e1eab8b156d8cb8eca8ad3e33cc3c7fb5225b1020cb9aca54018253124b2ee94414fea018d19c6d4e14e58d09ff3ca65121b7694e2eda78e406f9f01a5ca752fecb4ca4b0575b0927b67737cb340b79071040c742245489e90a67baf430a0cb8ec4fc7d5bb4abeabc2150d61d05549041f2abf180d16cd96108b2b034e4a08ff2a2b618cfb1622da40b37750f1ae88c762f54246056d5734b5700b4831b8597f229149ada9ebe182f385665fd4ba1be71eacaed35ea4afc394b6af1eb85b8c606402ea5f797503242a97014331a3f1afa5b27ca148929034a6adafddaadb0e0474df945c24480dc1842b8b0be40fa2aa4a95bc6e8d387ce3107c655e0c3870864842e43ae96ca942965a9098ecf3ee4720113fcd661b37f3a4240fef7d9955b1fcd67bb3400aff2bf1cf0c879576b465353dd5a89c1a5b9f67bbe2ea9b90e248085edaf9741181a458525902841a8a6cf9a594fe9af831c07799a8b31f2958b7ef0d9c686ae404a8032975804f76f24e16949e16714cf359d36c269f43506007c7072bfd86412c126978f3ae1bd191433ba3caf358a8ab2e0546a4c67fd88da8ab5d78f47cab76e9c80100ec5e59ea1ec137ac658e137bccd4d704260dd2293c38761b48e217398e2a3346f4c5dec1ae00650872c5056aeef51be834c8f2a39ad1d0774f764318dc4793b840ffe466d86c663b523981fdbc4705c74e5a0b74852e64216be3c9dec43b488fa2dcc5d5b744f6ba4752a8355130f8fce4d46005309c342c2dd398b83da9aea8ea27ffb651c5347aa33b42a87850f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6436c81ccccfeb2d990c5920ae8407af2ac1a6eb8c7e5a61f3e3baa1d4c02c09","proof":"b485bc61eda36db62f810dbf319a7db9ab8a98869f38d0279e9f74ee38bd1c2834c12f183d761d8b34e173d19caa41565f6ebc3b0f8f8513bbabf9cc776eb145c090271f37ce334fc496eb989a36eb6d90f56ffe4dd2537f69c9b2e2c5242b03a6a57b80a64b9d48aa68dec0259638ce3cc8fddf9c8247cb0456b52d3fbef00a2a9ec3291a23f4db6e1a051908921889561b9f40a73c7480513806fd26f7190ac3333822e77cb869d0a202a236b8af4ae5c3690d28f3199e7332d96fab7cdc096f177267416259f4e53fdd26fb3279cb6dc292380dc7642a037fe3c4d438990ef21110f118127a375ecfa7b04cfe4438cff9c9821b9a2462685ff28d33ea381124eb1c55f9d22912abec0ba6640d3866fdd0d5aea39e7b7f269cb8e251e8eb739602a070ca07e73cd06336eade726f1b18f1b109f2b5514933982be56167bd13a451bcbda80222ca652638b4fb4290784e40f8bc5a410a72dae69f38dc359f7a4436a9c2ddfb4baecbea7cf8031796207723e2f81e5ec970840c58a762ebbc78c4871e23333afc45e1f013d6297854414e9adc92d8b0786ff76209dcab71c92ca2049e4d353946fee40a6b7e82d2e3dc42bdbfe1a7497ae01bcc2c52cfdb081afc5ad6c933bff55719661f837ae0889d329676f2064e7d623725250c6e9db138e8ecb15b47c16b3e23e4db74e3b24ea140802d122501252e9fa77b7e32dcc52c3ee7c9c6f4404148a204ed9d5cbffb55dfdc1f598aba1b4d559d006d4cdefd2fcaba9b5f3f42c7ab469ab313dfba36d8f0f4850933be41c3e818a90aa2e2666e4e255308fddd99866b8827165425a1c8e1231dbfe0e3b78fff7f81498c60147024076a29dc4562d254cceb53b9376796e2ab702219a3c6df55225e2d38f2740c0d5a0aa33355cfe0936b88368ae819466d52dd9c492d82b9388d8fc2027aa203"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8e1b90da52c9c1bae2d366b84c87bb83268e8e217dc7e4c8a7be868156cea66b","proof":"dca88176763e9d374637e770cc18a8f23703d4763df7d90a6d7aa1423093de38a6a50c9f5b52d262e27ca46088ed3675ebfa815dd287dfdbfb25e2d22c0bfc000cc649d116c73b7e05a07ed80d1b43d966b34525510ca7981b48d508fc8a9117ce6e94eacdaef6747325d07feae6d22ecece7d509c2f352c78982dc1a43a17595b0544fb92bde196a8b7f65be7f55f9eac1a459d4878f72e46bf50fb736b4202827827d77bd24174887a4e9521d5609815cd0d1de6a0a52d11b13cba9b35500591d916a92ed50d84f8ca138d34022a54557e7ac30d3e0c634ca6a9ab0b00380aaa4f27d6540e89b5014d2f080e4dcd5961640add6bfd2639caa64d0042efeb385e14c6872a7dc389ed9b7dd77752373cc2db6b6cf6a107f1316edfebb2c356666afc19a23829118aa46bf5b81f212643a6a40dd9936c6bd0776b8df042802d2e0cb1b1a3593cf61aeb27443eddf57e807652eef5c8fa15b312d1a328a681fa44d61eeaf4247c60fea50a151f7a3abd4e04354544df7e271fe1b21a77ef27a760b43cba3f3a1c1f3605b4fd4bbb956e784cc4bd30af98271a4fd5089821644864e4acf0f6e79c79c26316a2c0080d44e006caacc49555962b84cea9d43660ab3622db87ba18935f199f58d7f3eda296704e4c6d2e7a0721f6fbe036bad029b56f223f39cd9353c7a6b4644aa006d47f3cf1b43561d12891d55b666c349846cd52b850aea3a03614b20147e884c3b48d8e704804f39c2a3a5b71ce646f5f6cf64a04d4fcbe4dd1e8a0345017d827c9d1b5ba6ebc570334bfe17686abafb1fc8a5a0c4e9c28131e1a5dd13084a2e1d99c2a78440d18f47b4509ce94a193c7d777314ab73528288320ad8b6a51621a14af94552b3b0ce30173bde5b4bf7f3048f100315715ae5af787612d95b6a0219f3571bf668d5738eb1ac0b6cae8af8a179f0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3c8e1ce6a4c040adfffaa02a59bc7ab47b94b121c995df98565a86662799fe09","proof":"ba46eb0835995b0128d02e2674335a8e4cffdd008f98d2594a2b94e8c7738b6dc2b775d93d2894300ced5647fd9606454ced83c0acfd076e723154b9f5db80112088222db9bb079506cdad6027d0dfe70961333fc78b7f3e3e4b2ef70ea5673b64bc0a96605ac9ee57cc310c98e3570a58f833f7d8ca6fbdb979a60e52d83d5bc1136e2249effa03782b7e9589eb9f7354f55bde5cc1083d4cbfbb8c0fd95f03ce2581af20d1984fd8b642b123307a68efe7ff49717fcb09dc7ae73de63a1a088093c91f408423158fd3e10a9207b738137458b2e1209f04d9c2653d242db50d38ad169abeedfc9eacf02a2706b6c036971afcbec9b07d1d0448f74ea9fd387d5423a4d01e77ae23cae8f34779ea711087f3f53e005034b3b73e1f903e9f412cbc00db943f008cbd854f78df54946480cd8016f4589352fe8ba2c3c564799a1dac3e0786b17dd9b6cc7d6b724b9800a507ea3e0e1346b1137b9a57d89fd05200f2ea0fc020072e2a5fa9c567ed0a8b13d416157b6d9acddd77e5ca467f3d812a8250ab0b1998b264e6d4a283d90cd572ba55e1294590c839b952882937744b0038775c63b5d079e3e5102afc2cfb4065866580a38070066a755396f70142f62dd802203a0f5aedb6eb4de8fc57d5e464986b2f54ab5636625a1bccd8b6f1960d1e47e6f56aca4a5ad45a4e393ab26bafcc67cc282ed47a95d142134127451334c85146d4c05e8c0f5bf74a18f0a1fdfe2f74dc3db256fed83981a9f9d458cb5b10c70ae9994c9166e2b06cf20ac6650b206a0cae1a53ab5b1eaba74a736ec5498815091595989dc9b0bdc1a4ccad7239be4f102cccaba7cb924baa679134fe4adbbeaa89fc5ded6cbd475bd0371c0feb008566e688c6dd1b0ff3e95593d76f0259928075f001279c500b2faecdea0ee8aad48a38c58c66a56f7bbba7e3513007"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cef81229264702fcbb4a78f3ce80437e9389ccd5d89c9994784032f4958a541d","proof":"eeb3e57c731f7e45a256229b6f1ce5b8fa88ce0682f6f5c4eff8c983b3307e42ac435fcc03b1d60962d25b9e240bd47127e207831b2045c36c2285224fffcb5dae6588e669760447e15499f0db22dfd9def539519e6682fa7d971af47c79b01d34af287f5007409bc621622c2c749c990fa6228bee2d484f29e826898216b61f9411990b2cc7991c6038f5c47925ef13c5161b84dde81447b6f445964d3eb3011e5542198bf87a1b6066e2623ac30b00fc94f57031b359411f4aaf9fd4cd0201ce96811653aa2f96851518939b82811c64557d7a3a23987e7c754250a3a90609908c23637f8960bb8d5e1593fa3274a95060ef8384c5eca7fd924b0baee16b0b04f9888fc56284fab4c07bb79dd8fb6578d2687dca2f7ce355727c886e509427d487dce709cd32a209bc643f66062f74aa6c0b8d0762bcb7ba31464758b8d21d72d1aa1b8b937316c3f35c37f8289f3b91842a69291afa5b913a7b6d61bde404f2d517dfd57095735291361603dd26623894bda6109d74dba39c01c3d0171727c2b21d4a8a82ea6152c53fe05ff775da55a1920f9b5c8fdbcc7b1020cd2bd23918d0521ff367e8217c0d393f226c5748c25ef2e0b60cc794e54b71c078733449aa044a68c200e08e235fa09be014f7936743c92d66e2dd70c30fed5035ac1220961e85ca2aa04483dcd9954044eb64585d2d87ac0b54a04c317df2d160cac12b04571f79661525c51026d918e90bbe4b27902febab1c158eb9356d95e0397e1ae63cc4564b2eeb0f42dab40b995ae130d3e2b3eb340d4d95fd809725faf16111c2ccdcc3ddd9f633423b3afdb2498e0bee12e8b0fc00b51a3b74c74c3498db260718b6fec7df12d02af21ae3604f61bcbf7e83b10f28fcc70cd4c1ff587f4a02c19c1c281d656fa7c6d870ab5befd2640d26c39dbf8b99f540ca1deb86ef810a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ae9c575d6c3a9eec8d8eb253ba8d8c0ca432e13208b9e7164d7021e47714e734","proof":"f00b0c6302e587dfaa3ba58f0887546f8f4394bf5cc69a7706139293eec8ea62a4f463be7add4930ad7e7ec43cee3da96cc3aa40b3152281abf879add8fd34116a31d6d061f5b05bc696d05e68c871b1f44526173bc84c8b268fc562060c294706c57ce28d252b6fe07e1720e21ef8750aff6a1b370c4446b2a96a2e0157476a56d0b466a87389eb297a82789508b38ce1e5c6c7939bf0523e35de23dcbd9002e61d8ae06e0746184f8c61ed51bd3cc91578b7364c5565def01a6007bf14150c30c82b13bc827b9d6c76f295de83103986bdc2b6583b295d35f7d7fc46e31d022ebc658146ca5d560c39b997c9dd17cefa4bfec163957fd9acee2eebb90ea40c4c2c11dc38e8f164a27d4d345e5bc8ed95a6c0d5997f9d4614e0f1dbe1be6e17020f6d210354dcefb10156816d450d7c7904b55f1bf4ddf09342b659898bf1010a3e207ac8fd283776238afe0a8b7685ca0f0869883ddfd975922a5fe118685da6d28db3e1c2ec26b83ba303006d08c254959035cdb7debf4068f28140c2c9415e5de6f0e06cba2f0ae0012e7e15347b4624e13ec8bd08647480007036c5cc31ac8b751bb17baa144988ceaee1efbd04dc7b7b7c108f47f738d452cd54aa5f3f587183c0211530b0ccfab6f60de8346741c9ab82fad4ba6a348dfb3220bd33144a26edf99bbcbd18cd58e64731a4dfac160e12ef63db7c3e140b988b1e4a745846b44653a01a49ebf89177ce22cac82f4a67af13c024c85509d39002e6c4034bcc8183688dd27f62c7b414757b5c36e919907b00de603dd2f85c65db6a22910c369dd344bcfb0a701b498bf8d35084963a3b4ecad305894e8df079d58bf555143be867ae7f0def3fc1e9c9e33c1ee4ed17756db83a99f707cf9932bb9ce1ae05a9990af2d97381fe7bba75dd7f838b75d5d306e581c4235d1915b42df8eaef05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2409df2d8d1c3e826a3d7edf3559fbdf53e83ab42d5490e0ee7b4fb53d27be4b","proof":"524b930fb33fc172d2cfc28caf51fdf14a98579200b5bc3999a435d692997a5240c26a6c25dc9120ccd698a0f7eb9ec6b8bb0f4863af462a054978fa4b7c773f60d8630870163117c864d9b34e56dd344f1f4f2226681af8bb6f5902237b126e0cc842deb5985203d1d2930a88df5d6a556537831d5acdecdc323475da56f26888eab5ef3cdb51977fd8252bdf3c253026a07891d75b2ccb6cfceff1bbbb8e07c53956e59d838fc1c86ae74d5bb9c90288b4cf11e6c709561fb3eb4bcfe62a0b5671743b34257f3ff32007ee9461462330d336a2ba58d464575fbb4b7c759605963d0768ac479b254e138ebef18db3748b8aa3aa76a83228b001449ffe6c8e5f5cdf20b5d6fbfb8fca2773718d00541b5b953da3754c2d10326c78f7eaf18d34ca176baf53d5f9a4a0e599c2098786a389d2ce54df7ff6e9b2c9aac7cb96417548f49dbe0eeae0f977ebeba3ce43925d66cf5420923d80786257ce0dca44a23d446cee14b41ed4e39b34020d6d3e0f7255b267311d33d3ffb4f7ada6e8d730763ede5d5a260d10dfe2b6a904fd15c3d8d3299fa0e90086242c44effa5c8ce41e663b13d79f07334963cd03c12bf00f4a6e3598b0d79bb26206cd1e5163915e106c25eb08b029871770e2920a3c100574b4247cf6aba5df2074aee979b1d4e94d20b3f16790b6362f67521647b555b2a710bdab8710a9ccd182b46df482a13c4cb01e1dd0f00d8c534596722b65773577ea2a4bca976472988a3b6f80123dca3bf4d444b3c6af07a2d02c7dfd625ae3783847e8a523638d43b5c3748f746cae398ed4a25546e3e0b00da2997c55542135bdc31bfb984f8ac937dad345c4dcd373633fd57338734a832723e4ede4614b9644f84bf3573d5466720400017e0be20b2085b0277fff07c2f9f269734c15c2760630d9ff83818a128a87995981ab9f07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"58f4a14dc50112aee61b42bd5cd80352a9d5a9c64128ec620772f95840dea044","proof":"e8943ddc6fa714c5ae0e86355b123ad6ad265479878a33c89429948dad10e43ad0c5c7d6755feb02eec288928682132a6b930fc9a28c2f06e713b035b693794818f7132c2804c192a1bbe2fe6bd85d4133fbc7d7c7bece560a688c13837836535eb56bd6618a847ff185c661c6a3a90008a97ce5789105c6ddc8b15dbcbc3c12d7f095f913b1bedbbe85fe5d5580fd3d4283c574d31a33823b90619c4f6fb407b36633de3199a89c62455cab85a5fde58d0d0df971c29882d902196b3bced704fc8c887e8bf0f7dafc210bdcd4754b4d68a8af14cc3a1e9f6cdaba3f1164e109e255f26fdc9ba31cf1851cbd1959d9a15b75eedb913c62eb4bae2462c5df8059a62558b343c0e73cec0f98be9a67521686e05bfcd3a8f61bdd40766385bf3613c6c50e10b3f64ec04a91585bae78337b68ac7925a75fccfbbc9f63e440cced5c4082606d67d6ef6c6e2b99384eb6fd2b176e8aa0d71dc26351bf5352ed297e06123208dd82855650d1f2df1b1d55b3cb3eed58dff6ac2a475d48fc2ea0a0b2051a9410139028fb6a6cf678a0d1ebc4da7e8de21b71d4af71e8d7782c3d93cc2b5a4f3b1b627783399b5973e04ffbde36a28dd49d754aa2111524c2103c3fcb04f04a8ecabd694a378e3068799e743090ea3a972248ee48f4e8af11d9701625101042c9bb1c129e713972b83c2c43ed98ec97c9bb004f8910c1fdf7ba08e98313e4c65e0d47563a0a53fda917bd98f8b5f26910f1b24086a00018f97d2a749529820f81940878de37f118e7231799f97c35e646052cb33b1f7aa2fb3edd176728d6e0d1d17869e224b4bb3578eed9ded963edd67dd9b098f239fefedd4b7af201cf12f111783d10c222bcff8b5cd7d0dc310bef3edfd7cc227be194c29bd45f021cd0d165873104ca19bac59482a3732684f283df8374e6ec0df7e31e9e633c02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"96d0c0c596cfb7425b9cb515378b6200176dd199bafcc562d62d41f5e3dc2470","proof":"82c612f8cd3eb189734b9e6df66927b876cc1e226e7624e55a9c76ba98717f596428ad6f0be674655eca6c64c77eacd4a1d9dd018d8f879d8e9bfcfa9fff84008af2def776e91448d604d63264bc0c2617c0f8fa3fb4f323888a37576051630b44f99a3b7de14f490fbf6aa88b2820ebdc25c11aae5c5b8034e184aa0f84ec1d19673c4923ddc9c6126b38cbd7e78bba0fa83c42f6fac67946f0168016f0d50b48f990a935a287f0eaa7891749a96e1868b2cf2831a0c5176d4cae3f9d2750056cd429af4e07871c026e6285d7e89a8c37e6f3853caa6036fcd552843b9c2b0776ab6d22828d0f93381b26d0392c84a9793a80a68ea34de86cb658dd653be3795e948c05d583d54278a84c308984dde9302ba617ea10966c79edd60de41f7655ccc9afa2af0740da18d393f54facddeb110540c5b2ab870bb7f0165e364dc403bc1c63cf6d94d634d1de0172e5868ba25d734c4cd8ab51f3de4b1eb64e40fd06de185bbed79021a1200c1a8a26f732fd91e1bf057feb18c4547c6fe6a82dbc704266790f11a0a12698b0e74c7b2a7a4920a93b78effe039f4188ce14e62f3f30ccb07f64c44b6c052528dc5d6a56099234cd8e4aab7eb1ec189788d46ce8107c9259be69910d7ddc613010dea6388dedd8cd4f1f68d1ed44250b24515437d77ec04fb0c0b06ade0d1befc30046570628da836846fab8ff8442646b9d121519440812dc1bc5fd1ebf44287ea5aac397daa8e1645bb7cd9704238ea8f829aa79336ebdfb6031c9fd7d78d0044dad92d6e345984a894f8cd25af2730840c2b8ff27d6e80e0b25a5aeb4ac13d12b1d72711d37fc1d7903b066259d19d5fefc914043424b0b9e88acf01cb764cfb162f79242f256298701b796ef20f42c4cadb1ec0ddca4cabc5c0ed26329fb5e98cd4c436eca0b4f392dd9866f3d07b2a726039a03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"222fe398d70e63ed5c5b83da08b50ef1daa12e6fabb314e3518debbf141c3c1a","proof":"04d5efc30218d2ccf017776fcd2a87f104860f9497849018380a147d058add3fecba1735545cb7bd9e4c21c88090dc899e9d1aec393714122e228552f433173fa4ef02607e05d83fddaf3d68839f20b74f3dec20474e6ba7e3b86b19027371187ca0db77852549f15130427955a096d1520726e07fb298fd7db80d3037a0ab4d61c42b0bb65f90e02eb7f735b642849f269f51f4233ddbebe7a9e7f477d0d401f8addbb26b2f5a286affffef1dc53b2c756f76ccac5b1199982ed9455478b904e6e499d8c351aecd0726988813e41454b232729ef153e0e9d8495badf1ad970a4628bc000ac1b8319e8bcc31f2600539b02a8227495e68c3bf25848e7893fa095ec483c22ab24595b47904cfc9f78308e0469f9c8c3fbbefaabe77667d61fd2784a21727240916fb9120a92afe0255bdc427d63ce079d374ebfca5418082102c76b480a24f1ed76579a3d12302dcfcb333b9a7ca0d87c04ffef7a3cfdfde7f0d7cc7d16e95aba3169d4e6610968159ff1ab8c9aabbd074f22380f87e415d8268f068adfde5ec4602ad8e8ae07999bf5148c2ba90cbc86a889c2639ee286ebd1d98ed8aabf5536cf092c61422e5cace9727b4d9dafe5b858217cd8a193ecde33b9807c9477ec7bb37028adafd64bc3faac2ad248594062d89bdb0c6eaa7c82e376aee66f936389b43c22ec31fff6ff58a1fed66e808b7bb02bbaaab1c261c5a1d3a8715da59ca003ae60e776bfc991d8df044b83448b8d5bb780f8cc34d4bd36dba9f1bb1d4be4c348fe5eb2537e0a43df0e58af0181a4046d01f63f3dc6b3f3d562a8755975b7f709c1b68124335678a63798819338dfd43e2422dcbcb2e282daecde925cf0eaa3910700a10e6f5351a54410c10a4a5b858681546c7a1fb000ddf57534ecd9c0b46d0f125cda7d8613dea5463db2c2f9687e46e6aefa6420901"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0e179d23a0fb193a206fec04a8e4b0c9e13d218c2bdf89e0f23aeabee6f73078","proof":"0e3cc9fb30444685c3594210eaa4111ecd0d773b650a3b33df71a87088c9575c78de93fd43d393840528cd3a9325729e3b8ab2420afb981723b608f4c19ca773e0c1f1d5ce69ec2b2ff7de9b44223333f31c6cfe55ab95dbd2e5f2396365d31636a7ead4c8cd847fbb15e439c52d5ecd88a33ecaa75457c78541ed3646c7a047c385df4d69328a690483378c4cfa0e2c24996320260e7bdf1f92ec389f6aeb00d9aed4d26a1db7ad8f8790600dc461cfe38fc1b1cf37ef8aacbea96425fc3c0d60340e4b09f80f16428951a9780e12cde148cf44d06a32a9b6c7844807f2430e9cebf77f60b15f838615cffaec2975ca766a3b4b4039828b01a22014a6b9b14d44fdebb317352636f9128fd3fa455c1e956f8a5aa83ecac4598e0b1be1e5de374a5d18adf7314432a4a7ff43cdb151036d70b15c13ba93ccde9d9012473de255da49ad1a95bdb1068cae2bd7bac90cb7d46c96c1bfdb2cb07505897e60b8f018fa2cfd0a78a38b2988ed3aaa305b657c9e6ad4b6153ff5d4f15e73bbdae29b023c89be2edec08f95c5ca8e6f6e055b8fa41fe950e0d10991b21c2c54cf295810a6373611d90ab59d54b6f4c57223280c7d9e772f9dd48199749e6bd087e27b2c349504b53c9197e542a158c837ccfd37c1555e9da2128b4120815e6d0733b742dc8b5ea7d4f05780f951ee3ac6434c9c97bb038d844d22b373d38f5d607ddf45e2b5e8327693a5e504534158b076ccf96fa03c7d7e808fbb4bc62838446a97756c51ec4852418b671aa1633d1b34d58d82be57865ff11fac3ebd524e0990a2064ece21e50c48530650117765d6009062609d6cdeb747171d26099639ad8a0000271ba495060721d82c801e9dc35eaae69e32d968354fdf587f4cea4d031f610dce34eb3cb656750461b24080d722e4afb39988c245f8c18e94e61b281d57b10e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2ae9d96619eba9eec6fddc94474abd567480f5d928dc0c939df2ac5cdaf86909","proof":"941e67b1f13e1ca79e54f35c37e4e619aab4240241d7b01dd4a8634d6f5db144d812e03f6b092903e98331c1ed72b1b9cc0f58b709af5d8cc0093e00a7542513a0517bb0c67b6111cb373d83b0bfd2a80be751efcb1e08aadbab16233a1d9e41729b7a6574ef8eb20683ec8a60ab4743ae3f55b375d90ec83113556a1794cb166ce4eaf6facfa1f9e8adbee3a7b06f5ff622d7efdb0b57373ae80182f926c90bda35581d94434bebb637899397417d25e4e3c17fdfd14875e491384d20290204e53ed0bec99f2dc35d286689e9d4d19982357547ae35ab8a9e17ca56b8916b03401060e2cce2874b0ea4e8fafe5d060856540b433ab2f9f7c707c1db6eb4ca2b28e748bbf8a01b42ac29d061c8bef16e6b7ceac0a30375c701749350d5e1fe2566dc2a98f965caae9ccda9edc286546c68cb3686c3d1c6facf1d08051a20883e341a8e604d924f02b3500ccfcf26547dc477c12c0e9e614f3ea1f5d64d6fa07586cefbfeffb3ce5383f31866421659bac3eab894aeebed6b76d58fada2f96e407425f5d50b66827e2a7a9a2266b41e628601a9517c2d3a2eb7fdce3bae36c0011e6d697784bc226f4e5885d2881dd52087b7e589b6b42b456ae15388379c433e1a334ed13ba89acd70ab41148c4d991d2298dba94b68084a647ed4fc5e03c166be40ea11ae5bfac33cbfec62619805dee1fce66cc57956e415f75abfa54a5b0358352619e9ff1959c927036e2f355263f7da2bcee7a14dc2048984632f6a8b73945c897a6bbd13b78d7c4b240dc7fb12746ef926474da14acb0436e57c9f0a1b9a54e646d5deaf646c69a6135014420f4b6d72d15c4f766bc9d64bf1e29a31081126088df3c360929c9c9884e335df562fda85110d39230ee48a9e41beea7207a39d67e6ac2ec3c6d74fb588550f8c20e8f14c552c15962c3fe7ad6481f61700"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d60f35b18859647b0fb57f2ee4a4300fbe8650f9e09b456fcc0fce1114ee5b2a","proof":"5479b7833b41bcc744c3aaba758c85fe485844caed81820121f05d384194034c84595218a0cab64ad94f438c9a26803435ae3f30dbe567f7dde50c6b2b3757641cfdeb0a80cea50607cf551d992ad143b8fb33bb48453e6d2a29451e25bfed121cb1527df16591fc83cc156a3ecdb23834446318f4768373967a354a6ce5a233521452c486fc8a177b90e452c0ca92f668cb4a3aba6a97f4cc9dffb3052a9e0d5d4a19be3c92370a7d9aef4d07e088b88fec7e628dca0e6dc3137d4be5d6e9088e57bbef253cfe30c45877fde48a6a8c3b789602fdcf0ec451edb951f6b3020082c10320d02d6df403fd88dcfed5b9ee852eb7d94e253e2ebf6d969aa28e3d23089685c907471f2f9a85a3bf72afe92324f1af2306b775f75f17803d2871170cd250cefdf0364a55b3d66fa6893d001c80808abdd0aa3403ad546457dea8b7056c3164b4584ebe11d2041ee27baf66f7da36021d334ac30c3e599bca91dc0b732c71a57eb8ac30fbe7294ae0b18dbb2e9b46411cfa2cc062206417973b637361ce63899f15850bd86f76c28e44d941a030c083143904e90ca3d157f196635e61287754594454e7f5be64a8d5f8ef7fc500ec85b1933c7e4cec236bdaf2a70f5dd67eeef096cc89cdcc6ffd5f10c5807dad9eafd01d6ae05ac0b49d1e460e7c46565d0c64bc1166b86adb8a0e0026ef0c847480ba9805cfd0017b7f28f27f5d7ff48403205051ae026f960efd77d19b574b62fae285e9c2a0fca822e48135f669d8db2d75dcaf378d454b9dc0d76c8995e7ce486ebd60fefbddd8403a19d9ef5dbcf8a5bcb3e01deed9579219368db23c63a42021bfc0bdbe8346aaca79751463fa63d1f997af4137f6db5475a461853c8278ff40a18d0b758078ff75485ac70f9c808da830bbc11846905cda86d25ddd4ad21dd56f0d8639d5b7c5c53df2000a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d28b96c1dfe9bdf9d442432aa5dbebc526051a89440c7c7e1639330d4b44d959","proof":"84ba90ed27a9dfbe55174912639813a847b466d26a17c57fd29805f93653cf56e291b290bb1963a1e8437bdcbc809a4182a11072089490d67372d3aecd92921a44cf93e6c19b19a3ab1a63b90fbcad229f3e83a7778ca5823eefa3ad8a21e202203d1789de52f1764a121eb3be01b2676c6e5c778871a0556d3b02274f3eb641719e5415bfbdcc143188415de76d90a25dbfb045e210548768b20fa4956d74033a8a64f3e5514b131c9a7622bcb31c4574a8fc5f8082284022eb5e9132136c03359792c06e78e039975f2433a1725083721a890da0688e9c90635e1a2cad1a0c008123a653bf2e1b2858a70fd8acdeedd2853ffa0fb238c6786930a34955661484b7b3ed148d12880437b8d1400219a274c74f88a92ddf02800309d09ecf843c56f627c27a646c53fa52152d4ef6420529f7f934603ca87cee9137f96010491524d1a467a95090b4b448e4abc1e151a05e61d9e05b63f9f6b41f854d5e22b93b74a3477ed6aa608a04a217620026c3ddb0e7959f917617c33bcbc46499dbd612da3af5f0a6963cbc4a30d6dbb09888724d32237d29e27b7ac07fd76dcd8caa1396e0cc60d4c6d07fb32eeb2fd1e7abef49bcf11b648dd33d1461e4a1b34de451d82cc40b7c764d7084f05d1bb531869bf340cf54f06ff71b5ac3955a438fe1236e58f1765e48f14f1689acecdcad32737bcb808fdcdef731bcdb6aecd76a3e6aa24a3ea9be942fb88fb910fd9a100fa2e9e09d8bc3a49a1bc6d49d24a8ba093412387299f72eee5949bbdc6608229b9240198d026df0560daa96d5eff62ddf7f304ff879056b8a8b8da48426b362d711365507737b3a4d8dff7c823ad96aa8226df4c303499957302359564938827e6b17697a918dcb0b1b8ea7eed48f5e98020929267bf11eda7d472743d83f33caac0f8293848d23e943c485e5d6af5ad900"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7a55ff9141f8fbf8e7ffc8bffec96415ccfcf65f3f362418d9e9600c12486b25","proof":"480b84290ee948b80eebe1e712d8418ff116d260f7a8978663ee296f97723c67be233b4d8eaed33bbdeaf88b89cb1d2550ad9eb27280d8a897c13a336f069566b6acf07444207db6c42447c9a62a931aa5d12cb4c4a540c545136a399c876124ce01c1926bc0ff4c4211802c5a0be7e47e7aa644b999058f17b35cf666cea55b1f24892397bd6c9dbd38051553e8910a807378ed7a9131ba66215abf8fe3770b1774aa3560c29ecc90d56ed49b52e586ff68a82c1e3590c9384a1b74c9c82c059b07859ed4997cd49c59df74eb04f2618031cfe69f61c2a07a953cc0038cd103b458696df7da58c1f52e4a9da64728b65d5e478c3a09591982ea819cdfc3dc001cc86244dce40d93b10018317aa693606dbea49ccb02259c6da73a7b8ad5f1756e5c8c566402633cee5b2191c72eabfc00dcebb0ab015a0697238d3fe12b117bce0ff1a749ef51b096bd2bac922caa338b46a04451c22c42f01c148753d58d350e2d4aa6ee42e112067f878a5ae69bbd6f985d32660b7fc48842c1b03e60b3701c58d5ad6952f44b4166f963cf73f1fcac7e045e5dad4798d217ebf644c94f38de1fc48378417608a64b993a1c6d28017281742454f02af6bd287c6180001f1d98243224294ee206079c0a62e3ce7da39377b5c2e1b93b17074ad4004be3537d7c76d2240c2c36ef75f04c079e88ad72668609a970568bd7eb0ceb37aac92b14328dbf574707c779093c38269042d3a569fec351680b0174f583eed054a48706fcb7d74e59afb15f717eb8bd7ed8db670735040b5055e58993a817fcb01e644f0ea9b743e2637c937cc2fdbec0f3ed8dc163bca0270a64eeb6e573bf4c27077929c2cbd70108c68309584a48725088d8942b46e4d6281f73e6a205c763c4d105db25fe1bd71b388e2aed4b493ccf49113b1a9e97c8a280ec91c828deb646b801"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2e95a42dbb90ae00c01f564c146161681a46fffd7c8f9c37b33e402a0fa9003c","proof":"9a213458577632145e794e5d2c00fbbf681eb99681dc6b26ccd597c3b34250428ef6bb87b48b9a6e6e86f093f01ffa6e8e0c1b7ce6bfcc1750c2eb6a193e59604cbf7a42dc7f7214e46cd24e7ac2254c05bd0dc4653d066c23d2fe800448d12a680c4f58d1f143db80eb5d95dc1d41b53292d3ba2c3e081c18898e063dcb785f9d4d2ae24873c5c5c973a8a49c82bb2e7d7d8f3a116d3de8e534460fc2b4a90bae9b54e73aa5865ee20cc8beb892faccc3708e09808a26033ae74b81fed2df0f60b9a5f90f5b1281d2ff084a286ba1e57a29ae15fe8395e51b716200542ed6011ce2791959f09831dfe4b1960fa53758c2ea621758d79f45a23a73e79281a65958823fb944daa119ebde0d284cf5654d36c75540ee03498754cf9d041c423a50569a1139cd5bb30efe234e24dec26aeb722399b78309635df69d1704b233516866d3a890f2534785cda0335a6798eefb683ba09f5446448a4449aac37304cf40d2b76fcff880e0bbf7af804816e8b4f9537234515667be3c92e2999c79313e73d61c684b19b7463fbbabb957aece7d8f02fb2d4edd9bf665c48ddb84c00bf87b8e3f224ef85e46ecc3b594b105e0cebc9bb729e340915c7108c68da0022aa2453cef5b356679043f2cd1a5f0ae6d48a938ae2723929ca785cc256f9887c6c8434084b60e8f63dbf3004ba955b1c0cdb3601d66afb1bd540171c688c4324a8137e67ac0bb3cf28da1a93cea323aed303bfb8784cc00ca77cd86391068d962a21c1c633d6f171f07a02cf34977048c9b396c2b1f0926997bdf1ddad96238d34b01f457166eccf3f93da699954373c69e2f108bd46c6522ace04077574a8a00e64916b519c70cc53f232e5dd3f99e2ee83ff0ab9d8c5e57a009ced106b1b33bc50c9f0a9b8c50a18d94aa3938f92f27ad52fb002f53430568dc150a2d1d64490c0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4aa7fc185e5ba76400a1158d9799b585f936408c53a3768e9d7ae8f46bc1f353","proof":"985f828077945c04de8f816419a09663425ff9b48d4f4d195bb5d8aadd5d2a681cb050ad33b65f9d051551a1a753893f857498c13ff51e88bd940ae10ed75e010cf4cc8a3646f86ec4b88362a5d4556ae8a71cea5537bec911474c4c01b2c0777668a8567ddc9f5ec27d28e27ef01207e8994e95ccacdeb837f9324d2703182ae0666a24198127dd4f25f4283a929066ba2ab95b211ee36ad4634e096bc76d0a8adc2fa46635c1785336b141bb158d3f76c811c338be02fe2140665543977d0685ad9c0c34d6a04359b2c26e0a6fd36ed698fca375b0a5d7c11470757b9ca90954b19c574742d14cdc0d0fc9d97a72bcb030483d6ff232bff853ce7724175c54ca88a3e329e2f880d8495b631a87f25df929ccac19214c624e467f56134d1a3b183c589bd4a5a616628e67e437d0dc38e0e9c6456dd203261e27d25a79de1c3c0c85c33abfc26a7f703cabf411d6ffd45d9f601f5a8165ecb32c170e582e6e4f08c04a66efb721dd50cc6190bb7eafee96e268c8a995b78e211572ad8b8c757694046d413f1933b9c85ceb8bf7e2f88da9d9a0471f4b0dd13b1c6cfe8e2cfd66f2fa6ff750619ed3b993c1510b73db15bc6971b79dfcfd11e3e2e3bf05a61427e67305a6c7dda0b22bbc73aab0bc646c03fee9f7b61285f0ed4e14f525e3ff666a0221e38326e8d9cffd750fbc8de369da1dc04ba56aafdd41b836257bde0014722fbd90768c32a8dbfb35e6375dac046fd10b36aaae01b2bd312a43097e9a7e0a8641796b21c5439c7f003f397961bd4500321597c2e72b4a2d2ade6e1eaa2fbcf8cb4a7b63adea88c8a1344494a9565557b4ed4dc75ab82bc61d592b89aa0f488f46d0b1fb92165fb8f0cdc7fcd0fec617b9e73b81573c8c013c094d9aea00bcba0fed7a00ecb13d5f9c68cd46eb7e84817a69f3e8945267382ed53b1a200b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c09579d9ee07400795b6155451a9e80565e9d7c806def8b7918a8a5beac83563","proof":"3831f9c84d361eee346576b9cbed116ef55d58b81cd18abee59483954376d96620ab21bf6f28f64d3dfd7468bd0d64ef03c895fdb2256b2c0f33250564e76c6c1a6f708304e1571fd6da0509364faa37eb8bde5dc064249ac35112590a05f928602b82b8f206624a3dbeff0a96bf77380596f6f01ace4f21b996d88bbf765735d3dd81f9eda75c912890a553b9c63f6b54dcda0517034c598ac57f29913ece00a5c1b30ffab1954204d3e79f7afa7429418f269234bb9ed4ede6f21a755fd600b62d1da722b08e80f482a5c6c35a149a0503d73077856e0b8d203638c90382088a79e722149b20406e11bacb1b3372235f39b5e5f3e4c9cbd50b525ef885f137cc97a3660792d9eadd7f8899f338212d3690b04b52017b1dba64f64b54c8666d1079e5eca317a682d644e8ac79d7bc9d6e03a26fd7c3f491594697a041e8655268948e2954e92970fa37469430c4590b6ce2a6cae01594fad040a0e21d263f7e1af8be0f275c12a4253544b0b9e82aadac3447cc2a40db1e943d4e4d448cbb0ad83a5948844e7b61248fabedff3853da48c3e8d0844f146409ca69a3c6e06b677e385cd2b71e837c4a677e7376c30225c8d2472c9dcb65db7ab67dddb884a80cb6d72f38f50c7e27423cc9251f198f411be16f3631f6e25d1c54fd8f9ad7776d72d6cd861635e8386b85c31b67dd57954c1b4e25c8c642e0bd8ccf2dddd25a147c38bcce063ec73900b2e1540cba7e56fe43a8a93d5d4c33606ae768489c052452aae3818d69ca6543f13778568eb12fe129b1c05c18d5095232d11cb885ad73ec253817703dea05abbc772d9529efa5931031d08d414dd13492b1e38f3a607ff61c5bca53702e5eb88486d2781f6be974c602ccd9e2f8bd3ca710e71fa3c706a62fc304dfa16e64bca25322a1f98e09c3248075f9e80963cf25f3e14d528500"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ea92becf3a66c21997b8b28af2466202769e4a976f57b0b26b4edc09aaaf243e","proof":"6eef61ff718f4a49bd87fb7fdd73722db7a6edcba54805f769d4f1488fcba967f6152069136e87f29cd1b8f4db48fac1e51801de47397f3fbd2678e3be96014ee62ccf12d5311d9dcef402e1414f98eceda743639f5645c5dbdb1c4f69941c459c2e689fa32800d8977d574de01687674c0aacc470a9ece177c38b239f261a766a483550c875734f0f0563f1708caa0d2fb6900487c00f05eea728e21ed39100f750e5c6aef88820101759ec7eee96ed69986092ddba00f3466f1153046d6e0eac8116bf3177095afd6e41468686b3082a0d2dbd47eb5395c39e6d623888f00d94a7d5548d7022080045fbc5b26a8b5df0eb785659541366d84b090c32a02452ae59596e98363557d8e0cc1b8840fc6d115109008e633ff71d442df5cd86e674ee05db74715bf0090639098446a036b8a28cf80a074d905284e074f021c89733ba4c71ce6b25e10cc8789cd3530dfce88625252edc61137bd05b7be57115723f7e896d4eaf829a71b42f47afd6d60c6cb82781316f1f87f17388b7f52d84d17128c7939dad181bdb396d83cfd4f969b2b6f714c94c33eeaf3f98bc519a2761113ce7aa26868d3fdc455765944d34203f9baa3555a62135aa7abaf94974d1650e586a700f19e51f75a38be704eafa493edf077c84b0fad1c6c176b0c555fdb34a5ecad81e20d33a752c34322b41bb67645c2a61c8a6866faf6eeb08087af25e558c0856e24a1feed71c6ba540de15915d21b21e5ae62a90e326168b5f04aa102b32915d5129c8616013adae01b9456b8486ce9b9f1ca0f35e0f8a723ac91d3a02debf89f11ed167506ebe63ed7312060b95fb633bdb2212834bd5ce1630d05a0d6c7511c55f8c9aa20ecc83cee72cb0c3a4bf958b14795277ae7732e6d1753b090558c1f822f3a667633edf558638b196946c907edaf5cfcdb0b870147fbb4003"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3093ff10561bde15d2592a29da79a8b7646f215284664fffbfd793fca3e3aa19","proof":"266ff8fa8a97c4822458b580fce8cab021f3753d65dce31359e23a2933f6e748b4066b59a831ea9da9c15960520f232519e3faa84e77ffdf50d83f69a984dc7e827fa93b91bfae7c7cc7287f458b4941ec0b41e29864a98726ad8b8ce52e37414e01de4368491eb6f89c1d2c78f85e81be654e409579ff560dc9f644d514307a3735eacc3019ec506ba33ee0a58ce9f162ebf727cdb7172e195a7651ea85db06a2bda222b9321bf52a41824c1eb626955bd28449eaa1c139c413786df479db0ff42aa1850eb6a6083330e62fcf775b65dcd483715f901021aeb4905bd751de0ececf0d8c6bf434cadcae9df1aa170894ce5d5c38d8d7fa6ac42ac466f703ef286253d6a93a1eb45ba10f2549d7ae2022a8cac66b9b0878e3c6f7e427ba62452cfceadeedfb842e90c88cb33627606e3d5cb87a8b5ac50d6ce064daad9fff2d59f610baed3938484542176058a8255f2d104e06e1a58b4bf43c35c6cfcc1e024128f507f5eac1d533cadfdd0277903d6095cad2fa625eabfc0d23cbe9785ab8379a1081ecf287c238176a093a78d8632e9ff57dbd990545b4babc534858542067aabae45456b309e9019049a89366ea10b204c09abca098dbd9e9762069c6711cf4f8557a2d38ee1ce5d179af14d76dbd80b60b8fd627abd91ecf6c6e3709da64520762538823d4e0fd03445035011b043dfb1c50760e9614610128a6d128fb26829d4547dd9544cce562307496f38663401ae27c238dd5c3739d5f3738e14e3836da3ab4371c94ac771efd6d852bc3310a072980c3147123d96cd86be8d7ce23c616131669a6835e0c7a1a32618ed81430a40b658fa8b2caf3efe457fb8e5426d277090d741fbb6f13bdbf591c9cbb310f39baf228db3e7629c9268846504a09f04c8fa5cc66e1fcdeb6d07d39ee4f548b82475f1ac244e14535516100041108"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"68253f7ca7eefe7c4de5bad255f1b6fa9cdf4d7d2d5947a93c521909172d082a","proof":"bee98f11a0309d5dd0709a246d49734ece208016af3789112ca26f98285d3e04b0e2ce722528cd9db3c94adce33d90ea87fedfff705f9b6ca58ded660c3a1a6be260260e1891f3d8579f23d9d045a763d58fa9e2b88ff5a16cf81880a079b5168c2bb18dc8ba8c3de201b50a11a16b89722ff8415ce031caa249bd5237537229bce0e09522bb2e08ded7b02c09ab2deb9763bc32ae94468283a2735e3f25fc095ba76d0ce6e3c7abc492acff7a41d66b9c4c2b86e74c2f5cdd0e511ae9046c064c39f934748bd194a5afe0d81fb093d899798584c9aa50569e9178c89ba9f70d2c809132367c478292d13f78473cb2b0505ba3f5b66a3a539e2ed4eb20a4b00e9c6de8bd93f09836a69553fa4be9e4bac04f5c8fde252718a7f374e1209ce91a3a991d6a4da7adfc10f8a2315809e2fa747b844c709ddc90bf50d201b6a46460ccdece40409dcdcc1ea6acca67109fcddd1f85fcbff43776d5db2c797752123f426208d68269897b27dce45439a6f8c6d24bcfd71e1b24d04b8e1cc8a4b37c6dc2b2fd5510c1e8171ddcf86b85444e670a0c446dabe9597c738500947e57a8620c75f28b8e93d93b7bdf1598e25a99a701d93ead586483c01a6f2289a7d2661090137b6b8d26998e82b47c9f575edc4d808ae2ebc044d9755212f8988b2c63476abb9c0a0342d92a1f6ae0b7d4448b3c9095d34f1fd19feaaed6f3d2f2179228d0a41f4a217028b09bf72ceff76cb8b8f5a87ac42c60cce56461a61e0b060514363a1495c67820ed335cbacb3bf8fe066cc96cce09f7f97b014dc857e931646b18eda873327e86519764ffb90c432b59b9ee1dbb43565f32a225a19ef5155c341a6bf932f71c030cc5bee321e1740405c86d4f18fb2ba5a9cf7a4b0111837e00116ac1eb283da26efffa61a9ea80d01d3785bedb586d07c77cdeb5e424679205"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6424836c11bf9d0eb6ed7ac7324b90d9448cf47cb48644eb36bd2d40bbfa792e","proof":"2a4ec3870b3e35488a5b991433361f2e608cd220318ee453b8c41860e7465a7406ce72cc99997866662e6a8f6cd30c66db7d8097aaa4c4f875f2e1dd29a19669483a7f5df30d44f04376254e2a270a1f21fed6217874807e7cd49bf0638889715e63a57a7f6eee7a05bb2a6cba1134125638859d305d26f48ca8bebbf1c73c779e901d253de12f5e223a8131ba93606960dd36e4babe275d9694d6ba8bd7f90524b17066e695c7c7d5ab1ff31720cadd1532d49bfebd4dc15f82df5bf49f2700cc9cd580854d0dd0eb775ad65c1547efdbc1b60546b66afb0874fb942ad5130a1ac4393f91403c43d7a552772755c94f7d40fbb8a5ce58587ad2f9be8cdf8a2de689acc946f9b3f521650b4b9c679b7711a0ea3e28c27c62158ae0e99fcbe97e02eb34b6bf3f802cd73929cf49d85e3724aa371b09c3c67f0c62c45226bfa02dac8abf8c3791ff489eb3dc32aa9498ed4ba2a71864abc8b7ae44bdf2475c8f2ac40e02968a49b853340b99e22daa1da7bf6a1337140f895d8e76e2ba6b317f2f988e267d58219b8b4ecf5a6c4bcb7b666b10ab4a0ffb8266c57617829fc1ab4f380fd4a1c3187c6eb19213d9dd9eb050e8e15fe6bc2a7f6b6a753b5fe73d361c909d2b6c48404ee2d549c3fd240971bba0330770bccf2eb99e0a1dd4cc5bab5a88456a2dd69edcdfb774c88946eeca8d8ade255da78621b240fcb0c53b6c4d04446c3774df3cdc0f2e89a72440cdae0ec4dc78e3426c3f23404fa8d549acc457ec965677ef37ac6f75c91b7878265b69a6a37356145fcc8f05d99640fd28f62a2881ee840f1b182dc82f4f39f71cfb68d7afdf8aa77c754a56a643d0bae8e043cfbc85b011723fd991aa47e8e08afdf92c8c1342f86877df7b0562e98e4865097464f17fa80af9cdb382fe104610ea30e2ed394ec67a4c5506ae0ae74e519e0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8ed460bdf0647cd197d9c51c04b69de2967abdddd43d7457a6ba602aa079cc77","proof":"dc0c3cc85ab1c1a71bd6d80f587193d608ac279bfb36ded3e75d194bf37d4e350264b3750460112ac084f2bcc58fdf5ceca3dd2adc870e57e089dcef9a33a46416e604ce359dd3456223f7d071b1471ab0ab13b9956e2d7483532eff63f1f12516e99d8c05aa226abec4ed029ddf65671097abf078fbc841e97dcc3ab6600f1d2a7cb6b4b5baf14437fc273f21132354e7fb41e4a9ddb5eac89182ea0ac17106c7b5146f210c2c623b1583453f247c4ed73e02e2955a43dac39ff74e22d8f50d01aee8ec0c6afaa6f1560fb67d5dfe38d1188e9cd6c88a8bbea9980f57ea6904ba9549aca61dcb9a3980a2ab64a6d8cfb7dfd47d374bde4729fe42215ae30c286e739684cf7135e21ec28f3490aef15c43fe3a310de5f3acfa40d12b8a54a8223a3364b5e3c758f6f142ab3e7f59d8e006eb2b39b6a400855bff345f9ed2e504ee49a1b392747e966994d2dde3eae3bb138f53401f396a71824dccd7c334061864f894d7cd7f9fe9e72f11d32f9719b978f88228f6ddedc2f644989d6b06981152bd68cf12f309d4bda7450b8d0756558865f83d5f338a7d0c5de7f40cdf6e06c0adf86b3c5c13816f76bcbb6aac2f948d405fa7b8310f7f0002883ad677071cd862a3ff9c526b544b80d57a7e3868ad78ba6316f8a68223c23d8bc7060de74c70315661fc07178ccfc0bfee7823b19c35073624c205a17b9a9790c1f2b7ce7a6cf9ee4cf6348bab409c175204c1d52ad675c69e2963d75684c9bc7bfb950778689821d594833d7c682a5688130c76d1ea5387fda2adeab619ea8fff42051438e614da4a8cd8c22b2e99071dce250898f1aa613de0e2011c5011056f8b737673c0cc979b2fe8c18f067e38a601acfa28c3f4b6c6483fb55c41cc16211c9e7b0f92aeb0829937e24d01ad8dacc3d5168ca55c8d3820fe12f7238deaa51b6e8402"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fa4fd50c6ac64eb375b01b107e6a98f9cba00fd602c397d156669b85cf7df06c","proof":"6283845e465a25a6a1741ba3d007163e4656e074b4c02d6fb2ec7241db429d3cdcf325788327a90fc1a29375a71773fc00d4b2720c63c4bbaf091812b2874d2ed274d99c1d2063fff4514891ef9da5ee7f4aa54d7907227b61602916501cf90ad85097785fabeb49e568c8e2d59bfc7fafff8b4aa4aee4c9a8c9d84436aba92996fb8c15f1b4bd095278bc6be36c6447da622a980258dbd76d76ec6f0778a10f565a7751aa11c7b5347c3e40f1136362367744bc5c200a5020c2259098bd3b06ea621d9f6759a15439353f424e070172e4394a4a2fd672f0556689180a39480ade033a971fea81aa120aac466938458d704a7aa34f331cc25b720dc399370510ccc24edde0ad1140df2393b93445d2bea26fe00fbc86c8258c17cd4fa0989b0b7e33f463250054f77a1b1cfdc238b67666550a257a4ec02b422a466e64c75c7486b85119d290d6a607463201181bb419832daba5fbad6a53e5682b76705dd864fecdbdeeed9bd708c2d282bcb9f0c6b58d42d52eb1d43836c66c942477c6580a5a77964dbef9cfeb3f06de35111d73526bf1531c8e4558add33f112729bd4f7e04e86f71f504c4007d3dd0e03aece56229a01d7b55c308e8fd06f0a7eeff7251be71005f98cb2dcb7061967b7916fbf2236b400f6896b57b9a4f541fb54490310655bda406e4ea746542f352e1191f080c00638ce0908707b3d49af66ca4951268a8414c2f0d03f99e9317c641358f8d9660a657030b17428045872e1bf1ea46d65b037fee0632254779c019d0d47ef8f8e68ef4df89979aed9d135f24f37151008ace6ddff8d4537ae19995033e2a69753487032ca1cd46f750f319e6c0070841d4a9beba8901aab2ff3ae2df1177e4d9756d916a967b4bfe51c215b9cf3d06244b6e1aeed521090be966bd9b4b9b801de81a02cbc7fc96351cab04236f5104"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1c7ad086a8ed72e49817d7bb7fc9b42c34a85bade700050519af04530457820b","proof":"b8dedd9130ce56cf5732ebe87acc8ec9d434b187e3233926f08991ac968b3e16f2f4817ccf85a402f1d3e8811e13c91bf57225c0bcc70c7c97c8a970ae2fe5437437548511807ddea071dee8705ddd2f94802a3ca9418a5f1e78d553f678c8206efd940f765646526ac3bd6dcd1675c2154982209ebe5cd471e8e79d3fe90f6af4a7ce4bf9fa9c5ce6b1cf266febb044e3ef867ed1635c3d5966990d910afa0d5c37d0053645886a632f6c90e8ac48982383b0a3960b37a6edff1859f5a1990557ffa3a0d366813189e9fd182b0b3e7a109237c738aed404c69f5d41f305b7047417128f9b054a87ba4e980f7749fda1b19a3487cac2d9d4f465a495d6acbf0a3810f51569abe6c1e84dd8a8038501b196bb026df109c6f65488302c3d30c053d42178fce33e32d49883023b438a061dcb4f30d5606ca1f47c358da6c833b73ef23a399c48060dba665cb714babbc5108ccdf4ae6b329566350de02eb08e0a02e8fd56ac0b641f52ef2ab8516abd66fccbae7a245a87d9263664db3a35047c616e10ba1731b1e936b43a659ab0bfc899c78e849e0a88f06abfc31a3f7b93bf15e2b13d922f277d3d6af4edd356c88f5e02dce0bd3eeaee760225ae0735824e0346f7b9383b9735f0e51ca9da91d247df03778e9f5e34a22cf834d91b30a56767fe521ff32d2461e56d6c0ed00484ae12baf16612e2264f812597a53e99458c348297120828982bba1338e31f483d3cfdddd0584670995dcc1806cc158d1c2928883f365abcc407dd614fb6dd66e7ebff09c622a7cb45db657b144065a194645a92092bd2ef15ab89250447f8c03cf8abfa165caa8d9166054181069682618564d8dfcc40875012e0a888ee61f49f2a6ea6b4dcfafce44a140bf85698ecd6e203ccf5c633482e337f9ec4699e7e44d67e3d1e3bfe97212c614f82053fdb3fbb0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5ae72f8c807109ee375c7d050c21480c13c256af011cb23097d5798eade45532","proof":"feb78404af2973c91de7fb219e773e24e2c41f7c84d00e823dcc0301efc17570328409259c313968887d18662b79deaafd54b4b9c5f85439b47b8aebf894f42efa6275c070f41cd71a9e2d3dfee0a09c618c05af3e51b5f65310c09f6973bc01ca6693ccd73fd6a08d33a92e110d07a6071fad8a36e9f0a7e99a96416b8b7317e64371924c03b60baf2253d4482a49734a473dfeca599a0e004ba6812dead602f926b432593a300e0db2aa513d622bfb8af6af39253484a9e4d172ceca485f09bbad722964330faeba65be3563b288643b0a4c9e2959f31aed5d587378574a04a4bd39d22144462a2543424eeef39e17a9d3390f6572b9ce80fac663b3cd8534e29946b5d727fdd9fafd659bdcbf949bf915a5070027e9efec411f125b69e57d8ca67093386de3d844dd6f5f62c39d748409add249364bdbf23a5e1cd4d12e176896c5bb252028110eddf7366dc0d6e31d417c12f3e6cf4525287446c5e4847198a055cf61a046766aec80c0e15c4890fc602788b9cce09d0144475e763af715d4aa142a7ea1ce20b09de8c209d7cadb30d6c76b59316fffef5eb2935d199321668d1905690cff6dc368290ecc131dd86992a1a59f551abe5f2a2cdb75a6891a68af51de6980b090b9f6f877165379411e5bb287d73dc2df4c8c8744363aa34500ad3c856103169c30b7aa9b6e3481598534f9ddaff3922018da3ab093104248f8a7a902a5ab587f813085188dc29078b9ea7659d39026851369b9b5c7e1295856cbba6ffd827be22610bd6732b5dcfadb68f16c1b8de3e155b4756772673854362c7027e538cc586d59b9525a5601fc5c8601d57113281e19cc35d6b8f52f40ec30faf9ddccc7620ffc257c75a7174c132f33643f77c5852b1158c88378180718b597cec0efb373677fe24c60eb74e038939be72caeed14359fb295a3e7180f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e64373dbe6f6a0c9671cc09354445b4de0174b670dbfa1a5b671097af1efd56c","proof":"a4ae91fe051d3083185ed4c934ebddf47d647c33efcce7006be8ae5aa2b1cc371c8b397c7cb09fe791e0eeb1ce5a7a0227114a5d42ea8ebd5fa5bae14bfe994f1a1300654d5274f93d5fbe297c9153f05034eb78a4c813ebb795c13551fdca204094e550d214ffc2e79104c9c7194c115903af8a3f71da6749ffe4103353a24ccbfbec4826ed502e7df26017d2a2c0400b4085f6005d501562df4b86484b1d0382ee621eec2a6f830662517195e65b208063e9d6f7684438ca5555a05624530bc3917b993ad0f9a4403ea2f14bfd20647a068cf639f3bdb577e5ef1339b9460572d59f014036084abe512e51cb11fc3404980f49feb15a16a5bc29ff61d3b72ccc9b9e5484192eb974eb5e79fb66209af1334fe807c057ac776336b6f7f2883244b62d4ad85121cd39818fce884d60a5992a2a18904e401d0f164990e73bd34f566cb28b8c839aab8f7c405382844cbcefc063c8add017a435088af48837bd605e58790652c7a8533beccc6cc086b2a71d20577b4e6cc1faef332caa96e9cd3106c34f1d40f391fd81733940049260553543b707f18892b9c646dbd5282d321830fd4cd1754022d36e4fe00c0409f191c654af2da396f71ecd03bcdb5904385446adb96f1b713991ef0011fc6ea89691c4b369f45cf7a825fc843a6eed418c4f349e0d19a1356725db960a09b9dcf57d581b851beef34f23f867f5a502916c04f0e97c1f5e8916107c7902562859956117cfb0a8cc5f5a56d2c0d26aa6d0994c8c18f5af3a883c2e21f0acfe0ed3fb40a6f25d133ef77ae25af1dfcde2115800d424c45b59b051abf1ae6f8ad0c01f145083822b1054e8d5bcd95924fb2089174f0027f9ed75d43ce35abfdd7b2a6f8e592a70bba1ea3aa40f5c0ed3530ccc00b3ab7f89b531d8c19715d1742603153df51d5c621d9332aec27cc5b176be5105"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d44a83f12b2d5d19e4d485228f6233a54dff7b21e524a52d635b1c9c2b19045c","proof":"9af1fe1a655764c97748d489c0ff8fa57d1ab1a71fbad1cfeb4236adfe72ba5db63f088f1645d1405ab0416855601b8e31c29cf96023351a56ca971cf0eee822aeb4d87644a523deaa69cc500a5df0e2ac1f8d69d784add92b4a62c9197d3d025681cbade8781cf4f17d7be05a690c4a4361873fe77a83610a5d5874b839285945564e6a1a6a574f16db7e1abd1079feeafa89b3b293073cf473f24b65ccb90952bf18242b847e50c1f115bb691bb2411a744dc629b9d2ebbe184bc58f295c07465439e7b886ccdebca2cc83318e316d8000d3be6a67156e6f4ff1224ce057005cb14d097f110bcd55b19c422bff4a44583d8be25234e6a7088d15bad366d53d40961354b378d6fb342d401bac3cab9264e10eb17d5bc8ec86113bd65ec9fd564880a1523c9ba3a8f75e9087555a9ab540cb962a0777c173c776da33ccb4d067fa7ad6b157063c8485d5ac1a6d86b4cfa37ffc12109a4e598156771edceb1d14be0eaa83497bd1c8eb66607e1c44bc883fd53d08a08d7248281e279b82a6d4261434156c345b6a315737ecafa13d6ce59681cf8edd2a2e9769d2fc5f68d50f413c69d694717486668b1f48c3e3afbbdaf69ae7a535d12536f60feaf906bafe5d5ae563077d94cb6b65ab75a0aca0c03afe98a798845e11035b0772ecc9e0a12e96cc4c33e6feab1bdd844eb16eeafb37c962fc5a19cde1889af71e95a29f3f20202ecee0c3d27b64be506b4a5ad476b5d5720814bbaff89898b777624d49ad3b445c4d0c18863eda24fb527adfd0488f2c4889498fa382b8e596529602051218d2fb83a6855bf7546c8aeb472b24930539e7e9e5c7da367c3303fbc2d498e813117aaa6d4c55e063a986fc15efe32037a98849d067d4035914d9318851560604482cc3d84831a2ffb2f64ca479dbb9a07cc4cca6e04bb47fc0ebbeaa29df5506"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5c06a51420bc2651829b078d011f437a7e9ff08e43419445f93c04cd314bba2d","proof":"14d893acfd403a1794bc7e2ce14f0b16a2a7c97fd1962aa1198285bfcca6964414c708dd1c2af837cbade3a6ee20258b8fa1ae713986cad3d4b655665f270d3918602bed1e4d72c8f8433b84665e4694961301623a949a0d58da6eabcd7e0d7cdc1db20b58de07845ca9b11320cd282f5415d2480503b198d47a3e10d751b301c6749f30d5a0b34ac1a0188f6ba832bac95b6d34686472d6dc7994ad54e00d0229ecf68d67aa0190bdb8ede27c1a548648d5eeef72e1f93a1df3f14c91317a0008a16e427143068f36ce29b08e3ae7399e82d541bbf218336e931bf7f77d7000d0e980ba8f2194d9107f3993fe50872738f57316b1b66c4f49caf1bb0411cf154a72d0787fc213b5c7a1af0b39e458a7e575364ed8cf16371f3a41281c5da94560dfb97cbd743273324fa654fc735e7e826184d45ce8f0bae415f285598f5b667eff6f78171364b9f09d4152f07c363950eb8cd73170c86651a56cb5437ebf221081fe3e7c73ec8c28ac935ab3b2c9054c19fcd90a86fd6002dda588b33bbd41c662eefcc964fecff8541115baad932fbe61c6e2466d381ea30548243fe05624ba785add0c0b0ca6c6bc8870519969052e20387007a57dc04028f7f6611b7541dcb53f3c77022b95852b789dae1c0580d0f71a8e99712ac93622bdb4dbe19e579a8ac949657a93b3e191570ffe9fa8e9130f9a42342a0497a182f3ba1a2cac3822df13304a40f633c074cc78a2997f17bc4a7236d788c5399bea7305da0f2f16f86bbe347abafd115fac33619866bcdf31ef1e13170a22eda9fe7da0b7dd9176c861bfbcfd46515848f88287768040a26476ef5f7edd8606f8daa6fa490caf0ab7fab4e3240d4ed3871a369a3158087111c0ad39027cedef242d9e85f696810cb0ed7432ec4bc6f7c375a51da0ca85c66e2e6bd2a95b8d3263d2a6dc93195b0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8e8912e4c8e657619ffdcfd76a013b88b54a0b2fdedf4aa55a7593638b4abb72","proof":"d4712bc6517eda5e7815db03079d21725ab73ae02d718fc94d66017f8134095eec13cf030efa70d4fe10d0b483a516aa1552fd0a3ecce8fa046f79019a10c616ac91b85d0801abee150ac3c55a41b89781e80d735baa3198b861dd543542991c3c5c51b56e4f980155eaaf052093c33d1c2a5c81692af988d94d9fd951e62009606e535ecd43377805a87fb61795f45277a6cb5db4b8f8f111c54e4b7141310fe8a6ccc30c72477bdf0496e7d104831c9e3ccb70ac169306e91bdbe6170feb0807879be5077957e8f3340e5aea5a16924fc2e3a89772078a0a3ba700dccc870b428c1800f3c5920923b6e9cdbad55c44cc27532e916ef964815514930620ad0fb6119437e996db63f4f838eedf8b054d8da73a4641dc9335a7653cac03556d2dd8bc9cfa832b4e4975d213b5422828f8f1ae97b983319e5c2ca0687e3f3e1129161bdc2b6a3191104b8b80ba36b52b586c0a3c597c713c2d0d8c247db5de8c4e12e5d073e8642fe13e4c240077fbdd39c88305549f59b5591f3c9f1a2c527d2ee8a909447b5e209bd3e2f1d937d1650b099381fedeb6ba21cd9e5d23a13a224d62f77d0262c4b8c65f16efa509b98dc5a81ea7ce5cd0aa499180cbd571a8422caa0e7c1cee27e173af2f613adf45173105ed9a23e0adffbc3fcc75d103a2a17426c466f4c33732074b0fd3d8a7139e1e997ae755c4a1a614178f9f9d5c3fd042b6392ba805917ac89b61bad1bfd71462c4bf2d0fdfa046e58e174ab2a24648534a20446ca0c1775eef9c06ac846180069c07d71924ffa0f486daa082fb9fda14767ea97da0bce9d319bb8370295bbf049d4590c90cb03963e3fdee71c51bd5308fcebcc292c4c2955e37cb4b36dcf25daedb1f3d54ae0050bfa9cca137693e0d05888b2c8c4ea2342793051f291af481c81ce0089a3d940aebc3ec1e97663f0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5e8c62e61eaede10735f22ab4e16d6fe1b8a2c788797572a86fc7af91cc04579","proof":"f400000de07e7e36ee072ccb673099d9d49f44aa60eaebf93550985a940a3b25921ae8c3d4a36645f84e2930408e62dd6082f8d319d8c31ab6175c711751a61942e483832a96ae40996f5779fd2a34e2ee639361794eae61a2e1c0ede7030317a68e5c6309cec976b2e31519f2100a6fd3f212ca343b028a1bb4d01c62f01a6d9d79cf1314a7a5c4474ae2436fb78a28df30b511fbb4abb9a69d39f1f48be609fc8eeb3970d6db00988e1aab3a0d86aeca189fa54f9135e2a756198358cf36003c3d658d501d986e7b5803870157f429158489d1dd30bc50da52c75ce73f4d01ac3a4497f935be4c64ff379b95c0eece5023d9f0927904031ca4410096479c6802e4deed0a7794fd8872bc3be1952fa6506c44f3937014a8c345a65d251b9a33c822db273fbe24f175438069b21f9b83e366afbf24e8a005dbcd3640399ce14d68df415f1035587f1e0fcee1937a9254ad3c6d4206356ba9be75986261e4bf768a8a22b06d7d11e2738e7454efde46bca0332f9fd9f68b18505908c60d657e0c408cef866adfa1dfb62d7cb787a9112c93afd76e18150f7ea5692f96a32ca539b60c0280297c02c39e8d55f88512275a7d3851477607092f9fc50de148a6c32c7009e8761413b61049ff02a2ca7cd1b437a5b6c703a4944b28cfabd55f44e02e40919897724a515d3370c53087c5563b1cecb1918cf82d07484a0fb18230e94bece9d110c1e9e9d5b17aefd684b9b94e53fe6beb21361b194a6f7446d8ef821364dc5cf2f388dae75e4ac5b51b5c6d4776c7824e0aef997f19fb1c5ea1937661ac676ecc6b2fa09dbc15a744ca37f8bade3893b0af79a00c57c62acd00e83629ca56cbd541efd64c83037d6fff77445c1c307beca5c0a4ffc364a28201923f08b7c1fc2ee1b29162d7c29bcd287b51bc6840959a5ee84ed327df89eaddd5ee05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a46a1785e7865236584dc3507cf333262133e6a55cbf347be0f1f49a6c4a5b0a","proof":"cab3c0a45f30f6f361f0ee2ca92f296a8480b0c6cd31bfece587d94a188cfe7390c30f081265cea1c5bdd8f82ef18fa00c600e88c0d74ebe8fda220328ccfc7c84cfb6dc77f861368e918325648c1af5cc35b44b614e7fa58b5bd81340adbd18b4fab125550190c1ee141f4af27b31bffd26ac09df4286e655b8fdf11853c51cf76439b3f5f3598b01c1caad0a27a8f8ae0495d0949263950e2bebd799935602ae7fb456874d04d57215dd170aa01c997b226df04e428ed15fdd6478929aa10b16a85b2dc80cefd0698a0e3b66499eb6abbccc8f2c7adbdeb11dc65cea4fde0b74c2b785e404afaca5c37f606f797533fa584da6abf1469d92676959bce099500a408037bd65e05201356fe3af473a0331257381181f162881548a01f57a161b8c303b47695eb19fbc72dd209c63ef7de75bc7a386d2fbba0a318fb57ebf9e49d403111836ceea6a644e34bed8f9838482dff49f0aec0d9d3e548be089d9bb208a274d9c2adc6d0b513cb128fad9711529bbc2cc08f84cb6ac7375ecdc453d460a5c2d5b6d989bfa718376bd507b6bb9d803f251c35dcae6855e5324c6e615769e1dc82d757ce61c058b6a21e3ef6d7a950b3c5f5191096a3431842b5658585ad076ef2606fd6599a79b1f2a5f8ed2e3c27d696ddc316f973f3972d596af4322b0de9fe462abc2bb58d9fcb981a518841e835efb5454a8d1719acaa5ce76650f16c55a27962a63a61033c2762f9f973061b87d96066529137f1eecc31d540057fa0a3f103f865d8d1fa59766e2ecf0b40334809af55027ca901d36387ad56339de41b2fc5fee46cc9b872bff6e284a8a15c832d84ac908240130756f5ee6e2048955f5616efa1fc1166b7f2ab5c2c7520ebb5331b193bbd6520b323a66bb100063c26ea6c1dfbba48ace12aafac6467afe7e5a3d9224b3c4c67e873cd2088208"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fc585b382cd9623b5a2723ebd4701c802c98438c48f59219eb700e927f65f755","proof":"784e53490ac8e58ace871debfc48a7b425747bdcbd445478a00d6c117ef0f26a5e22d98b62b999ab6e620039da2e67f4728137e8a4d9e39e1d7b6882cd06ad07a2fc6ae2a70cd516c33a978081ed9a6f3a370b44d977cdac9d6224ed3aa22e3746e43c445f13d77f635773d547c4c24df751417785dcebce0e720488dbff01045a29b957fe37b14895b31dad660cf0a54bd7ea8bee2aa1a4ebb2b7680fb23504b22b6440114521d2ddfdd24459a5ff5ca590370ccb8254db5f1d4081354e9306f13171b33fc20bf57c044b37d82347d37171faa99db57135fccacba74e4c9b03f8de6df42e800f1fe555e907fe039caf35edde5f3d91b81e1263231bf61b0a6b8ac3d3a7378f72c9fb0407da835f9c196ed65fdbc5141594b7aaa48b316dcf253a7b46eec62accd88e861d880d5a79134b5c35b43b0f50ed50b94b097cfdbd2d5225e63cc83f6dd128aefb04185f356b079e6b62d1e3d5134e41ffbcc4bf012c563cabea0b2801a2d5d7e9364be14148c634a42545555e9661306f552c502e7b4e9b6210048a0138daea5da513931bb0a4ea64eb66b164a19ca480f3592be056d8bfb3cf93fa5e32744517a6c2e9fb63aa66ea5876f9a80e21d2b1b24b3ad355769dab6f0a7d38161794dc4cee403b052f5be178ba7f3a2ec5979e03c332363cce471c4b3d9642e724ea8feae89d617e41e9f471cd8ea2bb3be290c3f965144be29cf8b5890918783d0a43767c9b6ed74a358e588319415a50b53723d134a655b6181324e2d81d6a0fa6a438138407c0a84ea035207c103981b7e2c7353eb05274acf1daa1a15bd5ba424f81aa9914a14e4589f07b6ad7dbc030427cf179b719b1ac119b292dffda162fa57889ab6ae973552b8bec935623d9ccdba4494e570cdeed552ac381767da5652278b1d03efeb4c40d86ce4debc0a8e441cf3dfa700c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"04a2fcb28fb37c4b870d04f6fd2d10862fecf372842148690fab614d6d9b762e","proof":"ba3fa2c76ade1eecbb85ad1959b599714ea651d43f6e62cd64d89fde0bd7f8479e3df9f92a3a4343f80fa826b2f676ce239763d072ba864b12b6cf92dabc967e60bf23dfdad3ee3ab66b00f83d410583cc461da3b3283e803c1099b436b4587f32f2a5211ecbdc0df9d614ba9b7bb179ff8ecd4b9abb6dd46467175d9174da4ea4d972ecba537d791a14338283b1df45c3892be726d29e13d0f152c3e78a2e0986be66d5acf0e5220bcc98920129b2a85ab1540e1f990f24429946a8c7937e00022d6e6b8e171aaee91a53b4561d2190f866628e041f0b54c000c63e4c340608fe42d79fd387e4ebe86ff4098025991cf11803ea7ff6ebc3d66a376ed00063615e21998bd4a4e2df909a78b6cb20098077fdf897f15b29763cecf2d8a9ca5033da3f39f7049ddc68c031165a699605b07a6f72c32d12f3156ea15d63ec925a5702fb66a1b78342d5c01e77090f8585fc1168426dc56df97a0db29f2423bd6e2474b3f8983f5d59aadfc80d5817a06ea7dccfaa7d3a8cb0e4b17f92fd5d8b9b4a94a29ed86a5963c3cb72268705bca51e8f0865e7a51150e21ae6cb7c267b043d90a7390a745ea9a700a08739080fc5b3f979cd66df497fa817e341d618da1d58728a3995db79f89a34fb7706cbd411f3da5b4997d140223dc554992bd16dfa65d4fec1fdaf77b9ce09ccb5f60e3d666b1fe161de69e760a4e5cd5843e2e7e0621611659892e41e56ad08b31e55da69f2f81826db6bdf26e278cb39ed72fd552cc63ae61fe6773bb0ba5a9dc72a8fa2a91d455c25d22897e8b12c422b33d0bb149887f52d3b0913a148149e7cfae374ab9892503fc7bb15b6a0e17228dbab5918f81fc9f4d5093c513f57059dc0d9cfa0d842ad7e5309c7b5b11f7e0a4531260b0cda79aaf5417706590abc676587be18d97f2c11779b467c47854bef46d0b501"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6e68d777f3028235e62e60163acef0519e94aff23116832d39f66d10936f263f","proof":"48dfc68c96bf90eb34bf8ab2b3f6c7926d9d0bb41020960b1c8497dff99f04608e65ad07cf08a5916cb03f886a6d47628410470f6f62902f3987534e561fed6646f1eee149c244fd86b192a896d948946416c62fa8340deefb6db20028b31f5fd266bbf2bd0c84e69cff76e49d6942e8c6ee5b194f566879a6488c5c06362955fdb903632f4235eb8ae1908ff5e22cf1985568c4576f000cf8805f999bf26a0afedac8ff1706dbfc41652f254c26088eea3a109ed0b7c87fe97ddd45171d0e04d49a316cb2d8181eb8e99c0799d67e6679414dd0c1d1879a5dfcb00eb39d990c46a30248011638d151e8081b564e2457e74e111e690aa305ed745552cbe7684cd862a6cfa4f885398853dc748848e5a335964c58cda8dc1788c48ba7b47b435f24fddc4a91db7d0a890dff931ab646543bffc5ce7ccaa1909271ebc0137997118a79f8e5a21847e25831c582d4ab37fa298b7183f23e9dc93eda4cd7dcbf5a1fc4e2fc779962105eb663f31c789b00914e90a5175bc0eba542ca6456efcd83355ab99075221ab2fc9e90b7dc993ae49dd2c54115a9e2feec0b56bad5987f7e59accfb9af4b0d239409da0cf4b085c7740798e39ede3595605318ef6f0bf2a14d9efeb18ab7fabc2f927fd52f191411ff4acbc7c8142f3d8e7568a88d524a010bb2e65e05d71e2242854d3e33ebc8998a8f39bcb5df46a507f4e0f3e40c56d539fea2f53717baf30bd7a6ad89142c83d19d69f19704a01805876313a86e75385cb466af90925b9c2b0091e8412e1e159f3394da35a026fb02baaee1265f97aa5c4ebf3ce113abac253c576788c48c8c800cf40c08a9d5e9283faad69c2832b476fd05f5cb37b6fa150780157e359bd2d1ad6c59233f97dd121935d3d628aa5e0754ca0292777838533cf3b1fd09fae5924d8b060d07e50a0e7da0dc0f0c6f4808"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c4df9a2eb017e807fe6a29b2a665701683ed6ba353735ef46a7bf3bb8d09bf3c","proof":"ee0f50be2fe04c28e32fedc4bcad4a70c9d5dca6f547445088893098b09f55143a7a83c347a5f907ce9e2c2cb9ff8b723d3c06c52f63d8ec286ab645f4c84b6962c8caba127dca13c42ff20fc3d26189d8565f3c89ab2843774b6cdbf32d137506a15a7742a1b4792951a8667eea3e4227f135d09a7cf3f5bbb17704cbb89f1ad5de186616dc245ba17667c25dcde652e0570e95f86c45e0ccbd3715437884078f7e11917c3c44cbfcef82fa97cc5f1f770cf7d5b0b354d87061eb39fdf820021636b411910dd85146a73f0ee5733e0fd09e3643a744a275d558269723da4209b846ac59e06af1f4fa76cbf739086d1108d1baae7f494c2b6902eaa23d6d7823c682b078a9921b78083a5db30dff5fb0c63d71dc20769f31ec9ab14f29e788165cc44751066d450467a89e57ca9f9d15f2d148c8e6aec2d7b8024e555a369274ecf974bf3655f7345193ef4d007dd9413c348630a5b848b49f018c1deeea5502eebf8fef58bdda6e80b45e752a1e5b75c08169d9cca8b427aec8edf85eea2f00aec4a5868d85128bff6d85ec4cea15f8bca360e2a9126f64d7c3a64dd1a92e481a1ab172dfb36829b6378963dc69ab4a8b63507dd2b7d314de9b63ed6acb6d3280f8ad766b12e5a4bd66c34dfc8f97cb31ea19a4f172a66c7871a240bf0cec32162e5f9c8a66443a65e35ad31d54abcc6f470e5ad37330b11069da16edfa6d45bc2ac1b0d6403b56b70e438ed6c7a936af76c3c402647cac7a7177e994541552683666175d89d75fccad2bcce7fc4cdb643a22263a64d841a14faca2ecf01c1494d1e66345c614f36b6696337c521e7e8031ca4aa60d2af77a2974907d1a556a2017a130898805254f9b67574ea7b1e0975cee4c3f044c8eae8fb9328e212a0ce84ea5496d88e27f58759eb47a36d5ce9affccdb884bf7ca6856d99e0027590a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"520fc12abe89717657d01b58410fdc2c58931b1937f8aec85c17e461adef3638","proof":"38d9fbaf60f288f993141b807a9190ed81d262aa6b7f8c6566a5da1174cc155c78e7b26243b5a9e6c3c6b39f1210c113a69801f71ddcc28df4d8d1a5f014923a744e0591f93e184343ea6bc71231636c24dd35f1be803c363d78aa89ec5b7b4980b2a4181019020d21c400e5b05d1bd0ad2e509c6bbe536fb3e467b554caad7bd967fe5ae7822dc7f12e95bc874dc8daa089f661e8300f0545b37719327472020463318a08a218dcf7f4688e01ecbcd780f6d695e719656c8942ce70cc5d7d0dbdb66f1508d7cb668c945ec00001081933cc4124a8983fcd6d174ce78c9238010af4457511c41b45c403973d87c873efc7cd296a5f2cf47a0c8de1a4b3be28726c9715cc700a3a30bc9efb92c878cb16334a7af3cfa07c81290ba47fee777e79b066ac179f490014a6dbb521bcca9b4ac81e16dadf4750d7f6416fa655d56610108706acefe4ac377c35dc5ac7b045e921f0ee5017d5a40b414e882470d31071963a064de8a08a928d1dbbbb05fedf925ae08e099fe7564f84feb16b3318c67e825ce2d4a010ec6839eabd71f8aad8960139d14bfd97e06be760e17c57ef2469e62d544bb90ce3771e9cfb507ba8d7517df081dd5f42edf8336e207df0c1d161c43d1fe004b47abf195629f5250a0e54b152d84c397161f5eee82200c48d454f32506c4d517e1fd7eff428c593423f13cf7c77f627930c986bb00bbd1f96cd25506c9b17cb83318e08d3d1e0f732773e926d9179d493a495f6626d2de9e5d902e2b6c7cfaa4e4f852061efcbbd6f96c4a6a94d902fba9e9f5d8929289fa5ca6d7e533f96b84994ffa70a6bf7baa5c40abefb6c2f7b1398c765cbb3118781144bdcaf084dc1835f895077ea5383081fd7a9d5030f424ff301c9a2c13465b25b08c7d0262368f617435541699148111bd770d6b03a3cef1863a1292a1a355b2508"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d22fdc7e0ba8e60ccf59a1ed5d56b94c92958498ba1983d5943f1c8b3f7cdc6a","proof":"aa86346d04c371674b8aa7a00011dfdb5759fd7b6e31e74b66fec8994953ff24723e0c17f7d7e8cb42b076cc468265185280eabf727b1f157ad1fc12874fcf76cee0d75c0f704862a8c24464385c106abbb8652954dba6a467ea6002e8bde80a48fdd896a68ac5a2bc9c0b3b380a38aaad2394ac63a6cd6c6a2845aab6223c492d40c0d59676e3f8838bc0baa7c01735af733f1e44b09af01edf9e8c9579d50ac003eb9e20bcd45f773ebf640f630af0776bf4db4c880a285ace9eb40d984f01715739e59c8935c96778c34bfb5050111c5aa0b4c09c10c29aca045ae9a7ba036254af66cb4741ec0264970833090ad774ad6b2afc7e7d81d04c1b9e158c0e119e3ddef8bc1e597613e52077b054335eb6b98db23bab3f81ef9c0560352ef555b86308afeada53e95047d297c6f7ff55dfecb193dddd1bbcc11b67a1a72e2d4e6ab06de12a784f5445c7fff8ae471f80517ead843aefa487181fe6d88443101f52e074da95fcc6f791d1adf85914c1f78b7b6d186dc634210c893582ab5b40052a122e818be53a0b71ff1359144b0523f1b76d02791287bc713a49e3fccd5e16066ed06d02201cf4a94986b59224750a87ccfeb0fabe0968a691677857db2213fcaa28f4f61e2495e5f50d21f9cb17ed1456dd6ab3fa37baeee83dc1f6d930290a6d6f2abfbf32d1d73f428008dc20460d301ed40e9f97a907382d7de5c4a865420d26efbc434c6cf8507eb6df65f7e4d06ddc77617a32fc180b595585352e23aa5aa7422c2f2735b76431ce1a7bedc0a78b62ff830cfb788951e563254f1d78ac1490ef7ada561387455496dac3a6674475578ab1142bc9a5cb0862dd690a1a31a23edff9ed715d67740f317dff737913e5ad8a45aba3e9cbb6f17f18ccba0da554f7c93be9267d03ea54b84334f39bb0c19b78daa1a9d964568de0210d870d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8c0b775c712c66525a960dfa8b36aed247a99c5b4a64357e5a82c56ad9c8d60c","proof":"762a31001d4d6f5d0b7fb09d9861814635232a570d947e223f7fe83ab8fed60e8861c2e9485c6a7d79d3c1bbed0ac3d4d5f49d355be1f4ccbc342f39a023b32e8491f967d30a5761dddabebfc9a83b5215db9b2c5009634bed88757d6cbcf5467c1a121540dc80788c927d06dcef446b9455331b77b9cd58596df30b6da4bb5c9849960945751f76386c471940a6543d6ea9093984cb1efaff683e785d46b3078cdaca029248d8f2b755c41a6234bad6dad058f61f013bdc56b4beb293c3a70ef443dee1e08dcb39545ac31d55113ab34ba0db56aa34775247863a4dc4460a0a2a5d78d3096236e2c1b0563f27a7b2cf5aa04e55f458a79ba63eb58016dc3415b4fb92d5a573ca767c9fb8c1531dfebdeba7aeb3a3131a95d12dadf12604db404656fb2a7cd645ff01dce5e6fd9ce2c5c756cfa354e9177e859620f456f3bf2de4a54d7438791ab972b9b2b38d820fdc06fabdbe554e2f5bcdba31858c03057d8cac3937185fa57ea6512178b71205e4952c629cb8a21b2d292b9cbac09aac2c10d7a3071b31f2dbac4637973f64777583443e1c8c73da27a680f0dff958ff1326bb5eb01418e2958518c5cc3e7b932504af989b8a992a065f473e2dc7ee093d20b4a2c6ec5c69f6ac817d7442f8aeb090f071a1e1fd61be6391d7ffb581de71d071bdb0b591c095da7a603abebd942b94dd60dcb9849d8d8c0308e99b80ea434454959fac13ea62b69925c636edda6f063e7a1fc4160309d0fc8e062b7aa60cdc10dcb84b5cd7449066c793e55327cd8df14af8d9755fcadbfc33409524930dec0be96352c29c6545ec50a55f92cbf521ce73788b23b96b5a267fa42a77d0460f6b3f7342f6a38c26c5f8f0078c96e68d6591ec61e34d9c38e3f1497415560fc1f14759c9e2611980ab6879302786b0d0e4b75bd8458db54bcd25dd9dd93a03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d8a42e143f63d5a3fcbdee25faa64a59370093b96d7447e9dd50a286f0c75334","proof":"642c22f62db44615240145cf27b1f39e4d01c6704bdb956c98b0967ef2391308d4136ac421a9e99a74a3af6166df2bc0c0f80849d25e41eb9e6d4545235d455f3ab6024938bfedc0f0969997a39081f62e12cd9ad68ba7d351df5ed6ff173e633a5fa017056ed60331fdc3fe0b45dbabfd32c8b576ea119d30a02dc271077e1fe3f98eaa2b31cdc31c2cb66690f4b9ac22d84177e6cddab8a50a848cb3c1330bdf0eb9c1213316131db0850823f673668584fbf325837450dd32fc72397ff604728ac3e4896316495d0531dbc0ca3f7a0569995b05976c982b58e98d6f8a450b6ccf7f7c6cf11ec18ad613d1c55c3acfc1334c00d3b2552ce8487b0d3cb4db2b4eeae9c145ec43daad8c3154477e11ceb1f4a28a7d38b19d96b4baabe463a66b4c7a4fdfd3b7c5f817417ce93fb98db2bbdab96b099430be37110a57b826fc2bf03a6400c1cda82bff5cee32fc35567ed5d164b9c0738e23f22c35b3186ca0148290dd1412760be541dd061fe871cf8f230f75739b1cbb0a52a8eb570fce5b5d4e6a31fe4ac244a44050e3f1e5c298d880412ac726af3410303f749640dac257ae360d9856973437ac5ad38de1339ed233e4362ee1687ebe07a7480ec693606caa2867502122f7c4d3f54b2245a07fd241ff895d2ceb453a23ac85c84ed59856be8f02f91f3e0a9e187fe282d79b99af92fbd5e289629c66bb572ada1356046c92ae04f36f7df2458ac4ca269c4b7a25a49e805b155d383503caf9f350e0937cbe167ed07d060a98fd845d80db4ea9f354eec94e525b58e1d978a87e2b4bb15a106e85cbf288fb403c4fb352713b8b9b187b9a55bfbdd12ae4d31cf665ae1b42ee4f4ee17247cc13ed1d6bc68c600d9e8aed3357611bf1b5f26ba4cbb299b100550af9554b24736c2869b9bd946e1c9ced0038bc78a135f88b095a837465b702"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"625f448a245cadb33e730d08948b78b59a522bc98ded65c86c9b05488b6e9d75","proof":"50c5a124d70141e4e6d2533b6c4515dc3bec4a6126f7e2bb7839ced2bfd8901902e88979679c842d0a918a660cbad09ffca8458b375942a5fb40271fec8f390fb47a7e78c10c9d263d26ea76c8cc3d8b83194494414c16ddea085b9bf1a89f418aff92bf8afd5c1e5aff2315774959281090b6d4171d537adbfe8e4b2217b5239b7419aa72da827841eea81814dc1c74c0c2a796784dfd9c736864c5ead73d03fe73d5cac189d680bafed59df27bc1edeb165d1e6c7c09645603e613b3944b0188286a74fbf7d9e3d6709385a77dac8a1eead5dd7a12854086ab5c29bf9f8d037a74bd6b26493d9472c89e8531899b288f7f2675b404fbded8eef136b1e6252f7680bc6c7ebfbafe58e6f43609d246dc5e5ceea84ad492b714b900ca906603027c22fe1203790c403d5fbb84a5bbd158ee5ef5fa80b90b828f91b2e802f1387c12e6ae6b967d17528bafe310ea88ad3da756f4b21a7008f8ab65d2362b686034e4c1b6e1ae247655fa66b0fae99e036469213a50abe3105d18a10eae50740f4610032409f53d98b8500aeb68531d115ae85138465c639fda82a51288dbd7c820f68d397660d7e0f1a1d638a16aa9245ddbcd5d9df7b278540da00e86e2ae653664338590460c7246ad5ef8d9dfb1e31c5977829a6e3c5b56c49b231abbf17a012ac42ed2ff0d124cfe314ba7ce060574162070a8e7ace4bd58b1830f9c55c40e4057aae990b408a6e08ec15283414aae9216803c169ee1714f88bfe4d78b793a8a81ef0982f07780c78163b5c689c7c9277d5cd85af45a19a0396809a615ee418c551455557c28ea9b0b86127279ef5ce0f8fc0f138bfb17acba6c1281028a244f03e71bdd15cc33417859984fc932d08e341b3d617bc43ef193c34e0aaf380f7546c3ad9ac7f735b090fe2d2b68e6b251ec62a31facad1aeeecbd8aa32fb205"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"30a5215dd8bd0dfd696be5bf79769e14f6f96d1c9e34684a2f15a462eab48626","proof":"4ab48c3049ad116856f8b91c600c29540c23d94e045e7ad9e5e05a492c300141cc4ef3ae2f637b41e9571b94173e4523e3f09ea57c89fb9f9ec243fd425d254a8cbb3749c75253e49616cfb9804605b4e3063d8db4f9dd9aa8bccbb1d0cecb47a4c2cc1cb835f22cc33930cb934845d21380ee4823517e3e2651ac32b35e591e60a1d86225a58da49a78c7c17dc984e7dcc67f75b7d76075e9c3815a16be170730e458eaf3e33b5f9a26a77493e6c13bff943f5e74224d28db2a299f5ced6f06fbda512aa2721802a28a548ce6b120362ad4fcd73bac94749b65f0ea7a044d0000fcab8e4122fb045cc97e52bd0d03039e9005692a4ee9337b259abfec02db7e02aea22c57b1343cd1f2c0706f9d5b244fc73dedff1c616fbd03b836bb48664d3654c735a0f9676331b8b14c3e8808ed8918a5cfc41de234f17b8961d7a16f1202236c0fafae4669aa6959ff23fd56709a80ec114d2ffd2c456a19da03f5c14152995a8396b8f53950838706dd2a36d2a3eddbb8b0413fcc89cb8fe7863cec3e18e77ae2a587f1158c4be3538ee0c0b4b66c78dbb8bb253cc42ec1a03602db66ea16f2cf9c81fcf35abc9cb14b8a68934e16fef91370adfb862ed084b98e05380627b493dc746fc4ad60bd80d8d04755120410a8db0c8ea58b26eb62c9fa5f6398c55f3c0a99ca41aeee41ee7aa7f201bfe7b6c6396134eeef45c038e8de3a1fb8b1330a156ee2dca14ea17f42f101ba0503645c5f4ae1593a39066e4b07776c14dd61cc6222d37ef109c2edc8cd01643c8a0415022c4e9ec009058dcceb03266c4c8999acae9f4ae20bb2165d1d647303b98ac3153faaf3c7055a42cedf4344e465b050985a54b1d2e8f8bb3372c680f97910c8a7985c0426d048d7b532a809f4c0e12a4a3c089306e23c787fa99f8502bb2ff12259a99c99cad44c00039e04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2ad718ded60754768c30564d1ba2af17db4327b76c2b604577f76dfd9748f57b","proof":"f2ba93706f0a9665a559462b107b53f36d64f284062f92c64ad0c804ae510d241235c31a03242434f59cedbd984a4738c5fa23b2db28e944ab540c5f816c78057449f3c56a9a3d983f70daaa58b8d873d6e18d8a2ec990fc9ceb562829913579cae64ecd34aa19be57507a1552b528b624d08516d97bd64596fb254806426b7a6671bb6025a90be355c457504c0324946b9760eeaf7eaa7f1813b98efd821f056c3881bc8b69dc6b87af3e0b57dfb8bf8d2404a39edc7babae518760235d3f0b1b62c918ae90479a8dd40c3ce0d9852c9cd86668e57dc6af9f8d6c06f2624f0ece99580089167f91df27d516981044028aae8a30e1293697365daf849fd05015102c41504940d4245d33056a59312c6fc2382e011be0a4d583603363830c6a6c0271759c60709826568fdec2346dd9f644a77cc98f035e2dd9ece607380c6c2a2eff0461b0c259cfb98e9ff5f8b26b3a3ee337e4004486941f1594ceb5eb833ce6267b87812846dca1d2057b98f47e177bcc3b3e57f088345a2dd6c3e868cf3b0c3b5215fb919e8f910f113c5ee4b0bd87fa1b0eb929061ee2439136f7b2b0164e29a52fb87125ea33a64983576ae0b2f4dc0d59bb5160e9e4233a3f6122b72e2e4bd750ebec7dce6c17e61b937473d9085acd1596580f0ff3a54b9bdfe8e82d90b18cce4ea159d0dbea02c10d90bf3a00a1a0b89c591cfe85398f3abbbf9d0aa8ded68807822879fca1ff421e6c4b4349e8dbbd7e8d1a51f3a386c6c0b94d35e4342f185d2b442632056b7c748b8124b2c7db0f98fa8562f7a84662d8a4945c3e9d46ac617b6064ee02a6034288fcf2e5b4a00a4291877551f99ba4f6a11503d87a3618821f0b1d42500cabec671b4c3c24a30d9f66e38cdc9db4b937bcb608945d7bef9f96070ada95e64e6bad4d379d10fb93ddbb0d211d43a7ee0404e10d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fe860413438d48ff127459f27e08315a6f1151e17a0471082d7759975d43f325","proof":"6c592db72fef8eaf640ef916696b41403b4c755e677b13e7ebebf9d1a1f2a83e3cc0f1d74d329e5a2c876dea9ce539de9537ab34f1f7fab1bf7503a99928e86bf8008eb1b93ec653625794880ab73a48459c9c2a45b13535a5143a86588c113906e6d05e724c31220612c4d4e74a481559165b2f565f6ad69b3892a6d51dbc6a7840f428b3fdea9797dcf94ce5b843ff79af10ba487bd7a0b4e5e6034e0f130e589c4b1025668069dc9458b19c5b29057a702121cd6ce9b0af05d2ff24e4b805e3083dd2f5af9f4bc11921b3e365e193ae5872b618372824324546a817fdee01f0de37de653d6dbe67f08256cee0fe4f2e04d848b4218794f0ee3a2a2b025243bc64b84a66529f1b202c6af904a21ad87b528492eb02b56f8c75f6fc65c58b4cea03a9d2028776f80e70deb35b4d06b6f42c3f9ea050ba8461c4c725820d844972f5e4731871b8168a13c55f8080a115797067ace90de974f0403b68955abf70fe1819cf4c87e06b4c225b8ed822bd773ae1033e65bfe229374dfa2b4471e4279403ac477b632e151bab47fb022f12dbf42548b712af1af1188f7eca69ca0564b6f23674904da0bbfb40c5e13f84ae3162a17c89d7b1e827c0ffaa6952bddc46485849917f65bceaa5ac25a77ab658f73b15fa60ff152ca555f1bc90c78568160a3d007e38bcdeb5cdcd8c1365a97647a3c6ca4d5914dea7f8e64506e6b52304fae1548c19abc6faf67eee81f95fa7982d8403f80e3585ae3906de5c38aa51582ecd8279ef1263c760ea4a48f9d3fa0c012dd2dfd40ea4dc87559f2c117cf75a3e8f1b02a3eec29891d47e8b8254f1a7468d85331c9a1b06d3e05669366583341622d67f0fb4db10f55e1523759cc84d493b7f133d45039bb25740801d1b6b044d31153177a123535f85c310c17dba63720574dc76cbf5fe9f2d4424dc5a0104"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c260ca443d46a737accf5f24c32f101ae4a754f6aa878e8d0d901e4d19375e48","proof":"fcaef34cd45a2e3cab9f5d9bbb92988451d266f90d0cba4995d629d625b8cf544491e5939b24baa8e0e29a0c3c94b36089cce99ba8dde07d2d40c99868e08b5b083aac566e859cf233e3f062af389e9a44d4cadce2819899b0bbb0105c3b734c9644679b4ddd6d711e83231a91daca8830e80e31a5f98690e8457ba7981f96434593b0f91c03cb259aa911c4107ce59d2fcf1a04b06016d8c6f282b5811d7c05bb2859bdb2110810949244774fcabf6d05880de1e27741fb0919a44e209c2c0d2a26c7eb12253f3b56b50f323aef54721cd41aeee99c2c1dc25970cc45697c050ea2526f3494c8d2e1757bf82ee342d33ea4146250f03068661ea09aebe21643bc46cbf755906d8435051611c513da0a47d9c4807542b98405b97940833a75686e973ea182d9a0158d4cd1640f15a29deb16c95f6e18924d9dc234e13d051509b84c176b20e009ae21dd1f8b953cf0db543ccb3ed66c5c78892c471bf64ee81a724fedbd818a212c144f323fbad92b4469b06587eb296ee0fbcf9d0d3665cb0c7cc38b0e419374a971283899b138c65ef291086c5c7c7ae56785b64e5ba19955a0356533bc4a8ac7ea399b20c80ed3610c636df1d25b31e5cba10c30adbdf746601a1dea3d0dcf38b488bcf82bff178e4cfef593d7e29847f6ecc3974b492f62f29311a1ec1d5d4bc4f4ff44a53781d0cc977f8423e7d623dfd8968bbf451075da01a4863c566bf9a18e17dbe0deeabe1c2890516fc5110a5e65bfdac8af9f7a4a5fa1c59ba067efb96d2fb42c627ef2c6ce79dd88ba612083f8bd0bfb99ba54a247179875baebab9ac457c3b4eab90fa8a96249685a443ed0a9f28750b0d849df37492e5808f4c665b7c9b6c174cbc90e81d0a92b7fc3b3200e60ed6217f0025288fd8b8c86ac3e74386876c7b8c3a5b770f7986c3d7dfce3fe2e70a9aec102"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2c9005573497db8590234fc967524c82c030cd77b5e85a098d0c5e564a684b5f","proof":"e0e82477473e3c4009d5d7ee3306a7abe73f62094a34efe9783f41e968c8cf50a6ba259ab75470af64b456e956ac136ecd4d25e03a4b0b26c41e22e29e6d2c5104a046da8c0dd069c858afac73606fc4e933175397e9c105617c2554d6c00808c2a4c9084430c694b1eb238336e8329d4d01eda1eb29b10add31f14c5dc5061a40eab84b8ae8ced84f554d6157865b960f4c15218383eac7a0b07a36eeb95c067ae19c50cfc92d35f338ffcbd9748be6adea955fe4e9b15f17715452e7432f05451509b52769f934c753beceb331ece6c4e7903df3da93df7cadc83b4bee57020ecf05e63277a1003167b02736016b756dcc5c00b7ca0a45d2e40ea1e2b73004102a5f7a8a6d8ea47545c9bafd6113a008eefb7cbf28e32cb31eb675325c962648f34eeeb95ace599db01265a442b42a363ee874d2d7a718e06fe05e16438e5898d54da7613a43d478cdd45ab7c711cacca0975182f48ae7d2e61faeb210e128ea1a945dd90adbaccbd81afdf0c68a6128dd9ec61f0e174be40a4c10c57ef80c20e95d6675ca2a3e6ec3261bfad6e9a9b2c1dabd38bc30ec98345ffe2cfd8c4ffe5cfbcbe04482ded0379f7491404a05bd26e9a056386315dba924b969079b71a42104597044a9006295419ad3c7e7996f35ece42b7148583dd8f83646dece447a4a5b4d73c65a5b00641219bd3fecbc69a7823cf31cdaa8a025e32b9c51cd2bac792dcca00b3fea332ab9cc48c32bda0d0752ba45e9d8ae777f11eba4dddf36643dd36b2d4c587d54c0016a1fbc43a7f8ef50fa6e38c54c15e001618795ee6528fbe8e826722a33eadf2d0029e4a1b9f3509477fb56468130c8a40db261332823c16cb40e153f41ea226d7f6b9e38e9121a47281ad9934fb39059b569d60e043a0d07b5d91b4739e6799222f748a522b73ec42fa4a3333d55d202023c7c980e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"183b1cdb10040d7037035bb9d4202fca72db1a588a56deaa60a0b1a528e83929","proof":"5a47e998dd61fec674c2a105bdcdcca74f4dbf9757985f20638e64cc17b95a722099b581180e78455cfe45e4298b886e61d3cdd02716fcb7d2ffa3ad54800f612edebb86130b3f502a308eec81b151bc40ce156e85bd13b6c71c5df0dc7e5e4002f4611e1118c28bfecc86e2c9daca61803d09f66c9d4481d2284d68c00da81553b5624ccb93c759223d801249938d104c54bcf78847cd01563e4cf93c1b6a093af4aa9e4a67047f809eebedb66a7198bf08dbee372ec61c6c68560a547af6033a8e621ece4ee1258200217be606eabed1c1adab7aa568d788a0ec12bf14680068256cf90caca1304dbe3f40390f866ff3aa3f123987776d29d78997ea40ad5c7ab08e8bd890b5e63ec57ebd00166def454284c46f59c4cceddf8fee41dbf73e12d73ef545054e483d7fae472f0963a2bed12cbe6513cf4173e673a84a942045a25f8a8cc6a9e71a96001cc70a6fe4858b371d488c98ac2d6ed47b3764483105144cb90be9f144feff5c794f16a53c25a4ee9cd0ec7066a08af46793dcbb180d9a4377abb6454a6fdd22333a74143f855357a667ba0c99f1dab2e7255a885c4ad0db71dc583afc33224c7d519313bee595c8df507c0cdd17195685074ad65f09f051b6359a976f2f516b793bd464775dc37b0439e7cb813f16334fcc74bca8500ee9935866ce1fac472211c3a1219f38dea8d16c4e0f4e3c2985003c1826ec2c5c1c5506bde60e6449eb2a025a0b9830f993ee41c822a7a1fc5d916afacc5e629a1960921810bece11e7ed04c555efad9e252164552a63e2758ebb594454240e9e4e35a0a459bc9b8f5c7b83fa23918de6427c56e3faa0873b8e491fb2dc1e316d74c7c239b46e364b41fba532a3e3241a0813d2a3834aee54615ff1fe35880eaa7359f14ec2b8ed5bcf31b96d29ae346a24b9091857d69e826faa660d845700"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a049dc5050d4868cf9d3705ac3648adffa10077d831d9df4aa53f3004fbc8b41","proof":"665b8222f80f36c7d0ce842aa0cfe1eec0341108bd3e5b2c901f29ee11af781fa8f2b6b30c76bd265c24a214fc6f5c19252f881befe22eedc089e3d82265f83198c83fefdfd51e4037e1e3939e00cdec69a46b1d4dae98c0c9ef820f77ed5823b223ef1cadd8f9f703289a47862a57fdded68da75e6afeb2e60e803c0f427127f636dba67f7ac41467b1d77c396e6da7118fdd2bc2d55f33c2221332f9d1c00ad2141a03bc66f9860ad005be668ec1448b552e3b8a3d6304b96ce314c60d520776c2e4a4e59051c9a261914bdcc975fee450d5ccd551d8d6017e51cb7a301c01bc9a93a2d47c1a7248acbdb135329ca342e7a3d3aa1fab0916e0b7ecf1c353068072399112bab28427bb5d687528bb6022b5f7cd20a3ded34a53bfae9741587c2ed4e884928160723513b405ec5a1bdc02b53c529f7ec4487b72cf61cfed3d437e2002951e7a959b9eca70785199f536b88ee7112b1d341edf19ed6f598b295680a71334fe39133a57053189b7f19f535b0ec5d628e6e4fe79291a3ce469dd7808356507f8b859afe7ab9dc1ed4978e122636349f7495eb5ad26563dad5965569caf8117c3da1c881aa2fa5bc77a03cdec10666ab99119137cf4bb45cdd942392c0b65dc184bb504a6ad7797b143043e889307929ee474b46a699d99d6e72940b6f6dd5f8cb0a4b649ccb96d448ee6679acbf578069922f5cdaf8678d1364369109677abe43b88be217b28461ed4a812c3cce01f5fa0b050eaf9a72fc0137430ec220384d4b21da6182cd8b94c578231e66d6cdb7385790daadd979f8760f1209441f8f526b2db6faaa188d3f259a4b41f7329509a843d1c277a087b5510b306b2ab798a14393814693878539769e62b5fb11ac34e4c2a5c8d725f129b54600bdab5130cb3ce66d3cc04c473b25a270bde958e9c02af006c2be98025fc050b04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"94cd41fe4823ffa03b6188afc65aada6e31588d82e2c284a7b0cd43d2dd6bc74","proof":"0497f5383b635e72cd5d91d9e58a82e564c8007510ddf9018d263c75de0d7f645cd5256711a99644ec634ea9943fc6176db2cf36a31f1283f08767ceb292500e54438d7c5d8671a194b5b93ae1fe342123e600b583dcb4c6ea29605b5fd1de25140119b214cd0af76c8238552f3ff2fe75676dcbdbbd0bee53200d85d846a26301d62afb4f0aece0c53883352290b87f9a34c341a7b6c2d72fd687de7f1ca50772d617eddefb50098ece79dbc28ec04a566b6ee0c29fac053f02188fde78cc0825aaa5e16aabe0b642b6346893b2a92fbca71a350305942896244299583d0d025cca5ad604a4dea673cc5ded9aae41c3c9ccae1881f8db7ffecfe0ed62e59315808ba9eaa5dbac262f2e6ffa4472645a0647b160468c5f79df45bb2e5f87fe2340b42af0676bba89f7507ed31fd0790edd002584bdfbd0d94eafeebb7745977c784db79de2fd06bf0a5544d923db9180d2c6e7c09e0fe48c564ea017fd778c69e462f43582c9c4c693b2447926312982d37dd4e7b60636997e1a86d5845f2c2e60f2aa33d32242eed3288d301dcff3f371238ab393c78cfc27bc0b6b6707454c7e8f330338a8d00cbf2414a9b4581c62d03063c4f91bf854057259e036cdb54cb00b8dc7625d1a11ce5160b83f94104957ee8a1dc139d22e57c218b740dfef0f5e8a0381f90bb43bfcc22b5727ddff33ed0bfec5f6723d9979c4f2ee5015e06d801ded45145689e7d0cb8550e353276f500ca409d517fc8211fed478f702301ec6d16974d81df8af94a518b2d8f90f1a6aaf336b2a8ac3a718378a6b6b0f2463a857f663b694acc3d7c0512a24f0e27ce2f5d00c3e5943201d6c1720b35b1b46cab6602e8e74cc62f3b434656916e97028bdebd7e47e28ea8c313a960fcd8b006e0f9eeba8d4d5565d3082b0ae1dd25ae3f979a157416817c523d6667f883504"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"660c77695761dfbb5fc95d9f6e21514b23db401224cb07654ac2fdc96eed090b","proof":"c03286e02cef2135063a46668f50c9c590971f4071dd50ec063ccb28a9810d3a6a88685598add2affcca927e4fc4f57480fea34f814cdeb3cae5b49beb6c1f3cce3688a9029d86b2de81195ab93872b0c128b957a0d1e94c9cbd3fd084ef0603fc90898442d81820ebbe27b9b4413d1bac779f31f4550f421b0f238e23763d09076d431ff46b28c5fb7fce749bd6e43c9d1d13cc6078c9477a8c88cab24e730ac2bfa12f0039202524096481e21bb572df5515f2171a91849be32d84b937e907317f52df3c4db8e6eb1af9868cd43a924d0772a70fa7f7bd8a156b137d94500116a557467d21752bfff638785d2df91aa3cce3974a791999f0dadbc01dd3175dac6074764190aff45eeabd61fa608d3b46dac66e02b8d9f7f9be002232bbea7e160fadd9df3e1c15a383fc9f69585a795f613a14b3b80cdac8ba1e7794514b7bfc662f70306ba0f221180306a337b754117ee4845542f3385888e107543e9a7664cf37af9e5b2671422f4dbfdcb4d2ca7a3eb05597de0f35b2760ff44d1e735dd06b4d3f4c40adf556ba87df4ec04b4130274940500311980cb8287dbc73b8326afe508ed22019df4c73f8dbe61ba9be2db78eae9d4afdd37139c180749976348e720ae125056131419bf7437d5d023085c7120622b021e96a8d94543758a455765e6a55447b5403d289187ce3e49857d0d8b2d15b75afe3f613437579043d2fb06136fddc33cbbb4d370dcaca4c3101edb84acf091c55de785c1e1805813d7df45294693c82b80ce7565a0d32aea4b316a2dc3958d1b086be18990ed354c432d482a54a32d83990ee772de638f6ef0ed30d94da6c6ad1cddfc0ef1c8be47e28678c74b892bac39cffe7ff2403d3e75ebd94fe10361989f7ab4da8d0c33a4a04fd998b0fb0875d8941bb96cb1b9e2a83a58ca10c436c9992a1b044690d395007"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e08aa10a861f99dd2fa519056fe45cebbffa981b08611be57b65cab6af22ba64","proof":"06968ca7fbbe24faa1af150cff0c3bc5afa85a3a334a4b681044c08ac932c617023d41f189a4cc34191629c33def4f76bdbb299be40924b34d8c0242a2603f77b05bdb8211e7a2287a948eee3ed9113ee82dbe30a745536addd45677405df22d4c7fb6939bf15a5cef67386e0827b32d8cc19560505c43ea7dc501a72234963d7153ad71ced3d678c5a97adf54d46383bedc2f438f8db37c88c29f4c13668500aaaea803d4f1b4134e39a31c151c936e6516e322f57bb88901d7d69222ac1808be12bd59094a4985a166ade044a9a2c883ae8384e6247a87af088b1d5b9bbc080ebe73ab37bcd0f0ea96d692ebd58b898f225811291da6481e65d4fac370a355007a9a9759d7f5f351458865c84aa058ac8f57201f0b9297858607146d144d4578454bd146e69e960bbdb5581f56850f0c33edeb7275c725dd175568218a0e717ae8559a3eeb1ccdec59b98692ee3072f8566ec8065a80e9ee13fed93469381014196e5e772acbd4788940312708c7b5641a05375189f242249b7667d28ad34b6aacb3a84718c9cd4bd7d6af236434b6727d188180d948a0758b5a12a3db6075de3adfcaf10fac9c20898f52cf8ec7caa7e7e5d0660e9e6ce537d96b20361f6862a208c8a3ac96776957084aefd13c516eba41b61a28848c1a3d3f3b90f46a330c08d0a92975cb872885d511c411b2a385b76689995e6b624c96fa3fae41d935e68ef3ab16a554d5abeea015e1a91d9eef361ebe3634562c89d7e52fdb5d5401f27aa2df20a1a2edc307d40befb44bfd95f1ea117a3684b34af82c826c083f6c861eedfaf357532602257e531fb2b6158d31a8378b07be6342d8de947f995e6bf3a6ca58c056c460c735b9c6a43a3b4f2c91d7c43eb0b7e9d0cb52eaf86ea101d899555783ef5835e79e59c6334b4a1ba0d4ffae4c569bb3da4135b74b9a6200"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ce0ee440c38c0c88b1a9d855513ff79ef3489a95e283b65b780e51f432df5b52","proof":"a08dfc1458b218248af0fe709396d124942217a5217690e91723885f46f4ce184ad5902d4e8ded3d01723e5fbe4c112cb58ae0ea75c55bb79548e284c164336c1089409b6583ccbd9cd6579449d86a646242ca14e0a9b7beb7e63a77498a486eda3cd43bc0bd6e53939fe41c2576acdda3b28719588c0b7809a6d7729635035a2d483da508fa9e4b7e32a6efe3b7c8a736a52dea44dabfdc986a4dc0a1f17302c4e9a8d980f2633e47490c15295aec9b3c9255002ad6fb3e7701e48e2fe956003664a9635b6ed33a321a73dab6f4ac0e773da5937661cbb7aeb00feb7cf63d04a402f4c7fb0cd581cc15c5d8c8ff1ceecf2667df69ab2d5e038184163b243b431822d27b804f7d4e6952ba6654a4f8895aaf0d4fbec2e00183d3efe811cf1f5d4efd01fcb8ecb7a9978d48a7109068ef9e71423613a49a683cc87c351cae5b485ea6d96e11fe04ae6b9f986cf276612f488264c938369b492c818b21f93a846574eca99d8553140f53a20fb94f3cd311bc39bfa9371706242bb8552bc3aca55daa09740a58e79074b127327b0baf2e70b7fc8347efc8ec21382e7076ad91a8133041228fd00ed4b9676f5c33e0f9afab402fe296079621ad137b869eb4f8c13cce898ca4c1d79bfd583c1c42785d97ec9fd21edbdc0eff8487b25f276e41657fe0de6e8e5b2d485513b1835f83bed1a00d1378c54737ff7748631fe61f70053bca6d75dfb1d5030d2a481e834980169b6ae8063339b70f7507f60d5aab1f6a04a0f920f6ef75cd645efce80f7a803f570f21fa54e9369f9cf5bc4e5959fd1d6656b5b8693a7504d34be1da4d9a55070b38f5ecfe0ce0012b4bbfcde85fcc1d5800fe14e9314a93a46a848ccd9fb6abb15f824d6885a274f80245a4c1937fa603dd350a2e4ded5ec0ff291e91d806ca8ed5cf46eb8082753dc9f9fbc0a3543802"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5823192d436134d0cc54723c3d0bc8924445a2f40164796588ef68c6a39c274a","proof":"94f6d5d5b99865aa214e37c752202c86a6137dc06f75bc243e4938fded3d0f728a84868a840b581c988339b922c70272dbbb6d91250481cca74fd03c08da406d7077a87af281ec5e98f0b7c0dd2cfdc273fbd4e1e9ae2d17e4c439977654155732598a31961374a1625b3ec4d70e7a9ef01478a966a5e998d374b3489d29991029b73cb4456aa9a1e6136075ca0c1f5ed9b38be5c0133ba1e263d85cdd40e40d075c26fda98088c49032a13d14e95773f26844b27cbd1fa0974b87af3ae6650fbce7352d85c1bcf2ce2c6e24668d6a3b3ff1a477b1a8066980787dd94d87c9075a6a4c47de384492eea47019fa615e0b03b2dde1a3c4202c0dcea24960e2753f92720b59029f50737c5bd77a319d1a890619ca190d56ae12ba35cf42a2ae413eea675c2867788356735e8bc885628a65b7bdb0c2d94f7a7b99e0c9c8750d2748c20560e925f78c3c44add6ea77ec595d2f1b3d66469cc4385869950d1ffef413dea3a3a5efaf1ef58936b52375e43de0ccb834b5a2970a55f53324532a7f7145cc72c53ca85090a1228bb9d3250458b669a775ecc213006e5899c0082e64ed1376a7cc61293495746d6d020559a20e40983b75aa4cd6729012aa1c05fcd939789414c9120c8f13a9095fbc2428becdbeb51f75dc24d8628f00abb6257fae4761fa1745f09b1a3e70cf7b12f8d5dbf27b2798dca8924aab967d9b69b8da083b11bcf00d5f48e32e6a8fc14f9778556c5eecde01f9f8edaa95749615f3606e35010eaf40ea0addf15559148b002a1075557bdb9f44ab518e2c8b234f6c387aa839aaa7e265c6982eff089d215655759a6baed96240a015fe972a0b624c52e77c206188a439e5f098f716eb2abd9c72131ea8bd5ade225bd66b2c7bb760062ee40df10619c9eb65d31826eaa216e03a650f019d6cb4dade919f85a29945763ea200"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aed4db5b9d5439c336c9c3a7751285d9b425469bddb4e26809b1b2385bcad025","proof":"448664b326e85dc016a9812224c798a142c08f0fb1bf451a980c785b94d83f4f66ef125f61bd7685725b3c979dac91d7412661d9a1f49d9275eb76fa3d43cf33929511916fe8c1173b5efaf2ff618c02b2b2ddc47bd8c37f3460b2a046ba2f75bc5a4ecdfd50a06915b17377a4fcffecb924c66df03fc3bafc1abf9009320c31d8e3c512a2e2ac57b817efb062b01109b3ecd5143a9c3192fed3c2a8da5f0902f27f62fd1868aac9fc45c3493f84de3d04706c52fb67502973cb493b859cf50e495bc14194dddb69541fda486b10fe5021d032c9853d022f0896c55ce1829e0bc44f966fbcb40bd1c4a8cfb187bef348b947af60d2a76812dc53562be506c766f46f62034981d418f2e4d611b532d3fade5b2c4d011abac79f7bcae1bbfe6e1b5a5d937c2523c2cd950c1ca43785c7f14e3aa34b942abee1ebb9cab3f25d4e26281d6d4811b1ada6ff9a4e652cb2a66f63aeed77e147f77ad6b683a959a0f928f866ee1c7cfa60e52bc456e367a28491093de32f1f10496bb383f58b0b0ffc35924088a4052f53636cedf98a04b12eb96e742487dc977c0808f1846992d37a12b8a49d6bb1a7f5be0d79f0c164435bd5baeed7fb52d3dbe5f51b6d99566f380b8cb837fc8495ccf3fcbeec2e602c09900f88e1372ba7ca917f879c3d4e370307a660bd316336ed1e648d0856a869b3c69d542a03dc1e67306a8f0e7ac50c3b55b05d0c640615405672654d9bb7702f222a38d360a93e81493208015da11b89617467118e0bbe3d0c26cc6061219878d76322d5732cc3a8a8764b7dc53a9b793c785f1ab6cd2b4ddee50015edafc068de53ffe380d942cb81062d07d6d3bc4415e47f2aa4cff499252b091adbcf8ff330f532614dc8c4f4371459955a9c1dc30ffb3fce747119e184ca51439833fa3a03fd06517a0bfe027eb3bd1aae5466c000"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f2cb2182b9e93777112c54b8e97de2f96f7c709440d1c5ea789b4bb6fdc9f761","proof":"a474750f6e7ca36c2e8cc4e61917ed4b53c91657caebb9265b96b20d9c0cb424a4d5a5ef5ab6b1c930ee2c0e466a0d9b025c2fd7927b8e8df869ce36b6ee8a440e1a71426f797cfcedc031b39e88e6fd1cabe75480aaa690b7494ca608ad9e3d4849cc5a8284cd9ab3ff7e2f2f6d18e537782d4ea3b93e6911cdf84e92ad152809ea998c6971c8667f0330dba1f2106e682a19f069bf555aa55fe03f4a6efe0332ac4be93c40ee0d19fa2844ea9f7febe130069e69a325db4f7b2bb93f6efa0b5a562b78632165a3776bfe9fab75ac3c495e5b3be2dede2833290df02d3c08068ab9cabd4b05aee6ad80ab6444df0e43bcbbd145c138ad0f6453de81514a1d2a4029f529ad9b746a593827d94290ef073341104b867007d0720477a930e75858eaf617efbc498fbbf7032ef3c66644287486eb34b39355c8da9b46ae685d695706bf7feee7c4cee0ddea9b96610f57d5d34749717792f0942a66481c8a50455e4a7a6863d494e96d9c636b4f9e7389747e14c31ab130c266bf654729cc4c291d4cc4896a885a891680fe139d87dfa6fb64221cb2c482ba58e7110c0890bad64850bdc198fa3a482ab37404380de8b6c6c084d906281484d27ba5511374849d54e08ecb52881bb91fafa6ca7dc44e91eebfd8921f14d9c20e93a071cc00b42a356265a8852ed07ef9b8800ce888ee10ea0fb514dccd5784f615deab23723e2b2e8cfbe702293bc959e8083d4113e32595bdb8fdf2a2c2cfd394bd9d75a954246a8a2205cd245c00fbc8697d612deb2da12b57cc0fafb3a28e6f4866b53b2e1c0fb46e637fdd04e7d6d1935b75e8a831adf55031cc20419dca9db2661325732a116728e3e7b846e7b631a245ee02bea4651426f468e5cd90fe6108c9d87376e40431c480494f23133b00a5fa64367d02e7266e3468727b7664526745b9234f0e02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"04ce31546896c2051636b8337c75c161c894fc3eed53e23df312df733cbb935b","proof":"ccc8fec83ed3ee4de4cfbe17d51fc49dfa3db22d9a1bf74d8ed1fb7e03e47d704cc1a4a8d942a2de4731275fd841740210469ef1c77d3be3363a3dd93ada157b4e501b00f6d82327e41c95474edda1900c481b8ca98a6f62d65727eee71da55a706a8e33f45695749d96f25ef57fbcda573e1b6ec417740b28806cea92c9334a07c9a867f76e35e246f6242aed3814d6e324e93d312175d3539c4f3896671c042b818280d3ae1724ebf7fd93065bd5d3e739713bb1388721622c095690e93507646cb5680f251b6b61612699a0be76b3a1f4e2e10e5c7830cc35655cb8b7c3022438f6845fdc026764a6688f27f3e40e5edf2badde7a17cf980bd2788118c072da8c23084e8c60f5257da315e31088a693899203c78cb45b49ac539a01f42048cc91dd71bf8f3d69cebbe57aca6d02d7a85b8a27819c6522699417695716a87bf0f430111d87402bfed0abe72282adb1c0177758c94716096d54f51fb31c7f17bc377cbe82ab0683e1a89d75f41be917cd7645ef15c80133a1a1a5801d25004ae052065554c9a9720339ba75f7c2cb6cb3c1f6565634c9f5b608d2a9aa909608b80424e63d33f5ed4838d205cf8fcd3388e9367d06281d15346481a577121830a809ee52565efd87f6ebaa32ed9b02e98ae2ba873406a902df7796b58142e40a0ade373ad1db54d4dff9f1e07f47d394a7c06daa15cb96613a1c307a24bd8a7d06a9a3de2fbe6cf0f69235ddcce30dff75fc9c8b78e066f3f85dd3192fe57925c09de7a4bc8da2b59e00f9f22bc6912a681a520949334d2e592196bf1de61102a8bb7b4681977fb41af3945cfb773f0825bd4742b59193117aeca4f69534d444bec273ec4cd6078c809131f12ff7012c89641a74e63ec3ee3122d5d3e00f2b02932272ec9cb128d73a23699fef8dbd7b1ec973f4678e5ef3bf1ae00cc9e0990f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b699c2ee3647ac8c96920e597ae8c3ef23e55331d8de184e867327c34e89050e","proof":"a43754bec3b737a43570442a5d67357f0d21dd63fef3ea3e6c723448aef2c57c70c0e8df5aad143513818bceeb87a07942f45ff088e2ad91cf18c98761b4d76fe6141785d08e120525e6390f037889d8c1abb4a6b4a9584bca34587fd223b459965a298b6c891a3919fe35fb6e83fb073352e067f11ab2656cf0fb28c8d4e933ff6e1d8e10f8acc383bd715a66808da6986198896bd64e7b34362ff511dd1207f216fce9dad49433969ab50a6b47e2eab70b23d438ac4f581df33a7def2df706a98de1f08ba37a2f94cd5e1f3edcd8a0bb428a2f2e90272db3b11b55720eeb09d0cac47599fa0b23187ddd4713bfab87500443d10fcd105b62f16a61de9c634908d99fef4f2cc6242820c8828da25e4025ea88be03887b700b7cd6d4a8619322a88b7fa650949a0043f77561a0c54a9bff364f4d05f09051d779d97af967ad2b78dd7836eee97545b763c051df9c68ca2fc387f97a976e18f3664de49a375b61826ec38ceeb26514382e5d37d29397f2752b7a72a33f911bb5f6730dad19c536a6e950d66f5d34348733e2b622b25941e645bb70ee99be7dbbbddcdfd24eb80cec19fb2855b11a1146128c53f6648a0f08b2f5a6d75d1fd98b523206216d1b7e6876ec5df0e4e44d956f5339fa380a2b1b1399779d5bffe02396d9a37f9aaf305aedfba6f15db87b155a2a9e3c7c2a9ef77e4f161b19f9c9afc4488999e94c4ac81e4bb99caab87f3fa989dfa321804e10099e1dab28bf762a2678c89ebdda181cdbc2b2b1ce05c8b29199e1c82b8a6ffff4b653ca95f19a050a48a9de74852b4ca81bae6b6ad5436efc381aa6409f43ac60b78d6cad2d8345aa5f3d90bb1939764f9884219f0eb7ac503369e956ee8a9a26c2ae6d24273372b06598c780dc05004d81884554f68c770fc01897d2668af9b8718870ff09df685ac202e0f4ed01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e82a3c9d5967ade5164bd3ebb6698db67562f30103a973ebe7cca0badec74757","proof":"7c0e81aefd66760d2fb35b777d2c32cf51033e7887b6ca66c99ad6a59925be3ee44d6ef28ad6c82d680ebdd9bfb043af145964cf71d3bd12de2d6bd0dcc15e6e203cdcc463ce3b8aceef6dd8bab19f65a5eb91364b14309e9b67db5642cd8756d6aa638ab37bb2bb9683d7317a614a5c4581d2a96ed6a84567ab1497b4c3bd0f3ce4878d2a274316d551c5395957b3a43fa9e7c6833a6a21848dee6ef4ae510bbf12d58d8bb7f54d7a3470b13e70cea12c620deffd92f2094d6bab041eb3890ed57d39b0b54bc9384e0fdbaf61b3f3ffac78c865cc461efb0492cd74bf3ea507eee450d843c2bfc5dc705d3703cce5cd6c43e2105f734e983f71de8d19f1884856c26308fdb0ec82ee3522398b174b19f3148c8769c9602827ab9faaabbbb520c6ef502b5a7d2854bdf4f1673e8d028a790389a740a5fbecac4057dfc1757f225c65dee112d61c9d1344ea5dba6b368f67f90887475d66d99be79460b8000b02640ef2b00a837d045dd1ac7800359d4eca344bb35d7ab6f8eb02c282ad129e5902115a549c5e9ff5cf6fd7ad000bb886b5947af76ff3dfbe2e7429f108545d6d8c069b52da5c1956bd7528b6340a4562c81030d06ad148db759002618343642dc0a1792ac4cf04cf9f262bf4e3775fa1c6af4e9dd659ace3121df1141ff933322a69e5e91f3a0694dc9e1fa578f7cb5c40719647752e6545ad96fa0d53a1e8523ebfd141b2ef28a85ff7d097112ddb06813c33b2a359210c227430ca9b3c55668a67e1f6a3d4a2fe8999e7c4ff011ebc8907850092038721f56c00c1b08f7b3beaddabc13e16138b071ecaa574ff867ea6e5b7f247aa491da4393c07b240912f22cd6b62135d2c4aa9e59c283816916cb5bf96ad26c65aa59cbc49c7dea09a05ef089e264ccb2994f3c20d9ab529cfe20e75ed0764495125c534c0c484627a0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"22866a3feca6dd176925358e18a5a53f666980eb5f84e3a8301fc25bcf853368","proof":"0413d7abbea8d6eb0d480e1bbc734ffe630e2775671083d176fcc6f3799a351b38f4541fd4f6e9d1d8075356a0fbd2e3fe2a1d64cd31b8cee8a4600fcb24355f244d4dc121225363c14029fea40cdc810cc522f5d1dcd1f0c83689ab0b1af205466d53060080d557534ecb398bbb46558bdcd3fca67c732e4dee1be3295a406f59bf443bf813548c5111b0f6769a66a2a4b1b5b374c2ae974474257249f547045202310123814a9f384fe1f7313659fc1e264c66c13b7cfccd8b7e4bb2b453053c15cee37eae89447cbdd180fe6911e9c87e0c7e365bc0ce673b98da91f210038e7d6d7f031d2b487b0d4e54e4c9c7130c73759ff640dbac9f1376d3785c1653400af73d268f599063945dd67371014c0770b6fd3d18cff9c2e5ab2352251103a835f2bb3f85ac24cf9c3ef67edb11b4d178bedffc163b6899120031e5566b0868c22dc7b5088a6876b15ba788f97da1bf4ab6490da6da69da50fd0217487246d29d43f4eeade438bd8fe4502b26048c67a849c3c3214803f85211315c50bb6e46120660d01fc2df2dda5bf4c872b9abd1544099f83997afe3deb84cfc2cb04e4a3d01b32226473837eea97bc62ad5524e0a56df1124736fe7888f48547b021bdc02d4447a41dd4358ed59eea432846e2c158a5561e34e9b38c75a435d63926fb267cdbcbfc4e8101b1aa79322e6a3532afb429e4c11ed3dfbfeadffaeba08236a5c8b8df7bae27dbffa3fb284b37d6a27d9985315b3c9af8ad52b65ab856e0af0e2fbb72b25f132244db8e6e253c57fa2da211b99c8fc4bdde646b4950d1c63a2c6c76d2159bd69826129d536b2e53be70673ba76d80884750a88890a49eb02e22323b5fd7ae7d1ab716e30c6febd582df5aa3402935f6a53e1c09e550e9e0ca73bfb4df7b91b5ea62d9a8db8bc2564f14bc4ba300cfdd62cdd5f7128febc0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"96a453498728032c05c3b08bd4448639c923437ce9f2bc8e27217d6f68e8ae0d","proof":"56c5ce8acdff84f4d13f9b55314b526dca3bb9bd7bbd8a0d5a74a8f411d82846aafb1a806a15941655dafbb32808162de5356e21a14163ba52da990d7d2d2721305c564d59676e84c2219f7cf7c9324c094d1d86f64fc2da328593c3421cc777325e7198d1f7c299c83f6f18aa41e055599a31398686c149ec187e5524a80215260d7176f4707b68dc3898bfa4030a916624a59923a4dfb9cc9e2db70ed5b9023c1aecd77d12cdff7ec345099b1e39c9b8aaf0ac213a45035fd40a9c21a4fe03a3fc3303efa13e495e473f40bbed9866fb140b927b438575f18b9204dee4e7087ab2787e4702db13bc10f5b8b7fc605948662b7094f792791167f05164fb8f52fc64660ef773cb6dd74fbbe2cfc9979788468ae3ddc005825ab90baba2dfef3b76cd2a9ae77d17cdabbf2f7a6f3330197cdf2d8ad36d6bc3aedd2a7fc9bac02ce2784891dafe55c13b94b70fff07ba59241a1ae1b584041f5a3eca73df4d7943c47a57fd7c20ea25e2ca818aeda3c22145c3bb81c55e92a1a1809de34f308b3f4eaf8f8d5752f3dd554619fb5f0c8b42304da7905e4e9679cd72f89977f79f1facdcaa1f50e62268821b8070d135b61c26292d84b527a3d7ed3ea2703b92e75b0463713957a62abc3da141c47ad03d5f99bde601eac502c3e0a2ea4efb484713c017f98beec9de7507cf30ae0d6cf41d3378a4728e0070f9f5fc9c1ae51f44574003cebde550d8a4f8076709fae00cabbf5fc1ac5b2201842e400cc7ae0bd63802744826384821bbbc61065c5af9390c4f14f066e281d51ebe371b61c813a34b28fa4dd31ce296b4e05fda2ef60d7bda4de6ae4b923e5e8d167d294f3c3b8f4b1fa22c9b1290ea4e2b8c2fa783eeeb757f01e69655ab1145570d126dc5e0ad0f40bbabb4441660f8844bf7fd3f6d5ce29a8efd1f39af2729b1a222304294d801"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"462f286b7b3da5969cbdf65117d4fb603f7e0f93dbd8156b7c8eeae778651d2b","proof":"665ee4e698340b0114a5f7eac88fffc98de9dbb88f6cafa23e1809517493b4434a0202d81f5f577d8e519f3d70bb669666a5e147d74bc5e7ac5a3381d7fc275432efcb07d4ec06a1999c8ae7ba771ce3889ab430c8aeb525ec49389344754a3ba6785246ab786db8e24656fa4a3128e255a257bf122a71d2141b8558efe671065d791d429b7681cebd8b56b12de0bf74991f5fbf018418defa8870e3ef0f8c057a5e259c3dccaf40661c6f6b91551af6dfd2e8c41b94171f2f21b16e53b098092c9062a643ab11bee8aa8a0ebb9acde58967f5fb16758595251b198e838b930a1a5dee939116b24e0346ec2e52d6df6a3094369523e0d5a688153db756bcc22670a1f90cfebb689333e731edea6718e75df82657897452c44d093df1a3bfd43cce59a374bf161e27ae2035af91877992297883b88d097d7b4637d17abc9443363649c0a6523ba0af4967b6adf0a7f04653a32af9a443eae2d423f9e4a073a124729e17f4e1043d18de4fbe3248aaadb4c1a413fa3a2bca286b298c2a078c05293a9d78635f4ac183801d5420239fcd35d6433809a50b7033d059db9dfbd2c028ceab8dade88130631bafbd1b399b6ca9272da3ee08c79fa02ba07e7284b1ee58341c6ad228f81b1a51619256a770ad5796ecfe7fb055f7ffc02d5fa14f88b6303c49a955636e8c9532d2f9e1b6acca90281e0aa9cbf3e8a70390f7ccdebaba6a7c90ada5cf9a28443d4f7c2c7a12f2f7b020c78c35d31bf75d381abbb623fb14087dfe56c72a604025ec70fb0647e8c1f60699602d76bc9a1e12756f23acab28006a3af1fa1f9b8b2be460d62ce2c9b4bcfc4f76d155fed06fbc68129d3123184cdff6fbaaed6f504434d90c4df8bd94c51f0f2675b0f52e42adbeffef4e1f01e58cfe4991a89845a398ea22d377b8a79de436aea963532f1e6412c49e924b0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"acf12fad2e5d1f0bfbaf450fb3426d9e1ea0ab8ca985266f9e8ade26701d8b00","proof":"60fbc394775a9a89ab43be851c15ca27fedefac4423cc1894ca2a5445655af53780242bd07e6279d2c7d95b719a9ec6491716e284db8e7e86fb8f57dcf263f7a3e55a825941e64361bd0da00705fd74f13e87ad1af2f586da22f83f12e55846c428d887a59aa4841468034a72696d5b53f9241835e353bb7a6831911122d65433b12b5660908eb47534af2de949aaa93e66d37fccc2f03eee61272af4cc38807519f9c6bbaaf861cbda900448dd93761b6fafb545986ee944c169a5cd38f3f0968dbf1c39d2bfd9f182b952a59559306fb4773ec91e19df4a80214f4495e650ac2d76b7094a0dd6364d5514a21fd07de490fb3be2c5c3946f880b9d519f530126085a3206c984e9e1b0420ccf52f3a55ca8aece4590c2f7a791d29a9231eab0dfee86fef4c7b317e54d793c55452e8752571ec6e942a9a860db62548f348a85cc4fc7a212126ab8bb9202f1d1af86ad3a792d80e65a4fd80f1ee13488a3b926dc2372534dc20f640d620aea21244ff54cf6de8acc901f1ecc97a0a9a5801f71de608e66df06cebedf44358950691b10e52ff5debe56d05a61c74dee12433881daa66d37ee37664cc0f739eeacf09b6a881c519055cfd1dd2dad8f60d1aee197d9ea84544d0cd5490de14e6e8c608df90b2c00c4ec91ede72bcb692b30f641b54441f67e255d92f93dfe828245e9446aef91c863559221020098a3013636d7c378e245fd17cc2c896f57617aa4fc0e2e0977b9c9b7b7e3fd9c8a269b9a0acae0c48cd4332e911d9f65ac534346cb676734508cfaeca6e366f1e62b39098310c6d344acb230bb7c7de973b9602a147cf637d90d7a4f902e3d3e3478d650a02fe48b5776aaca371aaa0f53f22efc9c068a531b3a64ef1c0f58a4335dd96c4cdba0c3372b9f27d41946a6e925d676b3fdfff8d79eee7fbdd3112d94e5dcbcb23950d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f496c4cf8d241cdea7fa25cc68578e9d48bc5a6c52511a7e123ee5eefb75d475","proof":"fab462779bc8ff5f7494c7d0d58aa5aadf1230058767cc2e8f020bc3fabd4e404e100bdab1f421741e8830284b1e0fb1066d78fbb7da1cd0695c7f4359537272fc89afeaeafc97653ddccea449f642a44561feb9f2ff71917dae80eb00b7c1639ecd31c87bc30a640750026d6f559a3c29256c1a2af1248f97caf50ba696240e1f818503a65a794031e30d0235764b1e9bb26f1776f1493e18468fbb92943303069e81921dc31a9e566bc2920ba1cd40a04b761bea3702fc896d1b2f89b1c60d2bf8160a8ad405976c8ddaf583cc3c2c94384ec657b55fab575c580c732c7504a877f37b6f87556d017f745cad6038d15bac0b77cf5887698a4d826df65b8837d4c7dc5402efbb5cc9d3befc661bf356212caa25493e6fd805f6e8e43a96671bd86dcec3fd1c9f6d8c293fa029f9581cb31dbd915aad756a47ec49c5ac11691022e895ced29a67fa105c12bc059a8c8834e684f5681f64becc0ab4f2e61e97676a56123ac0361cc07657bf90b6ccdf38b16fdc9661f8e248227ca8d42fe6625370c49bd61cb146d2737472becfcacb1e7973dd98b41cb2bc32d863be41fda1743cd3e5525d755f286f40cdc1bf323d43e4f9765f5909457bf234a2ed2d99c845460b669376b17f956e7f2415584bfaaabded2462ea057855200901ab57ae542622c47cd37139fb17e185b3a8126f3f75c62ea653ebaadd23cfe992ff4d0f4e7de65db12ce96b0a3983c8cab96b3df29a9b704238bc2f54bc221eab8291081353b49676ac054809bbb3afe3cb0e2e0af0d3b774f0f34d585767f561870cfd230d2a9b509ae5bf07e2a4c78d03aac8a0651c9d28419d17b350b6fa6e86748e8364cc9fe52831967c19f7cf30e55132544f9b268ba72ac6cce07c7ddde61be7da0f47655a1da9f49b97a6c42f0c5fabf964686c7840f2b91f36337654be9d90ea07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e2cafc1f1bc024ada887de9cc7dd092f7e3d1886206816179cd565da4b486835","proof":"a037013bd29644d446e4cf66a290729efc3c029201f6858d3f22c4443c04ab12f6e55a8a6ea6de8b8037de42d803d502116a90c192d288cf9e956351870d6c22def190a8cc6e2371648eb6601091416a819ed4cca2c5b34dd4529b0bef952e1e2c302e9e791224f91bcd311f0b9b99359a2e178204fe99c79a75ee66f3ae951b161d6e89b58c1c1a13e6b15dd69602d2b9b706ae54b42b9c23002a3eefbc020ffc699ef03128afa9f64fd0af876207a9187bfac435894d723ef2e5b53f6c3f0bc78306725d2c039768a8ec5fe762cdc0d130940b0ea367dfa45c382d6ea13d0334ae95cb7b4742013b517cc5938ff5ab6555dea4ecea6719385ccef71ddfe64992e3e4e4ac3a174ab035512d23a863ac1147e07274268bde6857c897ecff147b10d37656d1dcc12ca1cd1516c8edaa90d6f0ce95a9c68305aaf7808be14c81153eb24733b0268c7ea6f9cb7528e219d202a538ec4860ce39127c1ff7b0ea7873ac2c05e97a7036ee203a1a4f0321e04c4e06d8ffc38f018501429c084f3ab015a4de1ecd45780138b6fe1193fea7fbda472bd0603af9e0e576184671003cf471e070f23c9fb073b5fba7965f38c1ef1e469b90af907c313d3876fc4810c6ea01acc04944676a4b6f2271646271700f8fcaaaa5a98956354949f797209498010e625416c3b1612c5d7cb08aa2555243f5c0158a7eec98d8578fd34aff92bd3309122ef3f8bc1c3ab3f1cd4c6ff0d81c4373df360bac48e8fe91286026d0af06608641c6c04ebbf7bacc3d95e52ac1111ef25c8deb1c272d0c66e79f7ac2336e76f453d3684c244ce633c62cd3b608f57575eda09392a0b78e90e14d78a16c4b46859dd07a7f9514d90021ba8ffcaba758a8e5f6066df8893c8bd7f51aee09b1012acb9d67e955aeb97ddab56525e00c74f5e70bd4e7f4eb7867c38ee73e522d02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"26f77194f758037f7f599dca9653c4526f31eda6cab01807cf0939e120f28e48","proof":"c037848c3a163e84202ba40e53e13176f61faf7ff92c9752bf21257612f8405c68c2f59b453fa25ce270b88d59ec8e6690e95d928c88ce7818f10fb18a7b460d2c102ed3082e4c318540297c00cb9035044fc506fce337cad7b470ea9f57520a108d8e50d7a417b326b2de2ce57d169a765777d3eaaea7b350fc34084404126fd9f30ef49d04aa252c9d40770fc9fa68145fcca9aed6de69da54e654b01597070d4b3090ebced11063f574e7623cc33c1cae60c247ea804cd9d7cd61e2770a00333eadcc7423de39f8b2d0f0da1967ed2bff65c93bd7d854c400b4f87d753b0a701963251ca18d2450476ec4a9b8f96d9b630426b511b346430da2560222161da6b95f8f980d9dd2cc9b29c6a3d789407b26e451a62c4c6da179956cf33ae603c249d5ce9236bb80fb42a0af1b16fc031a1e41bc1dff95f1e71bcb72b0d9d43fc6679805893ae2bd930a40e368056c23b7debcf5bccae28c8a5d544d1d177854828ee047670506cbc9ce3982592fed68c76c23c9a4b412d9cea5eacb9e52de7d26e2f5b7a1182e20b19c15159489cc85fe3ee2d1dd5daa157ba3286e14bfd546f0862def4da29fba9922f4184b6769b3de18da8ab4650f29f6ceffc0fb7074126e258dfff4c31f78d771edb9b80da00bb07bdbfb16c99c6e7a23e35d454adb5574f17795a2bd98ac960f6899368df69d16d5b5b3a8e0638b37b2f0990382a92d50b4fda70c90cc12053e48e65f7491f8cdd81d7bf8e9d69c53def9c056c0f9033e56bbe2aa9c085657e02af3ce76e909d0da72a06892d679027480f03a622c7a525eb31317f5d3a8de068320e3925fc537679abe6dd332f52a91766f2bb6f53243f1f0b26fa46383e62e4af28c6e45ae58bedb72480c13656f47f65bb8c0c40bb877a1063cc7f90736d8e63cb9b92f36490336ecbb4a82f9934a57b95f88d00a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fc60c65a4064d66033d6cd1d42970ad8fd416a8fead893df3f5ad496caff6d0b","proof":"422613ddb5a6d2c3fdda47cc4f3516977ccec2915015f04b4d468368840e2458229477d7c3f38f8ec896b10756b10925048b76dd955ca1e22a72bf592b56524cf065e0626eb77608dbec65b7e64f66480abc17738ef7ac6bd3d41bc0bd81310f0075511b1a02605834aac3a9278b1c6e0e72611a65b041f4d5c040cb71b8843d11718fa2961c5dc59d3b111b67d8a6fe5c0489d6d86ed37766d21155b42afb087956fc197d246d34d6f731f58ef2702257f1236d5223a7e928837fe2f1454a0fd7497c458e8e6f21159dd6f49b23c5be24b901f6f4585deb84a6592b7e5d6a02de6f531e210e489b63ccab27b347b1ad77722c9d116153e0da67c5a07a3efb3e666dde59d25d35845d875c18b77be3497a9bd996107820051caef525d672097e046a49797d81f9023c82a1f807287dce221d944724a61724826207a9e09bbb3eec4d561fa385a34441801dbf7b6d3ab048cc4f961f2325d69133c3ca76912147fc6e6538c5042368c9072df1af0f7ecd213e3e0d96252e668e3979243ffcec1afc9f82de346c38b85eaac8ed06d2ffbdc5ac7914ae44b97026e41414c02cfc11da6f9bb076ed6b1d01a08a6076f593e8f738ba5467acdfb52756fec60a2fbd75862943ad0e1f6393a869aca48370fc976f4d6b57a2597f6126bc1e38103551165e1b2148aed0f3161cc947c5b9a5aed64db1d930f37ce34dc2567b0220715d2764d191a4bef1ab7c07e9a0c4665acd9eccc573108ba843aa659444843ac06c56603c8870729336b09a8c1df9829464dbf26fb0b0d88b01fbaa8f284fcd9c891984b4ad83bd1658ff22211f5244e5f3e011fe91c2e6e7e68027cace8671ce9a326a673c8a0ca5b169d769cadbbd9c8ee3e489ccab1facc548a433b64fe38d380b9268e38da41fc5db0014d1f2da054e4008c3dbddbd89b356394f1a0730f2960f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6ea73f4c064e90472abce26bc3b4f708f5fec30942c66b03398e8d9873490d76","proof":"4c0fffd223723b5cfe0c8f7cebdc91605383e2b474dc4073be39863f262a77093af2c7462ae421268d343dfeb32173d0998400ce3610210b9735e0a0dde5724e9254a8f84b49bd7a491c71b594b89246d58f8ac29f3b8cfb2d9a84ceea5bf3470ca8e3da76f1ba180f68d84694de016947dc242695ccc13463a4d827a01eee704895925ae89d3b3c5809a33e227e5052a8b7d24f31bc8488099b573f0ed5060f08b007cc6b798a7e9da9b3fdba74680e5a887e935a93a2650a838a02c4130006c636341ec713b778c159187a55d802db3b5cd5b52a334ab713386374da13f80cc018db737ef546c465351b4123d72b3eae5cfa30c876fefa84d84104c857a50c327489986fed780627580fc7227f1366db27cca6a81b9e806bf3b096294ff86e12d62e38c15368fb5da980daa5a270b4d354cd1531ca9be87c0999925f18b84484c10188c03f5b66604ff5bb7f1bc864e604f0bff4df2c477916b967854bdd7b8674e518937c0a8ef8cd4924a3bcdd66edf3d6a6ef752d86a67e5523af412d3994cfa639690a163d5321f36df8ca3eb3864a4b7f07d1e830d51d2bd2e10eee5526c4e3c6c44395e81f964c9e24b6b5eacdf2d65ef1ae6db5c8cc82a9e7fed310d8c3ca88919dc78f5ee597af5d7a4106ff16bf2acfcf02559f2090b59bc35e2df8c6112f9275b691cbf199759abd2ad33d874aef5bd5681fc71220dfdd72eb2846aadbcc4a5189331cc0db66dccd80a357c08dd4b7fdfdca1e4ac9e2a945ed065041c978d3bfc50de5ce25fbcfbf3caf62a5efcca20556a75b90b8b54d436b6eacb243dba5c6eeefe7656dd85e236356f0b526124d4eb59dbef05e5ac1f9c77ab450ab1a7f03465f9aca620892a40fc3c5e484afa45fac9062a8e290cdc71a0d6ce8175692c37987d7e53936c11947d8a17cc38af5b6622a1ea9f3b49f69b600"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"00b0b68bf29f190d87ee95a79c4ffb58af10464f81ede9ee4fc92fb036c55342","proof":"70d4084ac3560b3a379ae23386275eaaf4618609b370a27c92e010c5a4135d37309d878815ad62e4d280b72efe7fdf47a044ec7c5e10cf4d8c79025a1f54e77e6ea2bb6f39a5a5b442a0440e9e640a388e42097485d952dcd098417f0816784f52b28628b0c313c096bd7ebf4eb2d7d34242bf41d1ecb88e43a7bff1d1cf5e614cab62a1a030d136e77eadb62e0f5d384cb580ce8f449340d68ce43580049805a9755b37f3049e3437def3a7379a7a58a3429e87d4b41484cd383b9ef7f85006c67f0d4ae572fb920e86d38ccf7a18e37f1f92f47a89037d5041f40875f71b02e6127123ee45125db2307213612c934f93ae9bfa2208db0e15608dac3259950e82971221671b6bb20091f098b6e4f3c0649d61adf303b0b213fa7c3d928f590cf21b7fdfcdfcf0790bba2e8ee93d159bd5d709e38ebe7048bda1d20ab2db6d20463c4659f729568b8ccb6799a06f3c168bd4a3d4605456e7f958c7e9298e797d162351e511956c9b2b6bd991f360b44d28ea4f13583f724f79444b2132d0973c046f87d640d2c8173bf97321eb1930c3b5324af9e01ccf6c10ffebd464a1c95c862d8b3d85b7d8c9926001186d64df0bfa260a0773a12ca25c0672b39c5a345f8e1b1d2a1c2668802d7e72ee3059bc38c055b0aae9a9d143df853855e96010260e85bb855b3b4e0497b23a45739e5b571c6910f634c118d059a4e073938db23e9a3bef5a8925d9bf546e9f3b7e7b2ea862800bdcb5995cdb5b53736fdddba3795e79cd4a5986c83757889781d199b8d58243417de27c0c67a46b748dc4ae916ab06df0438c868587227e16160abb843588920beede218e468d5ffef20f2021621706bf9cca91b48342714a97020b28c8bae0aa4632ddcca07c21af0aef5cb4045a1fd579e400eb51d094160f25ee875219ac642a00dacaa0d5796b76924a6f01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9846bd664dd875c6b64fc7b500d3d212efb9cc4c296639a4e924e2be8b586130","proof":"846f4759e20b3ae1dd9fe119a4fdb11afbb0958a64ec56515f674c13d431c8122c92af51707d5760a03b6be7c7f06d3f87884f498d3954e8606dc39d457d7853daa9fcaa2e0aec532988b004eba3254bf88d484bfb9d57772aa71f664b5cc736407e3b02977c6acb0214292e983f8c7e4ae7c0fbe699be6d3ab6a0d79bb42b41ba1907a6cd76d77a41296d8eb9f7711a535bea671b6aa4d36bc669706e8fe20e57821733da87184297e05fe4ee6ce2c871aaae493a169e2f48da560935ac6c0662425534188b415930138a3596f9733c0d01831e1a2200618f836b1dd1870302569ab93723eee7134fe945953e9ef7cb3ca99519195820433697d3680d328e6c2823a216af316b35330e11f6f31504ef27347f4cd3a5bc078b7b74cf1770e453ccb94c7a9a4a8f3b65612c37ebc1bf34a4dba8c778e0377c453f8bfae6045908027d618841a744ee2705e761bf67d660e552c4297f240b2e00018953e3ca7a14fe360559949caabdfab66029abc148dfc6668d0fc817ef8a260ed7d730e2be195c538d7d49ebca76f83bb911245edc3f477d9dcfeaa265a6a1b911665ac4b5397c82491a797903d5ce337c78b5713762975562feeb8b3a4bd2adac109998ce4aa07d5f57bdae24441fae96782b02c36ba7d4326fa9e8bb0222f852129a24e23e221e993d8f14ebf19feb30b2b2f8e28826fcd94d33bda53e8b651bb67d270d588005685d8027f001e068c12c4c914200d087b35e4fdd4c1acb8fd696e3c86d1ae8c490fd0cc54f182c29ab9e5e23ff8b8110877a41366dfdc06926f88dbb90192e47d1d1c36de7a39abbb1c2deeb123f8ac5c8b610f6d992dd760d7e96fbf547bf5e88b145b531e60dbedfadbbc6223e44383204802a5912ea434aa8264e2e06cd2fccdff6c5f81166004b315a07b23e028334e3ef2977262704729d8a94ee0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"58da8ffe6fd6bbf1d9c8e1ec0460f16e9a78103a398a2a0775d676994ce33a01","proof":"80b2af8478169c94d41f8eeabbf78c011095712471bb5dd8cfc938b8808fdf06a092a914b9fb58b0ab34bf7ffa13702fa3f3e734f85fc0e382abcbfe65f53d21123931df952b67e0fb1fd738f235130ab6fe99a202702be0c649d8226fce2f3542d665b205dabb46baf98fc00e03de547b8f54e0941cb874b86c1e6400291156e0eeb35f8f367e90f7ea338256b21b2229945a559d69b0ee44e1ef5ead29e90b8056fc6b329e5351900b40fa31c368c0ae74bf7f1b31fef3a3ac2ad90e20a307e2a2076c195cde985cce66ed425886d9448106c080272b4a7d1e7abf9c1c1f0d744525a6a8cc681146ddfba3b520c77b506936138d1f628bf320d349aeeb4225342a54ae522f810718bea489f1c72de1f8af959de9e21e348347e6c10658505834c5ebbdc7963e73e13317cbf1c41aa22edd0307742f3055ea5c03e65270be565c8aaf2f428dd3a6e00d34b30a6a322d81f6c3a929a80ca7bfbcf1355de4904798921cb51a6cff5ebdc777daf2f07b0b3ec2391030e39e48c994ca755a62a9584aab5dbe99d4b5876d6e2d1ea8571a758c146c773692b772d8411c1a338e4e2ecaa941f738fb914f550c5a7b37ceda26e4f49132c1a6c29c2c20280b3773b662943849ca198893617e0b94f61616c26ffb67c4234d0881b044dfbf375594063ef24a1ff244e0ff997895ddc7ce162bba078284dd195b02a4acd8ca9e601a1d719ad9e849fbe8dd6bb25dd810895ae101a93f16a0ab363a9e742799de3fd7b90ea46808cd7149e4b37c99b234989b482b5a9029cf3bfd82d6ddb878dabbc6d9567a0dbf447b24b94d72b60af483cb84b3aaf3c276ed30fa990c509bf5eb48d53237ad7508bb2f39bacdd01d686007a24495067004a147b7dcc31907262b5df60d93c212b57627b2dce6380c8338309d9f8715efc5e2fd4efa8dcaaa0e210daa02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"20cfed568fca0b733d4a070f849006b4a8720c03885826ee3ed0bc2781e27d7f","proof":"aead3991d2693e6132a7edd3254e40de6680ed2e76c0798b96ea09d360f3ea458a4803d7f7689513a95cb01834fabb5b55e5c84bc9679abdb0c30a76ee61f1641cb56549b0ef3a1cc6fc572159d9edf38c2c055b178377e36c81906fb1d620202ce7325df0cb459d02f85284e5abfbd4cb8fd52cf8ccb8fbd8389deb52d6b95319b3d5bfac209b1035415e29d08e245ed8d88bceb52df9a7105796071c1c2a0f54ea81df3b47dbbd4991c28ecdf9a44db35bc89cfebcdc37aa0068464963e6003443e09994230b8e88ec256af384e6f187898d2393f91fac8d60b6fcd4312f00c0ec6d20eafe37e5d6d140cea45f775165942ba85832c9a3d0c5991c2905ce4cf279589dfa91b914f367c63b138bd7c84d50dd7eb673d82170a9fbe684c9fd72686e80fcc3b245b46fc57c3625165cde5022fe50add8f07a80b5bfe8e3bb5c49242b2045d39ed69dbf16f581e935d39367938862d938824f876eae50e7c01a18882e9019317c672fc0bcd90de51056e71cfa197cebaeea7330499539c6dbc32cb8d32935594d68251c56ba1b305e2b83d50b80ac661c0d2a77395e49c2a12e1e526eb39330a213c5d215507092eea3fdb00a0177a1d10121452f9e93c053f60362e951e71e29875040d26f58a9a554b4b35c39e62033702307aa98433ac6de671e46185d8b82651053666bad9cc977e83c8c5223f04d04bea1f67ee573e57d35722fdf4f71fb4f1dd7c33204df43d5bf1c5e78bf4d650f0e9d575f50f9648a4a8c095e3aa8a52c02772e2cb9246eca308b72da107aae9954d6cba6f3426d507aa644f94c07de5520a3408ea61d5180e9af91bf50f7bbe15c801c698c983135015af7ee67d8fd4181ec843d1ae2d82b905116ddee2f9154ffab7f604573211b0582aa27c4587d65a89eaed40053b6418b88cd322107b8f56edffd5cbc0dc7af05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1ae453445eabc0d8ff263be12035ca49a3de6b5d3bd0ffc98cb9e8ed8995d965","proof":"fe0f4e76bdd90bc8d3ae7382526c493913658ea9f70f4a5d3a3730b848869778329a4f7f77b2163ff99c9663e1bf2a488e895a49959e692ea036e16204d70958883426c7658b73c2272c518a61a6551bce07efc2732e222be4f9f1022b7a4f43e6e5e993074ad24b001adf79f93207521d6b47440ead68531f6a8fb39bc1393a40930ce9f74be8158d2a20ed72ef434d13f9fc774eaac40edd7fa9081ab4e002c7d0244452b6b692b70b0e5f267c6236013e33b52601f9f5afdca283c9d3f50a1fd26181b0ac9fb14c6b580e96754ad9991b5ee4bc850f2df9be73ef88c0910aaed6f50b31173924c1dec93462d30af8228a3628265a30de5750d0635729754688c37c50fe5fd595b588ef9f4320a5807d6dcdbf5d775c86647ad7992ddd7535f2c429cd6e9d7da81e7f682151dbc010015023560fd85d5ed6a92a9c07fcb87376b150858dd447eb691b5f3ab2789cc454b2d083b9fe0a9723d3686850797b4afaa4a9acbf44ba10aaf40124dcecb869bb88c7a209c39f79c01565d2ed03fa0dd8232f025e70054ce47d5127d40fee260cc04461d965e2ddd563a2081a224b6e9cd2d2da0e2265d206d9d45df5fc7cf15a0baccf1b784598420538d92e132c50aae423132b3b43226cfa3c78510de3eead27a687f016f42b7ed78fe0317230216ab8636f56172dcc28e259bd3611f331f0e55cd7015c4247c1edbdd1f2c89b2734f7f42b5f723bec87a1740443444109f3f3e8a38c377f9f11fe04049e4cf56440fdaa50123e5459ed12dcb3660199f223d6e9c6e1fed28a0acca801e2770527aaa8af0ac9f79d4f2eced6068084fedcf79cf6254c3f2dc1da6f8f6a821e0d060d7cec450727a0d11b9dffb1c4b22d8ee950a22cd7b24374f1de89f8116c5200c7b735d05cdb421cd7b0bc39cf569b65b0b1604d184ad177217b36fabd561b0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f2d6438adbea7ac09042765c4d34e80ce35d3d58cbd1975cfb061c102ec05554","proof":"4646f9593cd4a7c1ea315d08663946b9a12831519ef499e33fac0592a769b522025cb810381436d4825e07abf60c7fd377ae36df55e2f183cde2a0184a88334d305dd6f3f1dba87c262e3bee72ff410883143a11809a155ffdd34b2a10008f6c4c137b846d383aac33a0d09b2c3bf3ad0579ad1f65f205d6ee0358f1019dc00aa6a3959573758d9f9d788cb4506b184e291d58eb079f49715a0a2f63295c5206c8104fa24c6facb867f84c8cf4cfd6a03899b764080b4b0aca30f5a9670dbd075ed7a48a250eda069b48c2e1e70449621a644e049fd289e56c57b132146e7100522dbcdcef9024b56d313bfd5c98b3d2c644723e7d449d613ffe3593f5be8208a437ee1030189fff7960049ebeca6d8985206072c1499cf74796a0f323de7a23f2897643802539a12b380860caa8e258176d9940f6803aced7511fbebc49fc13a8e4450b313f3924df02c048c7d64ac79776b5cad22bbd45ac52a28b4d406e6ae88ac82e87b71b9da0cc45e2608209d92aa69cf97fb00b8fd34eca19cc92aa561ab9aeb9aa730c38aacbd25fb1c430355aa56cb62cd14e9a9177cca650eb22785c483dbd9e94f6f081ce308cacf02510c493248440219ce29b55c5e11393bb6344d4fd8c03ca571337815180181ead17647b22fc064de6931927e5a86767cb49729452daa49569a575ab64231a7cdd19435c77de3daf5b57c64506c62fcfde446ca7001053345b2e0af8a503092d56daf29d27d2e872ffbda3414a4324af947a1265f94c1b84e9e790d704d0e996abd03380c2db8a7b57ec27156b65c8d65f6b34d67fb4747e4290dfe316ec9f1d0cc2d24fe4a554fb0512b0bb2b9faaf2fb7273707daebf519b4ccae50a60a99801a242196cc05cd1546a55cdb0efe2622f023526b5b573b1b3765afc06f77801c2a617ef75fba4668703008d187a4f15a701"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"125b5e89351a55d597a8c00dd80552774dc3f3861f8079031f11201fb9401608","proof":"345adf9e05cac48efeffd10114689ed9be402e3bc41d8f5049932da04179a24d66f2441bc2b36ff8e1634bcc2228d5788b27dcb1168d0117f78a079ef7472712facc60b655e9efd834e94dd5f754e8d8d9a716e40a49d480ac09bb484d5aeb1eca5153ef26870c41dfd48be1be960c9c8df22b50d52ca032f36bf8c66639db3c1a720302462bd9f7aa61ab6e248f3d117aab268c0d1b0b324d6f1ebcd80c890b248ec0e3c73d7cde58390430ffebc9f569ff3d5a8e8f611cec080e99caa6970e5b096e25e98c5e10efa38951e91530de0b6f2b1a921ccf446f7025c0380b7e02a0d81d7af38e5bb177627270f56d87d4d06044e3f73c96fa78d828cb51f8fb45b2a92581b9f05c1f13be83a0ce66c779a47192ee29527678407de4028cf36014c0ec463db7557b59c54f2635212d7bd707d7dbd965173aea777a8a1d7c32187c5426a45eba605144317be974716c17496ffc4aca8b75066baf0d6b95234fd310288cea8977e0634a78d2f3dae67381e1285715a6ee2d6f8ffefdaa3c3be168299ed315ac0e2053433e42c85e955577770a5435d3d4b7fc8834861ddccb36eb6a7aeff9d4881c21fecee92450889466abea678c38ed970f8af20a95af73eefe38401a2c7c93886dc2d78e4b192e7ae294b810d600dbc9ca91695f75a15a520b3d6075470c6154c0504e1b13c9c08c8a44423bba051c3e1a6f7138a1221fba7c3470d21d7c074fbac6ad4072c915217b1ef94d919f83ec3ab81b3452da8b155d119e7f26809f8be68b6b3fa7efb60786636414541d5c4194024066edef441bd371eeb2a071cdf300bae2cb973c094d00ec5cc07df42d0cf23edc55f099768aa103aaa1be61cd3ad6981dd29fc0200387c0902d34df526a7d750b9561f6105c130391a11daef79e454324868f0066709373a27fa4b1b3aec6cd4362d03a9a774600"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5a3defa8c09ad79a55353001637c5e35c98afdaf2de9c43f7c50edfa677c5779","proof":"6a7ad8c3cd6ebad13d08c5558f6007dc88e79668639637fc3bb5aaf017182e301e45cd75273d42cb247512faf18544368beadaa7ccf07f3116e67810079aaf544a7d9bf39312ee9c9b27c1ea96e1148d7113e983a1b393c9adf1410932b2a165c80a4f2ce2c9b4f8d5d2366fbd0d846c94be9d7ec07e8db1936c99c6ff92cf376a2b22618bba60ea5f01f2295fdc8745f07ec996abee8f66a95c4ef3db0a08099ef9418e36a01b2b5d2513ee1fb2f5231ee79d74a530cbce6b9c84fbbea82a0cc8906e5559984ce6680d173854df5e0681b84547649a906b053d039b85dd150794a5f8c303d65b35b133b0628d4440856bbcc5d4b325ce37fec74b35e3673c074257427004e4dadfdffc161be1d3e845c1cc33dce71babfbc291d0be3317a82a7006935008c9046a270abb94f91956466a275cb0765ded0564722adab6732454cc7d69f71a77105a7d656560850e825ff477fa0005c1b321d2416954ccb5cd0e56b043090aef2b8c1489759255c11fb5aa33df31728d6dd13a776f4bf1dc55149e73062c18f91a28292c86ad163555673ed0633571452f73b5a8a11e776bcc544c7049b8c2fba905f6c8369d78c7ed9851f5a660a036eb3561a3f5f989a9473bbac644e69473e4db21c998d3cf6d1fa52490b888a7de199090a4b7252a0ae511e2ae40fb26ef49b6522b8e6d029e2b8a05559bc04f8b64b82ffa0b67314aa23aaee6cca8728ed4f1dd86d826bd7ebc19e07ba1f87e9cb943d4df484a11badc15fe9bc567dada806dfea05b425c845b69261caed78f03fbea70ec5b1c34f4a548982ea4ee28823cc8c4cb1717ab33df49ebaaf2d2d96b675cf7e4d1db7a7cf631eee23c810cdc847a89434b3b1587cf86382c9ef55b0a2797e9c19cff474a480bb2c53af971abbc0c3acb9af9110d135cd168953af81230cc7fd01ea39bd17106"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ea63e3873cb26eada679679ab135a9544eb5b0b953b3bc429eb434ca0ad1f016","proof":"0a5f524de9f48d1ce8021b05aa05a8b80d2c988f06eca5678d1f3433ebbf4e02d8f11676f62c6ab6a85d4de6960103fb218a8dd0f6e337ddf25e4defbbc7701efc67a41afe59b11932502a3227129632048a053cd8c956d7b40a1dfd9891f946fef147af07c4027e5ae7186ae96f0387283c6ad2426ec7455202f30ebbcdb61837164145b755fb7ab5b50990f4b859cb21fbc64f7b2d7dbf29584ae71701a00e69a438898a963f9049b9fb4ba4fbcde0c548b7440a31b80cbcbc8e2a418d880d4f59a52021b1b6af1882bc44c7218b480b7d32648df77dc9269c562be06a3c0e92efefd6b710170e47689691430f5c053ca6cbdf691176194dc03bc6de3b80438ab3cc0a2a1b6bb379d9e46fe1e940ad09842ce9eaf7e9f8d183844398136d173c7e030726f95ba60252ecf90b6b097636acdda627ef9ae41da4e269f1c598717297bc48dac6886519c6c3e2d993f0e5a1935fadc3ca78af387822f1658f11752ecfaba60b1351a261df607aaee7d399e989fee47d02d48617dc2cf58e14e441904876f2eeb1d2d204f2a7b0d956b4f771ce5b7b69bb28c17903c9984e90022f3a958ba50592b791f9f8b336d2ba94941461c934c2ba2184fa4804011d14b422466f91be5149725564cd6728ea8c3a9168f1203d5eb143ed7b08a1d69c992610501bc54bdfcce6873532f63cb182e0a8925400ae705b494b6181b57a5d36326a4ef70a0dd985d22c1a42f555aab58c5702a0f41f8726da69deeb6609cf97216c80e40ec242a24cce3b6ea1616f77e2bfe8575c4484b74faaef78ef68ad710a50ba087cc6a25f6687550d109f2f7519d6fe96bf71f53e029911c7875aa046ed50da30a4370daafffe393be99a9293f929115c79fe1269fecb8d2acb9227ad4f07078648f60a2d3c00bd7c03121cdafc921920e44808c4504b0f8a1b5b91da2609"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1018d5154db804349d41b82ba596d26b7c4039b98df1ce2ed4ac202ca4244d18","proof":"5caccae57e631cd8c6bf2efa537c8379d282e8e4371e5648d7cfdd289028a16488a259fe4b622edad289af7ff528ed70408bec073f1c89fe595a72a3d575a848e045b866d3aef66097756346c835fb1b22c668cecc250e626f587d443252335efcccde91f7d4ec6d4370ac8fd45b3f96f4426a54f8b40e8f40fa44d410bf7320c68101ee696e8a4dec00568fd03c785d11f04949f53dc0a1b5a02e19c61313036c879d60d21aab8073df0a878c3c3d44a996fba635234d3950701d383469e60c4373c974fc63a30f995f2f9418cc3c69d715d1ed87f8a05fa2f1c63dbef454024ae577035584040a82a5b80c49ca4318b09e1c517ae4dfe4e321343bbee01462a08fcdc779d1ea221244623f40614bca264914c0aa7c85b58564762499bc6344ea8155c71865c69f1b3743ebdae0e257320e83cf19f00d33ba05d4b0ab19cc7f5461b079c9288e00bd1bd298e455bbb3097ae378721596bd5e817eee70acd0790edf5ab9d99b3c852dd81e6dfa54b9151062bbe9d2f3c5fd12c7590753578772dcf40b23a5020a2615b4539be059718e240d78c0eef7b7e08994feeafda23a16088bb3e963684085cdad8de94fb5d161aca2e7fd60e8c97703fd74d8136eca0638e9f58f2ed434302a9e0727afa814a5d79760c2148b0a669db265ec209d7b2f06e38e4fc410d8745ed165b65dc3d0b4249a3da5db5c1bf1667123d5df8e4a20c2e43ca23aba939338f6fb7f637781fc3a8334b769caaa3e7c8dea52f53fd753de9eae87b7d68ba46bcc0d3bf87c56665e7e04e6982f7cc3878a4f4439a7255e2ed421998a94aa0d0737f147ccc065fc3a5d15221168deb1b10c78ac4b060950b90d3ebe4c581d31ad14101b1cb9e33dcc6749183f2618a3e4b7092127724f09fc4ba49905650f51049f1361f1c7137bfc43687c4bc0af6e8d40efbc94ca6d0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"14a7c7096b22669c834fd4c2fccac0eef6ea359de1467c6348fc683a58bb1242","proof":"7e6681522847bab459c1a45d157d69eff4c03b3d242dcd1f64a2cef6bb83777c00f98de81c2a80ad4cd06acd58de850800d1d06c1401077aaefb62fac8a1db1278f5e8d51589034ad368847c9b0f5dbb53e6bd4f5296c1de8e8eefc49abcd45a285a812cdc8b8409440cea4a8812f660dc3ee839a7ce1e6cf3b53b28f7b4fa09b903ad00ec05eeb23fe3c3770e014311d8a8f099bf887615e5a4789bf33d0106e6bb3d5b9fb482a7e9fe9ba424e444d90337c62136b0a15bc6bd82dc1cf0af0d72e8ed150a64d064fae9ba6dec1eef009a24fb067e88efcc6478b8c8267f7c0a54333bf70e3c7cacf9f03dfed62ed7cf0dcabe08d7fbabe888f149565dfc9725f8e9bb8a4ae4d1edc5a7ad87d25a49c9777d5d7932cb60e231f81847b18fae6e9a539bc4224f62b50353a3f853b94f60fec211f0a6aa438023a28d7e8dd46d1b6a36027ddc25f82a6bc4ad90dd069406a6e2ffb376972f1bf260efb8e2cb9c73b641115df442e6bc7d05498ec825a949513e86e48d31a374a6cff3a6fd3ef454de93b4c59f8680335779ffc7e940593b1d7bfffda88a68320968450f1a18155fdab786b960ffb6ac084b3fa549a1bfb8f9b2762b461740e72e22e51e9e1a2356845334a713571512b9b149585739632e20526363000af5b730b8329a357bc1019ad2d1338a1b4fb004e17324a72020226163e713b47265d94ab9b8a2f51d470624c3b9ba6d6fa95ca9889d64ccbbab43af80f28ae449ba17a90664f7452464392c9c7ffa934dbd519e2fd03db766ff93e609e07a3fc93f0998c1d50d3628bd4dea0245aa7fdae0bcab46acde34370ea90414ee11ae5190fafff881aa43aadb608a37cb7bef9ead77fc42ffe4449a4be8a9de41023f048dd81b649a5cb4529a0fc5c5c6a62925f68354cf68e4dfa8cd81269ed1da35fc4d30c05c6e95838f2301"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1ae05ce74c247c85286e7c45ee1c10b5a6876c2e58def891da57a20a87ad4376","proof":"ec8e2b43455f54218bced62879c77e02ff0682bb8df505dd0e0e558de622964504ddc040ec9f904e408fa1e21e9023de9a7dd49d463581fc8794dc5d57aeae3d0eca4f6c97861b3d1602cdf837dcc913004f2485e003534424294ccb890360440e0e4bf21f4b902a222b109d621cf2e8420a30058e438114fb666cb9cc7f66350d9fdf07bf20327497b16ad500447118bcffb4c49cb2b4f8dcc4b6e52b73950206b50e97cf0df02db53c4ef49c6220c3334c4cbefeea5b9a28c555a4ac36590924fd973288a10ab494b9c126c927ae1ec889590ac0eaa4e1608b95e89b708a06ba4286bab061d1b2b69732b84c5222d7118e62a057bf1ac03f4eef23ee6a962fa4a99a321ed30995eac81aeb482af15a800ac10f8b72089dded588f65ea1db387caeb012f49892fed9cd46304d71d4884e3f459dc5dc8f1dc42ebc0eb4b54320ded1ab4b99ecff44f7f7d180896d1832639dfa2d49bab0a2fa1af0da6946a375442d9bf95b6d53c5b34ff9369f94ade005060b03ad056a88dbe3f65a385a81507c3baa19481fcc7f41da40d7714d45ee262e333d4392d613bd6dda054dac55377e2849d099abf99a9919045de2e0aa563bfc3e5b46128cbf028b97c888cccc289a3f28232847a84ab286b9986e25728faf72064b96df973071b04125462e762d666a42178ebfd9505fff64d037cc358520b16c1f674f9d2bfead83ff1b23261d8a86d90f99f808ee1f4a7f7faf5eb7f28c2dbfc6b6892d3d1985cff4c7acea6e8cd4ce663148e695cbaa887852387dcc952b69a008d24b73366f982dd57ff751bee8091d0a4c75a6e737b36d6a16dff274627cfe8ed42e707ebf9d6d240e82693ee053e007b356976f4a046df303d3f1b5db0de7942ddbeafad8b890846ba503048d82e71c9653c891edc2a0012760562a875e868722da0d726b922df514cb0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"906be0d18696c578309aa3b1da06a617cba380aa84714f18dba1e5903da3f728","proof":"405d0c7bc251abab652f71e84abd6b40d749d8d16b0c31bf0d86f7b3797c4820a0ec4476a3845b03d63e3913076a2b051c9577355efc7ed8798f44aa2dc8ed43cef000597f62d512bc0a71a3f8708e9e8c5f079e2ebb29f35e72ea0fdeebe52c2e0b624f3b85d7c130be89bc636dffcfc93099ebeef56b8353a84612a2906b4623206952dc17fc53434d305cb6cb94e594696feae1ab708d2d31f89ad9f0530d73dd16b42118c54112cfeb3cdc6a77ff599f491d184624c8cdc26948745cf80295f8764e85b50d25c52df08cb5510bc06fbb56aa562bccdb6f773def2462f70b0e107e98de69283adfe50d6a3fdc32b2246e9da7198171e56783e7becd31f834e4ba678acbf17a1cd8072ab8d622e2beb72a1b1486697603316e65b939311a718a99774540269b4418cb3cbdc29a5d7f727213ea84a1640f9b200cdd9e949f14283d19a2e71dd09dd9775efd2caf8e518feed2ffb87a0cbcc9f1c4a03ca88426eefcd0ed48cda54490efc793e0e553f9d3e0e024f3348fca1279b1d57998f11fee20a2ee43ca08c6af9cb37bb68dad79d5ae9c279e12b3128332cc90539952706c55a30eb23795a0a3daefcaef6f7a813afaf266a54275171eddc145bbb9f9165ab805074e98106a57dc8af45f3536b5bdb1de57d726c4029421c54c3e19734daeaad38aa2c053222b834287732254ff939c736a610cd1f0b3650a6a3e7bc5378e947f477a886a82d2971a8cecc50be434f57396eb07e0572c1e6cba705e693548008d449994dd8a07396dc4f37b6baed48a768d6117d0c43ce147343d146d161e7bc5961fa5c6974b17ec19b1b2474c49d9aaf390b427cace4bb4d17c7a397644fa4756218dd13fe669665b6f4ac5c5bc22b9a4683296f484c9be96a193b10059738e6b72cb91e01ddae7075e1135b218447cb1000c982faae27aa34f0b3f08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"58212cd3bae3458e97709b5ce522c23730c9e326704b1d7210dc4fdf4e7dcb69","proof":"1afd56b55cc11392dfe749833f0d832942ad1226b25b9f349ec4bf114cae0d27c667049df3a9a8acb1afa5d5ee858a3e1aadf95d7fb362437c5faf5b0f29ac34b8a7e198286c561290839053bf78ba3741bb69a1b130d112e4fa7c9c09cbaa53206ba90a8c5f915d704b7dd26b2026b40ad2569365347401c7df22c0e9d0ed17b5a726d2f61567e41247878dd4f9f6fc2ee936dfd0f194bc229456254491ac0cb52cc8b3538a66b33ad842948cc24f0f94169672641a82c1238dd88e9287080bd82a3e4cc16facbbac89a30034afaf4ddab598b0bfa5355a4691a195da80c50dd887d99998d515ad9e6e04345b8dfe4fb237a512aca12a924febd97927fee126eac9766baf321b45fd8b7dfe768ac0bf0c062a999ef608787feb7dd4eab8a172f2b55863aa16dff47af42a1bb567c64836d08e4bba859862586fd5f94a5f3c26b832849ff5dc2b34578c00990ae20fbfd5c2dc706171d3e2dc6b534010d6da225ceb2fe48c6de2b3b14dd613c2593954bba117039ef0714ab5c0afd5bea91b6676f5033d85563cb93ee4518a32277e88a6623566e76943a185d3db9e2f4f4774a8a60bb999fd2a1ea1c2dd761ffd61e741966200b0c67bad7727991092558e3e324341b6e4c05eda8064d472fd5bbcbfba3ff8376d7c7339218916ae044db049d0ec3db215def20be1c1970524df4f6d5a3f13b1942cb3b3cd15c312300ca332b2c67a68e1838f6d653dbdb6486327484f1e63224105fa728c6bf43d3b6da2456c9f9dc250a067c848260a4ecc6f127e3d4a190403ad378e7d64d2ce30870f4a86d18d4839358e262ed32a03384b435fd1da4a2936a5eb89ae2e41ef0ab6862b0fe7214e924e2c2c61662f8f7f2160e52da85efd7fc21528ab670e4bc000af08f306c905996a35b1c87d14ab43c1b4205e81224b6aafccab1d3ad4ba24642a07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"245aa2e5b1312d473f3dc5ee85b35f1b72f745c3ac6a96b52b2b6d407cb71b0c","proof":"60ab9446fe9917048c2802da2a7ea667ab923c9d9aa4eaeac7f4fcf9b659e43d26b722a9736b62716c4ae5a317b54b30d6423400ea68068a12f9fe24b5e8c6363ebe104c40c91c1f3b24488f7fb5f1a1bdc34a42526aa20904dc07361258a6278aaeadcc3e6659df6e049af7db5905c309916b52dd68a96b1c95744178a29f6d501607a1d975e327d83e73cc0c9e76251cf94b6493319ca99fe86a14bc26ac03b96134d2b2215a84a5d5e2e5345b9d3286851b82f43c5ddb054e7d2e1d013d08a476aaa6e0ee810b626d232488d73eb1465c9faa6286ae0797476648d4c5f700bcda2dcc8df0afbbd91f022990ea22d35aeac4f26103d18e84a01289c87d4f04fc2a357a93e318c307566d6ee12eb7e1097e3ba8a6b298d57ff3330549236a566461ed856bf77bf83cd89b039d3677d1b604e8f5dcc38a1b4133c7284be0ec126864e156007efa9e881e1609f5311f2eeac4c54f73b544548e7952112f008463441b5f4fecfb2becd9853f24478c0a9ca1cda0a1c1ba50a8736a2fbe54ec134cba5349d151eb0d05dcaf2699543cc628b22e6844aa8f0b624c295f8a469526526e7807787384ccd5133ef12a1c3f88bd7cb37fcd819010d09ea81d671cbd1d36a87bba0038adf7707f232abee0948c62c1f5859ab1327d5dcae269535b11f11ca8b0848e0b8edb6831473b04c44f6369f8bb95f58be27e5cf3bc52be680d6212caac0739ed116a675672b2378008d710298031b3a162ad108b38054919be6511a49eddda68c4fef948c8e593088cc89a5d400fc323e8de874bde2acd90a7ff27bc7913d3808640f9f494cc3ad35ba7f8c80b83d434fba178cf21d726e2dba64916aaab88da8e907130d7a0300a1e089b70e4e1f228ac8c383e5a50c65a91b9036714a23c82931c761a9ec9725fa12f80d55c74237e1bc3012a3a15322f491b01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"242050de5b16c4401287ebe1d84f78b4d71522db7458a97a685d44e20cfa5013","proof":"4adfc73c691b040835c01d3295fd80d0566d9ef29b0657a56e0b0fe379f6964f44c24ae0a9947adf0d33834f6e2e24979efea39b55191e4022b1175a33e68d5096ee7835a20978734457d087f1e8174cac6f9080777c05d3ce97416b3607a816a2a3c52c26507e1bcbc599774690c2cd102445b84f3b872485a2a786fd4eeb003e105ed147f71dec5e57a16c3a2fe1ef2c4f4cf669cace5caa3d73b2db4059044135325fda59d16a5781264fb184840d15cad98e5ed3e881c2ac11534c8a9d089b6e684964f9eee300782810b0191bdb8a022baf0f2274a6931d6552c3550a06a85ccf252119f9cfd8181ec954aa0480b5df39ad56bb35a631ecf17586bb082e08eb3640b647c9a4e166372e5da6bb3c79b1aebb1174d217547cb4ba5192b14126f2813506a01928caa8c1da7d6a095edf2ab20405b602191d71af6e8eba6e65e46b60dff399647671b09dd548383fa22f034fa9392c539dbe16157ba8904474a25888089c24e28bea3e8af7fa2cc232fbe7fa479d5ba823493c55ef09ff1f02fa5a136691b23842e13eab871e12cc319054bb3b22bb1959d80724543dfdd702ba2e671dfd9f57dd87b667f46e407f7abf3ac4dfe0040011a18f3b8fd02c86534ac4e3ef9a8b4904aa8e40e7efc81706f437cf7775525e60d721fc09d5c25a394a44777e829a6660c8f4eacffcf13461261d17c448a4d10d7c4e1e54d5159261f0a6cb82354c37ad66a01c0f36ef9a725b3a88037083423b121f5c9172da3e47a66d635d9444473075bad55ad17dc605005f7a66fd63ef88d45eb3355bc02221b4a3c8d6c17605d2560c9f08f0358628b6e122095453bbb18690bd72022750476a0081690d1e3a6dd1a88927ba3b7d51c882cbd12acb8f851c0f928565ba8501b56005b3e14b0a0368fd8995dda150e61ea4814f53bfc7bb27093b584e745c03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"36faec76445a1109e1038d50fe40ce4ff4317d853d1c712d0aa3c0bc91b9d641","proof":"ac575a45b80720eaa22ab17cfc7d3c90fe8b109042498e21decad7b0486380662e95f08d2965357a814d526df5767b16f63614f9a22ffd91a019aba1cd3c626a8e1c1cb84f28dd2915f52138650577407538e1ca2486a5949454e1669b29dc0154dd16c90d915d75d2b65ebcaef79e52d851958e94faf556ad4ddd9ba8a8b74fed52355d29edc45900025cf868afdfe20cedddbabca47d3548bbb22e9d436c043e97ef8c20fcf161da1c9df2078d02b4b6a34ad1a8bbe106bd331205e805a108ac8f4ca3f99c424485eff56b219fdffa64c830f8bbd1763f7ea6a6fac7c6aa074ee6b18a3de3bdd4fa70fd10e28eb9908db3e32e863a13af90b10b776607ff3c84af41c8c79c492123c7e57540e9a9e9a99a5726fdfa65b7f7a42055c2970f1df2b96451dddfad0ad9f3e655e81bb3f2c6a4396bbfbb3539f5a7d9de7f41fc388845cd01ccf6760696b48ca6b4d2382ef653f1ee344636ee4fc7e5d90b8d7f77e80081f654418f94fbd24a1a016da3c93d9c4988705264e46741fd19ac759018cc232717951f269489ec2c85473c7cf3b4a61f7944821629af0f7650a965087a78c33bfeb57654f1c28a69ee4d86158972a0f8254a3e1709250527527251b71dc8541a9183a042200b281aa6e6703820a8ac0370e76e07a138992ee87ecfcb2c621ddfe912b924e85f166541123d24882cca6f2f09d4a2e935f5d8a19bbb6f63f6772912096cbbcce61a4c37d077b1431146c307ea1fc89caf1b48eb2f73a65a3ef95e6643e893b6c232c6bad3b87f96751378ea9345ec030ad9b964109edf3226a8e29142d161a8b35ee7ac29a94b2e02e78e93f11470b7272ddbbcb1b0fa68b208f3f29fcb5143209a5a4a11c67afc5794ab303026b0de76550a8127baf109d8374ad75fa0993c8df6ce62c1e338bd6c49cb249ee734bf242295d2e5924e02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"665ba5edef6706a1c3523c7fb4db83da8102b6531fd964b2b594637484013175","proof":"729426b0c229f2f4142f8262a5e8d9d9e733a95b4aeb966942e7dac7545820317210735922bbc61296eb512998e37f6bf5c2062002d9bdb3ae21f556db93a679844c50f3ab25de7c5a729740362ea14f374547f22dbd61b95c265e36bd24802d8e94688b85b7afe45cfb3eddac86181e596e2742d7fc90d70fe02d6755b012149c0fa7fe6f2836d646b51cd5027c08f68d21720b979858459daa4870c56a7902441170ec89a2e5a1ac842d9b10fb6528559eaa6dbeda65f27bc0150e42ac180fcc2d823224131119f3e87edc736affd1c4ec00c2fd72b2c5375c9c06c9bba70a3606e433868be14a26243d57fbef99450b537f75241ae740e3b4c7e3bdea7323d4ad5e127fa67d63b61e56e70f27ed0713c75d8239b2a44c7fdbfb6f35bb3978bec20aec37bfe5dcbf87265ba419c526507c627f16163d7738a8b7a12313c2632ed396331e8beb24332295e0b155bc77515fd3ad0612707b13e0ddc8f9097138303cfe1d0a2faffa1efe35998b2cd5ebc27aa4ab20aa0137cc082670a7e6fd4d8808e5f960cb192dbde6e4df655542d65cbf950439f0d3577e3440d3e689c27bb0e91315b79dca497c929ba01a52f3eac8531b9c885e28b6f924431ed00492690ed98e1ebe5251dd167e62bcc890910a06a21fb8edad4c4ffa4e50791992354848e52deffba2ddfd4c4aaaa351ed6649320d06ca92f259b21cecd12559584542c8eaaca41962c50479869674d5a304baf47a5d5fc1637fc7b12d10cf13ad067ceab81ff23c57efc7503d1c53b8169282477a1928196b7f2e333d9c22e59f185d6418bd0602c1b71e847a9b2abfda5ed4640388255606f020030a727cd80fd5563c200f1a13b37ff9ae766df3eec055e4e3798da6f7f276b7530f76c82acb3d06acbd509fa9c2b0e7543d66c41a050cd469a1042c4a01fdc555bcffd001039a0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9e704b1fb466d418de5638c21cdcd74ece86bdf1a880cf2cb630c7379757911a","proof":"1cab61b312be6990fd826755515a73f47eee2cd0b5b3c65b9ca8af02d4bba810426c0c87e02a84b56d9d5ae73683aadb52c784a81b671b85ca76ed8ee0245b4faaf203578f6109a6d1233c344ed070eea0c4d4da169150253b668fb62d8a2936d68715e7aa9a30410078c966588172096b435b64cf32a25c7b7ef2f4c8a78102dc0c32a19603359ec8ed8d397db21033530531e7afb0c94ae94c583b6b2667094ccd02ea6356a88bf1eee2776a5e4c772ba32acca1a1cfe174901917774dae062a3ac94ad672e1d78447d43a31d3c6ca830c5c5fd05fa432aad902b828ddfa0bde7ba75f2ce3323af060c0096e159d6f4263077521a57f2ef7468dd240c9940604a1193cb9bd46ebb2326af56939fa5e899b1dd78f6809475e14d56dfae8d43d6480b274a98d08db544959c9987dfaeaf7c89a5836459f38db32e371557bbe2e06cf4aeee25995b01d38a97c9592fc7f2a51ffc8fd9987b4f79b922f329782297465ad6092b2dca5fa94075d0730f8438fa4e141624ae8383674459f5534500d24ae3da080aeedab7a295e94d3757eec50d24baed363e8306b15d518be73b07dcae1e46a294b5d7474e560008cb0e40e4b72fc4e3e062e9446827d2f5cb7ad0f2e9bf79db1522a36e2bc42b2769565aa2906cc643162d34521b3f7dc541f752942b33b32fe01ed69584862285838cf9310f39dc328954ffba2655d877699f5799a3e3f295f1e782649cceb006e0e47c13ff7aff1c16c7e2ea804e001e48e0819e87d963042ef205bf5d59978fe6a8aaffc2f2b146ff442e26f14a741a4feb05cf491317ebde3feeb0bf71642586c641037fce5a3647d155cd957698ef00a6f0b1da24f28d84017676199f5aaa0e4e328a0c19674eed4f1d2c36f58d041efbf0b6e3d9c8cc2f7ed8e164c31b855256b42b147a72339cdf1baff418a7912bf1c05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e6480d722a3371600db1c54f61392d29e2097ecf5ea2f988ea63b1fb31f7c266","proof":"d4d1eb2740d55ac2a9175fd4d393721af2069fe6b0fa61e50f93a9e389a0e912404bc3a59b27ce130648549eaed6e15e25bf62b5ae5f27c3b3c63f740c2d54268ebd4f72883e5b7cc9a9bb70ac0fbfc58d79a07a7b6a426d69ffe7911b5bcf1b86cc0813087175a90833fe1bb086824028eac8031313c5a644a6aaa4bbc0a444c4bef88b372c516c908761413841886795c8edd2f38e5e6d4e56b9baaaebc60b3c964664bc03e542a561c691d25d04c1e143bf820eb9029888e3903369e54d0a35eeded6f1f209bf7b34ead7125a7b15dd5453213a959091626e688790209404a67cac531007775e6700183ba444ff63a9c6a40749159639bc4c0b6716f940586e70b0818e25b575f64ad335b146743d0f3313b782a3668285456813e529455530219a86aacff258cd1149cb49fed668c47b88ea9e3c596ec1030b20c083b3106c8aeb95a29849a983848d508420b01db86725950a6dc5eb9559b37aeb67f0163e5ab419e9276e68bdf17233243ad4ae00df1cb46539de64b4d888b851de4f6190571e22e93bd3342a9cfe80b8358e60cc470f2fce27169c8acc85d6d22e9437224f4dc839feb0f169ae80d1d346065a319dba80caa52cce1982d43b00093d62707ebf91e7d3d496e5573394623ff51e6af0eda1c9449d42a86956dd8e373b0f12d09d9218fd4e9b8ebb95e77b37dc551b8d87e450d27224e991ca9ef451032608f4c46819576189ee9c2b2c88470e6c20af39567d6adcb3f98228fdd0761518666f23616d13587732c87c3bc770b8b97ddc02cf1f3b91aa55b3b299f3d47472ca90ba94d5ce0cdf90b79cb7fb038424d78447454ee22643301633e7890e1f72ccafb17206f3f5fb50785498e55d861e724a354a6344f736fa0920271e8a8504367e3c867bd64bcf544beac77eb3522d9aa123d2816b82b4a080bc767e2b1409"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"38b6b8754c2e629ed5f64d74c297eb4da4f7d6b6005830ac63fc2311ef277359","proof":"f81d168df9dbf67114fcc59d8557d62249468bb6d997c4ebd272f33829c090594ced2a48cc51abbc6a07432b293970755e21da277e1d9b3a2f633ef832f7fb5a627a0e448f00f3e2d4f774c2603670ee09938523c269f87eb45deb120f7c54638298d47a09796fb30f755ff53653bd38849f1d892799273aee5f93f355914b6a8ce0dde31f7cfc88530b5ddcd5d2a4dbb1305f923bebda2cf152a75c818b970756ee90b7fb0f2689c176933af5c2be0fb0f724d41ddcbfebed5fd68c42fa1d06639b41e33b133577865bf05756dd697a40ec6f397182622f5e5149d79e72fd06c039386d0e895b2c5a1d82dac232364b5cd5031cec58e5c34fdda3aa3e65e02d829ae8873c8347878d9bb454a7a951b4b3c2a5b583f39657044dc7aeb7e7ef4d00aba2ab39031028f6cd9d96c130c81e3435e991226c130d5bb061843158c90ab633914096224f2dde6e8c70b39be1bd71c73291c8e5ee6de43dc90b3123e309681837d89ae4feecfa65314b4db444f3d75fe04c7e9a33e600e6c0644a0ab27f58ff3b9c49ec7c5ed8503c6ca68faa56a392c62ecc4da7df932b9e50aa4b1475767512fb9753d01782523984b4063369cb4eebccedfce7eac05c83ca9f773874ced0381fbf64f6f1436117bf7f772e95b956916fbb3a2c8d2970b23fa4457a71d486a5c7d9ff08cd6a2dc6c0ddcc3ce8ad8f1dd7efd04f9df81bdf38e5e85d4a3c7e85a6b3904797b8e186b3f080d1d00467cccc9e80e7cbb81ae628caf75930704971a896b9e519581daaf786ecc3f5ae1bf2c9dd565ea7ff64ec1e86d7577b343e3944692909210ea2358463e8bb97a43da1b5a28bfcb230d63b0592002a043461dac4bccfadc250cb1e82a0ca593b85e760d522d36b30d4ca077bac87550e01ca02eb9befa61bcad6cd2b258e377c1a251cdd7f43c2c29602519517c65700"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8ec5b4009deb63af857ca62d3ac045dcc303d425c806ddadd63340f993ecf057","proof":"76530107889a45daada472af6f6d39d08697a4dfda5b26850ee8aeddec28ad56ae38ed2d5a91c7bb5975107bb3d1ef1eac234d0837a37282ad013ab18d264e061a0421ac7eb24a45ed0af9ac8cfc357581b0f3611e57f73b3760db8580e64b1e865b921d90c34befaee3a42d9f029f970c147467317d2e44c527d51221c5860eb6963ac0ece7740862dd5dbbaed911e5cecd386b7dae6044879ed0f4e515af0d89568afd10e1100da38226c62af469006f50a212390080dfe739af8718f8ec0a01f6f27cfb44e3796bb04b60cc6297d4c826ac46d510beef85dff4d0b2df2504dcced47c5e86e697e2bb7d8c04a51d0b29731b0d5a79dfbb3180420f4633db1a464512ffcda341ce154025df8704ed4cad9b4f7cf31be169ef3278c1e8ed856c662a422e040c80cb8d3a8ab4cfb437fab577e39023ff9bc1cc6f24355b4ea469fc99f591c925556461a241e2251ed31b92e3243b76d949281302edcd82276a026edd423b61e788d40d6aa2a0c6e74dea858e6ab45dde1e227ffaf46266398f523a7ac2b666ec1875d85431206f4015876f3779616b2ab80847217798c145e269de6b94b25c9c261eb52b47ddbad81fa2de9527652e2bcb48d481b56b88c30524b8b54c7f3077687bdd367277cf2ce79214460ef65f614c86197cff8a7788db46a4ee9d8e254a7b23663732be7fb7a73268685ff89d41fb5b2daab45e6a917616302e453733fe457cd9666a65306d26b76fdfc58eada6dc802017e26e311a104d9e4ad35407938880cb1beda2929b976977ae081bf7b30ef9a50dc8f16fbaea194c7ea54b22f6ed8bf607804e152305a1f8764903c10458a6874518b741f26d1057a507e8044e2794a8398f17cc6ffb5ab620643f084fa0c8d101588b33a47d0b9a5a6efb7a047bd2e490338d4200fe7ac31c23dcea1a765739000e79d5fc0b0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d66b9d8a8e094b174930fab2c85f270bde7c3aea2d6d02d0c6b50341c1af6120","proof":"c4d20a596b473e5e0eedfe79e85543160e9ea841b19fbc481375b61271b88746aa440cc32f711e97cbaae6b350945fcc7cb7e4e068cc240edc4cfd96d569d26e04293a20f0c8d4d13b117da183247503425492265a039b9ffded7373ac1b3519268324712d30d0187f0ba997170d693ca590f29f2173aab1e2e9773efbc41d65b182c74898c5d97ad8f8c576cc6f82c55dedc6d6c2f6510b14966f388c457c0609fabf5c516950c98d0822152605d1185f4c55b3d6bd18809d8bed7718426d086235013a2940b7f6d2c376463e439867dd9af896c5ff1399577bfa2ae2262a053e6200564e2be7d1391849ded14fabf9decbadb1aba4b61539d66cf80466741bce8edb407dd2786b2fc10f07ea36da61089739a06cda1cc8cce02fa128617c2076e280ebc39c3fb7aa9636069a722c1edf482a460779e1e81167df90d4b2f16fd0077ab283654b78ee11cbbbda8b1a32af13ce1f67c879d3c1b10eb58bb080737ce72dac1c4a0082b000aebf3a2099aa155b933b19ab44c46c521274a1fc612c58b4e0a65be90241524dee9ec379d2aaa3af9e19310f47f482cc91e7c3f4881b14da06f226b443f18f021115a32c5870ccae1082063d83d65c1fe1460ee82127e610d1f248016e01256e810635712be0437fdcfe4e55fdfcf7ca8723ebb2d57d88acfd9b9e6fa202a5dbd4b170113f32e777b44f118671c97647ea46dab69f78c43423ce207e3ddf9f0f090ce0d86ade7481310672ec1682ba16c1211a135e6dc63caafe846f824bf0bf7d5ade47f7b962c10d00f3cda860606fc4c9eed51a2ece4fea1a88c87f0ea232c03b003d67c64673f2387996908a445b24e196c0016ec536e10e9f3b47f635ec9d41bafd790a39b8e71393bc79ef2f56a1a42dd34704bc3bcc108bca6bc1fadf31d90fc4089a61d34f1dd9fd4eb72724594d7fc1c901"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"00bc4d7cbe199e3a511d9624f41a4ac973afdd926f251015d7032020049cd528","proof":"be3430c6d9bbb148355f1d19dcf76dd48289da25627773e22da2d83f10936356aa901673d3d86c8f7eb2488f37d87354efae5012be5749e7c6092c5f5672c67410ea777ff56edc259cb5571e6f923ea787aa60653fbf503918b2122a24711f0a02579c523e239f5b10fa205454220ddb9240298dc72c02943aad84896091e66ae000d35ba3fa407e0fef70563673ab6e1177e46bbb92466f0d1d620a797e5d01bab0025585b0eeb74e4f0c3ba70894e439683667cda331b84c637c285b8e8d0b3169cfc677b268491bdacf280792bea7b78b3bee6054f364472338395ab278078c9a4a172a085996dc53aa41c130c9a96c1da148c1c06a296775150390b4a37380c54d0a1474411bba89f2ed14b261a76d43e671507d623c1e3eb56f119cbb43f2bb55988155a7e5af0bb2924306115ef052c81fc844998a5522e156f2a70a5b0a85c8f44f5f526e32c16a10155a40f2c0b90b3f79ab1f6f4bd0a356d457b17aea00dfd4fe7ef1ac71646f01efe9ba06197d24c6937b459542bde3c9ef11cf6cdce94127d7959d786aa677729361161af7ef3f38f07263d00c1eb5d16ff9ae04961d461329212c7dfc28acb1ba835cd6702433189867e5c130e315b8736d4877e41b213ca3c3a54e1f32a300259ba43131ce4a55e18f974c2262371e55b31e7de6bad0d25b42be240eb207889bfeb24f65c2c4e6ed7b9273cd25c2a961f7c27c3223f6cd8377fe16d763b73c65ec1a65352bf282d5933cc113a39f948bef935ece0e29112e42cdc5753b6cf19db88bd968dc8c85dd62c60d17862a01ae84284ddc884a144b733ed510aa63a53b148f24a7bca1c36fc01f6c137490b7c57d5d2253244b5b3963576f229370c5d5c1b84c4a38d39866ec2327fc6ada4849d357074df3ebf63c165779d3d3f40dca94e9edcc17d822d3adeb694bdc97a1d319620f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aa4f39fec42d37d2832b2bc5c4a3d84a4a1a3e8ea8bc9953a4489ce3742ccf45","proof":"d696a3c8adb3ca58e2e355843919015ca79908fbcbd559686fd2c056ca9df809c642b2a5ab83d7f8f77cd6e041a9c5b7d1cd824d157574592c9aabcb217abf76a49156929e859484b1c849502079f56116469ccbc61d274bdf3de31c7f2c030682438ecfec987df4968024b05e75318da2dbd53cecb510626f4eff0e84679211cd73c99187c3eeb1d833fb23e71b850618aaeac0c1974f35966b02e0a93a700ca1938c6843ca0b0118ae929985ef007f0902a40dd5f20b2457b368f7a36e77007b6fed9f423f4b3f9405a54063a624645fe8de5260beb04d4e2a34417dec520bda05c0732e8e5bc66d8fa6d5521e5ae04107a4c8d0510fc1c895f0fe1e0103532c47ec3258ba15d5a55ef8ff63dc7ac227b3108f2600423c35df5d49ac8e367fb60c31e225ee3ddf0b2df02e7fbb92b06b1fb79a4735e51647736cce65bdad62423d51da8d1b1c356daec0974d645f132cbf53b74ba33ddbdee181d483f9e4303e7261501b27ed7b76342942e5e15a0d89534303e0f4c9cee4ae0b790a2a1f3cbc9d2c65cac18cdd1dfbb1ff8f5d266585d37c7f1cc9f5abf04cdbd6dff9566bc641b13c4beef01145bfbbfd86d3ee6f9e14da09901255090754de6f8113fe7e7475a59aa56a1f42e87579d3739a982ea262df7bf89b37293b2dcad1d47c140a76477aef16e87a32bf130ffdcda8ebe31a4d09ad7de4579c7edf3d734caafa1c30709789a8bc2812cf751a53711fe61bcb2e0e8bbd0755f8d13e0b502516c76fbee914fdafe93610b7ff9a61f7dbfa2e8a88d28181915cfa35e50357b1bd2b31f8564ec767e674c5c12cbc13ac7ceb79b0e49ff0b1f9ddaa8c1b9f5a75d8ea4a91f5d1ede36c0dcfa22208ada37859a48da1c690b90ed4f18f66bf3238f1bc02ba6b6c736f6be42aae45adc76d648a9caad18a39b9d8888aa41423447fc4bf03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"46b6c38449d40634b2ed6b59693cf2704c79353822724cf89c88a0761cad2052","proof":"6221174379ed1c8430995f7ef9e3591e3f5f1733b71cab05a3d2f8272a0f79490226ad0bec78979277aacb065c9f7a87c81ce631b5a2ffdf0418d3f03a40886b1c7d3517931da01474b1ca11b7a40d8e6462bd3c91a9daa049ae2bbd435dee61b085c390359f8efdaf993243b23cd5354fc00152c2661331c575eb78264aa63d8e7c0f39eb6b77be8d609f95532a24094c37669265823f6aca1737f2d4166202931fb04ea558499627c5c61365b49ba3c64dd5ab3a67a18589f18c72a04b1f01d888724b04dc15bac827e1831e459515883e288b0edb8dca07132ce39550830e7ca27846b4fb0a0f7bf63f60fe592234e94bdd2bf4180427bf5be3ca1e9f331c92e387e93629c6bdb48544af0585033ebeaf3c43611e618f454e05cc54780f545aa50f921fa63f67335378c24333540b16856d4ba0e4b463344e9fa8e089882dfa59cfa5625d7478c309a4657241ccbcb70f07db41dcfad7a498b1bd4f247c3e90a0e9b5139312b15f9e10357f03948a28cf5e97e507a50f055e74bb41759353644d10b977464e37f7606d76abad1115ab0baf3959e0acf5be3b66b9c4052877c065af45b4d5ff2a24c8dab9bf556a956696b09207f7fa232d0de673fd26e33ba8a9e93e4f301b41f9d7f0cb644727d8d8da1c9fb0c8f6deada6a6235124b3373eec1bd854e058b5b872e4bc9c8d9868fe9e43e8f3b8aa2aad442d4ea40ae05b6ab742ef4289e04454edf340387f58f619b1bf5474119ce4a7a4e40be9eda43228d08efddfb9d68f6eb32d35365e10ef42acb8d45cc8d6f280c7ec013bbb3f454c90447e3b44ae4b85d2c37ec7bf9917574ce7bd0aa9b1e9e18af0a36822a918062f2fd1038174f794a4c491f2f11d5fdb319566db2d2f8eaa3ef00e0f05270da1d2c1051237bcafc97847908f4be244f94c46af0964a2f122343e2af73f310f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"264c026a7088bd1c79c4dd018c5e8edc19fd3e20d8e692bd07795f99fcc2b479","proof":"3c2ef21f6ed245da5e3e196502e90d0596ffa383e96c5d358bb379c622d5047070ecab1f56e6089b5f71507fa938cb19c91a92c98d37b7809555801dd715e33eb262046af1a022087eade35d7da01537899ea86e2e66809e6286062a4d3ef301dad48439f6e0a28325cc5013d07774f6f826b2dce6b597ed540d8220a3acf6533a1d333e748e4a8b4db0921785f66b7318a46187ca388af5ae127df4da64ba0dc775c80c4180de4cb5f7bb8600f1dc51b09ab2541d6caffad21d609a72fa150592af5a8736d465434a25014c872f78c9995327123c832c76c276ab308dd32404568067efbe18a7f2f352f17e35bcbb89941b891ddd2074b12f1b03c79404ea34344e99f8f7a7aa95b06855a95c2f0f3dcbb7a867344adb31d525fdda0459754b060788be436bb7e0051be481ab61e12d9ea8d5336945640528d4341d53e908100492b2013ad64b196e40f82bb108bb9c1e49d05522d9e85d4135b70aea867571c4c2e314f273a356ba23f2bb6921b81e53d9bf6d600ad74ed3619660429c62057c1bed4baa432a7c8ff4a5036df1329a3d57abacf1f0e6a5e82f6119fb85a95b90b62429c00ed691dd83df8740e2b2bb187c5ba3866cb440aa4f9fc3b77a0c54b65132ef158b906ff38c7627ead724fd1a432450b2bd1174f48a0c47f8b6176adaf40134812a7565f5f1eec064ad30e38ef06bc4c432d2bb6d372f77843bf2679a67839f6359ccfb65140ac16ed724f068581e1a4c38fb651843b8435c3f993206f7089bf3097d72f7fee53c85a343460a3d3a8ee67c2da8a2f620c6d708fb17548fa262d344d3bcb3de1d810b8e85da45248172d3cf7d62fbd3ba04d4d4ee6cebcf9a0d8730eb5b8f0315a8820448994b18bce60a6ddde66ebecfd390e44704c793e1f05b75a341547229ab0229da7c39b2f797b62bc48279d81a097de8ac0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"387299ceb7bb49088ce333d8ed8c3c72ae31bfd8cf2362341fc926d2310ef92f","proof":"9615520cd9d1960e7ec95bade4c2d8af698927a990153c6f65fa81b7086eb31274f2d507100b2ba3ee8bfc0b92384cedd63f8433ed31af84f4bc154b7c038d7f9200c138bd7aa5e4500d8923a4197b88f055bc0c3d768c6fc3d6fbbcac52b2147ccd14756b4d26dd5bb3daf11e0ae72b8161377d20265cbf71df58d705872d6ffa48b4c634b1f4ca0086e2242a8026f2a407efc5407d77e1d2398596253d400b0c67013a6a732a3b3eab8ef4600f417882e42af6dcd2c71015a2a929de8f790e924a97381420eb715d61fde5f85112050d88d93e808e31e422b7105dacc8260c80fc15e4175742375259e0bdbd06328a3b4921e751b70e2bbe255d92b1a8361a4875791718e6d0e41bc94267e608fdc630869d22810af22ec02420115ffca97b800cdba2f92f8f28a4eb593392624fd2e8cac0910ba240d98b83ad3ae977411ed0f351c7876fe9c074b8398aacabf1c7425d449496cd910306d59ae6700237729273db1d8e23455fe0f3f5f9fb70b641fa160607d3320a79e432056cf5300c7eb6d076ce88cfa0d00bea0fd1c18522eb0de24b32cf2b05a476be55431e5aca0e1077035aa8b8914e37aeda6d08102b96bbe8b3ad8e1b8293b3f61acba018275882b11cb1fb8a53e77929c76e2be9b53d56229d8c7e44cd03873704855d35037666bd8227b3c2889ee8aadf57b6cc11dc8bebc38a64bc204cc38c1f0db01f04377a9049c1010d8ede0a8d272992e662d397fe8b5d2362a8f3019d40cf0f0a2b3438ee6108a6cb61c02cd2f2ea92a174e57e16f7f5848a47d71edd1aeb53d525518c77e00be8e28fb89123cf109efd60c92a7b57d77bf3e12998557dce07705c673a4ee671a1f3c22fd9523679d0f691cce4d226070a70ba6e280ef3cf18622806e03d2eb772b177f43e0166a98494e75c9f1de7908ebd362f527ca707b9c1e207"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1404df2856fe214719620fb747867e06bcd942c96a3a14d8a14a93145992b526","proof":"4ab96e7e5c3c5e1636be866ed626fc6d9ce9184a087706578d73543103468d0d9066b83ad7631b78ef5fe15500fc632d475798efb31f16b2d63851b063378b7a0476743f7df1c3e0b7cab14604cd19a64d6884caf57f3ea17ede0986be3c255244bd11298cff458d216b41c518ca963ef97eaa2b90c7f03acd2f2463835c4d7bfaca93990199bda3448857dd20dbc5cbf969a73c344fbac54e1d8317ac91160c7b278fb66e3997de118588a47de040fc0e8b40e0673ccbceb2dafb90b8566304a4f2319d5a9b84fcc33a0f310203fc0ecae7642bb500f207ec85a64311b4d00f82a186289be504a2041f73ba9c4478a4e3f0c90da0879b587c68780102f2996c6cef516bebc20e856f1267564c4ef8eacc4ec70db7033f26143ea5656814694264dd48b3040a111ae42f57bc2814eb7a3914b93baf8f130a4fd158ff092d84392450e43c62f7671f74981bc4a1f43ca535101ebfc1a314743b5cc354983c704b7e93c4daeb0ec70a34de44fc2306f7d3968a7dfe9821021e62563da7c20bf9109c00e57e111bf34d5f04609c6efd3db755fa64324b1a04f944e651c743536625b065dd79e7381deba63c957a1b18b94e048753c5e3e54bf80af4f9bb179aec1c68f2be1d5fe72f24a363617aafa3ef20a5fd7848778c2e2a2eff45742741e92e8ce7e0fe4d4f743693a4ecc75cb69445281d9d952b8c93c532f3e0aa63c6c3753efe51e382faced7e15c27a32a02200d4ef69bb1073a66fc4a65c2d9dbb8397ad603c6777e175055cddd39381f4ca0d45079c32a1c45688b9f78fa6cf7131a3c02d2180d85a6cda951f7d5bd6ca38df127f8d2d8151da80265aa55946c877809c4eb755abbccb59b08c17400052f2bc188224128041181a134c91a68cb89be050e5ab0e1f5da4a24a141983ee260748317dd52fcf52fc96a7e0635f2010d0e0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e05c2d578487d2ae9ba9db9f4df06ca1a64ee3446d87d7787da53060b31f0b12","proof":"b2f78b33d11bc563a6de34152aadea8cb85a17c787db54c81e11e2331f175d6cb056b2321d3de355fb6a00328515dd835345780130f1e2216dcd0685a6eccc3cceff2bb3a7580c1441c7bb63ceb0a85c60472902d2d8930144c8674266f9d37cd486f0a650da85797c4694ca66622d9f6c343e1c637444044af7da94708f970c0de83f55980170fe8511837c314ec58265ff4d7c68bbd0fa9047af59be53e300c4ad0dc6f8cc450dc3aadcbebb225c580470fdbe6c887b2b156eda7e1e446a02ba23a5d002f275f3188c1f3a9e3f4b1509b9d9afb79f2c5c66534c721089f603a294c7024562af3b1671b2e0b6af6c789c911196dfacb3e41e5cede30f28ed6b6289548e9fddfc49f763615d4b38c0f4f4f2e2eac0386beff9775d0a1ff54655a682ba9f26e75018b19c9c3c97b7ddaec364380f4553a269f73770af5d2f1351b82202a1f31e49b06fe8f0e5fd207ab86c69809c975d739397b9df1adcfc086ab273456b0dbec1a16ee246aeaa4ed922a1d783e52fe6b5f9bec69d356864b339fc132299de726df7ebcfb2c2ce3627e0462c28187d75e2fd764143c040c2e4755a69507c8b9f676b763b5bcf73a6810fe0b5bbf30647cc7283b9d942050e0955984f584166fec5aaf0a425d5f0e376b9d4a06233decc9b70c3556d68306b7f1d064180e14b74a6aa98ac8e33c8e97317cda692b25ced2f6fdea71315b2b3893b8ea26720f44671e31f1ca3818fe1024a645d886009d8d12008da5feb10bb276c9a5d6f7df1287d510e69a34874b9de46663c23927103be12623632f78268c61f623dd18f38e61fff28437c123f9e81eaa2c98f9342b0c2eafacdd5b118ed042929b4dbc6f4ea281680525f0a15de8eddc0c25d6eb7f054c113cc4c1ee7d7a70e58178866d1cc653b76a511143c0c4a8cde2079ed31ec64afb61479cc2ffdc10f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e4d519581214c3d6db6fa4018c0f9efb2e91adc1d9b3c9fed585a78819f88c7d","proof":"42e4382acadaf4c622c3cff198aaff8d51ad6b36305b8aeb79c364295062da49f60b24f787753272c7a3d2d5ab20bd79ea27ced35b6ae05a0d474664c4f493101ceadceaffa069e31cf9647738e793ded347cbe00286913e3875ba9d218fc453cea63b4eae3518774b9b083385705ce6ab3b1cb7c3790b64cc4061f09f03f83c75bbbcee668ebeca82d660d473ffa035d1019508e1196576eb3564e055051504634e7d60b457e1e661937dc51e62bbd0084f72796359fe258af91df7b80a4801a4924d54eeeba81a8865d0c4b673a4e79e899ade7a5c399b6af7ca412175bb0fd0d0c16ce540d95702dd2858416fae6f50b0c000abe3c9f620dc04e18de6ab347034966a2ebaac0d9ed01019a31b22a6eedd83d4a662a1147ebdf1d0b7b25e0360b86d89729cc51f9fc363809ca4dead7dbaaf88ed8bf2c72c9c85f9ac1e0d791673aa0f3732ec8d973d0a6ea95bbd3d4a7fed32ff2a78ad2c5831b27d86d01e6a2a0377044b32c8e8b7d1a84f091e217fa4cb9ba2322c16848d319adcbe727678316c2a1028a23c9b062614fda28636ce9f54e46dbfe6fbfd8f484343b5843df65f520fa9e55299d6354d871f0ae0ab2f829e2ef23884fa2adb75a92d5ca115aec44d69fa0be6e9eadbf0380e6ab491a9ce9a32b68e8452ccb82459817254082c553e6fc10f8308bb806764848090f3c78e642483b189585bef8adbc8e66e5f1873391addf070675dac0584411ef576a820e96b14a18c39395484566b99902a40ab381c31b9abbad87e012d0081fcfb91ebbc2f73739f366226037e4bcc2e04788d15b7d8e64425e99d26c8a5a8d124a6c58b4b0999ecd7bbcab10069e77c479217cf6ef48ff9c470526e9b0a6c6bf5efcfd763204b4e0fff5947adb4b88e0dba6f912b671910f0cee0a8c0ce38bc7c59e06045d946636625abf3aa25862704"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7a4b215c06725da7c3afe3ba0ffb1b07b64b558a8a0524292c1841adc39e8a38","proof":"c0961fe7982c0f0f2303a556480f394bd4ea630baf2913aa1d6e5b419e00b45ccaa1cae0dc936dc871cbab3826e9af7415c678b65d8cb196690e07740bf01414408a72109f66f0865ae87e386b646cec67f49827a852cb731151014d3eea313f9269ef9587d48336fe0650ab963a243b33ed1ab614805ea18e2b6a653189154964aff9414bb6bca03ea57bdb4882dba8512da34d65dcb4aa6c488b78215fde018d2f628a453fab24d0316dd707ccb92b5f8570ffdb69b5d67ea51816291faf07bc813623e55b6fb97372c3b2692949b0f156e81ea604aa9c19db98dbf7593d0c6a268cfde33736e62523b468ecf320d705f01c104df58138938fa6e5e16ee868b2386f030edfc38cd1cec73f9e2b0220fa583b8f601905175e4f43e3b7f0aa2ff2e8e9d855f631c5ac7f05cf16a2634414b1c320c8e7cf755585602c38fe41264675b68dc755149b99f39c014c00a757107079ac3fbe02330c7780bdced83f0f662c5e9f109dc9fc9a3742c02dbee231b64bb24188a8524d870a8d648429f122f2042b9041fe514c1eb2bebe4e4d0bca50a87873c7ad9cd312ec6b65e5b6b84c58dc1958eb9a86475c179190644632afde2514a93ab552acd602da19db44cf70dc68ac2a763959c6a50bf150f5f015651b3a0f6caf8ec43c27964b05360a4b358add1b2ae32ec647b75fc641453985846707e92d65cb05f2a609dfef6c2e577d5a41152b8aeeddee154d6bf7b0c1e808b12f33503a6d0cf01aed07b421ff5f27a61907eca96d7d61c89cd2dc22ff3759e42e336b5574728d77e76e2abeb9917b0a6cf9f637930aae650d5a90e072ae906cd6fc22fffe8de4f9c70180fed9a95daecfa934a9ed5b90ec1e1aaf6494f2f876624630e171e4a017f244cc392c110f5aadd4cadf98bb83212fed402e49b283db6c4d1f160232fa046f90bb5612ed0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b889e0f9b0b98ab0a25510abb25bd1ff02119b1b4ccfa560c5bf6ba6b3a36f23","proof":"60d2d0707fdaf9e85741ebdfeae730d12954e81420d619b01bae0a7b54b725579a49ff80f150cb62bb030510560cd280328dd6e7a754235a0d4761efc566d337e4b1a2c10caf5d9fcdd7ee44386acf78d70fede606160d8d115ba4fdc04894467e51244acab4173d2daf5c3c25892bfc9a5cb604ca0f283b40100d747852f5177353e9e090a01cc6d22a3424ae7225f3cd235fc220249c734631f84fc70125008e23fc2658b8359e02fc213b3e5e48a1e12671a1a8493685b83697d3f0880108ee8dae8500a934af1ed48427c8139f697210d09d7c59c7eef68a56d50b6c620db0fea6724ebf4350a4b40915d34f3f497204fc196daa717a0c62c10e8770444b9693ef7a0414afb9f759b6a62d5836866d6e0600d919e94d3dd5cb5b06d25a3568715311854cd7b798728ed4abd3b164ce0c8e3259b411fbdcd23eeb92591411bc85743e4c03b79af3ed43dda53025e133a56f80e4d4c3dfb530b5b89c88111a3421a13993a9f753d9a35402ec1631d1f4ca887b729f329d19a2fe79ca8fda1aa41b5145514153407860bca4eb984d6e5a130b2eb1b49de9c0587738bcc334016c8a710a31e0e791c30cbb5b94db2fd32a7fc3cc928b8dbff84b09ec800aa807c0841cec552321caad0e04b6727eeb29d1649959293aaf831946b27cb6ac576b025916d7ea1abf5f955a9a319cdc67dd476bc3be34216634bd3f2c5df396a160ca404d9851e1de63eeb17ca39403d3d87d227be2778e756b1e76e136493f6029de3bf9551b1c989b0b0571c3423281c753d96d74ecb48c245da33a2aaac2b1710c1786483124fa3f4e4905fadb5691f94a9782b2f11ffc94ccb0f9e8c4bf2c5c8d773dc59f936dcd92540f0c126c3510a59e03421c6da9d2bd67939b92a1ab0d5484ed6278c318b6a49c973a20a29b56f3a979d681a01574f0136af9b803530d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e4cfd2e64c2ccea449375e834521919b90e1af120cf859af71f0c70d08789010","proof":"08be8976774db92bde388fb266880088b1984419b7a17d207fa0597a7bd97016a25958aa3260ebc1d25a40714b6facc8189ded38a1a974f6bb236a96cb6d353066f990f052c49cc687c6d6947a0b3bc1c9724d2233c934307c216ca954917b6468831209627bf66b683ad564d736585a2bc70fc7b8521f0cf3fe4a50ca3d971f4cdf2202c385fb20fca36a929441566ba97c1c86d1f2f177d3ad7796fb14ef0e2c5379f6018cefd526ced9735c79aee0846b89b41782e7df4e2edf431ee8c60475041e467a9bcd1cc8f98db76fc471ff9eb89475ad41ac1b9924c626fdc1a80a3c18d063a364d4372e91268b9098b7871e2ac9dbebe88de6ad270c05893bc5178eb2c2fd525fe56ead4cf92e71983d63786bc3d0ccd3816e1f43b544ff3e80120c28908b74c6492512a0cc27e17da7d3cde9229f468af0db9949ef0c6203090566904ad3a42d71428332e37e81e5690ae76da97dfb62607f3f7acfb0a2039622f8789769d6a9cb9134de35ffe1d81b9df0e131024a7155a4e1ec031b5963a257d8c55b5a0f758ac8895ada46cac08523f44f0124ea507df1d47b14e48212537f00b7aa0ea25cb1554b7a668a8f8f34d68847a9769201d683f6fe0030abba446c32b9f09c779fab8c232848be81908090a4a83f91f799ca8ae2618bec6407c2359e0dee8e62240b45f1b1856acbcf87c4151f81f6b7625530d2517a7a686a480f64ed2429892fb22e06b63ff6171c65870b5985d96eb18bca985c9b1b275a626ba81abd6131f0236e49d0305b7ef9ca869b1b3620d73000456742f9f63b67073c3cbf0ca82a456c7932605381b8a912580304a6f67177dc58e16d0495d92b3d5ee9990e3dda8d090541dccdfd529aa79fb59556a40e9ef36d8fd4eb027f7072082a47d700467e64bf11ef981ee4bf67448bdc52cec0cb613b82e1b8170f815d00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ae545800a22803606a5559d9d35f75049ff39d3c9511a31f1dabddbc2ba1026a","proof":"1a3a711bea4fb24c0b28b9ac166466eb3c205df6ccaa654b0479f878d9f75c52740dc2e7de1a48746eca76c5ff277418e103c4bc91b60aff1beefafd82bae9403064237e3f523160f9756ed9757ad164a9194a63bf44a948a335cfa3f990b96628a0995788d7f8094bf3f2bc948442c4860344e6767822f88843826b1e072b0554aeff193ea4ac8acc2f66dd9660f76d6d59cd6f6ed591159609f773ae498001be1648272f063c81d5e7cc70a99bedf7409ddbf1de9559d0b2405d25617dea0f76bd308522a33cb9d201404e7c1fbc87f0dbf450d2827da50ee1c25739e0eb0de0e86dddbc6c53002bd25b69b5bb4be1ed96f1180875d08495e1a823e429c44ca43039b246ce7432ae4983de8566f27f8bf6e7f99c5a32b0a60aa56ab8de9a6e3c745d683b152a45b0605ab72b097cc0811cc9b06b859b039a8a9b833c152f3460f1681c46306b09ba37d9321109b652685d3fccbba40fbeebf3df20793cb457226295f0cf99163de351cac00490aed52857d69c178a8d81bd17039b6a9ce63dbc62bb720a6921e6876a87f2c17080f6b437a389268658ed6c22dd32483674791ea5952d7d7ff1060a0e5d4dfd687c0adc96cbb6aee05d69fadff2949a4f7c28067fc530c9f9a5f17932b8728d55250c79f10a6923394d218888648e1919c440ccd302d0db65098701e9d8b12d301bf956d2322bf6e6c27d8bd30a8e2529af7d72969c3398ce9fcbc806428c87b072da6742df5415e720d334970ab9af0deb0d749bf883265dab2ff15b2bf50ea83273caf820c8ff05095b376f3e75b2ce9f38909001af6ada3783e201683a848be3121cb34dd076a5621ed59ecb8c39523a48bdcf3c110b16b74c36968206420ab8b48ccbb8840dd30d8e8c0ee4ce2073210a62bacf81c42906827bd2e7b1b0d8be6cbe5c37f8873c1ef7d23693d7db01f209"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7ada48730425d2dcb3476c1477c013b25e7d16294ac3c290b6cd3219d3da9473","proof":"1a343cb66eb3b78a61adf5c9ce29fef85e87db52122311c8df9ec72a931f5a325017fbbee564b39d8f7f0fb657c5ccd08b40218450aa35b0b36930ee5b4bc033087642077471f483c4f4111c141597dc6a9127d5f739f8ab3781eab8b12e1f4c222f5ca24dbd9fc8c77efdcb06ed3f00981fc366a6cfd041c7e8261611210a3aee25619d4805d7fe2448e1588535c4234d0d3c0c939d90525509a482d58ad00c8596104bc0116bff13333ae5f173a2d7109324c733868287ef3cc7b2b91af5042483602555d6748739f38a820eddd8004fc40bcd440c9f0fd4d779544719cb0228d66f01dab9ab18c81952c55f59b4b95f794723742c02bf7cd148a36409ce6fac8cd6c9cbcf76c760625fdf3a61d02ba429d9bab60b5a3f5ce6e6653e75ff6eb2b1acaae6bf2b898fdaa8f8206e7f338d3f7312b95535b22862d4aa16ed4b292068b4244181cee86cd8012deb5085878312d3e09a455f7ce0d428b14d59043088ad8dd985ca1568708e9faed0d17c28e63426d9cf4b4c269b957cd0564ccd55c4fd9352c816b9d6f63686c456d669eb88a08da4aef9812cedf7a21f790c89428e2ad4a976f64c4c34547b715b110e99f24e88356d264c31c74b0fc73ffeae023c53b9f1d7af84cebbb98d9cd9d50a0604fdb14a26e62b9e7aef4727f943e533c4928a4d9857622aaa34f2b5767501e309398b1db38ee70261c2e0dbc71bbf77148d477e75e2167cfcb5032707c1190959d60bc8b9dfde1817c03bf10ddf1a636045aec28214b3729707bad52dc69dc1624b2ffef0caee6be3db2f62ca9e44079800606162c34e500d1b203b7cc6d8ec16a94620cb11748d5a10a027df05c37faeab1fa4216334371e8f72e88726133681894cc1b6e71b6419dd4a537824280b658496dad387f2129e2983777459922b7ddf5d1256d44709a81708fb201b4c06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a0372a650e37fb2bdfd1fbdc42f27399adaf29368337787ea83c95f89afc7b41","proof":"aa46a0696d32106dc3421541a84e0727566cb5902270f730ec5669300b3a6a189631b2fad5a3744787e8fcf7d2ecb9ea31aea6ffad60da05ad884a1ee7b954366ef0e80d3dd2b9b9c6a35ea205a498187b8859bd3572a356c093428cb2b8f819c2d26f8f944fa94c5bc96dabfa8286f868d2d41e31cf7f7f409ef7e3890ca90b061d47f22494dd2f45642f2732f922140b3163dfdd0edb48c9625d6b7da1d70be7eaa7fa6a8aa41929de385bc6de0573861b75c8a0aecb785226b894bd9b15050382d0dbd0749f2075ec0d23befd7aa1c4c410ef2a2c0e573b3b9c95c2d67706e4e34cea1bb9c93df3887c3969fbc17cfcdb3268a6a57d3845c02dfb4191725e62d3925ac8b7538adc5e1911c4e0dd61aada874761801d7aae620578bbf19c46104756036ee2eaf5a8201e326c1197dbb1ef8ec8cdd02155778d52fadbb9870056572a80ba3bef2c0c363927e82eef135d72c062c728e6fa43be135fc1f5ff53dcbc08dac1b3eb3af21375d49fb464ca559882fb9ee23571bd8200ad3934736ea6c1a45ae4f17b2866d9ba17005678a226e3ad813d6d5a09c06db59946ae9570c8de6bf8f1242b8093019b53632330846ce0122dc7d63215470e6b2ffb7f502bf8adfdefc457bb1d147faa082c1427cc3fb75c1aab1462df27b619893755221c8687c690d2b288c48526b1d5c9e159265d6051dda3d69817cf16133e8b69cf627492c7bb5a9ae6c2e7b75257a3e8626b6255f96ae682c7826e63f9d72a13793b42406e217af5c59cebb2e73be7a9cc64e0b206aceb076ec3c9867c3a01e92b306a4e78832d0d03b419b3e31e0d761de46192a18079a2c983c0eac5c03fdd894d78898cd8daf2a0e621c87efb094d7c264f01c802c81344fe297636345572490506c1dc7381a7d5071e632bf9135c1952f6648864aba4e1ddd02b5da088e60d04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a28ae6ac66dc5ad303c9f59576d6d598903a2ff8126d202e9c129dac49963629","proof":"2cb335f6319bf758dc053ffdd72a3d39c43022b3c692e2af683d12a6ca9de10984de55b4fc0d7c8d99f3dc58ab41710db400c35310cc2fb80e4aca4f1a630339986e2968f5bebe117b51c9f20704b1c0ccd5d83e17343db95bc8928a83a61e09fadcf85173ac38da0b7e876d5c7f9aacf0a736d8465d6a18baab00472fccfb78cb8ed2c99723d3354d826a362ff790b7da052047519db25b81351b7f7d6601020f0998078379a637e025b1faf5fca270d8efe2686eb9343aa8d7a26329bf1f05a018ae6b72959df3782b8dec2e8266cceca98293e3c716585c3570839603f609508781fba22ea7b81f467c60378c0dc6b635e98de639b27c7c80ffc4d6463562f830810a95a52316c9fe486de43ded9e8c92a48bdf529218fb9b367795bfc47142f47466db5866e7ff24b986bdd0c7b05de52ef4b5052c395bcbf8f1eebd31450c4c90a5bc07e57f5446effa1de6fc3487845a95de5a46b7a6ebcd03d63fc91b8a9df084302835406ae2cccb57954b9ada4c3906ee5b796d45245d2497349b7582c66a8b39c7a357b5df72b68f5f0137499a06c2fcb1b8b1a7dbfa2eb7291842124cdc22893a6fcbc1114df3dbf81f503b96487eaf0c844c7c3a4f1f638c4667fc4a4ab83fc00a57500dab6ee59f22e1787345c5ad030909f76d796e0271eb497a5bd1ef1d79aa90911e4738ceaf954c2827d1572365dfdf191ce48c291db3244e8e4276badde5edef29c9495dd0c31568672d5169e137b35d339ca1f3ead81170ffa684df62fb5c322b60b8f01ea1e4081791fc2b4d3736e95ec523e1d7df0be2ed9d598756f2baa7b62f3f8a2e5f30c8bd18e2174005ada6004756d8384f7ad904838bf07a1d62155f6e8deda25e563f6d569165c5046ed0224641595879053c4f345ea2de187bed132f6fbf20f7b663d5c5804a1a97e22227ce670737250f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"74f5644897e944f4196c260949e6127ddccdec44e15c9f24ae30c8d563175b26","proof":"76fb7dd194ff25dada509409fd27124fafed5a4ffc9729446db80ff09f1b5b1f262e5f8d39766ba2c453dfb39ac473502e05fcb6bcff2eb89522954873e5e13fdc9572bda24d7bcca7d5e59016068d2aa858c2deca198bce3eacf0f281dea50fb2cc10267358b1155c8bf616610b93f60ed0755834cf53575a8ddd1bdd9f98751a2654226f145a13243191f4564a358f0f9655568b8c23c11818fac92750d909b4a08d39202b07dc359145dc23724120e6531649eb29920ddab070138c352d054d3657e2dd232a496969b987583444aea48278f75ab53864d82c04de68e9b8053e236640b79530aa3726eeb0da53782165be69d749e66979a963ddd9418d1e612eef7caf8f8ac25abadb036e0fac8fbd05fc0692f6774bd6b5c38b553b53556d5ed61252c6902ab5f3a35a7e8ea26417b458756318df945e2721710385f5ab253ee04b51537e398888b9fd294798ffeed1023fe118c3d79ffc4f87cf889ff61f542907b185af9be1f16243ac1d00aa9773c50fcb4459be3678c47f37cdc0836b5a837a95500380bf76cde8995600ac77653c309f167500cbb2fd721a1469a55484589f0a18a77d3795759659f4944ac5d1798a5b5f3d1f7ada06436cc4b5ea63f44f2877cc13f5dc06f8f98051871a87c74e4a50cd01c35d13d6dd3eff71763eb08f2ced3adaea691bb881c3dd77073023c4f0741abd6b430dfd35ee69192a26a0a645cbd476b9127143347791cf2bcb6f20ec938c51c56b5a58c71d2dfefa26167a3ccca2e021695046936a24383d19f2ccf414a2855d1bad3f69f490579f204471ddae86e8cb15712e7aa7faa30af3ec42e8d2dfecccf6977cb4c4467cac4d2f416fa07fd40448ffd620aeec4a286045760ff35b60bebd451ed8de6302e10b324ef621965d2862c308824c3a2d9cbe0e57209fe8c625b3ccff91d40cf80a07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1457cd9d09834d7c8d9fb6a39a6a7599bd84f11a96d41e42845337012a03b676","proof":"8229763afe64f98757694376eb48105bf5da1dbdd1c8cd48bba0f17b3b908c06f65dff4a63837569fa9a5b4f630ab877f65482ddd71817c0f288a4ca21f2e6082e2a6f127c97fb9216220dd75d7179cfc7393dd8463fc01601fd268b9c6c110ab0ecc2ecabbf01b41fbecf5e20169666d39cc4061b50a48bb5b83e30b7ac4613ebda65b0a2aad4fcfc385104688d188c35d79bdc72cdae7fcdb01411e95f94094f7af7e62cc91bfc2c2ea74c06fe4d2e229b03e9829beeec3bc6106128953e053afd58eb5199ef085fb798acbcbe99be1aa4d87783bec5f3b6054e530ad7da02ced8b6c6bf00a032c70db67b188d8b13d06e494144c9cadfcea2196712d87a4932f60b8664087efd9e37a6c0686e196dfb79202f0de7b872003d67a379581c03b8a187089ab9e0a2f3a6209bb5e35316c58aaf2623859acdcd4a314138b02673cca73545a1f90387f1dcfc7fd61e447e1345ddefa9e2f615e40ad427bce8ad7ee6c171a13384aea4d1abae9e7a257084f1096dc1eceb0751beaa316f1d14947bd4ba0e0408f1ccc8782e6950483b88c80952c717cdb49e9d7df59c2aa54b53771861fa4afcb3db28f09bea6ccdb55633a7c6699de25158a947505d9bc11de221749535b71a1eb889416adda13e7234b710f1d696e65c88bd74dac3c5c793d827b87d351e826cb4949eae14f813a217b7284a860f0624f97f8a17c147f8a0b548ba518d272705b154484855600ee6bd9d5bfb4dbaf953a8ad2101295c6cb5093646973fa616ec36f38c496e18afc0322aba40ad73b1fa2252726e0a78d38c562c50264266b4887751e2d53d0da3e3fdbade09102f1691a35eac435c7d1ebe5e0943c4e7e74458e62d2f80ae3e0409f8b33a21e70f899eae9fa3e4460bc796b70fdc231f6d360ebff7eac40b3fec164e25f1590dd4b7353698499ea48b6a745e00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"46622c2ad4bce125102babaf3e711c666e616a75ef3e8dde38cb8fe03b0c6968","proof":"18a667714b510fb3de8f4363cf9c881cf70a613242957d6c99dda05ed9987300c69abad074aec3aa5633e3037da40c70bf5d6f54b06e7d555508295431e43446d696a2e61d0eaf4f2b0c29bdc32a034ada6b66cda9e4c5fe864458df143f3f7020598da09a6877081a8abafe7572a71d287a2022265c4ac480223898d3c5b523564345e3cd8e7397e00fe031e7d9e4ef8bfeb00afe22e98624cb19338f61ba09b37822d6646793e837e3a203f0c7d3963545cfb81f073ef334fd243319f25302c35d528104a0a2caf11663566c5928faf623fca38e17e63bbd7957e9087e2805e827980fc5f0d4e3048c44a1901c72f6ccbec65dbefeb6343b99751f7206f8265628e6e543997fd93f77d5540c8f9bd09efde505ec7ee50625e7c61275d6e33e5a95708891e1f24f93892b00fafca27c954e080a1a18346c07c7e7ec57fd8f52c6399b0dc0665c53cf3af2c7cb38e2c6e8cd4a840d7809cff87ad430039fc25b52ec000dbbb1f440166452b1d47aae8dc701d2a929f6eb12041572c7dacfeb6f0e2479d6bb13dadc377f205b864d8b61a08f639d726fd11a73f5b8d9155f3c42146fa84e5f2c4812d99bec4312c8cf12ea68461dd5c7ce63d94bf0d1bdf267503a8622373b1d675f70a70648db9a92c0d0be8163e216095ea8672ab499f3fa0682d4dbf3350c5b215e6e36f0144bc068e5b7c87aaca52747e8223d3b16767a6a9411cfb61119f837236b92db9c294f0c0ef8b8edab44c383f9ff4b03cc82256b941f01e51177630036cbe91239714dd7c7cd6dc3ad387dc72e1b17da40bcf16bd2109b85cb8523f954701373f3ae5b50340fb87a0907ce88672a1e122f681839838e3acba16ee3d839099be94d687c24d4a00a28c652b482d8f16f0583d610091640aa2090e787276e00f9bf1bf68260dc88e1c186d706416718df146d8f2f00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4adec275c4b36d8526aa439bff25e83afb50c717894e4457b4982231197abe19","proof":"062c871e79be2ab3acb7ef5ad8380b50337b4c509058c8b001371191f2d4ab46c6c38651abebed0a106b1619a9f5d6e9aa42fbc607e53954e0609d453f20bc086853b7fe47a824f1ee865317161dc7ed47989715a5545ef9a865da60f924f6758897b2aa18308cf34590ff9622b10dd681a0173788f0d16385797397ad8386422f7f4147335a8678cc64eb8219360204ee6336e23685bb0ef7fe8e40f7891807e8d70b5cae90e867d59e08a883e320c55a183b6ead5d536581dee998db1005079c7052ce1f47625160447aa767d0eb59fed25499a18df11ea183aa09292ebf0daa1a4e124fd2a853519b152c28d6b6d8e2d0ab9c374ac4ba1a38cd3b5d9d8573283c3a595d1fbb72d0867b90a1f6096b340d56599ec56442a9f15738d5343d4586944e049a67818a1cbf6ed6175a745c864cf0c0945cd1088de6d7004aa1996ff656029e84aa96f404666442e452a7edc7c21183fb0854bc6f6801d9d817964e163fff89dce38ffbc2656fcb59f25794b8f505da35171eb9157bd7812ae8981a3cb3b215a4826c534f5c987a40926d72163ea55b9a83512a2f73c692059f1b374a82e9080df48d099451467d7bb55c0f2c91d331e602c0b52761107bd13ae66954eda85fc29100f80a0c49681a465c1515aff1c2d745f2807af09e6e3418c93b80cd4faa96199c7a92ac2f80d84d69b9933aef018182b1ecd2d1312c0677067ddabf538d9a2a5d483ce902b2989640c8d49e1ec62910cbdf04cae91e2203a75d12bbcbc96e8b0a2abfe439e1ae633100ffffa33901993103816eca029e7a9f1458708a2e5f1b2baddcd5f4732526e5ae62094b305219649ceb730a24f26ed83cab073cfcd6dea30b129b56c0209092397b29d73c9106bf60469d02f60612cb04d443bf23c8d6c2da55c0bd51667b5e129dc258eb9b5b8989da2690811288600a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a2d0a16a26530964cf26bfb6596f9e3706603c860a29ff3f4bf70ae50a623835","proof":"6e9661e5ba1f76e5ac850b8f29e95d194f854f4ac6d449fadb3288f0b71bae4e027ad2956fad129a063dfe990a3a0a7b3a3a9711b644efdaa5d47d77a404a476d017c161648652ceeb366786e04d1f7a88e99e0f205797c604019d743e276959ac6add7cacf404badb0385738e793316ae9243e4b35908d0c4d8a1a02b4950728a2d7d56da58a4e5d8ea7698b8951fa1fc45ef94c80567df72cb7323fd98a206da1acc6eef2c3189ca5ff04e86d22759dee343b3803e58616934f9a29278be03c78bfd07a4f7ea2dd53bf17058086648b1f582388cdf0a9d9b8f38afdc112b09a8efe65ccfa69dfd41036d6b8496873b1c1f7cf50c119400e72f8c044aaeab674040c56cb220f49b1b7dc4da1d9b4d0667e6c1de69abc320d01b548ff50f4e31c8031e7571e1c78b322ad3ecd59ac64a83cf4bf9cf28eb943ae2bbab9680225a406f5dd51bf7894a0a25134420bbb53dc767dd09f3cec4ec8dcaaa6e4e871c141e932de9d0f71f5777bb8d66e55d008a95d067b0d703a7624c390c08e2328c50a8fb5ab74ade549084ffe431662b2f62a217e27e95ff952ce8f0d425b0ce2275545b0edf34ebb85ebb8e8a62043229915782c4d4f2a371590a471e53614a796ea4195f9e02ed3f32061b6e27a717df178c1e0ba1a77fd0688c3b254a08062d52a85e4e586c5c8828bbb4eff51b95e11714859bb9a05b031b6c8d47cd5079234844b5b366ca6784c51f4ab2238138a0c9a2f75405c572c628c1b242df74e7934968d7c873de27cf5eeba79f997737bd9e33f9393b29a0587f244dcd14ad60c72bc0ebfda65649ecb18a1763433b62997a23a0c6a7e9169ee64d6430642fffbc37dd4d90fd74320b8fed63d9c9ee3b4218f0134d0a4343925e97595c48618e1507df3e98c3344e479bac480f2195d0eee6eae55841d799e69668f9f6b0ec6eb90c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"70a8631459cadf6c365b03ffaec757548e95be3f12a374681f75c39a6e82c414","proof":"2aaad8fd41238cc3375ee90e1649459faaf95137b39bae46b17af4a19eb6bd77f6e380dac0364fd3190fc6141f46fdb8c89a626f93ad6b95907cbac549ab627f148680f2f51233aa7eb58958d8c7ead004ae9350b9e7ffaefb5bc1e3a938f46e00b575f4bd98378caccd9a0512e65af7bd31406ba4797c78c8fff1acd94dc35f875087d3d7d7fe81d14e16194a1c620e1e282014e35e61e05df476e9fc9a3a090b8e17511db1536d40a04473d3fc82ccd35faa754793e9c37873edf7a2c45b06b480b7922315fb0b6cbe87c4c8eed8b585160049d3bf44fea60a908fff02b6009a3722b7a6166956c2e5bc2d6a426db9ba56b56a89f8857826f682155841d2326cb53c57972c19db201e1c67df4f4a6b4d0bb70ed2693b2c616de3cd108e30629480126796fd7ec94ec48074f9f5090ee5669d5e8771b88b5f7010304561e96a4ca69b48d47ea18c13dc8eeaeac26a00daeff623d99ba4e91ea9af43b0a7e54ee48f961d0e75829eb6df370a81bb0bf221445e1187426d792ed2fcd210edd47be22bd6fcd5386eed0c4f1f967fb25a07f3b2ee21b05a86c44584c733c43eb5664e314f1184752ac9cf79fe4678eec79dc9583290afe9642d599f3fa93be2886b08d155d4d53f1e35053f65c6be137da634b58344fb8782fe6f85cd89c0c7a13c2a861557032b107ca73c65e2337252446015d851c4c10bbfde11433bafc47950a2c036112fe4db606d5cb81594646f81aee06ad440cbac22c656604301654a08d06a45d81839bf62683790e38cc4aa347f0d1aa251f305d1274a93074af62f32885a686de4efc28603ac06eb515ed9b5fbe88ef9292c016e63e2d2449842fb46000bfe436a3473ccb5bf1860ac0825d4c455e7c170f8dcb90f518228ad960107f5a377eb747a5a0fa7e31254f6628554c2f1e0b155a4b20853f9abeacd58a905"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3c49632cd2efd23567b9a97b171e8d473e2b07434d01c23e842c03e8ab003d49","proof":"f669fa377b6e5f9cf11d0311c1eb27622ddb06323a3fa8e85ef02847341059072635f6fe8cda8b37b230a8756f0b418b85d64ac008826654f3629bcc491899733c543bf70b97d377bc52fd979328cfff8c6d74e90a0cff322073b0485f143441226f596ce7be0b9fc883190072dd81b78b02286b2a84598ba2e074091ef4ac58ef8b5c42133a98706f218dcce078b2eaf20ed6dd4e21ff0945fdf4e79772da0e2919f0292a0d92fa2864d6a4bb543d075c108fe840665a296567439d239d12042d95584cdcb5e3b95ff51e8659be243f26e51298240ab204d3e5ef7576fcc20d9ea8efaa773db3c817ccb22a99346357169a1e21006b39a5aa3f83edfcea174536a519a998d6de0c7249b015048f09768cb1d7f613acc7aa403886c216bee151389a5d2de751a9a8028858c7f4b43415d8f4fabc90339ca22b13a0a1cd3b774eee12bfe1aa3e03347a69fbfd53bfd76324ce10d808912934a70dd797b9a48252a824c5ddd0ed871d65096725c93309d6bb175b8fafea181d4e81e59064c3f70d9ea9fdf68630afa13811cb1107ee0ec01e0c4f965f8204760d5ad284467ce10a00d97e2565332cfa2089760b0e81e567ebe5339e6842841001513c0f7f3a4e1e204b7014db10ed6c4244c8863bc423dbb2b7fdc2f37e07817dde49b5fbdf795e3eda14d9641c9ee9f3e68925ac96bd0168934630a75f56329653d57e9b6b16192451cb8988e200c6ba52c76d88369429ed07031956d70d22f09593556833964ae6447aca2f3de582fc7cab748a6900da827ca4ba1a2850807d1ca656d30c0e55a0396f205921ef13132ab67b857a0ecdc64eb47b12c6f9442498af589205526bef76b144e4e67a9a3d43999f0bfd923189a365df836c9e56ba8e971ca3e6ac0899bf3c3aa33b0118c2ead3dba752a26ffa5cd63d07904f202a44d532640b8c07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"78a59d0adbd61f1774415fe188f5b53ce7c48961e0c67c66a04bac7711c1f20a","proof":"2439cfb83f0ffc5e683d030c41ed6015743ee33236608bafdd9d34617e0d11334c90188af6eb5d628713a3cc71be33c079c22b1e236cbc7f60caa2dbfd8ecf48c2bb0eb6411903a7444b78c397cde7edf994203ea4c72eae35b3abea0c480359dcce94bf1997ed093ed4c3fa4efcd38c52ac1e6e680c969039e6da0f77387a7b80853b92fb63133ecb98389c74fa8d70b036ff60099148f089f1c33dabf78e00d61b0e72e78638dbe16e077ab771b6caf909ab5fd43024ee80bc01b6e3fa050c92f95d276a964094eba0a3abb296539e535303cad19d7ec1055ec4713aab3600e2a789bfa89299378e671dabf111633f7dc41ded62ef7d360539fc9b0139616aa689a410ed56df4d3a7269581afb75efd57db7dbfc4a00815bf071d5e0cfcc5002f5f72e95271ac96680c46796d86cbafbcb07075960300f07e1094b8bf5b069ae777fe025d4a7bfcc0dc52e7c137132c1fe999b015263ae9cc2063bc6213a06ec23be7da517ceab4d390deafa6e2aa729d238f399f8455fd92831370d1106132621ccef1b9247afd02fd2f828cbfe811e742abe8ffa76011dff9140f42cb02286465f5e91509d56c5f049ec4f7c2d63a803a127cc0e3d25e38fe2184532210224164d2a5c7079e3ca86c151a2975a667bf4f4f446537714020863b5726cd969e210ea23bc59f53d2b8cd35a668307916ea5931db1b42534cadbf41f228487528c5f381b3714b859237ee4f85b49af3231f640d93fb28b3fd2ed147c544a9c3266e8366b2d711c69ab8499a6d6b3cc361d659a59d84a29231fa88846bec06a2a6e80998919f7711ce95459ab3794381231954d5d95c98a2d29d0ae1b0cb0693c7b9fa26aa9ad276b9ffe3561c7a4f803b4f01a6ec0a08ef96fa756e1fa8c4506c55f6ca1f7c536392aabb136b7435e1e5c9fb1b48a336f6ccf4c615497347e06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ba2c8bba9f8c6090b068105a6a3805dcc1fb536dfaf57121532f730b0f64d473","proof":"50aabfc26d201e89c8ad4ad622a613bf692399a65a850a8830f470655cbfbf262c2bccb552c6621b5229e3980bb7330722a9924e366f1c43a8a83fdd3a703b6a44d8c22fa756fd3547fa263a5dbe49794fce933a62d8c36cf89ee70a08c501290a19bf337967d7a62921277d1815c5ec630ec1f1b3c8da4eff6876b934f04073a750769aec12d65e46a286ecebb5c637fdbd9ac963983aa5cbe0467cce210b0b1f9cd0a164450564cdca55228c479809a51161475b5854f923e22190a793d80481bfe7ffc8c5fd5a1b5e0037c46435e157a4f81d611fd3b93ff25ec9fe35dd00dafbd6d26922ba7b3d1283a7fef008707f29074f1424e15891edeb1276eef12f44146b8996d0023031d72f623787253644098666e888e642e0fe2ba4e0f7aa36f8426f52a73744425daedde8f45e06ee7fe7418476f4aeedf125c458750c6e510e554185333ed2f9e99d2e8f1b989ee4055ec42428158ed6971b25ac99b417617014a0563ccf1da162135430aab9872eeaa8abd4bedac97bab4cb033d360f92204eefc4f19d0eef541c2651f337b578c0bc220bcd1055b2b52b505d5962d5a41307bd15287073a06fa96d04fd839ba6ad713482e2d8412e15cf6b7e4fd08027e027cfc351a8ef64d7a8e297c00af34bc6dc2476f7167e2cb21b57e92a7054f5d169d5342486c484ae5fb5601ced0c05de97de3627d4b4c47f61d9f6b439e6f403803dd09f8bac698c6aea1463940354d4dd31ed06b8fe80a4e674e29ff315172d4d04c7745605d426f7b39eb724090f03dba2713a1ea2b36d839fe1845258660e6d31db1fe2d475deccc14fa8825793114de1ff071ae5b711ec8fd8e9f38946f52eda88a60cde0f79774b2f7fe8d6cef492d93bb7a2fb784c4d1120deaee7e0e82146f7ed56cd5149ae9f7d16a03d4eae6e505bc96d163c5b8291661d8f06402"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"741feacbf82104ef934ef0419cfacd398343fdb6cb311feb08ca98a126b8b172","proof":"721a465c2bf48d12431d08409433107c16462c69c7761052c5ce142d250ed2134cdd895b0608bf4cc34b789c35fcb31c2bade47eeeed3dcd489ccd06a41a9b40783c920d8787d73a4b4caebe7f7c6a59adb2a7be0ceead822d4d67679ab72b22083d714575d5eb1ce802912b03d57b9528c047bc59892f0032b71904a46e081724b3e1ee9cf521b46a0e72cc623cf40249fcdc42697270d327e9af249624b60377c111f4c22b9a80978be1b302fa03f0e4c08d8e993598a528b8f87b2691490dc5fd387ab928f9c5037a4554a80695924c48f89afe23b85f87c3744f9c91330af0f123c254da82b8c05d657514a1af6c615870d4fa603e0d8d9ac297440ccc4b72b2add79dbda6ebb43ce858d6676f095f175fc532286e67ab8ec6144494f759eeac6be357f7f76b37715d680b4319e785357a357bdb5dca12c100217bfa806612d78b6c21ee29499995a5f3f94a51b1fbd3ba93e0951fb0a14024080187936254df02e845b6f9c7db6377285dca79864b2c8fa29857fe9ae05049a800ffed36fe7fb7ca6c92b34ba0ce9ffb9f4244a0d4fd034f4687fe328ea93f30fbd51829cce65c555d3bbd24f726012232525f8ac806b6e0d5c33fc37379bf5f3f7b087848b6682c6ec78398a3e96fcc4c1e2c10cd2536040f21ae8b307ad4f2708d420b8aab36a1624af6c98a37956e949d6cee3ce34a9657b8bb6633a11bc2aafd3f60c67cd496cb75f1b144ef16740b723a3f06ee9478623200ef2c2114cd82da306352fc65d303c390817b42e62c5b6da1e90456ebae67ca032b65d02c7971c84243ce648f3389a2fd37cbf65d42202e39af0204652cb8f441aabaa9fe1953c6423f781d6f64f4cc74bcd01b9645fd5d1bec433f5d32aab235c810a99e4d4fd9fd0923330bbf3095e22db039cc55614af94efba17722c3adf0a42ea05333a45ca002"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0a52b5819346dde06335af87a8c18ec6ab491a79b58852cbcafeebb347b76422","proof":"ce1b0b78dd09f18c3abccc1c46e5db7a15c7a0cab2dd8c81eda5a4de2c9f730ad8d1e1d08bb8e8166deeb7700e6bff5c79e17d6f3091ca8ebb43a0219289ca254a1e33b3986b8040fec5fdc7cd15b986ed695be8df2f6e3e0b4142129a98eb37a072458612a5f87c5c364f21457d19710cce9b627360d722f9621e6bfa3ce13aad380abb6f91b48781b9c608d1492e7a7b93d4954e587fd49f71d2476161880cf2c19db2c568d063173c8877ec795be7ec0b35bdbfb8cfdbc7c5d8eb8e45ca0c0dd91fcbb8a29c944d0410a0c17101c476d9d4d1c517564803e52860a662ff0bb288726b313b7cbaf97f4dc6ce3bf18e8896cf540c6ff398d7e525550d0ce649320157293f82b4248d57c96e18787c5d3e887ef4b9b157abfe54cc56b5bfbe7f00cb4cc1439928a1b93b0febd556c3e0076852375a212388b1a69f91b55dbc7846b5369594a8f42409b61ea59f05b4fc8b7c7ac27c07dddabbe6af50dd7a877f5e94cfb2e8950197b4e0f0bb574f0df0cee758472353b237c37cbcb770fdd70762e4af1f4667abf823402af0e75841d388fdce20a26e1127298def0a6ae59823ba01386f149f797ca4317e81085e55796f4a17aa6462395755e23f14f3798e1dea5eeb47ee20f195bb7f8cb21d4adc938a1fc94b2a696e2744f4515e3139404f926a1a5aee821ab5e0ef73f088432e9c851bbc2fced895bd5b7c0715213c32614871eda41e99644b152111b295b35bd578c9b9f99cb62ae8b4259c41ca248f0c8804cfc7e3bf1b1c4b0bca3687114591b811185ef287a5e094576946baf5c55632ff2d6b5f2e5e8311a53cf393cd821fa7a97443bd8914b01c882d0ae789e640abef5c2949dc3fefdc6dd21a39ef49cdb71312543de49329bd20f64e3201120ad2a456b01e68496b3583f25285b95eaaa3dcd7a41cfecdc6e761bb2dcc30a409"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fae39e2914d0e91a99cf00470a96b911ae15718a1495bf94e8ce2484b186c404","proof":"822e8a69c1367ee159ea8f99c1334f83694f5da7a86d88128f19201172203f04282f47ac2bc86d8653dda9691d5f6646888d8f7408ef3cd5c90d81d13e5fce7f6ada1e83b6b92e2e1c9d9c37e1688467e3855329042a761da51574e0c02af94082b38fe31ed91565607994f3550e4c2132b59b9cb3385704bf98e48fa1f1d7248f7c88dd79e5f33cfb816f3a828e3cdd8301f16209ff031c743c1781262cb00be73870e74823b59be8422a3ecabb93b862cdb51f827983f87d99930b4619390f6a07cd90295ce64ff0906aa19da5af22ed4c1618c98e9653818b64db05dcc90568a054273898210b85d69329eb07e18126c01d1f3d403e39307bb017c1ddf3477081ff2ec7a877c82b2799def64436f3284b9032e0b2f564502f304de9ca7766dec0cdc07041b07bfa4a21da44b5ead35160cf77a2af5bb25fc5b0e4ff83e614609e5ac7cc6d740cc0b9bce872c0630625485cf3984774c97857c9853a85ca1080f91a7792ffb6094fd9196df0b147bd3941ac702d06b14933b8d5b615dcbd0e560a6b32c3142f78d2e43c4d70ed733049d8d3e32fd60f33b2b2323cb175ec23b694314946f911218695ea23533b878c139b5148e9e1ae19e9532acdb814a35a7c85ee332b62faa8b5da02bf93584102826b2e8879dadc926264803d24d0e704aefe6e74c1966006a05ed088a496a083f00c2f8619dd9dbe2ffad83c0d10044036b7b941b400b7c7e30a4add1e48cca0c3c957e1e1a0793d25eaf41d4ecb96780ee9be121eba5c052967747a0b80e81c27d80a6069c557514bda65eb42076c56a2cd3640673ee94aa0f6569a4daf5d538c06b319548ca812f3bc0b345ee36678a58dec3a74994fb8d02b76f14b60d766e7f48b74f3ab9bc933c0514fd3bc230810fe48378b9d6d6a6f4862521886551df155de54b41195c8c2df6e73a1262d0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8ac19fc2d6d48cd887453f2f9a528bf6f983948f41cff4b570c33df8bc683560","proof":"56e81a5ad68ba60e0fd2f00192691cc43bde370d2f0f647e92fa28cb8bdd5e75882a64dd92c40f2a227f2841eeac6ee03bb4e3c190fd4b09901755a8d6307d6e740b15871c116bc5f8ca785ca39dfde68b4cb2e108d18443d3317e8e396b7133eea6365828c0ac669e77a67c0c16144d13bed0b5809238b8583c7f2b8c775520ad9346aea3edb34613766c149f27641df634a96f809b320a3ea83848d665c90b902bda8af93e5ea68ec388a10f3ac5d1a1ac39e97a5b3b43d022624782b9960f6df55815f03e4fc44b2664103e20bd2b51ab7358e14b4467d634fde940ae1c0300b6cfd60aa033ffe7c47d75520a24ef4c732ee53c9d9bcf9969622b87d8d07a0279e89f7f10d51df51d16993f1b05bbfb34face34f074df78d75b7af4866b55bab352bf7deb50cf4cc4fdf5d51bf5b7d6ad071f298c7779debcc9946f86804d2c6c0341e08f9cb6db382f6a4d25845bc795b48a7f2609607482c2caedbb5f6a622e877880f89988432497f4d29df3a7db58f7422029f43ee64b7b528d2e320e26d02e9a6de6956d2c1c527232da262a286128ca78702217cd80c59240868748842d02738a2e69d7aba0a18c031a102d6bb1552a5dd58a2d98f583b7eef00779e87eccfdcd1ea5ce6699127a9acdd726657243477efb476b1031690b14966716003f34e45ac631ccd77d648cbc8472ee1aa30fbbe72610fd96efc304803c627bc4eb516913aafaed74b8ff151b2e0af47ab86d62eeb5bdde8532aee64a9d815d620fd915c910d389746aaaadf619988035883f817e5b8006f83e922dc41d70503af509915234e0d8a0e5bc1e773759bbf9d16201b3d05fa3de2f0ab5a560985ac3923adf91918e9927231d925f06926ff94c01c1f85d8ff2d17e2b4400d0350ac4054299cb6c399e4bbc9b7de2481c8ae3d5af993930d259f135cf381cd0e705"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0edc67cf593af342cd5b1697a79eafd08593b47a04ea94e5ddb9200db878b84c","proof":"3ac407d5ce1a16777ab4e3eb58529c856b0d9affb73e5af8f65a3d5c491fe82c10fe053093c10b76dc9bbfad335bfa164e711618f95ace4cff1c9b7816934a35d8bb8af94ecc5b7489b3806f75014961b198e12bafc02f248e42001e6b610d74f0e79c0515117f625361e9cc7e049767988e98056f31f56a354f0f880ef0992f3d1af81f9a376093be8cb623b7db7be9aa8e65c2ddf3529c497327365760130c13b3c52ddb4c2ccbff2973ff976e7d9dccf3b1c3a72b367ef88a543721f9d70927706df518091e7dc63c8de24671cf6eea6112b2a76b73571aa0925d3d11be06ccba03bccf146cdb9d17733a10c478c74ac25ccf4b117ba1e1d81d356784905dfe37c6a6de3a96a6d9577ed518828f76d1252ae458cef005ee8cefebdb858a7ccac585fd81902469169bbd40f25fa83cd6f6aba97ce18e5feae29be088fa2901a6896a237e0ce511bdbaa7af3ea8d4304c31281197dabf624b20556199ba1c477ec4b1a16ddda994acc618ec9237d21a246f2ff7e7d9a45e8a116a6afa50a02830962b7f600fb10c91cb82a4bb59eb9aaee18c592b4103ba07d3f050bc0e4e1828c7b15f2fd1a46f99e7301fde41ab5954ed99b2d01e744b2f07b283f7ebd93400e395d2c858f39a53b1e718403580d7fb5367b0cd31b80c1a276cf3222f3c242a9423d67fc51a0623df8ed645a0f8f26299ca910cc55173cfc636833a9f734d66148b08b45eb9400e19651d2ec5605fcb737714b3b27b7b7dc82db2addf687198ef5dafb065eeec6224156758d16be80b5fa26a0b78b042c74d65bb38f8a21ece865b1fc70db1604c48ddbf07f9fddc6e33188d015830b667fda066ab83fc3911509c477b1630d03d79917e513c62d77d05bb4de323474aa33579f0a6f7cb0b6f2e6734876cfd0dbd066e24ad64e335eaad15d9d3045c20f95acd24a1ec5f05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e2966e7cc1f2d5cad584162d96c8a00f565fc2d32f6ffbe74e2aa7ecd827f073","proof":"e45ff2f99c3277d07d544aabfa2e5235f6c2273639762c78817941b66c8455304a6dbb4fe167669e80cc088dd4ea2d0c77409c814d45b5f3aed2f52822341055b8c2e9352c438b1afa5a8f2d38ef0b9c80275194713b1fa0c1a0c87fb201a95530b54ff7e87c85d43d9196111ae3b1b94c1344e7cfd9f7c34a8653e3616d2f19f9b3e5ef85067d4862c2bdb2d3131797ee806f7ff2c2f4a6d19477b0912ec90a9e12825dfd810d1be6a2069bf4c4d86ca45f0eedcbc11bc268441a01fad6f30683ab6c0db8d6427a1f30c465772f92cf02d1dd87c51c6cb4e3a041f9173538050cbbc3fe5e4dc3f16ca3344c3c5f50dbbe9972c46ba5624cd559a212ab0c743d3637de0f2e554d484def89446f534022900a53e9179a4a3c77ced4b55265f1583ecf9192433a0845e720f8b7bfd0711193e3eca4658632712cb16a166a53e524c0b43460c64620499d4368c1170967bb64b01123ab8798f8df6c7ac12ed9657a067e8c5993319cd2b513e04671dbe24c08ac2a961d67e2dd1daf1269191a50305eaeb2e65798e1f7ac1195e6ad9a585f3963175ff10343a6e342b29d4abb116dc8b30bfbf10a2b468ce385b89181be82b1e0fd105cc3d0f9c0525e7a783f4a2872200fe9c1ce07bf629946ef464fd2fe8e33d5df66b8cf78613556d523c5a13db45d7cf3248e1bad593ce315cdca6262f169e168d69c2f482f2645b18d213f3818065501fc0d67b5e6e9732343cf5ab71614f7d43771a6fe86ff0ae26f580d3e4644349a22e3b4cb3403d890852439b50f3f1133c12ea009eb60723662716555fcf1cc32aae787af910301632c210b0901cae05bc9b65f9bc07c84376960d46694c83915b212de2f9a4642e2b8f51c8d488f735e769149de1c1ab8a95a2dd90611a51c03151edab3dc08fcc582bdc1d88c2523dd0777b0d8a2e7e31c89408202"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"605b5925e50ce8ce3bb033c7d152caaa0e479fb1a82208aa2d52bc8da4fb0e0a","proof":"ba0429af1d66186e54c182789197893a6ad3742068e7e1afb510fd4e08c87748aa748ba515aec4e5c9c249f1e2153bb84fb7f500064cf590b33aa86b0362a50a981b32cc64eaafc4d7b5a72a6dea9eabf62eef936553fecce2e2f082910bf804da961bf8b22755f88c9d47fa5ca587d05a7e4d0c0e2edc6bb46f2e226c9a65324376e0435179361f78211908c05c4e8d076c239ba7f64eb0bcd00dd7bbea860546c4d4ce702160557a7c5c075fd5b8f96b1ef3262c64604e09d1ddbdafb1e50cc0430e2ecafb59d51de26c23028ecceeba8687f81f886fe6f76a85f79b4e6c0064b8622c14c5602a2055a051e4c9e5541629edc88b44c21718319034a338d8256e7da45aff312e1c734dc0ce572bae95e5d930f7020ad54f3af85f9f5041df0430e799899be06ac2ca7de43401431b91dc7b74de68a3622ea5148de606a18a314056e4a61245ae60f1ab85e7ca93cd57c7d62c32564f5bd81ff56daf429f65475e88e6ccb414639b014bf241e57edc25d3b9c5e95616f734759da5ef580b896eec0ba12fd195d5e32a59e1d26df154754da2523064301dc5d02fe85f435da076bca7134473b2de9c5177f7fe0ed1db4db7d1f671525d6d4a45e10c8f6a0e3c789650bcc8cd0d553681f98970c97c97a63719e0c13d3973616b14d3dd617c6d5daab6cb8c2f41187d43ea49c7c16cadc6d73df57d99265b70851f46b8bf81ae0fb0a64bff1dd424eb3f35325132407f678af5d531aa84f5434bd67d84e64fe62a04378a1729c5abc327d2c6d0c8a6c97789b9103cb18e9a1daa2aed8e8437a83d88b54067e9d7cb7cf7874343441066fffe30f50f6dd36f0e8e97bd144d93c00523735ff52f1eab338b81de360407d4ec2fdff7b9787e95d978cf01a1dae78d024cf9288746448adc339748c5af5ec14258182913c00cdc37abe0abd126789207"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8c62dba8fd946cabb8c7df557d30368b66147a993529b1d36e72e4708f83b645","proof":"6e9f372b44df3eadd0c940341af4adb4a5f804970439ccb09e74ac1179080401fe07ba6ce37af6f6ffbf041d1097944bd4182d4069a7eb043c434ec13e675f185ef0fc3c73f32f7d80ad4ab749f973ea2ff1c231454b8132f46eb6db03dcb717563df9f75202ffcbf72d0dbfab13421b34eb1cd08c8dea8ebfcd03a02c7e5f1a62c18c5f25aee7cec3da7fd0efdc58ca3793a169a4cdcc06c7a21b6315867907d827331d5b25e5f1a409602d9f7f35e129f958399eb10ed9ab768cd2347fd10851a21f635f392db6d5695002542dad035c4b10f6ea95dfbd9e443f27ec62800c42ffb601cbbf9fa4778638f7fd0c8aca174201912e693b0759ca914e53c486530ebc6feaa39b47354a1c11107ebfeb6b745601a01f988ed30923c2e0213fdc104c7308cbb8245a5abfccc7d7ff181b848bb84d843574c5767f1cc2bb3d91e80aea2b867d3a8ef1cacca8f524e381169201884926af5b5693887c4f58c1fd5a33e286ad42fb667f0635caacdc1ce44aef131d024ccc610b144884f3d9d6c64953d069f49117d9efad7d53e848290440ff35a716f8c5510380bd2a324eca802515723887d05f7e78b580a637a5fd4dae5046b13b850004635638feb5d5e0ff5d11c06dea15d3346819e805f2cc92475aa8f3e5b672767bc5d5e94287dfa6f55d6d10d846027e61c996d97e469ebe7f1132f22b4ffc505c014fbc56a273e2c3c021325cfce91d194508f0e81740b2253260dee90786a05cfa94161ed5ed052d390a248f31895b4b03f68823922596f75f47a6999a9bcbe628c62e3be00e5f7ff3046a390a37a2515b87a5b74b629c70d8a4a18e9504c8ca64b81b57260bcbd6423d9394c288bf1207e37ab7fe5d186fbd3727017d71241cf6cb3584d34460d44308eda274773c41049bd838d2591c393d71d5c91c85247af1de2827ed5950859f06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"32ccb2bfcb078be72e93cc904e6a943b56de288c3c048fbe0e66a32a7d26fb20","proof":"0ede09b321094a11ce693f8a3ef58a7e0ae21fcbe0d1df49ae9587af9d3305136e6d581058a0c19b1f4b0d7fe700f3bcdd74443475d516946b35eb5a6e55f77d149452e61a293c3c657f97c74789d6bde9e09667a4c4e71d4ec1037bccc01b6ca263acc675b20df3cc8c1064c09f9cccc29989c441d6da514db0f9cca94e7d451611def16bfdf331c7dd72fdcca89227400a6346ce067402cbcbd701363fea0c7cbc51dd302bb5a8fe2fe1423cfd69dab82320badccfadf4edeafc4814cfc40204e831351cb4cda91aa5172f4afe5658c3d9e67de740593783a49d7b386f4b0f1a6f1631e279c6f57a6072f57cb690d3bf39cf874e030976714800864562924cba9d1d5db7395f732d05db5e7f7798ae84d1019b1f0413e780eaa1422d1946019e8b6fa36144fba49e9875f3cd58b23be4509c5c473ec5a0fd9598a06aa909281a8c14718c17af794350336cc4671ccdc51838a0f4b0eef643cfb3f51cdca91de0f177d8e54d6038a781f3d0235c8b6916d630c824c4c5fa79106d72d9183c243eb131a26dab996259e8c4ae4abf97d6192294c9e30a45bd1dc1dfdf64add31b72d8aba804de54d4830f91051ebf1a63073b91a3aa545b049e6227d329100977003d78266e6618e964e1b397f43e41fe0df41901d2fc1b64d9f5fa8e9bc6b74d8ac099fa09231b095d1eea4522ce14f52232953b2c151e4a2abc95a04e61ad4b0cab984abf3f11d058986593421ea37bb3aad6639f301bddd4a374075064440860d07bbd82e1b670d1b81b2568bfd5b929421d22775a3d81799316e9beec8d1f4a043f07be73dee38746c9e3b0b9cbfc6c745bd2456e666b388b30edeb6415799022b5b0d955e98ad21b9f94ad1ed4c0dad9351c4e8c5cacab7ed298cf5f0f0e91b5a5d612eb37f28b3cb4f436c42a62a5972d81f5e6728ce01c5c9ee950b009"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f684f439b4052d2d715b07d792cc45f6efa69eb6a5ee5a1b3ddfd6dd6c5f1613","proof":"a820b02a6c0b5ffffeb3638d789f8e586e4ba9361f08513df6835df94a497d1f2c1e67bf6dee0e895e9f5689155a9f8437d90dc388213db40be09ad6163ffa53c6e8ede61ee117fda701c0d7152d428b27c3f42a7d59ad6c7b9e6d2bf7f53d0de438c14e5ffb1f0f1a6f77edbb8990515b93bca8ee9fe97a24a779f4ef5d376aade3d9009de9837b5bf8cf12e5071be1b80feeea7ca426276b12ee1a54b614024835e74ee1ae5b7ee1115c08fa04ff84c1fb80e7248c334ab1f3b6dbd95c4800db8f9597930e3623a77686e271650729582d6c21b4db900ce9afe0f3f5a9cc015e2635982726dad360d5cffc7be7c577f35a9e65b7dba4e8de3e2ad92bb0b65a22c8343f567d867c1776a1ee4b664620d97ba24ac3e0ca45d8b3990ff3fd433756249d288f45b090f841f28d042494ef124f81f81b483a3ca2d6c036f952700ba0850a0cedbb11498147920e5bb8ed04140663751aed455f59cdf7c3aa51d87c66388f79608b678e0fc715b458715bb8a64e12a401c4f619490bbcc18b2a8e6be2e3fa5c6ce8374dd5baf7c3889da761b94d12a4463062f75d7ba68202791a3888cda5792a10a2f8ab9a17fdc2936f6fe59eda24fd09b44e052c461d035609180c492f87800d70462c8bfd0dad1a4309971f898ef80270a27700ea13b78b1041c839ebf7ab9e029fa0d96cf9383206d161fa439159393c40d985dae8601d653a4c91c737b548f7794648d9667e74f554affb82150fd7d15c6bd50ae185d8047db228b3118f0e19d35621ae4d253bdfe2030245a8364d0d83e3175fd7ce3ae20a6eecfa15db2e4274fac43e1c1709224419fbb6948b267d3f38b1362adc6cb60bf1760b8e31b422e47cb14850f373cdb096eff27e5d4ba44edd9ae18d78eb230cd45953cafeb3d648de993353dd0a3a759477a04aa52371eb71d23265262bfc07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"103ebafc5db71d0eafc9a24c8e2c09ef61646babb5f91273930bffe2602e6776","proof":"aa7c6d737a5a9cf824855e465fa3d524f2c3101bee65806b657cd9f14d93315c6a2ff25c8bc68aa0b44aa0f89236eb652b35cc76e3439c7f3abeb41a3497ec496e5ea806b69d7f43f798fe1bc40644a1c308716448de66613b94dbdfbbf63b41d0dd673891272ef2b94d665b8d3276c2cde963e6ef2bac3d1f94e115d37b102c84183b4dd4fef46875fc8f1273e9a6fc3f4dd2cdb873409820a257de02c69a0dcfb358312d0368598e7f1d9596e2ab1e2e7d4ab80b78083c6bde34e0a9721105c7a423423b81f49ec9e9b8a83634b50f8f3496b0c0c0a3651939ce3ae0b2aa081605177c33ab8d347019f0f7c0f6ebfa16a7276e5d313436dcd7fb08f6952c2c7af49a136f8b50f068920336218daeaf3f9bdfc184689dbd1729fd74ac3ed37da6f32dadaf8ed24017c59f415c6bc96d78daa2aa29821c457557c8ee8b2fff4788f095e2ca9362393bb8f9941d70edb58419e7baeecf57db13f9751c8e626a7a9c2097b6e4d11ba12ee5964e1e3820010a22c643f30def7cca98472f82a426629e3be448bb246750ff1ae450b54c3409defa70261c55b5d8b95ceb0ea945022b4a9c1d80d97db0e4dce4eda0f7316ac80eb7e4ab98bb175c1e8ef4d62e0070083688466e729c2f1da003462e3a0b67a2cc78d2a68e52a202d758ea5032fb663cde516bc30b58a374877be2bfd5774bdcaf2990fc80c266e435165e55c5bdf7487c760be5882b46aea0fdbca7ece74ae0f31fe7d042ff404ca04492f68115993e563eaa5eb5d3bc2b00709783e3218fd14b3ef7e65270a018178d2fa3d3c2c304acec7423898a02f1ad9c6761226facd49ee03b754c585c7369e6e31fd99a0737ab42fcabd0a6a03f0474cef417e1279acfb90fe02af6b9c2e5fa423a8b410c082d1375ada444ee0bcbf7ea4fd26b05f355d6b64cca81360e9b7bce254982f40b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"722bff68c8a247407bd8f1443e2fec4928d40ed7e46016bdb2ee1273bfccf21b","proof":"6ab472c36bf9fc3ef3ebc419f2857097d2d83e87532fd4f63200588106aa37611e6c207e6098e523a8c674418262d6a0ae0607acc823ea065c0e958dd748507b32ff32ef12e47f7c9f577e4fb26b6f23de1423dd2f9559d09974e8f86dd6e44c6cea5b546e17e0dcb601cf11eb5edf287bc534a82bb45606b68a177276704167336ca471542a050b39983111f7ef8b206a4b1246092ce3f536734929a8c8a3080c6049095b1240a4a6a6db10eae0383bf9502e14d7ef451b1940e8bb0231ac0f5a51368e4d61af6e0d2a874b1188b75977ff2e4c4cfef3186a9909c564720c0878539c03b2f2d2f0a5066b9c4b7df1099be7f914ae12fa4fb03197fb1a25bb139ea42d4a0a9cce3b6a92683274ae727e078f3381170fd6ee502aa93f2139157fecce7b6087a7d756d91348b00fe28e693074519b32e0ad8313af5d5173fea43b7efb587758cda03c2517a640cce593d00512b118750e33eac20319de74f65c2d8ebcec2457b05f2f4f6c1480dc1f39f2a0fb14bf2985640706b5414747f7e11f8048781981225d08511297de9ee3c4b2a9f11ac9ae41a0b7d6497d5b20640213eef485653877a1739b8296681d517f26881f0b9702b417bf518f5418888ccc053ef3efa99a8c961c52c77661829573e5609c50b7970692a5252f77ab9594db1ab4ed5c587bcc2c360c5cbb2a0f46caf5c71bd72a1e77cae6bc97e7f6e8c79d5c0ea2b1f44258642d8cce51084f004b32c18b77c4f072544babaf13943d5f1630dce642888ab028cb8af1a15dc6c372dabb17f99f336e59385919ccd9b4a7311cde1cc3ad9b1137bbd70b35283cac718488584d5fa6659b58b8ab4b24b632fd564eca0d68447a469ddc387d1447b0f6476cc705ca35ac0675c29bb0f2031d470c85bcfde7c183682f4e534a758ad11bc6759e15c616ee947ac2bb1beb8e168507"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9e2ba6d37925da05463b2ede103e9db513d182d0427d469747a9c88b0d92cd1b","proof":"ee377f7572a5d78b9593ab77ab86ed2aed3b637c837a3989ac60a63bf671394a0a0136ba3c6ef2dc0e41fae4a526515ddb697cfe0445ac94015a76d57304572b569f9f7dc7e9a7ef2503f9e025eb13b9742bc9e7353a70d6bbca0ff16f7d5f2feac9d28260849c4eb9ae3cb5e3df4aaa95c74d6dae38ecad9e65dc9cbb31871a5be883bc74f3daa7ebc810c00d7bc1e0abb3349d971eda1e67c2d7fd7983aa03622fc3cf8745eac72528389ed817ff6e4938b0df02bc7ef394c3c4fddd704405c52e93d1a8b29913605d9c4f2c3715e0a11c299ed55f1c7a2363c161a7cbc20c947259c96677b3b12ee63af9c0408142096020e8cf7c88d1bf555528c392e01ffea80d738be219b8b032287c39d6ef048e88b06dc23035faf4fee14890cefb365e805e98366ddeaca6f3b2114023db0d4b3d9e89e02657ec135e8904470ef232e08ad3b405ac776c5a53b1bc68ac3f077992c1f7700748ad3dbdc6b42072963174876096707110d568c1750b55318f80a25ad6c0d9b37ae651d1953f0c89c34e5826fa2d7a6e10ff881453c9c50d9f0a66e1e8f5e71c68bed6d367ff988e2f442ab928770a7e1da3ab9a68d742571be0ea277a8efeb384573d6475530300624dc8dff61e1d39d1d03ed6b01043f6195426513e480eb9dfb20cb5ab98a2898942681dbbb83d42aa83d1fa53916e7118ed504a11802fbc6ed96a2fd26104b7480a3019572594a0b4261d99653dfc531c7a9b146c46c2fd4cc7f8bda71688e8b040bcf0adf8d687dac0ed72b101198fe0f0f0c42315d1b51aeb37a8ed7d934f2a3f9cd8560f80ac9f6a2b90465cef3e19f95eefd99bc05242c9d473f01923b1921e3472c6b8cdfcb389f3b81dcb076cd94e8f64751c8de0c08da63500112785800870cb2823d664e79a60363524ebca84a30abf373868d07e793fefe5fad651a104"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"12611ede77890c2ad039c175e4b65ab51bea5329f8a94bc1cd6b79fcf748d804","proof":"3c0204929b9f88465b7978e3d70da7658d7c45366adf00e906548c4335879e0b724a800fcd233957ad03d3c1bbcb36ad452c3dad3b1c335714ba126dc103651fbe9d696470377ae3bfa0c42251b672a283b2b93154a043b167ed0b7198c9f0029ec699a7083aba174c6c7afdc8f1f31be4d94b19f8e4fcfc8af08455f651a1464aa85f8aace20f978989b1ee82cb304841b42c5018cc9df23ec574dbb375f7005609f05845518f1222a57ad49d4eeb7bd532fecb6273d4f8d07cff035cf78e0fbe97ad0580c1787497d5d59c023351d4acec8daaaa24ce54c62babc6c6f62c0a90c25731baeb7f49a7c06f18e539439fb82dc386270579e90691af29e0f796405e5c0e8b4c90973ccb08486e14cf7712bbbb8120319368acdcf1d821fc2d0e38ece6c56e1eaaa81ce3ac1aba59244a5a11482391cc5524cd9552586104275b25c669ac32f4ad8101a9e89b28f46467d30f0962d1df3f4d26bc5d4be0971b6f0fae4903a4a0c46adaa7e9d6756c107ab340e990a9f615d4daeb832be4fe4cd5402ca3d574fa0d30d3442f33cff7cf023dac259a68bf0d7b0e7490b5eda4aec234846beeb338926f2a301adf7f74b6b665967665c7cdc2bc536955c00394c1a04dce608be546880e93520ebdffddb3faefdc8b04018cb3ae480e4fd373b0abcf013ac88b5b07c0b7db8a65175e053b050d9bf428fc7be128fb4cac31762740cc70042e6b96ea7698cf32091e6d4d12c0444186c669299f129bf2acb3df1e36ed401e440525b8d9ef527c35233bcbab223eae2daf25b8d00851679c371fa6149b52b068c5820c4f4095531c6921e1604ab67a3024ab7781446ce3da2abdd228a55c7adbccd0d17395b199d34b7776ef917755d98b0ddd90f501db63a88b04b48a0302abe8db81db6a9d0ee523fe893666e080f3cca215a0a83d0b26799b85929009"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a6d8a29a42764e6011cc2a9c186b247f11ce21b1d816f8b7d63ac4473bc4144f","proof":"2414d6bfd5f8d348d33327b9f6aa6ba7788387bdf51dd391ecb58cbacca9bf603433792138f257274c3a65c8ef3e70dd39b3ec83f0a4aa9956e72d06ba25c77a2093f42731e0e70030925695c26bc0ff67f347e7b583fccfa65f94176471f06cbc0dcc0cb6bd77fdb4148558bd9006e0c8dee4ac41ff3f71c58e049b5f4e9248f270c65aa249a1cb6c140399fd53cf9ce44e9dc480e7f03c6aa972c06b49d70bc0901d8135ed18dda77684d51d7efb92ccfd847d9226eea0379da1d8386f9d0d4d20e8cb42be47ec432f048f55469183fbb42f36d4b52a994623a688f6c23c0b8ef0138c9247a9d60e9678b2d9ab2b10b80d087b18e9d693186af685dc7c8712ee91cd5ef0ea14a1b3bd8c8cefb5dd11e88f562c1f07c2baa2c7b3cac19d882b8270f41e73dff002cf48059d2143090d1b5535f3487fc1f8cbf55b683e546b6712b27d51ed5cbd6c767e1eddaee43841a278a0e84a935614533ece3517b366121ca9cbb6df957ca74428cbf3496cbd7b9ab5f612bda2100693b0b68066770a4ac2aa6011516a431e7d42c93354e5f3674188fc0856fd375993b5992591ddf91862d78856e02935404300aff6c7bc6f56aaf1b7a13cb0deae7b8729a0b1cc933740783c62f86f52db3316088c6fefef8ae0cd5bb75da4f337b1a84ea63b257149609a8024d46de72ddda9e39d816a596f3ff776b7b1c2951f7d84693de067a564c0801f15f96719953d1554e2bfe761d162c596cf4d8fa10fc9206514b0d99e1cbe1666cd1afc03366ddc0d9be470be312d0fdde63d84d720560141e73af709093c1a7fcaa0340de6b08557607a70cb5d6d94538923591901b1f26757d9989b04c1ee5989985b03824b06c56e700ff39333a2a4d99d40c1265d87aaddc2928809b4b39f41f7db99f6091bf8f3fcceacd71ff6a0284d20d956a4922f3de3352e05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"422430dd00d03e24639eda9efc5d471ee5bbb6057116e7726ee68103dea17630","proof":"caf6dd28eadccaa1625de887d1ca1db6126d59fcc9993e9da128447f75f2915ef690805dc6f9981831e6791511f6125098e9758b1fc836b0fe398bc8c7c5bb1256001abf63e541fe81dbcf4078ad6ed6e1fd1d87dcd628f58261f0248f66e37670e9eef775a7ac66d6e3432f870d55992056581928429e37799d0380692f5a74c37ec3a113bf50f3e0163d85a74762864c8d28954201e99e08a12026b7908600ded6824dbfcc3d6758d476aabb836c73a75e72eb9b505011335f178be5f4be0cc1c892616d2e3116797af332de4e309e48646590c66d102169cb3c05e2e27402227afbeeb13af0a898a40cd3f5646bce5dd786264c2be980a906bd6061e699245456bfe204e8b6e6c9946458f67e407beb2c11353ce93c96778ec1f5238865247c47609f5670a3b211e8a60d79b0eeedf8396cd600a3aeb8ca526799d8554f76283763ebe9f39ea18186ed782bd204bb2b75518d213326d977854467c64d56574ceaa7ca16b68ef280ee042f0cf0e9af9c26ca5097f24e0b95ae786dcca9ff4a92b37fe2d7cbe74189c6f9444b47dc9e486a86b5774e651aa58667c7270d8574663ebb78df13509eb60a25c2c5a8fcd447de206ff5336454b852f0273300ca2f52c67315b82b41cfc907b40da2fc68c6e4c24e6f414e338e5e1b07ad2522763ef6fed46635505622c099f2e6e23208134b03b8c6c4aadfe209083439ceace0141ae0c9db7d8d81b6c19804304bb41be5b2acec6c50de974834f757b8c99ecd5b9a2ad1c7a34091eef952fd8ac26589535496a1315aabdda507880baa3fbb316c4af618bc6cb4421181dd612b172c8a7b945994b4edde119961f22f6cc59d0d7d2f2df9d6a56effe06788977cb43c14f95538504b4304aa8f5bcecbe3ef30da0598abce81f051b11d4fcd5fe18c6be5aebe559c2e4d78b6b8d56bd7e13ca35b05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4abf5bb95c7c857593a2eecdb8eeb99a283a4d76d940a55ebd4d1cf61578d935","proof":"6a9d6743283863610098cfa40e72eb74d141ef69e8ff18309bf61bc18c692531868cf43f849bde266ace9929205261e6ea3dcb92f7d39427f3bf486f683dd85162cf6c6d87cd32aa46dcbb796b6a1ae070dfe02ec424d16b500e6e4f556654183afdd588c1f57d8f9a64205b671074dcbe97e4c10f2d938623edbcc482ebe8383469ae2c87f4d3cf50021928ed203048a84a3a29df80aa0eac95ce0e7aab940ba2adb6afc1d8014d98abf1ed4ac58b3b316f42e459fec719b46693a1fae98d029fbf09e7a32b5c407802a6eab4d45c99306a6d3f2d85501b6b23c99975bef9049e7851542388039ea077b2768128e68b623d63b289ff1ef837c7ef474e882227323ea59966aeeee2025b2236cf528bc8d517b74ea6f0139a2e491a5ccdd0071bce021def1cad57696a403322bb5310c8ae15ecaf8c3b9612145a68418716886300aedb0ca5b36aa8089eadc196aa78e08afeb4a59c1cc3556f367384dab9eb6320ef088b92e67e09214b59edffc27d112ff46ff38ecce9918b3a8ae6a22b16254009c448070aec10c693be1e2d3ec5ecbd369509b18a34d61085e7bdc4b7105cc87c101cef58d03940c60556ae846fb6fc1dacc886e63ff1d38bd23f627fa74010c0ebc39ca88c0dba3bb0a48a774e60269788bbda637001417d751b3a20384be2f546cbe6c0e9a04f87eca924a8ae923bdb9c5415619f466c28f1ccb71051039a3985078993df507048a7ac4b61dbb4a6b5de9fbe39af9647a029c5eeead82b24ea7fe664d0115778678bdd7d2d7b86f63d0b5b80a17282ff82ee22b5624367327db3d02122b640e3d306618bcf3bacc519f824c1d02bdfa24de7b5be5c816d6020faa141ad08f0a41bfd784790a15ec96b0956aff6c5cfd96ab8c0e81f34033fb4776cef3afa3d282c751a6a5c910629362bcaa6b65d06565dbce26b8a6e07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fedd8cc6663d021771b41f914be8ffb24222318cbfbdf616b95f65dbce49673d","proof":"3ce19be9151e11cf7b9626afaaa19d1c227d3ec6b077d2790d4910b46c05bd43a44d0d463d660f7e5658245bc62069c4b5fafc54534e73897161899bda7a0779580d5926cdaf29e252494a8f0cf9fb1c04dfd76b56e29ad8ade80388e99a46057ca26154de0ea3b23a3b6d43689dd5c48da870acabbed66a6b034df0aaecfb007e8820ea660a02d545f4a3db545b9bf9bfe7b71fc92fa5c5a3c875dec0bbb70f864b83b1e82a066ec8fc2466fc824694118b7916484b3cefbbffed48e5c6f60be0e0f7a0429eefac08feeb99e1db615570c9721d34de7f305c29fd7dc4323007f643ee9c21cbfdd3ce8d32f06cd63122a93b33ffec328ec268aa645459ad3210f8012a97a9e87a0f6eb30507bfe11f8791e2a811ceac904e0e395279d5435254e029238e248daa084912f471966bf5e37ac2ebc1632023e14e820a52356213571eb2fb5ddf4752e5ae4ea2ebf70916a1a5f497c44c9ba72c42f7149e066f2252947bcc9a5f54e587ae4e77a37bcf7ff5556b9f0e614f843ad284df8ce68e69160a968b5a760f9318a77d4763609c1376bd9fb29a603e44dcbdb4fc0e6a3c06158292244c3bac1089a7d27a45e6454a09e301135a139d1d9af6407e1e887dba73b8463972c27fda5ff41f96e15293faef470cebe336ea0557119799ed906aba490cf762bfd6f7521923ab21091aebf385d75133db4d45781baf9ad32eecc2ff4402d4eb758be2902d8f1e565b7815052a5056966a817371dba956bdb771d52568da47c578f10ded79af738691ecf5ea997411c9c42f062ea1ce1b3963f901045f707b130cd8a31f29cb6f77555a8b154137ce974d46e42bbe4d34dd6d7c79cb5688cfb90ae5255f890e9ba6fa2c647829f2009f738d42294bfbfc2a594b65b90dc1314f5b206d590e94fbb0e29563037135746db3dcf6d61bcd02e28203a92201"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"02bda5bed8cf85c057ebf997d5f5450bab50fd63654ed719d835a502d048d110","proof":"c405be0f90edee21ad7309891f06e17e6d6e88e8a8a698dc309d2f54c0f88275d2af615b38622daf5a9cfcc28f93c7a49f9bb89d61bbdcb54290717fcf7d7659da2590a725ccd4377668ed675a3f90919a1219b283a55c91fdfc49f3276cde7bb4e2910ce8022c917198fc9e7c9c4190c3f5c4ba15ff2c1baa3dbfa94392794267e3d4ffd84ea6e76191ecdb18aa871c1294ab127b8e34b4a989197d434df80d41c364eb8255994132245163bee159262b6a81fe084c3243c4df38e421f28709d0d0a17676466668d68eb99f3dccf6871303df2406162b5ac4f86859d6153f04a82c45cdc55d7bb34c08d1ac8e0fdd21dff38bcd1860b1fdf492e8d8afe6625b4c3db03da2dc51fdb08b3778857ad5894c4c8c8aaea532c7d8f9b3f4599b7a072451c97271d5a2b085aab6e9e635dbb32eb8ff85188455489c43cd69600d49187e400625d92bed5eb54eac591994f24533bf4977837723b355d7d4c81626900cbc43148ade9149dee9a32e0987f1a585d31b765d7e06e8400bf01bfaa0461b55f885d9d5c359b387686fd74e5c98f325e3c682f64f1733b873c1b9470c66b24bfcde130cc0970d66eaf418bd7ebe7309de556160fa935615843d26e7187ae3625462e65f6ff3b62c96407e89e3d15d87da250711d30896655244bdca70f6fb30eae2f4788ce4a4cc92c3ea60ac4a9501aea8e57c74b6d5c78ed4a2104b663850f25981ccfbf42b933b88bdf9f13a5ef8565fd9d3e9ac5b349a022e65503f6c507635a76f21b604d052754b5448c3a0c84b647e14d7fb8b559cc6aeff9e6e0930c0656834db92588b58faefc8b95f04963bb820a0d44dad43d34a20f8b1e15e55f347f1ca8a8a55a28e2cb1c246ccd774947f135b851c8bce7ba7d6952b82c30ef1e08cff3c7fbd6304f33301f63f66544c3158a25feede5e809dc75b2386c80f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3699b2ac30d37ba3ac7311898788870e491e2ec8bbf8dd6aa046dcabba9ae940","proof":"9244edeca50cc7d77ea7e83561a69bbf1612fc077734384598dac5b7ec60b277382d3dcb000d14949907f8cde1452fe280ffafdb18af44e606aac2b3620ab84c546134be9f1621866a25e80b16dddac6a0f0ee160e1ff998c20bee77fc55023578e58fcf96fa77f114bf3692f770b376889e2ed3042995473e4492f5db46086d865f78a2df31338f1e4ab0ef23b15e560d544b855003775e33b1ee936ac63f0295d0dbd3e8b8aa317b295daad72aa4009365c6405166f8114a106c6bf80d560b78b8bfc32aa8b600c5edee676aae2568e7626c4f9512f15aadbc1c9975ca9a0da808a98b9cfd0c598977d19df1bf1dd0eae156d12e5f360f473153606ab3bb6886a72bf0d24162c6318c28b0c739c6c2a2326a6380ed2ce2fca15df091551e462c13aca19f9f39d3f50487694e4acf8f9a30a21b5e3760882361e42cb1c78242e4dd0ca82548be7b9698a79c941866c0834470254c2c9b2f219aac7e18355e66b47fdd7f7647778c999f3fd7bc6d0f28d8b172d629bf4933e90f563ab9acbe109ad0638a78ba8faf62e495302cdd615560e384efd99afcc27d18ca17f96f744b9012e832f7dc57fc9bcf591f22dad52a6d7552b8aed09ad43814e9643f5f105f58c62957dc6eab4f254bfdb20d0f5cee41a3cecbe0859f2512facce693d97c1c8645a4e5ee1cd652444cd4a182808cb26362cd5b07456c14bcfdc0dc4f10b679247fe4b81d04fbaba57f76f953d090f84a075af53c5d533f79aff9a4f2f75a78523fcc801ba35b9e4b7de3e60bd3ed62df1cc002a34aaea88c6a0b15d210ba2c5e6741305463357f9fdcd12739823e5e6b7893c6e1b6cb599c2731c72b2364246dab55257a335714d797db20ee70ac121bda5e0ffcb4e2b488992450ae8859066126592351d968ff181bdc9b3c47e9177aa613143a9d8b6f35afe4ce97901e0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c6e1838de78aca836a3b1a166e5ee3baae7d0538cadb3a46ab11d8f47204ea5c","proof":"8ea11d60f0a1f8960cf4b5e18d6ecf99f3fa6901f9eef9ce259d33019fcff808bc4ec2468d0eb64232a453e5b66a030a01b56fbb679b8f02473faacbdcc2eb2c042c77b85f9ddd46c67c5af6b3d1c417d510e20e6d5b3b53e48692111229e176cc863faa0f5e1808f678e2b7728b116e2ce8e1d088db2b958e36264ed0d1230afa1d725a4a8eab6dee3acf8d26938923a1c9d49c433842a377fa4a2f024a2b0a87c972ac76ea318ef340db3a73763a0e2583ba8b02e4b44cc783ae2fcd57ea0b0c5da4cb8be13c6c55f29b2dea5f9e1eea8108143a5a5c718eaf1ef1df47450a345a8dc13091aa3668ce463f7156f074d966f5f537ac3ac8f78f97599815843d8ea34275b1f1980df5f78ea7a9c08556649a704dce0d58bf0d4e1bf325abde7b6c8dd6c8a87a22dea3b1c074a2690e6734aae97d91fc9d6283cba9b140299f6a841c0112b6a84c985a58f31c365e3c3e27b303d930fa0fd7988f73e7e19a18363a813f503de073fba267026f1d864c0da681871e218bb54056eaf4fcb5e6b107363e697a378021bcf4eba7c3377e26c4beca3f17e52220c5fb9a24adbc50037cf6769aa2f0ef26bfee91c2eb95a340875db48688e57f20eb9a5e045cd7bdf20038b3d5ecf98ba29f41f7038b3b11e01d7d4a485d6ca7c3f9599b200c1e392416d68c6b33fe27c12d35b1c86221c4bdc1948313bb4bee646c8669dd359d4aee1d34d987d2c5328d0df25d4f4d8c459a4e35c6f80e9e41236fa9b61a7b98e0582e8472ed363ab4608c99f8bd90dbc28217b968d3c414ba33295e94b8cfeaaa21378cd6e81e19139424d9aada9e08bf2680fd21195b4652582fa2c6bf8eef55175afbe503a26197c57cbdc5fa5cf6465c9e1367bef4439cd32ef2fdbd29b22914019d1abc9f7577c478a9a7bb9f8f4810d490a4ef6c01092e80098f3b7a8b65d90d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"806f2dfe2f4c5f029db39bc67671c1ef6bdca88013f5b33229159f5cc585af28","proof":"32c720b526941fb5720d73aefd32bc8a62ebca837ae0515a1d3aae8796258a4b1a4b5f7afd1324fac7139d262a81d1256e6aa7bee090965d3bde8bfdd2944e207a5839b40966f0b21de523f77ed67088d657a79952bef1536d6e5ab56bcf5803b26f95ebbabea77f8ced3362ddf3557fd5b36cc499a6c8a8e657a6a35b1f3735afc026b257237ac42125f5597f32a140dcf62b4509ee755ac093dfa652cdc30fa0df280f0fbc649620eaf5241fb100441937356d81ab8e000ecae2c169bacf0fb4438f1c77ad714b4de2b995d80e133d1a03c84273edfde7dfa374687612410754e99ecf79a5fbba55337cab75b6853aedf5fa9dfbba36dd025b92cece60d341948838528c5caa2457e60778d51b5576b80e4051ffc56db8417ff1412383ff60b26c796524de77e0a74eaba26daeea8531e4b76bd1c14dfb0d4e68b2f6703611f8b4370a1c68cc598e82c2044295b733cb0cf970c1a1166093ab1ce8d418915f7ee267af3aeecccde8483db127401e3a7b7ef3c34f35f7c51661b9c2c321f04c9eca842c3ac2e01260dc167f7e83ab659e81974ce31c50506ec0e24f435a554e9aeb6b7001376070af7c3faa021c87b7ae63401c0d23a88a6bfee49c30f8e80e9089a7925224244d5da98ab8f31abad5f8125063d318a6bd3766abae9bb3a053e843d805d88f9c5306452ce57056f27d73e169978b68fa0705b8c7c8ff9da75f80cff8369bfd643394f933c8b2c93ef25263e24ed14cd46bf0a5cecde7737f2546258ffbfb168e92303fcdbc152995bae9ae680d341a0372a0fa10f6e43f1b5f0eaf0e7351a45de75d901de1dcc83163c09cbf42909668e9a658344eb45c1e4e287418577aaa1698b95183306ac8edef9674995daa199f57f2f1826491c5c10ed8b47707c624001b06e586c42054f801dee2010ae62f4c574f433a6f21b82100"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"188338434be89472e057428278c06e3a3ff5d28ddd0a52ef9e9c8b1f8b2ac01e","proof":"0028954dd191762e8f9c5a07b7bf04574127ae41c3526c359f7c485e4f49361d6e45ef8371bfafe6ece3df537b283aef473c40a25c97329b91570b84a1628b50465a7184061c633f79cd75b4db863ef0cb5b752376d5863e4a086f3708e5020d80fc8f973b6011e0223548cefa5a5bfd7fc74aed1e2aad4f2f3a9535c6410b3fac4bf71e0ea419723719d066ae0e7e39d7b950f8b1e73358abb1d143a0473f053532596c620492704ee751127e381bd7cda7326c34753fab9e577cbd76bfe00f2826e35fedfeaf3264ff5881a411337855c5de76df66c233528c827417df8a087e777bda62b012a112693e9714dc4abca6314822d411945e1cae4cb01411e214ba98f20250ddb644a53f45711c8032ec32cec8fe280c7b0ba58a56e6eed7e65e4283ed88555fecf986f21230e9645d106bcef5f66d9cfbb09e0a47f371229357a6f342f908ef7856280087c9f36ecb6fdabb088b633688520c9c2db920666670b43432bae4d75bd0a11943d0c8760deb834721e174ae4dea11f36c801dd5a5285e38076a2ebaade83a47810018c379de7351a8635d91cb0728d501808b3f1861c2e902c58f269353b284cf3f9b1709e63ba5a2b4e671fbb5a2e28aac1773fc5830e86c9909f5e082aa29db7400b509f91b2dfb2f05f311ab1b01a366bccc700cc465fb4916fc5c2aa317199758b159596299d11bc9c0943baf67e91ff579237fea3ea4c4df47f32ac8ff19978317584944f6a05907dad5df807e6668507b6d03448bb982a34edcf7a8c13cc2ae82ae4fbe2d05d4105583550b85f80db6afa31e7c322fb4bf2ad15343792cfab786dedd9e0db7063297d131a9015881336f9944e4836acf72b02796f05f26b4f6809510115479ca27a8dd9d4c5c0e7386fd6e0ce71998999df393690c7ccc58e7ce4b262c1af9f027c1363457c50135c10e790d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9cca7a6066d944365391f627e6c2c158a497812ecfc8ecf85d8f0bb1bfe97415","proof":"5641b7ca82623e5281209409c1d8f7a6f880b5f497bc43c01909a977af7c49619e0af566e81134aaa05558158ccc5995cb05b35e7d329c68f0c5e5d87ccee42a5ac2f7515281a53ca8b6ab1903178fe83c2e7ed281897ab8986931dc0212df32daffde9e43973d034884c5c9446edfc33447af6a3311681ca366fb11f42dda641c93b1b750d98f17698218036ff6c7583d91029372c97ca07f82c907491008015ff01e339bf977aa85b627942ef9822f17a14f7cfc3ed7fd677d9460a37f8f00cfaf105f3b5e8ec55a934e714a3cbeb67c4244977c75bf6b0c9e1884b628a20d1237c969f709c9981b6266a7da55bb8aec9b6908a711cead98eeb9d95ff16b0accb9652fd0ac4022981066bc0822512e3abf1ffd66ac6f2541c174417a9ddb3f92cb3658a00a117bd68d1da3a31f964f0c186c6748e50132f3f907324741160e0a6dda86540531f6c30655ac336b841d31a94b4c0070cf5263c613f25ef8c51b4efa0924dc2b7275cd8ef42b7b4c854f3117e8127b1aa2b2ef4cd18fa72ee83e686aa48684dfec403feaace23e54d144e1927145509fb5374909aee184caa02bea21ef21f1c7933060ca4d7ff6e80a7c48006ad2f90c1f3f9d6f81c3976cf9714ceb82a8457fa493bc7c7d199ccfed1ecb8cc8870064d8ffb1b046fe70e9526f74fc70c2b1d147eead9613584321330283da2ff0034844a11c7fbe7cdf8277306a3e94eb8e975175adaa41f0c700e5b7487e58feeb4b45d1267a53c4f6f8a64146a6a5e6f08c9f68d81dd1d4d4a481f25cee45697e806bdd126828c7c62a736d982b65aa25d9cee1d4dc766098d2f67e5646cc199de940125e24cd879668a60c3d7970196a1433146d06de5ce31a8b7cab6a888588d13bf5a29ef4f7ca591b065963b0465045986ad624bb8d983f69991b10e9417680783552024270f4fde70e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"08176e7635c74b4ec0b226cc37109ad6ed5b1c2f20a246e9f57b6f6b619ec52b","proof":"fcfb101a71107e85b0d515c19bdf0beb8ac05edd396f533fc5940231e1fd5773d677fc59fb5a8daaa8ff556ade31f3c8098d50a39f4769dcda413d2c3a193e06f6046fe5289e35c08f0e1c8f0ce52e0313bfc078c59cc5b3577ed05d120dfb6a8445e28ea11886e3f71fe57d864f680b7e2520456e5c03d09d759cfc5f4ae07e04c9f5009894be82582493cd565f67ea8457e945ef02c597ca51829e3bc63003d0431b71dfc42199f60601e14de1de83141fb6573716cba8f7c2904587558a075417ffd7f730f1ef4bd2d89d58d5c3af76b532f50e20b3a6d9c3433434feee0edc61e2e5b5452fd7ddca1880da9bf9c7d1438335cccb78a1f9cc48e199e747207861a294a59c688529b6fc325c4b632ceaae13ce63665ff476b9b63c3362b56042c01101e5934897f6a87fef6f2d96516f1fc197ce700dc8cc2d2fb169a9141f224388bde7c0be1e69a6f1259ed9dac1516c999df8144aa254effb2056b37b335a13ce9042ffc38da02ffdd102596b0ca7fcc4d13be9082d403c027e1a2cc2093653c5f42e95017c23272e525744887b1688afefd43ca1d0de00cb1353cc967b404cf3fd5c2247c981f2417e21a32df32084de7faf6e58b8c674136a7f0fdc7410f901759423068a2a81d5aec6505142ac89b40f8aeb57ea06dfc96c4b23657014a26c3f1027d2e00b5ba0726669854f93360f67b981716dc99df616b3bd94614cacc86b07bab6acc3c2e2869da6ea600082372f1edd2c05cae1c13decdcd17d20fc9f4d50382242e9f9d75e1e1dc519f89a64f823a5a214484ba430e3d6f71074657f94e5a3578307204de893555b5d73030fa9457bf504bf89804a15cd1775802b10820aca8fee60d1ff28a75f14447ac482108f40d390f443ff93b5ca1b0d50f92a8589d11ed84b9c57e34346baabc9bee006295a81f8f8bfa49b7648ec02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"68163ee00358dbb12746e6549c07a87e8665696e6191c9aa379f2a4f8cdc6d73","proof":"040dda7b2c2a275dc9fa0c79985e97bb1352924720a49030132e11cadc91b823dc65b21b7ab6d45fa4bd47027554d76d1df01057ee69222a2bbfa54a233e7b1170fb95bac6b74d1644496c6100eea2b60b369193fc525e20e624106c1d3f05189aadb7773da52035471d41f5148034e87b6631a8bebda84f1b343bb517ba0317002ec34c904f14212a6f42296c59edec1eb569a54b0609dfeb649c5cc4166b086b0144097ca858262246f8e7a70e55da6273052ded9eb757d3780e65eb1a0c07690c65b7ba967800ee0baad7cea60838ab9be81e6e00bc09e0c6742f1a468708d04607c3a665c800e144d24f3174a451bc619c4ab5543cbe070cea727690657ec8cc742553b293ff9c06962ad66c4df0bed595b96c71f0d7cef6c43e8d088277aefacd01c1b75225052079113bfb0034b883fc0d21543c2e42da58726895d951e2d19ad51d383e0c78e3bc9d5a58f61f608b75ae074868a15e5488b3e3fbe738ccb05aa057ba02dda4980ea9b22c3d023f13fb90ea39c21f9b203fed8a0dfa0478ccb10b1f6a293a55ccde241a6b61400cc1674a195f73044735d51fdf6d430f9c9c2117c8853c8fa5685cf2efe62074c74767fc52b7d7db49941563b847fa4cc81f6f5f33064204dd5604c09a3cc1785cef35195a85b82f508ffa722450c71c3455405a77f807c31cacdfd794fafdcab50a9e04a3de0c77a03f1f1d10949d71522b3b49750f8f1c41c11eb04eed304fb3273f912b60e92b282d44d8483c787fa0a8f9608be184a9d8b4a87975c0d110d04fd5b490ff8765e849369c04a09d0ec0c070715e10a52c87277cfa617fd827ffcac8051328138c863ebbb913997c5a0963bd06a865e9ddead0347eb72ac9aa63ff2d826cca273d0d028086b5f95709d76b0ce2c4ff3837bb1b74b66a51ff6d999e5ffc54579575f5aaefeb9e346705"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ecdb806d0b49952437b4678c7ceb544242880861fe3aae3e92b86f1b94d1bd08","proof":"2876b4cf6f93180e156afab8bd049753c6f62198047eb0adc9c5ac814700b2013a48142aac585d1113b280f3944474db906f887d01b2144297aba0a90b36d01da4a46f6cc38e1b65d36bc0adc5823edc24b23a275fb169a3e5df3d2a1ebd311720d9bdcb20b5b638c9dfee824d754493b067c4b5fad2fa3f5005755965e1d44c9bc3f2c8be3b2a71c1f4be9415d06be1eb20aadd6d788a472e0c651eebf1210e7fbae919654cb3be692d2cfd28d240c66c953b3951740f07d1f1405a79d2390ad13d890163fb85a92f708f42f938af292b1123f2fc961fc059cabf52dfcb970164055981f388b9dd4efae8945efc2d87e9b6431f40cafde05e504c2c39a4a47af49f926916389aa73e4f95854e1a3ab9da41fe5be6c5f0ca154c4c1717ef14006098ddd149c9b7efef779762aabe43ee060d5c2e92b37fb8ffab6703ecc756227efbc98930034ffa9d7484d5b74acf18bdb2d15e65dc3496c1af1d649a598207eaed99c5ceab8c81de14eb6898e3b1b463ec5a631f273c31a7578650ad239d6fe49cb39623f843e048881d2cf5c2f3c8b67b85f08893b6e4c86b2d4b8c95ca0c64da67fc5db69501b93ba9c2bce986de126f812df785747bfe447fd85ef3eb5a2a79e7839cb384726c60af6f63148292e45621c0305535817a9bbae6e7f97533cab8b3d6f7c70bf7a51d611a82f6722228c7f0218516733c00d79e92e9f665623846cc906d7a026f11b0363ce99508b467893799ac15bbe46fa6d340da3c0b5bc4d82b9d8bf77763cde1655ffd7b822bf58e71d8d28e11ac61f5fc1ff9631f7a942a151c52aad5c20a0286d617a1b6070c174658e0c05de728b5e7bd6b8383433ea9e6b8aac3e3f908c36f88ba7af1aa46db02d24ad3c7ec32022604b2022e0f4a1937e0589e328b1ac2119d806251521d0265a4ed4b00299ebe594be072360a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"423c9e7b16b3a1f2683cc4247b28def575f54f7bdb43414cf333db5a67960b23","proof":"909efba3b1576211bb1cfc136516a835f49c48e4a28582d81555f2169f8af94102d99ff0b75e751e9fc228a8a1913518e86dcd785516d05f125194dccd0c0262c68abc941ce12f21f1b3676efe6ea85285fbe8a64e56e9f30e8c3172a2afc61c3241d40e9cbac431c5d17bc5a82f38c3b4c09e8444ba95a22ab372ddf428340bb6b71e69a3ad3acb1e75d51aca6c45d7d0c3199d4ebd6eaa26cfcc8854343d0a932f710ca2668048cdabf051f5011cc8e25c0ffd3aa8b4ed88660e2c76e5210f2d56575ec4d1a1456a4173246a75ac21bda72b89d455c82aed41fb36a4513d0b1efc6af600d2797ad01d0441f2a80682d9c670d5a141abd72e0241cdd85acd5d2263c7e1cf31ae85f7039d3cfae145dfad20877561fdcbec03d220c87d4c691e16302ada56823b298d838f871655da0654f6b4fdd0ce52875d12253cb65597461a948879dc576772cde05843d50918c627274dc9c898d3ff5f5761d3063dda400a1e189f914e51b4f5ade8ec55663eb022ff35768ca6bfbe9a2b1c6960ecd400c0f90d2496bb39684b894fa637af1e80fd149eacf23cb0ffd3643b2fccd91e68381ab3ecb9f93a3de305363fe00c045598cc9e1454f1afd92f248e98141228265a3116ed8e5786049e77fb4202605577ef4598b2deb3f066dbb9943e9d5d10636424c78443b6f34a992689003746c5e5a30b08312b093778273c25b69010345ee0a689bdab00709451722a5fe299cf09762605203cbf2ac0d6e7ee4b398fa735bc3ccc1cc3a1e6ac258da3de590819b6467437a9a6814f0d3cab2d643a82e17dd2bde31f14e6b58752bed678f13bd9f22987b6407179930b5ce0e76d69984a47636b99b0db874c4d4b5ea1c744240466a00594f2853041c5ec3e6a98cc3954094aca6891e28db773b283977d40ca8cf28b9edfed3f07a58b98f3ac4f8cbf0a02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"02ec10467163805b8760c9881d58836fabfb5b503bb6afa146eac736c05a7c73","proof":"8417c4f315615aba690770a95ee6e67911adebca86dfe41c640d2932044e23242e840572523d6bfe3275af5b82e4865df5de2ec7e633e83dea7b44fc623eb36718e0497daf6025fd72599171dcbe2e6278942892743d92f0d4f082eb66ecd3658ef47672eaab7a22446761df8f899a5aa848dfa750627e13be4f114a9c853140de5aa95cbc64222ea48e6c4fb7068b90f5a32c077891ea7d852bbff866ca5f05dc132622ab30baee01a4dd7c89a0b6c0097145ba5d4f1f27c7644504af52ae0978b2a92242e2554629b1c2d3935b74eeb5724edb0cffeef70a2cb4be2e01a506641cf4ea66f83e01e527368d97113ab90eba7e71098c27e2c66d7b67bbcf7d6ee042dff5ff65ff31b2adceeb4bca6343b4dcd6e35d3b8c9d97b67a5fa16ae20c64ff148bb29dfd18dfbc16912e765e0297b9f7e5dbad3c49deedf403a8fe7327201ec3976248bddc34d6691046f3f0d0c48f4a11c3a55c25c727ea183a864e6b6c9e1e836d175efdc829ba1d2796aaf6f23837932f9a0c000d6eb6794e113f3f8ea635b6f6563e5e2e0ac635fc7e1f99dc43c8293eaf5c4ba16915430d6f867546058c1b25047bedab8b1a268c955909b0cbc45479dd26bf521f18389e8b6605c26028afe652d1929ee14d2901a4dbb9996df5055c047056e21a49e6d843d512a4517862dfe5bcd0ea702ef888915026b37c7b49a3903bda3ea5f7ba18d56670d426adcef31ff45da2cd26414337f6a8c77b510f589d640c1038d3f82542fc165c4a920e00092870aef2632e728c32fc18b8145464a59b678981dbd9087fa7601e09fbe3f40b313bd69d8497059cb370c7423ff713afc01f196679307638390648d1585f154b42823ab1453e7cc1692cb97676c85ed05db62eddbe00ae20bb0bb0882ac756ab7415d1396dbc3923b719fd6c23f2525a561640aeef9491ce800b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dcb89b1d863b2181dad18e73b10c08be61e22c0661eac7c6cad08bc358b93c10","proof":"8490102d39da502306061ceaa936682539e9e8b90a096b1a40aff396d2aecd02b826f4a7864ec46034c9809148572e9d712052713ce8a016681e42e31b152d4e5cdb31a389fd6a25df003338883bb379df5ad7823415243bc16517d39068043f2ca4ba1e5a2cc2106d0ea94b8c076bd90cc0926f84ce6309b71b183af6094c2acc9d0ef9b6c65da2ddf5952a16e15646dbb4833f035b718b81e0a6988f71cf01df80691fda0621004fa896d13c6a4a673a6f252cd98def0c3f037b16e977bf08e5ffa408457cc312409ee2a8e1fff4d31c6599692a80aaf7deb3d3ade631b1067e7ad9a2afd63efb207d29cf148bda68e35f3f552847f76af3d09bd3c91c6d7b3404076b5562d359266e5b1119c605fe2302231117609c116d2370a047b4070198b9bf0e37903889ebb2a636034dd6a0014b914e76f65dc5619bffc8bd3c583a34741f8e85a1a0f1977aa82f60ca10f30512d168235f0aeb0eba24a52720965e76c99afbea5b595b37193820f8d58c2663861d85fa0338b4baf905c3130d9d3e46799a2063b650c38058df98c7dc516a3edb12f9b696a138273df710ba756b47daffb011cfe893c57b2c07172a07d4d81cf3570abb7fcb7c5ee931126752e01f2ad11a6d522a9a4cf0ff355da11b6e422f4552aaed1ce8522cf7d1d0fe222931a4d15d602c3679854abc2ade2eef07f17c0c41b72a941271f33ab4fcb20e00310ae21a5ec3e12877d3526756d07a53759e011fbb0021281b934626600f84e43e9af038d1510fe64a2673f339b7f974bb544e2fe17bd59dbd126ac291e983e1638ae2fa4ddd712c47877370671bb53f6ab6ff929a8cf85dc2c7bef25e5159936da30a05bdd717a07cc12e0faac49817d5cfa0f516603e547cc567dba84745dd074f2fb65d35a963c5530aed65541f0bbc709564030ec20d0532fbbf43530cd30e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"32317a527ebb0f56a5d473eb5d738e5449d8df82281d899b995ab603d27c6920","proof":"464b6372186bb4d827f916739f1fed583e00876836bdc1295fb0a0ac449df86cc690a9c36d9f1c55af0f8348fd44f46c3e99f395cbec05bd13d496f27a489c45a23a0b9d28381956b579c337ca6beeb8b4d077aef744bac253c4e85617db745bbcf54788e0d5cfa12fef64813934137bfc948c9de9d830769b461b2b6ec8d1091b163b20325b987fb58c192a4c1130bab9dd5408dd9aa6f0fee8d5b0631bae0504540ecc27a1afef7d9745f468be9438cdbffed2ec2e854cfb21f0d363a0890aa38c42b75e76fb0510db1bc24368d396974abd5ef86aed33b39a36967e56fe028e96204456266385420e5315381cac8ce477dbe7943cfd293abb449bcf10ed21565417956224190eeb64a105239cb808b199e9d4f81421e5985bf65b96419615fe43eec5352e481c9f933659ff4d42b3b5d828ae1a5c23eeb1a5b2a3c9f47c6ac6e80fa903786eb67cfde59f7cc1fa6fbf0cd33ed685fe1e0e265cbb16c60a706050278149ff5b0503ab5a10a584555cf2ffc1ef45e94a603d4c4ec44eee5f181cf99ec62ba4971b0fadf8c82e22af45c0edb5bf3ccfe50f2b5ab12495601f28d0f1133165af6c32c1ae407c0c845a58fc81f1f06db76fa3a9ddc1c4ebb577331c1f0fb8f276951ae6d7e23ed382a52bd4f3eed9f8b8f040ad06f25cc2c9d52f42051e03e98c4fadc355e3b72f0cf3ab8aa1d11f076f8076396a7ff38dc8bb54069e95e990cef1d011f660263655ad1a9fb75aa1b1da47244c93c6463653d60af06665a992bf68129298eeebb49a09bbd160e3b37ea44fcdd329b13f84f9510c1a6bcb4c2fe5bee2d71f325f1020ade52ab3e0ffac04f4cf566df6b5a46d6c6fcbd40b0e96a0c7e5c9a485d3c70b4e7c7c0b492b48db614b9aa24740e90d4c063d90b8f5d654396e4c0288d901e3d56696eb27b2e7d3999bdd3b4e9e4fc6440b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"088580a2f68dcc333aff92ca68c1408f56efcca0b8f22615d92dbbc2b9858e6f","proof":"5cbc7dcc30cba7d3172f9f3c71ec6eaffaea17297849180eb23e3381cdf6be0678119c7b327d2e9348e4a491248c0fa3038cd3b7602617b63da61f407dd0b220d261d8785101ed5b87a672d0ddf958eb4307646f2dc116582ad86f041a0a4d3b1c18071f9a50089d0918c68f8bbb726c3a4ed183ef7effa6d036365c47e8244ef4beb33ca119c3299d2896ba3dc9fb51c8e91308014e4a61de8b743732383b028715f168e70d018526f347e322be7f0a0ace090af382292d6766eee44fe895068bde9b2bb516fe8f8e9659177b18223136c4004918a5740bf4f9c9b8eed65d04860a466357180bb4856d10795cabceb95c129328d1cbd1a034858f7ee8c04c6dca43ada854ba0075eff0199d95d479612c2f0d8d3d4adb13d817fa8d2ddcde44e4e543b9f258fc1c4c87728d0d75f88cf72d6e57944ad4765c1d39792f15a2048a02ba974131c7a7652cb72c9dd3164b319e4a931ce4c6c1281d623386d2210444e9848d8ed6d96a3d543c8094f80ffd3094e06f6ed8c605616e351ddc9985099227b7539a3e61197aa4ac83be1d1da452200936ff67e03e28ede435027f9b6f0eb6c33e3e8113be117dcffefc0bb6a94419a984bd938c6c43e4952ee87fea538a82c7abb1b599b59e9def73ccc85d9b876e12b22da3fdc18757dcbf102d4d196a42587ce5233699d304a133263beceda4ff0bbd03ccbac45374f3ec6b58070cceccbc714b314a37d00444d548fb7b2ae50cdc20a47ffe29201e1046b615ae5bd42b0ffb370ad43cd9f74c9b3e972c07d10e31795d6b3502ede5546faedab17a845fb10823e9896b4784e8b3f325bc4e50088d13cdcfa5b868d74a1a915b037f57c4be35c6436ca3feeafc76c6c3a28da4242f04df94545c246b380c72d2180e79db7df18e2dab5bd7ad31b2e1e3b2dc55c98c68bc066fa09ecf4b6f52c2040b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"064c9441d78955fcd3d1f79718bd1e4e4d9b91355cbe276392c14369fc76861c","proof":"d66740b99a0a2ee148fee378163b57c197ee34b57f88d6b4f7fe9a2bf6de956fb4286a38bf33701a54ad7415bf812dc4cfa172cc8191ebef4cb69494ce1c1d33febf8f6e377caffe8893b255193d12721eae75145a0c8c47ec8f1af8c37d1761663d0113033a4e8b1f98d1fc99db2d08402bac9b36a814f8305cd59a2c6b5b42b6f01385a8b285f9c57efd38674542a8aa2752e1bc97a2a47a6a0af61fbcd501f9fa9025af06cb7b26b3c74bbef869952382b068f3cd764c201f6b6b53899b0cdf939df5eab1ebe3e5abe91bf7f8385bc0063ee8372507bd5076c8dedd346f001af26f326370d96f3b4fcba8436b2ed35d5b86e12fb85dc4e9f55da56474f56516cd239ca4223c5986dccf6ca476d039134431a18e516434f239a5a96199812b82f4afa15adaa7dfb2a9610e44561364a0a335b99dac8000c3144356bd76587f8292dbdd1aca09ef2505a8bea4e98b2860fe69e8f4523702e9f22168cf2b3857fce1cd27228eb5998fa3dc48a7b3fbf7f5dd025c30f8b1de3a2dcc580254450eec3f3d61ca26c5bfe13565cd8638328362c5c61177ec4768e3b12fb24745bf6f6477a9d62e34399381ee846de8a04c10dac79690a5a88ea38b283b58c891f65824cae41723222d033ddda025dde3b00a5f78de7349394c42a85c1ad81432194bc6228698192c03729106e4c22c0bc38176dcdabc6711988a14ddf552c2a4864844479961f7c42f7a2ad7ef5d5436d1d96769dd10366b537a4b601a983ddf5251982cb2fe76802fb773b59bfc9fea68091895f05934b1e44d2f7b465247d0793f3c130ae9cdf742edda664ba51739de4826dcef953a52bfd24522225e8322d529e8d0fe3f49108add63e9b2ec9e6e54f3956369e59c3e04dee7d90ef43cc1da0397dece5e0c2faa81799b36113c75f6f3c300278f3748ebd2face0205c0751b07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5820ff3dc2951d13f5a51bc4ab8680279766503dda7b54bb19aecb6db68d6443","proof":"d4394ccd4675ae02cbd1d3889c4fcff0c6e1b66a9c12410b40c9b466668e1d3e62a3116406134a75b33464c0863da4dd674dfd56b2610f9b5fd980c121a9d258167ee3552eef15fcaf27a61b4f28612b4e57e1298c8be348a9a7760229f2da4f9895c217ca09cda852d884256283c8986a1355b561b2ae65af1e06161ecd770e91ed2650581cf179aee64df9fba0def8d67799b244e9470e853d3f75831fab00bf21a1037189cafbeceea041af0b2ad1dc0d61549acd5f90163091de501b490f3e5867a5013315e23606a122c78db475cba3e30a8648d458d2f1cffb04318e065c796e65fdd58ca1ecec00cab29b6ce6baaad58e5d4fe0a05b4005d9309baf4226b815ea0e6de335e6e328dafac27b04c2138fcd088fc38995d30dc3f909ce0e9e7bdb304b65ebed4a8d4ab193ddd65679302aae01da9158835cce83b102866d86038726d03cbfb54d5e686e0ba8a8f66e253ce91c9f6da37a345d469fed396246cd58628654b22d538b4b150e285a69f6756ba6b230fc4c503caaf548a1d02482b419b9a3e91edafa1a5ab825eb6fdc1ff63ccdfb8156a3286863dc7dd363716a200dc21f50b41dda5c389795fe7922126ed4cd9b95126727f4451825cb867c364205df510bf5bc16064d3ad23f3cefc336581294141833d79fa52bc4a17c22621e774ed2587f61f3c7eb77c730b0183567015aeed7e4a8f9c2644daa04ed3e74edc11bc4095a9a4321bb6d0ead769bc1d722577dc6479f1d6fd34a051608201681b5c8486b9ddf2d0499ca1e62dc11c1304ce0dd64f4339b690bd2580ecc5490f26cb791eabe5f5a982bb62cbb7dce963ef4970d5876122e79064cf9d6fa1d4f7bbcc5dd70c2b6183683eb3ff69a9cc21cb69fb064b9c82dd08fc94876950874ea05c33125cde72e998a528d22eb498bd21ff12b7e4045c1483372a2bad905"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"80468323f0181e547d9363efbf46e16425cd15fb573d3ab5b12be3ea5d054374","proof":"42ebfa55ea8a8bf33f0e768ec9cef98232e3d6c0e7a75c49a722fb6e3b9c8b1b8cd17c20719225cd11e1097e1ab5738bea0b131d7fcb54e7cc7e6dfa44a57162586dd6cd4f66ef0b455db0fd6be49349247e5b61128b2e29c62160e070c0d80192195674d78f83ff6649058cc669173b3b06102bb775844afb1ba69cb3eada050f49a0ed73cbec199da010fade7c033c3cfa749cdddd557b7097a05c3123fa0199a774dcb1dbe0df2f5ed665cbde9a5921fe31ebe88edc452c5c4b13b7eb8f05132afaa8c5c61b4b5c94733baca9127e01a1c9d99363a2cc1cb3694c152aff02dc2255dcbde8f3f0fb23c8dab83771115360a9061a948e722856dd0737b1b023e8dce7e35a0e5e0da2f053b041032053dba232cfd049d33b7494c3a4b5bde222b0eb1dcc35ddc7cadaf317e934eaacac55dad67e5e7f7c78668ab4e1c018a2127ca3d7138be90606905ffaa91afa62ff56d41a76065e782414ef567e2b8ba27916a5c219ae980a6875ac5b6a5d6b1c76042e234d54a4298a4b8dda5c2c686b7844cc1a42d6677395126d19fdafe6290c3a412b246f9e10673810fa69ddb02530625cb67e81e3551b46baaf8672201851cf70b55e57ca4fbea859e10bc0cc193150ed58636f61704094fcac69d7bfa79f0611e1280912a49c6a3c5a66ce52945ba2b93106e4d596aa939aa3d94dce16827caa4cef868987b9271e304f78c25064b2075356c9ce2bcf4b3c117eeddb97e522ac36af370697947ddca81893991828a0d7c4ed3e69d13cbd5b91a0f46a283fb74b25d94d5a0177b3e890acb30a846dc6b5695c13fe9b4445170df3b8984e4ad360d3ac19a66b33e687a859910f664d0869b04cc156a5044cbb6660ad6b104d285374966c29895044e39f4e5f08e208dda8b7ae61525f69b89b493e7504fcdbedf9ac74d40431e19a45c7e9db724906"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a09d359f5192b1e677033fb4f99d7d9fcc1ad05ebd808a95bb8147373e15bf1f","proof":"daffddfdf592595b8d2bdd97cf944a5f3e6ebcfcbf835ce8ed2e4bcecd656b3732c33cf3861e39af173147165c3e793e38ac9ca1e22b6612d728c6e0261bb43884b29b309937414e2503bde43f26a73c6d11fcfd42055cbf82e86e8ed5a9d3352084cb60d05e97cb4aca3f9a95c54a00a71824829c21a61029114539c7c07e1517f0e0b732721a76387faf9a5ab075eea52dadea3f08bef8c657157b852fca0cbcf0637dd146ece44a21ddbf95f2206caf1ffad13d8d2ff879168462508a6b0430fa809387f78f8f1f282ae0e983446083d5c4daea08c231e9a56c724a41d90ee673a3cee2409c5d2ac4eb269dc6bb83102596e0e1557a4d276791b6fe5bc864cab5440acab94c9073f31c219642e5dfed1c47e3795a500e2560c05c058db207408446f76ff8347bc940567675d89558f08941b5895d6196d7473a5872676a494824d4a5fa5132986107506f01558234ee438382eeb01511d6710771e3bdfc7ee052db8a7ea5e6c288195d30344f6f16399f0a3a87ced5b86eb78d50842f3c1e264a42905016ccd273b4aca3fb99e71c4ef35a4bb8d3e074693c431de4f7a400ac960d2186cccf0bbc41f09e67870e5f7f774e62d589278386abde47cc3e892226652bf1bf120d465564202eb668b904d8814f0c4dffd9a68895fc519f65bd1266c6e64410dc2865b2f72f78d36fd46592f6ba2abc69f2c490f1d4e12d9eea56cca96feba2b7f7fb0e68c01cf85bd88cc1ad724a82308129228a9400ea67b57dbc2dc6ff8785ea062eacb9693aa2769e9ce72160007bf75e27870402a5424418d60bb5d9e67fbe07f70fbf570e5db8937c60b469b45ed07f2d806deaac21fc24eaa3a8c6f4e320057d5dc85b4e4eee6ec0496d95bc9ee02c315ced4a8f753d0b698c162579ae2e90c5127502a93019f1ead290d61cf7f29f7ddadb550693a407"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d65e843a498a7e13392ab036505741f60be294be99e035e811064eaf3209610a","proof":"8c47c1d632eb656db3c1b2b4296fc15bcb36dd6dccf45ed5bc7c77307a6e2359d0461bf8060e4b2f084c200c04b7cd866de2fd0fb4a0cce7b7b7c3d37f98b753625b8ecf93bd55bdbb0b40e333988c89bc8933a74493b8239805bca9d5a3b42f72edfc6b2a3d4be47e39ff6438c86600d4ee1717e8b8416060c91d98d9a4fe47c0455c95a2fc9bf4584e961a0074fc6aee723a80196d873f34f281775b37e6082a5c5ac6a0fa9a13ec48a17998ac22370fd02fef8d925ba9d02436a4b7fe96086625a2577bab89222bb01138af04ea81eecad2076c0235b2ff77e0551d4c390f2cbf76f26df8a1f87484babb4b24c0eba9cf1566e864e440cab20137e6037c39c432d85d06b8eb86dab3fa07be6f2d8e4ebb13e6fc4340b221484dad6c8b694a380fe931a1f8a685aff48929e886e78a16e757ba54be7a32e591ebf3b78f54780289f1260c4054603f3273fc3344764173a7f10d992588315591079a49a78e7faebfc23e822cac02c0cf450e2d9f972c4622941d642320cb9340885cf3faab4ffaccf96160db3c8595b2a4bacbd9835c378f74047f91d2cf0d1e6c045160797eecc9284943791ec96be80e7482ffd855e0bfd017890844ae5fc9c648d36c1e092aff3ba0431ea4cf4cbbf286b3c44cc68fed3d9a18dce0094386e4c9e8c7f42e74133912dfee7bc5ed9f304ed3626c0f4b0b7326c8357b1e5731e7142c36202b089e522ac1ce0685c01d19877144184d1fb62d638a7936a3e0d528397ba85c1310b53d7f526ae46e15af275b800dfa1b8f655381255670c8844c6021ead8417f8c8ee151c4991a4887007d5f030a6f33dea08c8192b577d6e1808d8bce7e730a1a2ceec926a315c93677687222eb5df68f1dd53e86d78e9379a88381ad4de80b8fb19f06c6b4953cfef4552d2c539a171edf762aaf2405cb2bcaf7598dd16205"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c4eef3a859f9bb4cc21b62326b4f2a31a4f4966e1ac45521750ccc209a248a56","proof":"ac94e041844144ceb64321da513f3dd748759b209597fe909358768fbf898d748c7ff14e227c062384f8b4c1c9229f479fffc0d72d18edd5d66419a80b2689667aa73b7734f57f2b89579de9662ffaf4847c6b73c1833c7f7d614202149a7945486863d8d080bc5cc01f30d870d1522c96f917c3568d1b33196e7b542962c27d078e3c4e099d2356d296efa267f7540fb207c35456eca81e6750881dcad1b00b79843b05b29d70aa35ded2d5b9887c5d1d7711897a40917985a13c6fe80d000879bc68622f751823f61c54a17cd42cec99db14d627e61db45cb37a14dc887404544576a425ae3e69fd43bccd547e36774345dfef0f6aba22e48049c7677c5c7604309d5295c7360711f559f7f94b9af6ef2ed38c9d477d835dfe1213dcc6df2ea6fd085736de71a19665872de4614580f3f8eb7b9cbc94ce25c31819423caa37e6a8cd9f623a09972d134494ff5fbdc33d3ac733cc3a2fa9ff30ce15daa1682b9a3cf47d25765ea8e0852966f3e02ae76ed7ab83eefda8e7e3913c52998abf46da9be5d9196ccbad01ebab3c20eb17e170592082622c56b7a9649c98c9fc504246775c47624b3f1d9597cc605adfb3224ef6f3aed2d4ff1f332ced8fada676298a73e310495b2c7ecfd6bc09625d4b94431eda7424481916879a1a34c6e03315085f5bf33e7e7db324c884a9280bdbc5d7bd010a50b625408a450b938f70d95500680917cffa8111aa67868a3e31dd69883f869d9e944ec0b7a3abfa5c0001046e19998e841b1c8972d84576400e9a6989922050db057654d7307708f5b68717620e0c52bbeda95b44cc5da09c311eef28dddf47b93a8df449ba04bbc579106822660e128af5ddd2f42fd195a8befe111c37cb260c14bc5458968f835db22101876c0d8836e46a9be1858794ddee60498935ccedcc7486ea39894dd84cde4005"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a2e6b443f3d8819f4f9916e90b7ca043520ff3cf99c139b75d11585f898f7e31","proof":"c4983c381f1adfacba823011fd41e2a03f320a38e83c0b5c2d530fa703b2cd19d86d137fb61866e1d4b178333be5bd8a48d89d12c0a47148749832ab84c95674d4818ecfefdcf5342956eb1a57ef45ae541734331db91254b44b8574fd076f45b8a0f663e48d7d36cf5af79ecea4af47fe4c28cbf061d8ccc614a651cfaa092d101f265a6912049134585be25ad4f156c5a8e8b017b24290cceb38850209b5089146104a17797751b9e508e8c5c6d5fd30a2bcdaddfafc1bb5f3a12fdd77c10e2d47eab1236ad3eb91d50130f6bad04222b5e651ccd14cba04729c9cde59950a58570c7235c40840e1146cc8eb09f7b9876226ca8baecd295ab1d74cd172d71370c4eed594c39f2121054aa4731c9dd87fed864b1578bb5a6d9cd68223320d308e36cc6fff5f5c3ddeaec105d398aaf636b15b08a6e446c77582f2a7a973f170cec95785580533559e390041786e0ee2a350354c8c018ecb65d59fd78ff300186e5c5d427b23ee58f63cc0ff39ff3c81e557efa9d51ba43ac409332a9d7117201e5f1f2727640bcd24f43c6e885ecb8e86884811e3e73e371fb96734ad1f291fe47df558c63c6c59d51758895ad3694248e28842e85dd18252b8025be3624803be35f8dc5bea2bc37efae5b7ed3e911f931c8710e2f55e47e7807601df207240d6964f1be1978888199140121c4d0db777dd6f2ae0192ef03ab3dd4e71483c6ff0c3109b50b59883003ac15f039d85dbbd7fcddf619c31604730ea3c3a29581c4c16fdae3015b644dc73eb8999e1254f5eb8f7e94f6595c15f058ebda5bda57016c44d56415f7df1d52dc3857049010515bdfe1288b2afb0bd3bbefd43b26f774d952d279cea445eb0a6d850f24dec9850309c542bba710adae55150a86e8d0b8d5c7768c0aae50d4d03226105d21fea5fad4b9a1100cc751ca1ded9da10530a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d4de236d91b042684c48044c59cf2a2288a55c20abe4f8fe99612751ab432334","proof":"da90fdd00b9611b0685363a0eca36c79d716724c41c15948597585cbf76af40194ba9ab3dc8ea9cf18b0a192f1757b61f3d74152a390a0058c2d5b021eed7e570486af6d2936630d7fe8b373345f8dab753ae24ecc599805211e196861ff4f76e038e8d0ce62cc93a5c980a3f3d9fbc62453d9ec788e0551c03809b9ca6ce5182819a8cdf2d919ffca742ed9915730ebb7f1413a449ff140b6ddaa845ca6ff0f1e40bd800cd959fcb8bfdae54b0f8beac14c07eaefa35fbd853914005f25ad008501f9f0902d8c78dea510b108e128f6f28d5dbaec28221dc1e41b07a2fd170e1c836e16738884f3e17da8ae25a3ab43e1bf423c10b5c2f5d13d009a02378f31ba063aeb6b89a20e1ab628685f905bfe70e662f83b8a56088e9cbfa1f883382fecce62c6d4e524d7ef02fa9fc942ac7a2854af8f5e581125c68aca1cc8ad9b1dced96e54345ec6ce8ba1e912d7bbb200f78ac1eafafe8b60871af7d367b32f0a5ae68bfcac39a7540831e8bcb1801d641790a08bd3d8cea20ad1b15e68f77f3250ff030890bf543f5706430fdb43a64ede74a9e165bcad3d63c9d41312115c0a900c0fc39b1cad19c15ec1382a22882e4defb0b16799be8289ef3ff96c7f6f4ba4db563eb45409d90e65480f49fee13931bf4d999da635f69f5d75a19e72bc17f81333afac1a8fe09d65cb1903b6e0cf83602c96924cf906197132dfb03e717e503e4cf00173071e221595830e3cfd29f17cc047611e579605336c507b4e281c960a2a37d8d0b9b582c98c059b0803d934d38fc96ae4199e91ffd9478b37bd362acad8588713b285b477147b5676bcac4ecf319101c731a4b357558904e79409b866b4f1cfc2d0e661a37aa3e6abaf9721765bccbd89771688a8f2b48125330864253524999a17c9f156e7fb611b7cd981c1a39c643ef27d92b9d62932bdac05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a620a5a3d6ce46874d43269d54ba830f653c3e3168a2715c69f81db57e399009","proof":"9e706e6a8a14fcbc3c3914fa48954121bbaf57e394d87f79f95df1729763a1597cee1350f7fe2e34f1e851262d5608338751119f9864e77d02910c7a0fb7722e9cf4520646124752c256f5f9df64e1a56fff41b8aca5b7c603217c397da8d20162a42a86704a3406854c7da5d2020077794165216907b8debf751edf17cca960901ff41f1811ea93357194680d8b90a156a1bf4e512ff1cb79063bc16f1d6901219d133d4fbc52494f582ef171bc5e4f431cec094580d27c13bd0be15e21a80d4a5dc0a57d5f780af3165d6f541a8ad2f580276091e0c6eb022ae6eb9ce8bb016a5f64a82725e1eac48ad1df137eab7129307a4abf2a1438ea6d22123473b30b00dba0846469691b1f54733421ddd3fc7b81baafa1d876efc07f069f1e10a10da895258cc40ab12ecc37f1fe3923ea9bf13288b8513086f4fbc065cb43c20265583c4725e04028acb9987df2feab337feead34fd89efa96ba0bbe359c7d36872b6c99416a8995386eb51af086864b8c7710ce2c841b65a297f5fd2689ca26a3f3ae7f879e3a1af9fabf2d3ebddaaed08bc00f7d5457674c8072fd72b837e12406a280111ed0a60a0989f5763949656e4eb83b4469672d1106859a7c7df4e6c4a326c0327a2d39f66d70df5e59eba9bf0775e44b1d451b8f183d4936c78296c29f0cf669224c63a86ba24793ba221312c12ba2bc47ef0c44f90f6ead423f3d050d0aa8ee3b03948e345e9ca7ea8f5997c337ef97419ba6cec605f3f07f7fddb5b0e7fe92117c8ccc5177b6af36544baf730d2141ff890fdda8846a15892937714803c129d8387afd62059f480473bc2fb1f4a2c3af54e3bd549f34d11ad80e843c9dce26e4ddf772d33f659cfab41a2d43468ec9f8b890eb18aca9c38f5feda046aa5e2638a5e1a3ffc5e3d2deb32d687c3a57e9fbbee9176756543fd0ea76101"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"22548cf56a2db6137c0c6b56b5b53e6a16cc667704a6bc17c9cdd911cbc7b906","proof":"48fe2db96e0bd08d341e2eef118a179ee0ac3fef5d99df24025c558596bdde334a69bca157e0a0e5fd791fe20847145052500d239ec4e395b7e67a12ee9b1a74740a468f5971f15b90498a067e608876bfe115f92c3e0bf2fb8bff9f5b6b9508da4e15346ccd6162b3e246cef66ef1b79b36e00c46c53f01ca086f2756eead149e0c6c6fd8679f9910f11002a30592150d68c2c5052b52a8f0633015ac466b05637ee12870625b36cdd9c695285d40980907bbc02c2eb5bc75a08e735b9e99068e02e74b4813830741209a3eb51758f8711e075533de43397695725974e5f8035e1ae6cc740b3266e8b65c3c48a9340bd5227d53b2f4b06c029cd007ab1d95450a20a9fd92d5fcd4106ff28f67bac09556dc7a16ae6d21c0903ab7a3b9a86672945a7d4e5901644cae2c1ecbbd100ab1c66541d588902d9681d78d22725ebb0032009aadf5e6d1483b7b5dd401ec435e058aea032e230dbc1e80edf97361e60782618fcb21879657a29f5e058c4b462126badcbed62427d88ad8a1f3193299631c928c3cb1624bea25d55d11e02ebdb5b89a7d9b2c2a8d245c34b1c8c95fdc2c3c8e1eff5007757c71b91a36382ce3dc81be9ee139db2ba652428f693fa13879a6183ae11017f3223d707f2fd5e99935a4e5c38259ce704980e88b150ed7ca3fc071e352d8568721037d30fd1fb66468298076a7995242d23de6a438a825cd33f4dbed66a6cc04727a995d75c67baa508aa462dacc572c3c5a1c492df9cc800d760bfe2485e9bccfb4bac4f95359135c8056a24ab88c870f4808d01478b1a32b46f3fe977c1e4b5e72b4284bb27cfa88bd420733413a5c7852104d56a8bdd207eae7fcbbc4b9aa58611cbc8bdac469efc3b961797d6868009a27f6e2adb5770a6768b1adedcddd55dad21c45b722a97e72ae16a2639e0b8d3a671397550e440c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2a60687cd905a2c8f491254f3cf54798cfa31294810bfbd9ad818f002397cf59","proof":"44f2984f4e7539e2f8607462b86c206745f2160b2c32a75b9f966ce1e7fe2d306ef16035eeb85feea0d6972b3f6c84b6e1eb363df237b2334b9ee51830eb976dbc2cff103762746f25ece42499e759c8717093b330c8786fbc1b5a71673464223e7cf9e60b14cf076d0919da51c3be5c94e85a7071726ae6eabcb44097c3a56295fd87028934dd1863dfa629efde4ab5e1a178107b53d0eafd25c2b66fa1e70e33b38489b72ca82d9f43a181592b5d8d1e7660d81deaaaf5d978dc4b6f964a0ea4720e3eb56ba2df2512b2f54b774144c8f8d5cde91939da7c8f6bf510aa5f096074750e22c1dd70e8aebf7696d5f3e37c5cba0b018c721a6c4a71f252765f41ca8fe9724794d445571aa341e335d0c5b3ad7931fa88a79a00780c7adb9db25eb84a9d9a8ea08d7d029c1125525e4c2a1d672595fff48e42c72f7682409b5c62189f6d754f9e63338cb3a6e07c19c7a72581eadfa28987efb31b71b6bd172664a423eb37c8d12be6a02ee19326e21e5564436a4d6bec2d5a70bced7a57798d4720c27baf7215e24e62755a83b31168f4c99e9d545042af06b49a4f0b13000e1bf6491215824cdb2803b258c5630e7f3dbb67e1228f051fc789ba550269103b0de693f59d9607133169cd9ec6c488141b85b152c0112eed84a0af93bfb7356208b6716c2f416186ff936c9415649df891c8a30795da24867d71d4e62904190e4ea40b8631774e8bd604e0f8184fa50af17397a3c86fcfd36943dca17e995135097e3bb694d2535b54f5616db05f3e45e3221c5edecf91dc652d9fd1a5796a867cf489b3ccf7e667feff5db2a613a2987b45ff2d61e615aa380d7dd1c4317204298f82d104136a1698d3cc5c308b59c5e306888337164027d37ff5e31798046608a0befda8d695cf0d2eb22d11c829bf31f0b9e61777be2d1e7a1f067cd574960d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3e369ea51f7e1036e7408a6b9d264edccb0db892a5b23c910007c77db0ade34d","proof":"2a8ca52fee0f1ae5dc911b1994d66aeef988a15b5600879e27ba069a9fc0d108ec1c9f3a62f508c7e7e7e9be8b20f0f81e3dab4a953b99fd8ef8723252983b3fa05f490043901e12d5d94fe74e66acf9c502bee517dc5c945a6de6f917fa8466d835ea14a7175b0e8abd29a167f74d4fc91cb7a7d0d1f537fcbcbe8e60f5b151f94bbaa3af5a1834db4732a9576b1094ac9a56d563c2d6b9bf2b33ff32ae2d0b0b0928a6215d41e445abfc6caa27440175ff937346a124cec61cce00802b4406e24c90c73f8d92e0343433353df265026e65027ce1d98897a40cd8d210bfb60a6cf5a2c71f0679a50418d14473ddc1c2770bd83da75b00b7896e9c298935f400b4806d873bb3a6c91de4026740b095a2dd0a1a765c144f66402687d82d00d949c004bbf4d58e09c1f3e12a871a515ca591ecd5f95de7ec7e2f010e6d1e852721344899830bb68568009db10c1939fc1bfc10c1bdc2a579b1392a4363a408d1142e22a2c16b977a71abf985ba908bf9706db1c1c5536fda6d0d0270ea32a663477c6cd14281e92c392e5c4d706e335dedb7e79088cca05b7ad76acb20c8ab6135e4a56f19fde4819ff28da2ec09265cada2ec0ee7dc25de3c8f6f63d3947e4d19d8dd6346045cc75b0285af01bb48502ad1af91aab0d03d3f364ed3ddfc3017252c1f5f375d678114aa0a777d357b2e6aff39244b14fe08d6066eacf32f24dd7930fce24b891365df14c54692a9b561752371a5531baafb53fb92f1202d54390e68fa90489cbdced0b73a3d2a6bd05da0e8e27dd8fe96101274274a2b45c8ea60f85a6cdc96dd87ca541170406282de5183c9e7b7f58f2539c362abbd8a856246c05a925b3f1ea62d182a4a68aaf17bf045ae440995b7736c4299acd4851b3e04b5c6f1c1156d10e2e951f10541fa393843b23c99c9e95fbfd2373bf1e7cb7c00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d8cfe406ebfcbac8604a2cbd5ae0c82c45b7b67a037112654ea5c4bfbb13b62d","proof":"9863393fb94a415c4230431338ef1bf760bf1b4d68eb5cd21eb2490430959f70a6a1f1f4d85871698a9c9d3fe5db591dc1fdd7ea80cf5190689a8b44fae3510682be84a3244d47e2a358c2791c4017d4bbf9bc3152caab2099497f82a17b8105d24a26e7c707debe5650e9f060cf2dea14a3f50c5f993107560e29d6a6ac15286acf7abb5b96ff994084c40a09ca6bdade16921e7900f65557bc05ff86a2be0f5d87faa54ca76b452686c96c8102aeac8ea1337fdc7ec9de13ebf7a7b0d9c40a6ce6e14f8b4522dd8c0986949d0bb1203e149c0466588a310177c5b07e6c170b6ecc3a85301d74c6bb50982ea127457e6d3784a3b5c271e41d0848af75e729516ea1d90206df2ae13bbc67085486caa63e88fd1f13a4654bde99b03857533e4c1286901d583ac044648082bdf43f8a17579fe4cf65e7c2542ea6dd85e8bc2e7b4e798f5c61eecd53817a1b8222cefc2655f000ca92a070f96a51c7e632463a31fc3679353001f320f09ecf85eee906e90b2dbcf96603bf2201371d0a5e29df0b787e7da817cab059b364ae05f2b741069209f3b269b852f4be688cc227c62168904f6d2add8965e841286cb05b0fb60c7d8d9364158da12b0450911af4209803b2eaa7d6f3f0556f26e36658e6681d4eb3657ed2b39d207e2e1c8015c2c94d371a4c382b541bf646d37aa8b71d2a38d9521538d940d2ea9f417f0db3eb9c4e4466600efa9a220526a59d5a77c44faba331e8f448c912f61316fc0cda4dd4b4390e2eaa0c713195473df40fa9c217b04f606904681592073aa981e036f790d80ad2c3a226f59ced2868ce4779f02436796fca17ed6b37f1e94b41cbe28b03a62fd5a0bc92a9385d057e00563475e80531e068519d3e2902e46f837f66c8647a048497b2a996825b71b90858ce57dd59d197a1469bf1023c13b6a55f0744efb600"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"62da1d452e92910f7d638dc7279b477926c547842007b64e609d84e068523f34","proof":"fe553410fe6287914f7098ddd17fc2e120afda0f1159918f4489b075fedaab4a267ccc2933e3e078019509d68f83bef89e571bdc4d406360d60c857ede6e3d3ab65fdc754f109e46612f226f1e86a52b31a5282b90c032bee25accb0070ed70794a3de9d41fa50118a11e24df4e1bccf827f4776b1bd53c674a1b1022241f77b945e50265e1969a6f8ea7a59e5832d06fd5c858038d35c569859c799982f2e01ad09281c1842fe354965d448ccfe203fcdf181c4dcdd50074d1bca15a4b8040eb0624f767cc17695b476ea6fa8ceed8a2bde6f6acbe9130d0c0f37f3849f1e0f7631021357f092cd690b51287e7d46c322ea2dc8bc7bf38f24ddc2e580e28831b65a91966000806ef2ac5fe03c41f0eb91876b3ef23544229c22920d3a553f0754df7278eeaf7047edbd436b16f7d5985b84ac1b6dbf485b7c89b0b6b44e5c12601e4e27eca492d878da7f9e9491d38223744a6820c7d7e0c3d5de97947e055426699e8a13aa5d49c2f07d37603cd8e6fec8c0636cd8708a814074128878c50cd8ba2c8863f51c8ab9df9ca7a6954a78094ad72529e44977e3df7fbcc558dd09d801841ee61ddee96cc182f3df4203ef05aa5e95d7ddaae9237850c20814072a7ef8f730a126272b5ffea425a3f73df786659f10a416752911e3ae8a32abeb5c4a0ae5872fc77c3cccdd2a38455c6f6dc8e3f6096eb4966ede5bbe3c8f1cd72aec5b64780c70fe28b570cf3de41dbc5e58f131fe11c37d14500aecae80d36c253e22acba0ab1b87c68c5206815513df26e9c309dab46ac2b2b5d31a9ce29e20a786ec20d31265d816c5b0e372e4e69aca34e92c2fdea4989758d6189b3e3a87df68abcc304091771e0ae6870cc4fc6170f68a08902d425f5a002262d3540950410ec6058975e8adea001d36e172cca99b4738350c8a6e35c186c595e5cd5ab02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7631d2e625d9b7ac7384c506ab2cf2fbde298794860385faa2d9d53b94c6556a","proof":"825ecd151b864f00ad7412c16663a8a1a42a46e46a14a5158d8e6abe03c3e94816fb9b09d115c2bf7ec7b2ee206e993bb9a85fb908bb973360bf992edbaf6924da298716dbd6ac612f5a06521da8f176d87e912479a60300469e0933296cf87ef2e3879ff65115780b3845fdbd47d76edd322799cf6bd862632e2a4b53244d054c66c5c5742faa74d58248a6565ee7c8ec1359418b42f5e11d2b15feadd18307cb667aac1148b3fff7f63219d4f27ea77d27810efa74ddce862e24412c5c77081675789307191080f5f8124f5155a3ec1a63123da8443879d7fa7f4e3eebda08c8b0be8e78b2afdc217dcfc0adc7a7929a9e15448fc89085b01af977a9d64c7f304f0fc8938623eabf40f4726d75b0687325ad29b08777dc1c5aeed085a2c53fdcce4f8dfb2ac487e7baf863a944f3011c9f93f50b9e6151549ec06b0abb04625acd39212fdc1d91f4a1be6ff065340369b17c4633fd87cc0a2fc9311c5ab0164c2acea371f3418527e1934e101ac14f3281a2c85ba5c3c6a1a8a285884b6461c0ac9c99585ddce91d6d304b8877686e6a73e9e5640e247c64a63a84fcfa5c7d44755bb80d1917d0326c6699060588ccab23571ecb3bb6728e4ea042b4dd4214168ed215599db8f61bf543dcd7a2537ead5017eb4a9006299d2580f0411c9669bad516bf5d2cf38251744ec693cd676a90ddb2eb453f282c2cde1b044e407b6072910a479efaf734141718621bdad8d38de345dd049d9aaedeff8795b308db5a70fd9e0c067666e60fb793e1571b6ed1dec98221631e4a46564adbb754f8c979e40a7b4b578a5c88fad3a4c5857277bd0df81c4d9d78c4087c5cfc7a590a3568d3c7753838d18dd9b67440226812b33e26c8c86605b86fda04e0d04fd01e5c0dedacd5edbca07e0373e6bf8ffe4afb6b7fca3dcc3a1ac39c5516c5a710f00a0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"96dbc1f389edc3cd3df26b4465dd11e795bf582c9d1af7def06de703be27fb3d","proof":"e858d25e3eec61ad81485d5e856d243009838951a1a078480e42252df5fe081aa44330fe155a19bce650472019aeb68c6a16eba7156f4d7a5f3d8645b92dd0563854b2c9dbcaa4d40f8d8f5c3dd6b6106d20cb736ca6d82206f33bd580ddd00b7ef0f874116a369cd3ce9c908977e4975637c974c2625d3e9c1e532385e94046b3c79e2974f78c677a00f23747ad1c17e26f9dff8f228992064dddb57d2d0901007cd08571dae44841e9ae0fcd054644dac3da1213b769356484bb714a841f056e3ea2c5695adfc2e2627e875c025239a48b49e8268f1fb846e5c1902ddc1c02ea5974f4feef5a3f8d2bd021033ed3c628b51dd2e66ec9d63afdbf8acdc57975f0aa157e4d0afc2c7f5a3d2d735fcb94c41b4bab87b5bd800a02ca80aa2e6a6756bf0af23b2bb274c794c4bac1afd7b01d3e9106498881c0220bf9630158f5166e6bd17c973c73b9fc1f001cde8a32fa2ab67e06d2acb888c31669799496ab0dd2aef9e97637b1c6976d8bcd34d79e9c79be8c0743cdb72da1740a071c283349ca156777e37c582df90ae712540bf5c92b8f663247ba39e7b2b9e881c32a8543025976327f41b2cb57ca87290f958cca31c91734fd196b75503dd5bff74d2c5676ca3d82975c68598c4b06dab7e06002ee645169e347a01bcc7f8eb972091119f89537a04d7dab29de3e463b8fe0c0cf4edad1300e1a3f7b8e5346e5fe96b066aee9ec7ca47ac1edc10759f5fd84bf2102662d1af4a2d225b12dd1870bd09d7b9c0374c2b1fac251dec919056e952442856b1fbf2b0710ad70dbca0f6888f6695a34621cbf76620a4bf45bef165434e4454678d0cfa889a0efa1d738c8dc5e76d1bbb47f40676af20b0aea5b26fc2f113f347377a854ef7f13c21d15f78c5d0cca80dbb4738c333054068d8f3028806d47e894d138b7142b91d612d3667a2c0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b608f7c6675efe27981c5ad3a07c82480c2b10f7c31ab6181993d6aff1b8fb6a","proof":"9ebe6181265c449414ad60743ae3cbb6ca3cd6d2e7f99e466e41081047994d0aba1666772219564364abcc8ead6235a1178c1527de214e5d8069becdefc3b824f067d9893039604e0c93c4e663ea7328252eb54ce2c9ec22fd63b3e6fcbc6c5812161907395568e899b765e37e003fdcb1b5071d62c753380bc258e48a76f94ea16bc3f570289ffbb9bb2929d97c33821e20df27a709d92742597eafe82d39022f9ab80bd13f0eabc9fdbd6c97d844930813235d119510ffb7b0ae8aad7dac09c7a5f2759c82667da5c87d4bcaf6a43fc237f0a1cdf0b7748ec9ebc413747e09d6e819eb246546a59c15fcd50500f6e5f677b304a8c9b6c77edf4cc5aa2257015473fb3e6f673a069b9a824505480b6b03e74e0f2fd28f61705b94a868b84e671432d91ef4d0c3113772da5e2c19101a04ad66d8a86adcb0272428b1f2198349a00ae3002a8407d1227989b35c9bdd0b6a769f073679634588313872c3b5af2af4b3941efbbaa048fe342fb27d5f34efce540dd14305e56790405d5fde9cbb0fcaeddd49d6ec0c033bb4519a402e1be7a21606f71a6755088ef5f6ff73689c665461432ed8faf181b701d19407c40caecf922d9485793ace76f406e1510cd41a0c5e900fde0c142b84cd949320debd4b32613a4f08923051a402eab24a42d516a820975048d6b081ad69e1b8bf8adbd8d017049135575e53a541d1191a8485678ec7a8f66f7a620aaabef29a3e34889092be65e97ca1098658966ea447216352dcd6ef59f5685884111bb34bef3ff0c152ba78b4893bcda4be514869500fef1976a99c91e6b07d26ef549d5d8119834d69bb7dc598cf91e9d34b72034dcd662f3fac40be8a7fa696346e55cd9187957aadb3b656a7010a610078b107c8db860e3cbe70158ca1d169724a7e3822d9faa9533a6d8ce402d9a64fa599f898ceab0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"32c3dd34af8ca8832aa6aba85abd7c19069f68fb999f338b3f697ef3c5f9d401","proof":"36d244c7ede45dced864e5115e23b404d5a131eef341b5c63a0d01f1ab9231046626dcb3c1aebad6b3adbfd09626c878628baac5c57dfa150d32516b6283075dde128f5e9d77d0040b2916c4873ea0b92c13b2d9874a295602a2bbc69e14633d5c7b4e7120b39773f491296a597c22ca243a27be4e0fe533627855b85181ff35cc37c20d2b8f7a5e2ce7edaf765cff1f90483b0b614eb3120a4d8cbadbd3510484270b620b2f3465b842defa05c5ccb57ac97b4426932215db85a253cd0a760eab21e709852e0acd26ba70747260a0ddd4fc598fabd47726c8f2a2cf0fac0102a431e10e72053e4e764c88e366d46a1869d1a6c44279acdc88b8483a8a917679040a3c80a4c21f2edcf1e3dec9658ca4ab90673df1d521a286af36c4b15a744b462886cf4a526b695a68d13d7de2319c4e2e30d761a6e2c02dfe1803e9a1d91f58bd5d0522009a59bd875dbe54b09bca33e5a46a63f4b6bb92993efb14b616054edbaf1df940f0e019babce1975c0dfef04fc9195b23123b75257dbb7ea8ad1660b03d53ee0fc3cfb3bf4b150f7df2b361626015681cee1dd5a710c5543ef605245b62332d0f7241000c1070e2c82f45188b64d34082f236aee5ff1f37826e281a20d001fb088e7ecb472d4d96e3682e3ee8ad4db4633544d8bbb9fa1ead185636a2b15c77bbd866696d4a9df2d73055570e220b09abc55735887362a33f8f2fc09d45d226d0f5163ab9d5451984e28385d48c42881ed9268673eac7129a8559f4adbae7e3f7012e469d4b6134ddc8c524ec012fd563da88991eafba73ff102c0c6b3ed52d4632e383c6b2ac91f480a65f755f39f6e9166f430b247ba3ef8b2bf2aeade3ee4e076e91b54daaee5285de83ce69d6b3a11cedb1e17b95f7480b0234f485ff50527818071662805d8e1798eb71431a8245d08c98788b61efb1a408"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ec9c5d16bf81c10af056763b8e410cda6035eef801533673d03df457b8f32c6c","proof":"aa80717c68d0d7defce131b8a001882564e552b54bc7c46193580a9d684823566c05d65a250e5ff800715af06b10c80447811e8a935343b053a844e578bcfc7e80ec311f3b5cf6a757d0cdb82b554aa8de309b5f59820f8b2493c48691684e48f44f4473325462bc16bb4766bb1aa1ab419f56ca3431a9742178fc3514c9390884132a70472e9b0d0cf436649a71f3e4af8b2b931d67309c8e5117319b7cee0e0f64e51fe702114cd05404e1350c37cb813b494b7ec6b62b4771594c9bfd01051b9cfbfe287f8c59fec8d2243d815b3f612bcbbc28eeecd4345850fadf2b5a0044f3fdb506d678be4b9fe997297e62fad67856f68cd4245b781916183a1d9f7fd4161b30bc8c2831dfb06641a0add786f83494d5ac4d86d412cb29b414b40d62de853affb4d4c5de24b8ac502df3ae38e0fb9173a9d66720ca050718fab78b6a501f707f38a24e8a5ca0f600c7c6ec026142f0d50bf8638e3eda3df9135f7a7eee15338fc2009e4180a27e3f9f10ae732105438fedd4152a7e70acfbdffd8e1474090cd7cfecee17f48c467f93ed139be1492ea33c4f6c272d0938a085562d13282637f1b1464e4f033b9a14ffd8476a9d1722f48947ba2193a65533899e1e665697277176c651e34b21a187a613cb2017e6c712bbbc18a1ebd9709b30c85a567a724633ef822a7bd44df4d7d09fae7cf1547bda0525277a839bfa5b21618113be3153fad2cab0866d4a69f7490dd0485664cc629c559dd18e3b65709bed1a3758e43694dbddd61e256cc7f1cf3a105cf5f6d8d26c77e66faad99c37726a752d18302050fbc1d1c32484b3125d6498a3949cf10133b82b0f500e1febdc23b64fa75125696331b4656fbf4156e1e11850ac5c8e0d2d45a281b3fa88359c048409f4e8427f5d846d5b2bc584c4585d07d974aa4b668a982ab1e9d2fe6b5efef209"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cc45a5748409343e91e995cdf620aba0111300fa6c082fb9bcccdccecedc4e22","proof":"c09f47d9765536728d85019acfa175ad4eeeef8108d3852918ca543fd4d49a7338d271d516386728a36310d31da7cf06237f94c307d9b968f29cdd92a558fe4d0829e5d8d9344b6b139c1f376bbbfc1020e75e3320168817e0ce2502dc6a406f3a8c318b3e55b6911ca8a888a256cf21d4a743858fea21588b82dbec28c78f3d8b5e177ea29b39e2fb1166d2653f54c8112c9bd0075425b08e0ba503bd8791074212fc8d9baa100318f38eb9b29d8d0bfe475064428f4e484805b3a70ac2490fd57fb6ef631d43dbad42e94628db4602beb9d8522f85b04c42a4eac8f9833d025065007a596bb99bf6b9e8c83e0e4f5553d71679e89280689f20b0b315281a365893aa1592d995c9216d2c7a7206918cc6526e85c283a36403b767b733a6b278baefca824ff1493223c3359201eba6c377eb6a9ab75387742161ffdbcca50d385aa68637b18bfcaad298d126785b53adfb904ec3446a01204936e092f7e5441a8ac484e0ced28af4ed99ebf552d3d279faf7d23af9e1c3252e1173aedff3ab55b2bca87a8fd65209a5ffb402f90ce5341402f633a74f2f535c0ea123e911d5130a3787dbd42936f9410610d6cf24bc1a6fa2ad3b80e85bf0f81140b3105e010290a7400cef40a1fa023a8a3ff41fb253d99c2d141102fbb19eac63fea3ee3b208e7c8fff8cdc0a72c0ff7ec41965878154ddb1225cec8c8f3c74aba636c3ce0eb08ac5f98ba90892e646fa1126151e8c9f051a4267ec1dc8c2d0d2b6cdf36148f88fc982c5746c3c443b9ad5a1d788bd5b842081d9e4ccdd150acfde7d3fee0634a41a4add72d0e6f4c9e7dc98437c16ab3cbabe0ff0c1aca53e5cb856cb770f5624b50306f2e37c00ba6770d92f4a4721a80a678a9c3a14c2bef81c82d7dc0e3f4041a7935eb040592590a5193589d4a52dec47c7e68918df15dfa63560b106"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0efa29d58ae676070641ab942e72b7f9a7f31a956b24b08e36ed2949ec65e108","proof":"4e49479b29cf34e4379bfaa8f67b872d167adc200fe677469b48d5841a0613699a64aed0f7dafc05f71706c772e7fbd03a4b551394ac48fe53c8340a32716b07825550d7dbb673ab4e0f0b134b662797670f4be37731ff6958c307e3eb7cad414a6c1bf40192c66c016d885bc647d18b5063509eefcd12685dcf98a2dd884e6f64cad5e0c84e72c48561bc513f9c88d49545a0b90227d2bdace15c57915a9303564be1227f9503a4a55510647eff69c6a18a76747b23c7af4f9c0f3b1c69a70de5c0f7813616bbb646c70df12d538eebf8595073d6233dbf89ce64e06920020d5caf7a1a47492773f5f006ce86d6d9326f90ab182f764f6c48ab5f608810d4076e1151ed6d81d327c27a2083e6c18234e1fbe89eaacae8f6758d6013276d6d3ff2d5543dc1c95b8a83767cf83be1574528f60736076af16db76cd950d2042e1e0ee27373462cdb627476b2d6452ef4e86a4fe1c2ee4d94addb63e8bc2f526e692a5992e3338f904694c4aeecb492699f5ee560dc9e6ac4fc5a24a0096b1a492c5cd3087bd0ac08fb2b56ab8ab72c6ce85f8604acd62ee1d274ba146954ab8d65286f671327c66d2d1a97513ebf091cc98ec61164923b1c73056dec44a5169669e27226444fae8fba3331def0ae638f7481fce0ed86608a560b6eada4f43beb22042f4a8302a21ea4a2f8bad748889b4f188c8e82a17fe4e414f81383226f6e193e8e64b407de65ab983a91c0b8b4879eab40c65162942e487647502f9d2b8c53dccc405252188f0a448525f2cca5cf775d55e9cc5b8a4f3a65c166961d88d95c1843715e98f8f1974c0ffcc0e1fd24b6bcd7c966fd8d8970bd6c3ebda6cd154f0464a214a4515b83e4c6288c32641e512daa4cbc1b8ada684de1bcd7869b310885ceb44694f8b240fb48215c6fdfa740e380c49981c77b462d1f91f46935ae02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7a565512c4b70e929c0eeed9cbea214de433e0080a6e08f285d60c256cc40f0d","proof":"008a5e24d1d427ebe623702c6408beb7200c8767fcb31a55de3204e0cc6f49073409486850df62119fbb9ab4ecc02fcac5812baa4c0f4c7dd7273e1a92f3f22faa7f4d2ec3dbfa1182dbc1b256a86e34e0f3366d1c840eda657ed9d163b0801e4cba588ea839f886832294c6cbc46440510adc6e384caa0f43188d1c812e180402de6110df59ffa01b2904b3576978232db658a6f7e248217ad9dc92ec1e3c0ae557f3860ccb4418efbb4a45b5c460c4d1fb1486458d1cd08abcef049aced60fe70221472045448b5a6e8e490679beaabd820c57742629d8dca2fb2d7b3d65025e09ed1fbfbc332b59a05a3b0206cbcf1cc0a31422f85bf56bd11724763df46204555f04bb56ed83cc71f20b2de28a27cfaf619cbbf46b3898a1095920b9f635047d64a6b1ba6c0b326657b1e82aa22982e882dc5137a1e3c478f7aae12c0b7be82c58ce4ccfcbf40aa69771b9e97b399c4212219c49f95a0420628a4ef1ac53560c5352c2241827f0d3369a484ca858a98dc7c34623be1737ffc7660da0b579d0dbd1a802cfaa867ecb5805dd64424441442f5c211a397984e26be7f67630438c22043d22a1db1dc29c6b92e00b514ed7973f3abfffe2bc41be5b7bbdfffa65541b3bfcfd0564112dd9d9f4fe16dd648289e446688251395f90a76c78741b3bbccbc4d5e82ebd7fe9cd5b5ad1e9f5de20477952fed16d1f422bbddcb309c0666e145e95c0364f38aa0c2f2eedbd30401d98d74394f24815020d026eeea75e3882d92be5f9bee458512fac4dc96d133fdcd86de9b08403451ab1dc79a1f950108699447e3775d8c2d53ad43a83fca6c6112981e8b96bfb922d2703d5a7d0ca1f04c08e78b3dc68c52bb796139b67dcdfe31d5827c5cd176d3dee60d724a9ae007f2d550c6663b9134697173aa9538c2c1b6490c9e9557670af05c4a05951f70a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5c835308d5295cdddf8ee0da08f8e05e09bb9b934e361a8a9047c76c214e752b","proof":"dc662531191cf147e0a8ebc1c037139650844eb7f8bac7569dcbe236d9d87534e23a5f85f8eb51b3bf5955e0a401b9f215aea826a7914f07d7e9880e211ac05ef4371ede050120078e847dfc4e274a225bbb6051a567449a1afa80588eb37e3030b5451ef8fe6ae807e6f5e779e1cb5b0f892d1ba5990b70c3909bd315ffde6e9d28fcd7eae08a66cf30ce4b1a2577c48dd3dae52e7ce965cb68aa097623fe0eaca7465f1a45d9faa6dbeac4fd823966652c933202563401339a932992b530079463fbf095c67b5540687eda3c6255841769e34655e9cc8762fbc3039ffd4c00ec3e82e237217999961d3f8b19b0b6e97fb001c5c6d80fa69efb9918e7f4024ab0dd97cf04e9548efe39da391089ea423325c7ecbb39260b415acc0b49c82a49a0198b793bf0ec66765dcaab1fc5c597043263ee072a42fe6eb56fa40366de08b6119306fd2b3c0c43a81aaf9efae886af6dd33c2e941a926841beb0bafd243290d9ba278ebcd065d66fbebbac6437b9a03b1c407914aa1baab795470e3ada6d2aef5d6497e483b5545cab18d5d19a0c7723eb8b5cd8af8b1b4b8d2233c9e508e03f802a09c3f5fd7a5166c551230b25c44c820f3c25b55e5b7842a2c9265b0d78f8a8277671520e957bcdc40b40598e51558971def58c519e160e1a55d147360a8b3e7f19e702ceaa785b1ecce75503fbc5f65dd41ea54b60e373476fae310bb2a77839aab3bb5ad61e9e41845ab562a312c3c513e167493147035ebdd3495f62d74f492f245f76db76918e40621945419aa9b5176b6add8dd5052e6996bb15c20ffb481cc3ed6ddb204e55d1f22a836da1560a91b3756712ccc5dadd33f473123a7b6e1e4bc98686c9fffe9c47a3e4b741733283c302254c38d2b083627e08e6c0ba7a43708199ed40209ef535e4ebe785b11d4d71c29c671f03037a728d0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5e22f0edacdc9e9186d292a208a06693e8b2ae7ad82fc2cbc5a0e349fb5cd07e","proof":"b6ca4b8c38e0fa9666742bfe65aae4ac8ec6d59f291d229f965250170197cc20940d53db935b7b9fe2f51f1ac5064ecbed5760ad1bdd4b7782ff8cb6c1ac3d135ec07431b4648332b5de943f95e201e52257c516923d62203204012bb30b8238069da64a9ae816392666732e41cb7b33ce6e59134426f61d4346f9f24b5eac2442dd420c083244c38319ab6de7935226e06f03970bc04f47b159ca38d1e55607089400c889f3a0d8f3b7f2cf791dd82e4807e25457f57c03ba324591684ab707fec78523b96b2d3df3fe91eb69b29128f4bb7df06a7abd10cc659361a44e570772f7e50c56e6ec9e7aadd2ae8fa509f711c1ea6dcb695e10c534b5b1117d5a424010ba465707039a3f6fc6336b4b811504564fb245a2c9d2c185e0cc12139736d4c11efe82086c2984072e9f7678746f4f006c400b41d4cf66e63e69a34aba55b6bf1bb1b7c0c3c28660dcee3b47f3b08c3496a23f226a579d0be017d18f41790e70d78caf0cad395d5f664e1e4c41c8f881f29e5326587fe651c8273ec27948a0a7818a7e94948ada77d629e8ff29e42cfd6c6d4a1f805c0fc0acfaf46c8c4010f5c302b990f8c756394887fb478be0766c4896b66a33ec8ec8536bb9f50b52e489dda31acd84d4c8ba769fc7471d4d3b3d0e8b39a0b930622157e8c7dcc15a5a9221c66d52705ea0c7b769c009b16d5dce08674002a996d5385f657b76a00912c045d6f5a182452e8b3b572bcdfe547d5e05b30ddc37d2e94e5fd0a6661c64286ff21b72ae57dac84d644451a339305321eafcc9dc9dca51dc6448f5b7e7394eeac218a719d1399af5577a0e574de90d62c1c78000ee2bbeaf43995caee946c4a2507091a82511516d52ec580b77d2a44b7655732b6e27a759b7b8adbda900e197c0cdaebad16311bbad1f9c6aabf6d838098854700ab1e710ff5bf7ba030c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3ce27d902e3e77e0c27acf715733472824f429e29e06e2ac918dad68b49c6448","proof":"588c40129e1568d9f7a89ff3719dea7b4e2ce85c65cfce475070813305d4111672eac3951a23bd38341cc6b39c2a210eb2a0ecb205801a207faee60757d6e8284ab82ab78ebe247e39c43c3136606f9858444a2cea8fc2cbf60c83fd7823027b4a7d53c8f12fecc20fd45827d7313b72e2e70a02da629e47c7b1ff6a5bb6c33f68a1c67765cc2100b20838d723b4be46661520c87b969e7ae1f7db2a04f8590adfcdd29b9a81c02460e4c598a239e606957f0f344b21cce2714ced2a0c747b0531c3f7cf0965dae40348e4f13d4b93245a73f01d040b4b3b309b6c7d3928450e3253c6c6f610ccd7b73e23606a9fa7578357fa4cdd2eebe0b5c6611713054c22a215d191ae513bb6aeb5805ce703f16fff0860e3e73d473c6b6a5f1e26cf1d29c44b3aa2c2437b87c035ad8316352f6f7adc4865acb0a03529acafd0b53e7360269b5ce3186990ccd4e4651d3f09aec30c1b9b00e3c01a4ba3d246de34821471365d18791149782074e8f763c3b0d28bf187ff37b6d1fa0b45a53ec5bfcf5c4fc2002e439f61d51bfef422c534f6dc562be376ef338cf34bd9004b7ece4ad938a8ca6a42f43519132e0a9bc8f855c74a596a3d32777a89d185da34ea59600c1288c3f851df48bd540fe23f1cc4f78983338ee9bc09a7fb990a985e3c7c20eb52b2646b867e58d7af5931e31bb8fb7b4eb3a0a4780a562e6bffbca8e07c008635aed630c7296c28658a3b01ec7a961c55146a111acc045382ccea41e819f7470f48f588099db229520fd24b3820fbd2e8d45e0fdd19fe990de2e5c03a654c811c96c87c3ae43ca33944d48cf90ae29765edce4d0036cec12e92322450e4072460bf9ec36fccb15dd8918d032ed220c2742b735f4556cd97824649c78a0d26f605338a2a4ed29c6b17d45eb8c5972067d348fa5b149bd5d391f55ffd34d93fe406"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f492d9544cc4618b210e170cb5840c7218f5b51f8feea721caa809318e7c6e33","proof":"744afb87a08ef1e75c9d3ab9ff30e78357cff06ab2522201043407d8889d79482096c6af50d6d8cc816384ece7cbeea23e5582f66b22c94d7bb67b5b89abf66384e3dda4c81f91e755ee7fa023ac6302b602dbf7916005e475a428bee3088803cabf4496553fe44678b8e2ce2816b85ff830bc43b4bea614c52ce2e1b9bc7e7e38e33788db713e513540e9241e157842633447b170c98d7e571aa446d5bb3b015646583ab7dab95cb1bffcdc07995dfdd2e4ec7d7008d12f15203eb3696dc9050ac5c78d0e2c4b1b1bc823b28d120680a3747b56cb1c539347b93e8b94de980ea0213257fbd82487edee4b5732d5c943d372685a2f545119555742a4a162a00680d74a1e99ad82f8b3f7a9377c4acb0cdaa8e0a4b1a4562f2f034a0e063a5e3892164a403271d3643b374611c53afe82ca215bb54c21c7defadfc37d7a35b8590261df64d422f272fdcbcf90e8c102299e1bacd833220f6c0c7b37ff699815345a86f09255db25120acd994ed616d2cdb0b841905444ac7f4225d0b3f099d2503cc2417f1c0c330a04c37387e440e271f5dfd47d98f559ecdfd8db4fde030e16b261a7a48a58e7ba9df9c308ded2be20d9aae4807a97ca3b87fec9c501d8504b8e33ed579563dddddddac1dcc1d9ec2b53c8399aab143d97b12d4268f6ad4471f6d1ac1633e07f79407388372e8a73720fd25904087693d73886660fb9239a17e0834e7e7acd50d118d1e05218014bee9424525cf2c029e499b69d6577b74f29f6b804cc87fb3ac3fa282baa96a36dbb08f199e00fd00b89c042cb24a69c7927901019e4f5ba16bcf4727b24f19a2762845c08f9f03ef3dcfeee1222f9fbb74487192748314ebddaf0b876749eb7586446be0c4bd117a655d536205991ebd200e8303430e2958b56c915e25fca94424d292c315ea4f4ca446c6b2e3aa8755a09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f0d8ace01ff306b7473182105f6eecadc3f20ecddd2e1b0121325a97ef6aa72c","proof":"7c66fa46d7bfbcb0b2e55432b0206f225f49f86135204da00abc0728c2452c3f58c0e057f4372542a76a8ad6e45aa205c01cbf9a4b82988deabed1dd2ad7c472ce252467e0d4b17597fdfd22bcb7182e5d1b29304c444c0881680a6eab93eb65d024985ee69ffd77e3b3655d0c77aa9d54ac09bf9441d4bcb60e23ea99dd5d44e5d9d18047b9a9788ab7500ff22a28e4c5e58b8040af669be3960e786a52d609c2886e565a6c42ea44e13e7d4d8f8adb43a184289089a5efde0562b963b918058456f52bc73e192606d93751020a208755e7f4ddf4177f5cfd336d9407efcb0976bf883f140cf819128c3f471d893bc158eb63c7ec8bc377ff31d67241834a2a540c387ae82cb1745cfe5aab092c3b4ffe3d374dcf8cedea41d218f2a17d8567dc3985bed46419ab82eee01f2b8bcbf486a8bbf03faba56ffb31d20f5f31727f844ca86f77ac1ea22dc585e1db38d963372c29236c2349b70762207e6da0f7174ace180db17af5b93b5421238191c36380d3198ce0b63aa144b26a67fb3f8873ea4c301dc80af29e08ddec6de40e3e236425f78f50d1f7c9ea2bb740c2cb0762a48ad4e426fcddadaf8a5d836864af1cadde5356dcd118c134e9a86d6d6f9a485e127de62873611840c446d7b317ea87434945051289212d7429a4aefb3d871a6c50c0b8d74b34f92a4c37a64601fed0bc8079ec0f4b2435c4e07f2eab0aa37b34d804f3a87e9956f02ad77fd515a3455f58a227dd035b2704ed554b3ec65a4c2210f48b26e4f323d7ce5bd95fa691bb547040fab90ca03c959df703b611ef0d08c1e4dada8c6e2d0a8ba04ecef78c1df849ec3e1ff7064448434481fd95d81ce31a9b1422dfa6027aa7fa0af327369f1efbd1a48db5ee0cfa737ec2c15d6e061c3dd74a8fb140baaeb21de64a49163b7b278ba61766118a4529ff99a0ff600f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6ae5822f5d7ddf02447975e155249e084eed6e75f96b0cc80aea16b9d28b8027","proof":"d80b7d7470a4e90b477986da1f3c36bd7fd4f2eac56edf50dfca9a53a818bd149ade7e075beee348e78cdfb7180a6796d65322422e9125ff8f3e59524c34d331f6cbd111d416b1f47e26e310e7539372390692cfaf8aa1072deca9934e408d048a65006e80a460119b4314698c20ee3b6b79aa57cbe75c73f14ac01467f211637da431e95cfa43d38ffab501e3dbb7262ffc5093ae12e8956b61d3d01568090aea428bb6da154f8634b93936cf0e49ecc21e76443f3448da0a41421c3a781a0a965d647c3a693f64e2e2a7e31bcb643aca278a97cf34ea941fed38daa23b5603fa1f7075cc0909bc0e1bbe541f9a60cda7c4881da0d00122409c08aa7c4682118edf8b70e9530057536f4eaacd8c2435724f6a2fe3dac8058c034fc351da0e2d263c8c572c8f647202e065c30517bae14501ef898541371d2c929d2f95d6e739e64cc0b99bf5f318496ffb2b0ed7020b6e036b06173b61124ea60d001c45161caa9013f3c3fcd25b43f34978487c9686b2c2349221e3a10ea69af79fe544c57b3286ddcf263301301de47806f2824b63ee0ebb7883e9a26626638a55faa17a6b28cd7784d325bc4d16f02bb68ae7658328b0b7f1bc07dd7e7382b0adebd5f13ae2114acbaf39bb6a9cc5474f5141d15ef77844eff19ae62485ffba9a17646306d8147ab536d76d2542e2a6855f9b17a1f00ed09b02935163bc5b8bfb682bde187ac583ad48bd6519a570f2e5697ca93cc8cef801f4565ca66a274da7f6bfe75f8a7f8660419cb71bc958f371a952e09dc586a759a9b8a0669cb9b2f2ef12c07934242439085c803854809c0ecc3f3f50206e7ac0e6fdf1612ab8667d6eb2d22688b4fc83f13b4eaffe9b3e9ef9ec16485e7ff4648c65968aa000cd56ab995b0c81a0089b0eee1bcd1a63b89f4b158c2e7fe04cbecd05e10f4ba562c2da38300f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b0779faf70b3662cb9fd69d28163a1627ba9d0f819f3f51bd7ef39d85549c910","proof":"c48ced30c99537dc2f812c610d46c5b67d105ef926066383e4946a8383bf681eb0a046d1b8531721e91cb8e3f3c3a611e83b119730804ada3bea01552fca4a61aea81fe8c8d2162a4eee64c21b5cb7a0475f0bc28a4cd27b024d273a172211108035d601de5ab09500227e4a490fd7443d6d3157c84df353c39f5d0b010aa378d3ebcbef0a1f36a586e9bea385fa8388ac1e0cb6b3174ef40a71151a0fd1c8035f2c2b3a7ba6cc8337325a9a7a7bb66251591243d7ba59567414ccaba2b42d087e95ab6f3d014ba86842bdaf31554c4a4cf59843df30f815e96e2ae895eda707408b086140c154533887ba83ace20371c8eaeebcab52fabd2153ad8701d620669a48d148974473007839d983308f99e62abce198fd185ded93db694d205ce754d4da8526d2b8415849620c724250ea2740b0818f96f6d64110f3f5d134445a2c2873abe52d45c6a207215daad2f9d96d30583c716624ee036f73218bc4cfdb785e1f7be3c06dffe6a83433c5eec4711bdc8e5e4cb3afdd249ba1def10fc2ff73d8e6909588356fbe8a588beebb6ff69a6c27c6f78b52b9d3161b6eb353bbdd34365eed5aa94ee7605b37fc320ea21ee7db899f416242ba41fb359c0b70ec9339c26d3fc072db70d3f8af0b423310cd5b5710ed577cd053c63ab7d7bddf6e6f65882cdbef6d8fea6aa5a6449d5fcb2aa596d63443b0c664ab89924a6c6ccb3f36da74e8ffddbf6430a090c6740b9c16fb12597e93b2f7207a40691b070436264b5e5885d196f16434ebb966dc386a4cd32298e1851a72e2764add0579ddab313d34fa3097c022e711f5a6820450b812f1db7b9f5c675439d08d30f93434c3fb2054081901d7adcdde703f582860bf9d719dcf88d596f1f73f6971635dae11d20e838f4836ca5ba8cdb9f1d61dd1c4aa81cbfca644700ddccc08785d63ceb30406"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c83b9de0ef9b5fcd725b2dc57fc46d7b3a382ab83a95fcd339601653293da342","proof":"065335e5947b91fe2dadb519bc187cc155e4c7163c7f8e3f292782e8db5f86344099d63f0b8c106e3853fb312af0aaaceb5a3d7aa03280a943eb77c31265843f96b31cd0f075a678a3471d28a26eb3a16dec3caf073b576f2b6d7a1c0c35801f806a8d82b500d3dc6a59d2bbd2043b1e524ed6146da370386af84aca39092d34325befe601e3a20bdca8e0c58a23748c9b5d53aac9c42e9333d8c052c8f5c70a804efd5195e8d0f3aa3c5b2537b2ce39499adbca376ac1deb0b211fad95eff0b7dbad20165a6002819564e90dd2477cdde5a620d18eeaec67353c0244ef47702c4a07ac742fe31521f9c8a8fbff65c4895ea494908580418ae6f3be40a7f335e562628514d3c29445e5e101231b1720714ab8e053ff7c53f6d1fcc2c8ba380670ae3d59b9f96c3a4bf0187b6e0032446a7f1acd1b9beea4aae407dbfec556d15e03bd8fc664dfe46c721b77d5851586fe5f020464fc5c439b5e55ab09982de298af3aa3c7c89ff759ee8c37add26a6a34f4dcebca054890744a437ae8406986cea34de1f0499e03d4b5c0c0e9afc8b714fbdc851277f2818a445ad95f1d6a03d042c7400d281f5c3a7b1b192e4d85c6003adde36edf85075206930232370951b9233856a6f1196617c78253b7c48309d9c0e0f32a157bf433053147ea00c205518bdcefa849e4de6dc1f3f6564ea1935ccd6f6f2c5dcce86e1c694aa61afe2449cefc8f920dc7fb6b69e04f4279456779a203d62c35f255d6d5828dcfe48f83ab6bb34ad14cf9b2fa924c334cd313137ffd2406e9235db34ffbb41667e854550564850c614f2c011b97f572ed331774232712eef42acd9718fc38f5b4cd4b95d0a96abb21be9e23fc88d2880274982091fa67e067206774e5682a3980550eb0e73a8bb796d87980900460eb65f682ce9a0eb87c8f32ab27b3dba04e20036660a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"06840659b87fde08305ad1c079c3102fd2f5bcd891235df2c96f8bc5f5a74244","proof":"e874ac3da27ed18c739208dbbf3f14345e9e60720af71336194b9ee09c92da4bf43a04ede6352457858c7c2a6030b242694f62bb11abd688bedd3109a7d4b0426219f38572ab9f3d36878ac6c7e7b387e49dc0eef62c4eab535e3f907b7c26124c1bdcec81c01ca19b256675857294fa3e6702afa60045ec69a9ed791422e00f6db8e124639e092656fcc2435fe7c1c54851f4680b6e2d6cc53e3473f2868a0309fbe6f3aace057dcb41906eee29bb1a7a15d5c3eeed58a77bb7048bd36aae02bf4acc70e09d6e6104f0fae129bd2172c5a99d390a404c90c4ecd57f1ee25908862d19cd7e6576f9c56d0f16c905c4bba65fc3009644865bc13207366304fa57046d022e9b4687e73cd407243ab972c17b065a24bdc158ee7094809ec987026400e1f8bd1a8e7481cc470665c8f770f50ce3fec3bb731b0d4554678c168fee4cd8f1775ae7baf8db9814d9d7cba49ff7bd6de96f0ff8aeb0f4ecc9a2c249ba14acec907bfb3a2ec974343219ff0e051daa934a0814aa52e34bfcf1c70e3a7f68b420206db342ca5bfec5c0e0598617f831d66954c62768ef92f912d22ac9123e085dd6c4b147487d5d4a3ac9f2f2c3f826442e20b32143ce354016b8d1d603406e5d66530c001ab4e2e2f6de5f7ea9fdbd901cea388994fdd0d06acd2aa9de3a2cc64decfdfb2826510d7c6e241813e55e9c08eabee06594242bd077e3712e474034f22bfc74c1c344df7bbc0ead034ddf1d4ce99226f745f636402cc3cfbb65a8bfd35e4eb8b077b8887b78e2afd346501ebb9326180796012a493efdaa1c0b0a34dd912594cf7198c510c17c8a99c9e9d44d494ad2323cfb8484b1780f6360e21e8395039e4b791204bb6d97101a9fce6f86746f9820a96537b1aee8c2cf0bcdf5d0012a1f9c9eb08a0a97ded55314ea2fcaa710dc58f3aabcd8fc050cd709"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"86b50be52de06d4acdb7fc541ced7a45bf155ea325d2b1d5e84c812a1491da6b","proof":"6611338985254029191de5857e09a645e1a0fd2c90d67c75b7de1acebc66047e184948355d57d9efece42960e277f51b779ff4d3f727942a03d2f577905ade5d6ab1b902279aa768e76928ab7df841d03b614e0f5e76b77a27ef60339e00ec187c484ac64f679df6a0ae56b40d1da8fb8391d1824db632f1d65c9915ed6d1e37ccb67bc90d56a91abd95ef9391570fbe24e6bbdaed110af369b0b802143049080a8e83ad841d4a701050ee8e97c4f48e7d8293ee6543ce87ede9f5a68897090a6a316a8e32a130d0c6f684bb79525d2e8c9b8b3d082f27342ea6978e88073b0aec7ebbf9a003fba955b0c9308a93b31548495e5f2be9f9c148b6c0f901c19d10f0a0202dcfe1b96b0f7ef22be4d26e2167e26ea703d1f654ff0600a4d928670c68e2e476f3f3581a9806537620991dc853f48aacbce5557904c9ef08a8d5a872b8ac09f2bffb0c8954f03ecc0aecbd35b5022d119c1233280f796549ab98822a6464c6ddc9fafe7bad8586d1dc579f614f0a8abb5f6d3475f369db8ced4baa2d4248efd2370272317b2927f73c5b35445f42087f47e6383db3109c881ffd5d2964e4de1b7d4ff5c5431278fd470f83cd7b5ef859be93b3ae86cafcc70e020a62b679cae6abecaea4d473991b5dc97cf7a6fe4f1b4722b4311c4a358b5aa4724c54eaec362349104db341df6a2ad97c1f4adf585b98d5a34ce4490bd5207802047cd8897b7c601d18e7c5ed5b0e20d24a42692c9b62ace71e603ffea39c7c42436ad043ddd3ee11008bb00bf4cf068fdbb1fc6a2c001347f2ac7a0a71431d1b0cc059ab073c28a035387f13a84817c94b60497c481331bded2c72a9c47a81100a6745cbb915db7470698e4673d69c86f1e1bf367cee66a647c25d24d27ede760c641ca25ea306158ce59e55d5629dc2ac1afdaa29ef5a34a6627b7865202b8607"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a2748ea76a62743380f7df531c0fa9f43d42d40ef0a6505fe9aead8132445d28","proof":"441ab3b1854b609e7d2d62b4258cc6d6795a71fbbfad5c191d72ee44aecd2214e8c80846c0b25784c3cc4ec70ba1f3ed4c064175ab9c30dac86dbd9db083427cb034a686b7e7c4c5802b8817e8c5ceb921ff0bc3759fe3946d878af2967cb14a54e7852faa9dcdfee331ce8cab58b3af0981c3d1fc828b3b0bb702e0835c6a1ba6e2821d5d8ef4ba8382823adb5c07e6121e6f2a1b5c0de495a5ec8a646aa0068e16736b0db24569b4eace3a80de64578beea1b1208a2666af0fe0be519f700c439e922a76068087af552ad11f2557b2a6951b5188a5220c5bfc53ec17262607f27492f587b266372e6fa38a848b2f50ca40a853e56ce5e9894a9b457d6c2153f0211a6313efc7c03e11a7317ba087aa491fe81a9f02d3b44a644a84efc50225dacff9a7596bb2bcfdb36a3a4f715232692167c360586b8a093b85ac17a34d17c4f7d374110275f6c43d09d1442a79c102b5990b3ab09590382601f8ec4cc13286872b8ace9d479c65859ef4ca58791fbbaa1c8b27e4e02fc85cc90b7938d26560e7ef67eea468fd392bbb33ec9f5485409ca023f622e873896d10e90256715ca87900b4e284ebd148e892b6cedd5c4fdfeb15406c34405891b91e86b3b5b52d1c79b97592498e375ffc3f866779496db30fc87e4b6ed8b27e97cda3992ff7403cdeedb53760103eebb8a4cbe191497ecabbecb9643523b079634e6dbb5e11313232b175b6166448ff1d7cca0267195417b9fecb3a3dc112a7486267369b00679c14e2e5c698493eb4704b9f31a074484e6d48f6b1f24d9be14204e81eaae369e611a381f034c5139c7f0b39a8fcc4c4129729576a79c01ebaba3b13c101073734fb2a8225dd436cb3b006f83576d638d44b9b5f1649912afb187f292c571e08c5a0d29c116f845fd2c6490e9fc083073641e101c34138ed5e2ff253ff630e01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fc736c2e9136c23b1cb9cb80bf9d72420fade7dc8a338561fe72feed3ea9c063","proof":"8848e80042ad00213c45d44cb4b9dfcb49eba7b67dc7c171122a403c8a360b1f4048fd1b0eb46f42b656bcd468685db16485ae2e0a3367f1aa659775ee59de72c274fe4d7a9f402b84d030668dc0225785cb38d4601fb002bab3b7c6b13f677ffe19d66117efc9e810cfc21ec8f1699ceebed1d4d32f730109098309b9f0e613ded3bf9987801b6cfc1b37b70477fa6fbe9eadc9517891bdf907cf0b4e3ba402d0023e5b5878c382cfcb51586622b83d3abbbb35602d8aca6d71fc0d6ecb3c0f10138837056df6604dec4eb73c477c59146577c0f43dd4810ab622371d5b670f880868f44abe97b212133a7d37dcaaa67645167df3d44475cb6702dd7ae04f6040aeb3a2e410bf925bbd7ebf12ecf0e5fadb92d4d05e61223beca4015a6972539438839d4cc538568e5edee93e59c45b024a8ea7d799d01d851b795754f95317e40263c861e1a5c91786dde98b6fb6306eadc02241b1b2fccd82d48d6395512632b7454d2298b223977ef1a179b8f4bf2ebd2fd3f2770d8faab88818dc867c5fa00f2f1362d46600fb4df97d233ff24b60ef3e1b2090b80982bdc4545e7e647b7453cf2ee22f507e716df412c9382c2f660d6c8a280798ffa5a9287971cd0066162715aa27610ba3313ef08ba752a21760ce7cd0242ce853dd241eec0ba1f67bb01965c398d4ee2b0f7b1b357bcd5a3e36c3a35a133f30567f83e2307c155951640ea83c9912e15104e7d404a696fe2d3fcfb28ec8b767e08ae5bc2ce7a6091efade6a62ac428f85dfe7458510721195969c22ae94403906500216433f64ac028c9dac0b36746f27b3b1ee09d935a2408eb8f77e1f6e549e0805a81061720539c4e2a11ec6962fbec47443c2f3efa1c3f6ed7bad79819c93233603e111a73a0d3aa573b1e205427d29e71afdbb87de81e13ad79117b0c36f37a3e7be787cbc02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b2557c73b252f4c1d74a1b4334a4d99f6191a1d78fb477cc3b453e5b05878d00","proof":"e22a0001cc5a816b6774de2abd95afa2efab2824b43ecf038d0f359eda51ec1d8ce5eec746235ae9d9f6c1752fc37f7b79488b18b128b140ff8e3e22525e1734c0dfd54ff2878e847b0e273fa17808db65c2cbe1d32eda644c32dc7e9d5af405165864096cfed10b871332cf923686d662a28d7fe75a5eb78e1bdb76664f0f77af392264bb11204149066ac0df8e3b464ec116026f43f9dc66d41fa1edec4008846ffcea451783d170c08ea1592fe997064422871c9d3def66cead0a3113690878e3ece7e169318c78a0d1f2cf8265f8e73a9e5a9e296ea94682a4ba3f77ce022a4b0bd5af422192096fe4c1fd2cd22ca70b79b734a8f61644c42ced79f3c64af055d0c6399bed23b95d5cab88c69daff3dc4d0ea194f71f1fb55b4acc5714196869c5dde0602aeb8dab1dd2e4c90b04631431498ee7e39a04530b929fa5773632349ee24882e3515154e6af1430516b7c28d5ce9a424fc8732cd7a1a750c64128009a385a432989ccd07423ae9a55d017a3ca1fabd3fa14ff46f111c19362101e84d45e81a352ecd3c5b28cbc021ea5df70c0f5d7408b12dfd5e79da5d915333c82eb00614b0fdeb98d7bac800974458ca92a84ed2dc2b374de5f7aa3d7426f002380453a77a038f49b00b0582ace5002a9057c7dc648172032ec2ab1e43e6b5647b3d3144a118db8a4c71296f941b9be026213c34b13146efc535b122fff721286bb13c4629ac88d79091c39c30745192d021fc24a1ac0281547510274c95600535bb18e3bc0c37b99829ad394e60c9e3dcd88768d34a61a9caca8a6407a5d5ab47c9f11336cc0a876a3c225261c151e4a39494d91ab59174810830a4932479a474a1aff5121519049972263f29b126030e39402c737352daf43e3e175470c8810888c0687b6c36d7290e104ee30dfeb821964327c572db2a26c34185ff605"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bccba13d32c1ef2cd05c993426ce0b84234f6964fb56251c8c31441d3de0d134","proof":"ae235a575ffb9903604534706ddbc1ae5248bd0f8ab5140d8ba418313c4aaa2dbe6b061b6174de8d395cb806939d5c5c3e07797d1dc91b2c330e5cfee8e53b521cdcd44b883cf4927f622f2dfb23069412fd4099d88cdd7e1472ac903e73103402f73cc76ab4122aeed433d8cb633c7cdda67d01ac973e457a433d823864764736c4e40da0b9faa2309453e5d6fbfe3aaada9ad4275db5b363c679a5ce023e01fca21db0bbda28733d2b11c4292838caae49a60e89d0db7de442018ab0d4ce0b33961b91b5167a1742ab914c42dfac3ae1863bff890330282f5b1bfd5aaeb30234149f459c83fe432ec8e0d381b050a3f4800ffcecdcffcd49c6cf6b01c30371105acac3e8b66d8c2bc78f810908c5a7200657e95c148cb41e4e42790a25bf3b2800c42f04e4904db7d818802abe619ca7a30078b5d778e5a84aefcb430d507fc69884d70e80181a6f9915eab36236ec03a83e3be58188f5173a89a34ceb6570788a604e770e789bc9aa33ff43189ce5ada7972aa744a1b13f298ff7d818870af8fdf8e5d0db1a86e766372f148731ebebff2baa777e797da5b516df88947f1a765728a48c8b8cabc73224c3fc3fe29151184997bbeb087e2fab4a0c29ffa75f3ae57f23bbd270c7e47e3c0076be765d9f56614a14dbd4c29245bb15436f7734ecb6ed0629aa85e3f307355ef0f77e5fb54566d1b93ffa54855c9ec99df1c05d54181385d4a5d492582d1e9709567dab57a61d54bf887201576273911dee152dc008358a717164eb3a295e28cd8671d3f6b72a21d638248fce89c334ff21df7beeb48126f920105a83f9b4998da160cd5f20b18239fa57ca81ddb0cf8906c6325c04b20667951bef4199bb153be2e677391ebe70bf6e86484c1bc46487b6d307f1486b175883835678389dbbda2cb7bfdbd48c6b7cb12d7c5e81031c15119105"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5ef59637cca5af39fb4f9cf8f3bf27aa90321d55c4d15519cf876cbc30686b61","proof":"cc08acaaaa728febd9e55960955424b7d1a353fed77f4c819dbf7712330faa4ccef51cd90f9b679970f6d6d2ec5423c1836820b2eab17a12790a72e64aad773f8a5826cb5728d1e166074a6fd0becee35329fd313f38a66f905b191bb5f185514e7b8e343bfeba8283da2c389df23e4e8184e595b06cce25a67816ba2d14675bfee8925cb8f0f3f8f4765fc15bf5b23e11e5878995727df9ca5ca5b1bc60f30d02cbb788348272d73df888edd85cb6713fc72116a28d67763b4e2d15654ddd0e872ba430b1dd6481303bc461654cc7836fbb96be2a80cf2faddb71be3d087508d436fb52183c05d6515225e6428f58d15bdd224fd1cfe8177337938f71108376c8b80f39cf300975989e7bf35030bc3552eafb91c000a026fa6d8c6b3a5262358290cbe80d7d0a4b3383da3e4e15a10b17abd761308976a2466d5fccf20a203e8c57eb93c4ca161b5f91c9b2b4bf1fd2fa7e33bb13dc0cf8be68519b46acce3528568b9564bd258f3a569bf523146b118fa48647c87a313040f4e92f3a913d26e2474ffb42c1947ef86b8ed39e3cb33d6ae9300838de78d4f75343408f27f67eac60676a6a08717229b88a73e7180835792712160dfd792298dd45b8244f2528460b04402d89aaae9de64ee79d7397d99c50ee69c09a71474197589589b5b5332499f102228b191814556c72a7784cf42ee4dfdf8a1e7de7f506d93370a142262abc8e48d7d59ea615ce673a047dc35a12786a26c398b363c33c0a35d6f9c639c0ba1d06494a492ccb4b0b0cb297f0534dd7424f9d1063549dcb02ec5bca5b31daedf926207ee248d616d15296b855f7f67dfb47327fba3967fbb1cd5935e762bc221f42687b5b07110bf0b90ab80365437bf3233b4bb382a488d640d8a9b60e617dc18c0f54a38b1206f30eab698c43bdfb77b9da186e790250d958d6001d04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"eef03d1009b4feb4f05c3fd7019921455cfbf6a2dc6ea48675ffc5d741e2555b","proof":"86833a33a549904f67b5fa73e59497d49a2860e339675806c21931782c38ec4dda40ab33c06017be135a9347f14eb9e564294e59c8d8cb842420f8c070996c5244e366eeb4d66fa4707ccf4fb424f2e51eadc348ccb807f40dd5cffc5a146c5c88e22ba090248fbbd0305e9a6cad768bee60cb2c5f72e131c333f87f726e8d4eb652c518476777f712039c9c0a1650c31306dc41c3b2c1d1cda81298e73e110c7d74b016dd1c04f5b121a9a1f8f64c0e583f468a71e55a1e2e3d6d0e760682085f52c438bdae7d887e8f80f9ec7345fe5086339c25b3db6d69b58847ab904b0f16b4e9af04e2c956deea82de02d8f2ec530960f28edcf3f574b76f8313f2f14ba89e6f1d00e1108819840dadd12d0987168b03cf6dc53cfbe5e53005c3436a58986c03c2db3a531cb0d93dd419662dd9beef67675d4e44950fc77d51f52d6821c8fe78479ed74e30a43bb37b3e279e24b9f3c4d3bc400cc7068476a3a7cd592ffac2b2bc71a555b2ef1fb1dd019e188afc90f146ba1cd1ffc658e3a8aec3607fa811b39e742cfcfbca7d24e96b2592d76951f607fcca9b33028856d96a85ab742e7c2f175ec22d080084eb618c4aa2bbbaef6c0f6668af410078e9cbb2d23962ea25f87c2bf42f91dec829ff83f3fbe47f8bf4cff0912a33fa1f415a536f6437a2c43d23fc13d27cf3acc738cde50a45715b68dccad61216cd2d8c753afd9a012aff05d6bf309f75f0362bf5b18e2586fb76f1cd3c9c5fd6fe6777b46ae3bc7e76f444059dc848fbc2be4b68978caeda049642fea46e8d26e5d1eddca557c82e687a421ec3e1c400132215cbe09ac07b4452c661f4d2d005d61aecc1b30e3c61ac0ea398e343c6e08124562b3e07340cfe5eefce6520e25b3440e2ffbfe36302cdcb24bf5215e1a94cd7cbd48272fd9d4d05dd98985d5494ca4aa6a4381cf009"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6ee5100f922ede111b251ca05d5505e6ee71df56224b7c3a9a05af6329f9576d","proof":"2422863ae239db93118e822f4ce7b22b89903a811e36fd18b63031873a980d00cab731240ec11982019cb083b13c493e0e577f6c975873a46a6d5eb3bad2ee4dc492fe3ab461403b2a42994dec7c319dad3ba6fedbfac881f111610d3f911a6a9ab810a95c0501925936fe4962de3c4f1613148dd31047cb710b909dea7c0019cd87aa7fad81ebc7883eebcc2492a12e8f5d8fee18283efd5eb5b1d563c9ef0ba7fe5b791561883d62aa4491fab3c47a68f270cc018ad57e1ed37cf5b7a0cc04a75c3345a8a7097d8f203b50df4d572e45ef3ba4643258fa4434913adb3fb203c43f360d40678fce44293885fc2f849c70fc2cd08b0fbfc619a6145969b3df611c96f0337da9e13ba613ea26c73af02c6dc2434350f44b30cfbf23ca005f8a06d265012ec24fdb37378a692a51c9a6c0ea7b476673a3c837388a2071dc8d553b2e9bb74d9c129bbfb84f2c59d7b17fd8e70b4ede509af2573c98de7ef281d77bbc4ac25bf978484582ba587080a84a7a84d20cbf96fac25ab1b175fbb334f82ffa56bfe117d0c3a8abf6a78266bb578d1a0206f89c85072e19fcf3c1e4888d1970e5bad09e2af5132f7a6f0ba91c518bbf69e999012719ef91085a3f3e6dd47de8eeee5429bfcbd45c52882a530e06d8b3cb894685cf31a68a0dea8a9cfb4313c2dfc5ee390c5ec73e843e15be40496f4adbd39d957721361f9fb1464d6df5188804ad9f812df9d129bfb7f95562913b9b88750d81ff810e328af5dd539a3041166f3d41fd1f12fe43cdbcbc579f572b5ca38a2bc2234ae76ec2760c6b91a16b541c072a5ff85e0d80b2ab89b698a627640b5011f9929fe4200809d337e1280e6fd903c44588b73554d8755eed5c15a1ff06da168662516cfabb4bf44eb29203c440b190c073c19c4490446b173694b2ff7511cade87f517b808ca7006ab2303"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ec13bfd165d0ed26b0ecd794731946a17fab0b6217108f2c064f7419522d882c","proof":"6cc89f5fa21db56144758d3e5d68deee29130eccb72cb1e3363c6069a70c3b5690bf33c456495d4f0e76c73e60b897edcdce3558cee98aae8eac16a7dc4ed44e6e28a3e178e34e4a31e621d7ba5c741297c7524cadb43250da274b5cff1c3b1766c527efc3125aa6cf5b8cf2d6616b818b84824e9c205921c9aaaa2b28a59e36ce8c66682cbe7e5621d72ee4d7ae987c58101cbce79d3e9f148a9b17880a970e0c3fcccec9da8b55aa9c730b52a3508b22ae78f7510f0e54e7179b82954f0c0070c817e0010f60975f47685fc5d449a3d56017fd16c999fc00ba48fb682d820ed8c36a98749ed0ca77a306e5ceb656cdc5516fbe0bc8f0cc809edec2b0975c41044320165e1b705ef9ad569f4aff882587ec8d4e1434c03934a7a67903076e12baad880ce989f297ea8a07718b1f1946ffe0d8c0fb5e0526371460d92742b47b8041dea4e1ff790c602c3e4a9650134c4c84e3a5e219fc0dcfeaa716de48841c2c109594c08e25ff5a7e7bae92b2e3142afbda0ae762d4c037a9157b0a577853101bf83b08ecf2f611bd8e2ba17c095371d26a8956ea71bfc278581475df2378d6b552e1b653c682e1dc8e2c1e3b8851ee3611d1632a58a86be0e4811d2cc36c94a9a6008704aeb062771059b332d97ecdc10fe8fbf2081c0715f1eac7a17e6af0fa9d0abdae47066077e961c58ed300c81dbeac168628af3c42c5446b9f5c2aaa1a00e88aad4cd4501dcd0d0465a5c993678cecd30a0be1019d27cc50bc92007c6b49c3bd79c289d58bc357ec621c7c9b5b49fa24ef873c571cbeca12a89d59801bd8167103ab83a09dc164e77ea8fac1ab07e416a22b56452476c50fa165380892f700f95709c0ac95c4c9a15ced29ec3054dae2d35ba83eba30834bf0b3024d4cf4a96d59c2d0e88871cfd928d6576aaae7adf75b1606215fa73f72bdd601"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c2cfcb91915cc87c0655bd50c12e7793e8cfb39521505a3d3edec138f761b06f","proof":"4ad3a4cb712743346472f8b85df31ba7e98160d46ee82229bd28cc7f4dacae56388de49d2e43b2fd6a2aa8cf7710fe7e3576409a2a1c953630e5f8d0c2d315641c53c4bb6ba474dec9438ab8fa1cfda163a8f168525f73dba03743e01a865a2692da0ab5b54c60e151f855e0d5e796d0b0dc13aaefd8582c1a276ce239991171a0a85df12e78fbcc1434a054f4c493797e2e157e749fa43c6c907a3320ab0e070b219b279755b1b079c5b12df6e6bae2c59bd368c0d83f0a288ac0dfacf2800fec7172912204be324cdf0f9239fa8be1945ed9b13c29786b56bdeeb99a93df0096484488c68f0c250c6e7d9f3d1f1b3880cf283a9ca2b6c6a71d29432503cb488648540afbf03cb5fef40bde6de8394c1e88b1e2d3cbd6d412d5d1f7bb512977a4171f13963f216ea160e6ad1e4fe1d994859c3ada35e91315de3651a20b2e54f8439c0063e71775fb4925a12afe180f066784c576c3fb343cf22c2bfc092f3086aabaa62bf028c8ae575d05ae7ef2d550756156db74ffc5184be63cce18a0579a35d152b8648c7ac31f60050da39839704851c08005e76619a4cc2c8f52357a7235dac3cfe211f4061f636f050fbae0dc1d9f87b2f1da16e381333d4d046339ae59f4af2958a8913bc0276a7f1cdb9b9b2c97c3015b5670e2c1494cf3aa073dc46875c425f7554740b4e60c2a6a01a7485c9ca3fb8ad03be9ad6534772a2a2f226b827f8421dd084797bb32959b2b8cc03981c6dabcbe4bf4200ed1204c10224e23afac91ca3a6436d6e1c65148c8e32012cb4a27314baa12e00e23c73504282679f7ee604e0409ffd72d9fd21795274246dc5587e096c3458b6690edacd71c0f6eb7ede604f4d0375c6220982a16e833c80a6ec0d1cc5d30e07f860c8239058f23d093b0a7d9e610b5b861491b2897fb77cb299836a5bca2e5ecdfc447e00f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2070ff046924cef4618e6e8ea0441594afe391062f7682a03f07cdb7e3b92b60","proof":"b2b30de3225725ed4520662d78b4a12768da50140ae6a9bc9a8f8cf5b23f682a9cab55838b789fdeabf1cd6d93de2bb2126bb77c60f8035b7486d3944a67c7279af9e394434bb3fcf94e73e19d7c4fbc77e81f27612ff56448589958b791ca69888237aa18a906eece05044709bb83cbec80344357c67ffa240573da88bed0734989f025f6b2ad92fa2611bdbd7e039c7e009460bc54bc70963cd3c79be5e205ad2b0ca2abace98fbc2b76cc7c2e2a8bb855a8827586d349005ed448de1d910716608b6c3955db61b0c9d3b874c040a53135559b797a3e8437c504fcf29c090cf8293b865435215e68eff284028baaa4dde97381b270c0f0eed31960674ffc7e64666d70380d3eec02de45b4ecef0402d9d294f4e8edee8e6f1daa3ac834a6077e59f5f4931a8eca4dcb59c16f2bab552ee0cd09e213539abd7fe71f62d8c21e98e7014f79ce822f9c189e637cab63a28e230cdf75cc45a6b384ef9f7f6820655e1d1213c977014bb8a1b6a5968cd787998b6bd0dfe7511ef181d9bb5f1bcd1dec7ae766f2670011eb4a743aab2714c6eedfc2d957f38c54ba41704be7718571e27a3a55d86549b011c12366a0a0115ed8c117c9a4a95e0a656316a6a1bdbf5bac44530fdb2be4551eecfaf032d3c8b06652624941a66d13e988ed85b6a74d4d969bac35851ef73501347f5f1fea4879e216078c25bf92e3c34d4f272696c5308ae07fe3103c808264652a7aff6e8d3d929e14e1fe34b6d9f4383ed49a9c55298cf0d991f89f4a86247e389de9a6e66c38d759f2604aa2bff7a29420063f45388256a50bed8cf76d20ffe4b57ace8acef26c65f0bb757c9bb1b05d5c016e7b4f7216e142d46d4816238f6ab8a24122bbadf626674513a7745c2240a523998d0e629ec8600f0ffafb7ddbb90f5b2c5165bc37508d705236f644218ac307719604"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c87d5614a036bf7b09ff48e4a0f944d8e010c581335654b573ec5798d2565657","proof":"4ada8ac3f5543988374b801363571a7f412646c8b3009859ac13a74128d4d87166142a07decfcde7d716bfcc05b8d8dd5dff5e8f4fd26a1dbf8e7ba85998544dbad78418a53e6e3875e9d6b57a2dd526bd80d49f9bb3039560f21dcb16129e373cf358941d567f2931b96bebce327e5802a99a4fb9098eb83b649f8a892aa453dcaeb07dac77b15fb463cfd4db2978ad03e9759e6c586cc2421c365ee1f08d0e22e817e9d5442344c9d762f5e7af44f37b7734debd0d90fed8534c02672a7f0e3ad4d0e9d04aa4a7b0b084f3ac7c755ea72acda8c454baba6ec0843757a5ec08fc4eea22eabee89054db86537cd3544c533741551c74b9f7eb462dfd573f655f8e243697a22a75f4c1c4f25a5e3ae733a8554e61e1c2bf805cb0189853e9f06ac494d1dd85d84dec5399dc7f13d02c3d6cdb2f82b40fdd8c0450274012551e03348c8725121e53f8f9e7bba1b169ab2bdba6d7defc9cb3bff8fc880a4274c21de46ad20faa937d8f2b5bb961132c3c06d878c5f4c762a5ae0ca27c7dcdbdbd4eb86ed33dc7af970a52db134d0be0da333fdbc8accafb38097c2e6a007afab76024a8f725ba2f36d5ee9501a6c463c3b5406508945b64ccd0d48ce228b1829b211a30044ce5aac46f5ef511228926e8475978293a7b2eb9b7071e325c0f03635dbedf25ad29a8eaefe6d97c785188530359957ad0e7944a078b4ac221cf313c21eec152bf44256c88ab8150f2f0f43e7aee64a55b8ffe3a3b710d345231b7be0f74bd9929db6ecfd07101ce47cdf1de59fb623c30ee1d695abca6008cef57cf1876c4c5cf75611f8981d027bcccb44558f928d3de499473341f49dd6be038ec331a3ae446a0addf20670169a7d1bdcd8ad0e3ddedf475bf6d3c78d9b71783710d846b0bbd88845caa668ce5612999846ed47c9166a98534074e75cecaa453b305"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"180516e703447910951a7e46d381be086a38c7da1e843370394f53e7296db941","proof":"30f03bcc5e63763147f0fe09446b189be83f2256a74690dd3eab156363008d4c52d84ca69dcdfda77618e9332abd8e860ddf6b5ad87cdb66c5831ad28e399d6024b43e4923767e22667ce4b3d5c8bdb44b6dd33adb8222c061e624a0747b1b18ee02f31716167bf2999d3e94cbb3de611f97cb29eb92033eb3d6bc177128ce531cb036791a871042f633a5a9f8eabddff4ff1021a52d9b6377b9e9640abb380350fbe6c44c6a8e00d898a9095c8cc330ae249e053625748d9cf133242f06c5032c24c2ee1cd731bd715f79f9a883e674ca4ea580795ba79013a6880bd285ee08b61f6ac4b05b8b79556a652ed1c21ca26d36cd2d074a8891e10a55d10b8b1f1e0c222ea5f17b1558f298f7d5ec92ec5dc817c68f60c86dd29819846542796c5d864d8ebea0dc056bb625b3d3aa51c9d6176b7ddd2c9e9c800cbc04eba8176a0ac8f4a7b66e7d207f0e2804b73eeb9769ca4ed48629a8eacede1be0cb2ac0f6401691a39ea0f0010d6ea0b362b4b2ff14b7d3564362786f3dc147de56b9618b27a2f858029c1b60bc7d973dc4a42526f96fd2ebdfa8fe2ee8894218078612af6bf6571e716196717c8707d0f95da6edc8b82a3679dc7c8da3536cd39a7eada8399a93ecb6c73d538eff2bbde269e1257ad4fe5689e50f620f6659c9bbd20283550e09fecf33336531fa8515c45bf313702514be5c96f11bce8a4969593a7d730c84a9efe772fee78e613b619ee77be811de9e1c73988c65f481263469aa4a4876f6d352f46bee668ff8d88fa10f7d54b18b2acf7aeee904e0359e58e6b7de9c3c3c083293b593b602e1287245a2ad76367f49ccc49646b7855cdd766c5d97841c94cae5efcabf4c2f9d4c4ed374193961ecbf0a7c44bcf10ae829d494b4e18e0d00f08a0ce59b3dc8262eec8d47911f6f70399632b59cae98e8b3a712c2f02504"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7cade7975e3fa91eb9b18c7513e3b7f6a19035977f87ecb69060fff1f404d931","proof":"c2dc4448994802c31c0a32119001b8744fe6e46fd08aa301d1f8f62593a0687a1c74883c361b6297c3200824747236f2e09b2482aaec1a4e1d8f90fcb32e36060aa61acf2834ae6f1eeb15ca11cb2dd3925be79df87d18124524e4550349804f686b5b26282c335088592cfa1cbab17cbd16dd63eb61386e1a1ba22fe3e7f33318e0d2fbc6889991fd79e2453efddcfd2db5037d5aec1cc8c221341e7764a403b7ce3d9d48af86878ba7f9c0996da75213a704c65990458f1d46b821c522a700fd728a82dcb6f39b46d09936660baed85d792bc0c546f5cc885a3f5da868260ce661c7f4340db95493cd73dff0c4663001fe312cbd1b4a2c8700e72836147609dadd375a414888c8f79571d30d30de56601a5f2cf3a8f62e666837ff8c100a1eba05b22d64bb0b8da656bf688ad1329b403d8adcf1b9ce829f76840c4cd4260eaa26271b89ac0fc4df2008eb731858447ccd47eadc5c79d1161a72cfe5e99f0820a59943c6f12ba8bfd80e18a39a820799609c77002e9a05019bcf639699095406b1bec7256140f8b29a215e3fa2c0fe189d29c4a707b4b85d5d1ad82a5c9941c2175282697b0ead39c5c3f9bbaee0f49810ca6c9d0ef8d5245bd8afd7b49501e8b949e2ea23d3aa8a39c0676ada44b9fe9e588fce8bfdb1d912440ef4b6512a7449c0f89da915edf8fdd02cd957ba602fed428d70a5734d09e0f6e97f9e4d2ad41bd8e58752f799299a25c59568fba48d7373b9020d3af038f335df94a2fb7b2eaf8e504881a00953328c66006b7d6973b8da35d16cfa058273049d0441a23bf22a71efab53e95aa46bce2999f11216138455f84254821225161a9fcaa1e24df24d2edb539cb86245c31e1cc8d4b993547338f941316038b8e50c5e6ee27308ef13fcf39fb14d3e469ae31baa5e7c7c56f1c0edf9df59708361bb56e9e3f106"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"860632b98ca1a2e1b50162e755e05efd2e9a1489af45cad11b6280d054b20754","proof":"26a61c0b0a0a0feb2d4915767b97c9b29b34463a2c8f9800d3bf02591b0db25ad0c5f1bf0f31366f5f3461bc2723bc7674669381c4b0f037d7b2f55d99fe8b6d062fefe90724367c749ed65a232e7803888c1eb9b4f13461d05211a2799e9373a011b875e796f2d1cb2b9d7fb9167fdf2f0a138f683154e7545c7f8284b2f87fdedb5af741c7671d20736ad4913e8d44368cc9ab9e35ee4577f60d1bd7a23d091df16f05908713497b284095821caa0816eb4c120f0158447003498c0c5f2a01c936a2bd378967945d64a29068ef39192827d5e1ce1b7c3b9948175e305cfb010c2409a1b61afb0afcafc1e9c8f2f2e87306774f878058b5adaaa732d154ea73ceca87fe9c9f0f7960984eb4b19c801493939d56219f678409a6a47aac459601582bdc10c41405edd318e0cca07c36fd69196ba30c6b27642cbab57e93bc3a0a105c38acb6d854b955fc338eeecc363f3daf74f023500b5c7277a19084c14727d85ba35c76dab5deb7937f623c41ab789d615e9d006b8209b548f3d820635c12ece71f9ed88607b6a31d02a36d71970fa370de569f19dc2bcce325a2755581748ca923c0b15a49ee5928b7591d39bb5e159889d54281946bf8a1f2197eb41036ea2c28583e2144f6bddf8dcba81e8051515f63531632525c0106252a6dc59615f4b683404275fccf7d531133f7135a6edc6cb85d066cb4118d63b4fdc821c73884329f53602de0cd8447af434a70920d492d10f629d008d9c30333ac05739a5902bd68fd482cc819c6718cffea6aff4419115dafc7d68cb7651110881e37687b16bb0c205fe0b0180fdf650272321fe6480961180f0e3e9422a70fb3da9fc67dc86566ef3486c9cb8ab459655693a7757ccb36626dfa0ce8f2b1791d952ea50dd7fb1f06e2fa579138a6116f5a11a3ea3b7286ffebcd8d44aaaa793d33fb500b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"848aea3e4608ee38831da8d845e49bc5ff98459b4091cab465b7b79198e87530","proof":"082cc26d34272ac1e64f8d1b7e4e0a84628a1e1ae0720042780c00bdcef3e135a27b3ea9cd4dcdbc39df9167ded80620671c5006c7825f91dab2810a69b26867b6244071ede4fd5d826f1f4b9843688b3a0e66ff1fac6ea1c4430d895b860866869470f8b05ef066e7a98f727fb2eec9771931ef8acbf90a34381247d279d056ed5fc806fb7115377db3c2f776c4a387e31277d192694b039d78855c36bec902f86fe532d1d66df0dcbd31f4eceb8965cf57f8f5f38e1976a2661b346379c1063025268b97306eeaccec5a421789dcc9aad800915130f7c7b4f867115895420840c99b7021dc33e466987a5a36017d3e9ea08e65c5e89a9536d06e6568d12b4dd05831be0822d1da71ef1c6bce2d51e2cbf8b96b44d7999cbd4063754937b74a5c1dfaeae9895aa7c486df20ecae8f0f9ad4d3feda849a7dcccef37660dc747dd699f9d4e4edb4fbecdc7aef5c5ebca1c2a36b2cea9e5d55dd0d836873ea35073c6f22b8722e71939fe3f9b5e0d56240aa73ebbbc1b192aa926332b3b62c305aa8ee572143c74100f645afba64c0bcbee9e70dac288c780d99b98e92327d7a142029cd04b6d5bc5742fdbe81f03ebf027226a7631cd8c48a05d23fe2ace73e73b83fb5b73a4115d2757640d7b818a244c760803931170ca4299adb6a997c5b2ef4f13c3067aa368e8b85680c68a903cd5fec28577d58608452f4ff3e158c932bb0978be070f676d5c6becc80783a34c74dd1b7889342b63c6e8fb39ca42caa72fe0b7b7c3ac6e6e62b3cd2e5c6cd6ea178681b0a7da011c2c68382c4fd64bb7ce8dc8250c0cfabf3e9bcf9cdb94796c8ca91a4ba8b215ff042be13c06189c31509c82c976fe00d8240187d98e6a08aadcad20d5eb07a1ea4573d2ba242e41b020da0ca48f3ae00c0be354e4c8beba5ac840de40b55bf2ebbc1c915d3cebddc02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5e94574c229c018fb6edca4c5f1365245e21781ce7e276f21e3c8a560318e00f","proof":"f64dd65b61fd6f53064944e30483fc6b985695a3f415cf81f180194de817e21cf2a5a6bed6213e341e5e700566dd07af9d13b8cd664a4705415a0b0fd6664f14f6485ce884bacbe825b5119e62ba3cac7ab6e3bddbd93c17b974f54206bac65aac09a18b4c9ca2addd342caf30063d81d4adcc376ad89f833c75d775410b9f0b62d25744938a81e04568a46fd0a881db04d5189275cd40883df70da45ffa12082c25b9147ee1558594beb6cc97f5563fcccd9ef5e92ea15727b9156df336550c325ee2d449ceeb012a85c9eee6bdf8df87839cb5e163721db3c60fc5f2b78609d03e49a11c84201bdd9ccc8bf5c38153b55bb57f3d75d31a25ab3a8f15a658584caa4a677bcd9fb0f3e363dffbfc69b9b25a399ac73b7638a9b74514e64b7e6d3c89ae24df09a28ee7dde5dd4f2833b5b0174defa507b987edce64d19f860e5ac08ef230cfe2bb58d9a1c449b60da96985924a9f94f28badc5362495ecdd355a7e5d68763c8626302fb0204840d79a85ec64980baf881e0f587fe7d6b9127207960c595241d70c71662f96b7177c70a1a4a5b0beca44fee3ea8e4f0f032560783a64de2a90688a88c575062e4fc4be669a4f37838b40565aed6f19a24a9a53403c33223b09594b65188b3d0052a5e81a3cb6d8d695dcaa704d5a3e2b8379145cdeff545308c1cf4d5bce6a1bf0ed8348a401331ff6dc0a3ee62a08cf78ef1652c2358753148bfd9fa838197b31afd95f6a8bf739d7c0eb7b4f35ab4c7bfd0f23e2d8faae0ce9b5ca0fa4b006d8a8a664e501eb58cc159d0ff29f7b9b82351b39b2504ebabb039bf14ba09108a57f6b6f2a82ee784a3dade681f3fe2a50326b02a4d44dd170f78ab66636efdf607398a8dc09bf777c7a6a814cb94e5917f26c04532057cb9ad9fe079590c1bf89342c6da741d58865aa62d93746eff35c362000"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ea136d4945dec5b45d191e2f84ab6bfe4f5b6c8903fcd8db09f146654b9d4664","proof":"c48e851ec44111b46b77bdfefcea637265deb221aa8447bf053dd060c5a06e67446facf23a704f51010373d032ba68a4594aaaf9d067b75f71e2ba7fbf22720cfa1ecd3c287967bfa3e453fac4ddeaa5e25c3de3fc1f67e332b8ed22c1d2245eec1ceb1d994aee53dfe49569ea29becb9c86fdc5abe697dab4cf962d296e90545319b939cc9b8e2f923b861ff1391e464bdf1400de3a8c0b79e05fdf0a52da0ff8c8e40b3a4e48bf0cee9a5fc811fb16960f11fe15c5111e9091733a775fb6060223d08f608ecc278e1b6451ed1ee6165a6a1ef2fa938561e5e101e95af23000f64bbfdca59d27e36fceae864658b4d52c7a3faa6f907853c255ecd400931174a21f87273f00cbd0d3903cc6a4035b1d22c7cba4c985f06ff5f230bdadf0a82f1a0d610122621305acf5791fcfd09024f5534c0b1c6de18357128419f0faea04ee22d2f877810b04a661c855f416b3192078b4d20804a823d4bd49c8b644cc7dee977540fa2c9950fd40090d5d7c31ce5ff12fd3fcbe64f5f63ad924904b6740dc6dae67bc12f2b9a0e5d565bdcabe812fa6aed52ffb2b2efb31d653f41cdc2128e2a65cfa82a429892831b691bfc2dd9c13b56684827a8ed239e3dacfa17a08ac2f7b1f7995ca703510bdf81a179f9356a1d9dd7d7a48968f376108811f633bdacb5c840d8d29ec5cf10a56b55c60616f8941b33dd0712944a3cdaf798cd65892aa0ab38f92101da406711d139c87c3e03397ec058746ac7f2982bbdcb5f9390e9f84d400ad52869b620af26ff18c8f51a6534e4ba33aaf4f524b81b89e7012900f353cd2540ab071fb84f390b7df5f31889a42dc50a4bf250819462e8b882fb22d151c697ad080d69641a77a0a31c7dc07568b095d595d02648014dcb7e40542c50d53319a7eddbceb4084666dd2ed1df811557395d0f61eedf7b6b74b1703"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"362c65057176b05dc05d410894d8e935f582bcedc4deca17cfae27e5d9e7d079","proof":"44486a0b3f3f8341fe93b86d1009f5620363bcbb561b0e1f9491f22593695a2b1adbd26f791c39041267369a1073663e72990df663101ed7aebc8044122198299493ce67994e64cec4b0d912b8a06af2ab0bcc07c7f9e30b5188ce33fead7b2bfe5dbfa196d647935f5c45046a709850f0b11dee1a1c08b001d2771fee9ced6c35bef0346db9dfc78b0c2d0fd328ce6b4b392f5d19d08dd1d3d807b89da8f50dabcc4893cac95847fd6c3fd9f3f13afcc7439c8d73e16aaffe4e4868b74933098defab4bcbbf2734300edf005d72053f6a19d2f761805526312a5b109e0bc20c16799f438e609e7aab31790e2dc1de8d73a98208d5794753b813849bdf2cb63f701ba7619ff3e943b92cbeec94ce906acfc7bf596f891f2e0e642a6941a30b04605ea56e1ce3609051f1cc527e815f088ced921de13809c388d8fa976e0d7e5ea2751c15f3d275ab38686bdc077831c31c3ee7bc15f82ee29a841dadfa82677462e48285cc1049d7cf58c3cae213233dd417976af420349e2996afa98fbe944ebefa6c17d94da6a257fe13e87ea3fff1b005208b0b9bea71540df87367e5c17bdcc977478f28353490fe5d0edbe00a7a69160a4ad014bef4a397631581eabf1fda334e167d32de603af88c316f838da29e2cfb1edbc93ee69fde416bb9e7c137241ffd7ba6c867ce54363c842df52ccb3ffba8d59de62759910b8b76598bc34c0afe671fc8d1f4a853c92a8e57f6806a47bbb4b926e87806f1f4bb2d1ebf86597453633df5678bb5e7a6399709d15750bff109644eed9847309da79178df5532e2379a3dde080fbfa117f31bbb37476b407bb00f8580f8573ca5cd8ec039735cbc7aac37796bdc3fe50342e89262169f5f59a1f351b125810987a5a3bc4f200c721d51c61ca4b8bc9e21d1e34534928a687a89d1bc231ad011a7ed18b9b41908"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"16160628a30119d87766d6f4c3a16ac7e6b4682c4c4d147e0ab92b0c1eb6e901","proof":"6a0abe8dd1b6d6b7ca9cfe74640d698de3f1f880f72671545d37d18cb68a2a5e9033b994cc54a27017f19917dceac16fd94a8c5baff86989082f0f287c5c0d50e21b1c85053eac72b990d9da32fedff8478a4a72f8b63f76f9d6d2cee25f62549afa2d41bc520ef4bb939c7e227b10d58ca8b1301a38c4da735ce03e30215a36b1dab91008abe35feb9b1e5b5a1520a40aa8d7478eaa568644444cf73497c506cea8c9ab73cd78649cd5b05ef6ec414784b9515c4ec8b53ff58da53bbeb67301e6d735388491f9076d4a84be2ef9e9d47c9c3122b4bc451ade5cda9b09d4c1003426838f9dc93a9658cc03e39c401487a0b7d00e21b26dfbccb8057bbe80bb0c74486544d267186eb3b2cc8bda41a1b02f42294441cef9d9a70c419b41d08f22725c21ac47b82b43800903717a94a6065ca02f944b791e4679efa3dc9d6bc47c02f81e5cacd7ea02a311b5026aab46ffa8a3a318500c7dfe9d6262516341907cccedb4bdd4edecd6d481c57d5fcf95d173301af1a95cba075c668b4eef05d90bd06f828db8bedd566d8fda75aed50352b89776a594cffa17f8940f7c76b0e716b8d61baa056fbd89a8ff5419f7e7d73af31b2af7693fd460506f512a8fbd9d6e3a9a3bd5d8375cc399687945b699f39aafda10a44f058f98f26139051abfa83014d2de5500370265ac658538c654f8fb2a0d63ca458a082ce3b338d5f14fd21e884047c7da362cb2e310fc357900397727e842c574780d09db95fa5dfed8a865f0faf9d6cc4ecb8ea1a6843bf69cd05736db8f00454435b8f65215f35cf5086f3a9959061e741b46b675cc4873a3c4042106177fdb75549869b7bc5c7a96392d7731f43ff9d80d9aa82447700d12872f14612b06614d981078f9c29e09b93e0f744b48a7e5332b6503683bc42b5ff2b6bc4ec6afb0c6b0de625f9f9931bc7106"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"02db3ecb7bfac2026f0d9ef369d0ccad13432fd13d111157d73ec31a36d0c626","proof":"5057d50e9703b5f5ef76eb55bcbd88530d6d9bfcb0c6feccb1e8220cb9568760b480b53572ead447e7d3c38047b5420b692cfd05c52bea89d04ee65521b7496990a6ef587d746d6f4b9139ed63412a00df1cdd8bab3cb421205b5408b9bb5e6b76bb0f91a2def38900ab8b93d17b1fb1d33cfcea6d346daa27860def67eeaa1e7c1d57b2773766cb1d35068fccef81a5829cd232ccecc29cbd10f46ab08088045c32cc5ba05bd88cd3761278c40d04bcaaa00dce56bf5db4d67c1cab6e6f5608eab6a3cbe93e668dd0702f71901d756f5a5623fbc5b4dbfae0edbf65acce09075a1698b21b64373741d585e38433d33c56516ae5f5ae1c888f9dc04ff86ca503bad9af0920b8520a4e25b795581c60651f7aafd844007148337abe41d5f31256ac862aa2a56a7de9c0fb47573ecef380ee305e71cbdfeb1eeb103341161c2f04b83c275e6bf610f0404b759d34347698fb2d6b2fb3bbf9bbcdbdbe3248d081543cecdc86e5e9283d27b3c6dfa3060dc8c9e0f520f9a31ca6a5710efc4a5ba636d4be7d852c7e2cb25c7eb802d846e3223fdd2248ae8b0c706317d3027955d95d968499418420c2af4f29ad82db25a0071a2337a5ad94e5069b398c08bd3f6a7756b21109aca987bf392b05bb57bcea97da77af8820b4c29fd11f2591feca7c293e2ef27465d10816d45d3b476baa09e33a2191928ca0685adbc9b6f2add14009a8c8e1f96f2cfd5af9c6bea48ff0e8a6159603f10cf72613de27b6f7279a221c0ec70c45571d8d8eadc7b63b0f6f1172399fec48486777c4251fef7f39b2361ece5968d49089fbdd6e44468c4cfb9934b7bc8b8d84f73929d12a6c0c2c1f3724a09377f2be3a43c8c89cd0f9b1996aee5ab07ec3dbfe0a2ad7c3cd3bf398fe04e5fec4bfc249ee89795dd968067dfa794a0e74f726e6354548b0932bb20d670f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2667b240dceb77cdf77838ff4afa97843fdb32b7e5e4a8f3116fb1e88a86f251","proof":"4a00d88e58aa9bcc6c4f34459020eea4cf4aeb1626212ebd8ed80c67e0009e4a847175e033aebcb70f1d16f75b2b81d36f6466db0cfff43fa87a759e6294cf38f8482492316947a6ff0443d8a04c7a093045f169130a9737d38fa46dbb9f336df08e2b64124e83f25afa50ec132dbee9df564e4abadf7c252e2bb7a6cf3fec7cf11b3305d1c27021a904263b8411c80824e806ade627acfc65e80c30259533065b8a045e541572d548467cfc9e8f4551de6125b131ecffdbaae2a913e64e7409fd2effd5317d093230ddfffbdc704b2156ea1de948e83e23adcb685204a79f09c4683f6a0e927f4eecc957beea4bd351045e110ffe73c86f7101abecedbfbc160a5c19589b52da519483131a2fc099688825ba7533fc945556e965b6806d4c7e3e29cac42640af428f4e32cf7c0f0b4b4a4434fd2a4ff1309122c0bf049dbb5bcc6cb11c04869de5be1cc0a410a592afea109f2f6c3726768ec33420b7fc63024ef4568b730a73b35a1872b02571763170dff79c41257e4bdbd0842d0cd6695d322cf01dd0127f9855ef361177f7d0f861896341ec0ea274de450c20f924bc29aaf48a33a233d545177260bf2aecbcf45ff605c7b53d8d49cc4feb55c9ee3e78ae2f551271eb49298cfd56109111c5b4502b352c747bd0240fc44d317da0bb5af8a2a971ba1b912e13353bc2b75c9aa63f08b4d20dada4c3d5ae6d5db6eb806b3ae4c0d360cc2dce3633eb2d9bee16e49cf9f2c895fcb8da9a2f34b0887bec0d5863ca3484cdf29f7309aa6e6261ec6ba5f6d49ddbc0ec59ed6578dfc6d2ed20ca2a24b0677ea1e992ec9c1badff28bf6aea20e108261bd49b0767fdb4412a6d641529ab006eccaaf1dea352d8c08acfad13e7cc8f2976966e4a516644a8d1031c6ecc7284ddeed52d14638c0edae8909db5fbcdb5f78c692398069f2fb71602"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cab29e5585f7cdf16c39ba6b1f196c4fae30da7e597093e75aafebfcdb1c795e","proof":"023ea3592324ba502b5194523867500165cfc04b89d60beb9fcdb4bd8a976433acfef330d4692c177aade19c94827739f097a4d6ddb1eecbff38593026a8ac006eb3f2ddc26096cd3e57a159869bb74f8d8145ab9843fe95e4e1fb4abab8000ef686db0e693ff4a7b20da33dcce7971eb45977a020c1fd9a8d93cef3989c3759e8001bfc69d023bff63835b4f84e6f43ac69ecf21ac89cd2ae6d90b101a3c504b8f55d767c3f8bfc8c5341bdafd2a1064c3f8dd8b7d98a11e5e6b95bc5cde00edb3fc04a752082338bb35e7a529ec325624d6af3986478cf919aca48c570600f0a2cf8d5c05c40299199558e5075281ba791a98d1d26a2b06714604f061ef043d8e625645d11cbb0abfd239a9525c78878c1ff5050939c3bbae934eedf59ab7cb8696df0e9c017d01614349f3cee9c9f9ab0d7dae6ac8e4daa7c11b332877f6dd61d19281a9bfd1adaa11824bf7e2054d7e265a4ee5c26aac53dab27ea826874e860f335503bd98f838623708490d3485c6084520242383b85b9787ce9e5a10d86f75c4346045a8a2b31e84d3d4d4da8b5b7a12f72af41ddd66b87f7cce9407a163840b747576815fdf33ceb04adc342db9fea63f7f6d7efe2f405816a857e3b1c3232178562c704eb369c7292b0c215bc7485f736acefed005291a1cd60cb7a88233e23bff005878b03e82e9bd4c3038a7243bd5fee140d459ef1da472888375aad644a55bf862efbab92080c6eb8c399641aa8dd33747990356b4b7170567b5e4340f0a51e94b862a1c16812fbd1f8ae6a122bbb50915c8eec94f77999ee14ec7a98aeb10f698e6d3b3563f33c136a0af61be2b91085af02c9c063f162a640e7ba9245a9e76af9f02a63da409bbf1410cea926afe33a1c3ea954c5973acc0b76a9ef790c283f75520d83e09bdfbf91b9cc25be7652f5edf8fbacface8b0103"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"32aca7b24f48732e6634a3651ac4ab258c0cfad5b4b63d72ba30333c42df193f","proof":"4c1ce3b94353006f3e183c07e47e64b7e5c4e7774e340b051932b6b407af2d78a4bd4496b32fc430016fc28998a61917d57bc6a184cec04a5e3b60d6942d922eb812b756553f02157f958bf2bb5737b6c7ff727fedd7880d681d706b74ab040e9833b40d2be1f202397271f79f93d14de9a039ed1a2c34d9549f22a4a59e1571c7ffa4f2ad7b2641fac9eb04873f6402488c6b60d7974aad5882acaca0d0a40159b2a60535c351d2b11eb09affbf891eafd3d32861d414e1c5c86038291edc077e3a8a711b537a3911424663079b0263963aec7f7428874578995db0e300da0a3642407bc64492d979b841e912d7c4c7987121b429d6a8dad3db4ec2b9a570406ca5606ac3a94ac2a6e483be9f82fa0c3c71e17c35ace023058c0dcee22855606a45001acd24040e23f09d76d3ca01d6c740175537c6f13e7b576ad9d9e6c823c4d4870d05042b503d81d423578ed31c30a0bf150e7f6f152047c0fae9cea24dfa6cb61734f874257a44357abf44460ede7f78d80f8261fab642a6156867d20de8af4b95ac8cd712e7f887c2aea9217610636f8a1fd728b6a972b6ae698e93207caa1b66aa70483b3e49194c7b4ef608949db65c3874b64bf64b9c74e057ad6a2ed9f098d302f0b24504aa2fa3bedbc0a2db37f2270865898bc3102be6a9b0220a4683b68f6aeb0ad3f5dafab97bc1e810b5ad8da46cb798c45faf7aa518254704dedd58effe495846f39740077ee487b25331110484066b6c947dceecba560e30ebbdc7be5872c2da23d84482e46b5763ac71f3d28efaf5e1d0dd8730a83017a0c98d5bea57bd16036bcb638a003f0f86fdeaff7f8f00d07de9c1e1f3974c0188088b233114b9ee39947ffb94879bb5f2c297b3d968c5647614bd41df7a800d79f8edd8e1d6d0a81438a1f9e5aeec5d9cdafaf873ce94f79b97b633369dd906"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"183a03edd98ad20c2a95763921faa33c81397e182617b87b359c88683e94c434","proof":"5006eb3d5be7466167a196f3c33236dd66ec1f818cddcbd3c0feccf1f5f3af25d8c90c98eae2602df4cd1f1e9a6e1e7429ac3cd8aa78f73bb7f1e0954b59892fd6a4602c65b9b35df9cee55505599bf1ae68a1ff1a701040022c9c1e86539c4c32f672613a2a30ae382e388fb0c4b5e7fac57f094cfb77cb865c0ea70cbb4d0be56b81aea0651f8891f9644e4ebe4ad5a1f19e599bbe700c00f2ecb5d08ad7079464e2a9edee133fdd0ff94d92917508a5944138c1bebe4207b1756f9d6bc70298578290cae85506d4f716edfc68926313a961d5c00fa1224bd6774ee070b403f6afff4810b60b9ff54cac5ecff7aacdb42556041d9e690f17cb6cda494b2a2602c0ab0b998b0bdab151970d8e413886d65b8f64a9ebfb8081acfb1da4f34d44922b96396bbf2489e47874e7a5813689e203581beaf3bae006cea7dc3c2cfc3bc8b34ccf48698b407e95ba751fd5fddc8cc22c57458a6c1cd3dc7995885708009a47a39ebba00b5cebaa6b458ace8919a9a253cbecf241152fe45707bd7af619f63fa628bb73c236b861f60bb88fa9dd5ea2ffb10b4cb852e432df14abd4384398cb6f2e160b9b3bc7890368f09a3487e924dc59a788ac1396fd170db2545479fed972b164b33975cecc5c6d8fdb88004b50de1031760d8569e11cd15649a974a89baa1c488a8c7478b0af7ea7c2b776ab3ef78a113a4be3e6be35adf6231717e24954605e4fa5d5ed717034043319ed0c301ff7d635f8d8121b57aa96e1b76f70244ea7733d03a33b5f6be1ddfea0f05b62671a3c7ebd3160685bb9cf9cb12dd4c4dcc2e4047f2f9dc7b96c1d0e78e58796c639bad94447d14d6beee7d7b276874769feeca7b387e76b790a078af57956f84ecaf9d633f41d3717754336d407d330c9069178c9168f69a0a59dd654791b4a0620686d321c26ed4cc42e829909"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7e8f371acbb2203ad7fe38a130d0c581ed5aeb713d14aec55d91e69408d9c514","proof":"5a4d32d72ae81c1e0b9b43b744da2e80e425bd443171f565afc6eb24cf12ed55621ac8ba30456496f04805d1574f4e66d5328fade26ebf90d7b386b477e1262f10020b66167bd806e1da9fd81e60ae8e0e178501227f24089b08421933995204d6ff65d89e1e9ef91d190c829544736b30a72461b00085a827034e4d077f4867bbbd61d378c2b7a6ab5717dcb2544a41b272183f076d4cf2de4b2856a024000ee6131baf52e032717050a65478588a2d12bab24b8141bbb74422ec77fecf6f024bdfb3eb9d8d199e6dc7089dab9467ab558deb712062d117c53b6709d35495008e65615ac2b78a5ed094088da17808a11dd27be70b1c0efc22913f45705ba42f9410aaa33b6e8dbfa417b6641c73fc8e0e6df679cef347a90904bcd421d09b146ac8c006d9a6ac540a397b85a7df4b401567b81a4dbefbc07bb5b8d0cf0de4360c006d5359e9b4aa571620d790ee7114622898ac3e876045526ee4f86af7f53fc26897aa4ae34a5c2555eb5a1ecdb7589f7cd62adc5c441afee0a694f01c1b4decb6337a9a5bb45e5a2b86bd71095b2a75b9ba8bf3719e995acc51c307e3b92ddc707310cfb57ac93e50b1dd916e7cf4846df4af3968e2fd0d6ccc976f2d8d3f2af93575ddc21c62730e7cbda95fea4d50c4f399a8a0015ec916c63da7f31f7202edd4ce24a0f853a7acbba99e01006c9a20e7a62b6e09a98902620541df6c273ee062110c12addbf19b872c2ae36fd2836441b940701b16ff53616d681fe11108301c61a0a030208061a7d39f5612b67f127b789baaf4f02e29277c65bda950a62c629a9153138a00ad4c9befd09abd35594ca9a2587230fdb7aeaf9e1df046f966844a2e5d3ceb46d1ac16aad10080ff560b536e5712608c2bf5c036f85e0efe98fdebbded5327cc1670be8fc1909be12c7d9ee98f18e3dba5bb04e4c2fe04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"52828bc935d101813dad48c6dba56a31fbd75afec5c4fbdf2fd9f01d68f93944","proof":"e85678e542223f6dc4a31f1f7d3adb3ddaf86f5511ef1737be60758e88c5352946e81e87159f32f5d10706a8deb7ef2e1ac727b90db705c87b57bce278b0b835d27f9f43c77749e464d3f9b98e03d63912d1d890b4eb3c7471d4335cbbe12a25bced036f69e969cd82a080d9b732cbe37aac8072c205cba1978655f6cd09b87f357a5eeed92dfd624c6f63c428a85ac8f7bdb9f5db69f84e395607fea6322b0e43eb40b1d7e4772e85b2f30804e56912b173d4181d358bb1b2856fe26a0e190046f6b5aad709036b43e9f7157dfd43c67927a3d8bf21786abea10f5b2073b5084e9da0b674ed4f5bc9c8cf130268dd69a5b841883de8452e3d194292879fd5535a23f0ca3e85f0b3198e869b85a0f4946cea19d256e11e3329acb79f7a61742c34ebf5f315eb299d339784b46029537232ef91cd61409471dcee20ced8cab85556187e036270b0116252da0402f7f81575a51ed99ec1c1efb208a2ba1ba37d3438dcad00b6c20f462545cc84cdfcca0243616f18aee981cde2a9df829f0b0764647ffb94c32a4463459fed2715b63bf90f23c5b6be19b19c7376d4ca00877f2f16313bf72cb71286fc35221136e19054592ca420d18576dd6c2af8180782370678c80f7a1f35111de04b2f547b84ddd552554e70e7a9942b13a964b736dc7d56642cc30242e83639fe649c78a35ea7932411b65939643bbe92b298a918459b545425a45bf0b793067669bcbe2e2db73e7e324104fcd1a6d2707ce802d8042216e071a5f140bf371b572e2c5cb4004a09118c2afd0be8713e2d46af72707791025e98068ad5f3e227a724c0bb7ffca2194d60bc319ad901c9a0200961d34058293606b25acb75bed09aabcbed4dad2d06f487be8268dc5a374d7e4aa6df53650392da90baa68b991a3d8d4083a35f491c338922edff42e0402c4bde1f2028c806"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1a6373dd7cb81586700789ec8c4d1a2df41a2a5862b500342b9395d5c8684222","proof":"80d830211ba9361d48dc1aac7091ea58486c50205da11cdf5c712c1256211f1ffa79399d23546707d85901e2c3e5e6cc23ef2510a5ae9ad4438535f7e5d50f7032467812e2ae2c27128e13816f3cb833f45e340dcb908f07148318633c2cf21852220adfa9ab12548d8b0176062c73f587e38f66194828c390a200b7e2d995207003b38cfc70b41c0ceaf6291e815ba2881ceabee4e1ddbe7222f25f582bef09e1799edd62b92b42fd96404fa96c50c86868b5c21b1bd2cc609565a448302302db2871cbc326b1ecf8941e4fbb1c225ea3a845e496306ed447c430cd82fa11032e7f576c186e4c897a156aaa3fe6d201841da59b5de02756b1547e08b1b5d905145f71d64903d377b399248f04599c23d63965a99d7d19398eebba68a7d8250898971d12de814e3214803dd072fabec96a1a68644f3257c3f21fdc357449bb6e7479efe6479557c33d5c6b5552fa7594dab0bd58ce07a5955ba7582a66afdf1a82dbf19745d17ed181015c02ad970aed155195176bfa4505c34b95a99a189149349d97e8775444cd358723f11a27100eb0fa5826fcfa1ae798b6325d7335675f3e8cf3822da67dd2c8c9f11e6e71d77e1e91feb12b2ea081671ee999fca2df0c8280bc529fe1c4cf7cff281d40dcc62bfa1ef0a49bb8d13e85c70e21e318ed6e0e057918b895fee76c81a08bba400904836333fe2e1336d076e3bf241a2d484c9c63ea543b089e48466274be3989a41c59e746529471ff719d1ce48ebbe3db7ad4d62587add925167cbcb148709196aa3d188307d351a2c9df299d9bb02f100bf2a024cf69359202108d1f46f39b7643b9d9ebca8eb78cd3af58444529bbdb1fd9a35a1e0984b9803dd4f4b51daec1ca9f537aa901dbdd12b682022979b3c80b4fca1e4cc44e92cc44c4be9a6881e514041b53aea987f19c6fa200abb9e94c05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ae67dbbd59898e74c8388abd64184fa51403b6736b06aeffc3dd0d4482d48856","proof":"20ff74ee3adaa2db863f7293be30ee19665762848c0c8a64de9ea3242aa9422bcedcfbf4b4de95780acaac3a359b116249342ca8583ebdd87c8c8404f846f322e0e539fdb966e2c9c153244a445f8d1597ba6dd78dbef09dddc0bc7cb1b6ba7dae0b5d4e0bb8d968a8450f6848bb393dbbdbfe2621f8b532547daae23b72490e42ac4254ccab30b8f614f9dcd869352b58b41941aa4e1a3726f6eb630695b80b7d8798035713d12ec765bfe36bf81f1b7231e09a39569d6ab52acbc4dd10ff0c413f21fc4f6d520155b97016d66bb6a603df8d10a543c29970caa8d535c8200c1ab955637ee52331ceacd782f993954ebcc159e36557e6bd8db56e47d33af96bc2d13da677a21442a91f5aa0887e4bc5f4dc0f0dae6626a93fda556689ca45739c321d5da3bca470ad575b1af4b074c0dc7af8a0f1211795233e890d39367e1f82b5684952f2b221f886a4d6c83c84136566b5c6e5ff8b171615206ddb8972128403faefac701cfb17003153f9ebc629d17ddaa4d1f7c73f2657fa095a37f81d3a936e6331364e27e189548c350ae379def210eec3b7905d73a77e4f1397b312a02bf986f6410e9e05744a16adeb132ac609d2db193a6dd6112c295aa674c14abc19344866709533f27db6137c060b12f38b7994fb0efdb2c3f611d52e4f1022489b2571691ba878df716dabaef15dc6dd560a61aa3e4afacd4d25aab2aec34aa2514bf989c18dc8060e25932d908a35cd42672d12cc3bc7f6d9b6b7c693fa7fa8046ec5ac3e0765fe3202a85fd794c3907b5b79766c829319b6fc377c86f541969aeabef14f69ee22eae9ca6bee97796bd390f37497a2ccde09febc9827965dbb8a1f1c873c754fff77faeddeba47c776c2787b13aaefffe4478593338e490f4a8ab9e8001b3143c2588173bd57a425c0266781bc68f06dd641c3528badb905"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3e52d7e2bfdb5250735e25edebd7ac677fe889793d309eab0d69517e6efe2048","proof":"96d7ae5073187261abadae3b392f2cd5760ed2b7ca0f1c8daadbb0d32bd75c4b78cf5085096c67421b212157e3fef180a2d67987f1a995903e00fd6cd12cf71ba8c0dbd91d6dc1fb4d9fbb1d3497e6130ff941fc62861782b13f9991320a84204a67a292b8ab9d83db62b9740ad43eb1ecce4096c161d47110e83a4257283c618852f1c542a0066a79b22982bdbf91897a6417f2a369871a5b307a296f7f250209f3b589321cb0fb9d7b09e6c0871ccb7aa83ae31cbbeb509d2d3f3653ff760066beff14942d1fb78057c88fb5fa69630303d4cd39c6dd585a9141e566f9f500782aa93898393316cf4dfabe913e9293eedac21f81fa59e5c19cd47db0d49e29f06ccf31e63f8e6ae1b710552705f294d842cb59f25ec5a150c822bc7212f8530a29722dd66e273db1e8d6b80aaf33eefeee0210ca0829047586bd5bd7a0147df00a1502bd236acfbe23b45429bc894e926aaa80d1b8570ecf1c3c1ac4133077f4e5177c0c8f1f6b1a987ea785560d83e26a064e1d08094cc025cb56e3205739bc23433fd8b6d4c12ee7f03170d97676cdf62395135bedc0144c6e3f95b8a820781de83313beb58995b5742bead05eab78cce01855ea15341ea49e80da3a73215461a4ee1fcde80677844d9baed38d52dbdac5b831793305df7b27b44758e050500b90c4d588fda1016a2c9a966888699553bccb3ed6950e112a15121d36152ba8e1cc0be141f95a7120b87a92e46965b74c6dfc6d7dae4179a630beaf866f317e36e0cfe4613675b5a645b9a42759d857846d3f9bb16a5ea50bbd5bbd3abf2428f824bbf132e5456b0d9c774b2580cb7d9b96b132e3257656c432293566d849f18d8ccd247a0a65dea21083082501bd2f3e307e7c920c7edf4b712500a13601615f1a5a9493e2d95e4795da58b5105dd76441b2b1c2b76e9e30722841878c08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0404b8cd0c368b30cc14727163515deab979b0dbb6b292266f4aed5bfa24775b","proof":"424ab40c09a6471f1e098e1df3b450567eddf31b1d158e16f9c6061e0ff2af73142e6d51241f9543bbf15c6a07c21ec76e57045ddaca7eec46b5558fc5705b02900e9e2e6df61743698e6b47e2aab37053857c8c6fdc8b3ca38147ce851d763b0ab2c46653fb687243bf4f3884431042f26db69c4a970e77da546d07907fb33c632edcd0238a8c094d70912e7e460985fd19b46eae298d011d8d3614d29b7604b77d60f55c6d24e583e3ac865a3ddf99e432c750b190d39dd96b6abbe9c1790216b9772eeb28bee7a9cfc6ab700185e0584ae8dd549147712e019f855ac6a008089bd1826e329235b344346f9149934466bb29e8b17d9da9f34498a5d7520928380aea6d6b4a90f20cadb963f20396f5985e603f8a8bb5845d6492804e765d184c52257633e68b993f71def31eb227cfe886d9ad2ff3b08bb256a1ea17ed1569402b9229a2c19e3eb7b607c0c752fea3fd755d97c6cc404a1da3db1ce6004801c2b470afe2bb901b0480412f9d42c850e36a4150cbe21c7c065bc5d1949f7276521eea9a990d7bb38be5a29510f6c239a91c3c1d4cf3140242e7a08c775a535cbe371f34a20420bf9c93ebb18ea9d18ff55b802b66d838e79c7cec34c655fe6bdcf1713bf10468d9be0f06768c017a17e87602fa4414e67c413e34ab049cc812627140d134ce26139b7ccc24fafa661967e2eb176515b3a31cbd8d60b172bf330cf1eea10c9b2881a60ecabab9ea356160759eca99e28aee78f34f2c444601523e3462eee31c15f0b6657c94a5467691adff3382ccc7d36dbbe473b5dc68e423c0835817a2fbe0f822a8c4252d39b3bab112d368d04012320170a79ecc052b3121d46cab90fae77093d8cbc09f5270b24b620107d87f6b51eb4c7f94ebd1dd0a4fcf00d69037e52cc799c695a43710035a953bbdea68b864232ba70774ae300e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3a017e8bdff2271fe32d50f33b59f1f26762ac65e7ea2a1a2ffe9a0d12ff556e","proof":"e09805a0800cc6c502788388a79d2c2a2f4adea7c8c38e1b37997e329b508456c2e5d1c9ed41e7892e1581d897476909fe6550db83f57b1bef81958af1f14e12b2cc654bce1288efc7d81dbe4bc09b969176731ce1e0b9c159725dd26f2d204bc677c8e299f72b605622b2f5120d4298f09dc49a7c05061e9295b3745d82a46f7316238d54d725d940f4b906c2ddb89612eef790915adf97bc72c8888ed8160f67f289e2b15b519bea0e11725e1caf7344b65759e18475efed2df0ba50626309c4100fcc5885e3ff16ab86579d37d23545d1332a717f4ef576c23437fca2840c24430aaf420a6c5c70f6abdd5d7d2d16c649f8fe6afba385d614027543d3432762d7c8642f64a8be0674ae5e6ff0c561729a20e9378d64de5f7521cd6739545eeaa39fcda13329ddae20ca38dee975024ae4089f8e54ada7b65f73cf8f87453496e200b511aed92d1996df440895994b958aab1df28d53a6149145afb4521b32f053ad8bb2d083fbd0d00b660cc937698ec07c2664e2f82880fc513fa97b9548a0c721c2ea65979d64517668c38bf9412a244e990c139594aa1fd906de61255f30321d329957fcfd130e3b339e1fae1dd1580c2f9c38879b5aa150035375d26714ebfe6801b6f3b2f9636a7a2c4e1cc4e02b4d5cc728b8bad046547508682a73ea80399aec9797b9473ceae6de079e3fccdc473621787b7fd6c9a337fa8e2d7aae20e8cdd7d3ddbaf6650254d1cc6abfedb32ad938b4bdbea01e34894b6caf15ac68f00fc108e34fb02d2017e1978ef3658b6b5d0b063ca2d2204d217b1bdd15660063a9b5cc9be48ef67e5ea8cacf6f0eb0073887a87bdf24cc446364772c1798f024894c255b3f7de91951bd5121de938bfd29e69893282a330e13dabda40faa4643f73b3795785e43149037089d13ae7e2116e67ae9a37ac7690eb1b4c306"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b0c7ffc7a95e8474308560ffdd5bf88c5030a3a6839f3008ecc8f4b44838fe1f","proof":"289cfe3f30a0445db9ee8b8cae1f6d4361d1755d1323dc1301c5f9a48b1af30c9608757ad0a33d5f3c38170978270f8b6c95dd3e6cd5b2f381dab24742acbb0b5206c4bbd9e9f1cecfe5c1697bbc76babb5dd95b3d80d2ecc5e460c59462d76f527ca2d233616694470feacec65d7a5226271c648e3d581be0d01f292248104103db106b4a4ccf4e9d191335a19344c54fbd309d07ca7d5c89de33f8a785610ca1397f5725e97774c5c189fdc4772ce6d75979859c3044ea1b1f8276014372036160cd25c536f49641ab634033951f31566f040357d3075427d1c6fa2600890bf82243cc841f170394478168b80dfa642285b7b4d05c7365fde1ddcf7483035b9ef3c032d6faa230476c98bf9d749b8185c99ca21f712db7889e7f92a73fcd6b222c0b65eedd0bf989d9134c4544cee06f991aa5d41d46af9e05cba61f1b2e4ed068630a82aa1ab502db9c005f556a4a6eef4359cf05430afb669c5b7750dd20ecba643906e42e1caa5c072a12fe029034bf6b4ac5dab4991d0e1f5709519f298a80095750f49d5ff7fa9fbcbc5e82427c3850ca56d50c301e32880309d72b3abee2a7d9cfc5b4339eecfb8badeed723f6a9ae1f699efa6f2ebe42fc793e416c4a784479c579e1f359c51f30860203ed526df8fc1c3fdaebc7a91f086e09ba4c1c0fdd2b23b86d0dba866c46e85bd85aab8e23f4bc489413e51a1bb9a4cef414a286dbd0c3f70f38dc5125ca7153a37d7d47e82611adf388014c02f7ab8d3816e415b3387a4d3f4c962ceeab1a7d4a25436dc21478518ca30b16c2b45bdee505eaa2bc434e75ebe73c2b1f2d797f41fa18599c5366b12d688a7941fe1b3315620e43893604e7209660779d655e50d4a7e486ad3876d7f3d1de708630bbeaa708bca6f3c708cc21f64a00d7a139658bd5d5a97e4459facc956bd305cf51eb8e04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8e0c8bea01addb179b3ca89691993885cd06689cce822cf947319c3b518fe07a","proof":"2e16654d53aeb8e6ae9c4b988dbf9e185488e92b175810618e2f6841f2ede60ed89c161b77f354bd9940aab63def10f08672df47f1ae7606b3a0d92c02247a19ec5efa0e87dec5209d9c1cea7e7c8ecdb370248703ef044211a04c0bd0176b153c9b9d9e5a598438bce7e8af7aa7f87433f08cbc7620a2c820285a75bf733d4e95740881437696b65d5b378931221813c56b07beb6c696a7c228695e82e52a017f193d984f08e3eaad3b2b6ea367516640c201d6f50275128ec38cee0e193d0f29b66da64563906bc1c3aec907ce382b7b35b8bc7eb4e685c813a6567f45ea0bf43426381f7b7505f296ca5c8cfbeffb2fdafa5aefbb5d282e8c532b3ec6c51e6cf4e85661d4b04cb2c288f30b7b50059955f6458467f5d6d44507e602730267fa9ebe952636482cfa652a2fcc72a0811f1dd9ef8851a9480225a323973b072a94ad5ec145b93fa89903fb3255021631d728abda08ddc98950dc294dd9960939fca92d3af051c58763ed602f791bf5ac7f1e75821674d0c639b34cd4413a5c106028e415f8964f5b5ce8f7454f986dd61ee005ba83072eae60937e84e36f971924522cc1c322fcf380cfa37743060a0c969baa5d627a285a22ab2dfa60ef6b00fe3cf76ccf1954db74e760725401c44075d93218029d16343f9bb932fcc41f3766c93b93c3daaaa0c2b282caea5c388aecddba019e70268ece1f116997946933beb9efe9acffcc82dffa1c817bf7f5a93712cc7c68ce565000a7530e84ea0f7c4abf18a623caf5b549cc1598d77187c48cee10da1eee0d011229d3ec6783277c20a8e40c5eb04f38a5db08428b3bd48821ce95484bc44100f7cca6cec782b74ed58c18f877e5f5ec4cd7bbdab2ea87d527f369465d1297491797e05aa4d6da0bde12823bf62ecaa4fcf70c8f7445dd16870f6571d64ec5260b3538b794bc4507"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9ad3f27679fce386116421ee495c4c3e196c45050993f49b0f04deeef48bf443","proof":"54d0eb1d6616a103b24570b253f3d9fb293af999e8d2c2417aa7195693dfd54334432319ecac7cb738c48523db65994b6898044f630bbb5cdfcc04e481d7632ede43fe360ab4c2a937549cab6afbe6e5547ede330c5376540ba8893e592227032601fde83df8881c1b29e6afa7abda323a234d6eded0d7ec0e65140fb21cb559dd182ccb041b581af4fd92ae390668d6cc1596bca0d382cfc6887f743dcfd8014565ab8735ac403da4bf2741ebee7419a16765ad6c537a70ede04509ceea620d8d5d62ca8d6c3c5016ba8775470fb0b305148d54d85d29011c9f768266fbc20b5057952c7586838c2ad0e8562462d4ae2f353cdd1520250984f35fd3e2b70c31421682488583211b17faa4fa883b74aa7c37c2230cdf5ec283860082d0daf71d42587384713916895c9c97010f8f01f7c7c79d9f6a469d8a239f5bc87e5f717a3a62eec312ed6e73d10ce4945c2273f6dca53753a6068e9ab98e0f4408db0d41480d3fac375423a366c8760c0a8b97f03b921d63efcd046cf1eb138eec84e85714eeea0303a3d066719db115e7625ad26544a36e19954c3a2c25dfe21b61be0802f5e4c310755a7cc49e6bbd1725d031bd650149955074f1d9cf51b6e723185e625f0f9f2761ee613fa10220da376711267d6914752e6d28a78760239ccbfa052ad7fe201debd1f011f79aaa880d3545e748e88cd194962eb241992835db356eeeb7ddce8b8d78f1031ce86596093635c17332f562b92d73f4a0bfa74e5d13532433b06877bc16d89cadf7e233b24b43d50c560d8ddce078f4a22b70ab03ac0776717abe5916115e94d228f3e47e0459a26fe764a3bff8e3f41ed36214f4171815366e9f7dffb3eedec1bbad3405684c439fb19093731af4481cf702dd725905a21280cbe7cf42f8c0dd6d9fef9e3ad10db1e2d56967502f2160f3a7895f080b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e0e513221582f79967d5bcbb97fa8b52f898173e6d0739d32323e927b1db6a6c","proof":"fc0e92cbdad191b5983c49d318fb91feef804d7817640343739a3b2de7edbc2d9accf7ada0390660310ecc0512e512affc49f3a4fe3bc8fafcd494a032f14311d066a589029504a347bee80f03433420736c507b5172874a57a7645e16d3632a04362ee2650316b6ddb352079b271aeb06f336366af4302c539b7ecf0c200d7b11d1388633609138f3a1aa0ff653fe2a59762a897e954f0026d62241b0103c0345dd4ae9cabac7b8114c2fb453a0093c67add88f17eb7715b07df67281f0590a9c649e6a5ed57d03ba674c5e4cc3e5959a8aa706e901950c9fd13dfefcc59b0b443e3856684a2e9e248fafa3645534df35dc8fd8a12f1df30efd86ccf882920c5a494cbba26bb407d6ac99fd2245c70d4387e2bd27f0f4e9ad3de150c2389f78508bcb101562b735cacb94a4607d95fe8aabd73c30aed699df437e724b582f755a7f6aa35b8bd399787f04bcdd452665be5920a79c952a675de80029991d0b29daffe66301b0bf8a480994b561c36eae68e27e32b78fb27ecf546762d4fba24ea8719463811ede7bbc0a6711740911557d8a647b165c5148d4c7bb621bda6c06ccde34153ac1da3e6821d670edabde38db000d9135cc36f2c2b0bd9b2278e956ac48b8e181d90bd5636b6f460f6206cff5d60b934dc496b9d7167aeee0c36363a26740ee7fe02d109f2520ebf30a4897db71079b8fa5bf78e2e7a34622d37f1738d2c6306cc04d392eb3d0a4dc0cdd853cc9aa2129a4a637cf8fa2261ea66d59b656becefe52b93f9a6f3776e9d8c4a05182b5cdf53515f9d3da4d8b66e9132644c7219d549ee350bb1a2bc52b26c60101f78321963a121c6ccb338bb9fcdc597ca46f8872c9ccbb8a75e9646e5e99d7d9c7c8fd99b9ca22235817899a68cc0c4d226c3a0ea137c6fb73806367dfa922fb5ebac0a216b00372e9c7091f53d10e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"de422e4918e1e741b129326701f7000e8f1ade736747e8099c641ead01921b14","proof":"f0a909ac4964511de3c0d3a43d7210fa43f1249aafe56cb603f3ca848078f36d1077525c148e81268210f82f5b603d692590e09d2d46b002be817b98d20d7f145ce4a4cf884011436033bf0a59e07fb7e108a04f615e70988d95b6a652713502d847bb569983a83b1b76adc5af5af948581bb29226c3251ab33b333628ad902fb2d41f7f2d647b16f3b33007a14cb075561cccd73e40f4d0cf30f671f375aa065125c5506a59dbdcc82815f629d4315c53e875a5a70a3d9aebae58f8d8887d0152b587bcdd38f68811483c2e0bca9929200b6c869ae6df0325d44e790823be00befe3b8e4946641508ff1723a979ee2cca65dfdde398610bc142acdca279a65294f9188bd9ca94bbc5bf5a61f142f51208b40d7732da5581f6f82ad39e62127bface54dadb82303bf2a9291bf46b1f9ce1e9a780030f6fdb08fdcbfab57ee7518c3751cff0cc361e0bac5f9bf9b454aa7be1bb44632bf46668c16c739125d45d1ab765fe3a1cd5b2b6079aee14550c3dfa443546c073104b09cd30aacd767408ba2631d403a65dec7ffe56387300839d08a3d5a3dc477380fcab46ddfbdd496126dd195d3915a7f73795450111d6785afd003eb0410c0533f299bbb3b6b2882378c6f08718230971f5fa9fff90e04a9b68951931421e7765aeb128157fb9fe11b41e6a8e0b6ef78798cadd1b648c40be7d897eee3df7f31b6022dccc43ad1653de2fba0812f1f9a42bddc33c6720774aa930c89c3bec703fb2fbcd908787e85c828a98a9dd6c9ab705bd9fece063adf02fa07884b8a7d1a53cda6eed894bbd73101a300389e2c39034258fcaf4373e89b02e24a3afc388b6d6de07c1d56a896594a2162ed5f177be08b29882bc585c9b55a8e761df501dab2b1ddaca4463cb0ce1d0c92f2875f7427bb9dc7c0a85b8d0d58b8bab944556bbf0507e69d799f30a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bc842528bac3db23465274d645c17eec747a057ebe35d462bca05c4853171b07","proof":"ee988d032b7e82427628ddb2312e2c1cd9fb53a89becdb1029748a6ac2837571346719f5e860b0835008e9e6f74b31f05ae208de0a93c86647acc4cd5d70ee151ab894a4c25ff714554f14d74f05cf3a3e6c1a397390d5a21d6da5af9879372a3e2b5ab63390a0387e1c62ae379c8cc4657325ecdcb68be3518a5329699b07407a9c4cbc9ee83e2f209d418dbf618d3695e639399d5ab3731f9df6f406c8ac05d60571eae398ba917ce6ca20d75a686dc8a3478635df7a5a89f65506e9d6670987c8a5d874a3c38a3bb6c25f9f1b322995c5b69e322e946e42263424375546024e48c3b042498f3622f710eed81fcf355f94ae1124e1e2c9eb66a15db769054d36495992704175bb8e8f630a01a90426ceee551a1daec52dcfcdaf5dbaf2c64b6035ed442202ea685715c1d679528ece66fc6d5d9bc0e9f0f3010a18dbf3ad153ee8cfee1d748b5b915511e85d8e219e6489a05a2ea4ab57657b60a5ed2f850b9aad1df817552ea20e07e3082fcd4f2f853a63f3ab22377ff31ced1fb89c517f92d4f5839c28dfead6d9093d215468250d3b246a2b656c7cd52636168f285403eab9e8ea36fc6d02bdc0a6c7293aa7ffc8b965efe9853302c80fb5fa179ebc3fd4833bef34de458bf7a90fc2a56a48d9c641ddff7815512f2e55c9ae5880386732767448b679981614bf85994c317f952897869c519e79c9b6937bf8b9196c7c2a5339e5400db582ce0f4f783e059d8251d4f2a65522c78c0e707ad53989f22b5cef9d7dd67a03b86a0f1b0d3b7330e6273050f16b6fd9c8a0bc0e241f2b5671a25fcbb734e0802091120df230a3ab6c53357c22a50aff8cbd3a0ff3adfb141010a121215d0f8095fb00f5b68266fdf4a99e6ab2c23418a548cdad784706390b241d79be27aa758871aaa632ce8acefad0f0162352b14a42d5d2fd8a29589307"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b2bb9f6c7e3b3f6fc2131ee61ddc91245e670a7a47902b35c7c0958e4100bc78","proof":"bcf9fc74f1dd3189e4030111af41f2eaed8c4739c1e41eadf2634ef89769dd30a44792c07ab473456b46c2308d0d46bc5cb79ecabded95df348143b39af0c25e68368b38d4b5201fffdccc7f3c12d640f5dbf1767d22b43bf84cb9d22746277ec893846d1f7764be839258c832a20a0d4fceddac6d75da9396260a35b3f1c350cc141f3dbf6ced060b25580e754142e3a777b7bfcae6e56270fd1379b5c0ee06ae456071da3cce1358bd1f9b794dedd42dd8cae8ec62e4b556f5f87c6fa6f10324886f00b17e83c682f8ad45a79287960b7eb8e2c9f06d67f5e2076dc5c5400d0af7143c051b4116bc8cb68b6ab8f805a9b7ed5110f088184b69a8e2738ef01368be3c395eb9f2440d4f966f868b28bbf03d64f2f6d6107ee90dc76ef0d69e372eb405da8c2ba3179e6993eaa0d7bc4b8317d2a5cfa3c9598001ddd5fbf78e65c8aad6da1f4ef799576bacbe41a4b2f4274632978219bc13d16104f36723f3412022df1e19eaf26271385992be4b0843d315d1a58aa9871e3cb3b8030831701996fa597f4539b37c387122da96236351aa981e4d60b600241399c250b989e2359ab912b9adf8682defd2306cbf2bd6309cf5dfd558090b21a4686c72d247cb2006b3ee78a14ff942b8f9312e0cc5bd47dbd8223eb1b4d77c7003efd9dd2c722b0872b7a416bbfffcb6101460aae6bde4072e55df43ee89bc18245a43576ca56a76eaead4fa8f16173579ea9d850ecb1c57c6f6ed93c081ab7ea310d3b8595e7bb01579eb69a8b4e02e8d5a1f744c54530785302bffd0c57d313b64489573c83cd8c1a4582cf8e3fcdd270bf35044062cf5a5ea4a0d978885a91a471ad88da35fde7079cbedb5ad52244c52bbc9d727a25ee576f6cbb7621075f612600a0f47082da9cef2260c4dd841806e4f9f30f3e81ed15dee0a14bc57d5aafd4b2a511f0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"880c10b001c95ba104094e98fb565bc06c21f90bd377b016a7f056b2488cb048","proof":"464407faf1468c6725793ca66e79707dd687c340eb26a1c3a225a8577157157a2c567d18380bda9a641ab21c042aeff200d9a5cc7a7d871a5eaac047b20ffa55b8c5a12fa2111b9bd4c016d4a8d6ffc2aa700703a5925f390990eb7367356241484d12085f0a7284544e8c106d8d31167c64cd231a50a796b90bf932ff94f176cb3d5c4c2278ca039f6bde2cc06e6116d323a34369348b57d3168d6a1b4e75027e1a081c367c644401abdc2b513ea85e74616fd40eae75008177d444d41c070db50553a6e31325f3cea3bc1ae2cfb46a91692fd8e2c41692649c758e8edf0f0b3ab0824f7677cbaa28e81c9269d8bcdbe7f1a3245501c9a9f3b3d8ee3a1a9476aef0fcc6b8393586b4c280bd3f3665f57d55e0ef585d4db469b8585f30c43f630657ed94d8c7909e6648ad33f984f627a7cd208ded3b82441c76a2bab813692f28b31699c0627d12f8663a21aa395353b601826e5efadced92d09bf8807cb272f46ce6a1cca795f27c4fe926d62bdc18ce3098e2dbda50b8bf24761336cdab0bcafa6c0b2f8d00cde909993c0c6994af93102758044f617b9a71e0aade1fba10d85ca79169d41cc5e76bfdbca2f7cb6f2bd4014dff429540128f88817a011a6a4097eac028ee46c3e0e1f150c29ca5d3fdb401e42e187a4874d96d0045aa2c16b45ba904afc05837d6fea5ad82b737a73e2db34acbfc91dc2f2428677987a04356c02623d9265678dccde0f6361c8ceb4458a1c97e50531118b3c94a7bee9e2ab4bfebde13c7af83d04c6ac1b4a2d51e7e15955e8021f39c4f1733af4ed81b70522f5910621e6fc1e1ebce11af3a68ec1c1afd8266b862383f86fd87fee0391a9af8deb71510cbf69e38b3b4c9be4ec608045d6fc10aa54cefaf06839dc67104f1c37de6d43daad69f53fd60ff35937089b5e96661a7536c770b549093d24507"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7071d637802cc21de5b0a7918e80b6f6bb27635ce104226116abb16a6ed36026","proof":"1a45694906bd2fed688841da53d407e958e4dfb11a0ac6cc25e698808a2c376d52dd0cad283967636ecce5ab029da617eaf22e26a774101a88a01fb701bca541b8d512a98688838f796ec5b393699f9a72e13aa0c20c5d9cd8f06f34e646bc296816c9be379030e5f1998a2aa26a876248f820ff07fbe062d0e95502aaee3f220f113f85c3b1015f4b2b1a8a4ed26eb5434c7500301a0944880a97d50645d405684e63d482015527b2a288859902288b27dfe016c11f27488c42fee711475d02809a9341fdf918986831ecd84553bd0184fb891eb0471a092f825945c1a432045c70c3ac29984c6f2b98263d6b2725a6d34bd8f404d2818ec01e0bf4d9daa629683d9141a78aa1f6ecaf0591c3972a6e30c0329c509665ee2d8f2c5144fa592c7ad259b6f764cbfc9c40856afba441d7bce6ef3679939e537253e8f923abb51cd653f7b531aec710e9b4773d8605fbb230729a70dda53f286a0964152a1891775e2c85bb32bdd4fa6127e09bca6ebe8dc39b3355daf55280e0387f31b5d3945a3231725e72e77ea48799bcb30b55ae44f8afeeb723b59c65c655af0ac0766714c0d792aab31da97840e64b10077c98760d40f89b25d5de51fb15a93ae37def0494ab7b5abe3d79b1b57654c7d710e171f93a62474279231ce7e1e665e6322d3068a88f5a743cc27d70ebddd75f02be323785e531ae4b6a0d8ab641c532d844301cb1a9cfdc9f3c8844dc59070bdab157d5bc37c272216c7b12b2abea0e224e380ad5bd6e079b1064ce825efca7d32e1d2cb045ad7e8c24af734a6e8c8dbc534f42d37e06069abb692f5925a328f7fb6f1159a76dd21e63b54572b4354a962c6d6fe2bb2b2d07f6aa77b6d36b7382a926bf7494c38b94e817b3e8f6ae0349c50bc4f21616764a323e54c3a9857bfe817338b299e7fcf6ea6016b1aa174d08c30d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1e6e551a57eaefe599ce845455fc1d574713af4b4263bb673c704b87e0b9d85e","proof":"023352f73eb03af75c084bcb0b4fe869ca0808ef29851dee00962669cdc5984d2ef54d65de2b6fa6e6577d87019b1c51cfe980cc406aac1fb0caf4a22d0edf2f2e301c8300578d73aa57aae511ea0a7d14fcc6bef81c4d37e7b0e4ec2f45ce5efe552b59534191b38c017f94641c511bed4eb71c2a0110ec650d204bd6bfb72889722525a5190633ef924a0dd6eaca77d544903ed7f36be7e724a4908b24f80369c8e0a400313d692b70f34cd433112cfc4f8da16af567fd0ae813788587930f1f09516a06abc8c6311175b4f060d9b4b6f85edada129bba2b285684e496180c12d41bac0b3bf26acaa5987f22d369a20e31c99695f15076d3c54fd2a7ca251940cc2050c1cf906e1f9f2ffad5fceb2c95b5ebdc082ad7b7177a4480d83c71667854e1a9816c810c09cd1600c38a5d52e18029c393b5b2524e0c2c0f46233f3dda853f7c517a62dd3b9b367a3cd48d4f2f39cbf06a797b4f267aab14851a985a1607363148a0f0ed61aee2a735aa4456bcb5ef7b62b09dfda3216d630f4b006d22ed3523b0064930bee4eee60cecdbaed258b39e9f5c598f16aabc878204113a983ae63d720359008c3ccbf5f2563dadf3c73034b77f318ac498d159414f0f625a3759b71ba3736a7edfe70d08f01a2565d70d9571af267b2322bb082cd5664404cb3d8ef4ce819ef0226c96245de27e71d2c8af534aea6ed7739c15ac53d76ba67e59feb4046b1d8d67a09af9b52f9c287878a5ef6dd41b04b49302e4a8d5054e0093584716e8b80759fde3caf369c213a7c3703fc9875a20b08922949974478e7c5fef3afb954725be2e95434d5b2df32823291c532ba54346c12a282e6a471a0ea87f96c16a49f14e500244775a9c69201eb980a6d026c8cf621480d432070f2757c3041aeff50571675a168108112ffd2b605e39048e606c38dcdbf4e90c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c2d0fc418099b30d82fdc5b93435a73755f8f682f8a20e2e5687b2a53cacf528","proof":"444d53e21f7edf49b5ecc8ed158fef193c8ecfa424158b9cd12c9862f033f147dcd522e302c7d36dbbb54927bba440c45a69766176771308d7bb478885416f419a0edd288ded38b118d00ffec4af4bcc6a178438c37a4719d44de5b490d06d161c46c9bccee54f9baccef4a7308a1bd76bbdee902bf09dd7c2764c63a02a654c52e468206dc6eae01e55d2d7bc449724ef4849f19f6fdb8aacb417015693320aaec850f0f4da823678e790ec47775a59a559317e4e9b79793597cbc3d4c9180a2e5b822ce2072614f8cc234536b87ab7d92578ee3652ac111c0911f24ddb320562e0f717c355e8002a4ab5326fdb760800137d92c34af53ffca2e535986f6c644c3752c0bfaf92ccc89b74c47e7030ccabc7b67094ef25902de85917fbaccb2f564d65ac1eb6c671703afc3ec18939aa2fcd75135f0e81bfdde2e1002d6dad2abccb389be400edd9e1dc545a7b3ed1bb89033b35fd6f136e374d7e51779a283cf2857fde4495ae684a4251f5ba9f588e175a8c6ce3eb8d288dfe19a6d46929248cf4b6d540150991b2fde4a26a57be499831ee41d6746f5f18212d1a6fa3b031521a96515503270a2acf2584613c119031b8e4e8000ea46b6c741c93d3a240437e1f3f5f0462d4fb0735e8835547a0410edf14ca8ceab4244a14d0b8ccba3335b4cd9f89d947f689cfbb376d13dbcbc82b088534c6be680e3b3092c50a06522e361c4e7a2b9483cd366148e17b8d476882cac3dadbc71b413288cef6dbc715624a3b479d1e42b2b3675f544dd790915f534f601de6b2ae32c72fd5067391e32c4cdd97053a243ba3281f960b2981061676e17d6ba4518e99f4cc84ca071da54354867a2886e2542130237863b68f174dd09c1348eda583bac2bfb0f11596850f5292da818010ceae6182800b7f746aa0262e06454b5e366d0fbe92af99f10307"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a285b048b6ecd5672efb0f1cf230b37b155ca5ac712e38a91ad66a9f0a299017","proof":"0404f23fd019233185d23ee04133769f5a68ed875ccfa7caeffe642f65c9164a7effe62bef9cac83500bd181d7c2c480d72451e8ceccea7cf1080aec0c2e5653bcffa4a846ab65a7388eecc6819900d27a77753227fdeff380ee5780328d714ec83b6ba19b9990eb92e9c8992904a7e799b80dcf28598c2a476489a55e8f9a280efc3d247bcc432586543aa81bbb72b325969777e5b5532ed91a7c4ef3cb4802f33abd1b1104497617ad0e334ab582a2ba0530fef340de0b443e96e1c0173206235926f0e2b72dab601f989f2d63d6499a2bcd5d93c40e2224ed598232482205da908e583a9181bc2b4f678b0d282e9e4a69495c95ab7ff89b08ceb08bad3945a26433cda03aa9c8ed4b206d18f71f32a56e16e8d3bf89d415e45d3e542bd86cac500ede5e511a6cfdc1ec59b352e7845251e8270cfe9752488f069f827a176be0381156d713e65682523a0dcbd9505bbb4c79718f429c8f2f12d3f978cd9f5f640fea4aeb0d39708362f63565880f5477896695ffd117cdb6d696a2c654b471c0624a265e9e5147faa588e38f6b33d18921bbc9bb60579cbae821e30e202d689c88930824d73a5a5b2e6f98941d1ccbc1b55d6379f2a1d93186760924f82d048a89e6f632b0a9c41646b69f0fcb843a9a0b5b8499d1c096541311a6ae1ec14a36c28deedda9e40b34104b8a53cc59bdf5f394bb11c1cb3a112897f22323b85c127c25c212f8d4077349ade9d14ea1ae8e48d7c94a579039ab038954f523c560b68bcadb17bfec36c789aa042c36a0e919e80ec3145061d710e49a0edf962d51f45d7a8d9c6dfec075c6de78d55f0059124a5b0e0067114cb43de6e5147bb518b1f03cef2577c244c1226bcc8f50886b2b917c22b56c2eb9f78a307994b57b0a8e72bc1cfbfbe66532bcc6159db282d0c42d964ef83af00f291d546f8368c000"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6a0c89da747afd920c27f91a001440ea95e1672e5f014d7add08e1685a1eea11","proof":"c887b858c92ac09b6d94caefccf5e79b925cf7afc8f83740b83bf21f97e5705a723d0fa4901ffa8e578e062bfd016d9e2498a2dd620dae6d45ba65097b7342121a3ab3a27ce74fc96856a6e89288542df46b19dc5b37d05265c1cc912355143e920767c687c88911b46b0c945fcb12800f638202a04d05875172622b71818670fb14c44663f03f593b566c503dccfddf7c6b621a8e9c5a1a24d9a6b57d21e504a7ecd45c13d6ffe964141b41b700a2f558513e1f7d83390c251b63b3f48c8b069d6d8e2b7cbf08225599ee914b72737ac34a707a7a28cfea0b1b4826dfa0b60580d1a87452f9d83735e231452031efb979c0a130aee5f359fb1aa5d1b5595a6daaa0acebdad725c89c151baf7ea2c8dd1ecf791c4f2081475e6fe4f86a14ff29d63e7954f2790d23c909ea85bfc894e64b7d336a7e48631aee9c3d3cc37baa70aa886c3a83e844f8a6d819c7aeb2f69cfcaca22d3236242110bcb2c78f91b7219ead9faa398b1e201610f9268ba096dba2376962735d11cfc3a75edb35d9f2731a96583bebd8a6ac8e11976fd77a5614fa9fc7cb7ed5d4843086cdb42c0b69693873c39afebcf9ca0d3d1022d948c21ada6b7e58c7b4e6e05cc8587d670fb8426a5f598f0baaa59eacd96049b314e2c348a16c4d82eb55bc2c110b5ce6994407388d01a45e8bef58bcc146a4cda3455981e2f8c16ec571e6b9fba198780aa60e4a94bc7437632c125f4b4bf840cfa107afeb87ed005ac21f2d57436acda7d723d0bff1435cdc7afc6341b09e80ce017c168cc41a0788e219978eb42e7e87531ede1bb4c88cd4f4501248c4933e2db64af11b377fbf5f39bb9bd9bcfda6634f552f224300fab20b24ce1301ff57294ecd9e638ef21a8047cb26dd27b835ae77004f0e6396949ffef72ee181bbca710375f6199090d646b500d50d987a597cfa0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d08215f0e758c7887bc24f41ccf06d8aa45b6dfab8e677bedb19228fbc62aa27","proof":"7e54d572916c8be7fc27f9be806608aff45186df9cb92f0df0de6743ded2802a64dcf5f25d6d4dcaeea97b0f0a293b230c124630c2a76c1e55f9cb93357fcd5116f1e28a93d4ec0a067b64e478432310d69185e9c4f33ab4a75f4e28835cce04388bf6ccb55d27f034186a3fd5c3eed219f40d7d41e2148ce6e8340ccb030c3a59c8c829aa0728f56a5cac6e83326a32caf467dbc2e8ac7e1227273ba05cf909d56a7bfa21a3696fc261d1c3c063f1ba6c51d30eaa0433fd4d4cc5df3ced5a01c4d3d0d2c73d3404134d17ed8df2f81e8859bcf6fcfe6ff1195341a731d393078ca866857a747d8db0024d20ce94d877a456fdfaed7a2562bfd65bf6b9dc573dd67b5ce647496829af6ed4799cb89e915a98ed384da3b67eab48740b3b92b13dd4e7964cae81a101634a68259674c1f22f29e8d27052f9fbf5b913a9752a84112efbaf0a134c373529c6ca17d84e80f2fba5bb08b1c94652785de2073ff2ab547a215aea5a9f2b8dd2446aa4ef827b299dd9e6210b1cfbfb7af812cbc34fdc4524bd0e829d7ff67b1848661b91d70b2ad84a78f04e625fb65b1b8d2f9e63221106ec7e3d4adf7ebedf23179c8d2d4fcee147a62fb7d0e21b0403d496f886e25206311ea83e2bcf1d685ef6180571303d86468b09fab41431d6316f8feb63f50456a40f2cde40b58b3de11f8e5b0aa0c78ec73caeb80bd1ab0cdee20c61abfd5298a90466973ca4d590144eb4cfd4cdf6eee4d740fb826d061020217cc0731354b40778b9412089fa347be2550c68627963029fdcfa1f45ae96c0e0a01f8def3ae2c9e586fb91c6959507f406c82299704b78ee7744f49c9ceb0fcd0c0f1fda6cd0f0e45ac1b2c2f1cd26262ad941fc659ade7c3c9a10265d002c7b60665e600bf27365be341b84eccbe0f960f1c2cefd75f7ead75d22058b296e008bd772ce00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9839d8a114c44e4392a65f3cce860911c06244354356f309368723b7fab9ed12","proof":"ca509b16ac0333c5777096a6dd23f719092265051d418754070f35f0f76f562696c88159d548ac209d267aed47f84ea6cfdac08cd06f73a94b6d43fb7991190e46ffc05a42dd431f5cfe1c9cbe34071288326ba9d72439c5b9228b03acfdf7305466f9db7154773e3afb1d83cf6efea35932e8680398682d13554c496072521539e17cbdfc5c472fb1d04c11cf46a44e3aef1e8b70cf551c083a837bf994890c3de7039a1288b616ef56a89101ffadafbecb9f34311e8b6202fcc7d6db47d50a65248196bbf9c277c45fcd3499743f77c0ac25ff5490475294767bc800526c0ace934cacf8f1d073550ee9ef91306e7b0ddd5a1cae54696371ce4c72531b564cecc79ce04af28369b81508d6f1a89ace633afd856134aac638461d1a55be3457ec6590c7a40af0f2b1fd4e78c9286639ea5607bd118eca4b7faf8e095c50743a3047cbf9eb8e26e17db48ff53a5ecbcb266f7831745cf970b2949f93d3a07636b227db7eb7ed2feb17f8dcf7994a68a044eaec8ccff30e2f55fe9021e86d6a63c2f3f8663be481f77ba30a4862224d2c8da7ad21aca612ac42b5a234aa890c4506506c55836466b6a824679de02e30d58427d650399c0a0dbe2674a091048a2b804123f796d3a58ae25b022cdc80345d61c159a264fea795abd38fd135810e1b30a7ffa437466c0c95a952d3c3afc2bc94e2361d27237292f515436aa011a845ccee8c66179619c4b1f547f9b16eee1784c23dfc0fbaddbd91e0c93ba19d206d28e9c5e11c73365f10ac2d7c67db443d01d99055a405be738e7e6025d6c5054e8e395dd6ca947f1c34952a8bdb1a085bfeb21c2a25e932a77c118537c436e54a93ddbe6e44f56379fb96d1f1a45060fb77dbe3fcb3eb227f2f7bf39455550c078d8385fd8c700e1beca961c3deb052bae436aab1ee6975ac58c2a6dd48e51c00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"044b71b24641af50d905910fdebb53abe1395a0d414fbaefac8454ece100513f","proof":"207cf9fef17ea71f07e660893fa1b1cf9493315cc422bb3c353600738da4f42214265401e0e9b47fd380fd2a15c51c6f36f884803d939b41d9266274ba0034310497e652218c848beb1bfe28199509dc9f188030291a9d5427203a9b71a87f2d3229623ffb6aea124409ee4f17c6f5ba6d8ca485dce97326d6e0add910f81f13a034f95c42871e89438b949ffe16c4f77ce077b53c7c649ab6e535876c6f4c0e46c9712fd69fd64948b9f570165592afd710ec7d1f775b32533dc32e5e2a7700382590f7f92d58bb44ee9f9d9a9dfdfc0f37480ea6cdd1c79cd7eebae2c1160f5e421f63205de8571c648422580de124d6ec859584284e51d1f8ae28a3faf357f084474c749939eedfed00675a350c71c6d755681f0b9f4d2938d5d61cb9b77cc29bf393a48e91341779da7e8605e70ce7938735048a721d1e79584f1628956d5a9b52e91a6bdd7ab3791a0ae85a2f69a2e672a167a0bd26620ead6e8fae4977b4f77253f40c2cb093a7acb5d3283e9d66685c3e191261daf84f026d2fa70b4efe1e9d7a9eecaa4d25f42e75f52e84923abb9cd97bc7ec9081585426c20af30adc51e3bf15807acf6c6bad02733ea15f1bda524818a1b2f6e631caad16539e42b2ed8ea56e145cf224849cff9f55a191c945b3abdb195df2a34ba641ad5767358478dcc46f56a7867eea12e6c51b5e5fdec694b03281b4de922724343fa27764d8858d9bc0658581c8e422a4836e62426539750faa6ea68b48bb5d3d8dceec2092755e3d471f64e802bb46ec0fe7b7cee4944f73dadf9a77f9e4a0559ffe983f3297c7f8e9b16609d24a465e401864a778b4189bef751afd67d2b31de4f238584b0241515fe947a4578d05465b0e1f35f4963157259e6b0e292a6263480ba00462852c6b7541db18349783eb699f290f17772c06268c4f8fdec5522a217d9c04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a63082bf16fc6ed390e183a53b5588865e157a12469d2a4cd044585574dbaf27","proof":"eee2399e6500ddeaa60b2cea90d7cba28ac3ba98a65902f57f942baca86b4964ece4e0491ae62ba861cf4b355667737d18a5b488caf9affc49081be13e407068d2eeb1fb122ab866a9355efdcaf221d4e492604a1a04be1c8b63d4c3056ce213acf0f15950f4a02e9dd203ebabea99fe05239293984dd8c470cb2f2771f80c64d8dc29f04d1e4c0232c76deabaea048e40f5893a98f23c8a9179ae3f63b4ab03943f7e63aed5364e89b9f19338f33fd84f0c043a83cc9d8212d163a479d86f08b1355578c4dc12026583f19d1663ee3ff55374c85e35ed1b23065a06f73e060454cd9d2671f3697761a952485da9e53b4e6e09edb54fa81bbdcdb0b94bac163488da46182d7055231ce4432de6006bb799e96e63e8911fc0bdc4cb005a4d9e413e54b48c4a3b0e00c4bb239ab2c9ef1fe188bc29fc174c7bc154cd66a2ff68234ce7a9be3d8f77de7aaac2f894dd727d85d5a0d3145ef4c0c62c7fd744a32513e851f8a13db43fab53546aff32b92899681cb9cfc9bf0c40f01f90800603171ee61760e4d665c06171f8c1b281de0d322939802b0856d8deb0675e10fa6461564432849f800b88581162afd3e7fbcbd1ab7336013b6cd5dd8b12b6d62eb1ad0474f8c46a1a9124bbfb094d1a5ad29e8385dbe1f409a7fdbed1ddef8277243e4d0ca94d8c0f8aef9f3abef40bf4914d654a38ae0a18e6a596408fa19985dffe05f082703a185f27392b6d70c84ee4222fcd2b399dada74e92cf3d24680962d559c87d02401d4e6d18abff2f1ace7298983daaa8e15a737bc8c7313ff6b17b5d17d27518f5aecf880e978dd656b105c4d333106c58b553cb2b5be58a817756c955b0b8ec076a336319252bd9575ca40f2e759a5c45231d27694a8c1119842ebd0f68340f831f5646a4e2423b07e1b3f377a13e9e2ad2bd5797466c793ac4a0370c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"46b7002f9081db1101383dbed000993b30b03319acdd3b55b31157ea7092967a","proof":"5a6cc2c2bd8eaf351cc0d44bcf675940fa205cb2fa93dc82da2fe1f802885421ccf52a2cf5d9b117661f2cbf54a338b39c58a90342d1e7d40ebcee1cf189061ff061874215bc6d084d4606a3a2d2ad3a42886b31e9040db6a0f90c21b145fd339ca940c73c189526c8fdc8195ad6a20bc266d0a31a4b79061586be8f3986263775057609c88fb2f6d55b553c5d129ac88622aab15bb929a5db18dbb827da2e0d05b3063f8a407450e95c33028aae4b5597387f30892a25a74c1af8c9093caa016f56cd8b0b173433c05b1f911715f7effe46d46b37194659b510576f8d37de03a874b888afd4d78d2920bf2a59d2f4ee867c4781b01805c1524d3c4f82e3be17ea18fbe5e8bf48f86bdc078fa5cb4ab4556cb60c65edf6e8777333d7edb85324c049e28fd913f8d6bb70ef7a491961bd10868d1844f814d5dd8bf0878562ea6154d9f6a14e4dac05f939dc4b2bc26630d5f72437b230340150c43640e0d58654224cc9ff68acef6492fcd6ad8a4a93c3aba7d0de6233d332bea0a3fa8d6f494b283f4d83ac42a0c27b21e788fd520252995ecbafe2750d9137a7e5428eb1bc7ab0864177f5dbd49ecaccc4f62c887dc2bdc4034d9af233bf84dfe9afbf0b1a64dc97c9fc90f6226929b9708f8edf90b6204dc1d3ed9b9dae7262be9dbbd05630c284cef50ffe839eb87e26a636c6ce79d04293ea8f71b86c8dd079076539887324f846ed770ab3369467dcb5ba7895cfa7645720706697e0dcde522e392f8852b6c720b469cf12c7a05a806aad1ace190a01e8c10b546f290831d46093d2d750f49db8442523ec2c044c88d256e8fdb89648cf21a95baa6eb8dc1bf94bea1f1297289336d05a27360d6f90f2f5dee84bf1095d1b25b45104a0fc731d636f57003f0e8466377ba828b7ecfc7471f5a87ad4fcca77ebb988ee1a5ac06ad9165308"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"20f06894941b683fdf1881ba583e2ec3122fe6370dac489780aeaa079cba9c73","proof":"04f9df6f7fa8bdf1911c7c750c79a6975a826dae01aa81c597b3b92796c2d66d9674075150d175d79818e35e9e372efd4be725cffc3c1217a3863da4c444667be6bad5246be6927898ad5585c93162b3d4f43771ef4333cfa5ab9b18cf72ff751e7be9dc2bb7ebb56cf96d0c343f1e8badefd4d4dc420c594d106e788cf209065067603ee0dd535061ef8d2d11c90f0d668748f07877f2520721977f88049400a9cada1e109ebbf764ec204f7d75c5d69294b6c4a6e17829bb8888ee5aa344098953b1b3aa1c968098112a1ceea19e38473d4898477f6b29cd255c97918edd05e206ad6853b5a6f1f2889de5daa8bc3cca16fa32a48aed4336d3e2479a4c950040a47ed573243c7492a6c9e89469625e83379ee53b098978a5531d7a4ce69c3086e8e18bb238f219b6b709b9516721a7257bf5d1c05d90d989a99496e0b273669e76c5ccce8c1d6a8ad5c2386d88d5baaec4543a6723df66f8b6140a9017a72902974781769799b1047312218e20ae0816f43b8895bc4d6ac5ce663530078d7b1e1a2db91c1c538ec4acb5440b004f4d34f80bf272effde9ca1d791cc4ef4b34d22c1d27240b47d52ab9a55098ceac0e225e1e1453153a34bfa0a4a43a4b777f44b354225f1a4462fd60980db6603de7bece46c98e837dcb16e2d5b60cf7480a3254a0f09cf64440259a8ed2d4cd71510f4079f01ca2e7368307661540d40c577c31c76191c85163e4dff5391cad2b4bbbe159df2faeb8836a762cdcb4282343886d3949c32f530d158d1e64df60af2d8bb1692060750f530f97ae1a4b2ec7248c7b15409da83d8148e47746b1cf5f592afd5ea2de37d2055cf8db9b602bf37018b5c21b2e4cc3a974b0437c46fd3a2a773b17d91818a6e5ffc017d96e21630818c23702f170baba53b43ae601a6a62dfb1959c46d7c60e3d346052e49322a05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"00ed60640d988321ecc5ddc473e31ff431cd24217c10fab3300a08eb2b6c4052","proof":"72c341c80b8bf8cb730023e7cc1b8b69f694132d7b1397a4a0acb0d42ba56649927018a205747526ed774757435b857e214d5023eecb27e10efec892143c9f1d240a3c8ecf1e0def5eb2677ca3dc8277a2e3cebe119b2204988c63d055108e11322ecc547234e7e0e7c2f8e9cf92bb67ba3aeb9b035d5641780af0fca71a0848c563e2f87bc2a4af64fc82e5de2fbcfa4634c9d15f2c5dd11deab37f18c4310a7f546bf3a58c7a732cf46e9004842bc92ea267d4914cfe14bb4881b7abe03807304f2766a1a5111d090b2f049719b48a88ce5a8744a37b579bd1b774bc9a2006e408e5042411221fd2af000afa9625c8e83113023882d23d957e7401d738b814b0bff8d8df14109ed5a4b694493fa219b483d1970fa748241828bcdce3240839f4bc09e8741e8dd6cccc7ca81bd23c104aaf29d215247e554a17df0075295140a6ed929b1678faade69ba6d6b7b7f4ff2dea62994d57ad5a846da2ebca4ec70d565113d61f7953a2394e6f24a7c2b6468da456e9721ac8feb295fd4ef2f97d170cf954faa41b68e96ea6ca50d3d967372f441747bd371dac4be39178a43078399a30511ad451725de96bffeb16a947fa1c7d8ef918c06e36b9dd6e298001982994f8bd0678afec35a0a39e6d08a3d0a01b3a77b5fce311b310bacbb0b01e9b3d8c2d0f54d8f442d7b2822bd8d572e06d8fddda83f54611ec891938cff426025c8e888627bf89942c040257077bef2c5cc4a88d103cdfb7ccca610d9e01798058705db2391b926661f1ff61e4e90937f0bddda59a19f86d0cde96d93eac68c457000dd6aa74f97d12fc7cbbae55e6c9b5e5e30b6b8302371caad9d1a72e40035424b95f996f59a69d2344fa47585b3ddba6297662b7ed40ccc4c5d900ba97e3025cb699b7c9dc7703eab721655d8fffaa3f7a690a825926967dc9176e556fba07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5454cfce7a2c51ae8507b3b9700088360c8cf2d42bb411daf46e09c435e70a26","proof":"da193607e0ff3a8e330f48991e926f80da8ac2d8f499e031a84dfb2e30fbf82312c1ab1a144d433ba295e3a21e1299d5e7f560fc232ca7aca6ccf2ac15f10e155ab000148c4eb26d18215926dfe0da2d0b2ff018ef4a24564e0cde225a5c2b067273d058cc35f739af87ddd0a677f9688aa40040eff280640b8afbf53fb00a7c0b7d5e4ded3f31f6870131658ec17f3078a85a530b249314edf2d3cf6406c20653b63961d408b2f4e8920cf0f36478f2ca1e0d548b7bc604716cd343fd0b6e0ed3620c868dfcfbfb5ac7c605c5a078dfc6a044dc39c0a2e4d877c018322cca07cada235155ba5dedf318d6e6dba416019c48a60836d96b29e1a59698e86f0b11444314b343ec18bc80cba316041fc5168a9567336ce6533a1eaf5ba18260b52fa20f8078d5d43824cdc1686801b46c28d0d96c7844ece2967a5d146c0e812a5b40c4e5a1633f2d5f03278b6a7dac0c87d98698192729552fc10ecd1eddd3e11a2c6bfa9776a68b9b0ef4b787ef034458d34d121227223456f1dada36ec5d836a96be9a17af71fbd4ebcc56958bb8804fefc69b8bcd57013aadd526a889124245ecef9e1022c86268d2637f2118288029c0598a926d5dfe580cc2ed60d036a320426ca30f0332b84dd04517778c0f1aabce5465cefa1dc6698d996338558a76110afba61baeaa4e41d1d559a35a40eebb0b85826d33a6e58fa60ea66cb7f3f63b2420d759a5bfe7e99b5c69b13485eee854101a468dd2e89129070cff35edf60facc531f4dbf3a1b0cc172691d8030ad3718cdd392b1cbe4d47ee5299845e037d52111940928cfa964bd5ed54d78ffbd5c53ef9cdca8dc85646ecbcdc9650fe6d62523d92ffe64d203742936d3e56ce6d197edb05eb1f4fff1b072b54edbd8f051288019152eed027fc9f357bdce08846a8f54a44092ca69fa88638ad69c2b509"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5669ff285731cfd2bfdc02e9e81481a1c4f2895f0a10e4abb22494cc758a9779","proof":"0a23af8a53da3c8697892ced0b620b8f5034562dce1546f67dbe7f42eb02b12c702512c83aaf4cec6c6bf695b7079f8ba4a2942f1243f199c120f3a012c4180408b77a7faebe22d74679c6816b711a5f070814ac0a3f5a44488bccf2f26eaf757e22382673ea9455891c7be61d1e932b5122fb4b171124ebc593ff806cdb717e070aedafa6f8c123089650bdc48bc8bde19673b4f91a47b8b5ba52838f79d10d4c526b71b75bac132fb14eff966abe5848cf7a2aac5efe335208b81c73b6420d50c7dfe413cf00b5a2eb8ee3a408ee477a92d92fff46c354646bb9f99a58cb0fb83f09c348460879f1c8d1c9a48b91656c484c210f570a452f4072ee5475003fd08734922910811f25001e3d444449d729c68191336a59cb2b90e2694c82c841468b544d039fb97aff97abb3960bb7e5f24f71394f3deeb6b6bb5dff15abd4791806f7d639a2ecf956e58057edd860d0ebf49bac54b1475cadcf05ba5b55052b34191562cbca5d7d3f5e07ca204a3ee8589634e134d8b90346730a2f4ae5770234018e47e2ed5b086f3f112e99ebda857d2b3883e489b66a2faa121dee3241003ac209cccd37b99acac883f272eb0efa91b326b5255aa56c48516998dc451301dc233c07a4c0952402a4999a9a543e1c0d9d4d8bb323a5aa9910231c2a3bbc74028650868853da7d6926622a14f6d5757e3fc57a08f8db7c2523247bed0dc6551a2909b6073bc124a91f340209093f2b0926f87a52fc6fae459e953dda6ea36a7ce36c9e99d463a62af4cdfb0cf905132babc3a190a3e29a3d8fd506d810a6014c6101ed88fcad6f9c227ec7fb0183a1efaa28c28e87af43f4472dddda0e96517467439616a57bd81a019c234239183853d6df3a197dd177863e7a1c9ebd570f0c122c22536b8520b2348e59976218a9507539a7455a6c7abc4b2fa3f5dbf30f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"18fd5be7de64c36e4fc6f48fdda5bbec3c5f2c651afc9441b6a45bf999821328","proof":"e4a5c7839736e557067c3d04834214f3bb9454b4a19768913577a73f5e9989382c288fa185d6d5ca6e9166a7756ee2e977e21a2a20e4e45b9ce97e3984b95067e45a0b1f0b1487de94491a211280d351d8e02863229327e5afd3ef26b95e745bec151d6e364f5810acbf182412b24669ab13996807fb0036b1c55ec7c30de45356d050a11ef2e0413a1d0fe7fc02f84b8084f1e92853c805354f9f966635f60aa80428b3b3be19123887f36a47afa01059f688a3d70935bac1cd3dd01fbbca07b7dc9a4bf60c67c4bc5434e1b56c4c441cb9ff41c4e8fb21780c1a498cdd8a092620205900b3028f08846f971439ed4e833eb6698a43e84341394bfa6c4a1a3a02e9e7f08d7485e4f211eb313bdc9bd29bc06f0adca2087166c9b23e501535097a45cdf0c4a35e938c33fa44c9eabf2c4e0ce983194c702ce4d6c791c2541304d41fd5990361bd80ee462866bfdc2f4d10c8bfc61936aa58a21e2c70ebdb3b5e5a9c1c53c9ec592f5090ab31e581d822f08a535cfb5640779a884a6a45e3e513e2d8945755b9aa86e51a6bfbf78b9f6a5be641927c5a0821452947445bcdf8622c3405a1785dbccf8b52c987f5be017f87ba9f9f201ca14341c1ae557d001160f40689fbf27517032669335f054c70cdc538f3f87273525097fcb2b98763431eac752a3d22675a96ffc1c187511061604c66961217076d3456c893bc2c61a5722015a8f3b5d87c60216ffba1c281cc73a1d5295a15188fcb5fd0e23d3f1cb2683ccaf17e70e1821e136684a41800cc07e7fe867bff41b142e51924e7d3c0415a5a9d3cc429f466d774667d0b40ad8d2fe38a58f25698be9bbbc15e357050f77dd73acb65ccc933e9d5d43a9a95ed23002d7d1640632d1167746850f143c63304e4c5542bffd064b1c29d05e7870d57eba431e768911716eff99251a072069308"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ee10489f06287417d99654dada01a1de8739e2bdf8344453eb1a663a27f79a10","proof":"e42c57146231daad89400067ca9e36d95fcb6aac5c0058fefb9e8db188ea3178bc8e51993b6436223ff6ceb5f6b0ebbc9c004b86be864933165082392ff06044d8415719b698dac3f8f365001b27150e15920cb593d5767628ae6e29d4f9ed515c20435e465193fba28b58638325d5702611c0d574651437c98a16a49e0a82235a16b82d24ecdbecc7be39b2177f890edb095b5deb16b9a75f70e8eb398150041b82e44c954a7ae5452d245ed3ce81dc049adb81528a7e002c0ec9c5dfbd9f01bf3ebf3a8fd14b6d42b53bd37bc2049855826486672b3748549a69fb340e4402762b8c7c376f9ae67b0d66d0ad9cd80bc7cc41d05197d67e7198c1ddc474687e349a96bc4b6fe43bae500d3440737f46afda8b1fed0e3a50c2b503d8f55a32048e0a6fe3aba96881b6431314f885e706a4ed252fe30020a4cd31e72ab7ff231bb4709f65dd6706def2e2d04562bd703869bdfe4b8d9581f327b7002e8cba801b8246fbbe95d381ee52cb8b05b9c44f3daa81a8740a2d6317ebf492e888b89c54bcc045c6ae47cc28208f06777af965580614966d32744a2748b8bc69e4d29f7cf0c005f92daf4de001a96b6d755d467f93520d5bb5752ac142e83dd728185320d8a8f79a683beb9f2837c94bd9d9993de80201e5ccffe76cbb526fcb737c2002ee789822ffb2a67cb92cd62e5190326f00a9aa3307105fb7e471c44a3b16af70267fd1283abd5d1cdec0f1d0ecc644b3b241fe40c555255f943d03d4025f2c453c7abce11ab020e146181fe596cab2a5377b601e977bc8f3f73d29da7310592072dece030fc4fb2f0dd42f7e20075fc8d90407377ca1cc756b0392d52f59650f78f865b64a109b9191e501b32c5b0fa6aeba1bf00860e33f65d555d065f9840e67936693c6336ecfbaeacb936fbab1a0298141297e6e6bd1c3fc528dab960907"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d6aeae2daf2fb7b0696c64fc94813162be628e757a2caae223380487e8269904","proof":"5861db652203e7439f07d284ca591b3ba34bb8e83fdaf32dc0eca1585aba8c5df4294957055a2dd461da7172d5e0169692c64fe20425ac287767902f108a5f5d1ce25e4a576850d298bcaa9cfcc40bbf2be565623ca01de618292288fb2f4d34e837b71a9672ebff99a9ace410f323dd888d17544f1cf3434c81af9ac603dd06e1530d4df5fb43d077956e4eacb316dd42a240f338792700b60901d72b4da3062468f3836e2db82e3d5bc4752ca09f460dbc67efb8d710ea77f4ad33f963f80c75a97b4a35eb9ac1c63df68e09e84fc85ead5481384fccaa9868a1cf4e8146055acc2928f050392058843672a3aebe7969ddbd8202c483f53a9b999edf36997dc657b69330d02d8fc186da5cce2a3498e8be116b42cb237b1b168ae1486f6c37406809d9ed61b0ca3375f30973900e8f14291aba57aca414faee94d99149596fc67dc80be8598435adef24411429bb45fc51493494e4437e654be252261b991f561c5f5f2a42c2b6dbbac7c7707701b718fc3493e83908c2da326ff6ec07885dbc5ddfb9044425f0803046214e7910fdfa9c16bcd0e54ad97897fcb2c82087028ee1c9eeda37c2e53e1f4a9df84e21ec841c62a69f5d9f0c4f03495d2828f7730aad0566969f253be2d90eeebe4090fff0e01c9d8494f22751c609f92770ce42389165bb8fb02dc418467f726c46524bfb65c5b03ce4becf55d6d2fd76daef002afb66f5186dab7efb677faf6a9f6601374ee2e39b96734c0d64ceadca83b3426235f167b805857ce6b94dcd987413bcb529696581c841f7838e917a17a0fd33c47260a2cb4ea886fdbd84b089795fe82ed6ba136ed023991e7ac51eb03cf363d1f5afbaef3d01a17b3babc77e7ef7b1696d5f8f4a3fcec9594fb40c569e7f0d05d48f78341eb01b86e95072c56b2dbd97b8f633fdc49a7567fa0d06d2d2d707"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"46239e04e4ac821e43a3f0f0e8f2e8faacee3d37d63f0ca23aa59b98e9c1a324","proof":"5c78af05c074d913129bf28ff8483df0b5a9ec40450644185c7cba8e0f908b1464117011541565bcad908cef247b9a6200ae3838d4c09f36c68a1620e2fe9037962692f1a7ed6adab6db49cb8d2c8e8455b6fcc9c2248ce00bbc39301fc45b0556c7fa091b6ecf2d82ea34f0ba512270abc9154f7eb1ea8d70ffde4cf1aa6b3be80f12f0e19c6b1a617bf8ec77471b193bbd1a866e0b136d00393794d9b5b20a5794371cdd4a3d3995188ab6c820df689f18b341391020525efb64595380cf08f463e91a82e3c432c4f97d15bd0c53e8450e222e1a009222b8beda27f64af80816de27d0230ac4fc4ac1dbe6eab7f6ccf4c0bdc43f1430a8fbb94573c41fbc3c6a49baee8c9461d247df34d5a455d4e6b23e67194b1051c4addc3a7f88409d140cf6fcfe06b3da89134ba7d213d4ec46c7094b7c10afba0c25788eec9a73e5757cec57fe985129de92ffdc14f055b46bee3a6c7ec14027e05546273cef6b245946a3308a8617d4f7dc78619e60508249ca1ce062e1df78b9dfc3f321bb21646e8681c99fd16d3ec5d961b8d7bfb515d42b608ca657e172db5010be7446e3b619aa15fad5d14f12afd7b9f68c07337529d494e7345a686888d5cea8d65f5bed495a09a6ae9cf5b6d658b60fd1d8518359fa25bca34cffc6e4790fbc308c4df83594acb184258d06a39f96a8004795ed0f0b9c03f5338d2f4f0fd3c3fee0e45470dcf1e0a383eb3e37db463b7690c4a5ced293c767e1b0bb4f74ad51c26d5f9e4d4083fe73c497e9ce1c6863e1f4c3b45079a3a604ed9ce96f9e30bc75bebb3e5512af6de56a26b1581aca406de9138e0008a4b753cec18de8c5c1b36793647474defba1d93daa6b22250cebf9ecfe3254e2510d33928c410a22fb7bb1d2bfa60573971242ee3e7262b7b64177a61975e95a74aa3e222bafc315bd98c849945d0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"acac0a60d46289f1959f301dc6e4a5afb846aacfd4aa3235ec0e50934e66e81b","proof":"d0a59808de30b1c412f6e935dd237713bec503d6d6c0968449cbf83accef4d5c829e1b7a96fb5c6125c34ff5f19d1269d7dbdbce1b5dc824dc754bb62892037cd40798c68ed768e6bb62838fc7e3dd902455d9cbe7133af0cdc259e6008b8c2a9a941f13fb7d7d604f200946b7194bde2e10cfe7eb7bb2f3dc4675918426830c1875cfd8cc9ffcd043530ac6dc35cd2a3b37f9690d05856959312a9f2307c70c92b200592b9cb05f2cc22d90b3730c486e934c5354b6b853d5d4e6c0f48ca9023e824347b497e4d942ffd087e2f842be679f11c266a41e06802cf22a973d1b0c10dad819f990d5e1e4ff1d2875ed91d024233ae3c2fb83afae2c4bd741262766e28353ee799dc292d48cb6190b7df0ad35ad316f999182dd6a63607ba6fde27e0e1e0b30e441a6501cc51ee3951b5ce2f6cb12020ecb68520ab897b06ef7965b9c27066c825a7729b3796a88d04462409e2fbe528c13d41f503734339874af1aee2c9fd3c83b8b9eaeba9cddd0e1d37b4a55bf66755a09fe6a309dcd86339915d4d725460cf41bab7a186958063f5d8092648c25ca11aecf18c2e16ff7cb081782859475b42e88ac87ef44101ad0e282184de704deac3fb31b9f5dd478ab894236ff7e83b1bd7d2296d62463b84435cae88a28127bb9bd91ce0864737e011e2d68f684c3ebda235195f9322fa99b2666a36912abb7745cf0cbb9da222c7d0a262cdf017e41cdb0a44366e50278b3c40e41b0375d6433b200e12adad7166a23426050f1332f3c4ee5d2ffe37cf66d636bf3cfd0ea0b0d80a1935341f5fadc4f0cb6d6252045ef825f1de72a77f3f227837e55a58ff0e9de9316ab780cb3112655a0c251b74875a3ed01399488135953f7d814c3754e71e8e630c34ca17032de041be15e4359a99639437fc638cc09ba24a1c1a8c589532e05b3a71741681cf304"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6e634e02b02f3154f7f0fb5317dc63d2f40496744556e6c194a78f43bef8105f","proof":"54c0c84e1af9abf70fbfe4cd1cdbb3dd8305af2591b38d84fc5bce41f9aeae3568560fbf2e1e6974649828d42a92e7c2278ef922228bc32f8acb5b0270cb364f94f4f25bafcefb1b30d98d9af290370320576f0333ceb438e49d57c710508b5788174e39e3334c282ca49cc8775cbcdf14571fb215cb4af7892754dc2551840320635dce07d6db6cc59dee890c70fca0cc2993d40643831828e2794c5449ad0f962db5c9213ae58ab022945a60d1d25b9145cfb64392ae0a33f3219b7ee27c02590a6a31a87463b4c5531d52301156609a50d2ac05ff1607aa7bc5c8b1c859060a5f84dcaf549953bd044e7a21f96839cd562b7ebc5556a98faa423aba9bce5c388f8f3e15878d57f92c0f60619037dc9c53ba78262f5c9a1ae6ca9a7191614af6f6861146c64bb69f92ab1efbfba2998174cb4aa1414aea19dc2947a9bf852f04476f9aa1fbfe563b36a45606e6a1c99b1346d12121ce9d3331f4829cb8fe5ff8dede101d01aa5cec5356bc956bb4d339b45c880c5f162bc022f0b84bd9327ae63d7dfb1deb634958d3c27fdb457420c36f516243c6e882507ba47b166c8356065577357314eb5ba9c35e5146e5841004d7a4aff6c891bd6df6c1b0c33e1f193229cc2efa201c054881254dc77a78f2a80b67663db557b56be2f19a52ce6e2bf8057af3bdd676850505708791aa1d0c152f8838becdd9881b68ce8adf8e2e7476c61d9ad3ab6394d7a030999ad58804b3d6dc97297347ebca1b9570c2d33d4cec5373e7df2a1e38c78f97bea59f92accce08f1a2f6db65042f90fff22be41146ad43db2b3ef48de2eccc9f2279b0bcba8282924630f1428272069eb1e1b3e6410d12af70a58cddd332e79d9fedb606d55d85cdae85f62c997021002be89150d63d4e31b97b7a0f682682bf6039872a004f1a5fe42d79e3a7948c9458dfaf101"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"54c63545c753dfab83b5dab25944fd82dc26f4cd323580a6b4a4119c222d9879","proof":"8c40866283a5aeef1d6aac53669e9628a091aba59f246282593ef9efd67db41e123575b8f8ea76be45b032bf8c58427a07805df898a023d2a927e6727137b43fb68b2db1cf2fcc6de844271932412df809e686141066a5b4824721b3dbcb0f5faabe8365cd49512bf982032baad9e3c1c39519188b4b350d1f9b110514514323fecf42d47d86e8b75db52f6095c5e29339c1892c227e5dac691ba9de01b2d9087545b584ec653eb0fb0ad97bb403872c7eef1384c8a4fe942a4f4bde6ba60c02b380a89f5e4d2d46f705ccda79f2795305a71b76bc770b3837784364d44a4004863f313f3ae82c5195b4639a430ff277564a15a1ed3710eda57982a88de62747ecf4355f750a5ebedff458784fc9b62857dc3a9d01480db532f077fc794fe168ee1240bfaef2bb75990b500323c4fc5b370869809c773a2dc58e21579f505b133a67a90d3dfa15cdbdf43873425fdaadc7c2f816066a042ebc48e0f48350467766eb7e370d2a3652174296c8f60d8b7a5efc51a1ed5565e27bf7cef7d1b7e069a69644f7a5fa1f3586f719d513980650e9b0f0ee4fde1f3e97a9640c85193531928d9a5521765ce6f94a762d34bcdc14d7146cc38e0849451b84a6e461c2ab402c32ab867ca5b1458dee392c84d90d5a6f29c3fe2bfc0a35d27d5b6a7810641d1e21eebfeadb328967355fb35a6612423116ccab6a7c029507fba702b2acb241804168dd0f34430375c8d9788f25d57186917f1a99474b32add54c0d5b506562ac638f235bdfb396f8c51a6f63b2d68515da68aaac18d4a1b5087e79b9cd8835f05e6de636473aa8c8438519c0361424c536245344fd6e816f912cd751ab2e0ee74c49c99a4adcb2d3a5d51ddb86487f63310c985f270293d2eb9c4f59b4a20355ecc5dedb86ef65e908348cd2e1db479709170484025838e9e5c22720ee6a0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"620fbb39e2712acf161d376cd473ede0df46f6edd985a12b5897921f00a93c76","proof":"c63c04a44ea90d1dd89e18f1e8b657d6b6f6b4b3f87b77b5ea5dcb4619cc5d27ba0a5f42bf81a409d31473670c89575cfcf079055577cf5459bdb6fe533d4277fa954e0489a628b80787f55b0b69a602b1b44751034ad35cdee5f0adb45c4a1198e557fb946ea9e34c7ccb4be778e4073fe204fac181d49d1a086e4772880c7209a3cdc3f05665034faf4d9709a8eea4849bb022d817697e19f3527545882a057da9270f35fe0ff2feaf43687e920ba65df42229e79ca4883b4c723ec518bd0524e5b95c4b2cc010b77ad07d08eb3845875ca882071587246cbe192a102db40396e3e845c85380b337d242557d20af52e27ab6b4483413690716902d1346923c7c43c097613a3ee56a096736d64b88792bee2fbba13f77a6cee0c1d8788fe54a064a967c483dfb5e9b3d6d625fd5f328132ecf0a5025776c84197b24407dcc0b52d692bbd72c0ea0f3d6f7e1e72895028fd64eea17b59020988217ad3840e6426831b2e4821bfbc5337b7465d543fee2b10e24468e3f166ac03bab51ed60e43f88ee11856218857c81d958588598db7525c6e63e25ab4e958aaed51a01c1103a1c9d794639368600e207a662ff6f302fd467ae96f8b0ae305f4e9fd0a3d1db4dba0fc777ca881c15549d04c4a8976d739e4d50ed30ca32b2e28eab4436dd675d9a9c69842149aa0abae957dbc87342b67c8c44aaf0a04634ffd3151ed0987d02d0ddf14006305fb900dbee32853e8fcc68547566db3bdf879c276a30641cc2017ecfaafebdba893623a5dbe9fc772e106dbaecc41602a3aa6f74c4cd1d0eb538202eaa4a242a79d22ad64e6b02a49c38dad41bf705c9f8c046f361a57083077a1c2147c8cef8f5923dadced39dc2de69b823ccf8358dedd4475d66e5dd84ef01a67e1370bcc7c4f156948b2cf1d94e4118ea78c173c824f7003beb2192671b0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c68c1fa5c09e780422f69ea17c6510c8b5cfc8bcd768bed085955963c5c64c34","proof":"cabbedfd203adedc879459a1cce097e2e044b9196d824b952427bd3c9783c8215867a5363ad0c97246675cfc28a4aa1570e6a0a6a52d043c088d230220b3395d72c3d21a1c69d86930813f125af1ad12803cf469cc30bd33b3273f231f9bb22c046e8a9a844d7a9ca3fec25fbb974e3849ff0e0cde861f4ad675a774f03b117811845fe24b793b67d35dac1d8976f5d1df4779bdc84370109dc59672602c7806375143a47899749cb51e0d539dfe7c69a65241a09d079fbc3d9813faf7c46b0f8c2fd445fcac48d0cb3893dcc9e6339489bcd14d41fab737e30d8b50a221c80e9c517804ed13184a7a37a177c3a59cbcf17023c7fee9ea2aa99ce6f78158f103b8b23bb0d4e4788280a0beb168365211ade91ea42ba286197f6402f15b4ad77dee8add72874f929838fabe15403df44c28c4ab0681656fd581e269573ad97c12385d1b070b52d6cdee97cb1409e27075c5f14de0bca0da5f5112c70f8b5b1838006fd021b98a0ca5ae4d77cde531d38c81700e7f3f05282a9d6d06464fff247d78ffc38c63c9bb1c14e6420192a324c8bb80bddca735bb7d8a1bc1d11fa3247d3e63e9006de54078140ab2336f8b94b7d03e2a5ceed31af9fbd4b0da3477dd736c361f38ca74b2bee18cdf5f63f6a8ffd04a1990d4807360ab4524f651103f015604875f9734b5bdccf9ce3afb4620f1d75ce960631852356504e6464b4b217a0c6c2652442ce691469c6057edd4c8d92d5424c93ef0399e6d18a2f6709365038eb8c87416f7dc1a88979aa6485a45351212c60b4a8acd1cb3ad36004d81152c5a667325350023bfec6fa9fd434b9069323746c83d495df723ff1180a744373b7e037eb12007da2319c5a83c9528246c7a21a753ca3162f2756b814d36b007021f586b6ecebb53b6e6cffc0620ed37c479940d02b077afe0145818ed87329106"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"503beb45cec447edf58a9c78fc94314e3e6e78a61e797f4f95ef205f12bb3c6b","proof":"e81eef693e772530c570f6fedcec47336a4abe8dfe13fe20be95c0edb1cb812546b0ce933fbcbb5d836816744c92ac3dec7948f13a7cb1131cce25bf235c7638923a75ec1fad841ec78d6e934d4cf69915a91a6822269a9d3f6b72b0d9647c39fa18545d08d348137034392f6e524abcdf13eba37a9b31a0c0183fd01ea5ef3ab5b28ad13eed24bd9bdf97017177f935787d1dc1966443baad012e0247c8dc002ce465cbfaeef6b4ed24f15ae42b0bf7b3ebb3bfb62fe348140417da3df93e0d0c68679713e58ccc05853a90e5dbe958b5549bc5ec48436e0a333f34521bbd055c6620bceb244d1ec10f3375dcdd0c2391d79d9c25a8c6f70515163dbc896d5c9274a4d7cee98d2982a6b94ae9d04dc637ce5593621f54728f47e31575222372bc2b2ed8944899bb4530cc70c02e9460eca5108f4256848e93c8e5a72f61d4523479df2289ac07d8deeab70894590698d304a86eb24ac4230d9a3fe83060a316f63945ab7b49bcf1c84880b16631da8fece7f26c7241868e867c2ba2087a8c7b74db1ccf25e1a700de8c3eb7e975517ed647006287cd8ed73cf93102203b04521217e464493dc564b4397d28faeade4790229cde9d2e094b7b59c7d6d26e0215b81041dc8e30fe5e006e1c9e3563b17089d455a3256561f24aff1d68189fe5667ee26cf3ea71e805e757f3d365f3cacff275655a19303c3dee746e12f571ec66eee4731cab5682bfa6961bf7012ad4e263b9fc5609aeafdad98996812b8cd33acc74191a18402ea31eb6c3c917ac27bee29c610a971742831a2e58a42babdf6b3cb41b11c37e323878d5c670d5dfbfafa18dfbe50472cd60555ed2917383834204b06ed8b3083753b5dd9fcc35ed006eb6309363b3df3e6186f90c1f84b9360577082b9315f682656c31781599083aaf993df213a93a8c66fc05cfad7233f30e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a8c2d5311a6928dae350819d543dd7340603f82b50d8e3d516a100d936724f05","proof":"187cd3f25ee5478963da7a920b6fd8d07f52b4e5c8a4e7567d297f3ec4852e743c6a578b3bda4ad1d43b92e8b43bd4f66bee512506dc9a42681b163e2c4bb91f48ccab1039ba24819d132bbb6196ccee527a6a36e270259f08fc92376430f376c85134902368d9c6b7e48c6b910eeebfad325f400403fc0079feaeca72f80970552611227635b8e2700e8e6a5a73a16b2a552d7dbe95ac1466f8c6254832b103f356c5459fec3e38148b199561bea2cdbce091024286b82b82a27fcff96ce8087a1df438dbe279bb52317c5d428eb79d5e252ef622e9f4f4d4905d5e36ca48037a5972e1d10fddc68dfe0f1121912b292a80a338983cc73ca1701d0559eaf379c879027337bc6883c738f8dc5a58b81bcdd6629248a76c70a93f5aefb639287064d3dbcc50be26284b39436d17089042d5b22c1b705beb8ed10a7570cb3bf65294a26c07f6c59edb222075263ee0cd972860c76548410d6b1a4959b9ec7086795ef14fa43fac3550954ee82b005d4a5238269bebffb17bf36f56de90bfb9cb554a08d4015599169e8a0de806b6c7dc58ec01b672018ed580b70333771825376924fce052afeb49576b66a700f8e981a6c77c1e2247908d6e1666969d8a6d3f570a1ed0eed145c754183d6d12526dee258e683a494c78e87ac969921218c22b56cc610e7abcd137e829cd73e0bd1eab904e9185169ac92e111ef202639047974c0ce30b0345d7a64a95a74b624d8a1d221e50bce861a622b08d712ceedd1762335069f7cef93cf36967f6c93e0c68b83446f7e34bf34c45544b0c6845e9b2c96b9e56506a93308e8e4003715ac1df984985f0833c0043a56afcb7eb9f82eccb1701882f8f4b87078d8e16eb914a207282cf852891c14a9029a5d91c1774a1e70219ac3e34835fe46e67fffa93767600604d3b309aa906db68f43bd84c2f5e2c05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ec73af95724d27c89b36d4b72ca1b55e0e40d5f4c23b15269caf9854d6190064","proof":"009e8e83b0f963087abcd5987190ab20ee9f1b4576a4b7a05b9cb2423d2e2a519006fc7f422073e2f09f6659e186b257146e1812a6a6489e9891450310a60601403525ee2b372543b8694ea1b06e3ae129f456001c3acb81c7c1f2e9377ef22a12cba6f80b88af7da8227c905cf48805a7e01e8c9405da67acf26d6d2a10f219eb12d03d1da3913afc22f19cc8116fc662d382d6a857e3034ef61cd56c1f3d0a7d5450c7b02dd25e23f88e81decefdfd848c25c2533c715ff71a1a992aab950d285bc3ffa5f2b475ebb617e7f359ac0fb8fa8eac4e3972d150b9771edcb0c9085638693f99522f45bc294d2e703abd5ee3ddbea06da175e68898a1ebafe7875748762ea0368706756f73c8eafa28eb5e28453ecd9bc6d658b5cdcc90ec65773f76b397c22dfad8dd698f6ba42702df6f6112c2b8b750ae47378ecb64ae76c45332d35f39fa30d6a4b70f6a0c31a42939e9ed4fabc4e68dcc49358ffe34fd8913ce340eb6702341bf9a0c337720db91168245a28953038429ad273e810fa79778028bc69f2f065dbb55037cb78164c839135b07c4118d784af29229a7650b616c4af9118cd5177a34db987655741ee8c021ca04d850903d473a3ae2064f71f60f7c2b18915c6b564de7f7b02edbb6a82bb299c2d2077033034cd683eac690a73a844f38996c7ef1d8c3e406d1ac5a136caf0ed81bf58f5eaa8e2eb19527e49b214c9d26082a03a6c99c2c69e2f0b408e4f07c3be20b12162ab0270975451ff00a3852993c327964a259287b7564a8c01a5e094dc309f4dd9eda63644d16300f1e06788ab54d9004b941eb3f4a59238bc410135ce79552eaefcba0d9441e99cf541a5fc6d682754772bed5235207c09a9a5019afebe8df0ad58a425eea56ad250f47fb0778ea007a21f69d62c840bfb52473b97c7f8253ef5b3ee7c8c5b04b0f0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"34a6f340bb57069398118e840d2188720721c51d8cf53cc0c0cff9c5ae3ffb50","proof":"54b7d13c57389f17636d04185ca4ece9a5636a010f27ce361ef59be17cce54675ad6de21b5426a04d55b494d590666c4bdeb9fa695d87c54af29b551aaf8ae4d1c5fc6c8fbe3b086c13eead8192c6d3d3d3a48934fc8798add4b591f9423d72ee8d62b3a260e89cd4815722b9ed64751d33a1e5ece47e3a4874b7d2cd33378462a6e65a27704c123368ea7d696a2e5d07d38835bbfa8f93e65b3467ea721160fce850ee3c3afccf8df11a2f4e252792900ec486cd57c2a416c18f7f3646cba020a8e6ec02becade75b71496ca812c83c102961aeeea8a6916dfeaee7fc969705a0af17182af7e422cd3d2332c4acda294ebae226f6b045bc75e3e5805ffe4b1f8cae5a962f9a2e9fcb8018a46d29c30b343b7bea86570b06acd017025242576bf651e0e268d001d120dd2e3b90bd90472204fcab2987135284640a148ac79736ce44e54d25e3925ee8aa15bb9977af42a286db9e650a560178408dea217318331e2681c26baed809db8a8efeab7a7c776b2e41079cbe37523e3f5bd5cfa248461a08926349f99c5d741f2e5cfc892fc2be970fe2b5dbb8e4119bb66b19b16e753a538562fcec97ee183d0d831f8f17e7874dd98b17b3025b2d5ae7658610143b606427ba6b19d5f75aa510aff514e61b8c218fdff1fd16643762e426e89f1c5da49300fe217b5e20b2f60828b6fe8024c9ce64c3fb7cf80c04388b1778eb0e28f8966e8300168c22e41f759172ed7c5dcb1fbcf371ffb806d485ee5c38e5b04d1674f377864240948da4c77b357fb0b86d7d75df363f30521deb885d96bc4a37765647309952b46620518abec099cd2d9fa5bb3b48936870622991a5202b7f2afb7a51a8870a43616934a1c2d63e5a4967b44417ebba3965267b4bbe8f705502fad6a3271d2072a9b4dc4552f89e5c81d1bc166357e479ee79c6bd7949875a0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9456c3b30e11be0620ff8ac9476b17d27daabe3fbf549c9cbc618935c70f1750","proof":"547aaec5b7ccfb1097f1c8db14addd0e522e519df0118d9bac18aa713cfb1844dc1651425d07d04043ed9cb68ee248238d071c05c723d47173e9615c489b2d73a0b128c8a0d4b531141fc823841aa1b4064ab4a2215e3f594648f8dcc95dbd403a1a009d5a3070ec5867e1731eebc94f8e0e8518b344c568cef4811eb5fa6e6b64e87a9b08f4c65cf217d9eff76fac35063c1760a2239a13b8f5686dd14db207ad383679d2d3d06aa90882b58dbc6d857531242a37659d817286766ef676a20c5696d9d24bfbab15f5d8498887373fb4341de5cca1200f11d0bd4bcce2efac005650b2b3e2460bff60f0e069d82129d7da04d75d25cd0deef27bee502a9356145c990592567fde78a6811bea8988d7180e0cc8647e88f16b832a542c88805e36a8a28780702fa21e662a56f7cd23de7e274bb8511b769a18b8cfdf2e3af99224205893393c83a50d9d0fee598b52e37995c47a909a6e2207f90c193fd0f45a0e6edffe3f75737e9563957ae8c788852a0f33fa6788a2158884c4c2e7b4bc824580952fcae7d67a7c71a16bc8d41c41558b26ec3ce31ce6e00a507aea4b150355fe0d74664e0cf3b0b8659ee34d0a605c21a848ff7efa389b2a4f9daef383a727386b0adccafb830b70e358763fecf1797dd98193b0cfe71b79528977c1cc786c8660e97a50ab1bd86f7a4bd4ead66a47d45e063b6a2fc7ab0291398a13eb39593851e3b8c562f6effb4acb585511e710f00813a979a9c9189c32b00168b0df17c03d83bb7fab9e7452d672dde55d684ba95690a89678b8eea86767e6d09dd377667e880af3bcd4756a13bcc48920038ee739e5de2b482731d4b03a03e6d9b77a9192834d940e2f4e73a64ae1df2f5f79b2bf199cf52c8e5a4547fa367136070d27982e333749ab8076d06b8ad6c0375e479827cd39b24c0c3ed6959e88beaf0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7e73769165489a768c2b12cd289c1d0b3b5162790678f692cf6d6e1f9d007e7e","proof":"b2ced972cde6cc992ea865ae440aaa0d01cac92ca351dd9f0210fa0ee8ec324766edf020025ec1552dfe6f6c5af18f7286c0718b6786a817d4d7c67a6301ab2c62ac4d5a4df6485c874a79ecabc60a96a155b84c7ff530cdc36604b1a429934006fd0a582292f40f6a11edd651e670fdae768d8647a8b3babbb0deb7692d0b2ed20df75b31312b1c9b7e17ce67042dc9450ce75b900294c5e052225cc47749059bbae245beb647c1997c637e8d284f761494d6f35b7a6c354af34e61ca0c620e956dcf958bd0c81e40c1109ea973a1d1e8c3d2713617370f2431235c5efec30fd243982e80c6c3b794f989955b1f01d18e0e7994f65b374f299dc48791215f19e6b3ae1c11952ea10017880427963204af202de8b6739c7d50d2c2c2111325239a058dc8f981c4935cf037804fe67e8aa9fdfd0fb0afed3813bea2347049514d9e7738ec2f00631aa8aa3bcddc51c4f84c7a8302504dac87991f65b605bfdc56feaa03ce98771d098752ad4449d945c7761ecdf292eac8a9613747365336bd5aba372e5bbfbdf10fdcb8f038c7f76ddda7b7e7cc751dcaad8774895c9078c77daa963c847dd09fdab2a0c46916af537673546dc468798f0549556448363e0c77268863f26b0c81a80c51dc9de0671639c1bc4ea46392b5be98df2d32cbfe7438c0edbc116f7241d9ed0ad351c7d554ce3a6fc96ec552a42a0b8434a019cf2a2d0866a2b0259f799cb6e18b62497d879e8873786cf4a7e6dae780b2dc93da300c4459a6b484446c65549285e0e8a2387391dd1e8efeeb86d9685f1c3c718eff0910e2ba7edc7d2d30c265405f3f31ca805f7839ec3f09b910aa231acb6f52b06601d7138546d1f9d81ce1e28ed92b22ef1f79d4831c0d020c2f98a6cbb2a63c098479171f2ffafef86d91883984b2aa4a1efd863d455d8961f84a0e93346acd01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0898473a983dff4a37f3f46233ae19f6eca7826fe68a9655aee465427eb2f02f","proof":"2ce5a159668844c67eece42908fb397c49b8fc5cf231dcb2d1fbde896aca8b32981451e23144000a53ba070ecc179e8587fa9575a798f385d615d8e8dff47a67cec94798b9a7ab84f8cb750a9590c3119931d00e4498feebc1e27c94237fca404c75936a14c261bf80ce5bc58c72c4b8dffc01473b7c99fd6e512d7863c49c389241ecc2460f089c3347be0e22e746cbdca047815b31fc58318690a6532b5209f7dfa85958d5d990fcfc10332a15fbba05795910ff3260b49553e6267b4ccb071ab7bf690083ba9b716f19e299f77accaa70c439884a1978b1eab686644ca106602ed0654762b2419c0e90a8865922e59d40a97f855496ac6d750f8537411c4c080acdad2ca01d980c9113d9bacdd8b1e9f0b51f758346160869f4390bec19459e790bf330b50b4ea9539e4220f90f4bfc0a2c71ccddd61dfdb12bd95395dc00daa75e8f36727b91b89571e75bbee179b5c45b7feacda5a56f44b345c8fc6614383e2395d5a49125fc15d61bc24b08dc665be8cabb4c814ddbc272ca0066c440c0d7b5927941f68baf151b36aaf950200900cb1a0f91e3ac03c45f134374b3736ede14e3ff9c29253f73594dd227734ee9f1f177811cebe53d011decd56f720cc4c0d12d9932eba7ab7913fe050e05ee7ea71327a33708679399fa86da3f5f53e2289ffc2c43af1fcef1f97a3f9ae14c4a818fe7a5247e5ce3f226da1d0ce30c82ee06ae18ce09ec27eec9ce3cedf21298750de7d551af525d84fa162433056736fc8450f9f099201be379a49f975335f1cfc43ba9a5fdad351ec96a0f97de1c3e2d37d62db477e7b2e9b846cc0ff892b338fd4ae63c74419c8b82aa0055fd71620ddca1a87ad23098be9cc7c9dc1dc5deb7f42196aae2bcd28979db18c6b700d18bdc39efacfb4ec3d1602029b4b8308382f0a4a436aef1c6fa064219297902"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6a6ab9154a163c38942c188dcf0c9dc8b4ed43d2ed76355e904fd7de5855f846","proof":"92336e4f2d2b05ce8e6c31ec1c78e26aebde40e7f85acccaa6ac71e4f1d1db551e3fa692923f3845a651a9aa9f820338bc12c7c304b5b56f0b4bbae55c2b0476d85d0927ae5f8142ae34e9656d6ff8f8a254210b3080924622cea48fe8628d70b840458d4e4c8d5e27eaed12428ce27de2de36eb42dcc014a256a5c786acf176e1f6502622816b0706b9a804b3fc926c4dd25e2f1e12fa5a6663eb7bdb7e110dd721b5e641e6ab6f5c7a90b3499225d7016817b729bcfc4a36b94d9fdfd2c10a59d2e808671b3f2deb6e1756feea99bcdb7906ef4c2455cee4048c4cf824d205460aeea92ae4d0aa1f867523553d9f87de0ff9e151556978996544f82e8af45c841d31f37c4a6cf5896a28a1ef95168d5b8a3e8e3b5af7bf989d194c7ad09b33163b02c66fad4f9e709c7ba65d8775baf1d325087d188ebfd2d6b0c9fe4c307a4a60bde07986c0f83c40446ae42ad87caa6b74b9ea9da34860d9fe09f8b83d33daa222159e1b3418539025ab73ff57a46d81722030a3b158f55586ac302fdb264271bba0631783bc87082b9eeb693ece0912d0da2ec7040dd53584680729d238b4217a2cafa053c71ff05614de7ab547b208951cd041f480e3868d374fc6a8765882fb08367ae7d602f5eebcb2c241253ae9ef33b457f318b0d489e55990157b42330720687c6a0f8aadf59dbd2ec070d133183c2aa3e0d2f15b51bb7a015f60a234f3a94f0b778f6b29e29b2eae584e2b1148932ecb62ca578192cd561f2a01d6d40343badcbd39c02b6c3d1b9c625e5b77dff575b1ad7d94ecee4521d50b1732fe8f6b706af1a619c0a0786079413a76def40298c43c16d76efbc206959c27d2a505541468bacced472578ece5a6c0ca39f30750bee0b31880690722c1300fe0f5d6f2cf868f310c4c099283e611539a024651e30a7841d2064d570fb54d01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"264ea53d5e52775cc3bb04e28e376c822007913d87e685b30887c4a07284066b","proof":"9ab2f40f9bac7d94d288adab63650b4d64f6c6ddc7bd52639e936e07b3079908ea89a2f7adfee531e485935a23a8ca902603a9b6485106568bacc37d2798a751381afacf5a8284c42b61b488c35a81541ef597b18e54e1f6e4fb7382c14f532ea0ca4e492bbc3576f7b55860dbdc7faed2edbc4d7ccf795393eef0d4a0269a6862fca5d74213f50d75af87c30de7f0ae261fcc005c26228c373851da59c4fe091efb85a54b76019c6b1f92001ffc94ca76486a010f8b48de3905051276a8c408d7a62dd1d416b2628370ae6cc3ba3a0f5d6309e6853e6c7c9f1831f3a8f0aa078e5528e08edd9f698c95bd5d67818975451d9c8577bebd1d932ebd938cb0936c66777a72c6c533432dd8bd6950518d61e21f7cd40aae4ba51917d3dfd76fd02208cfeaff33e1451413414f7c2fd338f8a3ac225b3ef1b8b5b706e42bf86d4c5e382d231cd3d1a26d2dc948b7a1879460076b8d765eca4729416bee8a6d2f2a0c80eb4ecc76b7b6f59918f1837f10afb1dabe1d69cc3d43ff5a0b2ae55dfcb801460da2deb392fb51bf8841cc4d616f4bc6bd7fdba4c6a024597b8b56bcdaf73ce443d2d22ad93fe722eb772038c6b2870cb4870908842ba5a9982a56ae68647ecace82e7b492e3823b4d9fb99bc8119ef62d52d2f753dc692ef74001bcd7500c48e3db667bc5d8a9fb1cc4a5bafea7973f811990f03c904984672765b68e5024ee16922831f83bc66a5dc495cb37eaa565aa8db8e4c2537bd1ef725f8d2ea65a74b12b70ab242121568c0a04949d628efe1dde66244948e3a83fc0aa746aee24d468a89b8eca62cbe7213b5f8b56e26a885eccb397c199eaea3d04796746bc425b672dbf105071e4ff223be9e9cda9d629af6f42573a5697ce3684b86ad49a04efe65e7c5692fb6b406b921d271a5dae72a2bccb95bc074d0e52d14fce56ba01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"22e7f9ecb8c7a82176ec2d6060be286405df45be1aaa07ee7f5f78355b97f23e","proof":"6a4d2f5150b8a9d26e4b83416c67e6ce254b3810467e02773d99bd5ffefcfc63ac1fc695c5c35a1f332734c6bba5dde3be4d8a78f4391dacacba977c483316249a27f52b25f8eae974f30a123f6bd49715c1dcfba870a16fe4982d9d827ef246de25d0acdde547d48046f40505082e5be5bf68837be466ac11f87f285cdd5701702111ef0def85a0f2a3cb1db694347ea7fe36b54a822b72df2e61ba2e5c290f3cfd94e2d3c9cdbe6bd8936436d476c2ffbc1c416d0b429a49f4555405aede09507fec22c9baa3c70c738563eded4d0eaf757c31c3647190a31bad8e8a25080586ce56516ab9de033020adcf6d2c0b3cb77aa5a2b4732c6052e387cca27637472aa75b5fb34b82cc02f32c582117d5966c3cd120128c57264cbe959ea8c74004f44bcdcc604b4c075fce3baf9ad21fb7f5dd659ac6d137d065bd0cb364d92d4876a651ff184c3281ffa31cd9b24dfba33c17970d61e87d41f945803e7e2bc678eced2e19b7f87bf71fcfedb023e13086ca46c66eb650882eb1b821b30eb71e35f8f3ad307bc9d9efe60ec271bf432fa6cef41d500dfb201fd4dbed0f9e4bf23cf28dd33be27f5065d78b45389d33cf3bf94583a9fbded03edcf636dc538cde323075f27b2007d90c0c0c512ec512b965c275c0e6940d8e30f79cee4cda65f70952b33e9bbd2cf7a73d1a05a6d7e01b26d6f1094a690a8832c73eb6c38513245c08f2c49b16a35ffa19fd7e5cf921dea7b21996b7387e3faa0fb48e0fb7b3c65f0230df41fa07947229decfb818fdd849d54ace669a7848215f9e8d1b079531762471df9cbe76362cb52431421129a384546a9da38835fffc28f1cec5a6bafe176e7f31661e1c262f339cec0894f18bd2f0fca8e772fd5825a49eb834dcfa4e06b9101b1f1b1028bee7ea344a9aa1ea5074043b66842cc9a307eee13abcd20f08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1256deae33e67ce620cde8d78702830f7e0749a555b0920fd09fd2427827af6b","proof":"883d355d5e09063956857ba1186d2f4d90247d3c7694bac91721eaa244441f5832991cb22209123bd896bb131a18bde6386131e509486fb661ff8668ea9b67468a328d973cf99185e4fbc74da74a9573e081014ffa7ce824ef87345cdeb8aa392262188b7df431c5583554e0a3d3bd056c2cec68f0b86b32aec3c1e3ee56ef7b7b7b4d662284c16014fc6807618a2aba1e960023ac73ea021351401647170e04b1bd80757f91db6bfde5188e02a30486616d189f10c0b127cdb15f580e20190b74e1b68016a334066c975dd1e82aa538a1deb416b0e3ebb9d704cadfb8d86000c4f2ecf98e5f3af992fc6d9f14b9bd3a3fb7a2d2077f1766a23dbda24deab920d838875b5bb02b598ee4a133dc7e8eaa85f62837f0ae6011430684fb516545372a9295a51dddf1420b5ff91a893a2d0c0e1d87363fadd249e519d249af6648088a1cb5706fbab7a2c819264a1406b606482a068ffe3262c800133a85f2c6e239ae084577f6dffa410f64f32d39007310cbd987036fc6a7c38930724cd8a43a72221ff71da5e3e775c6223e8dcf2e2bd1f68a008eda7a1b3d0ad1af0c0ac24068069a662aa7a5803b86cc2b2048886ae71e497b97a3d81672cf24ac4f309f2d6388e962fd25ff6ec63b01f917a698584dc2fe0caff2e508084542d174a2a64d66aea6da5de3701d809b649b5c08eeb2c5f12aee73f7e5e193a83e3369f551010a888b067841221dcff483a52dac176863313fa1f1285437afc23ceb47c2ce2204f855ef5340068664ac22063a1cdca417062c316b84cd79ee812b7d511068bd07c8ef0b7d19bbcbddb240732424975914cf97987cf7d4beaffcf46afcee7a9a344be363fe02b1c07bdb1a6b42bc27d772025aea4f315d2dcb3fcdc0167c576c0176315bdb063cc25d53c52b4b38a05e0cce1fb35a2928e63f6be3db3ab0d5f801"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fcace6e761449d8b373f557ab5eed9d95ae0f6e2c0814fdee8a121129b343172","proof":"b21745645ba57b0bfe2cd8701e56570d5d094e2166a6e2bcc412acd29820c76cfcdfbc71b962d6f9ff9d5249f0453f77cd9379676736fccd96c333e23bfc6228c65b0eddd0164eb19b07f6b6b8f2af166e4f7f89b72708392ea21f55a89bf64bda71c0387321aaead85a716c51bca27bd001a22484c50374e5dc4b74f6dae807f25ad23980a66cd3bcae39d181a45ec7fef29a9d46461147fc4450f7cfa39f0e179eec4ddd47abb45ebe1564b1e89808d762a5663a15430bcd4c01684ccbb502d1733ef6ef4464b825c2ade4fea6e381ca0c7953fcfb9972282e0677c794cc0ed63a2c2535b9c6d438653c941cfc3c0b2ed5966497abe0b5f49296b845e8f04cd89d6b19f1c17ecd8316f24c7d22838172c9790567a1271fabb5dfb3f340047c249fb991f0451f6d34d5f89f481b201b1ebf5cf3d8cbd76e384084f33bd30060f83228f35ea4c649de2a908369dc066db9fe414347e0f7fa2dd3cf1ce0d5a6323eeaf0c7c7b5981952398eefe8c7d1bec868f615c126d5d6d4e9907e197d3a0532e6d7dd624ae447a370398b13ea2ee5b41d52f02f428d6f6002254bb8590d141e8dd70d6c2323b0430b57042fd8398e29528df99e938ce65f70ccd4efc4126b42cb9155d19b64a64485143d8a15c995b3b4c6fd9a5d757b3cc4dacb9a18e1554862f3d8241f474cf7735c3a1197c64d470dd20e98669279b8e96dd30fe7c12f44025df4fb15cab19cf12fb5dde7a197db660664a287ca1b1179a59ede88233f54416d0da56ae9ad7909953a2707e6d4ea464dfdaf05c7e0cf1fa0a6ee2c4c2c0a6b66a9d16c6e1499dce3c2a36a2705e8378e1395ea00c26f29aba751374454b7e5bb661a6c3672ba24326c5a121cad7f19a485e0ae07395960cad953fded00f30678c0c25faad97627aba9cc3d1f0a2de0c28b1b7d9f605418d0b923904f00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1e8ee74f143f0ce727300669082f81469ab8cf54238076d786ce86db8041731d","proof":"e821f30cf83f5cca393713fb51ba36e7cd8b0d481fbecf0e247c2969b4a5970752fb2ab6b2b87f36347fcd5a6536eff31bb4b2e28d42e5571eca630bce2b864a4edc58c162be3f664100c7cd6f77d751bc540c7f7f0ebd31c7b26a355a8bd6631629a2f64cef173e6ebcbb2ec56813b4797934c8d2fa8943e210a051d5319b4f47338ce7680c0c229bea0c951bf7c2322acadb609eca435833725f8d4bc64d0707744a859eb9805b594062e3c20bb7a2bc17e4d5bf03cab7e8d01b1fe54d030d06f3640bf9b95e3785b8628c2ac0006b4aeca67365941f58a195e7c7fc8fbf015ab4d879196469b8905a81f0d34f42571c76761ea2c83e161a5ab2dc4cfec35b3cc2892f8ee340fbe7c1b5c2b0aa7ba57d892c562843d820a118ebd7fcec661880e273306b31229d7dc722890fe1a0849f3617c3bf2b4ab61d7567375db8dd11c252320af050f94ad9fde4cc0d5b43e7d024dbbe7abf1a5f167820c6c53a363252ea7d82bb46bfaf3797fa0b277ea7ea59f6bd282d01fe700b711c860da7c97246086a548777092bcee70bb2ba97c94bbf8f4d7ab6463540d248fd47abe95e197609f794889243464bac6929db120bf8a084c13bcad06df226fb29df0a91b91b92371a23e123326fa508026ff367e175e18b578b4468732626a3a874b9d08e3a56b2038a06a31d7a3cd3ff4eb745c53105a3c6ca3e304e8fea0d66cb0e1d0b2ff81362ee92770b9ccf446e30f46a8e773ad7ca7988ba6f7fd4490f24018e23021e2ad10010999cb568965ae100be42da5f193546f5df43fc88f465c9dd46ca0b5e30f5e2a6ac91527af02eb2f855f642b3fdf1476c92559eaea882307d8fae7c1d9667ac8163fa2a9c2bfec4b2fd5f64320fec1e36ca3639dcbb7cbaa62b78040d247ad2aa955d1bdda86a04c4399f3ff3d5f4923795585b38b41df49da87608"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9e9ffa9786257e2c5f0a1cf76ecbf9b297dcfee3d31546fb911b518b3e569574","proof":"dc745386aaf7e3351919b01ee18b53b0b8d6540e82b1e1e731203130690f73015cbc95492164a9517a6c27d2db28a64546100ee45fcc228e8cd07198aa20664eb6f343ca10378b9d017535731702f2507a10e09bc18de7ff4f56cc9881ae8b57c8090ce2cf5bb7bd824fbadb5a2fb599ca2d3140f9b0aea34704675cdc4c794311ca46d2e6748893b2ddf0457f73acc9cf065e3074038706d71157a1b1acc8072c602f567bfc5bf48e96fe76d29aded813fc5be9e09b4cfc234c61308510540851c4980f62bfdfb0c3b2487b753e079561b28aab37aaef95ac99b7b951959900d2870718bd6fb21974d47c1f07033b2a1e3571b0436a5b04e49ede5d27444a5b20fddf57d94e6a3df3d068e1e20bdb251f2b0d44133f5e2714359f6933f6e0649632bad0b62b4f5052cfb6e8661fffd9ebf21d982c6373717bd54d6812a1c05a28be650b6fc4a97f4ebef77c19403434ed302cd23d72a7559fcac9e77a6a7a178c781fac76706035dbf320215cd38a6899d6b5b0be2cd795347233eda4e8dc5b7e981bb9be5470a95c54afc41e6f4615d1de679fa3d0b848fe604235e374252f24f035caeeb28944d5ff199ba2d3c3fbaa447f497fa9d0247da5f4b8bd6f640012ffb2d11f69a9a68cdd5f41aa8b9a35c8f657588782fedb93845538d3675a696a04c66e090b146674ffb5e42435dd1e8d56c3902906c1f4acba096c519b880324bec7956b20e458c5a6b57cbcc317377d3b46f09b06c7cf0d75b2d96d70c471d66ebb2b3c82571ae54f84d6127846dcaf61f5115c6c36c0ddd5464c0c3ce901c4c484d51650de2e2a3a863b39f00c535ad25623ce5fab338441b88e1c74b9564f4f052d0161a5b5a8fce222c552da1b6b3a5cbb0dbcd21e9f3e9a9397dfb104a1186bcd3dfb503af6902cbafb3d228ba81bdb133dbd87d3bc43d8cc9d45e702"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aa20027e55fe8226ad87e696662cb00e5705eac235513c370403034664cbe045","proof":"68bbcda393b85cfce74739cfef7471cde9b576e604de69334f47180452ba9a02b8f17632f748c1d9dbaa72b13234bb86d95015b89a812b455427f1b967f65818b0e48c25fe2f90761057cad784e7ae9b0d2b22f8d01c8172444b3daeaf5ba077342a807d316aa95947fae01c92c04f330d239968f9a440f666d1313e9dbc70076158c672b8c352a3f22f5080cb199a71beabaf380b9d6e333cbb5ebb26b0fd039cf25d6fe88a7d96f0998e8277471625b1aeea0feecb9a28781eeb0ac1636a0e363cba060dc99fd00728ccf1ab7aba267183f6fb9c07dbf607ea6eaf8e9cef07740c98e4418af8a87cea3510c2a0f55283cc082b02f95d79de1b4a8d18d4e956ca39b24ed61afa8a0aae6d8e81e416807b3787e3f3caee9b9290f601f0545846b4be3e730870a91b0b8ca5a55cbea8df88ffcae23ab95e8dc5878bfb23c2510b683715d8ed3c78e899caa6377b88b4e387ab3b868b596633f64fb493a39bcb16d0f011c2f646ee779ab44aa1735bda8b7c4cdd42c9fa5fee29e80256c95fd75f6e81ff6fb3b8c87f4fa9b0fca73d6be877217e2a6dfd98f51c8c88191094bf6ca6d3f67f7fddd4acfdbdf530acd7199727784750702e2da0e50f1a551e942e04b2c67f2d2b9fc7f0f4015bc6d322e4c36188580c092eef1d2d5a65207ddc5819f2d27dc38559813c0d25403b9a4a0059faf85f168f4395bfb9878ba42316c062be974180604fbfed1edda616f2e71f46d2ee7e6b4966664c8a6148d9b7db297b9038569f807344c82e3620b34ffcad741cbb61ee5ee3ffdd9b3e74107000e026de344ae45172ec32cd443f9ce6e9e0c50a293428836a023d92aa0124be28163e5cdf3b1b480e5f5198f209da799a3f573b5637dc1f4a3863c0ef49a873c96501d7d6db80134d51b9d22edbe12dc2b1f7385c95a090fe12289824e2ed0a306a04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ba0d4822ca72c3de84ebbc0bded84b5a67d46a52dd8ebb305defe49b65ab3175","proof":"7829b0b899304c6b9d447aafed901ba4bbabcc61fbe25976b4f39b85447e3204aef21bd639fb5594ca5f11071a9aee78fb08edb50f81ec580192a5ff1b9e57448c12d2e82ded66d001eccb3bf134db2e8f602ffb0e5cd496feefa1ff26c7d233de5d55344cb7ed2d67d2396e732142ed875932b20fbffbc1abed0b2d2141ea22433002f4cbaaaad2d75323a0684529b4ba9ff8704e40b2c92683f18f6b9a7b062952bb7e70f3ed3a54507c988d4860ed89432e93a8dabc710fb1f149a7ea7c0141a5bbd8d316a6175dd03eea15c6e7328df253e55890e7faf4e1e0a47cbe3804fe78837dc9a28843b5a9ee55b20e6f0a076036dafaae07e18a298612700b9c0cda093607054639da489724fa9a457cb358885d29378ddbb04a0f77e17ba67964c0ca1cdfb8271ccf9e09230380fa2a268138cfd5ee7cc791888646665f1df71cca1f86803e03392ad1decac611a15b160d39ff948af71df240485d360f5d3c32942d8179531296bfca3659c0930faad227e31ad357dadd057d0e036546beed15903f929103a22b8ac393367133ffc4c45d5ed1801dc862df5fb05b6a29551b6a101de0b80c8ff78b1a4b54136b0688e718bb8833b0c970265e68348c4f40d60ebcf765578b1144e0eaf5000a8470095d8f9076d577825b5fb56602fc506288667268b151310ea509811679cbf1b43909419145fade9338aa9de4869ec982c8050e80fa4973c368d356c9ce811e01d09e170981d89d425ad8ba84282cf1ef3f0cd20be2947953da3276ebe0a34962084add95f343afb8859349cd3dc2c59a792ca67b3b287cffb027c6086b535d2924a5e2eac0dc03736fba8bb61a9543b3483e350818d85f1247ed6fe9cb3809e8617f4f57b3cc3db0de7b208516892e84d30ea7eb4d7be8ede865011fa8513e3b4664c9707bb88a43d5ac00b152a719e6fb0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c8661a10ff650773d2c1b14483f782f92e282bee3bffc9a6b6ee637547339165","proof":"d85a59839b8ecebfe1d91fa828d3dde645e47897e697ab04ecc9cdd2abdcf4435ed84de63d8d4cd66ad06fcc404521dd4c806f6a1daaa3fa3961811937f2fe347ac80938bf807b0d865501d24476adff566b6e84d0d4c60f746420121c4b401a22cadd02914860e5241d941d47a4f0126795c1fe3b800bcf7bc748d305965b1bbd8772bc9b84356c747c64f60adfab4caccbffbd60de1b4c7bbd078567a7f30e386b22093489f7b32a48f0fb6cca665037f339264c3946cf66e647a1adfa570b047ba6d2681e629a8274def1ae6c20c59d747b8ee483e8db8d084fc5677beb0b3635f589dc132207ba00aaf3011c2c3e99d67f45676857f72d43e741ecbd117f70c2f24e7b34490c66045108579120d11347c4f59b7933f14fb321efb670e4257eb84997e285eba2c1fe95f9fac8bd6886c442a6c86a52676304c332f9c75b462034fd385bdd1e8c5cffd92b6c1c316f42c4984031a2e11f37130808ed63fc7044a5277f350eb8d92c520526544e75a87be0546c13193aa55b8819f67eabb260f4189f66e338727c985d418bc606ec5e57daf1f087bc814359a592669b2790158e530637898cb20cfaf85c6eed4d03c6d070615cc0c0bdc232a0c6e52830140852d76d1b4f77d5cf1faeec5386519189a9e09ec0454c9b9c04e432e6f44340073416117b0e2c415505c6e691a9148b6322afc86b314d8de676efd76ef2c44c0c3abd02c2ad1939d8f8470f4de03440d3061b5d3a7479523c27ab10737d82b43e8ec0a121e317d48a8a8dca7b77e4f73bcb8fda6a341fed07a390afe857733808e2ddbaf1e4e72ccb07f067b81f5b6705b7c9ee8ce9b4811b009d0e13b27c7c6d403ba3590b68094d291ee570c8258dd3d04621a7f4ff23e78808986d4d5e7c0cca74f58eb4045eba5a4c1139d05d703d47efb81be7d711287ca76de530d0d907"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a012a15fed2d040db414ebd5b6a031292f54d35337d9d12856eb38bc15c9dc6e","proof":"1480ab46876aaea1e0ede9797e0c82ee1f8fa96caf4bba0c2c5bf838b1788c015ac8a349d91673b5ad37dce0d0c1ccf73eff5296d0e554172e4c9cbc373a6e43903e6b851110ffea88c2f288f05352c391d473572105dc51c7d5c2839b5975588cb7c9b4e8ebb6b4005b2bffd6b6b21f1db06c3b9e0a8b54f767b590b15799371b6ce3216a137eaa9b8b00ff7ec12be2deb814ed5336d6c2313527156a427f0a0e042312d9f68adc3c0853e51465f4ecc49e42159cb3333130fb5670e3796204f21f910c3060975792bb0b590f4750297151b5cfb140a66dc3ddedd9f5f61e05aae54f3003416bd981af8b384bcd8737856341f486e2ae53ecbb3fbbcae109098edf49ef2ad1e74be5f0b2973b06b985f5d162db7a0180e5ea670d628bb6264d3a57d3f2034b65eb8498d1d8249976480254be8a7b7e9f1f54ee315de6b58d53dcccefc1b846ad17532c3bd97e2d61bb916fd59db48cae48aa631b46055b761a14a67057b8d8f53dac2c9ea339052292c0bf27d511cf4a9de314a1f5a0616375de5c8b10ef34f5cad7beaafc1efe58ce39f86fbafa692f70d476c44f9c538e289a73db77b347022de0a7543976ee3f5da643f50b4a8f443fc7bdf6885c40357540dfcf7c4c6803e93b94be0e9d6b7797ae2f0072ab0ec88d41d37f85c94b261512f5becce4ab05fd2ef47507aa82aa2c79d5507c0b393c4ec9e845f6c1195f056a1640bcaccac86fc9dd25edd2ee407f3acf3457e8f909b9ef9f1662b040d70ffe84abf85765a1bc2ad17b174a1107c204705afc35a505a01d9b0f4ac73430277a5663d3fadb74c864f93a55d59e7aa6de30d462462093c34b944758a4cd9e6bd652c244dbc4600489d40ed7aa825210c1191fe8532a7fe4ef3510d14403ca00523ccc350fc6863709e834a1bfba832cc55658240186fe5a498692900bcfb10f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c215946c1d88973dcc16a4f67371371cf8080db278dfbf80a29543ee078b7a0e","proof":"6c8e77c8242e06386d842db9824b87f0ff593dd1ea0843dfcff9590cd10901194ea663cccbaf305445f86e7ce7e7f7019cea26fb922e3cca5ec80b7c6c3fe27324af71bcdfbab2841c28851e5120bf56cedba181bae56eb24c35bcfe7253a246f841f0a9be930e4dcd84ccf9d36d4aa87996bc8a6e1bbef1239be79acf5ed7266a0c3ebd8a15f4ca54abb48ceacef69d487da9c5e26f3006c3a2085c8ddc360467fcdd6249247433784686ac52f0ceccbb72ce74f9ed03dd3c75c4296c0cbc032609de0fe7ad445b2409442346a469f537f3a732c28fa8247b1ee39e4188c80ca8cf0eb129aad704d071a7d18bc13d2dae82c0618e8bab36185b92b247e292483e2a9d5caa4f30e141a8eaf0540cafa17eb981132206ba6f348b47ba9c35531f2c41eea18f2ac99673f2e92843b685fe43d63b056fd6bf90c1b65c9c8a118e7a0ef6c8dc84dc2cce8128f04eef6ee37239c47f9d605f1fd3ac16917c89bd9021f6555f2e516e0b35cbbd30772cf0cade841561821448d820b9cdd9bee1bc0d23ec0f3ce4c18f81c1584f642233de36ab7b814647278b38d557b731400978425cfca2a08c17e0461dc55c301eeb1b79dce2a2ed7937abd6bd70814753a30545776e6b23c3ef529d16479edf6e750bbd59f598aba0c45dbe1da425edf94148961998050d09621c61dd894f50012025cd09042828be5e8e340651f7319465ef1f12c4230757c45624614a6061d7fdf24f91e6068aa7ff018e20d8d6500eafaea0062a47f351584750b231952d54502301bde764f16b114c51d38cb58dab5d949d6092cf97c9281bc794fb96b303b767b0254a562b9fa49a47966a6139e7bf5b8c121a50bda5316458b6ad672b41400c19ca4768c507fb95498a0247dbefd1ee8504aef5b18d5719a61b7796aede612e04aae3fc373cfc22bb244d62a2209e0c9007"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"50c616dc10aa954c895772afe541e59b36db10f8a7bb1fb62735dd3eb56bca79","proof":"bcd372c2f07fcf21f708b961bf17c98f7d313d3967307425d7d24d3eb3b56274a02e2e7f979a0781b8ef09f03f0006def241665e9a4abbb47c4987ac2855dd45e4925523e6f9286d76540750e71176e1a8da2ca1a5bffb4fee0864a72d205e4f9616878f72aeb98fabb0391a8ab50af54fb9e679aa481d27de2216b7dc2d6d6c7cb81a0ec57c438cad66860a598c597707fedfb333389a9fd2240ae2eb3fa506334d6dcb1829dd326e70870511ded336172a2b885cb1c6405127d84a01ee5300c9f5e876036a86a05660394da633f7b37db9c6a48fe46554ca96aef326cc7d0a289a4f8b52b88d0c5a598a735b0324cedc34404c2e3f21e33981e38558615c5e545cd31f0cf4ca73792e2fd5e616945a74a71de46e81381a4b068788a54d6f724820be3ad6a3e874a766989ddedcabf3a09c23179e8ff30fd8948d51f1ff9b472e54f67b41f165fa7d463bef4a7881283f8bb28b5c059290dd06f9fbc1891f16c4a4a06c1fe261d1f8ca98582badd7d32ead79ec4592255daa5d816fad992e392a156077844c26185baae69dd5a40ea6df1524b7061ca77062cbc107c79a6332b80943875dc22cb71908e211640fc97719e86aae1ece97c8671130f70da9ea0b561374db73e9a3f892cc3031cfb615df2f3c2c24df2dd78496ca0a735fad502bd0b25c21e05b7e5d13d3cfad875ce6d15a383be9c046fe1679400a0cf6bf5f239af5b526594168f23b3b8746c3e83a3fccafb0f80520bdce61cf2bc8bf848d3afed3134e2de31c0ab0081d546b22783eff93614f973711c670a9b453bbe7860faa2f613c47507bd72d6e0812493d49035d46a33895af666a44fca9d0ff9774446ee6745f6f381c55474fb4e2af46ae5cf1d2130011f0aaf75cb536a96f86a00aa2cd18d24b74c8819ced6689f2044c41d1683802840b6f06bd6cc98300128801"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"caaa0f034e5494491393b620f5f43a69714032c385f5380dc4dc3a0e017a5d3d","proof":"28a6db1f9f684e3575f3cc50e79f7bb33bfb38c93804c45b5c7f63aa791022583a906c6648f44dc8acee1b4ec9d4880dcf62c86129a8ff338c795718529781748c52bd0a269dca92c746da1d936bbcb591f5036a00788ee70fd6c5a009782520dc7e1f264cb0f5efa4c9fb9674c51f2c5e48ce746bda571219bf981ec9bf840aa0e22514b1fe9f722b8dc5beccd7c1bd89a476978dba9489be121578c2d7d50a53b120f7fcca703ec70f1d03d5aaf4e494c1f6f230d7b292aaf1fd8e6bb1980be605e12728a95008abf50f0ebd15308fe89af10333238c2208a3efa5422c4c0050c8c96bac394c9564925b7a2255995e43745a88a51aebe288eef92f39af54309247ed84dfbecf2e3c75554055f685bf0b0438a448b1eaafd0f09ddf3376f3793e860b96b769965ef432eba8ae2ce4288525521f179c51c7cf9b644dfefccb7bfce2b7d9bf6449ff9cbf57a85c6626787c4f6d58b3f9b56526edb4170429772492dac7c383561eeb1aa716ff5f4288132a1129d6f8ecd768b7e77fad7ea6b07f24c509ecc40dea355f4b82e5beddeb8e9a2d2b40c5a1a37cc9258e5bb7112c2fa6fa25638337f8bbcab144cdddc01aa4bfa0cf4d063cb0d80feef9ca5699c30c262140dad30f8e88ddd02ea17497ef01b2efe7ff7998adf0b1031d2bef6dab3136c7f95c910194ef5ec6e754a58bf1b7bde0e16fe44257c37a4a00ad9312c931f86011919f3e34dad34eeafac26aeaaf1b88761363c2dc1b7cbcc14efbfd067460698f5dd34e2b39e3d5fc42d58cc780fa1c74173f32e471522bc0437ad9b515d4eba6efa6dbc5f8f1737eadc90107ed77414410ae58a862d1502221c830243fff7c80562c99878700eb41c2cc3ac0596fbff0a9b8e413fbc0aff099bec87200f9716f6c205d2f3794902146867d434d52a7ab1ac77d575dbaf5d2da61beaa01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"de497e502d3b18c461b959ef15feaa7933abb6d694ad860046d8aa6f7a8d203d","proof":"d6f5a6cf8506ded16605c0e08b79ed5ef3d54791e11adf0193b8b4adb2bd3f1792a7398225f49a51df8cedbc1d25237eef9d68951386832683dffe06e3d6a52364fe297e96f06cb6b8ff74a07dd4e8b5b5a017dac29050ad644a710735b68c0b20f2336980325a94fc6e1ee27b79423c22deed2b5e4bffb246622aa58c0de77588723ac72a145d71360a8c2eb784a6db8d478afba2272067a26920a85c6a0e094cabdbd4caede75c48c1365cde11d9d1108e15d1dd4ca14c670ac5c91815d6083382249ccaebf95485328788bb84eb367251732e123e6975563850ecff992c01c403f08564da76879469d3386b1e4388a1bebd5f72a2169df004522a30ed130c348e32ab2299681cfa90185283774204279bda673837088fa65fa4835ca0384100c0f9c58abcf660f787dcea82fe18d89155c7df595538e73e293f2c044c7327b0e1eb690df7482f5a7b8b89bb7ba5e3ecdfdb3e86c66f67655dac80dbc4134b16d27c7c69f9dfbdfea79418e5e6e038fdc992641ee25b1d057c20cd9d9d6e691427ba41f19ea3689c0564fb42d0a4543682262bd86546c2e988a6f51aa16a1a72a1cb39d0f5b59a74a156fda1b32e3b7e5b469527b32fd2991814a02df0227b9e1cb7bc4335ffd93b7568b673f709faab472b913f065ca6505ae75f8173e27152d38dd89bf4a5cec48e1ca7face542f2aaaa6f135a01acbd698e80a28ba343c509ef885ab800d3f63d4557f8b8e6da93e8b2ea1a690ff194776f39ec1609c1448330d1663b397235b532626f1b144561c2c3b58ddc9c14faf50cd23042c2f093aac3431788f6fb9771135ac11261941245c6f1bf4ee3d61b8bdc7917c51994005038fc91ffe2521f06959ab9af40dd4f81201fe9c460566a68e6f90e27b500e5a22c0621266be80611dd124b1f4177bb7eb883d55fa3d0afbf61460c5d8e707"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"462732338018c0adad2402bdde9c0bb194c6493b6010fa6b5cc0318d7701bf27","proof":"ec93b2d20a1bee183899545503cdeb3ea3afd74c6ac0f42108401e817d8f691c468546e2ef6ba0ddf2626a4fb6976994868a930b770381160943554a60f8a165de172d34f1330d0bda28b54bf0791bdfdb2de37df97a9ea8d4bb035f291bca54ea70f2ac9904c99e1cf8b7cc48b9d809d261e44b589cd2cef5555034d6f27e12f51a477e7319b0fe6d8cf36bc6598577ff57437072fdd29c3ba9901e8b95cf0693e3ec857515ba9dcb9673b4070632971898ca41c448dfc0fdee69d75583780bbf9d77c0baa8abe327e7f41984db2de13be7892d51bf8dde8b8bb2e8ebf73e0cba26803f31a02df3159895deecaec65d50e6085c8bcd3ad05146d8ee6c841c35708d6a619eabe60563e452cf158a3186cd60e4528085191375b2429fff038277364ff4c8f54ac30dbf7044e925b4b5d7f096a35c6ac374d990e3654b61166177ba7148b2d8fb31439b7edc759f0ca4bd92b7b000eeb34974eac9d491783d1f6458e93791c7a44f9b5f5922d029b61d68eb4fafe599a6cdfe748161b90db0bc71beb4d77173c1d217025a1ef7c00f34a324cddebe974a14fef3bf67268f7e335e521cd58d25b3cbb86273ae11009a82e599ca3132a4ab1fa8ea387bbbe156767812459ea01c5aa8f62e79b5f35d5602e79cc9e2ee4ddb0e47692d518792f7f5539208c7d5b2a97917b281a12aa84a85343a12c3e31085544e94b926ec7bb0bb74c8dbfdb69e91b7c12a7b24bf97b8d29bb90ed778773bb0ecb9c23798d372ad4c026b4c905c1643f66b20657eaf5b15cbf04692cce00fb9404ee6bf59be61e87766874109be0fd940b339859bc1478c5f68fc19fe47479a88ac9bf7f150b1f517915dcfd4b969f6252a567f091f888b326dc843a0292409dff1d104071f015c04f536c33a164840b91fa9e4b34fc4ed3c77859e8cb73c3d9cea4882a8b387270c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b8edbb695c6bdb6eec261c81f1956479e4e6ee84d810f0c0239e72f1d3df767a","proof":"aa09ef67d6e2aac4a67beac0cd9cbeb840bbaa584be7fc8db89c428d626b901c7a4859c96b29efaaf47844b126011dda275206ef4f2213d02e64b22babed706736b8929cb2a7fb8a84baac549081c4d2146ed28eec49e7aa79374790e6e6424db6f1674280260653356ecafb73eb8c6546a9bc38eeb512e9b423135e7435261b060160c32732f2fef48c9640fcc484be2f7093919d162133c4f250f7e731050e4d88ded7469594e894d1a64896e0795c0530383213a1504c5a3e442606064301b25a9e7291a4b2864375ffe3c0dd34a33c49806396c7227d85f80f4eb8fdc7011a3e01139e9ef8da7318e1eccb63c12fbac9b508c7d110454102e15ac62ac70856b720c8627d6f0a51c56beecb8a2763c0bbf87bd81f379566061ef8d80f60192e1f5bb759578c143ebcb29c4d8f2e25770f57ba32b02b16d940d8e4f9dc5445309dfc636d45b60f4974440463c1de9bdc90ec059efb402d29b763e34f1af82b5a74d13dc6e906ce16f1aa87414a369dd4b8ecfad3345113a44dd3ee9d53506c86a330fd650ba73e2983099b587f043655b122d4d6d0d5fcf1659ae3ed6d601fe8fcc793f89da9909d731d62ca0e8912dd83aefac765ebbe0cef49f9e1cbf3729863d8a6d8ad4753220f466c05df0db850f9e12ca18430fb766e53635478bb103e8617edd029edf9cc4d47579c2fabe25126533787b3b8dc78c268f5a9fd7f123e43003b946d0cfd214717d7d24d7b854884e3a5a08f970dae441f50858e0e3ba48de329a10a4ccf454019895c507202b2f76f149294b8f810ba846a88e8cf5ea663226da906aedff243865f0e4ec7f535768fb3b28ab21873178ea1381f045fc810b81f639e0aeb9b7742b084e90fc6641a1bf982a1b42168a09d800cbcd4063fbdb71bc1292f46311b4680f71a9dc5a3bb1a5132350ec87cf104cf1f99cb0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6a8947d6473c944dbae5c8cf14f018f2f9fca3401f3bae64e2f1ee9187402c38","proof":"c8bd84d6f85e8904ba69897cb0cf0ec9a7170e129d1c3570bc9969680634da74c66726f99774b205452adcaeae689ff24cd88e72364969c736b9141d96393b7db0b773734254ae0cdbbdecb312b64cd48733af5217c2e7bee276f89ad7ecc56c6c93b0176adcc034d9b5403bcfa524b5434beb51e70f6ef02ab3afd41ef661288e5c8f12dfe5f64c665ee8c04d2d3b52081e162c678d059ccb070c9b26db920408453e7c91a8beb2201ecbc72fa5902724a44524966df2cd476073e68c8ede0824ad4928996db9ce3e93c9b0194822f6c811b60346cf41cb6eb591dca219b80b74eda45d594ab3adf8028f6d014030d21df65167711b6ab6c927b1dbe5a635365ccd64a35ae9d5a5e93d077c8350e6fbd38e28672b11493e00a406baee29e326d8ed7a11d40285fa883711c5b808c89cfe53e6de2104488b7ebc8944b5c08b15ba937962bb24a43ecff0ce58f248dfba205eddb4f435d5eb14a3a02abe973f1cbce6e370b4e77207b8bf88de2aae3e594ec8c17f3e7cbe0becf9d0959b6ce567a48c639388f8eb50e5967b55dba46cb94fa0887a7d6c1c600976f8d21728d47008befbf7ea6128fb8e910bc80b839bb535d49a02bd7b7c05c2547d3904daf0636436a555040346651f2f8ec325e82122157080ca0a0cc20fa17bbc3e9026b63d1e39c6b953e6d0d9dc5f940e2a53d24c75e739149fd43919bd4bebc75cc21d4c3032d53a45962b568bbb60490c176c00374c88866ebe4fe2db44ae411ffa5e6e02b8e3f7d2d9810c902bc7286412eea645355edb4a761e5b6c33249cd4a41b7032f5a49720dd84ba3f48280c1b19fe76c9d6265ec762b6212f571ac77d49ab1068102331965e82f8270b782ba3b09e31e6b340a8f8c4e5410503d5a736abfc0cd49fe522554919b486b238ac83f36dec315861e30e2b5f952c47f54812f21f0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c20646a35f4fd184946c106055665ad7c24c5a80a2ba0ce6c3a065afa95b936c","proof":"46af6911bbf917470f5b51b003dc41a3208931680329f3d3b385e17b1707190caa276a1e835d8734d1539ae40f28814533e1b4c533af4312a39eeb3ca794883d6015e1f7a655387b66dbb21fcb01433604956e3826965247b34e861b670d4a2b96e6926197c0a1ee6e2294e8bbae0c41432a0caa74cf4da46f23dcd08f4f1f6f2f86dc016dbc0d06f329e3f9eb763ef9da39333f1a95faade3affe192a70eb04159efc29579548b6afad436134c63a048d3e370c62474d4e2cc462e46d362f0a9c6b9775b285088d298ddc448b3565a709e61701352c283fae653ce1b08908087054016edd8f82df45604638e699b518eff3c7376d458eb5ccaf78a0affa8f0d8eede87b053289fd448004b83845d6b6b1921d0ef60c405dee43a063daa48b016c737aaf5da6c68ee0d73c520066c032fec004b53e45b41855fa934436887a7d7c121cd8405aca3561ee4054458370f3336a6f4c69bff1973219b31206c8397ba88868f421afc593745bd3f41cb48da1994d41eb96ed7b4e230f259e4b8d5e6ad65a567f41c265c60ed8fb34cf18b5835e22bc9ad3482ada13f0a384a88d110fda8c9ef26a03d5dfe520c3d8b6cae405268d01413ba624c73fdb3cd3b9d38932a487882a893a537b5d0bc09e4aba3d2d66734abb3bccf1e8f8edea87f97b964b7a8089d3574b48173bca2c4e32361e9e0f91661e2257f30afc1c55a7ccb86165f4e6cf0417653635deb9431d100aa87bf7a38c0020c7ad3eaddb703bd0abee151e2d9646fb493233518db88bac7fc97650df71a4c313d404f795857e8e4fa540e6da7bf619112a80f01e2f93d46fede3f619f09384a5d968899e96a1fcaf752a3125da34013631157dbb6205129980977194494b1cc85eb98882ceaf07c9460d97cd10a2bec447249d5aa267b59ee1cf658a45d6a7ce89dfa16a32d1f1109f02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"40e19ce133c21b7059243e44febf9d66833cfdbb74297a55a4d7499fd5326678","proof":"16748b336e537a95c09f8440bbf88b43136b6f52e1088198f6eb899025a9662082136d881bdb5104a28434fe7598084696f7d5fc2d60dcdbf52fdfe04c1c5a0dec8a3cce03f020354a133c07d4c96ee9fc5272ce0700a7dd66fc675e72dd3e39524d39c2e30a1ed306cbe85dab7eab15a583dc3e9173874cb2a7db65ee136715bda7b4469dc3a4166942b8018dd89600e0646b5bcfa63b12e2851e60efcb6b0b237cd2e404f1c7da6fbf2c139be3030827c8bb5d1bb2ef7e39a17ac0af451e033f487ccf66d21150ac081a355324f4490b5f48a0d67e419995d8333caff77f00d43b3eb78c99e891b7991a0ad96a23e1071d1a3977fe99af09460be7bc6c2b7e26ef573a4185786ba733e028f5e3355ce6d0dea880d7cf9174c286ba337e542d205ec4b03e91ac38146b51a6771b9bc9ff0a5773297d81662aefb45329852e39c87689f9532bf5453eef652487ed700838d35c041095e0e6a3957bec1e7754106e833491cb8b71c156fd5bc963a1746057d36d9eb4351a1a13341f876a5ae207e4a4d57b0dd84438be0ab082d90ff40da2976e77e4736c718ad09daa17f36a3f5aeaaf0ae2b1a44fe9e401df428b2b9b23d1654b28cc3324000caa1626ea2b4ba0be17ac4f18afa27942695620b252f340254fc1ac8ffcfa6549dcd2985a61791c512f597f53cbc46e6bbbc0c1669c490428f35b9ca51a383bd03e193c8d844c68fd5b4b50e5c6c45a8d7f5bc993455299c3eb13ac9c47b93903a7888d4f4b2beac57b765f38c513b37f88f765f4aeb4b6d62ee59dc8022ea5047fbe8eaf71471e12af517d52efb82235b43e6aa379ebf23bcd44e80d3f5760c5374e53baab7513269c9c0da0f4c0f3fe6687a265bdf55083c3ba95fc055802bca8f250d527076ca9ea18b24aa4cce081079b13dfca2d7d75d669f9677483f45e5a05301f9300"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"90ba957e80e84133791e9bb11577424a359f04b947833bf8098328acb4de8312","proof":"c492d5745116f906d2a43e05ff129dc107f1a7a12e277067ee0a6cf918904411541fcec91b77d85b4046555e419d646cf4ea70d15eafd21a0247a01a17ce36129a5a73866f2ef994da1023607caeec09edd48a5231899dfba99382da0bec315e42bc0cbb5651ac5c7775166698415b6c3c55ec9ea704556a56840334cb5d4519af5dea5d1cf2c38448cc6548a00733d4d36cef87b0cbacfef3733b21573d810307a4586b3893791e053fb787a7f1b4388e2ca024cd29225ff07cd6a57acb9c0d0f8246b935fa22f8fc28e4ccf5d718945f7d8cff15a5a32ea3cb18fdce6a8c0220cdb3fa43033cdb7b889233d79812655c71a2b8398298ac02833f02a09276040cd5a62891232ddd0ed1cb0372c196e71067af3308557815a3b44e4c04eb1f2f0cc35f135ff8a2bdae471d6a01d5922e79cac4005299469dce944831ce746f346047397fc5bd7e2c8e63b602511057560e4aaa174b85824d5e271b49dbd84b50cac4bd421f89e8b99a4e51e7acd8f0b4ba07f4911c205deaed0c4c01434de322b4c14a6cc524cbd41617dbe0a80b1c7ef35a697fc106f2d9d87fef939a92d1159c3e287a2b9f294a3e9a4b89758db8fedf7d069ebeefe750aee381f977c60c658a5675bac6dc49c1b35e07903e53248e0167eb5b1dc86cea6491af2a3147251bf0b6cf899e8817bf2a6f1dbd3e92fbcdcebf196dd874c1aa3e064f095ad0271daa282d844c4da76ff8ae2ebc21e7302e15d99dd89a49316310127381ee95b63e240eea698ead93c044633bda4fffb02e196f4c7d8305231325a1c38b3cd12930a8ab070a0efdb20a99797cbbdbb6e45cd1828f14b36d148dbd0ea5229755e2451e30d8996603ed1eccd9412d4ea4de10108b2a259ce4d0a690a99765ca539601183568244a8d1907c4fc838c449a6e4ea706f12beb0ab64ec7dfd13f8133d405"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"66f651dd0adac519f6195cc95ce5eeca2bf097db6d959ca2730a62723a444945","proof":"0a535d6d0878d374f5750574f63beb9fab997b01ce7fd6a88fc21a76566b1672d6fa631d880c85b52e799ed13e0bd939999b3e9cb301e4e22820c9b3ec398650b2c0c4994bc6eda2412f381a260f344816d9fb172ce90bb94aaccf3ebddcb76c700183e8c4f0a28466317a7c3b39a6300457847c19ab76b7e99fc736002d062e031879d697a94cf44d6ab232db6fea7664adac13ea0735e960e83fab1297be0924c1a18e9526d2901b43ff796ea32ed879526c915cca84929c89100487207507ce6c9d823543a418306376b2c5cef8ffd87e783491032ae7ac141e39879b560fbcd657ca0d307595acf97630f501511b7deec778615a69d7c163659b0d560705a276c11aa8598d3fddd1561cde1231bd1cd5274463931b1f3584c96ab39bfa2978535a49345f1301a9d0da5d6f089c6f027a871617ececd76f77ed349af9b44bbc361093cb0d277fb5830ce6867a65dcc7e96326af4ae6a829b005df98c7b149eca27da7721f85142b431182fc33997dd1f96da093f08a4dc5b47552ba26e35b842d1df7ea18cf13124861d58bd6cc0770acb185d1b8f30173c1ae20caa6147fe62412e71cd7b682b6816f83433032d4844f414454260ad704fc34ee5f5694151e7ef9406fa172a0f6a272639d4f1bf69a159f7f69aaf98298a9ca6a0c58cd2c0e6a7ed0b89342a90f7f78cc10791f05e81660ae1abfb437479baefe98ea2d723c9f1b12e66fe12a20f1d7aba0f9ee159811fa6653fcc115af57f1e695b11d12506f5714363f3bc08ddda7456aabdd417a50f55346298e275343444d105d2b15003b7ece8bc82e4f4a81392444933fad648e374e415d40afd0c108ca5d8b0d055ebefbd605269e30444dd89e29c50ffb5027e86534b5b06324462079052a1c053de30b7bb29d9a04ecf2922fc7845c18f4fa5f49b7ddb26ada0a07141010770f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"50f1d0397fa8c00d2490d6f4b96672caa53763d5928b0e28e0cda8f5e28dc461","proof":"9cfb402e00a6d558f62b20bf31c124fb21fd8aa5e2c23a0fad77c7ddaaf5776f9ad131ca04a5d131db080533bb2e81dcb3e4eb199a58a105b520a703a6261a79de1cbf3a19a8a9e7580084d5b04f6feffa9789719d255be8818db66cbf8fae32cc133bf755f412798c583926fa799aff84702e5f87ab4b8ba5150a543d708d6d6519b326df9ef385e62096c7704a9e7aedd58c4d321f0ceb16675c8cf839b809194ab3dd65b582b864b09eed6eb6e94ec59ce4ef8930cbd43b0ec9c72db15705354a076447119c06f09206107a161a93bdfcf40603e037eb6dcc0f1da86f2a0ba8081769613c042b8d3c750a8ceb72866e28467be2778650db07184fdda31e12086bf0f5c03773a61eab200320645dce1fdf987ad80c1ab1bf4d764396655528a468c4c291c94c47d672f3bf48f621940beb915fa0ff9a58d586f65f442f6e570058c1e7210abf320ccd42856b1ca7b4bff2a1cf3de68a4f5b7c30df9db0855fde87e1e0e502aebe5a02d4e970d6c103c229b50d87b3a3a466af9fed147aca48f2ef048d62126eec05b81cee9063906e8cfdacde30702d6712bf4dade6ffff2c7a4ec174763c178ff3527d57c2cefea9eb62c1cc6c4786879189c9498101641ef200d9f80b711ec8a77918902d9054b2859b424170895cd39b9ca7e28e83d20a42baeb51c53c5a536f1337510d1b4dcad768b28a7bf76846f85a8283fbe5061c7811274ce75fbe78c51c0fdb64aa5ad93d50ef2d72e09de04bf640fc5086b27e764259f4303fc272ad7dc730b338524fb6a6d4e78d820ded1efcc8dc601df07c4a3446f45e5e39eb0074e2aefc14e662819fd32fc69c92c60d5d64c152de421f318995faa2fe1cc4cef8bdccc637baec36458a83bf3f2bf33bda9d2815a16d0f066b3856177c14edb8e8d46b8498de0aed48e555b3160348e4ab91b73d8ac704"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4698f17bdddec03f2cbd1d78b7d3a311aad0bc5b61678664f4f6f6a14a0f9771","proof":"446ebda911ef2086ab246cd5ae88eb734489fb6183b2989d7319f1e67ad1f1195e19d0cc857314e1b63101c076f6905c8d9630c22a28d7db4bcc3fbefc903e6cb6e8d59ad63e9e87e62178fbfe1073e78b387fd3d8ca55c4f1fc6894ae795756a25c067cad1b693fef377bdb2f5090d21daac19d47b17fec5e63eefd1a23c15f884dca2eeb114ad9096388d1225093779b7d0ad130b0410d3e5dd54d470b3d01447eb478add92c4b612fd2e67b0507108d4149b5d9280d3e6756dbc8c16b6c004743e2fa9c89d3299d7362c4332bcfae2ff540ac1661ca1a2067523dec714a0128d5102f09411ac743f534c8a6a1af17fc0987d55c4b18238e0371a5fea52c4b0680077c66726848f222245476589d662746bda41e31469010b6583a8241025c76c8c67f6c9aeb1fc61feb076c1cf86626d918a4447ab9fe576ba65c6dfebf606875d3f98cb38f09fe1d703f71e43d1410a2551c6ff1e36d992bd6531dcf2222e0914ee1aada7e8a7b7d534de15b2e5607881cc5822051dfb7c1bc1c1e0111510a491da8ffae7bb30221a840b172a821391cf276970821b6bc502a41f85d727d2aaf7a8b010fba2233e04eee86fe4855aff3b7af78426f8bb9dfa7923ffdbf6676d78d01cc811e123b2c0da3776000e07ae25c71e57a9c8fa5713a81a5bf28233c024354795e5c94b964ea182201f5af2a2a1934696bebdd20debf0970238508043901cfe9131e322670a4da2e21cdd9adc7b8c66a6569402eb215f94d84061c9e7eb1d9ea17342e6be1262f970cb6389088c3fbea41d9bc675d911625e4542174ca55389807d7e31d33e298a3215dc322c9fcae6e2451538515886186b7c20a46b6cfbb5b406aa6abee017e793683961642876741a4aa15e89c4ba19fbc4d04bdf1879f4c5e14331923f842f615f2011c6c6752a2f2f825af24cab9e5e4120d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a4924851ad8569efa05f41f3c6cb9c159ca61a36b8b3f1233ee37272bd27a717","proof":"5e792c49a5e3d2d36b9b5fb76dafb6b4752da4453ba6c14acc2d1adafbbce510840953e8b2d2bd78023a22eec1e8a3bf589c72d6e318bc03ca2b4c33dc732e12ea43204e394291128752b8c9a4c4350b7935ca210293d3e435400289db825627d6ca111a2f029631ffc55353cf6e4cb61fde28058425d87db775cf77c2d47148e283cabf952ff9640f3efe565af5912152c83b0575c6b802150cb1ebd64fe30a44f3ecf61dd67d6e6a32b5601dd978061ddd4a6129a992efd744c73ebd86cc0eff24db2a84d8ddc16007924a201fcae8ba552e2bbc90dbf5df360a8ea7a8d4020c45b9e9ecb8bfb4f3010029eeb2f287f5c4adbe5b94a81e3aa073f88e4ca7059e741ab7c0d9953eeb0b4e85e939caa546c4a1bc5a3715f2f798485fe20a5101eca7a2b44b2d3034a19b2546c147088d8d6495227e48f0453804b0f9adc7ad00cc383c83aa774c951db8a3f32dbbf4571003e3c7ee4640435020cc42335e0411f43412b023089faee5b3287667fb4ff511f6727e3611fcd20933c1e3cf696742e0236e70503a461164555c84a5b250b1dafcfe47942911a25c2b4908bc3bf56a06b9d18fa6ead37a219efd6b2c9a891accaec59ed6669c47a9001bfc57d2be421ceafcebf1b6fa72ff6419cf79f55970137f7f4558b059219a38d14bcf672c3954f54565be71e9ac7b8ace5b35c4c22c91ef251b74ec25d235acec5e515a3b79946457201a090b8246863f9d6d1bdd9b539082856f6c1f74eabebaa265f417434ec6876a2f583dd8dc6aa70a7a2cf6a7f393a18f17badafeac902b78f7fa0221e4054f682bbe1ef69bb9e503143242a4b3daa322524de1618e158d59ce0fbb0f124bd839829e0af9037477b73385cf61c8ecaef2e05c0673feb299f3b3c75707eb6a066758e710ffc5fc493381a28122c0898693c11012540e6e03ff37a48400"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"420b1761a13b22fdfa9366c261feaa6e87085112abad500f3fcf2e7b777f961a","proof":"e2740c5a92ea1e1db209fd8fb82adac3ad401dc1df70dee17da643c638fa2a3a5cc7e1296fa6026214372caaa196ead429a15fb3ab9aa75d4ea9f0635910d01b94508897a0671fc219885cdd40cd9ca7045663f175ac9d29c738e02b63656b46a0762dc1a483f45d7fafcb55b68e1db643a5f7a407a4519c15c8a2570bd1776972e1576902f37e452dbc406ddd10e65408bf1f3800e7225d03ee3c735047360c5094ad2b98f6922998e60c4dae6d2b5d4d00f9dcc7b085546f01908d55a78a047940500093f2d525f60c6c683fe4bcf625af821b853b48fb1d638cb92d7d7d081a3ce451b7c0a0a61a5f86f81de3583dc096183f60c4199c5bb7e479957e4b2564058be42731cac51863bf20104f34d279c8703ad3f5fa1291cf784c318d7126f68a266555d66934eb167708d8bd01616c86608d5b5d5d2cf38c7e483a3a60622870c753c2635a5a727814a5eeaa13b9068eb830504d4be1d0ea0ba2a5105d11f2950702702a5358cff39de6e742c65d0d0f550b0818f4f011e03643557d0a71746edad0e5dc961c35e95726c5eb4c138fb038fe18082ae5173b32f905ebda5934c050ff2450f6d852655c42849ca255277308992ca26f582a5bae191cef19290e61ef2a95b921f7e161ba5c63bcdc3dee8fb5e3bd136911f85b09feb6368419c643bef622bcb8a52f5e2a9e9cb97b3aa76dc48d6edd2fa2df7f8abed50e1f790840922cb86dde1eace46f9a477ea6c7b212fe6fe43889e8de9e62037d893e5e6c6e30154e42d4e69820aa69d46c70f04ca6e7f6624b0c98e086f7ea31b75c774efcbb91852a25b9210fb70a0dc7349d893f9d6e587bf00c5f46c6fca8b50616cf0010fba1ae85e24cac756d5468408c4b8bb72d840fdfbbfe54a6758693eb02ec750b245f2618a6f84d115ebf41acd121b741ea7af7ed1ccde28e5c79bbd60f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4c1d3a323bcdbeeffb45437ebd6c6934c520a9ce674f49717938e014dc5f803c","proof":"d6043c27c17d9e77ef4ecf07a73baab059f295b5e7957cd987b71ed6ea646874f611482a0c2400beff4607e4080f3970404dac4ffa3f20b4181085b651468018023f0adb7f3722ce91134de7b3013b2df8686c2f9c718e860b3b894a230ca124580212873b8e90a891d8b345ffccce597a057df8961c059246f4603befd42c45c66e813c0c9aa6485d9a5844fe87edbc477c985aa4f3d4c1625148b60d4fc408629eda715a7380187b97ce98945dac737ef5d08e631b18dd1c7b71a020a247026d89b5676db64727c39655e41d9f564ef33c658944de5195106c1f65dc312b03c63cd3fbc081f57409e6943da2709e21ffff2ab16d0dbffe970477b503420a70ecbcd3cd4d472ddc0ae6b412737fd3ce45d5d2a88a96fbafb468142ccf1ab10c3458878891cc517d1cc2c75d1abb7507d740225e3f25fa7931182334b661ca24c47dee5a2ed7745751ef155609945a59116534a6c7cfd8441c9a6fffe72e3d5a5e4a1c887628bce1f71ff1f3da1d0657ec7fabb3754af1c6fd23fde9223b5f214004f9a6288140afb2ec49cda258aa5e7403ef8589d085082803115319329d11d8973cc93c440c59de83ad9750107f4753b417f21786e8de89611b0a816bd558a625110bcf0b7c7a77ac1c65c8c7c169dd43c7dfeaa0b011f0d306977468f21bd80fd3719e5e2e7b85f376042db42a6c49e6d685df5c0a010724429ea9164d63ae8d4895e642eda30f945cc683c13c41a277b331feeef9fc200e00276bb07f7b7a675cc5afd8ad954c051ed88688b6b585c0b6e65aafa577f7f0b0fa756b7345308afd5d029c02df37d85787a5be61c089e3967f07b0b38f1c7d87a955ca9c321f99618b8750bbcb2cb50204d858f761d3765fecf0e9d16ba1c7f70451343b0ef08484404ab27066782214e953ad96162604aceb68f9aa1203cbbee6a438000a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e899905cba5eb4060c494706e72be87b6c4d310aff19ed7d46eef67b0673d772","proof":"7ee7538a17af8b9b98eea514f29f1f3ac0e1985ab27d076eccb229b98bd5c81cd0095a3599e4abc406678e2586588d83b98c1da7d9bc973f5ff440ad702c4a765e4f23f816ceea6e377a4be2214d8f4e001dd66d57889b92ec90b0c694b31c20a6b577b8c1345a8227c0a8c6497fdfea03041a3205c83a46bb1912a878ea724ecfcf786fb6bfdcbebd906629275545991f0e9e048a157cc4d8564a1b4808ea028ece719e0f4a59fc00972d97f081672fdb3b11df4fe880273d1bc0883d15d803aa81e42ca067ee949e4d3cd37bd35866b33da7b5ff81bb6d0c5278426921f20936c87100aac88cdf20deb612cf2f5bdfcdb1a1955bd416239c10921e10ffeb49ee1832525f4efc0dfd8cb02e4879103f6af6e83fd2b4b5de2e33d3005a820400487f55522800161d5931d9e1f598cfd96d9113ccd4c15dcef0d367578b1b7279ce731aad8d2a4ddb857064004ac033fc49c119eb5372bc02c42c5d054d410f7aa0593437e8e6dfd450e4a8d59489ca2a6f6291a8a4a6e1269f15944e735d8b512a5934cd22e03fdce6ea53d827dab270fb93c8a23f2520ca82f12a7f7ba9f955a21c4e52bfd34e57428414f549d699bf2db48c13430e96ad8b982051fbbceb5f9a6a15d46809dc6df3c866f64b13026fb3c92ef677849eaf2a20ecb29f3a057104f279d9488b618af2ed6e26ae707e02ad55ea9c81605c1f36f4dc68ca10923cf01b8a18297aae463e2ae3d5b445a9e1a2bbdc96e0a96ba00589bf3f82113766369b49aad613b73f937147b8f56d79f0f206a1e909f34d252191456394d5ef2c48f37159a49dae0a3df0a94f9120339a7874040bfd4b7ec5bd195de7b7aab633434e40cf3316bacf5b6859c9e6c969879381313ea7ac1c263dc7b875e74d19062404d56873d9c510f5b8e4b41aa62bdc25e18aa05323978fa4c816453a4e8a0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"22d7973c902dd6104f00614daa198e948e7e1b06f7193626336215d35c91f026","proof":"367042c52529e43bc59ee854e190a1c41fc9ea5615130c26c9aa5c45c1a8d8367035b169a88ef67ccd3f442851b7bed6a92d457dba33f4e694544ae67764cc64eae4d51524fe964fc695c5bc1836d703e6b140e8d389b0d167d45ff05fce4e2248c176f378f66e13f63ef6fec12d10515d34d8643198ec27a93666687ef705772195ca87b64250e18d800e8a2d8fabb03d79ab059576692590692048ea68b2077c60826b1d999fe9b74a7c6e74769206b711eba264833748820c6218e0a795060398e037ab6c9833ad19ed437803cd3cd92ace0d555a66ff798bf363c2670d0602ed22490c7635d6324f474e15c199d18f7d6ca0abc0ead93fe47f7b946ed50b5ce4cfc6f6271679843d5d94494ec67c932775e1d686a58eaa6d57563759294bde940c476006c3984341da126694860dee26d21c758a6872f754ae444af62e6286815e4b3c1585f7deb8b4ea9f4d8fe413762d6841d0b4ae42caf946f1873c229006c900e0af8262a03dcde4b17b862ac93e282ee621a615f3adcdc524649c166469dacbdd6379abf6e5954c46115f3dc0b9c9bf23dc0432e933aeb1d6fc5462364ec2606fb06c1b5f8d19b9fdcd9defbd87c853da824089313fdef4838f140fc48a93e64dfa71597bad8d6536b812ba1e173eba1949cd90da25c4d366385e32044e8e08509a9227df956a72a3a53ed1d501ceeec57a0aefa7768b30debb68135e8d09eb6cbd1497a3b6b716c4ec0704925ae30bb3ba95c9b63d7eb782e5985e020aa4e7157c7e82b4b472b15c394d867522085ad749d6cb3caf8774cb144865cc626d55dcf9785a371176aa66cb88bea3ac4444fc08457aee897bc0831c581f8cc4e01389367ac3c7601d5a3a8cc6851a8c40a91a5ec4e5f8f71be6b0854a0d403a15dc918092dc54d87ba68d358f9ad84fa1bb7607e9aa7c55306ae3ec2508"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7400337a3a4a0351df1f84168d334dcfa7332ee185b6757136f4857608261511","proof":"7c97fd9b452326bf9c5757d78376e0487ccd5a988901b212c3aa83a73ec9c26f128000c5b55ca1f6a534e585d2a6c97f615a8626dba833c619a3fe37857f6f5a8a76afb738e6daed13d58cc74ae29f8a576fd7a174ff43784ed4d853ccf70729fa05234ff03cb5568ec61eddaecd1711e2cdbf17e4ad9502d2467c341bacae38e36fd43e38edeeda798a19659b52fc9c99e0208f6ca9c0b754151da2d8a3ef053d8e0975c1f9999808bc5fa248da9fef3417c14277b27d0f86c094081e77f60da245edb91c1170acd48183727fe6314b805514606e87cfc0d1169bb64f44c20488f3082f9ab00a0391aeaee4b7c32e14b90f95088fe326c24796efe4014eaf17947cee21570d50d37f3451d85c815baa0018ec95a0bf171c1eeceebc11969d1e60636d2e46d26ec395b8e959fa7ba494c88b4914998f8fdc690c1b2980772d1982b54a760eb14d64127105f34e0e0227432c93de32b7a10fcbdeea7a387b414b000b683e41862998a1c86b8c81a4715c31921ad0fa332b20c5b858ad27f59c0aa0752433edb6b218c0a38f45b85ac53bc2a628df4f1732077f5989a13693927fc25b5114a32a3f4704ef08ee5474df67bf52998b078162584da06a5f55e6f05d4a47e4e35b5f23e8111be653a63f3ff3fd3a89d40c35e6fe3e492334e4331a55705559ce279a98b86fd5b51ca3936f8e4e9c53468bfcc8d42a92ae24d979c31290ff33101409d5265418cbfb20ac260c0d0aa95fbbb433823ba01408d5f4480a369634556d497810543938d4115e1766c75752b87709a7f91c89172f2b455f460046c19d442a9817a3422dcf9ff2c998b6e794eb5f8ad44abd5201812ce460315c3266ed10041424d07cb4c404a3c5880f86359e5a788ea2ae67783620f84905d3a70cd005c5892532b6db31e7788b5c6b739a0b7435519305183f2f4f211506"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4047041cc1b4c73f286b38d5f0132f11b26c642e21d9abc95fbfff4dc0744332","proof":"d03ccbc18e70dcaf4f4f0b08ff8bcbfcfd5f9ccd563b1c38a5a436583cb3cf471005a25d830e8b0abf327c08464150c910889fc56c1f61ce6ce615d15b450c12b6df0c168fdb750bb1d4a6c67745a97686b519d97a9c0761aac3ff9cf0eca0263e9ea36fd29c7e102fc5e1a1a11107999f5509e38de6493d0c907608d68604343420024734aac1486e7dc98cd38b240c2dbfdf50b2002e40da3cd714931c99056c97a8ad33475e415efbb97dff1671e343e1db94e4263f998de1b59c198241079a266fdc205330c41c846e8ee350537ef0c92028a1ecce8dfa2dc9e92b5c850aa0485fb62a180166307c5a7bbc9692217003e556e9098943b44e259e0ed4ee2ac8ec8f7f862c95b2d42c5428ccee5b0a222f954cb807ebeae6daf57ddb194454de7e7b9ec357075a9c8abd682b053e1ed79e2eabf3a31577ba985b467c492e3a709c1f4642e364faef501eaeb6d053c99cd306579654e830861d7fe447732a3372ba75e6fea020063d55cd40886da361a9d1214047a93cf75be24222644eac3678a9fde8db362f96eccbc49c5fa4d8e812c2eec6f6d480bdfb42eb67d17751326aa141cb63d90cb28c988a6eb833b9eb11a61ad4b631e00551b6664e8ffd882db61ee2a810f1cda7b96b5b7d72d4c5c46717f206c3f3114b7ce7c28fd1d98a15140165524e93e7a64cf6da7855281a32a1f822b7e3a2655819077a0e73c35d0366e0f2b58dbf18612c0727b2b9b48b49e5704901a265e921c3e3057e0a7a02489e87b28417b2618cf13f6f598f6786f5418262ec5e94478f4519c07b6e7287291a74f13c13749e0cb734a8912edff3cea1675dc9f17045a0e44dfbb5e23b584d2633c6b0833590031b5ea4f73d8e6b13d1844e1b812108b67815a9b77c6ef40425d9e783720b91acb2a19a116c0b9cb73413d87294854233c1701b1eb6929b03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"68bda55309423f89f0412730f70cead80447cc1d4194abf237ae3e63b9ff3e7e","proof":"a8c5441dba3d931de92d3dc806839639fbe4c19f9e77596746976c9905f4bc28061b02ecaf2a998258d3744e2fef68d634284b3ea13870cd5ec0112a4aecdc54c641a79d1d96f6946e60afc3abd807b360110636cd8695e739fe5905eb138001c2cba174eb0ec9cfcfb60b7b3cc0284fc783f466cafe5e146007bcbb531c8834de3b813a24229b831dab23a3f69048724b46aca56122ea92905808e4daaee50350ebb931b105d49db1945dc10f5f7cb6ded19ef5fa01632b2086125075a6650c316b6739fa688ad07b796eb8bb92a5eb18d41a08a3823b7c259e5cfe8350710210d3eb0a023866bd289c8b19e7abb0290fb71d2879105a73f2060bc8a74c641de0e859e016f6c5816f67d53255e947d3d5d585fc87c2fc1b89036328d16d296dd4c25c0612e418c3aad68bb2e0731ec7ffd829879b4460517a6baf86fd33a20e3e2c83c4c5fc0ae791b84dd6d068f01818038a83b19cc36e53b3cfc03c018c1b7449af5cbc508e15abb649d156449834f24baddb794330a304a2d7b8cb13582dcca81ffab9e38235836ad748c133cfd3cbe8932d196f487fb98533a403ce7b7666e0e8a3af3c407266514ac49fa642999dfb19bc9abd6b4a6d70cc63bd202a5fc6e058de602b54731476069a8c990b4404f4d598cf9b716e2beebfe4212fca4560795c912e1b1aaa3dcfdbc13748cdb67c80f8232e530ac23d59a110f1b853577a6bd6d63cedbeb32b4ffc150990f204a7e4734b9dec39c922ca24e10e4065433efed01f84d84c168dd18d167cd2902d22e6da06396239b954286d8f5fe6c72846186d7f58b4bdabdcb344ffbba485c2e1da747ac0f5a50d3f5c15d57a8b5c2ccb90cbe0d4d6b6c20d4712658139a2db1072d051fbb35f025531a6bad6de6602eff454924adab21f602e1548b4ece695b98fe86046a999f82f59ba844920be06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"90452acaa4d3663cce16ad351eddbf878d76afcaa12d1a8dd79c613e2de0ff0c","proof":"6a952df676c3d2a0cf1b1876028f4cbf342a4436d7764fb34effaf8f41a5572872d237a753be871e9951938e033c6d9e23b4b0467151f81c3bf700e45a54e10baca2599fbc5e1dfff1aeefb96f639416972268f0b8b30d842c729f135204983728bd1fefb84abc9e02375e6c59e54c065b79f8972c26d5d1551cf820a70d75468a47901777df92b367e4bcab28dc507505927e62443cd66c6d9989e145b39201c88e14131f4beae01bae26aa6442b256c64acdeececf73bd70028e8d1c693d030c710c1a14ddd89087866b3dea272afc3cfec582150ee530334ac5573f34780eb422323e13bae9ce208effb0c5cbdd0fd8266bffc4d43e2b9218ec44c0613616c202d8cfb9d5b5b1eb67517edc6d69276f29296d63e5844372bab1977c3d72098e4a56d344922f85bc100306296ce11bc1c61bf2d87256f0d98385978a30fc6880e92f9cb992c662d5c69581dc98886669b704de17c12f5286b64e2eaf84773722e33f3bc08380888fa279489e4383231cba06422ea07218e0ac169280a6cc5ca062ac60d2aadd316ffa86a1f4f180c108b4e81cdbd738f1ed143264f94df830b627249e3c052bda1b0c867917d0210203f8738acf40150d305c90ac61354674421a98859a0ebd63cf64ae32cfc25c32d6a1bc285634109fa00cc90527f38d4b94607327482329ab761f37d98b336f763372c0b7d10de49b34a457516c61c57646d671251cf616db7ff64ac4036772c530336ac057cd8750139d27f41784964702571995713c6056dee0d1f123e84de550f71b6c157d9a746bb6458408455244a695b91c3e114a2019feece6ca0fb4af4f723594604c117b3e8ffbfe572c5f1716c4328109b13897378c30e93e7365f906f25eed6009e76503329a9afc5ba60e3516acc5d20bf9cdb09809521e40ff4f5d841bea9f3b1a3ee8f1aa3f30ccf80f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e25f818338a6c33300da1746698028a449f13c9a3ec7f6e5e39e0a256a025e0e","proof":"f6e4eeddf9a8d400398ee5fed85b75095955b1933f83255fe4e1e74effdacd2e2e9be1f8c6d1bac93ceff7f43dade9dd495edb34b8e6b1c4fe066674745f721e42b0b4086ba603dad28ad99e8a0925c10c56814c3fdd6cb9855375569f91ef586488bacec6d8d18349a3e380b10a76a8f752e69d3cd84f2b2a908bc706a82c7bffa9e1e180ebd8f8bd68b2ab7df30f61dd40675022b39f691c10023e0138860a97ca452fdd80c84ffb714ea7bc4ae31b0e509f74f8b4c6d4c39ec5cf8385810b225b7d250c777f2f7f5f8281fe11d770ad6922783efa5a5bc0d50c8eab7ed60290a3ca80f157110ee318dea26bdaee1b745470c90b078f24a805a5aef7245377d4771a19a22c22010926a4ad649964f5dbd938cf6f1ca9c384637e5c4a995d1bb0efbdf664da023228277f72e5315260fab232163dffed804ef561802577f731846c493a325dba17bfe5f5c520ab36c8fc5b42163fc673157613a109d4554d2dfeb19d295c3d7666ab3fedd82681153d7ddc3ec0a5dc8ee3d9c2d816e105e31f647d616f4558c205a802a77491c7feebcbad2e79cdab9a980b0094f5b338cd35d8042de03422eb1cb7a90f11541fc4fc1102663f1e2b93b7cb574b042b96006bba172208042418d1f189fd05644ab4a93574c73e68297378c2d1d2c3657f900f6e5eb5df5f6bf27483a2f5784e2b8169e57918c7a26fb23f59dfdc20c41dd03152fbc82134ffd33a4b34096d72ed691bdff864436a2f73252ad426046c61cd625aa6760ec33b81c95c404bd72f6b572abbea8674826e899255841cc477fbe636c8d0a052c92bba11dba490c6eae0f80ea5806bf3466530eb6a1d1d30a9be9a6ec3b0b1bff32ec2510826153660e65af95e2e4659b1a78251f8bbaced8930a004a8590876022a9aab926582f0d92aa51ecf6d909015c0cdc0fe57341ac3ca0807"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f8b9921f23457d080bf71fecfd7fe8c188882685b95c2d7764a200dad138ac1e","proof":"a07651aa4ffbcd9e0486107f1f39d522090927d41f8d21349f562717ad0917780eb0af35903b4217b60d9a91460a30534631adfa8df28a14e8e7a5165eb33961987856cb84b33e6f31b2282c008b2b8aa7f92969a525d01ed23612d4b11f4e440edcd9d52b12ff62bfd94b481690a2dd3c7bcd23086f173c119da3fd13ff1d135c586b4678a40bb33afe309897ae43b1bad267b60d6f3e1148ed89a191698b031c1f9add8994b893bdc7213e921fb4678fb5b866e14222ad2bcdf4b1c9a713065b85bf7ccca5c40cd6127b973d52eaeb7c555cec4b5bd62f556f304b4a7f7b0086e0c959c0d749a58bc530b11a9bda1b76049db14136d129d8dfad8f5fd52f67e6d7f85eed87189c64b61f25cd5fb12cd32c8ff1fdd09b0c12e1378b1483424a2a48fd8737340c40d4c6aa92a25867e5df025961ec0c78b76aa22e1d54b1c54fa21d2e80880610cc1b38efd6f12a3ab3a67d5690f482ad8f2c286ceabc6e9c2c22db1730f36d3c97822cbd2f36c44251789ae28050e74f8af44d256ced617e6286693cd62fca5a4bcd0eb81736d9b95f2c2c0e2bd7fb024dbf8d9ee6cf417f19023ead5b97b70ed213ba0d4f6f6b9308c99892378014c645fe37d63777d63f52be56f43178bab3d51c2ec6683a770ba0461e9e448d5b79f1c6a33794aa18054450588ee33f5707e440a70d44b8bbb4267c6f5ebb9a736cf9b3c9821ab05de71d684eb1061a100d0c24930e25c0aa2db388b96eaf67bdf72dd7cc1347f91f651656738dfc3b9cbf601a5eb965c54a692ff4d2f719c709a973556afba75326883a0a5d9d04fa579f83ffcc784d53c2946e112ec8712c5729c33c778b0ecf9d06124130d4f6b753a72bb76ba054ff54bb5b40bf6b63b1479cbb24dae5b7d4f84f057714fa9633416a9ee319d5889ec0df2477ae6f505fcef4b9dfbacae409ef4001"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cc390d754462c218dcdb72bb304eb531a54b0a431fcff8c0d3350e0845a90721","proof":"7af1ec1afab77387f16fd7feacc7cc98653752565525fc3ea574196e143fcc03d2fe17eb410bc039c4869818e5fda550b73c1ec09d35990ba8869de0a00a645ff209829b92d8837bab876f158400ef892d76a5eb56d013b4c66e490347879b7b7055242ffdbe66218033a14e9d1674e9df7f60411a01925f3ece72bccf9607439ac95baece974d75c52b2775c5262bce4ae92777ab7c6d0231e7a099764e3f019979db525385afb9c6aeb230a772e6cb77d7c91f15e1a4efdd4ec2bc9387c804074506663e740fb2afa4eab593d72702042b1b94e3db680cb5c7ce83ae94880d3a8114af3b8689f54bb862a531bf12ec4f39d8649e5c3a8401607ce13a03cc585a6d3806c6c15ac0145e537e3b01c132250f089b3b58c1cffe44b98c20e60e4cb80caf5b38b99505759a76a2e3077fe9c5ef3ccbd54eccee37b40902d498a102c0b5a4cb80e0b7fa7f818c72393189df59f43368c0156f5bcc735ad24ebc017baea10af391aa2d84179cd1cf29b8f35984f4f8a440c019561dde9a94600c2f62f27d603959bf65025872e4af84e8eeda01e6db26bb657b05cccc600811ba611db2584930f787b6ec7b92863108459b7de906d2f2bd3631609d7f12a733cac950f037962fe83bd62f47e86d3936ebc95e9e8da9ce33a417c3f39b5df80505da49ba699e9c296f63620240de068ed026165c1bed77177b9d0e674615fd2e634414322b1bf26bfeaedfb794cf97eeaa644ac907e4a1bcbfe3f9983c933fe80ee9344eb4d529133a3088d4638337153cc60970198292ffb28ff2fb463219b8ae5b06048ef48c488fd72af89bfc8299ec9cfa39dca571f7305e76ec7e7ac7424406697b9558d53b87e79e43098984e24a5914c3bcd85473bd85f6b6793b9ac42ff50190e623ee7a7cd378f178b469dfc437d86f2500744adf17d873524f08b3636c06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7e7e71f849bcd66b9caffec5b113b27020ac68865be533f0ccc79f1f5725e555","proof":"ee429742f36afb50d9f0673100f40e20420a214f219addb365225fa511f69a2d6c90cbb41dd6a3b3cef6600a6f457a111120d1b281ed0bd4ebdd192b96aa1966aca1ad774b700c0f4f6a4618033f5c1c6139974bf60ec1c6c4298b3a909bce60721c0cc58605d62e648e8760f76cd1f1c997f52c39f727c276216b5090d0af61d4986b7de9749e319cbc26518f662d7f42aebf9634be9687730e022ff42c55086395d46be5e284ce4579d52e8b1cdcd5e904c41334884833817a5461db462f04ac6e268c6695116d65f64d19c2900c1bdcd70b5f487e320c5691343bc0c4fd0ea4556c3c2c5dc3f9586919496013dd855d746dc5a14d43512096e903dd6102053e09ea9ac1e44824be1e1c2a45f3af9e512602ad55b691ccbe79ba71391133495eb665c2897d01669c11ab04c645bac2d5996a2262022afd9569883ab171196326001beb7f9bd18aef838d028b1a93454b2a91cb3dac3cc0f86c0268865fe269a69f0ba1999dd7885e00418f7aa6845be55b8f8fc99358fc37266e811f7b1156a6eb6c37381ceeab9062c35af262896f8ab083bcc9437316be6a8242d1a2a87840f4cebc3770c2051e9bebc4bfd2d18dcba3721fb1247440d550f70bd6254a58a6a28630e933ab43d2c156d36cae083e51c7e944fc63d348855d03dce557124b0e071c64664a54da376c3c10d1a37d4c0a84738ecbb297e5ec567d0d786d6f17c8456f13b90ca3b4c3d571b5bb2dd1351b37e8d14ce0bdc3867464a851bbb83a2cf45cfc781a8463814669589703447b55157d630dfef3ebe556ed28da952e67466ded0738023fc7ac02c96fc6180c592d6b91bbc83e5dcd5d8a13709570dd556609a188497b0ec26fc63112d2b61fb90dfb7affd06e686be875d46857fa820bbd8a7e6108ecc936c990af3c1e7556f53a50d17d14d76c77ee45cf38c1fe370a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e6f347c27a3ff45244f5c8720a322360c54cbf973111785162e315c6c7beae2c","proof":"c8e6104497b8a0446ec667430e632d2b673db5315569c65393839f703b36944114e093235ecf922809265f045066bf5cb0d90fea4104502703f95f84d3a1d85fcafccda798fa7a2e56f2c6d1e2175946644c6817ef939926043511166fa6a05b6e69a0e9d801953bc6dc8fe5f26cfa27b212ffbac1c09bcd85ea7296871ace185a5806995454e2c4bb10241f735008251d7be7cc8ab430d825899c38d3a176029fec4b210d97c403a8612f8f4a4bdec8f5d1c05917d52e345b6cec231dbb7d06b0c1aaf8c480a678599d6d5fd61c5e9eef066ac993ce067746f6ca1d763d4700a8aecc68f4ad28096ac8f482ba240416569c623dfca74696977b0047d8144e193602feb24e763e46fb9575f4eb638f61743419de54771f549007a39ac6f0a7752e9baaf820cb6f7edb9452436d7ae3e824f6171b8873a2dc2d883b781bc57d1c66713b6b4f284312aae780f505d3273565aa299a948d7a70b4a1bb9523c6fd3fa4471b63bca1ecb8913c6ac4c957700a54d252243401dc6233cc15e6d3b5586eae78f7ca9a90afc1478f7487b606ef75cfc369a027a975d03de21bc6e32ef4413c190281a8a6de9e114a15eb59a21cc6ccbbde57a695f5c0916d2801f2d7a4584a1fe276e685a306b20ec1833a60a56f95632586ac1008096293bb2da35c412e2e1931698c7e370c39460609ba33c73a1ba128669985a738dac64cc601844b60ec8f7daa7ad3f67caf1bcdbf270c13a14fb1bbffbaad899c538074848ba9cc228a3bbade91efdd3f275ded2e68f8c92b2693817eb1b16a3eadc49269a5bbcf234a5b307160905f959d15ac017f426b2eff31ef83253216bd008976fecf247d0655095758d1be4346b7d1da1053ed3d77ac47f8c88c1181ef6a5b28bd7ac7c1098345dd5237d075a652ae20094e6583e9f151e38f8232a7b2187fc702f796a80b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1cf52938dbf280754d7bf9b95f037270da024b20b33a8721853a7d1f078ed307","proof":"462ea498b84e164017a5209f529674fa0fd6e5e2bbbee523372749c3fc5d881cb29fc08eb2fea6f5c1d3c9df7a7e5c5389f65624d28ce1ad27917b71d2795125e6566c989959e0df2727f6d125f6c1154c7cfaafa9cb0f5b4d4e24a0eebb5c7f14a00e80a456c7be461930c5e9f916077ac4c9e7ef906289fb45c2e6c7de912ec1d0e6518aef7e9c36c9ea467670184f04b13e42e8e994ba81aeca412175fb07ca10e97e2cbda1b51afa98f4723e3276ebc7513a28a52d1496ea32b4a601c90d1119b111a7d771aa1cd74de025259bc14c1d194d93157c33d5918e0ece76480e7a49f29a71e1a1ce51fe236aeac0305f57828ac39f696085590ba6506baf7445aa281c49a6224fb3aaf017b5367c3b7d6ff7620bccac2718547bf4a7184bab497a6bb86c4cb74c8fb41ce30b43141eb13e08f7fd6608c222d3adf6374ea76906e4030186148f851d1599311a858d6b5a1587f948ff1d87fcc245c4724e520c5d44dcbdefac56c4e393624cdda02cd6826961cde40f5697c3b9e58a3221799549ba4443adf17274125672be439d2519dbfb0fab57c099632d48772f742304673a3aa3981a48cc1ae4442fd6aa212074ca869476955213af2d54cb2d3ab486637cdae9a504a0ab000dcad01060981848b9dd7bf7b96b60c81c6562daa35b77f42dd02944a3e8fe02f187e3babedc26f7308a4cdea57bb833121f0b37238c4adf03beb8b6ffa0368bd31749969cbdd57a5824d8614dd25d2ed7ab7eff6c8b1f4c51a211278eec5825d32f240d70cb5413b79de73d35ceb430c576b4f91d21aa406b306b0bde9c3d4d3888df257cedc1dbf25890c8f88d15aba7edb70cca06af1d5488babb4d1862aafc5077e71f2b398880ec4d91ca82e5bf8a600834255e78b3008aaa43c79fa8f676de459c97c501a749c59b9cf3c1ba0b9a70b803ad14ef8207"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"642ecbe3b6d715b1fad4d336d580a01527eb7af63f8320733032a1307dd1f35b","proof":"d656b90c02eef8230d2a009328532da588ee6ee3bba12ec13efbd88e42248d52d2fbdecd5393239cf3ce9d3bcf438eb8f1cf8da7a4d3b994277e0db9f8fed34aa2fee2b144d3e4e6fe0a4c8249a465770e7e8e0966cc0cd609f84fe925cfff75982ce3e1f4f243a374c5afd7fa7fe9963eb12d4b6b15c989c6445c667a789c3c40b2e3d474c2721ef5e54979052cff5709243443c36aa2831151325543cea30e01eef7a7284c446aee8061ec94dcab5e7263123053b503abbdea9a7bccac580c11da3ace9b02b39b3d3f231a7b4c0aa87e04fce49bd15dbf4218a201593b180728642a4ae71feb389678cad71057b7af45745d8789b34ba4f58493f752f89802f4fcfe50d5d56b2317b82b2d7b54432e77cccb935707569fda9b37a0bc05452cdc66891a3f08b00fa365d8a8dfbc412c25af12770c28a536de592f3553ec374b40be9dd54f82b9c2a7335e120ccfc6802e8530dd2bf286161ff4b45cdb82751b56ce3a230b32ec5a7ac7cd0ff381a58a064b8afb0949d54de9ca635dbdc827308465b8e3f03da246201eed73fa35101016b1b29da65f7c64ba0eb28481ba8b53bc1352e16deea92fbf503fcf620523ee90260c4986b4983f1edc2f3c97c95f740e37b4c01673043fe7b61f04dec93a6de43ead3dfd3613e1236e479e2e06932588941a224adb724323b42912f9fa7c8e0c74d6418e570eb50c8cdd885debc046f809640a077606d674eef529482142f7090a315ddc0f7c76942a9275dc75b03bbc2340ca4e29dcd2f6e340cf2b84e392f8ac49b26c27216218e964068447ed6beed49d9bd87b1e01de4f5bb489b06bbfaa5fc8901de22514a547791e14ecab127d5d3c3c5f42227ac9293b442e1cfeb2706873fa73409ed2def9553907828704bfee46ecb63b835fcf85d0ab5e69f3419462b95b92ff24ea9ed3b8bb8f517e05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bc8047dd84810ec119965c44580364cc8e4fea4646677216e8c218e3a119056e","proof":"3a250a528f35f7e994f67b2260c1abc4079c4bb57e2b7d09f7a39ce8e2eccc748c17d38ed66624bf27b8e41ef85f44c3499790f459655fc3bdcdef512e03b943cce71ac899d595355ed204fc445348a43e57f051f17743c6e93ad39078700961b666866cb712c872e4a66dfda9baf8a3def261105add8ec403a068925bf40d2bb25ded5b7fea69efa24fff1844aaaea69ea1ed3b33cd9560da7cfca90d029a0f34543391cb3a5cbe30d04dfac632ca0735066c9c3c2dda58750f1a0e44e7660a87015f566f03fa5b75c73d0cc4c262a428d2e3aea9bccad3e5de77ebb145f60c947292da89c1bca8fe5bb88e0a73cb194a7cbf1e939ebe65444cbeb8f4d08e40d2141ff1390437574a6ed3288bb28e3f74c356ad35ae63be1be47ce9683ac120a0d228b22b8d004a7538d6313c3d2295a77ab5056f4e4ceddcc88dd91036c97e70795df097073dbb1c68c25ff1128d7b606a887ca5af7148f7d8ac6b8e7bf570ee9c6b51fbd55e0eabd946512bfd567d9d52f97a36d1f4e35eb02b3ff6f3c63c823d4945cf73bf9781c082e8b6a6c548e1e5ce318c5617c7b9f6f000944cd154b022d139613d35409911a3b14808fa041d24bb3ed68354eecc410c9d60f41b007a1bd9e40322b878eb2579f8648e0d0646680baf51ce85a3cc21ac521decf25782cfa5b5e2c9310d7b88ab4c9e7420efef208b7517c505267c1247bbfca6ab44b68dce6fd1592b5b0e0bfa59cc4f5397bf62f5ad5847afa7380445e37f52be1db85e84c664e6e38d60c3f2513e2d5d26793fcd406d10f28777f86bb13b25246ace3df94cee465972513a07d839bdaff30ffac4c51dceb093d33e293c09bf7f3b014162c0b969cc1b453103dc401f3acbbd05b7914dabfc92f071139762c2570dd08f04cc0d6d49f4e1bc6a7c447aa37e36e30d53d2654b0704937b942af6c007"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"666b192de6a6b05cfc2e2eec1c7da049d6c30245659d290afa4769b76e71d442","proof":"52fcbcbd6316e25bd4329d8fb628b200c0db5ccec7eda5af514059cce7eddb544ef0f16826a2092f010976335c7e89e076a299e30305499965c62f83ad7b604d20ae9009f2d03f641a647c5802849ceefcf50300f9ce2233b2cab7c41d52411ebe140ff42996cb4b7d5c312daa8dbbe5663d06f76bc177887a60c11d2e81196f68ab58980cb62c70b19902602be06069c97dd7f4587c5bb077afe7ad2ece900626d18cb8aa0a90b0a9e4adf96ec4807a19882b9d0eb857bcd3fed0d3d6a3fe03fc60bea86eccdb468672dbbbea660b66bdee88d61615ff9cbef89fc36e2f8004c400d657b89c4db3348fe59174bfd5f83c109445aa265a91d98711eb46b82d479c7f4da3c3e81cfa22accdf4e417671a4412e646fb7a43c383e5fa3b10543c471cb9cbd092015ac54014358a5a0c68fed2ec692dc68a07176989624ec373bc6262ccf65c6d6297a884f7f9ff70ea3a8eb2bc152282e31c90b5479c7f38c89a50384d3d65ac93555496ddfafb9164edb7e899a5fac4d45d7ac1ee1afeda74041280f9b50505cf81e4e4611ce5b45d702fa92d78521c442076ecdecf0635382c1158775dddc67dc6a4fc1b57b7aa51354ad09fecbaed8344058358fd043a92a00228bef8935d913904de2d97fc695b109de587d0adfeac739000496a2c4cce374b562cc03e50f5a902f362964f5d7cbc60efffce2fb99c0f8d84d3c3fd0280b54cfceab77cb4e51494d6b7740e59fd2702e190f4e7b83aadc7badd796e41a7461116e7b6a5e6bc00a1c38def838f4a980fe4ed5f981ab81d6c70d2f2232daa85576ee0c722dcdc399d387e264f0a6b94b84e17a488bce3db7cb1b5326fe6afac45392335bbccc9ba1d312cf1a40b01cd1d43d3333c4551eb98f638e33ae9a72a0e04545346ada4b574ae71fb7fe112b0732922709f0bc22159bd7a67d950cecd02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3000af4fecadc34f198ac9a5477f75bb23d6d0610efd1c3c165cfa9471f4ff6a","proof":"9e91e8760f57ba8528f88a04315faf04296650ed25d1b138b2323aaa869b4c3d769d614163d58efadc900662a5961fa0e7df149a7f078954d33b0e33ef03dc3ad6c79986f90f648a4a42d68e247b94835280792f6a498fb94a2c047da37a0f294afefc4698e88f5fc0a3187ad91ce9da4ba0d425bf719f7d97e0d93fe60f3921570af25250e24b8a5a4ad21127ec828781afeda660ce32f7f07a39c293473d0858e3ff200d6acdcb02abb6c268956e314efda40d95c72ac6148706b1c9a0d807f8b47e0aafa96ba6fed8e47a2fc524b9615101c3288f41ee894eb7ab1003fb0ff80a829e2fbbd8b897f642e3ed925637520c5a1cc58846095d120b19d9eb071e9893047a8c907081c22d429db94049a2ac9ab102df3e1b357bfd701f5c6f6a4e4c99011f67313088eb242ae49711b95d9a8f385a27dabd0fa703f6d04e206c561cc33b445cdbc62c61ddea80be381a36bf8acaf9df27c8d25a9ac11e609e6021507038457224bf2cac25d5a52e7a99134a62a431dbb9eda317ec1df493d99307de431101fce8755ca33500b7c220903eac75ab29009d875bc507c2b765a6443f84b17a137d676f26b6dd19912da67615ebcafca7652bde4bdf3e8601611e6c71182e8be3a02783fe391335bd7089bb6ab97ff4a304fdd4690379dbc6751cbf6a2ad3fb9ea5df188e015c8c8501cbb8bd65252fedd710f2d7f6be148fdda61a59806147747d7d3fe94eeb04f6112e895c3eef24a18710f45542f8bffec143d574347b976a40ba88bd097a6fbcf9f3d2a74dee4b89e30ff8c90c3f1d321a47bc2724878f116b372bbdc6a78173a32e02a794d8f646605b03298277d3142f8b96487817e8b436ed63b02b22832eba55b1204dbb5a0bcf6f7a96359eb5e44fe9a30c450c1637b83a3a16d0ad78f933d48cc542972fa4cf70e3fe5ba030f9783dc802"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ea8a1b78b35ff9ebe8e222ce52778515fe2fe71e98f8fff6107ed4b2d8aee66f","proof":"4c846a4fa7ca1ee646bfc8916403ac9fff3a3f780a5e8a568422037a7483f63ea6fab00cdc643e9719d8500c03239091bf4c4e5a3179a0a722a2dc15f33467236237623c51aac3ee3b32b05eb8ad0f437326d53d647a0ddb3bf3378556d6ee62f6ae1fa2f839faf19383523f775171137baa002164b6fb8f2e6e011e72ba695d33ea16eb8a757014441659b4a4ac99c6f25a0607ba828ea39a0553310ce03b0fbec6a48d1786b6ccd48d46953bfeac0cd289c7250292472b09200b35c1f9460456b0e80e4d5a6c164dce2025da07bc11fd092ba309b3dc048bddd3caa359070522388969b3b1f797b45a217739ff516149d2ecc3755635c86a147eb24242fc6b2caebd3888b2fe34d0c273f031c5c34e57d7a7f0ea12abffd89f15fff9d1e017f459709a89f2b40731ce428d75000436fb0874462f74937d7be445da50f9fa65202c34fd3a4fc1f5a51cd377c0457455ab21a05cf4214414c791b7e74e21e22338ede921d8cbd4921e72cd1a5675f0de281764db5289e6a33288c6fa9a268b15b41d63f14771c239177a6980cc9a741a0030511a33111afb6f3b697b37d30833c47be7a0c3bda22cf4361386ba41ebd279f2fd6c1ece224cb809fa44cb172e467ac703997c11ec795c5d58e314971d72cb9c002136982194c7a0b31da6739f67286374517b9f8275c035366c008cdb4121b2a7fd2e294c5934514a386b1ae35dc8c2c754072b9a08925f5b93ee6476f249672bd569ef3bca1fb8d709f910bf62181a49ed37e759706c5e6fcc279b8c2d371cb651cd9771721d5a569da3d10f72ca9d1eb040950d558e2d61cbc7862408d0afee3aae3aaef233ac77d6aed6bd25d8a25991c5ead3ae7ace6bb87c2a5685d93404d48b3696fb248142440a231609430b4105fb058dc2553f75e71abd6e594db9c5a21f87ad7256e99395573ff309"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"46bf338adf42e43c64c811f7075ea2f391c9f0d1147455ee8944eeff3220d67b","proof":"1889588d53cab1cc13ef19dfd51d213fc7ca78ce7caca3ef7e39a51e1c134971ee514dbbedc606e8d47744458e70340d2394986afb391c59bc1c4622bdae4d723c27c35917e2774599b517d4f8cdfa8f263cdf39efa387d2c9397e9bb70c7334b2486f2dfb6932c86a731d04fe1086e1aebb389f5a44a13d0082de824fcdee1343469fe8e21de18a2857118832476eb0e2ec13332ff4ba5183a338e3529b460d0f967d4afb8a3f9735306802d9d970df4bedfbfcd1616d4c5cebad7b2477af07fec5171017fd59fb54191286887e8519be542986c498505259e5a144499d0e0848d19aa86342c147e5a4a8ebdfdaca899fce88ca889d87b819c54a1d133f2b32feafb9b9904752dc60816e27f680672f266c06b0eaff071ee225f8735c07f940d469c49c7fe5f409ba161b43f9b4847013417584c2229cd2a0ef01a9d59add40207adc4fc56791981c63cb73fb429dd6bb458575e40e65ec0a2630fa0db7493b62e5c03d4e8386ca01e99fab6629fe72ed5484cc0d8810aafb6c83d139ec4c12fee3fe25c424b3329bc349abc4f6a08c14a3a74434cd74e25042d64ad995b6717ee656881462f338f3b85923565df4993126876dc9212eb67b4ac8a2004cf437e4ab95a0b526292b917cd76bcf3ea16b686a59bd747bbc8f8205d16e349f035b38534d85d667c956f357196acaae61902da9b7d7fd4ff42d10eb62819493256072c7dee05b176d6e42cda56fe36aa86d54af94ca26644a7c02b0877bdd47db40105564eba2d729b091f93118c8143e894e08a4e34d0cd9b0a7df80932ae4066e50285c1cc7c7c79e5e15d07a9c670e2eb1a92cfb43c830465143d55b2eb20320f9784ff27020203ece9ca64cf7b6432ee87edaead422be678cea29075aae9c07baba5af01075b608ceb9658e406ee696cadc85d52679184b9ddc6c7eb9c91a09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"feb1d957bb4c50e5f5820ddfe44a920a1cab87905e5168dd6e8cd045b21cd94c","proof":"4c2aec330330e544ddd6438648757c6d0a49e243522972c381bd7ff415173248903960844a45773fefe43cb21f6ecfc4cebbc6c502a08a71481cd187ceb2826bf801a2ec55649a782e44a792e26c112b8e97c22a3559959c7629a6a9b036f54e1229bd7c24ff09b56f799ebf9c228f2719e151c1c2bee0ea481a9c5dbeaf485a16969cfb30ea90c5b7be9541581bcf2ecce04a1a350210f6f16b9f918dee580e88f2b409ae335fcb287c8eac974ad98def07c4588c4ae7e7d89f1091a9c1a203fcff2535cc2e53624e685bf97544ab0f8e68f0a66a875c37e3fbf2594dd33202ce4e0b13ff3d2b42d6718ed05a01f3e42823578eeb0ebc073ea1c385878d2d1cb630a4c536c7224d010757f0d9d363d24e60113073ed8af07ee9217c7e49fe41ba38bc629ad0a3f85a343accf3c69260a1b3b1678b43e08904327973e029635f8898555ba21a2780896231b3ecd54a813ec05ad696a268fc980aa16dfc9470596a250398c60c5ca9d555b55efd7ae31bbca16a6aa33b3f31e23a691445b67f10c64fa54749fbd421cecb8af864ffd4f4cd7eadab5e1b32b1af77c3a2b1a4f059e0e6ffeb820b33d7ae7bef27b954075f882dca70a0baa2f729832a435a160643526496d72c544c2431362d263dd8d36139a4f8445f595959a73d746ea53b176e96844ba7a7239cfb4381fdccf5c3225bee58e0f40efabfb7d8c08f8b892d9915f2b88f82ebafb2532dae8cb85a13382a420d576dad7822151d4b8f11b26df264ac72da360e742ddd19f3ac69237125050069b5ce9878d698e22bd1b2a359594f0e34da9345be84308f4c50ff504948a0bf6a56750643ecfd0140eb10c2e9b3290c7163f0e854ccc5358d9060e27811c86785669a2590354e73cfb6057a9e60085f5bacf90d4b042f4decf3390029d21ddf92fcce6e63ca9370555f73d72fa606"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c2cb1c4c5081ba9d9fe5ef9e1e0aeb2d376903580ee98e9c50a7b5957a59de76","proof":"102e9febd8056591aa0be25ffa530fd0b003d4f4446553a8f4178c8570f28721a6ed8c5675ab767c551f8283758c17bcc8747c4df539f6c95fad59ff157086555ef999ff4810c2666aa66255ebb1f219c589d727879f370a10cbed72f5422666c296e69ffb707bce9a18e4f3b42a55e3021a547f12f9daa5f1798db21edbf605e2d69f2ed95bbea88891ca01b0581cefd0367c813d3dc1ceb3ea00cb2103b30e67c09eb44d0a4bebc4bc4cbb1ef34576e527c3018634f5f0b148817488fb0c02758ecad54f58e70fe7971823e6fec2fd0813cda3f119c9e9ca20feb2b231ad0cf882bc68bee76340186960aea49cf2f04bb15cd783fa04bf0bc8ffb7d002ef118276b175f48c7149cb063cc6b52ea6c18fcaded352eba4d1250111568d697349fe0fd9908ee72f71f9026737bf2d9e4a39a3a7c320847b036d6902945ba94f0cd4ffed6295ddfb0990b1ad18aa12d547594d6f971cacefb95252376fda44fa758ee4d6c9b66f272c10505d5c6c8cbcc55a61058bed80d1df166771cdb2a13319c85151d17e2cbcbdedb49973c7b1020777998ce9f4d8251cf96234d31e98f80958cc45d9b37aab61adb84642a60354217600d170f9361c15c585d65786e6845c6804bca583cdb304acb0ec2025924dc4a9ba84c34ae93d7589870022a70b5e577ae8d6fb9c06a0787e7092f88f07c2b011ed5f46caa479b20ed5f7231a9cf46c9881ad0fbca7db7abe66b0cc1abc06093b0dbb5461af54b61e42ad86b96efa74f81647d8173b14c36c6dc79279abb2d4fc7b72c23b1b5a788c0a3067e8182d1026f68999dec995ee34b2cb7c549ead5c5c44be5fc429bd71951f370772fa2c35823a80b87bf299addc1501ab3ed8c37a6c2d5856a1304ff3693d23be1766d30c05e244fe75aa2ebdf7cbc274e44a86d8492c65342d5a5188cb6cb7373ff2170e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bce934d867ec46d3a978f95aa54259d8c861d1058976a130ad073b6eb231a42e","proof":"324f19c4d9d071feaa16ff257e5c627997c37e2dcc1871982cbcc39645314967b24d09e48d04f6d0c86fb537e4f93abf48cbba85eb707bb51c71ad7438f41b11ca6d996e0b1b095f55da46cd35f690dde76860474fc5ce810e0027101a62e3741422bcf9b5b0bf831408adb20379c848e2098cb6f2334741fb6601f6e7409055e18ce620f744f39dbfea718357eb796d70d6c0783e8d0fdf5321453271fc600fbc4a8d93a9c6021f4a02bd3c5bce7d3094497b163c5e67f44eae52ae785a4202d19e231fe7b9e159409a8f0b5645ab7fc83a11c94c1f3366ef567cb6cd63c6024a2c583deff208a1c3a3cac56011cac182fa49ee4ee934f83f528433623edf48de91f1dd1b5a3310c311f2b74c7acdb98a8ed8974d8f33d9a90e798e2858846d74c63453bf2dbe0118f42c618491901d3cb122107f0280c90d259f7f926f5a474e6745c6398725875fadcd449d24fc54f2a72a864fb7e57a3e1275d1e1e4020aa6f0a0cf194c2e724f80e35397b061270bff70877170177a2cf317442a55445d2ad860b79bd4a5a6b988fb0e60a3e47e3b07e44e19d0145c6baa39278b1bd655b64ea28c43134371e2e98c0e75dcac04d7afaa139b6579d1120b0dfef3d1df4bfc750cb32577dc46a36bb8ee7621eb71b455956171d279a1fcc3ae5f5a7be01fd4827d21cfc29bf25e04b740887c2efdae70ee21eaba05c946ecb7b4db3bc030a616bbe0ec77cac7f78be089ac982f5de70e215ceb7fce2e55927daa48f7071eacbf9bafdc1ff7074e8b209aa789134e6d34bbf95151fd120ed3dfc636bf2f47a2c799c3a58f1659236d7af0c916a60efa18dbf9cd775d0a71c93600afd2aa3a65698deb3a9157751a93a78484cef1a7b880cdc65534a561c88b85edb275850ecc7e5daa631889d90caf531b0af4b070e887723b6b856c820ee53b7e5cbebc05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4ae195c520a7c3863d9f6fb93c36e6d4a2990ad53a84ec060c283877c3992979","proof":"d00fe8e6de367c1bfe98a403bc4efe024c4f9b96b5075e9cead69c265a2d797e8e193bcae10b383a33353a76ab21b08277b26b24d674f148699c94b6c71b8e54d08cc78196b0fe7d3a3fbe22bd1951dd3ecd703d3d4a02593ac827e64a8a7e002a5b7ac55941b78c0a09032b7d6277a63f9a72ae5c52b6e7d3ae51f28c59da7aa2509c7ea49b05263a9811e3ad2b2b573566aae191b2f2725d9bb92baeefaf0ad64f620241e22809f8691ce8cd00caf7be526a57d6f0e299a0cf8129b769c4003167f66586c5418ccd2ea8cca610b687348b591254b6166b3f0eb34ae0b2c90c5671253a983161e945a2d36ffb3119f595975d027f1b6209758c573a90f70e6c4adca8ac2ea6461d3c3398febeec401459fa00aa7cde3b5502c6c5b7d8e0a7714c9f37953437d4b982145c0a933b3dab1b9e5a6fa854826ba5c811f2d9ca650cda538c010ec77bf315cd770a80c7b57bd5e89ed55fe8cb22c7c7f0ecdfcaf07eaeba6ba00b7d271276f306758208bee17fb41a587c9ce5609332c4d540dc4f73b66313e9833d13161361598fc636005b565bd6f703edc885b0b0b6b13acdd7194cb335838bdb7b2b1fe542dfae248630c5a8eac83b5186bddf28bb8d6c74f4658c8a2cec902736405afc185111acd4970657b0c8be099c4108d3211e6b321309dc649becad5e944a313fa36e3b0d7352a617fe4aa96188bcaeb64a518676df4ff44ff802d82d847275e687cd8975609643a60e9ac4e895bed67653bb011b072124a1026ab169716bad701e30ba32e2748bcfb3d49e83668ab625b5dcc4746f582e36143c8e9a2fabeb6887ee5ce87362fb4184ac787cec1fef4feefe5148f418e6c9fcca129580a9431a90ed323833b0029b180178b75c4b63558cc092e9920681966290f6a3e2fdd2ed322e2264a2c2aa8ff4670d95278cbe01a878bf629803"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ea32419f866436d954e482d020962979cddf6d659cd2461ad5ff8b2985bd8444","proof":"c49864a57dc97984c5ab90b4fb04d5dcdd1845fed3fc1b6edff2d2d7065f2e50ec5702b5ec0cbb1546b4568d52ccc69ae6f77e0033b56c5f0dce76962559851bd0f6f9f6028ad2d42a2a6f54cb8bf89d36a5c27ab6dae354e2170ad15184ad589478e6ad0633d5a307f5c69b2caba05ce373478ed7e2c3288acb75c9e121d373a0658e9d496d6cabbc70caacf729c9e283b89cd0e4b05e594387be0a580ffe00fc32d32f7fdd4f882690b8f6136c762c88319478439d988e5aeddb8f29d6b40ce7b22c9e40bd79c90e0e93509cc06e2ec35632b217f0f90f370d984dfe35cb0fd4062f4502558d44f42b140dc4a391cb3dd1784b495ca0437c962118c576f527ccb0d3b275ac089377f03bbfb215d51aa9402f179b27694d13c0106d840af04cb4f0274cacb05d50353e726a0a7482070d4669f92a4ae8adbaf8fc0d60e54608b275ff3fef1052b6fb38e9ecdf7b2fb79b1b7b2a52d2a717b17cf82cef075c13beaf943d8e3b6cd4a49363597d613c6b8c82998bb5c4fea436ede921d1d53a141a6060c68b9bed92a657d8c459aba632a083448c9a08330c943ad1a9b6479664905a1c8a81d4dcd12c98c20e9f2a31aff0955488883594c6ac127e745d62977594606a6fd5956f53939edbb037245b7da9bd7aa5402e7f86dc3743c3c79817666c14328524e60260fe0edbcabc6cf26c23f94b5b5dcb5d5e924b5a63d99e4c5960ac3f4fd6be10f0b46e5388d8b44f9f13a5ca4de644896f2716363e597fbb3db6cd7f95323edaf9d7ffc6758858b80880c4b518437cf15c6461fb13b97082391a82e6ab5713e40e2170459da5e6a63a7b99c94c99dd9a59b81452b19436b027c04f4e46389526b9b531de9ccc9dc8dd80b9ed6df0adb9ac155a0ea561fdd40302362cb2b3c57156ae25ffc72ee8c5e290ed6e62366f1fc36392a43adcea0303"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aca7efb0b10e631e31df4af18d2eb4d7fc00b1cc9b65668ea559ce3a1820361c","proof":"8ea8437aef776c77f4648739446742fedc0e5364c03f4b753980f40459740b58d8c5b542b360caeb5e113caba05a1dc9a90edb9d0ef60456bdea03cd73c6414750af49f4bada744de3d56bafc0dbcff4e56f4153856947cb5f313a1e1911be262aa6bd02a78d6c8e4beacf9e3b436d302e6d18a498952020198aa66ef4f5e40075c6b6a93847c4f132b2cf901d7202caec98340e673ab928c5f46a99b1775c0e84281ac4c04f46bf076413544f0e888c9f21b726e6140f112a9d9d9e1a51a30dee422fb04992c54778b3fdf50dbc8d3ebbfe9abca1c5420567e6b57f9eca7c0c8ede154c4e1b41ae53347eefe3b204bdbe67cd8a9dc452c8b803a25f9904bc08c05a6cd021ac4e5e0b9e93aa0d28a96ff2662efc5d04e35b21da6021b19e96075ad67f2a6bd33d3beb3905e5ef7f7fc6d8a7248017b42ceee3f8846642d9b0103a3fe9231ea2316dc0051577302e1a58c3acea041d09b37103bac3daf6e45a1088d002f9599d096b4f53eb91e6050f879b41c22556a158a9c14a092c04caa339ecedc5ff762ac871b011ba08bef474697bf1819b1830e054de388eb9885b1434b4852802954b1943bf958a8c7a4d93683277ead05f541fdfb12b75c287d10b0a5ad3068261970f4ce16bb74706369139a5c5288d90d310c0eb7acdcdfe5d9f22266f7fca224b28200f4667f8c6786d3a56180a444305d118af8437b1dbdf386bf0458de53e1d38512c25f0f5e3441fa42f565aa0708ffcb59972423c65f53f386cdae3b147ae80c1ef37eb4e93af53fc74e62560faa9a0970f35c42b1089df67ac19f69fcb2b0df34ed4ab5707b9a2be66c5e87e72833920dcb81e83a88607567d8f596e0f4c9c66e4332fec9dd5b3b829db03907e5a7596e304f5d25efe3e0663c9c7bbcd119d58b916f780f754929cc513ad05bdf2195602765cd4608d8e0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1e29be264fabd4c33e649d5e2d77df781dc3ff7791357c55c77e924a6841fa43","proof":"8448d81e4fd70b77785c6457a8d1244bf69b80aec058e4aa41546f6e75f1c97a880ad56727fb65a181b2b371dd09ce3bad6377d6172214f004c9b844598d9946bcdb5d6770a6c7870ce4ba73077649b440e8e308c6c9ef870ad56ce335a8f43ff43292b15863bf434fe9e32df96c6298b44a3f642abe501228463e809232d25ee09fc5b8c35970042676e5d66c56a331869f88ed4a13d0aca2a15ea52a9f2f05dc14709ccd1bc28226ca6beddfaef598e736463ef07f56c8fe871198be999d0c3e0eb75c90965181a6d592efaba094bd1ab67b9ee8993b1490c0413c3c7dce04a43a31e927ac99ebfd8c27d0ca95b14615949c8d1a17ef897d2567c3de88f275aecb01fd6be0a2e5adde860770bf600d4a1a35d87e92db21cabec48796dae676d481b1ef8fdb471167b93532209da30b5245da6d5e890aed204c663baca2370a22ab4188bf2b65d7efb6e6d68b1261b1a7ac598cc29ec8331cc8843db008e0365c3cd923814128a3c327489a75137c122ea2835e610859ec3b1c63746b02a51c74f645fd263c7aab9bf2c8a2b9805845088645057f5e794926ab87bd2561962ad436b9c0457c7df4f8b3eb99085404c2b4594e5bb053e37f6607ae01e18d5649f8bda8801eca3d4acc6364c1c3d259a4df4ce7ef5153aa21dcd1d7738263f21a3e9d586fa9c7088ac68978de2b69c5779aea7d9e361cdeef4e4dd8a6a4892011e83e1d257cb23b0f6f369b4bddca7dbc9d432e0ff98d424fcd5421c1af99324314d1bfccad3d942f5b829204fe6f934207d5f4c765ab1da338f7338528f5dd1468622891afadc7660c78e1e11ce8ab53291aa8e67541cfa28b08254bd67a1c7d2b688b614fdfaae7ecb988bfbea68394aa5d3fe12c909ae77ed07c3e1718550c337d0bf5b8b35dec1b813f2cf4846675a061ab2933f04eeab27000f7e0f53404"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9a9c05bf3b6f8ce2f86f8049e2fce45c641e5c727b9329830c4a054bfeb22665","proof":"66919996ffeb848361f5a34a9f7abc6110e738c9881b045516a2ce2baacc086ca869f65fe2b5bfa9bde0738d133b5638244bce23b0424afff47f62f031cf5b0e629077b1f9a2c34ea3ae1a00621d31a674453643d163e4a8d63c630892afec62ccd95c783b60aa3b6380f75e4c0c67b45abb8a1a94e72add1c10e454a278b921f836d7fb82a3a76cc121f9c53a0ac6bbf47be92df7181115aeb3994bcc33660cef7789a7708591f26b24f4ff276338d00cc545d3c0870d59cb4e73fa2460cc032d1b13a749c3ad08861e5173114a5f58b0026a6e8825c9ebaec5c7c241e8b1011e87886405d67745cf20a4f105d4b1a716bdb15a0e396435dbae9a6ed0eeac0a44b210a73d9b9e3079f2cbf16582b9ef8c02c5ed35bf0193530edb0e989b816f2cfa2e36d24fd515ad780de61944215f5b53c44755b437aaafce0dab9d750d7904ba05f7c3f4cb620dfa7948e47b950efbe8a6e4cd97d497735dfe75ad4a9717c057c1af3d00d6f635bc6463ec8f6d83cbc4bbd1ee1dd95f4f5e5f89483a3e0d2239aeed1a1d19229982197e7567c4eaf3c0928a8dcae836ed70c0d5e89fb4352a79737847d0eb6702989ce92b5a9d94e8c88efef320d334eaf8330bcbc3c3370ea37e41b61b3a4aec91c1b81538902f11b9ea67c983a72b1d9b14749750981242494f1c4734254ec228e2698d0f18ed3d15c68dddb400841ecdfc413847565f124c5ec1ae502410c0a7d3c4c5f4edfdc0e83cd4fa418e7dbbbd9a8b17e9e72aa0a29fc0ca6f768544c08b90777a3d2edcd5ccdb31169cf2a1b0683c3486cf19a23025ba71f4d0896fe66e8d3e59b4edd69eac22393f4c2ed03ada8920382c4e19442915046978ea561ff695a01e2c82233b79a97bcadc1848fa5c4eb3d0d405cd1ec3a525a94c869f27535fd1fd3c83b270af6937e64e2fd74c2be70a86fe03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4ebc36df6a0569435dc4a4ac9ee08948bb0418792c674bdb4996d5d64e94e16b","proof":"948ae70ddaf6971628cc580ff2df61405aab9f979a2777aaadc934541b0a63408e7fd6cb5ceb2e64a7cf6a890f5f721852bab3674a7612bb853f351731a0ed1c36c67cb94623ac2bc44955c7b689328ff6fa0c8bdab5bf4f87fce19a995d5871ecff72fef266021d02688fd242373104a5729de218ad8eccaf7b461d5ae016462e1040c07c231d719781b2c365d59987fed639e29a871f4ac7b4f6f503eaf50af9c0212d5a1ba34ad56ed2ce5e906218c3d689b36663c41aa0e0e71903dae00c366355c224b9a479f3c5d8561cdb6392d16392b0f30c016293ed8498a5d7e40646a3925580b441bc66c99c792b12a3dee12d0f5992f6dcc841ae38f1a126ef3df0ca6311eb6d1febd971d355f6aaedad78cffc64f0a9e62f93a43723d35d710b1ac987215e5c88aaa2d1ff6ce7bb9e03c8a63a0ce5650ad0129fa9fb5c903e4bdc2aef927295f211eb2056b38993d1ecee5e1b24ed1b803a0f7597f53c67a965e288fb512efa21cc892a768e21938cd5ce744f3e9e9f5312c0474254766521731c53ce40ec693cdeab1c7476d59164668dd181622e050d47f17d3c53b01e21763c0c1ce622d29cde71d8344a31c601a6fedbb29eb5f6ba9ef573303bdfa4d45f9cdb47e067ca07c3a90c2be98d05dec5d94a0cf8e53fd90977c4b7b650553a665296a82e9bafa2178299f5c1db86800108d3ce7b278e7fbd2501366ecfef0228b4300ba30c7d21450cb0780855a21f1b0af11f27c216f483e32d23c231489f520cab532df09f7112076c5abae750da427d4b10d24842a6616d5657521fe29539025eea8c0afbf6d79bb2dd29321c028ce8eca43689216607e18b46f65c16a03ffb03b9e760a6a005c282e9d0e58c2a9b444e4a36dfcb655cbf4271bcbfdecf0a3df3ce2b9ecb83e6215cdf25c988796b9c16e8d2f63b5d07795c36661316270e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"de1d4aeab361e803f80f10a7db166426a468f757d1875ef8adbb5c0550cd8718","proof":"8c6d07cff5fc8112568abf35ec5047ac905ff52599e450d043b9904f154112065a7fe9aded3f87a138922aa109f3320add64a6946c2ac0fc99197be1a5517d4004b0761f20ab7e7178a864e8e55c54a656b10e9826b6e9cc833ad1bf9da1af6fe406337183065e2f317d8630ca3ed54a6db6b3a08992d61fc9aa2e9a7ce3e04a3944bd7e755c767a18d2c93898127f9feda6512e2a4d62c8fe666a84063d380a4ac476a5dfbcd98c01a77d4e7a09d99988813c693b1428b739decd248aba020b89eacbac4054eb93d10ebb06d4d7edd00768c7623fe8331b989f0367c130390df2fd279c733c7122f1a3b79eaa55948e4def63a274e7091bf3c0ab02061a4772f243ef9cd7b847ac621ba8915e45994e31c2e82ec9bb9bacd473adb8f471b3428268eaf29a1efa96f3f40558d18715a41d3ddbe5e6c41e529c8ec161b2f6753aa6213a7c770d1d5db2ddf5d5699923c3d5e0aec7e58e75b36f8db404a70f731f38c073653a5576773575b9fb182a8d86b9b8ed98b981b5e566fe1d16aa132c2bfc7557ed1f3b22867525118545c865a532e4e941dc721f79ec48abab4d11034a240130965fb6feeb5ab39545e85255e29504dfb14b298818b685bb5b1781904bf8dfee26b1bfef30e9a0898babc417670b6ba64edb5183bf1aa0d70a89b2b952046fd78ed2a5cee251e8889e877ab56311018a34f9a28768dc06dcc2e8ed4b67460716c8600164d1b19bde69ac108ae3f40d76255d5ea413cd053add69ee7279523cd5d9d6536f0403d59b2e7f8ceaea7bba4b6de0d18f6e1bbddd2dd036e64ba8b712594c01085633053b145e4713fb6e4b3b9241dd6b7ff204bf7e4c2e817e41f068955e0f974cacc55a7942b295659f23d81d45d7f7ddf660f498e8b9f80b5499da39555a2901ac0fdf5dfa11dab71ae39e9232ecdef0f248826aaeb1570a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f82ff22395d8ad503e0d0931f7834ae24631e907bf6d4fb5ada9485bd9cb4e12","proof":"cac30ef9398a8bb89867763d226f418acecc4ca96ff7d878df25c8bdd6b97804b0accd65e7905ebf1afc3a6e5aab59b505a56e8d821667f48475d1892811cd4060db6a6e40c6a45be61a80c887a58b9e914265683fc9d5cddc7c1721c3142203905c190faa63a51e2d28638ffaa23f85f3d40a040a972259d01ce5e7605c9d0db187858c7818f85052aecbc5e94979133a56d5d344c816fa775838209d891a0f5fe11b9b4650fcbe4f5b6fff4062665414b1e604d12e2885dfc49b7b998c4a097d061907c2f6e6e696cc48d27d20044449e4ee4ef0c42544ff8da10037d6cf0240862252d1fd8a305cae265c5a57df7d41419d54071f007cec0d2f32b91d8c7e680fc3be565d277bd88cd5e4ab7fc1915fc1a7032926dd9d9d93399d22ea1379b2285534340a1d99405ed3cd221491cf41ec18c43aafcbd2a7a2f9c39a6ec6396cf0b700640408412653cce20611a74b9cda5e12e327c0c8b3ae1aca368acb3fee9fc2313f967d745649a7bfbc10d660370575088738cd7a24f7f0648e608e155ab0d4c1dc480ef4cd4ab6269cba53e1a3784715d1c72f71cf7e96fadbf0b7754e17f5b764e816aea5f60468ee028f600bfd1fc09d7d835e5876a3ff3651166f88822dc013d79aabb127492d959ce1b4acfb92e7e4dd14ebf9b83fcce12c8c025cb67b7fc3169bc581343637200451475d8d7f7827a8f1e49a98f40ca0039d6c805d0016278a3c5eef06ce157bfa16b520af8741e134939f9c604a08a85fd5492a40cf45bcf0538eac9aab3f64a7ace53904e9aee5577a144b814421784320678e82234da225f3a9c05b3a05ce04715f55c36980f07ac4a41baf3e9c7eb598695ba43ad9e13018e2b73fec72f20d5e8f0efbb9c5135808e25bb18351341862020e354fc7a53bc37a7ff8313ef6a3e7ec7cc9f90e0c97f2e10b0fef038b3de907"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b4c331a9b9dcb682f4b691778708e3627f75cbe86186d17906dadfc9252d5227","proof":"3eca5afde06a44fae02893ae22f66a1b8193a9495f4380ac6fda88155d37a574d865a063986237c75494db2940f5b0d19ac16afdb1904a4acdfe98d36ecbab0ebca6e079201ef88f925a735e6f2bad32ddd29ff394987f1a3844775bbe94d71d724f742b24aff67470eae28081619b4797543b26f5b783bd22e82c75983a5651109059f1513f879fd76e59b441f3e63499b34099268809418fe960832198780ede5655a5c2e5e99111c7919bad1b76c66c7238f1f50bb40669189d3bdff8860aa00d919df06e37c31431bf52df24acd938b2568caee4f2938a11109e93959a0552220861a397a5db43bab7031236942a4a88ee5563bf62b15aebc57b7d449834e02a95f9bac2fba86f448c4caad837fdb726b13f6ce1d38e307d8ecdf4f0b26d787cf59dd8def550cf03953d49500736e619251717b775028d14785b66b8d53a440a2c6079d8310939b7c49d1a39444265b59d093819860ba9718df6c042c53e3c21a3883df043a51c399b9bf4a1229749357efc156a3e27ec2a17df8d23486b6249b748706c4c3945cf1920bc49022691e6fecc4f00a633ab1210fb7735062828e395215acd112916219edeca894bedb93b6e79bc431deeecc275779ffa336a9eab0d6935188208f15e2a9fd5f210c5784a83b791666d995d9678cfff99960f925e36485fe74015d0a1efa39451a40bc0e7bb615cd56a3f7dbdb43ce0140125e2c962017601c48a93be86d3ebc362ef139a5abdc47cb61a28d319565ac0f24b1e61537f079d81ddce457706fb71687e27407ef063c05a7c33238428b9954709544f6da63305b704e6d11bd602cc7ae48dc512bb4b73f6114da60f3f7e42cb100152b62a0cff22e8e1cbe905c56ed0498a20a0a4c3588702cc46d7eb466ee106aa34a9537b6d17d5cbb9590a8861cef57e6e243c681807efca5ae45e8594ea0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b00e77c3a37f7a8de38fda2afc21cf527039a6e4f1353922d995e2ed16472e70","proof":"9e4c5c4926cd44ba43ed8c5fd84f5344e09bacf239f8f0a175b07e8a3479e05f5a32988a9585c42eac4d158763c2e19036de58b5686ead7d7b9a3e87cf91d63504c2bca90209ca1257a5bb58cc28b8ffe82d5471a75e734b4261f3b5fe56590d8ab73fc91b23ffb3f5b1141cb98b9cbce068659d37fbe77322e758e5fd97227e1a86eb88812364c5422a4f2edb71b435b05315898adc39bef3332e097229b603e94dffc6dbe0b88919af57aac2c769cba27c2029dafb79808a23295b3262e904930df2462d876649b528d38fbc4ed00499b61f3a6e643f451e2178feb9b1f60e94a20bc1edb366c768eb5add5157db1d0450641c752a6afd4bbf3ed118260a724ce796e50879d412f1c01f293198ab100c91555d798f7e8a447605e039c55405286ca574d3cf5b02d74d4fe958eead278b89131dcfaeca14f10613c1a7e6ce2eca12b2950e3390f5c4ee3cc38f2c65b4306c2a6134d0c7af88e10f2b11774c0a92a381cdc74abd17af1724156de18c0613dc49f3e47e1f1c2114fa01f795106f98e75d8015af59611ffedc5d096643bee5e743367beea6315d34f8f6fca24c5112c716ab8ad7c88d717a9685f60de017208585add5a679a0a38652c5c5ebb61d78beab1b385bfc18f1b31d8d342bf6ef33d2442aedbda99ccbd3308f7e4bad2084bca6b70604493f4da6d2790b7199bccb079196011df70747cfc0a870c3a1628c195ec77ab1cbb8755d5830088bfe46bd3028f7eddec673967e5da3d541a86496e6856b806954fd5e76ab213db183a884f46252396518db64067731ddcf99131cef774ae914715b98f308ed9a6bca898c5c6de9d4232f42dffe26a54e96a1271bea870832ac4f1c1ce831c1689a49d03518e1eb788e4014dd37d5d86f69f209fa6805a3cae6a9e6c05202fbbdc0ff83dccbebd61638235f1b792a5f77e6990c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ce636941bb9e274b9655fd57ef57b57492104db53ec3a4707959af910aa23215","proof":"1e14d3931094de2c02628afeb465ac6fe7d0e376fcdf5621846c0b05c067845a886ebcd14d3090860c3018b3f31b6c38b2a507e0be466cc117d411a8a412126d98089ab377c07aa2218599487bee7db0d47a2c2c363459c36c4a46a33702ca2ec08a3c3a689f8c49262d58cc1840682416112a968ce04dc6dfa66c488fd9922b0ca1b6328e9166bb5e5b861604bb183d72912f57472bc80ba5fba95cc47632063dc2132554857bef7f2768f66ea5fa181c174834326dac1c772a6d06d2e5f40d244ecace7318419e35c22c0755a1350499eeafcee9e3ca7e738d6a11a2557e00e4f97e16b3254538584c02d1d998f29f1411748f4b136b9d0d23ef61c9dd110aea0a18990c78d6e69d161860103deb6a673d072ddb06cfd477625cc50159bf442a3a9ea0392d7be2a5416015e2b8876ea661289da5236eaa95a48585c2733931cc244a3791a2783a21c64b19d3e5115a54bf4229e9feef73e59df3306e3c5624d8201a522a195a1967c173ec7a9b3e77ce228c962557d6352a36b2d3e15f12183eb8943acc5a04aed0a76b853da394e49ac44360a4562cad2cb86581f0aded6b820db99ba4b1201ef95ed1eb92f4be287613e6e105f08c8e5c1f6181bc39e940b0ce959af08beea7f3398dbcc15cdfbb452b839784c626a7af200f9dc0e0376a3e334db375c39adee775081b5c2a57407302a70087648274e609bf5b8f23e27ec8fd075f8c0f6498a34ed065587ac5231bf3718813d9a928cefd479ea2954d33107968b482084fdd818a110f16c190c7516d750503e0dd4a91ff25204d2cbd440aa6f2c3f53d6c773fc15f0a55e48dd3841aaffd53730f73ab35b6efd7fb8974ca870f4ba3a56ea9c92ae07e3bc6ea8aa8ab51cd8aa2b35ee5887b43be64660d1b6a502aeb804ff7ad72a31978734912e4c33ed457dbf7728848d03b48d86d0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"020496e704ca017e90ef938c14f53cd35c963d2f90f41ce6a7ba1371821f466a","proof":"dcd07a948f35dcddf57deba6f329ce6d9cc72f8881c29d1fcfc7799c28bb6a047a999fe4973ed196b009bd89f7ef6961c6df4f8a63e5647916b4e819a87a602dfc92322ad49c9ec853609e98a8f339f578d9d6a8dd12d36a8a1062f350942162785a97924e76e890f1ab4b914acd86f60217ea9515433dd174c3eaf4d1074177b961b0d01bec5f6947b5401b213ced9ddc8bccf07802de995afad3be8e501f00135ec8a2a452182ac128e9eec12d14e980f93944a8e6024b71d2c33c38937c02925065b74203fdf86a23c6b6b6ad00373a4e23fdeae60cf589ce0a43d7ed34095eabefe20bd8fc92b929503ef8ca4fc57572930741f602f9a2fd968e0adfa02c9e7404ca482da1d1f87966189a6d4efb6f77d43b16ff18e3273fb8fd7987e648de2f4c82f5b3dbe4d7d8d8135d89237883f4ae20237209d074796f75c0d6ad5fc821500a6bac534078ab61e2b91f29d35363d9d7e7d9f4d2177d70f12319d33ffa07fc53716eb098a23fb66100d54e41035fc28c0dd7e07a6ae2c7f0cddd8218e81e03895e0712ced711a24d91f0309df043c01bc0954552ecbddf43da86a576a05f54ef3c894cbcc751fe0763f7c6ef2db0921389f138c2113681da3aa238727026c7603bb703920231f41b54bc7feabe87d26897d01b71ff2f5eea7ff64866d84ebee997dab0df3ecf9559c6180cd47ea60fbe3919cbb33c046926f384e711c2a154c6ca78a42715037571fabf640bb59f74cb6d5e4995ff60300544148d344a434aa37aedd8ebb24fbfac48d5904c244ac638a861fc3959c84d9813fa0b02cc91535818da4f099667f8f4fb5541c74f461d8e72dede0f06789f06c031ce613c0d1c90089aba6d1da8f3d0875fc2abccefbb0d440b8441931c9bbd63fd51022c6cf427148f40489ad71b1e0e14aac36784efa799b5d4d12930a7582c713409"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2e397dccfe5464981a686dc684f4d50fdec1586c08d6c638239d044160f1a426","proof":"8affdf68e72900281b77d083d6bcf63288835b8556945da680ba4e3a907889484e84f873922f7b977f7c68bbe8d92602a69bc5737e32baf25e4217bbbb9ca13fb2ddccf2f68ee32657fad52421fbabeb6b388fa53b92623e8584a403f6a26e6a7ab13e2af0ae2fc8a62da0e1e0d6dedc7e98055fb3fc23bde35012a32755ad6090a61e687bb8d337be62ccd75a4ab4d2275ccf7e6e620708e1f473e545c590047a3ef8006fd2be7b0bd79446ae6f02369f97ff034c287d3e9b9c49d848f055003f0715b4181a236764b416041d8e62dc47a4c2921916d5890c0c763c9e8aa9022c4d9f887a9efe0486e5c4b23e0d2e447681bd8a4560d0ee5af3b3428355054e9cb8d10e6e1e63e0d42b186b141bfae844dff7e44b367128cd044ed1aa2c984364e777f535490e8cc928c9e98322867924fed448fd50c94ae694b83dd7641f55aa3d0c5497e3a736a780fbd787a6db62b20924af97ebd36194154ac5d17b3c3022688c955e338b642e7b2760e957e6abf054d74cd928b5edb9b663fae520f95704d8903666d2585df26386488554b3b715070d26fe854e910be8b4920604f51b20af21a3ecd6c972f38f7a38871a3b03b7c4f1b8458f9c6a42aa3d535a5e7c37c2e0612015e7875b1fa5b9af70f938933a8afd017250107bc16845f2d2235c288af52b5967d6d2ab5ec071dbf9a25d4ad08a45c28005b554791c7d4786a9412368ee7d7809e7e3ff66db495533aba0ac6bd2306805cd4b3a4dc84f76840c8f17acbab780a733f11df198e3bbe392e8bb116656bed4a2d21fed0159c112983b45d211b487112536820ed409ef37dd8bf9478af34d2cc9cd05d23cea561d32315c3b9b7d312df33cac7e73c9a89ec280e4f4917592693104658f6e1a893ff04007b84c4cfb6fd4dba2f0e15f3f54c1861722beeecb1bbe4ca9612fcc2fe4a84d01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a8ab7b2bb692ade5ee38ddf8b659ef3648be5a5a63e390e66529c533fdd2fd5e","proof":"42ee4ec4e37f6eec111e6804a4289f1f9af2c5dbde9ce0f6104eebe17ee2613fa2f74c973f9355f79297ff48837c5a6a80a2498aead75ba64b7445672b731414ac620891ba69773b870c71070f28bdd013933ce89bc01eb1348b8a8a4646d87bf4153fb45bff1050e66c967f74a1a34a0a1136acbf2bb91e03f3852ce0a2a427b21e70cd2006ac091952db0db51efd9e668a0c4da44d26c1f463afc010564406d44bda3928e0127a4a99cc4ae1292eb74d3b4ee9579084b3c883682335056d08fc01b0142aff3924555b7a08cf8fe6b541edd69f9652b6d612257ce664a6330f10f031a663c9bdeda1a937eb20dabf7c67b7b9f1b450cc2c30cf5d29e5085776fe58dc853db2a7bff457dcd26d1520f3e39d8aceda1813907fdd678facc3e8185011bb72a449bc225ce88165f97db186379cea1eddbe6cf15f80419bca603479c01b28db58bfbac0291332c7ee6fd59267d66b7c1d72f878ed767a775d4c076b92ecb6e42a22b46eb114c6b7cf846262bbf56a5a02e88a1f4334a2aa02c3384408079122a41a2fdfb9b292e62c3dcf36cbfb759e3a95539285c28fb17996f07ad8ac448b5926000141f5b04f6db5136060dee77017e95ee5b901cfbcc7face77225cd6019c7a18a1d5401b1b773a504b9826f2fb6ac297b8a70ecc5df811274a767c40b400b08f30955fcb6489448980d794e928b837bff777d74d621100fe22023ab8e3024d6ef7c8076461054f7709f021bcf36c19c3f5b1992fbeb1ae8333e0cb611c640951c736c1000401ba1f72ec42c7a68dfab50dcdec9e6f2c49ba35f44f00528d4ad029b813bfda7f81113fb0fb5c0448ba42dc86e6cf7e1403386976f2492f518d9ae30cc487a40151d506f2105c9e351ba35300bc711d97a10407df5ceb288b6a03ae9e0d09ae97ab1f6e4c008cab9d2f31c6a735f9bcace49906"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e254266efeb1841509ceb537797c15300d2d16baae519e9c7629c1197012963c","proof":"248f51fa14f909879f849a6a65d11f4e43e8bed4bb2aff9b615a68cf83499713c411c17b509a8753b5e14500340ef1c04dfb4150da866754575c611d66c34775cc1b98cd0fb3e08c6b29985381e401f09e0487a3ef8b17fac665e4ec455f9a2e1486e675757f0f2daaf9eb5df0b4a87d64b0dfa2490d8f1843ced165de0b605932fa12dccddfe65ab0b44902ff756baf4ef907eec0779bb1a395be6014eb500e8609f296c07ef7f70b7b5122749d7216c599559a96ac974026f72b7bdc1f990fa3e086ceb4366b8e21b1b426ebed7ccce52426d063ce9f74f9e9040c4dec5a08dae2896cf714e797461f280d5a21c240341a35c7010ac58f0ec97821e410ff6a1c8db258d75dc8e81a5d58e32260c5a5d4014139b9251d8d580d17d3ed81a2301403db6d3576f811bb50f74003ab3486bebba341b4fc3b66630478815be0f52632e0e18a81f9e6282de2be50f1a5e1903388e61096fc4b3efcf3c9ffa3c1e8534ab10cfc201296db416b96eba041239898a35037fb0b49fa721891a9d0fe477df477642cf0ed054fc150899465511a8d0b1566c15ef59dce30b9bb9841378d2a3ec4b8d457c783d6590d7feecd5599c148a0b1dc08b789cc705a0b5f96aadb49108ca018d8e5d2a4dac7261c6933476ebceba4910ea16daf824e8441e5d0043b202b7956b86384faaece0abb2a66a75b05981f9d1e60244ffe01d71ee6aea93f986441803a378be140e7df67e1e6cb7ead018aeff9a4306b76382ddaec2fe20c5a23766bc15ec68c333f31cc5c7cef8b5d8ad84eef886c708ae96c29f7884261862d704ea4c18aca0669d6ec5efa8cc926689073ddc55012e32c13374a87f254cb1703387571e5a02ec463a4f8ca96f1f5e4c27ce6d4072fcefc56121539f90ce565bbde6c082053ba44da43a246b0fb65e3bff0b36e14c70be769d4dad1da02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f63594bb6c4b3693f35b5c4739fe4f1f963474e1d554cc88299459e38c562338","proof":"50a91e9f0ef0ee8e96bd973eb4b882ea0cca53607ab38292a24a4eedbdb25343dc47d011d7d0739e83f1efac6470f3d18e1aa76fda37da7fc728593f1ea7517742fe6d71839fb19383ed1ece6de52d933d804c7b22d3c771f0debb124f2df71234377edcd508ae26aa89a891374c3c1c444b0fc008d4d8f3fc62557697a8af1313278b2ccc7e52bce8571b2f4113e08cef163378d71f324df65ab76c3fc7ea002072c23e0cd6326e850fb93a523ab3587456bcd16ad924b8576517c06677c90c1f12c236df08dcdad880c246602e39bd473bc3fcd0c76884cdb3314333a2dd0d4aeaf75fe74a2eb75ed7be35673b4f25bc694df7ff66cb890fb122b583980f5f260e969ed69a705be62b467c7233c68499c6436cab07c270e8c2bb2708d908240c617d4ad49662ab70addc1eb36db28bf85d1c118d6f2a7b1218505ff087e239728c878b1031ee4ceba62180a091c354697ce2475608a57bad22d3bc1857996594e58fd7215acd2aa427051d0f72c0fbf889969cbb10efc8ee23c7655d530e2a242ce355e7f4b1b92723eba2e223177b4871b38b7c26a60d8ef5c84a4cc2ac373297a2b3a24d19ec55708380cd23b685dfc37a7d243a00348e620d7ea7e49f6d245261e68ad91c2e296100ac8a9b564410320ae75a0e249b1b46f9e265dba8195885555561f813326e9f19640bdafc3b6355d43daaf11a43ee3808de85b7721a3666d8cd2b0663cef60b89dae2ff7e14b4093d9ad097af32a79528d9b75bf43e5803f428fb4d66095d7478825172f1a48fd2eb9e849ff425b29208c8ef6dd35110853aff2188d7b60e326435e1ebfdcf18c832af6698ae01445c92088dcf8a3de86beca8c5a58e7b56fe22d6df7a63be9a59f22778d2b9bcfc0ef54614a2f40ce5d270e0dfcdb6f874d154c06c38ff75ce16d346da8256b41ea669c71340e105"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2a0f9cd3b035f66b1567dab8e05d9eac9c6eefb78d5878eb3289d9173c0f280e","proof":"3a20ed2e4459f14a335301a3ad0e4cf031ec1b57a4704b0a92f40421be0d7e36040a6c7e66f0834e31fb40ee11e2bd13ce4c816121f4584405b0b7e468dd37598aa5912a2c42643ac4a52093f3ca755cf93c56be6479895efc1f88a62d1692327c6d3c27d23518d6a88211095b78cd3fc7b271595683804c7fdeb03c5abd5c4653ec6738d89ceffc07717dcf92b0f1dc0fa1eec2eacf100578e7d76c689cef07b32b1091de6349652e244d629b97ef7fba44d33451ca153bdb9153c7ca797f0e15cb139a07cd67ca3c2196445775d01f030e51b604c3616d71ad8fce9203ac08e8a69e204cd28d62c7d632e9ff164f773435218202b117bd16602bdce1010e01b8a89c3d128d8bdb855b0357be6573a5c1769dca1bf458133f7dcc967712994a82179daa46d6928d8d3e7559a73a551d914d8308bfd4ec1c53cee0de2209076e429c83cbd0cb68b09dde700208535a6a44ea8d3167e1dcd9d9a4d76f8409994b041c2fe8d0b292d23d1fcaee8dbaa72b99a8924cd2a6a89c4fb3c427b5a2ff7c6ccec5acbce9674490710eb089c49cdb3f0b9dfc7f289bc5007a75d6530d2f15d2892dd7f49cbf12388348a95248058b85b04748b6d9211cfc0f2bda40e26722e46a2a71af98f3f3e55fd4c153c3b84e1335e2c124826573de02b47a57518b1890d7910301225930923a95bd050d5949ba7ff999d7ebd1af0116b0cfab069f48e03a4cf91e0508e510b066fe002051a0b1e32ad164917c8e58f56506840de34b5aad327395807619a8d3aad94585d5298ef5a4399886cb9b67c99d346d56fa00b871a88313d914298a3db627fb2686974ccaed587142b618dfa283d9f9582c45145158118b295a4c2fd15e4475e66e567683d915d9926b99ec153605a1e79c0dbde78b4d30486cb4ce77636e1b55a697cba484d6c8c28a83e2a7f2b7db1b610a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e20c18f104809c15e64d4f0eccf7140a916b23e1a5b161a69cbea5bc327b7d63","proof":"a8c1d7e88be7991db3f919548d7e6249b3b5cebcae554a338b00ae1172cc150886e6b2c346ab019b2ff10f2a3adf6b7d310f4a8c5f4351d9c72a0dc53854f64a6ea2826df2f64da64c5d04301bceb6cfb1086d429888cfa08db5a6d4bcb3823bc4c089a9d8ab60d1e23ff7a73ffeb6f1cdcef5a767c0ed7e68f8d20ebd430471ea434510ae14bccab8582544925852f77679d1cd61bebb2938af9ab891161102ade958421bf3d7b5828695454b8b135b3a98e41d14dcf01780de5dc0a370680384ff104eacadd14315bcd3f0b3244e0882c1da65d34871c780423821a0e8ad0272581ab4c16cafa5fe7363890dcbb29a53c2e7e1104c1aaf29731e5695109e70260e251cec203c26643eabd63a01d6af919ad43d8f1ceda652ca004cbe5fab38c898d99fd0da0bd1b1fa325c8393fa74eef713f1e09278f69105c559d8eb3a2d60e91cb47f4b7152cd295bc3e9fa20582697ccfd999e163efe302ac54170367bd880cf52a0c62660acdacb16719eec92fe947de26afb6c7d33f35e5f5cf012163ad2bbee3df316fbbbfed572b877a5a74810b89547b5354f9abb653a73ef7362541b6f4478dc715f25a5c0272d41232aab70062f94c05cf68d80662d91f35f6bfeb7651d2f33432d44efd3f8f46fef3b12c6a8f435d37358d316a5dda8d150564a6281523f57013715424d2b24d3ce25b0e94994740cee3fd19029a180c2df554a0047f1dbbd0ad5679759438b4bcbcfbe30ea0a5eb7c2d0f75474c923013f3d7cee74a3a8ae302f054c67aa63ee65f30bebc895e6f213a392257e2e975e5a4ea8153e9c46cb5bf9f1272363e229d6d4209f6b946ea6e58c5bdede73ce5a286ad022ef3e061f4b94ed1d257f3af0c8aca4bb4d0f2f9cbabeb154f2d61ec245050319df3ffc07fdfe5c4657772af3fbcef30339475a7709c4497814e2e94ea502"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"80e5628ef8b5bc17cac16ae3cbb68a4cc63b28a779a11cd40faeeb6e9d80b827","proof":"a85d43eacc1fe7ea4a83f8fc2f8a47fb71cf72bc8dca8ee2c8ccd586150f5d45e4630b9bf779b84e05d045f5c7c2df13f1ea69b32909846928db38021ee4e124a41f16ae6533b4eb8e6103b414218befb1f72908fc44d3bd6ca602362e245d5676e9313cefcc1fce03d24d05b7876de40923dca685905e80d46436422b0ca22577c39f53680a7abd56c9e82381d636dae522acf10225cdca266aaad16f7a240302c4aaf1f6bf203d622fcec5f22bf41dd5bf6b087b5e9acd6e42119a1739dc0f145c959f1d1a6996ee5d1e268996bfdc349e4b22479bc6325042ad81260178024e9060171216efaecfdcbb1dd9bf5c93beea6b8cf6860bccda95c1d203571b017208dd531b9a61436037a86668ba3dfc6685ff9d8205234bfbbd241af1f2f444c4fab7d660a13d1fcc27593f346db51c7c546c95bfa6c3b346dc6fa8609e9a0340789962c7dcd098742f1290a00dc615030dde17f1ce39fc2f287cf89fe67b15e241c4211be4f1cf13694f1c8955615053574d6f7a86930634adc01bde5dae4d3c1d4319e4115f34244f6decae28424c3932644a57adedf72feea11a2c9b7e1adcf705e999511b5e4b08f112f5118acdac374a297d3aad4c4bcfa0410c06c5678c475c14f1ee4e25f5cd831e410fbad5b52429e5dfe732bdbc0d5d015d81ab245ed60e0cd1d9b08c431c79f546bec194d0280b4f59c3bb4d73995e0259f9ad1b406782ca73bd683c801ca9f595961210437bb4d93791cb481de47702f9cf9d0884513e1591b9c771919b8b71ac29f971f1e37a5341099347765d86b455129a4e20dc0ac9fd08df3375bb8f1f5fe2f6f2e36ae0b0de88c548f4645aef8f75742592e75af0032b9ca6239bc7da98c93d2fad479879860c947c15f3d1761dcefb08e4d36d8356f07ebfd41c2816bd002786cc4ac3833929d7a65a9f173c4c6bb303"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b241ce8a75379bb4c20f132066366d2d2bfe7198452854d6a7c63b461c693706","proof":"7464e031f8365b8a54f9f7a9e80991c51ffd7a952a1f566e6ac7888868ca5e0b4e5f5ad2687ef150b10b099e523712cae66a55bcb4539b3dae6b94de1c2e9f5d069bc0d1814981db7aa0d1265c806e68b789fcf63624e4ae118291752fdc81285e5ca0dcb5946dcfc701ed4356446f3df0f813362cebb5522124efa10f5cff421e56c753173e4d24aceafb727e9ea00c73d6569850f386234dbe84b91210d708eb807209c6e41a8db1d1855357c56a72c6ac9dde272248c480f005ae8074a20c573242d0b36fa09e2d54a329a105ea7bb00918e63aa5b2f7fe2327a5b6b5e60714901ca096e31b3713818f63701d91cef63a44b9ad7d770819ad0bc3857b0d54585e16107c74105594efe266e5610e1b4d02c36ff05883ffcc0380767306c31c0cff817761ed1bd113c6bee55f089bc7bf7f0b8a33b2e9ab87e655d321f6f65d449e9594ec31fe87b08837aeea97e17ae22bb0f1d1f825a79fef1b8ec7f2a62c48c3da41a554425bd4f2a7cbbf6eb3e3f5b740fae678002bc0ee34197e14160d163cebe2714cb6bd95557fd2c19116bb2c32bedaa83cc6691bb831cd1789ca7a5e160b3591b2f189a784b1ce3e7c56a40fea5c3c1e7a7cc7b1d9f0890768c41ef097f309419c737b3541642e56bc90cd4486183db23b4cb3c0e6266b3f8f9336246836c9b6cb616a8a76fe6f775c6403a2698782a92a7e17eb2a72482282ea68ca00938193cd0f48ba3c0c4ef5b5f241167709f88908ed98d4460615d76f4d11e0ad365fbbef5841771d45b0cce3b60b78f5bc6a4529685dde26543067347c566a14548284053978e786e4a6b404fd9f046dcb300381af26dfe8ed285123870f9584f0aa5aa01426135a0e5169c660e77d52edf0c8ed2a0bb15d75b749f2920571a8cb8ec1cf06a76b7a082fef5b8c246ac6420e48c0cdf8c6e4fcfa23217d01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"68cc9084e9a3866082163facb8231c18a28eedfa9a6a43e59034a5c2eb750a43","proof":"08c53b35b57230142f19100420928391b4b7e615c0ee6f5479cc7ac40fc77e29da163ab796dc192f46d0ba2da2dcc3f35c8c8dcb57dc1e4ebb5796079d49a8772a3b72b34abc5d245c0297659f0e52244ac49c015edc42c38beddc590345bc5008691a693a5c9a8b5e4b24337b3657c1714883b4661769af87620e5f887900696f2c978bf606fd173d2d733d93f2416c83a8b592c8d11342ab8d761ce5e795057db064fa9605c7e3abfb2afd24be37b44413bcd4e7517b691f12428e769a040580f77be14ec7c74fc7047930cd36b1ed37a83be68cc49b1c026a874077d5fe0fd688c19b9dcb2529132c8fda747d6695ddf45b6c1a933df7d02d352cd912596d7c1ae8fd9ee6f0837659f0ac4feee44b7582faa5846fda474aedac7abf552c4ecaffcb7ce54119d71934198c320061b90d3ba321141e537a6e89294cbc0ed271fe1ac925815111ee8b74a18affe812f16b74e1504d5d04abc8a32cfd64161422c0d355837ab9af78ec3dd4b6098e35d49119bae2d9b6c82cb21e3b86079866753a54be7bdbc9e4f75afc6f3baa89d9433b882902184aa2592e42f512172e7e2ec218a25b625b05c4f7eb626da1779422da941a13d4d8c696e03e326b3b22877ed4c85af94c4a04918b3b813a26b598d27d29ea8b1446bdd425113d943ec9dd15d4eb77e9f40ea60367f12d07e827139fc7edf20c5f8c5d1c23b5a0c5e9e3d5513c6cdb0d03918ab12ef2845fe60049e0d3613aef56cecfe7e71d31368f2ff80aecb9b5cb0b5c6830cb758b930be22b8e3a82a4a30fb94cc7ffe2112c1b36ea1d9412f18be67ff2a2844c495db27d9a2afa15e271ea263a1e1d35dbdb9e2b17043ad77b0692d439210acca13a43dbc3801927235747dffb0bdcaec5614fe2120d5ea0ef50b4185cff5c962d37b953013db9de42e1392f2d462dff7746f591860a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f430d417a20b60bb97dc8511f505dda8285b6ddcbf5fafc7ac187e3159663925","proof":"2e37b288bacb080b18db2141984bec2cd24f34780ff4f9730cb440de0e5d99775098a86ccf941ab14e32dc01d60520ad9be0b78cd0dd6b193cb102072d4fa5726403cb302a5449aea06bcbc1c795b1eea9cea93e7a12102eebac05bc3f77a2309090257647e4b9359283c0861ee8a2b79c8d7c7bb4e2f8a0fdb9abd678c8086ea5089c808d69ec56e20372b4353e9a434d589c072aa11c486383427dd9382b009602f47427962ca476ef421b6bc6dfcf0b57a562d0fc4df69c5035529b9501050cda8998747fd098a01039b1255d64f8470186093a2af9dbd37886f51672b208fa14621a4a9fee942ba53dc0065dc30eff50a73779f3a30e96813fc852f97b761ad0f22746038fa076f535e0f3a7ff71f54470cb12bb2c1fd23f272dfe5994254a44a4438854d0a9998bac23a2110c68ef3d1369b11a51f8952489ef23077c3a4c1b34022f830c1f6149aa31bd0258b3c0ba56e61da679f5d58dc4839485c2779c652b2e43e4ae3b6e2bb117aa67c4849c113da3ac6abe0c4a29f73e0c806944c80bc19d1d934924ab0e8fea6ce8cdc7f45f95a954d1a703cbc675adaeb18c18ea228380413f877f3e7c5fd22ef37d053bf18280aa1c75d04f22f248c13a2f25c00fc4589e88dc2a799a48ba978d9f445afd5ad4e975b1c5b493b6e6be605813b44afd0e972cbe4ebc3f7ddc6bdc991fefcea9f7d76beeb85c9457546e5f6c4076765deb2cb261da6736b92111e03788b6371a49f393dcfc8ec82670118bce2a745cd1bb0be5e2d4f1a7efbab17de283b74dc39c4e51912ac0e9cbae964b4b0216de55cbf9938443398269973638276d5b6bad70ee24b37f2fbeda67f1168f45411df72fb294597ad39b39afbc8f09279612199c4c50ed191319f743cba8b4061e1e2e345750c1cab14c62e83a0a7b5bfafe4e8b18e1def34edc719158187d0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"181588fec61ea81a4e9232662c160808e3ed1f3226e237fe8d3f11235397bc6c","proof":"0e138e9258fcfb6a2031c5125741540b115b6be00a7e86b33d1bc8db5575585b8afa383dfd4a57116b171c7572f7cd01a2d37ebd49a5c35ad108cd3a03a1d142b80dc2c9cb11f00d0b01f326513bf3f330f872d3936a802ce0d37765c2131614f2fb428c6ff0f0dc9e156220c6f0aad57681ebf71b77c1ee052823e29b51de79112eba430d8416f89d856fd61fd7bb516821d009f591e2e6a7c03319c0f55b0b3c5f4ffd64a128ec5503fae90ba061f5cdb20b4b9d8f2dede2fe60771824c204ab1484c3977564f814d80070eda9b40f1c3af3f25757b5ec849dc5a8bb7138077c3bac8961f7a838a245abf3e7b205875d0efe0559dbf1b350683f0c4050cd22d6df0fe0bbebb695f80ab71ca171b05ed89618438092fdeb7a5631ad77ee5906f23f9df18d8fb928ed768e7dadd8346eb2583e4f256ba09c4932fc467c35a219b804378df210efa9b81511775cd861a4b1fb014a2a3e39d1982abf5acfa8e861aa5c51f067c245666c1a6e93cf24aaff63aba953145af83049279ca13429f07bc69c5ad573e584f6d7141ed8521bb942ffb2bf0fcb0917e74902e76022e01219babb4a6a6461f340b003c7fc7dd86fedb75dd1ed737334442d997f8c7e22d4675eb205f5deba2ad9240dd1b3146133d15eca3f4631a31d7301800c0e63d46634be08edc66e3b2edffee7f1892431398b15e5650d61f67f201a301fac79b4a235fe9e36a3bd79df718b729e4f4ee48c71b5a354fbbfe584461419b5715479a26d906ac87b0aa4f2a7e16520fbc02ecbe9a9a2db841b50713b8f83f9a4d5ba390cbc923c3eecb0106b0582f0b9bd4671380877fab7073f71956a66db0822c74153601b29d198dad0cb172346b8f763945e6908afbd6be90971776d0cf7a09b0d0e6194b95a40fd44d6a690814cced5c3244186bfff6c9752147a14e6da6781b601"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8cee726ba60cc6043c3e3f081d7d93f2295917b39fabd71c1006add64452b75a","proof":"ae21d7ee19f1d6017922eb430fb28db37d229a3af9df6f75c7fa11b6affabc3a00d8612f82e002a1e2f53b14b68f9b1bb460097b97560e83dbd44cffa26e831182bd3fd1a03f41e87b18a6f71b73675b7975f8276991369b70230e7e926ef06c5a43699254dd757ad661c9a9adf4fdbfca866887671017b1e771bf987af5dd295617434400d94076384087681bf38f8d625a6dfabd2555cc83e866b753a72605ba1472f892dad7f6785e69ec567136b9ef39f171560eb5c077308529a42cbf08cda087ab13fba17dc3724848cc43a15f0d8d8c2175a877ddcdda2dedc2ae820734b70e88fb897e2cdf7bbc989a2ffafe24567fcbc7192326e5c7d9bda310b354b4dc4997c8618ce95be74d31d35f9c631b70eadebabc3377138a8cf304dc2a7d68438a244b1c59b10ba641c79f3345bc0a339b28a0d255857d82d688e99eec7e42384eac10f33337cf6e834b1e73e2a6895349d523e5a6bebc0a1961ddd23345a0e2addd9c4f68d10509c98e880e9ecf42a4a7278cc3bb23d259acfdaf164e285ed463a3209c6b2b126ff625268d6c298885edef8e089100a7192b7cd6bb4e3dbe2b11b289c7a470eabbad6992866cfb5291bdbb7fc6997dc498d2ad6c01e14a862e334e704813ee4869a137d65e5dc97daf0d6016b8a5f8415001c1fafac84d6cfc6dd3797b065f9bf1564d594ae6b235d015ba81f21da5b2f61bdae0cb126684f3cd89998960e16ee777f406e65c9b4d3e39940f57199b7d2ce3e880bdfd0016c994f0fb01a2fbdfea3a735144019039a7a63e708d60183f24f9e2e7edf92114d5169f6f252c0303ccfe421c036301e59e76d145c5c03c9290df744955ad2effb7beb8d76d0a964a44ddc6106cbbb6153393c3aab5e703df01bb64dc5b910a356f94c414dc0f0b2e2e94e3b93121ea674de11dd706149fbc32d2d99c166d02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c2a299cb757ee7ad428552334464a00d3e0eba0249b8a6d8d935f682b7b7bc58","proof":"582bbb8aea121090fbc4b5a0f8af927f27d597fa53d71ec2ebd27076457ff902bc4b74965f2e24cc51e3d251a9b1146f8478720bac967e5c998acbec8a26d55826c9d1264e24d7c14bc5a15003e9851f175e7288acd22cbd90791de89408b64ea4fbe5bf9b7f9721fcb0fb0ccca600fd3d80d81d11e685af8501b192d365d5226a4a097d14b02ce8cc712839920000937b8e4a2d51334c17a9fb5cca0dab3f034e2b58c685c642a744bbef26f79299d54c9558c4b633b6529094dac251e137024b9be0ce58dff0360a1c4a7c61c1f45f80e48ad8d31b6a979f559c92238b390d78ef761e822ad60844e3aa322635abfac4d52e1f0803ae9fe5e72785ef70ca7ef4e0e0d204956648fcd5d6b7c34362412d7d35c5dfb4ac2666d368d985a2942d220d732a592ba7753d1cbf8eb576c57763073d57a3ee0f6691ef9eb0b442e9581211ce6164fae42296acc161cf41c92a3b2316fa5e439e07f1e6545051ed4f4a160b7f23b244dc18efcefb4fe6e0cf1a21cce22a7cdaf4d4625805a6a22bce211aacd75d9d447b6f9f724153a971b3f1ac6419b8878aa8765c6aef1dd8e10c53d65082ff5d23a5e5371fa234c8dc7dbb90dc336d83dd6766af2225a8d202666c9c73d8c2e498b6526302468ce7457591e51bf90313b4d3ca9c636ed06bd96e69eae8ed06cc9558e3c97540a024231a3fabb8e92c2d2bc7b4331143673ad55254725d32c23f8afb4ee93f0a52daa70d7ddf293fea605f7767fab4b228dc90d541d46779afc4310d8f8bdb902d8657799a9e9bd2aa1b479f38bece071549591b7a863fa54b7c9a7581dd7eecbb2b86d96bc5565c54159fd5fbc4e520e70f82b919754c3dedc94c4114b2d473a9d1d0e0982c339faa8ca9ff10f4398f67eb1fec0c6b1f67f55b929637bdfcd26d032b2e614410bf5a7605523ab9a52c8fa5a43904"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"161293f6d1a8b999408cc692ca56813ecc0af176e9dd361c23f16ee63fe2512f","proof":"a06ee7d192214f77fa3f7636cd4fe04dcdbff3272dd52d886f65b09834386f36b0b5091c971c67435bb7d5d87a52e8aeeabb7e05faf019ff8af5a37f073ba9065e6e3e500a71cb22d7be7e2b9eaf0bdd6b7b3a92e3bb54094fbcd1ea5d5bfe7540921549a1bce86f1dd8204886067c9da5b4af1da4b29199674119a47f099018b31808663553945c22b572c27b2ab134db06cec820c37a1b60db54880afde9068dd5e07527987c604be790ec64aabb9cf5c470293b3a515b8a756c8e9f474c0a3b981c8f4dba97a9559fcc8a6b8e6c8a6141429a026ebd67fa60922123ac330fc4429d7291ca5aefc30058d7a6309668df4d9c315cfa8d7725074970c35a9d56968a26a6176e16a3b70b686bd0e06a435335ef9635902bf801ee4708b4a77529c09ddef0964d2db897ab99d14640828d7370bf4f161eafd843efb39e1a518a49a42767b1c0b28bcdbe717136006ed46a2a0f835efc649e6823a4007469762a09c84c48b50b99de471017281c52859a86d3ef5a0bdd9c2be926e4983e80e35c4b482209226a710ab3cb08009abbf72799b6b3ffbc503aa6eadf5cdfef4614a36fca54ab84c9ea9d4ef07d0c8f40e54657c995481949b06af4afae92e3a22eb4026c18253cca388b417b7360b7ed73a90a6ec92507a71a5649c64aa9ad12a95750e4cf369534156ae96a02b3986ace0d4d66c3565da072c10ff2c8ca643b5245433a59f567ca2340f66194924040ec416be5c7a18849edbb424da92dca0eaca63804d67579351375b585816dd89e3a180fe4fb5057d3211fe5de93d2cce0b22975667e746d0729d0b20540b533ee32c69f0b6b8df2707b3b3ea4415679125e342d1c20cb6bfc4fc25bbf2dd72aaaa13aeb9e5a0f0d85ff9b304309b2e189292f0e0d647d97502b3f2caca13f8670de1a19f997afbe7256b6e2403ed0c44b8b480c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e86fb0239333bcfe74a7df4c66c066edd682ca1500cc51c8899935b9f9096741","proof":"c8f9dafcd880d18d3f7d0ed06e5d20d1f01f878155ee4f18aaffe08d145f271820c59f075ce72027010ceceeac977526ef59595a362e1ba13501567cbc54d95e7a510f8ca33fd728659a3f4e0b699ded9ea7cb6d823cc41277ea15947c275e68205a16d863f51b0473c7de60e26a5b920ffbdecb8310b99cf7802f81a19861386ceb30ffd513a44957b1bee5cf08e565552291a62ac2e4450eee7184fedb340d3509acb7d9388a4a869d9d58c4249d30c573b2b14f61a49467b462c2ea07ad03c1bfa449c8caeef8730aa4fb35974ccf9c83b86f455594b11fe6d7db29694e0162110de4a02a3555d67978aa5595ad05dae491c8cb5a8636f0f2ab3375d55e0254e387adb5f4b53a4392ab1b48bf3cbf47cc14b36f05ddc3859dbbb59f5ecc157855b4026772b52bd05cc269d286739513d862ce041a9d1e509358c38d61dc3146770a5dfe6d92f20d648af00ba59fc1f6b3c2a7bc6b374794532e1b5825e866cadb838af7b669faf177816f6ef22b353e563d1c26763ec3b54abec8eb2f984ce2c214e55feabafb3ea1d747efb10667d11ea8e964fa483b21049e7829a90c2776009e116a32238786af22932c9d39a1aeb898c827910af51fda61412f723d77e4bf56a9f03b09248a9a53e81c2bc83c6bc5dbf09bda10b7334ad93b5227f50ecac76db26b7c6ec9734f166a2ad19c8d604005b3cad90a3d50226421bd710057e26a9df77e59d60ffb303e0086eaa5c2a3a3a3c07808ea7af7ecc8ccd0548358a20e8b9ed22ea4b94653641593797acb2799f60449b73a36b45354884457931cd098c0996feb8d1a486008e3ea24bec371f251de1f9c12bf13c35c59bad3cd61f88643f5dc96b5dad217532c9613169f07e01d70374cc533805b0d4f506dd80d99b7d640390865177f67676c067d436ea43982918692ff620909c063b579d106"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ce737738109a4b6dc7ba984e97c5e97aee15ce20fe49d5fe2b4ffe4569815270","proof":"12c4aac9ae53e768d23c60b29a501bd5ee68d10cd0db2f424967386542e2294d22fc8316874ff218be37ce9a3fa9b7bab7d7cf1b13cafbc54cba97bd1ba9265a0cbc33606c4dc56b314b2527ee8dcb5c957714fc9cda67a655802d8a2a12941174d622b45ef816fee59932102de346a2cc1e072fa6a05d03829d52496cdbe017e7499e5de620b55fa0fff0aff2f6cd344868c2d2e82e3542062622c5344fea05306901f7d24ce2a3c44d21094349ad3590b5c704792b4e404e471473dc916302e32e607dcec5729558ea525488442cad5d67f4d2f695808ad898800c35bf88016a7fd51a98286a52839cbc40b7e885e071b6bd41f136c70093672b86befa6b4ad4d26d29a9b002812544eee768981e9086ec64687b772c340b686338c9f01e5e56517b778efbf05136c3066d6f6a12d1911392207a733279537f1becf680192822c94d1803ceb84d994df2b6265c514c6156fe0e7c923e982c453a3ee19f3b582af32d560039546225636994cd2f3957678ad48865cc25cd9c5794077961247364b395aabf8404f7c58857badb83783aee1721070d2e62db3daa469f6c4fca6d16a1a9430da58d163d8dccd6e335863fe9678940c9871b10c0e34bcf6e438a42901b9cf306e164e3c9285264837d21f41ba6bce5456f0a688a0c245862e88a5f5ea50db62c3a60aa2836315da6bbd2a6c79a90abd7b8ecc1a95f1f0ae0259e7338b7007e767ca35e7c46f65b8eda2f4435a7788ce014320402296fb568e6e373528d5e4fd4666491a417e2dcab0c82a8912159b9b5a191f98f3224e5884b584a72de7ad630730e6975f6d14153dad9a1112874b683e20f84688cbeb2852af159073f9227e4dec68359b683857a85d54d708edf0871d782a94a105d71b026240e6b4f0609b040b684b24d969d7ac8b388cfe6bf0a2f63f855224a4cd55761b10a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f46bcb6d87041ec65f526f48efc80c36c5dce5c107a1eb21046c563fef3b7c5b","proof":"1ea98c22937c4367bbca3d5e6c6e7ffe9f1db8406bd586301f3eaef52b5b4b62e860dfb48b026f220e05b8070a7085f0500c615f29d470fa798a27c35a20405950658bc38f34365d395e79e435e9125e61b253b1223c0c05da19e0d7a4c4376fc02e636536acbfc64e9a318f78802ceae3e4ba76858cb9c486d2f91e74b3de1050ad5bd294e642e35bdfbf5a443f1a97fa1a81d1cd751a5ba53ffba450c3ad04cf9f62b512d169699f0feee22aca8585a08e1dc612d3348def4d585040f03b08a1f59c0a0b1f66a377bbec8a8b1ea84aee0888ca9e9d4b5087a751bc51c1410c6686659d281f50f22a1ec6cbc61430208315cb8a4fd6082d1d5cc80cd962024310e013ebde3d0f3f7ca056f1f5eca2e93e336b423596a93432c5e50fe47d2669fec2bafb56b3d62dbd2680c8234b5ae21d844cf4b46a9c3d3ba8aab93d7d7025fe1b407bd52631cc3bbf8edfb03f3f70a9a78bd45a26559b164ed5d81d1f633cf8b1af31ac6114ab853ffd86e4f92a5993c6f2dcab00a9c38c7e9f15976fd8792e8264efb5993db69ae137dc68d1d52b010066e90f9bb260f9db1e59e666fb30d2f68de5d616eb407cfc45e4582018700d48dc477e8098830da875e933e52a5b5a0064dbc24e7e9e791a2a2b03d48d3f30a222218643c0a3015a3c123ad69914a0334ad259d95aa6b72477e2fbd4a2b4c1dacbaaf6aac4b7c6f9580e8d08ad088a1818a4e5cd4fb7df08736113ba50ffa155b19b201ff50b2c8d84775288c12a3eb20050fb5c5ff9072abb78b4d50828498b51913313976ceb8167b195a7485adeebc3e205b3f5f7bcdee143c032f874008ef20513177cc141f4dc4c09165b70e288fad9b8fbe1389208b22f38e44beb172639f591d24b789e29b4800440bb0c355583a81e0aa670e73970424182eeadef46fcb1f2941bbf61afc60d576cb203"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5e6878667a5e18771fb5b1b3c42d059523b97b55ad7301bc0232ef48c2641926","proof":"6c31724522cfc769f37c8711dce8f3ff7e8e8dccb5885c2aa863a69999eef967401a68c85dadaa4d2cd6f6fe47e86e9dd1419a0122d9f1aecf73b42fd126f02958694c0e24d2708466f8ab98b65edcdec4477be176190a2ca20ec3ded696fc3d8e6206d4ed1f70b0a4a48c92785c359a30d1f3392c0e110350e60024b36382178ec557cf1946e171b2b7a351a7004deaf0caf868dffce81e9c66eeb3cdcf0e05d87bc34948c6abfc9e2aca78ba30526e10f6fbd96c0e2a1063f109247f9ad3009e98b73d52d92b3453b1d0951a2523b9d41c651817414194523523b616782a01448b8c6f41c2ef4c1ca320bc0cdd3b95fdfe99d4eb111096973933ec54f31f27062417a6edc6e5dcbc0a4b1f6cd7c14e39f76f5ad330b149ef68462dd547d37fc878009bbcff25210ef5510f8067cc7b4b233a2a9d1db3ea294104c51d019d67e2677e0e15cd49caa41c7aca0cfa1738fba0c389dcb53085880996b72510c217bea7cb231d7132f6cf078fc40d11d9dad666b146c6cbcd020226dc28088e9e4750a44aac5e8e814596d77ed964adca6aa7cecbe84fbc30107bf29a88a2a5d74b6c634118e722eb4f9cf510767707668a6e8d51adfa393ff11dcd27eba545e97eca87f7cd25aca6fcffa276ba6c95298530bfd120f9bbebcdad4bf978cee11343be6971473852a8e8f1094688498a9e177c8722e5dc2c644885a94e22a917e3517cbd48fb6adfeb48b24c671ecb84f47564d62e57d0f2ee87a7189c1604a7c53e221233f3653db39a57ff0db850a2b3f1d8119e3e0a44d6a31fed4598c9118515b612e3089fd2023d7f07a212b0487574f7e476779695717fbd75688b5703300c733da90be5c928609cb6760e41f47b82b4289defbb3bec67e4b043c3da137b0f3728ae1b38c87d40802a0945bc13b9d653edf4b0ed8374bec5f65624e9c5b10d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"404797c559093ea96604adc18c7063cfa7e76d2903ba2a13f180e856b092f121","proof":"9c89ef4ae6ac77eccdd18aeb2dad73df0ff1a159bc050cdaa3d17c653457917b28dbf76fa13d7526bbb660fd23255b06393b8ce3e6def4455cbbc10d732f2612d09edcbc53becbc395744b7cb56186daae97226ce9538f56b192446e0379206166356be8bbe1ed5532a8a469073485d385076f42706b11c58f76614c911e2e681b7da87b004ea9a0bee61de17b8ef9b1d8186f078c43ab99ea9766e9b544f705c75abb0f64c1b11fb02fff0262f21647adc11bccc5377eec637ad9152b2693093700c66bf83ca885f57c0b0e853225205c8af8e0efdce7d87a3d71e957d4bb074ef9567a0cc248536f7853e090b3f8e16aac4827a95fb6bea84020ab8f608614f20b2c04db7db6ee27c3a25d398585135b1802f52939a4546b62452611ff6619a02dd62c6b3d8b03725d481db4ae7a30a15ebe73622f522035dbee0acd2b4f5e3679fd516ca4d0cb28c79ca19434ddad8b4f9f2706f255c0b25ccfba83ca252ce81708c2a6a7f6899b415efa1be33842340cb29bc9a6879311e4415c54f45e7f828a776670da80b2fd726386ceb1b099c3cff1d059884ffc3b9db54fb6356875d874fb0e97a9d5e74167446c6dae841852fce353ce8b3e0aee58515686479b37fcb8dee16fc82b0ee7f38714ad4ba21588f946f9ddd6cb124089b7d088c4926f285cec049ae16827aaebc0a0525b56bcf719e22a6783fbee2944bd08e357fd79f07a8d1e655030eeb961a7f54b3b697a7f64d2d95552063604c71b0a46916e4d901109eea83b4bf4042398d43f86996047e102feab22480e96aec959c0c0cd0e32eca7105b24500076b81ec4f98daa0d21b9fa5a42aa468f20381890cb1185272791e8f68f2e38ee455763b5a5ec1922ad4d3332b5525e7295b2d57549c95108611825399c01de60ba009e536ea6ded738dad631dcd247d4dd04f8fe394bf300"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a8499d0d4808789354faa66bf0beecc2eba2c2225aaa866cf55a500adbb3187e","proof":"b8df3ad8168296a81826a78d34266865ee85fe6e0f57ff6f0f14b97034b7de5e20f6d3f0d368c7f54b0963fdfcf87532443d71a8846cd9e02c679f5d242fb20d048d75dcbc37f984e39d65d38ef55452e76f3dad2dfe31ac2325db3c95cc7a67fe9e72e25efb7435144a8b51514c9e9e1f5f72dd7e2849498f007b54b0b5774178ddcc889b75bec08baa0b73a92cb13ec3cc5042394ba51ee6cd51f64071c00dc2325fd43c9ba32596a61065123e3b403f62ed08860ef1e7a27e6b8f2340fc05d9d406e0512aa5780e402c1610e185b2c634ce587c51bbba7c0cdcbf3451a20a72199ce0db762d95b4fa96e74d78a8ebcd67c191597687ed7845209059fe3e51e68eacdf23d595b1dc5b41033e8eefbe5cadb52814c42eeed67d6e61ba7af119649756254bd80e24bb698a89e00c2c240b0c1b7e9f58fb4b2d7e47753193ba3718b50935809d5a8fef95371bc3f17c5180e9c73a56e5e693de8ce764707171777a5ba4bec6fa884a38f59d56ee99d86561f9cd77fbf079ec430108e2332f046a121b2c378712b62ee386a10246e0b834758d7c7b263ff3daa19a27f90c5965504249b661dd0563ec99a766145d4d017f1d53ffc1cf3ab0fbf47ea4b6c79e4b37bc145d9fda4b9bfb2dc79394caf15776b9837ac06c49061d7da70db4ed0b900036f7aaa4804583cd29b7d921c5983a56271813d05f9c1f76e6f74cbdbca2f650047ac4c8217548e93df59ef086a05d1d6335e31d7fd81cb18b914803fbf2e8265470de428c6c646393954fccdeb09e66d6421a1c4f7338e593c647fed0931433e268fdc7b816a4cdcddcbe1226980af04745e4b000f7ad2a51f6f315d5015f3b256b6babe8e5af642d941cebc9c7c8cc1cb9eab33ee0d91db99eabbd449f8a0483de84289fb6233058fe3a2a39c3b558ea394f02dfd9f82b442ec35d96478700"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0ebdce71c8264a490d74d34620fbc9ad7e39273e81ba021ceee015e19863355c","proof":"2c4433adc6ebc5e2a85388586b30a163763ceb14d36a82b5c79bd2de3e460b1570063d1b04416e5eb500d596944871ff04ddfb3c9d8ad11e7c677bf8b0b3a7389acb2b181b8e1cfc56eff5f7993f1a71b7b155273ff51d8d70505dd1d5624d159e6a63c0f7fdc430d9bc678d9389aad6f26e7783c9338b996abf5ca76d6b531dc33381bf590c7371edaf731d8eeaf575638c263c1f9f360834918fecfe0f5f0f9449265951e7d67df73fbe6e9eacb086feb11c87a1ff86df7deae353d720310b416c516fe4a1f3d0a2e8cddf8bdfde778b2cf9bc5575b2b3e71bb8cf86c286040443160d8451946ebac12132a90abb68b80805df0ee9cdef789560f68874b56a56b69f0a68363bda7305db100a3b03b1c24a7b71dbee61368c90ce7d31d37e59b85676022c1a4aaf244e3a612edc86aa9ec5d2a7138d763213cb3e9b08ea4b3c8c9322cfcb71f7d9a2757f70aa174075c83752a6273a6316fa5d032ba48543044e2dd51ef6d7c10a59754cec7a09dd2f91d5420c76c5b6d6973cd3ed6183a83d362e48137460b768cd9c3373eef1e90c2a30ec93ed5e559ff15a0f8532e4c0520c11db18ebcde1fbb1dfba54320c015a83963611e97b3f7b9f42619ffad70117f4a743f66caddf0678693396be7d9c5559624e41f7b392c229b18bf5de4d441adcdf545dffa7cdcff2c5e18f340bf06479ee4eb1aefe714a30a743ba4a1e2723b6616e7f13f39b761c2bc59c2acb4e00d84c3a57f0101609d1807660390c732e90ed8d422dc929b76b3695990d8beacb21766d68e57d99be5831260a5a6137279cfe58b2fee34899d3b0a0f4d87129b06bdf9b188272580a04ba88d52e025916be826ea1deee5941be371dfd544a44fcca03628771db12cb389a794eb32ffd0913fd840d74d35d72e09b227f67edccfb78daae5f0b712bef9c581b4185e04403"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7e06ebe979aac199e4ff32fffbef90304ac408d7b978237704b185486920607f","proof":"24d714db0e2f48d2a89f9a94db83e35c680f1d0ca23b5f7a9547dba4bf49d443a431804813b53eb11cb905b3716ca5ef83fc6a1232cb5f5c573b56e549e4d55faa48b3f0cb7d5280e2eb49035c7289bc281d0bc03b34dde67d27bdd9fb4c9a1d680b41632d4e644e81cfbecb4f09cf258b9bbb10bc1d97bc45fd4cb2e944f346ba0c0d914c3265880d47c6f2587c097adbb83a137ea803505683915e3481bc0f02e100bd57f08bf59680c74d421b2f4357534085228d542452f7679e30082b0f99d359a0d5df657f878c7a7cf342f6d18fdc9772aa8eabc6719ce3490378960ac212cddc47ef6c1a3969ec4c8f50237729dcd648ca8c63b417548d3d416a0e51de88a29509c90662fa725c18f0b130f244b71da2586265cbc76fe027ac7ef939109b43e056c22b3dc008040f6ede085bc66976bdc6680bee26f466bf62d9180a2a6fb4e1a8e4bcfd627e2eca11b22b869423f800f46440d6deed8938eb2b9218a61b0042be9940d0a8f03b37f8bb02fb53ab1b7368fdf7d8a7dd8c6914949324d80da8103054384d156b54c550ab3453939467f1d32e2d5175735bae0d52ae46d40c9cd69007069e1dbb5999dc9c4fbb9ed7b94994bfa76e34a0addd12ed794d4a31a7148a5ddbd33c5d0aa943b1cc99541f16ab7e37b05511dfd193b2674066bef73c0c5a19fd74f9323a58abf3bbddc15fca6549f0a09a35e073fb9c79fc2f4e9a7786c35733b4ef8b24e9a20c66404604046adc6568d799357231b0afcb2deea011c501347e0bc25582b8f6cae9cc77502907ab3191653a6bdad09e12a52e8c6029b4eea2a2fc0ade7e3c5db3fd183b920147f1b1469ec13e73387bbefb277753a8ef67e54e2c2035611cb3f0bad23aa23036cdeec434cf0f58b8885c920bb8870ec2231ea838c78985348283e2b92d01ce0ddeb625ffe1716f061b795c03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"882f1ea08aefadf0e7fe501362786509dbbc7a54bc4fe35ad70ccb8de5ee9152","proof":"9ed12204669306473b574775def733b4c8310a765f8601811a227d42c8ab6043fe9087439ba364d2f4c0b80dd4b7bc63c8d3d6a51608aa92cede570249591863e425d0d804b95dad5f14a706ce2704eb0e5dfb00a84d1e25c45847092a83e71cac8d0360df2b6bb7aeb33695b74106df6d2609bd0df1ecdc68e5c35d867f7700ff0617c35641b305713f28cd7fd0c2fc31e344a42a52157d57f1e5fd0888f30923926b887e22646bb5f87e7b095794bc12d3c4e04779e6e31628cac0664cf10af38850dca6cdb06f7d36511fc759a662f4a49c6fd75c16fd5d268eefa3bc2e047ee0709ab63c66294cf8078481fb7dc6ee34c475a3d9aed34aae859e70550d3dce529929e572eb0a8f2ba390c6e110e8a2c5764f8f7309613fc5e5326eec6e71922bb79864568eab593db9b3eab21520b3cc1c1dd14df740643b4ecd34d42367449b087740cf460b8aa2760a690c767924e17a1c8d8decb254c2a503daed795c1e8302d4785f56896ede299a110b9170125f45a2cfdaa9add901305137511c0d5a513d49a6569009968c0c61f1684ff6f3e866b9610b55f8ff9c7b3400a35477b8dd9843f579a315d4c46469593684ed1a42e15ff3710f2a490395d3802919744ea5cc8b5d67d37f027f55bf588e0e36d6e857efe15b1791f7d070c690d40a75229ff103834e11b1cfab59975b27e21f535837ad1c55186be410b10ab7dfc5069cc98b140d9e839be12cbd93cc47f69bc183169736c3714c66483ae102f14679744ed9aa0a2ed861f05c09976c4e73efc249f781d786383c49102e519498953e508b8998ff89b78498e2fd833bd3ad968e1519780c4c51944447c1e6c1f5b7534955bcf92582d02fa1725bc0ae37a52f4b92a250721624512bbda4a0b6704204dd82d3c72adf2df5cd0d226f19f6ed93d6deaf011a6dd1d29577d8c8d27bd500"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"121e215be53330e7900071705a27946c418dbd2a1c8cda59c1bdaabce53c382c","proof":"023db3b1d59b3e5634e7829da5d0e479900ff40c72e06743bb87211924e9e13dba414ea7303571d213eaf805e213086110c3f912e0c7cf335c67841bd32f1154feb4684e652af45bdb6fa218bf3855f54928d9bb080845e151b8d4fb63c080737e46c94a5836f159128dac4aeff5b02b5723deb1b14061b7135bf409240cd50fde24f6b83ce88d085aa0ad2bbb420467b58898b75f255eeb89d57ec5d800a5045a0e8b8d087fdd3fcf46f5220fdfb6dbab273fcfeaa5ed44e26e715cbe77080ba604d025ec2054bd1b2df96d72b96ae40f01cd482853345a29d8d31e99f6c5063a6ec11b1c2c048b14b377e820fd38e1594f800b5acd2a159eceecff18d1075b8c6bc48003d81f1a14667dc16b6318bfdfe117d46dbec76f92d8e7a64c6ae314327ae2b42f36ff8d1aee08cb692dcfba5355a107aa2f42916239fcd7ba524a23f09e5c7b9a98ab9464a20dcf05c3b23416d824ee1206ae2483f16d98f81d007140ef755fccaee18b4404e159c25cd31d11d3cd83eb5b4a64b1c85f62303d0b53043f3077fcff5520d14342caa01b9131f608da073450029ebdc76efc29bc892cac1530303312c9591c92960b747aa1289a1ee5bb3ea2bf2412532e151c67c6351687c52a8cd3ac3ec4973b3bbd50f21ab95df64d7356061639886b48d020d86d128e704ab0dd4b522f7d4f0b05775c0be017dc0a5ad5647cd41652ac25ee7a35b2086553863589b93b2b0e802cd862c412670b1e423f123f7da4d76f7a01915d0c05cd7f2b91a4db4dd84021e02546125104aa357da53d00d760fb51304a8118fa2be63ceb364a567b98715857acfdcdd8c5f97519e4a0c8ff5343cf1df87d5f70fa463e822a70eb54902b72caa2b062d15b7a8838dedcc65ac2313a5be6ba0114da3cdc2f30c2204f5067f0f6c7f1db985b20c9d783faadf9fe895b43f71303"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8e32d5c3a0552e529fc68ad4ddc3ffc3784ce07cd7cd6675d6d4257f8acb7f35","proof":"8eacde6d250b1c929336566ff544c3cb553daf754c0135c6f377f47f5c738b2258a1ab61122252cd3351093c12e837552cc12520f134b9697833e216acc4c60064b10ca9e12d19a9bca5a3d72e718d682ffa376712090a1c4ba6591ce7ebc02e68521c233c2f139619bcc7df8dc866839f60356f7fdb87fb3bbb50ee2a67162451df96d97afc200be8217a022b58cd52fdf3ad3f44f54b2b5d3b32e7e420fe071583c15641cb3031d03923e768543cde3d944e98a8fa1657eb9bfa793e5dd508aebb859e4d1f5825b681c748ea860730b80f939778578a284177b8c4dd8bc00ae42a7989f93e919a916a5db7cd6c30f73bd68ed46592be5ff36cdde8e24eef153e4a958265114598dbadf75c058683710ba3a693cd896746605066875de61354c8e1d42c1b561e97c8cb8445026bf9cc6f22b3c4adea5ce351be56b1bfdd0d496e17785529eaa3f6fe2adbfe92084a00c951ba8f78da1187cdf381b32a50a025ea2361b816dc427987ee349a20f19fa22c06a767723ab76ecc78d4f9aac981129486a640a0bafa0027f69d9f5411b766e47012b01b963c53a45f5671ded5295ed234cef01278496816df7982acee3a27236c0418ea3b7cc9261e578a393d803d38349dab0e1e82900d00d60f3d4219c6a69fefbf0304f9c6d2d2d8f5004b526044b72836ba1bc44f56449f841dcd48265762f080c82eaeed353672de2a5d7d0a9e4c3e42ed9ba383cdd26baf99502e4d28bddb1999b5f9567cbce92de4872b34a48db5236a61e76ea92854072452dbb1e43c6c307aec86fcc8b7f07e1642a47146fcd3e60367120a9e82431ea1ad13008179f9740270e1be7c08cb82af75ee517e7d7987bc0f80c80e4c29a68b1b9d67827d837618fc31d908ee24906468ad0c086e3d644d3a89c7d619af72cf86d416fb09b969d40e0bd05375422be7077a07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b08e80ff077c7368fbb7529c155c9d10417c2cef7eff664e8cea25328e9bb760","proof":"2003b93cdac2768a2538a13ce8fcffeb4c826f509767b5af70ea0aa278bec230aad1e1456c8378e5be6d7d860760acdc77f63a66e9af744681b68fceabc5a6779c343decbff3c63ac3e8bede0c3467eee13888504295de040a009891183f80465cb6c0932c9a21c5af2738afbedba14215efc9267e095174c831a443b4b68221330aa469472a46d3c1134f677dddeb70399b86ec91255b5332b959add822a9067e100a5bde59586928ad2975e5dc3488f2ec7fe1c7191a64a35aadd735302500ea0614eba4b7d26334cd5fbc6c42571e1ee232e1d27a5e9a95c8a25c021fce01863ca92183f0262301c5e42c700ac5892f86e107bebead22ff890cbcfe332747e26ccd1f7ff1b05d5a605e5565db47d66a281b63c80b5ac620bc88fd1b4b2c3738100ba1a5bf83243a0a73a27034813043b051799ce1556eb6054ffc3cc7965deccfd7ed53e9297c8f6bd9a8cb56acf561b4d938432a4e8b5737c303416611437aaeaad4c8ed1667efbe011b8e9f1e864e292c751b289ec45a359f4764c564497c9d45bec80c0ffadfd55126a043ba012d265bce9d9dce752307c828419f9c6d0c2512e8d3f59033fe4cad2c94c74884f0117e0127bc2adedc1651391f7157381082fc8a8ecc8399ffddc7fa7a3d9b533b0038c42c5e8861295c9238311c947ba8bd8d17cc1a4eb2a1d3a6fe4a052cdf19ac7261dff27a34fba9db8b69036359eae0ac1b60f60882d9d8d004a28a67e73da5601d01c657e2a2bbbe8218c5c44ace1904112bb5afd128096195248ce0ce38885e69631e80b2c04084e6bc3f2d6666681ae8f4b01b7d646178c2b2254b537533c80b41eea74fb6fcab72c8c1c202441dd1f43f54c4ad4b05cb2f70075fb1ad4be1f9d03168810cab657d5b5c4e016343398a88e6eccc2a71c6d8a0652af26f5709117c83653725a554ff9861e60c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"605e0c2109c3c5ec93a8f9c46fa497ec87423e17888ed5c17f9b724e3754ee75","proof":"52cb29232dbb38e591c40ca9e69271314feecfb7ce75c67e0e4a5df501abe97948afde3c0b461f1b7222f7fdf9c58a07a36c4f8a5ca2ab0994a399d4cfc3471c9a97ea2684037d26412b4df760e4d401938272fe58afda2cffd8f876b6c24453a0907918f1aae87442ae5d448ceebba19724ee213029beed85ed148eca4dfa7ddf7d71fa5ed6f576c07e49be9023106aea052f6062dad2f70d47ba6d4577a20a2aae5f1b1fb0ef8823e16b4d3305baebd6323fcb2748162fa5946bf85ca7640f7f28c8a37d1679e1981d20d98f5ae5788119f048ef683a6829af3977531dd10b2ea3bc840f93bc475addb2cd63902a15f362cf6da6a8a334bb1cb53d5af16b4070e9f558a41ebd597dc4b2b2bf75a2cf6c991e332dced40b3ec2ef1e644e2b2b122a8442a5ad160249d7829abfe6f68ec1a481e42197dfd7d46877b59779cb6430ef822e28fd146f2cebfc42d1c411a97fe71e2d57ee0761d6c2d8b99c8c8d7cc04376bae638e30c29ba16f7688dd24630c807e957e5b10f5b872705f6df5658ae12cf0471716b6d257404d5808ea76b672fb435fa145e23d0a9139fb21e6a76bc948831bd9fd8598928d289fc746742eabddc51b2497b5f45e49e19271588656a66ef3d227109310aac19b66176eb06cfb982f6005c55f3625c83a9ffd6a537b027f397b1a45eac29cf14e97911268eabd3c24204fdd485fb81cf955d1bc5177aced15cf12811a463c7d13e16948f87a8076f9e7127ec74f5ecfbd82f30c97c2e7d4cdc77f6956f4f1ce6381fdf2a6e8059beab52990f2cb2bda36b4eb7a402d2385b6aa1139ca891f9cb28964b1e199637f36c4e12dee955af4298b8ffc31f5f987f67266025290e05b17d59173c52c92cbe758a28d84209a76d0074a17e0b79b7948456927d5455d81fc33928cf5d363d84732b74ade189972b7f55d20c06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"946feb1ed3f804e7d815af39391097020c44d8b1d41813396a78af6bff46ea27","proof":"3eb7ce9cc1edbdc066b1a2cfdb7e8d20eba1457ceddd67de12c6db9287d5d40efe28d7d642fa9615d7a1890184998d0fca59fbabf014f70f03314d7568555c5694e3643dc42ac89004f6b6b43895bc77b3d4b4405a3b9d7ad14cec1ebfe582626efe3888430fa7376bef0804e8d731f6131aaa3ca47bcad6ab8137696e34b2525be3aa4bad3f390d4b0ce110f740233581114f454db9b1b54cd8e77134dd4c0980ab403fbb24494ce643ef4ad284710db211d45858c115f2ea4692b9c080e10103151cdbd7a226bcdb6da9dbcb21a418f9358a05f0755c54261cc6ab52b4b906184fc865a8b6b4854341771e4e433cf7a9728172c08e2354b8870f5a8b1d80771879045543975fb6aa41df0fd2271154d149bea45324d3e949a65edecea10b545287ba69081132ccb3ef44fb903812d4b86cd5888c302aae454068402fac4b3e52cc474e995ea825055b775da1090370278f1ce8d03e6fc68876a3c0714de47ab82a1392ac8ee23548482c200d7aba185164717859e83d4ea03a5ef965e65a74aa2d92445ee22a80029c3ac129bbeb8e5f2d43e837a8e2ff5bba4f8b0993f81cb6529b6b7f8ebdcd9b6073db4882432b8e2870287e2a93551287881d42edc326d80752ccc7b3c26fa9e101de710a3c2ffabae33c1d79e3b8c33fbbfdb22b2e086cfa28b1e928726ea1978366a020f20836c1d7f262af4f2c1f85d8d553f4d10b00712d520242a4cbe5dd7f56d836fa535bcf528b81c1b010686d7dd9b300607a7c9d70a69c5a78dc68c7af1f70bdb499a78b661bc9aa5fc92da56cd3d984f2092a16dc358338d57a553bef52bbfc848c3474e24856ee4be088e36334d1964403a21dbbbd2cbe4411d55ba1f27e72494e90315b33520b988aec035f13da340f0253d706929a63a0fc249e17f2bde49490c6bcf4ecd3d1d6a46c38c5a5bd9dbc00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"acad035b05aed4f4110d05ae61170489ee2db09d47f8aaf0d7130d0bcc9b850d","proof":"2a842737dd514fe5f71d406c26d31445de9a000d3a30aa140096cf0e6a99bc737af45145eb49ba37dcc2680499ca567de9a671755d77b74a4ee18b4ee0765b537024ab14ad85db57e397b32240defcfac1eec3460c7afb6b558ef61ac8602023288b641cf5707a6b7dca99a21cef60e4ea91df75d6d878acf8e48608f95b9c242a751597c770f96b6d635b09f3a606f2d9115c954ed9a91be9115b12789fc807baf403e60b86ed5b15ce8e99c454e9def62d1233f53b77816c6cc36055a0e9067738b0c86c373afe3c1460ea47b0dad53e5da333522686e1dca43a4e4c7a6709e610a568d568870a451f86692e21f97def6ea21a01371d9b8cfc1b8901419772b66a4984e161cfaa6516c751227e860be0bcae32a77938bc3f89542b28ae8336e61ca4c370f34314bd6d5fe8c474bfb6edfcb738d21bbd4bc51cb3db3ad2357f46d65c6bd0d734ae330f07a0fde07e06ba9717d6cb4a0d15674d49bfefee4554769dacc6ba81294341356e56190813ee97b4062f5b5b55c331c6409db385f762ecf372a16769556ca2f1236f8cae29e3768d06e6efa742c8fe2b64ffbfb7d84e0e1612ffdf452a27742ecd21e322bc6bdb67f15311265126600af7dcb580e35fa82eb4ebefe2d9ea1528d1c71c08acca788b27b47f8fe42bc03487aede62433862ab386feebd65c6a21ab08956f5bd190e8026ec922c246b6422c4b120f8473ec2d8c91e546e30157da336efe4222ad81583a4e212e4d75fde687cb1c2da5a2f3ae832eb15e23dc87a9b24713f47b64acd8813ae13c78b7e1596f8f03d2baa1f2e1df0460cac3ba8ce3dca1ec136659dafe426e5b37c46899c1d2690c268b6582d09b5f57585a0827266ab43f860059582e7e9a06ebe9ce15da74ea5d998b5051d44ca335e25aa998131aa9833b6d24238583c8e4c1c7ae38c18c363c0ae600d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a07bd73553eec4bc2819323f5863ccef408f9344e65176972e95ec5466939249","proof":"f21752df0e74b8a9586f6b1160550582f666470a7b0627ac8a1e8b50e1999f6bb8b5f6c90b4ac20c89f7bc69a1489a2c48acd23cdad68a32f98dbfbe3da9bd05d4784ae1bdfd0c7b6a051a97a1f5e29627adaf8e638b8e97c1e50473c2a03f52426d7023bee6f5d2f38cbd212da03d3114cb3f52e01f9d35c27ad0339d73df7520f9974dc4e1e55a275545dbbfbf89dd48a557aa26e9fbcc68586c4869b57d0b23160d6ae410f426c002d031ebaca81df6fdfa62dc6b3f665677942ad72f390b0676d2dc3fc5bb976aa6d5aeb44f34205a09d97c72c308beed551f7c5f023b02ec889d0323c0ce5ff67cc281717476d0c1149aad3ea61572a182e86b615189722e76438114fb58b969151dfa181660d9eb79f973a49cbb9c6872f0196f184749845fac074012b7bb4b4ac341fbe441323ee7b9707345f9c67eb5681d0a59f65ddca298ef93eec5a263d15abc54e01637546426bf5cf9c910681e461b3be32717e2c48ab43b760487c8c9bbbaa553973fe60027a0aebd256b1589d270dcb7ad1fa84a23e0cd7131f475b488a1ab0344823919f287ea5146c58bf2bf58e867a563381b448d3f2c5fb97b153a38079ff6db6b7a2f0d9cbc8af932b2d38161a77a367a576aee9f4f4bf1c30924d8505ad60d4f4de22b292b1cfe9af967a53444d94876db10f7241b66606fed5d1bb63bd99dbf5f8a1367e9920bb78ba47cd97fe605787152cd069af298a647196366da443e06ddb2a39dffd8854fc6b4ed5ab91e31e0dd2baaa18e80e1e193b9ae9e4098745676aeeaea1d427f9a0163f248653657d44f259fc513a20ba2c02ee3eb6603b5b1a20789cc34164d874ae7fa09699d26ce4c5b8149594b9e272ed83f2292f4d065e0f55f34226426f0fe6edcb5a18a0714823adb8902b7e9cf331754fd92c78043b02d5738bc1e3800cb3cf3b185eb0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b4dcf466f503f22cc77c3c80f103d819b41ec117569c40267efcccb3ac3aa454","proof":"4891d3a3e6c1dc152c2a5823e66028fe88937bcf9e7fe2f391ab6f7e9ece3e694a0829a0bbc4743c8638dd4657347627669ac45ed52b40e4d8d53ca935a64120c6843e9ff0b553e71d251626371454ee74d53c989d3c18767b8ade4ab4866826667da013264a8e60b848cabc89249795d80a555c1aac4c3bd11eaf755638fc2c29ca8d0374e37d565d0fe557afc129732512784f10a5ad53333b55ad227dab0adc0be7c41f3c0b5c9765d564084255bef69645b4ca8c4a8420cb1816dba76304a6abf6b6af3a58ff1514fe5524a651152443094f1e708e1640c69d0893f7aa08dc96ec81f53e6ec1a27a8389138f754eeaa6d7d10bae473799fc0fc0d861e71ea696631c83874a6a092a329cd664936c3584dd4c09464867f9d180b6d481f7675050c541eb73cec5b163f97259a040fe704e82057571eb0f1efb869d1ce3aa7492837e440ce081e102989f337958d7f4261e0166bd59a7b0d800b43c9a406755521c20c94adb79977026582d3cf41200eea8205e1de90afb0b6f3c29addf4b772ac3cfcf11190c1c895095466cbb5a87bce4150a471c5d2a8bee423194117711de514a2e1a5a75adf09f6aec86b68668ff5c99219b15b0fa29e8155462ad01537210a0497058ce7b8adfe633b9b01729af0d009bb9c9a9b7be0aaedebe492a7f28e5fc947e3fdadb92add72fd4def3cd7b290089bc2487e2de483e467ef25118807f9ddc5f6bbce83918690f88a18e3b439c013ba9a5d81e61a580b45771b950c4a146fbfc7d25833316eccb33c188d1dc9d2e6b46e6c38e0cd9f627a9b2f63e4e581ccba23058ea02df2960b940d243cee4b9eb2e97071164655cbb87c6991d477be3cb11442a9f5077305305af2af06c328f873d3122637e9cc52833dfa7010d2c9797be280dc560748b57121e9f890a5a3476568732adde59adc003182303"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f6e9b6574c91336a7459c07cbd2d1ce423556465900557c47167385f8f529462","proof":"0c1a1fce6355f37a9927ffe1f62d57226bffe8db80012904436d3d11b25a5a1becbbfff0d99631302059f43f5487237ecac43289733a0a678c559bb995a9727dd89227cc57ab8f667dabee188dd4540e263968ca47ec2e255d3ce635e716de006a69d250a33196fae551d353b37a50db44343a97611865130f16a2cb169d8e1416fb788f1116a9f0a5449b46ddd97581b9e4750b82d8a746131c187384840903981a6b5f2a28337278433898c3e1e5b4b363391432541f8f36e07134a118fd076fb2d60ae63ff2e06d46ee744fbb574c2df846f4f6444ac5f59f8a776a83f80784cdaf057c762ae3016de6c88b8827c8bee0883f35175e340fd6e75b9767e8219ca01415b2863f1e45ee5bec1c0dd7782feb5d374ef256825e42567c2d01de6fd01119c069ab9026898249d25ae8cb87885bf274b078c5de0cfaa9a3c78cb83bce28014a53db75873462474957564a461763b3d7b239566b6798a24564b81432e278c863908992881fcbe660050c9479c82d34afdb106843267e72230929256980b76ae136ada2d75114ed3262571038fbe19b7e658c508299f96a8cc32ba325eec98cea2ec0efe7bfd85727ee64c5edb7c601b79c10d47f8c7d28f4d96c27416617f358c15be9cae1fb0e924b49499f27d7309cf69c101b1ca2857074ab1453cc80e2252e1739f25cbeb79685aacdea3b8e44254797824bd093c7cb841b2e3196fa4bfcc1aabd63e1b7596cf3e089d0a3c51ae70f213ecdaea71484db116943a62005ab19f97a458230a41825add3e61c31f13f352881d6fb9666369e32ad0b70dc62123ede93324479473957b96419920c4a4fc12d18ee2e9381efd818c247e6e5f00126d170dca429c468e6cd3648e2259c9a63663e0165ae460be50fe10eb9165705c7c51bafc0ed4e69febcf271548b81a7e55f4e088aeb96c78c19b500"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d250e239f2ec3e8ca5886f8b70911bc8f5b500b949eab71b6b225e53bef3a408","proof":"d8157f76c657de44c06e4e23adfec880674d47e7de2375b717d583d72f4e31755c182b6a93ca6ea9286d65079522b6f632914aa49faa50247945b7c7eb073377d618aae9dba9d2eb6130896f87a900caeab7c87fb5e733a0ef8e3024fb0f3832225220cd463b7815c5b4d049fed5629de0cc3589008bcaf3eed38d08d14bf806f76424b514dbab169335973b2752cb7312f59aed21b2ec4f761e3936657e8e0ccd572a8cd89b3bb3eb8fc5d242187787ddf59fb06b2da89a070cb619dc53190c1636af69c0dd7b9bb480b581412dc5887f5ee286ae41aa8d404007e7d06f7e03ac96f00ecf74e077507505047694b9a987470370ae7785316d532f6d81dc5c2416c7e745d964848c65ea19811f79bbea1a5fae7d3d566f788a73553eb6d44340c434dd35673b2c01671d98e19787b2e71f28a38e91f3dfe1de5bcb9cbfa5006d9c0e517d74bfaec91a6a351f59f0c249dcddccca0d3cce433d8322add531144c2008953b819b6f4d56b25121aaa9adea48e45b2db05401ae575bba68a5793e57006b86da1a53b1de37e6847dc6fb000f66a9fefc6b4434c75725a36137951c6852440eaa1717c19b19ce96ed7aee1a87e38dcd06d6659fe80c40acccc35f982994bad7e44ca0e1738fa7ac43ae6d7763721dca83ca61f0915d81e451a515845a4ce9ec0f1411369be848fdbd19bfe36fb56191be11c2c22b16e7c1e6437ddd5330642aefd5ea46aea0d7e8352aea40e56dfc3ae9b5366789edbf8f15ed9a5b292aef49519823df091132910227033c931ed2e9c430e5e5d7e9ad21ad0d75b01b6ce75910ca6cecbc682b02c154f60a66599802ec1e37c0c61c6896dedadfd9568b21ab4bc16e6a233e1289e8c37daa01473e22136a048f4a9eaf82b11a358704fff8849442357439f9ac841861b6a7c1502014fe1cb25fe53833afc54d995e05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3485d7aa8a81613db139acf84ecbf37e1e40753b8ef7acc8e1d340e348f81d5d","proof":"e04bbefbfa50a85d4c4237f0ff4fa707e25f49c69e3787d6f696ed98202b070eb4e27db8ab9e1e681b65c108f76d57604adce27cc447edcf7c7d8a41b9c17a284452204bcad1b17d687d7f6bcd9fe0b96d6729f0f73933240ad6fa02efeb2c351cfaa41c833c07c8c3c8c16bc8f54101a60ab3837a793eb2b5dd53738bff4f0d1fcbd55a0888018a31ad2512ecf17eaee5cea92b0ec540c4e1fad3b759bb440af9ae2f3b63f0115391492d8d9626f4793bf72cd837d055badedd94dccafc3b00179fad72cbbdba022ea92fb84bfdc9fd54812738a09756588ae7450181ca1b0a96fc4224f8015e3b77145ee855d186ee542e4ddb9ad0cd590b049f16f6653b21b056e80abfab9101ea5387e2fe4691a37d6783e24689dbbc33c9695de4136646f24b2185c1765f1ba29ebc97350b7bf89d20a21d7b3b5aa3a26d1dcd7836036a863cde079a38f3bfe707a14ce67b2fa23b46239f2801b2ee566c080583303e349a0e424cd0279c7b79d73af56f63ad944caf885ed8fa0bc94f6636e8085ce64f3e410ab27139eb09a79eb86e9cd06fc45930ab3e8e1d0bfb62a62e7b10ca257ca2466b9c4a8c3beb567495b2a8f45704f2ffb8ddece8e7e35911772d0617100e0460d5b23f99025a2bca8cff5fc4995e152ea387aea84e149d014328ffdec339ea34b43717daa30428109f7e061ce9db05ba160698adce8c2ee7c4d75e226231066f8c40dac0ae21a9c5e14ebaad434e699c632fc4e677fe44dcae82c7cfad2f6a9ca62a212aaf20f83f959c9eb921d6ef43d4f2369850da87fa5588cb4ae92e848bfc14a90d82b1a1d948c961c386f103cc74355dcd763c1a16a91efb91b7789a60d60dcac24f50ea0239f3cb2cf73d60085d519a8af45f4b296f35fe98c205d7179717d18099df99c864935ef231aab9380f7936dfbb31e3cf5d4ae3a1300d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"307da944b6be45d7b3096d8ee157458f50392b777baeabf7c19f8d6b240f0457","proof":"0afd2e2a7973b42df34f29f0dc2496b5d3a70b3272c5e7420b8d1b01d2f8e0106a7785a5d8456be6901e7caa1063118143c0664f6268579fa3d5acec07796023f4ddc16095c307082b4b8da3a8c493eff45ee41c478a9c6d2d96912ac0e4d11e225b874a956df1f0d94e07590bf98b996843de65eededa5b14d23b59ac240701b9a5f6cc4e4693a39d26db94fbf9d39e2ce63930e8a141dfb6fd2ce309a47c0919525d65937b2d439e849e6d0ab40f32c5130ad6e14ae78ebadf204539875703b38bde17e0e57d67c501bd954df0554754dcf294923603a44efe210ceaf3df08342f3a0031f1d8743d92b5a553e01c065d5226d2210060423948df2d2c5e831eacc35ebb4dd03b9515f896d994c58c9e67cfeda3c2451236c546f18c91f4f3313ca30e9773866c5d5eda60108311d645b1c3f746400f275866b968f1b195a62ed225d9f9ae83aca71da2049fe49a86de69c6435777aa3892d914bc8ebddbf5461e9734b930946ca688c3c918d4044e1fd9cb8c5803d0d699267121e54281a70b1e4eadfa83d6ca4d680d417c1e7e025e23ddaae47af5c8a6539d36ccd00abe3de6c7dbc8d714186a5a8efded3c8b6eaea0a97666633c365cbc4b61fefcde4204640ab25ec1fdeae202929e6577177fc821596f2f0eddfb6bfb87a847678cb115bc135b764996611937a00ac2db2d0c6e5ae23c8db1a92f449165b247dcb86879f6f36653c6cb2351581b7754b9b514c0dac37d28ecd31de20600b8555ac4953f5c1002d6867b5a7569962bcfe921c3cef11bf26ae5554f3db8ec9170bc7f4948087c83979e8d6ed476c72c3c2f5b6b32faab55b0c2723ea4a49400c32c73a42fbd734d0da7374e2ba16f769d5afeeceff5f4ad08691ecf877535cb6fff7fe801ef0b83c23a5e44dc784d2d6dcbc51dcceb4d18b0331356420ad240164d6a9e02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e6488dd996db26db1f6a3c047177d867cc7f962ec75dff63463b59f3a1d66021","proof":"eedd744df84da58d025636f897ce924a07428dbc024d2c11da0cdfd15e95ab1a2687d0ffa7b82c2e43160b792a6b80f2200a22257253916e4dd1a72d2efd7f10ecbb0ae093d35ee0fd0063e3320b1151a57190a31ddd9a3a0a9480b731919f67fe6a5b33999b45b70ccea7064e8566d72b746defb1b34fa4da2035253726f17fa91dbc12a4b7f34e787abdf9fe78f80890f7d724242a93ef001896e0b8cd7e06b3b4b1a8744d9c086c4a8da012eec58ceb48617bbf2f774bbdcb1596755eff03e833920a6c9140dbc5856aa7f2b715f915517c6d3b0d12bca682161f81d83c0b3ad7717eba71569af45a0ffd1d2ede44d583a933da40e14719a97e511d0fc358d4ce5cfe5ab5d9e7e28da6a94b127cdb63e7bf1d2733d4e48333c60d6d97db12281956a7ff2af868ba3383efca8773f55083880fe83f26bf6b4de8100f86d92eb4bfe34d45de8c2db62b40aee9688b7f0424a773c53c9492e82d3c7681aab21ae8eb45e19b75c0147e99037ba847b8712ccc9ce516a370e61c1c9ef74ec60e607cbc05d82dbb6a7f9f9b5f6a92c276ea877f22df7627e5ee5b5798555dbde25a64200e1898e4185b0ae8b5497230a0028f0459591c457cae2f859d795cc9544c5c559428cb648e53452c6b07636ba06861ea14f676bc573bd1f4e410b475c0612a9c920313118ef59ae9f13baf1701aee4422230b5d5676836976fbda766681c844ba6e4cdcaef07ed3c30d76733d7c1e6fbe939c7d420afc2d7e7baa3036136be738c1497b92c181f19f4c275d51e519a103a7db8292b203ad0b3c9fb6f441e9ee5d459bbf4bb843e84a81583c97ec5e5f8def7e6728984fe5dd8c035008268eee3ac20294edc9f724499599ce34aa7a1b224b3011251778644960c18683207a9d3e395dd89c650c6e53d1b4135c64e45fc9d6511c29b42b76fd0ea6b858006"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6a07fb6759eb09908edc29946a091ec6f08e7b514ac93aa1a94151f41aaa2720","proof":"708187a0a1e0d5639da62e85778a1e21da9723f11b5e8033cf00c4e79096305dce31be99f03210dcee57cee3ee135d284faac55bcebc677adc891296b694ae054e55f7be704ae786ebc1f87028523e6bb1d5f0d8726986718f1c8fe3f98ccd12982bd154e23228b316624d489a9a8391558738afa1c81cce2bb4be704771d277d1dbce689d20156e42a773010bf44e031b50a456b38da52db0adfe1b2867e3042db1bcab1d2b7e2a8e571ec7d9cfc11c4de84a846d621b98eb8db09a32d8a006e60e7ad9705434438471d95746f1f963d03c2b3ef83699830f4b8ebd5f3fa103f4bf998b1d00ae9c7dd4a15ffc7126c01f91e6c234f678e4820b277ad0be5b40d6a8ea42a15b9160dbc92bf4962f8cfd9dac7e2fbf8d05061766e3e1745a65443c0a24d89290206747d85287c295e2cb30ec3d5d99f96fc4432aa53069e76b3590dfd248eade7f88bc482bf72e65d336ca5bf0a9b6c9eff5cf5ba9975395250b58fa7eae61c7bf215f3b9d518f7e7c27535ed6cc96528fe647e52abe187c5c1416b241272ad0168c00dcb86368c7b38f58ead747c00a501835701d22dfd88721982c19dec80a1f435f566e3a87052f8f9646b519943a2a4158b0d932d90db8784e83350d5bbf8f133bac25ba7dc74f905e3ab2486a4ccbdcd7de31d80311e328b656b30bf6b9117c3ed6f1ec301b3ef317edd24c4033198896d9073a8e3d05439c0a6921e45835293d4ab6cdce55bd14b072651cd850dd1bca59f8ca07251422243104c2c9e626e8e061bf9ceca98eecf66203ea2533f7415f2eded811faca1caa7888c65dc2236dcd1030bfe715c9079b0c78e3fadf0de07996f41ccfdbd66c2f24545285bd07e69fc37b1a6cef51b76fb456ae584a1ed59c117f0cb2d0c709233d98b45c7f95069a53b6c7f17212590092bdbc7ae411cad45495673e31fc0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"826838f39b54c39e43d7790d8dfbeda27fd9bf73ff6b2730ee9b53ee7466ec60","proof":"dc134ab120c7ca7010df9d7562cc1e1d39fce2c4b6225a89129dc6567965e83620b92a73b3ae4cf2a40916fbc410acfe5cb70d95ef6fc8b7e9f60ee0900a9d0e36164f4de5846ea4429f971646433061a36d2901f018a877388f5292f832ad43dcc12b50570b285d903d7b56d2166ba9a4bbac692c02d674eb4a89f37c555b40bde757dcbd2424a254013b09d3bbdf210d4797c49a40e923612a9a23f5ffc407544dc54d129927a077cc8a97ae540d8df8c02995de9ab50135ccfcd16e28140bab1ec30d005e7339453a3f8432b19a9a30d83574f841996ccfc8a956e1ba700474b84b9a0ece9980bc298bf3eeaa15ca1951565e5bd7d402033362bb350f3e6f1e9b6250f6a634021c11f2cfa12710a0464d717ee9e48b7a4d5dccaa381abe26265230045e2bece7e70968dec4272abf83c119bba2ce43e320b5e8f1c5e08123467625aec4a4258d86c81b54a97b507b210dac606586f594ebe626e45dedda5f087fddc3701c3e27e6c218bff269108db70c39e830c44a3d8b262d7fbec1a3731e1bfdefe02ab52f18fc852ec45705d3f085e753084b9f2f60323fdb4e5c061ed2c82ddd328a8cfe99454a425864366c92189fad557c708c706d6b67babd1d7ffe62dcb9e0fa277a4632fc82f0cdf1c93abc0f2d488e00b01b9e18484e790761a07113b323016249e31e7b4603ebe3295a6fda490eb5c085bc6c83d9b17a32783e875be72c1b617a61b34e40b202a7d581bede06d27875e53087b36f6ee0f349aef72a1b248a61fde67b3e7884ca5dba4991dbbd04d501be2f6e811455846326442fc0cb5cfa12cac25198b0f3c500c53cf9c64e8601c8fcfd47fe9db02cfa4c6c5364c4d86f2d0ff746519fc5a55c69e49d9a48c4fff5d4779749de4fb978094c574e924c17bd2baffbefe1c900ecac3469c44df3575e7537d314ffa7dafe06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8021eed01f6f56c3c07b3d67d35705f4d47706177f4c07ec48d77794fcdcd816","proof":"5870ac5dfc27327182c2cb139bdedf3fd2af2dc9c3f6eda314d5efad4e29e03d848e8e0a3b54475dcf3769a02b19232cfa0b6d007f2c7cfb1b8972a0db89dc5f62c31c23f589325d98f5dc9595489f64f84f5587b624a8c02d156c3857c64c25bc15dcc217fbc27afdf3927b22d5bf020eac15a407a0be64b4b63c91e5da8973019d5e67d588238b5970e0bdbf0c60db9eaa5f8f865b49cc7dadd14888470603886bfa2577aa0447e9c6a509059f256740c9d4e88ab883e47b2229c15b520800248c179470848f2601bd11f522bec211430305411804d0228a78dac6a2679100de5e3ee2b5edb9cb1e04f360986bb0b6685b2b1117de69a2a0c7572b839b1901a2ff471750a654f40fe936fcb90e803338695426c502a75a6768b260f2831064742868187a05ea1e8d0c4002796884e187ab64ac75374bdbf14c0ef6df80b93486ac2b6dc94626e99cac69f9e01ed5537372a5edd43f6493e2925c86e22a1b6b32a63fb08b8d3a308e5ec6d72d26dcda26d0b93fe5e852db597f1bcb99550257962a7d4d83a71b47461a0d9cdce3d01c020ea4614666e58745bc39adc63c7b03b8a91e3d7914c7cf676f4acec0838c279d2f137c46e84046a29a1b5de19e537fcc3d3c7c7e6de353be23414c8b5027c9842ec56c5ed769f8471e6a6c2dfeb274369f3a543847aa453e6f8c87afb78598778f4d40e18979a8d39295b180d444390c5893ac020b7b3111cb88121cf43ccd06894852a80c02c0a86560d7b352552b60ff9a217421b60a579e319bb18662f4f37b7276a2154076c30ee82f372fc037286c6e1172cfe6e021fa08674deb78cabb1ba53a2de81028a3949343ef013f10a6b087bb6bd9b0f36505e4c0099109feae2da8ddbc62e454b0132cc407a471022de6204e74f8bc6a89568a26de6773863a8cc492cfed076318889193877c4c07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cc24b032a23e8cefd4c3969cbee9b322beb6d92f51c0c9bf76371cd159169228","proof":"d0a34484850db7c7e50c2ba4a57756102bb14326e9ecd4278561ddd5fc369a203018da8a615f4a35b68662f3243383dd29ffa5f93bc45d19c126321e00b9d26ffcc693724c64a7eec209ffdecdb2047fe44b39e41dda7c8fdf8fe4f985fa0623da36cd75a4b79aed4217583f132d447232706e787bb7302df38186ec0d2b057dfb371e9e3e0883448567e2c67f109c17eec4e806f8358f7168ca8e6c2e09430b470490ae5a70bdc534df4de010271ddcf2e88900a7f40171f2a1009824047f0b337ebaf8e818f35329efa494d1df102e8f23f8e624dae07b8dca98c1ce2fb70abecd53dc25efc7922b6c47000630e8666df0a916cfd192748c390a0b4242bf498886198d1a9871e7c44de9634acc10496b8dca7687d1b5e77345cec52ccb657612216ffda141e9ba531e85069bc474b0d8509d943d685bb8760beaef861732299c6ca62b179d68eec7ecc616575c8920379674ce989a300c69afdf5570d8ea5d161094b8da5a87845daece345a8312eecd19caee2bf740acc1ecf5d9770d54133c7976dd0e9d36c8d9ea3bd7963dc5c48597f3e60ce48ce18b27743ae201005fa0ae20b5b66bbda5beef9a206e89eb1d63723b73ab0e701544f8058eae83041eeeb243cd164f3b51cabdf209876f29282d96329c5faf25548bd7bdd5dc4cc801dc13aa1d5ac1a369bc57b4ef874b6fbbdc322435dbb66c6991ee95317f6b1d48bcce7193c389a94f6dd66ff6ea51db8f5c0908f0199b2af79991a08ac0b9e527ae31c7e1fcaec7b4758182332190c774173e41bab39eb7d46a03254edadd3e1ebedfcfa082a452217e1394ccdff184021fb1bc6ea605cfd4ea584f1d7d94ca25bb38bbac86abecfe4dc648d7862a684f571cef081acc5e557775c025613e870953fcc67754f3ba6fee46616cb6f1ccac097239a10e2cdac47b337e9741f39c04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dedf8819dc787a7120e3a3718e0aa52d60766ba58e3935fd2f26aab7b2b34001","proof":"e6e1cc1077f2770852b3e8d40adbb27536949898a93e9372ce1b85696ec00e775e65c35851402945a168e40ab9d4390b356e8a83137039bc1b3181f4d70a403024c1bb5c165191b429c5925e081bdfabbea0b35b9165d4f8c1dc710d38ed100140ccd8e4601e14a0eff0139542bf726daf61c126daac5d0b63d538fc028c585d526e05186359e8137d1f652731160fcb293b7487b06306af66e10cdc34743a0770bd5e6699a63e1bb8b793125903c45bee73b46632d664868960cb5c99ab2b0a8d63458430c66732a8544d05d669cf51d53a6886aee4f799eb4d9f16f204fd0abcade3b37b91c43adfd522430b5d81f262b578415d1943c402165c91eb5b8e499e0bdcc70d795cf1a690c2a3bbdc9a9596c70f984095436aba5f056b1b755a614e9f2492b55047af2724f11d3ec2e6e6201b5a5b7e024e25f1b1e00b3f17a85e30c54534e523c126b6afcc2c384668ce8c8fd08db8c7f7be06f1a367d18011486c11917cb5936170013f79b3c19da6691e811cccfd9a31ec4d2f9541f9712675b0968474b5a455507310ed45fc18b5cb402ad7247fe3a2205700c93f48fdfa5daac5ba473490283e39aa6ffb1af6fb603150cdd8e8d050e67ce41336e351cf44aed4c47b0b70f69937683ed2eac5e9e38cd132d6de251e619529e2ca2c69d5240acfc3b0fe294e4f59d3615477d79bed6f283d08ce96d59a592c13c8e6774a2a5ea06a62da90e0908ce6b9c8dd7a0fea654b8dff2f7a1e1e4f1a07f9fba0ee204075726830c9a5578a7c6520d468408d668db867c3a788a85a53ee0b29e7e770f058ea12416ca0e5f2a894dd82a7659b70f8a3a6c4217e3c4c5d9f164496ba08041b0ae80f34f23fc4adf78fc1291fc1649b738e8ab37c2f032c4e4340b58601c16b56606ef03056c1feb47f42aa083f3e6c5d3819d59704ad8483d862090404"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b0ad8a5b9696dfef964a701ea8aafcbfac20409abf39458a1422baa8731ff05e","proof":"ce43f280ee866a648cf310f2b6564846545be90ca50168deedb5dfb35ad7304d7c9e676b9022173bf9aec7066ddf2140fb1a381bdb7d3370019a2ad2097fb53af4053a9c40b7b6bb9541918eefb82228c091223c3b247a9374a2149e11613e50fe0a9f91a2b83f412236d098a4798b43ebc60746a570637a2efc11969dbeff6fbb1060829579b9c67398d19e4cb0dd5f7473a934cc55aa00198b4e1ec0dd4c0f83ad43c7a0f606fd470c9a2a01a1012bc4d4bd801175ffe53a6efe746cde120b8970823c39fc87a51eab73c2e4b47615581426eeda040ad0f089be19521278067cc7f8d314e4b8e6c1179064963ad022470ab4c2697ce22bfeb730b7c6a0c73e64172d05d56e35f3efa7b6f865cff5d030632ea469dad1e17e152ab9325b0b78d4a20fd51eef749574603af6008bd8be1d1a707adb26fa0d193c2a13bc169004ccfe796fac5e5a0dfea9064b70d26e07e3188edfab5e60f4fdb5494ecb59b74286d7733fe37dc187113b7619ffda096bd2f319f5fa929855f78da20f3d92034eac766891a8b5027e1bd722f0733a92a7946b375032b1cb3968016712a290e83ee06d83f2735ea0f7d10d0883c9abd3fcab385a93db1d223b9ec85848e73ba349a298b5a961b48e6cb614ea96c482068f73db4d2aedc8854b0a564dc6b9f35f4cee1a5e0ae18ad033776c84a2d2da889d5d222e4d5b22f80c6056a5800c792616e07300551fee2fa96fc38c839c054c726829296c6785db3afff8a2afe4b7ee53b2bda1706262f8dcb692a913680f4f41552ca9b63f47e0703a64df3206c9145b02c6c520f8ad407ea84552685b3a6bfd39e1dfff9773a9dddd3352ecfb5b8c170f64f4d02278dfabacc56af84acce92607f54bf61dec3d2f7b0e21252292ce0287c8eaca2b808da480358033bd91ac5e271aca7f6c4e7d8d5f7b10391f50ca0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a6c3fd879bf331e2f712045a05df0734489267884b591ad690ec5b669f40ef0a","proof":"68776baeea2ba1dcf33e51cec150c474eb609019bcf107f376cdc302af504634bef86283ae2b3379b258716930d6d3087873eae6be77755d734d8e579655803b6675f4781e2d588ee32a5bbebe25713b85d5b3c810018d9da5e22df12277ea19b2e3ef14a3b436ba8d4d301fdc86390c63864f283a00c85417b52b18af526406a5f6899bd8f51b505395465096aa18e3418bbe5d2e9aaddb27df65933428ae0b82946532cc994a26f15b20b192396de050974e6748aed937f68e191e4c7bac021b52cf8e8bad302c8fa4c1eb555a3e288502be1b3c056bb9798dda96c797d604d474e598a8fb0a398be5ee9beee9fa5b2ea8d22303d77a1cfdc97ef75f600e5ad2b9225209656f83bd4ee9efaccd101f79542ed34ee200c413522985793cd0664633437e59791fd0a5c2f63f40447df892ea007c5c7438e9ae3ad319a4c3fa77a82365afd1689b24714091a3673c866d3a869033925c3285fb40b867f33ca11f2cd42787c4c48f328703fa5d1b4beba0ca3235c467835248e09b99ff1f84de0378d1a75328f2d6015c76451c7d14c26bbc6edd8f43cc6b095de524eee068ba468eceae2d960eb5ddcf4ce75aa2cdeb01ded927093104f251b3a45e815a12f8453409b6ae4ead261c2886e6e8e8fb0b9ba11268c31b1a380cd5f404461f0b273632dab0c9393726bc25e817dc70a8444cfdb82959ac6a1802cd88ac0e1a6e387a94a9c60915646cedaa3d97e65d343b3f39890102e83884f80039520eb0d38f515cb25bbd287fb7fd2bedcef259962d2c2375d248a438e00ac697e3fde3879e132671b386731b0997352cf2e0c54495a3df073f2db22e12e7eb7f176af5bbfb6ad93d5d9a3af45b72e2ab5323a09ea5f69a19f9b7d77c3726f7bc26fd15dfa7025f398a097346bb8d901ef4fb32248ce96544342c2c6e9e3fac08c035120e3900"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"602fed6ec4753be8304ed60e96c2f8e7d33c2a1413c8fe694df2e8604af4586f","proof":"82b8a15948456d031ec37d010830dab30913b87a55a40348c32f283a4428f64f20719798eb78fd091a60b983b04f892a6675ce672dd20a9986ae9c12cc5eaf1b6837e6ff31fc8db6a85fa1084ba4e2e1f3359519339691e7e25503e70913ab37362e6478a993eaae96be8becb29afd378c5a6ff454de819e0c8ac58a7f28a825a348db4c0b9e597bffd2b24f658f1d27049566c100be3bd40ab16b6cfe0d3802079f71483667aaa3157aec0a13689dddf46f00c5227c4313ea393d0c4841b506492330fa34f589e03d31ace0b7ecc86124561aaee2271cc9c57566466776670c0eebe4e2f5e7a9c4f898b6369648db00bb397de328c43ad25e5524289bae854428434d8cf78e70c21f0543673b33bba7c1ca95f9831764392ea380de4818310200d5b6245fd5ea5d70885cf3d13e5a89799d83a748ac6680adac9db28275c760f2793694cf55f20194aff79776e2cb3263b9439e7f4bd2014d55c2780ca5bd5dee830f6635c19745c61f3908b8b137a38b4925273a4cc782529f8f6b76584c56167b8a785b0b6b88f88b0258ef9bfd6d3cb1ab37ec5a00dc1c4ccd8cf81310025c10bb2fe04f4d1eafc015873e9e19f2abd9202b96bcd06435d0945319f2d90970698204c4fc2a1212bd32ac525f97945e5673366fd37c8e849604076dee7b2f40c86ccfb06482f35e5f7dd0671471c9a2ddf520fa2c3e45207187bda25e7163eec5950edd797aec1d64c22656f1fbf061d44fca4228063f7ad58c3774fe9b4cb2c1375c951a446c3f1e98ed6ebf3ac33862dbcf154c227b125a079fb5f9f64f526f2e549df509818072eb149065ff82f9a7778d27fa045a09b1999a1df18679b9e3d8d7c492704a3c0471fdb62451cfc7dc883efe114ed2f1226c1df1f7d10a19440a8954d9ddf114a0c02a9ec700f6265dd777f691c6269ddfdf7bdef9e200"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c48cee63e225b5336376a3b87581088689ce9ee1d406ffe4448cf516bbba6a5a","proof":"1406cf04123608c65d6d7912581cdaaf47acee977bd9ba2ce5f046ed56387268c03757822b349699c4a57e7d192238fd3d239d8bc73943edda80aeb0be781f04b42d6e15b5d4513e14b29c54a29e854e4e27acf981d5f1efb59f2241bee4c552c6bbd35ab954fee71b5399ed4c9f6ac4f75a6ede66f9436f66fab8a4dea0865979340d33101d5ab4df9fadb7d9a538949991542eea057af9b37ae42a36ccd70460d565c649b64b871e836828543b4b07f35ed12ef30e99425bed34b714678c0e0f09b8097b0db94cff51128cf1b4b368ce34193ed93947e6166968e4f8bb1e0338520206169eb56a81e396ac51c2b639f413ed40e1fb2f3f6a2e218ef1711a3bc2b7d34ec13c497d4b87bb61289e4dfe522572adf55ca4c1546286ec05be8b2a9804fa6ed0c40a3e597ea0cf7b31cd063639c9c6097dc18adf02423d64532044e0f1de46054bc251eca7bc4df243c207e30d4d2cbdd93fbe488d99c0b506f85ff2b434d1984cf085e903fc33697be9baa51ae6375dd0a42335a1349b8b14796f5037d0ade152b95100520dacab5305972d050082995a41c8c8bc2bec115d303bf6770caa7c93ee342f0439f396dee086f0f7ea6d8a2ff026ba0dda12763820391a7f486599141ec276790e5301b197bf62b89595d599cabd026cda36184b027ab4e273e1181a75fec1ae86c5fd840b2d103209e1eb5ca2007be759c3a99fee7dc01b8f6026f97d89a77966f6866f6011d02ad59f3b36c50196ba89a51d509d10bc3843be0ccb5f2d8578acefff55efe5295dc9a556dc28850b5ec20d0c78cf068ab6caf1623c1a0b2ab33f212cf774ca7efc68b4940a17258fe70a6048a0e8559e9c549728e210b2600660827d0255ffd53f5bbbf75f67764ee4dd334f593e019453f4b88d5cb4fb911e73bb55a46e0aeb8c312be0a562a0ee64e8785f6a1c07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f05b70874b576ef0fbdd803ba1b5d43652e0a465d87733f41cd30f1d4ffc0b13","proof":"aecd022a94110d63405cce9c1adab9080d0f1de933727fef61684ff4ee88815a34bd3dc6f04ebcdddd21aefd8a29d919ba89b01f05c3bd2d57f41956ba842e3bf8dfd7e15d499541db0c4579c5e366d9853a14a15df7dd0bd7add130d57bec508402c28985f7331b4a81146f64929a6035046ebff1bd314bada45d8a1d77715ef262b49bdcec151fe1548b2a72ca6ab211b43b5005fc19833f7016b2aff1bd0438561dfb3b728afcf2d337e4db4612bfc005f40f9e8b6094da7fe5399a9e8b0211c56bb32ad025654490a8a49d88ca5774a92cbe672c6be74289fb141e3bb10fa082ca6a3ec55de580d8c5d75d56b7bd33ad744c2ea1168b57b1da857b762819863427b3489acc3e753b4b0253b71e627bd867ccd76a3e8b5a16492e1105096be830817869017e497eb2636fe5112a2f5611b888edaab806b3fcc06baea0652db66357ff512f9ddf55375f8e68968d1a8cc5450b6cf17f5aff6b519309050e22ecbb5c4ad4364b427d3d9dd3d8e65b30ff8e13acff4710f391011a44b93f4b2cf0c2de440d857f63e0a25d00d5d4eb377491cd0ebac1d69765c02f5d57472e78a0ccd0e16da1f66ec1f889921d342e38f154d97800bec04f0a1a28cdd543f73d48e471c29b5c1f0fd43892e6725f72d6ed7c32676a97d858d138d69d68f13c06de528c056440449ace7b7c5379d692ece76453c54b4a71aab7abaa913c81c56e667399b2d5479b82f047d0eef25a7edc4961fd2d8c64a7c97b3d43a83371d262ccdebbc9fcd92f94414f96c5671b474c94bce76a92698c6690e308691473b3208ad70b4f0f1d6633e192053b77e9e04a0a78424cd1cc1c34ba2f656903d49c2ca59aea732acd8e72db237a0bc843d40c84ae8b3db87f6a12ddab7df6cad2ac0c1cf839df0491bd2b84af43d5d756edca037bfdb792bcf280331c0c6045716509"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4819a5f458d6892172d259013bd521afa65bdfbdbf5558fbb81f114ccb93fd11","proof":"7cbd7e15e6db7a0a89c2a735e3a03da4530618e096c3124e9b785d19a3e13c16b2364e3ea8cb135a6515eac1e026d3fa58fd74d27e60585bf3d995187e6b6060ba54ff48961e97573082b151530198e69be64eccb6bd7195ebd2bd3c7e31b15798dd04035eec7740bd3aabe7f1c0ebc1d89f81fcb8ba39f05d02cf78b926034eed04be0ca66215ef57c5485bc58b5a2bb1393d2d53406a91a82d4c42fd3d0b00660b1f8578b6b928dea6d8a496912d04665d16ddd9df79904372c9887131030c89bad7e9004d3291ad08b902322177e2202b53ac5d61f5a81593b53c0ab2e6035a553051a23e891a2a305cbbdf3d55632e032a0ff48f2c4b60ff1bc68b6b673bb49d0b7ff8c590f9ff2963176993769657ef3ba98ccc0852e090c9378048a3491abe44af57105bf3e4100b4d940531887ba0fee256d26a0f398baecfaa97c1558ac03f4c8c45e78fab70e437430397a1ff170de7a1c9f27ab7433c1df87ddb2196b64aa56270d5a6d158df06eb2db2003973756b7a5e9ba8b7407a7bf59a954d20de3eaa754b99ae47a9c03f232db367bba8de9d2675fecf0fb8a83c27d543730e6c55a88c6b713f882f0e3023ebeda7c179c963f5ce01a19913479908ee8024b4e6eebc8fcffc9a2082673bbcefd3ff67abf0dff351d7434d3a8c383ab14c0d1a293d5249baffcc020436171bf9d86ea67ffad1f8f5f4d6e40fca385d584c1cb229949f9eb77e53323f1ee0b776da58e1ae27fe269b6207a018a1632f187a7cd04e3ce124e69946736b285b34a66f8c056231326b459daea5365b265f126025d6337d23e4c5b9f9c75ac75f2aee6987695f8bb8302f16b315825117b9a02039b7a27beb5616a4331a9b726638795787805811733bb95bbcc546952404aaa0031f389c24c80e6b9bc2cad94cbdb06cfdfb65aa68005b811b1819abed2b8b6209"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c0b8bb1806de7d653dbcbd7538066546fd28132de8705ef5a2eefcf0fe0b0a26","proof":"fae1f392291d2e9594e6ab630c192b3347cf531d188513d8d182a67e2c8a3f3dfc02e805735d14a3abfefa3ff10702f4cd63c0782ff8448e6bae6de46ec09753789ead10effe6ed54ef42986e3b0550bce0edc0952faf01a79f86b2e3fc1d3120ee6d13a72d6d0e8dac1af64bfa3a29ae4d0b62505bb66d8f625d13af11cdc287f2fe1eb05a88c7924ed825b05e81662c8ba543e8d343f3c5d3f4f74c7f0e103afc19f43e838bd3bbd7d7a51ff323e043f6b525b2f99f6724623897f0e129d03303fa6ab8de86a35b94481cd14a7b338952224aaa6029ca23e3ee8cf9078df0ce8ffc161cee23638c624385d201145ae5d3f52c0d7c166ae9bf0badb82f68368466874df6cd4c58d8a3bee4ff61bee322d7a5fb4e664889ff4d7a1dbc8b2e81afa7ea7711c92b8cbde69f1186affe3c0f61b91cbb8fd7aa76aa45b95ce620d47e60e67691deac19d8eb87980ecf1b7aaffee283d31f484a1e4f450ca09cb3b64d8b51c9cd72b8808e8f714990e8a8a2fd6f5961290d6446e4bbadcc06a9bd642020aa1641b7922a67e22d5e9b37a9b689b6fffa190d0a79701f1b9432feb8d5e98f09cb673c70eb98db93ffb315b68a2dd53f8dbf7a4c5e7499d89c654dded1f065315de07f3e210d1757f061f04f6bb172c4b49b8c2fe292cd2ff54d8d6ef0ada4863824ec488293a0ca446542a56ff467c1f995b12678e8bd2badcbc70bf04da1edb3975bd7e16870f6aeda4521e89f5c67d407bc328584ad23bf63c72fb2856ec92b2de1843b58c26aa106483e3c5d944d2dc4dd059e5a8fee9b5c741e05948db5a0b10b686876c41a7c12ef54e6965ab3463448b3edec44fe3a39348c66a2d7936a04736957ebaf7a35f3207cbf6bdc1599e14b2f434b9a802c7f7b7aa0ef25e15bc1cdea40659e1d643cc25a27e0f4661322610e2826c559f98016c310c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a6798ab7e45a79c7c7148093a093151be8ca4286336700cd7b29278fb3f0f979","proof":"fe4c881cf7cb802c7c2630f29a5a36f7974c105f92ea79f0c8de314609128f68b23b27e104ffa07c8d3d93a634a1ae1af4559d6e453c80b0646b33f89b5f82079643fb8d817bb2713630f3f023c3febcf055eceffe33355c7a9ebaf024974a14027ba6dba02f1831c3ce6af675a171bdfd3e16613ce51c8d371def51f1953741a4c60aafb9536a868352e6f738519aec5ed87dc3654547fa0340691953bc7604df17d08e9ff127f12b37aa73554d8b545c87d092786060e9b633286261bcb60633769ad68bf5e246b2af3b8b41009fc9e43ce2be0eae236d7769341f0b302608b66ba67b3edb59b78769d9da463a4cf9a696d7dca9492740129f771c34c3dd32523028ea43821048c57ba8a86421fc063f3365c4556866fa2f444909581cc21784439fed8fa2578de7cd8ad50c6c51f2615c565c05411c266654f9f2b3d2415e98447534e447366c7e597e6bfd6745d6eee390d024633912a22283abc5180d7ed4cd27d167baf4de9c6491efff75cbac1d3b68ed0d812e58dae94af9d9c6410b105a401cc5b5de2e413b85376ea08cf79e7ec954142dbae6f8624ab064c1245458e4344cfb511c1dd3a4333b903b24f12955948d2facd72a148d7a59eb5ab26b96c4f09f109e0ba55839d700829b76f6221db5fd5b9a7d565e7290b98331980f3e7c7277208dd791c3ab44a850b7496c44f9168a9eee7a5a87d5807136dfcc64d0f71da671062ce43d38243291bb1c3cce3784700c6b0a4c89effc6b5cea19506ef3a493b2642e294bdc5072be1524ed4c905427e87f4467852c444942823375567057eeaf23288ba34c24d100dbd21dc9bbbb59fb2626c848c27d032e133a39ac6f8bc31762e5c28a5d74732d10cdaf37b905b3cc1cb591ed60db627ff11d0c4e17fb0666b818a763784feb1180842a01a53376cd5abb427118057f879fcf0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a898503d9f15cc05c0214ea4d5adf14249dba129a5052257c685c013d7766850","proof":"3c21f1db2117d38f3cf35f87eebb4ccd26b6ac2631eea6cffa17646c6e6cbd76482015ede5386018ace16a4d2b8929b2609c1464a27c1a756e09ba50bde30a442479f974749d64749625a63148324b4586dcea59c6cf32f82650879da0b05e724e76c42e85cd1bc889f41fae7bb7f4128a56a3b0091a37f17204da1671a83d5c68b243571a6cf7d0da644b01ac90feb6413b2d300c74501101550a1fa2b6ad03cabfffbcc4847b6540476e31d3b7ddf20d4faf014e00b2159f196794e8747b04f7c162efabc52729dd211b47023c00b9c59aa8bae61724caf501356d193cc20ec6680066a5ed4f0973e71008a6c17852789f50a2f6cf71b3beddadbfaf98a40d86ebb9a62955f1671a7af81678f48a8b48e808e8e2978cfde2d83464bbeb055ac2f1726440ff7c125373cfde9b672e5f3863f51b90ca741fa8207e1d961d6a4900cf0a12a8ef7e7f19c0436567fb0ad299ec1ecc76c393abc5fbfebc9611076cc4944d9bbc36e4268b4a249066c4281b7f4e2ba6aa43d28c5fc340b18cf3206e6ebbfedb314259db99106da5a2b0d7a14a52e3561e3f46b658ca4c02f696ab36de9197e0196b6c9676f07f84a611eb88f01c78d64448a1b955275a9c5b53fd2552ae46d754e5187803ab06a6126a9dbcf24cdf4f6a95625c7bbd2eef57a0982db4f9b943b442c1bb13bdc6ae24b8ae6a9e861ede9a415568e276d4b77b37190f523305758b75c80d773da580f704ac3b86d59baef52f83b06b797ca32690c25c24180f9b8d28836774579a3418b2a7d4fd792fe0318d00ab65264307fb350734a4fe57ee681b7e767c41fe31f688acc43b2b7309d29ea4dffb012fe884bb81103811c122d1ad08e5c67564f1d5c21cf0cd3acbb1071895262c0102e9649408081d539f1f2b15bd4d17caedecd28dd237a1445c8bb54d35f085738c631346fc0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f8a41dbea88c83464cbaaaa1d8a219cc9db2750b0203e680e8ded9a16c170e61","proof":"642ecd0fa6b59eac5227f925d174744e3f81f701447e258b0ecf858e1ed8b70546031f5214a4b81762f901db00861ec3de3631a32ed37bc2b90aa2f86811d67f3e55d5fb105fe9881336814af73b236fd507a33daca67ce8b1434f15e49b580f7ed1f500a26f75f64d4e83e5d4b4a85bab7d57488f7577fe5f7567faf7c8e0131ebe1a787e9a8743b131c1ea071f1fe7ddf46b214008760f4de02677e3777400c4aad917f441a8312ed84f51b57ec30dbfea74346d6a79d20d89e30d5e4292092743f0f8d7d95ef0b0d59c254905ed0d123374322f9a702698ba3617634cb00a30d3c4a63e524802653a6caf805f191e5487c475acde55dc28a43c1b6e769d0e48e5194f37e0901385884315fc23b44b0171ccb9b78107826917a330c2a2ec2a3cd13b7118a6b997156b78905d3cdb501a586d941831cf5c3ecf7d33f0de5605aaad3dbabff6d4d65666297a7d9c8f9346b039408eafd7c2841b5628b2743c41a0077f409a376a0c327ba2797c3d6d97e3c76f4b872b57927d0e4058766f3917e82f7ad8cb69fc003ea24fe9466d5bb3b1401dce7a555e01dcdbd341a440ec077612509a950f2e764f7789e6ae549b4949d8ffb89cd4485e332e74365461ef1adc6c7f566deffc4af99a761dc0092563a209271733bf37f4728bc98a261dab2ea051cbea30fa1693781fd301a81bcc56d7eea88837336797bbe413e8f659b9776e76bd8d469739db88db20aec4bd8a99de5913c5514a16c9f125695de681462bde8b2d4e92796129fe14c4fd26d4d295595dd31b27495b75edbbdc26b394d405aa2c3c2fa022e45a54fc6fe8e880537ca880c4acd1d80bffb608cf9557b2bb376d214219590acdc356fedc6771a4c172ba07ca22774943369024440abfe0af0025ae4e5113dc78e4fd809fcf1745e46b0803f9d5e79616a7fc0d4beaca354a0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7091d657b467cf563e38b7527126febf8c09f6cbc75d007095cb0438dd608244","proof":"d0ba4a34b43e47ded18a454ed13f868db59e2293a89beda135d1dba8f59e156e622c318eb843e74d75fb2c1105ad8e66bd72be962184cf0095ab433f1fb52e4f327e6ff9a07b14453d96202d3e8109fb528263b67634a5e0e0d4109a8e52ad324e35f575f9bda1d1b4a3da5bf8c89817955a7fb35919bec3748c748f54c7ee7e40c73fef11bbb55d622ee6ab30561a0963a545a6df56db70d90f1702a4b493096a3437625b411cf9faecc83912d8aca8e8ad211d9b3a8cb86277b9329056050bb20dd95e11a3ea9c48fc71966ec0b58a8fb6d414f854027231372412549bc7015e58684b984a6d6328b4d8662192f969d3b17eaccdf767b52d96087c87db5e6518e3b57515be33e9403aa78f9ff90285b68fb6a07b400ba2e536beca15738a73e6e0ee2f0e3befa20146519c869938892ad67ed484257891cec041caadacbe6ca2e3fb04fa79cc2901a0f69d00afd73b834a39fc9893ff6c71e116884226d96f002b6ffe7d499ebbc4263f762d3fdfea6e6f9b4f040b2bd3a42be54dedd4470a8ace164359ab4211bbc59b655e0fd5c66a5cf54106970e6c9c01910ca0379f644480798f4fe6577e014b3331a9c62188a0f03f446ad940bdc5cc5dbc7de1eb448a671af527d4422905be9d700a71e92d69a7f4d6e748684a3ab6547139bd1e5aba7a187efd121bb9fcebd5e71ece1ee9eba9f24e42e61deffa1f58518f74a962eac738794bef7b367a600ff32abbfb8b4a70efb021d74565bcdd4f2d90187a6a16208f76cff7fbeaeaf879f0eada7acfb9de930d6c9e2b35ea6235ca07c58020d66205be7c8a81833ce2710f51a3cd1cc8b1d75dd8118f4d0655b0f46467073d969cc8c812186ef34fdfc81a2a7603e60e8b9ecee8a52c7bdf22b8b6cde2810f11b28b5c52aa3f7d64b66f0a1cf4d1d456a7efe8802613e725df99f906e28200"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"62cfcc09c5727dcc04a6e4b044eb88a0b135adf61b5fc8cbc3cb4ff889af9a2e","proof":"5e6f8a283cbe9836890c3d634a7a2dc4932f6ccc829b2e2ae823e87992c3e869a6cdb02b70493f4e16f40bb24a7773a152973dd28d677357d494bf6050d442448e46c044aa1528f3d9b823f170bb191351aefd3830d3637ce38404838cdc9f6244cc7d4312668dc290e78f8f783051ab3ecda63bcb8e3695eb6054077328da288140a2b14cadc7d855b201d91b6d7a78da86a221b94530f9af5834030f7df2021673f9d35fa6c347d91b583dda2350f9147fde4729b352bf9c1c0fb7d0d14d0e640982524e97881b1cd6d2007e8b16b1e225cf5f90755c8a5a3889afad45ac097899d1132994c2c8054fd50a25cecf00554763ae07fa7a3829287719f6b0ec1a24e1c990674c2d0174fcb9902993dffc6ec89a3e219847bdeee5fc9fa7d6a47b32627efebe80abf2a67e89c1adf9a1d4548863d28aa38e4e9db1ca22e5c9ef411a9250a5deaefd0863c7afd06d41fc43a0282efa557c4a599537d1e54203963148fd25fd9b5955bd087a731a7d47a20dcb0af8280a2733060f2817bebe2bdd6b80e5dcca1ecda70f298bf5657c967271ce421bca76a1288a4d16df580979ac119873bdefdc7ea706c0ebfe49a138d2788ff2ed4e099441aafe03155963daa2742894b17d167328b9bb22459ebce14f7182fe5ef31168486064189a3549ccae425e334c39dbddb6ae7966bd13ccc6110d54f58fec2ad0cd332d5c7b78b40dd003dad9a6b1aa9ef24bd09ab54e12ffab5e91f230dd44e2ce76f81ad00e46279052f0296e713ac1e99d2a8b281681b100cecc7088503bc4d439081113404c41b5632af30bb5b1da8da70485ac322164c022f787e5b416961746103dfe1956fd6b6d4f3d2b5747f5477df1f343fcfab991dc207f6074758cfeca2ddd4824c22ba00aa8f17aa5bfbda953536c16697e24bab1dc3f15e80d26e441964d25ff56b60f08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9014e1537d5aad7eaa2b278c932dc656771459e3de5bed7e5a41552a38d5435e","proof":"7a6f2b123496d9cfab570ec563ade65fd4c5b90418ca6c6b759722c8e5d9220e8477ff793dec216160927bd01a007956278b4d00b7725017aa64010ed4b0d836823679e6a1ecf9fa33825c33e39ee03c84e1f9aa88c5d39a178cfcd514f2477816851d842b79abadf958e1be3f52449fe088219bc0a739ebe70731e8618d5c330a15b97fcbd8ce21ca3e176c69b236baddc66e94e2532cbaf4aa66922577e7006ae107da4d84cbaaf0f2d2621ae75717b26769e222911484cdecba27088ba1035237d1bb149a8b09207c4ddd24d05bb86dfede075348ada55c671d552e222a0f7ee15902c2c19abf2e9d677011f5956564c0f1caec29dfa5abb3f05a3c62061668e41f43213c316c20f9b3501b7c766d138b3ce7572bfb87cefb8d1b3537ff2048c89f227783f96607efad7e60579a5d87656b095074abc54db7eb94dea600228079f3eaec1d30e61adb8c469d2010f9c2ec967d229492240fff2d207e17904e986455a25d00c33eb5e4e0a142182def9827fa9dff4e60aa2f99d3edbc47cb2df6fc6392f1a221c840db5a4e31e2a77a70b08957bc76c5a2923c46dae0441d3bf6d6f6c65e9509b86b986188e5496cc4b361313ddae18e4a8f54a90c573cb3178042f6ac257fa3314e8c847df584a5f29dac8985701ea022bbe4a10e7b9f202b68dab49642b6b59490e6ce3e6353ae01b824654a93805f8aab5eaaea10be8636b8d64a2786f70745c0a55e6aac0ab6a005a0a983ec462ddc4eb860a14041ae784892098efcca5a30ea170d4d7f323231ea7329234b7d58b532ac6e0d4819ba7f56cdb7a48c0b1b1cfe3a7521b10ff6111eb06105b2be4f33671c49cfdff1566c0e27d5fe9ff7f53f62f2b8bc00abe2ce014b07b8422ad215bae8546938e71e092e22401b697f06b20be15741e879ebc6bb911dede34a98a77a9ed4d16056de01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"669051f8ea473a404e3c191a20a9671810c168e283d85700b5643da46b33173d","proof":"0ced4723e0e308437ed9c685833754026aafe840a1f8881d579891ee66fd3f46c2a2077718f77f92547d6a956a74f6dd57b639312a180f049556095e082a893824f787b4da0224b6ee27539d9483142cf2f2b4a1104d661f3ab7b79b5adc9644162a2e1a14e054d8af91e13af2ad016c20f4b9d4a9728f29d436734c3a4b39152df550b2306f588b9c9ffa2a5c3ee1d20050f39dc4239b40885df66948c20a06b10c9f6933afdbdaefab0bc7fa8d811d32c728d4e09821efa7e390a61306860de6ef3a06868f34aa540baa15691bdded743a0b98869c1ea8cfdb7ea62bbdcb053000340cfd791efc91175d7016b76334a2b30d1844f83a9b43ccc049d85f3300ca3018c51ba3d4c03d3e685776a673bf1cb6e38a95754daa0c0a6e1210ebba7c520a34f8d2690219de0a09572f88a73f046bd67cf64c4d78290ee9aa82238b3b4e750d532047f05467c6c22fbace209594c1b4dffa22b3336df987d6d8c2d754b4cf7f32304420f23f27e966045492ffebb3a0019ad39bda2f2a7c68564b2d57de0c890acca595064435d0d0ab0b388da7f7e9cbe985799251ecebc48e995c055a49ed452ed1ec60175310f6f4025db0c9a35552a39fb28a0b3e4d3db96c1e2276f83763ccc2dbe9d241910dcc0d9a6029082eab8a64b18e14df6ab7f0dcfc6b849bcacad3f8c0fde526bb021658fab50496c69b23a3969bb05424d6486f4861a28edc79ac93afba06a347fd21124fcb28ce681bc65ae96c5aceb06d7f9fd528f6e61f8fea80c1c04f111c5ba519b31f298a30deadbbb7cd516114840b03826e2af1726f0579b6de0dd7f7ff97603ca70fff7077771e66f2b88807bfa3440424855e76825e7f4b1e1fe0be27e7ed96771c5942c027eed21d13e6b15dcf90730e9ef975c169f39f59217e71df37b5b2f4bd1aa95361c69c0f94260504f2ffa40b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"76710df5c38a52c021a0482714b5ab3de310dadf1faa79b00b174ec917a8fe77","proof":"fc8cf3bd0f15fc46991e57749d379180ade0aeb9a967cf0951eb7eaabea41c5d48b2fcb216df62330c8981b106632f589d6b88ea73c00d82b3a892409d708c178a265d4d16852ce6ea2e40a7e70ef52b719c34efd69f5e85fbcb8e4a4704dd422c0c0e3e70a1055def9cb795d758d8e465aaeb42e77154d927bb5ccff0eaa71134b2d04cbdf5cb1df782d3d5de9e3df8a5875a30c12bf874fb92572e58a21601aee829612a85a3254092a7e3f97fa67433372fbc96c1d173151173a4f6204d04df00943dc011308de8703044101447fcd092783c366378747f3ffcdfad75a805be920a2fe781d81bd1791ee189b529c9ca2da0485ab1c66cf7ac8c89caf4ac00e2373ce862a6b806660f316804f4d7abedadf3705633581c26b9a13d5b54de215a967084d498dba67f96d605b985b1297e5e86d89653295021ec307ba60abb12e271d1177a0a4aca94b5edd3981d38d3aef8e0562522df92c11c0f0aec22cf753e66613ce3c3ce1e474b21b6fa09357ea542b2cd2372d91b0d83d631be7de41f840e4cf419d2e9c6adac77e50b4f9bbbe4d0f0b03ed553de68e632856ed26b288e796ee1e4e9bcf67308df5d28c01409ea1ed8dea132492d0f8e36bf1b4b214e025a7891756d43bd3d111e3dd7114881efa24b041b32b6cc209398934d6880527e055c50206e047d622e77717c1a0369e6af4baaa26a56df86eeb8d6bdd9c043c0d4e3094507691ba179799dfc46653f52c4282b6e0e10578b7575539faac5657ecefb475da68b0189e0952498cb95b0b4496400bb6f1a01168aa25ce32fb17ec20c69216a49b71679e00b3bb0785ae02f04c806e5757c52d60aedb816e695448525cdcb0f0775e41dd043e55cd1581c791ccc66cdc16066744477f7301a5807ce77d265d6286c5c69c82f615c46fdb74cd7bbcb347c977974f9f820d4465800"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"40d4f3ebdc4306bb09986ee459f31b72b3f10b008f1e9147dda7e3114c73cb28","proof":"a09e86ac02f8c802f5e6e4a6e1f0a8fb25729b95f12d3bc061ffda0753cb82483c305c0cc01764753d7a385f6b344c4a410a4e059a5587b4a839afd1166328210213ff31fa3c304721fb42ca724401f6451b424fcb764c3d0ec9095e326b305ddca9c047c3e23b33f052423e1ff08deca9c620c8286849c9f592765d43063643e76144de45b82e98817a3bd3902ee2a1a618f1ddd57e765036637a8dbba06d06e3778b2527a6745e368b70e24d3a72ee1e3b0a080bb17f7d3f7828402ba0ca0c3ce8a5c85125167e7bc91e67c38a52acc0bf1844e488d73e048a708bb9f3ff092ec460e22ce891548bbb71da9ec56180088c373a5b9036bc6c196ff98655eb6e109fc7f8ae2cff7f5e525134cd5bdd8e5968fc9fe7d8f153e6f9b4637d96794f168960d45c2e2f52d40286427d38d449c58ed2f2c031e8330d63dfeff1c62d12c0044dbf5d4346f384f34b96511c58d3ec046cb40ff2265e2f5b43221589015af456eb5e5d1d0797827dcb1e779eaf6365d2d2114c2097d2770047e37a112271b81534e879c586238ba68c509f2fbee515b38f9febb8b6db7cd3c88837a52c58b8bf1f642fb798ead28b99635f6ebb782247e3a90bd9aa4e72180ada6fc4873cde0c9c0d42f9e1aa366d858b9c8b2986a34f9f79db389d271c65483c1d5d9c6ad8734fa1e44bbfab7b8c646e7e4dd3511a777ff33f4027e5c8dcd65645be7969125474974b18d64a8ef5ccdafd81a74913b278c11c99994978a866b41613901cf0694e3e378b48211b51df4dabf02eef74a8dc16681fa06fc25695fa0cc07c7900820d3e88c2e01188b1eaf514e790221fbcfb1d4cd49a3e6531c2ae5b2aeb7be3443a0b796ed7be008a28821327abb8aef89eca7705be44f7ebf6eeaa047b00a59e3f0929194c56ee7b5ad8573d281950e7939830b4c8275df5e9eb9f41530b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0695ac8447f57128e161339b9320e2300eb63164a279f69278be11bf289bc568","proof":"84a30421fb8c9f3a378c9f9dcef4997aadb5719be22e49ccfcc552657ebb6913ce238c6e2059240747f00f792333f96fd88a2c9d74e91300825f2ca2b357d05d92a201db36e363206dc37608041ce2ab50b41c59a5828d083c5ee07f2575b501d2007f39f0b601e59cc0f9144f311aae3be89da892da34061a6d598227624b67fee9a2f568f4d1edacb0c046f5e8fd1d3c2ecbdd4a874fa53afac12b47557f0355c9966dd8be9899880d4b63750e2ae5f89d4eb1f7f352a0e32ed5e166fdc80b3c6949c4d4edab7891567c29dc48a8b9dcafe35bd82cd8a9272e739058260409482716e57dccf900f3f01b864f5f624e0789c387762552c91cedd8f1621e870898778ae9cfc254748252a4e05f07a290024537aea4dfe783100689dd0d45f22d28949a7355567ef56dbe48ef93e993cb394bf8bf3e08c65515112718f7bd8e4f024e610c652958b91b2fda02acbc3ceb9bfb1c5753e43f884dafa95b5fa2920b1023a2a79876af95b0f92e668a5dbb9cfe5d6d85009e5e9e8efa55c895699d4e78a626923adb2ce77c4a96289b2e5e9bb2b78cb74b75d7146219a0c7517695083cff2885f2e40e8c268abc3c5ead9aade7d36d68d8f9bae1d6dae7e4e158a861bcdec5deaf22936cd463bb29d13148f1a05145f3d5b9ad08bbc51ec9918f34508a7d518da82a9b0db60886c5c208dc498e89ce93078631343cda19c742416a7980821c0971d67ae808cf9490d413f5845387321248829a6716c8bd8b0346ea083e0a291d236edbc0c85cb86d21fb2f143fb2cf026ec8a06b27a7a4f3eeec9d5da60e26db9d205273306bc4cdce7fa5362b2713ec53914337603ac3ea84d46930e2b708e1024e09fd17e1c1610cda647f487bb530366f6816daaf1f6ca3abdb0f228a95c9ae1a7e82c19d49f79f56fb7c18f28beb57e9be53f27583f51af4a00b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fa95208ade46977ceebad82f2622354df52ea04d251456b97e4adeff385f9c7c","proof":"5e24356cdda56e7e6bcad98a4d70c737449114e70b104c2c7a8f8c407d7b2c0d2622e46b31859cd63e5a9ab000019072b30cf0cdcb8babf615e215c082ed676f40cea3825d02ea55385857d2e1ff1d238d8955b6c7456ebdf070dd767fef60470235a1c7533de48a824010669d39171ac456cb2bba7ba0fd19d405c748b91f65f835fa51811ca0a9412fb93ce149d9bb9b75633567ca15701ff3e6371f89e10f95a9d263a5f08549a8e101abbef9270c439caffcfbdddf3424b7250538255e0da925aa6955b0fb62e557a21b4500c19b32d697bdccfe1e517a8e64e53f01ea065cf2e4b908e0692801f0047b9d0aaa9547a723e0d0788c55e7bcc12feb18bb17be99d7dd54f6aa13b603bd9520fee0d0454a7968f6e6397306eb849cd2464336b4a838a40fc051c4b10d752ab8716ef3fa3514c186609c233bee077ccaba4b101415861cb7e0295c4212d575f065b709e4f98f4d48dc76c6155ea85687df24468cd5439a7f89c4c2b129886bea41f8d076d4ff5eb4ad800f74b659f9ff260421629a30b7673f7cea6ad2ef164092cd53288ad7c72f01b4214becfaea02cb680a667820e813a6444286328a09abc50160313eb84db6a5c2d5bf25a569611fdf071a82bcaa63b9410d7c6c153ac0786c6942fbd5bccb1fa46a9ac5e766574c0c1c46ab08e5bbc0b2dd821b50d6e57cc1b54eccd65f4615ba1ccef32b3b342cb75f745e2af88813977e5e5d2512b75bddfb7e07f05fe2256ac3a8bdd89e59c0260ad8f6ab79f47384a34f6d4d81e54d8587e419a532d5171b01936c21716d345366963a5b1aaf412ec6713543f98802d45dfdac9594525c23a3cf2ce639b067727bf8c99d93d4902a67a117c2eea8e4de222b6b2c9b67d3d76041aaaf76012b72096e68b42ca444bccc25ae0749a94222f0866c963468b4c70aa4d1795a84306409"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"36d2315ae2c37ebb702a0d09b91ccc5fee3d9610bc395dd5496b439da297f34e","proof":"b67517fafc9005543f995489a994d33918853a4a7c77ef20497c454e0c93ae7c0e5e07686fcf2b35ba950fa73ab35ba828a61680dec2f97225669ba05525e746e0020f53c0b62bbab1a1d02bcddc00843ed5ecd45b054a52549803e5118f3039f2bc584653aa4718bb4cfa4c88e3015ef69906c3c61db6b23cd1ff6e447ada57c67906a6b09c200212064a3dbc6f619a098f10b7b1b79719c19d8839f3fff60584ef88e434acd6d5d7634a3364657fb7f0b26030e2e36056b5d12fe215ce29090a09e794e437f57c92fe136e0d1bdb411782ca98146981e7f7efaf983ca06a01b0df9f7a9681643f859d535d9e741536f23202b14352f5fca92d2921bca2d230982d60994acebc2511276b0ba2df60488d9fe0db58d500ac5975243f2a16be72227d69719b9fea8ca31fc90d60b8ed41dfc3bb099ca95a31c0cba2a71e9249400a182c0902a6bcadca8602e187ff1d548faa75c89707fe016b1f5d9ae129952d16a4c8be51c3d6bea2b4e717420b8be2ccc6602767ac3933b83308b7f46e5209c88735d61d2448490696274c3b013a73ad40cc910c897a0105294b85d57645013232b223de8cff79d2446567d81a07409651c969338baea7be861ed39fd7e00174b472a5627ee6b6f740f4c79a50f0073ec9e51174a95a469faf73f92e0ecc640cc0052dbb78243ef3f19703680d514960bbd6f0e21012d8051b1551f58c43237a438c45b7d2290f23349bc899f9f45ffaffb9efd8b6b190f8cf8fd7f93a1a49f4a9d86b2ea584743940a155ffb28014e8fc2baec9dc578be7fcf6deac2b395cbc59a575b9980f4fdafad7c785ef70b00a0cd5859e4cb9b043287364d5852f55ce103b573106de998c7a27d70ab696f85799036abcf9c3a16dd2dae8a56897034eb88b840e92ccff0617781f27d73fd4446d9da0848efad0670f4d43b5966406"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c04f1b5e62a07af908cb375df7953071fef22012a7aafb196b3d159716128849","proof":"9ae6062217788c66d00add98e3411d23601b8a1da05e5242162d4608e1def769ea2228f7aa141f5fb23bc6e396e8600d01c24946272283aec87c75f2ea90286384350e8250fc11d089c8e1addff6933801db6bd9cd32e3755a3acbf4eaf630604c99a83dc9226d19f56cabcef7a0c683de9653d932eeb5f4b5e6e92a8f8b0b13ac14664c355f6b3e6a2bc69ac7c9b5ca6b091ccbbce5da4f4f500bb2efff8c0ff00a032dabd083b0d828598449cc232f1ee4d8f7f175123a48a23b9ef5ab5d07f499c3ea9007c850f35e5dde318185146d25077ab98591d54fda5a8eac082e0e92f7cae7bc04aceac3f3e4db67363c545ff6ce31c598f5c35f13767089c86d48b4dbb73af186d6775859fa80ac3c0e833c2e54fd7e9783785c601ea5da374b472ce822face722c23e47db73994abc684411e1ef0babcd314cef4fbfc37f7e65e209f8ac6bd26ba550514ae975c3bc0697c3722fd654efb7491d41e55256b76686cf19b099ace35333bd33a3db78fe203d22cf83cb61f7b152ee9a13f71f95e2720055b6aded0e405f9643667cf15e82792dff07e869a4e6c5a05580808388d3548c91c167125b11ade8596c6aeef79b33a465511920fe79cc7e3e66edf10ca4d9c416010da6ba45e6ed90a49574105264818ebf22a66505c9dd50bebd85d8047fe1448cf98fc0d90f5abf1ca9d82fdfb2791aebf61feb0e1fb702efb3b4cba5e0c61b94ca5fc7670b1fd0a9d759bb7791b3dffa22aae2d41a6075826c8d3144c6ca496581c1a6a576f2bfa8bb94613a06039327e9aa71aba988a3c4984393c07a0f2bd9b55e3ad509f259d6e1198f0af5625e808610e3a0fd0383ec23ef04c6c9f6d93275a943688c9ee244d1159ffda96f88197ea69f53c60816a553157c1087c335b421d2e5fcb46d428ea8c3297b72041a2e46129431492d8d2b5cba40804"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4875b7e2a87e6796ccbad249c899d365128c716b2e1ef9a8a03d1bf36d92c654","proof":"16e97f334857ef97f35fdbc147a657e89be2d2ce7d8fcf144ded328d440e8b501af52896ca3c4ed618c67629f5dab7da18663a6e1375b5801c0bfa0062ca801b86bc8749066e2e4b69c2ff2f381330ec290e5aaa22b9ec41565fcfaed26567396e84d70bed5b22bd37447ab1bb7a9dfdf7029764c84c4460bf75fb20cbba280cb8fb18a877433fbce4c74c53e503ac30e28d49b6ff2362147cc3ad7c4b91710f8ae0e755705f6d2fe9de626f2411731002fc3e9c673b6c37e3422fbb6158780ec04677339bbf21ed72c624482a9af218656dcfe8695f53dde319d648a6afdc0500a7f81b6f1d5a708ae41ac59ffc50ae71fa4843ce4f6d88d83ae5db22ee432f8aa68eebae15d303baf1a1f13f9abd4430b97efbb0c4dde57c56198c75e3884732671ad6835f94b94381d32217383166b48f3e0ee08696fb31ad746731a98a1dc21b137a7bba6294ca33556461294409d8ecabca1dffd3325bb5cefd6c7b5d5630d6570df99c928a3bc1df91f7d9a915addb3e0993613b621b83b2006775cd1426c057b6224641308beea636c64322a9068efc36865257b93bc70cd91a671d224efb1a8112de73a7b09e98e31a5077be0083d8be4ffc0ba98208effc0278b10972c94324ad1148f50a694dd306d8c550928e70be2523619aa23acf68c7755123b663d0215711307a21f5ad7d76be0a885b73c6eae9a547a02a77977da3fbbb07b062b4f4ec34638cc27da84c01f4ab88a1da5528a7cd74c659c1a22633f1ff04caef50d585c19754de000379b8940284ae4fb2e945cfa0746d019262c6062428267d0ce70ebdf48d2f35e4e73110ee4eb0212602bc99866a458b7b757fc88a7d5ed7a5605f39b67ee7e2617a6141898ef4b4e010c67c13be328ec67f1336cf0ca86cc6ae8877a8092eda3fb87ce2fcf6a79972f5a751b2e751abdb22dc61ae07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fccebc66567c9feacf6ec208a670e888f41c7ded39d689e134525ce7bbeac304","proof":"ba2233227fb2151a4da6aeba3aefcf566965a02cfd4ef2143ea075c749c2ae03ace6e47bf4ca18a89e8aba550b373f8bc8a2c4a2309180511e6e599de3c1be29828c17337bcc39d3fb7a26104895be8991eda385befa8946e80fcff4e47d17294c83e8e0b8dfefb6fa01058ce62fbb73c0102fd49e7d12562e61cf93ec4d70009b075c8a752159a1ef870b3a57a01f96ed4af47a76d44afdc45976daedf9f90ba78c8f1b8a96601fe2b5565ce847034d117cbf7ad188c6fa45349e798aaaea0f8608a029dcd5cc7c4a8c67f5e11d9f9ecde90b3e43fcd67cd58aa1d9b7f35300d09a9075e8484594e900c3bb3835f02e2981520a889284f4724b619e9f23c37cb868f49550aa2f1e7ab33be5ff6d5dff698c707cee5e1697912bf35c15645a2a4849df13abcf961ea255721898908f8ca4cddc226a0b4a76e4f9b9517ea6c459eafc77f86eb4e3b0383fd9f4407539548b6009b797f594132522129f2e6cfa768077b0fd596425d148917fb67120c17df1d156cc5c0692ae619c16d79dd5be5dea5a05ed54b6b8345659278882c6dfc0b63b66ef7afd1abbc97c5d67d8afa93fa82d2610fa85942a3bc030735a45a98e3ebc27a5e28c355f9f716eb552170423529dd16039f2c8fec55ad598db1179191cee7d6413aa0a112cf55956eaefe11f36c727ee68daac1aa7b429601fb3ecc6b51061efe6e4495d9bd50274734e654260e7a1356afd8e5136c82eb0f087a95c712796edde02396ced20710a5f93b12e587c7df0332b2508a93f7bb0561288d3fdf979b7d0d5ed3c6a1bdfc3c159cc56a6a250440119eca4df82483b54c133ce2e535a874268e81fad80aa1e6b3957177e50b99410eba734b49218dec20f6e3c6f90f3332e8881d6bcf01f4addaf2409ae6d44c771f31d410720634bc4207d7e003fe136b97bfeee04d63e926ff44a0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d60fc9d90a630c5fec9e6029ac414bff8bc175af8ed18ac3fe0a757120649e28","proof":"82ae8b2d99aa9af94d34ba64cf4b6c70244055537c5598c66c678fda5ca527739c14462d82088df46ccae8e0c078fca30b5f50c3ce7bc9aa6d4be2250aee1476ba7d2c90b29431c834b8e34dafbdfb01e0589dc4b385ea96e6755e0e0da46c724c6c80ae001c34f044381e598eb08cefe7303c64f020ced0650c959564146e0a85dd7412d2d2eeaf6f9eb67bd8fc8071e469f65c39d55a9bb1cd3e33adf739037f573dfe7dbc0ecd971dec96d2c53e031b074b1ae408ef7cfaeae0470e1f8701a885139e8dd3c1e50c4804fe48d2b4cddebc106438952ea500f9825a8468ca04da3b59c38926f2866606d684cd195f7b5b23cf46f7ec059c577a180a058d372fd63f6d575755c1dabc7cd1cfec5a6168f7930a51cb8404b7076b595753b1eb48d0a71ecefa205745cb49d58f56b3a35b4eefbfad6b2a21a276bc1834084fe141f6df4784f43969881a4a7591051db6cb8112a1ca289f24a5bf90721190d1c07bb27b2b16f2b2ca74b235828c57621351b5ce2f9010664223d62c3c2d9a17f07ce853a709094f21af05835516f1ed9138e8bbf6dbfd4b35675a08b7f6dd0d5b43f07f58775d87750c2cef43c27f922b97d2c9bd2ebba9ced41b404d1f21c94c3c5450f5e1740e436f3915d3e39a6ad23e7c6bd55a51cc7c485d91a55898acf82a423ae61bd738d7e3df2e35e84ed6be7573b0bfb1ad387f2b5062bbeed28ecf78f638a5b0e6324540bdb0db7190c1fe13ece8c5fb69042819d97a81560f823a6de81ddf715eb72ea2e72857c3603fcec4b9f0baa7232823f2ffff7967bafe2f4f44d3234557bd04fceec820ef2da8587ef62a65e8cd4eb4b6b8fe85a744af0f52509847bc6ea0fe8e856d6ccd30add3e0fc6eb95d41fba09a195fb96709419d0089f395f89e8dfdef0002d5a94ec70b62da572080eff64249f41f8ceb397e140f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"042af067da5de30a5eb7c9a750389a0f05601b997a886592383b773d6f147b5d","proof":"14ed8ec24571e79b62d95180c38274357dd12ca17bc2b8ad1fcb7b030882c60a0c2fde4b47148e3d87a4816f48d79539d37300a5bfa30de5bb5ec9754cab63093475501694a1fc3a70a353f1a89521004456703f8b98a4202b89797b044a671b4456589ddf6dc5a2a23e28a38570a8d4bcef716f58686b4ead5c5567794e406d4af33cb3b74ad6ce16f9be50d9e123743494c83083579c0d8cffd8c4937d060d0a5d961d3213aa1703519c394091e4d862e8aa1802a09861f7a5ebe153f46605484b18c496e40eccc33d30fc0b8727f4837beddac865eb2d20afbf7dc3c5d60576a8d634dd6b5d280fe18c426df7092173a17f8d3714cd2015f16ee08dcce7027c6c639cc803b508fd36ba54c22ef4333342a42a988e28bb500776fd0262f66dec12260f5715b5beb393861776d4047eab89385beaf98257fab8c6aad7a8314564514c646f88172ead8e1025c25fc31552a0120c474fa931182c7c2530f5d0226a3b1db621b97fda7782df71146d49ea3d21f765982d6fdb524520b4112f920506f68791e5cbab442f4e7eaf7c13fd141c5642fa9043ff2791e3c47edbcad11f568c4bd62e86241c95ab76aaa50835f51de3dcffea3a66916d7264757b34ef2f2c59ccec36d7d9de27ce153684b3d8b30bfbf8373cf16055441108f947219026aead040be4f7887fc0c879fe0e184f2d5500f935e4db38a50df2a5c92bdda95266f94206b18227b4bdb233030700362781951c455ee2668b873da8cf727533312c1ec60bde1b6f0d8e5323c84e4eb70a9c8fe7e333cb8442ce8850eabfbde935b24377284d885436c5dcf88cb75bcf0bcc4d2926ce25692a34238ae08188da468714c926b172731a08840af9011dd831a9836df5c708f0d3e1c3f6fe327ee90a4d50b01a04d20d4e16cc36037960944d76c347f1760722be340adb7f7c1ee108"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6695986d342a957cff68115fcc271aed3b8e00cfc5b76b51dff6ef5df964480b","proof":"94c2d5af36662dd9b970bf7f17102ed4811b9f9322eae1a342c1474f3cf4b87224a1758a46e363c815399257d84900fc59313d6559e4db23878c884dbf483e2d4cc61fd3889c2b3e8dda4656784e3339b686c1e5bf228cf938a97ea54d29e16184a804252898ac8a2d385a4f479c6a2b5bd76a435f4d8aef9618adaf63659754b6a4871116228ff1adab2f9a3392f899c69aa54924be41b7a0a7953e16269f061ec3929b475e5f9f994122374ff9eeefb77d089c4199331deca7557068dede0df72f7e23fc7796c5d2cd8dbd1ca9927724360a90f468610e53e52cff0aca7101bc4bf4504ffc1b5ba3ba94cc551c475de6371fd9ef6cd80f3d71ac355742093e1e5cfbfb6db0d883d3fd90bb6aad69846542c8870087d943dbe7d50baa98985d60972390ed5c58081b13c297268ce7cfd8be08a41efecff8e08688086133ce16c05f808c8e4cda144651c0ae811e7e52037600725c38aab2c6eab7f4d1454c008821f855a88a0701b88206e5242138f53f6780d71ddcfe2b0e383fa3fcf4362cdae095c7567937685989462c8816b143d030a4edfc966ed7b46322f28212f5125ec776748f2e6c12d356301b0b18d943e2150232c38a9b90d70c45449153d2183a9545d0a6479b93e4c0972401a4f555093a4bedf65fa037f35d0de5baf00246ec02eef0e1359ceaa981bb87468eb43afd4df2104fa6ac7cdf441619610a7c6e8adda91b294affedd8475d13abde3d6a5dd17cf9d09794ee26d6e1f5261a4201b0df81e7d345a4a0fa63e08d429e1adc7e3f51f988b0cb7de3906357be130b41d28648d531f03c08f04e595b8c5db553f3c627242875ebac245e3fea0c0a672e3c6ccad2b37f388c48a19bed3454eb24926e22aefb35fabbef1979ed73fb740d9561eded9ccd3c8bb936fe12ee9a2a30dce758493d7be2d47c07f4a582167200"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d80fa11dbe9e94634917325ed8430d979ed3f32df4cb566752c10e2592accc2c","proof":"764119ab876df531a9f948fd493af495bfe66d234ab4352e00e4f722fb050c478e866d4ce6e7b28330aa8efddcd22f8eebed04eea3362f5f28da49ab2eea4d40f2d747e643673977e1ca130e2d03f1ff4a71bcbd4ed5b07d9d33e98d1540db732a786ddd69719662ea907d1f213216cb9982717cb2bdefe57698e4649d1b5407ea774a10b156040ebf862f4ec0932689ca495004537e29f61d3917971c4a0705a0a9372453cff71f6b5186fb9880c8dbfb31fd857f83ee3774a6462b257ef60656f31e880f02385b9047e89a517fc75159f43b966a1961af2ec6ce1f538b9c08e410213e6e0e59d5822e2a424d1cda96ad7135eaee4f8330fd3b55929eb4ee5d322a8c3b08f155262999ea1351a8d42a41f57b0d57b3e7d671978f48cabad5490e1dbb9ad28a3eb431f7ef7447a27b3c91c8edd123a86ea58b644498b3c6e91fe6c3d95980ac9aa879470900a05c2febaa451055cb83d9f537044f2415011d7ffe84ee929b68773256548fce64ec67c5915c2a80351d8669c2c784359452c121f08cd7e691b931623af6148cfcc1d76b6479f893aff59a81c3f789f349eb2a2e3ced467658b3010536bae0e96e81408592850f124bff61aaba5dd5111d15e316fa93b55ac81b2f59ced9144204810ea8ea2f723a8fef174980b8f8b156092d15a8ec3a625a4b56410268bc5c2ff62589c54e8ff8412c8d94d5761af3c58cce4df8ddcbb6265ac18c7ad0dd819bfaf64f1ba9ea55fc0fdb92941dcb1ab72e233e2a563cf0691d93b76ba5f817cbd6ecfe29284fdf2c33967e03e00c61754ddd1e882422acce24576c2345c6984d1a4bb709e38d163757dbd52e98c2fe09bed83278b3df037c3367a920e4801c1207f43fa486939bae6301e80797fc59e123c10885a7e33d3cf834b806731d3d3380412fcce6c8f7b68c731b47904cb061890305"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8c37a283480a150b66c993f929c538c2e3bf7ca86fac250238cf8613f6c88a15","proof":"10e98de247e184b71212c614f22c2faedc3bf6ee275706e0a04c669e7d07177c78930d63abe2d3abeff0fbb5b49278e46ca40120e803c6172a226d8bf727be587052380704f51598e53a17ff25830ecc22286fd5b97d1b6d17611d420f17654e2ef7b40f0ac2a47bac4c36283416a40639cbc180c039279101bdfc023c88d23ff3b86d119ec2f69d4580e5288a8f2791bf9b357c469bea52acec0fb047e94400d2976ec620e941c844b02623da0af3660615f039c08a5ab42a5f2d711f3de30b09cccb89598959559581fa7a0bae62e41ff2bb995d39ca595d04cacc5775bd0010551f1f827bff869391ca805da5377640b08bcd6d436a649d761acaf5e05543dcca5cbcc2d7d8dba2816742b6a22a9ef29af7fddcd1bde1d5b67401acba5c0ce29a79019fda9378c92f1e83d15992d50809c48c65b956a03377e7f811308018ae02c0c7d902d05afe1ced7b0a37e2f8937fd5c5f70382ba6a4c46693c0c594aaee66741af50f9da132ac6f3f9c18fed1e0cf2febc964116e009a4338473c341de5d8f9e1dee98552ee9dc410f1b2f848b5f5458cf31dbd0d93fee2320c5c8329c867ed4e679373027d28c9602f14ea989a25e60375affb9caa8c6140dbbe0712a71b88db86348eef722b1efc8d5aab2ab11b77e5845f81be60c6c94fcb3707a90564aa7509623dff23978d1c2a695bed0d8cabda84df31baf93197e85393408460a14d772c6f5d6aded45d4a981e352aa9f29be62f708df71068b0f831d0072284c2d85d038012210713bd6030605a1f495fc0a6bafbe44441412f5ed57365a1ee538ef07aca03bedea92689e8527622a1d32a57e86d7f74b3eabadb849806de21ff3176f709f67c1968dab889344cfb226f36a4d964eeb6710705c9af22101f8a210b326b3d3833d912387c46930bbeb0b5c6cf5d160b41aabbcd6702f8a0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"063d1633a55fe7833021a4fc3b6b3a7c67e35236e8afc149f6c60f1bffa9d25a","proof":"7e788dad1fed894f2e793360c74d72b0d0c0912d1333e05b193a47ac2a703d5974980d906610eda8d8c00373c435e2ca414bf2f0fbde640b090f57f9f13dca298c03610243e1763a864157bd56cf7a3f61e71859d415fa20820d43f87847ca3476916e7564c54e5c1eb40a864d286264aca027f944eb78c46260eab19e3f3909e52e621907aaeffac1a4122e3d6d53750c33ac3aeb44aef8fc56974d7fc0eb0b86421152848856a4e3627d300de3344dc34d570ac73bd9e304af9141475ba20b1da7810dcb52c796cb9e45f593903813ace552d0252a65c049ea5e096fb7fc06a8e11c58d7a8bde483d336ab991c13379dbe48b253def4798020f0f16e024d4cb251c340aa457bd44a440614355040c6a1ac8fe51c9f8e161f17b456305bff1b969704816bfd17eaf8608fca339c8939b83f7428b072fb27f49e83e168173a4754286f9c9090309319fab0605e42d9d4b48a25d1bdf42985318ce04db798896d26e49a34db255aa862ba2bf39e6021b5796df6cc4d6d56991290357e9f1de34e6c618307d1c9d6a44077e88568278cc49333173b241099527baca77615da6910b84faee5acfdcd303d30b55c0843bc00b78df2445e9ed965fb83dea2a227307334f7795065bd7d07c166c3c478efbc3b9c59b35dca233067db415ed0e429ef0c4609b8abd4a9bff395beabf32837004d1d786b17034cef31e31f6d27537a506c564984a718a1bfda981856f4a69fb598fb19edc98a2723ce7687d453001e20144a99766f3082d00c23a95c8bd52f981d5a033b0805a2d4544bd11618e0749127b2e944c95c86e1ecdeae30ba8f39f7483936006ca9a1dedda825251d4ad71515963722900fcb74ab6d1095de319708a011d2434719b8f2701539ddfa3a441f0e1beb34cacaeac2b2ba1ecbd0009e93e443ac1b975961e5b35f3c89ec8a7e7901"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b2db2aeae8b7b394f12dfbd0088e0b6f7b7a386058434e9c386f074197975e49","proof":"025df3cf2b7a26b0232c70401c313fe392e10de583336a48003a0cfa99aa0d33367ea3635c5f4b8ffc7dc3927bc8b808692b76955bc5fd16824159438314f56c18b76f97f47f863c639dd1fc57aa635ea7e898656cf880793043ebf0bc2c1c6a5888a390e43cdf5e50f032175f8ebb9167ec3ea077eb40f3de1f88ac0f154838f7f6b196320a092b49f0e5585dfe9c961d41745e1cdb2c803474d3cad332380812dcaf88867491de611fa3e82607eed9f65266268a51c37503b7205cd20a7e04b35b1676a6f2cc93c8f21ccbcf2b9bf21ef68b5ce1b2d8cbea87065bb770210a7455e8e29387ff3bbe69487d428c2f359ad11b79de84e14c064677b4852ddb28de9f490a56d841a5ce3a97012bdd6fc3cd754b59759047b7ef64467aacc6ec528aee9d9e959e060cfaef0503712a1756f2e6b5da136c61d6741642807bb91a49a8dec639b4173d5ad0a3e56ca8927aa43f3d01292b95cae00dc63055819dc974c45bbf18b37c63c637e21f84ac1fa2f6c7aa0f35028deefaff072ca47ff93e7756671de0cc4ef844818380313ee6ec8d756417f14e206dc8e951f7e5b98a36558852472c387026fe8992426fc6538081301edeb084f6e17e512576d104e4a570fa34aa15c22a5ee778f8eed5dd6e5bf834681cb9de8211b1b2e1cc5717728f45083197351745b2147f1561c4bd0be39937d7bddf0ea5ec97753540e378defc557883a5e568d8ba59d2bcf7692518aaaf3fe91c857322640073596e4b10022f1ba47f928305f0136a4dd5dae5aa12117574e0c3823ee61a63302b2edc93b441062a7117f0c55199dd0b3efea7d498f20a1024b4f4c503bbb7ae79438f9d4b690dc06ca107f5f931c775380caf23426d6e00d7a17829278af7ba9002352a78450d2d5ff3e2d9cd7a961867d309f352bf50a261a52993dd7c9f01d78422e10ee70c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"34be16f721981c054281bc5ceebe81838cc09f00c8c8c0e28fdc6316ebd45467","proof":"aae17985e53749aed3eeb4813d8d03eab221b2bf75c91b5c088f4f967d2d300ae01d9ffc2dba8301ae2be8678bc66fb03dbdc9efba4634bf23f2eb084a05fa279ef054e2e39d6f57e679892b01497e24b9af7a3f64e1d84aa9cde2981383c90cec8ef0526af3c6bb36054dfd9cf3954a233dca3a038d1be32515bb02a04e0806cd0a8bf1ecb6e5ac4d8e7e041d58e1c47c2620df56e01029cea30f01ff71c9019db112e2d78a55f5ea4aa56b0a21c2eeac9a908813f6373d1b232dfeada2c705bb70e92d18ee887b4d06890f26c0a7ef64e285dad6e887b3cdc7487b571a5208ee72455be66ef3e3ddaf5f08ffa5d271fa3884b224a538fdffafbe38f200bd0cdcd4c04290da8f3c30dba617da4665ffd3004cdfe005db9dc402cbff133faf00c2d56c3acb2575e0ac8dddba202fc5b191b88423b50c6b5e00936c881415a844c84539fb4fdfb649bfa10a49b47164f4a4182ef4e431ffb81fb9b64f4ccc21308a6e92c99dbb1d57b191787c7885f6381be5a502c14c344af304d6dfbc93ef16dc4273a439e2bd6da6591554b1e9a270a62d702f77fd4ba4aef2ca044ab28c7d405f9a590afdebef958a8563d8462c66f768124fabd57a8300320997cf7e097d4ce0c3f1f8306ebc2d936ed969128c6c5fd78af2253f41ba78447d32eed89621dc0cafc0c395dfb1317c0c19ccbf414dc9831c9e7315749b84d9940f5900e6035c48cab761bab83da0ebae7ffc124d124c94200d2b2149907a3c69941d1047231eba3bd34d9fe4ca78274e333d9f07e8230089c3acb3611bfac8cb33c77a8373da38926d68ddee2dca2ec2c8ecc6901c3bda818d8814d08cd98d4e2d4da4ce1136ad700303ae96d6c48fbc23ca682875eb8282d8f0f41ece6448831a36ba77043dcd390e42d0de4ae326a7e495498ae7b0b0038a8eed11c2080c5fed7229510b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f0c5807fd541f21b867f68ad7a051abb91222eaf1d4f797d704869a2885a9332","proof":"6e8d18eee741c46e8fcd6fab1773b6fffffa92ebd70440550089cba2d8ae1f4ec0d921a7de39c7ba71c12232cac26de3a50fb0cddc3c6e154afd4f442a43d80f4054d2744a610f2778ffb7b5e1bda93b43949e3a24de91d46202e868ea2f036be6f286b95c3a2b213480fbc5d044df47a41c320b94374875fe4a48ab60e0fd5fcf57a9d7126a01f2f93925b959212cb62e11c1589d38ebbcfa0c1aebdeb6d10f93f1e5d4ca0796e1ed651cafb580d58109ced7c9c5c7bb8dc8c465ef767b1104e15f2847ed984b41e760ec9054afbb9e4892d1b796854fed6b3557dd8427390366a653f309799ed74b2f177b4feaeb9abaf5d52625493f7bb154a79f2f527e43da118670296525f518864da2a4a67fcae6ca9f759dc46462f80e10675bf7d0363807af7113084d9e29f6114d2c8b0e399d34572247c3b271c0536cf5defe627de4412d704dd0d5cd6c34af75bf8d7d5c9d86f53375d26819148a9764baf5c15392ea5cf7b44f5c239e17ce871bb9f14e85cb3535c2955b9a95c5a95889a2247ad860b54901b7b5cd2b380d0ca7b9ebe7cb1b23ee0b0e69644bd03329b60e4007f295574150e087729d95b135f941ccbb9a172cac599fdd25d2140747312694065ecbea728b0973a7e710d80df23c61f1ecc9505a6aad8cf1f37c6a384b5721347e43d3d65cf847d75c856ef48c2c761a8bfcc130d907f503a0df657c39adaf2a3ce784d7b341236b3efc0e49cf98453b0f5d095415679fdce83ecde2936b3c4f70c21212d5d526cd5234e3b853ed315ae6f4de84ef1b3fb62a655df6d1cf9b3c80244566018a266f7c8b1284f98687b691531b552e31ffd4963747e67eefb9575f2da256e747c0fac2aacbfb6b3e0b02e68068676e70c4ec6875e2b29933bb081c326f94fb54c9b7fe2030b1175867dc54910f73a3ff0028b862cd3e7776a705"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ea07beacb3abbe6fa8132b805f5fccba08d9c30ea7c00d5c2e5179c71abcf659","proof":"9e16033157dcf88d0c2c267a081f21f24d9054fcaad1a0177e6a746e4a8b2614f07e697d71bb554ee35a2ab632c6e1cdecdece4ef168da7e9e81dc5bcf09524fa8fc458895b5064230d12e26fbc96389733474c9d7caf0226c1f367906186d1ef6d56f3699f8c100932c2f5d9a620dba517f0fe8fbb9d88c79fe48742ef003574e521850634cc2d36a14a81f560ba9b8802b3fb3bb31b57a633104edc0c7790a2415c870cd2a8b883a8bc95e362430a861abeff6ecd0c50124b6d6083b5a97079417488c2281c8beeab236c37ea5a69d1e154e206d596d43fbb484d250270f0d16ba59b53e2f070040ada6e2203809e9c767d1b97bc05396b622c3e129681f4e2c7b4fa6c27c33f77a1c510d640b2b744a780716b3f46091a6a20c6cb783646bb0319cc30eaa118e33985a3f89908733062c3d64677118501b28c87cf5ac6f3dee0002394d65afa624401b4bde08129de55db88bec4ddc40e804daa4331f6f0a9e405f6f3647003f437003e6db3c92990c45ac07e6c0644dfc70be7a664e6034ac1b764a8959b5c6dabc64efe13cb066c5179fdde2d97cd52096320596e1874096e9811d64e40b496f48269ede8baa99cb1e4bacb308a022e6f1f8fb55ba7b468e8c5e08ec88d2b39e757c064636210238013e3f686da8365b6c91db721bc9056c535f6ced05562b190a3211e30ee8da9f392020a04c0e4c35444f35ddd001454442d27c66b5f5a10333dff79a1d3b1175ca6ea1f46088380c04ce7efc14ad2ea20162ede9207dc10d89a0740c7019f022b14496747773664daaca9e96acfa0066765f5634acc55cc23b4d98018be4d29a48ff9230760ecea991bf22ad22801b2658bfcb47b730be73c9cad5497ff55fc43a50ae5fd9def422aa713fd59a79084d1812bd841331789f8c8233969721766e7c5ef8ccace94ef09c330799542508"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6c17158c16f1cc706d0fc30371ef149663e56bce3ae8023f27bf64eaca17ee6c","proof":"922dc619cb49806d5584888e4d61d9b0b31c7d10a5932b2528c66891b8b2557ae07ccd1573410cf7647b536eb76ee813e20e2416c94b823f98a72ddc0f2d56705eabed9081799243718a5e97a5aa98118539f3846dae6450b0cf97369a5102061abb3107c57e182fa4abaec2dbd186019aedd055a3f0712c4fdefb92c3c0b64ae0fa8dfdd3f1ff6f78c98b6e66dbf3ccd559d68db1f6670cc9f54224cbdc590ebfc603aa3ad5d78ee131f414509bb032cd0b1f5ff3e0a022834d3280eb55c60a74e35f9aa2a53d6b62dfebee70a7063eced8fea7b6662aa473f907bda799a30a2a1081111d5638728f4a4e3e157c0270cfa445bff9c7d52c05bdea281683955f9a1d76eae03ec7416382fe3f04517cc65c5120339337c8fc112c74369dac0726ce8430a005ceaeab3284f6c50d5937c978da066f702888c343bc42ba470eb5468696979b6c019cd707d081ed411d29b624c59d3713436bf340cbcc10a9519e3be02c24108f290201dd83c131938ae39507c56a840dfd11139007655a4d111f3070222713cd7d949f2e496297dd4d9d4c06a90d27c107d25b6c3eb7b2699c262d56f592d989b47be43a10d4d2e3005cfa65c07f83f37692ff351ed6af0f74347e24a7d3c9562b5858a382d295bc6bebc07fcba5e65dddd5d37d59af6faf79bc6936d08ea88975ff18bde2a3874a74b32446eeea0d880527f1e1be4524c9ce9d0d58cdc09b24edd1756f18996b5e72a5c9ecf74ea687a2a94936c30e943bf1c067b85fedbe417068e0dcb2845122a478435c9ba77e78ba8d2a05bb5acab347342f565c5c6a3b02ef0c476f9b10f9d8cd56eed4877a549fef2c322cd8ce912d7d540b8e736faf6e7aaff4a903d980dbd08747bd4da78c11890e438409290bb77e039678b4a7f80957193a21afa26816d9d8b84c9370295ce34bd3871ba23a67df00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e6b3fbe1e513e4225d9b35c94f7b997d64bab8953591f80097393f6cbad6a94a","proof":"ccbfcf888730e07e7bf2ac881fc9fa580087bb2e2d6c4999b6814ae90a99d400a4f554f2c8a78d5863c623429195699edb486ecb5658d0272eac404e8d599478d0b9ca0ad1d59d706f5da0a74240c03eb48a6a17cb4fb7a9ca791a108cc28d36f220d0e3a353732d917dc2f795f9d8562820c90f51ecc527f88ac178b4730332ac49614095596d5c369883a51ea414dd8a714426ac82f14620c775c371b4f20339a28ffaf5a567ff8da747c39b5bfdc2204410bcab01a683aa42d2dec39ddf0cd5f8ea3a58b9f5101b11e898d081a6b4cff87b0456b1a01fa304fc0a1539be0210451d841e759a6229270d2c4e65c51d4d62e8faa6937ee53b96ed429a3bda6aa827ba657a26075ebd103a29974c5195c64e2ee7bc95a4522b0d0e02f3476d3a064b7806676099653b2f5dcf570e2c52f7d4eedc476da83b1d8e26e20a54593cc670173b6df7192e81bb2f18e02a4284ab1ce7beed7976542fb261f11831f82a084e1f6579b3c4fd6d05d96b69d2f459c083d69f6d59d6fdcedfdc037e2af34b0a9e0ef8e30248c5eb395a828324dfa53ccfac8a535d44eb5313ff7609b6db438cda356d849ede9bfdcfed6ce6f109a6dfb32d8bcb43e19a2c641c289026074b544b64a6eb80ae4a4ef3532d5d64310da58bbbb2f946ea50a8d8454fcf5a4b08e488792e715a30a567f339754165af0e8fd9d59258c01875470bd8300594712106aa85c855d5ea3479ade26634ada7372917bd59cfc0addf719983b811362d4c74292efc2b623e9479dcd5f96bf46a27942e9d525a6e64a040fc3ff185fe3a7546a816c59723252b7319b338d438370e8066e4d97ef029f1a128d3200d81ae214238bb71410ee0d79eca259a41c39fdce0dd6512c81be443e92011879ffba707a75d072d26f11aef237478f179f23ddb7baa35f21a05a42dc9dfa56cbaf26304"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fcaf53eeaf08305d800127d33afca21da63bdf3b779d16dd2593cb86d1f90266","proof":"b4969a892826e06cd0f2c73567a2592384e31478e1ebb20b5377fa079148214cec104a60feb1e92e52f8c00b599b13075c27022569ff4f2b41de97ccd713da02a656ab4f9c8c8974b124f8eb8df89c2b141b8e6a74c1999343c63f57bc24d81e0405e8e9962925057528580fe77101759bb56990477237f19bcafd471b70f25213dc284d0fce87875bb6eae3d06827763b918c02a0476b666315b19d74876602b5047e25a23ac6d9cc3f134f7c9997b3384808bf114c5187a49236779cd464086f109286ce24565c3f2a605aa5e176e35f0e24d2669249e9bf1d6c27cdd28101d6fabaae26e1b219101165153c8f29020780ec20fdf7efef0620e2b808841d0adc30fc8fd49e38f9019592423070d74275b7d1500acd4c8de3a2ba8ff000f42c14537e72a5f12ea0c9139250faf2bf6a78cba2900b8773e13b516c9a2ee5342d24e397733e0a3fb6509378dd7c2f5eb7722cf863f5c7e499fbfd1b1d09895f4d7c755eb4b8a2ac44abe019ed4bc0e91758c506d4d3e3d1dbeb5a04314ee52c5af059070c81c23d986ad5f3a71c1419a7fed19555c5c8404cce86389d0b86c034c01ae56f58d651cd89bebb1cc8720b016fe63a0b81aa0b81c2b00f5f06dbf4673cade1a55ae73af35ab228115ea45687d5728bc8d5a55365c39122e428e5784f96159b420a06c4ef57b4bdbc962d2072e47b41a2b44f0c51117dff755382943de25a6e20756f2bc86c7d66a8c20ad0346399e6ef3137e4dfaebe62c5c26dd30f62dbd73c5e934f769fe5cdb62da4e799f40779c57fdc748c471cbf170dbe40419036052e0467f9e59737e35760bac5b154106048c98b82f21347ae760c03164c537a95f59ec4a2e82509b8345ceaf2180fa4b99603a6d0bb2cd8209967d82203d0a1779bb966a432e359cc86274108d75fe663a3ee3d3389a1204665df302509"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"703ad654b9aa375fb060633d155f4f9a40ded7fe9c76b421cc08f42de5c28a71","proof":"d4c30d83c57fe1e0e59e78da3a1ce8ee67c58a0f4073bfa45b446ae999819c32ecb7735b18d4ef7a4385aef953d2bafc88e428c2a5fb8d452705d7fbcc77a70722949985717bf71b667e530d0d7ca11d70827620e6b52e42213ad258e5294b1c56e8475897ff4d19ae5f6070b45d5d744b6983e4ec0ebed59491174490326e5b8a58ae0d580a43ccd853ddb6864b3338622d6496f9e1cd9a919b88dccb2c7d08af7284aca8c34bd0288c9047f4aa10572bad2132193f2a829a018ebc39ee8706dfb6e8e960e946465dd0954e9b2bb5db46c2a9b04c071188877399ca15001f04ac7597f23fba7879af921eea5fdbb0c3a2fac128020d4c01b6d8557a1cbf5a5ed88f0800d676e178932fe4f043925e203dbdfa3a4eb9511558ab8c89dc054324221b700ea241e3e9df62b4018607175b48f911ceeb348e3d23a23c31809b0c4b8e1053144130e9b688802f3029f85ce9a0cdb84f42d477bfccb296792fb40c494a6a70ad8d1701363d809959ef509b8dc168fa669bbc442436b3c3281fbe3b233e0f00eaa752b898b84448d8c5268959e8d2e889642ae5c641b7103272b7f258908f550e85f9ea149f246a0b9e258f6ca7b4e304b64380de17ca12697fefd25dd662fbb23a27c59272f81b20af47365f6fccbadb6f0f843bba513f7a3909633902ecfd6a917eae0dd4e21cb5a500cd8a94308a1a7c7756f44b1c643656766013ae6785da19acad03b4debfd619cb840e7cf4f0be5f94dbfbdc18dfa5c664813ec00ae716cc01d4c91d98bd0e1719ee8ca40128b7e8edf8bfc61a5c9db52aed40021f939bf477349c7447013a5358c9c014be98b2508b525ab6ad43f3c8f6d81f5450a16840349ac796f77b8bd82d9728a243d491a880c032f14542c2c71c2f04912661c887e67d11d11d408784ef7d8cc1c56f2a89495713a27dd4a39bab500c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b65148e31ce941b3dc2c4ed50ac3c70e49851edbf3d2960cff59343d426b2849","proof":"5810f9d7c25b236007cd17d21bf5feba1d2e19f38879169ee1c1347427b61c0dbebce3a6a77c96b3dee19d28f561cf00f817c6535aa3dd8a95a1baea6143d04c9819c99dc31fea3075e6677705064165cfb15afe1fb92aaac4ae14f3ba32291fcca19d577a99b3f6dd4d89782f31df0d49760e6cb15bcd306524603dfb88d27f76a38e3328e936860254870eee1fa6baff0ef6aa2526390bbbff28bd85c1a8049809c33aef5da03f65cf5cbe5850b930ce362b23369de265a88966babd25fc0e9ad3a0af9ea7829ffabc4d07ba5c7870cf48ef2d23ff775e7dc0cad85b4a100adcc6898637f7743a80f4753db9a1eb3dc96c9aedecca6e062fc8568d411097337acb76f8f4922e808fc20c3b30b745c3d153da50dac88b38ba2aec218e3143541264a61a46dd0cc82c5ba24194a97272c12b839af1776493c141a7365d883f24f2169d22af4d34477ed698f41040fb9960ce10bbb3b35bd41c2b81921a18ea0898a9f7f6d144e3444d77fdde9f965163a85eb92dd2228db64c30b955771f1129407330afd2baba6da0ff4779477d1917d9140c45d14a92ac3f71d2cce3f72e7f3a7c414de25bf23149750192ee0166831b422bb5a98f2450a2fa25025cbe026ae2dc40822e661cd01e3d813b2f5ea4072cf54b592bf820bd3eb9737c265f747b9acd322d40bfaec3efad503ac0cc01f5e29a759a2d63150ab23ea0a71a91bd4aeafd2b291e1cf6c79dfed954909af48ed12fcd71a4674a339ccd0d9de5f11553c67e7f33d8044a7d6b9a69c63c8ecc57fa9bc662ca47e237c76e734515f18b1a4a469e3bbf532c69921cebda001dc6d7ed6df7c0c4a36da97f15e2b9ebcb564b64997ec0ad6ff30d50d8306896fe9c61649e1d22659e990f183248e5e200390ce5ffbcd2750cef38af1e2d58dbfa4f997a59d68a113fafa898dc4f605858990e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"561b3b35fe37169b86368f5d90953280e698450b706445845110aadcbdf0f601","proof":"4483c483a4bd384728ccc0ea0c2d6ab3e77c5202f53b43b570f18abd3ba71f13246e91c0cdc76c94ef91e13d85e3231a3a0bcc2bf64d9e44d0a172eccbf8304dcae808508288a870f96f50b7fa91f98f5721b4e25f90806f1af0ae329a3e5f1b0cc5d267fe0f1dc9034299c959b1121ea31ee852896091df1fc84029822ba467c4c2090b2fa7a6f9781caf4d377d9d4f337e2caf0f94770612c35bc52ea4b20b6376d48e2c51424f26d30e86a99d3c9b9a8fe9db78aa1e69add4a63dc5d2790d89bf984e7bbe7ac496161dbcec2b4b6f9eb0fa044da5df91d7c77c56a713770902553d42acf3076f7f34593dc66b073587e350de61e0500e6db2379d100d3425624a6bbb2b6264bd5ed71b3caa87f216f57a31f2613109414d76c96f36f559675adcddd6d4ae18b88b030846d784f0abb3916de96ce2c2a36fe526e8ae9dbb3f8e703855940daa6c044b5c108d620f7718a492b13bcb51b608ff6c89e7de007c5e776717687343d7385f2db9f6fda4fa79fb80c276ba368f47117fb673edd70e446251f3d608678cc926e9c98fbd05abf010a12ae4d59cc080e35ec7796add41cca801dc7f373f98c7f971006c14b8094c762e7b6e09f8911eaf654f0a9b2c6a52ee4231cec17b10dcd7c22f8573fc42940698dea7e3d3175f589bec8e266d2674ff534f6a5daeed701e515cb611dbb08c5bad698c19f7e9b19f8db7f0227366bc76b7e2beabe4367ed264d71adc05b4b90e491daa1e004ac79e421d2e5ea94c402ac5a22403bf9ad37a26b26e76bffac84871618042c69ac7834ca690b1da55965abe2d6b67dc8491d30ce2a11b68d55a64ffb06d49c21ceac81f6bdf640e3a944c53ad0f3e555940cac7dec64b07e981991b304ba04908cee6e05a69004704b6db2edf596c66a1b0c88f41fd12648bd7a29c5776c4973de9a56f67e6607603"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"18c2c69ad2aa9566a2e2852cedcb1ddad40852fcca0ab3903b149880efdff863","proof":"daa5f560120e2b0131792f846fb4ce8cd6cba14164f78fdd9a7e615b4d99f846b633bcad66af77b13a88bcd052981dbb067f6b0963dede9cf79149399b670266e4b12b1cc8d558d2f348f5b8d8ec9bf6518adc00fd631ff7b1503a24c005be67784ea504652b8107dfa1c62cc60872e8b3f0c018662453ecc0407c65b45e63275b7b5c91e2051484a50702576e694fe4a3565474affab1e88fe7130ad72121057c4ba6c21262e150001a9a5ecdabbf7565dbc94935fecb584cafe3f390c23c0e083bdb88010372a4b0be63619a9f419ae637fc8570dc54be911f433b34c62008a8715879572bb85483fafbc27578670750595602a8a737d411c3f8b35335b92e44a2964cb4f20f90ebf6ef0cc16b103364690a57483b8a199832a349dc27f547ca50762f4f54828e20540e86f2ae1657b7af423d8e069c777949206b2349303ffac12ff7852b7568d48a3a36a3539c2f84fef6cb7415a9bbac260266bd607661e87fd67fcdda34957933880a5f83f754ad6d06b60034b385d84bae26c801e02098443fe66eb517d98faac829dfdd03fe9df6d2a9adb62ea92e29b0df21b502071e24cbd5856a78d6e09a3ef1188f508210e4e00dc973b950bd33ac6ca21f5f274496c0209400dae9f8d155a8c33bf68c3196ac1c3b56560a2fae952704d439134e5e48bc85a4354eb22d1e6a6cb600a6c80df60f03019f8726d5a9af9256362332e11119ed791aa2965f0944e1cbbd3385f2f78df59f01bedc9aaaf9d28b2c376a9029782f6a2e7735864bbe8db61fd029dbf334ebe6652e064d61a992069e7208a184d40c8e19d8c691c90704698a08a43ae0c66893387c520e226cd5c5ad4bcc19c49451261a912898eb6e1103230b7e9c922bfab837208d5f8f06ee45d401f72d7200d831aaac6e0c86316369aae35841de1629533e85de9cbf85a9552904"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c25a072c1690900b0ce4c7db8301a559624484a8860147d034746acfd1298209","proof":"244f72c40eda13e03e074cc29c551a77253887bb160df7f103fcae863d56ba47c6f4dd4541fd2a2010ea23ca4940a0eee0735bf654d83cc537a5e0d49bcbc313dcdea8e9e53c8c1e2bac3628760272c4514deb7a9e15da82009c0d857f313e25a0b66a19c355c2041c53cc5e1a8d9f28225e5689d783f15750e4a9d4cf62ba3e51d7c8c8aaea2d8661ae9a12e5c67d123d522a579ff68a6605bd4d5a02ddb00be1b3321219b32398077cff0e15d7c73b14e77f1d640b98817302866704cf2002d54f8620d1ae7c0c9339512a9984034580af232aef01029207bb4200eb38ed07dc55e4e12132c59be2d369ebc1ac1048b9386ba13d4d3b8a30ec25642255c27ffeca2e412ea7e1932b95bdd64b93b7fc27ff63e94cf494f8842a1f804994166dbeb8c4179b7624849c96b6e5c765e41e1846702122ec16aa10f3c522bea3f4295e1f48c491cd7c1ecfb01079b59dc48c865e8f4a8f8a144b89781a8305e55e76d0593c731699c905fc269148c4b3408ef874f9295011ffe6dde797a8808be55222bbe17717e1dc895ba45df7a508197aa6c819e401230daf1060ea8090c1f357fc325c3ed227a89b9127995c9477731d5b02617bfbd3dd7a25faba8051a006332677d9047ba399f08e7f0b8e419393482110bddc8bfa5d6c8658ae266d447e6188fe0988a96dfe63dbb630f2d47d019653021fa9bf9ca1749bdd70a509d2797bec4579b729030f884f14e19d7caca3701add6d315cf4bc81f6895e83fe5d6d490ab4d9d435ca087ee543f87f7691657e7ab6b71ff01abcf2fd20fe1e04ed0a1d06f3a620fc2a0b1913ed804702b1dcfdc450a1b43d5831eba007de955bebf07aa44ddb7dd80289615a04f8c50905aa7859ec001d33f8188abfe60fe352a1c3045c69891fe688fed80139270662cebc4f4f4e1a74a0e8394fff357724f981b70e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"58033986ea78526ef9ff36e287e55e14194b33340d16eacf324b3eb71c532c42","proof":"8e1df1c52979415607df87dd81321b786d7af6e68f77fc38bc1224743621313188163c1b1a0e9050b99928f63fff5398ee52eb7fd2a9a65a6e5c725292c39c6f8ccea4abcdd5c95ab20640217d4567a0ba95103094074938d403be3a869e6a34861af4cdee34a96974ae8d4e4c5f5545baa56f5c0f5f5f4249b8112d1e5b1946dc2c8577cff7ff59a1b6b30ed2867d93308bc2bcb58ca2484796325a3f82d90331f980cc874e0d9eac77440697878aff7b6e74c61594f45fbc031b1f9eafe60fcb2ff1dde9143ed3c67a6e81c55efdccfa835c8e3c990c353e0650ee72d6ca0e443ba75b2207655b329aff184c29c483f2f48af01e34811b23d59784628d331e8228275803700eb2382016e4efa986c5b4c92c28c0bb57d3539e96194b5e600962c01f9deabc3d5c674a1e497c1353e8566823ed512c4949a60241a1eebd1d4d10b53a679f7ee45cd2dcb590aa9c59ac4c9117f1754c701bed314327093fc047c2eb1f9b0f2d668b070e119cb627238ef921b6d549c2cb5d9093991689fb7a2f62ed171a964a0dcdba96185784fa025f9a75c2826673e26d692b47c039925a0f720521c304379a42ad305ddc548697b64ab61119290f37c8e364c170e575c61af2a2e0f7e178e902351d29889cda03c38b38524d82a3ce0ff0d0e0f3105c22013a59bf84181a127232c4d96b35e3aea1ed819d9da487821e6098d5b48049343802c7d959d21c91cd88b4a9ad6c8246502a6cf489e3681d0b7a2fb15c68b97d68646973677e2510a5c61a3388a37fd3a4eb22841bcf88fcb78be0db931d10800c94033e67622455a85dada8ee20488fb7daa060708a031bc8094d315c1ba2762b61aeaf7ba7ae0170a80f6e80a5f1849af4907e1f80a070c302d069be55d46c09e27a483aec7751768918aa22b12798de27de2a0d95bf677fe18a2b1f08be4c0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3c2b2bb6bbe2eb5f349d89e4918fd54f423fee90752e3aeebed600ad1bfdfd71","proof":"aab1377e03c733f83d04d972b7a6fab5591311958d9209077993d86ba2b56c3b201da42f5fdb729ed6543ac100908a91ec28b035cb6c2852b5527ccd6a42e65f88579fdf94aafad20d375a82e2489cfab66e40cd0be4778fa9070703bf8fc76cac94efc73787e64cc9e819a94e1523c78b946fcf1619aec0a2a393537256b42f8042690969fb287f43d5f4b987ffe819cb1f41245fbae5850cb9377f5d341d05e3675fe34c7913e91cf2f5e105cf750cfaf74b232065c829749034d0c318970879dd94d72a3a7e29bdba433c2741dfd7c3df2836aee736a7bc1f528e7e8f1101b0649f26a1840901975a9bc8f21c27b7b5cdd05c882c14e82ae6c2a2082e962094abbc581be3f2b8813936761859a5d74f54475a905f1814864f9f6b1f3cc471420564c820aa496b7a7c6d52df69a80ac3fe4abaf2d5581f4752dbda242510364a33572ac863837de9c8dfd9c211b13071f144c780dc385cf0e8f68f42432b3e40b07e6f79c6855be82147485e3cbe6e4e3d14d7e97fefbe6b87138a1ced597f1a290078d45b291d07011645008391944189448746357245f174421899dfcc28741edd7a50da0c2933a0ea6d2ea37dd8841e5df9445a94ffbbd976a43e681821a4865d05355e4ef2e201b83a57520ddd677d3ad966ae1af34c4136d93026293b62ae2571dc313dc19f7f6be5d668a7d1654de2cb0eff5702e42c10ba088a1b5cd26c09ccd8fef7f4a4571c97c35a3fa2c41a2b2f261e7698b7e5fe70587b6d0960ea539c2327762e34cce40ddeaaa899e0c82f12af605854941110e11607153d12f15713f89c9c6a1fb08fcef1f255c8fcc4dd63b0722ad2b1a863ad3b4120403e8a9038789b7ae4944b359f2ad4c94aa443a78ae8917c9c7924a0d08696850488e5ce8a42d0d344fe3d031dcdc2211df0d9f5a84ad28f6e3cc083f6f872070f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b660b24a018fb20f50f9775bd9f9fceca33922063fbf4a25308c9b1f38c89f36","proof":"6653f06e621f6ac95986aea929c63f0dd0225d7cdd313314454c5a97698b6622123094c56570974d041c21c86003352bbd13bf310bb709e0a317509f3fc4da5aea0bb481aa8b14a11233e27a4d9be03334aec58f797e40d0cabe72dd9ed81525c074a2953cabb6caacfad01ddff416797fbcdc6b639e81cb7c5f0ba0feae671df2e8db7e4914707c4cd74352939bac6a3f6b579d9ae7bafb55fc7da07ae358006e74ae3c37461704b7cc35619378170d71eac01e3e5d7111b2c67d5748c69a08691c0e7382fbe13025fe8f15e60607cfbe2e7b4e9e3d93362be082c4ea0f5607aa99d853ffe22617c4ed69e027c443bb9d2d2cf505b906bdbca5e854a6f654591c914769e57d5bb6bc79791bb72debf93aedd4918b681399fb2407e014295124361ab96445abf43e6231939c583e99f9717d9e8d634a482d97461937bb111f57a6fbd3f67c02ac743f2227179ff91d94fe291e4dd17e08f3bdf48ab1b1845b03c072032ca0ff6c1ffbfaeffaada3ed84b2ffb5d0d44308e68fb9b1e79fe9d05dc257a17f498b7d8a59806fcad3bd373f2ad346804afa389c696790c7b0e9d368781696025ebc6572fd32e3238de8168f52a5e90f309babdbaeb52599f105fe372c89a424ac9546dc5ab778d40a8b222d3428d42cb0d600fd56f62bd733c4975d308957d3d7bc8f15f73b1c87c2b11a1f45ed2f02a05d31dd4c1a30d5474ac9445ca32c9257c5f77668a27a6d24a6fc90fa560ecceda234b7e9e23cbbf54092546c74e1959216de57b505dbefdc64c6f543c62894e3b3478ce36a058d767d4d38d8e1ad1886cec312d4f93c98f9441a763d358eac9f7d04e946373f79d870950679f5011fc3af367b0a3f19d2c3458a3fe34b1dd93bfab61063f2f49a9f4b100cd125a0da0bac39f3a8a35682405fd7b99af240b68ab2b9ac9f48ec112b7b830a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c8f86d156146bcf487f5488b82471ffcdc14881141f7dede6502ccf78f68746d","proof":"62cefd550d3cb35f5ce0df7c44f26c9d03963ce726164972b8acaf4c9c294849dcdd0fb14077f318abd6ec731d8f3ccdc51d05ede4019208c12f949a64e91d0a4cc871742dc533e141042b2a4c880be6fa902412fd239a0ebd7c7d5944ee145faaff4c33dcb9e59a54e4a980789d33821fe8e9e4f5ba013618a7281ce2f3137d7305089f6120fc413175b4b705dba65fbcdd7a9a49c36fe7e76a8034ebffb80c59909d531350dde39429d1ae76a0513686c53ba183857159792be32c485e120040f3deed933771a9044c3ea7bb63f47ee5bc70d20be8f87599b3fbc0cc010b02b60351680880c559df24c093da4d74ae808763fa8071a4e3d86f1204ed28ea0c189d1c0a1076dc82bcd6b3944d2feb35d80113631b09fbf4a3a7f3cca118aa77d41f8b037c33bb7a4991ce9a6d5b1905ab5aa86388e10b5b05952d3bec15bc35cc483369a434019c56f85bb170d49880a0cfb10e10acd91a3b07d24ddea04e492c66a937283f90cb21888d382e5901fcf1bf72e2936e392730b13309038fa333fc4c2e8eb89f5a906f88365c92a23dd8af5ec49f59357092f3e225fe41aa88208a7bac989e372472221cb8ecea7df1f009874eb69fd723d72f63f79145a70c4eccb5643608d5008fcddf23359afc69faa8b25808c1ff546f9ae18dca94e83d6dbe13ede412b65bbaf2356e8b0b589f0439ee78b0c8100791b0f7d42d62178b7e96b1d5078f49cb0f27e91398bac2b7d3cb3bce44433bd4275ffb9cfddca5c51986c415ecb755303c06f8c5f7319f8b7212400f2c67f201afbf6807d4648695483c6042955e299a8a799cccc93ae38de5874459a1b1fd8edf074eeab0131e2c2272dbe21219fea4bcb6ebd5cede8f352df3726fa79a7b677cac48c7bc899bfb03235636bb88ebf633268cdb5d391e7a8173966f2f4114b81ee4202b03aea2b409"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9a4915fe47bfed3468522a4f22bf44bdfed16562983344d6f3e655a64d6c3552","proof":"deb01f6679a98dc116d5de7a6a9c3c10f84f76b97f4a9467a90dabacabf82148080bf3c711dfc5ca91f2066c06897f8da89e7d7d5d36ccb320a6489c83893c4c56532f640b287ccfcbfbd0dbe7c84ecd84da2ab23c5424424ecd18eec1a50a7bf250713a7e878ef4c78765a432f629af02c999180503b026478e32405666a04d2b7f9b55cc8fb58b7e7b6dbe5367eca4b57471af0b68192cfdf71c54913ed30a3d1621656930716c1036fd65ea0421f9cd5de33af6345f1de92790b35f58b00dd3cb258f40d9810542bcc45a8c6ab1d3c945a75f43bcc54604d458bdd3ae62012a9e7e7822a2324c48974a8339730d8c6ce5fe69fc558c98feeef2ee125639632e94b00ac803be1398e15fe104bf8b7893edf119b787d09489d2b430e68625658e47a973b2587554e26ecfb1be4f80cc16ef762d18e924ee7748f2f842578c65b0609003cfc6c10bdc847b4368c5cd4b315094dec1e851f3cb98acc7223ad35a8e03ec147160e1d0e727fd14a99966951199ad460310fa5a4129793823339a49ecfe9e781da14685701a0eb6290dba0c05d4109e16973fea03083bbcb7606240bc849f798db7ff61c1c2c1890373b3ae2d4bc8b70cd684658e05eabe6d3f25424e0bfcd94b9a29cfdc86a1efca9b0b0f5001b93048a8b04709786312225bc931769739d333a03d4debc939db001f370a1bc814ff02f25c7ae865805ade95ae20302ed8e9edcf27ba2314d6d0dda1fc10f211c82ff6ad2d471e2bd25244ee3217c8b2d08441d711f770dc8094fc85dacadf9f75be012dd67803065410f6445f3d04bcbcbf3cdb7f836f571ede400805cb7d49899e480439292e7be4a75d00404106e0e8b40f4b29431e205e3e8e80e6037008326e58e338f9467addd391bec101919ec4e7617f7de0f004281021c48d2bacfdede027d617a3b953251f20279c02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d25283b1c5cfb5d09434015d9db1f87409a03041e7facf8092f3cf23fbd34d0a","proof":"dca5cc5315993b6b547277d2e6d79fb653636ddb699fed4961c060d93d69b8474c8c35f2f9ef2b6a7b0a81e6fed9172629c3cf2f2087eab2b5a219b1c5da073d58489d0b3ceae1e52f34482a60174d538018dc35cc988007cfe760bb8e70a25f96f2289b7cdf60152576864a36ffdac1c9c872b65297eafd2ec6854771dd392f1ae57d0ee5b57961841448718bb9f17be6b0855b67b677b6ee5543be59aef90dc04441173f5fbfab51f37e43a530c778df0a7d6e0ad9f7aa0c169c1a9ec2a40a73d1d2fd805527d8eff28361b04fdbc5489cc20917a1ffd17a785fcdb4eec10764de28aa38e6ad943d46e91d191c5824ec75890363e3f328d9441d3a984e8c46ea46b2cfbcc8781eb6608a619458e0a159cab4cbcdf8eda9489afb0113b4437be60f5ead8841086ec7f3531200a8c11c27aa7a3a527c9fa32bf0345eec9002245039c3176bdda7ced914b1a1404a1ba90edc56b1b56212d2b0234ccb98456445f24d9f0f0c1579b47398565ac58af5875f6aaa789f6751287c0f341b08dbf6134a30b5abe07a701db8b27c62e6c641ac55cb7f4cbfa43edec4aa19a35702fe1a72902024910fa334e5b8163c348a588b5fa7a8ef7534e5a3c586049338b5a272c6b281cc70a0ebafe46dc138fa4db2d70dd361650a808c7f2da51a3b020c1a42444963a2fd443b207e634c8049bd5989807875994cf5e04c2f5b72db5dcd1e5464aa881c4f36e3f40a57f872b8dab2ff9441616e484fbdfc739a48942241db4512a3ecdd1010f7dfcdc6f5c902351ce6eadd8f3665831d1cbf4e31a80ac14d0ece5ccb9e194b5ce36bd6698dcab9ecc912329d58c1ffeef71cf4d7610021de77c3f1560dc42cc4afd141b932c39366746f63e3c56229ebae0e6cb10f2149050f110ebdf235a096988b4cfbbd9dea903905946a6c54b52029e46d69bd0d89560e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7ee81bbc195dd8c4589e5c01a464efa8844d22b2d5de6ec5d06b9d316b38b85b","proof":"06b8b55bab6e485ea8d619dd1f1ba495abc1cf7cb0c3ae570813cd76874f70798e86b3e1a4326c388daaca62f53aff08143ab6a04b6a882e83ac6d9fb8a06033a2e720c58fd4dbae0111cebc35c42846cd74772b480c349c27c2a8d6deb9a47bce3216bff459508162374a7e85c6afaf0de7f64c536ebb8f060f34519dba083627c2afeba5845d8c5eaa78ef55ad3af313f75211b7b44c864e6265d904a61f0281fa077275ddd4eb348141d65f31884d205db925ed33a30efc32415426abf10ffa6f76a97c4a8a7c4abbd8c07bf7e04bd08345f6da5c57d3e370deb47da04d0e8c02bca148c5895ccd48e2bd2724aaff41cb1d2165e986df4a0f72497d5d1464825bd334f6894ef59bdd5435160ef1c7d5f497a9458c944bc6bd685a36c9db32446a1b651b600be247ec99dad55fbb06c0e6ae3595f193cd40f1a9fe1bc65d4858b7684172525f8f0af93c38ede6c759467c4b5ec1e43c23d277a92e1da91c12587f8035cec2014e3a3717cb097e1c803d030a7d0c33d6343fc32681665294317c50b9e2ffa72774da4c1d8aaa867011a6e4c824cba455b785e38e92184da655668ebf20524ce22a502ae6aa858be3e1a89760d471f27b6209d520ec660d9d2936803396b79ad2d751de6b66cdbe78e515e26099c40532710cd24df0e795c00a1c1ad9750185b3be72b295ff02b50c672bb9e47e87f2fa4cd5d5a85293e2c21c1a59a1e375e129a657ff049cd84246883ed501109a339876af1e9ac5fe04f511f4864f9bf0bee36cf81a25958a9af7e66712ba76ca21f4c812c1abd5fc7492213aa7687a96587100f01410a345d3cab24e8cfd3ceb62fe8944d6f2579634584b758975ffd4622bd6406f09f6cb79549c9b0e426d1a873382ada17a6d483262028dc5aff9e045426281192be89b58289cb30211f952504074971d2cf08895cb0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"78c2bca2b03f79fb2f4da6c1c23a527116125a53f99c1b54def3219c135de01e","proof":"cea6c445f3b160819d6684864cd5187fd085ad48e17ed0a7c47cac5f8e239a01223e7ca1bb8e2fe17d805a25ca72eaf33da7f3b49c941596609a45de8e334e383ad5e1fdff06ea7845ae993170f72226f0c640cb602307198b85da8f0f90f93a909c9188c7ad9de39bceb68c8235819ce8773aeeb5a0764791b6b2ee4019147963cf1c28ecb0f2f1fc08784b2251f74326b747853520fb1b9c7fcc91b33bd10b5d0f94c37df92c7b600a0b170273e1467edbe346363791b0f06d7988ebff99044a3239304284561a8899f749f5684a85779da42327be2347d77df600f06186014e4a8d62bb3c39a5fd130cb9db2130e537ff4975e5d871a255641e2d5ae87a36824e0b6f74a6cadd93f1dfbac50029e1a8f1b3982c040dfa7c128e45a4263454b0f95bf8fd0b29d6819a2e21307e9820ddf538b93745fca5b8cc1d3fca862a38e66af931ce2463ad54d314a6b4da916a143144e6697306ed27407e3075f889088035001337522a0076429220e44b3d3dbcb95e09990a0323345f1606fa86c7677c65a5b472c17087d8f480c44d7c8b547f8b16992f881dd52d075bfb0bffe54cbcaada1682fca5361851375a273b3e0f03083d4e15330e694c3e5e2ebd0c07091ac76eee7a6d2f70cf58756b8fd61012e7d71d94bd5d0129ecb13d59b58c7a2f70489b614012d65f863db0b76e6e39cabaff84b31bbc6a0b8e6e2b64e601f678ecf51a12d271e2c1312af8b605fe040f586b2165af743509570b520859a87a7eec5a396c62e698ecd1bc4adebb65a36ba26816c8ed8d0a26c6c479d335a2902cbe0aa0f87c4ae49e569fe2c64cae90f4988c29e6872b219c9d3632432217bd38a2b487bc3ef33ec01abf97c8bcbcb1371fd036cca69ffb30153e2b0533aab70e78244ca7745e179be9823a391a297738c50590b97abc0c1729b368c0a1c3c808"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7681ffc3d37bceed1d028c2cb514a4aaf8cd3bf2651bf2fc70597152a7e35144","proof":"d0c6ea9311e46cbea4f0adb9f9e835e9f09a38007eac388f671ce0563f35072b4c822501eff51e15e5223d47f5413557c78461fdf0cb6896765bd33dfad29a2c6c5753d07d1de52e2fdd2f43e8dfbf0dbb409d413aeb1c58fb0bdee60948b9378cb1d96a89cfeeca157038f7aee3ed06c789ff2ea5dc909cb20b3d6d8a0ca54bbfafaefe6e7b2e4aeafb1e09302830fae315be92ceea03424450a24219f8290354198ec5f42fd5d00ed0fb1e17b0bb3a88411084035f3a681ce217bd39211c0a722d41634e2cf3c46e6f2ba1eea294edae75fb29c2f49e5b1ab1f4307c13440b1a68aebe00a31ba90cac2f65bbe1a6f27a34271fb1dd31b6f30c82aac831322e54878b6d080d9fe52484a09046fbec63766a69921da29fab3644aaeb6f871e1bec8dbd5225eafd43d2faf97d10ca341fc1e820ec04cb910bff7c845d0f796d6d9cd6add7227380630306027082d96ee1a60fc0ff11ea1816fcdbd8699d2da95ad44d8dba57a6932b1485ab55b2f0801d3579899003a40efb1cea8ab27d5b9b565667450966c3eeb654a0137f0772f497d2ffee4e30cc55908fe4a76cbb56e7368eec4e5db2049246254d0e1dc591f534869dd90fb40fac54ddbf53c7c5688a564e15a0b7617414dd62e14ffe109574c149e8f0887344331e8cdda941b78fb86b6a073d76a86b5422e1ed899db9ee6bda68bd122a21567c1d3409397c5c8dac2f5684e59f3420f3b87906a2be064517513a50eb6e4896cd223471d4fa23ca291d4eb0cc0bff54cf8288c96fc95301a69b31e8a884a99c59642d3aee4702fba14832f921c0766c3b3c5a8de50c68c1adc696a48aa24cf10c3a094484bf04f6992fb55ea9d8341ae3a3c56e2327aefd8048a1709d30aa7242f14e9a5c608162d0053b0a645c055fe2717c2e4b08c8eeb35ab0817003a8c1722c7188637a3761f103"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c20bace82d06e1b5fb42cf8d071a8682818a7bcca2ca32bed68ddf13f2990a19","proof":"92dd088a3abb2b57ff94efd7556ef7a7b17121f7a426c8bb5e0d87a7ec381327b809050c944149f118faac87815ff155014e49d749a4e8d7dba60ea1b686db2950d553efb2d6b33ea8cff3199b1174111b37d874844aa3ea44c24e4c4a67e1772877373c4083c72216bbfee02708a957cbd7acce350b7adda6dc627dac9cf877d9d2c098fcaee7a36099746a1538daa179606b9d8d0203c0b7bf44d5945aa305efc7a624905cab1da7743cdf0b980c1c34c15ed120ebb5e769323447b8b01306ee8a023cc1c87c224ae3fb76de94b0dc44e52409d26a9ea52b25087efb400c08645d6a6106dec72cd055eaca7aa497aad29193769833cb9cd8c4784ee0419b645efa006180caa5c185c8f43d6c906686d56614b4ec6f63ac07d416344c2646346ccb64335f55bceeaf7cc973cf4b644666482792455367080503d97b770c3131207e21e2a3b4e940660de79b2a790498ae24a9cb1c9a766f3c8cfd517f4c2018a636f21abeeb2dd65a359503235855b8ca955b174ccc30d956a7e9cb2bb97b3bae29b735519f3f8474a7ad77674ee60306d86a21c2246b203841b2427ed3884a5ae070ed2f4e413bb4a42bc1140a78791949a2828bdf0ff25d7581c1d4bca532161115941cfcbe5fa6135d461273ee920f39c239f5aaa88da5659058fba9de0f9820130a61959864bbaca0f46f3688491a10d66dbc32678835932be43ee8572b0c5a0266bbccd29f4e12161da5af46e59093cca44b25af12c9d40d96bee16266da7358b7c349189d57951293a379dc3f16fc936bea789a407de61bd218f8fb67421ae1e2f16367305586e4b67af8c0a4ab393e52294c7abf4b4a5599e7351e0f5b98be617492ab564f4956715ea0cf7be4b23b519649a3e2271acc02052f5c02e2d574df5ea83740f4311d948bda2e77e3582467c87060b79b9a0b0a9400c50a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fe26daa0a4071815db4dad82e6af9d4e2bb37d213db45998923932dd13104649","proof":"8c329d423cb328118e1cecf0ea84a542e18b2bde539314f102bfcc53d3f16e2fdef96e14346aedd2b04f56f7e623cdda255cf0b2fbc47004f0f82c9327e0ee57a24873b5f6001577235c8998bded53bb114846ba7fb8b3d58fb7d31c6a899372602a7a6051c835212ec82a95e5724b38bdf1c40ca55c95fd310370556dae9358e4c810eb633e97cd32d5ff365142a393d535268123d94ae9a02c999116f9090718f16afb5b51007cb254876480fad00d81e22dc579caee9de8069bffdf32e6036b5c313366fb860558ba41f888b4095797c3754ba9a1154f9d907e91a7b79304f091dd62d2d0ab746ed9c410231030637b9516a31007e7686312d8afe0c75e4ffe6d19949958fc05032cf55bdfb20f1948f814f11b8f94cafc8bb1d956cc6938f87a86a8bc30020a74e318c6780c163add0cf72a6f8bf2500aa508caa049b63070379528f9fa7de6229f7ed67299f0333db8d76d4e9b9fd03e7312e95d986a45f8b58a5d02a5111deef89b1b5d7495765072a8e3e4570a399a7a3af9876d4d1896fc56eb390f0f104e28641ec13c9a4b981ba59ca891d13bc97e59516d43864ededb914f39ca3707d05d8ae011f4c83cf9ff4f539cefc55266dd1348c3f96e40be5c6d7df52753a0e045062054e40aaa0cf1d8f3526e19605e7d9b9596d9e4416413774e7b1eff3977507543e6147b31ed4fec04fb7428bf9b0bea13efb69954840f1f6a6ca306b4da74c041e39622cd45e48c171e0bb7f13563de13de88d6006642b02794fb3b588b5f2071d4642bc561d1fe219be38e1686e0e8a82a89ae27884371ff3211d9b82a1535b14f05bf69a3354d0598c337c312d5bfc7e861907e828fdcc9cafe196a22699c552e187bf1c29f460cb11bf18c37524b87aabf8f01225b850294b6ba185a3f622a4ee17a972dfa2229e3042bd2a9a0482e39c41f02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e6cea8cdbef2a1f9fab274c52a3f343064d9a2cc89c2ce547feaf4c243adfd55","proof":"90fa93aeca5688ac6163357c3aed973794751a5455f9727f24f79848a4a1af4c380c653ed72e47651e9802cd029793fdc2edb2a43355f0ff6253f81ee7e6b2586ef904c25eb760062e90c7b34b870ab2f3746b69dbc62bd4709e01b953af117306fc08299c765452667f8c819584424f720b47bab4a3f94c8178f06b6bc711073598aa8e02f86b9a1580db20618fc90c92b5cbe2334de0d03a16b36af9b155013ccae37f54f36468e970fdcc8edb994018e3e8920586f0283d615fe53768a200741fbd368a178423e96df88ab7fa47882f776ae6d79e74f1548b1abec07dfb0184daef9a42037a544a3025c238cd62abc2d0417efc7c27064b6b1e608eaa045d44b611441393e60b0f15caa94bc6851ecd14d1c17dbf0d796d2d690cd6139b43c8cbef65dcb0357bc704c4e9a09bb93ad5c5b0eaa61254ebebc659d275c19c7318c8f719173088fc787b748e0f5b6e9b6c4fd82d2fee56c023b36df2618dd9023a727fcea37978867ed7705a9738adf2c76305c5171eff00659d54cffa869d2d4c4b6ae2f77f75f7bcdddf81d7c6ab90a736c0c10601b9c9516ffaa9c5ad5f53dc5fb25283e5ee8855265ae9893e2c744ceaee6eff6fcb85836e3ce160358870fc4fd0a6a042ded28eec63c18ab40798c8212b98145b6b79fd372aa1dfb626051ef8b4bda4d563694312141f10e0da57f0fd2625a73644b452db9164679794332c060a5544c2830239848bea3fc7363d7775b32dc9c32c92cd52809bcc941c5d7a3eeb3d33a685cc0a639f70d88bf043a08cced818d90ba899d5e7b8f9608554dac82e85f0b93d4965eb444fae1ad484b362c10f22d76f2506083caa4b6c3e254d7ec7f18d5781d20aae7bac668b31301c06bb5a76612ecc5c5c5adf3db69403662860585f7cfc2fa2c97204946ea0355e46af7942ee3e9da930f8c829a49f08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0e6f38b02ea77423d344f1b41df258b8db685a11c712f8b91a7e9c8aef7eac73","proof":"84b054fe6671e225e0f0c96b3131f604bcc1e1e178edee2a5b314bf49abd7e30be793e4f6839c5bddcc2b044eaf02f7dd9bde2591d7c2dae97443c25b8b8ad53220b90f2bbc4566524e5d0daf7cc393fef9639c5d672c47d039bb836b437d33116235f918a239b5010a5fc07c0fb941e140ed65d82d165306e7cb4a94419bf18e982baf3d5c4c08a44f1b28750c41f16ee1a2ed155f98e2b72a599956fa0690c16ad1ed7e08e6785dc2ef70928d600abb17034f7dd585ec9030372ea2669b90a7e89c4562c433211522c4615b915cc4742754c2f32b0924b1e02b941820e8b049c34a541ea46282dc9c21727b5d285900ca8ae930de3b0a2039850798a68980d10500e9456d0306b36115ff937352f13d9dedb636a57e117bb56c87c25210926027766ff0af89961ed9f21ff433cbcbf2360a0eda5971c5d1c192154c49fc0018a2aa7c8a0f9f530238fb193b50cb4436216c6aaaf4ee3384a7c879a9376a11a8acc1032ce0695c4cf8aecb45e9382101a88646d5c21ae0ce00b99d5aa60465f3a1c2b90e446567798714de8f415b844b6cd6c00009b7ccd842bcf10c30f31797a07761d2357f7452608e7ec2173b1b835d39ae42af6223528869343076fcd0eec0bce1d8af9f1a306b30092613051a4ae51486300f49a6eb8e6fdf4e08e316a7484f5d2e4aee88b9046fd4632340438a1a04866e3fb769a58ce5d3f0aabe457aa540c6c387d67931fabf847b4a3426052ca85a5e4fbf988331ceb4120f2e4526a7368803b7ab2abbd90f8cbd158a292d876bee8ffce6cbf936c0ba348154f55526c80656a1c7b59121cd234eae411a59d8b8aaa5f47512b0ed801121f097f4d1e61c34f583a9677934f78c6a5124ea59ebfd4195ad5c0b4798a2571d08ed2074cba889c5e520e995905fadb4d055f41d2e4a9703df9fed57c771dc878216303"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f6b945f7dd8bbcafda50c132f7b30333742e0320cff521bc5d506f67b3552056","proof":"ca407f5e72c076dbb13f0c159c6e53b7bb888cdefd1ab7bfde5a28a1e46830491e5bdd6b1b7b33b8dbd6f605250ea6f21a70f2ba629096a63374c6d1546a3b564a7764c494e7746fa57fd6d3267ea1ec3af9439f08c750a30f88e7cdb5e75c0bf086a601ed4981a5540b6c33886baca9ce1078638dee0596d78571368a1b515946fb37076a24d4fc7e2ede179426e91ca56fe32ced8dd4c8588da8e7125357029c17eee515f773178bf473b5e5e36673fbd5c100e463099b199c8ff9e42e1809e6b80cf07c7a6cfbe6c9231163f932040e2301c550ef2278eb52b3cec60cb509523a448286a72a2b4294ba863993df5f7d9a7b74db7bd62440e2688ed6e21f0dd4dfb52b933983554a4c760603ec1dd3981c4917d2216682a9035b598010fa6d68fc7c1fe9973a981684d728a4a0068ccb376782da6aed6d2c4b39ff01faab648099a1677676cb7acdc5cbfd22ee6ccd2a31629a4e8e0a1055d3bbc93e4b4346c2ad3df63f070bfb4d9b82ed25dce51209f3e9f3e48e462a94e66fa83647d9489029b02a24d0f7d3895241ac70f87e768038553f5a724d08c62ceadd1c802075eeb4735bf15cfa1207ad7d8ef075318ede0bceb9bfe94995bc47a52adf68cb233c35aa536cf36a20cf280c00b45c3ec1ed4d6f0494cc0f9d1ed9cc23abf7bf48b447c97c0efe14ef46a340876545d0e0ee201ab5297aa8d6b38b06e5242d9d0740dc74892386bb9f9ec790d49e9dec0a5b62cfae64ddd7c7db0848dbbfa7245b60b83023274c13a35c43cb37451ee0052b8718d1aeb0471b3bb7fd746f3d481d822878d4a39f1fada1902f78a95dc48db43714e61d5046dece5d5aded26e857784d7f90249a27301683fc46d44d6f5c8bad3f9e6702c6871413ad4b6fdfd890d2feb7a842e9825710d40df30a4a2f6f4975669c791b7081cb0bd50acd1d92708"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e6056a9d98e9928aef08194c27ecdd726b3109661f44198c4b95565440f8202e","proof":"fe4e90e2c2bfa24e119d660501934013cf85c2203f4ff75f83eb620222d918084203fb2fbc361e9b6d621e56848f5427e048464fb4a276742f753857e4cd3a6f366266f59087e6299acdeaa5db40ed50bfbe6ca5ea53695550ff214a122bcb19a2ecdb2c497b49fe55f7586df9a1918777c4a543d615f69c057eb166fb35980dd23e644548d7749d5559c3fce97d8a91d8c332c2bcd8ed0201c5bee7fe5a990268bac5737e196f838a838bd2513064833c7b9a06ffceb42e8cdb6a0e1b77160842d30c768b0c4540f11f92b254e50e78e31904e4437934f92f087b654febba0228c1b2a3ac3fb71d7909661ebcb58f2c0be188729f627c09a5f966108df84274c48d339d667baae1d3f31680293c4a594818dcd8d9e1e8ab5f7fcb235ed0727bf8281783cc75d4604acd617743a2e1d73860e9596e839876335bf01ebfbab17912d7c6829ac1ae7191dbe0bfd6f3e14de6fac2240e75c277c114290c2aa8645dc6f3e9ef55e756a0a5574f7495a0bb3340f283d1b058e8b1968f7fe7fe1f447364e4a84123cfc398bf28d0bb080be40a7413cdb951550ace49e2746248acc41af858c08c404aeea991cf65d677602644c071c0b2c49ea820090b876c5ec93c72dc0bc27b2109f4b3edf4a95cc79da761d65c047af399dab5e9c8829f41e5a737a841a041f26279e9b6e6835d0f220cdb1b9a072c3942c450a2f2c7ae150dae20b8551c13c130561805f1b66641a7e50110c62cccbf4893d9b9d19abb639b42717ade5374f36d6f8ede96948995c7735e3cb3b2b623de93beb64e96de15dc992f5233abf5cddd4bbe6220f760c676c36cfb86392d8414a2c3978cea6c149b4a74e5109b3da2f6c2b25df100cb1be8ab71fabff73df24eb529f91ab1dee980ef0a5e60654957d8ba56a3364f29735bf673846be51c919a6eecc4d4ddbe8e12bb00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"00deb097a3439831d5dd54da70e29c400807c5811df37f2cda33eb5ec1e03363","proof":"d481bea95470a25b26fbf9c1b6f439b629b9bd2e38d39ad78d49d00c2818a200260415188d22cb75174f1dca26590ffe677a066fd9d2fa66bff9e0f65c0a6d572ec4d62faeb29ee062fd1d951c25a083239c37bc6d1e64c042e8fcd0619abf4964ea91cb56b04b0c0bdf926b438526d20287a26dab1c7a677e4ca34657d11a25c9ea68f6c129c209baf273f79cbbfcba1ae8fafa68f3880cab2780bae9cae9002e297aa13afc929edf836a9512645d40f3639bfe385862e5e7815ddc96847b0f2c60d98afee3f20eb5aaed88c37c3c2bcfa3efc97874ed4402f9eb6d1390d1041878da798d6fd33ad9c1f7958d503c315e11012cab6d4e3194d81102d03f5d4c94df25a6336487a2e13828ad171bf5fa532cb01da164f0f018440242c8188d7af407b056b32be226c5e4584afe9066122af8f12064aa284f159cf42ff048e52a84391ec29115923c47f1d7b4ab8e25afac971a7a6031aceacd296570a65d1c650a6c98d13831db41df9fe95ea7f15c7f532d5a2df708e677da6b2cb35843b83b16936cdfe36c80a55ea5d9c3f31832d2abc3d6aa1e7dfc090d98ec50e12ed36c1614b9420f08eb587cd74af0ef9dfb57312afed2d7cf6f6a5ac3295eca7dfd413cc5b0b5905633a538d3cc040188ee78e10d2e80dd463a45624e79a3a3db013dec68cc5e2b572e59d740f70c2ade5fa9b53bd45e17993902c1e6f0bc3d19265f249b47131cc93a380aa301bb69b7e09db4fc08b11bae5c9a21022684e3073514bce4129057a0cf2dbcfd080a09a9c78c5b44117d5dfb9fa5055ac28b8b1365760e0116ec8ce6f72df2ab146f1e83a9ecd0301168c35caff4eb67b677623aad172ec2818db7a3be8451243afd760d02d4b95fa4576f1809d18ae04c44578fe404a635e3d712204da7c4cef75739c79e87f493258aa842e79fe1bd0c852193f40b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dacdb83ac9009d6fbaea1911def555fceb959e772f59de0dfddf5f1abd829610","proof":"5a5e5908008bcb37e0cec54da1593cae84b9b08170fb4478573c9441d59fff04fead6b6ddc664b9364a23b65d40edd1234e99ecde0d5ace40c1e5653b7bfdf2ec40fdcbe07c1256d3ecd119ab91d0b5a4df0234bb8891c4e763ec02a2381451a2c3ec902b4b6b9964000e864a5783da5cdf06c589bac5db83d193d4a3b2b9f4f6d1c7d7aec8ad437b18872809a60723e015b2f4d17672b6d3f6eb76c3c30040ced985c28aca05ec5e44973eae0ebc6bbc404958c762b7970148ba56fc9fefe0f671859aa7247c9ada1ec6ac2017de6d9b524c07e0b9c4582143ab3154f3251058c5199f4d03c2a3edf0907f6301907466b3540846cf2d0f96030e57dc9aafb4330ee3f1cdc3b4b2a94331d8ad9721808d4b093fc04062e397ba14a80d063876f72745daadfeb9ff074dae735458d3458e3ad855e5b0955391de29df73e5f3a019ce2d91a32067cd503d22db7303ecddc1e4dc60aa82e707ac9a67a9f95223b5306a69cb9464efb8200e57c9247d37315be48f090a267036f8ec9834b9e195012cc7aa78cf9024d662b697eda05774b376c2b2a7b557eeacb0f0856f193287d0d1445430fd8ae747854fb1a182be332760e8d75ded29505b96bee314ad90c985bd43acb5ef7a7b904b9df0a2a08a5297d709691e748117bb456cb2814a8666f22b09a37f6d84c70bc3e064500e0b4d0dd4717de05312abc730bd8d259714f8e5fbe23e73180dd67cac2ff388b99e0c89d110fd2d3b6ebb6fd95dd63e5fc120a1e34ffdd279f51d88a4f9a23c3409e3eea41b82febbe3faef657555ec24541b5465edf7323bf6dc28474d566e929089cc765992e8c1653fb87e60278acd74e9c2441cc5c693be448ec9d7b7057aec6c34d89be6951d471e32eee9501ff060276032d478726a55d4aadc58d33eeee5cda58fa6ad2254702eacd0a88a45a45849e07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cc75daa0040cb9df3f809dc47715de7c584868d40039e728f36afef37783361e","proof":"b0e6209c14dac866b9c2ebef1baa51f3f5216165edb3786944515e7cc60f4e65320f6fecc9ecae5b939c6f05e7fedaf196a9b4552569cd5bbf2d17a398a5a368e8cd44c22e2967743e70fe43df5d83ce7c974abd7b2af49b58a414487806da515623784e648b991586bfdb4b386f10d5e1f548dc7ccca3a05c2c06d6fa3e4d4badef919ec53b515d32b0f7c8b973a91586149fa52780d1a696690c4bf11e7b0a5f4465bed558148eca712b10469fcb3ee6ce53ebcee31b47136c19e727b967000211472106634658f5555e9c2c371294c94e02be7282fb1228c57c85e7edb80d9423fde5d112c55ecd5470d15f9d465231a65189d58185607f5569c2efe52628c83f341838bd3a38a65f1c287428652609243be7b1d7308fc358d704da4cc0638009bbfe8074577783d551b17b2d4f97872a88721b02c04f37d123407539f1142c5de0228b3943692dec5fa32cc2feb149b318f915975d61ba9363cfcbeb6432385532314d1c80bdba48e6e12322eb7af8ac4aece9d0f6ec5b264407f9e59065fe360b84f6ed174b5e3735dda8f532adaa73de4f934bc7d0b42a904d988d2609aa0892cf2865399885e9507f681440ca20dec1ebdf57bcf534dfd5cc5321ea533ae17418fe8f75e7470017271329daa0886206489b96abb3a0527fc34ed08b6fd2764ac2f109562ecc345db90864a8e6cd95b839203f7fe9e46eba62c8e34a7a3246da98cee72bd9725f7a9d0a3a5312abe34207539320541760d3734de6bf2a6ec79997ff806d4c6f2bdd0799b9c9dcd94d60b1bfc2a686a684534b9629547a360ca0f520895e571e27593231fc3b53e8a1556599ba5d33193d90424230a6392a2be3a08f30ccfc93c770b904c70840960c537c46f0ee64a91b9f4daa571c027ca1760af43bbbbcf37eaf3b818edbd42d89014a391e7273200c80682b272e04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"82759932475b3a50e6dbacca38eb2a05d5589313e992f4f368b24af190bf9f1d","proof":"680f7e4f5476a1eb9439283a78554d927c58f59ddbb2bf8ca6a05e4aee3e2a33c0c1175a632d0917c88d29058c5e8e92d63cd5bbbcaf5426748ac2178524bd3046aede2d9c8a9af531ac997d0c1ad22a9a5033498f956443969b6749185c7573f0b4059167caba7c8a4f39533e632701e51d6d62e5fb0fc8fd643a3111f431743cb7e835d675e74558d8f61bb4849940740eb4eaf4c153059bf1268432ce7a081e41a9d18ea29a8ca7d2b890927d833ad930bf2be143b550c50461cee6592807fc731022fed12eeaadb44bc38da2578f6a1671f9df10d0e83a5624a3d1ff380afeff1580ff48e73b06f929c0ea259742361a9824d9de58c86e02fe456190ef0b22156b8947e620617c5bcadbf35c35e44c1ed8f4c19d568b52f7edce3018677556c97b00f6095e4e9a6a00c3fc506c0f7946eb0dc821becf2d238018720c2a4b88037be242e0d2162cc989ee7be5a547c472362cfec45565799f687c5e421271e0cba55eba1c431892002d0e5a4f61b38fd8849f6e41df14677da39e2e13f56cf401cad15b62ee000ad83e3443885f0e99e516c5c63874f5a81993834f43ee7d5c2a850261739b3655aa4afb2bb13bb64ecdcf7a449c9ec5356c230c29fbf32de82fdd8bfd9ad3e2e0d91355a0094b139f06f7b8f194b4ac6ab95778d2a1f441acd0aefe74a5ff64ee944ee8974325b74450a7ca2f87eb4259e577941dc6651dfae53fa89b3249d85a18c76ec62885eb8a680acda94f7757892f72390574901006592a329851674f60c7f94d01a725744adc0565bdf1517aa10820003686c7173e3a699f98a389ac562b5d29691906832e32b64245a7515f89c07d15ccd11065a5f274031a00539d0618c9d3cad78cc1a1647598024d5ffc215d5fd537308c06378b6e356a91f313faf9c4c59f836bdb3b00b2a4085d71452aae12ba29a2d708"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b0aa460c2cb102bf6089aa519868b8dca8b83216f113ad8d5db05fd82ac2d70f","proof":"7074415382240490f54334d85e39316c7a3b32653121d0138bbea06d74f0314a60a86777d5c3b3ede8c64ff7401e613bdc18d36682b6d72ffccd74ff000fa011782fe560d0c55ab566cc99f1a2a91a9c1c609c00dd8bb997c370d8376e344c649453007f0b400ce1bfb99ccfd558004e9cb5062c3621ff704611958263198114951520bc0a7749c0e78eeb1b42ccd4c6128b7e7d1d1e328370125c44affe12067575759acfc8ac66752e5ef28d52ad76f1063481768a31f9258ae7e27b10d00dfc698f17cd8ffc9a7dd6f672a1bdc7ca6c25b6103f6efbaf463de1e1f3d410092680bb7c77a92a6e2996c6b687666bb2e331d0697210e835ec20dcfa1a6e32735822a51d09454977a0d07403fc0926d24695a18075e9d64f1b21e61478d177607ac9ac725818e8ffefb277bd50f8a99c302e758f436b9632d135ab9fd354c838ec849bb2968aad2d53e70ac60fe304aca25969fc1ac214016b07f828f3888c063aef0939cde9f502cfbad463ed6dabfb306fa93dc1650af60f2a0b458760060922b428c3f410f2c0a6a4266d9e7e8bf068905c0d2ff0d40c43118cb9d8e2144d0c838ac0168728f42188ab883b48b5f5a3a021cb2ec4fb699199afeeead2fd114647e271a8b2d659bacd72161a99dda18b8a40c59da61aebbbd0acdefe2da8726466b799a16bf426fdadd7a33a53bfa167903eda73f754d8123e92bb39103d719c479a4effab249d717e1a00dc9c74315211e93da995d5dbe4ca933e2d84e23d180f75091b483f14398d2a4b5059757169a477b4f27354b1a13f8f9441925729101d551d6822309619f9754e6ef22f1ee3189c97ba5f2f9bf681c64bf7382a327529ccecaa312151a097e86da9edf96534aec5797a12b3163480e0026b384a0f9211cc55b2a51e6dd18c528f474782e65cdea244bb272020b3b7afb7e156dd0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"be5d0cabcb6c81d388d031bde5ba36e39845453890b2b30b94d8849dbf7c5f4a","proof":"8c0ef629ec8b41d7809806491123575c90893d981fb2e051dd99cee070787f5f28a2892c24c90170607903ceb0aa2de424e8d7af9b00f596d3b2973afdeb7e508e8114e02fd2433a105b1bad665eef1fe747db41dbdffd064660541b9036204a348d958d064ca870fa368fbfa4e45cbe580fc08ae537db91dc22bcc2c39a30189ae07e29d37d32eefccd107457a8dabb3ac1458a238af1f07b237a6debe3e304721c63859071205f346c9bd4a1c4287afa4e5d5af77ff3783cf20966a953670d68a3016b279ac45ae0e36f6b0f9ab105b5824544bc107274955539b347690007729640b6eceefd0bd86f7a53c6b158a44c128ad808f6c49db4a5ebcfba06e028e66e111f76c75676f99f5a935c1fdfd156e730006cd261005aaf075af9b0db42461a1a83a198cca603e596ce01fa6e2752c35eb27616259d91195e19a406501c3a7f044db69daa0c0821d2e363c30ee43ae2bc2bcbd458068f53052155f01936b02ca84f953124f4fa315f44aa6c8d96131a650592f380c44ae8b0b57311e6726451cb1edacc7590aac5ceba201b31424b21290d8712c01bd1ace849c6e07c0f9e2e8f479f2e70ad85f2d1b6d33527d1621a6a8d0c5c89b8a46ac760c61daa228498ed9dad23a6c9001e862b7ab4389a87a7dea45b5959fb3442f05e941d8c2812a3810660a8e39c4532bdc234dfda35f07d2141d2a4d6cfe6c2fbba9a101a32c250a3a48277cfe8fbdfe4d52869a0186b725818648e308dac0f819d82bbd86d2e605e0ffada5b51c454350d3c65db6650dda58c49b6073786b9d12e9da2b31894fc00336a9e2960fad37cebbba4ad8ab0afaff5674ca8ed26b5f47be50082096479b2879f957c93b80e520fb0769f727b6c7c6c18cd61f8efe873112fc169069192c08fb3268596f0e4465ef25be18f640bec3a8eb5395efe0c9b06a59a9403"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3ec457df42162ff68b9cc54b5d28e1fd4dee47679e0f3437d23ab9038e0ef37b","proof":"587a7ae9eefd71e07dae68f9d45813c5e2bd4577522e6c44bc16a8a91502e4232ad7e755a9a0bc6ce66764deebb47dda0fe9b1f7339cca11e5ef40a381945b742c6d7a57a644c2074a1ea8f9355b26a5747abb0bd49f51de6c500f65e478c567de3e9d47cb7c60a54cf09e9a8b1c0ae603b815e3ff7e0d6c3f07443e49a12636af1073e416b541376c7be5a009d66d7f2624b32e49e18219b71cef31d9bf2a0f37195013ed488d87fafb7b4cf839c745424cb75cd9a946da28f600131a124408f2e3a91bdaf16f9bef8931114204065fafac0f3e4059e7dc129e49c6cab45d084c3b0539e99b6a660a61f37380244eae8a1e335037382eced09aff3cef125739d8aed1a7aa402febd94064fc3ffdbdbf864094f2199c47aedc3d9fd8872b734532189aaadc6049cd3ab436933e4b381e3274f5bfc1e422f609fbb6d134a93e7aeab194efbdf267657434b664147117361d952785ec845c56c9fade0606e7cd438e546869a550235a4d59908154579d43263c79aa85b36525be073448e35f9c1866fd4651909e5cf002a5525eb108e180afd3bd1a1d4535a7a0e95093f0aa374f2e6fa86762476d0edbecae5f3704e6c3563e69464579850c42bd971eacfdd57c0c93361d430e7247714662b9b9f4a4e7f5fba042e03d629fd1ca6d1f05c83b46e095970165f1d2d0b5b74e6322b09ee640cc589afbe345c04d8016282c5246251adfec36aee86e84bb10874baae5662d7ae3b829a415bc9dfe88eb7d81f202315aa1e2cd4e4f71b7d6453fd01b76731408d7947e696882eb457445d6ed50bd36e0fc8c028b4e3ffa61423c53de2017e71cfe8b3b7d1cb4e01452beca6a85f3742c14ae8ba86f687feb3f958b3ff1e64d14688dd54de2ea6577a573893225380bf09bb41ba814c85e4c8b53bbe8ff14c6422729a89eb30623ada0d4110535cc0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"38e302e06be6775a778b06b8aa4383280cb194f1146ece806addc52e54146459","proof":"40e3545d7ad6dd6a7c2b86d526e8e5d3c84a2e1c4bbd3738777723d8cf6651528ab2e9faf08ca75faeaf9a92e0a85a730be701faf5622e047845ad85fbb16616dcf055cc6b67378457afcb47663b774950f1ca31a2c33faa1a3f61f993ba2b56d09ce16ed6e5e1193d3b9337d07f1a4a9295b1ce69904a2614a0f200ce2e5d01bd06848c1dea09eab759248f4fcb126cbe4bebcb366d02ca2795206453b16b009d67b3f99a6dbb2cdbf94c23176ba122235bef813fe7d2da45aaf6e38d65f00cac25368a9e799e0e7cb8e0636c8382ebc20f71b2a4e18403db2379cb55a66a0388f403b79e667bc5a3199b0d2fb296b2efd4e25cf72ce57a1eba7c42dde1b960b2eed94cf5cede5816979fd09dd1010a85e1ac88dd1b31b181d778b9327db068f4e2156896dd14d7abd606037047d390909bd17f6f52c1f1a0dda12b6da7e232803b4c4d1bc75a8c9ea0d351b583d4c89408d7d6bb5fd63ea36299589f566263a49ca3597a78578eb58d03f8447769b92c9ca57d5b553c0a57079a571b30db40b47324398051223ff318b07539f135b2f763fc714602781bf57e32ed88a57f7220e6eb72a11729b2d02335c473587dac21b5a9794b1f14b737310e176af1f85d8af7c65016cded2436f110d6392e0d527fe9cd5f89013dab9aa55718fdd089566cb776f04f8f0390ac885f4c577d123126bb163c2f4cfa9062680af712f4f1068241fd2cc482effa8fccb096c2d64764613ad4739b39031f39fd048ddfcc8f29f689421a76f66c09ff175bf54ea5514304a273f2f3357ede2aba7544523a7e2ee61d45ef1f4c2b3c294177c8d7c3b47315e55f35708cb1fa8f121ee13eca6f2641526dc05e9286c2a17b6fdb89e79932838d49695b5a1ce36d53ac69a230260c1bcf9eb3fd447a39366737b5484468e3c11c6a76d51c32725ea423ddcb86040d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b89da4c2d4a519222877ea812076359ace52ddc8bf28c27ae7aa0f989e28d849","proof":"bc77e8dc03a0e70ab95e03c619da0041129c64b1726164f1603cd7952513be30e8b19d38dbfacea9f2a85602e0c2a0b27db01675ec4c3a721bc5011f3a4094017a1db392042a0ae1e99e6b14cdbf83e8665bd63a2aeec8d80f87e703d739ce226afa4d475c8144d442646e632039690aa35838657eca7fc4bc665df1cabd9f653f4849b96afdc00cd2e04174f5e4b3dc375eeb5fbbd8f0646b3be578aea7100eae8ededa8ba6f1dd723bece9d990f48f1e01b30f6bed0130301d6a0879bdf5046d44a613b2d9e57fb31fb46a7899ab9c9e2ec9f6e2c8c8fe91f1e541a0110e06500f9650f755e82dad038661517008e623b87a919ae245ff5241dcb24556a87c5c3e3da6e04d0fb7acbdf53cafe696df77c4d311488da0681c96e55bdba0b765f25ad1da36f03bfd2965fbd3d80cc493a8f740dadac96dce665b84452ca32c043a5a837b1f77830d30abb13897b7502d72e928f44943f655eae5740441e5a05bfeb5053318b6d91093a2332e71258c765fdd5cffc8ecd676935874dc0f81115b9671dc48e523eb6bb5536a15127f39c089f9de3b14e6990a7be00648cabf5929ca3fc341071d421f627d97b1a73b67548139024de8a990024c2224aaa3aa0243d26f64482e6bc5cd4497bf2892d943bb5d1af20d4e0b1450cbc57cbef71d4f25f2d2147e3b7bbd367500b2cd81ca94f2d96f1d67b829260932e0b2bbe32b556062b09e7e27637e4aee2ee664474e2114c2a67d200dac17c7371ea888b89042173e8ab08849952a5a8057c1c1e9f9bb095e6c3abdc7b00f834a7a74389630c3248a23ce1f4c674bb19b6aa7f31b9e7a473f130c2e17da5c10adc8660701c7ad3bf5f6964b2a235c3f416746e6b51c9c3c7e0370b9834835ca10c558003dbcd406c4689f4484cb220053f863287a000bec786786d48067de023901d523dae3c004"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"726def3a6480f0171a8bd018e9d2c513515010b9328773e0096eabfa8e072435","proof":"7e73b183cf2bbd0eb51f189cf8b1a99e672a6078df397931d1d78c47bf234076966e68f7fa6f9d2b9c7178d8f0eae856b8dfdf66402563e093f3519ec019810ff001ef0dafe388887fef37aca54f1ef13ca3600ba0d8e95b366b0f20e7457c6fb46bf69588c0ebab15948ef25af8a30752a907dc46e4e42d6aa7b1bb6c0b7c50d06c2864806fc7691c9b421d50b64f2e4fc808eecceb91baea9011387428830479e1106b73e02a7a9b5137ba8fb8bfed64e3ee7af339e1dbbf8116d1016472005fb2d3393671719ef7ed47f658718f7945b8941bdf39bb74b08f19a5cb38aa01d8c8c015f7f42e2f8f363c076e5fe6d5478b98613c6d202b4ddccbbdb6d71f5286e8cdf84a0c2df23f8a5944d9d83dc5ad124477137e04bc31ebc66619f9653442835872532bc6bb17cc77ab434e7c1d7a852081fb896d9287d2cc5e4e19352c940efa8c8e35e3db34d54031203badd10569f34a6505a6297c7108d10eb26b33a063f31e894777e1af6750eaac6c3cdf85ee07873a0b52db5e209e0ef02d7f2d923b0af387006434ac8adf59a185d1a2fe64a71c6e7d7643a45f1987a2072801788338d2bb2cb7ca02769df8b76247a8555bc447e6b61de507a094d5c751e915fc22348ac233d5c4187288f38a7d6cceebe9f3a7ea7dc14f721688648c9e3c5430d6d1ce5105ef1a0bd6e9681ba4dcf98a3ce9dc7a28ce9a56125cd3a5db970bbefd98f1cdb0242a99ceeccb5de455428abb8989d4f5d8ebf2c97b335fb4854af4b8b094ca070ac914a92e7692cf1885f43011e3452245f4b24a1d83dc99182b80086e5de7c9c22ac6bbf6f4737f4d8ed3cdab30cef05f9e66941a6f09896c4c1a7b5c6f22793b82d1f223a1de372b547cbc40d35f2ee8c9505daea0298a3e0179ed38b846d90e299a4704d9561d19b7e0ef54622115c53c6fc6dc76bc717101"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e25a561ff5113ecf46f2d7ad2b0fb04e7ad3b4e13bb7ae595d3fb044d5bbf476","proof":"c8994c17ad09e7c8cc668742ad00964db81f5cf20df249f7a182c96616304d6e9e2b7c04653e508ffe5a1fd9150304b9407219f5fc6d734be41e49e71e5b135ce21f08976f67301ad4bd22c2a4fc48314e97ca8d207785a14169b1d25b2c220f320ca7b0aabc73c9c7432b352b8372df1f682fe1e7166493816cac319c436d42ebe64e0e5226c0a3486c94dc121f91ad951d8825b72d87c5657e5431cc61c703eca23af83b5ffc0d86bf2d0559f5fe71425a679bba94ba3ce935951b938eb00bdf68676d1d03f493061b9c3ef18a4dedb17c4d094331e3bccb8458f64fd0650b303aa8241c4060a3becc96f2c1097dbb1ca6256fdea2c0014e637d603d3d6518c894eb1e490727ca10da974fac7abcd1a546794a43076607715c484b9cac7e7bb8019f4ba867cfbfb595983ca439184c12792c4f0d8efd3af8225b50d59dac29d65ac8354f61a09e0724e78ae0714beddcb0ada3b6552c2bfd996b94b4196021faa15e0c9fb35c92ac565c76f86dfc7a195f383638298a50a73af22ac1b75b0cba0ec293d7d1a9a460a63320c2612b345cc401ba60aa720b8d009d7e39d8f460f4a0845d1ca53fc48ace7956f7fb2d5125d171a28e21cc93de134e09aa1bb532d688439ea9924d394280ca0f15667783749d54841021731ef890589475b51206c25577f0c40c0a294d80919cb40b9ee14e01e2428c8b2c6a525030616a4f970de210e13923c4a3255c92d7438362fb5595b39d97767a2e27e70d825efe2b540c82756bf42e3a9a97a0760d6a9de7248c065aaca60abb62ef03709d419527955aec86d02aaa4923b36460fc50d8dbd52c5c7d20e9404a8e6407ffa06985ed091c9df63ee30ef5dc6f8d837eb45291c33abce8ffb8b495259d6c320726dff7420f68fbd196e9e48082689a3585cbd4ebacc5c0abec3300bc7eef782631322c3c0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fc5bb9370d6d5110ea7a534d4aa6c0d895dfb707c8a6c1af0a1b0af48028b375","proof":"e2d5bfd7ffa31f8adefd36a284ac63f5d3c66e825371f7b2ea08a89022c0b7348c4ff89d5c8e68bed1f75be95805e965c6915ad35c6c20c0fe373acb1e6f3726262bbbd2423815e242933b0dda9acd762cc9b93ed97790cd498f5b3a75306d57d0b72822bc8f6e79c1de4228d2e8c5d6aff4c99e56728be60c78397690c599525a1e203ce37552d24fe72f14ce60fb3f83c6fab7c31de9600a2862de1243a509424e8de694261b53a9afe969744b870e5d2958153bde333a47504685b8092d0e993193a14742e6277a15122af32f16dc55363f9e3fbf3c9a7324eb359ac6e6011014b7c95f3f83602a42ff571a2b3861fe519badcee4232cd3fd91f0020ca1140cb2200cb88486de4ad22d114458a4261cdd467422087ebf83e66e913501127b8acfe82b6950824cd356f1c8308c8797f13a797919a2a420487f2266fdd4a12768492071b79156fb0078b4a8e36768d0e6e546cda432288c6d30b2d7b766c842b0a3277e0d9fc9bd50e5c68478dece147ff7981c7344038ae7dd832b49d34948e89ea2f9d27ac1f9d92c907a07134730468fcbdad842f56bc7d0e7866b43b107ece37dccbb26177caf80f03f5dbbcde5a01a3f0de6944664086910a0d7f25054b2f4944236478e3c4f35efce0af3ed7bda3aeedb95d6278ea257eb763c5bc30fbadef7c0282ae8e7bdab7e1d754d9b93646607509364d586b21fc2479c4e864b9643970a6068c9df2620c01b41cd6890df60a5dfcf51f0bb425d57efb6af785634b3ea509745f385b769e4796ee1ad09e172a7546320cb4a99387f561d65b3176061bc111005461db4807f2ed5e08beb73ec5876b8f34db766d725ab3c71ed19293de37373dace3274b8d45852212c32ba25059f9babef0d9c9811654fb49d0ca6b889b6523698269068949b65f9f65b73d39ad7b9e45a11490d53e3a14aa10e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d2fd4f6652dd8b01985d2a2cd2f58085894c2cb48358a6f92f49e7b5f2193525","proof":"4a072b85c2d60e50533ba2515ed83b872c391975dea521691c7caa1ed702c328b0cc81802a42f79a7879c0a00cea3166d9a724a7f6a3ef4b7c66bfb14fa57d7ece40b2a96bd31f1030d4b66116cd81811e59cb2eaf461a238fc767d402d76a0a32b85dacbec57b99485c88a85c1d88dc02f8fdf25c73ffaecfef355c29b1b17d779dd80c16631b0a7f41071630fa81cab46ee57871250e22a316ce7917fc1602af9b123bf1b9e05b6161cd1642974f634bb3579f4e1026c40c3b7541a77ec70ca1731c929b0d33eb7a1d689879c2c11384101b343c51bd58bc24d84176a1dc0c56e6ec07caeab2d16b79c4c5fa34d8f499ec989ebd897c7d1848147e7d8d29480a4e74d8ef9ecee091fe3486f667b4a180ce2844ba166787b7c0b2636cecc35992dc07c54d7519a78dad058ac9e20ed8c37dfdd8d8fd379529fda1d8328c4b3f5c18303e34fd791c74776f56ac73a390e452e6d8d57835c7f640797344425768169fcc59299aa97ee0df05686c9a5490cf56fd3c28a851995c0032327742563036f06856ea3ecda12177326c0561f69bc48f1a120fdae3e50e3a4caa86b19d1514581a8b0dd66f682c4d406e7997f1c5b78598b1c3d4f17dbec770f49e941e4d0a921928e6941c7b0a447dd6bb212ce2605cda6ba7983b46a86c26b702882a09120dd0db5ee3ec97ea56ae3d5cbb541d0fc241d8279bd1d2e87efb41d8eecb0f4cc300805b97905f211ed17b3c8ccd63bb85fbc27004d74e0b3787827c2adc05882d766a3277daf57fd5799e77278e1b457e0d74fcb6167ea9d86cd5fff6ea7f0241509370c2e4a7c7257081517f691adffbf35eabe748d7414bf2bf221421658d8b53ff7e6f914aff1c3c53e006829a7d1c69a8c5d47cdf4e1fea5dbeed10065a18de06b01212fa844bc457d7046ee90a263c13687bb9de71b96cacd8e1850f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e63c8aa40aa8f7eba2ebcad2cb2429d2a14247027ab691c5922f1e3c85ef395a","proof":"74cca7ff224460d92fd852fc877e578e675ae1dd2ceb4464e0b66971ff2e9e307c008843e829f629d3a357a6ef6c0048ec56b75b367af76223fef71e1d5a38742473213fdba37109b657667c66166b33b983f13f801d547bf5f434e0f7892e5b0447c8d72c14e83bf0c3e8fcfc7293126c1ba39ed7837f3833669bb98bfa4d3533fc7d62edbcc6ddf02892fc93bf040bd067d126a476be5d04f07cb1ad32700ea0c6bfbf3285e3f808a399bff53fecde06883f14777c40f36f658108a3315408e636aad12f8dc6580dfa5270b0415f528ac2a49561c5fcd136176934e6baf70f6c2b7a25330f422e27ee5d938720ca4d975cbe7362bdcfeec571bc192419f92a0c60ab585aa240cb1919bae1af88e7345724ef42e6384de196e88ad8b6fa4d3b8edcfae0ffb701f4339466bf358353cfd0c79c331377f54bdf7ce8487c1f151fe04a0984dcc6ff5a0e56c50f688206c3083f598173c3f69d0c7a113d8f2d1b52c2a827f417269e81c3bc737280d613a3a1b7ed34a7cc1f97ba80e47b442eab7f6a8b3806aa4823975c2654f29313d7a208b170292d602d3669da2998fe2d4f263c79a2487626a8d4a7b7b88004d408f249fe898b79e0ec735bef70dad897a10532e5c814b256d0cbf7d3863d7c18d86084fdc7565cb02551d7726bdf48c35824bce18dd0f1671782cfa05719b563d6c23ee074afc4b6b5cb2b8c4e95edd71245bc8acfcb7c6a4154059c212a59ed73982d5d647349fdba0dabbae162b00b1961528f88ffc37dddf547c18c4f9a83b18b49bc9d909f720089445e22873226b03f72fec9a971d96a967e2bf2fded102ef83c89bf44e7750b310858f0e1b094577262ff5f68e5271124de50f4a7faebf1d547dac8a9a615a4d3bb904b2855d6750ce2d0d89ddcf047f8ef6835459cbe490f81aa809ffb9dc0119c7a530502023909"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"46b83c41362d990440bbb4df99e303ab702ac5616f84d74d89aed3f4a50e8b0e","proof":"e048abe4307992772125684f455561f381373bd33353e1ec127da243d4b47b6450678c9d9c226b5f9d225dd121e4d4e2d61d81a5d8ee412c49574d79f0e1b57c3467c82e67304551370c3548587b1c237f1479101e8d49c71a288f276a7449366a9e2cac370d9fabac7a68d92f26c39f20f70e459ab6d828b7dc9df73fe9b8202f8b5119f2d8866b91710310daddfa4624b5c21af88d8dd2dc1026d009593b0cf9d495381aee85849a203bb54cb1a806126050babaabe337a786df85764dda05c2e92b823621d3248aa39395dd510fb4b79cdde4eb6ab02dfa8a660af76fe60ad8e00f35ee43f4b2822809dbeb82e8bd6b78ffd56042c87473f8fdf120a1aa6d043abe518f53b22aa493ca7788f4694e4ffeca60f3cd6b2599b8e72c3aa8b17e3c605442eda6bc14e035d971bc086bf943e280ac47979e03f4d0ff0defb85a4b90b2ee1c72f5e6e6a9469f53c5a92d6325a6082ea8042f8e58fb067e61541f45b22ef97a0808230d5eb4a53d322895b1d320702c7ad47cf35cda4c4212d5ad2210a849b645ccf2f6a8ac565834b538629c18ab9d07cc7420f45de9e1722d321cbed927f72b51139821cdaefbb41bc213d4178a127185345ccec88e2ee8abee4324bd5ed995e981d8cd18508abeff16aa8d223e811fad3932b4f3c977fd80267e94241a25a1dc7f08d70741bc8347d4796952c9d577ba0bca5b49782864bbf7306c93360688aa8bf87d82846dcab9e601e065db1e478e60d8b203bbb7b7b9517c168ce0bd649a94e75c36002a1bfc2669c5be6f917a1a2e9efa184a324cbf2a6380fc1ba4e0e68f146279927b580bf878c849952467d243d4b4ef170d82cdeb61cd558e13b64dfa867d1adf5fa4efb34e2792d92518b137198200c1e90dbf00082bcdf1ad9d7f87f59005722e26ad564f9d130cde942bf9a2e58d4bdfa804a00c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5434349b16cd225ec094c0e4aedd1b7b761d8b10f2e185e4ff76d0de9768d860","proof":"7c54bc78bfe58196b3834b1f31b93908cc2bc7cd6a4a9f2f735f631f5205ca0ca0c836aec1d34c62212239b4b7bedffa48fcc1a5aa8bb276290f2b6c6a5aab0102e562935c1eb851af37386e3ebdc83175e0a8f70af3f418035d28af55e92337a2ff23af5608331692befabcbb7e95d7a02cba08a6027aa6d5b2022a9653d021a3b779c3a923bf8749907f71224e80eed15151a056af49e016d85d652a305e00800d002b24db367aa5404f76702fc362e33998b3c98dc97ba618bd3ec16756050dbd3ae9f0270ac93e24c4f7eaa7c49a9ee1f7682429b1698dea6a85c92c3901eaa9d333d03c04490a8579002fc6346f17d9baac4164451927adc02e7a3c4c3c0827d35f4e87c4336bbec5582d1870bb1bbc01a3e534288edb1a8772c6142e12586090a56e52a1d2cca078725e495d86f365c4fd3c067f9841026d837a908060861df5398ae5a1d5273df4c376f17ba9bc9b5507e2aec26403cce91da6a1955aca80c0b6ea7cbc08dcf19439959ebf7ac4f9eb3cd67058a99fa1dc673400fd5cacf4ba5d0f697e536406b8cd8251e8dd6757ca094668c1d766b30e9c2407171ae635ab6a459f38510d9887f9e49746dba9b895d63e939f0a59749a5c664eb32270f0c091e325fd1a994711919eb8e3c78a83575dd76fe053fdcc9851c2a8a67cf8b01a63bb6bb5786bd29235161a18d3517f4dd3c285b60b3f1f61d72c435e053ce3219f0bfa1049c1ff35527a1f5b2a423bb855fd070379088318ba91c388424ee700af6a96a852b45a4b27969a02944d7bd4dcbe176d9f603a47d7a6620313d24fdf4b808a73beae1f292a5ac40aff6e5e03fb8e873c8273c4c8684afe560d3cef0d4d1108f49c8bd8b63a5235af5360277123be79d909a1425e694298bb0773dbfaa85c4ed70ff96ac4bd419358da7bf402ca3c21af2b1c378ae189b9740d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aec51192563cc4b048b1afe674cb0d0db103d3cf59e29f22a899a61e7e8b8576","proof":"309373f8bcf8d67fa6580d3fb8df8a1edf86ec80cd4931ca78fa272897a5fb4508cce18eec3e322c75d552aa109c0d76cd1ae6805b091936fb927a4c0a7e41114e9fc434c0e1084f11f7ca13c90a21e10b6e223f64880540b404e8346858dc5a664b8233c270e4763828364e8a5d25f58377223c7e8660a61d6ba94fa88d2810673b608fc4387e53ce5dcb9653fa5f4b8b2f33beae92b57b901d69ea8f75fd053f69163a13404f9b6ad99f0560635f8efedee8c9d371d3661dbfad42059a960de792b63f095789d484f906304bf5d50f345d6120a98727ae74fd26f7f7485007b8e9414378ffc260ef2701be3b1c7cb7bfc7a4cd6449a2e0adf91bfe6139cd0cb6636ca3b3a210e042d5629bb7b797b63d00750baec4fd164880145cbea4441afafff6d04e2f431c69e98aebdbcb1e938df7e07dbf43c8378bdfd755dc3bf4604658a0f8bd6a85f2b07b36c1be16b59bdefde2afd727f63e98e4b750432e600c3cb50aca152f83dd124a4b15d64ddd8e796b08c92c7731accad591fe280b52458a2f037e87d7f447ba8f8c2995757f32afc37e24f359b245890167916e23ec55161a9ac32198dc052142307c39c16ae46aa308320ad35f542dd0612b32fa8e662a9290371a1c14df262465ef57cea3d00794f4c01240de10c6fd57bc699acc3b026bbf1e216a13fecb89e93153c45d01a25a68e00bd93d33219f7a3a88d31b5df83ae34688cac47582521ef3a0a028a0cf2c2225a678fde586f7b387b2d825614c47b7882519d175fd0c0790a499809a2d117967912c84e874e07b530b2ad32582c267ddce320834f0d23f31593fd5802cc8b3194c71e57ebfcc3504006ccd458e18df13a16af9e19f7e22f1028c614a09ba865f4c68b50c5fd57e5877a45e0b4d4d5ba964b9aa24a0377118357478bf6c58c7a9930e1dcb66dfb4d16c1f5d05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aa5c8a7f5c8d3a1ecb7b802f5380e454217286eafcdc96090f16c86d2971522a","proof":"3cd7c2e2c9ee136ef5c30a66a32586680aa261d8886f0ed5f0f550a85bbc7d3a7aaeb887b5b917c42c1a6b06a95847283272709da30dfead27d8c58787251a749cdedf0b3f2ac8406729dedaca345d626f25557748bc0f6328e3ab703a143e2990a5572f0c6ce1c4d2330885078244a6553e5254f2bd83bdbf38a70836dd0572f8a04bff2a84a7fa2ceb82b7c20eb7caf39f01d58ae909545fafe10704b0af05197dd2897e0ee352bbd66e6143021837aa2a35059a58913315bbe9b0081f200e14d0e4b515699005fb4131c743d7b3a98ff674d59bbc6ee324e3dc8ae19cc2033804354d4be29693f66d3404efa3f96f2363eec78d20e86615752e3ae85362371ea036ed86bd36cb43ff42b594b12b1797de50d4447a3948bb8385a43ddddc6d4aeb021c32449f84c5a78798cf90ec28f15a2b16c0c37184b64eeb0a7e706370b8f5eb083ca0f5b89e4ec82e8831829c7aa6bb87402b791db2195d0112f26700d8392d4e3733f9ee2052f83458c6641ba318a7e9132c04785c90fb36873f866818c40fe3b7ac643347479725c3b4e1338ffdb6acc89e997b0488dfd0c40e0b084a00ddf703166af54f31468910f4f6bdeb17f6d924a5eb2c6bf7dac66894eb2d1087493682d1a25d60ca122ccef78f48673841295e58495a85d15e2467c25a6c46e70814bfaa264d3d638972575cabdd679d400613484aea7b3f97a990edeb003474069e933ac937ec077b505efe064709a4a854c51c533da64fe4a9e5a2091d74748663e8234fa58a9b9bc21240d5094c164a4700464008c146658bbe5f3925587dfb2c839c3dc9b541d1078d0ae49bf44f4c662a6a2847ca83126502858c3dd920fa1207ee33f1dca5f6d05cea97280fa2c48eb0c4aff91e3b5f3b42849b0c45eb6f871bb203d5a2a6044348a02a0fd2760baede35043572857f85388b4d0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8e9e5c75f4f55a44d80aefd3452c4e6f2223b55dea67a50a5cf4115119bc2834","proof":"1490c2f61fe88e5af43c1a96aa26a55d13635c028ec4b5cd93256d2d5428f57c0e392a62d75d5da58377d9b7fb253131777f1f0164f3d19c7cfe9a87bbd0e05788830d1e0fce7dbe5dd266e2d82cd7a0609bdd89b7635255fa81adb3bdc76d0fd8596a49d6e7ee4ce6343378ddc2073081655935387bda795c616b9e6dda8a57246b9221266872ede8cc04c11fe5b033ea93b7f65490e96260b9175d65b7be07ff2c895c7c333aee82221ee9f7de89ec83d8e9b7c979103654ad820d1fcfa0001e61070a8c9ce8ca49175f22a7dcb7de3f39ebb39573f091a083ada895930a0c7a43ebf128ee50f874e236cb3ddba9efbe681edb0c0a99c566b4052ae9cefe59484bc688386c16552b5f40c85d95ef24413f4eafc5e1e8315627dc5eee18fc0478ed123d372ba9b7b10553ece5515fcca086b8f28147dd22b2d58e244ca91a717cb58ce2b6aed49811588aa82b0273741f88154f1daee5a1b21c744d18f84813d47ef55c39c99adfb3f4305411d0c326e163767c5509d5393cc8b9ddd7980b147809ee7a642198a0f01ea19cd6f2228ac46bb8cfe3f2b3e8eab36efcedb4de070843b7ec80d70432aeea7844da8c335340242b04e0b2677927daed996080091b128b0bbe61ad2065b5e46305d1d0b3bbed79d380c9393349c177cf19ab74041c763972ea4a2dabd4b9381e873a5ed399f369387f4a12a047bb1744a5f557134078901fec5c299c35cb139e701516df148a71a142defb1f3c7c99dcf010b5bb61f8b7eda0d28aedaa84b379b1ea2704e3156ee8f5846619c3f6dea12a1f119c27f4c5d64ab961b89cce15307001fea741a18226a1504a1354717fcdee9b43204a421c19341576385da0e77373730faa632fcdbbbb1e447ce767b56e1dbe9b9d0fb2c1b97dbebb99f068dfc268c46a5dfe0f86aa0b9a32dffeca17999dd8566f05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9a46e9c996c08595141558445f7339adef21dd6f63daf17811e4a41c27fa484f","proof":"7813603696838faf7107db6733440d28c92f8151057b941a25e829053147ac5064a92160924fe92531bac05fdc09aa4d1e8414ea32ff1864fef024cd839fc1074a86435e93a8d52b555ff2e1f39fb335a03710be52da54481add8822f2884760ec68c045e4434d5efd06d77d2ed68c1e74eb8ac0d7fd2b6ce27cf6c483a980313ecc39c039ad097ad8703d83b610f04e420721a2e8576cfafb8f99cd1bd0ee04b3155288500410aaba4d74431ea7ff42cd4382f2afe1e55ae3e359b4c879a404360969b05906b7741128ec74d41f06b31fee8e03562138646c494ae576c23d012c2296ff375d87252344b2d606c21e689d929d2b97c7f79bac3fa5d8e0c8c04490563aff032cea639877fe05baf474a69a05e8a4e6a0a0f4d5046372b98b4528aa868c4fe91e3bfcfd7325b97df8ed5d66e295e5b3cea61b19f3ef88e7678772d62c760216e1382fa5d8c55ff42411d031ffc3bbabd8ebeeed94c77329d5ac10180b9c8196885d5e5d81dba4f029c2979e9b8626c1c2ada08e52c57f2f081e6668ac34bcd4fc5d202ed635e9e9507117e588231277ad25baf552451e3e7c1d161601130d35a887e3bc6da98c09c561bc1506f8333a64c840dde046495f732b48a82d2f8d4ea461b425e398bb0fd005e89de9e7f2db625b54db519ac65f77c65378758f6da02e7fffc4ed158110ba214f9586000b6ec93f54d86f6a8c18edff222011d8362e1775890d47a1b0dd5d0aa25d14841e5b2a42b1f8b53accb943764c3a3255ec93fd3f48fea0725688e0b046004cc4965ffda92b1d21085311fe3d0050a6b71260ecdfe789c1e2f1bff13ce4fef99375622ca305c8114e0de4f7c008aeba0752b520a175e98471784a5a2226f86a4ab5e28aecc58b6629b392413b0304c528b40e122329d2eac70deca5d04c9ba2a462eb36666ab5d50a0faba02c05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d2a3c44d8a7c0ec3f413802c154932c3904aec34242176315ca0993910690b26","proof":"02bd261d4800b33dedc1d76d83aac63a0167ee389cded576d2ed4beba0e0fb3ef4d8d2e515e109ae7e74eec70abab29be104c1623c6b5c8b8e0bd3579f3046059ce30cf4074dc976310c6f1d54f10c760857ee8f504611f5dc1d00c0011c4313a056b665d674a3dd6ddac7ab66ce517a4f0a183ef25bf8c31e8cd4b17c0dc125d8d6cc50b5f575d427e50d34cd0d2e1cf4fa7be6fa852a5a23296e311bfbe7083ba215c3d1dd845f65365735fb0d354c099a51db0ae75e8d02850ecf4c1dd204962ccd072a35ed21fe0af3f3c298395408f01496b26903004264a7207c57d60be6d330d50d3f9c868a6be89a6589e5c9a3136ac62b5c97ef87f5edfd6ec15c3dccf13f2f73d4aa3d9e6250a12bc47340afe105e1ae591db2da5e971df99fb67e1e3c5a1b377b0d0e17a4f7b484677ff5f42a63dc5f13d08e574ee4d2b31e6319062866470e0f8abd82e93840b787f9a843de11109929cbfb8fbd0107ffdbbd5c1089efe7d172d43f5d3caff9baa304790963e6ea1261f2910bd09b11cc1de256746cc5ed5176bf8f86b4f97e78d5d4c2bd59acb55e43e25642829c973b6f0e19bc516c13e7a743dcc7b70d3026cebbbb1d5b26a9081f2b4da9426ce5fab4fe57d878b32b3438e3de8de094329ac0de7976dcdeb904dbf1fdc8e1d0a70f57d40cfe850ea23855a3a6b4042469b1784a514941e818999fa5a6d8c5e5489090af05c08452856c4a2f3e15292e7b1af34be85887ce805903f0ac9747e64e7dbbb23e28f206188edcb6b6afc81be87e64517bf015285489fc62fe1d52b696f68298760a87fc922edca03ff5f9847844218e28836fddfcc4a98e002cee158b4a736e58996afbcb3660963bcfa0105809c8f69a0945c9a15c7ce4babde7465acfd9f508658608d9a0b900ed19e6807a16b3bcac5ea18eb834ba75dd4a820ecacaad4809"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"483eff2a454dd20cc0e234f9a248c6dea9ce10c5d35c4fbe7d59eb7cfe60bb6c","proof":"02c50dd1fc0d11fae7a052fd3f02d294f070b0a4b25d8aba9de2d4e61dbf4606acb58210a1217c849620e4c84b82391300c6911a8678df01d03e8e57dea4f919c059f1a6fb9415f0e72449acce8a94f807eab84963496f1d34ffdd87f3ec3e5a68373037be33cf6c3ea1b92d8f9359bbcf689a3530d4b81995a7d188443c0424844e6be0d29b996ecb430327e63f307685eb0c313d138d0acecd058422f0a70d1bd5b4c5c6aa624df897f8d45a0955e89e14f07f99a2ccad0cb5d93e0042aa0da1e9f3f6af139d10f3a02e72aad8efeeb5ff214a5b6892d578d395b1ec50d30eb44b5d58a0e1ca51f4918610ac1989352da5f8f5f9c7f9cc62bfdeffc01c2028b8a3a48755f5ca06ff4bb0a46841523baa6e08883fc0e41da9c33cc7ce28c56328ab0ad572c799c9d8fcc8efa8b212c050d3c54a4e0177561984c7dc8d0bcc5790a1c9abc05e8213492b1ac66c24144e9e203fd2460cf25c5dd73e0f20e9b32d10167f5fe51b449d1c60a056a48e48e87669c5e03713126e2ce5b1722304170a2a3c0e6fa69921d1c40bfd98b25d7a423f352f23627ee2e05ee987640388d57b22f2a5ca1b27811cb6735355fc4d9c2882ed0188c68a8a4a762b6788b9a39e31a60184bf205daef455236798bc9be2bb7b2d3f7130535e79577b849ce21ef3653a3ecc3eb8eb22b3ad8e8ec2b1b8a9e7f35acdc7b991109ef624132ed387a838ae3a22600b6c5ad240ffb2520faf6abcf22f0a778622e434b1d91ea2d818cd6c92b92442c17580be8f8b9897ba48cedfbb056001dc8d542e599634c9a6d6613ab2bf88e1a88850037f643e0289fb946c3779b68048ac3fdbe13924b4b915815c6b78ea36ff2c09c79193c555c26a1c02ede0a984dc33cfd6de108e6c6f6851054e7fb7310163844f4de1961e2c69c858b12eb773f88d9d715eb7959a8d44e309"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"caa8f40df28c5bfd6ccca2ef1f3f2440d329cd05a0c316abdab0261b1b190361","proof":"c82f84ce3388b498b1d33738bf3bb012ce13a50b40883e636b28df044d143d31721c4d933258ae8aa91651e37ceab36f93f13a1c8d1c067a7baa356db70f9b1b7a1cc5493a0d862f43224ac34377ed5cfc59e253d0624753de8f28bb6f8d833844c51c75a4ac082f03a0ed302fdbe54055a4c43059dc6d33945585c04cebeb7592037c79045b577bb5e70eb4b8d8b2c88c6db25306f025d4de6acb8a662b8e06d5dd13d9aa8d58656ee7f6597fba9bcd52732d2b899de67250577b7a4d988802d7e53076868cf20acb26bcd63761f2626f1673ce4394e29fd91e6d48a785820256cd01b320711d1ce0236343b8725e769eac517b1771d09b75686dcaa84e2f29d25c9a20c23f4ca25b2b01d01875526cad5fa16fe9f414510a32fd5e83495a34b496b58bbcc37151fa5b6d69289bfc72640d3efc630a9b376e434c85389be00e2e7af8b45dab5a429ed78bb10a296ea3665b5f61e52c2b883c8a77ca009b0a7e3401d56a5866144c48e86fc693c7ada95085bc965e99c0f951db87face9f854adceaad75437b68e9338ac44b3074e184a0c98ac724abeed438a6675a32063b2f78016a6f7fb71b0d5bdd58a234e04f4bbf6e6101dad06604813f527d7707b4605ee3718ddb1fa87cb84789e782e94e8b2b1e3f837ec855216a74806aea2cd42abcf9c1e1a1388216a85cd1371efa50cf49c54c6e0a7bd0ebbe566557f4380b1c4844e9870ee2ee61fdd685e4d247ae7966fc89bcc709601a9292ec4a72454f5b7895af7bc8e10e89768f5a15047029ab69a435522b58c05fc503d193a153304ad20be7ccea7fa65f3f1ae1a44920c80b263cce1e9568aba9eb111d620109695a68dc55213d8d141ce60efe6bdc948e345038b79677a8510155a7514f598e470b40899911f6e50e4beed39ce33f286b039ddfd286304a9b162c92be051656000e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"047b163d9de951bc07f35145bb495af713d7b1d642974dbb0a2b0d0c324e9f78","proof":"96d9a2034efd66488f123b4dd4b52584068126e4df44877af8d111f678b10c2428dc031bcf0be3e4f986de5c50dc499500bc1fffc1931af4dba656c4e3e7f041e8d5c101727ab39b4373d43b99e3121f399702c0b813f99fd4116b79592ea96886578e71a53503786993c2ddd17a3e3fc5cacc0aa5c5659a0850a847859e5d7880e18edfb9f70cb2eaaa37de7bf46841a619c0ee33f8689963ac47ada5487302f50ead6e3c3dba222b46f67d06752d65152280cee8a3d6fc73b2584824d20f041f033d657f53503c212575b0d0c530c50037f77e9ee7b9325a5e43146a7aa30724600d72001acd7638620d79aafdf3b4033b3b9ce9a2c077a3b7dbf1e0089050eacc10040dfc9852ceac7fd0a5a98336a21abf77fb69ed5696e4dd21f5cdb3471aab519b5bdc50b640a714cdfb86abc37df8eb591a0d28b9c4f15760c9e49f4a58c5fc4c277b65ac507310b125c36200a27cd38a7534a2966c995f12c12abd5fe067d034e82777bdb1ea36691070f6e1e18d0cf8bd17a33cb4b49ff8dab12e54902bbe7ba261b359983c20609b1c4358f6cd5d50222a020d86d042d2a7228d2c5092eda9be875c27ab2827e7bcbf1d28c9472dff1cbbda16e9dc47409515bf7162a4a5f3aebb209d18cc9b3e08d36435f8dd04914171dbdaae81952c3f84d845d4b7799d706f73b1b642809725dfddeecfbdbcc9839a5c0d4155f3d06c9c3e797ee7088762c1ec2251beb8b6ce402f8e4001d9760d2737259dc35d7d3406f5297cd8f574e009f4f3fb10e7642afa740cbdb7d5f112dcc64c705795a0d6135d501a1eccc24f183d074788e5ec3ab99beec481671bbfe28514527a5bfd0ead7160b095adf02456021971efc7560c300daaf3d4f56f2ebfef942113e7945aacdd0fa2c7cda1101380cafeb1565d5d43c616c3e6e595a7a1a7670a454b46883df306"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e6c8b86884ceac67d83ed0ea44bf6c25d985a53b1e97872116be2e94ec1ec772","proof":"1c454d4ee69420d6fedaafe24601bc7313cbde30cf66297c4d781f5215d5f36ff0dd4ab247ae52f0e3eca77efa57a97627dedfc65cb904d2b4689e7abb0a463290cf1828790db181a84b37fdb3ac5d0bc63b868bc1c89059fe3b055a19ba371b5c4c8a3521d45c534aa1403441faa69bf33e7ab63ffc4907f9e9860c1bdc4e5d28eedd5813e910678b84a9cfbf66274dac6881cbd81f8ce18f0f23e0b854c9021059bfb884bb13c5e00c5dab66a21ff912b18f9257af0c5e278fa0c83dd3140b7f60b3d6cbfbe99cf2d5045cab0a1018ad5f1d8459df48ea381f03c8cecdc10a92e0d63b0b53dbcf20157f6d1b5737dc8929d1ea641fe08560bf9ce15dc6e64ff6363c9133043f02acc58516c39e29f09e79b85741707c6facc7fb3db3ca010dfa3870609d12f748a5fc029ac1bcd5f7f4f9afd470deb89ad300877ad5c06442ecd148600f5dd7b069357e5207cdf2aa3f012c4acb814507a64330011a1adf625223c8d4f246020077c30225d3b48c8d000684c6662f74cda7f42f961a93f76db4a7fb7b1e29a0d34483151918ac8a78ce9f7394610bb6228980eb0590fc9f21acf6fd9951a0717c7fee7a42297076e95d3017c919d2ceb89d4940de6cd26b3b4296cf6017cdc7f0ac1cfbe82e592e055de5acdd504fce8f31e2e56ae8b17e600abaa703b1064543ed5b9ffea4a18067d9d00971d35d1ea07f6e553045e1d178823d6c0ac486d17569c94ab88bd1fcee72ad16a572be10f92c278e09949eba4610b65f99c7bd7b2b6f217e707c003fdae1b45ad1fa17d0e2b6a99581471736263a23e289308335df19c88bf913cbbe2eeb5103ed6cd8687b7d6b029031b68140db0cfa74df36bc7b94263827c5186a90759de6d4e45aed202746ee924a60b90919745207c9c22ed14e9ee7b874c05cd4414c62a2505120158549214f424afe0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7696bcc5cb6d36524acf49ff9f7cc7aaf48dc32d981e936a32fa179f841a5b5a","proof":"c81ed55994eebd330fc80dbcd64414af6400278c9d7fba63970b2c12e1c9a14ebc5deead8fbb17004383bf78dd5870c13a9d6ae3e5c6221f8927eb3695919c58f8b90ec8d74a5a8cbc79fc5d97a239b50480020dc6510d2eb5b0725d65807f051c903838dea4db3b793f6b7212e7829c202020a306ded62df9dca778c79b534bfb637784a95549a2a4c7403987f24b69462f9db467edfe92643f64bef7f7b809d75f079ab8b28672834ecf3a41a8ed89b61bbe23fceff5969029cf8528baee03539fe3a3c4e7df7c0e1419ce14005e4c665b690e94ef4d5cdd1906da68c655047a23f8bc9ed58a6527266eb8088d0e3005b560853bac731ec6baca089c0cfd1a549e92014aac39f95779c287ee4f90bd35375a7205b7cd36b57028650e769945fa82f3ab3c72e3320b3c8b36bed52fd6866f3a5cac363ca2b271bf4800a80c07604ab8e34ee48b6bbbc53a3564f1191eb131d784492b1eb5550e70ca6a2a7141b68ddce271ac38cd3cc899e9078b49ce0104dfd9f8e5970f5121028b8f1d9414da412103d1823eb9008517e3378a00156f39a5661c66ad447d4e7f7cd349d73ce493ed21a88f86c4fa588dbf8f55eeace6116c24bc6fdb1fc0cd60281a704f402817c763bd992bdd3adbd46dbc437b208022f299f663156d37d4d340f48a312e8e7f3bcdeefd63ee693760f272710d451ed5d0be86474962dd6555ac91275336a8a6d729e380e8ce200d8cb0861bb14e94c8b921e2b718492cabd63e88f80c116a626950ec923659d65e8fb3a7953b0c3652c9c61825145dabe211c146e028111e0d0171005dbb2fdfde594c5190b76d68890654bad01f1ae71d0cd165989253a549e079aca62fffcaa38ec940d936bf3792b4aa956597eb36d50e7b06731d05aeb780ab6740d57647faedc5667bdf019beb82b9e96ae889c11f0e204d3cf007"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6a46004b7c0c2fc6808c565990fe1a993db108ac0d5c97f134a6b2677e16051a","proof":"f0ccdc721c7abc78cb5084d9d3d0758d7f62d6ed104b4ddebf8aa368d2e68b17cc1d4da952c89c9cccfaba29d207e2605d79f66314f92b2feb8c9aaa5bf422509cba347f3beca443a265bddcb98fe002993d4b5398d2a1fa2f921ee68b8f060e5e4e1024cf658d3f0d9414d15ee0325c07a0407f4620c52ba52a7e9d74eed94f55d2bfac15250a07384790e67b30a2f1038aa154f50f32c01836aff84f427f0fb331b595236f50b7cb2e41ef7a8ba07fc415657e62f8941883ac7a7b13080d06d2d365bada0a0ea5f49d864d66bd550675db72483063a827a9ac16e960b53b0e8ecb7cdd183432382fc77ca916dc908d458bda7dcebb9436a866d25c948b89791a3dfbeb1d5219c0e70c5ffd8451736fd2f195f1bf5c6b430721e9d668c19e411c65a8090cfcfb186e1a8966cd6e6b71f8e4e6d7988ea84d403fe03739e6e65ec2b97ad06b4325d87011af836efcf7525e44f44af45e33f61def6c34eaedb4593add265ff7de723095f38a66e7f2774a52a078ef2397776670d91ed5ca1e0470763e79a3a3c9f598a1c3f05ff6f28823e749d63da3d4f27c3897ebd346165708a4c81de2e17be048391db0a0dc8c1a676d536494d8dd904dc4f168ca4c87802c2e46eb72f7a495a12a891ae17d2d22b7a1c3e55d7835f93d9f868052f535fd302ac25e9e6421e5faaadd4ea0c0e6ff6d146519e61826b3ee70206150dc47273802ab131acea12575fb2dcad1f0cc8afd5bbfbe50a02cfe9dcb289dccd20d62695cd19831fd6ed08d47603d45605f1a278d2612b1e08ffdedc51cad7bfc2db66586c221e2542d9bc6c65445d8da4c11cd381cc64a852be798cb789833ea950e117ade6e08785b70f8c02471c14ab06196e9a5a1bb4091b17f7110eae37bf6d00a1b9c1e902fc2be2d41eb74e7cc73d0e416d0bccb975d470ef15698b195dcbb04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"805d1e8d4ebcb98f01134d619d8aa78ceb96dbb31cce1666e12293cb90fe457e","proof":"5ec55a1ab2ccc2b99f89fb83c2dffb1df42200dc2e8b77182dd3428a9b617240c81e2a340c530159722f38a26ba9adbc985c3958d689612079eec44f75a27f08f462046f85365c08806613ab26fee6aca776de4cbee01d62dffc84cad0b6ae0176bd3d3b75cad820ba4f5333d134a4db8b654feb36cdb8a0e80ad0fcedee7760d94c7cd62d34676326a472290b303a5115b0960729ce5273f420ba8c992eec0d13f8b0eff48446971fc9a7a9c39dadfeb79741e8865964d44035511dd0ab7406489a357e33f1ce43912de05afccd9f0618af0c3f879b1e26ee73a13a71b37200521e65770b58316360e3c9f3f3055b553cb64893e32d5563b0aea52c0552470126c2b3ee2ba2066862450f1f8d4ee5a170b83aafaa4f58889262a02d76d1c60ce038ebaf123178cfc1da021f7104b7c1fb538602c0017bbc92219eaab5bf11480ea05219f68d51910e46107c698801dd6ac148ea62dda7e730e345455985fe7cb8b8b11e9940fadc6727a5df61538a59b59b5779fbd70ed2569bb3fc3cb0675dfa7fabf3ec05203bde7e09892895d7f5219c6c5ce6b1568d81ae6abf425d7362aab8abf49ab4b486749bd8ae40f4da07bafde16fb3b41162ab0e38b8de4dd64816a37304e055a9748a2d8dda4295f1f0bdedf0621b5452175de82ed37484392a18e4bbfe0cdc9140bdb40ba7dd41c7ac9ef78a99f6c3792d320549ca296c190026dcc2ca4e0f87ed93a1ddaaaaaa27a70911fb030ed679ab37f93ec7c1604141e4b43bdb2e330a35d610526d4f6a56f2f0d0b80296823686ff0fd747e896764426c7bba4bc2d944ca907bc6516fbe8397ad9cc324ef7c2d21e659b5503f48e73fd68217e89e68fd40efcae36e8bb8b596f34fbe4d0b86d87766c244476b6570b79b3990e74c8b3f4a0a415f7e3a8c3ccfaa46e2d8c39d2fa90bb1480a363b20a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b6c619ad750aba53b6e7723be978752cef02364413fb67ab25f0082c94499a17","proof":"a89c9c1f9e4bc77d231a8f6185919a73af5427a0b81a5a92047f1c212e4edb65c2e3164ade6aa75990d66f4d3e9e4a8060883051f62bd656b3ccac73b29d983024129f2da0eb4e6729f3c0a0c8f330fa892a9b35626e6dc8b4550dd8de894f21a47718ef700e73a8d40f58a942f4d48e6188aae602ee34cdc8ec1c8e65bcac1327c542e4936dbd351a34e397b9e2d870cba9650313a5dffd69614cececf1c8059011fc022d6bb4b6fbdc8ea63e6e4c0b1446c57463824bbb1aa8fd33fc5ffc01d580f258c7810fbb9572277a2c0f45e1a9ad023c2c5b4724e92fd56e4465520b6aee1d7f8ddb3f88a7da8dab9728cdffcbfd746ac93a52a1bc42b3d62bbb5f636e3d3c67f828916f40c71c6c5318a8160939fec315199180fdae48ed19bc4078a2e4fa64619a290a1c214187b50fb50d59410295fcf6c9c4ab1d9e666f52883120ca12c05b7cbf6426f368472c1ad5d6dbf3ffc24dbffcd6d98e3a7f95f62c65d2d1ea8b134b7d11037af1de64e6fc0d0427cd72ad8dad88bda4833805f425487c15caed18db689e72e6c9b3f1af0332cfbd10f2b54f74565bc36068264e885160dbdd487f1e5c5841445a43838c22a4ec2200b43425e20dccf5760d2e5a015456eb9a28280fb6630f3b04c94e9565e954b4994ad800fa06f7429a207919243302eb962eda319864d12b7f83d3f3e5b454fef6743656387543fd512c37b88339fe86e471d3fb993e6812b52e1a4779a0fa9808a7e4230c3751bb2c44854ba26b10891a9ce062b95edd32e7ca5dea98e245be78fed944c3d219fb07084bf6fc6c8691b3386931697e72af758d06138d2bd964a759699d5563b46f35d50fc4365dc9dd57007b2e9655a368272e1e765bd0e2cdad49528e1c70b108550a51dde10fd13253b316594f2278b1f03f77a815daaa4273a85f70c53a96ae24f663fd0006"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"da3e67b908ea685dd30ee2426edff83b8b99bc4cacb058fef6bf4e2c747a1862","proof":"60741b5c3ed98dc7b4e6e0b31fc9e908a4125e48c09dc19a8cf445c430cacf569c31118563b69eba6c9c1895273fa635cffd5ff38874123be782900149aecb4d2a805cfe536de27a00cb1732d29b32ecba6f9004f1df507cd368440abb34fb02becc350ed910ce1c14c0861466191a272ee3b0a19fce236281cd8752d2d2eb08b40b8ddedcf8b206f83972920235e12389303e17bca8465aeaed967e4c58b80813d022b8ed6f99f6fec2fa1b1d7e51e1db14cda1a18e042950e723ff210b500842612e20d758a0b9fb0ef7c16fc76e82e792465162cd6e75a8a7f9aca4d5e407be418237dc730f2b426dd8f06c34cae34ee4c98f5f36fd7f5dc0cfc10c78280fb8c96cd6feb44ed2e826dd43eadf3a3a91bd945aa43856a22781f2d9660209196cbb0d07a41318e2440aae97fbbbc3ae9f5c788e040fa83b1a157e1aae10985aaecdc7cf044e8d9f5df2caf34a8f7e25d56de761727e33caedcb592362f2800ed2b2c02b7875453147e333cee10402c46c4f38e2b5868d9cc2135551318d9e280e3eaeef29fc70c82d52bf36f16f2ed58982b318278d80cd8b1ad994640d3126ac8b31b38cb9da75ad337db19b738959b2351e39c129d8eafa04285748bce6410e6ba80739b88c5c0b0dc45923089f81747aad05713ee929e3b2d0087d58ae33cea3f4bd34143be92613047e1da36e9af5670a009140693ed610287e2283453be07ca8c3fea2adaf0a4a05495a1a1e6b963fe6ab472329d8a28f691bb0a29d79de1e59a8c732ad58c6da46de53dfa864b71c7611e1e0c14040d695739810750674065b403c7b4cbd27c5e799ab035d7ecee24b1d7659b20c1c0cb20224652a643c4beb82831c829cd1f75e855e5bd17d795957f346538bc512341b095ce38506cca524b69ce81fb8f5aff219695a2f8de5f2ad10b208887889f0474e97250409"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"32aca78e7975af593e3a87a0c46dc7dcf07389848317c389fadab2905bb51129","proof":"cee240c6f3e7930e2588ec214ac6ce21455b8379fd5dcd273a6dfef4931eb85254bda3cf09a0c1d289347364d15c2f91eec5c010d5ea43526ff5113bc9d7275dca825d10238367397c143dc94e963dfe392dbb6a54444779e98c84bdbf1568573ac28565983628cd61a7edc79938cc4f279f852b873ffb19a364e15caa33902a2475f30f1e8503a37db926c641df659a22869828bc5f4c86cab2e7dddccb840451dd719666631f1d8ccb5ae35c1418d96a147ff25a8142260cce0c30e54e9b00863cafa04e44a25b359da4da46e33a083609798306288bcec2ab69a9a34b91035e122543feb8a63bb64cf357f9be2aa94ec13606d58cbe0fd55536cf923f53489c214a85c585aaa580ed6e54d8135fb78ff32df87204bb58f2e9415c799f13734a026d8dc74daa5f2371f6457122efca7eb95f466c78fc982c4bd58a60fb1b4776dc672e3d61facdc0636450fe56c7a26d5be0c885fe1affc1d38194c796b569169f8b028b5844a4903da78e4d8a4f7d85cf926e5a0588e9c220241eea0c9f7638278b3822b9cc5b2de23900b9c474d30ca1f8c465fb9fd2af958463518fa2370e10260b079b30ac4f3d1e8a99c2884def9b1c9007d288566111e8692228eb7540dfc51b0a0c12be2400c68c5d115a47c4ee21b12cf19ffed0d7549842fc5278684c5f210118974938ad2f44a57263e61c84b3bec1d3602b4fe15d35f6472f2daacc71691467c58f4c2cc3be92254364eed501256f98461924ab4de8642c3f388c25a1ef10c0e91fd81ebe689cbb772900da0e52152f8d37e7e77ebebe0ca531545fa022d4bd41b961db2f1872ae3103793b7bda51317c68ead59c303460be23f4172d2bbe0e7cadb1e9c329e3b40033d4e5c734b4186f8d4541ea4a0472fe093dd4f99944103118af1d36fea6ba95535dbd528100242b889eb29cffbe78100a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9e28bc2e6a04aa6133fa6cfc18e9d67e6a834d274dd02c34a360392fd552ac29","proof":"408a9a44bed6672e7a1a7259d57ee15319b250c77701a4657b223f0d25dde738d848629dc77035de79ece8200c61f99560162654241826d9d57d67657639a2078a219b829226fde7bfbb61fa954548ed9f8884e6c7528449b0584e6db5f4657840ff1344ec53bc81c1ae74f7b2bebd0172be60edd85fb6c286149141efe5bd0eb1f8540e97c6ab20849f210b4c8c8e2ff87f54f498817b23a8a163d8c26b0106a832ec4bdbbd770721b22e31d7733e689f08583e4a661352dbb0ca85bc44e101fab52ae81187d09b999a7d476f7649de445efd8db7e23d3a099286c05151d90a243f85adb97f03848baffeb533cf89f023f440846be2f1da3cff1f672af0df71a68d987c22c0fa22177b0e5aa52ff463891ae951c44e78f6a0f4a6afb6943137ca7395d83161a4875e4953a25809abe5c10f220336a247b680cc2843d8890f29eaf9cc912a13c7ed0a9da70f48ff44661ff5086f67aa7579aacbbc0081efa1621a6a3318971ec20e84bc00b116366ae5e963f922b0cb3f1da264b5869762d9452cd81df9bfacdfcfc370b10b8e72f4534ffc6e94f2de81d8e7a7615c52481543b8fb231b4e767f68d54a0a4371b3ea4d143fc66c7be6b5ee2d2a05bfd8f86309e2e2a13c5ce22d9e70d3bcb8b55cee5d1a3a56e62d64b301eddcc49e3f4845367ace7a902bf3e7843e6a9354153cade30697750bb8aa39705d9ae7562df42d7a824962151156f16d602bd8bb5369b3ea05fb87be89328843a6bc158f9d1e6e7f20a4aa22687047fe90d59c3af6efe7f9a0b39bbaad8a548aa22d858153c4a403f602fcc82be452c6471cab8e9fbb43363b2cbff4ac5675d4307f35f6c9f7a74d907b5119f99313b858ed5b553efe2ecced061b0446057938afed0e98e3b2c70a7ebee2b884e481eb238c2803c2a23ae991d6a3a01fec84fe41321f5750e7cf0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"702f8429935b42c68ce9c91396f2d65db9df183d461b51f57a22593ab62fe97b","proof":"7a65280948883608bddcd429e3760fd7c95def8356a3ee385b0bca1e4faea96acef5ab1ca49044f9f2e59c432346a34a4b8e13cd90eec21d9ec80e4a0ce1af30d6749cdb2f2552f8ecfddee80a3589f42c2f730c3f21e40559e77e120a771e0c12fd1760d47e20b36814640fc9f7f0da5ca95c0ebb3ebae5bfa937808f6a6f10a42f6b4fc120639d3aeff9176b723db9420366c6ef1772503f62528159c5bb0b9e25f7c6c0bf55d30e07350c410ece1010146618fd198cf60a937367958d5d0ba22c505a1de0f3aec7c673e06c38034be308dfc4f8c1540c932fe552828de70e543fa74c01c098a3508cd96783ce5bd2e5a891125f7fe8ebdc2d5f76462f3446d41d26d6f162a63913babbc1adc67504eacb122f15981dd7dc62118fb3f0115d2e11e094c410f10674b27ac8967cbffbd2c84c11ad75442db7eac8015a9e870da641db153f8cc1549320e650f21fb280fc83dd5eacfb53ec63054af7681ca01288455abb6709bbad79604290a6a905f0c10cf54e9d7e79f33374f74486cdfa412877bc2420dc08b19ecb7eef540f38759f17301eb2d579e7b8c9a25a0a4a5a02e8d34f7ed627334d4d4ff0c8dac023dd573f77d373e645a1589aa56aba9ee0783a5d355d139947999f254c200bddda4aaf5eca7bb5a4951f1ea96e8c6fe35b0fb44ff337d4c51678a1686857d2a53c5042126a1173d3014c1159049d145f661e9202f6daff84003ed2b86693e6534954477957ab4d63ae7c21cac9ecb56cdd4ffab63545130e3e0f08779c2d425ca31bcfe25b3ed3d61c33ff9a310ea6975142aebae4ac6fb14b14cb035bdeb69c049bbd6da1d382269d2bc23716f95b795d01f24cc11284a22dd56307c57789f1cd252382070f70ef17da60763b5a59d44806ad680a702da22afab7ee1da613920902b4054ad68fb150d3ea9b8549241b8708"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"80cfc7799233febd7d211aa2135922ed2457c494b914919b91177732dbf3a32d","proof":"08aa8b58b3b36b893f21c9d4914e6fd96d7b25817d7cc050ce4c1c847823d51cc486c3277df4c6dc02036468a950867610d3f08d5d62c0be87a29b77ceadd27848ad321143846c0f61dd46a1a62c2d37975c7a2f3d5b603057adb99262fb174ea23a2946c41d1e7e5a4c5a4a3804ce9f3ab3a504e185f52919ee1c0ec0d3e002b9970d2cb17c8b60a41e31069192d39ef5939c40cfeefaaef705398db8715a050f60a309c352b0988fc4e5dcf82fca23da9242f9aec06efd39ad7a8f97af220887a1ade5adb5b55b5716965c5a21af4b9a119af1b605f52e7da595c7adaf1605cc8ca1e5021ba36293af65ef8768ca62f46be46bbbcc7f00e250c94efc10633474c6941aaeb895d6759f9bc92ed88f770c66d4293fb23562dc6d680528647a7aba1e6373a14dcf4c399d6831275a027359afd3a56e8c00d32981728213ce9f262005d03053215951985f0d6337e91c74e2d18b75a77ccdcdae878233d4ed40633622a153b4887a8f5ed1046f70d366a401ba39cbd73629a2386c94d66cdb55353a036c3cbf3ef510a4e57db33e79c87e23e2401212af1264b3e89400b27e1f5ba6e859d6eb1263bb11e3df5d1f3bbedc7f8e818fc10a8d919723a1e85f72e03480c29e9f06214b8755d09b05c151c05fca84d2acc0138871ba975c07ddef82610633bf94bc70d7fdc47eb88226168620d8d7b15f013fd8f2fe12f223a343de6b7ce2efbf4b870e191864e6d18270e4e4f0c59134df338edb222112c5319f7d12421ec3423ab3b6351f93308f54b27ae64b36539e80087c024ee2f850811cae75f455169fa4fa5138ee520d3d8ef2363bf9be103e6442d9f76f3e503650521c6d5b38b0f66a8760fc85dceb7e938ba1c02a2ea3221fdb93dcc787e2c1b514e6066599f61a8017a746fdeb28276e5a5a1552cd9d9bb4edeaa81ce7cc43104ff40e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4caf2321c7764bcc6134ba2ba36e1c2baf862538d25629c8c72dca9ac55da07c","proof":"32d5113f1ad00e4539ebeb7cac4cbfaf6e98303f8b3449c5af78a8e1c649fc7aa882db74baf8be01f222334602dc18efd150b367c5b6f13b615731470ca8246a70c28dbb473ae68063834e99ce121fd0231bcab1b14615d0c1439858a670bd4e629e2f6280f33e6580318c2b9cd14a70b8347f287e123bea6a5bc33e9115a37bfc4fcfde2c9246464269226d52e313f69b60b2500015a141066111e01fe5f10f1d3c1b57c17ab7e3d97fe5718d2ca3047dbac99cf4712f34edb558e4e31034005a2ff2aedca04357a053c293e15adb5dc6a05d04389040028dbb5e682a2d1c00a66310f3f28968301cf4677b72d7eefd5358bcb11b2e5b96462fb929a42f922c64e226cabe05ac394ec41fd7d4ecf0ba65164b0087bb61c8d1796ae439e007557a2b07c3b6cb57bd62d45e322e690a923a07c7866c4d72e88944e9700dfee91f6ebf8a0324b412e1e6a007eb65e0863c1f95e1b0a4e28771fd1090fdf7898173b0b5a19a3b57dfd4afcca71a8712e0641bb343005714b8446d1bd2d946a7cb2158d62c74b1572cb6110c45765d9dbe49c5aff77f06a8e2ada5e90112f9c24d54dca9061efe19d009d95e0137ec3acc80b8e67df8d13803a27bd2b0992ce1de24e4d435e6606625ae82efde9d3d59f907c344f4e90c0aeb4169ba68be1b65a5622c605860bdfb038ceeea2895659e1949d3945ae85ddc6092738ddab5c4a322734c656d0eb70d8466c1f14c9d9042de6bfddc864bf97367e70ef86be09d616c36024ee1718ef8e9544b2fd7a5a7f4e706b2f0bd068a014dcb437337adcb8a8c3ca6ba04a523b959a27fc3f712e5803c1757ae2af4529323f52f383f5aea27ac037cc27bf100675427d46ebb62b35a24b9aecf460f4601469ea69c15eca0dcdb08d66a513864f0ecc08d0743d7e82c825599880a9cbdf864b586a6abde6c479f0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e065c34d99d8e092780d785e6fceae0cba539ab1e7252b97cdc5e50441c90914","proof":"8847a85f089e3a404a53bdf6c1976aa084cb907620192e8ced1613f9d555696cd22f6f5428fb24fd887873463f9cc3a62605860de52b5e7b8eefa01b802f0626c6729072f3dbd3c3c7aeb44470bab2fe1878cec67f407a10469b167f90cda9227cd9c218d20e656e3bb4c9c74dc831f7f400ec085d50f080394ad51eba067974728625e02b7b7eaaf9b23eed563a0ab1ce2393bb62836d5a5cca42cf14b3110b3c8b67406581fc94b4cca1f079a7440942c19eefb10366a757592a04315ae7082352e63290a171cc0039c1a9c459c855a072d1b17c9f26e13d14c105fc7c8c0f3e83fd2e9799d434c85593245003d6a70ce4b34dac6e4fbfe3db845c22aa027c9e38880bbbc8696307dd623361c09a8afa951ae518711a50b00e428ec65db952d4df3bcb227ca9ba4d4384d9e330266c3173577f633ef8e753563b8e34437232aaabeaa1bf3e134dddfe0b743f067c23f7735cee6ff8567c190e1a00de21d137b6c5e7459c36985bc3053cbd3d2f05bc22abe7159165255ce40334bd19416273227d573b9447b21da452eb42ab9ea6e59fef07040210e5a3e8f3a253ca92811f201acdfda30cb1c2fd5cd101496acfd04206ff8b0a6df805055b2792edcb6e63b22e5b42c1843fa81e5509c5817b41f0aa778fe5844f62c1e242f4259ce8be360eb0e985689b8d99a0b783c94506cc9a28b6293a785c7cc511bc2bcc43b6256a0ad3803a9ddecb842ab02e68bb6522a195b340ea400de284b838f2ab2d12334134fbe3316f6c2d1a0f39e14f4f76f09eda9ee03232902e540fe2ca5e7b2e7a4598a86e77af2ab343dc69b47f550e48a482cd9acd5d261df9b86c1af171b2035e923042648063489a4ae094204edf0577fdaad027f7fd8bcca6e1599ad35d360bc59b0f7656d4137bb09e98ba0ac09d6245bb6aa910af533464778b8322a5ec05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"484e7927532340c0e8421b31584ad09f588b62f260c6a1bfc439e4f5028acd53","proof":"d45061804394c84cb749102595a329841ae563d260791f5bf7ceac0252ea140ecebeffe7378e9530f1404aa3a9792f4279dd1c3f987b7d97468ce1a8c157b01c62d633cd9e7e741769308e018754b5b2c2aeecab26bdacf12b574a2bad01f351cebba0762df583adcd0b1ace48456921496ef5d1949999322f2c9eb2e5e68d1d7eb7890f0e0d1521c5af9c7442c954b7ae41972366e5175555f169a6ef708c0aae1132175a51390915ae91d6e48a98357cebb3236be762ca6296b44bcbcea90bd44517c0dc5ea7a20e423a0d5a4462f78916f35730417326260600f1bf155b06dc4f0e7ebb96225e00c6bb3509438b193874eb329fa0430cb8afe3cc7876c943a6aecf954f1a0d319b98f9a98d89b0046469dac545cf34f2cb6a667fc089da063cf55b5466773b2f061301ef3ec9acc348145ab8b3327c59a2f6e28ab1670724046270a95e81adefa1ebe9b23bb7cce6d80b9adb20125b93073e3dc267b2fc73f2e41c45f5bdf7b6006dfb69af889eb7fcd3f7950c8656420e524d4feeabbe58045a37b617a2bbcd12bbdd9d24e6176f7284d0861ce79a93b959088987f75d7a5c24fefd4d1a00fd74f967ce460773f11a15467f1932497e9f78ecc3ee68943e5257464f9f669e4d4882525db0a5a48730c47eb94a59e6b831f80a944405847eecc4111e2e2380904b3f2029ae89ee6541f6be7778b3ed06c3774b8a20b2ec3b248cb358b11e3d15ce7c36b9e1354f6b1eb4803f391b0bff4b66936ec4244f5862d561172e2d9172ff5ddd3b720c8aeff8ec65c452d89efa46a1cce878f17c0038c04443aec6e5772435dd5049bfbe7c075dfbd9838bd445b914a7fef53431140dcd497ebd94defea0ce413a0e3515037f4d0b6b94950c2ad9072bf63df4be0f02d7b9492560dc4d163dc76202a7e645c314abc700128f0487d288ae366e840b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b4052b10c1e6e8798c58d9721003a62d85abac0497fd0f8b45fc752d7ce2fe61","proof":"d4ce6df7846b11b5b87b086b429e357feaa0e5d1dbb526bf84a6efc7435bc20a849a530ed26c43232cad9d078c819b63695b344ba384e1e8ccf557f8b33ebd4e780dd1577971761553bbc5dba97b440e468c8f114168308880dfd0cb97b1c76b4616e8da9096cdf056db22d891d5de58e9e29857cbd389fb0405466c39d31d0b2c7adb005f277abaf105670af50578a8921171d70012fcae44e255ca66446f06489023e3ca54046b4480003f542bf2d9486520ba77b6aa44a44b6b00422a990e3c2da0de01b1d960f8eac602573375f6327f3c9f721138810a960978106f8b0d4c24501de60ce9e9b39ad8bfdcb3c18dedc302a12bb21dff00bb8d873f47bf225eb719aab5995534a10ef70729c7c858d21c647dd7aed3ad4aa9e5f46e621f727e9171594ac9bc8f253b15b2fa2636e91d236eaf35e4f4ab68c1a07bbf74412236009967836ede55e16f62f5fbe209c12d4480e3652b207d1fcdaf90af34a473d49650cd425d7b222a9b5392136e2fd804f498e7e83c650adf2b7c62fa2239485c42b8baabb5a566cf5b0bc4ff5c4cbd94c41b934f73be89f8b2bf8c6d2c5c3702bb3f2fedeec3d71153f876d49bb39cda9cfebfa06d98903f592a1a14a66542daa0e80fcae9708dc715b576a198852b4d4376a890bdddc59b3b1f61cd1978401675d2b172f8eaeacfaf29da2e2627636108bece3df8f97c6eac97c9941e581412e0d67b7fc7716b69c54146ede9e204e2df5e1685ec05e69dca9dfc33e788292ef2f60383452b4413a2433391bc9450e5a3e585cab1fe437ef24d0a6c95ec0922acc6e831ad52d2cc1719e4807d23d76df08f8aa12cf6e375f8a1e892e17150dc76006f1b79842679eed88590d417d3cc771652d44bbf0046d73db077746d0773615904fad5b6b2ed0e07361c33dc723e18dd5fbcfa7a9387282cc481332d0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e6d694fb8b1a2ae0161a12792969515ae66075a0e057675b0e324f2a426fc64f","proof":"7cb926b2e4a8b1cef76ed108da10b5fc3fa3cf399ba677f74ea89660b7ae787ccc024e79837e8938b638e2175969d7a8d8b480e829e8b97019a566259a4f820a0cf5f906bac8cd8df174e99fd0b47a0e565af7a6d5dc2dca34246eb09f23385548e1aff13b4c714e330e322c9ce8492cab9f20bf25e03df52287f413850eb90450df4d2c52cf90b631631f2293ac7386aa5e55dc0cbe24fcb6726b2f4bc3170dd5bf1c58e1accf3e2259417f69ffd86191007b670d3584bf91b98d122726d10706372eae813a388d2c886e7f515aa0e8d84614ac3993de2f693d074db62fdf069ad01895e178f6628b786957ca299a139d899a62c95481e137dc2cd73634320d0c13660bd4be236e9e57695b105dd3d6220695812135bd8ba0715f88d48e240bec77349fbf8d2b529c226e02cf6e2d61c2f9a1e344b43824365978366d01714ca812a46d7124b1f0812b0a46e1c6e345efc5bdcfc167218db72e6436fc2ff76aa4fb8b1f7e0b2d9a09485788278ed77cb9d86cefcfde9c981ed2d643a807c3726887ac91e417889e1e2fb5cedd1b6cbf7e5e4d1cb88d505e7b2f9dc2b570af160e73df259c6a212d8d47093d2efdf62728807303c222d38b1918de7a40e5dc163a9b0786072614143dbd76ee3a668c668dc8fd902d5bf6a7e162d4090ef6ee48ce73392f498109f5d813e1cfd941fe1f7a5d45c09d062d5e5bc493476244ff72707cb19bae93aecb1ae0dd88a131ae7519007c99f020d34c8536a8994c75f80cdcdc9d7a4a630d80e26aff616852d34b265e043e162bd1f019710dcf7ac28e7a1a99704c21f5e9fcafce3791f7b4b14f4c2e7ca883483d9c898924e11c2cb14a74c2edf0f1078944397cfa77d2d867c9951bc71c0c7327f52d6da0e86d44c00a3b202c605006fddac7d52bf2848d11a1d5d78ccbf8f0ad56a21c0a5b573c3d05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a8f0dd273911eb16ef5dcab4a89e6f95e3b2c0456769d7fd6acf4d8fad976d4a","proof":"8017bae7cb3155fcac12f953039380363751ba80bd6a421ee3734fad7525d46a5e81180bfdab589fc8c980e9806013a8c1d950f0bff5eafd464dda126da4ab3e760490c7069364d42ceac079e96936cf31998a3339b6dbdee7ed41c27f0a5139705652a6ab0507d73e27fd685b8acec7dc10fa29f2ccbcdfb8420e951d7e3f71bbcc76f328930455bcd460c208d6f860e1f545dde65bcdf940c9f1279c516403dfdbc603593bdc55183834435181ad3165c33e9ad2c395f0e9dc3cad965d6408844477c15bde549a8e316e851d213f1d320d3e5de032264134d496ae64eb0d038c9adb4819d1ca8e6400e4953069aebc4d294d164c915a543f0fe01ddcd18a21ece7a2a3bbf3174051421172f3e9b8e5ad0476b453c243098c21caf759b1130f5230389e36ed54e4f5a65bdde3f59b45dde87d7d36521886f0ba80aafd2a7e758882136fd225abb2892fc7df29542473f45eb7d0c7cc6c9cb856c631a33f6e59f2991169ac62b9e9ddddcd3073bdecb46c88922485a08e9910d6950f4209626ba8efc36e908f804a28f2e5115a6e5e0c377025d48db595ce32333fb4ba86c2238eb38570d92a3b274c8f2c81a69627e998264ae24ec3e8c0613652637375ef4de26ee534cd020dca54af4e51013a408a49318ea3e3647874e93d9be7cdba3e74f68961f59e2070de3a5cd89d07b6767a8cc9b87db1b7908406d8c1e3523c491c847d5f0c458e4788497a9095b38b35ca8788ffcef16652f20667320ebd5a7254a27e46d86fe208dd812d1c67e035bffce89245548d7ad53de16d7efbb993152d94dd237cecb98f5e6949227875f82035c3322aa16d3e5d06c7e03a7dbdac952c08f27b90d5281c76019f47421617dade7138bcbb351a20e82be62f6b0c5739071faac4b1c0fa7734faf5aac3b6ae221e89dc846359d3d6366f1fa2d6b0803e04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c63e7f8256d40210a3ac86ed95be8d9082209c0d5d8c1689beba31dacb425f55","proof":"7e80bbfd98e8521f048df483812d207a5d5ddafcd81b81cecd7336cc87e9c24f36c86546e27f8e3751b11579c0ca98100030dd4efca58ffc249e850a51069d0b8e1e356cd45cbb7c3182ce153e5429a6e1c0511a2b8236064efb887798298b424c9b9d480445ef85136e9425487fb14ad2e0bad90e41ad938200bc7b8e9e633dd9c7298d328051704be93038c99da06752956917b732d1ac1e5f18f13a039f0f7ff6a096f5ab44cc6121648e264d44e289c041bc4362193f646f7023eb1e43092c4a06a7522b3db32595655f2c5dda8de35b017fd1947ecd1c6283991a49f00d2883a14289cd834171b68bc7a509d9f0e35c97766c490f02b6cf05c2fdf08b05ba8a6abbfa8cd571fd88cb9d3fbef5751161ab9a52263e6c81047c5955aa44781830815743d85f579ca7f5edc089c1b4dd372fa06af710e6991d84a6fe149370be8269e3056f25a1ad23134b7d0f4cd62454bd870e0b479afd97c48b40a2e34452c7caecec0a4597c7600c324edfb660695e4c9c1dfca7ad5a30adf950566b3cb075da60438b52ca22208e9ea7e28af5c4695ad82ca72ecc1e9c80a1f2f7a87d5e0f61ff19c820022c2dfc0dc9c55b8cc8281bcd9430b644c15706871abb392b60d2682ade8e481a0203174908d9f1073f43afe84c98bd865aca1414b9c6a04f0cba0f862f45d2ecc30ee05bb3de6d7373b4d26fd5e8f6a8eb5e7b470411f1281af68198a1f148dd8b43e69210d46a6b1a39eb2c25ea35505a8b31f2ab85d44304bae88c1e6c5ddde28dc18fa2ff6854610dcf17048f6989941b2f04d9f2837298e20b1b1fb01d5a6b4b25dc7236e9e6bbe4ccd259109a421e681f41f86b9c26991c4a0b3a018c9af5c40759b3da0a3cc69bb1a38369869e474607135e955507c04e7dac7d7f79d2e9e64b0668428ab38122335aae255a04293410173c2aab0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"04db9a4922bd4b9fd853b685d9e60821ffed9320bf1c6de873ed215ca6e37f69","proof":"f230beb304f1b161fb7647a8a3cfe3bd9c81d2eb2c9e8db9a4ac58b88e85dd56147b2b3dc6dc3eadbc94a49012755e6261a3a24dab9ebff6c262b9caf07d173364e80021914676f68cb1611987c09126c17225af051c4cebd11c3957e49f2a2158a4bc3502db47841fa41c2a33a4e74893202017eb262a65a84cabb7012eb2685e9c9b37476be3392248066a95c407f43bad1c56f1706ba2f1163b41e35ffa0189e462ba3d919551f008540793999c9b0e2b4adf92504f1c4e908b1b87746d0e542caaa69bec92f7cc8b96e3539a202b95963cc5e283b6552173370598f4440248a220c59fe85df68b9b8dc05b4eaf817b51fbe78f4e516ecdc5ea2deed70d368affaae0d07b10fbc23ea7c30990bb3aa7b6e1fc53cada2fdafd39f350bedc39fe735f17cb90c0895a3a79b7d1eb1bb1a5a52fb42884b20f981712be8510a349d611daaf51e1720122456005fab6e68935425e80994411249887ac1ceefa740e7e5fb23c3913ee2fa7e1bf3798c7dd640543bb69ccc4d84c231bfb17474ff92caa9108177e26e16ade19599d5ac2cc9d664991ff4861646592917b426b01a673c8b8718898e18ce9fef5afd46292771cd84e99a907beac37006b9b6aebe85a71d82d49b91508a92e9d5e088d657bfbd7d8430b64862c3e45eacd53046258087a82d71af147a95901fdf579bbf063a347ace4408ae774187a5f139f3c5fb06575823d0be16c635553ce44c63fd9e4d74182217fa2563349038a92aa52a2f57c599cb33245e69cc3e2f3849be71229d712e9604121551f30302fde8dbc0a520d20b8133a88f34d77e7526889b092f94201f7f944b241babfefe1197d47c57885581d6be1b133c7a639fb58e082e52e08c5c99d16f01e658ab61956cf4e03efa00d4e40379d60a1932e81ed07b0b6203c1249ce822ef3598da72cbba7bb95e5ce01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5ee84e01a51bd273725d70a71c2e85171e51bffebf5f7367d8010238e76cfb28","proof":"b81f864a1fe8437ed556d3b867e28e2a9302f15374d5193447dea059d59c8a0efe0e366738e106cd93c0881e2789c300a93ce23a63ddeeac9d4fc1cf4879ae0af0968fb43c9054e77c132aa5c3bbc83540e50b42123fdcde2824917537f05432763b5e84176b1c8b7ddd100a1625872292498696191afe023f7a4bc9579de521fb876f073ca37fc0c4b72f9def8313d7d9e797f1131c24562b003fcdef5a2e0143c2a22dc0d013e6476b94b409b5beb9e8402398ccc9ab202da45e02d714e108a7d2d7fd1ce3c13d0bbc1cca33763a64b37064cc55b551527bdfd4951f7593013c9ae233637e8e40159cda5f87f8fe52ab9d51ff393d0ca3fe2f9b383a7dcd7b2829bb966cc9fee86e6b3fa0029314e1396b09c2e48a1fdb4a77d37d7dd29d75242889864f8c2cd3cd6ce90a33fb8a178f5b1f2d2efd9a6ff398a7276cf5545cfab5e7fb105831e71fadba3dfb156df3d06feba7f5ad5381e5514338c498801f145efd73cb24bc72e0c39d717077b460096c2914674f90d41e2ca4435437d3569a5af55a4ea6a88d0c737f26b53ca755bb7692db0eb1a5b56b309d1e0acfea56106610da4b7c3a532600b847d4915059e9fe7f1f49d21862e3f06475775b536c4073d9f1841cacb4a0b165cbd90c11c80e57955ae34ea44cba01894016d6ea0e3289cb022a1ca6fa757d59a3afe2fbe8f9fea42099357aa5e7c9992e62624906f670e3cbdcd3d9fde3ee5e8ed7cdfb82a1c615b8de649dfa7a52aaf63be7b66bb0aec1f4144251e4f14bc323095f51f68359d0b18ddec2c1f1fd40f92fe787419aeaf01f1c5856ef627d413e8ed8021002476e3f16cf231464e860973814b26df127fbcc8b55bc01730be6558211a7fd1763e82e918eaf90a1efd481575cf6089d427d83294b11c6ca1a555523404f9e64c38760e149fe55700a83862a97d903"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a65995a7ac16ba3eeeef649c7106ab49f390314adc2578d7da7c959e1db7316c","proof":"e0d4705396fa80cfdd9a3f33c89f71471fe5d99347a381d99f39d8ac1dc2ba03489cd8a02f0daadf15a2c689054897dbc88e7810c319437de1ce5e23eaf9b170be48d78601f29ce9a80a0af4c9459d4d70afbc787b6215d811374d724f3cd504d8093c89ddeffa9750e35e1a94b0e7eaf557fb91cd625f318ace13eea0439934eea7763ee1d083ec728ac8e29ab36ce2bdc984ce7385b034d3b81d05395fd00aecab21db63f63f994b134f8f3a75d5e955532b8f07078d6819f4a73bb23a1e0b5ac200e7857b2797bde357d16c85bb572f725371e8bd920571db5821e747b307ca63142c3faae19c469a2bf81a1f6ebc6a8a1c0b274cf19ccc7d3c89628ee92d02b7329103da6c338e9f9ba246cfa55ad150d09c6594d9cbf3b240525dcaed52c4c930da4697bfe912548f4da1ed6bbdb2d49166f50d0baa03d9e5c8d9a45b1bb8f696c8681e361fae2ca6204b580834962e7cd6405105d7d115ee7450621e6170a6eb9630787ef094604f94e596e3d98a7ee636beaa22139497e4c87c1a616e8c8666437ff9e2500c874deec97d875dd8b0027a8048b814577a43dd039f7f02926ddbff0fc8b5b7ea42fad6d4e3a37917902a1d289d90b3d12489f8975b1b30640a514b9a24f5b35ab165d2e425dd6abe5ff34cdd85341a2cc8b4bc06673954888ab039c99d4d15554bef8cb85b61cb10cce4f40b0a0704634b2c3108f80f0950135c99c49f3dfdff9950f47f89bc977b300e75793ad9813a1d92f35056732d5041c11d53c2be24d40592eff9306f1776314b8876ac12a17693d2f86bf9721a385ca1ff5be8a16cef80e5dad5488cf3accba6e5829fccb9389a07fb4b7d8921db8272c0ce209d342a919da83b48f4cf405822c1bc7fb1dad8a679e5188f9c01ab5303f10398fffa9a1e0c132ef16e6a0e181ebc9e2dc9f8228695de4d64b006"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"18c9bba7e6deee5a93a3a4a9980a9bb2e7fdacbaf610541eb1abb93e7d45440c","proof":"e2da2bd80f163ea954701586ec0f6ff8f1204e3f70f9d086ca8e8a3c3285474424e7f7f8743bc541d200e92d9347702bb80631a70b2b0628ba6ebd0b9379b74ee8571a8943f27a2d58675b3826f920bcfcbb74e3f2364ac11a96307a20fad66ffc40c3c1805a123f515b1639c73589b860c3b0cdfe7cb65ca1cba7e2fa842d3d25f9a9ad1e58856bee2b38492136fcf18df36d94a050762f0c34793902693d0102ec4cf099704afbb5fbe2fc3ee67dd0a340510a787e3237a7081f12c208870d1277c3a21c4651d4b8446a3b967ffb45aa2b61fb5942e340dc96cfa0886bc80124e0ab20100d6905c9357a78fc4441dec0fdac31f04d87a6bb1a3e7c8ee0ad114a7fb4c3a0c16353a030c7798349beeafb3e709bc283c72dad6f081c798b87067e314e8afa60176f0435076a5eb5227fdc86d198b3ee501a94cf66ad05f21c6916e1336decd23dac8a1fd68f0bc740270fb708ca236da1bbbb02dc9da47a3e40b8d735c9f734cc2d230a66ef13095eecafc12540495a869a477dc55695a62335141c482cc3c7500de217dcaccb41d8968549424f1beb7463298390fa6b1d6c6d34cea8187613345739263a0cc1e5ecb554ff9766188c31d92c4e6073c3a19f6ab28d328fc7d0af342acca1ea6421bc23d86f55801335c8899087f0a300321c5d341a827bd3d0681aad009c1c5440741f77917a7a3feb3710e2b87a75ede88c7c403b83f063556a7b10c755f0c86ac24b8946c2bc185fff6b4cf9aaf25577ab7e6ce1914708428dc5f9f37fb8aaed20cdd833a6eaf4ea34aea63e496cb592f9548af9e59c568a8bb2013555a5ccfd18be0421d05f8495494892c23fd795f5000c091f46e93ea35774485a452bc1cf631caca2ca7e45565dbcfe1080453173780dd664ee7f4082cd1afd81edbf255f68ef3c1b894e00e3d8f619f9eb5268746003"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3824f65b9ba0f0f02bf72746206dc3e7a8d6eee053f0ecc3599d853a21a50e2e","proof":"6e7d8a76ec139d6c45ada32905d38b53185f539ba12004340b33f9caf74070285e3208418c85e8f464072376ace388d5f1ba682a9d9884752557a47952624e3fbc7ce3071dcf8e4540d91de1d04dee9929183d47ba2b136e7a544d1532a7517016e53b82e103991bf0be6f8f6f54790b0f6bad1f15951146d464da2e00dfd82a1fba035332c304188069347baf4d7dac6a2298870a422cca5beac7878f6a9705ae7831c4c0ff0ae6e7d0f0f5fc73d6af9233f3c348e5450433dca6807965bf0fa7125ddf344fd8cc2324af61cb45d07750fc419db9bb6a0e866927ef380ba20a32c5059a5392440ade9ef9da807dddde168cfb1f8729678c64d7e58cc75da1551094b1663136c02421bfed0f6c26f2c1cbab054cde170b2a0eed09a916799717380a8e9b4b308247c8f26fc30cc080f4fe82fc52924bebf2120f0e2e2d777227aea9791b6343587fb728dc90d9abe22878b5f523c9569c890614d0ce9339315d9e3bc3db712be72426e5cf0bbf5293b0f11ffb293098645cdb8c5c7f7bd2f860e42894d5dca749d63e6252d777eeaf29f4c8eae770916dfaea2729e0bf76522fb4f71493b9447bdec276f5f388e06e00c68ec8b8e5b29e5fda604ea8bf5d8a6f6a713794f662d8b641a662e37cc7081e96583c8f404c957c4876718831322a39a6f9bee72ef449d7ec668a07b2267b10eb199ef7f63785d1b888b567e0cf63090a346727e4f1ab28001ea75b60ec3a8bfaa93eb85bd3d77e23f84979ee3e5962560b24946661104337c5bbf3cd438472e04c53915dbd797cbd5ceebb8b6af81a2e931b218607c2ad350079c268578fa9b636ae2ad7842046e12a36ed1e15f30284432b0fa3f9177a40f6e3358c7e9945efefab27d6fd88e3e83eb0ebf4805f092c69b95ad4e999cc8661ae05c2aa74b844699b434baf336eae59307ce5988000"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"627adc346a628342b821ae8e82ad512bf826e7853f5aa7bfbfe90b5c63b9c424","proof":"78d4b4e528c0a338d445b793764dcb99da6acf7d689d7f74de622714e0e27429fce25d26a015dcbd55b0ea6c2e06c03495984b26f5bcdc268b2cf9ab2f9fe70790368faac379f9f37d09c80df27be3d90034d806646e909b843bdd4b3c3678242c10e02412bc94740699674edeb44182bb32d62d69101a737b8d11b552ee5579f7bcc621dae5e74776ad779ba9a8eaa30b27dd543d91a06ca33246964390a90304cae47dc012c08c7e18cfeb8ac2c5c41579b49fec950b021cf944b61beb840534861bbc116c2478281c176a5d6a045a737d88fef9527b5f86b3a0c850807b0c1c89ae61dd07a2d36d45fd6d17d81b5eda2d81bbd74481c38ff98c1f3f582d79de7e51c18b4f4f26e6d82001544a4ed1b142f0c5b0198e950fe1323e3b6f96359a48366e3df86d46a9ca1c5923bc23772933bfd5838364272d97749d06de5a70aa258b97b7b43f5620991376f3504d7c90d21445d74c7b1adac9630be7674a6f48c90d2368018daa6ab501ca964ed778e62dd9c21d02418dda5fb66b69b745173ee5db5452b14db2cbf354ff74c02d40f4fea0c42f2382e0673442d08c08444f8649f4a7517e9d5c7cc9bd78e546c136c47fcbe6edbf359cae74d9b5ad1d25107ab9d95bdb67980acfe9400046b634f9be3765b2e64ea3a8f858587dfd135e6a9253ed1defb37f1395722d6b8af1e4ae683279ea010c3344cd8a54046b3369255ed6f3748f4447405345caef318dfaaa02e0be6674a83815442d569ef09f594cb0dc735c26a84b55b917e8f0e5ef83e868c72a81ad121ff9d4afeb2285def032fa40540ea9d46867681c1d38f1a3e4e835826506d022a92b9a32002f01eaac215de5dec46bef934572b4463ff1ade29acc7049be38d0a18576c62364753ae808b631a81e367f4bbe6985d17c157271f1fe182ce5e833a2f933352bbb0bfe1c00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"da0bae9ded66f5fb08015fa8dcf56f84bfcee99d731a17c754a7ac38f576377f","proof":"e2e8187cb9b5a378d110194e2de60fdb1fd21889d275069df273eb230a60cd4df2268e537db51bc692a697f975837c97bb9a3fec7041a5e32455b74af19ee37c1ed5ebf90d7644867dbc31694d4f1da14c7886ab8c5abb71fa5afaec16ccd304e08602c7c1e886c2408c65651990b4f69ed1215213e4f660bfa45a06b0dcc926442fb8f9399c2f78fb4e1e05743d57346187626f057ece89cdd52ee81f5e590e02b68cb68cd29de408045b384824f3e9054b879fd283c3140cce34177329b909efdc89bb533b1345520b7ee4854fb675350179af59fe2bdd4a983986de82a005d8cff627b13739434721607fd2eb63f60fdfc1e218c8f1527a06e649073515395048343ef31aee925c4e5bdfd0f3852cca7283af8962a86f002411c6dc6a6e28dec814cf02e28f6745814455d5fc6338a154b19d9e6a94dbf4a30af6368dad7018143e03ad30026a57ef42a31ff4b04ac55b07ed2e6e75348bcf6b76a6e1ff1820823d63a78b55b5073f09c815d6a597e062164bfa42f5c241a4717bea1aac641ce09d91b8ba8bd096daaff251ac21b3df28ee9063fbc63a99d5e08940cc706a4adc68dfb769e88ba14f9cb88a8f128c4ba25ad6713dd8914b9431ed6b51331952692d5fa4f1d09185effd3995c4ce22e347e9f9f5412571e8e0695c2f45925222df475e4912864b83c599fe504c88cf556e82506d57bd34cf4dfb750b8863782291d56ca8040676b0359758ab491aa3919da73d957afc3afe1407375a13b6793c3d0c539e2d89d1bc52b7a05d90b50cf513b300ba93a5b416bf0f6118ea1a54aa47940d9da8b764101edfc6af95c7c049344d4be66e48019e0bdd73f9ba845b568c5577ae1b71c0e5d60579f6b5eac61e32b40051608d35cb5a805e62f40900519c8b132449b50cd99bf9c71bf1d677f7a6dd73496a1c42cb0c7b5592409a05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7818f3eca43095faca02d141eeb6686b97b8aa73de51582b5fed7cc892115733","proof":"6c59b3811d1bd6b35f2233795856a2b68d21d177aab4be2f47e829e64a6fef43cef128def05e0481a57ee766a819e0b2d51114c3c24a593f54f7f529e8bbd64b8438ac099473cae61ccdf7ecc942413ff58b3bf37199ce45467bc6158f98e5210460d360d4dd67ad9cc01d0782057823ee94fa1394cc567ecaef54c27dceb669c081b83ced4d363c98190dc08c0b64a930b4989fc6ef6ca32b36306dbbfe140aca86cbb010204daf459458de0fb86b42b0fc75df7aabf7e8e53c17313df24d031647c798f7f06bf7f70dc576d717d4d2bd2a85cc31fce7858b977de2752f0406e0d9db7a436bef33f2383f8496d4beaffb6772a01005333058fbb508dc0e23214a246018e7479b9c6cf731ebb8334a45e3a10d08d8cd4d0df64012b30cec20746817e5a6565275d01d855c2a91e9ab50144b5cf383c43bde0e6c4a79ab4ee8530afad15399bd999860388b06c3b5ea2189e4152b9478a998c8912e5ed797ee53a0730be8265c5ba0c3a7a2bd2bcf797ce2c60831e6fa6791aa80bd518726ad07ea94fcfb82c1d852b647ffacb2d71f291a8a19238ba9ab75d9e0555a4c304b0a36d6aedf31ee49f0d3c0de810c7d45bde10478f480fc3832d5c8728c6bda0c1bce72128ca7121b80718258eaebda9938108fc9fbf22e69cb409948f3d4d4e5572cb65322e659a6539b982a00ef480743812ddf70d38079aca6c8cae002f4460baa082dead2692dfb458f4e22a0ac45eaa2b5b3d0b242bf4f1a4c35c273c35d08e694a3e5487c66e42ac83883a500565bbc946fc16ee670d40d2d9f515618ab2a303ff8c1bbd5a6ace481ea4e5e345f5ccaaec0c1b2c023061d1a0a1de3239d33d7901995727aa712ef81c31710c60531137ddbb3827e2d3efdda3eb32048ad0152e46bc38451abc35718fd9a581f71612a131e46025d189ebf99a2ce1bcff206"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1ef846195318f254b88e79be735d5be1e7f3cbb8a70275fa81c0ad21dc6f4c2f","proof":"f0a51f8e2fa902390b5c445ed2b969e670d4b74ad49265fbae9ee1bd9e76ed5e2889d014cfbda9f23b0f06a2c469617b67264f638fb078f709179addb0aac87706b9f22421181c6899983ab2130308eaeee7b7a4cb81feb81fc44e552e1afe01a8f56c92e792b3377f44758a23fc684797781eb2e4e5ba3ff029a381990ecd2ee37c554fad7db5470f8ee92ae442b73d810da2eb93a3548920d17ab8b0b0a50c5890ada472d1106b37cc84aa92e7cd876bc443a8cfdfa87779ecfd8815d92d0bf293fd51b217ea3f77b787fca571adbc42f09f44bea46bdabf4d4b174b7dca0ada734fdafe27fc9e0ce29a8e000ed4562954c4ada08cc3559bd98b721a46de67a495969991590ed534a54d8595469c56bb530d2be52217180f2d3227aa285679344408ac02d884183d58c719fb526ba181549b3bc4f143296f38bd46f5d68f77a2d2c586ba4a2814700974b1125b20ebd5bab18a3ce82eb1053f49551301f30628c67fabac91c5b454933f6d8c6d66e1c1590cddfd375682c06e86a58c7f6f2dcabdfa721cf88de5595b4a96c82142b54f955009e652a45afa81db3c3cc1272596f5df39df49312d52f4739e63ee371990b2dae5a2b80052113fe053cea6e9542460825e4688354584fe53f8722ada05603c9afb75643150911d7838e09d654f0020dba6c6ecabb28ed722156f2977759251939eebd84ae149289084c66cd441d413b3e550bb448e7605ba04ff8be5e37b228f54ab556cb147e625db85c9d0465abd2367e1a2d86621f82e43728dc2e2f14df109e4f2c11a56f6aa84f3c1a30292bdb085afa90570d1106c5cd3ea472e10903fb0da036fe75c5696e33695b328f9bd0de48246f1cf215468773145adb7cea0cb38d4608dd90a378f72fe99fc053987ee53ac0185d36ee3a8eee3fd4947c70043505b45d347fc7c0ea79c0e3001"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"04d0e1c59b378768c12aa06679ecfa7449ccac907aa153092efa263ea31a3610","proof":"361a6aad16b77c17afd1a8ff61fce723eaac548f700b4ea2f6b44edeed3b8120124e4ba26cd75584ae69f215ba0abb8b72dc363daeb4081667cc9313e4e48d4562a742d4c4bc465e2c38380fd138014790d10d1b3c8b77e7bf064ac168a31835c25297ba87e3dcd622d695c13abd257976d6f7b94b9fd95bd81bdc5fb664005d929f35faab3e227b6ef9d7c42196a351093466b0eb5f4143c6dc971249ad70026b81baa8d96fa4b3c39b595c1fca0d4f5577c3ee2c28f7edeac955b3907e85077b2447c243cec36453fa25f61ab8a0ed8675e8c620145ab1f3eef2a44d83a109f01705314261c784ed7e0389e009fa6c6fe745371e4cec391e760e552171e97f36445cfccc0840f775fac56515f1107418a2b347d90e834d9427f2d31ab00d384a9d1d2d399d4f92cbd4dd45f168558268a37dfaefa1a51727a4237f9e2da154e0b2de8acd2ff268235c3871904f6a997d82aa71f6893fdaaae0ebf3f4e73a74f80b9d808e5ba8aa3d53cc3a17176550543a49a3c54e9873f4ef2aa4f85eff01d26157633925798bf4ddf2a55ec1691dfcd3ff5afc45ed40db3ef7678adf3f16288d95ed591d00f8b2866de8433a9d2c1141967c7df19fdf7393b0154236523dec154387723ec1623fa8b74876ded30be81e49b68a184458706740a98e535c5e1cf310eea364ff5d5f181ed010bea53866d913aba1a3a740941d4875ce3aca531e85f01fe4a3e322b9fb0c4fcabda9c626e6ddcc20f7a762117578172f0d42533e9d5bfb2fab919ddb43ced7aaba504665b1785c384c409ddf938a12ed80753bdcb61222251cf7e36a53fc8e7cfcfa68618027fcef9040e6849f1a659494df6ea4c292d70d973b22a0f075cc1803bd380d4edd6f8e104a53718481160571010d0822026c3fc98dfc8ac7bae64d5c9ff6bb975c23f75b63c45e5f1425805be505"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"126a66e4cdec6397901a34e32cb8d6d37cc1c6df06154e819130a8c59f86ff3b","proof":"9a97ad3f2739ad1ba27d86fbff787b3c3d3008a53efea954a153f35e382a183b9637c968a4ddce66251793900288e1e30acae69c6c9d3a6d9000435acf108f07ce46f8853f46a3fe3dc244a1266e1599323932fce4625bd25e1a973d6fcad527a47547e6d1ad91f1e2be9991d2bc9840dcb8e2aa4b3762c7381edb4a8745c261a20de8e4b9213bdeff64a7ab945ae0a2135f7cd20de62ef3a9de3c5013e31c06450925df8199cd9537c4b4ff8e52368a7c8b433919cd42d5f7541d8a9b2bdb0deb14350cd04fee7e1d25bd7a834ed4327b9f053717b9d55025cd7a17bd9fe50a2a5a6151231afed9715992fe6370d08a276535c8c172927aaaf76b728ce1fb207875257e457fb993f8c02663b505da753531c7b80d4d89045ff5db760a5c737b50042354e45a2a55177064df88b6bacdcef071a3616b50a28a49c78c723deb7f2273f731c5e20ab0a319af0a52eaa4cfa6f830cb8bb61316fd65603cbab5c2527c923423436cb211173a6046ff66d19fa172c6eee5a0610537c88500dca5921442e7ace77f44f56628ee00ea833751683a3953367fa7f55a5a2a93a281304e4898012185d99eb94992211958cae1603414d734dc71e56d1cc48dbb8aa85cdd4dd275b911f24be3d446d426c7587f19960a80ee6efa542e666b07b747fb4af311063d515d366c00b3d02c08b9ba4da959c6da67d6006ea16094074464936ebd6232c34bb1f421c770e768d56b1a224d6ec32256992926c3168d9eead95dd91670b0067234c1cdc2b0f85a6568f7686930afd6e605c31299b62551141d9ad478159ac9c813105a6c63b2f848767d78c78651745e9a755d3ae6c97a5143865ec55866e29a2694aad05c062deba45e960852f9a027ebf4bda76da27e945f1ca869019207c5f0d2093d685a97d4b3d388f73c3636a82441ccc78e91925d921f356404"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c2853c13c2d047c62834fd9239508210d4b1e788c2ddc60d2f3c804076704f43","proof":"ccf395bd306ec856b25a4a4fdc6d3504706828f9110fe8e48425aac5cac6102478c732d4f9d861969e04effaa51137b8bd1a2c8b03d4a03f7c398bc0c118f57e92076a805866dba8b516a03a2fb748bb5e9d96d774398006613a5aad9e91502d9480c8c64fb9b19ac195eb752f8ea23bc4f51895e013d513fe27c929c5282b6f701aede8a431d9d6defd579096a021f418e4987767a953566e11118907075409d68d22a38311b29d395f741c797d91657ffc44b73a99986795f18e338b3ce90476b56e97b13a180fc62bbf8642e085b5d6ffe9cc5b8fadaea0efdc427654a9067ee168933f99f55523b19fdb65b4b0a3d8ebe9702c14f89dd28321b51cd2ee20f811e83051e17a19443b1d4d98bc9964951c328f9fe9d5679be6409d07f338066e68a1d749b7ce249ae4670146148dde5181a7d529a7442a1a9a50cf8f12b87a4cc6efda16e3d7d874838d93d0b79ddf0f6d714adf28ff155a445b95b21edd26e074e9294c5347ddcc63168726b3581f70ff2c4671e2411238d5344b1ed5c21958a7cc5a26232a5c41e7c4dacff28ea5bfdd133a5430936dc260ca17038e5964dca05e2baad91d9dd4d9c2287232bd9877dd0a9962dba356adb08d4607f00776accdbe71b1ace35c492f3974ce1a88d76b6bb5394ff278102ba22a32da5b3f1370d819a7003684438911327037150117fa14733ef74122396f2ae0d9ff7f9a437c23e854ada1ac41c38aa6825cb878872c42c770a60f26e3f95e34c7596044137c96c64a4a4f3f1351ec0df1ef0d60422a4ccdead8751e72b37695e516352750262c07731b97a954fe53d5f433158032f1d409be801fa92e930622f45cc5df4aa6cf8378b9f42282f5200065eedc21c7ed11885437c0778dab461f5f71d2800d8e1a5140d2e54694878080d561e005b84518e0fd5da6edfb5b6cf0ff8da2e205"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e6483c9f3824aa28a94cdd65ca778cea1b67952ce39d3af2339e032c417ffe2a","proof":"c05bfe8561da931a0d64b5a4a9b1cfa033b0007fdd89b4276f155d266a048047ba1537e9e2fe225b6c107bc1206fc3225c3c1f5da0327c65849896b2efc1021b5ef7f59a941806b5f1f52f7bcae4db8edfadcf35922bb55c1573d9b35cd2a03eb05ef597d997372d0554b1a97cee869a36e06ff22d29dadae1dc69bfe7e73e59367ccce34ca3bdef74abc44b27cf0b7e0ceb623a203a00653973c2856bb5ed091473f656651bdbf3c2e42882ed45f1a149362e8bb728eaa0f35e47e3320e710791d0d962435b073f7fd87544dfddb6eebf12ac6fccd7127d067c10d7d66e3907dcc35487903d862f2cf604d53dc2a67faf7935f1f24d16915e7c9852bc8dcc43923185c617c04924a5a4c1258d8a9c88fa26840102e9eea2e94841de4507660726629aadc5a7a6ddb2a9975a97a20b14c855b70a063f0a5ff9e9a1097853554364c729fc483537ec393567836ad87c69bad3b4926947ad7cafa319ac078be660d0c6ee9d85753324a701d945e74ceee5e40f637203d9ef8f00a094d0c8f6b25c7c407e0a5fcd613c0d973c3241bcba8234196c9e16547977d620659333cfa233847a055f0e3f5a252884d296954e93905527baa8d860993518f350612e0d9e1ca4718d29f7cf6b8ab890fc79c6ff3730ea94426450501ace5bbb56196a957c5a16888f5f68befc1390ee2091a464dc266b1cd84f636cd60c348bb824d3331b7c6c3f040f2f94e64421c147497a82c67bc9fbd2f0fcf1cc616b4857b9960a9c1890d0469d4f55feff9a4bbc27304dc9648ffee194bacd7be9668a353edc5aad7034cf161cff0018b8e339aa5de07a0b22e2bf142f8a3268a5eaf823bf92f1567a71417a8f89f6c1f3744bc5e38b2dc2fd52603e416d7ff58575d6cbdf837ca2021e476c7785cc658d8d7ae94e7e64bdb602e82e30b1b8521e1284802e0860c40e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"645c692316828eaf30e63de8d98b270bb3298a438e1c782cab0c3f84311d0953","proof":"045974eaf3431ff9cac61eba12f4e3e8f55e0628ba78623e834d78b268ce7201845ef895bf7fc006aa6ace88f43864c897fce7c8c257a768abe8e67330d5d027d006d1dc5cf54d13b36cc654922be8be955e83701ddb94ad28d96bddacb8ef2c96c95101970c8c7f5bb93bd376224facf29e7cd2ed5ee32c8e897479cb03b55cd7629619169a3db6b947a2ed107ce4e8a55c106474eaafaf9098504b95d31c035f58f6566bc8598e01432aac224e6fd2107c352afb85b1f7f842d294e080d109465c032a3ad14cd560a9f49248695162846ddca7a30691ffbd14ddae6773bf0cb8411587b7258079510007c97f49e5552689e7bc7d364e943d4126b1cfebeb498c30031b8be5aa9124f62ba2d0e0446edd19ad829e908bf1b85cc07ef4387d439e24e0ffe6ea07b5a77a3ade745464ba00fcee80cb63dd59c30b5713f4a3e019d6d3c909067b1818127954727e7028964eb8882f3e88c732069beb9a4a67a01d94d4883472f1f7e7061d6b623ec76fba4a38654ac520b8e3befba76a0b780c6b64fe4eb70c6f2ce7b1b8f0f4b30d4f4a17b5da005956955a9515e6d75d6bc44d9ee25fe6f85c464e892bc2d97bdab3f1fde2e5ca7219ca5ffc59826653bafd570080a2b6639d7a960a7d896458f3fe4d34222b2953ac4fb3b9aaaffa5df0ea2c2eb2be0273048a51ae553c1e9897bf4226803d09e7112039c941c37fda506b7f307ba49666611b3bb938457041ca6edaeb28b9424bb745bfa44fdeddf7e2e363d8c9bf9ccc03d387ee20835143bc57aabffafa6d225034d49fb6026b08acce3cca6904eac1a6fe27750b8cf95840483db73bc49500b33f8dd6b1c90814c21c67ad1bb4ac3a39d7ed6ead30b8f89d066a9ecf3884f35a8a1afb4cacaf7f4cb906ad04a3225e3ba977e7410eaad55b39e18d85f0ab992b667d0ae61b536f6e730b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9e7dd75bb58ae9bbd1f4c0a45ad6db9307e66ce1db68cdf0eaa50a44c580f316","proof":"6a619fe175cc688bb66ce507fad893e70cbc0e0c25edda623e8e24af42948545ce9895e26b43601eee72f6bea6fb09a2fcbc99c17f6693db907dea41c0808868b6821138ce0b15cf994f5ea77ad2ed005fa149d154b411eeaa12a3654627c324ecf76d5310d81dc90608d3f3b9384a3ce3895ef8df97b8bfe197209f388bf62b164acf3878d4ac2cdbd588b04c444a1756060b85eb881fd05e77928b75fa79092c198f0e8658d4272734c97eb834a820e751cca21b8c01d29307e75530a8cb0774c26af17388a69cfca8614e9eea503cc4d867924db9fd4e9d32100f097bfd08aaa3c2ac5cf08cbf3b7b2a8b8c466fcddc7bf64e10743ae5c541746d784ae71302526a9d499779995ed82c5bbe08f7065bb956e118e077341cb99dca567c6e0860aed6de5023f473d1edcabadb7958d13892a3a6af3ac6cdb5d0d51842eefe0fa056e8c55884ba7784b98cf2ce60b28e523729295dd2d165071b25c047c7f76964ed5707e2ba88deae88af14f692297735dce400b17cad114869027d8011c845082065f381cff6ac54a0889667c7231e190867a3a84954d537e5993efce3b3475c6c4c55c6dba518b6d535eebbbf1767e531af8dd42bd2bbd83f0675bf3da869709da6664b68adfe736acf1f2cf17ddba8acadfa49522b025b1d2c0bdac08042884e59be136e86ad539d7886bd33c105c7042f401ed744b138b3972f4b09bc1a14b52ad04039e8fc7bc9e9243a711fd66ce29a9780615faccde7a971c4f8902fc20f92b843bec391c6f0758add6acc8381bfa428a76aaff5c211245ab0e77053be89b20fcb02c8528242911be4807320bc8f2af0007f2f2a3eaa9148334bd02f1345b6d558eeea3b5cc6980e71c0ee39d713e9e526cac8383492c2283544aa04a173e57718f03d5d7fa64c6fafa16eb7a24841c115de3ac56813cb23c2075100"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e04e6490467976943739554d2ea0e1d446df4ae34fce0f79a4b246b3c6211936","proof":"84cfc1854c566c55a324f851411f788dfdb3c235b40e59da4f0c7d45e9310e1932fd0040ebe530e3d1ecc44059e61479a373b4a4b976f29f0f83bdd331b0dd73908aaa19f359a8686520b4ac518464d59f04dde044e7fed569094dee2ccf4c3830aee3a5ef954e26dcc36c384fe20770d41307893cba22d51341bdfa1985ce07dc0583144c9fbae24ffb54f16cb8239ba3de98b35ad93fae54e4e3fa41188e02d58c70d26b8f163064dccd8f2d9c9c163b6d8c2337b6a2186304aa040a95500862dddf8920bf0f6466ed65e8be10dde32e9e5c8327626a25e57c147406547601046bc8e3621a20d68af5f6ff1dcc71fce9ce6c6b56e4dac05f041b8d8387d93c448b84d34579429d8b495374defd1c463039d9ee7c7fbc237bafe7336c008b462ac99525506f514c3f4d9750c9cc246c3a511da3ae35c8c20ee0ea0dbad04d478c3aaf4ca3ffe1d30a3561105a64aab76636e612116bca9b0fe961b53f0fb61ad6a61668d5a7f744ef404e52723e316b96d8f402b3553346a06466045a65750a300a570686fa4ddb26477d9e34236a1860092dca4e5c4c3f01b4a9a2cfaa3e5ba4b44682166e83cc04980b207e9a155ba459e9eb65d18f485e2e26d6f7d00d226ecfac56ee69f27f9fb5874d134e33220cdbe18a711f87970a31054186c6fc46e4fea19f45eb92a239b0ce11ea0ba1382cb65e336bbf2a955ebffacdc8345b2ddcb68518d69bf4b8b36340f5964cb468a481b3b962899d5d9bef28dbc228380098a934a9a48627b10044046f413445df26f8cd0cb1c91262c529a7021b0018438edf60720bafbdb85817f748a4f11b4a911263cdbde3c30a2735b3165936ab0f1f16bc4b71e1f6ad97aaa1fcd7a44607da4fa333a00cfe597daac15c3ad0af08cd3fe3894310a6c57a042fbefaf4d850e877174083bc51bd6edf3596fec27300"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6485268d22003ea3e87c11aaceabc147cec4fef632de3622ea8dba4a090c2e2d","proof":"fa9c8678a88bcace562a69f77a4cf8a729015416477b888418ccd610d465035f44c57db82881d86bbb32cac7727dcc6b8edc1a80a9f39b6d3c0a60964a554567da2c29731a801e3d2dcf66da9ec69787a3465f533701e02ef8785a54e7ddb9768e83f8431452b07401d503c2286b6a706aa5b119867a8174a36caf41c92376420d5838276ef4943460b94fdbabc731c2542534807382c2672c208fd924533a0951fa680dfe98c197310c5c437ba962dd6a11507f7269817c3d20e701d06d3101a7530ccf3424daae215c2df2fd5abc982736a8c244ad81d2cbc88a7c16d9370b0ede95cb18e9274e233a69af9c954e754a0d4bc0ed46f15ea6dfce7f1bf9ff2b12669db2d22e37d327c29672edc2b660fc3e49d6ac14e6b70564fba7e7d07041781ec85f33c8fe2ba16bf3a959a761aa4014b1719908a0b67d0ceb3c658577283451cacd03c1d2f66259acd80d8fdbd768dc21d6e7f9c25173e3933b11376a50e254ba0a169797ff109da146a0efaf0b0e663665c4e02f58b862a16c35fe22431857325912b13c90ffc4b310c2346c4a3429100e40f6112972aceff045d9ac5e760ac5c35590006a69939b23e29d6f83b715d81fb993048b1c69156053bb4d133802f009ed486bb518b614cba25b1d10bed463bcca66b8878eeafe5d8744eb4946906a2506cf5a70b04d99895e2980d77238ea85773ce8d58531fd68782a6e3692804b5e883068ce87eb312ecb426daa4c5070ea1056903858ad64592da671473ae982638f1d678a0f6fab69f1a46c2e79ddf93bbe38a949535a310e719d8259c2f597b1a2abd86b7c449465f0168c8472748cc80b3d403b8bea9ea77fc8fa3a90e74a281f334e3835b670a41eefd82bd7d36e032aa09f962972ad10f2fa0f05e2e26b55c1a3787898f42e8861f9e90a9a6e0ca2f74b1deb77cad0f4dde4ff0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7494aac2e3dcb3c311142b285e2df0a1b4139e0f60fc517e801beb0d2c33fe53","proof":"4e44ca28e75276b3bce7d08620d75aedd7f226190f42451db0a3b9723829ff70546057582e7e01dc84fa772cab3d7180844de89c5f582ae1560520ca95ef3444488f2b0036349301face9ab16ab258df988fbc0581d4d093a7d6ccfd56a1405d26005ac9c4d4ea83311c7003ae146c01662c5605d92e7e3c82ed984f8a0d5d5b0131477bce8025ff6bcecd8d8a2821743f05bbe733ebebc0b11bb1d423da3704b136e16c019b1e0caee249f1917e7275f0e1f240b34f1032f3ef6150d1d326078cef9a37a179fae5150014bd959bf890e2881a782f56b26f50956d4e9fabdd0dbc404043ae8d5072f42ee5e7fbdfb38addf0e6c8603e9c79f312ed89d492ff4366e6818f7ab5225e326a301e3085115a1138eae163491f0d73cfc14ebff0cd7cca026f4a5e4653d63af0dcaa1f1f93ce072f604d9bf65f36d694d58ab6be32008ea6011795ab44caabd2d7ac9a09c79a5d990c3742a3b799f932552e51517e19b21b6dd4491ecd73f11ea2e6f3b515eb2d98abad0ba71cecdc1f24507886ab0d84079220fe2fc347ec9c340e227fe17f67b123c77f06be8fe18dad1bd971f449f4a517abc2a4b0ef4d74a85517540dba7fcfedf4a669efd1dccfb628b01dc144e0a0ec1f9a3731d217f35ddda61e2f9255c9a07b1bd90b4ed123e16a4cfd7f270c1702541b52dbae7d2e7dc13521a2da0f1a1609dfe1f24da05e0eab86535d1dbc61f236db2d4e5ea3f676f6bb47e30a9a98fc8b42c43f8baf104ce98e358143c41c6b2a12fd93c30f1e3a72c8141d5b13cd56436e90d64899805ae2e051c7135ad467840d63fa014e5e091cac87402307ced71f61b172b3828256cc2d8e4a770a28dfed7c16c2a8505457b92e6809d7e9274fe2b5dc7bc3c46454026207640d377fcd81bc81e197a78b97bd40087770e22b5c9cb656d38dd94f2002fdfb7700"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2627b9dda178adad91f49bb9677d8de60a30bfbefe74bd0091f7fdb8a65cbb2f","proof":"383997eca5e74938b6337a9accf3ef88be12eb7fb1fc76af5068b63a7d7c1f04d4041e66297dd023ef9fc78ff8cb09e38923cc3658b70ca1e12ced8bba7f924aceb1e10a3513e7280e584a7b3dc1835aeec56fdbccff0911f118a085da45f463906a0b1c3e79d6b706d941128bb02da3c5e68d459c98e2998ea899da08d3a93234803b6b86760b16a6b6856eaac0655b60edca373ec0f80660a416693a99b30664aae5e71ee6cc427636b4b3755239b0da5fa8be822db48058e96763260b3c07eedaab1d8b24733b3ad8b0a56711c83e892ec2994f124aecdd83b53748cefd06aa2fdc1fa7316af234a9b42a6521d62d82f6fe8af59b4031c5b19862279ba31db43f6b20ee4c649e7ca4011e7b2ecac6f77a3ec9185b394b72737ab1a952eb4a4a65fbbd10530010a563afb42370f8bf3acccee6e88542a8197d96d9dea1a26b788dc706e894755719eeaf8992f8ebb1aa5a557cf174bed336dfabd0ac97781efeab09f9d99c3165a4c82622cd63705fd39386dd7803226ff143d078309da3467200a11588e0d0fdb0d59d860ef77c68f274b717e457c7193d75ad644d4cee45ea2be3502e3066ba680efdcafccbb514c2cb563e7460a4dc1092158d84b5396d6837a92aacfed5c32d99796452d96a8d47b78c5d999742a17693e2da8f86e73362c6e78548b9733ac92298cda8314b92bdf6fc9859a2b633057d4e9a40084d14a6d83c43d7c9ab24a1cb813f8edb6c8bf4ae53c1934eca79ec763870c4b4684de0229eb47643a1edbe62712615eb6879e358bc3fb0b40587ee920fa082dcc2767408368d4d115c3e49bbad4fc6c26adebe5638f7dc17629de215b0940ba45563c7d1adb5eee887457cc5fb655bb1284429f986533a7149b5169cb204e232940f49be54bf911a6bdc5a2bb70751dc9531252615601f8aae35b7c47239310b150b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0c3c6b69797e6370e00288d4857ebaf9f03e42198ecbb17bae98d2410b960f1a","proof":"50689b8709a9f9314a8365056b50eb4b1bd8a814a340e16cb0f05db73f912520a26fc5c4b49a25c171cea92fb4a37a438d2746a933bbce1b4a8c744c78d01920fafc83401d57a2a082253a3745040871fd73bef1a6cdeb14e21eec99893dcd0aea611ff1e86cf847525870bdc0060c37a8f81da7bfd5403f0c231361f790c46276d3fbf0ec844ca124365361c06ab356359b5a3f6b7a0f724bdc17ba266ba70f482b266cc43d1216137d3939e765b8838c92d21c2926a42402722a026382f1013324c05423d76a7e9c3560ab8ce719e1771c53e7ee33570383fc854fc96d120deaee0301a048cb8353714b13ebd4936e099773d0afb491a21e81c3aa0f948840eeca53337be3f22609586f7f067a8bdf2cc7801bbf051a389e0446367e64fc1d005e1dab7b58b7026f896c325b92d4be3dda82ab23846d92a29d7bfc6f3b38128a880936bd3446169bd4cbc2f523240da77c5f199026c2fc2e9452240d06255122ef79bd23b81820a260c928207ebf1a0bf0d9470810de30b4a686a82981040e40d003184a817ce4e713230821e96e2570014324ee7d239324e9dfb5c9d9f05df69d9dce29e589e1db64acfe4c875dc98c5a6b4faee47d20872e312a8938592052f20cddd72bd3b091e9ec52ca63d09b5ff325b19722cad80a386f89ba6c726f4aff9bac31442d8981b4d7a2524a27aac3b3e6a6393dbfd62442ebbe8f75a41a262142347e2b1bb5efce34d0eecd27d31b42d46ef4d0fd0ec7152c06d007450948de0f949b50f3985d60e8f07ce034e028a5ccf2a02b7a96fde4b7038ba6e5125cd9fdb3ebe4b0a137061a93eeb4b67f95ca0b44c1ffaefdd5468bbad290db1c2e05d0a9b2142eac34c08f5f2fbf5d03a1cf9d452ed266c8cf47890d59c79907c506e70433ad48d2b8300e4e51733248909b34f40f3efc237058b6de6eea960b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3e5f3d9529cc9ff4a69e570bf26747851959b8919f7a4094b133324c87884e0a","proof":"9ab83271057f44785946631c9700e7beb8298b56f4bacdb72c9c875ec3e5864f7837acef3b45dfb33e06cd3130fbc97d1f108cfa25f26808d992334e15949a04f8cb1776d439aaa4b4718ec990484aa98a7c82a18c10e52eccc0991d0851e850a009528ada3b9d5da82ffdbf3a6542427dc194d03b649dda208b4031338ed978959630e302bb9b94465b59d6610aaff4edac1c0576e0cc3bb37d1cf4036caa0186b8e8eeb2077fbe49b428ac15f402001e821e6694f4519a3879b9863c5b980bf6088e35cd129a409eb3924e9fc6966609bf583e78aa742c1572313af92f610874aac69c896fbf43ce08749afc7a1fe3362354da6e2a7d18f1e565eab13b847c3ef992f365e66c7486cb73482266f97325a9e343f608bdde6bb2cd34f677290384faf695a6c149560a33afd73cc99f3bd7694fc219e894b4254ff45120b231239662ec3e3e7e61e8fc13dc2f247db724bdfa70bd65a02a51130a593280228c528a935ade3f4d6f12c41848a1c4f140909eee05fa46caf2224d0c5d56acae7d596455a84e1b4cae7941165595c057b9d6ee6532846cc1e6b199a61f825116f234f642d165edcd5b8b1d23c35468847fc1f4732ed43eaadd5910489b7166ea841686921e70e503178437831dbb83ed13ef38262b4e0f734f7195e8db5e53fe9d3ce4b0bedea704aabe4724b1af0e0e351a109739e251d8ae4e44967a416744bc46a6b1d8a7f9e73c3658c0968ac9d415090c17ad657e03c1a5f0040c185b422d43eeed0245d568f531757b3063c7fd50caccbb821f12b42e7cb61e83f3c7ecf9078a67268efbd37c0374fc13104e2998d19a608bde56396103a378d3733871eb0596d4e0a911a2697664cc2ea94f9c31403a6ba0942ceb8a669554d14a7cb34e0c2262da16f6816eeb7ef18e91d2e3ff0d5bfa5663a4ef5eccfacec8bb9c1a210b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"947960b0642435a3f3e8493aa8727929e57ab0c8e5b051503a68b568f0574f4b","proof":"ce742e34fbd421c0c5f27a5dc44667b78613c9f93bd31994cdfe518a118ed84666fcc0f32397e342f310a5f8b3b57c09edf0f7d63cf6d1b968b74cdbd824550094eeb75da118fecca682a705d95147f3f96cbaa3ad6d725d2454229330cbab4db063efecd681183b06314b632cdb147373facb9fc1a3d7057ff049fc47f00f3eec5e75e8227de1ce3d4628f4a7e8393208f9c654666803b3fb38056f61d72a0610dfffb322f90c3c66152c13fa8297ea855d4e7ec534ee0604eaf20bad6af402e3a5732f45fc3bb3eb2b7eacf00f4dfbe4d1a5fd62c8949e6abbd8f593252c0ada9492c157949e998af4e5dfaf6d6be568442154973448f0d87510853f864c50e266c02ca5faed9aaf60081068643b1b957d5bb162a14226643c7ada57003b1b0cdec232d97574a7425412155ff3c6bf44e4854ead9d1a0b762a6237687b57026c0e00cbdeba982ce29baa006b2318d8dec4608eefb1a62d3675a47ad79c21510e0ddf8a069f71fd93a5c8d4c1fe11e50e36992f132ae350c171353ff106793910b16cdea850e4114de15b411788879563a24719a1f127d86bd78c6b44780148faebfc4a24e07b70d158f20d2999a60e32bc9bfc72ec0d1e05e738249494ab57600b0aef998313bb4eab683984781257dbd6ed28c741d454c0ed59939106a4650c7a504b1f0162a40acb483b0594105dd7daef1cb78aa5197c42ef9815e5e450beaf7d9f6228410ba156f9e85b56ec7964d9ed715c01f1a87267904f9e68014e2ad5c0beda58acb045c0255bf2a84f004b962aa740cac27aab5652b2b050234d54669fc0931b82192cdc0db5f466ebd709b351ccb0fc15d9b8edac3105ddad0d7470f5a24d01d78359612855da26ff59fb9abad5e854c6b81d10a7dfadeb6c0f59937e73f5953a906aeff7cb488093608b5bb6f8ff3d0e1bfb1bf044df2a9c05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9eddf6b8aa543ca4c2f0c0412cc4a47a881164f4e2f6821ccc6bfdda2f3afc5d","proof":"44036b4890eaf5803eb37680e1f4b6c3cbba448657d9d3b1e4cb1c105f247617969d2d865ebef7d834b10fd7dc65593ebd35145c64a1581509e7ef9e978b1f6f70fdfe1312a4e7ea29041807d3fb9c4f073bfd3b96aba14fd681e61de118cc2a9aa0b03f9b75ee1c942ca7321da2f6556d526be3939d85ec1eb5929f093575365d29f4d686da8e2ca7fe14d98396bb2dfd9f35092e2fac4f769a584960925d0a8ab8c2c130344d96554276e6e57c61dacb2fe3950b2a91fc48a77392e19d33058a0d2a02995b5e850b9e1688c3ed3b1b6424482480cba9957a585a957285b301e6f24bfb258d5ba0f0ed26fc852508ed46b7e82cf244120fb0caa80aab5d301eba300b244fe65e5270002483cb267b00431eaa0fbc20415e66c7ce73f2ec0d04c25c61b10faf9458c7d59e7aec783329d5bf272bb7b43c436879a739a781a06eb6fbdb3d8adfc1fc7b49e6246df836d93cf8832344a191914ecbaeb5b7084613f447772c67bb93fa6ed90893e6f0202890073cc71263013793ae8b713556f6324637885c38aaac308c591e39a91d50b274eb26da6f3928859c679b6db2ded229f22ec74f3cb69ea212b18502f24329161600a27d309069766b16d7433fff766b1c6742654d889c17d01c8e1d8b3db7ab536c9dd64c0a27663ae82017783eea1b4e61e21c721fefb305e66ba75ab6da1db283fdef03e7d6ac7ac01f31ff7a9d387e7b30f1650a22c9a57c045f3626df130353186ad5eca2719249d502a7a0bb30a07a99737822a973730954fe4daf9049ee42059191b6512ac692a2259e573c7c727a739bae140643dbe2b3e756f7a3cb74612a5bd28920177fae45f17169884b7446db7778f998d71886471b72c199e8585fda1f760317988fcef1030d1dbd08bdfb1eb806fab791135b25e5813a71bf3a17917435c09d8fb9e0d0357f801005"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"de06d699b27f5cd9e7d006bb37a57a5060d42c50402b97919b1548d70b50f42e","proof":"c6ac0fd93f6c2d788d168374533f8bc73ea7f27915c1994ab069f840766deb5f46dcf33a6eda9b4921edc8bf819b2896f47b1ee837191d37856f454d52713b141c737234a54b999ebcfd1619f335e7812210e38e5afc5b65116bd45545ddc757ac642981374534849057852e22f04c57770fe7b4c71674c1926785f267704947648d0118ea32694df898f76a350de1f0c9084293b9467a1728d0bfd41a5f4a0ff1fb19dfdcc6eab4873c8e0e1a446aaaac3532f4e13071dd61772571eec04f05bfdc7d53ca54a69717a1e71905ac684125c41ca50c5f7ee01f63c64b4cefa30d1aed205ad242dd99e30a0e794c907693cdf2215b97db1b16eaa56ecf39f0ee515a58b9279e1c682ea130a048ed48e7de220b9be6ae86e50e96ce9c2e48beb91c963733b40599eddc19e9b4f17a92312076360fa9d01c0dac44709e683f64df6c6299679adf17d063b98a86a9932291b39dced50b4ab83f2737c7d7c3e4e1d843089fc4ea6d089d984830cd0fbbaa020fde5d834eb5fc9c272e869ac6a0ef313aee702fb56f38a601301b0a7779efec379e64765d78dc6915abe716dfe0cc00781c9191caef8eebfb506fed49f8f6625e24ce852f906b5d36f50e2d9ea317d738bc36793583cefe31f526a85b4da5d6b8f2d622f701c06bd75a84f880606440778859e08b12c95afec9e70486d7c7924390f1213e69bd8add89a43a1c53869d283e53c861fe8be6c286683726a7ce48f6bcb6334278af7ef25e130c22646631721e899b17b7cb91dc19c4a29b44b74cf481765c3480ea807458d9f49457cb123ea8e7f74cbde0a47f3e140163ee1cbdda66abb40ac8c0b362e0886a8465fa1128cb4772266fdcc8048e83c900fc94ccc9bd53d2a459f40a7642c3497fae03f3017ad207224ca175f29e612d385954a841ee4f2a77b08f4f63f2cb386b2c09d408"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b013c21e95f90d79fa753f84bd50994166ad71997036e7ca13af0dc546e38579","proof":"2acf6ce563bcc745d070f2091afecb25fc698f58f76f0183f6d321c23e86242d448a0f6455508cea69d7b382cdc70f08dc5663b464ea36b6aba1ebb216e1b34802b4d5df2d17dd2317c53a17a523da8272bda03c35c99fe893b82fb4fb2cde12f00fef9e81386049923388ba7f14f253c4130278dbb1385fbd076fa8c738f669699cdb0898a519026a153e950234344ea80d07760285faba1b3e85161b937d02ec2a513b3f1019ada25375917d5b1931f030a951d22331a8126aaa232c9b4009c0a2813d4586e6dffd6a7a7ca567ba295ee6b7b8b3d15bf5e2f9320480c9d00b2cbc79048b2ec1e7ead996db65983c8cd93eb3975c82a8da6b35ac4786eb280a247bff524b1280e4e0cf4c0af0e618fed8c4dcaf42a21e83ebaa9862c888eb7d62339d06732c36d129fafd11c86b45776f1e83d72b2c2deca2e94650d9518d506091a2981099a057833d1ffbe2a75fa8683fce60b1576933e09c02e3994bec005c64db83b60b356c1bebdc8c374312953a5ced5ebe63ad5563c0c049e258a06ebe53b1e83f1f46cb95f607fcb29bc5687e8255c7490183224993adc09e1c85631aef757db40a9cddf5f3c7de19333cbf31c64363d4db24643f964ca721611a2608da7121917e4a2ec0609b9befc949d58e91900ff043460063ab3460385d1e4ce02d6e0f0df15e17726fa26ff1c7251512fa05643386c792f920c52c37caa9268438900e05cf98678352c07fbb8532638ccc2f1e27caa4e44293edc8c176ba2496bb5dd0bc77775078f1132cd20b7011593caa72ff174cde2bda6a708067ac254821f3dd069bae9f0a6e733afcbd1150559a35efbbba6bdfed10747985f2695a975e2951ea57d5bb79dc0114a6ec8bd24465b9e9bebe30e313e16f6463bc0606a67d4d67e76eda27db8e958faf2d403948cae97dacba1baca6b8dbf2782c4607"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"20061ceaa4cbf1081112b3a3217c37e906dec41e5e8fb9f905a3232237b12c1f","proof":"bafc1d9aaca94f811534a1e63b2b13335c4d307ea32a66578f24ca79cc74ec7dd480b600dcb0d2116caca0b9beaeb08d56f8cdc7109b461ccafcf47d6f04b3481885843b83d58ae4ab576d487075e539afb3d913ff3551c0aafe0f59077c26775a6ceb01b2639cc140d27b1236977ef8c51683e344da99cea197a59477ed343fe99a6d9b42b4db7552254ee5200010541d705a37d9b6df158f01b308428b1906e603d128056e9ba6ba74f9c0e3b9c30b43754d4fe793389738dcabb2b1910409d4e5efd911f6a1be6ae38f6518787325e2da108c84d127bcfafb0e056902a00c70dd3cd941212521453de50362825e028c1220c7850588923b7c6648d6e3f479aa079ecd5e775debd189da9dc95afe83d5afd6ea1fcf56e095904de7bc2955017e38a7f264c61b86979109d9931099e99b9b281d7073dc863bb6515269389d2c8e71eb61e5976b3872795608e351521b3b93dd92cfe8b4764145df91f73d7207a00d592ed88e2f8524cef259a62f006fb3f2c7b99a88c2694f653376660d2a4d1008d1bbe756058e89486fe360fcbfb8d295448dadc896ed1099626c887142529600c2bd554426f56b08d17fcba170df3f4ef4df411717f3ea3f113b4e857272dced7ecc9e67c365fc7124bbf64cd601483e1110f46b54582d54509d572f8f4b34b12c6205705164831711f775b373b9a1bffc0724ff05bf2ab663ae4e2a6e4050864e573c86758500048c5bf1c04cbe90a0afaad77549c44c7dd1dd94d2eb2510a1f1ba9bb97d2a850615bb1a1bf9e164f711040cf59fe9d021b9683fa48514a6aa94559eec9f50be133acebe0b72d44c9a527d7cbe9609f577a051c7e53e539c8a26c8ab5c5975ab5d682cd5196d340159a25c53f6e98e2c341304787e7e05168a9bd746f5f41fab4ee7c62a8f6b106daede374e33c4ad674c3ca065979d01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d073ffaf3b692007c2ae022afdd3c8eeca4acae22cb15676afe4e66aec390852","proof":"609c57297326d4dad825eb47151716d3c6d8c38d842e1908409d15cd7867dd4ed82cb5d9ce9b7a9a01cc3e9ad410f642baf00287974cf31798f4565108b5547dfc2f59ae108d51725522c76848b54567432e22ecf0a899b3121dbc822c2d9e5a2aaf1e22f6340591aa42d736590a983e895c919e50b581a25f94985f3fec9576a880a6c643833bc15d3c0376f8501313ed0d6c8c6cc493258895735425f98f0baa98050a3000531f68b8089468b7e6eccaddac07ecd2eaa2d9456f0fc05f2c0665c99c44eb9ade97d2b0dfabc848b94265239f7a07c9c031086a34eff6dd8d09ce286c97ecb8fd01a1558393e8ebd27fe088f06a823def3bcd686c3894a03801265e9969075b5b833670ccb0b0d15e8e9db0b084c073f03896e535172cd6334feada08c8754ab3e10931bd36772afcf71e1a53dd27249b3ce8ef5bdd594fce5f22ac2ff3b0e66fd9db39d85d59dca486bc506ff0f480154240a2bc20b3cb4823ce26a6b3010a80cc4b9b01ae0f7d5028455615ffb92b0c751373a47a8bf4993a9cf0b2f59bb5fa68b7172c11c423972dc066e67e90f46028f5ff062f7f25da16fcab5500371eec9d2551d05eb17113e59f510ee0853b89dba5ad3733af1800629cf88cf76961a3d2b8174492e802ee3e625ae00880d1f85479000822e916113df8dae3c39fee2fbcd79ee1417c5ff82c93444f0dd29b81ce1b9a3217805ba62e04df269551b169b538bfff193551d1a6d52ab664ce99b364862801fffdd9987120cb76bcc3639ed01a3ab99f89f7a6aba3679d3d04e5dbb3a0fee941975a8a3b6cae9a7aa3922aa76900aea247c6e95298b73566a75025f1826505b0d372712238fe70b2e492fab3d6768535554f0c2b8c6c1c3c70b79feb715cb971fb30b00a1a597cbcb5eca3015c21e069d19a023a02e8651af67386ddd1e9f8044f3f9c06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ca2d1c1f45ca85f4e8ad2d8811c3792817c1ea303a064cc9b2ca1c43c9e63370","proof":"6ad32809256c9e14e131ba0dd49f3584d155b2547f29ff8abe01c0dc6c338c2ee624bfeb8bd1571d6c0d8ba50c678c0d667f4ba0324b8ae68b0ef6aa199d7c416a079dd3cbbf54a837fa6cac28de2f8939c24dcb0e34dd925af35c463a667810ce0421be59e2ca2d300cc7ec2ef852271953c728e47dc9fd3915e17cd010795c990d45723a54b47ef5d2a6b93e7304616d57f82a5d0057eebe74734993734307dffdb8a17361469b38ee66ce4e25f799de09c475f0480f0bd88799e98ba4cc0f4a4e5578b17f90425aa5f673601de7d45ca91482bdfc8eeb04433444613c9e0c2ab7a5900d7f7556ab387219d1c9ecc63e06f5d7b2e61df273c30ee32d6baf6650d1e2826e9dd223a1dedd512d44c3fa6cb7a899cf318a773958eaa39de3bc1a0cd492c1169f7be326272651a94cc430f9333142582333b8fd2a5394ccf61428648fba257255698ceecccb72cbfbbf36c857ab6ccb3b654f6526e9b23db9f042ba47b02ca129e57f70ec97b7c76fc7ceb74e747d0d12448e3f7b6314fb7cb62b300b5ea4679aedc081f9419a328b072460755fe288161e93a6fe42b111427123563224a54b477637c7e3199076de3906246e53a998cc9b225b1cf2f03c5a6c01343c0ffc64afa91153feac1af5d9008240f8d6503529343b3b911ee0763d3154f6e4dd9c3243269e6ac08cf4ea214689e7854bd2fd0f93f4220a1a1ff188225b22c131ad997150a2dbb301a3dad8339dc04aa296449460a87f88a7fa092d184790c6abce4a77fc9e14f7c79242e189eac6de39a5da21642ac555946206b3721468fc315668ed04e1db3c111c072cb4dcfbac855ebde9574f32952dbd4f89ee791b8dbe5150d2b4c3375ee0042a1115279e9b44ea6e7b86f2fd85c53ec3a0b20d42f2e26fb8940c8921d5da8d52f990f532385d3f2011453293e5b04f51403d0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d261f598375a49b972d831815e02a90a7291b80415a788b458e544ed54a27458","proof":"7e5b8735dc263984d15ec3a8ff2f845a054e842a7b855ee6ea451a514840771d5648397ef78f2ffdde90523bb35a1fa52a177ea512c12f4d9e0bd2ca17abe1235e4a167a11b3c386ae78867f23bd6cd661817f9daa7c375a385748177191601d92c45514dc557382db953f193b8a680caa6540e506069afdf395f284b1ceec4079a35dc5a266f1a6eb30962f9d7be77a3eaa86c37062961ca927067d3c976d0d686df0cf7ca1f89ae5e3ce7a2450da90a1648bcdd58a8d9b83f84734c00e1c0ddc5dbb91042f6dd30b6cf9f268f1af33a25c3d5ea6b0aa99bf473804b023d100043db8f787516a2202b681072532e8dca763330c2c6995c8b4bf9a111b413e71e2c8bb427252291c77452aab410ec9b4dc02ec40ae904d4ed97e369a7c12bc11fa1f2cd2d95e8ec63da89d70577d03847b2672f1033d1eca5f55536b4341e1300e763c960dc5bb15acc072b1f2dacd5d83201ce13c8b73aef47cc3b835d2d56fa80a37a82aeb2231f9580d1fedf44baa5bb9cab5c0522bddabc889a9a8030c43927e75dafb4ce6d2f55d7c0aa9d0abc684941080c5e6dec19de3b3f4011f1a0e54ad2811192432e8ed45cb3744c3eb7700df784fd7f844107095a5bc9ebbfb0cde6098a62bab56d155d2661772f9678e4c20003bf4f5ba1061513557f5d02458cc4aabefb006190a5be33e99a0583380c48ec8e2376cb16afdb15cd907e9e451641ce19d9adab3ddbc7b61b0d74dfa42cf6f0036e0866d61e44b818fc996fa18c0f71d795e452c62384fab0dcb97b89591d9f8e5025cf144fc496a3cdb3aaf673aa57e62a57731b894003880851a4b62174b99ef520d74daa7074f5dd21c8b62e708d0637b651608880d59bc708dcad073e345c609f12d6570e8a1d6da11ee0cb6878ba040f416ae3eb2c6eb365bd01bf785d812706b537c9a66ed3672ebb505"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c863f73e4427e00e6f115ddae88e859f7a74f91d2396fd90c1c9ae9dd08e4e54","proof":"6e5622d111c451a1e6b21c01f72847f0243d132effaf6a5a097573856a5d13084c075ccc59fdba26685251a981064ffedc476fd35be58401064dd56a260acd719662f5c61d55c73f2c79d662150a21682fe154edb0cddde3455f07fb3ebfa30e22b3bdd8eec468cfa7ece1bf385f9e66fa210850e53c7b58e8f93c62453fc976e69ace4a46b7d4b5ec181bb161d75b07918c498e6091be68d9018f88764dd3066cf33246442709654bda7d600b20b659f10af1f21cc4ccbc661719cdf2c4a90f4eab9ab120107e7391c458f72cd150f12c83f7a95b9d9f77ee0699f957af7508dc55153db8c81c225b3e6d972618c622fd8b53c711bc10d45c508f57eecd9c69aa0796121ace6516b3627ecd801a6ff6c107ad68a11c234d8789a17927cca347b2472aa566b17ea821011e79bc7ec3225782d84b28b50feb9c9de6d624c9935ce4f75fc519e08259956288790f69526770d1bf65db7de4d9037d1c2aa23fb87fb624a8b22827b6eb66dd58ccf64e7d632663fbb1cb91f0571a6fb3db2926dd74402d0a638e1dead7ddae4f181882be5db85dd3f12ad977cffc344c85deccc27e9cc82a78be8e062b4cf64acf436fec61644d12eb1af1c155415755391dfd2369525d878d033e967667b94eed9dcb24d162317ae798e0eb8c17e54297239d4b4d6025ceafe2c6fb6fc786a88e511fa0cc9473e7680797f6ac14fb2f846428a961f290c5d5dbc32244bc12218f06439695793e3711ce2d578ca61351689d02d03708cc4e756ed6b28b165fd8aa1ef31d80aa83b4b4f617595ab191bee18a6f010ecc67fb03905057aeb9dc3b75cae58f96909e7c712e5fe08cd263bdb9f9b5bb34e41924bab0c61dd0d7e7180d1b1ca5cc79979a098d38c266c6425bab0302730e1c300fa3c51ab18c67630efeddc792d23e92e21b51233b7c9e0f0c51a2d9b90c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"12b71334da3e6a81b7a1011abece1ff7edcac33a6ce46a9bb569b2ee88fced2d","proof":"1426ade2af61dec2070df84690935077d32746c319896960dc8cddf9fe455a77a2db5dbd296a14d34c863e725b27f9883ee40e275730867198219f43f5e41d138811db05b2ab002ceb449d8afc6821e7f6efd6a1df281b5e410cb7d71fc52a5498a62e7c53c04038d3ccd1769ae6a108debd429b81b3a1405697d0d49d93747d40d85c476bec8ba0e308b420c0a8cffe8cf83275f97b3009f4404b338467ae029453c450c71c8bf2232aa131f3f3753cf5f42226e85788e8542a39cc4e491106dc70cefc190be34cbb2bf0f738c8428e9d2433c56173f3b3e50534b1fe482d0110678bf811312a4723032b07b2998495f4cd89b7dd8e138d5c134bd79acfae740cf3342f072115908a327c9b422fff77a1eb98025ecc30d785321391ac533c6c12b1abfbf14f54d00908f358713dd2efdc15049d71a9e08185dc36c65d02266e2cf833ee400d13e594d1deaeabbf169355a993da11f99cf4eed38106fddc733d6c5c49d75f747b13d366b6eb5a0876b31f13ef5f36b60dbc8a01cbb976ff89329e5aedf736b2b2dbcef015e0ab27a1b71cc34b9622a7c1ad5d10db5f8a2bae256e9701afe7fbcf2b4fb5963f27b9be6121b4c6101b7e7f846d1bd31ca69924743a82ab6640f0f1cf8215cb0e6f8f40d0d868368730b07039de21b5d5eab6cb569668b553240e76d0cfab18c0986c0acdd62abc9115f4d464e5102cedbe13f64c5a9bb3808eadce4bd5a634fec5ddd23c460be200eb330eb92b09925b12e7d27d8c61caf582d7d30e65676fedcedcd8091103f63cfa6e857763cbbaf7ab2384274cd7d63211f48836cd4df727c877e4d7db4bfd3f4ed05c4f751eefa374c2f220393710695986350d5750ba441c96fbcadd95285787fbd370b42e76768e45070ca005d5116a968cd96bf15e8c24b44c709fd82d221f41cc94d022c66e3eac9201"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b673d12c9a378d8995dc0bf6dffb799da46c46906d14d39b2aced7ef95fc450e","proof":"10f4b3c825906e260b7ad947abab1d50221377464586153796daa4ae8cc79b019219605cbf9bb8ae54738539525c604281613c7cd5e9e3d6537109589b202d628cd1111d70f66fbb1155f6faa94b871a1316a47a584ded6dca966718e408935cf267f071bebd16d9eda5eecc2cdd7c2d57f0e7b68d72a970786c89851d13976ed1d13c5447c1e69c9373e3a446289e64606430fd0649c3d4661d73001d5a9002b9a88020d98d1423d86b288a5b581c970d227ec771f220ff06419f4e4676320abfc79e54fe977a215206a2bbdc0ae16e7c6ac33907bb88da4f4e5d96182dcb030826df1e193a3860815f507315895fed0f20d3f54b4377da32fffade94596f54a8463a63e457d4cb31c3d2feed0f6d38775a9329c9cec625d99d0f9f15474a3d4ad3b5255138592731e10475dbd05a59e2768a753be27a247e60240bc6b82b33e68e90d6d09e3b103642efbd93dc0a8b3bfde03735eed7e83e83f66d17e8736a944d5b2614168f5f84065182edbae5e8c6ab228a5fb9b2287cee1f6bdb1a1c5c6e971510e68444414aa41abaab7cfa921a9af72d360ae855149b642cb998eb2de4ebaf010d233e982c11d2a74668013d7622c2ceca58b89e7cf433b75647042c548296f57825c1d0406bfef4cbfb03fabd4f12ace5991d808a169c5a9cd3da3c5c64350812269c5b380da1d2deb755febe982f6e139e91620534f42100da647228401990190aa9a56d98f7e66ee34fbd87153d4abdb28e59eced249895347e41fc69903c7b866cb993f2770567d31e05de20f87ea504bce779408172d5b9270ed2a3aaee50a958ada2ade684ccd250387b3a10e8d66c9d084667adfd578894581ec08092265c4208baf53a5b51ccb4b230054931f60673083a453df1e42768082bd8469ea387895a004f1e0a2a3c2537367eb24dda7ae4b598be67f9e18ede08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"88ba120dccabfcd6def13efb612634469e683bad13b8d85bcbc68bbe1ac5ba10","proof":"8e76054e260d29efe2ef9a5f049f007bfc6537d9f38740ad5df1defb9e67ba6ef6b2574532d9bcdf831be1732b49e2c53e4fce5139703f13426244f4a8696739e81a642b5874174e2fe73304a2f4cad0a7e00ba6e6ec849851d3d6d46c07b50f1a19f8d41e695aeeeb0892ebdd2cbaf5139010945b76e1bf63a3ad726f8d9614adedc802c29d215c212413987650020c81f13b284d899e80771a2fdafe4a410ac618fce547932bb91a59211d20807d1d9f09fdeca3519e84533eea2ebffec20a6096d27a0465daac5f9410051488c9ae55ed9fb65e79b6c93f3d7b94949bbf045a1bc1c63a57267dae0e6bd121fbd0e3fbc6c144c17a11c079761344beb7372cc27f843ec6c449b12ec3eda32b9cf2390143148886b1e66dc990d5a45a9f7348b2eecebea4f234f3ad6876148c43b3e63050187e3b1dee0b511c06535f66e2763e283647e0a5f289381e08ca27b5af0ab126aca9436ef0a1e0bd863abbfecc5c80950b79861c3e85d2a53b25d1a61748c556b68954569713ebfe95e60992372f90ba692ac4ddf93608d4d22b5e129284c480b4de2c3c7316d9eb93613ee3fb5610cdf6d8409480f15e2d7fa110b8ba3f05601db02b4c662b92c4c6caf45e7531767e24eacf3c69975162f3003973b7519ee6779d2814bea3224118c9ef879527684a3b6694a719b17b103252359db4997ef18db126ade0369f71250152fcb45610b7ab628cc9144725b0efec24a107ee04d73432c895291d9d3e17a1f08f3c41f6d276e89b57f238af5b8aa8cb4b89fd5150ad183db6939dda8b7b82bb08ab3172bb82b48524223b4c25ade57317c349ce31eac31200b5a7dbaaa043b6fd5b1d43d007376bd5092b497dbb44d4e7a1786d1e4226582f37bda1ea50f0bf3da3044b57b6081893ece20b578c59ef84a82f8b02a5ba7f53b8bc19511ac80dba510f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3c7dbbd05e52f5f8ffe27f17784c3eb6f231a93e8a38584cfa1e4660cdceba34","proof":"44e5265671dddf60ea23fb7a6b35af45e430f3ce73b54edc1240b40b6e89620b2606f14707143cc7ba8e09eebd1a18d22ac598d8d394bc32fdc4da902df8d62258fbf02c521a2a967647bbdbbc8405b8082e6b7a7d383ea43dff54b6591e577f1cf56241371d23b9b0f41a6533c2a4634e60164ee005d758cba9aa4bdfb3b200d541ce0d4b82a73af0f9ceded4b9946b36e367a269eed810882add133e9d500996ac827ac1026507146664ed0902bad52133b7e48938b3d01b7b5283e74ee0080030fa0446c0324e6b61ff57319ecdbc4805e970844d56a25dc0ebea9641fe04506cc0bfa5c18be9ac29973f3fb594113bba27141cfbf1dbe9871aa2652acd1032c865ac792faa5ab9584fff8ec41aacac0c867ee2583aca11f0b0fd254bc93b4cdaf2152443e2f4bbaeaae06e2acb2dd791e9bd711bd20ea4ceb420c5697b23cc2814865667b48fc45c16a279c63e8a06423c72a60bd3a83b98851a744dc43316f2d453793fe1b45ad2a3cd48e16741299fa7161e519a42a566a8f7099b7875447864459754d5cee0d106b31d88bcf5f09004cd69ae7287f7ead049936c61722ca45dfe4d014ef16e3be2886786c1e42e90b20cf0e2c27d1bd0653a4e34f46e94b7ed0d5ed94bc0187aa5043259e765b91cd6b7d221b126fd9e9b75cbe6e832060e6b491ff02aba07a0555bcc4ba41c03fb55967ccd192958cbe85c265e143186f1ead2f19e7221ba0d472c21dda38e42835d192354ce05ee17562c7d116028f8454a4dbf0e7ec1ee306ea2dd10f2cdcd285ac5aa1219122ba72bc8fd6430344a0bf81a081ae9378a34320b0693104a0a818042e3140bdcb82c85effa7b173b50a1b02601ee61c014e8f76ba622c4d9bd4ee9adfdda8fcd02e5e94c02cfcf0725e2d5a8b2c0cfa0137362330c3e0f366c16f7893e9a62e5a40c4fd19b3ce50c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a001b815c6d693ad4047f44b1e3acfc1123abb746daa86acd92e2bfd5c8c2413","proof":"cc6509bdfdec2ec40b0e7cde3ca0338034c4a88b88f14857a33776a6026a1475b6e41fa4f68a4e5e31f3b6d4fbeb5999e8adfd705a5acdcccf069b44f38bc129909b2f165c6df153c74d03d77c94a1ced900a06990fa588bb433fc2fe5e3a10ab65174c88f3b9249cdf4546c8db953a4d5ffd16e499023d2b75e3299fc3f561f4a8991dc2b1a505acd10504ef5070e59e728c55f5402c1f61aea5a774744750a2de6af433783545525ed92395baa71a11a8d1bf6051a4e422773738340a9d80451b5768ddb0a222b4bafca027b9c5d60a001b01eb6a322ad050ce2d3c0f22e0348881405d8a96213a9dcb72762836f86c1bdeb282c567f67cdd18bc7b0bcb55e6a4097b5e0e732bc97374f6e1a8646200f96c214c80707e026bc118f84ebd3498849a046799e84d10d53c04c3fe590e3e18cec0c10a025060f33fb7b44fe3036461ff62449c615e976d3c16648acc1b9a6afcba9c3b92fa8248ec9b471d48804a2930251a9c352f2363689b52e452b606ecb34043922ac62dbdabf782bd63d66bcdd252c2b1f51fee2b796f87ed0639aee79463f4106bc4816677d9944795e53a6ea97e4fdfe94def3d79e9b577dce573f2eabf53c3fa7016326319e7837f023c8c5d03b5a183d538469cd42ceb0f44d4f71cfd554bf13e773b5b7ef19f10767c471965375bc6653502ee0b6c241e3465177e804b4b78f9a5bf66304145ad04e5a85bc82f5ae9a0bc5138c2c1f62d5af0e4d1ed99c51693536b689634ebdfa69201a7f0516481273dbffbe77d69d576ba3b2c57c3b550325cb05c293a4b9c14a82c6cc906000611fdc2122fb95c942bb54c66e46975f002449bb12f5afa2085691ccd42f5e50e17c730da1baa39eef175143aea80a284f4c275c4647125a970d95618cb801ce9764947301c5e54b69c9c7c67a6570b94ee8347563b4c132ca0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"206e3ff564fa316d4cffd41c942686917ecd536292ae735588de337df87dba4f","proof":"06f99a4ed59a52bb5970d3ff8a82f7780eca873de0c38f0bcb05000ec177bc6cc889ab4a3f557bccf84d1f1572d7aa671a91e020cc36104f48d395c75160de2588317f3ef7c753899871d27312b264c25e3a5d554bde9daf2f4e2b0f8a489b79462329e51329ee9f4860f800036b3c2d81d024b48ef8f3a25dbaa3f5c11af608b4ad5e22133d5b6f976f4be71f26750690e1b54022fd82e6e0041ee41c74650831c45cf0e711d78098809bbb84b4c594b078de08bdb359f673f25c995acfca0f4e35293ada05c675005e0e28451bd3edee62ecf7bf213a17f9d5f9372477c3099424fad2447bc3576498056361e80a0b7d1034b3db95e20f8295dd17dc31bb0b9c489166cf1b04725b75bb9dc548f335a019c080e3abc2753f77f1248755de0554f800f8a1263f41adad6ff5439de7a4140b2e8abde7e233e1d5db3c531fda7026e92d680fdb8e24e2b045529e8e7b8905483a3e5a8886c7eb1147d827f3f6191c3eba2f26d3814f04ab841dd943f209a6aa509ab05bc9fab1ea68b3197e6a70dad5cc9a0d9ffc00cb10540fb904a984c835930813582c6eeee22fb5c63fea6c56303d2137e9c9cb88ccac54a50567e5b004f83ed9d2c25653489ec7b6116930f0da3990202647282617e9f125d89eabf1d3b8f88caa734a163bdb09dbfd4265c298cf9e60a0c960d5584c5e962d4a993fb5e7838aa74a8d3581fdbd86b7d92ccaae0ed99fb31411562c86b8dbe03368134e19bf6aa2951ecfa7cf6813992532c4af8a7bc9de59e5a554caa7f6ea00d99da0d8ef8ea1e4146dbc19cfdaa758600286e3878c1c2d8d921ca5ce4b036e864ad3a83a3b0020d671a77bb396790b61819abdf9afcc87b854810351048a784469550492d6502845f8e89f60b1ad8700e11296ac1a33cf3d6bf398a9b00c978208735bb4f8b7b0ba8f514e017877ac06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b8a58b236bac12609ab0079cca242f6165e98b80c450c37aa49740a56b81c658","proof":"fe34f288b1a9df91c1d6373260266d4821c4c9c928a7273968fbbd660ad4ea19ac4d3c308d6cb1ec52e7d990fd6ef16d23a3a94d21b342b63ad74814db5ee837e09405c26f407fa47c65f3bdeb20987d5ab29fd1f4b9bdda125946170aa789594aae2e8e9164ab6cd600918ce0d471e15c75949228e37a29402afcdf5a9c1512f5016fe583d66b3b5be0073f3fa0c25a4e82438b2217b5c59511882e472cad0c4fdf0d12324526396a7c7ce0d8981ffa1b5e711b2de939e96bafbbd2ab914b07eeef0374e6834ae22b9dd6acf731f567a6ad6ab7423567080eeb0db3327e300a20a9da5fbddbb8f189ae1713580e61ee82ae80bb813954d19881ead046129e49307d9a1d46620ec8921cba3c444d8eac0695559a38e7ebb979a2ada13fc6125a684b206ed16214f0f76c0637d21633147814da22557a06d288ea58ff81f64f1df41729bc1a66ac7d495208f3195f38e50342d29b6c23f0b61580bf0cac0b232cc499f3c20193a7ee59ca5a5e58071b0a374de3d4b05575df84e7ca5b7978904ee6c762ff10a9005d2d6b05bef7a628577fc5c66181e8a227bd54f898a666e16a7a3013f93bb2a0a27fa62b2d597e9b623c7f0b9d95837a3ea6343eb0b70f2f0d68b2cbe938a6505ddc262e0df611ea2ffd77d0263287e1b2b3c064352547a0752c56d872b5bcd7602bedc55f45a103a2b236c483b0c3607884f2cd2492e9606d1ac72e9767432e7dad810285894bf48f9d74e16869fdde4108c11029a39d0530c656490247f131b19a522501d5e035151f47d29037001241b5d22d66ab4a3e46643fd899035a45ae7970896f5a689c7f23c46c1462347b251842a543b90cbe502d58b5194cba3c4bb8f669e278b5e79d85ec3d869312def5dc617a8b0d54a10ef1e419cb9a61400853237c3f9bc8baa2bdbcc21ea2a5a5b9d504113b9db2f502"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4e491bcd2d12dc67b346327c731cdd3de6b30a5e35264f5c6b5f29cc19706e18","proof":"941fcaf7157599f1cc94d5b14b6780d4a1c1195edcd33d5301b10def406d2744263c8c492973b20e77a6572c5af600218cee6f9752f8df0fefbbcade1e43f4760c7c755661484dcca19f5eb06c9e2f49a59830e839e085e8f9c432867bfa631486e822b05010d63d0158a06da1bca1aaec8eafe9e7b1e968faa932df66c9eb6afbfed1c668e44123c64f78ad29e1f5175a3fb8122c72923c48cfce96ed2e37016d300176f9e5bcd85e4b68df173713f2060f4bd13737a5ce853472030d5640056f5d810cbe2cb9789fe1a0cbc35a037b9242f32bcb6d66831be7e2b3f6bd310a682f32f7bc6ce6145dae3dbb73693b8f332322d19b97524e7e4d71601b0e8e79c0c869b3084311c2f9beb15da6db18855a33e1790428a5a8df4b4f9b6c2e8279d81bb516b9a62616791142412814d63ff583198a99dd6fdbce9d71be2ca4fa38da8eba55bd6fbc70c7dca75a6c52409a7b8e331d156e476980ef04f16912f374fc117a13d9d894a917f84a8e460b705dcb96c89e5a1e62a24ae1c6404910ba59c8a508868b1729bebc48ae1dae8d9e3514a4b698d55162aa023551d894637a1ac890c96f66ce0a614027af5e11ee2d674e894d42a848b3ea825c442e65d69d1ade4a5ad7cfe28950313d58a6a0757155d33b94f8cde387e3919cc79332792e683458e9e84d051f84cc8e74b64cccba1184750b2b2dbbce8f67964b8bc101e71162ea29ef802c431a9e4b396e2945a7ffba7d03504a2b0c2c221ff898086cfa5da6452e69597095b16135e4332e694981d055d3c049a642f82c7259c438d1fd152ee2464a4ec9f49aebbe2b25ad6184a806c94ec61a74c1b0da3503a9a946cb56ef04b79bc6f7b7a1989b941b40f79e61483222b148a5956f614c6d371da3cd0d10a74e3582dbd04146b93269ef3cb82e7d35fca612d302585a79d63116e50208"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b284a063f6788a670f5cc9ed28ef855df26a5a9caf685100c20e6bd8fa079c52","proof":"706b5e4d6acee97baaa920944af4ded604e6bd462073bc5b25b46d659f015423be2558cb204a7a1c55bafa2793e3d4eee0b559cfd4dc3e62c18af3a02e908548ccc89c45153604230dea2eb6c612275119aad0b236f2c0ce30ee191cdb3fc805d63f93fe93660c5fa2ef3acdf44fce8efc2444645f43dc68bb5870b07f734d300c10e7ff6be087652198af01a227e793f4a6ea22828bf294050bc2243f400d08a8e021abc26937967d05a978dbe5d51ad40d97c7f03c0191d273494d3d0bcd0136a2bc30b2f0f10286d7e7f7433c8515a6076303614316ed8a455ea38c45d2026ecefcae3f05121d96f947085072bf88a2682192209a3ae3cdd24c34c4c9313dda9fa1f8ef74ed4194612a65f6a6be24f3e1d13e966f75e45dc52259c5f5bc5d721f471075c94bc44c7d9250ef67b0b63f57c17699c256da4bdbf103c99ab729dcfdff83fbd6b273ffb3a8f2f3ddd552a52544d214c85a67f4e32f4743ae892df45d0c79f6797a01800baccb1356cda9c71c57b0807729c2881df0a27d8bcb50e8001609b935d607272cf1a7391d4838eb7cfde323fd0a2d285547ae7152d9220828eae87acd48422448748e5a84d7bf59ecbd6f39fe20bc947ab5a593647b348274783a958c23c1d2ec80fa7902aaa3b5c34cb70a25f6f48e7118bccaa95b5bbefa5f2509d7e11cdb383e18efbefd7f3c5e548195714c2a5111a25c43f7b032fef7d2e32684014da72ee14cd08aea98197e3f0d26dd7bcbfa6f79a5ccd4390cb62e4a88bd78ac03dee1df3dc67d47d2b3d58b8898666cbe07fc69f61e1bf66ee65423c5cefab156f9602deba212c7b223c39d2553add9dcc991b16573a41e29ca93cef6359f8279a9f27303bb1f370a1125ed146bee9307fff5268592ed800f9725628d867c3c6d1ab5cb4826fa14a419f389c4933fad9620380b2ec2c34d09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"36533805d7c93b02c1b12b4c41261d222c6993453433794ac990e9b25dce6530","proof":"0e7acd5d4635adeaad816c2fb7ac4b6761e53212dedc39264e98bdf461b5865c32db9c305bd957431c6c90437b89e92466136128daa2edd2b84fe9880f9ffb13bc1bb128b089fb4dec27294c1fee153f08e6afd1bc401b0d3601b5149ad83f7d62f791708fe87be9dba36fe187ea9b6d8b364ceb8071f5ec2b10068a24257c44979ebd1320871527cb9fc00fdebe28d0c6c48c4d2dcc890d931d049c6e08530bc75e7efc020f4feb2e4f77280a8798976219d1dbf4b2803d318251deb0c7e706d07d7e5ede859530333b3e3b4f0ad3ebe9d2899a9164562fea1175ea22409b080c2bef07f7ac9789efb9396048ab31711204785fec5501d70863d7ae4b68f97a0e88d4572c4a36daccb8a8383ba70111d23339fb06f5ab6635b3493d8664bc72d6a0460157c90d159d8ac788fc03255cabb83bd2af1446bb43b3cbe47cf54067fa8647659e028870a20ccf64948b49c3aa2c6ec7f8b609c72f14c9601d6d1f58b288bf2a01ccd84b004ac9b1adce6c288494ca8bf9a8f785c26a13e8e160e305f6f96bb30dbd884e57f40cf07612a88dd9f1c410af4dada34a3ad4760e7c7d5c387009fb22344aeb2cf81b09e7f4d0b32df0edbcf16b6975bbf08757f652d74b76b4657396db9d13c53cbbb3062fb2b5186a5018d7d74d83d16350fbb949b859cc92d0453b141fb6b95e93ecea48c4e66b6d2580921d6d0c0669f902bed2ec6bf6d2407a9723b520bab06103fbac5d85321f1658ab944dbf5bc8259861dc255d4cadc95ad6436e9ff8dea0b5086f099c88615788059549e751cff3ab7fbbf818ce8acc60c375401c587792fd7bf80dd4257f948281b8b05be58b53106256d61a1ee06da7ed52467b1e47b07d396e58ca64ec46911a28474c0a521a57be7ff30e11020f6e153373d08f04e0aee333de60117aea07ec9c3c4fe4cf686d0aa88b03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8281f26c6e1aaddb3a11f2877b0ee68e3fb79f11e75960facfe4b8bcda7a903f","proof":"c23f9fb5f7debc0f851ca8a60ed73139daee61b146af8914ddc99fd0a8fd2b0c1ef50695288eec4f6b1b8d9206b4548a0a4aebfe1e5358b795eea7d32c952f3a0c28dce7f53adc7a34cf076bca5f2fc65df1e82dff01bcc01417cb07528fe4161eee4850ffd2957eac5d74b1bb2a2db692edaca41bf06fa99ab9864aba6dd166bf26a5b28c4f32a5f341cdce6b2fedb51b9d236f0e796955d77b1f395aa2220bb04aebd1f2b0df5cc0332838d956d7e76e09aadc063f63a334095b751b009b0bea9f5e09e53bacb87bd599c93d8b0086d9e494b8088a0dec0c66ef53a051290c6cbeb7e2b9c51a9c953321e72371523383965c725cad4049b5331b4c86bcc219fa45825cfb24414443db03ad3acae85f5c7361f8cbb87c54caaf5df94e0b484a1c852d1d1d1371eed48fbaef3e53b764db76ca2ff0c69e34d2da5f18226b102beaddd518064a4f0790668bdc629e5cf713f551718817b60ae10b400d2f1a393816fbd161903c7ba6abb9854b2a21eb07ccc651aee865023c36df83cec67a8e607440590b4db975d466b5065d619b57db145e846e3149d22c8e670a8fa249c20cb630bdd2ab62d42631f38ee68d600aa16d098a8ab0d3aa02891f7898c510525bfa943b0415af93d609c02fc7da7910e8fb6695b481184a68d898815a12762471d22e8721a71999cfe6f923128d6d9304282541204e0a17682eb32cec090eaf7dec362af8f9603dcdf638c8d762e74acde21ab445f40ffad472fdab23b8015c426a1ceea7621be81c479e6672729e459be21c2ac04f40f3762594fe5cf33c6b3ca05b9262ccc66ce0856e56f1325d6bcb17d73d7878c67914f55ebd8ed5b8087fb5671b74a9f191dd00445a4d68542eba8f3791fc8b09d08de806a17f357c20062be7a063134d18593871f0a44bcd5d642c7e03aaf25ec29b0f6a5c646ff86103"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"18078d007973bd22f1b9ad00f6d8705004f6f82469b5c10885597c8eb816f55f","proof":"a41156ba811a3b1445ed5e6ec81aefd43385755032b3eb09d9f1c0a2b24b8051e4f4222d84ee297878cc7a3c249779ad7a58b041f38d8f622523f593722e106eaae6798363cc30ec15923a54f0bce19623569b115cbe728e613173315954902bba603eac70c60e64b0c58ff190147365abf1ae0df1839bd9fa5a53727370877da9f0bf46766a8a6ed304272e1b621f81f1895036af63e2ecb1c272dfa34bfd00afa43ee2b73838923abf3775cbdfe14fe1a90307e081138112f7af46ce34d00b7fb92b32334add2b3e4b4fd9569c3028bb633ddee095e9a7b4fbfe3ac73d9d08501746f1253589ab7fb5b386f16799df77b8e6dfe94b6cbdb071fe9bb5b75940585acb02c855474f823b2ccda027991cf625d265029d5cfccc4ee75d02291e2b06b90153457aa8c33c257b8696863740f1bab8481dc08a4563b772f519eb4902224997fb6ce7ac5eade5ed76539cb94f099abb13e963067267e4c9f3b6087302107366026fd5f95ed732d0f360ba95a4f36ddf673558ec23f916943e128d5f7c94e774cce033db22cd985c373729d40e3927ccfe4bd404ef3b2a01eceb62ff35e8359e000ff7e8ef63d4493f7951fbd11ea36a5004895986f875668dca40da25ee0907ba0102c01c31761acec391a60e07651db03671ca2427c79684ebcd905b4e06df6f802d014ad072e22b7c02f7d054237f2149f2a5057445f26f6689f80ec4a2ec38f1b6d12c0bfb60e3fb34b865db5ac740024b7a442380f727970f1165805be6b5ef0944fde9fb30dd253a21b25d41aafd50e2f3880f8892505014ec0854b5cfb65abbea9501ac2945006f705bb95c2d46986ae805330cb6bf66aeba28dacc216a8b9ad210315a57c6c93daa9665f9f6bfcd892bb2eb45dd435ac17b0c41756b6727a0bbc364b71bae9c8a9a41fe94cd1806a314ba9aaed9c65236b403"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"905b162cd50f31ea0011c7a8a2d660b86717f0af4b5f39c8dd0ece6761eeaf56","proof":"c8ad960b1d1d635f36f2b96fd3584319899e6e36d6fe23394e50704bc830fd38fc44effd4741f562558d9ae97d4787fd7d17171dd7b9a4d3d872f14dbe00f474e8e923909383b0480e521aac47dd52730ae8ddf1c03f7f6858896c3f0dcfc66ac2278851642fa3a5929dfec953b1953c93682e586c9e73870f5277160157443e49145cb2bfea1ebb3303e36a49d93f0962a1bd449d859049960087b48ae58a0b0f2c2a5d9a6921d80820d5cb3ec820dc57d34529f263f4b1c39beb9358084e0e902d193997c98aabb76449041e52e19fd8af0798d496179cc30d028b8790090b9c4edd5366f2805321c1f5d27d7b317c622e34bd2a2fd57fd6c1a0dab49d9e3a34e901ea462c9146ffce9fa23c3cd0c64202680af2aa43fc655076a36ee53d27de42fc24c8d52a894f4357cb1a342ec6452ad9c91c85d38fbdd2cdfd6d9adc53b0db76ab8dbb3e750880de85d26cd67b0bf41c34004bfe0f1555a531166c531cae9643339ade6617a6a5aff8b075cd638d76d6df6b65edb5d8ffc753c5240723a8ef3485408933ce64220a7f8aed64d6e15dcdf5c97df2d38b049a2c7838891b7e7aa91429e918f3f45f06dbc6bedf2b35455d6c9dc7111b9d4e2712ca1606481c18cb6ba10476e2862ed75ba4d8ac7961ba5db18007c3aae6df4fe87ff38c5142c4ba1f32fd3a047758d60c05c78f2bf456dfde71f1ae61273e0654a2412d29380c0c05142479fcf0b9ab99d04a0a76fdc7048d810b85ddaf81f909ba456a320a05e9ac0b83aac00068cee1c0b2b7e01a369c16f3a0413162096f97c36da9183a18bb443f90a16e3655ec14a243162680a8db9d74ec4aa3b97e7757afb44d65ce6db698ee3e5483ecbdafc9d5683aee68d27d30454f03c627bb25063063c202816d4f28575a0f73dd55f87730f3161a7a0a689b8894fc38124eead51b5c2103"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7cf7639721b550bda76278d6f39cc86c18ffde48c2409cb81bd2f43eb554f413","proof":"e2ababcc54e3b1c06fcf8384906eccde98a814f9979ecf6899c935884ec5bf7f3eef60b627ad8e84bb20c43600af09ae99a074932a6024b552149388b166be3436c68555e56eed4414f6633e4fcc371c15ba206e597e2ed5abb31cb7d064c451a6f1893f6cb895bc117f6c3a2c924ee217d3d903f3f1d2b549f8127226123d7a8ed3c710597852390fb4fed958af11c49a797e0ff7e22f2d285452b499bd50043eb972144defe4015b4b05a7d5df4ffbc3bb2d9c85ca19d4aeb4b8d7a3432b0a508ebbbbcd96b6dc45827a69fb9782568c713c414c9ce6077b633368517dad008a9db9d6e2d6e37a19f96ed0c0dab08559354c9355b2fb7c89711c1b927de5587625f6c959536e798ee41fc2de18ccf5279ac743c6fc8543ba0f7bee4956064a4ce1a43e650327d0fdaa6755263f12ef4cb8cb2896421cdbaaf2b66660dbdb7b5e68b5ea9a4109662dd57cbe9b2801f6d39a29986d6c82d6549431f892a2f6458c28c298d115c2e52879d4e2668e298b65821005db033c507a76d9a6583a8a56303dd2dd6ed5ca7af12c9eb6aa1a1fc2d1ed362da20693761a2630429452c031c08c490a393a50ec8cc8fdbf30d14564a6cc859d4433059c7f991910a71f90581af18d461a725fc4578d767528b9b5208810478143936bea8e0ede739836b1411635e137959cdede35d7c86cae3959e22aa18073e24761d6722d01d756ef370922ff9176cf8396f01b24cd92ea2786ebfe835b412a7864b26a7c43c3fd12747708d5a1a0694a766f4dd38543ff11bd94690920b059b7f5551429f0b9d9610e50cc756325e237d377eaf9f61db07f94412b7e58d38cf609e15b8ece607ae7cf0fb811e37bebba4a30aa04d5e504c210995abe339916abe4e78cf044b2b572e602023a99f36c5d9b6564c21e9551f1fbfd6ffbeb863894f405fb25ec6b54282107"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7e8cbd6f9c4d7fddb1d4d29fccfaab72ce89c9ff3e69b2dc43074526180a0626","proof":"b837748996cb2272f879d9d600fc870698119159deb94b56f3df338fbf1c1d40aa5f9b883ca0eee229b6e218774ed5a38573f664a88cd028902e647581538d5d76487a160713e008b4a4fe388ef0f9d21d798f766dd62eaee1e74153009a13190c2053c1e1be976f10ac520c9b24c60426466daf2d93daa66642cc2da9c742452d098db9b10c2467ec735792fd470e2f4104ed669d4cde7dfde938f093d8810ffa742f87d0e16587887c3c3f95e21627357cb6158d59bad081d34b41b437f20f3a732f9c6ea37a091ea44db518e02f2825971d119602205556df7f478c03220334a9688604246048818a42147e620c2c85d000ed9494657da174ce92a52c4f5458720b2d6278562e83953e197cae14c11315c862b4921924b0b4d2ceb33f5d11f6a54e79511b536ccbc7be5277546ed997a0140143fe3c087866d75469db524cfef8f84487038b9bd16ec2a8a1ca441ec5e6c1edcf59f74c6a611073d5b817302a20da98b50813129d933f0ff2344dcf3b940156ebf3e4f61d3208f022eec2176e12e2e9422f443307ff73bbbe13f2f3fd42b2421a2976c658688378ea9f414a1a539d8b07cef1486e082855ca65ddcaa42a6b0395e1115bbda016733bc9b93d2a61fc153f7d7b4da70e69083e756bc3de6b78f9695154cff9a202cbe1786c7716c282e604d9976c5be35c3e6743416fd0788a55b50a4e873a25237ce42d196e64d9e32e8cdd649625ee12aa31a54e715970b1b03cb88b04992c0fd93fc5f6084c8cb4078fc02cc02c55a5b240a3228ad645a057bfb3368b8a0a0b6a400465507a8d5f37f95cd30f9a216bcbffcd9fabd7b3e0506fa856b59fd92970f33f981309f9666752ef3ddfa4cf7e44f14dd5c126133c111a92b637e14c47c7d80ed106c021ce29f674f172cf63cdfdf001f8f3a5628f36e7e4fa7e806a4fb3e5225709"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4213eb23c0d226da8d0a8319e48107b5868be30393595bc9637e055770399930","proof":"2e2f832415a1e9288124f2fe0db0a30460c6f8beffda57c238bfdf6f8e23c41d222ae3a6c5a686e3a75d095c070b458b6dd33ab9fa1c619b83dc6f3b0624fe28b8bf48a9e0fc34dc918e67b8f05216a7c7cf9601bab1443be4edde4808bc8178987e42ab4324f8230dea1a6dfc1b46d089553309dffa60ad5ff525649897d738cff6cf02a94a057376b09d398b9faef1c49e2f5bb6ef10146b8c9fcf0df33600ae8d94d4c9c788a1e69e696427f2c6b0d82265482331f6453726af8e84e2a8059600aa142c7aae39c23514799a0b71f5cecf63c9ca17345bb6be42117bb39008a052bf9928a1dd25bebe0b66e0395e6f70b15833fc34a9dd00d827a263cd3c41d00b30d84d1b5f4319d9f6327ab80966ab357afd283a2fca8a73dced10c50225043837fbeb280c3ab08fbe0feae43b67c70a711bbe31729eb4bf2e88b0b12e2090407ebaa20881fa5f2077508d4f86a60901acabe4e83e2e728441058d331207a2f1efd8ea64dd9030530fa39de3f03ec3ae148433762a51e421910b7b98324e7a812df45ee7e8cd21166e17a24bc75c691a6c839cc58f53534acf7815c3a761a69be666c674586cd37effb22eebb63ee6f55681e04c182616b3084778ab99685cb1db3de275f48465240be169dbb0ba1e70b297043d6a9d3c0c656505e4e052c617aa4b30e154ff4d190989bb88b860732ae8154ea4e438fed4eb1fe4650275c2f848168d305f85d964016d5226f68c60874003839b676c6bab2322b61a2e6f18916908fed752a7fb4b5f0fdc268a54311602128f1ebb0a0eb5a953fd39db44a44dc117800c299814878b0104046ccda346fea4d0e8d80a15f6df1b5e827a5f330c05bbedabb90fe108190f9e1837a2c6845109b907d794e06d4236a8db5e0319c82ea4ae4fe761fc977d42f03904fc6a39cd7b78d610844ba86339a3fa5507"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"56b08eefc843d07a4df38170aeea15fe41ea717d92eb8df497c7a4a7f433ff64","proof":"1c452fc6fa2b0e3b96c8b42feead432189846b7833e06b74fa43c72d5814d55ab82c7ddf54c0624df7153495ee1a43a7e10612f97ffab1ce02f28ed65eade2625e3e6a87a32a1fb92a1a8cef39f7d7ef9c63b2a6400be825422a9f5de9a09375acd4473bb57b56c0063adf75d3af2b2d72d077abf08fec14ae743d264539946c3a2ac3ab3470e81fef4ac4a0de21af9b79a7ea64e665b538ed6be5281d523d024929187d408d185545b60c60e3e50ac4c09070f2c9cda63e5551e5b02bcbfe0e3ae80e09f29e91a016a399ff74f29310a07be36c2ac474a472dde3f03a0de50ef4147ccfbe9fefc2f9490c47735d6a1557897e2475315d7c2e6bf3ee0cac6b2c60e8a863ee64123c348a41852745f904eb947a0ba7989f70c7cffb0928ec3a19e0d93e9ccd3fa4c3d259b688dc14c53034fa4fc24789bdc1ac6ec3e7f96cd616f6d33719797eebaba520c9f19b9f61736eb99550f7d3e350b9fbf406ac486e16f811edd2c0901ff6495157e31b721ed6fc72c181b3db1a468bf54bbe70348f14fafdd87e9199fae1356b19e04063f3ea0e6cd07bcc2f2d42c2c5ecdb5b189b2e50b96bf7d5d879d442fdab2c74bc0e4fbc123c1d41fd8b6d326e0fd9a8fc0a759e8f4267ca0a9dd9024d12cb915d41a183f32714c95b76751f9f89811f57fe4e1efaff0c6ab0381b1391dd95e8d2b52349e66594dca1c36fc8e6df6afce66e4b30251e1f3079afda2e4cd8d4960894197d1d221cd23b2a7fdc1c0c2826284c36e2c11702d39d219fd0c3f8f257e82fb175892f6b3a43ad58ff1c80ae403019411cc515ac2586fb2c2eddd1ccf8395a3d0fc6fb5970ec21d7bf92eccb6ac04b037833c0a676d7d852499a270722a5c046b4e03ded703a4516245c986d7f28b906de77af9174779c20df88ca214941113377689c38bb76b6a5b839d31a88840a01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"94b29fd99824292c2b2d6282b717c84a9682db317c45ae0d72bf5811772e4203","proof":"aa016c6cedba1bc3e2b74dbe45f82d35a77e2fe77edaf57615102eb358b9a75d929fb9fbd642ffd8db65a2822aba0d0dc421c90649da7b59a43e6ab61f9e725cd8b3b00387c576dbbee8869b2ebcf2423f9b6d5262cdc3024f0bae8dee5bc77f14905aa08d9b0545985040a41b6c7eca6efb2389d3ea3129429c610587551921cf1ca4917bb1b7731daef2a4446c08d46143a06cdf54eaa21d54530ab8df4f0e96a9ce5818537cabd92f83da5045aa10a61a664f9196b3ea4681428dbb7a150e626a13ae6c75463ded7999b5b95a8a740c848ea9abe2d72ede5471bc387899094290bad1b807ba2c876daf7c2a4fff096cd8425af999645b39320c08e16b5118789ff180eea85f339bf6db7e1208b556556873e15f41ae3795de726737b3f5402c2cafe3002c392f60e7f2a77dd14c3394801a756eda26fa07a12edb7128ea6bc0392342331e0f8d24d834fd2a0e7e8899319996e487a7cf42762c9432a4cc7416ea2d315d2929f1c95ad0f6f7dde565967d47e9454fa98a6e5e696e430fb001029c39972f073273e3b03160d05bc253b7854739823318e7358be0b9c956c67b1005cb29cd18f1e53acd53ad53105246e69e4533b75f0ea1655ac76a0e8df95856d9355d4c31ff6fc0aa7bf637b11eb21a47e0d9ce16c75de32bd3902ab7437c1067e6afcd13d63511aac812a4d4acd285e50b44cee215f5c96535732f04e959bc5b6148d920e5892131a2360072aa3392f8a68cf40df7ec7be4f097fdf12036083c67751fc6be3c97fe47ba41988a969b367a57ff31e66f2d5716ffa79a0933b8a7e7ee723f5ffce7f68505b2565887e390d5d17adb5cbb1dd05e863c46372f49c00bf5ebd6ff526c37d7615b2d0e98ecb37fa94c00ddec0fcfd5ce7560d105a0efbcff90cd17287f1db90dfa20cedb234524fbcfdc015d21bb4d176c45e60f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"60fef7e31f7ee00a2ebb5ceaf9b524fefa95434da2cd1d753be9f44910249349","proof":"4e24033e5b99ce0f8f6fa7bc962f17f59515b088476dfa11ad23459150adb230e8e1acf142660f89f0ddc9b8c20680799896c067ad5c3a921b6c5be35b1cc77ba6f2ade939c6cd426842d00bfa23979eca863442733dd533f96a64d74534fc549c3e7824c342d1c3d5f607454784fd211b14785bb76c8770dfd357f6758faf55c089eb9d4f0f7474565124c45470db0a0a3f472792ae4ca2c8678ff76a7e300a9418c70e113fa43519449616864c470aacd9e45edcf131935f1fe308d1629a016e7d087feac76554ba95c5c50e736f715a127aee8bc24e19fcd7550225a1ea069859b77c2c4bc95fd17e6596a87ca4cadd0fdede0c1c1474d725b4ff6202a60c886a44fb2ff16cff904e32bc0c4cd92c96cc211e336e2ef36b42f5a4dd5f832d82f3c701fbfb72d0a4c297349e6bca2a3fe3ab88e9b6b5f8902b1555aed5b643a4597b4a56d243a92cd0d0c23a98480276a09275f4e9c1c3a8ac611e15a95f78acd71e90eb56bcad1265afff52484bd811c101039f51e1d7c99b124909d7d32a2e044c58129ac79d7398c192f1c47e71d3b140f25111988c190876b509f7d06f86275096a4cd5c209cb80a94d9e34f9e7bd137e3e00804cfb192d3946172ea03f80bcf803d27b988da00a80acfc81ceefc233f7664cf0b69f0c2c9cdceee8606c0f235cc4fe16c3e02bf9ac789066f9c5e992c50fef3d8249c935bbbf76b0d167c68a3939561ebe6a6a0f99f51cad38eb4b00a830f370e1c9302f66782a7b5023806aad72f245296ed9d4d6a3a7874fa0ab680e241a7628fa79afb34f7e71f0ffe1cefb8953aeaf55c5904b9ea824d60f43406116ed40d59fe644780ad414f6f843b93eb37d259ea3c1f51776371c40579baa273e30374d0b1feab6edc41ab0d815da0bc729e6f179d9c57b968dd7c97df8864e7d7e35036e30f2117e922c90a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"606d07bc6c4bb120e573afe7131422a37155ead0b5fe873862daae2ae2d85070","proof":"aec9c024f7960115ab5c92d34edc580c8ba8e9ea1f4bc05cf491a1b7e56a2417ce7ea9ee0c462a3818485ee72bf2ded533bdead8ff492446099a87352c4a354520c5eaffd8555449587a6454071a7fe7785cf919052e411d6f65e1685145ec7b247966c7ed4fcc7969deb51ed4cf457e61c009b842fa12e12de3aac6a212b0013c285674d932d656d57c5767dc6a97bab993932efa1107a9b5bcba3c53b8550eaf5b9241cf082e0fab36faf4e082ae48e18d7daaa617938c25b894ee79bda60769853c1a109ca2cb4cd1ec0571ebc0b350e6b30b6b83df1480645422de2cfc03a4805661b354f6716a588d7f185e8f0223fc9a5b8efec5c5c39a55862395256612f1e66636005e018aa50522539cf6e6a4f74e7ceee1323287f7767b9e678a4530ddf8403526c49098d516ac151b4a847132166bd8da670909598fbf61b73c22fc2a45cd76be12ee5c46117cae5528cb46654fb4c3034cd8bde54dff6bcc7423d4b55c773076337b680cc7ea1a2f26c2432056649522251ebfa582bb8f5ec144ec3cf367456ce04b21618e17365c3146bdb014ec3032847131bc87e388c63a2cb285cdda5944b7aa20906d03534f4349329b3984cc0b8bdfc011a114012f431f82109d8982af04f376e1445f9b55b5e8302106c1caa5f6f98a8d1fd7807bed48e0ff5455f6f51a10b661a6b17660af90f96ea312f8b98e314905fd3890eafa395e31cbfae627c17614d00dc1c75d3bfec443e468dddd15ef90417f399004c928964b9e05d9de2f95f7662a30981245cc139500323eada3a4035d6960f48d782dee12262e6dfce1383c46f830cf6243ddb6ccbff06415280ffc805c925a6c701734888daca2fad4f9fafb5fd270566b62526422bb0e55b09b2974a4521aaaad0eb2677078bf567df96e46efd411aebfdb2709a09c7c072f9a32c368f50f8ccf0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6a8ab60407a61e4d9c4d6fdef44b0a987842ac6a740ec595c580966f634dce62","proof":"a2fe57a9c1768902dfc2381ed0d9f4a673e678fea58da7b45703d20c81a1724a28a74c9cbb7e42f9cea5b241f699a5170bc3b5f616b3cba6060d20f0b1a564175696ee44c4a79704073312499644bb505d85131ceae76c8219ec3c9548c47458d8cbcdd40403d6d05df647faec409ca65ef750a4bb34d8dd140d30a105f90311086dc970ed8386047b33a15e217dea67d098cc5640707898467a7baf3547810d17f3156e82d8661aed6490818cf1cc9dece382be1b77d83fb3dc222384888004abb277aba7a0147dbbe870b52e050bc54d51aff57b84f5312d8eed621e5e4306b8566e2ed31dc5bf025db001cbf1dcde03068e427d4cf9eca371fb0454d48403ae97fd02d148caa32a73b88853723f9edef31c3e5f80555e233671e742e1244d3659483573b327fab345fc7ee7ae754de0f7f1cc0f59b68716c63db5e423f174c455288e452e78f32627bf2cc02dfac6b3b752b7f00aacf67565e02c6238ff16381c0e172d66df84dffd4f53143aa933cc5c230967d1b3c026b5cb5e29906c6ae2b08de2978934459e3ecb261a9d6a776ba150c1e43f3304e0fa31ad609fb358de99bd10cc491f5a879d8094d4e91018d657bde63ea706de3ea717ccf179d13dda4d426a2926b5089b8c1796ab8b805e2fcc269a74bc070ae2e226047dd541677230ae659636c38be696cdd4d353b09c530815a3c8b786a57e4699da0c5bb36ba4f999ea953d290a00ee3f9fa88478129242fe8d53406b11d4517bb1377d92670486b21c92a63ce58dc9f310f1096e9c26062b41cbc5d16ba9ca046a57393660d8091cf29f704213551e900839a3ec187e8f944370a12d6300e9f693c588e47cfae089a7e06845da2bd3260c694c679ffb11a56c551ed52a21f7d2f7fc802c01565208afeeca15fefb41a1d5c840bb10a7b2ef5a2613fd7dc80945635e9c1002"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fcc4b368ae061b084bf3bf345ad46acf84a126b828f0ff6c8639d7a3c0ee5300","proof":"e4dc46a05e415948cb6c93cf508c3e6e2299cc26229c184c025890dfa925d352eef42fd17d83eb38a0fe0708d6b5454a1a8b78ea8dd368ff4dd921a6bb3fa72eaa3c79558894e9d204c765b11cf4eb55f7f51eb4cd9446efa5ea5869a4c7b426f22c17896652674de5091835205493219fca8a76ca72d8a93eaf4a02c1ddcc56f951a9c4a68cc009514367adc78b2d9981db237ffb86f303050bb6208ed25a0c91fa2c73ef11de42c5f9ea2a90ef1f7b42a173b75092fbe78c7d72c35a2ee607781d8e39692f69b2126d3ca0d5d983107968a731600bbaf12214766d4875bd0d84839f5c37ee6d700b286e08a27b62b80b8e9f2744eae12d67c1f32ad8ccc950f45272f5a4f51b9074b6f727f5c3b9de317288b83420bbb00b62c557d5f95a70d2a2830078c261725c41988248d32ac17b9785adb8b38450bb986a76d32bc029a883d55bc7c61f0a625e8a4d6b2a4e8b0f514fa5c16a858904238e8bda29ee3f3aafd84f5cf86ecccf3a402e6f0b3b6fa414dd5270a8aa70557857788975ad28467d17ee9bc1590a05d1863c2ec31a1996e406d8a19c3315d07d92271325463b28f056293afaa60cefa43df9bbbbfc4edf72c43aff95b4bc8a3a9bc3a3fb36217ece08f2c5d5d6eeb0ddaf12ad99ffaa0d24b850df944aa383bc5995e9605032eea4f5a42b8c640c4674f374d19d141a91e6ec8b43ef7894ac0ceeaa724c2967ee34deba1b50b4453e24138543b401d64c762e7b56867df9693de5d2761fda7dac6e1bd3e2a85eb31f9a2dbf6a32782649dc5e97ace962972955e01a8100fa7e1a0a04d90f5717fc8fbbde368cfcef5b7d08808d8ea41623ed6db5e98c18d12e1ce6c16d566ead8f613e1620e02541ee774c835753284fa8ffc7f3b920282e0cae6bdd89797df1183c9917f48129a120d54e8528af87e7f0b116417bf64d400e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"32d510abf2bfea20c13e61dd25e06e73dd6ca4f96705c0d4124093a866b14b2b","proof":"e038cebabc463d1e129bbc34ecf8e9c263cff61726568a08708fbc51607f9d0f76fda56edd24e66e668f2c934d981e51ebff724258fc65ccbfac5a0453e15868b00159838faff8cee15eaeb178dec51b6306af6598ecd87b1558808b80366335faeda22dc06fd33382fd570daccc7167e9ce8696a9640c50fb981cd5f3c2d76377193c74c69b94d478c674feec92074be076f5911fd3931045258194bb2bd4077d6a753813ccffb258d44755715c84b652bd81090f9088d30280e13fbaa61e02c3fffd7fc2a3090bd4745db9deba5cafa842ab864607075156703901ad1df805d0eea767ab238f8f24f1d0ca8f87b36561c6cea7304092e303cae25edb617434687cc439028b325379f63093387a657735094d79ec7fb10a4fed6cf1bfd9895646d914d9591a3daec427b1c5b8b9b669e2d959132c28ca61a9bf5ac85e46b918d23920ede29156210eec753e525f067f935f412febe0852f5bea29d407319964183e2bd57764799165c705a592ac7581d29e73c65d8b4107d9400bce4c0e96232a682a6cb91e401fdb22a87b96498fcb3aca55c7e008277a2c4f723f719f67611cbad2849ef37ce1beb72ae97ca10f679b02342888c46948556fffe00e12e679d83199a4ac0126290e77223f09cb41f8c7c901c4b7d5257be6a3dcb1122c6e6044851e6e8bd29b2fa0ecea6518da8bcec69914cf44716440fa682685bf2dc400922ba596efeca6cdbfd3ede0a72be23777acf6b3327e587455e581684d8cdd1270797caf6a0ed43469cea8d526e35066873e519cd08f4c68b8aee37a72dac30f682bcfc2f3a75fbbe5ddf3bb5ded0ecb691cdb404ed126b419ee91d84ea4a32461655298b8391997e742acb8ce04d56918b02bc59632bbdee77db167da1051002fa93741af140e8bb652a2fa7d6bb90454d14a81a4b0670645dbefca8c735606"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9e1052d9964915601ff4f974a1fd1aa2db20d2b7bdd812b0129cd80eaaac1b1c","proof":"dafbab02b2d2ee36587bab6658aee6f011d22deece28c25a1a985e96e4845574d8dded5e49aa88ee8f3e1bc20d67b56b0588340b51905ccec23636fe26886205300f8aaf9c60bd42b0982f06484d79a5d3f23bf71aa04076ca1dffe5682f5d5cd45b5526d84959652a0103c5853ba2bb65aec999389a9b08c9fe0f71168b436d677cd1302c4b402dabd815c45245330463ced3600c786c61c9780c8efd153d0c37f7b154143236518eb091088f41fb6d2090a48d4f1cd6d4ffe53a206df0ee06809fc8caf573d86b2ad97ca1bb7b19f1e8332c64f74c77c1769351307607060ea43b61e2499260c1e26f77bff33b24282fb455a49e16fd6fe4c733a9e056646902c6225fb14f4c4707e609234378cb08e97f8b2aec436dcfab3843de2cf7675cb89ff31151b78e3a052963e8711241360f1633d87d453dd37f411335d631f63d64315f51653fd1fe895c8420159423fcb3f13f831aa827aacb5cb77293558a45284647999e724c6971d8e3b6382ab4a73ed8089dda0cd8972383426bf446d844b4193f0ff296f6e07bff81c3ecb3e0bd73539f9fe405c947f87b844524e3ae1012a3ac347fb5cc7582ca3c21824ceba6b5bd8085bdb76f75dca22313d0885348d2e268567f031cf961badfcb0a5a8e80c035fbd5f0e51443c6795579ef51d64084493392f5a07a55fc768edefe2023be9eec30419704dc012a8978a7bf7b9402641b2895e2e1cf13d89ece8e15b0ec0f3ddfa32df52a297cc7f13cae1a673154f204d535178dd9c5af8bc31db1b5427347e4b850e4b6fce3b3a92f23f52c6f1560486791769a1526c5b34851f65d0d534adf67538f885200c9a8efbd319e3f5f60cf477ae67142177376d8046fc2172c21f400a4d1de3b63337fd36ce2ddf10b28a31a2f5cb45c6000356643ab70c36b762b6c42d63870888a125e28a8d08004"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2e3f6db694c03474780d9b866837dbcc13742bd38d788e0973b921c8eb300849","proof":"e27203c529766d4298ed15cbdac98c17a5b32b89a3393ed2abd215284568f95ce2634786c0892a70a061945a9bbd1d61a49ec8fac632914a2a1ac36842af1b49b6d40daac1b3382a008846a51909ce79acaa775776f525f7ea3b4fc667493445eee0a0149e2b6aa2acfe8cd22b6d8773bccdccc4eadce5d959b6d358b1501e33c1052b2b6de80f80906ae89649265c346b736c79a2a3fb91140901ec68747508358e464afe88852ee0f66442898ed3bdcc26e5b1882354fbe3400908e78a4a0f323e713f2754023d04e53c51b043855935b5b4a10136b8e79a4b6b23c4808400468d3b08d274d5d102415b1ea6e8bd4878912f7eddce9eeddddec218f52dff5edab12f4ac7220809517c0edd938a1b0d55727c1345c656b4bff7aa1772b9c97a36f048b78cea74a81779d9ba6e37afeb282d2911781fce18e89d2bd8a4ae5347de16def8c942f63e17b9f2d170077ef19e7cfb9a89a1a3a6e6d14163cfd1d048326132fec4ed56f899b0f6e5b0397e941dd463a449c2543e763bd3be55b8954cacf75e03245cbf8b206a0c838cbcb6fd8ea1a144b513d8f47a6d7f9d28225450b03587ef6da79ae4f47bb57583b01cd81a15b62805d9174c4f94d48a10e34255b452447f8d4bfe8466dbdebbe5f886ef876cb39af0b30a0821b36b64a5e4556e0cf1e51f1beb9bb4370a67d2066772145aa24032c585d137fccd6a9f1da95e075689b86f5b6ad9a010b3b7526a639ab45d4b76eab6be33f87351a125919fee5f96323e83578975e2e4ede319949ac0edf5fde911f804f314f8a03f930c973b61b223af6d506c7d1e4db4bd02b5a3b8259f0bc9409a92670d024da01ce4fbeb45b80611b77390cfe4ef1aaeea53eb45d9543e9d8971ebb3888df004741e7edb0aa298b00d173b013de8f70fb19e486a7fdc44376941f7e1d9973bf89df6d25806"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c42a5291d1968d2e203351bf6a2183ce572d869fe60cd11e6473209398c3b948","proof":"2a634180c23d2e3dec0ced539b9824347cdf67cd0948ca17df7ffd523265356cbe7035848ab81d8ec8df4d189417a7fe243330cfe9b0c1df1faa8332f6de6f54a2bd6fbc566219c1e00b42e09c61a529dd62a36730b7aea5c606ad40ecdf1c6f6cb7ee4fa3779770f8f34284316101b50086be6935de963b252109dbc0681a39c35f818835bd8dce218e3d02882f7fd1cc1078677f989a1b109df1859b647c06890637be4fa468cf624d66939f9cfa11fbacdb8de5e56fcb57f11300ff36970f8a71ca47410674a1bb1a464037f488910f1c9a5533013743d2f8c5de078c1e003abbe2410f06e8fd810020ff0626fe76133a961ee44bdd13e32b8b4e60b70d701a7316480b23513a285b7ef54f34a1d17f0319c42ba2ecd7dc1d445cf7b9317520a484881b8895eb40a9745163eafbf509a870dbba993ec0a19fdb0da9ad637cc6da9f970d112df21997b29d4adb7123b658f0dbd3848c58d221f8958f6e322d1c4bf21be096f7c23983dc10fc5577990a24bc14f996934e647eaef8a7b7a317dc7858085fe9737ecbd6ca789ab7d3c987405f3be2d0740eec71a167944bf62976b8432429b1bc449396d9af60f862ba8e57795b952947d56c93e9adaa1647352a87f4a2dfd70d60b7ccdabd324627110c76b3044c6ae1858209ba75aa240f77b427e71bbd7b15329ee116f98cb861730fcbb17f694701d5ce20ca848b78d8600258d1bdb451ab8e0023915ef9bfc9d0f5b176af378c1d240dea29d32d857527a880f7d46eba652f917f4f6534b2fbeb4a9bfde92be198436baf40af3d8b7d42c880f9ba2db3d5a74c2d3d4667f1e2f158dfa1acbb5be462d814dfe751c5c0287ab56c8c07cb6d230f5b620e9e8707b61e015d80545540ca2f38b9086dad5b0456a27832e6689418a8dd8bcfea8c50c402c832c4dd2469b3366a9263e4634c09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"50dd162c2a18d3e780b1d8d4b1dff36f8cd6fb0da44bcf679c7f5fbacd247a35","proof":"4e558c524b1c7250c43154e2464d7677ad6119617b1e53f26deb5744ee5d717d52cfad481928e5f295c293f6bf3ee86560d295ea2cc844434c21ae819b92131b8c19696bacbdda597496f178c2132093cf7ac647a0b67a8a739dccf33f2e7d1a7c42806c702ea91d8fbafffb29695780ed6078d6289f8bf4dea8a7e91e73845b4e6ac13e5c2163faea0ac779a02a993d7d0a546f4910781d0bbf96b73db54d003f3f1e1355c37ab8aa04769293003c2d39b136f2236863881cb9dcb0440c1001105b10bf81c4e5c4032557d3aef51b9ce39e8238614517009f4490ee2784750ae49e7e0862ae83a7e36bc433ac3b40f34c9be110d2983d20abdef0fed24e912fa6bbc32aa44774629de1edb69ccd50baeb1d2673c5d884a59374ba1aa79c1243744df15459427d718e72a179c8ecc65b2f8e818e9ac00039dc52fc3c08916973e05c9dfdc626ceba9d65041eb21ea0acf0c27a4776a7850ad8bdfd02c0aaa02d8289467072ce9647fcfc29cad2796c3948ecb347b4adf072a0be84238d26fa62d057344b0a035ed8d34641bba4999b603e0260d76e1e38e0e0b5583b00f4da2eaabfa5938ea907e639de033a809d034091ebd76e15e331667de211ec3c1384613090709294aee8b7ab6176b5e73b6d84d21385416debcd14ef743e74258945140a3a7212c050db504018ad971a42dd4389864c31456bb9bd6428cb0444f4515e4043cd0f8fb130a812a99e27f9acc41160410ef2ed32e09ca72016d63f99671ec41b8424cd52589124ce48aaa6442c9704e906913f3f94fd5513a61fd82e6a09220ffa26f8591d23ad058987888d4ce3b884b9e04612dd8ad0c8e31e94afad2a359fe01bb588e57158d33ae81b3804a863db5d5a1fdf5fdc3ff47e7e8fc8910b0b3aa07564a9f8f4831b5e30ba47de6215cbbe994a85a72244c9d69b78eabb0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"905e8f6e25e45e1706844a9da08315b966a1655e2e8c314065e994bf5637ba0d","proof":"1af1ee7b9567690f4d59e5bb0aa94bda8c710c40189486ce2ece902282f1fe07f6c414d5f18567fc9ec5733c4c336328df6092970845ca763e308b9d92f8413618d3156db11853aea4e9216e44726aba73d81c249802ce9ee477110effe6250e964aceb9c7ee23bdf6914d4a5a0904dd9e12817401e8233e44d70b456aeb082cb0c0f1cd7a4dc31ad2d7779e008810af6d080a530e8b1199626f64392fecc5084311daaab57bd54040f1b4e665d44c4a21470fb3e3e8eafc91faf804dec50205a370e0b38108ad69d941367a501296c38aac5b2fbb15d40a36075e2d046105059663fd08eac172e901619bfe16a19c4e4f8508dd455857653b23eb0c2a630d08643e1d4a94ab9c314a1059eeb77724e050fca595ef9094d3ed1f2a95339c293a28fbace0eb029799d504f5fc52d2dd851b6482a776f7ccd6e1f9c52fe20a6f043c4482b729e1620cf6f825e25ad75d251ca2270775ebab6b034ce442caf50039f2301be1aa0e9f968c14fb5360a563d717198b289d14ce3d29c5abf371ed245a9873e7e662ee3824130adabb4dff02529d30781b8ff2b3f2927f14cd0afdb86a44702aa610b4285865c525c3a3225b63e4763d1e1b852da8e367a059f556eb4a8ed97f48b16e284e178d05c64d7b8cf519bca801672c9c917df9f62d09913f0c5e9dd079e0dc38cabaf75a91bd8c6a8bd108fbbe1f26525210806dba235cdf144883bafb7cb4bf2df3bca9989cc4a7b4a9cc8d41b6e1e78ca6d1c2f7bad21958d420d156946422706deeecfecf8648bc6d3a704240e46a66acf55c1e3a58af4676c53d26b678be696120a8ba4564f43243b29e49f8edba1573594bab938e7b3a36ca32ed40e477d80989becd9dd3a9878f86f85ff8fba2d0942b48083d1cd2052646cb26aa8b4822ce764a26372e05a22c47dafcad9540000bde29a6b0147507"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fc618dadd28edc617c479a5eae8220bce5f9d5722475b7b6fb2676b44878a67b","proof":"06f469c3d4daf33f22465993cc2ce5db697ede9d8d7b1fa831c11db5d0fdc3513c19ff64642b731121469cb615724d64a4c514e1e73a4b421488c00a660fdd7dfac572c75510e2876beabde63362f8623750ca5526cd779d44dca4979447037db01454cdcaa8b7bda72a0bd40fbf2595c35a9d8ecdf9581f9cbb680cfbfd3b72e01220e495cf77918fcd2312a7fec74f797827397188226d630f4c472043d00dd44521401bd937a72e7fc0424cd37e0e78bc152c3ea2c9065337172d5f03800caf8327857b66c7f8402995ab084514878a525ae6dffba3582178cfee5e918706b2d7fdc486a2e6a9ab5311e2ac7d49045e4842c7647236924bfa9c83f5a0ed2d22d89271dee7897cc75db813df6e051cb70e58bec3dc5489a65a89f1a421a25fcccccaace2b40171fce6f81a9e43df59f835b2727df3595d2121118f7c08124296221b8a1423ee3d1eb8970d94071a2eeb7e3b137c451e1739da844ac0b3865f4099b66db17de3ebb71f902843d516ca5fb5ec5a2286daca4b625459085f5f10d2a0fe08a71f577fa53dc93541264f97e806f6b57e67c59d241be9d0ebcf0d7c043e61ed1ee932abff44324116665fb387cf3682d54ddc587e6c909e4e8e7a3728eb7ffd6a6d78283de5dd55ff3054f991d57bfc0c53254554adb376339a6e20c0f2373755653d67624c11f80ae3a5a3343ce2af68b9926f8f9bd367323b27798acadd440646bcaeb837dd2c4845fd7cb8e802d24ba409186dbe8251656ea64e005ae40a983c3b095f2fced71d4e504481876001b65a90f1bf8167fdffbb7850809553dddccd5003b8079cfef18180424ed19135622e3c708896e68e6f096103d969de9a82e76f4d0c77f0505defd0ceb1c38c0a3e4c29ff436972e4827d2b0c11403c08e8533e9bf858c0883f77c20d4e6183080229ffdc9921ee216fc4100e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"22c3ef56cd5c3b17f70e3427941e1bf6239d0f9b41599629e39b665ad5a4d540","proof":"d24bb94dbd8bee29561004f1a0aa1a01ce49cba3afc8a7d19ec0050fa2fe317ad48129c1d0e3d170a476ca0f52dbd7e1ad8742335b7ab45a3b6afc5dba60aa4c3293a677312783769ee09fedba6024d57f0a89b300ebb19b54137bea872a064a76bfba69206000b01ba35080d1f88859036be04e096c5f16ef37cb5a76bacf283e238aa714b5892c1ab862546a49e5a6379f52737c637e9990f3cba5dc5459047e56b4354106f6dd8f892689c5791f1d28f6ea31b821bd0d70142f437bffba0a958e958588ab586114d19dafbfa1ce22fc83659703e5bb1d35e0682414e86b0b44e16a2597e7ab7c3309aad86556d81c4db57e646ba90d4e67b993d84e371f4c2e86f09192e61bf2d4b1ebf1f50580559a8ada35274c33eb8aca9788abacd24cbabd388b9d361349e71dfa4dce7cbbd4ece48398cf020b2ca46b57290dd43b7c082f22e26b17710224ba6c9a8edbda28ff3e7327852dde7b731eb0b01a4f045e40c13c1b3587aaeee870e62432fdbc9f2ec83aa781729ce55accb4fb411fcd53ae7f1b6e38fd73688ba9e3e963a858ab3f88e2fd615d6073110f60da54aa553668a0bb3ad8ec8f7190aa5c37e0fba40d746681b525e1b86fe0a6da3cff6067533cf54bc6426198a57e7270d9e35495f19c4befeb37c0aacca2e28c5365d3a735a01cd79a2ee18993905d67a69d71fa8e43fe16caf7ce3ec8db99458f4e0a3e7c4093808d8e334c3f378a3d51fb8cdb686334e1b04d84c0afdd80620306c2144a5c6713ae1d7ec6bedc7f6153a6e0415d832b0274f3a48decb957ef046267c0633e0fde89201d63bd4a9b45097a584ffc68a895f27cb159361d6d8ae8941cd32c19be2f14537ef4083c1d82dd7da28f303374bf32810ad75e6f5a77a259d45502e713e55aaffff151c1b0b98e67057b2a29cf28364490f39a570c87ea9cb2c602"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b012b78916cc1d0a9c5d9128eb0ae02b682f6d7b01539f11687123a04795ae0b","proof":"dcbd3ee93fa129a7857e6e44aa68ec95ded9812eb83fead1b27737c9a7c1bc6a1e5f62df947b2ab08e9d989b680e14a6e7c01ba2f6ed995fe78dab45e8f0b576f44b1075e4d180af3759db500707fa0a4ec37e5e79a3b00b6830ca3615df821ccc2dd0f144a1231498e0ee774125f1cd0214971fe15fac57582ac9fe39e31e0aad649a87be8c0f9c5a51fb59e395b0c33860078d4a931956e48b6a95554ae40096f3e8fa5e823109a84ce5c29378312435c9e374223f296d78bf1133cf8a29012851e373228506510bb0f2d0bd207a1be69194bcbc380f0fe247da774e17520fe6711128b5dcdbaa14307481595587eeac1cd85dcb6b042f57a0a90853a28918fc02c4d2781ecd5536055b64584000da8f75e2bf478882f7c21558b88161e82b5046bea3d447570d5e02cf8828cb7ce4aeb2afba657938d799834c4864c1fd6178444ff53aaac007fecb096e1b4c69e99fe2c3fcd06aafa5cae61550f3c75e1028fbce8e83af59e385226e4f3cafe531654956ff4014a6ffe51d86fb9ed27f1cd44210cf9a66a3a4aa31a91a90b513cc9d86c0c1348f7b2c5490e7ca31d2650934e4cf0b6e2054e12426c2514227641f1ba085c178cf94c0dd11328b4d373435c86b9f4419c54b25127f54328f211283de8558693f042ac0ab9332b8af95e664b039847fbc20594ec84c1d0543ce58aa9351555525c2ba0e6ce621f226be1326dee0f3333a0e4c1e76043f9a3a82a4598831636ba91ee58d2d577067c5dbab18226fffbacdbc423f9606371589f069c93f8867c73fc8edd683e7e5b1a0b5007f54a409993cc5b1cfe0017b31abc45d70bb2cbc5db0593ce2c1b5cf47ae48236a99e9089c2b44e792d14a5ca392704e41d4264f145c0a6900924ed1577f941b0b3ef0bd66e6bd46067b5b5068ed756117a55d64789f11a869eee40b629b761406"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"403d5af2b4cd8bdc4b7f26e3d1dbb6ae78ed2fb5d4340404b03289058f23c049","proof":"58a4c58a26332d9d2cb2f1c3a590f97495273c9df8427dddc0e3f56385d16a6b2a2ecf5be8265e7c228ddd0eda57ed366d9fe4d7807e2e7371cba4fa65ef782cf2e672b7c4e9539e7c4ddfcd2ce53f927e279ac4754a1d99e0835a76d9368e6c3029bdb346e70df174a0c033c0b64a9b09c93bb2f1cdd3ed0da1f315098ec614f9f8db797d8123e7ed8421561f19a5bc247d30f9aa82e4329b42c90995200205c5dc84dac583ab14882c3a4667c0bcab63221434063ee72ee307d803402da701cb8f11a6c1dc936b5237e8efa0ca85f1e5d6e5eb50d051866505e38c5725e90e76c234bc8224d2e2d7108590ca82952ffa9b5a9303576ac8170d87a28cf1a47c0614b98f5aacea5bfa3933818d1f3c405538a97498f65bcbdd4a5c81fcad470d8c560d6f393ca0c99b1a4793d722f44caf430bbc36e5cfd29afd2cd86087ec62e6e0dc2618c8206c64b903247abc1847b49de55c40e18ca311e02634c745ec7170591d72ed7dc0533f2c6edffc2ca8f8c1bbb527ab1f6400dd61556d7bb901451e6299870f537f4ad27d58a2ae63e73dfe1e178f2643def320c00f71ddbc164942a9ca74d2cc7c0321801729dae7cd2de302ddeb7c0bef8036348453f220c17412889fe23f4d32a3ec68c72aacab379999c9180dbbbd8b03e5b41be7f927c314e2d3410bfd6bad31b4c36b4229fc9ec1a87ad03cc21117f5e748a594767b1d47c897ad1a1a6647a3e8034fc06a410f608767b88ef7c71f8ee8c4b838bf40e845d0ccbe32d583bb5e6a5054ded19555c477fd9e05086f220e8c9e37dbeebfad3b482b43925bb46fc9267a01b90d0d5af62a87403add826aedffc656168c65dd70c5ba4a2f31b819a36a2077be32b477b79a6e9dff035d0b1ef66a9110f374200bd5bdc8cb01085095234b717c9e497ee4dbb396ebc673fc43198a0931b55e3d00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d40a7c55d18388aadf4b6ca0c18b1d9d30753d3c8a68fd36959a23298302de42","proof":"bc2cea39aae00a799b8b53bcb04ead35bedeac8673a0e84836c0a8bf8f6cf00972188e190f434736e70189f06e240679acc7d6ecf94d472e14fdf6d9f8c30f1148d5f145845a7f44992b3fd510cd11602c7e14d974e73b42349699ff81e42d173cd636b79a589b0ee3c51bb0ba75dab9e82fdd2f93185b0a2081485e04b09921616bcaac1fb86b2922cfd6e4976e8cefa78d75dc65b5929b9e920e58ca253e0b33b7a726280beca6b7a01526a6da4d73ef61d88446e0513321dc2267eae329072f32dab65a42faba2778f2f235879cfcf547e8cfd3ecdaaa3e525118761e9e027421907f9bc28e8fe55abea38054d465eab15ba8f53fbdb5a7e07fbac0deee41dc6fb912a42aaa4f3a7d0c181af212c777ce1d3f8f3d3cfa31238c8323dccd1b06821e5df8f45e384fc6fe1568e22e5375dbaeb6124be70bc6272489cbbc9c52e00227558ec6d382ae6982a94db0dff163a41bf30a1c66549ec98a0a91106d35a459648787ebe35e42cbcae6f92e37be5c2c483109a4fa9ec8d1215a860bb8345a0517b003525c1d6a989c7f855d38fdd6611e5e5aa569cb1ed7c0cddda7a006c8f092465b13df6ecf925bfb904b2dc8680bbe51c23eaf505b52444308838c67005d9455da9688f10a697c6875d63a15d900b092a0338a5eabccc99fa901186a08b2e9fa831c5a61a8505901d518ac4fa4530179bfc95540a7b6f1d388f2b71aacd8f9af2fa5bf5ade7cc5d396ed6bb4efd7bf7514a4e2558261ae6f895d7b21184e55826b0d2acb063911381b6068619d80ead64ad8072e7c51e2f70b696c45cecab807edf1cdc5f7b0867c1ef0b64fd86fc9f469210bc0e8b400164f0f9360a61c27bd8ce3bffe851b6acc9acbe7c5bd3449c8c1befd255efe737b29e46d0a8a1e5e550b05da9fe67d049291bef2896fdfc9a89a9cc3b059c24d2640645004"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"568b8b75fad0aec1859ca30477cb63721161fc18812b08b4562bc625b5582a78","proof":"ae1db63e53b0eb12b854bbe5a5322a4621b537a1c09a9a86ffcf6056d197ec1bccf9eef33d2a2fc00414367d80e6fb039ee4199a9c1181ad9da9890a8a7bde036af93f041f18636777ff6327c7ead6b887f29035f70af343aade671789afb27a8a7b2d3e5d4879f804e96536eadfc6dcaca2afa1ac43db3ee28c65a4bcacd81a4d97de9c67737e4e12fdec8d48dfdf5ee0d5972ac2289752e845dcf3e189a8058d4e1fc1db5a0c8fc25ab057f97a9965081ba2ebac55fee5afabd1c9ab27b101bec37feea6571ba743e9260c5a808cff81ab33abfbcaa215fd6d2d07563d6c0f3cdfc4b4cf790f8dd86e33eb517227d1cbaf3e81219e5f538cffd818ed906101900577302e441b44063e5e59ef83571fd59a65e714bfa9b2243ec2f313233303460b0eb7458d46642b6db1e9460bec26c8ca4376cd331d0f81daa62929f3ad66c26a9ddd8eae47de985851f8884a95090235c2cde3ae1651fd2ae5a6b941fb56fc4992bc89132c1b590fad37aae6d812654b3ccb01c8c1a951005517ac8cb736e4d7ce6076db18095fceb7e16ef0a263c800571ab9d0c808a62ddb61db4e9a2ac622bb1ebd4ace5e41aa1c765e17d2ea152321b491f21f8850c7065ec968796660f016eae1dd5f283048bcd0cb6fd08be66d14d0450576f0ab9b11c86834c975c65d7c7cc0740cad9243cfa9f178e86626bbba092af8b0725d10ffd833a7cd37a0632891320c0797012216b1f605f3f40f66d7823b52c8a0c9332457a433da162ac4bae8c6c496443f5f51a33c0ce92284bfbf0663c9ab3ae9afd66bc6c18575949ada246b33de58875115ad9c73af91ff4540f309f681bafc57fc702a5dc260aab705e230214f55fed1bc1ec6369a97920666bcb6e7ebf19d557dd0422d090df080c6621897e18cafb8d069e77e0872d2c3524d0a15792f5db3749e0004460c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"08530a0e1867c6a7583561dc5283504b88e6b5d85fc911c3f11040c554e80d60","proof":"a89ea62a8f63788024047ea2285bcc73d579cf04445f940c240b5e0b3a9d5761bc264807324e36823aebcc2b6c985002a1582f3ff2849b22a482788f3a7c146b3894e72175b3589670b9ac8bbb1fd40c60c425e25b8b5d08f6adb9566c988b05627f64d81f612b5feabc951179d0a63e4b0e3537cae916a55461abe74acfbb2519195408a316f56677e61a4e859edf3a15e6880f11471f8e4211c7c8bdbb320aea4b34d4b12eecbd914a10f51c6269f5545f491dcda33ef44f7ce5b63c8dfa006cdce9b9bbf76855e0b15a4dc9c0e4d6a8393e2c40864f68ad36a1430fb23e0cec3a490ac834e5967da755c62a759b351a9cc21f4580f508401cc49b8eaea357181f212a5821fadeb933a8552e530fc4b5ee7c17074589d338edc53f7961625fae356123bf62617548c80b53eb878307d7d2c5cad78119d46334e5de49f78e78c6b78773ef622cb072b7ad03f46540d5ba27f4b354d185d3c16417416833fc22a8c6d5321fd6e7e41b571211ab26953f04e45fb80c49f29d0c3c769ae05c1429a21534948edd23b30ddeb7e4f7a7d806feda8ee60d2dc8b29590a525bc9e6d4e9cc6e37102eccd948a76ee61664476720ddefeb66c8fa7bae6d8feaa2fe3596dc422928de72cf87080f162bba2103bbe4f100e8c9709c06c3eb046f8f8117e73d48174627c0d25d36bc26d428ef5a404e1b7e3e1e60267c12eac3a630fe5805f0616064d31f5be1a724dcf3c2e5566bb4eaac244198e87aa5c8865d860cc211720aa34c31f49720f232c1947f8a89e8e0d4a9378d0caeebe5df7eddbd262f258ec32c96a86188ed1f2a3837946170bf6018baeb18b91f9ed10902a7174cf4863230db4ce4c00ba63dcd9a743676c940fe4a7bb8294c8b8be1e30d370ce0d280b847f22b7b6520817322cb92e2b8078045aca248aa66588e1045186a415091406"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"083af02b9bfea7bffb37effcb3255406e05c33f17120e2e2f24f7f18d73e1039","proof":"9cda673da134acb7decaa6902e4d673651f8085753918b816608260fb86c194e7ca96d0f7815961028b1d721e93f90eca6cdfbab44f73e2148bdc915d6b18e0e3654506306028caff7de592f6361cc472c3d684bc5335432601e0f693e207264c4db7a97d9ddac63ad4f6964eeac7b68acf8025dd190dc9f2cbd0611ebde3e241d138a390e2a80b378e627d6244e718c33b5a6e14b8809f8e84ca7aa22f3bc0f7f5e76f0cb50ee1b853a1a7e99465e13250b7fd19cb8720e8b69ed983698e90a3ac8c7ad50bcd6d5262a4e73ea9c3384827e9f8b3009418727733d6f5e8c9e0f8ec92d1d6e0c4d0309479d37b5fa8f5fec52f4c16bb429403bef1b99e82912324ec46e304d757acff3b9fb56c434d5e58b5e0d5f8c271e3498d6afa728f4c4216e853d86e7113c5558bc8adb71847674e1e94b1d9ca95e8e3e980615a36cc740546a7667aeb7753c92bc1f3717039940512a5e5ca9b147fa37416937b09b5278988f806e8e7c1234002381bc767e2baa7d7d4c7c18834a5f2c60ca79ca5f4005fc06bca5862827d908aade082df2717879a259e6a81af9a97848c5c4b75d7a17f0e6dcbc475c1791192a944118d68e9d47d674f43d0f188e23f90b5931bdf8011c97bc1b7eb79ff362956539ebc23887f1697ec01a5e18f880dcf1cd1f94d30f5eb6ccaebabfb77a014c782eec1de1149f297b4d4ff6c34e88ad2c772fce282200989fa72a4006379e9ceed63281df8e1c86d616421d678df74187b040c52d51b667ed21b270dcf3b64a6956bb2ed34f6e266301abffe124ebed478467ba1f10328ba5c9503443a0a8ab05a386ee0835dd374549afaa1a95d2d6642d0f35402a9eb62177745fbd9da5298d938a1dbb48f666e9cc2e18060d19114ddad4e40706880a46f410c4e1c4c490a17e8ff58d0416b363b2f13c0e4b52d44bad00d0890d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e05baad127e8bdfc60a985814296e34f8c4438f4141a2d68fe079a352c267f2f","proof":"94d4057dd1b26690b53a16ad3e037882f713ef2d02e21b8d4591060ea3132b1116fcbe0b0bd21c7156dc862d518414a8ab3413f4cc1a0d2f82e6b2b8755c7169e43c120a1d3d499200284d1f9fb7e7d918472b3268826dab90743084a653af26a8be288a09028c360f64470bb315c332eeea56c844bb089e0f44cdce57630511a25eafe0dd0cc5c41a0afe6075930d7063e4bcfde3102f70f5c0164e65b93f0b6a0c32fb4c77b2b8057356c8bf3bc49c24cf5bb8f1d292c335c8fe960de5ac0a9c7a83123a552c25d49c51f219d8809c5c140593645b1f79054e15f4d2ea800156369860edbb11017f4f5d4b084d00f15a327116f1cad8c1818197fcb5504c1094d3803b1571faf674deba0ce8f1cf1113b06a1d5a7616f29f928e95aec702723a487d98bc7cd4fab9a9c310f6893c803b03b1f0cef5609e729a86443c7cd92d42f2b8f3292fda137efdcdbf89ced4ebc388012023cc11c2dea8c2d5bbd1a739563a4733634985a530bdb9dd0fc53d56e2b818a6ca15c2cb11ac8cee0878242dc653ef49b07f4e04687185e8c80f92ab1e10ba10fa818cbd2cb1bebc008fc7185836555d09219824c20139a31b8d5f9cd8d98911db89a55ac3143e56974a415f2241b6f8bda37fe02e6be8545fb5579785dcdcf15c7f4a628af9fd78ec3d7920aeaca0a55b04bec0c403500e085c52bc94ce49deba577bd4ec0e31a8c60b3334447f18adebd54ffc98fb36e010669738a551b331b888e1042d278976ccb36c3d945c68264e60ea5259b1cbbe7595a9a00b546da8711b472ebf74d2068a927960e45408479716fbb96886afb4b4560e5bc31c63f9000065c64acd559a42893d7583514330362d96757205f11930dc9574f75eb09c5b2bb4682204c46bb133290559792bd71ec77d3280e52a3a4f69b95938123d6629c1150b545c01369ff2d608"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"207babd23c8fa912903abbec57aa4a7ca21c6abd6f657d72634be0494e040118","proof":"341b00f1a35da8019b293aad57c27ec1a35b044c8ce58dadf08e1c5a32bc0b4b3ac8da8bc09275b013af10727a64cabf8abd1f8d8ec19fe6f7282a0b22838a76dc1a07b012892f34ec9f14ea2992f9077fc1dc985d2c8734afd3890466486219329f36bf07ab3a9875b200ee50310f75893f5893f7724076be698e5fac08343af3a46a68cf2bc729e425b455f8b61f9191007e2109bb5d41e4a833a3d0bdee0917a342f3c46caec1dc113ffc89679299085d9955f7dcab98e97de53540f3cd0bca0dc68f8441cba8841d49338390de152ab5b370bdf319de8657fddde8fdd80f88dd1b89d74a55a7106e531e0bab63e9d7a22172adbb05f023f3b2e61d04651632b76d77ccb1205fea9e7b188b7d18688bce972a7244112af9b0640b474d2951e2c07be01990da55985ac24aecb03f6f04b62adcdb687da6123df5dc3654f0615ce2b2d31e7f7b4433965b2fe875094531f589114f521d69e788b417bac57a2fd6d1641f76e266c70286247578b1da90a1f008860e5a34bf8b9fc9d49157ef469075ecf5e63c7692d7cbd7adcb9addc0cdada66b56c5c8b7538e68fa3adcaf043616edb3d69ff47eaf2f5a758126e40581c8f598c483668c6d88529c76be216e92277eb77b361af78d0b95c68f5fb239cdecea4242d2bf3d94a5f816ff71de2bc06024b4965d3253f4c8ebd2ef25994184ab1aadbb95fb38cc0105f8385da30d8c567a6a1893be2ce0a80e7135a301035aa7c52c09c0ac3336c9d840e1308f5ca20bfe9841e0c15c4639f68f4c98658e8f5edd36c35d66deb18146dfd8c7d60f70cb83f7a35a04100fd43fd07c7384f729a206f3f3fa44b2b2022871ae7cca6e97dc8e99898d0f6d2b817862ef6ebc3650b1fe2bd9d3de771c249f48d29b9808edb2b22ff431fbc919a365f10d850ffa73792a7c5c4033a79773d659383bf00a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"36efd9d638e78ddaff41858bdb476301963885a10946bda2780b06a9b39c387a","proof":"f2410ebf75c3989f9082601219d92981c836724a5896a1bd54eb31fe1434613754d5d41438c1f540f2806a5869c2b610367df5b2cec6f5d87c042b89f213652162e894aa9e4ec956d00342e4432eb2869d5f0eb76a55de3d1e07c83a6ed27d77643908df36e2c56a01f790e3f76cec67be56d23158ea0b963725004e8b38564730393d89830fe3337df39f5ecd7fe8e9789ac47696799bdbe785828e99a5cb06f4590b895080040864afdbfa004233f14cb1809c495b3aefaae38882a0fe13084f4fe5ceb76d6546abc9cde072b2e1a0b4abe85c1b08366dd452ac5647ddbd09a41bc7a0ccdecc26c29444b0ed0b4c241848ebc1e3868ef0cbe3bcd6b6c2c65d6c94dc9c369e8dfa28185d4ff7533b2f8e302258c1b78c65eae8b5b5efdda67aeca30377dded4c18b60b496426929f6d7baf3d8a1bde0fc9a288bb2b378aec600a780642e426c238fd19574f8e8d1c1a58cbdebda7b355cf9bd7c5b783b86433b4a27db4e85a8861cc93596b53b3bfa62162213c055c7edbc57d0351d05a5f597e8e6137719d2c42d0c7b82261d7558d7b19fe0276c83f06ab02dfea739dd01526d1108a55965cec5eb378fc6cf80aaae1a2a4e8b5829f3fe026064629d7a2110810bdf4860d6fda76c061b061f0d7f05adc49dfd27d3eec0b5199baed2c9c2a664d7aef1f44f8b9f266a32a1a66395a60f596df321eb6304c687ebca8aba045187b212e738aa004aa8f85016fd9cb7d054eecc8d7562b54c8fde281d686bb35a20de7545fb43de4f8eea8aa2033b2f7ae0ac68c78d64a6c6b98ba502897f937526fbf85d5d4d390568591e47ecd079480fb1b8ff1eefdcd34761ad1e8dcb537ac3e7f71632bc18ff0a9655bc7ba78b383a23ed61ec01f632e770b3ffbaeb50890a1695273bbe72200aff497ba0a5e08b4732ec924ef0798bf7422d12705b50c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a6120b9c997ed852dffd81f0365f8b566c1d94de23461f05b4872d56d073c360","proof":"922b16a4e84ca67096de5f4ae85797ea72b35524462f22bdb27abb7a5776a213b633c12d31844fdb502153aef414cf6ec63bb73ee932ec912378d942f314ba04c051a5ccfb41a362c30e8a68c08d2765db0c61c5a6b9aaf776ae904f0f8b3331eea56255457d8aae59147f4160286f57395a21eb375f6d9ab94e971945f8fe466d015f5d6d6f7c6c4775932ab84c4815025797e245099fba218cfe8f26db54003170aadf761bb4082859f88558b73c3fd6180b3bfea9e2f2699fab5e67700007e1cf604e6c9d4404e5cea6ed903799c206a7f4299ae56159fa3e517e6fc6980b360d148265c03e5f61dd8186204f325e209ddfab0a7083425826566d8e816a1b825dfb844f29aa0d3e37a336f85cda18808c53d436b0aaf900e75aa5bae40a1c5c41d6bf51862aaa39c513e407ed1d0c9bd3684352b27ec7e6a765aa04d6ab73c064b5bc42cd5d424d28773f7a082876dc2e4e8c72a5a41c407a33113c678226a61cf266e3c42ab3d5162da3a43d622ea3ed63890a47f749dfa9544c5a51a716083b5ff07a95dfbf68183c7df4e9271cd937a94057be15c183d5970592fd00528886c90f681448dc1a0ba1603e2fbbce8ae7727c15c89195b10633458853207d4ad9009679cd760424d88ccddc66a4fde7f0c4b9e94cdc46f3e0d50a0c2857034abcd684bae66f9aa319907b1b8f06f35ad21bab928652571cdb0ddb2b860319ba6fc98117c4fdde4855a280f743753c3dbeb2804938d0f1c80fb1f91cea2c5e9c04c5bade0b38bf3bde3c3bacb017a2a81e75aba1bb843e0030b0cb03f4091e729e781907ccf66ee0de7d09efcda591b0840f75afc0378f14a17a96cc8412280beb5974dfa38ac66d1fd5f62a6d2f3cdeb46d753daf3f1ae84dd1c6e810e70c9975c0b125b96bc06d5b06d3271d38c872d11eb54ae82ce9625aee0cdb55b909"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"04c47497ff4eeef31a57e34497b2204086cba4cd78afd9dbc4fb649087e4e437","proof":"94d08eba3e478962b311d6f3ba0ef0859c48a1c1c8cc8b7fcd3ac492a64ce11192e05f651f1f8e286eadee4ad8b7350337a2af5ca03e435040268fc945c0783cec79371db938e8b50fb7c746c2af8f03e9b79701a1729728bb4be5ab651f4818f0af1c371f4a03db7107931bf25e8d075d09b2fe63cbc5846096a40ff474b9088d87da80eaa0ddc0e22bb20dccb5a4a0c415e7350b2c34542753cf704622010039821a77af2e0a3ee14aa74d89e7110ff8139a2c739cffb2a4d902b3ae169509a58fde506711b9d0a3a44e91ebf8bd7c3d982b85d37a56a0d3db4894c032bc08dcbb96b2c1e99fd3f4436438f140df00b884624d7987074f42075e7a1a40114bea26bf8ee3bfb1ca495e821be993faa95f5f1dc38d80a43031cc8c3cf4318250d6d6d96a572e9603b6dffe698b9f2360a2aaf3b676daf1a4bdd627c84e3f5d15e0368887735e221a5ef1280db84771ebc135711e54f86e2fb3f7fe9118c59029bcd875578227eb6c964d9b942a5f4f481d53d3932dbea9dee268b3b49a4f39371ad62b7dbf914c1b6f0104bc4768abe180c06ca1b2aa71b91ddb56d462113d4ea69694e9adf86ab9facfc1a825a0b9ad5685f811ebe5264a07f8c8c4530fde4274f2b2dbbd1cf2f0cc3e54d9b38cb78d7b74693a4c32a5fada4b5810d74fa47326de57f79509243b746f63ea5011f1282cef09e5db8fe78df82e722bc308513a6c51e4f18a7a5653c5ad789cba1ebf739cf5845a55ed84acda9290056f377a42d6f67d25e003960014e395d664526c04879e514e860934a1b3f405ee57529304bab4a0edaeceaba3e7080aaef468e8c6995e8d3ef6c78dc9aa238dec3557a5390f57ce6acce7e2ce150e22def44bbf75e415ba09240ec03b3a1258e25e00280f98c65dec75d7a88da4e769cfd4b6e6dc96175b2e455e4f9b7f7c5a8476c4b002"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9ce918d3d5f96650f8f85fa249c0e7b95765abb9aae2004b88e9c857eb60ed1d","proof":"b4d6d1b70d7dec97c83acdcb8feb4c514f0cc34bc69640f17dd122dc5c5ebe10be48f1ca92f63f0d9a8abf3fcca9fd5384c481ae4db0c8c79743ee9bd506a738ce4d0beed414d8f07f6097e0361f44c9e0fa102c97cf906d17a247e51ada096be648d230b5cd2a2811bb6c575e6997b1741db5cb4953a8ef516baae353ef741704c0d891493ba2d5193a0260635134dbe57dc6f96f66594a33e90be9f8fa9d0c545c0ed75e0031e8c0052e45443f14f1ebf1f856be234381f609c7a71d6fb601dc0af2f303b51f8ecf24f313a6d48e780ab1cc879473baf26e80f46e4280320a56a452c447d2f971745dc51fec554ac102fbe6edfa6a51a18aa8d372d689cc0abe538622dddb83629347853632019e9e846e9aaf4328799e56ef9cf41c76f7580a818126b55a1a08b7ee71e495d015bc49809966c0fe42252eebd1de03584f38f6f05ff010e50c78e6fc9a48facc5e99930729d1997d2f551797b6da8b7ced48e2e112ac8d7647acd7b2b04505b01637d45913c3d97f90fdd2aa0d88bc7a9609fe4b71e9250eb144d0788f45218789b028e5d331866c8a0a36e72d4f96b9ef310c5020df7fecb65c5f638245f3641dde2c0d762e163f9ea950f71c04be966a4db410da9acf19b747b4228a1c1b2a2a4906fc568efea9e1291ecf4b59fb7f765650b8a8594ad6c76d5883e8d0d254e6929681efa17f0feb1124020efcc083700df61917ee44d8282a78903475981430478d89d83c549e9987e35c7e6e63b02903accb41a2ca640716a7a1d78ce56a3b4d00cfb4631db0b0eee1c16694238eaa6102955be1d454f50e306bcd0b579853adc03ab227822182f14f8df310b6b2742705af3c159a3f1db8ba0a0f878f825027122599aa7a90f0392c44b9012129d207d3e46528e0f0e8da72234d8ab9053d047de428a8073030391a29b758a34af509"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2ab0b88abab3af7f9393c184016057b657fbef20f3adb6a3d549eb920d6ef47f","proof":"6cfce869e9978f38aa695eddf8d2a0f9ba472df14684ad605d9ed0b858d2e849164f170ae9dc8792cf31d6af20bfb10a242c8182a40e08492be61b448ebd8d75cacae153f43ae7257349a948667b3a8ee3793cad0e40b494e10f9d220989656654acf3050cc84371a28b08d685755a48a2f427733daab2b9acdd6a0874843140e9ec9339c3efada451c272dfdd6c2bcb9573fe74c0dff8a4571345ee3a402b0f73d37d3f0cbde1dc0792202e4011278a9731ec24691ae2729712abbbe0e23d0f89d47460b417ae53cdb15fc3b71d3a68dbf60c3af93a29584f4c6a9e7c3f970f981a64f5662d7be5c12698f14cb563a9ee6cb1b117f6f6f90595d84caec00a6e967838f990f39c68e601beb50cda747364d02209d3f166c66e64810f339c0067660cf76dee63a0f8a3913df4edfaca3dd90fc0c416e120b3e39212e4a53a657752ba074c8d5290cf62a2d0efb843c509c1a72ffc34aa5f29959f4d5a9f90382d9245ca55a2f379d693b8ebf65142df50e66c5e8588c8d999a1e21e7d9e7b1d012c1738136afe1b83d36fbc5a69523a4e9d9e0850fd5fb148c3af95557639be6d0627f93f6536cea67420a8b13a5153078cfd7865bf0697aab32a29e902784940662f4d85098be4f9457c8c219fc31278825acd2ddea9d26913c2540525650a3d96d38f51ca283275b6fc2285b16066a3b8c60add88467ae08a1b92b20b4cb70c32f9cdd4be9cbbbce530a11e772d8a53e1d011d39150f82ea8d199d1e733b57958be70f02370afa31af5584be7bb7f38227e1df906be23392ac87ec178877c3c3ac4792185ff70f3f73b27abf9d3c7bd9590753eec6aae4318f6c188a862bf5dc0d9bc106467c98214d4e6492931b5489ad50a2d55bec28f21c1063f0fa125054cfee78aafe1abf0543c4ec3a723049d33011308464cbc84421f044e88cf7605"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"da6245f3725efbd97ea1cc2544c4c0983e0de15b1a58ec81ed8684d6fcbdeb7f","proof":"7efc7a4a8d596d277b8d45603361592452f16b98f711e1d0f32d22c20ebda650800090b339d868bbd5e448caa53afcb01c8e78fa54d900751282c087fcf4ad6c3ca914ecb8b1063f3ffac6fd035a942545ddaf93ea21c87143d00ca89a191c2110a3f10c61755f2305e74f67c7fadd29f748aecb8b56c41840b0e493423f8c5b12bf8550c8b363ec7265fd6cdad248c42f14a4b87baf7cde3ea7e5f7f51dfd0f5a54dfdc6803ed390aa7503795e5428153fcc8af8d196071a86a10f48eda800c0f3da8b7f48ad4766bbb3118649eb96e5e14fbf4cc63455e749b85b30a9cdf04dc5314be87422234e2c174886c7bb8ba7136b50d0d3c9890e94884d7050ee21a82834bf0759c77e90b1da95bcefdce20db4aad1ff9d6802bd888da8478f8890c28820483ceb6337e4d57afef8c3ad46315890f5e5fa5e71935ffa7ba8302152ee69d9bbf0dc1cf67fb8d8b13359957fd5ee871588c139392be57dd93ae317665b425992d38d3728298de017287c48c176b7ad5921bf565386cf51ae6e5618a47849160b051b5766ccd13ffe2bd3e837ee937ab77d8ec8bb29d1e9c200bd47e60e4e82d132865040bd48f6d8a9e0644d943e6300c67685ead7450637f0c67dc1936152b399eaadf5412171dd3ec10c88256e2c370ad981ae944f3879f366c49711a276d1508e61493812e11768e3ceb4bfe9ea3868f691de67a54db29b7f5ec573482b97e58221d588f5bb2d2685fc6d52367674a84cfe7ff37873f5211abca44aaa2d163a819d8b8dce3bff54a55184cc9c1acf7e46d13e424a849c6d4c42412a2bb283283d78652c7fc74cd8d6f571c10b466f76cb90d9b5b828037f58d507130b82ea8a01fee7c2a27f783e75dd953cdd3f18a00a6db07cac679d61ef76901153b0c882d21402cfbd2763c42afc56a4fcb4683ca6690fabe2598a3c2045908"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"56e003d2b78d0421b4c31caa21055d934bec7dfd26bd5c9905c223fb6c860508","proof":"ba79a46f05bc6501010e79eb5859eb43cd74db6d282ee73f0f9aedd3603c9702361a4962522d3044ae00df524908d98140720289bc32f4e0b2cb0754e0e482366a24466a9d311aaaa14e260f296760e1ad002abc4cbb5908556c8671ed360961b08bcd07d624fc990e1655f077dcd15dbf138364ea077724362b3baf2edfd3638f76db7f24bcbfeb7a3a85c65f27797f8b1bdb3e0c5ffd7d0f6298fecac82a06688edac3d51fef0129dde3fbd23313303a59082b1d465dbca8827c60e6148600adcd2fc9e794546f966889ae088564a1100d2c21c9a12d786e4e2363109b790f42bc06e231e3b4029c63f92a35aabe6a6f4b2ba3665a1b7262324ffa6fec84138a020c926f87e90003982a9a551c743d49dcb381abc1391c211173fac6a51b4de6c5f2f886420df2f4919874384f4946841f1ee814ba26d76472faa52619495ac869c84283bb0463b4f34f1e7c093832887135e6522fecb52987fc11cece2804f081a07577d2aee7a7c2a99d633e514b4bde3fd5d38b067d07d830d75629074e843c0dade3cffeac65942f61de68d54e0eaa3a5b63daf4f116f9a5f7b3d48a009c346b8448787896fdba30a955348dfa7663be2d5587d456b8b8b5762761800dae9b70699cea56ac662f4761ae45efb9282e0e40dfa5137540c7dd2f6bd932009c8165ad0b2752bcef4c4bb193a3cf5e6001598f687d80fedd0f79d6f545de0d2a169849ea72e0f05c11d7bc96abae339def0dd223f0a41704540dbd54250c106eade2e9b714e8b9b05daf50b7e6d5305bedc70861460293c531aa44fad457097ea8aa02b18732cdc9569ab7318e89f0e913145565be38dec374012fe266025cf83a73dfb463ce910234bc1ecf163c973ae874fce051ec4e8ae89b4de5d61c06d51026eb41c781253e25df61704a1ea542de1e0f3270a80018d8c5266cb7e508"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"427e7a1ccfd379264a821b1dbadee382cd2f055db8eec1eb81cd76425c3ba168","proof":"da03856688b70c0ef99f3f3a5a11c5f61ba77682af1919c9118957e50a23ef556e328f29c32db07420b6d110dc352e8151dbf47bb84b48f2c936c41edd6a2709d8d1d5860927040abee2c51d2b2d62ea8abbb105c4aa551ceb62169920917477ec08ec2a41ac3736a696d2527cb02db4b40086af0d5742e9bd6539dd29aec03a36e502c07ce197c2f2f81c5ac564c0ce8f5db6215d66d3d96f51f453839f3a0f830ccfc0d307c6be414f287c2aadadc6c67abc177963839e772bb327f6626b01d0f8d997c4e1514f301b8970b9669bac0940d4805346b7255739c71f7a882f06be2e0c4aa33741873036e524bc550583785b68503d630e30133724bec1a6273338ca6cb91d238835bb8ca6827cc593d940ef38877e3bdb853ff045cc43583e60640110c87883fdec7955204bb719f4d93986be642c96b1812c9aace19e974a14e0b488a2e49101f52906380e6219369cf6902a3e4a0969585aeb2a577adb5a2be08ac7dfbaff44f570da37b3b42d8b23997f3370773f48ab82154d248a23ad16369df22d8f60783441e2a884d94a90eb6f6e4d46cec25d65051f4941617237135cacad07d88748553dbac74a59f6a2947e2ae27bd0a09038d077a5b7df5f8e261ac3a0ed8a06578aef4cefa23af4dbd7d4254b873c11be9992bf68b3ff996f6e5cbad05177948380005982eb9790aac7e01aed54728acec776724a5c2450f7545606ffe3755da17e2bc7e0b16f36b80d1f56e7aa926a13fab5b43ce269e4fe506e045ad1af895463a7dee4ab387c33f857e4d338d569f719ecd6b401806bdb5be2f025def026410f5b283f89b44f34d4b806d54a3b24789257e25b5a7869a923c9fa110c6b677d392ce12e5cb2ec3d12738e1f785f644dd3646e397b3ccbc60dbcb77797edbdb22f25801bdb769eb9516702fefde1f8200d32315daf42709306"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"08dcc5e6ce40c41c9fe7c440c7c976d25187a3a4657eee71e378d70a1172a374","proof":"3e067de3db687c703eca900097e25232ff214e4dd2b0b1c67da8151754ab283b7aff743c70e758ea041e74056cf8bbd000477f374fa426b857a459dae3317277a2b55c191af796bec15c86512c34bedec10f283f0415316d81ae07bf11c75d412e737d541d0240ee9d346caee9ff8a4567bf8e13142460decf6273ff8963b14bd62fe227fc22ebf84c4c534290cffe2ce205e65ed13ffd7021d67e39a5bf9909a41c81815e8fd0f02f8d9bb652cf24c704ba0a6fdc26109e82440ca7ec153801bfa7c50775a46cb8f881ed76fff7fc5ce97725b45f7d1e92ab5ee4e605db2f0bda6630768109598426193eb5cfaea2610723875c8f1fb7f94a2595805c41936666181e55c01af67f3c13c6a80267eaa6e953a82d79037816d419dbba9bf2fb2cc429d3cd4aaa8643db7a93212a07952a6c1a6322f21f7f568cecc9f07082fe60d29931809a43769c52b5b5d65b9fd9cdedc63c38f4f8bc424891d41a675811479ea3becf0346aad43580c90a128f6edc0619088ad7879b609ab9b6a1a5371a67106bfc96150ea853ad893eba2e08d1d4ef3a5635421236be795c87a944ef9469aa7197d90b9f76213bdd97720f7af86255c2bc10ae289b918b73e08ccc908c54ec758d370fa065d3976e8ce8939eac3526c299a33aaa57003b0442453cbaa926982e16ad75890a6582211c72b7d4843613aaf3fa8bc7de04b22cd80db6af302fa60bab52251aa2abcdbbed6e52386d8bab90e7787af93a5989c15f39a3c50841e4a18ea82a060499717948ec81220bdc21a06a32c663ba37a1cd7e183b43d73db4a6fece74167de5a5fd52ad2606c36e4f92eecbcb11053061c90a28b92f0d0bf39c07642a4c0ae807cf615c47b1c831e3d970876964f53f4a1c20d92d2eac04877bf3d79b5f9084cf16c3a1ce59d0b214d766e11976639ddc2709a121b12003"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"40d107961a9a47a4cea2d432f953d4f3826f57894efa01474ab1d56050ec4348","proof":"448f70d8d8915dcc47eb0eca79375cd6c9b500e32e1c32865729b995f0000b7b92760d37aa3901ee6ffb4a5fdb90305d0515dc560dd1973ba3f25cf2fd601d169817911f2507553d785c2961d7e1f76c33b647f5f3de4e99ebd35c1656664745eeb554ad34d12e5fee8fab7cf5ed9f67b9f6b56172bb70c6072d98a15f01e2532a764d5b48b7c1e839a8107f723761ef93cd6671806621fdebecb58a867bf2007b84f4b1a61ebfdfcd6b37e94c0dbb742f12709347e919b87cb7f19671150509e083b56b5480b3a331f85bf3b30bb9db8e12707f80861f14f1961fb759a5a80afe493fd9390aa80b1c4b34c8a999dd576828f89a213fe9b9296f2ffc90ab843d5214d9ad04d60f7b7c583eda751eb0cb4c91ef9ff8684069316015095f15073f187fa0b447ab294600192f9997db8a95a511c9d0a3ba380eba6d1c1912043823b8b0ac4217d1688e9bff1079cbd2a1d50a5f6ad9f9c49fe5ee15289ebc95d52f4431e2725596f8f391dc37a9b4ca7947b700e9c29c6fdd3b58711255e970355d7492199ab9b2ddf35dac9df25da876323b28de42e7444cb7d82d9a145a570144b41038cea8998e52a32aa8e54aa45c6f51c4c30feac45ebc0608a09134398b7dbca7c7b434a2de390ba335a2d22cd7c0568b1a813ab2df1c9e75388e3540730dba11e0d590821398e60b37a725fbc0e253291d510b177be6b4a440463db093111ed6c00666db7ad657c52df5425bf36a8f3b20bcd4c245d45985d933dc52fc2e9c8d2cb1c2c98f40a907f2f18832fd6ecd1f26ece469a460911d7fbd264065521af32862a1ebcc7434a3d0f7430cf57b6b423dc1a3ddfaf5c2e7806e89e04a673673d909d715e35c22ef281879aeb1f55dc616ac36fe64bf81e86b9f2a76f302244e18fbd2f0158f6d2a1c400be268be4555e0fca854eb414160257833e2a30f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1e63b8b580b9be6be528c94e50c1ede5ed26e2f5f2d909861022e5e3b87f283d","proof":"187da7398dd1498e9707d1eae4f674ab2785c7f1c8ed3a51cac0f5d694a83847e82b675cc3f601b245ae08eae3e8b2d6da9eb3a185a1ae3439cbce106e69070ebe7856105f9353a7981100d16f7296baffdbed4786da251af1663473d0a8f90d9cd9ca3a8c6110ad839d47335e07bbe7fbda26a7cce229629aaac2132102507beb253437c34d27dc230c583af326aa551b3d33f08686e64999794635827e230b6c021a6c8345bb4130269d24b5c0638ec6644c8c603bd616c90036f05d0f86073871fece5f99fe85e24ce838961ec34b3f9ad1f99406e7fa960be58351299f0a2ee3d20d8cfc2882054ab1c19e27f67b1b7742e0510ce8a9658a973a2b74d726be5e428aabd032a42511c55fb03bff1ce82d0971c32ad356375c0769bb1e3d7bc2606f36224fa2af11b73a130001b450d0a52cdb8b7a5570fbcfe3a9a57de60ca20699bab4e2bc92acbb6276aa82906c048852d09814da65290135714b4e702fe69716fa76cf642ff7abc027fd493b1325947d6e298e44893df30c3d136fc72a64dabb5f6f6cb55d60cdcb7b2e6415e9ade6405384a48a90eeea6d049311b07030ba1490a6bebd40b7cba31462b56379001e073f7ca686e45a187545ad2b9d0a988205ae5a9d60c96f9ea18804f83366b9c0ccf47385e47fc4425ed464962961249bce32677af8e1079ba8d95d17518b7c6ed0f5edc2c0adb61304c317943f67e2212772ab8f6b160e2272bca836ef10b54dcd75fc789638c4cfcdda001e4d5e427007c36eb6e8a67e684f7d9d932c2fe09d6115c9f172cacd3eb7fa2089321e262b0918735cd422beeab45fa4b4eb5dd06be0365daa4ee96333bf3706fbeb1d893c7d564f4b88b95698d3e0e8705158906e3018ff2baaed0397cad8d392490dd063c22e752004448b52adabc57585231229f4305b05d03e11d7d3286f57de0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7877a4c890b88afbc678a1ff4c7cc8574b43c68fd8bca2b31fe3519c528f9e7b","proof":"0a4a405055e4124799704663cfed503e26a11ec7d9676fa1a9693d26d775c15fd4ab4838770c816a00cfcc283fb2f67595e925dc17b08b81fecf75a3da23be58de66bb03fc62cb6e20db9b0e41117d53bc6a64ecb34129f9096479dd7a533f1ae660fedb84ad35ff21dc76d870f29aeee9a287679ef94ba65fe4cecd5b958c19c332221453d223bef4585da8b4c25d129703fef676b14798ad50bbf578bfa30d6c43d0848abc3bc5926fb5dfe42225a148973706b9798d13fae5e8bc1c14cd0d2457f50f04eff2d7bd23c652a73accd4d6322da6c19278d35eedd5a212ff5a02f223668056170abf8fefed3b406d75ac727736d28fd092751cb015027167d62af2d65e4898cc9f08df331f7e22ef3ff6969ed39cff59e258fb297f2ab35cb14cfa9e1923e71036d6af3e6508d50f0fa6e2e30d85229361940e8b9da6ccc10b069863eb68a196d047aa37e7a43e41c03d6d38c174c254ca3a4ddeea3b29ac594f0aba378c796d83cfe1737ad74f1d991bc65ed793974b2a85971889f4e8a78e7d36ad2034be6672471c0b8d5f1f62af787aa898a34c97f646675e4e7d76986d4c66bc8a3dfbcd7ec1870da6267e5e4f671391ebe734cc66cbc5d4a43ee8a2805c24d2bcfae5ff97b75ebd6809b4ed7a60c0d7c6f76bc44d39185e9b399239e15b18e457da18e0614aa9ce5d1f71e3fcd466dbed0fbd4eb383bc98a24dc19efb6954b4692cd310292c82d8bbde0d1082fe9e63b27225015958cd1d2bcc53d38303022611b51209b9a633e2cbda5a71c98f6f4ab62f2c828447360dd3f99ec790188e223b6b028b9e8064e52528a37e6305b51bcb1b1975585cd61615c32aa0ad62745151e311e73ba7e7045d0be747f4e5fdbfa7383e3f129df5a70ab2af69aa05b729733daea9699d6f26c787651225ec096be15cc6ae0ff23314aea81571560d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"406a99d9bbf466d4e57093da8114d7833a3a852698eb3d4801cc344d52700c38","proof":"0c3e2aab087b2bd4623a0634264c59cd1636da127e70d259f8300ee6e02fd6174a8ff31a9cf35e2754d23a9b4075e55b8dd47ba62afe4504eca5dcd2ee52c34300b6fdd4fffc3bbc3b50d36e6dc5c889067ba1d5f79574ba927456fac5a12e2c8a85e5c10a2f08078ca8569081c9fb4468cc4690676f3c0edcd0a9213513a222ce085e294ba8bc2cf0b15f3dfe81c06b391b3535efa67d70b5f1384d5b6a5f089d51ed94094a53785ccba26c49c8d1ed7dac1d068f5ff8528a06022c1e87820d7f9f8d6106ee16cc05c8e3e490c3356fd1581d1399cf1f7b5bade1e3605bdf08d20dd8156c2b7cd03e8d29c754590b85fece51d83ab1c36e9a43662b866bd52a6a3743213d6a28254a8ed0af23f7545f253bfbe60a28557e69e54458bea96140766744f4a28c104ac8acd61173c733121984b8c832ab8e4ae32a53b3bc17ab2bfe2897768aca46da599ce9168475d4ab1faa0bef1ff0943b5500be0b82fe2602163cdb6d3dbcd6bf27d5c4de809aa2e381b2c3f51ffa1175e1dd894e25a40c22fe89cec20a7a8ad3e9be81820e088ea9ecd431332249ad43d7d4e9c318b7423dcee62e382427162d40e07c03271520ed25fa3f0d61cc8e0e00d909d790d8ed0cf6ea3cb39d1fec279e28ac3032063073528dfa8a00d55e84b1166e426d4a5a123cce97860521aaf0bc6badda246611d0f2db7ee8e93ef84fa0c384b042ee1142aeda57fd7bf2f55d9ce91705776db703525dde2bf1ee543f1f341d9d6aa8866434456694a68241085ebcc112c16cce176df519af28f3d698e295ade5f9143063625cae79b732eb6aec16d43dd8340ab63732ef39ed4847782c17e20db0824d0bab25be9c23e209b3bf8ee26ad5bc5590f34bf15718f5515ac336f2600cc37302f66857dd58e59955ffd97f5a1547935cd0a5d9ae3d9a4a38a0019e9d5ed6e106"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9000837ca2f4da9f30a4f982b11f0e2e4fbd15c4a13d0ac905ec706eeca7b539","proof":"38c36b2c21147d43693209204c60fff45fa5c900a5d95ff5ddd3b4c1eda0a4371ef74c996aff742b5955f7729399e395d07fb16662054f0d837aedd6d8f5637e66ba894d092b69f1111e86a7aaa70651311cd1bd498d569c11a33e028fc12b0ab051128da2aacc86a70c24393d7bf9820b648a9ca01f496a6280b6281e0b924b4b8203134b71c60f67d1e64cb96b2dddef563d5fa7879191c476e8b9c35bf70a580153ac152c48a2d0f4ba8fb05744c2b7473f969a190a174ead81c580098b06d8cd40422603452cc2aff1e68bcdf0dbd45ec5355970f9017158c16b3ae2b60ede34646e0a13e8cbe60540766b32ed45fc5c919ad1e92c4891f46e20f186cd76a212e1982f168954cf86f4412870b27172870bb5a7de89d9dd2c05e469aa7366b21da672ef06cba1e9392e4767c76abd0d178cbf31c976c642175b95c67ca628144ab8ba86d7abc5b4ba2c2fd1cfdbf64328e6541f465903b2667baf3a6c972a10b90d9212e5a8d969e3a74c93e9bacc3db557f6a467bc4d5e348299f35cf53f6cf763d0ecb7101e859525a864e69faa8e1195f2349aefba9eb2c96f620b121ace2c690ebc3e684af36ef7f5e0c9d5c6edf9cacb7b7f95a09029bab2207ab726d8835ad30c0360e2d69ecf18f799353b02d89da936a9579ebc6a0d54ae68f568e49bfa95e8345ca4d730118589b6590cd1a857f904b0b0f5a57c6da33686b4227eda3acefb1f276ca9a9c16f4c3d2debe373561fd5a841418666c08f213b3e6fc691d1eff25c76faaef95927e6110083e0f17423738471d32d11911514df47694c367b97f852dd4eb8f1381bec3ddf356d88744b6294c983e92b25570ea5f8438a44e61913d53fd21c18d7a8c2daf00abcff4def75baf16840d92b9b13a63508845eb477f15869076099e627e2c79c34aadf811cb1eff07de4f347f8f1b0d606"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f28f9a708ac2cb3d11c32990223b747b3f505cde6fa3e401df29c28de0cade70","proof":"d2e79cbe349f86f8f26e6036a57113f612ba8dd7b69561f164eb12496b61361576967005a0b437f8ecdecc55fc2429454a232266cb41a09a25417315846a47584c708f66b4873f30bc887d1b2eca95ecbc14da29ab9b76ec612448bb13e7497bca21c592bf49ad5def089b15dd91da9ddf8c2f3e121734a040c80b172fc6ef751fbb244f58c5c7049bd89ffe2f7298e6838ab571c0a15133ccca1be442917a08c45889c6f04e965bceed416ac0b28c5f88df53003496fec9481129bdb892bd0a34e1fd919225b5821c067af77cd12987e7b2170a6a3501fb0e732f5658613205a0d50e973105e307360c1ddf2cbb21b59a4fb6451ec014ed15f0bfc6aa6d6e353ecdd286bf4251911e2a4dd35e0f6848eb2cb95c3c8919ef5575907f30ccfd0df2560f2302dd51d72757f79f462120b12b6bf62a237e211b7bced15e23c4cd0df65c0ae05f08c7b44ef27ec61c380a7ca0fc215ee12942c38cf600376017f6666437cf3507616fe4261d7183049b4cd9336eb5eb1b9729a4f677a50728f3236a90dc754cca61a6db9f5cc64e5e8d175059ca274d3c80e24900b8fd41aadc273f804ca86eacb26a48cc29734f0b08ae40b1cb1931dead62bece06e5342d242674fa451d04a2c5d14e3e0e381197c306bd825a1968befa7cab8454b8aa0b658d27faa25304ebec6dbd86de1ccff40115ce86340ecce6914adde4450649ce725108a4f2155d3d4c9c881deca130742b020b724ce66cd844ce11c651269a6cb6963bb27d15d918bfdcf48221a60f1982d5886e76f5e29d9520d27ea10d9182c95060886afb47fbd2eaaaa7c797fd7389ca94173047f6df8eaa1b814964237029f21f892f7e59a22c240a3824b64c1ec1ff54b42678152afff0d0ed60c3cc60226a06044583a36d0305d51bf011c5ddf1677e69fdce68986cecaf23aee8ffcb21f702"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8430519d92b0c3540db7f5b8416da45e5cb59382c7a469939c9eca1f2a594a7f","proof":"e81d189dce69c30e9bcd9cd87074cf8e032c3b9071e84a6017a9e05c9801af052ed7a34d5a6811538a3a801622a98517bd9751d1602e8797e2e377eb9dccf20b0af116ef75a8e5e2bbc77422baad3b29ae94055e5850091199571d6d357a895ed27b09b17f5cf7d86655374af22274b70df0023d3a190fb5d005f9762a44225dad1b2575e17b52d222abf2a8d18947613ef5676b6b1e7306b6b19cce52edc00260af97ac078409440fb0c6e2a3cc0a62dbb7bc90a7a611e3aa0eab4ffca2d403032eb7374cef43b8f965af9acf798f6fb9ff2276f0169ea953564ae9a83fba00e2dc1baf7901cec658c9eb5e0b2d5c78b92a5acfabd6e450004858c8237a0d44ee5a8da53a29fbac3f610cecd53bb848528c05dbbb29596b0e7bc7a8a285455d94872476a08f33caea2a3ffa211b8cf24d3761203ef080e6c7f5bdde8ee43b5f64efc85b7da3e6e4644f1ead1cff1bfe5eb3ef1799a5e309e9ec1d7c4655aa0de2682fa0b453f8bda3cbafbd445b29530e1130d72404e9a327c344929c7d4b7256d20c4b791f6f9bd6a1dc8ba797d7bce208ad94f0bc777f59d98f63d58b4026fe37fa81596d4f5a7d2c35e8fb85e6a8bcfe2e561bdf195a959d321ca7529c472af2175ffd3041155909dff6a67b0fd54c0c9d0d680be7940f45b6a41894d01c0a48d782304ee63b5e52ae813a8816eba51345c694c4c7aa699d315791ba273d808f201f5d4c0d39921d5d61cf7b79e8f3e9c1e81518fc53c67a851c5ab20216ec25846364ef14f7243835093fcae8a3b85f7003f7bfebe7416dce3b2e809c3724833b707d2987766ca4335d86811cebb34d9a464ce6d2de35df25ff19c44576b1d5b06a5ed632ed661266ebd803484e1b3fb61b7e56cf510512004be8740f0c3b049b5a2749f5c05583a173159929dcff268109adb30700367b6af4be20f60e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"74fce51c385d80cdb33b46ad5e0b544fe4f86115d1ddef43e1b5deb3a7e88743","proof":"1ae8f536118f99927db73658731227c41935a9d62609d38b6c9ccd60fe2cb5163ab6c197737edd438fb9efcd6408dfbff9a220fcab41fe424a2ff83d883585692eea8ca76adc5ad8aa0b5b4b4ed092c2a324c9c405fbdf14ced11f2ff36ed4546e3a46d79baba3abcf7345467094fa92ecc9431e81caca7fb616912a17fde64588850a1dee1378785274e357b0b4258cf3b61ba2e9045e3bdf2037c3f8597002254a5bf1a9f30e6fb9828b8e0b7434f86451ab33dbe974492a6915ae9a27650ab46c351c39e9cea9f51cd8617b05b474e839c2c41bd343a0bbc0741409666c0e4626d2695364e8f11b5d5592c048ebc5d70c2059144e65d52676a11bebea521b7a2da5ec6401f55c1e115a6898dfb7aa2cf72f3c9f35ada41dc29a27ae60e45d6ed97c1179a8894005390f5c99af1127f70d8c847cd8a90e62f1d32374b3c30f228251e82f48cadf49bb7aa61a52781d8128ee40a4a07c0ceffab33d6320034c26854159c6036a31d5090d823d3ab0239e7ffcff4d7ffc4fcf07d0bfe9f59b055803c596e871b50e0cfbda39da54ea4e710b44df69d9228121ecb6c6f04a9b3a002efa94f03530ba1c191972f8b66e64af6461405d02478bdae828f145a0721f9231df4aa09da04efdefa60898815ffae1cc8ee40670d0a7eb4336d6d746320c7ab7c2e06259a4dd6b7fd0b79371b86b8620fdfe6fe6aff3022195387470cb28a68ade544ed465065330ccb3b96e91e378a604ea2997027a2f00fd925c5b1109fe201aa613b11ff6670bc37886a7c1e7b8e8659ccf8a27db9d881e3cd7dcb5106a98e6d40300e41070e976a60b85caa70364579e8f929d707e20be8261dcb17ec7fbea23ae1e324eb2cbadb5e2fc8b982b8450e8a66b0d10af08f1bf98e96f052655f29794c8dc572fa754da5918cc0f493694b49edb2fd64586c0b17175cd00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"068f9aa7585b47472ebc418703e75124d73b1dcedabf5d9cd9fbd2cc79933953","proof":"7e4ad08811de0af4cd4ae7a443ac73ea98be24e9d96b15a75a80438519d1307de2fb5ed866fb6b918db97f044c05982ed38021366f927c7e4749c5c93a8b752c126aebd3f7256e9be60d6ad916a29b01df875a2835b1fed450b6b63360eb072c4c1041d0fe537f12bcdbeeb2214e9642ab81b7eed1e515a5e743dbab49586d555e2c924f1fae018e3c1ea50370e995958f6a0640f72435b3180237deb76f8005846239d7df55acff7fcc1ec4d717bca9309d6324da916aab3155d10bba05dd0518ed13c97b3e9685c3907205af53eac613d1c0994b5e1abdd088a000e78b8300a279d11567277372fd2a74ed31e5b9b5a2d6aba6ae0bae4bc4ff60ec3e93120d744a24d07fe3dada14cbeb0562f1ef85daf9b061479b8869e96ae3b0c2ced71f7a242eddbbe9c7d0f62d716b568cffdfb9f11db0a946100684aedad058b9d879b41ae951f0218190a5a35c98d9f7e2bcda4d71efb4163205efc5db465120a248009a968339189e8da5af3efd6caa28bf816ac0f5a0b807daa5c08dda3c1867593c40483e54b3e8808441a637c7189158bb15eac96b08db5109910eab78c513685c8443648bea1d334f8dece6037659dd596ba55bc0b7f767f7a8f897fddfe948fe0a61344a2a26b008cedf97e05e5925314230355f90d1beba2bd141026b641bd68dcd2e7f966b78ca9ffa826c66668df2056bd513150584735d46a1adff3b72761484ea2423dad2ea475a6e3240b812e43972c7e68d646ca7847d9e6a4d5b4fec65f155b626551b79e3ed1a6481c8a585a207d6558a886da08f1aa78298f845bc0c2f221d8f5dbe1d42eb87c4fb5a223957cdd9490059fc053312fd52c05f51afd5db5a6225ed1290f73a87f57213caa8a37eb2b8717ecddf8ed01f4f68d20dde276ee78e4d779fad89c67c43195fc11116d8c0786943fb3d9372bd68fbc10e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7c1d2380ae23e6a6d9ea8af25ee656ef605cfa70f08bac057ff4e20e7af66775","proof":"deb8b6678a192f0376f9d0a8c106a897969f948d1abc8a384088a1ac5305981a5a0c132fbc6fa7d1fe4221b9c13e7db390c7e76aa5d8e62969a3e22c851c934dfc0d7051451e4ad80d9cc5ba9eaff36bb910ec53ff68449b9a6ad3d39b76571d422aedfdabc01f50f436ea4620f1a2fe06b07ed0a65f4c2656c9a4d76e7f3a11570b18b7f7e42157dd50dfcbfa019a52b0528bb760d768773ed68d890893c40b11ca9aea7c135eb037cdd08a5fa92cd8b02bbf64c6396c47507c0583e8fbc50060f5f879fc44f51b8237c552dc70535aa4a26f433095e2341f22c949a36c7f028ca1fa5ce615e3da7a45bd097dfce854dd2e4cdac5cd3efcc09948a314e748394e7064d89ad53143da4f4557551756ddb461f8aee2522ecc020b4486bdbb2a5c5672efb8b4927b9287de9da95aaac20df954206630714ec0487ca49382bbe41c46ab414c1c25fd2a59353c5fdce44ab0f1df4b8baa0a3b497474c25374605e52e47a19c1ecc55f046ed37da3aab162ab1a8b5164af4df61e338c48d525413e7bdcc517d8eea788fbddfe9088d0ec417f6d5e3428d41425edc2ab37d877879b2f1ae184d1277b56deb98e56f08181e0d81d145f36dd4311b84ef5afb6d35a93732e62c3c69abe07315e7d2df4b8c73926a52a6b5e8b398a7b7973662df252d443ca62d5ec1ed2bacaaf8547a665be658d0c999e4e80501778a888360c6c44ac02de25c85b4f29171d842ee5b0e4468a910844109e7266a5b9fe3e82d0c8754420f62ca2a25296caeeb8b7da3ff387fd4742c41f0f1d4e96f7f5eadee3ac18af5a401145062c6bbc2ce262676911683b908fee09f49a5f2c1c41568948a2201641472d8b562c1e1d1644dec099c1c9afcb02cb08c9a80475646afb0ef28a42570ffae6c96ba2e49f240af216d8fcbfaf4b42b63b1a3d54144cbd3b050dac50eb0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5cca29b37aa658c081db865380d3d52b8d474dba5c2f33565af918a8b3e17a63","proof":"3adccbb0b86da936fb389efe88732f1993ac9eb12b5edbfe244321f3af45172ae42978e16bd8ca0b45603de464b090ad5690b6a6a3ee6e2f8b883f12dea7a92c70b55b517bf63ac102bd9128f8ec860902df88712f3c637b6960db68cb6fd04418e2a43a0eda0fd00f0ca2d1890144a3ddf1245275196309bd2858d56240271c60508412e7483ded7a920d0a7ed311921bef42d066b847deba35b98fb3e4560fd438ccafe92278e7fedfd85363d1d8079510049252ca88b0c9e8e834b8ddd10f69ef8c58ecffc44bebbb3fd71d49cfd69ae10e48a060cdd7bd18f859de463a042cab7bd0635b57b84c810dbf87777ae87987f4d40a3169f16a9c78ece8fd705ac66f31b1d42106141c32b47e52750c543dc3b441f8e2b2b7e31a749fdba74d32120993645b0b461632df74625b578940fe39101fe8fae64b560e946f6e345650c270e4d69a546c7c21dea222c1d3469bed61887067670b89c6da6887e4fe1634e40514861e4f8c9511afa9409bbb2bd9db044c21aa6302940aa854eea02563077c4cd2e1c0dd6177c8470ca93b9eda67abf0255bcaa2ce1ee0a301d7b9f1ee5800d2c76d01f43deecd0688a0db1b29f65524dca9e4f5c7035605d30e2e2a8f620a8b5c4897bc978f5276b9e661f632da5e218ce7b69976274a0aae9816f5b4280263c7f924bc47c48e37d76bbdb81bb492acc52386a72c2e3afe525f2b4a1736e04be4e685af83dcdd74b849069476ead00553e5ed8f07d7f6d36437f42a0166b6b6625a45477a1ce64f45fb9a458fcfb2097cebbf9b998c47989d6c85ff491e2e3b58499dea06898e4d634a3415d97b6bf708b15a16352ce05ec5bcee471f594d15aa323da19c95cba5454c3c47283c0ab57c5aed1f11d46afeff264a20590ebe51e38816169598abb79303ead9b7872d5aa5504bcf2f551463550e699d8d03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d4babc8569a0d5d0c933fb1ed5ac590244ad4403b8ef1ad3d948375dc18f5931","proof":"964fc95eb6c350ba2d9cbf06f679af670b954191cded53402f30f0b3f4d15044d2f2b1b4296918b126ad2aefa29cbbc9f070c071aaa6f12f3c8108d08a739e31bc2149926b0f8a9b9149f19688f9f753af4b485b7498460bbe7f480cae27033c0a8cfa4abe79fbc6466a2fa1c1ba1a33bd7e865fdc36086757a161e661110a372280cb91a202661f8bbe74ff9b95a0d422337605e0444c290a7b3c7af8f902030662e3df346b38f5e0ffcdf4e93a4c1522a52266276418f46b7668ecc9a3270da94ccfeed67ae424a86ca1ccac820263b0f39d51d14f4abc79422a3958db440e2e70e26ab9c485f53614c380a5b85fcd0f0703da2cdb75e28e45b061e123f90acc3e03ae876725e474969b33da5b363f9370f8ea5add15aeb7a93f0f2504db5c74b0f4e889492d8e96cd42cf22ed453d267f0b1a51f8f189df1d7455c4110b7c68a8e77cb493df07369889d3edfa1a88325fe8c627efef7bda6554a306885775d80fb614381e33ee9497ac2c5012f5c828b0c8e6466af1a543fec2faec385d08e4842e5d6fcff9f07f02c0830fb7a4a8c8ac0dad8e0f4c9f81b8702544aec639bcce6f7bc8bf200a9d168c33d2909eee8c4561c4cc11eda4d85f0bc6075d023a100b43a468d01f4d8564616d02492029248864aee8c99d222417fbb4ff78f661829d9d23188b3f6a464ac3cf5065bbebddf644911a9ba1d59699b6cf0815bd734e3d29bec7bf73b8ae4dff230596005c56c81cfa7d64137fb24c670de48b0307d66603eb1aeddf8bd51d8d9a24bd87b0d2487226c6c5faa4d60b8ffa72d5cf5a626b3437b40a828a4c848ff9dd4fbe1db1574c67fe9bdcf9f18219339855972320d935656c795101a1b89ab4786546cb2f6370cf84ffee282912da66f0ea49095ead028437cf277bcb662fea3bbc3690e9f824a61ce23c8dead0c62be6f3700e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b876528c0e4f772a6cfe4c67c43a9cb387be9f51358fc22d08db64319ebdc35e","proof":"5e1528f2790f7c196bf5d322be324321e9889219a2ed9c5ae3e5b622b275ed19066d2733c5b2b9733fe2cfcff881cfd4dd0b8737d4769d7fa4ead6d7e7e6e11ea0f1923f6e67b8cb4b5ff070e33bd44e11c6fc7ea66880cc87985e98c653592298df03f1898ffdc92526434107890b569b070d6cb425f3b9e556bb1183bcc607b21b1d89297f1d7ceef9005be31af634953634ddbc660d4091b182f52179e3040c88d764152574b094206f4e022f52d8ce7554914b8a2458876cc48b62d7fb0137349623ef5ac4cad15537f45b7bb21530887509653cce565a773f283ab85e0032f1a3af4bedfc13675bc103c5faf2d344b46390018231f4972ba4627812744e8e25c686db563567bf8855deb18623aa4c8e30689c868cfb8ee19f5c5c6a7a0fa6cba478b659e9276b0cfb761755797a6e788ad3ee22b46d79a29e6fa4d305463e84b6e8f5680b7952ad48d47d1827477a825d65b001558cb3fcbe9a70a6c76eb84ecc84baff07049c794eb0fe9b0e1ee472dfd25828b9f8c5611d44cd591f107a25ce506991f9d5a0630bbead9764084f35b6c6b7de0efe12cf99343fc0bf2ebce106a908b8fbf23065fac8bc0216235bdd3283de18027573a370ce6715385874d4c2cf2a818707d6ede6ceb3c3bb3bf4a2b929f6ccf86b4e9e95c2cfe0c82d08e2fb1b7880f8287994e871d6f7c3663948b69d191f4e4aef60f046f440c56392db03c91801c2100d1be7e14ac134f746482c0750f6831d3202e7656833156288c7619eae5a18c75e8421f3187da1bc745d3f05c94975353f2f01badabd1054566f738de829cee4f63ca8157eb92b2bc381ff7c37504d82f42fce398bc8c5498756ea6ca5609a346653a543c335966e0c7c14fe86e77511feaf51b286f88708a739dc464c0976b1b1d968fabbb5a8fb0332518ddbb68b6d56769b6dcef04107"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9e0ea84e294ab2f1d9bea1583b89c5ffa2d3fa2ec43784624729f98bb2c9a50b","proof":"3c01b72fb892467b97d45e5e0137efb9eef6ec6886af8937ad0751ab751e387bc8f545a9a8c240879f54dce3fed7514e891f8c6db033eeb976244f82da9623545c344d85c8971bad8b8b11319105c34b118f37b73ce4f9f159f46742d0dc7800ee2f3b9b9e6e17059da49669fb24f64750a8a8904d39ed11b7ac5ce68bf05b4ef9d648eae01eded03c744ca4501903b21a3453d60a422d847cf77de5b25a65079f76986310f5998e1a852a536573778869c83820503e18f6ec26ebb10d97b006a5ae9511b2cef477ac0f26e24fb023ce4eb215958a9cf4a54a94d76d38770400504f48f4a11f0f099e9c4f5db5e4e1287dcc5ccc5b7675cae340bc35f88f4f68d21415fc2e54005e82dffa6a299d08dcf077c716c3ef546b56a38241ed4d296fe0b8fd2751a1477f6be19e3d39df989d373c837487fbfb6e320d32d1a1f10571f033e518ef3f3b86c5cfde16daca690fa3633d356dd67d616195e2b193d21b75de20f9fdc62cb35aa4590523736426f4b1c464fbb9c061861e9f79aa99ca0c473434be99864a0880292a74ea8b6115c25474998b0b98bc6947adb503514b296af0bfe678c40e08ef7aaedd477276f5f97c621e2ed8fe176a32847c9823433601565946e9e7cc791e29006220ed6b182fc01dd89a3752f47a269449725ca4330dfc651924f8573e40aff75a9751cf0670b844e787ab1076b7341b8d42fe63b6487a7be665d1e290043153e0379a2e196dca181ab0d58c66076c66b72a3a2e550c02b410c7cfed22e4f24a5ffea426335e86d21b57affcb0175a2d232629a5101dee56f1ef7b35fb9dd13bd4e7dabe3bbd97e89fcab05a0ad1f967e8240ffbce44b9d51a91f7fbfefc10bbc7baea7a88c8a4cfed087fb03d98de12f6d31ee9d10e5c65c654b289fc8f53f048c01e7a0dd1d011ee46e638e15834c1e153bf01a205"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3a9ab0429cf2e7e972a75451179f7e76082ab99c834c1fde92e927d0871b5c3f","proof":"da7ebe3bdae91d3b8fd102eb960fa8ca3fce27697a96efe39db29fe19dc77c2f6473e45cc397160bc5f7635cd8e54abe9b020cb747cecdc68b8f1d4a9294e9048c8c61220fdc8e5d5d24725a48b3f3b9bce5de0a66939649bfb59473a811834f0a4a73ee8893b25518c47c281bafc2f827c8c153d03f3e5c93a735c66f859d01d2e1a263218b1ec8e4bd1e3ba86d33a2a431b92b840c7e897eff9f1064ea610d05006ee3fb9850104ffd44e0e80b194acecaea004c09430bfd618841850cb20af0cb350aa6242477deedb2bc130719bd1e157ff3487b5a17619a667210203b0c0e633328633e3c4fa1b260904b6cd6f97a74ccfc048ebf2f78ba6ec40252751ca860e981c9a2e29ed13add7636f28591995db4f3d228c417489a2fdc9c024719705aaff6ad37c8dc32140b73e4ae7323efd9e18fa6fcab9215afa3e8dc128c5ba009bb19ae6c8dff894363bb4cfbee7acb77ed2a11c0cb4da7e21494c2492b7fa044057c58b6e60d3a848e2a95fb8295afd0c05fd40f19569cba9aa60989b1370eed9fcb31e89e76165e0a19c8571045ab162494995671f73b3977d51378401da05e5bef82ba1f9885eda126011b55cadb58553b486d160e282b3f8e06d88165c8bffdcdaf1f644191c9f14c57f1be9d710c110550ace1aee87fa229c6f1e942da839fb2502b14d01a9daf2135a948e2ed56d0953fe1f932a125b25bed8d4a320835662e11d652ecec17c6e0bfcc154ab3acfa6ca04df0b33c1178ec0574b777e895c74845dcc743eb7bf4954219ddd783e3e225f5b977d9337f45358510cf7e20ec3b6fdccf81ead72ebdf9b6602ee15bce696d835c9e47afa9d0ecfd72e84c192a4c22ca744dc2723ee22cf9ddc020f77dfd957e2a313cf47dfead49c3490aee06b3b81147cbca585558dfb90b8b402dde582e9cb26600bd521560f9d35407"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5822998383f489b25c1c168e86a637bd0fce3dd356d034a0cb7602922193555b","proof":"388e338e87b1e073908667c778ba13ac0443be2a6cee9228dd5433bb36246d6190e64f4a94df0ff0b813650c66f65c26fae1fe2cbac2e0e9468874d9496adc0338122590815005adfd7b23604caacc9e689ae251c42f473754c5d98fde6f21756051fd2e8cf025888a2194cc1c0ca7b5ae7895eb9282632db08e69a8550a5909f2a397c10cf37c80d8a6865c1b7d8854329a8bd0482939fdf7ee1fa7c5d58e061e90a50439a49249e9c8fee12062e584fa4d588bdc5a4c878778c03ef403ba0acdfd28b41f7557bc658ae99ad929abe256cbba00ddfc36aa74f447716d9ba102488c9b4f3463239d697c46e7275524210d00bbf73cc019b02a592001ba560a72daa5cb3ec3838a1ec2a05154221802178212ab0e0b1b20e6c43bb33fdbc1b55a7efbea048dc2152b7d254e8c49c98983c7797d47ace3873374182f9270c9894f68fee2ac7600172e260a075b0263c2c35e50256f4e8437a3bf9f83b000edf67562b2bb205e04135a8f63625206d0d5ab0ef5ed69bbd3c2baf508f49a8c578d38e6e656ecf75b94ae3bbffbe1ead38b476f0f6f2727a6aab05467a0e27c47a85f0ceccde602737e555cc2b4e903146b31924f53fd967df30eaccf5f3214efc4495a79b76332734539695cf865c1b61bc4b51c47477cd70437df2ede2108f7881c58195b2278453d629dd2ab35a401caa8a1c4e09ffa223495ba4e8d3108567f608c90f59d39e810434bdc61e98b8fcef7f3db36d02a7fbd08bb855467e1fa44352281aca2f76045898c35a8d39a8588cb1350b3d0b80f22d8aa208f8be1a1d52ff8617e4c889b55afe2523700daa66b3ff8bc010a39c637bfcf4b24acf6f09b33c434ed241ce9838a195807d971c987f2701b76b6207b702a3cb63db95269f60d516b69d6f3bb5c20f5caadc8bb0c80c16de9540dfcc37be59d77a65cefb06503"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d21dcd3f4e8e731b685ead8b3399c426c182fe0e079202d2e15c5ff53453f42a","proof":"866841c9e8b27d8f54fe52645d943ffd6ac1f1dcbc43cb7589a450be13c5cb094059347fc88060db49a6db20063cf68416859df42360f199cce32a9c2b2aa568cca474a7547dc06c60dad06f26b87e6395ffa545efb9a2c104ae3dae7dfe5d1aa09f9aa9600f94c0e6f164034d5d9d31236879e3e6beb20b1b1dde6e9dfbdd14aab97e4c02b29657cf4a00d1ddb0fdf630d1aa0188f984dd9c7a9e7bcd6221022e915f683a3f103bd4fe19f9cf3b1751d102897ed2164e4db685f7001ec27302c920b4244110484faa51e554ced972843a98e999f692ed61d8132e57653cb8054cbdccf79b99d84b5f4b85dc42fe28f4e47ea08e0713320326bc6c38ac61bd3baa1f0560a3a2752bdb25522c4428550b0371b535c56d37147082a1b37790605092df881c7368036485475e1354f050dfb59f01b5e6d07cbcb8f32a2637cc3d1a060b75479f88d47acbb1361e78473c18a9023fcbe58a2175e6054f7482fbfb695e12482e0aee3696fe7fa418c4119cc599465dab11aa14aacd708cb2dfc1f20ef81f0b189c4800f353044b9754c90203b34ed888c5d9becaff9064db4bc9001244de3bd70c50108b90d618c5dd7088e63dd8954eb95719f96816a59cdc2a6709aab0da1a015c2c6aa27bb5fc851651df06c291d194924eddd712d0e45ea083437e7811c3b8ff8a0c8460c69be5a857e17ae28bf3168a84e834418ae142fef637de7342151cedf3dbce5ec764f62a596f0e4edc47170454fa753f00b1cdb22b676c113d872a20b5e8499e85ceb12830ba93f37d9f81f4160dc8210e145c74556e602839ff2c6c7a96734cbc4ea871c51dbcaad23dc1b85ac2458d98717729796f218fd3aacce43c446be0cca17fdb6f32cbd81c202c9092498294cf8bf1414b0a891ec76041749101c9e89cd7b8a94715f8687257eefc9dd9eced7055cec5370e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"329061b5b35d593ce9e25996cf917b4865d334d472d87fdcb83150053fd9ff55","proof":"18690f7960e2410cd5f9a592552be328930f8df73691edd03f4559f5cd76827454d95a2f5d462ac703379b19f45293f326bae73c285bfb73cf249c8e7e32c87afccbe9fa7fbc55aab829ec27167821aa79a815aa34a195ee17f719bb4c21631fa4a8124b93f4825867705fb672eaa5f210a6a59378b726e998f44c23cde352340b3a986849b89eddf9a909eac023d1165493063d73f76bf821f5dcfbd2feeb04333825a7f662c22abcacfc74f4a03aeac1dd4c46f4d739358d01283d3347810354b35533e62ed6851454932cf3560cc7f8387c85b25627a03e84dafd592a360a0cfc8c755e270d3a10ceff96fb67b8ad0f9e822aa88948daaa3d90657b55022ce63afc487483e7cf0c185100b0a1e18902112d82c295a246a2e6e3d823123a7c0892e4445afb441c7c902bff449b297570d8273f80702b55a8e2d475b0dd584f78ccc5f4a40ceeae8f89bcb364fa0cbc16a13bc2aed9065a0190dfd455283a742ce3a6ccbb594e34aef01ca424e99ac8a64259e1dcbed31fbbc985eca572ab7d687916e661888f08de12ff63c45f932266d96b1fce6c454a78537e6536805c74e01e78864c5c94d60da4166107db69ab03e5a6c6b3321a560421bc7011f7c67f54f575e5db1cc5f52a33cc572298b8c9acbc2d75492c74181c0cadf28f9f4849ec3d21c394eb557a7f8c08eaf45755c8f0d709f2f4fcedbed603cc3b0515f940800abe337b48a2fc4ce91d963fb530f887324099e75333323d0346d2f2fa5456de1cfc3eeada4469cee77836968006f64b7caf258bdc59b00c2a75d5fa1d1e1a34d444fd151143f5eda59fe89637caa8d78f08d07fcfa67e4b54339922fd69161f61e6c95e6a64b6eb616ce82b5d0017e8f2e9c649b0c17fde9111ee22ef2109049f360a16c14f77a9a1c6d71f632193dbe85c01044ae7269c5f8a83de221703"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ee3f14525811eff4e971c8f93363e7248be2838d39dfdc9d535a26ebd9ca6e00","proof":"9eacdcebcd1a8240b6eba069413dc2ae788c6bd47b835c61abaf58c3eb154f1748aaa13a4687d8df252a1e34dc90ccc453884b4842d53f5e8d1774eaaeaa952118d4cf65b71ea841fc7a696dcd97f00841626a435b3f8bb918e6ec11f0c6c25d7258b660f8cfeac8ebb6dbf634a56615d5a75a40342f07d285d12fc5272eed4c781215946bd9a5bcd1f9b8c9f6fa1512d77491468b39d2a06ac45315a488dc0e4831a341bc2afea73d2aa3599a65def18fd6e3405cee7ec04cf93b6ec3b4bd0e7907c850a0457326739304b9ce2b84a1a79929671230cd1fe7792a40e60b3a0498ee9f5204fd63ed810bcae10594340d4a17942fc8901be3d572830e8504e602129dab68ec34ef88cfa1a50bbd719e8bcc289f8ef29805ae1d4331806428ab7732feaedf2bdf2479fe361ec8e03efc993c408713bf69d23c220a7443d876a42b32a939138cd7c26dfb749dfc470268ed9b1c132e695ef89b072379823cbc8d7a4839942a9768b614003953da16f5a9af222dbca2bc55df8116d35d00cf25cb054862e232276188687640139063db8909113793070910a0abce652e8e64e6676274e2bbb647f9fb63a8d9c6fdf0a85dbe153edca6f910356c065ccec9e643213a6849850863d995410d5ae1d2d749f63151665b82f63a40dc539d646c7ea1091a8618c3ff511e7e92bfab8c32c6a80ea803388ebfba805998742e3293e6a3d124acbd4ada21b95f326e8ff54d566a5f8ad65488cce0dab5ce3c8b7c698c7e2328f4347e16079ea6ced198f963ebe76797b41237e4dc36210abd084e30c0100b3c601f285720f77e43db54b6249cd3749e0bd5f08a779dc02822c894312c870838fb27d8cc76730940534b469daffd70460b7a21732f7844a445008d7cb2ca200ca885885bbd8e2ddb61ce992f00c277b24e18b5be6126d3b7ecca07d382c53400"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5ef7c167aae66b41225f3152a86833b7dde6cc0add90e5c922d63b3963059077","proof":"4e26ca4cae32079b6d0123e18603e2c77b499502c2eb1aed4bc87200f4c3df0b98004b2b2029f37b3c85a0a5c224f840ef8938a7fe9a054de98c125823eeec7be0ef81311f47c4d179d52443fb70df521443adbab860c4735c0a569543d3e174e0abdc63a5c1aa527aaad87d496a1fb60c6ec4d3b08905dbfba32f11639fef6d98d6ee709dcfd5905b2aaae863d34c672aa8dce61abc979fc9856a49d2011403bda14b2aa2c16ac6145ba8fa92492b694671e52ea8d3f6baf95d17a96d39d6048075cd4cb7ce25ef694b4dd16dbe4563c85fe577673240ca9be2dc84ed435f03b22bee5b689f5545594c5a9df69adc8bd9bfbb8c3cf61e1365278eb0520caf304c3be4e81569e29031cae2917240779149133b09f338b55fed5b4a91e2d2cd2c6032f2b3270017e589036bbf3e4c3ace9bdbb1364586a7a6f89c770a103ca749de0c11f8a3988543d3f030439bae8438a4a49dc0c9f40f2d1ca14509f8c35f37c060e42b1f626b04dc938f51ad6f63c5913b6963cb21b57f2374bc73c679417a286b4b862a4fa76f2663ed06b375089f5768d0062b59fc744577132897ed1225ce51711815f4b7be51cfcb7c6d7b889633913e48fcd82250ad7e07d93537707644106b4337859ffd12966e71990e77346978560ac3fa7551d433fac916018e1f7e9aeb04604fc27f177781a6010a935329d164622a7478b9adb744895d16cf52a4529647ad182efad5c30ad12be92dd14cf1a533890118b45015e93da622db651ee54f9c3b7be55665d0307281e7333ca1034e8f20a7b789a4ae7780e5443f632efa94bae0eb6570998cd2453542f995593637adb83d896cb5c629c4982731224bca4076db39f4093b35b5820bae316aa87b118bb75fee62f5c1ad17a5c0c5085c3aa8bed328b0c6efebbd30212e22126f6bb6ccffa7e618d734c527c5b2dc0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"522c8162ce03d785cda13c48eca2d7c5d99060be2235300222c73c169ab87231","proof":"78312d56f713812c088aa9951a88b570d46edbfe8bf0d8673ad7d7db9714cc7f6201e5e79b9b13e1f79ba1f54326ae8a5fbb6c69e10634301a5f021ea5c2832d380a0d73dd1e848fbe3cd848db5a898f5ac27c4625a8dc88df2f801aa1e7166110bec999fe37ed2b40e2efc8ae59c639bda918ed190c8501ec926c13e49fbd5e85ab7fe912e171e980523610776467c8e47bc5d3b12f29f2f2eda9c3dfde020c8aa04367091cdf6810835545c2f2b2ef7787360973a2d8e12c0b0dae02178d07ba7776a730a855e81869136d6a6e352c5d796fdec1cd84e10503cfec412bd10632cbbecb4d9c3b091161b081ec8d259871abf390e50b25419a1822fac9ee0c26aa976ffaea1d7f7c874ec32c4eeada8865e323019d1b784ae01b0e5198c991260e933f08ff3c3c69f731d7df2a2c06910298253536d1853622b8bfcafb8fe65b1c1aec9b6df646c9ba1977d81b06489c5717b7ad7242c5631ac93b043b4cd8684e76a31c49483fd472e839fbfbd68296f22296067e011f5959833238cd1d316e3e621f53fa0b3498d25a6dfea6f7a65037e775a42f7c7f32532534135a353816f6d6b3809de86ac1437f79077e0ef5a071d50f8f6d381cee14d4e95c8daa7735c6f7fcd24dd497761da8e4e6649b01956b1598fcba89298d59bdbd1626f70233f05ca8cf38855d990f57ab4d2032cb826b0a16a5570e5f3329944c534d9e8532187bd66dbf5ba72158f3919f0d172bbe09010c204439cecf99dbe158bff2027fc23d7ebe0b0a8de6666cf55d464f3143c1220637ecf4852c3f494c90018ce92350789b391d6251f0390921b69b34accbee0a2e505f03973247d55e56ee1bac32c860b6ab2306769a958c62eecdf23d7d10bdc12ebb255a435af0f32fc0709c0ac5e9ce56d1d528709526d107897a5b746a65582a3c881f8361726a3f337bd207"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8068bb571cc5075c16524002e7f5ac7ce8ce91cbfce0f3109b4f9d65861b0223","proof":"1e75ab8b3a843cc867482133f2b9f1e5c287c4eec0813b72dd98cefb44d75f3614edd98493e78506760d0937a997e145db7af4365c3a28c461e969e272fef648be491c840bd3b1add8efb3bd10dfdc6e558c2a51afcf40e74df74ac7d0639049dcb6e157b529ac818d281d7ad36a6cd1cc7d857de103d692ae27eb3b595091551bde7009c0dc1cca0be241a2996ca233a0d0e6a552fa95162943a45ee20edb0bc57153174b5abf6af9c8e817045c0b8068e0b7f4c95acb264cf72a1d4500130b555126f92b9e46218dc056f92664bac83de0d32b5a595331b22878cb4b8f0104b07acc97075ff8bc6bd58ebc2a2e6290b9d0e0b8e4cda6a1073352babadcde3ff8e96b1dd5628504a734e52353a246b57ede29618bf0ab40755b9b097c4b0e57807cec20eedd08777630d2c09766922ec75598c3ff9de882f9068066bc8e9e5daaf790247f9b1b1ddf8d9581f081a5a4e86db2f3047976b27e496a050c88f60558c178e46bb1a7f44e66581b03744897225d238da26e80d7719c48f3238d0717f2fab47287ba8e8fac5308db306659dfa7dd79d461bf7e5f776e13d67b49e661fa6bd8781744fabe3c54d0af1066ffc0fe004a2a3db2d5a3c7a2d167d628e03daa578b53a67b96573c098845eaf5982fbbd294acc7ae56747b3f8dfc0a848809b01a5039e0786704c7bf2fcbd550889de7f3ed22ba6c818bf3c4231e1ee97742d26d99534a6949d3d5b9c7db3b70e318f2cd0a500b06d3c76352d32133f1e653c26d24040239514c42b41d2fcb00f2e39c28f4cf9df57f217149b7926c076e6516effa9f1ecfd66282efd1f377edf1e55fe10effaf5725af6bb9ed0100ba8059be43e5aa12973f9fa4768ec648f0f060b1a1806d440d444d497e524e455c5704c95ff0aa6e64cb825b61c33d50b19a679b8893d70231431a4b28a1b2d79b080e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b0bbf4c72276d4d8133abea930ab1f8ab5588b7ee6f276c89498e4c4bd8f074b","proof":"82455b78b9c905dd98f6b3bfe176e1f0916fb179de5709bebb9c5460b6c1b95c6681c9f5b0590c4c03643b20e235c47a4e7b1151a0c5b8d180b22520d325f843c63a5b85ea22c74c6c4e8b0a3801feb0b230f6f889806cf6cdd7f3f9c4c0fe0e264ff8768fe77e83ea014152e875f7b0dac5d01a58809c43404d82798e1fcd1a962301ed90aa96ede02fbb188a839d76eb1960d21bf0ce47ceec5813a657020d5d51d6751c358e017fe387514a96d538d78cbdcdb3570ee1aa19bbb52e157d06b7f127a812b36bab7ea37a0630f04220990bab6e2c28e165022236d850ac940fced2c6ac35a3fe50c9de409ae6ac73e1d3d2d0dc1130e6c3c4e544717bcbad1e22fbd7d9d5cdf4297e9c9eb8e55af1339cef2658bbd41c65161cd2f6c9463307f06ce460fada841c57819fbe50035f8cf6c3da6442c78e17c60f1fa3ff0fce63f0fdeddd50354bee637e1dc3dc54c6087eb6f8a355cd95934a4e854c188fa8530e8dfab80011139fb3f4db22ee43f575878facadfee05819f71d559e71d1570d10902e6a4fb43cbcabf9ccab994aa1b3b0de526aef5ef69845cb67dc1ef98a49fec855cc29cec9ffa80171c9ef3466817e10cef693e2b4089b1124acea73320d42b6418f12d79878fcc06eae5fa4059fe536916463d3b547fb119d0b26f0c642dc035ac24f12139cb4a313465d757c0f8a7e8e0d7d294c813b2fb36a00c1f449647e8fe2f7554d8eb99f2a1027b314b4896d3c344f33338a1e5f3c83931d68196236fd8a1c444f568c79668ca331cc43e16cff3a708fcb8375f421083800302ee07c5b50d0369e9299925c93fc27c87935d90e075c35d15b10e44a9058c80451dba39514da364e759046c31d30cfdc452c9067910a41b429dd4fcef87e895c084a67392079337ac42c3f3e1de93fee9c86db6edaf6677cc1684d6389f840ce02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3e28f7b8fec5b24e1781eb1625138ea0d343c009a9d23cb00eec2d31aca8656c","proof":"727a761cff62d680c4089dce61eb17b4bf3555cdafed0e940b134f7609680040c811685f8bfb58e36145acfbfbecc56d4dcb6a33caea7448300c36018875c512265e0120c954c2b7f06c7adbbe8732e444b888b861d143d374f328b395eede3b3ae7a3a1196108da408df9665d88cdcb888bf785c00056285dde0729efb67c704005f04d02e802e2a805a13357fc47eff2a29f5d8a60da4ce35c18154dcfea0de0135f7f7638963591b07eaa53a924a8cebe59a34eb419d09d707e17f091400124e5d8e3fe5121b44843096693780b8477266588b9e0580ba67acfe0e4a1f4012ceb7fc8df531b7fc887331bb6bd4a930c7c3ac2ed613be5415b58a38272c25d9a592cd42bfb845f6ccde358ea490281c4addca314489d81a71e61a0c667a274b07ae31dead6f3c269ae268d367bd23d8304c89dedcf035e451da5c93536b66236817ef23eb3e167e7bdfa3da32dd8d9994050d4b8e29eb4395795ef4ae01f6b86a285c2a5244b83747a5cd449176a0111ca7db591b5a2782e4d8a7efd14237a3ec3cf004b76bad66071c4b76e8f862363a38ed949f0d1524268b3da3c8cba591c65c8ecc5533d15baf472f55126a45f4b5db1f910e0eb5f19e65113e4ab5f4dce6c6440f422492dbbc63909d4914aa0a413a941165f9bebc6f9d71f90ddc107f86456e07a417970f705b409ef9bd55bfb3491c801bfbd932b721423861238259a0abbb3d5189d17491334187041a4378703ab71e2a7b1e8e9ee219922f09a5296e57cc74d7e2f586bce7b1462386e99d0ab3f70fa5e0e3d7c49e6b078bc320a5870fad9071964573d5dbed7eed1e07101bcad782b336c2174fc14888ceeea4824cdd8e5042e748387635fdf9950404510217c5b992114ff436ddeca7e08700250e571aa69b3fcd80e0620fb41d7ab18df5754fa1a8baa478d0787674455020c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8e67d973c25b20735ecc54b17b150330427ad0243c6917d4ed740ea2cc844673","proof":"b8d0cabe022f85174b4cfb7a6131ee4dca35ee074535b042ec0c028cd3d3572d0c0b6e0d94c5d76efc0dfa930082c295a1e1e4219fcf07323c68447436b84227cc3714d8544e1c21ee718fadad039461ea13bcb9ad03370094139af6d41a4c5b227fe976b548b81a4db1609d5e8ce3d8389f2ad67f0cea1bb3c087b3707eed0ea124fa4a93e77e190bcccc4706e22995cc3b2319aa4660380ed40f9520dc09052c88f8429b5fac8a6712d627fdbcb9097073a7a908957a0483cb926e2f7e3903a6b187d73bd65cd36e99e58895a143b1b29edd9c7132b007f71c26b53f4d5d032254c09e544d851670aa3ff29e387514df74f928320e9272ce10ef0136f53d79a457402b978e06bb2e1b56d0f3b348305bb00ac99109e8f99be23d35a298101c42853aafd88309db67025d3e90e92a02a9b805bc9d6c2aadcbbb5828ac6733375e84b784870c9c7f8087f1d06772b24dbb52093ff4f91d5bd9124ab43a25767514aee18ed08a0a1d3d44e5af4c2a5b22b7fd6e75cfdc04b28fc1d1b7f6569436ac986afe6e89588110e02b7c8ccc0537d5194c88967eca63b9368cc748c48c4476b0c5ad4d1666d257627dd9778b782782dcf90595272c27b8320073a088542a2effd07c67bec1d422eeec4df177b0d98204b7084a6aea6685c4346b9e99a037ac1301d2ef7eba24960140c4a56eb55bde42ce4e9552e448e5dbb24541746578d42b88f50fea5025b8791d926085609d0aab1ed62dc18a735a3e518243a11547e055eaa82ac1178d12da956b10975126df46c1ee439437d5659a8356bbae883c3ec01d02da801de7f89a023ba2c55586ec2f220105668fc5c7c4709a17f6990ad2aa2a7257cf23942190c11adabdf850a71895b964315a8d456b2798248a490e0a9df59e2c6bdecdbe8de54abcee483372e9f6951a0de74c2fabf30281e34802"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2e3627920d92b6a374952ddd6a85f92fd89abfe8e1241dedc909e2c5606b5d4b","proof":"b4396757b053321499f86d2f39266bad7d6d77bb062d67c15c26c848859fde4d6e6e697fbd48f4b8b82d5921e7ccf7e81d96a62d8b2a53401f977e19f90c380c6c89b2c838290a295d87cac4e491c385dd8b5d1af6f05fef2ff699c2321cf366721af3e9c927df1ea132f65f66bafaf96d9e1c2712231345fc83246c43be5537da505a631db3a451cb0c5cab22b5b422b82beadb06156f1f831d6feda78b1000b1f828bd85b9e10c9ee1a01d6a750b6fb52755796a6b31c1b0d095515700c30f724c66c254b97c45538784bfbfa56e58e6199b5ab18afc10dc599667e9dc84028c99dc37a72a8462864974f9f87a0788b6302bd99f0af2f73836adb4e4a42b79081e21b1a747e92c38433fd38d2b9254e3b27f59b4c69a8a21536ca68e851379980c377560d6f7d099df7435712ca02b46cb8f4cc770d259e7403d6623705674d81921ca7d65e1bf1c37fee15e20daf83aabf70562c7c0e2beb1152cc3c7d1055c70f0ca47a77351e8fe32c9ab58507fd5a6040e26a7e6e62e1829f47309b61abc22e0082818b5f6430ebf08116b4544e674f64802e872c5e1417dbbc8ae5e00cc8bd82f5a28f7df1912b5a92d3d4dd534dcc17b7e2d862ab406287a2a535d1596fbd08c67d8bfcf0d997c0658691cb15847da0516b55167f2c5caa41b02836782b2903623d63dd1f702c41225d6bbcb4edc6b7cf4d61c4bea6982a854acdc365a1a1d3363ec76a48a4f1fdd80e39f699c2d9a78990c94efc199fdd31e44954002b5fcefdfdbab0d5fded646ad82b48a3d8adbb7f01c898a5c8cba5722b64d7844047a8341239393af466cce8f39cf9182f9ba3914c48dc087a57eea90c52a0a3dd05f3b75aa71de682437862fa53e557bf5f9703132fcef0d61478e7c001708fa5a8f325dfdd068e1d13b1c9afdc36d877314ad109826c9cfc7ee6e3d903004"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6a2ee9e468ee44fb8aab72d48d167519be76db2d6f7ea3e4ad33d2d418a83338","proof":"f6c1cf11882eacb577f0eb52979a301a2f736e39d410e6862adfa1b34112c830c83ab5a46fe8daa7c092a9a0484a6de5fc7d3ea4cdfbfd10d43b8debe91fa802bc2787be0ec4f6c4ac6eb87aaf386d111dffc0a4304e884a35776e564da3d6309ec67e5a2e25a8803705f86d714aecddf3b1abbe875863dacfcea594bcfd95228e3cdf35ad0c9fbd36ef41d460b2fb30d3737ed855578718b4deb010808f1107cb1f5b3c727741df3ee7981daa28fb428d6cb552877c0e342adce91ef02eee009e71f6e7851b0c05e30ec7b34b324f1c4f2ef29438cc8a3c3badf2ceb2d6fd0a84a56f0ec2ab985b17bea14dcaf468d7d57daf53801be9e97314e8570637c95b64543b63a4e2b8ac762c86f10dcb6b6e17e144012be10512dba702ca22c3a5061ec04ef6672e517b725dc6d396fa19fc2ca21702dcd1211695cb33ee9b94871dfe0b47fd99109c761dac9dc93066bba8dfe8e2052757c9c33c47208aed4dde5b386bca6ec44708c0c981a54afa3b646951c73f5c747cf743ebde9e8db2467a04e64aa11501cb9580613ecb75d0173f294443ea482ff443b192c1c0673bafaf4b46bf8031723b71221c2f4a7adeb0a6d63bc4d3d83044f5818670526cab59614bc20f05c321b487c930ddfc7f154828778340ad0ce9e21f4b402d04185901f62e02218291e7891d7f8030ee56a847d5ac2e8be9a7015b3babefffcda6803eb86b5646ee771cdcba797d63f0699286731f07d92d4d83a1d1190df8d328eca57a397c41d906cb99de50b66fd8c0b240e6d5b6b92f8bfa88ac651e58d1c3b466f91cba4b619bf0995a95079e2eef19b31883f8c5ea6d6e0521a85909a264cebaf35ce0c1865b0b3d8959620f8a9bdecdb035ecb6746be7923da04100d35066a92f0dcab2d41aa8933e826f9ce95c6460dd0ccb76860f4dbce6e1efa710665446e00d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ee3290dc40e74076e7f4c8eff6a05fa78f5765be69be373ac154afd1bcd2596a","proof":"5c2c6b47712f43f89782fb1e2b588e5c983a6f7fc99543ffc2408b75e0e10161da41580cd9ae86e169878086a4fdc0d3d8cfe999d5deede950a02ef98bc1922f30264d9a801cd2ce3359c48369d959b4c2a21630559481ae34119a21be52e17f4c43d9a28b0848005ce08ebb121112973f90c293e843bf9241271a4d92b79e4c516106cfaca39bfcfe78c20099fde88794a70c57306b3fa08773fcfb6c49f80cdebe813534d74cd5b6123f1b5cbfd5000ae9369c9334f62470d708fc66ee1800e9c013820a584b50ae4e0fc8c127d0338f08bb7418268ce9853db70512fa460f8e50df693c2281504ef71a066c0ca8588b0e6fc638ac4112510a3dad64f4527bbeabb3cd7adbda056a8e651b5402dad3b497128f418795d55363dce8ef88b8240e9da47259ea139359b3d4585e8f536bf216ff052f3945cd715af610b986a2790a79f0f295cf63054136940b0c6dce555ef5851ee43917148dfbd5773512d75d029655fa2f48ea9b546d883e1e7771c4cbd0681dd3dcc03131065cf88a469536c2c8ca5e324b572796b456963d7001a1dc433fee2b40c2aa0a1f375f0138e301e85e44c7e99c4571836a1ae6651924412ab3e728241b0098258882571b84c32f40e86b4d36369267c6c4d4224161ff76582de0eb3ea8bc10b160d04aa3a4ef05c0ac5a61add1864ffb3a03b055e0ff1aaa07870444654b67d240c3603aa46e07f0f3cd138b5344fe4d135e73538ffd12e7190db43c49f8ee65c021b13fc62f60e87532e5759d0867cd00d79208f004dd2f3cff0750a54af8e00204378bc105374033f8182521ac3f433bfcbd8a969042ae80c831678b63a4c7f0fcd5be6ad32067167d5cff4acda1302b2111af3212fd174ad9aead06483af43a0cba08ef2f0ce492e051dba98a8fcdfece1b32fc033f3c4ffda634036221db60c982c4cee704"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bc3c75a3940988e25406e1ef2f906b8d01ad8d1650cea183d886c2ca5e06596b","proof":"2a82db39cb84eec372d021b290c749619a989584aeaba81159e78665531aec395a74a9fd6e63349e8eac166677d1b057e223612335097b888897c07d44a22f5388925408cdcb9c54538668660ef3e9151b7fe9cb8bac38c62bb2fa85a52acc7d3c52e84923140d4ab5e1f2d24affa6412fef35ff8d1d5c3e4186b0b9fafc6d3774c4163ec8d753afff49ce0ff559363e420652463289f4c2c08d3ba4ad24b3071d624b147c93a6b8e873f551892ccfca416b664e15a71987d6d8ef1b14ece0053f9a73c7e51bfdfd754cf88b0f580ebb2b8a3f1f8f9280679b3b04d586a0dd0774a26711f7d69ba7b8c288019d9546dbf672a397e3b8a281124cbe6c8713a6767aaba77bedfbeaac15c612a9f4e493623520b0e0e2d27909b312d81a8c31252f2aa54f62e94ae079e0bdeca69d600efb9960f15f07b824e99117f49a1a27cf48ce5fa969738f51e50d43c10b0540b99fe2caa750d3c88ac1dd117ba5842ace13ac5cff8d841fc8537453aa3988ec02d3cd085459dce301bb6ae419bd5ca4a24c8a1a083358d5b9463a8bbbd5218fd1e8211eaad8b16562ab251d5a591ea3b10d782c38ffdd03736d5daa6d3b9db15447274efad150d4dd474d06fb140fabaa20e63b9c2f5e3220c49b9648fa5c190a143e3f98a2fe07eaf1d7f93909ac33c63ed49fecb3ba9b788e13161941050b55477ef2ce78f4d76be69ecd227dc8b0886e8cdea7f64768c76a5e5b50589b7176a37fd68aadeb1b25f4f10a65fa30039d21cacb69d3ad7b0a691586e858eee1e8a62619e0887b10bd9d91cd3f83b267f92e7ab3e861be5456505d82772c3994bafc11babd55b0a8cd149f7140b716e8f945e527963767e42eb9f84ae2d01772e6b0e798279c0e14807ff5ac5edfc0f0d408ba5cc059cd1fea046a9d1f59e1b2c889b716548114278ff0ffd073f6315b7404"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f85220f05c75fccb7496f468d4adb4f57c30c98a424de01e5d740b0a43086f1f","proof":"00b81a81004320893c99b99b996dd28a2cde15554a75b5bc599061b4fbde1a0b06e0a93fe222c371c204dd34068af58fd71631af346fd6b27189d541dc2b874e24503a0b1eb04c6d6d724bb3b0b9e5917d98be65c1635c5234e29e876ee80e2aaea2748677baf70b7f5f3f8d74e5db378e73af544c327d9a14bcfa68ab6203402a0b6eefaa2b0a653617a3b33005ee18a2c864ff3503474f04bb0759124e3407faedb4276f13af4866cf462f06657c7899e22920f3a83db840c02d44481e0e0bb110edb4765000a755e0bd6b253955a57ca9a622b21d5fd4396933b87653a2064ce2aa48596c6340f3bf97901a36aed062a70d65074ceb2bbb4806005837c67a3a3eaa05dc59126faf2674942d51ed8bde0c646f9885fba50dbc4519ee9c1751ee7faa8c25afa59727e47aae3f3f435ad351ef204f8799b831faddfeb74de910a8c6111422acf7ea4d9691b13b8aa0348806080646cbedca9fccca7cb8e55321869d66a8c458028487d56780f4b886b96bcbaec7557e603f88dccb95fff6f95300fe9a1af65a6e19217f0be317c94529771bd2c1b5b6122ba32a06bf67122b0adcea0767077393891071944c8955135910126df4104046cd24d6d740d5944867344ea6f48ef91f6ba319e7568797da43be5e0a79f284a70690c9e4be3d6241735cb78b0a33f1127ceaef297dadcf1ced0406ea3e03d8d461a5849b383969667ffc40875e71fae8588ed3323e39e180d90b80096dc3829a85a39bb6c9281be549e0a3f353a3f65cfa5c33a82507c4879c5abf62f36238f3cc74754df33951e11858277eb2f8468d151f53a12c70833a0855d889bfecbe0b045067e7a22d09404ea17996f4c178771f250b3d98f6175afdb01dc9409948e4356231ab222a1e4a0f823a3ace2190e0204bdab8fed25f99c36ff0b7b8f2e9c2b724dccab79a38cf07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fe9073b2651a3cebfdf21777dca399e9f91231dff73b74b503e6cff9f0c8ce6c","proof":"3a4326a729ee04b9117bdf88c63395fc582aea5fbcb5196e0ae5460699385a0d30cb614daea331c258ae6b5223501595531a98917bd55720c5c6fbb97da98a46b055cd5a18e37ae64fa539ec21acfe9da079592e92d8c512695dab6df7d56410d2737d6fe102353319d67e4a93da5a9ab464476b3fc92ca7f0297d8c06be3973b46ce4547f86a68ce1cf2546c1433c67dcbaf112a9a0ee1a54952b6c82b226055380671c5c7b22acb7ef0453669dea16d72a028c5484c7f6747e6cfe957e17014335f02a5c552fafcec2e8d3b32efa8c59d3681cb552682f6e2324e0315def021834672ec5f4bf05a9bc573c6941f4ee8e738f09db8781da1d405c476251a53e1c9325f4b53e5aa49f36ab7216a26c0fed87e3e320081379788f9b60046bfd1d7c573e51394b7b3fbfcf45ca634528993e5ce8802acacb21839d9441c3c9ae5bc0b59ac58c68353afa764bc289ae075f38faf90f935a697f18d717b9f372fb25e643b30c2254d30a91c77171c2aa17594d254cd4449fc8091d0c54b6029f0e19287c39014765848e982b50b4510c9ac13e76cfa4280af492b55000ff53417133de07599cf0647eddd1abdbfa03f935d36a3e1bb48aac9b6cdbdb907cbb4d632c3ea7a489366fe64601c7512a278566e9bd8fe5792f17005e6e2819929ef0194524014c03b64ad98adcb16a8fb7a13e80f1556f85eea10efee7c2416c6d23dc1d8ac44f7888fc11df551a06790d9f90933c94deba6213a87ec85d09716586bc3020d2c092e8c94c6f04b2ca11f93bd7c8257bff2889d041f9ea5e8774118535679a25a3fb97988ff6c5d686c59a897b251f06740ef152891b864efe2e8aeef304343e9b33ba36d81f796a920e20ae9eb36cb4fbba533fad12bfe289ebf5721202ed4d50b8fcf2c695bc72a1a6118c938eb9ad529e0216fdf2343ffa7afcfdc40b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dcee5272afa043a51d409cd499d584b8d26d9e4ce2ef3eead3471b20b4062d61","proof":"d6b1ba62065bffa9582a4826d879e114751d8a8670aba9297cdeda5290682e698aa7f63d4ec9bfd5fa7535d9c7ff5e0e40cd62550eae4f19399e23f7734f645854d80ba570d4e9033eb7ae22474ece96415caa0ee49828a3fa543c9f76642e006e48d1b549c127c36b55261b99b00adf00b9e1dc077e041b7d0cd9444d1fb1407c7a191f6fc5a6842c7ae75784feb44dc427159cc42cce0009370b2ae4a5d10be0727d3f07d03555c3ea38f5cc85870d35060e1ec389bbc12b10aa630972a4067679569c01d18195ffcd5560e4fdd4b15fac57ed30e2d1060bb63b3c1a1b150674105b0e573d923f056bea75f8a87f99edf28fe53bea416aa85e4a3108fea671b4c88f85f6b58811d6324cdec72be65d44fdbbc06ad5ec9ea2e3ac58d4229554745998004a3698a897571138af9b6875567f5f088e904e74b7f0067b179fad34f65e875d859cf2533c1c5dff076f81e74e13bc2f05a9028d1f9cc7304513773a8230490554c7959cf15fec35b29a7bf1efb296b053edabfc077bd671045197513a27c435eb2e7aae22b4909b77184d7619d7e31574679fdf51c04fc120444e417a1b2474ccfed7173748a6ee846154b5950f569b5d04bf477fba43bb14d9ac5d92d6052526c2d3c865909f867ce5bd8458ef14cf29b40ed58ff565fd9c501d0d2ac7886bcb8899e1b14ee688c6c639ee2ec688fb235b15b7d4ba310a963e42277c5e85de1e08648068bab730a61f0bc968e058a65d6bcc04919452720faed52f286c6ed05c4792c8f76d2ddf990ca2d23729fdee748fe67bbb151dd9506bf02e24d2da1025fd126f7c7baff6a94fe00808f53c00480f03ec33224ce2db51686d426ead3638018c1d414c00636315b2f24f3ff5eb7b5dd97132845f2da09d0a051a2d61a4ccc057904d5730add2f85331468575e86874b557af57efcad59bc708"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a4a8f9a53578ae7b0f7e73438681790c8caf8f6c98c3e05227e7b37b4f43fa38","proof":"24aa527c7ab79e1c9847aa3e4488a209c32fef10c4985c39301f1d939a76f046d028a0be28233fbd4591dbbf305dd13291c97f739034b8ebbf2af05be7deb4205a81ea85f86a794e9f3c2e2c5b39155bcb6ba2e5485a9fa38407d6281ea9fd2854484198e3f2d66d8f9739e1cfbcdd6e80d214ebb96082e2f14c75657bcd012922ac19a168443e5ef60279cd4245e872a68bfc5c3495d8bd77c73e252acb960e89c1b8d741e24d11010ce0f203d7ec37c232814e0d29d9d2c9d279626d9840078f4ae0bd8e48c5e40ec690adebf3b13894e3d5dc519b108d476a98e69a030f0f66ccaf4b0202f3ad47320271f1f72f59e3707707e7de4aa7c0b21b01ca0ecc15649658a060c41ee0ba059e8bcf677a863361c27d6bcba2ac8ec64c9f0387533ba8d2b35b957c43549147070da50eb5972f7dcb69814420b319a2984636191a7a0ed2432beb0a3bdc753ccc0dac3e7beec8444110dc02fcd9a95377e983677670620b6f71617144e67872fdca7ad341ad8abca6cb2de2721545a79dc11690016948026e454dc9f85236a7d7f9123b3bd56d18d536e00ad3ebd828610ab6a3663d605e5f28031318c2f54f4df17bbbf41f822eea824077d1af38a42cd187b04667863c923bb4ee3f7562aa626025c1851f73dcaff8bc7e9f92f86374b802fb2655deab11b5d751ab83e68e17891624978f08bd33c6012d0ab9c37795d36f3df85e2cfd8b8c448483ec90bf691c0897b3a58fa33955f47b45143c7c3825510fd954d0d8588058f2047eeb8545f0f309829f194bd562b36196806047363618aeeb06e61be3c7da1f929b85d6f1a6d21125495163bc10b3293048ad29fac8c4c5061116a72bb19c83acdc6eba5373df803a67db74f53ccd05310cb0012aceee1fa2081eacab8748fef58799c504cb322857bdb86b3771dc9a1e792682c58f58d0f402"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"28a2352a0d28d3fcbc0861c4dcbc50441f4895f57e5118dccdb49ab493cc1508","proof":"0c568b2f722e81755d9b2deb63624b7272f229447c9098b8bdf28c718337f27176a84c6a27b4d3dd378263d2812cedf3090ee6929dd03b545aab976ed773be6d8acfd8e381dcf1dd888e05645ca9d901551c8806d085162030e1048a2b7a5e3a9258d00a7a0d05d3a36203f84f46b6b7bb9964f2ef3fd18771791d78d374b53b60db6d80a9a667e7d2473d1ecc347f395ec15a33afdb9b0494708fe876c6e5026dc9bd03069c4d69365215db68d1689b3573c257c1bf2a5c762849a2c8b82301b033162302e5fac7960627e824c6bc3f9e9937d8339adc1bbfada2927d488f0acc4d98d1f8a6325352bb24a02a90070cdc876c4c8a9b3968c64d3571e59b7a581c5b2b092d7a493c809886584a7c698405b8bb30c491a0dd5f6a152e934d0353046749c5808cafd11d600ef717fb43dd9bee97ed496672424f57daa367c6d72b34138a4d7f822d5c1ac93110b7c4bde61d0dde31fb91d19231222677909d1a201c278e364f843acaeae86482d57f6ba3fc10b0f7f28e929728b9637d94b9f84c0a61f8173d4ec48c5fa778f8ccc0e7e1cd26b0c99d6644e4d405137f9cb363519245ceeda7b3cc6e735b860d3d0ee4ae01ad4f5a2cd9f79675aab651f74c18124abf19b1184ccbf8d1285ae4d990cbb1fa0a4fd435be5aba7ed9a8446c1bbe1aaa932a3d4c2ba065ec751c6815a22bbe43ec3b3ca90a1ff922e319bf12a29f6fb8c0d4a53d14758255b9aa4db583ec2bcfe529996f9e98e13809914c6088837f80e63497a22804c96f2aa4d3cb4e603a78dd3c433f80843eb94941992a35f5176072ff6c6bb662fc863c572ca28d00aa5814ef6035cdc99a37c0859545ee200dd95e165370ece9e78d48a778658056fbaa5172b5c4a4ffbfb08dc300a28e840381bde6827ff530da4bfadb11639f13639b5963b578c997953d25702637b1bf07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"98fc39f095f9b5bf5858304971fae5f90d6236827c9f037d01b8f2ee8c71823e","proof":"4a053531b7a027ea01ab1f76aa6ba209896af4e5174c545781d765fa8c399640063f5d69aee19249d2857e05abcc29847b3aa27ba3dfd4fec5ef10cc6168841a26aba7555bab53442122c7cc2a94fac4493cfa64c4124587b7a10f241ec3171a06fdc2e6b73ce46b3a1301ea3af9e1cb9a54343188493c2637aca58899a6154f1cd4576a8da5097db929dc30c4a22fcd1f742d1f56449c5349c884e5d8fc5e064c07a3e89a38e7eb0b6d13cb2a6fbf5350c528dd13502d6c6c9ec1da4f782e0b7bdc1f673c4a55a405b23ee745dd1c623efe8c868a6eece5ca1abc8f69723e0cf0553637c85a22753c7d844e715cc76e5c0a249962757f36aed336896c9e930172b14a66a1b569598f22bcad8065c1c3c66eb23469aa7c437ed34d43bfabda2d54de77cc0573a0e451fa41de2a3be432215572365f4de04df935cb93434337431a5401f32368f49c5d0d4148a3c937c7344409cedda459b4ae87cca4d7de2d01fa116cad01833b0b011dda98b542b9fc76f03895b7b6e6ce34e029ce5479e06a64257205c529fc4544e1a519c937182d5d8b5f02f798f6a6649d5341ab068451823200607a414634233eff7b2c2f7c9bc6e2732d41ef0f2cce56114283cafe081e9c366d9ea398de720be8cec1437133c3cd1acba5c1c501326ac67ba5478f28aecc8c2801a3fca60008b8ec1ab6df35a32c236fd6f2be521e30196621cad37712ac085773b6f096f88f48f4d95400dee3afc8a6fa443b866dcdb283648f2f4fb6ce1c195f475bd7cbd4dca7e298b20117a6dd71fcf6b1fa925756bcde0963408032d1e5614e77314b4e049181d4e87c7591fa9a7e1e88de8e903d1f343f2d783d9c24b93f7d6d3556ab20d1119b76e4275a989ac32b2b15423701d16beb8d0b68a5fbee6293747e01fa2aea02ce61861e423e14ddcd484f19c82d24353fca0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3817a735c40fe022ecd3a6a49aa37a26ac0a33964a2a53886509692c33cfc338","proof":"14ba84b420a40caf86e31f06106b6c79c9628545c398cf46de929051468b15450e0eaa0d822c31957d322a0e71c8a6c048f2ab5fd0b42cad16f670ef14628976d29eeb6e338d153fd66e68f3936fa33dcedc6a7e2d8d7540880b16e8f74e635bece0e802d29d7c79675a9fe4560966dcc4545b6ab1a974db151244d21bd7d20815fb53209b8c39a62cab957154edbdb52f298b37745a588e5c530b3ce03f1001e6563ec4321900728bc2c961d8161b15ec83f913ded7526ed6ebf63fa650f007d0fa3a3dbdd436ce1dd2b77779ff282356a0571725b11ef647e2b4755f648109e8277b32887e76d5ecb7c389f193d220abf16b3990ca8a07a9f706fea48813319c62eacf18cae23fc23be24f3b66200d2ea7dcac50725999d7341093a87257210637c4526c976ba7e29ece32e90d622fd807f863d4e1261c150cee4d65ba584f049b785acd50ca3341fe66024fd3e517315d19a7e9774c7b8043eeb9d9073f6c46526a54d426f6e102f04b569e0b882222c47c3fbb7eab2e70cf6a82c721df432885c985bd83aca0b6b1e871be69289db31ea996c049e89e7aae9b66b998c13da6c145d8aea8790395b0a24f4c810a29c727c441ef9f42d34792fd0c14595c4e1e8c01c3e409e49f936e9f7b416ba5f6cdbdb12026e062f3592f7a65618a047012ad27bd28293a700d4159bdb93f06e510ee71c2e5c4e6175970ec558e9a2a70f882220647b6ff6f783d4dfba64d8af04616c48c7874a9bac55d7d4269160941ecf1b2e4b0e06896ff18697e739d6ffaf53032cd8717ceab198eaec8784aea7b229e78ae00aba447286974572ff57c6fe334906cbf026ff70d28b66c31a8070624c1fd65b54fc767cba52075f3e5489522f10e4f341ae59075ff72cfcf608d0d574c8b0a849a02b3197f7d3eedb43ed99770c423a5a651bc0e990174c332ac0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8284542f348c4a47c431501254f03b11ff9c3ce971764d93819b07d5b1c88e70","proof":"92462203d96f3876378e6bae6d195f4fafd61373ebb58bd10de7567280569b4b7ea842724d3059707239373fd06597ed0f71442025be7229f4743e732d686567780c8c8c0b9b86881ac876ef44d7c421a33e11705ef6b45cbe2c7202a8c7cf38b867450328653bfcb9ba35bece8e6ab691415a2bc8f6355263977edfba997241d4bccf649fc8b5aa91ed2b29e6f0a10a39784d030d95e9829d0951efe419710f71ad0ec1e37586126bd6c4f8bde917e1906bf76b2bb76aed6b16caaaf959ec0ccaa950527261f7aea142742de90d3903251a43cfa5260e84ddce6b3bc0dfa60464642810b7d99efaacd655500a9e59f0dfd0754a0e0d92be35fd9af89afb352096db3912cc9aa9f6a26c48676b9d719a33735f19fdea302b134d7d8776d21f138c9b14cc9b9b558a4c35440cbbb554e66b753fc7d06e8474f665780ecbd21b7b5aae999d48d78bc7a3294f47604fe858d5034ea11fdb0528b63e566fcda03d230a203facaebed122cf6a3ba041af6b4d03d396ba20546f13700baf0681804918de9ead06ebf526c0742ef71162efdbb6275d7c8a82eecf8d380324f7ce9391555e1fca4beb0ce38db48870792dfcd94e8dec302d4d68583bb1c85822e12fbd63c675090f08ea197f4a17b532bf2806878ad3a12aada8e33dfd4140e3305a2d3feabec8781117428665a839fe5b800d5ec75a9423a4108ca265c9e38152751535f6be8be7c0d2782a2b339b34df70f32ec741fe4e1b754c95b2f82be2811ce834387da1bb0fdeabfb781c1a3b0589dea7d3e69ca49661a685c3e806511967756f7220e9081672f620b8c51b7d5617e04c5c00a749ac473047991ff69c8b2059459a2c0ab622c767e87141da70c8b11572c06ab8efed4759c0db10815ec177220ff79f94984c02bd02f68ac4d033cfd0ade747776f5a74dd739586ea0927b8190a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6818b3abd9d5c66ace3f0e36e26d327a00e2b20faa666774075ea4913a01547a","proof":"f4c9373df6c79d8d45e44b228ffcd1e4d5ff8a3ef028fb0097db837e411ae1741a68b089bb80c96e680542e3c6a2b9fc1a47306a849cc8dabb69cbb408ac661812349f0643e4a2b421ef285d942a589d9a34a2aae1f9b99eff74c914cc9e8520c076f3d2b1f1867220c53d4e762f6facec6771c3824ecd09bc8a1edec99eb1668e3b593f83890cdd40816f2a8e84eba404611eeb328cf7278f37d8b44473fe0b93f351286ba694a67c02999522b69259bba6c66e1eee0f53720c0d01d1eb4708d3f05b4fe3bf2d7e2d6474ceca45de37c885451c1323b44c3f799da58995f10b726dcb6d767db1f95b68c5ef37510e506fbf54da1747c481433ebcb8ba415c6cb8fc2ae25f8cc6c26c53c434409b73c713b9ddbc45c63747877f38f572f6af39ba5f046b90bca09cd8fa8e028bdf5551f339ce0539e68184d8adfa1c6330cb5d7cf3abe186004b02e608e92f6c4806c09405cca1a91c4c948fefdd1329e65208b08dc7861aa776222d6c42474fe04a6dcc97bdfdf03d424c52b57edccacffc2932f652f62ed2f2f51de18aab9611c2f8a6c788bce9a9055298366f31a8be6f2f36591c5967959c8069ec37a0cdb02263e5051072d3685e203ce00e75aa70ca0a10f42a2afd95515d51c065b2a24091883b187b273a2a4a4d06a20898316c8c2b8a4bdb2726d94ea5f08e3175140d37c34ba5ab49ba06873c33ed4ea17e2eae565ced28dbb3533dbbb2b48bd2ebbafb1912373a122a7759cfeba2840448d971083e9059172eb003433f42fcdf13d7d36a625063077f504c04469fb7650742360f982b8029b5c89890ecfb4d861b98f78ede2a12930b28bd71fc69dd121a5f381a9b634ae98dbe8dbd205fc9123bb087f6bf84adddc3edc11643a11e52da5d4e03e46d4920ba0b13f37e668d6551f3b065b0ff72273c00fb7fc0798371a7c9dd0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"927bb74f2aa2aeec19b3731d43d5da51116938cf1a94030b31e7d41f00c7440f","proof":"7287fd669825b563c6cd09d38b528cdaa20976020d2d8a675a987df9ecb6f54778400bbf726ce26c9382d8f66069290c7b546a56e029314858c932c851306f51e6712770a7a10e28296f3f1f06d19a14f468f0f354200309e509b63907123d094ebb395349fd550361eef990d39b740b6a5c9fcbdf33753c31e540fede426166b94fce078c8e05a558825999e3f12d290391e1405bf5cc15ce131c000214c705a58e028b2d1afc1ceb5d34b4988692b8e12f624cf4dbef75a9ced9e4aee9e6020ff762db19f48d7fd38f997a3e14cfc1dea1e9aafb0ec3fb6ccb727c3528ed0dd49b13a14b471c573efdf81191071fc86a210baf8207eb6de7720b2273c89438ce777ba088a3ac5193e6c014222100b5da715ba4035ca1340803614bbd65a059dc0ae2885d906c33a6224d99c17b69ab354f39201caa7b384217816a8410780ab6c7c47b6c4fd74cd1ea71247b88ffd81e7a7b9db91f0d688f34c4c189075a51928a707e8af1e58fa7ea4bbb0d292ba9822025fe926d98ca8f36ffeb9b157b4c1e2fdbc558fc623f837a64ee35ca222bf4beeeadf2a3827de7b2b49fc4f4a2350ed70d7bccaabfdbd30bed71de9d0c97565a2fbe867c4fa6b3b4b3c8a286d443bc013ee5cbf01dcda40c1aac1cc0932d91253248b1aa1f857d4fc90c31aeb31bba575c6a7bac1e3bd3d142f4e90395346ee001c8708986a782850036fbbe0e0a181cb03de817e62ad798dea9ebc08c1ca2c66f386976f5cdc2bb83d9433ae151eebe62a26e847317af21273d13e03fc953c96f0d7ed43a032bb38b41fa8a08718e8d9b6feebb9c5cee9ef0db1f17ecf44645cbea30527539476934101fa8055a8c4295e9ed04b0aa5689b8051d4e53d8b8fc94e08fa2ce7cb85c11f5a180db0a3ff3953482b11b88033bf3e11b7f50819352248e0e8499bb2402357f48bd9203"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fa14436f8ef52a8760377fcfce748d60910c8e3eeba8db91f89e2de4faca251a","proof":"425bb7b168232e5f0b54236101bf5aa75c2b7eb8572d9203e06bfc4f5c6eb6236a5b7cde814f5dd5f8ba65169b9ad12921c2e297a6df266456af23af92ca5f06a21e56ac50dd2891fc53cb76ec2c526410f769708e6ac65ec99a98ee1e2ec00ba0df51c4d01a5793acce2ffea21e83a056d0efe3bf8b732b7a14ce76d071b93ab93427ec89c1c6475fd0c8b849cfc8609a8678e6f984b258ece922c95605bc0595c4fec195714d460465973c6ff27a19b4feddb2a406ce08515ec2437b6c9a0a3dddd83ff56f6233c8e3d24d094de05acb19699ccef6daede7f31d8d7050b90912960a45dd72cd04465d86e0ec84aeaa706b1b01429b1cd05708e0efcb0fda4a9419f4c6441720205593ff1e319a2b67edc1475bb292338dd965a0391c6b1e23b010a48c921515c7df11d7d8753ae27ac2e074c71e1110facab4df5f489fe54e9ecaa0f4307129888372e1cefaf89496e4881ae43fb099b0ddf6f99ad006e72422aa60efa179a34f686cb5474281ea4e3e363eaf141890fd85f1afbaa39c467d0237127c152ab6cab135a3de7083a0ada572ae846004f7d5efb674d1a2dd8d5cf61d631e37b4a51fbd781da47ddf06fba00b2a955742abdb13527ad43936e04e60c65a41c0a50a8659b2d8be8b463eb38b4ed351222bb86672356399be7b5e495094e793087b8144cd176db504052342fc75a44e2e4ef121e31f847962a6aa4c98cd87b0b869e23cf9280f3fabc17874ddaf3243ca97a3d81c320a279968f76d729752b15d077b68977d63de5a6ac1c8f98366f7aeb44103a09f04064594dc4fd69144f001899614e889c432c986f71ce493f0545b1ce1ee9048a7d61e3f2d7dfc9b805c1037e394517448f6b33a720913fab207ac44703754e985a9c8b0220d636bae6753197986e65a767423884f47f233ccabf5c7ca899f357cabde1ad502"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3a692e329b5ba86e3067b50ca160588c29b0ef9932a182658c64506934bc4304","proof":"a8e05d505a1571adfa77778909fa02968c7123e6c300152ffe194e0c0c5ad667d6c871fe06f1ebc1765d0317ed378372f2de94b6905175e03bc06726b629601482a0b03e3b0292cd002af7fe9095e4c171d79140d0c875027405e6c0df0e5842488cc2bfcb23294d1daddacff4a99ee8c4349da1275bd492cdf3fd9f69daa67de7bdb23741cfacd54ccf8fe8e6bf3c10267d2c55d3559eaa383bca3864978b09bd1872ec02fe4945831805174617d977d81310d579d7cac8549d5986a046dd05f57fbdc629461cb920b30f46981073a39916469c62ac0a39df2f24b1778be00ffccc6e38cb2c5f8ace6ee9ca99865fcac1c9be94d415c1e8e2c9317cc4952864e2f3612aebf46261515c0ab0d6b7f4ba584f27b0f2c17fe3a8d21e7e7459b3297c565ffcceb2d3a78db13b73e5f781861beb87d889e2035c962b8212bf9e92449e686c66b91f88969cd95198740ddadc94e084ba6548b4144cc9ca9ed41f242006d70463129fbb44cbb720c46dddfa668b2c9c1bf268e1a9e00d18e526412f1932ea5adce3f35e807ec10df0198c941880303b7ffb098de5c290b22b9c05a718ba09b0ef803fea33fecc73e218b8e6078665063701e071412f69ea40958b672660319b42d981ee1fce0ee1f5bfd70cfd5fb9d94e407f46bceeb0eb8294980259fa79fe3bcd59e5bf1901b5bc04165058ea9303e901e15039f3ae40e7a3a9fd28b2c4c15447756426d84dc960ae98fbf604bed474ae95b1e0165d10210044012ef8f35f61bcc81d149615699811d3f7a7605f55ec30ac30e2da44dd0e0396e66db04dceb4629362c3f0b6197c7bc29d97af3ececa352b85dd51aad9dc73ce8f4c0209a8f0df3e9a040c9b6b36129c3e55233a31805439fd6eb8fd844ed4c31b037fff2d8f4fd7ebc0e4939b4f808a5248ee1260d1b05ee71b5d3a4ae66f99fa05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5e25c4ebf84f92afc959e25020cc1863acbad91e27f48ab50864275f2b5cc445","proof":"be7afc23d42b73f19832340f91db4e5eb5c6cd57de593e706a3498fb6010625550774823f2e18b253b1b057325863cd0617c0a29db031dc3ec8706b7c681702b3832930ef59e4047b08053e9c193bc21de9b01da3c33e78c1513ac1cf0174b2c5e307d1f0e413b1d65c67f85b99cb7d26635ce75fdcec4b35ffdd6b2fbbbc024920230ac925525b84e1b148fcfbbe8c4ce9005606cf95838620e8f43d55ba60fbb9914d7030bff8b7a9bd26057b4d113a72afc5eb94582fa8709f9282e99af06ef5a6a5f1e04d347d86fc17385df1eb7f332591b9fe8efe0254c8fd6eb5e0101be64287ae4f3df7f544defaa01126f6a0561a41b538742cfc57670d69893b4748415c56c6d3caae8d231ba5bcf991ee0aee028494ce62520397364d8d103200d1aa79de658a36652fbee191e80ec331e21b8dcc29d9d9f8c3c84001b606b9573062cd8083614429b074eb0b49d372402d74329549eb686cc1e549a687b5b34030efbb4b8fc78a7958553d0e533bf4d213afe69098608d27f98fdd6cd5d011854ceca6e06c81214428d8a39b6e537864471d419d74f5457b605d23984d8a7c04d98f34fab86e233abc0696d16e4c02dd483aa30be2cabc9703553ad1478dbb804901142bbadf35502a67c9ca7fe5a76b2a805a59710eb78a028bb6d9b42bce5771a3b0bafa41a29fc73f65eaf561080cae457c3f1ba31739fb0a1d3d7664c0f768653062467a4bdf1994a7050a03a2985515b685fb134aecac58b05ce09e53e6066d885a410c49ebd14e82c5a992a665f079eafaf76cbfd228491d5e2d614097d7eeb3999de9a2751587cc2039ee916f64b0fe275895a7b3fc6ff574be4ed58451aad905893f6739ce753f134aa70f128ee69a9dd0e501ea2408c754fec23c10799616ae7df38c34d2ac6db3ce09c64c5bd64a526ff21e3faed8f8321fb9cb300"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1e2bec3fa5a78f0fdac73ba53a97b6af9d4dbdff4ec0ad9c823e6bb6d5eca92b","proof":"54c3004f5fab12f3c8674086d134105d9559625889debbd4928bff1858e84534f216cb09277c7410b9bcf1a0256415bde4e01bbc743cc7a61afe6060ae61d85b946e2c5a7364ce4171d0df87be866bdd6ceab934e3d0d5584ddabe0c3100966d0017bda64872fd276b0e26c16e139fb603135914fc4b519b61b0815309bb5f1e177321738676841a8ea142f25e138126bbef6d5e8311114f78b2af0cda04620ea7bbfb475d5764a618ff83f7678de17784755e09dfb41eb32f6545b459dc150e850a7a12ef83275c194bcb244b375f0d6bcf53a341a2a89535b87a2201649a01ae29ad2f9feaa480cf327bb9f9282f74baf10a11e280a8895dadde418531ed77103f5393dc3779ce9640c163d15c960a6ed12a267ed50ee745ef6951d094077b4e1930177d06b759a93afa14c4d0d86a1a2255c9e0bba153c09f44f9486d57150237862e4c52b79f5cd4a6443a1b9c033504d60e4bda5bb019a5c5ff9fde2348e689ea8cc6ba335f480a87a5281cdf85c7e7941a340c144e26f524da03e9c623aad5555972967682664fbcd1745c454de5be45efb3242d2014b7457e09cf8377dae4fa7449f18ba7b72a6d7a5865adc9a7c1aa9230d102e4cd723e5e301db037220d2ac12d2313d481ed3770e2466679203f6ab6f0c72b731bc269e5a5bebd67b4f39499cf0288387f49325ce9a5e242c6fa977fc6aed3f3c2be2cf7f1a1f431d04b679bb69830185d6cefa810a3ab0d8c04a425aa41529fb73ee95f7147ee331a945bd61a5eb1d8a60e81c91aa2458e284b66663e87df62bd5baf4fbe79833ca4e10f15ef536feca7d4e17ca13965743835feb3b046b0e7283f4e1cff172512991b17d51c89ab30bb2b2a85a15f1c6c13827ed0a493536e8fbce72b0692ad00d84dc4f08ab319a324475a0df1ba172407bf61d752616d13a44d7327b9766700"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a49501eba5bb7d0ebdce83f346267cb75b480eb5f8c633f75b1d9b10f84e903f","proof":"4e93eed205f729d93b0a98efdc341e5653b31ce89d93459885e136f0cde27b45fe8ffe64beed0f7ce01ae242a44439ecb9860bd318be85902cc7b5df1d11ba76d69f33624937cd3cd205991d894d64f36f5ed04426ebcb6cb569e9e8b38d6f1fe0c7c6369ddd647b1cba6aaa73bdcf7f91914d81d6968d0e2a06c8fcb9e7cc7d04947d877390ceb6a321cc959b480ab602f03031fc93451dde1a31f2af11b30354e618f195759a9e8c9b03857d5df452859d6599b914b1ea46da17690c870b0fe173d86ba8e1e9344a79349f56012eb2cdcb46274bbc61a51686a5b6afbf9f098268b5f0e6d8ef97e34ec8f3b8e8b2f602a7c0229333c468805cda02e5bb8a30a8456e0fc9215393ead64feaddf832aa6f990e9e2a40b8b21f0fc5bc41e12d3b90862d0911e8e270bfb1612a40cd9810128b50c95ff84f9f3fef7b01248a24656a4d26a90ae303c08839a7953cc785643ac3049ad20d2e5c61d6f564130c3261188d138b2eba908a038566383b2f1754e8ca3ebff2b52b107a76de2e2796b76f28b58e2e690c974854d895c8304e62b5a545ff48e59a4b30ae02b92b9384615a80442e8fb8bc01579e32db3e5eba0e7c68c1fe91b885aae80e541bff6a5b965ddad9ba97b5fa69f347e46449414edb640473ab7e760d7517d21bc98433863708facf4aeb5ec978be5441b56bf3ee3599dc4664b539e112b57e232bb42f00ec0138b713be2f1eb85b8bda44e58a7d33780eed9d004d0bc3a2158359e951d85e470405653a1851883737f0d3d9ef8e52022ca6acd8f625f3f09a15c11b3754104d921cb8a0d78910b35356238c51bd6d968709721581c744dfd8037d611ef014241578b574d3a399e9e83819d16396331eb5a7fd8f551f30cff47e7a2da7d2040301ac1fb5b2618f70e8ce198a4f09afff4e5970ea39b45535f8593bcb360df008"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"081051372a370d815450e8f1d0d7149abb4670cc9bd2e8814bf159e9cf3d8011","proof":"8e47eda2fe18348882317f4576cc8bbe32e7536cff50948d210e10660ce5667750fb14b2b459d2be0cebe0c62b167de918281af81746ae05b9d851512d7adb35ea09686f918bf6dbd55e8b04f1b64f727e8323b96babe324d622965b4ebe03527877db4148fe07ef199674db1d72c018431bb28dfa58a670bc9082afee2480529804db98a2a916b24c4ccab3e820cde8baa4986e19c352116664964d209c400477476c352730321e0f9faaa77c57c7c003993981966a387cc37f723620052b0cebfae9b8f2b43f7ea4ce5f787dfc8b7b1e6d1ef82ecc8966b42fbc45a772260cf41d03df3f7dbffed01fd101a2a4725e98b813d057166bb28326b44f7cbfa074006f68cff8a617fb75eda3a8e548c7c63e66ea8236bcdc7b34bfbfac53ffec40befc176e9a6531a58d42669a4d898c8a86f3dd7593a4efb2a89aac0e41eef47ef421278ea4cb2227a70e2166a1ce25a6d0226ae946fd60491931e3605087ac47dc23574c770cd4e2fa72b07a7f6f17407d6b7ef79e84690baa0e1213f95cf07d46ee4ff5860fd80b4c0007ad8ff2335947d7f159c18161338cb798fab3e7491590fcfe0598fdb915f0c51e8f9593d12f4ed755b8f554e7465f3a32b3e7e3907290d9ff27aadd9be1d34bcda52d69cf675963c33b5450cce6071d9ad61997482d2c4c02817879820604dd10f5345d539a6dc57797b0a3e0be5b7958fb33e3aa306665d3866ec3ffbce821ab489a4faeb806db4fb2886e3cb4297b76cc1d0272339cd1594eaa5a5282564b10090d0b0b7b2c9549849aa04bb7c61f04f9a815fa071491f17dd5addcd2fc7be9fb6befb58459716eaa93f49856d55406ef7f4a637ed28d47fbae5943b5a29c15a0e14a6feebf2693677667f2d062d5e7acbc6ac9013259c1aaf07a1c31c5002667ca1ee94872161084931b838581cb9b0bf9d9290f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6e19683168ade033ae483c7fc7d1efb16a68787fe74aa71d60ac38912b1fd05b","proof":"3c7f38d5d75ad02f713e4d4b4292860a247581411db367c6fd980e9fea10cf6aec0811bd3b341c80d0f3ff4dcc1944b9409ae3f9dba77efe484da7c31c915509c8eb98d7afa4422f313510aa52fafc84a9356aeb74bf7c5904af7d4dc0f10f48661f26daff9ba1a9a0158ced812622b4c0a5121f6bdddd7e4bfc74bfe0e72c4cc021109b86f5ac6e25418d920c8b337338ec3ed4acf2f05f34e42d71e452910876d5fc9ffc49971b4998049c8f2191c3696a8c50051f05bcacc0ce5a200cb30f3c4774ab9186bb691d461ba1574e77777f8c71e4bae176a5d54415d6b37d10037e96427db517f2f92fe70b0736c5812d182ebf1567f427359acd191454ea826e8e17e006c117f497da3bbffbb212c48ddfc7bf68207db0563ce0f11f01c3e13396fda312b4d1099d9373988a3ef6ed7121070ad8573ebb0f159e24d1f78a29786cebb1e5097d6f467d340c56444c7f6bfc2d52f41404944bc51c9d4ebf65e61554232c625455e81fbc701422b367ece263ebecc17183f3408646ad447a347c7a52c950e3576ac2a7bfa5aa269f780e6e2d40d83e7313bc85b189b0c7e73eed1c4cd9ef8a513f88e68b77bc76b21e895915a13e6775c720d0e98a112107c45e6da02dafb9e057a8c73de8f47c8cc8137ac11ec3bf54e7410c51f361523b3c4a381e2205382d2269e843aa651241de2acc8f25cc20ccbaed6ce972474b1b209b0b0089fb9b80f185fb4a248fa4b18b0edf89f640e3a84fb38b16b7211feab3c26d680296ff10a2960fa5faec2f27b857bf03e1fbc8b1db63690563a3583d4d075a3e5c3acd1f6836368023fcf5e5821da2af204c7e442deff29e1d57ec4fae2c6d194e50ae8ab7e2c8778ccb650f0ecd263b8119fe1c685bea14242b8d552c6c08168028fb051fd4137660fbe197515194ec750f12ad7a9785248647e27578fc04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aa0fdf19f29f527b62104e4f2fce213b13e23fd57064563592230b63a570e67b","proof":"be3b9e3e5eb29d789fc5278eea2fe4612af07d74af497109572d6f84c777eb0c4cd7af4f9ada685f44a214da4265a988d6817d1a86ea2bf6afcaa81ea0522838caa7e63cca052672b574d0b036ad05468785a3426dbd09e34fb5e1c51adbab522ceb7d91087919098c9134c8755cfada62aff1d47da871a501d90a7d19a9de7540780e45be3141550557c93f4a728637af2654cbf78de8e40b70d9ecd5b33d0997e6e51062224b35412051ab2eb00b595ef86d193c42c0ab3aa2058236a54b06736d12cc9f78078dfb772274cacd10c0283e10ecdcc1ced58c1bfff13b33e20e60732cf1240bca4e31a72c811e07152199eb0d09381a39bf71aa9163f141d177746307a15d6ede6b3ebdebf5cfbae2bdc917fb51903fbcc4d4338fe7b7460b17022fddfe120b521bbcebe4545d289be128ed434b8c99b48d256c21ce0afb575238f67848d9cefd120ad151da88da6f73e1b80c91b1222d37c9ba2e6cb6b8b649e4688313dde182c91b3f4364b3d46052225828af959ba3d468103317d1c26c2a24fe013d0b5081e0e93aae52f7e7bc84b76a4b1d02e869518d06f8aec14bea6d2e36daac4184aadde5087b1eb2e60f8eca387b56af0d490378a827f025b3cc087c7aef4545d0880837cefb36df05a476e21b15f0ab211a7f074aa778017f2e6318d8e98faf2777a03cb6f3f88924b041b7807e0f9e9723140b6cb98fa1bbcc607694fe6463304955eb2666c3f1a08b41f43c05ee45f7241c03196b176d719662aa482d51f7516f592b0631169d0e87973aabd3b9ac1860527daac8f57176c07366d3f612495b3ec2b771070dac4442863a8dd6301e877ae93436d8898be9070cf02498b1a941a8ab35d08cdb1c4b5a62884b8296ccba1bce288743016ab3f00b808a1f87da32d9cecb4b59977eb71019eebc7cb28e15ec654c6a58731f4abc0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"223331494f38427e220761ec265b49a0cd697ccb5946c37064cba626f3983773","proof":"3ef36ba000f3d57d22594aedaefdad967975ee8073e0cbb5f04fd670355e083c648830077ab20675ccd2cc08456aac8c55edc48488ed47505c16588e48106250c039b53327447696c9e61e7e3c767f1648a46cf7fbe9be245baa64b3752cd379700d05aa731cd00fdff00837f53edba22bd506b11e18d06db3a950104b7f956b19e2c4a6c1f83406851651e80b99fab6e44885822ffd08eec0d70916a13cb9018e104f72ec7bc90255b3dc2f0d59511f098fd12f08e684aa2641d6cc91fe9803af1da7d728267bcbf7e6b1f44e7c4b6ec185c78b63632ce3ed9d8b8352ade400a43e0b9a2609380e6f72c983221628745fe0edd42fe4e3fe393f7621b6d89b0b6806cedb1a8fca76ec4e210f1d6ff22386e6d5dd25a75ee93447c0c40e1f605be83d4668aeef8c8b3377bb1ed20c023b913a7cb1bdc29f9826c015d859458a5272e23b160e539e782236cc60ffff7dc072c2dcf7010d315a11545f0bb1a0aa7e96adfdbc5d9e972b33de605645b8fd8512db42976906f71ebf4e1190c45683126a75deac45ba146bbf8c52da40da150a15370586eb740c897721a2bd77d0635fac4a89dac8920414437bb1fd01cfb6ad93a797141e74cb335fdadc48db0ca461f870258c7f1d02fa41e1b6c5ba2477b8b1036ee6ce5aef622f46578e0720996e48aa95a5ce6a7f160d7d53b0bdfad800d6938768f988c29b365a3e150f99fb75de654113931a3f10ed55b96c61f7b49b7d1c59e567ff0289cb7efe234e508512fed35329e6aa2af65f3c80332057bd84e99153adc1d0042b8a5f50f25eb9f161fe7c8678948cdf492b5719f0de11fab2bfe46c089dc5e0db6986d987fbbd8456a7a4dd3643f2110f8c8d6de5878d9a89088efc7d590297ac229fdc9904037305c458ae4a51739c51b80166ef258147c18ae02d6b8b741816810586d94250a507"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"eccaf008b8913ed3883b422daad6535493100112143778bc71546a33e0386040","proof":"4402ad976ded3a3327fdd6b943ab20a55f1c77e5e4d654064c46d0ebd9607d7f42ace86ade8a1cac3c9c3ebb92f7acdbc3b87dc3ff4fb9a92352a8a28bb0d502b86d355de3e30c2b7222d80c0b6f8800199a9c29d80723fc53efe8bed0ded115f4ae017f7e81e27ea3baa664932902acbdfdc03f4ec523c53927fd6f90b22740f15349e8214481ce663765b726ff26ecf231047ebbcecdc865f74c1a62e29c0e9a0bc3953a47d66345b5847931a61af3961f95445534e201695e5efd7027940746f7a090c6a968121c21b2e39c1fded73acac9b8c8afd5e1700f26543d6baa067216970b86cf4f27956d2c3b2582cfc4310c935f2353bb8b479109bd6eff310bf4a82f72f877fa420a1e827d587f604ff2ebfcaba59678d462cb5b7f4fdb6865e6efa99e6da96250c9dbbdaf89b4bb99f1cbedf747856670608f891c5512715120490af73ad65b988b5ecc15ce03c8b0e8894814de93f7f342e1e44e2489955e5a3b53f1025a847cde36073f34f14231c21cf26fcb3f772fdd096213efc24329f0df0ab66cfa828ef0e58f40c50b02cf6572c679c2301f3a7eeaec480bfe0472ce4f921cc6bf96128dc46e43846abdc1254f8c6616fb0613a9ae3c2a01faec2b62ccad7c6a11b484dd542bb472840aee822f0535c76402c1ae612ccbd706d71866c732d9a10340f5f677eb71a31833be32c408fce6e704f6f6839249a15a1d1c0cc28d01b8d0cf44a1646dc425473df41ca1b6450cef804f886da066dc5415683e0eca077819982cd967446fa1a4ff76c205776066a267c20d7b987d8968466cdad16df33b992cf70abe964734287788dfe51fcb5db253c0feabfa113fb941684991ff61554ced3f16cd4ab802c145f8607c8c245b46ebd784880b329b63f90305f1e7fb1277abdc42300f6ff8c6504f16c0c2c19b8cc4b3630754051dcb1502"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cc3ee687ae70805403f3dbfe3e6cb016ff413422465281bc192e9d19df88c824","proof":"9290e62115b25e7e90a8d0342005f13cd29d71b9fa70b94d821df82dc237795e1aa70afbf425b51cd24f4d1c1d6a2269dbc993964159ebfeda0aa22aa6e40638c6a5499c7b2b871e68f6bbc72d27c65fc95a274854c949e5cf70263d6f18802ae2902ff4ac97d40ebb9d557355e187ed0a3aae5c5bf084057d45931d196d26562e21b9d2049bb550523fb24e07f9a74f38ce2cda1198f8eeff835dc566a54405c5d32e76b431671fe65711b02326a8e4bea496f185bb02be832a1b307ea8f6003bbd183aca9d679eb491c702ecc0db82e340c86a537633df55ea53936a6b3a02ace13d86aaa24873ce42f291aea2341adfcda7e5be207a700d9d788499d40534a41790818440dc7cdf001e21492e3c1b4008fed5fb1f65088fdc139bdc458d1296ae7687e3faa2af153fc05ba2eb694d4bf9a59edb04f54364b2bd6e5f4b0f0ea0599ed775b032454945782818f4add3de5a5cf319a4af23f387df0e43ab197b56b0b21dcd1002760f7c2fa9bd7037b0fdc7200a222ef3999ae71ecb538fd701809b0177b61b8035cd559259a44509f1ea04b507167f8784b7da6672a1d0ae43c05ccfbfa4f9188f13337b1d15f8ba5c0b7551f9cdf05d2bc5dab8ca1fff7518bc5847e434bfb53315dee6120e767b5ac30006b318b7fdde0b72496557ea6902048e141f20e67fe1fde10e249e8c25ef9922210d0631656e781f0c05564dab54e8ba75d21e021a0b2cb5540243ce2926ecde19750051fb4367ebfba03dc46e1a40c785c7e5259fed7c7727374cf2d14f535860de82ec554615f19fdb7b8ba73c1cd5e549e943d9d9e2013c8b68741695fef2249f67edc9bc288b4d8246a7091649ded31d38a059911ea5aa36bde6878872730c53f5c04e80f498665e6ed51e0fc6831dd136fdd3eeba3c502eeb79304ae11c38f48ec37adc70b9552c865d6304"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b00c8c89e1d37038ce2f9a0ac7bd985e43cf70fea8959bcfc046d57d45415c32","proof":"6225fff9e490bf58dd50117d4e1782f7525e129c305840cd8c1deb5e14a3d30338f150de7c5fb9d00be09f31c41ccf22e2d1e72fe2be3ef3915d856aecb2b219a40e476336fd1300e5ce98d5a0bb46a2964b33d62c888b53826df53e89f2343f8c5298c1d06d4650a7a49b43b58b009a9030b8cf86f44f5824887c38ea0efb6dd19015c5165bc0897a5944cb98f119dab13ac996ba2612cf1dcaa5e507b0d603953019ccecf9d89c800eafb45ca457cb7464b0a853ac91933aa57d4e3154e10fa7603769b2f304d851eaee285b4fff4154354c0a17438be2835f5b195151d70f2acde2b154bb7bf66927293144acdf1be04926e6d33b80f99a464bd292a36103f885e5be7f31c8a592c76dae2a05b0e70e0dfc99f587e4fc5ac171307612f25ea2a32b652d0acc6006a1fdb147e40ec538db4cbb82a719781972e424c9006d42801f5e553d0e82afa00fc83ffb53b804923f8593ac4de08d3bf5ddbd41bc146e782d20c10a0732fd3716fc14644f3628045a194cac492b0391c01cfd55042c4b2a631c3326663b6c8f0a92ffabff117788b4051399e248238a695bd30a9c334520c9330324993d1c4bdfbebee796b1304cb8698685ba2b6b08679f15dc0c7920f44f209fb4f40f52a72f412219548236e516f79300d50871ed9e9f4a0eac584eb2c3ca37766d7760a3ab16ff182d539696ffea2b42c8999aba46d80e4e3fd44e7e5013ef5a00d26b3be0f086bddab846a3cfd02b459b6f46adcf8bff10f0085efe0c79720994434cae0fca9a574d35ac62d6d099cd013476d8d50d6a6425c1693cd281d1a79caf4aebfb97e5dd8c56e4aa6f54deb820c33eeb996fddfc6ecb286a68c370c6b8c622adf9bc5ac963acb7abb8563fa81471d05689ec0ebc6c260aca150153af3c2501ddbe9370362c36ecbf2106d99ccd8266f81af5a5c852150d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e22bf26ee13a1c1ea3592c0f46fc4df6a6f4b53dbf4ab68c1175e4a24297f469","proof":"de31a64fb65755812598cb304b72d4da50dc1f1e1c03739e2fb73a9089251e37d8cd5be177637f1e4104170a33a24560553cf1611d10c8a90d8e6e3a5143b674b239cc8fe8813ce08179164be028b43fb99bdfc4688f78aa0683edd0ddf26d1d443f1ef2fd53e8d61b604017bb7ba6fe3b7ea8f63b7b7a4063f32e7a618ff83ab819098607fa47527fedfd18e0e85d626cfecd409c595084a67bec1ba5c3690eec0595435ed31f0366cf2256cdb7140d8178fe43bc779cf9c00b7da54c1fe8046e9782971601abff3924d18c564ab46436a75c115b3c7df50b386574fd9a7202fe6c8ebf6ec667f678f7539c0335b4e5d336d878bbc7bd93af0cbba4fdde8f6354b92ff3131f85a8226a5f0000e9c673021d62f7b221f58f7e2bb11a53d75c4c9217f13b235f73123d65378b08473ea03933712a47f06acc1cbf8c4ddd708a506aa652043acfa434e9d0c710644a5f9dbf3c49bf0245e7dde24c7ae8b572d73960014bcdb179ad88a905a031c30c878f72560eed757460fff9c6d90d9a5a5e57a463efe37073a3c15bbc6568e1b478f875ac4a88047c9a71a7c3a6410e0d35556ab9ac76bcfb98bb1d5dc86b21c225b9da9aa8809d92fff0b317ce18475d7b75429fa3e17d9a8465ea8022eb029c3dcf2dee2153cef8e772bf838f00e09d022adae0ab8204721abce87dbfbb163c4966804316255ff1270386e1886a783621262c58cc86f67a7b22935f597b77d0255fa2129ae32b1b1580fdf49a61dc9775599a3e54f27fe56e3bc97b06560b331308da0017529420539644231698b1f20773ecf0465d0135b2618aed7dce01574c483a2c4fcc5e11bc547d950dbf88194f09f8064e23988159cd62254eeecff5258534d14171abb6d43376c1add496487d05b3c5813288df1ca00bfbb4c905b7d41a1afb171e006fbc93011a7ef2ff544501"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"00677d054f530d9e96befcda8452c55318b18d7fefc227e68c074eb6ceb38c32","proof":"58b2cc728368445bb1df0cb227a63cfb9f63be02667f3c87f6f3daddce3f120fc84224a5cf255c06fe8a41b29e743901d1d480db1386a719278dd629c1b7cb4ebc4d4a42c533e0a1685d3fbed06074d11b40af17d8b5fb1c5d48dd56d1d0612d9294fb6474d2c39a4cfe3d4d4c90937bc12996ab1ad469ecb2bd9111889b1c648ea49482a05c531f0085d52a0ac9ec129eeb832ecffde8cd2375907bf64d4303f1d71e57eaaa2f09f64e8cc919e5b3fd5ccf711c710ed6af07a6f8992d52a3021871dc92b0a465228f3923e4b6659f9b6da2f5214c136654a41f6909875c640f646617429353b07325fc42a7ee79c7c789a257e208dcf2c1a9bddb5d7ce3440b0a8eb63e9268b1622208934477305412741e97cd70c93c04b1dd9abb0f104d00ac16a7a3a166f646c4944956cb875df5c9266857afcab96a850d74a86864175cb26d2bc00973f10dca4d8a02e8f0de7b9d1964b77c26eb6302d288e199105c0d8a3027ec07ce0f88296d20adc6add1499ab6fe5598e558a288f48d938af3b37ef2d71801beb93f1cfdd9cd41536ed68ee722ff5cacb6d54f06f9ee494de6c22f8092df8ecc764e49c31d4ba3b499c427fb0ff2c2e80451cc1eb9037dab1ed63f388035dfc130907c2f4a396e002e57953d66b1589aa4549d07d0c75b27875704506d31c7e81b531ca0be45779c62e93e98c860f4b4e1506af99a754be618b64f602a7e01ceae6bb236ae04c7e6e489bff25e9eabeb5fcdd65eabc41fb252d71c20c9f49f4422789f226ed2afbdfde66dfce0a9ef248825f0bb4202f22ca0d17096d5bde85e70cfc1a013c949df8ba98341bd2c1ee5d65054d2856b74ee5bd138d9a81a0a0bf6e1add7091376580542790b7211b61320240a937af7e78a147802cd14e68e17261e72695102319cbafa5f986340631c78cc0988a15d6f10b7ee0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"42c507dafc47c8c2e9171e866f58665fcaa72d8be18ff09cc618a11b5652867f","proof":"e883f860e45015c6782269d08cdc85ad9559cf95931e64d7dcc1d05b64fc6d00a203654e32e0b5130a644f270e3b7227843c1a3a46fc1283ecf23af31a096b1a305f7e70fb6154cfb58f66efba79b7561ac9b607bce6a135a408d5b05ff0eb3a08b17ddbdce98c15ded678fd5df6fec4bec772272befea2211fe2905324b2e0289c067f138323fab5cd624492c4393de832b9ec0d6810f8361a69cc0f1416704627804d1261b4c3a6e61b325371c79bbe2d173c8bc3d4d209322c58d78e1e1097451985eeb41d0ad608f8834e35e63f0d1a10c113b40a317fe1df7170a1c640bce488214addcfbfb4d97b2d2217ed059c3922fcfa3acf76b66c2029f0d7cbf3cf867adb185440f38a924d960f396b212eb7275f6b404a4611854c81d4fb9c5109220f182d7f60db0aa19c7e3820f86180aaec828b39e1ce1cbe57304f890f47fda4e21fd0a217a1c867958a244830e69ccc2ec141e583129de3b3817651b6e32d4ff6a64d1aad3e279c66da29fb0672b03b2515ce83c9d088f8afae852555f499c7d4f795725f03eb62d0713cbc0a062f5928b291b379eaace3a7f19a9845e5192a62b89368602784fcf3997faa0417ca5361664805df1ec9c0af7b65feef85f04b81e9829fee263f29bed0c56e3c7f898692fd7c5b61ea086bf5f8fc8521d198082ebe3dec622a1b500723c5f0c6019652639f7649a62d0be214e54149e5629c8c093fac459e0fae56e9e5906656ab2e9c0a0e23dbda2205a9717691387782a76f0f33c10cb60e6a6d019bc3d8ac6f5e354f72f9abb41ab5af9b3a2c2ca315080e889175b22080a54b6ddc7d82ab7ebe5438af0c99faa5ee8d68906bccfa73dea7a3704d00dd509f7d0f70ffc32d7fbf025b47d5c08a5929e11edcfc2033a0c006862e89ba81953fa7e88b8c086a68786f0bae9b7b153ea9dfdc8a7da445800"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"80ea09e4987207abbd533af8bef2870f87de109cb2c8a3062d6d2f6aa89ac125","proof":"92abf454fb08601ca6626498ad9932cc926d2a5c731c48317e471df92d7f5f205ee2367570b6d669a4229530a121ceb77f452fae94b3ae238c83707c8c3b3d0eaec2018238b1983b53e3303435d4bb469b9653bf3a0c74b202f7b75a21d4a9417007f77c56d8f55343d22758d52a1338809177c83c6606fe74d0a239e3e37b283dcf9b4908941e92b92d22270b428a97a137a6af1f7c8944d5eb7c0277c84b0e22a9fe61d9f6d72e275d089f597cce07ae30e0ff4c9a77462be3e7b430fa9d055d95e67a9cb6013c4efbe5825f004b0a4982429869bfff6f488dd1148cd75305c03d2a3229c80d1a91d2b180ef73e34f3e26184452f2f2447d9795c85afb9624a48022f58a823eb0788a2b4f643734bde8cbd17e439ed62039d1aa31d13ae1056a9952a6fa4b9ba2f75cf184b3565435df04129a9578bb5922889c5571faba55f605a198cb32ed241ac30b7f1554f27499fec65a9e4ba93426c88830cc164d63147b4fc6e50404baefaf0f2b77bdf56be4b77cfe8986ceb5b04d8f01f063f61e28bf591170e1a5f4152280fce9b9b432a5443838306ccf53ee97387605299800761ab21c257c626c51bc5fddb3f674ee09e6c951d965cd0e52b4543f1769fb5ad41673d4be09ba4a6f751b2502afc876f6296119dfe834627a7510bd65322908926fdaa4c0caa4b0124b932ac484f44215abaa876b9d5657d476cb68545543216ece63e61582851bf9ae4fae085c357a8bb4a6b9b12e31d1de39b98568dc7a0ec40b2b3c56ae09a949a65cc8de4163d3f644403ceeb2ffed554a464b0a1cd36772d611b2f360a3b84d6f10c4a9f70f7a417b1298ea827d298e10f7882196f82932686e357e048356867185e0284b18552b7a375898a5adb3228bc762c16ec30de23e036d4a87f5806aa66c4bfd51f5ace967e6ff54635d1a596650bedd2f5800"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1e6ca63cf9f47726917520d34c89c1ba997370881f14983943946e71b67d7118","proof":"28775942211bcf9e86528c0513769b51c1c376df746307be75f7700e89cde86d46405546b1676d94553033e36f7a0f9fb42b53d78aa7024a5f82985ac985ea1cf0e9547c1bd7977e8dcdf358b7f6fc5380b93ec2b3d1af857fc238eaf64a8d535edc32862fed467bd6103233f97a7789d41788a12ee90ba31c4863bd4193fa02199d103754a288a42c56874f3f80077ffed7b4203831e302b2263602a52eea0c4056de9528acb2d04897921de06e7d2f016821948228b93fb8ea8b3aa581830e952b80903df52c68e4e5b995b90f4e9a19977edb32911693f5abe6ad68ad840b96c91f78549b94efdeb391835d822cb526568096b566f35f05fe081e7bb0ae1b34df5aedb65f5149e1abb456207e4fef4cccfbf88e2bc1fe5706dd473ca0ed1c8cce7a85d84cda93387704d5d9597742692a5425ca7141576140d04d916e226dee25381290b4a6590ad3991d770fffb746a2bd224af7ca0038d7c3dc5cce1c792a0fb72fd044ff988abbf735dbe0739703fb5216d8a985d731982c13d29f4b3f567a1f800ec424afbabdcbc2b5e6bd716089b4d867be5183a7de8f90bc4954036c45e800bc1d6092361286d49db574b4c1299f2c8ee8666feea1546267d92d3d28ffd30f29993b75d03a7a59ef2972472a3a9bb920795f4580aa9c609b685852e257664c7ed65f90c296cca9fe6ab0a9a6b394e3b9125e56a01efb369defa5506acbe69f12839007dc853140e97e4a22c2c720ea64bc443488493507bb79b55a3e4710a4b51c54562de99fa31f1b7dc2a08f91b3df5012a3916d86a7306c5a09469d82284d20cefdeccdcc4406393b76b17eeee37ab565f7bd92bcc4dff88246ff08b19589b8ce081cf5a16fb309833c5b5b36160503f2d11bc2aba0858e4003e24cd4a6f4e53f491721ba6077f6c85f0f527ef4db934adb4093fadf6122960d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"84aefae97227def114b314cd8a54cf595ce7c6a711cf917d6877a243de38120a","proof":"56164e7d8977c64b63f089b1cb2bfb76b1cad7d94c2d496db1c8a578e3f56a728e1c7a1076908417247758cd3dfd63488cd5500d562b3e7b5f04e6dd20a7984a4c6794e4bcc613257aa2802d20f1850981fe33482cc8f862f215d672335086454ca687ac27738044fdeeff60f83f85d37adcd5561344068b5f9e8466781d5e57d843a4d1a3cebc29050f993094dc0b928dce08e82f4b93c2d8b600faed5498056661f8b6030657276835497433defa920023ef6701fdcf6a46fd7a958e2d6305181b9c1e62c90c878fa7af5cca93e11a0cd826148976bc7c58eaa7e78e73530ad40001f1791e92666065c49a438b2cbfbc18ac79c8a5098a23c84bcbf03df937644dc48775c12dc8444bd3338b2f07f92386e586d9105c04c625900e244ee01f62239e6cbf6b145f1a39a77c9159fddc500a5d85181570c63a027c0e452dec00503fdf8b47932071117b0991093828b4fc6276e3b4775a87f7e51a86b0e99c5ffa24697d8efb8fdd0406c57489bb8c26ceb6f05d791e6967aaedc2fa1ab5a21ae0914e3c89304bea44cc8c0157533ca19c7767397eefd0b0de1b8979b15c2956c28cd1fac4efa7df74ea5a65dd965c831acabdebf2399e75e061ce940938a10928e0115e88d9588218430f76365bba0e90b4691464af6d5869bd89c0f8272944a63595e9e1d44e96081a6fe6238bb1c1cde1be5adbedfb47c9b5125c7105b66f882375fe8cd7e3b1360137ed709c0d0f3beeba60ceadb5bbf66bdfcdc8afc66608d4a6258bcf7f40a6087ab6987b39e6a51cb74ba87cab10b17f8087bd4591457c31ffed93d5b70d1474b8512bae2abac3590a70f263dbc38d1dcfe3907c7250f99cc9492e87d830a44416c12493766f76d21ea8bffbdc74adaea2c31eb073080027f613982dc795af4b35b3b0849a2b09dc8447e95f1efdad7f2df979efd50b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"32b880ef279aebd7eee4001388268ff3202a2549961a9fb68a9fe704acbf4c20","proof":"4a101898712a7d2a99c78f508cf09b08e345de5ae59d4e09ecd5565967a5e402e8f85870b98dcac5c690a5ced6f25cdaeba16dd5cd3207349b63707fd4faff56d2a6a95eab2de713662aa58d851928c686f4141c333969d6708498d058ed7625024a1ad5fcc45d6cfb427c46a06edfdeaa6ba3e494f3a1dccf2d502b0b71fb0086f9a24ef55a6b1db4d9f531497b3d9b363faf2ece84f232598975ea793e620fe031e0487e5af171c6e1173487a3ece73405e8ae882e46d7f3546771973a4601b97406cb22ba3a13238d4dbcd8c532b04f62e2711796ec08e3136f20c795d30772509028b25e34e876dee81558bb7621ff57e9364be1838ba70319e34bc51f04d2b9d2d58e128a41f43bbe1c2b83bc97302136a737c90159173b3b0567ed89260e24a10d9ca5cab240df20702cab00a3850cdf95e76c6dfce63a546b0b994466d0e1baf7d3437809010106d6c19349b50016cac2d1f2db21fceb003fd17f6214d8238b1dc339f9bc58805a1ab35f23d8e103922d7f7038d22557ae618f6e6471105744c6f88665e3da4cff3f6c1e91f705864e803153d2ba9f0adc7986408f30ee640c984e99c2efce6d5a58a2e4d713150604f43cb6c712cd21bac1de769a634c3877ae4b212d6c6f85601ca3a81bdc9f758f7b025cb35e852af6560a43ba034aab365eab33a1188d45019eb61bd925623b6eb2984b73defa2e0ce12b07d568924b9e63ddd61e4e6e7d484dad10332585ad1d24dcabc14fd99052c434fdb42644bced3df5f15749509261cad55d364e16c37d2a8cc58875160f807c3321ea29c46a817c04229d752b6c8903553a59e5295915054201b35949873a927c5ba61800780436cf8cff97526dcedc636e02455a7f4396a9034e3fc7bd438f1ffbb20f9a89f728df8a571889cc35b54da925a525edac7ad5b0c52c0c9a3c834262a500"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"34f18aa11bfb529c0f9c82140a286e434b2d90b9854ba4b86a7ce0f26a322f42","proof":"d49932a1b8af6b0f61723e04457dd82fc797f9af5f9cc059a3b823858f6b86333c170bb1adcdbf85ee6c552501c413b3f7a8390e23c3e1a7840f873a72ce192b88dd673b4e08d430890f289403944f482b4ab7769d65c67a2a6f77344ec8594986dc674a37dca8cc54ace8d1f25bb03b735181acaff6cd697c40ab1392e2096f8d59c04f63bbe1f0b88e74bd2394502149716eed5f7721bd0690fa4a3428d60cc95867ae4621ffaea9984c1108e1695aea7977f4b3f830537c0ef60da1be9408f14795ff237aba5b940d957e4c042b90aa70dc56d1c01a3532436f7241ecb40f3a22eddb88fc58ada99a94faf59117b426a47c17b7bd31a7817770c8c21b2b3440d013d9ced575d922cd181d72390e4f7f9199d2727a6394dee7729555f47f7794184bdd438ea86c142e91f8416acfe440f8bf24871a05bdf0ef96c907e82e2a022f312dc354271af8ea997855cd7b8f003e0cc8c104bc0f345787d072bac23b90076bb19e90c304a36abc8493756cac54b9bb9f7555b6c870c215fd6079b86ec4edaf5ebf4b44546e3be88bdfdea5500eedb6bef416d7cacb1bc2a3f56e2e4ec22184f157a52754ab24e745a7f0ace196664824a08f2badbc9ad2c0d5dcbb264c19605d78a41142549331e210f732d3d8fdcce354f99bf555e4b806bbc46535c2a42938ce6474c63cbe5599e4ff9a7c56a30354a8b09b8082ad6c3eff7fe45274b10cb0d9158bcc65f1e542b4ea9369bbed170b9be6f02e67e0b60d4e24ed55debca510b82f81f43f08deb4d2510805c681a4bb6110fd69aeb101bb3d27623114c2e405ffcd8c18f8890d3015ac48d8fb22eac62438a3ea64378fe9070b387cae103086550066ceec99d19422056dd540ead26012fbe11fa22eaf080ffe2d0175c4eb55427a6d4373fbb8d9d8080bfb5597bbca189467f89d966d65117cc60a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"00ddc4c07f2727fa6b403c07ed37129cdb5ffbcba3cd82bba923b55d3e7c8f52","proof":"ca5fb53091a5140eedd76f24c9d35d5370357e1077b9e0e3819fb750be20e9236ab6d9b7d49bbaffdc4b5170c65e7d50de4a81c0a0ca4438818277d23cf4c35f120fdc94d84bb9496c8286975f6750f7c33416f24f3a760f4853a11c3a50066e5a44aaa977a3d038b0b230c851e4f54b8bb42ff1ed21982855875813f01d7079310301dca0c2fe061c1c13cda88014b16127b4526887f3e49c5c6b4ae464c40194ed4bfbe4014a743f16207b2a5793933d014fb53e53f59e660555d9d9d02504ba679232a6007d4324aec6e94f3c8eb0f9df3b83d464b0610d8cdb137b3caa0bf0849f322a3d4ea5fcd2f63f4ae3abaa31c4996d97bf0ff0dafe4a93b50531249638a527a46db27d014e91615288c352324669a860943c62a4ebe72847ac440144fe98f6f6b4c270068403d3e6f1bb841c32a229bf3f90365b410e517635f747f25d7ab746e73dec1225bc94ace637e3fa1a75fa9073cf72ecfdcffeaae44613ee4a0e3215d6513ce86372eb761e485f690ba745023643cd100cf5882bcbf7673269dbbd70d02a30428a0690aa4d15c037de11013278d4b7529b7e61d311457e0080c9b22381893abe00461f7e36fdacf5fffa09b3259c27cbe7c3aa46c9877ba0d989c8126a2261b5fd506f0d5a93ccff3a0cbcaa5b18a93b9843c2aefe196be0cbb81ae4cf474e0881835eae95abf8076cc10216625c96b5a4bdbc9d9bef5ed802bf22137b0d7cf0e36e3e1b24123d95d1a003bb49d40bdb7d77bd43762f78de6be10c0fad94cae15741e5187eab4e8b6d8f54024cf264ce360b00329360625aa05c1d72371d66f3167d410d68147f0dd076a4e0258afd9fe5fb2c4f6613505fbdad4b8f8f5730d45568a024a8165807ae61bbe344469f9ba506c07037ab0869133db7a8862712620565720160a1ad11a662978c8f4e0ba58865c9ea708d0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2a35bd1428217136822be919d0cae02c7c083629cfc7f755b30b5ae55038c949","proof":"d2fb525c269eae5026a88a7ca0d9f511bf5e2b16305c72bddd3d86820002165eaa8458b129173c52e5338b8e64a4e2a9463bbf596bcf244b23bbbf122bf18b01b20ec72dff316308319270a130edc10c7779704880c928d26cc12ce713b80317fe2a1672b27d483694c5d0dfef50b9db03a0bf350ccb1d811f1a08fb245bf448cf7d972ae7e8514aeafcf3fbfdf0562b5d09bd107adf393a116eee6636c3b006c11c160fa798241f83bb121aa0d0d893ba6bfa68a76213fc8f672ca0885f640d489103eaa972e250ff8cb18c6ec512c4755e3d135f73eab07a46cfa76f1e57074a847a4728bfa0799e7f44dd4c332073f31b274cce55cc32c1a98f7cf7bbe84c9818d9b5da3463d0591256fa1746cd76c63f47b8e32d8b63b0cd843b33a4f00844659b3f97cdec905b250362f9c7c42051ce30be549f09192e99bc9fdac8db2c92cf2b56fc1c01ff94ba75cd397bd5e0e2f507e6405119964d006dc4965c516a12635a314ba7cfa0b584e9ddaf1fec01bcfe5c29818a7580814cc07301fbb00756af23d43a85034e5b71e85cf1ee6f22fbf96e7cdb0667c408b97f5cafa2ef180aff61817467457e00546c5b1a9f9fcfe359a619cd3176a815621f59188dc915923c9e1030221fb3aae7e2cad0e8ddbc326f090b9c7af1b287cd086209cdab47689b890b328774f082600ae208c063bb3e15363d4acb99ac276611a58b3bd67be8e7c3da70316c6daf4e9a15ba007d49473d85437a2ebc3ef4e0845f00dabe1c285e6e41588c6ae3d985f67a5876b6e3bf55b134426a2ae02cda61e307beb05b56c2232a743b566d74d85f05c557d5bbf10e8cc982688d09375ffd491f87a233ea6c6efec8df3a5dcd4d1a00f2a5a8ae3755e5d34d60699c82e3841cf24131021abb66a818e875b34a83cfccfb8697782dd98b6f32c67daecbb10552e853fc0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1cb2c6ceac7eb018848e1f439a0c69f51ae7c18c624f6424df17eee0527ebc13","proof":"1c51a63190d1b9a196380c29476e7cad6d144c1462e986525e78f1271ccabd11f67b904c88a24ecd9d7652a4ac9aaa948cf95c72de5c321ed85cadaf97553772e6459470aaff9e44a2688524bc67aa6e4ac80ab6a5a013f4189ef45398c39d71203a71d6d14c530177b6e42a8fea78ee0a839d44c2fa7faebff0fb4b2048b504cfe5e97a504f218308f2de4360d9611597a454766f5b95d59ec767227fd48a0a55d28fd320cc7e0f0eea5aa5f94df644d3c8e031b69328228e3dc7f0d60d8e0f4e157603373e1f73002d44105afecf6d40f08d16a9276f648a454f5aa05eed04c6e7e1f04270457033ce649b8121c0af4fee836b6bd4a2e86066dc92e54fd4612656c51cc5ea21c52809d148cd7958a000eb20515aea0acef8e2c0a7be419d789c2d32d71fd9d2844fb5d02350a5e6581edcd85402beebdfd205a06f95d5c503ca3c881dd51410ea3b38ec980759c0fc9c082dc283a00e4f63405864fa74fc12a4411846de2f1573c97e52ac33b2c6661c5e83ae322ef8258e93047561431e1c4a4d781bf4c5de43e3cfaf1e69dd2c1ea21d8079a3ea370182df81793ddd837ba292a6ea060b3d98ff861b56f8634ad00d384d4c958e16c79da2b8a1de99843736a65efa016fa9103239e42e92b3e946b338e7a73bf8f0821d675aa196a7545288e8d075b823ea7da5106aa1a597de09b32f337afc840bc514387800f0eb3f783e4a6365d40e957ac6d22bd72ea5b2cc769a20690459dbcdc09545401a91f504622dded83ee1564695d36130297eb764db876fecf2dc96a9dd88e0800005123d26c5d6f17eb7e130877cdf69dd70b2d23d09597c5545c6a0cb796c1b7d1ccb45327f6af18019f7bd1c475933b6e1c30636034c14729b952678d12f31d6c35f0faba85a87b700044b4f117746da70c5b16afa95a2c5fe74a199634acef5ef5f03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cc9849353cf13767ebe26f0cd5b0202354b9c8ed0851965965c937af01384904","proof":"b21f997377048d8aed3f99d3c9ba4c08039c4b888ae858c0eef8a329d739f473066fc0d78c2a62fc5442deb36ae24bf79a8b68a7a70db7e6456bcb3182580930da955628183cd3cfa49552c4398f6ccba759f252601a6194b8ecf17201b4553726831a719eed76a663a9235e5256f5cfb5b3a6cce971c1332d6fa68deb78c47b60eb06799af665e6681706031be32cd621f8e2b2141c158483c46953716fd107de3bcc112dac54d8a715b8e2c645fd046f59b3d7b76d5639dbe3c83520dce305bf66706459bcc77bb0f24d860befe23e62987bb2722ae406af473c6516d90d00ace4fb12d187229621bdce8d062084223f44f43a1748c232ce3876eb554cd80c4ec5ec4c06b75c0e1f9a5a8600ff52ef0ca6ed9e69e4678b17274dcfe2d9a43e0601353bac259ecbb6fc8fa8880323ac3cb74139a7f2c9d933b2e47c00fd1103d285148ebfdcd1d63f4e6201ad6cbadd7e75259f09c088c18e49b7896fe8de5766a19ed691b300fd92c7275ca54f07e76878fb5174ba4b85478fb45905bfa14b08d449f62b0d21fbfdb911bfe429cd8580fa0340fa0cb374731857b7a6da86582093b8b7fd8cd0ee2c2446cb6d6ca7bcef35cab1a0d0c75fc371fd419454d90e42a301307fc7af3986db56f60c691819633bc9941c2decbc7dc430b3339fcc564ae8ceb06a08b01e471a8f9822c6db5f32bce5312fcd7c9d3f22b6370c0de3413036d5e6c627d9318bbd708d871aca008c72d7ee9e46c708ba439036c931d37d34577ac5b793d06ccfe857df096ddad47c3a1a44b94543564e17f0d986be507bf03eb7d108b097302d0d2170058dc14d7de86dfde4071b88732325035ab9276038e92daf70af2e8b92dbd48024c7bc8eb10a0bb9593cebd89534c2181298f70cf90a9bfd75d6a442d3ca0a9bafb8f8fae4c60d0024b8356d7f36c86d31aa930a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"faa66e01115c36bb5a2b8042cfd8be9421144c85efc6ec82c3990c688588eb6b","proof":"6ca97ef520731730335f7c38772efe4837811312d4bb315050336cff3e5e1a5dbc93b18b0ccc1415f6cd1b075a360a31121f28fcb4169845f5850c1733a29b01e616392083602412843a0fa66bc699e59b75a56fd02e2771e4c32acf0ff6d4592c0a4d596556e92f29e76a44389d1b66ce168ec699106271a041d17d1b556b5311ac0c0fb14490742c31a0265bac547ab51ea112e3e08b315bb65d9b3533e10fce32225b7683f64b494679554c02c619a6c1abf00b3061c8a1aaeb974d2f45056642a809b48306c00c4933ead8cccb3f5ab2d1b5fa06028088a129d20a4f7900e2ab2ca5f01b9415dd6d387fafd24ac8f2a2e3b9ab91a03dccc971f8f4b7c53df65383347f009e70b5d1bbe8a14ed4ccc188d3455ae672a435113147cdd62239cc00c25af4463cab9d241daff07941dd6e3704f02fa11737fad5f7bbc033cb54d69ff91c97529e33a0051d339a7d0534b4990ac4791852fdc7313915f6f87c3b20dfd2a268d2cd4b12976ab5a47216651de408b2284bb98e98ce0f38771bce2944a68c5a51e3dd341be76795494c9142d17f719e675822a22cedbe4502ad6f4964d6339ca202d464f7170ed23b4decb94c65e86cfb44984ad09d0340dc44ba6a72e6c5b733ffc0399b08574cf61c67e746220856579f6f2188e9a2aa738c56649eaaeaa83ad53711ad1d85e14ccc5cc629fcd1f0f7b8a709adda7f868c8ab126fa11f9524d436bc9980d92adfdb42a4b89d7766bcd50395d66b8e3971553003a625c2413367d70a391a6a1b6b26f5a0c68c1b058c077b4fcc919e2eb55e1cc3002cc93874a8ace21b7a4dc5a5fe1878f185ac767a2111a55613253b9ba9cc84434a0169f7f23b9e4ad1e29019e9466a59e7fe14cdb512fd7abdc2de6c1f2a30da62005352e160ccf97b9e0318e79e7f2ed4c32b5762f204d86bb8e526648f10f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c804eecb244d743fcc71b38bd53d0abb0eddf8b8ba2d8a8493fafec3e18a494b","proof":"00556b8f814b67dcf1bce6f0a3116c628e59a5ed4532e11c95ec3a53cc64d83bd81d9b070a87a77e0eb2435697a937f49d52088029bf1561f0546f1da0acce0342c8cfa9c99eec0e30bf6df1da92e59dfcb65a3658f076b462a75e3ac678c028b47f39cb2118fa03cd22128ed0e01a53027c2635f4174f0cf3ceeab2f636be300d78a7d256b3272e11d362e7fb8f4d081924a769a1225a3530c2d76bc9c78d099d80e65d26d12eadae25101d530cec4ded945cdc61c8c396a9529e748a41390ebf62a355e6ca03cecc0d326fd2e68acce0602d61249817d1c624a71f3fca670eb84ff7a1e32d4e8ce215b56315cc7ab5bbef4bac4d7b0575494d54d6676a9b0eaef92129027709f4292239591454b4f21f29516a6854e13b5b67f25abe2a4931d896dee3f58d2f936097fbe5222b52cdfc3ecb5d6bc19ac4b951a9fbc8b1b61c3a620a83f997b49d04db414eae48424e090b5c81d3a13d8a08edf8d5cc21ef3202df715ed531ed0170fac861616001f417e50316e81e0771657dd62878da7703fa660484ee020fddc9fd92e53e8534c67a3fa21be5b3d32b3ad05d454c3ef370ce7e8a8c4695dda731facc0d94825ff22953afaedda9776234ed341e21097b1b348f353cc535cdb55cc575abf04f72a44afa58cf09fd09e7cf456f1da73b1236a45986933a5389d36c337b0820580d53e3bf7e118734f623866e7f124742da69f084fe888a7c84999a870779ef598354183510126c4a89ec6aa19b685482ae060c3b8cce0f58ff03cd95cb95cf2f8f40b5fa5e788014403ffcd78b752392fa19ae743753d818be23e2b8c6f30e50ec8fb181a09bdd6f36c8f63d7634915c167d7cb9b9af9aed9333db6a782d1b419b0e887ee5850391188b05c59de9113e8d0bd04866cc32adab599d4416cdd6a73bb898eb94ef501aeb408a425cbf0fcf2d05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5a5713340049d88979e8419c8a7d9f5df0a6e451caeaf571e75724a5620e2844","proof":"0ca125c1423098d37830f88a1a406a067c09718945f5d1c0cd215f9733860d425c30afaaedeee5a8214078352e27c7206347e3d6ebf378aa14f221c7073a7d641a893d0de89320634bfaac6ea486952835bacd9cf887e7b6b3a764f63a8038152ec2622a164c80d887303cce12c27a003abaa22106f798b29ec39e6028a2773307766ad4db003f2cc5595826dabe0d21bc2a976d55853fb97564f54368004e03842f9eabaf170ed4d1c8fdbb2128910da3e9c0aaa0d3bbed9d4b8c60ca51680b0950693a3f808c495513cd0d1dba1494054b1e1cee9efd62480b389eee6024039680475db3ad1359a8452b09d63ee9f38532544fc59d1f3d22de28b4fb75bb5dba58085cf0f2e0b9b909e341a68c8c71510f20cd3c2b511bfc47e916140e331e42974d5038a33e759c09e87041240e4a1fc3d32ee12ec2137be01d47ebabd16f32b62a541420bba7e5eb5e3e99b291182bcc06569b956cbb08450e0c4967a806809eb92bdbdbba83564a6ea88c8cb3bf80e3cb29dfdb83cfd7fc4e9c78eb97202e2c86b8f8169aeffdcdc6216b1b4f77615a48231bac07eeaecbbe5f3d6f3857da43bc15356262f73bfa9c109e94a75dbc4016564a404bae6803a647b06ca02f58e3980e1c38cbd1500706b3588ca599d4335af3de3250bede647365aed4600f3e4379823111f3826ba9236bbf66abe8f4cab7574f753b819f2707d7d6f539157639e78e217083b2b3faa34f6c5b6218ea4863d63659cf040d74ec459dff49281e7d4d398129260d4b5e7e8c7c024c55ae6639830a8dca12f6fba4693a967a6e803f4c600756ebdbe17129cc64fa3142be1c16cd96610718440233b56b0c5b1dfac4ea03bdce3a94c9ea3030605a413795876683d4672d4bda3e402fd6321c063e2fda0fe81ecbc47afb2112d59d96961e3e2dcd3864f5f1e8238757878f290a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"deb531b1d1dbc85c955ec2e511dba4699e183f5e2ba57ae8bc8957f0316a653a","proof":"305d3036e6c7f5992f83da7e6a2adadd01c14f0e6dba93bd0a65372c7c81ce660a83492e78f22804ba3321954b789fcdd0301315f2078d61808d75830a1696149484a04be6f0a32347731aba9e1ebe0b4d3287f55d596668811732f8f4e6a7350627205e5e08d366f4d8c37fe1a71d528179c990b2ddbfa37c353cc704ab4f1e46f44ee8383ec344469f706e8d20b7b5c52e3e58a9034fb4abf3d2222c310f024effa98f522f2135f7a9c38eae3e904c97285b9e812c96e9fcd477b7b7ff7103e3a9c0a60bdf757e6f64ea94a7e1cc29a58b813ac87fd69d3fca870ece9a0d0f1a58b390e042d376b999a7eda7cb9865d81c1337f44cbaed98ebdd826cd63206bcb9ca789c1d6e612321dee2757f74db076628ca9f220a5f9076a709f5ce061a00e3eba4f9f22cde50f0e71365281c109d6c649e2e8cb72723cf1cbfd6efb769fe50cd537fd3a5e015b9f83ccd24d751f3ba18cb03e46c44e0abb411735a973798fbaae18c91e00ae94a938e88f0938942a061f5c452bbff38b013dfc83b5274dac8c50742762a1cb019fafe88dd03e9fbaa0bde8912791b33cac73ef3377b0e4cba52a6319ae5ae71a85cdea3821bee6d7fc317bf1be6e7908d5568f7172b4d467b5fe517192c52730c573c7e65723915287ae212f29f5f1ce1efe80a6e68147277eb0b722347d25f159f53464fc93fe199dc03912259bb82de17de9f641d1f4c5d4f9ba54d4674b66e243ae2d2008002d53eba6a443254ea6eaed4a540d7302405cbb0f6a74d89ef6340821e2780b2eb1b38bfd845d655a6e69476ea1e697ba84745a2615d0f5534fa7571df4adc802517b2693cd50fa6b5db6eadedf5874a1454414fbe8fe3bcc962142bfd2f5538e900946375d6d338cc5d79393930a30adf4c48de396005b060af10f5bc7bb0ef7dc392216c7dbca9fcb44a824f25f60f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"902f1664e418f3e18a8e524d5dfea5c2f42f603ebf5818737ad769d4e50b045c","proof":"385476ee0822bc64c1677a6859eb4330c4868830da18677cf4222dbb436c0247e664c5bf65bbf7ead14796f165c6836ae3e411b363641119c9904518f16c8d66c0ea752781b6023768c74d56811b8a58f7d3714cacd660ca3659be815f2dbd17a69845d33a6e30d8e0ec83da42a4ba749f5c368ba9de7456a4113b65ebde9a52f10fa1ba464ee94a85de670fa24944d3de9d3f79e099badcd62b049cf6a1730a62bab34b27841a702a826a9c889f41b61784941989dd90445a62b171d83bc302159d33760e777a69b5b2083e059ea307d489a05d2da1d77ff80ce5568c1d6a0a3c74c35c6f5997b7f6e86e480ed28dde0128ab0a232420b3af9f934114769224c4cd7d1afa495dc864901f34228c1052130200e30621b8d9f8bdad34bda7566b04035bff96d7bd37f06e8af762382e146e151e240d39b3dd0f5f516bb67fc87f0069d8d8d0f00a6eecf832101df3537797144404e3d18736b1878b0b2259fa32ca300b9bd54de06f206a170615b46dfc236166eb4b00cd6ddac49750f4781559689b867b99c19bd2977ce0a2f328c0d7dfc75ba84e5c75ea72eabfe6fbe6d2103e0fa5d4ef18884f62728106b88056ec6f94e5ebd937d34e772ffb0b52417d192a3a11b5aa55a007ba81a4d6a50a4d97a9855657ab74b3a0a031ef3295b1cc5a7047eed1c2560aa3839875bc38ddc77f5b35201098c1e7fd0282d4861d528576843d20d4958dd332a39003bf2e662dd1e4256b8fd9143db0fe1253048e717763f052c780c79ecc0c87322d3aa1511f0c918e2a15a9cbf026b299f2cdac2c095c68e88f645ea5cdeb7fee6bfa9b2cfb13109942d0120a1cce34f46ca72d39576eba16c43a2fce93154aa11d73d7f7f2440be5a26f3ccf6eedef32e00b97bfec017d0a6a74b59cc248607b058b1f73739b515a2db4e7d658442324cea4972f1405"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"681143569df171f653dddcbcb448a86ac22436fbe0bd07a88978e0b332e66372","proof":"3ac2561ce8d1e08c60ae1abbfc48b1e6ec973dce05c74a33f9e2cb44c7e7377c946a6bdabc437ca727cc81f7e13ed2c97973875f2b3bfeed30f6ab697aa5ff11f46e5c2da178207f7b0869724bcfcd864f5a3658efe48d5ed70d17c2ca879d4fa66cb5bce4f6b5a543c93a6f53142a561dbebad4653b749e45048b965a9b1b0f8f72bc626fe9d7cf2a3919a303b1b529c20f55caadf9ff9750d58974e8ca9a0be07bc3bb6433ff17736cde3a2914863286d87958dc9b01857a20e24ae364b70adce5d27d5002255c508bf68abc480250fe49241acaf3bc245a91f8ec76cfb90fd4d567a915bd36892053d4c669db49d022c0f3a53357e067118cfa15ed100a01a464355a8290a1dbc6f493cb3d45d7bbc5a2d1c5bf1be7a1fa05250725b7b855005eb81d80331a9089496dac23447b299e27f3b036f93491fe055467a880ec3a3c8670ef67fc72f62f6df31b923769334a0fb36e33a1508834e3012b8de4b430de3ba9dca13802065cc21fdc1a238ee146b105f30aeb6bdd6aa3dfe4e9bcfa4cdc72a0c2a33549309aa0b0144f0a5eea0a48a73dd43b80414cbc363853549a4b62ea0e0fbf164f726a1423e168cc69dd91f4aa327fe621b8cc4c075dea0bb92134fd0e93ddc4c05b8dc95edbaf778caeb95337f68adf749d959e37077c99be19da2692945d77570833fc6fb62f258086c6b8718d11c356da68754072f2e0b858c442cb0490bc69cbcbc32ef7159bedb2d3d852243cc62b5b049af090162dd50a80d0f794982007236e5757a395b1dee5267d06f3513bcb9aa710f3eb4edc3d266e8668c0f53c053c6448ad89ad478865904debae1b80e861fc7212d82e67fe2ea115a1e38532f83c60f242656aeba643b202f0eda97981cb2835d6bde970c806db9716fc2c32bab2131e37d8af3ce816bfa01b994e3cbedb17f32e8bc4ac8102"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"96440c5222ffedd1a8a4d5024e04df850d79329282693648d4dec00617e0e619","proof":"180ee602bb79976ed83c17c1356d2c7d00381e705baf333094b7601f77b885197650ba26efe1d137332dbf58a52700eb73647d95b59f3882554ceccfebd1d7753e37e0bf50c12cda2184772864a2617bdaea05e3a0813d11ce6c920f169c7e0d4aa2b2d387ca864ee8cd4d60809c6fa31913b091b4d8d271d5d6f753cd7244765a1b2f288076197ee8f6bce2d6f7cbf9f4ca9a44542bdae921b8dc70325c80078026c0c3cf2185ffb3c1229461b0c72c4970305372266d3e8f5ffd78383d5e0055e677e6a30e82a5061528386e6fed8e385027b6c0b804224189afddcfdad00e628442bc0e4c63e80cdcb7cd52d0f4dbcbd22d00c9c7915e54e7ba4264c89960c0b2c956f944321d5d40b7756994b2bd4329248641d9b6a3f97585f2be33792b6a98a1ffaef6f648909fffa82616807335433e92077e70055bc5b1f7405cf9256092c43d7f5cb8da40510c567398004b4d2d199e2a484d1612463ed2b913527c9a2469e4203b223f0d40897bf456d44e636f8281e778f38aa316cb54f5140d604a11547ff2e232b73a5c71781a213830ecfcc15b0a2a91567aad8bcb40d02d11602acbc3b9f9511c04033026d87e57e8eec3e894889ca50442692634deb4be6da0a25760be1a280b9f958106792b877bd6ce53e5db1d2ed665fab47a5b897932764aeceaa84fbfb37c64c7d039f73c0ba87343dee73ca73f08553da6a860a8035a2a616d0ffecb4f5dc5c900f46ce8a158609963cabdae29363f63deb4acff5ac0d1e6473fd546abc5486a7bfa627748a2bc9752856d1c55d0fd19600e4839537ab11f3134818b7513520262a52d3907e63b7402a4921cae129c3ac00918841d0d93e30b0b898aec433cdc95e0e5d7ee0b81f5ecc5e203111fbfa5cda954120ad9144ee9ce556e2a805469191ecb3d6e13e2fc5ac31b790c2551f4fb8ed29607"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"00e7bde84df1ded2e32c20875503f779069cdae5c9c33830594c5eea0c583b3f","proof":"946d9029509bf64c547bdd54dfa2ff155c52443ad33d87882255a3b3994c081874a50260f724b06a948ecfec9cb90708ccbc4f05ecd9fae7bbbdde15598bee785c5ac762bad270d0380ce6a64813fbb3ac7bb5bf4eb01a3a46aff3fe6fd54e0bda70a77a0037897c4b40f86cd30139b26d67af9fef888532b99ea2cbb97d1458057efc4b3bbc6372188311b0a84f1d82f0a7470bb6fd5c1653ef2a87b7e51803958e2d8e005b2933549f6ecff47f08bf71900d31d34f2622bc6987568c9aae03647a95d5950f44fa04114303a57540383998b625404b23230714f59c5b18fa024a5d2365e360aa14f4802ce52f9bda4cec4b6311e0612589f3d0b0634697186bb87cdfd2c8b458a5f18490438b7552e76bb8966e81974063ae402532a01a23343a38cf59547fd54bf857236699ab3705cef2db6602a41441539a84db77df0c7702d74ea9fe92cac6fc7151eebcf3cafeb79dc199d4be9c9d4ab52b32fb9d3d439af59296417ac576c3f4d7000dc02945c17305a90949677d2fe70718da0e423d24eda345ed72b46f8cc27c16c595df6cd300b9cae7edc13b60765cbbc1449d26b61c90f22de2641089d90f3431689e3caaa234b02da117baf3c779ebbfad6c29543c191c0d635af9866d5d877029e957b485d85e74835386f57139b059626c22182fc65fa314cffa0022199964f2d743c45c2c080b497185aa5ef3d683d76a2d82d73c11840fce7903cc9a3e58e614702a0340b304a8975da8277100cdc4f224403b676726bcd2335862a0e10d7203a550ff0c009200bdd5e8eb0145b7cd5d03a89765d8aca6fa291e13470f7e671247be094240feea82214fd55f9856148630d9b32af2c00d1a8bb1e48373456f89110949d105ccb91e79a9fe220876060e099b65a4f3295568b1206943b77deb2e2a84b3b84e5942bd498d9c0fb943114e07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bc5c207a96d2441932af116376be25eb428f2310268fbdd34e5bbe2bae0ec06c","proof":"92710c0c3ea92b428f54569af6d01996226bbeff20f52a59dc90cf7ccb3c2971c659037a24cccc36a1674976b57fb07a0f3b1d8c8f444873151310f8cd6a5928f2b048e1f975e7601cfbe0df8ef177021e918788eab290cb6cceaa7224388c2e26be1ac5546d8df6db6b7584ebdb58704cd198525ce62c2bd92bc859b67d0373b0555d644a745a19aa2d3af9cd8375077db9fac48ee112a3633aaf5ea723cd00462241a38973042f3e8075a860ae8ad14cb5b4165679d1878884d41e6316090b96cf7cf529ef47d2486f2539c89d44a69fb272adc223ea904ac2c5b8353a3700c6ab5436eb6b943f8cb13afb0577b43c8e6bfd5d2bf882ac913ef023b8521434204a3a24c12d8b1491e896e5645a45203ffb8338e7cf10ac557da0e108c6bb7d5c93d6249149d02d198d7ad449453b0392a56ba3293af88c1f2a50ba0224f77f8a838a111555363b51f7295b3dccba4b1abc018ff031e5d3a3d11beb8788b72db043d1f61d1e344bf64e52847c53d57624669a2facf1f1721b9048cbdfd4d71204bb3fad716c3f116671bc3b8cb76a8a93cf8c97b810312b6002667690a91e7810822242037343db2faccc4f6cc87489f770ab7c83b9ae79c26f5c85a9762f7a20a72401976b6c4591d31a0acb3386f45d62eb8e65f7c81d54f6bfcc53b38e4806ee1c39967fbf599a5ac2521c84e6f7ef0a61ccd142b9154ee67c3f3cc08708f4ab70b9b43fe0eca73b535c6255a8241d9426573d57ba5dd1078dd3ec77882556f1b808a2dc367b7594389cc6a186f715d0318ee60a701b6b7a3bfe2baa333efecf621800eb0b6003874cfb7128530fa60f4d72325f2999c9d789ed6557e33ac4eb2f643b5ad46f08217eb672972ae61eff8b36a6d712bb139811b6fbb0ac08aa1a48eaa4d073459b968f32dfdcbc0b91d6cfedcd6ec221de7aff5011b4c301"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"72ec912335bac10b8ee20b2bfb5034a0895e6935fbe068300753941ae73f0379","proof":"5aece919e29bbf75076ce60d64f0088e2dfcc3dc06924d330cd21ab46602bb4836dff8ab28328386e3b23dfb2ca13211f273214447430283b658dde67451d83dc4d39f267d5a8cd137aa2a9a1a5af1bbd1c9573f281e61c29384b1fc7175037320c46ab9fe5e103e94b6152b601703d77987cf6f108bb9a9c1935eddd85ecc4832307e99fa900cc3945a291e516ae045d1c924957558c12d59280a78dd561307ec43364ff278d1bc98297a498b35142d8761c5c55d48c2815955c20b6c44e601561dfeebbb6f4e5249ac8087b0481f84e3eac3e2d12950c0e3c7d3aad2b70e0a0c9adcef1159cbb0925ecd4b5202fcf41a41af90770471eab9c0470ba377ac5ace1b38530e249ef9bd1aa319f405b63ed7e414ddebcead7035f5cd4baed6ff7aeed5de80f0ada7c84246ef0a8a71159fe52ad579b1fb22320821f5cee933ab7b221377ef3cfb541e4123cac9b834aa85e103040f14a8a4c42b60725d8514a93fe6f860059325ba1dd248e8d661aa6476bd6ccc2ad2ca4b5d90afae7e0aac31595e424416d340a9f59f635db6566c043472b7ad97ba0972b29a49d01747df8e4c6ceecc9244563791a3a5607dc931dc5d9d3a68d3992982ee26dac7ec59a42b69e64e779f686746c31443c3df3c43632a3eef293155e8cf1a40f1beb9cb909309fcc81561c81804187bd6d188aafdd04f7eeb265cc167e8e1b69476e57e7d2076be0d878ac2a74855b10de383f015f7601f8f023ed06457b4edfbec263ec9fa018cc71503cefab1d50b72190d263a5ef533d0bb14cc292e01cee50fb6db17881eea7627ab159f61d1169cffeb877bc3f5add42db0fa6ab10d4e06cc0dec87ac6d59b76a379371f70b6ac39af0d8ca56c078b603b59aab559558ca93a70213a60bd32246d800d5e2c3f66559a3d6d318bc0da6db30cb3fffcf2477745ab0ccae04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"223e6ea486633f563aec8343984b3d75fbde7022f4e75b15d56d71d7bb436546","proof":"86ee03125ef5c90038f0149115cbec48622fb817563be74a1222a41d61f7781ff09bd291b7421cd6b72323d1c624c66259ef9eba791abd51e7e0d8d49980ef7af0075ddf3c14fcd50ca10781589f77bf2ac58135e316ce2ebf65e2da90938c675e6b804bf72993fde74d540ad5198fdd38549e20f36795b9281afc6ff416057469123800e3d1b71dfd6248dcec4089503a6ab3c20662ed7208b55a9003ebc60396d02a7b8682cc629c20bc56093f5e5b43764966e44f9d362ea5ea3dc42e5a06ebfa7c6ab6a195bc4e89b61f03fb6d50d1a92cd5a3cfdcb17865bee4aef9e70956cf887e996ff3171232ac75240fbe3c2e27ea38430ee7903827b123115156450e70c1f43091e512eeab05d65b6bae7e3aeaf5efb553b6781a7422e6dce3f443feedd338be8b45775124f56303fd300f3b58cead2c6fcde2bb3fef291ea6353e0c2b120f363f31eb3ba315f0ff6f6a07a71c912a33d0f1c19f745148c9f3b57b0e184f4242249f0af06df62c1549cf3854eebb72edff0f9d04c7e4e9f15b191fea01435d73793c80e4f63a4938fc9ad1fa45877d80c4162b9949951152c1932796a1571ebf21d38f268689ef834850960c68244c051a788e0bb2707f0dfed45ba826e6168ac173adc3d2d072c7e8b6aab4cc3a4a3985e855df9c406d6e54ac402e6fdabe11a23d54def7c897a581f2fc15e0d9be27ac8aa6af28b8a02ab52f73ec20337b8f682a2a130625fc9c3a96b4e7adbc3cd7a67c86ed4c9830d30e832e4c616f7188c92efa75e76e9596a0aff3fc808f0d82fcb04884f1b15bdc03eb24b24728f6a7130f9ae42dce847280a8aeed1e75667d10ce1254468fa319b3a7394eb2c79a774b7db35f12b3ccadd1d7b7dbf3801cb69cb35dfd63a764651ff003dfd2e7436e1549979473a7d31a9a25bfa2ec0ff0e68c15981ca50ff43488a00f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f819f431e8461ca3abb0c9cd6be77d49b04175e117fca1e9b3a07b6f84b28a39","proof":"c4f74d6d3b478560a0a48d22c19ebd3f946af014c752dd9ad6aa72e8fb0f98783ef9e619b48d4186e6b034b82311d1a0699ebf4fdfbae8c00c20b3387bdefb5f322c3f132f48ec1307780f47ab0332da789845721098549c66d6d38270bafa722442a6804ba5fb2bd4e2456829a79e56e0e2d0825849bb77aa48958865914d2f61d58fdf0d793314efad15966075cab44b156368c5fd62e76554b7593565840ac22190cd55c7c1f6ac65ce05f6002a8e6c762682898de8b737e8acb6ec7c6d0ace6d986280f4749e144685fba7d7570a9e409c5653d26db3754c67444e734c09da7fb345009cffe2d7bf5c7f8a73da4fdf69448bda7e6b82ca88e3e9a83d16094eb0411aa4401768f1b580cfb37df43c1fe02edc16a07c2b70675bae14f7a02d5605f8c83f29c2c2ae5f2110831a20f627c1293ebd2c8136b3042cfae77de208f6239ce46ec4fb06ac66b61ecdfe513cb0bb31199a81d8bb6c5015088ab5ae1f084c2750643d4ea88e99a7897b097c65514243335cf70ecb5947a3305687eb34b4a16d58e0536cc6354905f13853dbbc71083fa9c0ee903f693f618d19d4a27c900e3a722c7142cc77d60d3407ca3209d3d9ed3b26a50afba89bb4be932a6b03fabeae877f02c7b3f2d79a07e6abd6433e794e5baa5f7e4fdf51d4ae8cfb3f7cc6044c11181781f55ccb87a930b7af55bb78ac6837c6032730969fc033ec55060e8eb20233b82845cb3ef8a16e40ab369a0bbdb88e0ab38a086307d4c39f240f52cb9cdc4695d16a91bffc47fdc5c7e8d79bc9620c1e688ae7c05c72ea7a180142756c1d8b9fe4ad7756d42430b5b9bc63c014deef217c4e46b9f9812cf62449dada08ecf2e751598307ab6c6b8ded2a7f326defd6f7183c56a26bd99315a0096c42fc922ed7c6099db8be775d1598a35c7983d5453a2f04294ce932b8179c06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b0cc7269790e9aa9a720223fef4a15100160456f1ab31a93b26b02e652be682f","proof":"a20d1ec2d2de5a5bc42bd50031a2efcf80a26ac33b6d755fa7a3b814d0ee6a32b6c175337c51607e2dc32f8fd6fcbab62c63e192443025ed4f102d77616b3e7cf8956bea3a740626c4670c518ca886cd06fc309e20cfa2f28ffbd2a3f8b2f929ae681607b425c9a73cc568ad5e315574077f3bd08850223e8a2f1fd5b5073c5f0bf921150c9387deae61abda47669a2a97d37fa7d2267b486d749ae3e99d0302d333f3b9c9433516a933ae5aa2ff3a483fcde7352b7d290b7d7890138b5287039996404ce7f470c2797f065c2c320cd1f4133d8ccfcf4d18db7ccbe1e7cbaa03d4cf5325c11a3e287d9af97cc131a726109289a702d30f2f9a001dc1bc7a6c2c66a6602130d9b4744a4b6d351372cd61444bc4c2159ec639049fca448d07a750408b79269cc08b00dacc0d6354cc4d6a1d38aec829097e7583f2afbb4ed4ef0db80872acf83f23485e6d73ac0756aef612f83d77390753a5f38afc7d2977020f8c07b5b63038ccdf9309585b0cded9b0878316076a834336756de327c6df144e46a3a31f9d1e89da9cce6528882c2b8770e8f40d46219b3dcc41c9c5ac2476463429144d9d9f514db342175d302d3d97d29c8552676c2e436e9200b38f7be324b067ca006b5d5d659afeac26314b89802c60c68b07f96b54cac067af7d86e70ca8151c6b4ec21c8f5ed34851ceb05bd8b562d3bc123c5de4e5377daa275a965c2842026e6bc37e3946e7e153d348e8c3687dd030ce12bf40cbe6f4116efbb7050826009a89775a0e236650e9f21bd9c9dd27f8753946db5a3f637dd7f83321252ef44de73701fbee143a93be736be10d6537883099387b57743a5b135688ec4dfe41140e8d9de607319b78c50ae84de37e9a562b8b6aa9448d3ed4b610a2e70a8452954b6890a13bf61a93befcc068921adca684923b889fbd6973f1268b6608"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fe330e88ba68721d62d72ba0fbfcc9617492ca612ec2c9fa30bddb9192e5d669","proof":"a6e779bd0403f434aa21085ca96bfc4dce2fe96448103a3891b7f031284ef775ae28fead7074212930d345d80675d11167e1c65174cbc0fbd9acca576b0efd01801d47a3071d29baccd8cbc955be8c32df1d0a791ae8bd9707da426e6b78774cb2af0ad63f949a2cc15026f041062557fd0cd1164cfb48f8d288ee3f3b0fa524a6b187eb57f338b27d3ead3c589511ec8fb95e839bf9af36b03271417df91f01713eddebe681882b03bc491fe4bfd012bf39f270bf1ccd37d72300a3e4d4f40ad39435a4ff82a27aaf1d41f8f010c0d9849ce449b58dd9c6fbef09d53733480e04ee1ce30eebd1d73c31b33dc49baf1e83b837a5021def1d7dc2c29389c3036e7edc8ed34da3b51db87ff7c77bdff6b1c1533dd668b6438ac9b0a44fea43006bec4c4efab0fb11da17004c0c25a81646cbb0c47d9b10051fe2a5485f9ee3991c8ab2634d4b713b674869d973bf3b39e3b4f9e560b98f88c7c337754c85b48b42a4710e785fbe6ca3432e506b92031873ec061c08144e0db346e0b85c0a092c2818ee2f5e41bec21bf900cf8659c70e68e54d87a4493c9461280a2b35a79d30054a7b61ad496b47267526c7326040b769cd22747a289d181ad37a7213b1384a6e5ef9231274b9aa6b4b1d7fc9cb24984fd573e8e7abcf9eca980adf0ce28d6e6dd0b033834aae90d5226013bf308ca58e3f439281da8727d84ca4df8f6c54656ece133a700e5496d5c2570578b40ac76c8fa9f66b5c4151afa57051c06902d52dbaa14a83d48567cfbfb90ce7939f56b2acde1452907a4d34690bf74b836ba8434699cb1235d63bb51d9ab100f0113edb0e5d606a833bb3ea721c59124dfa6244d55c164f35333d6b191b6a54f93d9c2121c716c6b2da4b01fbaa1eb4468ed309cf668427352f1d6c0875150e345bf223244696628aad9192366c9a654773870c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f8ff03f44a4644deec0ef600e329cf8566b68b732868f49b511dc4f338cb9e4e","proof":"5c3a77386eeeb4a28f59dd06e8f3f1f1ab0e432b8dd44af7acb921cc7736887036b458ca29a63b929892ffca232675cb7f76a80b742e192da9c3db45340c386cf268cc25495c446cfcfe9a48e1cc2b159631a3919d58ff09845f883429fafb05266b314683edb2ec46515d68036b1153455c266b97ea0bcde5f7a750e1ee27052c4157f1bf00c36e4db8779d90f97286db325c702414e294473762bf5262610714dc3e87bbbbdce0dc2735967cc7a69bc0a8f512846f5903d631a7a9042aee09ebd809d5694b6d72eab7e4aa1a0adbacaf5c3036eabf3bf5ecd7065e6381d603721377222e857834890df4ea762f3f19613bf3cc98adb2214bce17099199206f42ab2aedd8f911d99ec2b2699bb238ca2042c1060a13f38b6bbe7c490e75df4b12193e4cc3a277f7ace6b80369f42027523db5ab52abaf7f99e0f97750ad796af25cfffced462e56439f429435ed2b8aea3cafa2365e81a00b89af06c2b83e094aeff3fd4c26dfb7d22956e53f09cf6d05ce3ca2d90be928adb6250355ef291faea6d80d31e1c33cc6036beb38a02ef0735b969affe9956866707febae025413e64b0bf22c87efd79183c91b9568952ea59fffcfe927ea20b3e77ddf9e6bb51546446386f48f1b3426dd7d7b36d07eb7ff52a7c2859c7f64a54d0d51308e806fa8c2d2cd75cbfd90919e1a1a8fefef1d9217ae8a30b5143127fe5d048685bd3a9eecd10840b795c7573760dd93a792aa7f8bdd354c84d1ab87ec662e37cf39541431839c7ec565189d0e63c7c9ffbdc12aceddbc6d36551d19977c07e841df1eae5126c80b5b1f1cd0a5455513be93eeaeb7b5332c8e4748a67226c8f84c9e5a02b858d8408f7600a07a85809dbbe7cb2c0dc34eda40377830163d34801752077769cdb6ca4e9abc15c425200c20898d0e8295310edb762923f0f5eba8034f04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"06e3dfd3629ed3254e41ac5043fab5e69c8c74d8104938f96409c70f7d4e0b74","proof":"ac4eb8fb50d16a441f97908785610ad25e285420db93e96b69ef66e3f2481b04eace7487d9f006af57e06f1dd3212900f54f0373f2ed4f493f79b0aa441db2629c2c1f77cf8e57048c27a2a063aa37bd864fda92fe7e7970c82b3e2cd2bbea50829d07c29f00fee51b7c71848a57e15ed05025a2db21d0eb5def005c0c902563bb2464d6d8937698b9cc49aee9260db53604fa10c77180ec90941d5bdba5f50ce2aea233e7972473d2cfc00ceea69fd36101afcfd74c050180ac04f220e55c032d6324b0f8fb620384ecf8ff42b5c0cefd316e6d0cd9eaadb87b7f085f141d0cf87e82fb2d722cad6d55689dd422cba20b68bbcc864bb5e45e01a37508fbd34ca87e9b4d3d67f5d485dc6085ba14af687978811df67f1ab2d0a1fcfc61c0062a22f1ca9297124193b5e68ea06c83594a139ca614d0d963da83217e52828d642e225bba3e0ff40254de3ed11890fe8681be7ed97a50fe7d6c9fe1b84784af6d5be21e16e4fb9600958b897633ef16cc795032e8bec7e5b2360cb9bc6649405f395446166214c9539ac0382ff7726edced2838ce9c406c50ac2ca9bbe5e1aeca0cf6dc8abb5bf66a51f8c5276dbbad9a5a9186b1fef8ce3677a8742f022035f77bd483bd1a1bdffaae58c0d019f7d0d0f7946d130b23314c66d010c317bbcaac0fdae94d3e997c38c2d98292fae0098f66e6779e3ec5a73a407893bcd1d46ab574f2d172eedd6323c45383baef68e525ce799e54d023957d87902c97b6460783181afb3ec5ea0e7584c0d50b02bd190c6cfc0d376b03f5189c2389b25d3a7adf2952d8304038a6777b56d3392c9a3574d8bce223f5e1c748df50b17a704fc14a70b10b07eca055748f8909fe9aa60f52495a44f21d053d43ef079dc9ba4ee1860040d8ea9e5cbca54e9ff44c5cda4e766e7cfa3681c670fd2e13f4f051b5dba004"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a2f28cdf44521ac56521ae0a34b49adc1e9b80bc635ad22db4afebbdb5fc495b","proof":"8c131327529ea1eb81def9ebca082cc57a2f167e931442f2ee800865d8627d1c86c0fb2cd45b8d3e6f05a3d4fa82dae7baf658f9b3313401214e17a1415a1e2aba1ee3741b61783b9c2a8da6f7c8a6e5011c2b046c1538399b9d03f0daf07f0fc660c86d1e405d81553adb38ec4ed9e68d0308ed1a76525155f4c14e84830969a0ce861b8bb3abbdc73da46bebbd816851a4596f48051c7722ed5419969e7107934308c7a894176456f38986ecc2f29423b6bb5afdc395217aec07f870247e02fdc00a4fa020b94500415dd09cc7fa23f71163b611228688d118d9118dedfa0dd0835200829431cf7c56d9315f7c91de2a42000fd4561ffb6bc4c7c00d71307ee6ee3fa7ef566a342a81ab9b246323b87d7f9b5aba0afa28606e9852909b1f2aca1d1a0a4eee21ad769c0e49ff6f1819fcbd8f2245c71c413aed6fe8613adc30f648a1fcaff9f6137a67b5303faea619acebc662e2324ced9d89af01d72d827af42c8b678c1ef01d57c8753859d1d5bb81b6508f2564866bce1be35e9eca5f2f82d1fdeef7ba5c003781f995d3e9fb11fca88b18cb59cf26aa0822f864aa620ee2b7e0b43e9ee9988686b0b1e74c8d1f97b8a9e126ee6e0bf93df9bf926b891420b9db4df3ad84c3d2515d16755ec173fd429a2ce1603dc36319b671cce149332aa6041d091cc7d539542a5f93571208311cd566b4ac989038347d7177516f478e6a296b9423c0188573f1e802499740a183f2556b60b7eac7f6ca2f1f183b7c021e3afa67313a3e9e35794f3d4af2417d743b4aa626efc069389cff97ea39165e1b7110209040a1b37d2ab697333d2bf2ff57f061cfd515dfe4debe5d615401cf2b27ce3b05a7c3d94e20d7347ece8abb3a188e1901d1048fe362b866db120fae1bc9dca6991624985a2578c7ebdcbebf9da5cf07a6c8b56f844af203b32b0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8669d0297ac78e47b770d6fe703ac79dd77bea66b197f51779bbf7c8d4c27071","proof":"3eae4793ef318e5c053a10a52904eebca117460a8cda95619a362b555b534350960a7ca75681ed8e57207ca7d4e5016c6eb74a7ee98794b2e02d83055e5cb44a1ad41b8a6bb3525a203589898a23a536271a069862ee447bac85049382b1bc413ad3c215d71bfabde7290e3cf8df8602616c847c830a6dcf447e6f6563d7a755bd42170c398e11c8331eaf4f0c7e31cab87adc9146727f544ff830b4fd815e0fd853a627bce36242404155b254bf00ca8732828b0bc3efbe9da0280637ba4d0ff56ba239815b169cd9a169f1ffeab9b609ab4ef23de5d9fe0224578c9bea340a4ef6264b4bc9040c45f451c1f7b3cc30b112f7420b8d06c5da05c7ff51005c654207ae624605042aa236fcaded0ba2bde204c0b8117ac0af66a7e55ce8ec1a28aa9cf8d7e555946f7f337c396e7106a46be9cdfc80a4f1af817b0ad3a16ac11a9a4a42d87a093e05dd5f6b86792a9b914813c261818e43c2573e433797f3344ed43cdf8e89999836d4079f0dfb7a0b79ea2c36dcefc26298abe5eda949d2ab42a60e4b6bc80e249a80d06189701d0afc23243ffbcb11160a1ca462d484eaf2180e5516046e7db698fb0bc768d9cecb2ec4f7b9174c2555b95c926327f256ac1d7eb338bc62e190cccbcb3bb9aa46310771b4fecab049684af0c8059790b3661ff2e785c96ee6fd7526466720d64ee6df3f2f06ea17023de6740f4ac924306714f2d46530e6808994b1cfa73ef8a6a03c25416e7b02a3dc7503c364fb96ca647dd84cd086181e03cff153ea25d08bf481da9f21bd09db4bc628e33a25b82743127e476de316be1c9a78d8e04a88496f8f168ecdfd580de5954acbeddd86bf2c02d8da9a6d1e7e29255a63204b2b5db945be89945514fa65773c7cdb426a40b5065ac6916c82c54de4e00c1719d0ed73ab20e5f32bb26b138661ea433580136c04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"927b2ef3731333339161de288996e8e8a6c03ef0b71e54d049c2f8c1de46af74","proof":"ac8d16a89c260c1d57b0be1df9dc76d36968d1fc59c92973feaf48ea85703260baae158b4d061407ef0d365ea8760e74b35511fbc351666d26325696675f3476b01a7c0dcb7ebe6777675b5ebc1a8bd8dbc61e17c69c060f9f659f338ad4c07562a530a332b792d5d63f5a6320fdb7b725d00e11cfef40ad4ab684e938410466e3769a20649899418defd6674ce4de30e3c046794a9a78cc753ea03fd5821607fecbcbf3ceb3a5a63322d457c453290f71dc3870ca856f0e04ac4ac6259ccc0148a6c833d23cbd31328bf3882ee6905cb41dcc02b24ac9aa44dde0310338560b549bef4d453c5ab3845d8fb9458b7e052d6a34b550493d258e47a4473e6ac21c0e29ef8b9f42a6b14a3e0efdb8a233b127effb2473aebd77c2da1a72366b4d57c22cfff3ff4d9789e5aa6518150c0b9e75a65ac2bd88aa07da8529d94d63123c9ca88858b49f87b853bad691908eb6870356c7ea27ba128f4ef2d70d73a53e35d4d246a980fcc55a89a399a89864ce9eb148b4ed3cdb53819581f85cbf6607612c76442f6d71110868c7925b7415af8d9380892a8934192608b260a865a64f511a1429e443955b6299a5646bd7b98212fb3dfbf7ab69eee2ec1aaf4eafa4e038e0a76642817068568410f02e05745ae5e7732f8c64120e2bf3b312967f0e1c1442331dbd405848f4e7e89f9f6b72c9dc206fc84aa47d51abb100df548ec46761a22778a27eebc52df263adea62da10aef05968c4fa90023db65a1dc4f2284e1520e483a58a7b2826d786c70190bbe82d1e62e296e2c40dad3213c58e0570b2254ac3468f9744748b6b2889d5f5a5f75feea683ee01d354646bb1e84f54b2182cf80792cd7f61a4e539de9043facfe923c12d9afde0809c58ce43d75e3a34610e0561afe4d17e5b79462bf46dd887905cb4c4864dcb6c82afba21e007b3ec7a00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b6172ddbd8d4cccef10d0156e523f9bbeb368070b93be70e2ac2d832541d810c","proof":"c60a3abaf677b7e8b8b971ba81845919e0be6876971919f0997c40414ab21d3f7cda4a02dc405f25602647bd5055a18aad936890f6d6b3b1a1e27377d1b4b73fea1fe18ba370f3e2aaa6f5a339419eb1db401d69578bf39bd22df6ab77a1aa65628eb398e8a2e4a54a74c1ee35c410e19796238fee6b63857e860fa9b0295b784ac1e18d5d54e4e098f177fd2929db45a855e15f8d91180e3b0372bc2bf70a059ab1dc5db3cc87805e495d18b1fa398ebd07824e7576c895842bd7562259fd095a9114f6d8c6d97f8019aff581e8f5753c5ef3dcc1f55610539527d000d988067c89db0ea6000acffceb0742a032c59477cc03cca27a9db5c501f991854f341f9c34e7b7cdd1700cf93118c6064c8b801a7c3646f49c084445aa86893c8c1548e6684d1188f4226eb2257e638c82e596cb35bec3bab85c18fcf18ce60577d67b023ecf4579c337c6fce7eab1a4167fe619944fc892cfa8593f3bcbc7fad1a61538249afab972885de86a7d87e8acf945f862a557cdde5e781e58b00e5a17c57f8ce029b719a75fe0245508acd97babae29ce9526be17fdefaedf76dcaad16b5c5c90d52ded0aefd4ea2f2dbe92796108f845d2c685ab1cd12538fb8d004bc300621b39e7de4ce954896c282eac70a3cb37b6a2e40350b61007255fb5fed08405c0ff57b0f58cf2af3bdcf29be6d08fef22946651297734f6b0c76309a208f00148d49a80307d5bbb57174aa09fe4f417928f0b6c5b01e0b2d68f5cd9c2ba501ce2452e0bba19598ac9af79d41e4c9cbec03439cf7c993e5e63278de22cc0806ca08904b895f2ccc675c02e8d3c46944a582a91e2c94931aa74d776fe60464f48226c61c3b4067dd5da42b038e552ccc6cceff310ebba01472e57657bf9c07205a7b3db47b7ad61731e5f46977ee16b4c77a178f8db11f64c817789ce20625c04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"949438e4dc98e3fff1addad74e1a4707451f43567e116e24fb33eb040e379705","proof":"10fb589f4c096e2a4443d33dc352e6465b62984857311a20367316a13b48fd3c560717e84677818ad6661128145862a80e928e65b6716e381773a9d42d58f5652046fc17e2c895f5a3be74165fc6c387224e408f48a0d5b19aaf1ffca8ace9001c0253941502bc55dfcb8c7af692edd3c31a3dc3944c7a1598be7aa85ea7153598ddb8894461df19c477ad661ad0b2759527c2ddabed0ffff6ff834d102f5108b551b0432f5daf7140e31a40c16fdeb514dcd0f7b01f5f099931025b17231c0efdf243c71fb5b4ab3ee582a2c3dd67faede740eb7893f03f5cebaf6fdf9f580ebc18f8620e331e586f43d7f793a4833ea3436e78896be68cad235dee0948712f08175ff008ec71a87ac0d957be7b891358cbe71726b59c95056121795316da4f48b904f0e6b9e43471014cd44d7210369b2871cc292bcb0f3c9568ed5b6f905b00ca2ab899270b17d1ba16fbe96301c1b844b5778f27502381aca20e2f80174efe940ce53082ec1c8d9edae2ffc7c5bcd5d9183361a2f5f8f6ecc67d8f177a080a6fa295cc9a25015c9e06e64913db6ba223ac4875149c825e84a4171f81070e0ef53ffda7a1adc485cbbc9ba985a2773eb3f8d783c5fec3c562dd58b6d0572dd6342eb5cbe5ad088a166880d86cc8559cf9aeb002b91fec51505911c0f52f03b811c8db58a30ce33594cd9101d5f02541ee8e7ec3fc1338a4f7f0a205900c6ef6c827de3111018a211ff018edf4abebec98d8f32152ad54ff321fb0e6e00e0e22fa07a7d05d1807789013d66be5645b260ad60a0621743f6720647e30feec5ce84a7b8d73baaaef2523538eccc96a92a128687fd3416abaada50516dcf8c22bb74178e04c1f29e01006208354e76b129d794d2de4f02b7b81a44b3a7060af0a717cc67550f9fb98dace923d4be99eb518ab73a29203ae74fe4fd281a1e5d10d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"926c718ac91d85b8300f4cb98e3b058f01066727ca354ca6351f59f68743d940","proof":"c80fe96ad607074d87a5b9da1225e2df8deea8ab8cd8d7dfd1e308a4a936843b08046ca1776fa46bf5f29c662234c24cd0c4d54a9ee2aba3153e7587148d8657c217234191f41f452f1a7128310a3b61f8fa59a39d2c967fd4a90504968028277cf49b822c9210882517bfd7dcb7e1ce6bd97cb8bfcbf4ec709bce313c5f9c4e5de8fb60df8b1e16f46b0103e076decc97cdaab76ac340fffe9770452e3903081ff4a65c8675112207accf26704d4c5f194420ea158ada4f9ec39150dbe7260a63a5ca76c039527c2b0961c3478e044fb533f680ba2558679aea91d31c06460460a888708a245a893cef38bdffae113ce32fa05f981a124e14d919b7536de8650605d76ee07455b72cdbe2b1db08b87408a8c3c354acfd6247e7a09ae2ce6f3f2617f47aeae65fe28eca0d99207a84b770dcf9238e3ac2a1daa6be5f6df4da3b28341441c0c3ae450b247e1fdf9c446861da0d927b09939a91d79ade64e4300b5a2f4e9ee3bac70a664fec4f39023c611cb74d8f87dfacfd3625e33b5174d860a8f529b0a61e183184352c3e8cd4e9a5ab1e466b7099e7048212b3238f8e87465071b77c4d352f740d35bea977ae0238bca5239a72e240ff134dcd213cf293386ae0d0e00a98d43dac6ff4d8006e6521a250e1c5eb3ec6513602bc6c31a18034ac0ff443af3d901ce2766208ce8e540c7afa8805c65ebbf15bb28956fb116f0c0e942598cac00bc15a0a657afec94d28fd2d10d834c88e26115cdc827cf6c43e16fe2e492148d100d87c5d891201649c81eb6cf697ca46520bfd8bb8754ecd7bfad570cd5774f5ccb0a5383658b62dab44d2fcc4846c3b6c36210196e5f8d830203f39b08dc36fc9c904ebc303b7f1b3c918973723d5aef2df7daf343875a60a832c30c76115f70742f6d157f602e1710c36026e79cd1bdfd30b183e7a64b40c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2c3171c5c06fedfa058a93165c5cf794d9711e4fd27090dc41b4f64366809311","proof":"7469df2ef1af1192ba3d094006b2fdc18a8828bc943cf116889899ef7dea7b63acf4787a4f46e99d9ae4816138df8d88dd8b883dcb6db40a6625a4a3f789e3750ee267e3f96ab51bfe11f9e5054bb3ac98c656919391e8042c98b32eda52de11aa1c649045f9cfb10ff1fcbdd71186d7c314b8378fc564efc8296690e8eb800371a74293481ed16ddd5b5342c610da158cb313b0cc62f876adc4526655c34606f9b2125437eb1618a7f3c14fccaac89bebc274eb0c0524b9a02179882b62d10aa095293999ee6c4b74eb5e1fc833a03fbe4698e777e3b66a3f5d89413fa1fc0f64ab78a94c7421359adc2c297bbb73e81540111df09362f2b98716db4c8d1c2aac9cb1a542c3ab5e47bdd4198c25adc54e2c45e49068cb34034078f0f450700e967f1a1813cc8c6abf12539bf0eef6e7667f568b08d6dec1b5e86d94bd49013d6211d9695409f71f74531bc0b2854c58978626f2963538847ad603b8adea963026a39da5f84e3a9c52d62419efe685c8dced018bb25fd14c31d8c4e5f2ebb30fb6a4217091103c30ab0d7f18b1c86f30ada2262d0ebb185700ef97a3fe04d04d14d49f5fc91b7196577564c3385eb3069b051a9a0f9b4ff3dc35eca8a780f273345b8d209dd5d9117a0ae371dffae04a60f8b0edb6dff0fa0fbe07c8e1140e4b9aa0f7bcea29ee4063f04af49778c66928b5bd6509d786db8ebced7d74eae422c2785dc0330fc5a6ff856051bfe2e7b8cc8f657b965ff61bac6aac03cf6e2b7ec6ddc61154ca53d9f2e522b50a92aceb28a8b2c58e0e67a05d26ef0ccdc067143ac9099da95772c1f7e5964fe7e06ead09eddb07ba6fdb376e50521701e6726653171a12a2684a0dc329e09ab325816c338739f286ec1c0b556f74c94d65d70521d9f63fb0f0aba8635058d11f59cc22ddeb205020cd238ba04a433a10629b08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"38c04c0996fa8682bc99e70f81e036d1aef2ea6364668a95eca53ba8b6564f38","proof":"ba353141b39f48225c5953f50470c823368f77baa226e543c198a25490e0200a60ef7d590cd9bb4d96f4ac777a33f3e4b500891a6d79d9df45eae126870bb2165a24ad91bae94ae3df895008aa925023ffeeaaadbd5c370d590cf22f7fd96c406a32b4e2e923dc7022f123eb75b15158029ecb645d3014e5177845127167cb55e62a5df1568844a1fbedce1b797798199994fdb861acf26aee084b8dafe49405633c353a74c00094ab36ee1830fd0a311182f09422d449c80de2cbae4b096b0031c50e62667fc67f9316f809e862a470779c7c4492fc7f30ba79d16b69a01c07e6ecd17b1995be7f48c5d8b771e912bbde361ab4807d4e592a2c802e08066d389e4a29e9df50345e166c0a8a3ebafa498dd409c5dcf3916159396dca3b2a13791e0f15fb32eed80491d1f340f15d3c63b90b7c924f634e1ee40d21fbbb4486799a5207574d36288ed945202d07d4f190280d61f016e9853684e39fd3ef1b6e7ad2fccfeb205fc56546f39f74deb728bf94161bba9f869ff032b34695209f155e724b43a316337d65ae4bffa9720d7adba1a296d6d0a9c4922f4ed41c655d65220e7fdcd423169cf59eb7f6593af5a0816d06ef2a1516f1d0e00bb401aff5de1b920d93a6bda72f35ed4cbb0c9262dcdf676c41c2ae617b2dd8a81f89e674f151c8f08638e1ab7bd6308ce0549ae5019f88abb01e0d3be33be8feeee81d207c449a006eaa59fb991a0e3a9904a8640d0e7477ae227479ba3a1d2798a799f323600e8807eae2b9a0801c1e337831743ee73fdf07f44731325d48e46016242a4f3de4a820eb8f374080933f51853ebe8a6476ab4104d0b1fd783c9cbf868456464fd9609f98e27f997116f9aecb15c758e1384c53880f4834269555e5cf746cea0c4ba594cf829e7a507cf402a525c09768bb433c5b513a0e9c5f2f4912a3904500"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"06e57f00d17dc1e477a7186e5a438a42f7d04f47bc2444937ffac70be7ea2743","proof":"64e95239f62a0e3705606b153bbc3b47b14c111ce9322dd35e36a1473d9ea5721e7ee545f745958df2bd78c174fc18e84c368fe9045b04862e584a2632c58230103624b9354d624804ec8b5b7ca69fc98c6521738c4e1345419bba7ffe56ca3460fda0849799b0db55e8e14385bb4e203e7baaab208caf807b0cedb9f87a45302393f642ca329c84c3d53c8e6b1caa70a32589b06efcbd85febb595dccf91f01f006103468e87fad492cc1dd8f35c7796b10167217e5230028c7da17ce2cb308a40089e8b71872fffd0dde014f1717acbefc440c232e79cdefcc56e14003e1094275e6b00c6aed5fce217065e61281cd08d09753a8204109adbf069796e2a475cef00e5ec72cc17e46dc55477b1fd8807e1d3d86a41be596427def2938a4776e8c61cf03279f26c1499ffb70626df1e89232cf396d5466ecd9682c17d7166a0580352d6da3cb92096aac4acebb5e1d0fbeb3f7646b28869124c1c733f8a6b406884c04a49bbbbaecf4771c0d532e8c65da0cbbee302a09da4c8650f0f2f06420a02274466e3becc1cb80963b97c9f5e90f88a66eb8477fb2368f1713ceacce7f680175b5d7a701c3fc9bb978b85e52a00eac8c8d784d80e5003062247fe5c82b02b83b1cfec3503bdcf3dcd9eca5416baf3895abcae199c79db5f1674ab25a67123e9b7579af1d650cce28162d05d4f9ba95162f007026e147c9fb5c72a31556fc067bba6550f9e6d6c61c5cc43cb90a15fab31501e7235cc218481d01e75001de6d67020f69d3801fb048dfe32eec444a0fa994fc90067afa2b48cb0d94e35d42bf6144eb72163955a2f169dab0b54491cdd35af3e6a4ca309c8e5c47269510717399fd11db0cebae3dcba66b9fec0a714813d5175a7f031d4d4f1d0f7b870f0c879539a17d6dfc1e902ae491c45bb4156daf32dcdaaa8913ae1a04a2a06c02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"689b585f794128241b7255468f2de36f602467fe9e7c5b20eeb596f2f6292945","proof":"c445ee458d8dc4faca39dd7cbea30e3f98cbab81077fec8615627d7db88d08377cea96a11df803c393eabb04690758b4a88a8b673e062ab024a1dd8fefc9ee45968fc1d195576b79260d25e3c7211d86db03da5863083ac62fe7241db0201d06f6e93b23d7b0bd63d9687027c2f50c2331270f78411cab5aeffd4ac3922bc373dd9ce46145e18fb71f1fe6e6815e1814d9c08cd596a00679f163f92ee9ad8d0b9f8d6415a957f051a704af06ac68a652efcab06250105dfc720a23199347d5054188e00407a611f457cd10b2dc43e501c4b3f6d4a35ab6fe90465019aba3b70bbca93e6289d4af10b392cf051a53d2a992bb3602d735b5da174e935dfdbe3f2a8e49aae63065f5b70222bd84dbab725fd68d7a35a91b1058a3b6058f162de549bcd5face739f78a876d6cab8eb5307e343a702d46906f39dda662e8332bb1060e4e9c9b7612ba84f75933f2c509a0956f498f41401a560734792d43feca65801004b249305f6ae3abe3e0ccf26edca3a9df21f4142bc296d55b22e6a70def671364918efa429fda975629140d5c48db87c486c491f8ca909821e8e897b2e2d0700c71875d619181038614a3f03ffe2e856f86fc8b1039ee1fbcc366ec20c1a75aa0e7b28ab19fcc79a99ab1ab380c4fd45410d071ddcfe1779493875c9d75c11787ab0606439961301a1041fd5e4842a43afa6490ed3c6b856e82f1ef923cb057aef66d2d975cfb6cede635dbe78fc0eb1bd46d31c1cae6958ad174e77f38638203d71cc2ea5ee836635151076f07416ff1ce5c62e4171e6216ceeeb499d966a8638eeb08d2ca39d6d5defcdcdbe706289c656a0f71f4219c42883f8af90ef2695d051ba8ce3d5ef0f35d05da7efd713015e73cf705d937f4834d042c2f11d0fcde0b6413a54913c690d37c44b6114ff7146fed92b89c75a88d271e2f3eee503"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4c7a8483493eedabc9b6044cbef3dd485c61f02d8daf1ffa1e39d127ac34ac32","proof":"da1ada8f63db06d60b158d9067f2c90d2d87ca2e1ef8a608ebd0f2660ac699701ad99ce62936a41d92147dd98baa93bfb47f51498dc8265bc175f8cd50858b0212a65b666672e844dfd9aefca04cbddaf003afc72dadb08b0da20f308efbc7195eaf2e9c7bc85a856fc41746a05ebabe24da43ed72196ded315ce526801e3915c09b2611d35b8e30969116f5197cd4a16dc2bf820b5957ea0ef8127d32c0be0555d57b20e4c976ed63eb479108520f8b4b641eb1e7f18e479534f687b9f761006557c22897c097b85b0f567d3b57c08d76665af8263a6e6d01fba114a7fefd03f02b42cafe92c53b15028420dd21c508e7ac4cd2f3de85b53c050a5f6bfb642aea183f35772b59546115a73a4dcdd4857a982e5c965f06b5ea4269fe8c28972bf65feb16d60ce701a039ed618d690e58d8be45ae782aa83e4ca3668725036a2c10949709f1c879006b1897a1eed0ec995c14afda5e33a9f3b173c08443e7c6539491e193ead9689e48b81d335d7ee3de1c4fc9ed5f0fa63b8cf4e528e1650c6a4a856a1f1f5455566e93ba8eba3aa6259416c4d570382ae35404096d6efcad04380c8b6afd5cf4a2272f6f9026698037bcb51afc2ad7c59c7979cfa1b1960908a0d75f6a766cd66c3b7549ba94121a21d74d58ae9a1007e742352e2898de0074e68862b24bd407166aec5a834b86e8acd63badb6b03e9ecb4526a84fe0d4b029f8d0a52ee6b4c321f22d4ccdbb7533f5d0ac513cd12b02460dcdc57f9247986b48d1b8bf12e21636dc18ec3eec1345679f9ce0beb5a2ae8701ae5dfbb6353b1a346d40ad26dbf35647be9b2d7dca85d9e2e05b63adc147660df8f66612feaf6064a6ff6e3120f399b3f13dc1ff4e508b93fcc1808c7a15fe05ad91cab809ad033bf73ddb668b5a04a816a58f10dcb312d70b223c6ce704a2a8722cec0b3f3e06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2c68299b71b8cdc4c324f1686bf26f92d7def6c11f80eab13e1f58bf97f7d96a","proof":"621c66f2c38b0965e8f82ea87b1e9caef0e297e31e820d0586d16c7ec613647fb07ff6db7fb5e17166947d43587ebadf4c8c2eb6d2690e907e9918fbe5424239ca52e0e4e1b771761d573c6ad81781111ea7a32ca0711a0ab047858ec1ba7c7fa45d3de616a46c16411c3ae8f1f189b085fa4ddbe1783bf82774458f56c81823aad631c18a9915d1815fe3689189f6116b2cb1a4976fb740e206c722bfc1f308d7127a1e46b92f10aa589cd58d0fbd22727da0124f9072577120bdd8a8cea000c3763d43552d8e6427b891919626616cc4e20586f2b0dff40381173ba94b2e05d8123b5d36601cf4015a56abef3966f35be7d78824a9427918d98a6d6fc9d42406de586a0b3bd96765bdbbaa89a9df9f90e1a7c2b1d7332f2efd41aeccdcd75a3a02ed3187fd9def1040ef0290fa33dfcac57a040e4e8bf211b665b22938f56be499d2017aefd81356dd1e5e58262c8139249dc535a4b6f4b510b694f738c566b8d6dae39c8cd2368ee653756aa4c6488f80d2fef88a2d7e77392e6c5eb3a069266567c260898194bc602c2209d00dd84674fd2d79c1b4e9f70062951e6c451570afe5d6d93823230a4c2568ee9890533901e7bc1cd3cb929d3fbd509892307c74110ef042a386a55e3952187bf79c012ccdc3757b13e2666911b60aa448c4045278f4678f5a9a508f601a287e84520021bae51f245dc893122532c1ab19d70166030f178b444f8cdc07a14de53a185281247f16b628dae18769de7ccf2d0b37d6e36eee3288bf6903208e0cfc853597dceca0c2a40af7b36723e8871fe20a56ae1ab4e8cec4f3c966e08e5b9795695e6730216512f4acfa1664665597f55f1742bc8b9406a9b8ae201d1ba85dfe269530c2850ff1015e902c9e7d4228f9d10d22d3b90f4f7992b95ea764bf7d6e46c2c712173bbd51a8803659526fff7ec404"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"587dfec6fcd8c83e285f8a3fc05208d43b978367f9e40a120e3fe9bf48fe4b15","proof":"ae93f1071930152e20c1252409b27d4f7c40515bff944b7fb2c71cb7c432ec18aaa63e9986c7b0d747c9d77742a0d8b2bb93474142f5007cc492f322178a0972b80221ca53e508d0ea21b36a84ef2446579f32ae46a573f50212f3e839b5eb04f2192dccde12eade5537f435abae297146f403e6ffc3322058fabcfa9c500f7c0b9c5c472575909a4c351be9a5def8505a62a45d86bfeb2ef28dec833b176903d3325e222f4be16f13c52de0538a72aa8a0f5a3a9a7c6921e4f26e29eb620a0db98537222c290dd93c97eef42a3e6e56651b0d91ddd34a421c3d171109262004ba11fbebcd968fffedee60f8ad72ec62481c792f3d63c482c4ace688d71a623df4812961e0568f907122d89eee365f27d5c84acf6d232836eefcdf02cad8bf786a6a9055611239da8d151a4edb3889d12b344bf7170e7028003f7417bebce22bf45abc65f5aa63eba4846ad0d77c1c36afa95d9c7ed8f36da712cff9015fc138b0ddd8abb127496a0b6bfb9839a2c8a9cfa775a3f0ef6e1c57becaeb31067c07487a3a77d14a94cb51848db771b5fd9c5609a51044f84bf2ec30f41f638576536e65b9e44d940f1b6d8f21ad95c8d312e3057987c0c43b05237f215b63a2304f6a154b46614d2f23788b0231eb42247fab1d40c746a8b34abcf51ea0ca3b8543c8f179edcb55ff535ab0c458db369117e5e74d2ad156965689a98f21e6f235649a3d54fb62e6f3ab03b21c8d653cca5f54817efa4b14a0c722197e22a92b8b7bf4c042189beb9c66e6a43509cfd89004e14753b6218467638e1d94d3913c79677c4239890fa38ce0c515a3ba505478197b063dac300249f7cb15cb07dce46c3415cbe1151f213bee625270406ebc42a45bdb3eaa105590a9aced5463b1d7be0d945da80408d2288876b692670c6c5fd66408f2fb294b79e6d9c0f72c14bbb209"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"92ec531abc1b444cbe4faa8bdecdaf6098cb8617326e5caa9938acbb8f4f995f","proof":"f62043d78c362bd7b0b6d89b24ecce337a9a0123a527c1ae94d734da28685e4c283abcae9ab143038338f39928be67c7a541471836a4f848f01e124478f97c7982607bd0544cd0c8100a7cafedfdd48b8dc061600b8276497c341b7cf42b2f4d548503f33ea12c18ef9d4ebfd8a846a37ed78ebf764549fda172104e476bc37bb26f8df5008386b83234942d0c10d14830ba5238d3761b7b157910eefe399b06b122393e374ece58933aba78c68eb9ad415f43f87c91d41a672e4a874d91de0767c6a35a005602938f123419835b46b64fabec81f90a7d5635f57779117c350ac42a01014cdac85399d87dc73431e3e65f0d618052c97599d3cfae5a272d4f57d4c7a25e5a7a18fc75ecb55ba5f3cf03bce6de09cc007064628792c2b70186275c52b7920d1bb9f11bdad886d7ebe2f8f830c2ee3c9bd053e3c541ea327a182d84237515703ee2e94063e5486e06cf3f9b9a79bd3f1ba01b60979c36b7dff06cd4d2b97dd13696caee97802e75e850c11d2f36e567bb8ded633c8fae6ce8230b3604c4d7385dd35bad48b01a49add0770321d490faeeb68477e09c3b4cbc265c6218f57d6c3e7c51a3a3e8798077398ce2ac269b8954f44fce882d3006989720d0563480093bfef218f3f8787fc098ab552b91e1636a62ae4a0e7fd4b420da71084e067c68fa50898e47ecd59f5bf65b1c3577112065b3ad5ee7f8a9044670798efb5ee7c55f3983aa422139ffcb3a12a6cc51843a4bbb718ff4ae470edb0e1fe24082b05548cc77b564ac0cd50c27ef51a6bf1d4bf77df169685ea2a27aa317f8e3cfef370da196fbaf76a896bf0ee3a35863816c479bd4e2283146ce28d831acd015045aa04c72bc53d458b00b5e4f837efc1eb8c38ec101d3410c8610b808f4946e89ccd492083812b03614a8c61e1bd536c554f318a9c983d08d79779007"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8cefe8aadeda184c9d69f7b2f97734f68b51cbfc6f47d61a46b13eed3493144f","proof":"8aeae6461cb8210e62d268aeb22776b25127d006915d3869081cc7b9781ab514b8752031777e09c62e4e45ead9b9422b34c4e7b95651b00e42a2a2a1690948220860eec545a076b191697a26a0e19e45295e186fa7b1d71af8437d5db3c6d1370457c7cde1f0567b395ed9b1bec5b5fafc593f87f8884dbda552a3801cbe432e1b28cef57008c540dc7ee99b5d38299479154503cb4e3db6931e9975553d7f051671d7d77bd83235e50f034c17610f1a7151d0a441f9d7a7c9f767b612b9680e400536ef342d362b8763c513f21e3a73306678604a570aff7414f1bac78ccb053ad8e5bf7535a7c60d976e8ee6fd25816e3c80a2d77bfb5a32229f87b4b3e94f0e27edfd1d222a54f962a9f4ce81114d5bc50ca951a8cd626861b70b9eb9b47c5e31ccd64a5307958e5c4dc3b8c7f4e4aa948da527d398fca00041c9d113fd44b4ea89f8d5965015c37290a999e0e271243194618211a2db51f88aebab4d410d181d9ecd12e73ed57a368dce49f12a3caae23488e7bd37236084af4bc80d1d41e8b1c4da28d15cade3af65dd5ebf52f76c72dc82f42fe880c8b69fb4db6569135c79119a4695daaad1de2099bc4d0b7abde41887b8a23f7fd56c0b41a6ad791f305d97a0d6d0917855142d48a754fd694e5ad893d9b8555247fe667d53058e3d86e7c3c131aafcf7d41c5ea1f09c3b9fd625bf59604522913e5c70a94d8a41139e8a467c02185acd102b05a3ef56e0b7b44eb4b3ca257ca66a40c0b3ba7d7c75760d02873a68b256ad78d544bb9f114d8635f9762f24254652f145171c3e4c1a309256de44df98eb79545b2a0d90ba2031b2ac888ac8968e9196d7251bcad012dae7f6407a9257ebb24dcd129d5852bcc0e09e529818514dd55d89ebf1df130e6b5a80f8f76613da66f1801521e2c3ce168cd7a2ba5401f5ba1e3bc5336e7e09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"62d137d3f53d2c74100a0abb3e884ef09ff44b84d40df9c1ae09eb4b4679ea34","proof":"c0cf6f611a075c1dd3b94cc02d86e5a60b8e4dbcf856998e65af6cc7d069f00e9638deb570e023d347e6df9f05d4997033abc9d78f9347f28def52edb9c8ad2ede5fd0f49fa608cf2fb71cb00e581a4d0d25a843b1f1bb2f0f66c1455d0a667262e9b9815aa48036762aa4fa9cb8e27b98bf76500687efd3190af899982f4251a808bafe69a972ddaa1b99f6a9bfa653d5d5e60fcbfb7882a0d7a5756a1e890aa3062c04ae331f850c53522e6e00fcdb0f17de14ea8af06a81cd1b517e35aa0c480e1333d6de7403235858648ff0c0e56373ab927cfef44e4ce8697c6a74a6021aa0989872da09e5f9bf6c6b553d1458a77f70e2a23abb9edbf8056f0fe244708037a23192d9410d273684c7bf48a4ff6de667b87adb92628456d8ccfacc296f28bafb526e79bcae8697da7da9b397c80f02dc77b25fae4a88361ed4ecf2136b58aa6579d15ce1f49c02db6c046fad0e269ab3624c973485f71c24d66b86eb6ed6f5d4de9970f3ba4d6f204511d17573c8446cc59dbe2f3577d90a1f314bdc4fa82007ee32e4b615a4ee48fad07f501483269677701502288762b679edf9e5185eeec15075bd96130a59d458718771eb468c242b601b0333c012104ef50a5658c206153d5ca92d16ca1334e068957a2e31192c1f2b8bb9ed332a9559674dd66ca8e4c3fdbc2673d98641c551b86334d9ba52d1ae6d133f402c1e42054faf6e31c8517a1b13996574a4d8a53f03614d98cdb5f7b4d31ba3383cfa5b87860581630e58ffbe80b1ef752254eacbc47f821855098f0c0ad6463a1a3dbf238f703a0fae63cb584312e64dbf5ddad6445a7b7dd0bfaea5d5c09a4cbcac639cd911b905cb1a92a04a9498065349ff3f594f420e0c974294d68588e88d6538456626b60d13833af55280bcdcae11acf6b0c5cdda9d7158a9a4a6c60bfbb5c70297ff9c0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2ae1ff3777131e040a699b44b68959f7979a45a95e1cbd95271bc4f0a0b30f09","proof":"1652353ea91128a0f3e6cd15a10b3622584d388eb7dd28e0ba041e4368d53b5d021272cc51758fdf91a2892a5da38b83cea78ba67f04002e9b3b86fe590dca0f40184f5d5837a3cc80b2579adb2a0e3f1594c720b38568e0034e6980ee5d4741b4d2b878b7e259f9d999efd11bda3cbc067f3998d901b19717ad9b072d43d11779edee5446585e192f0de00aba0888d6c9f342f112f99d5714f6dbf6e8e4010aef00a85a18620f7b2827de9deae15598732abb087c8e72c5e4d492313ca62f0e32923853dccefd28b38793e905aac103275db853e17cb960c5f90180afe2520696bb0552a2c96434d33073f0ce53b02c8fc85ee1fb7f2a911262d7b705f11361dc4c839323cacb258a6a0538b30dcda6071cb425c6dac9514bd5577a6bfb52072e37229645dec5e0cf8ef8a6532db3a54a916f882ecc0d60896e8a5e161533458084044bdfcf4e2e20fa9bd3812b2c32ddce2c14e9f41142bd874f79dd9f7949ae5603b8114c3a75f80b4a61d64c427809eea6b793b25deacbbe6e03af9ead087a4020c621b28863548e15f274cd6bb3ab63853ac48b30f54084c5dd38c50a02045d7f3dc2190f0598557366345ede3a4a52b8298aab78c5a66d783b82b5165b5cb65269f29a5ac7369a1388f1841e1df715d97bc41faf058cb3c34ae88d19340ac91de688fa380e98de6cc1e37814457a7ffdd86ed229b4df4088c88221aa336678126e20d7b9ae9805f70db91e0e7fc88a1a23f65c6d3967eadedff34a997c307cd1cd9fdef77a02c1bc0bd74f0f2ae329e8e9e5fd2368a6e7e5616c397638c0d330239b2192820ec2640cfd1ed0e3c89739b57b46b982f03358ab6c2b5853d3743c91f7f762fbdb6a729da037d7496e8ccadcac37df35e9ba5b2e0b9da90a13ae3492eaad736ed21a68f77bdb62fa99a8865b2e348d4be6c87e7656bd0a0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d8199f512456baa27dc15ddce2b40ec3f607c94cc1fb5c58353b6c854feb8414","proof":"94e75ec96f9d17ed77b30a681cf70f34b70d22f6d0460c1d3e2c54ee3aae726a5294d53a16c9b33c3f62f26b3f7785830123d8308adc9c96b336bf481eb501014c964114c13b1a143b95ca89bf4100e17c396102a6f9eb77d275121768dd9543fc0357662448578d311f0dcbd32daa6d4cf5692689cf4b5db38eb83e2dc977210c9e8920cbc8b515a0ebc66507db3afc5dec4400373053fba1d05d590306990cb05056117dbf3cbe3287db176128dbba3e33124b80fe07aff889a7c00f33ce023b6e5e9d834eb33f8f85b5e4ce5dce463f9bbea98bdb108da2c986072059e001e637e6bd6e65179b978b9f453d163cea52a7ec96e108e34789bf4dd35dadbc70aecb0aba8fe443d7b2dfb5b6af1ea1793d421ee3760dde10c8e52f07c8d56c63820f75f3fd9c390660b51b43b699014fe3c9fc266f557f66a8710098d8468f08b687257e6d149e99a9e5e5e0a4cd6e5f946d18729f0b217f2053d023c84afb5286816cb2aeba401e8fa76542c48548c9f21512fdeff1b7f50e07ef36a5ec0a23c49a60125359ad9cde03136bef76f8fab8fae443866017671f196f4900e245490a58337bb28d37a57e4ac736fc15d2a6b9dca9ee8fdca5a2c7bac5f3d67c6728586f72e84de720ff69d03c8b538780c0725fd366ff3e81deebf78e93d42cfe5ef2e27db26a3794f3265064b6268aed712bb0c1a2e19251bde0873ea644ba674f76cfddb8b51e48b20b0910b956db839fcf815bb52132eac3ed01fb7855f482419ce4b2493d8ce54b32d49a0f897eb7600fd316bf3a5a89924eba764d829e090ce87d2c383ab4cfcf264420a294fce498e9091c2bde61615680c018bf684c2035a60bd6e2fdfc358dcb77a688968cd5ab6823d32c5cfced8a98656d6817776f0101a69e582b0d9408e2f9161ef1cad71df8d255d5541fb8ad21d3b61501694307"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7ecbb9d62d613a821d29e00e00f16635a5ba8c570c4cbb7b509407ac68519306","proof":"fee5aa1a67ab51f0645136fc9b07dbbfbedcd15d4110552dbd6875c14738fa60229b454833c75b958a26e348cec9dee9f83d15d2d14a416662787d14720ef30b969b77dca5c8212fc45776463f0f25677c72888b7eca32de6183a5bcf792bf5c5e4d6420835e61b04540743bb30e1b00c9f4412d4b30c709c9aa2f5aaa2722018943aafebc82b52f2d702ca9bc9bbfaa9adc981f181a13ebf786dfa762ceb60bdb6aabd7595e3492e6467ff0ce23e2fa26a8912927e2f2ebe8a0e75effddb80dbe2a033c770b66fb66de5f05c8b92f1b571891930b2e9a62f58eeb5820c88d0ee4d2fdc041d6809b55f86b42c4f615a590426da368673eaa1ea11cf54ac8266aa8fc9108f93c3f0e88cef8022b97d65799543a118447f45fe09cd9615bbbd20ae822117c06c4f5a1e4443f49fa63951e7a47193fe144a9bbf3c9e7500aa64b046481461e1d81d07ae2dbd5a0a5a7efe75f81db170d96ea07a93177366728ff7c8a116ff883d2d0f53f69aaf1247ef09014dfeb1e77fc78a35eb2d551b6744a20fe4129c71e071402aa407f0ff81286917d85bee749217ead8417132dad364432f2575e30530588c5abbfdd61699ee4de504c011d50bd516ac2ab92e87c06c9186cfb623ec5173a9fc200ad4822e485a26eba7f36311f8b26f8e5016e32490777e2f402e82a194655bb355599cc75c38b423173bd78fcb8bc6d76c15799510d54b25ce2bb4908241dccef9a9f2c3a4fbab5a2b3132dfc5548b0ad35ac078a1a0bf8a8a5046c7966177ffdf6ddda8b85007cb83cd4a25814d8fb588046f46af64ff236c4199b27ad4dd2f417d269a990a856819d38714c27b5578ba247025d420de7b9eba86e1ed97adeee08334b99f6eb0e03d2df2756695dea7ec77f95ea410f4d50f84f2a37e070bea2507bb6e492064156c002c1fc227a65b3e071d7da4b0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7cbf2ee78325924d6b180ffe5f5c36515c0bf480584f89bddfc8f30577f5d525","proof":"58422f589646542d08f8864d27a7330ad50df2c6381e5523f0bcac69929e425ae88bb8ab03dde2c7b486a0078eaf6f341e486346423bedac14927f17e54c52724a55c7389925bfc10b7dde8b1e286182548bdc39e2499a1028df3277ea8c9f052ea589348249eafc7c5bbe6e326c3811f164ba4e36555f7af6cdc6760caa4976994c450b578f5c3fbf928ffe75ea29169e8c5d41cbe0bd868a2e8fffcfb2ea0ad674116bf74a48ed2223d23c3039a724fccb31a1388a936605028faf7036000979c3202dda51a023b4945b01fdd49391c0c7417d5742427652a2b7d07366df0c7806276ba15497ca6223c3245dcac1001a36441fd4e5ed734d9fb6aed191292ec0dc12dc3fe6860be2a0cdf7d0e165bbd2a7e3ec729ff767a2f3dc85c6cb65577e291bad875be25ca6882206ee4019ef1be0928841876756caf697f49ca96c7efac0749b186310833b0e9048e1ef93d5562ae1f9a39f70c3f4da17cbaa715615f643a116b4efd32daa9e5d14559d6dad1f2862d56b6d74f3026ad4e7ae4665402ae1289c74481d34ba36750f61adb4fdeadc29a6424d5d7fd3a58697f2ee931672ff889a86e81772e05850cada60c9a6c13eae3307ca45dad566abcfe4ece82d2abf0140f1e50866000cdfd781cf820b6508dfa04d4c8cb9671e1eb1372e3443a26a346a8aec6d0dab8317adc005e4bcb66b5492671b541749bfb77c70176e29a61a0a538063d8a5b8fcf0060187587e305b93bf296db398faf336201e6ab76ef003f4aa76a4d5ed0f36f385391ad372b32aa32b2617c5a254e290f0f6b2ad609449c63bd07336d0ca0c0bfc7feb6535240a85567027ab1852104eeaebb56100830ae24ab6b1afd7e2f9446a2f0441615810ffe9981b26ed9ef02dd35a55200afa24299e838cf61a28b750257fc66175abda3dfa0c27a01a3e9804e53079c30f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e87e642a4cbad22fbf6b956f00fee129010e9b8e08a1fa23a2bf0df13cfcab30","proof":"6ecc8492c51494135bb397d63087245163ad7a55e5953e03809d3bf1caad19760ebd63d27f31855ecdbe37b3d201536871003f0fe9a74848525da559b331a27c983ef5ac318fc1757956f80d49eee348868f3ede7e83abccd7ec4a1ec7720439483ba26bf2612ee9a2fbffefb17c748b0769d62c29d700a1a819ac58df95a73bbe80ec1a7073f002f56540643db9757ed085944b31036e25d5e1a58e23319f095c3ce4bdd2ad13f8199bb4958b41c6b188aaf5329928ddd793dbc667fee70c0f2cd9edc6a40c4ee2d59972cec353c308854b12e6758a9f83736f9176245502066836eecae8ea8708c2a57304cec0911421b5d456e7995e8098f3f1f92d92c15de087b6cae73d1bcf041c672d7e779847467cfc4d8f56e4808c3c31fe063d8a669c1f26fa977ef1a2a26b620800873fdec40ff5315a49e3b90a728e8a7348d17d48712c9bc55822649381f9d23e4333c866ecfaa5a28156b04d57c546013b0a5b0af839c1fcd22488a4840c5644684b55bd49bfc29d3a46b693b6ff5ec3b6d609183cac68c653d6ac7fc7714cbaac99d3a4f75ae133977c6807d3d48e6c0c4f3084bb092253e868f3953a675111f304721cac56fe32d3209b2fb721416dcfec4d7aa64d11f8db86032882ee94176975fee0d59a81129cab74c159deff0252256c14cec21be7682f10450a6da049ad0a78c4d5dd4e4ee07b5a732e1bcb6a2b4d5db254a4ebf122bb2b64acb3b2b1b1d1f47f7d1c6a5caed4f5f209a86580daa02338d867d2dfeb781f24f772976a33623fe996e1caaef6d41810f97053445b57716e0992fe0e056b9e07a81b1cd8ddb15d8f6bad83e34223bacfb181c120eec246d821d6b3959d7a7a674aba5e4b6fc5cc22a9a0b91c4ccab08715ad5511049e035eac23d1b4c2e5a2bbfea1c26e182b9156c5446f5f61e0417b3c5b8b08acdc01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3262104405cf207c76105ac404fbd9656104fb5a282dc4826d0ee6349cde9e31","proof":"4c241de11ec5f0068b0b1489b14541cd0dee0ad253abfa75504c7b5ccb61e37e4665f6e38edef9ba26125d7f46869824d4635eeffd4318c9470aa2f3786fe372d2dc7424833faaa4a55c1a6ff665abab2dab41c26fdb1b4abf28dd3ee6767e4abe70704470f5a22a85244de1d9ac47da8113d7dae7f1a115d187b24ac89d594e03c8c4af2b53acb20d29f8a92eb988785c0db8634f44029f8224d8f02bbc830bf064dbea2d3a62e7fd36e791eabe1791cc1c124f2299b3f30e249a82408d5607617b8e7aad0d109ef15db4d6659c5a36725b0b91e678ca36fda753bf46b1ec0832c908507d90fc413cb148d38220e0a115f4abfbe101ee62e9e28469cd96c9382a24124340e729a4fb8addd47554f59ebc931651549e278e943bbfe006d6412dce2699fcb54ced0117e6457a5087397f6fa01456a8141ac9308d34be9946ca6fecf3af66f0884fe1d2a37bfc49f68e260c30e86c29cfdf29656df07a9ab15d569e4f82f03c2e997303807b2f6ba972ed88eeffb1a7062fa5e1d186447de8ca27f015ad1484cdd1028af674ce683864f373f059b8eccc4452a952cd29be014771bc5fc9128b44f7c078e33d4c56cced25cece0b8cddc6e7e0700065db6c4b6673ccc1ead8ad008f4fc4ee99520148c780e8f6a50f551dae8a41470260b75cd425f814736775ff1b859c4c5815c51409ccba5a06e17e13e4e709db5a6549fc2b592a570adc7beeb88deb90fc3ab6b540ccb4e11a40b1bed38f159102351e88ff1738e0a1db47133fae78bb69cef16350e7b1bb8d93f991fb3fa2b1f08166c701734275ede6fe7aa62477e8342d1d1e900233525e89b4452959c82d027553e6c03b0c8aaa27b66cda84e9bb2bfcbf53789bc3724ede146f13c4af1a0c6582ea7e0fa57e867a8dcc444284027f98a64f4b9d324845ba5edbec565346a49d2deaba0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d21805c856b4bc58efbeee2eafd366350b0483e265875a3d217324bb099cfd7d","proof":"2c7caca396332f9f5668babe60db4372549988c433caa38ce7d2c3462529b462027ff82e93a8ffb244ca0e3ff1b3cf2eecde193e2a4daf06bcb3d7f6bc4b295514ce030cb898880232057e2d3e7721578e88de5b390ea6df915caaf62c7dd10e5a4b2c8db924c83cb23875ebda4f49891ae28f1e56dd58d51e0c1de047ce1021e21df173c67c218f6458ad3bdaf4c9779bba736ee13c78ac3331abb30cf42905465e96ad7c7308d6082d480d28a88bf9fea93004c6b113017445f7a1f34f8e0242a82d3cd8c41d2b0bfac050cdc9595005cd647c92889d952c281679058699041aa0ea065ed5ac8535f9a6a7da2135031d74d0d93e52d4c540b00e107085f31e88408723f807da57e74f7bff11c99a089c116a05738441af24febecfbc35b3621c47ac259ce0ce28a4b28b91df5d5b181490674d48a2c44cc494a4f87f188f01862bd9171b3302373cbe435a46856fac994fd635921d9f679b1c81c7d931ea6270a68d6366fcd89de8727eed01877d777c87a142d286b44cf7b7d6cb18a005056caf8c5f67dcfa9d9358ed415c8795fef4120fcec03c46b4a5a61a88fa66ef72e2337178e120c218e21e62dda590a74a95b82a8ceb42b4f02193ca5395a07c102cb2f234dc030b35c7cbf6764eb74bca795b811dd1c878aa44c4c3254ca18142ae7fbba1ca1e3ef8cf6713061676eb45f2f5b7dcd25d2cd71c7a2e06ef06fd5278addd02afe33a00ed86ac4a84964832f790bded16973f682d1db051893a1f4b7e42d8cd52df7045bf052e6b20c742101f90f8d654bb8c513cc28f696e428b7dce7ffeb2f0ca4abfd8655126c9e5f8b794ba250066dcf25f58c2dae38f4aa01b8d0861ea5a39e9f457a271ef28f43531a724cf66b8856e0305dfe5f2a4679a0d0ec9f31d583cf2fc3ec1645f429a4dd6571112d26894e322f0b878731936af06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5625ff973aa0a974d01b3eb720637e99242823b934e80ee40e252cfc213fa108","proof":"da06439dc0cd6804f14159597bcba04b6e87fe25770e4569839bdf381e53955d20bfe7468d5562304970cd9c14ce9950d0662d52e7e10906e6a5a7e5d64d515c7cf9884d6972b2f7ec19c69a6bf6469c0e3ecbefa718ed5f5867afe59bff720b7cfeaf2b091a0e1f4839f1dfe754c5acebd04fe4376d585cdf2ac85811baa8795974a4cbc77cd7305ab2a4c9e8bf8cfb1a96e14b8fbae90d6bf97374e2e6e90d6e824787318bc634868259364f10be31d2b8d854901924671bd609a34592d109846ab54bf52bfaebff98f3c60b679353ec18fefcd93776cff808c6b855a1ef02f604652ae9d9f5b71af9c50287bc56f736026b922a97bf0c2ddabadd77cdf20814092403eb45ea410e6a2573ad67ba03179673e5e523628d6632538a263b2b6f42c4d5d8ab3f1e0f39ff2d80e68aa24284397f6fc88a4e7c602d97d135ddd60ac0c479beda440737b7ee7b3e15caed1ef3ccd05d63fa7199d63fae790885931cf60247406fe678ff02130809373a843a8f3064fa41a65e058ad071191db708104404b62dc5fefd4701c3c131c17f72c516e3cae042a8a0f68a610052412280363ca778163f19917774dbfcae25b2712771c9e03d2caf4c75eb3cc5a71d87f31c140a0b96b5cab14090092a87535ba876f189b41371d7187c6beecd77a146634dca4b5c35250540d88229369edbd7763726218208711e6f67e97195ebc015f85bb642e17a1c0753413484d21d393124e6806d113d65f2ee62be2d64dc9b56215a9ceb1a9fcfc47118a65ea154f268354293d1c176bc73e40b7de4126d1133723b00831ae10e33bc115935a9ce19401ce5e17496a476f46ded7762c87409f3b578977370ad4cb6244b52ba2accbfb2ce2cfe5d98b9015db120c38916cceabde60bb0f09e7084e82b02f4967388302515074fda812b7136983122d46e7defa61803"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3ae23c808d14cb909bd84f6f7eb92cdbbf781f44cadd61aa7b6e034dc07c6719","proof":"101f77019a50ba23337b1bd1d54617e6e342a57c656c52c9815e5820c6e26d565c77bb770223a8300ea1668c7e440037dcc0a341af6e7bedb3f0482fc756e279461e402c9401abeb8ed84b247fc666ac6d75809531131274b9cccd4642be1957feaddbdb080eb49a55141f449759e8cd53fe1061552d918597d24e6d24426e7513ca51ff3dd64e98addd825dcdcc391402d3619be7b8a7090321f1723585a900808aadb9554c60ffd191fb4ada476ac1daea625aafde740ccd8bf4c96aed9b024a31ae1f7a91afc11679585341d40da3d275df758fca67c9d635fd3077abb006287f466225cb31d593dc4b42ce771a006fe4cb5d93a98373691e0c5e8febcf2a1259fea7d5f25fef1d646dcceaf19538ef48da63676ed3c54f39f44d8d304c2f426e6df4ac436f13d58002d167f8f31eeb651f55541c7ed5bf9c5cb8c0b071133456b9d915716a09ab78c66e3f94ff27408c11d7921fe61fa7454a0898fce41930b3b945ec058c71b68ec6fcbb1c729f78066e6ce24a96dc5fdc85c0962257315645676f8fc5f6e30eeb21a5d765ecb5bd41dd4f3d543717cb19ff965bb780488ce6c0aff1953b3b254f401325c6c239e7c86cd2e3c31fa20dfa1f260890b23d989949b49aa23481cf08049eb5825f80b3a149762705c2007fbbeeac36df3e639e63b9f5f37304f3b4b523a46a5b0806914905c6639e7c39ac50054acb967103c68b484bb2815395ff0081672dd5cf026757c70ff3cc0542312747f2fcda9041de47c67fc7bed6b3e91a87f149de443709f58dbf6c3f7ec9501400163f663e3066cfcedf0a4f6cb2097dde352ac9d932fd0235051f9d3804a1a0e8ba3a2e3555ad817260118c58d915d116baf492c09a5d364ac6663fbf3f045332fa90834a00a9e12efbd8bab72b95fea7c2c858600d644d12924684590124a2c6ed4fb5f801"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"34454a1dcbdc57fc6275e8e0f3c82d4c1a84125fa2c741763972e8c374e2a504","proof":"1eb78476532ba7a4636a03dab532cd728733b943a12f0a3bdc0f4d35d3a26265f80b0b96d153a2c2d06045847a2dfec8115dec298bfe0a2265c7b748724c4f3b366d1f4e8bddab05c8399fa6b231a0b6a1d2368a91e882f1bdf9d12f56d8881dd2ef96b1cd4ad432f3230a7391c18c6b5f478e71a8a72e9f6dd81ca4c82627344ccf2579a243364875ccf9b405febdde76515eb78fcaf27266f5bb1044e3780d87b0350f716d30e8c65bddcad9b19a458cd23bed84c14d3f9e00b7392bfe700b70701c3204d73aac0ca63ff373d39bce6e597ab5160225beb2fd1655c98af40a22807e3e2d5ecf2b36044ae5911763e6f0d77ec455792db1fdbde619ea7ebb396228d407807b8b7ea2810aa5e405aae067479558be32295c1254d3ce69b5174a3abdeeeefc80d631fdb06592a66fc764c7ecd405171152f9fb7e7aeed6ff504d9428e2943aa289383df25d1525c412c421999197ffd6b69b8d8bd305c1488e38ae921da86fbe00481157f68a70a2a2188a469f75d5931c73046387f35b19c46b9064f5b72985ec00440c2bb9fd9a83afc0786d8805815c69d46c1a04aed4fa5414aae6e06e7c1a8f59e2f27c22c7fc275e141a65bafe528a53da5b30b248d15b1259ceea07ecaff61a6604acf1404ef44c3a762dbd7952b65668f7b6573cf75422d5a8fa05438fdac39c9ff0448d887a4baf1a0bd23612bb7ce6784370289619a6b8d694e16800bc9855204889e936497ae5536e3809bdaee5e9a33f89dc326766abf4c6f90c6815621b13612e40f92b4f660555bc1dd598a96a16979a4802106cda09865e22e8ac55118754d17c7c65b322373cca652c6e9a55c670ce953013ce938af7608ec0335535209035335c8d2d06ef0c2fe8b819770af54e88aa0708b30fa887853acc213be4e99798e07a68f66015abd1939f6b3b906e70f4cf170d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"023ddc2484f0a359f1ee9b2d522e60f2a60e52444022299ff217b4c069466c37","proof":"98933228a7b50937d2204a2e54a77d99c6b4c7cab8bb212e81c4c1954b0f6964de35adb29029174ce8cacd47c17cbbe08e01cfc89da5536f804e51613b96b60de2255f0cbc5373b6bd58d28853d141f17c792c9c0e923fa4331e0ed6ac8f0e7730a2e178d8826bbbc93220b1de7653de02d37365cbff585b3025c9bbb547d1003fb43306778a865a95e4e81d26213f310f0e79403c32b69fed27107cbeb4660f3f9d2711e4c6a59c9cff1d21dc072122e6511f1411c895395b026e2eea8fa1025c3a8ebdbfe28921861565d76da7d4f2eb2bf3360a3eeecf70e56d06a5aa7300905b361afccecaee219873c3307c613763b8a6db3a6c6b20d4cad50f20e6a552e82ef6071091c1e36e8705179d6b8caf3906d5fa91076c16e44e639edbc6130dbaace7f6b1ff26a8013925baa2b23433170ef62a2260143b4098e6ea536f5853845348b3c12a7558012f1ff5ef1be7f7b52827fab9d414a08bde661a24462d7c0ed1135de55061c8b14b4ca3f7c72b0dd5c96d87c657a1ea86a5e4a80bc4ab6630689641bf662e47c5834a21746e6fd81120d9674fce1fd8a4afd436b07d201a30d59f1f411bbd4e83b0c41713e70aa0a2dd3d4bdf2714acd44ecf04575e4b7a9615a6e70082b6b200c6b42a675e05103da7a2db95b0d24146307bf9d32e466386a72f6a6eaf654fafcce103b90b2a7f2ab2e5218909c5f3529d5b9d2b9c4e432830cebdbb49c03ad510c13d58ef62e4e435c6bd593892b978030957a193737e34c09f9d579045c05daa5a39ca9e733b3cc61b8ac17ae240009ed3a135f53d30361f76db362b5695f1137bcee833e0837f43d3784dbfc20194bea25a9a64761798e38f5e85030a0afec285f3fa4ec7ca0b879f438c8b6fff4251b02dc7114c0c4104cfb36e5dfc881423986b89aba9d835b183575eb3c7deecf533d6b0e9fc02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"02be32f5045f818d1e1bb5a301ae2b6762965568fb2c138f2c4ae1933e212c09","proof":"e2705ad2ea71786824df0312bfcfe052a4eff03d4a6975566416fbf2db961363ec3df15a50d48602f4d7368cee2501c918d8f804f53c4c253518d0d3955523242ec0f14da57581a93efcf9ae25902c2b1bbdc08e66fb7b93a0fd063eb42fdc4c2075daec7da778156d1bef3e0d00293bc7ef2e8209ed16697ac41ce7a9d05e268bba6149642cc6413d35fe098f46ddd1382f172354fa12feebed1fb8d489cc024b5bcb396138f64b7e9c24c0e8604d6d33d2dad9944b90ba06eec4512f19240c8e522037dd9e5d74950de015a8e5acd82ca6b55f7c4c2944bfb9bad2c60584082e70a11c3d18e78509b0e0136e56dc34bf58fdab89fbbc351c87fe9141fb3306f4851da8b223f81b37a095beb4962a8f211c833ca8e11a2815b5500638a0581dc0cf5386630ee8331212629b37d7a5fb8b0b6a007fdbc4efa470309dcddf9b3ed20ad8fe3751841a97a6c48c0f6c6dc7e7d773211482c051a2c17761634379017c5fec8bc06caaff78d601ba0554fc25b9482c2dace00fb4e0c713a061ce453ac01448c1c745f984fde12e0e5edc93930a0d9b0c803a144e7c9ef2b6b6aef079803dc0ba332b682e427b6bd7ad408e442ee0bf4d49a5088d9bec9bbf9275c950b244460bb9ec4610ff9a3bc79d3b4970ad6606df401b6cdc58fc24e28de87e335e5bf2dc04c74d86f78591af7d5b718530ab8adce3a9fb35d33c9bfd197e1e2148112a6709b536729d4f42cc86eaa046323a2e28c44ce157f4e92f3e98fe2b0dd05d059e671496e03745258176fb8fb3afedcf897b0723e692790bdf6999d44db480913820e91f08fa31376b7a83534650707880e6ff7f1d5f074266384b6a30229e7da66051d1abea951dda4a52df055878568700118cb8f233eb93ed336901b18640abdd4ad5e5cd9ffe1be472b5b91275d31b1db8d2609ab52491ae5aaa03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2e6c1cc65d733ab4de15e1f09b228fb636f9f4843ea4b042f1873b53c8ae6554","proof":"621570d9994e20556152bfa4078436da256ed714d9990be59950bb8e36e85367445426c3f5842b712f610c68c35d225b666334c985bc6aeacd37ec898d78964e6cdef591e18228bae99e53d660551618585a4eeaf294b32883968781ac74442ce09dca5dbd062d205baf3dc56d5b77545594f4549da568676109ed90835d1f7f39767f37c38c32bbdc6d1c2bce815408ab7a0f3f23fb7a065931ecc195eec40e16546b96261c719eb0aade0378382678cc43cce2cb4aefb1cb2d911618d6d8070aa7060a3806dca6c69dc5945e348f33f9f236e86365725b80af419eca8e120496e3639d5e6bee7ba7fb4630fc18390e8583c66e38b6b656a920d5f001bc27499ea4eab5a0a9118bb21fc4456982879f3d908249f8095dffa5695854972a3817687ef456a8dc2a95bfbc82e47b9f4e0bd8f518b0ccea031869f2b1207c7ab13d60492cc120079a7ce8d08e4fc8893db1e893645e64090ff35547d1014d0db3665c37922766d6d3426fc32d2c3257d0220292c527f47d718e0d63f8377d626c61268b98df13db17858dc3b2450cf1285f953ca204f133b1a85c64dddef1f00d08beadc28436c645f164127e1be830170e3b4341b2487539d693118f482af2d47d50b12467990e885eee4a45c16f643f346108a0928df1374d8f0beb6381bf56648261fc2ffb7b7954d61815dbfd01e437590ff999261f53cbc8b10f1a1012bb548ebabd87b3093a03e6fcc10aed997d65a9fed794c50866d206890902c6936e742034da5cf42200a6c6bef30540bc8fdc1b6eecb2a882dca74dbffc28094967589cb205f18f4a14639fa1d8b831774104b22dd12f79a29f57e43020669603ba006a43eaa1699aad5c45a10d1fef2baef145bbf6c90b32abbbd879fb6510d5c705185dc30aac4b7f3fb196adeb136b9921a52a8c0e2df9b00179c38142787a9f06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0abad0cf9847f91373d6335d03f6ac31b1e7e688fed8b925329cd1ca3d23d511","proof":"5061ba0f29010c66aa5c61589a7f27f67096aa9faeb22ef4df0bb1f88d4b2f3c141ccd400dde3e1f51101bd76bdaa77e29d305b1748960a1e5f9c04cfd1c5b72f402b8bdd9918a8592647a40e9d68fd2a323d1d3e5dae9f399fc838081bd0b5784350d348d529de58b73bbf08d1b49d1fdf62f34cc8c3688040c695e99815f458f1ec6ffe4b23236fd6da758e28fb01b30f3df335d771ba39945385496196001feb56b89aed9e8bdadadf5db103943a0fd014c525ae952f28d296fd7c980e900df8ccbd2b42f5a7802f25445624877c8d8e870aa816160be5e31f495cc8129050234af821d889bab36f82a4e1401f3c2ecbaac9c9a34f71526b426316d3b12459850dd4c022263c9febdcc4ebe9ea888956913d8ab1f2a589aa01c56a2abb943c440670cc8c12e9654f6773aac927c280af58ba42fa510948d25494188347309689e7f7d4cfd9aa25cc24a026ce4abafd412e878289e84df7d5e4f9bb084a6268cbfd165f5be43673699ce9399a1cf6e3796d99825df4b633145b83b42ba3a02b08de9c97f68d534c100e76e8b17188226da715ee8f8416e071b51923a994d6f9a120b8938f3d3c3803ce28fd123791b612a6ae99724f6769c3136f3dbe4e76412c26dfd9b4afb65970bbfcaf2f2cca8863760716729e2d40ed91e94ac83dd47aac5e36c32123a8765d63c970e672079c37f9d5924b04a845cba8a2ee49d0c4bec15971f102fd38b051f4bc17e4c2e586d6aa70c2729f9b7dcc2fcd186974955d06d67559fcc5d838016b9a659fc32d3f361a52543ee125e78ca07317d5d2421e438caf8449e19a8bea56d7a6d7920bb4a9f83414f3d14059bf15a5eac87d26f00f87011e81c15a93e3ab97c28385eeb1e713f8de89d23133e4fb05a4cacd8034092f2f4681dc461399408cc4257486dc6ef9cc6b57a7caa0aca494a4c14d109"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"06e6ceba3276a65c727eac0563c684d33224bdccf65aa4300cef811c597e8a7b","proof":"9211eb554aed91bc2431537d4f8ed028cfe926bd125109565e73be8a7b7dd4301ab86dd599e5e4813b98235583ea3e441cc14eb9569e9895f7c6633e61b7f738f2607aa133973266b7a195d950ff892fd82652f94dfb4113b0d8955dbfbf2c7f7441a10d65a6269a546421c71b3925f45329a6939838794546823711a3d79f5206b467e4569d548fbc29f6b33ca454e9b26baa6f99a7ba0e644d9f62d6d96a0f136ad762401465052d4217f70cde7c20e597460e5e87b9544fa97df9313706001f94087cbd5bd5fffcbaa28fd757f51848115096ec26f35a5adae749f114ae0fbaa6da6c96d0ec97991ac960b064818207b658233100a51a8bb1ffffa439122854707c19acf89793fa7b6a70dc446fa2613d1919712f21a8ad645917d27b4e1d52c3b6077bb19ea93faeca7e2cc6437b825d9595a97ea887ee5a6e85e22f214a488139f633a57da62e1ce7af3b4eaccd8d538db15ada91398b97ff23431e5b3e8e49e6e1ad62227c723efa04c2d7e7672287d2664fd8497f409ec1ea52a5e073d673af7badfcd0a770c77b91f23dcbd4215f3df3b90d2dcc5fc77be7c2b78d74482f4b0407fe0fe1f4737ec0752ae339806224db1651ae1779032d686a34ea11a40ff458559428c8c40f57f131d7ad1139f99df4a1889f0748fee05a89cc4e3870002660c602dfe92412e04fa82fac74581bf6a8a3682a4d4033f86cc3dca933f2a765c25049f97c7c6b5e71e3512df0265e006039fe8fee358de91d9fd1e52f60dc191e6b269822947c1157ab6708f237a347a529643dabe0d72306faeebf5d9ed48d90bf01a57822a0a9117700f1d80bdd8a6bdc78cd9af8452c97b8394b23f2366ce126e17a87cb8d164e96d353cdbe7fd6e15249e287f0796b29a16b570b62e24c59aa047d3ac305496c3596363639026f0316e3c4caa9727e95b70edc05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c255f3bb8e89168573ec63c1505aefe564b03151467f772099e06c346118f824","proof":"9eb1edc8066cc56751f9c813d4b2f263c90a4b8d2208deef1676456355815b61788103821f6a69e28603d6a318b311e6849d17c67321a5d1be8125826ea10f4c8657dca0fd25e6c4dabca15149a6816c2b72ef3ec7026e106523207592e56132e224a02e3a25bd46a3b842005057def2a1098be8451b64ace7be1eb92392e1197720972c87ef241924b8d3af70099224f5e3105e96d22aaf03476cfc20f5790cb41c545561a55809aa6b821c480a2609059b61201bc912de552dd2c8d981b507edab08dd4f60187a1c4afd48e5684ea05a81d4be43be9a391de29ff63daa210808a09256040cc3630c757f7a4f058ab15a818261cdd3481d511ff5b23dd5da355c5b311c2a5ab6b025feefffb8100211f117b228f6314130f5126edf99b87d19fa0a41a69f74ec7cdf1faa261d620887ea99b769c6a33d70a822e7b305e2697cb03a13ea0fec4d867275ef8690505835c7068af1152787d61ac81ee5495f7a5686b7ff9fddbf4285ee945d1ea88b7e30e5741c75c618f48746b492ba00cd744104d3a6cbaf9a0db28a3acd8a88675a579e31c4225f577557977a1c1136b91e266895aad9d27049e77d152c9713c4ecbdbeded734821ce80b3a249e576f60703d805c68e44f66240c2a1760facec14abc30cefe1055b4912696d1cef6692f831e9a2bcdc214e705d32e2c5d5303641d831cff20736a8bf02d4cf111a520bd977574bc1cec0211d3b85367a8fcce51aa51ed5d954623dffa229f717116e31ab00fdeaf7a2aa58b87a0d7c8c83847597ad392e2314b3a148f676d0ffefe02b0ea06886e16a8665b5cba41c22ec82766ba534126cae7ad431b686469e0f494424356a836e1f10bb3aed049282d1bd1bd3ce43424c804e1af44bc6eabc2cedae3260a3cb59fcd720a500cfc47fcd083354a58989029e99a19d7395bf559d5eca3f00a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c09e0e9b42598a4ac04301dc3423d5061b1e29f654362deb15951b5a5803a833","proof":"b63ff0c75d8ee6350185e8b6b3005b8c5e2f921432212abfe6248a48e243924f86cc23ad8950916d3fa60910352a5cef198b8b2a50d3d6f0121fb0ad78758b54b4024ef6c5f3e54c94626adbc1114a9ef09814f1b516ad7792dc845fa056dc5cc0d44bba13f000ab6089d082bf8b6fb4e73c1765e3e57de90a193031739ed7527f26771f043eb17d93a34fb8e024b691d67ebe4f95502cef7388298292e4210964d8ad9b85866c34f17a691ae4dd0333b44dbb12a95031fdb3068456d7731208ebc3c14aff42c8d43546b4a0cd72b6f3e5b531e19576374f34aa08b7ab4ed9037ea30789fb8d4d71ac00b2c26bd58a5b0a8878349656b55715f645663e32b23964fe5aadee0c70870b6a1d72750efb1f1f64adb4640736cf2a1bf55a4383977da4312a3fbf5a2192406e2b5549af64224f854519b11db6d861a507e02227eb6b307969d3443becd5289cf32f73a6b3bc4add5989fffecb56a0b36ce710355733703f5241034fd4ce11cb2fc4307d9623ac83ed0deba3b8c96fb18f47728c1d213ac7249d69b5b546757ab2fa569ac59481008eb316eac0ed61e7b0ed3e16de3fc692e01f1d23dc7c5437e2e8623b7fd078f21de6b92e7a938c07ea3275e4e90c06805793521da81676254552e5328733240928e748c4c1b90134dbd1675f8a4e142fd67d0b4026d3783d5c391fe2d7fd1b0aaaa46dd9473f18dd46bef3423827b4b1dd0c80dccde26a4ddd8b71877ed7c9f33dace0681b3d519f211a5fcd854f22c09e7f37080c8c81c826635717b0aec5861bf533f548eceea3bad021aa3b682cbcb21c4bdc7caa8e3f7458423d2cd483022f4fe487625da77965e7bff30c4cc9998c46728ebf92b3c2b047a0153f30ce74bfba267a065f1e99b5d89671b105f5b1f3ed7146aec058c16ed710c292725e13a1cf30ce2badb3be7707770bf701"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"24a843ea0a14efffda4e3254ae7d37d2ef957db48596a9baef6a029e3d4da914","proof":"a8a40c9cc6c3174242b713df2bbef33432e202369e4e0c12d4121010d7ad0c7d3e38bdf21edf67ae32b89ed5f5ae6ae2332fcc70800124fcca0976bf54019026947df37cd907e489458ec3abab77abeda30c8d14c85b31eb32e7524a9a17b83ef43677b21741459a6cff4f58b7bf0a0716b7e5893fcdf47c0d0d5d8724970e45e30acd8b7b4fd4112c9b52efc44151de6533ebdeff9287cc3ab50e4c7d73f606439289d0f6389e8faa7fc71da2e17ac7509c620446996287ee54b6311d7a5f0afb514701cfaa65a2fa64e8cfc182f66f08d31cab6ec7e906e52275a52af5f901ca3e9e1c7ab5c3d3551031185fd922b4df30983f083dd30adebf38efb073150dfa03c43f29c489abaf97c8e615e167b0b0321a732c684198412fa5aa0347ce4e6e72632971bb2eaeb3803907f8f995a55c08b864f029870a3a959b06ffc5917cb864706cec2efa084024a02e53142226a15dfa4c2eeda4a0b2c74a2ed2a22d2c3eae0a3184ea8468261f029f4124fa3dc99fa43241616b28e0e0459f948d193df865f8f9a31743745c1e5e46b44cec5ff921d13ed82d4ea24b7be4f72db8173032b448cb648dc9f3b35bcb30bd5c4d8103be4a05a904b669b29ea87cbfe5e261f0cb36d3182e868911ff698629660d8fcd9e231185e7da92161249493379e56f7228b2b1bf47b9eaff69a9f2c5aca7fac122fb848941f0a128e8a79d096a9f49088e55d4a8766dd541176fa8f7af0ea1e2d7176e015f7190e4bcf5673a3b1c7688ffd8a19b37f783724c93cbd3d98738f35d640df9481a3658d0f98778422a31226d5c835cb6a910f8a98bacdf320a1f288ade65f3141f0731ba448e74cc64153649c991fd8231baa1c438fbf9487837b247333102ab44bab58c1d91b9ae600f7ade079f92111b9c0c39ff506a5cffb2cb59f1fb7cbea122e55ef281d5ef3d0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7a3f5c5beb526a7515b879bc8880be15a109abb6744e60b0c0e962b0254d5076","proof":"04a0f9d3618cd3dcaebdcd2176451f22f690ed9305c191e8e9ee1a86281da07014c87b4e5e95fc790d7e23887ce06418ccc968cb34ee2ed814eee83b69c5663674b510485bafd3f5d343ffd8f94db948fc4c7e29a36efd8016e84a48de5f177a1eb85b7bf2f10bf0d125ca4a9d88a78e7c9a1230c66cf63c18f0d4b3c38f8f3cfbe2bc4d3981f6f28b7425989b07ec25bccf71011d754b81bc3d4bad8c631b0c639d6d240d14205e8f4807d7593a7e41c5df4bbbcaf7fe0d8e2e8c0627da74097a8fed6193987804fdfb5aca591992717a95915c13ff191ee847859f21ea67070e1b033a7a53c10a8b57a04fcae756ed3bed4e51e135fbd331e817ba1799b821689805a359e117a0e0e72d7afb8639161bde842f819a821d6dd6efad7511f002b26f6a8e6159838bb6a178033e8ca24020c8920a4ec4531a808185003413f863ba49b00725f190c8f1b07571c4b34d132c2483620c4f983dc29e56cfbd6d993aaeaafd7ee2ce7b96e0ba408f4daf8daf35a4509603a73bed133c2fd1f97b5c1c5ef1e03c9865783bdf5fb0d4f05701c1d8401fd75c6a776b0bf25339d245c4548814c64bc5fb3ee01ba22701579b8844747eb54e26acef794d78148a800e4b47b06a5bc57c5199b08ede7edb55f609e7704367491ddaee492dd2f823a170ed64d8e837be95b5a7c0e652fe7ed0effff17f584d5caabdd1c8693d70d92bd98d17fae074a6d7c0db53ca9f6b1c0cbf787791497dbd66791cd0b940d19836784b158c1a23054b87981f3346885efcb1084615211439ae17578e5b8eb503f940df39aeb36c72fe78e2f1ee0d999587df30e663a942ed0f7209909dbbf4e51f39857538d834ca2590386889206d343df227d3d1555c50352e88af952f9323f2c5b908ab4654474f6f379a4ec1826b98fb4526ca935b492d9000593e9ef30964ab5601"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f4f1e56a19b301b5cac3ef771060e1ccc6f4add5f1f6f26eecb8bd6ec6357749","proof":"9e7a3499f83b45fbe453fc85874339fc2bd4946207a20170c1a9157b8268ef0bf077411ad8ccf5128d1359599a04d1e159584c80d80ea189c57b97f76892a15a9e9ae947d472f58fa84af4a6f11f51481d5217d566bc6e6f8bd2ac00e933223bb263428863fa2e2b766fd304de8e9986b7bc8f14cbb2ebfef6505fb152d74964506c2bd382004e881489f4ed8a0623834e862530dc081ee84cee0e29c159f7005028271af07b509f67fc58244963f92de889b11e678ea9aa2041081433210c0af9f168de601f471db126560eb779c906e9359ee390125c9f84458fc0fac3290ec859b596802e75cdd1919cfd348a68c0acfbae809f72d35d2f7c84acde2d4774fe80c3659f043b051981308e2515944351d279ce9c0d5170e8235ff5f545f038466b4c8823c42ae2f2825d0de46d78788ce714ef0a0a8a2a6811b2537add765e7cd09ac21425fd79833a40ae54122ea663c900bdb26eb91bc98213cc43022c7f007605dfa79fad25802ceeb828cbc8ee46ff719b9273733deac3c9a8b07952741c36cceec1f764d4a6589136af69c64c89c26552f08b312425571c1ba8ab76413012c800de8fc571252e14775f34eb17c10a2d05954a5127b4488d919d01e820ac237f72c80e1f89c83739c1aae434b233ff8ed7ec9b0418c80603bd3273391b62d88ac1af37489078082cfddd316885b706f4818ac29d11c766348598624a30727b7a877c690398aca3993848d854f0353355d509dfabb1c171eec3d6542c3122557ee802870efa8b77f47934fa49ffa9789694fe9164f0f637978c3b4026529c7be1698813f03394750a33ceb391d7af9f22c966a7393d2e65e7c5be18da53cb6b3aa678cb97af94c26c19fdbe1d064b9d23e61dc4ed4ecadbeb224dc2970a8d0dc564b684f4564403fc3f97f34148a73ba96ffc73fc781c3708ff72337203"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"76103afeee65b81dba118506fd6b53633bf2987af4353a9a98d491757cfcd55d","proof":"58e81f1aabd06e96dc05d526c30b163059134f471787930f16521c6f4769475c1a9c72c60716dfeec062fde3e36e7bb3e6b8a44d18914bb0767ab3ccf17c6243be607dcc24a52452548151de7c9a8ed8303a2e22476157a58fd4a5e8ed69681ee0ab85c319ccda0fb3a07a9f2863409d2bbcf7d4b602c88d306661c0b3cff36b5762a1cd13ee286e5495ad3301924e727329324cfeae2aae741f2f3ad1097f03611ac629ead3cdd35ba11bedb37107ecf977c64d9fffdfe38e55f25d63f63304f671926738d74437cd029ec8f6fca130a1c9602b3755c9f46467dfe0fda3510fb01dac5e48b5a1adc8b639fd3d258f8f44568fd2e2c50244333e668c731f4046c896cf1ed898a9dd97f403301e647ecb8dfb5a9292b91a45653ec1fa6cb6fc0edcd1802605545d25d6cc960bba79f14262ebb39ad7b474bbb1b6931b5a9aa16f063426122edecc3349b0653fd55871d205c28d3f8088f02f63af996cde1bc47d02b41ce78a01c0b17051b9e981817c48ed502354c42d801cd69d976b8d14ab2386200930ed46734ab932676044f0b84edf07d7e6470123a208961f79aff1e22c581499f8d3338979f2ad746a8a7ab639cfc8cd942a5bdb70817fdd2658a75a3e1241ed8c5bd1c83e897323c463ae3c5943232e6f6d6677e0eceb31544b712c292ee050c0f271dd164d5adf51671c078051412d6cf096c8b0ff561eb15b370e39b8ca32255e310e2462f963a0553498be14054c17d74c3353a12c4e37a06afd4d745941136bda97cb934edd57910f30556d1e2852cb0e1b025fc1f0473d281544026f5e3e1144b6ed4d9974fdd5c80f9864f9bdf21bba75a7477894b894928904ff71778b5479e02f0f1f290ad75166d7618de497c0cdd42c83e5df0c8ecab101e32bac71f70e2e092e872088933c042a5d6809df1237d9b4ac07592e6e75df08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2621d65e1b08b186134443b8b83219751b24583a16a22af6ccf7ddcdc3d27278","proof":"4840253b2409809a7982f4d4189bc1c24448591d2ebb8735d9724ca39f3ead57de248bba85715a3a0e006b0fea49528da444ff3c95541ee8afd035669930e44cae0cb65578465e97cdec03f5128694024224a9abb32a39e9da7d7dc9aa255d74b29e6ca2723b707ec8affeab6d36325c7f8d5b470ef341c4759595bdfda9923e9b2dc1942b802b270c5cc1d3f3d09ca1bdf054620d7fbaf0a0188dcce36d5d050154b45af5cf8a18deea2e2a46ab3ff7eb1018a5a35d2c17bf86a2341049510bbe3b6b478b9a53e898a1ae93c81d9d132dc5341100541a8d33dea07499c1450fe42c9dd81c95c6718116eab73763fcd1090e11d743a3799a5692f6513b0847552c0003567226ac3a3334c594a06cf43e19e124fd4356b184458164310af6cb160292b2fa7ac295e0ddcc7f34ca0ebd2fbb76e0cb504b5f71cdd2be0c2f98765f201495791622cc3bae13a58f597d469212787f5d857f2751aec516fce4bc6c1d0a86721e0224fdbce755a9e5ffa52562f4817a5814371e72e3adf56aecdfd51f5a2ba6615cda74c26f28c9b2590a7cea17c3d742ac55784bce59852ded61946994bd4253fd239af4b20cdde3572d1bc299b30d2cd7620c8f04cface658345e02587d4407c61322334eb21b1766ad62c50436d5f68464773ebcdcf58facc5fd24fed4c1342d96cc019c8692524a9398b331a9cc6d589649def3f62c6ce84eac4bf2768fef55b6507f674529124f8048a8a276a31afc7d02a6e41964ac055e87385677e71d4f9da2c246b67f3e90621eb23bd69aed606c974043bd3a3d15f3cc55e89344009320da428e33ac6a9576a33484f97af1fef26972ac5146ea999f3c3e53b8e107c6f7f863ff9265f2bbcb41a72691c88fce93ccb665a5429a9ca0cb05bb9e3f615632f829d5a98102cd9fbff1cc9e9e5fca9bfacb39ad589af55c7c05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"38ba32228e5bc0a368c3c6f4f39eb156e27c62c491f999f3b3093e92c5f1ed5e","proof":"14e123370e233ec82e8273122f296dc400d96c893b291561b233f2494c2af25cacc504d5635e3796394344c6c1ea0b6961446de799ef17bc20d7b19ae4f7c012545d60f4400c8cb9cff9ca6811e313ac3857a6a220bf3f3e1be3755b4c702465dabab8ea679fce942974147b9f853daf994b3e1426cfabc314a74a4b6c9fa7159caf8819624c8c738eb5eec48ff82a73fac738b88d2280bf138cbc6ec26594074d1e0fb28d7820951975fc21181f0612ebf1766cb860a9055e3e86bbfacdc70a01a828db6670bc1b527709a6fad755ec50497f1ab7afef1677a8307db221770850975fa2348408cbf1338db1041a37368bdcbcafcdfd5fefa0b093fbb625253ce8cdaecf875470e45a0f452be35a1caebab839383badc9f2003908dd8dbc244516a6091c73f125699d34cece6a9e56ac6de11b13e9b37375bda1a90e93ff491f9830ede4b9f8e6b6b385e04555cb0544921dc42419968d8e1298c95890638f202ec338e990b41247b96680abace0eb6f66ec9cc7a2c51ad9d7246212ae5cb776823b283b0bae84883dd24fa35e46ffaca535568296d2555c4ba414a8218a2e62062451a15a0c86f25a92d847e932a9a3591bef1908c16482d2c246800da6bc55609c245f0f8cdf64a389720596de497d794f824e1c0d67019fc316b841b57555ec62aa6d119f67221c81ed1dfb46a16c9088c5738b3641f80ac42eb745e4543c40e4572cb8edb6dce91972751d36d0c7f029af75e4a6b4ea08fa416b5e8bc666103c4b63181006d9f73d2dbb4cba5fc8157caf68f811988a185f0bff6dc03b34f0c6f3cfbe65336ecd98594936bf206dc943ddc48163b66ccf1b8dd20d3c3445faca9ab8b92ec53383500c6cc2e0d37876f4ca891a3b2421b348aab98ae798091b6eb2be2a755b16f0317ffded0cf5a330bf63762e448c58c24063390290870f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8c8e94983ac4364f78bc4fb56ab8a1d085fc50cf84fe2bb9056d96570c31bd46","proof":"0a5c03f5344d55e506dbf85d358c365898ed329a0ebe26a16e2e70b3440f8813ae67f96269099a1c3cca8cc25cdbd6c33cecc6d6e98fc2e0996f234e0f08ef531e34876ecbee6cc5d2d9e8389881fc704ab26e558d5a664f2ba843e8bd6a417ffedf4d62d4c4c380c8a26a7bc165ab0abf40cd3d347787da0c9504db35b85108a7e125ec6538b78186aa915eb376a590689c599eaed1ec4327d3b22063fdf30f70499f9309d09df0895eb25ce60772e33a1fa4d2a5b66214f2f76f2eb753b305ef5323b9b47cff73bc39f213433e861803fe6b9408a9b05de3765e0393659b09a29074349b6043fd2a5bd4c27b00bc5bf6bf51fc10dc9ef5f9e8facf1c642f37b237438eedb7b75625bf33b8c5d181bf609bb6d39de0374a8225e6e06408e72eca674acb03526e2ac784f02e72e7e6ee988c101213c20c3fe69e39c913b6f217f89d42873121ec8ec1cec3ea1bc46a577c64953f3feb3ccbd1f6163cf7f4c212587a768d60754bbfe1187966f8290712caa4f036e12818bbc8223cb02afa1c240682357ea3dd231a536666b0a07a04e7826aaef0a3bbe4665d9ef94e247ce620305046dbbac00ec409fb3ce233bf817abc70aafcdf0b303a423288773041bb2f680fae1cbae99d0402f116c447999f431dff3bbd64c344218413ac35d0c0b653b2700c71350ebd4382f8c1b5fae76e2bef34cbdc109ab93fa5fc323660b46504108f218ebb7791b1f76fc9b6eda1ea46ab56a990dcf3bd1fd6b5029ad6d4be7104cf0ce8c18c2bd50608487d9b4c29b3664fd0499c6ee386b9a9a6aec1c19e04068c769e89ff73c29208bd8df04680e4725d4a6cfb01e4a2a1e82a253be25f78834e3e563473a9588d13ce8e3eb6a95ebf5158257fcddd6ed6ef2cb44a369c0308fba90de605a563bbcd8e8bc368563b458e653f70b92309e376786ab8835100"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1ecb5e1aeb19f5adf5d78036caee164c6bf7352804fb0241ec38b69c75997d17","proof":"be9de83aa187b41f4958e59deb0ec47a72fae6d434342a13b6eb24e15335e23e70b6aa081be103d6b6ef22b299f08f3fdcdbe5dab76bdb0555ac861a6403bc4bce34f14478227307c106bba04c4e01c68a7c328fbd2704ae253a8eba79404b15f29fc2813ae2eb22d0e9a63fed5b8fbd894ae49ad728632cae60af1131ad9d48d218eee7a71b9f9dbbd9ea3fca9728df590f9bb2298478fadfeef50ad3518b0dbbdb0970d07f0a4c6684db16f6b4d7d7f8c24aa797cbab784ccf76c2712de80a6e5ae4e2aaab4ae9f609bea71f59e6cfc0c95f21f915be0de4554a76322c4f0844f40f8226a1b76bad64c31cab37fa55480903d0cc23419b923fbb214574114942123e672b9cc980e1014cf2f25526b3764fa62191a9967c26f893259e2241506c1cd31190986bc4cc9947e20969f1dd6441723376c4ab59c8e5ca4305499168469c5f5cd25cc8497ba30e26c71b97e2575b67e7fca5370e9b6c7175aef8344b8a9091f1dc26fad944becb38e6a5c3463afefeb9c7f647de04729c59dd11932214a338cfbf0b97a225776f8727d0a7dd13a69db23879feb0ef8bcfcb278ed548ecfb82a3545cfb1b9c562b3002760a9ce4bbbc2eadfd9b7d22d28a628e31e05732a7cf92786ff1c9d5752f4b9f9d0310d47ddb148ec063e490735ca93643c16bceee0816c59bea3ea84952bee047364c6b7aa46cd2d2b1a243e10a860f04275f58436b85d1bd1b2ff1270fee4c2f3b29c6f4eb9a378ec67310aa7956b85aaf5afadf4e957d53ebf8d6f49af3048d8e6eb821ae5139c1523f1d75ca71d00a39284cd5db5965ee69e7dabc6f2e6a4b6a55ed95e4a00a60b5e304e44a7d4f6959015f39768f2797e15a6a41270086b0266422d57a8b515a86ee387e7224d116850f7255b84c14f4ed65b4c731d47d160fe4d8d96cb098f829e6b43f5b49d69e360e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dc39ad6e80d13e9ab5faf2cffe66891cc11dc5ce92e03d1e0235a4eea62d015a","proof":"1abbec8900291282b537814593653d8ac5d4b0c788aaa2a1b88c73d66eb218618af00ebf7f9f87475030d15d73b8da35c1f835894bc16e6abf4d0d690ee5ab5ba6c521fb2551756fd6c572b52bace092e36a45d20d94cf4f73e0abb7688fe1601c6e566d9d7a37bff39703e81ea18a3521b1106d6aa1dde260ee1fa1e6bd1f225cf2fec5e3f1d3da393d46069f1576a8e27eb074a04e2c9a02b554ffd8e5040d6bad7cdfd2cba7fae9ab1b58a142556e18e1f478843e5c21f41675787848f60f4ab1b85ab8b5222f533647833d94d1a7d4ce1431c8bf33311bb3c30c3a8e180754cea61de7fda339765725c588042ad499f56763727451e206ff48f6846d6b470ac0766f458b33885ff2e0cd05057275bb985649cd546e71469dd22394adcf29566580d63460f2a335c6a2e389e5dba5b11b84bf609c4b3608f167da7d31d421782bc44d42ce47fcabb5dc3c786b0624d5618090cf9509c71755e05678f2cc241aec7e36696759a57d311856e4222b279baf6ebe3928b79517505e80d1eaca08e65a2f78839e63ae4bea9d6e1e76653f031e52c1014c916fce00c460e07ddb0bb2afa7bc6e561eb7b792c3c25b659c846848c37442e3073bede03a7aa9b022504802652d64eca27463f699c49547b53931a9a0f0d5d0d840b2e635e2c3ebb063b64fb07f320e9f8814a7bc29b27c58efd6d9bbaad2c4da1299819e42f4d751224e18eed92085169acff39600aa41960fcd4dc804c31d90cea04f9f540444eb52dc7adbe834cd1618c593e1c6e667b4dd366cba5780215214893e30fa0141475b30fef3f9ff9caef1471c50b1c461bc321bf7d5f1118bbbafd9d94104801ddc5338395e4e12489c1d7a01ab3d24e18c2b45a48fffc823b9d7f4a4f72eeddb9f0ae1b0f33eff47f03f2cebbe4cb5f34dc491e1f8e0124e9fd050218ace791cf709"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f6ed984b81cf65d3839ebdad61f2785e4d4bfced05f113b5379d25adc7720423","proof":"02f1b4b38dc375a76596ffaebfca241415f68f17bddbb6c24d100900b28c2c1a9a4c56a0d6c99afaa0d1e2f20884bb0cc78a435436c333b2e1a814513902064bc4956a6c8f5547245884d586d41ccca2e5a1b7c89160dffa6bff617cf81ed912c212644261c79ce01445ddba384204b24a29e89b0f810981b41f13506ba59260dc87bc8a333d11b52edfe6c6aaa561f35c3d53ed36cacc50a399307748260800d3ed63af8a30f06b655e118de1c8e85622d09785b4c1999cee46ba69ee561e0e551a3d13b53b729c00bebc776acd9bf13eef94d08aa7cbc35f066aad2420be01082929cbb9aeed9650bf31f817068aa0a4f5aa8710b4a7d3e2f76ed4d438df1188cc09b17cbbd6f91294cf78307569a073688c6fa7eb51dc26219d18486b175600eb114cdb65400b9f5d5bab9e10bbce45dfa8c75311e259ff552c34b2cbab338098924dd1ff98615a04befbf28cf11c77cb304fe5edccbe6146d0447ff63169e66e8f958fbc828d7b77a52c1c564b9abb0505bed22419ae21a5dcd3f621cd61b49b75f0f63d745a1b83523d3388d5dc69cd1a85d017ceefab481a891a0d8a00ac2580eebe7bebdb21a6bc1c8880d9303749ced25216636b958ff4de44bb0705b85e256a0ad5cc13beced95431a0fad4835105f49fcfc9d61fd3df9992de2f0284b1c6746dcc677470e4e06a16d4dec3951be48b4d23a772351324d5382cf263f6445e09222bc6bc170a96be3efb2a9264fd898bb6b8cee74673b73db368760f52de21b69266bcdb4adbab5a2db422c7570d70206aa31cd1db6347b4e8f1ab26d6dd121193e470fb980b0b54fd8acc74c03ece423ff17e3562cf7d79ca5c36576676bb62154a56155d7b32f01c4b96ae540c13bbc8cc6dd77a5ffa21ae73c00faf9fc2468d97f70666bcf3bde05f531750b0e005c460ef07bf5a2ef0a0de9e01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"54d7cc5fdd6c6f443ff5fabb5530c417916f793c7b74f398a9f97c13023a9874","proof":"7290a509245d6db78bf431ab168dcd3561ebaf60d0069b1a9aa38c5a33041556583b410a62476ad6bab47372bc6340b90b1d8b19343cc378c09631ad596c2d3a3e478086ba58ee236c7273a0ee7d9609904c9236c1636d9ae639ec3069ae8f26e28b6414a6ec034b3687cc4eb8af80c4971f7591915317c3ba94161ebd7eea1d12710923e9ef419915a5d2966840dbba3349bbc4757c14ad7e14fe44f77dde02e35ebc696d285f2654b2caacd76e102ecc05e0fd59f1b6a5adb0231dd5844b049c2912f57f6a4c60e99bf943e5107c5556df4ba12a9aad4bf47b951467fe990c0e8f30199c3419b933c751ced4beb125c2d7b51d03e779d2922f55b347086e0be093842f9e08d529da2407ac0fe62ab57f4d8f45d68537cb4371e3b9fecf0978c2fdec8b7ae5904a9a658948be502990aa1fa7c646fccfedf7f82dd59425ad4ee6523ce942176035f92f61b2801f9ad6509ec5aeb90589cdaf005001509baf20881705078cfdbe53b60697a4cdc550599ce4d60e4366f2e17b23b6f97588271f240356ac5dcb8fbc7fe4a4177cd9a4a48ff046b1a5135f6ad4765f12dcaa2b008c1e714eeb4e3987665e387b697a39b5582f42dee1309a35763695fb9a17d95d7ab4c23b732570c134bd9f11fb1ad06126c35edf2d9f4d9c20748ff54e8aef1dc8636940b9b9d88453d582cc5c0cd3a6e87f7ce5cd2ec3d0214a232b60c94f1e5022bf7c9d6ab633756fa620504f8748b5bf244113d76b6f66d5ef3eee5c507b84628eadaa161ed91b1ae9fdabde61eeff087229c1fd2f84cc4c8e33072fbf561af995468e627f85c588a6c08832f9fdd13a4e4f743ceff5dd9a12e422f2e719da8907a4f3956e38408cb62e55fd1ec630c34c349067b8596b5c501ac45ee608972585080c652557d5171fc0dd976fd49ef031fe1eaf27c53f4ccfde91af320f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0c5f7d0fbea280e8dff8ad0f2d4bc8c8cf6028f14cdce239970606daf6955216","proof":"ce0267b7f86b27e23f6dd69024a162ffd93081e69da7d280cca53fed1c65b802f298ad6989f0d3b84e7464970f3f700655b602fb0d28b758af848e8909daaa3e0065d4d9be795698af9089455c1b2352778e4533882965a6dbcfe418dd63897f72d8d0cdecf7ad33d30b2a17ee24b54cdf9d5a588dcca5b208ea17c9b501bc7c8c740254c511991d131fc7f81b69fa099f35540bd16122dd6264d3641085b40962284044053eb7e9deb9ec92a351da4f00aa020a98774f23de416f272da33a014891c1b547a023b620ffa3d0203e9e0e214e41e3f8e6652ada6b00cec5e5350e1abbc9edfff65c87180750c1b537f699da9f9613007022523a163e4cd121172a5a930176de6d972b8b3fd34706bdc1c20d5f7f85c33109f020fb56dadb8f4375b8cdaafb10ff8cd260b72e12605cb9db60a3afd7c586ffa0eb68f327d0b1840be034d54eb2d906f5f1f6de5fff1c4f9d73c288dee67f4cb03976014cfee48d5afa1d34db2810fe589623e1b4a237660eb1f539d0aaf1d74535c8a4fa5c8c463c48688ae5580f4660d7161602723a37138c17dca3c18ab4a258fa2272f8b5c435202b77663e26463b7129af49486cda12a553268033906e3bfe07cca3fa8e981460ad24e6e305edc8fa928dcea43c5a3b338976cb5d4d3cf01474d555e865b9760e6cd81a0d47018e0b2510846e910bcffe143454f916f91ff67b42b9d8cea72cb6577ceea12776bb45db7fe96dc9c783995aa82b509f12c7fbb5b8f8a52b1e7f8c3f07cf180ff3c2488f38eacd7253ce3c08be1362fadfb5805c6ea9814fb4376e33587b6fa0aa6f1a440be496a35c18d6eda7f0285a93f5999e9d917f2dd11c6c170e8322734c2edaca54faea0e67aed0853214d1e037b4256789fc9b946d0c5c1bdf8454e430843a8a61d2c4031f4caef0d85a440731e07ef6aa76c4463c0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fa4e1e3b8f6323c813209136e402aba1ae71e5e7541c580965161f8466ea855a","proof":"248b42c56af9176d4a0346a359a0d71275a1e511c2fe0d35514995efd5013d228ae0bad1797a92339a18d73be9795629bb9844ff637ed97058349dcb3ca8941bd8a111b15c6e40c4f64bd3f06d3e39a47853bca77f6c24a692e785e95225b80bb8c5e34800b416b7bc30fff8a385fe4d3c01e766f5d0b11f755f1cf3c9286c4bf606e56d46412105137185bc71ddd868d63e072e6a2bce240df60eedcbd57b04cf6ae6fb71c88d588a83f7a16180948b9063573f339424d80e64e0891723620f7f6e218d45f3e9639f60915d27d52bd4223c36d0b9546c0912e2c3b5e2f36d0e442dea7bb5b5c015315368aa3027602f5d3ab8318089468d6991fcbbdbc3dd2a4ce3780ab77ec7e3ba15022d953a97515880c33afed6937e20f539fa28d87b0e9a8e0e620dbdb5599694b228cf42d037a5c7fc39bb99cceb9fff61e3e98b7b3c70e3d8db59297f752e3ba1e46a54c98c0e460f867effbb5e6a1456b975c70d090601d9b3f465e53bbbfeda0c3ffef48211e04cb293b6165dddf7d8a079998610d630d4d309abc3f39f56ebb652ace247ef81519597945c261d72a169691bd8494e630b14128752e8da1678bed5bdcd03c2d447b958ec078fa2a4ab33756034554e28586210a6366a6540c4ad9b121b176261e6e4d9acc214e5a3edf2e7c898372244b530e754baf3d1125132282846ae2c99561076f35a703dee42c8858d5d1e1e60d44d86098fa6f3c8dfa5c0abd090b354754958107542b06e7880885f3403fa9b5e3806fda57e70292e31b988c4707591850df4b619929d206a583321fb6468111f68484821db87af9811e3c4178023e5af95e2382392a6b0581708e64743637b4a913e170e07a54a62d05070a0801e117b65e25c321937df7b13db64d508753d44f4fe1f4ac17bacfa5e6401f0027b8f6fcf25a813d92b8e0e97ae6ceb0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dccc51e6f2ac61d251fdb7a4d1efd1ef24f62372a524623ec87b4d851c6e6b37","proof":"82a89cc64f1beb03cb9e827325d10e2fedc528522bf9e305b4273bd89a1f3024c4eacca32391e45847a099e4d22518d2c758c16adb48969c50b0417c8dc7064a08dd6010879778e4fbd8644d0a520d9a04ca36152ac3caf7c14131825e38885484ba7132fb5aa75c2c063a1d6fb52f7452e19179d758ae3b3da736c54fa63a60aadc363a773ee8525d183fce241d6fbe91a154b9477a9c60a77d2f2185975408e8ff65f01e5be9437f58155e8020e67a9ecff7422cb629e3ed6f2dda3464870e9413b59df7d2a3198336b485034ecfbd3c67e8a534d8d78cc7e867fcb144bb0114e9ad383400ab3b7ca918f363d664b2a66849e33d94a0942668684f3a93fd0f0e4f4751806ea0296067e7d9790e0f9ddd5e34ffc31ebfb817a3f48e040dec5dbcccbced7f52e1d93fade2a7ce72a86bd48d5d712952c638a3768958486ba9646026b3d9b148e293e4a9f33448b7c8f90130a8d45accf05f47b2be3ac9bd8b4308f1d927bf91b509c15fbe61ddc7c5deb035ffe9a3b9bdc8173713683d2611035641b82fe430e8a2a8588ac207336fd2b4c03649791c1ca58c3b68fab6aa9776c8df21f994e0529958191c499fe25848c0d18fb1f572439d2a41d015b491f04ef21a5ebd17fa1f19710f8b305550e0150c58727bf5aee934dd7ddba94cdc34482c27717f455aa37b0d7a8788b1d453290bfefd9f6128e17cb17bf3a7a1ed3637368ba39ced1ef458cb8fd2be2dc5655205b0feb4e502615a335371e52c86903d5ea1e36e60b38e64df7e31685a9e52f1a73fa3f8eda6713daf21ae4e6999696f640c5166ed11a236b2106c18ada283e1d00996b34b2751fae80c5559f5d3f26ac647972da0b0b46572d8881378f44221283650b9b1c925878608287d110d3204fb6f9ff2b03185368356dca3aa887c285eea683239e01609b9b796d3f5652a05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"328d8520946a471ad6a66a34a3dfe2b7e8ad8a0c4d0391f7f866141a1ce0f95e","proof":"d64ffab520206f7069d9692777fabaf726f813f469f5d23f1b93b34a5acf3a3e261d338309fb9691bd9967bfef5735bf070c93762db4536df02a86ec02b0e046a6e2bec311db4b8dce61c570da320f4d90fb4e98d39ef5ea6db9c06dcb605b3d1e45878f7c72f11e35a7ab6538adc2cd0c21141852ae0fb602b0b5f3b0b58d1ebb462c93925355a47830d1d2145f868f579becbae7d18182f24dce0d114c7a0aa0c9433f9c3d1ee42bc4e60443b14703fe3f61191cf9c8c12bb6a23391f3a7075340c78f29533b130842cd68ac5a9bc370126df436c2d67e7a0efdf1086e1b0f30653c9944cd53409f9e0c2b1826db13e879039bf97a459e78f614678312a459188cfc7a03acb60607a5456d5d37b1152fa179e38f13ac15a9645cfef1fef4087642f85b734ca3378dfad2b383115009ecbc134fdda867ddc9015e99d6643b5fb8726e957232eafe3fbb2ef545805d8b3b482f937fd7026369b303418d278628bcbf25f1d0aaf324e34bd69bcc4cdf200988995baec7f2fa4acf1d82642cb370464de0bde3dbe057b3550e00aafca1005db63770d4c10038fd65361f1af9b2063c6dcd403058d9c7c1f814e4eb7eb13c44995a23552e747e237e67437a3db3563a532a1690b82a4b6a954e027a321d61dedade2b3f5887dde7f4006f5aa6a252dc4d5d12eb8aa69380507ed15182dcbf2e1eebe6735ef317c1b4b24323efb12734534a69eed9c93a0b92a712b0c9e2a796ef8642c423fe704ddb58cffa804b3b7840cfa1f8a2befb43a62988e0eaab07917fe2ad2b93a38852c8b75008e5395f887fe8415b929459fd10b902f92e509cf741291de59f5158ac492aa4b4f9660c16abb74eec308005d282208afcf83f11664beeab98edf512a32888f55c5f140c4a55a350bed5f9d8d987c88beed95b533d29a9b3c7ed1f11162ccb503bd3080d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5ec728d7af87e797556b6a3a7b3b0524a73deb58a296201794dff599b7f49135","proof":"087b9ccd5441ffa7c3278c644cebad5111218d77373d2261f130c0f3812a6d4fa2c8df696922ab8b81a2fb063e63cf4034f2734a47a2532ab524f806998d1a6cd4ad1ed7a7fdf6a80360990b96b75e015130144838ce1473a4794f3e8ee74134029a37a93a2c36d487dc9e9c378fa60d2c8159f6dd1b6b3f1d2b331593927463c5c6c334baee52e7d20be7c7577d22b9eef3adf6064df577fc0cd6390feda301779e0d1b28795df880e6da9f7583015d4a09e9505f263f2e7c84af8148354c0615c2f594f23ba6e29fabf8f4cf2d8f493bd1dd765eb43fc9eb197a3b7c0dc70482392fedbfe36ac2fdf1ebce3c082547ac5b9bb25d9d9506e5bbc28f1c051e711cd49bd87bf219fb5eb293427694953283fcfef776020b4011207e096b1c4428c49a94b4aa6214e6661b475d5cc00fbff96886f419f1d186d110fea2da67f73bcab4191cd342fd7c7013a2d35b899934cfe2ed1e86328246536015384d72250496cdccc6ae0c2fed51df9a05f62cb8be34aa92a29895ae89cdc375f8f0711227964e41dfbea81008ec422b3bfe5fe8a2d16a5dd52f240187936a0d58c6d17d235a947555fe91c24eb482f2f335e32dd08cef87ca1df5b34f0781ba3eabac6173ca8b7edbd8f3e24b309629d21e4c8480ed2514db69baeb0b28bbb6dba655713a9c000bbb017eedf3de381566baef631b9c9d0b439235a7da8c427a2f04e7fd3fae0c895fc0e3e868f9dffed34fe2560d94afd59963a1a89f7638b4803d7b28492634071f3c0a3c31ce394aaa8427453d76658ee0168a1556db81a53fa8df0561d6b2fdcb0b5a3ba508b43cd6216202160e42b76cc5c39bc09fd889ad39c5de71c64be489182a91325312ee4ba138ba965e0910312ef2c517040ce7ede1914b08daf986cb164f4087306d4e85c7349904731b35b5063726c88931c67aba33e309"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9c7f807ff8771faeb3a4faa0872347d378b68f698910c6d5a2c44b48f3728052","proof":"5c76a27f325eb236239ccbf7f3f02e9fdb57543288b854ac899dad881df164220cd392731635c5a9aaa8149cd97516d3e7e375010dc7f96eb54b5e0a5349e90c9a00b813936ef636cfb7249dc9867f2b762eae62a43e81fde6ba6fc3da18df1f6abcfb1e25b21859ba7908e01d52844f5d653b2a5787967a648235195540bf175001c2102c49cabafc15010eb5829ea3e356459f43750e83587e5f1ce5026003c9be468e67fd4496b8e7daceb40d1ccd1534572006865bd793dae94f35edf104e60051b8813f1c3c55b982e423efa69382c8ccf44416b8345d849fbcf8eb0d07b27a548682a5dcafd5f95fda2b2593e6fe28a50210f114cd656e2972dfefc1719e4a89da95b3c59544af93dd18c525cf5f4dd7e7117440abcae991c178e0524b080633f043da21afe11516a123cc0050365f4589ebcab864ff8f28920927b444a894ea01c2eace6d60d2a2222405e94a1a983bb522825523aad1f49ddc8aeb220e400842fbae44577962c76b41d204794565207574701e2afca4ddfc14e7d33dec662b7f43b851f0137773afa999325a1b437de3150a3097bb5270a70188c870fa7605695c9525420877c35d3d1bd78d6aee7e8fb4f5f4b6b8acb2e19b4c04683e27d248ee7e8b2caacb5a227ae5cf259e930762a70aa698143240a7dcb04061d8a9f0b90722d63e7183cb121f44ba09c77d687efba0ab6ddee51a3f4632be13e4e53eeb68d563c139aae68f88190718f1f7c701c0266ddc410769960a91c03b2a37a81e1a7c823c9235d5e9f5f739eb7703bc1ae2aea27c574a485b1789426760a1d26fc8f670d830bb20ebceba6b9fa736841a9e5f6fc6a7004567893f6b6f0721ac8f4888f3d2e38d8fa8f59296754c821816cc00723ef53f854eb316480f3c7615a56ba6736d803e48ea2e5b1a573770831e7b28d4f1fd446f86f58cf00c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"52831abb42e0029ee555b0dc7ada850a692a547e47cfdb21c14fa573b5a0f448","proof":"7cf121a4da99581061062b4354a6242c97ab5b23697a3dc30b95e0dab9dafa0450193ffdb8f7d41accb79976dbc0854b8e1bf06cd7a5323c236af15321e4824294cd61667d4c6bbdd9284ad82eb2a20a818377b5349b307045f3194375bf155e78f6a5ae86ab92cc00d4665ba1d893dda5d3bb8d52ce7a274b1cf8986575be5ef4f7fb67788551d243b2f8f1615dbb3897edf8808eff269dfb57936ec6f5cc091d6233d3876721e4113993735c5c9bda032d015edf1ee7991237923a451b1a011d2db2279b1904d6d90d18c7acc9dabe4d1b8fea871740c5b6c9adc511566e0e66bb999f8463f0905b9c2defac50c17c22c7682d074399c3d614d4759b92571526a23ed384711e8d87604fb62a8b19024f4150049d359a8cd3c2ce13ab4333103ef120f8bf03134b85e439700b9b78d4a96e7b4f94745daf273a223bd5ec24340e73f28bf407eb9c4c50329208d48f1c2328d1fe6bd827df4722d1a9bd9ad679bade38347b20fc9a814cb15cafc2a0f999fe1b359d20ba26e0ffdba040baaa3e604e9b7ac2679381f4f515e492c990b301116af05419f9e8c843f39dcbcf111356dd8fc2469c81bfecf0b06480fbd7eb4d8d7120421d75605fbfc4b8d742e04406da595aad1ae137c5450cfdbec28065379e51e52e6daf8023a3539ff6878b15ceb317d4ec61fdc3da39537aba5386172133dd9babc86d6e31561507d2c1333a66c89a5d31a4c0dc8f80c2a21aee08d55c2e81d8cd87966a02a5f8fc10d82465fc76290b70647d3db3752de6ebb00b18170eee6867645af2da2085e5b0a2183962a0172c6a4b5392b962da075f6cff7afeeff7b8942fb46c3e9acef5ffcc3f45542b552970baad1f5b4c86aa6a08ca1f0797f719cacc91bfbd47769ff075180a08bd047d991ec84aab22ce5c90d1aac2cac6ff02efe2b9e2d7316a6d8f9c7702"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a6cd63697e3e9a3cb3b2421835c180a43a4f11811215d7990712fa1b049fac6a","proof":"d479e219b220114164332f8e170ff99aa6c2e6afdf3cec63ade2d0a3877eb07942bee2f93d2075316bd49d0087c39679aeca46d32815be69c4c0103f281016648a0077c00c748d63a3e30cae7aef408f2a3e7d669e0b48f8a57a09b3eb345823c46719ba355110fd492798908eac5dab73d9a36f16a1830ae60292fc4d34d85f28e1842fb07da1104a4a42199dae9ef6febd7525002c7262bf7233f3e12df106a445524317fb9f10122afb450d9eab6e29cc22835ec7d92fd936b0df24198f0d8477a4ec96c1d37233767969a260ec709500939fd208433075ad280d26ef0d09fc07cd0d0f5f16c1c6757b0a46e9dd43f0705265c05faebba4c995d593c0cc0ebcb8a0c79c2cfae5094e5f6d15041f3cfaecb430c5b5f254bcdbd9204e0d566c2e070755eaa40c89faf4d89fd9a7803259b7d54c4a5ad9e4b64c399e4f5a7817a62cd49edfc0b9bdffc7e1fb694975daf48545ee3bbe64a55877379ad6c14a4d68b9a3fe4b38231dd28e17870f4ce96d533683c727e1cf64fc161566cec8435b42797c3b97c828c2d741ed099ea8556931d327ac21d210342c8d76fe9e8c92105cc30c1ba1da2e229d81217f51c824c9258b4be35f886d63430abb4f7dde0c0962fe41677ef436fed7a9c570ea00742bce6959c408aa9b79fa4e9a5b3bafa47f00a6a3d7a8db2d4a997eddae3a67ef893ec9434168aef61dbca6cba625f3183756d9a068a1046034db208e816699d231070f6595460ca72fb9d44206f285544b04974ac57427a674f6cd164980de1c894471dca89d754c2bebf97af2946dc029aea019ed36679630db40a7337173a7f9a77e611f0d27a89fa60bdd0f4f56f47b40649df6b7eed61e5d5aacd27bd224a5e47e8225b280b4f89e8de9e460c2a5001db2edcf8714eef3b7d7424a420a5fc8e015c5560e31ad3098d9b1669601940d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"204ec288bbc676aafc7d097061ba291f3d79455279ab726d2d1c7b277d390b41","proof":"62db02af5ab504b941d116214028d8d66991ffd85213eae2743472fc11f62a4d4e78cba9d21c0ef7fcc5600af5a485d318863266b4d50474e1d29ffad374781b50d60787cb8c1f91f9667e02b2c6024a8bf15d259a33bdeaae757c97cbc94c1e8c58002068eb42a160e35556210d616a8304a49a125b7a1f1050a7835144b93a921ff08318df7c57b4c0ad419a78ea1d843f7aa6b46166191c9ae5ae70e180016485e7f5e181a2be1726ce4505d0f7150c4e44fefd725023ae9eeaaf97f29802e45fbe661471f9b73f2e8aba31a7f67fa7bec45417c1c2d933b690f372ad310b5a1f2b43d009979f0f16e62f72d744e57bd8a5493ab4e3e52c3077300dc75f337220e46f5c4928a92a81bb027064c05f8c48c38dd02bcaa084a40f52a1b8611c40cc6c1aa8b54b555154368d1e85338d8785f37ccc1b6a1dbd35172b23bc095d404b401fcd760e35cce0785c477b54b02887932adfbdb151e6de2836c3baab18d0f93f148a770591e92a297d178ee95cdf7c9a7b58d7e3fb50fed96d4f135e402ef25b8be10f53f24e16cfa8063f7f91390bd24b2cd6c5744364ba43075aa00b9cb33e26737beddf7efde911c653f80ec45829073f5974417bd65409fd73ee2c2eb54b7a0f453c223fc299e7bd4340e4c0ef5e4a328b197d46cb1a1329913c6d38b853de5d22920bab89f8d8ece465f4ae66e55ef9a51a4b06bd5eadaf64bf319c0d4c81ca48b8e95c8602c3470fe2dc7af62c18e2646d00640b9ed8bd6b0e4e927473f58c58762f77208ba6a4192ed6475374bb9aed1422f1543ed18a40aa3cae14a4b0fd3d595120f899e165adfda5603c3c75d92b0a4540c58f1ebdc1784917b3211ef799356f9d6e50692c4fcf413c0f0296201c994f4126d23d4909ac03b55209546a590b54956d927bbc126aea654d48450f802122832dde0db0f0cd02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"56bff8d43a3b6648a4320ea82f466a7dd0c389aa18b698901ef91ea3568b4f1c","proof":"8054601f366e415f768925f534a6311fdde1f86ff70e79af1df7f719332200477e21d4330f8c1a6fe7cc2b939002d2b4ba183d424ddbda30b6ab6da21e688647eccbbd6e6f121798783a6e3006991bb071bb3316d352b92bc39e808a2f98cf446416ef321a0c46ab6f5f12c292ea37801ef6245f13d01e455a117b4e4ca6e6590cdab9f721c6687c27f26a83b09eb92545b50c6277128bb035c35047df7d8a0b715b45683e00560b5a03d32237b914bbec084a80500ea164d8153cc95624d20914922e3e1c8458c0d6519364579aeaf28a9b5dcc9b7ce3fd3374de0ffe5ad100d6d5d54bb7690cebf6163eb7a215cdb7f9f42357e44bf36f0954963811fd0d29283a843acba43d42784c8f2b931a746e4bd0ba188caae3d46770074c4870087fbaf4a3ba1a4077d4bdb222711364cf27fa6facc93cd27409eefae368bbb0b8752a0f347d0e66a29eaa7f8987d59c1122520e809c468ec5bf989482f90af3c223062a217b02a8309b0a198273b2d683de226ce1a6e7c53663c60f45d3139fa471ba02ebd3ada0e065303938fd58dfda479581ed4566f324d8a5268987ad15e4254a401f7c475012e1a97e66543dd7552d67d3aa41f10f4507b1f705a8af57e6613aa1c59de4bc7c1fe0ac89b178fd1850e2c73c896a8a5089f8af731f761dca72b69cd75b19646e03d36298d861667c826de9ca20ba032bd58cb616d20af4cd35283056528e7f736007bb104278d0cfdc5103c768ce873d73fff9947738be0817a490d83feadcf468c4ea548949f6278f648eebfda2c9cc203c37a450ea7b417e1cd12f62f087c560601fe5716460eac5287bbfda79e944f7b84401c6fc3da80c47eae9d676462d7745f24497b1b1b57dec1584108bfc1100a58623f9966e850d359f1b3628e51084dc891867e88601d227c1d710cb20b4e90797c07e0af72501"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c28607bdfcd09f4119b9974e6eba55f00468574db9f1b959fb1f1f4ae694946a","proof":"aac84b9aeaeb96855e4c2c7a21d7a25f2309a7d9ddf870bc666295340431084e80c4c8df1136fbdbe76a95cb720a6fd03aae6fdc43decbfc591cccb38191e178e0d7a94685514af3828143cff91046fb97afc5033790c80fa75d50d5d37ee62e0669280e8e7801884cb112d6d8ca3f5186ffb237c3375fefed68b0f0450aa44315139a379bfb89add05c77fca7d8d1e48e0c8fb45d2f8e15438a9856e2504103b4f186bd1105d00f6dbc33a89e75efba596c8ece9765d774be5a3b4cb649c80eea7c748af13f58ad620269370e3c9045d7f8b500550ab884d0b35a5978182c0898185c445bf0c6c08e56cb0575f5db0620df4850af3d7adc881e8daced34890012edddd3f9ed826d5dd7df085a57526ea614d29376bd2550314dadb6a0e0687636adb748eb1d42fdc3ad8f9a6dcb11fb6e9221f51a3afe770b36327567de744ff47598f45f462dbb0fa52114d50e4b74622d9de3372629f3245462c8b2338e3de4a9f2ab85134e5f72b176b11bcc42c6a57bfef014bc034866ca81e475dbf60c58b37b3fe7eebebbf2b2a7457550d70c2fe2fe840128331d4fd6a69c53c2094ad66a6a754be5652039bc04069bf530a1f74f1dc363112f3726f2e99e821b120dd69d99dfbec13a57d2ca3f830c877c5841018b132f3c4c77d65dbb207b0e2669a0fd39cb95f0c8de5b1e08d09a19ab5d6786723ec6f3f2dd6efd9e7216e2f261a402623c22fe14f428dc2cd1b80582227c2df5b862d1c6ee6cd4da26a0d6b36dbe1db9c1088c7fdb2f14d46df0d7ac2db5b7989cf70308b2ee31ca32c8eb42337c5a849ed9d31ddf98ef5152e943616e6016b1a4ce1883144f44ebe30330764e5f9b585806556985334f695a90cd88031edd200b2392ea5717f05cd7d3c23c0079a9de3f9cd2099a311d3c14043821aed49b06b897cae168d1c55e8fc5ec6d03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a4eaae496733b5df42542b2d1fa1d8e1e056fe2b0920d4e749fd6a072b8cdb0f","proof":"88a1791198f81fb98f30979db3f45014199b85c29d4ce3380153f99e4239ce4f9cc85ccfee45af616038454067b69f8f1e872aa0209dd387039cbc3f71e3d966688894d68873fa4deb73d2647203f300cb9e18e3940c1a32068cd432321b437182d36ac0512e818990f9597c48fb402e91e12fd4e49f2aec5e68c8c5a7081a1bf527849d8df77a52a483ce3a623df9b9b2f4d8ca6ec80948abe0c8cd80a08108fcd2ecfe611187163e976eabf05cc248132e9f10f1412ad09bfcea76d6830701c17623541008cd766a6471f4fe8d492bf30c77e4b09ee1b890818d58b07bf7071cae1f67bd5e5a356dd4b35fe602b604836f287139315b81b7e2037ebc0bd855e6f825e92442c08cffbdcf282f189100d5abae941f9fa37544de381d3ad7ca70ec4d122da454996552e8260ddc90ae36a1c8e722910d3f12fffb820a02b9d205607e64d0ea8e8efe62f2d258b3c8e2e6beab89698b9d54dc81948ed5c0ff235cb6554fc2cfe0a9a47a3d4570895a9898e8591191df0955024d1be814b7e23d3a92f5476afd547d7fe28c8eb81a7a4ea463bbf85ae887407e4ff65b32ca6ac46d52fea4d9b61d75a21235fde72c8cc87799645266c0757fa873a99dec7eebc97f349040897abd14505287114655721b0c09a40288f9fab8b8a07fe7c505e866003a191ceba8c2952cd456e8f05dba5378928c3e492bc8a72c6a473cb1c89290724a5518b68c2dbe2ebfbe6c4340d8e64d9639f381ef5653904c0cd892622d850adac5d37130ddf1554be65ee0faf3e344a133524028436c65633291eeca2071650c33d4403aa172a73c0711935524f78b431ecd0cfe232aa83866bd54f706ef7b21fb8d66ac396dce6bf9d6c8e8714df927082f7266cbd2ad6d3ff1ec72aeef0ee6c9b3d36b7efb9a84cde37703376996f2ec1f7ed3507fe2ce80d3eb5cae0001"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c69d8a3f2b506ff882867adb64f0ac602c9718b3cac6f96d644594479aed7642","proof":"48588f7840a1227b1a609dfeec6039a0ce63dd9514a3321274804ef980a93b2ade507a26cc50c71b9c0d358703e1d5cfca1985113797cfa6446ecae46b6bc874f6c38dd1c1584076d3aa74e269dd0b73d0d6a18cfbe2c355e837db775f74927f509053c3a0f68c5b9fb1733d2f462bf42068da4e2c9b11470d5ff2b98c38fb49a7c6e5b165a47f2490858e0712da5037a8f8d6322b9c1f2d7c2658694de9ec06633726b5ff1d6ffc09f874c5f0d27ee447a5adabf06d45d989122682a7f0a700a249a1753dbb027827b01a0739b6730eabb3bcf4d061d06bed547ca3c101920d5c53d634d0781a5854d36ed56b6b5baa88d034c2b46219eb4faa47fee253687e182bab946768637216b2997dad5b2f1d2ee0ef86e336844c52be865ab79902125c5d4e87d137b2dcceb38338424b70a1ca3ab77ffc47f2828d279f534e45b70b6a4504e5255cbc218108b8d91e3f5633ab48700762fdf70869aff3d14405253762515a2be69db473a733cbc916136d81ee9fc2544529f197bc913c9b2151463adc4185dbf6b4df1d1adaf597e0a249d4baba520afd2d353f21264b047c880a45440afcaa6443d7377540e96d5442cd3e39f44dafefa9ef0080280dee74d635142e8121b73bc9f871ccb99f1cdffbd46754c3767bc0090ca614fa822309dcaa35a84e450d58491c8ec4f9f8abbdf604cda71b1757487c63a2a5e3c1a8edaff6109e2bbe0e7e9c888bf58fc31923d589ad402ec6ed2457c3ac412136d5e991c270885ad50cd41542d2432e038bae49539d906660939f69f6178d2236f33110c3675034fcb70a7e5a9a40ea1758d2b285ef2e1986c81e03174a28c9eed46446012173413767daca377fcc79e891bdbf76325b7a1cb4cabc1f2bd2c705307ca1f60f62b0836c400a4be3c92ada5d63b4d78b9c116a9772bdd2e580e2e6587f90b60b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cc1d8c57139d8188759a0939dbe800444fd3b572c6220c111984a574ffe31902","proof":"fc6bd237624734b61f891db45fe07f93d15e85defd4e309e956b5ab7a9535752b000dcd34fc6c2e64f98ec5cfa16e855de064527a36fb8838044cc3b25f64d3834695f9351a3d9abcdb38168f3d3081e6425bf2335688adc34335e2cc4b1140826b08bbe64610d9078eff550412e733d048459a01eec527a803655ea9ae57260d15c477c785462c323c440c5c8934d80320f9a855fbce52573c3e432405b7f0183ca1f4b27ea584009449b3a02202835938dec9209fdd7b85de580ad917527051f32b80854ac188726e029829c14c8d021c9976dfbe63ec4be8d39f759ecb90c84beb8262d1e27dcc1d9ea0ec31830c74d2810fe7f0e52cc5d6c69c1fc9b437d02f6fe599839dbb751a149df88382c2de99f336b5e87083796249e16a7d54f09700d1531e585e78f665a220e347118aa9d29ac2a4cf4239056563a1d09665f37d2d92285853206d18d6a78af0f117f7fe1c606e473ad4e9779b8f901728c445e465b7bf629a0ffcda075eb6e963028581dc5d5b3e45e1bab97e647324e3c6a3764e2583d32912ccea6586202a35144198a29ac887e27816b0c2922d9952e495fecee44601d993030b5762ec7af9b6701669b042ff9d10a05cd1fcd0acdca797444d164ba29b652f6ba98c4dc073c32d796a2181390ebee06faa5a4d0d26fbb08cae6e78be98625b3c1125dc575dca3b9c8a52b7593d968da087e9e0242ecd66db4f948c9310b72cde84a1dcb4a5938c077bd86b7e305721c8a0361380bdd79011c8a9e133d5079cf6b46d606816388fbdb765787892376d0a53589a31af9ec766c0ac5e78e1a2074c4e3d84234d4177d858912d04a35911fe866857527fd0d545e19efceb2d646aaae408802647d7c9d88d75eaa204eb61d38cb8c292ee03503d381d97abcd99b1775ae68531383b1794f8e9658f36802e6b150d2416874700a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2e4a4efaeaf1569e8c904a6e7e9b9af3fb426aa5c4cd51102daccd0db431b300","proof":"540c15646eaeb23dd71ba08e70510b2d946d4ba8dce5349ef086ee76d62e0900d23baad2b2b119372b16d808ceb90581a9800f06851f2e9b296b19510e3e0a2d3a7301cce254e7fab391f73aa3b70216f9827443bfa8a9d911297ce141ff607808151f580f1f1a95aa1090881e1e8c3de620f2de92b2e3d732eab9f46b9a486d08bc19a5d9764f923fdb6ece5ba87b764e1ce86acd8c44f8f821195975c66f0e7889eba847f16bcb310edb08dd57c0798c9e6fcbbb0b69f80dab6b9080198708dd8582b3f40b48008aa67100382e4ca7f3ae7ea641c7f66413f8a0e7837e060ad8a7a69000f5bd30e8eea69dab9d34626ed50d53dda735a0063f6bdde0c69218bc2ff0850efd41a3edb48097b02dabcc1ef3c63a8fcc31be533ff10e64a5d710cada5c23308ba52b5a012f1c861990234af72e07b3ea11430023b47bc1c0c54236d45c926b1e1d74f0c48791c4b86e8431c2e3517c8947b8a8100b1c9409b2761ef09c5f11f14b9a0dbe1c3ddc28f48bbc40d6706c13bf8f5eaad57152674f35466374d76eca3ded519cb61cf1a908a8aa33c869b670699706dec75a6082f343cedeef339a8c1d13bb5e0e08e567c983f1d25dd13a81f1a7794f94b565ee8e04766e6d83972bd43c7ee68a1f06b82682e875a9a96d3f90c1cbb7f99c60958d62bce2428bd5babd44e4179c48aa145932d53127ba7a37042636b458c58c3e3a2f321e0f19d696a9d69c8c1f2c0721328a74a836b15b2ed850947ed5ff45bda235d060f7a71f51f7eec574a1e46d23a8bcab6586757255ce3ed7b5419e9175c62f64c7acc5a1eaaf72669f52d6916883660344889ec4348fb9171627c5f9a2e85f421f52ad6da2d2f32241803883d16a1684cf85d3fcec4d04b99ef658b0b5c406b12e82fdbd746da0641e21aab36c5c286449059d5e2d7fd2ee1d8465b508580e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"94055334b02a3153bfd3c70fea5987668a7dd251776292d665ca17abc2bf7643","proof":"9c413ff614085445d450ae8a8ca23cf9c1032c277d5c7d4dc32f967b4fcb0a414212b7d6843c4d93195d6ba2cc964a6a3bb66a877d85790b14c0f239887ff920a68401e5374980b1030305f462d098f6aba22c2aa0e8db7d5bb64d75fb9aa31d6a22330deb3fa603888d7b1f933266ec671ba77c3c6d8d46f27990b57ecbe45f424ab3df785d1939c082678c9f22e1a2ff14f77b5d0df59ec9101505e000c105e5acee6594688c035cc359f35e55adcf2fd5560f438ca4337d38afe4635d2d08a5e0dc6bba98a3f515fe9424c8d0e67e14286b6a7fdc2b85313d22a885034a08c847e4d4833f8389a0dabdc68ac1118716c53ad13b01f20d63084f48cedf6262a8c9391123f43ae031ed69b3c621cdf1a1e639bb0b637d75a0761c98da129231fad649c95c96638c07b271326452f2c1c939ec328e3173ab6b07e127bb73513f2a20b0601c7d88fe5f839fbd761701d723f448b35d4e021cca9529acc275d112a41e92495d594d508592a296bd9dc17d8843c4609ce1faf62fd13e0e69705f78da27b4d59b7098f75a8655d7288a9ae76b9691bb79678e40b74573cb8f83bf33e6cb0b7502c1ed3d36ca8947d81f68ada301358389cef8a01bd3089c283589648efe709dc86e9494127f571fa98c9fe6bd9b6a4df2c11994c24cb9c0b1bbf35d3aa9f9a864fe0efec4dc194346a0a3123d743dbec7812e6bfc69ed1d60e7aa3924c412d3f374407909e577084cfdb3522db7c6eca52b27f9278cefb17756ad3c80860eecaf50362d05560b36d0f90a9f01d63387e1b06b6608ec14a7f102b9488021093525cf27ba1dad98eb6eb7b481185023417809a27ebc702805d3560656ce8ca6d1c0a72492fcbf10252f062bc273f493999b9474ef37aa186e93dc600a429c3d669d8f38884b5fee1eaf768da3153ee73d605db564238b5b3eb121c203"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"04e92f514804324c734e02fd7186937d003573df296de884ea8e5a31cbd90d34","proof":"fe40a3d2b8a2a3050b94aa02f0e400c3185522c8f09f39ca836e23bdd87c8d543202eb1ed4e19db727eddb52149345831e3d13fe9d7a6761773a5bf93240190d14a83138ee43aaf5cc1477b94d6507f1ca3af034674d46936603b8768fc6de1f945c76f04fb6cd1a33b5b3459f4b01968328c191367e2d1a24175eae414ee0606d0c058326114d5bf8b39efdab01c4b38dbb37c61b9388bee3dea5bdc4d93d0861c82501f234d50e73d34dc2491c48be9a5e65ba8dcffa1d7487a3ba1bc7740d7b01b5cf78ca0a3773336c06493e79c9932efceb950d95f89b8211ddeef5d803ece1bbe49169df589b21c2d77d58d3da9e71f08b82f4825809511bdf56236e483ac208d38b83fbc2476d660b2399ff01ede54b241185c933aa8f520e9823e361945356f7c0022c2e93e08d839535d3d7911993b86fb45ef83b9d0326cbad8251aeb6082b9afc6ff49967cd7d45fb743d73dfa71c3ad4964089d5a4f6681f407398befffaacb36bb9a0135eba20ca856f8401e15cedf86554882d4b01d7500f3e5cbff0f73f7c9a22dfff6a65a8d1c5ade2498aa2e773045948ede2d456af8e53b05169fc4cefcb9d8b67dc5e586fe8b481317b19a57b932a77cf4246fe56f116b6823572d46f645238b793721d3b311c4d41b2e49a7927f48445aab636217802b2542237a97e9e2438206a2beff5d4ab0457d11b542ff064862504168c40cf08d8c7190c0dd8d338886362cd4906c3edf5883a9dccd369049364e2878db68d3c70fdf01e7ec7f367b58469b9e1c893a3b2b8cf2263df9c45a3517f89da3c842bacfc0098b08f135f3872dbba6d338b76d71d5bcc6825b5b08968edd7a6a8f21dc5b13ea913047381ceb7eb5fbc81ff079fd875b47646790ddfbaa2fce182060b2c71210cc62be4de21a1497ee9252e60be0daa0a564b2575318a1a452d3ab00f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9a3c71de8e62685c1230ac641bb718734380fba30345696511664b184c30d561","proof":"1692c92d2c71ca75259b91dd4ad814c0dc910a31e0d511dc8bfcbb415b472f01e6c2f7b3f6f5d062b252251ebf049ded6330feb1207eeaae8c93fa71028a2c558a5d9c7bb2115916d2eeec421b5cc2e0767a0f28f5fc3c24356813232732685ae2fbc3590e47e3ee6d0bc734d12f2d34776756b3002a6935db6b0ff3c6215c746d57f66343426e95c6ea6992ece6832f76cadb870ea957fb2489ed72f0e7660ee6652c994ec454d69e373dd26eeb91ae2ea33ccd65862718373696834aec8d070b67f1ebc6015fd4e4f5b96befe247851dc7bd262604ae2da4a119ce4b2cef0b64e49209ede0513230ab74cc63d9380fbe54903102495960dcdf4ceceb7e235c6e030c53e9b336668499f756aa14c654e7bd69eaf3ef265d020d4ecd792ab6712ecda43f197719e7a0f3c8c3e7520f11022690988c4b5112c7a4e16028dbcf480e17064c1ade3e134a9e97489062b290618c969a4e53896af039be914eb6912db229e3d60b3112e4606ffa9a91289ee2f033457b740fa5aeda43dda211ee6f687ce2367854b4479db849c2b27fda4d93ad5461ec4948bb6550c34d19278f4c75265fc80ae2c0263129482ae5579232104d1bd95ef5273b6f26408c99e8a333551864e3c87f2aef20176e4ef2aa5bdb80a6370d8c393b30168613588d720d480392aaa24fca1acb21054d5f972e1d2928442c3c3535d75a7630738771e3373c03564f28cd6c6c6ce591168edd4097c67dbe98cb7ba7f9b4196ef950f8d46ae33c22c11a1a2252249a51d7efefe7bbd461130d8191f9c334b7a8e5d390fe7bab1e0c0a832e7243f23f1c52528235a72471abda80e01dfb47a999c16930065d0807834a898c8bccc1671709881158820e9a0b59c7c18a3362ba529f3b42da15f40a741d49b8e1f97953dd0ee18de0d55ae17366797c80caeba778e115c63b367508"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ac5911f1080e693b1b23824e910162bd9445d50fc428ed71410b7df0625def5a","proof":"9e162860bbd9fd54ebcc5348f6517abaec983d8c7f93064baf2a148b940d1c4572e29296f66867e2f9707cff34b42c446476b6aff1810dd94e1ecebc73f87c69482653dd505285a44654f3b7c4f1ff74b2824b748798e519bc2edf17b9d95a0ad289ffa7b1c9e1efb08ab297f2fb9dcf0e35705416bcf73ecdb8deb4b21c0639ae5b34a9914c8757351c25089d34458d55e8187c75b1c9092e6a17676e5b1f01df27da25029afe4e8620445564c3eaf06f6206cd41d1e0fb68d30f480017a7096e78157ff3140a40985a16da1b84e32facb4461f54c1be6ef70fed8173a50f0f1ce5703f02455e562282dad7a807756c9cac68bd253e3213151be731e8ccc52d48858a7a016159b2b3b4950f2c96a62a03abe9d1149d5f29676482590ef7630bfe6ad4426ca8c03b7f66c6e234f1417a2980c70e662733ac3d7c3f7a2d018d6e465f9e0be0ba3b49d0ec4f9080cfff8595a17f09c0fa46de7fef364753103827004b02316a63461f44f17d4050753c5acfe597d137a40fa86ba5e0a92a86b2162ce58f60b5a662667ebcb285356ebfdc5b105f34009aed83147eb6a55b37ef7e7ec05c4f2c35a3d18d5a64e210a4b84ab3e6c00c31f59a04e7ad6677b32e025372a5274d2254ba0fe599bfb215de96dbdf7c7e93dca675ca2e9169f7aacd2746f2bab325ff17cc04148ad781b8fc9071c8aee30c4601128c40b45c0b23a60f7d0aaa1290243193bf01009d7d5b818995888619c533af35e34a6250fbdc386428d2a0691a81b8d6e3b7e48244d9d0805ce095610b5251329cade448c48491a45498b1d9cfac6ba07e4c2553f90eb0a3e934824fc0bed44f340fc87ca441cc70027ecfc840b9c3969c4b9718f999f468d778da1551554fef8b40990b24a22c7c04a3c8a82f69e5c13f167bbaead9162de827b3cd4b25de609df61919c521a63300"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"80b807fbaf6a5f57a8044f76ce20786f5fab0d30213183a132cf79a7d9399b43","proof":"64c3d6cedf9745cd490ff89770a3260006f71287f3cec57493bacad21eb1d6305a3f62f530a39ed853a94edbd42350fb713a0279b7789879e3a67560fc11ee1d00720aa514711c345168954f2098195cf88d9ac959b084ff1e1ec6f1037a2c3174073edc0809a6e46613f211fa57ea240085f4f928e27cc887a3e2ea54771d480454e2f7d497632f06b6c4eca85813afdadb0eafca90d8688d55f054e63cf9003199273653e13b9b77ba6664f11b542703bd303facf95990ff83b60202a9850558fa0363e8f1fe76d6623eb958a88425500bdbef0c3e4604aaf0d763e2267f0736c633286e99b53d0622d3af14d64092939de075e0736ef7443be52328c17846f465a5f75f4c8e8de16818cb192384f2ac2a6eadf64a0ec4e8f3b65135730c62aa05eeb41509416af782106b3e423fb04ddf039df7a3f66c209583d8a9d5d16878302a63e00d266f43219e58ab38b99ec8b156165ee4ba46a0d99f5ef3ce683c2400b7d1eaf3ba90d7d13a6b35a922149ce52801299064b56179eeab7af1011c94ca18ec0cfb96081d54ae6205fae36f0f233e294060646092ceb9ef7b3e621f5e204a1a668855dc9535a5e991d521cbd51b3966be27bdcd11ba2ba408640e0b6a2b36d46ab52dd0f0f3230e6a6d23cb5e53fa95abcc36d528288644ff29fe24147d42892e9184a20ff49b1362883b20d8e51a596b8632180f0389587b2ac44904646631581d17d2e7364b4cd33097f0d19540bc8af836a03835bcde2aaca24e86c64bde52ee0d37446b2efed8b628319ff7a252c0c3bce4e9597d5ce1d57465783614799e69a56ee397f4bdeac801769b71d9dbc3cc0263fad410942451ea4d17144fe4287559f85a867118cbd3d2a3e1a943d4ddf55f5f787b0446c6252f0a2ec938121e05739b1aeda11e350b5b0cf3d100423335ca843c0958d93587c901"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"98ea210a9beb4fbb22e8f9da6fc60d2e83a4ff6f3b2feacde77b293bd249894a","proof":"963f61fa67e4bda5772efa3e8561e2e20dc9ad41a56e0afa7cdd10f86c279c051cf0d0252eac625032875715d1e877a7d9e4fef3b31e4c4f062dfde6d0f44002d86626954fe187db487102316a349fd1ec21b1e5e642685f2d2e065d6a73c81e527bf413eeb736dbbefc28203df67f721e61b3a1cd27e9a4d6fe912d71b6e83a98067f7a1ed9b457cde78bbf59dffc1bff247948927eb9a3637e5c0f655e560ebd4ba027436105780459068c9de8cf3b483bd84d55ed946f5d6fc245ee9c3e01036411dcc39f3d787bf1fd70ad2a70ec49202e6903cebfdf067455a295ff6d0846b1b0b8242511d73e48690843d465996f0544f1dbbbe548d8ec11bb73333f2650c6e63764151c781270f42e59490ac5ecdeb7b8853441776abc84ed121e8708fa849bf463a2e6c3f924d8b0f35b83dfa4995dcccbcd1aaa290fab254328fb3a349ae46ecc3d1947fdb9fb4ccf7b94e7cab4f794355a4164215b6f86a062cb625a855e75a65ab25ab2f10215296f4bca7d8221bf75fd2257f413a3de5c09236faa0cd57fa2a13933ae6e45142aabbb50f2ff501b064322a6f56f502444df2e5fa80145757c0dc2e5f58706dae7cb9fe9551a5c87d91a41093c0d846a793c8a428245fb9778daf34e1f900d0fb3af9466c783fc003045e0b7d071b5e2c30cd3311e6c0d5dbe6fcddb86eab080cb2c6649958cd5de89ef266aef325bc28aaecb7c405156cb22d97eb368391fe00d66a6e2a595be5df6d276fc78d5948be9b404540049d48cbf640bcf2bcd5828c60d1c48f8df86dbeafab5979ce7990bc3b2d1160499d976584f4badc24c2461542db326992200e8f091cf81f918c9fc4aef963908e87b1b879966058e274da1b41d15bd43a9dd61da121763605c7eabe0701f08554852d242e032af57dce39443454c16a0e94a2fecb8bbb3d316d59b46bb180e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4e75dbfcc438509b7e716c2d8a95c57229828bec39b7728125d93bbc1f7bea73","proof":"be3a6420fb25b98519b944aade2d25ae387c735852bb41bfd2f30a8c3b783d74ba201be3529ad83fcd896483af87d86bea8c91f2e0f08c18d8edf56744f3e92cb25ed182d0f98d14a0bfdabe945218d8611b12a625111b13e330c3a14ef4d67b925111aa500f6107bc4c1aff597a3828d4d00d0855cfc8938ae4ec8d886d907ab10183ae6715976d1018a1babf065618c241db2e65ceb389f3df9f933cbcb101faad57596c4f8e16966ae626b400df0e0d084b1b11c8b720eaccc6b40f7da903ca7f2d605cc189d8f6491f94ccdfd234b712a2b1344a4560c232586ded0d930d6c8d72d98203e50f8f5f742d1fb3fc2baf45f7e50c4bc3c0bbffb577042bbf01de2c00e427fb0dc740378ad56d2eb763eead83bdae703ff6137e1c529e7a6313444aa92080bdf3dd470ddcb8dc326a4b76155a90f293685e75487033e990d37a6a7f0f93165178d1c5f40df138167e7aa247bb1a34bb37212369e1b203150d59f8185fa3a56c3a616613fc77fd777f9b07462821647df9d38167b51785cd402becb26354bc6fa3780caea73bcbb9b88e78ef1a54632e73c23b785c3a243f8f56006279101d0f47d2e9dc4254a63bf1f09717ccd3b5c853e3c16d92bab0f7b37d9c3bda30ac580b58b695c650b4709f87276a7bbf566388c7b88e12c5aceb185102fb77bfa8de72c91ebc99cf5ccbc8a6986949513279522a1e413e6f6c61f151decb4f46dcad597f4c48615d070981f226e29fefb7fe0662698571e71e6fe8728c990d29a3185da119302db76edceebc49ff0fec280adf8b461917096f979c29d29056b5125a74f4f22109715679de1cb2b6a52e065cb3c8998773622cb1eb5e436889ce2e419e47502557c96a5505e949b27145361042331c1f0c9ceb9d3b09a7a41f2580743482834da44e71ee993a54482c962584079640e169f080b0f50f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"108f63d4850905084e105614c80276d05c4511dd052a50097218e36171039754","proof":"f4235bc905bdcc57700814134cc6e9e08cb35be0a243485ad4286322d7de340016cc0edbc9aced1689dc295c48b16793cddf62584ae838c52bcb493ce458f541ae4aae0becd2bf8994896b6b0077989b4b2f342f8000ec57e1c68bf57856922fd8c87d585dbe07ccb69852ed4dca929caba5f8dca49db97f061543c4ef5fd45626e41b27311eda8e199c84f836671b016ceaccde15a58c5a5d828f36201c14010430dcfdf6d32fd8fa0b2ebb0e2a3117fdeb9a57619ff10c6f1229ff61bc6d0cbc4bec10103310d5ff7944744ce7c69155eb47d8004aa4ea1ae363153f5a450f8e48542b05721804fc2b456b72a0595b7cb11247eeca55cce38bed9046e45518e0cb5ba19299be7350f5febe829d4aa282cd9d76e50b3b4bf696921542dd4911cafb345e7991f9bb830685085f128045bf77c0e23c792c5d79fc01096106b825085acc2c77201a523d984a317e5c429087347ac1ad1d4c14c9a8103917732b420ab8c70f73902099e640292315da5109ec8d9f28f6ad524ac54a5aae8c1fc74634447ec8c75b41e726d5251c66a2db2520abf2bf6ab0ddc6b82180be9db2eb0b66f81def21f1489b72ac5fd96f2caa86d8c4158b5c3178d9a00f40c34799eb15f49eceafc9d924c6ccb4787dcbaefc073b114aa6966366f123078bbf0415cc60827c86cf278bab4f075c72ee09d3cc3663ca8d2cec208116bf17250a6e0c68282618a645b26f9988b855138fa82c2e25c77c2602bbd3eb5831e8de6b9b0c681bd287c8fc28267ff4599409ad6dc2cd964b183166e93d9780565fe3b8c87bc5241005169641d852ed6def1788810f54fbc43d6a569305735e09abad71eed51a0dbf6ac75a093d63d8a1f66fb02f3ff550d99592f0dbf1519a498c6b8fb22e3806fe1f9bb92d630a49629ffb25f80868d07e5d89241ebdbc2e125ece9f8bf17407"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ba187b4bc1bf63d1832833ccad71a4238ff1c215d7d625210c1819d777ea3348","proof":"7089b7184139140cc6703a1935daa31673fd850301e806a4cb36119e69625817e8f9b9a8f5bddbe2867b359d4a79aa5407dd9d54abba231c7425b6bcf223a16e9efaa04e5cf17722f9a7558faa66498ce5dcda6d6c30f545e258c060e3695e49e617542f0041422cc2172a140c2268c0976b5d7d4c682345a2ec640751d4925d07d48fd2d88be859171ce03c7118b39df4a9d07932fbdc4250d649a71cffda0eb1c1c4c551f6ba0b482b2531ef1eaea4a6b711634481e4aca04c10a93a86bb00b5e732cd8a3ddd9728164650811bda4c70a28d19fe920be1f9cb37a8bea1d3039a523aec52bbf9a97937b153e12ea82ff092bf9868bcd4acac5c47338b5a425da85097a4a70838c2ab1447f2106e5ea9869728be3255610b2447c3fb312d1b0dee7930a786c759ffb87d6e7b6ef34d326a04890545a6be07326e562c2d50d5075a9bb5d7422e97971c70b9fd9af2527536cfb6f4fb4f569fc4ba9ebc0213aa6c145fa6f08183ad89815ec8234cf03af306fa2aec6a46989d810dfc40f985c92f9651ae041e560d9c8b3936f05036537fcb75ee9ae66a331d506d8869a98d1945b2bf22ee01e5b35778424ed2f45de98353292a445a6ede16494e334534142e557844f7517137be71c7b02e3145b3c8c76e26e9dbddd2d19208ada05b2ff163430edf052d745c268f7c8912b65180ecf5b29c61dadeeefcdb014a7b25cb453c3ffa167490119c4c058eb6444b1aff4d27e94007212213e0531bdfc8a67209e44ffe8719fba5b7359d07409defee8e9c87379bc159e2587e7f1f0cc1338c568332ee263e17a777f5a61f7ff79d2ea9f87e27062d26e248cb1a58ceaf33c0133c6c19c85e0be246aa6b8945fe3f6fd904f9abf3c1ee8e24722392a5372dae00b60907b0b3172088f692ccbc4545973dcdd6e3a895a4dbbafbd4abcbab9416c56005"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b07332bb8d6e0f0df2e9d6377ac9aa0246f17e081e2712d5444ca6b9065a031f","proof":"ae283923b9e608c381fed1e46f6088408b638e2eb250b6e6b14d82f163e9777d6aaf43361fceb9453dd0c55dfa627a3ef969d8d5ff71a8eec33ebc4f2c19f136be34bb490419d43d767786ea43af1b2f3091cb4dab0f5082e2d7e2db18fbe57f2a26e551e6680c42bf140b9a8e1b2dc4b8546dcf20206473ab3a3de93d5b801e9c6c239230498c5d8de258e4074795ebde6eb1273fba189763b081dbcd86f30df2e39c1bdd53bb07ca4f63c0a39c851b486752485a43636e4d0dcf48840f000c1fc45e4b326570b2f098b21464fa43673af1fb0d3ad8e8cfac901dd06f9fb8033a93b6d04b755a810ad1512b0f3c36a9cc3e97bc61fd6d605c05630ac1ccf01f7c614f70f2182770b55ce89a5c66dd797801777226783c441ae78a377393c545129216b4672dc1a7d2ccf2f715d57650cd494bf7ab188983486786ca852a9a08ec8ce803351db7b31d9b82cca608ee27ef78069fe5a1ed57714641a956bf237136d46713d48010e98b56a5f08e6424bbf29d13638b43f8ea0f340dc247132d61ece8becaa14c8e1136c85809f8fa0e46189fa77b64488ea28f87dbe9fc5e5e451695d80bed114ac5ba5c74edbf1ade76eabbd7b614881c54c122efc41b33d77acca1bd0192c38bf35437ad787f730bc1949fc00f74a0acc95ef964a5a812b34c3643f4f9cc410429b44e0d223a06df6cf6a7787d6279090fb8a46de63d8894052e6d2ce4f2c23d5dc2597aa8dd1d95e521bef85ee62504ac6f3a45f53216f5459a173d395156a9c0ff7e3039e16917459bb324d7f07b57a35cee8fb2d6a8f22ceec20dc64d37d05b426a9c7be148b3edbda2fbc36f4a3ac9e180d3c1c854c745da56edcf8e8f2f25a698af6b194f0f27e1df98e828dc3adffd5ace3bc3363904a75f3ab13882d0975d42b0ded1ec9a2a894aa241894384dbe621ade9bd2f000b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"daac6c247053179ff77af3c4b6b7e81fb94326159245d1afea749628628a6015","proof":"f4872ab50ee32c2aa74a71267e49db60d0054bcdd6ed56bb17486581c3c880614415096bad4f660a9261dce33e73be7851a80b92561c661ab2c20067b80c3c611ca19819acf843f700f086ef4d5b8a5dd9eb9d840dd63eb7890389a04914c6681ef526ba9b2b60f104c2eedd33a5361867c0456554b9ce12a20abb00ccb9eb3da1055c3a2fa4d5fcea238f0c0b5649dde9c51db447c72ec9e749dba70eaf540cb4d52c5f048060fd39e9867c4690bef8c9f92868734442b5e7217c9c8a86ee029784aa9498a345c73699bb6ebfe96b92b8f67586b29902076caca69d83165a00a0dd6b575bfd9443d19770aa0001158d86f64a5724ba8d4222a83432a76ee166deca2ada8c61bbf466cd0b8f134091797dfe4989e99417ac0861e4442c59a7345e3413bd088fbd594c2f34c676bdd10f6fd3b6c3267790e7b358a143871fea591e694bec7333122821b915616660c553072d2fe83f5c20f73b8e922074644244985cc63891cdb19700521219bd9abaeea020579955064efd15d86698575dc46acac6e1472f4a2bd2755bcbfc8c41be580d92147701c93d7e2d8a549a2f5c362694d92ce95b8e8c6bb11d54b1f61b6041702f6b39b1d940a1e8c9a08a99c8442f2cf8ccb759e66ffc20b838ff5d12520b1e443c3f081fce8bd4ea565abd22d810aea41061afedafe4082e741ede9a8099aa3b8debb27f02aacd350234d864064df202b438c3df7a813319b28b22a00dca12416dd44c826525faf2af6bfd68c53c582d4fd0b1b3c6e303e33f57e6dae670209ffcaf72cd330f2a1c95c717645e52a070ca37edcb1a6809656b2a341e8d13bd66399dcdb12f1745480694cfd57f6d88f05a009d5993327504d4eb71267156cbf20a17b37e0a4918e890d519c74b008cc9d2aa1c0bc051b1792aa6cf72cec4360f53eeb5873badd82fcfb471b07806"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b2cd732e957a661168656e1d14e9c49ec70a73bd2a040034331120465fc94b43","proof":"9ed33412ae9c8bcef22600ad259b4e4e67bfe12dafcd5a7336e7112559c2b22fc66a39a5896f9b83f387a0823c4fed44be15d3b98cc41744778958f834440956ac6186fb1ff8841a35d690edccac38b9abc175883e8b6eb7999d09a2ba207c0954a1bae2ab84cc9635d93f589a3d031edbf6c08fac2684128f6d0737c0b6b07283f5cd715a06a6750bb89563e16c1977437b620dd14bcf975d470a257929920c107fefc553c9e24cd4badb639ef762d5120e1ec79d008b86fc117e25e10f8d0fbc9e0da920cdbde38b6b9ad44604512b1c6bceb141b033a9910925d17692710b78241200386f9f25ca16cd20ba927be25b1e742f1a00eff72bdf7268e3dee24f44083e99c4e5d7d8aedbf1e080112a8b07799c1e8d31f56a9a9b7c6067cdba6968247cf01d56a13c87acb786e06b8527df14363ed8592f55d6cc6251c836560b9cce95892dfcef6967435e80102cc908db9e449e12330b16dd90030b5fce7e4c526fe195df19013fe23427f3fd55dada2c70c8538bcfdb7ddc2b03930cf32a39e426078b22300537196329ed0b0a0db6e8fd8d979a33b307b608609460bbca489aeb9f5aedb70515f60295fe68ac6dae13bd6cc2de06fc68198736cb8b197277a25c1f3c01de82f6a04f527d3434c756bebe25f01de28f711f8de70e2a08c25f5247495382bec1faa5f589fa445ff782d552ad11e52c205d79aa4a4ee336aa224cf68c6ce276ea8815df9f6e09a9df0bd3e4c18a4114776b31b8cbcd2203a84c2aeb6dd111bba4a5dcb6d3c71a5629995a5675976465af7ef5855486e76cad15b4d60dcaadb6e53175626860fc7ebbb9a5720b464803b17e75269c813b2d173820be4de7bb6f1791644306b2382272bbef64da5885b74d0f6eb40224d7be5b078562a10fc1d6b3c3f4d2e84d30a93801276ee40d140b87788d79d98e501df205"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ba6e82554a7c035d9f6e649ee5d90b481c24abf392d46caa6dcfde2905ce453f","proof":"ee180ed03e53947eaa79b17cdb36eae6b4e9a2718355b9efb7f7636e00824c43deaa5c444b9f89a61b05a670993fee932233315be119e7a258b1278b4665812a8acc5c0c3961788d1e3a4e264641aadc46a7f792f3181344fa5b5e9569e8ef57bce9b94004823d9da86c4e86803ac0e231c2698687e9703d05967cb07ade731ab3a2dec7dd8928df35121fef99bc43eee082772888cbf7446afe06a70c890a08b38ae4a27e9daae4401218ace12cc3c5afe3ccd487fe7dd690e1c3828f64a2095596a52c74cd01bebec2794701c60d312093ec59c2190f057ba5101689aecc068e8a622d1af4c43111c26a55db8596bed83bd7df4eb6ad2381fae77c6540fc79b42532b227e662a2c7db6f0de3716a859255ff29a8679f84a1379f11372c57635a59a1d546b3ef4b30675e30d1e4ff5c870030672d46f145ef9f10e77bedce50aee6eecceeea954c203d8f4f3317a41b9fb5285ea166d255da88818d3567c24b7484eef48fc8a50c0125a22a0b4c0a0e0eda2241fc721c1a056bef4cf111dc64165523cee812061b287f5deb4d2984314fe895afb6a93b6bca1d6401fabb68565498b8e788bb72b496dc76b935b6bc3e5c0680f824f701af99d3bb51921c8c058cf267f858242a8b71db5df13db37eaedc6239412db68266355ec5370ba7da16fe6bc848a8b5554efeed3e5fe673ddb3946ec684c869975306588fe4e340a133fa2465ddea0e7c34d60a3503bcd4355cebe0fd1660af58ce001bd593f0e5570e82e381bc2621ce738b1336776011945adf481f9a88c853a9bab4f4a682dbd31136766a29bc52c54c1e4bc741deef4f0f64e6974595a0a034d92531d88a08915ea973d2abfb212824334fd813883a865c81905e86ea281c8f629782dabecd5b0c132fb75f0bbe9a71f6c5ef0b76c05f34a43e7a96fa8dfc79a80e17a840edcc03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"88722dc2409504e1f2b6417c6fc81ae225bbb849a826358742820adc33883d1b","proof":"e411663750fd6f93f41be5061f8810e8b2eacc043bfa271ae1268a2747b0444c1e86a82ed7ea24c1ce0c94aa0352ed72c126a4c563088e087af019a1f8cc25028e58df49607e9d0cf638f14428dc4581bbd465bc2c8bf5b8395ad7c9f0f55c020aaeb9a8aefee6a9e58fc5b7f69c06e19960a88f83b870804e2ed2c2ec27ef703edc44336f2d61a2f86a9b51d2dace8e5252ec99b5efadc06af583348d537a09812adfc99ff228d3502b0bab7b5d78e56cae325d6bcea87d8f9e9330ba777b0fce1b5d5fb0dd29bddb58f20744c51882eb92a7c11b9fadc52c363e05f58b210ac6f936e7ac13387ac9506d86c301879db5ebafdb930100a44c711ef066f64c0ebc99d6c302bc9ce79b4bbfd80771dfb69286997ada271b4f6c063b216a1ca56522d4f9a66c2297a3c57adc3ea7725049830c1b5a02aafcc34514051e30b8de1a4ade3fa9a64e418f7c098f856d5b19d506da36b3094b20104b49407cd624d262867ed3abf528f37e5f81fbb259861847fc1a5a5312f507758230b41d77e6aa56cc030867236792844bfcde09ddba63bdff64772092cbc5f1da6c0de295f7e25b524d018fd15fbdfa595b19e65c8eebcbd8eaa78d9296d1e4e9a31549c6e3a560008c1fb7d83e0c34da6da4099e2df0001a634164696bece5f8d4de63d1b6bc226453b0c0fad595ab0a0e77cd08f3c9e1a38f6012ef21826143bbbdf6bb2a980ad0c851a93658989ba0c56025ff8b56617aac732960a5e0e3aef2da57d17ca245a6c48bd609856aaf15a2a57cc9b36b8cec44c78a601d83faa9263cb19fbcc936b43493b44533df8b5d0dfab8f200ab1cc4fd1d90897710eb4d227aa3aa9bac2951a00fe92be12f2b25598e5a2cea7c0dee16fb3c2c399cccd90a832fc56c2400b88cea59261f1fd821784681301616814dda1d3ad285473b00f087ae83ca9e04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"346ddc42eb1fdbbfc9b952846337d97f4a3bb338f2b44c5abba01a466920556a","proof":"eeaf5f388c2e88170ee7647b24b27190f4a0e02dbe43e2583b0a5d5704b6610efa0d4663dc60f0024f4e6ae018bb2aa612868ed3021ce5f8ee233b507366df5e1055d89db960aa21080fe0602dd8344c6fe7bf1d56e1302004d939a844bc1f38b2fa78662ceb457d7b56e8cddd3648b3de5761295376fa3195d6b68f85f1c00aa812be85cd6e28882f69613e95cad6513321bc1aae07919fa1c2931878f44109db78bf1c5809a21cbd1a80dfd27f0f7d5ac4a50356928c3a1bd9653f7350fa02190bc2745725ef9dc0117e4b1a0bcd401309e7e330f53c62bb144efbf776e9069641e125ebf7d6a79f215a1fac0168853d4a3b1240b7467966c6a88573f3fd41faedaae7d92cba7ff35579dd96f333585b6da3e1192cd095be5d0a4fa97d8f2f0c638cbd914a802f1b5c8e9bb8e310aeeb2d95298ad4b873acf3949b35968651f026de6284c642d2a054eb6882bab02c9d255efcb9b9c166e38a0af746171766fa47d824f636e876d29adc5c556860cf7ad932779434296056912d0107d0232ad827f03a38c222a269f2ff8a53892d6488a95b1d2663f56aa720a3b5438de0730891832a2b7fc003a2797d6cac81501712ed886383f077b1f5a24a6e98721973d2a777c448df143be5ee1080880fa07f960853417ce360ea4518413fc930b6460e7fa43a16ca46116c6d91f97d951a7c84594ec97e30e9e24c6db105fd701a44b202aaee05e35570b1103459fb9433fcd5710ecf843c207f306843a3419ecb3c7a777ab73a1fb9dab49880c670e9d1f854a4a0f8263fd45a76de8e096f9e837116253668aeb9108404d4f8c9b6a99b07a388c23e10c2671ba1d664bcb6abf150339dbc5411831121858bd8a8f11db865c26098fd7425ff5e793fcc8de42ad90a26a09ce2a99a00957d131e1fd1ef9b73ac0e7e47cfef13d05b29c44df2e99909"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"40bd5fffc1241679c46b1480dcbd86100cfc138ee5dff991bb6c6fd3435c7229","proof":"e2b1ca75a36323257592e4ec70a4cb9e38cc8afc73398cd48df6e9fd2ee1395cc231dd70116b3d64a1c29f1704ee74eaa3dff399e598be30ac0d060efb117a3ed6a4acef39e8bf682ece1455b44fe295269a985e712918440b2597b08caf73230a3ad6941c0fad3613998b78a738ae53001206d3902c050cdf0b10cbc5131c4cab6f07a6630c6fd77c8ad1a99a88ea1b7795fac81337db7ebba5a4c4fc43fe0121b94f8e6f170e1f3c26250159e81aa1fcce0203187616f277a67724869eba0748f4efaf030a721ad7036e3f1528a246972cc5639a1ebee92abac4f2337c070552f345a4590d65c67790a8194b73ec6d3e8d4a28826d93beca14d253a567281ce4603b55826a4a863faeef8f1357868d236fa968ce918ac7050f2441d4eb0a3c40e997c58f744acbfa48b5dbcc9f922dd2a824f8bab9a932c881db1144b20970b099665bfefb0156e2a8ee1ff3ab32629a6acfee0f5f7e214d576fc4126f996a04c3e1379c09ae677e4953baf028e29fc3b986c4d314334655a9848123b69a67e4aa3d637bf4eeb9fe8cf10066f0e744071d7e56bced13c217f512615ce6ed740c3137103686d4a19271b4cba6ac10a92a39025baaf555c92d3dbba23510427fccff5506e51959e7608923e4fc15c34efb284e4122b9dd65424a5f013f14656b2c7c945ff54efaff60b86af3f4742f44bccdd559b82f2517ada09f3bf97a46304e861f7267c5dc71ad2332addcd58931a1629f1592ad2de403dde316baa8555868762c0d7c2245a664f983fe8533d9dfcbd7f32554242a83d8930da87bac6f1deca8bef6a5e144481b0bbd1bc789f42027548a17239253e19e3aa6a466b9e51886d704fccf6d8193bca22ccc088fa17f88c7fbad169fd4dd96b046786605e20cf41defeb37b671a784c75dad8d6d669e2ecedc05cbe23a572e22b4641b659c08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6a1439f913da47775b766aa893d8dbb5be774481a69caa3a5691fb1f57ada813","proof":"16ba3d037ffe5e30e1d4d7a1a2337b64a215a9e81d2598d02f42df17330fd57986ed78e71d2f480654a4694f50195c9ad16071004fca7a6a30d2a8bcf6ff4e28a832af67d408c440b4a7ac25cc65b8f0a79d2c0ccbeaf4ee515ae5309671b56f4c8957a1a50ee89791e4f22a99875d557873449014a9f1f4cf116f207ac9ef4c270c947274f99535858a8ccaee8299fb193b9a105d78e36f106f2a5b55a7a60644a317e5523cab5412feb9563b1dd23ad9d313f9e6f71c9eb8bd9c639f53e708497c1dceb73ec0e45fbc947c4abddf6a64c45259520c18b8ac3ce8766eb94a0b5e40093fa4a9fd6f7db89dd97dca7e39891a6e0c51a2419ef005c6b68eb28348def54f1bed41518c00ee8cdd8f7a3bef4adfbb69bae4e18794ff992a3a33475cb0fb78a10218b03693827f23887e26f076c230675b00b8f7b44ea97ee024e97e3ef63149044b6aed4998903a497a1ba41756c8b7dbdefded70832a31898c786c0a0262305652fcf3bc671c7a92e19e68ec2b827fbc4480a98b69e39786fd9e0670870cf3253d52ecd5ab9b99e57f296719a9abb2226496cf2bb06f8f21d54b3e66805cff9740ae86c8fe7a43d85882f7b69f8c00654689c722f211150f10c270e00f4202123c9b4279cfd3acb28598ba88950aea86606a832d0b8d63dc729842dcba264be711a25ec8419cc0f487588e24836672ae99181a8b1897d0d3a92e5fcaecf915bd3a549ede4bd3cb6e46d296698708efaf2b878078beaba014b2bd55f0714228ea2219cec125792e1ea5999ad0a0ed540d9daeb25280cf0e99ef451c9c1693f0153a86bbe919e7614173f10dc7e71c42dcc74074808d8f8a8890a801dd4dff007a2f024073cfb55a1d2118319a9ebaca683b33396afb0235aab4c60a5581194fc7e666ba3966f335713add6a2d065df99dbaa33bd857f2d55921e90b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"eae8d90860c92b4d04fc9a0f08513ea61a0679eb552b544a6ce25d676ea8c859","proof":"be6f66bf745ba76c1967c3282bf30b8a595485877b7e921c0fda91e52f8a5c1c6e206632c55ec83dbaec5eec96aa6aae999980fa0805797b8daeb301adc8234b6804c79fd85b92cf4e0fe9df41f287932d98f374d2800c77f5348607cbfd9009d4367a3c5f3b9cc64b8ac49cce6520bebbfd5aa52921afb73640861f4475e73c9469144fa004b4f3e20a014f07ea57dfb8204327f7406a265643e2492e0d460be7fc2ba4bc504bf41cadf08551a766cd4bbaacb8ebb6df40137f0f0c7e6a4807780ff1525132b3f08098b42bacdd863e66bc4ef18c4295d791ae0515db24ee0028d269e665f0b220b59f5d244d1b8ee035a6558c6402aa95cfd861a1cf1e9e5b9c8bd2151aac1f580b2bcae82557f1677c97bc0f8c93e7debbdd41859aa8c513a27889292aec1c87ff1ea2af36e376ed57f07db08b4efd09105a6d2832de560a6216d59b67e7e7fe05e65acf8e4fdfd56a55dc5ab88c6ff4ae31b701cebb4b2c6e6dfb079a2559d1036d24445a13d1455418cd52728ab3bcb11dd1b94ca63d77b450d79d9af9873a63f1bebc697ce1bae60c7628868c4f4be1e8703aa582702670857f92709017a22e39e876f2da3ceac1e412b965b5c60b7e59889ee7637f41e0827c3f211542c949b11dcf91e15ca29b50f5f93876231a66a2e2d665b30601de695409d2599d39a505131e893d5b3bf1428cedf4de97f433bc1375f0665c7664c71ed79d5e2de06cc0e1add0e18ff6cbc1908ef1b871a560edf686f6e94d3906888a4feaba039babd360cd6948d5c41ae7ed99be97c4445047db0ec256416178095644311f8abc768c9b6b081bc7f93faf63189c6abd27a1da597b4ab7eb1e5f9d8d0fa9288ee8c68095100f5e24cb463186e34f2f2acf9ecfbeaa6c7b9e0b41c499cec8930a426e03475d1bd06cd85b37cd30f1c147ccd537531a2d6d3f09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"364c38a7a45ee1dbf57db219fdb914ca8afd3a76600801499641d80a7683632b","proof":"ecc37a757aacba653a8705202de30daf4d695c29bd9f3c63cf4c04da8b1f8f37e05395eac5365c14b210a11100d303741d9eabff79a156fb94681dce7327ef65d88598d107073f7d5d1cf5ff04a6c099ee007b53a3bcb907812c0b6a4dde8124aeda4fb1168ca75dc270561dbfb6cac7a0dacba9dafe6e4c48b267b177993133cf7bae2c346846bc17531116e456165287b59483f4990cd2c7137379d133630161d3e7e770e153ce62df2f40fb8090fd69219344714e207ecc753b4fd3054c0de70545d99bca7f0ab06b3c00f803c05632946ed97e99ca6f14c77fcb848bc7057a04b2c1505529b9f5d7f70554c26e0f5782992c29ad21b8e3fc4533e2a6b41fa6883f9ec61f6d29b88ff562289e90a91d58e5240dd27b04d0403f9bc2e6fa7bc8f379f5f1eb7c1e6076f927896460c29382988603cab0f2f08504a0fb4bca3ba860c5c1bf680ba8dde8d844c272c4caa131a83e647c04008a38c5260bd58c28ecb8bd7943fe613bf3be483e0fd837a64c232a1ec6335908c4a41e5eb955c666f867817e1feaf1e1b1c4be9a8383e1ea2a238af550796adf9f5ff6b574348d6f34625ca924cca2e74d4eaca1cb6ff998a523bc660be525294e6ab21549c37b738c2f51d29c251fe56aec703a77c7e1af656eec2d4ebd1fd105b24c61ec6aa45ccc1eddfb9a58b97018164fc77de1b344aa20276a3cd318881b58f88e5ba03171c0b51e7948c6106230303f1abb4dd9ee13c0892b46b2d87194a1c314b8baaf66b4d5d484d7cb3c668e728ba89f94a12d5c99ce12bdf602f86b0915edc7c97d2692dce8991650323cf01c1b708aa96bb7941768f2baf7da067f9db3a62759e063fa67d045db1ae3f5556c61b4b139da0510d6b0743288dc965648396bf2b8150c3da493c0aded53a7547f0e5e48fe42f917fa6fc85dee5a11073ccb27b7ad2c0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1032f91c6607efff49550005d3767672486f75dbae34c7b2c9df4440fd46fe39","proof":"b458f2a157aa066f4afca024b4d242f56cdef7d8fa78090d472729f267947910103e2af17cf0f33ccab9c48ed8fe8b76d45a3fd5956166dfef8c63c837970711c64824a53ad6c7cf1278e40510d6ed8be0eb6a2ec18abe22d6b8491db6c97e566e470317039c3094205d7fbcb6c905b3f751aa7a23e81c05b923c65d22295d233c4b1f295d3e5f99d18e0fda11fb8fe1cf1c902bdfe54c12359215c33a7ecd0e09f4d0228dd4c8c59ce6f3491d9bd3fe0860e15af59fae5f994d02ffb8b9c00623b8060c3af7f2610aa071ceda44e36a6be12ced5fc28caa2f9d87e2819184085459543937b5d1c0486a5394bf0f79efff90dfd5a40462944caa5be77313007fecda6dd840155b5d574fabab1e8c88cbd9c10f231fdd1e9da431a78a63fae56440583fba50e221135dd7e3b7eeafaa76dd08e7fb414c7b5996552a920288d62130ce5595ac7ec0fad4976d254827c0b8e6e3b865f19440dd25019d6528c0e52eb829605f690445bc6b89a98796b9ab9cf92fc451240fab725b2439df6300af3884a4b321a8bc352f8dc74a9626effd83c551f2fb7d320dc629e7c6ae496c880f1c95cb359642ec542f89f0cd3adaa62ab71b80b5f4535bc45d5344639a27cd3694fedb6a2e12c4a30895a4b795c910dcd12e28563ab87721d4a022e5726d5a3f9eb96fd6b4155447753cc2fbabf64f208e095c425cd6313774c610ea63158d04f6041cf58bb40c65fbb684f4a88c83549c6b42035c36112e8c2fccc71fcc5c46a678c5d8d7cf4dfb6f6abd9838b44a79818c741bb3cda1277129c07ef2042703c8829319056d7b0ec462c11b4d94e56cecfa115d3ed7e2b8d438395758d8151453a69132b390b5dc634f114dbe69291d5ad3fd7288d34fc108340fe1e29b4a052bae42abf55eafbf9673625bc7e0c7ad69387c3d33415a3e6c1c9fccbb521f06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a851c80b3a2e2ca65175c671e6531784f887bfb343c35fe071189b10a1af6a7b","proof":"1ebe5df1c13b5446831de95713c7e37c2eea56cd6b54d997b1307a48ede30c788426be6fc7db32abd3c22e796dba1f11b02f253fefaf9db71c18e1da0df89d0346e16a8e58269e5dd2cc08b66971097357b644dbc78bcb8075c233882b067c1c848fe079b1964ba5f2a866184936e710990c1a2adae80768f081f0655064904bdfe4aeef2ccf3187338a29166561f5a3e661b0445a5b115e7c5a46850e0bd30bd2bd7f91c2b5e853b51885082d31b6ed8366c4c0c0caea32459310eea176130f48c473e42b5ec5c4518f028f2fb626b6a370331fe1e94661b1af2548de8a37068655c50c31201fec27a26fedbb333f2fcc4188d283da321b0f08206935a5e22240d82ad0ef2e989b03867b364aeaafef74f4893c151de411708bbf9c139ce308b04576170be8a3aee3c75e75a0ef4249eec01c808f2bbf2beff2b0d190b1023cb6be1b6d62fe3b8444792654bf865c89a6f752bed8fb2e0672120baef5971112bef868ace4fd728941cb968c304a3d38b4731be04efecc5837daf93994ef93393ed8d90fc28977fc38e39f263f696148a9253913a2ec826894e347e27f682917e868755bc0542f5f9b03b7a2c6884c8a42e9dc044eb1598f45c15edd68cacd0f78babd32c60e71e241cdbfe35cdbc47f0a3e466f4ccef70fae51b822a8cb6508860803d82144eb18fd219bb6054e6fd13d7ee541a23aba4988e322c53e071b4e88abbeb88f4e757eb70afe7f14951a4a25c0afd3f2585df586cfd994e1ff1e7a7692e56957ac1f3115f43247352aa52cee9895a30f9ba01f5ca672f05219d11526834acd8f9c192cbce566e1f03ec077082b66bbeae314ac9b71028b15b2f039699357196257e605f1299b926a7c4b23b58e2d251b85c6a25d7ebaab28da8b03df49abe7b3bf98804777479d0bd28f09b007c679a994aca7693b2d0f11f70d0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"90af416c62ee85f3d09a0eb2e883adc12356946697cd5fb8545952e844b2a077","proof":"9c1d1f237cb84552e24d4378ccbf7e76d37846dfcaf7fa01fa9a40b9eff7c91ac4363d7089f02c777ed84172de82babc5aa4d9bf3908bd8192bdc27127a3473eb4854f16f18b49f91df8f8ee1446aefdb050570a27e85f74d73228c93044ac050ed82c5bf745b9c433b67b8ac098798cd55b1bd0ec7eb4d69d469a5268cdf1447b38cfea0eab79296857668018571e20b226e835899bb766144c0ad385fd74033b96d651f8f2582fb6e30093375617415f3d4684bb898c6e4ab33f7a476f81075d730e73b738368f4ad7ea97b46c499b87646aa925150e3b3c9d06cdb5b1b1091ab2c6fa02fe4b1a4b0500cf7e22803766fa7bbe0c134088038fe5243cc28146d612a425e130d326edf70a44086a6cf803b9c83e2a34ab62ad8ffcef48d0fb4ade0af16e78e2ebd8c200e6bd7cf99b359992f360216e64df397d0a9343d30169a8f2ef3b8cc0361681fb7da285069d68974f708260d2e8a51ec69a55fd33d655f09b3692fdaf55df805ecb69a13e145b0a3682091c0c849e4289e5efbb13677e72f63556c92a7427dd10f09f4d0b7f97b907a8695482309de8dec2d0ac63627746f8456bf742c41b3e45f2debc53ce040d6059d5d3bb2a7d20971145fae3507b7c1fe99348859894ddb82db72dbb8536b00615197d1796ff00df973464cf9e4fa4857c1553e534f3b1b93e4c032bdfa528b78db13bd9cfef922b5f907c400471902917d4480d52e8869c1cf057572504ad60d10edd9bf9ce4a1cf59339e40d735c2a90ced933b05a172de36c3cafaaa94584b929b5efe327e095804af7bbea6c760d6eeee8a4912384c1294eb1a5f1759af2fd5696fbd768855e38187ea4c343b1df02b49ecfcd0fa1dfd5bd905e0368761f839768dd8598f3a3f4c7b6e86906baaf662a0501556bcac2eecd9ec98afbb62c311e94e02c9ad45f48d45d9d3e01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"769580d12ea93074025fa37f85cf9104165452e29e52bc05499431c5f8a14774","proof":"0807875a92b40ea34ec990814c1c38ba88e7ed13ed0f80fe6bd7f6012311466ae6a3f7faf462fea8f75ba61661175b6c0a9358ddd651f721ede77478d334832856dede6f508f218be7b9c9ea2b882e6f1d311b98d6156ed7a5cf9816ee88e2634806821fa38084f9a0a8415c37c70e5c6292f824471e3fbd0bdbcfcab741fc1e0a3cd94cafade8180cbe382b2981b1c850581a6443d9175ee4c8660282525d0e7e3912639c9beae458a6ce6345bbea831dd0e69d0fae05fa1b78143733fc1e06b4e1e1a102b9f6ae3aab8b32eae20de9b49e5302b440dba77cf2d1bb09c4300e48408737020a513cccc41a43334639f9be01bc111f39a7e503193418f790963010e6b1829d8e985161449bfd089121ca5e3a649444613457ca4ca37d67c5b55a0e7bb254549b6a8cc72830cbe3566a782354a94da6639ae7d85b14b94bbd0a52f63c045badd47238ba52f3ab78e065997cff2710b97e16efba78cce20cea9a2fc8bf941a1dd6d60b455f3240c72d18cda9f730ce4ef2b7d44e5090041af06f58807deceb87a450cf5b48c31424268e0a3b5bd1e72a85fb7010b7512710d8c42964dadc26c5bff84308c0bf533e41c1374f0e518e444d46fc159201264b46e0756e9a42110890f8c4bf505dac432b37848f6dc7bdaff413e2e27860f756e45847883b7189325b9855644ed226e5f2fb9c9f8d174ab304a32faeeca9ed4c5fc240ee53c4c225b1a43f4462010e23026a258af67f737be84040bd9fb5ec80db073faaa8e8c98fabe5f85dd26be7371fb7a80e03b94ea2e748d5488fb8605d3f416d7a8575fcbb9f5e2f8afe40304b7acb63b4b19588bbb1c70f60bf44af392a4407b042b6a2f281cea545118c2ad23ea6a30e819c7e13cb6b24ce3fdb5b6c75ac0050d7d7d06ceddb74294e9e394afd8c23a18eb3c9510895b70143df76917aa100"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d0ee0649061e645d0b9bd45ce9757eb75975458d5cc836ee2d6f7cb97fd6c25b","proof":"18b5b7e971ffe52160f9c5657091b19fa8b0d65ed0682ac5ce149e2b7d19a80fb4be8c1126f9bdb6f33c46637f1b7c6def0c9e843921f31126b2083122d9275e3eabdabd8978e290d6a806b6411a9e0a09555fd4514a8f74c4d69d6661c5f440caf8bc97652a91aba6236076176ed80ce97617c19bd051ae10709cc55c9b9541f33e7af955ec06e6c627e8887f93ff7c2eac5fc266c21c7001d55dcb415f8f03b0232d2887a9740a876ee43f51744615f24553c14dc0cecff3590d35dd021f09a8bd3a195f739e2002104f27187e9276f4dece29dc1ed13dc6be37763a765c036effeb5caf443e7d5e6da01ae70782bbd545331f578eb2fcaeb675add866575440b2a8a08a4776fea722243f867a66a48d442cdf874ed78200013399f72afa35c2a36cb86ce8dc1d85afaa6f678e629b9a0c5d36ae62d0f3220eb0a64cccc5275672fbed3c473fd001eeb6121cd325baecce82beeda38cfeb53180bdb1455a46b2d9ed3b035c680b1ba9cf117aa654ea3907579c033e83684345d3fbf685c2429eba75bcd39992b1c373c7ad8a34a8225591921fb7b8ad7916f008f733a04c53aa02fdd704e14bc555e9bcbbd373db0c8c37541777a1f69c752c5541b2dd73644a9a7cb725cc41399cecae99305331c47cdbb255815f9a66f1c59595e64ddc7f9a78c1c2b24ae94ac1f1cc60f38765476bae53ea343f14149a302c141f69c07dc21bce1ed85f2c421e9422d9b8ec1b34663a68a7964ecaef97a013f292a50b2c38e5801a8f35e59d22e2973043ef2098d4fb4db77355ec2004427c8127be385d1c41882a4ebc80fcf0f26764cf142ca212c9cd192ef6349cdb30937900093338f403210ec1e2deff0c52c3e3d09d5ea5fd617047d1e3200b5d148be97bdbdd0cdff57b399d3a838fb88eb028611e58f1c7865f1f980498394bd29039a1259209"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"90faf8e86958053ac84e104a5f6624373388e1566c915bfa1d53a72ee78fa813","proof":"36d494a577e54f753c8e86c521265bb2c20087aed6ed4ec197d564b213f7367eb0c4f86bdcfc2bd13b50fdac262d96ebc662b434be808f87af8dc141d0e9ce70ba8412a7bfbfb6c1ecd42c2b007ef0fc797256170aec2c37a06fd7a11c63e66b783d4db087a6aac9a587b7f5c1a895a1d27ceceeb5d39c3cf38d0abb8860bb7ee44c58f0309f2e7fb8b6c8190d369d3bd5ffeec2d4aecdcd7ee13705b5df7a0d95f4991a0cfd593c52422c30abf0d5c0dee9c4a7497d98efa61adc041002db024eebcbddcdda9ba7f77a811d5eb7a8965d2e2d11b168744eae0894e7e52a08072e7fc9bbb3879526932638aa847c865638fbcf359dc39b9cf466561148611358da382ede14cc0a931b950c497e5c74a115f012126ddc318f283a76409766794e043096d896ab25e058ee1376b70bd3d14f068546e8dfdf159785a7a108f108798037a7a5d8c0ec25e5f25e1e309c7ba9f721a2f41d9b4fdf513954e3b401336d6c4b027b4a618715b31766985c532a93eda3f596cd9ac1643b19364730c7294f9e8d75d1715929b2778c5fdcd7766a02628f0ef088b28612a55c45d17d37611706857ad6ab90f116574916a4b6beaa928320fd30c6ce0a93034235d5100423745c751c516323133160116a900055bde0a433c65b00bb67b393187c6422c2c46602717264c8bb3b79204780696de05a3fd4a68fa464fb65cd74e53d24cd8a24631ab10072e17256be905658d5a19a108a81f773913143290f7a4c852133747041f06256768801b61c89291a5d4aa29eb8848a167952ce325fedb48696f5afc562065da930f6ba1182d1f8902fd6419c7ad1b23d717523c421f20ce0c986352d21d7a67d3fc99f5a9a6065c690b850c1cfc93e0d77619858e7edfd8eb98993b8001a2ea07f7004e740a870f2c25274256f62ecaa9dddcc91bc6c9ec9f56d37a506"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9074e311df8fd4777cc66ea4366d3bb24d70a0c91dd65cb027430fee5d6e384e","proof":"facf18f57a783fc1239f191c093a966ac443ca34d4ccf0807d3cef46b99e6944762de30c023778a967f78bf7b4dc88e5dbc93cf1585106e1a22949ccb652530f1026bdc9b4f6e8c1e0aa8b8c97d4c497eb29e217e452a19f07140fa42a9cb57e92afdaf79dea8b558de7dfa34952cb8911bc96e974a2fef256ca334e350eac33d1f9b9f98f5a8721c6a555ab1bed3c6f2d01d14aea6af90b503a1946ff2d930dc9eba47267c38e4bd515ef86a5dcc5444df1e9cbd0f647b2314faa50f9c81e05b336ccecaed9c0b454bfc6eb2df272ae7a158d9b8b18b0058f52735d50748e041e1af13d67ef107abf2b1e2b0f14cc9ffb08dbfbd84a874ad103690a8cce0c6f40bfba2c1f1787da0bc47326733332b26495c178611ec4a333c7a907d896464b70f5082bcfe943b56a2761b637b6dc032ad385f2d1f42fb5455aebdc4568c123c084f91a7f28d70a85ceee13793391ec39b2f6afdc32843ffb2bd3ccceff75385879724944a1c9abe44dece94542fdf382e074a8bb90596ad18a326d1580bc35602e18b134e70fb74a44ed3de797f2364d3701f6ee7532c8bc7cb881cec5fc3bcc7fc2236e3cd592cc9cfeb86c16bbf7fb5d380fb28e4fae1e92b101b3f00041ca07128faf3cb876daad248435a222f3a94b7148ca3ddc75db094aac0d4138128aab1886b32debdfb000e993b533adec0ada356d455bc211eebce2f914728152e6f5ceac79bf90582d3c7d46f1045fc0f6491ed6b2d8c39e1fabde585ea29e1ecc5fa5ac822a4732e47e2076e324c7a7d954236a3be3f882e1de48a63af5357aa6ed20c486774ad8e92fad4f56c63abaa8c92a11999ccdd2c913b4e100a67c7f486ed9521f1f9168aabe30d4780996202a14edf5da827c76ee4aa69fcc4ab00f1911980e595c8318adc8d68e62f3b2c5c3585c1431e2ffbffe1ed383ec685604"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e26e0aa73dea812e3b5360ce12be9845c680aa8ad1d8b0536f91b386cad2d271","proof":"32c6f9e62dbe0a5e393279c3de0c2682202a88d43d14c7ce7d47320bd5e9c60f562b7c652d6d54b0f650446924b6d92f79aeedb74070a7fb9bd620a27dfc787ac2745acc788cca85dde55d45ce62be6d75d5e7a8b3070b802966c7f889ac2213e6d9fca90cc43af3eef646b571098464e047425ef16592fd4264830e34834944dd147d01dc2b5acdcf20918fa9ee32a6ab9b2b7f40f6bc1549970e999a0148022b6b5f285d34f8f36eba8897ca99cf492529b9e3579ffd2c28860ddd1882fc0c2e12e6255df07c1f1fd2f065a16fd2c1ee76135dc166b44bcb20bad83768990b527b313a39e475cb134934e3ead964a75b4f30b04fcdcf5cba4968c11584c17984f933b6d37e13a03234877a3af89016d5cab644581ce1cb8b35902135e2587c30dd0557f6181bfc304ce78b6d34da0bc0cc7086c254efc8939a5cb52e09306f96738c79caed129e3e79e70164f23a074e25814f1cb518213c9b80d9a784204a5473d98903cb4a6b8d9a8e4ac0e879ff20422d8d3da8a86a00468c4e4cf5877fdcc92566739a0e6c525c204f3f0fb5045d051bb534e1206c8c7eb6dc3526100e766d074e661ddd8d7d96963e7dc15ef7fada06923027b8fd080ba1f0dbafc02048584485edda15f7449f6eb1f685e5471980f27acc74a3eeaa253163f5341572d6f1cf9719edafea000e93a95ff4124a3e1bd60b17aa508b1772189d77d0d872763b3b2cf2a081f3bc14ae2a009951bedeadddaf1b589198ea08e045823e6219fc90fed977dac18c85f3846c71680a7bfde7a0c648aa1d24feea2da59dec90632642698b9762d0c77b814f64835f3ea4389f749c906ceac8a9035059736f8f2c47cd40061387c7c0a80de580ac33278d83a2b5eaae53a113c26dc9196b0ee009a9fbd7ab62bb12061baad1ec52eb3ac9e0c8f3475852dfe6c4e54b045220550f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cce1b8eb2cc2f44551134bce276efe4579caa47a62e9433b54d470bc09b8dd4d","proof":"4abb31b3ee471b037e2048b9e79a404709476adbb05f7c9182001b8833814934e2a3bcb019292bc36d8d5f46935fe40f4f3cd9202885411cb3aa608b13521373d4be93ec0aad002fe39888ad9fce8dd3fb610a9d2b55c188f8525ad8b67abe5eeef3384554465f5431422d11ae392d96e5f30737fb98cf3f1077fac7ac10af5a19f748f55744dfc3731aa2004f2234c7563521ccbb02f65b9ed23bd020196f0ee9557d76b4cd10754c87fc2a85da3861be11d2717591f5a937318263ceea1f0c2127b2029d17a7f2268137bf328711d2effc8d4d6abde50df0e3cc88e3ceea02ec35dab46aaff73c12ca753dd12fec876c4dd988db30bbe4d077f9f2544b5a33040351c49007c198cc7b8ba7c8531e0fe3b25f7923fce002b8fec14fd7fdc210b69b265a24609844dc38032e2d86a617565b471ac1967f8403e25f58299883380e669f5cb1e339ba9b0b19883fd53c2cff1e025d171b8f3435377934e447086bfe645909c4a4797dd610f492a26a069569b68213af4835906ad81451d2f4650674230f9f524f63f84dd63299049b78497c95bffe6e22091208dd5861ffd36f1484216a187d3c60a9ce3e11d2a9d54e3dd8149722b0e17e7623d0a7ff8795b41d846012e4750c9fff88bf966096fd685319580ea83b49078ff802bdd80d9c290e5ce22a7fa08ed55e6b923cb49c5df41a43ffdca3ce7b7e0c1481a5a333a1ba07f21630c0e273284530ea13e38d685b26e83cef2ce378c3cd6e023ea78ae63b27345c52244f03331de8998c1fa6e637dd11a674eaf018b01059e8cdc5a350f022b0f3e24a2673880ca3d8a67b9fa698d84e7443b411f2582947711312f45132745aee1346ac9e4caf064896135914e37e353d54d64cb3f7b967f52937cee4c60bf6d2efa41c413db5b73c9d471ce584e6d67e197216833e2164f3201e4c2f2704"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a8f5de55b81933a3d2ce74847d2f7ae8c68c768f22390485360b12230886d13c","proof":"a0db226a759fd1ad7501959c5c55439695273bbf228f2028198cb768e3c6f04f6e098f9b6fa83a6a40215a1057a950bde4d0fc2fc5af84a32aa70420dc541a0dd0595dcefafdc206b3dac1673aad09a9e1228dca224873a932862b933c89d362b256aa2f249bca0e4e19c6de9131b083a4ce42ec9f641e0c7cfe3af1cd1436317a7cf36575b936884c57ef6327868ee6127e25940a76b19bf235e95d64f4a20c257b07b5a954fe9ba9238b30ec9be0fd52a9d09bd7c7ea2b0fe83d4b10bea50af6127770d3c6fd355d2f0fd8baff5ae5a41d3a4c9fc6d5b215b5490f3e43940aa42b3e5acaa105aa3c04b6c41895ae11431f0cc91b8801088ac373e01b060129165be4919913d1ffe1ae281d7ad0aca8fe21cbedae7723eee979e9b91278054c76b454c0bf0bc7e46c511a1861e78c4fca8dc7dc518c87d3d2efadd01a59fd55900fe879a6c61726b5b007486a8d196b109cdf813e9c0807676abec386a54b05f29be0984d0b7425822482f3b4dc5d81f96d399bd379dd048105775bbd5d5b7014e34e23d60d46660efb3f1925c4afc27e6e337a33df4a869b161c0cb5c9ed6074e8f8f23da89d7e9ea232ab816dcaa0c83e140d31d74a6982cf4dbf93e0e9333cced1ecee1e9f15858e244f5c36d6fd5c2ceafe0a41d0ae4c8cafde52f494326c4837dcfe06afd9d46f561b92e460a74a7a6154f7e0222bb9b5ce317ec6f7491a4585619bfaaa62ab76a4a5e2c54b5039bab0be3275d9cbfeeb0501f1ab612536ac63d91472764f5ec86c34459181962da56e7c541c66160d7964d2ce57f97c561861aedbfd0f3eaf9db5195b079fca7f447131ccab006b8f4a0026704a6205bbb09819b6b9849454137e8037325d9b481450db34ffd55b252db17320b958026d1da6691f87688c1afe76620c0cd086cc4b8d4b61f0e6096f12ea72f0a4fa09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9cfb4bf2cd617d58ca5c045ffd754083579056ffe3c5892cb57c1cd1e3aa1c4f","proof":"64a7eab8d8e79cead37e11e7327055090075e30f290e885469c05a7393b8ce782445367daeb894d3aecf4832d38648b3e87ea23d8adde167b25eaf2b041d6820d2071f1831e5aded63d859222441ae1a3f26b83c1711a90ffdce7b1ca86a9f1f586791e8538958bdf32726871f3d95691bc6c9539fe83681c0d122c261bff97b59347b872b136957034903f2cfe0f1f5f6314f126dcb26ab3c2c9d6084a8940cc34f48014834d63fefcfdcf9a37764b1e21e84b87af272438ad1e16476a24704f0ab166d0fb31850cdcdb850c054c82287ffbf3cfee0d5344dfb98c88778ad066e8fd140df29a8b2bbba5fc0a2876f6456fc3e3d9e57acc514f0da351272aa003c15c3257d402d996a4671996eeb8580ac36ba64c1af6085b1bdb63f9df0f470065512800677c8cf07bdedf2f4ef31d0e30eb71376b5123eb490eab7a96b66374a19f63f927457af70b9cb9a9257bb29174126d2431e14ab0508756686355c177ad7f5083d2781a43b801df6be09561dbc969621542794aaff0c8893372d7610ec8406bddbfd89fb541df5c2d8ac82321690f822058419e172615e23fe8c0f2f1657fa519eb6967dfda9679b60a6e8f024e052ace2639c744319316661c63408e8cc0850e12c7da7c2e8cd69f9ee4de8aebb7f61f80050ff0f196da202e1f215729f25b0a88fa799743ca10d2182f856a9f0b8279bd6bd07b385c49b62e96c18361b806d7b382c45f209370bbb59543658938d8155cf4be02c30427dbab36f68d4d3e983ba082ced16d10487b072357ca28fc20dd9ea4a2762a6d59b6e50b92f626ab966bb1a21acdbede0ddd9c91acb23037404b97d296692401067aef0aa08a31309cc6b1203c8bd823a3ceb3495b0faab09c099649f4e2b957e09c86f8d07fcfe9c56c0b7659c6bc24a68648093ecddba495b89d5b567e67b585458f9a103"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6e1f81ecc3818e934d87e6981cb7ef8c33a69002592bb1d3b12375fee115ea0a","proof":"8024f3730293e308458d29b974b6000728b9241438bee8f23f44774440128c0f4e57a54423f859bcb68c5e8c1f2e860cf24f5bcb1d9fe8e5588eef9fa96cd073ece66e798075dec1c98d273e4cf5832f7a32a393990858a6278ce47d17121e0d94c853d960cdbb9b7179c2642137fd9da46f1fe7afe45dfaa5b463415657fe4d99ae14dca5fdd4d580e2e5e5c7cdb1fc3961dd02dfa42df31e36ebe03532d90cae81a136886e5100d67693db4690c3e00538a2b35aa230e434760e39d194740482ad5f62a2f1d784eb71948b4bf4c330ca9e146c11f8c5d0a99e3c163e83c001ca444456abd07e69c161e8254b9d744c8cda8fe24e18f96fd417d5bcda50c432b02ccec03a7da35f505ad1f3ded7b0427575c9937fa57e76b8b911e0028dc52b68f090b97b6e0f22afc1783f6fe56c12d3c7f1b5b8a108d5eaec2e3cc0c2610ca4e2e488a8f2ce0b1b4cdd823bff72d700119ce19bd3cb962ab067cfe7c02770fad88ab59826ad94d8a9bc30226572ef012e4fc40a24a83e08d887a00063733730921d3e2623514868c9a82b78adc4ae4b742eb30c394a37a6ab8b2875da3c2b9cc165fa6c5887ebaaae4982a7168876b8a2ca89d9570abfcadf8ca9a675c267d6a5d643251fd9da052b0db9024b096f98806df6e2ec36e83640e83a8bdf7d3b7ec70b94c4441de487788a2396ca9448e796e541777e9d2b8cbe76fa71571e13a034678da8080771d0bef7001fd38f074146e5852c82f08c81370ad86306aa3536ab914534de764b372486ee6f1fe76337ba5f69efb119c62e0ca8edcd1e6b7d1853b1b51d6944720e9582325bae108497f021c6dc195acde758160e1010841be18d7bcdf91583846323877a4b5142cb1b150caa1f2bb55f56251cfec625a40297bdbd7fc80279467afbe31f3acf3ffa6162df6ded1da7651fe0676cd421c50a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e0bd4fcd55b08cda99ae19465900154d08656bf68ac00b00408d66d84a19dc4a","proof":"101519b1da3733b95239d97b188db6082c2444f65393a2fe6c7dfcca2e4ba20b4ed5bb6dcd5af270c92fa624a7180d85c1cc5e1e810ab86cb3bdcc81b7b53e0acc9ad4a7b96a25123bf24d67e361c65baacec38a5c89f8cbf6e9dcbf3fdcf045f21bb93177955f9ccc46f8652d30d4ba9b9ddf4d0eff9a122bf7436a61728c3cba88c91d411d39431c78a1417ed2ce8cbb3319c2ac286b9b6f3fb4d52b600b049cb8565b6f280df2eb5af49d0b0f2cc5cb8b442026b7fedeb5e3b6d2a678ba04ee7908d99e4ad9b2e27a2cb36dc9f4afdaa09075c59009c9c187d11d2a11350a24f0143879b79bf774304c690562a1972351dbbe06b3d9bedea150f40cadc130ae818b4b37cc654de5ff8e1b7acf23c4cb3ad68f3476867b67bb7cd2e1a82342d60a03679d7aa8d0cfa22380a92b842dc999fd62b0b4aae367b52f2cbecf96487c38d74b6d9458f8fafcd36bea05d25a67fa179279a3cb726c3b59324b507415d2da820e611a6e763c2562f33e40f29b8490c2fafe4e5e952679f7b3e8407b5ed4da3cf8c0538cf6a6dc95e3da1e9d6c5a5e786effb60803ae76c816e75b79025062fb9051dc311a239161a66860ea52c02fcc5835782eac0bfc6bdee1d4b92e906756c2f334e1adcae185e1be4c3d23b14c75211cff678b210093b112d28a196464345efcbac9805a8043f14d2191e32c9a5c218e33692b0e2df9526761eb48d8c3740b8fad19711218074ef9db76673296bcefc5a9cdcec3ff4af744260a6cd20cc2b8074f58ac71bd3bd08a96667cfa8942088921747f653d1f9b93e860182c062fbaf09a5266e12f311bf85ac8794d33ee4d3959937e58a72ac2bfcac415a2ab3fd38901297e49e113d435e19b4914c78149712913b4477d90df9c3a0409f6835e6bbadc3dc11e5442242f3f5b7726d2e20ac9070bd55383581f865f5d02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b4064a13fe742a70b2ad3e97f849848c7f4a466d812283f5e41ea5d899c90317","proof":"4a1590e2ecca54f73d9ae412752fdae33f0319a3b2310055a5e6d4fa530a2f0a543f1e0addb03667e0e3af442a3784a55323fbfe79adc4f109e860a89167d568925049b80874bfd727671ab295f226560de705cebce87f48b8e86bafc2eac549a4a3f1d1aaaefa9c1d39987c733ce61f5b01b9b351d9247e8c1e6c0338acc100b0b5dd70a7f9d3fe4db23550e0f5de2f251bfa0f583a7bc94eb45310d63c770d38ae3d069a2a17fdfa4c89efa3ee81330062ddee679756c439dcf99f51434602c936609f4c98dc08dfa5286e5bacd6b2c8680373c8f09c72a46218f98eacc900642a8641edcb74c88d11e1cbe0a8699592b33e467aff983f2b55f85ad04be078889d8c9ddf052c02e53ba1b2fe736c8848b6cb8eaef984cd67dfc242881f5340448a36cb089e909bba5301cdd3744866466320327cc946e366d3057370b9641abc80431b9a9f1bcec88d2e8fc0a723099aee12297b3ca0430ba6cc7575071960a821795d500c47ebfd38fb1f5e77a30e603a12d070e1de8bb0ec6fb9143b1805a4458aa844d3ca97a933ff089c334546dbf3a9fb3b42bd46c6304d73deced8264267cd7b17853befd2f121d90c57d3442e718cd2ae48effc65d135c6115eb52f14ddc753f1f0ded036154e67c350c89cdd36ede825718fc98a5fe1b0ce53534864f232f62a6f79aaba124353214ac8cea9b087508134f8fe9136a6ba6c25266696889f3faf99c3cab4bcf5895910e8624222c34f5f6a190a73a50759f00f386f9e0a46c27d48fee165a41f2e3725a1211b6d173fd6429ee4d673ce54e363f21d62ad3bad09d3838d5829152c495d0fd3c9156dc6d3e449ee8eb67a7a5cb2cd393fcd9d05954744e83b1a9733a3f5758c9c3f79c273c0a70f6b6c5715c3e76e07c5bc9c8dd30436538b62053f562c9986d5a8b30d66fb2a4a59b6e1202efd390c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"201488de06c351b58994ec9a518d305737396f509dc4f6d3adec76758e85401a","proof":"86bd65e234710b370284577f6a4c762e1137a7ebfdf06de0c1506586933c0e463636c5fedb068ecaa0db34e48407ef76dae50bbafaaa723e9eabcdafedf50b2b583aba389985136b2cad691254dd8ad7c152ba382b88dc7985f65091abb8071478f12b36143282b4ad4a5b95a2940001fcff549a4b487e95251040769f4afb7915c820cd5c363dc60316b0ab34c2ba5381c96a64a847d30b319870016612b007e2bf74907de1fd8b1493e6498db79cbc6e1f618e254ec9df57469490a6ae6c0ed2787653582edb757788b36461d1b65dea3ba9740c4bfcd76fc6cb7edc222f0bc2e4176a189a67b123fe1be2b9290d0216399f0902f4665d9d4a0a8b4eeff43d903ac40cdfe2876ba787b0f961e37d0d1ef75b8c85b4b5d318fc0acfa858864faab87e5ac658139a641123a8fcfb792ec913fc5206e817dea7c7e464dd167a5c0eb8719adaa82e9393ea5a22ad55538d3d3300ca09260bbeac9be3afe42353347c7f8386a94067f1f156b1e7d8e3541f2316ea0a89956e4ec113088fe011664ff0de8f4ee66f61104d84921d56c0f0a269c145aede7a65c7e32af78ddbc6315a96143798b320911a3bb484020fb353891f133ab448b6193a881c71d14296a32f3472210cc3f00fa9c1891042ab78869417a3d82ec9da4e25016239428d52db145451573c59a0db5ed3a2e33cfc344f086b27f9f2e7a0854c8659a869092a7431447506aa90ba3f39fec39422d98aa1fbf7cc6783026effea26600aa8c06b837f5a9de93a9256c672528f497838af43e65a5e17a60944dc7b46ea525047a86314d8107021041f2c58b32b8417e482b3071f1d7429b3da29ba8c02171cdf5d6436360a058f6787bb8d17176320c09b8eb0964a32b4d5bd86acb94a43ea33b5a00dd7ca147e9f80cb0bdc8ec5211b7a5c17faeb62cf876b5c7d4e02b23ab09c530e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d8f6f051877f494a7637b3d6b0003cfcc5b7cc116fc3b91667e8a2b5d4af8a29","proof":"94620323c679680519e228b383af4b8bbe5fba147acc6366970f50989f62646e28c4e0d62759ac0a46e968d11e19d18a7eb70c16ba6446a1bdf69da0ea4fbb1e10c4e05a265d411b6b5e84985a0fab3aaffcc0fa09fac65fdc8c6329857091335a5788c3b69d49f158ee72a2acf4e836c894077053d1cceabde0bae323d46a319a31ecabcf2089ac6a164409a15c2b7bd78ad07e20800ecbc85d03e00d2620091e8ce64430eac01a00810fb8cc1f8bdbdaf98c387782d8fc5ec0136ac1065703e5b9b8b4e6bd7c2acc15041dc50d78fce05301ad89392752265476969bd96304fe47213f2f041e7c8e8cd4738d3f2858b824551b308183c7f38625d0a81aba4baa43f173a888815d605939ea2c9a9daaeb8f97bd4ebe13ea866cd295238d4740141ef411ad0f6852e723bf0cddadbe788f35c83001df1f3a6a885ffffa76487e80e0963740a20764f1e721f0754407b53eac34e4ce9f0ae64d421353a9bab93610f26a199aad18911691832b3462c097bbee67cbd9edff17dee097ba9423f025d693337872573d2075f95a62a5bacb7103286f276231da830249b696359bff33fad675df71953afe099a06c822d0027bbbf95aa19633e89ba058fce4beaa851670b2ee2dede40884d37c0d32de07308ddf713c53f65ba87e3b7deb685d60aa06907a6f1782845b48f6a5fc7ab2c7d42b8911ac75b9d0f3ac3f54c8becef43f3d0e341d5b4038044e22b4954d89a7312eb8ee4ea9196edad037f5b523781f5c4682cdad11acd347baf7d03c615693930f64348043b79e9ee8c65940f27d5c12704839166e8b528c78c4096608e8b94c0839253391de949f3b49bbc0a7a1a67c6bdf6c80f7118bbd3bebfd70497871a19dfe3dc17b065c8f0bb5346fcd12e2ce0152fb876c6d86d2a39983d96c56c02c21bd03072aecfb7519e7d4b69957fb8509"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c81bd91dd597e44a8276530dd81abb9236a053a319f5475ec35e9d642d65f617","proof":"c46d14337398546aae1992d8d82869d9eb6001416223d8b551c98c05b247002b46a560989eedb13ee834e099aae934cc44318b57cfad5ece27bca5893bfb6234ca2de8732311f348036ea29c5e41a9576d46014dd83793aa5a15048d5b08e76c22f61a2bff58a66ac89700a189179ed30527c5bf25bbc48542871827d04b962fcd4558e3eafab7e1342ab83c9c4f2b98d73380a065de3aaed41005128c4a530c7d69b8d122c591da566fab046e1a6f8af7d31007822d735a755f6d08e1aa4107e291541bf8d48885ac5cbfef15005479a432a1c855418f953ac75c261a76b60ef0a455bfa79c0793e2ec81ce260bd0f7060c59c0fd0b1baabd2813bdceb1414090fcc4a9b5473393aebc7f7eb3fc1f8d94b61fb81a7aebe0d313cfc25428901c94f86142255033070fb862a9e93a39f1dfc02ffefd50e8aca24995ebc822d67224ffa3630260c80382a94287e4f4006e89748fc0cf894a818ca661dfe66f9211aae3dff6a74bb61444892e0a5922e4c0bd7e3be19ccb737aad8820d2711c2d49246b959c13816fd7fa711329385d531e7128d53bd22954d06789f9d27aad1878d4d4213876a996d9cf913d0dd17c3bffcaa97073d135f3e876faa540a0ef566bba1d7e5b487add63e76139d4ff47cb9be5d6a2a2e6b536b114132f96e2679418aecb7e3a199c381e6dafac24120b8140e80b54f822abe4a85e26169cfe1cfb5ceeb94f333f80f1b3fc4a086c1fe454a7e27bcce1768da219ea70e18b6e81f23b76e197347c977ec01afdb3e1da738fce58370041d7687bed37c82e99656279414089a19d3c6d0c977dce32e73ad1b01dd44458e4cea323a9a9113bc54a6b6b624cf769b5874585826f55edc66a546bd27cb50f07b41dcd0694ee05ae8133220a64b6a872ba855932db753362ff8aa163aa6ec808eea389bd2ee98393d11ea504"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"eae4a6370d702f0988a95685bdb75bbdcea0492f44e3a34796be5dffff306664","proof":"68e7c3b5a47fa05aa14a0e70118f519127155c77c18476b688553321f37160227a737a0eacaa3322f1700f404fffd11e6c24f8add0382da5caee18716f0761474e39423acffda397336d6f3bdfda027064100ef03e18125dd083f3fb0d3e127cc07e18fae8273e03c961a4e64c10b5276d3ae7dbc4edf65848f0ff3efb5ef35d7cd0e568abb54ab17822064c5e60d64141148af0c518a2552173b95e3247c30499132bd0385da6d431d5a1861cc1b4e5ce7deda9c1b2df53b02699757e1b590e7ddf65697265a9dc95c48a80c4ffc9c4a4e258626ffbf9bec2f4dbc8be03670c16494cec00ec9a1079a5a945215ceb9199e1677f1760e1e0681f4b5ab4955e6a1a612be5a71261492e2426c626397f2451896afb3c8b2b01dc69e5b2ae00ad57a61e34d5e33e67413e2b02ed8bd4599d184292a71c047e01dcade80d706f8d5a5a6ea725095a1b8e81f772c6ef6a22ec7b59d57212cb98dc70da4adb69b1232b2ec11a89aad7e71c790747f58c67c78db250715cb6830d6993dd55c7083d28656050fdf31d0f487ad5869a62a56da2f1692a32dbb25c661aee53186141037307b4345273f394c53453459cbb2457c9fdb1460f95a35052b8b7599f587ecd0d1d8c5f802ceb2a333cd45cbae71ad82549ec30a1d554558532ac218e307c3b8631fe749287d90750c0716b860afccedc6735059a70f7dfc6b98401cd1351fa463658cdfd40f870cb686cd5ea2ab3727a55cc6e4b02e7b0daf674786ccabaca2318280f90ef505376d15566e78e9bd8bd6888bdc57c7db657cff2e50fd78fc5aa78503411d7c544c114e068d92326fe811784f9fa230eccfc0a9ccd19c1655eaf2dc9e652ca5aa8259a60729357a67cca9079e81c285a0c5493fe565b5f71771e0d5f71313736add7dffb4bfc503c9223e60e8b5f22d765ab2959647bf87a24fb02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f6b7307a17c9494135efedbc4103ba5580e16a481aebb3b078bb8e407b17547e","proof":"0eb8eccabc44a3a64d221a2a88280cb48570b4a844d2a72b5458f97233f38171e8b1fa1ce140c0ee53e50c105b7ad2ac5ed688dc4eeef40cb477f0c22bf88248d8a240a4f639301cc12eace36b4b8253637ef6b0d31134baaf2b53fe0073943bfce1bd4bf50ecc1eead3f67f40d8abd5eecc4bfe8908b5d19dda2c09fe775a459cef9f954c9fbf081a788a15cfbeb07971c0295865b7e153797640db1f628d093a7c1bb15d3c67380d68031c4d58910fa947e9860aa3301635eb19bb3910a90198762537436390ebe141a6a85707df36a9caace9adb8681350a3c506d413de03fec8c354a01cc355712e5933be8383f3abe62e04390f0277a521064d07e8630f9400ac1fe93218bd1301f8270f477566cd58a55894f7cb889bee4b88eba2642614669eb37a0f6fecac827cefb2a25f784e03a5c6aaeaf8678e9235215f82cd26c81938bc415e336029e580d9e3b9865c36a10cebffdd33cd8801828d8f2bc5166877ee2ce6e4079dd01d54b3260aa23568349d9b2073e0bf02fefe893f5ee27b76d9c87a03bb37568ab707fe4458de2da6b478c475dfa3dea042b1bee594183b8c96943e4d9bf5b9602ec6fff02c8f5138b1ba2458a14d558278aa0f7b6406267aadeec705a34dca90c4bd4ff09f307f42f58277bcaeb39978193d40461b1a75e6a9fd07852a4be8742f513083bdb7791ab3bc21dacebb134c494e149cd1b60c5632654632f7fdb99acedb0acd282ffc970e186c4f403dd43b9a95ea9128180434f03d464b0a6203fb762970bed5fd5d4bf9861eb5684e2cc4924361beb9ab4e3616a18ddeac86f93d0890c402c4dfcdb7dc858bc1f824edf22bc733957d871f06d0647155ee25cfe3e8a5e07e9008060746f3280a84eef5f7d8a7420ec409004cdf2cb5e54acdfe58839a4b865766809781b780100a9d07279aeeaa8f528102"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"38614591b86820f7a3075c1d3db7cc351f554226f7ad17053959f853843c8d18","proof":"b6b7b0beb4b4934c1eaf5ebf38819e5cff595acc3bb6791e33e4f32ba22c8625841b8f28365ede73eb4da69ab4798ac219289e919967870f7cc51e8be347464734c02c49defa8939851d882796cbfce6b5042857ef79bc1b2d83b089de9a3973382cbefbf7acc1a8c7ccc4479fa0aebeb7aad8f3e751aa435204841502aa0600bb0ac2d38a26c9bd45bd2198eb42d4b605cd4e3c957209645c2df3c21d3a7005396ee84f507e967a65d29de48784e25ec2c407b75b998b442af8d81c1f48e80b27228b653dd5d0ad7b852b0bb9f8a18086e3ebecd7f5fb6c08f6c580be00fc07fcb78c37cc96eb8c888e6b599085cc186bfde39fd7256d6c4e4535df1fdeb82742817a73e4ac55c8e1fb17581de86f5a85098d1989ad9242dcc1d3d144b5ee7762b53c6e3b8741c2ea39a4351746b2123c25a688bac71aa42928e009b0a2dd27aaf7cec758fd6311a210a7680a3a0e97ccc4c2bce22da008a4e313adc184b70b760f451b53efc2da77f9fa8a98d2f42cb76aec12dea277ad80ffcef07f80112096344098dcb71d251f703c7f2687c99c9d53f32f08b753559f785c7850df5b2c6a6748286db46f54de35bdfc09d806547223e155cb85e95eb515c72d10e6294f90dc093579cd61c8fe1d59e89ee82fdb7d8c16d70cf71bf39678132813407d6c62e83440228a5185203a888b5f3108709e6b44a6b1d1d8a3c7bb3cd4f3b07e09ac1ce23c54d7e0485e0ca16bbc1d297afe5c0504f564b83f44d9c51dac0f3534d264f7046f06b768f02256a8ff230b5faba9bceae0434a7a1b9f2ed8fe17a97ce6d51c60cac46cdbf5b559ee3a4311ce1d45c28a2d9eb555bdc45379f22aab008f39efa32f88d78edc1a182cf097ea1d2ef0a16b5ffdce9b529c4b888d61dd0a4a13a28d336e8c1925f0e306f9ee0503d2cafb338d6e0d535cda3316ae734c01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4eab67925f1e944fad3ba89e112606bdc0616923cfc349350ae3e167e016fb4d","proof":"2c5d63cf97c4f06af9242e2ee4366f77f255141091bc226dc89a04f17fd02c0c5679c9b31769647b424c46c5b0b2d034d438d8a1c15a01c4550a67e93579d02dac937c9ba488e07d75e0c7f702e76e9508fb07af09248a9f0dee262ff856e33414de395dd2ec0642347b45b83ce4fdde8c0c38cd89db145b0adea444094fe32f6fb8fd97f60d464700cd37c07b28d7783099de4987a08c0118c7298321579904ded5f09e89bb7a7068a2ef4f81e50c03237c8baa16ee8469cc54cf6d81b8ae0f4b0a172ec909af0040c3804cb62603ccc1560457e2b0c45574f4fa1af7530305e62bbadc50eda85202090d8a790dd920b5f7108464be7ed55ec92a137563c029400f8c5a11543d8a2e889916483ae95323721f355a2869ba19b7c23042d03655d0eb72a55b8ef18755544f59d516be7a8c02b6adf9249921d0f32a59cbb25049ce4c52b161ca39d1700f0b39c54c6dce6cd5ba1413955ae8a73258ea0e6f24161a113bbfa8d32aa9f6579113633f82fd688d17bf130768ff7d9594932065cf7f90a5d9b76f1eaf2ff3080dfb9d09a440fbdd329a83067f635bc4d3c66e763c379095a5b3222d6ed764f4eae273f97f86b13c70aa254409ec4e5cffdf17a8d80320721a5263ac23d6b9a310b8620905bddecbff9ec3ffcfedb634ecf98a269b1324246d21e9dc38905ad4c99b1e88770d924f21f26c3c77df1e4cb3cefbd78b2190f6777318d3d0a21ae3ddd245f99cbe212b5738ae85e0b2432394166fe93337e20ec89c4a18b12068be3a19d5a48f3dc0c88ef3d4ec1a6799945496a703d536bc34dacac7891fb420149753128e7bd23dd16eafe7aaf9ea11dc65482de4a314e3430a3562a118c2b388cad20b8add675bc5dd31e692697f048389246f329d0947573cb1e6f7390fe5628c654b217133c0a6f24b0cc54a68917d67fa64a52308"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2c1b012478d697471e0222f1e5016d0a2341af5afcb4042ec9e9074382306a18","proof":"42bd6b88199ca00ca8f8d3dc41a12f72e271cf63a8dbd5808c8caafd8063d735f01caf8c142fd377f2b6d31917dcac81732838b72c0ff9fa4062a179eff3c01a34780555de3b0e9637cc3f920b27107bd2b77aa8f4a1fdd0367a84eab006447da6ac848d67e3617fcc4f17a44ed3f057317054479d51f16a8ad97636aaa7447bf02435a0544fe4bb0eaa2db1eeb9b8e6b60ca9e50841f3e20ba5638885330a0634a33223ec0b4ce338904e9795a508196c1472a6db69ec7facd653b9816f3e0bbfd56cf1bf7af8d0d5dc3e31ce92f86abdefcbac29b2815f3d5d790f67013c051ae14d0cf7cf8e18f47be53d11f069595acd7121ab04b66165746abbfff6c87e1ef1da73dcdadcf5663534dc99ee39eed1edf67f1b80aaf139637b228f93755822a096542436cfc7b899575280065e2cc89b56b8201785305450a7b64b6f1f7196e5c8c42368c58b64e13648e9a970ae4d5a9b24ece4e024765494bea99bd6035aa88c96b8398e138fd95740e0620072ccea2ecffaabc08cebfbca82aafc02557e074a5b2a5b2b51dfe284e7d3fb4f639a2da8bf72b7a31fd3003e3ad0218948461ccd1d8637a436e12d2ab0f71256b2e934a45034dae32cfbfbdad01e8fe73b0c193e7baa6e821362049dcfdc649d20fb59615466b6829ec0ee4699c0e78a25e655a0893a051dbcb3fb9d139cedefa8962541883aab77c1600b49a093e46c02e61bb87d6836a493550e862078f141774acd59172ebf115e3f95e7a8887a536caafbe290b726d5b04f2922222c5a289dba8e7b7749d91f6a307dec7f5222ab2e366bc0262a5bf90b97e23eb1a46489dfb93c53c927b3a8150166c3422b716f34d7669d5e04750ad7cf426dcf3003a9d61e48cb44b47937018f354d99d0f2e905e08deddb30a170d15db8fee9557d60ef2d42ea9f2af6d3facb1550d13e4aea0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"803a5c2b6744aa1ffa591aae8c56c7e59fcb57e57f286201f38000dc0c33841c","proof":"fc98f32551107b7ad0a0c1f22fb939deb94dc83c6e68ccf55ea3cacf0797b06d90d44b073940041ded250e5a7ee283bb36bf2a2ec08556e90878610c3cbbcb0aae5d8af1e82ac1a524dd5861ec11b9c3a335638ffb7bcdebabd7936021ba96744404e245d7b446881feb3f332a3aa0406362169488a70eb4ab0bb65fd1b31a0482b22e7a40ab3664f21de6593c9f6927c30314baf27f2b41caaa0090ed25fa01a0836be7baba318cba6dd2dacb1612d42ecc9b8ec46cfffc9c8e10a733f4c60ff159fd162371ab2d875f04a9cffab252da50940fbea20ac381c4da4334982c0442976d6be6ef4fbf052ddb67b2b35b428505af1e5958306a75d142bcc34c4666d659b39ec6c0dd0424bf1225c1d40b94f0d874795b3e67c4b81df66280c324432493bd1ee497725c016ebc9889772fdaa5d3c52fe63f520d79f5753d6d7fa6694c6ddef086113b50bd80ebb4a34c3c3272965cd3253f57dc5ac8d283b2c7ae64f8b4a6cbc7320420056b16c2a0e6b67ad64d3843479831fa5e2798d98b651a2fdc6aa7fbede57da09bc4fdfe3a0971e51e91da1f199dbf2d281cf7c0ea51a71afed86e683406a96cfc7d68b461f5e02d37c427ecad3073d0f951002340bc7a09584617f54bbf204626579484da5e68fde57de426e32c0931f0021167cbfb5939eaf8685288b61a864b47977d046d081ea933014a14bfac2504bbe638c61abf1adaf3f0c97c6989d86935070137d030bf8196b6530f5c7c26c043d6327973bf3884ce94997f2c0b8a6f57e8fe2895149cd2599c24db791fb4a109159878ea2654ecf5f8ab5c738ae37388a6febe1c77c8811716a913c621baf49d2eb0bd4714382ce04104454d898964068d57d46bba079d005b2c0eae6a3c9d82231a24c62a0d55e89e27d6909930be34ce5c086660609ff5527c982d14c2d8b7cbc766de4d08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ccd969432c8a658cc60d452507f33018c753414d204732e0f2e7c3fe27c5793d","proof":"6895e4929ba41ed74562460b9565ec52df9b9a161affaf23810c948e7af21046fafcd98a1751d0acd541a9600440e88a0c32f583eb411872ba3a88e65419dd116ec0a34382435cb11bc6e75b7501d5586d54c4be7d4b0910f540f17a1de94f0054d8f6c257fce5bbe9bac481959833b8da9b8735573c36931739f0f48ca68d4fcb5d197a8ed4d5672275afdc64b48951ee783b7e734f9a7254f85ee8739a390ffadd05b89669e24ee0b8384b9376e3a38edd19c54c4f72cd05f5e21534d17e0bc53570fb73a034b27dd9b83af01363d9de926554fcc1cad6bc93865a40543606b6d82abb776df86edee572b503d16358b1c5d4c96d13159c150de3c418835a7dfe11299d94d031e93711e9149efed9f03fbc6647cd94b63aa79e8789aea23e2f44aea27583a5b42288a2c7b3a7f828cc3ae18f1c3d3daad53c7a27d0a1cd856b145cb50f30a3d4bd313f778b35d95ac9c3ad2d090b2a47efa1a44094894898279abe58089bad1953f76dcf9ad0c32745722bb64e3c2476d766ffb867d6137934aa5f0d74a31dd5fac446491a6f42ab6f776f4099e05947f5d22afc1dbb8fbf0d46299e9388e10a4fbc6aa7b3d7370dd305a0abe1ff859e88d791594345ee7104c80a993af4fcc66e3c737ca7a92f6e862ea98c431e4410d6b874211348d40b6722a6a0565901019ec31f6a7fc62ea2ca864d12eece99b50932a9a68e54e4220c9c5d969e5b815e492fcbf7cb3216110f1d75ce7d59aead6afa95807579ebc832786d2340744c418cd131127385266652cdd26548e87500b1bdfeb2c056d24c2178dfed5be5f25de9894b8db99a98d7e264d84e93e0cbd7ae2883f349a42a380a69c4212712c772b7832257a548094b2b65cc573c2617e0a744831e70773c46030628de34d4775d8a65ee469ba5b3c4094d58c1ac9c91c46de05b17036d06e204"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a84867cf803ba94d9bf70c7aa3cc6e722428fb88693d8f31e7d2f318b0000147","proof":"5e84b6bcc84567a8d26b0a64964190ae841121eeb318ae50ee23c9ac01d04e33ccba50135b325ae059448b3ba6900aa8a700e1ada44984e9758dfbd5cf040e2932241a074cf0619eee37810b802128846a224651a9b757295f838a782cd50c28341dd62d05c7c7a034275474d695afedb5b8e32ab68bb3788d95251f2e495e69dc844a731906264290284da769dd437083700b9da073212f11df313896671102a4a117f0d352319ad37f2190c824180d26b2e3a16d51c1fe44e8e4f2de314706641b4e38a0c0a4d5c6f899fef180f739995d063d0f93bd721876714fc919f6065a9e43d47ae1a0a16c6a2adf58bd788373f98b2d910ed97914fcd86d1cf3677f082f04caab2807cb1de4414e9b7dbe71d2a14e3854c11a4300ddbc8578bff651863ebe2f326827df1164a523c9a09e0b09ee19e62219ffab526a83b853ff0725e8cdfb1fb8fc19aef70c3cee798c78f3f74145eac7c84339f54bcbda2812da47be2aaf1347d0c5086cad563fd9280c392adaf247a93a4cee06cea3b9cddd7a008289399d6c826597230f0285368956c3ee468615d2ab5d2a10fc7883d4d89414d880e04d1f508c092c3a9162b03546104c54a69c696be90995db101b3f03a012befa592c94e35d9e446d4b08d99e08cb636f34682de0f6264b1797e34b6ddb7b94b38b95ba6e55ee09ba69ac6a574388a19aa153dfb3008e7d8d7ed7297c2200ec8d5c60d2a6de9b6c4baf8be1f15e2aa0647dd9c158be94868f7215da506f7eb8039896d3cad2b3ca6a438243544cd8d8db583ed4f3bc86f3b70649e2e6ca05fadaa67a28fe5fc7384bd257f5fffd1621a6b667ee8c96fcf7885d409bc33a088c4246483044ec674b56fa835dd30569af05c08781db80706384bf2b079785042aa7691f8c258989cd1595cb8a21476f1b3fa75dd139860d088138bf15ee720f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"54bf0a2e3ace97adfc794e42f09a0a2b07e757dc420221300d1f9184d775623c","proof":"80f099d2699a19d0a70a4fd405c3980c12473e22faeb24cad80e2a74e2f78f51728aec4c1a9b4815bb5be00502636b550610681687a6b81bb9ba632f62b2c76ce63d1bf380512972c1f6e8cb561979dba284d1042af663abdc2ac8fd6695bc77d48a798839595b9c412bcfb00017f9639f7a4f698fd7c1cb4f7ddf29b6be3456b075bd2771a30020b54f9ef762e5b067da63c20502fc012ce16876f330c2a50c94697ec92ad98957f21af2e9304efa28886f0be08f9d9511e25db7119c2927085dec23a3bc4332fb5b81c66a37180a71555d62b27ec2d9431205b638ee3457037e774c49e34261c90f4a718b5bdaf6a6d0865096491e921f45b742e4e07dac68aa41356de8612101cadaa18e2b7036048359060c72cf7d01742bb94d62c77175dcb271c676a08dfe4924cc9d78b08b7ed5e4bf18fa35b7a88d690ba6dfc26c6326a092572b3e3d3ede334cdbaa7ce61b9a4359b670a251655d5f011919818e003603018b0a0d2a599ad945a1f499300be3c82b7c946c30e7abd362d43c5fce3cce51c1cdd46c89dded39f9e3703c30757ef63c4179b508fe122622dcb138ff3bd8250506937eda17f6ed6f67c17eda3d39f04b86b15c16c449dbde8d1f0697555265288ffea7c50a68c7c51d8264720a377af8f3e2792491f3f8b978957f5240227e8401aaa13f18168ecb80c2d3f576f033b1e07f7b498ce9f0c380afc81f3c8cd75f657b0cf6cc55027e234e85715b27959729683ad2e43f122bb2c878766b200137e59c39283f706366007168bf6b652ec6d99b3cf19fce790a7977642031d4b8c77cd6d6ea7c7e920930a7d6f8dbe7c85aa31620b62be31aa7c425dd777b5e5bd8ddf288950a3fdf3508691c0e8f1298628e3a94ea3de9f3cdf816b85b0ec311d54b4d71476e9ab70c557ac9e4240e825b60b8631e54c8a7441d36fc5a07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d2d0e11ff1767d2cbbe69fffb51702dc61e32d6c709fff77e8859f5ad8958569","proof":"7270354ff384bfe18aa556e8335c81c70fb5c570646efc48c0f00c95228f81342e2f6d7c35bf54d9f2a643069032d8313f2828615465c1ed0f0437266bf5c122d09f56937886712dea3632ad3179b1e00f954ac080c0d6dbed32c22741251b0a4cffa2abf502058bcbc6b4334c7a6fd5e2c3e89b619dd3a95bcaac9362d0b5444f0c109907a9f8d36b00c0d688964a35b6b1617701c9248d60fbc8172f84480b53d9d0f9bb97642f471465b49608eb8d4c24bbcfc4686cfae7e9fb2637bb170fd382b7ba96099dd9760bd2108aeedaad6fc93684bc3b64eadca6b5b97188cd000825ad3f1ec642d9b64cf0ed1b2422ae14d3b40aa4465d928076c8bd96a32b31204c042ff10ce8ee0f4149e9360acbae0a75c85918de3f70b9fe00551882557e50c76fd828ddc148d49a02ee6f8229f820b77c58415a9c48ec57306685feae2d8a45c6c319c63a1291a056514317170d5c77af63dfcc236f080e59db0cf6da0f7a3e97ac765c78a91e1937eea37d66f17e509183901ea1e64adab95521874c65106d2ef42f5ba5524e6450581b8769ea61126794beabe957e9ae53f9ef680c7b72ec83c3fc03bc44c79dd65490e526862897e797b865096e2d149da14bb89b2d24b11e37f490dc5d7e43dd85965b30399d48955b8fc1dcf8d960193c45e74e7216970e2fae9af2e2e2b7ed129e82043050a8875339f7caf75361be09b2daca7fe4d1898b5d69395caec2248e7dc712cb64e058c1300d25a35460f4187569212336a584ef38ee589ed7e02fddaa0d10e5a36f8bb40cb9ced180578b1cb574f3541600dd54d9c84d110ef83835ec010317ded59e17d34e379e34edee5727d3826a30f393adc229c91ee7808028d851eb8b46ad54c999fd8fbce6bbb20c0338ca0af95534f0f5855ac2932608b6fbe7e9fcbf59396bcc7e59bf714c1634dede6f0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5ecd264ea5def798fb2af0e43321d2613fea541731e7703add1ed5ad70046879","proof":"94f79324d7be0241fe938cfd67548c464158c3c23fde75f6141d7150517e2a765cbb40ea7dc9171999a0f83d2453733811771c3f248c315e791c181b0531eb11c489f81b81a602bcbe01193071411b0ce9e2682430f01d8432848c6465869e48e49c147a3d7f9cb53f45befe46ca7e97b9619e4a3387d9ddf748ed1bf4dd526f753165a84060af92205e32feededfa2b18d9516a596cac9067f8e0b1c6ed520a41c0db6a593da701ba3d6422207c441ba8490e6cd827eb6d8eeaf910debf5e0973dadb9daf86c6c52274b6a74bf07aa90136089471aeb47e9aca86cd34023f088ab7f572393cf63865fcf9d3c90a7910b565287717be40f57a812d5b3778d8293a025a3a003899c5b8c4fb76c06920763e76edf0af296b446444c08d6a1aac7eb68f33f2ccbb4b7c6117393ad2be3702c2861bdf353932c32050c78ccff15801b4a9c487021fa040f9e553d51161d99607fde771691249fb1c78e373c442371db231e7033864a6db9304b7cf3db74ac92fdf62f864bcd8dac9de0d45e96d263e767c3acc98b30cd42b7853816371ec33dc67e601616385a817b6cbe2ad589f41a0280357eba704ec185562c1f3f35ff089717d9a339f3ce1966d3ea5b7167a4e94508e92d2ea03ad5747c40264c66910dfe10d738addc91a555c11869b623533e86a3cc5feaacef1a9358b88f36c9054b8049b078cee129cd286b8587859f91d8ca70f929df331ca212c3b343fe8c9286636efaf432826a0248b3c4390a71c6d50906e6c426cced2f46845e062e4d813b0567d9621005a8206f12a31dcfa17789044960737318b418f9c1cac5fdfca370c458c9a0aba29ce0849f453ed2f153ccb48b4db78dcd896e228d21f014ca99837f03ec0f73f256c951f3b4a213eb103cec8ea160ad7e1e4516fe43cea3abd1d177f690d337562a90c1cc7e8cd23f90f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7e42094c2f678bfde7a4127cde58d13c8e2f2b5ba1a74ed888579cb84696c32a","proof":"10c1daed1f85f496e88a3bab9e2230f734a727e336e480a25c1b78c393852c77280f656925403d2d90569e8bb749fa195f626faa145201e1ac533a4ba95b355aa2e018b4fe936d3bc73c2bc06ffaa407b46bc1ff7449c4e172052ff052d1ae7964745fac1c5bcdfd35a7745e41f35094eb2e4f33b24e599cbe09e05c76b96e2bd1cccb02de11f4266a1ca78c0f58830b38390eb84d50b73de1934b7b8864600b640afa6661d30cad8d6eee340303655e4493536839ebe5c8912d3e464acf380a8ffa773b6c6d1c06a07aef62e574b644a6d7e1dcbaff5efacc7390f14b8516023e19bf5253ff834a61d1b26df28aadcbb7335a3aa03eabb96cdfae7ddc2e5218d4dd0fae3dcd21a7c1296c1359bda6118b7947f1e22b064205927079c749c71faa049686624aee8c22d73e33d97b72486202ae54c330151ccbd532186ee8c53e46235c7f34db5b0d495291796366207eea682927aa6ca03f18a45343904b6b77d8551abde9978e954048710182580d524997ed3bc4fcaaf7aac567e4bc39d7505cc855f798aed19270bc6651bfc90d33f2c8d8a562cc41dce1e2f0ca6ff1626e6ab5ef7790f83e1250f14621cb6a4c7fe4eaaf395ea1af9f08a26c0787dc323ed0b14ea1e9691055ea93e81c0221d98e820364fabc9deebc51e276b020fc77063a5a59d29894620949ccd8d0a21fce0eb7efdaeccff0bb7c2537ecf25f5b6d4d4c81421481721962d452fa64bd91aef43bf77ccdf752ce6c5999ad7e702fff24cc46ae34b374ccf68e015b9abcde2885c87f6485aa739b902d0488c02381195f5871409658c1deb83f0c810ab0ab7e6714183d7fdc8131b2d0a2f2475d4f44739ea646e88f86a1bb4e49725e7257a50057985b1e6976981c7c48bfbffe73110e591b0c34cbb04ebabb95cf4f5c959ce8829911687a352cfbbc49c1b7165acf05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"feb66b34c02f408951b7ac59ca9b4c921b7fbd79df71c5afa5cc429ba9fbf018","proof":"06d99b1b5b61f236c04551c78bb20f05a4121d291e2b5a1975025bc880e2156db6e6c7bcab6bdb98a7610461f7de8737ea47cdaf553fb463c210fd983cdd8c4e80a40ddc2b87c1bba335a8d0e82b73ead7ba0a9f746fa56aca1ba5f7ff73461bc24e3389d1562dc842ec057138a70d2d4353292320416204c1dc4dd6dfb5630b97cb7b609366ba5c9b7357bb45929c6ef29de79906f704aae4d2a3cca028700205299381f86cfba0c6dc63ca63a8de4d580bedbd50680ef2085da3267b26d30dddc3981aba9c4a5f379940dbb3c6e35a9576270be733ca4f3075ba3f4284fd08a43c2b68db67c6818036dc43b35bdb47e03ffebce938019d10a2b03aeb432b732e1f35bff4dce5a8d1dda9df53fe35d7d0f25564258728267b1daa07b9a5306d1268064326c3a5ed07602d741b08f30a6a3da1c24e9bc04f63a3afb918e83176a64c3b44cb760da26782dc9495b3e8e2db15247b2d640eabd182b9f57032896700e80aad0477b65ccd5eb32507739ff745f46d56261c85373b3195e6d646b2561aaaf271bee46bb5e339cd3db3ab49328df66a7b497d2b6be2181be2c680d055f0396f7537c5921c98ff91fcbbb2544aa35b965b74aa44be15217bf60e3bcd733085f88275cb0e7337ddfe5a3dd558e8340f71608e97198e31abd3dc6b988c6cb6e54327ee4de454d736ba5fa3b02ce6ba340e3cffa70080936522e131c3e1194aefd5e5758015c2e2d7c813a8489ec729c57b6886d0450a645c76a476413d348629b55332dfb27158b97c3da9c6b5ad9b0855a389fe5a0815001394e0f2bb5dfcaa82074093ebfb2be70fc0160d939b992e5cefd4602ccf9c83c292c29108633286d6a1b6ba8be1694537281c84a9e1c3699bb0df21556c5ae8ceeb8384870d09495120c50a9a24314b7aa6df5e3a9cfde882ae468c1ac9f5a9211e771b090d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"44fe70ad443af5383baeebb2e9845fa214a9ce6cde5eaa974cf04646f6228c24","proof":"48638d3a96bd0a6c9953699a7287ec3e24a09b382fd0d98c64a934dc3c93450276b10175282dc77f2d2b1b4f3dcf5edde2a6de10071dfddc4f8924eb9b1c38421e1f11e0ce370a14dbc882edab5cd87a5317d2a1d88ba9f48c450ddf815c381bd01b6afa28803bf85c3510a209c58a06fa67de51a8234dfcdb29c573ef2d355b6cb30c4ef82cb914e13349e0c7be561154df145f66d39b67a22f4a1b6b53c801e955c7c76fa1c51f19f17a8adee35aa70dd8153d730eb2f4e0d1ae16eddb4c04e18e3fa82542649c8890a3c758989bfac98e567f3aac98800c3245fa3283d0027c6e79216148cfeadc0ded996c88b8b679ab28c65e974ea283f0fce98e2ac5267cf6a4bc085513122f99d6c613f5b7083a34d3e07f468dd8d01ce7aae31e1c106699b82f9f2b884f13b23e4b17701927e79c449189f7b39c851380e7f873314f18723f49451b80544fc2594fe06ab186b177fc372d8387881be4f8dfb826e16f9a35f8ed19ea7c1176be4b22e34f9fd7ab29ff2ba18530dd9daf74dd936fb55424511c1ba45d2d6917b8befbd4f0ef8deb34c92695d4839505cb3e4ea2db5d4fb85ba4f17f3813ad4473e1c315b4ee72c9f37f16c1112553ddcb206eec28cb283c0b8cba2b33ce844fd420e003b0453f0b5e78f64d20fe3ae79200c4986bb902149741afddfadf290e89bf141e41a23e77c38e2fe3330b5f017f4eb308320204aaa2bf65b35203974e5f658e9f81c423fb7c0dbdaad425aae8132ff5de5c065312dc5ab682d53f164b5a8905fa443c90b94fb36cd894cba54c8e043bb3d6b11604dc47e3e46de866453812ce8c9dbcb7295f657f7dde5c4639f14f4c554b6472d05cd89d4c3e53181e34f583ec6891e966ffb4c63f22795a6797b3ac5944370f4d63c17fd3f4ea2fd9f7c73736bbfff1cd8e5b0d8be63bf65e85679bd8041204"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4a29d862ddb92ed2de2f691d26c7e91386a08ff53ba0609e54b0c1d7f81ad405","proof":"0aaa45d18ba3e79adfbd58b62320cc8925362a6548aa3745b17e02bb44f0817772da497dabe8345224706e1b058ba0b52234812b3ae72e3f151d929667194b315211f38bea56f79e0cc42b0ef35c9d67f14abba9c0a455541100d34b5b07904b20e7208ecce2e18ef2a32998d47f604d32a1a232f5ef1a674c9c97bd95deee6ac0ac662402b5e74a394e68066cd032005135ea01f1ba65b3346e47127671610195b14e33d5bdcc3cc5e7eadaa6a1fc8e21bf3e61463f75d219670a5846245e07ed8f82a936e0c44fb0d108140a9259175a66181d5025dae2b75c7c78ab832e040899eceaf199471f08f52e16725ae3c5fc24aaab4d469e3c5ec34e0a9ecd181574b95f63329ac0e44b394d698aa1d8756175c77713c93dd643871c1b74d93422c2c5d0c6341fb2f6a45ed8eaabc94f6ee5496049ffd1b353a0e4e71e984452264e8ce70aeea40ed1e3d19b73258d9d09b30d173ab5aee93fa07eec46c3f3ec661c70b3b2218015231a99a292b3a8d46e90d80b23fa3692a09268fb8bef15d161387a9cac96794e9beae02f61a45aab2129be4c4b9c17227241ade77831018d60a00cdc5c0ea820289b836df6c2f096a787002e395e22e27bea4f9ede91bbc147d2f13acecd12c6189f2ede259a9fae439fbecd9452a7a62011993c47fe39f17826383e40d6d958d6ac5b1a6f758e0c200008544b9d2d8fb39c85153e15b6285d645319e201824c0a627dee957d48ee058af1c7f8405eee83d6825ef1a077234ab8ee2b332177c6b9a698ef0898728a8059a8b10d21e482a1fd5eac1092bb9a7e3cd23aab0a00e3fae4bd12807f699f3039c9338e5f93711fe5305aeb0acc5a1ef17c6a524d20ce33d367239d0a8e566f2f5936e3c287db1ccf661e03116f580187e0dca77ba5db234ff05bb0bde1b8e92215432efdea5984e68bb7c1c5c8700e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0ec3b6baa536bcd89348395b2e62cb46b619fd2344b0bcc7bebb1b2d1be96f09","proof":"18e5b9e96b4d37ce1377bb04fb13cb4967631f979a5e6a095bf742ac201da757f463a222dcdcc71489a42611a33afc4132bf70ffbedd0439dafb8d9d42b11a068699065f752e0f5ee90b915267e895c7ce18e4c99395d37941fdd3f7ab64c96c762e2873cd75751d8a668670c99be84b4ef9d176636890856c437d53837412647d77e606d898f18b609421957601b27fa2ac08e53414fcfc4fd14476a3e1e7029401ee86142859071a87ea420efa805e8e1d70ef48491f3c19402350b61d590237f0431cf9cf5dccdd8c3eac7964c3a69abc30321a51cc200f7b2ff1bf56730e724d1f7c3d6033fae6f790fae52cf7813e26a4a72b5f4027e42e0132a78bba125052fdb048e1d1f43bba87b76a9b371223dad574858c9a1bd4afce2faa17ef6cb07447d2b3a3e8b7b0d737fda6f4d2ab1d04f214bf982cbde3bd6b3043b0a903a0f6fba4e1778fc50bf53ba78edba93ecc431615bda41b6ae5bf0d48db72900dc4e6dbd373fcb8966c05bca9027a31b92039a10f8b8285f537aac1cf0474c61666ec1855e6e9b63d75d35515e8735f5a0afb56ceac5db5804b49d9a1566c081324d1c21225c16cb92f8f399382c23ab08df2e54b9b4f97d1b07159c395ba4c7ca8eeb480d7ebc9a31195fdfdc969c0a6546bbc6bfa16c392709e153915e6ff53aa80d9faae5f0666e293d73bea33cc0f6664a8c0356f362c3119c1664b5f60247a32d731df1feae8e96ad2ff9077cb728acaae68b0bc5d5ebce6167a7583e2138439c477a096bf60bbc09e3d92f04a06376f7760266a867e3c151cf087bbd70f0af46bbcb02706f4e2a1d4099b054392969d481dd062f8de48608c6460145502bc7eaf8538a651991dd9453d8b8f080e1b283d031be0a84a9a41bdd4a482e50f5f8754c053bcc66bd3f85be8243a1c3ece63455805c4c6b1f7c39e088ce8840d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"eae073219588098f79db20570e4383e237ca1b95bb7b7c268c389c91e08f550c","proof":"d6bb7bb8163cf864f9199b3d2d13b0c36b6b2dee3a6b78ae1061ef9a091e0905f47f3a76df70efc41d93ccb20999bdd0c45dff385ec2d19388810b8d240c0553b4762ebf0ccdf651ba204492d9ae89e5c0871b5fd72b2322dfc8c471e332515f3043e1f914eb3a3e35f0d03dbb580fd7886c4c538a6c99c7d8a775fdedeef5319b8cf6b19ae48143206422baec142d027fd78ba49b20d8516751bff37aab810757287236b0e89fa551e0d3b029f332398357e6dc5bf5c0a7226b4861784ea006f45ddd874379f72b65bf5df21797cd8e80588fa1c743021379532061f16a5c0a4461da81318fc796c129378cc27cf33d345323286763587f313b645763693b51f4be2ec3d8df5962b1c2fb123ade8bfc874faaa4545f142c53594631dfea45555a4dc9deee4b803b03ddced236eab6d4253cb49b69bc14cc8af6ea9da4518f07b2c7edbf4fe51a94720d08e91c91515963fff2d059915c8296042b61c33b6f1d82fb704a22007fe4974576065520fdf6f086f0c28eef371ec6f06d60e358a843c805ce5cddfa311b68c512af9b65a10b7554ff23aae430aacbec944e862e8c1d5c710bd5356b8c78535fb0f0debd0d5bd1f73909066b70592ef2af6d7038f15be4a236db1fe17fe7bb9aa4607e8d7030638859a0138e35639544a4c076c70c5d9a07f6518d5766629dabbe26ae179bb39ac3c56c5c2245ed71da5fbe0ff2ec66060138b59ba819ba2fa00e4c807dc0bc6174b93281c2e47ed4458a21243b06621eb0f7381a5342a9184bf2980d9f455208bc874602942896a7be6dcad77fed38ce78ea1c47f1c15086a270db29785128435ed32addb740b5b8393d1c0f510a5f874a42a61fdab9ea96b4e028a4068e43aaf0164e29ee4b9dc25abf57cb61b4067a6a3e9c4d65a73fdc649da95edc3388afca235e6f26845d03fe7df722e5ce04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e03a03e2d42f306194baad3aa2a1ccf27073acd7ff59bf38439d5e24eae8e757","proof":"1eb9613782c1941b1d920c7c1f4d8336cae51ef8a5e00723c5d8e0aca11a390c62f9c1273b3f11588b95a121c8ed632f297ac2c42e93274945925d0c3840c832a226161f34a914c4bad2b85609fa1212177cd4d4b9d1b20aa46501d23d0e074ad6d9b6f6feb14e148ec06d442f0612d865c061d4d3b380d135ac045006864f2527bcc532f27011029194d6360eff6cf32d736b9240a523923071ea4cdeb3db040fc1900ee9c3b6a6c9b001ae10a2f1d3ce946c0ee0884ba37c4f4743eeb6e60341135b38ce42c26483468737edd060b796e7962f8bf7ada9725f0a15e4aaf800be057ef2a4917f209987837f7daec6d4f88c65a3a5903dcf79504668d70f5f3dde5e85d151e71e31210cffc7c19f27310f21ee2aba97f5b39b808312330f850682b12b593a5d27cd051ca953a5b36a88ff5497a245043474c0ecf1c9914aca5df87b100035e954a79e660cafe8d7749e0aaa36242fbf46e58931c684d5a90b21987cec3d42f24961121d41dfca2c9b216aed0ea668945736bd930247b82498340e79d93ade994eeae72ba032170b3e766da340f6e8ef9ffa90627ec1d6a7f20a1cc50e9d72f40ca0f1a317ba207c6a91061a2f5c479fe5250a33ba789ce6ee054278656d8fd327e967db0bcc8380825800bd8c9b2759690a3a98506ca3a5f566a246dfb7edfffd2b9051ea11a66b90e5d6b87203773f7f500c6850cf97a2195e7a637b9c2f1f24f13281415276939f18e0ec6514b7dbd27f43889eda0c7ec31c041c3ce9335d0c617907125b6bfa45d35d91c04414ce9c4fd9b11e44e2d7004d2ced9a424cafb7abefa645a9c1efeaa2580108cc5a2cfbdadffe12666ec109188bb0f77abe4b2f763b83d4a2be24a115bb64064905f3772ed11028ebb00f870945e75f1e0c8712e2c576ed3c80b22c5d82d00f1352e2dc409b4312a96fd62d0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4c8ed22da24fee9bedf5421a5291fa612501f49af6bf25f04c701b6fb60bb16e","proof":"fc7fabc82767820a78f4534dca375f2cd69cb493a00edc461984526bc6723d76ac7f30e6cbc2fbd0b778f1272b36374ad93dfdd6b60cb30f0777202245fcf2458aebec91e847f9e9be3641fbcc4bf40fba91035ffa37d4138ee397065520ff2740b8f46c7877d3aa207963edb6c625ce06ab83f8539a3e729e9e12e3b84fed01c06c68f65efe0b65858ba3595931b54063e2348068fa3d7c4b0ba1a3f131e3012bcec6a4b10ae9806bf3e8e912d985dcbd8a3d63f8f865f4b98ca1eb17f7d80bdd83b117bfda53c8621e8cdf0d4595f7b758d7e059df586ab073ebaa6d7c5a07da93563613e8cf2afeb627295c13ee54fdc7b04a19effabb92156628896f9b0c86088796c29e8c281478ff7b2ba051c41022a1934270e018f9b314ba5991bd36f4890b07e85d1c10c5009a2e2a211b588e4930da039dad44ddc93343e96fc834f2bc1c96496bae49665407e63f6c2d398e442d7bd09d9479faf09c9a5331a5116446013c6127b620dd39e09bd5ae22acd35cc6c8b73a05359a5bcc5ba8c27d1ba66dbd12f2b982ad5d05ab26d8b34a17956b3625240a0ba93660a191b7574055c0c9044ea7d14ff4f5e1484367685aef314b9560919fdf4d1bfd1d6415f8627e844189e1a446e7e8628ea38f3c9b75f9475967ac6f8489ca3e8b21e80c65df5ec893049770b0fce4737ae8623488053551997ab6f687e786fc16aade5bf72f4da2442fe364cb94cd96acd6d152c61369731e4adbfb9e227e4f2cf0baee230827d63d6242b7334beee38539106e96ba2fb63072884cbc2dc7da50542cf6df80230e58332b32a134bb0bd0c1c503b1cfb38b10364fa46607183527b7a6ed6f8f12f8e238357078bb3aee6299435dc14c6eb765e233024dc80a2132f6bab75d51005248916dc2c1b54d517cb147176ccedbcc581768e439db654a8ef9c9bd037601"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7619572a597a9d3acf7589b6b9a9df6fec5257ac8a5fb38747ddc84b1671c342","proof":"8a42dd3f9630a15a81d705999e3de8921256ec624001c0411ddef724ff8355530411d88195253705ddf7091c008c0d9e2f6a1abcd60044a9d13a64ab10f4d31aca4a2c0b66559bb0052472ee2bd981d512eb7f434e1f68fa539f5865ed40cb5d34768cc8b3f8797d8915a712eb38cf447b8e7641823225798f8ebe36b69c7451798ecc13429511c3a7aa44309d214dce160a5baee97ebd2f70b6b5f179170c08897e1f20a74af8ae9e99fa44f9a9ae71065a0622570238f8acd89a8d54a5f909e1e4ec0f6bc33f92af8ea6ad9a8a90cfab86be33b4a28122ce548c91ab589f0c3e72fa0259599fd348e099a0e3d43c80594a5a6a938a1fdbfd26360322fc386cb014330113974068dd711269e44044d27754e157c4caf7b2c85c43fca7d01b75642cad94de2b3fe7d4fc50bce70cb7d723d86195be389c32e8483a3d49355e507cd0fb07c88bf8ea3653f1ddc6b7220eca2c1aec8420a0e8478fdb2811755a4f82955e68a4b1611045098e991bc8bd2a87ec0d41af0ab570bb83408ee4834578f6bf671e4f8717ac588257d5deabfa8bc59f303fea124d142589e77bfe2815353e8a3502dce95be54a6b33a4b80d1ccdcb35ff4ea6f388635c7cd8b141f9b13c847bfa74b65138e4d4467a1c4173ac08d173a5e692cdab8925cb68fda899b02684dbe542a10db73ad9d9f028c2c4c51d006812c2d8fbf0bbc69cecf36f54847e5ac49ad6093aa5121b2153cb3d61bb1e781de83284d7f67b4b4bdf005916b176cae46a7d9bb72410708c67eb4874bff3487d3ebb3870cea822d1b94e86c09c211840c1af20d80f4125596d22a98680bd1cb9a4ba9dc0fc275ab6df13a98e672ed7196c1f0d62c0012ee480510b2ba67714e2299765a835631f27d029d0a0ff00fd7bc07f39c0b140afae771bb42e30a9385f3ce900ebdb862d94da267bde5f07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c2432db2c51446c787fd7e804f2ff7e1965f04e275f34b0f0c898308d815ce2a","proof":"deffb893896e6b3e1415cc70cfc9c0d783f559078bbc941385d85cf243b5bc5512314806e4ed77e824b44c1a65d35bb89a17c95b9e32d5327ab168a6a2586424baa6107905fcd91958e9bc77081189645a09d3510c90cafacb5b9098e6a1ef0c907306bda5eccec19017dff0302808e29f0960e5926bdd05a86d8e1df721b91f5c1d7c3803939bfacf12434c09d580888acca796deb940629e7f43ade06a0500f8a168e56870381a99e0546fda768426d16de4e00489ad33d1d78e1e6a34f40e844335a7631c52683b7cda399d0d131a400ec49d058a7381c17b50993e14af07d4896c4eb95de45cc19f6b52a157ce5aaaf941869b27f0a57440da37d3f14943549d0ca5c9a111754ea0377fc369f935231b97752a6d917b9ee4436337c56547de120788f1ce6997e7627d611ecedb1334bf8ffb3252188ae865b5f25b5258562ccf4812cc6aa2f7bd1dfffd22e54109995a17f25e88e3cce54bf90e0129dc776e29ddf42755bee785b90a8bb6d64ca2edcc30d28338f1f5480008af11b76d6424b0e6472d0c9792ba5b9c9de4e105734add9a2b0a1e2686f2813e0766fdf46c568d3eeb0fc5c7bdf4cdc3f71fc7326cd35937e2f561a8ab71a8bb5c3dbe6e36ea7e7159f97cf20949a23d690e5bdba2f96657b345f84c2f4855c4663fdff04c4252fac6c347bf7e416792f2cf1e7c89507c66e1f1748a1797b64026c5d3140c04bebabdcf830269ad020d744651a8e51d702cbac12f55478cd6f2831307e024a60365e9e3fcd3009cbb99af04d797c92442de695df63b244a351fcc187a783d5ae6c2b219bada12eadc6100ff96754b1ebd5bf90a5712040900176e74b3673d21db33a45ecb5591573aa9d819b37b44b7aa9f208b5129a45cfb1668039b800a9ed3178e074055148db85960ca5beeb1855f1121f7cb2cb669125eb93c4e8902"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"60a2490af4ba078d1082ad49e6a13630869fc1ad2f2d621f292a84a045e31972","proof":"a2bc5d04e8603891523603acf6d2bacfe254c9bc75bd59b7a133e01882c1d14e1a3c622a1c2953da1ee9aaf27c1461b02103bc42b8719390610eacfb9242c0400482bff7379359b4cccd8950b868a36e4f86a1021b4babb6cbc16e6707ed4f0b7ac3d81b9b5f0e5a8a61d995646945a2c2d074dd04200ecaa27c9e84fcf74269eae3018aa5322dca4723445c182337164f8d863b584ff00e5a72e676501527039ab3b244cbbe8405a60a8fb4cd9607639b584fcfd4257ddb688463fd93ae570893ec0d1eb89eafd1cc8ae2edf0e4834abd0fe523a36bf5a99c7a45dd2bc3700e4e097697d149c9cb62f34e7c990d742ffa46b3065bd708d394b1882276144010b2e477e6b5d51df1419378bf18ae573ed3b2348d33ddbe916caf7be3de06db1d105263974a8458151e0b4f98aa2bee0e6a416e9e183d53cfb338b796f342ec29f0c8ee26b1bfe5dcee1005e84c07c25bb510568a084dff3773e4429ddcc298538a0e205323cc382101e19ad0b90b8a517ad8096b9cc3697843750a81d223e13bd46183aa278ce5d1a719a1cd7fb9af05b0a41a1a6cbc52374b8b61ac68b79922c617272a6471558ffaa5d1ff140a3d5e488cf6bf9273a5f7e3cbf896fcbdc01bfc1bc988feb1e1a4fc8f02ddedb57ecfae24ff3743acf7db87472aebf74f827c226b8b7622175c18b248043de7f94ad28510950632c20a9001abc906ceed497266a810467154eb70d5378b7a3de2042caee86d225c1a7256c13a24267d2fe91c0433e2592b719e7f3603637781127a23918c8712d71dec675aafa9854630d4061e7a8e329d664cf5f84f1e20566a3c43265e2318c85b6c7759ef38c42edee81ff780650a1acb52c6f0390d79e68664fceb3c410b601f56c8e3e478f123e7ac067bc4e46839f69055cba805d2fe397387b78e19ff11ebbc1cb451d650fefb2c06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d6237580cf6011eff180dc2bed61eaf5eb1caad924d2d8ac1b6c6a280870326f","proof":"2e7283e6c2a858073f65345e8bfe49b5c0c6b75b2ab5455c7e686ce16bf9de4d00c9580fdff6d2c08cfd701b5bb8419f5c2c61f3e0a88159c16479e5e403a37f5afbdbc2cafa440c9f8fc2ad9be5f3c77f5d0302d8dbf1ef8476c0bc3530864ed2ec5e10e21e08d35536aaba1ef3e1d895491d746f0e213f4aac548598c9be3562d763d032747cb9944898e8fe7d1580946cb6c7eeebab98c2e5e7fcec1f5b01436e15104df864fe55ad58b69a9efaf49ba1812472f865afb5ad02cc959d71007ddb9704cbb6320846cbb5ff50930d43fca4179977b36fb3ac6b43ce38eaa807c0a2372061538967249c3afe9e8b31fc663ae86f3d54f0e87842b71af3ba33518865498be31c81e23b567f6b16ce0147de73a9671f3043a6de9954875ca2e717eafac824550d5f58dc8b0ac8770f72271ca8d9f15b82e6ef05ffc055183baf3ff2616e5302292bdc0ae1dadd2cba160730d98b2c9ea43684e0bbf68b8be6286c605181380aab23f1056c50066f9e5eaf31951002b83f6e32687942a2df782841766492027255a429b2c4b16444b7dbd2dbef4dac3dbff3a51174dcd161db671e88206ae52fc3b8fe5bed76fe9b6f9022f651fb1cf0897accba41210a979a373f38949c218b673fbd16ba9ee3ddd7c6b84204793df8172baae468ce77b4c3177000eaf02c6e303a800e00e3df8a2ae87216a6e31cc5a7135205d3dd4b16e9aa674e40be97b2a8a356a5dd9b513023cf06c34c36a784a5662ca12564b6109a5d6544e37f3e17161a61907cc3581bc060291aa1f55096490ba4919de095ecdeb245f67679c2f7718cc4e8bf5ac7da9514ca75bd06886f0ffff8cd3e3bf890e4ce4912407c0c25de7ec02666ca53f959af7f11abc87d41b9dae8ba598d4975d02d034a8233412a73f01c21fd139e2486fa6d2bf646316e7545de15054ae085e17d08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1027d44cac52b3ce6c1d85afb8352a5376a70e5074a398a3f7e6cc660ff9de7b","proof":"f45119dc3ebcf600dc7991ad56ea9ef1603730c4e7ab4d7d538991092edd5c558419e76b48b5dd0d2e596b645c035d097be3bb60b0cb1e7e9b8feece056e697142b0a9a5a3e3db435dc07381c7e5250acbb2c1a0b36a47909697bd258c2fd560a4aaafd39d9bd37cfe8c66a5eff0f7eb3621bfdfc228b45b66c6f76fa6edf6710bf23945776ab7702f52556b6b1e299b04bee4b33c56cf8829a8557a4697270e31ebf909e0af7c877d9894d021c9c5b5867a1df48017f7c2c844d9097e19400347677dbdddacefa8566c7b95484131b68cc231383b3cb5a4c71f618bc161330af484552a83bda250cb4e307e30e28905a8cd9a1460baee9b38d0c7139b4ef3341e539924d1c07cd27e625cec818330d79f8e2448519a86c3cb8df3b17935c44550b016204051bf730e4b437dfcb875a24bcfe336bbe95a0baa9347ad78781d1fc60b84c43af22ebd0df72afa4b28fffb7c8344b329a379f5f053c9283fa8d136d83250c44ad5ffacb5e1f8dae1688d3ee6ce2db06369227e3a39d481f6d20431da15de87b48ef90e428c729cef61c6d2fd89c65bdcdb00dab55c8617ae334508fafd9b2616ea9d7f0ceb15ea54f408a0f67179bfd7fe1b01cda23fc2338f4571d877627da671d8ea3a1ecc49f68cdb465f88b20d3fe0c7190990d625e3cff92d845ad7c82bdd6eda66dfacbb4fffab427660c4e7b41fd5baec65e94d7f7ab372b84537cbd81c03a3411587933e58abc2786660902709aaf914148fa686577910aae9333dad24f4fac42873e4cdbd5e90886b36ce43882f3ef70eeff7be3d956e8812788b480c9a4dbef5c936545c97a6ea89ceeb8305fc3673963fc7e918ef10b386232b71c167e45266d20c54343ad30e1a06e4f10042f2335a112f0a2da40f7a1b482d4bf2c144873265f82fbdf1507e54a21ecdb05d9a74f11f543309330b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6cd71c6e75357dea7f6b3c9a4ef2492c6d2447ba54f8875faaf8e9fac99d3603","proof":"34cf17a0d5979006bd9ffe72edf01a041c2c94969f067e074afc68ff33f64544ca387323b8e8c75804d34cffd249c14bfa4d5a72681ff86b204e8da1a475dd74f685d86b6efd433fd838d6bd3905be2d12c52cd0530cca1cb78cca78bdee0919dea9776ef6c81f830ae019db62bfc15f54c7506ae71e21086009fb4c0335862d297bdb6a2d02e0a6eeee29f8f35bdc1de3a60f43b0dfd0b196d89e84310d8c0616fd5ff3e441a7477a54d67b9fcdae9897e156549edd0fe92e8bdf982bb9450884549c4012dffad9d803d42fc4997b5c986964b8aab48baa092870e1fec9e40e423ee5ab1b2331b925aefcec47837594982df58eb0d1c5b7a1a999319e60202b7296546342a93168eecd395816c85630b1502c329a8ea2071edbd5e37dd3484bbc419896a86deb0cac01e8c1cc9987327851ed5ec74a11b973f6a27f0d0aaa5b9a4460a7ab444cc5463d25220bbb7b95762ce4e50554013c904bb53e9c06645e4424cf1e965775a66fdad76edbf96a7b78a60e64c23f78356a8ce6af28c844799ed121a1fb3dbaa90793d983e472e5fe101661597452bae51739743efac9f465f645654749e6a2532bd535a1aa414341d8ca979a03530cc6fa29e644ce7e3e2f54cfac90d4b9f3274fcc18390630bee7dce95d6cbcebc09e9ae82a7259f9ad37b655698ff4936498db20fa6dcd3a84f290b8b7965c732157c84f43cd372c1a606c1ae32325ffb1b35144247d8987d38c01b1e4ec77567bf8cf8ea16e583d5c42d897f0b29cbf2357e768b80088ca72bc91f96a005c49ce2ba9237496fa473f6508fc03bf7dbf7fb7c632f2a54772d7c6a4173352019e73b026ff2fc72cbf0e356a15903eb8dc0d37feac33834a017553461fc8bac90b50550cc7f541341cb70eaf5bb5e6dece3beb248517bcb2c512412206ba05a6f0757c39e70709d3fd120e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8424fe42dce3595597f5526def62e87b0eccc35c8d5a6e2ed9e9351599837f5b","proof":"768338ae0ed1679aa8cf8bd972de17365c7fa18b05541cc59448c390a72aff7d8ed01a9f97748ca4e6637693cab00404e1dd7ea5631f334fe20411d6990d2d35988d730ce5159f78abd128d0acdf8ab2c14cbdd4104075d9cd22761278b8136bfc3c9dd7fdef72c4f693141453f1a4a2d37eac763028acf38d8d05d1cb24ea3cff8385c4c5f69c112e4ddf06822a26ab45223b96f58c4e2204466ebbeb57b10f25f5b1cd1ab0cd5eb428709e7fd0a7d2fcce4133165ebbc02ab98cc560af7400aeadf9d567a247694ec951c7493929a77297a5330be99c7acfa59e8870f1310482cf0745ffda009017c787db9b262e0b4e97b739d29b7e7f4e57ca9b604708608aeb3e6dc7c616d3913921bfc08d3e546889d61f3ddee3a22b1526cac5e9073358ef7ea9b53dc4240604326e9d289e5623f9c278f51baa6972bcb26971f1087e7e6f560f2edd1dd9247099cba5d30b9ed1812c93d095abbcccc3a611f3d777258c5e1862f2cc19513d1a72bfa2d244f47a8e1379a2963717407b86e1faa4dc1f965b9b60e2f1a1f5013e18ba0a4e1b723e40a08d367764132fd5e194b4707c3d184512e0867114c4c65936b00710611f4c2298dafd5a54894ec7d01d62a8a17e1cdddf0fb4f7d2cf71518c229af4e58ecf28609a3200dc4812b07e59963acf7fd81875abc0b11ed4efcbade02496c7fcb506d5e362feefde28683b65c51a2667aeb355f5cf629d6c0a9487843e2285608d9f567997bd1027fa075fbc9645c4316023cc0d4af828fe4546e658e7976ac50975bf5fac0f50f5b28fd7bf43519905dc32328428f018a16ab56238a762e1670c47fde1a0072a30eb6c2639902a8e65abe58c027033c0fe20d68c899f760dfdf518a0931fae1c9b5ea7e79336e9820a12639b060322133aae1e4149289e5718ec55602921f88bb56c4d48c6fabb790b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"da1eb4d42f94b22eb97b68e41191da40a0a719871a0fe40fa475315dbc6b747a","proof":"00da1847b7a248c881dde66fe064c57500bf7cb29f71a6044a0af4df8e96d84ec4e558f6d4919eef6d81bdd9a53520680ab570b25910a5f9e9a99ce302e1ed3a142762c891f2e6b2e9bacb427b87ca1afd5a42550793d8e38df2ef0828db5b6bb06e6e3a4c4c363c855bb2dc7560dc549222da48631c6918e65ac2ede5c42a2d20df836779d188641382da0688eb58a760c78d2fc14ccb3697af6e86b164a90d292f521bfc98a17627b3a93d946abcb07e832722d2a7dce6af0d3dc7fd8b1c03a2cce1080ca9e7ad8a66b437aa208ea239fbab094d15945506c76992f224300aa8f67ff6675a6f4c028b54e943aade87202342184411b96057db6e315e3d1239badbef99325eb580f187a57df7c32c53eff3169ea3b8e34f57369948f61bce5ccc26245fd33aeae079b96153925cc296334abdac4020e5fa78fd76f3056cd94f5ab36de98d4049a7e935785de0df9bb24f233532e45589512ab765f6ea1d4d09f6b7e7c3f505ffebfb8889b0369aeb9fb2adabf593d921564328d4071b39920b864a218e30b87bd8e40faf8fa3ca8580880546b25a1863ac3d0f3529d0abd1378eb9ec24fb59e2906aa44d09651874d6b6987a8074a8dbfeea9686f816e302295c079529cd4fb7dc24e569a349e8a354b84028f8ccc86e6ab28c3a04469b7e7fc45a9a83e0e97f6c7a0f68f89275b8f8f5a7e68d970444db120760514926883a8e98b24f9d21ae12b4b96303e97566c2cf6d5c73b531727a7bae3801ef604c4a60d34e6e32ef5f59e337ebc9bc8dcceadf1d0491e61179b5579b7bcc905f87398a68ad93bb1655b2484c6013ae67cb7315444fb705f11bd9217ce6b55126cf309d6962859d210792ec78322d2cd053cb5035c971ca6ceec197441f817ed9310237683e269ba95d4c43ebccc22e0f11e31cf0c7692c9edf0cc7fb545d51f5d007"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dabe3e297e8ca0eca0e9dc9ffb4781f8db23e6ef018984999b5f8f4c625acb0a","proof":"dafb7318221ce91d8e5ee526a1797f4ae94bd29facde2f556667a0299cc7592b7625358c6bd39ddd936480d29fab5d7a4237617435dcaa0fb191b1ec6b2c025680b4175d2b5d47d8811829081be611beb7aa198d79570b43a41e760fb4e4757b249ec4d53a29c4080e0bc1492e0e4848b032fe607455c4a1bd4e3f694542220d57262ad8031f2e7ce34e55a3eb2adce20e02c98bb70a15fbb2bf84753247a107bc298c995d387e2f2b25516fbd3e64ed2d1a895120c97e96ce79ce2c2d099403c7a42d37414ef124ff8c2108cfc03e1ad96eaa4b896feb6bc0876b36e230560af630c8f672f65376585df2ecc5b1c9d56f9ca0cc010cf20195e4d3133e004240704e97ee46bc91e1dde1e3d61208e216dabbc2eec3219ab8d7f1efc5f755fb264aaf7d101f8c84458a28b02d6f4bb3ceae8f3927b5c7f3770719b6ad6a60e4512a2df3e49e3df510e83a2ca6cef24911e13f456c2c6310bbb4ddc17dcbc6a80914ec9c3de8cd24a1f1825a3ed0bf88a802fbca83e4eaf36efcf482a9a5ffe41de215200b46e058d9b3e14b3078d1fb69d5f6a5d5415b8f901a22640e233c4b5b00bef720c03ae15a5e5391a05d8686a22796a7d40bccff466db88975e9ced42cc043c7ae5f9504c9b5be57c624ebfe04cf395aaf830d31752d123fbdad274a427a8d803301abd716143678f1846eb7fa13314ded6201c5025345a2037ed22d52aa2ec6ff00281629b563140b18136e8fad672f5013f704c4c674a885584af70772201d0bf195d6cc57d2616a01f06bb1e12ba739a888732ab1a423a6a05b13367831e80c9c1f85bf6700b6e3ba69b011aaed152c570ef7020e769a2fb0871428046ce1c3df4ab85a23fbbeada4fb15598959001d32f4ca7e2bdad966f445cf0bedfe47ec71b5718745eea4b8d93c5e42c75aa1aaaf9b2bc3cb33ae1487a1f00d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"201dca708b01200bc984d6d9de94a60091625ed0990ebde47cbc5e4e115b7965","proof":"7212e6e2d1a2f0c56b5c7b10c7da72097fa79cbf2d79dac495532c48f9838a3fd0042b0e7b102b453ecb7ebb36a852987e0e8830504a64e8a61dbc836c9ceb10328b1bf91240d4058d87008ddb449ff3ec39c3ce0ef5f83abd8744e2bb6e9b4ada94644e10529de347676812d2f3adbce35363d57fcec3fb76d4e44066f57579043c9d26e832756fd00b8f040d76473a42236f9082a12e45e291830d33f34f0d135d2a756926028a5c7315ab9ce602c4ec541ea2a3ef8c1d5242d39b380bc20ff2068ce250cffffe6c3845828dcbf6a73c197de93a670b1f1130ce821ffa26069e3c744babe504dda2be3b478eec4be51c2269471dbf0194ef610937e6d4f63b6a70f6f2c5c454346b04d870f737c27133e80413aa6fc979209de639f5e5c839b48d2d0c6f515686af7bf40f94a760e5772a9ff92e54d58a67e3ebaa92cc4221061c49dbd24cb0b648854e5bf22ca847674e46e0599119a7b2499482997c2578fe9739e1c06de2b42ba775c7ac705b79af1745244658ff90bf92d4e3146b260d98f398e43e0117a518718bc00e91728334e13d273ae58e1f191eddb113a2523374dfcbd821ba69583451ef9b8acaa8aecb59652c5235fca348a8d066c17b8d38e0f5196636650b548bb5e94d7b151b208d12f61e364db55daa8a7d5fc325204e0e741a0735096855d5ea3a36a1783389251974d7a5767f49c21c2aa1353db37bc2f71481601bd0bfff2c71fac43c41e573ddec55cf4927fc4eb455002278dd066644eef8e25d2ef2a81160dfc0b10e75ab00df9d26f537e23a2bb9433109af01e2f93f0eadbd4f57ee153b29e90f1de3884268ca75b01319beb8f54767957c6780648bc7e8f6a74fc184e3370a205c2997178724fc957dd12550ef34d5dffc00c688c7a2bc71d26c4089320523d160aea178b2a6efe98fca27467b885accab0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bed18a6beb0c7da155745c947ab0f268c65d4c6ad9f32d6465be38c86d25910f","proof":"98e1cff527bffd44ebbcb90db2726adae8eecad60910205cdee9e914b80a4777ee37df46f50802f13e373b14f337dbed5f3d91db752ede60dbf6c923d6f14a45caf8bec6bc7fd499de40d722b76efb386a9b8006d9004f6dad2206632dfa731afe58428f8210a8f3457c74d69c0e8b1c320001f1372b03d58ab607d0d5d4b472ae73b21cee7381371e328c118a934eb94c6a36918971acfa48feef1305e34b01dd6d1e2ff312cf3d675bbd184c98e93d50f40e00a97816b88da2ed457c463001d5eb3fc3592a240b871baf2f3436e20fd89b7e90e8ae508a5bf2cda7d5469800e60bd722877b6fe042c115171c4d336bac3fea4b063e57302830ab802614885ca636c7943d8f6f94c8259c4adf241571927bf4f6b12cc9b95025025399806e42a060c7b971ffdee8db090c0f82be58845d5aba87faf23b61150725ef8c00e633de8b9e1e25ad3ce26517e8b13cc1dfe650117eb9d3ea0b7844a694dbde3df8157a13514d5ad388775982c8a63e524fbe3717985c40af081d61e5eb054a3cfe3a7a9015b1ac97c1d1a86cd1eb4ab52a02e8752c4baa81f72321454df3da8791607cf9ee4610f2b47e8f8df93f793266ca2b101dc475c4f0584d49bfa82060fd5c60b9e1bffad9a41f193f6978c69ef779cb719c1bdeef684c340d597418e2390872435af93f33296e0a8f07591f174cb67b857d685e102d24ee1deede9a8de00a666d96a315f92aa2cc6a2ce8fba62212178636a89bb930ed58717a0e1ce1783f366b9510befabcba4754f1b68f09b55e1a0408acf2408ef58ca377fadb76a0041a573d6de61e9482bc3192904075c8903949a90dd7604f41fe48e99cc5100a11663065c09ce555d1370e96f0190d0ddd8de220f92dd3cb1c5a7960aa03a2e40bf67c31475c6730741b10f99a39e3a9f2f90712a7f9e5571269856fda06902e08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8a431bff46cac6e862efdc85ad380fa3b5c13838bbe3a12bfa33d7958e5fbd06","proof":"5e7a53657aeedcbde5f9c32ba8ec8cd60f382033883d848c738e097798ed424b2ac800437da4938d4bc7158339e8db90ffcdcfb068ad96b2476e1cb9e1df9534504bc844728f428e935df5e6be024bb5b184621ba7f8df7938ee6fcf9d227607424b57189b74e5ef5c1944dc42e638c24a916a561ea5ba749bd4ceca00bd8f17f41ee977f393899d7564fd728db916e49a136e1c7a082c3c08fc8d73fc00950fc5d7f71e10ea6166b60970556600f69ecb94a9407ea09c06554f62879f785b0cc99a4821ebfa809936ce205835ba6bc485cbe78eff9497d654759e18adab690efebc8fd8ecefb6d1ffd5501d0046a2bc2039a92d916b30aca7f5dadaeb95256fec2ee9b97cceb4a4069380355e3c28f7edd258b2212bd79682bf24286cbdd845eebb4985ee06bef0cf7650a459a714d4bad9bd9f60c91a474e032840091d4b07566c464668ce684d13ca4383483c164518131b5de7379619e23cef6f8c15da440a97d9e4d8855753a86b3ab18aa3eacf4fd4eb99e6c5fa3170d33ca4921997075835c28d32054c86e2590eb2c17c684e0423e921cba045cae8101c80918529679ceb26ed695a252e4ed222ce51ecc9869316a31cc5e3d4e8efe0e505a104cb515017b41b060e2d64dc4e305b53fd1ffc3277ea1d3f31a37e2011cdb0e4bbfc2584e74b792032997117c87aeccc807cf4eb32ffbb3c1f1b225dd2db18e3104347b83881f5f4c23ef8010f5b7386bed407b24d51f61d60b3cc566c1f48eb07f9086cde29ac7932734bac55a1ff9c5045343fdd531bfbf1225fe6b64df06179525c308be738c3a09f48617243f98cd7866b13dd0510ef2996429153260a1b3788706c583efc03c6d51d87b5254047189d5f4b5f294bb60def79be5d5572941a0a037bf9786a3c08e452c14ddec8717d1eacd50eb15b688afa588a3af5fbde732204"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a41ac95ca7352453f49ae8c800539a42dc13d4baf75359121fbe8e586407c86d","proof":"c497b0bd6aa814861eb84dd6389f0107b6d0fb5f0873c5da3ced12ed8569df6e4074ef09cd1029b4c30f51c0ad2c7810afa4fcf33f71c1d030b4f492b5931f7eba7ec9cf79031e0cf05ce23b0725f25939d5d1b6f331553eeb9f799f4531ca4200afaa13485ed87a2df6598b387ae0c52c809c534196f55355399ec3768a756620b2c97041876e96bae93461a493c6d29d27c0cdb1799d4fdd9cca36216bad0f85a5c7e938ba4181ac9c46b25d8a540da9b4ed4d0775f3b604fab8abf5d42b0922eed38687aaf3186efc2da226b125b037f7dd657ae3bed371e3cbdb5253730d5836050a48d207cee3a9e0f5cb72a3a0baf9dc02fb1b020f83044cf7eaec48250e1f2972861b0fed39f97849c9fa858a1bd1f1e28b7a82af1bdb7b8482aa0e2378167d7f74e8d16c54c3f4a9a2dbcf56f1cb971c36fc89020aae71895b2500053a73b7e0a43a6abf22c89a62d975f073eb24369391162081bb73c997878ed52de46750d7f3cab03fe72c4402c6b25a8cd70c9878978ab65a0cc907dd12f153226cb491ab062ef36902847f2be26cc5fd91d2f8129b292c01f1f2eb459ecf667d6806d77d5d093b10480f8e8e93e718d8b0190b8c5fb7a5f8ff91918fb7ca8b0204ad0406a6b0fa7889cc97d07fe57cf39ae909edcd907a94be3b478a21438a2a7897a4c723ac59be2e6f11448ff89ce57c7d40395ced5128098bc507356ad1414672e7f0eae72191d1092aa82858c79175984f9fb03746a0f81631e21d5ce22202589924326ab27269954b2adbef43ddd572f5d087075e1d721bbd6f88bc174b54acb74abd3df519761eafd2f57953ac4a7be198699fa030b015266bae90121fe197f87b999ec990643e1e06555d89c1dc3c941cba683d5ba22ed3414a1f0d0d13abdaa06985cc7c28a659462969d748e31bf18f694e012f878cd0eab94f9b00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"423173607dddf2315335cb8e9bdc43a1f12f4f6fbe21a83b5035249a74e9ec74","proof":"1c4cec6ee99056cab5cca35f0bc46b168fb88eb80e2c12b3a55fce0b2dd6295f18e35778df659f02c694c50b5f1d95908bc78e5d3992b22208fa2b8a09bdec72e859a0de37f1299f1cdebe106932733bd879cd5d72b6d5ab7af6a9130c8a2f73be4b9ed853cdc1bc46d300417c2b0b5a061c54de9c0d2b3e3e11ddec94d74c4fcd98cb7715b6ac120fc80fb580e0c7da1156dc3328e0b99fe8a43a2e8eb4b80ccf7716e0905e01420758105c231bb566a9ef8bdf8369f79ab676ee861fa1ef0a69365371db1d76e846e8ca0ff5a3a28d81c2b04238d1dbeb4bed3cf41745ea06aae3f7934a9ab3b65f8b20bd2a0581d39fe8eeeafb66965a07b31d879533ad23d6b0517261cda25f5a6e936bbfcdd133d1bff6ad2f22b160f02d8df45c4f1a31bcfb395f3fe3c4cd832735986e007a141d15fe337c29d945bf41acda06ed3f1432812f33e0fed16b8bd0132ffb0986dd10754533bd348ff7f9faacdac4bfff1cfa76dd203832880f95bfd3eb51bf783666b151e0b33aa9959d05abd747f5e5359a90dd007f8ef58b5271ebbad489dfb403284f5e7ba793251d7668e5f40a4a205eaa551f188aa4e47df70b6ee96e51c2c1a11b1a462a03e11540bf05dee5322ef8bd3b5179cd7a64247b35d8a2e6ed20d74e95eb2776d87b8872a20484ec4f71aac5232a153d6b4bb84a0631a81f953049aee1d1b109ea57d4db2800e4c6e67a5e9d198ce9a796b4b4efe8c25cb4630b90b1ba037af75ffa789ca5adf7d1de130aaae12c650c514a1558079385bd64aed4aa9cf96d4f93c3ec63e3eb6bb9dc7ece5d5de3967c56ad64dfefdacbfddfc3da7a8528fa0d92e35c249ce0fb70737f63f1e1e8b80798c890c42bdb3348320c6d0b3a896bf09bd2538f4af0c3076b01fa53fae9262711f33bea5765da0228f40c972f4873c9ec1cddbc3d02ae749b0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"285b789c508d17c5b31caf1df5d13e8c6123713624cd59eea5f2061a90fe8630","proof":"0c5da04e1767983fef7cdc73164dd84d491bca33308f725c21d1f852d427e918c065641cd8d9834748bb9848442da8c6f4ecc9110e3fbfcbce9fa7d5b574f56b2e04387b1fd1b3abd28cb7a95c2d5a4fdfacd9f5a9c69b534e7081c166fa26341220271975e5bf1a378e1bde1d1f57aea25f07cf49332b1ba731c0853e287b6446e4eeea7dfb677008502d9475412f09ca4e36b368c3ca5f654a076d5bc0b3029ebc2437d293aeabc0c2f4ca10087a3e2ab1c0e0185a580dc46f6da1ad504a03632355dd9e31b4e3af06efd4249dcff10ee0bbc08914c5d46df6fb106e63fc0576bb8e2cbace5cfb5f2c7534e89dd190bda5fa591c4dc61cee706d8c9de3575dc8b3534ec4c3deec284926035a4f12b587dac5549304395d5af9170a7372122278ae56773a824087cc47546ebaa95b035bdd59fafa8279614e88660c43b7150846f3778fa34386ec45f1dc7ffac73fdd20df52dc10cbdfbe3349fc56dae72469d8dfaa26d99c5f2c3dc32231c6f7265bff4fbda1309c18456f50e78718d99a18005509586f76079008256272891ce785d5e881ef6e5a376f4712433f678cfb0404ce24a399dc0d7faa0b3488fed5e95341fbb3aeb3b6191906f1c7e2c51866522689efbe297d8c8f008e08f119ffb67900aed2036fe969d859a1b5a46e60324b84af6ec1d24621c67bb278e80da9cef43e469d4eecf0e7c2ba56659d1521697608a86ded0c42f9bba817f7e54d838ab00b1b7a508f5df7b38d9c66927ab00d3fe4bc72d4d96d06648e3864bd9a6cbbcb2ca91a0aabb4281720c7b1a1a3c4eb282e3db45bdd057748b0a6ba3c132799c68767823bb64addd8d715ddc323e3ba73651e29e51a98e31dcff8ca2216cd3f6df41c4c223309309896dfaa65f8f8fc0ac6ceae2fdaf7cb18b0606a111089dfbe9e6418104c20a25e329617c852c13a06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"041cdf6709cb828f92323f6cebf5f442dbdd059fb8bb717bf827279b83a3af71","proof":"74ee49c19a5251ffad43da3be71454d75fe6b604edb57a649c75b161c7024d37061b6c881d35e970e1f04df74b12a8e3f5dd8d4b5671acc2c4af5f7e7ddc9469acf3430aee3f4d0859d02c6b56402ada326a98e33ad66bb0006df6e5b752d62c389fba7cb08dc1142a48697937e6ed395da474fb9eb204ef896f2bddd974fe3dfb4dad9aef04de42dafe159ad41aedf36b0d20518e110fca14c51700e269510a409ff8e5f42c5ca089934a5f73db8f4575d3a83a3d108f3bac91983a735d8508e1e3d6f8ac67c289de7a5d69956e0c3f9d4d96a2095075bed72e6828cfe4ff0fd6240da15f148e696f0ea6aabc47be79ee00aeba3e53ad63ef8b93061fff9739961a9213720ac80d50f21eb1d707b9a31970589c6311defedcf763ff18d150023ce4628ceda781ad7fa4d5f4e5a25cd0c20cae15d338b5fdd36e48f68ec5f0386e8694ab5c20874ed9a3b47644b2deb995b893a6e255ee356501b7c79072130e5a1eac97c98f6a43d4b6279bb27cbb429ce40be04e50c1b085357ee179ea11244ae24c764d8a0ade6e9ddc9f5fa7f0dc9d4ccb9d034db9db4e290e7adaf21602aac3fc7375c568f2a1edf52812a814863f133574dc63d8945fb5b5e40fb4442e66ed5ea9b1bfec10894d33b3f8327d0485da615157da6f1526e53adb2b15584b0007dabec80f6a31e2d04e5c0a8cb6baf93ff0464db8c0a5942e0cef5cf65b7b8691f678676fef5e5bdb31647159deafe4d00f5a185c6e4269a86c1e9f9cff286848b317bc00357c1dae521e911eb0a1f183c32f143ba2ee9f2e06f92053b910980406e46ee5bf1937f3af2060f9c27681deafdbb0f01f68b9404b7e508185695793b0471ccb0c7798dff67ee59a3dddf76a9b6f41f7f108ccfb160a5e3d500288678da75e9df81d28935378d1050ddfae536d4cb02a1232e0b27165b5287b03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"50fce63362e0625f9802d4cc0395da6a20ecb597a46399559b0954c23fc03a5c","proof":"40911f5d11ee25b2c63fd078756431f5868ef693bfc37276fd0348099cfc3c6a6caf785ba8c9e211f621307213e8b323e40719356846d527a19e88a693c61c13bc4b3072ea9f48654a33bdc8ae92120a964df2f23e1cb0940e70e14a895c44668a636b811f8872bf07126e87d96bc646f1d22ecac90338762165bc8aba984d2c56e6304bf40e9f7455c9e295b62b99aa541fde3f418395e2eb13b160fe8bd10d4bf35cb0adc1d9f7f297c87396b2370f258c82f3ec89c9c66768aed21c8d1b07e9a41dcf832f2da9c97c54d1735f3bc980331b24e6bb7fd367e57b19267aab0a2c95e1cbf6f67cbb0be76d89caf572b7f16e7301b6cb19bbe964cddbe00eee18f4b1491d741d2f01cec301b9566408179a8b506aab375409c78d8225c5373e395c4d3081b0f427a47541f88aba54c01ea13ccd9f2dabfd9090c1b5095c2b9b1bd89cf0afc4345f5f7db40725fa48cb7ebab36cccac8fcaa52a18c1b661c3377a807dc59ac6a9f097a5fe34bfa055574317a1b80a9d1e6acbaf7dc65d89860d334e3f9a06b0f8daa2dd20e3014c0b70b2206ba58c19fcff671fe1235c46f2e47bd00671141e8e378d1349134555a2090117977dcbe9243e4d76dc9aa057b3255012512a8b68a5f61b03fa85fdc436269a24823e167048fb38c90f2af14702ee334485232124a8488ff7e09c005ca80ef6f70135ee266ed2388bda4aa6699a7d3252f919bb70b5f79a770e1f88fa1879ed911d429c6c5105a10c3610f74c463c3d36a56e5db33a88cb09d87482685f5d54413888e708832238cada1ffb66cbc7618a98ff792a71870892872507a292f98a1e200af2d214f6e2fad2ed29abf2081123ff2b9eda888bbde8e7f54638ffc3cdbba382674ca064a7131c96b7bc42d700fc6c62bd95e822c7f55a8c501793b180f484a65ab526dfd056842ecd9dbc1f07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ea0aa3a1c792b4d6f4f93a7796355339eca595ecce8f87ba8e4a48bc16bbd80a","proof":"92d268d9e1c9373ba2704c0e1267381210bbe80ae5af147d24b5d2e3aecd781788437a8d6ba3db2f090d57db11d6218deb75f769dcc34bbb46e060e6ff04b536988996deb45de2b95554ceddcd1bfbe2fd6742396fdbb1ba7fe6d6a58f499d201a3894db48cae83445cf9e80865f8494459dab45584a5053458f90545d3ce77e99755efec10f4a23dd8ebf73ad354ed41fbb810f1c39c3b450f44736c8ad1f03969b0ef24b4b1527fa1749711ffa4c0474299df57e8ee2618850d94d7829270c2decc6e65408e04af1dc5e0a85fcf6146bb86b0d0a08b6513a6fd90430f60a01360f33fe5270fa299ba3186ecc88cf0389636014ae2649622790cfac39abff41d2174fa5540ebb19951e723e716a623b2d2f5b6895b3838781e5dc302108422ff85ce75324422632e769d361048d021c3bf3ba20813488fdb29ce55672f54b2dc4763a07ca24092d3478864bfe80558c5b416b78f883223afb0ff8cab01f710072e78e883d003e897f064d1fb967fc9447c89c652c8421359010ed526c03af62c685145dc4bb263691c773329b19802e97741f55d4cfd44e6c0ba4045764966e4e3662b84103c035b61f7c6c99e9a3e2f3efa421a956a44034806e50fa075b6a38389bbefaf3a10bbac0a615582ed55892efe7d5b9c78416f9aa64fdef3c984b2c473a214d816c0d817343bde041e2f2418040398e0cda41ff0ca14ea7109e7fca2659a466a76fb9db884eab8d7ee42522630daf6f00534a539e1384b6b7e227568eb2db794959272e16acb1b137e2311988bfddd2279462fe11d4b8a60c086ad492672a6c81cc005476c0a46d036c5b7181dd62f597f3ae850972f01944e30f85affe4cb4d5edb8396915a6a93ba6926a180a05e70b8a43cbe2bf2f1b39120a2d36c7a04d6e9322c231cd56f87cedc138c3bfe3837227986ceb9f70a573a60e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"643f1cb24c7ad571417e5c66e86e27744ed00f8bec9e967ec770da30a9154471","proof":"e40c2a01cf49bab87292f684e7e93c90f29f31e60d870330e254c67d09b0a9189a158e4acc0c1bf3573e0ee2a6b852f7a8fc4f983978ba11831a5726fae96312ae0056ba1660690f9d7453c57b170bc89756dd70aa8c0c120f6692a77b9f864f3a5c17e316eddb56ee46e7b65b1e3f910d957871659d0cfc7f7127498dfb9b3905302911d90a66616579a03e99e64594b5807f579017f54983551fd8fd3eb1084981f1aebeaa5d55849b833f5aeaa0648fa0a7c12813224783f673022e1af60d642a8abfe2f9477cb6d70b87abf024d9cfed8d2634869df5b895a9b0d5d9ee014e2a8ad38ab942bf6558effcd56d346b925ab10e212d90d1ff4252c81e2bb114362f2d7f055d8fa710ac931d244c2756d997715c0e24fc79706d4b696e80bc77cad755ff87ba578651fdb412ab41437384f841782301a051116455124262547de047a89f7d486ee3eb0401c979d74eb96d41560993a4603ddf9c4bc53015dc15fcb25d9f5be885cf0e1e019dc9cf79b135674e6f72dbd860cc6be72b7ff44c37a8c59d098b521545095eebbe44557594bd2db76ed4d1b2e0585606434dd97b3300d612ac55e6aeeaffa97b11757be786bc0ea38d11d0e983f42c4edab4a6727674da3dae71b219f1b32b31d6631cdd6d02cb68986c7f8de2a2d2e0a119f671322e963018e35e2bc28c0ee4955f2de8151128b6bd241939567e2afdcffd37143d4e6c45fa91da3f8493a1b5a39713330380670ac87872bc0b43ee5431e6d816663e0a011d4ff6e68ea54629c4017534ef199784559bbc38b5b54eb36b8493560a302335f6b5c7bce99e1188ea9c090678999298ae7ec9993d7b16229ac5b1ce265b6f1351bac76d5086e177be9627c5d825eb700e2df6e2c073c32f13f0b23405bd2dc2f254c5c155587fafcbe37f86ec20275d54bd3599c1665ae42ecf1a710f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c091a5eca103bf6126e4c4777fdf06c23fc4d1d3da6b1588e3e104683c80863a","proof":"de50ce023b4403c4fb9e65eb49a1b71c36434a59d5958c91545be47616381555582aa63144a78f98466a7b6f4b4ce3cfb6e8ac7963aac6b3dbce0c2ea0e9ff75e00c3a5e1f7f2cb4b31f44b3e981e8d5b3356e8d6fdb22f08a8f5e33e253ae6a16958bee7f9afe50dae295dcc87be89604f8c76aa11180e02ca49dab8159f641326e81bfe4c4d4659a7f37fba8744347d34d91c83435b676734f33ea4a98630de32a771a6769134fc1e3708b45cbd9177e0347715b3ab03eeddd56492a592d0904dcbe39621ed509ba940fa2a0b940e7a49aa5727c1abb99910869aacb2f7b0b6881739094ee5973bb9858cc3bba9116bca9c645336256aae857f800788e6163e418699e0e27b7d7ecf9c21871b4dfb5638056b327f7e4f715bf173e85604747c679c90c93ba779ac0d8880a39a16d35b16cf30f75ca66613a487e13e2aa051142425851a7fe34a2aa12f0669f320b9fab7035fa3070a07983230c9f25c83523b61479a5cb7d7ed921b6bb8fa6aa7db847afb3c78df9e3e1ba1f3e16096f7d20648516bf98381b22c96396f359bc58d7514757aebfdc75efc6dc04a07520723e3eb0bf2784f24e064eced79dd6d274b98a8d38d098c3d5f7857173d311e63941c8ca44b75808a2e4eaa34cb6276d049ee6881f6b570f93211097f4b46fc45b5fe2cb5b2252569e3065d7be8cab845d7cb23e0938c7579936c3fbac002a076c7942f67db2a86efc327e23d25c13d19245678a1901cadbd2deab5f5a4a38d23c42481b6a1eecd1d1c0df9ddff62f942a0b9a3eb0c8989ca70356982f18e7363f44003c5f58b609248edcc636fc53f4d5dcac857adebdfb1cdd301f8641e85edd7cc1ee5fd57d927443a1d61f3cde64eb2b8a7daffb2036be47a7c3be137e825e071af99d7e182d680259183566a36c34206918dc3247537886757f9ad82351c90f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"087e340b2e82c7bd3d1fe2f0c7de99b732578675afa27734985837a7c30a0362","proof":"422cf51b312abbd4f0a087fc3f2d35c6d9038f88cdd5358460c14d56448d174d503deaf0c6bad8ef4ee04bd39cb00ea6e001d3256cb86773f76023afbe3ef55f52c7ade7714f2af6c91adf8bcc5897a05e6d908b61f9be049a1c0b4ca7731b7f6e28ff32c8785482ed5133ffb39a14df1112684529982f403945ab367c0f831ae9db8726cde77e9c067c53e072de01fd4390ecfff42e2afcaaa9768c5aaf18013aac37284ddbeecc2b0dba577d22a8f85059f5cc375133876c056900eaa03608f07a6d521b08cf4dd39c811ae3cfd59faa7138cf2bbf39d221ca7171750e980cd848c4486584c233af780b5afbc9a061a43e94102ac888948dccffafa4b4e3189a6b57e08f3591bc7f32a1cbc1aab41cc25f1daf5bc59b768d70617e8ef7d475e8a270c654cbc9efefc86f134596741052dfa2061eaf7e94f675e3fa0ae7e56bbcd03bc7e1362042959872c771cfb03f4b9b7b707e1c67ea7decfd9fb88c9b539a6aecb141891110adb9b7ea71c16027affd560879285eca16ca16830b71d54fc8e5d79783620784e19014ff1e7d8d7d968c9b4e3ede581df295d84930bae21c120b0184f74f91430b6e29db2146fbe1191e008aab8408261466da2013a50e4a16698946b624e2521425a2f6df32814baf529249fb32e36e496cbcc82f113752b4682fdf41567a01b05ba40868dc850293f72f5d2b0c8a99387e04387d8d100530657e33e7c0b877b48301a7939412ca87c5306312d0a70ffe9dcf7acacada57f0ea1da4678bc5c92d9dba12da0af0bbea2ae28946addf3903fd9a8a57d7a725e0098854a9758f0f51309c45627bee774207d96d14f6318eb101023e6a36603c504066fb1ef1c9e042cb8cc4082fae93dea68827e91241b73c2e7e15cdf9e7043a1bda4b3a8fda60488a10ebd37e4e75531a02e383cf63d95062e9b557517707"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6e3b76a79cb08415a9a7a98abd95bdfed0170e6f1ddf8dfadc467659740d3c10","proof":"2e2ee39ce1364bb1e6d03adfa816bb53cf360b95887fe68185035cd83af70937ecbdd0832a9b1133c62690d3d394084e8f6167b4505d3ac183487cb37a3ad02b2c6135a015f318cacbd1bc0b267e49e4143986abc57abc255768abde18d3fe7538fcfa8a8d6b6fb28136f9b3a4a094cf383fc925df50425948fda87ac6aa157688d5ce07fdb1db9e6b9c6d72d86e249c7f9619cb7c13e6467f55bb5bd3846000787458630f0e2831d602b60c3c9171ea7b4721a334fbd7590d424fb7a0b5710091225ea8180a59aab1fc7186c4b5e7dfe63e92705fa02ae2a9745cac070aa5013abd81371d361d5e7fff963537ccfe1bca13c4bf26cb89e41261b91ae8073d11f046cc4b9a4879080c7b06c356688b3e47ece9a93dd1f0acb4516b310854083c840aae6d130d145c2cf307ac9e8bd5f14b077ba755c4a216dc2fb18c59819a70f2d719aa93229652974c0ff28e3d37669998b4f86c65fe76ffe80e7b2ed22f72b8e3e3687717195d5ca468049edb1ad53c991899b346c862aaf1c9bcd7afa469b8d893294499ed37409e37052362d9f365d0122c99174a6407de220624eb12516ea034051041ea3b8ffc0c19283faa6d2bca854d240b0df5974906aa6740c0675a678d3d7d74bd25dbc245d6e87514eff141b1bb0b8712ed75657303b581412dbe5ebd29e820becfa36998bc87a991a412b297d25df511ce548515bbe6b1d96728548044497566efec5ec840d13c24240661295aae7a4f5e4a2ef9fcb5087543ccd932ccea897427e275807f658371bfe015c1e002b01911219e08d72cb04f064458fff47f38c8d95017c3828ff9b10874b504e9c8915965e29e9a127c70b4055afa75f81edcdd2e822ca5f0f65f274f653e4e5d07130cc440c200af666e9f01b7fb613170c3cbe8ee83eea7efc96398bc1d9eface26cab5d5df8887b0c6170b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2efa5c212523a4160aa5790d71fa9076a156b7306bee18cffaac4683e05fb668","proof":"8485c18f27730ebe7e87ba79a59bca1e9b142d7c38a9b4502ad160ae304dd214d81f771cae6117457d05a68df4fd19286d2415601c375d07e1ca062c7b27ff467a560b37301e37a1e3808db33c9ec29a8f2024356f9cde9aa5bf1a9639f3e7492e0f085226fb97ce8f9c6c48682b75185d0de0858ad7a30248a5cd032e7d340f37237c63f873098dbe6c31a291a08f2a7ec0bf2ea053e86c65a65dc089f15d0d19b3153e437e7ecb33373a5c3b119dc953e964c9e2c0195d98a680b1304a8a0ac70cbb967baff67c542e95729aedae37307f215c2e932cedb7042a4618fa0205022182a0853f29504843ba871e79713b557e1e04a950a733a394ebc1f22af2238074f9ae656a8043ab29a431146c3bb9eb5ad6eb69f5440bb2031a539abbc16634518d64337ab773980d42112f2ef095821f7996905b935b60143855a829ca0d9c1692bd6c6074e9d939e37a533acc8a122ebe3f828cfc1597c41ae5ba0d1623a228579f51e49a612c2d3240fb1070e4efdd57c568d620f03dcb7185af7d15244ec7d6cf3847d086e6267691be3145c4e776dd27427cee66abfce0fdc4dd21783e8c3b4ba845c52fe6c721f9c2e0db932ebbc1297a1cea547fa44052e0223f0f18f0213c0e349732e4891e51c36256f85293a4acd4f7e6e147652887a4aba76c9af555a70ac26c0ecc68a96c6ccaca53902bdfc856d78ebf0fec6eca8c08107692df326b03a1043dd54decd216058904f860506659182fb6a344659b03ce9c3f5ce7863f0311fbddba633d0f5a0e27051935c53102d8f2a9a0ecfefd6fb16c46205c3fd639eca7a961f6b9140ccd8905d94b1685185df4ecdda7942ea82b6c1842478542993e8ae02bced1fb6b2f7212220e606f1fae2b3ae629e8a420531707eabd89acdc5b48534e04a51229fb584e287b37e9a9add9bd2e74743ceaef750f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a491c8fda96746da805fb9e6fc9f1f14c91a774a7ddb8f7b0e61d2d6bf07aa69","proof":"0610824670b2d5b7c0c752a47ce4a777dd5a34403955a780c1424ef99381115a8a922c98ab24dca526bc70ea06fd8b0e64930b0545b4bc51c3242befd4bde37f4a5349bbc9a8e8b6af16955aa4727c54afead2da1ee8715589ad64bb7b3ee254ba676cebe880f1b3bafc7626302d5995555ed672acd2e465d8096dde70a3480926f5f3611afe04ec05bf57e52686490c0c61b9e10bb5d4ea8325d40364d5610f1c7c7a4ad1c2d6af0306b7f3ab8f6fe880320d7f46f059c9b072687df826b600bdc709b262d533f245c39bdf10d76571c1309fd1b0de06fe158c1c75e10fda01021bc929ef386731e7b06098657755e1a1adbaa14debf3f3638e9d2b08553135b201b6461677e774d01159c472b2649eb0173de633914c98e306b9d2ec90310ec41bca7e178de260edbc5a1755ce3df8905d9a6eb72409e02b55d1b6b89c0842c867722779b66553461c4a2f98fae949a7bebfec421eb0b23b2706a7ddcfdb7050d5eacdfd6d1a99eb1cfd09c2d25b4037e5863b9bb3894240d1dd3f95b2a2221e2de40689bdaafacac5b0f201ab53bc9fec700797e174166db51f5def39c034f26c1946da6b0ec187819a6083cbb7823d6fb44bb1a9e689ef7435014478880ecec240c1ebf61686868938536343277e673de110976c20d634b24c50d17c9323c2611162facdd54f59e1772caef06d14d17d62df7aa2d5623c8b402e04459126b0bb4999de7100fef87745ba8802bb89d3613372829a0914e5e22823cc648b4c5cedb5aa4954f4cba656e0a888889f892c48b64e1f7510b1f77213d6261136256095a8af43f131ee2b711f1b93cc1920f19507a2693eadf32e64cf70f954f57b458418ac4315a46dddbcef31d058ef41f3fc518ebfe6fba71bd5771b3893d70feef0d941dfa152bd91fd39ac7a7bb6bab6d604f5fe0188866cfcbc2ba2d7cd00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1a69117110bbab30ba1c7d0b94d975d55bcbed2fd20246b944e59274a1a80a23","proof":"74cc354719e21f3a75771561d7af6fde0952a28840ed97fcace68607e5eafa3a561ac7872c7c14291335744c1d08072e1e1f1aff4621a1ec6532413b1e0f4c257c62cab1ff0c1df713d0a92b6dc51788b3fbf8e8aafdffab140d77f3e7e24d2a1812834359cb82d91ba77f06e8ee3fe1a8bdec456a2ca7edbf37730d184d0e32344a224f75adf487204735ca4e5cb33590a4875a08039b18cae146fb482ed20ee59c818bb271a19dcb01a07dd6724cf1fb04c3d5e524c4ddeb26942b61b1420657de13f50e714ce6bfa114c2402465b14192db115e18b092004fef205d3fc80e5257a569db6e4d72c2f6838a9abc65165507283827dbb1600ac4ba9e474c4a503a660c366e824467da06a3819f113bd6a5c9d42ae8d9b2f3dcf420f527b43437f864882e405a1db65f3f755f75cb451938362ee8fa70f97bfdfc1fef64241f6c7641b5bf9c3191cda228dd705c92831401a43bc8bdeedcde40e8159f8b5f4b3a98be8abd0b012ca6db66a75bd0c2ac40893f923ccf7ea192667eff1d04e6605c986a467bc0134f87d48f773bcb235e4d2ac90ba5d5bb2a365d45e2ea50e27a7180d35071a501e40f40bdbc0ad7e07be58abfc8fffd8f812ef1be56e97975b64c2a2551df2dcb0f1b99b661631e1c9eecc97f94bf8a32bbe83028e97c11ab017f64b44863b0a4fd965ee1bebeabaa01f39b07f666748dabe4b8dc6abb7cb1594c921f83a2adea3e83bbea0da819b34cbc8e830c9400ad07b9ebb7707ec076fc188c096d65e94f5e93b7057b5b890e7b3544d08630c63b9725c0119338739b3214e4a9b21f94679f972402446c71e40dd121a0f3a9ad3d1b7f0f6f5ba85daa6c7cca58eac328e8a0c720ad9beecb54fc07ad18a8a4ab87c1805d7aa025054f4707052f323b7caafd4aeed1761079d7a165fc98828011efa34eba8f9ae898beba06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d8b0ccddebfd01e3f420f038f38f449605eae9052db40484842f27e0de00b627","proof":"3407bcedd042f521e5fd18037927aca09a109d3a0f9896a6c00284ec0673ff67b42d6ae86159d8137f2380d94496f7e540f3cfcee47ee957e3350203b3608f0db6cc3abef031d4f9693b3469367f10fdf273df3bc41fc5e949344ab28a7741189263ce7c17fc9113b6e4cab9214a92f1bde12ebabb9e51759dd8aeceef28934ad4f0e8ab0c4976226c21192a094ff3086c370acd29da9e8c05a4a9cafc469b0bc2945484ed564c6584d1d8f9cee63df8567f074ce679a78489fc59ef7dff0906799e9f8e1fc66c2525441ea6941b2641e690c03cf565c51693a298977c4724076204501c46c913a9cff9e75c882085889279b8fdccc5757081d38a2c450c2c327683cf83c75e244e0304ecd4cbc84b222ded4b14d936c48cfa622dbf8a29de5b30412b77be1630285f63c435c902f60e475d5e8625c25c24744122f853b3384dae225aa01e4bc3df3f8c3afdcebaca80a53c8c24bc22a4ee9163afd3ea2dcd76369f49a9f11b8bcaca578dcdd78437b62a1143b97d4263811551671a0649bb3a5c99a587fca8c833095dad3551921610d2cbae313b12108354b5bbefdfc1f5707a2348daf7aa450051e5182617d2332d4aa110b79e30290aadcd3cf30c9f215a6a104175a7f3bc9e0f1f3ff387e1f1e1fcf3382acbb6c1c4633964e030e387094a45fb6204ef2c62c20fecb448dd5e81f7360f20c15f3333165f61001e39dd5ab07038e3b0013fa320bf9ba9e1abae59addef34bf01086f711308da30739c77c3846ed07960e967fe53a1eadbae5668630bd94848b632c3e42a2b68c955e950bb416bc13f6ca7cb67156063910b73c2819fd39f6c9434f378df9de90ead4f165322c63d4ff4983231280d83449662b1d1731d590e6b74f01c4c66011ea95620cd83a9969dff20927146ecf65ea8fadfc1ae0af2c629c54f06fa1be6c7dcaba0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"80be1f5d2c5b1dde88bd2d820d7395e17e5b3e448444ed7613c3fe31d824805d","proof":"3693c49dd1ad5c21d34aa7e66afcd3d572ce7816b44ba40c21ea7e475ecf9733bcdd6f43a9863cac01d2b9dc1a20b7a9d6e382537c62f50ae6faf32031851f180cc3e09e44cf97e027c96920be196bf7d7f146d1292d184b5f7b5dc2fa65580b545c72306bb374cc7c1794656970c0cc1428315ebcb9ef1dd9189a7144c7e77877d3e6155b3af906abe6fcd165ed618e7adfbde75775945b74fc016768557c0538b3d90103b7159937bd669eff579bf53c93f07c586cec782742b2e38a965505e5f9600cf5929fb9951130a4562fe6f3952f1d49747f951708eecb85b88927034004de36702237dec07c7d869108065692748e9c4b6c33f608b9b0c41dd5d31ecc67807eab21781b2b388ce37004f0a8444bde133a8f2677b403740f34b3f87e9ed38736f4f6f97ee91a46152c480575f23eb9287ce6a583ab68e329afffce484264cdcf02df13fd80b5488c627bfd3ee5fadf975de2b2dfd7a6a65ec4f3706b4e9e0c8c08d421beabb4b1947dc7b15a1da6cd78b2de6023ccdebc90ae818159b4d7912256064a71232374efc99fe68267b6d3791ba36519d90b7d18b089a270f208e6abf2c68f1f300f09fd95302efc9ce60531b514e5a1ba05ee40a7e87b4c0a7205f40c902f268e5b606b6fdbab9ff9ea3864c5c807dd9f063ef0f76d930b18e2dfcba4a5385c194b103bd96816b47a9e9601723a4b79a113ab973ea26c1c2836f8ffa3ca18547c0be5e08049d022100789482879160b2b4348c45c788370e2ffb4a62c29825a909d84c83a8ba881d8e949d6f3568559b6d366b4b257751f4c9fd9701194a73ac867cc8ee48de838c0a1e582fb7fb85a97730f9cb388c81034f0d1a413e334899a6223d819789b1e0de69ca409bd1f9fe7cc640d4b1556072ec815173af74477c73e20e13da9f067fca6d9d93ff3637c91223d07322a6f0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"52b97602a84486410950d89a68bb82196430812f5b73528f65077a857ff77a38","proof":"28b4ed1ec6ca288e4944f3d055398b7160adfc2c9cf1d2ab5f733da68dbf5720a824f510c444457121936657d056f858432a54307201a12cd4d6aeff00bede3806f010900b30c5a396b637c14cce59122150bd4d2c96612ed25cbc120727f75c386c0889e6a69387e4c6fa16c9557347cf0baf3e42b9cecfd9bba728d265f577ae61ee875caaa2bfbeb61824b3982a4b8763e2df216eb3869288b40b2772640bed871f24f9d3c2465a966b9dafedf8bb774a665facc86e6faf092ef40b604808f7bd198e214bcb892cd585aa9824170d1702e798f2ee4d2787cd3d9fcb498f0370e00852688ded6f6b706b2146498018f36e59853a1c60f766b769c43323d33b702cdf3293093db0759cc92eee7b130908dea7c8c7bcd3712a9e091ddfd06b14aae320d3ce036c9c425cee570916290b563d3cf26a39607202ae65413c38ad5758ce26dae479ae4c0fcfc933fbf08796d6b3c0974c8c3878e1464bfa83a81079b602df6cbc9162015794d5f5423ca457db484fb9d4c5d690d79af8e6491dcd75ea2cbf0b627650c0f0df44b0c7edacfdb4896aeb318498ff31355db1ae905b196052a3e07c63049a015031c7e905bc603bb3493e6ed7234251f7e9fd7e54567f180a9c92da6d7b838690136f441882283cf579d8e4286663a525c2a261c6d22c983b591953a6a49e0ea455e7b737d95986315e969fdbbbcb9c63b0de02f3a27c7a57dc6b84997a40dfbf39d9edd7b29dcd959ac17c5b64578631e4c2b220ac58dc2db8c840748ac2e6cfc0faf3b381eeff730b9c51da2ab191124bb9f066081f6214ef54b47b8914b3848fa0b572dc42015f2e0fb58517cb1cc2b8285a54e324892bf4cd56cad3ee7b36e92212692c806b285ab6de83455874ca117c8eebf6010c46ee6f5c5887289142fc282add63f1878f4649bb3be217c91dcb818c1bc908"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"38dd3d6b8a18230b4605ecc04166f2ba8a764de836bb06b8bff275f4e1798530","proof":"f0c99ecb201e54e521de8f79edbdd4c450432082a0f1c857cc78c4b67aeec16ce69273ff12092e5b651739637cfe8426cdf481c62483d08657e59973aee6087e6c5892b0440879ae8878dd01c7afab26dc434f9ff4d2efd72656a042365bc40ea09b413578a7860b2b648bb13d32ee90b1b9afe179294a1a599ce17b55d3991a7cb8a7d43c8c8739fa567d20b2afd0b08031245e563753bd0de77a6f7f2ef506d5599dd1beab560a50506bec7c591a56b97e272c7476e4476a885e320b8b1607354671e69c901f682db851dd3b90d14ab1b6e94a28584c607e34abec5ce9fa012890b200fb20106338f34021ed410cb938e45b479425396bf5eeb42c6247574e24d59b397e5556c6a35414349ac802b7375dc08ad8b905da83370d9421b4932b4230a384961fb0f5bd792efadba69d81f9d6f61201a8dd3429189564c9c8a514168f81f9a6de211245dab0e5bc2f3ac055922cacee7e398fa3c458d06467e6131cf3400b7ff8d00d0f35b7aa28c5a19fcbcecd487edba2cf1e0c892335708c01d86cbe1c21cfeea7a763a7b0ffe35fa8cc450b87db54ab007ea3bbedc6ae012d80a3030145b5e9a5e9b926acc7d1af566405d42110335298b30578326d93a108dc9d0945d7543b7360e8542d103e5f721faee4bc816a258b837c52c57c694e71107d1ef3560068ca0e0fdad345e1606cb3ddface9cce85f5ad8664bef3402d1388c53fa756df4dd4b7992ab53360763f7338af8a783cfcbe19481d793375066d687c3bc1157a4a64c99eda9d65071d9f012184c84afd5ec415b81c5d40cea528f649740764e197c2261ccf7b71039b10c44cac8f3eb24f658993f396ec0fb222c6a97b0c327d4d4af8a2f19ebbea1d9a759b464c3efffb51bbdd9d1c2ece9508fab956d4951216c4854a863277f64a296def8411c0d7e0cfaef601459038dc0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c891c4ea240c3b18c1e080306120a204d144a3b8e2dfde61deb278e7d7cb1934","proof":"72132bda622360f96fa95db655a9c3d24744079f0eae921b456663f8ba5584041aef55f617d1417d12e853bd57961c9dc6828fb294f9036a3ebb85c9d5ae514fca5ab155f4d55e873eac139441dd0bd2ce785d61825c7b97772dff8749fa27502afe003fcd83bad3e5481a594963c4f799f0594b0274eeb5cd9f5fc1f7ba1314c8e15c9e88135cb8e809bcc91b7878553b734c10877f7669424dc5442f2fc6007b3746f96e0b393dc5804857d2f7ce5a6eadc3e13c01ef5d62b5131279b6410d419636d659f2964917cf67fb59c0649d39ad9d4c1d74ac8b35064de80658b80b82a3e8022508b7433d649fca3f9bfc5a31a03dc74ac3495423072e9ce652ad0ebcb90459672cd3d1648bea37163887ccd53c1176c1f77d8af62509473c542a4e28a01ae4d5cd5cfeffafafbf02a2002ddd3eb352583ed7b0e2e49691de158879f81d8837a085350773f9c9d1953b761487d0e98b43faa5f8ff41a893e4fbc749a2174272beaf4af2bdf3deca5e74380f352ec383d798b60930c5a15425e81829e07c41042afd8b69de789c84d4253487fcde57a192b704ed4b07648fd353703b90fa2190e189c65a1b5380fb5c3a01a6bee50203fbe722d678e90f4dabbc66081844a3b3a588c72be602c68e9cf9c52442aaaa45e3d564fda3f9a9fc1bfc607b8eb08b6c6d49ec41d2981bb58b0046da986dc1eb32873a54ea96f83475eb017684bd94df1a880c6483b224cc0f0a1e3bcb522dee4e5dd3c9c6678d474d3e047f9495b5fa8c9b52e6074d297176b47dbdd4d1ccc52984638e5ccc280b4fa86b2e868ce793789db0348fa0b53e3a59153359c4fc3e899a052244120ae3af5188599fd981cc6af726cbb36e9b265fb348629e2d611d2f50b6dbdea9bbd3566b660f41037f9d40cc5e65e5c93145ff6a6c1854c0ad9becb2c91c5e2f7d0d8fe8a208"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"04433493e578c1f6487b56af1d09e0401b1b24eaff7701cae3509941c5032954","proof":"fa582a77d03818212fd2a9e2652ee26e573d69055f0e0696ef49adbeca4f39043cda7dce7850d01a908d4368f1aa29710b509a513f60a31e9ab7bdcc8b6bdc625c7988736d97adc5deb6cba8df2d41dc5db6245f06435cf160ff0e2c58025d323eb3a9aab02843669585d1958aee83fba75b64a14b9dd16c00009f2b0d80fc586b9a13e0e39f34157fb167d723b806a3e2c34ad29660e583ff2c3b6f7cb3560da144f387627b59bee8ac74f511c4b9fb79c66840d1756db526049efd2c227309b92d837c3e5f80e882ee511effec4199bf295bddda1a5f487c671973e309e2070c8f4f383b8ff00059e0f9a06ab8f8946f670c9c2032a90103549dfb276f82680446d13bd99ecbed64f6f14fd16bae7c6dab9466992f44f8857e2f01599cda5b2c44a0b4a83e45799b5cd3ca2dfdfcdb199c1169b35cf4d30a32273290aaaf5f98cc177a03c48fe09090394dd619cdb0c89f69aaabafafc55615027343fa6124161649df8a7aa75eb0f869253db37d29950f276d7fdba9a281625dc83107ea4e3a0de2f781f8837e09c6c4c27d6087c1994f8c5c20f8020f7f6b4704b5235f5d56f2f15420e110d4ca6fec0c0374d43b8b68df9f9c9d99e057c9bfbde8e9201de4bed5e78da72d0a99a10ad3af9ecee116790cc7e65c5c30da9e3dd223fc280c68f6c7725adf09275bea59a498ac9aac5e446143d1a4ceb17ffc7ae9f9741d02aafe876f529723f18961c4c6af407d48b9165cc08c374cbf8033cd50d757685ef0dbb60b150c1bcaa3c5b04d65b7c46259dfe1e266a2bba8718d1050c2b27e334ae03e42254d0d725932ce4618686b709dfb198646f0653eb11ea0978d7809659a9f11d042495db82a2b254b6fd5cf9624023e29648b387d0b7c3e3d004b760277d87b0268e5b79c55f09fe1c493debad14d7ac1d11dfbbf61bf9aeb36eca70d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"02e7316cc0b668385ded432e8f9e45eecd51fd63fc689329a3387771b479052c","proof":"706b0848ca39b545dd434e5159e225822548258040371c6fdd55d7de6e09b24c4c303a7b966de3f1ae90b010558dd6b40a91ea908d4d63f5bf80428812aebf316ede389d461289a5a37d2c0de1ee9ffc423b570bc80c32b62692bca83fb61835f268ca8f5c44b49820547c10bce6a8515c970f985a824f020d1d649dc9a5ad15af329027e36a90e863d901f0e68f508102c8e57c03a9fe79dff944293c50750d7e00ab487d4efe26268bba11ea0effa72c897ef6a851ca3c441c5fc45e43ec0e0ff1c33a1f6efb9238f161974f3c044ea18c70f9cea61fbfc03d97a254f9f300e808352b6d4d662b116f817f1a54c58ff46aa747d86162866704551807ad2b78bc2c5071e90f52c5517324ee16a45ca3bffe873a69fb3fc529f984bdda704e620060d5f41a0829a260c6f8b6ccd85c95e18903cb2cd74f458bb1e0cab109f125acfd75281cd9f8795f406a3df47a64393786d33f3f2d755a33a17b8577f0474af225d97eeaae9adbb680fecb4f01821d0acf584b16dac7b472443321b7e1b877c4eb2aede2bafea417673bba6193a250befa85b8f6a74d8db74f1ed1e0569c4f4c5479ff72b5baf26c637cb06cc914c1b1d9ee831d2611452154781fd4fbf039ca743de06aa09b211a38e8d06948cf3e1f9712fa9b85aa9b5027cd5f5be993267a34f2de27a619c74c35a6e2a68b85faa2273ad1701bb8cc94668350cec98a5bbe4c79288a9ca9c6028326768ff9fdf4452c3c029234729096a9dcfb3ebcd35c2c93212fd8ee767021f9d72bac2b7fe9f4162ff1f770c1ecc17c3c8cb37a0e629a20effed2017773d6589027b4569dee258a17ecc9c9116acf7383e938a314347e5064c1969e4d2fc868d35b2e031eddd51c22a2ccfecc294319a559ebf9cb0e8fa0c0a8ca94a07fce0367a08e70d5d9ac15d7e324b9a9754b87711be3130909"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b630a708c57bcc6eb29a464c6bb084dedd69ff4ca2210a1ecc2fb53c3e73d058","proof":"e2e202f70baa965d0b6830457f5759b56426f761a663b9bd1a8f7f7e5ad62d14b896b99335e86730ef9db0d486155c11e795915b85e487c3feee853cb5cb0218f6562f094124b6d98859f56cd70d7dc9582b56c3ba905fedaaa925a91ad964554ee8ba39f551d9b151c454e9805290032a017f8e75838cd3717c237212c0d7787a714f9b1c889abbd9c0bf903124efd44615b9470fd0d57d2a1e340b9509300c90ec26b1b479d55645157b3fbda5b2cb8e92435ec27c65dc1dd9e21eec505c09d5d559a03f32dcb2d28f25a37fd88c5e21caacf8ee3043a5778d6fb5303a6a036e47b1f04760eff15db42ad2c4b1a84b54324304e0f2dc731a8f887b2a5fe54378069eded9b5f100fcc4299d3ec0a7282f435c9039ccecc3537927ab958f663deea56514ef0f6cd087b022ae8b245e55abeaeda7310cdf906972f4829ce5275ebabd2f28d3b7ed84fb526bcfcfe233876d82b33e1599ed23783e9fd75c6881210882c52e34bc45fbe301aea17c795d299fca523ab1547e0b24e7f669912c8340789958791388969744cbd695bfe71c4261c7087fa4031f90685464d853350e2aa470b5b91f35e47dcd566d0e1991d35f1c4ed0482d90cf86b85beec007b6262f8c96d5b4203354e1f5ddbc6c4f984031af005a197ae8d063239d09b5c5eb0a7a804b2c9e60de7806e67effa66631bcc4d8ba41a5008c7742774d1fd858f2443288710de70ef84340e5ef95bbecfcb907ea1583c14133e5170478125bf25d42656e925c4a92cc9757fb67856517287faf70e5b57cb388eddb4bdcb82df093583d1a5196c3857f06021d655f2bd683596018398ced8999e7cc4984aa701da9f15dae6d064a025dccc11bf7fabbefcd757a65df78f1e9a75bbba216afebb5769d0ed99915dd711e24757370669ba9d65771f638dcdd2dd5b9d21b8207190b31430f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c86d1f5cba580fbf43d630bf816c3eb2fa97447a779babf700f2ba1bfc25c645","proof":"843504a8e7f7e8f4a0f9da9daaae518a821a2197d214956dfc9fbf2dc0e7a9086682ed173ed2a30e555961b1ad520f950fd9ff5a1c0813454874c4f32c1790320aaed8646d3ba624260dd2a751ae97404e00751fac0fdb1894bd18dbb572e042501be20a44ec1443df9742b3d4300d93ce0ba075540a76b1f6a59102dc0e6f52042420c09924d500c51d3d328f196789232504abc24c704fbcf203f57bbef50c49392a33647149558b5eb8b3169385da5367582264ac1298edaf37d00f97d106430f3c1bd70c86c5d4d749a5a74f548e5936a0ad770de7fd00380f00398e440744b76f48a95ef6c42b08055c84cdbd7c0040585bd0b9222992ffd54d159d4e1466a7a9acaa8ce3ae39d30d96d79bceba77ed1654d5b9b428ea6b2a21f5ec563cd431cd000b50ef651bd3348a0aadc4ea5347c534cb985002e9357071d0a53b3e9e020924e83d84eca469f98d3c6139c8dbceca9c2482bebc1e8393b1ed5e3c6e427c3f1b91aee34e80d4dc5fe6e55f2b1f8adde23c2c6ee8f9668abab7c8200c9efc64b3bd9ad034af9650f564f3fc689aabff1381adcbb75aa9a04c87d7231e10c9c123e4e8070ddcdf7b1f54bab6539a7e8b08d46a4c80af5ea7653e190a363a38dc4aba09a58f84e73f1c169bfb3d84420623c41c57b30c17ae7f891058503c90a6152866bb377c4d8fc5046291d1f8895bd3c1f72710cb3941ae7c0744074057cc962c32f95cb3783e911e3d0fb01bd7f368c8ba0c95f723fcb81218b37c180860dbd7cdbddc6a9c3bc1cb0201b865069c7a101ca43fe70a9bc1215bba5610250c60d4d75bfae585839f96ef0587e97ea29e5af65abfd0bd4b5f20c54023ab85e1cda21993fb7c740d9008fa813847c99cd3add21ca5bfd7ff94c414530120e8e9e47d25f483b8da990130e2ea6486a57c497498920a66df8beaa315c406"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a4fde5c91232e782fae4415d31890f41ae817ecfb471c5ad11445b416f4cdf60","proof":"0ce47921fd16a3354c89a891ba3e0248d10cdaa47df0d76554259f25978d1e22a43f555ad42a2f98b58ea055dbf8cfc9e0b80f96eee79eb7b3dbe78fa4a2eb5db6b192f111c45ef12b7344f7e38546f6c8a5a681f61d62ae756fda2c20e50d642a3ca80c3eb5d2a09e7078b05702df0e0160dfee5e0df25efe7d54b57e0269672190190dbae78ec23ae5fe5118543b5599c97f13a4a2dbfda34cacdc09d9de0931e45b803a7fffb1f9c8c3e6ce31d4747cad7307a764841005260d9478ea45075068c4a8d011c686a2179757cc94a4bcf71ab2bedbab7e9265df696c0a2112013e727094fc708494b1ee98f617061ffac7bd56779248e9185db1a15b4b7b976316bcfcf769ba61667b06d919bb5f1cc01e781010fd0be7d3a809d3c444e0770de6e9918646ac70194b8e8f6967d372aad39a6afdc692666d6c0c72cd4a28a476688aff394dbe6c0d5c3c9193366d559e935be1a76d214351bad65e19b646e04aa6825278dfc6e4b42542a9dc2ea3acdd573fdb485cbdc51fe0f0ec74e28ec87a5050d251021c0d9d105f355ce6d12d65eaa0e8a536398d18d903652437fcec7f2ce0b80da62e0b39e5a4f8ce7212b2ba33937f7c959d4fd806dc8a92abd222127400df93105e2538ef1ff1d60a4b18f374988fbd5e3d6d66832329dafb72ca5e1a2947528f935bafc0546b211262309d4934aab8c5be173913276783bd709d008a6e086e10a8e8108db6b53fad3dc603d8308fee7217870cc87fefcf25b63c482c368cdb129deac76a784d0c3165b753ec78c887bb2161709deae6f0f8aaab4bbc5998afc37a4866ef2bddc7c4b25f9c84b73ec72026cee4654022b159b40f0c76042ce7a223ca9f769ce3cc525bf1e139c583c72e961c5cf5e9d5fc2c6568046a7e76db879f67460d2a9ff7d7bd10e10a0ea1efc9553c11c02b9ba0f2d2f909"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cae666b4a61b08f600d5591ebadd008f52d3a0db4b7cb2d352f11f899a8b950f","proof":"f4ced7b2e3f96ffe11fc32990d9dfb614f0c85c6db7e08bdb293413a4129c433ece4bd46af486a21830b49422696a9f87a98962e2d1a2b294cd97bcf67ab120a6e6ac313dbe82c6eb7881f728d36d5d7f48e82beba434b169a5369bb6a9c7817a89f388ac2dad5fe6cfdc846a4426005af4054bd84838047f0f6dfcbc075975e17545f7cfbfca6f2009de3964475f1290254a24be488dab91908c24f50b70502ab8801ad4b77d1acfb0eefe72382f4b3e4c0589f087e496a93c8c2ed2dbcbd0863bccb83485838308c6d6d8ce86bbfbad9281e2b1da330f7896f80030781d903ee593a891e63dab22737f27341cf1d16a52be897ede3a1d06db0eeaa7cc7cf61062b44f9c1b7c6e77b198d05206f00dce32e7d38e9c65ec0dc7ae1de3be989410e9330b2a88a28383c29a13220d34afae8fa3d29e2b92d5bdb9ed4e42bcb0367365e91175448f32635c5ecceefa484ddf8d7ad5e79b733f8b5ab80ad1284ec77b28a1143e29a7e2994be805f993ed62bc0a21d1dea47a7421144cc704ec9e91518260f31478311e6fc01b32482cdbda730bb5422eb46d843e0b18b812a80c9610280c4931e453dc29dbb7a497a96ad75fab9517ce15ba8aec92a80184fe06102a8856721f6e53acb5ba2c07086fa46c40c12e386b1a085b58bde7a8e920e3f6b36dab604c006c8bfa6b7b1b1a475cea03a2e8e9e794783fd0f220d493f278744ecf783cbee9b99bf2a0e5c87d6bf35f14a6f1e0c870e211617ee84dcf0d44c3db8b108faa5bc9a7a4a97fa120eca509a6cba1f4ef2ad7c34c47e86d42a0ab109ba770ecc628e129ca8fe371dda168244fd7d26cb857cebf1c8a597dabda00c397cbe8727a3f81eff586bd9a6ee3dd9bbd91d198a3e4a3293e49f272551fa3200ae7ae9afa2a850e02ebcffed0d1586a49bb78f749e51019ac424941a94c65c04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1c7acdd9a1b2b3730b9731d68c09e31bf8c0cf808552bea76965963b37256231","proof":"342ed66dcfa2ae79a720fbe3cc35eddf2b250c14b9bb57e4d00f4b7a5a43b7614c36794ba6a192926783f05819c4c3e2971fbb314bf4ce0c22173b2edc9fe51076786391a0332dd941c7fa53b03381465190f6dcf7168c24da9380eae99f2655ac99f3f25af31ec4261906406981ef1012991845c62c5ba6dd43c8b53979b8131c21de1712b8e5be780b0a97ff85c7f1ff73e823393b9dfdcffdb73ea2ac3a015db3bc7e6bd3ee7452b6753face329c1b757649bb3991c6f1bac1db92d1630058e78d712284f46debb6e73aa7292fb00a5b66fcd651a9fbf9b7647ee2cd0b300646dbacd56195179f42155e55f8e59ae7644e7ffc74159f43ffa382783dfad09facaf64e76e76667b1dd97b1dda0eda976b6896f401ff516dfd5a65a12c02868bc682396704df2f3cb4befb54678b9f5ec99fd08d29e556163e2b757c9393b4c967ab33cdd1deab29f3fca99a178e0ef48d1b138b3446b4789e2b3a413d56a22aa2c94c7c2f1afef3f6b4967642d7039444811b2df49e44f01c08c51baf64d1d1647f1b02a1e116aeca7804d6c80ad67ed7325135db225386c989ec55bfb3946f20b1d38b8ad742b5a142e81dad626e27fb108412328fa8bdd26fda22275aa7252a931decfafb457067b749d67815f93cb35f39f4c26d159f6a357a6136b3651e8b71e19d58b0379437b142575ca7e965ae89141ffc870353473b34169742f460e802c94fe9a4a07ff3ad130cb1b8638d6674bd665017bfab56081c208f857762c4b204b2fdd5c5a436e7a44e6076817d2de8e69e5934a903b6af5e572d8c458089f519da763aa3bbd7e070826fa97ea77debe95c794c7c25aa64a917a96e865d5972aec00ef1e78c443ac00c14990354cc94ce22beeb4c7351fd10a6b1640077230a684cf1baed8b68325095367c91b7440c497aaac5cc678771543c72f2e08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5ca67cad70487c7dbf2281bf9a79f40fb8f00b4d5c4542310cc9805ec89ccf2e","proof":"a4f61c5d3752fcfdcae6030bce8a177d9dab228e18c2ab057ba605f1750bf26ade2a189501cfda04dc91272735af70e8b7bc5ca83f8859caba6e89f4ad63c208c20eb81d6856ad32f8b438f316dc0c84e20315b0c14d26a7c1df222fe3ad0a2440e32e915aa7b2008e1494040a95ccf9d68cb1b47879ed5a74adc65bd91cc819a07a320761e1ad5e05afddaf767978948a48f98d39caa8035a0781ab74481a01d24b0d55167eb7ad80d43d008bf86ef28e5abaffb6ccc5282fd5d554e801590ba3351dd0fd6b58daa0ae77efb9f1a2f4a89ce0f98d0e037634d686e42ffaff01d2536f22ecfb533b44611cf0dbf3304b1202f2726d18748b540c23b6b86853126ec50b3f1d6233aa05fa940f692a5f4204a9e74a2ca9e37c671322c191eabd5926357e881f7ce1e850d6c8013d16a839dcff150ae5dc72f39e9aaaf722516139e2a8aee1785e0096bb200305874d6014c5e62423c8bafbb786625810264de56dca827b0518f01d6f925e8233811e0872996c29b421facdea458910e3a097d5287ae237045f930fc64a1d64078424eddc457de6cf5b3be75f697ba5639db76c0f685b4cd532295aedc12f16cc714fbd4746c7e2ce28a340cafda026e46cd8d8252c7974cd755f810c77f1d0444b268e481cb45a9a88096ff90995b9836e2ff326e403ec11fc313c138148f8e53dcb732504d4f6bad22831656e5fbe0c743fcb029a6b68760a67685124892fb44850913ca1be3fcc82404f789c2af5122e19165b222c1864f9d4f2872121d4f1bb80024b7a8b1e66b043728e08d033cbbb919200ae225de9204da52bf01f04edde603c8207bba9996befce40e40e07a0e8413e2cab3dee1841eef471d69de32fe59e6dd4390f3238e2d9c7fa8b0475477ffb0107bd820f168512ba705f69c7f6789495055856e8e4828934589c79cc2c0f463800"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ea60faaac06841d0f2a0b91163dc1b159f2b010aaf9ac0f6d2f0430482125844","proof":"ca985ab9295e6d0a069cad897f775df8cda7b13df41eff091cfec7f34680b30a768cf5cd0edd2fb70b71d95c5a54a76747ba5a7d87cebd994dd253554658b7690248cbf334e093f894459144c17b7c4ee839adfcf295590abdb27f89c036452604c13a7d10d6aef4147667ab5760de041874cf7e59da9614378642bc028d224eca7e1968dfaeac7e55ed4a80f9f512f8f9eda3ee3ca0f3ea79967fdb5e4ce10c5d7d1a8069d6e17f9b432645906a6aba0c095a9c3960fda022852f8f48d3690d3984c6170d073e62954d90a172feab88c3410a4335bf7f9a8de824fec5bcd00afaa9f7b27451e56945cb1dae1424cb23b78f85f44320ccdf0a21536a2a83503b8252b102b74978ac733cab928708dafe1a10475bc406b2d02a3ac30d96a1392ba01efe152e6f34ca291cc703d0b584c7d051d09831bedca79c295e8c6704ee1276ea16484a049d75f7e3ca63bc6fcc9ed2ae057b3982586a869112a450acce578a67651c1b6b7ff981ff6e5fad608af7890019326018ba1b0ff68dea0278c20f24cf39a48f260c9a48081dab716540dc07cfb5e5a2e9396a3608353446183d42b4575b5cc85c3348faf666fbcbaaf7369223000ae485d00f76554b84a06442229ac8cef8803b6975868779f25681f9cfba6f3c491f840b7589405713ee21021e986493de7a37cb8f20cd7c609163266fb7af8deb25d5f30615f3256cdb53d92628047b6919d7407d30dc07b2062cb6dec2d87027c733eaac5013bcd466d99f5afc0dfbc5620ad3822824b8f16b8305f9c65b7e3f2ec2c7af9c073ca77e02717f84f689c3a9d7e85fa8cffe12f193df03db3e9dfc8fdb818704c8ee8f7895d07bef2429356cfa2dcb01b8305590642d1b8b7b68635d127719a4acd27e2e434304c2215a207fff3e51be6eef3895022c162a93ba951b023005712e9e3d8145f20f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"94512c91e815974c30c89364ead6c8ceb06606037d20f293c50b310490918d79","proof":"9a42dae9e1581c530c81587b3fff7d637d9b72a8479464781f0f9fa14396b717da98aac19443ca09632591c498bc8809c4340e5c336ceea6a8bce7b129d8773534328d6c53f6f3463b1fb9180be058fe4fd7aea95cfa56cbda78c11933737b43d29ab2e650d0f19b5a9cbcf11d8b510d30c4859a3367661e8208a1d2bfdc3c355180ea80bf563fb446df552323819c16768bfdb75b48ddcf3b48bfbab50528075658e3494dd0d0b64e4fb74cc56cf2e5cb5b08c53417cdab8dc08f90207da901330685489232d4cc0b2e4d85d05ae36f3c35b8687c694f2a41d6714e8f20c80dba11057e607b036d71106eee3c6c26436e5c69d777e074909ee6ab963130a10edae132acbee81c8958c976c5f61619a0bb10564029b4c95471e8f0457e681c0f92f327ca2849d2f32a3a6bfe29b0afb452218d331b57ecd4a7349204ad0d8e5e087e0809c4ab4e39c3ae9ec95173fad73e4651f1c34b29c3f8b777d52f055476f4db18034b678c89fb54468de5a4dc6981cc3803424660d9978a168f3507462af47a059384a761144849da23b19ec5c6d7cfae115adc2cbea1ad0971eb557d03a4c75a24803f728ac10c4fd3ded5ad948134ede90c0153250dfcd4d6736288714ceb15fd48cff343cd0330a58d6a89b425662be3b4c97c65842fb8e5106a297876209890c7a8145693880c51feb4728bbeeb77224ccb36abb17069b15fcc1e18f43d6235ab9b793bf84203343373051798ce5c4bce335e147802c3e0ea14b418a249ed813629761eaea6ced2baccee824b9c7b00e7ff71c0fb5ded0ed217cb1a9e555160beb129ed7f56502cfea6b212edbccb9409c1cb9cb02d6c56bf86b923999afc7c6dd3cdcf1ac58d23db401cae62ecab48a64636ec74d36d2a1e2d0c0d58241e39b54662a6502a08f0d62d937f63166cba81d0bafacb45b46860960706"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"54f1a5777af993124b52828f837f1eac1ad9130d7fbe85e9e72c3b31fe77e02c","proof":"6e1bf73db9e075a9f92297cd5c344346417d631297e29421ccd6a28459690e0400769c59949c6dd4a21939d6cfbc327e6075b2f4273798f532beacdf37400d55485944212d04641d2109d25bc6e2b273e417adcda1720cc7aa7c25d9fc0104034aef71cb2108f9dc86ddcb4d3baf7b1987ade8533c7157afa4f8ce7f4d185034e527806e7f90f6be466f5296305ae21019ee2b3da3f339bd326a192c495eda0a77b90008e215e34188a3ccebec346ef16707837680912956a970411285f2c9094774db9d0d423285a7e601dfd7b7a30607f573728f9e87aaa402e26f4103b3053e705fe665867e5b47b7fa04a88b3d30befb216f757341d049e26c460208192ef2785ddb4d2abd8ad836ff44dae235e872a5f429f7fb4205f2ff680e6517ba39d275f4a4a61f79699b2e88a1e444a1c20efa061d13351d64cb86213c76813149b0dbf0d9e9a3b263775dc00729e2f79fcf491c30b9f12e93e77d85cca1d64630dc81e382673ea5b6897317c75548679bea286707a5c9abad6c22e9eacb3574719e21577b0058aa32d92e12fac9e1826385665805a437662c4cb3b29fa03c501db271a3136216300c77dc5e80c56f61ee66405e1d3b33ddaac64ae5fa552e8c62662a6a66625a9ce10d11c997664532d99b01db0e0b117e7024aaa7961cae2e4414716b4d5a8903a767cf48570d5db4b336948d4a99d1d26083cdc36bf7c52d27683bf265efebe13e5fe21772803859ab92e34ee6176d8bd0c21b4089ebfefd3e3a7d3ea29ab944b5351f4bd5e8e0fc113b64b26ebe44a9765e951db7b39c8c47f289fc479f395f1eb432f0ae8ef61ef763806a2c627650de029b47a849584f34975942dfe96896c173a6423dc9de69eb24430a4309b8a3cdfe495af9c126d208b83c6f090cd0188f1c68bc5714afc41d7e97ebdac664b1077cdfef9306855e04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"16f699eb9725eee6864a6f20b5ec6c90f006b872dc5b24eeb5ca27b0450da717","proof":"ec18624576fe19ffca6562953a12b4bd5c91d59b4d1785c51b904248f67e1d22e25c9f6ceb6440f2364f980c5397d25a1b49ccb266a311880d0bc52414e1017c1cbf2b1f15d6e1705f9ce8f4b09108e66c36755735276145a076fd9f8000415b76ed38e93ddabaf88b96ce7bf7ef920b0439929bcaee1c92a173442b2d505b3a6126d174afc81236e39226505b6115289803befa69cf9c24c58563d458556e03154f317ad24e4daae1232f7b348a550f32299fffa92043bf4069b2fbdbc81f0edb6b7dbc92a2d46953c5ea129c31427d9b316a5dc8d3a47bf8e5901a35eef10632262fa5aa95df6fafc862fd516734548d90c3d2474f36bc8c0ed96dc2aa514f9ce02150885b0e940ecdbf7fe6ef732ecf6ae9be4a6d55f8c7b93c5355c23624d40e0d32b88a9b54579888792cc2ccc7de351984278af38768e96009db451f53208e8bebf1c0682bea523c981fd455f53a379c28e0dc336bfa5c6d44d081e24da4e6c269286f88ccb12743629db34af0dac2035f20b612c61cd0d8b5bd804201624071c3df2399406e395508c082966f4756b7ebd83b52d6fa081b8eb2e67f34262342e281f6be0fa12f8cf8a1cacdce836448d48509fd5a41bf4f108c7a1b06e473d45c8dc182613c0baf505b575e0d00c41ec1d49549df67549e69bae10364f8c592714b8306f1ef78cdb463ac7436e65cd1fb136958521b63998e1570ac2bbad4dde9aa353ae620161e375b8384a8860d9bf8b4a9ca4c5dd7f54e363081378cea33970eb4ed3435db7d0247ce7f43f78f7aa9caa49e7a903b426a62f3a675c805d186f93e6b334e7c02cd31192bf737eb91469b942a623ac6433994af0b44513562dc8a2457200176a9d3bfda8cc5817c1bd0c9440845613bcae50aaec1057e0bd86d0555fc01458f86b915c71bc9e4a16b9f31e6540889e23f7fd6ad4c0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"94af48172e3f26bc3f8a1675512202d00a5db9659a4ac2843bc85adbf4e8c233","proof":"14fff95ab47758ac478b61aa6f79ce0d57467dbccad4604b0195cc53c2fa4142323cc25b193e9c2077a460585eb628f2ca096a548b0471c83de7ec6b56985b0abc6952fa3056073f24f8c63eeb02627a602db4db84feb32829981c5ae403d13ef0b563851e6b4382e4dd53d489b74454c07fb43d2886d85c91545a3b407ef91cae35679b2a14c8b87703f927831ac63a57baedf7db36ddd7dc1de8f599c3860ef5c7f362a5c4cfa3db479f749694972249cdf7c9a967c63151684f50f939bb0bda1b5b4045720de68955b2762048189c427923b051228c5fb8940966f8d97b01ca2310b4f5f4881dd75af2437af2c1611d7efd8f430360cd36f43622d7254d157488c975a2934cf9cb11cd0ab13fdd14375388800edf3d149ed2487d58b43878dedbcaa4bd00ce61aedfccae40f19bb6a09f71e4f4feacf7f9a618b43f454d3d3015bfc19a3e75ee622877d0cd959c3e86c4d445da597880c0f2aae54b0fb34eee16809ff6b9f372220379371a65b03dd41a2559273b5b25d20f3ae8d01e2c2e3c08de7712d5ee57da8346f00ce176c785d86818d5937d6484d32b9f6a29e844e649704759a0628357a6bdb4abc62e598c482f50f6f4494768160dc26690691b405b74ac5fd81e701e43a378b5291a1ee0014910aa957fd4adac61464e7abb1126691a9d6e8d0db83f8ee53650eacfedab511de10234dfa9f85013453cfb534f8e60cab94e731164d42b43bd1b048639ba295163ab32683ce1e6e6d0d05b9f1552e421ac86e37541e2612513e1088ff33b63617ac7d73b5ebaf95b3a496d967a3c99de875c0592dd053e4585512164508564f4cf3e0f18ceafd5812f8ce8a762f75a0a28e128e02a3e1fe0aa936b99ad5d5c6f08e3dbb8f0c6fa3ca145b49c0277187f1ecb2faec6e3e4120bd00cee738825d205319d428a9facbda11609a901"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"925776c6eb8cd1a3aa6715b8fabaf3e1cef63896befe4d03f01b1197206df065","proof":"bc180e4be2bbe37188722420cc28009ed2df781b586a5a02261fa4cd134ffd4d2ab222250c6c48c836669131573ec4e5db2272354c462028619d58ae683ee33ac84ec6bf2eeae6199692d48f4e2ed4a134c67668239406d9c624c5a78e51b57d96cc33fd348471c384cb42bd8527e61c410cf4110330666a0b2c899776a8a9664b80797ba41ff3b57138c10f8b890b1606414e36a497c3b62be1826f1b454f08cf519c43b0aacba43d372555e46b9a93060de9192fdc00614840178ef2af4207b0ae18d84c8bfd53cff2cb80026d6407af099ddf9bdf383518927dec1fb77a0258ba690413f5481f75cd905a6d408f5893d4d6e3a63e731a5546b58deb35204b86d571cd5a7a56b5d1cf7a8ae859be9fd46c604660098dc75c35f5c0792d4a1f6ec833812bd0278d5c3d1caccdcd853e1869c714148cd0578b21b2ec0b4042667e2adaa378a3af6825d5225d0b0e9918a027aab0b4dce958fa804a8d18abcd38ea9303b8e7787654662a6f4230386c2501c56987c4cda0a13c55a238029c584ecee90f717ab0f7b307132d141af6e444a1838dff9e8f23370d8345774e68de1806819ca3eb66913f99d50f46486c20ed67d440c48054ff93ee0f8de9c87f0168beb42ca85e8528980a2e4b4327247e820a89dff49636185274075c9d9a8fb7376e9ed459f8f3392f3bdd364b49e2d1c3f2bdfebbafaef090c23b7c8f7e6a9c6954b42a2fe59c386c5866776da093b21c63f509ccb87397a7025b01f25401b355ca427e57ded32b8fe63d531a41e82feefff5017a6437de78909db77a5713665520acc50e7f9b2128fb270928ba27889d36ddae03f7e39a2fe6aff551c943990d22e62b0bd603a8a5fbe027caf13e231e0f8fc525876ff16bcbc38451e7c6fd0aad5431e9bfcf1e008d56b52136800128db7df60bdf2fd8cbc3f49847850cd20c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"90ebd99a155182c3c264dd00620d47e74f51adad1340e7ce675f19483058bb0c","proof":"a450342e0aa6ba4bf422eda5e42839f2974eb39fdabdff3bc2474bcfe6f10936a886b66ff09e39724cabb6c0774fd45321f93abdef41dc542a033403f1a4926f5c600f442f6a4f82ea40517377a8d8a57c8147dda06d776e7c06542eb870d9420439d8535b727742988beb1d2675a9cb97750e8030b092aa557dac7e09d11b28d3ecba084bb0abe68230a6cc8ca76fae394ce59db0601facd48f593c726e6d011d7c8f2dce4d559ca90e40770aadda3f54f417daa1b9e22f36b19f607b95020949af0f80441872c2c9093b94df0faad874a342282f100e8a94c2bfb26670d50434d5d44fa4f797be64f849620e23c2c8fd4cdeb1d5275beacec7a1dce89d7659eaa1a05c47428831f83510b37e1a7e9a596c91a1094a2f6159046f0ac6371a29dad4bcfcd989f0cf69ffbe915b6089340c99bf4650809b389b2d36d814232309de5de1c26825bc251db419afe166b70197a035c93d8c4c353d4121586572db41dec8e23afb4f79f2cda663de7b5196d55f8a35dd20b010728d6dfbfa8d09276e1c360e1e7e429ebfd28c2979eda414aeb7d1f9a39136c350001444362b375c54f80cf2330437142898c2f6e8ab3ebbed9f5fbe42a68b863597420967a111653cc871cdd44e59cfcada105f854fd39fcdaa25eeb0ce3f0ff92dff6ce790d3e0043037079a4e5027e6b48fd831807ac7a485001e82fa76b044ce7fbb8d4b391814a8125907341d46292f2eefc2163fa0952e749d923ac77d6576e458bca2b0ed1612c6e0f6033e4d1fa8f09efca231da07222af1a1f4ed8e5a3e2dc1da45a95110ee77cb56b5e2b36255c951ba65923419fc9d1380d388ce1e9d58ba2d921718544f4322d1f39cbae16add4acf5bd6b2496de8165708a4cf697579fe7b4e7833056a92c659ed03e5de2f33c7b97a95b7b56f98f626a35f1570501339a7cfcd2d00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5a02b85efe538c8b8ac0a5e1f73fbbe7bb1cfa801405c9b840d98834cde63f76","proof":"32392af2643a23aa4c2dee5b7249844d2d6a8ed070a28d3c98fa5edeb5365d72923688ad9258c930d09238297c88e8513d06e6fd24a4499023d27b2cc7e3442962d7a93a339034d535429534c28f5003da1e82dfef947eec2d6a238f460187648670b86495ed1a2883876da47a390f6ff0fc87a3e2f0ff5aa73054dca33922070ff717d01727b8d61757054bfce9c51fd7b4d79c34f97919b66174ee66861b0ad744918de1d8e6ea16592ed3efde3db360860a63b301b362dbf59f298bb9f4095d69a0561ca669c30ee615aee3b8a8d7e4846dc45ba567512661742eaa52d60ac0c5759aa4add2f02b585c38e90697182a05523bde0c0366e08601caacb27c5b301f31706ea0131874339b0051cf27e0df4d1ba1a4469bb5bdd55d2a5abe68121a805fbe2e64b420a62b368a4974e19a01147b4f0a256491943caa958c7d14116a5c207078ce85bab1a1d05e889f7d0e1123d571fc78b187bcd0c065b0ec8d5e4e90c4c89a88f7231c01cc9b4e6a5177a2931571c8c7a9973a8dd5746a639b5220db2c35d6810a7c660bfeed2ef927c1ba98f2b703a4028e486e3cfbfdc28a2f049fbf0a6a12950e98cea409a2961ac54a0c7b262b64d69c65d891517e59431bb840746c27004be366a42ee58f741bcd8a1de07f703f47c14f90623562e06f28d48b0ec255e55ff51dc1bb75211654cd980b6aa1819eb3bab3908d1b661d327be0e925784b8a8218db380944c37d2a00eb0816442cc3e1de7117d18f4f869c68c0ca367a97ae93df9cdc8ae6e2104ee29b9e7b21e230a7e50f2ca807de5cc55d10346e1905674dbf85c996d1b2d6958c52d89bea0f9c112dd7d6055a64b95f1dda0cf751455def41f67c4cac39baf8d22ac541964572de542d87c86a880d9c0b28a792cb6c354ea3f210611a81bbf8132e2ac976061d855ca3ae657c24913706"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"10554e5f21b6ee735e6252d64a06ad6676c617415195d4251a7a1991e0d58c56","proof":"dc532b93c44217305a9247aad67d3f2242eba26047f536e42efb66acfc218007c09df8cfaa69eca4da5859e31e042431fb4612567a568081f23d7e293ab55d5bb0bb6bf768d0e901c961c49eb8c7784725b4579c00c2196b6529ef31d8a8f12384cd8ceee6e47fba61fd6ed0ece0ecec370cb15d1dc2fa2e6a690cb92ae461213fb1f4b5061d0e4e045d26562e91f2c3e16c8cd02a976e57d733017fd0d9d4059d8eab054c5ed057136f95a218b0a00e07a32bbcaf339641b035b67c3ecd8701f1b9b79c827f652510ad9d27055276e7ca2d44d73bf5ae6d0911e09db4394d09b6ef024fc6447f6b6101239ef4d73623a60436337be2f9b4c700109a96db2d399a362ecc271da1596bf5dc4bc758d8accc9b6f6971f001025587ddee16e2ae5f90e43c4a794529a402fe8cf483a24a76d5269044aeef642c3a6e53d5fe52ff30c8391d5d36c115348818194941e5e406b16dc56ee19fb50abb555d6931197303449f9770eac651d5bdff58b31ff2c24f97303908bd0f0e8a60fd9ae84fafd73d8056404655a1a5e2dee4af8739951fbcf804ebdb666d555caa78bcdc73e489000cdb65be15af0163107a76aa2de67c5545dafbd43b870306f3c850254788571f6606c3e339462257bb72bb53e51b628e15e7b00174c458e87b79245aa041a640fc3ca865909bd77327cef63f9390ca9869a937b9851cb5f57b938bac839bcf164e031a1391cf851ce96052febdbe9b0e20fe19786b7d369acc77185cb304824d4cb2be74bb141e82c5f2d54e5de029a461d4cbd0edce8f34f3041df8ff20c256f0847ef19e554a2bf099ebf897b47513fd346ba629d5a6cd849772fd010f054d22dcad2ef3fad0881272b1d3c106fd6e4fd87ce53e2bdb5e1ac4c716d1d36a04d5f50810fe18a41357a7a853ad05d651814ecbcb17e46cf5826edbe87a67200e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ba8e85ec361124987c485467af28374cc9a12fce4aaa3cee0b52897da9b20a3a","proof":"8edd93dae16a55fe07a8f6079ae8de05f571f93e5b94949165188b846a83ef2028bea17041fca1e2d1870aa5f83a7f6b9e5d72bcf9188c660cb78bc74625002ac61e9dd1b251895b4fb20ee7d44c018e012aa99c3c919166bd15699b0ef05c3ac454f5e4e9f829d464cb6d8178e59b66ee75ef472b93690f451239895e8f3c60a889f5a8ceba2936eb964702ba8ef960026aac2d1d7e324d56d3d2b8ea9e0e061982da2d6d1d084a9bb48ac1a5dd2eb5e50f61c95c8c7aca37da866c2677290796c31e7c0de733a7710000b7434ec66cb83cd047bc16702f518bd2c84aa3a3044ec6b2c923e77c1cb9b7ea55cfa4b24a6bff9604a2b0a29574dd9ee0ee305977e465ee1a1bcce31094b0c2ee0d8ec26213a39d87c17e3bbb3bf4124a7ac7107a32f75f3112db9667c3547cb6dc7fcd9e2eb8240cbd53941a372030cb678fe4455ac98d1238b6015e7f3feaf9ee9326142567c710ac29cde058c3f6dd6e32116136d9ea79137360eb550a156f966507c3a49cdd07cf87cd562a50c60e500cf80b5c3edab8252c848ec62926fb55a864704ec781375e542152e0b2bd0cc8bf30045cb671d4c8b72c3aa8149eddd4340b26448839127c017a651d8baa12d1084d19a8a3d50439719492113ce53e5ed2021bf851f82bb9e1bb2204313fc3d1ea5234fa6e98c56a37ebc2985efe55c4f3286cff9576c1ba602fe6cdbd50ab34e1d63f161fd8883705e06553a6a6987e65c2d57b6d3a4a0e5c5d6178cb211fe2b7bd338e6717ad0c2a992d26b527deacb141e6163d58bb12045a5f8a3173a366705d762eff0496549b9eb96942f8c10d708eae97d836f88f3a953a76a25274a39fcf282f107786e1feecb1d59332d4a52305577c6dbd0d61d507010f4a3d96d15cfb0393ae71ca3a95b31b0cd799c293a68318e5927b53e1357e2e97cca8f316ac3f0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8ce7471df4066e3489dde8b56f4270f653ad0bae2978d3187ad085b1c861c029","proof":"a620d3781f3d273e7e999a88d5a8996d3ee813c26d2f788f524be9b910f3e35806764c0b15f897cd9bd774297b3491b27210ef5ecaa8fc325eaac6265e18794cb449a8e2c1cedbe4ecb226a8cefca3ce92e2e8de781b4ef2bf22a192fcf3264574825368e129570454460079a208edc26b5c88d46bdefab520ebcae2a696e3730d738ec78bc2f2d791e6db89c5a8f65ab73662e736eccf988dbe678efc608c073a3caf41742f32abd0b1b23faf851ca4c2660fd3ebbac0c70542d476d1750f05489c89c62563bd16e982f34d5de89597565b9ab4043fe8934394b0d4b9b40e0a52a5eb4635690d7fce087496c19e34f93229b37218e8830774bfa92c2d954f4a7c94abe028c505c5771b5841ab18e31d19eab5ea0e0391aebf1340ef99ec111a7a25e20a858753107ae33711e98e2ed269b1a790181b2594006561bee954c9765a747d59c1f61649d32c5dd28df0e8bde1e38011ce312a2f6ba908af40b7ff7e5ccd810d1e648f749b0b0791f9d16a2f8b565b1d7775f58f8c33be8b33284d6cc2c104d4ac2b5dc58b3ff4d9e271db9a5d2307522bfb4bdcb34f0d33564d28793c40d43ca001553f585065a1e51b1cf95b28c445b7338505bb8ef79bd56010064a1ec64305a6571a7708dba346cd957d56d309141b0f3e180c16b32e31c31f3b10d34cb3be39d4318da9310d0074b32a000b964c939c73fb0cabb0bbc8755107c42f144e2b19f5b2a248cbb32512276a46bcc6739e4b77209b5c40b757f8143ed0d8217a20cf43167b994c84d3dd15953e2e65310d40829098e1f1c477d0e616aad4b4d29ad8d97aca5b57ed6468e7959d7b9dbf71de9c963d3eed367beeb210f1cc6682e451b5efba743256e0c91f0aeeacd81e60b58fdf9720efa82920ed0e339ca273f634f5d438d57419c9bf1fe5a76af8e114d8b813dbfee2bd5401710f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"78c848b0565068e065e5211211c0e1d779397d05d74edd133ae3a5fbca71f026","proof":"2625ec7b29eb62e50031f0c268df4c820547e1c524e8a208e2554b7314cda46604ea53e022f607d5b0b7c23f01684ac00b10d5d4ab618008f156d338f0e232735c8e23bb59ffeccd9a477a36cdd10e4ebc520f06e5071f3a8685e55c307b132c48be170e123b39b2f4ee095f98eb0820354b7cfb73711653a9b61c73f3612a4fd2d7a687a4c5afbcf28a854501466abbc9a6bbcfa634c06acfab5f05ab63d5081eb5c2fab99dd4439f530f38b11946b9c8992d9f0d8e1e3bbd00c31851525800b6bc1845d25c78cae4232dcf8c44e609d0451032395d811b56afba5b420bd604125f845b9bf00b21d7163b32c6b5756f58a9ee3a28e84e855dd3821d549afa4ace3ee73180d6ad4ab2ef756c785f80db5c2058a7f77498401dbf61f0f87ee371826edef0f82ae7304291b7e3f7a40b472be9ca74b389c2a3586b9b7e7387cd3ba64f975d48040eabccc56a2f6a0a1701da2b6721a4ef267d965d67db3bf53e2dac0f497ffd991443d93125ae7340a0b4c3dfd481e6df0a5b642098aaccc9bd1692d43aaa66c24c36187eef4d18b0f476abbf569ad16c78bd95c643ea06b7be1ed8256340bfa69af1fc6ef1ac31f3e9630767103b041762df898e87011b47492d14605e85c7ac91dd1ba5ddb8dc3b5ba2e30c8ae4fd2ca62f193d5136e7de6b124e0e4a158378b6a971ad2abd324e7780c59dc8af6ce986e4df4fe0f539230579e2963d932c8123effcd1c54c8bfc41e27e236c157b428c4534c0bf1761c3bc5d96e2153abf100670e784d5c9b8163c8e7566e2bdd932a1b905fc0125dceb752ebeb3c8705f142da646893612536e9a559837a9c741d93e3af6f0502aa4651d2acb3207565b1d466ec3111df61dc6044dc569023c9aafac6a529b1111a5613d0245c15a8275c8f640d4666942097aa32aaa9566121c60451c2839ef2153fa690f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0eff8b2001b592d2223b36ac66923703ec2a3fa4bdf621b8a4956e73d9825e00","proof":"8efd14d54d02c2046db2a92a65c3f17821684fd2ad3c669a96c494b345f9dc13f657fb0c86e3fe0d639bbf08a4b7894831e552b9eb2d364521b59926a545364c6e865bd92ef9e88491b10fef3b6bd9e23add78b3f9f04107b0a210ce328a76413e5e87fc71d40b3a7534e30bd397a3a9ef9ad974f6905fc5b423c59c812eda7ec4fefec9936daebd5aadae5e4f7271c15739d417c48dcd39ea971756ec8b0c024af743cfbf7941019d7927838cd80d36aa6b0f0b06cc7399add214a4ca0115018040992c2a68a360e4df1c8df05102cf69b129e00bc560640cb9eb0418008208589e85344da48a389312debc022157c2c5664aae6e33c404d9204cf823b9a917b48a583b187d2445db3e11c860d5c762b1c8de04de3cdabebe231e462c34485462292efadf92bb4374b4c8d2a102f002aaa827772f81fb624c0c130cfbf7b87f3adad06abc624d2a8f675677a6d3163662ffcaf38f1f9581d0d243339aecae755e3d5e428289cad62a18599ba2fe21aef716230396bc58119145c998f0ef6e0572ea88bdb614526fb9a10cdcc9f0fdf954f9cb5fa24bfe37b93cca4d2699d326121f20aae42f52bad09a226f6dcb4d21b4ffd41f024472626c6b65f743114925ba3df317467c07f405890fb663b2a7667a0f77461fdadb0395a30753f52c994ff67ef28fd8dded134a96190c01fd7ea104fcc84e9a3a31f4db384a4f0d3141130a301a6e302a4153cc7c73ed6ee4ced5f43545fcd146a9d49688fa37edd026757a3a5a11495ed38699711fe8d789c253b2796e65705293fbc3a18963c466b94a7a415c6ed1df069c964fda38791fc321cfbca31f6e7ec528260f18e1f9f4626d9d47d618a34b24694a881202b5c8a04160d227888e640091f7eb59b527d5890bf4adbcfbbcd016eb716dfd9f7cb54c9c506a6fd682674218759bf9c1aec5a703"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6a32d74c511a56c79ef94e5e833b4d935a6c4da709c2f2d800068c740296465f","proof":"f6cb51a8e7066872cec97a9ca6a97f12d2a702e9a2d6eb70e87f0c874768916c465b5c0b3702844c13cd2e1fca768356ec33b34b5019e47c14c4574f6c2914116000d0e584a692b22d7204cb0812ed202f8a9a51714f41ecc2f02bcaa2ec37334ae6b86f41aad4a32e99f36e22036fd8ab1c5848952ef8e70c4c0ce781850a53434a650f03dd0e6a1f042fd58a58266b8096c2a7c478f15ef446ab2989b5dc0d9f15c603476af9a551e323d7ec793a94003f918429abc8929e995d3f6145ca002e346e1592179fcdb6a73b55009736a651e7aa55646adfc8c1d19ada753d5106248d282c3cc5d6e0aac0e138afdb2b4d4c6c46707a5cbb629a04a2bcbb80ce149081ca6a81db921171b19817b6611a7b24bbc513973ba6b8b177d0517c7d2159cebbc6fb1f14dff0e08de0d644c00e04277537a11da506894751b889f4d24c32805bd680eb0918c9890dec5525708c151784a5c09b1098682aa34bb2633a127db467e33a9d67618e2df8a3ab681eb8dacc66ad190e99f22095d590134a2d9a6b70ceb3915595e139f626c7fb70f1f0a77cde606876df88c08ada4a1b19925d61eaf5ddaae83c70c1d86701668e9678989bae3ffacfc5bcf4ca849f3c3f00596678f735031403550ff5c2f40a6f6d34fc30b57fecf3805e5228093ce2e9d7cf5de0575c2a474bdf24cde72e6a60cd45b68a4559dc1ee5cd2cd166b5af838c68252c6c9f61ee405d3c086f81dcc300f87fdc39b806d54bddd705865bba6c1ea15118cbcce83136de631897c119b13f958c0dc65c0e54890709bbf3011911aab67b02665ecd55272568f963344f897d4c701aed0436fb58b956065d9f5b60c91d40ca9af9d2f15cba90bb6dc0f59804f2e80a16e59c53071f70f0540a5ee2aa4a0030ffe748de969864298b36ccc7555c13f4114238e93318d7266903ba49f3c505"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0e63cb23f3769b5b96a6699aeefa703a14e4158b705f3159666127dec02aee31","proof":"e0cf80c34ecfec7ee02883a778bd4a5a23c50394de7e28e705fd90efb698963e5e9d891d0f948c6956fe166cbe06bce2de5d44de1bda3de0390e67652d11ba316e664becaaefb8875a107113a104b4027059044dfa181b3f1f047bc2fc8fb6338cb3b49f7f25eb8384c08a6bbc85be559c39ab200f51e9b99755ca4bda35cb059e7d4167cdb4eb8e012f535432563ca6a05e90a013007927852290ef0f625001c659ac425cae9d7f11841c86fb36bd8c736a8419274586e0fac47ea0f657e208243bf8d0f47984de48c37cb1da8d94100118ec71ec27708b7c8871215482a20052c1cd5e67e8087e222fb6a589a20458586548f46bd58007f9f0fc4084b31f277cc66317f2dda6ca74325e12531da8879f92304fb3b17f26b61c4aeb2b74a95c02db2a782bfab3bf9f34eb7b4f2c2078ee174bc9385e58c25d15ad5f564f40658eb36d39a51f19e82ab30b526a26cb1ae18e912a2f85078c8243f40a217371393866b5244a58b82a7e64e33b79883674473b49941996ac9c890a15af18ad35734ccbcca1370fa6239bbcfd1d4d23790452769c6d769b215c483020ec1cc05638d005251ba43d088badc26f57d7477ef997c9886888cbd55514bdafa811a6cf1b9a60612ccb52eb67afa2acf05b45a3404b9fc62b64e9566f0f5b4951829baa52fcc3436681ef45790d9e05b0116d50d840891d69e6509f2d4fd18c415954066a7c99f5a4acf818ce12b2a3bf35fcb62ca37cfbc93793504e95dcf9c6ed6c04226eecf9d686380cbaa2f8b0d34933500b17008b87688ef0db1c5f7936291e84513eae0f6d5aaf8ef009d9f8c82bf96b71c43ff3bd9802428d93f105249ff9863610ad1c3badb288f00521a8be815aeb02e75dd45b7696a5ddea3cf2ed1767b00fcaded223248111cba07c882ab686f96f8eef3c4a4047a412eaeff65b9e8b1701"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b6a01012849663c37ce19715106a86311febc4d178b8ac1574af6281a4be5415","proof":"d64496bf0efd78d023653bb4ba382ef118b597c660b1e43e4ed34a438f907950bea80013e772cbe54374f991509ffe493fd16e54f71d4c68bbfbc959e1d50a799e0104d0b887e58dbbe0e5321dcdfaaee654f2673c97d562f6d45aa78a36d11348cbcd63891a71881dddabef1d85865a0a9a8914c63438697af37246b45e61705070a1df11c698f2f41f3204ffa8aee702049932c60b4cc22ebd7d805d076f0a77460732fea97a345a591720b984eb9bbd2064783911ecfbe1bcdab6985cce0e3140c19f21c01eacff280414dd50c61714a4cf7941ec38461236420a0bd4c40836b6dce5ff1fed6380f4451bd49d7d6e4172b9ad05c82c4ab6bbe0dd3040210e2aa97e179c412266e1826a187a60c934d2d8e92cc0cc2bdc55d33422c45293091e0fea8209d63db303fa62ebe261a628b8385e7a1629cb1559fda6341d4aec04d889d9d6868a7c2559d5615d603598651bd93771c70c32635d2ce48ee2c96c11daa0e265b557d7883033c52dd9d251595f69b4d7b43463d2b1d28a7427b0e90e787e3c32c8175bc09d441f6463f6d608cdf38463714add3aba40e51576b3287c7c8553e9f301e7a88ba2095f36f8e9f5074a87c299098d1d5bef7a62b3fc0650aca47caa2838b9c51f39bc4faee818b99e05119b9fd5f9a1072c561b4a23c848445b67e49d89c44ee679d941c1b01b791b019e5a98b04326712c07b0deccb6227a76046de58858e6ba418a2eace93d731c35bfc54c9ecea42882c97929cbaf7aa827e40489465276659a1adca621c35a4a882f28dfb0aeeebd0840c98098413c4e12e94ae30205f34f116c72df5a9fc8d53edfc511b30d2fe5822e8503c47a16279a51738e66c43d725a1c3ede937c5fb6dd2f5a7ba6a829aa905ea9ce871c0b2938f88bac73ca58677ff7d371df1fd5c18edf7f5449be2bff3c0710c3baf90f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"26d9a27d5b320e7a2b7d71103acfb97dfe8383e1fe592b98ace89df53c71541f","proof":"c6777b4a117122046a66af2486a3af48afc9c7341ef74913f6a4601cce879e2f049f15df6537a57cfabbf77adfee39cb9e3b20a0a0ebab673e9c1db2c895a33dca44fbacf7685c1cf9d7cea5c13d7af4f37ba4c3b6d6ffd172cecf12f14bbb7450906ee2448bebcd07088ac79dbef50b98979702374c67c1c50ff2d041f3413c7fe6406500da213fc429f896c17973a3e2021f7a288313d3b6658e7adf4fea0920c5f2b3008e154697620c89f5db4088fbf5ce6f15103c79a338085e533f7509f9b314517cd88f9905064b198a17336d9ddcc42d537dfbe504a2565c514a310dbab593f67977505b272f8fb85d6987dc953aebaf6ca6015b3b4e0983589e4c55a8b34619e4108cc19c120600e36160246439f97d662662b2a5cca8a0c1f8426d4e7fdbbda4a0e244ebfae18dfebc5ddbc5f99478fa56f24b5cf61a7740711e419e1b476c7259366df1398ce772e54819c641d0740c55e220be7df1af4727b85cacf924c8913f631b8141e058de02f15c35c8dbbf017c849ba628c1fb636cbe42047e154ee57e4b098671f61fa52835b839024a65e2495332604be231db0e04368e1b8831474ab2bb1bb6da7581355752b9128e106c2219cb9dc088edf7b485217e65ad3c6cd9f2691fa77aa55c37f5b19e7a44887d7b88ef6e48001adb3116795a66e306a2ee26deaf1efecf4a3bbdc057e8eb60ae8438bc549fe0fa72a4d05df8e029453c15d547ae0353241fecfc4b22de26ed0f1c89ef8e0dbe33c9e7353cba59b5df2c34c5f9f91391ce24c6d755b287bd6431d7901aea590e1a8097435d30194f57b72103477e60ee59ef30cc2c1a9c9e116e1bf6ade9acd8b6be3c62223704178ae00282863d1bb6f917fb8b76cc453aea2121083e8506c875691c470cb09f943a08378b67093cbbedab35b70e369f1523931e319d6ed3a958d238fb08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0c1c921794e2fea024147a17f8ecf200026b47cd23bbd27d36b7c703f4060839","proof":"884aa53011b61857018255880ad33370805622bc2a36844638bd7b29f723b4146638e10adbf87a75ae8a57a6e60df18787e118098d85b6110371f956e13111642c8a382eef52485c3653596f05a033659919a86f923a30cb7d91109b01db4d54f031ee7790c929cc5a0c0b4c1afa7dbb3b2825720b65eb301acbf0f1e3f1c45b01683b00c410997f04ed9702824dc150d153963c3a436fc3cbc8f4eb791d2b028f1d002d5dee4a88461124e9af0123cb0722caa1bcb1d90442b36de7a7f99706c903500762a377df359b0e720ecd9edc1e2ad1acb92dc34bbeabda857af5ed0d829285a48c5b6ce257af6b2473299081f7d39f5ea7977b3428aaecfa9e8f1346acea59477a661f91c13c1482f1539d9ac28f39a580182d67d17e92c9cfe9951e560a61df29c76616f02ad3460a639f984b20d3052d648237b0f3f37c8fff5b4faabb6cb7a67ffa5bb7aa903c09d674e81b24b3a1f984f3b81fe5cfabbaca06101e380112ba79f8b1d9d088fdbcc37f4be4f2fd9f0001ae900d8edfb1d481973ca0e850fe57c84824e3529bd5029ef317e462b77d03cee607f3987e308c98aa2924919bd04e0b7aa1742633a9c399da07d1724b72aba149593bb08d3c955ae7193c25c0a1a157082bb48a660497f5c2dc72e94e0a343d7ba0a11e0095c0433d128cdd8b858138e64ee1c0b6e204dd476ce21acb08d07b57b3c02c5b5247fd6607c8927ca2d406d79e22108feca375457392c3e2bb0975eef77346846ec6751b7392fbc38135bddfd665e5c3bfed96f236711f4cb763d90655a74961b1a256842abaecccd92448a5744ff7fb0b68cccf535c924c9704151c2b8efb5aa7d6cb454e57b9d0b02742f47fe97fac473d244287d817922e4f5bcb3e9a8127cb546e66072b54070822dfe305e0dde25440fbfe6bd4a5d21d4b23d9a1b914b2c0fbeb9a0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"98fc662290913ebbc9cbd8c6593ba8a3d136080e4d29a9d6ee24e67179586105","proof":"367685bdd394c6f99fdcd7ddf0cd62f1d8a2002b6451d91501d3bf6ebc485c720e29e9f9243690587c1f6079ec7b12321212c424e5ded639a226db93ab051d3cbe0a883589b085f90b1277a44e995ecd0198780a79553ecf2a11ee3f746255771cff0253efebd18a78a0cf65012f44335a0d63903e3f027f6b03b8fd9dd4a72792530219b848bc0b0780c281c4c165cc52e9e64504671849371658266a901108163a09d95a70deec16d2333e7bda5a24918178f399e0ab893acefc695aa1d105328b7b08407e4d2dd2c45f6011ada7b6d3451bf1d76df4e186e21cf8965f490178bfb17e8b8c30d1c3487fc4ba4cda2ef47cc567a9fdbb05bb011c9e292b465c348ce7eb514d6134880b91e25c57cc25db47217932f2c905ba23811c6983447942d18956ac7f9453416fd8f32cc8397d317b79c34fd75ee64cccacc75cc0676eaaad960a0834077f1d272df4e8b0d1d8a394623dec9b8029c2eb290df7218c7fa0ee539e5dba03efea19620d7439238538119b9defe882a98b9cb8e29d3f127920450f3fa2af80fa8cd4bd99b82c524a7bf83fa3f1e3d2abe8ad5983714e2d6fc8ca8aff215f67e3e6d9f7fb532e6fc325beac5c14398ce01b1c1a815912a038a4318331aef0201bbac7fe877c6a977896f107e1d1efe094a50a0486610b91784a72d4f4a73cfcfc5666c663f0fcf6dcfa70b57d4161ba77b4efa0194dc24c1846b6f458a6c156638303b9272e6596d6019ed716baebad1707b1b59a8a226f517a31a1332fe5e0b3e934636415286b4ea7a53c826e6a373defe11829a1826a2fd2069671932e3b5805215a698923eb0a0a6daef3b758263067028881812ea3013e1a9e99054941ea4cef6577a71c2c7fc8fd9a6c9756eea54344330bdd8b77020aee52a4fda3a3094d008d4e7e0f50939774608cb88c70f0b49db52cd012cf0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4cc503692f734fd802cce4642132b098488e38de6656554ff3cb35e45e431e1f","proof":"062920911e97fa9ebfef7271358ca66dd5dad6c27cdab2e8ab0d442e2788c55070aa8ad7c33a5cd1f364cfd702900ab9318c0e320a01ebd3010cda2f7aae4654c2596803ed2e7ed5e4cb7c67e4f05ffa3e669085a33e5ed6e5ab82f3f9d4ce67dacd10df0879a744ffcb231e4f300cbfa63bc83743026de75bdadbe4a3ae0625a6827ca582a2eb80a02a4741c2e18964da81d7d65f1380cc0d6dfedceb155b0764db96f8ca3c58b09f8791d5317309dbb2c32f3665d445ad251030965e56cc09e1ef8580a67067b3e270f1012580a0a944542edc89f62fd325e6b52294005803f65fc90860c86dc3eec134fce07b481a78a34e56bfde52e42c3d8988f4668c381c3bbe20c3ca7426f508f7420e2b8e56961fbaaec10e9ff07b3ad6fdc42e786716a05652074ffa1d5fbf3efcf7990f85c35d7e27c54114d33529cff6b87107516a4ccf2e3cfd1a6048ae45b1f90f440ce76cb0e19ef0ef4672082823af2fcd2a2c9ab3383387870eb25be23dc34bf7252b52f8dcb76432fdc386e0f79c3f4d156efaba86ffcb0a5eca2aa01d29e92770d55f36cb586b0cd6de6b0d18e1a7c15ce0ce26ff8db9c803c4c499acf8d91a8496771d2db7220f9bf4f3fa6863f35c5fc61ae780365ae937809411b4041d3a7795743a31716e5cae689f3c1e8c561a2682d6340142a28f53ad06ab033d567133acad9d4eda57cd2798ecf204543f0752a464b5075c9b1074b444a5bd15a90c2e55f2ae1a1cf144e91c92d09d22a3c25f44a007103693651095e02113c8ed6a5e3e5bf22783b694ff2fed57278a9b225154f84f4a785b5130d5d4cdba61594393d96a0393c2ae4f53783af87d977dae189db1c83b43a8adaa4e162639e7fc63a17539263eda6d16400e4468e146fd48016e8d9392cc839211d397a9e9cbea5b84a915fc2677ad13ea1db97ed84dc5010e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"360b39fc7e321b1ae50c95011e3bd036e2eaccb71ced460a82c7e4ee508d630c","proof":"da7b790e7f0a1df44ac615df49928c9551cf9a2c9b50e3bfdb65d502ecc4b34a0cc3c97f7f65294dd53f1b0c2441198e9a309ac06ef2b1ce67e3c5627099711cd23831ec30c5e6804314e8a55848bb13d351a85f4e2d0ac8ef07aaef02a0fd0a6e2d383fb1f61a04f16c07a467ad0ed30ac84272895280a58b8956e972978828cb51eb510f83246587e5af10ce8fff2ca144e2a03e865eb0649de080ba26ca0b552b782d8ce1f7846f4f02b98538e9c94b5cdabceabc857ec1c7ab7bd507f7021e01ed8b3a757af83857dd428e2e120ceeeadb49b3f1981fc5d7672ae2a0120e929670dea135b1d74e590b830f5143bd38a347812be19e7bdafa3c8757d7b17c6091c4b9fe51854f33fa579ac99b78c962e37e693b86fca749c6e2a69c966f11e66940b2856c4b9e3cd56f810b237ca6cc8ebd57621914c66786c92587636a4624b1c951eaa04065dd2c7b4f072412649c7d152127dd5b8a93835313b46204235086d905e82e3b596dafa69e9b86d2da048576392b7dc8580a1070c002b5044442cb960ef170c158e867a996e122333aa1b598c4b467ba0d8c48903cc044fa5704f0d1632e02a271a4f48a9468f95f8a4b830f2952b0e8ee1e952519205a837f30d2ccf732cc0a5e6a9ba9a20b04ca867c733729a07c1406df718d54be7c1d5f22a9b2984df6850ab589817bdd49fa641e8ed1e46bc3c1f32312d0fb93cd581c8a2e23909fd145a5aba130c7e7175632a7f574cb90f7c6a57d8c29756f231c6a2e91dcc857dd2df0dbdebcd1fc505c1dda5db7d9f173701c980cbb6022f9382f26dca928eb45480dd5b16862bff3bd4cb8350567b3a9e5a193c527acb8014655539279660416978ad1e27153b7f53a666bafedc22d8661765f7a35982c999a02aa3e10319109babd54f3b6565138f50b1f974a7c9d8ba29fa0165f819dc03e0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bee402df5f6754205a9a1a1db0d8baec79fb623a062772f0f475212979486165","proof":"3c32f699acf76edbbdcc11fac6f36c0f29ae1dac37faa07f2f52105e1ecb9e4376566379f6c458c5c2227e11ac4c37c562a087df54f305962986802ab91bf86b4ecd370afe8375c38a231e5ad7151676100447c75c87226c3305332baf93613eb0f426a2157cb5600820d4317beff85fb9bed18bad49b8e3d8e26615c56b4824a7091faca612af22061f684cb2e14719660499c3f14c067eb86b11030e63c5090b4ae0a8fcd4b79cd72563b3ec6da2ca5f232b9df9324a38d4a55c394c6e170bb92bdfbc3685feb052280deb8425e57a4e091d1f10fa96f38fb8adc6526a640dd62c290d240feb2882f1676ce32115c7eb4a7964d93010f850514288bb5be4223e2275c87e736142cad373d98e2a77cf81a9b09861a01b909dfdb26def89266d18600bcbe9de979fd495af224a8ced3d4aef3f078a2ef6bd4a1cc728b95257638c98d738309fefc9795f4f2bc91767563ac2b13985f91c7aab9a2b2921fe445a76a8101ef1588b917b99e33e2508346a335162a262e99cfbd5922d4ac489f92328aef15f401aea0d25b8c3d282055a78a312064e47c6638b57212e691b4e4300c018657f15c87f92289a976d7264fa0168bb14e49c854c410f8c2aa7e807647c12031632349a65c0d16a3f160a4e305f0d3538fad52646ff5267607ed7fdb03e46fb5f48acff3d870aa207e6def1ba47174e5db8879b18861ebddc6dbc16ab0d6e40e830f9981b2a947ebb5489552195264c62c169173b943bf808a2991723738294c895a5edb85b5c51515691870ce5ffc60dd6fd4fd80a4ebed620b6ca387d709dcc735b70d810d06ca8da20506e1eeaeeef54271aa284bf78a1ba6ab03d2e630236b3ca426d7d7623d310944c0efded80b542365776b8fdf90aca16a6ca0d89446ee5b6563a32e5fd2878dca668a01b959c26b9f74264acc3c3bc70ccca0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b2f7498350395c9819a7aa73ba9dafd4bbba376230e47ea235338f93892b9223","proof":"5025c24c29b8b5d2ca801d7e598c1ab5de226d9637b0aa477ef94fc9697c42337af8cfda26d8325e9bf4a2d53d0da10bbd56808906bbfc23013e4ed8bd4b0c358e3f413661479322b25e16726ecedb8ec36bd3a3d49106d72ed89c3b586dc03ea85b31673e64d2c49280daa2c30d46de55e9e245a0851d4dbdb2155172219e14e536e3f12ffd8175b8130a48a054ca66febe299d4024b54108fce0c273f592027af6ac56c7be5eb148a3ae11dac3a9ec4036290aa87b03c00aa8d262d2bd3006ad8a5a2cd245b5ee16556c1023867625043ab6a9c5ee2acf5c8019992676e80ea090effc9bd147f1056fdf50ca8c88223720d8759f2fb9fd4e89330684de0b55ecd0ef87a103893ce380880a331acf6dcedd8d5bb7aecb050026bec7ca0906773ad24e794eac8b47777067331df4ddec3ce1f49e26dfb486bac83b50d7479c77b4309c0942e9ae05c641df64c2063db4d92b71f0b2d92548873650c9e2e0ea09249e5b025090fa8917373c63bc4fe409324d74b2f6af96e5bb436e3194cc27304261a5cf9825068da2b54228b5f0c190a66f68ba9aea1e277f2512323bcb422736afb1b13214086938e8e1bd0605742066dee552378a1b1a0142df192e31ad2e5a08b6774502bc0ea4c8fa2b62982cc51700a5f0004e8b7cf7ca6a18a7ea76155aba4af251c42bfb0a0fa9088080a30027e25823f4997dd9be0abc0f4bab1213be7e41d19cb964dd209678b178608cb57a7fc9f8da195984cb8a231a6874e65dfeeb7d8bcf2ec3c8179c5cdf35adf3948d5450e6c2ddb8e522157787ae994869f8481f6cd0c886446dd36438e479f22b56601dd8770a053128f18f9178f4ea2e862bea7726b9ead9715d133065020f2c81ecb36e14a4b9164fdfc53f44808d09c5bf7b8926e25fde1beb0129138fbb95286e164a7920e8de0076dc247cda2f01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ee3eee2124110c6b51de3eee316e963995e09396f317341378c6444d7797131c","proof":"dcae00d2326583751630296dfc1f41110fa3f1cdefd34c8581cdcd0470a9205d6ccadb952816665b74df15e4fcc05b3ac842ccbe5572f52f7a77ce8b9da7d400e88954513d765d9814454bc84f3b5c1650eddfeccb4a7286227a74789c6df6724a9c5805671b272db7d7e7ab58c6a4f1d2f7c0b1f1d116748d646bd060024e10cf5ac9b97f3f93cf802904ff2506077e1a94c9e69957c880e6c8660cd15d8f00888019d5d477ef922c258007ef9748e88da4b56fcccc88d7c2bdc3c15e2f11086cc86f20dd11d184bc51cc88b6bc870f375f4a4596e6206d0697f50c8a361d0a2caf91cd0e83cef7d1fc7fe40376371a9c1a245e8ff6101f6e9a5a1250be803558846dea265c41094e62f11f25d25873e1d502a7083c3b2d9da846640a16e5094c9622a2efd3249e6ec9dc722805ac504a69ae43ec15cb31885d29209b28f61c561dd4990239c24d1f38ad4dd404e1ba1d81c1217b76c54062572429bbd41c34fc802393fcae8d3aab0097e9ab2071ba9baaa5d94e9f2afe3890e900833e19130eaa837d1abf24607bff2be5ed00d98b44adde919786f77067bbe17f9ee0532ed68d8b5bb448f87e5380d960e68350d465fc847198694bccfafb0bb1f2b9d14c94a742d1849b67a1083d0875ddd5e224fe01629a63c3e467d7cb1a2369f6c83f9cb131e11a14b4f5950681ecb5590903d6f99397b656ddb5827089618910b60952e37fd6c2f9dff0dc85019db9ed5cc4a45a9a40b59871994b92e84c6b699f6ee2740423946ecdba2c0a97587274dcc6318772682a5a1bb9178b916374814123da14619091f73999fea9daf61c0ad19130428ae2c2e85fbcfd274aae15335d2971dd37f6985057de155c9e7ca8a98f6db3534a8f53fed68ab2c483196db123039f0292376090ef8b73089d532c5d2d0d1d0b8e8d6d727aa980cf5f6d5a84f404"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"be91f32c9d84e936f7abc135e8f8f94a6833352c949d1566fd7a0f86d54cf954","proof":"94aabb1e14b4a44cd76971545fc327e96e6b66b8a171344091f178d47342151dbc19f1bd6d2f4811cb3c6c488eec98899337392d9d172dba46938ea1da28096b48516f53c1352f9fcdde28ac308075f55379b047445b3f840dfe8bf726ff7d5ad4b9aa810681e3f14297fb0b0d3641880981fbd95229333c9e4e1b3d7fd6ce26d984301c35c8dda9c189e4ee406f87432445e02795cf883d8252884e7dd91c0c3c3d389d93e4bdc5802bf840cc8008b0b0e68ec9a88a52a7298c18431762b1036f02634420fe38638953d492ff68a53a5ace822ecf24ccfa068dca7daf25320fccded83eae31ee242f05681fe1340a37087d57c4c848479277b09480a9dc6e138244a0bb5f16c3f21c6cc4b620ba83db6028618c88a94970a7656581a03e17194cfd4a30a71829a53d02c98ed9b06377382660300c9e56d0b6038c3d6763c65092e9ca4a16196596b9bb6a8f54fdd1457ff95fa8f8b1836b6be4adf3ccf1034a6c9710793a00f971e42d027d630c63abc9e91c15fc5daef74e6f95a7ae645877ec90f5b45a565d5a8750c96a73a2d92f487af6eb8eb62483f1dd8fb424adb94f82bea69757ed95e910822996736ae3ad9edeb1bd0118319e0465c8371131152c78dee54cd0e998c6f9291e3158b04f06b197322bdbe74f3c4666bca9c501ea15927c989f737f1c8a4335a9408b1fd21acd29924be61e6146221dc03de505482482e71a1fe3fb0c4da5e489f95b3b78efda5b2beb403b1622cd4f9bc7b47ef21b206744c9de3be64e0bd0bbfd8054edff83f0fd665bff35d48c42631e0e7f8b43caf671adf277e5329fe116d0d9d15100760ce06c58222f4b9069037483a88d4f79affa1e2cc18f96042df20c9b522168002bd65597f0133672839dc419363c09574cf6f6d99b0cfe8056632c1239684a414cdf8ac978dd5ab7354f76519a7704"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"92851672a8bdde5524b048e8e327c9a51c1d24a2571b99cdff69ce37a605a571","proof":"3643bcc7391363879b22ad22ec17d4c96a3841a1b4f80ed1a09b0ac9ed33f578800c194d5499a06d7be885c2a1142bfe3470019d426094bd04c5a5c8a8345128582de7cf50ffacf5234466e947f5d12cd76b1c8eb4d095e8dd081a3f2df26d49a20d3b2dad7c6756abf891da752631f8d5138997ae698cc5bd2a7880dca0cb6f11a0244bd426b5234e73c5116785cfc774c1f2b17eb65e868879032fad7d5a085cd1b95bd0dc1bb0b4c0be9991a4af5f8c31cb34f0ce074dcc25695a6c90dc08423066ea1fa865343c8bc84f8e02079918022067c1599ccf415b9041f508e20cf89571b17fba4188399f1cf1cf5651023186a8dd0633076e1774d9bdd2b2e40f38cd7d90192a0ecf5019877ab3b98c26ce43b1975952cff88ccb028eab6ae1603482387bee0e3504eaa1faee20b21d74ef668f7e374f9e5b574011407b628172c8d48c03d29ba6bb7333cdfe00d427aa8224d91dcc87606f744bf3708fc5ad55b498b518229babe0a5d58c64e6925cf370d51f635013c17ed362e0fbc6d53e578e3fe0ff7c962ef2d79baf14b7f7328d6eb3aad4498d26874efcaf60e457cd026a753947fc771e32d2bb30df039bdd3d5589acb7e1ac541c678c2775bb174f59d82c7cbaca14d63060cf7c7b44999a4dd6481d0d6fb09a07fe0d0705bde515655cd8310e9a610184b4be7984f3bc7c36f977f5c88095ab2f4be8fa46ebfb406096d9c4dcd30ab5caaaf0c367e7e1d719c8f25aa3d3b44746fd4272e07bcd9c1418b0ed142668d5fe35c94b5ad19d73bb9da692bb4ea0d87a1f2faf062228eb38546b36e64b6c5e8ab03216ec6707fbdff6c4c600ec376ab496116352efa46f272df35910a9aeeb771e5b04354b24113b51617d9d5e8ac9edc4b0b82e0ddee30119e024f20e3afb965f8eb1e5aeb7931b7aee88062a27b9dca49fdccdb99d5402"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"569831d7ba69cf1445e7192559276d54069f8290fb07e640948a4f6a7272e73e","proof":"16cab479309253e85ab45984dbfbba4372df64bf45e3f4c4c3a5bbb4393ee020ba35b11656920f48c303eba8de67e37bb2f0b58901e9ce2f161c375160b3f84c9a2e7da442fa01ff416dd4fdc55b93ee5c4ad9d60ef6b5d49c7cc905ed342731823b113b241f407daba5d302dab8563de872cf605ae359c2bf1f51129f9af8091adcbfda1cb1e38c1f39f618c26fcc0d0c6537ccf2ed9e88b6fedbf002c36508e8f70d4478c9f29725144ec9a3fa14333090b8854cb7e52192daeff41d217508151ebb48e946e2d556a311dfda559396a37acda28e8576db678b9203e3ba410d7e2d4deafc6cba49049aeb994e585e24346e6aef1821954cb063988fd7a66a535062fd003e4a474ea61431253df0a34ee6cdf0da6d59cdbaa569b1f1b9316739726612936f165802753aac34b368ef1d86bba85f242517fdb3ef6cc32104b50dec489f236146ae65ddb732e091c0f4c685ab2cba8c6f131f5914bb51d068c77586476b71bb708db1b6d00b99fe7add3e5ddd53d52fa52a2cdf76f7f35c4e7b47d865b36d5fd5ebd28c79ccfb948089fee7ba4549247763ddb1c8f0b8d799014de45495dc7e5ed96d2a781ab857c7f086f210510c09a51d8f0ffd1974f56dae3648175b476cbbb686af6b99b5f19312d886eceba066bcffd464f3675770468030cc932719ef14041e69e182a8aa40dd407bf908412a044a1aff013551f4051a2eb6e8293bca182efac45409658a4672df2c999ce01d357858e670635544aac765eef1552b43a58dbcc1453d8796df3dfba39a543a9b41ffdab27e5df9d6d9ee61c64db674f70e2a4a8bda8a08f41ed876af061038e0feb3e7505ea70ecc0daa31ca5dfc015f4c10711959b37eeeca14dca6f7fd85ff0a92881b75df85fa939c0ec6428fa7336e5ee66ca73cf82a901ef88081528b2d1c9e1cc5faa75f72a86507"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aa2d766d8d9fc77358c8ebd8b176ecd02f284a0a33016ac9f0b91ba90c704838","proof":"fc556a3905fd2ddee0bad4f288d531539e27783cbbce28cfa9a1b670cd199e0eaecadc3a1f73f5a0a596133a7f1945766f0b4b7d33f42e49750dc82541446a09208322c7a9b1e95d01904158627a8e3452c5065fc0af8cb670d65afc197c075fb63905c872d8dbe7fea23bd30e265d80ee1a12888217785f2e8d63a633d298612eb3bb19d6182f91d57d754ad3a8dbe7cc1099ee028b25f8de01af764faadd001d064126b131de0369edfec23a7ea5c60682e1165cc75063198915d00321c60bd78e5af8a699a921c9990e6e50488bad53e7703067777a0390c228cd802b93075eb914e50f6584a51a26e9b425a35c74bec0c688bdf45bcc362436443fd2467a8e0fd024bc31e843cc179b02cdbf80a5b6061fd360c58dc2ed1ae438616cb07fc480979bfe2de8a0c2cab604f44c755599c0a723c7fdc3761ee08935ad4dc55848041396a81b91a32adcc658d085f95dea2884ffc3c174076cc1d0381c5a383d1acafe549cf8afc8f1800b92a499294f6b19cf53b2ec69a740c0da419b70ca5986795c41b75c7b6d81c231b667313db3bf089c8909c8d51f101fad6b7aadaf4d263bc250ce4c8b7ce22a8a5ac29dac364e8e8566b69d3108aeb6cb796334835a46be93e4f3948178e4e03d91767928c7a65b9429609fca69fad9ec443778975e30743e1a4979e94b2f0bafca656ab94e021b88397d53ad1cc574ae1e13ab1f34d8b5b70920d339dd9fc7e222823c0824e14830f8e289fa0a9cd2d6f44c4e9765ae224198694b18a0dfefd2494968a21831aa6ea08494db2436466e87afb7ff40fc64d3b45468015de1c57e012a783b6ba9b6816162cbf9a2304b8bfe722cdb4bfd20865e770fbec3b8de695295d4325b690eb60045ebb5658739ff542187d40fb704c678c1f9272e65a4dbab060fa93a7864cc2f6e69f848dcb6f24b67db0f02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0e3f206dd9039c3f7e90956472a49735d7dec1eadbfa5891caa9029105a44655","proof":"fe86c2e7c8805f51d2a9ced48fb6ea50f8140ebe15a6b8117f5c4d442de537419a8f292e18b80dd6e2794f3ef9779edfc01b61564d3085b80e3fabec52279b07d4cb8a35c9855c0b341046a7e7e50247277bed33dcb9259848ec761213919e57203809dafafaf5c0c6cd47119d7380d3d21fa1cfdd12ffe9ba7a8af0877031778c456edee760dfe6a6d9d0926478f2a26c7b610d355fe3130f83fc73d2746e03d180ab1b468bedf0a0b775b8f5c195bf360d9b2c4e28f6414257a008259ade0822f167e89715d9f38f80b31f10619600384aead3c034ab1efd3f6a49dde2cc01a438186502bf7f1d5f7acc3e45aa4b9d3f1dac8a743964e50f7e60ec06ed0e58a4403d08c795a2fce9885dc1dffebe536673cc3aa136cbf3e4c9c3df5083725552a587e68d1b828d39469344eddc915da69a5e2e1ec7a1341cbad3762e0662741ac45bdfc99710ac2531bc9e773c903a84cd43e31a010d13dca1249f6219393936bb5e1697628e336d9308421daf5f5ab7365a7bf77bad41ac27d4de7d420b4746d120716f4671090d59282bbae48e398e3cb7a3128edad074143091fbfa66755a503a683d49cc5e3b785e286df17a1ea11cbb6038f0e6f6d86ff19ffeeaad1fde341a5c16487b7a747046f2439961c30212d659d34244f3630552605fa00653a4ed98ba729ef6f288f56b9c2544dd0a3aaea4916ec34a50d4884a492b5d574c7caf49702cf3ab28745b193651897201f8cd3996e5bf2647cf1884def14e632c34f63598c705efe788176db186401adb6cbf9ebd46238b73f09cde59a918b852cec69951f089ff0e58b01bd4e302bf13aa85963a28d930672d51f68b214e516a296dbab66799aae1ec408e1f45efe4a86bd9aaa605081aa60fe0a74e6841f80d95973f8f32eb898407e4386c21ceded3887b530184034794d20b5ce3c3c0bf05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a8fca08a13eac76d99fbcb72d92f83d3d1f6665a05df376eff455e847786e55f","proof":"904234391385aa4672e7378d6249c8505f51c462a378a07e98dcc549c0d2e06dbc20f108f056bac07d79962eab774967cd384a02a4168e912af4241189898a2e9ef969df4ae92dc04fb04de7355c8cf0680f363fbbc5826ef12cecc9ad0c6354a6ea40ebf07acf17b6bae4c092719801a8f11efa3a4363b741c644a694f06959f2168721be45cd349371ee25279e2e74547072083d5698c346b1a6d1a2fc4e0ab2b23fb3983808a817123d77e76da2686bd412fd8f88add6278cd6fec82ee200781d41e1bafdcd3b72f9d04a343256fd030f6483dfb1c2d2cdc2fe7a753a7d05ae91f343359b59c823122b85c6271dd1b56d20c7118092506cdf544a610743188878a5d21802c25fb787d0d947834032c647177d7e5a88eaadad464d1924d3640e121e1ab6ddf3ff08c958637228e482005fc0e7061764f6dd7f4544631a46671495715bdb331e58be806e3fccf3e3e13ced795618348e257f744356c3fc813fe2992cda777be68be7e2b50970bc538a06c37739ea95d32d0529b7e5464b3f3ecc7a74a5303f27c93a7779fcd6553086db7af147ffe5c27f5561f8fc404ff020786c4b7b7935fe918ccd86a8e5ff2090e8bc9dd3c42f99a64260bd927806e47f06f178d2e400469b254797a1b3cbada3b5acb3837f47ba74d6a841228776ce44ce29eafec5d9023c22edc7ad984cf2f521d8966e9a784fccff210b5f7a7c81140027a5a110fb4d0d299e6934625111c96f4955742e88ac2f73231008e9772974a0f5f0e2e25bf007048dcad6de27df634a0442af446a1085137c288a0e1c65694e399714488d33104ca0dd515fb57ce9f2970eecf87766b6b5b3b7056917ec72a36735b66e9367b1a0133faf479cd1872af9ed1611c8536dd8d04a0a5fd29d069aa45d982f71d5b656c5b793e1e8905c0beb25b50a4d701d83e84d62392cb00d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9079acca69e7103788613bb4e4593935463909752620372b41a9e5d00e44b656","proof":"0044ec68a89ed459eaa3523e6f808ff571da374b45bcaa5060cf858fe61e554b4e2c8797b0a145009f3028b48572a1bc6a03d494c776f9badb771a8b7cce264ed0c2988676aff26fbdfa4247bb00c379570eaa5f976d3d38082540a18fcac21498020c19276128d35faa73580d43d52c4f80693cde54368147e9658484f09f1e90127eb5bf1eb4e5344c15a97dd58148340ebdb46347b521c48ca0a07907bc04a266bb06ec83dbe0cf1d238aac5300622781209612acfc627bc4d1f23388090d6b6e15ebdecbf6e65fa154f188b7438bd5a1df611f11a641e3cb5ff0d72faa0d7a4e842d917cdd958e29a059c4cf0c92619a910ad32a9d8c450f36e3c434ae200097427c6f9ac3c6346941df2ac1c0bef4f36db49e18b7adb44757123bdecf7318efd3a52a6ccd51a10af8ca648b7db15bdde4bf07d731b279d1755a361a9777fabf122e8c5050231eab496507c06d6bd14cc2ea9691a344572a45b5be9fcf480211fffc7fc88e402513e0d5927df5454cfcfbec77af3f48bfaef9d712ec6a3ce6151e3ba9c5d347ea28a5cb46b9931d2c826bfbddcb1409c70d51bc27dc35774cc50c9a3567d80d3f74f452888e166beddc6f1be99339940f634dff7563ac26e6050affbb601d1dbddbdced9b56d7cdf00b17587bba3c483dc946b2b5fc5b5cf6b4b5277d7be620472c39b5ea3609fadd7181252f0f1f6bd9ee7fb4cb57e91834002f2384bff0ab99aa1a18b1a8e96222df78ab37a3962f7eec2858c406d7283291635a330b84b110b36b1265785ec896b9378fd11436df87efd754ba5f0b1166cfaa167d8371ff6d51c2d523d9e085432990365a2f77c4715dd61c256fb631f21b096c27aaa1aaf68e166d981097c0a136b573d2112ecd7c9aab90a0cd970122c3453913367c2203913404ab5f80674da76c2ea76c3ea653ce7f0a8b87b202"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"de4c06e9fc5d241214d0f09f578f18e6d63f604faea37319d165a69c624f6b09","proof":"069e81f1a593ce6b195c74462a65474bcb24fa5c4e72b47654f84501c00bda3360606620e2b46eeca2d161eadedd7e7d2c8c5456908a15542ca1fb72357e011614e3a47654357135120cd06651f5f78adb6fcb45049fb79260973699e3bccf5342ab3923c1e6bd9fc07e3e9cfc46faaea6eed19df932b947e2085115a25a4d2749bdda4d67945a6b62d45e502b798e91c880e7fac101fadfe789c33839b10d0f8f57a003a481bc10293236c987fa5c66d50131cf415e80cccc44f4d8f0e27b005068d6471a03caf3575b23bab64b354f887c26dc3dbbdd8f70b234e7c5d9f7033e597a7fb15518d42ddce3489fc9331faeb31b0c50654c724596ce2373fb294df216f1ebf097445e960b923aa787d14b43205e5dde92ff206ee0e2bf0821216f22f02f1f8da7562835ab0a7b48fd68fc77241941101efa43e82d5e49a03585423ea13ca1942d1a48545c2fedd444894b6ad4913173ad7b9c9ead652796c3267732cb0c62a77c206932e9dd93bb139fd10038d5142309c8258c0f2cb4798f1061d8a681a03a30c8dc9b1dff30e87dcb3a3f310ba603bf46c2fae22befad7de600ba10e2a28f0b5747e8ce42a11cc35195e5cfc9f558db840aed4f59d0a4e77c219654b918ef41821eeb36df26fbbc8f29aa1d80049c2a0d30ded3e98fcbd7ee195cb4eaf85f5382b4b47f7236ef1a264fe9f7dd6784281bb491248a38b51203246c451e22e5a7393358cf2aadcd99fcff39718831ce4620456008296765cdd23e16d040b54d035c3847edef1ed1a6aca9102b86ab1cdd56f9dc885be83d46b7123aaf569ab7184f9eea507ce7d6c0bba452294ef611574dc9f462658be46dd32bd859cd38ee7966de266507dcfb0a9ff8ac87b42411f61ea43dab161d0f0fe302c181d68d8450723b2cab736d2f99b499cd4b834dddd47f1089458acd53db010e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6c777748f0a9218ac3145d988c30b852f6cfe3ca0c7c1c8ab5670d6d68883a5b","proof":"9ac656c4a55c96966f9a7cd1ef581e1ab229a8885534e82e7a392b6484fca846fa9901c8842ced5fd912b22aa0ee333fce365b39822f355cf7a4e0a9bf8cfc2bee85295bfca2e0db8c80d9e540719ab24a209215f5bc948cddddfc7f951a4b1a225c0d919e3f5d32c67331157e1ea13c036a9ddf9f5a89e634a35c126c11fb3689f0269e2b1d4f500d2e21940b5dfb5119ff390f6e7ab9175b2786bf985ddb0df2ef3c887e0c9d4febd06cfd9247caf0dde9b67fabacc52c9475eb484dfbb40ac6b85410271788562bcf55eab02d79e6c25718eab25efc13f429cf3eb8b7ff09ec55e086dfb04bd9d5d63cff6ba4f71b7ed041a46a184b9f44992425e3abdb73d84369f8b4ec033b085efe2d708b0300580d001bc492dd9f0f20429cfcafa72a5055e8d5eef481b48a1099a4afffbc2b0e9135464f7a00f3b7ef514e65b1810192a86cad237064441bba4a0e44cf69b4e942b7e7406d7516a4c595fa0e5a0060ea16169169c7b28feb493fb616db0def4f64a2adb79f16d63ec205336221ad23a2a4d9dd7db3f4b98b333ac681f0db9073cf06b84a45ecac70f73ec180454e25fca715b5459c3e79201d7e96f570f3b1ea3395d85b092929b2dc643c94cee517747af4d047d86d5479feee52a75f17ddcc6a6d8665d160877497588508477d5c40e5513446263ca37d328e5f4bee44146af269b43a8805dc52d5643f1cdcbe1e489bd871c88df4bcf7096e057f92dcf1060f9ee35a0bdc48573e2283e4e0ec22f27e63513a924784f47a9cf3e493ed4a5e44b745ffb9ce40161e17c830006e600e8be5b3d637789039319c88154c32dfe4ff4302d818aa75be9410e703067960c668d776efd2052b280100a825885a739d1dcf63d035cc5cc3a808efb11e4f0c4cd1190ba56e9d73149bf5fbf3d808a55d37d5b3a36f3e4ad21cf5abd7d05b03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bc051c58ab49c612c521c9b439e9a5f969f35a1c5038a1c9c65fa2e6a26bec0a","proof":"ca1ffc56b53874112573ee03f508e2c292b0ebe839488fda3d71e08ce8111e5ec87f9b15d0ec7a11667341b97899f0aee9e8358f537debdb7c5f5ba31d524d593a1dbcae6148dac722a986ff7ad9edf70ac07298b5cddc97b2ff548ed7c1db2f8036856e471d02b01b6674f4e6a51c2d2cf70f98d6640b874973c0eaddfade4ec70f5a84eb44dfa7559fc48a073e813325ffce733fd77cb239d88756934f7b0129aa9995c688a745278faa9627163ca4d9d27ac003b1a28edfb388698319e30987018c883280e94707164f7536a3ea32d7a95ceed9086fcaa31d1d77d9ec76045c16e7c91faf79c27ea71ab868087413aff999b0c360aea0f159b2c7001d725d269d4dc2f41bc259f3d1db80dbbb816cc07310f609147831e885b1cd6d6b0152d6af97fb470c8163e2d154067937c40cd863fdaed6eda7a2106425f84222e456183e7e5dab5d6107c4c756dcf3d73424f201290bc554f44bdb91857ffce53c791ab79e0c5a5f85cdfe01bd21046f5e7532c39babd2e6c804ee1eadd894c2ae2e5edfc870deb49c277aacd679290f31d0cce2f669ef65270aa33652b709a48c471e12151a09cbccd6eeed94e0e66b4bfce172f80cc5b936c8d7da97176512bc0d1c4a211bb33033d4660f4284793f2a60ebf06d8767c65d7d9064b507a40bd809d260bbc643bda0c1937c30ff8928bcd06c436569dfdae3d1f77eef55643b553fbee2576e8db3c193c767df150f02438f1acd0934805e2fc0fb97bc2d26fb3c3870deb3eca24b271900834a82a8ed8d349e0073cb2aa8501e2d18231da89325038a42e1f59be6d7a2be58d16dc9cb96c70698bcaf932a48044aedd392d55bea21e014d6cc34cdeebd39de4902892f504872617e9e2775d875b1a3d7ef1a7b38045d1eb6398faf73816958e98ebd0e056ba300dde38ff8997cf415b8138c9c2301"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bc35b6a773143cb6036cf9c2dcfae37a57658f578e6675f4c7288b967355554a","proof":"c6eaf4672a7529ab6c791a21c31ae8902948d7a8c7559f4487d7e47e7c6f161dfe29b574c6b5a051c1b42f6388d029c9319f190efe182c4bca81954aa5e17417f491c5676ebbce91edbb15bf9a7c1961c8d0b1665304024f6e9f40a51cc6463cd67ad7a5db8ce68069fa0c5312c3734873f5da7e0c92036fbe714a9fdd255c05dcd7bbc999da003dd2edfa2770657b04430e28abb39090d04b68b03f5921340c52e646072f110c659ae14e4a9168892659fcc5f7f58879602f3591766c9dd908ae925ac28e334c8c0ff570e87f8b95575dda5cb28c911393723a92db0471500f2e1d91fb464d6a4ba8107e2a6ab363fd20079738e59b1b23bf57a6f7d59d782d5e2c5dafe68a7df827a9cce3a92566b52045084ad600e384166c4a14dc5c2f1478a859131a28673c644462c890b59c9960a34963a7681396a147b8032666e85f424bbaac7717750212c5072272251adfa82797a7eb5409fbfdea39b1e4ec06241adf2d18cb80506c9b606351a72c2237d13317c4b6ddd4513dc0f7c2fac6556edcaf19b2c086eef59cb37720ea7f5505f3e07f3b403daac6503288d669716a310270cad77009fc473d4b0efc24776dad92f6eb3ee77d6ea0682ddba7a83424784cd0459f1bcb487a51857e1e57340f04bba68dcf920e58febe34614465a60167dc91b43ba0f1ad8985f5f5e9fe74fb4a5c96c0a20eba851d17cc6b2a1c99b754aaa0c92d4b155be51274069bc41f700ffeb6bcc72894f500a2180da6a9f0c643ae9f61846e716d8137d49d8d23aca6d32457218fbd0cb1a7b88cb13ae9e7a2243a3ad881255b7f81466c9b2499ef75f80913ddef5a435add7c852ee134049c470a3442359a1f9cd5c141c853489223da09275c0bd0d1decc025c2ae1a82e19072fedb2018fc3b958c53c93907435466131bd2e5a1f62ba260d41354c91df800d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fe2ac2dfad4e6528f95d02bb13c64ce31e7cea83fd97bd56b37c4bf03f27897d","proof":"5a21ecf34aa08f1776348c48b967836d2e9725a5bbe4cd685281d7121c210d70fc7ad8f9925b468f1c8a74f1097b688fcb1beb5ff1642fc4d4383ac4664daf7b5280562d44036c96a3bdd726a95845d5773bb9cf3caf7df308d260a924a2ff0604ae7d446ce53d0d503ae4c006e4e1a7b7d11b17a08b94cc7c19b7b554cfc650be2b1ea2d6ba2090ab9e2bc27795c01d0906487cb8059319ee8a844d608c5b09e48659922e0aff99903b9d31dd977354b5d177327fec7db720711b3584d761007d085401f7d1bc8ab3151a36b702c2ee3e28f780557b7960df60fd986d5f0b0ae49e6f85bb87b8021c6e944ba94297d9e6e2dce9ad742499e8fdf8c2cbb2200c3c041c4bbd9e0e7a18c157ae17c496f81748e089f338e057f5aa29ac3bb6af345ef6d9f257e43ae5bdafbf91ccbe42c659b772a2909454b4177080c7bde05231c6e516e65179fbe8b36bf92eddfd0a87287b40fdacac3af4e74acf57eada465132f329adf6312adc56b5443853f4f528b40294c9340ed11398f4efa5697da16ba8c4c55360a48fda608fab43db47c453fb72747269917bedc380d1dfed25ae26f03c649f4b6dbd193f918242371fcd3643f2e6db5942a12a49a89a7e2e6b7f50cc6a00e0a2c3a5b2bb363990b615d6cc923c1a24e26e67971d1cbabad662d50ece0b0fdca515837ed7c6acd3bf77b4a43c7dbe038341784d42d79e5502cb941a10416e2227a7c5c398a8cef728a26b6fc37870ea1dc532591fbd028f21605905bc40a9ec636fd119e94f757cb97da43d7f760ab259744d9e9e3671e3348645554685b57f407385d14bd2ce2047fd8b6feb2ca81ac9a6173658aca8c7d23df173e4bf1eaed780aed72d805eab23228c9f4e9a71e578bfa59588244d9df4a9c009cb24bc49d6037d367355005782ba76b2d45e76858e24213d1ead1921f49bab01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7edc35c5571f301ee309fc20a0b590c28f881d7e41cbac90bd6d7f7dc4b3957c","proof":"260157e20637695120b2b17ea051c1127aa0bb091e90a3ce550bfb56e27b0d134e7b0825718650c58c2f19c12a834f48b0546b15bfcd21b44e4d2dbb06c2690074ab1b63b899c80f6ac09ed6c05ff807948df3f927b52148bf6f92db30b6a2007612bd67408a74095ab019fa364c33b87b77484beae8db6a99b46abd1909610b76e372b179c1aeba48360d4a45044419bf7574e0320c931f99ff2634765ff30a2865f9a45848336df87e4e79ee985cc43e8dd5d5646d4631fb62938a46bd8d0ee09e0fa1807d803ce4200d616becac692d865ef665d7520ba0869c4131db100e3270ccf976eb9cd78c22507f153b7502407d879176ac0a9047c5a02d40f38a0c0e1e9b908690fbb0d32b7bc4228373db8620466c3a82db26735575f08fdb7168623bca415ca67f02e0cb5ee9bbd6eb5c804c8b12e5b63db16b4ec0224b330c1aaec68f89a3d2190f2102e1d0b0d3d354c3842c3d86b812f9d3b102ad61d18f4e84a40209be7e0c387a751b42b24cab1985f688ce14fc82afd88fbdf8f838087b22bb38ae8437e84fcc36a938771ee80ba8771bb2837b6f669f279884edc6d265ba7e52717cfb1fd159cc66d60bdd440aa31784979a1f1b4cd0c5d9b7edbcc2665a1fbfd1d4f97850467b48e83f77bd20a0cc3b692b13f4e95c98482948ca845e209643620de4665b1a383a6989f263d707bc5cb1b74518224eb85ded7857a57434599d1bea59369e10f9afee64fbe7dce7ffd5b9a80a7fe513d67acda0e796654ca6545f6a2ea669ecc6cf195569450e65278512f34131c5afcfd2283916b01a8afaf1e4df4885e6a943a3bbd5e98eda795afd409d49764bd7165db1b4eb8f42b3d6aeaf9cd1abf5c7ae8cc25d9cf2045054072dfe6d1ddeca6f32c2297ebd07f76f20795a35b2ab145e4f607204da991dd81d5039d87b18565a2371a98d5e05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f08139e273f4a4540bc494ea1513924fdda634a56ace27e1dd2c0ceb328f5435","proof":"aae0a13c56246f37bff3d4f45d9be02bb494033dbd192c3981850b89de6d0625dc25044a0545ae968e9a9129526f14ed2d38f87b684f4b0cf382e108bc1e811d68441f8c298128dd3c55e1e66fa7f202d92ee203523686f1e6a5aecdca145517160df8e9563babbde9ec80c4f75bd75849554dc6195fffafa872bf46ee8c89699df8b1fbfdf55190813f27434106550ee88d41333d0cb0b69030fb5f3ed21404db866eb03bca49b0dfb117729139e5f5516003307196bfa14adbce6e1b708407ae0c78421461166bff51859e67b552ba0c6298cd4a34017d6a14532172be5205589114b1315aa6cc883dce9d643969a81aaba0ee529445df2df5195818972f7982fb5359a3c79616db05dd96d5022d79c13b412c65f2ec61e14f47f3f58a6b569eec377d12e102cf8c65bd7f3837edad6fae4281e765db9e4f75ff2187a9843ecedf2403d909138c232252747ce78cd9bab067c09280d16c23a3a15f2df66b2ea8e57656ba7e79f03b81b4767b54e12b3bb258694d2dcd2728bd24ec41ea88003c855f7da50245276dc679c3b851f46fa9ed4b98d9fd89d92908682e05a0b137fc9125f4c263764de54b44332489d1003e1db6dbf66ce7a951c20eb9f9573f347068cf22071b249c1bda5bb4cf2f0e1388214474fd3e11e984756349f6e6704276c61af09d1a5ab544e5689a1570dca9707a92d22e1eef979af1c88a7c5f8827f8bdf7ab4e95cc24083564de1c1f770ba06ea422409490fcc80fff76e5bb390914018d80dd88db439f4c471a02b0476b958ada63e9e94d3783f7cca053152903244d14c5f639703ec7708d413c19b2020704f503e9d096b89d3af53579168b757f3c9aa64d8e985872be1845435f4247307fd78dbb35e7117062e635f256e8011c23c297af5eac1fdc283ece351750409dca23ecfa316b31c07be47756dd8408"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ec0122b18b7cb98ca78bfabf80c2d1b8886512eb6e9dd7f60085d1f103e69508","proof":"94fe896cccb1539d6a78a703a85a8a14cc0f52d3faba345335502583a7e51831584cb5a22d85d8269486fdde5ee2e6b8d46ad66093a1fe6652115436620bb5708c44e034266c76f28cd497380df24deb3dbf8277865ac2eace22610511efb077fc1c0686f42c8b5b8444c9fb35d78800e316b82eec6a2397313c3581c0806122585a0ac6c6803369ea0bd0fbc967018e2538209bf6c13034667de8c3b0f96d0114262811d281d9a951700e8a843eae79b50e5a4eaed325b456e9b5b813627606e8aa4928fc5f13600a947707adebb813760f9702a72acf05b62a43f32080b8017019164fb41cdef3a1dd7aac79ee2951fad764d3fed182e20b562637a2245121d8f5eac5639ff0642777c3d0ab0bd5a9a1a0b0a1a35fbaf5fcc697b51725ad0f6215695893c9612fea69ba5332ff8df289ff6d5015a5364e89a19767db1a6c42eae4d1615c03a55cb6ba70d818882ca946408735bc788239fd44a98cc1a557552639f8fdca0b88362f8a58afd7c6c25e72d4e77f5f7bfac7ef298248b553ab70b496d49bb064a52c9ff7d350737662ebe32bcedb9ec51cdf9806d1ce70c5164f2cef0e6a8f27434a3376c0baa3932f131bd84e1c6536f67bd6921f80223b813fae3df126a19a747277e866b7200d7e6fae60b28fd7edbc14521a7fb9bbe2d67ada03d3c9e3b63a02095762a1464e41a2c43cdcece9c5e72df7b3a316544d05519cf5130712a8dcb88cab3515a905a3eee67e53cf6719a5bb1731a1112d23b61e149870d16d83d65b8a73ef80fb7f8cf3ab7e5f7ec3fbc47162eec7beffaffa560494ae0c26cad43cc014d7ac4713e9a070d92c2c34d9b6969b5dbd48bf84f3400c8d715c2ca3c9e36a4c59b65b494235981094d0231a00d20ead2d75985fc50ef737c4e791166af73b7663acb70fabf96a090b227ab1d5682610816405408b03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ced63702c5b2fe5d0d65bb051497eeab257821bc772c0406a66f277a3cf52f78","proof":"543ac6ddbf2b90a55a97f98191f604ff1d8da053ebbb3c25156d7e79c256bf78009ab559a93d12394d341557f499e64c0f1eb3f188585f7426c3cdc0f5196e1e4c5134515d7bb65dbbcf5538f105e3e56bd52749b94d1f576fc358afa0914a4db6b5ab2ed6c8b37b724be21e103508f4929149546c8a91b94987aa2db5aeb210d6a094616ee5af6289a74e7f2e71a967978ddfcadc978c2fc8e6ce19c7b8bd0339b2e64977ac2492623e7a1634a974302ae6f82e0e11419ea5f47220a7be0e0fe9491c1788449e20a7ee20115f8b4800c3233cb2fdaf8415513bf5cc8c13e303b6a2e19037232ee67b656adabf472dd194e6d7becf75597b3cd216035c3c4a0bde12a1a429ac7c3b74d00614ec0dc9a17a551ecb077c0b82203081191a8e060032af8df6adcc7a6f94eeb571c1720d92d88540f610f0c4340b93f425efa2f54b8275c3971657b1433a3718d7d23d87b4b0a3e596f11e59372787effb4cc12455d24a99d3792080921c31eb0ec27707d2a99d3d496fa6138bb50196b6fed15c2352a1b05141951cbb2031da85fc4e62a92d306d3368a07580956592068921d5643a51cba259523d7f8fca1ee2dae35c15b295fb6451fabfced9b0b8d61d547779842e0fa936226a2238cbd1a2e6a8e1811e87b8d8b56b9c6c767595cfbf364e6664c33a1384727c778d433a22ad9583d0967855dd8731b17034cd5ced39876a5c36c6a7ea027ead09fa0e938ac9e80a600c61d1fa3f1ff48cd5224c329b18b1510ed3c5f7fb1c2a078ef54a6946f1b19698ef22b21773d2d476d814b922088f3cf21e86e623ebca2c91a3306d48e686c0187257ad04e4f7abbb4785e7104eef5d41edaf013205e8d77b521aa8934ae76d3394b388f720c5609f7043b4b58734091b7316b1d2ca88087dba446d6562087cb68ce7392c1429070ab5451a3e093403"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"324c09a4fdf341d5276e91cb53108d665bbc43ed92a10af4d888194584e4c545","proof":"fa7b92fca4fc0570405e9e86e4ee57da64625fbf14ce3a4ca9da45b05883e903423da79067a1e86da265ccde4fe96bfb9ac20c4fcc9641e5309b88085cc9e80bc6dd487c38f680a32c35c5451b96ed7b7bfac781e3999e8fc1536b9a4bb4ec5fae7392767d63f6189bc8414207410eb07aaf6b3562ad9c6c451fd39b4252113f83a802a0ea61f8ab516f76309ea5c000779173c2febeb23229f4eb5649448c00bd42eaa24514a7c6588fc0766587c3f8d04967f61edd3a64b75f870812e7a701f94419a1401d4c094417f4f28646cec8715fa72c9cb7d18c417f36d98b830a07fa8e675ecebe2340e5a44f1d56ab84141996d4212c36b90e21a1ca3bad1cb81134385992c32b5abebd0992b4d93afe7c9d00040be42176ddf29b721a86898c36a89ecba4e4092bd5c30e337e9dc52b7f650e58c314d92799dc0abed876aa857c0a73d65488808dac4ba8378c3a921f11e03cb1c3a82a26d2b8311f234369f73726986e386a72b70a6aa9b349d5152cc74603e0d95ab4bd09eaa6ab15e7df2353cee1ba0533b30c703d1ed607fc9e1f778c15701a8709bb21866fddf9b21d2d6a84cf71bfa98e31fb3976dd0eb5a16a4d1e6e6b9c0a80fb3d763d1c3c49d1d97372ec5a865bc5e4e93ae93990aa206a10d13de4fc49a00204406aadafe5bec836a02b861b6498a1ff71c71101fd129dc44decd61f5b6e31171631b3744cd54f372a8705793b5843ae065f96c1e1cd1281396ee87c70f9e03d0ead20dfb020bf6a703efd32d0bb8f0065b57ed4ad287a52d9f4ab8e27db839cd002c2e1f7ff3d6edad7692b49e936c2ef39f7b6288e35e919ac4a284d4ee363423010ff38a9c067b5d35692693c39c54e0d9ef4af441e2b2176fab9cfe94b276a57279110dd690fb36a33da2de81e5bd894bda2b088640f27065896466e58d7100b2e9a9c72c506"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6aff406d3bd7f61d086924b5e7ca1bcc4213107ebd065898b02e359e7cf04933","proof":"cc2c88da17e044fd8b861a87559a758e16540275d723457357a6568324356321a63f75e58ae6dbd2eb2db43e02bd570c446fa205d964a52f3ef631ea1be04b58824541dde5bab96c224887396a962c175357338111ed8808c78f29f70b2b5541d89ad8e7b899e433512ced0c79aa1f36a00e1168177f37291fe85439fd485f353b77780b2f029196a9f98d9cedd358e6512d1bc2b12e7323b5c03436f49f7d0ce594f6537a1a9520de287e258073df360934d24808f0153aac233151163f150aaf53f1a6bf1434861b0c372d056bd3374efdb86874b6d0a94b6abd9500abd7036a9ea4d846e083d09fb549a4320cfcac94077c889b10d11c6c83240bb67d6f1bbecb44f80091b81b450ffb4d879405e70c1b19eda4c9419b4c842449e2bf7f2f0690b6677c9f6853b1c7e5d6eb8b7648c87f4d5f488321863610f0b2c2476b57442af1a004aac1e5a4b5173ef9c1e5cfc6d2351efb2d572b978db886ec1f1d457a153dcab5cc77e439de8190d1d672ae5b496db2e153fe9bd06da2841e957d4e5a0bce3b0a5baf9fc8b921877325ca01243ec6a221ca69ff68342e0ddc2e823e5cea914ed89e47a5815d603888e56a114e437a02a6f669d0f39cb08e05ff0410282a4e46453841d88a3430cb4b53508ac7fa367958fdd77c84aac16244422855c0f993f2639e5b76b9f2367f43932049b58815adf3273f45ebaf6d86c33c4525d2cb378b7df6da433825ae744681eedf62ee9dcfec3d2643833402d0157aa10412278483067cc9645a3aa82e90e8121fe6986eeb514a5b3789647b69faafc506cef63ccf9c78fef168fd593665cad9326a59831b932329c37801e9e6d390c610344f6c013ac26e30f493760eb2a58ab9a4cd4ee7f5a2ecff1968168073f9a60fff19887e4b79b15d3e6fe7da8289b61681c1e0eebf05906285df939fb0a65a03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"80ffdc3c88bcd20e25dd62c9fdd4e8b4999370ec618f82aa587217ae08522b19","proof":"2cf2c3c253ca471384b5f1d37fd132937e61312d75076bbfa31f1ad6f2aa2235188a44f218801173941fdf5a6e3893ddd9e751308069ed4a84c1c9fea4f1916240b32eedd862c8b26aa3cf816e93c220ec919f9df7463bc21bd3bad7d01e1a0af8d788e5a32d36099d514e4b6ad09054c657b1cf8b3570d7d4f8506b6a07f1693d5c8f84cd5c83ae696f9cbe7134f4c6a759e91d89e9be66eb79e9ec17160a0fbd12e60edf2d700a1cb07eea2cc0b1ecc50d6b88fa9f39635077898036cf3a0e43c40001bb618688b40af2a91be532fa6ae5fec0fb154f874b6f6d075f5cac05b4a3bc879f353103e381b5194f7bbf747dae40eb2b1a003f98f627c6d3ac8f525c19d96577172dc733d74c672269344b05e7f3ae9ac68cb73c60fa4ec2cc4362449de30d65f929730938efc56d5f3d47c6f6bb911016ba9e3b3e2aaf76338274546254d00ddd65ab5cbc2b0c85679dd17dcd743c8e4df6fb53c25c6950328346342ff36dfab75801d64d2ad4db0c8efae7037ac172078cbeb94f58cb00e90b6b002b261b39fafc30bc8f1ebb438287103f306cd117ce27a9f97a455a912a6e5510945f685c543000ec9b5c4febb68a6768edf74bf3c5ee23a96117af55814a13c6a00891abe4792dbbf3f4d39e8f799dfe357b4a3f13cce92b274c0e581967432c06f3a0a50954cbaa5d5a1c8bd7972a0e38ce48a66099abd49110ab2aead415903526c9017b0cff4b42b5f535bfb96f3a8e652c72b2b7ec5fa699db1989a079305a45a837dc30354bb2f559f62dd952826bbe61c4be3575673eddde751d7f026c8defa678541ddde66b930b00567d639631d38fa5fb6d189c01d9140f3efc0704f4a22a8705cc8924f127480bdf8a47a99536a89d5a3c72b2e6026a3ab7db0d6cbe82eea064802ef6784ae1617ca054a02f9d42ea42cb93c5099b6c018af604"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"085929f2aed151c43e3d46a93b08afe2da15a441dd5e592403dce6df162d3c05","proof":"be3b4c6dac019a24414a6917b84da321e86534e91c3d89154169750b73c5b2529e830fa3f86593bcd49f0d263dbec862c1b1225308ee50ad9cdfdaa7db6aa47682805507ca4440c4e4450f78fd8cefb62d43bd91c3542564c6919d8b5a02ca28f03c831cd5444a5410f87c6ad6f9dbbba8e5544aecb274a3854229c96b929e3ec47f84ebd7375c43c9a510dc0fc0a59e477cb6fba3645965e3533baa662fc407c8864235f4e608779eb4057e3b9064c377015ed39afdf18e5111efe823f52e0caeb35872eb63777c97dc87ecd8042ee2ffe0f0f3ad6cb10043838c9734e4030c00ad80e9e54d7b4e0de196125b354e4c175e7a139d2ea7bcea33ad10f8d0e126a8105bdf1d0eedfc6c73cc87cf69e7250ba9433f30d6826d71425ff7b5a2f509fa5341462ec46fc148e15ded9aacce4788675ab8a2b13fa145045bb7665503645ce4f0aaeea9b3bc98020e3045760940e83ac285f9ebad12e2bc1f5370395c28e68d3704f97d00aeeaa124ce5251bc0e889fa464342eb772205bebfbc60c3f2c2812bde89ac918e182519d1ec78d25d5994abb758d35ea4f9e9b130f717ab5201a28a5fe2801fd39377123670181d45a16371884b0f39a4c1224ad0ffa348c7c1a42a7c1de38a0d58cb12aa5fadaaf01119326d1ff34219b3258a703e56951071617b5fa47e5858fd91f6d3f550279abe82ae89f630e6ca44cfb0871a2cd2254080b2d434f8f25c149abbceb04d73a5136c5833e4810e812ec8e186694e99718c845cc36ffbc7f2040630014b0e72f5021685ac0b99b57429a2dfaa2e18f9648d6dd30f858c7c65d207209583a760313fc8aa97495a63f4371331308ccdc642920d919d1cd9820fc369080c48f0f80a897c655b2efd85e10ca6c3be4564198002d9a0c43982db644df41ebb27316b70ed16f15f15ff33df3587c0c5cc87eca02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1c35c6edae19a82e107a41f918b1f29c29700848e3e15bb49bb7927988ea5a2e","proof":"62fa6c35c2af74d8163affe9a39c1df89c67fa3cf53a8a9c7f63795232c5d458dacd6410a7ccc7871bdd4c6b3566e2495f27abcd251d815608d3207334bf0b3722feffdd64c638693eec2d5b1ac96c4758a1527566554fe5a1181b714988e051808d6958b5ce87101f3058f521a64649350c2a12429306f54ee5cd5735ae6d095a933634d3d11179cd8e48b312b773ae407b76e1b2c588e7926584dde7c6310ef723e657569d4bd89222b7ac333f18a431a6df06dbca02d9dfa350319d027e0abf9281f6b8b600932c9cbe8a23093b10a03ca3d4df47d55421668349dc8d57054e3a34b132398cdd8d0402f0d34b99354190571316ffd208f13645893ae0584782ce103339740fb7cfd60e5398f6d8b89ab358accd04c5f9302728054b7efe0e3c47e479754234a1e8cbc35495f59ba2d6fe08adccfb5c1fc811f1c7f22594132424b7a87a7bb1850d204663c153c3f7ca168c972c3cf506b0d2e7fb7ee09f7dae9b9637e28f4be476b5d2f54fe14b4cf3267aa557ba5225aab6de9ba8b01115a8c353bc4f9b9d732364bc44caf378c702b655f525f5b986b855cb7071024f5f4c381667ebf9a900a9b328464c886767d587c8c07e588e07cd3aa3d676e1631a904dadafad7129f529cbdf248c8040c103437275f36ce600d4dba3fad39fd50f6af8ea2eb364e2a2a2578e9a38d9220b8b81c3a4d3c3db6001a50d1e079d90737a8b9a6f4a81e8920ff5bb933bb8759f79c186508300a9465f3f00fb3aeef45d923cb5b74565fa268c3504d442bdd3c33e5ce4865560fc116e45efb5fd2652024cf86d912fcc6049d4885808f77b1d6e87a79b357fd803bf9012c799b937a86ecfda56a0316a4da5202791c782232a509bcdbf8abc562da8972b542e9419a30ac59f4b0d21c506a0311a9f933b9ada7ed6c1b97f39244dd94803e592b372b000"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f401ff9eea889d080ae7b3e4964d28fb2466a1a86dee0e1e017006c1e4ce4a63","proof":"60f048ab1f5c5fa86a4decaedf4f3b010f8e5d69f97d0ffccba9b4760cf24a0fa4a74998041d8253490dda27a379f836d73717df77b201796c76e71900e9d4598e96b90bde3de28b50eed99a7ca5b438c4c2cd23d7f80e5203f3a3465d10e46242d4dbd2151804a76a344fe928d48f325c1fc14c0a8b9122bd93d619466b6f00081ffe13a0ba3dd2d9f6da167e665f5f898c133b01820efa7e31859c3b06b804c65f84b2a8a0fd71e49707c9bea04136c01b309ba24ae9ce158b424c9b96910bf3b9b550516dfa6a890f2e5f50aa37bcd68c3fa73f18d942956048dc10dca002443e0694fedc14e8716347640cc3d4563201050a822537c69a1e525e3c75674d7893515f4aa1ea106dbe4d2913e25aeb4bcbfae6c8293344e80f8abcde077c37984a2bc18d37ff483d8038a1411af927efa38ff2b1f7d235a71122c56e6c8a68ced8d2df6442cbaf8b9ab52603d595fa241546792bf87d478e38ca5c4789ac2388a51c4964680b56d3fffc54c830ebb123b69711008cdf6ff89781173489eb029ee3f0bd6cabda88bc6fb4ce0e73caf533cb5bd5a615e2a09f11a5647a63fa5b2e436ad0013d7a0cde2bb2c09edd42a4b023648f8f2fa89064dfd8fa1694dc4c0e8a6b1aec4d6c7db935f81cdeb904d62db46af38887c4f4a6a0e78d7dc8e104263fd4cccfaa93e88cfd46b26ae7ed73d4a0d11fa5b2cc2909c4ad2e89d7cd1128fa34f8503a3435a84f7973973d6b78a6a1c75d5412e305ec6bf185c532d33710e1672dc5e7f0ffa04b30b97d72981be057d1a871c3066c0eb4159b2924da518e8820e39b495a6e58465a56dfe761c6738eed28b1df58c5ca45f302fb336300651ef4ba634c91f8485bc7dbbbe47ddcfef74dc2810c8db9b227da1123e85800ee72d5f7d0f31edbc1f8c1e98ab9d4482dee42716139305b47300024c6d2fd04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b60ae85fc7e764e1b17e1769b25d9fcf6afab9daa354db37095d11c10d000531","proof":"aa937c95917d1cf799454544b95c209131e5fe2dd830f4321e99b0d4fcf405301c0f0db77c26e5201d61dbd68cd0cb0f26eae3e5b33166bfea4daa9017675e66885d3b4ca520cee5ac8d1a82b28cb591aee0778ca12379a3cd4988902bea1734f06cc8ca219f0d1fd1ae3116ab0fa6038b8648b81e8904b31ab1f2bccfc0573a13ca007a6e4a6f680e92b4f4cd89bc72a8aa005106aa350dd2d143c321875a0626d6b26c4aa322f5bd4a9be94d3960eedd4d8189bd107ad40e6417f3b6d1250c2312e0ae6681f7b6a28579532a5c50bd3e98919001664234de6cfb1b6e12be0392a1fe919871c2cf2e2df7989b059c526d3607a9734bdf6e497eb6c276099017802d14bbf7da54557c3027fe8271cd178a41f60ea26af11221c8d44d7b1a531c4eea49e687832a7fa533cdae2f679f7fad2a1a963869c0d8ecb43a895c256b18e49c3b8abc8e930cc94106e6085a58eb44afbd5be4d8503d31e37ed23972567be4c77d5128afcbae584ba3d0a22ddff7eb170e96ef29c3288b800127997ade11e6540542ed31341dd30e9f0788273e7c83294478d6788df66ac9e4e3c79c6110e04bf12c1d461a11678f78944e93dc5a84fc5d4acae462fed7e18ce3a99e4c6870c43c798814d903237e1c842cac83e0bdba783bd0d3b786bb82cb6499027f1d64032834781779faecd294ed9032700496547714fa56e08aa53e33506dc0cd2eaebae00141f1cd98440a2f6910563032b9e80450d1ddd984ff3cb8d975d95f07da9463608ab410e62d6cb27c46e2c4bb0054f38bc660e0f5303a26271252434ce689b48011480ff0e1faf66843bfa9c56b54857a2338d86cd4ae69dfa2ed192263c15824610f9bebed900c30876d1a4cd4c49993a6acf57c0571239edfc3d0065ebdc5622e27428158bcd9e78208a1f261a54c3fa48dc0a06c8288ea868c2b07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9c1540d63e55b87e353b23e570e4d10be51373d139690834b5cb667c4fcc2e1f","proof":"e0e1765a8efd518a38623bb8c0db0547365f53e85d7261bc3eb9065f6f948c1de4b4d593a4ec7ba05d178126d7e59308fd923f836c7587ec30352cc73107632f3845e99ea024dc0de1b52cfe398acf0f8670476ea194e848438ac12d40ed493bcc4fed4fbb9ba7c830259227214414482f8b1fdbd47b790b0f6117e16f02d23ff7d913ef21dc4d72a9f19ebf5093ab90b3804c7bb69313eecb3046eac9dbdb0dc2efcde743f1d806322afeb5017af94b83f9bf1369c92efa0609a4a3e2dfaa050636fd07687069bde51afebc0495a38c9f6f0e28404f5000a92aba0439675508ce5b4663644bf229c05fb5c58b21e4bd5f4139a1f34fee96e7ea8f39e1de3c12bc64cda5e5767776a6904c0828e04c8b7cb853a3100118461b2cc16b859e1c49644be22019c9f0fedd943542570cf95427d4c95f4ca00a3cb70579642c125b3d168e9249dacebfa9a48734db8d10d5e7b53e82649d317965d00a469b5d086a6f60f6e808ec8f5ff5398cf79240e5ca70098798eae9076dca5f779db2038be2035c8321015c10e6c8fe352b168e60a663615d2deadaa39bc8c380a8a8c29a1a04264093a78e5e1a785e9e09d377817180f63a3803b92b952577f0d35147b3cc42e098dee7956d431525e108365b74611002a9f0d0195405365597b5a49cd7063e0e6a8d661562776776b51a1597b620cef321425e0bba47f561915539250a9820b2ea71c4553320889a2b559cec78cf62d3579ee7642a174cebec537f3a7f486af61951e5b61b13caec39ffa12ec193a4ad50226cc6a1afbd326b659a1ebdaf764a37badefe729d9f7dc2aef24e6c4d9bf3ea9d9de10c3856fc6a83f61ba5ff7ee3873898a90112087a1c5b2d5dd8f8ff7f5e8fe1b00c80640c4292ef075c53068dad027a143c773afdce43b60e86376f831a0fc901f181ca887121b29e40cc09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"944f074147a709273897fdeebee6b16475e5c731473aeb47424079759aafd569","proof":"2625a6da178fe143263b209bcdcc2d76e0255f36f8ab25687eb081c2b761662b14781e20c31a0a3bf458ecaf42f923a2394e4adddf8e6bec63f5381e7c033d32c4d662ceeab48ed25238a6d1c7ef14cdaafbabbbe78c5621fbce156b1df42a0d5ab24977b3baa05c1fd3ed6ea07a441758dd42bd1462ec34d774e9d20b46ed18c6ba63c14bf74f388e51942952e15591acf79eba25b3d03d52f363c49a844e0d08c0bfd27e599f23c1d526f5bab58e2a85dbf0dc92b1eb0791054eca1a2bc000b9cb3557341e1b9ba3370088306531cf92e55cfece060ee07258297f8cd9290f3e232e905f40ae833ad93704309d232bc6b86433e261eed0ea7a420dcd6e1e5baa77e651e96ee6686cac1092818556120fb465cf79b1bf4c4775e541ee70fa3ef034efa585a902dd69415b7fb3a0677f286ccc57017150c64794f976948f505e62c18e9814c494eb6b3bb2977c70e9a51891be37f180745eaaeedf115189ce64e08622a954a9f6ddb5e1e5bbbacadc021535aca24d71681f6a3df9d751cec86d1c2c2b69f95b4df5f33125ae6bb4456f79b111e439d6b5c093c90f1b273ce6557639fadf386ca0235f0c5cdc6b68f6ab404423770e2121b27d0cf758b326f134a0e42f4b0b5c20668cc5a65ebdd05afdc7186234577493db09181ded2d2acb7b669f67b20565112475ee6bf13ea97ec2281cb1a00babff4d123280a502c95338ecd26fdf41b3ba5c6f8b10ab7ac779946ef0a449e70a5e6ba485e941b0e6031f62ebeac8371bb747daf19537d75eeafcc904b09d503fc9a27860443d5d59a90c3cb8d11a8ab65c0c73b24f0f8d96467093a41e31c52c566835bb67e0a98ee638e0d56ffdae9e58d9dcf91b5de45a2fb444edf650b99d069f8dfab55f4477d2088a970b30ab61b72b50124249682069a535b6a2e346193cae5ca351a0682fb409"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dc76b4aa07ac57a286ca424974c5de0c5643fd72eff12ae20ef6dcd1f54ca707","proof":"d8fe4ec04ae73342b4c8d84c1dc47d5f4026333921b4bb012b89e322ab5f885b3aabd629741c2cc136b69c678d88c1e87863129f41fbcc08051647f7e732b529322ba1cf5c78ff5c7e4e74d31817a2a914e6ffdcdaa6a4a6209c660d4acf5e6d20b12d4efc4135f5041d587ab7531345458f838f3b284ce07db45ad1e010440a2b4993f92ba68568aee2a5d95939c433a4189839351bd565851528e565cb5e061b471048957eb316d4b7ac12a9d19117a40107d97f4d85e672421ea848b5da0ded386648dd6fa93a9ddca329393451e9efd6c6d3731b91c25dbbbf71741b710beea9cd562261d448dd26966b6bfd9240e6590927db10493818157054b29ee76828a06512750f3f3492488b618e932f377f93c98301a53b308d2088519a7a854e72f90332ef826adc2547c4c5a4ab5199771b9e7832c4cd7ad2b7ee3a3de8e52bced3c604b2877c4333b99cc5e2baa7898b299b5468b008ac16928707ac63e24a76ecdfd1f57dc3e5679a6f19568be47433a8f9e79604a1a65e09857710e3e73fc684ae9d491b8e9be0babccd03623a7cf11bec6398eeb279cdf58428333e9c0da25cab57ecec45e71ef48f36fe59cf456db251b3e8431ba7e7330d1dc25b224d08559cbe1202dcf538762800eee0defbc9576ef30c4fb45cb5d88f42e4bc396dd232a30b3de1e4d41ccc65ffa77e550879bd6dec07c2d24748012fefd9a0a41f5ce10700089b46ecd2d106f39fa3cbf91cf8fc7711de164dfca1f5357a85fe2ed616bc365316f47de249a5483fa2b4428eb5adbe711d0157c2b931f97c9afd300c265d9c9bba7e229e3a5757d7a67b83de8bc9c6599077f40a3df3dccb57db3d92bc7751756b5221d7cdd2ce357f15fba3fae685c948da1004137fd953cf69042e5c2a8dd4b70f5cf2fa455e61d8cb989661536e98f9f1d70517172d52226800"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"947af01b97177eaad2b99dfa4db3a7b9d2cd107bcd41bfea657e037ac9fc0c79","proof":"22adb3ebe34adc73a9d3883bbaedca558919ba1b2e6ec02bddadb58a2a808e32800f3f5dd56535035081d3c0c045ce1c7c4ed7d6be4421ed6db1fada8a82610a0c1bad8e53cecb3b86bfb7821f17f6e071321844bc1393a73f43fecb5337ff28663020d05f3afa03c0bc91b199cb50f37a71a918a2002c10667275c3c1f4cc148246fa344a169e8f7e02c58e68df38d69ea1efd278d7e8434f6ae1a791ab570941abe1236e2e47f2473ec7be139e5010be58909a707e5ca2dc85962fdde7c00448889bc4642bcd58cdb39d99d140e3841d74be77e7d7e3c0e57f1b45c2ec9c016ec5c514c3bae0e26828084a6319b146963ab4febe02f4ec0aa1d67a7e67c1248cb5fdb2f796da7f17b3a6fee8369d553429ba60c4dff3b7784f2833961ecf30c02905e6d249f8316b6286dfb4fa1a9c93ba3928d4ea1cef012f466a6cdf4f5bb6110e22150e9491b88140b042c23cb16400b6c9a2e7c791bf2ac4a57a739b013608deeeecaf722a1c5929ca1d6144479cc92b668ce8e1c05d60569bc01f801abcb36ade274a5ec17d638bf07d04f6f0c0af4e182c0255e2036aaefd41489877065f596450dce898f688f6b23f2472a1189d250b521f9861ef0b55762c058624548b8dea8a779ae04285d94a1e9d315df9806624f50329d84a3570ddb902ad5a3c142a7c3bf9a697ee13996c0f1ff6fb4e851cfbb29e74cb5312541f77019365ce0e9ef2463fe90b65cf828f54818a14979531bdb2750bb3475669bfb884e472d04366a9f044717b3685ed71186e756e6824d509d07ad78f1abebe9708bf2215468b67fda30c615327deb24779455ea29d48727f831f545b41ef4932a17f4f40b001eeaa3e1448c37bf5ab3c569a7930202c18f6bc57c1ac371013bed3c5050efda8344dee7fd4e4b61fbd0be6411ab5832236f13a180e0b99db4f7925395b07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"881cb1b7513e01761603be7af331f5c8d3a95fe02808cb8179e4ad852e9f2047","proof":"ac7bb376e0f6c834b0a2eeabc05b10b7e445eed2e0ec62cd7f442dd8debadb54c09f658ec68b278bb317563067ce29fa3a1375ab24c5e865e43be209bfdd425536aa913f17a1eb114fd6e720f4eea9a4f0147d2277dd1c0e8bbe7c23101be042c288df7db3a9602845e50f764f07dd96a9a07284c5316183c84f06167e91590d02707316b0d3e772ce353b1c873fc09cdbbf126129cbb2c26c8d793f817cc206fca6917f2e220baf6c4c653a9827a7b8c65ee43187bb7f26d387fc05acb4b201c34c257d33f6ef4733b1e45661193a237855da44926e5e38d157281f730a220f324c2284654b979aa3adc508d8430ba01635ca68161412a0358239e463a3e44fd016c629311f0d80de5a06c85893f9a362c78ecd9cd084fca9a66f12850cb27da0442e98e18f21fd341ef8e1042862c1391e41182c47b4f124e94b19dc3e161f50b2338e9d8927afd2e47b23b33509e95d95301c9a8a10dfaa60f5aa4e5bda027ae841e2a7c2323a8b6f6c52690e473614a26a13430b43cb3183c371909fdb049c7d188df11c9f25cb20d8e6805c700c8532c80e82f34265ae0bc6ff8f9a3c32501fced359ca31545cf6536c295ebce92af72650f2ba489829870285bae787673af39ef8e14d1bfc0553807b1f45cbff0f91e14d98b8c1a198cdb4cf50ee1102347c3e9bdeb1cac7cceb64976ef0d889c5c3b44bc14130157e9ccf5835e5844c00e5f45b277cc4f6f0669334fa191c8f254ab26243ed2af234952b1f3978bc39cec8a56839bada9379f16f0eb2ce89d78bdc4433a7667534881bf88884ec020c4e7cc3a8aa3f597c280acd36801999bfd9427bd5adf083a296c68ba2d2f29a1e44ec028cfa5b168fa0bee29b196f986fe66a8fddaa48bb22ad5c6dbee18f4602bc8d9a6f9b5e35979766f282b75d41f21ff3855bd735eab8b87f3147d5b51d01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"16a7edf23b217d6b6a2dc0c678aa58780b1d0091d11adf63e335f262b94c6f22","proof":"cc07b63d420ba4c95d57ebe8220e29c8243aba01313e1d51def59f9f655c3107a275db6b2d3b3eb457fc961ae8b0da18306d592b1b915e721b782e9b8edb74233831619abac3bc067d37599a0a28957c2819ef5e731e6d1ce75a93e7c7496d499cd4419adbc8ee7f6e2fe77d725162b5d9fa08aa4ef42810a29b9898bf180052a366b2dc7db690d013f0bd2d4cc88c7ac4c413142a4e1791b2bb7d1e17a47e01ebb2532d2a8d406ee14929ef833d2e635033ab6f6febd6a85856437e130df2047a8c97cb1516f6b7fc5337ffbf9ebe839282cb4726ba3591a4b732f869ce1e051007ac7f9063faab363b9ffdbbdffc1601333e581363edc3bbd2bb9565dea0018ec1dcaa27e5835ccab3047bd07c1bde5b855989d030177e2b99d7e8afbb0d44e095ef59a8b397d2360bb597b49f5690fd6d089ac4a87582be1c37dc607f3749d23557767f1941bf4ecb6a6b499879177569f43f4739553f6402b4dcd4a52539b4f77dd9fbf1fd25b7ef2e80eb22be74e9e3de972a07a34cb8c638eab7002a656cba2479f06522aa9032d7f3bd69770d69b3e09a9a6f46a51d5f65434869726d8c804447f06ef692dabea8e0b91947e9e476bba82a6d702ae5b2baef039a3c71e8df7197322678c196c60dab04508067961a23c1d6c31e3805f32a7e60fdcf711ac0101464d2ab8476e5af5f46e66c2e8b86893f65f5db118bc5fee866f2c366b0daadceb7183506240e34a215bc6e12facc389083c4b45a0df9b4be324b7f49dec61d390b0285d8b6a26b1b2b62f727a21a2b5442362d66e84f971d608a85764ea2a9b74c5bdd3be4683a56782c7b48716e9d9594adad1401afaa90c162327665ba93548d9ff92086a26248d0a063521f066ff5d847acd46c19afb22675dd0a6e82c5a09b21f5f002ca891e1c5b5c0f522c2e0b03489a26fef841367c20ad0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8698ee34311a3a3d1ef9c04a8cc9d8dc50b74d29a7f83169b129b90731190428","proof":"48e0c0f4937edfb11f29e910796d73e5fef597f94d3cadf8ed77f29c0d72d72f549e4ed2af29047b1cb433e6c007dfcabf567f3f051d2ade0819aec757663b1202814ce241abd9def504783d68449abb688c5887780fc96696e222291dc4363bb2a26e7525bb33072fc8fe927f029ba3bf13c3ce0e103ce0e9c4543cb106486e164c50e2037271d17868e04ba96d5e33cbc4b2f7d4045c92a354138266f6ee0fabddd0e6d7ca0815dcc824d332336e58349ad1477bc9b1ee9a2a0a9cfcfe29060080396cb9bacaf890c9557bf7f6e6db2e83ddee1db842d4663c03d6c283d20d40e798df539fc7c80a8e953fe8e0ab465ea99f61d72dfce0b50acab64c09912b08c806ef0cbf917ed3e3a09cafe9693efcd11b35fe84342c01eda18e2fdc952a54e8ee2dd75a60009304eef90b100cfcc1e95bedb2210b8d1f56e66f80f3944b928ff4955acb28ae4b9305c6a1743a17955bf3551411c190996b9219cd28603612170d961aa3385c7733b341e2450bbd10782335dee17b7cd99161473cac3d1746613c6ac27c43e663951fd76c80131b8726cbc98a5f46476e0ac1775608a17742fac7d7d0d8bcb5ec095e230ee2a0d1ac15b5bb3e566fe61c22c685ef1758358866b2db74688e57e22b5fbdfc764d654eb05d4d2c0899e3b6caaadcc3611663922b0d2cef258aa553131b86612a9eb383ebe145da023e9e2305318bf3e9ba077893996786ea3307c86968698231f76cf8ba3a209cf4ddad925cca3895e52c2a2acc048aed463e3050e7aea032162ce2a23b1b0f429ef1473e2025b638f93b5c76b697ecaad7d55bcc074d77d6832d4f69c61d818ec50ce4fa541e7e15cbfb289ecdaa4219fb70314f9c983f80f5680c6558a95a245f1ea06511ea07cbf8f700b2bfd54c93ff6206da44e87de0f9312ebba80adcf7c9ef5fb2ae370a354f810b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5c1935c836c1aad980f49255fc36409e6ad0818fa2b9f649c22fab38481a9e0a","proof":"6e69cc95a8a6acb40ea09d22e89efe9276cdbec73390e307a51b9f02db7c6a6da40ef33c53a536aa19f6ce71a00c4112fe9c9c3f87ba8c100294f1a2da1aaa5c0ac4b9125b6a80da1cab36b14ae25e4be780bf12b5861ef4e0f1d835cf403c010eb154c5e53540431656537432f40421723979115a34bea5dcb93fcde90b7a0c2457a439b8b89efd4fd3effda1a3906c57100360acd00c16aeff01db2b2cc203b0258d16cf3a09150cfad10da51d327f18346655b54a7359b30852164a5e3f0128902f73ac8fc48d6e2509ed74e4dc3011f13c96e84059150febd37fedf7a200a427ac1e6a993bf32034163b3f487c84cd39e0285c7ebaadc8939ed7372622530e187e470c8dc3f382aef8efe1203f887d936b676c5e55fb0971ba908aef1b621ab1ab0fd3abc320c7b81715cbc7355cab243f11672d5faaf71c90104494e21a38e89d311c60745bff83c27d79d4bb0f8636892d51f8121c91f90877bd976b5e58ce329ce512f6d6fbbdae3f7bde7cc35518a5a65c619e106b21de7ed9b1c1751ea813a6577235ec19eb9e08383235348ddce16c9989376d3c0d3075df891f66be1a5530a97ed3ee492655e08c89b6d54a7bc7b603da4ff520a45ce12acd5a048e95ff55f84a792da2245be46f3a6d968e368b25d342b52af3abe75d4b09907abcf9d51b150e27c50207df5f82664e7f6710d4d89176c635e88b0d0e3e1d5a2f2e7c92f8ea2e42e8c95e9c0e3e59d74180549c0bd7141f09a9076f6a3137593758e0fc4549d79932a5cfa6bf7001239dafdc1d302d3d24d414c85cad473db3789c84528614c9aaf4d5e9581ec102c02976cbb8d642e4939b593e8372e703b250f61b9ee2de85d3eb142923c4e18f24a9ff16cc65e31695e2a93b117f2dfe7d0356621b23c62b29d64df82888aa29f1117f4d545274e850bffc6601026317fe0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ac156cef4c01e957d369be04251687efca82c47479c659726d197efb35b7cb67","proof":"aaa751291a864de18e5d055fe30e62f0e44d561315498be0592524d37c88d242b41bafe822c5a94176fd8074b7927e6595eb1378adb9d321cfd4d3534f8a150cf817b3ee78e7809bc790103e17d7574d158e98254b064eaa8050378f39c00a6fd65d456ee4bd3ac16fa8bcb8f9edcadbaf2e1a7d2e76001182bb9d63a67db7792f0ef73219f049fba571bb2590044c903d586a8ddd305ad4b7c2444cf01bb2056e972872831c88b477dc0fc773a5377be452e2f0ae5a69608db29b9fd840d3009cbabebd5c7628017ab7bd8466fb3db2e50a0a19a9907f51b55a21ddda18b20530c8dca95ffafc5d65f59c150ded14e644cc59967e6f80d2b27094a779faa70a2481648e2f3548d13070c5cf5ef1e464570bf9a06e57a44de45d2998778c772f2067bb526d5290674afd2fd02038f9777e98796c0513737341341fae520d8518c64f175e7e65d5a24a1300502e0786500e19ef55b371c9da69eba76a90ac830d38f1eb917a0706e5177ee0902e4474efbe462890d942bc61eda167537dd22d053eaf55472839c3415526647e6404114aa849907b17e709c16957ba67d489281f7470946f02ac07480d7df504dbd981ebf204f35f10f4fea753fb8e2a0b115f663e97b555fe31def86b642367935624f3d797d032f4cdba9d5243d50a59f43f08804f7aaa0cb8e842f0d09df55d3f64bce93ff6e7204d845b0f72d096ef71dd6ac8d6c9f994a10e981c502276a8eb02b98230308ff616cb67b3abfeb5058d6c06c0df980d10aec457b186b071336bb6b2bcf6a52bd392f67d455b9ab69c77cf2dae83b541a4da9d216eefe887023e2923c258a2c12b172d46f2036f3feba7c50faaa3bfb2d9e4c8ae89a40f58900ccad4d9dd97344634d972b919aaeabf70f50691653b3a346090665bb9fe7daa7254e5f60b59a4290e448564bde7e0085e720d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"08d13c057fb38f11275d31c97670550fe3246d86818ee45d6d21b9ca3e2e2050","proof":"58f86287a725d6f841dabac2a1ed301bf5a072290131e16fb2f6e13fdc9484117c61a74f0e73763241993176680109ee05093e7f5d9375ea23dbaf61e82fd052c04254accac180d26ebefd43d15cde3456dd48bc3d06dcb507fd869fd0d6b52162922988e912f0963295bc0d31708d12ff59c8113d631a1d4ff4936114a98f58092f64ff92b1e2cc3a71c95b078631a813a9e2a39fcc9915c9130cc5c8595a0db6589117d029bf78c5de3d2d4d6d25e9ecccee8a21687adf0eeba2ba96e8c700689e30bcb59a829a2484308e4706c8b66cf4ffad944abde02dbd07e4fe19420d924a81106cd8ff0ee6b3ee76f38ec2390a08c579b627cc040c0686d20e93d06dba0e0c8de9cb7b26bfe91b51016528233b2363cd48a9fc0bb48209a054ad730c546e8ae23275f10ec29b5011da2c493f1efd12dac1e2a9146247043837ecd01bfe148de0eb682e607c621ed4c439642bf44814c6bea775c162dff3ee85e1b8731c76aeb570029cadf18f228e345dc36072e41917165dd1a9c275442fb4308455acec6eb777ed1610f7315a5282a654f2f303852827d69326cd1d5c4c58a0be2dd8f5a70cdd897ecdb79a39bc41c796a93c4075655da381eed443856357879b18f01063055c0ee389e29e6b53715e1d88c16c1aed963d4c520dd871f4525de64fba77d0fc31d8d346fe8b23200234d234bcbcc2d2653ebe11cc4acec64f84646f3e20151b344ed1716d1ee4f7a8ec4b5c3184375d7ba8ab484c3dbd013e02b53a982161647cf001969f08a4c511f3c7cfe1d942790bb4d80c05ac809ab83cb11f98f9834b1a2a82245a6f921b32a1e65cd50dc8a9bee3673367065a113b816c3d383f2958819da1b3e00e019bc59e821c7b7955d4cf5bf34cacd0e57a5007da0d7a4c8ec1d28b10be939a529d106124f622d9e93d005d59f20bd6eb2d8b88a705"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"364485a922a5af075c55cffbb31e915c5a78a16c1646952e2c4ab0312a5edb29","proof":"42722ee2ab39fa6a0fb3307208dcbec7ddfa4144b0bba8a288971c153fa5fa7da6f00cb6303e41447fe6354b96f4fa2edcd4e83369923288a37180127f17e4551801bc560b3f498af90856856fd8eb3752c0e3fc0812317e9d3bba64c3ab116b6c421a9cb0b8153cd6dd88bb42fbba563a0544c9d4c6e360de1a8ef3aa81de3f30ff61ae72f969cd2a3f2cc54da075c85339caf52aa38e69c0849528ce2a6c06eb38558109b3eca75bcafdbc78a9450dd40cc02a237fc9071733ea1e6a87020146df5ee49bf9a1c17720f683051e9daaf6e1079f8ba58d9812bffdbb88d3be0c16f9994551117acc796bc5c4578ecea55f08c9d47736049bde65867180409643882933f245ce0bbc5eb0994cc3debbfb5701511f4b8021c6b6b54c0251c7d008164e4dc505b86020c3b6a83405304b63d075879505358d972f66c8b6dde9383c5865c696070006fc10f6084830645d3876156331e7775aa57d9e7cf29aed74084e7eec15cec174e0e13244ae4871afd8b34eaf6c01523190a89a9183b1eb6634f2b5fc0541cb123b6588f52cde908ad4ea9d954102e185a0073da342b1052c663646238ab96bc5eb30766703516314158ccb7f86bf748a5dfd844c62b096102a6e93146bce991e461f7484c5ec651dcd4b5cfd90eabe3ab8f1b9e46040633c2d90dc76ced474db83e47560d1ce54923c8d280d1b04c334db11d696e25cb200270aef1c5cd9df1fc23cb400b923fcca8af590c8a75d7bb3728606e416ef33c626cc41f30ee02791495233477e4a30748df1798c4bdb9fbf9d340fe8d704cacf1b26bdabb667c42bb8955a1e1afae922de6e73c0683e24546c0b200cecd9fe56157b584d0261e5690dde0fe41e8b53a8cf47178bbb0ba0c7ac9efec3f0a391ba0f65ec0fc329f4a64a4e16c70b8f7d1182c705892c615a7c52d41bf13721e6da08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"74de7a310a4c145030390c6ce7fea9e4eeda03af220919f08cdbd072d7e7e860","proof":"82ab9c47290cc688bf2bebf54eded61d6c7de76c7a413a2c39d5d1cd407ef23142bbc38176111e9735a9f814c6552bb43cad720c9b0ce9af01234dcc398e1c1ac044c46291511b63d6f82cbbef864a998e52bda53b2d29ee4a5feea00232b0317e095b799405be054c9302d7aa37e2eee5c15a8ff52b7d85037cc95cdb14e36dfa2e14c79df63457cfad74d59aa07fd8b0f403b8b8442d453ee0502c0adb530d66866488e4a88c5092318e58b25b8dbb9a79d2a8030b56854be19694638b5506e3ecab424e4b7f90b7fc360f8b8bb197b36014a4ec9d52fc53926cffb9541f06829145310ea17eef5285c01c1ecdbc80e17f3b67b9a8be94ebf45d4cb45bfc63ac21b6e6a13f5eb158056e881c6a0f32265b781ea70aa5e2a1e837acc084ce5ed670dd537746df5665caa0088349f0bc80525da1ee645d7f7dddfa918050dc738a459e8a2e57b329539ff15112598993d286dd9efc2d07628c73d0ee0df26235000c2be3d53f2139f99239b4c5d040953154b1a75221d12e6b131d7cf643750c58939551b07baed363372a2a43a5d6fcca3eb12ebd23b46186fc626d1d093c4e36d1b747b6531f728870c55f969c9c6feffc2c0a5ea8e981fd1605e3c76a3f6c809fdefb664ad805367603c5833dfddcd47df738bc36ea91f2957c16c587a364224e5aeb396bd2e62456f9d91be182cb0b245e7819c060aa72c53a4790a45973900d02bedbb8dfaabf591090c5cf519e434317f171bcd83591c4b0246f39754440e638029403d6e0e2cb591bf2791b6a865564d5b7f523d6e0a99d0430a1422f6a5e3a4b2a4685ac000bcebf3993bfb8c91acbf974cbd5842c82f5197393273d01f17ecf5173714bede91d80d4c9b9b81629003e3721eafd50b4067272055e08a148229a6f50a60145f0ad444cb0f23eb7018aec48a3910c7ab52971b7663a0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"60f50b022349a776ff24bec6889deec71b574fe1dfd0cf69167b798857d04b13","proof":"72f6389d2811224dcecc8f47992d93bc279a230691b7ce229254a1763f470307c04ac02ffe859824182b5ce1ba9d63ba71063d57c932abd38ab0c2bb31c94f0ededaf352cf1a174a89adb24081e0d81ef5afe0975d497c582dd03734c6394b719868e1d452ac26b91820209340c4c2e8f7f78e8df5fdfd7cc2ff5246565d16653129dda6404e9862855ca05a54bc245256a5ebc68c34999afa2ad5b20e841d04b46ab047aa428af2eb674d1f1574e9c647b0ef0829037392ec4f41b477a3ac08c130468ecbb24fbecab47ba9b33a726e7843c2567f0c47dffcf2deacdf9f9f071418daecdb5a2c56051d35b06e37a34785036d3a4f93475b453a3037e201190208606f9eea3c192cd8bc4439ffbc5219e99a1e7d32fe3f4e6aef72a81eb7d76a3ebc137ba47df9fd3ec4cb9650d747c8d315e5c88431970148717a06f0b5613ed4f66d5b6b2f32247e7c82f41723730cfea4a7d94d63542ec8764b6eb291482c14942b32f0944e95ee77b1db5201f03c71a807aaf10dace1fb501d2d312b552da22070fd8450531bc4cb12b8d283f779f7fa8a8cd5ccff3c88003a6d24c3072e46d8449d75fb0e58a6a10005e7edeb0312f51bf9a38f1728f192431b0b3eaa61bc6adf2e39575e560bb953c5d0570f58c7af7fd6416935317365d31d5cd34c225e6206b85a25e575355682e4921753eff96f06b0d88d68b189277a51194d6e2540cd985ddc78dd242ad02e187814d4aace36aa60cba3279ec6d737b4698abc221c72d92dd8f45dbbae3c95ad58cd4ab2d332a17ed466048de54508888e462f0df2f8ac5d3de4052b012f0d20b8e12dc14efa6cd662af1c4d52a7bb7ff119f902acf6ae567c7c888ff05d8eed0da7d859707e437afc5108844bdb61dbc8ced508ca9f94717466022f3292ebd75d3dd60475155d46ce326c5c196243db4594bf0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1afcc604664f66b8671332a19561b4f1357293140c58b20ec00ac7a18a10a422","proof":"8a408502dd8a9e58eed0786cbf2b5d5cce35e22cff13fb75f28b2c1ae6683924a82b105d45737ad062bee2e5c1cbf5bd3766464a1cc051cd83acf029d5a6cb18787e7e37c396931b360e745f970375e8eb57a770879523d6c658aebef59ea33e8cd414ef311041d8080caf53f7ac9118d212e9f940086f6322bbfc280fcb442c932b4d6a5be13fde0f3411226eabe919e9e2e9a3d917bdb81ff07727bf7c140cdd7343fd11112cb08d110786cc53fd654e72f7c071acbcf190291ddd6c20e0039031eb6be88e7190b39557705180d4c27fe67294faaa187b4a9bc7dc6099350a98091513040fdcdd5b0c06fde378e5b9487515b3cb3a866738b1e359c43b1d601052ae40af3a9bd45990822dd4a00574b7f6e64d2a10e37fa75bdf6a2efdef039cfecd6703ac02cb9b349dd0d6ef0f4d366aa26420b4ae5a7639ce6d61f1962a6a3c8bbadb432e1ab33ec3c728acd78c25970774d35521400eb7426414d7112ad007d48483419308acb25adcc0930d0f1aed1c373833a295a8a9407218430f35d65769e11940d90a715dd36f811b44faf2d222fa35ad479fb90a2d134becb05ea61ed1ab171a13e4178e299b9c33e615b75967b6b9f78d12d075fb100da55263d011b5cb0f78a6580d9b8d17221ac274b5b83cd45f9c8bba2056a4ad9729b3561ce4cf2d904384634cef3866c7e1e0a4b242102711bed62b08f7e1483b079903104b78852e7343418529e8dda87349d064ffbba325b5f58596e6d0146a01eb13a63217befe6650bd8dd0db111b1f1c1e9c03e5f67251be04ec530a7178bbf3030e2ca2c9902dd557cf5e1293733ea51f10fd54037c2da62a32f20025328201721bc1864f45ec4aff8b735e6f07f640ca21a8f84a916622de49ab75be4f81e802b8406891b24f01e55828235caf844995494aa033441a82a7fa36c2d9dece6c02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d082185236a957acdb9015ffb11c2bef3fa09f65544fd19451969f515141d904","proof":"1e0dca930863533a3cd3ad0ddbbad8378c95926125f684d680ac78101b65ae67d4496b45faeddd0a4d5856746ca5d789c8b11843a0d2bebf3670c121c8031b1acec15b7427f7f47978301edc2aceba0155f593a2b1aae3d9b3b6bc2404f47f718acf47431fd0901b11245a65f6e0928fc7867371123858eee8b9f6d703ebe5498d3359020def19f8c4559f5629c6f3bed5c7fa93bb7a51a656b6d63f554d7e07b0b73d1b204de12d35dda64677c4b5ef8150f81cbe3511535665df4da2634c01e0f051c5e7ae7c51ee4353e42d60fc1416431ebd034a670551a5afcf70694b05a001250ad3635cf3a363b2694657ea080acdf3bfaa7a8475ed79a033eda7760b722e03f45ccb917350762ef42779c8879ab8fc37a4361a2cc3c96e6768f64d577c4d105fa456270a44668eeaef5f4d3ffb2818b1ba7fb42b1efcd42941312e41604aad961864d650e08c46bb9c150c033278e756cd1f01a58dccca0930bdb84c5611e4ae05ec1b7f5b847cdd838e1b2d48c76100524a586c9167cec446c5be10dc7cd429b34de3bf4c9a6a348dc79c768376eba1c2e6185ab5b7c9bd5a763556de4ed62d3c862bbc471bec43766e2b2b1da163a9842ab6548fba54105c96b9182e3daee5896db3d92f0cee6e14a0b3ee2e99b081944458222e4579d0f27a3e0632b1d66ccc153dde4b3750bf3b1db62eb7422da161f35a58483d139e4b4e566750a2691b83c744884ffecd4c37973594edcff9af1802d8a8c41722eade857a48e65e8af9afd96db68e1c15ab70ffab6c3f44eb5bcda7f9a369b6589232944560700a7f1cc36cab00ecc1b8e9836aba6f7bbc9fe56d7b1052b569ebb6c9288615a359b6d192f833650e8e25b6e793cbdb94941c2743871ff30811c9fc0dda9405dbaa11cad757403466a09dc6853273c5143c6576ead74f5821b92580af163800"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f42c728f19384d4ce788bc3bd0fcdb34588e391b38cd055062d88e63dee0710f","proof":"ca2118200225f71e6b866c00181623eeca34de56b1acdb2967819bdc2519d574b4879dad6a2977a1834ea20408a23bfc4dcd42511395b3dff1f1102146184a0b36af182e37b562676caab9df0819e36d53cd96c0960867727e3261adc45f3c73aed4c7eb42f57b5c5ed156a3ee8f5d7f4d8b16afd09adb2239bdf6edfba6bc51eff397c15ceb1a05fa90386d43c6d5c9a3b53ac8a550ad8fcc102dc136b91c0a6424c1b076a9d8bc173a897e7eafd13d7bedf2edbe51c639084aad4964f2ef0ef1d596d8962e96b001e0e32d2f7f5e21af5bb09c12582cbe030091edabd83102047fc218930690c7af1701d0ecb2e065b3015509e1fe5368f9c77d8cef60a45fd2805e971eda5fbf812378427aa0c88f4dad8513df07b91d3ea65d7a99e3c745c6fe5456415a1b0f07a40bc1c8638b89121602f1b3ea2d62639539d3e353c45bf6d6faa30054fb074379d73799984350a0691a4fc28b5c6344e738afeec6ac435aac4134e0d78c75b17aafd1003fb301e80e6f34fed1a6925c41063fcb36f773f4c1cf13b6b5ca8e90309c279b820109a8ed085da6d96b802208f51f8ee4b87e36cc7f06cea1ec5b1aa8e76c8cb2634daf94ef247cfd866bded6e4da63423476b293d915ae17c7f1dbeb42db9fff69ab61972674eb7792d084928f3020941833fc08f96769487565a59c10ed6f0069373f6e7c9efe9cd55808ccb184fd9ce9254c4f114ee87eea9a7c440514ad240c5329b7489ddea8c938108e3bf9bf99354382e23fcc31495cee75670ab4a948042ddd50f521df060c7894940e1eabdcb907c419e41f26450f54ab87b087a1d8d26ae82dc91a17e88fb81c5dbafda4592f3cc78f2b255f101318197a284566d98163bc50b225a0f413d465631378a3d0820df8a32265e021532e2329589510290b3a6ec5f24bbe75d7a1dec7ea1a07d04001"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8c26b77887eb0530062783e7c653bb978a1d83b75582675a2baf3afbe7937706","proof":"806fd738b90cc64c4bbd6c06dd69cd2849e78efcffe8604b0e24051553379c21f0320abadd758c615bb67924ac4deab38eeec4aec7bc727b6a6053c18aa8466ab20126bc2075ea8006aff5a11750ac296f55290cb123fdf78f29d1cf3c44d3545a4c806e0059c1a8f1707f4981c099368826cb183b028973831b7d6fca7b545813f8fc62c4a231e2e79049f34c1caefffe9ce3ae25df7535327ac12279bc1b0fde20b136eb7632908cad620f0469eebe8258c2883b74d48123b573319bc3dd0f64975770c90ba8f8c5d87d297e88777a7c6358181cb8e2e9c31a16867f2157067ed920671d5be727fbfb733e96cc730be46d82a69addfe07e7cf45d798da170574125627d1f4ee2ca15fe80ed2cca735f0e8555716a3ec9308e3ef1aa9fcdc5510b1020d3096e6caed577584b87624bda89f9dcef7188e4f4e92f121968cbd35201f594ea7474c8f93eb7785682871e7f8653eb6a7b116f133388e40d10e6a31565070514b973433d0cddbcaa63f5629609529fcc46688a2004555da9aca077d029809606c885e5994fde43fbc7ad866da3af5b227038c8519ce13fceeacf602aee95e8ce39ff3c870dea0d71f4c4e5635f7a50dbe6043eaa4618167087c4224883a526e285f771cf2a77417d95bcff3eaea5daa109148707b3af0fb40fea533c2e26b74cd98081c1af424d6102d2c1a4903e004b114f0ec9a37362c18ed4b1ec8726f83a503e646d03df0454a121be9b9a2d06114cdde33792b93e51f769d5fecf4bc79a840c37fa20f06637d4428febd334c137e2e01a3249e3521592369429ab47777403e49aacca51905433784fb20ff1b342b085c61be8b5dbdfbf76022be283039ae959009f82fd02d93727f7ef7d7c9aa9d873a0fbf24c29f6ff68c0332e51a5db08f24ac9930d6f1b08493bb9df6f1c54e118baab0d6aed90f67f30f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2860168ef8dff0d5e405e2b5cb17c5d63f9b8c3c296621e41a336b052618a62a","proof":"a40b05c6508761bcdfd4d6cc0d01d4bb7fcdbcbcfe7b2019b29269f62e0ded3d688cbd0aac45da6362fbb21bfd672fc5fad29b1c38ae3542ec4564e1a1a7f95406fc57a7195dd709697f4fb5d1bd1ded0fce9561c59a47561383b6071a19e16050b150e531f2c3664b4791704a6fe0c8de1be61ff6ada678156c94b0cdc0514ae572f31ab4f44ae0cac424d19da9ca627914514202256be7b0534b210af81908168d78740335fc80a91a8293db134ea41c234d1c45724e82b8d77bfdfd995d01d918a83c358cfdfebfce25c53ca33de2a641fdf67e551f32adae2aa83c527604d009f37cc0694fec1861c93d2f72920078dbb3e1e2e3b347646f4eb7d2e5fd56306fd29251985de144d956a3946ce2fb71ead7dde3ba6d4104f4457c45b95c30682813c23485aa841584addb7e4ff147e87953b01b94afcee0a8970322e9dd67d0695f1e5dae6ab632e477ef090c9cdb5fb3df946d9b339aea4c4086126d2463d20e689b29aa6e7fdefc04ffc15f54d0e55570500fd5dd29c7ab1188eb5b7a4e7c42c455a157f6727c61e29b5f2e73fb9b0ea2063179e28d3f76d7b7ded2094e32ad00671e6a41d532c76e4cc41bb6995926436acbbaadb7336558a130b9984f24bdfb1b27d40bfdf3bf6aff28b3d48a8c17a2f55e2fe5638ad7e4cf55665f6834d4df3c3ec5817aeb8b0f7251de3cdddd0e3a5b36901a4c7e5e05be3d20f5048cb8669485ad36ced18b2d9acb38c93bb2edcd730d124c6572788ff446739b77a0f77229d0efca8b9a551f73fbe8cade4869dead994414142c48d4a042645e42a6cb92b9cf108ca414eabb059ba121a8b150dcb2da86621b155d06ac45583165eb25dfe1f7221a8d37bb341a3d097210e2fd953088029a584733d09218f568013b3554d1a7143b58e7ab2b329e23f68b50ef234f2d0845f2716f4eb4066b830d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9e3bc1562d20b97cf30b7db6bf7fea92c91eb15ead5047b5e528431ad986ce7f","proof":"e2f96a96cc2ee181080cf8c882fe8198afa6f00fb09e6624349c135b8107d04c3eab885ad6be34e471caa10042ff495247bcbd00fa1449a2d8d2ec7889d2b5735a8c59cae1bb4ca5490340fa9ae7f28e6681164c09d2bf509de72cf6a008c40ed4d398f9a0cb4dc18ef549e07a384d09d4b530b7eccbbffe1f81ab421ae0232107028969ea74c51a3a77576429eb7aeeb29c17dc7e785898c97ec15a45f38e07941c892040f9d01f3d569b38d15fe8e201172fcb3bbc736ea551a56f2c5a9d0265d3faed142fb700147573a3f36f29fd8172bafcc44df3293b08d34d1b8b3c0a46fc196f6cce6660c91423afcb562c526aad2079ad929b959f2d7f4a9a42ae495ca71babc4d6ad2a3d06ac95b8d9cd16d61d6742a2ac8a34600f08cc3a4fd909426d3bd1666ccbd7e0f5c118ac21dc8f6db4a74c2f0cb6a116616971dc823b080ac484305f2aa3f438fb664329d27f387815fe6e83696a1acf5f2ca12444446e6e56371e9af4d79cc66c83a50dfae8758bef5d7c8284bf7675545dd23ece3337744476ff77e6e8f4a4e93c6466c4001afbc61f1926e2c03489c06488e0d0b9113a04a7d1efd09b02fc4d132b453bb5e70a523569f6a9fde9a9181c9d3c564c13b845c8c754adc59ba701228cd2799cbd6da0bb20955eb31a5669a3bdc91a631c847f73dc9e81674fa43b1d93554258af57bb0de4f8e4f1fd5f1b9262f2cdcc6aae861317e58a4e22b5901384c3716ec4a49cde80be25deef3318321c70204b54ee15bb5d790c73e6f0fc183710375cac581ee31fc24851d7a91b07ba4214ba5a6011a99c39113e1c076f7860a1aafe4ad1ef9cc2a55091b9e2849ff9d3442f1140a4ed7fddacc58783031120ae674abed0dd976c03f1c0ce1168aeb2dd892f0833d9ec5d1c7d474d616966bc8809202714472fdbd8008a6d14cd1701906e6b0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7e2b8f67dd35c98c16c66d6a27d3329473d1e62325b203c4c878d6e95398bc35","proof":"04f9ef74b8cd1a858bdc32887d14f2785510e07a258c11590f7fcbaf73b3dd7b92251529a1bdfccdc22fd8b94d5d83e179629932b057daf37fae6e322ff47516b8fa011aa92196a76edd74f73136c7464af3d92ebc2bb9d238d2b71a3d9cb517c66580a2711e7d0da5755ab4d5844221e565aeace08122589726d1706240fe648e8f0c17bcb7c8d225843d26104fb263b1bc01bfeda2d1b254df0fb0b70a720d49d834dea8a115f9702218745d93412d5b533d5e3f172cd89c75f03fdff4d10618ab6a31bfd215df40d7e2a3d6e12daa592231f1574bfe456d331b929a4c8c0c72eff817c9f994f49cd4ec1e4ac14f7995e0bea802aeb0fe8592664302752e4b66ca6d34e52123bb9017845a1a3cfb757db980784d9066c40b9bb3609bd97b36dcf1d108a131620b803cf1df9532f95bb1c1da21e067f113f350e93fc7875167c662d6332c887401e2079bd570dab648516e51ee4395ed0cc153bf3a719b4477d639ec789d2e8b1c0342b1eae244bc50a79c8cbf48570e06b1ec65545896e26800df71582e96b618980571616237310e0ecc3f5124a069c210859b6b56c0d267acb6a3f12cb29c5c5bb465ba654613f7f0e6220bba87a30553bd65a1dcd1ff61fe66680fa9b8f3c1fec5cff187d797096f51a03b0abd4a19d36b30eb9ad38c5aaa4f1b8342a9ce39373e7d86ea8a0c4919d812c251713170aeea84f4607d837288ea9d8aa2704f28392bea749cf567e2743b4a8aaf5b75d3c7105c74a498a11532a5a598d4ca3bbf904c90f90a8859914c400bef562b80d16b37d955af845114f0fda1869a094e37ceb191cbe2efc2d8c89798e46379832432177950f72708285fceaa33a72bb51896e4b5ca188eedae5ba3e9183f3d16f87bbab854888bd2061fc2d07f2babd84324ebfa7e52a9bcf34004d6b20c30d928723e51fddf859d03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a4f31bda0f23635509b941457d299ba9f25ae097d101bb77df70dd2c01d00a7d","proof":"00e470289dec0cfa88b7d7e70bdedac87d23c553bd965d95bb6804fadc1db557a88c0cced65d2562153860b280f8c1dae9826f0e78952549c23690f7015c774c4c66f218d858c51fcd7b7bc40d6263bb97bc8ebfc44d6671a1a0a575a05f9125dec85ebba28410eb8666b01fa6355087b36e30562e9f219f84c65ede45ccf6452964cf9955b1f1a3d62faeeab8fa0e212815360c343a29c4e9739dd7d69cd3028efceadcf704f1c7ddc71d7727a6c978410c3e2e8cd1f4ddc8a61e2a82196105bf0615b970c7169a87fc7fbe824ef84281039e738833a0ccab7cfd2f586ee003f8c0d7abf15e77115d0019a2e413f1d0d4edf37307c7ca741a013e89e4927624f0a5a65216be086741d4c96f46d561f55ce3ff0e665b1be653cfad3636921040da8aff0ec4f67c1761d3cd91e419a86d1d8d16df36dfde49dc36c3c8a1ed8031ba405a59d3f168636b909d59af3e013cbae501982856cafa6205f6d1e9f22213362d245148e69517296e64396d4f122115820d67e34615d13d3e0b2c2d2e6f40504333a4ecaac85301b9be51565514130c1efeb95debe9154f8410662d9e8c3c6c32c32a8e2be05abeb74f4b3bb5af64d775a427a49238fa17557d22c51aef129abea100a9a97d0bf76539d563199c91f7a0350a28a5d0c02d4167eeb5ae21632a8fabb1e0db39433a69b3e5e801a6d339e707af7046b5fdb70cc31f0fbcbd5994260f7e80a7ebd4ab86c73a9942e479f27ff1e47c00c59780379308c843d371d8504f48afccdcaf380b56a87c567702ce883af76b4dfb7f70c016ae111e3a5a3e5969df589110fe93ea7317b3dd908222c42c9043bddeffc856e7e70f88a33c32686e105a116eeeda624cf0421cd3f0bda1eb5967fffc3fa0bbc97e45cf480f2b3cd75c8048a33ff4aeb0e322cf0c443fe2bb8ef5f45521f6f934034b940e0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a8c372fe8d2b1ae56eebb21047a0a89b1fc949c5a8e8c13782360cd2969d030a","proof":"8a6883b614c0f469c333a9bedf34189e78608a153820b335a2d902f786fc196d522353c958ccfb5f50a49b87b615c2999efef7ad1be2d0a1e9b5ec772f5cbe1fcc6f475ea666ed8427407ac34606d1f3ea841bbc69de5a896af178c2b626314192ec3581a61229f5bd85b57ad85b97445989a14779944f86a58fc90769a1c50e5e00f1bb2490f903839a7d743e05cad6e86671974049ba1ee54ab2107ca7090767b8212731bc8d355645f7993835631fce5910c8702e89fae7a3a8363adef502ec00edfab92448799ec6d7714abf1ccce0b96825a9b80c079f29c8171be82705b613406a4ee3f2dd4ea70893ee05d756edda1a5f3edf747a9cf5a8f412208948c8a585ca0fd4ebad630a95fd4ebd242e983e8add8c779f7dfa61a5f2ae931577ea655f4c96d574be78662a7f99f733bd674ee0f29cf5c7c13a8f13bc44070b1d7a7371f30724faa97fa9ecbaf979df778cfad8368ae98ff3b1286f7dac2f57237075ab1b329c4a8e9c52e9ee75517e74d7880f148e3036b07f16671614ea015408115ea7bff4b64190149ac9df00ee70d79a17c892655eabe33ecdf66b1f7e7488e2a5087eb0aab5fb5c7cf6c60c1a2b6f08977394a3cd3a674e883c89acf0654e317d68e0b7251740e263146dd04b91cb3988bf466ebddf9cf5958c1bb289423ecfc5e794a439a013d6220ca8e06bdb85477ecfbcc4b73bd2d275cb0569fa2f900f90c004f85a42ba1c5a8813483d261eb3545a5291dbf1cb16577edb17557f7e1fe0a8909c6e1b8248eb3f9007c296decd19b796490c7f8a6f3bfd4d6e697a7229f52c4f5fe994494fb2bc19e356c7a69562711d0c5458d66ba9874b2fe8119933532912784781bedb689bda1675560e6bf17f09cfceb71bcec776cf10ad00647d96bc6ba95ebe1a3613893862773c1dc80c36f3d229c7898829bdffb77806"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"10ff2af7dac3e12ec1b62c9bf69d13606f7f09e5603e33afc355eaa2475a0436","proof":"86c5ee09519d6150ccbe57a6d7b8134eec93c4b3c46d543c6422802587edae2d467fbb4983b8932d36f8f314443a6657c7f9eb51f4a9074164434719a65677023e6c4ae5e19ceb4b9756f2425bb0af0794252ca525bee825dc36420fecd1b362bad185c841a03eecf27f3a916d3ab9fb4cbb0610fc9659d63a746a895ad55903ae02c25a8f79e724a160474ffa9053ce5e1dac76ec3c5539c35c10f649d9f20e6070d5c6ec32d3a82ccedb56bc00a59db145af10131fe38ab2500da51539ad0e23ad35e953e3b8a6c92d79854af39a90c2cce3556f2db9635c34581be425f903dc68ad64d969c195ff8b72dbe0f0d1bc194310d306b8e479d40118af09823d33da742170885719fcb54d6aa1bc436d83f323c11bd863182f44b29290987d1779c03a9cdded132df618654dfb7d026d0e5e96dd43330df8e54f9d4bb637026a63a464130dcc6999fb832f75329afcf002b478b1bf3e70f264f18e894f6569892264145ea590e865952c99dd8b888d8875d6a0746cda2d95b144dea9b524f77e6180e712e5c132419162ac837755e6c74499d51a637b93b98b068768b14ab38c4c6acba0e19d06ba2309410030e2b5c1104d4e16b2bcccb04a0d5ce35a386ce76d24603955f997e0cca86052065ea8ce9d4a998cc1d16f02671e7b802530b4062056ab843d14c94cdb705151706b8a4c3b97c9213e20317c4c467b0fb0f4f0d824a6faa7100655fd7dedb22379ff4e812121363eeec405e29c1c8bcdaf15cc9b3bd635e81725d36ec74d900f39c3d7768ebd341bbef50f4225fe90ba01ef44d835f8d561feac706bf88e9e317a15cc5486e948d2c190235562c92659ee1a1c5774ebb259d573a6a4db834b271257e52611b733aebcf0e7f5063dcf28aa01c7e00d1987432880e6579c48d7ebe60fbb5d843d8070c683331f7b24cec3c3e2fb3707"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8c99be345f7ff14f04111e954ae4dbb3ee4b0026ea799e146ea102ca18f95768","proof":"2afc8f4e13ed50d747a0256ebc7310ba251ecc962a85e54e564cd6a0109b4526ce8684cc4dd7ded533d0c633fc53880e971f7efe29cf1893f32f529aa3acc7604a2126dd6ce512a4afea8cfd5c57f47c7f9182e339f04f854d574554fddfca5bb4b99d8b30cc4f8c27bca6e7b92d6ff62229f1d0496fc591e658e3f3d091e014b8dc4a9ba49e8f310c30cb6589654a41e01c8b42fa25ba0dacb487df35b8f907dec5169f7eb9d09d8e132ff2debaf1673c850ffd5caea2be78f84ef9ffb10a0ec9a013d184a8c3d1901211bf7fc05a1639c0350b1330ff1139a9ea48418b040e84526a1c051be1b08bbade3d3a89015c4c2e057c0d6b798c9ca93bc2c2a7bc3b14fbd655f724c7d9127454d556cc61672a08b21510d753869570010f9e3c8065ba2fcd842914d694147cc752d81df550d35cf7d650398962130b8fe2334f2f35d4d2e41239212a69d537e0acb2e15c71560f4242b50ff883b69991fc3e2768089a0aefc630867cb4d009b9a126a32c643a062766010f2fd822176a7a858f1234d4107d54fb628b4e0ea0126d3b5193a12e524159cc69188f7b1ec88eb319620ab856bb1f6efc8ee8c08d204660283e0a03245a58d38ff6a5805c69fdafa29922b877830d84c260469166a3f1c8f885191d905bc95f006a172ef1cc7f7422c315cc8b26c4b4778aa9b25c5c859a8e8f312284f414f5984c90e5edea0ce7e979198080036ef7721b414a31046e7a79a56c7172c29611bc43b41fab597dead3ef5a04816709e00ee32b364d9daad83afb72dab670ccb7636b97de731e13afab6b521aaecb1a9e8c102b6a5d9e59eaedc44aaade9e70b03a39c11a73bef070ee727784d4989769f660e13bd470ff683e667c98f83bf3018e25f5543b84948c119800c05cab8020ef21ebbaf93aac9f38197bfe996512db33cdf872c24be37f3bcd08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a48e6e313ee64910334a8cfd1a1e051100d4804cc1dc6395c5400ed8eb3b2260","proof":"fef59bd5f4ee680eab7640a809e1b79755838424c6420126932f3451e3783c711eb79dac7f8e7ae0089cbf0610b323e8e6d1d57274080a5e776204a1e547935cc0d3b8289acd06ad885cc17488b726d955a703b663e2ee9adb300c1a2f43583a14a0a9250a6fef5fcc49239827d0579aecb16d21fe1b2bd4dd04e37af27d2f3bfe0aee432a1dbba62928c11a3f9dd3e9a64c95ceb0e129d93967fbf25cf2e70b25c8e6d2e26558902f04a414e900152a4b5859fc70feb58330b78e082ffb49010bdab09b63b0da772145fd16d27f234f479ef659fd26da100f52b33b6b360f0f56d82f6f7419d3000dfc1fcd1575f663eb9c06ced276c3eb01b242e7b444cb5ae2c17e5d19a771b7a8517ac9fbaad1675a318a73d9178e046b0911b62310d11c8cd8f261978b650ae7fca67d6cd900262cf22def298e0987ebe86cad1d57b739d4ef3008949b5a03efc6f9b35f1470e533e0f43a5de67aa52a1b7186a7e3bf2a38f4125995d7c09e73b0c5c1feeb025160bf984615c4d44cc60c5ac21ab6ee39f6038af25bde5c8c83f28f37d4ca94676348c4ebe666f81d404de8be6fcf834b9c6fafe73697cb01a6847f8bd470a369f8ffba85698e11cdb70b8d6bb69b742fde1a54283c86966f539d5151304f2538f794324b87e72ad6df74562f6df2d6460846cd3d2d40ed5c8a50ab3087eb49bb6e452a146a3cf7f0065327c0c8e5296c186fcb45db6583fdaf1f0a5ba91ceb78c248f17010827deb3221088809ba04619cb43a35c08af37a8919898480b4e6f1b96424795478b8bd87a7cfb22ebc5f06dcac219f05e64133a9d42cf88c1d2a269ee7d5f554f2182d4e7cb39e5970cb3999816af30e81862dc32ae83505d81764637a439ec8c284124fe97dc9748ad9061b189285565197a2b71a73d6b1d1f0d6abe81c004b67a62335181abd625eed0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"70cbf436b219deeb7fc75cdfe09aa72212d15fb27c194dea9c07aae26d21cb34","proof":"b68b271bd69510a07839c814af3ea6f9bc0d5250fe6a883c42d0e483f73617722cd61153f79b8b8eb50eb7d1d46ae0cc2a8076940d7d4588cd42dab507f2146e72ed62155b335658ab4d6a0bf391bc95f7f816470382a99b36ac12f2b688877c2cfebdb438f517476e7cd0520e9a451c757ba250372202057ecf246fcbda874bb7e6f2a05df8347016db9ff746b84c5a34b3922b27a192bbeadd0362aa29d602c0a408e64aa1d3b1eec35d4444128580b5403eefd3205cbbc2200860860cfd027132e3c50155e0453aa8ca585b988631475d67b2086dc93dde36309e52252c0ddeda0a02419530e0dfb0ceca11fe2209823378cd2b723e40c4da1f2ffd2c0c776c7c3b454b3cda481c32f279b4bd6697719e32af464b376a9d98883d1805571036bf0eb924e881c1abc71e4a8c9e37617175717f581fdccf7d6948a581d55675866727bf1ebd23916014d30ecebb20ba432d2940fe4d63a798df373d83d4f7069e3caa41a3cd036dd92efc1fbbf4da95cd85405fd40859a997ff28f90ad34308b22be27b43440d641e9b0f5db81d2fcfc26219e073eb49e0e5e9c576621f4c5f720564c121a61af80d3ee605bf37df09f7da9063d3f4abee6d286d63cd0eaa00bc12772abdec6a1f4320e388c45cd5ef0bcd382a5e23762f39d07a4457b38867567bfeb5661a655ce0b4c58a54be192e3c831c7b3909a399251164c51f2a475b9e1287d1c42fa9589b177e10e005971a8ad8f48df94b25d142d0cfe856fdaf266a24459096f7e7b20d56a0a5209c00cdf87c262305b5326ab9b32b6fdf2844593e427003a0318967504eda75087d573932316f4b875e67fc8bf2cabe6635fc68f40e3de5980566cec3478a7578d9ec4f914e12ada64832c3ddb8c2aba21eee050847420eace81bf2009bc00e729853b2525d35260f44c26714e4b058d70a8408"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b2411742cb96c87c0be7d8962329d3660cdbbd3f3ebb42ca31213f77530ef916","proof":"423461502bdbd07b4806d94869b909e9549eb59145ae5ce4bf04c75afc52042d7eabed902f294c6b2a7b3ec91230259783cbb22226d4458958bcfc059f72e55c12665a1920382ef18d67514369aceee8b00ac9824d007e785bd3522fa8df502452598a19f14ccd6719f58d8c37cf1e805b944607215818efd2351c9f94c86d531735dc38e0193599b99770c75c64a96354b7ecd594175aa7c29f1d1fcce02f06393d4ae9a886ebb2b7144d6e88b069dd0203082b81d5c57813b5bfb27202d902e069ba342eb993c6fff59425f25379fc64a2cd9bd216bf7707c7e42138f4c10c96501383ce8b88ee20c8f25d2903f311ea6108556c1966081ecc75401a0ca0590a26880bbfda4ceab72ef435a19d32f577cf7a4ef8e587c6cc648631829a6611921d3bee9216d9cdfd4baaae9cb0f6b5dea30669c60082b8e4e4223eea73e24ae043430a7b6dd17576fe11f6218c7f31185be380f4d276f5eaeb34111c92977a0a9428b230cc79519c4e89eb7e2be7eaf466114c368d82945663daa38bb56a45280e3237a6ac82f61395209e79bb024af7fd53519fba44dc965909368d4acf02d8d3fc0474e1860cb5cb1f0d82d3f948df910e938161b3c715784ae689019011aa42d19e031c253aa98f31f336053da172c61ee013141034441127d28aa309356c13df432ae60630af5f33b7baed66234e554149caf11dfeb45b2faa77644959c24245343f5f78463d5b684f276e3182bde74be07d93a17e3cd51fb0eebb5f0008ba261c6dfd127f2a348b44e14de0687666a1ba58d93ee8d25201925cbef80f4c9e93e7e21a04e372e860d0d7f3a37e2ce864bb54aac03b6748a7d503e395434b5c39f475be5500ce2bfce176ff2d1043180ec841a00024eda5058cd32cb80fbe58552ff7954f56145bb7eed03c3fd30797199602dc451fd69a870477be0e01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"30ae541ad25a7d4417527958ec915dba55030539e85bbc7314a76873ba8b5b7c","proof":"100e103af461ab5d1be0ef5d26c530934937f43626fdbeee25707b57ef2f392996f095d402f1df6be236246b28dbc8296e13fc3706fd8972680ca70dc867c916268cb572af89150d723b403f2e64da0daf64e4a6d3f122f6c4106a3ff43fa20d0ca03a53baa339e670fe2c91aad2f1d2499120c820549cf3e6ebd2ef73961507669b028785b12647e746da636997cb410178fc3462af2d00294ba04536cefc0491ce8e892a33b82cc0f428f3668beb0d7b1006440bc3935f7328eaab18b3dd0073b17df6b4a35e53016b8a2a89d8d89227156dfe6619defeae42503c410593078c88a3c5ea68d9842f40d313e39a81417f8b1d37399bbb92d3393f0404abed4886f95448744a8d54a83cf126845474ce7ca8813e11b2368c82ce721b1cfb5c1a0ea26d74bdb58ced4636b7fda0498249a0d767ff4abaaf01ea9decf39de4db5bc6c64bbf93922f408542368471ca8ed0371347895616f1ce1652ca3dd7bd0a7682da293bdf3858ce9b19c2778a98743b0b1902d8f9c7c40cffe3a0634a34854258826fe427f953c09ead4881165dbe3f7d3439991a5fcad2ac31373e38dcf45d6efd539df2fff41c8e625c767f88f2f69c249bbbb46e0ee43c477e02ea184a7518b6c75f8e97cad0ac04a375479c801408154fa4396e7de0ca211b9b5f8efc5d6618068ebb24ea96262a53c5258bbebaf36e0be089505d653daca4f029f3a44718cf339156b41d90383d5ea78023116a5c3c3ea81dd4b062018f1a0fa168d86f9c556143ad626a16313e54fcd2f3e70b4b4414022de9bcb09c76d727bed7a224b034219218634737f9298890180181ab5207dc7400a1b72550bf0f4422bbe176b07d599f1e8ce516c0f4cdf2bce567e266d5209f89b7235a9e6e10a4936deb03bfdc0f82ca165fff8543d25aa199d0a40b0c07ca6d72534daef87d9833b5c50c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7ae8d11a3f77abdd28e47deaa4d904678d203251452cde62f301b8e57277aa27","proof":"a0dbd625ba52a587fea5a1b254745f43800ef74f5c34e3edf375a7d4180b3132ce8a5f1603642bbb1b7189f0a46198ffe6bc5a786bd65cd3157fc8a8da1309612ac8585e388cb8eb133add857ae145f6d6eb1fb17c938d0075ec279ac734a014d84af2af474133cf6c3121cb14f9c3c87cde1239ce09e028177bb99438bd3a2e4438979cf6997ffadc2bbe626e77a7455a1d3fb442b1ab3d4ce32346fb4860077e1ec8a342ee27fa5ba92bcb34055a33d4ae53f8aa683c8a3a36dcd74cb99708ffaf6b147aceee9bec5ec9254a2bdcc2b9d7911157c9e1b8d90b83e50354cb0ec2a5e250a5f4bfbd6b3817f79a7e5f206be4af054d455773653cb45f6ec1390602744ac2320ecc848bc9400493cd111337c13ef0712209cfd58eb1f988c4972bd0aea88c0a4fcca798185a0a695c23e797057bba68cf3cf2b0a307bfa402db5cf01c78e9cdf63a06e5f6ceb43b7cee934db39771205873c8f3e122513cebae48e8d3d69be798bc75986fab4f1a5ab50a96962287340df569a1bfcaa7b930f952c4423db5f5e07628be4e687cf773204131c722fecdc335fb96680e68999216612845a38b09ca18a4af9ada1922b143910beb7e2326de73812707ec93147cb15e1e501b4746773414c02f0f4195be64440ccd37b56a966c67fe03663fed37fc0d6ac46bd966a2e92644e60f2531c350afdd3e2f335f8aca859723cba9c4788c37c2d5c025ac5ce609da6b85d7da65747375a17bd6ee0490508e2b6974afb19e2636696a7af5b4a61435f7ad345ce249a5685ae6971c844aae238c65680d115b7c3e37accb191f739a6ce153b38ba4fa3ab07d5ccc1fd0337c871ba9e2e5f74312e20eb62f973728c843f0d1c35975dbd0f4bd02f527472dcaafbbee5f872c650ed61468e4da4ecc7bf9879805fd0cd4ccb8734c044aa769909a3a3eda570b840d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7e65f21f421e583ab40a3e87b4182905c1c8565bdb69d8cbd3e702408714bb31","proof":"fcc3f1981dbfd4d350457541039d79c860a57701b4d6ab2cdd9db9fcfa92354e248a1eb343ee87b4241650385a80025f199d8972fef87d50d251d7b88bee4b299a59452dacd6fac75db0d4256eb9ccfd1a744feaf11ab0d2dd1e73e0e2c53a4830f0a1e9b916c512f3bfc6b81dcf1bc81aa817b420774a7c029580518b6e97205087001e57027e5f1bef561110fcbb2c96fc489fb5c523233e199ea7a5c058015ff4555144a42165813fa2651e47d1717c79c1f6386b274d20c7154fc13d820b499a36eddcf92c24f1459f7066d859284e0b3b510551a08378b5c67ac856a20464192f61fa1656580314fa418b7a0b3ff54c4f671f82e0d22e6fbe7081beb5140a6ce172744a8397ee815eeea7c3eea78f97afaf9a364eb2108088117737565f6afe8c8a1381eff65d4267cd31d95e6e47f0fb673343ce9016b98aa29a7b8b64dac3df55a586aaf05ecf963bacfbc021650c6d7f4ea02e8888929ad1e0aa502350a06e85897f1334d0d323ab272e8dd75b67e43d9512267d80fbb2da1134c71ab2739aaa4f1478f852d84f3f56b42e5b9d1bf4995836952cb6a31ca790ca1113f05bcb11b1ab729103e8defacec18e20fc919d9c6d1d94aead501f82c039835fd84549e1a2606f7d03d6d80a25789e8829bc528f5aa2b46e2db91deb8ecb4423c48f00c8b7e2191157666a64ed0955fb73a00ae925576d8d9dd6d176987f8d1dbeef712e3ac2319a117468ba3aaaf08db97b9451da4368b10f57019a7cf2b12568e504fa4322685af072cb6f8f7ccafb5e39e21d2202c7d2b68919c8285d5b693673e6a543a633603147bd900d9abd63e8189dc7ec33ccc66799c12d33f30337e8b7d4456714e3f09b99d7ccef4adde6b6380cd043cda89a5faff98d1353f60ee2c21a9923f4106ee2219e1a994f105d9f0a752d2cde3a8bace1e33605689d05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c8accc79e1377319e69c2716dd712e461e1004d2d03550e5022644eeb9ea6279","proof":"d4a2196d324283a90a06eb173cedd5a77309ad616359531423b1336ef507403678975b2093ed75a95fdc7b7d6d3811632b4ddab20e9c621323aaa14b3f0667248ab0c406dd4615b4a346d7e724b2679b180f9abae60cb5358edc9612bdcd5e51d00313648caea60cdbe8abbc48e3243dd49623d2c5e28ab94696ec180001af11a6d7ac07b2538c2f7f4ce5ced6ce479d9f3d7f842ed8f020c4551d46943af60c8c114af573cc9a432f61266c7656d570a2d279d95466f20075c9fee8f6927e0007551cdc30aba61ac061c4c6a7be77033045926b532e1c791a8bf3a0f927aa027694c73f01524c3a481ca50778685895952d1ae1230a748ed66bea196244761d940b3850931dfaa16e94e8284b8e59f1e820acb2626a8de9a79bd652ea37a827f64878463b145a1b521689b11e9b6a6b368dfab426110e881dd3a162e5a16130bc71c998f271160a6e08e089ce9143930379dad7df41ed078d427a28168aa81142bd321df46c2bdeec006a46b1708fc9937bad2082f70786f1b6e11e18a10476f0be23a4cb3c499d0b5cb335dfb70a0b5d924d1cf88a49b97796681b5292f75952c527b91ac62afae54932c5466813faae1567726c787563bd5ba0d2f9f4556e2a0288d2f54ed2f8744215f87a54bc6efc26f45e92d344cbfe99baedf52ff4202694ef068c31b36b3811bace9571da2fbb174fbb566bca41fde8bbe004223a1a3639045b40b0ce21baa7e6427d1fdfd725861a0895f7169baa8dff7cdaa2585dc08b2adcbdef626e41bb6370396711dcb51cf29c5576112fa6a40fee24b69f5d98290c9e0da10304a365b662a32d2c0a3c47bfd418552e415457dba4401eba04089c8d5009104db0dca506b4bf09494aa0df830ecc2fd201bba4ebb5d9996e094644343bd9bab7c86d945b758e0ee1de7731c433ff6fb617d9ecf2f25496a70e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5407dde0d21714a015f5ff5754ce6c41e54138457978763f148ff7552aff295d","proof":"964a58adf44b86269253e1cebb711b175ad787e47e8345e1953a109eafccc636ece1b925db7ec43691c3a3ab7300a1ead7cc40f7e964b606ce44d693e1e73552b4f0d4247011e6ad36ced06df3f4ec6b5b7ba92c19bf9a794ddbd53ad3e0514f6278e7681151b72c2686b9623ff0c6b9b05e91da4eb7ddc9df363250a840026e5121368ea4373ce2e2e2c3978805adc2ffaad1e06b6893683bfad6e457ee0b0879fee3f16ed5fed631d20ec35c6a5c3f5d96d85526fe143652b9b9bbd4590b0fd7246cde35228f9c7efa143a80e72847db2c61ea9cc454f430a51a4d674f2a03549a399628dd2eaae1d1721623569e8171adf9d082a9418b4ea0130bb99611734caf42490226ac88edafbc30077a1f28f4515858463d8e23407f1d4867dd743196756161c563c7fa9ab032093dbef03cd89c8a43e7de8901f0f9736daedc9702f40727ae44111ca43fc2dd0e7ab5b44de6b8d8d1d6549e50edb5bedf45fee972706068ef61dae6d781c154d3ef03bda81e995597465108ad5433a2ae91c1941db474d34e2a12d21eb6ace6eeff1d85cf3184ad6bb6249e73983a71ac18714776e22c3dad65a7a041a271b40a25e3b303db563bc95a5e9f2b8f11cd77df7ddd3d604039c230555991d7479d86b9404a7704b54efc9b1afbd1d36b1c03bb5e6d3a987f19b07a501f79c906adeca235bd4135f0dd61007897826c8d88ab5eb1112a6472dcaf080cb9e8038d15e2e00fcbbb57d0731ca358c7430fcc51f658799106161dbab729da038f5d22807e86f78455e0fc8bf667d9c1126ce0e5e4509ee62d40ed1dcc67dd41b83d79162b9a2e9efc42353ccef4f66eb70e04a58f686e7d3826b0201c06562f24ff669b9b91d2834532f7519579b9ac2ead23f745033a7d082c584904d2ccd3d4a4eda26dec32859c52637d74bbf7aaf86f232181dc2f1300"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0e4889ad92ca6c07355ea1931cc399e21aa50b5603f0825835db246b9b602206","proof":"763ed9872e44a71f460d0c6344e23a3537de6bb850ee1adecdb33ae1f68e98465a26db9d41272a15f9a9fa3a93486442a1c864e038277fdea705ad5a9f70c72af2eb6282b840888ae1a314cfbad0fa5e19da12a72c995d4c9e5700953cbd225178b42aaf2ef68dce15290966c006cab96c60003d4d73d579c17a5fcd9e8ee86251fb5125ddcd196b0330b12e64cf648dfe53a2b58c31912aa9595a465074f902912dfa542e86a8980215f28a121fb6cd6e68e0049d90ecc2a755aed14c734f007212627223b2ae252618ab06e9a1bf6cd31f834fe15edf61f9ae965147517b0f5c130d2ec15a3340917989cda117634bc6a0f91fe37e5a2493f3841d96f5765304bbbb811c540c877cf4c830a21cf0dae6fb6d6f8a8da650ec82731f79ee7525fac78d3135550671136005b681d4eb7c6f9b0d6947537a7c8a2101cf45a1c54096c185bd8571295831282fc0f34b8cdb8ac50e9ad6662f71ba82f71903b7d13e6e54c1cb75884b46f5804cdcf85e50caf2f0be47ae22e1a58ff7911102f39a3fcc0ba74f382c864608e0539c7fcc8577ae0488c3091d5c96fd106e17f3062f1d90a48feffb8fea27aae6466f88dc93f6cdaf8cf96b643cb990c7e0980c7c352c6c05bcbcb7029b92120f42b0a3412de1336a7a3bf42c2774b2ac668c685e1118d26e6504157e46941187f4a40ba8cf1ed4a3f607b8d82057ed88f36144fd973b347a289f0295a60664e419c14c97732a656191a2c5d037a806f730dd26889c36b2076e4492ee8a48e7e2bfd17a43f39b9254bf2622c76f2d66dfd96077a70237a216b6bef835ef7022dec955197e18e5708c3fc3d80efee706dc1cfc8068fa785c44a299280c4110d04f3e696cf4b61e0a5091594211ae67be85c7eb11d2560c411acdc2d9752e6717ed97e2a45e59f8a8ed4c56eabe5d4e8e8b912256c48c07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"202a82e53bbd72b577f752efbac9eb2c3d2ab1ae4569b4dbad359178769f4a09","proof":"d25fb9664a69374c7a14c0ba81f21b9fc47c2f1705f90391d122cfb7d867ba38cc2e92cf2edb0b9f44dd0d1274b137a6f5f99f8a5a1a12711c406422622acb2d620178f866e6efaedfee2aa1b6dfdfb8cd686bd0f6cd500d12baa747ed14c261c4ccf777081c5feb7ed332acf403acffe58b5f04eb5f8172238cdb65460130221527a4414e4f3588a2e8e2a3340b0bd613b2df3a91a39e0a6435ed732e0f93079a8499de02a51efb8fa61a3adc8e54c2b7ad2055b2eec2cb2363037d0852780aa5cf80f5b37b5854b62eeedbd439d1da91cf1bc8dcb19d18c4ae9b4ef41143058670c4d06bc82cebbed8cb7154d208638ce145ed6a96df0468b9c87a1afb3a3e823f13719a081f7a97a03279903ec0d59f6d0f20dcbd9ec8e87f53c1bf6b3d24e839b7bd8815d9c0091b7f991be6481aec19b9bf8ccbad25898731a3ac538f2fa88b1b5b2c89c1c979de3142c01548eba56141725bbee61b4e474f55e0aab65e4a0c47fa15feb173bbb4abf9d740208f56c0ccf495a020df1a9fe669a8741e103ec7472d81918470f076c94e255bcfff2d265a77742e90b445c9f2b3b259e117bab28e6f512f424eacff953b1f9d6cc2e90f46098617ad62d473b24ae5e59661aa44a25eb45f2d4c40b17e527792529a68a4ae8aebd4aafdd1cfb1b0ae684162d45be532d17e3ea19730feb0da4e9e07b0b86df72578ee54df44aa3ce199eb2cd883601acd764a4a2dff9c3bd8f7fb616fd29fbb2d23caa56907658ea692e37fac6c141d33e278ce09b9011e19c6b2dc393872aa7a54e0eba3a114f9358a26695ef44d2463dd736664dde7fb63ba74849ee42ff4ab3e2e2095cb36088ea7973466b2464aa61e29165a5e954a2ac767adf05843d0fe27bba4338dd605e5343f04e1905d33b2e47bff394ce7b7e2c1e2399ae6380024bfb4f02c8e0b9aa68a8408"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f84df89ba34183b08c1ad4c2c2da448816883650df20de2aee3f077e475da579","proof":"dc0b686ed3c3dec8453aad566ff2ada29f03a6420578437aaa5983da8a8dda51c27e2e52ae17a7eef1bd2f8012a79d3d8f758248002db8634f4c6cadacf5427dbc5ce84223c0e235cdcf689c824e8ee3984d929aaf9442fa2a2b4581e17f312ca837c2aba3eb9513c2dd480e910cebc87f9642f71909a7b5ae6982fbd26bb248e83a1c1ff293c22a203aaf4df9296dffec038596c843e8d4a28de38fecac2d0dcc8b0c2d801bd13a3b20dfed07c0f357353937435fd3cbfd7164f598ad79870d00694d2457f9c22f086f9502172a057dc8f04d6de9bcff848fd6ddb43d74b50f32dfb623fa62e3a59f094930d3096ff364c18cebc676dfc6e68027e978eea468eae7dabc9b2e1f7cd87f206f1f90ef8c202f67e69f8bb98cc48fabe20a0b0f13287a0d447579a669a4c62baed0d3308ddf17120c82878bcc6a91c67377adb2554a72d51b50ec743126a9a1db146b8d5d3ced187a84132f255f6411e3d124f422f8dbb5a83c321d694a0b5f82285d67cfef8e6260422be851fb7708d44b5e354240840cc34960edbbc7b264317400ff3964f3f03efe60b788423786c19b3a434a30379ccbe0db5a046214415201ac1842b39c5602cd321f944d150fa751b81a7d04d1fc6b389c8ddb9c7083aa9a7629b118b05f37f41595b2011393568b97e83d2e2d7ebf18fabd175cf80e6811bc9895b016ade8cd20d2676a4b1c491f0f6f5c4ae06b74f7322d647cb47295eca241cb3268251af8b702984ad6a53a3edec0480ab1b02b895968759a7545805f9abf8e036ea27550cb64ae06e05b78a56176192e492245b28c69a3255a2bdd69b1d4a19d7621d71b215628d4c1bf6a8d1a1e497b2ce50bb2399f00b277beb3b694eada07d1e09604ec54b3ccae750dc71f740ef107162df9b208d514300f2a0b4a66c34778e4ead4e26c1794e63ee1f53c8e06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f6b5e0516b12b5fdae38289efac31ce45821791a47c72151bc54065e56bc3239","proof":"c857944796a1fcc6df34ba769c706caad9dfc7f97f96fc2f7cbd53f367a5e04b325d431e1fbd5dc8f399311e57225e18b7dd73ec7f0902292dde85079bd93758788fa5ef46cb05ef38275f3fecf56cffdfa7ed0620e29b198b5fcbc62397cd3964c1414695039510453e2313c4f733a554fa2f266a6aedc3704adba6ee0d1d1aabdf13f157ce0b4e27cbd6d940216e83b2a9dffe79fb2fda11a7d82125703e098eeabb011aabd5d7ba2726a5863cbb01b32cf3db126addb54c580a690631770ae7f6d0696dfcab8ea8fc176a325827712ba242b6c6313d850c929ad8394e830c065601faf4b50941229afe4282521b14985587974cb54bf41d4aa08337acf0145e2d4981009f8b081b031ef366005b8016f8cfb7767332ccd30acd96bfa0681e92d433bd83e59235bab9e11bdc18479766939d7b6a75a9243bd8de82a971b45bac0a40291fea50e4907a1542518de10498b559e4bedcf00d039b493383323e279a532ac2315e581490f1f5c18c3763013e92d75f537eef7e86ffb809eb0c0e56f8c319fd09794ca405d5689d1d610ab8bd29493bb9084082b9974770eca262669a5e86530b227456c33bc741f54d9ebd738de1d08dd4fd33cbd428fc991b6a46f641451849f83a7f6628c8ecf6561c1b371238d8c1605164004bac488595176df6b9605ed0efb399a97f9bcc947bb46dedbc4bd79c9d484597495e4bf3851161e07f5b5073c1edbdaef7224547054bf254a5406263ebfec53d677881ea5cf97824880a78f11260b0a23fc0f1e3d6c0da5812dd735bafbb1f02785000736c842062fc657e861fd20c42705128508efa5fcf5700b282952c962540de5eee0d2130a82b4235f309092a89bab6d995a2310ebd4f3c77f4507a59c3e9809f0ff5c0058b1a998420c5b7e90e986be0373ad72d7fc0210dbbbea429276ae426d365280d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2efbac8e8bda4c31d887a086af4f9d0e1b1629efe15b5a21d5a3b0b03d98766c","proof":"d635dc721e217124f91e206cf645df8a0c399f45e48a009370e42bc3e654540e2ea01c055fb136778520f4fa7be452c0f5275b746e4d195b1587c44d3545b0050a4e4e4cb53715c565c6095e5af30bc3bf8d1c211f3023cf79301e46bf2c74099a13e99f2d7e29cfa8220f3c0438cbadee285aedb7a1d0ee815cf5247ccb3d4a57acc9010ceb8663bf3b61374776fc5b92cf90f4744e90457b9d58f95e6c0e0107b829e785d4a7007984a404f457d77a0625a40ff501c3f51ca8ac6c7ade400222142ac601a074d420809037ba9e175e17474911745b95c58dc45610952a92026ed1c72db6c4c02877bf125cb598518db620ea9feb62320498acd567cc2fa5747ec543044613587971b0d343ed9b1bd83cb294263d298b357e5a2d315cc17669ca421a8dbb86e9f9d45d21d7ecb40831453b2dd7afe05c52a048a32b2022b93b8cdfac343cce3fe5dbdb231c8b0de7170554bf1ddfb08f67d5ac8a8ba72f6e733e233a75aa4cd8d0fa9abc6b2bcb9dbd393c4f9129ce20099977ec91d840951f1e657b6c363d5941a5854d4c32a5cba93446c33aca9ed7ce9963052ca950a00ef8aecbcbf528de9496f21c8df59fcd194cbe98b3da732bfb50269bae5837c84e0478f43e32035db18a7cbd55d824f39acf41a175212159ea3850052a6177e03498eb48c9910a7cc1a9af3581ac07d421164d85b912c7aab04bb01692818d0c43b27d3c1d3adf110b2cec1d93c8b6956d7f67988119e602c3262cb92ad3ea487f48d8a7ca4a4f681d640cb47a6aa5dbc6761b94b265dc707e4f9df3bc8f376f069e8f8f270aded5854c0531dd115046d25e506430dae1c36a5c95d4099d1b82094d2984dd55e3a705c2df2110f16cf3effd9620eefae35661e9e07ad441011e06bea67d98dd736b98ab4a70a0bcf8837f6b6adfe04187e03e68488788b0cd2102"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"80dd8dd6389d769cbdb6c59cca860c5496121e7694bf2a7e603a59188900987d","proof":"5427d57873331a306aadd9682338ba2b2cc099a26d8bffbcd8c65bdbc246ba410e07fe76ffd9217c996aff062884099d07c599b55b14547a5ad35285c25a494190770ff6fd5d00e3f1d2f2951368d153779e527fb95c89c2e122b4380c4b6e7844b627042eee4eebea473183d7675347ae954d4824a3481ac1d5939998a28c3e7c847c919aa8eebc4e63ffa8dba6bda73941ae1e9d708931a5193fe03943d6005de2cfb9929328da8073e7d0ffe733295a1d11a553888f779643ecd257202a083668efa406eee53bfe51853e1053cfc879b6d593e01df9078a39fd5f6134d00af2ad23046d8379f35be9b47c59b32634a27b03373aaeb66dba05931300d3e23a6424e1064dd5ef905ecde297e5245661f320260c6dc299708c9f48e1fe5a6156781bc6a385e945302450150997c963a71ba0870efdad734cf4b3928177dc8673f4e875c25ef57ce375cf0abe539e75187ddc5537dbe3c782f097338666c1106dbe7618ae16d8e075cb3b3c98fe78d944f9e77da75c3c10beb302b806eb5cbb75c20cf632a49ec1b0e4eac68bf9d96e7435b6edd9c0b3ef350e7a8e3337bae000f25d783d0deb0ecb5158776c3f81909cad05787925a8f405a3b4c887af68cb019cdbb9c8a26d0430deaa4bf1c3107b01abb924767565ad445568c4b0a93fc97b5cc3549bc284581313523116e81a176855f5d946dfcd9b52638891426882f121d6cee3feac60de62f933f777fd9e1bc4ceb58985cb0f6fae879dbb61facb4904f0cd40b153f3bdabb4d5f67e1a2672feeae6d7a89b4a6cf3f65e96de3bcc011e7803319efefc9d3166fe06683815ef562ca39fce13530aaadfefcebb6a28d743115e4c4d00ed462b3abf83dd7b9703b17d7e88eea4dcace30a6ba013d4e5e602bb2a7c806c7b03d764527ebe175a3a8963e553de37122906fd20a54ffdf5b60b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9adff910fa88fbac1970cefc062e570fa65c6f4e7677b725ee630bd24777842b","proof":"6896206d32aeed248546877e382e0a362d9d17a8b554c3df219e428cdcab752f564b7257258a6999edb60824a00f3bad278e9fae36c709b0e006fb1b43d5e75be4ada3efe9c2c001b426a84bca6c98afc21da332192802979215df63eb64630d5e0f0d0fa453ba7521af2d3f2a44407416cdf9c2cedb6d969bed3f2ef549275e34a3d76e8e5a41058fcdb4f1134e91e00086706d01ed495db510c3e166996b0d605d927ac6a6b03f8001de58604c9bc25f622e13a0ddbeb7e0b7967f6a939809265f66e869345b2abe2facb9a7185c585ee76f4626d455f370fe82fa4268f10c9c4edd609116111a38e07e043fa7f93464b2662c0dd3b0d10ca98f3c26d5142fb21da8a35d82bd2f4cb998c301d4f107cb1706a71a13bc1bea61124a4074691afc15158976e7bd89b04780f13ef24ad6db10ca5cdbbdfd950eef1a62b2bb1b7d2c72b4083dd4799cdc21122447ec80ddd23118ce182215920f0070a076227779ec9afc277ef43228ee3add2cb1121951986f1e355b5936bea651d7cf16ee1c74b020b6d5cd03ed41c5bd9347603993f4dacae8dddffab30923d24da27a16fb75fc21be18bc381d8f61d0a085635bad3851b0703467b9637bfccc9a62c9a01f41189ff71a55f1d4dc747b11fc2411fd20eec0cc32d7bb2d248a83d376c19c6d0a048f3f075dd2376eae6c8d98940e3aae56391aad8c9c8695ffd177378d76e77e549c6ae36811df4bd531ad2f4240a9f5d88a70c2e5424011bd6658dd7f9ba6159e2c109dd4386b23be8fb490f971edff47e6f16ca4b6ca090bc1e6268d84b06ec8cc78c3e16ba478751f68659dfc03c5ba5319b62652d7a171b6dabe939a3d3eaee494097ad4690097de623ccfe7ab4253a045c97bc4a7a744782be697dcdc090dda0289383a8514059a417a3065e435650ec505676c063a9e331b73c5e5000f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"940846545d6f39cd6465dad6dd0ac025a386523f5ec8c0b3e9fdd6fc00114c22","proof":"d4d01ff278af0f0446feecb60be67c44a849776816b116edf0998253b4e8f44278765bb8cdfe8cace42c52f6909f875a9bb9b089399114d7edd842481e79b512061097ebb944f9962db766a9830f62ad620df5fa238cb70b6ff56375959f72466e10d735b6cc9de8a3cdf723fe9d338c80d9059c9006a2a6def661df65110e3dba453d65e8e36035ac3b9fc6fb96682e4968023d6fcbc2786d83530572762b0cbd4912c7cfc322a23893d968e5fce57d1b009ffc52514efe16039c25cbfc5102ae87c0fb2952ab25ef56690c0bc2a6a7fa8300d5ce39b70e82819a5309f0d600e2ef0c4946dad6abcee658437b32446c8858104c901e7c25dcf450d2f4e60f1bba8571e7080ab794d3193d07e42d438d76e2e6a97b28f6df236a155bc068d14fc6bbe35c344f489e4d8bedc1917d9e1d67a7d2d09660db2a798671dcbecb113870d6f42ad3d30254f0af46f8089583e29541eaa46adeb1579c6ab10f35e113259c5181c20069cd9aeec2a366f02b2130ac69d2d69cf398898888ebe01a712f4cc4855939f1aba860661e2c5486ad7ad1c36e6379326fea93419089c902eb597f1cc7e73b6bccabf63ab2bd1083bf8b6bc67aa93434b2ed09ee910b796c4ee63892a391ae47d2392c69eb8ab05c59810539c706e7f230076c19cc6e7151d9bf0efa6c340b18bc6e2ee87ab5cf1339239c7215699c0387d716742b0a65011ecd4ac6c5c130c2814e7623b4ab86bda0de0c5332757859746fce10d9ffa9f99d475674bcf17d97386489cf43d0ce113c508cc198867c451b74045ec5b895b1c50e54a64ba78f411d02cd228d85cbfa6586dd4a8e2772ce13d61034647e475a37644328ee332313ef2d1fdb0863634fcdcf33e0df46b6d75b18f57aaf348fe146560dc22b5b6607ab0967c4496d98071dcc7a17a480a726faaa61e346036f7f54d802"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"860a9e6c3ac3271218d0f397b9658cb0d697ba28824df6d970555cb5149d4f65","proof":"62a9e0f31eec3d4e19eca75bf3a1e2484ac024d4510b58f707f139bd52259d1b502964ed18e786369c38a3d202f3a28318c75f15d83d5722408e480eae370305ea3d8f4b6ca6c8123ff77ee4bcf64d5cd6fd915e13420aaa6362ce39e5bb633cb81a1c38ed35db47615fe8e1ffe979234b2cadcd1c7a24c7d13d8b6dd7f07d2f9b6f84da0c59b674a0122e30b5be935ada5b7642bf23c7b0a58e348fd6cbe803854652082fac204e934339ce3304a511aeb2b5830f226a909f5717baaba17608bbad42f3763b8003f0b1a013314988461c502901b0d669237b7a6d4b3c6ee103d2ff46729105cc31f696a6f731e6915952bd0f7017b03f1d42672c8b8961ed22725bd5831c47f7b072396e861766beb17391131a24ad85f4a5332985ca6d4215946818b9cb59388d4574b9933713cd8f6c17d52d4a59e96b10441938f975c7718cc4c3e088d961111bee9175671df8ac65c4e379cbc8941abc73db9d73cfa21f0821f7a21a9a0f1b996431a86cf030189a80151495f5cfe1a225e9419cd2e57466219695ee972253dfd131115f52f8190149836d19dd1200dd228b9743a86379745e5cc80f5fb3a6d20b390b8e919d1bd9036eff54a6871b5d61db31e6da897a40d34cf2b0e77981f458dfdac8946ca2215af6306540e2a43447be43c2c92a313eaf290711cb87459847af1b8d94496b05020f02f58dfb0388eb051e61144f051e8dce99afd67346dbd23cbb3b9bd9b345d40d2cd7002eff9b823aa734b61408365f98841054b1ad321a8d2cdd41da806317f196b3026afe365744bcf69f7a63dac63ad049ab84a727bd2a080d66138d3286318c1e4b06ddfb5927c18e7f5061919fa29d50253c0402c000284205ba7cc77ed4a24dfdb413a1c8c7945dc30a009df10cf157c5d618ed08f9de44750ea36086a7c6ff149ccd8aff11b896740c03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8e5496e5e27863de678b9db537c1f5ad2cc26b6d5bec9e28c359d775578b9958","proof":"8a5b01e413370ebecb9c568aa9b45d6450697c10a5acb456ddaa7ba668dfbc2638afb5c8a4cdd11a13781a12618f1d12e1e3c82746cef8a0e3bad9afd7e2981b785b89ff3552a9ec9efc18db15738f37901eb74ba331c534709be4321b601d25826d0a22b91a9d6907e75647397a3015103a5e3398a78db3c0a339f5ccbe6902a0d17c3800844c0219e065088f0dc41adcbddaf0728134c9184d4c923f47d60a1440a46a50f87688d2f4c38f726287205a90f585de3be3d9dc7c4bc4df1bb10ddfbe0fec18191bef7552cdc0461b374c4e3f5fd22603fcaccec9e88170946804140c17ed026822af845a91e24119d4ef27c306eea245cfc664d65eeb0d5f726a106230116e03d8863dae6e511f2fde9102d7e12c7182df301680ce72ea22d556361b1532a0cfb796db1f0375726dd558292457e2fc2a2a0b2d60b0881ce0972d8a3e484f9f86354651db715e607d932012b97ec92c559615ca25f282fd9ac22d12e30a56d8d5a71304c0d5860159aef6b7d5b1eb747163988ce2d71956e99a1c90bdf84e359c2a244caca351581692daa36b088f4fb29765d97f49c3e64548754c55c46b95362bec3d708688b8cec3b988724b71fd4692e320bfef58d426276360bb01bef49b665f7d72e1669aea3619a7e6e95a5ffb54289db447076551c16a6c98b23aa7c1757a588772bbd778bedbdf9921e777cf96bcb638d47431de32049043859d2e15d3e0b988bf2315b842dbdd3b9c09cf3591fb66a159c48ab86a2392334d60577c6cf595db1e9466f23dadbb423e62dfe52f478cded7513bf9ba426ad5c75e2c9450dffa9d6035efc475eb28b9491e2cf4c151b30ceeff98505765e05140e6a90d6c751b49399ec716cfbb6ddee27dae3e8b89eb2b5d8cd2b51b034a504e3d32cd1e79e807873dddf9d3f989a2bd2380e12aa30d0f72ebc7a2e100"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f4793bfaa01a310b85c0d7a4ca1872ec62008d7023948e9b8f0b75f49b05ac0e","proof":"4c0b4e73b11427a109ae4f304af5c4d2c6f8feb38d9b7e54ebcf6e8a8fd36923d60dcd7a30eed00fb70489c0eff97da5cb410f4aa1f9b31a74004cdaa3c86a0b78f50d0b5741d9a9d1b418a39bf780c0204abd4af7146238a6d588c35733fb2fe2e3300716ffae9f4f5d77864ad2070fabf636fa80d63d7c4e115b9899b9f76c06822eeafed2df26d265d3580b52d275388a58fd8d815bcbbe68138af9114508be57a0d92f88b411124ec998178be5e3c43261e08c46e3e83a36c23004b7f50ef5a858c03f00b6416d4dbb41bc2584f755b579d76951b559f06e11a93627eb0a42cc355f452290fd6ff2bb65d33e33058000b0f478f29e57f1050afa66a48c7512e0da4f49a7ed2e151dfafd1dfd8cea1e62c7ceaf8ad5c1709cc37c8204ca607e9143930b43934fcb3664785bfb09223503aadd5f6548c08b65a96be5ebf263ccaa4471d0c17ae1baa3d71627d96bdb4fdd6d3040197808eb15fa2d1614e92b086822a7043629ed515bdd57dcf09c44af14013e1cfb2a02c2a18115eea1d1075803788a9e5c1d964c0fc29e915024603fe9b3667370e2318fd544f2defddc5d3eccb54b599f81f7f9e0c9fab136acfa4d003a157e4f78c292f7e038f5080b65742464590c9f0c1067e28af2621875cf6baf4b77663db9409c83ff329322364792306a801f2f1d042ae62527a7410fa70b9f05f6cea8d2d4660811bd456598004249a83544365c828a5b001f30ed6bf0a4de927cb07b4fcc91dbc2b68450d55dde9e81fb3521ea2d6f67090491ddb06d6e0fd3dac8fad7bd036a47db0b3bd83f6617531e393c56835949fa69730dd5e3b2e7fb196854f7dbb12bba0545eb2d61716a8001ee1ba89c8679bcbbc284c83d2afa88ee0dd03c3d23cfaaaa84640601ab85ee5857d4fcffd2d1bb5b5902357ff6c26434a56f474e6e4e48a00aae8209"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2a13c79c431366dae6dce01862c1edd28859e0f28112d7993c7da91acdf6af3d","proof":"243b317d96df59f273c38fc7c25c7b8a85b1e41c1bedd05a1fc63dbceef90018ba34943972d1f4faa22de76cf4c9a4b2e786c38a8a0836cf112bb945926c76482aff30ad2d1646020f9393206681a706018e6fa6d7d538caa61234703395ec4a7858075af8a77bf43c130cd499bd5394af8ce77f73c6c92a40ca96fe0492e77ddb61bc2f23c24ac5ebe201649131fee1b570507ac0566f0758e82a47728e0b0f64d75ac200c01bd0a097da08fccd584126ac5bc45bd4ee7967f89a7334159201f729d3cb40d31e07b8ad24f1fd87d7957e00275eb8f9f89df4d73c21abd8de0424c24a94a7f7dcb7689f8456ba764669325984043ed48602a92cf8bf0835356b54e15d3de0ab725612befb13b834545df968dc685de34f696989dd683ee08e0da6caebfb43171278bfb9a8494bb017efb3d9c0f8e7f6798404abafa73364ed0374ae9874d0c5891393db9fda1ace642f811fdb4aa2dac4137e299e5962b9404dcc6ec1613a6334339b3e2f6eda554b13917020aa23f199021ae53909baa219461890f8a4c07b8dbebf43802abc2a68b042b9a2ebb6b9ebb559113ec89fa3350b645b4646942ff7bac6321ba02b96c61d61bbbfab161d1f912fbf56704224d479b2047c0f94245f385f441df680d462be420f8b9fa76abaeb83a230ca6818e33500b1242071f801e59138eef895a77111152d9ed996d79323fe2b32f52f68d57aaaffe3e876102d366a23b22c006badae02acc57837ba49c8fd0ffe47446c8951f4f02ef94453b2cd97a9dd75afe7e2fac9a5a5ccccdd9527da2ec12c8257551d7a6ee1d7555aa31948cbd66efe15df0c96f63862be7487323f5cf19fe2b6786cc4a55e860d480fb37403e63486733e847e0e64132cc6867e83a475a25b5b870040bd7a575153c983c36ac334aafe22a85b0bad472d08bf2a6e3e1ee15b99cb07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ba67232ecf2b73fb3d2c8cbf9f767c5f76849439a9573ee8a60769154230fc03","proof":"887c84934b7758d2d327fa0289e36bbd61af9d9c1e31cbf2f74c5f252cfb5f0766d8c23dcdcf98d0c3e5f08edd996ea872c3de553c0d409162574ca4504ef577b2b64f8efb620f5617b098466b43383f9c1b8a74bd8c1ad9b10a75d11f81502c84dad9b6bc882f2d63e6d69f784268576ade88c2919a5238517d5d9050800b6f62406aecbf87c3f759ad59dfe121df89f07e31bb67fd5ff3755aad317fbd1f0723da3253f205118f9724e078829b120c9f562d082eb6207dc34652173721b4049e12c186fe5687f7314989a10905720d01225d6ecaae1cf0ac63c9b24a34c50c4cfcc7119236e5d491b3ffe1b21fe736f54fcf720789dfa2dc0e32b76050436df0036ec12820180d6fe4f0b3e163b21bf98c499443021f20d6c048013ed49e20b469e106098dde328781bc3e42c04524bfad2402d51a1a7918b022d82948296ada8dbcc67d5ce4f43696fb87ec03afddcdc9af8dfbed4088c3e8351ccbb4aa42c499060c7a41799f61c2c618a5890fa575bd2c67bc1e3ee19375a4afa928f4377247b238970d29c33f061e30b626d68a12a8a985166a702ccf473a87aac33532c45d1365d29e558c67cb1f1b4ff2f641831423ec98bb649e05dabbe4abca3d10dca116303c124533102183f0b2365a35e71d266eb0c73a579b7c4139879f2802d6f802ad78ceaaa67a553b8d620bcbdf92fd01cc758ba235a10b10df5069b43fcaaed5e81dddfb99f6597974a1bc96648c8377a0fab1f7001e4d931a4876f754506cf321a17c7701835267130367bf7fab57a22d75e1e7f0dc2f45d7213ab8365edcf7a9a2eb5a1117dbc57787515a71db4d03aeecd5fb87f931fa4ce3c8ee0078b9fa657150297ac052242b33b1d909aa513f314ba5db42bc95fc52201723060ed24a199067e58cce6e0884433624dce14e0345a5b369b2d1cfa4fa1f2bde0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"78aca17a4b39eb06a738925e8460b5f1bf87325948ff2e8cd78c7de74f14685f","proof":"94c3e1a2a9b4ae9d067b57ccaec96958bd7a38a7d7ddafb54b2bca0a0de7c33078855fe2ea1f44c78dffc2ecf82fd595db36a7b268f5357e622a68e7b77b797c4442d3a73acf11b0146e207684ddeed5ec929b93a383a9b4661e396606302822d4e14efddde4f487376271438d83d941418cd19165be6123ff38f4f35feb4d4cc935b10319aa39a534257dd7798777c8d6951128036a75fa78635e9c24fb6306d0f532757ea41134f6b8ff28cd8ab45cd4fab307fc0c5c69dd93b5d284a4b10f6daf1d9d95e8dace0863f0f2709f02c5e40ba1c9d8c82d3ee56dbe7995a4f301b6cac4b124c7d67c5e6567001871898c8866369e307d4bf4e46f4348a46e8157543810601bfe3148584025f334de7c35a5202ad9480497a62137e063fb441a318ed47784fd4281301dc5433e4681edaacd6a042c6e54f7584240b4626276ac6a206f79d295ea6f306bd5865607e8b9c3c17a9689e014a441d358ca5c8194780eb68f7e436e0c07b5b8ef188b25acdfbf9cfd13e36eee55c54f383a72fe89db243e4bbeab5d71036a1df514fbf7e6b27d009fead46275d00d8394acebb703977cccc4f8a742f78d6e0d0ab1e15ab23af66a027c5f5d55b23e9d1bd3c6ffdb884af21a0a5aa6ca0c313a2050488219a4481c6753a30f2d404232d0bc7734f0d80986ec22e07b9ecce0ffcd93122429ef140c39e82631a453346c2cbea0f19bde4e9215ca80d11b4d304695e5b108bd4a314927fd5a1a2539ca3dec89e3d238c61b30a51de41b22f3f49b18a509293161b03238d1bbb5232600fbc56622815efa05e01851ee6d5b9e6267fa1ca85dd1f5e1e165c05c557aaf11039162f9e02e7d36b66289e739d6ffe9ba684c0c397ae6935dd8dc864ee0455b3649daf004b88d0f0346372268999cacf2877d0871013792af46b4fab1d9e88ad401fa77fcc16600"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3c315f567e6fcb6d7165c71a3ef60eb1b7ab10ec3da789eeee640eb187670e65","proof":"066d8d1ab53ea82ab9c887d28fdb4dc3c3748f069d1b534c3c07d69c37fee2250618c8b5854fda60c6a6b01f2b1e5733525d39fcf98e022dcd014befa83bd94aa48f375388466b618cf6a244b5b405127a4762f0ebd876348eb2cea0f0ad1c66ec57bc1c8aebef13eaca5933dc5d5c2a0d301435629b4135bc986a55f647ce2e1fb6cd77c36ce7a049755d71d306365165a2afe962a42539725c2aea2b34bd0d87a752f610438884c5eb061ee2451d16cac4e4ad54ef7a3a6b4432aec992be053e81dc288b6b6668c04d489d716f217e9763ffb212a1fefc7f1f7005540c620fee93dd35b2cd37f8c9ba75cb4bef4d59a11c58e99e62f91a7aa548751990161cd0bb9c14bb567da2631b1f39b1fb0e416cdb6b393cb37b5854c5546c9137882ea8a20c31c2d73b1e0ca8c22fccbf872248e889c05a96ca574bd94a7521de54127a59dd70fefb59d34a4cd4d176e8586795c2b20f7b0cb0c927a4b425fb475d416468e3ee8363ba69b8be3c01da9f10e8ec6171358d447d2a5adad5b1b8ba190f507a36c5f404f88ea8bf4c74f3be238d1d48a03ce4bd5edbaf7a341955b2c30d5ee497a6f0015c1fca686966c480166d14dbb269414374f0f4cf31d6d0e8e97bd2c3b0dca51ee70734f3e6b944cfe132d50537af57b64fc5ecb4d3a51d5d7a0dd234799702ec74f0b0e8cc674ccc5ba95e12ef6066d764131894cb830af5d32d9e2165c799bca2a52064978d4687c0f51cd25834d081495e8e89eebbe86bce17f20747a025852401ba98623fc5bbd0d1636603e40b7efe794660369bb136af4ba0298ec9f3acbdbdd200a474fb45f39b745779e7eb205977f6151c112e51f91675622dc4cdf8111bbc7d40a7b663ddfd268f3d36488e1d97890af96b60e7de0ba348cbfa70be4e94517394e99d1e755d601c57a8c505fe2656b7a565bb55590b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"40eedb01bcbd1b44c4212f025a74e8c5dff127d6487ee3d52640a78b585b6b62","proof":"c851b3c783c2bd974a1e1555ef229c3fa7936ebab239bd5aa0fb4940d80fcd40822f2b60946e4e996a561dd11737eb78b6c4c9cace8e71726bbc85dd7dc82e006c5ab92e3d90e9bdb7a3ce0c932e566bda5b525ab80cfc72bcc1d54abf06c430b6673b6d1f0a560cd35ee1636f019ba19a2e0f21716cc22fc09d821e1929345f61f587732a626b1cb8751444b179116471f216e16b0c41342db78f90af52b20da99b24d63a1ee42056f56cf1cb3b3191441e7a0f95c3da41612f53e630407d0b913a26a65aa3db2a86d2105244c6dfd31527a943172e5ca973e993dd6304dc072acdd9b8f49e1dbee757c0caf2a85a22170da1f8f6f15794f1063eda46c5e1049eaf0ca9d7c37e35eae0795d0a99fe2d21935bae583606bc62dac54a481cfd56dcb5599b5276d6d28108c0a1dd75fb6d2072e6ae976f8986081acf2ff26fc97e24adf8d96108f6d33c7a624179ff7ca8c5bc38c455dc9cc522684bc48f3a4c1e4c6f551e8e00d394ee16b44f98b7a621d6ffc80980d7f60740d73a1a7b65a148ec81a4c2626b8d0380f91ebdbffe02abe64bf483e8b4b4c993d0a75007278528b2332ff4a6c0beec459d2310bb5ef3133c65ee69c7b461c3972333dab4aa52043ec8f68e98fca0a55daa10bd7186eafb05ec42f0750895cff51c7df8ea31604d6e53e367d8f724d48eb708359184dc2b58d2de110605863c9d3228d1d0fdcf1d64eb992cbe09ff4fc1d3e076a70fe3a647489d226f5ce94a90b805e54935d20b80a4e7142d71d0d717afe7b085fd26b2292434c75fd7eb2189eeadac6b3635776ee1a669a62afaeaaeedba92afc97b6188292c782bf245e01fd95b579f3ed876e7494fe684594008e47404e6c845ddf81aabff2cdc85fa7329fa8705200ec604de8adb0ec3b4a3e2c31e6c1e531673044fc9e1ffbf08a813993057c4ecd9c608"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f86b70d0376b86aaada4d8fe2bf8bbc13f42aa7464624f7ca5274faff01c1b0f","proof":"ca1952c9c1ad1b70d2dca8669fa6f945844d6a6fc3791916ef653e45b267815d7ad2daa9ddd240102b67cfdc18be9a74db8e425b25d1b62c491ae577592fb31becc32004c29eb68a4ab85079b8fe5e7a8affae97eee3e3a0cd8dd31d4a35b726d4a67ef2073615bc84cbafaa52d87cadf87b5bbe6bc75cb774b399ccfe298b3ba947930720d3633eb490e5275e93c50919d76507989e7ffd96b0388fe971850ac4ce485c5f007c8ff5e3e44e6c9a84b93705b80866bbf72f93960f90e45d1e0d6fd6dfe82115c6697aac405100af90826ec247fa5577b7c1b5fe84792713f00b5c67c8b148c24a45c157751b4de0385da4981edeccae9ffb015b32f7e9eed32f4ec5cce241f5f184f90800547c4c6010961b24bf8f84bda175641cdebdf9dd558c89405b05b8e1124c86dc91e9fa601a276115fbf8d28274639feb8577315f704ef25574f9a0beec36e5fd388014b974b7388fefea88d9893cfb26cbbdb177141a333dcac374f7f3c506df32262fc251fddedb8bdc82d26d6839696205e1b255c65341e5be816503967bd0549af65eb3360e8394422ed7ec0949d2141a43186d6ed8a8f1c7e512b191220377b91cfe3329678cc1257029251fae036f85b92a5a18a0c460160421a6951bd3b437b31ea1284e63459f3096c7d811d2b23a676555b67f073311064b2672a5b07f4080aea4e244b69378d826d95d04834438da2b45e853f93c23833eb0d2e455dd013f8532603ac30802a1b0629d1d8660bbcdd7535e83348d6b2fa7aa18f364621b1e524fecc695c4c3c6de9505fb57572438c854222b5112f7571ee3ed513fcec93a4c63919349e5dda421182880c3f16a50714a18b624e7b15943b75f4337062ff264be8b67bcea328b5a022a060443a8a68d0f56ccabc8390e712126bb96c81b6f99f0ba7aa1c801cf76ea96542c73e717ab0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"42573bb8c67929b18b882a0f95ff6346e20e2569d44c3d744d2f54e903a78c26","proof":"7e89050a3e76883909f11eace904aeddfa62ed1804b66b22657e83bd65a3b81ffce1773f4dd90a0a08f09add489bc8578612b1a2e87817d7d7c768a48c28e42662c83c7370630e760c55178288a4a25350f14bf1c41b7b36958489b128a960116603c1432d9ce37b06307acf2bc2254816ddc869948fa4d99b19437db1e0604e005b75f6e556a2e3d56a4f5b1be59ffd4c761e7c58408538cd32b1a86ff8920a17e0e87d32bc6f1abc96e54e2709cc261a3ff62be82b093feaab6b458137610fbafdbb3f1d916814c52ecfa0fa32e89ac0f5fba4f9917eba4cebf1c2cee4760420deecd4f9ce66987e70d2aaf91414593f147a3c6a0be1afd365fb948a5751213e48e8444d6b313bbb82ddc9783d8e522245529f59f10bcc7722959d94128e4c406d7ba020305c23212124e6ba7cf5fb06243ea50aeccc33f23a1bcb3478b542ba172a497b06a7df33ea323a0c111f351b1272d00479e18a75b1b2c29ad830401c1ee500143e2914d39b776e1398f213f8e842a06de1bf583228fdea474f9d7c2ee46fc07c2e97837d23e395a9658ddff69086a0c43eaac1c9db646ded0691555c535a9a2e002e1fa1b74cb7b0cbceecc041b3a5785846458a5e014667dea928bc5035262d825434b004bba874d1734a0e5144ee808b191f2335af998f22683d80431e1a22bb006ec4eda0f47d1ff2ef68b374399951e8271238147b9dfff637bcdab7182811c04a73004b87d6dfe97835fe70edb73f46d13530990cb1bd8670685e0244c7f7eb97d5a985f39251fbc64a9611721296d97e13681280e89117340489a890ba195c049afd95b95679c57d3aa8876e9a2482b59e7b80fc17547179a1cbe4c3631f04c752789eb8cbe159f2916765def7118a7158df405321e1f2028f2d0c22e22dbd7e6a6a4244c4e49e8f9e66862db2bca265326ee90f78d0d705"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a846bff992ef1dcaaa5455b2dcb525a6fe77731ba535a189e944faa65bed806e","proof":"c81561aa736f48bd7146d53edb4feb90cf2fee4eb753a65b97a72dec6eb17a5ec8e168e26a711c1ba64820231f02408d2578dafc32ff38d523ffcd48e34238562422a1fafa7d88ab94e49d4594bde90f6ff65798c2764bcee241caa6fcb2472dc67f9f6a471998cd52576e02701597b770d66e5cf7d4b4318b233f1125402b594a1c50b7e5f5cb5a9b0ff678a2137093b45d0122f1173046f4054be265bfa608ee3ae3069198b40c8bef46441f314ad747b72534e92e4b54b0cfc34954cb970f748403745166c593fa9fb077571a67d80d8245e3a946d743376fdd1da181050c061cc5e5da0730534683d794fce7b69905702e33d66efeb8014afab60ff2c62420904eb55c6f706eb4f480d05939e9edacb857fdf046f01669740c3f1ea8b6650e5adea2e4f2d123b13df2e0b90de9e99f514a9e77ca8118744b54c170c1211d5edefd8091d9b0d171f52be9358bbed8bd24dd4cf43dbed5ef10ac398002a72234000656cb721278561f68fd83b2c1713e71b2b0e62fca377f4b420a3680362c684faa298c8bf15ead5bad10175ec539f6243f2ed8725599366cfbdca92d2e5ed0e9adc82bd60e2eba2cf4b2d01d17f51ec9868e6233005c67b1ced9ccc4a724fc1bd2a4a10a878224af9e8c2730ea136ddd3ece37f7ee0913c2385a00df897c0ee3812e26a40f851c100900c01fae8575d0f4b611443de83f96055adb333837b415ff3f66a31daa0344429223d0c1b7b41dd7099257f241b787db4de30a8069d456521e30b4be0421fb4e15632808369dfa7a0e2e0956ca1fe40540c30caf77d057cfd28d59d82f8388d5532f5e4ceaac58308390bbab92f625be721cd04c7b25d934d8cf3881379b3bb1caa8a73e2372a35147229a14eb618de5b800d3900fa563349b5b97d9d33a1d44ae2da7fb52d47e4c233cfed0c58ac49edf85d9d90f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8e633777b0758b6e3df17370ec64a6d8c45a895baec10a4462e36f82d189ba4f","proof":"985e13d3aca13ab3be63b378d27e7ffa5e206658a6de0aca7ae3020ff41b2867941f3d7361afc97a028dcee08d600359bfe659177d7786115979375b93bab453d2f3ce081272ae2c793fe60b7ee3c3ced76c75ec95dec83dd9fbcb68bec35a44486e60fe38ccebef1781054a5f00d79b7e8cb8e4bc495fc270ee91ca5f80de526d6fc454107977599dfc65da8bda29d5b331a8e64cc7880dfb6e861c0cb42f032931c62fdce038cbed4b330809b086cf887ff9b674ddf6dbc5c8f05720632f08e953c8b1b02f46b27482379b4ef5a9199dead6ff6893219cecada074c75ad50ba4853e70ac0470cb3fa638eca03db3681d6426d66e7c2e3a99a9b641ad50f732e60b105c91bfe37cad566f4cc00dc022d21fc00e45ab66fc1f13661da7bb7f5e107d2bc7572a2991b8b7dce5954c80b51b456ae37db6af8b711faa8ecbccdd7268a8fdb732cb5f5eb3786381ba791d4d7ce6266eda070e3483edc4507f112b1980651836da6279ae7e1efbd31b22bcbb79902b73f08874542dbb710c2a8d3532ee0d7229c780f5bae5a550703e77c628ace53b8fca719042ca7c1f457073fe7888459d83389b5485c39492258c94e63e1265a500cf11967775eb6ed7d5e3a503c21d38c7329193fbc2142cea1b34c2dc1dec6b298234f9206ad43ab90da7281076d2e3fa4caa0230e757d12d92f52accca8218230fddc7cb415d57324ddfb047e035219f27080caeafb42c6912e1544aa6a0abc2786bcb468d280a78b8513e546036e1bcb2fa9dc7e28ac900eed65710738e48bc35e35803e8232a428acf187432e48c9c50ad54db30a091e308f27bc24d6623e60cdb48f1b1c88bfdc0c29f3c7ea11a6787ce5b1aeb2d6fb519b961a46cd49285107ec056d61e72a20da2660cc587be4a13ef4307f1e8e7125dfbd9d7e41f4987a5117f299647938af95db30c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"84c6c0cdcd59a271a88b4f2768b4dd8dd5ec5e504367a4165ca3a76a10d42c61","proof":"145dcbcaaf784f92274c9ada493e85a4f4895cd05649560d4a851a2be1901351b44e0e0cc1c7d778e350eaf119e714df18657f3752b2fa659160afb949ce7e1c9217f2617bfcdcf3ede9a343b6a53d4cac75abaeb7e011f529fc028a92e687624e51faa7f07239e8c6f1cdbcb2c0159b27090b48961fc5dc445de7e82193222ba0bd56f1dbf8d249982ea702f75688d9539029675bd26c415cc5223a4a68d40c13eec78d99f17cadaeca7fa348f953f51e8214bf74d13232c115d1f484b745029e80a9547fa0a8423821a2443b8a7e1e985ba08380585b69f9b4d27a129833059836e5bfb283363b6fc3ba19451c7bafb774e250110823655f51a8fa376dcc179400aef4ee4f7b10940bf197babfba41b3e11f451266110be074df9730f7356d0063e3b18cc96dd76ce56aa8299ac1e1f45e890d98ae6a371fb811b6b0b9344452a2cc1f8b8b471451706d669d666a3fdc790ea83850d292214ec573b4dec465f4223783293ac103e1afe622b4a04b33164400841b0cf36cd19077b75e5f0449b236db6b29b97ce71140633759eed611a469f2a36e8c3904f5f1f904bcade50d646ee5039b5277fb6fe61682cc8f2574902002acf46ac6f9303339148b893b425cb94e2a6689ba0e61f879208a9451dabe4d836a399f0f0bb4b802185e3d3b01446a8f0d5b11f7db01992dc5656b04eb0290902f9ebcff862e7d78feb9b3ad726003bc8ab3f95e09b43edbd9789aad031dda2d601854704a2825d8e6edc99e6a781f1f73a457f368838a25c190e72df026417e51cbd7a6901027aa4c82ff3263663931b414c6a68ea723d41f1454cfcd8fc25fef210d9be155f6ecf9ac61645d001de625efb567cd67424afdc50a19cc3319f939189961368cdcb7405033940067752d372dbd4279c6331b01e11abb62f687c084d7bd937d66eba59fd2389803"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4c557dba68022b131fe62bae11c2987764493184ae0d2f0e6c9ac228c8a02008","proof":"7a59ebcf6c0fc1f6608a0b60631e2f9ebf1ef42dc8fc978cfc72e8e2cafff41de6d2e5a738667e5c24467b9685e5bf4b644ed7912632475d8e5f5d90e9bf5d6aa6c285d51c006b7c750accb1f82a05b864058fd4bfe7d8c5354c256078d1a264b67cf1f11b8af992d7ab6f0affa856ff036be6cf1fede569f6ba761ca9eb223484ce497761ee04ba35dd6b54d0c0774465c484f409c5e0dcfaf5bb42eda2230327cdc4ac83776ed580eb3a40acb4e70f7374dbd3fdeaa1981e9fcebd1991e701b4c0703083b2bd4a25e05f60a0dec07c067b6e1698ec20ee14222f8154ad9d05c829c5e941b822a470b533e3e53d5e70a356198b718814cd541c609bf64a0220e2ee3cf13d61800f7dd0bd7b7d08370f4dde12e0e0ad3456543662e59adb8d4cb6881e32ab45a1e6896466b3066e3d1696b3281712115c478de4bbd0705dc24eec3f4ef224b3ce334d6a7bee77b0a259dbbbe52f2bbc7a9879a89f0eaa425946f4bc6bb8fac392f6fa5d517caf31ab34d8a51bce1affae6988ed2df9b47230637622aef55732edded61bf2be2719106747d9f9344139be7a550739446079856ce6bd5b376194f653ad3894665d6e4ffc254209ef8cd3882a37526b2e4c8f615cbe9d14d38a823f63b1a780c81ebb270c1ad90723ccbdb74df4c3e4213279cb485ed2c15c659570185dec65667e2bfb5aac3ea6e3105434f9923ffcedfeebb529d8be0c7262751d81db47c41743d6e0efe4187fbeed36d62234ee3d01f8732f01a0c3e8dcb30136741a4c8b70c1f44120452487f3381314f7b1ef52a097352d67c056af5b4eb28117fe347f334cb95ebe564a140979c0bd6adb465a9ce1cf5169780a6619f5ef89daccd9f0d5c8c0bd6bf278c3ca4d1224251b6e8d4c188da50fc1e56d47c475bd3d80dd6e41a51ff3f9147f309d78491e5805ebd5e657849f0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"56140bdc7803e04237ea3308158548c6d66bcd6064c792c8dca86f50b655fe75","proof":"4a64ed49c344eb973763ec4f0011aecf22071644f7c788c8b1f4906695570e18640049c12eaff377293736025fa735b9783cace4b444d6283da8803de9186a4656cca1373639b748e91364db914538ed115643ac64f7ec0c60dd1a102a9f6555b0424eebe257b4fb59ad2a287eeb0e3b72e8c010c3b99792c1f2bbb6fd5fb63026fd6a994cf7b1bdeb3cdf7618e951ab344634008fa6454bf3da68d30837330fcb90b7515c8bdd9b92fd0f3590dd0b13aa2f171157c6c558d63e03e6879be90a7faab2ed1689e6a3874e7c0aaf84d42fa42f5f459796b1c8632b17ede98e0603b09db7bc0b55439b6256144a2c24e30e85e76301b9f2bd4390b4b0f8dc9b834e04363d27a89f9cb53d61c1e15d4314ebc568e0a0abef39f0a64e25ffaaa3e21996a9e5794ce8c04a09c469a0a507346183634f34ae7a13c98482b225c41ed15c987922bd8ea855212adfc5482d05e7c869faf19d9f5e8422a6af33dab7d2c67a9ea94af0ce87ecc67471d073d0463c3849f8ab93d1a97e9cdfcceed28c5f344d72a76b4739e8b0e95eb48a798fa8833e5fecace4691c6ede97512051a279093366396cfab9ad556b8d3119164a35ae0ac1a4b07ac63dbcae2c5231bdd82ed4323a8c7f89dc36ddd14b5fa538f1683613755b87c27f00d624df4957334ef24f6946fafaece9647a7278578b2a47ea539be46c670784dac6db306f383070e1020b5ee01b45ad64d25e3fa98d97e0cda5d75ea46365f5a01fc4b91f2c138394b26b3c9b779bd2c35c57411e05c309ff81c6913fb9aec1598a3a6a37f97c6c4fd06352b67df4829af9b4f2414970842f8def398b1a9f5b66a227ecdb5d6ad16d1820ddbbc0a482c4587f16f555807a0b8b788c43e43e32fb2102e73e0a85e3f1b905a529980eb1288d8ef055adab2cfb2f6f8e61b73d08d49c4dfbaa485325bedf09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"48c3a10ebbf44593cc5b2181970034fe5cf003432bac7a5ebdaabd7e349ada6b","proof":"ce5714c5c0caf967267b9bf8ca3513c414db885e1e3b868bbc86b3c0c35ccd697854faa7d8eb947ddcc567fdd5831f2948826cd0a2ae56cc337ab2c98192cd2f3a30967488181b02361661cbf05a00160081bd1ab210ac2590f9361086c3cc7ff0ddfeeaeefb8ea63b9e36c3042ca41ae7e58e9ea7db6c70e89a508cca223f40dc490a96f4fb29cbf05994163983b7313843a76ec3d24639ba1d3e715eaa5200d42553c77c10a2fd4e6dcd3e1b2f4c16b485b076ce08b89042c49290361edc0cc722c0de1af64f2c68d7c1556d64ca04cf4f74ed1d75f9a91bc85e1f669df50ab8e4c1c396da63c65b6cdab8c1ba97425019d3910f6ae33f1a43ee66a3d90b4af486e9a6bb1b25ad8f820f123edc513d9ae398fcd6d79ca1fb43222ba4e8c144a4ecaeaceccda35fa5d6f9b4e23080a3002aa1fc25f21bca8d8754875d9e677b228445c75ab8255f38899dce1ac9a3f60046966f6b5e6e8565822d250a8f523b0c20c06250f7ab97278f5bc4e8a21c7b4495234069e5024e0acd3757a070a442f4f205af32f2c844a91d3ac7d638db5e0bb5cbe9dbaf47b4a2e719bc66d45e3af605b57786de43de238e6c5f51ce56e6d0d4e809d3c8c50f0439958880b8803ffc16fdd4a407f514e10834ec7f56357ebdce834784d037f529377e15159713409689e79bd6eaf715c08cf2595a1cdc24c31673ae78df2b7b2983fb3913da8d1a7eb8c589bb21c1108875d884543030a6c071ccf711c188ddf18087dfb1d0247ecc75a5e262c5137dd78359369081f335a357cbf592485f51908e0e0f44683167b052662af8a68f071208972a3b805075a0bc81050b3e1f8e2f7ceae38e2ea50b7d10ad18e2237efa07d5fdbd2eb1d00998cce16473ea330bb591073a3345840ed4e45709ac513f2a4c7c4ada05079fa08cf46f577fe565ee4d9c6ed39efc0f07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4a6d8e3dc97578b74fb114450dc1b40595f4658b42f37bd3c69d61ef45d15b41","proof":"e01e0d12d89ee900cf005a2e4ea53d461bd6c4602767918e3f70a5ff42050d32feeb3a0c7f9009eea02f8a67e12c5a628d769725987f7c19e89036e2c798111582f62bc6df79b76844aa40a6675c4f82bc1c929d877b95a574b4df5ccebdfe1806eb64f9f0212474e11c99a52cd052a95e736a124e8f4b777fe6f5ef081ccb7c1cffbaa014f1451e930bdc90997cdb97235e2d70006a4141d3319c355097a30a18e89775082731fc538b10e8f9a5fcbd20bf40e637a0512a37509d906c2d45025084a7822cb759ddb085692bdac07b143ae86c5c0dc2624bb39da12f38615d07805932cd7e77a7b6738560b6ef0d3d2eaa8e9b09e8550a0913bb77efdbee042a745f84c5028d8113b1df5705dff87408e71584628409e45b44f07502da726d5e0425c900e34cae6c512b5560381b85ef8b02a2dc30692c467c5d3a0118bca2230c775269ac54c08ffebbd11197bc3463f0e0baf524eb6972162a6c71a4ac8c6f3088540608a6ef2466d35b4abd79c2915357362148e5f69169067b3492a397219ac49370f8be54fdde84abf66fdd9b43e8b0fe24166faac05e942158f180ed570a650f73f95015123feb799666bea4a32d09d600d3c65773a6a34fd28b2c0f74769af50c338864b7de17a0a8ccf4cf446eef95902ddb1e7ac5cb171f6f4cb11666df644508451059e38c9a05ab397120529d7ba4bd4b2b23c9951d615b884263caeba7d035694b59b5c899af09e250db74a36de0f9b51766d835e55d9210b140d2ac5350ff48a43ea6b1e32efdb9b0799e4d472d14722dacd095617e5b077977440d7c798f419f7f9d95407f02c743f76733ff263f5f9fcae340f8be17e4b3746c0995f1b7a3be6c7b3b57cec72ed900626992396070992ad1a9160bb725f10a05c1414674fc01f0084a46b24c3823f4b3b7092449118d826adc0292d67fa903"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"441c5cc90ac0cb4cf9043b41b3bbecc77a5d6b3235ef42039b53c8a181ee597b","proof":"f24743fcf81f253e2f4e4277335a7555a897d93ff54062f1c7f8e535d99d2d28860061ff94052cc577fced9ced8b93c0aa24393d3e0840cf2d479f15ddc0277c340cf047d65f0ed47a02d82b421342d6add5bc3047e605ae67371292fd7ff566441770770f5f51dc5904966b608434d6bcb082745249c7380142ce5e17ca4d503d9a473a47dd4a7058102d2995499c92c05870cb1294f104be213fdb02ad4a0b6b63e25d7b14cde86e1c39627ecff0040551c798c994d4f4bf60772714378b0e9b9f00ee62bff0da73d563d593e213f821c36ddf22aede56549263bccafbcc0b98735728c0fb881e74c4c4f39fc406894560a720f9faed11795988163921dd41f8afd1107546616cba591e64186fbf17996d91e5022e4a71f7ec225648520f543a57faaa9ddd9100ed76b5056b6f75d5e18d860348cec686b4fb898c8b7d6c2ad8452e97d6e01c479b33de622ec6f0bc9a452af650292054c5c520d1a82b814392d7a7602416a9c15b25b89fd05e80ff5535ed5751d488851c406fe5edee0477be4b806d19ec645a587b00273716c2b4ccad9153ce644e14f2f4ff7fb387da00e2dcaed8c9374450c8a71042f607a375bc60a2f5fe155ab5ad74f3704a4a6a729672626dc0bfb1fafacdc3f78443919c3e27fe6c3db129937a28b69f6a2fd70f823ad728211bd5750f3d3880312f126b585646f460abb59d00f925b7d6010406e2caa4d626641f98bac8c7058c1fc7c89621935bf02dc7235d8436615f523d1a209e95f7f77a78da0050c2e603b2c38b05e27c7d2e157756549d992ebc5b8b129aba4aef87a46e5f47bf2d44176d9de93b6a5778fa4a59357a4b4cd5c42047300554252ab0cc86ca273e5417f59a02f79291afac26ffb574393b083a1490d80db65b04f4922959cd154e16535692cf468fb9e6e55af0c6ad24c723fd85cbcc02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d0953e5da0a6a5d82ad661b884bb9c806751656785501af154079930c9728937","proof":"7c1bf8ead9168674db56304afbba38719400a44328251f6a554a1a2e29b9765f3246c29e0e1536b31b16b72a819fb6c0799879188e6ca58c6117a04bf67bd3221ccc05c1b5a7c7012163af6c979935a85476d0715ca0797c9d03cfb4a474dd74def5666bab2b79936a419938a4e5d9123f4f65932386ea1b1248ff31f339af5611677529a3a69a529640a25715db368ae3b9d37674bb7deba23e1569dbfac806363345d518c81a6302b48fc938077b4462768be2acbd941456cf354476bfec00354cc941a4b6aab5cf2a27b150f9ae6c5482b31acd8bd5acf5b8a65d1b46ac05660dfcde9d9ff065dc1a164e156fb12b6860412b9584966205c047f5931eaa1f1c535defb8d44a7d733a9e276c4d2f8593e2b047229d68611fa752a15b34fc68604b920a49ff075a0a2981287bbdd6e51c8286367bd8ef0e60d7686f879016584644d47168a8cb1916ed6c3deff441f002fac9dab11af60877146ae1469cf0024e5732584bb70396f47bd4941bf259528ceb3dbcc6fe2e1658600e2de3966568f6935214b2ca49ee79131114c030662ea2d9ce83cd904171e9e8c3ce015f8253400af68cfba04ecc39aee8752c8ee0ce7aa364f0c7a4e9ca73f73db8ade97b4824a69f1a260ba04d83c461fe7df67f80e3d473f3bc88120704017c898d6ec50c4094b56bd8efd352345f2de11b698b69e6f843b5124149d840f39a2481f5325904d200a18c02cb98411889b1a68af484291b19d5a7761507a204f458a4ab39547022335f46faaf0c6ffdc6cc2d26107457621b782ed6807b297977c6e3565179d622fb53e900a54a67b315a8bef76d4b717d4d115fbbfb0043e2f2934d8f54397529ad8232a3f660588b80dbf56f1d766a8204d4429d720e41a238367aa87109348bb9047773ba798dc29b83040a48d19920e2c56f02f2e457616b6f1a49e90f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"46bc211fc938548107bd5f6c3b60eaf7bba62e5e9e5b7ee9d0e4413611978a47","proof":"3c7f281c4b27ff90c9179e90e4b86cce372ab1c327ddf376332e6a5a99da153fc8bc1a32d9403c3cdd3c6219e124947aa1ff4162ed480e8262a57d7fa8208c409ab1598c61f548103fcd4ad037c7dc833370de7a8763eb398162e374ce53ac74a6cb862801b3366aa346539d37ebc084477d8d8a03e5ae789b0a9feb95b91b1cd618abdb196607b35208c652036be393edb48c31823e5436224b45471ca6960bc8cdeb09b1f71281929f08c34c2d1fc9e896ff83b861cd68ebc60406e69ccb0cbb5aa8aa2ab7ae624474a5fd348cfdb55af1299c971fe172c6529e5a72074c08284a58166e006f596d6cb7563d08e2f99485b7642e266aa5dae6ea802822821026d5b0c014c8ed229a6e3f2c66d60e4ea70849f38f4b3afd89f59ccf14bacb3260884c7c8737b4a3193f908855b83fc3c1b71b341826653b41ad8501550cf757f8a83faecfb95d20e83ead071928b2577344702c09bcd21da15dafc648e7a60c020a6f0a5b9c754315d093793a85aed3ebbd784b642bc3a86faec0b86ac2b65c803572178f0d07d4646cfb6e5535741732fd829836a2e9df2323c3ef910eb5490a2079366757044ac00a44772214ac5fa0e3b3c3fb5513affd2883507d7df027fa6af88405950cccd8da9260058b59d96b11f4270b47261de4facce3764d61655e48336405c2a012c8b5a017c857277e2a3b483a6511c60f9357b879b472cb7ae2d54e2bce40d057857f7739af1d8c74b5ed8f07bcb63747cbd1ab8c25d53c7ac6585358b7b3936091af090b23959a95576a4cf3176a6fab6b17a4895346116082b1354c2499ec4ef05c1347c89d1cfa7f104372dd9ad05f0c751f371574df744e64ebba092c7f79397ecb46cbdac6a91b93cff7c4609491d4f96c6984c7e10948f8b55572d3addccc32a2691232542d378ab19bc285fe848a00f09ee63d8605"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"549cd37350bf2404aedada06a5031203b740528ab7620d2234650f5b7fd0be3f","proof":"f08b6bc3580adee2c12c3030e3621e53e11c2a87c4b82eb1d0d6d70f4b5f69659e1c07042810e70ee174603f461cafffde2740f7a70108090e85f4d5e4a21664b6797c1047fa9470ee8477bf3f5f57278903fed4810ceb651f62948d733c4031961edc3974b2e2c401c4a360190f65cc2ab6e3e74cc50f10d1753456e3a77d3dd0eae4c5847ba75f7f27918e94bbf446855cd27b996c29f9d8833f85af13c80d7d4b8391b3c7b33617123e31acc5a11694b4e0f451d7bb93fb2a0fcc1166e900a53ac875e4e2c02c33aa380d920473d8d0d703b6215e81cbb2b6062242a59107628f323a350a6b7d4972ae03d6e24a88cd10f82fe87f359da12ac558ca12c81760f6e1ad5b06ef47d7e057bc9f08e36be78f23f90ce3bdd5624c82b85cbdd63458057c9c4ede8690d37dee3f555337ad4f7ca506e12dc0767c65085bc05c7c77a8ab05d9279d5711ea540ff108e2df3f764f9e2f1d80726032f6eff15a53313d2ab75f2860b6aa77092e5b5fe8f6611dd3525448c5a64bffa0df05c0b3b9c400d4aa320c1d64bba39222b907395637407978e1afafd600773e30bf08a54ece0392d4ca8540a614808543daca71d9d91b2d23a3fbfab5633705eb3fd76a79252b82343e05edd01a009580ee643deb68c157549e5b58e836e7d2244221beec1f68326b40adcb04dfb18a7715bcd311b0930bcb43e4923e939c5e674ee89bb23641e4f4644676cbcd47eb33f7da718970f85d0ece658e350f93b11983cc24fe3808343e86adbb92850be3f05ce0cf9660a7ce0300b3776184ecc78fa343e8065c6346b4a367792daa6e8909fabc2633cb2e1058b3982f91d4df42a6f902c64c86414b15d4004d9fef3347c6b032a5a013493d98e0611cc4d6214b7216518c048305cca6e6ac3e889c28bd9c4eb4748b49a1c876092a12fe89bd159caf7b3c18e005"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9298b26ada9ab5488809ea8b36b8250c8557006ec10f5142c7e27f1e45fbd340","proof":"369a8625aca63565033634861c8f2fd45cbb15816206f999b038056747766f6b3e04027df2c1b895b990ebb07c062e1ac84bdcb32217d574aa55406581c17e77349aad76fb5f866970b1c50ed9cd1545923062af77e223cd8a97bd4dac6f3d648811deac37828567629191c34901560b084b17445ba5281fb52da0e6d68a071fc96d2475be6c0949f0fb3bf54b757670c883de27d6bd20c4790b90455345480334959def541e40c078f9ad5a89ce1763870bb581010ac5c64cd1267a967a890355c7d519bdc7c29041c4910c1fdf578291a2419b844579bdf26d2597bd9b9d0d0eb43fbb5fbd5ec3cb1ee42c3f1e28b0d583b323ebb558addad80ce73fab621ff48d959261806be5925345a3a4a46335a8c6eaaca1209bdab1dc07d2c98f6c7ede2511cb44cf00de47b443c72a9ec155f64f2a1b6ce2483018659c696695361f3ab29b79a624007d506b7be6fb13411523c400bbacf3ff1a8dc6c5198e5e3d2e0a5612ab6ca6c0cfac31b0389ba15185305db51e20f3800ba9b9b5c300a2377068e3b5b35140698549304b00374d34402e7bb9c2ae1e537115dbc9ff62e22a163a7983f4e19d31e40180ef671acd295332349bed096b17e0e94a42f1d583ee0c8c20c4c96e5f158eaf5ff8c4909db4cfd7198c7ef5c93df9190e16fa9239e40676d4b86f820b6aefa76ee8949ad72f90aeea35c8ce3d8dd4aea792b167ba7d62b22d51eca9709c3a39270f2fac567ad6534dffe679c10dc4b3d26409d8f25a59328945d545b554bc3bc7f6a6231b053548bd46fcaf0c8f41d11ebb800d8b3e477e47bef1d0b3120e83d0c58cf7748df162a31c01a94920a838287ed26635ba764fe4b25284edac60e080e95eabd1fe7c4d24379daa5830e49d0f8e35fd9a070f94331303a6eb341b840dec9515c8598afdcf2691b9fc783f58c4e78a58380e0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ba2ae277079c95a66d9a9ae993701873fd012cd2155610d557ea1acb90634837","proof":"0cf4ecf31385209a0cea5bd723897e385ab138f89427ccd60d4890d0a5aedb5f121d49e69471e9a5bcf15b9b859b7e3be5dc2e6330656ad69b550824aa034415a27895b51bcc81b972c56d661e17329dce33c5d86aa7e10b33d77d34cbb391018671b0051d02915ebe028e1f68b60b28cf40411938baa9953d6ff6dd985701137688a079fe069ba9fe7c1964609f9e2671d1d9e4380e92b6a6a1b59c8d00e50837ddce439de2eef5e277b900917e685d40e96de1d7b7390757cb827376dc050be01b2d6840e1212d1674cd94bf2bdca57318011d4730ba2c92d022e92aa34f0db46841d5c4c357e49a2541bf7597a90c2cb7260ad96705fef8cb787344260a4478a6d654503ac9290ded4c0d05a64b7f64cdf1f478b724d8a1fff1df0d0e4c7792ce3d4324cf2281ca4c73068650eb8b5edc5abaea3a00195096cd14b54405018e46af648cac49add87a6153056cadba644d73233429563424e22f4f8843ed254cebb8ee1a964e968b24d9fa83bc737bf4c5a86476f12cdac60e5910dc666661e6e4c469095e52fb04e4fb482dddec1e3ec9371dd1fbce6c5c24bd6838cf536c963a4f153916794a3dc7e4785e0a72806180fd24450c52cd077e3c89e613c66a8c2a046cc5b4e969efe1267fb992310c8d0add969966856dcdf42ab9a337d253c86e85f1a8a0919335ff9c0b1ab324d37acae31ac3612415505bddcd2d8fbc586485bb3c67d87b014258b3b47d145f3ffd778b89190f7bc547f6404405865864569361b357c8ff135f0897acfdc8db209ac7acab5a7c619e0439b3fd4310d440dc9b0a3382b68d3d6f5baafd8cda23cabd628ece4994e095da3abf8490e02961e375eba4fb788fc84925974a3d8319a49241d48d2ce1d29c33065fdf91a68f01dac7f27c2e9a4c6ba7774cbacb6350810e0c74525937161e5d600676920b8003"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"001af1fe135b253cb8a503b9fba79dff98ba19452d31eb0223adce533d668b61","proof":"d683ce00c85233cc02447c9f7b3aa60d1097eb1f79413c261affe6ae3e7d0b0e34a2778ef14af9008fb2fe7b4624023a6d6b62b7a24ac32eb0377c892939f12ad6ad059bd41d661cc5ed2159812c26e24213a5195bb0d587bc211eb71fb5c12086a583c5898c79ca2698be169deb0fac4154a613159ccdaaaf4dec4a4f9480740581d1bac4dc32e6f5a40d2415fec48efc8ecf930528d02cb9aba3977ae73b0a6372557e25683f615313ed5db0b3a51b1a8f8a594913ab548a8c1b6eff1da60fa57fc46e7f1ebac5077021ea14afeb0393ffae594adf1686b9c6bc86a67f650594c1e094b4bc6a11d6359e7705b34d6ec5172667ef9c28ab86ae3146a73dfd6a28856ace1ff41931d4e9544758991cb8d5e6b665f705a9e5113367eb1d17d2784c634c204fa4cb773e29d05809b8e07aa79fd2c785ea7c3447afeae7a1c38964fe0b91a1d82cfe887918b20eb119e37ef428dc6438b950e13b6be50157b76b07586ddc873bb9b036d696dccc149bb46f8a789c0055d37854419726e2a5e0401df477b2a83e874dbaf3ab35188658a71766b661d0d93c6000d07f971a6ce303396c913b873b8c956bd57ca360709a2079e88201b91df089d9988394587fe7373a9265a3dd5602061836966f13b575ca93100acefb844eae35d2e65f8e25aede44f6407b2bdc6ad47d9fd2e28ec414523504f4c487ad18f780c687e8aeb7b9d972b87581125d512e5d8150b33a903402132f08d7cd29b82a6ff7c78d3c4fb09b750262e3495c42392dbbc027b672fd2d1a25f9841b8a6400289a4b02015f85b0139835112eca232f0a73e70168bef886542e36f72637a869e7273ff6a36301ab0a9720d0b6004c0eb65f4e59330a550e2cd4799389b49494b659bea6986dc18f0251e096b0df2451ab47429b6e3ad12c16ae1968cf69510cbe321ac6dd6ec55a03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a618e6fbdaaaf0b5be0349bf3f9e9de3f97a769e3d0f1dee3601af853e61ce1d","proof":"545774964f9f3192917d068173c40e05b1d7c4d8d137c90ee9ecbf2c2de4c40a84024c2d712249df23035f3b15595b2ce0dc3740e041a846de53bd72db14e33d8e11df9a69e42827c8c486cbb92005d82b5e9ce82a472627b45fba3a8b69e9226c6b8e5b8737806d42abb4ac8e9e4222e3a66d3477d6aeb0a63a02b621b3e451fca1729722be9672a62916f1d855740a9e9d91eb7c650c196107024e5f732c0f69ab8396d77f5fbd574056399dc6433e9ced590ce8e21cfe6243e0b903f2140f31cd4f61db516e8bee5bc922b760c10bca8a36eeedbe4a87b890b505221f0601bca66f5ca385e7c0743cf5ad1df1b05788168305c6dbb4b5c72c27befd43d470c49463c0c8a8f47f7155aeb6bf3c37f8bee08aaf7133a6f2db6fd612ca5cc1569265056974cff226a25456f2c4361b8cf16e15f53ace7e0f3ff034d3aa438b0e6832738499d3a2436e37ad78658218b9fe280c18a0840e7c1f21308a6610116b4ccb978a3ecd27f52f3fbd8ae8f7f420bd74e2b26c47d73e2f390d7045501c18ac723941eb5745ce374f31a2a728c41dc749bd2695d5a6120f99c6201a26b455b6b051edd44b74342998b667a3eb720c1832802587f1ffbb6e787e8d72c5646806fc33966c0aeeecb0aaea82cf3044e8f77bbb3674c04e7914619bbaf78b9b50b46f4a2963e2752bb8d6832e4f458dabb9f1358a25144ed69603eed13e88ab2c869efd764612b61fd156e3d9e2fe071a05e014446955911ec9cf845609fa627d12a8b9e72ae5a7d35b074454f0b9b93cf8fc36c9a5d8319540b7086246b6ef3e88ee15bf3f0c7f980b34d68dea962afd31a5107ea71912577f740a397e7fbc4fc6482807e6e035a8da27dea674dfe73736fa0eb53f6886801b5ef8400db10d0bd434660e1cc529cbeecc713413367e7e16d1fd78fb52d0b48a21461dbf244b09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3801ac5e28b78c8dbc53b38076f2acf0b0cf909c57600d1756d2f1406ac00d10","proof":"cc015deb4663a30212ea116f619e5372819d804370d631c769804b8e635b56262cfed7e2989ade00ac28eec71062a8b515eebe7c1b5a5bb836cbf232a336292656db4853de68ef9f3a650475065104b7052139aeb9a2f4593644de82280682467405012134f518125d566d0f25dbc7213864039624c40f9378bbfc599fda4b2617125c2c80d7b02ceb64f7167a164883d411e8866193a03ca1c9fbe963ad6005d30b2d1484eca1c07bf6c6abebafbb5071c5cda36860030a901690328367e901a560dd53c34b922df86c15e094bf9eb65d1f38084b779c178eb3638955369404627c7e2736d8676425c5fda2810dab4d082d0d05f621062792126ebd1e10b21722181cb2ac4b92f7b74f00e149ca20a2ffabbb14c93f64b68551ddb1af5c91358e6c5b1ad95f05867835f3f554ed47b644dbaf00e324768b116c15b63e2f217a3c3876e1663e5d3efe1113b5e92b7cd4b8d82363812ba464bd3fa507a38479778ef7190946dbed289d850bb9827f352fcfffba8d33a7bb92e691b12e46f4f66ba658f58f2f03e97bbadc22c1495b9ac5bcc514d775555907a4079a2993cc10211216d18721a08d7d5cb337eb3f173debc85fdc6b781e07e8962ce9a27c293b29c057113d559256ddeed2a7fcc8c48b3bd35ce3db9683b4cb4bb194ed8f953422da01f2267fffe84fa746ca425cfa2c2f50540860db211edb38236e17c69e1233eec4221464466821eff9bd086100496cf8660ea9036f86063696dee258a11c45e0d1892c3e8bb7f8c4b9267fdd11837ee244f2d34f98091a2d27a18751d0fe14dcafd18ac5b3a9dc5c310103587085f32da81206f28680ac571d932e5768d1162d6a038884038755c8c81d5594aed6c6d060381ad5182ca10df440c3f8effa0a24578a78bb563ff90b370b34b892c60fb47c2745b9db2e7cd3cd60a1ae6be700"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6e3f2619e43991b5b205df453c98acfe679573c7e47b4deb9f3a62b5e16d223b","proof":"40a4f5151f2ecb70a1465f986e669714dd7146e7cf71a4eb40ae77b83d5206487439663735f5c2bc8abc522763a2780ed36f874188e495fa0da4197c5ba5e365143734c58a32bd8bc2360a056ae3bf5965062b31b95f94ac7167dc766a54146986688e8ec58ba232b6fe0ba9a5d4b586ecef9a0bfa1b009e9c1ba6807d08cb6061cbf51bbb3f07b95e5898efb0208a1fbe38692c178458b91e7b30cc08c4950a237b0592bbd83a3f5e76ac411743db7661cc4e9b5bc1968b8c55d6c881915d05d159cfd904c438d626ad3153fa0fc9a12a11ae207d0e80e0fd4139b471a3390cee354376e605feef9107050e671138ed3e875fee634d0e75853804ad7f58d73a0a23ad17db042e824303dc7de58d1808bdb8e4b15355272fc8a69ef5262cdb30661ba2aba9c546dca732d90de7410ff88e11550b3c53013ea16f2ceb51c6c0393c7bc06573e6d2b88e484251f8b2332b508d32c8875066e28983b64186cdbc63784dcc251c97c1d191d3c3b3bb8466b354b8077844a5defc05e9824bcb1cf11e562eb944712bd6052705ed9a5d0ef74dace15860be52105b8cf762d1148294302cb0f754f922c428a730952ca788359a4717edef1c6400197b8fe4719e83b206761a1ff602d14c5ab99d43254d76c6f35433c7dfde0537f27a661ab925afa8300a944818f4aefe3c809934ffad33c562ad553e0716708e06265a901c37eb994230727a15a90037bfbf7755bd09b3cd6a10997ff3dc164ae1966e55d1a9beb73af28d101bef3a373fa4fe54a1441f94b452440410265ffd377e780fb59fccbb76debf8a84953d71adfcc82aaf44cb0e6d6607f4bfb24343f32f933c47c8323b0752fe28aa7e87933a9fb785f1ed02bc92988cf58d3d2e22ae103528faa750b004676df6f6fac618559af26d3f637987a969f82022e612db94cdc4fcd812bbe00a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2c44a065d9c38236b6039f390e15e086c5878574afe159a1e329a7e1bee49a46","proof":"ba02dc5be26611da0a0dca97f914ca79f19550e85401936462be6a79a1faac4e48b0ccfe73c70a95ed34b889432ff889a75a0172c46c2fc9f18e2445ec1b4c069ef8e5fb5a695be0c590558b900a9668383c291299b9b1a1aa9255e8159c34673054992128914a942daf2ee5355a3752d6c1291d1e5d88ba06364db4be12e9353f1077e5ade91b3047da3b1a758dc6af3e561b7472626fe5f1a67b9404c330093f3122444289e142f0a012923cb6e47015fc8c4336ea9e216dccef0e5c0c8d013e2ce42d5159983652fafe7b07f8b8a234497530c7dabec21abdc9752e6424011008dafde7877049f4f6d710f38d87ce5c10e78513b432eb68682ba6c3a3455b2239d3ddedc2d5d1e4ad60751755bf8e83324a7b2dd9b32094ab2c17b91f6a1124bc42240ac7fdb0718ddc208d8ab0973b7f731eb46e725b5f206b01f3ba59763eeed3d583b5a3b6bfdaea69180d87952bb3013c3e3f7bb2a54228096fcd3d318cbedad48fef04c86145f0f5e5a2e5ddf6d1abf53ddb3ea1fb253ad74034e10ffebe42a5effe95e63dbe9f736edb673eba96bcfff29db92f9cccf2d045783b20fc6e934974f77bb95cb588763427422fad25a0db32e454eb0fa758805a17124188aa98080d875d8345dd12934c58811596ffaf183172b7c4aa2bbf0d01d81e2eb0c94e915941277128aad33edfda9aa759cd3d67b9212d2f84d27ebbe892404ec2d57aa72ed4d32fb0f4cdde8a00abdf5ed87e93289c6c6031b6496c1690e77c3eb8a72bd44ca6f2cb7ecf257c1c3ba05f71e55fae1c13f566da5a681b01525c3a4fab0ff552d5a1496f23a3d099875831b70c13f54b56ee5f8a2ac51dfd127a2e5917a54a0edc671f124362a2e041c8a8c45711d738c51b548d0ce2c2a59f0671030d8f2f9ce8abcb53b3a1d6de31eeb0eb3984ffa8b40ad82df5df60ba3a09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"542ffe1cb2e8a99f379541c1d1fa96ca9358b867f8c8c2235305da4301929b6a","proof":"9682308464806ed0e9b39dc9997e0f377c1a4dfec01c7b5cbfdc3ec7fbe4d2695c81c5fddcd398e1759e8d10937f6475c80afc8495bd8515708dece94f79d63c420b3c18b7be0e371bca9fefc1c69ba73cf6ab27b8af63636050ca4469eebe7914ad58f237bc69fcbb63464a11f8255c73f27cb52aba213cc6b4d8a1487ffa7faaa6274bfeb0bd780c0dcadec194763523e3eee502f09300a43e9a18c14f290b570d266316914faf239af16b506b2937f4e6d66b14b99799918ff85117dc0101635fbfde4f846d7bd25d5606f9e1e33e50e7ec42f053af0d481201d1951cbe0d1295e980b3cd5e8526858dc02003df421a5017db18eb8eade5691dfa0dfdf27c70332e02fc9f20008e70a05197088ecb93cd7b8ba4f2860dd9a7d4eae0be634068c2f18d7ef9b30d4f8a85da4c6573cef954cf9671ae4aaf3d4761ab4ca37e7e2490be55685ba588e97c89aa6d93eb056699f387563493ca8f07d0ed5f8c7e664addb66c9fd7d848b98c4324faf4ba2bba4d2b166784f9869216110d4efa8e23eeb6b671daabe5231624ab0b304285d3866665986bb79ed4946e2f75bac47a09a298eff14d2cd3ffa2dce0f56103154905ce264f3025fc6475b43602d0e78b13de63fd745946f8e4b1350f46612630bbd539d84444a55b5f17114fae886730083831302bbdefed9e5519710a6cee7fdae95d60de7781125a680c673edd3ce76a9814b4210b36331462547b6d8509cee604406074e0c943347c7a87a9cbe78e27d44bafb71f3298c2a9e8f1089787f6ff9c414f72cafff68545097804a97cca544a4b7503d3fb3e4366d3314b56c755e238a3941d3d4db8363f4cd09609a947076376a48362eca2c12d13c19dcd75cd53cb75ab6de9b04a6887f6545dc3768d0ef9f765f06b64908eb5af0bf933441e0aaa2ad5b9491849eadb4913f27772990e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dcafd0b59bb5be0224fbd50536797cb5539adb504a57c959f1b07d46bbcf3c62","proof":"f03e658855b93c262cf6413fd18a9cc46a1065a47ea7d484b958ac1901de5424dc513f53972f16c309df2ff999bd7f7f03ee171e18ae56d2f84e335c87151c76d8090be1941d7aa62a6bf90c4e3a7e6e0404a5e0e93f28473be86e690fa955769c4475c9e74714bf78d3caeea4e05f03c2be32c7e2ed686f831b315e29700c1bcd73007200e6e98c9aeb6857abc1ad710765bff96334b8535cb9bc1c24530a0f1b2f365c75a0f0c0a9655f8cd413802a50fb2681986b3a440e98035ac6ae470580534b0dd997d140ad4c3232f2e51a5cd1bbbb98dbcbc5a5ee6cada5590ba20e6018941e019665a8a682775338f48b40870d3d308bdec25390ac1bc6e039b450c820fccea0d0d1bea8baad1251827451a0b4177a128483ebaf8a3360a602222ec0a46bda31ea17b0b238df0ce4b7933a62e98341433c544a544b2bfe2da7fa56e47ad45122528bcae6fdcf6a5f02ed35d3d4d0754162b3b38f11a10085f62f2cf60eadb520526a141391fee0b55681a27a6d2a4e78e6fa5236b00a1ea584f5049e211fa37007f7949a0d2f32a508bfd850f3f4cfa4ea619736eab0a05b3f3e5a70ff1eca4f37dc7418634d2bea830d3ccdd70db82cf993642de8da508c435d113a42cf63e67211f5b5da63c22f260a671e01067d25b181f4d7bbcf6a4c9db94510bf8aee50838cff8d4fd1903668679272de4b478e57968bdc4e433b8ec34f7dd23828b984630999c3318b21bea257a0e3b2da5fb00e70df79b1c6d10294673d1a92bf065c5eb5d234433398d42016fab6d5eba80d672a906b06a8c313c6db0412f8fd00ff66906cf6e711f46b9fe1f3cc178d78e04f5bcc8ab309d35a0bde44a680a0ae452ab36a4b576bd86a2371e46db094d74590edeb97685036887f1e09a97407d96f12a0aa7966371f67e444a0d24872f6f017f235cb139f67733fb801"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c619c6baa01312fcf28bf531a49ba6cc06fd0382ee91971ad21ae92431decc3e","proof":"0c9304a7150abea0fc6cfa3ccff56cf73700a15507fa0dde3ab223dd193e28776aa97d8606555da72f2c8cba19cd88992b7fdd566b9d48bc7bc3ebebd71baf09807b67e773b03f3ac262421f1860f2268377fc1174207a8c31a68d10f46a55509421f92b0003e7ec21951da40b0d4b4faba2912397f7b2af58c35bf7cff51b3fc2559400ac5410aba103012d65cd5a694115ead8bbf3d0f903f87c5ea789ca0ceda8085ffe18468ad9bc678338782d3498ea470c4635e6eb2e15ff63f0579f02ecbde78381e977095245e80969c1789d12bb429e5daf5d087f03555a6adf98010875c284b80b1809f0b002c61d4317b487e79861e26d4ee0d028d1f6c4365f6ae21088fa6d58cdfdbd0c4bdfc2fde1d62cc74721b6dc6df06cd2dc3db59abf30926581fedeb9fb6ee0a70045abd79241953ff5587dff0d8a0e4bf785baf57d2a841c2dc11a21bcdcda2c1e95da6b80c1b138494512b5d7557f621b6b410caf648abb6271e68231dc1c274cbbf43d2ad328c7f66b39eb80b830f05a3accd6be10ccaf6d3070e6afe1b0c44d40a3bd6e65ab3dbbdc67de7130641981106b8e3a6d247cae6ca942a0025a452789c905f42476d7899676e8ec8c4e20ea9612352a6f4a2345219774c1943cd2e1d2ffae0ee71b9191cbd1d7decf5f33d8761c3638307a3939bd0750a863559955853d70c6945a01fd49731703a7ba738fe973cce26b964197a469168fbb5c9eebbfe242f8d04eb548847345aa4a9dfc9bf8cf14d868f4b94d7b239291fd3ac0f61f5e7a75d0805cc5a6111d030183069beaae7cc4188e0ec2b9cd1ddd6a48d46c4b3c823e3b4c08fc3fec658e9703adce4bcad4583454fa0f7f0690f0bba577301e3584268d865d929ecb6305acbd264a6576fa3e0cf7e169e6b472fa4e83ad8acfa6c98c1e5e42be99b7b78d15c8c28859d740d80b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d818c4e0873a2295d12fba0205e3a1701b7cd12190fd1789ef7864b06db80877","proof":"5c903046d97fe9a08f25a878d2c888a4cba5222fbff59919c2d03cad6bef18363ca8af39390fa3db4d5aaf199f7cd65fc071265a37dac1a10fcf5d4b0dfb0d5d2ef9c08d6be9bc909fd1111ce14b1cbda7c1867d4397a580f5450d38c868d63b7071fc1b4f32b2ca76a59ed3c72d49c66c8c699ca98ae7deb4f5fcb6d3e3134e707f886898b6c92574be4c570edb66b1a3bd9d18241d8d5caf29ad07a28ed108557f6f3a6c1c989d79fda4314dba596d05e4612d2bce427c5e44af2b9a337a018cc09996d8e50802c97b26076ab8a6895bfcfc5189612357cac7a683ea3f2601ac938c6709967d7522101eff891051fcec7498bb74fa39d72af0898f49e8480b6eb2fa6ebaa0afa148a8780c3288b95d5415c6cad9993d6c3f027141458e057bd22a8bb99d3a8598b150686d68b5be8eb0a507ff5189760da4cd59348fe4745dbcf78612f0a57bb042d3ab3a15e620c51da1789ff8278b36c516a97117b1d84c762608f9a13e99e410ffd2a2e608f0baed015fa1824dabf1c0fd9a5ef0b6b01d00ea54beb993d94a6430c3e56356a41f1f5ce22e5a37d2887bf7611810a920679271b66ccad72590b0e7d9c2508d26be59c4ffaade920b25c4f4387c7367205a58a02315acde4388657ed075b0bae25e9c1656966b7a13ecc92a1264bb87972dfe54a16d1bfd4f07acbd661013652b50b9ad348730dfc6b966ce80ff6a234f7828fc15e8c5c1fa4771bfe24d0c1fe1295a826f528b2eabf39e952cb34c3a5b3f02e5201c827e37d253c89eddf8e8772f174fdb0d31bd629dd0e098b2dc54121a30ab26ee4bb7167b449479e00bd25c0d4adf84cb03e276c3e05707405ec09a701ffa081c59ad9c72a421ab0851add6206dc75e9119b94165065ea0d8f84373093355db0bcf446862031f973fdd06ed5b0afd88c962fe62f4c22215bb15a2f40f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d64649474862ecfde7b95afc09bc0c94f1a02da1a03afe195b9a8875596fa841","proof":"c8129cf0b31164cd8795b729ecc267f21b8beeffc3811b0fc1ba8b03e2779912445124564c6dd8a37d08bf54903f49fd085fc4f8e47e6fe350ca4b3a93ca297c6cdaed5906d80306aa67b8d9f4275a84d56428a95bee4ae1ff89cd99dc8c601bf0565925e70a100edc2093fea862387ffe1da54fa8ee4445d0caadd998b0df74e0590e7b010adbc917b2dd5dfa833e5fd44c3671f451cadbe50e10311504ce0b25f85b2b242d430dfab1242d855e8cc789d1e238090be11639fe3d6e766a160ad23743651200796e2c436577631e9168d05bd9a189e8ad1c8e16c592e6523f0dccd90b4e8dd6926335622d02a25728141a3c2fc8d5bdca386b1718363a51033228265cccf613781dcda12234f9d74f1f11c54e9b726fc6613f1f15690613314f7e7c2437b7af7a2ae219a73ca901a3635c713c47eb0538a42e0e705372e87951ece6f160cd5dbbfc0a55e93f7655e1146d4b0a87d926358c218e8b7761ea57390ed27d6972b1af5dea78cd86b1ec804bc4fb2f98eca3d615f61578c3e52c122260db4c0f1f644dc15cf7309bbde6d2fb3cb3e6954aca45b265226f82aad18e472c21a6d9a3ad740cb4675fa05cbdc5bbf84d85debfccdeeec415a8657faad47c483e0819fcc673dba1bfa8671eb1a36610e10501bb131d78353da326b8559e77308428201b2ab6c8517db8cbbf9ec7f9eb672f8e57178e289c994e4aa661f20404a947fa85ff42590066fa77415be4a1d7353f9546c1dc9eb26049610f61492640e95e17179acda0aa58be97b7b083c1e615ec7b1e6a92bb1932971076a793417462baa3f14c02b580b28df1d28b82b46f847cd05d3d3c15406e0dd9eacb1b571c93d26bc0cadb5d70df45ed3bebbcd04f8caa97dfe737ab1780b3dc5f4f4f0685d73650d7d595dd6f4d7b671ff4db3d407aa872bbd195987bcf6be9ed0dda05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"14884e294261e11b6ac914273cbf49213c7de819ebbafe3d87eb4a381654f103","proof":"c817fd67bbb7315e500cf7ff42dce5e04c91a87b67ef33ae3879ec6ea4eaea4e58bb5d88d7d9cfdc4136355446f424691bbc3b3938ece171bf81742649ffb94e02440b9a57aba42ec7d5191c668d61c6917faf11a2c132a79a365fbaa9897013a8358d956fd6d45452934414e7d83e6791fb4de7717f9fc66052ce01fb84521feec0a3f11e3002ac6004a715e58a6cc51ef375b999551d8a6e0cbadf25213d07682bda44afab568ffa8f521d8cbd5b038857ae033ffc100beead83bc1e88620080faca06ed5eeb38618909f424ff280e638dba3791026561da6a0b65eed9db0f16723f516169273eb708b72833dd8f0b1dda46fbf2da40023d52c1bbdd1d2827bc406e6ac1f0a34d57b0b435b04428b378d8b8e7a156588d4f23c31ce719453770a79098a73903d1c4e9067c65c5099b3b162bf916619bd81df9eb7577913b78bc9f708cab6bcd472ae73fbb3180be66d3b4870fbd72f6b831fd1f8394cb8f4e7a01a8f9288bd0ff38ceaeeb97e5aa9da9367a0ae243f7864d95d34ae250e24d749d4a9b5ddb6d0773946bfcb42702d054b8f135ee642ac6862b6dea2f3ca31a364dc78353fc3854e2d70dddd831991ec3ff670a7c164f13b58521638b4d932de0ede54d144377c9c6a442ba21db088d5ed41d18e86f04bdf7dbf7fc3b2cfd01561411a9ff042a848a85e57d38b8f682dc541b0b2e80956139be990f69bb9278624c46ea683a5b6ae0c25231944d9a013b408c1600728649f435796815e89e6b827ce4d5e14769e9fcbb42c384e36fead4388c2979cdc0f545cdc6a453675569b406e22a198677d72bac2a193cfa4a3fe9798f2747b9a1e55f747c18b10eb86ef1660ba29ec7a896c49331adf078c04308a0cf88364b8e74453a2fa9a816aa08db0c9e1ccd219bac7bbd7a8436b70d1d72bf89698154e310c165a016c0f1b90f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"30b4148550ce0fccbeac28c19b6910cf1e031762200b1923000d880ce5239761","proof":"b094ec9d10990156ca87caf38d618807c508b4da2f11412b4c64ce319640ac483a0f17114b9bf09a28f50822f374327498ac49185219c7f29d24384144998b1c20594f867b6441db6771a7bb824425485fb561243e38ee9d09acfd40e4bd595b803bd3671018b17e79fb4819b73b6c514a4e21bfa786b483a94e79c21744e01b9eee086900a7a446ced67ac1cfe2d4fa7fd87133203ad67ef25f9b980895c603032241a063a5d0d056601cf0a57e893a9e391982bc74f54ea9be07f2aef0f404cea9a216fec6c3781e8a28434c8242f0df1d680ab1b8df89bd9d77a1deca100d9e7c7b9425d867fe939bd17a392a1e0bd1eabcfdb2f6116dbb6f024e2314e8152634edb0e871b8a5a9fb701ba3fdfa89d82379b90e05b4ee8accc03aa4ad8c525859b0c50a626e443c6e805f76d9992095e8163352b7a751fecf812b4ed61d0ac0fe6a994cf50ace0b01edb66bbc7f266774c24625da77e3325d34780a2f4362162fb71e2ec7a0d53ea4e540ae80fc2fbe1ef5c43dcc02ea59e9d1685497e56b18afe14ae2931104c1aef41e98ebac16105c5a3a99483433156015a705c24c5be63aba62978345aca9dbc29ababa8a3c40c9de9b4555dfbd0fffbe6a8c86381ceeac8f615bd3fbdea286a7db52489affcb8ce8390fd2640ab649ace4b818f93e6eeacce6531d62e8cd1338a3e7d7069eb849dcf214b7d21cff985bf9799e330e9a4683b4b437b4733f9684cdc59bb75c50e0e86aec636c0ddba5d6ad1ae52c59b62f2346a2b4d4a2b8394a79e9e0cd813861166211b28c7aaf322463b96026342c0c6076f56ae6a4807052a551f11765178e1e3081151e74d2466208b77a68424c1bd79b45ca55722e2a2c1f2c8bad82cc528a4e188f595824d713904fccba027b6ea1e8d88261b30f27f3998bae2b53ac803317488bec932af2ce7bb8b91005"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c2967dc35797b065f2e97e1e378af902a8110cd51b840533a937454a01eb0707","proof":"88f5f6d8b0edddaddbf9f871ad9fdff828b35f54e97ec205d42148342e8c8f409cd9c34092295ce830ae58a5e9b189b4be5266a248885b625c289edcaed419358e6670b9c1d21ba2a6461797bdb556176e8a68f5dbd3f2262f3997f94012dc64a4d597ddd41cf18327ddcd1ee392d09ed9d695484581fde183db100b76de474babc87134e33d12048f985d89124519bbfae78a451cb7454e234ca360dee24d0e4feade79c7d4bc35c52fcf4f311a4fe4a4816ae878c59ec177cba357b57b74026bf4c08ee04c4348cec4f4c3e97423593af589c3dfe9b33e473de6a821d7630faa5c55c68858f0665cae70e3d0b6d3816c995c1db2c0a55a34325368628bd60f24fc1eb7fbc879723fb02679137749f807796def8ee992b10a6b8e4f0363b976c0394ee866b0de254df1a912346e5b1254d4dfb825e6ed8ca4bbaa336e51476af2073a888acb417fe92940ea913bb6e1f247c8a509e66d019dc684af68edda05845b990f028fd56aa9183b6382fec0dfd72c022dfb6a61460eef8d717bf29c32201e6bacad78f70b1653dc30b36447771c08e08af8d568ce96380fb76cd8ac495456b2a91291ec5e80fc84f596df5a7f860dde4eda68a503e1745f2a79d18a5fc24e5a31d6b8a5b34164664001edbacea8e81e9364ff5bff0cee002cbf4d7b1e0203d5de94a3d658f0a566ff7f88d85ce4db2600c561f82067b9af5c85d0c904e85b35144bbed102806269c6ae207ec771d5a20c5103b562850288c584c986534483070f4ffed3d0ad0759854ed7dd77e14f77b42c21c047005ed4c33155a526f0fcda365dd330bbaed6cb2e823c4fdfb7a8d8f9453e042407059151130aca437b6e5153ec8fb5837e7c1f7986fdc3d9a63f43f3bf7413839ef06a227f9ee604c33a08e239834d3b918cfef36a4f6716f13d6d35e6c9790a86cd2b71e9e9a70e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e4c4ec9c7ed500f83a3e56f1687c92b96f0108d980851f441f14ea0c0c362102","proof":"921258d341afb37c13d962a9d674bfb84c364058e09ecae09935d591563cf82f141b43b3c11d114489c9425d2ff21a7fd50ed273c414d2e4771707593900806906a77f04c5d10e762dcec94b9d0206c7b1622c093e0b8433a619d83ff776ba66f08018f44fea7c3c9cac08d25a9e4c36b6c6ead38d0ffbaf309a70217e510a5709a7b39738c0d72c40ec90662ba37e8908054da9ac0f0169c69993283a7b8e01e708018578a8104f0a603cca44a5f7fc0c15a1125096206459e0e0b127c9280f44726638bbbbeb08285077fc2df3c4bc92bb3a921b2e2e066d11e6b3d0c3a8014e5db81a237503381e0f665822f08ad0d0086b87574d96a53a57548c2ed2f766dc36c58f26874429494b94d180ab357f3e221267a61ad12e91898b823e004a5a68445f000e43c674ccaaa8a5fc27adfa07e99412c0666b9c853069245561013066111516b9109a77161532ec649067927c26a81ba70a8890ea449323da32d16b185a4e97af30eec5eed5664f595cd96f1e51afe097ca49a49c54fc96fe8cb607426b1c72520a7740d8c29c81a338390b7afe232e872cd6803f1f4f5229b3e50d46a6ee3cdbc97018fbc1b6483256c99693575d8dced69695b73827976884052c823bbe6856e3c52aafce693b34fb9147404cae1dc0c57183b1c19e8ddea998283cea5697efdbcf6ebc6d8680b8c79406ea5e251349ea3f0157b62438527cf7294616297f4f49b76edf9081c08ee38fe7af11b926b41c31ae8ec3bb5a5f0bf310161aadfe8fed1ee71bb388a054887a7f1242d00a19065a53b401488f6c325a5cfc688307401a7c07594f7d9178219ac71cdb7b8ca4533aeecf8fcbb9a8f3ef7ffac0960421050af65877dd5d5486846eb91b6ed1cd15917c8ac4b6904fdc880ae665058d0a87880ce80d2b1df342b5805c2795c2520344d10bfcbc8b2f133901"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ba9840b57bdb80c22214cb91248ebe61da78f56239a884fc9e660834e6546608","proof":"fcdbe9d402bdd1aab00b579db21131df44c81b153089122d00b8acb3369c8d0fd8876d2ab8623a413a6f73fbce81d044e085084ab6737d1267a8b8f508fd807a1eb82a84acdc57d27938f34c6c85a7811c4429fd6708b9b9b7d55f26f36281664051f870167208169dce8a0f02ef2ba77737a9b0a5755cc0b1a2572ee05c465224cee197b8b82362e00395bfd951a00dbb7bd932ca12326657bb4cc13f1d2a05756af075a05966b535532fecdd04fd65bbb19c2bc120872083af43572c5f2209d4059a14aa583b6247df1296e40f4c6610a4fa3263ca3f0f039cd1f9cf53cb07920a5aae1da1d36c4b2d0f20c0b0ff8ed3d785464e38b2f10425b53a4146424cbe9001ccc0eb00ecf7da35634583a41dd344e9f98a5f5d454c4535fe9c865450e8b4765b6ff8e06ddf209df499db39783ca01d8fbef1c6c8995a4dcd40a00d5e38e53116420697112e0beab2254fc5c3f6d8d3eb7b2252760cd4bb49a5af36457e1d4e1c5ed001305c05544b0458a8167e58809763b0997dff18a6f2b73c030e401fff5d23ea79757378809c684fe08904034ff97fadcd1b6ad9e5f8ea9f205d7876ee7072d83b22429a2e64f2722e7afd5eddf0bb993bbda4e61e87997d503756ec7fba05daae0b4f9cc1c69b7c638d1b4561a6e7e691a590b004c0b09b3d5ffc9783d02e50d8d0adeef3f58d0ce165930b5251de7180a0967bbb8883de0b1c3a73e474276549c180863a2a9e3ef27882f753d0e516656daab7c918ff2cc154749f1e07b1fa67f5aac0b13a2a1c33682ed5ced283342bc4b0f0182d68b2bb5e241ca316319a14c292a166d68a9a11fd584b0a373dbc3aaa36e56b18514cf8243c47a42ed9e07de7bdb185856974bf7faab2b06ad6ae56187d9a3cf4698acd0877b0964a285d313c5e0df682f44d404a06566b25ca8fa82ee0c9549c78044103"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e6f280c9be7bf5e65f978891a7c7c32e9cc6de5f62d56fc0338e7186af10674d","proof":"d0675b0776848d4b144507a39860612b6392c06b34725aad6f9bcbc79f06607dac5862e0f8c9ad7ab95f8b1eb031aa57a1f1e244403993906d0268166fe7c572c64dd278b3dc451f721852510267ab72663e23892b7c73eef2f165fdb7d06a7ce6d688c654a85eba90759f4d08ad36ef2f18dae4bdfd80d2ea24a058377938780d33067aa73e42446527f6fb1cf2c8a9add019c8b4bfc70816b63f5f637fcb0a5b049f025ac39a19ffec8b78fdd1d028ab3ee22ef445584564ff263ff20bdb0fe4412c57acf988c15164ea3cbf2003d44f11abc48be3f24a93dfe140c781a0055c9b39b2971adfe081baa41144c9d60e55e2f3d3f1056486614df5f5871b2608d6522481e1f77078f7c69c0b3bd7b0065fb5027e0d682556b9aefbf51457fe2a2474420a2522c26ea94d4445051cd859c18401000a2b817dc5cac2456e28e010781f99a62b370e0cc47b47e14454968f1ba1d56bf757b69a700c278d2244bf2d0615ed4a6a6fa7f518685c5c841fa26b7bfbc4b7046d0be51aef755fdbb05f207442baaeb0ee2c23a1707d31a083eb7bfd4d32eda5ef36ada16200c98a8d6e1b60e7ab1cf6b19f2cfb194d83adc6ce9905bb7790333834398012006bcce8905c147910f59020b564b04afeff9ffd1b19f578e2a6b8f87bc72f9e9e8c7fda8128c0602a736f5b79b0e61c25bf4513572f6350653946b970916e4a49de8108ce7c5a48af00c31ee41503d78c615749537937e2da33e3819489180d59d774f3b024c4726eba7efa5e908147e38f90c6180b84d0e62975a6fa9249f9339f237389219a55acface00ed8f18165910b00b24dbeacb4e32d5b4fcf34dc7691bb366790d4c2e9df1e0979429ffabe04fcd299ba5db3c01a6bad02e85adf377c851c37808caa4d533f70b07d1f45d74abe7e29b701bfc59e29d1255acb0e52299ceba3b02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9cbea765ecb2b0c6044f20ac1baf214d26342f215e2ba7ba0d4c95a2c3db7230","proof":"48589606864b92d51da5829a6519c76a8ad7a23fdf937de7dad097fda48c9d42e23264b770ef58d7e476c58932c03f33f34d07d53eba17dc953038a5d54c406610d3b1ac7fa12b5beeb5e71ebcc96a0428b1fe35d3e8ce90980290e728fbfb182afb525643daf86961f971a4dee390146eb1e344337ed9b547d6bc27db337c2f57aff31ace9756e7d7dd51fb8e1e240aa26c2d864042b44b5469bb30c3b4500d3980d8699b755c1c9128c59c4c3fc56f53463175cd64aa486c15a8588d37e9045a688f6019f964b77b22c430198bb74d55ee58113149acdf5d9b5663b04e7502f84ee30e059016536d1f8d8741f77cbb93412b7855cfa70f12a573f02eff3e16127867c56cd71396f055aaf92a9d74e7ddddab277e05572f2001a56913553166f4351a983108f6b36433c6c432675d1739603b6c0553930b833618a72fbccf376639ee7f71550bf80284670aed6fa47ffc09647658bde852d86e5344c6380f34ca7eb76db0f895d66502b7517fca0e3e8c0de259be21615496221868c13ef7580453e64f7b7854952ae7948dc80119980c8406a8dc6a9631f7effa5f9d1570432a6817d8ea31816f1f4355848821f5d46f5b43a0c66d16e84031b73ab20fd23190e2ea693ba70870a89f8ff78c0376e6f270c07661a0c7adb624875aff339b029ec2c2eeb591886cccf6ca20da8d0bd334198b938f46ba3270d3f179596eea664499354effe758fb2c0b7e4c9bc7ba5b4881becf4a14c2730dc37943e237fb35f455ae7c1dba1fd5549b9c108bbbf2547a06cfa4df4712c1b424db3f909cbb6390bf31d6f56486cc456268f78e3df0a76d07a81737e50397859128236e3179657020ef900cf3c507ad9c5cbb61bdbc7bbc247139ab81a842eaa3eb3790469f0d5dc9ac38593491cbb557b1bd3dd98d08b0fdefd20cb0050501570a44976ebe02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3e3000c8be7a9c5edcc705f75d41defed18354da14acf0f9bd1aa367628a4f3e","proof":"a090ebc6b4f87ec490ab3e41968d1ddeadedafc0151eb3cc1086dede0f3844029a816fa40599668a4fff48dee167bc1700ce2922221c278d1f8b91406fa1a917182269791645e9c4f49d31bf3ad84a9df0a0769c63642ae50415fc604017e61352848e5eb0e44f0126253251d3904e06be2a52a15a1f2251597168e337f46d0f5c570a974223c5434db68e62ee205bcc4729212ec38dd2b95f69bf48bc62da03729e369403119758b1249cd061245deac0193e6878ad774a5b288039bc792505357cef19a4e1dd4bb297eabc5463d406cc6a195fbd65b81d4e34927174c17c0c88c017e7270bb1a415f9d7749b7116810d16d5d89e907ec4197bc6d6f98f0e124ab31cdcbe2c566eafd8bc59e9cb17a03497a9d0636a03069957f37847e1ac1934d2565d38abe2e5b9b49d570ca9a6aaa1fb0519b775d8a15c2d796aba2e8a70669f83fe191f87a9e1d422ea19530a8c0d532a2407a1b2bbd3fdecaef7866d2e4a28e7c6594f44b8be6adc87094d0cfdd2c9ea8c6d6cf5f51d06eeeb30f990010a72c5ccda45f1a2303a8e464f365bdd9efa30f161d4da36ff3ac337562d6477c41d237aa6f932912a4f28b0238178aecfe360887d15ac1cd957321421568c53664856a07714e62e4e59dbd7111233c0f176df0d2d8d38fcd7aaf067e99ec44d762a951004723dae5da15eb866a776a6519138c5bc73f0f9d8f577191f6fc111c4c83a0ab2f041e32a3163e3121f8a544d6b531ee255ec7f9ad345f7cfcdbe20e2a4dfa276176d8f5a6d0ed8247d46b9d451fab4b6fb84079f0b174573213b2dca3e618b456044083273d2688b96c0e6ae8bc743376add1f24345528bcc83c314494ad08b5197e03ef215696d30ef9e101845653d77975bc46577d3a9866880697e9e7c208a561da6aeb806694f1d0cb2fcac337bff860b4f1b8fbbd2ed2ef0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d8394e0b86e97c92186f54a7a1d759a9bdd79ad47976a30651296b516bf60475","proof":"0a96ba63b988698472fad09381893cdea652ab656a4d18b5bceea8da3f84192efaa9a763af1767561a5d4d0087afd64f47484498e9086ec43f5a9e07e78e7b6db6154808a0e6de6de9f17d6d5864aaeb931ed3cb74076520415717605f18f46480f56805d9200d9601e611569ac8e0652e43b7fa2118fba5df9b437332dbfe23c03a860babad86be7fa15f04cf5f4091e533b126a4240ee894addc033f63a00bb5dc7687e9fd1cd8936f0ca9f6bf08cf676859de154a5edaa078605c83370a0efe8f9da65c687696bad9b727f2eb1dc298b1194a0f38f8457dee2976e5141c0a08297d0cf0e73c9513eb66735c1dc43abc9ed456349d3d77f6ebb2cc1d518d45680c978d4b8217fef9abc6ebc0ad62c146eca0f7fe5699622c608682d7cf9321c2b1e00c4cedc41e771340c7453f154d447df88ff6130d0f958b217809dad177ac98ebf099ccd4c5a8ab03122d3899717d66c1949be3e5fdb9e68463c1f23138302c03d680862cbd972aa332d8eae6773461255849f3104b3b50ec6e0784863fd4885260f4df1dcb4c35d6ccc52eb9927eac3ed327ebe1d3cfcef709e2632a5e6ccb9b37f795acf7a5626636e14910919b9c6687944b025fffc5e9a6793050564e21f2861d01d79b1e8b8361a8c8b1e676d4790a7607d346c745ecd3421df074f4359a23697a8b9276f7044205fd560776400d70652bbd2e02f60bb275c1e47a3a0c4c9d97e30ec313fe160ab0fe0c870eb967a946b0bf3b2e20e3b98269c23e908aad986159254fd6e56390c0978b906b9f3a40335ea4697a8dee9d6c536f6b68f05f5aa9e34f38bf8263658ae664209e840700e7757274becc260df8f92a2b4e2c3b9aaad01b7e4c0d5efe46b29e5703238148d256f88cf753a8a770651f06d6c44fb930d92722ab2456bb80f098b5ba68a5f635202fd7f70b5aef1fd7a508"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"624b565023e4e45805aef3c441a161e2f972f987d12ec87ec907a10863318863","proof":"1a89ca2052ae6cc29e8a445f21bf93d8dc406e430cebe546f24079233a1c0319dcde1c3742382a5dad044c31ecd0641bb4822a6a7e6da4f43b2a9cae1f95f86db2de18f00de379b77d7f2f57a140dc7b7e69848812621cad2fcb3c8ea955e10898a7be85ebf778a99e794f722a2345b69c244c4c5ce1473408e917f979cce45d2753bd44eefb60936bd53b6e5cef811dbf2766ea5e14be9dcba7a316f69ed90b77de78fedb0a1f45d6b33d39bde4c6d596a6c24586ecdb966a43ecf6f6d1ce035116f56655ca7fc9e7ff5a9e8ed30f86a7ab50ea0819b6e97f07108d5fc96906dc26c03d66e342b0370f3d2b1b3c31345bd6f17c1721096f7b04714c9b05cc3efae51f66b548876eddf157e5dc1fb06e0a04369896240a99037ab64bbe473047d0fc154027bf029d6b1e3176ef88e663fbb328910bc21cd625897f409f341c3eba5138fe7ef73db745fa8067d81bdd6da49b50e82ea017cabfb172ba560fa20bb45ce9d2aeb76c2f122ea486eb54194e1273ab6a23948d4587890711e368b45e4e9c9750655276a06948c879e2df7affaf6eb6801564cea022e58316f3069844e06d1f533fa63cd81aa893c8054d5ed2c07faae5f858f7c791dd1a0fa571b550c8733237ac36ac188f4755ca731f577c676eadc1b7fe14b4122a5ed8f3bab83a6ed7fed848bcf070d9371bd481cd2a73f1356e0b61ce01ae670e4801b9800b1bb6f25918b6bdbe8c341f15e6025b23d66dffe309bbfa18035ce93b7e5a5c0c44ca3f137107f28142b61bc6d5eb8ed54ff9acf2c90b463c9996b8419ebb300f7a5639ee0ab01269f5be90a6f9c03602d70ba62546f0573a31bccbaaac7b6dd15bbda8ac03eb40de9018e6bf32e78b364ca994c8a85e1e60a4081695af0a8c6a076448f4d0889a522cc7b27f3280d88559efe12310ae88d8c4298ea9c529b2a803"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5063d0eac504784fdeab1e912e551df3e0333da1a5ada305a57d3ad33f3a3a5c","proof":"58cc64dd419b4668d25619d160036dd9acab9cf4c429ee6202445522c964931220a7134380d633e5d64a042ab4ec86771ec8fba408e3b51a030bbcdbd7a74421980b699f69098f9d514f4f2baab54517ece1c5b7603c6220bb4d9d95248770764ee356a773e352c32af8da55d01d1eb0aab44884a5f50ebae6a81155315a80050be8092b3c65a2cc07673ef3c9a2887a35cf00cf2ce43df825e60ccfbc86c50cc9fd5db8993c4d00e7593e7ee704ae6abdad53afa5072f7cb7d399dd7bb6db035e8b1330c33b56d0c95bfaf376c58e9f76419be10a64a99145ab9a9586245c032454f3fabafd8d8baa929a781fcff2d0daddb6c6b4e7cf1c7f59c51f6bd42f685cb576296e3843fa93a9f5f527f9ac267ff26d13f5a2446a0c87f7417ecbee3bea02eac7a5d14e0b220a3f425c87c67863f6d3e32b847221daa4db35cc874c122a70c93bd3a7484e09a448cc043418076e08681d07a9274429887225c2a2477a70812d31fdd87056b7e2eb48c03e5009bdba9ae85cd8b8ada0e72e559bf57944ea0bf6e470d58c85a12a187cd59f5eac80db21335ac337d5961d29abb5a1fc74484b76fa562befe6eb027a1500965c7f19140c8d9e76679d7a51503e02c606627c206c860cbb7f8fb4e83ee25ac2f7ffb58e93c3b384fb58c9af1e79c6d894163216132400dbc57a0f08e4bb4c74550429de44701e010a29c6782079dee6e12ba02e57f50bf3afa89830ca1db5ea085df260272960df02c31c047a9c74fa6d0a82335cb92182c331550a129543df5db33866cc0aec1097eca0400dbda888277d9e69b7067572409fe59e704435daefc93879dbbf47b72a25c398aee6599cf61f774750c554daf22819df0cad09191f2e0ec64768ea70f1f89d76164dea146203818eed291efa2bea2ec83707bd1d875946c2f5015c974ed99b423c7af5cff00f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f04f43189ecdfde1f0bcc4196f7e268153ebbc5b5f4748f94677b65471bab574","proof":"92b4b10483534124104362087b46fe7180636aaac435e94cf348c5e4440f30438aa356084e48b15c36e59900af364c0a9210c71b7641c0dcbe5c0004820eb258d817ca06fb3190f3f2c23ce621c19d409b6b7388ca91eb6dc994db0e638b205c52a7720a5f2d25fad3b6faec7909e37cadf8d5519e564cadca6542eb40cc0d4fefaef46519c88c3817716276c2e2ae237d485163981a9d9072239ddff264e207526c1c582ba004eab92faeb85048af344fcd62e2e789d24b9164a5ca837d330ecfb3c3c4ef694ec4ddaa38c26b4ebb0ba83794768f1d514608242f064b1cba02ac086f977838100f23f2a6269bfc806941ce18c89b3efe2486cb4e4fe66c9f481e996f1611bde3ead644d9be2341e1f722bdb98b4841bc601d3cd188f8fec066d8c6032515b4b2cbb6c5cf4b40b73135626f9995eca85c144afc969abfbb78101ea39dac5293a57905b5c2a56808dcab04edd7f2ab8038b90b6a3707a0538d1f465a2b8f942a9d0452e2bbf394c51cf1f035cb3bdf51aead3d58927e1f252c687a8ccc2300d239937d88b758162b46f35f5dcafa14fa7534159362a3bac62e5e34c951d28eba880224b95732a52cc647a103ff7cf4eec6646d36568d325c1868a87ccc381dab95a729a591aa9678c25f862d2db9385e25a961656aaee1433e63a2b264de21908974bba45c73867eda646d9549eafcc5ddf2d4a342395c4ec866683e5dfc34ce526d2d58549fadbd43edd6121d17eba35b4849c0300fadffa0023227e2177c28d475afe7f7b23ca186673bfce07b309cfd3f931bc310074a830310d96bb28f6443b7fc4b03970bd1a9d3779d75e6c1f88e87091ec8d4b1fc6e7f90b781f5f4ce99f5433ab8ebae6e075211d2896c8e721687d59e072aa1dd6e07a77da4a98f8a8cdacf2643367f1cd0e9f2ffc551910c7a89ecf3b8d21ffc6f0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f890407c4b744ae2e9a1fe6c36f12270d1a081ec81069ad59d8bcead06090a13","proof":"0ed61dcfba0faea9223ecd733766432a85bebc1b06e4869347a29c4833e5db725266cd48d8d7fc5f195dee1e6c7066255fbbf63804a8c88f0914c857ace7671cfa807408d7c3a85e3218cfed7480cc11c3c1bf00906591e2ab1b4e9630eef045fe44d990f51e0b3663c0c85055498e3bf1f954cdb31bc6b1adcf396501493a0686e5c56430e1615e4dd7e73d697296af43d37d7a8a1061445150756d93642608a2ee72840e1a30bdef618c05fb596900205a0e55eed637c285b6fffe47eaa90f3e70877e183ec2bc4912a5281b7442425e80defb43c07b97bb86784209361e02bab8984ed6abf6e40c4fbcefbb6184cc7cf08829d4b8c363ded5819ffb853b57985b0489e20f54d77d61cbc8798cd02c7c93409ddac93ee32120f428aca97e196037ba797986f221865aef64dd0a8e31d586dd2f556f9e1db77d4a82479faa168e2810ed00aabe916f38d234279db7e72e02e9d61311abb6d3d331cec0e6352e101c6b87f40b3817c09a8b0cb1b38ea35893e9775ba6dcbc01aa42c47246921bd666961138e962efd2845a784e47e610f008ec0e12b6966e75f3f516cea5456c8880f0771b611ec620eb9209020816be55db0a5b3e11245819ddc744e79ad513f2e0ca8eb48a679ba9fab76aa24a1387c4d5027342422540902cacffd5285542e218cae951507c2eb68fcf73a368b14a0048dcc19ce1bf6bffabd7459264d3056afffb2b3a4fcae3b897b723871e3ec0aaac5d2755ca6d6ca65ccd73057993652edf3c874561e8786b4f862345210a3e2718f6a9db2afc8512904fb91002ac2256ae7f201bef1c285e558587c381e719432a6c504f5abf982f85322fe14ab41257c19ed765bada8df2e244b072acf1c523877523ae5d2cf76aa1c27fb508fb01ee870821ca318d203ca45295a8eef9ae4102114713ca68756a03f6ba8972d802"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f00be49f714cccafb40dd0fca135a253918ea9124fdf94ecaba767439517dc32","proof":"022a0e1927728309167a8a008f1f9eb3c22d19839ac32201421ed4e3be2ad830a471def4249b68f1ec1a84bc42a44e3ab7be754a7a65aad10216edeb6b0da07dd6daf48147a15dc22d8c605780aa2da8b540e1fbc6698c18533126981e008213d47a5d71f6a97affbe47429b1aac660fbce345dfb27bb8efd8e1ee3fb7e3271fae31b59e6baa3f5db4d05263faedaf13e65433d6c234fb17c4ec81c4a8696d07a1b75e321453a836f7fa1700ffc3fb52aea12e8ede5dabb8765795b21a899d0d6a6fecf3a579fd5541bdf263b8f876f84eb0a5edf454661865d434921017c80e2809741a4cb0eca4b3b42eb5131e09a1fd6df45a4d1d7a146eaf15ea6552f050f2ed7ecb851cf459478c9f2b2d70835964e5e05cab13818c7e9c5487493b5967f048f991cedda1b2ac9ba8d105bfe804bf4be416802fd040019c63dbb8e7857768e1fb27c7a5df12f30df5524a628ed451217eb7109f5727172a7790316c946540fea6510ed35e01837cd1c0d1c3e24d1266410e2bd9c2f9316189c68ed751032a950d0904112ecbe5f6de72298cc2627413934039c8e9ceb188d9f5f710ff49c4df3139cc0dceb84833d164f62736b11709ea9abd3cebae0fb85a2619293e58c28eda76d92c442b82cad763f7d8ec5ee0b9316f13e3eaadf0f724f21d536721221b29cf828bcbb3f548556deb948beaa9327e827fb1559a7efc78efe1378d550ea680ebf570d0e73083abdc903e26adb2c0e6100d98a386386737279802d97fded35bb8196f84eb635d16d705211ff7003b44cc54d39853918951ce9d72b055507e69723c5db2d6669f34d1fdda21f2b55809ddcfb02407275359d19c09e74f2a478999c179e928718dfbc57c94ab33262daba70738e6858f5df43794c0910cb823455b3b72e6f97a069355e77e090c8e0067bbcb324795e67b8e0383b53b0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2e0feb6562858c7a70470b68eaba575d3281c1bdeef4e5038389b31d6ae7e14a","proof":"7215ead2e42495fa2210a8b9f7d45ab99419fdbb8b9961266a67894ec0848f4ddc8155e7d761e75b83d37f1a24253da7ab90f91bdf9494caf568b87195d1b044c21ccc821ee0e1b32e583fa32d57b2df41e61249898dd5c37c95e2eb56f9f54bbc4834a739febc82694dac2822cfede1d784ed384477f24f90c885d783e3220feb21579a5ca2800216044c48235162eb9df54cca79e4858a422b1837442d0401e9f9b8f0c2af990ce76335815c9e5d86445314914aebec854ced8e3bb99b4b0457bc0890fce9b64e564b61c6158e45cc35b9099eacd534be002de1c1639f4a09aec02f72e23a5547408ada314b756e2486abc1f4484a40799b8883354c88b825b0e52300e22a126e46e635e1f009066b757373e73c7ac4adee438ece294ae224401881c8b305f378de1a51869c9054d23d414d1b538196f0d7af5649ddbb1045c4bfe4a24e9ec18e4703653ac760dcafc3f7e7f4803738e03259c6659da0c0082e9f29d10311fc3c17fcd948ff3ed8a9867d15ea08150dfa49e15639bffd2d448c4f9b857677ad4226baa06ce5836de09a1ae649331e931013b5a372d884ca03f6841c3d62d3c124d1a8c0d2a5b3e7299a646b474589433d29568f9d96edf245483903b6ed064fa7114c3eb5b9bcefad312522c6f3ea246aa90fecb4f546311c52c78896106ef60181288dc2ae6054b76f1594ce97a5af6ea38ae42f78c70e4474b4f99e17c59d2f3c2a18b1326bd07e9927ee379fa44bc7f7dcebce75e7a73a781534e93cb63481a63f48d90523f8130519b4c505025df83913af554f399a66586067e51c656428be268bae1371d3288922b41e7d956be4a44061ff5e6607334c6c5c04c1735ae1aa62a8d7db89dac6e7ef2848fde4bc2bf5e149abe6803f042ff29c35251ab88c0896e8d13f87832e742e6bd6610a40c1e9da0901fc07e707"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"96ab3fc03302f2564964e06e6320f1be71d752400995c634b3fafa47079d9564","proof":"927926767ede4003aba38508ade0c83d0e16fa46ac40dc2fec4b09791756be1cc2e9d31d3b1b847580d9e4d125aa34884aa0cd888c4aab72db233e4576d0ec7b78ef23025b2f39b31e2332f3858443b98fe3e2b6fd47d5d0c7fda5fb19ed4d0dfa97a44429960752ee7a1335eb48cb81ca40f7b2e61abde9d4b96eafd510a56fec9885e26a9baad1c7cd9f7070956c36a9e350e26543112fcf477765f8b9480304c3977cf8505d7fe09ca12b510c05996b281bbeffa2ae572824086ab21e7f086bce120c2aff2c01692102fb10797c5f92b370a93703ea0bad68efcc9fa7f90ef6f6648d5f82f72bc207bb0b5f7869b1e52e1601359bb4a2d8690d4feff8b82f0ecf0ba39a58515ada3c1e72b31fdf1dbee9bac35f36f46fad4c5653ed36ad1728c476feb4f0ddd272bdcd866265f6e87ff7ac0c0c3edd5126b6492515e373522ee21fcb2b79fc3fe530d4bcfbf58b20601ee192556dd412a098480475e70604a01c0b100bdcfed3a0daf1ca184ba2463f34fa5e7ff2853ef8f877e62a5ad61c1cee4f2541cbb3703bdc196df92e624172e9e7cc91a371b343b9fe6a4862f62986efff0abe92bbf2c5c1b12c869bc5a5c2ddd8b6d8df089b2cdf674367163b4d22586d66ac1280a44f895a6ffa696605d52da57b7a269d3340f682119f69cd5c1e611020657f1986d5b8e1f03c9202b30b5583357ec8d7be2bbacccb3ff9ab467a5c70730d315301d976671e54f409b4ff809e4fc6bea394b2102dff40a12f270206142ad1e3f75a14184a6cd80cac2ef0a30e2d8025d4492dd0b9e5c0d36f2f7ae6b80b71dd54c66dd9f88494563f154d77f4b0b61744cac34af02f3619b50f8db0b390e56c33aab474a087a6a02e8c8be39d03a4355c4f6ea128df81d4b206cf74f76c7bdf6ffafadfb8b9c131f2c097fe6f84611c42121e3d8531b97f8402"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"821a9c2de34d05da809f31ff936c2d8578686c81eb5db5e8a8013af81836d859","proof":"1088498a68f9bc369b1609f391e52230d9b0c37338d8d6413683eaff49aa3b764e409441add52fb38f7f178ca115069f65c8e3ff9c05101348938e46dfe94e344cbff62704be18de272f2648b6605522469f554fa5c9024da2a1b7fc3041bf5c5c8c1e78bfc567156f0a3bc319ba4f69806838e07c51b8d79dcbc5541516b418c014f960c2881944608eb21614257dee22170530c7a89301b277a82b62d692018884e49fb9d294a8730331e29fdb248dce1d9e2a8fa6895d2a8ff920e8c8f20ed0688ecb904f3120f38bb9047bce4fb32022f13d68e0fbe9ecc9d70b3d1c640fb69ea0ce820bc7241f52d1046364302055036ae76d8acc933c06a370a41d914b90a6194a1796ab47703c95f7aff93718950bbe3756dd7aac100af59405cfea420c03839da9bacddba12d7f92afe73fba959676da0c464959c90cc1136e7ec15c06bcc8ce719e16f9786836eb7333e870dbadd163eb71f93061c0e156a27ca14a7a7746f78d0f4f5c48f5c356b62af75583a33fb7fe42c80a9cfbab32cf44ca18ee26bb7a8a11364d9ea26f0b11e79d258bfb7981e7c20dcc94385b7ec941815bfabfa53c5a723703a4ec1eacd32e8452a26964b7c0f484faf1b1b436d1ca3d01bc65fed48bb946b0b9ba9de5fdab95d5a54c04ea8d6bcdbe4e0e56f52e4d03237e5f2cb0a5d2c611bb927c2b2d9eb0aadb4f2b5b920d09ccf2af0eeaac7e4a1860086da2c8cb97c5df53406bec1ad3d9901c6fd221fdd984cead74360f1bb94cf8c5695dbbb224a7908e1316ff22aec8324db9866d6c4bdeae9cfd9ce395bb67e4834924a2210955b173f32e791a6276188b7c3e780d70a00b957a1f7b9a76284d1f861de68b49c7f51f241fc9ea2a4936915818f4378d4be24369c6e52aa506d351c47441334628930b70e62813b5bd14850b6034f75065a488ba5b8cc66f03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bc13e687bf4cca22eac6a0d770f1317f693b9690d073c1374c38094c2bf9bd17","proof":"7815642bda163279bf10ab1007d1e56837779990c5c42aea916b1976ddd9f06a4cbfd55d8f271b64a457886751e50f1e79fbeb6eaab5a1d0d584aa56978609355856ddcf33197602fb6942c510f0ea6a6a9cfc0a578ab9785afc249bdf878816f43f054c46bdaca648c0e164559dd3d31119a7f71d1f9b3fa21ca4d805a69c3a98bad138162357061c328d03e0f6043a2a35aeff4017be8c7368fb41b780f80d2eb41f8c7a6d16678baaac5c235c1870d6e3cb5aa5677d3a7b454f3f30cc660b7abc0c3761aa5529b6a32080715f7695a34675eed09b4f1598d598405c003103ea310d2165122dfda483423f44c2a9b0d762db291033fac81e0bf5975b373658cc2aa0f1d175337386bc7ded5ad83732fa6f38b36cac1d732e5fc60c9754f23fa636ac60d8ef9fb2dfb5338f458c286d7f980904155c46c02904407148f7c1580ed3e478a52cf52e0089de9a245c2179426f70d99971a99f90629c8e5e32c206f457d34a618189a15c5c4ff79b7413843154b7682ac704ba428eb87475eedc592811a8332875ead9f2a2d512914d12833c5c21a4270d43d8382318543cab6e5638d6fa03f268cd4679cbf7e1f62ff5520f6337eeed404f6ae58753bf43fdbf4976173aaf05b1c03bc1e7b27913dca198678dfbf2e1c2d1e5fc04d53191461e34acbd27d5e27bb78ce9e5eaebff679ebee047392de84572c0651138a44680b343a8c607ed4bd7792e55f2a50eaddcf592e59265091e3c854e418be5566d161b5a4c817cf5374efa851158b4712688efc8cfb1bbdaca13fc46b3b884670d8d1462b8d881eea2023c873b491cc99b00c1389ae887e705cbf332939a8ddec256d82f08718d2674072b7742644c2c159146f3fd881395174a45240a718fd31849630846190bc1577629a9c31d6cfa926f0c70ef9ea489aa6b3c8e88c9d5d7763ef104"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"de1e772fb9ba5191614230303b1faaf8f32cb543dbb8febef4c298128708bb4d","proof":"c291a09238df78c34cb584cb3001b77fd795f67e60585b46e0e15a8ba9d4442122f8502c5b09de9d3e9c2031a1d43959f296c6947afc8cc77242c0d4c6293b207c750d2044eb2d022df41b18cb976400c4a42dbf06b59f954b30a3f99be9451226c0363d5617ff677b3f1a19055b2996e613e471cad15e890e2db54aa2df0e5c3d49aa8d0601b33e96e8ec23c8e566543d4957ca0aa0561e58f6fb2e3533340fd58825ad1ae409784c953a7d7cf4c85f9a037f2937aa9075553b48849689020a4898281f82912ad66aab39a683189bebce440b7f8d824901501a3762b39c4c064cdd32e4fcc87178b587a8a591bae68a490f0aba7fccf1c230432a879fd508380e16f58f374ec93ba0d70e11fed9a369a1a259e1736c8959a6902cb3f38a741952bdd879a03e0de6ef49b93a71c7abc641a9e45b4b711b6e2c80363aab0d54426cf8dd5abced335b9ab29d63c3d3a040c7da1f8e4faa591242c5c8bd6d0db53e98d4a2e4133ae8d56567b125ffaf046c80ed71487f011d17aacb2e65727c7f7626ef6895dd037d3f4f78312ab6e565218db67c34346469b0245def3a3a82896846289572251c9a5f8d4eca2b230b0762351037232f62316d339d6df822d2941c383e78981d9ccf15351db21371ae9b7e4d2950a0f07d4fc824f8e7848da0d00898650a7d3560cadd1dd8fcdf030ed0df97ff19b57de77393d3a1eff29c5b4f61ee6420670a4e08f625c400eb1922a2f780e20472db2e431e05dcb102782b1f68dac83c00f018f055e9f2823a0cc8eea0255e4d8f443d9f1313fbf06c6fe183170cdc9ae2056a8752eb570ca75cf35d9d0369491ee8116755341f96fc5e16781d9cb57c7bd70e0191f7bf58fd58ad0ffd49b0cfa88cdf303a3dfa2b1cd438a0080c203fac7cfb8b13295f6f68b62f3b8f3e511fa244f59c25b8630b5182ebf40b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0cce314a9c9406ffa6c7bd06731dcab7ea087e66464cbb7244f6327d55be1e2b","proof":"306b779ca64f98e8e37a80c0547f8a15dca457064ee165a3b698bc8796faff162c37670ca686ff29166c58c0aece2a264fd96e6aec62d447141d9eacf7560521c8f9e9dfa04b4a8f18e58dcad42723e7210f78798f81d92eb8331e48136bbe06124a8e37d9406b9ad7dbc9b4afa854aaea1846985e5ad251093cac6b09985b7c39a933ab5f5d1e19a0441af9f7a771e6d23b308914b89276088956978843070668c7b184e37ae179bd16c382dcf2d6e2d88913d124153f65e289f44b5b179a0653cc3f1c8332b6250fbb9d4f35547766f35501f266f19263af24635662a50b0490c3fdb22fc77a2ae1de5c74301a350b432d861c750cad0b40b4164430f77c0518cdc00e748d9d375da7cdd02113e2ac771bf134e819353ca95153a1e463de6b2a870ccb122c1e6e6a9110fff6de4b3385ba155c166e72f7b458f028fbc33c165a10087bcc832a1728cf1261175578f7822486ae58272114dd8c4f8c96564c7102ec017009e563970aa62f6748f2b96eaa74d3c17da3383ed443c3117bcf2a498c1df55d7b6bc70b73a02b0f211885c12751f8a1c0e41f0cf1e6a336bef2aa1952ece1d046052338c906b37b6b44d4f28fa4a0c0b7cb94ce48cb1a565e316b3306eaefc05e68b269d7f7a4b50d149681b7229112aab492a44353e3d282f07e02c23b476ab9bd36a52b8e5bab57cb7df126ee4ef89033707a2de6bc1db76dcd517afdf674103b8dc6494a6cd4ce99adab609cccb510976427a9e61ee0a14d632cdce2c7ee034185ca34f2db6b5c6d5ad6ad94ece5ca3593a43d28c59f20138b6008869f0e4e142193f024ec244fe31d2c062303d635bb39887201ca7db6ca73367f1d28f79ff46e0ddb41397a9bef53b106119f44107720928a558bd125f8d90961b7bb1ce8b5215e0285160cf0d4485870a945f82f6e0982fdd123b300ef370d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3cf436e445e9738833b3f273e870d318ecba4f8199223d57e28361d9d0e97669","proof":"06511c6e7f02feb34ec1abc57eb70ca008881eae7d15dc3069c8d9939fddc4315a6623f94e6c6936b1ef6fe4f17113182fa6f8b709198f177b30b6777806fd4c2cedd35f49fbe2073376b3ca9734dad43872ffc9542cab2aafd648abf1fdfb00faf9008b96532ac4c0a07ec7e76a769095e57504d7101372a9b1eec696db362084883d26411691ff0278c8071394d03beb0d8ac37d30df3336b9a5374cb191086dfd7e14415068abdcf7c375eae9f75b1d6940843cb1aeba22c71596c8ef9204559cbe9d7a7c6a2e1037ff1132d0c05a38ce5e83c966abf49d8b78287152e906b465012b518bca940ec4c8918d8e98743c7e6ac1fc3c4c5f36f59fc0a2d2bd068815f534b534aca517f91b98a6d6fee3459552a068a5d5fd1b74c8f18daf275c20ee598a70b0d8b48a6a7257eb637f6c144f36d4a53b7fb84d46352e1f88be0a9ef9edd6dafa5e780d746e8485b8f056e716c41204a82055629692ba1a49282cb8f1a2265a3a66456fc7cd5eb14e2817b8502585edc5cf3b0b785ad8d1fc8d058c8e0f99ab8f06c0c78f3596a48b11948b4e976830a08006f48e0d9736a461168a904f7010b977eb5ff0a7ae7f916e53b817e0079cf579d8baeeb48988542324ba78a3b1662bbe9b8398a5265587f2cff95d1fd0c74ce12abd653a3fd514ca1d56ba5ce2454e3f4d94ee01a224b72e23045ed09cd3ebe1101cf3616b241c6475ceaf01f2fa15821cf99336e10b944359bda88890a993e2bceb94efd00a78805552c9636e3fdd53c728db35fc2802e20c9790846c798d9fabd7476569a3445a48e22568c30ce9da7d83e13fb22d6461f6e4ad0a0978a7eac22d0557f6b488822a994e27a52c116823dbe192ba2e29be61320fe4e8340a657d0a60c28a87134e007198e82d970dcbe21878a7e280def52414ec89d0f52c10f8247788c12942d400"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e287e7c9b9de583de14f34493b36440218546c2fc30d5232e098e6da4d693f30","proof":"58ad8e85dc3ef8df422bbf37e3e9cedbc05c98df7f0c41515e60e4786f3dd4619e9f18db94b6c21ca8aca69455aa8a80a6774621d05bc08c6d8afd31f841cd5030916a70bb34f6cd4ee36ec666be050b2172f8cf43ecda0cad616a61da29ed37a0c4b4e3080d8491dfe6a76b2b596fdb5632a60e8cef86f38fe0095986e89e302625f2460c41c9f64e91eb1be5160857af39d65c6d93a3972eda0533f53dda03572da0a43021ea03f7c33bb8c7409fa16188768f9844602b150e9ed555cadb067fdff106100aeba235f337fc0367396172565777cfb76df2ae89930063ca520b76bc2ef5f6b552e6dcd167da2aecd8aa76f8986a46655e83ae076e6b3f67b011d43df8a7888332d31ac4695b62a592ecd730cef9b931283d678a0fdc080ebc23925a9c21f7115a3d595553ba533d0af982c327a6abf61f1c273119e1c77bd0055049c72341e2ace5e7a667c1a4ad9106cf7fa512c3c094cd5b4d69728d22dc222caceda349281b0e4830cf894249e542090a51b99ffdb4791364d7c9e2fb0063f29e2c0a85e014956cc08749283ae2c6aa3f2188786bb360bf17bed905040e41f22c6c3fe2756a3d1dcfcfb68198df11d12ee470b215f45c3860c7b4cb1bf279cad1ba3e1dd66e510aa05a6a687b975097d350cbea0a84e14851c7177a3d016ba40d866399a946d60105bb311293f5d5d535d2810b2796981c170cbbdf807e47e8a8369ad611304dd91966340f1844c2db67a8b361c28965962de8a2b527b00c16296fc88cf407373a3661b6cbe969bfec3e9eca2f105aa0dc38a759e157be42da6b4b8b07b2de17b93c451e0c4f5fa3edad7cb54bfed52609f0cbbd462ff844b684dc65b9a9981a685090f2df4839fb8e41cd3c50b177e72b565d7c4f9dc40d17638b70f026b15beb4aa304c04feabcb31e4ce5565a773811e45f285edfe901"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d6c00b8bfd709d79cdd12a0ebc8b4f28bff1d288a797f33ebb35612758291e3f","proof":"e8c0a057551ea924829ab1918115379bcfce66e824133ed93b31e6bc8a26f22d2807cd906c3395003d368db16650e55c9062be817054c0a1411b6d5af177e23112723a1429767c95bb6a875cba6bf74c49e8087b84eddd70d1a2978908fae34ca40e8f99240f75e5c6d0a8e31ef2c823b31ab28a5b88b218196deaaf08e84018c1526afa0f218f40a701599f7a7c71170d56439e521b34f020c1ed205d0dac0e04602d49538b8141024cf2a2a2c9a49592d448708e9f761dfa4701b9981dc50815f7faa8bfc4d13957aae35e2212a1c0c8bcb75f9c0402b50c24eaeca1f4f20c289b59b4fc6abc49b214395aa535de5eca406811fde239336f49c8f037c9d312d05c0ead0a4234b0244679cf2b77db65d6734b6704b2928ed03f0760d56f9b3c4cd2ff782b93b5c0966ec04ae6c0923c4e142023ef8f0aad767081f59f9c445af41c37e467d179b4b008dc846105d4056fa9cc631b46181a3204cb5cc545a0620e79cb75e5c8731e0887349bc6048d8364e8ef2221aeb3b743e562edd100de58c89d7c3b5eb2d56f94760099d8c9057e99e3524789891f1ab0c0207baef79e4f0275901c067c9dda592a45a8148d127dccfc9d7c838878526fa19f39e6d00f17882c6f50ea555638df3e08d8a3fccad4e9518a95321f6b9bdb69ebd14a444c6cc21c461fba3b44dd8ed33f560a008bbe076220fb1f667c87a7060ad9b6df0865cafb5c4ad5d7f60d6b0bbcc7701bd6092ee7bcb80b9c66ca2a9bc7e8b328ef33b49944fab98c68280309ff88eeca39d099749ee7f178d897b9953823034dbe363c454691e117d63b9460781e2147ed89550eb836df822bbb0226097f1b427270cb2f0e72365dbee148ba42e5c63c91fbb145d5b520743f41f9e805e829d5c101eeebc993606018524ab8c6ec4552ccda589775802daf23a64f37240dcff5cb00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6484ad5d61dfb8a23139a1fc00ca30622b5e8317a6fd8dacb86ec7b3245bf444","proof":"0a18d9c04a9d7a1f1548e44f20a6c777b497b6660a91ac162cae5a0af0697127aae257023f1657f96bae8e3cdfa1d1809aa19e12d9f4115f1b6668a013cca46e10a0eea8f121ecf56960bad79490955102f26c28d32af65f35ecfa56e2b8680e52d04fe3fd4c6a6714f1137a4068680339520e84068e8a6e9397a92e5ba50d3e588428ac9943f1117f346cad57d28a6d334ab6952819e000cda42ba028226f0147371a8a66419d381448f3706f8ac74e534a9ea7e31699062e6c9e5f6605d101bcd63d0b80c1c4ee91a2c8f242efed4b88e85f6f16bd871727820b40155dc20b9e560ac6cfdcfbe4f2d05f211ea1d555bda084cb48ea19a0117d11f4a13e663f0285fb9c4d7bfeaddd5d5920d909294e67cb2b26b1ddeacd27e7fc7fb38ff40f5a127d5fc5e1105e821f48831ceac7c52eb39d7d2ee7b80e2be10257df5ad01c3e18f6d3f262911dc6a4d96f8de5640e38f19b4c72a7018a54fa6c8b1adac271e0cfaeceef87268273997c09d002f1cff0a3448da7b5efd4aa257786aa4d3e3bc864f3e67b974074a0eb64a0e6e2597123a292306b0b1dd14faba304986c750ec08731c928d0b77d5948f20cb9083f74c0d052ebba257b2b460971f70ce2971f1c8c7555203c0d80e22359861aae2c850f1b2ec63e6e155566c4eec21ac5547c469dd3893781c7a65fd3f6a8734b305a11bd2e5e2fa9d371c69c3a502bade653320e9f0189dd277f7eaf8d8be401b18b6a8fd0e9767cbe45db3b7ff0c055637ed02bd3d4bd0ffe9a06e0f8cfb836de959565535946ffb4e95aaa3fb3eabf293b2aa858b4c453cbf7e2f237a43512db4be7d60ba11ea2aa3c393ee7ca89c10967b935336c22629d0a064ce1eae921f3ee50d1dcf7cb974a6e1763fe4ed55c9c0085d65eab4b3e6befb564dbc0829c8d36801436db5804564c30f6b98845e5ce01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9ebdecc1c77b8ddf863d3a4d10489dba7c071584c6cf77a867f021ba55d3685f","proof":"4c98d31a7a910a8d90cbf8f392a76685dbc414aa9db939ce7552798258ce6879c23f132ae22dd9d15ccf8d053497819edfc8c724b5b7eb64b57817ead97a9c16f81b60591d0eb827af3cc1c7e2a4e5dddb11f0750a6009d410caa6efd1825200a29c05e4e18d39f5d635e17a18e4d2a428fe9bed06f73e2250a2a7ded9d4245e9c1e3713c50e6084ce16a4fc7ac0acd3449f153c2f1659c35033256cc9a73303556f76bbf4bf1970d99864a1e25386fe988de590967ad658d632bb33e30ed2075b59bbbad81eb5e3ced08161879d6e69af8633a36fef12acbde1f36caa245406da85a74eb6b4e88f1e7d72d2c0439040b380757f62dfdcb3a383c0e76991ae7756591ef51f741cfcfdabccfd630c5886bb4710ee190add5072cabd9148e0f14f984e09395744fddf0d7166878ce7449a9332304bfe17c85ba4d25cddde75e262402c25b53d8a52bf743fbbcd321963f521ededf86b2d1eb9bf9da0f2df49e5254617e7ad0871487e65beceaa37b6e4d5dde53505c4f21ab29d238b70aa86883d5e89b4e0689eee547944d5465249d4cef531c13336c38d9bd1e057368e48b70a687ba54c2f8a1a6c3161e35d92d51d8034e4d9d3cecfe8cf6c3682367f31147726076c78959a2d91403af385f82ac48b983d583cbd502d0ef26b3e7fd88e497ae2cd70d57aa34c27f28faa93a771546f29f9729f8e503589c504c4ba9c31b33a50c10b5034f09cb572247a95bb43018ee176af80845c7ab5b37d22b8e634025f60052450db6e96fc805a26f3c4930fde391282873a59fa4f21c619a04079dd12948bc2d43d8f3e4f17e50cb0af202ef9294ed27376b51ecfc5e45699f976fc06b5b2ff4ef6e84bb4b685b6db09f218d21f961c07ebf1b3fabbd25f761bec860b0c9df7388294c5d2180e5bc4795729f32db3afbf1f7b81a15c6345884773dd01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4ccbbd614d0d48b466176b4bda62c0ca10ea64fb3fa098dc66f4476aadb2bc6f","proof":"c0014545199fb2b97b4c531447f3501c075cba3d794a5c85ed1b0252364ce21e802c23b95ebfc95417b832c63f3a021cf033d968a9133f8ad949e0c2fab39231b43296692520c986768fcfcf87317dc986616894d2cd29a06ebe5385c0315f3102789b40c4f96a521fe633d052505d8d2d39c8a636cd1583d3ff1c0519178a4cb2020e52a3dcb647300e7a318da1419c65e7b6739a1f2f8d49a29d48dfa6aa0756dd979ac93a81553fd1eaa82e942338bfba39bf46a167a5f2d7864201019e0a396355c635b0eecb7efbde754d47b3f1b2da7e12f3f574aef866336f90435e05dcc9c312a206a83bb45c8dc82c7336913a548d7d8e3aada68ad6f06e7d07416f7470d8486fbfce60efe48d339af785cd12391785c88b97b5a90f288e8635037bd4f6179864f55ff67854d0e1a5bd91c941140239a47ae568ff7a4006f3770a4400dcf28f61347d693b2479e0d58070b5af29347d0e9289c3b468e13c4bedf5040a3ff5c248c8243db0c3f65dec248e04f0ec87db3916caa8b297a5b637bce729ec7fc7a39a632ce6cb6b5073eeac24ed8dc4ee91b461e1a57668a7e6c885730578b3438c7a25ad2213d9d1b3d4cb469fa944994bb9273722f6094227c0f47e584ce26d501753e9a08ee22e7549c806b02d38d6cef324ba092e6a8dda3cecb869e492ca01048ce1714f92a23df0f7c5b4a6c7a594495940895e17f99a98d25632d09d94f391de4ac7b6f16ebcbc6a76a4fb9b54915383af8ff4cb482599ade16560410206c037950a8f27684f468326fdf6fd141a2f722f508bc570c0bdbddf4c5e6d124e0381ba725ead9a3ab62c853d2f56b9bca6fc55d0f50d7f0e69a5a32594f6807975eeb29fdc494f977d002d94d2c578e969ae3fcd7393a1727281e80940da406b68fbbb657487e326dfa0456380d550f838a3021fd59507b4d8c94605"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"52fcc3f941a51435f958d5b412381ff4bfb0dbc282d0c3ce76365ef99e8ade23","proof":"5e4a6e0e1f039283399670a893573ba8d6d14cc97a1a14c426e6ae7b46454632666ed340c472169c48d2966f41d4b12cbe2a694ac0240fd5049231bbeaf5b109fe7090c3b3b38544455c188eff672321185ab0019f87a61003c49c899390785bb4e20ca54348354bf73b80f666c9578e01ab362ad0cfaa0974ab05a3b46aff7e4c14de52c95918731778cee5cffd87e22e7044efa244cf63125354307e4bcf0b47018b851e51cb973ea9dbe2404cf535466c7e67ef795953b1fcc6a3edd43407ace18c3dc628244ed251019d31c6643b585b2f4c0208d2861c471c0ade7b210e3a93e46d925f4e70d145c8e4fefa9ff1e6f0e75c2442b4a6275de625bb6ed01b50ac0ec8081bc403d1ce500eaf90be642ff0f4741b7f2f0bb9b007f312771a1902710c9cda25d5ba84d6d93879ecaf964bcdf14730d20763842088a676e3295108a728093ae12e14d768e23879df16febcf85ab7e5ae6e431678f6734db00a1eb2382cfab62fbd36462dc58b345ea778edf6646f49ff4f25a92e2998151eda00b85b77474a5ea9a81ef2910a72ecb9616ba80016e72f5511ce2ee6e3eee56242664d4a50e4f4dea6fb6be611c31c8452fddfa7915d95f830821f3c234de2e16d984ad7d76b1788db821126467f4f5297c71ccf0ed26e951013735375b9d2cf76b48b082aa81438301588bc7117065e96e9125466466ecc96757987579e3d201814a1efba261e8473e099fe3d232bd068c2d3469af6db15568ced15a4c96e625848d1f7a46ccda08629d39122ccfeb08bd3fce4415c96ab7a3c2ca50ec0000973a4f66ed414976530ddbe6d59a33537f80a42004b15072742f0324a5508f539267f80c38b52e9bea103ad1ae3e40aed0cd44189971a08bea8abbebb6691b44804ab2e6ec7481d9396569d1f6cde56fadcffe5233c7806f96ef7cb648cada9d002"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"82c4369bdfca58580e4b2fa18acf62b995b940a419932817e9e54a7bbe031f37","proof":"704e8b0d25d023f412c487b644ca691582faf1bdc10072f68ac6a0232f20c66cd04de335c5134fe0fffe2d48ee74613007da0616cb72d1d0a61a79229c5375177aa2e455899b94116b9ce27103155ce30816ab03679c20efe52b0b88a14438542eb7067f2758806d49009e9ce95f654d0916f3fbcfe748dfd605fb7f4c477952326ae2dc289396106cb0f06a798ce900337d20376978d4eaef3b9b9583504c00feb33cacf6e7c1214d762b64da670f95a244106fce5ed1102d0914e814f5c607fa58f314f66abba3762a2b98e5ebb2e510ad1615ab60bc8358f08f6afe621a09be48ca96938c4b182e4332fa88eaad952bd6d01c3d412e15f71e9f53457e837afc252f724ed4f554ed0ba853a3836d9a0ef93827414ecbed386d1fda1660ab1d02c2df2587b32d4a1b9c7e804fb7e1f16728e15cf01498f600e8ffde51a5d00fd8060030266943c69be124dce187821b8743a516cc18eeda63dabd0fd8142f6fe248f6456f27d714908d93db9cc003a2e6831334831a30ee07b253d2640b88638e6ef2bf917d40b8513a7508701acf5da1e67ccc7eb91ce6ce04fd5e18cfeb4c06e689ac806cc71719b799a52f1819b31d962f366a48e8a3c5f058ed895c260e16bd72866c03bbb24b9bcc4a5de3e05173c2166b93db36929c1c1e9b4f06d117ce3e3a043715e23510658842d3f3748e11829f44cabbd6dc58108a47b80ad764d2cf706a4c7880206333ec61242e1aa63b45a71abe07bb7d35459544c5c16f1746f2c3f23e4a5d1c0e389883cffd7d39d79f1b7d42bbd6aa30390d6f74857452f293c304ff43df47f45b41e890dc97bb1547715270ef09fe3dfa46f250c59346283dac849ac74ac7d03d4f939a0486a9b5adbe7c032dc38b8a965fc9639e2206329298d9ab5e75b7232dc302d5d76809abf8f4b8efd51cdfd5202108922cc102"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"62d52504e5e1558589601247b201448eaf3a88a77715b1116dc0b1f15671bb52","proof":"2843aeb322671cb8cbc478391ce3adde13f8c3cf1acfb1db13eaa5228e6f794ec873c54a82cf2e194c6f851bfdc0532837c35547c369ba10d1eeb46391b0a349e628f34a8a7575e70f93716aad168e030e177af40a2458565f28beaee70d6d296e210734c4df23834e75aab3d6cb4fd55a4d1fa520f56ede06ea8ad2a8bd107633e4ace1f856ae8214cf8e4655ba0775aaedf09ea4b9e2464d969a7b9a9108005654bd760127ad72aad69e10c1fed4c556fd5aad828d5074fcf49d100286c80c725b57718d71d0dbfd97aa8b1115e10029af76d361f0bbf28597c7960e2a88011e8ace2f234d1f3ce7ba6f777d50168b7f96953249a6e73c5d9b839dba95727332b54766a3bd6f1940667f9b2900dd799fbcc0bbe1474bf73d352747a6fb963738548b9dcc58564efb56237b6db4455583e29cbe4c0d2dbf0e6d177788d2211384dc8956081173a245b0bbd8e138d1cd96439a216261b18864f108cfceafef3a3cd62a3e3b0f60fdb1136b50e4a05bcb34dd7ef560880891a86f4b8497caeb05bcdea2f199aa4727f4f2e10e418e4853c7dd7911ee5a496a7191d49df8e5972d44aa97c8d3ce6ace270bbd40b2afcac7dbb5f130ca3b5a665ac503bbda575520f6e8c01f9123943c6a934be9838e3243d47f1400c637d6bd6f1decfd3a50591bc40272fbd17fa05ef2b05caff128118350eb46c3b7389d5df4ce6e05e3228d0bae0fc6402c45a552ca64d0ef6a7983b81d00ac4a3d24352a2eb2936fec89fc7346e887d802b65c550d7f6f08d121eb98d82d28fa6cf2a71483539917fbcde86fba047c08ab7aec98d174a0a2975fe4fe517b1ce41b99f1395f70138b24a1dd115c86dfe226b917dd43c3665a0f23dbaf5c2f9f53a8c5ad02e292fb471b5c31000fe79f4f40f12963f14bd46555cad43f1f1d8c9f0be6b37445c811da87ba9901"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b89428863d2b4d0a68521be4bdfb6a142ff2573f918d4069ed97bb0e791ba33f","proof":"40dc5e3659f043f5c6485c26b8237327063b946c356f19aa3247aad35357a30340b261fb3efc31ee1eeaf195e9d4d72b9282ad5221dd7edf4c54a517c9f1a112e855ade53be885c3bc9f74650a2980498568df790d7dbb1ad5c837b63542456b1665ef3670b3aa3090cf0cc585e718d160abbdcd273d5070374cfd13006a445e4fed2c39788ef0d26ffa0bfb3c36fdaf4c08bc2e6a8d2e56f82eb12b872d2809f903a1454103195300b2f7d8acd1ae37fca38681926671eef0d352ae2536e20c1187e3f15c621f6f955e2de10720b56c2be92f788f63822ce71c3ee8d641170962f8d08dde0b1e1bf0d186e4bccd9c68a83aff400d0f95f4d4f5289b7842ff347e0286fbfdba3b17ffd289623ff3b1c49bbc3353fe14c4a79ba1a477734b1210b284fddf5d1eb55dc0c4581dfde8e08710a87e2a5529edc57d6eeea376be2d0854efb7f303fc61c8b72ccf682c218ee05ba62136d8caa945e53c54138f347958828eb31372ba3df49b76d426480969bad1aa6f3cf9460c3cc0bc5b3f5e45a3528c7f9e997a5e61ee695cad482fc8abf86d4813361c63410a495884dce675943e8e419d36b815277cf86924fa30616cd53373fc5ad8d40f4cab7a4d6cb7130536c66406c1a5b0346e0c8e285c6918bbaca7b762fc0067c264cfdbcabedcf74b76decaec0aed21d1dbafed96e8c98fed11666835b21e95629be32b32f8c180a533c885d54d84f0609685245e95308b660770e39d7d078e0ab6966099a6bd86d647eef0365d0f33c8ad4a3a7b199201ce9c516c6ecd37a41c3e98d527dccd1209397e664e0bb28728d40e15a901c15382db6d2749bbfbf4e1c2f648199f6dfbb3485613786c4d5e1a505cf5ea862e3618d1ceaeb54989806d897b93102c899d8a038839df86a854aaef837c2167676d544e8040463f59c600a6ee3ce9f842f6c303"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3e8633aa7f41fe90d6bb08b1337ff161e51c3baa9428a164dd4db971ecd0e070","proof":"34f8ea0ee7788b24b22c479199043b5f1dee4e99d76023bc4ce6d1511bdde816a4a52300c9bda5756155a9fcd87c0fa7caedb8faf21717cdb5350231eafa4f603a14f9aab87d46413f27f6ecd25c4a300ae0479a2e8fae0a0a4e445a518be773706346fa6dcf8e9c994918d15cbb776b13570427f09984410a6eae2efa8b7a550ef45dc3ad0073c8bf455152377ce825e32f82c00f92680ae9cce8857848ad0413be8214c8f6d3c782a2eb599ddfcda4d69526e0316b675c278e1973649b3403a27470d1b704a4adb56b59ab2bcc14beb7ba6217c06d771bcae26a76f4d753034427231ba5a05cafea82984934cf3c02d77cd4191ee9566d31d7219b0c6fc3571e51184e35d91fbdad0cbfc8f143616de886880e53aad5c30645562e1f69e645529dfc91b06e991b881c6ae87f66efec1e8858722d4c21c69b2d6c9fd558e5718a540ea6f7ade8bcde458023670675941ee0a7fbd1731aaa2cdcf8bc638ceb23b08ba386d0baa3d4a00fa88629dc24d319574e577996ff143f55079b0770f14a5a5c3affe93ddb316ebd88527e2ac4faceb3e6fc394fbb4ce72ce41b3b29d10de6dc370dc19f98027fadf84f270229be4600b300aa7607438519f85ca483935eae6a0768cbbcf5b681bcce9eafa47fc522184e357e34d5fa2b514acb9eba3d3b9a6c1c78ad1ce04bc77877509c83d1322e119f13ca2193aea17659ad03bb1255daae9dab21ebac70a642e6489fa624428bee6ce2924cb5c8674be0196eda12429e7eff4536c0e6e5a68a3711b597fdd435c5c5582153d021ac44a013822fc00a0c83bbdf5fc64fe4db83071d0c3c64158521bd3994fdad3877db4c73104bde018a3a4ece7b7827c31bf022a32a6dbe8fe7760010e1853928383553bf0210c80228ecf6a852ec001e99194b9751f6d25ed15630922bb5560a1975ee78ff2fd908"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1cfda20f4aadfecd6e1a7336352f6c384a4fb66aa5d6ca75460227c42b09e541","proof":"ca456bbe81fca2e69915fa972f19eb419e9e590b422b80405ceb6e1cba22760af6320e3cbf739a07ef8100e960865569e0049aebbde65363bf7714b9c7c3562a9001660e5318208f46445605ad345c73432f66e089032156561804c112bea548a09c5e5caa469e3b2b5d62016473bb21e09d8b75cab4ae93c94a33ee2fff0743d61baa3d0e0e5cdc09e2c8c8248787c81b6332bc65eddb986dd67ef3e22eb0070618e6a887dd6455228506d51469ac8fe5725dd636e8ea6128c6463d97c6570e6be4c03e410eba89f8bcce93acd0bd3c0cdcac9b8f389ce51d04da5a15263d082c09858cc48ffca4395ce8dd9cc126c91a8ae9df6cdc5150ef3873f46d877d389aaa8fa1bae3dec5136ee9742ec535fe80f6267a65eeff491aeef84ad8204224dead1ac3e878809627ca8912889051938cc289aa51f35e3bcadf2b20bca59708263fbeb8691053d9c2452929838e2fc86618347c5beb112b49f1ccad8c3b7a7ee089a3dcad33034a4ada9d19a9781505f32c59d0c56d7de2deeacb9baac6a56fc4bb61711f45080acf419e8ca37503925175bb4f7b72f58d9ed6c6cd35eb05653c7c20ed11a6001e6fbba657ae8a5f8db4e7741516def185e927a30a56672d6128254bf358bf9205d1c066ac9d5119dde93a649d89de3fd9e7dc5bbeb530690742b65f32f04326b4999da413895d836efa6926467799e0c3023db870badf2f269e27c9eb93ab7318690b69af4e16d61bf021249ed7eb5b614b5d3645f21f4f1f8e534346aa113a44f30544439db0452dd5ec43712ad451c2fad193fa3c7e4a4a5c0eb9e45d9b3581c8b58a307c71749ebb36c2c7ce0ae87e8af675b171e63a294a123c5cda66ec5ae225e75944dd9e6aab6ce49017d1d5c426cf9694dc57900e6523e6108114cdaf2c846be89bbaee2de7871405b54fc6e9144ddda340556e00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d88539432758f4762e59765fa5eaaf65007c0454231b76e6077b45648dc53278","proof":"2cf9507e230ecef61a54d7b08e44e7893ef88098fb5bcb2157b0bc4638c73b7f54cef464636fc889e7e1148d4c9ee63f2bd336e4ae0f6b5f90d80cfb2893d635c28a65b098802499313d0b91bddf71a7cf1cd986e20f49938d75a12eac81a06e24e6e32163f585f904adabea482fd1f1813da6ed4f4180ebf37e51ff543a3a7a9c68fe03716a1d7b220f974df7894818af8c6741d51aff51c44537665c423c0702e5f9ffb2324e29fe45bb835c32b77d823225d3ae2d0ed68acbcbb61df8f704904811cc362781aba05d61320c75446ccbe5360741f9744305a2125f7b148a0b36039715d917f032c9f6597b0ca03b650de1e829f8bf0f5c8c9e5a603a815602c4cf392786e385aef7670e6661469cccd2ca9473e14edf9638a5e745baa9e9389c9348a291afb83c1e692a487f420ccbf8cfd64b303622614f5c59519d4a8c4150919a3f5a686a5aee8979aa142c456373d851151847ba0bf66b13c0ff2fcd2ca40a1d2ac9df3aacfa2f64a6bdfd4a3afa0bbb6dba196f449b7817ce124426540afb4224d11060bd66baa4ecf0fabdeaf13b2b03f86fbb63baa0483cfafb520dd4d9acd4d4f0f5c6a0c2968936505ace965de35044cdfff1d684b33733c8ec5c4a174fdc32ba81b2d497f6cd7fabc389732413a6f08efa0fd23d08bbd605546edc468f3d8763a514dcc41a535ba985cd838af6cfbc778e6c58e30ef5b8f4d27a0c0cc7345c49f0f3e1090cbb273cc8820daba0985acbf7128b3ed06d5797344c6aff9bb60e0f8632ef743a1b347f59947d0da2e2a65b8e08583ceb3f0941ad039acb6d078f00054966e8ddf7de5cdb016073c617f19e0edb97dd6a0f2192f456870bf2ee2b9d75eff1947fb2f00f708cccca5283c824686182ef9fd7dd666406a8aff9251f760ff2b3b24f27a1606ecb4c77bc432af766b0b83e2ca8307e620b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"848806b30370fe7edbbd87e7e7148a704b7248711cb19a715f5f58f781f6da58","proof":"aee7f33178d51d4d3add21b82a386413ad97ff889767b9e38157b31f972b241184cdb42b2abb34ff3b2e690c744a343bed6416236e89447f1a317bdf89d42c70d63644cf136c57d748e2d837c7607ce60ab35ddb57871a24a441ad8bb5d1366a70bb7e3c46eb5fb5c4a7a1aac4f4f62b2991b690e536c312f81fefd46b947121f222d69a8cf44dd1cf05342c234d596a23e4805b22646f50c92ca048e411990e9975e4292c9f4ea09a1da3a94c7c66005c44fca5834293119d9dcc7a326f4209bcc9480d153df13976f9a1742e1aed7922a5768d7f4ef06be94c3dbf3bbf3c0d9c034b10428fa8702e17cd7b9f093e902cbe44fbc3a5726eff86603e1b08311be074e2a5c932cee59b071296bc8e0b45ae740d1b0e4f2e7dbe4e8e65d3ba184be6482d8c043fce862bff7269631811027d6663a3efc8bf1fa6d36500ba185332ba23ade6f3530099d9b79bcfd7d5bce6a8b28426f72d3d2fe38ed15b56c67e412c0a2c2cfcd90be062f24199a7f8a77b9dba846d3f45f264ed8ecf4865645d62bc2275de7a500cc2eec536b1daac7835f2919618b69240c3d5b34eab2a1e60781ca6ca16c6555a06829762826313b8acfba0f7441111ea7b969642ddefe03b11a62ac2204d239bb3369da56d9158445fa685720f959dfb123fe4f08072e47e3962c662057399be14e63711d87f32086db0f0389bdd667a6b39b46aae586e6e628a2166250852e8ad0d989082da2f74eecf2de756b9666a540ba819b9bcf425360e3ba8f505ffc68edcbbce57c634be425ff64964d401949fc4aa68ba6e4b215044582a45ae1af9bf6f64c957b67fd5952a42797f80640b89664a9e15b26ed526e58fe1be0dc4cb5616f63c2770cf150af69b20c9f4c88383b826a3cb43f2c90e7bbb930e8bf7aa17ed31826d6c2138352231ed2bc31793da7998e77b7bbdec05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9207178233e25754187bcfbc494f41acf85a4ae9ea7064b9b552f1637e438a51","proof":"28660e3f621345a97b786cb8b89f0c51fe01444f06b6e588e576f2d73fd9e939c686a48977f12b64ae99d7ed3f7e206060af5b5f325412cf3b31a1a1926c0f254a0f1635a82a0c69606c69d92f95d5e54dd27ad69b90e4b61861a4df99448950ca3587b9741076be4c9302092ff349d0ec684620b3f17938c5eb1cdf7aa67c397b56d2d6000770bf6a9fc6c603d919dd7ea90b6bf6935ab1b36bfa08329f360437d373a38c4c98790464911fe74a0671419f1618de151c7874e9cc408e285d070ca6d10431ca2864ad79f15c1c930dafc1af67457b8ac4af48c5b9503e69a608808fb166aad35604c6f61f69ad2df9a4eb7fca4a70a78e6d7c3eea984423ae75e85745ea35dad71c51dbd6497e50b21dc7044af197fc8d19e5465e0a7e5b0c706e729734d0d6dd786e1410b0b6a5008b79abff24263eb6d6750b609fd59f5228e2543a07ad570c46e3f61f2cfba67f147592a7a0fe0d92bb2822c8b342330853529b7f9b036a26346754d79079ebbaa837ee47432007591bf0017960b90dd900c2d70c98e9e256f17811bca1d558abbcc22bbec140bbde1449ba394254fa8220ae7709e66dd38cd276117360da4e9731ebee3c9857614d618b383c9afe6f5a443ae0eb4f288f75995c4f65f699738dd10e2a38f1dc11002e280ab8db84b249541a99c61b5e8f2c5a69538ef3a26067c13fcf89a58ede708d381cb98c2afe5e5934e26e31d2815cc1e895fd9a9520ccd3319511c1c0f671ba3467b4240188260db009691c000ddd3d3802b53e23afb44eea69ba2e8b7b2309381d0b8fd227622456cd42d89802b56802d90b62a46bc74a73c782f13dadae7fd8067f171b12e22095344491c8163f282b468d8aca5c92b2298a724085417fdc8090e97ce5cfb00cb4a3fb56525246ffc6dedff25ff9538e32c2a336a0d3d0b09efd9310482cc605"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ea620d32a847656eaab09905471b864c62bc55ac9a7de9bb458b55104e70881a","proof":"726ae69ea168121705796ad430512345f1a5a35ed22f38e8b60b91e76366c4247a7b9b8e99e4bb259f2fe399371007d4ad260b6da9d4352c4d97850dd2b0ee325a0ffce97b61395801c38e4c51fbde06182784c45775aba998a878db1b211771b4c070deae15f362088629cc6976d00df08726c38681c97c46a52c516244577d382fc2f16a61af359045a8a5be22efcbbff5d164df8e0d5227384daa52874101e72e471e1f74c92085ca0ed0a38842a46fdd66505e51f02e29a3b2d1086bc10579c3460e4bb7b5c67efe0913708a204af2dcf7ffc2f1bba2db715797340a2f06acc0fb72095194fcb3814db8e8aa0c3298f22356506706480158b46ee9cc1a2706b6de606ca2d0262eae659e0fc2f4cc55ae2b42a3a40628f752074096ddc862303340f0474e9ab51f43d3c92dad97f9658286b534f7d5fe089fb39d96c2a944aaa8ee0e6b8da4aa6542f4175e01db022c16056500bc9483e929c8dc7e12730adacbb0f4e97bc379cb1003f81a881e4ee62acba6736273b6a5e57d583f6bc56f9a4d80f958bba8c81d68081dc3ad7d69f4eb650a141bcdb73bca0f83000a21456cc2b555163cc7bf4c5926552dd0c4796657b9a79f6073a782dc8d0cfb1ee13a08e7dda8017eed71bac46fe2e6dc208db6e6d55efceedb41e4ac8fc40c59c677965c38688414886795a8c0620e993724e8b77dc00e9ddc708b23bc0f084e9367b8a9cf3ea85e2f6230121f2ee068f29ae3b999862ec5a6ef1e751ac7b6f2744b4e7b59940549058596dbd9ca3c79571047b57aea28195b5d1fdf5a2d0f824e228c51e8365e9110ea5a455f950be54f896575f83bb163d86fd1c895ca069ca335989570cd24f5bcb47908c9541d2ec590cdf4b31376540ed12f92990188635a0358fb2fbc41e5071b189450e888aa00559162d238f6dfca45b04863a56f37440f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3c8f37f8a62e68282e6d3b87786cdb3434f21e5b66a07e427ca22102356b1544","proof":"b6766df95367a2eaca207f636f33329ca360a7e4f1bf048695f97bcbbd2d857108c595dcfc9927e2f1c894d6990db8bc8a4db479c62df6760739cd49073890738c17aab018b5c292a21b7bded9062fb04a13c18bce9ab04a2a86ff4b5200907828d35fd31ce17afb478b72555af0f6de9698fbdbcf53698282e01338061b2a3649ac4612a1628ede39c38b23ca2d11b660c510ed7ab0be43a8d87be425337f00eef3492ec8a9e7302bf08339d05a10522ed9e4eb63620131510168bcaf72160265441158c434963b416fdf4b04b79575af9c3a41846eb85e9a76d9244ae2dc091069a509c0b73f7534337acf648e7eea7fd71221c485b3f2117fcd2ffdb3ea39282bb5456ca39514e5b018aa84b1a5b56324ce3bf06bbad890944f8a96c3a259166a6e81ea47d7b07d91ef8e2cca3e8b82b7d4772fedca9fbbf516eb055d3441a24d5576a1d336cf48b8fc17c8860e2cc8d0a67677b30b1f3baa91b9d739f0078660f53d202538078d49106b9c3fae3e2418071d6a7abc48232474d11f28ba116a91c606f439e96796d9769f2c91522f57f26672eb7660453f1028355a8f9d18242ec01a63de6983fe5bd2ff0327eb5274a46acdd4926919761a3e5880053b11b431e458b8b42b2f38d5baefe38505bf3836fa9503e2f58251d5a0470457c22bc644790f760efee6518b7d65c17adf6df8da2f2d7651e78d3c02f0a6bfb3e520e4af593d861fd1969b1497bbc27d6d70e6f3b0023b70aaac130d2f435979011b9e01ca4a9ede4b9ce532bcc4339b809706b0eb82d703055ef0a99354bb5db72970aacb7695431af5f04dd95f66fc3eb0dc96384fa871ce6324675f06deb1fe62ae5dfaeb1ba409a10ab96021f42dda1a714447e27dc0173229f0ac23a059200137be13c6a217da8bbe51fc0e8dbd2603e1a19fd9e92d647d74b681b12a733007"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3a147f9fb91b347c51b576a825719cf7ee8c9ce38536fa3d68b04ac03c63dc70","proof":"8e587649f4fc8805790bb0fddbee66e126b4c8b4e522819c93cdef719340e066a20d0a78d48679d45dfb9522dfac7e0e9866c9db56fcb39e453059ab80d585126c907f9f5b7827a4188390175ff6b172c08ab14c38d9d25c641464fe2cf867391c9d9bebeacca7f3cfe0b3b17958116a46eb18f2cb2b89133da902466692ff373125a351a723cd2ae0ae4cecf33a122ee7ae46e43bf102f74ff24b1980c33208f3fd95649ead528784f04d4afb522a15c41fdf5b083a16e26f0c8e50eb21cc03d1e4ed3a02f73b1d145b8a7ae0a2abfca1a11bf253935a87b9b66c47254d450d8cf852ba1cd182deff0bdff4e984776406f962d47854b619c730b7209d7e786150c335e7e6d63daf92c49cf7ff65d2e6d6f8e3ac5bfe3299ff86ff501a0a1640a8982c2dd09d13ccd4cf559a479280cffd23db350d2a0a18e10052b635e6de0ab8754429a9ed1bcc9728dbdeef30049140838238bda0a7650e4590d8e6e64d1d06f80357355e245a4f8474a71478816fcf0c1f3881d3861bdcf3675a1fc5ca3a5c8d59a39d8674fbfcd67e619b1a6c9d0e4dcfc1464a616507b6980d34a5e861fa1fdc8005d2e3f451b708b2206b6d938df49aaaf28981b1ab64ebb40dd74a639a7e72b6069990c04aba00d6856778d767d51235a876dbd1a8850f565fbfb36bee26494345eb8598900a193c8f243bd716d692c8953a96463cc725a12d331029a84f5f7258d5bb57148d594802ee69a4a5579979059b170422902e850709d5729eceac6ec872894d40cf8c893ccc559cda867c120362d8bac66017fae69cec489476660c3d02af01409b30e52cd78367353666657da3ca70ecfb2a8e9eae9573eef4c78d72b7d03e881bcbc43d8a965a4152fa5bb3b4d306eeeb75457a45b70e5b3e16143f5f8b9bfce276829e5806f4e791b09d273dddf15babd5b720b39a0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6cfe6ca66cff7f6fd17803173dd621b79a427746a73be76d574d34a872578737","proof":"c053b7751961f5cd763ba2b84985b6b94494cb6898e2e4a667cde782d1775e36b41a7b43e147e67da62fb8a04d2efdf8e8627b21d5ca3f17e1a1600941c8104822cd01fb9abfd89050c38d556b7e0d556dcb8eb2c2a72fc1ebd05f5635ad3236a4c131b23ed924553502673ead70453107dc588847fe2bb8410ae9a4e5d77a356307a1412b337ffdab4f3164ec47a788b7c6c7c685ab8ca5dc246c12ca81ed0396e79107ad5db17f1f93e91721c8fe759fab3e2009559bc82da3a5cbd2a89607d86b09cee60dd67c15c75bf6b72b77f07f003a8de747a846aef6b2445445690a10edd45b721f866b3926ba305f2a812fa97fd2c2c07b8ad06d2c1bd8121d0c022e023a96733df91d6e92b0a47c162097de58fae61c0b0d7456883e6529d1a71552979ca4d31458793ea2d44c5683acf7afd383208b1ccae948b4d4d038a8282c8a5ceca0a289ae1c3c29e9c0a31a20a4c6f1afd8f0e052f998367fcb0fc1324c30e7ac9d60f8dcbae76134faf77d5407e6cd816cf20cbf208fa5fdff48ad782a7423827d277b8c3f4cd91a560b002c7545bd5b2ee2db881721018981b3ff016d42f8fcd6832c0b8c11648d0d4c46fcc2b08e602b96b55a474c8d9e654334e31deefc82bf9cf7f3e04d899d39a893626e6ff39b5b67de01c98e950aec0a519057acb717e994d92551b6a0dde6dd2e78d2435223cbf8f2f33bb3e58b22725cbd553a1059a02f6893a8cbe1c78fc353883adaeaf29e94253333d55696ab8c3a691652abf36bd94eba68fe10ac0596fdfa08f6ba11112d4f3d8c339e23c72515a41b7e6fcb061d32162c806b81673bc8989e59145635762523a8aa31fc902f26111aa30c3c488d9ef46d0999ecfcd9fb366d575682a62c1dfa0ab8a0bc97a25ea508d2014f2344dda6f0ae9ab6f6efa983c12cf9c92732bbd97ba36790c4a3a7f202"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a01a8153393fbfc53f2791ad42c56d41cbc4684336740b878dd7b86ca75ff327","proof":"4c7e556b26ea2f931ca030b0ea08455ef1148ec56cb5bd92f85a7cdc7e159d7c3c90f73c59bfaa61cda989e31439cafea85cca5a891171a4cd5161059a20977f8075452c92ff00f0575236fed025de22af6a376233f953f26a2a2abc5f58a221dc4a315c01659638504ca36df3a5b6de5b77dcde62b83864b6cca79e24732b5aaa92657426026b02697ff72a4aa6e4d20ae72b52d15fe392a27000caee8d2a0d245f2e24646e924103a0cdb3246aa697ca2f7848132edc2d33a0215075c0b700e9413e9eeda47d7133ebf1c794682951fd72ab340db14435412701bcf952990fee12e6637c15d66c595bcbef12ae43e5a97e8939d902310c06677586c833436b38906a259919781c549a942432f71e889e6f31eab31f4759c64e7db9e1c50f6c281a710530197d2a89267e9fed9f085613ea94a8a808aa3031bc5f5e356d844806a74dd9389ab55514fd1da373e89aac95914c70932e9d64664f466a418133251e37c2f3a7696bb31ae20296bb392cd9370653afa1e75a074bfd181a5b87653dc2a4c2f4885fb27445dd09f669ee45417fec74d4b86c9a191928fe14991b76517ae5ac2e1c9f6fb9bc18bd9093ea70b8f12b6c9dbce8aa6a13f99512042961164c6eb7d4939e5dc4e35bbc6878f74054c526f20ad3f1d59e2725e61bd4a99b64509f1200397b5fa1b9c2a7d35c4ae9fdad5d09520a7ca26150fea04f913b4b7e3a16b6f57b0a09c4c8cd6dded75acfa6cc6ba0209763d69e356959506d9919696a3ac4ae31ddc82f511fa961d165c98f553ba119143f598848b42c811a95ca20c07e33d1aa08762c510ec6992fd9c91ae5c695d734bce65a4853ee35a04d876c4b70edd25f77422b6afcce1c11b428e24fac10d95cbe3cad480acddc60ef7a05674add38cbc1fed747d3c4db49f29a06d96aacccfe9780233e1a21d8ae2e9300"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d856b724059eb71e8d9fc33e83ba63a4b9a431e8c0ad8bccf40296d69c16f126","proof":"00467884dc5df5b8afefe6a76ce0f0195733146d37b4ac605c6336ab07b19b1ea095b22514f80fd512bf9d1f44070d7b16d9ff714c441e553ec34ffbf65a2c6ccc0a3b0695179bf9645f06d3735b23ae98f4386ed5b4dd24abbb13885b031936f82e0601caa2b8db21fe6a376b0af614772dfa4e419c264928020f9ce8816b05efaaabba22a3d7b95e9f17e4fadf2804b5c2b670cf823bb42a7f9a3c8f1a670da8395d62375355d60c517ae0b2d31f26eae9eba77d4552fffe2a80b62bef300e25c29ecbdd5fe31472d17ce93a04881aa6844559bd655e4110fe896c44ef7102869008667359d1a53d7e29daa7978ace8d4a58b40a78cdef0626c2a7e6ab43127ad161c645d3ce91b5309f3701b334d07d18fc0b2df6f695581802b172d9bd2a7205176d5b8c74220258a49dda742f0984debd46f224ea370687f1aa20dbdc2762d0b413c0bdc519667527fc7f3eef3ffe74dec2b72c20305acd38a62dfc79550e66b5c35001ad17a07f8978909e3fbbd7670fdc1550c75cc6a829a4db42fb085a4c3ee57a584c0cd26f77d84476191ded0ab72de89c405aae587872e87fe4428c3e10c1ee57841de331dda1e40b3eed8cc782d2497a075991d0321900de9a28f23cf1add91343171e50a6f7647d2f109cc370e35c081c27728758d6503ea40f82ab48b25f745edda36e4278182680aa1edc7db941fd3685b09ee4e4fcfc050bd65194f3bfd3498408c3a2e8cf74004f20e36579a153037d427d3c351ccfe565b8a4cd9630a189ea51eb175e6bc29306fc00ae3d8df2398ba971a79ac13fce6f307403c23713b0ea9a05ee62db0461573de3793f3ef1a9164f6a6ac02485b10aa4b46ac7025f1bec712f0dc28246a0cd2b01823a6bff962fa928d2b2f4460802b90f58f6a41310c33752d06d0c5650c144158780ba51343535dfd9c482a0f10d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"feae2fd8e99b60b1377d2713a2f01fd781e207891f87db483c22128762790c49","proof":"821a671a8a69d8e27dee2fba8018f51b0205ba42adfe6c8a1d4a50c600c981423690ea162e3d039f725bcece0c2deba2e87bd9bf8a6fe2bfc13f264cae604d661a4cfe003aa68ffb9b4eb59e62801bff5f4e0d37eefb06176488483319079a1e5a60015ef2fd2b969ec6c5cf2b9fd0c2f06c4395d022c659810fce5987bdf909116baf45625216b24efd61a36c233681aa76f5e947bc8a97d2b4e69a2f215f0021eb5b6cd3640b0bdb5b422122c2a5020506f4a9cbfd28c42c31ebadf2c2d20966efcee0f7f81c7ee54eb1c2543605314c331863c705eb7630f66d59506cd009283d979b278b066dc155b67c3312969d36a9f926a638c0227c90640e08389965a0d729be3eca910185b669f8a5df7ec2f588eaaf02f46aab79500a73c1b6dc1f427decf1def9b69aa520ef820c7bf4a26f22e21720846ebf105e72935ac1922c4a2300add1eeddaa1ec4858fc555c521dd7f385643b897fe8f6c86418e87207f40c0f9872b8642e4e7c586bdba12bedc671629dbcb7c326a233b3498cb3879230a72194dc900aa90c2044e7fed633016ef20c06c42a3c3481acd82299151f36ada6e7a31ccc69032b3b99e85ffc60ab71cd0a3cbeb694353ef393e8f6290f95092e105b6c75f85ca157040ea234e6dfd9d51afe869fc43188bbd4bb10c3e1024d856930ed2223bb68272ad3f8effb2a6ec013fc05a8fe910e6ae1dbdd5a0b50e6428e5d4c8d2a70b5178fb07b9f1701a9600fa4c4870978b12d665fa072d09049aa8762eec0bfba13187dfc9ffe47533618de48c64afe200ebe5a315ebe42f2642c7412d26780e04f495588225f261b5863b2e1cb9cf93f2e4b6139ae86968746579810b24df00b47f93c444ad01f00725f3bd209d0f36b8be247f971380d80942b8b78f491de5167601d6b3683a427142a5e3bf84cc9577739d84f26a2e8700"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ca060ed175cec54bce3842aa3d739c0cbab0b7bcaf1086f4d46936ad32fdb72a","proof":"0a544b853781fa1e4bee360ab6d01b7c3b4e5ae66dda639a1ab5e0102d1ec43d7c24edbf13e9853c48df41611f9eb9b8358c5db353e06c1460caaccca4c19f3ea0032a2b4b385568806733ce5a14819600a5f049a86a3013ad4a30cdece2703d8e01f0119a25b1c750480e5a60c3970d410511b7cc3e615aa60e3901e994934fe9c52a496663633b07e119a782b56e0d8c1e8ef747105ad416244fdfcbd1af098ddff596f939f4de6d6fb9b18237f7e4681fd4d776b388d1d7b2a55822d8350eaff07f8bb258ce4f463f7214730711065d8e34c2bbd17b2609d1d088c8d556032e2a76691763147c21fbd24817855c788fe2ee8ab7073531b061b7426ba8bd6d36c32db05b266010c3542bc323b49b22c8016ccf2e579d05ad2e9166fba12f290e94c20b559e2adb64224925b042f738442488fc1d52446e6cf9d2818adb6d113e89be3573fb87f6619a73972aaad08b018318225fad8002b45be1356f6fe44b1c579c026580825f299dac60c5adab628b5136f777156afae606903a6c51ed72ce4b8efad170e6220eb845155439ceb88f83276e22d55d9188cc3b6687e5254d0024b8325739c9555ee9d618e85d66db0af84232d684610aa2845291e798eb238c8b871114c84c1f2965e00b087385b8f87f55ad7bb1b65151ebe09778ca9a41ac06ebf08a37a34c2aadc9bc2470226e495a224c5fc427e60d8465bbc223860dc81ba38cdc22f07dc88f2def2025db6fe16b2088154e6740e03d8e4e834e947e4c3017babe16cf11e730eeec91820535cf3ce48be3776db7bc7a5848ea09a315d81644cf1682fc2a002b9388f41821ab754f1153f4b33ffd8386150f0b401836f00e38103d31c7a5421db7775a21aa203557dbe32cbfe8c34f792f1b416126003c3ad473a6bad6d1b47c73d1cd322f7422d3754ae70ec78453ac7196d4827803"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"72047bcb7914e35bea9b049485b2048846571813ea49ad825cdc0d9c0e578a78","proof":"fea2c30faed6baa73b10146735db432f830503948f74eadd5957ca276f7c5c09605db6acbde8ca951d5821c9289441b8f62b74024568c6ff76d079f4461e0c4e5ac4daacf8ed1231a74a6fdbbdc888d560385cb635ef4199cc7cb0cae5cca02a428e5c92e874d48ec8118669c71e4926f94aece9e8d7d86e46ab0f0e936ff849fb3735568e0b2173bbe6f4fb14f710cbae49781d7adb182c1d7c3ea9d34c4f0b2c338941572defb5456e92c2b20ab8fb0dc7f06f0eadf3a555f257e66518cd04c0039a2775ed83fdf05c1b320adea6aec0289d1b2bd2016c17c66a78c1fe2b086ca2baf5c9c4fea195329f94db4fc602e9763a37241cd33c3ce59377cf898010fe431b5ca47dbb9f3448f45b9d22318e22806ad13c379893175cb4527696b10a2c0ee803991d7093b6312dcd9af8663dc342e84c81a21e707be2c398e72c07114e8d27bb1914ff643a217a4999430f0a90e8e56091b37e79d782ad81185456616411e0cb4c576d6ff9713a1819b195d00df72bf9b87b32766edce552c231ab7fbc574155726576662f543b4d76f7825c40d98d759375c329e6bcef7beef1aa59d221ee6d43b33b7b109fa8efcd5a238687354aced7d1d3e7d1b8937218222e0c463978afc1e8806a538e6217bab7793d72d3fceed998c996ccbfa05f45bc2860e68209fab66b49f37bf9e33e06635131d0c63ecac81b9930611fea43f824751e3096d94284dab09d123ac64c6bd2b9b5951ed68decdcd6bdd605b743b1b2bd38d6e812792ed23cdb02881af2bd19921dffbd7cf3dacbd894d8ae48d76a2c7607aef259a382373ea8fda8bed284600a185cfb7a407b82d8fe5753c4190a38762a999a7d7fab0425ab1b533ec53198a6ad1c5ea2f61e2b5ddd56b3b0106df1100d24e41d2f0d09c5ab7178891723f00f11d250c955fc21bb7f245ec0f7caeea00a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ba9e3f3602ed8f352120f78d497f11d34600db9784198e7b1abbe3aa7efd2275","proof":"4e9a31ab432eb48d9eded35aa019f4f7d5b4bf2d9b6bc3ec0e288d6f3bf4315338f221bf9a60c4fecd09c84a12deeee5cdcfd89c5acdb762f55c70393906357b0e3004163831fac584138316f5d32697604863077486a8ea98f5897bef04da5c10e016d658c4385261334486eb2b3b086ca9f31d2f68f744ce5fb35e7dd94f20142c03c748cf3e6fcb40bb65bee528083cb2dc615996400c4259cb39921c27034dc91bf9338924c0e8260ca4856659523371f176f07ffc4d2a3c7740b599a80fc79f61be0e93fafb667f91b46f10c32cd385824851461d926b9a560028dfbd0680b68f2230ac58c3bfe368f052ee32669a138f12c41c4dbde8b7fa3e1646cc40986d8b535561be77642e3aa03c889da0b0799db3894581883d8af83c43b2d93cb898012e601d2ddf6593cf69b3b29a97aad33d750f54762682d0769e61e6331edcf1a456b9e7cf7e73d561ced5dcf61c8a383fe3a33442d61094f84739b80e2de4bbe6eb300f965dbd88dbbce7830ad96350008e6fd23960e08497f697f07a16427a597d9b5778662397c6b5d9781366b9a2c8dd5a8caa4e7cf9c85a6a3abb4a56cbab0681352e9498a4f35249707881ac4bea2fd9b87be351a88b40cc6f45177a77a42482f7e598ef0db824376a5f853a51a2698389178469b2835a8e85aa2a620170821a5f04dd29f4b5235335b8c5697658c996f5fb52337b9fd658d7b45a6e473f94dba4152c3a82910bb7a5d94e1caa7fd42212ea9d411c88b814f2707e36f6bc92a0a3bb9512124f2ee1376b84255d2bd3a08b35d88aa512e21a14310b9069c51de4436c1d55bd0202c096f2198f53763bcf1bf44afc2879fd65971f4d6d513fb5211e5e09cd30692f6a0c04fd9456ed2e273f77b21432b3edd0a5bd03ae61d577d7cc338e4de9f5d2966a33e90ebcf9d8d66f5589a4457a8dc2f10702"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5eaef18692f221f896d6ef043aaff2a0dd9c517882f5496052e7fa6247ec5a74","proof":"861df042c905eb72e78dcdc222dbe526ced6d1497cfd55eddd1cd09d4d9a62695e135a03cba17712918cc1ce15ba3d30dfc67874d8acec9adcf50a0882f505409c1bc6879f424b6d43de634f908db0c2a94c8ddfbf537c5eb0ed9d66e413152c7246734a1b2eb824df5d264c9def995d2e02239c6f5bcc801a9ea64f7599fd6c9120a67e6ec3968557f02bea04018f7596ed1c6f4b57fac5d8ebd2a2d0edcf0b55711c50ed63ab6049f086af06d59b5bccee36c19a0d0a33ea81248373c20a0efafd5d0e4af837c0c547e221286fd46ffeaf5445ba04e4c8dadb1a6193259307ca6049347639b4e21b7bf0d7293b30be6f23662e0a54281d4e85e83e6d3d1c4aaaa9f8964d5141925af89c799ac3fa47e20a914cedb25e21f276cc5484e9fb713017c9f9d262ea4203dabe32bdaae0454b8c855534cfc66d57ac9e88e9252c72fafd72866606a0b004f44054ceb8b1ac1dd8ed6eb3d82232601af9e99ab35d092a608136da4af03e313fb5745871fd165740dc0052ea356e53d75ca135ab3d1c809a3f6abe8b74cd9ac39bfb7dae9a56257535970f7beac49238aebd80a4053f5a5e7572aa84d3fd697a48ff7859fb42ec7551a4529504b5b103db070bdca56fe6d406a9caf19fd2c1f0666ea5a29c40add64012fb0d56d28ff37ddf56c9d53a0249abdd0730d012b987a021aedf843f0c5858c28d7d3bfe9c5f6ad5e9c782264c532a62dc86b47232681f8a82517d38b56c64c62acd1adcae4719a89961135dba0370db9d30fa4c62c41ada5cd5243c2bfb6ae50cc0825d99b607b9182721604c053327a59c3b9bce6d1a634a564f37f86a212d1dd720d7594f479bdae6be7a9bd7a7913dd84fc5187db35bf729f6801b325f31afd3f591cc1f7906249039014e8f0f3269def6c9e94831444766dd21f21fbb872ef0d3e5dc5eb0dbdbd6ec08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f6d4ae356db7f0c95ef0a3a8e15af8fb487eb766a6baee05e6aadf4065bb5f61","proof":"5cc3cca8611573a2640fb811016e9ffd6b3b953a78451ece5a50fa162908687ca6aecc03cff2794f22edbf5a0051abcc15a53b41cc0c30e93843fe77ad65bd1d843aab255dddc79dd6e58977f850434e599c24f7a4923a4fecc45d3048be4961827a74bde6e715ab4e610baad44925089c88c04c61c029e932b444c1cae9a333211f59926266b0aa5f2061339eeef84d3a2464331ad0f863c5589fe45ec8850ac20e7b6d3ee56e0a7a9cfedcea0ef852162c7294196590abdbd19d61e30b830a2c7d3fe441629df3f220beb44f0cd4bd4151ab8bf88362951d335f3e22fbd90262d21950c1bf94f0601ec94e5a54d6d2f65263c65651fbaa1d0e896b6235ef157a9e273c8ac202bef7a75206064c257403259e08a5bf886a643d4d9a5150d26934dfa918ae484781704276ff90e6f0be31ca28b00129de9fe795cba9f3d8033db424a6bc39e4a3a471a87e6734a946bb3e0a527ac027dfa8f75e4b41621e774d74fb2f7abf7cf24bbcc7748c667a8bed52439a14d50e54ad9cfe2a21755bf40318e4e5fedf1c26e50a523c7f128e31c9f6dc593ded29172ec86065899b586234e6bd6d5d52e9efc3c105b55d2a9a37502a64d2b81efabb8727983fa38133245d2c9de90eac9e875baf4ab875bfe88ead64e642c57995ed480625be96a2e61568b0e963d3e3fd6bf020513be3db9102ca10a25e0a6060d22ed2da9ecab5ad633a2855536f7ea151bc7ce9ddaf02d814613b0519919953019e6d3f6035430ea9137a6bcd404d1254a7029d1a93c067fc91cf28c167b1d1dc718a91e1c41cb32950b6f121c892eb4235a26ae945564ba260b892d15463bca3672464f6e077714774040518d5bd636fbe031c2c5c79fda49cbdfa9d94ad855d4bfbe26a580bf74c02c013bd692dbd0601a2b1ddf570445ec2490715f156bc1435a92d49161fb99303"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0ab3c7ea0b916f3ea24928a4feba0464d7fd5b862836d1c418f799a9002e5748","proof":"3892d837743f34617e00767f47b2debee674ca742fba4b85e1e75603d951a6172cf3e993da80a7519dac6a498289a62d9c555a0a586e0a75e07eaae392f10909249100cf96d55f63b145110245346036406b8202762515016f783df9defb310bc403bd0a562f28c7d8879bbbf209a71ecfd18775111042f6e3911e8bbdd4496e0b6fcf4b4122160d3068397af8f54f3fa67aa6d96fccb4b40afc7efd38cfba089d451555157fb5d6ac52a156b405f942bf5961f539f9ba4ca9e7313f5e3cd8061260f82305c17cb10c47b073c19c321d65c59e04b7edfe1469a83880650ff503d03a9159c5a91ae99c55121880ef0a2168267cd154bd48fc21a85f9d38f2aa0d1ea85fbe1ef22f7d40568260efa34540b3161d180a5f88006cc47c3aa91b640b449184824680170647404f424c32e509fa55f5638e4c6d1077550c77eb1ac303c208bf9f1a4b8160c62228f5052acd282928d5d8f80415c8cb4e2775b6aab7080cb5a285bce72eb75254b7824b658bf338932c9ba939cdd0749a6571173b0c4fecf5f9ae7c9c53e9692e3f3969f1d3177c41ab58f529a86ceb09330bd8ec694a5ccae5c1361c028094dfbac717158e7bcd566a9f6be1e38e5054ec18c7a5d42d74b2c593078c3556524917f5129e16ff6f19bfca12e09292d6be57fd01d749321a65fe7d97f0efe8727ad144b05722ce5e84e08d3c7e8b225b190f7400a87f0afa6067392695ef0904dd1accc558e21b681992296bc20264f50bf47a446128202e2d89b8ba0b2f158d65b11d751627818ef5346c4d9da6d918bbab7e21477d602e2c322a0123467a5d4c41e96c739133706c723409546d587044d8c38f53dc76b43aaaa0f2aa5b99f9402b71d014b708a69e8318c6e5fd2730195dc36956740375f76465c066379009c627bed2e73cf58ca41ee5ca4624b826e6e152879fd000"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f61b0c88bb0f4d0e0a3d43b9c185b22cdb2213c697f9eff75c9b551ff5748371","proof":"d441c5b1927a3956d097011bb61611d907421729ea47d040bdad377c758cd0434c22b140e9815c825bf4ebf8bd5a777f3b44db1bb48deca0dc4ca0c19044cc234eca95bc42972c78fa49b871db52ceefa7271421ce2f78f6be269d9799244e386abdca1b42b962834fa5e0b110f87b1799906ee64c7d3ba632821490905ee04d445ea2ba44c733afb64a1fd6e2ca0828087152f2ec79ab71e6f935b82fb01308b877621bd3125868d8fb76d462a29d26b184b277395e2bca2f875b1eab57b001eb52022f63a844c310a3d5fcbf6465c26842a0803c724184629f3eff4ff3f106a452f603e22021157da185d39133f6507b12d1a7cc7f1c8da8cf3de4f5c09333acd8dfd932d604e4d4a50799891f6e59cc9c2090fb044ade6f167313e001273e7e37b2ba66dfef7192b6f091b6671febf6b9eecb0fd321716bfeb44d095eed6e0ce35d776af794760fffdf4476da36a09b138d844117b036a2509a2e7bac9f54b438d8f398c715c90c8851bd7102949273664a99e65a5c99498e8a9974600506c2b7abbf8af850917a33746c6f87604dbd4ddd8e7133a795210b7e1390e2c358c45d774d8f42032c6c6d2e7f212423f1d139640080a06ac1a7aa8972bb5473511826488f1c883afc19d9656e5459b37b06adc7c50810c5eda85a68fe22d26c224aa8152740e548adafcb2f7657204fc1d544467ede1d4cd1f9627748fa71ec5470cab14a82a0b2740c0467c0530b8b79de438f40520750f566ec3e0b76e3e554b409e9702ccce20c7def3ff341d80bdeb1d5ab82821ef373759ef070f73e1714b6d29c9ce67e9eb036eaf9e119953dbf2e4eb8ade70aac848d9918efbd18c124b32c0422259436981879198eba82606c36874209e70f1610e5a86d8e8ddfa5069fa1658eb66b315ad83257276e35807c81f7d3c36f1bf6e4c7e9c35a28b89506"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4a3f3ad3546a92d92fd8596c590cca545034b3a715691155d05246a142c4b823","proof":"5682f377979c376fffffc1e1b93a5de796df547b48bd15ff42f5f8138b0f0d12944ec134d4389f9f4d5fc2b95283b739657baaede94bff15fbdd4422922050085cc88c63f708f5657ab46efbf43e36cf52502b03481598e9d845079c65472d73ce12c3622969d6ff9746f394823d28cad9c7e78baa02b3520a2e6d525f06b54fe5d56543f0550b3393df17e9307194564405ec5d654cafcf53710a3dd9f1420cd37084da0eb29a00c2d882cdd57a411daa9e835d63b6d40561c440b9badff90df7f4002048d5e91c6afdc34fdbe3628e1feae7155016412ff2fcc6f43dbac709143a728aac652cf963daaf5d6b7317567e5330ef4f4da5db481a0a587a03b938d2e54c7d03453ac5f8811fd3e211f2a93dd4d06d2f6985381c488f8681d8ef587e2b0dde8e714736fa73a862db638ed7267d9965ab7c68fa06e52b69f7dae4215ce5b82a0e25177251fa8d45f397dc442e0afa2cb457758b7358a624e6dce469fc6ef1f7b614a4969ad8ace11ba02774f3dfbd150fe6135fdedfe99556d83556cc2a448eb7c22056774b1471b4d31429d032df8516b93a3a6c94007225e72d62f82427e1d4eede4b7979c105ee81c49619c83e00c91f8da57b318f04a7e48c66ee53c0d402b94bed959b599f6ba519fe1b09a0c227bb7bec23113752fdb44c5410c37619f3bbc3d7d5af0de07e51b140dfa688b753a51115339019a1f1004760200b87ed8c7731afad5f50a0f8e7def80f3f84c1da1ca03862e95d8db90f3038244d637858194e45b07a4ddaf25e6a117c54fcdebf046bb16e69c10bbdd85b3dd628ef1544048e5adb26b5f742456d4df351d2edaf0e44c998f076eae04e982f0a842da77556292f9063afc64cfb9abd65b0417600e2ec85aa96dbb68750970035e41cd96c92ddde41402408405a37521381e829733e398934e41177141d6a06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7ee9ea2cb5be900f99b3c152fb890fed2e4d7fc3da377685cc72e0d908487a76","proof":"a4865009321c26289cfcb7f58e02a5e8b3f54f81f1b0f3f739900780f542d2009eae52a93bb67956d0bfe3ccd74e112ebe0f1029f312342c2b71f59924293a24147a3d4016c6a8b5f196bd03df7c1eb8c9ad06ab3aeec1036a870ac08146e90ae0b7c0c3c65606086dad19b2240752a8e933e126c4cc0ccbcd942a8105a14868ddf044fe76997843d7f91dec748944a93d985a8d990ca3cce1b23b347c5efe08fea9a1fb24912c02b5e11059a1a5971f705219f7bd897abf7ea6a23a15a154069bf218f739eb6ccd604e9dcbad3f25cbbf3d86c1b5582f61930b089928c4140c960d713aee9616d38e8093fe95f89d6953bc491265e24fc9d16a18b4d32e595e42bd225b538a349c95b6744ecfc62ade76140c3cbbb7c7c65571697815fa1a1ef21ee72821668e6921b7e909848fdfb37515b67be228075e375853dfdfa61e76d63435ba30acbfa9040a9df3b6717da09288f99fda8e8d580b98dd24f6f01e038c549a0410a428b05027ab841534c845be25e84b27c3684089c585bcd563ee3b061ee49283eea784cfb8170d431684112d4e3a37be87d1b7be629581a0f8123cd45ea89deb044ade00598f539cec10d00112a748f970da47f025e2a69d426a258078296f6c4f33b19f702cb15eae399ed5fcc63bca0be4cf3e43bac9fa656730ca0b2694a78aff8f64be64c43b22c77ae4482d539a690b931288c6b3de4a9b207a5051c83bf272aa1de76dacf122dd5dfe046a4e460b0fe1bf295dd46e5a1653a489874a8e750fafa6677674a3dc98eec07347cc157fab4fe53d8e0e5270d44fd6432d731de22d6b6cd4bbf074025a6a8d89c3228bd0d371f96465773c3aec63e2690145834cc12b11477fb91a04d5f44b39853e348d1b4497f9beb91f4ecb0eee73a4ccb9374a4ac6d318c02fca7b34ec093a0df66df2597b06061e1048f10f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"98a4a66e5c22ec86793aee71f7103cfd04a69e1c129b53a7f5170b9280f7016e","proof":"542721ef0005c697b7c10f9a9a5eb282ea7c38718f520aca6a41219a5fb33570b880c201b74648cb64ef011de27b21782f31f646a30e352a65cbd4222d66b17d2c9e82473bdefec1034ab613a73aa6f03071aafc9ee5760fdfbf030b83880054be720536a0b8b649f9698092a3cbc8bc8cdc15d5bc5f8f1d3e25f663ca07fe71ed2cca4230ae8400c73743901d36820b02f057a3ea606699f3d2361bd889440a097b559339457792a59aff257f32c2ba8dd99fad356a32af6dc49a9f2f98040a26b5957ba2972ef3494cb98b448b7d4eadfd4a8d3a5ecfc17b36606105ff2f06247c67b3fcaf67f427f49c92367b0151ba3c34ac9055020d0e437fa4438e23132012ddc4700d416e2f51344716efa58fb37f7ece9cece6096b62d239397c581cee566df0e97a67fef0373bbdd5e09ccc6ba0bb3e12b2291e172978dd64426003b4c25578400ece7ecffed6de487742cbf1e8f0ce78f28f466596b1038a302b10f24f8943240ff28e5025e75a0c0a816efdcb060c8e9ad768f5d5d795d814255fd066245a5acb64b2139dae58817bc574f7f95313594100fab27bbcc64e9b602668b48bc5164d9e97dbc0d7fa407a576b4258ecb9f2d47fcaf920f247e694483eaa07e075b73433f92f8330be08a5a2e63e6bd79a6e3c1463a62d54d11a242c25dac4dc7c489c9ec362a7c4a08aba178249a3fbe6e1b3ab031df31efdf84f1658605b6ccaad7cbed2d6fd1ea2e47a6c397cf17d2b9551f3bd8f7855bbc42adb6e923aafb864e6dccacb0af9ce06a6ec679ff35809be9596988311b555be94294d90e63e51825c6a1ae6a1124c5d01b77af617e23a4d3ee35f5fa24d544f6dfd20d6ed019b527bec18c549bae211d6e18250525573190bdfe975aea6e4d7f2bd0ddcf18597c4355555ac206a004dcaab63ead1b99758f0643937287bfd6c0fe705"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1a0b8f6c9743b28cd0fef486626c373892feadbffdfd25e49adbfeff18669419","proof":"fe5324e33e629a3969368b0a2cc101fd0cd556af645442079e5cc50213967a1a3a2a0130e13661e7dcde16ec84e39c4b6070532b79ac4af07c1ad4f9000a85242c4fe0c4eca1c1c350aba202ffc72ac0c38f54d12b482e397262b3a7026c976810887cf39729cb13abc3130b00fb09623b6407d5b4ee1e6f2934df8e99725617652420964e9aeb4b9d3fdd17ec74307da78247f34f61a2c7b351af5f0572800bc8fce8aa891fd16635de0c40447c79d29d0c200476a1119926b1230597833701fd0bba82fda405e1eea3d75ebcce3882e3cf5b2b840373be9d7374f77309c00cf22f80888a4a40c8cd98fe269f479387d56d733e03aea3ff7bbfbf691294d704ee19bb3f5573b85590820941490d55d4c45ac96ca2af03c9ddf28635b899ff060060ee50ebe3c1f8129355653f6986253dfdd99c8107346404216d66d91d761c76f969ad4b916968bb54cb11f83f33733636202eb7828ae8a3716d38f5e1211b400f42926e8dbe7a27677c48132852faa69306bf2d5b54ffc6a5bd32a4d616386af1b76a27c385589d5e55ccd153c1c0bc7346719d19c554288df992416928067aee4cdfe8334e1f9dd3923d2e9918ad03236d3cc47e72fde6170079a04d720648951a5a7f4396df76e0ade34d59c2ad5ab732c1aa2760d38460095387ddcf0828e92f60943164fbddcf748c84047cb5570825bc4393421a54371ec877385219c2ace81c085861d29a05131795e67423af5b74090acc117d153409cfac26e73f7aeeb57e092fb7239753974945cf4c220735d5bfc4a6170c3f3355f96fb80910b0b25cd08724453be0743f6919fbab0067e7c9f5a8fe7c7b22168e682274890f2533c7eadbc472f0720b6fb5bd34cbaa79d6b55b14c6072b76c938ae0c4a6001a1bfdfcbaed7f1e1a1b21ed0577ea4da8cf2209f2cc9c65f494a0a115e78bf05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"be97f974409965bf846ffa3a58965a80b4c6618178028839dd51d49583ee2e47","proof":"2e45f9b4fea65b724e4e91dd41007215d51b13ab49b9de8be8f35f2596d4a15be0be3b6c2f688f5b493ae0b5d34ca75cf51f35df2d7ad8137e634e9aac1f8e527ca6e749e39098687cb0571ed825326204cdb85c599e6d56cf6ea7c2e37dfb2cba6944d4080e1d430d7d3d17e0fcfced3f995f181535b4ab2e4e1c63e0cbd540c7d69e468acc24d2ba4af9349e39861ec971985bba532d3870e37facb801b50ebd894447277ea5e217cdb9cf5b61ee2f2b6bf5e5a36fcdfa7c7a7fc744f3fb06c7049b49598d66d11d5e6cd79cdbd2cbc559d2d726a0d3f386b8fd5b18ae800b6e30efc1f7ec4a289b629043f13d6005ecac32b10eefd3c9af6849232b84104c7a252c65997dbe228faa17ec531608ef363fa6bd1e8d02940eed7452ccdad61daa448ea98c951ed52bb1d3b9f09874d1560fdab7ec2710a7af6a85896b0e1456627cc6ed1fa5f6e700507446f7526fc8a3b7cb8e0f9c60021f4b6b83a9012616d6a83aefd5e799489ec4dc81520c6abacf98cfcd9170f01467f1cacb33090451302fc4b852e395911edd9b0a8cf2b8d867d74d8b7f1bc10911c78cdfbfad496206daea78b8c35ad1b3c4706cf9a4874eaf232883918878fac5c6d5c2747163673e2784f8a89c0f1a8759a4e5fa2e05dca45c09670f97c752ec8c748e24ff4f578069baf27618251172427bf201b0fb4a5711143f2e44dbaa46c4ca4cf8bf4d25bce093216b4063402b32dcf7ad279e6ac090da420cc132bd5fb732c3ecdf1e6fd66a9f5b12f442a7f4dc19bc0a1e8372673a7be977712173edc6c23a0c702e5218f5a7a467659b78c24bd089de99b18fe61e4cdffe25b4fa49acc7ba4a1e4428fc8edc161712d8307645eb0f9e37562acecfa8044c55bcb5a10c83ee636f140f55ce211e9775da4a43805cf40bf66134e61042de740a48f8b90fe674a1f5da0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f2271d184dee55c5fe102f4e391b506185f01403dcb360b18924085947fc0148","proof":"ce8412c5ed562642aca20db8d1a421ce89c3116cc2ea214033b564c6c2879461f0b87dfc4e9ed60fab9425dad28bf3fe416aa4d895f089e1ec786b9f0f7efc38dc11a4f3971148543187f468b3c5de7e150e3c34b3ba98f505572e80d6607e75b669b2eb1bfc3a02ae847e1e748519279b61b80b1fe55c3ec4a8d6e56f47062337b4c8a29b1350bbcdb7b4e94140ae2e52c64cb4767aa8f9925b89f015a6e0017a422f4b227dd52545faf22ca9b44ff25361b9d87a5b75fae5d94ac57618e0098df963243db1dcf13e7e45ad1673b42930f6ba7cc3eb43e29f7430073d67a701661a25841a01f0096a7aca844520a24c179a8cb799a5ce27a3e1a7c584ce6c14e4e3c90af4515a3405b283265879ca781f5ee230719e7ab59cf733aa10834d7cbe1fb554ea095d10e7ac9429d9adadf10b2c88d1c8e0ab8c01ebb4eb929b77131c4f7f73e1f5950335cffca6fb8665845f04b708dffa14928e7748cc57cf6569b8eef01df1da4fc0c57d96ff1c591450086d4c20f28cd47dfa089653e134cf241a88ea3580e13b1e988dfb409cd10f9c70768949c44b80edcd769d636a92ae159447016cc9632305c244fcaf2f0a34e700c1bd048c76aaecb7a1c541ddb66d242c58be093541a947354179514ac4a9176b16c5ecd013e66b4cfb005bcc59131cb823cb264f098a40564e1beb73fe59d0342482adc037501c811e640ccc1e7d673af5e8facb3eb7f14c518d7635cc8bf0cbc9c786865f1fa673726edb1d36273184af152c377a9e2cedd4dc7bc20155752f6f7aed1b9051a9b55ba05f2e79a8557686e252fc33d316fb0e7e926d890e3d4f339d65d28463131451e5dfde61e957dc52d7bfa5a6cb84214e89ad44cdff8d618030e000d372d1c12812682617be0593887beeada21ddde8d835083f6f6d4d4e33134260324c4e51c260637e9c410a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e221da217179f30398a5757a7eeafd11f1ab7c14a5faa6dd869029bb1fea1325","proof":"62ac7422d8f4a5e63de972313472b558a8715981b29b74f1dcf2911a682cea39aca14c29e82e56a9bf08710601267446a68d86534f7dce8a811303017327641ac0b9e35ce16d82119c916a64b2d9b6f990fc6013f3d8abcb88873c0a4808673fe09dd5caa48faa123f6549f0beef8e6d0e9fcf0faed59246bc7071402ddf5a7b65af9855c2f297c1be939b85944f7e564d152bce08f6738bef16cd432efbb70e6c24a6a4ade4208f5d9c11778bb9239f69d665847036ca09298288556d799501f7644ed5652ef440aad41a87c3f0cb1bf8ddb75b0f2edb000f1c3f4826b99d0ac489413b118f810bb1aaf411bbeb112d9cd2450729afa16fef408f398574e55264315cb6017d4ace6274fe32e132a99754e423d595452cd96a1140f2c265c51f5cf3bc7248a1938093223bdca6e25a64068a989d0fd4630336784b6e436e5539868c9ad795d313cfb7349f49e3bc61396cb3481930cc63405f03ca7b8bbd0b3f008288eec30ca38139fd272ffe1d6cc98784dea9d4a2b839f658eff52284e30968993f3961ac12482c5002620435e79ba7c92874c89b4a73221cc1998fb41f57c481bd978bf8cb4b536e523fc5512a71fd81e55f3fe9681956aba1a3e88f472a56293a060a385461f4a297de6883921b06bb35ae604d5871d7ef0e996669a746427ca2e241e12448ccd71626edfde9a62a51a7744099707a1e5f9bafcd83b64058581b04f1d10a6e14efd1433e7e8b5c150dd879d8c2f94e886a06100b417c7598facb6590f411e224798bdfe1586ce054570f903b03abc6f87da1244c2081597cea26d6debd160036b47585827932a23c17f3f75fb32e7eb736c72a028855718de7de6d6643b97bc4c84666fb4684f257ad32b21afcbce0fea10685a2a2a704266d95f94636b7a0797909fcb86bc1f5e813ac7e0211609c289e4e06ff5df009"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bc4b4dab16e7ca649ec030927f3b8a72772e3703d431e8fc5e3fe43636fb0f31","proof":"969a022ce683a93c139777e4c642b2a72c349eca96ed95d48811ef29476b384278130b04bf8e481492f6df9e41281764d4a004fb72c2bd7c5749444d5002c8127c5be893cbd4c6c37956bd3077c5d26b81157c150e2c93e32fd483648b60755102040237a7c4759d68beee068e814468a0156ee48247bb13b10acabd3e7f4d4d7964bb6b78b07fdccaa04aa57050011986cb1bef4380b45ab3472c3da6ed5a0c358a1b6a4c80cd0df91bbee7fdf6db9fff9cf0ba0244aeb194810337632390053f6dfdca57c53effba577d6823b7e08af9fb53d6942e4ec7ef6789b8be66ca00668431976a2bdcfa6e4e8ba0107e4f48c6af6afc1901e912c0bf358fbf075f4e50ff1b3c9549b544d7f3b9f10fb72627f90c968f2bc3996bf7f9d163ab84972cb86bf1d4c07af647b3af3a2c755d3b34e210b60039ba1a91d1bb5d47ec739f06baa122306c1db4e0eab4de78b3319ed771fe6aa933dc6a0551813e8c2b9a9d696845e9377c26802000387d6c0a6e34bfdede177513c2c816c5e89cb7c0beb053ac78af96032157448bbc7462819b67eb33dfa0d9e2d4caf59c4de354e14c117ba2a87e428ff10a816d720c0a57995e6324adc717ef221e414893f854186f7c293cfc3e3fdf45b8a51112d7aa09c036547ddce356760cdf2d1f0425a6b91dfd33929395db251e77ccf4afec22f8eb602aa55bea58cda4f03491072406032c0a2c8e14673a86c219210dcde9a4cb4c227b8116aec612f9334b4c9b491af4444b5d3a7c2784c57a8c7ca1bc1cf211995cb6e0d190f2b77e020d2cece3f4d958657480ccfa3d72cd78a1b4d3d6101193da3886752f3dc6f3622680919b04fac4295786615e66d30eef90bfbf2ef2679fa0c17df35a6e7d4d6a251656b676f8634100c17095b6a6ac2c67f55cd8ce5fcb7c758a6281c4f365b14884fe269e0b273e0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b2bbbc204f07a3547ab6fa5f805d0f64822995a3295087f40b039ea11fd50465","proof":"5ecc9b4b38d03cd836dab4e9611122c075a890ff0f8626404f55d3d51d98692508db1c568bf2b4d08d0ed5beb4c7b457be0a61019b6916dee8da34c7580a78659868a8d6e22dbe680de7003c6803c98fc0d1e66c8d33e3113f66b48dc03faa373286118c98cfb3209d8ec62cf621cb522a820697d331def807b88ef52b567418d80f460c979580e87d4b2ae661094188a2c4b91e85972eb2d6cce519b14bc905d20e57ba72a8b9de94b16b840eb669136ad4fa4f13a08e59a72d912994cb310f47a83a4ffdfe3685a84a75b9af694aea29b2107eeaab3f8f8c0457073360af0ee691c446b33d266a5d47d87e6c9f1e6597b8ae0957daf73053a4f578efa4e91b106287d63e672b36ea136fb2fddac07207f6e6e975d0c6c73d91811a99fd310e6cd9d4a4b000a2a6af90466b97c64b4a5777502df5189210014b3347d6c2533bae0733ce76ec4ece44814a694bb6d09d0062f94aa1b664844806b57367c09920dcd698db675fd5e54d4b3e9cb65f3a7d558da9797fb1c5f7f48d4992b23374626c365117eb1b986491c78cf34fdfc01699fea96e7c7508802b074dbc3c3500210068c8feceebed772d4e981c2da01b67a1b1a3e9ea5038441dcf6397b13afa7492aad71089f78d4ca5e6c733bcbe752337bc3fe217a6442f15bd5860f41d0f650c1a6fa5763303869630f90efd994081b7f2cf4db1bdef699efc5df92588e82e9220d4a52840fd6bdda674f8a9a993f867a5d522c58f4e2b076d5a6373a5926926cc4d7a65f8cdb7fe928209933d743d3315e3790ef47b9477877f1b17de0c3d8064598e164f7591d955a26350f4048560779dc014a9fca22b7181264486b848e42b1449a3f7581cdcb48857c483dd0a80d4d01bbced3c2ab4226afc947d6f00a9b8b9b1f82c56087da870e22aed3852b245adc2794047f18a0893f9c7ff8e08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e8dbbd3deb4237fdff02cb1c0a054b42073baf02c3c2461378583db5e4dc283a","proof":"ced00648b3f1c0afef58e2f8b7dab0b2cb56f9b52626a2c18e0f6a4ff263c61bf4bcdacc72d82beeabb3575ec04acf3dbe134f7f6f06a17859ae8c40bdb4950e62c8f399d5ab659a35abecb955d55cff27327c817e901100b70d5e28792ff81c76fad37288372b7e9678ddc06e95e60d95e43c030abb1842be239a45ecff6e53f847419e9f6772db89d9357091c784deba34fa2ec18a6168425af5bc2efdf2064bd5eee5136900d13bccd708af8f48c346c1fd67977e643dff83f93591ec790e65ba8e9c89abedb576084610360bf94bbaec8ca8d8ad6739644e624b0592d403d6ad02cf2fe8fa61ee5dcb684e26c1a5184d5cededa7d1c7bd83f220ea878d00a21839851ad2ca17280c59889417815c5c98656b62895a426b06fdc3365bbf655e331c6ef87372da81ee4dde4384a6e146b76d15a46436a752df5f0a86f1db7e6c676115cf1a1829d111a155b8d1454948c5cf322743b37c82d0325194d97e02c67e95bd2baa06b1a10008aa60c3d9ec36b6b805a223b1c3ce7e25ff172a4b6a38a45a95935d8fc1a2c2b713fec2b98054c3fd96a6159738dc9f75d9a1e6e362a2dc66ec4bb1b62bf944b54ec1549e7985cc317c522de6e06e891b515433255a7a6b2e33916d1aed25903885d6a2b1c039539cc251f7908b027237649b32971c20cce28e2b96751d0b614b7c9c06d611fe195828d0d6e81e74eebce7c595f93d80bd8a7bf81608db489dff41dbc946483b53b3e1098c3f7c0d55685b8a885452baf1aafb33d4b53a7d33481a853b13997f75c4dc59e565fad8d82a6bca7a9405b8c697b153c3d91381d5f41d54c118891eb74d3963d6ae7e37380626e22aba37634a27eb4a567f34fd13a72a00fa5447d2db7bb7281c4dde35c498e3729e640b1ccbc94493f9089f293c888161496940748508a317c057b7ad395d124625680e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4a1230d86a1a66931f8edacd5cf13181be511a9600e75da564a310a3c9edc043","proof":"9e2e376f7a1f315c1a92acd010960631b4e6ececfd76695323e5c4e82f36ab5256e45e5a1e6d82fed8b8e393fecc2b8746fec62ee5ece16d33bc38e68449997bd2f2b6b2350b94d6f8ee6669a7b9aa476dd1ddee25b816ae71f63f6b0c3c60678a8a7552150899734eb402f8cbd3c7b4c5275aec8f25df2a63a6c3621892574dc55804b29c47ec3852dcc8406153670c25b4687a136bcfea3c93278be4f09b0db5560b8778055517982f676051fd2b30646ab361acf79ab5d186c1f3076c4f0e973f2fd1a9761cd079a876b30903ffc5840eb347184effd351e5cb0869891a04d6115cb26e350ebe5bb1423ad5d2a755993b0eccc31ef6d73d821795444e9d631cdeaeff92735b9b94cafa7ade12674bf0075af329cb656f00b96635dc3c931fc0b8d9632baeac8f846bf82a071f6739db93f6a407b1011857ce574fa56e9e1d2628a2e9191d4c012b8d1bce31dbaf7bccfd6800bac1df54ac95c35f27c5aa02e6bdc03ab93d6a49dccd6546b48710f0d78e11071ee25564de87078196faed303c38af2b073a7a4e81e5880ce9d6f72c3b9ff3a66c8fc4f444040ea97a4d9e0f582d959c92d63465f9bb4981123c128959b277a8368db866e4ba270fddaf4730a8cf249148bab3856cde2efe0708e4ab84005e70ac1fe9c428dd9dd72f790817963a99206da7f64ead0bda3ddeed7742bc721237051dec2664991bafc5a5b372be63ad72b05026386da12ed1555ba6011827399125a089196a69cbdd65538370d643d5eeb3b7870ce87737eb6cdee04ecfbaba4cf8e3f5e57011019c1c556a74780aff1dfbe08d6bc7bc866daac04bc4f62c468bdefcd5ffb8dd15fb4568a774ad661845adb67c809f5d82fb518c8129c233ac3a22023199e6d83d2ab8e9d50a8b1959cce5efa3d81de3e882a85e5e297fde871b3be7e58220726289c1f1c103"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"56d181ef3cd1b3b24d67775c58dcd89ce8177518ed8227fa0162b3aea0f98a49","proof":"5c2c4e2278765ca2c68e9614100b4bea7f7b6aa1e03fc7ab44864007ad0b681598a0a1f8b1cb130d1da76eda680d7d96edcc52105a1f795025792d6bee66c601aeda66716f98041e0e7d8e1a43b6b9a28d255abdaa013238c720c8aaa014b42b24b07e88c91ff9853f76c08821263d23d28e20303a260a4709eaa7f528bb2228d5815d84912ddbe1f0d9f5c199b4bcad94e326c56b10dab55228e6da6c279a0d33019812adf83db367b81688945ab904ad85cac24d67c535fb52660b70c56b023c51f9c6f466c3f27207c71363a406a7395729b03c9e412fa95416f1d69efe0f605b5af002608b0b2587ce37c9ce4870fb5bca42f0a1d8974fcacddc951358455425fadfefe73e7d4f09aa6fda9dd0d41a30f1865491aeb75deeb1f624a5222efc43da7d30d7550034adbf3ca20a7526fb4d3b489971a1c7ae856dce58291b6802a36eeee85896c340765e6aea947ec57a94d2ca8c67642a0bb6dbb98cafac013e32f200d16ebb8e6ac57c8da4c0eff8152723aa3c0b1219f9f945ece22a7211e0529fd325faa02810cc3c87bd5023b2288f1886554e299957a13cce5606501d7eabe621edc2be1a37e7a523c7bd339522172130c15b3f450fd1f5cbfdf5414be48a83753963c0df4d6fa21e5c44e6b235f7e3dbfeb5df394e2fd92d95a29d335e9611717ff93d929a5cfce64f269633dfd7aff88bf22a15699fbee65bb0634f02decc45d8f62faba7aa32f8627bcba9af20a57792326ac45a844ed074a33752881906856cc86f657c5452b69b89822a4c26f19e0a38f026f81adf8369b6272f6405ccdb069b1c02654a4272aeb344cdc793207448fa73ceaa92484ecd676f08b53a0e4a1d5bd64580178766b0948b4937165cb5048efaeee01f1d81a1f1d60463d2675d0d54250791934828b88ba3e068f708142df541793ecedf456d5fa707"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"448e62f8ed407b82fc817e067bd060a3282a6055f851ad9dddc06bf29185b33e","proof":"940d36e0acd8c749bf313b87cbb87d2de4017814c0325e41e26a0363fdc08565828c87d730031ad73a10cd4cbf3a35f2fb836bad4a30abd62fd8084982531d4f20cebdc6421ad67128f35f866f36144ee3293b5ec593e18d9794e9851f72536f7ec34b0b3a7b52fdbf12ccef8ec412df1d96f81f80c277d75eb94b1d5f48fc08d1827da72f572d8d543866afdcc31faef3ad7250b5890284038135242fb301081991b846c670f93911b6a98f4633edd63a9577ff9f87f55e643e8a1ce31ee005234423dc8dc710ec8963d371d5484f779bdfa144deb74f3b4f559133bf00d006ca0871955f16aa0288bac7bba10dd80b3514874848ab452f5ca45c2726576c04b6e360070c37ce9eb7bb21ee4da52dd1836cf0a42c955f1fbfcb0880173dcb69c6769d93b60ab2910e18b7200a59cc44d67dbc6448fc3e37a2d7f2bf9d382210c4776f2f7d54883dfd12100960d518232fb735ff0481cb6c6e9b8cd9bc98765cf8235a20ee9e256d2934e5e1c937573fc5e78246328251c20738cbbe90ed691fee0ee92fc1d9a9d2ead6b823366302a7f7b80fa295b41bbae3c82c4e5ba77022e667ee00eb5408113c9d4fdeb9a7fa1ab23b684f7df4e0c1787c5bbee9ce1d3cf6c7afe0fa134aa54f1db00fdf13cd706452ba1acb5983364a738477f0a13d750262d18c7e14520a8458cdf2425eaa8e39c802f0b412e7890823d8260b4e98116c6c5f6a9e48fee4b82d5078d5027027007abe0bcbb138e4ac789d9f5f962453bc0b3646af73651f2d65cf4a518c40f5399ba3673d4023d11b73495664769839fcfacdf359e25226c488b759462960b16fe421c1f492c9d28b8be5552f34206bbaa89b7c3eb549f8b87c3aa65a976a37835b3c4ad3cddf6435c4d415bc868b0c0c31f12a44e811e28725b71c4fbdec2fc1afc2e88cd2d753e713e8681e74b30e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7c7152ed252d1342ed0593b8c0844f76b647c127d1e234c67929ac5b68294e07","proof":"4ec0eb30fdaf5c2cfe1b198782a4f21515be9b17c56a6971eff688eecedeb65e38c4952d7e72e25f92ac560725bbef9bc4991acccd358ebc8c34b5e8f1cb27152e08b6f9623592144c831ee53c2b9b2d007527aeda3f9195c6898e2fb490270d781b6cad63eda9b3d377014ac91043204cf9970fdf79bf1a8c7e9ec1179a9742934120ada35495062e0bcec090515b6ebecdf225671764c73e2975e489e5c70e2afd0a243b72b525e086dd932626d1e8fb82cf5d2281cbda0d868a39c622ec0cd3992563fb29ff1a48f2c61162db29674401f88537cdd5273372c07351766806126c2893201d6ea402a335c484056ecfefef3632372a9ec3faaa6219c5fd4c52dc9369053ef968636f540a86a8bde328f8a93b273930e62c926b87a9fd0a8d0d722065aabe088234d767540fe06457d0eda06c148de4af1fdb4f772bfa8ba81a0ec248bb6e4575fbeefc007eef3c2595fa521810e06b5668767a3f3eb01c5418f6b0385fc2db148be7b1fd378a61f87edf7510b97dfb6d02a7dadbac055f2477b2c1d6159d6cbb525f1e24915a8748c56a2c3e29b5af99a61f8194ea99cb2a1ad87b41bb52fca604a65a8ea9127204dbea92aa50069c9ee5b827269fd510e873f425f5cb38f34a43c7e3c2c54ebf22be9c13d3dff8f70db0dea06675ed25a918a8a22375dd771e9a6fa823c229d9802d7737db9702d812eb7f09cab0d707646d6ae3cf4d67a9f337fcaaff81b20cdc2459b1de230452f7f14dc95615c2931d332e09ce7cfb4b1f7253e5c6d05fb25b7fd24a326dcda73a9621ae3165192ec039d0e9a5240afef21113ecfd613a74b5c8a00aa6b3110cce3dea7dd8de0b03882cb78225348c84c00ecb29a5e9199101d5e6a2f37b30fe29acd2f6a5534f389a04e7735e829086239355bdde3a953f6c0b624fca33942297194f847487c3d56000"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8cec5fe4e74648d24770455112fcbf90b85187176a0b1ba25b7fd31500373e06","proof":"46c3c1be351a94ce1f4b91c01b4ff23afb38e596de242be93989140512fcd67c589a1f556309ca4c3fc59e568c73f2436ee0fefdb29f85f5b9b7554caf16393c60f18856916cf95c0b1d344bd9ccb21f133ba73ae118fbcb7a9a38da85bb7d114ae45e14de72a32eb5c12cdc267d0ae0bf5d63b735bd1194dd5ae47fd3f5e242e74ebe04056c6978b89177e1fe79ed141637b46f55bf87fdf6121983be31df09f2434d0e671c5781d90819cbed1bc9bf6b4e8c18642fccef9c56958e12bace0d820ab4c5de3d641b028e5c7d078db8b1c54cb5892538998e5b6ec6c9fbea1e030af206d36fa0095cc8795581e22f9cc814b52102a28b3d2d203748ee676e8d102080572003917bde8a9d5869a33f7a31ec6ad8787224cb6a9a6f33126f2f45036688b9ef7ee0eb2d03fd27849cb333cd5a198a040d73901b8a54ba4010fa2e23defe13c6efc8b73d9767505b6c19e3f40a15fe561a6ebd9b575bbaebd90a6a2ec4d8e386caaf6c360faa0480031f374fd4bd5f38a1cab67087445b624cabb679e80965074c32bf7807ce26c5eedb19d7068278c836d9067c55ff007034564c0b7878d69f949853b023fc6e0e37a9ad4cd3715369bf8883ba71a148cddcdbd66d28cf057eaab55062487aab90683a9878d053f4910553cfa8930d1db7117f281f14c1284d7e1fa7c81882ba1d0dd6c172b881de8ed0ba51d583d670556278172f34e4204f383dfbffc802dcf834c17f7b9e073742014857d4b396fd5fc86fd90bda089b3a38eaa2e5f520a4fea8f024e7b347c0a0dd858549fe5b0a040d7e1d0d1e22dc058bb180943a506fe7dfdaef64f72e9719a0ee7330a64f369ff4752329bbca4c83197374ca41dea753891dc67f0a2edd42e772bb68c15050a726653f0612de8f5b4c46b47c987f83b2eddca5faa9ec9a9408ee17bfefd4c35089eef20a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"842ec4e205ad74679ff7f58240ceed65ba185f486857b4a0e15d476fbc8f9d14","proof":"d214c17cf802a71f64542394b332c874813d2977840d96d5ea19d6ae82d03530062c0c5f6b931b6e49eda18395f075590e37e27a8e9ebf5ca05174445c8f6169d2071a276981f0909d51d37705b30e7aee07dd7e9fba8f1609ebd103b936774d92254070c3f541f686a748646ccc99c52c89a2d7c88aa625ad4e691fc43e86415c3f606c230c0d64657fe9da1b5d0194801e19d2b178ef4e31c5b4a776a0cd07396aa59dddd89f85af3b10f353eb01c2b37f6c1d83f3cea1b44be0f6cb90fe0166fecd99a6879e5f3f9a4df396f5526bc0db821760e50869b7f87411c29eb60f14192b229e7151749252664cfd9d68ea261e213d95bc0d8b889629855f3a1a0ef0479ffe10ec1b05dd215e035ac2cc146952a844b6388761950c9f9035328d3cdaea68b9cc77ad9219970649d03ac3fe35c11af56d3f9645a8ef394426d79727f220cfbe37b9c9d68aaebd52840fd9f6cdd4bc7721f429d981d8dda6e5a21d103ae44f44e3882306de0d3c57a668859de41ab2b88400f501ab5e6e75034ae6692cdf4695a05edbaacc0b883f8686387b71ce813f701d6f145dd2c46dd3799f3590393e0e891517dddd4852d1bb36bfcb7db30f2f116ee4aca830ad3ccb87c6262450ee14e803ac96e7ce31a68db18985f2eda9eb67aab85b4835f9897cbc7d64b64a09d22086d8a49a3a8f841e17f96df35a2a9ed1b3bfed1452d408abd0660530d27d233141c3b9abbdc2600e1966f8f2209461b4d395f69b9c57c23d266e075e71832819c33d5602feef1c1777bf99bfa784232c1168a400f5ff1c59f17e344e087526531892ea61ba42e6d426f2e138d6a4605d5f62cdd2167bc3fdb8c607a8ebcaef95717b2149f45f5a19261c4a4a77f767a573c9db37abc2da71a21e052c5cab59b32830588550256dc72381e293ec117be16ecaec6772938be827b305"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b20dbcea915e11134cd97d8efcaa6fd920ff2d9e87e8d2baeeea6336ad92a860","proof":"8edc6970f2f70c130cc0426b2b6261f2be672750536d2165061120a2f9c4500110f08ba4a0158891939b38c6a5cddb9d9ff27c354a3825a0f314de03a87fc05fccf9d7385226be2815fca37ad1ddf19a50e93cbc8103bfda5e13da40d45f587792c8496d10283d9cc20c4ee9ecf060f1de2a709cb361b670c0c4eeca0f35f33a110cc29309ec243ef613a99671d733a5f471fc7cd01e06f716844017eaa8b40e8c567cfd29fcf4ede0bf1cf3558dde6682dcdc43fc47762caff4903f2b9a000b22be5f174f403846a4ab2f7b765ceca421d213623e72efdf17d5edaace2b1f000a1767d7eb3afc7ed208dca2a345bdbbabfb61d350b3ffd9a38afc7f26fbd6642ea3e0e594b75c6198e1dae1c6cd3f0bdff57ff9b627eb05468730835ed9f0489cd4a9d50f28e1f7eb283cf32fe44af2b7b554c1b65c4df3c7e7468c0e19a8354e28f0b3801d5ce8bcbea38cc4e49f56cc2582ad429d848923a87e5f89714a66560ef1995770c1746974d9adb8014626e22be820a4ed79678f21a8c54625257f7c6a2f1028571ab773d710e830944eec59a81da90bbc40e9af265e94e490b414b24ea795004f1de9cc266d5ee639c765aaab2bfbc57e3e618bbb46700520d350a2905f6099cbad7212817da78af1e2e5d638b9afe13fb12ab2dbf1688d39b079a2c597122b60d7078ff96bc955abe5daa397f6215da337dc4c6a050d9f011a10468faec7eb46b72464ef9664759783f0b7af96fe87db38e5e8faab170751174b100353eed3c1e18b0d2979b9e32125b4b3db01c33dcf8a9f5808186f4758e84a5880b66a67bf07184d4589eb77d00b10ffb37477bcbf1f0e43fbc87446e2120ce890656a2e99b11e457228d9d4adb42df99b8a624a138fee51eb970d3ee11c030acbc9efd9e985e494ed34ef89a472692f397a119b173bc0afe31f9550667a0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"502b80717c198122d1ea8e1704bc0142dd2a3bc80ba47a366a225528dfecdb08","proof":"2e1affbaee3b041b57ed9bb2016ada8c4b7cca6711f20c3605bc92b370b6ca5a3a4508ceecbe961577badf040298e03df7bd3c24c5e942b85fdce9385efc0e39d650816178d2dbb2e5d07a71bdde0ae2158b9f826166ce4ec0a66a23958ee23912d70085a4739890fb1d74e8c4af0be11335e9fd2eaa7ac2c584dedacede68317e3c50c30de9cb1e6651af10be3f147c0413c8e9bba4b38d86a31632115f0503300d8197dbccc56b5a3e34822b49bdc24c1f5df9fe74a6db9bbec33ce035e706376f6299e6837d0f57a6e44c9b1f8ff096ba19267b4e97d0074138f49b40c104e023bf5d195c19789458e9f3538cf9e7c90ae8486c8fe474489b63d007a1ff3cf209d14a53986216dce98edad7582a14a168c8e2fb8fae798372cf52f72c7a7746003c99c4728624a5555a64e79773673752038aae77865b84be8974b22a43629c9a9d433ce050bb6b3fc7b29b87a15ced6d3d4ec43fadfb9e3c7fef59ae5b0526ad76bbf1092302075306a5fce4e687c96ea8501e8e8ba49e31f3acad939d6b44ee7f1c49e23329c8935b6d2cb7542da7034de7924ec1fdcb35cde610be422d628781324a0c5c3ae9e31db9096f1e1c44e796745365e65ce80f6b1a39671b3a6644eae7c5b165dfcc00a7fc830d18589ab37aa689c8c20d75e7f95689427557e871e7ffe432d65eba24c0e751008830030c769324db864309719264b23f5111d40758462d55ba56a2e861964a39d63747f6f1b0a450e063fa251de317784f732096f2ffcfe8dd9f6d90ad7016f51f1ce5fdf0b0588733dd531502ccd6963c4e70f90a5933a116ec1531843a7e519e82b3f2cd24b74ba4a8f65feb5318a7502a73419a63a93ebdeee0ba79e220c685732cd65d9db8603097af7e77b22e65e40bc307669362607a8e360bcde2a3268e2052a7d2442025de094a2ecf4bedf6cc04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"929a870762c59f943b4147061c6b24f71b9c3625b8466c8190e4cf275b91164d","proof":"12051eb6344fe56436ead5dbbe0837e5d31c7942e41c42c12cc49cb009a717316e2dafee617472b76087eaad98cc04c6a46155a065162aec48bae5a6bf7b5c27d0b3a009603354c401c7af59f29bc7f6493395ac9237f5628f34919aa18e3379c2cb23149ee09a42e5d6b153fb8245b3cd0f9c4e695b37b055c0d078e658f9704f18f5bda8478eb063f507649f6097b340e2c203ce0811dbc083f34c2661a10db472fdd29654ba9c91e2b22fbddcabd731644b42ad266ac1ccdb3565ee8c7502efc7ada829c6b27fc5ea18e0890d396f80978d90ac04d84dd56a162a4df9d8029277e136ac80141e4340b1b6c367c9998dab63d415a4f788734b10549afb6038f445a66450a81d6f8ec00932b1363b0cd3f367d271f6d8b7973906bbae2c6f43c0386f07133d96555b0b71bf65ec04a761add2e7f1d0e4acb346df4731fee406d01102b593c7cee292820072541497c91c16e8d0d89b4a433b6979339debbf01628bc5911dfaff3774e5db6ee085cc62a5f8ecb93e18b8c44eec7dd19ccfdd79ae11301eeacc92426967587003d85616002e958351a8e476158001d7c406a46636964218a711122347160e2822e4ed1c77dd515465c2fb02fff68941e4579862f46500bfa3c75865dcff08aa728adc233bd0071b00160fdc57f40566db8d361b1a885dbdeac5729d153422f837a0523bd36b6d067115ed1f23ddf478f78e09006eb289ef57330578ed748645be101886889d02ead5673e436fc993cd85761312dc10ce4d431cb71715160e8ac0e3c05c241e6fc56b6c88dd7d705cc9f3f2bc0fca7fbf9da948437ba9942679e47de5645dccdb9558ab2d23f28fa60a6f86c762c1a3cd4550f06d56874b0b9aedb50f12528414ce11e108ee7bbb678fae1d320abb7ebb51958bec2a09ff7bf41aef22ddaf71c89d3a87afcfb5f75f5b1fb34204"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f289a8b82db7d3190556344520c9f0577186f0c5f943ac8561cfdf410865817b","proof":"369d940beee58e11cd0d851c95a791a57ad4d2fc17a5d8987bb4e4faf4cdab3da0118be22bada6e32ba9ba480634de8f24092c6ff4f197865735422e025cda49c0cbfbb35b55d4651de5e7b77b883fe2d00540e473419e904fbff68eabbaf55e1a4e81eb21fcf901ba3efe13c5f5f9473f04f15e6e88ea8582f68df72785555e18010f90c1d34b39c9348a235d9225068eb1276a3d7be311e2955f1efeb55c0192ffdb08889847ab416d77ee1f6c5d5822ba7b548485c2319dee42233ce2620ec1610b27c57bcdba05c14ab6409c55924be1cff236b63f6900fbab67caed3b0ea6c0adad11aa83603b67ab8de10075609a0365cb98c03a569d598e2d5d33d229cc2296d0d9e97b5f554bc08bcfa65a8b299e8f69d2bf929daed90c0add14fe77f2cc43e917e3520c520ad7635c3293c8b914ca48e06adff235b23dd538b8570680fb3d6d3734c26067b020507147ae2c1d614ac870ee485a98df506b510d0b7b08abf09b6845bf73b2d6ad58160fee425445834569b33440d94c02aeff09190c56706c9a23c44bcbfc9a8c73bb59ea311d04fb3d131f8dfe7b6bccdcbddefc14aea71a084d1f6cfd3a6e6598e2b53451a1ed987865fa675c9feeaed0477eda506e8c1415a400f932af2a52ae94c43440c10b3867e72b7ad9e7aec043f1657c6bd6ce0886e4b8fb873d70979e6bd488869fc5dbf5aaa3cba3cce18368002bd94da0d6cff2cbafe33e2118ada386683624f2054cd743141feb247facc73adfb25b0804518dd8fe12fcb56d1ffa899ccf44f1ceecdef9691dcdd5f7cf929fad4d52bc5b30dfb990406182d33cb6a2a62761bd241ae1415b581df5ebfcf53e028f2ea482fb3f91ee5688eb3a7f4ad474be717ba0209d0673da742ea82c8d82b9320ac15aa02f76bd4e20232e9ba67cf251991d2349e0664377ad7d7f67db3c786000"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3c4733cca3259111da641450c8ffc82e449b10f0c868363bcf615564084a4024","proof":"fe97478944e7aeb69b977f04d9a6153c25d964468eb50b5d4ed9bd628055161a102d96e2407d5478dc2297ab43888f57941dbbe36069dbed44c707f1f00e33067cca711d7e629bacae1275dd0e41423ef1e76ea9b7b5c31cd21c71e370da8675dad979a77bf28500e9b44c2f9e5f8a852d8dab33860dbdd727ba637e0525db59dd8156dfc514c2f1349f2a94a96ada9b20fdca4ce6ddca27f11f437a55d4ed03f2ff84fed032e2add81eb62efcb84af99949e3702b3963dacd9618a412a3ec0c71e846d1d9bd5a4e927f16a9a18d954e6b9194c3552a5209c6638ded64096608700ab3fe5a673527aebb94e094ee258b75b38bd64ab5fd4c13fdc2a4945393099044b1f04fed076c8bc97332668af5befaaa338046296f4fcdffcf8e6f23f0157027ba9ed0050a6b6f9c34f23bafadad6788c2efd230beed44e829ae734524672416ba57e9080e9a5a8e7205d479e3f315ca0b37ef4d238f898620e8ec16391f422970fdfc4f0b585df5b5416ae174c36da748b517e5e779ec0266acfb21b57230fb27084d728f75faf433322fee8753d854e4d8a97552326dc72249a1f2221cbaea7e64f811ce34c9cb59573ff966cde1c0e2ca6078dfede50f821cd9ee2260542659ad0398b678056f1a564544a01b34dd1c4c271390f9f0aea6b8fd362f14529a033e12c4b47a2a7927320ee0afc13c5c30e921560c13f9a4a63dfab7b47ce62d7622fe3bfcdcf6e1acada0cc4bb0831850e66486f7bee0dd7e177e124569282b5ed2473560a11c44441b1dd93c56e1edcbccbfa25ef7c8384a85dcdcb2143ed8da19d82b1699d2c11f7a4cbdde684f36e3b40e33bb97da70eea33ef46a006aadb20adb2efea057312aa4323988ab1b5a5b037146523143e2c9ac8ee7a20091f74bd7fd659322761f819fdb55a00a3d891908d14d726f99373c761310c204"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fa581debe053df5af04163e5027708d02d54c389d0a66aa55d5bf0983a829c76","proof":"bc98e3aaff89faa1748493b7032d6999b5e1709ed35e21ed51e684294239c64f6e1296db6721ec19f9e1224bb47e5a4c5ded97afd987948978f3dc4afd310c44421e7b28feaba8313fa6f0352517156c24e603e926d9fdc37f2eecd6dcf9fd74fa8df1bc63fdc879260891403f2e3bc3a473d9e193e4d287bd18077f6c0b66075fbbf550e140a37b938533035acbb1cef913d77e4e140cf9e8da2082dd157e08c6ddf87c91d9c8382d8f37c77a7f9c4d43f66b8769a0be1e617f7c0331bea60afac8ceaaaee973fbf46ab770dd7eeb636d1d74a5c16204decafbdf0cd41f35049ed213c49ff08ae032276d36851a9d1df6ed4aa7363d67e426316011d78a0e1a6c6d4dc236560ef88a66ca064da4f22d4c36660b3a97f54969e0e8df5608cd014a20d3d13cf5c7d73cecfe465ac7551bf8fe08e21a803a876e422a0600ef8e38ae3a1515f747c647e73346b812c6ae7cb7c351af4a6ad09a96799b43ad796312784cd46235007fb755ad32997a73116e3c62d14a5b2f8d093ef6e4410291980ef2bc996e1e94d6376cf913ec073d93d950a924a63f1f7f9085c7cf6b99a7b6065efb9134531b5a475f4e5823f83dfe31b648490a3bd1f2491fac524fe02a7f592e0c99a4ecace6b131caa27051bfa3ab0dd375d9480a32c46aed89f82f54d478b62a83253def1a6d171da5143d2a19fccea03577c1b5b755db320c7e546c287380f4275b35d04f2dd0b602a543df816f262b95706dab9c388e0ac7142576892afab06f1b8f3405c776a97d6d36644fb1d4bc71ab010cf5b35f698fa913c29165f6ded6fe5c542b831a59efeb37fb4fc3a1dd8084d9674cc0cee6ca357803de068a1ed7f6920c92171cbd03d59c10e437343a7a2c500f5ea7684faaa4f98e5003a566139c1fa5ba8706042b20aeece76dd5e3f16d176a09fa5cba9a1acb63b60a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"44c64c9117d46c0e8a5c641ce3a4e2c985ad17e8cd43d81401fc59e0f31a4746","proof":"00f80ac4fecb52f65707fe2541e7cc57a8d571e7c2ef3f95baeeac30a28aa9146e8982106ef72916c68d2863d5a14dc85cd3fd7f5583ffd461cd56a178a8ee2cc4368bef8669e0ce8befef8f10e0413a45a948b507d031d5a9167b36b3d9a1281450eaa2a8fd0a0d21e9f0f3f664c254f3233a9dc7e6cd6f0c4f6e480acf101c3e8e180d4a8f223530a709e06907de325d2a6636560a9b4f47baace6857b34044921d825d737b864365f789571f06e226cdf95fc4a88b0a357a1d01ff7fd1f0a4828f340bc8c83d52e57b0e99073deb32edb445132c37fb55676671c0fd17f0ec41c91a0cdb96d96747879a7b791a47aac114a3b6252ee070498da59fa4d4704342b2b58bd16b27ec09773774fd60013f2bc48b2ecf83c0569061af9c40d7176c2228aecb027ebbdcddb4bb11483f64cc4c17d77cf8323d661f239493f3f715cb4fa3ef9f193917017ad39f8a5b28eccaa30035bc11d3b5cd964f51689c69d6bb47ec09eaf3b910625ccb5effb27977b4046a7284488b0d10bccf4094e1800497c9bbd093b307d93033f574f3e04c6c40c613526509c6adf2159338bb09dc37d8accdc8b2c5f10ab97267631d30f36ebafc6fe116732f9262b07ca1c66b31623fac5bd45f31775f0d709041c056b672f8f0418b080edb60d91ec690b7943bc5d060a4bb87426879160a376156c326b6bd6d107bd97a20d8949c6d5b10e33fa3298c521b0986a5da8050ce882491aadd5bcdc11f12c2e3c5f9ed34320fbd3095e0a2fe04c8c47bf04e5505d6056977ac9fb319f09545884006fa7dfb824748012fc4cd54799ae5aff124b7bffdfc3f63e2b7d228eed29a8873f073d60672d5b3c8f2198729e4fba96fd64e174d838451ba63924c210051ab43c9cf9abb6759a084f1e41a582c61fc488fcb8351356c410a7e24a72ffca3df6aacd087eaa43fd0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"42703432f249ec57f70cb66823716a34a6990e5ac067c75f2f19209b06273c6a","proof":"405ab91257364101323b7d14f6297c580fcbe1983afa99139a3843d66da1b152e2d580b9a4eb0f5cbfa7f00821a8225ba5fc3bce4926128900741b2ae52c426f6aca1e60a6cffd50527456e99642569acd871e7ff827cdf728bb88730d3ee120881d4a9a6dd85f73e1b39341acc72c672f946ac30a51d6c0bcbe35cf38c3fd6b38662c2dde70cc4ed8d8deef2fb47463f11524fe04327ea90ccdb0f61cc8dc077734195c3d2ea819b2931fd35ba951d89bb45c9d397f921a098b46a3d9145c00e1e4995715b98039873c471789698f9c18c8735ef5340bcab1b71abc07fa3401042aeb17e7ce748ed0a67a5f462aa9636fcc971f95881fd10e8751f8b5725a008839bd103ab5ca9f50ca93e2dd9a19d79ebc24105fad3b2ce1274a592aceb048e09e1add763d0d46b689a054d33f0a368bb36fb8148752f7d0341ebf6131d148e27802be61396ad7c3f891f44d3ba4415701e8e462cac82a7ed4668b2e091a44484155a69fe966a94b890e19c7dd386b05befb584f25dad7a69ff1f79e0e482a1e30b060f5dde3e8fa00c304a2a13875bb9d17ce10778b1450d90b8a80af11467a35c1f6e1f4f70085c71ccf3b8d085c7b132bf61cf79ef4506778f63ad0d71b3e19c09e8bc81d2640045438ad71e11e212ad4f5842d8ac5d671db2f26a9b577c479ac060fee35d88034bb8f65ab8a73ca22fc9161b418ce3d835fdcaf497d7686061a2518cb0a3106212634507d612b490c8a0c0d62d40f4ff8eb0260ef2965a6392e54ebab668446324c0ee9cf4d06ca479a0564a3d931782a6bb100b7df376086bd81449fc56e80ef2598334692e6ca79c2f893676151ebbcc1fcaf7c7b7adac6335b92f5744bc83313cddf86025d44e4cdcea681c7efb15ceb95141e790bcc1a18d19f9893fdd69a16ed255fbcb9fcd7e0f93b8ec450c308b02359ac8e0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c095805ef82a3e639c7a90dff476859aa8c730bd787e971909cd5a7a5b1d6e6a","proof":"a01291fb298cf2b66b8eb8a8282a74b5f14233e48f8fabc8a1f590261baf292de83c39a12e4401c3c843621d889128a8d2cda58c140c43a98e9f28629afee809d4c5384512036e28bff5790b33b5333294dd8ba64ceb4f01e3936b2ef3f3ca5cc46b0a837c93e09691e5f0e8dc05657e91e317b361fb3f2f6ffc3276f937ec67829ac7119fee4df5d67c6d38c78f93f4383405e9f6d822fc08c93cbaa4faaa04081e3077647b0778454bf828762d3daefb0d5be0b3f15694f2d0198231bc2b0353978dfd7394eedc81ba6201840e032c4655cbea5b735c0347c47a01406c5d01883e95e6244aea205eb3b7220ba24a5f724e61a4b3bc0204da5060a7d5339b15eeaa78b5d7a276f66b72caefafbab06e0faeae42b64d851975bfb14971f45933fad379208dac1e875bf01bad162e27b4d40392ed0da39e42ddfdcf3c61f6db59e852216951564e654d8bb50751f407aa06463105749573e974f064a626a7634b44be0f37b2433228caca11ebb8ed08843c3a93b03b84a8185e0090852c0873068c76b16a4fa79a277f7c8da87e2b67caac3e956c5dce643cb27831c5fcd7d617b2eef73d04e79a03876bcfbc03f14357d50c6ffc4e3f8b441006c3a7afcec674fc03e39e4831c8241a30e2beb68f43b176483f40d58f1d36e389a37ff734104120797bfc49dcfbb807c2f919d2e78c3abd19969a883a588f907c5965c613ca3c5026db57af167be7970c4438e81714a1db391a785672d192f7c7e0d8121adc39e44322b14fb4abd77fd337406146352914cceb28a47af569f4c3f73218b56839ee0cabe76b890652f0620db8982c624d9c777f9c658039f6b095f9dd5db2f9513a17e16b27be2078b54500035623eeae9e26876bc98abbb531b6435acd11c00df80779b12f2afda4599e3e547b56f96130cd056b976f393a99d0b172f1b60c0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4a656fbdc1237ab080130c9ca586e430cdecc2c26c0de387986648e0f4b05402","proof":"74cf20371e2e32c1e8a1bbc4036906298108345a85e02fca75574bb83432ba201c21be3671fa5f49783449c5c2d49de90382f828dd68e27ee7bfcd2e6bd4b62e2aa89513ab9abd8e5f53bd2f3c62d6a855838510c339d60da74cca9eadebe530489c8f247c338f0323aaef7089226e1c406e35465e38f2f3127e3e4780a485235ff47b32789e313455e3a81c400d7c268ee6c1704397425edf6942315a35bb03c84925f717a03299dcab3b91b84eb3f7df4d92007eee9f1045cf2f6633e54909876f274d0614c66d935d3e68b87c87cb9d1fb25731fa989c6f16812ecab9460798fd2cd3e3e8b23b168f85cc28fcf02ab970df42c1e68d94d79b95a805505b7012dba3125e5ef3435b1be29aee30fc0ec95a353778d66336b9274de4e630325da04a5df241c06b534f2fbada9a682f8d83b805576d50dc6eb1e7ee9c45e8b663d6519c1a4a60a09d735c3d6bd2438f318885420f0b8f2669deceba51cddac3422e695a419d107327c3578b4cfbcec28e0f9819e92358993e560170e17895c6567898f1cebdb33171f8faab40bf62a58b011328812badeb29506de9b89a08760c42f41d9c8a01e5f81ac99a213e656cefc0b077a94373e0ca7b80dd6a6d5c6e01f82e867c047fadebd3b171d23c96c7a9e1e533154f11d3473a9e73da81ec9c67f61337f89c2b4f1ed822abd937756e0d104a41900eedb7a35d6241c98b502b51a0101ef6a8282dccf575fe54ca20cbfdf8b9b8c117ac778e42b475c880a04010a05a1d974eb8471c16106714ad468a8ff0add4d42e60a4d28eb0077ba7f5c82aaa11d11e80f57db0d9fde4999ec30f6ed94fc2f8db8f7bda135807366dd1c4656d0e2f905c18c0adc37022b8e875cb01add81d1075e72253970233618778ed0cfecefd13019c4bc1b4a107e8c6fa300d775aba30140323caa7fb0cb6cf693e0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"02a222d7931109e9904be3125c2265052f500c649fcc026021660a37c9797c12","proof":"067394f0b425087ba0d8fb4397d5f933d49c8a9c51d48c2edff3c4c63f796e2b7eb12cc6391cdd2df281238d32aad8b5aab2038528b6b6489aecc87132e2d3057c4e98cbab0b9e0ed1ce518c41d586aad9c046a877bdc04f0ff0adad5c780270ac8f102143bb918a0f1bc54a0c8e7724d83a7345a7d75da08fe1802df3aa853e4ede02c4f10cc34223c232f3a1ad3a4dc018d7322e212d12946fea8462808b0022731d5aba2a74ca62b2d54dde94e38bbfa221356c86ab575908fc895dfb91080dcbde5dd58306a9230566287069ecb9c9c7d74fb71a8ec10f5bc97a8ded5702f8835c911486bd02e5ad46220817a55059855bd0b74abbc2cb5ba75439f799658af721505440a21a52466f28373baaf914e0b679c983ffdf61eecb2f483c4914445040c4e248df94a3128d0d1f5c019c99c24c6cbf00babcdf1d7f247ba53602e84c619242caed814349531512f90297b34d148b6bb86326587552772558a71890952d0558f2a0afc89c448209ebf69d1c0fdf8d17eeb0750054019db075f105582c1571745a989dc66818a22508299ecc893d7fbe07ed363bfe344577b3826028fa9d9ab8d10985a9b0314c9bc2958952d192b91a3435438e7180c60bb0764af6cbfa6f1235beb0b6bffadfc712c9ee116e59cdb8b3ed1b396d22bdcc7eaa7cbe4b5fcff8274e5a15318333f094a50a61ba3a234de4e5fea421bf752af5860c4c5be242cd58eb6b1048d7e51fd81e98aa592140a5400a4299b9a2af3cb1811cf4239f94b2ac3371ad9a4b57671ededf633ef459165bcc6c25fcab71535f1240ec7a49352634e2b7b6b0e884488456aabf342e3096e7e9e96687bd616a8a296704bc194625c3de923e336366148145475dcb3dd6e5708196552772d6b9b1f1054ef7c886a42673cea9df914dfbd738a819ce9a40073bf833db0be6c969445505"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"84559aa388ba704f388712cf1c86478da19cd5bfaa1997f67d1b798238c3e46d","proof":"c8281021fa1656c4ddbca75384532868e564c7c3acd30471fcc177bb055d76208edc78398df2d815af1e83401e4ccce7ef0f0bd5368815b5316226b7fa90077446a7b8d4118b3f3d6c81dc6ab12a61b682139a0d946d5a6448c38a0ec751bb431606f9248ced073290953df2bc2d4b89b043db83523b7bae4702705a6364df01747a7ff2771c902363126dd0d414b38b3063fa7bfcc144491c693b3027fc3e078550c4664c5f9de9226589e395ce9ff153af8963d7410e4970ea1ee8349dc705da72a3d0e67034c1872233edac3da15b80c803d7a908f0a71ce5ea24af31e40c5c6fdb79cf68be6ec3ece6f7c1995ae91ec4785b6e90345d124512f73090a2204462417ba8c692828bc955d3273b6e259835580a45d70076b493c0b1f917f202fac7e7c923fb4cefd5e63ec292fe4f6c19372dbcb528a5b959f1bd3bd2dfba50b48d57ba225ed488eba32c1cee7a1d3d8bb6e871b7d3fe70ab2a5aabef7d7f495cf64d68d84541de85c1c9cb1d26b17394afd244f3bead54ed85cc88d10002682a8f2264b7724a385f96890ea59a73586eff2a0bb48a3c3e1862515fb4f2f04c8e929e95f6fa431f608c06c2d616159e5d8179cd83a2a4aa3e24f26e1a3b9f0322b9d27784603decbfa15c876528cc4c98d57d530a50d967c8691ccb3ee6dd73802df9890a4baf2478139fd6dc1d0199327fe894efa3271242e0c38042372b3e5a2f7e7dc7aaed472d789b54db6be2482477469445e8219d22c4b97b30649c346ee76090b0493cb02b456148c878cb2e0f7ce0a9437ec0219ce2c491bc152020a0d7c0a012128cc3f5cbd09c90cff64ac25a74c2de110af939ec86d05e8b5e2ed0e052955a970cf11b1ef6b032d194ae3da928b6796c59686d3eefce5d67bd0de6471c2c9c3cc938a6a90fed97bd42c0584cb7b40cb1a8b442b0b3e62f842e01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0aa558428f45c5f983f1b687c35bea64c80ac7beaa4f778e9fe4568d1aa68d52","proof":"a45359cc7b6b5d79ea645e92913220a523ef5a5a1bd535fe5d09313fea217c313eb714ab64851ead53bcd48f9879ecba3b197e54983f7e85dbfec0ddc2039962ecec52f4486eeca361710e56bec75237a4bcc0e95e913b7c82cc74fdb522072d84bbc47bf1c20a06e2c1ebf95f316d0c6e0764d7254238dfa15f74cde783b733b9b700b57fc8c668e95373ccfc7fd99a4eafe8e6d3c98c59979fa469d1c244046202f30064b0d695dc2a3e50c694b31efffd987c53735ede9993f14410dd1c042af9186c0aaa26afa35983d65be7036858097c431e09a02bb6bec020551664083c599fccca449c5bf7b6e486c5a9d96cd603a58e78281d63f0e880702cc580784eb8bfc8c4146a320dc29c7a41600e8a036e38e25934484553896a05895bc22174a91b8cf5245dbd0232314d4cd6312a5c48e22e605abed788c191c1e941612a6a002968210435043842f5ef05b95f4d8e2a9ffacf4c3724cef08ccdbd37eb09a0b002beed7e2997ac3481251b8bd16809a3c56e3fef22ff9a4520c7b8dc302b5eca52e1e7b199165ae4851017290d28ec10181263060874d7bb22d3cc2dab6bee641886c4712ddce48c6c3352ae2f23d96f100baa1fbd511f69ac6d29b28604d6e4d3371ee4c6e32ab5fd292d2d76502c9c5f2cc7f7e5e03454f861b998c24bc27b1bee7ea4d6bec10bb968e8c3444da4581cd59fea8d1b52f87822faf2416f126e3d28a6dcc7a5298ecd42c7a6c3624f6c33cfde97ae7eacc40add39bce46ac897ed772fda4eb52a5c81894675bc298443c2454a87c4ee31607a1b64126d0d7057e09f6dac810207c2ee64654d6516a78b167d790a36273b0b8f949ccc1406ecd1f513112840d54d4fedd183ea443a7326248c95e85e5a9d57d916ee699408750a94fa50df17dd0973987b655be492f69ffea002609c72a92e0d6817711804"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e89b2abdff8c32a306990959c7daf7dd6ad592e578c81ad8e1b258c1110c8973","proof":"d425ec198da30e013aec8f18d582d6b314efe34c3f98859ed68d3926add2f76dc0117cfe159bedeeb8266d6c961c630ed3f669b693a30a7be45630acd9f91b40e431c0b84df795fcde5eb3a719030db263adcd2b59c9a7676b85eaa8c4ab2e256cd12418da9b5874f715f81a400ad828d91756a144c8d0d48b5f41df9401607e6a76bf3c85e896198f99d0c7de2132f931ee6d78669cba8e17e2a475cb7b7b0c3443e342116a263a7b7f815dd76f225a4cd2409fb200c58cffe3f7c7e9d0c803aa1ac55dbbe852a1db8191b9fe4601373a24eb95db62688a54c652a98f4f800b66562541da866b68b09658636fa8884f63749de70a2e93a2ee3e1fd72234a8544a7cf252dd35ae4b30f497976274ca178b963bf7de8862651411227bf22ac32854b58617732c4a253aa32c3d82eef1ff88cc3e3a58e4099603cca60a7139dd25fc17d27cb92834673e5d44950c3d60a94d7f130984a556093f30711342549d75a4c7862d9add6414c3624120ad90e91408f6372492c227affa0076185de4e47086c912b7a51a26f44436b4c6895397a23f67f36213169b0c95141a13094f4008020e53f32f5e60b62428edb5f8b364b77fc03d068654dfa90ba53a0f09d132196cc47bd4878e664dc72e9b1e66da4a6b738648f6ea3670ef2043e8ebe23d9001daffd0a943674ce74832402df4cd84e036d28cf673302dba3ae423d06a502302387f3176c1641dc35684aa7d72e0b834eb55618db146203fb2156a422addb55ca68eb27399f379f79e4e8f03c4e840b94e6756d117ca966a1a87e6bbac27271d7c338e3d40a65dd9563443fb4e4849886a5909ffd9c0e36ceed8615ba000092783ce26949ffb9d423c949d0130182dfd63d4853a96269727dd60b512c2ff6c0586720e38c979c50b2b119b21662a711a28ea4d1fd560e30f2597d69573aa1a0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ba7443fbbcc1922e9fd01b027464b8f10d83ba9e97ae09dba61f47546b119a15","proof":"1c8f787b7b35b68046dc4b246107f2aca1841e11db187857f6aee616d0f3f300d8e76ff0037ced6a8c2258e62612341f1a2df1d326ee5bf9ccf0f065be476710e6a7a74cab3f1e0b72bdae6cf241d6118a3f10d588bdb1d1166313b994afa8241c08e7c1307a5783cf3f19d310d998a67c79288f86cd595682378c86f6136300e33da7a0140af3d3d9e5173e42bf4f23dcfaeb97018a37f6d96b1b82b9243f0c399a666345a73e8d2451f2a96a65f908516a1fa0e18d5baa15ea2945a904440ba872ee1df4cbe61825bce7ee0b41cdb7801163ae33056f2d60548b89b9997308529f8d5b02f93c3887e1676ae8042983672059cd270155be7e83d34d32a8146458e4c79d43e7eeb442181f323553a340affaab5ebdf75c4afc07eb3e98e10c41e0f45a121188c35223a690ea64afcd55326a02682e06a68ed73a3922a74bf8437eba71bbb78b082cabd5a41977a12cecde07b0d71b89659b16c14c945a179609b4b709194319f2418fb849ad782dd23448c07751c4818125c24b649f44ee872288f3cc18d120a86963448da622d1cce4c3a4678ca531d2f5805fabf0df8a1216461dc6de21ad41b948cd8b9cdcd02740e34d3bb1d5c2243a8f953996f299a21bf030fa1b67286915a9850325c9e13f15a589cbd5a41da0571c5f4db0951fa911cc5e2b1de9d580047fc9ab1959c2f10d0f0c082ed052bf7ded7f60641e72030b5e134ca1b31524ae00518258939077310016972fa22b2e3bfc5cdde129c24424ec2d7b65f8d41a32cc6c5ee47468728b607cefad210d03db49b54126f06c690ade78b6bbbc09bef88af32e1c3fbdf61be5daec6b77e1e46dbcc4586c4680a32a54ecac38dbed83d72067aa14d67339ad6477113cdb3752fa5a3f74c2eee4c802936c3bb476c83a2fae86635107ec2a006076a05db14b39010bbde99a7e3f3e0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"82770e015675cdc2d7cb7f2ae16160662b925705902ae5e7bba967113dc1da13","proof":"722d2926481f7dd3aeb4d3b3928b2abc043e958f0465f236c06b01b7b5d05b39bc2e0b54588f3e1ea7c8b91da8d0cdf1c17223cfb5d27badfe44c6eee21dc55ba8b91da37302af6f9365c558e6b41e6c48d76f4b6f85b7cf9e726286df2b2d7a240a66d409dbdfcf9f258b9472bdeab22a26f2f6f84c7085345bd45cfba8983f01969e48696d10a1e6c4ef2fc336c333fa5946478009a63b773ceb7a61741505beca07546f9327cda7b16a005b9e4c12778ddc958cc057d285787ac00069190e7547f8b7b905f6d5efd0f8ddaa0441e730201c41374ba34710f7b17b2cfa470814c1ca2a0b3b3d7d3f80b72283942a6f1968af07d8e3bb2704bd3d7a32ca446e549cb4eb18af62f81028fcd56cf63388dbc93d2b8e641d85cbee1b4e7ebd0260c0589f4e5b5e7cdc38e939bbb78b325f8505cdd2b1a8a53b91e15082e399c52f322ee67ffcec8c0e324023e13a8bab50414446b9851ccd4b3f70548989ba064e1e17ed4c7f3518c73fb3599403a76e2f0809ec1a839fdb07b2a2e096c8167b6a9664449256443714622e74d03fa45e7c27c6322f109919940d0c610b2569174840b2ade031019105d4bfafd323d80e8384f078af7909ca8faa761144d0c8ed038a18bc4ffbb221cb1d68885f2f61be820880c3f594f5e48676a84f48b2f1d956b83f8370bfbe427510ecb76d44858eb396d4e729f9ad6ba765b8af42c6ac0e732a48170fd3de67f1f33ade3ac57eab609cb65037c7ddbab415467ad16e33d4299a9d4256bbe8a2f75a8c4b2909b7fb437f35e89c2bfc496960089d91cf90137400fb2d2b4846f6ec6aa1260a6bc8186587ec5983fd25e11e7959f9fa2fe9403a5b14e9d14fe0a6f287982ad025f7baf3a2ed2b473777e36eecb8e57d37dc290be994093192fb49740c4faf8fe9f33953554ffeda94d337b52b2e2b5b731af70b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4e84a7ae33200e4c6fddec0dd0773da2577a274a7193b2da70cc2e40e7a9516e","proof":"b4d1574971caf47f5acf8c968be79f5fbc8bb95e604ce899b3f56ebb2c415879968d6c7a34da2364ad2d5be7fa2881c845ae44e2e62d88bafe4c32baf9932f74cc9256574bc045cd4bf66f18f255443734a2ad513a37048b3275f689146bc131f0555b146ef94cf7ee941699bde397e1f215615787ab869695d67b3cb68849166470d4b5fec1139673ba0b664a86a46cfa69125a3ce4a8ad8fa7642cc3966c0612149ea8a00f84eefd7963ed52915fbdfa20fd81e792829a84436f1d83b0f20beb390febe7c63d0e40b66d342e883efeb56aeb63e53d457c4975eb9588dc5308944ad61cd04b56eb9c21b2cabb6e18c0089d150bba8b20043bd91afdb1de09688650dae63029d1500254bac150acb1022395460bf0881fa5f8fffc6f97cb3320c6a8c3df8f00c9c0a385b43510a9e9cdf1f2e8586a21c78c70796084fde2fd31d0d5ea806b6c8fb382eb93ed8aa50fafdd7559838e6f4089a1f405cb2067e957ee78b29e15116b00584327a8dfe4a63881dd4891e0dd7f74474c69632c01a453c063a22f5d67edc80817694fee7a2d61d1c03ac57ea7ac134af59ff4d2c8073af47d99f7b321bc0361d725746d211e425b575a1cb3d04cf515d7d5ec696dd97eb8aad5348285ca3b944785d5c4462f6cab624ab996c64c30d043e850c4cac3061e833925af32bbaa133d8816dacf6dee878c84fc20ace1e5bbce826a374a6d7ae8f3ebaaab892f4361884d793b3554627f43f513932a23a670cf1da7f24fb5687ab84327b9fa6d884ecd2ea7390fab4dc89f0a7f605ae99b1ad6861c89bc4064ee888d5bc4635e380ff9ed7a42c0cb6818421c31b7eb6811655c40a9ce602b22f13f09ba8e90d64eb9b29fa8825168ec33568357289405ee27de5581fd4f6401d00548204ec9affdb40324a9ea4dd023116d8093f8a357eedb5424e24cd4f203"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"54e54403cc25829ab855b18540091a6a0765d221c96336c4316ac140727f074c","proof":"906339064037ee9c767b27f66065f8bc3ecf6713e1fa60347791e8ab15d24b04ac43cf000c6995fb3997c9f8bc705f66f1a2bb5940fd9a0b4d477e73b1169f4bbad4860245139a4a02877b7324ac4ff905c9b5aa8281539fb765215081849b1bd2bdaf012c2d3af93535698242cc84d3f2bc09e525491106f7f6089659290f09d86d01c0153e6b863f2acbdd54e808855d5ec945a23cddd7e4928abf1932ac0720b79da024ff6574d4fdecdb9fa7f3b2b6a53f46441f5aa46735ceb82ca62405668847aed7ce814da522ba6526436f12700997322292b17d9b5d12e4ef76b3097ad7538b1ca7d4da02901cb7963944eb6c036267796f3400b15291025be19f4990cddbbeec7fc0aa9b7f51481c22d4ab06112d96f05fc360d287aae1ff1d4269867577a7a16d87d4e587c4bec29291678d7970dd7b0a7e7f6de723c3984a8217b6f41ad72a0f6472d9681648aff16a88a2551fc1dfdeb24c1a9d4a82d2dc31700cc7f0c8830e196be6bccdc6effc5478d6420f4ab66bd74dd0f610ef6b082755c29e97f0ec8bd4378fbf2eb20a5f81c19cfb5dc25156c97cfc5079ea6d0ad9157cab7559d2abab8b988bc79d77d3a5b4be9b6606d8368638867e994dba8a66008029dd619ebfc079dc475041e17c6ee5a842354717b4141429105841fd87e537b204ce1b5feba9a2c660a38ae6bb08ab3032abdb1c22c93195ba1d3a85faa11f140f5f70690c88bbfdf8026bce680e487ac89daf6fe7b85667ae54efcf9c6553f205c4a0618c132235c3ea984a8434d2d1cab00ce93e3e8877607620780fc01c00f9da1fb682a416bd9065a0a3d82b7068beee6952b5440c546d9a771e71b9507b5a5e9a1b8f411d85f526bac706a85a790e2d7485c5fc9d3427a26276ccb10aef749d8f12da1a0a5ebda0795bdbbab8dc560f1e1a72fb1db9b63881b875080f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2ccaf0e648f62376c7231f3dc2e9553e22d4b4433776ed501836e13b8541fc5f","proof":"90244d91152c786e7d593be72eb00d09745eaa08c96e18a5561bf598eb05681b86476aee1934054e0b42387e5c26d1e29974c8431cc59f405b945eb529fe3660feec3ba7d9253b5b45004cbcb9a3a61722003d486316d586cabd9240f0ecaa29f6220f044010ac33d4876560abca8bdcf6ed2c2bdfc7d16bbc2643aff8cc1e693aa78da0c66c81a4d37f2cbc8a690772fcd51deb0118ac8689ca2935da8c01091f8f712a5762976c1480652c7fbf66e87b1caa7c738f9f953fe8c0c3d81ab2028b3e47547d0e170cf342919145456fbc2e24c986cb13cc752febdfca0d0aa9009c2dc046bed51cd1c54f50037d73c2c5e3832715210c1bd72e04c8a191014a3ce6870b14908afd16b3e1147b22e5c8c289baa2d0fecd958ceaf43fe2c100b8106a7638fea36fc4f9f131437c5e713d14966de807d657174ced7a8eb4af150676d8d563aa71eb732360e2c8946d1b0d2b0104a5d3c800512d84d00fc182f992788aa55070ea5a4c76703f325b2f77eda37c052c9cc67fa0f5420b8bc6d4967d3fcefeb846a533096806bac81df55dc2de886f7a932e273f1b639e3107da8bb3052c1065d7551a43be0250476c5d7f811918ca2b228bb69fd69b59a362c4276c5292ae1dd78b4d649a9186957ba7c1cf98b50c25ef99f408fc8a5ff3ec9908c96e7a64608c0bb6f33dc20498bd99dba141d5f14c4c9279f893318827ff4606f2145a1296b6959353391880f85da9f0a8c50fa45c93b3f614b276a887534b0e1733e41026f12e2e9d0d51825b3295148a5ad30ad184f57c09b4244a5cf6c1a5382c4c17fb121409be24b5e2056efee6c7819d899548afdcd2941b6f2433e854d32f4728c57bed1d300fe835d95c5ecf2fc394d2d1dd834815d07e930c42c358e00de4d70dcfa3eb0ae7cb1de96e18d51f185c33d97c4e996f3c7935788c72f4920b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4ee50ba1ffa19e527930a0e225d255abbd8ff32918d0bad5a80890226b820d67","proof":"d2f4e68740401467ae5d4310315a8cd8bba49d468c143a3dc8e824010f43753f029b4749aed2448a3948061a0ec7a1bb5949184ca28768e90391de86126be8716840a8d68bffca73efe42e7e90820ca140ca83a54fc1f3438534ab1ee99ea33134afdf5d7d9324032e1903f16a42b9171ca2ceb80d57b85f2914bdd7546f524ba5e711b52cf29ef2cdd49e5bf9216335575e0414d55c772d647bfb58b9852d0a16672b7a490f33d0f371312101398d0ef5c726b86cc0138f25eceb4f4ed576025bbd5887846e031a0632edb8f85052e45e89fba3b24f822ecabe2ab117281b0e066a2eb720c0576473bde961eb53f755d116d3f0242e0e8bb902f79017ef26057a9fb4a466dc85e38eb94c1cb3ec5d5c042e7eeec68b42c968e68f7bce0057211a0c870bf9639d736e4be4b156665d55ae6dda4f6b82debcdbc53c5d9609937c10e60e43e5a802d3ba850dc3b65a6d90337d9669a1d6c5f6fb0b5a3a3702d768004c225e538a87d8d37f5e1f0882449022e268207dc3e8c11780e8a06807666a982b69a17c97c553eae77eb8d41f359ce1c7ad5399e8ddba1e394e8e609d6834a49ff6231e2d158ed5ea29354ceb120baaa6cb186edac2b7b4d3d0d17e336f033a69c5066614fd718b9291a2d494e8f0efe08631eae193c23c15d3ca8d9ef310042f4970404994a2850eff3381f079dd4decdf71db42c8dea46277dc7f4e23508ad7068deeaeb992487ac7a92ebce0dda89272c688169b61a153d96f6b4f3274bae7fdd890fd07db999ad689edcda72e5a6d0c61ac2eef3c598bf1d3de4f38006ea3ae30c8a002eca9052fa60ed14464149ceb449d0d4052282de54761de902f0a1e05019cc70c5fcddb1e6ae7a5034b0deecb44ca1caa600468be1a4263f705a8d9003b82e332f6ef087ec506b624a576e29423790f7761333d6ac33803270c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"844ff81bfb4e4dcfb2afade109e5e85b577468ff1b7861d04d16c08886a03670","proof":"2057eece89040ecfb540968f655ff68affc3d8639baf722c4eed072e0d278d1306e88575d3cbfd3a440e62885275f3932d0d1d2d11f2e994dfaffd0c65580f585e878b43dc0929dc3b2538b3787224b716fbbd83bcedc8665ddd64a74a40252428473de87852fbabb561c3efeba4c30df786612743f2f7ef0ddc687da187af7f8156a39a576fc28431bfc8a80676cb906a7af163c30fa41458f445bef57805077b63f5912e286ce7df7b2c41f3b5edfe12c35852ce6b110edf441e8b9be73404c3953ee37e217096e4a20e110f6975ed7ffe2d6f4e966218a782f28edb6240078a4cbefc2ff267d4d554d510cc881f88f152cea24033777d588af3bfd0da7b1c86a1fce7230f819508f5a9829a659304eb251d5665ebbf53a95a8768601cc801b09ece8e27d6c4d51064ff5efd82a51b712ec0825d7d5ced67eaa182197ed2248cae96baee219d8d5ff148703aaeb3f0cc94916a78e318b2bd13e8063c4bf46cda7085e5a3d68b23256cf79c081490264685530f0baf44965b1b4030da4c1226b8c4b814158615ab7fa1cf31260259dbf546bd487ff37108295f37d60db4be653a1f917c60f5f85423f76b171e0bf82772fc43924bcef9553df39fed3d6c7807b4a1ff39296406d818f88266fea4135a096cc3778e9599e39a560404b472eb1b5cb4d4af8bb8995642dc37e5064aa01fa9214f37f45599375b11ac7b53b1e02e58abe4bbaec409a072d9ecd2391e87c78b74e1b96058f6a5b43d96731cfd154d488bec922ea9ea274043c5e26a3b132a150be2116e7d8015e0dcb494fe611c3ed8b40ff8591cb1ffa2b546adb50649198cb381a92dd4f84369415dc2a42f3410beab8b8889e4d31332b028f14505f45330d2162f595b0e60f4a5aea9bfae370f1daad81c4aed8909a5c88b5eefcd1a9b74884234e64e73fa8c92ebb99928e504"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"387200aa66f84a7f7c41aa1ca5170ebb132e4dd3b25c89439cb8e49801b7a758","proof":"76e97d6a1c0f33bd6e72d0ae93df30c7eaba1f3b97a1d9085497582bda2b1d310c552fd2b914faf3d7646238e74d81a2925091f4e256a10b503fade71adc3138c07978307c38d87159f3483e753f2e569142c42c7be05834a0426712411cab761aa9294b3ed1312a0e3bde14cfde292a4fea594778dbb7d045b077d7b978cc15eb62e43007723457d7ebd1384a956ced02b3a52e51a9202466d1ab3df349e1061231f425d61c086cb49c4022b9d489f0a807c369bad2a58409ae74fecdc8c609b91a2a889cade02423da7e4093cab0b5fbff2bf0996452947beea452a396290c34bfb8d6f41d889eff2929cd414f240d5ccd09b4d143209e5ec6fb37b8bc847946187f9ca5dd9384f84cebb58055ffe5c6c381b5562a54c9cdfc5610825e455ade16e81831e4d57437de5c165133ac744c0a515e9133ab083115c10935e70c410415cc25e8db043e6820bf5c3659ecb88daf720d556e6eca3dbfd7a3fd6d9206a498854894193dca89653c1d38a893cbb42cae3f45194e8e5219e4f29a5a1a6ffaebcef565b7400a8d707d113a9fe26f90742af0e0f657c5c2b74c1fe5d43c6be66c0b95e186a8ec95dd87904a366eb77f0d54356d3022bb49845562d2c8456ce012edea238810b8295ef521a46abcacf6d39f6ee0be4c81dd19a67025bce200c04c33b273d254e5e348fe43dff61bf948d0227387a15bca1659d02ebece3215e2d12630feb4d65e88b12d204c1a052f831e8a16e2328e6ba217cb4390bb52747e836a0682ee8afe618209b68095bdbcb63337def98664f583e522f959677239d2a6545f12abb3070f9e9baf6b374a2ae43fa462c01475a77fb2d3881e32d3548c2a1beefac87082b046e3edb9af9432b5ddabbf353f6c752980f03d31618b0662614bc95e772b4c102dd31b5fd1c9303048c84672809cfdcc5544ad9644e90f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"44ab10ba387ca693a31af04ccef7daca9a9fd667eabe652cb8aa71e3fc023e54","proof":"b4eb79a10546217113b3ba5a0101a07f9d718817251e738fa60fc6e4c821be483ac765c4094b2d5b8c9c01743fe75dbcb40d1c53b5abd5e0efc9efe74f5e4a598ce42924a7f08b33f5129951a2790236567fecb6358cc5acf1ccf4e04555836638e3845e670df2efca856ff8e940d3fe9dd0eac38cfc808f0ec5ad11d6e8ec4518df5c31a33e02e0307bcb60418595fded147d750ae26c63fd574a02ed3b4404b2d512a059ea8997df4a7c44227e32e8e0d2ba19cba1ec4918341c6c6c285f085a1067bc9e187660c6578a6ed05a4cc36f0961f423f22d2cf96ac2689225d3017cc692e74e65c25b02d7f0a75d010cc43822f95d76d45235ebcc32b99445f76a2a7a8bf140f12487017d4c41fd05be61fc18911d1e2a49ac78c214cf69fb944e569fb53aba55894a4b5dfbe8a31bee42c5f3c3d3adbb2dc05c9010450f3d7e17024821f83730c164c81df1beba83140c1b0df8835422f4406f18e6528bfc77509691f3af1c85f21ac084c29c8fd30b2f61121d752c64feab4fd2e1df9080017a4449a5ca3fdcc4ff24adffe3b14f3bd2c85ea935f7468674673bf738c234271a565929dc128a1a39b4a2ae58d1ab4e924e25919726cf33975b7d12013ebc796f043aa4acd9b678fab0d365de9a1e163a9cfae5c7afec2ae02e76f212a48ca92d6a00bd32bbe456276092472944143b6e48e7a5f6e6d435125f47fdff12cb7e70669af448f348c4d3fd20b687884fc12b3213186555137f1503c0c22e056f7d22888b564aa071a45a3d5bdb5b57c6085acb8b74184a094a6e2dc88782760fc837c2aff6343d4c34e96fc0e2ccb98386e409763d86446744a3f8316accd7862477a0a5e5173b7f0ca1fea0d2c2d7a0e49097a40d46180a0ad0dff8115a4b89930e4108bc21d39477252eefea19a4ffcd0381b415aecc70f765c9e3232002844106"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a6e7f03756472d983c7f35e6952f2fc375db67fa11b2a9382a3233bb9a667172","proof":"12c3f3f50cd9964e08d4071c7cb4e56e7c09fcf099e028e9caf6b27c1e6d504c245a70a99edb9b2498f75275f54841c74984999ad67629c9d00c08c9cdd9df496a3e426ecb6e6bb9b79ba40cb9fe63488733d4189a3861aa5554f2d7c9546e51fe31e2d7ecd5528a6803a7ed77fefcce79ee1a6a21ae2ddd6503bac9b4acab0e82ff324c4e8a4ba3ca0fd226050e527b15a584831d24efbdfdc58c1bce3aa20f326d91857c835034a5f8148af0fddefe3a01ad25bc70cc7877303d709fad70043d401afce554d58c5cc38669bbb6e4dc07456e855f20b42d981636b543df410c020cf02e28f311582c5f2e905a69223e4cba5df9dcd8d6a75afbae0be508a712124ba695e1c84450122faedb138698e9c8747b0fb1fc02910caf218a20d6b07d0ab7e81d99a66e37a5c1b089c6a8bf63480ded1847cbf140f38f274399886629d86fda30fcc28dd85afc74a08c40be4f6a0b462978676df4f1f8d5a938120471c40fed982536dd1fd1263bccf82a4c48196227d61ee9e5747a2b619d4579890d024f8a29f57918828c2e3919728312563a853f9a661c1f8e98fa247cffc5537ab8f69933605d1700150cc4ec416603cd3fc4cdc2d228ba4084bd24276bfe7460b2e0396a9566fd0522b691a934f7e693324cd392f5e00050342038ab10fccd564a85a55eb72a5d7d233f85dc56314971a59111931de3abf12dea70124a0d9862ec7e7370d8132cb9a66c737acbd9370ecdfc2df752c7b3d523df2a2e58094b13ca52d02584e99e733a272c6ac3137ef9a163bbe55d106f81a4c85b4c88ee4369821a6f594e8af2edabdf624fc97d7cf305d06253c7801e4fde006e291de644669a8e2e6c9bd3c156497406fb28705ae2dc9f5f6ab74a69880fb032f4a8a01d05b54bba44110f1e03fc069d8300f8b098fa266eca3ff6f633e8febe2469cf4301"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3820e182724e412f389aec63a2147d261f37ef59f9b5d4ba2cef01f793f62c08","proof":"84dd9165eab15caa694943d181d67ad27394d0855223503e67a9a7f2a68e9f612cf3a76fbd9527c35b26597dd61a397a8dff7d22cd374ca1c7ee3e5d3e12a779f83a5c50f5852309f3b8d447270f736ade07b98342dbe7fc71fb341ad83a5f3f0c7f5d3494a6f6f613d8452b2ba76634d0c221bf1d13053d57f9f3faeadf545ddc130e6f2c2268bc4fbae56d262fdea64cd82740eba4ed2c370cc1cb60e08907b424c638665398990eb7ac015baf6e6591d82f8efe8c726b8982af2decee930db1a25aa1894b28ee3cdfea542f5328b512f1049d82f197a9357a003494eae007d858d7a235e6b600705191ca78413e0f1c50a062f63fb562224db9184e388d390404f5abbbf772730591842608b161c679c9eddbbaf52665592f9edf35cf6860d01770d2eb427d86ae2fa58fe3b663cba6da53756b2a168eb37cb7181a6c2758248011470cf8294bbfbeb97fbd68aa1b31c81b53edbbfbcca4278907080b077e7e6ad232a10e9d6403b85e50f0288c46e36fe045ef00d8445a4401c9ce3383215a4b2228c3b314119a0f9bd0d60ed87d66d47be3695581abf2bddeb4d94c1a25e4049e1b711c182f2517ebc7dc7307d81b541881e371355a63decd42f04ac51b90b40eefcb2d49c1d6202326955f46359a39e23ba414f1803dd7e59b89a5794da8d3ca1e3ef9bd96901774719da66218678b45ee394e2891c41b3f36b6830b35c05fd3096cdd62aea9e63058fb07b92dbc41504134d18bb483bd830b09a740286898bd466ca43ce5d17a1e587783e87df4080143c7e46aa9545615a76220d720a43412ddebe07930b056cd186f2cc8ed732c82c897376b9dabaac063e2d9ee0046481bb4a71146873517a0df7986c7f57106f6283b018d0fb10c72dfcaa4fd060c645502d9bb1ef146a8da656eca09a0bf87cedea15bf2a7f7424a01e4c94009"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2ec8a82b76b986e584a0d65ac8ef9700d6616431bfa9650ed74db2b6bab5cb38","proof":"e68234344c703034e9dd01936d003eac12e742d0b131fbdf788d4a2d07481233b2a32dc67a773f64c22efda225139b374b50496d3148b615b20c57e9eca70b4f58ebcea2ae8d30b521f7087b94a5352f93baf79a5658ab635dcf9b61ff2cd93bde5da1e47cca603b39f4c92b96a67fce5c7731d687b8df3204ef7d4a1cf3db040eac8d60f6998891f8b54158ca3f9348e8b807821721e137392d81de66bc77081b9d55122900ba4824187206a381971e8b230bdd39ada640ec926700c1e11a0113b6b04e6686c7d731416e547a5fd99bfba433ce247cbd53732fc0fbae840c0f00c32dbd28a1b669a2f267db0ada776f175e8e874cd0c6d1362c9c6bd806ac02400fddc4ba3100a4aaab25235bf03850892927ccb9aa682f8ab3ed4d56d89f65a05780474880eee3dc3f2a52a4964ebe908f8c2c7e3281f427568a0b7c16ff545c15e860f3d75f6b78ac7baf0cbdb46bebe7199b6a3e3adf748a51016bc6800a5442dd979c4e559f08da3b127d4dfd55ac60cc4164c951c9b2950fe105ff0b11da48ca54edd207d8491107c42d8af96e352eb799c6cb71c6aa5f83cbdb960d30003484bd2123f6e3a0fceb8a1a3d9c58504ecd3926e679b072119e25be03ad0d7439c77582838527e847c3ca2296c707b908b86f8fcd10082a367ee7ea93f073e8845139c7dc35d0258d0b11020bebb127a13dffea1de67ad9d17ddb57b7aa78b4b95f5a1828cadb6a6b20c1cb53fb36fc7e2ac8b3cf35c88b60cb96fa84257dde284c19858c7691bc21ebcbafba61b0c6df6eb68b363ac86031f2b0437db846fa2192a8caa70803968a785333868ed2ef85031d55ec49d5e764e07237babe5ff391c6bcb865afb354165833160b3b3e425e56702bf2bbe8a5301ec1e2db7e0c58b5567859c7a4dd8ff89a3aed0d3ebd437bf8ba01377ac1f995815ec44c0e0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6a7f0e863041f351fdf59e704c13cb9131bb360846ed7a0adc451e3e88b8db5d","proof":"3cf6d7c093c593e5fcbd2da6a3fcea55e400b4083e9a29a115acd73c80bef047d092789dc18450a297f8c006afca904a2573e4b07a287a8528359cd12c380039ec3d8e9d72af819edd109b29831b2a2aa4ef4751b507c6ad687547cb84e0247036bf861c2f8ffe39f595a87b722203aefb1ac703f346f82b268acf10df19cd4c708ec46f150a770ec9d036afc3f1952088551cdf69a5eef33036c27e5cbd99071ab37f583f15f10dc3e6d4a8ef9d0badf14d8b5499e2e1c5f8d28d42edc82f060b6e5c65fd19c61c04feddaa9d1407be758418846e47dc75b83adc4eb0637c0348490c2847e8e1b57762bca965abdd46f3febf281836ee43495cbbd2ec094c3fd6787a6aa552a5814ee232a035d4992529066d29623bd05f38a0adff1cffbb4eecd689080f6a91d94be9828034303a5cd2a02ca3ae6df3b4471685f077656f7e3c0c4e71b3a78b7b70c14023807274665d67ff0492cabe0bf47849246447dc713268b9b4f530704f30e97271f196b0c286563e31d4201f49b02321fc492635746cfc80a68f09b1af7cc8c8c98e6bd759f115425f41b65d4906f6fbe82b354a3f5e75a3ff6045a6b69d1494617e1731628bc2b330ef8db477be5194f6e295d422b8216bc35969adfa2b1855f0bbd49025ce2cd5c9b6e60c4fe9e7683bf8b3621efa86d24471eff8133cb638fda4104d97f8fa8526bb7906e06b3c6e89615a9e6d500dbc29c8dcbe2d1eb5560d80f32c9d0d3d31d8123de955cdc1cce9671a944cd2d6c63a7bcb8323aab768646d946f61ab45aacb7c9ab64840fcc3c9f9a8a55cd0315691d1ec74a7bb9c735572644404796c5dbea63b1c8090470458d01c105e77bf30cf1a2880ecb40a06ec627fcba5c45f6b978908b8a6ce6a725236cf140f4f30d17ff660e542d24e7bfff2a39489368fa4f74de50d4c0a865261c20ff80f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ca5f1f1bd8bd049d4dd2773c4b1660ab8acf162314180d03bdf78825df4bf843","proof":"eac04f90ea4667b42ac611d74cc7a2f024c702b7c3f4c0e05ea4053971c874478c8ed4ac0f9a821bd8c7d9a8a0ecab17f848b95907bc1a10123d1a550463c64dbcffc66ce6b4698cfab9bef5e6c5a190a2c7d2e1f3c555cab9afd6c4928abb70f2f3214c0925f8a2ddf54869c25f0ea322e0735f42bf22623bbe855d64cede0d96502374030c5969605e105c7a1b517612f849240999f35b95e1d66c471d26092d8baa0fb8cf3777859bd500c7be3e23fde47d89ee8be8aa18c53b1e3d466e0fef859f2bf5af7667fc92b7fd33ef503ed4191aee2538d7429ebfacabf814ac0e4670882f8da557f4ed8505a550df9ea3c92cec6c25cc94938c0085d3d7c8ac402004e59843e8df5bcb0ab788019f16527649c1e0e6cc58db1080e099abf1393a1cc3425aaa4531112ec0a742824747d54047d510c4f7944f31ef1790d82e2d2470524510adf0376365ac1fb855cb91f67e513708f12b03e6db7d4381bdf75a426c90369505f4c3ead2171af49702414818ef35f1f4e1b08d21c6f50820747e6ee0bc1377dac59e0f644c8c6d9e18ead2570af9705630bbb8a59f2077094e3f1b0e33cded3f9629b63b48e8edda63a89e701ab6f3de56a6fd197ae875443939728a0a8526400760374ed569ba8298665c440abcd516053908fc273a6492cffe5e40c8a7c158dfd7fb564a183fb82c92a31299a0ba28b3f521e6edcb8a0b23c054a847c503e181a38e0b518b1939ae5172a84b7444899c74c28b08a85a812de65c327b52853e31326b4b61dec987636e434e136172577c656061027fac27baa658423a2a15d7323c1151481d12407c18108d9aa95f448394bee60ac273068963221ca14e396c758723c4296d3244e3370e85e6641c6f6047e84c2d825f31851905a6051937160e7d685e53da7f6126985e2d87275c6962d1dc20c62293236d840d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c292897fb4249296238eb5b6ec1bcf893662181b280e60787535452f4bd0a11d","proof":"56bf137d1547c44136069312496b48ce95ebbe52ccf98a488c4346271da7a06b5a752aa6fe89b14978ad98601b8b80736c27c6f01de32ee6297df328173fa82f7ac4b10c1045c438784a6d3d97404af41f5edd2d80a03b3fc132dbbd4dd94021d62c04311fd7c87b74da3753029a1d64c6c73cbc8ac3506b94684abcc21dea097adb555c089da4d4c8ba0c1b50d6853e1bd2246c2374bbb2e66b36cbcb75a50f282cf6f4d99a5a482ac2e4eb899515e7f134e5391fb6258207307b2674658e0d23c6a14a03abc5d689213fd0931236200734ac1a8fe055d760befafae0a59705b4d07c0349b6ceeb74ddc4dac0eabf1cbc77f4f8609ce4f2f0bb93da7974875a56c7d909216f26bbc14ed40dd91fd897068c3174c89da7fc1707ae68cb8b306bf0dd3ceb0865b9221b4e6b72b6f76794cf2f16128eb1803c1cb08ec8631a0d449a0dde6fc9e0a01f62b5892d9afd9ca963166968e416cbf17e0f2bd49a030374ecc19492639b6064247c601d986469069e88dcc040ba8d96d09a0ea18d55ba4da2d33bea99407500cabcf8de066a81ed3f2c4834f1416dfff3429d905d617d0214f9e39e7a5af981c3a999254bcbc11e835df26349b54f2f6672d6152d055931a0e3e2866915ea4778c86ebe9d3d7097201485d0c9e4e237f26dac5b2798877158721b2efdae7bd692c8a5af4b54667c705d28f1b834214dd25fbc5e5baf895d6283437549b942a2c7e79400301105659e8dcb7eed5e10bac7993cc6505b0961ca1fc5e07c595f7a156761203947345a8c125ac2ac480b1769215d7e781c520c827f564623233887e351d3de9615803bef60bc7a60904963453e24842ea0715278df3acb4b4211ae35194e93c2394c4eca6646200913c375c44cadf3187d930ff6e31fa39ad81292ede4db3ee12ee5b47adfb1d2d013357a2e6a9b6f64c28407"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fce68d3ac7e732ceaadc1dbc77b60c61af81c2b3ff4187991a8c3f128d37ba45","proof":"004ae2daecc56f77fb7c909a2e8fc781849f004fc503387343e68812665a216f00cea7f39afcfba84c9199ae3069fd6cbfb5fe974ff34f40a834205f73b98456ccbf889bd7478fa6a6b7a8ace44a37729eec6956ec000422bdaf376902b1164b2c7d3485f96391229d1a8809508654bace960034ce38ef6318b43ed189007233fa8d90c39016f0e7a1f94b63bcf5847626a1937018b56703d5094523a8e2ce08587d290e56601883804960001f91b04086126f432d42c89b648e42430d7e49003fb1b9e1603786dce2362f07a9a7ebd2579cdbc8fbbb4144b1fcdc4beadc1a037ed8d532981e9fc5de7ae8e0c7c73d1d59e9df651f4df1a5690f2cf0856bab38e464bfddd2f159fafe56ee845c7870a31e03c87390b13413041f651a321810038a134d680abb70be6e7d7ee493cf9afaf29868545e67001b95947cf01e9400001440a0dff73052bee5e1bb2dd0c46f97c640eac1bb4eee5f78942194756bd92440f3b913f09d79c95fd655d3d040ad9cbc4e18c06fdf1ab7a367f24d74332c1db66ae2cd681a1bc0b4dcaae75ef71cf37eef873f0ebc5ee9df9376ddd1154003ccbb14665c41ca6926af117d4c630d51344a05cc70a4afd58eee1205971e74524e7638ebec91f21a28b50bb7f654a79de3c6584592639e4278e017056a17da30d0f0f1ea19d9164259346d2a9e2f99a1753a76a8cac45c9c30b6729ff752756270487df68d9b8c70821210023b3fb4c1b6a6e22fe65843bf3258bd9ec771fe10069255262d81caff7b1e942b2e2938ac1fe9b38e4a1d92d07ecadf3e5e741252e64c04623844c90fe1f321bb0e0cf497ed84ce6fa2dc8048a1dcd8ff240e125549d9c8af331ebe0938e22dfc5064ba987d58cf0f9bb1bd18d20ca9b2e941b10f2cbc0b2fc2aaa8f48e6233d7258f5a0012862bedd9a76eb8967737464a530405"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9ec906b73d6538062bd94221e7f82ea259941ca47c48a7cf040ee33a94b6c27a","proof":"aa65df8e6e29fefa35d355e6ba95df344ae8d9acfcb696529b0ed4abd9406a554eb3ac2a4032f508e2d1d5fa2bcfe1a19bfd9bf30b283785bfad6f8464214a74766c60a3442d74fc0334e0577e7eebe3bd3be4dd1d1086c5ca994696c2f09f429cfb8f03617892dcb444703ad7b4958868b0314fba158b6506e772510691c175157cfa0b0c0409100f9b9971eeda7b543f1233fb19f635ca6f5dbf3ff8426f04ddd61b97873798c890102c72a1b0339dd3f35757bf62b585fe45d003b1c88f0ec57fe97541e5ecdae16853b87404aee89dcb2ff25b7f65db88f02683abb8d90b429594b16935418155941016b47bdc8a3e99d8c3bb46189c7e861c20c5b7827fdcd27e90ce6d97860249723c94c383b4ed11226ee1817ba69a9ed92d3ba0e246f60db4bfcbfcf00151cc28711e4d9eac8915efdef7ccf7b89eccc21754b79d62f8e7f6e785ba54183323bc84cd163852d7f1c2fb5915a3b0d2fb7edf22ad4c581af4f419463df032f98d85f3e4bd6cd76944f51d7f3c5191b493262a9c3e6b00ac1ae2e7d485eebefd2b6c2bc89ca66eef3dc302361c1ed6e9157004ebcb434892a649fc78f62a6d7c55b6a63bb4eaec79c6c5bb098ddbe93cb0e91c2acc807682da162b015abf7f5e8e795fb342e767ae5c9af147913bb4d9e831581f736c49d8972638001c5c91d6e6a0b4a9c58bc0945faadfc0bc75932c0f90f538f3365294982eee67dbf1594c354297f4b8511b943a4ee862b0b62669e55d0a556dec6e56f9f2af90cc3fda33476956924a4e63ffd0c6ade64c83bd71e70423c07b29172c9fbb128dc8b7f22f9c2d09d48f817dc803c3355500119233db79864daa247a68afaf56cad0655d8030c3583bbf2c6f23194f27f40462738ddb499726c3f7043559d0307af2b7a7ed9cd861407a8a032462415257c14c9002dfc2499654ca09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aeedf1727f026fa37b38e3f2e7d43b216db0f856168e40bc0a5d2766ef69d468","proof":"ccfec4d99a91ceb51882cb5262a8987e5b74f68791a164808da6700a69da0e39442d89505edde2ae726b57618728c8abfdbabc90baca14f15091ea1dfe6ddd2e4a9b6098de26959fdaa4319d7ab6c7c6a093569dec1b68df99d2c461728f126924e7604a579d67cd1c99c51ead5967ed3bfbedc28ce38c4aae026988cb4bc15f4a47549363bbe683807369cd878ad8ed1e1aa27cb63bfe9426628003089c27051fa6a2cc149adb6b4b2f9994a1c1891d7d9d90d48094d9b9bf2a890efb53ed046a1664e6186840d554d9ee704e8b715440e4095fca16dc7aa879e70e3fa8640064fc41467bee04fc29ae401cdd19f33db37ab6ecb27514a02dd75e0763c9fd07304e0c1e245938e73deb2b6a4b0194a03537638c2fe131d2d5485430e71f831e8e614671348cf8ec9cc57c464682a3ead686822b11b8003d51c9348a155bca06e4bfeff7e2f00585abb1b4b2259c8e0229ad7652a5d530c4e2e42353c34fd375162b0db4a3d5f7ab5b379684a07096ef4f295387562d11a31924b3847068c53ea607a651636b4e1948ce25d7648693e42bc5246becdc1dd2939498f131d49c7c82829f11ba4280a4c76b7320f3bab41406c55ee208e57e6b107d47c081a8327e9ef8d5c55ba88186bfbf90f49ef33ff4d3eeb0ea318a9213fdcdb73e5b2c341a8892a44440f9c39ff268982373cb15da75b15c0de5babfc06d5fb494b78690747c34fd57ef56bc6342ef117f2d6ae6831df8c7411016c9d0268bd0f659d7b40054c4f8ab11cdf44c7c8925102319a558a52fe31638100992ffdc47b84696e960d217171857c7190881a2ea18ada0bf1fc238f12bf1856e3350364fe783b33472fc76514961e4ab014690dba6e1262bdc0b32ad9334949d9bebfc332aa83ce70555dbfb6f8ef73c2bce781ffc39fbd90a05684acc0bec0870193ef0fd64db3402"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cc8a5354005c03e01374811c88bdf1531a1b9b987a7a779ff772aa3029b16721","proof":"f6527a6274314d930e3649efed4f55c056fb8b3bf44b205e309ff8972e85880d9443f065cb7628db25420ce478b30b65eba0ef3b704a28180f454ff845739a215487ea3fb6417c29c07457dbf4a408f96dd8734cfd0131498cf1f5ee5d308e6f50ecccf986f4c117780ec10e5afd211cedabe97439cabc083f7216640d3c5924acaf5e86374d13f50148b4f0ca0172956a32d0c0d13f30d2a78122ffdd7ea4053c14b60d724a3c6ff0ff6dd30e8bbc2523c379008b0bf8e973a7342a3156d10b4eeb7a8589ac0aeb7c6883ac736f113ed89c31a2cc90af9e49d67b09a856290c12d02418d9a701fa3ee2c67c38b20341ebb08276d6f32283f41b1c3d74662d43d08f2bde5718bb48659f653c0bd1793c4ddf83bab086b779b4edb5c795119156d8de19f07c9c0fcbb70bc96641da440d001c05235a50e111c4e836452ecc3c494a446c8db4efc3922b26beebad0aa3de150c41bdcb06a599e923a40c85f47a016a59fe4419baf39e2636ea1a37e1c974d1aa2f4a5fbe3e2644a4e2697b84e11fa46343dbde7e4bdf7737afe538ae82e6abf3e82ac8833f630549336650edbc32709876099167914b1547acbf05ce49f3457a6f2ad7c7f0025049d12c2dcf5165b225ad641a76ca374e9ff1f526b53f22e770f81f8878325697d8494a3415cb48145b9c5b46e6d8d4b31cc207bfc6cf3bcc0cc3c7062b9bb6d59425e1860174602aead43985d0646ba5ff5632770e93a5b94f07f2e9480817911f21d7d2bb7d47321c399040b8072ab67b5382ed282418723261a2384eb29028549e1f50c03a66a02034d38585e56aa34ab22e0dbf701984e26353edfbb000ff1449e49194395719e07b8427ed77e09d74b0447c8c5a642b3a9dfb9659b383e62f8f89f8bf070eaf521697a83a218f7e74a05961393a31701c793e2b58ec2fc1950731eb52c406"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"265677b0b2675ef75e972c75bc83d366699512a72a2f78919b12bb2d552af917","proof":"c6e201ebcdda84c40a1d518ac1736e0ffa462cc165295db61550ab7fd4b6a62fe20d70b818e06b53601c52fb9b00c18ad0f1c39fb4aeced8827f98a1f528ad1d305d2c959eaece37397981f2329461bb21aa237131c2fa9d8cfe638f898a775770cd734b733481eb0079e296a6edc8dbbb68f7ecca90fe44f848366722dd2e79d8c40500c5d4861577913d61effae351588f483dd04b856a72c16869982bf802b47c010a58e71eb100c04a11dc788ede5ebde09e86c97b806420f8367d247c058504d24420a7034c2512ce91076e68cb04a7143e057f8ea5da2030508689a40b7eb81ecf13fa77939d642472bac5b5dbbc516bcf50b0810b9c68daf103a86901927f06d52b9a58cef4711e76f6b92ea6e1ed99c4115fa074d28389b9adb4850ea0ce1e4c19c66a139cb72560bd22e582d91704b04bbe4ce050ca44aa86ac396f80dd94b31a573176f38ce9c6d695bb4fbc32f017bda4ccbcc48148fb8561bf5a56a4f7ece8052eea77f99a25ef6a83c44aca30700f8f6b002ea6a21c1b8fd04f7ecbf1cf732498949f0508eb1defcb428e3b04ac3f9efd48432a83f458f7d34f7e602a915de735ca23e898b885dc2572cfba69e2da8def07d4836d29b6aa2b69a86b8c5cf143a4c6d72091b063fc1a5c6fe7cf47e8cdf1f10914fb4d5ee76658a272ad6a2942a8de6eb5a6aee9a793c10023535325c0c4c4e2d0e54f0b811268b8d2082c8a076530763b9233d06970448845dd2c2841c95d176026a93ae60f721000a68d1028718a9b1c9e9f0e75db78bfc6c19108fd08a35047a38eeac4164ffa8e8d22cf1a50b004f203cc7d854fb1f1b5c028fe017924d5c70e96915e0241f2708339733cc7e2ba72ad56377c13d3c899386a592733bd7474b12cd5c40e0b2a7ce8c3c4963532535658b12b3ccc3a71bde9c22799d774d12b29eda68cea0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"687d37c3520d0e7c1dd1eaecc43ddb9f7aab44a5a3afc7cb986c5f219b187f2f","proof":"b26ec08f9f11e4aaeea35b650a780cd1aeb95cb10718e12f214f5e4247a0220f78d1829e1c7275221f87dd05261ba40b432898df5f10529c8c88920748606d5408e91b954be6c7db10b1bc8d5eeb631cabf0e945be99036a7ecb2de9d0a3c8589695a4fc7d0a3a1f10e2600a439e199bd524228110a925559820416eab90e01a08808d39201c62b1e64f8910ff7867b03dce74c9c033eadc02d374e7dc61940011f8d064a33b5c4dabe86befa76ab14e8f302cef910ae8a4aa7c6a39e331a804c259dd48b25761317a798ef077b10717f168fbb5ddf7ea38f1fa7a44b89ca90b769945b5091f7ef13df2450fc74c9b3452a1f476d9194ca9812750faa38e1a520af6c0d4f973316d0f2f227f180ee255f0aa4a86ce9888a5e2e17f8904c0637638fa8fd8b9afa1289b929fa12f0729cb83c9ee8c7edbe3edd59f3fd99add513a52bb555e898abdf0a75b91456aa3fb6c935b00e011567cc12729f84ebd47b52876db29ec4dde78543152a8a49ff4df0d26c423e609808183acaf0060a4c5db4aca8c99c71f09bd0fcb7b0b2d951dab15e0c0d7808488f807885efe123162f87d7ca9d9fc1c92b8b5615f041b7e6e19997325c1bde78aebbbc7fab0d3b7b4a12bfc85fb1fa253a37185583cd55bdb44788dabbf0716f9b01aae1fe99425506334d4271ec7e38155b5e5f1df63d0836c9facb7c08ecd4263f8ffd6c144d8f9be23b0c3138d62cd5ace93f86a942e71f09a6687668c63e7831f2106c688d1ac150e2c270ba251249d137c4b814932f40a5010f13d9eeaa6d88c3ee867408e489c72e269a712c3af0a73660fb61e16c9f9c1426aeeafa04d4571a2443c0437b2100f68a345ead844ca2b8a1e59b8b3dd6a71063cc630de4002514d05842e942566007593f5618dcb738742675057062803986db28812f034a3a97f915c9f5a3aa40f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5483c3d9131d20bf725d91a988343c1fca1e6c5d248840fbd889bb6fcf89fe3d","proof":"f44f1d7f12e905bfbca4501c285eef4cefc2630da8a1949465054b2268db6e73240819ce13e46385c3ee7fe589a4c44e97fdbed8d46cbd5b64df341bb23bba2ffabfaf36d5944b57fbaff2a1bbde850b4e9d67287b4cfb63c73a97fdb0939805f02f2a967ca320b81493d0ad28bcf7d32ab99e9e324dc56697b9370714eb7e399af24b5ca7379725f9ff3d72b17d2548a76c3329a4d79fbc517f19e966b17e0dbe4667e384696bdfdb76220fac811e3ffaa52c30f86dbe890ad44e0e6c03b30cb62987306f6d9c25baaf8551ea9dd3a18f84f4e6da563b7d2691a47de7017104e451b00c94363ce57288df936f2421c7529eab9480df308700380f61e8e41225f21b073df472bc3c2432b31510963f7fd171b9f3999fe2fc948c80683a5a671cae5a5db7ed9ab45a3a6e2a9c3d77737b3cc33cb64060e96d08cdfe3b3212872f8effc1da5bb38fbe2b510caaa8a4feb78aa81125e41af9bab4f1eadda7bb7f1ace38653f3d9851cd1bb9e62069e387312e2c73380fa4f456e988b987dc3c20181adef85de882a2b10aec0b588353afb5c153cb0c1d915013fa5326f74f23342584c0fe0c56ca41ec26557e3a8f336dc4c8ce033abd906fa37bb25314e1dcc713422626c6743a4074784d63041535ab357816adc8b95cefcafec17af55a309e30dcdef7b13f08b6c21394d478d4a3f3536efdcf4bf7d3e4898fc293251639cd41bef20780504893b1423ed8a5bbee9d8daac57c64307278e1ce5b330a9897f65f207f6c05cfd1118c9e029fca4376898fbfdd0f6b2d6c7210a0f86d53c129782bf27f73a503d66097f90ae2e537b7b4da5416a8326699c5230211dbe6f8be8f001db0974e20d584f808fc2ae964e95310f2e890fcd5d9e20a5f7dc4145d7db70e40ca9080db6337be2bddb1e7e055851784f655bbd0bddf1de91f53d8a77a4b07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4eff764daaa5687cb8fc25e10e067d9c2a9fb2ee291980bee3f51f467a1de829","proof":"7673bb417c17194d48ced8acf6d581fda9a4f07dff93dbb7a010e0e3e799593ef6a1fb22ef322671adcb83cb8dd6e42803da635fa4045c1757cc3d9e387b58368032ecd66ea3932908e79306e8fd9acddc14a9bed6995ffb89cfec5982accb1432974d7485f2921c64806a8cb0c8d8e75a09de69e53b2e19f482274ae42e071bc2e5996bfe5125ed7fe147e1223a0ea07dc906780455865c5a6ae4acb171cf0f52381198888db61ee524cc8a1108e87ff1bf5c6d33ffac792728535c7165480b92183b754d9247d6711ce04c5f4cee3ce02096a656e3be50987effbea0af6f000ebd1bf740c85ed55e5c74c140d679dcf1226d0390349957383adf795099eb7e0612e0af78cbeff74099e279e6c8af6db8e9d8616dd5671a253320ee708e6d7d2ee7847333821ca8f365e758cb4c003ef911eec7355ccd82241ca7f537a6d60ba8df6d401301dcb7e8a84c5bab5fe58d6c378edccca7dfd7a6ec5757fac8fc55fc0d7985b928a947f35b41b70012fcc150afeb58f49fb57b21450dcf948e6473666de5adc1ec4fad6b766b140b4a314b5e99415436e3b5aa1ae00899e7b5953cd6e8992925de88b282f6b187c2e3ab6bff70c7be71ccdf0ce1129d9d6a306e711c45c73c28da076118aae76090283e3ff5fb643c8c3d387241b7fcf58f0c100522ef1a626ccefcf13b0089cf44468c1d60e9db7e4a8e7d54ccfe3b804003be14724633e3b88c7a2478a9468d5b3c6df8209713acf2948287cda57a7d45dacd3650148c7195738146222dc60476047cf865e4bf6ec28e7422439924128091692be2e277633da3c2270c38740a394999c54a2736320cd45988cf80b6cd0a75587b2c7544e873e6821109dc0b233dae104d1d06214ae72e07d8f8405ac8cfd2930ac5dc69f434cba8f913b262bf9922060b19baa275a5a25d9662471cd640e10f0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"82f1553077f04efd954a45d7cc2c2b36ca0c9ff5854735c78af57a2aaa4f3422","proof":"121e85d4aa3d8fd5ddb05bb4fe35d32af9d8eddd700729131d48d1888fbd643f56c4163d71ad92886399fd89fa9b3afbf57a76c5b319e7fa89c5607dc3344257c220202330ee1969ec4d36a2f932e771fac7c189bac45974b3c0846eb0591f357c463b2c052b2c9f1113561df63e39a4684d2c9c2d7984bfd41f82dbf0cd8746cd16004eb41c99ed5e9c939aceedc969f291e5cef8c3fbb5eac8279ee9af0c08bcf1e9c062311859c23c169003bac2240181074bd67e692eabf46eb4b8d1830602c2d9d1044a12fa15db66b033b3b7dd37e2092c59cdacb5dd66e7a61ef7970830ca2473edd2f5c2f51bede439cc84642daa99edc4f988a90dd11ab8be74a516da47822212fc86b1eaa102af90a55b0839610a6e6fb4f7b6a971bffda49b686ce8a700900e6db79378996965d620bc77eed9b7f8b3c75e1e3753a1027118977fe841998d643bdeb648acc36c57dec3413cd695474c77725753d7c2924411082a80971b6e40d451902dba28539da6c864786bcd0301c8820dd4d84e687c0a2b23dea6befb3858c29ec6204358c8d0bbb148a40434e5db0f82201f954f5b4965145c76a6cf7f73708646eb5914e14f3d3d9163ec3b8921749df9535c90e7cf51457e75c8a5586def6b9eea1db5b59715e2f228f730bf7aae8fc619168c38ca0632be16504382d31352f8c53229d1602a6203faddf5f26af6f41bbbc96be0d3242c0e6da2b0d9f4df1e0d13c672eea0be4699d17f130fce2ee0eb372ca32c9a783660357a6c53235e01297a82ab22a5bc22314bbef3fdc51d88115cd42491340d6dea05f8d717a80c2eb5326ae6a4139056a92c5a1d38238a997413db3b0e5d4753632d56a4e6b8ec09ea39517d82dbdb463bc5e95185e04a6ca702f68df7b7ee06db3b61212efa0d2db8d4654ef3372e79bf0b95bba965fd9bb078a821ee04bb06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"70243eec54fb41fa670eb29f27f42c3208e7fb8fd49691d3eacd0426455d361b","proof":"8a2b929dd3dc86a5ea25dbad943905d6e1481fe7880925fc5488366ad3e6c507b0c530c84ac5205f240056ad3ba28a1293514907d18d8cc103cf712d83ba914832522f42beb5a76283561eda543d32baebd148e505e7ffdc116e36475d393512b0745d126bb78003d0cff4f88984c0bf57e79b9bf0eee91c10da82cedb0bee24fef2db9db96655fd86b71e5c8cddddac9fe13e9f0330085277301e7862cef50b9b7b9d218961aac1ab459c22f32ad5fcf7886d1ba10d218302ca3e86a8a373085c8e0e4f0f3372e38c7c8c7ce8691dfefae17702a113957459d8f52e0b25bb005438396c410cb5d3163fd17cc37c4efdc2796164e6c7454051726a28e2614f068edd53858daad92b9526bd521e465c71f01819cd840253289b3979a4260866697cd63af4002c9f614e99391ca5c3199a627d3b1403b134a56a87f1706ef25d03bee012d92f63fcb686953a61bc7d2dda3ccab32e64bb9d5047b33ef6b6c229080e51e2aea74971e8577272b333b9f921b582889f58c4cb09fc317756f3cbcd11863362f7f20868910d42a38bea030dfa5f819e17dfc8912bc6ba1fd4e614e763f47370ae5731b05faea05efd62de1290972eb0f56f332c71ab56e4ab9b55182fc6743fe93f382c08b4fad6afc5daa0a2a7b117798aa7905f12bcd2141372c37af02508551fd92a8360ef5653e84b550e25abdf4c308a784dec2df7c87dd29372bc1428e74ff7ccf397af193e6acebe42da9f032a24ee8f2c630c34e65cd7d32cce16570ae4f4c792e3c5c48ebf803ad1c316aef4333ffb24de2d85c12b52ce0dceb96a7be8b16ee5c50791ce0b309727d519bda22bc6c450c296ff1b6441d839bb65ae7f16261df36cbf4bfc08a7caf66e69da54cc1511d7a1777b6b6707760dda0d02fe19f78da59ec8281c2d2c773ad2bfcb4b217e4c8f694dd32a355e8e0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3adbd03111ab8bea6f91c05f1992c1827de85edbc9743e0389130325de7e336e","proof":"8c68f666b00f253b85660b4daa70bea322a5353c38d0a3df08a5963d39738542984f582e4a1bbea95b30394b96072d7328b3c60fa76088a13476a5dbf62a900a1ed37662fc6bf57ba93b985fb548ba5711aab6e3f306c2349c7ea41029a0634fa4ded97bee64320b47aa81e60539556f4d6c4a156950db726b10ea9c9df46d341537dadc7b31baa772c1022db9509ce5299ccebed2bba1c2835206ac5e8b640131413a77117c3d02581bc4d5bc0f56419e9dcf0530642a79201bc72fe4520506195e7199e1ba342429c5dbd9627a5cfd6c4765985139820ad6ac6cf034c5d00912876fb2d0f46431e4dcb147d331dd334a0cce7e5e7485a31d3da07a0bdded68fee5c8f86ab6141a377e5a9374c3b1fc992846b8d90ccd4520232ddd5dd05714006fbefce0d0cabe8a6283fc4d43b53cf55e4f9796f528082fe411e959f7cc53a60d5c1f32588192773cb32acdd80f9601ea8784c437b7dd4257420c1aa3183efa3e66f889b43d3cac1574acf6859aaebfa5187ede8931c2bdcc0178792d91700e70c69b6830bc63d566d53e9d4402537336b4c37a59dff4d00a709ec70c8358d4676f66201a27c3946621c1a91a7a2f17eb41856d0459cee7b3ab1ede950f3b667c316537abe337ec77abde61215f6e78b5cfbf658238f8d60e3d7a45581c205804dcc99f05c265f53e19513bbe0ec8e4d6b01fda02c79f10dacf5ff1aa9d4ed0c8135c0af4ec3a1f568e2cd1f36da67fafb006e5121f7ad49037553834b0614a228e3c7bcb57a98e838fd3d7341f18170fac8333ac9373de7cd68c27f8dc4734de0ba4c80aa63e9a64419df6b4534ea3c28e977b88104ebbbf6e3ca238fa458b1b418f910f044143edfcc2fba923413ec2737c11f1f01d5a74ea0ce2ffc8038d9c433789c1b14aadfff0c1ba35d9567c022f7ab0255b0202d76bd0fc8cdd06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4059452e2db37b8b601c9ed58f10ceb4a795fe9be41fbff757b49ea310313969","proof":"7ae9d4cf5570b5f75a1562637d5ab0ccfd8ab3f96e901b3fde11bb22e1dc78374ee9f5151e16a7349ec5cf8ab6a60627c9f945e76c30072658ef616925999c6e0400e3a5440b8539b923e3f5eb04351026d05b14f2f3ec8705d710b5bdac94791666ebdbc79bc8059fdd538aee2d80d79bbf335391c8fa02aecd4fa5532da96cb726531a94d1e13b6d0a61ed9a3008419a9eb990148fa689cf0fdfc2ffd34e0c0e64cfcbdb3aea89e3936c4ea3da3ff6b9532643a18691e82b95d9cd6684f8039371bce8a0f2426e247977dff2f0902664e371f9d9fdb267d4cc1206771f2f018e2219738a59b77476ee7f3c3d315f4444355af457f2879332a87c8e5e3b2a6662b6860bd2f3d0435d9d546c1c67f9c5dfe5dcc0f2a32740303522112388cc352ea145811e451ad85053b964c8b217587ec07247c201ed41497bb14aff4773326067123350d666c1e8c1306a5c6d94a363bd6a07af34432371d1c1fdf2f8354fb2b725d982c7bbbefe49902f972e9660e71242bd1a716b903d42db7e6aac0d591c58fb76d33dc4441a8b4bb6d46e8e816a13a56e285cb575ba73f8786248fa1e6cda7a42413f90aad931b7e44568906d68cd3fc9aa2649583e8c7abd896d200fc48a01b190e307c68d418bd8a336d2bdd9e2eff455240f3efef0233c04f0e8031409e107f35bd59a767dd447d5f44651505c49f523b9a48b912a6134be6efb6e8015ec143454a40ed435922096792e6c80c537d550b6e80ec0624c8dba4dad4f2e0ff344402291f9abff792d16fbf4a00ad51a3e4f8b61263dfbb8ffe824fa3936b98edbaba33465ebc030277c4efa605691c520a668cff345fadfc14bab3e3d54e0587ced8778a7b55f0c6dc69947d8f8c66ace9030a503e92bd9b9b8cd9d04b7db70af9847771e1e960b6a01e65c33b437be2095de01c351b55a5fc66ebc0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"30b64a7275ad2888da5dcc703c7f94c0676d8a53f858108ecb93b8de8fc9781f","proof":"ae010b694efc42a402ffb5e5bb78be81722efbb9686af25e172d05f585f10a254069c6eb1c13de572f9d99d7838f5954c594eeacb8ceb7df075e169ea90d695a6af6fd1eceae4d22068e1771f09311a1165d8ad663931d7f7c863f1609d35a094407f620f5de552c852c014ec4f160224e61025a4491bfb8224554451e9d115e3dc181841e162a7e2cc8dea44c3623fa94d0ae77ae4b7488730cdb440f6cad02a09bf86bcb9048ce00f14a35313c13d607b6a880e93cd24401297dfff05be40c08addffbe1f83ed821b691364d6f11be8c12eb8267f73a35a78ad5a2b49e3b0722204269e0acbb1bc92228fcdc4faa7d7ff63952da84757ce0ffedab3a12041dc2337843c201c0d3510b7590453f7b8fb8ce100080b4b95ac001c87907a324309c29d3e5680d4233623c1419b10c806ed73d43f77eb728ef57d0a0f83af51f5dc85f2b0f7d7b42dd9c09e1c6deb861f52d3a9ba89d09b1adf31df428c87edd182417af1149f949bdbf014f3e7e67d2546c7038e0ac987cb8c3eb80f9bb632734ba18be95f3cb21c342838dd18ada811113a2dfbeed0479a05def834cc1f8832528622d45a3035aca954ce6d7ed1cad515c04d3482c0688c9399e88ca317389279eca46c484e02d48c73efd33c8f5db22bb6e138d9c0ac3dd81b0dbbbe137ff52dc14b8444d3997bd868758571cf1b8abc48c987eacf2fcfdae666d9667377052d859d5c20a42e08d6d79d7ac280310b68e894eaa5efaa52d833abc26a92ed666ea9dddaa0234d2effeb25eaacda54f8d34cafeeacaa03c095516fe09bc75483870745a5030d626094986d14ff4abb43170e73663db41674f2c30681d23172b36c22bb074b5d0a06249de0b9c97dc09499f3c0885a114345c9d59bed833abb60dd348f008aeb3e6c76a33b645932b5a88d61e5f493bdcc822cdd0975c062d630c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5ac3fc370e58f36f7da66fb5ad915c3c6f8faebdba813633286b47b13b690f56","proof":"fae70866a2412764174745a663dc5bd259047302bf048b029507509f07cc10125c3df1935d88c6e4d1f86fea6ab43310eee1ee7aa26724ae7279159302b2ae5cbccb5f1d484f4f8f419c2868e0de17c2f220f48f880531c704aaf7ff9944a94820fdcf3bc0c951df819c7065c4d94e299143e8573e47b43f90dc4a119c3e6c177a65e2e51c5d9d881a5a9e9c5b59316767c0720c473dbc863ff5c5c3ae37c003d003c8c6eb2ad8ce2314747aac7d0f93369862ada62ecc295f2af161b33b810311ed946535ea8c02857ba99c1860d91879c69856077ec71cd2c7b1268ef6d60452cd731cb2380e1f4c42ec2aa849100da0c367bcdb58625ead486c3fd8052a4fd26270296ec53fa73d6804645bf746fc835be8408f9428a31b7732f4406dc10e9cf6d958eba6bb4d22b89edfd08d572924c43377cb19e42fd4258b6e1e93033508c9effcb4774f6c8af15a36bcb604de80fd5cdde5daa020db748ef833a8474bdc0a35356c6cbe205c8200c80593742534919079a07c1cf018726d5371e9bb729eb5866766a19576cf150a89ff143e4eb2fa6cee46e541ac2213dea39f44a50bc6cfecbb1331de80329af78b5e0db6bda72e8cf9f44104b2ed7181a72d462b169c777ab0f36e5104dbb6cd57bdddf458501fa55a0e7d9cdc223df3f28396280dc8525b7db80376f615886b8963b0788ec12df35b0e43dff73c35f6ddee06e61914a7be191a71b59cd5f83f0c6b5fc112af700549810dea9e1eb6e5a28c5139558c7378fd8cbbc92424d8dcf80520e02a13bdbf1332694c8a237773add036c12640505dfab361625d2a59fa7f804a21842c657c0421dea4c2ff2e84af4d60876f407b7c1eec3c623dd6a95ff041953e0189ad19cb802eb75fcad431bb1e1ab20ea8ce788e2d3cdf64d9f2ed354b8c22558ed34e6bd7f8ef01f0fab7a8aef29609"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4041b1f172101bc2cc8375d9ff6e73480edd8946a0815d57e75312d2de91f34e","proof":"1e7f4f3f5889e8e045a28a388c50b67025c6e572b4a8e7174b3e17e34ef1ea1fb65a2a7cca4a0fbad873e6ad00dcb212e6ae06c9e26723a947dcb53834ab9e363a8d3d31150bfa588f905f4f32ddd5686f16acea316cb47dfb97fd499551a00b84e3e27e6539c35323402de83d06ed456d1894a5ec7edafdacccf0c4789ce40623ab0fd838190e67e9d5f3f490be5d712490ce9c2f4e15ee1261407a558ee30b055dd75d088397449769dbf69cbafeb8a31891cfdb81b3f441b652f83d962d0a6e8c15150bc3d5d8bc5a5fe05e68e62a2dae705bbaf34b6dc0c304ce514b3c0f423ded2e9e652810063c1393a13893e5faf5a9bfcec285c5dfa02594f6fcd14516483a15894488406e6f9e0f26527fc8c6583bebea718283736e673103f10d43a039a8196556378ff66834d0aed6496329a1b7eb88cdc924d5916286bb6b0c7108a6290a76c5ceb3f3107252d5bfba1593ae176b4ca3fc10614ab1570c22d23d04845f12290de45be7045a1ee8624ec353d3888832d69b79bc18082835e6ba7832def97c36191cacf0f28840424e09c00fadce8fb11bc6b9ebdcd7b546b6dd6f7cc350554f94d7c54824f441ad653618c8911e230a12c49e41a6acc2d62e813330e182dc6913e669441876ade7c03598ef31a479bea4b7bae93757002d653940fabc603e2f7d280a72d048201fb9c399b542d231eeb4f3c1d9b86ea6b0dbd647fad9985f30ac1a8e8723111bf4b364e222a3fdc152c8e5453d71c39c96e7021fb09d2bf5a7167f3c8ef13dbbc8508bf4ca46159f90b58fdb4c04dbed8598f23cde4536a100a9eb50ac627c3c22ba57f39eebf6b27fc802b02f1345620d775b61a08d9b1f2efb1f60da56f24334df348da61657c48ca864a55a6091974f10d00dc05979e3e85c4bb0e6ca803413bdfcb230ba3c1c33b9dec554f52a4a5d3b600c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e0f47312d9ec93e80709b387460cce6b5e75e286290a55b9347f0dc758cf4b0c","proof":"cc43a5476140b5f77d0861f56ee17bdb8e1160ff6489e5f13c364b159080302cd275386f8822159c78443c66d801cf2b71bdeaa0611204c6848cffe23f08856c528162d292ec316ac56fbfbaf76741442e75ba57d2c668f53cd2e2a19a7db129660d2b5f6e9f4eb718aaa6ae8bd112669b2d1b14d06483e0958f0a5137b36f0e088d1de3c2acae8eb3793651a78880f3982dee1488d57f7d1317cb8e360e8001396b5793c163b9627195db4d2671d15ce98cc21551b69f4219ea2b06b2d4b70e8435a10cd9794da96c86813affdfb1d70d3820bd7e6b379b990bc16a48617303ccdd756cebb10d8efcd97159a0e86fccf49610b6a324182cae220879a4b14e1da414db9b8851c89aaeb6eb4e6b15bfb714abd1948a8334df00f40d28cbe6f96b34668e9d00f66e3af7d1d193f44545f4cefac9935ae12d93538137b01051616fbccb0e0db5fd4e09fc40952de4232e4e18983408e4d0daf74a3ebbb1423a0e0bd63aec29c0c5e59f58da86c022c61ae22c097dff1efc3ad8efad2a8421dfbd5e96d93fb517aced06f8f2cbe7c583f520955b8c6789414be89f2f98101ffb0748882c8425e65e11a5e661dcfdd1eba3a4b96583dac11a5b8048665ef9c9c9506442a521129e291fa558ec64ef9eced80db35844d35823e279b50927ae90e3f235245e7d10a36752fb6edec082d5df8367d247d4d27e45708e49a4720eeb57ff53f07f353ba6b3051ba8f4209816d87c508470e645fafee4c3a2ffdd7c2a75ff4c8208132a7cd0a018f9c23d2dbef86711435ddc60c2702710bcc37155ea585827aa8d0c3c866d9442cc35394a327c0808b9328bd48764d165c87015253ae77638869110077424410ce0ebfe00cf32dad906d87db9a942dc926d74b52b2772d109161f278cc3f8b2b48a02c150249d36ff8d5d771d778a655338204a71e6a38807"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b8c74649e9e121d1fbae3828b154d63063c95e44b04443a6be39edb5e050fc46","proof":"dc6200d84571e2e8f149762c347c0157d1ced936093ca04e6648fc30b6741d57107cc0148b5970a86577c1a23193e6d788d03102ae8391cbc0a8049a5113a6062e3d7f20f90966a1c2b936289055fb50483b0f5e834f5915f2cb961cbbfd093f02e0e574d10d3a415bb12ba8f5dfbb16dd864b6bbb6b482e88402d188952065d4bfa1f21015c4f95db5caf527d577107fc71a59b82bbd3b93f99ea462121280cdd98e5fb099b371a02c691ff7310e936d4819edbe96a635c9d971ecdd538f4009fd6c85cf347f199ec8fcd6c79864e15ab1ca2f83279e40187984f9a0bba3b0fe66ace06b34e66ddb8602c44c24ecc37bc9c4ab6179a6509f358b2768cb563465c1734f83637d24eba1e4d5681c9da327ebb74534757ab5fb54db2da8b42b047f838bb71c4d183dacd75cef67d0e07c2380f883f68923310ed46826b6f612e4ea62a07775dcfc2d2e2b4863fdb5683218247e4d824407d53e79fd40b1d2ff04ea05b27c8aff00f58b8eb648685ee8442714bc20b5c54afc38a3d8873cd409d342873bfe16f858014c1b74d48d87c2a1ff71e47a7b206a9bd9f007368b1065019a0f7ccd9bc8cf637e39adc042ce2a1c14681fb9b15e4bcc9a6d755874b09b150d42aa8d7a9cfa63102cc71fef03bb6f1d136a3e9b650f8b95c6666b55ebc95579a4e89971d43c4a4566fd0eef123bd12469fb2b8e19eaa036212bac67cc8ef411a9bf737c38c7a8bb3ef75de232d23d34db6f3d54076c86cd017bdf8b5f8ff744eb4571128c31834c3f970a5969ce62d1e7831aa11f22d3af76e341f2627dc1146b083281c85288c24e3d18aa98094eb7d32d0e2da4a2bbc385f51c00343fc5eace75855a85ba7ea2ed0a263dcc40344d4c4af2b0bd337b01d5b6c8fa13a89028bb76f48b9a36513ac643115952119d975a00bb3d0ecd7966f1a0dfdbf2b2208"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"82777b9536fc82d09aa21b9705663a86f186008a0a700d45445a8e2449620634","proof":"0807e7693e55db2d8cb67d9516af01afcf7ecdf2734566113d59ba107714ed2a48341bcfab6a83446a1d0bcd44426cb1ab6e053caf9469a93a1f8ecf7a39c473121807ad27e377d21c8d4763b417696f324e053d313e886c5f8310e69136e370ca4a0515487ed0694372aea78327d176dc65d44648a112d15919dd0b7cde9d15cedd3be68240bbb2faeebc063e4a13ab05a43da696e542130dd5e11542749e0e415b7417fa91d24fc31e6e2e13e05225102f248d55b53eaa49ebec65e77a900e046af93aee8cd462ea4c763c0fee048081ced844d7acc47b1891ca57b6819803e6a479b21ee0c3a026eb644412f1528a5887c35d34b8870137b6e928625ed166c87f9250e4dee9706aba522dbf2c07a823a91b3f30c79ec8b023975489a3cf6e56de6d53323bdd15214c06e614e5e22ca2387cae033363a876b0f86ab8006b09c0ccf423029cc39f9fce7eb64da2a6ef9c2e0016e59a60b68647fa639d0c89095a6b05d1e388a133f8f2c79224e96ac1be65dfee0bd956feafe4d007b2df08643c8fb44e5ac3c0a01d7d341cdc60302cfd085ecf2aeac6bc6acdf44cf159f35022d7ad5ea783ecfba1b5d091749dfaa82f9de071489df08d64f982ba489080090c176d8940721676df32f42aebefa6c32f2d9cfe08613a781be528f897ecd6077aeeb41cfc68f0bddd6c089e7f7c8a55c423745ca33dbb47bba4dacaaebb571e5eef0af114ba60bddf2c6b1b4d4d018d1d1e084da5b4ed96ddd5791a26ddd54360601352f6e042dc04a2660f9bcf13a496530155d1fe28dfbff0ad22eb9a0347fc6c65de6e29e90bfd859cc387e301d1491975d30a2f696b53518d5972d84c0f7411b1ee77a58113634eb19679ca16ac011d0ac31500138e5bfc9b5d915e89059f7d9e0105b9fd0458f55ee8cb73b6d9f86e87d6d201f9ffd0a87396aef8a60c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8ed4edab08fc2de448672c8d24ee3f9e6e97ea9c1f1ef8d8e725cc8d9ccceb46","proof":"c4dad25cfef205b1be5fc21f693f16273c39437472159bf07f943a00dcf42c3e5eb2558983f717c265d197f9db691c557a959059c22311469bf5cfabf9c8832eecbf2b03c3c54adab64da89c27800e6848392bb4903b266553fcbcc55bda403ab25ef059956470b802d8decea990191e2568b916100e72702c1af178748c996a7ffa93101fdd740e1657077fa31c77a0651c7c6a23e5b4ef285df516831b95068b219a2812052e2d0ac7e970ca99ffdebf99f7a195e726fc52471b178bdd700b033c6187af2d6d9bfcb1f8ccd2ec68c168aa2bbc3a6e7f0815ea585079b2d404e8ad56ed9c04164195e5cc821edf7d5885404fdcacad4c45c63d3e08533a0d66fab17a2ff4861b26cb10bfd5d4f41354da643a2f6ced7862db3fec4ffeddf06502fe9333af29acc1242f990dec54a21b37e8cd152f40cf129ae3adf0b2bdd67a407fa41386729bc303176d5511d9a26e0ead29363e4c4cc92d8bd4760b6c6828ce6aadf3d6196807506945d21a433fab2cfbe126f9e559638cc8c3519cbeed38d0b8d459fb997e248dcf04e40bc74d35fe414f3e08ad1a8fb55f621eb7c5f23ad487f95ac4c5a45c8223c6cc199d6762a12e7912029de71da3de19b2324527364020332ef926eaa08c7a2cc87e71b224db9c300936e4033341b5b1d0709d6d45fe740a305911bdb0aa5635824e0382e41f3a83e7c3ef96ff8038912b5c868b4524d1d57b179f4dae6464a39ac951c511eb89ee96286b9e849621e3b6250b0831ec916daf4d5f5fa1678d3cb2db5dbd5048ad62722e0a98adb6ae58d3d9f72a772a9c0102e230ded737a0efa3ef95a5d7fc7aefaec948ad7297e374474d813107328ac65902fdcbcd60b2d9dc60d3efa6782924cf3fad9825149866bfe2b72c080549b5313d281b6ec5357e402044b7d0bac0cac3d50d292f5e654b851fe0e208"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f2b7cdc651673b3f89c5815a54504e29dba7b1ef06c081c139edc7d51173345a","proof":"d6433bb19ddacfdf9a7207268b816806670a8ffa892832726bf5df0253120d7268ff288fc1b78719c7437e5cffe39f9e5a747709b8ebd5554a6fa0a06112fc378ee38f125a6617889ee5d1e1bdee5c53d7310b4349672f8dc833f0e04dbcdb449efacdd61c10a89780da045c1e4e3241ee0ca9371ca34a930a688446e2f1b31a488e1f007f508022057b7614206e5556ae7068f123d53f18e169337958f89a0b7b5ef9c94bd83a9d6547ecdc904ae3760eac878b45121559f18f97285d358b0951d7633f4eb7eee5588b58c2ef08a20bbbffb1a994cd6d0ea6ba8be6d83d0907aa4ba339cfacc4b1a71b78b91b57f5398de6e563fc82c6d1447844796e064703fc5a86f3aad867d5964b3be8cc8a59776d6dd899b8a6e66eb165f07b30176377546217d0b2ef63fb690631f2112265f44fd385792c4ec53766aae5b3155d1a7d021565d07ef7b2a638a324db85e952026fbffd55bb68ce9f3ca7f499ea324506f6e12442c657fbf669cb32f742e1142545b8a67e811337a135ddb7bcdb6ea80b881de832d604d59430ef3165d93b72a4898eae9393bef6cbe21e969e763f3c619cd1490ba403322cd6d16dc1685c35f9d0344675464198442f9f93252115826f28bfb30ca9b27e805ef395ac07ad622d4dc1c99b60180d38b91183f8463a97194884746c921dfb8edec75af137b99c1fa4a54ccc8dfddfae705ed76614318d6e3a5e5ce6a4b78b94765e01c29386fd2db0a277e425cd671cd02e56b69233dc61d265f8b7ea08ccbe5a6f2ed4723d7145b0fc5616a88b5cfda4acfdf29cac9653c01dccb60fa863635ff0b0ad5bd9d5348fc1c46b238be69d2d10a294c46bd60325ed71fcbf06c87d1c65edecb27b598f17c570e8cf23441bdd886cb2f292b30734e0f13cd3622ff9bfa4b188e49774560c6ead26e49fa4509143931d954c3c0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a8d4767c3c46feb41f2cba139bf9a3b5c5c70f0cc835e4fab91c09d38d04ae68","proof":"64f47e6118fb4106b7e2864c1dedbd0f7c0e3c94b4f8be9f43264f8c7542c16b983ee86544f465cce7caadf76aac170d431a680c914f57feedcf6e560687a926b87f148e5d982a49862eba4ce86ce24f4dcfcabb8a13e61175d5cd2a57692679e4e3bec414392bc45307df461ad1f996fff6ea2535a98079e195b953c76f651fb27973f6180ce2cbd7803ee8ba7904c561882c3cc9d5c81ba1d09d7a8cbae20fabe8bd4894dfedeb3173f4f25c06890721db26a05dacf2e6b3b99a851016cf029e52e6e5969321928093cea1cdf9fb0a2832ee7c59e6b8a831d0a046e6f8d50052ce579313e3f135d9526f2958f7fd6a913a39cda9cd752824e8e8b740b30e77d2d6189f850743b7a2f9596be5b43abf2a4fab638b4fa0b8713dcc17e3bb2a01d673c3a3fd9f54262ae342a1f1aa5c6c2dc13d299f88dffec41084a0cf058545e461f5c962b268d125e7c1722e77be401ee6fbc483aaf4c00f42f00ee4940f66306de30f265aba9e63ecc9d4dab4a775f029dc2d4cb576017139a3a1189f140ac429b6965697558ffec01303790eea82ae0659d0bec3a1da4edb2c77b30d183c9e4d6c0912e7d09cbd5c7d9c093510aa4e1c3e834a663a34d8c65e37025e4453608a12b02ace61c4baf3d773b76789a93ce1a99ce13a605c29f51f42bf42444d5a2abe8cdf760e654705fc7a0ecbafba5b7f0acbd155b889866cf66820d673156ea875c2655f6407c44c16a3bb71edfc03f5727042e4d0ac5e6bd3c47889bc274ee08bbc69e506f849b99bda6badfa643f1b4383345645faffdbd0e25e040126e443fd21deec3cb4a06372abb965fcd83ff63e3978370da0e8fd95ec0072d84718602817f372ce29689af9a5181ac30c961176bd52cfacfacc28315c8012430e3e114cb0330578cd83e9cf9bec26f3efb49e977f787473d38a25d4d7ebab8e03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0e2deb1def52bd27fee6766677fb618c99498ef92f330f9a6f146aa18c1d5a79","proof":"8ceedadd0a1344e0f4f0ea0952e31503969aceb5251bc77c6eba3c31046d4d7ac2809a0419ebebda78d872cf2e8044ecdf6aac5f0ea95b9ee1d25975c72b6e62d03274d15325903df18c2b418cb4b1eca8530df9c713631e3d8f0b453cc2635e0efb69d775c711fd787f4bba6028d4fe83d6d0fb195fa48be99bd0056e54b55e7ad1764e5a6e6027069d3cf910a93a4d482163737e3d2ac2f52f90b20922750a5c7525da4d1540d2180ee4aca55f2a5dbf56ac045147d3dddc70ab58d40a8d01892c75d2472efcc3f27ee66b63e757bce3d712f60c4d11fc80979ea345391d0f003eed3a7d78f4754ad0f341f1b031d56080f61a4c913f47b7d5aa4f19a2b34eb8e57c2bb9ccbc2b1fca93b555d2cbbb425b03dbddf37ba31604e97d82231f38865527f03073971abbe25374f403080b7cfdbefdc222671fc392966a959be346d8577c77775391a65c5544f90daecfd9dbaecc51270d548666e024e167357e1248a0026b121805467bbc500efff77fc68614142547dabd9c9f5a04ad1050df4ed6daea1a214b9b0e563824fe3c7b87813b54dd2e4120f8cf86408e50e72962657c8c8dd9363daf9175f249969af3a77c16043abe3895b30999c8f8f9355dfd4caebc496a7c60d587d76f453261fbb0d17c0571ca8d08ec5eeb56810e2e7f1c1fda8b0733c9fa41eca5dccbf316333333a305e1fed4b210f38f0eedd71e19d511a05c878a19d020d8f6bdea8d4ca3981338e06c4207d53b08297f16372ccd9b14e6493ba9306efed8d3e14d50f73bd7f52d13ce666cf14fa6da301e04b736276da076e8f02e5c75eb576a53b6e8fcf7de8d363a8d592cf783682feb790ba5bd42c14d6972ed1b53c5da54bf9385e3f4f51bb732fbb0f9919bc6cd424c0051ba059a2f179a237540cb19a03e35a871d076859c60ce18e5bb1aaae0c916e571a603"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d8c62767511ac75410bbbcff714b98d2b64935505b2b316cd46b6f68e26ecb0f","proof":"745a0599c89453683175b9a9fe8c754c6edfee5fee634423421338136d258320d60c585a9c11b704ceff1666bbc457e84cf409cb020099cc3576bbaa3d59a51f10612a20d7cab0806381e099caa2ac2aac395ba1070f9cb44b349a2eb932870f048bf018679aa3b0d5031514ba676445777556c641d984803a13509d7dce8832d463932a746cd87f61d71c99703173b4bc2fa652c9c609d07a3365ec3a87160ef6508f31dee6780193edc586d3387b8a76a773dcb6200eab898c5afb50be030fdcba83045a411c454c1233030966cd1523902c2c80d977366e908364c0516a063c062145095178034de0c166c91f74d2736702769c63ecdf643e86da027f8443646222b0600aeb5209f032f54a81714ab0c7370b04d37ceac220df8cb2725525c43343272a88349350037e272a20521bb5ac76a5f1a5120012a0a0e35df7be7442658f1f7aac0e04c749b1b3690b1ec433916b80df6cae6657cb43488d28da2946d2c3999bf0816035a87451c3f2f0063083c184172a0cdd1d9e8366db2a763584181cf28d8d1823439d9afd1aee706a710a1e3d95d195eee331e3734c35186d98c957e60670ce64a8ef6cc8115566686177d80f28c06ab2196cce86ba38d008724d1dd70ff2dfa7c6a2b71227a8151759e801d84695cce57ec4f5112225e760426c5f82658580d77d6a0bad3928e8963411646197f4b5d8fe0089d56d483b6e74de07719aae6eafd862224b20fc8a2b59e31087ef0f4a516af4bfd2a29ae16e866137ab4907c257d3cebbf296004d37c93d8ef2d4e83ab89a0b4cf68fd9d46fd617041e56deb2fcc8a2f0e1d0190f78aaebbbe93d08943f174a7e0287072e0443e6f9b8bf79a98b4146b4e0c060338c415414da27215b5de0a863892230430901439b5411ecefa6850de13220c7c2fb3e47687fd05ca1c59c88333285a61309"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9400c845cb0f80591c4c92b78d45573052c8aa51df6a3e762474f9aa9476222b","proof":"9ab9709d1a952abcdc59ea6dbfc11fa51dde0a4715554900eab8861c0c936451bc2ed5581bef70e080cceb43714dc76ce95f705097a3687cfe803bda2b59c008b07515df1f6d59472b53f11c24bbb1c1f81fdff24492286e72d020077c9c965cbc381391803276e7dd53fef9014ec9525a29410f42ed18b29330825e50eafa68fdecccf578938df37e257e7d25b84b825578b10f8104ebdcbf5c51c333f4be074ab66ec531837aa7cd4ba4761be2d0bc72d0ff2d25cd0b1ac0057161bc7ab509dcce1d8bdb34f0a9a9bd39cd70c692ac580515841a0e2682fecc00593dcae10e9053bdba177d3bf00e4096472ae2c295a4a6bb28d21a4857d3aa04dd5b10d44f04ed622c0157264959babd05f1a49068daf389d671f3889c6c80f038b298e81a68e06016f87098e9360d9e016564cc564705f396200f3030c0df8e58dbb46e1cbc099063a0a0c85c1800c3d56c4327b602e9a34cb854327ca55878ad4442142626061247d5bcf36d88307197e1c7739c9723ceea4ce636f538b4cdb64a4b8e4cfab416a6d07e05326ebe63fb3bd6764e7456943e5d7a720bb4544f39282ade1d30d68c5f23d4d8fe56a3dda65f0867b4b40354b7c034dc19c5260835f747c93be22664575d5ec4f86bf437166cf43715ad2b3d19d65bd3b855fb89e0e4ce1c604a45bb176c3fe93dad62627c156334a644f70c926fd12c1027551eb7e8a3cb3822c1cfadec5efd87db78579718def260e12b6da72ca034939d633a36cd9faf751c87727a4b9b40b8ac26e5fdf091775884ea0cc01f06498b86bba249c7d75f1618144838da5a5daf41f5593707508b89fd854204ba46f7301864131c57a20600aa295eb74826acfa38aa43e9f4df75d7c40e53e54f2aa4a9ddd867af6e61f104059a4776e3081d1c967c17522bab1b7f1745923abb035ca84e368939a4e18400"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"527398f0bb78af6e7dfbfa06aed63e1bfe33ca2d5d9400b96d5ac6d8f60cf674","proof":"2e01730b76a11183dc4be203e1df812f94234f2b5cf64f05ff983be9b2573831164ff77e5f973897b8ceb22d17903446298c3400b08745b32357b46697ee7f3f16848f50b50454ce1f89308ad2a932d3eed59106a8a339c7152fe760594f604a00d3f3d192737cdf56ed952e0e700e640d767e7db655e51d23ee6f61a82faf6bc52c3e0a9cc0069dae980cbbc5a36ef88d15366bcb2a45fe4974817bc669890231f7a0ab9838220993a935a0210e5a90073b94944c03eb884f971a6542fd4a0ba2a0a758e206e3d063ec001c974ae5e6b4cfaebabcd0d3e297074722dd5d1e015c8aa04ab7280097b3e4ffde067331792cba856c8841b7505ad56905e74ae76742e14b806c0a5aa24c523b8562ed292325795f5e6e02e2eef0a9723f403cab381014f783f9114a6ec532955062d7c4e291d7bbec4d9741f802a9b55e620c180b8e8fbf07a405e357672f525b7c73da6a0d767b58c35b660c80b6f396c4e91b6d2ae5a1117e1c774edcc55f9ba83c95db9f3c6cc28d4bec6bcb7624635c032b5658bb5e53ee06b7d17ceb68587c972362a18939379d23b71ef01f599ac036b17b14fe36cceeae4cc547ce8fe57c44963ea71da119b320c1e654ac04bcb4d5e9014c160f28df2d4da81d7b3d2e5b2959a42b04b649d0577566f026a5c9f7cfa96084bc4c9469aa8711977049e94da55507ef9c329f1d0b9051e056025472f04865de064fd761456100f3f566443f4b166964d01392f753740dc581c3a0f27aa202f21f301c1fb7cc618be33a432570fa8e8c4f44ae5f0e3187ef6c25e5bc3b463ce8133eb8f3fd029b0e5d5fbe688a8be90b888355c18ab705753ca65ea76f565ef8a6a4b0229d5dc9851a11f949c32b078870b63bfba18d824979c589fc753505715d1c3d81bf721a3f71b12154d45c4593b7b252cdfbffb7728c4c941abc3509"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5c7bb21203a9af0d2b8e206f597432888508c47c6947bc858a834c7b656ca814","proof":"a445314a6493dc5ebfb1c9cb2e8fa6648dc1ecd5db356cc0a59be859a332716c7e28c15627132210e33551d6fe416bbf6896f8ed85601c445458a34c111150313a73f0dc0745f435dc5883e064b9d8383c2db692f84fb337f096b5f5974ea219deeb380ab3731d0c5805c79754acd4bb33eaccd2436b91bca583e1f5ff5bb6040dc96ccadc46fb34b73d1b74c2e527af2d57b5ea142beb4a567311226d2d8b0d3bd3b97bc3e8282d18f4935cf22492c0ff35d2ad4da0a71cbb96b2737aea6f0d2ee2e02bfc3897526b9a4fb9ff86ab9c9221974605ebadebdf37071309b8ca0c88af5a1986972f701883153e28ebf45944ec5427b2ceec4b6fed8fb0163f194a5890054aaa91ec11ddcb8dcdcc9f783fcd8da236f9779e128066f730acacf52e5cd4b2807b01fcb0848981b1a8248ff60978d7b860a0f37abc2c5514ba092b06f23517455c93d96076981bd98544e6e30d45c0d5650ca4fcc48b366823f10b5300d37e6eaba69b6308d931ad78c98e38e64758f5f4ad9bc91c4518608f26ce3f18563b7f245dbf295ab5458b57eaa2bc3258a5a7424b674d616bf7663b68cf2feea5b0281b96aacb8758909b8fa0d38b604de10a7860d7be16697b5a619cd469007b04a3d20d3783fe8d565a1b124ef6b342a9b67083e8f235426fa88de15d3ac23a23211752a1abfe813cb520d2250537b030d0544224b630f5bd91ebfb6442a2c0bfb1aaf5f28c0add94ea389ef20aa56145fd9b833ad41fd1c01e00f3850af26a53db4065179d3015a556a0bb9bfd3a66e3fdbcadb48bf5fc2bf59ff2c23b38e4953a885cf1b09a0ae226c4ea78daec400384150c89f4be9e8d9c91cbe12945801105433241fbb645c7be04d49178dbb53631dfcc64e195822ffdd7eab901654b331f1381a8508b8ed1fd9090c52195517a1aaecd97ab837312d04c89860e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"de708596486edc515a60f2c75d62dc6ebab777fa7bfec111cc877b4cae0c3f42","proof":"16fbf42ddc8f0bfc44a188739e54e2308f43e037651077f61df10099f41f2d434e52cd7716a46434ca5ea8ef3dc06435aada116186997c2f09e98271407d907f3a77670ab3fc75a9efb797f3e381f163cf2e0cfa30669ef3d39dce3c87080e2b0e94c5b869b5aa8fa40521daa467326fde3ea1d699d7c09a70b1f5be19254309919d656983d7c14fd3177781a2bb5227f1918b4d00fc6ae13777ccced0db160b449ada4a5b9f42fd22e0fbeda01f459b63113b927e8835576c5dce136dab7b0ff06ec4fbc276492ab324d2f09f895911f80998d58c7dc2deb71489ffea236d0d006bed208309b84c17289e4c30736939fb113b3147574fbbe9a974110850434caa518ddae07854fe4a826658267db39f3a6430015ff29798ebc6be382f96766048e01bbdebc86a7ccb2c391afc89810e7701c72b07ca81baffa23cae14cb0202a6fbba78bf7d2aaf75aeb474ef2d4d5e7a625894bcb6738f60298140c6992f23be0a4ebec5c54d88cb654c0c80af360e36429da063c0bed1f67eda5acbd18a0eb09995eaf7592c846427f66bc55c2e2ed0ec3fd573750a2b1c79f9519ab5734bbc95ef838ef4abb7cc52766e0201ffe5e639896bd62f975a973acb43084891739cbd92a269c21d8dd7e0cf0340706b6ac7049f4d70eae79089548cd9389f26264a6ed8017a733d87a3601c2fdf04c7cc35b392df8391505db226c543de20a75dfa5427bc822e7095cc00b38cb4ae60c58d13914fa7c96c8483d4c6746d82ef61b894c916960bcad30cffa58a69a7e3b862368d7c080ae449b5112688e45a60527e2480628efe6d5eabee17a4b6552d550c01cb69ecff3f975eb667dd3aeefe2ae37d24632f89a04bdd325b46c0655c96f35c37874124e0b3957d5a18bc451c0f2755a3fb5af8d54c355dda541559c769abcaa428b555594d6eaae75a052ef60f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9a52d505997ed1d7f51471a8f96ae48006ed328fcc63f7ecf8e05b5f04e4d32a","proof":"220bbf30f864a6c75b7c5dc9a1d1e31163c84241afa351293b879a2fbea9d81616c5bb19dba8ab15396219090555ff43bc1d36158c75fdb1ae75339b6acab67570a53c11bd584298c417b7372001a50e0d38374dc40693038bec5696954a785ade4ae82e4c97acb2c0781414331dc520fcd66abc11b99e9efcc801b2a337a55df1e74aa974e39989d65439c1642362492097538c622cd65a0fd5e203955dca061b91aaa34ebda953987be32348bf6a031d14df618b2aa6914f1edbb1cbf08e04135d5befd3737770a2c48063a241d826455ee57e231f70922d243a09d0bf230c7cca39d4f77179f322c2b68ad9debeb03000e0e3f2641c2f433ebca31cf8f847b4f055f7021d3a0b079ac35f677203308595254205e9dc4e4fd82266985f7c07161bb4f456dabc9224f898862539ca2eb8ab69ef1cff9ecf9804b3cc7f9fab711ecd527244522f523edba934dcf6f7c25b82e44b2bda1b93f4d03b014216c93168742a2b39ed65805918f8c3fb6d91d4b03c9d00fc8bc3457571dda96d7ed67b3a13d54cb7d1f58e8804d5d726b85f729326c1bcbc00c9131a8a412405c38533d29a22d3fff16699e0e31686b9b21c494044b236e1b20efb1da3f815f6e1217df8512cf6976f174f9ede860065ad06263460ec69546c919dd8468bb212bedc26e8395344c8040ab02a2210141b483aa464c286c175aef1eca1ec61ab2acd4f0a6e605eb21f4819f84a326314cb9f7ac92e8426ba51b067164baf30b4af8431645c2129c7a7ffe2a746e13cbf6efbe2b56bed2f92ac9fedafef47c9dcdcb5982db47de693e28dfa862446b09e3829cd35a0825d707b5b9a2ab78bab8645afe54bb9c94406066104685ebda315e0c2669820d0651629c7a4faca6c37aab7bf1e09c8cb46a33d748be5c1c41d2a450572126ed9f7651241323849ea50bf8f84ac00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"68e5a853c3855a2ff40fe71ca229a6eeb80815b76e350e3fee81c631959e7668","proof":"a45355ea58034250a23b0a0d55880867282396e3523d750e040609b461fb9b72222c50c3cabfd0b83dfe3a1d85d2d862eeef605e902136d4767e98336774e172c42ae04657cfb879c62ef2d118a96a89a91f061232be4de37a3db90a6d16391facd73f04143f396b9eff58c5a35e62319332b5b587a9559d8bd3734739b33b51291428c02c716eb9143dd7f83ad4f0741aa4955aaccb99d54f443616198c280ed9676849d45736f16add14ccc4a84c50db24440b242e0e8a3e44b89d60219406e100dd1c1e0ce5c2b84e69aed67da310c34b7f22feda3926bf1eaf2ae972b504800221000e74c8571031090f24f8a29a7213994d58f602abc3ffbff7c5521308de56a86e952de860c52c08f49dcbd38d82f57fe8bd9c15310674b7a0dcf0595c421a5b3f36023ef47d1d0f0e5238361b5246f684750f92534eaa2f4cf319174e7005b916d771beb529d8cf37921c1ab6073335ee9f4892ce1ea36115af58a33c6e1c53fb30f371b9145e1c7be2766ac646863cb4e6f78b644e382310b65adc746ed4cd69c58496bc228da543f19d123ee7a066f6aea6b36591c520a7fa7ef7247201f36b7b08337741dc3262165a7b18ae79dde97a3b613643b4b7109027555f121cf4978dade340f04b27d42841cd0f173dd4a32ddbcead0cc3b2459e0d7e48ee3236433a73e7dfb501ec5fc5fc58d9b66b45fa9dff339836974f873452a74ba67de08430684ad4cfe8505e579da80da6f2d8c741113a368a0a0b9c975dec067a89f658bc0ca8dec793c8a40eb84c30408cf7c22b8159a427ffdfef4524b26b6664cb9e232ca3942def64124f79f61c45951a9a5478c2d72b0320e48be0644387dcebc64b4e8659268b1a4e58aa9b366f52e017b8d69c2761da9586259c35032c3eae287de57c05d6f248f60d14d2d8e470b0f7d988e70ff4d317f8fdcb3302"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"44a3301ca4a5fe79951a5e6d945f8db15bd96a61a5393c92d034357bd349bc3b","proof":"bc1f765c13c11b966c067f7fac1c5a0227dc20799511c734220b2faa41efda6400cf18b89fa55ba28552205067d7c45d0838ce8c0641c5347e040bc40dd1943e120f7ab396421061790053dcf511853bc4f507e7e01462f5c725dfee4f511272b62eb2e93aa227aebef5fcbbc537ecc427ee669354029ae82121f0e5ce48eb4c5cd3f511e4a2df7c51bba049e8ff4d6225a3eb33356d350365ea025880b4a40409115c93d3cb7fda0916e479e03bc00b06304b2f09aa7b1356ffca27ca112b0bb1ffa53e541e1eeddfe3d33802074f203d789b10f8ea920eb6b95b1b8f523601129a47383ac74fd4ed7bd799273674ec164ed0235de3d23d3642cefaa1f9e46316685f60685eb7ae30cc686a30b7fb6f591eeea80a83de469a2f172137f6bf684a377c4290070d03dbda0a7dfd9236884067448a81aafecaa3b877c85147c200ba440ec6f809abaf0bc2aacb7e46823960ead0e6df6e9cf479a05a7dbb39e506aaf51e581f0456b5bdc391d9894b23d5c95ef40bab2679b7b95db062f7f0d27d06d13446cfe32c6c601163257f290ca12c5b850fe7ac0722e502d863285fd022dc5d311e5663b1e0552fa978b91ccb4b170854533428eee5a5400795057b785008fdf7fe15b917e274fe3c78a6706d8e0b5f41a7f65de3882dcfe14dfcdf9d74a8a60f1c413a2d2d9676dcf566920d6b62e53a012fad1c2dca396c29bc85f71ea0625e77c1b6a98157913909255b937fa65fc42f7416d29110766e0ff0e3091374760f3039ae08b6f8e0296ce1326d5a84502dcb6070738957a781aa4a167c08eaea2d9b259c9d2afed6120312ec3c9d4eeb4ede5514e325b732f420fc513f2408b53db2eb58bae29ffd68e76a318130b5a78fe8d993c5c10138ccd25145eb06e91cd86520dcfb58a6716fb96e70c3385244c2509b2dd498dd7bcf5431eda507"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c43d968cae4cb5b061299cd45a7e3789523d500741aa629eb9f7609326e8eb65","proof":"de92cd463cece04ea2fa5207ce5a0677ca7ce7549325f50067ff213271a45231b45473673e3860053875de088dafc9e68dd99eb5f87877fc6f23869caf7016591acea7225ca4e5255b73d7b9f4257cf52d420b71a1a2bbcdf2c2cc9f98beab70dec53b1852953468404a270e3624aa0727b40d47a5be25963b7e4a622ac51622007cbb5f3a44d048f69b4d02a013e7fe15ec2e6695ed741fee95ff645d6fd404fbdbb31abfea9d9b62942634fe70531da2ec79660ea960707f56cfd61abbbd0b9d03c5191f37ed4d1fd6c67b333c5a74805f16954bef2723b8b8b1d80bd96208061d246445a11101fd91468bee204b811287812087b9d445d44d7ab908ea61167c3a4e702f40dd2b0c8e3d6db47baab7a14ff9b45f8a76caf411949baaa949215224dfd782d71ed422e80ea0bdc7d58310c3600e6ede60f4c2e10115cfbeb941264907c08bb04503bc903a933370becaf5196c717a4ad53ae0c931ed2fa80b12f43fd425e6af4f32b09dadd39dd6b587eafa05020b3177c10026ec99bcdd45057a55999cea59f17d013fba32a013de794a94ca6720d5ed0ad3df5a2afdf545644023d0525b819e8979c9e1419233944baac7f2f4127cc2022bd127ebc480eb59ac3239505e35ef1c5d2a3f1b6b2aee5a55c2221f03e8487a90b1f41533a8b624800904863608b75fb494acde5760628de29e74b74ded7a29d78d2c2e96d62b35d0cba09ad9dd16042d732151ce2fecf0ece051b3c0650c38586ea6c7b7e067088a3592b2db2bd3cee509727c8ed8b8f7671f3c42ff6e128a0fb245a2a71ce6008e825c7bea356aa235e4bf4efb275c915c6a1897d37b25751312cd94851a3d6f933a58d12113789a42001451e1eef48b98e66229d40ef4b97c8521965e90a50f24c34d5fc15fbd67bcb607e11033c4cee0fc2e2c773148c73ce654a6a0f1ac06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"18e6ee3ba885d7f4ebc548d3e926e1935c2c86ecb6e93b95191222eedbf00409","proof":"a2ad3828ba3845e9b953fcf18e697bcf34837d13d13e587098e798bf63e9061988237af408c41954bfeadfdb3fb407c1f51d6d7df80348c6648137a1f3c6492bfcc6a861a0164f551ceac05cc8cc6665047a836af341980c1b3a2ebfb1695a1716ef3aec93ebb26a952b3e8e7e94aab19b1ccd2b8b2ea6e5edc3ff69bafc1940a2b050810cb1876cdfa7ae5bbf5c435081bea7bde6172e31e6ac30026ee8430b7fc24333cfb8699dac583217354b9e65642a2a2126825c3851d593f28bdb25091a6b2c5283bf789be6f00a251a34e14b3b49a6d81580ac4c2d984425aba36907a4d22149367d0d366afd7cc1b1eb38670d512930bf5cb31dd53d6e20c723f9203675f18bca3dcb9225958d61764aa83d2df463f36873ce1b11b5fef24c04da7e40100ae39f6b7e6bec6f29b536f21ae639f183718b61a997ee18c4ae2466e677601c765325943af7dac2879240d9e6726bad5ed723be0b7e380e0cc89187060df2634a726ad8ea4feb50d6da5a0eb2c496c96d1ecbf432ffd75e4570866a5e743caefb59cc697a39763e7478e9d8c4780abf586781d395151a55f575dc1ffe461a9c8cbb86e6dc427b40ce1b7ed66e0d606a3b03dd693b45811f732de55152799e32937b88f92b9564d696d67619914e124337cc2e9dba450df97889f935bf33d80d06ceb5b6e7bbb5e25775127dc5da17b2c788737aa705286b21a406ed865a7cf86448087ef45b721a86d67ff243f185f80611284c5e34854048568b20377aa8bf01a6540be79ca60729523316342d28da0351432e1aa2a5c7f094adb3aa6c4c9dc5f27fffe1ad253f85d7167e5aa57257ed3fdfc78ec1bef586fac5c59c225687d3e9426b6adc9c1d996326d194278f9a097e4dde1a596c59b3f79e542e0cc515cea40233fb2cafb69e7ad2c0f61dddaca118e44651d86d72922b6e0fd10f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fa770c807f14bb691258a5c762be08d2f1c1fcd2de390652974ab9c70a884762","proof":"a05f911f610553c257732de5944ec00cac4633f6ef8f018a60db4718ddf73e263cf513e7562fd0c5ecbc65b279c2b743289c2bff5ead31585a60f5dc78703019d62ec443a01f76be0c44e731cf4cb4b0c02d973b43c4f02d2f8be6fe48618111707fe7d90ac4dcfc3d4dc04853a9c4c1801bbff68a1d6348e4c70417678345328175a0d4ab0bf655e568c9abc790c0826c72858fc05ec2ce695abdc7e668cc056f6ae9063a5a51369c5a6d1364fcabba75346ab546af2b5f22b0958f34f651006e9afa9a868b9693a887374902462f6a8a910fd028edcb1828eb1c56f6f65a098aedafa06693b2901a4e61d637cebea8c2ed3d68d83adb239e20ba62fb3b055c9ebeb0cfd37d766012fee56811d0681e5c5e2685af66fecee423309c3c8736788ce7038ea669a2ade16425a0fc4fc265feb8f24a5f4cdfbacd9f324e716d874f5e32b574603a22802ed16eaa5fa1ef71958a4eb0ee1d7877e7744b71d969ca07eec316f7085490a31ad3a25e9e7018e07d3efcc211ba06348942113a4f23c2262840dff028cebe9339d630dc1a6a2cc9ccf0be748a7927d54eb63851358dfc72a6f27aa5721d6f6ddb61a4e4ab36425fe1e408ca5167388b5b2c5af83e0cbb0ebc42598c30ae8ffacd0eb6a747cc543b9d4b2a9a0a71bdd6af73298a9204fc565c424123e5caa4686dc40446bfdac054c04bcbf7b7be2d2ffe2b5dc6d11ae552443b7141cf77e7b0ce9d4546a1aa2638fe9bac38123d92f937ee3995c6e871423aa952605e368c343f9201c20d4dc0358bc337391d5d1c1c2e2d8b4679a0022850960bc449125a677c35a9ed19e7876c5aaf06f06215d13374a1eae29a5f5b45bc9aeadd599f9df0a2d22bd97bf3159bf72d644c2e477f2280935118b7eff606d9d309aba3522390459e01ca1e0f9b484e6e2a11e47027f7c4dd7129c67fca05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"649303693b467d5c70b9ea6ed1b6bd8ba0049dd09380e5812b4a051c130d265b","proof":"5031d98ba99281796b8507b04bca5844f394b2ff5f6647bedd4762043eda3312a054233023808facf3d92d4b5a610e93f57f1f84d159c52c7c03f26a91c8863b081872a957f12f8d4a432f30389fb2ff668c627e8a5c9f97136ae116ad5e0a4c5889fdf24bedad00fea949db427c8cac5c5ea5ccb305957d0370508752a68516a367becb5106c382d5bbb89d3011eb119366cf5408d48e6cadf91ef1241a7d05fc835b61a11df59984032b5cbe63c8f0f4904d4589fe3d6bfeac294c9ebfb50c0e6fe1d604fba1eb57503fc7cba72ccd35ef727d11c89edde95b4c2062aa870f7c5f18c62e07942ea27be105981bfcaaf9f1dbeafa5b7f37f309ba963abcb71810cd7ff7549d63cd9196bc5b294da06440f85027836af43b42db1e122aa50471827af702fee2cd6b807634b9657183f62ebd9e0b473324d710a9bce03965ee31703d30b75e3f6e2cb3a3032b7c1ef0c9edf3730fe928c003229bd17f11fa4b015e91dafb77585fe356f9cd2a4b8b5d3d81a34653d24666504f32a68fc5d61b3e86527d0a0bfd4422454fd88d0c199b97c51da80d88bf2a4bd0826363a8070860a430ec64be5e0187c159421f92347c8b7340b3d6bd05ae8e6a6a6638ef4c5e3e6042eb5d0ad9983aebe049988b1be2f3c0b4236d5a052dfd1be315421fe4ea6954c1a243fe9f2e6bad16ebb41869f4205e58162d65a2011e81abff0d51f4d728104379044eb98ae0f7678e2f943a0252c62b2be1d55e695d1cef0584314fff68de6b8b8ecfaef71dcdc96ebf60fbce951d06fa32bb96b40f122cc1651cc5c52348777fd3cd5dc8a291fc4b3b715a52da33dd0f3b11b42a67d3de6879b686b93463329f2f46ead09ddbb052edd1c020d21d4bfb857ec8444ec5797a41c354950c136a0e13378937343fe1567767c477018c6d9972c511a2532f2f231ddd848c01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"104a7ff68e71b2dc618baf82a9608b6ac107f3078252ed8cf10885bcd8368744","proof":"929ac1e30136264cdc18332d8992665d0221ca024cf5f46658b54232bd6a80502ed8f4a6d346b63f67b57b7b5cdd592d13bc02abd5ef6684420931bfa7ef39413c95e8bdb7c4baed3be2052a659474ac41567325a7ae1e85927385810642ab1420d10df5130895ec42f678b2a0eeb5b40194b3be55cc904e3b93d5d82056b7093f6279ef9b74daaf7e7d0b93f0e1e6abc7cb5c466a2c8d25ecccc76bf5c2070265f8380868cfa7e52f0fc724e55f4d7e58886d5246f77cb4de20c17f067a56056897def1e41ac7c585840189b6d92ee9d13c9b3139fb794a6af3432193485c03d632d3e989e85f73888530a6cc041a5c9ca8140bcc7d936f365599a953268e0e7ef3285ec4bdf3e4c46df43715576499bdb83000b3c09254744ab6bddaa964517498aed597b6473a1bfd22e475a1de210d88da9c81a96b1b774d52c11cbf4a7dbe702ed2725edfdaadb4c67636df95b70d60f667f1a32b8cd7805983887c581a5a22665dfc9bfe4d73e97b7ab9983d7a80e6bbe294f304eaef4a026aad96885cba09cb5fe820a9d2039443efb63ed5a5c9b7fc4e675dd5d181c8c07a588ecc1d68c0addc1f359035170bd60947ff849e97ffe503b901d516650c3dd59006525ac00d4add22bd140298625f9036d4d75bfe0bed3d9de76afc14b2c07f83907e75563a38817e8f397d54f185279b83b5f69697d4ba3bf7885ebedf821a02285340f655d01faa83e96f6b9af31fc5abb9ae5015af3bacd934d9f3b3899c1f6f20657e789b017945e9540a60e80c878127cbae73c9644d1fe35790a97febd678814a248b131a82dab33a2969792d76e05295baa96067408e9bcce2a90d6a1f213a12981563a91da0bccb56eefb9f0e0ab8d46b447278768f7b446f806b1dd67d9f08f1b95a7fc075819dcf34c3af8e5fc5fb9076d3cebefa7f20b9af1b9bf8620905"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c4320d0b54dd777bf67df72dce643ccdb132cbf4f919e73305b4f3df7b63d916","proof":"b852deb13325aca58b892254bbe49993663c4b0f746a2df2a128a1d73bb5d6472cdfca7a10e77f5a042ee576d88aecfb3caa08d550862cc368696de081d484145c39d9c757e1a879aa8d04a0fce4982e053b6ee426e3d2dc81d11b78d89355504e89ee3a3b2c87fd4498daac89a1ee565f0049f50d72058061a01b61546b3400dc364baa23ca69d758cf08a48ac372667738a889441404151fa73ca1d13e910ba96b5c741a8b7d54d6fbdcd9c19558b4a02b9599a137d673232559500f7b41004a14f0f516ddce9d1fc5daf6ed7162cf73ac5130a4318adec6b874773267420c682cbffe84d55306306a67c3d38107295022c76ebd31640dfcbdf2db1669196138d5a04b9214737cc7b21d77ea2f23047e34c2fe9c91269427d3c8d19bce9679c6ea6fee0cc51c66d4d706b2f81c013923c7a3b495b92bf19ee83d0c5147562964ca1a1d21b0adcdaae94fd1c6a8fccf73c510800c4d9ba19b3ff7078b7ded3148c69f0e00bcd1ef97aa5c44f6d28b235b2efef69880a718d53661d780b0f86f98b4a37625e68816a3640bca481fe3b682e1361a677413e0cdb0709dec0fbe4eda81db65bc42becbc3cb40c0e031ec5934bdbc8d38da19ef801937f6b916e85edef49ae2003e3e63eb0d079c600523f3ac0e1c4497f3d787d23f37548fc473480efead086dc391de3e42a8dfe6925e8925f8e9ea4eb497cd103a8b5f87683769aa4d49f59170bd26df844cdf2bc5ea9001b81d0e3c35e9721d4d75a638e8887da4e8de5ca319d5fb4dfb26811eff5d13a22a3b2287f21ad25efb59d547511d35d20cdfaf4e79f0dcbb57aa7d877e032c8c7da1ea5cc096d6f555937ad53a622c76d193123f89f91a1ccb47b594f5a2e734386e1aaeb3e11be7232acdd4268f08c6f0d065e8a40029906aa103e7bad84c8704d297ce6e0ca9e9970fc3c708bd09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fa3977d316d5b0d69e6732fb31be1c2267051ad72983cf8e8951e0d4f0a38b4b","proof":"c0f43c7c5f06e6f1dcc37507b05447d6fa33ec437c4fdfd9ae17da5b98869a3e4ae3e549a189eeea408e621ffbd2ac04bfcb3bddaa65ae5c13c1e00351c26166007b04ffbd07e9bd619cf5e99f93109069d76129bebfb84848a3d999f08d4e28a4dfabfcba2d2e234576642e7fec68dd178db0c02d562c94081fe83ee6416765a94a8f5ba6c80fd48d512566f30ee2dcd84c3eb81a2d935fb0942d48f0799e0aa4280e810702f7c72f275a7bafdffcfb57b560a6dd26cc16dcbcf39c117511077ed3bd2626d0324961a5c59f5a8fd36818a916ba4fbdb11d57bdfa6029c91a099e4211ee2474bfd96490fe745fe314b72cf73208f3aa42dd752c04b3bbddd615bea0918bae734e934843a723fadb452a36aee1252662bd5f808f2ea9b7cc3d4cbc18c22e9f9924dba4fc8c3ffa1a74f096e9f28badccd991d3dc85aef1363857b2a8a32be01d0872f92ce9b272e5373a046d9d99726ac382f512d00e62d40d7658ea92165f0de5ce92fcb43ea0a80ea7b5cbc86303fbbfd2daf1aba9ae26211350037f42a488e0e6daba8d93e46afca9b0cf4dce643189aa3bd8322a92c5865654c67a88ef15812bd65f32d9b64c083c9133271e6275565530045c44ffc24069e8ad42593ab2def207054d6645e89ade8776113af9f6a92d37fa97931800fe75d6f7ec4283abad19b841e12dc36955eae3892111820f6ff9879e50626dfe4974dab916c8764706caebddff6a7e6d55fccd12c23e8c1b838f96e3f43407943d6ed2b1e574bb33d33a039a75392d286335d9c0c84cb7967122a9b859165e74b00076a4795c8eaab97bf14a6ed6b54afcac14c9632b5a7565dde8eb4c78f290477720183083b1ce4e35bccdb5cc7530cc20d49b72c635cc2ce7c872e5b98c69d00ba0f9d1ed74556cdd084bfe25136044ffe5e97240542cd63e092b69fec0e7b005"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"228c6fb0953cd35511f1d71705c04bf81ef612745bac7f7abe407e7cd01a0050","proof":"64d63dd97102535fe30e9d8fd1e1641c9fff3b171f0f9708dd8c5e6c68f65a67802281a156b388c5df5e106f72a727811d7541231bdbb093c5010e5dfebc175d0295d18117a2efd7d60e80b24eac44f20a88730fed2f05aa6fbac770efe1fd520c3640e83cc6832313b0af2b558e22336959ca575f92a26866b4f6fc7fc43729f08a4687b220e7dc4e09de56eeefc9800ac1479b216108203f339f761077a4000f9ed772e2498e10ca99d57e4f979d26f5550a7dd89ff2fe123c156ae05ad90c50690a152d01812b312c7fd78efea9d92c1072aae01e3fe8a2d23fa7381de806729f4cdc8024968077937aa0dd426351dcbb058f6b7b4e78b523e72da878cd46dc47e4e820bb34c07f40686f48256c69a123af683c2b46cc57b2005462b99204e0e6dd843eda40c42e40066e8e23742596d3fe2269faab5a0be0822ea1d5ef1bc212a5c6146f72d39607d7e24f6c98e27abeb06047ca3aab5ddae4c9fb5be5636a17c6f04f13e04c80fbdeb76a7935e4172c26d795a26c7b41c6be6f8b3522677ceb211b12011b690de3ebf53829970c112ac52319087b4156d694f55776d25e58ea34435715671fc33889f58a69dd77fdd4832a32147557a5c4f4e4352b627dae3040136f84c2e9d0c0462bfdbf8e79a588cb8b21868f8f90eedd75f34f124ec21269180ee3bb34644d66230eef0ef087d72714b81f523207e176885b99b778ac17895f475151a5ffc8651ad04d7d4a9500e0e9e80e1130e6c3497e96942d09f2b17fb2a4cd656ca121601c71e7b8b851fa659d4f1f0d70e890640e051ea8027cb0e242415032b81825bebaf0c0dbf345f83540911e1d24d45c19c79dc8a96ae2f88c51392a1d45130293138b9195f0b7447f52236cb0357d68dbaff7f96b0d7057555b1b72883d8dd6deb12a03562d24e8ac4f0727370e6fa2e97dd5592a06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b6bd90a20fb4f2fa4f936635834fd0f83f9c6fadad78d41edca6b6421ea0a507","proof":"0ac48142c49f99fa5c5152787193cd909cbfe599124646382810320e1982db708c2d03098489a2c192d27985244c64b475f2bc45dc8bff4e3c665cc3e318634d34aeaec0374069fd0b9d457f6da73768b2abfad5b00fca3efca605b25f2e17091edacb93835eaabf2abb68e5e1c6d2bf812ca8f610228f9eb395d135f60e661fa9c1d160ff7c078b31d8b6e078fe1f8636f4c3ae84bebdab6b90e3ed9c08dc08a7ce4ee776857b41a37cd49057db86c9382ae9a9dd7e3d503e030870506aad024114911087fae2bb5b7c39e089947a47a8edfc1eb8f587d0c8429b581a32d20d22d00fe27db9b5190fe1065381fde3d0912fd7ac26fd95a6332a403fd1e55b16ba7c24946c67877167563b043e1d92ed02197b8e5537a50f92885664fa5f6b665c114116509a0a5542c13b093efad1f7b0ba7971ef9309cd9390a1388497a01fa287037ad54a7e891b0ba40f7e3a0a323709fa12b0c8dd728414626444488a100eabf6d248b124c950ac8211c7dda66dd8c3e394e11a85032945178a20b98e74c656eff2327e4ca08914673ed9c1ab0e7acf6e97797ae7bff8043ab0d88f743ce675c7383c2f13f70ee4a942579a1bb1b31cb75a458d1d7ce8a3eaa552a8ee049abc371dbc54b616417b7645f6b5b9b7fadaeec27f16b6d10fc0994744a37261824bc85dbc3b2d8970ef86bd596badc53747b83902b5cedc8e48f75da351227636323cfaa571a687ba216f9971bb939adfc976b74fb26961b7c1e7fbd375146c984a2cf837e5f6619c28afbd6ef806fedf98c3c47cf75edc47dc57d62c23155d0aacf3c84134a0a78b22e7f7c0bf6b8bab5b27e17156695aaefe8a020947a546bc478a9cd7d95fed119d722b8d2a626f76b4ab64ed62fab5408dc9402f23cf09372df44ef0839e6cd365072835664889ce2e285961ec0ecf353428ff2819eb08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c4b51240dc852d389fcfe6a6b62dd9e2562b02cbc516451fb6907f1917ea6666","proof":"6c9cf2522ea9ff8c07f672607b426b48e55b04288b865432f870c3efefc90f49b85d2667933b617db35bde1cbe19e1db39b187e103f3f455232cc128db756e011e764cdf3b3239f3d4522d86dfa99e84591cdf0baaf7d5918eb94098ebca93790eb03de0d3a1c02f1b685da7a6efc9e8064fd05b19a1d094e7731ed5168b2a2267e091c15e1135991329e8b2b96156a30f51099231868937b9c7a44eeb2d5801fb8e403f7ff799de4454a8c369aac7045ffd2bf4de4c350ab6ab7e592ead880970bedb87772f31dc008f24b81337d2daeac5572408d467f03621c9ec96a13904dc1bc6046cf20b60de1554b48c0f8d893e0e37d07550f9f61a604bbf1b422750769dd4d9b6cef1193d858240c8470301174a739f6c208432733d48ccc634ff09c6a9694980c2ceeb02341290a749093a2610a5abd2fc5a88a855338ea7592822207362558ed9272b7c97021a64434abd5f4bd838c20943e3925f94aab69bd2589a25465c11f2eb6064a5a8bb30ce877ea73289830823464d8677a85da6a13b6836b9d9e1bf2207d710661e7e9a82d0ee1e94f35e612002ed085185d31dec534bdad9b70e5e3d184dfa675003c389b86ba5791cc39eb3dbf40fb6881ef5486c23ae99aec8bbeb6bad8adb0c0bf69f1f1a84e07a5da40922e07a7e5ad181a2fd28325d3829aab3158b8d1a31ad76eaefb7b86a17bd4ada7b6fcd7b470c514954704c13bfcf6e1a386f96143f4bfbc39ddf50ab9e960a8235455c4162c53bab5138aed914305f267178ae336230e6cd48d0cb51ddfbbb75e2e3e90502fb7e088e4b4090eaa7375b1709bfde3733074a3fbb25679eaa05fa28ff331798ae68e64f4118c9e4897298ad9d2041b413be60b36a3471c5ca6b619133fab193477b0c4f03fd0751ff84f4cbb30709aedd930cd09bf6130fd250f7c64d6a21700ed1e0ad02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6cb25b86fc699e56dc5be0a570888745ad235a54ec50ea1bb6991e83b3476104","proof":"d01e12284fce04cb00145768c5e98b305819591485914bbd8da29cda8bd0616222387cdd6116a7aa2e174dd3b6fbc1612eb55d81caaa991b9b7659a6e93a284180ca4ef91f26b80246be6bbedbd408c85a7cff171f4c1c808d017b8290e03f05462e869a7dbde459a6505721e6c55a98f67c36104ebc9cd877a727931010b6624721491947a8981b2203f3779c5491a1b05bd66dc700dd4221fb6e9d34676d0cd6ef6d0eaa7f4e95065d925d5bea979172480b59a18bcc49d1cb5812506c4903196bd9329b65e7d0ea9b8c5be87d449f0ee52aeee608c58202d657c01222b40cd0084e0a76a24381f06dc2a0baeb617bef0e5c0585118d55ad06bdbb9aa0fb4e8cea8f80cad70f418202402c6aee450afb7971606da185baab8bff42fff0314546a7d4414c011b2c68e7b115585c714d7e60d629c21d881e9f6fbaa2033ff94f2e45275ca2b7edef08d02d6848948b34ba6f3101d9d3bd786eaaf9e2d6294c06d68d69dba0f7a674092f573d7ab1696b886809ca81dc23136c03312700496f3fa6bfbab90c1d85ef5a45c144f55d7176643abaf888371cbc4e525cfa08ee481d82b3e19ccda89348bd9d79a122e0e63db80b512d79f8309804ee0ec49e16044e3066b767a5266aececc07c199c095b9c390e095b1ca39ba204acf468ea162a2400649edcef26e726826bf409dced84f7b230b23c80742557290d3ab19f55d47a523a5f51cd34184db063d07ff73060a8f6837856ccbd96583027237931995078386b0e27842d1f8de1acd88ee5c21e16732d4852397acc07925d9c833722143f86bebb0438ba7445f0f5ee558a73ef65b657aa1505feb21bc19e64b1a90c3a46cd5b5fcb3fea1a11f60ef9d95eb866777e128dfe786fb16dde74cf88a2ddaa0d9e27b685b3ae96ba9c127eb527e72707d182a449373af7547ae79708d5640103"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e2b701a5fc8e011577443c35b639a525dae258e5d57f1ff7bf47957accad5114","proof":"4800a7e4aad823148db095515b4518e9bcda5db1372e2ed39db0cb5ccd5b436ac866e83bc0caedbed90cf82a41eb7ef44313e5f40a2850ef5301799e2951a23d0ab86d78453759b4066e090295ca1a6dff62fec7d03db8e33c610724f6d77d5140bd50931eb272024435498359394a8409ab39cce5301902365a403ecd00713f7956262678827d03d2dc7eb17da42b030bfc8f013945d8db8e022dc0c26e7d01e08c06b88197a3efe9a0a6a12a033b806397976a18b09048179da121e3bcbf07e875bf9e37ae4a1fb564853d3f56066fb484e8713bfa58fdab4bc98b4632f90328d5adfd74ec7e5c4c6aee40cfa38ccfec130deaa3dc6437118299fd8faadc2adab130b3e48e821277417523ca67cdbdf2b41735193889db108bd52137710e4af8556b2031794da1c9eaada6b3e6bf0f370da06bdddaef13b4660512359bca05ea7bc70d0426de3d87d56c0474be4ab864f9db208db21504a8809aefb99e6b5d5ce9ef895109b8e0818ba89b54e55d1f8ce099ecad7525c589f51c511a54dd511227fcdd3ccd38a9450e5ec729fab41bc7bab70c10148597d6ac7245e192093f70caf15419755225a26a9a21ffdc8596f4b706bd0339c2dbc653262de9bebe6fde7febd3a6b67dd1004470da170da340621e70ebdd670d68bb06753142302435de12c683a52740648713716bf5c6bdf71cc0e1e34c755bae6765285c42e4834b7871b3af6c7027460760c2e0f22919e14ec070b51805e7a1ed3600f436547852aee50a27cacdeda75bbca74266ca54d4fa9cf5c2478c42080ce0b755a3fb151a64479794601c68490bf160b76eeddf176614004a8ea7cdc078a61e9c9987a2445249932479e2a5760664560d9ac85bd40d04dfb9aaa61fbd4dcf4d0565167008e5cae6c64c6e79984c511f99a34c9b46c70fa2212d84bb183c609fd7a56f8306"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"569d09be5004327207d01de1ae9c81693dc3ab71f37066d9b7f694ab67dd1d43","proof":"221fc3e6244c044f553802c272c17397d78df7b5a8e74fa4921e6a6dc1fdb310aa786ddc0a0eb9e92bfb9f4ef7509fec41d3c7c373bbb4ed2bd8f986bd8eea7120012c5949226d47470feef27e60032880b568ad712f922dba5542a560409677c4f5f7186d917d06182ab76cfbfdf0547c94746bc326ca7c335e602a3ba943386a14be64f11f3b803b2c9264eda400b6246c4d88a1810352a76989d3fd6be70311c6279de7833768e03bedc1504b1f94d00667ef4c90dd663a24035f193a970b9c8e3fe3ad8db8b4381c5544410486237a7a3ccfadc7565eb1fac9e7c836b605fcd650be658f15dbc3ab85e07357b33f7dd3ba2b46f006b4c7103785a7f872064c64c5c954a6a019508c48d276d0b88f940e26a4c90fd01f34ac222e016a9b6e3a79435b706c7a7f0c503d29b12fca22827efbbb1c679672043bb3865162661ee0dd729adcb6f9e4ffdae3ae36176096d50fe96f024b922ba2124c35a1108c25507e582026238ef99884861fe7f68054987a2f9005753e4831e6d0e3124b691c4a742e8a4db7463bca1dcb53fa51a52d3d205fa9e53fa75fa2b50dc5a512bd7fc01437766511608341c6d2c5443d3562ff9450a63b6e34e1321465fa8a1dd6777c8f1a2bc2819a75fb107a5cc66b5afa49f8a9dd1ec8a7c086260b262e172a6bceefde29863deda2eb1034444d45f626ddf10e7d55a6a04a32d88abc7fc25c32fa25b899d661bc16ff541825eec15929f952d9b4494890d2754b4942514a2b120c0b8fe0924f57019ebd68c3a7dfd981755bf9bddff9f0fcf95973668588816c0080ef30380def5fe4746603e6c075e0fd9c20385711dc9c17b992462a8eb763e0e3e7ffc253b4aacf07defd7e00f7f81ecd3fa2dbac5434867eed718f0e50057373aab76c630f91003e40425d699726c6a1c471cc0d875b6769c76f06a03301"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8c65c18f7910acd564feaf833c7673091d4423728bbd1aba8cd156b16320f552","proof":"c456dd1c776aea8e1b631d491e066844c5a99c2ba4b7bc1f3a4bb1753e24151c5c40af058f441ee8df06340b7bf762c1602b073ef15d5b9e10808aa1c7b33f723cb99173145b91bf0bd363755bef55a82ffd559f53928a7e6fffa9924cce180104b9c887313dce29edd3b626c164f5b0ddb7e5df4b0a3c590adb2ecf0b8be814840b34cede346bbbd53abdab9a81868938268c810ef8abba22d39f9788c1770e9e37a6b7727ae2e287500db85c7a76ba90d0d08042a8329dc5d1a5300b765703eb1c9422d1550be594ff227e98e94e9b71ffdec441600414255f1bbe7e36b10eb2965d472ad2c821e28ad0099b5466c45a6ec17f33350c6da7d58b9c8e398c3afc269e91357ca3c14a19083d3f24111a9a0e1d43cccb4c091dbac76ae59dc774d23c6e7e82803bcf0b574ba34ade13585fea422ca472d60b99fd43b8c0b0eb1c789932c96a6901ea88a3571ff0730ee81e83afebd3183a556ac799cd2b150d5ce0568389202501463b29f29ed32b0e369f1070ee7433389e5b3b5268faff8014a6f7e6dacc19e125d68f464a61303dea799c5a04f94c4d5eb57895995baa0317807b481e746f185dc6a93b2e143281a406b587cfce0f95fb8c93d30bdd88f75694af393c23b577a9486688c15e086455f9a5b672714931fc900c133c8153e0079c8bad77837fbd45b600004cb0ed37abe454772422abf89041a8c3a61610495a86e5a1da14c153125dfca735c4a749fbba64eeb6c7dcd04aeb589da93cd178479e7e15a4c03c427ccc0ed0b1aa885d92e9e50b669aa27c6f68707d139236087b30567d3288e2ceb36eb2a8df71b176504c649bc4ec2f781bfafdcd93864c462f69894cb9cbc1d32c7f34dd63dd91b946a28142ca08ee61e05eab04da32a7ae00a6ad53402b94a143e6bafc8163886008614fc2f1fdc286fa479f3c5800ca2a07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"baa6f33caa65ffead82c73580b0a43c49579db0f1a98d9583afcb42cbd32fa79","proof":"d29f4c937794c68300366b6ef18e8110ec7feacaa89573355679868d267f1141c67b6670284fdb46f7414b10fdea1532b035e833c18cd1a61196ec44a2a65773165b156504e83e879dcb636c4dee7a3c27ca5b2b8463948c33d92a8bd0d1744fa8f6c0746da9c73c6c945cb7e0f178cdb758dfe7ee3e5e03a290076c2fc7b14102924036b0ac71d26c25ee77bcd131e572eb6cdf78ed3e3fde0b31a59a37560c41642b5d42be23ac6459545e3870f6d28d0d3a4a8243e6517d7b8c9490455f06ad7f10bc8d2fcd7dae83ba40626a1b5aca38345bf8e6a112cbb74c7991808e03982cccc6d0df21e358688a5c027dcd6ffc6d2d98aaaff9afe98525576d05100f56bdfd4462f9ce2ab1675a7c4dce2a9c3308a35a02f2d11fd30db5d5feb4fc26640f58fd42c89e4d86668acdf2f2d5cd1049a6c25f394a49dd018d0b473e8b54b8982ccd56bee1de1e7f7155e07786ca94f35423dd8bfcb88d1b5874c4817a568a0d966995ec4eafa9ab616df23d8a61650cd727428035fd182fd7e0f0a54f711cc19cef493708e69678537707cea12f1b0a97c328e84c1a2cbfb84a6c9dec3256653e4ade65b5fd49d85b1767582dc15e060120d8bd2e3d895069e8986ca67a6e4112e0ab5d39ab943f234599a8d6ff51538a7a0358630f3f0e341b57a8c67352633db669ab49a48059ea203559e737031451b47242875759b830bb5ecf35345ea42b1e42d8f9eac86868294556730025faa0040961e959a55599fb81de874c14b8579e2f5d3d78cd9686d5c39c58c99b16e9ad2e9dc4a22b940535c3ae2f580826ea5b0659d52074c28e4570b6024f00560ac5d5971660f177dad39210e24b0ff8cc403e59410ab91ef93a98d530551aef62f9b57cd869c67d010899d2300d7d57d4d6b29f6ac8cda2b59490b7d8e5c893d24e56ba0b16eee09a2a3dc9a20b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0eaf85d7ff2acc800d1f55d103ee0ac9747912b2c12cd9e44600fd2e8df8d538","proof":"0841f035b4433076d4e97263a45c4fbbb29daa4abb8740cbdadfeb2022c841545cf477a67af49e730b2ad2cc01282cb5b852d04ea94686b0cb1746d3772e682054ee731c07fe77e5aaac4eaa2ef2a3f645733f1d6f60683b5941ee328d1ed072c8189daf277d13ec394e992f50737fca29d686fbe1d63d4f09fcb04d6800dc1461de6bd707c99586eea4ce107cee073cbaec31547a89d7b2890460ce9912b004ad30d8d327f31a1e332d45d0458f2b92692acb98122e0340e0b3b86ae0a15501240c92f264033dc5d287db4be1107e34ef8ceca10fc41a89c184ee916756250a3cdc3d77683a31fadc6b70056170762902d80768fed03095342dd8ce393dc72edc9dd8c786b87894aad64098b23e17ee10b8efb60bc66b0708257cef4fe0724a9abf000b7b2f0578b13f39e16bb279d27089aa0830fb386808f10b7eeda60118b2263407abfb511d939c1d865be23fd9430cc799e13290e48a844dba3e55b415d8679ddb3a2ca8450086104fc2424f1b4e3f421e018647f208f2087085386010f4cfeb61e7fe4b917393535bf22fa9fc30e667341b22acbb5ed132a63ef3aa6ec63887981bc3266c636b52aa4c5bfc49c975fa7f12796cb41679a2b2b27a7971846745db1633df6cdf45318444d0bc3f5b1791507540ff7f6f71b63abf3eff29865dbb9a8163b748df37d6419fae6be8d171dd0bde9c2646c31ac41865971244cc352e95b39aff2e2e5a52aec587c03082a54de77f910bbd4c60e93632e5c942824d6dc4131c69b7178082add0bd5cc0c1e13e0ded82e132d67829e52a485802d26218d051009c66d378809ecd481f0e0fd8d3163fcfc42b3f9455f7f666eb7f459a9d80fea77fd3e9314947ee6028f0e105b5b33a07176900e21c33bb19cd0cc7aa0b828490da4a52770116d16191d2bbaa560be359131493541fe9af3e870c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5aec7fc3c265b5f9efd50374ad23ab352fcf5642b936eeae0b2938227f73a60d","proof":"041aa65753958ede6c9ee67db22797867575df3f1c810b34a777e3d7c62e687be6c09904ec849435cfae499d9eeb260029d7564516569e22004f52ab61798d5182ce89c97b591e811577876b1664e600b671cc4cf3e252d21662a93dcf91fa0870100c7ca658ad5683d3a21e8ec054b40493abdbf991820a6234300fc1cb161e6feb042a00567f0fff4d9330e4f8f09d636387791c176f8beab95ecf76b46b033ab90ff7a57c81a8a16282dd200eb5fcff005375f4520c7ed843763dbb31120cba8e4ebf87eaf920d720d41f702f8b7ca46e46318556263ec0fa105303c96301da670e58f68a26d1e729d629862e02e4152ac1b8eac6830aa0e17001dd561e2a9c9d587e15066bbb2c44e7c2fa871a6d98728556a2dba40d32c209629c71027c8e6dc7778c6d5ab5b46b00cc7c7ed581c896540359fada5650bbbca5ca2fab276a4bd0e9fe121be13d5fa2b979afebee8d7c3e7e5d91ef50714f95f16831be54dae1339a3f825e6336c2e34bb39a667463e3df7e62d8c83926f647fc694675561840c5782a74be0ad53de164c5727460e2dc67897b7fcccd2581598348b94c3462ccc55fe6a99eccfd44961400ded0add790108f573f58a7e58a9f148094c3798093c7550d88c73cf3f30666612342058f535c96b76e06769dab54e8175c8c423a2c229938b02d22c941b2e5568db970f5e600f616c55fa01819d67bd43799681c983e5cb540949f1a1c5fe4393f4dd4a8480e33eec3c3c0bc8f40ce98724e6d68d9c951e29db29b87c08d1ca11abe1162e0a058c5c9beec454425bd718c2e68722c07e115bbdd5d2319b99037ff801c6a0acb7c2a03544caf05f5ae71bde40477374b3f77b71186331ebdf96a06bf7bae02ac6270d270a661f3d239010c410773e92e1568c7abb94ca1e401aea237b670705dd99588f926451eb92808788b04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"264edfc955e05b56ea742f1dd462e3fb2f239698b58289bfa916017344737745","proof":"2ca43ac645aa7038ed0e7757e540f7a3551279ace38862c7fd4790b6b3a5140d24f851063aeec1a5c0f6ed1bcb8e0a8938bebb7562907ea797b20f1d6677541f7ca7bed3007b7c6f3fa8efafda5a607feee66edd93e04386d00358f0c2acf1283aab08bcb2bcae75845560cb769b6cbcd979b381c2fa330539a0944083c2bb62dee84d257a38a4b9bcc02c781b3f5bbe544a41b4c480f03941310934654f590074f1e209ff4d671eb9bee8463ae45ace6e25558fc9032f800d649d90093c890b704b4ca0adfa0284a63a3e08f4a26e9a0630a4c360d56aecc5dc31540846250e9e07ae3406c2076ab91e7c6d9c2345b72b7b4a625093f7a2aa483c693ea24910e846be0668875cf57ad9b4a28247b679a27f3823e50ca377b99d77f76d2d6a5f10ac1c7bdd60df1a87f7247f85f9150fc09d692e9b891e58e232fe96e8ff9f1888bdb1e4fa320d74f4fcd2457ffcee91c96cd383b49215524c65665bd350995c50f0b121165bf5da03ce9347ae0ce70baa55851cd7e533030cc3e0911243624aa265a2f19d0573d23215fe03acf3e58e0ad59f13fa6634baf04c4f05b3e09e42543a66502f225cbc3323d89dde8f09a4782bf70b269491340c8d45ee642ab56bc09ee7fe63cb810e168a145963d1bdc8803344c1b7af6efe28f9274aa9cb61311ede0dac2f815407af47a766f1f41ffeb0ca8293377af90a49418d0b8ee69d240e92047a6f81e23f8610a37569d9886ce4f427c71f39d8b6726964149bed2560ca5ee4d005ae91403153b3e4c0f429d32bb3a106be159b8c71e1f4c4e5ebe428f4b203808e5cd53504c04fcd0bf6c7eab9a99617ad3b7b97f208b6501687356c775cf625e2c5af32d99b28306f7ddecd30d07c3f5f512d99e3eb90a9595d4a009deebb495beeb0f2859614d25367b7c64acadb70d62948d7794879ba79014e0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f094142989fe5dc52a4de9c7ead9762ccc24f83a8884e46b40cf079047a81603","proof":"aa47b9356128f0db0b457b679a3493061eb710ee590d513e56fb26cb2728313b4411570e24264c07ffff57aa46112d926c1b4f2a9d9361f78805371d86da061752723d517caaa4f0c40a793ccc4864c28af1c14786a5718d9baa1eaddbfc9064522b9f44be8cf85ea53717894983b8923875b27d714a5f0ec360dd1045e9f92a513cc385ff84b4012676048d1a3ebc73a422ae33c35472d89cbcd990a612b4062b830698158932c2876acd03a026eeba5a7463d9a25a5bf100463ca7873bce0ceffc36c3bf4feaa6e2e2f54ee5fe26c019fc2276b23b8a69f49ec7f30bb7150c40246cf4dd0e8fdbc8755493698ad5a6b154d0914246f93529968bda67c27e5944a6441ee139d6dad294e3fe611f7ef78008d67918a3e3c65a87a82b5302903baab3ae599d318086f0965af5a277321fa023f08df098a11a983575942f2c377fda9121da73523fa7105f003ba362dffd7d8010d36c728df8c92f16fc69964c25489d92775c0a1b67d02171ea81a0f85dd4e984d5c003371e5f46fab5755d146ac0f894f4e8f257e55b1d4851d454f0c8b1ea534135ad4bc1bce7be3b7573912a308913905ceb94985ed5b3b022e201d3944dfcb01d3a970cb665eda6217c93061056178c45c8a3ee7aa061a525510503ba60d22bd08c1a81e2d60b6d6cb8e564742e5fa19994b828d63bb3a2f9bfd97971d75aadb04bb788dc819206e6d2ae40da9965fd8629423dc8a3c42df89d1164e486312259ae2718a6c54b26cd1b007b2079f1ea12bdc7ae8a24071e0bab03e35312a3ca9284347ad07fc089affc59642232c45b21703169c81891ee23c62fb8fa3ba07e2f583af61fe5893627d429437ee82ec12dc4567d2756ea6150052987ec0eaf9566b0a237dee6059b4bf52b0f5c1949077798dbf4e49857651be398ed29fc96dd500d05ced871d65160fa6105"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d8519b12c7ecb28ba93087b7d560273e7ffcb1af969979b60e05f8bcb7bb8345","proof":"ec75aefb3ddcecab9b594f8c31bce5b5786edf6fe434324c41c3220e1e67095d464b6b83085734f95a485fc04c8770ca613f21dbce8761dfe0d31be3f9ea845b307a99b066e73c5bb7079028c0c54f6c402b83bba97ce4c76ee93b8b64615e6812eef3da7368be524662f203733f85e9fb29583c8fef204846db848baab7ee4588d1de26fb6e511c9ab84b33ed9eb0929358b9786ab02d7c210f5ee9dd78c902ac950eacb618b332016c5798f1cf3977d1922762a3696c781dab00eb6dab8f0e3142e7b7a30ba2d6e3edbca531e0c270629dd5aedd66d817f7f2a94803daeb0058ef02ca7b383d5cd76f11dad799af50b7dc71c5c08344ebb242b60d38d28650baf8cee2747853930ede11fd53238432db0eedcb1e90209b7b2debf4b28e99528c1119a3bfc5104aa09cc92a713d0c574e26e0bbf6557d9f21491c22620cfd3cd00cfb74603f7c99ff386d335e30b56911c79c0408206859b8db61e12f2abf13d87c3c16851b36e9af5b38e60ffb02c3d074dd00f2ae452170df0f1a78c64c144821e00e9ca66209312953d6a7204b7ae9549e71f0e79c7890344880702635523c1ca945b24f65192246140023c7cd37d7ed890eef28772fab611f4f28e14a48801b6afca7c006c2e2fa925f14e57a6616ccf8e1e2b7bd3e65d772c340ddbe2d0c21014e0874b5bd54e9fe416230116f6d9e0e939ef9575329f89328870d295de439f94e1ecbe243053a506632a647cbe3b222724bd44efc54accd7415ce021ef200176cef0f69e2ca43c94bceed25301023223b49511d3c3c0f56bb9e5af10c160d3901d65b024af1b35c4f58792874441d2ec522005fce683b569b3cbc225a852c3f3e646e05cecb066de5cd580dbef3a4852297a7bd74db57501a91cd960492e5b716d8209e4180ad58e5224a90ef3c8ce4313b8ec83050a56af1cec1060c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d44b2efca1d67a846abe54ac0b0fc40a70dade955b669aa9a75d8e394a3e6d69","proof":"20ecefd16594df65a6a0fbab7e1388000534f2bdfd6b7d1c2090dd6c26a0491a764206bfd4a1b477018c98f555f990160488f37f463e31d482cbcdd095d93515acf2178663ce1dc317eeab4f6cc1818f086f7a5e1040162f7367dc730df2790304d92b691e7a6f85a060853ebcf958490b69c5745f7bc24f4de8e0f041856c09dd3883c640b4ddea38c0a4d8b0c795d289a094a93e1d53bfc8da409c2df8d00c827d3f0e94d66d16c597d55266f8aa4b08770e2c520c2e3c27a86ecd1bc2c50d1c01a6fc4514496b7db303f8cfa7806c0a69a4b57cafa56afa894510e2d30a0b6e409ea11bc272e0d723f82ae001f40156ad6eabb258ab2c19de7a5c65da1d3eaa5608817eeedff13a795eec849c343607985efde30e8ec62ee517321f3f517d9c7431b0cfeee5af6a9dcfcbd068232a7eb08b2a412a1431306d0829196ee07678b95bae0091f8384e55d688a55241e34711161a9f813fd6a9616480164f3217ecf706eaf6afed348e0bb686388ed238177c3609fb9df7dc5371f31de6e99608d69245a92cabcde8085e6c66f4edb409ceba1a0371af5a9d0c521c4327e6f6517accd90ae133cd12db7cc9e5c848b89efe842816bcbaae487ea980c310a2460db684c54ec290e7a5c446330443de7f445d1fe0a4f6e9a60c76920bef176e830dd204c0ca437c86516735f577eb21000990d862f7e460df97fdb1deb6c76e26045c5ad8170cf8f67f984bcf9bdb4ecd337cc0bbf247d4719c0a5fa269b4f952131076e70afedb61f0e2c388bafb5b4358265070cfed2987abc65fff659bc7bf1364909cebbafbe6e9a76e194979ccec5c4704eafe687f2e873293d0e4f591696438db14359aa9c46c4214709655b4c72c26b4fe6da1ec48a329456ebe5b0691093a817167fb69cc7c7a52fe49848fe8af36e6a009d971e291c6e660ffe8825602"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ce4270857c6e19e2f3e2ed1e47dc9c828ead3309c0889a8c826f8a1866f2b26d","proof":"6670519e2a473995714eecca4eaf7c42bb44aaf32dcc3c1c90b0de8a0604f728726fc680adf02d78739b9d20b187bdfa5b490fd499dab3336feb365526b2201fcc15929abfdbfcc8ad378f5f1f15ff6468e15d9784f277867b25001ae0beca5b2c3589c4fdf905aa08e335fa28d86499806914666cf0f4a8a0d96fe5a6b86a50a8ede6321ba238435f1502f1f95f023bbee75b795e30ffdfb1c72c78e41bbd041cc59b442ccf4f8807dca08bd55653b42ea60feadff86939114ada507db5c4086af47ed53c6964e00716cea844455f71b946b8f29827e2df563059ae74efa405ee475b1f17fccf7491dfdfb1fe61fd8ba1479c1c701bab79d9f8063f3fee4539d6488ec6f9c947ab0db97e904f8a6e8e520db5999be822092f08e6e66f6f0102ee64794b6273a53edbc212f5eb903852ebd1c397bfbc856c20bb665df47d68687649009d7fc28c4a2a8f1003bb9375679477c0b6ed315a6bae4d050609aae1134e6f437f5b2a02787c56c5b3a043ce913be89ba622c9b6f4be61786ec81e483a241dbe8aad85bec7a81762787839b3755ff769fa6022a7d9d66782dc84787e7a3a43649fe8bd025eeb310210d9a97b2c0a073b0bc9a07e58b6e1ec740c4e0f569cc64bc5d558b44d34b711eae9ae1b8cc640d62fd6c392dace48badfd183381036f55985066ae19489889c4553252fa2ce1b3358de8790f463e48a2b578fc414023a1a984e7be29806920c2af215dbb7689bb49fe797e60ff7c254e22691534f8ce6328954c84c2b993b6e40f2a88b1e60c6448dd7d802a3b16e9086e57b0f49309108d08337462d586d5c170801b64ae2ede624bab51a624eb6896228bbab7d83c8a22dbb0e96227666e16a07e6ee10a107b0b24b775df554d337d5f5df690a97ed3be2d8ce49a489f691594c943825eb47ab70fc2f0c43e61783e3e5357705"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"faa9053cb0fb1154d7ba026e2b6c6dffac59f5b0e0889e0daa638978924bff2b","proof":"0a998c2a3fa45b074f22830c7dfdeca2d78ef314c9b04512ba76c650f5dde57a22da9bdddda074a35875541fe2650b717a15bab73d2307615f17b0af96f09011421ef46ebf23900a3d2da735d4aaac138962c3241881ca385aae5a13766382227298719ff8f4061f9db2cf318d44c7b711078da0780e936ead7c91b50bb0705511bfd2ff8a30cecee56d0d3416553c4e984e5cd30488d12a36a2d0e8d52dfd00cde545a8110112ea992e7af88950c28fbd08ae87635537ee19cac1ef044e950858129cbe0161f70095a8bdfac25c7f1534de0ee4353d4aad8190e81d9174ab0c445ecdb4ac67263e66e56d9010922a7c40a3e007a2ed528b668ff9eaa62aa5491c6c4852dfe0d93029daa805d12d3856ff8003794b27c3240b0bb8d398f373547889a297a27c39dc358d1400db408e01f9a232535967e1a59992adb169f57f01843e489c27e87ae33c623c2701a60750a95b4e34b83c302a99508fd03a55b74458059de9dbc5e1166f6df6127c29b9a2ac19c9af01b0d270afd679a522b35108640d5baae9e0181343bb62aaf5bbad50c78c2eb0a57b695b3679198f813b6b757274909a424745e406da2fe8c7cb04b8e3ec7ca089a213aab1a6b4ce39e65806b233fa5a76bc4e30b85bc918c68f47f44665b2ce2df648a4863270e6d1d9e1079e5e3b1e3107035353eeb4430ff9594d584f3d64d116ce23355e1694b68502696a60c4f7db2f1e31107ffa8f1f9670081c8da03cc7c28696d63056ecf8571b25b09e3cd644b8ffbff95fd84b94f520daa44aecf005057254fa5c138b982bb55c9a174b03db9f297b078fc7dc140ebd38605be05deb8959d942372de2291af11fc7f4068fd245156661c37779a7202069771f706974834bc9e6ce5cefcae9260ad251db1aafea89f4c38e598481cfe30117e2416ac64aa2c388a76cd90229ed0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dc7fdf83323e9d24e5741751333b21f524612cdc8e60c8b0f080ccbb8cdb580b","proof":"c005458c1ccc2e886f593bcaab71fab3000e00355852418c0bca02b5afd4930142881c89937a84675c603a1e486d902cf9c18ebeeb0d2d418d2a92a14a7ad00cbead5947282b31bddf84e88f7e4a81adf0343ff76823dc8b29a8a4f9e2b5b23672415077c942128b20c43cd28e3c3b50a1b20d47348eba5ceef1542fd4e3570c70c7ab0cb1f002353f243ad9c5e3994f5e1575afc8ae81d36714e06e55eb1409a0b729c4d11fecfb86a744d61883a47de0ac97cbffec9140d0b7760d135ea007c1c1a78e087d5915994077eb8c500f119c66783d5299626fa8efb538709a0107e43d31c5729d660f53bb3fa16336ee1a4c549b4a2e05d1b411811fc336be02035e83b69a835ed07907379253b23b1d174dfc8183240b8136eff358cb7c441273a0a5574772bb1fd0ae81c3438385276fe2744f3129aec2856e725e2abe89ca7fd46e04565607969ac586421205de40d15a501cfa35f5406ca4a4e069dc45bb633e0d9a0d0d019fd2f8d849ca2fe804fed8bcda10be894547686d91101fbf1524d0fef5f421a9ccd087365275968c0e1eb5fc7e2ce9342d6acd9bddc102f2b040d0c6eb92901bf54905c77ebc796c8fe38307ab44a317261d69e6f1f8bd413160a8d5018744643a6d5de46c84365a8e234ecb100dc173997df25f1bd7789c746412de618d8a14a7827d21e9211f5d27dfd163c2234fb68e9ab2f92a60d8b910102029101de633ba6f9a58ef7af4793ab94f8c8d8f1586574d38fad800013cb66436314f41ac002125f980afaadbe240d52e3f0ca53c6cc39aa74b59648ef54e643c68a6893d1cc607f6668fbed4829d7e2dc7ce215dbf4868be9d84de343964457201843b2e2eb375f06aec7795552da2a178ad927e67f149d47eb243894fbb07358664fd37316e480172d4b61a23f383a904c1a0c9604fdfad43edcf7f32d104"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dce2d3967837cd56c2d91afd63602357cd368a5b8e795066f046bde114058171","proof":"a840c4e21b7f3ef29d66d23d5b524eb35f6cb3fe0f9905c098109774a64306489213402fcf66d6bd3fc33830cbbcde4d6b7d4159b5b5243d08aaba0dc09d1f3016b131693837d6fb002877829bece27c256e42a905eb8994cdae493106de1f19ce7cc5c087168508c8adca3eb39b79b6d05e9ae372afda06b9b74d84d0e8d17f379860e4c9c574ba38ebef1909c3cda18e320bfe347c9c745a1bd988c94c8b05cd6163e9166e17db795c8c2110ea8f1f1f35816ad484ee53976df176ca344c08f93eae88088d7d7fb7c910556efc31aaa2f1f25aa860a847b9504cf818cb4a0bec189ba1b8744bf2a0c9f8c822cf931c339d06a344b74111e16918ddfa86bc552a47396846a43940e6d7e6b71709b98295a611a035a995a16fb2c30edfef71379eedf13c79289f19ec053e530b1df1b2afd1d1976d3a515b907e8df2e9bfa301c81e9a2673601b8a5dfd363caec6242254b01dc67f1710f1c0f1d9769d50556ee4654720031191fdea4e6dd00304d382da741f3b634e8c035772f32966a04446d0e90c7455afddd39a018d757d4f5fb3fc0da9c3962bd4e4faf212be7b6f7419be8f49d564924cf95ffecbe4bc15a96731632439dc049a348d908a77526d3306c0f964910a0dadb201fcc44dfccfd3c842d00a1db5034349d5d3960ae91b5a20fc2eaa05dec0da1f7fcb78cf93ceab28d94e133d90bd5f1561498a94ae640b2806ac2e326e0730193fe21edf7f866b78a3a8cbccef543b834116f5ce7c4d542c964d993c8b9c10da395dd85ab1d9066fc1769146d57dfc50cc3e9584d8959b4fae1aa7f8b8cecd563568b50a7c35aaf9c669c50976570d9b3f2979aa8050e2616f715560f7bd2618d6f0a4bdf894f801864b638daf43465f453972a2562c4d0c2696db19993f84243bb004a627569bbccbe383c28ba15b6758b5a8dcb51f9e0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ec7f928e83a26ed882073fae4bb43e20b88c927f912e98c8eda3710ba874982c","proof":"10c7c5cff7654b33a47614db6b8065c091c75fed84bef2b742605e5ef9f4ed0d8260be39366bbd9528e5b62ff6178452de913f9a2182f85102a86d17f3cddc7e04dd481b4ed480f2279dc3c44b2bf2a837c90bf21d1dd19c50b8b5d57644961c82944141408d5af74190eb8fc5fc3b358c8d5a3547de0cb043a8f44f4d721a159d78493b6451f4df6db2ddf108d87e443f0917f48e110cb8ae3bb885b858970556db77a8b325b705525b88714cae53a63331625279db25a567b036e7b1e1af0d8f3ecc471c1fc77fc9b53b2dfb36195431d8d4c7574c729fd5f56d5c93fd21072c50a52f908792d8f56a85e1666e56fcfb9c2d03459d170a79d742e25d225b1e920c7882ba2b80637cf0d3474ea3928657779b4b872d538f8276c4a69c9aa90e2cea4bbe79532076280e02d147e7220222881a262c98621a915a3aacb6d1ff0032ad401d80f8f903ed7cfc9c36de56ceab8dae5e8554c192cd265524c7b1b3231e57e63433f74a2422cf2e04d62489d55058540c1f922083658725121b8f9e2ff410520907195fbe7f61ea1123a1cc93b0b4ae5a9b1b85f5c10072340aef364e90d33de0c9af0074cc6c9900a62a362bf9f389ff137952e5c2052860d921e01c92a4d21fdc5aedc6f9c12bc6dd8b42401ea360187f2186f88adbbf43f6adb8551662ed8d1d54c01b8e23be5c63214be200bd5cd4f7d2d9288b055b3d553608598634cd66f6953181632b7b2dbf19c5e8ff4d5ab94c8ad93a5605365ad825c34f6ac2f1baa0b3b90e925c6adf646adcd678a9cf55cf1644e53f4a29974f1e2579ec3250226c53c022b27e52063c1fdb85566fa2d4a78640d7f43f6abc88253a780cf9f1b4d22556081e47837e8cb2d5d0d161065dfa90d93e34b62e69ae04670445d072036df1da1f940077a997f220b5c06aa14fadb67f66d403cf9df9a2cf0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"48214b134e358e1a077e4088444a358361774acb7bfb7d5bd5ff1bfdbdeeac5b","proof":"00391a4f2ba29d65ecd475eabaa33e09acde211cfa44b12e53d62e9ee6966244caa4437cf1cd9547eef59bc17bd5ec19c1264cf7ba55768cc87384b1828e1a3e92bae779f2a14346935f48eab26d1e61682f07a831d5317eb56b6f84c97bdc7868f59a841d11e32db05fbd26493824ae51c2ad288bcad40d53c2dd985cf45f71b56c8a7bdaad032b19f47b4a57139b2599e35fad5331aa6043fa3f2729d07d0e3431aea11193fb7232b1b112d862b7c93a473b0cb9b6d4901f767a0394b31104bc18f17f601d471d4af3d58747416ee2da6bad119606f03205f899899ea6f206d63b98c8e5b5f10e1fca074d5480f8b3913a3eccb9e994898dff9507fcdff46860bc847f2db88825b53b8eb4566d91bfdea71464c2162fbaa02847ea9ee5db2552c94f65873d280aaea230aaed2a442392259a2e19370184e25e6c4df4375f1582af4799240f1fc6e41b5a100f97ead713332d6d2464139850ee10da65cf5e6cb61f2b427596c9c86567dc37f41cd45f2fac2a2585571acf84a4e71277101c4bb80e2b71e38d5181417da0648b2faa2c0fdcc207c5e8e498a46ec226ea911e427a785850548deb6b36672d70949523b7f070923b68064c0035271e98e6d19f14b0da2bff40ef2e345d45bfc2e772d26f109ccf40452b716bbb096f02bc79cc4e52e465c9061bc143de9957629b5e337da5f7c611f57c0e3b45d8ef435396163ff821937f0a9178afc87120328eceaca4aa80ce100631506e1f29a312e2e17146466f41493fdb6b5dcce71ab09b253e6b86f612d9db737d718a12d0e3adf9c6449cbc6677d69f4bd064cc784ca5f33e49f20c39923df97336ba0aba6a176d1e60fa44eb04be1acca43b934f5273923663855a0fc78804138513be18e6e18b700afef6dd20870c57b390d91b85eaaf0a02462bb4c2bd398aa196d4cd36b69ecf0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6ea9ae5aef4512afde0334ad74c1379d1b93edb4800feb07fca92952da105b17","proof":"427f412d87807795328f4e6e53af36913eb1a5572ac6f03b227fd362dc8fba7d2a68112ab14aa2e094fb10b3e08deecc27569eb0f9969f9dc6ffcec7bd895e77400bd8c900e0d7498ef08a2d03b206d2ff4598f654f2706b16f149c67869a267b810e8e1f0e5d96962d6a8a455f5eae3cf354b9278a9cde65e1b2dd4523ff034b8db7bf8f4d4f9a168a8d1b6326c2dfe740fa395a63883b72c43246b2092390d705256138dd4f2265efc7717e748a4a6d532309e26c8ab34daad960f9afa8b069532ed5dc55768f0fc018f15ee4a9ef384d4418ee5628a64f17c2d312b84d500529aa23cdfe34c0d00589d774372cd36c0f7f9a13f53262fd306aa0babb302144020ed7af96ea6b89ef2b7954a94417c04cca106098d4293a3750547d4030b46861b19b2c9ce46f214a59487c3838d74d425cc4bb612b42b7f6fccc9ddec8164580fc70598b36c55323a6c64a33670b6d2b34af7f469773ecef1fafe9f8801506a761c60501e815e4f72706fc992e6b93815f2d03563fa2e0a35f51139b5276856ef7f78c31348502e5b2ba84f32dbaad44109995131497718b5feda8b401553806c41f340f069e95eb787538c269901b90fd4653f0df545ad22d5737de7e615e24842f8c09d5099cac29b79f89b06603f7bbe123d4b7d998c477048b4475c43744a5a86afb6af2c3f448110d262d15761d12d4dff24af66e7abb0679eb7be5f56d61ca401199314aeaa1919afb3189605ab6aae48f20f5e208958b25f269f45a6080101e06ab045308cc70ec06df2cf072361ce811ba943cd27ca22e260224bdeed93747496e72fb5610b26ef4084b680a1ddfb95d2309868280ee3e54b31419a8898673a049db1b11930b9c5b0534a8f80d5a4a7906ea9058e682c97bf8200a774499acd4856d6d2b1309e579fbdf26bc8ba858744c0933f5477959cda5b09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"408ee01b6490dc635aafd1adb6c4a941c12927d03c5e8b9ce5bb3392421cd639","proof":"9a81c8f94586127e015f521c77766ce1fcb6c5a59f0886993b38aafb03685844348c131c6919988b51decfc54c5fe8e53e1acef6e701ad20bc9a6340222d2f602aeab2675e0f8a67cad23c1af209e65baac39fea503ec9777d34db1cd11ff137ee5df540ede5c906aed13fca90ebb4e166c9904933106dd450a7a11281fb8607a6bd4689b3b51bdba5e73cfda8be7b789d09f81bec01a5dfd23838b5d4803c01b9a9c32b5a8d19968e4203e4df38564d040616b407a1450ee98d13310b4bba0d6f2457df706d05dbec882e6ff264a6879382fa9b26cdf0832f881c871f352305de67372727cb90ffbc80e821f1d79cad29362d5249152ee9e7b5f0d8e2bcc11de8906163fe0ec0386a67c4982e85f9cfc1750207f3496d544c50b2c9d6da4b1de6ac99c8a0a3bf89d3489ac10b58b33e0ec27b33617551da0e67219f6c5cea0df24368a10f672bba5af4492bf57a26a7bbb9b86423125004aa05c5fafe150555fc77ca2eb8cc3b151eaa9d0ed40f03749f7bf0c7e77cd2cab3779bf740730f4072829d90f6080b92e3babb2ea86963e827a18b6ee92abe2206ae91c760560232e4a4e1184d14e936b452e9676e4f27e88ba16d276b3d10db35c43c5e1ec67d36ce4790deaa6767a26734027d1771c4aa711370f37fea79a4e54e30d820fd3a7fe42bf7fa957f33b6d821a41a71acf7033afca3c81130601b25ad549de0e30b1c401631abf1aa3162ecdd8a8981c46d6a05cd4543b7cd0b08346e7a2ed455364520f51984d92060e6b6cd7fab086d8faa921d427c7866e7095e5f0ed5b66edd7db815730419f2bc7b5593577916c01939e753cabfae79188d2f87776f24cb135e351f6c048786bcd28fa19c1a76aad6b619921adc1d9df6066413be8298ddac0f10cfea511bedde9349bfdc688d761a0a9ebf36b806d1cd1e817374cb4c8f3601"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fa25dda8b7f4133011521dfcd3c3fa1ce6276d46af87cc82e5e0535da33b1f53","proof":"d49bf7d010746edd0e9df83f3c53475d4661b872d9358439adea85a9b609ff1c9c4a1be488702da6cf89927c1a94a8b8e57ccb88e3dbac26fea5622a193a6968922afc421ed91ef0fa3d166b16605f4592577965451237d55902860e77890129dc20618a9ad144b64ab61b0a47e4786cbc8f0757758f50daaaae8017bbddfb721ceaaffe4dfd154db45a3439d3240c5dfcc97aac0de12eeeb409c1e4385ab20fb8ead8e3a84b81218df598fc3117eb84445377b33e5daecf7c2c85f72feb720a2e26681d6ca82519d5075162105e7050fa2c62d0d1dd8aa519cd5fdc9c148b0bb4fa4361eea0d6233c74162e3294602520cf72e6986bd04d23d683ed3b715951028873b089b374c842c2fbd28968a9f61d2ca096aa51a484f4576882a2a215567c90a6d3c626cdf568b76c546118e00d7b3b93f0d7b1c11e4ac298760ab1eb7124773447b274b2be1cb1e638e2e06290ab70cbbd7f8ee4cc67d61feec4e462048624b8859738f8c5e75f9a98adc64b569ec8ff6dcd2df28739cef79c56f6f67b5848dc5f8d7f3ad55ee0b688aaa97cc4b1cd9074ee9488225665557e7af1327d0cb709fb0b340818d71e77d3828377d0ed820db627cc61c5c4b18e738ed2e114743bee3478f9f0e5afec3c8bdd083aaf988410e0f2074bd9d5d920ba79c78e07dca738ec820b080403884e587c8566fcae1f5e8a8da50a6c66ac635370fbc119b69f9ae801eef964f1b8f81bcc01c9fe1d63b8785cf5a2ed2526918b9cb5fe6adafa0e930d88577eaca6f924e50983c80562194384815dcd1f31beb1c6fa0f3470232c8bbb278079aed21ceab9e12395e634ee3fded7fc2f3de7f824052d6773f6dade977495884020b51d4dd36246d239e03e9ac3f1fac30896f69c7636af073ba15277c8125d45e507bdbf0ab054ff1763f1d2da5e30de80202a010a220906"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b01dddbe02f77af4c08ec0fc87dc083bc5293b3f46a9b1f6bdd00bb4c29c3c37","proof":"d2ff04098a37fc4764b88995100874e0790973261dd777f1b92e49d9ca13895e363c6df0091c9ad78255a874b10f106f2d2181b425558f6e54a7d28ec29dea5e8a5303738dffb8fd9671dc025b0e36f76a0f271a0d27d663b9b3d2a245a6b01de650dcd076abd324a45d2582246765b6dbd2c312ebce8aada1e1d254c96c7f69cbc7af6505242e10ee6a29327cf0604390f62f013ea39b15376a630e846be6022223e56a31c22c1c9641f82fada0a5614eb872198463023f805c211c2a22f50f503fe1b46269a7ca71bdc2b341e344a7a47894ca6aa78b34d6fca7cdeaa6380f183ba718f5449208fe2fb8426adf852633984d97eab968178ecbebb421d1755a7a37ccd587ab122ea2abb2b09e3cc930b6ee9f78fd5d4a92dce8007124069f3c6a8aafe751665ab3f2813426ff988a506a59c2d3223d7774ba833e7f9ef84f6310b4018f92d9481cc8d516516b2bd104b63c3127d9111b89a83936b52ce7cb0b4a3302404f8146835945a614d9fcd8ecf3ef5fb1a1157d1f796befd07fac8b4456efa589691ba8aa1d4066456d46c7e98f297367daff6f5d5d695d9e7a5cf943a4c126f2bc8ec925c82cf793ae395907e21f174c9dd38ace5ace038ecda7811b34469a37d82597b7558436d4be09ed13800c5c6edb0f918140bc6b83ffafd34b382a732ed56148444182e722256e19fad063a3679f1e2a29661b8b948a618250868cbc0d47fbbe03767bc594ff0f1a4f7797467ff8c22c239c9ed2dcddc86e3fe02de335991362458fda1f715c1c39cfaa5e197c06e2941630a04f3b2ee8f745fc18c40b61c9f81c94657ac1fa4253001b9230e937b4addf01189462b7f6e17c4e114d5356b22eb116d2d54efb1326e4b99f41c91571de2222929f1f0759af0617bbc6fbf2a1def0e1fe95ede77a163a51da669b7c4a24c09701bad16e964706"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7e7e606f732e32cdc2dfe039e1b19b709e404c4530123936eb9529fce3520938","proof":"b6d8d89e1d19efe448b965ec20bf604a7975b6203ac9e48d6abb18c9e8f8775548c1f0403f234f98e97395c60edd944e52c8c9beacb544c742d369731ed28f1f7e81c449db51bb541b25d53ec8ab30b6a82191c1affec1c4dadbf95390cde445009029574a288d2cc7c0fe7ced21168603a8b43f03504c41261e5c0598a9285d7381b8e821e7a88880bd94d58a58b16b9402ec7b0e531caf180c327053920102db210dbc4502b7bdabba979d5ab5d02648ccd6fb2f069e4370ac27f0a07d3a01ea9fe4b5d7180bf15475f14e43ae5060b7adb7e013b1b1af39296979f3a5b00e3c345f5988f06b4704c0cfcefcac8b66f60b6cb150121327195739b1f73ca919f605a3b38ecafc7d2c85590ac5e266c0899811c43d0ef02c51a997969bd9ec2792f52dbfbc4f3edb940c504697241de4c258893ab7ed92b425022b1871801e54006f2174ae4b45062a017e3edab56f387a6394080ad59cf120851f381d631a36ee484d897c3b218ae386e974f5febf77dc016715a65535e90bd7214260b6272c003694410a550d517bb02d0ce8ea7bf2f5c55b0239b85563b4d2bb002567bf1cacc7c55786bd4869b676ab03eb03b5fd50b45f3d5e9219bd0083eb0fe3c6d3095c9c475420bc7fecb5b4b4bdf7f0ee9317184c0c56b894dcc14812e8c51a4d6448392c3dce71b8ee7cbc03b183dd0d6aaad5dfff02c75b94f7afe44a25a12d56ee76625bd6942996a351d7f7581d87691b19701cef54bf3f2630d106b6ea620aa619ce2fda1d15f5078bff6864452891e5b7dc4638fb8471d98b29a2cf20981052459c27538112ef7947010f1318ac987d39d71da534aa8243d7eac41efe44687615701c3faab16ecf37486586c60416f4a36fe8199e3ff642da0f5ccf055b0eb6c0786177da982ae4e455c44278a33f90bb24900ccf141ce793b1748769e007"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ccbc9a51b16636504acdf8d65c101ea5a8fd8c8c89b17faee63e34116c34db30","proof":"40f210b1bceebd6d41824a3102f4a8722b5f8f2b7a1496dc0200fa32dc82243146cb5bef6c6c2ab1ebf3882635d350074020e54cf85fa39f1ea266044591100578659be3d18756db6b8cb5e70f889825a642bc08d61d369b54e8c98d3b0c742168192cfd98e68340ba2339ceba88697f500a4f2c35293a120bf6d932d3b8674915bf2c2295ed4ea316ec9ddfa8e353af305e8198b3450739459459b99d60db006502d7bd6624f8aaf1fe4b05e036c63df464e81c76a23bf8758c3d5818cd7a04dfad82619b4be026f898cb8ecd19668a5903b86c4ad2352c40f028423eb4d201064c364b0be9953b23c4fc3e26b2c2234e9c5205dcf3f557c9e5f285b329fa5c3e447737d945e0cc7f92abd7480ef2e899083faa783011697236d06e2afbf43cb846c71398aff9f883bfe8c8d217b8ab62a53d151063f061dcd8720affc17b0daab102078a169d4b6bf591631a4cd5ae8920342414dc21bf59ff5eaeb963c012cc97aa418ffe676787fb8ba0161c1e9cfeeb2cd0bc98080930c4f1b85652932242fd39fa52b844054bcc6c94d93b06a084cb86b610b0e3c18a3a22e61fa8395d6ca1522a607dec179950f5f222d21a7ad623ce8fbf5dbaf31ba78e300b042a22748114f820a5606af8c6797634a9f99a0547344cfad2472cd1953753e28e0e2b446bf368e3ebb409d86807f6441919ead3deffa33563da1575ce9f83020a1517badb5e9e6d08c38e6c6dcb5df6164d29572f27ceca5fe0c3782d2c52f4601447a84cc6d3499658311001dd0cb6d0f889c729625681b26b7f1c4e48a737db926cb4e797e1556473dc16c07fb3563a0114b22da24631c4ae2d2bb1ef34f4e88d5d38b37dc0cd4f8d15152b3483bb1d6388558b9f5d81ac47981e146be6d492100184f64227a5191d33f78c4d04cc458222d36b4ba5e893c1392fd3f59eb61bdf00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"24deb3656df2706a47ff472f4463094fbc0c312337fb0834ce0eb0bdad4bbd72","proof":"ec07624902a72836d3b61f7138c328fb64970753a5b410a3a7e803dd69bde84d4892df96ede2c184b08082051e36e96ab0968a19b2309937f61188d43d86290512b3c0e409ff16801035211c1cdd2326b9ddcc26bb3a0750f7795e86ee1f5547024b0e93ed4c85449f8ce5d693a9de15ed965bd4aff66a19393815e2591f2316226615a0678959b52bf54efd9e0a7db539f1f82f67b2a23468e0e542777e1c00c04ba0fffa1aed87fd860b3a194401a14731efe637b2bc84f390e7c283ad5b06cb8b27b1f137b0b7af7024bbb69989136bf7afe5b77fec1f9aedb06ce447b806f6573448c8568e6791a9ce95ff196f98c0b8860d987e1d8014fc7b54a3aa3c462e72117e0c1c9f92066a510405de6d3361ee0229cdb285f1c3b8fe4800a6e74ef6a94c3bcb673749c5d18ce4fb3282b61947c887625b42c13615e0dba71c6231d8c2ffed52e13f2639fbc6e36b68f5979c4a1300610e125e8758eb3e46adfe1f701a86277e9be80266e4259b45a651d4a9560d803c5dbce40aaab0872645757cccca24e82750ab14f1cb22d164a7a122da5943f766c4a8dbe800819b5a38587c441ada4b173ad7df15a216d84b98c09e92d4dc4fdeae5fbc601c2ae4cb36a53ae4bc1c15b7294a71a9a5a9065c2792eb3b39cbb46faac128d149cbe762fd0f3490390bfc721cbe24ab0cc6a9c07d3e0a43cd20d6f20fb4ba0cd85bb64554a445dad6f3272a8fb2fef479aef9f0cfe8c8dfa1b3c917a60758d67b8cb59361976c6ee474ff10013ff9ddd0ebe4fe4911705e7edd769a6adef4b0d59eb22bd9ec09a4fc3337839d9dbb87d948e8347e8c70e160440cd23673de30767cc1862edf066837e2d37f2918810f779fb0e1fb96e204e36b6c2a1b74ebeb5e3e5d0211e60e3881ed64202f14db57808c810fd2dfc7d1c881c2e0c9e5dd9249ddf492a6ed00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e0a07657051e41f0e31f0852bc95e6bc022dc54eb77d03fe850ac7dc37bd3e5e","proof":"a84fbac534e890344db80e27857fb97ff423f58034f105adfaf83b4b111d2c2bee92ff63e152b45825ba5dda8bd1d44c2760164e6cdf85859f6cdc8016627c60b47fcf82f5606170441250e182ef8ce2537ffa525cd83898d155029aca8358220ef329599ea5f80e97c4c6f6e741301988c232fb3c572591818a8aaf0608137fa1a4cb1e3889d229a7ddc87d229f413ed5396cba7fc539bf2a5385018fc10002e7fff01639614f13617556142f71d11d49d1ac9cb89c61985acccaf33f1314000af08d6d07cb9e74645a6ec2ba107c3745c591d6352373f8228439daee3efa0408867d3033db99b3499ebed45a05d1e750e051d88dd6f4d2109c7caa7c271a652a9fd634c4ab5eaba3f74f5d317904ee66cae12a11e639818737799d4be1810f08d0525c3f861390202cc52666a5a1ad3f6aa69ca059d36a0188f7247270f87a62834aa49c99d79e086f1ed2e4764228e0adf1aa2fb3f3b71ba5195c211356384861417e5ff7d01735a5fbced756e18fcadf454378cf996de9d631c0a206b91e92df25588862756ce7062b4674d0f0d64739119b627b86d505852de652919467bc0834fb9a59e0d6bcedccddc7e64b92b943553032e78fa69798381cb7e5fa1f12a3b4d03719193f74ff03b48a3966a56132b308785e42749b1f1b88de409c6cd2f1de567fd86a491f14873eeab428ab4b81fbb643b5076f371f5ee725857648f89f782c9d82b0224eafe5b1aa2d3fb4e3ce9ab4097f16a89b3978be3fb81b2966ec0155f9566b6b13a70df40eff8ea2e2dabdbc40db2b2fcce7fef403bf9855d249fe323ad0e01f1970160da4ac741bd4ee3267e824f7964b0915c6b20e2a33b400987a4f597659e8cb8c0e16ec99f7eb29451b037019bbf520f67ccaee5b0f4c925490058c4cc49d2c47c3aa70b87100daa59c105a896dc6c15c7fcfe05b04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1c5dcbad445e040f298f958173cbc8905ba3fc563c0de79790497534cc153179","proof":"4490efbf79097df843a2d99f6f08ac4fc2eff60380670381348ccd841a5aff0f4422b89ea8128858714dae982144894723128d09d54aef91c4dbbc0dc721605342943f8dc9251624a803500130a4c836d22383a450aed41fab9001159081831bf2ad2e862414323bdfda75882d346786e761bc48cdda2dffa5d99045826e803c8805034a4206a53cfd53fa829667f62bd9b0ce1ec98cf764d6d79170eae4d906ec7a41880d058512ef7ca7f19913eb758b31379ad1636010070c7fa98415de0e8ab5f98f3a3b4e15785b37ad072d7df8e16bb16854497206174b618f63f894036837d0af97fa673f41dc8bacf4dde29fd88e339ae692e47b072b167d1cf6ee05383ed487795a771b1bb28f02c80093c11a1f8b61094c94fb0e11583ebab4a92a28702029f3a3da025b31a16e71116c5b2c5ca6fe221e97fd247f6888594117405071af02676063372de95db32ca398f735fd15099f112b5018210b97c8aaa34300adca1454847291be773b3ffa7633a0ab51c3d08b1232a455306a521e1e0e655490069701950ad574196be659d2b42d2355fbccf360d27499e4f00c4848977166bb3cbc2c6456fb300988e2a61c996204de6b4fc4f75d4786e1778e4cfb5a67865c0a5a1bd1144706ea4ceed0538ad11175718a27c6c5d4db15d3271a2c2817ec2993ebb80e8c1c295d3693c52a51265887b45a8a6d2adef987d26eba94394b08a0ed54922bde27ee6da57bc2227153c3ab42995f04774f417504bf71dc022c36f71a424d31406992979dfe3461b379397566f6f8d23f445c215deecf3e914a94b5e8b6d338b84a2e2c859d6872f79b0047adebb486cfdbd0efb6f93477632c449272522601a387d9d34e9e39e9ef2fac583c8757a6067229fb5b71e2aa2e06195eca912e73e8e56f887882ddccaeb048ed98d3075487cd88e613cc7f135f05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0e0c34fcb2968b989210acf5cb4905f664a82cbe8d31d59237ffd2a66abe5110","proof":"766cd5d8bc42e9e802b9a134cb32103dd7cb16edbd6cc70cacce3eae736dc30d6c514b0c687631b0eccfb7c065e7cc7f632c30005ce9886da498c5f71b1c0837b22c085f91fc0363563d17f16599d138062660530db1a6973b948fc162350e05ec49f40acc363fa9163f2d83ead512dd806f8c239573c72c34e26f47ee8dce45f9acfbd16abc453ac9601a77a4724e14ecf76d238f0aa9bb7cd882eed8422702663138578cf81b18d288cd9bbc78e64c8cde5750b45d54d1cb3f115dc057510ae72fa690da33b851d7afa1e80b17a373365afc0274690870232a52d04cd4c901dab7ab74ff4d3d194c8620299646a5a72047993bac2889b7d66d28affebf8f119af654ceb0a234fc169c5acb50c88d0a8ebc12dbf975732cc9d5ec1aa512f024ca8e24d62f1537df4b3b039b0988b0cb62c2423bbf504a34e2c8b9c6226d9f34a0227e254e819c5ec1e0cec6c1d580540a334baeb71c3bbafe6a5649e18b033af0aed728cbe5fb3d30f7cf33e4acb9e6d521f76cc02f741afee4d2fea1f82a4eb6700d0d06a380f98747bd54bb1b59558e7ed1e32d374616b39f65a44653983eceee0b5e4af50f47da3d19d8a6539f6595c77103be0042680f2f7daa208b5476ac5c7a9d9779935a8dc7ede8ada1be0393b164b6ef0a860714c9af9035d1615c886e060cb51c6a90ea62f0e7801800ffd172763fe8bf4d9e58bfbcaeb587e3641c81fc80777fccec10eb0294a8323395bc5ac2a64ac015ccace3633afd29df7c66438853f0d55d309e5921a601c6fc6b62c57578567f6a77ed8f06baf3ceed52f07f7ac591f8fa940a6a464de5cb176da7950c88d6da6699336edb3cc21eb907c82a49f5a217b2660d7447f204ec0ac32c79d0143c351799ad868618dbf7e407f15537ce99f21ee04190f1e09098eecaf3ed55bad9bea4545364889a01012f01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ec8db874227e6d7843fac5eb7f161550f69a32672bf2d4732e37eb0894c79448","proof":"c21dd94e3ce4bdb52e0f668f3721610a287f0a534e0ac6f43ce0202ad645551288ee58f201030caecbc59a671b76a283f50dd885919dfb6854313ca859bf3001dae1b6f265808afd72906dbf0b70aa1a8225131d4d56ceb2e0a5e27defa10f673ce8d3c6a4b6df26dca85ab324716b4b48c90de59cf372bc5c405739ee24ae3f8899bf09d3cee483468d4eedd22bf819ebbaf673a52191b040892ed0db1e8c0ae71eea718f465bb9e53387b07aaf26923163c62829cf5775be84f8609cce5d0d72cb951736d20ab2e1c66b8b5761bef5e5a293a6a487c9a919d3274ef8ee5e0a587ddb6ec18b5c6d1180627536e754e6733837b100387a1791544e2164e2f80aa2d4a097f057fd46ec414c4ad05c62fa7a91af77d46e8a9bb7d9e35da9f0917dd2fc56da884aa4d27d5f2f71b6a90358e012f1c4c6afa4b1e48b3e098741e46998900fcb541c5b81ffd1a685d9d0a9042a2858083201e4b4752ea93216c23a6c7c9aee78ac5e4b02a789918986e0e431730fcf477bc22a3c8894f1a7be3a4a265abe454a54d67ee68b7e482287dbe197d6823116270c9b9f7d19df3ff4d4dd504aa3b406a87e620791999934a576c2f0a1bd953b92576d3f5388ba2c344a9369e42f3d336902e7b6d01959e8ff78448cb1558630d5b52b9d6bcf17921663ea7618fe69d8a4f4d84017685c1697d02f4ff7da64398b56728f02d52d823707e344f42d4c466f925d23cff5ee4a469e7fb7a2aee054a68a69a5680902a0cd5e9920aae81cccb7a6ab49c224981b1cba73d29fb58ae3b40a945ebd58f24e2b52f4507c581a62308a2a11eec83fe086a9188b185764a0a9482d0573a2063ec536657c746ce229c37cc30275b96e0043711087aca52c500fb3e57b4ce964c1550ee305f19561c08bae7c1465fb0f51968e652abb980245bd78c6479a3122e461cc1d0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c8e8d00e652ea4c43264ef969fc09206b60aa26fe158ebde915674bfe96c1865","proof":"ec6ec2347cb17b72507ba55a60580131117e187a0870a39dae249282232c227728b22b25b773860527ae69d382aac9b26b32f0b4d6222f296dac81a92dbfba3d729c6869aa462eea94fce9903cdf062fe0f1f4748b25ad26c62c3b55ce754a27ae68c59d29e79224daaa5b515e29c10c8f87b43803307b6d8588e4ca21ab1338b321a60bd7819c1212c6b081a7b96d381b009c90c13bbdb41a5934b078e97d0545b841bebcb77a0b68366439d831b15ca7337d381875975c0f49a6167f7ec905b2fcc950fa8ed1a63b23c998bbbb10d4543206e8a3c404b19fa4de9250c6b80642833a3cf1edfc42b590fbf2d97e9a4e7a0eff83b25301117d4630e8639ca15f0edbcbf1b1ec7a74b329b13b5dcb1c28bbd94b6223e811beaa922d7e2e2fee713af1bd1091845799bcbaea6a51fc6f2a40dc79177a535c0ccb19e8827d2f960c20b55ed69197607c8afbc2d176b1e4653559506595955f2c1bebc90723a8a80a22ca08250a7ca5e5ce54182e453c7ea2879b384eb999bd0fc8b1523fdaa06f325286adecd0663b72f60e3b410e1166d84530a32fa469134b59fd078593d4ea148e355238947d8c6ccfd50656d25e8257854a249e5a03ff16d02337a7d63cbb49700eacf6eaceaacac38182c6a1184b05db3dacd945be428bd338d30d01e40c36e4003b3f29be1fd3c89b1e74cf2a1c08a68b42cdec2902f6d05a80a931d0ba492402062daf34bed7bae49a7bba1d2b72f840e3b9d6e179d674ac24ecec0cbf0ce404877e4ac2117065bb637c30b20d249722f6a98545f317648dfb86ee06b567d217ecf2ca01af4f7e4aa827f7024b7fb9b051b61d4415815deed29617cc4963dd3cd3144974f63d78130ecadce09b11412a2da787d0096f8abe870c0c61c00eb75c02db7331a351a9e00ac27ef0f6a279edc912e41e0f0e43b6468d14e8510b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"da5f2ec431a63ee4809a5d19181eb41a565937101324089d48218d3f2af6f612","proof":"ae6c671b29de71ac582116f7316e23c885d6b1dfed3c82e0309d391a192b4b263cf4ae9d02d31cbd824bcf852f1be1c3294f5891ac400dfa89352dd3bdb24a0d0eb29c5a6ce177ec0b88d1f24678b94ef6e1cb4e40844600b6fdee147bd3f37f123569532da97b91995fc7589fc7a6ce87274beeea4e80a9cd9648dbcd4eb96eb632dcab8700f5bff199111516383ba3c03c9443327ffc73a32c8370ca802e000caa5e83a1cba316f0df0412aad569e5ddf32c5e1777b4de344f37b548c498091554855acd0f05e0f77b715d82525854defa957765868df46a72645ff7c4050e4695bfb94a7281c983e4764d2d1a993c234167a4e0367f4688d3feedc8270220fcb00ecf7f91c4d43e98a306fd6a0517e57634f4889a049b56db269d1b83a20db480e9b2458ffa8c294f28012d9dffc7096cf23f658b8cc91c5cc029e4078d30ca63cdb704cb4035a599fdafd3a6c6394a5b586bfc25a878c7746fc3bf965e0e187cb748a7f1a35220b41697993ae3c699bc1aa790a09e5a483511ab1400690f344f026461d86f81c8cf06cbb80442fba299d56d24d28671c9744cec1bd3ec7e6cdd23d16981b13d81a5c4176286a711410d15ac2b13efe7859931eed869c0082a8f487790c1b3f93e87a9e2d9c7313612cf3d127eca755a1a58c2f342185a4c08d401f731177249615938b1ca831e4a0a7df556bb3c6d33be1321c2ffe14729020bd68556939d585dc84a095c245dac6f046d1072aa87d44dab286158497351fc24d26ede23af97fdff4b055661e23f5eaa5a2b7d6627cb1a3638d965f4202144fd00f7dce1abe29a3f61b3196c950000cbe91ccf6e141abffe5547d50bae2664794ec9c018aabe58c38bc99afda11822d7ca9023452f15c0dd9f8d51166c089784bf08c1953bf8e7d38163caf8065c1e41703f31ed2fb4a7595f1146a00b06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ce4d6ee8eb120d0bdc4ce83373e8c89ca0be3448cccd602488f16eeb7a00c364","proof":"24c554d80da60c3abcc965247f747f519f52e208592e9814acdaa130778d222108e9b40f15e6a250e39927afa4c9801b1098b82a40f157fdcb60287644637c24685fccad7c876e4f4eeff070446f568241653df5b199768f63a7452d31a6607526440135a9723e2bdafc9e5ee180489abe75c0c20161fcc34d37c9e2bb09c502708597b7f13470b2748472296a0119463ed643039ca267e69606a8aac3040f01f18b9c455a2efb564ccf026ddb10930b18a71a2ac737ba9d276a04e25c8e930f5aa206dfa6476b9c141db0a27289cdc07b11d8c83391a2bab1db7e2c2db4f50fd6747c9d94427eea06d3e69da7dcf0db439aa6c3302242f1f7f4b640c8419b436a99c6a7103e47f0bdcda0d0f1018565b375b132c370ab19d4d91459143ca133a8e5dae79b2a1bb42c2a54687afe749790f84e52eb281c3d82175b885b99c35ab0f45149caee67290226614ae6a30593b8cea69a7aa03c1fac241400630b5a7dcae9f140207d5956e65b094efe21d253c8f6ce41916e0832d8a2d5a94120df40c68d7d790074d4923d3e7b0f20ec855c299bcab97bd75fe0666d2c9f2ca4076a24dc1f84286e434b7c13bc1cd0f50fcd76eb2855c50fd8692d4fbcf7a7583249bc363564fca6d18fbd0244c350a1a92ad8d4a8a48e8dffbb7f8f9862eea159786484a4557f4277762308ecff455f80bdf1fdeed349eba6d61f81cfd3fe4cbb363eaa12f90f942ce1f787be38e467c0876c84ad8abc42daaa9d37159657f4997da69c156ae7afb044f961b6905851dca5ae5ccb984a05f5c36649fa5caeb1450514743553e4e366c15d9421cb7ecea410a6165d45e90d9094ea0706f34c2abd245231e645190f8655e4b63d389242394141a36085330b11145a2a7989edb31407575687e62463205dbe5ed18887d27752d7b5396799311c8f2d5f5e874b567106"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a00c8c09aa78e503dc9740f407eac9245450f7e6f8ffa19c9e469bc4f8255f66","proof":"9e23f25354bccd1ce1d28c4ff03f923dbbc284b7169c85fb226d90fc760e41198842fa6a588fbe5a14034bc4ee24d19f6402c738260f1fc1bd3f92da5c5c6c17920dc2519525dbb292099e1f29961fb3f1fb4751bf7ca37c5b2b43c14f1fd977081842bf25d495fa078b074098a010496fc69336faaeae0452f05f93044e484a8dc13a8f96d8045d67b392326ca32968cf838baea98b179bf6fd8f5543e55f09880dcc472c9aae567e2940678d201a7efb303b592aef8d22a281ec9dc8adcc0d50312165ede9b70df87500ca7bd3e8b71f803b1f9ac9b51767d78493b81d4602941ae46eb0a77dddd997acf0d85dbcbbba50946a7578a4dfb8a18caf2dbec963060a51ea66a78da38900a1516a8586bf08b20c4f4f90f3846df625924b87c44ac088e8a283be0f23b63736f3ba62cc816c94fcb1b8a45aa5d9a5c9a2abd75a6b4c255bc0a0812891eff5d8ffac0a7f156df81873d1f912f378c7d6b8e6fbe91956f7502672541fe6cfda1ac16dcf30c226752899a60007d818d4f4e2d91b4841b06dfaaf271843168e0d75b07a45dc713ec5e4534d367e6342ff707ddf57b03f70ae16c8f52a0fda74faa9b2aed87f41165a46a546510be389c2e17376f6c05100bf6743d90d3bb616eb6b9ed7ba10ddd9dce14921cea9c340fb58a4c312a2237882aa2ed20dbd19047b4ccaec07b80193a8aaf7309726c697f90fd04405ba4d5a341f974b45754a1fba9acecb29b1f34e96f76cf8285cacde49ed58ec9db031ced1722319b7ff0254c7951f504d63c8b23c49e20949f235a34d1e8b11b8c6350c901ed4a42943d17f3480af85b3fe5332ce15a397a52451154eac1e93c65f492a100c9ed97a1fbb81f4bc50603298326e479a407160c8f3b60756ff86fe3b03e2ae3013c521d777a8aeaf9842fcf8bb19c09e6dbfcbdaf310519da26f355c0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"022f8e0688e489773b41e81b6654fbddf60727f3fca361b917f12cc28da8bd63","proof":"6471162b93404488fb11f8267c6c79e61f95d647d75bd345ebef2f891387a8413c36b50166238db3a153737951ee543e5020a0a146c7295da27dd8677bf14f6406a240234c3d08a3fd6add4f65667a6262200c36b714ececd014dee405254f7c7453c5659de1bb7b1dd435d2e5bd754e6fbe1910db674b16369b58aceda83660afe813d62afacfc9f651ce5d0b6ce184a638435fe0ad81211645939664e04a02f9217f4dae59bbe5337e5d85b96b79f2ceee1d3799c1bdb5cdbd0541db9fad0d271d23e49f8fac245014af60ab998b81312acda7feeb4eafa282a0d1026c340798f1709cdc516c7ee7585eae9667be05dc1f61ea707ab3ba0300a5971076d92b94b8d49ce06dc108d4ab33a174f305d41b0909e94ae5677a1bc96d9a971ea058ce62e76107f5128c33eade577482bfa93c78ea9682299d009f85e162c887006cfec2292b02c29b6f940a671a792dea5af0587654c708d78adeaa024d6b8be8227ca0bc58d644da279df59027072424f59c2f160512378739ee88be70a065463d224e6f1dd4b6d2c6183355233903e834fc4c99a73fc158adf4231f5830ce3b657261a3e15537495c93c3aa1ef2f1437670d537893693f2e02fac9068f141f2528836a3cddcf1bf4f83d4d73ffe67c07ee15a6b5d5293e3ab4f68604c97e6181fa48695e5834ec930b2905bab24b82e75c192371a13ee53b1a15c153d11a065169ed5fb9769fd41ba56549dc5bade0d16d654d9a035367fda5d31ce19b79b7f64dc7103385510e2725b3f595f5035eb0c5b07a7e8440a0d872c3920529c00054ce4a196e419fe88037f55be920831874a192a7a670b565ace9737f0a890f343452e50dc96ccceb15996d40dc6c1bfeea4fdbd7860d6bf25ef0191a53fa83a7b0a6ccfec51f24aebc9d08a8ce588f29cc9d7cce419059ba2ed8ba93eeeae0f7606"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"38e9eddff27ca005fd591dd2ff900c3d5e0e0e8b5a64706adeb939017790c74f","proof":"80acf9f627aff91bee44eebcb304ac99cfaf112710d0b15569ad2ff3e18e6d3fa2283d101569de9a16d0c4a6ae8f66370a30a37b8af45808d0637c40acd26565b001b3c83b7702e66beeddb4180d74a5dbee0cae971c6984f3b6cc1299916133986a7100b788ab3a31039623b948769b2af4ca0a5b7278a13ffaa8f599211d223bbf02d3f1ec4856d9497d10fba09d3c66c8c33d51a8872bc9d6e0c3e2bff305c60c1f0437f9357bde6a14c74df19d1a0679119da58974105e0c7dd07f2fb7001e8170ab0291f3703853f7909f4f604a5fdf19e3d27f85ffde321a07fcd95f02a453cb5a79b8256851a95f49b58743ca30e5d693903d4dc670d656f51a6f67045a7e25a379711a379e71bf56a1f98cd3c7404f6a3e662078ed9aa884a07ab349123e70ef38b0cc85278b1d591b83e1162d43574515f5f726a5d04d696faeea212e1191ae0c931b3239cf64f5bd325660ef2d5042659da812547ac416fbc81955de663279a6c7f1969548a5c045f82c9717e16af4b11f39558985b1144888c233e43965bccf70a1d3bd6934e227ef0d6962cda204e585be01085e69c1a1a28b18a087847e42748da4bb8d8856f54875d2ff2398717293ce2540a249bdef828234f8ae1d750ce8dce78cb2814a88d9266f5ffdbb8b62ce2e197aecdaa6e6b07f03c44e335ff050bb4cf0d77930200a581dc6e726e397e3d0577ee762876c32b0382441d3e98f004fa83706ed3e5cc390e17fe3cf9207dfc0122c49d95425e5a949d4883f617661bedcc6f49ec56a97c6b7827e55bdc6067e97aa74c62210d105194cc35422a85353bd9c38472fc120326ca791e03046ce885b56ef2e1df78be636e7b10ca4e68c17e3a32d24fcb59c974d723d12d69155eca528abf18fa1b5f00df86ee6cc47042ca92d5b4a9691c3fac638215aa9e2f07d54b2c4a65524e2340f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"98132762ea4c1c6af5afc59909b7cb0006d0387967720f8c4b46eec17a7d1c5d","proof":"6612c89b20ebf36608f778891faaa1b74899a756a75d05ed63d6ae34554dcf0c183291c9cfa93e5b947417ea69991f6377441f228ee698633f6880f7ad0a1b1114452ef9c3c762ad6721ba2251512ac822158f7b54aa6e969148824203edd96e3447f9aa997a88cdb4df463755685e0cdd1dc8edce4d03da15b79971b650b810e9fe8baea361d85859677859129e8a4f9880dbdf8a985ec156b94fb86cf29e0032cdb9dd0d2b6086e4587aff5f534b924512639dd93c5c69119c71a3b45ab801188ea30598e0f6467e2f2de9dfc961f17ee0f3f05b9b0c5de09072e02ac323058ebd28d28c2b0071360809892680f9f14c0c17a4f31fb0b5b60c1b6c4d60274c028e931aa68d0afe1f093bce71180e47623bc8bd59051e70ebbd50291f69dc672624868015992828967081b1a3dd36963fe096efed118e479b22e4ef262f732fc258243093f72d6a069652ddb72dd243527f17e13efe79f988d32ecc3eaf212f4a0577be30140d98fcd44498074d42ed27551231d768c27ffe803a29f4b37d38b0f7f87cf113bfd13e2747655661eb1d7b12eb0939cf079ea8a41d0ea743c744800229997962203b737118295639855e9a0086edcaf29bc6236fa7ee1941ab11e0f41f7428bf12e430dad756dcbcf196c575ade78d39c7f7709596b9b24938799211539b5fb21fcc4218f64f66cbfac8200a0638c02e993669a1f99cef3fb66bb8c855147c8488a7e701c70b90aeff3e744f9595209b269e49a573e13706c11320eddc45a9b0e65f80e4185247dd869922c7d36a84fff642ba7302c05ba1e05eeccd192d50f59710fceba99b09223f4223809e72f6d37f0023bc439481a3ac0ead3fe92402e91776183dbcf6f9e9687e9e2fd9d94b8773bff7b85eac86e8d6019a4ea726226b44a216aa58ca66fbbc839aa49d665a1b272b42792475884d5f0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2495741eb7fe8128d07d4e983f74c24fb44969787a21a860b33b6519575b5c1e","proof":"ca7c5ea4bfda3717afa5f5e1bb001e9cefea1e3ba7d86bfe380416d8b84dd732ac7b652ad01f1a3193cc2b970371d5e22b7c115f2225cebca16527a1f4a83860bcb766b6d3d3cea9a66b83a7f72e95693c9826808727e28312ce37edde2d0478086ab18c3eb1cad5bbf2da37471441247b7b53693ae9fb00b987d79f6bd11c253134c7f84467443e71ad86cf099df0eb77c73c769d816226b04bfca2c7bc9b07b8fc36cbfb714f120b1ed0c65d68930da801e447691ea35deb5e9f2dac682500ec5319e1b6fb1a014c59be6c802ff30269c96d740b1098f4ecdc76de6872230a18da29d24186e374393c95ab2b1d9c3a68cf231ccd2a19ad1cc5cc4ae2fd577344c7863791a267c6fa30c7da4656d9d68f458c29a103fb2bdd13dfd004628e6afeabd7b1250fcbb878e492ba8b16a57db8937d30392aa604c728fec90f07537228b0b5f7b5030743eecaefb6447a2de829da1d9ef6d043e601f31bdb70e9133f2eea40b63d2715c13e1371780f039b578446b2cb4a88974a3fde739deb4d8d2cfe81f6ad2e82df7e2d52a6646a8a4c41a48d51492048c84c4e5b9dc7fece636fc20c0f0d8aa16ccec1697de804fd07337d0c7a6df4ea24a5c7cde778db99976a625cce5611e363ad4bb7d608111c92ae1cc389d821064cf1a1821dfa7184af5dec32759106f48411b454ec235ca7c23f903cd3fd084ab65e7aea7c9342a24317fa07c5d7d5f50c82acd0c54279e6c5878069d1bba61a2fed807bf36ccf14e7540cfaff511a1600bb63d06ea267ff17ada16e150cf04b0b18522d7ac39af2382eae534450abda5fb3fb52e91bbf2f02351f97dc4b7ca1ea714401a211624f161f736f06ddf079a674e02412fe771e094020c05e5028b837b3a2c5622f50db9a0f9c6f5e920ca29c7a145c01bcb04fba6334ca8c2d491c3778e0a2ff02d1fc8e05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"66eb094eedce0fe32233f82a101ce4901c35d387e2b142e77f404322ee6eea3e","proof":"0e66a7905784af7322f7a94a9d01f15b5a3b1afc9c62ba5721758d81d99d4e5c820757abc0bd21f5f2f4a721542e1be7cba66c00cb4c9f7d646a129b1da6421bea732f38c421e196142ec5ac53bb8d95b1f68158bec03c5d6b6b15aadb7eb674f6793754c973d4692b6f72fed59303eaede6c099dcc87f0bda8872d62a2e210e57e13a6f8829ac6acb666368b3eecd0db90626a05988802274916f6d3b0ad507a1588a1d5856eeab122e8834cbb521cb84976189aeb2bc77843281785b44aa0881edcdaabe5b33181d7be7a0d935df58c3469412ca8f5d6dc42a539f8d718603f2c9fd0c12d106a860c79619c8938f90eec5700596564e7d5e44ca7d29c18c1e9aa2b6f0206c8f09bf07b50154ff277aaf76cd74a8430538601de5a3554b2f3530d6d1d1c0c78da59ff50b817012b2643af39edcb883602f3d6c195f87cac32b28011fcb5be0e6f307226f97a7b48b5f41a525403dacdb042839e3efe5f1567b34fe6bfe8e740ae451b1470e7d35209ebc679cfd5e688c5c271339bad4456f13e025b233093804bae0e055f3ff91061287890fe4c72acf3f052f1836217d6b6bf6c6732757ba2f58a34f5473dab8b576b053a5e03969a4ee65c3e7b6569c1c30e42ab0b3c68088749aec076ee954f4c65338fbbfce04444dedd994d0ba42684a1a46a60038eb276118eb476c8cb4451dd63c5145c9cb48d2d46fee48ba05a35536100836ff7d28e146282f10c1245d4c846c9128525fe41e4bb72de1ea737d272a91f9e7c1573c92f7f1760b8c74880f4103c393c808d6ad87df448c0af46146524534acfebcb209cec5ca30b8545ca8f436aeefd9f72f055b026522a5d3e46e90022bb0d47d058094e1c2732fb40717a7634b48a15229a7a504e8621acaaf098be4421fb9a38896e64bd533772f45af08c759ce8223bea790c1c529ab560e0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c2d8e81cd9da0b471cd1771472e16456e3169c4a7b42ca3d131289b936c8b44a","proof":"460800fea186f2e1472a48f1bc149f545593ba179b1258ba07dda1778cea5c7fc806753b7b8faabf152eeebccf1c3664e6559dde560e4128751e13662c0c86404e68e687d58f4d58dae175a77248154d1a823d465210f512baf3981bb9a60542f49dcc2887075ca1424e6306e0dc2e6a55c7dd7f58c6ab8d75a53e2686cb030f81ca6d92f917d8c26636d698567c9fd4612c1d43eb811bce738f828d6ea7bf048bf47dde6442e754d4722d67c63db483eac834d0521801629768f5b8dcd4550def9e1cf614729c9d95738b9d9836580906c9f17997ea9d6d27958aea74e2ab039465af7739687d4aac20555a1c3b4bc0e82ba6cd64bed4690ef72c09dc759a355c02e17da971a6b36ce1558683b4688d7b162f457e2eab7a2651ea14ab7e570184c3556af1f547a278b670a684d975639c56afbe405df032772059c52d27e5534c69c3f5d74de7442eb8dbe2ebc04552b272c83b9f04bf06c1a7d464af55bb1c28675a02612f2bcb80ef7f6fa878f4dedc73a9ca86492c276009d236cdca2103e6a3dcf395fc06e0b4d1a62fa8d9840d015d9f0130d724f7e6e6e77653bf971c1622d450425bc127b760cf2c5b9d4cc4a6cb6c3616bf6bbb8e1b5fe4d61df433f6a4d3afd1b43e4c3ea02334dfd510d1f2214dc90e6dcc4be33711d8b651ed7c46684bab5b4531d06dc6dd6df77bc8535d9ee2bfc7bb70cbc7460355944df822423bab22199eae0118c5831e761d59ffb7de2add7d939139600498191206e27c5c52601a761662b875c05642489b5dc98909a60318f5a1654f1afb6705e4c168f6140a2aa933d9bcc3227419852297e82fccb5f18e5af165b1e3a6b296cf511638a9b78d22408ca12b6cc7d5202ca210a31e2addafc5449a3a1a38ce551b800534a971ad814b66f304628cb193ac07a135f2d187777fc5a4284e0c7884f73201"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8c5ca12d4039f043b0bface17c777b711cffb4090d39997999f2011ecd79112c","proof":"4ae93c575ccdde8b37c1cad7ae83f5402aaec92546e65e4b3b7207a1db327c7372a15693f3665d0056e3de5c952421c157c7a6d6daff805cfeb871c5a9fcdf2a6430664d29b4319fbeab959603b1b6f88b3b173732bef7e1c87494b60d81d16bc885e5a58acdfc68b609c6f2846e4791b9b9defe039a9e06c234bc2ac2b2cc7545c70e5efe1e5132633c455dc9e667d37ff1b85e3ad52d7bf66c95106702080e81f1611ac9551e29d4145a80a73072d9cebaaac0d71ea878f4fc9dc6e5cc12074b6be0872f13c8b07bf4d5ed768c7bac2a51b1729f4109957b802e3327f7f906e6ab47055118371f167193a60750e102edb4fc01fdf38f30ee4981e5beba734928b541cc1b6a4a62492ecca22958e52e18264763e7f4aa0f6fd1e52b5b4aff26903d206046dd34e702839b95e63d1fe91742bb785b24f23f8d7a99906672b95ab227435899652c20a81f413c30460da4b54fa8224e0ad2cad3396f7207a63f57d21c54a839693de0a1510e8946818e9a6477c1dcdf44fe1cbf27c46a1ef0f53b14879bd60cd4a77734a6834f1fbe154d82cadb5c193544d6a2b0028e2267a1465c4cae1d8491b7ec2c75b66d2e2c9bef0c164f98b4ba6229d319c35bc495997afeefc5dd42ae382a2b5fb7cb20c973d7d0bba81e6f2043588d31947a51e80234b6a796e1decad2432d8937a5dd044388dd54cfc8930ce6773e0ba78372081d59b2d385a83011cd0e3be59bd7818ae0bf8ce8395c78c871e664818dd87ce66d4e9c941c2db313996c31b3fa45f89527160986dd5ff7bf00daf0705a3291d63c26284c2409c36b2073ae58d2b956f8b1b84372d8cf5504432cdca12635abbe351100fa2b87652a3db186a704c0cb6a81b8e92a9894a2db24c07934cd5a035e470693a3b549cd2c01d57f7d10296d1b2df8f301156b3c9a4a085038720b00e96002"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7a5a5ddbff0fb3c0f38b26a789c482e613de8a86c35fa9ca81a876f3ef217d1a","proof":"2475f61747ac79c74cf6370572984c88942d0b5207031d07336958b78340646cc8fd5b3468d4a950c7b848c481e79957354d0abd5a053939749a62e13bf25d4d6a57a413b90c4081af2d2a914ee64be46332d32a14edd5a96ce423b47cccf06f8a735a80d95b54f443a1fcc521b7e652918da88bc0785f9b71d927a7ecc9d93ee9a9350bec496bec15161d4c08eb051c9fdc29b5a90ffbddeb4fcba047bdf50dd5ded96ebd52eb28f76449aea07868559ca9ffa62096b16bba00c265db9abe002aeae4fdc907216e24ff487133e02d36023bd35e4861dd9ac790c3f619c36a0c149276a96458d832a7ae3c2326ae76ca27481d8080d1a13f1bf3c04973c693026c986ac728e57dbea3de34e62bd8f436bc2f377c3751b3055a4083a801fa3d5c3c4ef2b0065c99e05510e705ab824f6d33d67a05f6215846d855dad8ea72bf113a00b95ff926a3d64794c66afde403fdef74eb29cf6ba2467fb2e7e313533a4e0e216fed4c2fd9e177b696788fdf910254b14d76f2faebbf88f3a765afdebf3394f39f72a1993aee6a15b225feb0cfcf920972b1c16df4cf5154c529938cdb5c9a274f47b3a0acababa705fbf5e0547722fd4e3f18bbc539f03f98bdbe91ed2d304e2f615dc77837c4cfed84f90e42ec91736129ad7897d673067520a685925c46a1a995ff2b641ab3d6b6db993256fc3e5fa487b402e006a261d181013541528e4bb492000b19bbf2e72bcc1b426f07ba3d91ab2825ffcdcb43e15834710e16ec139dcaba8cec96d44486a5a29db0d3070c5349b9021b3e16c48a23d0789065844c9306aaead62daf5a1c0a5a8ecb900f52e680099f53df1fa299fa4475dc0e889957448ea6f6a60046998371b3fa91314ce3503e250c02fc559aa14045900d82b46567dad89468e51cf6cd20c47ac64e74f5f52584517d4543baa1b850f403"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"62960ce926e8b5be897482a6645d05718588f2231489c93f398c511d04cf6f24","proof":"82cb184a06da30fd30f1aea26784517fec7f52d7ce761e6cecc5559aa15e981c5206b2bd486558b8c3f6365480c190ee05c696c8d3ef8161372aa5707ee38c3d768079d1878e46c15ea9ecceb044204a3f7f500ce38ce70cc22cb0e400acb84baa357e95462f9a5b8d66b1ca25b996e810f7961524a7508abe42ca5cf621df0856c9c60e418b7f7ff0f64a6e0b8f36294d9bf5a26a7bf3be815cbc29d0e9fa03123706149888b8fa34a1e97626ae2ad97a4cd4ab7980e151fbfef3b3a7f761074f59cee2e22d2509f8e3339a5657af7c3706b682ac07f1dcb06d2f48555da50b164f13f3baf74fb2aa69893a5702c2111e9838a43782e1a1abb4522d7866de28d4a03fd03d354c0954fb25937e23a0d4d3b2785476518404feb9456a9071595b7611831a198d494c74866ac4a4ec5e7c6a7ef028039b10f1c8656092730b5c465c4491d4b3010daeb276b72424cf64bf1eea1a0c1f72ded70f1c482e009c594c0848dad427d6901246d89721706c67e2351b80b148996580f67ce45b2189ce4fb67367d749e8e1af4f6147de7648bbd8f22386235f9076ff65adf2809f7295015c953a3d9c99b5ade067d78765baa3e26b2b257431e92e100a9362152ddda932fcca681f6dd70296614d59255b7e7b923e81a6ccbe04e2e60bc6efb131a12f316a5406314dbb2a93a7d139cd5ddd1f5d87aba9efcf2c3cac09c5ab33bb455063da2745455e3078f239d61346c0562479c1858c524db71b160eddb94f0432926c2653f9ef0b82a4c5f1800566db28240f6c0da83837b4fee175c847dabc170a68bab6962dc1ba8d17c7111d8b3ae7b57470627b97fc6e08e8e52065ad7611cb088acb797cfc89dd3b17a7f788dbf3cc9a7e7bf038b5c7c752e1b8e27707a3b60443b5e0d86cbd663c277aab1ddf7825ec59a99221881ea67a5c93afe4893cb40e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"74343b22ca6de47da5e9655911d34159d08ded140dfd6f22966d190d7f9d2b07","proof":"e688f09d796dd59234790d46413603601a061f721840d9b9a917142c4a15dd3b2c6d4bd035307ed64d4c69d7c112ef1b92aea8b141dafd6d48b980af9815ef12be23213bd29a4e03cbb13e9ce2f8a39354c0fc95b39405e57198973de8f55170a877488713b3b9f85bf5f1ba21660a63c5308b855abc4c0fc5db2248ac65f37114462241eb86b1466bd5eacab4e9566f024ed6d1a198f2d0c2aacf789099c20cfef8db2a2db4f2d8f9c0aa13d1b570e1d08b831390cf6c6bcdf7c288a5968e076abf2482f625b72714285485b8e4a4aa2e47d2c6fbcfc45967585880c202180db2ee66c1a722299181e3ddeed66fe06db6a74228790cdb6d390b408724509865147607539bea97e0169317e4147c715e6ee6027c31ef60b86f5c5ea60406f612d6fbcc4514fb48e3bdd8c4d813166f6a5a706d879801be26e3722724a86ef9406a7542adccdf4d245f247f6236a569471098bd8cf0f37692c0be1f292d7d4c1c3ec3d9d15a5fbec01821c023ad666e6e11a5f93ad89f656125a31bb84f27391ce89ddeda32dc5fb5e5a5b4067d0d2eb56ff26875c674d486fc40d7996e941f607ea069210c3a6a13b4dc6c5958161b00d79e368e5ac7abf8089c2583a3690f5caa8c8e0353530d3239775af9a3843a0af7ee66847ed4ece364935227dbca1a2256130cc96705a85ec3ec6e7299cc3079991848eac7603c7b37ba0ee775bf1723ee82718e3eaa906a4e0d25a397a5a12d99789aa4e962db06f02ea3c4a181f02a0a3c905bad3c3366e2feec6b841af2ba56e5d6877d01922802712171d538dc7eb66bc3967ec2b93f1ccd7aad079ea3a98b8c5cbdeb1824621921684bb3af080892179470c905830edf53467e48ca643fb2c59e56024be462f96ec236e92deb03cc29476216f5b18e6ebc15963a8d63f8d1f3e109120695566cd1fc454153f006"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"02ae2e7bb0c57a107ec4c5b027992c4383b4fb86debcb7c1e43480cef3312476","proof":"3ad44164fe7d771bbe0e037cac4792ffb4488c3e51ac5a454225343547e04f393cf8d02b81b4668ec9ab19b1dcdaa370a7a54a87872bfaea98e9384149866e14643559499c976b87db52bf10bac7819c3600b7c819602ea37cd604be039be232ea4a65fca4ab43f01928bf9e2bd55528d1772ba5c8d39acdcfcb69ea71d7224aca774f718b04ad308f0107efc17b1cbe7b6810a7f21a4a3a71d8b8f057559c0a27c0564188ee73718861ce3511d65dcc15bab9dfda087f5bdd90205595527505b790a1abf88518babac8e35d487514db05e5773763a2545fea1992e492162c06d4146a6132cc580340a76d30cfc30b7f7e4e25a7b2e3f3f3b4eb8308c071074f04ec1f0943fb534c2a95315480999ae5c7c2d28216a0718d5195fdf449953935bea1f7bc39454685377e31ec262857c34ec6e136ed2692e5f4d7785af5e64c232ecdd5ddc6631b47b9c71b7b78db36aef64eb9600469eb7c3edeaa0363d0f73550b2f89446ae9b64c8e9acc16a433bdd8ab1f470da7eb3a823101d30ab7133446616b6086d84644104f08a90eee2bc40744263d9273c0e7e405a00260ab88b1b6a03089a551d1a93fb74990e2f713009d8aeea264a26d6c6946e81b51f8e7d1dde6226ef13d7e338489ac25f5ad5d38e47d4f8baa14e69442a5640088aaa2265424eb1b184adb7db52745c1f3e90c71dd676114deec1337932bd2649990a46477c89640a13914664bf4e9af25835dd87e0366380098ee6d62e6bd626442ca00f48229125a4c1e90a41560f7f1bbb856aea94227a44028b376a0aa6037fac663418eed059aeee0b4b582b84adbc44a4c8dd17ab230c2b4dc4bdcfe1c0745c9d735fe33941692426eb28fcdea7460cdda307365f40897e8601eb0377200eab9c04636d8c3ee4841fafdf994ffcb376b626d1a3a80b699ef538c2baaf48af967b03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8a570aaae78f203c0e1fb596e3bb98669e19de6585158daf767c60f8dd43227c","proof":"acf146ad5e2f2004cf8c4e785615e53b3f83799a9e4230a4c92a5c730fbd806c321e38be323833d8ad9503785b17ee0332426e60ebdefa5e6c118da94754ff64d86c68486d01e03f56ac18e36197dd6047ad8fd447676ff858591d418c1a5d72bcd02724b11c6f385ba4fee14eb6f6cfcb5e75b4b3a6fca2c6e9d14023443f0afc26f8c6cc12713ac945bd35f48db47d193a39656c9cee595d0bc36001f3b3083e1fd3d84b0fdedaaa7e7c3d7d882a34eef7f1f5296495bb77b634c4fc1b4e0ae6ffc8e3da5f3f0442eef51c425ab27bdd12cabee4d028d37245776986801905f41fecf6a0e35f3266e3b7d1f7949574ff92dd3e98795c4056aa15a25bf69b15b2efa9f81c617a999586d4ab407b1d9885f2ccfe5ee252c6021fa9ab212dec5b3e61bb22570667e917874847f2de3e0fcb8e52c692e2ee248c11f2cc8ba8466360ba208bccd0d67a4e5d90f49e677a65b7d6c8fe02a9d786aa8f4f759eb47637cc88479728b3a96a40b1c3d347db67528030a2c294eaf7090b9ba34741b80b65b034dc431eab43d235ff9f3065b34ce83c6104566aa00779491d8c8872533b7cf46364e815d5b9b4d3897abd06c49dcecfc0b7743cb2553e863f5ad1e81aa014b847ee0226661fc20cea374178a0a57c7b0e33281339556814cdd421aa143f25b833169d4f59f562279ff8010f2375d4ec0010b23dff6b285712e081f2b93a4c30e47bd42f51a6273b10005ca58ea2fd4bcb9f23f272707834e2df3ab63feb592e181f6b395a94459fd623049d11c712a4f90fbe55632f23aaed9742fb83cd3eb8fde8fa3c170fc848a0120365cba920737953aca7bb7784ca16959f1c93314d2d13a171a2236946539d687c70f420559b043b6e1de61c1290a7a894b87450091451a31a7fa55474bb6eea05de105e80a8463903a6eded3ab9aa68e81306d400"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"76edf2506771e4edbefe6700d3b5aca61048b09304f4972332b008bfee55a949","proof":"a6e1381ac923371a612d91ae82b29cc42887b5ecffb3c237ad699b5360875051e01bfda5996288d425dd3a4b254880c1c6e632996afb6870d287d52485b09c7d2467e36484aeaa2a2027cd935cd838fc87012f04090825275dcbb75df17c306c5c18f63ec3e974847d1984f13d2be43fc1567679f3122ad59b1420be8fac3a58b6c8fac2e8a1010654a8197222c03008b3d798f55b5bdc44e509f2e605599a03780b9e31cb8e1e8657c0efab04cf62a1ef9c8e77f62f44d92bcfc486ca68a10af4d076dfb3c24dfb3df384f71302b3f91a28d170dd4a70d41985b8cffaa98e024010211b341bb1962211d504e726b286701bfdf5daf102052d5f3dcb0bc8634e848cafa24729249f083a515ad5ebf4d4eb019f357f23faebbaf2507e7fd0ab194e87dc6ba83c194e84597c69249fc8ebaf46eb9a9d094634bfd736e7a9e544076c7a09264d0897edd43a4d8859a6c2c5d95138c94a3aa0ec66df8776d74e6c560657ccde79c22e6ac68be89c44b15070e7c17e2788ee643438a78048d6d3c0723a1d3b4f827d65eac87046c8c9b638ecaf589d3f00e7cfddf62873ebc0bf8e19c09e4350d9d2b023f89fef218c1806ac43c6f9fc11e412888aeffc845193316d8ce3dd20ba1b3dcd638491ba5182d3abbad33b1d15a09dd64c1d051c0d323450b4a9e22ac4a552508b137b6278c30d2164a192baa9e0895333e383dd116dac746e919f27c68c7e9d4f9d0db133c78d985637dfa098ccb4816cde3e3c22f4f90ce403fbf208a9e0fe18ced1b0fd4ba7c16db1671d39cddadd24ae6aacfa50d31330afbfba8a73a4d70fddb50e621cb6d6a7b6e5292a6946955a528b73be59180cfb3d22b6403af97f225ae7d1d5f236ecf6c4b9405596d66e10425cf6de1fc70f19c4f5cf9ddffec7eb2d133eceff5387afcd5cda507b1f1667d88f09a8f6200a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6291451c914f633d426c81b1d6162ac53eb06e4cd27a218fa6d06fc8159e2d58","proof":"940c483654f016d47b21c59dda799e22c6515ff96ff7cdd8c85f15cd212d205ba222927a9d3062d11ba5b17e37ba2f9fe5e84ea9d23fd442d45c8f6e34c6d064904c7385a3a5d8aa091cf65751e7a2cf6042babcfb18a1ddf245353f51b6eb1416ffec8edbebd1e1b97458446a147edf2a068c3c3c67dae0ed36edcda290301fe196e9e2d9050fa206f6ac36a43c09c043717bda8d086dda34e159b46ced6c09fc564b313058b85c0b1a8df7bbd992bbbd092681b1431f38507d45252b3dd9024647c57be30cac134031ba6ee9c926d2b80f379d134f4cb4dfcba9b740b47004d85d9c73c8d211aa0a540d5591e242ed0881626e83741347036e7f2e7324c753287bab9fc3a1d52cc11f0c8aafaf4650a15adcc7c26cdd995ff07b0889a3be6502c5556a866fe02fc2c5b2d541929854e660e49f866a561ed9567e78d33abb052ea8fa3d573de4d9d8091e50c1d628ee7a764858ade92c7b7827c1579684294da640da72eb2e520bfc109923c6f52f3cd3d79d91ca26b2f703661b64bd53170b62da2b3f75a61003d3ebd4c569256492243f0a927972a1340acb872a9a58c933867f9816bef0e5df80e2a868da7225a78562e088c22319a0d40d9ab37cba9c76d6d437e47c867906208a5ded74efdeaff4768b2c08b650bb3250dca4612cad67c49720b6ff7462bedc9a174b45fbb162b2a6244ba6631af756888d19c4f33e3e660cd27ab3b4bed67919a0c000e9f36d5b6a3954195fdb5467937b554620ac6834389f4b7a5683cfe98793b1561f2e50b61ff7e53c142f3426fd73e2b8f10c27fed1247b013348f3710c2d541e7082e2e5d732ce3abfcbdce9b064ca733daa539378d2c24c266ac8b935be1db78bfc634d12eaa54914fe99cb72cd083caf0705892c09c09154975662db35e9d0d8f70a015697d21a3adab32c2a1d349b7e8000"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"36a5433ebae76558f8a156ac1bfed479cb47f041b34b4193107480d2ffceec02","proof":"ac897c3c204ee3aabcba1ad8df4ab3bb7e0d17aad021b6a6e1bf3734db16f70f74ba1808dcf2462de403beadd583580f16d064822c732f0db06c7ecf3afd522016a2b8630a36f005343dcbc2b09e7b323cc8cc03836f49769623f55872348e4b3696fc8e70bb11eda36e8120377c80a01fe82a2ba7a7091f0b2dc820cc358a5d177c34895bd81ccbac6f44ad88349db15ce32271ea41b334b558c27ce728770027a17b6fc7bf7831f53004c3b89c8e1c45d3be370c146a035b340dccbc2fb30f760b44928058d292fbb0c480b2e7918430e5e0d5f01477671170c4cfa79a830388cc3aa0017bf3c44ce6c7dbd97bbbf5be54d9fca8cda927825e25a3e9667d57461e68d5845bc75d34ef3e301d71fd2d8d9d5b6ae16549d1cecc62774609b0367ccececbbfbd752570c324204f42a5f22429a236050832c85389094ce0c843605e75efc431eb0839c62f4879f491958bc8046b4587324f39e66c0b5dc9d06d4966d79b89b975a21b0752417ab43d86288e890054ed53eecc7aea3f9b99984607544751fde3cb4087944653fbd99bcc9eef0cf7011806d76b3ab1bf22ea37ce5daed7a593a62fe736f12442601f9856b4f4ee69f95a49f3af034e3b95e65dac3d7c5a20d049a9e8031d457d26a9370d9f786da54f02641365a393d6c4ca08660052a31eb156262eea740c99887fdb88d02196dafccc1e753f51d696da4520c1489232e9f5734fb05e92f310439bafb1b9d01c056f351dc379ec1fdae0e00ac52bbeb912a62826c2b595e92c1484adf790b1d0e7473e47759796aa734f923b7c4e40f55ca6973a4773a59ea1cebc92bd93c31a4e5d78a26661d23bdb642c2f6c60569a57303b7c96dcb727c490035f701d8efa76fd2af0e660b9a06bc296dfca078cb189bacb004279232822c886598fe971da51cb9b3d0dca870707736a8e5d09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5466defd7822c3c2a413defa501a26a9579e5ba0cde963911277b20ba5ec1f50","proof":"f6ae53c41694ca92c6aeff161213f7c0b8c2513977658cd70ee71b85288949552c6f08f38f3be1f0db915f1d5f82455911db94456c771a11e35ee04d018e146fc4190939b866be7e002495f377ed4e9e45f61d61b454a77c225a4b38f7fc792814d1bab3d0634a2aabc19c72a431b45f86d6b7e9c254f2b47a5d0faf8bc8b5632f1b0d359a80e40872eb75ca6d152559133fe5af46f9edb41777404031112c080f458a0ceae836237a0305caeecad39df619d93c9e4b60850f209c5904409d0f975595d37d28037ec5e971bf762c921a1c602a3e946621290dfb97a397c2c50454f3dd4bbc0d374d36010d2016d6d40b8b9eff714d90b26e8880e8543eca2e2e64235a166756546b78fe128632bc68499bb5ca15b4ce50c59772c3db2a37f0124ef5a53fdb1ff2fc4a1ade1c4945e53d31247a4f3a139a29522f642e736c790cd2b32bb49d55fb43e251d04140e44ef76e256a318e88442ff243e84004e36d6038fafb347f85b185a8be583e5a06c14d9b766fa50a9c14f4c83a78141463623960fcf3c013caab3a619cd951b3829aada89b9e178c3bb4f72ae35d294a68c07bf4527b27ee6bf1d1dc298ad54c487e1abe11936cbda432ecf3fe83dd86443e1efc0cb94523fe875759abeed84b55336abc3ad0b437d5d30a8ea49ac7ba821164bc4239d3374113b2f1e90769fb544e8d85c08ebe74112586a0e06755a22dc134400daf0e11f9601796fdf4199dcf0ddb971c851530a36e391b882b1a9787e14bb23ce83c3452db833470603c7d7ef1d97eb30feeb9514de62474fcc04525805bb8285f8f5d879851ea0cec1158f617e57630ae966caecba1e83d54664d3a5e76575ce27c2186ecef1743f80872f33b797e2db3808968b9360501f52fe1d9e0020716cd3fd3b861ad81af5f4feea4eaea92067ca4f970c95fdaee7e8349e20307"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4a7d1e3a66e4762b208f4d5383830e2d3eb9d5aaa5284db5c88ed1a25fcebf5e","proof":"4a5dec43dd0c4ff8cc3291c7bf0bf69197f2957296d620adb471f68daca8b90bfe98c9f8823f2c373e344f539b299f769b930f80336a7c235fed676bb52f1f6fd2fe57ed9d3ec91b78325b079d6cac621fd5cfbfc7bf1d63a9f74a2236295c52b2ca79f9f162ea563b3dc1c68cd0d92fa6b4e90409056664afd9ceb2172cf035b01e6c3c9078ce10a6dbfe382d788072b69f8f3a6bd673d9023e808806f71907d55146b2d74e28e8b99c0699771c163b5c8041cc14aa936e9fee8181e5731f0ded0361df74a21a269aabd07a85283099f1d18381572f4cdb3ee980ecf32577068a24166548d2f077317c8e718c6922006c1add9f156f72e4fe04089023b9cb596a930fc7336b04c61bbb4bbb7462d93e0cfdc181f2dc326b4738bd181a3a0d466093e0fcf8239d19787c64ed1c612fa726d7e8e4cbfe7cb5dd90a8fbdbb8ba7d5a2b31b97322dc20efa733f2df43b737b2ebb1cdd4f5cb0133474ac8a6df1f46c47ba9d78f05c60a032ef13d9b2735f6dfa948fdd9275ce4d74bc19bcbb0421e6ab328de57e6304a5f4b57a8f381796bae67bd01cf6e5db4d18132f637c1cd7ca47360bfd858b1760be442591a048b7d036f65e4ca0b32d6018c308dc391043a8ed30bcb5ae02ea6f8a07a27179d5e2ffc6b049a19542be942d97d8d93bec00b7c5e3bb7cf7849513b87957621d55b4b13b812dfe99639799a265de7cfd2bc091a6ccc5bdece39eb68aa476355f7c02897b19fea16706991223179c76129fe76b26d44f12c775c8835a913ca6a227d88ee832d9837e145d168f37749764da76628c8ad90df1afe5a433dbd4f21471591ada9742a3918f3fcea22770deaa52b7b0f00e7e0303d9ed3e7ab04dff26c1be95124cdc6d2472f497f0fbd14b0d9e801663a054d85c47913f51e0a9d55c6798a4a886d418626aa39d851b49cad32b60e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"386ef6af9ad35e3606cb2a21b90b5f31f44965a97eeee2ec735b4b3125b8d43b","proof":"b6f7aa8b15b5440b571f703e0c98bd7ef7293b81b9607e81e36c06f70be37e3ab6dcc4ce08f82ea0dfcce9830924bdd54bf792d383726925e4ac50cacae7bb57382206a0cb2e3257b4738f0059ee5c8b730fd312bec2fa426b1b81b3aad10f48ee4e8d2b8c1bd17f4bb664e9dfe65a280df23146360418c36ae0015b83e4816968c5d6eaa6c8e4e6f6c4cb3f8420f76d20e40e78e31401cf48339e2bc6ecfc00b75a9b5fb06501ffccbd30c95e4e7d0d95bf2fe61d105a4ac15e3d7e4ebc6807c8ed59f268275a7f677bdc5cd05c8a6ffa23d1503ce2917389a92118cce8ae069036b255709a98bd29585edd883ab58cf0f68221880fcac691849ca9696fe039a64c125914e7f573929ca56dac444ceccbaf8338db72d6db02400d0af8b5fa512e14011d4d26ca13f1102a10af38d8c7e2dfac03b5686be52c879cf8875a08064ef212d6df46dfd08b741ade196ce2fad49ab22ad17940e4bbb7f8995c88c9157405fe7b5e6f40e486ccc0ed842c59d7668f10db8535e7d949654475a60f381b22b78542fbac095a07e29af804699b2b99e2fa157224ef5b8f6698b82bcd4f7260705543589533d47c54509bb4ac7f0f53ac0749815c5131bcf47bd528df325476105697b1fa520d213e778310f5398665a8fc4ce7bcea5927bf44221fe1a364e4246ed03da732a75b866f87353ff619d12fea4fa84eb3f454d27c84fe31ce6a506ef28b835ecb5b141c442650b71d979105fac0d25e2a75ec83763bc91ae61072b8666a0cee01fbbcceeb22753c3c73907c74626524fdbf03c5382586808c45b41c5cfd04babe0991c6fd03a992e9534dd9699ab7fe4af9aaedbcf17e9f9a372d7864cd2916d5e950c5c8ba46778a674e6d8258fac062b9b97b4da76037510e607340352929d27824a45855984bbeefcb75035542448940098178216d298c07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"de44a867eb2776e4380d817180a1987f31b67e646b764e6b507aefe13aca7466","proof":"4a6919847a48175a7bbc183febcd4fa9721369e0cecde029178f06e85e9cc71716784bdd78c451076c0ac4f913266d1cb7426680eda69ea03dfa3573dc500508a08d29587d1a5dd27d5cc0142ae89933dda8e5a851b2e15578b3713abc1e8c2d5a9ce91201e76e746cfd019bba2350e7b927cf3b5b333c5d9e99a8bb8db58334203fe804108a7060b658a04b83792bee21e84ba352b8a3edee3bb42515d8f50f181b503c93c6a1f3bb6f41eb6740bc468c2a81d168b9d478c9caae38d90612057f1538224a5915658ae7e3ea635080743331198a905c9130bc5b24317662020dbc0fb065b4a5c6a180389cdd990c5c689b1fe60c5eb06753c4f79d2c39185703da8a93749ef0a799238e8ce488c701a66a70bef00cd0bf1ba47b468dda0c846eb6b162daef13f2acc1f37ee8c46141c91680182ff1e17590f35ed8a58de6e378521b244dc5452eabdee40c3895f9bbc70af9836061ada81fb96ac4ba36a8da4490a2643e5abb70edda5c98e8095559982dbe68bd2c210cd6b8bdc0a9903e04019ea4a80b90cc805665dfeb875ab113b7e98d323ce56d595fbce740fb00702e357ca4055a371a02d20aebc396cf9a81bd1e276929eb9792dca5c16c175b62c06d105f2c9f9a8e94c32f5b0fb40a58259733162fbca665c36872a98aaf42f81053060dfe4c035a4f43c9af4f661df0137985c5c9eec6a539bf3ca17d8d4dc5256b26040273df3cde48f66c67c923714e7676d4af81c9051b3dc419646e084af656a8079aa5f9d8f093cc7b1d0c0e89acd6f07b8c623fa77d01b235835f41a518744af8366864a9241f01b19f3515f5777311db9141b03936dbc818e45c56302105558bb1157d4fa929ff5ed5848054b6a8d0c51ec66a0035b0570263eb4a4f1c0cba551e123a8605f8f3a81be3fe83058fac1c56dabf6849bdd72672d678b11004"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a2c5677aa89a1deee0873b20d14421f7619596f5ed104820ea439d06931d0730","proof":"46519659a2791c0ea24cc3243784732c209cec409ccf00727886800208c1c71ba6f0fc08f0ccf0344e99681a0be9df668d4af6c1c5cfeba8d31ad9ad40fa770cfea8cd06dd27753b7353d936e0cabf100887e9c4a7763e6d054518e46ddb1e3ec6636809b1210f973980507f199c095c163c2900788dcdbd47eda473fe6b341b431415e06eb63f54e9435f495c9675b1bcc2e5d3e35f86b9b36ce57b5f79ab06a92ffaff700eca5f43c3938306eb3ee1958d285f19e338ab50bb965a14eeb003a8cf616cdfbc1ce88f5619d381459f84d7094aded00c168ae95f73928ab04b077c86a2c1e8205c6344f50ddf49805d0f6c9136e82bcf854d63028a480f46a95eeaeaaefe3f3e786b4c80c14b5341fedfc55b709dc2078973e7a54b70686fbd1774ce464e087060dde5fbe6ca9f078ef8ac79c0015e246cac11d3b0e84d32b158224b7575a99827f28d339933c0ee92de52552c0b69894dd591f3053b9c13b054e00973b356fc7c8c7e122d496a74561b3f65d31c95a0b76ddf9245b88fb1fe5dd8088baf83de069d6afeb92735c4814d62a84014c4d2e1d2ee3c0076c5bb614102380b3757e1b7962ae37268b246b455b36d86d46689d9383af0ee5cde0f290fc2f2aba5a2e346567fb4dd4fdf234f0fa4108a804bc1298472e87b6e2e822565ecebc1f6b17dc75f038488886826063de798f8c9a8a4bf5e91109570a8b9493e242abe82bf090c8b334957b6e7f95b518b3024ca99fdf8c059748c7374aa8a131c16cd1d05c94b3b21f5cf7cdf851f566532518ac3aa70e6f4865cdbf557aa670a856642160c32e372937de669a87eb9e058775d0c901f9c3c3635f65239571a77a29fb7d2b1f47391d450cdc451acceb830c4c98dd7040447cfe32fead5b907021ba235661d4745b9feaf32b48792770429ad9b164344ea7dbc366eb0bf9908"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aeca108a8ac4567c806958ec94322de53cf2c6474feac17a6d37c5d8762e5f0d","proof":"48a7c646ecf57b5c59eef875f325c9e3208251244bc8c0e7da9210ef7fc4876fe21027db54303941f384abf05b05df6671e3306a30558981e89b65f0a61a7c06c0556d6733ad81d36859f8e84a0eeb0247371803465ef727d7a9b67bb7cce6648ac21d672acfb77368961f9c163f66ec75bc65744ea4dfe801a9c62233ac1722e652cc01b7c5ff2a7e26301a02b1690ec94fea74953914d7d2311e76c851300bcee9f31adbc533ee0a0944906ae7acf55c538756e46df24ee6affeaefc504206d08409890ed5f75e161e98ae08c9dc249a5ce92060ab244eabd454b66fefdc0464cb204d91394c2c10d3d2221878d5d7bb076ea6bc861022638c4a01aaa18d335a49f2b8a2f67638a91fa7d48df4670b2d25adb7c489b03d3e6641d3058434353e1cf0150f653229545dc1b71e5aef89890e9e95d1723ccd87af0a80a3f4f74b36b68112318807e6c401e39643d505b2cda6edd8c32519c255bb8a0ec3394b7b28efcafaf8b236c54a426b9e590796919c386077a4fdecc952c78c7057d3c46ef814a1c57b41e121e8cc6b1e69865507f59fce235f7708a2b8b7b0e4f4f7e12daca02c371278b69509c1467ed3ab27206469c46add05f1cd8d64f1b5ea07630474d4fe571cd95d813f2630cb09b64f945967fe317396d6f9d515736fd14ab92bcc4d17c682ef4316009158480819f7ff1b05db8cc5a208538fdc6b256f34b967a608a1890d048b66e0da067682f196b853d15c402e72d1c80b8cfe5ffa57c35b0c76bf2fec83f4c415208131ec332da81603ed1176ffb10c1a40b672c81bf43a8aac5025045f53166c4dc25951fe768a4051a307a6e92b8580472385f0e84e7980213e2d8879a25481cf06b5876f60231af1305e550804ad61b2757d31142c0cdb9645d592cf1a18c9bb4aa162c611a5c3f202357d87d63e1d3a74a8e8e6490a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dc53572d06f19f84498a23d38386da1d62402f1b431203c16f7bc9a839208a5b","proof":"70ed94882946034fac7b1e6f28e6f929c04ebc92866a7b27d9f84ca3b3b6351f6841690f7ee23963b3c79d81690cf4f13b46abcaab1a086f45bf474c921fc6403a457b4680fa78cdc85abf0307bcc059133f4d00919517464f73ea17b4cd2d17fa49c73e0d4fd7a4077e51bd645f022276d4f3f96c870e0b6b90cb7895243d45fb52469ec0a25ec407b4450549f0b0d0b63a828a527e5bdd16649c8d4eadcb0bbb862948a775f4816c3abb20d8e294eeced1612d198e706dd835076f3424c6074408037d62588efe0a8da6324c27f90a467896c1dce99f3016b78c5ecf9e7e09540d7d6cf5addc0a47d755a2f257f09274e8a4b74392cf0b70c4ae157d01fe7e44a1b4df28f98f91da8f0ca374826e17f8527712d486364f3549cf397718d13c0aabe21e487cbaaaa43361698db7daef04dd645c477f23dd3219e887acb2c31554de693b91f35ed72b2bd59d5b8174ff22e53a0e003887d1d9921e32ffce6c0b2423e088a4174459df813568b7b4d1278e6927efcfd2450b263ddb85ef67312126eefbfa79987532d061fee13f2da66d029017b6517c2635344eb77c49a8586d62fe08e13b7559a19d9df8dd24d33d0a16e975d0613a2c69f13c47973b87a005e82ec5962c9eb4ec50d13aee742017d7229d0c992c35422f2a4743471975c501a81a8666e36032ec81444ccd8b3a3e921a68aa0bb10e2816fad8ded27dd9bd2502914b7a594d78e18dc3834fbd1362a6bea0d49f3dad1571975da99f8445a6770c6bcb49deca2c7075fc86e98e5e7f9922bc207ae94e814677088ee1e0c355520eff4e52b5c2500c5080bf823493232b22f6112cc67e19b39ffbb041bf843566c694dfde6781bb98ddb06b793ba23e046759535fa076bd0d60314ae3903aff0647fa2d54127087c4b244ad2050871da5ed1d3ec1bcdf8ec039384cae5302a601"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"24eb45e79d8e40e19974ce70c19ce90c3ef62a1fdeae57d928dcfd6591aef604","proof":"f2270a9316e914593de09370f295b153e3f25b10397fef253ad28c6c7e231263b42f8cce71df84f8e6822fbde104a6fe5257a49c9ebbd61a8863b0463257937efae9f02a7fc4b12d71a0277feb6a62bb773f649428d6d2d7c1c86593961da178f4f57b1e45a57f9a69debee48757c6871522752f4ea082d19863ef6ebca7a34aeed02d538f3574b3d3b40b0f7131a0dbe2b2ccf87dcf7e327a5b877cefd44e068709f59023473729514491992743e1181b86a9ed137aefeb937d7c1fbb837a003dd63e1e530264b9bb078c2807172fe927da5b355caf407777361ab392d9bb0b7437a435591bf0e94ba5d02ba09e92334e5446c8a6f9aa098ef1df9e12bdcc69589118a31cba264f3a8b7a9e80156112170e7d8c9f3fa7ef3ec8c4f5f7cf02079e9f798a6565b4cb5e5810ed476f712529dca1e4469c84e611a7111361d883239028957cda7ce05ea28bbf93ae2bfe909d75a9b1b9fcf74f99d5264edec87f3996fe5ef77da3cc9ac089db8368c7022375bf0fc2b684afd3fa0da5a8bf8d1276a82d681c6981b58db0f81fb137906e29908742e6ecc138920c3fbeaed961402cccea49d109c6e4bac70c9e175b1430d3ab8e47832f2bf7721b3b1ef3c5781f3c0827e1494e6001687a974776f25b689e43f91169da6fcbfdd05700efeda6936c5012c7acabf3463d7591cf127609b06fcb2b9262f32e3ac479f4836a05681860d8a200ec25cd5d0c511e09f632998778ba436a744b8a8f0513414181fba56109b0c95af372fc85b49d6b8be748a66288d9d4be4262a9ee5b34f4c54b7123580e069c4fdc301740eee4d7fab8563f5969268b4524916e600251787dfd53dccd7f038c356d42667034c1bb7c502299692df6746d9b03692e8eb6ac433835fb010ab071b77a5853a68f1b263e5a0ab013913f1b0175dde3115839cd7a38e1799802"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1032aabf6d16b5c3f08c7d3b37d4ce6a25a7f304cc9c63d73368f42b309d0655","proof":"1ce953002644d8c793f6d1e04ef2718a658a340d74fd830a9ec403aa04b34824c474304e6492180356a4000e6c4d3d3c71917feaf9364a796590792155fca22beca619e1d35ef949574ce70084b2bed6e1c3ea21a93e8a86b810fe69b3b41529e6bbd5fa511e24a6cfefcffa5c7769a0d415a6f99930e9b0a1e53c1cc49a315f83e03f61e1f8dd35b9a5ff26e88842e7d1e345b29104be0774a14e7f47b2990c080b353a809805b7fa06751c57b1b6df7875ebe53a851db6aeef72557219c10dcb400aecbabab525e4b92faa15fc4a567f21c3728942030bfdcac5fa5f65860262e45ae4686705592f6312f948b65e479e4c4c0b86ab02cc02261f6daf07ac74a0258c0ee3fccae395b38cb68e89231b4efc0aec4c738886ca1ac52076579f4cd0a51862fdd91cff0ac7503de06fb772dc5e7a33ad9555918565ada0a69adb4e42e81411cfc7ede64729330f0958907809c9de262bedacba54991fd60dfe0560ee1f9471cbaeb05198683141ab08042da992880d6424e3201630729779876f1aacb6bd3124b4a3401072fdd0a93115a77456dcba5e14768beb007e508856476c2afc9e5c521e935ce44cf5db4f6b976b1ee080b89cc8abcd0ced38f32984f30b4021f1493e92aa96e00b7f197d40c77342e518409de8f21eb0f2f2de6105455e8a4685728433e8db4451ac5de0e43d52096dadaa87e6bd278a87bbbb94c30365d68a861ab699b411cde7532e964c2abc587635cc015ef76bcdd625d5b759b164dea0a81c477edabc09d6320f23034a8c9328b4e4b8a3ffa2a1b91c58be204d04a8051b54adef9933eb382ceeb61204cc681fc2b16e150d28058f6c295291e17af5f9d5ca3ce9fb5b758dd283c03ee95530fe97128e0237bc2f0ca3169398450049768285dd36606ff2e1be51019c49d269a5108dc47dbdc73042b9cd6e6e0b0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dc9cbcdce57854e750351c7a355bf48e3b85a8ffba424faedd2b252877b48359","proof":"0ecff9a9d67e218d7525502252b39f2853aef521b2bcfc201d4097f8626ec9568c806a55dfc79b1438b6d6fb28a8aec114fd9d7764a438bb251546d7042cda7e9e1baceea0cd7f08ca445a978195bb0c9a6acddd77397943d837581eb0ea796b9848c1c67b3bef6960946d13da450d0d5e674056d9a49d0ad1584a681f2e9a6a74307db59ee0f1e7097faa4ee6734f040bf58cf0fa76d0d0d0b3b2f548e7420bf34dc00a6a17e08f30a631901d60ffebfb1b846ddf30f252e95fe0e2c4ecee0af8880311ae21a3709be02cfe521417fba5dc78678e2eb1ddf7413c464935dd0ceea7c0b1aa5b1d4f5d163f76041ca8ab1f4f754a90c25de23162259ff9a4a33fe46dcfc81ecdb76cc41024bb14dcc74468878e9290476afc4f82bbe9cec9af53ccdaa3e3d585e15f6e7bee65e3900a672b4ab80300613122454deaaa93b293080e8dd7ee71e28c410e3ac2d8b4c32c7c352f1a8cca829d7c4f0bf24afa9630429c710a0bb1b68aea06a7988582b783060a8c22ed5d34d36ffa10bfde61ea71777abc6c236b879b4641d4be1aa91b9eadec8f8a3dcf525f2f35aaba0ceedf477a3e50ef500fb502b1e497974f3f34ca118251b5e1e420e827e033c4d01fa5ef08962abc64f5bc67e14b7c87e6acd7b6b2983504b985b353cb8234aa624d42ce1a5c884bd74d226461dffc379890192eeb36bbca13f72ebe065b64430104013048b4070989887e5326a0927e802401dce85ad002cc3c908d543798031b6af9096cce4f029aad66c822dbe14f77c56d41f9c62c239f27aa04a455be8fbda9c096781208e65473cb71691686245ec6934824643e993067af3118e52b5767767219079ef32c1a689b8a7561a52c4f1e54add2dfcef2cfe4fb3c3ab96066909b8d7c08f50309a821651aa558a3443807d10aa2402bf10e4bbc3e0fdbd3179f7cd0e50e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c661009715137c8f9aae8b09d4e7353cd0d603c1691aeeee74dc783aaa38b164","proof":"ea6fc301fa20b203028bbee0cb91536b0f5029066932f8b96e91dbe6c6086f758eb6f51d8dc4e8c24d8ffd2490b13bfab4a5ae746cd67a8fdc0ee6abca688351a69cd996a847e99c8fc56ffeb7fb9bea6827951b5d789a6dddea7876747b611b3c332bb23d9561110a248eea023216581cbc88a730154efd744aac2c88c411031483683ebb74075b1b5e61b85389084b6e482419b464e08446c0454767e69303448a18ff562b65ef6c38d2a0f7400ba41d027b7a362d53172b7ef65d163a7a03ba4589c8f3b3bdcb8ec18973ef8102c0a9aa4233ae46ccd0a2729a2a5cad1202eec2b20f6957748d363ae4e2fea3f5e26a73c64e84ddac1bbac0759ae97e174c98d3871260f4b7aa00f69162623cb1d22aafc574148babdfbe38043edfe04046928c0d571778ad16bc318456f058de1ecd43276317d8d89e5f0d322b9a8444395a154bca253e376b2cc89c31b5f77202ef297ad2c42473eb9a9b4463170b4901886d74566e1f67fc2783b1400791bea7c2dbf93cf5c7e60dde422bd3906ae957b4c60df6d1bdf88af6a1dbe252f9267b7af1e0117f09fefde2141950eaa2242162a51853b67fe367b473b308dcc98b7042f3d912f7ed187ba686623ebb95103aae1d0cf027349bc75a338da3dce477d63c8148c68935ddb16c6e5468bc7e9c499e40ca43a88db4a9bbe87067e1d7d645e668750e7200448e61d3bf1991a8762e78c5340a81e7b9701aae302d8524673ef78ba8b4cda6067043276a4580887f2dec0e4092229564af878d7709b61a8772d6ffbc70a72d0116c46bab83122620292836d25cc5ef4b2fc4dc1d525a3b82c4ba94992c7631745886a796cb0709c76c54151bd8e967f0bdf2dee94d98da6c19e901012aaf3732b80661556a1506f90e8bb60bf428e759c13a2ebcfdfc3054545de51e34ac95025ab572e0c07e01a405"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1c58a3fd7cc4da8fb5bddd303f546683fe592c3b948ed8ed13a4aa57e5401d24","proof":"2cd602adaecf9e8cf7804656b08027709e83fe4bf27504daf61ccd432bd6a44a9c956cac30c19513ebbbc129e854356ca2944899ddbe413b19ecc2df11b3960a1ecb45ce7ee364e93792dc07c126b0013284b0eaef0a03b685455ef691fcac72a6d893ce0a343efe65d605a82ce17047f4db2bffbb188b77293535ce32ec88376311039462468b3eb244ad6a063ddddc9e5e3a67dd6f21df1b97be4a4392d10b568ecdfc528c1b079f2caece2482f1de695305fee8dd9c702f10ae8b48c5e70c7c187fa878606b86e651f160d4f2e5072c3253aec7adbc28a730d5ba408b59085267cada3a5c7eb83da1faa75986bb51795ad5131f6289ba92036be80fb9365dfa13a0a0f9f81bdb527afb57793264171e7de665da4766f86408915fdff47b6324919fa908de45f8704bf176d4145a611a12db612ff4c075376386d7e64a3448ba5711e0d1dcd6c8c3b82cb653950fba84456b6eef2d8a7774fa2f23ec3aab1b8c3edf36a1cbac30ffb0c5364238a4c757f2a33161c666e06452065b8f5fb53c781fc53384f61333a58c56e0212fd679b65e9cdfc1bbc6ba642f819ddc66a553ba6153d777d1e73e1effb97889e69108957c8f35570e44871c4612277d7a9c34fa43b0cf0fd9840896a427b155b3fc8728bc6865372b233269daf35dbdb4f908a8e7e6e0789855d077efe14f7fa128df8762f4ae1cc10cd2e8edab237db5de2b503a32c1dc926d30530e10c1f4e0b9190067f994203aea8c52875d706509f268a67ae05884418d89cb34354358529305603aa08ae402ed772b22cf35fda7770ac4ef934f56314f753ea1ae1f11d4d142caf72c4113acb936ce717fb2133aa65b6bd13c1817d77a1e616a32fa60bfea27c5426325c2913308a93545ebd090100593dab1af8b9cf9b6901992c99d6397ecbd079d7adb845b7833cf5a5b89aba005"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d617827a26116dd5902a5d8b5ccf4e6d3055673a41fa032d09392dd6e1b5b67b","proof":"787270417039b52741fbdf85b1e6dd78b635dc05297bb1555bb200f008d89f753ed8f6abc3053645151feb059ae0f751d43ba3d3c9383931b001f6498a07e76e5af07a92d760994fe9f91ccf8cad8e5801d22285b606e2059fa013e57d291425fa507b9634331e230e5c5d23d46be9e38fc9aca60f9e24d0b18e77d0fdb0061e215bce601e19796837d79bd4f3c698ec407a70bc7827f621bbd60eaf2b3bc705112083e048686256354e7c38a3411d575a10738639e003693e4413da38a7bc087453a30572a5ff812567af0a100f2f2946563f668e70f5fe672fcb2056279b031ea39b34e30968aa1809e3c3bd6fb4f2fdff02873c5feda4dfb1237953f6dd60e20ae060493f5bea15062f2320e66dfd5403a7bd04124ace9df2a28670210f7eead43eb4e9fd8045e847a431153b33dd63a16029f30f842c07d7d95d92f8cc7ffa940bb34b1309e625f07f20391ea048e7986e84b69be59b62308ab4edb72840765223fb58ce8a7f2bf4c5dc943c15565464d0c5eb971d1eba2dbb96e3aebd2e76ce329e270cb858b26c7303b941373d2ee3eb94e96bd5345548f6f3deecbc17b23617dd407951a59772f128597a2d742acf74b0cc298091c3406c7545f16a49fa1de1730d190a626bbe8b2b2d7b778b2b6a64bd816502a7cac805df21a4663e0c224dc05268c9ba2c8272c15d81db5e1e7eff6ad87ccf2b4d4b5634cfddc258ca446e814eb3ec0e7db23de4c2d4a1d76d1d2d2204ed6eb4ba39ec11323a0b7f467e3a098ffc433a195dc532fd69f7222c0291aef19b99b9d3ebbc05afdedd4af29b7a9e8d18f3e6f05324623088333c31619b4b18b74353ef85c06a5000cb5ae1cb92bc01ed254a958d89b9a1249f63c51d1188855579cbb383b4b8cc4bd5017ddf58ef8e40a83150ff41cff7963a4531fa02d0ba39ec45edc187ada4442400"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"000ab63dcece41fbd971be5ccbfbb347fe7606427e7f14c8e73ca6fe61e9bc02","proof":"caa9e3d2a2da458f56a96781fcce7ebd90e2bbca38760d7c9942e854bcd9082c4e5e7bed28ec7432dc83192c128a097ba67fcbba59edb93c0f88d02b313d6e27e67dbd6ebcd822a398c8466cf6be0980c2775c89e8d08c06843fe933771a143352981b820a200d4dc7b3cca749b3c34f62a26dd7734da906cf258ba59f67961c2e3f1ee6feb48d9c2b870d11db6c6edecb1dabad098c23330bf569be3ad78a049904a6f855083788a792515c21f8000bcf79b47a65c4ffab8d9ea565856c93076fce9ef063ba208573a460cc0976bbd73dd06704156b0e0d9406d097c9bdfb0d80fad96e060f5d9a8be15ecd7281e25c007629b59a6227d35698999b99872c538099a1ba525b51c7714a5fb048289bde48d05eecd3428ce7e5f97fe068c47109e43ad04b4dde98057e4a3e00c0656160ab1dc3e8a4cd773ddad679b5cb32f44f14b13f15fd249a6f2fb5f9725253eec6d8a46a32440def07422c3aa5c9223143dcbc3d8cd3153b6bc11c2f293c147877a19ddb7dc7f1fb4de1a83996da0405399acf122b53f7000d1cc9437bf45612daec0bdd04ab7e67cc47693e56877a3275701ec42ea19e999a72922e45b20ef82bddef879d1b859dee4a1f1c93155bf449029d8a929b2a7d369ef925dead5d58b4a95dc12cbe6ec1fa2567a9a3815c983d884617e22e19b52f8037ae2d3f363ab5588a00078e0ad402d3bde0de9e6ff12d5c512f3ae324b062bc4592f5ab5d67d8f3de36e2b1f1414ea1fa9b73129cd939aeb6028a74c87657362827a3eb858e6f6be5903cfd90034ac09bb29d9e15a452d4de875f19c7c269216da3611fb0e360c3caa96cf7c65f011077d682d197150dab916c372c3ec2c420b92ee06c9a23e6e432743fee0c84e0df4dbe94decddc0119c7beed9b6fea6c7cd6b74ca6c2ae3780eb11e375a80c5cb1898cd2caa98806"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a28ed746efb7dcf74f81b430d7ddc8cd55de2938d7c126ad700ea8e6f3f3787b","proof":"4c51bf3580e3d6d6cece14e232b7103923a8c14e2ab70668af8e2851c7b15a4fb4cdff89b983222c339fbde6b2c3568c113f0744579637f84d24e439fd814f000087b3d0cffffaf1421ae50a3188de43e17854ed59d0e8cbe3f7a188cc65323360a4c4d158f7b4045286743487a7bc28124a65a58ff4c066f47c3d6a6fa3fa20afbdfdc14422413a35b9b50072d0ca9b1a3b71493f47793758c33a66642e0a073e3eeb814d34e5c3bafe1f037d84c817920bf9f5cba3fa9c5de17d8b9b0884042fb7ec59a68fb8cdeb6932b93ab83dd14b9c7d1576c43894de79186b152d7b0e486d64c5370feb835cee38bad7280adab81291d64b6a5b88dd68b77d0d9b2e4e5cdc755a43aa015e5b59064fcebe102efe247e067ff3ee70da4f6ab4010aac4a58ae4c379ebc0b97b820b35b78eccd383853d346fbfcab745398e345e291c232b4114bbe10af76a3577d1ec03a2e3fdcb3d040fc590b4134893de9458d00cf5e8ed9228ca4bc084385d3a2b21fe1c26423f1719cc535e6e4eb0151bc1e47a36f447a073e3eaf4497cee00f84f2b2aa0d2ae8605fee2123e8258d754999e31e46ea1224b1433c50592d4cae537c541642cfc9f8557c267003682a16e5680ec438f67604bfa4091e99235fe89f3c65997e4d96716716ea65e02bfc7bb79421cb7a58f5ae8f435444e58bb4fd2f4433ba1a9d9c7c4dc55a56f8cda3ba1ac6460b5ac2ae88378f0217beb488f12f82fc40c1c927dc9b91252426e13962d49a42cb73f6029738aa62f4a57dda3b89191223225a98d98159cbf99d05da1078243c1b6532c517b81a52c8cc2c57b3f7bfcc59f90f581154f44ef2f9d770ffd932b74008c8eb603e07966b4c960762c595e8bd8faac59e2cec681fca2bf0516a11a1ff0187ed9e1dee7300ab3ff8d3e8b919dc850d7528b1dec8191a4a471a7020941303"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5cc68d536297b053e0c78833109d37974d6d3ac1331521e50dbb388fa5f0bd30","proof":"7cebf0fb52ff1b47788cb1cb8a2de3e2661f0c697b20974cbf2dae652f034c055895ec52601cac3a82bc7ae6fc6804f5798a2de3b1d9dbe2b8c2aaa91d72aa21c03d16d7638debe7c6fe2854e19af102d6c25931937c95d9015ca3eff38ad20c88d8372a7402d15254e24a9bb03695a4e9eeaf09628a48b1651fdb05b70a2b0b30b17fc3c163fd69b39b0c3742eb192a36dff3a827205fc95ecf36568214720cbaf70a8c2833eec880ac00fa8daf3f0c9179870809d8dbdaa60e85d8f16f3009ad343b6d73b1b900d2cba29309a88caca2612f0b0e5008a3c2c79807dc3a3d0b2847b7c3d8fdf33a842020629edfb2673976c789e15a3279a56ac60b6d26464148cb27baff470efa7c84839caef82f2c7d2e75bb92dda92793cd02d3d3fc6216c63dd7cabeb272b2ade00c692dbe34f051b9f07cadae51f6764e9e15d47d104bc4d3beef16aea3702e77bad6da9bbdf5bbd22d2b8fc926ef87ffad5db4c61b58f440f6d4d00d44e85879010f44475652a65f302b734528d89a687489431d3049ae2eb5d374f7d7e2221e5eb3302940b48c44f0db6fb06c414893ef0ccd5fc77b78162ec3a172fc8353d814465b95e17906db1a61f361b69e5e1f6dd2e7bb5358d4a3acde3f5a27c45acf861280a04077c642c67064c58597bb674909a8328d463e6a0c4f9c7fc18447b624f74d88a157370c9b6eb249c480d6eb73aa57685a1c48eef585c35e82cbde8324ac329f56285d32a746d87d599d9d78fc445c36ef7dc6bcdc36e7096e7f22adbc636cc3b30fc7025df52f7cda548171213162de151aeca59bf4c574d2b28cddebfabcad5404e8e54d4588d136204aa4dc34c37f636eca9ea17e6ddefe37788a926a05b9e8d2c741ae66f54b55bcf1f1966a6eaa3b002c0e38777ff3ce25b4c1ee8171b05476bafbe49e63dd5a837c16508cb1df3408"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fca844e3f3da02d758e954675b5e5691da2415138dcbadb805edc00df554e062","proof":"fc5083dd550c90e08d0d14180f03baec3ee8c0e5c38f41fdbf2e7baa74dafd5cdac48d833e9cfaba1fcdd7138a12ad8788949706fb0b5f497dd639b15959a504523327ee4b7516383fefc73838a1bc0aa5567cac992f69bd7739871385a3e00372d72ff5ffc8a15adf04bf6b32f9ba599795fde5afb1f421ae93e00db4b13e04e298f9a86468a839761ef595111551aa0b83367890648a246ff946a3f48b810a7ba8cfa243e51a3924fca4fe8f40472975b808bcc9443e16d7252a37e0267e0eef5932693edda4ee421b11eae1025280d33509a2bfec5f5440a2a2233e0d4003aa43abba1b3cc5576080a9420fcdafc15ec80af421bac6874e3d55da0a56d3612e69550d2b479e901d4cdc7590715e896589e6f392eca54889ca6739a18a252d7c09c6d0bf31047ed767278a5771b9708931fee1d10adbf34c8004f39f0b6d611290a6d86c95e573b19b66faf610897c1ba360a4a4d744ee21ae84bf520ad43992da837debca7c1513eac4c8f9d50397def0a8f9e25dc85c84ad23bfbd12da1a524dfe37655dd7117daeddb082c2240d8247a687bce66fcbe6b6f34c8b58264034fec8d0198db7c5eb18d7f6373028b648cd4a5a98bde01cc77b384d20acac5376529eea9ec7c175979712bc99783931e50c33c3fd246332bb53a9c68e7f53142eb3fb87ee23e967d9e88d31ed001cd5940a60856045420205a527a70784f9651ab15e2f01eafa2977fae6e1db40367d201c9ebda4b479a185bbd9463cb1636bb69eef65dc2b2b65c2d8f67f8b2b4c7d40e285282b7793d57d0743fadebfb212d82fdeb86ac040073664bf788cd00d8cb70a152578fc27b64781a745a801e55ea226d5da7b5af722f3e7b9b120200d4fb561875a4134251ba6e4fcae57dbb702d9e4befe67d0f0b992c5186df7f3437673821bd4302ff4a62e85c0e4b46ea706"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"00a5978d7860217f16a53825e2cb5d7919d57748076cd0710cc3cf25b84e3277","proof":"90b9f8d505db2e991018894801761aed252b30ba4889475561d8a2f3da17964f10ef52bab1b6e9ae7c79d8da9d372332170f95d5c3d11e06893a4251cbf6f606d017f038a28ef38ace0ac895f6fe086d79d38136c954b0f4b756234671c72a5e52f3a4cc4dfc905e33a5f793a13d12d0f5da6a92279a669a331637b169cad176f5bb155d9ab2432396c0e6010d40a502109572f17821f930a72af7baca60ae0e8f6b968cf4f11a5c51c7f4330af177430721caa2cc2cb24c83fc61a0850ce20db31cc6c1347c274b35a469a455376307ccf53a656696eb35af91b4f9d5307a07b2d9e2286f19a41d0d3b3b9b6e169162be8160f15e7b0ab65cd1dc8fc9b0a94f0634c839b61f555ec969073130600202e8a8794a878cf9fe6d1459d802e95060ee90a002dae81bc706d2bd64b169a23e479c34a609bb86d3ba66811c728fc83c36f55f819ab644dbca80bd30a054f87f0f8de3e4ea12289ed26629d7b2745257d0f62bace68e319161fdfea2d63bfe29445a35dbba446ea71360b35eac9a9f1fe2448d142a52fa99e068b9ff5ebfc87e2d7e37c16dddfe1765f9d7bfbbd9981c1811f5784629ebc02e13eb7d5c6c57f353255874ea92474251c6179d0a49351884aa58e4041727b6a30764e345624349b2a235430e9f8d4a83914a0c3eb1a77028e3d96d3fe15bae4ad495cefdf7dbcb4eb55afff8277aaf96f5ac327ce4585c680377ea069db00d2e248154e3b9e3734e908b23f01c2ed80d8b6abca03c154da4d0b607446b38db2be3bb5b7b136051b830d783740720cf297b725c6c75a83c00e69a7b8e2bd3fda174104ba7a6bc194496308ecaf53622db69c0c74f75934aeb2520554bf127b75e6f1940a34a4e21fc66cef8a3cdcb120f6a7e751a1cb20f684bcfc7b84b134e65ebbc896da55fa2c0aef254217feccd95e8a42c91be1301"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dea4bb439cd11c3b913925f4557ead683c9eb51685b51b55e7baec15695cb207","proof":"6ccefc02f6b305b2d3e7f451d9e1b31e2eb5febd4574d13f69a8d1c69b59e67ff8afe3ef8d1eab3017bff8def47386e4d508b0306a6a140ce1dfeb8b7051ec0dc2aadc1e42e62715821fccd928768a38430df9d004b4a9a8311896f0faa7d920fa3e49df159dd1aea79e4eeb965c3d1d48569dad7cdf6ae719fd748fb7644e3598f1c0dc7eff617d3e13bdc7897b9b9b8738aea08be269e6bbd9841874485e06216f61f250f227a830f3ba7d9520d455a351574a8b02ae60051832493e34f3018ca15ccd89163094f198ca1ca28470abe5db380f1d5fadbc69014875b17c4604ba34f98a21f8b1f06f0a2df04b874025638856c08db2afb9f44ffd8c66fad2235e906aa3df6f4407279bba081989024057eca349f6cb6b5692beab6e24df1701ae45b004cd3e4c48dfe1c6a8250df94a0cb136ff400d2da37dc2a51b55795522246083582a2c14f0de2d3a6bfa16d602d05533dc69e25c74093b83192e31f8752a168165af61d8a9a80e1e2be3132474252b144dbdbf08d544788649b2b1126e042434ebac8a8ac4af3e811115c97782ccb784783334dc9d8cd5e821e1577436f89b571b624ad1595494813d0153a2fdeba41212b760d1da04b5c6bfab54ff6456a9b0470864021486fd80378b1129403e572400d796b5d1bd681f5597f001141a12a7f397864e4c44a9354cf1ecfe530c1b3b76d1a28cfdc3d10f02cf37341b384b02c032242c12b1d3a6c5454d0f61522398a4ca3cd943c381f6d6a92e9c4a5a229214399fafeb4bf8424ac1550f4eac7e58d5439f8450757a5cefdd544d1e2ef83a6db994e6417e50ff5c241ca545f0fe5cc48fff1f100d03b7affc37772bc2d93d2796a07e2ab3d7e99fe374991ce5b0457700b7bfff84e793534b157a0354de15d6d3554550b004a6e519946d0de71e3df0864aa67d4f925e8b81270c0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bed1f6ca3681d1e39ac2e5765d88dde8f5b0d920e7e509b90ca7505ff2934478","proof":"44606fd53fc61d86f585cce1a918ff570cc929d8cf5b698c461b6921e7267a30d2fbe549dc0b96dd16b0c1398c50e8ecd2cd7c51d253c6cd3768487918c5a41322f037a41ad6f94e1491dceba720824e9808d33d129093ce905f95f45af74608626e9e08aac679ed2cfd27ad37253f0d5c8c4a44b9e5095c20ae70980a1085698808ae7457ab17d4cef3c21196ea43e307775781b6419e154a783cad8ca8cc0bd4f947d7fa2f172f937a3f249b8f29ac306187dbde6161065ae631737b7efe09f6e1a2916c968b8d053cdf8e969513a4b943c35b933f7335a95d34d346f9da07c403166afaeb564b6a358a335890bdfcad42e5785dbcb76a69374bf1ed4d1d25945d7fc6ef10e25cf5c30e51ac50e69474c1fa91e9f85e1fee1596f9b431b87a4a5e39462d533c2012d7b70ed8e6669f5440705b7eb489a712261030bf9e1f38c67efb414e324e50aa9e5383c5035ae3dc2139bd419baffca3ed5601e2b0f04a286b067cab2fa2aa99be0be1e365300f36b9cc8daf49e10d9d296ce1532c51698cab4f88a75f4e895b4231190107a59d5761833f413301624d2b7fa29238bc1d8618d6b86666b45081c0bfbbfb30710927ccc024d28b7d1375bb3392974a0b7488e029d74e9bdd7a5922331ac4f08116365bd5d49be1190376bc2b0589fb100dbe61527dad0f88f771d871649b58783a013dd3e35afc44ea2ee58fdefa10b56f28eedae824d99ad9160cb25808d761c9768c0c1e767a5fb3f79e563c0c3e3432ecfd2c5e5c48fd9ef8d1d2a529138306558632edd93cae2b812a0f6ed1f4a213784056261eeb6f925befede7fde5ee73800f1d5dccb3e6f9c2dc256fa4615e501f140683c44fd89edd53ccc17f9afbc856ed4323246693c14d4c0fd10799c50902e54469d299fb0cd173e9330d9c742e83d8ac490895694a76a8e6b802818c01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fe80a18a55a05013d84024cbb6c7d51fa1ba62a567c79f586be1b720a0f1b016","proof":"6ac2baec63980ae2f693d75385fcfcdeb8a58f32642956a530988e530aa51c663c5aeb4034e8adfd8e9eab29fbe07be766055f534f48da4ae4c3a7437bd11c6d46ab6e82cb96b6fee2043d7c8f485d0870f444c397e4ed7fc5eed9871da5df7974e6fd1eba7661807166bae5ceddbc2988e30a909a18f7b0488b7c9d58078a0efaf300a3bb13d50d869f0454f1028c80794bec8699f39bebe6a476f08b4a260da8beb74b68e4079e7dc960bfb72badd89e6e998ecd2ad4e4d20b6fa79ae73e0d896ee075a8253c7ef758f799ec1a7c8ee35c31a5c8ef4ccf3f593a7400fb000f2030f39f29ea76a27359be66922844e4e9c7f176b758fd648e25a2c10712744a761779d88253a11eda696bb7db8dd50443371be053248012cb411af089a07d113a42d87c24f246df96bb7b0936a665f690a661f00ff065e0912ec2d1c9e26c64acbb6cd006a260a6ae86ff46b59394b5759d018acde4ce192ef9affc53ae523ea20431d279a676be6016b93b95b08e6b2d18db75d9e83dee292ce45af607b07670b4c1770e647b78fb67380b24fafd28f51314dfd3d7c7332889e06228b5d206caf88f2450d278d5515bfec582d25f8f2cb6c544844b640189f7fe8a885f2b694467f2ffa0277223b69954f6eea5bca3a0ec96a4bae6b65acb65e1c55ee4467880563de883e5793dbf1fb74c74891e634a39c160f5393e4326faa577b1fdc70bf636ac9fe5da1a3843fda86f52e91734e5d6c4745aaa2af40356f7212b90d464cccd3dff07bd8feed1adb2df8a46c4120f23b6d6920e41a17d0162922a87564a880de75202c15fdcc26e4899e1cc36eca315aa1c1c693bab779f770c275bc963ff3755865c19cefe39613ca58cd5f70a301bdbc85c5e37b9c4fcb0d1c5034409d8111d1a731d608cf8d0d7d6a3a937f5c66b127a9ee43f24f7bf563e3b942f09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f408fe6c16df3e252cda84f1c64878946f8d3a0b398236ba291b2ef89947fb6d","proof":"b6c3ecac978fda8c2a0a8ac1f4373577da258a4422db3a5c5521924b0188590b9ecf0e7154a50a5b9aea22f0c9af1bfc06af0705048084f2ac54dad5d7970e50b44edc6c775b6947deb3fadac07620284a6e34dd661edc3c091689632a1f125e48a593040723e7caa686e53f90ed9312f140b0ebae4c2418cc3effc2636cb24a2f2a7486d1da2bb9b79cfcf29facd9239faa0f84b0d4e0211870a1634861b104a33673b090c18f555ab96cb02c924e274d2486cfd07a09cf2ea52d3f873d3b0fefb75d995e7a4e5530f8f9a56e2218833ef10ccc5d899d27871963bb0b099a03d8f3d5ab696dcb40a545c744d3fdd0ee76bfb8611f07c9eb39be42e4b4750f726c95e0b4ec17d355ccd452771bd164233106788957451144b12c45652340ab47540bd17e0a1aa899267f858f1bab196c68028fdb9540345823a946b64c68902f08fdc884df2617bbd92260e11008b96fd41215d424349fdfe0da5d60289a4d1f9edf27cb5a6f576100d4f6fd0aa22e6883525d18562ec9c9e016b59240e3bc268c527a1792c6692e91484b5b340eeb0a5cdb09a504625de24fbf8b6eee00ab1c8e778787177f88d24eddd72e54d0a75848c296880baf3fa426df1f97a14ab42ef40bbc01c212f0cab64d90e2f0011eb2fe778fac68122052e4f51b2f6790a9067a7e2c881ebe45c7c5b6d06792e64628bf41c6879daecf6a92deab49a1cc464a68578bc8183ef0e9a1e0bbab5c2c0147714150549195cf46f8752f879169696ba0be2da9aa7a0773d03c1d57f074c54f1d0c3adc98eb510b20f6ca73a8539936687a0c31124c04cf010b45717925b6d6c11ac53eef84780df1811549091e02762ea531b8602c8b05a62f38a03250c976df40441577c8530fa5abd49151ea180264b9a6176eb24ba68fa31e17aae5366e791a93fd59270d09cb7880715205a806"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a660fd612d8a02f880b62ed0c94aa8b6fd52e8bd2161cd0b2bf92fbe3921ef2f","proof":"1edc0c4093f1f2fc75e49a1c358707f3c23d50bafdf49532b97cb86949db1d69bc33facf9285733aa7115a2cdac165ef5e8387902d2d7d7b9e168418968931434c842f886ee2785a4803e0c1dc4d9ca91adca7441d48184a67c644ac2117f24c2426ce0dd9058f51a56b0d9b1d10e48cbf7aad7e792b3127723783792f37817d986f30b3299f7ffa424109268c09ed4877276a3b1655e9ba07428a947c929f0d4774b99930d5a69e8058514c6ef3b913ca249759f4e7fd457ea8924714a3cc0008bff46221e5a16e4ca56282f3e606aaadabe158adfd301ca175b73c3ba9c30404292fa19bd3427bd2068357b4fb6942abcb332f1656b69aa52bc5bb3b352a5f762fc3bc57380bc5c52a7242a4db06c60440be070f8c1d2df4e30ba924d4c44e36161295b6de469156af0ec1ef1910a358e50f8cde06008e60356c5909e20218782d933804ca8ba599c2155096a5e0dcc8a3fceb91de6b6bdd205f097046c243c0a7d350f2ee935b16b4687652fc0fe5c97e8d4c85e1ce1677bfa8ea9c941352dadcbbfd5f016bf376f79df71c9a60083d9ae4ad51a5109ace2a0fbd0bc28e16a860491b45cf58f0ccd5cbe529584dd26bd8a473acf0ce818cb079b599fe7d499840f52f37961c0359452c04f5b1901d7220aae4ced57041b26063da3818437950da6620416820e3539762b655018c87e7f855cd1fa1dc22396852efe198886560ae12af0f1239489256fcf9601daf156848cfacb7e6be269073913fc7c8c1607a93f7df024175a8c003b6b8e4dbbe427959db65595bea3bca32fb9dde4c9f36946873ca300c7fd3385937d3834b1d39d7ebaf70a286d605e4242867a7f88d6730afd1e79807931ce1273c2e944bf33c25661890fc35533f27d7b0ea1e476d019b9675cae6057ad5f3c742241da2bf6beaf00d5be7cfd83831dd85a0359ee30e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ee94052ec79d3111f605be399d72f97bf558818c3afa1654dc8066836f786127","proof":"e8b17fb4984d3e967b6366fda3c792a66dcb7f14fdffff734ee833f88c94b137fc8a1a54d0bf59ab1c196649bd2d37fd299b383368d61e1fb0d7e996314c400c70d633aeeeb599f6daabfa24cf0761ff471ebe97c98b4b3260ed1dd9b8a3c37506ef5cefa3ff6395ab685ec5b638cb0bacfece03203cbe77ff4fadb12751243776e878044e99d78729a5d642cf98a0c1174753a212ec2f2f9b36f95e9487cc029477375f7714f4f722ba1b2957fb0942c00fd857b73d46d6e85b7b752f42fd015e1ad633bf8c58039d0bd8919306f8221090e8fb91f39c0a21e16b4f1af80b041ace10b4be8ac64cd003ea991ba690760bc62be5c8de324ce6f9b74393a9ea6f8ea0fb89fe4292f7aac829c66578e5a85298205193a59b7271e365b09295e02126d7da05a3889c6efeb4840798953d59887e3c9b173697b54d9f8bf3ff5cb722904f7d7bd04ae4db2a352bef791b5af4cb45c44f28bfbe3648e3f93345dc2a2510532a9eff91fc09a0441b83d2122cbe32d018db74ce79c5ea9c6071446afe09b869ca2eb4852c4564e13d7da6c93e0aefe7f5bd17846c0a2b9c8409343f8b707479d31ee4f76d7460192da0814806fcbd9eb9439a917041226f3c363a9c1a34c658e292046eb44594e6d6fddc6f33183da4c6535e9299dd27beb15f0a35040004724fc03590b24d068d0d2d5b509dc1698b3f5c660a450e69f856267cc963797c2b8d397ad1767608bd3720d2a1240d1fb06cfb92af3f6793958a86539d0d320c5d0a8e83dbfda5d751a1ee5c67fc194ec229e325ca10b31205b25c67b497551a98274d0d960ddd7d7aa51a537beaa4713a015420f5c6f8205047205f70b623309f92fc369aa253aa906cd5c0ff305af6075c80973e6bea22a4837d0478130521f4424d2e895f484df03185089c9dd1e6082693001a8dc1cdad6f18ae873a0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3a66e9ec4df432fb13e7d32715f65ab3e4f821cb02a0034715448632d7bee144","proof":"a839f4a8db68f27a1ccba221ab8fc95a84fd514f4f402e19baefdafc9f3685318cc4969ab28d04919b5764fa95b4dcf15a595088d66b44fceb092b8004b0bd545233babb3342b52a047b181f899e853d3d8e08741e1145a098c60ddcff82231206089ccd53eb449ecd9369740220efa116250b2f592908ce990bd511565b6d59c6f78f9f2d4876f1f9621f49499074b532e3e90e31fe29664e11af8ad5fe4d0cedbac22e36e12d7adc2df5d7a065583f13c799e6466463b48d305f2475592c002f9064e4c29accf9158a9dea5a30a3d93db19478fb6eb9d0bb19b3636a76e9006c16c8d7ad5838f238e54e0473ee1a8f24ace2f091efb0c9ec22e27bc092965dfcedea06d3e96b3127ece9e99c3022cc299471735dbb301922362f4f34e41d21e415b75ba3b5d4992172d00c4c80ed753904b88360e39d0387b3970b91c24a3d08ea0e5a887a92c61560defa3f3b6e4902be2205461cdd46df655b316965ac4f9aaa0575970ccc10e1430c47cd6454669c7b4d4d9cf7da2f4160ed9f614ce577220a7187c292c58da2415a4db13e75fa9cd86b042bb633ebd3f1a6bfcd39213db08e658b962f18b0b25c257f7c98723784013fdb8c8f147f753abbc7632c1c053c6c525f85cb6e297b41161026dd0865a092f9199cddcd8d3ddc31a3d80f1473a66baf1d36e5fab5c5cf364f8120fd4e46c5a80c0127cf58296283efeb011f03fe4d6f39dad5fc8269995c8ac747ecc41c26683da7b1df21ac9c07cbc8311308ea21645e7ffe206119f556ecf2bedccd0e955030586783813c58443f45e6547e28d3b9c11a69591066ba3afca7d182ac8aa2ad77882ff7e68f5eef03968905449465e82ffa850f8648dfede062f181169a8c4bff834705d2db83b498a75d5203f3a482da452ace0bcaec2291862ca79bad3ed485e067fa3d996b8c4f09bee909"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"64a3344af0795faf2d39ef0c2880881f312b76d9966dffa9b15cb3e0d1fe840c","proof":"b438c2112a58ea3c1b56171e63b384c9dcbefb7a094e0f64f9522f5ba529c43cbc68931f63ad0da07909505ba94d98b3c0fe77a24edc1dbd6edf3a61d4850f2d968dcd8caa3c291f3071899d547a56c42f13bfc6b0de958e17f30519a66dcc1a205eb733ef6884b9dffb059fc7fc4913f5b5cbc90184b19116b47b25a80e17502db7be8588b83f8f854add8e9891edd7e5f7dc0cfa016be9d07b158d64f40e0555cdf28108190d281cf4726114359fb589376bfa8f40f5b179f232f5cc556c0168827ff1dc0b6ffe6864d26e8ca0b24ca2d357b646d1821e681a0cd78d8cdf016c5692ef7672e864a17029a530f64966d1e9a1280a143dd03fa32f40ff3dd15e80e2df1b7848a700f1908a84fa8abc104a568a8907b4a1baa36cea741e48226e0e2ebcf10ec7f593ebc0fd5c5242763edfb62abef934db3ecf038ccd72ae437e52ff661b84b768d8948671b21a3c1cd9dbb6a456d5c07f3cb2a328119033f565e8618a5a50c6f61db96274f8858855a716107466879fbb66f4185a4ccea01778ea4eabddf8bfc7d241d2aef4801b3ab73b8afa2d944c44f4ada86b56c24ece7b3463e94d576206e02ba05927146b6f31de46962b5876abdcb2ffdf645c39692c8e74574f63f78dd4915cac158a63285fc14d600415a5b484247cd50f3f7e8031f80a53a3cb30fb2e1988f62b8619809ed099dcbb7804592cb33082f5270f32293e5d356535244c0e004d631401dc88693d2f6a35687d4ce0b9153ce3a33a7e66e40f1b8eeec624b052c9ef8b2f060bf9192b8e6d98d1cf0d66d4abfeff0524159c52052eeb76c2855a216af005ebc12b8e3b5d4a348fcc6c1b25dc48a1632971cf9884d52853d3ee941be73190567e134b7ff0df41cd3f26c6d969180ef98a01939f5bdaab39fa33abddd9b6c4227e7c731268285fe57c876c32920f9203410b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ac6058661a7ac3e11a5bee6966a41bfd3f4509486578bbd0f86e0d23c7c83954","proof":"9a962ad7db285a02d6b06654c1aac7f3a10c2f5acee44cc0e154715afc9a592c963496ed5d098128e5949ff6ff7fc25d06259e22c904518b3c38cc9395009969021818c0447b3aedb9ea314f712237f6ec7da7c25c837e72439de2f8291aa11e8af5437ddb703a029f4e4a2edcb5e5a59c30f7dd7e3ad050186cfec39775a8328189eac177bd60eddc61cba4e322120130eb081c95f9f4eae4ecc89b8c228808ffb05336e364753e86964d43656ce2c42840db787d2fd1baba3f1b9e504bc00de4e2da889db461cc85b9f6e2d12f099be069357409db23270c5f09284c6fcd061a764ed50922ec5fbc7aebf36e5eadb61897148a05c994cd3e38eeafbd98dd3378c67954135310830b3a0a6ffeec916dc8ee890638d425cc18e514d6a61b871fd69d6aae440330d24b826f8c36d3cef28e56ff50d569b68c12f3e52134a4f40fd4d9de7af5ce5aabd0868302cccdb607d21a5ab86d7a842b6a2bb0cac034f632085d93f9a6011a487994114b6e756f4400c114cf4341254914ac874a91f9ea7b9a4033607a8d1ab8e820bbff3a0b48a8cb67a3b6d7e2a4672cafc84c820af56f1e6fa4e9c91b9a173ae42c8b323e9b3644739c21d5c3c214b22ffb3b2b69d34b3095c2b8d1d85a47343ee4d5d27c8cbdc42740c0c458755e96dd80b629cdf964a012c9c0f92960ae48e5e8d8d3ebc498e31c0d2bcbb2b7e541aa6dd6b2874d5d28653aa552e9032e4b467219c9cea1a363739b3585061c981343d6ab753f524d60b9a9b12cca67e8877d1fc4b10f5d4f56c6cb95704c99155b537aa6e7c5dc7e74c5e3e50f1eea0016539a35fb27f7fa0fd79326c5673961b1b3457e2229bb5d5ad932aea0bbb88530cabc4a0a2759497edcba4a1e1318a5cbc220b2a73b4d0590099d36308943a9a7b098bbbdfb646f225d3e9979dae324a503c6ad9c51670a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6436ab2afc9a0944a12bbdd2b8a879ad3de278346f93aa67fe5890a1d8cbcf31","proof":"0a8196db7dbb65548b5b8599c4416b147b8c5c3f6f57f53cb02a96275153102020e7539b94a8e038f5ed25d259169ae05a91d6498a311201e2fcd65c67c30d2b922e862385c9f1dbe1ad5c5cf1927431a43a673e152ac35ce9dce52e4f9eb816c0a74289769ea22a255076f8367897e6412ea8889950612af9410dbb7d256010e6f07b068f8f4d3a5a4deb83289c1616c7f2de2972a0a6d4dc48c54067f9a905c8621aceb4fc75fcc1aabee141fd8b7626ff8250c67988476edab102e1b9b10e1705619517ac03526c853f53a9ec0289c385cb39dbf4710c01a26d7e4a00cc0c0cb67167c0e1c72ff24739d92fdd84c8de04a561dbe7e91c669fc48cccde86537cf95b311d48e456e663db9f0e1dc39512be5e34c9eb2a82650b0f840bceb0469052692737b03b6fd2552c3af394eb8691e2016579502058b1e9d8a5fea8633f52f9427eba3ae14c952481dff2289f4919f15dc876f8f5173611d80bc2168d58f0f5829fcd2abb7fa785ff5a3744145091329cd8e4f387daa0dfe28bd2b2b402927ce11aa2e8672d65f9e77028bbc46bcdb181205e9616902a1370009ee878136850799d4f7d3631d1c57acaf7f43c0a8412ef181432d887d74fa6d420e59f10329c20d82523213af950c1bcc3b8aa00e491e2b2b5f23109e734667ec56266657aa4bd4fb6370f9c813139fa1b9cb1e81c5d3a6fa3fb9d8c397906a7a41f824f8e26e81755750d95a47a9b55199fcb5b8d5eaa5d43f45d58c0d3c17884d6b70ee48dde84332e7e260c27d2fe48338c51721d5a08413403dc53c73b5948f60b65bc143c23433b58fa971a9a3e8b6dd604660c968db8b6368178d9bf072528401f56f6126a4fe6d0c5e5d55bc7f27354eac116582b9aff040d9eb4caca29519406fda36e6eec0e367b945280d13e62aca1b77a409262ac3e72db95183255a3e40e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"12dcd8b96687218a01f95806fdee7b4a0a610f7fed9c347a96cbc81be41b6727","proof":"aac54a2be18b5aad6e2cf62f449f61f6c429321d388870c13537aef4638e09162a0de37a77d44685a4c26ec399b89e611c95a14a64958e12bef312e6a903263d4af4a84e468b21964a982f73f1e44a05ef16428791b39dbcc9cd6273851eeb35808ead054f1544046cd6a601d975c105933cc8ee346d194d33415f65bb029977436c8da2110fcc171f1dca36d40c0ebebaa32c8d2ec7b9ff1e926fed1e2f8f0b8b4979c3c71eb7e794fd7272417dadd49a4ad55e4db5b32c12cc7cb0b5172b09ce5f2ae6e5da12fa86fe1c6086ab2d3f8a18d2d25a8e9e70bd1e53df4075d90e14a0cb4577eedb84436e6c7d9d8db10431648ae7f1d2628c1b82db33c8cfdd4cec667568b98f80b5f8f36aac0cd77797001c9bc34ddd3e7aed4c76ae3bdf803118f5277deb74cc240ba88fa392e25fa032c9f7fed4c259ca7105da41b9c39b281ee401c09a9866bb18f1d453990f3f251afb055d6bb8974e5163d3f615842574868ea9b7015e6b88e098f236b444c5f6673a44b113eca7f38ff49c7ba87b492bfed73567e9a5b38ce67a13047cbdc7f45ef9f92f599785fc235b1321b7997d1a802a36110c31f24b6e10f6846a2a729a06a62e6b30742cb6f1c5970e5069bd6afe59a04b8edb15bb4dbf00c073d313746c2c1d8186e93b0d540d352eef99124656676ebbc805e5996fa26fff3d768c136463f6b7b1e2026293ad4d2a6c2cea262ab65fe6e4e6a3f218268adb2ef22732bce341b1bc3b079edd46469e5cdec12a74dda7468e4e023d2bd00be20cd2e718f0b703fb2f31ab202a30fa18fe87ee229231d6143cffd7027527a1c21eb194dd568e155a3ee4d2f6adc1cf0745330f6fa1a8d579965d4860d7429a91130b54ca6b43f648a9f53700468c3bd5d594ca0c666b176ad038c3987ba2705ee2ded5ea9a23c2f909db54ee20fd2463c8f0600c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8a78aa9279f7b29ec03c70c4479a287a1143f0804f8023f5fe9f016454bc0857","proof":"dcd3bb556699998da7bf50cb1e98098fb8ff1d93bc638807f35d7deb4d852f0a100b7fdba4ae28fc8fc69ce57599463138ccfd3e47e02ef06d9d26e23ffc8f6b569acdce16f1967c98886c849d2cb0bc22f1655a46e5809b6bac12f015b0de05962942bd44ad180e9a78ba799389a52336609f21a699d6bc8fd609852dc3a659227c583ff794bd7a6bb7f0ce62082b2a11c5da35cef420c91170dd6c7c25890f99fe1426fc3774181b21b8b426496d221ceb2a08a6773a1e3c638679cd704a0366cdadda91dd32e506e47dfae69ea3707a98acce63d0b1fe041af0f3d5f86300167bcfc6b025307b81fce9401eb445adfb344cfca5767e1fde96b87ecae7217a0449a37923a4b757a7f2570ca059a5732d667116f5d1e80483ccc2b4e3a68b1aec83e4902ebd671c1ba4f247aca14aa5f9a29f881021f5290dc9ee9a0299cb777c254250f428b2d927b6edc495d4efe1bdebadb0cac2037b14eac8fc1f10e3266ed31782ebbf318a363ddd9387814060381e1a0febb23b9b60f56604a5a7ec7a0661a2139a1f90cbb6922e9da429c4b8f02e6794ac2e79775fd6d1f4b01e9d2dc63731c990c2f27c2a500efc98667bcb65ee2f0cab71de6bd179d2c1dc449d26ea443fd3573abdc4ff5318063e5af6cb5eb72358978ba996bc45836af7906752449ab471950297b7d98d60b7b65068c0937fb6f8864143430e62886ad6a7ba6ff2bd9abf1cca0ac5a997802575faff83240492fd26c1323759bcd3bf57ed3a0926f61dc9f9123f9a3e743908c1f00999b86268622429c4e9958dfb6f7fe710704e17c088f631f4e8e29ed5c78a1321c3e7e12ce7cefa14c99a682904b2da5e6b7d43e2275ce6b4acf439019e180cdc82b420057e62a546bc27fa31234fdc7100c16c58055e51985096ece60f94bc38436ab6c60573790394509a24fbe68eab09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6a73c54004b039ae08691efd6ae756ce580ca03685d91e15d41a4014cdac3d44","proof":"507328f770eaacdef939b3baa66453caf605d385f7e02a126b118890af69713f8c4fe33efc62e58bea013ebfa7e2a0246e5b8d5ce50d519ef151117ae62609708453288434f4f4aabe958f5bf7118f0233b92d752a91f6c33f2ad69e41e00e3d1c8032142d32bac898e547f8561fa05709688cc3c8f879e92e2898a9ce7a2f73eaea2ff5c79528d00b91f90673e1dcccd99eaed1cac6fb90084ce75bfbdb0e048ca33e07b98d4ce33ec827b7534eefc0a871dd2a28cabe85be81f46050f2290b9a64eae325df075193c0ff811326b7f4349c60b60f1b4dfb3d4d9d83a4cf460ba8572cac2737b610c8f1e5d2022bfbae187cf5ec282b01c61502a6936518802ddaa5636da63de7af02debd6e9f1a0ddaf8e4a8f923ae25fab90950d09c13704ececd8f50f3ec41b43f2f1908bfc48772f6be3f2c3b9fa43f9006b73fdaaedf789c3e9e9b80f9f89c9c1fc71a3adbcc45e6aaa11342e42490a846d7b5b66c0d61661a25c91387705b336e3997674823121f7a492f7948be67224891d8df99742efae56982c14de8edd71151a4e1774b3dc8846fb9bdd384500846b01c07b6ba7fe02f4cdde6735fa33ac6188f85bf629ae06deb1748de7b78cd2d3f3af8d8e35e1440a875ba67a9b00f18901cfbc33392456732acaf891973ccf92e70b60b547ac25018e348ab383c0603b437ab5981449f0dd6aed9501e7106cd242df4bdca7d1cee0aa238abb3c19040fa373c504ea68207bd3834b5e17e4ece9af018ca5161ac83fb4f4c343e22a9faaf3a32be607379311fb949eeee51d0a9c4e49b4e9542ac5373cfc311c920f144194500b7a32ee9eaca0bcc5f2cbe3cb4bb6961ee3e506e2669c65438c279d40eacc4f2c5b820f8b684cfe71b386d2305d483a5bbc80f22654f1ae6eb69ade9770fd2b299ebe3d7f82319f0170bbc15d346d9f9969500"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7254110f0c247d8d80b866af2dc8db5870c2d441a9dea30e87aac3b0ff220564","proof":"80caca7eb58964d4323ad3e2cc55ea50fe74a4c60fcb50ba2eda58d3081cb0194c9f745cd3fc40546fd81db9f5cd91c9d361497a4c2b8041bc8e74b790555803d273a2b85462b07ddbc8b60e738be9d84522353dda123141727c41f83fe8e17b1674f458239ddfdf7fab24d7fa9b64f189cba96d29a499686913f370daa46c7e0a75a400aeca48386b14b7b6b1d22ee84c1e03412bf7aad9333c7ca273c084008c169b79eb9577218c457b085fe740501bef4e3deeef9087891fc153fdce4c080303786b1af9cf09cbe17764d0095fdbed5d85a3ef236a39df349266411417019a744749b47f3763e266445fb64d9d9f85a0e20e75095600dcfc7952dbb31530de1074eee58859ba59492c71e81ebf9d80c7f370e1022eed21e0f86995ca283ad0b8bf7c6a1e9bb745ad9d90abac10812da80044e122e7dd274fbea616d4140d5a725d52ace20a55c5436b966038bf81eaae5bd892501f7be8e4aa6f3da3bb416435eae20f0b8f8272494c109ad9006ca5dc8d099320457829a8446d8277f87f203ad5b6d63359744cc5d94f5e84b7845b6a8bc19361cc7f0fc7544338ece8478a8705ea72bb6cf515a0d5b938e10e4c1da5666a6a5b6ba596bce2eccb8e9b378a994202914a8351e2fc46f45fa9e8f68ac051675b70871287674b0657b158032ef4d4e996903b334ffa9ccf1708ca945fc636135ed170e3fcf61e8affb3287150063a0423ad43ea7249973b3f4efb7c8e2f4feb57cb032f9e6383f64fa1bd4038bef2025f1e4c1ea8bc53826569511a1bfad26990fff02b7bae3728acfe7a3b3a8c7d030d9985c56d645e9a8a768b0b0c32fef60591058dc8ede47613cb1229d09ba43c986f6df60c1eee917b005b971b45b1935bee34cb3091c4197d66be04c1e42d8af1d4c27f55b2284cf38c87c8618bea9158188d2ab50b5b85c101420a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ba772f152f28ebb78d677bf51d34a6740c0ceeded7f07961e25ae2c57d81f005","proof":"5c4d3687258d9421abd5ee587d4d027a7f9741deb63858f2b9232c173ac5127e8607a00d7c8a10299be8840c708f3038cc48ff35d45887ab871d107d27df0f4d18e25157ed4984ad6f19f333ba9c78ff1cd72e92f10a31a3e19a1213dfacd22928c15aad54851a11f12a07dd3c7a868c44fabc93f204e68f53f3881233d28234fea60f46f8da90726eed417dda1b770a6d9f92a94c6ec1f75834c2ecabaddc0c95aee658577530279da88c547f9299a6dab0a3e0960b7e93058808df96ce2c0ad8609dec4c8a908761ffdfbee4904021f8b50c28cdb1fad3a31dccc309e50f0af041cc4bbaf80366051e8e7b071aaa2ac3f14cf561afcc234286e8159f2255193a3cc03b5c684daa5ed92c4b85256476feeb9818c90f267afbd50e0018cb5b31965ddfebba1da398fb744397c2bda30bf04c8bbb1773898e77988f68502b0c0e56352721105e9b982c56a91e4948728c53013f3cdbb9e01f772e00c4936cab4cc02ad58daf1b0803feabf04c0bf66e733dc5fd93ea490d34742999f8ce2fc90364aacad76c418ceca2d928b6262d9eb2ff23062d647fc67b5f1a874df36a67604c245c36b82d19d8aa5316adda925fd4d351a18da00f45dfbad6c48f016834353073a624c0c249d71085ee767a06d6c102769a320fd56be9e5bb34062459cb7bf0bdf7f046edb1eaf72117aa00b7a6fd2f1c1e33b5b1eb78738946d71d23b2013c377d375011234b0a82ff2cd3e111da9d45018817f14b9a28aec8e3351cdc3fb012f9d63f505040c6085bb471fe10d59076eff3bfb52aaffbce3d7deeec313c54fc8fca812f2fc2f82ea0ef23531fa9ec52ae9ea25d2f9631685774c58d3b04ae7b20cd682a4e67a0607f84ae3d49c67dd91c4acabf3cbbcce7af09b2e418079e9a0a9c87afea637641038fcd4364c17818470392341ab69fe95efb874f0208"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"80bb2b7dc151d0f83680caec3411ac697664a3ac7a0347ea43e8ebda0d92367b","proof":"e24cedc69a124e846ba1c3ec347aac2d3aef61889c0490f151011db1eb0f2d1438f1b4926b87b5abb2bd2fe626685461031539ac33ce5a42da761d1ba274fe71f44eec19dab9e91bee84465fd818f1e2238fa3b77b8c28b9d576375c956b51390842ddf83bb387bc515ef07fdf759d42d0f48b21f295c72be14946fa8d0a8e5a6e21a2f5dfd9de6daeff450fdd7c273e0c47d8e752b1bceb97e27b8286fcbd0326dc29dcb9ab52c10b290c035a502f0b2a695287d2e25a512d6e8ed92e8b640b1399393c7dee0bc4368303740f49432823d16a20c3f78c143f0ef9079c7e0308c29a74ce664fdb120f28bd70c943b166a78e45a3b119fcdfeaa3cfbd01d48166e2da5d22f12f44d65dcf73282c045e3599cc56fb3f13f761dc64e53ece0fb738c028aa085cde1912393f621460fc8ed58af41d88eb0e73e6646486e7cfc0ea29fa2189d356d0744217357606873650c082047c8a5afd55f0e6c66397225dab288e40ebf2d475714ff5b4093474e26899c169632ee7ed3c58b03738d6724e6c34b0c9d992d21ef4f02d72f0e79b05a3c974e1e295307ab8ce482348200900c77750e83a537c153f6e856c1c28de7fa366e53b6e79553b0116bf48a9097e2d6712e678d48fcc540aa68aba730ae406d0c027e8f5400a4c41919a97e059eb26ad55b809b71846e8982b953f94c49d4d6d888cf27aa04ba005e15f175ae020df675b50104e875c501c71a63b4da7e56c52069b4b5f394212c930ee38816fd09e487634629b156450a18154f04b0511976a4676bfe40b58d22c2a85d745c9f2c01642003336f6a8ab298fe633c2177a842262ae5ad0377fe4e1328869517d63d8dd25a29092d62e1ca47d3108d351d3ff97c7bdacb5a155e2d99a36f0cba2350c43050504978788eed26e5363bec97d393a3f42bb2fb0641f0f4baf423bcee6e79800"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"868201ee217ba915abe11df319a75be0a3a2d634587ddd515d6cfb1b375ebe16","proof":"3edb21d023efbb8835dee8a6ecf4e44e48d859bcb59a88d630290c58526a3942347416a2921bd757238ffb81cf75aae196198cc29a485a5a73816e52505699566e21f9ea73e65983d20bacaacc8388d37c4ff6bbdcf755de17748496af31ac518a2d55b98302ca4fdcee723ef992b10e01633f6cfbb7be8c94c0339da7309f626efcb2bd4f2ae45f948eff61e038b2a9bd35e6423c8ca7019df35764f4d8ff06a48020d030f17e662f794fe2de5467c602fc76d4793c0cdb5736283b083c7904e514c83b0bf12f7db769ac343314edae8f4905d18256c848656aa2ed6b9f290aa667f4ac43347323607fa84fed2387965a6df99b6b7e776423f2695d1e1dac60b08d63de2dbe62d65f9c83e914846394c6fdd62c2c29a9466c53f3ac632948455e1a580410477915ff26553419092c0c16fcb1be101ec5fd1586068345bc6e673404a2f051748bdfd536e6f1c9803da67b5f920e1b471c8feafd97ff1c6da30490c5da01872cd094614d0e9f9d2522700912c380ffc464f312cf84aaee11e7184670215209646a8386d47dfdeb23236a85e3398f9a8cf14434f03b2bed44681b7ea213d8279222afe08551f11bdd025dc84004e2d0cc7ac04976273193ce7841f260c39fee4d0ad5df42b4cd5e7b3eec86fdd6373103417e64f0370bcb76d146e0391082e3b1ae357afd8864632542764c96830979ec567c7ae925a5b5c6df45b2d93dd3b05598e8d4485244bf993bb73f67d7fae116e3a533b541e842f8b31564450efda51652d3be4bf0f60cdc4e3a21ea447853b88922a8da47259e8baa5d4cce5364b931e4a97fa8d0bc98d19cbe6b896d774c26c6cc6b5c3aa3262ee40a98798ac3652734bb3716dabec8b7dbda6aa30dccbf6e98d173866044ce7530093d5b6f354f0ff764285a3cadd7f900e1a4a504664d77918fb8a8c174295f080c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f09f3810b57b517cd09da3f84ed7ec78a7f1be0c987dacad6f3039db462cd259","proof":"4a2e886fe7582e9a032dc94ce0d29b5e078bd5a876f64bd7ad4c879672bc511266b8ad526a5cac51e270b5009734e2dd40beff718d7542a59692488a64daa9537e28ae1bc76f35c2ef173811c46bf686ede9eee0ffc9d206fc485e89047519076ccba8a19cb1db068741de3234c4076bacb894f361ee31067ff15a2d36d7112c50af274ecf9a8c2748c5b616e3ecd701d400818655449978ba46e76f124c3e0a1fcd82885835f177b5a72be1e2dd43d62db2d64dd048d012b93ed8341f2a290fcee9c3a470dcf7c632cb52da4b5770c671849f70732316a7235d60f2d27d650190b584faadf84d2500b2ac4fd2dd96abb49fa9cb41be52e2a303daf50c36251c5c9419163f9ae281cef69e6ac004d1176de1307ca99b70a0dede0b9f1d37aa0256e74a35d46bac2c6b7cbb2399549433fd8725cf23f27ff1b3fa7be515ec74727ec7d935e8b8d1c0bf6a1cb80ee5a13e1646d391f343bdd49ac34f483edecf7012f1d068a09a57f2a16f63bcd776893440b52f7bb77960b672a1d9e704eea80736b95a0c3333ca0c3918545eaebfa8363dd51ee7799fc0abf1a2d12aa7204728148482bdc719a86c67d09be0a38a5997a6cd06644004740511427e74bd483c5f0680d731c8d348690dbd5adc543d4fa6e4105f81fa70a8e4ea0f235edf9b0c1d98d67a13fc3b1da46c22edd616161d05edd36b9ea389ce16e631ca720fc91849fa600958a9ecc5fbdb18ba3ea00bbbe80b5199f80d4161ff8b8afcd265803f5b8cd956232a0fdf1a0a5f555fcb2dec9d7dba82bc231a3846e1d80a90d98b8055f2a05f96d0b18b66bb6f13bb21c3b54146011a4dc4be06393fc6577f66eda955502b2200b04fbc94fe5e08d49ce30675b5801c15b05f56fff3d49f3139ee99070dd365336b7285372c95b030ddbeee60cf8b8b29c259db73a981fad0a1f4ac07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f6861721108715772b1e4a12d6012fd61d7b3cf1573da8eaedde199f8075c72e","proof":"4049082c562ae13af7b5f3f6c09146ba5c6b723401385a2c4bda033947291218be4f73a8c238bbc1d9c71758f349ed29be8b4d01a2edf3112f7dfde05890ad55a82d2cf3522117ff804c763c1e52d906d15ee313050742c9dda6fac9810c9418660f832945dc1414f4fc9f620aea22a98c3daf30ea2b58d4bba4c4e66eb3af26ac52268dece4ee7a6e8d4d1c4f324afde8f6bcce9de1b386d8e1868b7e664d02062ea915d7ac27e5cb973b21f71c47af3acb4d274a51558108769acdda7d580d7cafd5a6573a7104008e223ee0b9faaa57b149b6326a76a02c2e94698b404806ac3d52500eacc7d3ec6ba064994a509e110a0142a475f6223748b4e9a9d9f3581ae2ec5ccf1768c19bc64ec88e7f3216d502d6213784928197fc52ddb0565a57166bfcf9817d9e052af28154458593d381cc6efc49abf30bdca3dea18581c74c24c94c304235ec86b60c66b238cb2b2ee0ad1931a054a98f7bdd7b42583d8266664106a6b37393f37cc6a078ac582139ddb439af5b33190a3f0db3a3f3e06b2eb2dda7f6bcc6735689b05eee2894bd7aeba109c83f2f6a503a9d0555464eaa15e29c9760361c08759597cab6f6975261ed016897765935cb78e9c02d5458046d8426d74be23c286d90eec54a2a0043890986346cb1113078ce6fc31943f6046e0e0928434c9626f002938965e9883fc2d69f178efba757708c08eb277db3a620c2c4315f54f89b9d06e3a46b78fe55aec24fd6d61289787da1307e439bd4480578216c4406ae51f519426f51aae07f2a997aa1376e09101f7d25f382b15e156620ae49d9bd49045c934f2070eabab62dddb8e45c919c2ff1f518e7afb4a8047930a9ddbcc27faa2ac8e70e6f42497f478c674754728b33e924686d0f86cacf0fbac765d0c0e4b2ac606f8a588228ee50c91d3bbdaa6511a09acb7c0a1393130c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3ca1550016de252dc46b3cd8d137c90323aaa4bc49f7721c338a1a9aea1a9514","proof":"de47d5fe5dd021578fe96ed785150cf99ea3be821ef627e774b15582edd22642c450744904c932f75dfa8f4f3035391834a4669f09793c8561eaab455d2eaa4834a839c7b5130c1184321049f692e582ac20fbff638b1dede2aaa82861d4bd753ebf0e1eb3cd6bb3b4d8f619ada83aff63fcd2fc6b7bc16f5d63b6255350c52aa0f49619efd80491fb5f8dc0136c9f2fbb1694cd182cc5e3e01db4feec53310d157a9a4f8f392b14c8d5df067ff42e9c9f474892f13d5aa4ca92ee909b40e007466ff4808b79cce8d9ce95dd5143c67ce7b02074d3cbee9a22fde154dedbd40c22bd9c81814b8a5b7786779402ac14b70a820f5cf739e4ac0377a589bf81ae6b00f341a40997b2c12b958ec2ac44f845de6c9d4cc1b5e719bc644534b967355cc4667ca6efb81ac95649a74073e60a691eb95fa5a05e0b3a50665a89c7f3cd05d88d6f93a211cd6a8d0e380fb0d4eb9a55c1077587e33659be19abd9a519152dd2c6bbf97c81beb097dce3673b1c2b9eb0148cd389e78d2ea3b6b81653affd55ea827dbbc47c5c3239cf783172f52154db67cec0b8365a4f230589ccb715b82160a69a09f44a4b9d6834c2d702f33f61be406b34b9781f8990f057c11b9d9e6bdeab530c8a48e17582b5f01473133e17c43999624ad1c2b0ffe8e9cedee1f50bd63e82e936c45375177384adbf3468b3ce72e42cf155e14a2ed0f0e9c9d59b05fa8169679078c1c536c36b0d28d0c09a5c694de5e62742b2bd0c75e7aec6ee089e7bf50136100467cd9b5411edf94da337a29c0ee325154ee392ba7d45475555d05081ce3180b02f0a1cbfc96a5f3c32dff3ee916062014c7eaf6485e631a252a9837e071827eaf2c345302696f7e2edc2d5b24bbc395dac0124d5badd998e0bb958e391fd16f9cd2b62c8028c67bb934305f0e93dc73ce1389961a1ea7dc908"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c4e6b01d159a84a1344243f1a9f60a0dfa5895e8e22ab6ecc6cd98d41aec7a15","proof":"366ba1e2818b55802c0e8870c84a8f67f23309a7da6ebe6fd9cb5deb2bc345438059a4ff39bf259fce716071754e766ae2764be7ba4509aca21808821aad196d9e2781da4d025f5f0505adc833dfe76597e7514ed80e5501b2802f0283d61b72505810be65f0238b9cb707a24b42ab6e289d9017d3fe76fd486d9e73cf9c4678a979f2cbc52aefe575a680e5e091a5c1bd67745651bf733726d953fb4b12ac0f55706a43734f42826f4c4eee313fc7999b31e3e9784ab83475283bf770ec6b0ee8267a566fa47fd821fbcc1d9e836970786c5ccdfed643f867ba6fca2cc0d1062c61b99ca1b2035c3e3d46e5880fc02d519ed73b2f2d249c6225092d18b90170827c3f35abb1b6c7ec5c06863b23f47fab28dc084606d05006199a0f56a95a35d8f7d2d6dab54491a13428ea348226520608e46c1d24d0beeb5c4eeb7a25c3418e21fdf2a2a7b2bc12280f00545364f61c841ee6df4fff98b03d3c10db470d333cf3d1973304c01549cb57cdd5e514f011caa5bfb09b5087c2fc7e4297ebef778ab42692bef22bed2aa13129d30f244464bf2dc93bb81c1eaad302805079554bb61176158200331c11cbfc7cdc3451456b59aacbf885b3a426c2102c8586261d80040d596086d751b9514290adba5cab390ec44e9e22ae31d82d89940f37935ebeb1e3cfa91907110aeecc256255f4745634b462aa44d299f41e28f67782223b2a5588e394da0de9f92f84d2498aa44e6074d334987835daf72318e8f2e5301db02f69a9daf83f097a5f6ed97036fe1e5c41d0081cf7ee4cfe0b32f89a02fc64889248df36f9fa4f8fd988a79761adec088e314d7449580874a1f76896274f0c2eecc583455502701d56f3f098fac739d301805d93747d75296631b81499ed0ed74063c1edee2decf2ef640268f28a4e8536465814e7cd85856d1994a6628704"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"347628f604823f54735f4f20df3614d1fcfed26e3c3106df640d4b7ddc9d3170","proof":"a8682ffbb7488ed1f6752b9183616f31ce90bdc73a25925018a03a37edc7ed333e131e938deac7bbcef05941d5254537914e0b4c973b9d2371d4d6fbc49d0c5d249fa6971825179107d9e3a291ce649fad26e846df52346c34d673d7287c7f1406a4bf67acbbecdaaac6791cd1f9bc418c14b6ebce3a6796bf8ff410cedf256802108456045a62f47aef697c9c0d4ecd34cfea538eda9fdcf6b84c990d5d520c93673067905300b93bd4ab743bd7ec55c75a3300b6e760fa53cb8306aa9f840f955dbd751ebb46110d05fbbda3f8dfa272b07919f50f6c5b032b14689339ca01e6bfd8f50d0a044a2e6fc811d3bef773f42e86a8fb6bd28fd36606152c1caa59a69ee1e23b6447c7d73659a4ec53e796260a816f90601ffb4099b548f1f1823faa9a27f5f58c5c631f31e85e72c34ce01cdfca95e0f848a29fc591768bfc7808de421ff1415f6abc107ffd237ba4e4f5ed1331ef02a2fd11788aab8724f7c21a3283c2967cfdda761938400ca46ce1784328f2200559b47b937a23b4f78f402f201d4680b7efa4792346fb6941885a3b4575b22aadb7aff2575802188b8dec16d0a87a5fd67be0863899df0e6dc0189aa915dfa76308578c2f423502ec0d973282d56a70fc2b3f2d316ae9e64d3a4abe5f9071692fe6f65e497a969875670653c0ec51fb45ff9570696c8830fbd79e13c39af89de7997882d0c6fe27363f277d347682e27302731e2288f8b680edd6ff9ac33b31664a196682b1fd83deb2fe0b7227b2cca6ab5510b6e74ecb6d430c9b21c5480887f4172c7d06e17a79c56606cca63c36e535b4b79943f5c7249cde11024ada2d143ab9e37291b869c56c6263af9a4769151d58a0c60b6e32507bfe2346c8057a763a626ccb60b470fba0260ede3b8c017a3bc8f287afca4a4ecf6552cd96e4d888a3900faf90d6af309e330c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c64d025242ef1bdd7bac2732af91dee5cf69054a7e0364f68f20f69447ccd27f","proof":"bcb592bc9762b110d734e70e9e6728d900606f636457e04931e627337b7737787e87a7d0c4f734b94242ec5625eb2800b08fcd2f51cc09cb164f013d49ebc818e6f004dbc1028834fb6c981dfa35d496db7102211b0e0921c9e0cbea18969f3ca4c387b9562505ef30bdf7999886d1c0d222378dd13bbd53f347307297a39000cf90385dd1d8fdad8c5e8a6267154f4587e1ad54915d8508e723a5495ab0f006085d5e905e462df8773f16a4f41ec6657cf726f0ffbaeb31b43036b50a37e301d81ee5f6ed2b13acf0fa1c961edb10d7b7222dcd93ff9861d778bf8bb43781071655098a2f9e3ec23b7624491a52d1e83b829d6e16b30a3ccbb457d87f1a8f719e4bae7587e554d156f8cf87ad8ded2c9d50c3d999243c5f1906e01b799fe4003831cb24372f872c14201c8e5425a1081f5a655a5b3c7e462c126d19f18ab3090e11abe22038be1412060380980c72cf24591565c177ba425f8883ec47829039b2589b2822d39ff7d97091e4061a4ec3f3aa113585c22c4f81fe06dcadd11e763c195d253036dbdeb7a0c145d2cdc11c9daeec21309e87dbe4723e8d6f65d76e607a59571dc4ba8c8e088a625891d507d976f460515f82267eea2c0e75e92b5f9013c5c867c70a9f64d1b984d7459b8d3b5af5c99ed71dc67c0b50ac69f3c870864692dfd71537c68ad9aaa4c97e75409c56b6f1f3fc2db44d03c23c91919d5eca9c41b5045eddf150743c37d6e43a4139663e45d5c512e66de7ec413d50d9554a6085597c83a45c5eebdca735e21d459b93962ece1dbe5a02de1f789aa9ee73ba828586c021b9b6a43e58f2876c69c06ee229eaf821e85ec9e92667869807691fd49653e4d4edb214b31d5626dacd30f0ec60a3409a624b3be1fa9580b88f03c98e478d756f2743ac60052c58ce42be3c15a0490108b8b691071559cc8c780f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0cbdbf604271206bb5382ef099c0135944bed8c03e358e1544e1a9bc3595372a","proof":"fe5c254f5a63b75f74c0e333d274b450a031c51daf6ff3c6b57746d10c6e91216a7d2a8898f9de14e9ea7f3830162986ece4f1172c42c7ba86bc91548831173d628568bc3b8fe45d9d2c4856ded12d9ad9c1b3223c13cf22bd0c960ed3b724781ce79d3efc127861735ece5a5fee358dca86431f1098fa25e29f5cb150c8ea4a63db75b10cf0124c466faa6b62f333883ae21defbc9b741495a2ed3d94b19908a2416b21b772c618e7fa7027bcc4dc5a399fd005a566ed589c6de2d5b877720653d6fa44b2927839ff4a69cdd0e43cc3070bfbf3f8e13c72987fb2b430202901ea3507e2a042394155a28da4d6aa5c2e5ede1409a54fc612e0bac4db51465e728ce4a706c53b41a0eb0711e75ec0e70ee0f5375132b91ec948f1548f9ad4ff3a6aba7d48e188a131acced05a8978fa99f8093d549799695d844aaea67e6b801180f3fef9b169b918eb778a4075ccfed73f1caf0dba9bc4b2101e589dffd8f4196025707429f79ee5817f050500b5c87c2ec050df127871c0f2007201c2137d5626f4fcdf2c92946702f08388822024afd245c6647badc0c7ed5442364443c84360954d9927bfc13b78ef5bc231a8b4665d758e3a55a4be9a4009a25c9510b729c86c5a3e7c8ae786ddf50e06b8c2ff72b09513ca19c518ab4a77681da8405e7d56ce04adc94c122d2e36a4794a449086cca163cd423cfa8f90d4d010d4cc8b203016814cb1c5beb9675711876ffcbdbce11145cc56dafa1c38950ecc7d95410788302b8596a26c8f991544d1f2eb554e6b0b6a546c52c56b8bf64160ef317c2c349a28bcb0ccfad4b62daf7b010034a4f49a6d3d83d345ac232a5c6b1ee1a941cc357cf66fc39439a29bd40c3ed7654a04f703601e7464174e3ddcde6aa45102b3f6e9ae6aba1394613fd8d498be2e4f8da2d07e0c8fdb71042e27a900b73609"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8ec6573c41f91a42be06f0569387eeffee297a7d8b48cea514d2358ccebfc769","proof":"26775d5fc6dcf7b4bb46099382c444cee60d46c5d92e0e5136a142cab1cd86730c2ae04610c5676c32f15a2df58348291d93db439df5761d720e6192b1a679679e63a34e01ccb9d05284f4ff96d8b15d3b71ea05b8f928c0412fd740fc344a463222a5aaaccada8b74129fb67f4acb5f83357eba4942945e4c9f694157b08417477bde4efdaabd65a90966d15733b344b22b935e2ea2a7e54e2d795b1b1ab606118f48f86c1f9c61630dd48b1969e7c19df1496515762f0ed6f900c0f2aeba02232413e526a0df4d53851c1a3240d41737c32de758db28489ffedad0ca794b0d0260fcb6a41e14dbc79194546064ea1ac3d7a77162d4d773c43482d1a373855092ad0c3650ff9de3c5866da5f56f60d7d51487788597a0f1dbbf29d9cf98d96f846ccb40319c59f9584192cfe1e2a141978854b7f3efd17e53d09ca8f468812d5e8434ae29f354775be05728e5debacae886fe3bdce92089e31e1586cf916d6d16535ce9cf64215145df56a8380abf77f48d4dfd611db83e52126c928dc03c3d9e5311d3f1f33bee2b28c8fc778b91ffdf115daeea01199fa55e4d1c3a612a193c02b62fc70175bb353fef951dc11c3b5f85339fff0e510e64fe45b6318919499a0919e7c6880ccf3b32bf4ca16e3e4ec789b7b9d3e7da5bcd84bd1ed0aeba5154fb77e63bb9247227ad251fe8c85d5010138ed459b07b77e3d44b05cf409a4c3401f12147887cd1e0838e81a41dca3ac9536b6b627cc75420534c9740b6350e5afbee6c15959a713d98c403d28769a9851f2ef63442549674b1ccfb7c813d222a0dc4dcfd9cb8a8ad3799b1cd7cf74fcbb4f917e6f28a6d7cbee4879c398338ab27ee8c01a55cf82a636fa840566d15cb5e0ff03a26897415a66ba30b6f7f0d6a346607912169142b4fd4ac2aa406c83a8411eaf360ec351f2eb87a54c1920b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a4c41bc01537f01afc5c9f448462115ff2b5566486901b1ea15ce4381598c52f","proof":"a812db2e32803ff60a62fec02d8b1dafcfa367308bafb34178e85bde451bea4ea60170aa96115082961926e07f70481b260fd3b2bb3cef5c2ab45a73a49e4627e617e6b3dff68cd18428e3d705415c3c0044e99bafa997b5b80222bb17f61807865fb33cbdb6cf84c6df14f3fe107160a9632d96e4524c0ac6d5ffae6128bd5228bbc02d87efa28fa71ef6d29f7581ee21d3263af405e3b2f9a915009f935c0bd9561285f90fcb22aa5ece79b8c8a2fc2fb417699e458b9c08c37f55967571071a906da588b57d7d9cf8b0def4888337eed5a29371e400ed2e23de658d85a402c0518a32e3d184629324d112052422d0c593bcae660ae565813f0ed777b2951d869d3e37dff18115f0fa140de9f41a4eba5d0309fc9b72f62500f5996eb14032f4525d9d95b820e17dde87faf5258baeb82009ec5133cd8578fdbf26c0f40040be5950395052def57ec1e66cc29f83af58fca710f18c1b0de4f9b8494224726602b52e7e066f9ede23a814bb425af7c632f9b2ef669fc0273d037f0c71bd9875ec886bcac946f87e7842945fbf2ce5f124dfed3532b969d77d5bb68953144233f0791a6f56aa71e70c2849c6f14c011253cccb332c41c220f5bb8a29355cfa34bcd35467b43d420d8021d1197f70b981d102d6c1d2affc05b4f0ce4d002a8b39ee611373886b2fd8bc94b179943a8c3ee964d19a4eed68cbfff701c3a4d1066838e1d39ada108f13a96ed069b2b6ba63cc673164b73b99378249030eb85d9c5a3e6a5aaa62f2463e7b06395bb415a874566c1d8282e7947014d5ea4b897265184a4f3c3c29a240757be403cc57390b262402e26b41c211f29fa7ec927b46be7515fb848c1b08e4c1913d0737c31cf36b75958c7afc82a8982b4c9371de88510f847cc9bf8abc56652b9c0346b490dbd23b07307c95169d55a2f937079c57a609"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b67914c337aed0fe9867ccd6e470b64d8b03346fc729e12ff6c91c5027c9b32f","proof":"0a7109b6129110ba1b971f358fcb931557a9d07ea8ab08c68c3603a75361a134fe3ca17546e0a9fba4288ff86d49fae164f44c95da11487fb7d0b2b90d5cf1701a1b8fc236210a76d0740b3aedaa76cd5922f325df502f7c05af48ad6fd08f65d8bf3418642121b534332d0ed6838232510d121fb0eaaa0825568912a76d9563639d35cc0514a444965b7583f297c95f9038b3b372423fe516a7702d0b0cad097e8ce663a255bb3d945c3d098fcd2f28dac305182da90e3b4db9bab18f89a40329fff3cb03c4093a3b3e3cc7a6d4147099d01919fc82b718c1b369aa605207084035e249a4ef1ff7283040eb5715a29177837ce01ae9b9162a6613633ded8c573e51065bf06a7b5beb72358a8b4eae735a61c3d4789854166882fdc69a7c324dfca27c1149bded4d05da1d2a13400d0e6b05660b8ede73038629e7e1881c15135c525931be5a1587817e52fc5de4212e07d7c0b794d95405083617e5cbe5ac4cba12af2cf65c8ca580171b9446038340bc16bd443a06ec052e2d7f88b0495731da2b236711981e9b650a1479679bbb0f1415ce395fde34a9c017a98097d1fc4b3e5c202d84e0961b3174db32970bc1a29710ab9f917a2a9f269e4b159ce42b7df60ea88687619f407c685149bb75757cddeb08b124e9ca24a6b76c1d0312677afa3a46bf9fa6da7482dcf85bca1ed1ca8c5172747e51d7747815cb8167f40841faae320c5fe90fd2746e358d8da7b158c80caa7e494c81150d88bb9450da35605a9ffb31d857e3030602a74c9cbf02d35bafd8c773446c93ba084a9bda218a70f8f3df046f427cb4e196de89db5dba801f6e64a4e5889010054fe5d545005448499f7626034c7c165c2d4bbb790d056cdab4aef2dc2a6e2d00c428c8552b080ee613f0770ca6120aadb12f285e929a4dfd3ac370af806ad3899826e9d659d706"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"30278727ac2563722f4f13d55653fd8f9b6b2e1369b146d03ddf680b7fbeab1c","proof":"4c44b0c748b1bae3fb62e1be3743614a9d46e19bf3334df1ba2131ff9417b80428de34c5e965e34c76410076b55d5d1d70fcb1e15f3f7a8a4bb7589f99ac037230273f19cf6cc00b25ce06214dee8c74c8856c1db43c37e2c6a817ec86274177366a2af84cb7560fe4226026a209cf54e327870bd694a5f415c38d504c3da823182e33fdbf6d4a1074605aad62b85170b982a2c247d4a0196c5b95761658590b8cd22c807c62b5a735796b05132bbf248ee0331b4b11688f1c726d2f752cac097b4a8735e246bcf69ea6e7044cefa13e2911524d537f0bb54221c4f1218ddc01729695f20b37cdadccfa6f213ff503b95b685a393b951d0ab7ce46e439d3cd6f985f98af83c0d76b20397aefabfb562c13977752027e7558552fe882352fdb79ba09a3184ed61512020829d4fe1ab8e532643a5146d83eb5c7edc78d0eccec2ae2d27b9331c37f9aff2277dd875119afd56655729302b34c7b2be7e02a41207e868328c853c4698d7c01c96cef2443bc111448e95a888f54176f5d4ad5af0218ec60531a62ceb0f5079474564a605b88e9b471061f6944c436338679d61d3a2d0a0a9f2c33d67a9c41f60b72db4e7ca231bc99164f717064f0c2416576a869435a8d69c1f12ad9c754f972dbd2c5048dfa99b4010bc397a6e68ae67c86520c183204d4095eb11cb896e7cc438ec8e41589b7d6f5ecc7d8e685c775c1d1c86b6eb441d2c8f3adbb64ee8a48cf5df3f86d9ed554cb55d2f98ba6462557a039710212825e66c4445ad984f8640e3754c01fab7584cd46f48401acbbd87d13078d2a7ac1d64a92f3903ddfb276a215c620c627f254442e26f97a629b15e521bcb86327f54a6ba6ddfd22a38bf4d234297f2a8693235a70b51992b7455c956c66a3044a3146cb8cff08b93bc3ec7b681d038cf46abc2a3af825c4870c617fd6bd0b06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2cb914e5072b9b486cd5b29eaa8fa39409ca4a761b45e447c608375a92aab417","proof":"46b6175ce1e7e849cd948a940537c0999d5f990d4d544484ccddf8e69d776d2e7ee510d98615307f9cd43cabc590d31b8c2a097209ab7b03075f945cb8f01c6940db1c6d299d5064db5137ee1379a85b1c6501479c602620d67bc547d8200d2972b5a79cdfd1907b1f9b2bb24a04cda5d27774316d7dcd78c43a0015fa8c576ff9875c9a864c80ef846727b7f0eea8ccb8f0a8a880b90ef215dceb06135eac0aee0c4c8745635bb58add33f0ee75af580fc7c4fea2ca36aac0bd2fa7eeaa31058ea44a352938a4a9739eb86ebe5e2a1e4279bf1c360978f0b96a45a38ad10f0314063ca6d131c82880327762410937d527712831ff700a002227778798a7a358147fc8b4e5f37f9791f4b4cafc45c529d40690ab7a869807b68b7bad75514665f2409fdc17c88d68a07b993e434193eb09ee87ad96c92e91bc682498a57cbd2dee1adcb85c997f3256fb763cde85b7eba86c639a4a6fcb73a79027f67b3ec3161e8bfd59c775ba0b2d5f6efe9bf99b5c7ac50142305c2e3f2f51c86cdca91524aea9f4a446edf2030fd6b3cde2486e1d9bfdf0d736df85f0c329fd6445650c76749dae12a9a442bd299d76ad635cb16b7a45de1d12f00e7b0ca36d81b1ecd2209889d5f21be9c4de081f0d6237413a6632a09594047d0249e579c60ebd35d93f04491d1ebe6a52959a1ce08f250acba45b4697cc4bdc0fe1da7b697b36ef0700d05e3af8cf13b76e0c93f144c468d93e4a75f315d78b416499d8c453e78bf72b6cd8c7110fe084a3ed89679b361ef0a50949bba1565b4e234c88f5b74535ac001c235b0f0ff7a3e87bbc1b35d5fb607d596c42d041b549819bb79bf0df567f058ecd35bbf01a73b0dedf32851759cae21c056750216d7b3a35bfdc94d3b5c9039284b46216d51289d2389801c6a726e2a6a0c1253a85466a6757f0204c1d400f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"72a4256942d62d32ec18e87d3659f755e33179ad97438779563fa4533b206219","proof":"8c898e452c4722780517faac8ad6745e77fa41c0517b8a7d3c2134da36a9fc6a2e99e9bd7cf1b8efcca7c2cd87669d27e8338b01ea19a1ea98a9e78cbd8a4869540ebd80b90e3b801fe09c08f5488e2bff4c3b1ab812e82be298a7b90d66155c226e66e503434bb7b94ac6c9b91f032535a8b4422447cbd232c8c4a6dec2ca4bdeb5bd0b8b607b22954d096ff4aca18f466e809a06c4a9b9bb5aca843a602604fd3d71cfb1f9be16e65fd622f725e308263d49ede1d93b98f8af898c28aec90e3aba039f615093b95ef4fce680790d0927ad928a9e1de0eb272641fcbfc8180100ce5ed803b36cb936f309eb694769dd9d9b8cc59a2eb560bd3cc0d816d43b55e6b7d4819210280100cf665f8e10caa6206eaf6bc4c0317888b54bc9ee5c89050a225e1854ef5ee89a13a89ad0eae5732a4b6b8db5e6cb72496c8a5cddbbf644e4edd7e9e46182bbe435c371908af68d4473b991e79e32d0ea6e0cabeac88572740d65dedc476161b561c7e08c971017adade767d639e02f89f9ad915db0f5293866d115c354a5d726d8e3c9de9dc9adc00f442555e4ed560e87aa7af4935748ee7217e0e4f8c4fccd5ed5f69edda0a6fd24ba688018587053a1126f114545718a360c4aeea960c615412da4ab732847d5086a72704ae52ebe645843cf061f1e8ea9ab56caea57bddf3782e923def99054e35f9f7aa889f6819f572a7586806e2adbe3cf628d4b3727ca09dc728141b649c6023d8ef2f10c146671e50042c2666470d3c33c78c916816ae1f00fce7e4193dc5df17bcc7f24f37bc11d4d85e45a3e3838500683928b98c9d8a8c51d27294b019d44055a99f939f8cef17f7dd27c11f5bca576b354411fe6bf72aa55803164ebcac7e2e33ac68454d41dfefdcb0422d1e439b2807229abdea13aea68da7448d705dd7e7291deabd8bc17ced09504"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bc0cf407801c97fa8feb5b40b9e91e76e15b499c451a436f3be999c50ee28b51","proof":"e25e0df531af662d9170f055ed554735b5cac7d584d9c48c1e240d14046cce264ed92c37bb089bdb59eebff1ed6ce7608b7c213b453525efe165845863204304ba069c7af7f785eb982e2b4ae9ad44bad9e9f1e05b04386f06930928003fbb30708791537647a8bf51a02c3317ab0211c8c76b0a0cb2ec73d9fe72f6397fb501d17354d3a6c269a2b4f5213dea129ce53ebaf28e0d7336920f04dad02c1420032bb193be828110acd0923f063933f209a71799a21f747354359e1c81c57dba0259b5bb8c88a4f1c63a6048b647380ae287ab65facd597a5563f882145d1d4e01184b170858c2874273444a47c881da20fafac8dad093e7bc4a428c99759f6072167ca20e20d46efd537c6123461b422cdb45a9263699231508b34b100c47f1749a97979bf90c0f9fb54184cb72b7f146df08ea506cd6c102d6cbce9c69caed4e203b8d9039aef203bcecf294b52f210ba2471f5ca7b4acd54108bb0683ac0856dc2fc2f7690253953ddd7029fd8ad6d53cb1daed6868ca07c75571587e702f591a04d0f109d5905153c8d99fcfc0cc687d64c60ec2176765621a5963f69d0910c0d39266ff0df520a481f4597f3790fc0e76c28f41374db34703b9f7b6b81f75c2da2969dd8c711023a623b794e98863c7d10fbf81252905656df9ca580793218c2d3d66bc56195120fa1d901e66b917db1c28e064cee48fdfa5997ee5dc347b22c05e23ce27e0906128a962f3d2c8593aa52e74a5a0106f3ff472f437d16e0d9a71b881d4eb9813aee289b99c9a2268172d8b4ab57d61ccc1032528fa021d77f82f03ee9310f7a2f6bf2cba8e644224525ba6fcbd6cbfc8cd6d893c5397dd50ce8c9a4a49123c647ff5aef3051d2c99c37794efe762a14aa125b76de60c220a3556d6dab519336c6cf30572ce98f7c70005c4fd0355f1cd579587a596c96b01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"769baedb1ce84c9a2ce26ba8f4eb3c3875666b8c1ab3c57165d6bf785614c515","proof":"b46e72c496d800350f388fe0c7022a2c99411f5306329f00f2a18b76c80c7223d2da8190af209e279539c363211f49e376f6aec9f9a9c527244e211de7536b323c5258162ebcd2725802a9ab74a7d8cda6e0f147a9aaf9674c56a0d9b0053425e634a2aa70d63119a2288563b9fe8860c61999d4e4a2b82af05c0b9ef691915371c1ac29481970c66555c01a6c3211d935f52b6f9311609b3cfa13dd72d4310be081b941f9591c29eb00d1e4db31597c7d4d9bf00b3f6a476481d6686af9c501333ffedc2a7c251fa86173a518754554caa5eca345e2693376e7e333e020670baea2bab1d14de67d317e24c4372d180b2e5f379062c6168f762d7d96586d4370b212f3e5aba9433828a168f20d2b516a4e98e9ac4b8d441412b85d75be8c074a5047076f1d0a765b86e9d0f56ba6dcb15b8ba6631bba64adabf61118d64cb628b8a3510bced3a8493c157a45fb82734e44bf3314b2e6b000ba661b470de5e850925e5fa314176aa0d0dbdd14e496ecf18c89021e3bf9005156d60c8909f39e7300e667a96fc76d30c99c01dc4c3922948fd7fbf22da7560e8689a8c2b7650874422b704941867d4113be7dcff8c2cd597062b9de24b043a1603e6a56b294ee147c8d3d65928c2b1d0b9fc3782c9d23e7eea85a86ba12932cbb7dc03995c40227fa7c6816f6b30c6c9ce330d2b56c94a850244866789ad5c1f4ac49731e3f146b324c27126f0a64c60422bc5965c4f29ad2e976396d4d0952eb06060f8c64be1c207857acc7c6b4b1cb6120b7bc1e2f247c37c452e01dd7eaafdefa6371b455525464567bac2d6a7b23554729fdee05299303dcd4a6bee4d94f1e0f4047b2c91c0bed5d826d32cb19809f0ce40a5b1333e9e61f3ba43f094472bb2cd6ea1aee0560cf48129b2557afce6cca10dcbd55c8383d9a231395e44f86a759bbe731bc04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"62793d67589ca1b335356060af0ea1b5d3152154e3563509b9b8fe0abf37e375","proof":"b601fe75d090c98448aea760738b02c39ec5920003ad1e7db7d48cb1f2ceb075981d35f436df969be6072b52dc9ccff7665b0aa556fa0227438bc330df8916231428a0556cfeae4a2dc9c09a5d4d149833d5352baf407fa6a19cbc4fe8c57156c2a5d86fb67f66d01bf4599d4e305137975330f92b85c7c52419866c00afc77852b8bdcc312b0f5c042c8287df37b259d25f37736ed42822b5162a502aa49f0fd9a2ac996116e7dd52e9885aa5a61cb15a1d6346a5b1b2e06abf93db5c14050a7fcb0cd086bdc90cbdb453decd4decda3bb62708ac05ddf8027c55c42cdd270772bfa3ac9f3873b871e8088d9ef8a35bd50cc8a4b95ef291c82841591f88112cf20fbfe1b3dbaf49220e80f568656e2c279a6bba2dd6b481567768953c70d64ba6da9a7cb728288461411437d29ff9d074d0f3f538edcc9cf4ce681fbb39c87f9e9ac313b981a800bda9749d2599375aa1d7d74772d7cceb0aae3744f7f42252bc995c4a2ff839cd33a02824a851f086170565dfaec255f0040a858e3e5f826b38b76d471fba81fc192bd385c21b86e2cbe4880dba2f6d7797620132bcbca94738cec65b3642baa1abec02f7445eed3ebfa4a419bbaadc95d5da5066bc971518b0334dbf3ec0aaf6190e2cdf1d4132d1c5f2953f181a3eb109dc5c1adc5889794c922e2a9209483651e9bfb037d0c080f3c716bf16d0f51ece04a22387fb7c6c70fce8ff71c2a98dd5e60c12a0fb95211c4bb9ea3cb1ed5d1ded28b47184bd04940c3a4ba841a0447880eee0878c1c5ae8903e3266bbb7161feaf6ea39f0df59e4299c0fa9fe50f56cb9a0c6c74b47985c660e29e6f301896cbb78ddddbd9f3d8f00f20b61adf2bf3f9e09c0a76ca0aa3ef97c41b95d4acdcc403bca6975ba08d32932df2a073b5f4d264e402202efea224b939e81f720599b21ed2018764d06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bafd7b5a18a032c58b6df9935a3c4b7f9e7aeb6044e5d0a315ba1c7aca212440","proof":"58ae92d22b401f636af2a5c133a473139cf6c8022ec0ca4b18a7eda868048656a216d1e5250a3d64cab94f5da3bbd6791c9886d4d90cf8d218cea1716cff5d2626f79396f327db31dbd79baa547e12bff73538973a73dc46579fdd364868046e18443d600c45fdb1bf31f13d7f37af54147760cd45871c0b307d75252bc35252c047dce6a5dd7a0891dae7389a20cb625a9329935ef6fbd7f5f76f88780ee603a6185369030b31b35397ddb6db90e568ec1347d6c99ec46f59bcf24adfe94c06e7fb48d87cc33190b9c25f4094a89d3fb09df8bf74409200526415a7f0244b0676e9867b7286321c9ffb519188feb372f45ba79ecb68ca426c2ce96b4922d617022cd1ed01026869266779b4db7c870b984d6540bb5c1220c2cbf1f28a3b105c86a47ea6f7afa0468cae8b752034ade45c289a1195aa9f5e93c2ccf8ea345619744ffac24a2b9de77e8cf1824effe074541afbc1cc2ca81e0b9b8f0a7800861cfa936f25e44bc7477f93036adab327116ec2b62360abd0bcf0d9058bcf75ad6eec795d8d9aa6a20c5cc9bc2e29d029175bb960f045353cd295d9a4efe967135e26163f2191186011123429e28d3d38b7c6f0da507feba27feb3b1f85053fc50c3aeae71ed1094e8c8115a52dd537c2a1cdd74a3f55086b4580dd00352a0ba3076651d561fb6202522e28f2d38ffb7d22be612765ce2a4f526735822614d7241f4e04e5ac5d12375419c5441410314d830487ae59c1e635708fb54a0cc1ff576cccce82979a303142e20e3a2871d3386e0b171dd71712a44a40e848e089355c783c89c8a7cd627d432cc0004166dafbe177e92bc68338b4ce91cb6c05fb1e1e3b0976c6a4f06440c34942aa01194af785d7af5ace40689b0ff91f0e511eec320d69b31c83416cdaef629f99ce7569ea23ea96196fad8cc18eadaa0747c5849405"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"56bb2078aaa767d675658812e079549ede846a69c09fa563acceb65eabf0852e","proof":"4813dfe233fdeba32b2083d77f4e3f50d168fd7ef617d4e83d4b796723bb9e6dfa10a90b4d67a0ba77ff22cc20f00073cdeb3ed4e83594436f2d98f966e9825c3cb5449cff1fb406ee5b2701e3b96f9777f127c016cb28269943d28b9367b62184b547a073e92629d3696bba5a3268d082d0843d2f768385ad11d2fe10a06e6bce1c319d1986628d38c6e39dfb08ef42b44135b55900d91c50928fab85eb22033f89fb0e232620d53a7c46a7a9c84eddf751e449ff427e0f862cc8904075c4034b75724aa91d113fc0d0d8c83aa794164a0e315eae2d781d101dd74548133607628dd2d77d90bc22b744902fdd88f81e30600670e409f54a9c8c843bd604d82286c5e69f8199e394677759955e4d070bbddeccd70d555af5a5d48f5b7177fb7e2e3ba2416e964e341df7ae92c4e1fb335a0dcfcc03ba1e86e76a265294cd0708e025b52972e40219cc96efba6774261a88210852e98546acf8d138739cf7d0453a375849172916784dc3c35d984962f8a1fa6823ec72233400b9b004eef1704978d9fb1ff3535f4c444e6d27902d6d9378ab993aae7da0e14ad390eddf962b349a0f2be7372d171e02acb50cb87cfe85bd97e661d1b0fbc166c8b4290f0431077e603d02ca100e84373a3a78fedfb1fe6483c270e3bed27cef6570a3a52efa125eec494a2f7e7e87144aea7e336136c9eabee480e45a0df49d32afaebcc4b612fe5213afc1900c60a18f45d5134a01c4a1cab12901e45994cdde1d96de90ed43800a62137569c8c2b50de4ebcca50c58d1e7b89166f8a1d7c8990e8ffcaecd14aa357904c1f3a7a79f6b84da248ba6c3aee323dcb409bc93309c493a5f4771561f0250ac349c0c33b5a24ab6d976149093691b48dd5cac6eafd830059a06bc04157543181522ca061a8ae8f83986c70c64b0a81485b73ed9143f099df8aaf205"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4a7a85da61d7f5189a8ffa7902c8bbe12707871181f9e87ca3bb1f443844111f","proof":"a4e116821023b179751945026e7795cdf47bd42d7c191c73fcef9eb8d21b474880244873469fb4593ae68724835c6f22f29d2f76f265bf92fb650ae0ad4c990f5404dc038093df6d55358eb57ccb3e5495b425f07d4ea94c4b0f2a32f4a03a1d0ebbd42202dcf953165a26552af2fb9bc143dcda6927314343c4dead8afcc454accf4cc6869c4bbacddf717f4de9b221ca7280149dadb2e4f213f6af0d472301859a61d208a73cf20204bc2fe0ff7a3895222e8147521bcc48acdbdc50bd630e92ed8b5870690d09ce1217bfe61ec0d425fa9189cb4eda75e7584d401ad7600e98ec25f216ef3c076e3675dc36aeca1459e1800909ec1ca095ac6467b48063228ebbf215c3b17395af2adcc6999380c3dce973131a6f5f6da08e0971f383c445f8123ab740b7c0778983670dd2ced5b028771b6ab223dee40ab2267baa754a10ce62625f752ade79663b784d7d2c6de545cf4f61b92dee122a0943b2d3e0f827fc6bfc8826b8d759322e428131c99503a3fe1ae62b8b18553f2df1047b79df6c4ca8ce27791266acde715baee2b5b86fdb55d65d141a04c041c1f49ff33a886fba79c4f87a379eb1b0e59a801da6f202906679dcfacda60198d5fcb6ada3f338769e213a20ee25f9732142bda576bab0a54e3e2e6ed0eee774f8619d999a452058e03f6d162cde7b47f2f115945653edea15d059085a11623a0581fd715ce6736ace5aab3363322873c41d511342ca6384196442fac36e21d72ae0518ff82b275230f222e7bd205fec79904c14650a59ee8c5107f9061ddff64207fb9ff5196b42877405fbdf3f46c1ef31646e7fb6af70d9e2d1b04441cf9a9dbf16f33b8b5f674a073ec10c2c308dfd255c361e37560182289964ad1e474e5f7b59cc17380959627748ffc575263c88f766b891da4fb175d41917e7b0989da45265718abb0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"322abe1a9c0dd965f2ceaf9dcbab08ba836479956aeacf813770ac31ea6f706d","proof":"e44be3cd5af9bf49dab9b81e58129221b361e2fd8b007213c844703753bdc148c85f008ef39626eb46da88f0bb46a99fa6baffcd72a94dc07a606ab4a51eb352c20b461ab4b38b6dc9a82b150b996df28df43b0cca37236bb5ce72695be2202ab6b7f64756a3cb5ad71d3c21df34a2b38cbb08ae79f09cbbbf8e7522a4abb132693ae51fe874c93ae14c1890ab79a4ecc146ede399a116648f34906e3300ee02d8803d314a95177cee21e5fd23a82bcff62d230bb54501643b7005f4d37de00cc83bfd7dec16caddb29d61c68c720bb8b4c601b2e0a78cff795e8a42ef5dc0080448580195a9bf81dfe25b4c7f2946d2ee93e84c18786e9ee759110819e5996196a359fea196aab687d070a28fa2cf1118ffb0df1f213c82370dd893d1b66d5dbc4cabb9854de943e84e3013787e771f60fd00c7a5b2c5b530f2860a886d4d3186822ad5c6c3c0dbb5a93b190d3744bfc230be254e711f650d64fb0fb934df56bc761b84601f915f65c5bb36020e99a9aa54e2086435eee0919522d4bfd732729acc4f75b3067d438a493df6cb360ed7cfa2f21f8ad0e85e01a9bdedab85040f7c69e123d07ddcec39e5b30083f89cd0cacec0f1d2b2e795c2759e0c1a9c8a369a4cdcf49cf93069904ab6a665ef923f07bbd0f4f3697dcb16c7aefc67fa1c411207761277d4b7036b2ebfb48a714d8b9663ad4c8b8750cbf691b8ac53e9035f22ebde8a0abca20f28d95ed9e4a740dc66a97d9c22d8eac05cc915d8e07cc00b762faad1439cc4b35fc8f9549594f7275e623bed0cfd0fdeb111e5f92e3c1b2104f500cfd8aa314207c9d950c93fc5507c4ed8936dae61c30891d5863694c04b5858351405bd70f073f965d29f657a0cdfaa2b6df871369075489eb604940e059ff8e0051e83b432d50b28748f64db1b08f877402583efde0fc1c97603502505"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f620ce81c75126a0ede3eabf124be84021b39e23ca093df912ab6ea7ed928e63","proof":"7c82bade0c82ff15253a79d8bfaf8bd5546c69706490c503f0658b812bdb846470a674de0f2963847bfe8ada8ac8e448fdc9c5363dc2706bf7bb3972696b6653ecef4077a6d4fb95664c8776ffdf5da8232d11e3d56c41d4f14d24e2f794165176045178281c6a41017920817c55c4c2420fe055cde0df9b7a9a070d010d2c5bdcbe3e9f270392c0221f9d1576fbf537d3eea0d4f5c9aa3d2b2d278aac31270ea02956a320cbbf18719962e1a5b8ea019c3b2a4aa373b4f2e71e9cec5ae10506bdd42cc94ddb4d6926fef563e43defaff8d58dfb46d3352ce7d7efcf6eb4cd08505d69ab5268cba3ca5bccdc1b84f4397cc6fccbd5b3e2a75ffbebf09bb99c6d665dc7efabddbd703c34a19d848aaa42a4b140f8ef31d28cdc3cf826a4d73160b6ed039bc38cb38e33cec18927d29eee7c616064b04d489b0944ca06da2c7004d60b02bb89a7b7c67f81eca59725f1c684895102cc4d2196cda49e631d1ac3206476d7ea51041e503c10c428b830e6bfc79b8df1783f09bd19b19e49deab8f496ac19b37d46b8145033db47221031f1b611c141fc40e58cbff6ef3c752d8d66b10da07afc8136d57c9426c10ab4ec401a1cd314357f749fd2e5eb2efecf33756522770ba973ed9493ba6598407ef93cc58d4deb43950519dec7dbcdee349fa39201eddbb88cb8fe9dbb28f3e5f3c64de71b4b5b722610b1815041f3c19bd22376031b9b71365dd0ff47cb6e171adbd5a3bd0a85df3be780318963f4753b16a696416708cda0b163ceb0fde28913346ea3895da3d152f30ef2eb1254fcfd89b3cb670b73f90584303c0dff874a4291cd82bf3c110f93ee5d5b51149aa3ab427499595ea919b45be204c2bb4ded0a99a511b6b77ef361e64ef6b70801b59e7670bbeeb942a4461e5b807bc23a06b9482539d50d77055bb4d734e98a151f48fdf08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1ed26a27449d36e15049d52a4cac0440f02d6cdaf7f367faf287997175bb292b","proof":"aa7b534d88d891f00cc30eda2ac2b7c96e9bdc69e92bd5c8460b6d1bcfeb5d75f61e48ccab92ac78eaa16f8c9a7dca9599c3fd80aea236c3c96b1e091b0c2222c21f135d3553a57fe3b5de41d8e877cdfdd7bc180329b329204a42073e4cde617604f112827c2cdc79058ab4d138d8849af27f17c1a74f64a145b9591fc7bc00d86e604de6a30fb2f29dac0719b1d35497e8c1481e55b3a6040d17e677bcc202d026b387fc60af95b2b89cc0ea622c40a11797bb39c0681f7e074bb9ec36050d793a1b71eb829ccfc37433c7caf983ae5917fa0ab769e3b995637a7a082a5e0ad85bfaec0a17d3243f9a5f06da0d5b687fbf9dded98e42c62b7f1e907d0d1b5eb4d229a8fb13cd170e1b3b1055a2a4c3fe01a5cec562570eb113cadde8407218a0b69c53617ad1a72791e12e970b54ceeb424e3313005129559227a4d97b984876458778ea0c337d383dd4350e24c2942c19479bba25b064129f803058ac3f2b0aed6af3762f20a82646ab108f242948394c1f60d8576f195affa163c9276878e0ab1e4d493be549db95e73f251315d7b76d023e8b1ee78d6d4dd1521aa3322baa9ec08c45666e7ce85eaee2f2b0ac45f5f65f3c9ef32dca2d828ca07d1b4e52fcefc9717d53b19af0d7b1cb83098372ed97ae04d81f28fff6ba6f3a5b22fa62a4e7766c7ea4850c66da2cf10e6b5837141f0d7b8c3dcb4ce2581391866f6c13ea9e4223b1ec0c806af778075804f56b3d813009a8df0aa0a1a958baebd6fe46ba9454d47f9a74491d2831f5bc654747aced395c389d660ab86900674a09fd7d6cc7d374e899d5704b1afbffa71732ca6ada08e0a101a5d815fb98c5b4b4486bc093fe2eaffd8d6db7b4810bab381aa1b5ebdcb19d7151bc474bd9dda1f4e10f6ed0077a567fff5a65c53b6e263f0c4bbb222e1576e1a3c0232cf59235898703"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7422f8c0c23bf6bba2db2a3aa76669a3be5a6010e41085878c3b049319722b10","proof":"de9a6d3f0778142344dd6def64c176ddd41640dc49de81ec14aaedc8cd37296086eeffd68798a5618423c0a3ca7080d9bf674eb3a21c812bbd09c799073d455c8cee684e058c07db7f86462f53ef3ae38a188e210c81ba627842a236296bf556e88b98562cd4adc426f06885742e3ed2b463ab7c35fa8553b32374f140f24d7d436a8631873709b6e26808f4ab1ad0d178812f5805e493e82754dca85eba4c02689091fdf7cc8ff21f2affec040dde1a64de223d137041fa1d881fda0d0be90ff80c79cdec727a5b5c1ebb8114abeef58d61cb8a9f162e73fff400672ef7ff0becad7a2616e518982261f05fb9a66634940d1dedfdcb06aaba37dce8b0764a1e3a163090c5b37d5c0099ef63cd12d8cc0c4720ec353a98c23e5a1b532d84173304cc040c8c2ac4f94a83de765853caa15f83e9e7401785f7edac35fb3147295cc091c95a15a60f6b75acf8fd012bc495371791fbf6dd2c7115ab3e6888ab914f84d75ac48d025df8bc18eda93187770a82ebd9cc34002391b07a462dd86b9404ae0dbb4f85b076dd1d8daefa8821233525b09f0fe1e224fbca64e05c64f5215196c0087c55cdeb74db12a6f20287b3871ff4e2c7f1d6c2fc2a0bf73335504e73d0fc77380028509b285e1fdbc2f2951869d072f7bd8ef7f43a9d64a48cd5a53ce2956d1e08423e4bd081bb690a36d054e56502a23e95215bc349d0a7406daf5e98a9b5abac926f94892b2fce3f6ed2e95b5396a6c755f7ab53717a1dae3db921142fa474b94af6d3c9b2e9fe044144321559c2cdf08a1a3d9fdbd3746e628443e63c80974699e030f4cf702a045a1db95e8dadf1ca240773509cb797014b0a6fb4663d5b341284940dd11711370f332417363a59ff4a8237e61fc74188fecd0b17c908e191c1e3d8afe284b9f90c437516cf068e4dd72be36bf60d3967b1b108"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3c1dd812ee52ac9b7430bfb816e5551221bd509e62371f022d7a74bed760e970","proof":"6ca45cb5041eafcefdffae438a818d8a9dd311b6ebb28cd580fa781a9f2e420112895dbf1d4d0e1922b08a791bf327426d3a0b8b5e4a0dd1f5aad81ba6f6021b565e783c14de2bdb80a0b78f2b98ecaa958c4a4d59562bae97910be1cca7b6431e366e2a48da7e4f2a5c09c227fb8f53c7d16eab04fd6e66863e0658a55df47e4f3f36b46f8fd8b02dc23d5b5a3daef9081fd13a6e2a4f009490d65557eedb04cff4faec2fd9b4d7f5af943b48dceae83a7e0b62e57cb33fcd9ed2e846b21600be375b0f3d5a11a7734da1dfb576ac633809bdcdb620adc0ca4ae2aa5a6b960902c1f068cc65615b515018c07b72bcf4e262853fe8459baf4e87db7dbf47030b36afbc4cff6fb55b8f8f23f228336f2d9cd6e5bb42ad3e77ef95e278183242056ccbd2b213a83d6ee1c1aa1d8314c6b1918fc27234cb3a843d0e29079ce9c46d8e3db783e88cbd828cb7fe4f94688215fd32b354e00c67eca6a5f0f8743006683832c2745482cb8830aeb5eb46e51478455be4a019b11f29fde2236d63778b39a6a4ad54bfbe5002f34ee712ab8d25c6294c046d197a2ad7687f68a61680b6547cdc187823578fd9f36bc7868ad0f6b76c5a4788e75dbecd8929548fc9f87638ea4f43ec42df3d87fc3c11ba348a3baacbf50ab85746c2f4173f27081daebf53a0ce8d70f70e52cff0681bc752690244e9d778f19f6560030fc6f1cece5b953ba82139c3ced7185e005dc69747daacda4da477bb6ccd5779acc67ceac70db44070cbed532e460571e0ae1052cec3094bf589863904e74e168d4f49a21758c85f8cb8a78e90a80e41d88872ad59dda65c41be820426a4889eee488393bab3f57f2d67b6767b22d6ed7818456df5a895f8b544830c9c96b056472a732fa9ed4b07911374cbdf5c99cb978404baedb360dec8c25c89f70bda505ee936b1863b610e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"80912c927df91b05d313180edf8348f4217f94ac39055c3ca1d5ce403749107f","proof":"347012f70d8db9e29a5407d01b76f2d94bb6e2d8a143fbf183d7d53c21bf8d31769cf4761846ff6523ec487b05069ff239d19e4f0e83f42ee642d2d9100ea727c800c58ee63055fe03584e078219d72a910052b78ab66352c64fe8f1ddcf7a60faadb74ac4e13548c65ef66f78ed87ad51b1c129fc992834a16280d84373297535c339a645a4fc57906dbcc8ed8007e1a9c44daa4a2e4280296902023457980ecabd5d87e0d15943edf92e2f2f8965505fb61e2320b6b21f5e00b13fd7cb520f49fe2d5b5749204c3ac6e479da2174dfb7af913e67dfd3cbe95a9bcdcd0f7c06b41a6206e24f9cf5ea00ec7207bfa0276852c5d02bf6e6c5e94ebb19b042fc05aee7ccde80e78b193c2895e7a8635918deb7d27ed5d30e71bdcc331977704e10c0e67bf5237ddba7f818605887ff8c86e1ed9acfdc343064e1e9b7a3261fd340482053e65276a6de3106cd42d648e7d89f07c6b676e964a0c78069a632800612f0a3bd6cdba118d5e35d1f0a0e1803d7c7784c42bcb6aa06a6d530900c9708649ca7a808828eda9579616f1809b562ceaa128a977e73f4c34644338717a72023185cd4c41fcaa1ce6b575dfa6f17cf8c7f56293ffcf535cb67a3bca75ce9871c125918109f0ce78c6fb8a3e28dd1fdf549b0691954156f005ab94190156b6b05cad11dde43f9631bc06be36319b5ee39342aaa2c1c764f89ac7be23f602e666e001ba7651f572dabde3d8a1f08e3f40855b50f7460fe2d7a94dbd2e213fb0124ac37fea7f4cbe88e9e67e0191c0884a099e67a278cea59f2b95122d2d0f2033eda3720b4667da6d20d9b39bf9b467f8718d2304f9fe571087d0635ea084cf90611043109f53a2dfb0c17d69da598ffe3f788f13b6fc2a17e6d275c3990aec20a4bbb9ff6c607d52bdc3fba0d244c2b0e36d5f8f9bf41ec8310a990958b21810e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"be3570a80d4be95005481c2c6c74e588043b7dd919252761f5333f72661c7245","proof":"066b5608903173b9c15edb9069f52bab0d09f18674eb96b56c4da44d2df0df2862f9ee5c9fa2e97753d88616d77e3413396912d9f4efb423b2ce825c8edd9e78b403ac9ca03cddc8715f8759e01011f414591edead4fb172637b627366ffff4c9a321cd19e7879c6b38018daed4518e419eac52fa85ebe6d9dd625acfa15e10ffcbc3fff86fea9c9bc732667dadedb9f0c804a234c658b574fbe76973d91e6039917b0e323d8b3a250616a02467a6c7b26571d3857e27a58af339840fbea40096bfb42ce481f087a0b33dd9027b84bf7dfd66e6f92106344a1d6ba0a88f9d5025a008adb44adb85af5cdcd72db076697bb85dba40e49857138ed0f81c90b3e6f3a8cfbba3081547da89c415a096d7de981011833a8b835b2a006444036f47f4b9c839c810efcd023e4a98129ec34ca506240f76b388f8e3df927ca8d2c761d5ec80095b07e49afdfe953ec423f7c112a7cf74a533d3e223cfc2b80500c7ac903ceb44be0767a272901eb5d669890424c6a6135e3c5211944aeeb287b44313e0e2e0314c4004e9a6dba07f8b816d51f1a7ed6e6b667cf4763b38a42728dac8263b688f04a0ba03aa10d08aaed6a70e9f7021f4be22960a12a4a1967c74c81f078065906891010d84b4aed8fdb1d82b3d96d7d8cb341fac5ac4af2caf2c0c6305f22ba1e234246476062608f7b0f274427511535e866697a2fc841e918016c5a59ee51c0ce89b25ec310609f917b560cb2296f451c8b356ab7bd92a9d4e41e191bb0c52b48d49b4fd59155cc3a6f3bcc6c07113e7f514307ac11628ec45fb24f6948301ba412a6bae7073b773d7f007690c81a9681b715c170be945ba71e5dea65a9cb7d3f89f46001f1b6abae9516287b3544c9fe894bff5ba0dd6f8d25b0950d1ab898e8ee22b64b0edf48b85074cefe9ae73fb58cd31906635150f0090bee0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dc3ee1d7f2975216e087079183109726ee16a0f42969ceb7435d4d9d1b35f811","proof":"1a14f8ca1d9715bd984109dd05e24243b9e66142615ee97dad116317eec98d042450b385f4373172663b57266b3bc92f9e88e491ed57a1c4051d11c77d7048699ce504934eaa216eb98d9ca0b2284f67f89a344ed554ed53c8458e4196f976516a5b82b632a3fabd295ca51f1e77e59280c8bb86ab47c907a21285274b22f04a644eaa8a124d71eb26a44c6c1ce941c08649c1ecd837843ecca354511c7d9c06321f2d385bae45166ac73dd366df4768404d42bbf9f683891e36f894780aaf0ec2be74af402149f6be8ad0d93fef1fd7f9fb2ed2f8292fe89578f6894bc61e052c6738774617f181bc49c2c791a831c7e0a10dcd4369f69006f9e1c544cadc67ae097ab3f9c9ac00b4090a5e5b701f087e61b268a3d0c1f55b035f4cc2bf985360a3ab8797c9390f6e7d965851fc8ba259a1092622c7f7fb17075d56fb5fe514e2294f00474f91b98bf887ea8f3409b59ed6f1cc25b81952786fa25ae567554104d7c8a6b7d75c863ac4b6109cd43f01887e3134f93d35836b5d7022c8c413224a6a0923e302c3b2f411a1657011ed60f5846dd2cf2895540f55c28c5fcee9335cec4713fb48f2ccf740e1f561ee3d3edfd52b98f26b5b806ff4b14fa953c61ff83a63f09b4150347673070b1ae6a5a5cbd2461b0b27c6330e87dfad0679f3151866fa0b75343e4971f0397f2bb32e9ffc97e52b8ce7c1c17df6ce0cca5d2a55d4e6f2659e56225b7d6def1273050fb5ace038f677f021448875750fb01bc04f547f76eacd4878807302de5eb67f9f7068f2b3fa8888c2d834566d52ba26d16614612f9aa981cc0f744259759a24953c8a30aad8f2be6afc8f78afeee507d4500bfc4d5c7588a3d330679b0a54de01ef6a1553ec0bf3cd7f59a595cca0b92400b4180e916e07c93c7885d4889e654e7ea0b9fa9e110076f3b37b04bcaae87807"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c8bafe42cdd12ffeb7fbb94d908bf976a1446176b2cba3a347423ac66958795b","proof":"cc8ce67fe5b40c72e5ea9a3fcec0298bc608844df16a5f5ec7931da86f68fa0f4c42d9edddca776c76defe2470f960fbab60ffec15ab569a1aafe605d7c2d27f04a265ece3e9b1c6e728757d5245e2e80adc5cadff79683b45f6539f39f5b41ed2fa3c8de388b9f26da252468dbad116ebc40584eb5b2d4611aaa51f99ad190646268b8b74be8d7dddd9e989409dd4bd58b5dcba5fac504603bfb2d4e293650fb7a45eb6ed2a9081edace2757e20291950934056ef144696a1a080d5bc0ca50a17dc092684dc5afa6d7409d7d8d90f8844890674f66cdeb77cac1a7e0427ac0d685e7f5a8f03f937a09b6b731be1a6eb609ddabf977d96b2023088be39092d2e241306bcd5cdb9ccc4448e5e659f97ab0059f398722ca2af3cb858403dd7c60be881247a3ed35833d57472610aeb1e4ffcbb009912bbcff30792bee8f6081e56f62bf5f6f7f73e154d0b14eda3e2670f4976f9f13c2b917a1c315d6a4fa6f146de75e74d5ff53f1e91f6b67f37d0675b4c78d32cba7d24770386a94a4cd29e5f4cc41a90b41b53f9ae781576559bf06eeca5cc6b544a8cba68a438d3f6c4fc4ad26190e3630e8b2d34411e5af31da3257e65001ac692ec53c9d30afa6c7ef4404edb622e328f175991438683cb8a5c35e60c9ed48d904693bbbcad69f6f3ba6e5caa13d86c1d315973f4ee41c1f88556c86a8b6e02312f6756d7d72919eec175e88090fa75021330089af442905e554fd01a3e5e94355e6d250b576fa3385a13dcc0b8db31d2f4004faaf86e9d2e63b939cacba4b09ee92dba27ecf1ff786a1dbab65e49a04a51debdd2ce035ef7879ee6ea75a7cba0db06722236614ef6ba04d4a8400088d3c0e428cc2dc571e4273df607666c8376702ab65a70b97c2aea0b82e2116ff7ad2afeab457828cef643cd706aeee62ad09a16eac15b9cabf2f903"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"32dcd6c2ebcb1ec3642c388d84f96fd29b33deed5e3fb9536bc56d0106462d5b","proof":"7a19fc338011d17b24a34419037dbb6e1a38fc9b35357f5b7ed0a35ee2b82e655eb82b4a352b31b223f04688510304a0e987ec3a9346de26962f4fb2b2ff0567acf9827b40bb7cfee9fa1fa4f44e60fd8761d1eaca1498f89c534f9a57b18c54682b0e9fe3afc242a09f5ae12cc2924d46fac8b71ad024d9169dada61d6c9f54b1be693b571dfa8da47b853125bb074fc04ce7462c3f300db3df44234c2dde0167b031fbaca64370db8d666d765a6b79f9ae996b1bbd07de926ba4547c0eb00f0aef47c10a132487127f9cc0d6bafa1ebee534a537085d775311b3dc724cf009dc029f08c003c05c6508bf3627237faa966b6a812ababdf819f506de4716387220e20dc40e86ecae094a0d44e341b14e4945cfcc14d241dadee11154fda2f542e89622005a96409a949c7b0cedebc9a1cde8efe1c52dc90a351c5909a7955d3296b86e0387d81c53c620b4b19eb858122c7756638a1f00fa07f918f26766905bec3aa322127df6b40e94ee55b9616a76fd37c663022c65d7c7523efee0aa6f045ee7558ccbbff50eb15e827b9da420dbc52fd96353a1b53dca38cd8e6bffcf62b61b0fa8100c05719f15ae160424aa03e2b549076808b3aa8998b613c597e63588d0c86c082d8b726bfc0eda573f7132a18efb7e5900cd64a2adb7cf59fc127bfed30a06119e570e604bb12a5706a7d949e127ee21bb06168f53111a0855f67e2e4d720a5c0aaca42e522029a8d1594fdc240f263a4d6bedd6578cc67e40910c3853177ea1d5f54404a4cde1d85dc87a9837ac38796b43180b48d85d0db6ea7a0aaf504d86d3ddf51267f1c51a726c2804941a1bd68557eba1f5a0792ba953275002a0045fc2841d78dde5c7d83d0e226a83e5cef3ff9c518dae0653fc9dab00429acd564c4ea51e5596e71cf703d6f40f02030905acc8016a6232a42aac950d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bcf28249bc6c15c7b184f1ef8b071c76f7fffc81d71d69587e82c2b6be081e0f","proof":"c81e9f3da5743d35f2ca29e25386bf37164e96ce3d4d919db1727810d62f736faa2dbaa743eb7d1fe79d82ea85923d50d305ee992d1c25843270811f434b4642faa0a9c56d2ed35e83a3102385880f46b86fd509ee9262b95c730efed615f6396e719e563767591787d428df181175a1a35bd500142b93664b31d4f36a182b5f8d8dcf3187ec284959143bdf1a5eac63bd590e931953cadd81d20a369f9ae70ba75b85944c74499a148cbdc92d74534b1f2dcf7e55dfa70c795960c622046d06d0bf5bbc5f85e9315afedb2a7ba9d8dd7b467c027f0a85aad60e42fc2b4c2001c2e94c0ec50dd79289b39b08f9d7f402959c48d5ac1685b93713e4ed7d417f6994c3be5f8ad52cdbd174c5c30f01e81462c6777abd98b6ad718b04687ca64c3c7c765707a562cfc2dfc7ff815657bcb17a3d9a0ee4d1946f769bdff1d742b472ce252f268ca774bfe093e575054d48d3c4004e550965c795897fecc6fb0d0400e48af1893e15d03ef7599d7c71914bf0c9bcd266975b3f7a0cee5dbf90badd794edef60fdaae2fca2e8ae930f38eb6e5e16a9c867e70a17abbc8d19edbb4002b18362ad1e4908b257dd833b9dec170d83b4ac31493c613883e77101a570fee4646cca2f84fda696f570577349cd8a3288efc596ab209a1179acdaf6f0ffc544f4600834e3cb872009a647fcb989933b7bf78ddf66ad4976aaf1c8bf524c3936786e763316bfefe37c9a57a8b1fa37f05a49ad524e84ddbcbaaff8911d643132f168f2267e4658ccd443dc8744f0c1d250ed8f2039df1e1e94c7c7fbab1e9d4755013cc265d1883ce1e349aa0809eab328e10c665b6e34f7fc869974a5d14cb1fff50216a70ad1a1fecc977e2ccb83da4b5f7b7959b977a07ad3d1bad98c2780032572ca6e56c7e38671307f54818728334e5cd07fbc22545325848e5d6b69d01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f063b0a574b8a67449a30dcb93f1060971511da76bc21e475eee424c3326b50c","proof":"1aea3556be11534afc70b35f4c59b643277bb4e61da6614484124decd9d85403e260b995abf00f378304938751379029bc2784f49bec8616eb6dac6e0ceecd2044cc0478cba49eb164a3376a9f13bf21d246014ddca9d89c20ca2eda525d0f4d24dd7751755f9261fa573a988191b1452a44e24883fbf939cd9680dcfe24a051cfc9dda47d822fd2d12bd1fba86125a63d413af9e49f3ec41e43273a8854de02db8aea059cf12cea66b3199b554615b2965a27bd22978380e99c9efa6859020b60770748e056f38eee528936e0790b5a92db7616ca554932c1b7fd6e19de9e0158cc7b568207d1d9dd77691531399d4a7fc703400dc7b6a9535e51d8f63930374ced975ddd61275c9db6fbe04c78d828173fdcffb751e6261d4ff33b86c1192888f7c4b92ba4227aa4331a90883db6a96eee5e1f6d144565ae39cfcf8175651620296e882412fe921bd18f1af73857b9a90ff7c0fca684fef2f94a5343d0ea606c895e70a357adea9af18ba2624a04a90643eb15694561bfb22946cdac3b47414ce0fdd44d9ac387519b7bb2fea2f9ecea881da774aa4c4244cc0ce4208a315c66a6d4485876940a0b8725500a22bc6af058077632c530162fef0481e62c546d7ec68c2f570230f5d00e737cade0945e77775a1003458431768ec40471fb541172326af4555fbae2c1e58b78b5bb013fa5b5cd79bc48378e78f6fd613eb6ce0c14dfa1b17296b484ce3b9228bcc5ed221a8da8e80d13aba89dc52813d045241526cbd04fd6c5baa68d56422f8fc7649f00f436185d681b9902c7ba037333b22746ad3cb048417c3a84392f0eef68181c5765bb5dccdd18ff489bce49c5bd66479b45c5e1e904dd4414be09daed4d9ba30bcaa2035f4abab0dcbad3a3dc9f0602b72200029bab864c3575f021071bbe3a603b2008aad4e9758bbfa07b4fa60a02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a80c377f93d6e6fb9d503eea28cc457026f473b85d242b8c95f5cb8092b58403","proof":"9cfc1c4f6f7a9c25febc59a2acea654d362f2d22b2f7ae932419197ffee1e5554efd5e3082f49be07a5cfc3fa4f44f33dfe843d6b4c0bc98c9f2c9dfe4753e44ca6a357925d00dd1675dceb51061ddc34384beb691144a30189c01171754af59c0790d3a895422736f9112773c6491da1dc5688550f6f1992c5224291361e83206c28f6588bdb47e152c79cb5ef0d73a83ef06e45bc3790536633b48fe87f80a2dae472a77dc3c4a7117e80e084766fb64358f74d56d9aa0946b460dbd5b6607982630fab7ee61e1cff13d26c9ed7456dad0daad69ce9ed2df4b2a298c26e50786fb9f15d1395ae7a2e48cce301407e453113e35fb0e602d67bef3fb5d90e52b46393249a99fed6d1140b0210ca1e6f177d364626d1804a7a6ab2ac651f0c5390a14437e17a63f84279fbaecbe99621693e608bd25d0b1ee1a74d88838f67c1e9a8b9bb815d888d3e1d6b4bf0e65fbba8470145fc2bec326d353cd4950e0a10b8e016bfe64b0b3dc7d3300b78b40cd44ff85a7f0315ecb3a771c911601281773224e905af8792eec72f7e71a2267c90f6755904111d2866273109c4bf18d3f61141892b2b4c46d20aec0d08501e6ae27dc97ce103b4d101a66f9b01b723be03104b669da028f4dba0ecf172cbcff736e3662560ef753cfc7943ba0aea6601479e62ed66eb077668aa56a12915f30623caa70bd19a532156275bbfd8f939df221b81fe0698b9f848f0eb00ca4f0ce85ffa2891f9c13b1224b61fd97852e953c09501a8a0995888d2b89c5348152d309fe89bcbf5b1cb728e83151714d5446ab2dd861643dbe9dc5becb88157fed36afa8701f6a7165d0c8e661b537518ffe9a3a078b032f45bb1a0373659f7302fd819635a8dc3f3c4b6f9bd248b352c0f34c09b16505ec3390c928c97bb4862059a046cbd123a0541a128e25eff7564cca640e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"02cd069474af3f3729db98932a86125a4557f54f6cdc474bb39076b409300c7b","proof":"843ee1879cc1f39eb83ad8f690879a4f39130b15d4ff6d4ea7c01189cea892123afa3a232e7b1490ff14eb0a67763cf29884d1ffa274a331b562ea7e139a83478a27566fe3cf1da398c7895f47112575543313d72862ccac0f0127eab19ff50322362d3881df5f9a27bccbd954965add93a55072220d9e54271abd4ff21c38733b1a7869e7136a170949c098bea2f18a40a95438c2aa73f33779de228be92706ee0ec7ffa49948dbdeefa5572bec3285e86ced3c8907b1f61c47db9a867f8808179bf9ff731eabb48524c927b1f89efbc1dbca27241159c830b2d30e8d90630fb4816d96394f9ddf7ffd1157b0febb3cfa16078a4f1586d37ef48fe049c5396e765d45ffa9b1617aeebe7ba8633cd16828a764ab928a3235b8aeda5a528e846070d29309233367cd3d41bafa1acf1160f4c4e62040953d206c3f1536d1d6544fa68659af2dec60a681dfbac66afb3b77c5fe9523027498ea1b3e20b48d9778250abc27bd5ed7cc282bfba3d68002526d02283ad237bf8a47522c0ee052ce883146cd3c9a8e7a21610dbf3fd19887aa75fdd0a1da7eb8da0fbf79e5127c956129783365d7904e3be9493f8b1821d10d7dde95ca846aec18d139f8317923bc186564cc30035ac700ac111ac6d7696878a5d5cb858b462e70cd697d9c7096a23c324c1d41a2992f83272e2f600fec851d08f7160800ad35817ac7c295d1af41c27190dfc355d735aa602e1b929bc95721c70e1a7265fb4b5682b289144cad581d2320015b098ebc518c5f9a1598fe1157a871ff48f8f630eff37d22a0f55cae7e6dd8b1f8bd6dd1871f1784ebeeb7768de0d389d2761d9cb28d115d067102e3023cbb09f8ca1ae54d13d0e5f8d91c12967e5e2720d1fea7a171be8f304d00d9ad0ccdc2aa1fc55c25d1a0e669fe0a60f1bb53cee5e97324029169e1849398fbd40c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b662352fab46815212e43dc9a0fe8a63a3ef7582a32be85b340f1e026997d620","proof":"80a5708799439c84696b991c0dbae3354e942b30d0239995c5f47e174192587ec26accb61a69c3346880643f79694d717977c9628198a5e17b37b08ff944390ea6a1676fa3b8cc191c25202d9e7378ff3d1f548f4e56e80a5666cd923b76dc4412198156ea2c4cf3a471aa175b6e45044853eae215c2fc7b4bf0b71f4dfe3f07567c15bd6bb856a41d9a3786c25918c49b3cb4d889e4a46f8c33a91e24b1110c1b43c50ef7ec491e2556b006780e4ff6936aee0258eefc4dde18048a358b6d0582641683d28c5b8e3c9eca2c5451e48c47de1fcc4d2d36e5d930f47bf34d8f07b8d08b8931fbef181e2e400476a6db4d0a848b79f334ecfd158326066dc42d0340602854de7e11d326820290c5365de761a76ca51dbc7d3ddab69a59742e3a0e30dd591a4d4cb4f1c25738c41bd3e273a00a8cff94ce79e284e6ab97cdc0be1de4c41f46246f36b1054c7fec1ea30e5056e00c776a8e0f482c8262691a522c7d3c5254a4eaa48767daad0a72a3c8c46c05d8e26801b95f9eaee2d3ce30b91a21b8b7c9cbd6820d497f35650ba4e2ccc99e5e00739467b136a9b756ee347ea829be9286f9d517493813a18e0047641f51c23446e77a96ed0bf5607259e24a060a384e4ed938450200c9cba325fdb8df8323a980e5eb5b91ae8ae83948bbcd7902f6cad04ec4deddf2d8425fd79805d64b3310b6f6d94157551cd200916007e56684da7df228feebba9d8cc17ff1e0e67f3afd6b160fe14fbf60940e1018d4bb093ab55a1aa24bffc42ee862d5a7db03fc9e73d9386bb479efbbdec623dbc87b358ef457ab946d1b70ed25d8f8b13e55781173fda324914be68b835754f43345490e9d2c8c973c97dc4c977380fa2075a544dfa728d88b8422cbbbc42dd749370d559a2883f362606e5130a75f34fdeaec6602189128919ff8d5eabe48ad1a7203"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"44bf27e64cc95dca4f68482ca52bbfd84cd57aaa3781a748c6ffab34fc8f1911","proof":"70067da2062efa6ec2543e6d9c51bd2c098ec44b21beece0856b5a856440676876c4ad67efd2cb8543ac6f15183e6dd4737547b89dac8c023a0b29d63792e67a167cb5d45c8ab6584856a60e81ff0d060a17f4bceeb69381225961219791e35a2c714c346d548de5ced12e3b9babdfc9ced1f30cdf5a72d53c416a6d00308a4464cce75e8ab8ac0bb8f569921312e0525b7be45828eb31244d2bdccd5256a100af9e1eb57d0ddd35590ac8d4f93bc4914af629d1993aad8f5c0264aa71c4a604f94dc1946c2da3884e472f0c8e4735bc67cad9da64c8a4381e2a56b044e24d047ef7832f29d045bcbd3425a0a6e29606b80b519738e9a5c0a7d819e1023f3f05b83a60099632dec84f3b01aa882e9f7ca0799cfe77ff4d6594e20b71b92e4a165289f9e19d7c8e2c6597ecfa198ecfdd8dd0be51409240ea00e1fd46420ffb1b72c049fac563302fd54c230b538362074e4a872f093695a980180160b431cd6b706cd0a53dde84bbbf7cc326bcb5d45b71e775061421cdbfa00e279f54aedf633af8ca753cdc38fa2d943d9b91dc351664a9b44a96a1e37ccf7cf46234af181e2250457a73328e1218ebf8644a26bc2a8108b6557eccabc7fd0b4e4a4b0a3b209e3bd82ca94868b4e3cb2ec37a17209c8e0fcb85cfefc948ad4a109a6d7edc227e5f508e030b743783b2682d676ca5cd6a5f50cfd1302f242755c3871ba52074dc99ffcfeeaa3dd3d4d5bc060461adc323315736081783570bee408607e2ff79c21d04f8bb3e051c8467c3e8072ef873f9490f90249bec26a0911dff14d5133f6c20f5cb44fc6f2709749c6fd3f9ef57340001f9ef7daefff3e67a6d55046a611b525065cef63e2e69142c895d287b081b2e64544e6bc83809c65757ac6a770e9300073d83e86ea73c15b53c55322ba8a49f50b2a1eb604fdb20fcfbc61eb502"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"eef185e1dadb8382fa984394e7bc83a917af9b11628982b511c4d65d52053622","proof":"6cbbaceb95d299b1a583dd99378bb9372a3c9563e9b9d3cedaa94de449e7070748cb5796929d086dff042fd74bc1f0b3cae5eede977ae91da81d6f2fbee96f01ec25bebbc93f6eee545d3e08ee506a03a3ca420e7535e29e9b72e1ee5231ac1fd85662a23b82eb82c488818a88c45fb00c76d679216e68bcb65fce375adcb13f14576f19c8f52f1abce6224e04805cc5fe1edee8c1893cc8a4f3b01a768c4e0e71e980361b8cef4c8ca6fd080c3d5e5f62b2bb57df9c669cf620026a947ef201d670f3232abb24bb2ab76ebf7dcf1863051d9a8d881dd2a318a415bf0632ff0d5a16c9be8a3267f0ae894b6069bd8c43e84a30fe8711d973c2772b894d912b4740618ae43eec07101faf400b3cba26c2c445d05b81a55f6cdbd3968ebaf5c527ca8c0519a4dfffe0fd7d8025e33283613b170576b5af266c648ac674c4be1827fe32e71917a71887f2b7f5775a2594494b8c6131ba01a4a35b28a27694d8ad7a4eedf737de9d200bc81d8c0f79bd0c10f34d1dd59a248c4ec9acc895355f112d2ea44c5702514ceed6151bd7af2fdc75b3a0f2b2060d3e6d73f2138f3d36b942325e7643b20f08ffddecc83f9b9a1872f0169c9db8437cde3c8f55b6e617e748544aa150f4f24f879439cfae97c4c36c08e9d03ec0ddf47994be0b29a13a615518b7819babf0b47c4235b319fbf9a140453d67ac8ff8b1b712e41513bfb64f00f2fef9cd6bcb2a8cd807d5c6b2264af9446c4e230ed135b6a36c655aa474867cf0fa3d974ad898fc5fbb0a733daaf781940f53601cc165f8acdeb3a3fb38245b12de3fbbe5624c6e6a6528e94d9b3313c71e91f5513426e47a72b15d5134f501c88320f7eb423c0300f865a9064166e5a1274d1503d1f200eeb23f3dad455d0b026eef9816bfc279c988588cd91c59be058ef711de362d069274da616f31d10d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"accf7662aa9a608c2856e78d56b466ff7ba100390683670778de3980a9c7fc3f","proof":"fc4eb0a815b4527e7dd39d3cfd0cf7cd7e3d3791b18d4e61323ee417a8b7d53e1ad190c05e111ee478e11ecfc82327d2197f43857c7e7c6fd802b4d0dd01a144480c384ee53cac0c41e4d1bc2cda933dcaf43123fcc5c00b5fc5f5af588a2122da63f90efb1967759bbc5d628467cdf2c615024447a54c51b45598238628f71bee120e91858eaaa1e60abd895b344c62dd3aa6cb77aa172e73437476115d2e0d48e2e05168e1c8a053d1c000c6ac370f3c61c911d78f4094bdce6e2cdb670c0f0c70dabea832d5d7f78360778f528dfa9b887088cd0ae68ec0ac3137325db80dc6d71c9f886f6bef273dab482ded903b777b687d109b33241d141603e6a9195eac2ab380800923038ec1264843bb25e2221e8fde04ae686b331d29855f31534d6267638e9a4e1e20f80c790ed6768d02728416414c4652c8195fce530cc36b7bb662321fa3cc2a5edb70ae83d860b97552a50f4fc1f8dbef69ea8ad376c5fa27a883e60d059483ead9cf9820658bf16d48e632d5ca018bb20976ffaba4c7e5141876fbb4dd64c1a4ca6a8510b524d03975834d201d134fd535c6e7989db0b92f88667c913aff44c2691860135e6e29099833f4d48db066ff7fe44cf0739e54269ae667f52d9f6a7ce2fe524e34b6ed3cd1246d504a2e57a184008a0eb18d3a6720dcd552ee349919ebc72e619dfdb806d68a46b87e3fc5ceae332abd99df3d321cccc01751fc3e2d7485118ed8558fcbae74124d8d031f584a5cff7fce308d69126c0391dcabe1a7b945e5ffd41d4a1675258073e0b5eeb4fcc5deecc3fbf5396aad86e1e6503308cdb62af09713bee93f695e9247552a0a7a8a9faa59133c113bdddaf7e089a66309fff678a9e4d14bf9216caa02810c5aaa8c068a3bb38a091044ab8dbf4843bfe6dff0b2fa990217af9387a8fb493a6b85c8cd3dffd4250c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"08f59ad16b3cd6877503bca668eb16f6b257f06190f8ff4d16c91f624e44833b","proof":"0c3c3e66b3128920715022cde3635bd8f22744526bff3eb6d81bc753e551b513bade017ddf6df9e0fa12e951f0de85e4915fb8171d39dd0d96b4a0124135b4625aec52b216e154c7e170260ae8a2671cd1c8eeb642d5c6861c5b7549c786d2054c0c7c8b6274d73a470570102b583d07dacbb7b00d209cb5494d351f102677432827653d45c72b855d14ba5f7cfc465326b13e9a0c82b70b96ef0c35f7fceb007a9049d32c50a4dd21f25f9b872a2c4b3c91177f63ccede8fffdfebf4c82bf0ee7fc37efc48428090b51704dd06847cc07fdd8b128aa05635eb29aca9e732e0140585a41d20264fd496b78962970af6853f3f8fe031d9ac81db5a8b48a069b6f2e13d8ed839cba0414b84bac03f49deca59d3e05823f7c7d0dce1e0ca9ce674530eae843e506b0255a68f642c62101fbd6d68c30d863059a83c34ec7c5cfc557ecb0834f50874a1c277e5bc97b018f6a09abe13c83c634344f2c1ee2bdded75152655169dbea5132dda77b6850b1f4fbfdc94ff97140d8806782e0b762d9307c1444fa7945426e1447c6a750a41c710c2e5f155b709615f0410cfe1604b626269a220b6944a0ccec9c3234b8c31c97036705bc7ec9b18199656abfce31b3d5046c6a41180b85132707d060eca1e09668a63d74eef8d57dcccfa30bc64e736c5c78b4c47e23ac104bb29f7b531fdcd1fa85b0ef4e24387c88f8ccd90be03ffb7d7ce2c17730d82ac04fc9e1409005148ad9771b62bf21669dba5df2f6fb1e2a226675c09890bd563e291eaaf6d6cc150fbabf04b551b55043cd18f7732fab335dc6072634f44afa9415f7ba46215a1d0b6a267fa7cd748bee49425f445299624b737e22563c85def839a93b870dff027a6561795fa8a21bc758672d92d6653303de32c45027c71bec2c68fb0ca6239e0c10a530112742fb88549c4fcf57513400"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bea039598b4840e22b7e672a9da63211163b91f13c8280eac8971f678de24251","proof":"f80b104ef3b8edeb6846b60172da3b8a972ce184c4b398bc9cd92cf1c282d51be60368fdd0f5401754ca5a888436a6fc838a35ade62781e249e043368122682f8a5bbc866e27eaf8c881602e81e788b99c7f19b59177af1570e6783b981a004002c8d070c78434288192ca10ee2ce10ed3169ba5a427cf9760a36abf5f7a2862d59d0ff532e923f27ef32ce3c5660183f9aed1a16dec59f2494f7dda7ceba30fcf3551eab476d4dc9ae300e0962d4684c970f762b62268b2e3d38866193cb20e4883aff789799103167595f324768d2a71954c0cc238abc0270522edc34540023e91a32afb81caf1f98dda60814d34e63ce25e04d6d996d397ae89bc9937f7674017875bc5ddea194c99a1c48c3012e2d2bccabe5d3c86041ab42332e88e8255b822632ed5d6fcedaf99ee9feacbaddcb293696ad4bb107b37c6ba2001a88877e849bff3c7ba129d447f9816fe0e7abffd14cc354f5d91c2f097f6f498e4962d5081969f36446dc25fca21d98bd6b75f6b39ec1c90404a2b4d9b24822ff95f4ed4a37eda36907177e5ad8fb69bddb264abefbba7c245acc49a1785f65fd2011b52eae299737081e889530bc0b48da4a865d9a3853cb7e7770926a516c77fa953c07f0457ff738a366e69945ecb22a419206bd853e544e91513465721f8aea821a2b29326c60d91539900f9669fc816b9f6cd337b8fda17bfd52bc7d77885d470c4046ec1b5b1456f05a2180437098949ee19e94abaa41dbc2117af56f4094019d6db07691f89398739e503163fcdd895a1dc5889dd71fc73746a7a4fafe46a7fd2c0521097b8cea250b9bac31237dd808b6bcf46c407e7787c6792907034202f2e49dda01df2943f26f8cbd1ff952ccefa605fd93029afa59f879fd331f0650dd59e775604710bd4fb7988cf0bcf746961e3afae36e77d0378f9dd5d35e0da0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1e37d33da79de0cce024d81e0e579c19fc89079ce28bb8e6ffa4ee6c9925b734","proof":"3025d4b017c213fa808ceb0c839479f5880eb4f422d2b5aba2eb0a0a62d4d6105a6e3c589fe112870afa7b95e5c2c14d8003b774a7363da9cef7b2b9a2f19b263441c4343c41005141f887ec56a8eb3df8ef29d4519f36b70add8265ae934525d058e29a13081d09c13baaadc742d3972b4e4f3a559396f76254912d1fe31b0d0b2112d85ce6b79a180f2ac8197e643bdca2100b7bb98b6691d5d41b0f9bc109d31158ce30571d12c9ddd1252bd4883f1e8edfc130bdf7267cad058f7efb410f2e0460e6e751c366313783902d5b911207fff95485b55e84d6ee8a9c99b25d0d78b437efbc54b1de9aa4b1c30161f383783251e62dc083e49df6acc7b8539945aa5979d72a017d006f7887a9bc6a9c22aea5192a97e0054e26d5ff04c450c6782e7c346c2b45c77ccdb50ed3e12ea900b3235125a908e349c5e261f80ddbb40784e836788c4ec4595e2eb365a1494080cea3b0ac7f48645a139db1b82b68205caa61f4437d27e386903c2c5346d56d284f2fabc1c5efa8bc40d3e778941438229c67999cf816dc6f66a18348b4cd5491598178da66d831568295e68acd5f405918e08e94339a916cff1c6d66f2d1586d995b40cb0fbee9e6321ac3c1d208d245466d1ab001d5bdc0753a022c7ac771253b75a0a9cd6162bd6a9dc3b69e9f8414d2a02e55c964c3cc97a7b3dcdd943ff102618957dbba415475ff269e2ddcb1045ef9d012a60567ece1f7e373027530ddfc4eb44257d467b178d76d7a3b1ca91bce569804b1d223898dec9ec5ed0f4682e087cf1dceec3f6faa619ef9681d24458062fa7dd0edebef691cf59a5c2d402305672da7e2834fd4f1ebbafeb7245c2b0933560ee03c258bc4034f61c9999ff8ec2716f14d48555c3776deddf1dc4e078836f2446e033c2f0fa8534aa5a5f565ec84828f5734e3ab8624e7a355e47504"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dae9d638f639f49f0885d590303b597bc626f3cd3769d77c9c78148b5f88f269","proof":"c2d189815e220bfee0f48e11877eabc7c41e3095ea55b43660f119fd8a55ed22d007cc45481414dfd94327884e8ad2127d39749731f170fc644fab06bfad116ae0d34848e7eeef4d56e9ea565febfa7b0e0c5587fd9a198b35d40a95c9e8c85fe6617e9e78b7a37cf04133e6f242fdeddbc4b1bd9e8ae853267c81622ee2df07c320b1f15f23a8721f933d71c0713f2faf035e441a00b7d9417843016f134f07a34f3d25c0fe6490a2f858fce8d996eaa74542a5930830a8d547a1ce75e7eb0f33e2f1fb44593067cdd241ad13bdf4b1edcd8fd41caae2c15bc721ed5b14fc0de2ba239e20beb56153658fe5b01c2822743cdae59de1b7b082e32d1ded1687122e0dc9c683027065b8ef86ffc438f8522286171e5e52b1aac7466568b4de370ca85ff9be3bfad672d6d47d802686016769ac7ae251999c71e94977bfe1d5f27d163240a735b144945677b3b07327bf24e956d275830f2d4499590cb37a38e33194c88130f7a0810ec79d07209bc14c061b938f06aeb84a7781be12de8911d82f022a3e41e431cc0bc3cc2df07fcfaf6e87048989ae82f619d6557553de68d15c1cb79cee245a96619c341ba3e1adeefa6e0ce0b9d11e7066e73a3f1503cb5259d0b4a6d1dae127bc36087b7fe22f2f0b5d361326462301b2a56bb06c90e286190cd96c511be5404fba7726d2bcd9cf3a413646599aebec4d5d9272e30a259239d0c574cef2ecc7c0347c3059363c945f389b20f1736ec4aa34aeb1cb5a936d333442e494efbd2e94c5ffb5cd19c107827927e01a1564c23073474314daf12808403e86cfad3f530305083fec24e3e699ed679aaadd54d5a42f69385b85baa96602f7812906984a31c2a35e37f6dcc5b6b7d2ca0abdd6194b6873ad15a852d705e2fb07000b9be4990bc7912d3263de37898c3435e06a76bf7bcda03f28e04101"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"708fce492bf54a0aedb65aeca56f7e21aa1f4b02f2c2374ce1f71e16e3150413","proof":"6c01bbe22d36c7d03eb59afd0665474acc67038d2fca6f9547e003adbb306a7a18c12ed4ef18384bdae0d64c408e586e641dfb4cc279071ca6e939abbcb69058f47d24d9b9700983848bdf88fc5d1ec285a4090b7d5bf3021bdd4ba36b5a8c2f1a8d00847b574c5ceeee382f808e542e23af7302a49abc480a603884362dae22b7bb36cd32077c6c236b783ff5889bb64e84a193a01a633d0756cce202ba770b23e3733a0085c9a5afcf0a00e9f89ac68b9846e7186afbbce4354c13159a730feb98a1c0b1fdf1bbf98b5296a81a71129697a82f17d5613b976eb8e7b42cab006248d0c261fe909b2824cfa38662127498430808a9acceceef0abfadf6d3eb303ac42bd178aec8c47eb2a147c5016a58ced2c458072b4570c1279114d084747a1a2930815ce605acccfe03d816a362d5fd9c1638087a7425dd790444b200807ee2e1b8a8e2f44a3d3728aeefb4a00f664c99dd204d6d99bb7cfe6e9d1f86da5bfed32b13766a39dd379016423b9efff8aea583a9ae0566c85261895b4b62593faccbb5f4275f34466a75b64c82eca530b5d4a3cc0c81592632146c4c6986bd192291f4b10262593000db4c7dd06e3fcbbd4b242363d9449c69e8cf21e0575707a083fe0f025f2e3b98254a5fd8f675109814c976f13ce0fd4af293512ad2a21c50018ba225755ab825c11a8fd2e31213e08b24dd4650cf1d91bef7572b5e6e470e95b805b2eaecd0836f1e5b40717784a647ae5783fe102ceaa54c85b1516c0106f456f31afbfd744319399eaff9c823352c75efa189b5572e4609663e7fc50b045a57c0ef2c5e277e0b76b935466c3e98316e373ac551ad5e6c30b0285a06319699eba563d2d08dd7e8b34e88a735434c12d9f29f9db798cff4d378a308b50e69b00196141c47b92e5a644f74153c1e6ea79f7c00fffb5498b6b1c19c8fbf0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"986bf25f900106712ef881588319be4eb828af07bd98db775374857d4c47d24e","proof":"0681641c698116d8df847ef94dcfee209cf79a70a30c0a7412e34d9b12f3082160ee9d583f37948921f9056f87b911d9212a47f348de3f41dbe3a0af7890556cce92265d4fd579aca0ab0dc356aa919914a5df39bbf60e40544294e3b3543f4a4ca96bcd0e87714d937dcb40e12e34df6fd135dd06191fd72cbc0ce517d6a93276ce54eb5637720406075a574fd8e58d742ed9c821d10cae50b36f8aa584c20976f58dd51d0a57556a6904fa6ceb650159dca1611f5726b787846a6a86dcbe0e939cd6af6326969004ddb3c117e4bc564da20ba90a41c75d455f3d8db44c7c054a68e7908569f4cd3878de9472a03e6dcf430aab6883576d49e874bea11c93456401bb488f967ca2e813c0736d426efa9c1a22eb96e988f0a48e8a81ab9c175bc628b5941034b7c6764e5850bba5f7ca8418d4ee52d77fb8a003cb5627c6fe61e4a4a0b5cf0564e0e52634ca4157f8a21713a81dea8dfc70d2d8fc17a824dc2dca9ffb87aa8f34b82c83ffead8bfb1a513bcfc20e15d9b02f5f4bca31edf124f2220692182a2bcc0586fe5d28a43b36b40c00e51b6bc9a293de75ab8be2ea51be8e83f9223bcd5b65c79f476712932f3d645a0e9204792013df371e0679a8d108abe3769e07a32c165f752ba0f6d1c70f97a7fb9375dc66da153c143b33f3226cac3fbcebe67274eb15a32560a456f82340d223ffad845d6ea1fa705355f9646f6324644789dd31ba3b12f6c5864eac9c339fb371f186238bdee97a711011e27ea64a8adcdb4f5d26510f644684ea2557548d8c57a408a87ad4ef44fba887c7116578cf41bf1e410f8a77fbb4c83efb8d783567ab05988664a568a2da94c7b2461d75a3adad1ad4a07fc774d605aa852c493bf8ffff3028444ac1433a3f56e052d9d47d359fb29889dc37b0d0e7c638caae5d968107dc776bb0c4fc1406edb00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"34efe94d6706160cd984820f01a35bb31e86d9ef4afbfaec3224d543ce08c170","proof":"eec1fc2cc7a6ce3df0a518e831cbfd3c95597b7be230a9135e9a3f7f764d161bbcfe826cb517a7156d1169fdbd251088656d65051c080b228ecaf97db345202e7cac8f17d140cde9b870633b5b27cb4471cdd745ab875d95c916228c88abf811daac6974c1ff3179226860d06ab5f8f6750cb1048a35b63dc02532beab09746ebffb8a66e2058426c29007cbc02b53cee3e7b1de5d69dd1547f72d60d9ce520a600e36d4632d57179e3fecf259300a9ae586b2ad7c6c58cf19dc82ffb7d05002e30170bf2891c368123431ed6e5fbd82e5239d25efd8596c177e3195fbc4c00544ac804da1852b5e4d0d95d9d6b0dff0f5778b0d1eefe3b3bc3bafedcb3a344e943a5bf4bfd7d7f38841cd3a9c3d99e1b6b11124483092b04bc2d8702a43f51fea83eab57637c5b110a34501a0f99518a401ee30ecdfcef52d3299e9ef796f76dea3f2878db0dbbd8acabacd97ce72d3826749459b09831ce9708b299db48435c653f7b33242d330a3ec1017984191438ceaa1e8ea93f3de2b1c48d479530d54609845661305c6cad37b248071dc59095b2b773c47fe5a5082abe093659d366c207f5bebb75b887aa1a5b3e94bf9130fde1d4acba5630b09609d3e0a353dc47cf0e79865e4331fbc3ba18bdf584144266bb0bbfb84d5f7da25ed2b36ce0fb41f8c23d81b06f54d485039bf5a49726bbee8c9059dc42a6de9d12fdfb7309e4543bc25841d407a7ebe2ec6bf55db30478011e0db179540efeed500cf057f7e1f17160621cb972644e7ac73169e404fbacfc281d0422d88ea1afaa31039e56e07016a0398ed8b71354bb98a0c18ef4f81fb296cc5fe3a86843f3f3a78e3273e4f1ad5c49deb54a37690e6c48b6300bcb31fab4730c82bbd5d1584b99b2ec8dae80e45be00b317bcc70ed38ab3a30a2cc87b4c6303d074ae647ce7e3ba3a945b7c0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1634ed911bdbecc8682a244327e605c678ddc2555fa62c7fd9b780d1aa3ad937","proof":"2455ae26a3363fc7549cbdc4e551ea32b3ba7f41bf5b8387cce195344d60706b5e17b57da051228a1d0f66a78a6f7ca4bd5c1f6d8ae2eb5f5cf9f4036315be25fef720a8b1025e659e9dae201c67e758183604ff57613b7b742ea92d1a01cb29b8ca1639a6096ffe959341d7a4b1174e8a0f66dfcecd2a7195c53afab0332455bb82695304a22705e40d00fa63aedf757cd2f73d594a97e09beb41f603a8a2087790a7a080be6920bcc75c22278982e3161ec90efbfd6f3428f7b76efb4da7071e7747b46d5a580e16a7efae4801329cdbb2743e77adff04990b83cbec46b4092056e5732d3e1a20a7ff044dc05ee37cd8a7a90123d48030c216ba7cf80f1f7dca3b905f3ad4f339f62206fb847cadb852b893d64fbc46335c944adc5fec924bac9bc5f28535ce9e5060c86b2d3dfe036a1aec712cb6d68fdeae581bc4ffb343461c38f5ca6b457c4928916e5d3f366640636eaaf4af27e46af65b7cab08fc541cb2eb40bb1fec70e0bd5e5da7724afa8b87a7156605f3952a776e177f494117f680dd0a86d3c5f22fa40354d04616672d4b98522f02a7c6342e7e08821fec43da18aa01c47c800c65204e250ca604c81722dc4766771732c37afca6dd0d07207c13c00ddac5bd73fba392436a25227b404400451e6c8b7fb21c2840f9e7f6442a3c2c8fbef4d576ac9c06e552b204d612d6c37e98e8a7d5b5f309cb6ca45925925a52a4d2f625c69c54bc289a215013b1be1df0fc23428cf8b36c9012054728660c45a4d9fe01f3bffb79d06f52e74b55ef7f14202c830ad1c50b4320d6c0219eefb8cfe02a2b481290718d72500f48c9bef96c75bdd10e88965cc8b41a0e0429915c7d079eaf02cf67f024eab8380b6458e76c0a23878af617f44bd442cb0928d5dee08a1fa0763f83fc82f901f3db5dab6fb863ae540a6971cdc99181fe08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c0dba020bee1785e7a3ad4f4fa155514e9304f8708d8a08c2d9510338e1e1c00","proof":"90da390b298cf524b2c9b74c2c6d92e18d5cf2073ac2a444522d27c300ec591c46dec758625ec0c2bf1ce9f2c646380d3f1b1969cac0af204da30e2c3fd43a7a0462c8e712a4a0e5a23b87ecdd3223bd0e91e08b042ad303b3f8e5df33510c06c45df2e0a9a4cd1cae5ae0cdc68dfecae485832d8ef1bbd06f0461fcdae638428281a5d996b6e3afcb1ac12b99ef37ddd2a092f668dfb4a81631640369746b050ae291ece676ecec3639c48152e154b9ce0b46b18470bd7070d678262923170df9f076b5ed35f8f09ef3fcd1bca73d083d476ed26b62e176d3aefb9d629a8d08569e98eecf76d958399ac668ed4bbd5d2c59ccf18f069013d07b60823aafe875ea700f24a42e231057224589ecdfe66eece7f7b21c451da27485f069ef91333b9839943e1b4bd3ff18bd0ba99631e156664370e0eb2b2d8ea1051b131a95776e6419a7758e61c322a7ea9a0e6985a6209a012d9205a0926dc1822970e40aa44aa4862f907ac7bf3566b9729bd34592411167fc4627cc840f09b541093b0df36c3221cd3a43e2bd23cbf133952e6b47b16bb75320d504950bcc83897de76c955dfefff47aad7077d81c987482e4cc303910ebb69b7098a8de6dc3c9a43c4bcd74884e2f753ca8264ee1070c102f3cb9e6c359c6999ffcb12df223fc83665e31502a2990534707bc5987c6cba4a2ae78e9f6237651df38fdb0d43c1fe9b1ff814a40e0f3bf2cf59db86f5f6f48c08bd525889c43640d3f2af777599ee888bcf855c857d3a25aaa4e97ecc5cb508591afeabafe7df6b2d82a268401e0e5dda1227dba79193933e218c820b0d8e13d590b5ae5195a552c1ef132288f8432841f141198919c682c594878d1620bfdd8e785f165a38c8724b720ed15c16300780ed901bb4831798c4c2c4332d40f2eb3b04fbbde53e26e770d960b09f437416c4b6907"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"da81d9fe71a50cc9a3b8d7c771f760b210119a8680c9bbf689a91a5e5f9ee66b","proof":"e2d7ebd40c6dac653d08db5bc447347a78fc1812160b92c31de1ed88419f723e860714727bf7abee4e69ce534c28236c9bcd240e6088333f78e5a2a498642e3bf2cc8f7feaaa37c7099c10ee96aeb9f79ea35cc34ea952cac8cef4ebda936f77e255494f1daaf379f8669e7ae885405f1f52357a2974e17dd53039c0c9bbd6529509fd76880a332c7bee0875ab48308f854d9f009437b8e3adbf602c33371b09c9039dd9b7086686f42ea624b81801fec502c1f12e4ffd3a27b3780cf6c2650a8a344aee54decff8dcc5c252fe852d448e6f7f1187eb10389eab9e21c1c3540ba07d10034f91b5e0d672800a53a1d353b496243e82731cb929a6cfe613c0495fdce3b9409683ba4d2aae092aea7edf49bfd8ed2efdac3e65fc2767d8f5d8a56508faed074ab7a2ef14f73796efe9cc3f52afa493c682d2e53366196d78cf7165c2532424247ab356bdce1e8ee8d325d73b9c6c1e0981689e457381af1d3f5c18a4d11138bdf14dee1eb712b4969d51b94cf799244e5f8ddffb305439a7b08f1518e4fd7931a316b779dd17882065416df76e5ebfe4b0e318a54e5ce62ac0c3187a607002acc33b261fa52ed3188d45105f5585201b3f0a831feff5b98772a266f8878914fdc5f08c34bbb496626c68083f50ecc5671b3053d6e8d2abb4e78e6bbef1a04b7ce52fc9009219c4d5883994fba442270cb4c565fafdf1df78f2f817863feb1f0ae0d49ecdaa632e3b3d7b56799022d7ede003910568d7d8a933df2fe6a18d393271f1f9ff06f1476c4a260da49a0075286e448faedc8f53f820fd347cbb1501fb895fd6a5288199dcf8723e263ac5f8d8987db310c47d2eeda43b4cbd954c2989a6848922ffacbcc9e17c2f8ed044b7289d9200131a428fddb0be06354420c4b9cb2348ae28117b9a44c04dedcad64d4441270331808b3bebe39c0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f82ad354b801d08a2f7bde83796b78445fba64b8ed2fab887c92368079afe24b","proof":"2ec0af5438814941a2efa6fe96a02ec8c7c753548d5ac726b3673a0bcc4a3c0d4cae13ba8fab5f3e9c619270587999e5ac13cb6d1b4ab903c6c1881e77bf0b72aa18577b8fd184a15a86dbf51b3bac6cd791e3072858624c5d40145a839b27144609d48d88d5d0a93fb660c892dfb89f349ffaf883c3e9d2a912dfdff2a18f7e1a67923a5acc3426156458b588b776ae4905e0f509228169d4261d76547fbb0e2423602a1c5aa3934b70fa39fa4c557fa3181a63b61ab95f162a46f260e0480c4ece816d3398def8aa12726c98ae7a65ae576ecca7632287a92c992c995abf019678a3cd62b82b635c4bbf91558bdc7fcc5ead290d694e30b4fa0d16a581ec572ab03e3efda5b616a3af345903a0fa86f27883057f1873daf2c3e250ab9b49372848fa411adfcd825dae7ba6b4e56791639b2108421273c469a674b1ff6a443c8ecc1294d3627310460c514bf5785a54fe37b00bca1a9a9647a821114bbaf013c4c2d2524d8b721f2550e2201e255c454165845474a4426017bf9bdc82eb724f241e679596359d1f527503bd4587779edfa155f6b78a58f797167578b849c8576a98ee97ef6d0fba91e402a04ecff70b45047a26d9c7599fcbd8cbdc1fa3cd5dc80defa7a2c692a9295e450b6656c369e90578a9757ec46b37644870648f200af0b9aa311c4fb91c5880bcf3fb3cd5df4925f603187506ef248ba4a2ae553d5e5209ebbccc3a93f338615879ba57a616050b228204ebd4a7be55c3987734b8715a1c7a09e4b48d3450395d9fac0b452783963dcef3d554b78d23d22277c99177f8c7f5e23dc58978225af40afd20e23a086adfa2e948a352f0f6add14bf5617b71543c3bdbc24a8037087f4f25223754bf68226816c2b40eb17d2ef5953f54015599135b3ee59da9e1e71acbbd69b586a3064e9589e3429d2583fdbac4d77d0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3464ed0bd96a19d492cbc940b16da0a75942f4b9a3087a4eb41051da0d1bd646","proof":"96cb0f757883c99ac0d7bb7cb9c9db7e57e1b2b1cf58f90013d84574b81daf10dc0311caba88214a4592e264e1dc7414760092f073bad4733911a07526c54b2f4465a4190334232dbfa4a7d805f25c3d908989757dc290c996554f642f291e48eac8c8c1ce41c07dc36e3a7fa9a0de763e8626c1ccffa540f70c376624b8ff591c22315922a0d2b258145c0d4df4c901c75b74335066794a762c39d61204200d2bcbee82c869f39dcef8a289530120d8fc9382d4e9fb11af57c4707168dc7c06b029ace6f61dc9dcb5631b384bebc9af03d63bf60d50d77414c9c5192bf24e0b0e04041034b3b3c3d25c1444077ef833ef87eca26a142f4662445220b20fdd4dcce7d0dc0e13da915ae2e70458bdcd876059fc35835c6317d220755fc6d0a022d648e942ea11647f3905610597f7b1edd12666572704d61c532682d46d6b4c4056ea6b08615d4b7690bab26de25466a94a7fc4fd1230164c1cad8bc404894c1bf870fe392ce583a3161d62a4aead1001864af8aedd5eeb65f18df1eb18f0ac3908716808c351f4070842686fa289e0562164b56feec6423f70d02d0abf29486dd01daae996c91dda06a81f95134eed51ff9c6885b5a348f764c82d0c8dfcb42c9ee887a02237b178e298c6628fc85731c1982c7a8f2f58740a70e4a31e3e050f566dd2527e91e63c3ccd027d321ae4c42df34f600d6b905c4df91754fbecc644021d92a0352954cae07c9c7c67b6baa29900af1c7b8651778929b41eb1fed9180e7f4d48734befe140f2d1b37dcd2e4e22cfd93b52ec67218d58ef934b436609d64fe5a55c181de7a6324e215e383b2d2303aa935672360f68e18ed687b11119d27db1cf867b3053daacd7be84350ae16da5e7143ab614dce8faf41a4e9f980879cf2bfc10c29a973f41d72b21d4a4584e1901739a05ce5d6b7f01496d689808"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7ef3e689082f48000f550587a4992669fda334325f927a94067212b1bc1bfd49","proof":"c259a37a1665555d7bb85e62f376cb44b167f46c9f5c7257b6bdbd34b00c821f822a102e0259e54205d0b8ed18efda82e96af327da8dd8a0557e713952665029cee6b3d6f95a5a81e3b67076207c5dcf31bc7a00d2c9a3ebf654e1d5f710d277a8a122fe501986e82c0b4e1e3c8ee97eb6c1a73990ac66d1b83754264de89e13d952700b45884385f0a6faa64fedf31abf28c8017f6d3516bd95b53dbeb4190ff3380379f48fd8cfc57cdf9bed6fd67092f8f279101a33834a3978aba99af607ba4eb524a1bdbcf18b7e83c2c1bfe7dc97f12a11eba42c53915f3138e984270126b057a9217bdd2606d2408c45e5c2d42f3a6ac5c01c1d681dd20caf20f55c03d036ae38f19eb6ee67ae336956c3eae59b1503f1d98a3bda361fe8009a1bf22ba6c120bc0f25d96d05f288442768967c78b58c096fe8e203d8ba212dd45d9a36b24639f28adfd01e6157f783dd53e3d3c89081e0712168098db03c01eb89362f28001c63cb4ead4145e9d0f05f014a7d76c46f3dfe788a3751fbc2ce0049ed1d381b26698365ccb88ecd771c0c7d568b89d0bee9645a0373b71b1845953df215def14d57d4afc46ca5677f6a8a32a2640e5788d53470e656b595013cd5a0f74256e331b48ae06ade670144558c57c876356c85a0055fec038ded89cc7960751e54a583d9008acd056fad31e4c6515128c11a7d66dd6c0b7eb240b2ad7221316c7cd1c695610e64dcc0d56fb36f00c514c3ecb11c827f822d8d914872a623675e86f8888d693d6b3a72704e2672ef471a965a73b88355a149839765e85011ca38e013b4014cb732025d6d01a511f66f0a0fe4cc7d9581806e0965bf0a31989351d36d951f1950462692b32ee88b83a64920b8af00353ad40bcd57a5e4847ca7050facef3e31b61857d114d35ee58058f29723c940235f03050116e7d41508f207"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dcd6a8257c55afcb3ca7074ec404c2f4dd779f491507fda9d7f5756402ca6565","proof":"d0a2daac2f5038e1685e695350ac08127e1a61576f0b44d07ee1d3f665dd3f4c8c69ecd0eb96d51ee70b6691558e3de3d8f21387935b23dce3c645751503e37636e5dc7acdd804267eb89f5ab894293bbee0885cbe2f7585cd975b20aa095e6e6e630404b972ff3850edd432020638b16bd2ce3d0b1986d578afa5a548ec6651e68ecb78b26d94f2a6e4fdd49d812bb9d662b602fb31de8b1905d744ee288b07e4baee71b881838a63b3afaed3fa9df0ffdb72e7fc79cbe8d808daa22d4a0d0e573e04dbca38871091f40ab71e20e517e671c0bb6c943283b0c26fc124de7a0186641de6f3e4964891f4625b14ea703d348e2821740e61facb2112543103e9166035967db68515a997e316d4e8421a3322b2cbb208023b26b8dddf04136bee65e8104b92723bd3d89cdb6e771bbabcd4095d7f46eada128f98f0150f089363709c738ecb8733ef0d3990ee8241053562936099dd9c1cc875d62420b64915a51946edef8bfaa8b654dffa6a56bc1669b410a36b831516179793d7ca917ec0be51b4fbe65a343dab3b0d0de16247c02a871bf65286facc6a87a9f3f09cdc53b70a32deca62062e71ded7237bfca6448369f2b00e9cb5c164206e079f59431447666ccd597878ff22ee9729f69ebdd4b747d4e4e4129911784e8828f7d5199f1c57c22a013a9375870bb750a9c7f8b45e058aa68c06cec8584293e20265f32a4803202e1e596ab9644eaeb659bcf18881e56dfe42da02b76fbb33d67c9185359518a875edd4fc9cc769681ae440011ed0780e16c6300bb68536f42803afae7f0f21b47b11d041a305cb319a7c16d466ed79551b1853fd2e41e81ec38d4b1262696b5f060b6613610c3201517a0634c7b69e2b9de29f3d0558aa0fc1f321acecbc02bb4999f2503c644534d18717b0a4c5c74c6aae8f3927b8c3e372110eb6105109"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"56dd460d4f68cb7294a1b7876dbc3ed82878a0f498ed675153061ed1829f437c","proof":"f211c5f3094dae3162326ff0288d6453ad0382850011cb887d267becdae87f3a828255f6157fd185288f05a7a010249438474c2d7d4f47fe33513207833ca004d88d934bc6a5caf37c383d43cffb7b4f22feaa05159e23f6afabd0fac2d9a0237ec7d2862b604e822632851fa4148e152cfa19ffb37f22cc47b2f817efde7e3b28d51017ab94c02016c4bac0ca47119293ebb0330bff15a7e312b737a1c0a509b64949ba2cda7de064198cf031f9118e489b24fdcdd7b04c47740f8f84ec000fa8bf3b939ff04894fdac22c96f48d2102cf41e09d2ea844d496735cdc3aefa0b8c4a794afc0b43765f3c3fabc9c26c0e45c52b1bfd27010877f9e21fd2db995846c95b97ba212bfbc83ba6924341d4d672979312647400437f5ca47862baa95f427922ac3045f8822b1ddd2954a44d529f464bc512abfbe6aaa8a3bcd04c8f22cea1b71d9dbf143f22d2ae533fe598714f601fb5eba23a8bdfc368e798a5852f66350576bb5fda4fde4f7b863266341fc6594e76a85bd64deea1145500a6b40388661302a7b77d28310c91ea2e53fa912736574bf197fa0d0142e004296e8631ecb054e44f6b52d69efbc06f569d47d6b51379cb42cc435343097c4a9fefdb30ca124e5de6ee8dfc8fb5c3d1d4c0bb7f23d800b07b8d1f1286938b1585e5745f6447435000fa448a46151095e9938d992e60943132722c69cc983e0dceca780e122308e0a9a810b26107188919f1131e1a9cbd1c2cbf8d3505e8ef6d47310c50e4d01ab51d660f5949042a9ed93d0271fee490a3005d28040cdb7dca0b1bd426fa21e4c1bc3ce7fa48f0721a60fee98dbc3e3ee6203efbc81d9a1b98d75f3e66f60a81678fb492ec4464b71246b0261a4c1018fc220f1ebe7b3b67879698fd039ade114ffe3b944d836b48eb8c1c749f35a82117c4f778af2e9281501ff86d0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"66baaee52bd255e158ef2fbac6e888e8b9df9c29e4ed66966068cc63ee984f0d","proof":"2651f994e987ce2454909fdccafcacbfc1d71614218d2404f37fa82918a365155c892e74b875028cb5906d4b53db762edf732a3c4f5cdca4ee5f2bcef3458b2c742806e43c3609c9c97a2667d1dee5c1e49900331f6cf3deed1f708bce238918beb9a848de4b11249fad311a27d586960ff5f9ed5c3b63ef920eb460d0258a3b8067dd2e1bb59c23f2da72975f75f463fd46d22b42e92d91a6570724e736440623791705737002024798fd63be69e3436dd17e80f51834dcacb4de3fc2bea3098c1279387ca93b33df28a767e3a6295d99aae0313898e666792959f81ee3ba07063c9c9cb3fc8410d66d18c6d0d4ad3e417bc5488d0ed00d7e0599f398f971606cb48008c2951f1e687944481996baef4c8171a3a794333ca1f32fc6b12bef553a5096af73cdcad90569c0336c1a3b5550352b7f672977147d86b79ae2956b11e2c1b431a25022e43d21ffbabf5206d546bc9da6b1dda3aef032855c3abdfe0d42e9f2d47497251ebb98faed8ce9e24719b67592c637850539ee5b038e67d2069027ce3eee2f20aa83e8aa251a29c685d51651f6109b2263722b43612732746f32e35abcfef14fadaa374010dff3504e4ca3cfa0210c3a78d4298f04599d184398fb83555288f38490bad5cbc88d0f18bad67be2a2fc7981502ac989c00a7a6b562ee7dbd9abf1ea068596ac4f38588292a85f7f29a21e51d5884e49d373b44718a1249f4e3e76342c348d370054437d3d3b5a89a5ba31217241f827124c4a60aa6681d0c7c6639463ab0cf65882d661043261cdf4c3911ca45699eee2316975eea76e5de77a291fdabf0d0c536be74b77b3949cf04b80f61a122be75687306e6c69af941117b0ebcf0e3247829529a10b19ec3189465aff4d196e168c9d5e0d01c2fa1b6af595933932ab2692e4f94b56f8d214bc02167e99fa9745b68e5f0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"de9162c876f990041563d4c7ea4ad0e7f27dc890822ff8da6f0444a8f25d2e2d","proof":"6e1af90bcd16eb634cca4a15e61dd14e4beb6e9e8d0008014238d698774e422c0e78e2a3e6d62e03ce555b035cac3858a0298d36c0963876ac1044819229f224b2590085d9ea21de31e6038d6014170bc8c8369e79d27776aee2d3c01c167f5340ced45704bc23dd5967532711121bcf88e2917ecbad4854c254781ac42dab51c9b6fdce926b55e1a4af394fe96f8c788d4ccdbc411916357720f941e11fb603fb701dadeb1212216084cf501a4727468e3bf7d4bbea0b675f19793a8e84a708c027fbcc8fe4dda846e040c6f05c46d610f68052241e0fd67d64e455c6483907d46818502b5afb063bb52a735c6a1e0b9eaca9783b47bbe0d7c19bf412f83b1d2e842669acb7c6937b4c2ef9ffe153317d760403cf3c5eb3e90e1d971dd5d8718e68058d57ebf948acc812348b117d24a2d7160cc25e83c765fefc458889e0138ed248c9af10c34c2de8541170c4b79f6d9c05230ed66d545f7b77017323d85b604131317d4936ae11f1257bc7353590214eb92c5049d49d3b062c521a1d293efa35d87d7ee5175b2a5a00f724c60019a6f076cd0b491738b24b13d5724e7a450a20ddf38ca279c440ecabfa945c64b81a0e6831a593dd441cff2d1948bcc774d4409b44bbd33e121b8e525be9460ab9a3d2998bbff128a93bc5784fc4770c5adcefc9c118265ec816e6079ff02ab1e9875c87c23ca6c367e6490cbfbc0a173a1aa1bca463de4df98fa3c76e1b04102012039ff4fdb0bd6ce788b20d363baa457c1c00bfaf7de06818efcfc93671adea9fa4f00b5521680b615b4c5480c1580c94943be189049ddf4231274dbf5dadb956f7536224c7f7176075c96883bb3550cc64b9cdc11c32608d2e3fa0217a84ae75ad9ebd9c7244bac935d9fdb2a22f0ce2d6ba87bd77b6ddb71bf5c3901be14d3fc857b9f76d5953349fc2005af22e09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4ab6e1b87fd631644c12ffabd19dee77a3d9eeeea30a190f693301f3208ed834","proof":"0c96979a95b07c718f2d92ea2c3115c418c92e7e7a7553280aeced6d646b0760c411f37bf7a4dbf2f1a746e748d7b61fdc6af62a1f6f794e658d7a89c95c090abcd4b77fac33340be8fca149238dfef08ecc13a6916dce0e9c510c69fea0fb0778631030cf5a076b8e9404f41ce9b54a668af722eb0040a117ae7366df0e764027f1939da29bc53430fb688af29475159b74c1bb01783f9210be3080b233ca022ebe20209e8bc356f75a86e53b89333b8a7e04ff38cf7de593d2778c1eff390157c1a4d60e9b84b69d864066ecb442b80122ed592bb36e7e08b902fc6b5b5602469bc54a65bc8c4458959bcea3cb31c4b46423d106b07ab160f3291d1bb58a2c6eabbbc1e81bcec715b6f3a7e971c8a3cf96e3e51d84cfa99e8a17f08729b04604502b0a262b16fe063468bb8ffe6b9d3d748b7c84c7a34a36df2b852410e3001abb3390584512fb72228bb8e8a43b19e99813675edaa46f592701dca23fc9367ac2b0698a07b1e1a59647b5f956195c1160534055f37099e9ac838d3488b7309e9ff6e0cfa5ce210c4f9735b3e788daeb0a37f3450b872c0d98b41b67adfa253a986ba92b9b0266e75b4198d39659d28093c9477cd29b213f0452fd09ea0d223e4dd0e252f68de0cb401832cd2849692217cf6370fb0deeeed437b6e1817e54a2c1ee833903e8e53410214f8d223e5e5d92d6f1455c6f5766ce402d612cca0f82068b1728a2ccd66a7fa05c85f0fd6b9f59284a132f6e850be086a004f47b2ae8738547a60a0aa60339dd24917432bf9005ffdc71002aa055407b50d20b631736b7b7a725c2cdadad4f76a8b588fd7faf29d0bf48baa48037ff5aca45210a231b25eaa5a5c935a5d850283bfbfa2da880ca8f5b79a6b9036bc4bb196460730fc07513f7f8e27a5f5229de79fd969ce94b5f9bf5db98103bf12c2b6d44ab090a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"56f0ab0903238af435b7ed20cc4b32345b51fcbb6e27ec91ef7e407227abcf1f","proof":"62813b6482e4ef82ed41e2839708d528a88cfb72e20aa2a2a664b6171730471eeee6832231a22a83682ad0a88850e3520272e4cfd2e3651b4f505bdff592c46e246a79348db472d23786ddf8c77b3300f0851c6d051ebed338da6b5d4942bd60b0eaef07acb6fa7d254203e0378c196c976d1822b7a31f28192bdb45ea4e666893d19bdd00e4188d3fa40f0d6627632b28e9f6dc03587450b75411fe9bbc1d09306b550f5dd955901f964f13129cfc85fb7a7ea595131d1e6cf9b6846326650fcc18fd59bcded65efc58ac989163c13d92e8d44b96e6aeb1246e5abce7816c0f3425151e38740f6fb7ddae8e7dab56d6e3594db1422be92abb840ecbfdad27245c9f873f84f6cecd63c208452919ce873747c36c8ab6074142655228100e3b1ebe3136e308b48113844494e35ab8f726d1144c6acb54391f08b080da8b2b533254ce6f9288c2a929af11b25844cb543e1a5af7325496c399570406be8346cb6620eb754e4c9511bd4d5c19600f2d08539a42eb103f3f4ee91188dbe20a174551f674dfa3618a9b2d030627215d0362c3b453612272fbe13590f8625553d1c87504e99ec684613f125ad248a76f6d480561bae873692a2536211147218de61b2abc52dc79d91a05cb90e160d226953adaa5ea585199b51ba587934af7f86b077ee6119afa698456d423190a7867ba2ddedc1e39d58716d3257ec4c608f3e01e40f0b1f03f8c04560e67172a6ee037a8e4ed13caa0ff6c15943d805d30fa90cb7dd24b41740b9b9ac256d579accddbaa5ef7ff52c70a7cd9eaa441055e80fe2e18fc0484acfbaf685d95df2ed8de094266f36d1c4aa2fe6a291c0530d4ceab332df4bce339c20eb6728c138ad305bd98e544e32d8b360563a66e8f31840b0f6e076792cdead7f855c309d7f74fe2f3467ef4d0428b45ae08839dfab6af16bd9b00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"888edcf6464916dc07a206123711b9ff20578c659f6a0215dd3739626b567727","proof":"306c6ebf3b90f17d1a4be55e11e994431f33a377451cecacd1379b53abffef6a8aece6bd6e1de2d234778e32dde0f17fa2b5640322b19413e41baf43ecbacc55907707ce2470bc83a7c5e391e4cc9a64e3c69ce90e43e6135284169848e301712c9b348a58e622e780d3b2c161c45b8c75d01d9d8f7b15c6bc366c7834a748715e8328d33cb793eff4993a5096210612c0a8b37121224488d052b37e4a1bb1016f0e33ce3debed802ac6d62f9f5ac6ddfde14b53df9c650b3c83fde40d4b8b0b256ae94b74b2d4a3f25e93e9329c5e86836d5fa7933cbca04fae7b3002035c04b07961ecb1e8714cc7c7832333a7d6026cc94436384e16a390834497684cc56e1e02e0f80470854496fc4c6ac93748bd1b266936b16eee2e514f500cddd46700f2fd5fb517152d1b1446c488ea4464861471e84978d4a572c54d07843932ee33e6e753319797e4e08c820de31d4ec5772cdb9e8652a8522b44b40dd331fd940aea0bcac28263def10c9242870eeaba06f46796ccd28c553bd9ce729f205ad53dde0b4a173b80676d51343cd8332343350f33670cc4ab50db4455e8649058293dceda56b6497578ca9620896157a2635baadb2d00135bd5fe52bb19a1796abc422e4c285a87bf742284f706cc142086e65271005d76f13ee4ad127a62fb12e67820ba05d2ceab0b138bc6475b6a7caf816622a63b083281816e9825780e015b1ed28fac345d4fc976fa9af9e2e113a7543cea0e03da8c20b23755b8de45b5957e140f950d610d085f0b6f4dfe9107ad79e66d3b0dbb7a63de11ae552c12b6160d7af0546d73bfbb3b92a956c3a48a8d99703916f921cea603d13e856946889a5b291691af56fd2789cefc64f5755a8b6c8080ea6054a75d8e16959779494e8d0028f683198cce03d7eea26503345c749146d664ac458861970272fd470410ce0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"546f8bea95e1a90476e09c106f635ebfaa0de0a034ba51640abe7c25e182cc45","proof":"5e31a10cbcdc1fc3684f3a7a01b7410a28b43bed704fa3446c63cf5a1e15d9283839b58aa6df30258c60417aa91a5402d4fe542ad8088cd7d8d068a84ba3170932371bd5e3d884ad1b7bd230bf06aa52ea8b94edbe1b21e7fbb75fb8c6537d3392133bf1106555f7493721ed856c0ba7d23f7dede920cbea5f79673d41b5d161d3a281e79fab99e66396c13ee69366095747cae3701ca5181b2fbf247a241e0c06852f599315ab8504e276737280870c7f9e2109d8350b1d234254c0f7ebd2028c292881437c626ccaa547f46d208eb27da4d0f468f5b0333b5fba5fbe4cef0c5a70805f23a6faf82a1c69bd8fb088a05fe9f0aec74b8f3d13b36d9e03a1c00302cec1c4d9051fa92c5a21e3bda92e31b3d76b416df2c89ced775131b04fb84dc8d436ee5bf779d9bc5b01272d4fa6cd7fc300cebf869dd10a6cfd1d2e4d70627c8de89acf7fe192de3cf34fbb75227d88e4a2959c44c41652e74f12a4e80265680cddef09f414482bab7b75fb3c3d82304c3f4148b94af0a729c51dda5bf82a560ba72d9150efeaea7bbde5ca3831c0121fe2aba145c967269f7fd8ab704a495671268387e9873990a7a3de1e2cd89123319a478fc211d758539fa93f6c910c687fa680d6a38475052bedcdeb4a477d95d0b7e5b277d6b268e1bd310bd6f10d58278aa5cdb68a561dfdbe34cc681e21b0a11693c82ac8d0ab167c4a87ce5b3df0f98e5bb71ac11a331181f8a7c15e04358514d85e80537cac970e199e28f65aae7999adf08cba82e2bdff5c1db523647f0cb43b54e24a390ea785592a12d40e58f377d1bf15c52d3c274af88392208032ffdd8f024344e5b7800ea870a8597dd67035602fe19ba9d7183c0736fe51eb7637f51c7f7bc0e7d00b9a1816efa9093483ab3390c5000c11aa234b518099c4079dddb56b11a5207ef2f6d6911ba503"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6e0e56e9231be0c004f99c79c97ba4f193b7c713fa7569f7bc795a78c7e5da6b","proof":"9422c1da58b02cd75a322482003e34835bf86e0469430426c0b957798404dc598c3a2bb49a054662c7cdc8bb76dd6eff09195e9533016dec93f1948ef5760764ec388200a0f49ab6725dd57831b12a1152de7296963489cf37ff879d113aac7f567f87cb9a578477892b2aeceb101b0496c5289eac221e2a56bc3f4f24cd8406eff915d83ad3b20a61877913e76218a66dca49d0eec5cc301e6ad3a1fbde990b5dde68f6210807fc9e75bce3f206241dad23bed64bc58d23bea6276f0f25e7073c0e8792ae79a68ac99bb5571d2f1d67f5f87a68cec79f611bfb6fdbd6cf850fbac54e4622be2e0b8f9cce3165cb3feee109f372b3e39d927cf611def0acd764cc569a8b7b53791fac195419503a704bbe40334f9affbe37a3343129d6256b306c0c5d5c59f751f5c41c0f61c79107a11a2c0a0d81c2094af1b72d839e6206626aba1dd08db540722222f3f2e5a0732721281290c9730778d8725a938675a771a2f7cbc8536704b91cc704421a20a4603ed0891612aaa49660d8367601d8096bec6259aac4d07650be6876aedcdcec77466e5bd6c9346e95aa886620f890e51854f339024a7bb6e28fe777d48b2286430f296888f7a3bbcc83683f854e690107c010760294f2112eeb6e23b472f596422350e02f262435c6852a038ebe4e223292cbae5718d4e275356074630186519cc7e02af8ff85453e32fdc04d47886664ce2cd4ed29baebeb695a45daa551c791272db127d2be723b46c3dfc67b88e306522d620dcd9d8b8420de69a4a216dff83b15363044dda923691b5255eab83e398ca79aba4c74efdc7f381ba480760325b33572e3b5e9f4acf28a09c9f7ae72591bee613b6f42a5d5d4633bf043ad68ff0bae3a1d4346c9fc7939d054b975aa0951aff3bafff18f667e5f5af99b232af4fc9b667738fedd63e69e715a42445b0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"74c89fe6d36eeeb20bfe74b40846f650182b53369e2dcf68f0d1f04a771b8724","proof":"f4d3c7a0bc6f3a257084cdae216cfd485048f0691e81079d42b6b7ba6f72ad3c522833b056481876e5f0d4a9dc288efd11eaa56cffa25905c97c14d36eeb46031a1b8d1c88ca6727187d58f54a9e72119c21532eba42112d4001e9021f4f9361fed38f623c3f44da73e1a067226bf8df293e082028a0349833333767dcdce611981012e593a8775bb18b20770464bf42d813280586b2e5f0c6405f947454d40e163638bcbbd40c739cb68287e27ec54066d75f0ff9bf7473302cbfc4fb820608faee53719429bb2df62be138710cc32403fe852a748f773429b615af04005307e87d8a25f41118fbdc6d8bc6fe191d57779163cdb9e319050f3db1d208664e5c46bb285cb6f2f81ad4af64e609f29e6f8607bdbe1085d550764db7cd1fd87b402ec3b990b4b2b4633610274b14c71a4f32b2192730ca5743546d15314403fd521053db45a14a4bf949f9caa81495d89a7f9b673590dd954ce0298e55bac2eb2dee071960cf08f316d633e120a2a9aa4943c9f4c28411d079f4277e75d0f4bd2a9e8e161d8b2bc104ac864ff205481e9fe7c6f3df53e2a2ec482266cdcb80034798abbca6ca8e3172648f7c79b30b0dd15f5ff35af2f078d9de185fc7d2f1c5368aa9f631f52d049818c836f1af86b263e9a680d090c83fc35ff8780b2d2e5e33dca2552d7318d01a242a81c5a84e9274892ddc8560966a2d0cd30240213a3601064c6426a16625328f253117e3a164205877b26398ab77d470823c772501fe123a942e43fd2ef8618eb3b158f071eb3aadf5a92de1cc71ad96a5e8139c4ddd5a0496575cff8e108d40ca466d9cd3c58fdebd8f5833e6530499bb8b2a73cb8037b42c8d4695f0c9baadc5ed3652bb92e2b53b2550f5ef8b9c03569bc65b6158050cfcf94a5825573efc842adeb299a7edd45a025c167662bb32dfe337289f7906"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7a9e8986db6c5c5ac8e196689a8dcd9ffe4869b6c98c7ae5a240cf29aad56321","proof":"e041c83318130902f3ddf3568b6bdd93a85787040ff773cba1b1093b0ff9270272db1f0f41bdc45ef4c96629d2a1208523c6bed447960bacc081a3a8caf4616c6a8fd2ecd7088232951f708a4e4109de61a122f9d111b8ab0a850a433bb0b348302dc28647b0f0a5d23b92e4a0a31f7074848c4f0713f172b814ef602919d77cd6fa15a519bc34db6cde863ae0314464696a5a48eb15a0a1fca359935c05870e7a78424b138b57aafe9d26b27729999313a1c53b686cfd49ddb360654e0adc024db167a4f3ecfbf117d929b080ab00623ce9708e3c282b5f36d6b522dbd4f8067276e2f863361ca888e34f2ca143f48d16e38886d7f7701664f68817d4ef034c02b58815df6f950ea0241766aeee505569974ccf43aeda482d0f1e8526e6981864a3ddd78b7bae4990a41f0ccb08ad0ece623a6ccf957433b9f1980c891427000c6e74316cac4529b77caf51ceb43987a9cc9715e168ce926f2b48daa94e8d19cca59a1a9bf4ca62932fc3c3ea1cc257b41e4720925b1961e62ff09746c7fd0ebcfd55c6f55d1659280386aa9ece69c7e39dbf0e5dea2634048620b394fb6260964312081b93db6dadce018fa0e13a437104dcd4246de816819ae64b43a0c215f22a2ca4ed04c7b4272cabe1dd760510c294bf915612d85720650e0179f1b46276f1b97459a4784f6bc21a4990d52a65ccb96ffb6de308653b68154074eeb471c81649b123112c34a132183a85600b9ed6fc5da09288aff29cb3ba4d012946404a020e318e95e348aecdcbc23c75e186b4198615dee46a0e722effce1632d94468030f4e9e8f9a87613093cf6e1d281ceb40a9cdea5dccda74062efdc43c970a75310a54c9d207a52a87c125212a6fb87c314128c87b33fd27ba849d20044c0ff4f7be2f8c16c8b2e712aaa3cf0c72775596a7878616eed2533f857d2167c007"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b604cfbdfe6c447cc92006c39a567680a05c9ba8fd28f69f37acca8c6e52a821","proof":"664436f2e1c6e9928cedebdf1b7cf9e2c7d690d214c56efb98661154df2ab305a4be9ce5ee8e7388bdcfa25ad33f0e0352b40d4a46839260a808731a7190783c9e48c6c3c9940e4d9cffbac70be629f725b0c4ac2e4b4b4f2d394805959a271cf446fd1b3af9b26b569084b68faed322dba7d253b8deadccfac97c5f9ab28f504092dc29394abebaeb26996708a87a711c12b9dad07a3033c1a9349834cfe907f1c6a115cd7cca7b341a0e9e752e06e14198a79357b5c7950c2a029bcc84bf0bfcee23224fda0a906931d69c0b1d4b8a1fbcce64370e40c20e8f4a3997c4f80cec1de6f1d1c8e3f0b30cb85f83066a4d1205e8380c2aad02e4532660acc1485e7a329a900f402be07411ebbcf894a4581eda8137dbf48c9cafddb8fb0447405f1612eedb4430b7f5bb6425e2b273a460479a1730c0334eb1057c92143803cb27ac46be2134670b5b6704c47d73fe442911db8e6dc7722c5aa2fff1121599ce55e42c787e66b38c51808c5f0f02b34113a18f5f1847cebd7380286611cd51f74378ddd9ba94a2858eed342b8b5456dfdff6de8929fa474cb3d15c95ef5a8b2019e0dd7e9850b2cfdcbe8fdd308e7080eb5736ad1e7f5a42ed282e1e873058ec524088f68402ca5ee93b8abcd76cfe2aa2f9df1cbc5154b667485222f547f7db1842d86734e7bf0a521cb39924635c8747307abafdb389df48eace6bb4204b2c3f6ec821f91134ffa2349cf65f34ee063e5cb2cb37db497f7a22011df382c7866854b06aa9dca7d453ded20baa800e24a9218cdf3ca105eb7ea018d59bc7d6122d38b787159bab14d375bdcb47654a9d85b133c499d58a49c16b43e800aa8db1298a2ce4509f0fe4e34feaf5d35293ae49fcba003070f8aff4a8f3717da0cc720bc29f8e26f56dce9f32b1abc4cbad09600d91863c308427c4f68d935f0db1cd0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"266e4cf76d519ab6235681a2b8958b452a060006b3101318ce2347a13ffbfd42","proof":"5cfad470c8f401c983d0fb337404d082ca4f8f5b8be1063a7b21792a13df6453d41d18a1be3008878d02da79079bd23802bb3ca5fe3bae7ae327e09b64469241fac2f0aafc36a049f8e05224f7767d450bce739011fe3dbec31c2d21d7b2394f76dfb389c7caacaccd0fb1febe54f36c8b32860690978cb1bc2aa73f0e779a0cf225cb5ee92d6ef868b9cbb42df9191980f65b327302cb1e2aa3c88965fae4013171b7c65e90e037377e051fca9bf5d8cf110c41f55d1a4468b48bc6878edc01673b9c6c0eaa0f77f2eeccdf1ae6f941836e29a09cfc90d2720837e7f158b102c47443fd185c94315db8d5ce69589ac618ea001531e87135757199e8c1aba05a3e46a308de82d2b3b18511d6a32fbe0f9d3e7af69bfdf74dbe828cc8cffa737364b69740c4341686772312254eb9f305feff6cd92b51afa79c2cb3f9601ab20f6086c0620e442b125e3dd91bbb0e1aec2061c2ba8b0db457f7c2fcbd16747233f41958d584fe06de2c34dcdf944240b5744d0935cbc0c2c6fa644b443afbb811e05bca9f35db4dfb6eb69a494b67eb7d37a16a011ddd76265b97fbae2695e0155a4a5070d5ccf3bd5da803091ad878ee1903c878e989ee8a7605122453e7dc0bdc7a2cf89bfcd89e2117a07e753963cc27b1d8f693fc796148ee65d72ccbc611f6f651d87df8eae5ab47b32ad3f5abd97f9e152f400df14319d5db98d89da425b2a2fb12099306dd74044f168cdf9b615764140449a403b50b12a3587fe32b0644009f03b4f265c222fdadeb26c93ffa20698c8fcf6a27c5891d9bb781fa3e492cb698544aba15e8f5908fd80386ad72b58751185db2805709f00e7083e5cc58c07d1e7ccdb7ceabc886ac307104d406427d743c7d0cd60ad15adfdd30ca040564348d3bac640bdfb3c8637cf1a53154740b89df267f779696bd6e660dd38607"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8c9246618908b95614f45882982b54fd45351a14982e9d88ea004c2dfabee127","proof":"a633239a8b796bd3b4fe00dc2644ee9aac802510561ec6713974356d4dc7a36e40b560d0c3f5b74baffcb227d4d8cb58fd3b38ef3ac593cf2110c50efa526955ea34392b331a2466a30ea89a8c43dd91bf726b258e2c9ae1398bccb84d24a83528bd4decfab02595efed2ef883b0581443f9fcc6b9a9e19b21085a5edc0c0768a6aadccbb423ef5413a52803ece8613ef934f944a0991fa5f445cb1579e7b809bf140d4b31d04b4be3f20d5a80e2b294232ceeded100bc946697152c4fe9ef058becc1d63676dd8ac738d72cd37b32a63924ac4e9d669ded5fe72f3f4fb06f02ac8951e56aedccdf34c94e55406d43fcf0268f8422853f5533d6d0d4c6e93c6cbaefc54b6945d1079fc2c493bb2464f12e1d244d6feb040d097ce182bb56fd3f64d77a08643861599151c707fe5b8a671a13f342e57f64cc97f888c7679926609e3b674f0dc31589abe7e0d756ed3af05a7794154100dd62ba65b93c44c50166f0fefd21341737b204d145f7b15d058eeffa7bf323c26e8975e0eefcc4eceb0e0ca8c92c0e5e302d46c9ce957b2c04cb25aa3243a5e730ec623de6627c7ed310e8390a687e4de52e0673d812d32f8893da10ccf93be0a5ae342a665fd3310141b6f38da1dae3e3257b8e090f1c71b67038f39f0e3ae1a9cd54570650aef7bb5ebc2b1f0671f75a81ff469f76664396f54a993c3547f82acad09e9af8dfd5f117988b132aa1941915192bc2a781a253bff55a32bfc18e7beaccfe07ada1121d1feca79ecc1bfb649981fa3b9da4161915fa9de35abdfd9750ba87c51ad2e05e48e6cac35de9742e779d021cb62e1a37f381a032e1601d44466f747fd6d3784126538112663051e370b15c3c361d0f0ee5b6975b758f3bbee9671038604cf4ca080716a9aae45a49f830ce6b78e33aff549a682b890570ece232ae27f27a66fe03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0204556a01538fef61a5575e4f970556db49fd7797e34fc12b4c49161f7b2a6f","proof":"0eba26f325e81d77d548ecdf5b79cd790958fda66d4bd9c7a0cb6716b27ce772b056002da54a4d154ef96a68da0e354212b75ca45c4a18ccc82cc6773519310256b9432749f9392eeb3be852dce714cefd672bd4fd9535ff4853380d19bb2063b45407f2f08efedefa1e0dd77542b6b28ed0cb32e357fbe9fed8ccecbdad7607e2bf0053ba61c5d95c1fa2314a97d1d2441b1d23b504bd5a8e1001e9017712095d00dbf433d79e281e30d465a9532a6e6414958496ab35c8c7905d89bb9f6701f6a3a5d82629a9c97cbb78a69e37d91e0b35939b406f5b333701d380ce1d8607388255ec6f58d3ecfa0efabb514a51b1c8ebf65514fc5c03ba26c3911aaeb3093855125654dc92130ea2e58c82cea6adbb3772da63906174f352b3771cba8a60e0ef322f4f40b4c74fbbe3db8d73707bb4a552045f12bea57e4ad6b736238d4b8083c705529c03a0bc14f55724dcadcf6a71f280d20d933f3ab271627790a208743488f6a0a3c4a9e1e6e4b1b9cc52b210c9d4f28b720d2b0e7708c22b134a350ce5659fa32bc9ae3fdc4da2e0d41abcbf75a3aa5cf8c9423f1c3c381e49f56a38cd99b9977d524169c36d8cb7c9eabae78ef1e0d053bbf125a665504904ec59ba8e5f5c00622074ab3985a4ab579431dd87bb74cdc08dcad0424ef514fd6a5bd0114fd2573f57a34824bb6e7e21f9c8b451a47c5ef1d02548b4d5368aaa8d377ab00e65f7ad777fd226e53f77d4f30627de5f85e6d6055dc6bb3d61b88f2a6894c0f08242b37a80e502c62032d2ecd84af2ea53c448d0e6b0176ed727ae5859e6c00743008d7883b5e1f2915d56b186db097f36d9ce2f12a7b579ac90fa9e55a99cba2ebff9155070115b0ec1b0e4df9f6fe8ca666ef44b3392fb970f81f7029b172f4c42e24f44839152bd164975584401541d299addf1ec554174075c1903"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"00943418d0986a3b9851d4c41dc9d37ba1d46f8c166fc8cdaa0e41ca82b5e709","proof":"c2a31eec31df20ec1784afb13db882d09315a62d6a3e444b31db2578c399637afa2c2b402f9d0a0c4f49eb5923ee292b9d6656c5b94784b76617fb1424f0126e8c933cf4bc31667f5b5147ff183b43688cca51b66f3fd65f4d1d96e64bd4ff53ecc2e4c9c102fd6c3155dcc2349f122eee330ece43610066bc1f22d2563d7051286a38c088737841a0616f72540547498d526a21d38c5207abebc82fcb91de0e7c2ed7c49c62fe5a473841672384a4bf204f9eae182054335ef69d09f82a41021d58b885af65414840201fd3e524e5dce1cc8df63be6c16c9b8d3a0136594f07102cf7ce36802e119d5f4316af8cb44eb7604fb595e53c051b60d9179e482f223ec8ff01fd0bddd4110862385413798cfeb30f7de95ba7c3b1f357d0b3610563d82196910a877cd6f6362cafad0dbd55d7d9285c50f16545f02d5a4d76ce630d8e1e1c39a596b5442dee5ef6a5adb591d6273e5e62c5473ce6cfbb1a9df7260bca61097fb407b1c5d48e9b787387696823bdf5252749f69c247411ebadc7e746483c7bde6f3e5a8dd60c1475b2b135608a30b53b63b961ced4d914cdd9a5f332d8f5c16bff7f57b6f8794a178a235a208b2f45562d6c2a943ffced3f00cb8c2eded0d4407e8c2840e6b85d009a137aca7c5c6b509688ee6eb32804629848741b8ce126b2222c01792a5336564370b1dd28fdadcb5bcca025f855de611508024848d966068293259d1d2785d887b34b7807a25a410b477565a690efcfb2d4ce5abcf3c7bedb215a7b47f9dde017662aeb961531173e6a9109b72334a6ab4ee55d0c973bebaf4bc3d76776905fe6cd328766873d09d290d90d8096195acf7f464f537baf08948337cbbacf97637cbbba69f0c7a80730982f597fc264775f1013057cd57ecafc4412b318ffa28fa550626c65fc4eedce6b726a9c3d3b8de301b403"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"94fae3fe0980703ebc1c8a8b131e403ebe0a40c4bd8b6a2076f2638dc459b66d","proof":"fe1f666d826a4b2ca932622efdc02f9c7f7e2f6172a9bfa4ebcc3d63866e25208ef8d361628daa0e50aa515d1e27edda270df65e46fc1275582970ee3a92fb7f6adc8085846e9171fca0b74c7b6299adc7f50eb8f615da9802e55e7a034e35539a14c5906f2135750c911f0984c946c9715ac6e1322eab487adb754454d17b011ef4808546491dfdb6400a897e6bbf0b4234d1dbe76a89eb520c6a17006ff10701087a98bde949fbefdd9f9312e11b4201f82ee5117cf50db045d2ce80459a0099366538efa885e64bddec6379f1b9bf524bf370a355cd1ad1fc47f1bf0ae80ceaa6f185f01194e025496e87251fb28e34e8385bfc07c6d0a95ff4955bc8a305505795716b570b56e4b0dde8eecd77b049817cef142ffcb1acf036413f688f5c926ab7d0a249917559bb308b9977234cb904791284c3783a952bda14a1e25a36d4769986aa09cb75702b868ac2c081d961d4e5227a7d4103b00164f55749c84fd480d159e75c104cf5add85332553467632c8f81d63199ab4acd1503d3029a3fe6472c8e7644cd9a195df28f2f3d85c1f9f291dab697117dcebf88cfbdf8ea4c249aaaa6f1cb4cf3afcceda6ce199fa972c7bfb2ef335938ff64dd91c7bb021b40fd19539fc2bab197d5afd53b15dae04ccabe29d8752363de5bb0203e87d675f8fe30cc961c2a367860b7d9bc64d6fbe25e309de1d9e820438831d54f85e2590e6597a266073937f3f821f102d7f1fd26547717d80db3bc7417d7e4b1b1ce65e8b15ae04732b648a8d4cecc3ce0107f7569b16dc6a8e9448d6d575a22de4c35129869e2c4fe18c22a40b8968c5960b9df492e50470101dd4ae6a1e5989a605f6e6b42e9268f2bc306671a079eb8a66c74ba30f7887554068a1d2d5e43a7010cc12d7ccdb975de2ae64ea08e47c20d784999be843eb50f2057251fffccb34302"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c67f04ce76b90c17ec0780cd8bb09fbbb7ad1e060f0a021ddd7bd928b40e9a34","proof":"e67b8e739a01883184219154200ac1e37b52a24f37655dfcfda99afb391b07148a692bd1f41447cdf6dfd9aacbd4ec8c6e7254816b53eaf59122dbc30927044106b498c12b4ddbe5042b8463c30391e06638b2c11db051c516fde862f41e2f305613a9e6325a13738320a45552b075fa59628c4882d64c0701a91554d8b7172959687167815edfb455200e5a7d1828d219e4edfbeec51f45e459e6b88d7a5708d00d6f632d78c50710f3a6fa57dcb899904e1b41bb5c6d5ed5bee0d6ef5d4c0232222a79602e99527cabf807ecf4f8a4f3ff0b67075e6b8133c850bccfa16f0750c1d4031dd52849c991f433fa60b0579af199f48ffd5a21cd6e3f5c82aed428ea534409fc04e6d1d98bd426a97b0385c5272aa8a299af4827ba151dc68a6e5a7847e25343727d24c798de74647eafabf8ae65fafd5db202262b786487b6eb7d10de136d6f7e3cde2e14d4e9458b95a4d7cb5f30b9392342cff16607c9006678da4098f54e133fd068bbb581ee7eebdb483efed640bd27e8d360f6520918c747ae1bafa898e4bdedb7c836df10a48f7edba57746c61cfee02896850e8dcf920d24ab0ee75affe5fbb0d9978cf48e79b25fff9b696e4af07e51fad68f74773e0a8ad4cfcb7a05af37aaa6de733f419033263ddf5c83d7c630a0613eab9e1442603cb1828ab9c3bfd0945c3eeae3512bacb1e8b980ac5d31189f7b2857a28f43017c686af4fe27e53c58386dce3449256ca62e2da887cd6b1135d5b68ba4d2584ab4054891b13faee3c7bb16a51a9a095001897a45ec594bb85f7e2c22a8dd0f4b5e783b99adbd21769cb8a1cc4c47bf1f9bc2a435c2658a3436c2f443d03eaa594d693d5b28d93acfb2c3df0693612e8139788c1014a57aa431d218c21aa38f0db08be77d74e517c1d84af99720698f628fe707f25b6d117c059e459f6deb9b0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d0f5e7b6a360dd9545b210ccea64c0c2f555890354caf59e87e6a1b15dd6ef21","proof":"c0b16e38f7964a72ce233cd22896328afb2b820a90a349b573f18bae36add442ea68712b2cd436259b2a092294a47dd422fedee95796fa86b7e37fa648178f2c3eb339a129a7a514fad4060a1c85a926eb64d537581e15cf7a7a3020349fe56bae9f2d3543035b64dad9126b6d67de9d5b481a2769cbef007b8ad269e58143399623c4a63642707a40ac7b9455269df4ef31b6a234f4bafb826c55d4d00b24069a6ba4cd91bf7ef2b34b16ba8a62bf83ba6bc2e08d47f8baca6c7e22e1765e0a4791fe8393bdd7610f1430ac83b84353ea205cf3896893fa68a5a824fae98d0a4c999002793d75cdad7a5b1221bf0e23f19aa88acc77d2362a70e7e15f32eb5a141a229a17c26057095e0f6f683fa331a40f6d8b73a6c2a8369601240cd5a65aec0aa69266a6241519f132832515e7cb596ad08510fea95438e5637e03051d519098ebcf1dec7e4692d69b380c33b3cf42f5d5f64506306afe5db26270adef7adee1b1051b3f1addc6726811a79921ac0e566861ee0238f38b7b477c7650f05ec2a31856d6950d12f3ceede99d39a1380e33dd91b81a5f7481a9bd5d22ecee4d6c22f8a8224e07b88cf056b3f72c739fcd96bff049bb511dbea797da11ef525a5eee6e3b1a2372f9763c2045514212bf77e0daaf113c137777620f9fbb90b66a0846aab5b4fc77b904dd88c08b43e859d882851477dae2e3c46e52d8dd9ef412faa5692c2ecaee69565705e03643dc09f6524906d5585e111c8bf8e87781aa739065bbeec2589ff9596f1ca6d9d9609d1368c074a6c8c35c2fc1d08fa805611140775d8e77c7ab481ccade1e0f94dce8732001d59837e08e9dfd073f9a8f9233f361c6f1c6afcbab515eb081658fe306ec7425898b69c385f4bbaf0e3d1ebf099e51a21a4662b763646779837b5ce45e536c19284b559c9a539afb7b4aeac909"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5ee22181c09c4ce45eb720f3b733844a17fbafe17087ccf30f5962c0bc02fb78","proof":"98a367659b9b8d79dad1984b8caee1b441bd52bed22c225c970d231acdcca069cc87278464f05780e47c3c96bb6bb731df76ff25d24f354c19eb39825db92f3e083248f151194e1dd01f48bb4ddc57da7a0a252eb531355df9b518978df6c961baef792a80e26101c9990f5744928b50998f584191c6403367ce1d210945a04eb664bd7bdb730db02ec1e27221dbd4e8c2edd813086a99e6cb507c5757bf74089d3fa1f13da14a07f91304a1dc185b6bbb0cc92a5080af1db41371019099f60144072c295381f804e21ca093075b31272e8a9eb8df0f2ed6f53396bbf8f6a008bae28c9aa9bad5d5743fa4b4594bdc2515e3e99f2f040ab950acbe6e62435e2976c78c7deabc342cefae3f4e28bd47de977ed4bfc52da3bc5476dd314797523a36ab8328ac3fae2f27a1673dea113c573bc566c907dc2aa56526f505d374f33952e33693a4886acc898e6a3fb4a4428f07abf1743b059c3b478b7e193af19d235a18bad3537b53b9ffdfbfa26b76a9be105d2f24e02b350f9506e5dfbdda7b719a8e455aecc59caad7b5c16352bc363b68b5648fb20675b5df529586ea4cf958f01754b73c595f1a83e728fa5a5fa95bffdc50cc0a6aa96c7caebcbb810d9c21c05340556f62fa43ab52c84cb79fdf4f52848b871c93dbdcd337bd373f979958069882e42e814a02ef25a29932059921ce6ff564ad587170a92a2689a1dfb32e62c7639eec92e6ff6496988e4f419aa7e656f237170c4d9900d967499c245421fa4ecb5aa1924c4e71b3f1c47ce0249381afc5c53ba2301f72fb02b45bc5a03d88e72fa88cc659344b4072ca861665a73a0ed9fc63dcf2840d1365c3948f102ecc94b642de958b36969e17602628bb7f91d7e644108e7ceab27a9740b5d26b09bd8f305eb48ed50997df07e0e0bc7717239d6fe0518efedde4cc113241a6b80a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3eda98b5ccaec2149bea26e6192e6b2301b90fd231580c8bf7f14f3a538a0136","proof":"0073f464c2dd534804238085e4d2bef195a654e66436ef586e4edeb0f03ec30930f35b6503bcbdd30bdfdc9d025a7db2aa7998696a8045e96aa925588de9004f8c7c3df3a82471636b94abb2dacbc8f537fcff65849977651bf5e5d59ab5ea224e634d2b2609b7cfcd7bc369171af1f4f51e681bce58a89cf988af7ea4243329333f7dd900b9017b52ae91cdd7a0fa68eaaf6b2c4e5e65050cdf112634229e093181d1351d10fec17b9f708038ad7bfa86235bd830d82a0b4bf3d53980aea80097b3147686427c222d26f27af641f139c5fec9c6f776478b3e8d1e5fb1912e0f1e2b33070393cd64c538fa8f3d02a281cf75e85f1e6e4c3e4e26850b9e84c240d67ac410b1212045e48fcbedec56d741c4a158e1551bd56713df791ddcc87a4592f015888f8a72853de9a6dc03910d781fbb908a42a3509ce4d6662e09d5ae7302b75a4d07dc7597a14146897e9b300a51281b750429d4fba4fb4bf58bce957c2eadde2676b89724d526efbc00dd9ae6a164c9fea83788f5a8f262da9f17571fd6145e72366cb816e82c955cabacc4b6aba84abf85a257c5c860a7a848c0704c187776aeb0084a5d6d1216fd753805a7ae6ae05aaa9471a9ca0104f17dabcc779ed76d0eb5055a46e8b64cb73da74350796ee787bb74ff6a83a091193225875b8660fb6e8f41d07bfd223c08728990eae13d2f14031d2f17235adf708e4af80c00af7a40f73b1c518f25355f9adadbed75e8495d3ab56bc04aed0c55f867bf167a644bd056fe842846af1cbc2abe784fa889f730b38502561bc3dfc37f975d159a108714bf59d4f8e463ed6af35b18f1625e2328d7b44ba4aedb64bd5efd06539843669301a3b98708869b9e17a38add12f9d00b5c9c4a0f94f0c2d6684cd305aed6cada99bbc15fa8ddd8b9461e7b01fe903aa007cc12179dfea3919753f707"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c23c08c3343c730d87cb9a55b8d8a8a84665c5eab4f5fa2653ac06d7debb6954","proof":"2a1ebc07c3c5a0404549c5efe6822d9add81de26df882b4ac2a260d35fa9b04e2ca4eeabf37d40dfa9eeba9019301c100ff0537b2e16c44ebfef80bd1e41e03a8c7d8687c7e485d58ef32cd7190cb8753a52221ba6b1a69081551be993dcd1304cb75c0ef99886c5241a4d99ca026593ca0758f501aff6407f763cc97604ea769f095cd35081bc1716e481e4c9d3d04810856be477cca2e5fdd68e1ed06d38049453f59b09c9102dc8f3f984bfcdeda445a120c749f1eb176d3b6fbd16954402b9edd316a9083a8f4d22937354fe26d06b9e4b14429fa53bdaeea8663bfd080b7878e1a76bc3f2a0c25f250806e5247ca16ed71d84e5fdb426807a45cb910925060cdb86c469705a913ac90bb2b5c37f30bcff4734fa089f6dc52f16482edb37a4dae668be31ee093c1ab651fe33c8eac96a97bd019dbbbb5d366302e87d66758a284db593c0adf844c4f62d35fe19a939d822f166e6f4aa12047b2f90badf13569874dd451a64ced872998395fde19072abd76e1c26ee8ce007b6282218805f84b118799db3696ab0905d3ae038452da2788886a383e000334e7504df59fa100485273e34c48b738c7f30588769b06de16e001f865a1684e8f9b84b7ab54a0034655a22d460d20b9c4ab6b5bf5d67aa50f4fc6ff1dd63d417143cafd893164782b28c7a735ff0f1a9cb7055a5f139d5ea1ae4e9a2b329b5c7e83d7c797179357e11b5d1de0e4195ba5b8e8466a27e5a53d5cc56f831aade2aa0d4589340996c9e1920b0362627bacc9694a3cc93b20251d62a6fcfe64bf2c5a7cc8b8c26482838afa1cc6e9cbfe37e1eb9670555e3a98b34676b0aa294c34aa5678f0d275543835de6649aa06532500090dc4d616d27b47592f82457ed4fb2b3201c0777b30efa9bae7517973f320f88abde0be9dca742618956638d5ed7d1f27600b76e0006"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5c7ec59e1cb99e7a438d6e19ca9936c3b41e64ce33e1dfb041e9fd3acff15c57","proof":"90f6d298d502f17b6728702bfb63311a35a69d2b658adc8acc7ad13a23e9c70dfc6ec50f8f3eacf7536de5a8e9b43ef30a0ed346a4ae63fe44b6950a58f2c619743eb7b1e9609b520f478412cbf10c45a3345776047c8f95024801aa6f949144982ae77a11afb536335ae68c2554b6560f613f8a75c6ffc90a8ad0e2a2fb18439066297d729dcdd8d50d4773ea8570be866fb5ecbc7ee1b51a9527485dc923087a76e1205a78ef63f05f8c20d6c4903bff95e765b8d7482bfbeeefbaf4a5ac02d20d0e9625bfeca0c4a4e549941439c4710c0b73bf7e37719d75f950f0584a0cc4c1435b55f8ba52dc827a6698840e87cd3bcca051ae4e37dbd5ae497aee3a4e82588fe7048774d00d23f97252229a0667bd48094bd5f960e1a4a4c6991d817bac2db9412e7d05ca41a417f4085c1735645fc4d47b34683e63ae29aa17e3b2578449d7a14fc2198b191a0e7704eb89304d9d63d0f46e83c135a10c1cfca4e4444eac13c72dd31a4bc8218c1ee21b0f4f51e76c0b07f99de4c372d18175506c268a5295db80ef30c6fd02365599cd40eb7cd770888dc9eba07b8441b69ccb41414880ec2358e2923fdd82303fb423fc4d506fe20f71ff694529db2fa60c4f58250c45fadb0c369640d8b47b4b20d05d319eac854c4b472c26db31be96118fe03816799d4aab4b42d6a549a94016be8c17dd7f6fa17c7c820f71a5230d4cb9983c84b2962e72e85bdf34f4326cee772d8591ede2b286734a7ddebe722f1f50e44d1855b333d0906665ade332f04a1122cda047abf412c82d411447215a21c2b92fd631f0d4c93cef73eabd61c07e5d83f694b2a901dd836f2b3de6e8bd41bdf72f5205925f8a4f0ec220165d912f150d7d4727dfcd6a84c8e1944f552468239103873108722dbba85bd74bf72e55f11a55031808bd4de6642017a490eb1a075c0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6c04ada021dcce450faf73a33739ec8077117fccb4b64e5db5663b0969b57033","proof":"166830f51e029e67d2126bf73807bc8e1bf86a7caefb24fc4d457344f14e4a4dfe4af5074835610ac71c640f760766ae3da2dee982fe253ed622ddff3dcf0379ae0cf892fbc59ef35265441f3227ecdc110e0746c1b0c605f30ff353f383ab7432ccc543bd79eea03300190baf7223e479addd28f80745f1d8ee2abaf9be80435f6cc4e1aae5127ab12dd27e6429779dc8b66170309922619ef80dcf67f9f307c6b7f3d07a185e0095cc5aaece5f65ba950dcd1eae0dd2340a0ab6fbbd70f403cba74c084e32914dfdb22e2bfadd02188a64a75a2e8444831c4ffd45a9e9fb0678a1bf2ae75b6aa78b42cda6addd3e6206e6c1bb5a1976782d9d384a50ca773e14bf359f77bd9821d3a0d0bdafd9a1b0ceb5072d5b40ff8ad1e18a2f0ddde409d6e7a0ede156ce1b58961526df75486bc69a2d2f40fbd0437347ebbadbd4fb7ab0d2e42d67b4639268fe6bf47a42ce0aaeecf60c03b3a68adaaeb8e9deadb63be0f873464efaa4d2fc104a4860ad5d3efe4463a4eb0be27aff7461c21f1f3439e8e84c787ce671115c55fa8cf01fd3c681b9de97c0341ab6b74d5f1912feaa11ec989d529d751cb6edf75b3af872ba674c8a963f821b30288e3c9c1479364470e83933ffb3c5c4962b28efca43c45a9587fb04b0dae223059cb72afd43c3f265301bc31a5342028e68601687d246b34d45bf5f7f142e76be8c5ab9c9167c8c241ab6a08083eb5dcb6f929b56537292c5f91b7e6913450563530b6b96c2c13319d2478085566c5b8b62ce1023963a6e3675f4e0a85a924df370eb702db8674378883bc85902a5f301cf84d9386372708c37ab8f9b90b05edca053aa2ac49e567ff20d253b2f3125b7829c819e3b20ee75cc1b9ff725a7c0b11c8c9c9aaaa7c20c7abf975f6f3f8ab723445a5b936ded1a7f4f506acdcb13d810860b7b6b86e907"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"322bdc58708ca21fc1dfaf5efc60d5b4ccc65ca6716df9b0da67032f4e83823f","proof":"e429db46ddedaa1e3d8c3e5575faf63fd18c6f375356a8d51979638eb36f99660426906f6c469b95c032aa2754de606b655b8a658384fd5dce11533dd6d88676b28584af8bb087257e0af30e266e1d876b54d353e837aae1293a7a90210c3f4fc68ac805f374108e376bb7ddd3640fa967832acb51b9cd66f984e26884dba67c8deac814f8e5dd0995bd279fe31ac6063e28dccd2578a29a7ccb41c16d1091058a7ebeedf0960a06336210bab0510a7e2305706f607c4d65cea3ead3a4b940066fa7ab43a3e70c4a047ac99c83177d36f3dac6affe105d2672f653a6d028c80f44e13f97273307a42fad1148adfcb6ab261cbbb22ce5e61733e7edb973fc116ca669cbccb460c7aea20129a5e7b51b0882a6b9f108ff03a155498ff6a63c6c29fed86876d5f9f4dbb3eb17b3ca7460349d3b7af4725384eb1ecfd7101308e62dbc32b47878cc051caffdfc49568a9ed6dedb406d306cc42f5af6ed8c3c3d59286e6be8bbf2e0066ac99f04e847537ed9d7abeb067840c2c052ce8e51f701995c60f681a23513ba68c0504c8d596bf7f3d45db1407b5c9d7bd7f8f9b1b6474052d0f44a8ca578d58efb47ade232e9c326249ca594c5df18f54f682195ea4f3e753e127f18a729415ffe74e2a0019eb2e5f7afb66ea3966ce52ce27a02abc9b15af8670cf44577ecb347d641d1ce25a87aebe4b6e336c71df90e9908d40ed14d0a1c57558595d09c4f7001a7b5df2b826f63e72e14ad67dcf591e940b011faf511de3ba312d3a29e3ac00fb6db37118af60f54e10a4c30c819c2c06f9512cb66767216ad357329bb58d1135b3c33ce79e09967845ed34e5f9c5a8565b826e79f576bf54f6a50a8dd904a70e4aa9d7ab0edf9d3fcc55c77f3c0d8976cbecb890208e78cc819c070486f78c004bb13deaf5f778b787b5d86f3cf6e3a6dda1dbdd308"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cc76057ccc6f3253ea53a4d8d1a309e6c9a24f371851c0e61ccff0d2c7e6c27e","proof":"30791d2fb05207f1b847e9d9e2ce8678063347a2f6846834d191ba7bf967ca1b04ecfabc621d6ec1b4d25d2231b5e7192f6919bd95481aa1e479313bd6303063f61bce045f5deba5a7f7cb0d5b1f4ef2bb2b4119e0b20f8b09254db4ef05ac02f2ca04d86e731f64dc5d938cad64ce4b8350899169a8d5939d5b8cbb86186b42ede1313525f7d55425e3f6455f9542a7f3219a238f8ee0f0aeebdd5b3bd5be067f65462064e1ad9bb8d9eac041274202a9b6f4c177cdb3f31a39057bd66d5b0063cbd3f45f903c2e9eb61a7e44cba4ef3057c863f902fd8af1360f1e1caaef0d9ed4d9fbe4882ac9283e4ef91a4de4fd900f3528c87f25372b808f46c4f5c63c28baf01fa4b0cdbf3430d4ecd46b1b5c22cf31895f125c1de7be2cab5ea9ab29946e004d10f7c68c2ce2e7277ef39967cc453be26667a057b0c099e21e63b47e40c9107e181a3020f00101ee1e74f2b41f5409ec8105eed07035c6102330a2516ca50189319203162176300f5ed13ee60e47ec8368da4e67e58bb84493240e013cc83b1935f6b8a75f47154ed0f8adb2a6a1d90842e8f3d65014ba3e48aa593128ed7cb727bd0e12d09ff499e352595f820db9c5367735ad8297f728d5780125502f76fba53a9a7bdf6fe27f57970e2f0861aefb24ae15476904ab1924088f4338c381b54e2e2bd6194a0b4730ab45e47973751032435ccde451b7d6441d0e6e729244b0876d3f25a70c33c5523db7c7d60638bbbc80bb75c289ce8d0192605ba4e5a729866c9fbcfd444b530d6eb4a5f5c2dfbfa9593c8a87ed87972e927a18fca2475c2ae674ffa3f9ead112c7ac463fcf30f5edb20e277e83499721653c58bcd7f5b272e75bbfd704c5dd943cfc9579eeafd9ad3451fe344186116a56b903b8d773299869bb5af9c9e871c4dc0e2a4de132f6ae0e90906c8c8e56e7a68100"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f0307eb1f6c96d5c2b40fcc237ca9c584389b7cf990927732b976221c39d9511","proof":"50b2ecfbba6ab461f9d2c2e0ddce38166e20f040a58f80bcc36c11f907421c7c769511c00f0fdf6ef48884738838a19135785ecac26b67acf57c98e9ae85940ea438d83b8cb06faf44f156450a6423dfdcd43ee081c6522582df6add6cd53f1148e14b2939a7a5b816d385c6869cdd4a03777b178434d1183fdd1ee3bee24d6995421a52e23c58ee47b1c53943e43a30e2d7799dc824ae82a5b123857f10d7058ccd128c1a08f925f2ca8994b92f372750d91b37980000e01a7eace204e12404212ead028ad06c7c361a988198386d8523b500e82db0c6da0686ad5724641c02fa615e4cd0c95784e2fd41d6caa9196626fa9248c098750968a5e5d4b19d0f53d82a07be455e89ac1827eb1022287fba1a63deac88adb8a51195016e098c305202af54f66f7ff6dea9c6fed7f0319d09857ef2b000aade7db7ac88418ec60e7d44b207f10114372b41524e24aab6058abaa2f04cc2eb9f6f6871e9f6903d6912c2ac802047adc27906f833dfbf9434e8e83ebf9b81c24f87c938224899e0402932c424666f6d0e2fd1c0e9479a9961b545cc9c6818e03b4c0f3f1045a0817b0a284a839196466e103e4161d5e3ba320b5c91a3776457a74238db04a263ba2d3a04531527e0cfcc037ee9d0103d78a56d705212dccd3db1801d3ebfe265208055aaa6307aacf52868b319c6a904f70cce2d20c18c3c878968c8cb4aa15f7862611855f154044de362a2826bff4bf6f2146a0835d39ac0adbfddd065c2ffc9f17c2a4f2ed6ee105137906bcb91680bbd6095c6403489c2ae6fe5e8ec1d65ff4f130a1bdfbcea47cbac118f15e854fb64b2d2e3a4d634e338c2d8194fbc7dbd6e2a55573ca8ac8d3969301acdb683a3135eaf2c8820c6a94575e6636765bcbb92013377d5bd06f52a80841e1bf084215f66e39aab12e786505521d591fa038d1f0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c8da37fcdee98dea7f8bc5045c751feee6cde743f9b94b8608c4d4ca0a82111a","proof":"281e53fc08269116f10d780b1c786da53a7aed10045f743f1545c3e519502e5ff841a955a1218007e317011047558d41ea6a6d94734f429fc98f78d33daa03672c11e294b5243658171cd706f3caccda99a1beb8c8d866b4ef161dff2382f47702b1addb369f8004bdd7f9b2975254f898dfee5a26a8ea0401b162082805085d97c703c44648e76f78d413d4d7b6ce3fd12a67db880236821842e90fa90d9e0b97e61f8abb65101a39ec4e7a9b7637d10d81cf4daf6f9d177dd18cbdb913f20e1be2be7b085963d7525036ffda885bb99509a8f99417d69d1c235f63b4e42404ecf9339341cb0727aa112bedf635495a4becbf6491ecf25e6fa143f84a4b3b54166305177cafddfb1fd10ef95285f596044d67144e206c394460009cbd9e996364e3e11b119b410a2e7a7c3502f5a6f8125c27648946663f75b35ed02ef7497c2610dc2c83bfb757bcb6e59fb1fad78a462e29f19219788efc603726a8c1691d7423369c8c0ce49a987032ef338eaa2ff26098149e81492219b13ef89634f928ce8f860b9f0c7f51a09a0e6c62d06482c10888b56d0864b0012351b3184f9e010e9b2aa8f9bc1546c4617e326e2f63612f4ec6056107eade11e7610ad923a02b60e1972100191d67ca68d73907fdb1495dbfd43765db9c7e716c23a585f3353ba4eb884d876f4eccb4cc68d3b8d7ada20129c37d075d44925cd068a9c8d7ac4b748d25ebfda40733fe4370b407b2a409d526395097e4addd57696477b0ad532e3e6ff6131f24975111b68eb7e221cf863ed104421ca4780e44118561ec60131accc0e363d0511d18fa4d7a4ffdc66851fac775f2316cb24427f794b8a7beb3142aa16db51f056603d11f5bb52c36b877660ff8cf2073b2f07708723ef4fdf40fc45a09ebf1a046f0e87714e3ece04c96c4b0305666c7be05291f7c320b4ef50a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fa71c56c20fff06bbe53c41e803c40fa71fb27e711006353232b5d4e14e6b171","proof":"aedee43ce49f15d2651670790e9a8f1f4df64ca83c6b9e2837e9ab966e6cd255f26e07e52bc4ae628a1c918a0b8f4417562b771860338c851fc433d60cd8bc32d4558277212cbe20135153bc905a846c56bf48f1f64fa11321a97f903427147cf679cbce005e9579e18d5fe27b0d5efcddb0a7a2d1d1a294a12be8af15cf2e769ff48e2751ebb3be9bd9ba88ba46e12b6e422dbd0b08c7a8c3d574dbd0772907fc7ab699605c03a8ba1fb9409a9ef1779f4e90f33220e6fcc64be5c450e01904c8a5f6db97a48905f3ebfc6bfed570e76de4f9dbff9c1bb9ca72e1e108c61f0f86f83809d435641e5cb37af48d4928b014b87f728a36292e3ebd451cc18d6e2714401c36d6d85fd3249ee4d4b924e1eb29272f7d93d112a1f583c3e1047f837d38ee5c682784a242aa13fb7068982d1740e3685e3cf41d1d65523fdd1b7edc0044d7f0f7d024175a56e79af371f7a40a862208d4d6fccf01090eb07e4913bc600ed1267a5f27f11335cb943272d417f44f0465259d3543f1cbb1732d43b0f6308627a891b1d80a1204366c0255f7e2f4ad78101463852d0c3a20f325fa4f55777ae58f2bfb2e7ae49c69a8485e245add51e567b8b24980ac97fcd52c95e84a1896ab761e8577c0c89e57f2c77bae58581cec7aaf841045989feffd216d837751481bb2490a7b486abe0e5206955f0c68d973ffc88da933e3cf786bd94b291071bc8c896ac0f3a109f6bbff17aa3573ad1e0e76247cf066b601d2bd466a323c488c637f593efaa7030153736bf6a239ffa3814f8ce871362600f832d806d6dc5696301eba1e0075a14a9303196a5ecf0dd90e9f344b86b8778637a119ef8c7d330153d20df583ca74a5b6cbcc3fe52722750d8aa77a1dd67e96ecba6fd6569b0c3afc619eb2bab429fe3c98adb4aae2570bf40892fc79efa16e8c76325d25130b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"58923834ec3629d9eee96b6253a59db83f73ac6cc3dc2654bf35287d993ad65c","proof":"3c08a7606e6f3114d8f8aae129c7bd898013735528d12bcfc53e2fb9d9c6901b761230363d58e7433f8204f16fa0e3bbf58a546015afb3b5198aed9a2ee56c7e683631d73e45faa8c90a81bb692aba392969bfe8d7e32f5c1404d5b8cbbfa330804164dd1c5246b5dec4d6e18215538db1d4a289b7394c29e61e62fd702a2711139c139fefa7caa8714d9a37303fa0cb0ec26d0a1a9f9e9d9fcc151e65f408066bc733c284d0eb1219bdc5b32c747b3b57b5a1681ef336c70eb5b6f98ca5540fd791107b100180f087f45aeb01c43a29012aa184a5ce2811187c5258bbacba0e0a94558a350ec1cba2d3023a3cbbcf42e6a52dc334eb01f2d58badd9d3a59b4d4a1abe165e59fa79b468c27ed2f07fa737643d1e4a3e4942526534091f46971cee9c9849b71f27c58db88bc596dff200231d6b3456e3bf39af469d27de8e8a2ed0643bdd0c8ec82bc551535ebe731f7431692658343d3e340db2866af3c44329ce6fc02cc5bab2d09bde0423fdeb55d6b3cbaa969d85e8634831791365636f4b940a1886c84123d78b13c86099eaf9c39eb218f2fd9ab10b82b21b18c4c8472e4848c8c693b3ba39c3bd81168a380357e16df99ec11d38230e6a29a551663d733673517051974658711af489616af766334f6d4f4d5622100d9893aafda9e04fc2cc628871d97e4c21e6d77ff98f8d9dc91a252be0fa213b5b185b155ebb6c03e6ec6bd162cdc6194a5deac82ea8424978e38e0df7bac9510652164d0ca10a6cd2b83bd7e31990894f0be4033378a98731404f22cc85d0d70dadc7db3a427a4a72e5fd14ee34577a270641f200edb4955358d4e4d3d1200637a1d34458494956b82b23076e6679533decf4c8db883ead50b30c82d4e10068795d32f84682c20fa724919ffb12ef15630371b704532559c41109f6f3ec38bed4b75afadb363103"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aafb6e65587dc013cf8f56358eb98e23c3605cce6a58a68be5fad615ee0c3f56","proof":"6a28b50d7f039923b61a736e3629e0ef670f1f3574a0fd7ce27bbcbca3ee3910508dfbdae2b71ec554fa4dfdb60a061192adac295eb678a09175af118efed47cde41c2212410a8fe9d995aa959b2802a72b3093ff2132b8cc46bdf19274df943768318510ea9218fa9ebd1fafe3d65d51d445f7271f7bceb19a28f5c66d3303cbce3a674f21351784914f0c1535dcad9c3accf23c69766e86cc6138d9a211601e3f4e43efc7897e4ccb5682264f7076f216446735eb6b104054953c6096fbb04e9523db242c799dd2771d71863edf0a44f58191106df9746db88f23e50b74f0c1cb9174ba3fc3c8aabf8aafb4a802f6a79896da77e731193e03a8cac074db5061cd9561e998726161ed5637dd1c1526baad5e10f3615ffa074932fa169e87314fcd96d0694e1ccb8e25ac345c789f0a14ad8a825f33e2f70035eeca617fad267b0cbef402b4d404462dea65bf4d767eb834fe898e61837b3f39cae5689b6ca17a07a1ab51ee6119326d4414b7027a8d32d13f372c56a9fd11290a2caf93fa369ea87f8a2c19518ef04662708f74c34679bd93e81091c7974e946acfa24cb5067b24461a681b6e5c82090558805fa36a936a4ed6690ea3dfe46f3743a218ab465b4a07ee57f47c020c592c51468713d2596f5dcae9239308ddc33b5d1a28a177e96e06b7d0ab7dfdc70a887c498645c4f111a8c30ff522ac9cfd8e657a7b8e62300a010e95b8dbabbf48fe992a25c3e1abc3528e6f8eb9318ae4c00f0bdda85331c2f5220517056e2f1af8203bd9a3575b0bfe0ffcc7b40e8ad63a825012bd37c4eae1f0cc7f0f6b51a70d0f4bd3d784b602ec294f0559b65bd7622966b70fa53f5a08e7dbf371a98f6dd9fc2f09d4540f3986316de5f865c9abd13fdf085940c7d79dcaef298e78c72e65f4adf258fbaa9535634cdddbf6a0e1ea7636488b10c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"02e68d73aacb7acb2ad605002cfbb9a0f6be336a04fdf1f4f6452ff0628ab30f","proof":"70a30a9fbbbfbe02e7e8006cf037bfdc1e8eb0f73eb042e72279eef92d7557476a2646f81d4490795828473ca8054d3f044b1b3a890026d6c1b16c5a798d4259d23f682f1a4109dab32fc5aae4b587feb805b4f8cc7686badf76dcc788c7c22f1ce682a7581441bdf63f7c6c56c1c1b227a9b4c5b4f1647d1718563380b67165c6c7c403e4a4f05e6926d11369d9ca9b82152a11725986c5be41d1062d0cb10667ff7104842b9bd6fa0e84523c8098a01f8a56ea82e460f1ed04752bf8a4c902eae8b7e834e3ab45d466d21beeb1deb6eecba97135e9713894cbccb3f2a8c40af83d1be933057f8afcb5bfa2690f1a3e47c39746bc97388dde0056ea352f85312abdc62359f190dc0c66b836caa11a45e508228474c07145ea429b6690101f66323384725316d15f36109568c25f54359ce6a150277fda5abaf5d8d33d1b8414226c0f3feee40b45d79562e975cd2b5a70fd08704395ed1601856318fa5cee43823604117773d9d43bb23edb66c6f8531e140fb4dc2643db7bc5c8ae3d5681330abc73e665bbd755434e338292568ef677d1c1305f344228ba6e0377aed82f79e49579227aeb885384f64ab8beb61edd3e0de039f6ccc1cfd20ae38f9bf8e42ba6c8f907d03f62d1342d10225c2a23ef05786ef72558be3124af5213a438440c7e50b130728dfc0c1a9b4b9c461f7b339b5e2b039c672b4f58748014478f3b4ea45055ef14cffce5749f93c423941c83c05ab05f759738cef9dce53fe634334e1e80220c8ff24b3621edd11c6f6ee6412d25ad59138cf77ec56b7c4343ca21737e55dcf7ea231180f4324053dcf8d677eff5d0ce99c6d5d2def1e9609dca102d0301c0bd4774bd68da8d952ffdd0f1323b5995f8e13ec90e0e7f1cc6ed3c800926da043ae24c793815179d353b8b659fd15bd68e3f7d502c263a1a057486430a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4cea02c0241de81a145171798014cc843e52137f94319c1b0a20629a3c59630a","proof":"b0ceb8ddc7aab7534480a4c0be5e795c819af87eb64b8afc3c50f68e5432b953c43f04d8b4b4632bb7a0e2f0773be9d8fbe6bd47f6f47bac7b6ecd2270cc94696c172a0de8a417a55674a1b72624538d64c6a6e6af3138c1f41d109f721e232c5e83ba1b8fca6c38aadcf176dfe986a54dd9025a54f39cb0f6934024ddec2963e159d82d30c8470bbf2e9eaed3a789314468fa8f68903f1ce9d260a020ac0c04e97e4ad0c05bffa8c3142903022931fa1547190f4890a537358ee385fcce2a00f0aecbdd600ec3a4ad765248ae5272c27cd0d14b0ce4c986ca5a64bb0251a70db81e8172c05f2859b0a564819d69008a9b1f049e71b78e242e909f8e6c694c3502ec00ba954ec68f5e5ebac86e8c2ddaab1c7917870c57eba538c3ee06014b780ebd7fba8bc79a061176956c8e1f551a5b4d71082a19660c31a386ae2ebbd029924d58b0db1244190bb9734d53af617d3d91ef5e610295b930712c35227bc94a9281d908330e240588d0efc39db0af1729c6bcc2f1df7076786646099eb09a6db631a903cd0469668601ef03cc1cfedd7134f938bbec183e9aa46148c3a02c722abe9bd80c7fa7eb4d6814acb3f5aa08409d9b45ccccd9995f6068f5cbc4e33932d8aa941588ed5580324af1485f40607b53331eb0e61026e934ad123b6730393652f3e4dc7925238e4ae6c9da6026a979bfeb552a2075ef7fadc2b0c9d578397655caf41c8b4b393f066ea0f6c4d719094ab3a97e6c1244719ec0993f933e58208a52c23c239a5c8a2ffb11390744d397ca4e06c62f780fad2f1e8854e2af626e388a4d9ea86add626f02ee8ca54d5a205a216e84843294bb9c1e5e25936b4e6b9873fbd9a4ff05ce5506bf8b90ac0f11a74c32db981bd04e8a22fec4143603f6bc17c2b36937f6ccf72802b0798f868d60c8841b41c42d0016ee46d936190a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0a15ebe2e5613797db1495fa7d343c13187fd02dc71fe155391ca8bdb1cf1128","proof":"8c47734066addfca8dc3bf7396ff3bc26108676349ad30f9c153e3c71a2b0c3750a5d14bd97ee0734f6a29be7c6a321f1e9b45f9f4e9b3a554938cf57406e673cc3ae3ba7c753d3f5c9c349b3daaf11d50cdbb253a3700cbd0dbaf1c95075870b444045c862f461302a1eff95dab441d7058c913bfd6786a76fa6ce58958b57541e06d94410d66eafa50521ae0a595d306daefdefa6a3828618450c876b6c600c746ea4ecbc310d583f8203528fb7396caccfd94fe82e1c736df04a4c1947e0f62e6d52914452f2fe3c2f5ab4e92da4669a38c710a0fb492ffc57aac00da370c2af423ef01c9540e0bcad5b65bd1def638834119821d6bd6f423bc68cf8d5c361ab2337aeb1cc61cf8fb79bd37a2a642633417a564d622c3f4c651b46720951388179d575058bbc1e1ce2175a54de674d3ae1a3331710e0a8521b54f2477bf070c812961eaefb0276e5df71779e968b555681768653dc11557c964a4701c834ac63c1f98b416c19cf234253745d9e46de1a63d08e2c67a5f959ccaba6cd5523a9cba0de08ce4a2dd6a62aecfbfac8f26b4ff2c8c4bbe60f46a2e5a22a74dca539a22d56d394f95a381a15536bb07a85ad1adc2e1f607a8dc8f7c2e2d7c55a75424194f088affc9f088ae12bf9e4280257c75ff5151ea9bb0c73097d4fbe9e9786cba89b41372196684f339f476e2bc980619cd81eb2ea2b8787eadd6a82705683488fec2988e3c9ab192d74b9a07a5d8f7fdf3c4116a73b1a37cea99654d2169cc9b1bc668b6aafd96115c21f4f59c61c532abeba488064a7297ffe06e0a330e76a2bc8b3451eae24e61f5e60b754a9a5ccb56a79c46641a64e3551a2173810e915c44cc7eff364ac8ef1ce048e9c78bff541434d39775059f59bd95e9e5080a3250db2fa41b5b05ec655c618d2d829548c67783ce7f7cb4a0281827c4389207"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"968bdab9a2aa199b31ee408fc102a3990ec54cacb4d6ddb1cac8dbce15a15d67","proof":"08f83993df148f098b38c9714fe29a560303b29d89109f0ba8251237196917658c540c5c7637f9f20da9c3ad00772730c2b9c687d087e6dd9300e3974bc14c058a3d782cf3a52208cb8b66af324c2049f9caed86611101981f218248aa8f286c040d3043f4a346acc8bd662a13f7cdc838ef6168b094611c464b7e836f8edb302906c5745762271b54deda0498bf59529d08f45e035aa68eee3a00f319d0a401d0f800d4672cd9bcdb17e2563effc46e8c0d06d0990f6d504c4b66d7b450ae037063de3e017601d5a29fac4fc7150a3bafe458ec18ac1bd731d781f4fad4e70c6845779bdbc934f1528f05e812fc333a69783eae8f6cd51c6c6a017940104a487e43099451643749ee3996e067ccf882d69576ceab671986ee0ac8f0559c7d2f2a7776b907cd30156e2dd71059ed1de867ba5c0645fd04c85358235dc782d63be0edf9b914d20db31b1dae53196f878438fd5f2ca2b89fc5fac91958f5d2182da41561ab011ea7b1ac2c45edfb51d71ff22f5dd82edeefc5d9f1049d27747e5edc832d0881d7f5d3d2a0a331ee9f07b2bf091435a2b7a3f8639a276086a12a2cfcea41b122cb24c0c22931bc4af0ea719a763f8152a2691b733eb207829897789c6d1336bfee2576af127d18298c7ccabebd6da520cf1e565b94d8074708f33e26a2877e0cc7a91cd2e969fb7e022bfb1f2edcf91413461951948c5e7951c76bf2060d2bf22a285ab08ee19d6101b3cf4091377faa8860c3c58b9d3fea81ed78ba6c3273b64a7892d1e931ee76e46e6e08a8c155cbce137947418a3d68c2204d4c01886ae04b381adc3aa985d4132c28ea9be1e9bdd39e2ab6e154534f2d077db26f540d61a5903618c03e08d81853cd3194dc64cbee08dc0ac770d7551f750137cd851e5709109d9b7d8d7187b5a794da209107462b82c87f4def9a770bb907"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dac15d05116b21b5cfde4a9aba898e3fda588a17dc71aaf913dbb999e4cc6f68","proof":"3cbf56174c37a41059047aa66ae2a560f303d69b52d5bc4aecf6388f2f1e2b464650ad11f42cf4e975396ea04df75dd81408b4ece03ff3cec4fead2058b73233e06c6ca4871c20db5c9942a29d59c13cbed4fa44e2de9396e618a9a2ebcd012cbaadf6755a7a022d7067c2904313432690f0191cf24a8b877c8e7fc31b5a884c8be9c534a9fec32c41b1a569e62346091f2f4f6145ad0499f820d300cacbb50b9f611f98169deae931ab5f12b94ef569650f2a9070485a62fe8672a12f80330724360b63ac814c55c5aa78932f9138f229fb2f46294ada762621387f24ba2c04d48b09d344f7a6344360b363830188a5343281327915429bca15aa55e822c1559afa153d14141c588c0ecea6ce972cd1353aa34a558bc0f999be376e511ba8457866c151fc5709cab2c027e83ff0221169f6adbdd448ac3a21fd91154355604b2aeeeeaafd24e18da55c28bb81e775338e28254207fc7c736e461f73b22d59346c9d1094800df466f44366e697450d9f8fd28d91921daa63091061d63fafb2596c47df98bdd5523840f836ce1f6905c061b723416986df307c45589e8563d65fc647824e909d2e6e47300372188056017beed1e1ea3a729326a0b289f9e9d96ff0849cde44184a34019de6bbb754b4653df2174798a86e280f4c3fd96dca4833345d9130a9aa3208c8bd423e2d349f1afb92ffed1107c81a70b205b001a97d1d8a01761a202e42965b0055814e4336374ca2c93ea1187c3d74b42cbbedbcc953b23fab0d803ae38beada86f3606caacf73f4d914973ba4ba6af8064a46b4f949ac69ae1cc26eb527221db23927a16a91e4f77f763f4e2dcf408f5b4a69cef92c678cde90fa472beca5a4484682d1a0c367687cda961d960cfc13e03cdc33790d3de34221cefdd3cc340684ef53474d76533e39ab3793c4e0048ab3cfa99d000f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"48417612cf3127b4e2288251972ca9904c535ac26aab96720b608c7093250b38","proof":"ee1ad5426e2e1a1b68b212d767ad6cf09bbadefaf6f42c5dcc601e5d25e716133e0fcb8cb388677d5238ec74270486d8fbd554828e1165f1d8ac4c23fe2f30206ae662331dad5f0758f3352b60d4e04bf8cd1d3ea3a2b89f09212da3a369ad2ff0d68e216f8c0d8730758783e4eae1ef24c98991f2acbefffd8d42333b30ad2594df5a67c04b91d8d65a154ce5fdbef5983546ac7b49e86688b25b2717052900381eefa9b525fcaa5ad8021a8628501f523291def0a462f45d266730b49da00e8cc195952b1942b0510a1f835faf92dd99f82971e4237c85780d88c3aa913f0ef2370e5f231d33bce724707c8e0550626de882a4834b30cb76e8e877f57cb567d48c1ffbee4ea0c9bb4d12e6d259c232d3c913f8b027978c75a2bb9652f9b65b88b757f64b104edfd1fda7bdb117db6a071a77473bd3031c26f075390621ee685244914982a977206c60f5d861758d4bd128bbc85546112ea3dd9df5624195268c9688d368f00ee8919b9e91bedb495aae57c3729504d89102a70d522734a8684ce6202f0195c4b6e9f784b4a1f0a8ef346b8a700271bb54e47189642b1b53028476823901454cd0d8f802f37c8c4dc9873d5d08bcefbe822df1aa54292aad55c4fbcead88a941d3162041a52ab808062e186e6e549db702d79405b2b14913744046e9957c22eb161b773614260ee3955d8b764d8dc5022d3d7d1c82aedbe42a505c42628846d05f8038d11b64807c80af3868fa50a7c88fd10cd80f3fe3e477f49c5a150c4c409b0c24ef51df4021430a1cd09a7267b806307ae1576be356397ade65605f9efe3a7b5893f41ae74deeea4046efbf61f4d0758e87d0eea0a1401a08054aac4835103b62a1d83aa0fa4091b55d238cbd4f333a005ae241edad012a8d93c49ed929f7bfeb87e551920f306e845e5378a7b14186b4adcc2318da0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"126e354c75d98b6c409f1d1991f9732bea7dc912ed06b191f549f68b0a265e52","proof":"62f38acb0d904838ae3c32040dfe2782b574d1371fb99a12a01b878875761f45086448a904e9e8417b089fc187f73a675cd8e916365e3f2f70a579035bc7355618cd2a06f068d984a202110d1c06874a64a16c1377e661c400171d7ed01ccc5194f7fcfc6097554183ad5329f33572bfa64a21640575466125ab9bc01fda5b382243c077979abf44caa645ac7869bccc80ebcafaa02e2dbe51733b916f5a26065481654f32ccbd5d89d698e854601f0714937017582b77fb11cf29d1b5f07c0327d705efbab5a6e39352a1f911da740124e38ea3bef836c0e4624e22f6ad2e068e0f2338313ce5254ea54e81bf8693cd925d1ee3cbdfb1b6bae0fd65418ecb4d94089e0976ad58520d1e6713b24990312e60ea01877ece66723987b06badd450f2417d1505f61ba28580e3d062e6e5e5ce2dc4c76a257afd47447a8350a2134d28daec09619e329099f4e8bbc8f4b54036e97b554cea60da21354e200e4757249698533e6f05af47159acbca6d7af8c11cd755ecb209994cdeaddcd2def7372a965b4c0856ec694238bbdfcfddc5e3b68e2bbb42d442a893decc072c4827035fc8865153101188a44259a059aedffd0d0792d6a77cfdc8ea99d12271527f76750e8b4540f1efb56720a645eccc345b80871f3e607614d832f20fa120658f2d530addb571684092bd17a3a8dcd2e3841a7c54245bcafd3f222a5a01f4a5584553661881ef4053f8a182f6eaf83bac032de3c05a4f8f43240cefc1f1b4316bae6018620059ac94866c9051cceec77b23e72913f751a71928ec184237ac91ea4c68e620772f0863a1e2dabe4801eaa7e937b8d8c254de191dd29b327d5edcf653784cb012848fe255f6af053693cebd53db7350272a77e654101245b41e7f90290e307434b2194c6ec52e35172dcbd2bba4b5da979945231464f7501924951ba406"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1ee0dc78b475932169a72b153deff632d3442481f5e16cfa52cbb3afa1171e03","proof":"a27c0ea76a76c73b847612c85ec253bb0c8071f86a1fa86495fe623a0cd0e83dacf30326249e591f310e8742c80695b1505dbc834b8d24d76b17a0cdedd5cf43667083a132f47aff7b3bae3551cbb9a58040efd2d8f434a6d20c98e61bad29604e001945e0ec7c94c85a918df3edb9763d904f4a0966bcac720b3a6ca0b7bf405d5efb32948e26ae726d0235bd0268c28b8869914f6c9144dc22f004fef448077162c944801d107fcf89c50b7216e8ff1e4ef75a0e338e0bce65a18e4887dd094ed10fdc1a50ae0d739d8dac08bfe498f6fe6584da4ed346358a32f2914d6b047edb1f9e35fb73662e389e947c026f602af001bc5093e190e9bb6155916c5a2fe49e5eee73621cf8da38e2e7e419432dd56a3a370203ab78e5292fc9517c623602e64826915469aeb140b2659804125440a8f7c7fa8e0bdb5e4ecf66d9c7aa629e20885feb6c32982aca485b8b53cf3cf734ff483be7d3dfe3cd2e50dcce5b1e0c6d323a87695638ca87e9001b50a46f3431836d4141bf3f21f3530d36704407ac9ff36134166c5928c21d152281fc7069d949332668ab4ebbf40541469dae312ad5b0b89c50430cf0b1c910cee52643569efd6489757dee3d2f7916e994453d1e8a271463b771bb2ea437e67c82a00f9bea197ecbdca466c853a2c8fc7c5e6d407434716ee91dbd6d447ef8e93f0a3fb6ccb19e76447aee96b8591187acec57804fa5b8f254ca1ace9dc25126288af5828a0904f47a4107ddf1570836bc284e084196e80674068d0fe4ebf3192ef2f6f7036519a51e1f67b4a71b061547234924b7ff13e44a8f5dd478649e55222da1d2751692f138d89d08e2bbd92672636873bd13d6c1e231c9035e03e6cd4d4ae36e234bf579dce002c2e264d0d38f810f2ea23ad3550ed78aa10702137271771e38a48f80820ea04f32c1b87f520ec70f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ba9e54e85bec46ffb35dd540dddff24646171a2844397c6b110ee3da4a5af50e","proof":"be9a61e22170558c7847affa8e5e19f09a43909286472da9f757f7ed76f11a46f01e149898c12704af8864406b197da1a7c0a2de060271f3d3e5f863687cf56536d861b7e37856a6f461d77d31faa56bef5d95d6e19b4902d0d8c71197e86042ba058b6f52a6d8c84c0acec5ef99c22900c12d154cbfec230a1febeba52272664c1e41fde9303bb38eee88f072db76f8e8c6818df4f2a7826629edbde4f2e40d4cf861ba0df427aaa3f9d39cad80eeb51f971c0db1ce6c22419125d3adb40302a4aa781abf726fbe1dd4a1946534a26763c1ffe3dfce444cb6cdfc8d4e14cf0040775ecc7000768daf6ebdb99987254d1153e05fc0b4180e009ae69b7330b521e21cf26effbe41c117d020850a2148875dd24399dfeefe4f710ce04d57a3d350b20534ed1de9f85296e8f9a3ef15d312627ee1c5517e0db7238d3fa836a8424344336bab05d6a70f6bdde089f956257eb5d03fa4163487b8cfebd4ea34711636a4357639c1d6c8b468fcd6e86aaa19edb48aa8a9893d67332c9a5f700c6e62429683fd7c7d79a4520bcf8f65d257a0d1ed3e85f1ba38c16255788ff8df70fb712a996705117194ba64632723d794df4f130ebb78c87574130fed2e4f7645d61acc4496634237e4aa352dfdbeebe0169e9aaf5b24fe72880fbb100929e074c953f269e628e4609c6e186581309dc6d78db4b3b63873cf471fe90a4fbdc71ca913963b4554b5d96efd965f7dd4600089b248153cfd3d89c76392e71dcee89d4904341e92c541e4e1c9d24b2fb11650e3967c45348dd800f4db2a11451e7903d11d128bbc581ee8dfd202822ecddd0a1bc2d294bca234dccf6fb975d86b81dc410f0c65a9db56bb1179b31317aa25780ef10a2a9b73dbb781107bc6c8228ae878035fdc41f84ee9ff03b42b70f3fe0d7e8eab3f8c6b1ae4dde1bf7f056e5c2b6f03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ced58c6f3327b1fada4f56d70a6b76c0c246ed2f44d83fbcae76ad8fe529a17b","proof":"e0a395237000a672ba65d770af224965a981d0a4f4de56282dfd5530b140c62b2283ed8d70c2965d85358c08b90b5416f75b0079dd2dcee433a99b4512845e0d8ea69a3dffa9a1c17150216f116435f8e1bc8bc101d0a7654393158d7581237edc6bc75062dedfca1f29dae720d47045bcfb4b697e81931734ef9f986bc80a7e4ad185412e0f9a3b9b8f6479f41b382d32ce15cb48ee764eea0c57e89f2c2a074433ccd3d14bee30646d73be056ca6cd736a532414b11cb55a6f6f58b119ea0064ca836a0fcbceea25bdacef84f5f19b2bbac6601277c9affea8543db2420708ecc224717f4324aa4ebca67770e880d0cdda9d25f1a086f1eb5142bd0115fb745809ab9994b7c705d34a6cfc140c1adeb018f668557ce06703db925dabcc987fbef78db11d2e49f8699e6bc6276dd54e02055d11e262ca2316b938e5f3ded70658e9745d105e99af43410355bce899eca26be73ac762e634d6ab17c980152a58de43154c2fb1ca8cd9207efd4896a55ea169a6cdb2e18c4191cc0a4ef582b074880f62d510cc58eed6d8097e2ca1b05745862320ed71a06809c3dfb47d39d94b7c1c3886fe23591a52ef4fafa8c5c26815d159c2885aba6c57061d181ed9c96226da7c9bb5d4283090c8fc4ea3be337b625410e1c568b68a6677278d13c6bd26c8373795d1c1b0f8da6abe1ef0c81d2658f9d103bffe23f7220c47190f84b6485668acaba283f40d3715bbc24e5072c64e1d53daf737d8de508dd8bba2804163da5a255a1485b977bd30c79ee7da3134357a7030459581b53a040bb3b83a8a4be21c1da24988a02aad6bc2a77b1d933f0a93585d10d1e48516e3fc3a138c5f7de4c2a6faf0d314d36ce20a74e62f99639b7e8798019b52a15ea4f4a0fc33d10d86d95b2aeb1abf072a93d2045784c05b30a6880684c4f12d1ce6e15890c3c20e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"00e5a7149a79f8549deb006770cdbfa22b931207e731d852b045a7ecaf27b47e","proof":"9613d5673fd9d972a0d993b111cfabcbf1d19d25111b4f1bd8833d503eb64169dc8fdade2784e4820417a27023cad165f52ca25e534444b4b267bfd0fcb86b7f6604675d67431a2c1a3bed89c6a9c0e2282e596204e6ea73d20488401d711c43007e11af4a1a17cb90d585cdb848e461e2a987286b93291baaf9c77782eb090522292d365e63ddc92893dc23d27605af657c089ccc1ae1de07406cd8ae28a60b16caecc4a72c939297d8dc493f16d549415186157ea14dbf65650dfbf08ab80ec3245b8857ee839e1cddcb779a149401f45d700818a42611922d0da93e6b540652b96f8eb76a4eeb5cd69df3b70512848a0114ea9f5f6051e6ec889878b15542be0c23f62dd4b31ecd0e1e9729b7cfda9ccd136457deea870c7373d8d80d0c3d989cb7159c77e095838a3835ec643f98982839ad19e2ac5500f43bbd7f61ff30e06f7d4c353ee032676d1f557cd1d886cdb31767b80c3b6896a2a52e019bb608306c03c8cb0d5139ca90c402c26ffd72a1877e9875693dc4c83563f0923a54104655a6ddae35fb3739f7d0f02e3822426779a364064bd4b06f2eabddcee89704ae4af566b08e39471bcb80bef8ca45df63f19e9a1aa0969e5bef6a11119c543aa6f6c1044bb71ee14e23d7caf26dbd70ea387ced848a0b73ee3172defabcc164965f53329902221f851ce7268cbfc1302ec18f6dca597e5a1944ff98c17d870a9e9004934ac5bcd81b4a73ab405f817c0c4049ebb56b4782ac1dcb17a1eb9944c2b552412c40ff568be0a0ea537af8b8b3195e24b323b7bce5a46593ad45be611c8d3f2206a050ac6365795e63361f65007466924a7ee57d0a73ce4b78878119d384a71678322d16447cd2a553961fd046fb8a8d3c86d78eca37a9ebb5ad9800780499e3a9dcdc0b065a91a5182d90c4db0dc0c30591a80b6b81ec27d338e505"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f481638be5670ad1ec96247b6ede6f5db295fc7a8cbe90656d999ae87281651c","proof":"1eeeb9e9c2ba445d56aa9e75788387aa8652b84fba51d2a11a72d9176d69ba61ccc352a52d3e651a125a784795c28749e90698e588fa4ffaf6aae04a97e1a03f780b92f1f035dc1f65ef45d08222632053884a960df103449863ffcc91c24144f4dbe45343e744d7112082cfe46a71cedd025327d6ba886836e287fade1f0f7e84dc3b6c71cd82af749d4691c2242a57ff8796478044b33794a116efbf17080026b83c434e038464d6689c91d4281068f9f53a7c02b86a8b10ee3a6cb846b007191a2f0529aa13c79b8d7d92abf2e7c4371a3db5e734a5864961a79fe331d20050e9251497f0872feb6cfcd5aa7e9ccab65bcbce15c2c586ce944685e7f6e34b521772988602a8513e2d714924e733ad4f23f1870f27d1559acc160f357dc56996c9cf9c54c20ca03c4360c5c169757db86780a168638fec4da2b10ed8336502ba78ef1a65a71e213b9265aeb2e26446578d53d6996718a36beeaa8ff0b05e51767ba21f4f31e46bdc50d9c899b97d91cb1a07f91f2ef555de149c7905de3444c6fd5ef4580a0728613ebacfe78b30a4eaa7b9ca32713d549a35dbb8177f91299aefb0e4cdf66e513d5b93b4e51e6b9e212dca6a171024112944a3197efc1f06b06a3dfffd54668f984b985faecf6d20aebe28c01d1b340de1cfc63ad66aa403ba0ad686d410789bf83c3852aa0fbc43a907c2ea206c2d2dd382b57a9f6f110ae2da8b4fdc176f838a8d34bb91862bec12eb5e3720fd23b3b4dbd50dc134b2130671cf16508780d2c782391b2e23c9cae4c422209c0ce18acc121ce652df4f5210a207def5a8be4bc1e5b4c76d356596bab66350262bb4784a4933cec2774224114acf60cd846ea3f797e9288aed135c493012d5f01bb03349883962143a04099baa5ed11118765c7b033f596954cb8742f466e7660dd4c67c567ef372560f02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ee84f2d4926505f90bb842549706d259d5d5a3a0fdab90c32d141dfd359da270","proof":"2a9c8394dc1228f230f256b5f47e33a6ee503e28c249975c42616b6d7e127d71b090aab9ee25575cb0732ec8766634cfb41729d415f35238f78bc5dfcf42ea485e6be69d117f9918b9be95d5b4c5627680b569ed8d2d19bd46a7ae6e36eb48516c804725357c9ba38ddbf07abb24d579f41bdb33b5fd3ab9294ea0412f05606b8b405f86531ee28aa20d61a670144deeb73138a88715bc4c76617f3d8272f2080265211342262dd414a669fab956ab7bc9116d2ee6df51ea4ed30a3ae2ce1f0868ee262d1b6b2324762f2d78e58952d044f6abd9ac3194157a4e7a778840ce06da4d7a4a40a0d1cf573cfeabecd25d348f361fddad22fb1e74d34031d7a8942fd0e10827342cbbb7e536f5513a0964e110e9a3fff52e0a763e4c2fa38772724d92748aab079a322a0e45e58232084e83e62920af696af855e16bb89e36b5ec53949e4183fb99f36f513300363035aebb1e817746f2ff4c249a42c0332e7567126440507f0eb56e37a8a6312d2bd9075c5b30e0e69cc2260410055dd3c542d64a80e1b7d535cb620b1401a930172b38c62a0a2acd22941b6e6a018975a68d7951be9616df748ed2edd8ac9d69cf5515275fa40140dcf1a26207e148e37856ca2242e040780906c50843c8832f3f851828ab1fb1cad88e746b9bafcf9645a71a764254c25427cd097cf8b601aa3061267639334870c9bb50a1a40403851d08a73ea694ad1b6c9c450a8894e1261231e79c818671a1833178f1a2a805c18a96395dae3cab3073bd126fed92eac780f07e67362b372a45d869062ef5ce130d49e53b0816d8e1eccfd21588e869214083c991c9f50900345c1fccdda04b01d3aec670bff0c21d336abed70a74ae8208c3f29e388e146ce8be282a128644cc0d2ca301357619898a5027ee32b56d4a546f5b562db868d8f2f6ff5e65f48597822bca0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"58b463a02ee3a21d6c08bd43b9f221a7af3abf997365e30af5d6a1bfc8c8683f","proof":"a00c8634c89d58d457f8fe85f588b738fee1e11b615b03f0def5cd4ecd84f336aa3bd641410c469385ea2754843c46932518f7b84d558d64836811d23c86462684b1d60e12841fd62c9bfbdcfcc5ee33138603c72e2c9ad433a613d7f761072172ba32629c1f4e4a9b1ce39cb03189d83a2859be757077c795b020324f40f5052c2442c7e0638e996ef040c79897f8fbe64786e684083ff2b4601aec8d75a10a62e88db8aeac9ed2518e6d8d9854d47fa600656026b4946bfa00ac948089ec0db546d98fbc41deb7890a2c8b9b61996025134a6533eeccbe5a1f6a76a85d41055640d530e1fc5f7a4a15cfc06a776b35951b0d212f6c8d4e89c294115e02945d5cb7f39b44d6b0947829246bfc07a2af4607e3ce18c2b57a54e520dde23498216a780dcdb996c9df0297c5308a61441bfe39189ef74ff63207bcfc40c75e0e21f05878ba542035d383e0f06e1d876a52be1d7b75f7edb121c8dba56862b7ab347267b476b3a51ac0a05050c77022adf036909e3412c15b03306e57ffd3e94108b09640ee1bad1f91454cda006f10716021eddf4256b0862580036097cd262f05000d5bbdcb5ca6056d5f58e0f2888902267ae41cacb37b07781e34ad23f9f9085afeba060ab8a0a2c9b3ffb5a647ee4a2ce1cc12ba940b2aef51fcaaf93eca322a238e8e659de1a94c982d6ad9b45a3dee845873bbbbefa22ad183cb504ec26d968e26124ffe4c9ee00e570c72d94ca9210767794be3cc31369f75a6a0e6f9789217866cda681c4a3b4ff770bb7b742e8aa82e52dc2241f76005d77fc792da45dc9ee9ffe5e3c9e80e68d27460421cfacae46fef10074243481af8042d481f10725726ec815a677455db423330dbd35199db0da7b1bc52b18d78c719fcc7130184b844de743577b7a7a7123a9e7f4c3bf525c8bb2d8f34b2c524ee693ba93e0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fa1ac1b95b4b793be15803f7ce442435542aaaf63dab28238cd3fd22bea6473c","proof":"16ff9c485a941a69c5823ed39c3665bb7ef39adbfb934a594918674fa57afb54da252f443b3b77eeb592b56920857af103be1f41301845bb304b8b046ac4bb6248951f4150fa6a799ee91a2b46f1757e57743af0e25eb19842425accc21ac37cb473435006b39e38e1a974b3b5b2534c3397301029f5135612ae5bea474b9d0cc8399d1483fb4a3578e4d67e317ad53d54dad7ccff7d65cec07b71cdb7478d0c9d78a9ea49d97d105c657e34f762cc266b44ee46c6932f560b652a43f63f2d07b4247640b14890ed6669319b92d9683ad1e06c3c1593e1d63c0f2c2c5148ca0ffe0bc5d5481d0699c3202cc25079fed7da6e5e0108e31f6d50dbb1e6d769792cb2af20c21c62227e745e0b74cd8407254473d1ad3318811501c268c036138717fab046929db7375257cb1b53d30acceb807a29189a49666b8b06fe09ca48805ac85471a8798f346727702652ccc936aa71034d67cb2f9e53c532514675eb190dfefa8e8efbae0d53ab3cb6fece3fc340c5e2997a2d26682aad3610b6beed3229ba52a894e08e9c27afc7b2b9ca479176cf27744514aa93a7b5d8e6fe49e6f275ca510abc3a574aaff1db7ce8241cd42267e9966d52d52de5bd0d190be7a7ba4e3a242bf97fc35a8898857c134d665b94329220d0c3c66b5b19058082156197672c428509ed4926d099c24496760614793b541e95e05561a781504e5572f104796e2ad43b1bbe1f585b6431e3cfc1cfe0b52e8ec4632c62f278e67a2de7f23a79b2cbddf436074f194b8a908191a532672c229f7c34b562e8273492c15179c021984de1e3f1d948510df902f75bcb8e591f26bfc26b8cb3df7828f4af74cf7373476d9e427e37af50ec4f29b590f3f0bdd07711ee6c1b13dc8097c29e133e690a259802e1f2154405fc72006ff59cd97b615a8c90ebaa283865f473dc9c05f80d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"02639cf0cbf636a518996474edfafe3caa13db12a9cb19a38764c36a5ba0225e","proof":"3cc86ea94c9091a1bac1ae3fe0cc23e2ef61816293bb663586b1377aee281404681eba6669da87aff98b44a2d0e2846a2c1fe2f97f740ed206e07ad7993a7b484c47e143764b8f9e88c59e65dad98e3dd0339a9b7b1ec6a10f1b4a3ee0b1be25625eebd0e24226839fd4a0202647698f6a4ed13e41f39eeae7b735449958d65af7afd469bbc2999c03dee60d24dfbf73a0d944dda5ab5cd6b0f54703e45c37038122579dacc2c730511a2ea16949b886dc3f4b5f8c88abf1240c6f599179900494a21c1fd1303f2c6b3385f40712ac78f3ec2c454eaa28e0ff20b9634fa54c05827d6b1e6bfafd9db6df3aed8cef3a8c72f64f942e8145a76826f4f06d66c574b2211d2816f366b6ac8ce736b840748269d8408d29c945a070d1db9f0abbde52c83e159682ba74253bba1185944806a177d208581c8d044eb786d4ae64e3e53a6a8f8412634678cf382f3bfb4f950a368ac611a7f2d496cb1b8996a9e4bfb962f47789b70275a09fcff41e092b3f8a6085376d0972b103f639ec883cf271e27f5aca328112b3fbb96d8dd9990f10224a81ac262cf32fabe7946be44ef746d15b3077aa293aa89e4624760a8c14e142acb9c8d0b0a9a055f285171de0ba94bf7cccc10e8d4c49dd2b8a291c11838873475ca882aa7c0dfe2cd7730c6d6104f837c08a08e7a045788fe0bbbbc5197e1fd12d6bc9497842332b12f2867a8999a825b0d8dda3242bbefaf92785968486a0ab8458ce2bc6e01727eb09a32dd0c97840de5bf1969b81304709d0f37576da78619e5b555dd50b8a19ba29d157a8e88d2fb035a327c4804ee2a7b3b375f3271704b52e597d118de5351e40d0813332cf153b5701a7c115d369bbeab22538c1fe5880a41ab9173900b488b77a6d9697280022d9d4c673d6c611fec2445d55efa07bd9635262208e4ecbc260e5bb49b7ff04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8e5579c2b8302767bfa45f63e78d5240d4a7d9fe05874c2e448eb8c0c6af5c55","proof":"eaeb5ff4165f99c37fe53e5b838b714be8c5194051bac9b831ab0cb13216ef43c698cf409882104632e8771b5d9c9fb25a6d5ef2122b0fd61bfd1d6256cc6461084665168787d95c8b66b0d8453c276a2d7f7dc5ca1f72a91518826657c56b7f1a48838be54d5182ec10cccaeb149f0508a09f945f7f9f84905e885a64c8d158d21856226fc746e155a7940fc1880d749bdc10c6efd0068357e7f424f3a97c0505bbb38c9fa15ed6f84f6c7ec0cf8dfc891e39040b14ac2d9d5078f0787f05097b58829b5345363625ed50e9c533cd92a28d3a0e39240e1c099e93c6f1a0df0d42e21b3fa0e489b8071d1c4fd18ac01c4390c39adc44c7ddc7b682ec86178965b007ee5a3e339fc26ec08c00c88466311d81191ec9b1602cee98282bcf01f764947fa3ec52accf17c0633b12695cb5f0ec9ec8d1eca0e41636fbe9983e21d01390eaa09da8320826f97f948f26fa518826f0a38482e918b788c5ed690c087346ea6e57c9d18ef5e5bd8801bda56d471de11bd58e7ea8e2d115dc1ed8f02a6d3d8c60f195b23eef52127ca37051ba5e657801d9c83b8b99fda558c19229cf4f05a02b5421641fb77bfca7d3bcea1cea8e5b69952b3b77eda144f42351230deb3c2cea9eae867552fb5f2fc85b455a64d177c4cd95da5b820ccb4d62dfc5946e5816ceeed7f5d4ca1488b064ca6c444ab355a46bf3ca1b02b1681aaa4c171f744caca4c2960972e23821ccd592a2f2d6293f78b90fa36ba0975d31a3158766905a783ba47fc08670167d9a218b6846fbd0856cadab3df5345f68bc253d868e4563be5990d4701abebca2654cc8aa2865d1dc00cb5ec49a1f09f19aaafa53660c17f797f42f0d31a38781646b3f8302c54d6407b1906401288894a0a5e54a687803197567f58aa860e8a4094dd5234106f7d16e0dd066537b3f70d06d641da0740c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"204e4492f31832b1830bd04aa31286df65580f13b52f62dae7b63fc2aa5dfd52","proof":"9e5d196b8e594775a7ef506c63fb9421a7a46e27dee989342a778733851e3d38301960bb4b2148957117e90744ffc72c701fd9d7832af0e7a5be2edd8d531605fe6a58cfa34134c8f5c7b25cfd0abac46e221453b5f3b5bd54d391227e81df76bc1d032290b370d18589e4fb54723e27ceea492e6d266940161bf0b5b12b8d679b9d94a6ef4e11ad5522ce160c8b05a4a857b84ae5efffa08f24c0a90e987f0910a67217b419b60c5185e2fa7a16a04ac10b32cfe5694bd8e2ea4a089700250a44fc54d6d5e1fec7727de08a6bc16b45fe920cd7b4a0a20cbd82d8c6cf84d50c46bc669e09b0faab8b280bb99a23ed93d0d60ecadd8fc2c5548d9af4c908d458786370103241bd3f5aa9d07378b1a4984e3ff21010436eafdcc49a280b086852a87ea3ce6a54069ee958dcbde55fca060d16a0f510a28ee27e54010059c9073626604824a330488db18870de0e239ee5c73ba1c73379178e1f5071fdee1b1c6996c37be3bb9b6b4f74d96ceac183b01a7b8d9986c3a572fc4ff8d7f52423ea47c07ee75fbe909d066cbeef31ab9f1bbc023f4c699fe79e6d2340df0e67094b5b66e815e42e553d56248bd2fbd97a291d98458a83425abf5d2e4ffd7b0157c01690678e1611f7aaad0b61c76eab8565e69e80456f46f5d65dc872df339dab221df69aea3092ac2f0152c9ba1ca4da6ca1518ee28cbd5c16414e36e8f78a10376486e983764622bc694d3033888fab1f2575a883b5375df1aaff56c54c689bea5aceb29e5eadb308388f754eef1db62a1d3a57cd28ce2f4c86370b251959491a1e7a22bfc11d27309300cfd1681ac85e1aed0ace3a6b95fb2233d3aa978e5fa36810e54288a90bdaa8af909567644f98140c1efd3b2cab9df5579d32aeccd4b60857f33c9ea61fc45a4e0b64d7cb8502198c464b8e0c7cd69b9c23d57afb9d700e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ae6c3e889451a4bc57acae3d602ded812774adc4dacf156eb3a46987d19d2126","proof":"bcfd22c73ddaf2c6358910ab4f49730a7455fb0d730c86619e8b0f0cb6377d0b7e69054a2e03b633fa2f076ebcd9e830a71bbeec38af580743ea2126bc73ee68f8b66d098ef4b1c84aeca4dcc20ee77b8415d8c5cdbcc5045f4ec6c313b11176d6b6b991523c9fe8e469b4da9ab25f4278b2237078497ece31b62c24c347ec06eb57f533a68d27eb121621e3ebb32a5cb074bf8fce58ec2713012f615a521c0f2e7716221d7d62ec617f2c1c597a0936be1642e7e37fcf6dc0a60f6bdf7e3a05022f8c2e69ef267f5adabb880672386cfe8d7d38d413c0a33e359b769703f1029601204947bfc0f588eec69249c574164d37a4d77421d0913c57615f444d805540f1d8ea0c2ac15dc19ba824642bb8905de562dec255064f5f5c3c3fc7c69449dc4469fffd5e835d0276232054b5cf307a9393c276fbc6ea07bd2605a61e8e5fcaf446ce4693baefa3742483fb32a7a469f3b72b17eaad43ecdc8f39b4177040041fb808a6dbbab29a4bd7d66fe74e6e569ec04d9592953987c2210f3e9b3a313eb63b479b244aec2aa01aab7f65221574e2c0490824b0d0456f671ae03afd1ae0c918e974980b7abae6cb15aa174eaf60ea67d52aeea80050ba19c1c285e85c4cb303a31eb1f3f7d722915a3be4c37fd2f8539a2966e69b1a8cc121d3d43a5af008a7433ecf92b38a309b812bc4f80bb62e6936d06ef91703f3d7f029187926e25ce7c369225094be6894976300740f0228abde988bea251a2808902e719074fcf130b8f90a69ee83286f7c075845a1c9ad60cb6826e245d2f9c541b915d2001e27ab957cd4a204cbdacb8e504c4d8004c59abeadd9e534c64b127f8d76c34d6d025afdc71c43d8c47f8c54f5a2a1046046ac3df666e174066729ca25a9660c13932a5494022a94a7f6ca8efeccec461f357aa802b8fbb2a9d9147ccded3f00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d4df8bfb3589e8b70ccce6eccf9fb2dd0b1152c57cb0e592c9e663c73f9a5a5a","proof":"0e90bff57dba2d225c0ed07ff081edfc61e4f60cea947b3847402f91f4de945f76872b242cf6d87009d171c345f95794cac58e1d68e5358a74e1094e431c5f410c401902702807111001f2a5a2e1eb1ab865e71bdae36ef4799be0ee9d554c7b8405d63ec1afcd4cc35fe8fef0e5423217e3398b9c0a01e6589a70e4709d5e5d364e8c475d73e2e541b5f9d70b528d5ff7057c5637aeee53652b504b8bad5800a955f17f6d300473aa532210b3a62a3299849bdadb86b9b050ea9da094516c0c5cba002909c53857280477887de69bf5438c3d9f8022ad73a0d3b7d14635330f285c8e2fd4ecdd03b3decfb1da953bae99b14500088fb2024642e5965688f24714fc67cf38b8feae1664fbfe099ed1c938bdb51e24d2654fdd120bbaff32267e22967f51ca04ae42ec01f7646215bfb012e5f477d6d7c80a52aac11f2c05d41dbe6ad24cde930618c6d9e08fcd786dfe11d84f0328f401e717027e7dc77c35250a525ebb3ea3296c49bf36e432db8912f112c4a638624bdd6ae6df298a313064a655b734e63d10cc381e8d803317d9ab3bb04276aceeb5dda2e54fd037f13e3f322691aad0130ab37f9c5d5987efd29c409ea41f97dbdb57a5e31b96d08ef93e123b3adc5b4dff9d6e8bbee1948e9013c1a85172242d74431c8bd1f29e0b5127f41d56d6f17733e94916effedb84c04deae8c1473803825ee8c96fc765502f189827d3b21bd4a020813c3cfe68dab9b48bc9b48185012ab50da105d1ad46d015e8d14840154ffd1f388b2957b6ba8023b58085a41a14ed2f7ee776db22fe192b8825527e84bdf585823c1389a2b36296cb466fa07f75aad0231bf1c3e7bc0f5b19c20b7a56b2e3b32429adc286d46effbccc79fa56ed82a3e14a03e9aa2ff8026895cbac754ea01ed1d5371849d2fe417c38550daf42832f67735a72c9e82809"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7aa34f9f6914631030641787186407065f9972ba2147f8052ce8a2d71ae1aa13","proof":"fe6d894080dbb247d0ff1c7f7b72cdc35fdfd6f759d8ed452045ecd92d83972266c9661f69c78108b0655f51ba23238b3b5bc5a617dd622741fffa4387a6c26ad4096a554bda181729e44ce88797f5cd82ad880d146069d2b2e1f881161d3c5f8c70d73be9ac0f6583dd110b5e650d31341015694bb722edda2da088c193c63b27a4d45a24a5e35a9207d6a6007edc0f233041361e144dbcd713afa2c421f0019b452e1ee5cf3f978876aa85f99b191e52e93bd2956dc56ccf3b78e9bc2a25011d5af56011a3e3a40f7e028886e27dcca65d9a04c47dde42798347e1d19ab401ca1e68f08daaf915edd123276d98cc57b550d5739d6578fb6230c6c6b31a6053b671e6e7a156062081e3286026b075dafc9d74529242bc4b737e84630a76d24b8842c3b6a7f66f04ee4dd2049848d3f009b1ee1ae4abda64d9537b92c74db80fc4fa53edda5da0a5de72c56e5721fef1cc91e97207b32632fb8612a499022a726e70716ee90960f47d87fe2d51ca2820c5f068c7b41baa4094421da1b0678c1a4a414b85179b06efcc26b289d6703f55fd3a3cfaba138a0e935c8f6aaae95d3ca20dc58f545f86cef1290ce20e2b3cf4c5b446ffdec19b29c229be7ef92dd14648bc311f3e43b66e914cc34ad8fc1ba9e115da6c3132541d7fb977c40ca1e30408965db855a0eddaba8e582f6b7fab6fca37eeeeef238b873ba7e47168a98837f8cdf9a920640f537e0320e1952dbe5f84ff10ee6e6f78a82fbbdb014cac362eba416dd7b4b1c080e9b8d9a22e94ca8a47ea2425d13e8fa659f321373b729222de9be68489633695aed271ce3639a8cfb4b63819aa5660bdc2162bd14f45fd57c10e02a3631cc3491b469bf9d2e543a85315357724bd0dfb53c806579b81da0163234d3625c455625b2fccf5663dbfeb05b82f0bf97db263f8f95111fbf2cf08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9ac339a369f83288cd59a1680247c63bd32d8172861930d600ed5e098090826f","proof":"da0046c06a2c78795010c0b0fb9398d73c3978515d5099df5fe641b1f5aad53620a21c522a7b0c942cc46216b446fed02084919b236146719ce9944219db530c4609f524033a6b54e39317898d4cedd0b8f5b08e4ee7c573ca0990d900c9862964fd9b9c962ea23b058102581e890b66ddd6563ac4bd9a852fbaf90b6a19a27829b7f0f6b1db23b0507fd81ef0eb95a84f2e121e2222ef827cc5b5f5919a6f062556babab455d020d42223cad9e88482fd7988c82ffca2cb39db2a06338ecf0dc1c4a9e33f2d17489c9e4d677f40316389d57885676abe6d523510365b93c808e0ae54902a9df34b3044df6f4a8634ffcc49c58b4311518cb6bdba2511ef150108c04420d6be4a88231f46bcc587c877102072ed2431e97b90ac1ebfa6b84c53a6b6f78ff8a1cc48e4434952b4795f8a5481a8aa75d55458e6c29b7f69b60f6ede2611936972ba684d2e4118dff021e7567053eb1bdef81e4c25b9bdf6004704022e591456fe1ee502ac6ee9ba4fecd5322cc60b008f50af171e77176a38ce4dc814be8e7f3e8e6c48c30e9663659f9dbd89e101d6562f2a792c1993b1c4e412f0fc7f2c960a7aabb8f266e256d5dcb13fc8cea52e7811e536fca5a260aad27e62d3045caa6740dbd611d1f43758b52d20e4174c8ce80a0a7440ce41d3f433297e13f512f076738104bdf38c620c5d16ae7a015c5971de5908cea70c1fcaad0ef0da56972282b4c8cc6a447b499f6a9db2d7bccfce98a171ff2c25e16346740b569a4075374542d800a2ecb0e2cb9b76d9e586db6efde4b7385a7b325698ec27c0a7acc212652a5a897c63265e1937004b73c2f5bcd523db058f019fed6e046f33fb402f04e7a6dc651d2cb693916955f447e732179efb6f954a7596ed184e0addbc41e206d5b436df8f9025de068a3a35f36361a0d4d4959d05ca63f2ec7d04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4c899ee8fe61cb4e51742816e33f96028615aae17b537475da164e8bacf55410","proof":"e4cec40fd3c39b678dec4b1063f926847245bef41b3998d8f48512c280fa174a7027bc7930419e3381b32ad6a0d3e17ebce82ebf46d1e8f0f2e1a49c18c6025be896d21f9eb9d61aef68efc6782ae4daa303db8af17a345b335ba8d027399f2bae07852ae2249e9236f4f6a9e7c26ccb21d519cae39353059e7fa04c89859f25cd1e9f2c521ac35f9a5bb692ed5364fd3e794f542e97e572dbd54e7e14256c01a9eb0f5e4c85b2a4dddbaf57682cf9ca3ed08cd2b7f2a6f1217cb7d3fb63ad0383d93eca6127e7a78f07e8caedd049b2e5b42167aa0330ae7f152bba87761a02b4aba4eccfdc9bd7066577e56ef66db5ef99a01c9930c081bd00bf4f244bef28eae7a1c6525f0e9744e9525afbf89a67636657b415acab59345ec0759bdab810585347cb04f097a4de6ef977ac51092939c36453b125e7ead4aa04207ca7b714d432d0048b0b42a5e0d0f78de01e621f866dcd4bb939e31f6a808539202beb2a844b68e29c85d5fc33d29cec97cceda9cfb70a7a0817ebcc58646d9d2ae92b057c30934a53001c5b38e1b8cff71cc01641519cf9a280236754617dd4b7870526a4c3230685431744e901df5e10ffc2a9a0a137163cf2784e9d98e36287ed7972f810c1e381950d8df714ce94f5759c3021f56e42c8ccb6a1fd3f43deede1f16a98bbf13c4310573754ee5347c2c574085a4d39165c0d826950667fd5794a7979baae872cb3d900111b25bc10d96aca0dcb73eea6617456365707e5889cc498072a89a195895e4a140e25370ec7e5ded105ca8796f94f682deb800e158bc726130870452c5f663cd493506ebab030b199bb0cf762f56bbe4bed56108f0ce40c167ec825b06aeab1bd071b2dc6569f29b1e032943af23353b7d2c938a63f59b4015ee1b2c896631489e27cb67d4e583435d904f722ecb429d54e342679cde85802"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"146f23b7a5fea366e588dc49054d256cc074f4f7dd05d2ddd8106617080aaa32","proof":"acbeee5497c8b68ecfa44705373050deb068bb4345b3ab3368c35724091ef15a7e7bc78bcfe2fe187c6dcb641032bfed84b8bef75cd2567c7c4b6658237a2c60300a8a0849d5791c16517472309fa499a2732d72ecd719f3b730b61dd302d77e120045fbab493291d9b5712d95a72aa2069af6642d1bce4939109d376f5cfb6ab398610e1a235055fdf5bc35de9699973c2d90d68421e963c02a0a83f8a3660877762f737a47f2cadba179796236e60270e986cbf3254ae7b0033c44ce84c00916100fc1e56eb46708c572447e4ce737cc70c487b2b14f89bdc76993ede8c706d20e5e507db5ce2aaae65206fb073a911a5dfa40c7afce076afcef50bd8bae68fc0382fbc15884afcb6a9598ecd9280c5cd19d706a4dab7aa2b0e9270149e5553c0a031275fad89e011dd2793c438058b5094d1a907207c5215fe601ae69f56882d81908a0558fa59fcccca921f64dd0f66bb9f7b9aa9ddc44b8b85f4981b41c643829ba181734e7f7ed2863d4352b28bc477dab321ff1effcbd34431cc1525492a585b2356fe2dd48153aeeea502380360410abb8b72a0d613ccfd637c900289ecb3e9e53b99fdb0d4ad4bc371ce4b2ce93c9f6f970efad7814ac9eb3f53d6bfc06eae70500e699371ad7692a16f4f794bb501ec055382b9b344449449cd86e229edc8b78dbe173bb0a90c2b3142a71f0a7d25cf7b16b74c5d0338dd1e80952b8c262e87d922585c0d3d8e451ad6b68c9f8faf120aafa9ba097378aef25b6410053c631649a10b90e98c16fe678f7e4cd66d47b60ed609719392edfbf409418c8d465bd71afc033498edc15de66fe6eab897ba65b4849b4aec29800758d2e524e915f722ffe213523fd7b2fc51266f5ce64f1dae87ff4e472bf16811a1b2c03ce0d24d57607701269800207fc5e6a9c40c17ebc585d5b1cfd80ae55f1429c06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"721c6285112b0507313cd3e1486513d3892480db85ad2db208f51c30bf936853","proof":"22724f3090a236eba7c3cf7ca254ef7f09312f9fe88e042b41245991239e071c627f6b72d2d31f89e55ca161f1ca064db16c2eb1b692151257359bc04950fc0946deb858e0d6c42774eee70016b5ab7ac7e3cd492ae33ec3c2681fcd60815934100c7924819c051190de2a1f3e5ee7251d66661f69884ded85be86dae0b03915d6a8b27fb33298440952f9a85287157b941346d5d4a4fbb7d1871766048baa0b8a38059fd0ae2f34446f4cd732d86e74b65aa869abeb92d6ed9b8732ee86270d69c8d67fd15ced0d63214658f618c642ea928279784666ac3366e861e8975a06b8e09c49d6b68469eb405700f773ded43b53c314ec4ae6ce8563e2f71778512cac138ba7b169b0e48c7dc14700a9ba15dba90d631ea5b3017f3500d5a7557278e848adfcd6ac7444b7887af37cb4f218fbfc3cb83e0124863e3f7065b553ec2e24c2c0edb10d9ed065fef3d5aac15d4a8d4c732103f3ac63b4b8ce41ec08b42bf05f43a08b27dad555e65f3177fa56d6401adba9a9addfdba21c8677b9dfd4382a76baef730f2354409092d69fa5840cefbd1eeff2f452b304cf060dcb07987e2af94c0d1d48a2e59b0c3aace33f839653e6881a1b92e0f20a86210c59553a1bae49f8a5ff76601a67b4c889b294556404d187d58ef56bedc359cd1c538b9d555cc15aa640ee0cd7328e20d9ad1744a5bf6e6c054af51651f5b98ae45309f459d06b7042e24e8eec799d63f8dd463c0ee5c7ac5dee38ef62484a9608b738ba5382d9d62c472850eefb54745ee305e8092f3fee108c67bedcf6bdd5c0d4aeee29722d5a9fe208125eee4660451bf5751b14648185aa0944fdf52458fadf67590acdd2f4d8f01f49eb45ca681e67f0e98605a1f5fded0447838db48fff6b4024074284b9539885531139e7de82e20130bdf9e5888ddef7fdc02a872b7950d0be0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"90d6036f9ca00787c60d2028a667803593958dcd9b4b6a9caab36fbaaaffb80d","proof":"e6c4c8dac2fcb93e1e3bab67df021d4ecbf3291cf47f3d34cf89efd4fd1c8333eeb5359a891646ad16c9bf72026470ea0ed3922bac1913ebf53a64774396de34ccf5b90d213ba31371bedf2838b19fcffd7680ed1c71563045e3a3ead418a9726cd40ca163e79a56d1cd9852c2a1a49be024df19d926bce54dedecbc4d6854796ceeb9e9cf6a1ecd05abe25ff020e364c097165d95f0d0dda0841bde3435c60c3b8977bd8e08978083de7cccd63a0a9de7af0d82fce355c5aa9734d5e967b3006ab7d909573761f4dc69d05058b4fd360b7d2b8f976a21c46bd8de26f8bb91010e538073d7aa0418e5a5e437f8dd6ace9eef466d9da31d34c4153b8d94988c6828346219677b29efb503cf5faa1c74243bd600f299661fdd370f3ec5773e1c2464eabd1fa7c4115dd05359fc24acaedc7bddbc2e3bdcc4ed3fd143d8ece4cf23ce6585668f9182e97af6839260ed297cb1ab799679ba85ad860dac7f8b23615d3a78e05eec489017a5ab1e8410f507e7c183075d40c92411ed198753766ae0644cd4952fc4117d6b30c204b26293775a20433589c9a2078c61d947bb670a950e4e4945d7ee433cf845b1c560e870e238110b00f89fae0ad04818b8bd3f982f5694a536ef75c86e5ca21ac9c33c591383314bf327a08a6b7487b70fc63b78fd533cbeb8350ffd233bcf7ac15c0b59fe3d25c09658f289533fa125dceb6561bb22261d35ffd04df42efe82d92c98c816390e3fde24575a13e97335d576fafb7b54aa1d44dbcbee55ac9f661c0af447e9beb8cbe774dbff864414a4141a023ada15b02d14357fdfdeb92e0053d13299cbdea5fe5dbb973696102635eba4bbb8302e8b0a11832ef795250777895d347cfda6f1661928f0caa0649afc92d56988a909e4afee7386c22d19f2f52484dbb35f676e681111cf3cf41b47fa405b1374ee0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5afa2014d2094d11ce08363567d096e0ceed9a4324e25eadee9ec6746320766c","proof":"ea9fc4eaf8a7f8b644b65b07dcccae06c831e9b28a3c78c651151e8dea89510788f451e16cc13f2946c55dc43667798d5b3a54aa75bd4298c88b674e6b7ab6419a42210b374cf43a5283f743a8c1e84c10dcc8f153801bb2275e735719fd4d5f96f418b9238c4f6ee15677ba4c3c40ca92b167e2530045e711d5afdb0fbb4f2f9b6e843e1a77abaf184950d75f9ece5dfe10501d2b49bf771202a8d1a06a1b0567ab7b8458a30ecf842c74dc72de4a88fe4e851cbf8d0d38e80ec07c8b55660f1949bcd56de807963c8b1d7cd8a2b50898e4ec9b003ba9c10c3d4e57f233dc0040ab0156780b9913aa663e08d9daba5be36df42be8c75b1ac0771a2ca0090c70227aad53cea8532c22b9aba378d1dd17a836d3603fc6348fb2ddc8f484bf6c03cccdbbe0b28a481467bf85293cebae48c349b75adbfee1d8f29cd4b04a887b2ed0d266f6a6c9bc2f20d2079f6f8cf967a446290013a0b2858956c49f5273f67be25d037cac543f2172f6739ac218c77fa8cd37227c9be79643c68368820d5761e2d9d54e87b7806104ac904e44c35d98969255ad7344388778e5d6b70ac30509885a103c257ae54b57f538b76cb50be9446fbf93a6ae583e7b426057ab192c14942e1d72be16476eb8d3e530579ed3cca10f236582887663d06ca9f4c8b26307d4c3ad88b67efae057fc0e4813c154f972e61e3ca8a50618328263088de810681c9941bc57d078d719af2b3a6826b684097ba70f0ff7d6e653a87365da57d144b814c8c208ac5f693c0a75d92a18abcdc2f43c135e52b0bd463bff571c2bd54c84be4a864af31ab20d69b6915b0477f8a3ac7f8e3ef4debe2c8046a03494dc14ec127e34a7f9d6ba15f2183347ed895aada6660ee8c46f84337ce753f58a2e0f8baf21aa616638bf512adbda89fdab1f17628f505b24c37327f6f7110d884408"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6ec6c5401698aa089b8eac24b85c09170c2521effea42849357af35b8e995639","proof":"98b7ed698dc1aaef4f080f320e8bb0b58b6326fc2daa6b3ad20e9822b61df61d5ae4d236843effa010aa3fb5ba7f7e485908815b7729abf9d93adfdee9f5884afc4ae73785b6a9f1c1f1cc74d6cd00283a7830b824a297ac875a16b7c31d4500b0c8c66b3eb15e3247cbc73228993c89f21b5e9c0ea2b46ac535587760f0367ae7453e8aeb26464f4702621108e45b0b8f1d7aed6675da9d61f2f291073fa70a1efd25013263f26d95f7197bcca2eb30a7dd90d1141595af8148bb116ea2620348d56f09602f825db726c2386135a3449bfd3a65b596f191254259e48fde5b084ed1fbac6cc9bc45952b66c970a3e01951331b929e25655dead263df839e6e080c8ba0fff7206febd7dbfb22f03e13bf00412f1a702572287ef608a8153264274a870bdd740910ebe79e28ec2861b957d03052baada6881f9eddd04d6aa4216134fbedf2b36cd3c2b4e0736bc665d705b9eaf4bf6f8e986582dcaf98444ad52da83ef6d29908dec89c20cf6cf04ecbba23cc6b05bfb3793b2163620173211f0546c52662a38163c1b9733c7465a413cc1a986b2b0685cb092850f6b79139b5630a6c19d822452c5169293cf9d7d528946307e6772bd49f6a3ae5c604eef3365a009cda15b0e38455b09c54a976fd0372d0cda7a73f33f12d867437d65eafe36c70100359e6575b508815e67d23a004f5a17dcf9323da92bfbfec0f6c4aba265cfa48205c1b771cafa8566860c528c4094c653576e4bf3f8041409b6ef1d4ed6b3479078d21645ea01dd0fa6f6f862b7a2d82e7e2918fd93ad3f2608c1a1db748b48e2f9461970a9192af470b66bea8837a5fc445195cad8771123196441691226f7dc44c3305c0e3185357aff18dd8c006db3ef068b107b98e8429032a31bb06c5a2977c2fd11f4b7281787bc74ceb77f3ffb4fe3253c27f8a7f81bd3cde4e06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4e72456d027914310de45303ab3c6fb1f1b3f860b7ec591551fbd292df57ab2e","proof":"56aeb9e2884f704fb0a2b9a5954f752f41863de48d96f35e894b0641331b553844c9b6288766ff55a55b591ff2fd1cac8b053bbafe9a76df55e7ff9b8a516442e4c62a5f80cfb59bacfd678a18386f14e97c5de14f81b5fa5866bc46117a7439fa55f7a413552c54fb02258466e96f0ab35692c5d3cc6a090b6061801b52b93391711a35449a738f32a4abc3a796c145190e98c294c16e493d9f62a4e2e8a20665cd32d8708806c31639059c1553c992223c7a607e0fa44d44ea52c121d6f30f311e3a4d08c9a73c828239d571682d8429e58b2f58a45da8f91a527fbbce7d0fc64f87979c3c069ed4a78933604c9bb82441be4bce5c614f7fb97b0d4513287f0cb0e92e54de634e178940fb3b597d7594bafca9c707110b99e39a7c380ffa443607ca7402653885d6ce258777b61a8a097836b7d3b0e28242b50d305a05dc581ab118bd741e6a812e9783058febc04737749baea2a3acc830520dae2f032a41e64dedecf287ab7d2fb7f5611491c20b40207bb4fba9f90ae395c85e8a5bd83798e904f0d9ec165ead11e50bd821a0556fa83f5d25c5217ed904789fbf18fa388e14f7bd309c75f44a2a0fa18ff173659432178112a6d66f93feecb92e0cb039aec130dce90ad0e2cb19676180fa502fae141069f976a015ca010a87ee34f6358ceca0ba97bc6af315eee3aca203011c721152065dd97f5776de1b978570b751bac26a1a395e1b63ef26358139fbe9d68fbe1d95df2233c1664e5df1274450798e8730b6649b18576d10dbdc66d16ee8bea4d893dae9df6b93dff7ce4f55f4108ab0bd9f5818f8743540ab756ebe11473f061589b2126d1259e237cf23c7650f92ceebf45eeb374e74cb133a90777f846d4801abf4c0d064dc6e71f5d85e710260f3f950268affefcbe4801cbf59beca168f05faa7d9098cf363375f8fd6b806"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"26fe8d77454d9168b8bafc57df07d647e42672c2fd93259ca652bca4f5172011","proof":"465583969ab482eb61dc5fd5c811520db04059cc5895d52c5a0e5764a8e018799cd50a13e69f12b9a77f8efe5accbf6c1b4e1ac05431f3b4e97d75dbdd04a81a8a91e893875ebcfbc32fe53236f47f298109720b2f390dc1d4870068defccb1c54e6e36ea1461198abf39e55e675f8b7b0aa0c2b89612515d21051a96390e330cfaf3bbce0e449a21c29caa52ef123e7be5e5003b3151feb7493b4df8a257d009f774e585e1ade852daaa365e65b7030f989895953247a1eefb308d80ac8c6049b933f1a9c4e687822774e09846adf163e8f3123d669971750ba9da67767f10104345663c7dd7c8e843ec275d36474f478b57b98da3f60180fa5a7098f06cf78defa7e89a90ea5f71630ce37b252543f7c3200497d73cb4061955cf0d78e070bceba56a801eb2d544b8ba341a1d848eb57514f95e33e9bf2f80d22bfe36b1c12e6a862489a0a0af2e3b06cc59d107f8bbf019ee26a4b461b44c86f6b3f967f365e1ad7bda141e7cf0c544da4323102163fbfe8fd5eaffca8921c8724e7189d74500071fa76fe8b9f4a750319dfe2cce12be47f9e70aceb3bf7c7539e5c86f244deca229132a860e824209a8eb8b66cafa24645784e5a91101e2808fafbabf730c6464d38059542415cf7fad2e98d47b0b12682a9844d6bbd49a62f945c6e060792415cde7a9d8478df887789d3404e82f6b3acb15bef8d9f4341881cb0a4a305c4aebf09e284433f82e95e71a126331810bfaf6f853e0981f2395ebfad2629148afb8eb8413a04a65d4c19d2ed153f67f5ad79618be5bb2414836090a5542936384e80d969f647164cc0bdae2ae7cdd3a9caca13921f914abef886b9658a067b629f444408b207b6127ce11b15e6544ce4d26aff3511cf1c70de22c757b0d20089ce07226aef1488255cb2581f9b2a789976ae26bdd7982cfc3203a2208ad90c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"081813bb3976dc126c1b4d706db266615ffd1c477f5d966c2ca172aa56fe2355","proof":"a6171211870f3e0d74ee8b28d0f2e45bdda56cfce4fdd8b8d0a4666af422bb4bf283f789283368204e68ece88de555caef15373b0ab8a2fbfbccfe038f16fc0466d9928bd0d712af1eb03d511782fae365a476d97220ab123435bfad56379e4ebc25436464e9c7b5111c8d64fa0be26bbc4a17b0a5e3c5545ad44962facbf50dd7c6103421f3aef2a3c3baaf3dc77f2085f8b35e70cf7c41910166f1a1786e006656efc75cb8e4e9263822d1d7c7a6210ebaa70797e4e8a9a14795bf01dfbd0b88f04400b7ecce969e987cf240ea9c3d5c1a2e083957ab3ebc4a941c7534950e9840f140ab7cb65a7620390e309b9e435fc9e6d88e1954bf2776b5aa88d42c5a8025728b38e943339df21df6b617be3ff1b58142efc1802547857ee36323b12b0a30c9f2447b8eed145af1b25e031c18e21eeb89e23e7fb302400fd1165ffa0af6a37e090d78134f137873f29ba7470761285f1d0caeaf133fb8fe17989c50486c0a3e24dacdb396f62b629c1979bbe050a2d01a65732c7e750e616bba035d3d8cf1d9bfcf0d33214c33b0495e1b46f41adb03d56efbb75974e582d8db59b84cfa425999b774028332b971f92e15f482b8fca386a48704316ed11038c6f2fa148aa8f3dcf01d33f14b4b0945a14462f31c4c316c6ec0bc9279802a9b1ec0db60c20633e3607f63c11e8df45b6a3d991594eec59b9c59ddfe58f08ecb5839ef114801bc59ebb56b9e2c61f0113549c6ab1cbfbb9836d36557db4c36d5b569c0038059216f07ff3a5f5f072d850449df074d61ebbe751ea8dd4c72c06b07ed542562147bbc46a73b54d2b025c967bf2b7e7e09ccaaba12f43cf8b0f2c9f280be2eaa81add87cda3b5f01e738ae7542cdc8efe287dcb4a20af8f7d5555abb842603acb7395af4581b075b8b321334e4c0d163a2c50b0f5068893a36fd012a4cd80d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b6c4bb2f1758e1f82f17d6a454feb5d5279865f9a3dedbfcaaef3c6cfa915466","proof":"02972a9c8e7678f7c5b3861412287516dd6d7c56cda296be86d25e8683ac796e2c7f7c181ba59cec6a8f2234728ddd204c90d85c7dd0317aaec4e64933642872cc1512fb2fb8e5b8ed7c2dbd8c1f8be9bb7d3f9032cb90ad822fe244fec07555e872ea1ee777fcca1298e6b8d1ceaa0cc3957a815cf925921b7cdcdbb95fd3291db403662f51b9fab88b13b4d33516b2f948b4046383c57b6e760804ee8a790f685af3b908d56f7061bb4e58a55fc308fb19c7aed6d0ab571742a07ad83f6c09f18cd3a30297571bb256d040adb6f36cae4306dd145c4efdc72c356c62ef8f039e9bfac57ad8c5fbec1a1ab943041d1322c8dad967c85cdd248a781f0a0b9268cef80562f538d30b854cd4dd16bf27cfd42fcde7c171c418953739394f7dd04a9e902db924f798c445f02e2689552af111d0d8c247ebcfa6c04939047d9f6735c48e1b4137b602943870d8a43f1df43d142707566f3189eabdc3145df645210f26dd380441ab5a5635d2c57fdd174d1686bb4f91bcdd75da8e7dd90a1af8c66c62def2a1606f3d9216336c1d4a6fe57faa24ff3ab2fd333bce6abe7ab29d3d6e5c41643ce54167c2e3795a9d25763cb05feea4442e391b08a123b3f2e45c8c44e87189dbf6f312c9891861855c9ba424c29587b39d9e4663b955da50c5aa9a47c64617c0ade2560597d3ecc98fc75f6f2cc9fd3388e85e922877a3a1965e1d30e4b12a8b8e4853c011c2223ca218e0ce4bbc3f3bd0d091c442801a83e60c924150f1b3469c1e5e38fbec845cb8edb37a2137cffdc0061d3e5a5d4e9466c2fd52dca5fec7fb11a54c7118acc018fd53e4371fb5f3b8eb0eb706564fbf479da4778b9d85d38e1e022a662137f3c51759dab1fe67a6a9e2f1653c437aa3aa52af0bc52db399ca746bc7ee86a1d8e7f90ef26cc5e12d2c4771f7a470de1f73ae1b0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5077be0ea31d19a3a0ca7009d81f2e8ec71a51454c09cad78c39c8386620ed38","proof":"bc6a21cc47dd38834273f3067c411103bfdd1a6d73034bb3ddf171a3566eec65849cd5bdf397014cd62e310a097acbcd8fec51264ca116378db96121316bb464a0845396754207850c89068fc11ae104104861e444e8b5d8dc308a53a82a6a5d3272bf1463acd82a2f2152e4e4cbe9ef785bb46838bbb2c9b31670d40027156f33b3a42fe6d6a1a2d4cf2e7b132486defe32427d29e55913118b67ee14619006fa3a2225325c5d887b6827d3161100000131dff47a55c8f54bee4713edf0ad09105e7a1965ddfd784418cdfbb9b90e5edc6e3f415ff62fbd79a0d5151c631c0c2ea12d4ebc25a27ac56831ffaa9746b3d8b2775e39268f8eb993fcd36a20ac441e29ce8fb4b3cd25b390076e942c227e597d22806f05cc025f377ff30db89628d2f0d42b28c4cd3d86fe9d4e2024987794857dc2ca9878415a47d2d799b65d7f6636ef14f4670daac4e7bfdd2ab2705a404e3d4bcb4a10dd7a3faa3d059ec85028a75ae5376bbe2b3f68ff1c88af8b3b4789b42027bd6f439d1d3dbc5e97d630ce3fd1c333adcaf5808eb9a604bafc2788184ed6a8f4e0e669e016a386dfc9532869ea14102c12b382ed146f78443d803a9bb9a3676a3d226d06a68cc266545d2625de8421902b6f0a1b13431ba7c9bfd2978ded0e9338c5315b8c8abfcfe51824d98c4698d1b98a1fb11123cd24a9f8c9d36116da7020d7c643fc0410a86f43fe664f572a71770bfc0039961e081971ac071f5a827a6dc3b87219e35d77601a52c3d20ae1b0df9d0c0f4b402f2a3c53fe98dfb158cacd9c21a89f8c54989b50da6312eea929d707d178d3c110fc172eb981721c96fe190fda2b084de2e8855eb9e52344e867be036cbe1d114a8f202972d61c9a2efc767ca8413e813f900100dee265901e06f7a506574e6a56906a4ed1c1f75f1cc2c0d7cc2a92cc870ba30e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ce2053e3cc9471a6584cffffbfaf8dd88965b5a5335f2fb904782fd9d03d575e","proof":"0c3f8a8627bd65dc6de041868703491c0f68ae06f374fc629b1380edefe89d6e501f3ed553b8313492fa29a43bb383f9ee90342542aa9698e260bd8b1a3f507ea436287c3c3e3725230011643eb31d510c9b693c46ceb43e00611f6ca79d0d644ce975c484d569b041b3a7b6564f559f3df786bb511089a5d6115b09d2ccc945e7d8fdbb8402c7bfd6823e4c1da6b203945bc704b3b6c1bd4b3f883e4869dd0634e800c2a26fe09e200b677e3531b635974828f3fa1ce071d05b2a08fda5900d7c504b37da732c76d07b1b831d42e18e740b16c2c7902c0beda5f93b78f5cc0900a7449a3f703d0d225ca2d30103f89e594fb385fd46037e293f8c2a1b0b4f590c5dae8fed0b6b0ecb9a94fe52dd56f2dd92e48646d902979713d921c8322b679ae5207196d26b24dde311fcbb9cb52c0632fa351c816b4721de6b72691b700a0c0f9be5565f759a781a9bc3fc79bd5b775af97f3fb6254cc727ec7d3da1e32b28be16cf60ff29f6597c229123386c572f7eab385d537430843b70ab6d97c23990be33069601d74fa681765a528d3365d79e11825a06b81289f386312335a55632769f8126d72f16f42b405607780745fb25e9dc8d9e679d0820bb05b0383817e0cdc1e65913fe489909e1bc1fcdf6013ccf2d32da9e31a86e29b0b60cc19a08fe50e63e57805d8e2a6ddde78a5c7d455513845ba2cb3d5eea33b5cc94ca23554886bd0a0c558e9913ddab114850db4ed777198bffca6ca6cb2dfd1550408e410ad9d89f46fc52c759586a4a6433b1b80b96786bcfd1ea50e2492a3cc41d895c1c6a2b146fce377e6b29f546cbb398da84b9af412719872396d928a033d61372af652b9bb2ce502347d25fc359fd1d4cc216f87071d935f49703a2654e25c305b9d0fd090869cac7da2475521d4a6333bc7172311419c485ff4aaae02896e300"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e0f34846f8d67944f9a91d1e65027c3905912e43e547afe9b2c4fd283ba5f16d","proof":"a8ea7f34b3981ac6af295bd3b46d4a826a782ac3bc89fc7a25213294e4cba03cce1e57ae8d06e8a50934fae4612bc67ee4b26505e5b0150f253d54eb1b7e4b6498f969b169a5193a221941d1905053a51fd68942b4fd043e67ce9f78ba0a64620c0117e3a1245540317f33218720cf974238ab48f880485b7f5e2ab59c5e377325c5011b0f1ec55c37d0ce61bcde5adc5f6c913e9c2f2734e24a472fbf85f60b298cd66bbefe45157e42448909d3165e677c50d0b88d2fc109276ab29abe8404b09449642f13d037fe5f12eb4ae8435a471b6d8c62bf8f516a8f03d8d1bbe4037cabe0ab8ce1758647e2d9bba2a8afc32901dba57cd6d1497ed6ea02b9022120a60e7fd46bf97933a4c962a1858b6a6879f7e9849256f5162dd3b16c89d01764c8a895dfcb5c22c5d40f6d2c18506a3c0771f6f63a215592686769711105f42dc6a43b7f6e0191e8e0cf6b62b28ae0e21e99c806b6c8bcf5d731030f82f553760621218d50993173fc9c439fc651857eea276079ab4fd57a54da4f7ba40de075f45a8cf8740cba317eeb92abbc40917e6d05c0d7cc87e954a4f5b83b55e448310cb848a38148c6a074301d114b6138625bf8ff2d0695c2cd25463518f0ac3c50641883825541cc87656c7ef5ccf47e72cb31fc3d964d4a80c1313c5da1a7b14818b1d6d2468baf526e044ea3ab17556c8d18f5b57f87fddc7e5bb7f1c88e5f5e2e1c140d393cd63aa7e51e18d976cae0b492a20003a7548ad3ee3ea7abd9293a32034674c832db1161a7eed7c1b6654d4277b9c14d63caf24664607d8b721b7562ce2d406c4105316989749bf0135b07ed12796db54f7b2a5d6c44791410b47e15b0612c502d7c60365cc5b56be34bad316820726197ac244af19ad152d2a1059d80d21ec84b6ec1bdc44184b598988b968e7ad3a699f4f6a21ae702b86f3d0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b4c5599b526b5d7b59c195e6d7ebd673130e32a4c18a94e896efc70a4fc12375","proof":"0625ef1f8f0935edae21855bfdcc4fe1458cc21c7bd9c417de997667dc44f04392a4651cb3af7dd13c3bfb63a31ac0270820ac373e9c405011ba4ae976656e55503e120c4b3ed04c5146c6c7881691ed2291d13e2a56d63ca2c7a5d01fac3d7d2c80384a94d1c1477234f0bb8d8347eccd1bcd41485b4592d4eb06fa84c957144f50cd347279fbd1c9bc996948ceb5aa804ea23be9a0efeed9f1ab3192057d0eebcd7c1172a75b8abfbb1d76be1573152e4b03dd6adce7499121fbcb670fb90bc84f82683473d946bb7bb37414e04c6905de195b416e99bb318356b00e11390e004416dd3057d2918102be9d1cae55dd81a19745fa209711b2c7d27b15c25a1566b66b58adfdd0bf5d538a27ba627f4ca34cbbcd419aee196ca78f62825e813a1ad7446d9726e41331ad1ac2c7a8005c52046f6519fdbe0d9f8b667532d98d69d22d9a1512261c7e6e0a6611cdbd0c320e1e775696cf0a60a3189d53b442893fc42c385a927f5f86b9daabc795f7a6ea11703a7d534227347d677ff870e76753aad38824a74691ff43841acd8c4baf2030d70fa086492e5d9e5807a5affed70a92a8d83200934031a5fe6f64a54f703a83e7626d44c0696446877eea2d6cc059289bdacbeb92b0e2464739f5277fedb935b9d4b46bf90edcaec40c2e0d77864fc45facf869495fd4fbe3d595d221ecf59e9d6025af9bea50bfc07164291b61068c1fae4a90be051449d600c7e5c2e33ebb6357ac8e2aca86d3a4ba32904e3c6b6e1b4fd6154119ab8e3f52b51e689a80c6459b1a32699173b219a75e746b062c147e39a6474ce9a84c8083430733f71062b95fcd44116ed9398a67c7a188416bdcde3a0b532d75e365fc088099a32d316cb34a2cfd7b7406198eb380e9f6ea07b486e6e5cf521784cd07610377289f8042df75f49d01873320051636cbf99407"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f280a1ede7bfb1131d9b2e8878cbbb6ff6fb48bd55f0b6ffc30d4ba2733f3608","proof":"6ae444fc81ecf2d3f19242a908b721980ea59d7847f3a313d6d8135914af484684964152cdb2e4c15a76d7fa02fd5d14d9e469a84a8f34e02b6fb964a2ab6f4e14576536a0e1d49d435c5f16fd9134f7478f6defd2a6b711a01d51b5d56fc03ba85606c39679bd6296869374b251d5198fced47f1ecdddd506d488ac3ff9c256b0ab3c038a30b730a1527c58b65b0c9c2793b95f50cc6d1afd397df46049880646c7a51dc5f7f0d65404aff0ada542411c7abec505d3721d0260df5fd8182f07f80e274705c64d5e99756782e47677328940040414daccbb1e6d9d34bd7fd5093ea26c67e53e7d859e51641f33d460410b496a38f0ef689371ea32f11ed201673c7746fd4895be043aa39a04b634d463841926ba50047ad3fbb26c87463216423e0b763b607b05214cb2f6efc1101cbd5ac82d33cb5cf2ec363110bf22bb77792aa4ac3888230d4c3bf3cf0fe2fc730b1af9ebba8abed9b5292643380e93f35a584e3c19e4400a2c42db0c83b0120846b2f6603a047a641feb30b8499071235aec481683b380256b4f8f69a1d8a895ff56e41cb27aac02ca5127d00d1957f84d38bfaee80e83b3af4a5bd54e343c95bf0586a3832616592e907864efe246925762c1f579ff6e9efe85d192f14891c752fb02073a3d61275ec20c79f6a8581574def809956133ef8fdbf94ddedf80000db16583a438dc227d2c6d86344cee346916ed481dfb61ce63337ea3e4a4f24d87a869dbbe6df2479d000124db25de111336f218d915494664882749fa8618d7563fef8126cfa914348b9f307132e45e5446aed0cb6f73d92c3f73ac0d7cb7e1efd5a42bd5a6758e752fd2e39c16a7b21d2b746bf4aa8024e07c1b30e6558d8988257d5fbe4bbb91dbecc52ee3c17ad10a3154fe14293fcdb60792f03c2463e5f2224a6a15e0c499e884bd80f25d1e130d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"02105f043fce8a7272a626020be952d575778a3ec9221c908bd43ad4753fc638","proof":"be0f08a0cb8c8262bd027000ff767c2909d27d1e82aa170fa6d6e26ebff21a771e1db881a7e6aa687f1830ab4de9c09930e7192602e3eae4990681bab738351cfeb52ae7932ca4aaef4a221dde12ec8840e0177e581058252f6f749a425ee327fead86487f1d3842876a987ef6b6ca05e9d39c8dcabf6c951b5b093f8fea725a2da68ce61ac2aca6159ac2b2a22c05d377d370ec9fd9f9b53dde96285d4843029005b458e4a895633db514a9f4eead0c601865507a1a10508129aaf234117b0b0e04066b9d709c5c5220762440709d29c8f74b2c4832c46e23ae0fc3c12f7709b072ddf076a263fae5380a8486e5c0866582346b7c6113c0761b0dee0ea38e36d47a8408aa7dcda491471070cd0694edc90226fcd6678675d2a379180e60c14cb85f20b53bf02a074fb867a33dddf0be24be3b64592b3bd60fb37af828b2e47c8e62a927a4c97366ee15102cc130cab21a279151fad5f947d74cba1fc0bfec359a53091dc3c2f18c5c5c561cbd896b7602087b2deb79857dacac1dcce60b6345145f66361134246203b94165f853780964335340acbb013a3a91d900892ac0645831853af6369ad6a955b71943f929e6bec67a024ad2c5ac3d21125405c47344a8e6eb054c23070d6305de075ded21687346f4ad6f5f0303044be32f321018457467d795f2427bc04a466890ba148943ec24cf0a9a6cb1dabb33788a4a3649410ed54f9bc2556be3f1b87335cac5f6b734e5ad2f1d01f6eb963e0fdf316599658a07e8313b915805a3db1e62f5f248587b9208129ee2798891cb4e2499b3735158ea64d2eb44ab6ad3c20e086c28776dbeae50e822e610a0f2a2b61b5491896c690ead39b81b53df43742304b455ff710b8e84a052e629a55b62e15cba1ecb01544044ed8379a7dcb733806cc2f71ec4f8c42aee8c85b8ac2ae3f1aae2ca1a06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7a37831c35dbd0a00bb3c3892e8ce15488214d0a54c3590503be57dc5b12586e","proof":"4efadedc3137868808ffaed9957cebb293e3f8fd8c01763c144bc98ecc8cf03a062ff9ed419b54d5ac351582b9c12f5e78baefca3dfba1a55e3c5e722b41f468d819cd2d3e20f9d5feb0fa23cf92024c2fe84044d3b3c138f4aa9925c369ce77fcbfeefe6992a83887292b7bc12fa9a13d044698cc69313d779656eb93df092d191c876e5e40db151ee26aaa6c70a8217cfb431c194675ccb82c43bb668489011e8b997f6f02a31c8e06c7fed18de1cc496dc275291d421a7bbb926d37fdde0aca37b4de8dd296e04907c7c08a682d7e3cd47968ce4fcbecd2fdaef8cbc2750662b2ea6469081b5503d8f9326c248159b8658f32eda440629aef425cdf8d947be0958177d96384809f1cd69ef3ccb5d4d28e1a28721c0f5e47e905c3b843b1145035d86281e099e84e7e8a2a45d3908ae88a0a0d475760a8172cb06ca062917d2a359f22ad48619921aae22f137f53bd781826b27ab59c1a0ab3b816234b9d29e40f6bc0118a9d6b930d3fd611a3ec9ca2f7c2ced8191c1b3f7927522f92564b40c1a5bea5e86d34a8a6e450d411f0987e6fa22baf5158f08116dec24d1b512d482c68e44863fefe100e42d00c599de83a8a3e938a07840eb4f904b49c44ed27e01838b2004b1993c25d1cf975c8a582865250bc1d448620b80963d655012b697a861f1f25c8a548ebdcd7e5843f87d6cbe64331fd156ed877b2a1a810a8610064e2d385c19d261edcde74e479b92576f6909c729feb7194d066bdec9657ed2ba4bfa75d7a96ef12f0bf819d3e84b0d79cfc3ec7cdf4f5e5244d27bd956a5b5b0e86410207fd0e5de9e19487fec2709f8132bef014128bdbf6ab493489914802c473474d148749e07878b6f51be4ab20181e81a658f1d2f877a6c78912f0b80817c57ca579a885770a104882dfbf12eae3f7ccab225467b45fe27030b9996c0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b20888e57b1ce037a1a21fccc9121578c62fc3879529179938570dc68302501a","proof":"36a92d3386e8f9905ece57a86a0b242c9bcf5d7721e54b2c5c4f6ee6aa193d77ea6671b6d2bcf518c1bd1551680c8406630efa8a314da8bba872bb81ac58001dce523bf21997a1fe30ee9ca6510169c7db7d7a0eae7b0e060dd1484e704dad39608b04f293347ca4854e14ed21161c54909277fce0844066ddf71f6ae1be8066ff8425e08bebf0cc2c8f38b7c5d5a409d38305b5522d2a8f4d29e52a1f1e00027c829d4bf010dcb4edbd21dfa23c7105f4c1bcef858f98324c72f36e40a5a201e53ed2f50b9b4f0fd30f9405edb0eef2f8c28b759b71ec95905a3ad0239bf7064e78707ff3d126aa60024ccc1d8846735175c5d1651b55b64f5236e3f085400f3e430b59312ecda9229ba089b49985df22c2bd2b52b9fd4e45ee8b2a78fc9d19cedfba138c88c119ea97872faf3b997a30237315e22b3e6e1240b84c98ba0b60ce6f80d85872bc15aefa7a3ddf0de2e3a64755b7d92a4b5ce41c3c1a0c0e9f55b016940a9fd0a54455923969aca4ab79417af7fdb4b8512169337249378dde45d4ea89ed48c2ee7df91049ed08fbf57e0b221f43ae9177cf7e62950943c34d33469707ebd122d05d3b81df9e43dfda63a8e1b453fea9268e78c38bbf1dbd51039401678e58f1a55d5c051df0722a8d9d676f959aab22d27319f0ff2f5790cf55fe16b0af3c2a0a62b23d885e472ee45434714baa1b3cab504b9f4971e9d6b40986f94c751e5aad725bf0258210404dbcc1f38b32406c13fcae4745a57c51cb36bc97cac0f692395147a74bcdc01fdbec8419a84ab272afb67dc6664a4ed6ea6ea003bab921a857408fbf52958e22a548ccd48f8418607d632fa3659548b8a5668618ec3b9723696b18e69c17edcb78d21658a3f4ea86c09c10fdb1e6a31b8203d69dd2ed41c6762f14293e779101a209114cfa2428dcbb58fa420921633aff0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"92d259047203964beb5fb9ed7a1398080d960bcd66575efce741435d4026046b","proof":"860e48724326bb7d59c26666f889a09bdfb6939566a1462a0e609fedb46c1703749803decd312c64cc14f12455b9364a7402cefdb4ae4f5a2f717c3b0c7ea7572262c27a2b7b819625fc250451dd12b9bc9faf459ab087ca47b41aa2ac7b9f5c0e3852892a9d462196c8b57bb0e02da02fa8e6b9107de422a3739d1be331767cd68448dbfb9538d7c3c135a153e025945afd278cff6c4df0bdcca9869178690b7c62a7aef6c61d38ae178d1533feb74f22d44e396edbf4b719a5579f662ba6020583460de49f202780ebe59ea85ee35bd4dc7bedd38a57d17a0cb528e37c44071aca743c288a820e8fa97acec0c91c3cb27ac1b457e951febdaf60ca926c1e4aae28a44779ba6b295631b7ad2c92de39765587b1b26b305dc497acb0992e7d2852a0db7dd864c19daf6de5df355aac32b98b7a3b65ffb5f5c4973954222e5c5e0a8074c79851d7e4029ac12732b2012336b88e4168b5d5cc997af066940fbc3370f72f4927a661fc044b67b2cee760d2fcaafe6313d6dc195d83d6e88b091a07c25562fde1ab287e743fe01b47eac2da4574ea0a4fe539621691ff05079b630c761a441bc3dbf22983d54c3c22b1a09f507afd781e28e36b39f4eb4afcd5ea34e283f179c8abfa339c10550b76a6ed774c7d1c0299dcecafab5abf41e5cc236542c0b1f434be6a3189dae484cd0ca9e73d5dc1e7233968f77c7cd806c7dd385244b93d169bc93c2451e5e6cc7e614cf752e52d11f787031ee32a9417200b5742a2ed211a6aab4de53f71df00f494ca0286bfb80e055e3eaf163667542fa276265c189df2bd0b3ae54c4871e688d1303d343b979eca8f2e920172e36c5a869e1bf283f3a082ec2efbf496fb547396b312e754460ca63b26472a8ad39c24e3020ec28594151654d03cfd24abc00543bfe9e93d26ba1eb01a1b862363a0b5329400"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f86216e90db2020aabb105852e7bf08d08c66a90ecafdd51246123943e309870","proof":"de349b2820cf406380ca07b53bda07672ff21183d9ab0b6a1eeeca27bd8e147ef031c8bd392805810bf1ea31d374273042444801cb67cf17284aa0098ec11039deed68b40a8cc869426bd2228a56bd480f8636697a4e66cbd5903b2d519eb2226a511a9dc6ea113d792c2dd1da62a1058ec268800ef3462301e2e060857cb15f7a8e20034dc9536382ac0cdf845b2c8228f1c2a94f72219ab346e3626ceef207cb8f368d204eded34775958805bdbea4621336b28a19ad4339501c86f300bf0cde8721c02a55857e8d257db93682c7bd37f8a47e5d10f6ec429819c9c16eda08eed150e7e3525a3119de1b247c4044e97a1f97dae276aaabccf7cf4eddf72a1a040dfe96f516c50a0be364c236ce5b93f38eadc326aa0a5d3a291c654953b92a70db655bd5f6dce50fb077ce85365ef4c6d4143d6867d2890aca1bd5d78b2f6170ce35848bffa399b81bfc572fb3e586f1c3096188d3df16d65419daf8817469949f8d4dd228fcd5afd5ae799200beb31dfeb98f579787630938837bdd05541c50d5d7cbec783893d44fce367be02cfcadc174afafbd05fa1fe902135515cf01fc2ebba1b5407678eb3879ae622c7134af39721e117519d0953d288a94943942a05ed56d7caee2cc43d2a58a2bff11fc7130bd32c00f5c607295373077df8656207f459206526486610409a23a3edf820744768b431db615f2431753517eba4c38203ec51b633791738d1e8345b4177c9b10fd6d54934555f7366ef845309b74186a5de14278994a4938f3234aa6b66c50c8ee2eb2f8e8f7e7a7ba4a64abfc7a140dd9e8e64a31389a193b7eb7adfba0c7fc1b602d1f52da9d5bc30dadf4437baa2d571e96de8be761162b9783bf7bb3c35b2bc61da85f769fb2e3702836fa0de90636e97db70df5873065f15177aca30c1c7dbb9e1ff881146703616d217e0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"349ceedf28e1c7dd5624fe5032ab29a1b5a2d912947c3943dd14c5b3c099b973","proof":"003c0602ed915649ec403f5453412f9ea8b1a7d5a28d9e10117bb54703bc6e0edce2ad1cea246c5ae7065e939f675adbe5a230ad31ca267d00746d2765b309515abf1968b629d23a07f198ff046ea8cbd38f3d051be7f86abb9a0dc7a71ea83fe6df5f62993b8500b58d8d3e70c26654d9e434f7d8854b4ff664765c801f383fb354bde22dce2e22e68a175ef7027769901a356111a982df532de9c56ae16f070402987111a568cbcdd3351115403149f9c71bf09fe225476a0c98ee070f440a15906dfdce012468a5dc6d3bc02fd5fe0e23b732974438c13ba1f58c0eafe60b2224a5d62e339025a0e5a0d0beef8a8418f14fbb33e4ade747999cb0f2cf6c3ad0a37663e4a0da02c628be1eacc836d8d1c1ef54105a9a29d38624c3c470da62669b42720b6f22acf4718580dfda71cf233b636c0c94969d0756f0a93033d12f623915e15543886b74c61767c2d6a12c6623efa2414046e3aafa034cd0f07270b8512775b619ecff7f66ba7a46f32e6804b6d8ae828c299ac5f7ac9c3071f53f747e9ecef9da34933dfae64c4e1c935aa4db545bb271d718aad3e33e29de4262d47aeec4bcab67f3dd9104590dd93419565caec7270796096a18bfdb87f8402204a82ba5131cdbcf99fe4998aaeb10003f8d45b862e65633107ddf324b759b590e4b307d855fbde6068e00f9a4b2f5ead456838c16bed6fcf1af32fcea2bf9599eb9c46cdab56a6dec165e91103f087719693dce6375c7c55b8ddb388a047f29aa593e8fa49e930e0eb2a1f97302076558cb48d5bed36d6fbc8cfc0dbd4e297a54cd441f4863f59f16e17b6e21659c4fa97c2bf35fc3f332d8535163ab6ac664dff6db9a25d3b100d1fc579e5af523b21b14f73071183ff02733e5aefaadbd02026ac07da55daf73ea6600f3687078f9c5522d229f8e1ea11f354d7643876700"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fc16090eccda665d244ed93eebb5a29e075dd21527298340a20b234b0a7b2315","proof":"0691e22b1d8de0ab358f7ed64f67a385d93981ce003bf0c1678e95f628d82c2a9eede13baf832af01042acbe9e2b887f322b0b25e1bf31f03bd42f02dd434c61980c1a575d95efb02622630586ccb98a4a76e1eaced158382bd3a9d312b2935966af1c59d74b1aaa2296d1abae053d1f7b146aba501ccb96282c5b4784a9723c8064b97380ef46a4fe62915c9035bf6f4a7f3dd923cf130e921bfb1e4049140f7cce9b6eef6cee7a58d81a008074d50f8fd3e9f3a6d826919615a53ebffffb015ca0b623f95b4da5b0a45213924a32faa1f563ebbbb40a03617da1fed1cd360f1c6e6af36c2a5028dd96d77682fdd805778724cb2735bf7a8b000633808108159a6c3b15955434220a3edf3962ae4ebd67a5eb8427d165eefad148ffe8ab3f75920850fa4ea57183205445339455d4756f01e7716f4a224973b113f7578a6917000a531528afa2b6f9832f34c7c610c0eb7fcc7890adf5af613f11d7471794560a97e2968e64d60b40cd59aad99b6118fe33610850737def77f7a88f85ddb4696ac26d9662782d9a7bfe01b35d22e8e2c81a3c283bf61ee162e72849ceeaba13002ad3516cf00341e04492276006109f86863c8da9e0e455e216802077ba4e1c149b0e5b7cf2d5f5a89e035336783cb051d199c8dcfdc64430698b4152ae6f379a121819c23b8288d50b1f0ca4f4238f15cb878d8a3bb7726a305f067ac9524a848ee6302c475bd676292c0343cca84b4adf8d9f4a41fcbc372cbf83d1c8d11564fab4a6974afe1bf825d45c456d8f4a0eee822e2d5df4b49b8ce19d12ada27c46e20c4effac9f20b1d06e2c38e564187e798d20c1f1dc5767019d5cc2a22031e363b5e65db0c4152257430b9a321d643cdd4268121eabe2180e47247842ff03d9f8de68891d31d9639c2e9862435604f91a25aa0262d15d764b110908f2de02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f86dd064cec73048b3810dd63d7cf202226e2155412445376faf4d39f67b6301","proof":"70df21ba1b35b762f25feac600d1932c704eed2e19b357d4731abf72e36e48060c5318de702dd508889715ccfc06c0e5df140291ce046735826a675c1831733ac803f69551962694a3dd4c9d099d079615b24e349b58cd4f3618a9204ed71707de3f01f2d34c6887b1f9afe78a83cf1a9736e18e5b631143a6c040e0cc964207813380ac7c0ead202f5c0762ef0a91e669f078453911bf8c9f4606862548ed074ce65e310c9227e211bc7c85c8c946880135a5d4a1e72e96ae7fb5ad883bf40e557bad1e6f6d76bf659695cf448a3375c70cb95de755e98940d8d88ee636650d9a825251a091bfff67635c61c562c86cc8d828b46b59f5d424a5c1e3be4bbc44daeb074e84b38df7ff63796eeb7bd4c1aac35a0bfdcfb6e31103b8ef8214b15e78737fd77bc998e3ae0d6542432932b2a4b03a6e01574982b3ca1c1372c61466de484281a18863c81c8970b8dca3d732b7dc624a45efc49de9540a810c44b827fad4086c3903222f92322a7c4644cb9127d0c9b57310eadb0b7e0ca0d194114430b41867bbe61bf4b7101afa72f4062968411c1a4196689bc6b106482b99e9688aa0b091226f419e3cbc70cdc576d169ab40fe1411e62bf2037d810bfda89531ec63ea2eb95643c12efe287c24514db34294adbc3264546f6460b982a984623fcc4a72c0954d166ad4b33e21948037382ac9171b8530e5005d4fef26ef5bc45910e66f1a3661fc43d94b28171b57907e2b3d99bf491e05425be8576b0df48660e895198b99b6137636bf0c231cfca719947cb90aebeff539f9c8ec6ae699591554eef5eb984ee9bbd4a0ed821e9474caa56c8da169a47e73c30a2cc04822f218f9f88762bbfd43989fcbe0c2d4c9dfabe77b53d9b51b6560076b76ea41a37b07a936b530a1a217474706dc02971a8468f5ace826c31b6d6d5409e4f32e441504"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f0690ee36f515a8438f5ac07b93fcdd8bbba71dd02ef127525db4ceff2395b3d","proof":"5a019698206ee24f381e6b815abb3df282126dcad7d4bc4686238db28f5f4e657ebd632bafe511725294146467273e020432c70198095a88bba092ec6a5a99287e727c7882dedb3ec60474fb9d90eaeba78c7d58070b214f7715d21c468ef41922d89bb57f13ee4a5904a558caefd07d2bd0502c4ec5b09a0db10258bbeeb0536cd89126917b5d9b93ce7e4450b22612e04ed69bcd7029ee69e103ce0fd536070d084edf3b2528b18ac24773ac6631aa97ce6da57e27befed4749f2ce6527d015500bbb1c93d9b26af1e3eff92dce13c2e084299e362c0f6c25d83411b05c90e2846d7c863d79aa5640d9a847ac14900aac47c829d8ed0eda0bcd3cf92fbdc669441a26a761475c383b4fe85771bc865121ad0f064da026027407fb93f51fa6bc4e66c1fdab6f98007f30d8013fa93006059e554e9743416d99f60b36d3f101b5a9f0849ce3c720b4ad38de7e4f5565f46ba26e0807e264e76eb9d755dbfc838dae38df002819418fe2f249cda45fb6ae7bd1f7e88a1a02a925a85fa50b96c4476db864ccd786fb444615b7f438db1e291c57e3cd6bf390f27add8f231ced6752251f4e25d6e05e649455118c585b17108224f074243fb2313a123afa6e7d56f722596177542e43265a281d6e9e88642dd58855494e8ee472b3a0ba3df4d4938fad5baf0770d0adfcd04b55abf6fa4a04b02bb786f28a3317e9c6b6900840d3ca4a7e4faba287d52412aff643607a476bd651734868489f76a9407109fc86848e4336b96c2915de4e3c13e61287632cd57c6a7635adaf32cec202c135b7ce34e6ea035af6ec8fb57caad9653d8c4877ab957db87f899b2b553e13f345f809b38f8fc17dd245b76e7e8ec59a3b68a2d194ae345889847d7f03853c7abd5a53704226e77905d288a177effeae8d51b811bb304656519e19ce2f3a6dbaef024b10f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6e863e62304555f629297c4df63009278c2170293f71e505fa7127f4b57ddc27","proof":"b4a1c990a47f9e52b3545f03ab10b26a8afa357abe6897d29edbe881c6e430508e4027c43687e4a99424798c60e2d821f53d5e9b6ab6910308018833770f8769bc2f881f4c1632750c85783e7c2176f74072b6adaca15c32bcf4f876a3c1da0e3e41618219ff3cdc79d115cfe28b496a6003de4540fff5cc80b01b5be945a9780e14249df81d918d996dc3921f8715d17a5243295783c0a6897ac374fc6c7107e3ea0b7c1165478bc94a7d4741b59e5427b8fe18bb0a548d2386416ebf1f2303059c3311ac3d96c4a0c51db91fad5c9070fb3d743b39650e12efa8cfdb09a10b7cc69320429575f43a2404714663e31f938ec57878ec24e0518384103cf9cb766ce9f33c83fa604fa7e9d6221141d3d4a1f65315438b813fbda7ce180ca4c06edc425f842f4ef37503559149d67b159a73a5197dbe332b530c56d58eef6c98157c98c7e7b6712e707fc2fa708819c65a2f39494f4adcd490d46bc724229577363cf388d536ad967ead872eec10a72fca621a604b68dc6a11db76d1ec90853a4bf01d7467cb60ba590c0c5b85ec011b26e28f03f25f64aeb18815e4e4a05e1160207a602c5cfd5fde704ec0b7bacda2a52838ffb2255fcf7ba941ce8be233f50f3a08500bb0d6d7f2abbf0cfad07b69ae0048e8c7b6b25380e7ef6f2e6e2e970c647aef77bc3ba1d63378b61fdb1f4bd67454148759747def19bd2e46cf184a174ca9267c6a8a63adbceb2a6cccff8641329bb648bd3136671c2a5c0e5ce48e2c984bb6206e39cf904a48a735e3f8284798b280a0ff3f0835c846ebece60cf727b82b39ed881478d29a275f44268bae32425a041f0738a33a60b439efcafb3f78f6f5d20760724e3302fd91180377380518ad570b8eda2b1204eb087b90acb10990c57a957dd317a0acd7cdc330fb812f94955db51bffe9addad3c0ea5db08c0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6a1407846c92f3ff496c34e0f2683a9fe85619f3ed590a62478e86ae7987834d","proof":"9038d37c9fccf75a0c69772775b830d483b2ba49275a0d6323373666d434f723c003693e455d49aa9cefaf1f1b6d52409579ff6b86ea9a055c024cae13b632235ada8ff7334a100ccaad348629e5abb33f932605e3c792109ae5baebb37e977dfc56b042803f5778af4122fd1b258c378259bfa7a9541865471ef6499cdccc65b3dd704d3a3ca419d98a8ade90d7942470c69e1da99d5f0665cc82fb00c1a9040ca8ebd5262ef2d01fc9363edd24d67dba64def57c84517e3297c8bb587e380ae8e51702767f0d53c302aa1a2145a5a592402ad644c23c5602ab3d1db4bff7022ccbf564243eb1270c20553f9d5a605c5d00522b8dac333eb9bffb3685998f25d41735e1185abad6006315c673fd339d2f07e0b2ebcd05d4b4ba324164a6782dc09acdb14aa47880d9ec751fa2b8ec0cfd1206edb99a2e397d9d496c9ffc230ce22ba29d2055adbd6b30cd34b4e70065a9fca3214a254613f87b592a41834e793e6cc273a05abfa8d5dff3290ebc022f30c28dcce17969d20ec0b58054be637650ad07499956861116fbf4839f82fc84eae8b4b2f04982ff061425bb5361c574ae311043de9b60d976ce9c8c1d8bd9100960652a76f675b4e70293698f7d1005a6573844b74316a67abfacd6b33ee5f923b2723928071918e2d6c2ad31a03d2da0b684e9882e04f928f2ca65afed208138c70fff89fa5a4e930504b49d59790bc47cd1a5ca6f54a6382e5f19974c2082efb5a142face4e682e88fb1a9278090e1a52ff319ed190602fadd951bdd619c944354e36650c2579e605e459cb68f34ee82961e163ebffae3b65c142755a4aefec2371cdc01cf43e4ae046599e45da02b88dfe6c29cb3c7e4ce8b54413ba5bd934d11226aa980dd2029adc7861ae0605b666e2b62fd88c5b714cf5e950b22be3836920e9b84ddefe890d84de57663a0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"38d6f3952014c8a2245e0517659fdb82850e576f3492a49e341914cf06c6414b","proof":"fe763c9bb1a5a0c483ac07dda6902a2ee1805bb02a30572389f6c0af50d36a6b1c67898de2c261db54321724fd367a9b5292e763c5a6ea97a1654a4f695c013930dfe40ef4d25c900dceb23b6c809c4028ef6b91ec5c5dfe14cd1568804bbf2c6e459aa21ce5168fd4e542e8843104b941c874534b3223545ddbc96b25922b77dca4b361b0f60e342b8f2312324ab113127f80bcd4def85f5cd6254b7b538d06adc7223b7b09601c6b6040cf06c3b8ed2ac04623b32833b9699a70f5b8e0be0afb84c656df9b2f9eeeeb58ead900046a443fb7a0ec08a77c67048dcb2b460a03ba88ea568cc78f8b4da53240b821560a3d7e3870f42dbcd90b1f004402a0ef51f4d45a2bf8c82b2898cca45deb7e04fd6d9646bb4d3f3765b857d960b688216de0f2981c655a4daf2ce7d62e06e6fc269156dc194f8b304b6120f0f13407fd3884911cba85d35559ab404e4b1ce22ed7947d03a9456d19976a83284bf9882508f41ea1f3c73f45aab9a595851f84e254b0730e6b967ddc5c51ff3d3725abf513fa174561c44253d63d22b84dccc9c6cc1589ca15147ed92fff72241a62c3865f08c99ad0607bb234a0597860538dd983d48f16cfff9071dfee7c0528a519fa37c8a96679ced79e3648aa1f4768b541ab9a6cb42696b36001ab485b06c83b2e7826c2971c28b4fc97d8853ebf44c7d0b09b3a584da7e75069969845dbc6f3b74cfe608a66586b2f4cd6dd82c97397e68d786fc9662fa9565a9e3107c562b54001365807beae3d1cc32eba1571ba999aaf31b272ed1db881a1c33024daf3ca5c283ad0c639dde4986ea23730c633cb14292e5ff761824e98b6502b03f654679c643b27df33071d154888909d7a417d7cd6c5d91caf57dc3d21d15981c1b3aaa7030e59a41b587e43073b89be59d0401bfd8d99c6fbebfa9fffc74a507804d5b107"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"96cb58cc3e8e63af463b86c95deb5d9750b0fed4e9aeaa86e134d6bc09c84a07","proof":"b0df277e5ee9d414164e5765a3eefd0c472939a3c6db5418c0f173fb2ae0a95814009cd89ab00b5d90eb5f51f7222766863838b1d56bc750360eb9e497bfeb3ed2d119dd6cc63b5a98c36b3a1510d597b6e7cfc2771c8ce7f909fadc75399d23529e2d6a3cc77fc2295bbcd17fb53268d9eac0e3a0147becfe59e432952e1a2049068018961e0b71d2dcf4653957e9e4422d25bfe00d5919a95c9f260d15ec0e549e28a67649251f82c54bab440221bf27388f22ade4e38f64431620edcce707d378bb6a9b134d9ddb3bb5e24b7d65c4b71f85e20d18a2e1b69e4e57fbcb7909c89d921ed8c2ee11cd6090ee15544a33b557a889b6a3527491f4ddc243d40b348cadfb1153d0c5c6dea83b16d509d47d13f18e3ad5d9bcc1d0ab433a12e11e1a24cd09e40bb5d847f1665df9e9a8edbd1acd048962a2eca44656b2287c191c59a268e445f4e7aac8816a425c770d7394e689ecf971f834a07645de507bf17353002ada12a62041c0bf67f8c8a08fd15b2ed1d8d316290225e5429bf81aef763c8203757036824811fa628cd138fa748358a803ed0eb0f4c2765cc89e72ea6b01c8e7f5aa40cbfc1e4e928b1df4732d7ec4dec828c40bb63058eb3b83380116599c73c139efa173fbfa2fca9827e4d99316268837d5ba0a9cba0b781b17eddd06ecb4c554a81053f1b9de4f7e0b242c6272db254d5dc425ba40485c84e2a1e8153865ed8ff61c2a2fba40854c1574d3d586ff22e3c3982c25477583e4392ef0411299cb517333dd2541a79e9e533a259f4816f517f76b9ab638160b03a0033b344efa8e92a3439e222ff5f3ae07dce968cf4029efd4d1e78836f76d8e89a454357ea8970a63293a7b3edf2e51173bd3a1016bd5d69133f7a11d629a6b3209ce02e9b3576570c73d952fb9dfac53aeea92132eebbb426590c493da6c5878803f0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"48bf76d90b62ff00c2c5831273c7352af0f679b84778373c69a18200a1ac4a21","proof":"d4a7b9be717028e35056c7b554d7d792e1ca7dba363194aa5cb028d4458296246eacb9dbe4bafd701624095b61df5081f64cee1ea4348bd8032453cbdb2b1565744a71034e699945866512f47642ba5d44ef3880d5f132e60e643fe7d50760752a7fe474eecac162b8908cd6b2ec1723c6a037a7771d6e7ee45ca3110050885456719ec0133a6cc1e40cdba13bbdc1542653c25be5811ec7d4edc70d711d950df6c76c27a53c11e13b89d27a37951968c5240fade80288fb2bb806f9aaa0a2008f91b98c57599c0a47e4a1306954f7b0967dfd9edf5a62abf1d1693efa13fe0f6aa8355354a4fb15c1bdd5d1ad84ee77ee6c0d3eb0e574a8e2ddbf40b2243b1f127e7f03d0148926ba7992688c2c1479e53fff601b11b2b6d93bdc1c80d60e054438708a80f4dc5155d5e41bca43712a3d1d753ec9a593dbb773a872432b8c5c5c506939410d466db40749d5da26f8d09bb1c466932028c12a96c69cff052921fa23f3e13d713311491362e925e1f5ef18e15574e3c27281a078e1b95d8987186ad308268746c239db34ee9dad52c15378286792958e436d0592298105acf46e84e2d26cc686beacd779c465da0f6ab446803719fd4d7d067348cf6efae1f12370951626b3370fea8422fdf70453a8142151a33b35c1d8ed8f9bfc8d4442586bbe997be896e3dfdd311e0f7434d4902cb0df5bdd14fa9aca73f2ef908d274a744aefdfeb00c23b3547d599de577c5214eea827640ac6eecb1d34c6ce5895f740be6b1f1dd06063dc3f85e957faebe135c5d532a8520419b1a4f2ca2fee4c4d4a3cd902d5b03e5846cf41bea525fbef32593023fb553d0cb509043ef4cf52275cca5ea940a063ba61e69af8cc6243033068d58ad40e1dcc3cda8a0d6ebf86bd0bed6c3c1a4e620ecc266139f38dac80e6253116337c3461ceb813e9bc6e371303"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"eede33dbc59aebf83535625aae04130357b1471452376cb1e665ecc6d5a1a010","proof":"9052e41990286b76be40063d5247b6b587a17a7cc31ee9dead1a10725b995233980f722bcea4d548cfc1d235956fd72f48492167b3c07104cddc337469cb3a49927083bf65df786b9b35761b8326a0d0c4f210528f07b677cd5e3da9620be248801593c6131422cfec25722695fe6ed54c446bc7b4554a7d3b7fc6cd0f74eb1843be6abbbce3976683f3bf5b872477554b6965e36e30b3881ed96658a748a005fbb97bda052be2269039cc2ad566d9cf3645a51031c27c8a5d333fd9bc70ed0028e10a4634a10bf7f67b7547548226d986d2ce20324e20fcca23eb9325d29a008ae667401c18d2a38fd79a9c6abedccbcc3dc94667f17c55466b8d7698a20a40805d08b769b324c2e780e770f5bc5b637a50ab28777fdfd687ea3a7fb0f19827742714e79915c1920a1b8b8c2976045c63f76b70575b61d8ace728299416977f3224f69d985498f9f178ed0326649dd9aa7944bd4c1c675ea56df6dc7ce1b1780a04b343088898848c09fbe7277068248917c5f87dbcbbaedb6d3fea4c02c3603087062a86ccb495c5ec011a3a890423a191102b6b994c096896bc3201ba512c02d35dd7416f9eca669b856a7362910170d9a45a409e749e4e1a059f90d8266ae85a50e92095a4bbfee729f3185b69dd4b7851d7290dd26b9d7f146452774b77e6a6fe100aefaae7076696b58da38d38b2a5b77434d12f88567c5ec6d919075acaac3caefc83122d250dc21b43f0fb49a032cd78d66cb9c42a09ae9679c60265c818d42ceffc52753d9b4a441b2e943b493d1a23bd0cf0bea9a421b0329b2c074c10de5de60fe341da0cb77b5cea8214b37836281193dddc164b2558b7ab927db85d29fe06bfcd0ab73de32fc7707bbed64e2e4cc9bd671eb935dfbcef4e430fe0e42cb23c799c3187fe3256e664d56016dbcc9e04fd62f2f1d420b95f337a03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"50915f8b68cd53ad4274f7a7cc0e91340c19d9054366b28b4dc2e41cfc99db73","proof":"2a65f561785ddd1b96ec56fc055528ed5c584884c727b7e8f3b3ff30517adf7c90370205827b89ad005be8b462bf2e3f83b82c6b9cade57ba669172d1ca3b77f0cfaf1ac8cbeb8677122fc21413fc624ba951b46c9be212fc294277bfd287c626a0c01c6a525129d2a3029f2f4668b8708a4ce89f45ccbf685e3045e2f340a65f3571c84f7fbb24dbff8cb105bb814a42adb6d802e1ba7886db89d42edaa7300a9003591aece751a4a2320b6a4425e3776d88968d74effad2129f67c12c8ac03aea54180176e6f690ee2192bd20013d3a388510e1178d900f5596ba8ae4ab108d8a2be0870bba5e8ee55ff7e47ea583ede39f26ac179d642870c0f9060a5625142e708e1209dc46259b47d780cc04cb320acd4f1c4dec0405de58147259d3c1e402d0cd36610d6bf29a908d879b2897bd6f5725da2fd87a42a6b4087d1628b36e8dbbb52c99dcbe2d497cdd573f97e17dee943d645f24577a286e0f940e40c1a72e9cab104c0f53de7121daf6e638c65d8007371d6a1779a19a9b63d5f946b4968b29468e5c3863873d55614633d53a09c1ae222906237075a646ed01b02c725680fc016ccef099888d59e143e0471c174d37b40bb3ca5d6dcae6c35fda408325cae1c7dcc4776a41eae18c62a069850364e70273c228f3835162b5b1046937bea981706085dc581763b3f89689200344157ac77c1ab61d84dcb0777c055fc6a3ea8942fdfa35ec1507f2eef36ab90e5084a0c9db33c9d0effcf95e43b36bf58266c95a2575dddf840f3ac67d9e90ec2c68c59ec376f5993738cc43b35ce140a164cd771fcb71a9caa7022cd69323599416840e940e3cf8af3cfa0b3711ca70259eade608735e6d81ad8af9c7bb53cc31a1179dde9f8f98066e031ab091d7603a84f3bf9b9794b956c314c91c345e79f6cb78b2ec0bf6b7ad055bc5bf98e3407"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b0c208d73dd20b36bc1787289bdd5ca8c9ee3a1969c0268c73d83d9564d99a57","proof":"14ceb56054fbdd3aaaf755ae1ae2c0277de26de9c4b5543aa4224dac622d67653c372ad8ba92ca86bae6f7b6058ec8c9eb45192cad8b3358d8002cf4d36826353a686304af19c39e06253a5e1b3086c939821d5e41178bf242c3c4276709fc0806a656ac8308d5eb75c0175c1cf9b87640b6694a14abcc5677f30b596bb3db085e3b1d0c858022b350d4187b8cdf2efaacd51db5b0864b25d610990676cf7a073aa93d66529f898cc843dce22e95654f445c430e24437b260024d08cf7618605d161f8905e6d0d549a4f347a8118b28d86548b6a52f004fb966dd72879db7e0fc8adc8584705745ed3d0c74e4928f5bc9c33f58b5556e97b57afb03e382e5b3f7681b737743db54a8ceb19a9bac7414e3b7d46afee854ee10b40a4bed79a9626442d008791dde26a95ee3c17f757acdf3f39b9abd11a40d6438371c79591d5191ad37c07ec42f7abbad89ded49feb1f20ed80f80d6bce8930e041916b03b1f4790beaa4b2a538bf1a837e36766d2b9cdf7d39e51af911184924654ab60db7641fc4ce1835785b8ccc5f475d17351afb97cf18c44062271f690be25d51037b0626a89586cb885686439d5a79d88182d4964840441a923a0dc1847246586822e5f10a386cade440d70f3cfbd561ab44c114cde36d79a96d3dd55bfbe7f01915c7b4e08e244b32e7dc64730a2aa7815e23eeb005c6116b414d4704dafe246e04731a6160b70e5abba79dd2e903d31da39fbae5f977bf1c2cbcdecc51e23a8faa15e326f557974d7508ad82221c25b1805a7cbe839de7af34b280a777c3f33bfd5592a3e283fb8708925974e11e089a9a38a5bad6e539aee144c4bfdfe11018ea0677231b0bc44c98cb7aa7746dbc1694801e274dcd3e25136b45ba34d83341bf001944f57b9e6381202f155f363e3025be649eab44d5ed7b86246b480764cb39a09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"804657a0125deb10c132c678e21ea118b496798a6dd64fc2d5887b882d7ef507","proof":"80173bad0954abbc8b2f674262ef9a16b1285794cd2df1c1b1dcd01286b3dc6f142e94c89e7f378e79001006a90c8ef21e2d7e8af0b710347c8bbcac8f3c715f4cb809976756ef185458f38c0c590eeddec99aa585d5cedd25d24987d4146a6cdcd2c3e16e5623a2b9ddd54da64fc9e0fd578e0b542bba4029b538d8ba338c2098f2bc667c3dbaeb7c8f35b37cdaf38bbc5d0f621a86b6935523fdb53fb6f907a04abb478fb17863e39f8871d4aea1b80d669f464dc2ff34cd1747c371e7150c2d9456ea599b13fd41fd1ee5be7ec1072eca4a714c49552db3e4a2e369ac4909a483e55a5a9690019298c3eb38c29866d342ad6a25c978209a5dea92a02d75561e1f7dba8123cdab41a6a03e7c96d242cf0d32cdb321f722b64e40c1bc2b2756b6f6b838afbbd702b47eda7042aa49f9cd4926efa1fe43a7e219e6df496e8f7c58e5d17171f66b241fd0757532d499e986d6d672d63d13b6f2533800afbdf25eb69362dd6cd2b88d2a1ce98f259a3fcd822c2c16f8e2e096854b6fee7493c83b960781cec5b65a53089f8d933b5f2567ca76086bca016338b0ed71e5a441bf65d8b44cced62a053d83bcd5b3e1b28b2fd1e5764ef8dc28ff8cd55efe3e0c2218fcc83d13439b46b6663a9f0b3eb7c4d1ed3800ccc19253c42821b13eb63ac24a4480a0d88f9aff2c0494561149d5f92772c5c44c957fc35eef69833c59c30d33468dd24ad443f21efcb891077e1affeae84abbddf9032dce137505701b76e06a1a1c95ae6ab98e25fd15384dc94a1a37e7bf84fa13c7c86d5313018440a1a50e74df9d97770ff37ab5782bc7a0127e5d722df47dc3eb453da85a4c341f13be0a01f2c658e948a3c40cf4ad72be2f6eeea1c59fb60994eed5b4853007b221b908d2db473ef43cf4c04ef2148987e79ccb88c6951f7061f8af4e5dcad0a89d2a0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ce320c9f9ce590a20cd181eecac8577d8f63d7a253b9ae7e9a733792eb166174","proof":"187c3ae6e366eebb552374634056d29496d692c09ee818de74bc452ce3474358bc94c317f488b9d8071cf5eff2e136e59761451407a7fd5747ebcb3547dcd26ee45ded31f16e814c6dffd8f57839cda8ef9b7054d149ed05250763ab0a277c57ac3a1de1448a38a60e0960b577efc0c8171199913a0fbf77b8ccbf13e2ea7717fde06c9242efd9b165a2cbce22dbb7c1cc826315e99550d5ab1119339f1e2104533e635e1ff3dbfd714c3f4917be5f967efbdb223905e0f5b822f702805fde0c05549dcd4058005410f60a7e037ac271a95826e3cb4bd500c0fa90e55c35e0059c366bc8a91fc70d99a06a0417ee5abdb47ac8b3ee0c9831b77eeae69859ad076a755ca398a901eacf844937a6169565ec3af5534b31d926e9b18a63acbfc50c26db9834a2c984824c22b257166547647a781239a55b8ae9b56df96f56078662c40ea12665142a7943501ec4631bb8422d1b3efc6c8a261bf29a43c1e933d57aa0967c8d4fe3dc5632c19fd8635c5d2aba461805291fa8410d1fcd9b55574d57d2d3232362987487e2f0e078b4f961b57a3b2a7c35a05372749eef9b4e890b0be2dfa9cb980563d4829fc508b735f3997f78eee573baa95f158ee2502f7d0563702477ed4cfffcc0451b0ea232c941b3c8d3ed330229128896f878a5e5378a25e0dfbb082ff257c928cd6289041b059ade843405bfee01ac21c4a9783d6f202ed44c267e663c09992c4cddb0d4adcb8fdb9ff048dcc666a42687aa50649966376e58340b93988cbeb4d8b225f4e042c8bd0bec7c7b7df0cef01825df03ad4d53fce3ea147a00861c73b7b59dc255ff212d7dd32b6d907eafd20e94d74904a37e41783a24176ef238958a60291b205cae45c4d1856bae9e03fed8b708cdb6cc0c84f27b1566641a8f35e10316d2015658a595c5a5af7c5207565dd9f75c0a9e09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fcad5ec9c931ae87839b1b87b38a112c32a294df9907fc53419d210948008547","proof":"cc49761838e153e63030d1175337f5ef7c97fccb471fb1e404bb29364220c47f5437d27c77b73d9155f10a044f12b921e0f2c4b65a749984c1b5261aefa8a4305e792546f6e01af55403996d6e3b0486b2ca2138abbfb68a6cb53f79fff49b68b6403a4f12363048cbc5dbe5126f5ab0dbd44ed4347923827833d6717615492f7363c60be2c8e8785307dce7bc3370aff6d7f70413479906e06178694ece89085a1a905170fb96896951ed25bc4febfef3ddbd4a190541d91522c5ff0d6f900a073a90638b2b4a0c500d6cd4a24438fd5d5a96b532613a594671890fe17f700a2ac1008602670b0da66e8ac4d44b50d6347d50fb4f279731896252a90fecb147a233d1fbb764bbe361cd6754bfc404b02b0ac583221d2a4c080df2ac7a7b6a04ca8200b063384b0e1cd4a2cf1e7976ee7873c43dda907d55213e6b6dc3511316ee0e80c441fb4b3685508950845d68aaaf745505fdc9eabb797d804133f3f13bd0f851a3ad773c6481febea67fbcd4aedd153ed2f372d95fb812d94b43bd075d2efba8ecac36f3c45be43d760ebc0488f1408832b3a242e1719855f16f8280142aa3be9fc67d7e999295cbdd5250eaeeceb9224ec44021d26d9ea159990ed9488636ce872b977b16dac38be88c85746c8ef60d5a5528d1ec967243ccb3afd85650ef60f6d89d12a366ef98fdfcf32fc27caf75d69ac3b1eddfbdb112c4ee8d1144bf1f619048b1e7c95508026594f4b7020206cd409c49535628a490d386ad25967c59b0e26b70c601e0da4591902a4a5d448397976b01207fe8d808fec21d52d2f5ef38e5f7525394da8acf6406952299faaf1493602a00f87e3fbf037330780aa9e22d9b78990709a00f5b158257bc057a55c67ea0fcae3f49dc2a932c700eaa52155761e91582c724de2c9860695f8ddd8ef10fb266c89d9b8d81f78f7204"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b4fa8095207ed1e7527457083e77d35ceca2ad8105f6f6d7f3da39a0fd1cd673","proof":"b60d92725bc1041df986c1fba11ca6d40293dab089ae23305bd402b590fc3a669012e959c6282b5179dff241b39be53477d056a18bdf7a6c32ae89ae8ec38c14b8f34c303c0edb194f40151aae51bb14fb9f4f2484b609b2f8ef5ae49915e62c9e650ea061d1eb0c720fa6b64b68c7a43034d51dfe82d36f06ab187b0096ab4dea5d0d927c046120308d5534913e1844ea29be76bdff4144853d701f8f92f00f47c02c9fa8b8efe6203e9ddb06f76828de3117b5d266aa0f3506afe96ac89202c17e51900b9d5d7f7dfbb68e47372d479b274dab66a42e8443cf06fd4d5c200c72f07dc234ae0b49151e8e5a500034edd1e73d85c7039c3face35c427a95223052725a904a7246d36d3f5a3d034b940a49d33ff572d60d6f7462a1630b76ae04d2f4bfb73b82e2615881e4998e27ca238638f4883c8f64d2ffd0316fbea5b52b92225e07743d45c408bad0a3c816646309cc5d2c383d1daa2c7fa2d82a29d233965a44a4597d59cfb4453abe46e9393052c2d38d9baf827142b62b189fd27c41f8963e3ed0abbad4a8faaeca3a98d2ef72456a7d6e20ffa46bb1d1e2adfb716854e23334acdc6c1ea6bd3c11b090d84e6b91d397db55b81c24fef8fa42ba3e7254419b7dd3a88768ca2a744a08b530eb2fa16ad49285e29f22e61ed2cd82a8766ed7ac65b91baaf9a078b41534835ea96c3873fb20bf71a6e30e1e4a21ea904cb61cad14f2f47627de459aae84ad78a7ea7db0a4e368a08823aefcaf4b07ed6ad8f285d4ef2d02f5ffac3e54fb47b6bbe7717a010c1b0295570420d38f8a3c63565e98bf0bec6797438ef8b3c3e56beae0b4e8ae05494258ef04497f27a9952f1130d6d978d9ca5f9ebb2da727b2b275a1b0cbcc9b1ff93072d49860691e0604a45cd74a62b4c3d5dadfce793068e28564f543d9ae799df7d4905c2136f73204"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"64dd1e8819807a399f1282e83929767f7d00e2a7a21e22ac5bb6a2a6ec316512","proof":"6e26fa975a05717bafbf2a0fd62d7779c679865ceab00afddb1a1cef9212181ed20817ad0b63e04a5df0e5e63860bcc285059983be6fa2eb3e036889740a0c220a7821ca4b97d9a0b1a171f3e97f2bbef192f6bfc09715a438c91abbe605ec2c18157bb70428a15dcfc8822600328dda17c5a4afa745cf9565bde03441c2ee37dcaa2c51311c92ce006a3d8da37ba05d68b0d44f0f4e4d300c621dd221646b013f855f9fe43eed71643db70863e8367e2d45bbb9ee7f976ee47f873fee097a0ae193642aae1c6617cd1f9ea9adea15e7e3baaa88a7090c95b6648809259c1a077eda3f4e0f07e443d6658506d8f3cea417d1337a563c247aaf3d6836ebd975028aae4397a5627bfe55b8d6bb047294f5a6a717984adf4febd6e2b2c3096e0f261420545501255b53a1e178b109ff4eb830e1ca99a9553d1df948b70b0986802530f44414599765b7b9671a69f7b7ca2cbf0ecbe1cf96b607ea17509363492d507e4ccee584fb4d31ea25cc4883a1ce1fb4d0db7c7038060885d28750bce77b5ab0cb529f03e7a58b8a017c8543ec34614411317561cf74fb9e0ea87228c2922200f9b9273c319dc9dc2df84794fe2da31d65686ea8785a819eceea51e3d227208cb3c4f794da933d366166ba26bd09ff558ecad9134f5dc915d00ea0e60fcf7f92db0fb9fe9a0ff42a80ec92e472de13ff2d0ed3000d683a5c9d0844af10a75dc89f1255291d9eaacfb0c0bfa9990362d6a65d56aa9c49bae3e03afd3636240d0a9378a9c954931ae3a8f16d0f0bbf21bb612b42bd583cfe44753452ddb1bd34ea6adb6a67172dc1c1e5ad3fafe546496a2300ea2da4c670da2badf29bd61b7517e87afc056e8d1edd3dcede4135ad6cb80bea62b23d1cf374ace9a34ad149015bd6f68371aaabcfe30a77b695f7179b95c62f393e28035bb0f733c09c77050b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dc66a47dbc07698a05c2f1e7db2af373e901ba88c46be8c1847c4a8b338d2a0a","proof":"d067a33a00c2a34f850401db7583e104e42cf0fe24efa71887d3eaa4c90e370774cfaee13d72774f5cb98bf1b3e9e0ed9ec46dc03c0c2a272691eaa1041a1f6f38ada4cde2b31fca93391a86fc8a3a2dca313570a7312ea3c6c1d25f1b4a72333ccc17239c85c33d5e0161ab071025d9acea7c15aa03d63ef2cbe3e8c0f00210fefe1012e76e9065c65b110879e6fb393a1b7f6b0d8b62aaba95099c628b0109ced9df929b1645fb53fbdd352297875baf544f6248579f0882538ff4e15dc108a287372f05bf7da7f050472a782071847806ce99ac945a680a2b500a6564410abc5635f22a97cdb4e9aab1ed3757fa3cdefd81728030f2bce8162663662b9b2ee06434a7faca9e1ec3acaa7cc6d8ba9b506a5f4e8133aca5a4de06ed03d41801aa03c7d49f5bc6e4cded284943a59d2f263e4a4290db42fe41e0667bb3f44662ba5f11c8dc124ae15c80679fb37bb0a366c913b06e5ec6fc485de8850cbaad7f42d5c68159229048f1e3eb228b1b3601525910b07560b13a1fbc070432d8696736ed0f168d5eb2b1dbeacc9e9ec4f022bc098b8f03210dba4af841539aa43731dc25eb0c12920a11f919809953a16b41a024ef18b137f30f17d9f641bdabdd034e621ec6066280773d4b552bd9b130f60ecf6c563111d0ec73866d372433ef41f86339e760675fc9676e9e5a32a5946707e11090f71bec6a7e4b49ff8a1fd759ea4468e40cf7bc857d14af9bf49e1896fe8e296de6228ff7bd09630e6152db5c0696a5804105ee57fa8506dbe7d9158b1c4def9fc0214f68ebfdf322951c73412630d32313274c487d7c23fc22c5586fb45e09e7c713fc8bbc229298fb9b47457c8e9ac15397b95e76c46df53a870dddd2ea8ab2582e2567f56477d463b44706b839517669a8380b54139363a3d3bd7c9ffcc09ab12e6075f4c5c7ed4b8eed02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"18a76e19cbbd1e7e044af4c4a2fbbaa6ea574303cbfbf3031a5aecbea7ad7b4c","proof":"121cddc521867bb2bd8f83a180517869e0cab791267169bb18c98405cb76b102369e6f6e053f1cd83a9bf45458b8cc113f21ab5a2390d70394605a0a4c8a7c4bc674db40fa940e416c922d39c705d899ca5cfdb41dd9e91e2f2085fa89c70f24eeef2d13d5783714f16ac150f27326ac266450f93d4a6edf0a2e58865ecbb904305a32bc5b0e40854d15da5e14c01911e07090cd4cf6e0b88825b083a669ff0d93d49b54653c474c0f82e98512007ff3d36fbf1f4db6fda4e8fd1f63f8e4e205c9f971abf6e1c7f750c06c61eaf390913d245cb0a55f491392b7dafed2ff48085067312a419cff29282b7abadbb1896b62bfa59345a4ce45d99458978f1f183a3ec5571c35c9e650bba1bebcd7576948b2cf595c8c74dbd918cc801d118e8223448c06387d0c9b5a3e8bc1ec91d7ca88c9a78d04c6d516677bf9abc03d0ec418c6eb8bfaa6840edb04aeadc34bff3c7bfa7aa98f8dd0b475001d23888a1d3d086ad550a5321f324f149c743ea9e318b945e41e52085ae5dad5ca0c422730531004b6bc2a86f5e103f276ffd989289c5f7a61dabba2d275b933278a0122bc7a5e8ca9e08a251b464810dd84f1b91b5069c9d2eca859404dfdf37367739a34db60b8d05e5cd2a98807f820e9c832410a3b8a7715a5c9b246f3a9fae3ecf3b276179291cfce67ad2332bb41cfb80523a8b582d1917200bd2bfec99de1db9d99405e08022257f581ce2e2ec94687f4e5fec419d429311d6d8c406f11c21ccb96101d10c5bd6c85b766a261736defa004dd6d6f8bddb9539a646b808f187c5977dd77dc5dcfca7952dcdcbf7b215f339e0bd1a31a126d7a70b6598b56caa42ef1ea2ca59f55a69f9f0c6a94f9b86ebb4e617c62da55fc3864ff4f4117cb51a00e7700412573e2061f80737e8e28e87e8a51c859e0f7c7afa181fdd4853e2af2821a0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1024d24edbd1a4d4e4c6abe8de72077571eeb6802b3280ef12c855d562e50157","proof":"783f56765a6f7771c536a96dfeac1a0ab650842346ca51b6f405d1d1455ad5318466bc3c132a383a81a92234c1a93d1d69407f4ace870cfd85a33fad5975f97ed4d691c88c3e53f7af2376e1051b7de1e825b8734baf0949b629a6d8d76f9825a85beef1e1560fff02907f54997333baf54e9b9b6b15a03a40743234ad53393ef683481d23504d49c2db98b38d91edc64600647ccd53b0104c3e9488500f830f14d0f192edf01c5063ba2de88bf1c86b06463a7938fe0d061a2653607a375a07796a9b8c84309cf28adff691866b0c5cf0f6734a157a27c2a34f7a9787389f03369348bf98284e5d94abd20e80c84f3c4ad7966a4739e428c7ea9a480e32477cc84607798d270781f3d7b6a72e35c465cf1c9439c374a85fdaebf91a6a34a265dcf66b0dbc02cc530e1339bebcb20819f3ee12bb8b70430475a16c86b87e7c7e6ce5b7b6c7ce914c100fcb59e18259d6f3f736c2de757029299f2d2cab23d01ba03dc2c47eb8c170f808aae6bf0b343548b2b1d5d7995c2e775589cb338e485488120bb19cc94e28070783b309e0f150fb3c807fafc27bfcd407b2927b7c8c698ca643b15641225c6959046bf4081ae9d0737ebe33e36e32c9a2afceeb314778941ac88ad1ae45ea8760e541146acfcd73102df150ea7bba9b2d574dba02a25d528d590a98ccb0d27f22e4ce62f602d7c6606b047feeb61d0f1751bd6dbab71b5c2c08be1946753d282f7e9747e329e6d15ee6914f4e9294fad388e37d88e436e24d58b3f797c7ee67d91fc2951d06dc01cd3107194aefcfa4d052d81a09ee5d9a2a20bebfb72881bffb03921800fb45450cbe7d7a8bf7f6b4f1f4c0db6ab7704c13436b56e16ee19006a6a15a3509e890b303d1c4c95d6acd662663e323510cd55fd63e8e534f1dfbf8bc029aa52eca1af87551da422fe4189ae3a8975f2b07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a651df5fc0399b92d115722cb61b685ae39203e2e7611d39a3745782b839b606","proof":"9879bf77a65bfacae7a8f604178857fea1666269c410faf91d7203ace1ad1f0824f8d0cd8d42a3446aa5e02b2564d464582e647d2ac5be494a2f3b5253f1970e1e379b22892a87de9529c3938eb40cbf3f353eda1e3ebfbf7c27a341f459545912af009fdfcc2cceace14bec21caf43f7eb4f647bedbf64b69128130cddf442201ff81efcedf255fd28df49be984afa0301ecb03611cd87b2efab39e2d7d7d0885a30bfd16a94fde7d1415e9a50e1a3110952fbaa46b76d7dacf920c978e560d136037fe142e31f8467c9206959d2115177314fc9a4a1d61ef97725a00a9a10c10e60d8aa3364024f5246fc49eda56f6e65765b12db24d4ef50c5086dee1ad36de9931d77dacbbeaf2b9ded55df4b4dc14ff2d3ba6ab582813650e40f98d7a223e51722f5de2e1fa0357ad65bd51163863a88de1491d37741173883a598ed31c6adbf295c8ef55a448081473281051370a06445b9bc23629d9f36f8996f212087eb34749412ecd24090896e94668382c2f7facf55fd18f251b89f39815a26f297a1864426af2340d20a336e388e17cd1689cc80bcee80c3de7a4bb49e8b5d46dda258aaa355cabd2da0d092bbc8443b446392fba4ea3fa4577e3bd9473b6b428e63c5d10d20be226547493af5f8d792604a13345a7aad00a6382ba66c6f173195e52e90e159125983e770a425ec9279dbd15fded9925d11b054e71f82fe6a25c96c2cea879ad653d1d67b9cdaa4bcb0ad05f6c75f061cce979efd4d77dacbe3aa8a5fd34b2660187a7f7c4080977b8aa6f4a2a60202c9e7ccf369a9e1988f72132f3107628ad0705cce05c428e8ddb619322ae0da828e2f8fd063bf675f0ff272b733498157736c9581d510dc806772b13fabd1b14f63e3f4a39b88c1c8fbd0bfe9cf998a1681cacb581b6353ad95463f7c04547c3dee8a13881b34e4384d30a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"26a1c00df97e20a26265cf0f766d366ad76c9de783914b248898ef33237e5930","proof":"98f1945463d1c9b4d50d2796f8b38184eb085839854f312cf4d1981a8c2bac452a52307fe9d8d6599cf28a5e6418c3e8819f71064a0f8928fe19960465547d08561daa60a97541bd0aefd4cb49bae3d367df99b98a5f62f8ff4b23e06ae4e7165626ac399b0d2a6aefd7840f3be116a2037470bc73dba6d64ceaffc5d6fb22204c9e78667c5a0698399647c2283cd80a6cb0f281bf7e7fc53a2b6612d7625b05d7b525d74fe9990e83430e4ebe88643799cefcaa446aedbe652ea5704e56a50669734d7606bbbfd3f76a79ac4db7b7a632f4b8173df00e306d0e6abdda6359033c2ff27dfb3b1a38ea1443646ee6d8cf78e3b1610b81397c504360e6ce93e16abca16eefbe86455d8df8d8c9b3535ba5e633d88fcb611fcbe9de36afd881c314967b88dfca56f3676d7ace6169c48486beb1f5f12b826557dc0801dd15120c7b92905d88178087be311e0dfd58cfa289e2174d016610f3962f5f2d5050c7ae670ce089c818d966b4e966985b375080ea394431d9c3c7b889c74d55ae9dcc8330c09de2721e0ac38216f8e29f7fdc4612d480293842c8519a591acce390cc6424b4d87208358251f506eb43cbe3d2fe05a03403ae5bc537c8441b5fa0cc5aa96a921822d1c7f82cd2a2dc50cba7659d61206308fca46e0e236cffd0e269a8430198a384a89136ef81466290a3c33e354b120edd97399490da075c54203ebe5d4f5ecaf16534d6b97811df7591b0a978b8493b968bd2ea3204edcdb09aff117d19ce1cb9f5847309c6663fb914399e4f854b64b163a91eeaf3c35d9344d056971beaa9fbb6d54ec261a8d491e49ff91f842bde1ddb2c3f604dab91d93563f5765fe9bda2682d6d9047de82ea94f314125593c9aea648c6cfcf22327e07fe427f0db772bc179405d14c91d149ba21651d482d25a53e262de0b36ebb1f39a2958407"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6a8db2173e1eddaeb7a8d532ab39703611cfa2c90ea8b06e6efb4e6bebd3623e","proof":"e61c45e2c8b89196765267e16ddbc9ca4fbdf799d7e4575ba7679555f3baf719e0b022752abc166ac1a83447085c8574e9cb26224a9c8c8c0abf9961d2fa6a6e6a0b2fbd6eafbcd5d89c24e9d11d806acc7a201250cf7d38bd47dabd00987666d41e5523bcb7736a71e3e6dc1cdd324131252ff93cb8440426378ff4c803d7379246c4ebe2522873081e61e54d17a8041af508016adbf28e9d5f95aea7a8500d5cf565c6667f74cf0b5a51cc819180ddb4232e4c8ba3f4ce5912f049faebee0b2b4df738314e76bba7a316e4829dd9fb63e36669923339bd64f90b22dd4f0f0296efebab07188b45386ea38e04467497321a1a6c983d9598b633db8426fd660324896e566a769aa2a97715594614b12c8997a8385b361c152658b417a2ead6197801fe1e1278aed0ac3b3deb74d90c07aca31e11d6861dd7dcee0d174c0c60601e22ae78186a13c3ef17d9a1fed293e1577ab1bd32912fec9823cd605a4b10631a9e594de0517ddc90c382d487ae34d4a6a45bbf3c95c8f43f2278c756744a23e2c3fc0208d6965d44e2715151814dc38e9eac9816a4050c224e6f39a70b203850e8fc0b2cb1173cae0ef13f6040040a342070fe6428afb32456a8e3a5dffa11c80f0fc417c8663e804d124ad45faa09dd0d413b427481e7fa7f0282c7aa373b3ca34f9278c856eafcfc5c8026f7007196e15cde6fe4c8f7777be36491773a079e1618c6bab9b92347f44f27116a0ff6aee34b4708cc1b2f5252c24d16ff3363acdeeac45e3f607511ae4cc1cb7a4eccb460e9585b2a96e3ded7276fc9bcce0ee21122c93ec778cd52c7bc398e2df991ad8f5bdd4b9c51f135ed4f0de7db626017b3ec597697d4442a7ec55cbe10994eb3a7b15f1f61776b551037d10f9efe070c822a83186be094be65c2db17640d557560a4546a48a318fbe084d0666adc04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1e3f0a40545eeb204fc28b6c1988b04974dcad9e1934d12fd733b975191ff46f","proof":"d860f32c22c1abefa853bca178ea8790e4f2a500a6a9b3e38f217cdf6e96592060dc22ed3b041348a33ef59d0f776ee2ae178ff26950f4a3eb54cec60ce39520d690c20aee74adc08a2e6d6eee70264c26d8358841558709f766fbba09dd632076da8e17e1f5d453da46bf6b67799d93cedf3d06590826012c7d4a7a5d59730b3a2745f3554b8703a8931c20074bff9985e7c622fd2c1edbeec534c3bec105091eb73cb4687fcc29f729c87bfaded25527c57aa593f49e5cba167076022dd50dfd1493d47ca9159b186db07eaf580c739c0534fa0507c4bfbeff3fcb112078006461db9b837e1b6b7be08b51abea526d57cd6dc1915d6bb1b28e6372958ec742f889ff309a4f2b4e3b2eea1817a7b5289a1f8e29ba32d0cf276eea539c89257ecc641af0c67f1bf66468b056a124a5ce22b36d218237636328ee0135503b603c10d3019a922030eb8ca50a89d3e9ecb97252f604e6382a944b5dcfb663df617ba88603808800987c39f04b29bc1f3e5d4d70cf56e9651d36ddbdb3aab7cbe74a68014e53c2cc2f680c5e66976966f1eaff661d40d0ed486875aa161a415c187b200fa8ff6b98011d43c1d739da5b8d97e9d748e2ff0faf762217e7bf249c9d357ac260f46e1079512b3d460e2469d88d6e9e51cd645f90f78d0fe916330602344ef3744d387f24d0a89df973c4e922df35114ff7ed17ffc44303db1460c4b50ba25925d5923942e815ade350fbc0bdef3a6644d455686537a2c62a536004fe4d40e2a18b0c28cfd3f5b165eefe09800045f55baafb153127b125d6a82d5e3b7e7e2eae7ee273252e923d00b5ef1cc1ee475da5acb2b7c8a2a57801a6a3ef9e50c727906d6982a84d5928477fdc4ec4533e0e388bd861adba7c3147afea04be0ce155fe8276c5482ec576dc9d9be451defd24882f2707995769af7dd2255ab400"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"36a01becc36b0c093d8214b1bd65b3ffc0b7911df15ca7850bdd3e20da96ec4f","proof":"f4c6076bec6b604116cf967bd962a3de295b8a4a5348bef942019ec014445216bef0c6905188ccf629850f3f3d478ade44f40ca2ddda6c4aee11612818ef1d1f04ba08191d03a81162ce50ceddd24b6208f86a174af2c34f2de45d5ecb009b7d4ecb2be9d313a33b0b1197a775e0bcb1e485e4aef0c291a32ef519c197b32c1a342a8a297c7be98794e3a8c1f39324cfe7730221b7dc24dc90b89a986081b3053c9700658509cc7ce83c17b7c3ff5ec270e17a5501e9bb8a578f5a2984d2a8029dc465636f07c030b364a1daf28e6a22f1414fc2eff7f030a5e7286d79f4bf0f708befd35de804d9cb18764090c612da6706f86f255b72b4d31a291c27ddc87d7ea42b8d468318f1f0e625e44024b18d1a59291d6d093a681d0526e1cb479a716860452f4f5ad011dd9c34ba368ed51071977e99cd578613ea65ac2cbcdc0662c07a306ba3e30aaab6076c37b609f0ee930684e1437e1a5d6688b5f20634ed0a60d5f3071f628d6266d2de7fb6615377a9511a752f2cd0ca1b4296906001294cf829f97e9fa2a9f724f476c9bb9f58821aa78862fab1423a9b25c8886f911a6fea39d7a969c237b90c86ce0683de30561ac3b9ac0c091dd899925835cd941e073472e48a4557cb53da72a6a84c5129aff0c9eda4eb8f7f01f9cc3999157b6969ea6c9a8e7afb879ac4a5662c8d4104cca21903fba288b052ab81f9f762050108dae6c3c3717cea81e8f5b3baab2d87f44094ff737bde84f519caa24a157e54368e5c6a9c1c5d8c419c1eff39aa4d4f9587a295c343c62a9c4f3b6c73bef6bd21503c72faeb3d8aefe7e5e1536a3f210993259d70834e7ef2112b4f8e025e8a4dbf95f779a9c895c58afbd7abbbcc921ad0a09d4a5fdff4a1a8f27c4d80f8bb0b9e46457349aa2368db168c3b3d28d1b223bfceace08860e7385739f7f89f4307"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b6d32d983a6c5e43f435f5426ef52620daae57fe40c521ce5186976fc01be276","proof":"b09864438b5ae12663ef6df0461971b27b0956ff736d201a05df68bfb9f2e410eaf61316c16568400e7fba032f700efd9f6a919ac6846149e0e4d3b72c5696073af3cf3f6fe4e4854a1f07ab2d59ff46eb2e8eb3ec8a9743c47c24f1822f693f58264a577d3352c9dd017b144e790633344185388b08c21db80b474da747aa7944ae743207effec955de68b998250668d3ad59efc5991e2653233cfdc49dbf062370710c16c1ddb08707ab54338e80d42f65df3737bbe8e45e769cb06e09fe09b7bb7fee8e9bb885e595a7a0c4d9dcfbdece222d54da26b4742021de58deaf05e075e7b8b50e4c3b07c141e5e552c038a239b0d76dfd9c23d85d54588adff73448645b4ca6a6bd191dceec5782357651f0387ea90ebed1a31d0b9b28ef36ed7d2ce1f8b8a5ebcd84940397bbb091d59c9a80dd357b7a0ca390f4f447183a3c291612f8cfb5ffdaf5a16e0e358f67ea4534a56dd20e67c8fda399f0f9c0f3c675dc9a2992f94eb30064c13de72e65b98328a21bd9d2e5ead4cd5e766d73151800ec45629e5ffe6bc67aafd63014da3ce91ead795a1539fb0fe8cbffb96b83532eb4063d3defb340506b6a781a814eca3593b931de7760b85849613b105dab72287e480ae80f7fbc6b57e52dafce83af80d9e24e6d95d2f19e7c5be5ec5e9388528235659a6d9aa2fb6253ad0349e371e3f0098f8aab07fdb7ab977538ffcbda6dce6b9b9271171ac3b2794db28866575aa8642884e65a4ef61f52e3d68293952fda6a7efb13d58624e19eb649a9c1369a4e1a2213a81539dbbcf76ae78825a36f4c86be8f52ef9a80b37e0c3ce0dd6d505c91b651c7dcb9216d412d63cae606632eafb0a12e062279e954e1c4838ca16ba21db2fdc409ff46b66cac389da1830d8aa412255e38a673f830139c3cbe8f0a1572348d253223d616804aa47db86b0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c4fa08062f4d1869f25703ba9fd9d2e40dbd1908979d1c0ddf5290c5a8255314","proof":"8c2677790cd8c3f4461ea178d55323d5b9cd9c879010087d966b7154dcdf064854b9b75bb1f7982088fa7f71f640e5a012bcaaa4d2f4677a3b0d78f07f5123304efdd7667c2f68367aa0388ffa5650f2a826d8799ce1ce353c1eb76e718a071b8420a0758f6469c02cf621b033ce6addc3a09050851e2c7a6b5b1fd26526425423fda26d502e9e8dc1124155633c22358263e0bfbdb619e67183ad202542a7044fb28071954e10514d3ab32250d926b4081bdf8640832e71a8a64bcaa9465f01a6efd01676f96dc36d1e754f786ccf3dd91cf5014c1a2322357a752b96df5c0066999d03b5d8d77e1bcaf1bb1e741634b5b29ae9a51ad17e65eac2997e9d594c280a9fc4e5a7dd8192d93745dcf21eec8f1185bdd20bd8e22758e5ac765cab3ea65458a37bf65275252d1a80692c4f3fe7fd00b2981cffbac15a2dbb39ee164030d0bd60cc6cb936d2c93ccf5be848d60655111dd19d14ec601ec4e8c3b2ae35de6155c0f179dec6d1db5bb1f8151ae788a5dc80c2cd90a7d1ee1486e645a005c0c39e2a7e6d118afac717c8ace9bc6bbfe9b50bca325a4f9fdd5e3a7e01316694bc405fff6384bb4c4a71b232fc4ef475cfab0040814e37ccb44d7ddad2413b5280d52dc883ef44b4bb365a4b2d4847c78d42eb3d10085153de46c33602fb30185ecdbef58843a2b234d28a52a88e8223f62816ede1a658017c5e6ad9225154a8ba7c7f115cdddb9904ee22b186da8a85c16f359dfc8ce362e849e8d4732962b0e635aff1a310e238ae21db5883dbd69c6c23c03bab01db467043b048590764cec431403b7de6b1f547d8916402391bbf38190064c51ad9025c8505b159576404a95f39520c33bda2cebb98c9a2b22b574fda0d04186934fa0271e8c1ada004fb3fc98cd920543f5bf42ed4030029649cd5867782840663998aebbcfafd6204"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"62386d9eda6ea9257ded5299699ef0d745399471dc05927bd4cf115fd401836f","proof":"062620e9605ae5442757a41cdc57ed7df5c1cc58a6fc2c507d08d3fd246a6f1bfe60a739aabba108d2b39a9281a2b8740562cf7506030477978b41b4e2fdda6ec2656d05a1efb2c8fb9eed3bf95b875e3f6602b7e6672a23c8dc8c048956d94ecaab7fb3c76469332140d94e9796591ce6f7de3281c4af02b62a2df0e660aa653fe38f60e6bcb7227fae4b9adc309f2d329813a3a0b3460acc915edd6cac2b0763178c9f4eb21020893d90724457fd994bb0ea9a5fedead2bd8df8d110fae507cf431d7d4a01072505f44bfea398ba503525cc66681372a77c764e701651b60f7800f187f76b6dcf37dd054048f308803876eef8185cff719e6bf9eff70d943df86a0bbfded867be0ee610a0c761f4ed694ddafa2fb4b81cab25f1d0a684c5232e93088a53baad8f82adb3bc37c60cd80b7864f4bc2e9f68e9d54074db7a50719e7de7ae5b91687d9cb8780e78d0ea0811d1af251801b0e259089cf4a4403e6bd2f8364b7a925ac852febbf83d66786eb98550c7cf420b4491e42c4a0dd3ee5b223843a65fac30f3a8c8ede8e9db8c43460af3cdf94729c05abf17efdad4d808fa32e6bdc416d5a46c852d0f6a7218b644fd1ec275b0c050e8668970ce54d54f307b63b336a55bdf0bd9046ec4fd9912957ec0cc290ea54e33c983bcbf316a61806d770b26e2152f37b2a9bf533b8ca50cf3512e864d19b2d35bfbf9b79f3454bc90775e1ea6a6a00be1db20c8428373c314867b7ba5879cdf174a53776b0a7ca2e712931639fe21dd2bbc5cdb6c7a5ee14e26eee9316b5e9a86b559bab2ee651695917d410f66238356ecebf79c7ae0369663d1923f96ec9e1c84073aaae45c057a084fd52a314d7fb01b4d08e4db77989e44fb8e31af51f774c69c9a3f0c02dc73ecd2c32f206fd9852def795ca3ac4301e0e23a02bfb527600338099c470f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0edbad4be7ef36587939a40752423c8d1ca7a8f42e1607fece41aba806910849","proof":"eacac50a595b93a1b64390367d359dfad9cb5400954e24da9a11021e4ce30b332a45f19175aee47b00f159d3e58977264c9bf00adaa705eef008017578fd8434b47b74dfb666c9f07ec404bf86af91fe443d625066c5e916f7fc834f6698234314571e955353795b4f79fe38e83bd541651f5ac6f28c30fc4a25707d6260053a72c5ad68b248fec6c4f0ca36c5f1a675dfcf128a0c2905a262bc6971f4b9220b127d75c5aa28ebec5720c8be8887da892cbfad5116219cc5355ebf709e5016086ebcab37eac59e082bfe3011996eaa702992e3ab1fe92e7307b2cfa358002909fe9bbd78a0ebe0602fb4ad6008be4caf924a150a483c7332467e05357724c7289e8fdb9f95b7bad72a9b864da3caf3d74b1e917135753d09deb987c73dc5ae54002ce0e68b98bda6efc2af0fd14008caa98d119a59beb2bb2ad6662746baa47518750ab8877394f2df8fb56bb4edb4eb8a4d85ee5b9f1a367bdfc6522e3e3437e431cc0b4169d79209462374cf454996bd29350252c34ba1cd583cab902985301e78f1cba6e21772759b349225bb765e14c52935ee3c234dc91fdec32d1e5c40267f63bc7e25ed89c8a03beedf5ac139f2c559657dcdc0436aa3e446c7b08e25debb700f767594850c8f995282ae45be718d10567b4ad7697de323b034b7d122e6751f2003de5af72f4a165cefc770a6e2387e56513f67a45d685e2e8adff35576605262154cb543e86067e9ae5ce6d53780c65a3aa2634f89996af0c8afa4175c92754c1a8a852431b93e65177ee645ccf7f5345e94e9cd196381fb7827731210dc5b2da2ef1a1313225ff73a705136cc353f26b9a96df39983e25cf29b1a1eb5989efbf8545d8dc366747c70cf64f753f76a73a2d81ee405ed17c2bbbc1802f2d00b81d33241256a6dfa015038d4c0ac95b8588939ced8d5f3d6440e10770c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2c58cbecfa83c41f02600ee61193ee63e95ac22be973b2f09ee1e7b43037a954","proof":"50fe93147ca76dbb3c4cc625bcd295de8358092ac5516897ad10087fc363323a40c04fa2d056aedad80140ac107910896864c5068840bfc2873c8a307b0a3902d89861da6b500a7bdf3b680fa0986d16617a1707bf5d75de621cd86732fa7c03da70df4895dda0c467418653b113bbae47f2d78e21e5595a1d479a0dc73b487e4daf73bb479d788b02ac6be65a95767d21047a6cd4cfbabbfa1e0b1513cbf40ec5703d15e52435a569fc3b30bcc2b0c3be095fcee495974173f6aa2434aa0b04a12dde038d3a5b98fe54b9e52e11ae6276855011242017ce96bc548183cdbc0948f246e4db4747c09be49cdf9f73dee9422b7867902a00faff7c7bbf98deb402ce4c180e2937fe1a683f730b2d875defc99d031a2f8e6a596770f389dee3b31b8eb5d0067906fc99164a4e795029c78b4eadb6eae185ee2fdaf8ca8da2e15e428a1a9f0e0759a8aaf0e5edc01fb6629080c6cebed814c476fb3075a54b0cd24c0e79447f280c554de2a850ecfe8e42f0aa86a7fcff7f4db2d20f0570be297d1eac4c205b48c2160bbee270f5d70588a9f4430457755fcdadb921ba10b3a7822c7afcf5a4ea9e27b930cca0c2a5becad4eb3b6674f5e8d3086fbd851ca4c15c061448acefe06eecbb14201084bdf7bf5fc66da3f2d4840439017f64cea36e864a6c548e6cb19f46f32c93a21bffa69fc2df4622055fa083fff14b58ff1fee3e74fa4dd0a9580062131af6a06d10e991766c100f3c7b372ac5491d57c0b388834350cf7a7da7ac0127e5db20b4883ce0d4ec8019451af9e7d27b21562957102178188f434c7aa2f63a794325c2f4c66f009d63b6ce9eb003ad7202686fb43da2492ff7b482e2ab94641964f246ca3005757ff512947fe8cfff1016e7a6c25f3703db0c168d1c2b77a5718eb5803c80f5d73e0e65fb195eedbe716986dd93f0ba0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e43f3039d6d80f743e25014eb477899a42e11618fd416f2cbc83b53118b3447d","proof":"a4bb1dc83bfc106749e8c2ff718a15367c356e0709fa97b3eeb454a2dc28db30eecec238e15f0fae19dc9635b1b97a94fde59516eccf1c0819b955634ad1d17c18e3eb29a47d4e21d8572755f9d94dc5baff8afe7754e3f0a3211edb88b454143058157f1e8907e51359fe0d879df0757142ceef40b4921cf35625b966a23801e4b81e894b5dda0bf90fa93d3d8c34c0c68626ecb59efb91eae03d3d6e5a4a05d0af7d8031841b1104e1d69ac58fb0fdb10bcd70f05bdc719e705b4214f44d0a015237229c495e69dcdeb54c652da804f19307a6444ec2a414a294381b635409743ae85f2a70ed7da39ac3c55cf72463d0f7b4661cac694cb32bba9db2f17a72887afd83d9b19a386a38280fca25aa96880fe4cb30404c033ed56488673a2a088053b76dba93295e6b83050570e15a39f43c7d7e4eee0b8bf20e597a39180962624bd01d7f7327f69ade70d15723df2209803e1ed73e1c914c8f3e1d536dfd3c1ce5c7d37e010239db9448237957d6629ad454367f9822d37fdf51f2adea0b73fc428bf7d62bb215714021dd132ca8d973d7a313867aa875ba455fc6e40b42676c5665c6c9f8b971657efa6eaa2203c9872be18346b30fdd19aec016dae1c46f4cbf0836321b3c1c8c511d9f7db2c6a3b2d47d4df9ef486d7b6d01d4ac3e1722f02691dc7beb9e84fc43586bbaad305da00e7633f6f0f33395302da644ad483eaac248c14e4a2a03a9a37d7881fd4d0026f955d83c9740c29b64065e61b2a7331a2efa8fc5b11cc73bdb2620bba93636e9ff2168712704a8f590bb57f873a21e0450fa122f181109959dc217bbcf3af35d17b2de160826e396c7667ad65a5d0d376f2e4ee2bcb125fef763fd44f82e43fab6d82e70879cee6200c13e27f8c5077f3c0151125c94688a0a3cb190c9d95181263102d93338ee4d1c24c9dea8cf04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"02ab2dc801c85e9462e94a24e372fd2a4ba4292e94909427f1e3d735cb395011","proof":"a6dbc45b394fcd9445be2ec509fafed52b88939ddaa2a96f47b13ea47520974c14a3933548967774b4189ac676add50d468f85d183e40d9ec9488d3b3c122e73a659e068b8833a8af9c2e11124d722ed29c8fe91adadd05e710e15abb0591a5c203ea1e57eee4e2bc0496097d752ef46f96db1d54761fe1670bb1d2f39c2933292668ad09ccde9d69a3a6e495b043b55e5cf8c514f43086f92d66f2ad21cba0907a1866747ba8b19b21796fc5fdf4121fa039bd4f2d845cd1b8c548236990c02aa19c38ebaf9db801681803de8534f134f82589afdafb7b54b72c043bd455b0d76a72d07f67aa824424e01e07d9c2ffc74afa319c5fb7dad58bbd7f023298e292cca3061a89ea55b34fdd408f68a9d3f77eb03151cf4abc854376ef2b5850431123623cc6e52426802e5116bbacb26ca0ca1dea4e5d34615cf812cd336205009585470ad68b2f25fabff0174a057aa883e8a5a9c678d4eae50ddaa6adc69de1a365d8c344a6f8e0e3726a0965ac00f43a2405645385fe9aa98a84327f0cf240c8e97eafc033d264cf7fa3080afbb15810045e26f04e58e9b6dee7c1d3c3348526ac68c36632f1b9dea7973f3bd32fcc86dc8b610e83e15f011450c03e3cef35ab83e718543ae4639288cbd3d9e6d82a4fd99dc6821972c45adf7c8b4ff55b93e8c40bf36e3e51edf26572faefeebaae8916255dbbdc3e17374bd005a0e33271e46a09b9449e7d8db4cebf74b7b2f9c947752ac931ff1da9d96a4dda637fae30e76d7d2ee42a2a712ca1a30af0b68186f612bca9a8fa8ff1dd8d46cfcdc03653262eeefa47f54d01c3d9748946fdc7e0723e431deccd199f4a4341fce68b0a31971f4f173a63c5878df51f20c80606f0ed1243b9408ba13b2277f5e9594224008703ca78678dcf25282392b8a4564322ef64e87bd1881d9a3f8948b3b8a35380e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3e8060f4f7b78f8e01cbc23bdf0e87764bfb46e840d98f49c1ae262973d42757","proof":"ae27ba997b53ccc8a3d8cb2f67a73ca51c506aa3a9cd0d8e70f21a545d5db1375aaa7ea70ac26ed8aa37dad79fd734dd95d7a96aad823749e5a85c3f77b47e0bc62d6245c6cf68fbd9fa1016dad617d1b7f9fe76c48c3eef4bfc4ed666c5291930b81f1571f327310ed1b3e1d5f4fbebf541441040730d481444b00d165d5930cc4ace0c38efa3b18c43df7481ba82199c3fca5159cb24aacea3a7b4016d5501027bb08bafa646870347b9abd2fc628d03ddbe2702f78416b1bc61531d84f90ab3a1bc82e5545c15e02018d93a2bec1b68a8ca24833b217667e2384e8899ed067289b21d7623059f6e89c55fee208e4508e74bf0db08ed8a60c0434d7d52fd4ad0adf81fa726c9d0059f56f81378540f895db2ccf251bf7b5ad8a9993ee6b801761a3e1ea3ae96f8f55e6dd4f364ec0a1cf9587c2aea7cc9f2bf10c777f9464cfee3ee962d81919ea29862cb5a23f920050e0fb19e0e8645bfb6ffb61d4bf875aaeaeb905e130c9b788f6a3e391a6861cb59e26a6c59029cd02ab43955896f38e6896a859b03a8d34f2d66da936752272c84b26ed7599dcd3b299cd1b190f06c0c3cc43dd8b4c01a44dfe8a590e3e313705e01c0cf64f2c51d0f271fa93aac10b47328836c3ce280ce8562169bc781c85ed078e42171b10e6a473990214dfa1d8c94264059c4c198208130bd0b11619e9dbbd061a6eae76a858e5fe6b285133daa994ee22161e293cb0b45edadc5436e8ea872b7a760062a4681b5525a031147fe57b3ed12caddbdfd6256f1c9b24b88918b15f7792186ae4b5d9d995f28997754f43469d28df1a3138db85670385eb3c56440ea685ae402fcaad8b3b69c457496bb4f20c8bf7372ac9f2a89a1a8fcc9e62041a9b333072ecd5079b310656f02d1fa3f3755f9b481185d32fc2febebe9d806b28e8402f0c8012feefcc5d86d0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1efa565a91bb7fcb192650a4635311a2e58a05a75a5dd16e8c3502790e76bb3e","proof":"0eaca35d37d805be57ecb6aa63abd5f9b08e25532f4baeea1caa0fb23e3649029e5e7f8f66cd71635d024f90ce624a8159b63e1e222bc63e4ed0441131680156c4aba8b6ec42bca470f1438fdddbaf67690f72fc75d144bcc4f24153b82bad64aa95cdcce4e74cb5ff1d8fb05c6d2dc7eef8397542aedd4457782e6af7533f52b71a2d8e44fb6bd3997a64559706297e5c01b3ecc5eeb347f605f8ebf8b1b503cd634ded0b3c296b8670f068f7344189deeaa4dd226c43b4fc2b714c649c7e0a13e8d9f4904995f295782df749179766367075d1f8f93ec64bcd64ad093a2d0a84b39dcf3d8fe66fe06574f6b9699bbafb472b84a067ab0c8de936c982ec2674d49fde3ea8b36f864340f46dc770479baab3f0d21473f16479765cad29538a362ed7ad00030336a8b3d65078a6abd80cc751ed6a628a39dea513d3e0075d1419c06b022c15190e5d6d279f22b254c4b8219f2b896a11a1feea529dfb1113612de4d0b2c0eef30a7d0f568e4cd7a04c6e5470b76642575160eae8ee876e955c0a3632b4c269dcf7242c7df00eb0b04c94f07a443ffdc5704d56a477d5c10f5e708c2de572d6bc14bcd979f538487f4d4d8c7118ea7f4f408356ce960768bdcc713afb3e46e8c186533b341060596d0cd695934fd2898fdbd3476e91c42ae1bc0864277d751e74f5d5efbc1f83c0f1fc27d4bdf7e172ec85dd814ad14bd5f940316a64364eb345830a937ddbb17eb7776af2f6e996a7ceedc3da65e73fc5927868dacf7fd7dc955e2f3d9731f240eae7addb4fce10221f493a4ff963e100deb46bf2bc455ea437223f5dfe25f5deb1d1d600415824a37a3b766f9d933acb86cf18b963911b41f24203cb4acee179c7634dbd619b53d3ee664ca00e9b0f2789ab010366b60897dd82bb40327c75d5f45ca0f2a2b0f3bd6b2a4807311e1b2c639301"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"28dae0c79f56ae7697e1aaada8227c1f1e11e202892a20c54f295ac2b4bdd325","proof":"8491b57fa806499048505c002a44508680aa8fa638a5072d59b9f0b0658c83705ced1507914d56710f000909f0410260ec2fa6c6854bd0934a71a6808de2d536d8b048ad63ec1a66a145e200a3ab94eaaf49ebdf638210dfcfbd49de2e79157b864da13aea241a7b7c41961c08b85a506e36ad5f51374e21f1ecf684fea3347a7e70990fc1d0f43f84aed041eaac4ffd04e8bef8d254373f7356d91f32496d0ea3ffe434e820e9737a543ad2db8c9432d454db832a8ec1173af2fab307ff5a0b6f2449ea852314758999bc71c9a4efc04e1fe0a94f8f0352be10ca94c3d206043c5cc10f5ce5f5f6b74dc37319f428e494ed7b5dddf6ddd5ee0eb85ece2a7b79ea64d985e7080813048fa541cf983fda08d6f65a4eebf1b86e23060ee7b3d92d98b5a957eb1e1b16e5802bfb2a9306bb85eb4c95d167e64d443ae0f967f89a78e47e58355f140ad10062a7efc6921a14b319d6099938839e8789f0d204a65e326cebe908987301c63495b3927bd05922350b7a904ab9c6f92dd80a25ae4e7c5ac8f095fc6f2514db9a3ac6dc32dd1387f93a7555a18dbad4b0696281a38d2c785cd890f14edfe85061afca594b72dd6b72110f3d969ba3ebbeff0fce88ca5c5e3a375b9f355c96e57409a78aebef4c122aa1328837f6c8110a79d778be2d2f4f12251ba221fb538b86cfc294388e5c286fd0798956aee4fa39d8147acdd6f726fce20088768824f5be8a1fb7695123f9346477d7589894ed0edc4ba744dea46cf082da147721d05b59b7a8614457fddc800a60aecd8247e2b79772b70fcf56550a62798dd1aeea58c0f56666362d1e3ba7e6317dc61d449968388e9d473cb5012075dcc4a94838b3d98ab02d8e882a8f55901758920e435cc2b902c5eadbda0d6e33f0ec05b0a404c16bb04b9222a5a1eb38509343dec0a8468374224108600d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ac89f71cfec77fe321d6bb8f7a6a5ddec6ab78ff1bc58c3a6a66f1aff2366574","proof":"6c568c5f4ad807a7ccbf0cbdc650c13723415a9ad80135854b38e2e197131d0c6a206843267e38f22438b7ed57fa38c57fe8595ce3d2c0322c691bdb0d17bc5a2e29781f2e095d6d5609daf3dd43c59b6bc150965190f00242bcc5da1b30e42028bac960844fe4b11c458f0200a3aec076cf46fe3e41d70394c62da7c09ffa73a6d3a7a664e739232a60fd15dc70041d23bcf8d9f6cd49025d43d5f1c4d9f3052f1a79b3317c265db967d6042b81ce34b801255615b96bd14efa0d6d3c78bf0629a63bd3015bf9fba94fc15ec1a3e1992ccf53f98c5d1c82e9ea6370ebc0ab0f4ca62f341b70bc32a685959727a74eb3bae900bd4726582f0916b90aca913e32505868566ab72c5db8091264a25a5a9ef6104d59af96a43ca171e869f08a855ddc22ad834e547bd95327f8c5bb2fd3352a2a743a570b14d27670d59290de4c54e87f5ddd7bdc864b2a72f778a05de49acfe9003becf34b02977326bbac4cc813a6c9eb06e8d7427aaf330c96414c767e265d7bfee9483b00e6fe68bc1b84c4305628ed8f8404b0e77343b9b38d3a99aa6997a4cf5f1e9a3d7c492d8565926e64423d57649185cf3b7da230fcd190fbd40864fabbd4273851698ce20695a5fe56a63efe6c4e14183f44c373134edd6d7df6a3405c359a6c18b7bdc64fcb593b2b3cbabfec381854b8b6e5faa4b163ab3405a65f980e5ab8c4163169e7ffa17d571021e005ea5939d590fc269b8fea3280aada5abf8ca61c92df520a186363c975b6f5d8fbde9b71efd4972a70ef1fd24f5ed3d589e64cb03aa33c4053f8a0d462ba8a543f6d4061959d8bfe31d3070b43b2b12009bd5d2e6a489439ee27de3548c4018a8806ee1c91f1717073d32c817e4da32ed8eae16af7e7b565256573ad08ac8dac573e5c22bc89c2ba6bedd65a09098d7ed964107ebf761ae6f0b864de00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fc31c4ffff885284dd3804a8bddaece71fa491745bf7dd17196143062fae4507","proof":"781606e325447f470ab4b5cc8e9a6e79a87f081a50335ef9316a9c123854cc58fadfe43283d95db77b75e3c3fd35fca683fc5db5a39bfd9dbd8860af9bbe300d92a3e8f817545561eb6697eb838d707b4bd434ab52e3e16c3afdf11c79365c744ed01368c7a1bf7ae06b06b31c0115977520c729753b0f5cc720b2b8b77690299ec9d05b50f5c13d062ca0869c5924a2a0c89bce3233cc8631c25d668cd17a035fbb9b89556de6dc0dc9507376ce2326fe1d1c6c9ab772855138335912dc7d02d5f8589e8f83365e26b525534a79d9cb43683464379674b8cfcac831dafd030bda92393a28f179a7090a8018a7a6f3a6c4fcdd0aa12ef9bcea28ecf7e8753772a049957507a46060b4a118e81728bd1395efdaa6d72e718e383dc4fecfa22202f60651fec4be8509a692a2770da73450870095f15ac8f2af5ad55f98c7470673f6e2776f3a35fff0be7732aa56382a0084fbcfca964df6e571a108cc3cb47a20ee69364d945f32e55597e46146e1294e708774ca8e71235245de9b2d4eba326c3e0e380d52428ef9ea9bfd1c22322f7571a419348fb524bb51cd692d3114ec3aee38ac9bc6ac1c896c86c7ab5b9f410961a4c388b88f01610364e700878fac132a1d2f93082b1354febefa2f9e8578c94ec3229746306df56c7917a5c0c5e9612e07746a5ddf4249a7c3fd0859ec6fc5de9ed2bdaf522ae0ec4b7a3b99ead56df8378fca6c756b4c4533d8e07d14c3e5ac0e652283f6af6a204a174f2c13e816cca203773d559975b733ba2bfc7f92f907381eaf6b54121288710c0bc0517f677867f1b08f056d120b13f8522c048ccb43fea3f8e47245d21ca9d21983ea3e6ac566b42401b6a7b713964778b0eb3825093ce618f7b50eb33da53cc9da6efd0909059594023f51ba620de03ba022f2cc72229b3a8c4f3a238b4f253fbe705f01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c8859bd0eb12561271b52464b774c2c377967c397a06d29402e009d9af9c7841","proof":"e03c6177adcb9b9e3aecb9a473ea575b4024cc76257b2bf81fd3e9374a48e2087afa8c87f88666c9a323cb466e399f8923587b03b160c0100788a3af6eb3db58602d78805d9563aa795040805c55039365ae78afdd6a525a1e497a4f8004a90ae669414ecfb6e8376b6a6788a14f0bc5c6347948063b7726f525e8a6c472bf3ef055a0624e59f77cd5cb7765987952c21d91a76f251e4d176a19da7657f0780f1c6020870fabbdfa27aab41caa088f4fbecd203e9b4c2fcef5ac87732a5bc40be18f8e7b7f58086377bb1344aebc0516d2a9afe55a96c94d559ac77a6864ab0276de6eb641157857db3f25d80c6aa6bebf1e5e9037d39dca9ff40bd8390b2f7a80a04fb324249382a8cc03171cf4da6049446d0ff169ebad0fe54c976a181174a290c863e16a1dbd26ff3abaf5fb87e931a6455478c31feb9b19c0d80e147e1d5cc1ad16f11abccda8571caeec129cb531b6ca00bcc4ce8b306681e06514bb2c8c858e542368bfd24fdbed5b2bb8bbf9019b4bb44c5edf0b51050994c799ac0ba2916e7f2b2e31bb3fa09918069148d0fd8030788483b099503e25dd44787560d864137ee31987693000e55e166cad256c66d1254d9a05036faad0b1f5e089331c5cb50810a255483ae1f558b4fa26c43868267641348d8863b177e6f1f5c40750ea3da573cde1847d87502918608b0b9af8c42560cb9ed483e822b5d01d182158199d6d9fcad0a628b07ec9f0025f26ac14eba225d70d7698d0074d4d6b5e14ea58615800c4b76ddb76cca7855e2e39af411d0b6d6d7f3252aab683bee9c17c684ff62eedeba71e58b00ab0f8fca8a0cddef9d659de1ed7d1071285a2e886379df32ec1eb0f27fbedf37933363724db4ddb9ad48a45c3d321dc9623052df0057700a40da7006f73d0d1cb9b02d71e38841c0f644924635a54b973e38620da0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9c2697fe566586b36fc9ac3c5bb295df6487ec8eb6ff6df1e2fb0d9fcc28ec75","proof":"6e4ad3c2eca57c8d21c93496ddbfa1a0342c3c9e104dda8991b1411f7394e35b9a89f9dd169568d8640255e3ce9321726ad17ea5019b0db81b99ffe7a525423d90b78dd424868959ee4ffcd446feb18a343572251ec6fd8f6e93bd0bc6e9747298e10d4aeedcd92e1e67c4a4152d7ca367902c02edfc7ce7ff37ec2719c6f75e1bcd915b59e18b7ee83ab194626a792baf49de6ac45158333a359a4a2cdf4a080d6f259a177f9f37c62f42ac41a33d2bb0565e36d2f61718eacbfc052e600602697dc78ebb205f7a86c915b2fb93fe1a4ffc4bb518968ac44d19976050f08c093cbec7a916ba185f576c8ddb8e54027304290086824f543d95f8b3a43f543d2cf628e6bd6fdf630bcac64390a9137eee3b60e06b1f0fc1329305953368d7f55bc640a133a9e1d6ff9110d615c3d067aeb208c77d9074f6ef019dcb181fb3ff047a933e77bb128bec906398246acba385ca1a7f65210102d650249e88098d0535e83b038de756374f4f5691b8a0b2ec90e3d68b4995643e1b41391878d2ff0e750e673405205cd78f71152f25f615118d026f52945d273cf2e1b709cfb9fff542561c8460fc52fde43b79ca37ee15df93211091e09a3f30111c2ee5b875de0e115abd02bf283039bd7ec54fa324cd1992d2904ec5d31e8e4b906e83585ab6e41c60cb002a39dfc8cfafa0f41494205cab7d59ef052a492ce7f4f4c5fe460d475576f18eb594f642b69fb97d2e2b1f2ec3c00afa774b8badd7210e728a647c5d4e989f85cc9d6590eb5a36d03a1e41bac568f8c26c03911ab747abe336db249b210c6465a24084ed896edc37b196c3c92150de9cd7bbf813884a41665413e8970555b56e1b8591fa92b88944c61ae160cae8cb1fcebe6e767f9a529dcd19d1e40f8229f0a50df45d056d18b2f32852a2b63d88f104ca3736282515a594c4553c06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b428ff28e9639137f85c273261978d887ad9eaec7b214746d4309aa718ca522d","proof":"00c29cdbcf2fb05e28433ecf6e359e93ffd658bd37574859a07f7912bb751f768c1c32cae908ee56d556336d48b0b47006f1b432de9c28ad075f06770196964348dc17c2c1687c50c90a595397e5febbe592bd2b35999689af296cf2fba983076e8dcf5ae4a72f18120195a63c0d72a3b835956493fc2122111107a7d52f3f126a2fd469a64f9c49b13e3ceadef242f39695465369acef8882fc3ad559d0f803ec633f41bb3a6119c4a1b4036a16155d30a7002ca7c4024e6246375f8811f60a23823ccfa5196f39c6bfd88282d710dafe8ce622eeeffcbdb537b60ae6bd28029aedc9c0cd4c025c8a3ee1c062b760cff4cc54579715994f7f06f468f9836c2d160142ecb43307613bb1194e388a44da10ae5480be57ee8e5def8c3112ae2a3ed837f5716a36dad2f4755f74a02e50c07b8f22dcf98b018c3a3261ac0bf99f141a98ea8b26e3ca079009563ab4a16d1486d05f97f900351d8b528b713575370f82f5302653f57fcc078456c8c12b70bb51719b662fa039462cd46f616667931726d9d1c14b3f4fb8b6b6870083b22157790ebace33472c481c54d776d657c72cd8210bca51683b7ffc540a03b13e10efd4774800429ebb5632ab3b3417c6296cb49002d23bb61856137da65310aae121e886245902fc3792a36647f46569ac5d308a23c1de95b140567f55d58500430caae915893f567648b67db2ab8ab8f6605acf39b91cd861f03973b26afca4a85f3cc25c0de931066032343a11d699d767b47342305376c4dc136939e9aaff3220cc77e124604272c4c25d38059c000270b07a38c117eb34732ee6d477f45e27b5d86bfdb4109ca7c489e208f3d9f11218ab163a0d86b0c5570beda355c328b1b4e63df5747433991588bc43ae643b9a0ddc9aaa1ca823bf4ac185b43a16bc2079c5078c285ad216d39e756b61061fb906"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"845895618eb10db29fc69108f60c492e505ca58f58eba9e52941ccdb7dc6f66f","proof":"7642172955cc52f41e3dc54288abe34c41b378f16571ed994bce16c2e9094f51dc0150a8a45ba8a36b83f3f9778e64b38589491583ecba92fc0fe150be6c4f5c5cf5a05b4cadcb84a20ceccf0936cca4585f00708c308df54bdf556a100f3e52c41ab3dd23dae4a8fbaf4d7380dda845d51c96dbd9ae4549837663f9e9f10d49ff4e445e53fc9f35e4f29f8db9481c68473fa7edc2fa3fac3f50cdf33d75ca028907f65be4016dd39f66ff6b7423e5b75b26e76e95342e534bc3d0c13465eb0c198fda45000028a51159f71e2a8f32f7313b25cf3f00568e9ab8070f5414e201da01042ff415cb0ac548326b296fbb8078a18355ddd6e13813a290477a322b7bbac8a430764cd6faa5fc40bc7686a2c2b574436b8811f44a3bd85617f8db525e06fb05767e272b17eb0d24c6781d14d15bf3f0b520717b568f98c3407c04ea0b446864f3d624f12021c13e508abb4b0db2dc91b7595602b454ddea03f0aa53781e0025653e268e95379302d65614cd4b2b87901b7f1d052fcb49039bef882f57267c50490c45f0ba221f4fa82b5f9074d021ae0cc35d049cfb673b98769df944c41eaaa660106a3b0687498fbf6719fa474ea4e44c1aecf3dc5112264e02c275fc9d5b64b0043fcf7a7bf2331681051a0a3cf915acd29ad9a6143e5b5cbf1159b6d31d5f57e9a746122dd4a208459538f27d5a58fade1f304e4f1963e18dc63fc0afd3bd0f1b1a63f56a3af70228f40d585bad85f2e995a5585a73fdfdaae32cbe9db427ff4484d5efeb0a84c09ac6e014eb9c6b0832d71b3f455c1d497b5748be92a01e650896f5ebbdbc1058da7b92fbd5ae3fe41659f0d142446620cadf3b634588b4fd36092b281190b97c4a5a24c937d5b12a1778f6fe771dd37fc7ad07ff3ba1bed6a28c34e85f3e7d48804d64610f37c2477af133304d94a6f5e70c03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ba2bd8deeb6b036a0d473b280e1ba2d0b6e12fdc02960af4601d297681dc2900","proof":"ce22f2bc230f48aef8eabc05c985634bc6de46380328fa29270503f122d0381b8820313caf7e1854593bcc1807fe941b349a29d8f2509d18114aa069e2e03d5f5cd82e1b611846d9787c4df8ee1ec1d16d47c6ca8eb375c7603a46a938cdca092ea298048c94ee93c2d1b31f18316b23599017fbaadef43ce5839933f5db4674ad957589796aa33aaed005e04d502d6dc3078d1e363e7542b85cf9d87dd1cd06608550662b5f15d1404b3d91c9d80a252dc73046ea87ceec2490276a6af29e0ff0ea21ebcc61c80c066ca8b5dfa5aa3414d2c3239e4210081a640d12e0f44602ca422dc7f02646edc5e84e59f64bb2c95ec6925be68206f72ba2ead3127d5d50f8e3de8fd19f4c4d1fed96fcf3cbb764157b5e34e78b55359da1b32f9fe90e1344ba6323c7b4402b8d8dc394f975eff3d9d130303281fcc28f81843628eca2106c9deac88a69a74453b8f7a677521320a2304633408ad2c149843c78544b6752a6120055b3b51fe6bb496f0319bef2f070f0106f61aab7d93313436b7118d012483b05126e801c08e888114045cc10541115c5337e8f9a485bf084a793233500c40ef316cb2a44fdbc9666639264f35651da31b10bcff18280ecf0ec0b790142ac69a8bed75a3acbabf14a54feed350caa7a4118ac2714374c0bc878aa59060ed8928787fe753678e7347e01514fb738ac3794b13fb182f1a2350d4039b8586888daae52552f2dd6fff2dc1cbe354b2cd7a3b2374fdb17f9f7708535adb50e0e1ae3e986464e618ff11027e7a246673b477662628d0b8428efb9df91d043495d5c7fb6dd75fb0e8887c2658cbfb85e98294bbdc0d2a99b4bfbcf8f642e5a3475ab643a9301a3cb7c28cac015bb85487c67f4781d71b4b1f7009cdf898af391017c942eb7761f274f359c6a5692ae7e064e8fd4700746b572e5ec43702634e60a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5ca5d2d5e773a14b735777aff838b54cf8642cd7a6ec73ef74a53ff0c8f9107e","proof":"1c99f1a7411ab1fdc15110d7f3b9cb8f974726ecf9b482bbf2e486ccac76c5650c48750a661284fa3634323f981b1b79cf5c144ff904c9e337bc6b66147d22798ecb450355cfb972f18a9bb583e0c5ae9f27fafd47a64dc6d7d41e486c723c63001d9497f604a608feeecd77724399c24d7f6b61a5690552edb0e790eb93e62330fd712074107a7d1b96227ab28baed6d21476a89a3a29a59450490ff8b80c0069deb558578c352153c7dc54832fbbb209c17fdbb570310fabbfafad099c7b0ea29a76609e604b92105937fd22ab7ee28bc021e8d72dbfd19cf3d34e1f8335035cd30ec2b27947ced9c14d2d3c2c814322c80089baca6f11bf510d1fd879b135a8a731a4cf4a883e5c97c43a8352abc374a7c8f4d317da20437a5e9a0eef1645de2b7af3b7f5f65e640cea05f92572e8860f57d89990bc7de5e692da7e10e32bf264930d133554d2a8b4bcd8e99ce0a69afc8fd78f4de8e4b63fca206d1c0c05ce084feb815714fd11d25de243f93729681fc3642faac28502d3b5481e6d830d84cae08c5530536f511c93dea1e18c585f3a44f47d463c0f5d77607d9918d935d01d5e4f4e88e0aa204252fedd171ef9eac71b8a28cdb9f433c9e739212ca93a00eb8a4e099ce40d323e4b5e4e73465e0cac03ce2263f7a99e1f59eee3772568c02a3976f6807b95030830b8d189db2da0bac50b93ad2bc13863e94cefec3b0338cf541c8504eb51be822f38494e32bff49998765638990c5f48c6f33de4cd2c96a00d57b05c14a72fb98efa0769d504dbc499d509617927ebd2dcaf5ac22f5d168205c2740c9f05c5ef1cdab9e40d5dcbe4f18d6e5903acdba88ec2e4035f0d3ddd02930f10fdd478ccfde0f1c24a915ea7b851c6ba79afbc6103acaf4a1d0317024e5a08b3281eb0154cd2abe9fec35a276514d7a5c82a3884ee70fd0c6a0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c43ac503af68661f296de7b2edd1fe8e4be275b9042518a47295965c0c3dea48","proof":"eeca5ea39daa91c1360695cd357705ae9d1fc3ecb764e824ee2baafcfd9f310980cfacca2c4f568a09f187580ae7488388062ee709c9a8fe31a31c98757d7569a444118f1eda14081ff223f6fc8dc1ccaaa9de83468a5f11124ac62063d542506417cf20f04f95807b17acc4cd37c3a0db5c3a4458c88ef0f61e73176dc7340ca517cc75e5021a8339ad3c6b08c25f8d7803d6d1462a6b7eb445e3918629c20f6d0df2f1f356e4a24d60eceee0f38cb0872492a833cf5ed367ddeb1f59b20906cfd98f550d08179b3632a6e046ea9bebe9928b57f8106c1ab841c2760cba850b9ececd2788ccb4913d8fb8c4120352ffd2ce7b02c5653b7c2bfd7fefbe50ac7a262aa3a8b9d41b2e8e6c27ddb2015198d14e435f9b3e2a31bb338c9fd26517767c5b3f69a020f785becbc1bb4e72549079d56e8378e27e656ecaed016ad78233507092c0f6306341a5f0072afc67f730a90be9480bb643e2821f63cce2eaaf3d2c2a019a48749855fef0da58b8caae3a2e53ca83c2c75e635c9538514ed7f549ee8aa61b14ae9f1f905183bf3d3d3c57bed0027d1d2c047c1a5644386c52a37ef6f099cbcacb3686cfad0782f2d19600c622a162528d718e4b48955cd2c5c51e76c1c8dbf49f9dc2de4bafb37b8db801c13493294893737b20a1d07cf762f754ccf3c2b683f36c82e61289810702d5bcc3b48072e59c5b2ccb87c524e696467ae2e202eca6eba724cf47257d4d8a46ec7a976635bf5f62980d36aafd2bf3ac4a5a9ee37d36def90788f0b25d5dcf9c2d22a03b40a42be438a45cce34d32d0c032ad51c01513215f2fc14a9133b5276de73c39da63171bd765e16e63e4456430ddb2c24f3ad7a02e4a07ec98734a522bd9923693db601aed733dc837ad4d0a301d4c2806baeb689fe69c0b678bbf5cb79836300bcea8df48cfffcfb9ac9e22d01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"90b2fcf7e9692cfba4f5920ea8e0ae321894d0ec80ea2befb3a35c6b433d822e","proof":"940cb1e5041b4b8bf045c4675cf73ca3c94fc4f1d8aa28aed61abac55c9d721b1abc68173f00b539c8a0a2eaa075c674c1984f1a8f4ba651323c950729a98c5456cb7b83c05d830e8a2966965c120992dc3feb15d453a5196e92b6302de36f548eb952bf1660a47078e33fd2009753c304268fc5332376451422c298e7d87b3b3944236e711103dbf63acbb6ddb100fc3042466c1e9fe65c24dd0f49f70a9e07691b1ab52d2a360ee0c604b235f696129a11d6514692f01caa66282e06f3e20cb097cd48e10d017418f9ddaf1a12dcb4c0970b54c2cac1012edf0b17cfa6ea039026ce957d8d47e8d3ed020e84cb47310cf21f32ff7f139a338d935fef9d1f177ad385f9f07c27688b36b0fde1efb4494d38f9b625a130b99670b6d46ce7c256bcda7bdf6224cce006142ba79a8431a3bfd3b6d38ad174700625376b768eb6047c0043f48664b240bd17b9ebeb349ad0bb998ea81692b311755b49e61412aa4d64d43ba9638262d08e31f93977d64cfc4fcafab42b5a55283f2e81a0895cb61ace7360a514c8e5c75772938d1a626e45935f2c2426dc96cb7d77bac6ef0ce50084c76d82d39e98e5cbfab0d9821e536c8750f58b5c57b2f7d0168d330428761982bb686b0d44c7f00fc1ed549a14aba5e6c10fecbec8ce9547c22e41f17fcf1ad646aeee29e4ee6035d21cf9b92e7ec99f51571f87df651c6015cb18cc06f521a88a07a2eaad0941e019b8535756df67676987fb676ad4d499dc43a46775380fdeea42bce276398212f194b9d14bf700b76e4f9ae9f3f415fec9ec2f4c0c441a140e9b86ac0a77b9180c1f322670daa107eadf9dd515c7b49cd32d07ca2f1c1f503a90e31dc5fb42a90dc21473853d66939cea87465a3982f0a94f68196f3c085148a04c5e4f9059dd5788f42ba62433d8de7979dc11e1a3fc949605b69eaf0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3cbf21559ed1a285728a88b81383bde5f8f5affee7f946c2fba74c83dff76421","proof":"247416566dc6c55d422801c0feebd804f881e8ff1fe7f3f65db2f873081f8c7b5a473fbe118b698005912af5881c37e9ee49dfeb330367d84749ccab6e147621fa9034ff13f1488b9bf0609112bf5c844b2e23061e004569b88dd23e8d00975200c2edaf753605c01c0955726408c0a65efeb35fa4a3b4cf801bdb7330c8a662a8a40804adb831af4f567394f05729097db02f0049b1513de9e5e5ba44d38c0b57bb359f21a57f2e84d4edcf7749ad8f8bda9d0d6b0135585482fc9bfaabd70524d49b960f2eb95cfb2bb3db4cbe840edf26f118fc9e9ac2f1404f17199201056a847f39a5da9ef6136ae8456ef68193b871947befd3381764e85fe3723dd66c0ebe7867187c36ab064064e6380b85b621e667ab041c85bb0b65a7c39da4781eb2653e841e46187bd9e8d8ddc757128d33d6a6d338f180f5167df91e37e942645412ecbedc199a759adaca76cb649bb88e768123d509c0eb2024160e5707a648608fb8120b6f63045e311693e7991491e475dfd062749404adc9e84738dc0824642a1ebea8a67a8fae275fd76b62708175ae0b11d2df4f37880effddff3ddc5962df2081234345909bee3742008d567778bf74aaa58e719230f0a5ed387a157eda8ee254febe3cb6ec3415e6d03b89320be50e4a5c071573f9cd1a63328d36719ea547c12443e1d806d88fea7f95adeae23507ca5fc829bfef159f66428e936978eddec724fb519bd393f395678219bb9c5ca537b44d13b3610ecbd80368304df46480684fef2bc49b94bed8f0aec86560f7b446e3c0a9cf34e7ef9917e6c638e05e6fea38bfa631cfd4fbd74a3e4ee8a462a02edf172909dd437a7014c11f1a61e6b8433116e53c78dcceb728f85e84c8b589c82c3fd623d00442b41dc1ab061a5dc045ae3c4e32b92bc7f6b2b25e0c537ce84c94d62ac98972002b2725fb04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"704eed0e6dfa81a6b910967189c098e078156ffce040346ff6b0e04f89361c32","proof":"7acd6a58f8e576d907c9c8ab99c7a7fed1edceb1cc4e09b1c95b319227c8a46e8ce1b26b29387e751d5f57e9ab2dd05ae1018e4494d1f87439794e063790a459ea0f0de0411b39dbe6b65e15e8f5045667c4cffb65d0aefcd2db1e8502b39525e2c9d7644747666c8f57806d1d699ecc36a711b7babba21151f7bf5195763b498397aa648352379372f31285a13feedc17f324de625b40a2320138dc0328c00b25336482bab473bb8da937e61b358387e3c39d42d2e6feef26a112bbbec83b09879499327b3e7c245694b760b0e9a7e66fd0b8f8f0578ddd1ac05b7ff1260401d08cca269e489d5f409fb254724669303f9a1f307a14ad87b5fdc3e22d43ba65b662a9ede8dca64357c2d870c79fe9c4f4f74b6da3ed3e3e9ca5a59b36961c1ab850b3a7874b68c85543b6de9d66959d42b36c7914a32d7d95c11204c62b3e519eeaa5091cde937fd39d0e4c09c194ad30fdd25c134bd44a0d93ae42696cf453dc949568e9ad0e5b166f819febabf083b0b70be1dc4ff9ab3cbe4dc7dff698502ceaab27eb6f7a675d03f20cc3d177fd7a09b1279dfaf66274e35915f9e81f2dcc1bc068ae3fc1860ae3666dc9107c3ea6fe7e7a5c312fde5a2ed3257490531f3819edfb1147b3fe80c7089413004539c4a59280a4c5e6ceb58f1f329681fd5fec7cf71c905616a164e353b942476bac7fd83097af876d74abe7465b6eb9734174d8a49853e35596d8fd434262b06661a9a5f999b379a6c9639362d258c4815778072e7cda5288595e92a8ac723e780a3779c6fbbc9c83008c1766eac771f45126f9a98e6eb343d8778285759de3056ff27f5b01e8c1e66e7d83189c4a8902302d272f92aab43397a5460372987f4b955590275dfec7bedbb2b0bdd335c06a04535985fca9c1e312b25a86c4be5068a4359569eeb9423267fce993f11dc01f05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7af7a47d46d333d2d1235afd369954c39fd7df0b81441df936530a6689721d04","proof":"e2a9c475e265523c566222e9a3c8da7988eb04dba90884fbdd6d1b30a7c0bf72d4f3e9612444c98188b4abf22aedbbf7313e34427a35ecf0db6e83ed867da151e20c9f63f9ac55426ab704149c6579f5a673fcb7e48a34793ca65bd59c452145707aa779a1d09bd247bae65ae1e91ed0453bbe361eb2a97ae56bab071f91251648bd447e1fc5aa77665e3515ddb35ceba702d7598d3655ce2880b7e330f3120c9af2aeceefcabf09bdf7af922b1ce31a6fa764a5cdb635cc563f0eb6b8601d0cb943fb26be8167b0c08f14cb094c4ffb5f7c7a58cf943a552281e7d3beb7d50396159b3292a54bdafc05fc837f559bd2995d3189ba656981ef82df6132f3f14ef0f745f6983dcecb684484db8f9112b119ecc1877d94c4d8d7d7bf081ccaf231d07110f7f514aae971a51d984c6e13b17d5100874e4c7bbd472d6caceafb3821b2829d9e6b62d4c653e4e32cc5d0fde1345c33321cf32cc27de53ef2939775401a35797f07234d19c5e6d1ccaa22b4d52cea38d40fd3fa8e119725f0da41f669dea17ccdcf9bb629f3b0ba803f8113daebf2f46b42e0457beac18f0831ba733bc2e7a828b1a8b8ca577ca8a59e54cbe15eba8e463993d7a16d3ec82722a8f269c6c27017a29ca0cd02ab4d896b682504e27c4367893a68df5d42ef63f303bd3c62fd846db9c169a88e5ac0abc1b296d692c09ec3c099b67f673ad74e8ca23525b8d8aa115a5d1687a263fd321264d945081116c026c21bec4852a8a97412644f4c99b278ec17488fe9909ffc4fcb758bdb806b482ed3353005037605269ff9031ea97dbee0301e8b9c9b8372d6c1028ceb84ae3bf15381fb18a9c70c6d126b0ea6011616f405cf4364c324ce2226c29e00df3d68e2d3f604805d29b05f042d08eef207d99372e44f19baf53c832161bd632e22125fa65395ff7ff274abc19f09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a4b38d7a9b5b16515af050b2370be0d8b08aa4734c9c5c95d68f9fd93c44a613","proof":"a0520d23e8a7af512a4becb18deefb7cebbf66a19b135eec53a3ca9a19091e6c46202001ef196e915cfee050c54fda8a5caaaea3af9c4cd9d0269ba2f7cd9504b2bca2bc303b0344c39bada00ac3b5631a2e99e089c2604f59cfec91f8b04e31eedafdbfe809a5b7e40a3b491911c8fd269c16d52128bbb3210b01be3fe6e0559d4b677b56c8b8df7465a2da3f80c694342f3cff0ed4a5e90cee2404afa47a0061cea1ebaf1b494616e9fbc206a756ffa45f85dc064814ba1bc6fc3f241961007cf36e375af375b364d158affe65865d3f487ae32cf6a2266002acdd7c9ffb0ce2969fb8fcd3fecb3b0b75a7cf0b067e9859921dfa763d1161e3a687da60941d466ded59b55394f8585e136c9ae82105a1504af64fcf4cf112e91bdeb96ec04b365ed54671d736e9107b983d11ae6548bff0238ba1ec1ceb6310bb0511a3c70f5e6d6770966c0609b1549dc41a2aa1924eda85237397dc05ea4d9a822a10f14fccdc66cfe830b630f6badae35e995c625c6c4cf04972fc7e8c9f0a85c4cb5267beea4020c9887f42163b662340e2834805929f588b8c36920016e440d9fa6e0f70970ece56c9592814af023c5eb4db445f093cb981c1ab38d76c25e969dab54c98acdd72900f5c9c4092b71bb4eefade6950e189951ae0145e4041fdf5ea242d184c44b660688bf9e60a2eaa969c6ccae6a8fba15faa96aa1b42bf47b8efbd122e9c105c081d6c9ef1fde0cd67132a857e04cf9b1f3ae1b4d814cd1fb22f436f9aa0e9f4243fd37994af7b984fb7cd52118dc5d9928d3b18886971e567ed754af8b0753b3cade98211625e5d37c9845885d45154e1d7b1825cf92a69dc85835fc67e0b1905aa64044634e34d07f1313e4a3cf1c48658f73b700148826054b901dd8f0e66deee167775bac95ca4b7eb3368ed8eff81ace2073428e573544f0607"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f0d35d132636e7f7e1caca4b547b572b8cdb0d9d95a0398665ffee198c055348","proof":"64799e380d0233ccc0e246049d95ab2fc2f57e625dae9f3c29e66a2e39799400eab46d7034a8b98f10398bbab31f3a25c8039cc88ab16095a61ccbeac77e7f00245fc7fac7012717328c643f35d76d038b51fa680f5c6a0055a4a1d140872b2766a25ca3feda9b585db98265bbad87f12f49142e5f357b98069b1d16f0db895e7dcdc7fc345541ead122d7afab400256368eb5829f79490b634f494c01bbb30dd93ff873dca251a03da0f88cd9eb1575c32793997609d0e5ce08cc0e6563ba0f0edeb07a150013c6138a454187d71faf609312adbb88102ed6ca4ad0b1c59b08328176058f8811d786b52178f340abcb71ab464f8201c0d52bebcd688e5bf43ef61fe2133f5eb7db6e8bd023b7acc6ecf91c3031911054f6a1d3bc6d30eb2c424035089151494aba422ac0e8506923ec8950758e2ee3d5ea69abf76ea53b863fb030b6bd94e4692ff0e6fe59dd4143897cea5a2659ba29a6a94110f4a8b1e41fbc633de3f8ea8ccdb9d4fbe69d7aab6b9fc40e981bd7a70a1f139246c6eeb132c2106fcd029bd988a705c5674bc9d20859086b7d711ab3a8c752701cd0fcbf459879ef93c003307a4036192b7562c4627d6ac8ebf25669825d5fd97f62c3711b7aa9e111ac6d04f26af6686cb7cd1dbfcb62e7cf175f44358f96f840ef9c142b2200a1426998ec66544bf03aa4e9de58fc06359815e98ce58b2591af3a62da24b4ef0d2a5cae836d60ca5c94556f3a5562a07a1ff1300524e480f158602eb52e1ad5edf8d8e7da84b0114404ce596f8d0b0402865ab5b993e74261bf1e3ec43db4316091df9d3b531149018d805ef462d7ad65bd989c025e76b1cf0ba7185f61bc4ea115eed9305a8e3c48894167c92ec8ccbd8670b22cce0ac588c94493c40f1b5630a5bb4e5e2ffcac9f39f69f56072a589ebc868aa07957f962a3fe18890f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0c2ff396ad2c0e76465019d378f5456ab8a7cbb22d34a46f26788328211c5d0b","proof":"74628886550aff954d7ae4c9c698c750205143c8bdc543bd658ec59257d9a86de666d73fedeafb90f191bdda32fe1a015d7b643c0708327f1f423dfb68643e64a6c25ee05dacebcd350f839cedb65aa55df9d5448794daa2a7124360a010971214ce03253f68a05906935df5cd721120a47b8f7b991a3d43c79b9e4b758ba135947e1be59f45469d00960de41809d52393433071898c863c7a7dc555257521036d90fb1c53b17c6ae7a4aca8528839bdc7c38e30ba9026afb637b0923a8f9103655f794ad86c134cf76f6a80a2062cf468515f41fcf74e647a0c5d1fd1d77f0c5ce143899a62f9f15e17b233a6e553265d60730496083af5b923ac6148c64a33fcc62c041bd241e255593a5eba05eefaced35970f55fa87df423e93c134e1574c8c467e6c95c636ae09eb0b6e8865798bbf6d8d087218cee72db501eb41e415f6c5722c8944548c6dd2075f393020ab7be0dce86914e3e3e4ead511493638d30f403a4d4f22ecc9f4e2d080bfb66099a9457b66760307771068021ff1e0de5479c2ff7e5e2e5933ab92970f828afb05e7ac719f3fbddb1f1a4edfd23bacab176e6d2373a664c43dcbcf6438ecabe5c9fc2bff666affc75ab7beedd215699d63be0901a539600efd9bd90eb858b6f1a755daa5d963c7963533b4838b2160c6305141479ba970e02fa77b32b70fff4377d3ce994ead79e7820bf9fbe4ade6e0e1e9469d239fef31087415b6a2626e93b02ae8b0d7852033c4d3f9d01be71ace75bcef1c7a7031904423e62bd8246988c5df9df3b1fa5bc8eb82a2a72344ca4c860c6da762ca74b24ce7bda5145bfdf02c7c4f41648ddda7a453c862424a98a770a4e81f111daef434b80efd89542c88cd83cf7afc87a4e0f4728918cd749233a01be619f9e01874be25b632ae6b4a9eea1c2adbbae4a2d62b8243447b02194f20f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9e4b3a06396f96d1185b184ccef43fd4b97d8f36395f7d1ab5aa357bf672be6f","proof":"546fe9f614dfcc11c5cd723c35d0e7e654f04b16e89668afa8eaa5a53da9503dca5e73dafe17acbaf7e53fef738cf7a2fb15b1b73f517f0f7828c1f94c23343b820899243b556411bce4763a408993f261fc7a27b284131cd3a02e4dc0bf503b56fd7b90933abbbd309b9008bdc3f6aa959289245ccb4c884d984924ecb3790d87d1c5e93a521da81dba6850b1f73a5646649c3af7e3b7b967fb333c326e6c08c02181b5c6a7f3f1648b1cb6525087322acf62bf478f96ab8dff9c8e8f57e70f37ca3fdaae4bdc188b39cc3510a471b5f1395fb2a9855b6ad714447f416ee60dc640f60cbb5ce9fad0897bc6e9ab81899c5fdc5cf7c7b5d1f4162ad6fd23a8399e5b1e61d27f06073d6a4779e2f6c0a5611505d2eaa15090d98c2c9c2428d14bf25293c9fec1e3b035da861f2e8a55464ae721a7c1b6eaa0c98ea5adc9a8a52c82d0521f1b9fbf9e786f048609bad518938ff0ec5c9b08ef2d66960b456b5a5750e33e59cd3b5f5883511119f6eda07cd84b52a168ec794a0f36747c3e614604d26a9a3ce1db7adf30ffb96acef3b05a5f383194ceb610c9879d25c53ca31a65c8e2b5e43400f399b158c8c2c5cec3187fb78071dd97c4c933fe5c4d8faa183bdcbf71071522dffd0e334e9492a56a16f5a27fd47a32b9730054e9c69b5d0846201638fd8f6870dbb706b3f8421e3013fa72c003f9d569377b6dd98c02318f1cf6b74af94adcd35c2b530482ad8b2c449639362b5301f950ea23bdb30174e144864cebb6f115e4a6a6663b60222bff90a7c7f6bd6e01c02c7d1daa3d14f2a539105d2edd1bc0de99d8950c2bda5f922050a0e6fedb9cbb6b2f5a330af41f5d2665cd342dbf0a670bb6ddbe7d63eea661e6f78adb065066031070f18ead31a20457abf4faa3e30fe9dead523466bfd76acdc2e7d45578ff04341fd6cbc7c28604"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8cc2317ffee89b2311966ce408bb0a51db1edd6e73d119dc17415ac687b7a342","proof":"26e35b9a514088426958c5c92cf4654a06caddf6a1cf7615df8af0579eb30f57aa7cc42cde3eb972cc53b64f73f09f8855b40fe155aae25fcd5950d763694160b2cf4f77ce752ff0058a664410d9f53be61a7a4ce5482849271e9c0d7ee506629011fbede46939c9e7996f21884fa90f89788b95d36f56982c8271ce4b88bb30df79f3c0d7d687f2e0c506e2414c3fef1a5bdcd1b7e614da993844d3a5e8ed0a8e67b0d781fb8b3cf63a4b5f6d4d29b5b560f46dd2afed8121ac17e86fe90d06c022f8e4a31178818ecb314627ad3298410cbe36b567dca7f6e812980bbfcd0f4a9b50749af839201dcccd161d4b9009c39b1f4923013b50d49d8a9f3850e41f3889392a66cedeeddabe2cd76656d7881b09310ffdb4f9299539a44f0edcd93424d361ca6366c5533b3bd0729fe1eb16a3fd81dd95db0f96e23ed311c7190a1fa2c0da0683a716934eb0ee7955b63a638d6d84936b7df84b271a217a3901d021cc5e8b0c64e4b41d810afc51ddaa83022d8c18f894dc17f0af8f432e31b01d0916f04466ada914cff357f07054e3b3a4f5ccfe13e4019cd25dbe83f80e93c53bf0acc6b85c1d63d66e357fe187c830e39f3c17fe59d698db416ea3b3ff4a1243c85aa2e76d9566b83de3b10e1697d509033f584f1e075b3975c03a308df0793aa8f45aff4636b44877bf60d8d349dd5a2ed12d7dab82848fa57414c4c16cf4445e7cbc013cf37edabe5452bbdb439359d46ef5c4928a557cd54a88b5b326406c3ca1877fe56ac0518168c1afd08d31915dc6927a21a0c0d3be7f620917360176b868cac8a1899849683f8f7adf03a37a35d526adfcf413cef83c8835e7a4e91b80583c026c6936a1e686048d9692c225cb7513b13e5383bf3b11546991a5c40d06bec72d0ba894380ed494ae32f12c4d126ac181d29b57d92c7525ffbcac2208"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7ce04a0f1046094ae5e2724000a58155c6358d879e57225476ca7f597c08062d","proof":"203246dfc20b7320d745a3ab3b79acbc94d748dd19fa8f94b422a210ff4dbc616053cd213d0c58253d77c76a9ef09e6350239485d35439f9cc4ead6a9adf2e72b6d6be132e242667379c88ae96fe528f7860c43f1968476f30fe9add9239bb4bf0c515d8b134d6e16f4b590e39305368d3460640e005d745135e4455e14e840813fabaee2a6f2116b3ebdefaaf45f35ccbaaa4715a1988b4814b8d7d05f886051f3a7f1811d7bbae5420ac3339d005321b3435017ed4d69a7555dbab79deca09f5d603c1f96e046bc7d344d27e04d2f1fa249fe68673c722ccc1b646a40f4f0f724938d3ad7cae65c030e97e18b4ffeaccd7ea3e4a8ce726915edd64a212eb02944291930ca00f4eba39b5c4f76a2d9c824b7d2607a581ca0d543dfeef90454d5c59752b27dab95e8e7c6544a6a469ea9a0611115987e40e89e96f02c9330c76ea1f082a9641ac000ca5a98ef379da3d33129015d3e958eee44b8c5ed6c4d80d16160717ed22193a0247d4c3889197da0533dc03d607c2e174891cc0de27645802183964d12d7de1535ff05eb4ed67cd172d203b896a63f8777d3dd92fab860240956af60155c06fa6795a7f8f25bc15f56b27e439316d4d6eb5e1125d338639181710d62f98feec6895c80d39b2f6e7accda93ccadcefa08ab8b2c02977f72af63f1ffd65255e2b45afb4bd9e9bf1a2de87c4aff20d56c9f178e9b84a9127796ca972023cdeebea71bb91a22519c4a3a696427751d9076fe0186aed4155717d9c7034edc495060d26aadebc213ec8a04b769ef5d73aef52a03543e40c19fb333252aed79fad71c4a6d91253bfee97c56ac1d40b644185c6042f236ab07f9e25c2824bb985a26d28b74d4cd729438162d5a7beb6e9993748750686cf8f5e270e306980c143fbbf953934a18999d41d72b0d663a919bac7f6aa2aef3b5262ab03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"82403392c14209428855aa694601e1a41a0c9058cf681d94fdacf407ed55da04","proof":"96c9af0bcd002f4ace63b1898b6ec84e34bf2cca93f51d3aa7b7390a3a48a877107d540a4edc0fb345cb4971d391ceab63e3766fd0dec13caa9f03e38ff7791d8c5b88b33c3f4b4ba8b916fb3a9663c7151b1259f4691f5efca1fe600e14e26bb6c6ef9530907f0aacf82346b5560848fe9288948a29cc298d5b72504ed26319b86ffae2a699fb0142e3b70cf6d911c0ec565a766732459f4cfa43108d5b2e072b25956a1d9d294d46b1b8973a4c87c5e892709f2a221a9ecd5cc7682eea410113e957662c97caaae7673c637cb02a49022f7986918f0482a4a940a1bceb7207cc50d0bb018ccfcc708e36914ef28a4b2b1bc534b2d4c83556c5de0b8f69a133844507275d5849457114f80b0a10999c307001957cd8b26453caea027aa2a93d46906dd51ff338c656f1b07d390660d9545b65f86e9c572ac487492a4137261baa9a030e38281d759b19c51caefcc6e7c603435e3f20a89783eb9eaf1aaccf2578a66264494e67ba97e060396c30643e313951212effc5c9d619a641d576031682c1115d56bdf2ff990ab828dc60ac666bf62d64054d7278f116323528380b4f2a7a0a5ef965859f09c27d0708963bad571ded4f3557d6b8b3e5b37d63aa9916d4a1ec1973342eda6758de222e40307d9c404c822a7e1d72dadfdb4c90c84d5df26e42fbd20911731f265b5f295e92061f6ab652d96093bbfeb85f548102236100e0b67152a391c4a116db6142dbd871d7fbcfb56647f0181cb65ffadbeb085de4ece984cb087f8f9e4fdf8c555e74ff99a1a5d7896152268f110c8e918894408c07563c217d216bceeb109cda695502d64897306207afc0e08c5e943483a73a719eddb56873b259afd701f1d4d9fd2cbcbaf36aba5ee0272460a78417eeb207d3ad1f033f9a7b4d152757325d279bb25b26c6dcf8d3dc4632ce17de41fc6804"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9e32ed09dfa200c81b3c4aa019859d439fd4d4eab00c80856ea8f0da4f8f7a1a","proof":"7a722f05f8f96c979e85aae76eb7744c48ec1ae8cf79702ac5581e831f10857176bc81e5b555fd91a3de473e2d95a6a72bc5b6f8573f750d5eec724fb9602f6d085ef8912e0c0316b9b2437efdcb137be8825e6c23e365d8b0341471c7582a0ade99c65adce59ee4ce854e9471fd87c5a833c26c8067710df5a1fd4c53e4a45bebc021589508f0936f8755d22728518155d07c5abdf66e21bbd8a544a1f4ca05de830bd7831ba14643170c801b8c4d48b8ec61f3f4e554e965fad6991ccad30de869f00503b4b8288d33620071da41d48706f4469e032d385f2ad7f3ba610c044e63ad8ec9b0f2aeca47ca9724d30b98fb9cdd98a2f9c420b0764a46b18fa430e633fb9b6e7aa9d95708c03bc393c7ba37f9bc42f9d71e4c24c0755fc3d08607ccfd628a27e0af95fd6198f85332e413b5f5e893b7fee6275403e19bc762f24be0f688669a4ed0fcc5af439d2ae7c5e9c5e2eee12b50f696a084b92eab25b23d349e621e6a2065f155b853f927f2e1688a7fd2188b3cf8550d4bbc2fd76d23555416fd7815b544af966ec46998d083b82dcd1788dee5f4dc6987389810dcc7116a00da6d44f19f81454234d441ff3987109392619aab223bf56af8ce4ae9eb44de7071b1994ab8ee70a4e5c2418fb70ec3b9d31ded4928bb36a64ba6c8da9e6116ef1d366135b9197ae906ce028da33f2341ecaa6bf419c093a89c66714630429ef2ad17ca8c02ff8c53f36ac0622a8e64076ff4644dbb7292827067fc492f50081386e0d320e290bd46e299c3e01c7b2449772236c397de0455788d7896753ec6b7d7faed7d8e77f53489faad563305111b2095412b10a087dc94e6ef6c4109e7f023cf7c896d1a3c0ff8a867b992bd3ade61aa3cda9a9888688b44caedf102eadd0dfb8a50922b58da52cc16d6a16d64298cc5e9fd7f043f73d6a96fb81d0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3a1c131fcb6bb4ce7d0992e94ecde8c430a5cc29d1025dc4a36b0d53e418164b","proof":"d44bdc76e8b9f143e91c6f6115855a0bde88cb0338ae9db38e5aedaba9b591308e1f02a90a8eda17672d9fb483be7dc65beffc8376d84a0168a78dd1f2fcd83f0eed9e9b1c6f1539bd8e6094b5edc822d3b77a1b91bf02573ff0384e5025c82d9a945e0c69864705b77b64055aa9f8d41d72b685b7d24bedb5974e4a44c9ad72d57e65bf97a6b011b2800b943242dbac6f71a54925c42bc94ad290a96c4608014631217426beea0edacd369dc3d312991be293ab1b18f3b163c40f0f7b592d098e61cbbfefd2f3de32736f521eb4ba446ac005c0144e6e4e119039a4888533023641a67f7f831a2264f66d307085d820a6db59a60c4c1f25cc9ca5d9cccc392b5c03b431664fa073f9be0aef1d0dcb6c08de727b9b6c7b0ce30091639ff444633449272f2c5abe0f4c64371daf4595b7ee3ea2c1ca0cde1b1ba96f800e10775a326590ea21c38c9801228591d9e6a1174bfefcee9afe724be76ae1ec7ef18e487c964bc9de700e20e37203f83be4b204c145da2e9d865171dce18de9a81d5b30d830d1c5a4fe874261f482c579b11b10de773c82f5385ec079afc6a8ec0afc18e00454624f4daa73cfa5b7d74c7d690ba25ff12bed44dab4f29e4cc4cd0d8343ced8f520dcc456e3ab195db3b0d47f0a602bf549949b05a7c5c6ccbd19336019e07e2e5f35b2fa9cc032a03835526e2bcd6e4a9a1dd1d2e95c72773fd881e165060a209b5861d99a906fe7b0aec8de31ddbcacb1683e76c6b01238a21b6be24a721f12aa4954a2f0f5750eb56a1d4f7026616f8150d6b2d9d80c3095035edc42e412a45e6f35eff1c4087cbf6401857fe48fbc7009b75527929e2be28eaad022bd0ea386c229f1fbc72b3066e93f3a8093e83880694cdc15083eb6349f93350734c385dc2ccddfaff7fbbe85790a071a0e25ba6ed031dc6a4c7038b4cb9b6106"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"465da5f7ac71a60ff8c4f819e287e5f59c83bde28bd95dc8e26370c2f4eeda39","proof":"f42b619e3e268e8b1d91b2f3943f4a7f2cabacc8b4a1f3e6142a48781414d773a8e1d2a8fd05c7ed2430b828577a084585522f3705740b731e4a7c4dba674437b0a8b32dca042de82031bbbcb54a0d7816a4fd8fdf2aa4e7cb4dbbc92f94922b44225470606a27be5c2069385b52327e6477c341c168a22e3f8f1ff7023b0217a2df2b69d7bdf9eb1af303fe9ebd74f4fcf18cd3948f2603e79438c57bbddf074cd09fc44c6f0567c580443bd687cbd409da5a32d2b52006d2298daccef98906bff77984b6ca85e60130d468ce370459ff130ca9049249ff78a69d61eca58609f0ca2df0d3cb3840d38baeb7629e1bffb9f8e5a7c666815c0e86e6dd552f2d13f6235cf0f107e1f59b653d133ee1bb5a5a8ac4537b427bfa157ee21c3b6b805d9c0e9fe7c26d486a5dffa08f040e9ff3536ee284511b708102f4de5c118d56305619708b06ed430e624f74cfc63abc4b35059e04997cfee93f46ae7001a2e378401c6a47be64b5b7ce9b798fc6cdeed9e6dcc80e9f55a82e852e09a5075b9c5e9015a7f3e5a8b7bd43fc880630e13c6df3d34cdff71bb8b7d524f8ff2dda4029e66eeb92455316f2e9a53cf2ef883d3efdbad653a0245aae30f858bf87807203682aa72fb3b426fc71e91301b1e77c4c2d46d82dc5b450bf2930e86eb1d0a663aacf9fcb47d872a89772d23d457a4269e5aef150dda0f07603583622063b9620ace9eee16249eb7376d768565e6edafb4b35d8d4fb0b8b34d67c4941306ce66f385cb6ef5da0c26b693ffab4e60960722cb548effd71426cc979a3006822e303befa34cf70f312576d24f0fd03a3b6b2d2e048e2e4abcbce5347a34f76a65018dfe05361ea63231e9a67ea1bf0f02c6556d874ef6e42c73591bdfd2b72eff40a309df71103105a0601bb8ce7f9116e33106931854eff896477f4f52108f3b70a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9435a6eac634e9d9a9a45f661d2b7f44fee1c915a49475d96402950721ac7453","proof":"c4f246c2269d1f9323c055163b17af18fbc2896bc82c446e1a1d2b43429a100a62bf9fe835c753964fbb3a9e9f65e0856f78a1b0c50e13b0a936f876c13452188aa4732a85e6c28320c047274dc8983a3936493a0fa06276d3427c543fe5b10d1e1c0e8b2b569a967e4d9dea74fe9184d75cbd14d8f7d093b38408e2a2120a4d8269e1d52595b75fbe733682ac3acb074f6794fba9df448069fe8d61b0225704d270b83c6db545fa53733bd3a36c3bdeaf786c15afea0dc2c6ceb4a8db47fb03eba56438e3f2687bb808fc55679e41930693926d71d97eb342fa3adb13debb0a8292a7ec88089c3e8d8b0b0b55b3d31cc8f49b07706448822f3f65d3cae39474d6e00fd855806ab7b630844458978077692a521354ada54d47685cd528be3c1600324a4445c7a1b5a090a46374e47d4accb39f5cd214f6f7f9c46849f52a2f65e807f7e56cf4391391951647495ec29508919cc322f6a24c1d2b2cf95a66744de437bd866a3d04f2460172bdba30ef232cd12494bc63f74a566fa837a1b0cc6a9015b9cc3de1dd1ab3d7ef8bdd21a24db9b4106762d93dc912a4ffda1079ce24d03d326d7af2244d15b1adfdef9080ed1541a99867869e9e2d76d8fc0ed3d5127cfd4fb63889f90d4c133e98c84e8fa7d068e6ec381cbc39f1b857891fc7ac29f46181d1070029f692a009fb58ab4f85be18f1ef813df4cacb47d7dd91be9106ee3784813323b3019c0d7814c933b4d26dfbd7bae5de4a490906e575e7fc3a698afcb904802fd44edabd766d0c8dd79639d2a9b8127ef9c6563b823359cb3925a6678728eab1243512ccbc55af352acedcaa421d053679847dbb74cc3ba1957615d397a0146f30e777e58c2d3cb00420ee2d3b91338e0cd927964d8fde3b880a12b930fa084873d8ede7926bbc20d85e95599eca2616029a65f936d8ba6bd208"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"06728b4de71484512284cd6f2b701847ecec714f6ff8630985f3390d5a31e842","proof":"be86b216c11529259ebfea967c91ad45357a0a9f3042265143b664c7e9dbfa7ae49243a602709a44292bd6f2d28b20cd196ddf41161fdb8300e64b2d2a69da72429a3ff50dff6d9e6f2cedf0c93e650ba7f0c1370896a8af4c06800eeea2e8594a51b09c5ccdbef9e6870d6ad80b56d673d239c281dab7d92ec686833734bc6434f2ca580f812a6e548b48c07a21558e0f1721cf50fa1b7b2e530447b0c6a106f603e515d3a5b46843202f8f6d1ce8b89f383355ae39fc634088a2bd0322cc0fb49eb4d3e3f838eb97121ae8f8904326c2c2497ba5c820039e2d240db1066a02b6cde5f7c2a575e27f5d47984bc9a7788229e863032d8e4c0fdc408904ef870bbce309d5539b946d3befed2b1ac3eff1519a517832661bb7bde8fdf050b6e82ba8e016b7d352b8f1254fcd0cf6b5afa853681243645cb4b94bdea9d781ea2e6184f23de28ecd40748025e2941dc44d43c81058f5868f786713121dfe563d6c1b707a6b3e19df9d59d085177357c62b5af2692374d4b47fc43718f2af4159c225084bbb1e7e6690469299fd63de22a3db7cdf76cfab0f2134b1b4b4e571772354a65de25c1cc1d5f14cf2bcfbb18212a5e00ef0092a3f5f727503e2366e9ae00e00c8a63218f65339438b540a80fd5a1ca8c54657eb36800742b5255c88628047f083b8e52d46022d36578bcfb3b5503d2715c9e6aadfa18ae2adab00bf74a441ea006cbdc71c34155d83028d2b4644620a7f5fe59a3f04bca312d62ef936cd4da6efc8c3bed244ea012b3ceaea3edee83f8f886f997cbbbf3ecb4ca23cbafe59046438a640e4219f3ef14bc1d65c87a0bce99f9ce9e2f473bed1d62d4d5a223890a8c7862b0da4ab5baf180ecd9e9286c88959800fe60c807d3f81b266805b04b30fa99411cae7926a84059359235a095da291f00892c1c5ec7541bcf4943e08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"72bdf07dce56017303d3d892c5874cabf6c901b824a9d2d7a87fede6c147d859","proof":"78ae4f1bf26ffbfba106e7a35b65c86b1790797fb05d2924c884948df354a473cced5f564747ae1f603f642902f2a444dca641eb16a5c76e20490524a5db314768ec4288c3726a0284618272e64ebfbbca785647042195159f422ed9386bda0bc895f94149f0bc06b1acde9bf8dfaa6e6020cfd63e2d9f6112549ad29d6888158514a9f2cc18760882462722b5b2104895bdcbf8b988ccec165ddebbee85bb0c86d37aee106cb0b65c0e55d0892662a0d834fcc14e7f4f5ea0de0107f9666600b6e995026d05c3502878f99327a8e6f25dc12dd0c2390a09f32f92dc6e77f90df0cb94a67466adaa166e465f792201e5d27293f8e70b370ffc00382a83ce0830509d61cb1fa71c48d50ea122138911ac963aa50cd91c11d52040d96541aa02393a5c4bbbb264aa03b5cdbf2d8a8a45130dfc90e79f2961c824275cab7cb27336fed6c7255c77aff08d2167d7412a94e6398ac1980eb298f116ad0791c7182611ba582f47ca884982bcc78b43c146d218acd8c5d6b586b50fcfc31c287a280a03c4fc98102f9841483e882cc8d93f582194335dc371c628072b3aef564acc5245766f168ce19289c6525a9901e242921e8e287dab86a70bd6714000a907a5066022359e872298b024f48be1b8804c500d561de2f8b1223c8c30f3f4d74a7f7f28b2f755b601e44059c5365184a72583162fd4caca9867bca91ec4016951c1cd7cc411e44ca586174f1052049ad22129ef9706495327a9e90f1adcaca1173aa602602e349462a8688af8d5ff2d006bcc6b2a4bf965471a11797752d1094c75ea44fc80f5d267c233a3fb4e6a4e2bc067972abc9124fd21ebaa028e153dd4a37f624086814a50ce109b9402b0b31a80c66f5f473c6a90be7a972dbd28019f6a000153e85e45083f98e2701dc5a9c791dd39977d41ca22c8fcb1f1ee70585236fe0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1067abb35b852302bf647c374ffdb803d6bf683c27ee94d6271ed527ca4cd07d","proof":"02654cea779d632b0f7d775ca53e94a229f88b7fad0d817d4143b35c4487494c68c096175733ecf3ccf8c6e082b5e71215439767f23efaefa0149995620500207a7944ed29c784fc53fb92dd7944d7d2da3431a667dbb7de77357253b9a4a71ff4ee9869f441045ae70d0bcd2489b5851ccd0b461e926eced356e33789ecfc4ec7d74c7cee4094b0c5940c3ddd94efa17017ff5bddbe74743f65e28329edfb06a62cd4b5aee39daa92b2d37969923f8e6aac1179c0e9db459e600fa0f9b6ec0b1b36847c46bf97718c66dcf2eddd3eae784a3da5484d0e25bcdeac3fccbc69004ce7b94570331976e9e8cb05a72f88022a7b668cc845beb769f2a2d80a4f127c56901c6487b6c63f27e0e3036be050f1e561a49800119ba22672afec4e46a9149c2cfd57370420434fbd7ced98603c8228426cc3bd95f9575687e81d26b1db634a8dc6daf1100189fe472bf8eaeb0be9d253f805fd2e525777682d6a76b9d81d8266cee3005f740e5b33e41d2250c82672e595c4d1be342df3b313394d69b8208c8de887b709affa48b89286e7f1c8dca0bafa7b6c434dc8f3930c3863a3c63bc68f95b219cff42a465019274a0bb36671e48c94b7affbab0d65d8d58a6c8e7b62992b766af0aa84730d85834f4bf6a8293afac5e095284323016699fb5ef81bbc87d2428aa6cc6f4f6b6aaee31dc96906375e3fa2b925eeca1c9fe712d93b5996643f1c5871aa3f61320b1ce5c1727d156ce0efc72497b920fce243ddaee900f8ee8e033c48393c7af0a011a427fac9114263226b92a53ff3894cddf825731afee976ef54b86c24185dc9b42d0df9227e5215b19b72678944e4a5d91a344c20012ed39680d77c69fe24d256d5011b9dc43928fd4f062f88255c9b32e9e4aa0662d03b32d85bff4faf1494dd17d528f647fb0a5195597cf84998c3117a67cc0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9c3f090cee2e5ac3504ac17dc72de63fe7deac5e44a4e5861e36f17d0d11454d","proof":"b64666b4299b6f4276d40fc628de7a3b897ee71f4eb7cc6a3cb82708cca786351a2a2ebae94fea71b3849112a273bab1c5fa4d2e4d929ca37464b8534a73d234c8eddcea316a823c70625c95cd82ebdd18eff0b793209fec7529dded8024214296eec6b144cd9ae99bf3b9957ce403bb295e6829474a1d670d24e5eed6b8982f65756a09c9984eb7e8f33c10112e0e8cbfcff870b00e9d412670ad4b158883081ba05b13cac0aa3c74422b602d2e01cc4107a08c83513e95dca740188642c40c622dea41dbae1273e703ce06380e705e90565ff2c347b4e46e32daa6bd3e410b04d9f51c31611cf7bd11b0a09018086606605e41983c3c9d497fa1a518ce0b0136fa535bbfda5318198a106a671ffd528982d9622c96c2aad3e9ba2b776e535a7a6263416c252229767689cda6f32a75e9e1a4d500396bc88aeae8c5f391f306f45c0e12b2acf44f2784cd412f501b5c875a36f4ba64ccdba471b97531c1e31228180dbe14a0607c8755fd0c497ef8ab0a747473e7c02d2b5ba401d45bad3e4c2865e34ce7ed2fcc8f962955a6f08f16876d3ff0eefe43bfe3186fb6cf99f1199ebfd092d9ae3e5900ac7af22363789d3e4df9bee017ee736aed309b6a140827a4fc9c92b4ce363393ca75d2d76603a63f5b63bf4e9fe258cefd2e8381b0ec5c68eaf4fadb0ecd6ca271e11d207ca9014b0f98c2302c8ee30b9b17338ef2b1381ed8e7eab25f3fa052363375c9f6bd09dbb57a000a66c7e9d9855da29651f51d4cf31da5ee0a5997ad162e2bd579b08ff0b388ad41f1d31083ba5af931c0d744e09df4d202440c81569c0c982e2e0f895c15c781c3b09fc0a732517fc579dc028c7b625944e7d8902985c42de78c8fa775dc77e9653dc6c55419ef549e38c700f752c736fffb1284db5fe6f30c037f1372b07b5836f544131ea262e45c5b500e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b27f9618234aaffa5057d1d6488423d7062dd59206d25ad1dfc4542f2e699b45","proof":"38473bdb77743073c6a0a7e15af1701809441d93df5e644b6e7646d78d385d5434b5ff3d1caf674b9c23a902dc950d95c69e57cafb754d3e5426ac4def444560ba672df3273128a0849fcdee5c3756854b4b8555fca4df6da6d20c099ce0732bf6c1da8731eb662d27a54e7943f9068fbef086ca3e62e56757e0451c8295bd61576bda026ea004a5e85de3118e4886a66cb5df1a613fb964e33944a148bd320d79efac9ec6e42b28587444fd8ec37335bd38c765b04206df923ec0d9d83d290fe81a70661027d9d4083fc0126d0f134340c772e5828cea16d29240e92fe10602e8e1e588cbd41392d44bffd27d6186df44298ee7c69c59b58b99351db787854cd4f6a51bab6d7df76e7d07a5a54a11ab557d54458c89fc1b1654c97550e88e1fa09b02196170f9043d5d376f1ed3194389e1544bce56d9d1c6f6e5bbf4444204c442a9cb1bfa183df731a14b47cb7dcde0a98b7cd7869cde0701c594c8479132a4cdf2da2aa45ab614ba8242d5942cafd31eda8bda5062503f4b6e40cee41c0fb0f55200910f8b68beb18f16a9e5373c2288c89f92763bc9d008c247e84bee7fee550bdd47a94f2cbf6a15d359ee4ea97409ed8848af438529e39f10be4d3f45fcecbf706a2b05cbf2aba9481b4e31257c9b802664e96e385c6e2e411e75c44b9e67dbd9cf6b842148b57c7713dca6827606a028b4a116383bb05f3fbee0e764f0367f80a6c2aa172ea36686e1516472c37782a5d71fabb4f639af2daa38ae48a6db5c01d79819f9aa12abeabd0ea897728704565138753961ea96c100689146a4dbce54393f9f23ce87c0e94bb79488d1ee99daf07450844185bd09d4a86a0e070e7cb23d3dd864de178797ba2d3a9c0ea05b7676494ff2ae7df18dfc4ca50eb394cb87903d420575a3fa4b6d7c1a26bd5253e037bf928ad4e1d8d594869c0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7a5f6ae554a083443bed8011eb05c66b27ec422de185c48cd544897e69a69c7a","proof":"36bd7730b498a12a31b29da86dc9034bc66965e338db21fa305a5c561f73e014bcf70de14b3e14828d3f9259ce76b3df49ae582355b648a9f3ebfe3f9d566a1cacd5da0f1d0db67dd6e3e4554c8d8080f2fdafa3be6703b2539b22bf90eb7040de059f2a3d47491bdb9d4049ccd4a0a60ef577917a28991406643a2fcfbb025f5e5f6f3e257e2f35203d88b20bccef39ee9f80ab0c74e7061c9b423b48251809d3c202fb9ab89dad6d68e647d37a8b5a61c164e0a3eea00f672e53ded0d20c0912cf5d13abcdfad2cb0f2d05baae14b5f425c806cad98eb52589bb1d481931057ce7051603c5293b96d75cba003e93c382415c0f05dff0a6fd02e1767dd06f747a9d717c34e9c0844582c554a3874233dc9b0a9c5fdb32e68f5ec0cce0463a19da9589d1ded9e129f9a5506bff2a2018deb0a2e2ca245aa9f8338dd804160e6f84bffff691518b72f066655700247531b7eb2ab50ad2f430f3bce17ad6ad3c719ec3b48ceb3b48d7e20913f45844edb9cf5fba0d84b3ad9a9b6af3e5b0f76400a4cd9d2367d50cbc2e84c8d0a5091b58bf27358c87eec66112d2f83a7263385e784afca49301abe1ec1904d7d1e3828463f84dea4f8f89ef10376a7f92e4180c2857e55a9d451c2e513ffed0692917695037de6eab4b47ef68f7f5360d15616b8896d057064c72777b73167c16f614677512ac46ccd0c605a53e85747f33c8435ce16079e8a5cca0d4e5e6cb620ea8aaabcae5e6e2ef9afd5dabf9a7d386b6173a50c44cb10874d38bcb9b7cdfa4aa2b778f3607763d2aac71ccbeb68621e254e60c167be27c40ade7579cc00ad62fac13040c4ab2e2a78d1ac5535335add0341a5ca5ab394156676de9d8a7c988c407d66fa1ae7fec9c71aa249d1f056f330d9eb8b9a81b7c6902a0a53ad8e62803dd68fb5a2427d2db272eb3bdd8f1759c0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"68c79109007495c6dbf8b923327bdebe3a011835dd1f7ff5b1e1fb321668bd74","proof":"745c627d41b340338335f135de875a3ac5be17cf7e903a0865b6b3d197c7bb3e7060666b7815fd36d78f9e2e385faab1486804a101d1fa1ec723453b8d5b321496e6e575bbda24e727c06f276e610c40f2ab7b35b40689b00fe897812a6e906a9a21cfd43941b3a9bd9b68dd977f70c2e937dab1e189b72d379b32f91a5ec7195174667482173719c1031f9dedf24e418bc06ff3fc42763e98698c9c1e89ae08d76bfdcb2fd31be311473fdf8f4628ef87311fcd97389a554c550905d6424604153608d881815d865430ad67589660e3137f0bdd33c92282aa36421bbcaf2b0c58f1b68922e37e9679b253c32402c3926fa83a96368763291fbc006e8624645ce64b5f1ce583ac3b9a0053b38a109e08b642fa0e1bfbbd350920a962f0f8451aaa9548278207649d97a2d0e0712f57dd7888b16da0c8424b2054f5195acd1b6aae2227dbdf2ebbed85593684a000220fda60412a086ed502a61ef063e03bf207944a1e69718ef009fcc3e60168f35efac7beadd21f2291ea0c900a5a61705d7aa6cdc864ca7ca4dacfd2f07e29cfd280f8467b7e1a8ad8a1fcd195f0961347785c8887d36b847cee23836c7c8933d52739a3bf71779e1b399eca26ec9bf7754d605de37981728bd1f211c37f2492bb3940645241a391d97e9e0440bc24cc81610cd1a2a68b9588a3d3a3fbc58aa20daf30fa84c0a47baea552378c9868bf2c33e471caa6e46b999428c6084adeda3fa25a353dde5f1141b42f5f2f9d3ee9452324af724852e13f2519333f9b3b0a8f304ed260b9472a469283c0b86d17a2b044f8278d20090efe09a0bba219eb616b7ef955a18ab1bd056db190f75cb51114554632b717d77afc616147708afe4495e6f923f8edd387fa44f982abd11deaaa0f6f1926ea430028e0d1877924197075b5e3f45d54e5c28029d7f870a38f032007"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"98f8744ca7489c8609853de7bef3b77b2f7d9aa30a1634ddfffdbd68e5e8a617","proof":"50cdebf5f0649be4b173d60272324f3747ee2e0bce14172feb14adf231c6862432faa73ce9848c969fa6b15bae2bcc0ddd6a6c9683e0614fc6a405ed1841ef7f2a48f3d0935af1f565d2819df57546d22be3a68666b063dece60bbafecd6446bca69cbfaa5f0135ae19749b35898c7f36fe8b2d00cf1456869bdbd5a3b892241ed4c2e2b0b94de7dcf1954bd1220cb30e0794d8cb33ce920fd1b2ce89279b505abce99532c5b735fb7507dc05bda6ee53ab0843903dd732875e04f25a1478e026454d0bff12749c1cf466272efe19b6046a4150db2a98f3e70666d7f6a9e630df63cd18531102a6b58f1765b9c44a9facdf95366bf9de969ec289c3b1f53390f0e5bd8c84551af6f6a4241320db8af35a113ee6a7afea581eb2602e996a8492f0035ae695bc757d60cb2409768fc7883aa159a2762d4f1b8b810227147c719345284457d2ebaf67de7cae8f73268dc30b6a59e30f6c28aa6f9bc27a51d0b1f793e0d2e454ba02162d20c66a81b316bea800feb10bb7400129b99028a6a34a96982df1b941aca8c407d36dac53bb4c84320aa4a775edc23c8a0f1a294aa094630e885ffc2b4206eaadcbe48049c82c78d94faf9903cdb3af929b60ccdc89e2039c4b4f6c092fb4117a7e9b015174fb5b5c81e5d9f81d599c105e40c1d458e02571070d84f8c4a42be7f0b6a2fa7efa2120bc994db73b97b25e345ca17f44a047176a035b1be5f65aef4c422f550fc9bd53c21f5904cf8eadb94360c7b44920272c47387547a468121172592dcf8da0ec528718f208a92b409a3b73978cfe3bb3ab0366e4e7f78603a54b248896d742ab86baef797fe8224d658ba4468a3df4e73025c9626b64f55fa51b945a2d0a281f2c2a68861fdfd390f1de217bf97756601e2c74200bdfcda4d4046ab1b4bb936f21d720c744d714dfba4710414fe495700"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fcbc98978403f9ed5690c980ce8cc44316e8f4025ad7f4cd2715c5b909cf6727","proof":"80b9d5f67a56916704821aba360c60538c56e42db9a027285fb7e07b8f09a764d88b61c11449cdb6b8efa1bd5ca2206768ae76327ed82209a31219c5e057ce5f70c2176cbd4371a1e69afb9a25bedd5301d4abdd13f549d4a0976288bf8cf9657e9cfb49ab0c5c351410101e87da1b614543accab2268a6c39196112ecaa6a0d6a5f91d45f29a14d031cf41778ad47437080562344eae58c7d99161fcec4440d44fa31252dafeb889dce1b699ed81c8720a1a1a6497054f27ed36a1eba6faa065a13e511dd3ec4d805edea65cce8fb8278007bc3014a503429813c2910f8500130a89e9518c15045dcba48a382292cad663f85834a09ae48a5ba9b6dd8d6e87bb02661b62578fb4471a933bdfda961ea0232794822b5821118cbb50bef45ff6d3c72b66c3e0b23bdc33db0426a830c0582b87819b65e290fa4c558172d8a50262838bb5a4f10f7488ae6c0996e3b7520daa5688434eb8263017a97b5d1356f4500f3a32121626bac1894a348b82f9ea641142610e99a3e93859c712db855f05524caf86e24644c66e7da984a0acbd53e6a3ccde2508f345dc1a684c4e5dcbc2c7e040cf1e3290b1f66efd150dacac799baae1ae73fd11b61b27294e31dca27420469b67255ff64055a4f937bcdf2ebf3d2ae65a75a83b20fcde6d5baa5c733345427d13e71d89e9329a7011970f2c10c8b81af4687336d7b5b4eba9caf13cd3de6558e52dc0cfe173f8c4fedcbffa96ac51623892449c9ae483916b5b168460340a91e6af0e64f1c93f6b1587f2b92895f147c14341d7b6091717f34d88d02148eceab1ebed4f4e5bec515308fd8dc5019702c40c5ecaa3d486ff93f6cce40736b97be59cbde6985c6243b85c1846080c6881f9ed13ec766600dddc4d97504021ad5c66e2a83b18aa4b9d93731f2778ee4dd7fd59918a56810a40a067f262100"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4c59a562c2b515641ee347cb92a42a239effc3835ee7c48f8e306cfb0b83c65a","proof":"acb09dc38afc32f366f9941c7dd0a210210c5da4d950f30313d8c429ac9e434e00d392686c059e2346836ca5e15889e89fa8255bfcaad2629f454be19ba073639a71cf46ef53561f631818c2d8afcae004ec26d6eac1ac635f0d4d257afa9c5d7c59ed079824e36d5617d43d787fe88cb4e5ae87497d578bff5729be7e4f324fcb5e73f056b586d116070e697413b4443589a3e0cd95450bc40d44fc7eaf6601bf1faf25491492656fd33e7d356aac954858c706ac39b3d7b83ca79eec605d02af017144162e4ec611216cdadbd985662db93d7a79821c4cf771c35a8c8e810d0e27331e337071d2151b21d7dd8ea6e70dd7f66e91078b959db7782eb540076eb8bb9f790378d9103f1e21ce4eaa8bf0fd449695a3aa2fe8547efdb74429ba6798d58ea0ae31719d6a9d18d378e4f94a2521287ae54e1c45b2f40af28b61d73ff4dc32cb6a5b8258acb53cc65dacca810a412ba203ab9861666a94e306e6554b22ddda5d04614ee479ef826c6cd84c19513e5f4ae996a074c719d10b8ced05302693f8f2fad8f561a6dd0441b69d57bf683cf1a314531138c8d0e05e6ac96425cec1b64404773a2dd8064a4971c6aa5ddddef62e62c11609d7d0773cbf29ae51307b88a376a55577825a530a2af2cf616b1a6f5d4ad37a1f6ee4333eb5f1de4026cc1fd3b1969a09e012da2ac4e4af40a41e1e5b8820f2e664e3640f29fd1871007bc830e87992b71a80a005c67134c7519b58aabbda9cca6c346af921b2ef0c2278b171cd5b5fbc0e0ab30b97a0cc439078957b6670638becdb4fba1b51496316c2c99148227cbcea76f6c2e1e70688980c4b5dd3e659579e02d42c4f542c5ee14bd5cb9edd1b2aa53c002f8bda698cd74c02f9edd8047deaae2857fa4e9b0ba38a748b0d5ed5ea7300cf3347a0aa7433e318fc75f0e20bec25a7929bfb1a05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"72cef9e9563f8cda95233550db21eb363f37ce96d6e2f5ef226c57e9b2e91251","proof":"a633eecf0fd4779ccbac754c463ceb0d9500420d447529577ad42e1a717a5270ce1ca756761f9ab743474a6c178b26c69cbb591fc90b7cdf793f25cfef154b09820f115e008c1bbe79523f399b0b1163a9238471ba4acd7b2b61f91709b916640c27f4a9b7017587618fb8f3334a3b28f5f66304a373c41300ff772d154de87e290c317f2ea34bca4e4711348ec5ff06515c7a4f7957e68a4dd2c525c00c23037b069c4e3a0f2cdce37cd0c016ab3185f3bb247fadcfda9ba384a07cc21e9f00cf191065a36cdc6ebdf4867743d84d836b490bb549dda127af5ddb9110635001dcb32a3b05d7acfcc39548a8b2cbad8a95e617f841c9f7915a7b2c6129bd695d3ef7c8cb32479316789aca1a90a9b5cccd59c0fab9da7caaa0c7b05bc552e86a2c37a193dc62799cc2b8b3f241fdc42fcde56e35d91695374f76f1e046faa455347c07202ab2a47ce38df874c3c525165b4808297501539d43ae4ca4f49220768ecc684d597276ff6f22c1ca5d34bbb65d2a47f19592641d2eea881959ce115b2cf6fba19735d6c14203d3ab9cd6b3ba347df0f951ec3212392ded440932362a9cf4a498d71ca8f5c3911a8d63fc1c424a6baadf64b78703ddbbf18fec34bd68fce0899ba04ed81a5a9d3c6782e294d87ddffcb78668ff89300b70a630b5236bc25529d9a4003384062f251112239e24646b109f031a4630379312a94ef85703f0e21903fcc52e08aeca0e8fad18e6a15963a0616027e78a046fd48c2db7ec60c60ff50cb5861d868b0f1845445f2a31a074d73b5a4b0ba2706e753d0408a93d96cd33d5d585fd8acf48ff1f9a897cde32b47e47cf463b9b63aa2d1a867216650aa3dce85f0f44832881d047a6b65515a618e62a15a64e92f9a0aa9bd8f0900426a57daa4d4d8e07934449fdeb439c481591ac85c287d4a8f6793a96e67f6a04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0e0a9c81a986bd3eeef1a6704ebed0159ae63a9b4c85127c2ea0a0ee0acfce00","proof":"baadf187053b16417f2d9057b82e7a4fcb6b3775a46d50c6bbf10dae4234fc3c5ec85563e70da07bd8d90340a5422e399b4fd0e2a5eae90b893a43559e7ddf7cc602f44170c8df8bc465ab74df8e0811307164f1e88a4a3a865c04ca589f4b6b68e54557685345cc36d18cc545c339dda1e803a317c88f30a3b2d974af81d423b97a4457dd7277a9d4dc4a978b63f9a28c18ccd5ed71621d60eab3d6cb933d042f7b5ca5f5387f0f818f1a2da1398752d5ff23c103efdca2c4873496b0395b08a1e0ede29b13fd10170229e1030e58531e1fd5b4fbed3601b3806e85be9230068ce4966721f00ccf4d782785732d1d607daeeab409d7efeb3d6b6e59e652f237c68eaf44fad8d10c8706563bf83dd27f55d75f2391616765d3374f5405653a5df2e01306701ebecd69bfc2565c2396816ff96adb4c0a784a83bfa2014331247fe0d1ac94b7301a73d5cc022bdbbc53379d3cb71b7fef18039510c5db9065c73e44886fe668f31acbe4af68723768857aed2ba2d32e7e661a473884cbce23c84ee8eda824c41489d388dd198a4a7edfa56481f0dd7844e602cd272772ba469024f456a29ae826970f872862a051b6198420666ae84e50de75fc3d37ac1b691c370272d4c0d3b1a6086e7fed24fd94f0b5f89a1149e2b81973016f1d6fdd218076440ba4c38c56b61964087cdc2b1b7d93289ba3c1323c1a0fc790b6073657b02cf00737f62f01c62081cd4b176596748b25c92a3d1ab6cbd971734c8ee861b679c8e051c1c63e88e744aacfb7fedc5266e355530bf3aad353152c34106c98ad16c0d853d6a6f5a43f73c2e8ec68ffd1c6089bbdef5442c4605e906c4ed180641ccf672e071a76134708e9b968d0bf729d50cb13164544a4704c393ec82ced4f03ae53f20cf001406bb4c918e2bf60f2e0b761b7a7b382ebddd98d63211392430e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f8a4443869ed28e87d38f6c99bb28422e8d83d4e750e714ad0338468aa80137c","proof":"5c48971d2304e6d39abfa947abf618cfc849460d6bcacb7c16f5592c5deec05ba2fdf7bd01c2970e4ab9568675e914ccaa372bd53ea019bb46df1de46751de0d3856c929fb5c2416e67a7643abf5ac63bfbe3ee80faa024965ac04599f21467acad3ed6fd48d71b313f21f12d176b9e4e89dc8ae0247cb7aba1aa75e867f623bfd45e65be1951ec266e259b65d8e4401b2bd3884b374c8bbbc5c029372ece603cbb67f92ac9651cb7b07feea1996bc21197646a9ccce5c643899679979c2b709a668d7757bf4e554799846763730a04e749292db02a831e0f6ed2e4a9813c101149cc72179ad95cd31ec15d85a484c63f8ecda31f68f38d3bfbeb5e71f179f68f6a5a682eae3f7478ad9e3620dbcd32e77fda2e8e9f12026bd87d0298fa7b2579ccc78d982b62ec299d70f3b381a0f02034c39177bb232aa114f2a8a785fdf3114682972245f9763fec38beeedf3fd7c222138d5a36771ce24715dc36915d74dd667fc5f0a83e4766a212d74ee1f525e6d61ae8d1ff0e8e0a07e129908c4564c905372d3ed82d74d2a4f11d8504f6fe49c350ef8e0848ad91df9f86e7bbfeb6b54de515ce0f335642ccae8a5a50d0027b7c9513b26fd954c0e399f7ef2692b27d27ac2c7373fbe5c3e14556b740624102346e1ca97ff644ce90e67da4853ea741e140eba252bbccd15a510cbf5ffdf7962117c9a4ed45da712da7722d36b400f0cbaa79dae2031f9fa7ab96770873b72f58804f21f1ba517ea1c50878c2f7f259e9157609df775a4bb5b4c615e303ad67f0b4c56b6db37dc060726ca248be95bc09e0ba58131b47d1e542cf9e8545bb6ed3ecf6cac89331edfee65fc5a2b423d31d08ceef5bc3ae5d7748571f2cbefaeb6959894b9278c7b1480de5c269fb60b2ee12e21e9de6efa58602c200820c4ad03b96d20a563aca96095be72f61e9101"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"220c1c5adcf89414d2897ab64bb5f9cebde3db83575b24edfd697dbc5be63954","proof":"96baf68bf422f4e8ed43e5442e8c604d8746fa9a8e6c3b6a24117fd184346055a6a714b93aa1302f63466a6d39ca61334b4f94d396f5a6817127a277100e1b4e58ec278738cdc804c72591f6931ffc79992fcb217892e2f90c40cbc9293a392406eb2943cdc954cfbbd88af1d972c0f85cfef40541e4e5dc9797924366d6bf0879a9b6051b1a2daae2eb70f9ca5b25b4f208f4c951ed52c5fd105bfef8b93a0c71d97d4ea5b562610978351275bf328e48348f22e70fc59e435c53e0f6032c04d36096502fb95c48a63a8a4734ba22390bfdbf4bd5168003f3017f8da2bb9d0c6845cd0c296f81326387ef8d341ee542042ede36a056b1936c7d3f583d8bda65481a8ae205941f93e1f328cb524eee5f2ea41f7d6cdd7433329365ed6fc87556cadb99330de62bf183a4afeaf46db47efcbdf98f4f41386b85fc39acc5274661a87ea9e8ca2023a998f3275aa9f1d98818e2e19d9be3a9f532197f0d51ddd454c6eeede22512890d94ab5fbacc9a63ebc148970453ac82ad3e7679c9869451559cac984fb3fa6b039dee5137272c957bfec4ded6b6f0d5e1c0ba4cbdff97665fa6ee6ced15844fa510fadbcdbdb22d535f2ba7c350ee652feff46aa475beb229a0c78680f69fcf0a5c4c89e67b793a74e2f0cac20c27c3bd8c3a42c1d53d0662c2efb3f9737e0cc47fb56586c5161e4ec181494fcba8790d308a9026c615b5671e9487c596e1734a9e4206821f015ba717692f88fc670de9b5b0e1e3c3f5512b3c2c2595ba0edc15956e1304ead96064045695753dcf9ed873e423c4ddb9447580009dd645dcff7d2c423332087e48ebd55e13bcfbd2d39f39c7ce937d43022d50887af477c5b1d4d0ae6b76294953362c3b8d94e047c1faa1aed2cd6d14890b929c5e466085d0d55147dccb2e1ecfb7b28a642aca2ad2a0adc9534744724f08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"465340b00099716dfc7b1a8f628f2a3ae5b28a6a4a508a4f47fbd0df19403006","proof":"fc4ffc6af35f04d80f890b74c1b773b055ea0c01a130298ac24c1017fac7d41d422174b959855e85f9eca2fc4729ad7058499d09ce3eb9b9f0fff3e1eaa4511e18cb42e8717f3d4dfbff52695183ccb29af33a9acf95c9f87d53ec0b402b6b58dabcb373e916a39e72fbba7574cb5a5555818bd627c01d83e1a9283723350e2365b2827076754c2e867318beec63b0e7195f4dbc7c2a0aed3adad7b3e7bffc098b194a03394ca9d875743773461b263bb99d12cda554769cae3500dbc6239e00aa4886fcd582f24fdc6dbe50dd93d7693f43b59f972bf7c0aee8db99ed10db050efa074d09a0327260056a6060044db37852cc1c94cb4938380ec0f2786c9b54366d7e32e6e22ad013f52ba750c66ccdae93629c469d81b81929a0ffb7d15d4e905ad96e4127459373af9eb635ee94316a951e795775e513398028db1b1d5d09744f61a46345e280e0196ea88c757e8f8ac825016ae90eb8bda138fc9feb40158ecf239fe4416cc5debd47e7a88533c917cb749f78af1c64567f5064d73c956f2c5db21c13426780a733a13780a58ecc0b4dc0e0aaee619c314ade1ccb30af21a04320e9ba09a9fc7a6dced75bf21ab6d637c07ad7b1362d04d1d250827bc86aa0d1653ed246794bc55b43d64167ad1aebd0ee8183875ed0a72b0b840cad7440803352503db4756d6b5d41e9dcc72cdd70119b8ef6d4411206d3ad0f37260e6ede5fb74a4f0de990fdf106045f5efa0fa1498bc8be8fee184d72bed476144b2bc875686047d29b67378db041070173ceb6c5a309233c85ef2bf02d9150e65c432eca847f07486ad304a96e3353a7e0b83402c5562cbfddd67d3a3b76c263441a9bccadab065dca630a76166bef479c86f6ddd64145bb074154ea3709ab48ef06287d6a369086957502d714678d441d9885e55a4b6418ea92827f0ab77af61c02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"46d8f846ae4d2afdb0354e2af727db4e63b674960965f1a5c8ff197f76e3aa37","proof":"e4b65fd0605fab724aa83d3ace40246de512c676bec1c6853c71c35794f57e10802361839e4e868c0898e48525be69c5efd0b2a6ec0b5748cc0f29de2ad3ca4028d8d559199d4a3f9eae1cd281b9e66bfbc9ad97d399e32a24e0f6f7fdb7d506ca63840eec7719df3da1b3663f100fa08f35dc7784176ef75ff7038361da154cd4470ad2ab519eb94e6df924b8b4c0c2871ad950088bd0219ec0776b5ecc16031c38ed5360e2a397cb496f9c6a6b696eddcfff212710a6a6f0c18a81800be30d664502b24e8dd865d8a7532b639c1a349737d60a5b55b7424e41e43db6666a07c261b56674aa7a012e61d963394803627936a49ae85ac4bd22ed95a6a85ef72c5cbefa385998412a3b6d1de1da0d542855cadbd2dbd2b02e0df5a5fd3d428743bc40669a48269c9bb4d6f8eaa66dce44f32dad94525e879b2e118e5e9a5c9b497830e972c599e62ea39e68af4026e7b4dcc6d4b8e2a80fe7638415376d33165dce8c5cb5b54fd5a98c433c60d56be91c184355c7c4a8abc7ef56f8af2f050d65d037619b4e8df2c048f00d693062d7b46aae2c41071dccbc1d4bebc7ad13e844500b43de8bf8d7253ec8fef21f8f8ed3991df9c074393d15f1bde86b1613a404f8d85a199cc6a22d428965327a20f3bb90ab758b0532d07ce913b67ad3c17240141804954e855cefcf3d01ea0298b3ba9b0f6b3fa31a9117341987693efdd33e984fe78d171703ebda97ebaa07d1547b9535ef4e911605672183dbb86b18497de02d8dbc012ac73286f21abda43a8502b775b31a8fa057bfc73a59225b0c2742f8fb70734191b6c2a2956e69442ae46f3bcfe1da531b1c13500094916457761016d059e13e9961d354db54309d7c350672b98470790f492735469350ca7ca3088e78a61ab4d7954802726eb0041b218b935b24ca8a9b29f680966a113de9730f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"64310b09b8ccc6b091aa87bc74bf80ffec4d799ee2982ea0fa35ec24c206243f","proof":"2ed365b7e800f8a7901841e070b96c5cd538abb7b8188d3f10f2877ed512c13cd6f13eb003655e147ae5b37661c026199d4ec3ed09a9d35dada312d7abee44518e593df2d4dde2b6a232e985d8b17e575ce67a162937f6ce607cb87c7905be3a4a0bc88583546fac37fa6e09f1263f2bc7ae8dfa7465d95cbe7b3e60f6f25a3d3bf96a98e8825bcc85332914b6c8007fa4f3009202d33b289a3e7563ff091004018db6d4b44481f392cec757189f7a837434a177a40d001fdfe4ab1ec5f5760b8356a3f940532bcb65d62db992871e0c5e9f1e14fda7af378ccb5abb809c6e0c5c109a3a3ef92683755e5111750ab2b4283b7c4a23c887a7c6b8d0fea638b74f4640dc5d6a8c939ebabfd4c0b8c331bad27888c0cf464567040d1d85ecefcd0b8e424140891bb990f973df771cd0e2d1879fb575eb9ed8a9da1a04406691ca4d76e117513ed14b80c362bc7c5e7a8fc32d0e2058154c70eb4176fdcacbb3cd2944764fe03d21c9ac9c39d48d9b61183c7466c6400872332ad96d3b359808f839501fb0c4f58f2b17626f35c5ec888a0f6fce5bf8a376dd2a79c4fffe8ded3e02d87f3def0683f2b6826d4d3f45153ccc0c280dfbd11cb1634af8313c0bebb77702eac475cc1b6372dfc4ff3cae6d465a0a877f7a933b932fcd10e79154386d40c4ad7a63fc15cd1a4ef90b817ba1da7425ef6e69576e9f59c2a49919028ea62544830f7bbf89af73316c0e144e0f9ca7e0b8ce0658bc42e8cb60d93264d18a60b2c9033411da666319ccdcca2430e1dfe0bb96a38f73af17bce6d092291d9244729fab4ff889186594b920b643649411c682e36d60f4bf0fa7102968ae72110da311ad26d6ecea29621230a7fd9ae6c52340a4c4d752283373a8350511100604750e0df243b089121fc22e7791d9b0a803a725e78b97154b704d01f981e41501"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1a0387f400afd23fe1d11b7e7f455ef36a398f3afaf9c5b2c200a271bef1fd2b","proof":"aaa69a9dea209aa54dee02c7409f79eb01c65f585c4e2d316eb8f8ee3c6ec564862393016062e2c6e4548e300d885d4074d92fa42062040e1168607ef873bf3a5807f0db9a866b44edd3787dd17dbbb35d260c627bebd78912c83dbe6b83f641ee770559069a215333f977abe890b32721faf6c1d71b88d55f02c723862312449e8d448f15cc17157f0e7a6e3f4475703529cb7ae5a0b010386719a69953ca0080d956d9c6e514afe237a31167815977c607af1f2a29b2d9539bcafe1f3316039075087c11086dfd5e8b0bed466aca3971bffd23ac3b14006005f02a2361200c546a22bd8e2164c36a49871d18d92ca0e528229891ed149c27e057d976aac4513c6cd02d23436374a9c0999b2fc6e7fb5126988d9e4f71d287551aa693e9b4411ef39a3f05301f7e51608335732d60d2f608480c616fe2339acf94fd8bd4f5331e487474beee233c4330cd1c93613d867c5a29a086ebef179436602323aa5027b8954d27b06de40966dc2cd6dec8e613a64bceedac15beaaf8c1f60dd19a726200f2a2a1b63c2e7575c6a9d6ab0ed656d7fc71c6bf8c5e593e1e5d7eb099c164f0e01e5fdc001698833324e19f8f6f731edd264f63d28dadc5e882443bb91e2e180dacaa5164a063c921a95ba928043f7a25b9f84d17ea6e5742201a1261070ea8ee800f41a064237ef9fff008f6cdfb62e6b2468296f6a69019a2b20af734698ae8b71700ee6d323a47caeba637eb684e50e0de8e47fdee1821e11f7e95bf780448b581c050885a9be4c6088b129a701b97e7eaeff0bc02ed634a7202b28d53aaf7aba86d0c2a87f90381af6239251dde8ea5930aac97ec958997c232056e343c96991d7717de7c125b37e506109b9dd54e3e01f09bacc919f9bc49156d7707e58ca5a94ac5ab0bbda6aa5e48a9c6f10b3be4f64425fab31151f654ee675302"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e081a72bbd1a6891bcb4d8263dca105bba7d28cfa6e65403ec84b16f27feae43","proof":"3217f92ab107eb5b762b08361b1983dc515b28544ad7b01199fca6528037625fecdcc4dc2f8e4aa3b1fae1fa7a5e59e2405a9027c11cd0bcbc58991e0a89a55c52181b6f4cd4360a792bb83602fa6e9eff16399f4148f17acf734726b25afb350ed44618cbfe3f80ddcd8ee132f1b600e94a023817a6857c558c71083fb9812b32c6063670195c14a7eff99613a2e5f652d2e07db8168152b5932587ef9fe507beb12e13fccffc0fe9577f25c1048294f498b9dfbf17f0001f0ff26be03e700796bd3f711131f3d1091c022adae12300cafad487c86cbf47405adcdc6f3c7f0c881aa0961bc54292e406cd28db3a8fa1587cb19d54dca7e3c6bc95200e8af354c096848bc09807f791133a44c059d2dd24f6d89b8ac54f3ea452f01f436bb949605e0ddc201fb49a00081cbb3f566988a5109da30210299e34b9130ed432716ba89384e3414920d684dd438fcbc5399c6b8ca5daf0da27542f757394e377a915964084b552c22bb452240ef2af6e2a11c9f1482adfa3deef8a8164107b63da1288858e8bc684221424f7abf28dc82bcd51c7e288a4ea0b388dbc13260da0c45bba9d6c2f8d4adb01c454939cda6ae4a12ed272265d5d8f98c61fd2337ab0966634ecb678648c6e1a8b10919b8262796a7a3a970b81d4d462fc52c9a4de5e31029c8fcb08f8ab0d49fa1be4e20ca41f5b1679a6dcc2225f6c144e69cded0b3f488e7729a82d34c84715406c8d713a436ab1cb9d75bc88373609d66230cc56297166501b0c3dc89db7fc3253063f66c19065e6a2b84cdb691d2d774fb08a45c357e8e2cd7129cbf2891d3b049a6c83ce27937a8e9f1724ae911ff485c36dbad33e9d0f6d2af8d8743ea2dede106c374e0ac22d06d13b215ab708b23aa8a1561204c85bfedec50f28bc1d1025a4e104c338686e5e276155ca1c1c9b719f92856a05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7cb3dd32bf39c563da968f9aa36dfbe4f1f14dd95cc55fc9b44d52d574f65458","proof":"f62b64a180484af6bc9cdccec4666ba2d0849bd33274c7c0f9d97900a07a65537aefe6cbc80a051344fcc80637df15c7c6dc47489e4c71e6f924dc6a46611170d4357a3a5171fa59d9b9dde02eb9ff33ac2f6b30968422bb0ec3db50ef07156ee45776ac72ac1df666f8a22d2e4b0086a863e36f3cb34c8197099443b13e644b6e017b9f4f585228b26c882bd470598efd60d369562b7ee6b108ad076289ae0d97594a31510702f5421f75a3f7745388ca94ea41cb6d2d686e008c9d7ad1940b060c564c5bcb0d349e8d3bef4d408637201e9312947a1fd31af80bc42322ef0cbce38b7e578d75936eced17efe9117eab50bdb425d7db9377e6cd09c41fc76627e1ccbf4dda3b15f36bd584ebcac42c2ca0f711de8753e9a7484b25385d13d081ec29160094106b0910a9bf66ce38cb5f390d85a3f9be38bb564c92cbe4e7a56be1c0a8d97be1ed490880b5926334e901cb7bb3b3772ba0710e327a0543efc3202c4635bb3033ae07975e706fb3586259f8ae3c9525d92721a70117c02e1790bc008896523c7a34b680283d73fba4b591e9fe348d18b97b0a6ccd8d9f7e172477ec93f2b8d39328d12d646175c4b135d2c7d86a9b587e522959a499834b4573cb497050b8c1420b8457bad781fe5ad9062b5c12864e4ab76dd52d48e10dd8d742c72a137b26db169c028b27a1d0823609c09552436e8936d437631b453cd833e30b9babbd76169ed01b4a353602f180a94b19f94348fd52245f78a6b4f7f9e4fa0b665cf86b65bb67f7838076ee61301008b60fcdb821b704b852de6dc0371234a2ffe9278881c05595821cd74ce9aee3888e5c38ed97b28eec2410268bf0b110f23f495bad8e659fe62dd376da555950090a02df643e653c99281d184f4e30eba4087d11ecdf372627b54d226165a8d3731b01a9fa7e5feacb7258132bd3d08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"28ba9f2a71dcdd3a33087046a7abf304ccb910c1a30d378df8c05dd4c3d2a040","proof":"fc6447d3beab5f76f76dcc2644f1b53fda4c6a0f6c98e3cccea7fea7de197a1bfc3e8596de2ea949bd14decfe4b086a51ba0beebc16ef1d5762551c11dbc0e13183466b345575c8de37bcdaebe8c05be2bb48074146ad905ea5b2a35644b2544bec92b8ad5ca3678765497b531f49e0340cebe2160f2f8c219a3aaf055148d2bb580e7bf95f011fae64b708265f149475293a756f366a1f56560ddfb4a1a6a00bae01ca2f0731122aa26f04f993213b20a8edf81e096b3746def4423bdd1b403c04933d32acc2dd045dc3e8b3bdfb2f3ee8697f39229a93e8510ffc72d44590a1cbc8cd3c8bbb2c4cdc560febbf56eed34560e78f3af9502609cbe2673f9963a74df82237fb9a9417ebe09a2c7196cd65fda5fe8deaa840b567924633756cf4f7e649f6d0ce5c574da97c0dad865c4c45497e54223d04ea3080ef07958af9a091e51766f2bd857d9218049c1852614aeee7f3507e5e1c9977b9533189d93971484f38b620a91ec5b0b02e08d2783caaa0705b18582a6b456a7dca153e696e529209c1659f950a47c5fb50af4c51aab148b5e5c7751a284f9b3c72afeacb09527b8d54b1ebc24d17c14da7e34a6459c3bb8549004d8bf3756cc11d17f155a7260f685f1775117a289083cfec5ea8f2b64f114861c45e7bf7217dc06f0cabfde12767fd4faf8b766b3974a575e71171556b31e1e100929d5bd647a106de94b050f62ab7d44fdf565415c45536a31a14e28c9695280a4baa9fe7ca8777bca114e7e6ca180f93219c0dba60b1f5a7937f9b09b9e3a8fd5f09a812a9914e231d8070fb26cece6bf10904275c7f560142f5e811a35f1c452722f98b7224d5be0e68c068f7661716160f02bd2400675bfe091535cb2b050975fc54ff7d0bbadc3fed90c08e5df36ef6d8f2ee4dae935afd4626e750a475b2f94fd6dec4254673fa5bf0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"823468b68b02568d982aa896e5b2baf694afacf5f608e3d6a24fc6f59bc7e46b","proof":"e262741acd4e052d8abcd8a2324e514e59f4eeefbfa2c69a3787c15fb2335872ea6a70b0a36b2b8b179440e6b2adcd3c9f8fefd12f17b3fed7b50928cdff9975d41351caab27f403e09923abc5c0973b5c7d9a2c27c69120e8cbec2bcd5eec20dc653287fa093ca336b00594ac6593147c142070de833ec814fe65ecc8a17a2415090779b569ce0d80a6f32cabcedb15804e206c997cd55840a130221d44de039d9d9833d65b1f54c027ed5d79ebe83c89d402449855fc1ce050f2eeaeddc60c13f7362a6fe11737ca080894754fc3c6ad9d6ad19f7cd01055de3b4f0145520578e15926b78d78fe9e635c90d11bf9970c721e1957e308af1a49a19d0a77417b00c1f6675555b56dfd7a4dedbec9aa31bf9c967a51315cbd9101ce4deca7d70fea9fecee96272ad40838f2ab072d642688f0427c1c076f90890ccfe3a5d192348eac755a6f3732f6a67246d379fee57e6a715610f62865ba1e22ec1dc586b9328e64edc9a3e9144abbf2ff9229a0ce81cd8707eba81dccfbfa490a10ac99f47efc92d2090a741d9d4271e951b896cd5744418b939237630aeca9d0d95c1f7f37e827294f5219b0c7dbf03fab5cb8111b408faae6ef5709a277d8a6ca8958af33666a90bd103b05b36e2cbfa5f91f720aff3de4c18d44776b7739df254b95212c82356f70bb583883ed7e37fef6eb15c772b0c0fbd20a54d7f7a055ef9ff36908a248d2901a0f4ddb33f31e75afa8798cb4c63efed7c8c5ad1a9a1165942db408a8626309d8fffa2bd5808325777552afa01390745a5fb2e2b9ff8fb73a3695539447a34cf3e25c7770d465b5a5329e3b81c56d2d29a9bcabcc3341c6fc0331395f51dfd239ff057eda0ccf8aa1ec0add78e6419a2e78f42aa6304ab353b8e90d811b87bd47882cf3e1756968f7bd81b329904bd91811ac1ee1bba28dc4a14200"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"eeaf6353206e32e4962a17742027ad1eff6a4bdc99dd0eea1721a37638836506","proof":"fc2814a8ceb7c8107a09d79c48475c10c955ea16f1e64d8da9c76823419ac01144c4af09791a1ce487be0922333eea37e02ec2166801ec2ea25b3b1f3287c16766a4711f3b2e9d7c1348e15046dd4d6624736607a2585f283161de0d13579a771a9f0b0c9c9758af1890b7c0b4b7637367b98dbad64a6f05ee73dbb7ffcecc2f9c9f6ff810024dbd65789b08721ba4185f88b97f3bcca22381d2877ed8616301368793856b59f40e17c957ead72f93a20485f83bb13e29287e56382700c7bb03484ebc73fe3aa97b55157726b54f8c39a2a9e660aed2d35ed600336c47425507080b8f86bffa4900eec996a73c4169e4b3cd08a071675290b1d470849b45355c3082b0640df51f80fe8bbf898da99c0a74fa6b5a9ab04e5c280690583e8122671ed65c14d5fe605d9065e423f1a43447afb168799f1b93c6ca568bd2690a180cba5c3efdbea12f898dd1768ea0b559568e706196aa21ba48556b7cb9eb19ab1dca6b5fa7d84009b3038c27300f8aa11595bf76fe0c10eba9286423f329b9a049aa9792196d11a34a777178051347027ee224bb8304ad2074b90c65ebadf6a275c83c8cb111008ce6d985ffa51c56801a418cfac94d46cc1b6d176c86ecbd25459029fbd35eb88ebc5a2b4fdbfa3a84f9d5d9b60a9a8b11f6ac22cd8e7745687fa0932ecfda8ce85f9b03166c9a69d7457b1de6148f279852ab8e7d0ec6293d2ea401ad9786c1a55518318e6183bee636ec01830acfe4246c3d3213160cbae124fea6aac19c6f4587405b5c0858ef05186f7e258959c2ccc3d3b4eb6a9dbd683af8ba05e85c699811095b96432f50467d8218f7de6c171920caf970508a067d516f5d34156ded48ebb29b779961651c8a950966f8830b027ac0beb6cea296be042d791a6c4097714c08b82a6abbba5d5f6352411934781616e132fd877f0d1a09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5cf6cec78f8286529c4f8c73b24ffe5cc200379405ff614c24e002c4a0197437","proof":"2af0a7e393f286e89e8ae97b24d1d8bb1c1fbb0cb48c0cf56ffafd40df212b3e2817bedc1d831a2f9380ff7f8d6ee69fde997fcba2ce32356f240e5e8b0a827528e6fcddd09553409a6ba313dbba88c95e2325cef5c951655e6015634ed546038c39289419df8f134350bdd3fd69a5155b8d0575ca8e7cb74f6d5356f34bc4449de8e7a5e4208cb6c2f88a1f76151d4223c36efd4afad57a559357bb0bdb5d0a6093d7d05d46936efe9d29972d41a008c1a3823dec28dbcc5ef8790e60203007fe60bc69e1a161140a81d2f50f5d9a2f4dd3d17f2772b3abd8c4c5ce8de068015c30201003586d9d81a90f1d0be1fafef4cb36969e7cde98d00ffb6ce4e11333c8ce376c52cbb09e960b9dbb19c487a4f45af3c76e2d0432e7a0e8c67abb885082fb939b6ae1e38a8da4f7ec77d63de67b36794bf81c7d9ed6f041ef811fe9427cd4db0ce2232450e5a51a608d25653f6168d444512a7462d9d9bceeccf58a571a0b4ab5b5bd78a0ae3c2e37305c13998a40fb005217343e30a0fe8f7b55b864980d551dcb48ae9f24460288c769d3d5aeea60e116cc09c1a0051eeb6926394c16a9fdb35b17c3b0af5986e189255ccb891e3eca535baec5875cf6abcf47d41d686716c5591b65c303fd6edcea5b57b765b92c79db60f04c72482003e6f49064dcce3cba910c0f9fd188ce2832488da6ea1238e8afff16a9151739d59dbfb0105c675757b6cb27ceac585a9a58514a48fd0ade1aded1556a73d8dee5b383670cc015194ecb55be804b5d04a3f1fff38b1ff960f431621944f87c3417a2364d0726d80f50e7efa24d3308e60873bc53574ca52b81bdf99b0534c485d66bbbb87594c9ced678db4b335a7f4abaec82d3f3adea99942198ede41b4d942f3cd40f0943bed259bd857977ae55088870abccfc7acb6e8ff1d7053e6fa69a074ad89008"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2c8f0cecc11273591e0e5c42852da0db46cc5f1d3e67e0545106a79f54325167","proof":"82289cff5560ed6adecd797517dc1293268d1cdaf554609821489f4e222e8206c488a2e8a519a59cdea4edef0730ecd917d625811811aaac7d58f0b678a934120e68075ab27f0003a8685e95f755b358cd36161a1d879dbd0cb8830d7fbb4465ba83e66e05b139d761d66d4d4bd8fea69553d0748bfb6589b095b1fb3692be49722089fd311f46e705229f7f2c78df50388dac5b948198a6fc9eab6b4a8de40a71cd1b2e493e979dbf3e4a3a0244d340845160d90f1465ed50f00507d3448d095d9c83f55002c40f91a9d39589669c2dfc2e32242f42ba92762245d3348d300bac971eef5c37944cb457bdaba0f0614c96f45c3b262e8eb459b7a04a5d4e43689e21c1e7e49ef90fe89984b719daccadbbee10387529efa40d7dbb5b6323282702b41a7f79dc82844f02d262dcab7a0f8e56a94f9c4a4e4803eca2b8ed53114c729aad06929105ea9841ee2b5083748e039e9f5dbb09060aa882592f406e784e2eba2ddc5deea91b69aca1e61b7d12d54263c148b9f6e35c4beebbc46b21992c703d4f33a25ee7891454f31cd693240ea0a44dab38499dbdcd5cc5c18d6f6d6cd844f918b8ea27db4fc53ff8e15194acf3771e547c11f72ff8afe24eb13784047a010daa6ae79afcbe5608a4aef12e1e9321e5ce1e8bffbc189b9577ed259a45866b4556d83f3fceb7840bde22b709b47ef58a633f090f4db63a8921c7990f216cf2aa1c325cfe6d79920e80735025786661c888b075ed0cac34abcb837bea4ab4cfe27a781977483d9d80809cc76a8543192e163d3213216fe5af65d1b81623f27b930b46c4d8a0732d429c986b2b7ffdbf485c55ce6af679f54ab259260f4ec87b560cf2cede796b02d1620e42f3e298567c9abc30e33995f846e41c17010f524569eaf86d810b18b09412179a720f0a263a7db2be382c0e8e4a33d4b76f01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4897855d47e0e78b1787390a8bfb4ce1db1d383791251f79fc16f98ba3f51c34","proof":"7886780685d847bb433d84307d7404aface0d63169263913a4e05f70fcc2de5e54dc32084138fc611b0153251bb5da572799fc6b13f4e8d9499c1f982c99820e1eada1956d3a73d7a0d29040ce7f66da466b501297764cd4f44d2c923657ea4dccdbf73ba0adf9a09729c17379e5e4df56508c00dd64839abe78793877e5157d7040378577ca10cd4c8d9d19c0bc9b7f5aa3d50d6d1d62ab2aa8ebf0276dac010fc68390c49b2a79cd28d3da8dd1f24ea2400fa3bdaed00dcf60a8978ccfcd0a71cc942b22ffdbb5cd45e991db0546d84d47282d50d5a131d0e7c2899a106c05b25f0987b5a75c787d81429a89e195142003c724c58abdbf1da47c1e36834d2b50cbb259a1d4471e8e9962fbe82efe3836e2f5944b6c6316a16c4d58802eb326060b05ef26f485e892080f753054978b32ee8c6c87a49b04522368068477b81f1a759d016d55cc623ccf6ae47a58066377f229c5d931a0b56dab1da526d5fa5e6e95a60293c1f57b68d5e5eec24a3e1431805aa63e798a77d3be16f1b111eb56e4886a84e510c0c9bcf21f6eb8e59616925032d307f0a59e2bb48394074bc4252642d77e794d71a299f19fb6bf35480a177f92367938e312902aa6d76cf11173620cc6c16f7cd9ad1706fec96bf47716346d53bf3938b1a13a6fbb210a0f0865fc7c5a440b53d517e45a716fbafcba557fcf9a7b66bc32434938cde831881b4b0cd41f4344ed6a4f5c976d26c40f9abe98651035c9b4cf7b512e7bc1c7d8e244bc293ce1eb29c0e7e36a55a4cd49f8b4833299a43d5353197032378ce0c6f237a8989332c650471982bf66f6947fe98e465f294eb9c59014973ef83ac90c2e3dc93ac993316c98720c8e6fe2fc7a11bdd345955933065189b5505ef1127c7b0eb0d9c1472dfd768a9ed52973d119ca8099966ec9e3be8f25017302a2d39ac000"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8ab652fae88aed6f3c602278186881dc02d4b77b15f6431d4aa8c32b1bf83818","proof":"8800327b2d0e12e08c28e90d94d0efe8a75ad0b9d5fec5c8a750ec5954d591214245e25f6b5be890e4be678e9294580f81ab5b5b15aed1e07d695045af7a86003c9e44cdbfdba73230dacd7a230697622aaebcce201b05f73cc74aa424929a6a58bd1895306b1277d60fe17a282a567c85f7235ae4fe1e66bb2966dd8e90a84266d685c1c9a238ffd50391ad6cce6d7988c77025f871058a7687f8c87e55fa0441fc7e3158da75d529d798501ad1414dbe366032af5acbda9f06166cfb7ae201ddfed596a7ad3534956f5a59da07d920b702a686181bb556b28227f594850a0dbc9d17679e20f9640906b038e3927d5db8b15a1b4e4c3ba021e74180e433a43e569aaa22f0debaf525e307bf1871a4047c2fb8e495f958e244e414ddf6ab110a9c2ebcfd18de58e6d55aaf5888f9397df057af02fc5f44d089415df9bc929f565ceb05999e8a7ef8cc7c655f5ec4c2561121ab289002e1d11657d2a3728c3c482078a0fd56d9d363dfadbc2ac27fca5becc267d0c4edc5b35bb84a59b06fa46b30adad24f5ffbeae79f5a1247dcb4cd5f82a5001ad0a500325b444884e2be4235ce21b898ce9237b69ec96a030096aca85c3059786fb0feb2ae9ed4d86fee51912e9c02946ff0bbbfc398e8906786d2be174bdbf920c63514962ac235af39410a837902bc29f1c89d30fff512e500bd3d43b43732b21cd526aa0d6492d0d85668a10ab8e4f3f1aab4d62e205a2e17731e883390364022fd7686dc0f0a3bae4525699c007f0e98ff3c3c05a40b89e109bad12c9f5b9f89c78fd96bc059224905a80b721b6db009192f9d65682afb769752b0f6605f40bce3120858bcbce268b340f7fcd1423be33b13ebe0912f209bc01f792449e101b76344c4540b3abccc408da793810802ef9bbfbe50ff12659198ac3ac9c92cc681d566ca216893705800a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4818367ee5670b459309574c2365aff45f0051e0fd1f300323e0fa1e782e1f30","proof":"1c39a9c0ef77c754874ff5ae6b2b6a6b37802b0bb043cb01d9f30daa2688a747a8d814f5bb6565862710ca3591f9ea810b59953f9e3e5ef37a69090d5697f26688ba8b2c747cd0f955ed73cbb2b21ae96f11b95a989cc41e0cd43e8bc7202237c05ca1f4269a25bb1598b34917cd4ee83b5740ba365131f85f34e1d38085610c70e237a8ac054df9678b4bc1c3ef1ed781dc20167eb7a2cdd47f7a6143a768045f9bc6b6af91e6a311b925c73efb0fa9090a0ac3a105403e8b0a0fe65c1a6f0c1afb1d94e47bde953c8a5ef5ad16c6696a855329c50f295995b9ffdf9e682c0f36d382fe133ffc2f4689e075b22d23f4b1a8f5269f90862626a377675752f87b74fc2aa9f795c69b85031bfe2e850f462416801e26a5c7b8c735b60999dd4f6b3cce78080541240955aacfddb087a53d2c83a1fefde4c6536a07490363688f6c20ea9ffe4f8ca4e561876898a2268329e1f71382b1144d1e36c83d84c3787b1356815cee6dddd4bebe51dc455a6b865e1f85ccd776dc736568ef5e4d9b09c0019e5571d5b7de2cac1c003b6f0a06f3944ac0cbb53eb5153513230dc4b599c576cc34a3ebf4dc2efe3411268c2bd0fb5e99f0a189b5cafca6e149dfbf71834c3138e66a1a5e37d01bb1b2211e6dfe157fe12f0fcddc2bbd6e03136ad659359211407f1a59d0b459e6ee37c1c67ba09ab3187ec1fa25eb9d6af7e2a3df375bca028636952f2134cf7c21dfcd5fc082151e85ac1825a40d3c30b095a2f9cb1196415a327f5b73d417d4aa25dc98508bfcd6dff87697192b5f81e49e87164890ba7f84eeee13efd708f5caf4a1a59a55976823e317e13371232fd021fc454a117f44af75e6304a49b7b80172448929d4951c1cccdc27783a54cd820f747e3a415c04383bf86a51b669aa0d0aac00f728bb615a159743490978cf243cf461f05cd308"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"eabadee9452192d565197546c8a7c939ea840e5aab418719073ef0b5741e6368","proof":"7a44dc3a39b05f9a1cd1571a2691b5e34a61464b2c0dd1702ca29836ae0b1a0ca80816397cbcb5cffb2c0c5842220363c4739814f3563f68b6034bd36da00f51da2eed73c1eeab68af9e02b7239a30b8c1e1fc2c698a6f1d7c41513901cc934f82b848495fb3d4c19d30987a8f52be7a74b8784ff9476484b5b4b3f02eea990722eca95cc83bb404fcf9a111e2f451a5321e75068072adeca0a921d09b9f9d03bdd343826dc479a0ba509d02f2bc6d787963f5630ef6900eccac8cd6f2faad098b047b61b62ee7ddc32f26a48b7e0e753f546150c2bb5fe21d9bc20cfa39630b0a26f803f64368f93c9f5f7b4ca86bec2bb353f29c5ea2b3ed3f7de5ad9f8037ca7812867ab72b3d1833a4ce735e69418f8d18f77048c6561e57b28b9c173c0c6410c56f6b97616b591567267ac33dbc045933e1a7649d50b01af3d4d60cd7384c75441923626b0ee67129de7220f7339f8aab79d1983e61c4fe946fc4942a18888c9b868c052c925dc417d60f823f02824bd3bf19aed000183ad8a42a101d23ca5ca20b28c4969c6c4fc9ae1f7160cb30d8589b226e9c52c76f80ac6491b32d1cd1ff20047bc9ed6d4e7e067f5eeaec6be53e3348a0ec7c3e899f3046778c5b20bfb5bfcccfa03564c3d189e6aa1d6810fe8fbe5568f61fe1daf99f2ac12371e01b68fcf30beaec9dcf0f0f0590938b992e368f9bbb0be700b1775cde7bb642722f1a5536e96f3f43b81109e53549ad06a55a228618709c52da92c5d7a1c456ceb7250f836e37d9e51a4f8bd11680a977bb020f36aeb8c16deb331e532dc81f84dc5c51c448e99ba1211e4b40521ddddd37106d7af39543e05829b1df359e0c4c82f2ad5ecb4923f743e34862c11b6abbf6f23f03e288c8ac2422fdb6898d07fd118669e2165b76df4d117bbd6c7bb880f7e59a409d952741b8bda8b470d109"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a62e0006de077b9ccd775fd9a77464b5f30dbdb9dc11c5ec5e6f349e2d4a546e","proof":"9ebd9c6db5897dae9081350038f8f1e4a76521bd97c78ba4aa8dd40e1b5dfe4c607004cedb3038c2faa67116d2060577fabb4b53c1e3efba6e2e2a45de175e7544c4cf78a4e97bb697da2484c32e42a1a932157c715ab892c23641511714a7342cbbd486ab9c03f17dbfb37979c8571bf5f2915c033fb26f3789e7c6823c1b39d23c8147a34b9799293c31fbb21c8a0fe413028b1eba932b1db4c6bac84f1d0bfa3a84e15544817c70b427428f4da824b240da6784c1227fceff1d293e21030d431ef00f2969d55ca90e61f4287e6763b3c93167d83fbc898418017e18da8f026cf4c56392278b8da415f2c370639c8d90b7b10e752d1fd7a4b52cc6bde7d03fdadc92abf279fb1a1b0fa5fccfb2e5043b28724a929e312fda2976fcc90aa85be0978da76eb38b5b0d0686d6f6f7f39e8d8619e6d2d7ca5db8e53e9a34fac41e1e3e44f5a3214064b74275f621cf9ae29488df98eb58fe1a7c0fee618ac3e4711a0a5f0e1590cc3f41142521d57902a11b1e279ceb9154dd8f0cc29bab52bd729aada78295f91652fcf529486f2637d34012346180ea14cefc3a40763e8ff960d46d96ce598718d5085f386a87ca9daefd9616da92da6573ff83f3e3822f454356383eab8b8bf9f8d1f10c89e2d42cbf9760ac2000ccae4ffebc669e1cec1f18362e5a8adcea1cc2b25af117a015802ccba52b91f14f505d96eac02fe1ebee1556386a4115ca473e0dfd5f1b46fd75a28dab61d36aba6e63e7cd024c6a14ce5b3aee31befde29fc5eb3173345c26d71114a21d36e17409c9c8dbc69abb5b0e40aa9baf0900bd0359cc6c192f074c29383eacd3e76dff0e56066d4e3d9fb2135fd6db28293a0ed081ef71fa7ef6ac9b513c2fc013225e329ea123c5959ba8bb04456191c490fbb6ef99ecc1f886de3b18d45fe9dd3230084af55be8b452f02f08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"60c29c28a3924b63efa69d4c74e2e1f6b212e4aecf53e133c9ebd70c953adf4f","proof":"082515d4f02dc5fbb092501c751208d1e846adb17b53c3ba0eb13173619ed13bd26c9473ed4ae03f75452a648d62922329337337d135d2d95825ba861cd9ff29f84f8cd938dd8a66afd67fbfd2e3c3c0aca90cd778f0f6f27c63c83a61b9c637926d8b0c1298f7ad06493c7028a1256e582c90937fe0a3aa8807f9deb0a1362a0a1d8f3770615e4a08a9454eeb20fbf6a872e0f18275de94818e0d0ef3783e03db5f5738496c51f3873867333631ace115115b502b6f5191fb2e032362c56e008deeb8a7f4babee8797cf4bf372bc9c5738e9ff1c48c9e39dbb25eb8f4994f0438d23726fae63a07f0861db8df6b2312d32eb5785047808513c851b7345a8a112c2613ecc83357dade961d693752e67d8dac2833c791ec5b0f43a55d47bc8372ee41116820666dd1da47d812e75a00c29cd72059304e7acaf732812a011cb919ee84ec755c008f85cd958ad3b0c13021dd7e24eb8c783e4051b8a46eccfe7852d8c6fea3425c9a2b8cb3cd3a5d9f4ef5ad778b439c2e176a8bbad3a2c029d32a526606d1045c477cb183e25dcbbc4ddbba5f6ec7c24c04a29aaa81af0d7cd979ca79e2af1eea6145236b048107bed1d1eeb4e0e14d60fe0abbedbc3b0f1b9045868b080b07521bf8b19b542e2ffeb3a1e54d1d9076bdc1a3bab93bf0c7b5ed59121de350c31582e872d0d93a57446cd0df2195e478c97ee50464c23a4256082f3ea1f891c3e48e7bbabe4216649bf40659139cb90a5d35ee7482a682b3026726b06f61876cd4de709da10f9698ee4b27e0f6377b650880fcebe98000ddfde36b083538878566d485c1b9aaac1470379d80c97f164a464c07be2b58c21896a73d927500cf4036a33c8e17555bf1fb684c2a6e8fbb55a5022c82e1126bf5080b018da6ffc47109bc9791867e9f37d8e21df707e7d97b84c01b8a40651b51a7260e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7aafc43c2195ed1a224df92021db8cc021cf12be44111a8dcfb2d4555d319212","proof":"24169f1a3a5d26a02bf216597f8deb62906567cb0cd26a8d52a2600051a0391f1c3a2199917879d7c4dd346c0905ea04235f0888bbcff1457882974d2eca5b3548f6df66f17e31d6bd80262ca4ed4c392521b91a3ca4a9544eaa33f21237666000670289b35d2b8dc02a5f7e90cdf578b46eebb18326bfa97e709cb4767bf733b87d717a57ec3986f7c1964b2427344715d17cdf9aee1947b5d1e54b984faa0a4c208620f6c50bf4de044ac78c96f2b44804b300cb54d954cedfd767cfce4e03de1882311c9372b90e135e723ec3db80f4b811354aecf7b14b99fad39ab76e07d40297af4f8e22406990ee24d886befe726cc37802cdc23ba0f5a189a45f8c6beee1168b3a991a015b68a40dd16c39ef65f6ba2f59470640244a01a7cc376e2cd69d1641d9fe0a687a5d4063e0f00e968ab56d9622e510aae9d91bf53787fa32982d7b0262caf92a17790fc0fd3b86642357c0b88774723f2cc7af762b43063f7442fb8c53804afcc249f4561448669796ceec6adfb5af6f917047efef00571ac818c575f87dd403d972d08ed39ad600b22d216d58f2bfc9dc366e7147655106fa9bf0644b6193594767bb5f507073f84ecdb4e049067e1d3de066a46fed6d65805eb72969940d7d77d117b808007feb17717215c9fde68d47941d661937e60c5e215ee39f0b7c7b713e047c113ee8b57142d371b0eab1bfb5ff3fbad4a7a466feb68a66e6c9c2947091d2256088bc9159a99423d1fc8406cf9a3c693dca79423ceefa8668a87d1dd1d4c11525aaeb4570f30884e5d22ef2ac31a9457859781ca8673f3cc2b5a4b71f972ff41fbfcb23bdb1fdd4a3fd7a021981261d1e79b75d714123770ee0c8d72462f28430e4fbdf2052789e2866e769c75eb05d0de49908a9f2e4d1a93fbd49d78ce33a036c8b374bc772c4d60bfbec3cb01a014577300a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b630b414342240191e9b5a2a26fcbe3589f49095943c9db4565ef245b73bf71b","proof":"34dbafca84c27ead68d0799d81d080f9ad46897a895881d5c0692e8132483607d46a4aae560f868ea1ca9514ed4a4e743d2d76392aaefabd9a510997e5d2ab5612f814a2ed577a563002ba811a42825360e85b4ca4a6a2fac4ec4cbe73292904aa6b5f60531ebc71a3c6e87abeb1b480a2f352b4d81395e349e9a58b6f5c1535b12197b364c767b2cf7206bbd5c72ae0e36c6ec2e6cd4474ed37cad2a4b0610ab434c0afa89c2d5f69ce4ca5486bd9c6c4eff04b376149423ebe309d62d17b0a3c3b3a2f7c9355dc6c4f3da6634497854b87650f76839676b079d159bf94cf042c9ca9dec406660dc0833161c956fe22e70f21a3eed4d646f21a4476540ecf716a12b07774196008b64fbc088e5cf1287ad43ca3a905c831e91f863dcd47e1728c76a2ed732b59fe8cec43f255d2159b5960c9fbb349b197f2f8c4482ddf8c075c5f2a25d947e2b02b3cac496f324397c1edc5651d01b738dab86aa47ebb56440cd091d63e87ea93c72f71b2ed77a1859ead8be9b48f66f2083eaf0039277f05265b835b402ae79ce04dce461425c7a0c54a6165d8bc37d45e125c36b93a1b17ac04036086fd544377212b6e95dc602ee3f7f839c1ffc22e6a1abb4fc3033f6d4edad3271689e14ce9f257b119c6d5788186d5a2845d46483ae32de2df7bee00e499fc1e641c1be2a712d7f54caf32e54233893de9d97697cb986a45d001e96c4a2e8fe1dc1c8d34bb777e14203431e0e4e3943dc5d7ca7bed25260e7b06db314a3752914e5ccb0837588ee48b02109efd934195ef33fa06e1010b8116215a61be9fd163ffd09f500681504f2f0e52258ab30dcd5bc7349e12ac65e248f3d21ad8a098648fba2c289b2b44f44cf5b6bb3ce4d42638ca0a7a77aa84a83ea7c205c60f426edfc50fdb1366afcc2b5a85e74cb37e166ea420e319df48d5a7db6f0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"362066cbf5bbbc73c56dc1c4dd28e62fc6322c9ba36607a6967d0991c48deb36","proof":"6aa4db357a3fe2e54226c598564e057c71c22e475374aca186bff3934367100e36f31ff1833b259e4700dbc9905889932f7e38c412cf33f8a21f6b5d72adcf4c56ab27a07857e9677cfe8aa55bc7a1e18839f8609ac7e5f1733246212a2bc4251e32abec3088b986128db9fdc60cc75dbe3a6794be5e38906819f32db0aba17ed794dd9272b6352ef49c0074780087d07a76870288919a187343723878f67d0e9874b6cce7caf5b8ee8c6b39da0b099a200c3e80955319eacb2f26cd076c9d021a7f2dd19fd8ae91dfa19397c2cb652346cce8e7b0c560b4bb7217c5280e130d005d8425d334c02b0378320b492a6fab8f7fc97220ce3309fac8d7394c5830451676d8fbfc3f8d8b4bd022fee6cb3d311f62d990e719071a4c75ad25c63a1b6d966a40a3127117b7dba28f1333b41369c904ed49e1a87fdae28054a8937a6d7df440f77071248de14e9a360e50a8fe4a0209eae44f6fd51d92d962b1ffdd5c03767ab85fba1b3a5d834e1257f74405b2dd5d16dda646373297405d886208bf0e929b86f88372cef4a7d2fd00a056129527da88fd4680bf4ea09e59435152ca705cd2b156f95620313315957e83dd6c29d98b0c7868f63f88924963b89aa1104948683e076c53a2f56156362304f763902e529049f82efacd48436f4a26d15715fed8fcf5cb9571161bcc5aa1f20b0ee0025eb3f48035b5b1a6d035cc5ea18e6a7080f899cc1342b678dd7f9954d0cf6baa7ce205de84fe5aac1bedbe49ea7658063ccd5282fc05f518a4e8d0320b87e417ed3f5bbfd5692683845b7259e1a5238ea3c5da2828648504893b0a18e40bddc20417c638502ee21afb23e5aaba671194928274e396be5edb4afc537154546d57167249d3d49af41369e9d4b5bbbe0c03a58755e481a3d14a77a12ae5e2d709a8ba36904202d80b2cd8580bf844a705"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fc691145b83e7142f62c1d3661943066c8f448f2673c000dccc3656c6e61094f","proof":"18e8960405bcc0f689b612816e4781bdd363df951b1ab5fa2376e0b93a26e6522449799465318fbc95c602f21f1f79d57e487781acccc692855c8c52b943af0be62e599453342a053867801c22bfef3c0adea9489bc844194380f93c50758c13943cb35215d1780faa3f33e9ee13cac8ed009c0a91330ae8a4c085e49056540a267359f19ada32131f9cb6ee64a4e91ab89d10e76fc44fac74abddbc63274b0003d5459c62d9958ae5717e52d1bcd1104e83124a51e74759c2b5ffed3970e9043b670b698d04d95ad5ebdd7d7daa24f17bda253fe01c475cf6bafe997bbcb40df24c863013b8ae6cb92e2b8c4899105c9e9a39e6cbc781a9158b509771dab749e096cd7324aeee23a6a07a820a804264883f17ec0f1498f1e473af4cfe159e073873e0fd7c7e3e372dd6c64605819a2ce8a20391b70e4dfc5c3e2d3cd4738e2bdc538cc1b0d83f0b0232fb50961c67ee4a44b19319b4312a6f53d57bc88b331f6a4afa2934f99a1da9ffe62f89902b860689c0160395cc76ef4d1cb295be0a73b092a8d8f23d29e48fd0eba42cda8c327a44ba527ac89f75c749bcef2e7b0037cc1cf9245ae033dade3d8a95804a6593efccabaf448f61c92eff18157dff3256f8ba1e5afa344f33ee28a24afa3cd9271889814991c035e078caa5efb30aa013faec7e15856781f01daebf96ce76e2b957e7f3f0be1140ea35f0d226322ffa1f26404c28282a0c5b265874a220ff16eb6ff539fbae5c739fd199f228e2593a1c68ec1705337c632f508df46a6eb7b51b6170e86d399c617390973810d687742f72715598ada7025e19c93a375ca1555e335407632b8039ca5bbed4f3edd6697f8c84d36d5c44ad94e3710d01ee3ab7ee31bc80ab1530a5b5b54477a6f4f2cf0a12f2513bf077f23de8ccbb7dbc03eee47013fd3bc1bb513b2a1f729da4befc04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"147cfa74671561a0cf7d9b7e8c4394c3f0c16ed9c6ad4b93d2c673e0f6c68929","proof":"baab4c165b6d2f129faaea91542e5a73434e38cbda7a27db03c9e3907c19a83944b9be05208effd069060eb8ce3467932c36e484baec31560b122d76ce22e65060d35b402768d431803d8fbcef3f7cb18f06dc527035762b0cafb023d06cba701c8bc5af9cceac990ca7c568c24c54ce710c6ff24645c64ebb6c17ad9a06f46eefb4b53d7edcb6c0c3a5b994d0e3bf2facb0c5d1d5578383d7736fc42bbbfe06c557ae4bd181943d96131a8aa446f7dfcb74dbfb9f265a787b59a5f28e199401b76ba49afae501fa393dbf5c8701edd9fd69ff75120fc26e64db791b4ac9f10d02bb5e245b25f05ed3acb3d17af45a0c7473aa394cbb8d26f79c2b6451230b33f4116ef43b499eef49315ec0d1b48780635022e8c7fe8c508c2b6e8f0d9c4d02aa69f9be5941c019d9433c359299545bb6f6fb62a081b03d3b3f8881b8aba72f50e81b5f013bd06965b864f75378f6b44c3bf63760494b68d1a24db2790b1b608483301d0b3e69d61e6390557286083f0d0eeab72aa06fc61b4de126539b5a7ebc2d4bd6fd5d3c02b184f9cc923fc2382784d36008c8ed2847943191964fb949d04f14304ae0597b2f6c5785ce28636c67f313c7f272058c63b5e96d33a6177fa21b03fc2a65e7ef956b44834949248133d9b88fa199f69801644409e75f330cd85f9c4cdcba4146635decd278d44e20b9cc7e8fc113e713589634b49d53751242707feb5fe6c6cc83b0202017e05ae73a053ccbaa280c1f4560fb6e0e00f2347a8c1d03c8d84a572a1972cb4af8e77c32294d675d0a09efd679933bb3213640b00f150bc7d42aa6caad65b729ed745ad015d081f46071bb7fc8317c3569b42b4f2fb830867fe9775dea08a2bf052d7df6cdbb011ac16d25d04594a915bdd1086d75e203d3eba361bdaa1a2498f7f7b98fc4e6b8a07080bd14075815bdeb4406"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"32e9af692b652d775e21c656e7b830f4ddd5633ffe0f89782fee8eb2ac42216a","proof":"a2cadaccbac4c5bdd1807b71f16a1c336725bc816070e91c606921d0f9360f7192353d18525934694099d663ac0e6266f106481bb396825932c238adb290db0b34a35e407c3b2f132e114229aaf9fd57d7a5bdb947aeb4ab4bbe544337356e229ed8a3184d1ca6c193d7646fa12e524ab2ea87c647a2403a26ba887c566bae4d550eec39f8aaef7e3cb4929d996beed7c115a2ca23d14ebb9c0f7d6da2d58b03014b29f4286bdb9381a93e31a977151f052adc08907989db2b0fee52df874802420c7015e407c64a881f7280e9f79a66e626e8f89dcd72256547c553150ea70aa6d5e196d849729013bf7e6cc867611e4f78860409a0646eb19b90958d78696256e742c98f5b026a4477b5ce881310013ec0c8ee4f23a3ece72fe647dc0ad256787b384a45abbc7d6a344cf8eae4d0eb2e8eeb64eb4c0728123792e866f179415c80abe2db45ffbd17b31af43dd7dbdee7d1572dc496a66299d98818efb33a0614c9d9bd9d95b389d87243bab2d09b60db3c5ad0fdbe42d237c6ffa094dca02da426497103044d34ac8920dff685cc79ee75c20240c89c54d966cdcd472b3b2e146acb3520fbdb894ac4983da4d3ab41f1c51b390913048427e15f665d7ba023369d8069ca23d1f2a43d015adc7d29dae2baf88cfeff607e4b1a6044cf53590bb4f0c82a823e5921cdebb60099dd264571fdcdfdc83bf8527dc50b7a8e08ac0bc69915e1bbb1947a7cb01ce17a51cae47713e115b1ab75a174ffd8379a14b556ced481e738c86cd348088c9a3b9129e80ef2ac3e3de857d358f4ee6c9e54657400015465d0e2cf860125128a4d1f438322c4eb8657c3c7b66b473c214111623aec1d5a12a6a209cd5e46579ceda1a7611abed858d84b89176753c8801943bb0e4e2f92f13e8085678320f099f320287fd87f8bb913a974b77cdc733443a3e90b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8ea82fd2e20cf418c44fb5646568dc5616d702ee35a45a7ba81af3583bd59c11","proof":"26a5525690e59097614baf110fca1183e5c79ce829cafcd254188c2aed02f164ba22c19af2ea21cb1dd054dd30240db43e90d71ce73c9e8c7e30ce62c0352c01e0b74ef6c2f3206e1f38dab33f593b61ca195985d9cbda81816901d45ce3b705d2d3939b48392a4417c5ec2fd30611a193cf2b1bdd9f3c594c8a660481b10931355220c3233155cd7f8e515eb5ec5600e16502b0552ec9693ad54a5736afd203f9c0198376e5dc02e0c9680a017c94455a9c9ec20783f7f55d542cddedacce0358736623899e0dc2e600314d6044c275a738f790505c3d737731e52a33290900762641d65d8eeb36ffbaee06cc3016b386e3ff86eb403d220bea69c22918915a12ebed5671a7abf4fcf8fc18d9dd12116e9f51ab553ab027bc02d3009a0ea45cd2a5762660606eaa4baf6577efa263abf62f2ead7c0218587787d2da2c064c67fac2c4669e839f5c9b87f7dce0e5b01df6e88ee080a1a7e2dcb3d6c53ad2ac0d283de3bf248f9d1432c3318fc6e88328b96feae9724d2d077e2bd8fb04b8da7402d18454547d5b2b62dcf2ffaab571747fd40bb5eed1a087ddef159cca79de1604e0283d0210a08eed64c2100e2aa6be4bd82f824c1d82a3322df4c574a14a606c9008ef4864fb6df68a996241c6735fda56163f2a9fccd76915c299563963311c5f12f1ca18c97d9f549378aa4c7a807441546d8ad787297d60ea14b069960e7c6f0c6e03d43a44530225d5cb3778ed806355d6628da82d05976360cebd93744e9d7764e4d07a747af349b89082f33a25fb35983e2e9e6fe7439f02e6b9e80f38eb2d3961a3080eab4adf36cd735fb42585a35bfbd2fb8272c243c0f5363e56183c57498b10cb4c4eb0a9bbed185e6a8a05be0b3502f1a7c0bfc3c1ccf80007fcc8715c6043beb52639d4fe6f2d6d50a9768ad594e86bfe01e0df8cd8f39009"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d0157bad41a4e5dbaa1564a264566d386830ae57c16532427c543a0bba7cec24","proof":"7e9fad0efde1682f98e644380446ce0b7aac126c3b82ce7d6547f6d87d255d6ceacf491a645afd6517967ba9f372bae5646178e289abbc47f7ccec9f2c33243e9000c7d3e7e8cc9f7bd7b8a2b6e870ca36d7b1844d825d3930cd645bdbd6ea01aead89fd53fe46caa134795d1912278c559984983d2d3f2e5dc462000775756a51cf19e0e32b741a929a420d9767f2a6a3f78bfc99ed8e3bc026184e5924050581f495f6818580fc53ef0aa4b98bedfc68e257533adbd04dd9359b9c529f060343e00bcaeda4f4e4cf6b61f1428491f73a166bbb7b9a4de2b240605a274d7602ce08892df5a3377344481a2f212cfa2da943995439c4003a5edd406cfca85519685dc63cd3371c452d7b15342c9ce57e9dbfe5c6ecd97bd4b42d2970cb72a97cdef791f86f7d89f1f9c4b31131a9a8ec696c2c8a338ee00ef9221f6970efd6067ccf358e66645e24122b591cb033b5c5adf5c82b537f5b6939e8bb55f39b2a6df63f44a42234838a3d8727dbcd2e81b587148298f33564c7c353067507ea4f05622b7aa91b5a8f76bc9ac5dea731fa9e72d6ef0392039aae3432a41fc38a9267b42d6fccdf5abe1c74a54ff8b7ddc6c30922917039ad4f5192db8463b1cff7242ad6ab9db1c1de7a9eb168d64e9de3910b36123fc97a26e9cb1079a641c2f13d76e1030b56561d7f0361831a424e14153e7dd2bb3d4f6b5b1a83ef2de7dc8669d6848755677dde0f15ae284bca16e82f33e4a287b55fc324e5b5c89e53d212490043794c5ff88a4bd8990445a5e2a920c65c0fc81c373d12b5956c5c839e5b3e0ef21390bcc4fc1d8a74261c5db4d256a24533f2923fe3a0a2a1bac763bf8669bdde3e3c4bec3aabb7bd77d90655ab7941cfa376946c27de48fc7d51849d7903da1d80d9374be735b97fa017b8a7939204b5da53361e8065d99b5dcf185a0802"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f43a3bd3baaa3d1aec3404a442c7e00231e8d0a67fba94fd599d5c7ff0987c68","proof":"d202c4ecbfb8cd7b311bddaa8837086802fd184b829371436cb96e5b48d42217d229bb8bbd1244e76059f7d5691c141b7cfbd2c49127cc03e8cee015edfb1e791a54295d265a733f8b62cbddd8f49aad94eeac2fe1554a9195a0e32a40dabe14ec56399b5c620274ccebe8a6fe8a4ca5ef2f129b09b1bfb805406a238bc40a7818afac515ff8eef8552f07e2a548d200272f8267c4d8d9636d7799c284806e0a4f5cdf8c68990d8532e670949cfa4fe17baca8522c5951a4106113257a78670e83228a53f266a21e842c261aea6a307066e0f082308b0f168b38035facbb840bf2c5e71dcc1aff2cc4b65573273871c9f2974149253faecac29763c172bcaa2b867ea2c3363cf37836867c4b91b9397ebef17bedcd16b303a43cf93ca5f823727ca3f4ae8b76b6af023d6782b11f31d03e36da9d9a522dce36d932672b34a9265a750ea0b7125ccbf82d7f59ac9ddc41a092721afcc24e503e5a6163d94b114e78a92760c9bbd4400d0912ebc2ce12006d418231610d8ce68f2d9be25714fb14963824e23c17f50ab76cd5261f73443bce34157fdb417e52e997e465dbe66b0f06843dcc2d27a1d575f96b0efa6b654da4d2591e77c3c84f6fb810e5c0a20e2b6074cf4d751cfeaa2bd4808c2d98016ad522fa19b4a6a2071a56c0d392819d7ed851db02a48b1d72ee6b37b403bef21da1e78956d50799ccd31bbc4cfbb23771b602762394950c24f25217128830189bd3edc1a247072b06e935c38525ef600a06f8b9944e99da16f638f07c8152d50f30e815829ed32fcd54decbf2f9d634380eff1c2679c11f23fee61deb343d4d70b534cd7ab06ceaafd4fcff66d9e39435f09df7d58d5f4dab1400fe996c68f50d538e7a82be186a571fb2129313a8c20862909d0bc49e306237589b4c50f344656a269ad5a445ba59cc1859019b6bc705"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c6767dcaf87f9770076cf88fee023a359ff67c59ac9adc1d92ebf37e20ace640","proof":"38d3df1249ee76ddf83060f13162d00790e39309586b544619f0d16bd13bd32a889afdd886617bde84d756476c8e1c9524e6d9851b248c60039ae485109900265a3688e460844cf82556e1c1897c6263e04b71591127887ef7fcb5f144e7d31d0219e89811b5e5d4439f1f43397796cb298f8ea7ae767ba9d380e2ae28cd69210d9b8d0be9799d17873212d6e965715144de9f93de631d191041915f483f7405711c4cf98055ba0ad6aff2b240bade80f4ade45eb53aa514490053b733105905848ea982ffd2948e18404ea6d31a9ffe05034f388f0c8276726ba659b003580474ce3c4101178dbd9226cc3267f6338f2dfd7d026a119711348e7024c669af087cc891017f16c74b0e5c24d5509f4ee5113b3853628aaeb211f2c0ee4aa3f33bb27e0afc96215d1c7af36b8833deccd54a1230dd9e49b210a8684fcefa36eb5b2e9c51975eb1361d4a828bbfcc7e64c68a11202d96a8b33e65e0b347f1e0db49bc7536479ab871981126c1a060eb0d579ec8f9640c82d4a6ba32056973d48332ee280d61a74130c9f31afd6d921be47d2f2f286c5c3873dff7a5b9ffc3ed255cd880d43747451feabf1493a926afc3ab51d8fb766e9dcf5b0330cb61e080de415c7ae388b27cba5fe699b6db733e080e77995dcdf86358033a698d7dde5b2663ca55809c03cd2466196f1553f7962cee59a85fec1f8b10ec5a6ff75ac2bc2f798a5bc53e37b27d6fa54d9ccf84f3a6a2f92b13888af0b81aa54da22e52fabf4ee09a04ebe4f9a198cc048a552a64f14d2ef8c594d09a781150184958026842179a9325c620447aaa78d231275d179b1f6070816b7eb8ff39ba329ef208197e608daf20d4a68db982a1301c121bedfb80805554da61a70e309162208db0facd0761bb57ed43809d11d42da2b659933c039d62ad252a0c570b106893a2b2fcfb0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"54cb55f68fed9b99c64d839eba9a42b9b71c0567a49b2425cfb5836d23d73a5e","proof":"be8ed73eaa2f891979a4c0a610e54b5e45468837e28afab264b150992bd90c6fce3cd2c42e0f3833eb2846c499c7778e84ebc34576d963d4fb61f094368dca175ceb71cd6ac892aa17a021c7582121a942dd75436a2df001cdadef3b76c4f53392fbfc6534d4a6e95b77ccad828be39566e44bd0dc0e7132f817ed7a45e69611343779aa4644ecf9ba81796391a5a12b75fb09f56d318336d7b03ad7a2ec5d026ee1ed303726a7578a9a89768a447dca7f37a598d1060f94d577297ea98c890f26a906f53e656a881b67f05fc14fcc95dc6b6834f07c228ebef8507f86c0a20b32f0b19275fbdad836bd6d258dc1a90a0394a6b965171b3d2f0a509c1dd8622f8692985a74d7cdafa27d263249fe55178cf17a8c6ac8b1a5c887f459296a881496f1b39110615a3c85b57e9731c599ab0940c9927831f81dd227b95db6e3cb3266868597a7ea9e26ef1c9b569585cace5b56e6b70cb93bff159685a2c1a26b0c9461d5d16793e07de36f7e58631c7fb9292760d992fdfbb270bcd4e85204c212020d3a7664c607fc5ef319066559ddb772060d13f8dcc2089931eca8607ea6188a634592deb7b5c1802dd17fffb430f2739875a38098ce58d927d2895c8a4e2f8a75b2cb4e7ec9d112c1c99311e5b8e35951b6dce3799055f043aad692e3a3480e63694109c3e7884e1ac1037742edb798d2bc0f465ee33b8bc85d7b7ac6d17e98e66e11922049b3c5242f7d85a42794eb462c749771b8ed93ad245d7e737d25d00262d8544e775f0b5ef43ecfb773ed284b0f6006aa965bdef15df0e5f0977d8e4126f68dd65c5f11b12360f42878a71cfa25934e4e533758bc826a34883a288bfec0221ba6b74e9f7fb99c42e13e254b50a5bc3929f074267dc0327578df0ca109b7425a119dbfed6772eabe219375d71d66aa0557e026a79efdc7e9014b04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b8edeb2cc9462e09a679b2f3713bb54bf29d6387fcc64aa1c35d64ff70fc5c65","proof":"a294652d77a60444d6af557f5da98f70c48bfcde52e754d47a8c58e7234d892b0c4dacbe7fc571b718827942152ec718fe26826774ec194969ae5cbfe9383c02c6e118beb78b3f01585837aa4038a38a4f3f4eb5897890906e5596a2f36d1c3c00cb520b1bf455b278f650b5b3ce814579b0a990ad634d1bbeceee5c081e3158fe0d6c6a7141616b5848f4792525b0650c7644cb1c34c05d2d6a2abc93e60e000af951ff7f958d1d7f383f0f32fc131667f209ef8b7cf53d3ce41205fd4c1308280deee3827c0178075f8de540df04d698ceafeaa6e0eb9b6763a7919ad16f06f636a6e197a6deacd19744c8c0986a776b83b2fed8a394b51ef9fb7e45af4032682dc7021e3bfa97e5a476498680ebefefa5c814f67f0d4d0689c1e182ef6161ceded1f61f6b357dec24d74eb3794d44f51a364db5dd29353c197f0ddef9b366880595aa8c2aa89267b743d924d3354d14c3cec4dad4d592e57e29c01564fa0d568d744739b27980bba2a3448242262e6471c4710aa621ae47578a4647dd9d6228c64e80f79a93ff619c7bdc0c959cd5d970f29619bec23f47ef7f7dcc3b983d0a55abffd0e3f387f7718a960f7a9e49fefca7153d7153dff7508c3b3ab9f225169ed35f1243297de5f1581502e864b01d1e8b6183528b9a234d158f210c5c1556b84156fd5c2841b63294b513ab76239308619ec0ab38ab19c3eb17f77ff92748e646865876dd5e2940643fc4d3cc7a0c913e70d5341ba1abd39d7d2b65a24bf63cfce3d03847e597d9d46f4b8ffb68517a704bacf1441c3659cc2607be1451348215a974d34a650131f26253c7b34c55c94819ec7a05e14f1907aca8a848738d54d673c9a844c3f92730e7376c0f6f6fdca80b8f339c555243fd90dde8b80400ee3c5a6fca59c3e08c874ebb0c8402573f06712f555e27e6b66c996d504107"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"00a654c4b47ed3048c52dac2ee263425c8ac5f0f6f6cce7b42526a0d6b97c926","proof":"9e6eb7842ae59d2581f797227827c01e21fc27a0873eae52ca655eb4739f077ca2ea2242b32ad6c2792190ec3cc3b1ec377e063a6d93a74bb8f3a4cb18e343756079eb0d45e3d2e68f78f06ecaaab4a1cbe02a8398f2b4b98a30c5c2cb5c8405a442a256b0557fb2f64eddb19f94cc3a2fbbf192f411d875249633d614d9aa539266e1891b682a5be51859ebee30f854d93bf2e23e06738d508194f5ed6a610ec45b7bbca392ad5d73bc709f9913bbe3462ec18bce9c77512019a724171eba0232fffe9e7f6af46dbac059cb75f6c9356357345ff384267a92b7dc121dd5530df4e227ba2cc9ccf4b3b70fe13a984c5540ee6583dac1c68e3991e9673597c1772639c04b7e89a097d2f83335046ec5c63e18a8ee8df8f1ff1e45bf756e1139549e5c607196aea1c75aa58a3662fb829bd152a3ef4db346cb29aac2eed43aea3ec2342ac7dcbc3358c94fcc09e366be6a493a5dac2c4f054cef0620fca993781f6ca8f975436de2a6a692399efb7b4fec0e03ab75b96caa22a4e5a90cf671bf77100481376d1ce4768e480ff8be8bba3d53d9fad281e7a0586fa480eed5a2c376a44e9402d038e19223cc519ae4433978551e2f08b167ef778da4a9cde24476655ac78d2d1569d012b7c087ec7f4ed608575ccf1a06a5cb2a53fad6a76c69d440222d76af710298c61f6ec5b31edcb02d7cfe20f285a1ba68b68962b132f58c2e3c1b81b2987380a3922128a9958af326d3d8dca8666d282908cf15f890911e4c104ae2adbc1c91e68d62ddaf39545178ce804f533e977b52f92219258c6e02400ebf449e6f91d6b00e12cc970a466d2a1ed9458e91c57533159ce94b408e213030e71e66a50c7937be1bd3879ee2f9c7217a8c1eb00bbd8ec9b9b8fa9512130c6b717c3699b118c0d1bd4c592eb366c0fcb1e431f7c631e240fdc4dbb2ebf002"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e67e052c29779618e5600c6cc391dac3839b5f821b98a6d96a83af3ed19f997b","proof":"b2a2715884d68d1b96ba715c983c54e6822da66a2089becbd66b65e7dc656c72ccd9f92da7ec9b462f9267082ec416e1c3ce3670f734bead8fdef2fd261440127280deca3c7b262c1fc12c7b481cec01d235ca1dec03f720acc8013547543e1ca8c9d2a83caa457b3f0802b8d32e26d9277cff146ee26650976605e101c34a68566792b88c845a036f3b828cb4c77c72707b6106c7b7bb274a4bda8128bcbb0f94c592211f7782232ec10decd2816d34119d28de89e57d34855c400bbe8c1d0e6f61e5a1bb9bc0b71f17fcd200874866b28d57927aaeb82049a2c1dda6de1c0092981739b0efdb34eb4a4b47a1d1a7804f344beeb2c7dcc7add872096b2aad73b09fa8fb553aed7ff050902ceba64b6bfbb5100bd01488999a4f1bf5242dae1952851e83046b6c6caf968e6c49fbdbf2fe56af2eace1638eec47e968ad02144518558d6e7e6011a794818a57e975bfd784ba47ecc0a2bed659fb3bbe357652343c8ab2e41fd188e9cd89d9d56029eea35ed8ec669165ecac6cfa8346af2af9724005b3d1a3b8b99a3ca371d74021cee2f69fd4aab72bc470234cd4fc9a714771b6a116d76e2736fe46da62baa141f6c9182599b46ba9b427ac8d039bbc2a8d5f66a17c7b743ec574f3359e6048fa92f7a51c46786ece19aa8eaf3cd3caad0f2f52b84c8e9addd3ca72c94e8d25a504112dcbb6fa636b2f8ea3c721bb0e242320983cb6500e7a1824b7720f10bcfdf6901607d23618b71667a1d8023228c4895c0c8fb3813ff7fff5e8c91eb512e9b0bddc82a73e1ce34a28d1a27898ebd84856cafd5f6bb1445a29e9a58b79a9f6f158b87577fe138c58c5925b6cbf8a3f331045713d2d5edecb9cf05e427c1d073b36c3d9fdbf7cae418f19a06e3454f33b075f7174bcab52b221a3a7e0f1335c376aa87ff69c900331ef1ae99ed45917a40e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"103cad2c7838b62efea65da62eba423227eb65cd782c260ae47172226b87b378","proof":"926671ea254934bbb431a60b66cbbbcf6886b36c138e633880c4a107c385552e101247179249c37d2dfbd70f7fc22dc3947de1c1ddf58320f446f09f5ba0c136a8271e705035fe0b72918eca6d4e32580d6c9436287824833914d5f98940d01086c3fd56fc229a6a77892d8fc88c71dee10d5dfdbfcf3fd6d0fdd8d4427ba42cd6c50e0f555223140484406a2cbaf42da1c273d7c5c8cb34fd67d265ab07180bc680658e3dc267f835ba9ae78f82577002d73f399e591c26437d46405f6c32013604bd027cc0027812c9fc77b93442e432d021825fe8af80a90db28197853f0f9e6f022b1e48375e9f0e1025d8cfb89c2cbaaf2bf741c201934064090f9a870ba2e1ab399cdc0c72b96ec201aff306981fcb8e2e9b78e81050496efc5695b475c0bf252ac215a5ed0bcf89644dcaa0f42056595a9ab22fd3fe98177bef0ad03d3c606747a978be69ce935fc8c18b68e8d41e714b405e3a569c995e152734ea022a685f7b9276a03c36750e698760997eea99057c4aa6de4616d1491d36b8ba777e1b7a50e6c1942dac84d2febfb0b0c90f967696556e30702555789b369c3668e8d607e0e74ec2115aa03a0152c2069f3112782ad421b49462785256f8b8dc79aaf99fe916f37b09da192853d1b370b183bad2268c98edcd270a0c1bf81c3500e285837cfe6fa82b8a21da2144a6db479073bec53b86ba468952aa7913174d42b05b5cc6e39061e704806f297621d629382f74fb3ff893805df33e573be92d60f41f6aab4c09c3ab51a3bbec1aab878c99a8b60b6f65cb1309cfd5000c7bb9694ae0fb9f5cc16786a6906d9f7f32e505e4eba1a31c822b8f9cda077a0715472fb3aa4904ca506ac2bdbe154a947cd829feb4be879998bdba8de854a66b515609bf6b48ebb9b1da04d5622dfa45b7907fbf080b78a1e78604545fb80bcd73400e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f49df8547ade37dfc99083cef83f92cb77e1ac01a6360db089b228fe89834a2e","proof":"ae6416a38e0a3948be26dbb8e072d82236117a0611bc44f175fdc2d5d0b118659ca6f625278528f5187d7021cdcd6354c1034dfd93959bb2c24f3f69ba94e87218428cc460fe348d712c2575a386251975398092c04f14033488f68a9bf4b277a635355a1dfeae0f28bdfd37aa8135ff596c603ad92afb96414f12ac74de414ec0173778270c1f960d78e23f7fc5cc0f5611fe02a4e382388dec0648918b920013a833a292b49ef84937aa5f399764eff8032472a751b254c34524241c81c502c3629a6ab775cbdf4520668d6837360bbe707e4a35a9ac16a929f407a5491b0a4694d3b8077e881f34ec483023d8f236670b9567e9d1e28ab2a84dcef2f2e4681a142d2d016e112002d9d7e501a11f5b1661b8f7f4b5473b8798fcec71e8674d22458c0568b6036904da83607fe7b0f8fc211c025052153ec5e795f193bf2f6fc86e1e05926f742a9666f289913ca6fd242a4bd8f1dd3cea38b461073833253d380231d82c5ac60630275f1cbe15a81a6fb426d5a27025cf8c378f28ca7a0c4d5a0e8439ad871c49b589623d4ed0596d980cb5323a13893e1e559e635c293364cef99fbe31b441c298ed5da79af6b869447b93a89ec7f0b16fd410904d7e2b485833aedd3841a13ad5d7f30337bfa165076b48a0b068fd0b1f6f67626a3b1f36f482a015720cefdc5c0166035cb465a5f21bed58f19a1647e2f2ae365d3f1d4f60a581b69c3725a5d445d5de5551734ffd37d527144e991d37b51576ba78340f227cbb87c717a55647924afa51638daa7be0fc1bdf4da14fa370e1ab4b170937eaff056d780511d6f846bb824072bd94ef457c8f7e85141528d3884280051c51fa11929e872b455f015c9d4c2a96ee04872667fc6e8d736147862402bb06080a10711a7bccd3c77226633025f13e13b2fb127dab8ce5598490bb4b96304c8406"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a292219eaaf1f3b522d74d19891e25e6d89500d281f155e3eb7fd849d20de96d","proof":"fc89a05fd86f6f9b49a4a95696bdc5c0c59cc5c1a557c822bf9b1e52d437a752d8140be8ba46b33872a460b4ac47b027fe581fad868488b2e12dc4691d6b535d286215505b9424d8646fa3614a32d1cfa00a9e3d7f61d7218d9d4b8a0cd3ba1fd6c766a320b613c4c3d93ada06022539acd2cd505233ab3c9c2a0d12958d47256fe77527cfe96c65b17aea8d4f8ff73844981b074b2491327a64b300f3a222031b2beeda43729b7e4a126273095871a4bc33450ae1c00fb0b5d0ad278ba7250f8c74e79f04accfe5311854ed1ab784d1fd932c50b210ad4836422867fbb4e10868b7fc9fe99fb12afb12ea55062969535bec1f9daada91e8b54a68c46c5f2522483bef3a96ea485af06e54374de106254d70f3388690b2d2a5d193eb437ae025e40a340af7e3fbede28d39cc4d584e9ea03755236506a9b5115434ee95ae9b51b0da132b38aa9194d350994b5ad86e0bd7a43f01ea4e6ae3b90d8da4430c0d53dafd940fc6032f5c066ae41d41089eadfc222bc68615e1a544fcbd31c71dd6794eee05cd0e879878f2a5ab998ea17d810a38c3d4e81a6cca720ec4f2af934074da2330fa5e454860d5e9cb1c6af5d01ccee40ee9ae58c2e78f02b34b7b449b7fb492db27fc364c38be0e349f9c6dc32e88f357275ba1be94266675f9bb20c278702729bd25610b58b67bc828763196c765c62f133d618da943f9f914e048c158fe45dd436cc35f3a9c0eba466f9752fbea9b9308c85799acdfda1f348d081b1db478593d9cc48c4f3c00a51afbd6c2cc3a3dc6530c3792e90fb8c0715f490c3fb69db1e8157338983afa62314209e446df8b93593593bd3e7ac3535a07b2501d95ea832ad9b31bee62ff324ef89de48f714181515cd2e81a4ea4ee8b36059e01538f97796cbbe975072575764dc4d6dfe617691b5edee9921831b5d05d22680a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6660af1abc32a8d19cb1458bbbdced08408af2a5e7fabe5358ab212ff3f9c406","proof":"50a81cf2a3e7d86fafae0d06cf3dafa730da7a1b52c9d00f90b2b8e28ba4602e78bc90161dcf5b1da3068ef6e9a626b567e6faad78d9bae1d4718f990bc3d752a650a386f863739ee3594a26aad733abaf062c56e516527b72ac0d540f85184ac0c8c9db487bcdbd0e56f0bb013c94da005b1e7d4021d69ab26ac48baf434e544390bc6bb095b34c57c220c57c6ba7e5b8351a837387ccaecdf9cd225643390d8019882f6c82a23aa46210a2788f6d3892791a85f8d5ebc364cc118f1d7bf8026e7239620f45720560b6d9e4ee2577656c4f6e6570e777595151bda69c55f50ee0c5ee4b9401fde54aa533c8699c3d7e237cab6026fa35f7776540ab232a0524227aa37aaa4ac45d448e764d0fe4a327cd066076cfa70cbd2ff5b8412f1331193c4f8e3ec78f3dc4bc2c8a8ab3212506f03df007ccf35c56f97056c95aea7a060e8ac2265f6a81a6932585328bbc737e528c0e0e9ecd065ac3c50ed2306c146b9e51fbf96377c7e32d808dd40982292ef3e1e296cb4996a6b52b57df91f330333cdacc9ba09b906a42e6c3429b736b6d832f4fae61cee60e74f295da3398d4041a6613c7d185056864c1dae6d0d40acbbf68e7d7f755243f6f26e997994e743e5ced1197215badda08fca6c3e2e6262a672263d492aac60e52f6e419b59d58013a5243dec5bce232d33b6360ccb7608484e345a5d2f84f0109cbb3986400750f9c3ecb6d04830ba8d62cd47a2a15c30a6a43779ad2230a50c05bff33383390371e80bc87bd857d2884a267108ef48b0a7f03c12cb399f8dec5beb9b829967301f0cea714e8c039cf43dc1b816d3000c35f03ff557131de64ea2534373dcac851886cc2d4f96b254b8c70972e8f86be8f3ecb3f5c18f7377035f859fac64e9f0c74e1b43b9b0eb178c28a1d0a0a3c66d0a5595c562b61d1140716d9a6e234d70b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"74017821d10bea9038f7d88baa45ca5f9a965bc2eee1de38abfccaddafd5e949","proof":"7a2d187ceca9769897c4d570b54231426febf2ed8fb844598bfb03c9ca691d467cdfc9eead824006e9f9da23a25f7a04bb2610739e1737a8e127bd0cd9614259b688007457ec654fd06d7774abc7be4f28a59cb7b1f2480919f227ae0cd99b3e6a9c59def5d633c916ae09c69abb74616ba707dab73f8e689a3e09a821352162559e480992f9e8c145a17904a3591c9636ec283739d6afdd1b1bca002cc8970d0e7e3f08b8c6092536662ca5b7fb46c5dc489ecc9fbd36c118a909ad20759b0801dc56ec3b364e52de5940767d580eb470b7249190a9328404a5c1629f7c6401749b5f175a6c07b82fec5fe2fc9c0a1ee6f7b395eed5cc1c9b70075516d6b96b644c36f2eca6398a8f3f922639efbb54cc8f8abd658ad20880cdb6611d8c441d40f30e6ee7e42d5cd8a83175daad57db06e3a200a1376308e2584820e008fd1b52b7c31448a78ac5ca476883e5f71481a517185aa3350419a0f24f1c6d3b5145f86e1d8cbdc6fcf554d166bba57c84c3ddcf73cfb41fe878a90e7cfed6d038115a905ab8c0c0871bdfa0853a1a061e99c36c8e4df72ce0945874b8e3eb9f2a195444b799303b511956cad5d8c07034e55d2d7fbeeacee13ab899c4cff0cbed424ef8e8c47a3cf84b233bfc28c568981e0f08c1a23ab8bdb52183ce364a70454a76c407208bb3e76381536c1c2b721e8954ae704bac98f5feb6f12b9f257a7406cc7126c0c8fc0b4d35667a8ab39321547ad1838ff2215cd8013216de5b94bd0c68ed022294d55557a98f4c1fe54b7f7c8f472b321a6f48a6af52491daea2576508478ba2f653547a9f699a68fec87e18322f239c6a287ca270daed92ee6afc519c4e32a0b9f00a6907e42c3fa8b5ebb0de98323dc0d9d74f49ba0ab9c86ca50d03cac2c6898e7cac8d4a62018ac93404fb355f8f535e20766b3a3dd29a99d00f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6e8475feaa7647e9b63308d2c2fd16a7d28b047aad581c5a814d90a0b5619515","proof":"42f8b696038525623a780c816a63358a6e3f06e90de1859c7075f75bc53b8713d87a99431749ec1913ace458154d3b1b24136e9806f204cee679427f2e19ac5860afc83f1bacf71f2bb34d6ccb69b5047a6da8ecf8c27889e894b2f4d170f541c00b3843f878943ba613e87fd46051f2f2b5c020ea94101f6df8aea38fea117aa39fa904c0bc52ad8bf91360ad05fcdc451a0db74b23579548d26c0e25deba06c0dd792f82f7df9a6246f098f4b815a1b6dfc01c810dfe425a6c4a70d2ec190a18efda4a1eccc8e4da703335a76e6a7aad02117d96d8f5b571c614a60e54e00200272d64fd3cac71c1cf27bd35476ea1d804b1528c0f81e8a466b343dd111664a40fb1f06b69e7cc9c85b896910e3448fc68b6c343988929b0cb9ae4fcde863dac34c62195bf2093871c5bbf480c645163403e6de42b94c03ddf5cf6cb93864cbe8116cbf988f536cb0de47797d3bc95a22a77a4ac4802d5795a1835132a4f773a2ea6aca08147fb1220f397fb27ae5bb4ba5b5ea31af651ba1a726742d84124ce2ec48e8789d1163f4adf43776a0971bd92607723bc1c13fca8296c594235227ce84349a9aaa606b99bc9c80298ae092efb50aeba7745abfb59a04642532b700298c0cc1164edfc112b30b06df52ea7190cd28983e208b111cca8d59cf2ef327463d215b296c24f08f78dcde004cb79bb4eb9f6f32bbba3c0ddd16cc931b920a6d83bd7f982485a8769c73a4289cc5ac590201622fc13e5dfa071436c4db220a02c2cec8de082cbc5904f2e0e0fa7a1381641c0fce0479891316af627554e635e3f6242da2760655ae32b7f9e82f1eba0ae31a765ec9afbbfeb8a48fef184696a3e0aae9693fecdb30abd2c13247c4c2ab2acc79fc59313ea3b68d43a129b07e55d3efec11bf9077d44b797f9786cb0542a863df6fa4d1973749cb0ebee6004"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0a3d68225c52cc329ad88fdce569bfdd07f9642519a1bea0cc5f20f49c257729","proof":"6a7f6ef710113a394f1c35098b25d21692dac08f8874f2b82b115143d1376f4fc65daca59ecb1f71b5218cc4b8575394ca018c82ef626a2c91544f3e7f524e0264c87a8525156330b94e7ef1c04ab13588f8255a578faf3cabcc594e7b81f05be071259d170387a35be85862d5d0de40b8a006b42a38f7f52a902e6197a2b925addce51983bb9be837ed23f4f56c1a7bebeaaa5825e340fff35adef7677aab06a97c8bc25eee94d30eafb6719bb1d69fb6d77c7578f40fb7cdd439afa241a10278c16999686a55f8190df541c01d366d1233525c6a8b25ce30c6555ea5bd580f60cff8e975b47e5bed5080dfaf303b463653b7f01fb38df722233f11e0c940194e284888906028a81521204ec364504b995bb7099ce892d5f2b73c02898901401a10cc54960d1eabe1c14a79092c120025dcaa265ab88ed9b3c8a023d76b990e3a8eed504a2f6d1eff3f3be4897b7c4e3ebf0618de10ae80ff0acd1e075ad82708a22962b43f236ed4af2c803a3f885d5f8331ea6b8fdd1c88242fa7714e3e078a9377343cccad2b9e0f1592fd54f8a93794c92835bd00e0fde654578ce95e38a255afd1703d4739f134941a64f9e637dc28bd5f1fddd91d8b70c0fec69d647a4695ad92b05b2aba98ae8766198bc2951b973ba5ae4d7abd754a37aef9e46e0d82bd355efe232c13ba0a9c7e1709a672c0ab047fa6a89afe679dbf773cc7482c00db3dd281e9d5da83a6b5b8e9dc5be1c7de58155746403efb67f0a876cb372070c6f34f6435839fc5ecebf5bdd7e9db207fdf68ea79552a5eac524db049880062cbdf529f0ff8d1ca85bba222ce5a42c9f1c618d223e0dbc948dddd8c4dad5211a0180a27fb9c4adfd902c260697bf974b9b1fc8c3f0d7e571f7968b3a04009c9d6116107c2aa0e4f646bc3ce837f23139aea6af3da57de04bac89c68ad8806"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"14e423a02d55cab624ff9706b63be0a8f9f282514f093cada835897471be5d3d","proof":"5885a36010628a64bee2933c06cb4a186b94d8c3d56bdffb1eb30a31a6add86eb89e106e6fb1a72bab1af2cb4d00f1272dbdaa381a5e5e7262d27a2b733cff0710f701744553911579d611ca04399fcc943e321726e7e90f02c3a2804ebc7f46fcbe564f225f944d13772bf40b65473eadb820dcb5bab78f6102571551d7232f23b9f342935b3c47d9e16c427cb05e2d8534977ad908bb528ae84d695535870567937df88e3df9d78b8526db685ca8288d3f6785be6a65d29bcb0f18417a950f6db7ec529f38ced71b9a95e8bc2b5837d7947652816cab4cec19992a63fb1902caf2d34b0d901ea7e1d5411663b6dfcae814e93284beec1e26926e2fc96fba60dc11f08e93a8e366a54a246557e8635e90a323af7877406b54d208b58ac934494a8cd07cb2e161a6f25abf60f54f7b4a0656b7d4c0e6ddaa52ec8ae4bec8e518b8a8eec56aabef4f4fb6b2df516687a2dadff75db458566f55f21d147dfdfd22d69c00db49f4f44df3d0d3a36e5aa06cb6bde16a30e417f9fcb20da0535535326cc0daf1de97da6b02e639dc46d9e9033025e686399a0620882270c4c87a3359deee6db739d0d37bb5b1523e52c5158554a1bad4459ae001942f05a27ff234450cff5c78225951e939de616a05e6278aaf3050a01aa638c813a1c4d812bf05227c592a7484191c2455458add1e880a5b1c092f3f6cdbf3421bac640394ec710778c9dc421999061a5ad299d2df94e3f2e17213b98a2a27b121c393e58ad8233e7028cec3c9424324d047a7d069950e35689a48e0e632a363ec52ada89d61811f9a8e4633378eedea800f41bc0824e6075a62da31686b3a7366e50b8953f4a8305759311faabd3e7eb0d233cb68a56c87dae7ecee687814a359e723b5bdd7d006aae0e61a16887a758ac98b5579d4825f1c24eae98a7fc81d7df514e15c36900e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9eeb8c59f2973b76210bd068d9e1287766f68f61ff322e161870b6103aabbc63","proof":"a8b11869aed6a2da715c532690ae5dbc5965b74bb6fa91981ba903041c330f70f433524dd639a47b4f78fcde74948710576a7683b4e5c9c36bd7dcae757ed85fbab4c42bd0b00d9c3ee47624daece7fe7f23fb7746186827a63fadd7195ae6718ae52e30cfa3b4ee63d056e99c860fb1e82f04151ebd0945ad473cde04aebf33356437629912504c6ea16948c1553136387d21502c2581d9098f107bbbc2cd0b1b6433eac70ce68005f17bd69b0ef69d51d21f424c807a8e1fd55e06fad75505b954189a60cceaca917e88687369330859b73403caa3235b985a391f4f2e5903c64fc9439a9283eccd12cc8d7eab74ea352bdf93dcdf7dc2a9182b2f64376e55aeeba5f8b9822f0bd825d35d7218cb27af57a6e6ab2f454831624a973a904078740fdf9000e99b357c6df44e03d7d4cfafe0ada6ced62529abcf1866b788fd47982be375832ff27042de1c11d7f3b64b267adcf9a5f4176c505b62d48fbba1002ee575b7f91d6f2e3e00f99143757620420d1e5b4c53decd88283472d39798579c1be0c299b26a44660707d88d62f2f8deaad26827b25e9baf2a9cc3cc573a4c10e83908dbc3a49b2fac38b5ae9580dfe4fb5adf84b72aed892e0177be15ab5a761ed6ae90ceb525eaae12a5a121051768b165bc5bc0ddb06d23aec22607816a3efb6471c66571d16efbcc9732a2d6b7be84033fb56e7240d207cb4f6924ca6388a3e06260262d334d36bb189ed49a8eb3427d5bfde28d037207362cb5aa934c66ad4be60a75b21f2bfe51d0cf5e730d42d9ce22afaaa10738e184e1fc908a46d2fe5a69bc951f12f9bc16376e0c26b6c301f4f6dee7ce376ae368dbbc8ae23e99bbf04d7396f5346ec6a6a6337e8a8bb27ba70469bed26ff2515620e262d607ee8f205cc79a236fd95dab7c6a7821cf6ed72989712dfdd00992020d05897706"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"04426e305d1b1dc3b2550c25d3aa032a3aeb5dad00642f80d6cafea46c7b2257","proof":"9e0edd5e333ddbf8f6e0e1f0a86a4169df6df51f8b3828a534d5d5cffc0559020ecd784975822a59a1703eb31b389f202a502b08ba89c82294d089096ce1991d900df1c8e665252ce11174a51c6e66ebf2edb9eb4b18a3e6e9c809f5ac8f14493ce45eca8a8751c0797c5e68eab13c09101031282a3074f8d1372a57260408149121e7b923eed3aabf323c2c928c3c0c0f2ac1f749ff5c9819fa7095cddcea0eead31da97bdee398cd9e8dd0cf179ccfb376f8cd70329ce6b4acac2d366fff0995c6b14aae3c6e0a861afef10b0ac52cdc66bb16111f758fcbefd58253784c0f7e2eda39bb9bb219b7d24fc7a906175c78809d66eef11e6317891531a6ed4b2bb457989e77e27834e6c12108177695604949c25b1bf32bb01766c5049a0ea948f639d407aa9262ff8ca807842b0b5bce512db7d8fbd44c54f2b97230d42dc250609547b6c108944cd929f461ca10b492e97b2da9a03348aa86e42806fd75184b249d59cefa13dd8da8e2b47c6cf495c2ba377501080a5f0836c2409f95b56c4492452356de5b37f49fbf2386b50eaa664d46af352e198c00856fae0e3ad4f258c667458e65409f0ad4be6b05a23d430035b2ca71c06783e4184fe3c590b728571ac5b94aadcf96e3aa1c9fdc4a95b9d6e55cdb51aa823abe26a0409ca7548e542e2ef9699660ac60db04da6e7fda5ee57276fbfdb16758b5460fd1661cbb441e3ebd9edd3def6edb8c4f700d1c505bcb4871e5fd03b87998a95f5f809fb57d4cd6caf1ae755c9c7133b2628977e15d483ada14db245a70910db819af1f8c3438a40d8f169dae751d747c3a862269d42304da460146e82015106690137cb5db2526f284c9e04345a276f2b6c4465fa3b9fab0f28c19ed87ecf3477fa678969605cdf6a620518bfc8f9c63ad830e412d4f84181a55848c017ecbbca2dae6c65502"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8686bd5bd10e98b1e9cf1749fbbcb99ce61f62f87f9aabe33cccedbb3a2d1f11","proof":"2ed7c2b8126e85597e4b97dc161dcf67896e9de0c0478ba42c9fc7fbc60af063e4106bccafd29335ea3d03c4d9b0b1f37771998ca6dbf666f18b610d0755f958f85289d3f3a93345468cbb4a90ce3ba6fb8648583a032067a6cc26b101048c752caa75024c8c06a837be7d8d658c03fe0df1a61cd6716cf8dd7214acfcbfb83eafdd24b0190bf20d4cc829b60c0139e7dddaed344f8e4f4270eb0e1f43e51905d2f9ea86776a44a6007c85d4e4d63e701f14718a9a1d5a8d48ad96f5cb3e0b02d05f7f6f7f400d911e517095d4f98b71ea97091a1d67f74f8d3ce69696a2aa013e8831dcd392c1866c04c0f2c23c0525867eec4e2806d988e5c4182abfb62b4290d4df05f85d9268fae8ef9f3aa49533a531ef21dbf9602e9e48cf748ad8285108681efeb9da84513cd45d24c0414f1b54847df5024603daec6863764da5735d88299180088362fcf619b93ef8df317cd768e8d5aec6219a9c21b74fe5405c692a79b4d1bc740aa1e9a306cf7b05a2f7ba931ab1a6166782e12d26feb6c54668447a776b122a09a2686de33a657fc0c11265df3028246cc88a559b96d6431e6d64b330da74ec6de512410769db8aed31e3b3f57da536d7b4dd43f9b6b5e24755f28b0b720c672df53c2735179bb6b09364108065e6d1cbd34cf50ce46d805b1f20cd96cae95995ce4ea191041949c9ac32379272a47ec5a8735e44917b89e8495c03dce10c76defb5a2d2fb667be963ffcafddb22261362b83bad68150c90362d46a448cc4d84bdae2cec0974b4277ac81ce4ce7fbd9c7ce574f7cee57abb64d703ab131e4d91aacf1620fc60e198b44fe14e447fa6e1b0b384260326910c9015afd478d3abd7dfbf26e76ef7e674ac7e934259ce74986666d9f418c235a5e03292b6f0b2502544810573ba48c20cffea5235461f34424d7de1ce00de6c7e502"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"767c2017526f919d72e09d9e2c7188cf00682c963f67a1617fa4049eca58173e","proof":"ea1c6af17fb71f2a6fbaae8dc21bb6f7aa20fd699e8f7ad1bb4ea2c16816de0fa65c827fa6e97d44fe4b6c02cb1d801630d3d22daed9701c626f23228588912228ce1a2601c73652ae1648396fc30fd290681c530d58de30cacddd614505751bb44dab807862db4e839aaead1519d6efd12b9f507bb2b30c5bbdcb0c3b0ccd7f68e8f4e180549b49af1dc181f6a9e7729f2fe02072f7d1d6bba8c0b44d782b031cc4f9a245d25aee528dc9cf2a81fe686014b270584c6c9c45ecb538f77b6d0bbf9ea3d40d6afe1eeb7999139a806e48d4c689289f68ce770df59d7f5a6df709a03df06c6ba347c371ae8617dd11a0c2a3d3727b521f6721c0eb170d38b8581b8cc79b920728ef97ee5fbafa3afa2b924606193788d25b4faf34c7ce13a0ca0974d2bd39ee30dd85803449cfaf60fdde05c1f29e6113c03b5b7bc1e2cc296974f4a284547f1b0525d97424ac1c84f98ce5bd69212531a63819645e5242408e5ba2badbf3eaee0070decb784df4ed8339ddb0f31de5c00ca873392f5d0b80fe51c6b27c6451f246f2c765c60d51e1a3d1fcf8e5f791dea9b23701349946b80360a8e64e92fab4f35f4b66ed1cc1d6890ccf5b18a9f9b2e5805a0ba28b91f0347da492161d5c60f8a7ae6071e7d28d1efd0eb8fc732fe539cc925e765ba11f6e2016bc5e5d016120b21bad39e2c164a4d0a9eaabe1bd56fc1c25db4da2ab39382628d5bb2fe54221dbce42ee47284c044fe047d2a2a30b457908353d1217aad439280c346a7b14f20f2c44b36e9b12f158e88c0b657ed7d1ef29dfdf9c09c08f65dc894892b5513ddc5920cf437f74a3b5bcd3efff393310adb71e9172d63eba690e83a2eaf495fe1d54d881634836f10c1ec1f0d31fae7b9835011dd7c1b9cc04e894611259a8cd4ebdf840224ff7e063c0cd3dd6482e0b5fdd96c4e15bb92a0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"461d1f4d45358be0e39ffd708e26d2c69bd601f842adb9f1fdf2305d37a8c359","proof":"e03529c91d77f6fa0b00ffee100b765f6cfce4f315eb85a86af0dccfac326720a6832acd6b3c52db7955557c7d83e61face240e8b4f44813eea17ac9c0cb802c52c5e5db89b19180d8410cae50a94712665592192a97173bc24c25af94f1810a72f9ebe49d0809ebebd4fee6beaa25ad8f7ea8b93783f655295bb1c8f2a6170854e1268eadaed23226676ec26baa6caeb94db4ac0a6d1f4f665e74ea3e6fa8025a5b7bb6185173fb5fe851857fce7b4ba96f1acb68efbfc5724282be97d400079e894cc514cc78aa7b18517b3a2c1303aa0f1eb6050cd7d8486910b12201190eb0b8265d7852cd523bbc2bb9a088e985c74ae2f2de02d9af75d7d0d3f6eee36ba4e97c59ef63400ed25337cec7e757cb3c7772694f0c92b5d79d1a06f484b323b82f84b68963c4bcbe23e44aed9de5eba7e49986c8786aced399ce8cf65e91205098d61835af4514a373bca40a9c70e1c0d8de5fcefbc4067789cc965b88ed47c6260246fb598057ee71855d60fe30d340a29e2219b95782dea908134e7ffa1122086f9ce9f749d2433558b3842ad7fd2e0c95f6b5c880c0407f3e16583f2472b819963f823552b02f6aeae0f7391c93fa0fdcff2ccbe3169f1380e47577273e423216b8ec00df12a096a5638d50aa878c4e70e5d6e23c281c6459153c8a802d32aa63f1cf0dbcbeda51442e472e3e1efaa8b30d5e7b47515ee576b380db400438ab9f3cf0f4d29103ce0a954479b08ce2a7a99180d9d69c157c90b288446023c45422e31e5bf801c89f9f9f9732de6af48dffbfb340500bf94a1213df8960664c8e1b52d1cb1613a6f4608064f1fbfd7344983f9558ddb7b0aeeaaedd55606d554b1c769b430fa00a3618b35c1916d4776ab54b13f088dcaf99e426462a930f7a5967b1ab233a3a51b36ce362cf7702992fc7d2778d23885faff408eb15f80d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5ce76cae6d0ac5ecc365a19081b939d169e68c52ad1c3074c5c03b075d269137","proof":"8a1b2c1bb4e3d39b9c9dece75da79bdff20dd063ca81244fabdcef0444feda5ac8a7006a426eab14b74e4c8a162412ef3dc94f796185e45191d8ee7de28bf35e44b541038fc410333ed90e67bf0788d0770664a5036790e6e5805b8701966c08eaf15887bda9d0f351a50305080abca2e8d91d0000d52ac939478129b6fc0d4b22b2978eeb2018f3a704ef3dad7205d2098de67f9f1f332f2d5306a8011e4d0f672b07a1efb3e561523366bf207b37a6858c383991b9c92c17341005c0cd410451b93ce729a8efda04996db54cd62553d0fd1843cad176ac145151375888f0088c64a2ca4736a44bdfa1353bca8a08ef0b4ed798cf299f38e6041a016946704696493b053ca8f5ab0f90d1c6b0cd62cf3bbafdb9b9301dadc35a50371d053625c0686de6b431e62db4f8078a93b2154243961f9323ec5f6ca69ee950c081867f507dd038918f911c6532ec0928741ef8fdca35a85ac24b541ae7c13480f9af55aec763a1351d8d2305a23ff3c4062628932fa3ae4faa3359c90d5c9f7ddeb83ed603bc2c9e143e29dff5693a9b241b722209677408cc88e7cf5adfb4c1f06f5a9ee4dd8e46ecb1c360f0ca7a7039e7d57122f7cca17a36b93403e30e7f23ab697ae877b87793f31b0b9636e437e158483b17ea0c31ff8ff5e01992ac69741f04baa9db767d204672fe67a82c09efc215e40cfa90cada9262373b3115d6d94466b01a16839da230abcd4d5b8f73f1bc233219e822e6566f3e0dd32a5e71966a2f4a19ccd62e0281ccffb72e8feaef6cd70b3faf10ac76cdaa71a45dcfd095cf015c7479e1fd6a0f2412becff232e92429e7b02a6ca88dccdcddb7bada8a18254f7eb61d301d5ad70be8ef40a2d8e4a710a370c7c1f78458b388772d1a9d128a047f582ca8e74f287f073f55cd14b67e232f03f9c571e8c7b834be7a46b575440c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"482ba7b5d839e28c4f0011c71429ee253173cf96dd704f69b2ac3e5d97140b47","proof":"1ca2ac9073c77e0c489f7ba2daec7cad9110004d5daa18a777f834c1377eec2f1609237af2497a668134e0a23801a83c55d2e2078ae41ab6d674ce0f0080fa0b7ec8e3ff2795600f0abdfcdf5cd4859cc1848f19352251748738b3c42bdf5e4646cd113eb12dfaf7b209471abec99430167c5fef2f1a016d71826ebd23ff370d0ab9e3003d7bf56a13b9db8e9e5d5d23f9b14b5cfb6c1edaa299c1f5cbfbbe0483494c6bb8824f832f023f3faf186c7130148b982eda796c51670cb3e823cf0f231f224d8a1349e81eb2821c463e6044e43a7749aea63900a45283ce82fd780958dde7fba279c5da2660ca12a64f3f9059760467e798f22fd81047605834b36d9ade41ba2178d128fa1de71062fc4185b7c609be868e97159b3e2dad1e2add3cd89e328fd783e2a6710a8f34c349f0269de2dd3988d2284a3af1d9d0e4b349650aea2c00e83c9638442ad63bde1b8c833f96a112d6bc1fd0525909b1f7f3ef1ed423b53e7c9849abc84be3b4144b6fb86fdbc2a56886013d294e19bb4222d8374ade905088dd97b7bb555e2a14059e4f39c20ad211af1a9ffb45aec3b5865459de2dc3770bb240223127a84d0ef0c3387f990b51ab8eac3bd285953016010b4d604ffaa9b4ed51beb303bee6fec8573a8f9215269e5456f55cf273c7880e28561e695670fc591d62db57c694306d93126b400340eadfa6d3be47d576cb21e9066a67541081f48d7ee3c1c95e43be1f0ada45631c8b071d58ca903c3c862f90577ecddc9e159c2fac09955d1aae1762741c570648107edaf9dccaaa2b209832645018b5e2b5bec488b12bb223eecab3b8e974324b8482e1cd5e06cb9c7d525c2ba0aabad8201dffa4a5d2491b6100af17a604a7fa4ef68b61701e626d5a65f40d44bdacb541cd796f647fe9ddea1279d3d7d4e1055ea0a91480d9055cecd94b04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e6daa294349eec8e6d68dabe0dbd069db3f4d313979bc255cd433480eaef8219","proof":"de2cef7f9d71bff41a5794f8b1a67488e232a8bccbcf469d9203cfeb97c8724b268130f5032770b0673ca118e657fea97a12de9ce9c3cedd14b3fe943d222a0746894dacf5a652339d1a1d5d5739c219693148fc0fc1de4aafd1116a9c5050312e4bd257563d991987ddb20b1f923a03a2a65648afe296c40cc85d5f23f9d7312e94dd7e8578e197fd021a7921737730cb2f0ab3b3d3ef573131a16921ca04067b12ed1552050d7d2258480e20df2c5b768f7ba6b5811544b271db3ead818004645e0192a47ef4a9667cd82d9065fa268afddfc81d4ee532b1c1b3f9434e6205941d77653a76f64aca1dd3d2d37a5bfd0548a298ee7d6874ac0f085cd98c817658398b8073a1448a562289815e784ed3be84c7579b3a6d4dd19b32560beff5271c4b435a59e3584158ac601de5cbb7e027d70a13960e6b43e60dbda14afa3f3fae64e67f04f03435897460283517e816e78d45477bc6dbc3aca4844dda0e36168c2e4a7d0bbcd44875fab57fb5afc416c06bd1ae502c9e791619db49179f5222c443d3cb95f512690e469e51d71ff374609cc5a06d9b028f911e1b07d500a77562a9f00361160f801fc436b261d99082aca28bd08eae674a7531cdbb94588535346c62c1cf1819afd55cbe3d088cecf7a297e6085e0df1dca69c0b0d95c5da158c6fdabc7fe70cb5ae75d0eb48f3d949289daa9070e6c84fb49e1bd15bd651475c9fb742093ded1395d060f87ada85deb241b9acabb053ee3431296f4c55c7452688d76337ce80db95861bdcbdc0e60b4da40709f88ebb83ef29a4843cc42e7f145bc6c5d1f5d831a48f4733b5e877c5c7b219dbdd96becf1e3fd451c10987233f491f66ca0fed0f3db42a356b8c6e3e0ab2fc3fd042cbca003f00e3dcdcbf0934497a548412cc9611ffcf1ca6df99678c351de711c782a8ce4f7f0875468d09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"44b9e8b903f7dababe35c981e3c90f9c8cea5c95fa314aa1b1b39e486337e62f","proof":"72054a47d611258993d0655d7fabec7c3ba51537944033f91fdfe8e95eccb23e3e1c9febc6b0cad2a579f261893249a997b9bd912bd5a5504cfbcf1c5b77954a10dfbd98256da874620b3a223b06fc4d224c4346444c165d232023a46ad87970a40251c188d53907ffdb727c72d9a35133eaf404609b18b26108e4437bec833223639bbd7f3f7e4fe41034ff226ae664c8f5fbfec0db1bfea6ef324169317a093a0d68fe6c1a5eb1673ecaaa29f9704a1d9007d3d8c3b01432427430f425180568a1f8d35c9716ff16917e05b9f2e2bdcbadb9c2d7024d86ce8bf18150e1c701262cdc0e9426796e384914de24bc54fb48232a60cd03c404c88b6794f8c1a454c60aaa0733927d40048427d0d6ea2165be00fff7a487f20120b4036afefe970cee4b906c491778ba661a66abf078a556265c3190a44d91e4b678b853a904217f102568935ee0b2cf7448fa8d5f67ad1d2b814313bf9f1866cf204fe0219eba158c45488b7a3fe1a899306280c04d77e2ee6f56e54fa612d95c87fb0d2887de2366ad37f9094cbff0915de4d5067f04df9e0e931a2d1caa9482b3cb9af25f9e603e179bbe7121ac94925b13ef51fd3b00f81474766abd03f3114ccf06d219110ec6e2ac30c4c8190d215022bd8659d92c03c0bed066d827f8155211a9945f1f2792ecb649f5e5850cc1d8e4b4c7d05b8c9fa316f3f6800ad08ae8398f5546654bbaf1480b8a8081dfc1d5df8f2d66ecce11a1d450619252fdb2fbdc484467d0013ec8f5471ad850f06cc82495f886cca163c414614d4411287965cebd8b1caf565e0ebf40606136f827d09ae49ee4b298f0eb2e3362fd56582d0b7e0493de9625c355cbb4fa87118939ac76c6a6946edb608eeb4cd4fff31e72ee860e6841a60696d30df7dcc2274af72d0cda728392b5567c7ad1408006ef42fa58e5e9004d02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4ea48ac84113f7733a37e1725492ba6d3f18dce70212b750d2c7a8bfd30c3b09","proof":"283013c115dc3af2aedbb425eef5a27bcf6a34426cbf341bb17d00ec1cc86c67e0a666b9f7241b07b66f6f7dcc04f4e1b4f0f6de0b32a52a2a876ea9260e7f35685a47bc7d3dd94a8ffcd5aa33c2f087d1c3d50733ba8ed445501193319b2171de102dd585c4a6c84454d72f1863dc5bff111c1650cecee128b04d248252067f9e3cd75f746513939f6c255f82c7f0e6bc3f5b5e17f70f43d1119771390065040413723e02f19eeff10415d1a50d8bca76e6905356c9826d153f91b603e151084a7b345e93798cbc4c964230a36c87e5b7a9031a7e10f1b5785d0e5cd6829b015657196458b75d174718262304fe2df96f1a718516f182cd96b247b116cc9d4494a74c0709a92799d57507f4f0580ed770ae661158fb60869fa844f4e1bd855a9a87005500ec39bef22d687aff06cc85b36adcfd792c88844a22a5bb11c5021922650e3e3ab718644cf997e6e1b2f3273b5e6c04f4a9f60d306e03d906b9d40be07d0e5ce82bdb21e4c7627a354ae617d2c599aa428bb8492dff8a64526d6f21dc39c8c86b009db1f3a69962b4ef971dcf303928ae77ae9529c2d6bacc2ed057d082a1e1b653190bcacb181e2ca87e5c6ddc36969ff1169f5b58c8d96dff2141c858f7a3ae05f95f0877283bd7ca17e68b08bbed009fa3479398a5048810784c3ca9b6ee397757eaf9bcb6473a8a2cf0f8deda94aa886dbab68fd4b2b6a2f752c2983610bbcbfb31c292ff3e4f3e307f8390fd293445f97e608f635707a3ca6bf8c144e11ea5624c9762dba1a97d7e04a328d5434a9d4c549b2acac9e0fd682c5cf45b8c8cdb8fc1457af912ade259de95df5605f7e3e565f7bc21d159f41e73e7d0dcdfd3320f1dc0e73de8ef6f03e40b649e6234c892cc32b232cefc11c40b4da22b494a5ed7d91d255457d3390f61e80b2fa8ce073b7cb32954f6afae2e05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f4c11a51d0bd7f666607f608d5c250e88562c72702e3eb3f3eecd61567d3d76c","proof":"82a244011ea779ceafcf059c425e97b12106bee2db767a37337d0bf1f0095c12a2f0f5cf8c811a172ddbf009bd1b21b48a87a54e68d5c98092a853d74af73b090aaea1c3b325304ef37707ddefaa66f621cc179174071cc4749d0ca722d72a61f010e6f0276fabfbe3f41316e4fb0bca982cef036916958bb519bd60a1b66f0adfba5efa69c5226afd2b5681fa5b84fd0bc428c81f47495c3578e3c47e619e0ba9b9794f4ef22f7454cf64df09eaf495aceffe6c1ea1762d5c5af24af0a46b0fab9126c28b21d1700480fba20d156ab920f2f58eeec4ea36af5cf6af95e4a501a44785ab8ec963c6a8fca23f29c7d1e093009d3596af8e3b4b421d50162ea527fc3ec6bc4159c1b552cf44707b7242d1aa667b1d6d75ce803f23bc387c7e6b6b3c3640ef60c6e727a1b8d9950385459cb92362b0611302dcc83fb2d4a98232294487745922b24c7b040aeefff8437c6844be9f3e7f9e7147a361a02822113b70c8f7e2b456dcf87f6355497270995884bd5d0535559da9df3bf1fb3b5f10074e402368c8b5796483b6be93209f10931a5b843be530ca12f9e917f6db7a48a80d94da4b78b61b56b2d55013e3760443ee3ea0bed3a84a95393779e7f1acd7a42130bcabfc55035c9e93c09424459ed4049bafdd267b7c97ae0a0e76608c972f2650bc854618bea93ae5a375ac7eb4d246f730fbbc6feae965bb3010ae51bdf96e36345e513a7373015d9ea7392ca3776390fe0ff2d5918363ac7626869df5b801e6f5f043e9e6ed2d5b6a66a0c098f7202857c5a565b233b57fd9913dcce7265e36cebfdfd492d876db8b85b1dc0b8d9477186a3794f531f6523399b178ba2050f3eec2d76093ef9bc65a20f8f6c26016ca9f9bc7b53f91a6f427729cd5bf0004e2a91282710c06dd3b72f58d442cad0df37d8273b281e83a96db0f78fba6180b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"000d8a14f3bfa8c08ec7eeb6947b9eced68e8b4caae9b397f1863465be202a58","proof":"e6910c68824e6bef893ad61ec755c92888de3f0a76f55dd4923e179ee5ba8364543ab9ffe65df496ad7de4a12d217660f44d661534a9328ac4962744f98dcd0ebea1ed7e462e32bdbcae2b7991c24d4d05a37043ace12deca6c059fd9205440d048fd18dbd5331b320d6498d001d09669fbbb2452d47176665735ed12f8ad43afc5b9387929bdaa8567d2262d653eac0b5ba1433fb7e9d2bb7e3ec556ddd5e072f319d69869187258536a2fa56c723a1554e4254511b3793fc0263e23e87fc0c4e5b21e1e6bcbe87acbb51270bebc773ea626eecdcb2f7d241d07ef1e7e6a908bad1189c70b19ce37a4ee13eeb5cfb1d762fbf999a35959ceebe3ac760c65f7600f5668390665301f05441f64337e5ff56a3c9e314676c70a62300dd46cb9b244c7f8baa30248ec61d3cf778097d3363f14455a83e7228c414397edfde6ba300ae2b320800715a5d8030aef9e446ecb818b2532d89716430ddbd70f843950520201a0fe310d199871fb1d0448742a52b7c8596fc610c80c483da37f4d7600c62b4118304100f722e0f60f8f09a5c5ed14a339e52a98b9ec66a5f760cf735b46984595afdf0f3d737414217be7f06898898fc00bdfc2b727217fe4867d5c7307ebab94f444377d43bcd802147ccabb222024291efedb2186a72d416cc49bab40a7e48051d09a785b9f03b9d5df37b39ec85074a742fa8a3a8711d2b5a703b3178fe0a720a85e83f66a9fa562e0540ea76398a25293082f6970243073e4c6ab2797c68f5875b4e4753a25759ea537550b667b616545f075e399926956484812a62d2c606816bfd60383a8ce97178dce0bca1865262da138ba18201c55d66c24309260d3e796e3c28b95c0775560f05ddaeff3fff81de6d7ddaa425f18ef47ee30ad82508337d45f3559f214071aec8410c84b744b4244e9537e1df4e4ec3824e08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ca474a9441d968c54b451c3c1084f48af8781c34bdc67e79582919234a30d739","proof":"2ceaa892db4851ef305054b809ef162dc04256ac38055e975389d9f935c55f70b23fda7abe355815d9fbd5ec5aa5dd20d69b76bac9d81780d91f224606d8ab5748f64aa619fc7318301b949f7219e3b3e784cd0d8d2974ae6d89ac4cabd73309368c9098aeee95d864704e22acb5569861b2e441d3064502ec5ead3d75bcb35195230cb28fb6569ac3c3a65eaf55dcb70693547b437cc45eb75370916cf4050b4ee56c2aea68521f0f833b7682e9ecde569dd1f09ecb7a2801791a287bdf5d03dc51f3c6ff44a5982ad45ad61f34f2fa1c3dfb0523745e543ddc6aa31cf3a6055a9d3f03f85fe1d2f43440072adfc9e258568c97fa19b2fe2d807f9a6cef780b705e8a8f206ebb6d13d1ae5db3ec032eac6e6387047eb95f995984993231d133f47dd1416d7184d9eb809b671429d877d9d3b223553256b7433ec88a2a830551fab3c0cdb71878f4455ce7bdea1a5f7ac899df71a883665c37ce44e7e2e7586250f20e92faa9aeffa0a1d9b7539b39e0b66203ed13a9befc86e58cc42f80854fd2ae337e784423e4e76a5cba271c7e1ce929b4ee0eb954a8f688cdce0497eb6c4add161bfcd0fd7c8bdd4128e18c2d467017d7a09903451997f28385aed07c20c49733fa1fbc3cdfb56b36a58be3592c231984008ee4ccda4d02ae9c57d9a908163437ad213e364ad0688c8a6a0077958641534978b23696d5c7f76af42002077aa3e79257b5e29ff73bb43de95256ac736321311590640bb7ba4e976c53566e060d6e9b55e9330ecfb6d9a2dd5619430ed36fc60e35df3951ef4f146beb040a6c8747462da27046137aa1c2cd15af6d44c426c9c4116d03c8bdbfa84f546e48cfc77d53c3daf5192ece7779fa4615c367d6a1d10bff7b9be7d958ce525b72066e28df801bf4bad4f04a3de2bda5f611e384fb0f9ce361d7d75aea9cab1aa00a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"904f88a6f043a928c2df490dcfefbfc37540ac5d02bb59d478b872fceb12de50","proof":"a49fdf2e68bd90d16c288b58bd55b7d00337a054d7ecb6ff90c76dca9d253338b2440a331488c3031dfaba4c4c6b85c4a2390947b71ca96ff1a109aed9cbaa15fcbc0ee65dd1ce259385217be11f388db17e354604da6394e3982c1298724d27dab4724c602e00da6553fea862a75f11fabb70b27e82367114ed95ee6bef7a43a6cce9b78931254cdf264dca32fa22dc0cc13cbda7825d5c6855d43f51217a0d7f9bc32222c9acf97920e0300c097c92231573ea09a0f97b70ae5f5f52c38f0f595519dbc7587af70f66287697c4248d20da59dc916e283b9247df89e62fb10be8758818ff81cdaeaa0e803a8c2465b259d7e72a6a28fc55f84cf1301e87b31a3854e2cecbfac945343b4660a1731122cb9d28772877da276981d2398bbf802992d3a0409feaee8b1412d5d8b772f03bdb87f0fec4c0dacc654aa1551a2dd81d447103a35fa8c7812a237c3e2121e603ecf046d43c6616a6afdb6692bb3f62391cd1b5ec822f4fd49f093f16bd41f9ef3ee9f2c654fc7ee72f3448e916d1370d866fdfff19d644f427783d53eaf0a07e874fdcd2e65212b71a217058a0c4a27522d7173e80ef5f5ebea8a0e950a4c23be00a3b36fbedeebd5092aaa92e910e566c06c9f45cc284e72d6d1011063d703ace34f5b2dcbe59a8c8225396b2be597e2acc5d4bd0c24c89b34849eabe5c8f1fee0996f8e0a204ec6ced31e06b2afa2608fdbaaa3cb65860f4ffe1f8c8e21410a3543b991a93ae9c7a91ed089d47aa6e52ee915d88f70b14b21b7fde315ce80fcb7fc2ae460fbe48cad1880dba005c09008d2e62f837057dfbe7491606671ecbb7ed1cd8a874f4cca6a589215a8cfc002ccfc0c4bb12881b63ad42f6ecb99ec669186752116a7f84ed1e94e1b7b0640fe69c011f3e3d678130876bb39132d1aab46dbf005c91604f782247ad9e3ac109"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f0ea755a806be0b98360d130a6ea9bb10cadcb9c6f00e9bcfc4026949c20ef4c","proof":"189469b0852eb68b6f0fd54699c0273fb74f7a53cfbcf24c3b328cb1ba644736e0ec353a6913c7a995264447eb7fc46b0265b98b7b7e343544350787d055ec66fc99d8f5ddbd0a5e20ce5fb7c542813d3f12e3af492bc28d1086366e8f50027dec19ed35768f495db4af9cf33cf2b28394d94df1d07d30987e2d581815a57b30e8bcc0d5bcf9ee76b497cb08a56c93a03c22d8f10575d0e52fd529d7f7576f0a2ec89f427dcab1f2c390f64f34e2e74dd5d189f66498498ff474bd983534da0df14bf498bec68fcf85d48d757919da8089c0e3f645d13ca526dee3cd75f8ac02aa7c9bea76f7c3aca9ca037168c3a186d28e12b65b2adeeb739a7096bed94b177c4284c3dfc2c24431e2be75b563c1f85725cc003549542f096482731ae6ed747e91731cb659efa7112a364b3128812c4710be6b55523164d93d02f42312e8122aa09a276a98f49f363a54255dd6467e0b0a216e698e4675342240aa21078c07ea07e84cabdfed1f48f186ff9b16c075cfbc4f66feba6680f258305c9f0d751ba0f90b3e223646290f7095083efa0a2aa5fb1bdeaf90ddca8449f7f6d7850f04daf704f12e9f8bac56f31d990bbe6e0f34e8e6c99014baef43359bba3b22135556c92939a6843f7d51d99b65075d0c2b56dd1985155051d3ed4b83288afec834be39abd4c08b87e9993a277ea647cd8b6a0a1f91d5ba8f39d54f9b748752226e803c0619fb1c588bb5d86234f50a079e02ba8f1af1232b5c8e47a8e1297c290090de00fc2b68d4b11169e95996f1c5a8af23b8873021e3da7ffbc38dea707f71dac8c7aa18035714399eaa4d452bf0e10d154233cccadb8bfe1cb74967e705515bd78dd60893739f0291c58e5f2fe2b0823c168fd24fb241d4f95498ed170f0d5d98cf4f2f5bc919a767d7205312d95c6ae68c37d4620b7898b69d937122980b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f2e493e953337f6f42e73f8fe7ba08e14efeac71c8ded84cd161e53d5714992e","proof":"66431a8fc0a558108c040c3f1f721cb4722fbaa0167640671f9099bf64cee6315edff863adf2665646f8d7f0499a88d9be5b81de95ff9be87009ace8630f524ed6c7a4481b71cecfac71d1d10cb1d6523b624f8b932982987da1ccdda318101aa26aa3c22987993e67d96f1054a5c1c02f74ee938e8afa64e8f55060fedf355a95eba2ef1b14e01e095b70657bae4e9016b163232540be520f716ce0c58a2b0e42906269a043b3af9a5ac64f8ed8abe57d1baa8baa674b80e4af93fa2d015f0a5a07dbd2624a94b02a56f68b5f4f5135686fc2c1d1993a439dda5214082aef09748a92d3f63fc6968069fbab55d48899b39b3025b88198a076c002565d40842e8e49b0700df35aac38545a6d44b4b37008b3a356b54b327dcd38993de8636c6086c49553fed5afc9c709a94b55dbdcc07b32456e0b3316010569fde542d38e134e0b663f108d8bc63b6e1e4a67f8373fa2a8f41fea9ffe432de3233436cf9907d4a564784cb6b8e030bb7f629a658029ccfcdfa8c0ef906e0a0d32c68c458d26c299487a2fe535c82d2e20caa0221880dcd5d353d862fe39a581c91b8f1a314b18267e8d96b03f74bf66733fee24f6f387ba85651d13ccc1e6df0a4af2226e59325a1c00ae931de8d0ef28b8285704a2a5d1d3fe5377f8fd19756983a37cad4caada106ee3ca2cf5b30febd80cd7827dea4bae90d71e0200e08854a47e643d0bd46cdccd1c88826faa7f2db9a96905da8206ef67b6fe14b5a8529cb287cc7b6df0a957a6456413cc363d62c082986d9525eaed4b3dce4f208d15e4faba214c1318dfe8a3d4190475eaf0d6361cf95cf82e71747c948f478b5997e85aeb17236728f64b9c609cf88c56776ea903fe7ca9ebeae1ffb58c394bd91ea0e03cf6f6065335990b522ac21fabe37aab9cfd1d5872fc60b279b6048d5d8378c73da61505"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0e61025928a7d7445064402352e9adcd8cba9d79e6f7dab219ddce9a97c2fe41","proof":"465619c4be2252adb70992ed6885d74f4d1f030ca6725b2caabb4b971a1c5f6ad872bf9447bfc1788fee56d0307bca5ac8841fcb33ec50c5b374d327ef1edd56d0a908391deff527183ded842a831c66d0cd8534a761d3af12bbc0862b301a193e1af2183ba0211ec6213ae205c459346dbe6ea795a76e2dec54d1a426a9aa078aaac8ca97ec65d4abdbaf8c9a422fadac1c1d786312bc4e79e102fbaa144e08e33fe9ef38618d74bc4b55fd0516b65f5d62900f7b19e9834994454026df730b6a7d565f28d16b10777b0daf51944250c386b27a413cc50bc38ab698591c9500002493af7a6b8d22acae3899d2a3ca724495af23733a4d90a8709d6d03dd4d6594a34ec914ef7cc2e6018c94084d074125505de938285b98936726c7c2f6af6138e69c309f90781104bf313f103bd626cf02bd101a6b5cc0dd696c9828e55e673cb3f719603711473067a54d628ee88f4517820f11946a33dfa2b4cc783e0b681e6e250a0830e99528455f87ca5e3efc3a7a0f08ad561497a4d48c3bbcb7d026a41189928b6e4abe5ce447f6090ce89391ae640c01602253062602cd32c127108c07859857293659c3debec2ae61d6e1219afd95f0aea58404a96ded60f24e509abbff794c858d0de56b291c7c6407eb3d6be8daaa31a3be38301005663ce474c6a0386b77135fca70eb3c9573453834e7b3fac7d1a66cd3c7630089866f860d4e2589bf17924cdaaedd5fa134168d4a0eabc4ea35825a1b18a9900fee5fc41e4cde6a694c199728efa32612f4c42031425302b0445ab1fe973d2ad601353f2cc86a4200fe34f1ed4c9ae458bd74f34aa212b335dd59bb64045c91aa08f7311b332bb4c550a6367ea6c7821abbfabded3bf14509f3f76f36af0d12978adc100dbd03a9c9232edd3d5bab1f56c9377ab2ee0632cb0a83e7b3075ff0e6586ecc06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3c1b73e14a3bae9173d6bf343df49d78a30fea15b357be69171f7e695835446a","proof":"028caa050df720611f31fad1d6747ac3cc70b62517d7b1c7f4c13f57eacbb660fceda01808569775b3fa1fe92bef401d63bf5be4bb903bfe8dec6294a1eec844fe64f8de34775fb88cd4382515f2791d805752fc2020d46abfc00f8f535b0c6560658a8cd96faff5b1c5f43a010f7c123c978d3726c5c3da52a925661ce14c0b8a4e9ff24ebc93dd90128115aa6ca719dc30aa8548e767e498d44f0b7654710b0fd27ad03c8e9a83de508357d4760bd570472aa5c713b159cd832d514f530b0996853d3ab4dde896a0f73896d7b9e71807fed742b4bdd8a6e464964757f0630ff89ed3f094102bfeb6ea8f4a3714f6cf9ff759a9ea3850e4474899f3f44eeb62dec8506638b51972077cfeda70bc5ab083488dc019d0166ce06105b87ded1f631ccd606bf1198070589c54f85c80b921140608ee2f063457b468f5df783d4d658a8466a72e89f6fec732ef339197d08b55a94d5cf007e79d35378f3f2d8f9542b4ab45354eeef98d144c3e2edd1838b5298bd59d55156be4045d691bf308bd181c517c4fe9293f9d589aa1f1500870970042747870f9bd362fe54ef5f6359965184e4b2b4c13b67ca9a0d59e59d0f8883ed65a53d07c4da82d1421c38b6e2d50b49bee2bdf64a7f0f0317c3e63371ad32c265d4d744ef6d65b69876052feb01b6aceb0aa7663f6e067b4b1c9cb35cfae4266cb39c73149ea2d8d9bf48aae0c59707c25d8496943a022269b7c93eafa487921db1408d1ad3e8c7fccea6371eb35b0aebc4919c250e75902042e5b2279a037a98dda9572a9dde421fa4f90901549ea541aae2d9196776a6baa15a05b6bddceb3f9accf75bb6aa12a69e890fb1549a2413d9f693a9fc618016da31868f5c28d1942baca1f78c2a5d63a635cf08a06a77c724c7abbf4b0a6146816b40491fb8ce46a4207b0d9eb150c772e12258e0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b6b969a042c43a3e89bdede1c864013d05430bd1c64973de10560854ea458331","proof":"7ef7705ffc0d3284df010c15a276fb1883f9cadb6261d669ed2c48d04d2c5a043a84ca4a60bf33b79699b815a3e02503954d82093857296e740d1b6090ad0f0e3671e1d0cd1dc3ce12a59424dce09af758daf057470ca999b70fc79c562cb0521e864bc1f8090f63693726b0fc813884de9ad135c897c3ddd434ae414ede900619419f06f8600003042e219c97063f633baa9879e43f135d0a535effd583170b43a0374edf6442584f2f58d4b18031312f40e51f8ac2bb927c7bb793aca70a0994389e4309f6520bb3b35e4e262e1e9793b66deee62f903d647c64023af8dd03bc0016dbf3e2aa0c0209fcba6a7470561f9bbcb9ae05cb32bdb0fd2319afdf5218e20a896c7983944a4b4b375ea4d0faa1198e295096993b461e50cd49b245409c051abf203fb6e65e86270f029a2a557ffaf4f10343b99176c92f69f73010729a6f02e20e422ba99809bb67c2a8ca821f86379052865cd3c73bd94257cb627de473a013338bfe0b4d0276aeb0b3f3eed43e111065cfd8221381dc01d0bbb21af0df24bd3223b7afa46a44da2005d979ee98c467430383d9896732f203a38517761ef7f5a7cbff773437cae06ea5deed2fce75c4cc1742d2a30cc2c59e0a4e4bf285bb84178c0dc552ac8b6848a05b4060167c2bce93baaad7a5d5e4861a6f23d0ea67271d7c0fe2c1792a037e0ce18aef179910b232f83f7295fa77d160c207a476fa9317ad058badae25b63a9ea80e490995910ee0f8272790dbdc86466650b09fb32e564bb038796912703586f616a79bcd04ea8b0341f1a4b77c406ca8427a5e2bb9a47adfd00575e706e9c7fd8e3f2d5eb5b06a9898b50157e230d7ae1136a6d632b6ae84c685430b0830f1d56dceaef0f487f87347279b23f7454156056aa4218ad585bb269862f5f8719cdaf1b5fe5599e92d918a36b9313ec9a6c502"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2a59788dc1d17b389db8af330272a732da76f8aece1851c562d5c0bcb8559d31","proof":"b879b30427cb399cdab30858544fdc2b8f5fef4143620181961a99b1e9b479300e2b433fd251ddee00ace3fde44900ad867177bcfe61eb3a4a0c5eac99eef2747e2884cf68250a716393c06debd7a64b50e31b2bec31c1aa184e7854e51974022c1f9c9a40f784255ac4abb1815aeeed0f81b891eba01649770d0609dca28c19514a1887a1bbdcac18ca73820ba39d280beb89f45948adc72bbcfb2028d0570f77a7e3cf8f4b7780d956e20c3ae94a21987040162d8c89b4f4788c64c309140a7f90b83da9bdd0c0a8d20551667ff0d90effecd457ccd03f27d9ebb97e351c00de58e14766588739b85fd5c4a90dfb48926a814c69f43990f34100f925b74d54ec6ceaef5840548e0252d08348ebfebb7d3bd530a49caa4869685b6ef5950a3fa4654d01a1606b819e16731e943226a231a13579f6a3f2f141d41ca7dc9122759c6d379ccb586c09fefab75935f32a9509b0f36dd093c32cd202d702d4a7f1238a1e4071a66671156bd478a379d2e1fc47e4fceedef79f90812ea95f75e47e1da2a1c50b589b7c8791b1c05a729bdc9bed3d533293add43b76da7884e296137d5e0ac0e1c0c773e75ec1e27d3d241996f786a45e1865410543bc03636734b96240a83d3a596e80620d0372746b3952763fdff442581e9c2d077afc6c3cb8c759ae5077bf2914f17d76e4aa4bfda269aa43fb8dff24db6d79d183cafbbd6be73e800dc6ab7c47a84b3d9b685a1084286f1b444c2730c75c30d68f3818d2ed00659a70b844841c182a00a6869ff0b520e98e5430615b983797fbaf7c1527e6570e0cbac7a6359b7d2fa469b2b2daf121afb6c15605cb3da93521a8a4525e89651df087a35dc90e34b576e2715c34f2e5ed136ca114194ab72b05b9c33116f61f0ce55ea8b5e5756c39448f894b0f218bec02925edfb6b9f37e900a88ebbadfa303"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6e1a729594031949c8769573fc3b0e2b2bf6c3ed5d3248c7fd27269122a5c90f","proof":"341232782706b87cfd701d8c972308a47bac67c3dccdce04fd2d4e13bbd8c62202008637021ac7d59ebb3d16ab5ba5b4ebe636f37a47cb3c7fa2e4f968e8ec1d5234039e1070b8d5e3229baf7d1cec83f73da31a433b51fc218d9d5c914a1a17c644b21718377a135661401171f8dd1ca974e8d6d01c3740b9a25e1af3b5103ea13878e35a012c29e8d8ef4f76ab99a4a3664e75f551469e5f8c89a81adaa70b5f78f5bccc941d099e6df8867c8e520a3f6f2e997a4e2d430ae3cef3af960407ced170f47decd62218731c00996e263a823e308004a769fa5ec3fc5bebb4d000c0a9665ffa304f0ed2be7aab4015fe1324e450856adba444fb61e3e1d051b81894a57c2caca3c22f10c7ffdb697909de593c052bb3b54dcbbaa2aa69ad73c422722871137b181ba1377f581595cdcbb944f4dcc295503795ed8e165a0ed3e67042969e56f3c52a7597944ec5cf5ca96f1d9b47b07725831904ce40c0d88ea44896f250cc81c9f5b3c6da3494d68ee2a2f07ccf0468a161a5aa1fd18964b9d3626eaa25e1e5641abc9b2dec873545e661912675535ad32cb9c0f0f7c934dd990bb04f680ff2ec4425259e02acfc7ac1be18482fff1b1ce97cf0476463c6877a698a75cf7b96010caaeec73763ff2077bf5a4beeb9d919e3a2b4135948c4170167406096a1554644c7393143071f06f2837996af1629cc4194c20af44c3b7a743f4c2aa7144c8c8a7d74f250b536d310ac880f9741cc9f5f7e348c3f19996bff1f4457f304f81f75b3548aac73f37bdf17520487b22683c2f43ef376e5bc345f65440f60fd183a7937aea0a86854c0ff068448dc8816cf4e0e748eb0745fc22465d0f2b65e2327227e39eca99c3270bb3ec23a9f03153c344113c7948648f8620e3636c8d07760776037e210fb1bc1e8f6e6691b87bf13e4931fe48e26bf380903"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"16da2b8ed374150e03be56490c16c08741890075d70db1e926f218b3572ab458","proof":"96da441458d97abb79c071f31d4f914ae48ec95c15877b242900fbfccf8dc305b4b61f9f87216f4c84802cb4ca15155d078909b3f407c057ad35e6791685a52170b332aa02053b2f37466cb82d978a57cb3616d09549e2abab7488dbc7cd520528199c4aacad3fc84d8733a86a9535f43abccdaeb8368327579d3dc9bcd02e31a2a4cc380e1760567ceaf01e274749abf06f72affcebae74a3a61425d434420651f1f9fd8057a5eb899c0a542932cec831159a2a565c0bbb4b3f006aef99ea0ef1423b888ecc2c10434f14ccc86f7f0907d5e0cf5115c1ee5d9dc5cb5172cf09000f1ba522934a3a00ad4f45c446c0a2037ee6112c780d9af9eb9497974c76328c41cdfebd8abdb6820b39ce34bd34a01a5c0a9563f7cf86c771fdee273364739a7ca2426a578a45daa04c9d82432ff084e639538723052a1f9727057f2f2d05820f2ac0895338356721adf76d551a4fd95b218581476f3fb7d8a14550bf5c21fcc17a8eefa5121cd1b30a0fee136f791bda5750c9dadcab8b9f2c937cdc3d5d76640f356779609c99ab3bf576d6406b8619a5fc24b2bf55644b4f856045947062e921c9b8c700096b36ae845b3cb2bf4d628964c9530fe89aa8583cc87f3c00d63311d73d91efec2f3e3fd9e9187ebe274b5d388712379632694341f648ae131041ed2d8d8346265779281b7c6fd69a6078a917ac6bd472c4cb7d74d89004233acac37e2172dab79ed14c0d64286a03f4f47f489f43f512be39d8329567f53a5822c06c7203ceeffc4d620ccbc2c1cdaba924692033af1e3a4e5ebead3af9238ac4b777fad811da183374efadd233caee53e905a5af0e76d14240132124291e7a166511854ccdf140d708384e4139dce5d383e9119b835a4a3b68d32ac37e09c74ee251ca970859647bf5d0b74a90c620d07fd7de00e2246549cdf40c718208"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b88ac0d5df314bdd14ca0d9739d85a3e8eeeeb30cfa38cba5a46309bad9ba123","proof":"30d4264f32203645b1044bc7adaea0eecba1445522c78b5a4da7eaf93fe1ca1e4e282dd58118411e0067842f6dbc7e9be09158728ad8c16d685b66d5596852655ccd961f8232e38743dba6843e1b1ad09d1d08f8b08035ab3fb3e5b7bbae9f57f0d482c801b955dc4eb5029bdea90f1f901d14e759c940aa98e72d5ed05d00126090e6046981311261a9388dda8b8d4a96386cfdd8103f0a0a74b38c96f7bc0e30c5cbbf8f0aa72fccfb9c9c48216ba2656c8acd36e588571922e21ec82b690d909d0371cd8f09db97b8cbf4a791e2ada117f6bb51da28d8c5c295ae78719604662832642801859213d8f1eece6aad1f61e5fa38d4ec86b89964364bb7a543206259cf7e4b3f8e3c0de5e4eab0661ab9e89f92649ad458c77274a2ded8a3db47a8dea2a8f799663828b267dd81f1824665fcd08017c6855643bf5f989bd9f13d8488fd5a4775e0474f5e9c680af8ad917f72e877310f8a24d9f44e8b09b1111586bd7aea4b2e4a2d27aee1578203be2efb236e82400e7f0000e8d9fa91bff62aa82a16a4a4c6d6728c9cf8c62c162fa1a78ee093793ccd2b1fd7c0da2c86611382d48ebec17afea7e5bb83c78305380d84a6c04fdc51427c864f2074bfdec70216651b0e4bbe0b9d29a4c4838eceab8837b687442e1ec2ab31a9fd83e6708d1422661b5a1c63084ed3bb9e939c731de670468b12513d130e58a25eed3b60ff737c6caf6ab8d4be20ad852a08f26d9979b4841dcdf2afacaad124e43b823b2619460fcef15020f3303406e2b616bef61701f655ef80952d1cfba07b16e68d0b1970f033ef8692cf779f5123de1a7aa0e032a32b88ba53ee7bd2611157c18ee86adcae974957bbfed983c52df4c8544c175be98beff33e3d6f11ca524d807d620783c8e571b38ec7effe40b8ca283ac2d6e8ab4023509e7a137282ca63ef42dd0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"56bd6d0513e4e4d15a0ba00fe280079c0121f0e0e7b7bfea80564fb63f03ce29","proof":"10cf3c2f9359d44d8587b0f166ae425dc5d4c6d6896a2e07deff04c356605f57221ce33a2addd0db279bab42d1f04fee0611c93fa6e8a0376f699cda891ca95f1ee651a395cabb9f91359ada3e497dde84fe329ea138e7a50ced3240e9d5c86326eea99d0552b555f90c9025a0a131d382c210a70f56a058f1bcc919680ba46e496aa56aa6da842a242eef24861622f41815303bd26a8f526d31ea735a227101377107b0785b4cdaf15dcf0c101e3f33bfcb0c06c43b1a3afbaf7954c102bf01e72b6cbd5ba4521ff40cb8a739117070f3fa1fb4f20208c889415bd4a77b17082692db0a04157c2b891bba93e7a05c8c75ff30f91fe9be408e45b335b30db6607262737a3466bbc16ffcfb34f45eadd387e3c6bc45ec96018c7fa5f529c22f08e6df93ddbd3a6b423ef2120531b541a92d8bd3ab3de2d8849e0cafe64dd9de4e747521eeeeca22e6a9a7d8e749e2911d2133f0a36991cb6f47b2b6cb8bdbfb4f688da9fb7ad830f7e495e331c67d0e8736327dc3aa0c50d5947a83430e6158704c35fce533a5ef7d9f3c9a6e2fbd7345a880911577d3beaecc8c7e8a4aa08e1ad603910de3378036f1d00dc6665ed448fc604a4c0c65aef5085c757869e1ee1e6899c22500d1f50de5f5265baba3cc34774c5e13ef6e8e6c6185410d87fd076e34cf068dcf82ba2f6ff7352ea7825bf9288dcfcc461196ce33f1aeb6617a615588d023734bbfab198c3d0b8fa2c60e02f2691210433361eb0eaa93c7171129342cb573754ef956685c48931086c8e1230b663213af0de409148ad13d69aa3971a60ff62d589c3032b8f107c8630a2835826a98bbbdecb4950573e70a03fe256da8b9f0e18239fc96ddb63e372e7911e2d790ec53aa3da35f12a2f7afd1b7720ebe279f1337b571e091c57d02d860ff79b3147d0c6df0b3cd967199eb56eaaf0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6eaec388fe582712459086651dce56b1ea774c82ceec07fdf490ceaf3be6cf43","proof":"1091c6bc32600f581e435cc984cd88fd9471e608923dbd68d37d9910ef669e08c86c2f052304b330d9e99efc5a690d23c510ea632cc54441db3654ab4a093271b4dbf970a3d2a94d59c796f0e12f8533044ab87c0320daf5cc4aafeda63e47139e7c539eb37ecd2a98225fc20d6949e4916ded2c8f54e70eec72f954b98fb803ced8ac6a7cd2037e01ec30278d0d63046985c46440f02373262120ea7a7aff0c8c1b57d2c04c194333cf17388efc468a79d696cc518dcd19ebfa3d343ee1b807cd630ab9442f6da0e492360e4581ce79c92bc405e6ac64486c926ccea900120f44a2f274e1f3111873fd9e0260ee9f22b3dbe03c761af8a22d029792e183f613a2a6b5cca0373bc72f637738c3d4ad77cec6d0aa213316eb35fe9c3ad58f5b3fd6467eb1a83f1cfbca26a7036251ebbadca3b4ab78c9c5fa60a62ca5a5b94937e8f49a1a065f41130f9126ddde6e0e905a3838b56c86f6ce391cec8212ad766ae27b35a58c6648a83bacb8f23d1f8970956f942e678dde275608d69b8a8f1777c07db5d9da626da8e5e4e423068c5b032b22576f796b8b302317e303f571ed4c8cc919c7396a6882574f9644a54225266c92134d49ceec76d9efbc1cf207d16ba2119c144ba3e983e7fc9cbb814b923bd840512c8ba1b2248ae9ba7a44e37b11aef5d14d7ff95362c29f6f2cf0a0ad1e299dba0a822e26464de919b7da72110088a5c898178e57b9c00557d429a6bb10a9915cf9bb26069b8e887b4b8392652528d37da9b374e7a27530bc35d75db72111266811e2074291554ed89c0466ba0528d35445416eae835f981a0e6f7a3aa8b35c2693a5f012a46cbfb5dc988df9186363708fc954a0243ad7eb0bf63d8dd8356c1c090c256a0cb4a65fc4ae95670991a49a3bf4f04449800c70560840fe9cf849d2c185dd89d567ac8b2fe80a3400"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"46ddf7915d91561f9b91ee2ed414d6b26527e8901e53eb3e12b67b91fe75d37f","proof":"96caf73513197f4d65907b1a1b000c2648b3700524160667175537b3953e1565b023908b35a7e3f7835c905c0d212b2ceaa0e852be47d98698d7057b48ffe57958f287bbc4e51e6c436400098f5e3e5f4816b1522041529433f6cc2d1b9f53110ef2e1f0e73a3bfae6c86ef26f6bf4f34a0a023dd7870eb0dba45a986d07ce0203b401cedb1018f4e857a724b17008f4d77cabcba6d3009783b75dbdf8448c080ace9e54b44b9c4ec073a4487de0384241d7d142e6799fb2c4437e87dc7af3021ff2c8c2623162e456da5c6339fa49b1e83e48e9555ee2791bd455385ea5510872d4d654063af6c022423962206e2799d30d6918e5dbe4d22397149a177cb827f4947ba0372a5f23ba29a8ec297b973a013196e1299ae92045c5133b1a63b96ed4a5555f19cee7853383f6905e18ccedc3dabe4519b29cb38af78e85eb5d2727dca608ee2a2ae9d6bd606e26f17a8a4d9b15d74122968226dd81e98dc3452a19d8c426605d68976984c49492f50e6d44f53e3d2ec0af496f04ede5e8f17a01673efea092f23f442b820542991a67ce5ac96d318492780aef340306beda23715f3c715a34b9e08aaca84b75c6aad90989790ceaecb2b3917960f1915df399bd4f263811e3e83d7c1b8e0ca8d3f33d63e02c2f550c4029c3b3dc185522e86b502d4eb38bf3b2fe1d6b6eedc07067984862c6c2708a660eb3a2c3a934bd90b87713aaac0647e006873c1e1dbc2473f70a3bbb857b50dc634e7470a574722d8aac0f8adc952f6051cf3298c60d1b0a3a6eb6c097dc6041be05ab29dda1eb26cdb506b2044ee912ea2cb091618132e5e92736c07145ff9ec66a8e0823dea7656a67645a9182b4a5c6c1f0ac7aea5166235dedda0dd9e2257fb7998189157f94a41a04df7566d199a8f3c20c58e10a705d419ec134d2b696fa92e662a6aa25c26e0002"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b866e5c4bd0916dd2e6fab2bf79d6c7104c959fa9c62e65a4dd36257b52e4c2e","proof":"70a1aa77012496845cd9a3f0e3b5958e47bd4d9fc1b26e89d60e8db92841a25e3ee916e248d6b146544654f1ce72c3162879a40b133b2cb08a1b9af49cdcd457ec90c8f3d98e995fe561780700effdac46c8a9fff9336044722891c3112111307648ba0db39367639239b1cdaac11e2c32408a495b7ab521a0718867decfa01d802e3703e90f17dca72f29ab42bdb9d155bcaac833b00d1e681521c40fce4000e62fb905fb331975372ec36392fc42d8ce0acf26e570a0db5acf4d75e9bfff040a047639cae79a2b3b5f2873e5739de9cd6ff5adf936224fea313f6c33186106fa4a67fab335e23bf8cfc73f90099136aca6edfc6741b4e68258b1312eeb5f15ae98e72f1e9caa92fce0b48345b3a1d334c9bb8d98c2b3703608ca3169b9cf2ab2f81f55c3aa71e3baede4a57391492fbb1eca928f90b306fb464669d4933b2ec259e0b542a5fd2a1c75c8c503f98ec9545761d879e6197def0572407b544072b86c4a1c66404db211e7553c7fd472690b9e0a1591d6d3a169cc4cd04554c649d84acc0e8b167d4bc65fb0679a78e0d20103a435f4bdcfe2240cbbb680abb605ac46680875028f910a283634194dcddc125560c62199161e8bc231094cd50b30f64652195cf5f66b632bf77c25a154f3ef674656b67bbdcc0bc8da055eaac1707ab9e420e44dd9cfd1ecee71a5d8448c65dd30a14760891557312c54d70b1614b66541eb1a753154e35ac71885881f745709e3fac9b51c84551b8963c95de42d56b5e9d8d47cc1f14f4bec6e2636a43b38ceef59faf78b38aa249058bc59e62638b7ef4eeed68da5fbf5da472f121c4288e81320e796fd83aa1e431fba923c514931f0749c775f0f1f8b34c171e7968f517ba7d96b4a320a61c525f3081ff209aa3a97c829be50d772c6fac99345e66885d881c5d803aeebd99ba8f45776d306"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ba31c4ec25212949a8547188c7fc3f4e2a82bd68aa75ee1600dadc5548450371","proof":"32eca7a0ead6f14d9e4c3594afdfb7b5406c706dcda68bc38e44a4f44935af256c7f1fb54260e4f6f52ef677db6178fc67fb13d27b02d0e6076bd4c6ed4ae208c27c746f61d5252b5ee7d65ca77970261e65fcd632ad6b8fe0ad5a624e379811de75db32f14583d6c84c296868b2e5cc4ec06d0a108dfdb71106193fe469975e98ac3e61251bafa77ad5205ae86b2c89b1ed6b7f52ffeb7ee627ccaf7d59e8071c9ec803ab1d0c4117037af7835d3169c61f0768c94964699ba5cb927a19bc00f98cfc1a4cee893d3ec5a398751a671abd979aa14e807fec18e2344e19990c0106b7902709f4c4b297f187cd63e06ccf87fce4eb55e6cfdabcbe252a5b653b54baa972c5a081c9e0b77a1524804f2802e67297436a4f38c353553b82876ff665122a6caf9f756f0b0bff97e12a38d76cdbc94ff2b660d899d8c8c7049a12016e168f2e5ba885f11fce06a70a9bbf3fd2e44a70acd81761eab7104b6d17846d3ebc19c11b4fb2baf345adf4042041b0c28d1c2dde216211a48b04a439367edd20b8b82dcca60a6836191b275b503c9fbe9061c2dfff2129d63deeca0da978db44ea8e8011b8c87cdfcd946e626bf223ff030030bd805e1ce4a012af10994e43335072471c12f0e59023a89ef482e2796369936591698293ab2776297656d4060b565ca24d944c06a3137ae5541826a89cffae553328c9c95e9bfb274d3e482464460f26e022ddaac190327fcd4e7fa21601a66f8715dafa5d24432c9738c55c1cd834ca9158f52d95b0dd0b84a06ff69912ef419cd57e78f836d96efd88762109fec59debff8634731d534247993e2ed0619299596a1983ed1ef495621aa00a28cc2d76b2ca7477d7ee9809c35611880543d3383672f02695a06f3c2330ac7504f955288ff3a1dcb62c07cad00eec81438638f41bb4ee53a2416e187690a1770c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"426c6a33529e4c55e11e7fbd2c2531347eb1b6103128032e905b04ae3a64d247","proof":"2865781c493611a0e0b1fc38d2b19b443225fb6dfc0329dbb2790ac5115cbe5402f5216dcf09b42f81b9676a0a07d762a1cd8a3ac9c8623ec666db1f62d2af4d5a4db356b6728d508d0fb67356f4d63d70f2b01b226eb02f88ccdfb77322c24766ab81c86fd1f3368bfb6f963fc12756d4337b90b5fc9187ec382b8bdfccdd4d1aa0b7a1b51433b13a857083786dfbab261900534ea9189680782f4cbfcab107839372c3259371c99e467fc3a367fa62bb84ddb05b600e8462ddac1bdd7e0901a0677c550dc6ff1d38a58bd77750fe8c05cf155730ed21ae8bb6ec3512f02408ac12f2c19a1eeb9a46a9d24750dc94fa7ddf5b3ab1af768061452c335f5b5f1ec43206d879e948d95925e08d02cd9c6bbfa25d193a28164bcef51d5217979157487a03dfa174552c325d76c266abd6735e2be3b22a2a703f0a40267a7e8eec2ce80b465b8c36a59f9b85ec120386729268716568c27bfea13e42255cc330d51ea630e0621e2151559dcb1653a4c6c93e99c80197e39d2ff39bed745a8a844a2e0e9948614076e4e29280ef095e89b854994a735b2fb03a42c2443d8dc2a7be637a724f37d814a2accd323fd405de613f0b76df065a9da2195b8b43598b7db15d00ae3a0a0f6daa2829626e0027561d5c17afbb4bb9bf9929d653f5b27d92a046446297ee25a355c4150624f28c9baafcd18af48457d626e495d4417d9164be621a3c14b7420b3d83420d6aeba7a9ae86ba3b3d523f602bf221030b22041a417a384b314092b2aaab8ca07622b2446733d485c88735382cf85aa1c5cb6b211303e85d133fef9d5e1c04793ae1f4e16d3cd1149d1acc94db9c439d101ac533480dd55e98a0d624e0fe20e14c4c4cce71682b875d2785dd33b92981505017c4b902b10d584d3c2336a5964e265b42e5264a1d7809184224c64040d2c9a460a96a08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6c21d5540acddad046503b2b217b3fdb24116f7b2cb1210aacbb722f378b9228","proof":"e87abc6c3864e1f4852246f690e51ce637784618dd3d642e3363b033fbc7403116a41437cf8ce05860bcf0b5a6eb06f1d27b1e0c04fb8e61ce58199bd224e067f6897d4010ddbc19771b81934eaf8fb4786a5cf5316d0eb075c4cdff1be1fc08247a32b23df60b43c16a700d06f5ec271a60e666c7a7bbaf7a94c0ced4a5455a7dae5797e3db33752a6a0b2079105c6e622bb626544fa15e53d42cb6be3b0e01851992d40678de9fe1087eed1435c60824c06e7b50e3022ec9b8f03044489b05a15819a5fb067fc40683230f30fef37898a20662dc7e14a70d0fb94fe7c4a90e90249f703eb95ed4df05123c6a4617f2f612166e87d7f9e7382425f224535406e86841278620a8f99194bb24033ce2fe50744be22a9545147cc988c682c1ff53544d286d6975e02a90f5538661d47e0c1b14ac9178f5a89b9f2e6e167d995943fcdf356e01efba398ea2f6d4b1c2c6f62681e7cd67da3ecde67717d524f22164d0eb1ad08f5c9668fa5d391cc291b2fe5f1b4a957198db109fefb4ef1cfaaa717cc43f4c13c59c5108793a7618a2dc0efca7539d21f7967b991b8eb7103062784065d41257572223056de4b4fa504174fd7850a9a7f0575436d3889b0a2985649497294810a833b6fd99de9058678dd7acdb1abd3287669943dc1e14e628ad3a16115377e96412636c859ad073890121c5d5ae25e76cdac6e85481aa0f4ea417e61a222887d6c2ed17cdaccf1eb1bbee050f194d882ec96b03cf052b21bcc438b65a525ba4c1ce7b29ed0695ae104f497526bac19474a1d4e197d78905d2fa111a0c63e37c8fce764431b28b2f3fef854d531da24939e82c92e62a362ffdd2223555db031c7acc820d63298d8718adf0e9d17b8814c338f885907b5173ebcd01bb921c9887ca25089256c8d629a84edc29900d003eabca6266fef566e76b0808"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b27f8e82df5c4b6c91118ddd35bbb612337006f27a3add7c40766d9ed2892b69","proof":"4a37a5fe5e0fc79195d9edd635c594f960d2c8c13e096b15b8ec2cdd995141115ebcdb7a358f8a2b60bf4dc7209efdc80a5546c3c952ab1a15c8f78511289173560e44c6d38257b49d5573c4ef17b113d11dd51c296c90e9a906cac6963c45480c996ebab2d78c8b198cf1795fe891dc59f194b9e85d3ed869880da3ccdcf25c3fb1d6a383ed0d120eaa507885de0cf21fffdb5f568b7ae6e287e8da7c142a09f31ce24fdb5caf3370fa4786b02695db9ea7d9e9d8b53c1702eb4792a78ce10b278583eb779b5c74f2d6ae5932f26ac9fe7afe05d9a26542d577e5955eeee609561e5f7c9b3cbdc8e5700c3335254c390c64da2e94fea864384f4aeda3fb7024ba250d216ec5da74af2221acbfb638b1aefe8bb05037e8f56d6607e54352285716a4066d2fda3a8854ef0875177f66230d0e012a7a3c5675ed908650a224394d40ea5b73509a7807e50b12fe9fdeca0a842b1b1349e1beae750a3f1f34480f3c9ee04b25bd37fd9bfcc1a6f84098329f4f68a3d5e2266ed1f69848ef09a3ee736e9e4b334d6ef760b8d9e4464e49a6b0cfbc587389b10167630550a90dbc91539e352701dcb0667efb61bf94bd2f9c80182290667168819e9d62a46c7b36e61ab8fa964a9200dbb118ebd8be55eadc35166396f4b54327c20be86cc914dad87a96f6085f469fe1d34754e13ea416d375df5d041d189fc675dd1322a2868d242dca5e5c5d65269df720bfc91ce54ae08d5c782420494ba7df659b1827b512e55f7411c23cffcc7b103cd421a6aeec4a918b232a4ed4bf1f8d22068fefaf8a4a0e847de24276db049003d8d3fd8e3d30e29555815482b5b0ca1ca82cfeace1944298ebcf7fe6bedef33e7d6abb3e805860f9aa164ea2e885350e21ca9016c91f0982bb93f12750765ccb659a76efe5c84f700a15d53d821954f23b9a52d8566d07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bed778144561cc4f704393003e4cba3600fab7ca057977b21051aa4eb8487048","proof":"2a44bad1d1d334905e75026200f4a177e6a144825abb9fcc34de179570b3f7293454755f28484efb6042dc41d0909d427e291ac1b72a326cb62bc0c37e4f20793cb02e6053b0219d8ce5b3a41df339f03c8503aa3e67ed407e94fe1f4db02416ce0ccf26cf9f482337a8c488475c0f3193a57607f0bd2c14440a88dfb403e122d2cc7eda87bd94d81835d1b97b29127ae39ce509929a90aa37fb935819700306b73a0511335f6783a5399146f39ae40c2fa9ec15553e776e7ce009302d0a1706a8b91bfe8a346156d52b3b99e4340a5bdb29adf6f9e0257fb325ff1c4facc40cfe6d4f7830dd8d4351f93778323f6af53c19bd988a6146f0626438b8f45dea4cfc6d011e5918bdbec421b77bdc47e6217beb6534d0abda8c4c3de572a948f21b72a58aa8aa8a798acf32d934ca4d3e9c90b7103866b66888eaaad18ac34a5648646312dc6a87732f4ec1dc37fc1ddef21385636098f09eba631a71892c906a348e2e1e2f838e1ebe0f66c5edc384f6b9cf2acc2cff399dc8979810e570de0a4350cf5c0ac0b048bca5876e58e7809b681f65b0b9374210e3c6c2706dc9448f0858e09c93d89b37765f8e908c40732b365849a141dcf211bca53415bdb7194a704a967520754671a2ec58c9fa7b6d791a539d64cdfcc6481517d5ad92dba2a477eab1bcd3ddae1e9defbf329f75a3173b470db1aaa2dc7107dc77962403413a09a63c57aa509ea586174114c41eb45e608925b0a9cccc2b50b67c25dd386a1935724b6a6bf7a6c0a159b73e5c35f8e4fdbacfbc9f0bf045f546de81df1b110c46e01648862cdd41e4751348b50949b1c329091540b956c4c50c982f88db25325f2f4b9df4af2e95c1d9f25419ca6fe633ae51563bce8c761cb4174acba6eaee02dbd013fd0c0080247fcf9bb26235a795327ad79412207c8ffd53fdcd53aa6301"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"286ad656780b7dab3548dfd296db09e979dc96a2c67bf1676c01803810e1c70f","proof":"6aababae3fce87c98a9db80692f30167fbd7e282355da24a69b9d2ea3b2cb26d629df63079e2f0ce88a193a4c2d79c38d2421688be13104b66c53fd4421aba29fc624065234d99490dbf20350745f7e6e44756345add2e7e7486c6aa4c678d0a607cbf0cb3c771d644119a92cd46fb40d128fd5cc80f6c0bae459802acf05f453096b6c2b4c4ef8a0228b5382381e01227d60c1f77b61d21959e66b19427230457b46c80a14c4b82c444cd33e7564c3e2c10177f3b1fae826e6e3e6953cb3f034745ff8053789ee9d5ab2a30d5af66bbab95a8b64d772c2c45a35ad79133a80c9e542b77d1e6155f1134cdc8fdc036e9f940c7a9d6e2d86383203af3925ea47ddafb0327587f2aa4fe8fb0399f78ac2f5e7cbbea2e593367def86bcbda8f992590674d62e75ab404baaed562a0ff5ce4cfbb3ca35b2cc18d472a5ee0a039646d8ea4d226b700c7b726d9a5dadf50a6c1523eb18da0810b96ad219befb215a6221c65ba0dfa47e91fb8d14ad5e8bdf97b4389f6fd3cd102c6415c552ed515462d06b5ed86f2bdcb549be9c7a7d420afe89fc079d2ac4c3c2aa63bd11ab0a6a30d401bd233e20b7f50fac5b8111aa2dfdd0a38db6523f57da6817d2bfb3540564c9a1b5081ae7e2df54313bcbfa36c5ced0ec3526fc58a98f04777a25f65c6c10214e27283dd87c5f7651b8f964c9a17edbb65ee00b81f6e84fe395bf3489da056b6d2a5c2dd2aefd4e42c0f03519b185f14258c9f9e708a255e0f230f1b7fe927c47240676940f348493cc39fda46bb065f6cb710ab6afdf7075e0e7238f9dd7f100f4517955f654d5d9ca5555b03dedbeb7ca648aedaf895403266536d4fca641a8dafd5e5d33ad7e2cbfbe18d0543ff34409790bdf3a10bf6eb80c4f9c9fe0a996833c5c481bf6980c950761d398444a1195fe973f832416619875460dd2301"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cc386ae4ff13206b94b32bb53c9f648bc8b6c48456d8f07f0f0087aa99234338","proof":"d23f84b76b3a5b02e460c2e68370e6ab034550b8c0efbd63aaa0599dcdacc22eb2a4f88a264dc207db7dc32079e493d2e45e56ef5d81b2f29fa0162547c43c28f6c9ec9069543243e2d51332f445d374051756236b50afe27b8185d78f8da36684d6d8bb60032a0ff47177942ee07a7fa76fc716ae5a447d5c4f8aff2730c97e809d3754f6dbea60a938f1354c6ef5728d1096f531bdad863e332b3dcc390c02fe67482acacb69640b0ee9d67b3adb753082a11b9e03159ea8147e76103bcd0fff1107f3d7548710adcf7e99a17b657bb28b6b30d0ca6e83cdfc7e962a7a700716d8ef82978916408a79ec8257d16081e3e897b5b1db567ace979c7c66ee2e098c60f197c3f421d3ce4c5e9606d691e1dcaf0fe7e1363bd6bab1a9be402a4961b0acd1ca408224093a5fc2a0818db3df373055a9fe9bbe72965b20f2a2f9f12ebce7967db49b58bcf9c20888eae0f156c6edebd0e267b2e579ac2ce50c17ac5c8ab3ce7e566bda02e498767fda3518d73ff62a756a8836dc65832d1b60e9c0209e36258a42ca90133b153f4c90fd5e197b7028491ca52c31e8b38f2c5e83922080746b1487131f454ad29c53228ca89ec3c0688579f87810785486d00f8eb5545e8c81b51a9b439c449465d86e5a5ee827365d5d04337af3f43936af26598e278aa3c88c8e7cfd5600bdbc1d26ca629c89eb15df6029206ae8b3dcb47c06364ae25f3355e1b646fea26701069d11b25022f6c394643f676563e4efb664080e3e3cd528de2b8469e04d5c726fdfba770e2fec9f657bdcd686bbe58dead55d61525ef0ae6d33c45b892932702c9035d98a11d400cfdd3af28e976064e50a59863a22ff2468c8b58d9bb4054098cf7e70160dec7c2913ec2167243779c96f90300f21f0de29ba2e8be5f4a3ffbf5f6b2e49335ce7801e74a984a866d62608d2d00c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"883b032edd160606d5d173d0c9ca3d566682363654069408581a56a155088a4e","proof":"8430d100479ea6b5fd628cbdc5e8804a8ff81e4b182e8d649d3690040d8c87058c611e9837bec0ff94b415a2043025b20970af0031375576fcf5793a1d999b523039526362ee4432b6c9814e006073be12a61909f9e81819febf44e8411a653060c3cd2f573671428d44820d3b88405e9c15e1ecf8aa85a691d9e8d507af002437ba8bf325db3d6280bafe00342b9c63042103942346adc088e015b79145600222d264c04f4d0bd7d59dd66be18935e1aa3d0b7b53a1eb61ec19c8d37b7b600cfad2c63bc9bc7f9ce6665636f48cab6ef2af9e9e4ab728da041cd73c0c540c0a9856058b3ecd80b5fa128fb23364b6bf9cdfc589634f690cc79643b0e236ff7670fa51cdd70bdf22e55d7978beb3f04aa0ed993cfd10fd82ad092bd21019e461048a5df13954d35a12711e579238910929b3332eb6ef4a115f7d65967a66f7665ae20068ad89934f061df96a754cc107fd7abc2f6daa9b928c3a81d839140830106d1847dda5a8301e8b84a460428b2a6732132845d91d1c1bd8d10d1e915f272623c72a428ca6b60e9c2ac9fddff3717ece8c929d517ac65184a3608914836fea54b4427ee2f30fbc6248a0eda3dec07702a2978eebd3c105a5b440c78a2d6280c78c2186f2fa508dcddf2071ed781c2fdfb7207ef556157e1f82d61a790d6a50f9248f300f751400ec4d5eec149e6052d90952b6b31a6bc4e8ffabc86c6b23b4614f499168ae4ed94e2b8feaf9d13a336754afbf7d492e6bcc682d53fa2b6988f825f247c754a914012d1008e542e6caac8ff931d9fdd835b2fc1e5ffce814aa618cac29ebe741a6a4a9a5b35eebde4654e0d2d2a9df6a10e6ae3748e1de139c59474a070b4410238d1a4ad79038cee0b1f14aeadb3354a7ac28cfed18c60b261eea7cf9083ba3889140f47ccb0e5b6cc7626b9a760cea5e64bacececf9902"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ee054a4c835313baeb4f24d4b60a423cd2ef03a58e639d9c6d278d77c0d75419","proof":"22817fd05e877f65a695836f6584e5b40958d6ca48dd7775fa285254cb0af16328c5d19931ec17ae86fb8db3a4109db3043816e5ea346423a1f48547eb19d0625686f8806bec42dd3bc70677a2a6dfcae336d3af2f2bfc9c26ed377d1292e875344834aabceb77b9855de0483b02124734e568037974bd766c585df4be9c0c2b6c938fb36f76c0824a179e19018005dce898fa458eac8f3ee085fb92ea056a0924ae2b40ef229dd4db240c7c9fbc17fc844df20e21fe7cbad2e453a77dd7d1025849674347af17097ac6b2b41dc4979529f2fb27f61ae5c35b212ba004f45805443bba5d160342a23f552f5910a6a855d0b76bc6f9ac7ca3100b29fc9446de6cb26c6302f89e0e805a22f0abfdc72938bbd0858159b4c0d4018aca83c6b51d63c2e148f17d5cf3255035ff03c568081f33fe60582494ec5e3c69666673d3ae47b49ca2a62553195b94f7b04539c7ff6241a963f65bb68ff507f11221091a392fb45e0017bd1bf7f779f9da8dbb63497a7dd1d3c45167905d19f2cc3f7f9fd626cc2ca82d4397537a25a82365513cdde7c2f7134f3b5608d6c9e70341658236755cc5563f21556a3bef1bd0dcbeb7b1a51822746f570b4921a14f1dcb5667e45faad93110bbe371758524762c2f08119ff46a4500b4b5e34d2b017f36e764ef17ecf166f886d460b26241623273ef7eae7f7fc307d681fb479ddae37be2c8102ba4bcac2ad16d7adac2181a08673551fd0f6b049f80c8af42b38877c84fa71e3830aa3f150369f73d8dbfb5bbd38698a806ce5929e0e7019fd65cf532bc8ba01d7cf45aa272439a325e8032b6c5b49db2a5a55dd86a42ef0e7bd56128d841263783f711dc114b56ef86eb4a2878d919ca0cc30f075240e18cd8fbfd398d33fa018ce57cc2f4c14294e21c508ca349461bb49426773aba0da3adfc9aba40c02604"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6ce6faa89284e10b1a7338e3e91300d843e00f51375d9d1ec8e0e44752a9cf76","proof":"ee87bd5d25973c8ea08989b286fa52a44ef08ea82b8579ee6ed96ed6094874203e6a1912811a6f52afdc89bb8b2a141aea8c61a64659cbe44afc91119d4e02403cd385f4ed44bd669d8b859f23a83ae6d1437ed0b5c00920c6255c4c758afa7380322d9f8fa9da1030e3bcedc2681f7c3f4eec8173e7e47ab2277de9a3b9ba222b74d8a5c1d6e71d294e71ded49ca118f7e9edd465577f7a9500b8f9eaf8900f1173872efe7c219289ff02087179372af75a6045327d81b8612be880c17b3e05211866d26528f34af663f7d429783415fee57dd3d1796cbdb6de8bb82a99c3009e98322efd9433722e6cfe06495f5c02f28f43858c7730048c75a0c955e1ae4f50277f3db16aa065715f009e75307779583bc0c54f834ab22c1179df48a54a7c922f71b846d1b365c94ffdd5886444924a1d7d77f447186a78b01e1af25de9453610b7dd7d7139e1b6c7a7789cbbd7359907b88ab3f72c30e70f3854b15a575d0ea8aa6d5a41542f5c4a1c74c64eb6b2b52aa5f1a2719dc709c4b4379bd716179a3cf5da74026e9839a9243098e45cb8b26de96ad0baa2dacc7f639562f1ac2ff6f68bcaaf45e3e06e5b43c813626e76d8baf30f5f1b969bafe5ed23252a9d19daf9ca96cf1d8dcb6996158dfa3bb114515edc3396fedee693188c704a0413769a9256855bfae1bcc1f4d5f51e628cc79ea83d16c18f5c3bdf2dd827329e85580655b89a3bd10edaf4284f519a2ae70f85705bae9d75c816b9c0f56298041b5f1e7e67dc058030d8bf6867d25c9453511a005cff044091ffd1a2a9aeebd41d704088e5247f26f69ef0531e0d9dc580bc55b6cfda06273a02621fe7dc262e012b1d43300f3ad45312bd918175d41749f6a05cc1152de70dc31aaf9dd7f106cd07ff8c57d2d9c7ec645604ea1a6b1dfb9f635b6c3af47e6f70136483f327d51e04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3458fdc89bf99191db39504961186d4b87f70c028772c1b2f6ea2feec497964e","proof":"001d35b0eb5e5fe6d24f43da3c66b2692f0c46d8fb8b20f9acf7e36181d92a2782092a19986a82e8ef3836508d5098369eaf71a339e891f5b9e0c3defa0c2f1b3ae7201cda761c97da4302b3f2e18e5ef4a3eb171e626269cf1da9be7225ab30f8f190be2b90552c9a4fec814f156e5221b2fd140a1a88eb4c170f3544437a33ed6af6a01a9c42370b64862ad14fb5819c673cf47bf5481de60891546517170c2348bef00bc77f899ce24f4a3bef775ec4040a0cd043c3e28f8a60782905be0888b277c55543da96d9837e636b21660a3a21925ad8eb262be09b9b6101bdf7012e78ed95d1596764d95f8186adec47f7bc3b9d868b962ab9a028fe4597bd3846ec202188c7bbf36560daa15c368fb8917cc3b2ceab99e76a28bd0b8c30d7e937b8bc39aa97e85ac87b2d5026cc2ea608d93f67fd2d90e19f27f3f466032195781eb9e86f5e9929f489c1c2b57d72f264114a7fdaaf45c6e802ae8f7fb0f00a15089656e0fa7beb938a8860e745890d16ecc986d7d33baa8983e61d021baa000a34e95a18ed4064108ffed02eeca1e65533a75c2fb5920cef92f5fe1acc4f5567fe7f2126facd4dc9c9b1c5d74329c65feab54c3ea87be96e796fd8c645502b1134152cf4cf796c92bac1c809f0c15c4fc2d4534185d35ca195236860454ec877aed6d531922310a1369019de70fbe5cfc7508b7990700f2a23df3cddb7c5c661ca7e03f968d8d577572feb10cb3a492c3847a5663bee0afa3718d12d0eb66a7d0a7d6156786d92670d846fbcbd384558a1a61c6294508aed137de4765926cb587a68f6f916cfff8b0c4901f9b1892715627d87932811b504cb6dbe7af95b2b6cf3518158d7eed8707075791b26b17ba39016f1e3965c403c96e4ce1e44abff0d8c9fdc2e59c782d8322edf401bb16ecc8cf8bb5921c84c6d830f76fd67c08f04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1cdb964bfdade9e6aaf708b93adfc702da0d66d5f36ed8f349d09818b6733236","proof":"941d0fdf0f1f43a3fe8211ebf3e06965a266858190b82ee99c3be03b83fa3f368c68b36b8afe0d67bc893eae4fa573cf109710ef032680573cacacbbbd49cc2c189d64657feaaff9057520422bd156ec59f59bb902df7d6dd8396544f38880006c95973d5f937ce4443532554659311e1934ffadefe97e7de4750601534dfe5602cf9576198a1b921c662b208fdba25bed027ad89c2c62bcbfabb195fada060b3f882e5dea126f2206d46a6efe5489940726afbe80147a7e7dd5deb505750e06f9d9356b7d025e901daa75071c9e1f6fc4bb6647606cc711c113aa167979dd0d360692eb014e4766ece2830c7d54a39bcb9b3d01e3ff644844db6a2eafbcf9395ad4bea870477a4732e50f005189d5ec19eb7b21df69e52ee02817506b6cfd5e4ac9b1e61eda47753cb2dc50f91003b94c2c6dfd3c077d60844574dd0a6a60409a2f207ead33fc341763f0ea6bcda80dd6c1c946197d4b74863ce7055011e21724673b26abd8214aa0db5b61002357ba8cbaecaa70f9dc172b33b4a4d123cb3a50b3b99db1d7cc1e3ba398dd992c62b193adb69f7373ac0f5ae1f5c0bad3991fa43c6ed40f453a0d04edbd9e9c66b529e6bc6de5573056c17a4a9168000973461e8e1de630bfb5c48ee841aab7713dc85f27ab642538df745364f535ce588336dec02fbaacc85ef4aeda38b0da3d89be242bc590dd00d4bf3dd3d1ba83ed4d367a864ccc6133881d2310aeff4328a97d9a4baa64534752a85cc3364ec3f9e21b34de269792df24c45e43e8e3b43fae73fd4db2f66ae548a933fa9fd63922162fd05953baaaa8aa8e5e5396162be718e699c97f532325a5e31c35df1d0a7a620d23796deb3fcb9ce2b18ffff1799fe94db5a64bf31cf5905e22c546c92c36c80fdf133dace031059edb8b606175d18607b939a15d9be4de4b1f0aaeed334b1201"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b49fe027d6b97214835babee08df1862adf685fd33e2a767945512501930a60e","proof":"9c7c6537e95da3db3af0c7fcad00238d12a1f2dbfea3819ce9d4fe9fa37ccf7b443ddcd95586e3918373a2a0d5b0e731c1591a0a80bfb6b5a0356ceef2e48f3482005373a3f51b8da505dc84b25a0b61ffca1696b57303a63f4eedc90d37ac234646258a3e2ce946910ae9bedc46b4b494c06ea271505ccd022e93dac0c53a11d5baacaa389ef037f55b9ed48d73a93ddb5e3f7afebebf2f5fc8963516ff4d020880309f640de1d21471e1c92157f7b6f8719bdca11584c41c75b3bb53729704842691aadac8d4f03e53c5c1e8a65d3944888d9f75d61c5da433cd3c751201012ef358b03a41bc4224fbd66485b2d65544811c25fa7a3f5e30c3ff58d82ca15312ad51886e75cd6c601163d18bb0da008da8946a45f22013726ca2bfa65b661154eea062826082fc2b273b88ccd9c114a9b2532f5d0dfcbcb4964df5a9728f2ed4932824e72a8eb73dafbb339864f2be02172f99c12b17efd7a2daea9e876d24a0b26eb9f9f4446bfb8f73ac86919f415da2aff76b301719ad5b4df82b53d5745663510dea3e96cf125d5ac717dd183a8424189e83f8332b7f555c0c5137aa759665015b9c2595650b33fbb4d5c864a2494f2b50daaf36a254558949c91dd5397a9033995f29dc4068cf5b27366e342fbe5e6584aa83569ea8be12cc5b9a43123685a1acee4fcf5e16f8920307ea7cf1f6537bf04f24bd368e81b2f05f06566c38bd1110d2ccfe1d3f9a2a3963573217b72f1451712c4a6acde478493770cc64f6ba245099c811319faa463c3e5d67e4b53b04338fd3169105efa5ae0be46662d4516382d889b4f234ea470a0c984f474e6252ef3cca444028e468278432ff6cb87ba1968d5fcff26e26eef0cebc4c797e007e378614e32aea64841f080620002e45e5c4bc5b62050efb5cfd475c910976a8c7aec29e0556db124bbe33107e08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"00a9ae73f414bd9753d8ebaff56a79b5a8e33490a955e2ba0727b7677f866b25","proof":"9aa19916d571ce1ef33344255e67f7bd2fd31a0b65a9638218d75794e9948b2b4e51cd3fbdcec4fcdf7cc500a3542e091d30a6789c6df73fec667b6d6faab46f42424859d6cbb3fd0e8e5aaa4b7f82a06ce0f949e018cef51357525436767c5b1e1188a3eeee5ce4d504296ce86e63e3655750380447b5a1c7c80b112c8b1f1b9d350646dd5620e6582abdf032b8c9be13d9b3a5285fe285a773e35145c8a007ece16bee40955a3a84e6af21fb5daca049fb4cd016973616e9bba7168fd2ff04b80aef0b831fd672fc291a2a27b69a1018173f7180f06bd9f75ad23ef17cbd05ba2a8fa3e914f52451b624df72bc06005e0c6e8f17c6bd9c8291001e254c0a0e96b3b8997e1c9ab75594536db6391f7703710622af178656b8a647359f9f50530e865e80ded6659165fc2d4a56882006f3b0ee0b0a41129eb35e0edf5239f63fb825d2d04432b4bda4dd23c2c1283e1218711b63e3f86c2e453f41796fabeb3a7a0dd5f835ab7858329f958a10d2482cffbce4f6624e9c276b89c3a8d89e4777e49ff1c94e6a9f1d8b23ef1f2036d322d3fcbac2b66a3a860f99df3da9172e703e669153827eab85bfcbca3c698f3d06af3690611427d24f554b89fbe8105f1972ceb8097273a4c6dbdfd513867b65a5cd103708abf84d78dc73889c9f5ef205021bc815f00899cd6e9c81e2c21c574ccb128b81ab2e8a5b5c3c7b81ffa09047ee3f019eb85d67a48ee319c02742ca8f1847f79309ca359a837fc36ccb440c149ebf0fef05d80e5c696a9fe341a0e939c4b497bf9155c597a0aed6da52f20e4f0482464bf8a42a23a0bf4d22fc1bd3964c503056a8f49017164845d2b427917ba8b6fecd5f4a01038e1515aaba6759d4e441466d2d23bae92c86b87db729b107a6bb6a58fb86c6e54393ccfa45372b52c3c9ba3dc77860d0f7b660a72056e403"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a67faedbc6045d52808940409316e52b162551ee6689fc804bfe5ac464f9e726","proof":"1c885d4855855b6146714d982af7b2edc701201bc2d928bad2ddcef8a0b18c36ee2ac25a2ca8f7a0f480114902b012d734f0281088d19ea29e9598f2202c0022fe5ad8ce3ae8a9d9c44157c17711e4d77a2a5389e9c95b2249edf96f6f3c9a428e47792c8d6388e3beb69bf86e0aa224d26134a68d7a64d336193caf4acce01a6a107f7bed464006c1948f48f2ed76a69a9961b5e92266164331e7bafbb2d3025aa8650120381fe218aeaa299e5858ffec4fde42e940f54a03388390c09b1d0e022f701614afde30f0c18901146793fec886172a8a9ce38d0a76ed171b3adb0342d03d2c2c192766f75d736197aa922a8fa9197152a110113f052d56271a0152aa6e115e9ac1660e3dbafc130d53e101e3a61aa29a36a1355644acfb2c46e05a4a30c7d45c5ec3d5b23552e9fbe240534f9d09f3b825d0eb4d29d15a03c0dc5c30072d36a9fd75251cea9d00d5bd337d1241e23ab8350af324767f0c544b161976dc358d170545f390e662263e2932de508c40194f310ffabbc604aee64c7c76caa2aa7c6fd5b3cedcdf90fafba8549a08f98a79e37599a18a5b6d7439aa1911229559576d4c4494734e7cbc1596c4a62ca563ace106a6e34d43a3fc68efd16fa872989ecaa8fd86e9119adb66af4a8a5b58866f6c33c5713c33e0c01d56780518924cceb23c4de7e9318a82aa493f1efbf69647c598b193a74310379bfc0c2e8cb3e2facfd5be7a613dc6de697ba8700e420da4fbc4de4e9e767b1697e7b221da7a9cf5375cf4347d13c2a3eb35333edbcabc659b209adcb045a580752f4b7560a79307e8f3a73b88ef73960528d60e4ff1d95c907b60d182d134e998d5651e4abae7f8f7073b1489eba1250572792cd2c4c6d80072d68239bf26e96ce83c030e8e99354fd42a3adfd0a393ed23db91f18c489aa4b2a5003359a9c387024b01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7c5c4e902e1d0d74c87ef2d22aa55917303fed312c2f27413283cba5f2624f6d","proof":"f2d84cf5b782bed7246c495f39f5267f1ea5ce3f677fecca8c6d1d78fe2f701ca26bed0d8a22a8a6cc7845f5f7e5debae869e2b5fa54f8416406b384cda016173c8790181bc2844369cd52a17db6491c4546a81cf3b88cb768bc12d476834312dc0152e6517693541af0119081e81044c2b2b02eb9fabd9c9bcd2920eb87c60ab159474663bb0adb6f1386475d93f3be95ebbda3f3a71668987f9271cf6fe80267426973c3830b736a1baa4c2a6a7565496639f95f3d2b8e752fb86684e2e70632b93ceac91bb6f0b314314f8a427fc51033ba2a599aff3b0340e935141404005ae99b1391c02d8b5172af6e939a678da79b780279a61d39a97ef32b7ee0b425a4d1fa3a1cb6bc0b1bbd9b35ea00cdc6c7d5bf1fda7cd9872ecb5149b5e9fc79dc88a584fda526be212a1b8c590a3834874afd66d2d9def22d10a144f1ea306292b2ea8674e568532fc8bc94ffcc8171157ed38894568bdb91d8f1447228ca0f42d22ef58ec81e2abe8b482b114715df55a054dd6f668ed16bb07a1fd0f5c7458cb36c63019153c2b7402a92b9ffc07f26088e4e6e34cafda67b5335da1c972d5aba710fbb6031d8d888f8ac0c046afcfa0881a8b83fe7f047d2167e38535a273439bb838d5c207b082c1f0de5ace4d3c26651e956e02a2d6ddd921df1d19a13da211f87239ece233de4b44250bc3dea4f0e37948d87e1ab8c7bb2f1d32dde6c0ae33cb57c429420d475a0166f810bf1add6f6a8837c4ef6f47e06b7a6233666e876fd28e88cb402be5434bb40464d9257105dd2a6f0c44613a252d6498430476ec07fcd4906a9e441d8e09c91e0c5ed111cdd2488020e4007dbbcebe89c96294a537430aaae6cf6efcea797dfef3528aa3e633a77621f39ddf4cb2957514c086dfb2b952f6d10e5a62cbee6d04d3775b4ded7d42afeee615da5ebf46837d40d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0e4e9f056bd34f8c92776cfb1f73fffc1cffd143511ddfb634c348a8a5e7a300","proof":"7c6ecb525c8406e4b872db26e775782cce34f70a9f18caa2f490e94cf7bce7727e56f7a6f94fd8b22d2c6b099fb79552878f21d858800068bd8d9c792e4ce530fead11a16608d4abbc2f5aa90638044b078f0adc864a02b30903d33bfc7d6479e20c644ac3a53f6830fe8ce8ff3b75310ae60ea9b503bfc0667e256ed58e891dee0a441834f1d027e852e664f6028c7cd89815e38dbc052dab5b92d9fc1b10051c4db07046dd201dd6ee299fbb1cd55d1d2b71de0cc8c18e77d76c2c0d7ea508d17dc9fda88ab9ab8ce840d4edff104d6d7539cbb7ae728436dab4501aac430138cee6babca5ba7dd34cf52427940a85ffc6a25de12d6bae8ba59031db94046b8ea5c5cdc9a9fa1d2a567023609cd9b3a48a55d54da0db6a4f2eeecd19ece070102d1a8120c660e4a6e303b6d5b97f9466e8b69e86435bc7332a476c6cd5843bcad0f62657edcdb4ca96556b5762f26e8fa558df5a928d3d5e75472b2de3e73dcc62dd64c866a218399e1bafa6f129b1ba062709915483cf9fd9cc62928cb64548803a2546ba1d5207d8927b7548cf59566ddae72fe767ddb7ca5abe27e7946d74847d7aba94fab1e0397ef330d8c61adeb0d77de697c14b6617f2396a6f381554721d183d081f0c513cf3f41f3123355b24cddb8db130decdeb0568f649422496cd2d46aca35ed1f7dcf39b8ecf593949bcc6ce13c976f102869ac353384a21d6097dbb74f5ea03038396516c60cfc3b5787058f506e7e0e6f8e86e86918b646ab5ea4af6e4972f9043b2503e26c1523e3e1be12b790be56acde75366157d2bb026ebb82e8bc77b38ca8af047aaab2c2a24147d840b70e59657a016c57d290e4f7484d449f43dc46096fb2c51585d26c2d3f316c6357631b54d203408eac60250c4fc2f226392e4306e40f650d11df5cf493fdce9781e5983a457330fab6f04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aab6385e28076f1fc370939ed37556a7c35fa8d9170be7c506aa74be70e10964","proof":"c2d088796f591e7a5323f3d7da2c78838a2b352a6fe03e3d00b97f2060cb643d3c88c63c0039c5a25d0856c83236362fc8793e40200b33a4e00be3941da59001d2dd1e17690f64984a4cc5bfc5a64880856cd6176fb67de5cf71524013f1f24c72bf3b950722d04826b1977d02d87f5805f8e75481376f84303938d10f7e5246ae036d1faedce62f2dccc1ad042497747b7b085058184d5df657d0b6bc0a460aa755e3af30b6fb80496bb8750a49b2755549eef090bc796d11407631ba10d90e66ab0a5f40a42885bdbf14b965e40766ac7d6847d76380adf84cf3ac1957f50bd421a2a1dcbcbb8c120c267a166240a416c3e4e0e5eb83efadb769d1b9856c01d2618a9f78ea2093ef29ecfa7e56a2e36fad02ec163519a646e7f143972872399a8d7598ff8cafa381f832930ffe07d61a2fae2c26208e491a9255a8b6cb7e2b506ccf503d232fd78b6ee0021437af55914724d66b5be70c7bfdf18793d3fc0db6483008e55e8d1f577b9085db83aee46131397d06169ca732edaf44549b6174e4836a6cb33ef2a82f0079df9edc0e0bf2c66b8ca227292b0ecadc0dd9685525ce320d1a5bd964c8dc0d1bfcd30acb82538e35bc3e9199f0b48665a892a8bd5f3ec98657aa18324acdda4b52aab0d6066102388d011d3deb58bbb78ba7481f7b9a8f50198bcf7bf42ec0f82601d8f1786e39016827a391821015d42ff5c27b7fc4ea368be4a67edaab9641581a1b4e7326ea94971231f2ccd2eb88c464bf064482d1670f2b08472dab2647d6b12607462c687b812f92b086766a0446b3f07b262cfc9156f36483f50ff88f9c036dca714983b06731761ebc934f1a62483b4f092ee0f00088ab7c2dc1fc7e528bcc5f59d1714ca91ccb2da95c4e954c78d9c90779045ec5c6229a26c43249060f08aa0df9bcf0fe86744cafb9c9a1deaa55b50b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"983ed236e03bbe3a10ea15a25cc7c9d1569b0a7e612dc8c5a82d8ff6333f5a0c","proof":"328c0a1cda1dcb71e53020c629947b8c443e8073ecf2b70b9f554658d7e96c161685865b665fbbfe76a0ba6b813d1b34ae67ba8724feb4d7ae80d26b2e59d93f063f2cbb4319842138dc1f6a58f4e55d41a96e43156ff0fbad218c468e9a5c0fe6b539c9ff22bdc3fa49e9b92469dded3a7bc102cd5792bc5184fd2bfa13df2e35a1a4b7bbc80c8407fa31160ef0dc01d8bdfd7a03b46d3a5a76817c4a840c060d1476c556760198511364cc4636ee2f7ba4405625af90c5920ea359752c6c0d354017db4f3cab9f244c08b29217def4229ead014cb4313488e8c090a521bf0b04d9e5f8aeeb09897c6af4c5a02ed6e423fda02ba78cc4ffe170dfd520004c64defaa137b37d143b0dbf956355088a0c24357db878c4f46e50d96670e187910b9a69a3cdcc1e421cd35fa75c0988951f5726b0bb67a6ff22d2b1c93969a5cb6352c1c5692bb33c8691c1c066f81a21aab63bd70b3cb9ddc510bb8e48d754f2350e8445d3f248793557b8ac1d79c17789d8cf24205c85550688d9434d230870152ebea5ec7340566897d876e726b6a42e15a8d61ab4373826a51189b7c2af1f6e2cc148c7884f03242a6737e57c24dc0c8073ec40ca118e2fa69819f4bdaab173eee90a368a8b4433f5c49e06b100fff52daeca0c41ebe2e505e3a40fba11ed4a5275fc96b96c94c3ec63008085fb4a054bf366c4aa1264e67d46a5952afcb96cb09c5e5d69d24f940e532b216226517c97b29b2bb05dd6b0db073553b89cd973b8119217d06f99da89b52b05128837bcfe15d25594752f34cfc2c460917b4c45b8a6408ef109146c3f57ae13c1b8b322c7b4a139b8abab5a970da8934086460bd8bcec2cb46d156297acaa1590a5dc5a60aa78c5d2317b718aa498eb0af7e10080c80d4111f1fba0f7904174d8067ae8129cd43168fcd04d120555f1a24a6c01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"20b14744da13673ab8a3679056b3ca94416cd00bcf6f43de9d7f9ec9daaa0c48","proof":"025000cc769d6745e0f40700aab83e2de598b7dee70cff79ddd44e485a596c1f5c9a371e043edacdcd9bec3dac99495925814365ec8bfe3afb52dd834e53084e78e9c93b2317951b8d63a8ee4036bf712df488df4dfba9388a9fd95e449bca37e285d07f43f9318e899fb244f4c7f88f04df8ce8aadebc21804530957fa36763732e5419e091164653f67db318fe43911c602d429a978c25cbe94d1a3e3a06091995068478480a0f4d172ea94116a6567dc31a9fe75f49b9eea0d9d06d2f840c117dc584c7fd6014471bd3dfe0163ad0a30384efaa329707d6e4390697c9fe0c1254e0214b1f1db2c230ee44adf44e248af6e1f0912467b6390dfaf09784c739a83d6de6991643f57b497130974ccef25b8770164484ceba42d60ae6b7611823628739ace2a6e6d823f0e1db41e90dbc81041304cd75da01863912cd22c2d945a63c99660bf105f1a1a9f91aef22cfd163d8e5f12cac1a4c28d64d204b9f31753c95c0b17914bf1da1e5bc721cbcd18cd9c5d9aedff36f414bbd6a2cb0b128198eae6a1b9dd11da558451439d7d40dee715414b22b1669817df371df0297af27d6a56e146ae7a01d71aaade13fb9322cbae1417f1bb17917aeb7543359247918d20db9d750eb758276df4909ea740a96ba0538ff5666a73740fb05f9a07369319ce19f816907b06671e62170741b0e64025746aad46c963871dfd0bab1a08f4f72023249df4ec45dcae1039468b7b3d0e7d6e91ff4b30768fcaac238801e7877980135cc024acd86a446e680dbd4f8f7954fd31e0200fc9f16f8663cf8e8e07e048f041f19b14381fc7a0d2f4dc0772b670180654c19647a6046872d96cc230db06a29ff5da8eb56832708d8c79d5a95ea7c3d2645ab0b2ca7cfd22d7471ca09c7d5e27ceff6d345261c40520704e258d5bdcc36d8ae9dc9f76cbad0c67db50c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"306608d55e7bdbfca8d052a873dbab1a9b030875a60272248d10daf9a223b366","proof":"da1c3beac6a6c061b57207035c707be7f337af9976bb30855905d384583b70410e1056ef9191fbcbc1592b8571236b5b816d22b91bf1bf28477697aef18fe74b161fe552b84dece8e3d5ed647816059dd396e7d85d49422d0dfe9e33e69d5f0a320018341165529bf390605d1ac3eb14bd00993a47c220112f997ac6559cdb2644e652b554a365688f5688d507f1dd2d7ad0626bcc7f71b726bc6c838463c90865b81de30ecc0ac3d0dbb2762f0f39d65e07fd3c09fbd3fec4e93184d705b40c01b5b3d57a7f202d41376b31044893d835b9f0e1a3a32ada78e3aed36bd47d02b2682e6580c5e7f36524eaee4bc22787064693c9f4772ad580f3375affc8fd1658f63af1ed8e5315d21dba9747daee742b14a56173cbf91d347e35a6ff98996260710ac28518cb74c3243e5a960f786aedc6518968ba40225df8c66942c9dd3d70695a2b658d904065f6cc9ce5397a31f7c81d19203d50de21a088faf2eeae4e64dc86b7f744706dc2642b90b975f94d395b8c15737ae902047ae158b72b5911ccab8cbc861f74e97a67e481e87130227cade3146be37f2902cfb7014130dc6c04e78ed16b8ed9db96b7701ff5e606da35fd14025c9fc6b0e951367616fcfb724e8c5a2a179de9075d5a9fd821ec8b8870bec9fc2d8178c1ce87adc4145283694a8bd6d074c1027c51b778851de00dc7a08e2199b9b72cdd38ee8f0e63302f7fd82d6c5b8a7348fd6346cd5e520314245492cead16c13730514d0ab47d7f9c3ed2aa52ee62382b166ed9fd0baaf47c337bf4902950977b4f0f7f5d1e5b21fb13c28a901f7082b0d926000f239179aa473756ee3ca3d56a4c6c3966a621b42b1ad64d9a15aea149d1e2d93bab7b4f88946b922c286b2b92dca60732feff9e92022803c6613fc7a574b3ff78138d31044f700125a491a648db6fcf234d69f4630b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2852ffedf40a5d9c447159c75f6a3c8de67a769a1df8a7796e2f9bca935d0405","proof":"8611633d98239c6dec155f7c0773f421258aef0729455732cf11717660316a56a2c70e0e7a1bd2f48a961baeba2b11c98e8e8da846a805ac1d6c0eb20265b139feac0cccaece4e545a7d4ce107d50141d0d7d63fbc67816e4ed3b3ad86ae8074fab320c14919b807bd09965ca7e65ee7e52488969d1e379d56e11258a26d4b30b6689630bec687711f2197be1bebda648c8ede5e99f8933db7820a241570210fc97d598378b2ffac0ef63a064b62cc0d8b5c015aaa9a66291809f03cd0a9b50036bcb18e3f3636bf5bda22d4a1fcd78edb52f6c9da40ff6022877bea1bf4f10fac2e2bb811bd40a1c9ffa0967d3991d5915db365638d792ef3a99b05f6ba895cb81d797fc9e08afe601405f53725e4e79ae25bfe6c3141d93cfcce8c9e7315273893ccb3258f2f1ec29419dfbd9bb1e6da9d931d360575edb624b3331b1c6d398816f7728e8c49c43da3d019d2080928c0de448ed4c6de965a8e284ba5b54e24903884c376534b9a6458d2a011e435cc9dcfa1db5d69108f66f757b97aec6c420610fe721be8bf5a7641071c74257f2d1cf1bda516f09e88ff4daf77bc23e647647b4da3e65382fe451132cde31e343816315584eaca897eb5fc4c5987950903903dbeb0167d1acb2f5adef7d5010ac645285466271869c22651a0fc0a41322964fa5515b404ea6935711e4fbd0f3e0591c80d9bac50af7a952f934fc5defa49162334dee8e661cdc84792f57bf46618e79766a13e56ff7b7a664c6a8b3d725d54c4e3c8f85dd0adfa8988deb0e9878df7effbb610b122f5c6effe1ceecafd0982291700b61340b150e2a45275d37628dcee41c7aea829c2dbed3f645b8f7a6db0ddb438f5e50d9c26bc9e35211845c14e5ed33b39fc0bf67120064ac1400a0637330577bd21e9a8cfc9a642f48cd141556eefd841e32e74b5b3c4d04a21e00b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"00b1935703992ca0435edc7372749e840cbba380c0a42f404b45cf87589ee125","proof":"529ff777343a2d68620e30b21c06728c17fc8f63658392180423e4b9c4727d45b217e8a34f4ecb09b03e2a412dc462ba23071dff8e16593176bf12cbea821f07ec27c89e79c87571eb1b8c40e1e20f16aa4d8c21fe3b1d14f6eee55eca81961e4478716c0767b844eeed2c8c73e920d1e057daf924562d5d431fe1efd985281377b427786cf050c85aa355b61cd519a2403a8fb364998ff4f6d2163ca0b9790f3c3547c2dba4aa8fdb33e9e9577e7db25e8e28aa61dda2b0d3f71e938a43cc064781858d52f575aef25c5946829d7fdefcd9fdb8790f1135f3c72711e5cd980e74576b8f8433e3930c179353f8989ec38cd0e057fa06043c0262a71cf71d90142a6077f938b66aee4bb28e3b7f3ac37ec424298eeb1c0bcea504947ca639fe3cc65b3a8b66d3f8d0e581dd30b7ff3019d5c1e0386236df5e9719480809d3d81526765272aa9a4014a88f0a9fa409c7de6f42004bbfb529be375b3e1d0939c51f807ec43d3b8f41119b26a8706b2c4fc7dede1a5a43c74fab686df4d17fedd068c0fc9f7c1c7980be1661e15e0582938c0f9759146367852e70951fb2560df3255803e08d9b28d85d36d9ba8f9dad382239a450d8fe9eefa88787ddca0c8fc54efe057d50f25783656acb8f18ee08d01bf6d0ccb6ace620dfd0b27570f670190c14770ffee0c0074bea0a773e1c2093ebe27769982df57958c00fe919ec44eb03e83a02a60c5486ab3e9e855b1066678ce00453af581c58f31474f285c07f6e61eeaad55d32484aea42853a406866d8e0cfe09f0855a6ecb3b1f835b0e5bb617e4a358ff59de2b28402d464f7fe736bd172aaf554adb07577b24d3a024c1c007f1656ad633790179e2fbc1caa22cf48477a30e740c06668d304163db217a92d0f995e55765e82084546841b40e0f4bf1b500d84c6768e0ef89aa7b57b23aefe0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"06bdfac0e91cae80c4b6cde7523efb437c3d6ebc6d0cad27e123b5c0f007dd2b","proof":"36fd815ab2ba9d6eb00d063f38404bd4f5da4894909e91baef9cefb1181f233d722544796ab57bd04fd47e15d1b38a49a1edb704cdfb424dbd5a65c01a5c8d73aa0368e51999b5d802aecf62cac23d5892413e6c71520424b9683edcdd8b973e2eae70bc6f44f17cb0f59575243376eb89b5e20f9b9b345b46403f7052fe0c6b04b24daa8baeecdea4571c91b5913d131cf1c0995a5818f1accc01b751cd540ac8d60d895dac8caff075c9c5f255d72968999b05372f908ca92437d24fc9f5072ee20979ed482d0f3851a3810ce01fd3b3537628209ed742725239a639fe8d0052cfd2ea034b112f869dd9f1ddb5d25664e41e4f02e5cb5270f3addeb830b6184c0f87e16cc9b5db4782c290b027913355fbd9dfadd0e237ad8a72ce1479f450f8fa1d7eefc4f9a94238cbbc11ca79caaa515e08bb023d0e031e7d05527e2e4f00bd4e792bddde25a0ddfea529f5f9b4be1071778ba0f0d902969f475cbaa96b7458fa726b6e6c3e16eae7cc3157452a3c952d204df6e72980088dceb22d1068a05894c4c020db89ba266cdd0ebcc63a7aeccaa878588e657d5ca2a2909002789c1dbf68951366d64a80f00084e5be2284c2745f38cc5ac921311c402b258b1c1a9d8c3f086071a0a6c4bcd8a46c60fe72f5f15a29cdabba62a7d3cf0910030f6e595abdc1c7f2f9cb98cb8573a6b41a45709f82fe4dbf4a2c164ff380d63b6eba150e10e0de89addae2ef17682df10dbc39ba80edc1488c5fc6cb0581606756d4015828a7a5e600f5867401bfe6a06d19ea758a171a002570c75dcebd6abf19b6a59b6b13399962068e84f7391ad90938d333ddabdf56b87546e89d5ae00b596068fee13e0f71722fc723625bda1b9d9cd5f61c24f046288da9c14f784841005d8b7cfcb0f13f13e7cf6b7039276ccbe3f22e6c86a22d4be2795c4e44a74400"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"149e47c365af7e1a517d3534faa6ec6b209746f28441cb7eff09189e04c3a21d","proof":"669fbf5038e10fc748bfd3df03c5c6876d292f20730b15e5c7bb2aefb0c6491668c4f81432433c1f9a4dda23470cd541de70d7bc15e06a9e491a36e8f2f57b044677d3574cdf17b463f38a02bd85826c2817798c388a1458a9c67d23d2820377728ec82ddc7403002cd8c1d8659eca349878aac2012a442f9e33c2aa40c94a539fbae556faf887ad5e42247ac6640d7a0735b2492309812a47b7938cde81ff0395beb4a50ad22d7dc9f083954bad38d02e4e319321e73c7cf045bf731ad0ab0b3d824bea3671317cbe6c2a22bd8f1ee0e6c2aaea0c72e9b5427d1dc87255b008e27a8889c88e93eec30d45d717b13a6860e794d9739bfb6ec9cfc9abc03d093a5a9946ef08dd1a4b4ae7216e7e54f40fed2004479dbb19655552f90561fa8e297a8ae4ab5cc79831c888cdea41bff0407b2548d1132511d321a3fc41ba809d3d2ab05d98b35b7f896271726677b913e48fb81ad81148aefd85c49f4e813121725e20d651c569602cf46dec911d36a326902f81071cefad1f238e0fde7d97dc52b28e1bef742efb2c6b3977ef5ba42b908d356168a7f311141bbb0ac2d3b8ed4414127a4e0c759e9ed79551fd8b6049b3cfa0a92e726813da367f721afa14c007f0b257ff1b8e304586418f1d7c0097dae698ffcd01e2801e5fae200bc2fd4c5ff6c130345bee1317d9b59a27503e0771709e8e6d078285eb42894f0188a8410076391ae7b665781a7ce564f0958fe134b09bb9f4d45f76cf7a7313e031e66733dc588f55c7cd5c0c84e0ae2761d601547e85399ed1100b8f029872e5471eb915a00e72ffe5fe57aecb32e7f5d09183391d9bb44477e2d7852c2d3c00d644f471991f8aa3aa7a50d9c6cc74f0b2cf802d295fcfafd059a94d5fb156d891e90f0a0000af8c31ca682781482c7f05151b92e48b5dcb8d533e9c3f634d7c548ebb05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e2d45b0d8e25ee48acaafb84ce827f52d45d6093c6b03ac0548c2f2ad8022b60","proof":"baa378cf0a6365765e91aaef5cc974776ac3246847aaeeacc235a470d8217945405e092ae991d0d14eaeec0b987689af38f9cc712b5933a6a0bad5ec6b4e0b4caaa8807b96ef413d445e1bcc53095954723408722c7396bf9fa4ef4bcd3b2016a8f8771da20875b067278ab906bbe6ecbc4524210d9e1f154c8f358c2069e83b83402491417f762ea9ca99253f038dae302113ce04bdbe65a4a760bc92d1470e2e9148f41469e45ad280d4af39dc3a1947d0087cd91286e441d8b643d7f03a071f6b6e2a424f0fc2fb281516c6b1c8b1bce20b7dad98fb603810a3cc2e752307bc53d0a08a46e3ca87238e67d2b7b472ca941e57d094235cbea4f1e6ae01f36734c9fc5f51793c7d4b07a47b9efffa57958a38510bc72d1bac7c3f601d35df731c64adadbb4f5d6ebb65fba0327fae3f1d4df20a80c6fa98e942c58e7f39d21074af1c044808e664c51e312fb624ce58c0c37b8e3c5dda7799fd17333505fb2fc6a5db64251ba4661f2c7fb727650ee6c1e7b9b9c0311b74a1e783481a70d6608c736836456dfb0b1e5e9dd22c35406d7feb1ea05b9690039a1022eeee08b54b62b1a15c35299f5a00e119a1f43f505907339816b469ccbcb5c46634f65a0c354698f86469f6063ef56dc68ea9244978c8782abf358079087d9195349ac4bf0d287afbe0f676ca33f85d6b0dedce47b28280535015296e547b94a17d6e03907594c1fdf0888a8ee50afe3a6851da93e4707e734ac94e6edad457279c180efc0396db1ee620487be0e90faa108e73cb5b1036f675a2519812c7830ebe43d1801b7298e39884b607c7b3d9247419613705192b1614815d909d0ff21ddba96a32724a00bdc59b10b4ed35af56dbc2cc8699c0c577a9a3c7ed0702bb8418dbf00c0deecc86aa6bb3379fffdb329ac6cce8fead494ffacb76116728ef36e20484510f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d4f919675d1fc202d6c0094c9322d8a34c6aa61fcefa4b5cd6ec8fa5345a7a76","proof":"ee0c67e3d1f0abb30a27de39a061c560816fdcf3be2c6853711e10537152310fe0f3dfbbac0894cd3a97195fe14ab8ebfee8ac1c614ebc1b5127d6772722290b18566d28bb1f15d0b426293f0019f39416283f329c3292e7c657f789a936083e9c9245556d9f1879bff755369219f0f141ed49abad0cc100e0a4440737e46f6bd6f9626b88ce5a96f9ad3e93c2f094293b0b29f8964dc15b527cd03e665c1603a502c099b9786ad94dd762cb9d05b6ea697753aa8e6e066eb3c4c3d86e93c90b08b23a9cf85dfd403627cf79ae917b447e171075de609430cbbde877a57a250e142eda2e1fa7b95a6b767cd82bd75655908e72a495f53e8a6cdcf471eeef05167a7e652c3e194cf16ded68cdcc271379f730c3b86531e01fc97ca53d8295970948703bc5cdbaa9aeab94ef09333671ce808791cbde079af83158280de1c7675b4c43a0648d09cbdb3d870bedbd6c63de4638ca1b281549573aa77b009e5ab726fa069de5cdb6ee9bebc6651abd0753d5ca4ba38806bf77ac7527150a80e90122ea9173509e9414e8cf4e844d72417922ed3d206763b81c4070ea5235c5961700984e4e852decc108e67f1d316ea6c15cff26c4d7abfcbb6495bfdabe8b401d30d011c7312ee4acd21b8dfb02441668f5f83890bdc34c25a72be103b25168a664f4b2c6ae389c59e99877689ae973c3935c387ba6a1347c00c07468ba30e8b53b7a218e3e351bdc8cd14d6ac5c1a3427a4846bd12f256db0487d9bb997c4e4a7f6c701daeceaf585eed9736f1d561b5f3d915a1e987a3a59bc062ddc0da6ccf1ec46a16418eab4c3bbd8ff40ea006c92cb1596f20514727c8cf445712329eac72920986ea1ffd134929165fdaab90a6ec2c1f93aaee4efacdb478a3108f7eda0a8a270cda171235d45c8b45f77fcb7b00a83706785c9127a739013b70b2810104"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8adc0e6d88eacf567885fe0970350056fdee33de9194078d2002560d22f73914","proof":"e2bb22a5a2eeacb5c762f78625e87fdccbf47b1730a9234f23dd918f64ebf23a3a72bceb2df0b7cfee17e50fc856fcfe625686c1cdc17564f174cb76814f2815bcb395c1a423bb0ffecf148804768a8ea4d97077118141b28069c864f5b31e4fa67e42f6c3a138a4c112400358d98c16449e92b50be218d11c06069ca54ea9508676a5f4186ede956869e3ac91c45b1d6e7e0b51b2eaee20f65d80e26099410b3542ce051c9910989fc60be551633af8f280254afa12e82767ebac5aedf44c057babdbe8716b7247fb46e720c34afa110f595890552f9668add933e5e3b6810244bdd12c9ae624cb6eb3b1675fa12358c748f84f2399723aee7699d961456b62a00b317af2362e456c4c3cd6b7116fb28bab6b5bda51c03ea127f48aa09d1e0e0e56688846ac28472b519bd525a09c19dd60edd81b56d966e5167b104a44990c3224cffb6d8d19d52e2a7fd7d0788d4f2ecd49d0ec0accc2f42baa54568e541c28e505d08edace16aebb0749d8bfb0b0dd692f4969d981da64bc60df79da1b2272d0a0d11be4fbd95dfa50faa4c8a82fea0f365da12853c5062c6139837c7f68f00563317a284910f3006225c8b727a8ab3d633c88511355cbe7c2cad3428536380f75c2e4a3cdc39faaeacc3414da210147104aa675ff0cd90cf812c9263175e65b83ee46214ea57f18176d2a827c5709ec2b73edd5ed069ee2341c12b5447a2284da53cb2ba1066e2dae03e18ebdd381d4cca132aa206c8e4f53c954fe885c46a266fbdfe514092af3ffd5daababb5e786b01f3c26bc15d06fb9ab79c6c54aca4b05d8c33a2dea254202fc27792898cd2b5fc66dc8fc5f48720ee2f3267c5c2919a0e08fd6961a99f8ed6628dddff686629346cb67f87f50ad4029f1e8eb0daabf6518463c4ebbd38d5d649c5599220e6e5a6fa4a03e92add99ec33e04020a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ac1d9702649648aeb989d7ba770f58c48bf41f2deb8ef334d1373902bc4a555f","proof":"288d505d0c5ebd38c326211ff4781b73963c25cab31903e42098c06c7cb09851d0f5451b6cd65005f5b969cd5002677209876776be4f1af2f8577901ed94af24e433854d7d28524145dbb82e2891ddb8d0f8bb9cd525b703efe86fb41886aa5f44f23fcac045e37801522b638539320f595efa3457a286a6afa064a9bfebfb0930fca28730fc44d2d990d4a72fe3c0d0c929b87622c19068eef7bf3e863f52088bf98d21a9db988a513c0e0d73296bb82bd7d81ee02aebce36adbc683dc4c105bc452f67e8fef7f79362f109ce6e8d57288fdb9831e1a67c7ac888f92e153f09b666393bd3a95be951eb8e81d2401cf554472d711ca94c2f033aa29b2c206b45840a1f7b3ade6a1d5f053d0daa07b83807c9de0b52052a9f249dca9275cbc035e2f57a7a884e9c88ef902b5df745ce47115106cf6e2d579b39b4413853bf3477dcb8b9a179178b63f7dd12b9a72922f6c6ffddca081afbab08c29cb98dfc9020cc625a845253a7a09f86c397454e3d0c15afd1c52d75ea82391b48b293f4934ef0a545f5826ea11d5d6a25d6645ce6ad7c73f3c239f6589f70f6a0f04caf3243e0a55fd4d56857ff5b3925980deb4dc87630421cf65fa484f281a3b84ca9b7799c5c0e4b5bdfc18eaa08d0aa9a4590a0f42b99a676c6806d9b8143d88d9ae6695e9f0a517ce34a2246203b61c58f7fbdf626f22e648e828b113dc63b966b8c4d9ed16a85903810e80019af55f9b1979ce866f72de37fcf59d64f7f72d4d7b96a046de3a3c6d9cfd426d8432fafeebcde10548d42b5087f36dcedaca9540468373ce358043c115c61b947d2eb6ed18bb5073e894eddf193a474ac0beee8d39c3af477603b5013213aaa39f47ca8f3e4a4a4afedc1d4f83e7df9256befe5071b0e310f679d6ccac1bfc35339041656ef69a153d0b0b0a561d651cf80c83743a703"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"522e3545a4bdfceb09fadcdba3d50574358bdcb3d2fcce80abb0a39a3524a97c","proof":"1c37c8dd6e7f5b742574d2f3f4f97f046a173c95f872afb9e265c4d505e67964a65ae9dac92e0bfd1070537210242d3d584ad328939ae944efb4e3b9d55d5437509cedb917f85e936e886b67db8259bcbea73b0b1f394d24e47e6e99eece5425526ede1d5eafde53ca591751ae530c04c676596566771c7187a68c0987de5812a692e5067eebf08391f0d656ec857aa52fe9647110fd6fe9fc7c650fcc56a203821ad9d1246cff0bab3b2a7a3083b713308f0ff60eb466c3a60e1d34a86abe09b7016ebddd7764215b527ec3c501ad5b0d050e2df6fd08dc71d1767053b84b090ad09079d64333f1131330247566df476e3ef0693201010693398b52092f333dda92f532e2d114fee3c4cacaa2e0fbcb2cbe8ca150696f04d9ce21e788610845f60899b8734f86ccfcfd677c6d5f968dbfb780a1406794ed7f158b8dea07414b54975abfca957cb8631af3baff1a290cb98114b75d7b397bc43532a1d274d95fa6e28afd0513cec8aa313868692359b8bf9c4b47a05aaf92424cd7068fd78c6d3ee3c8aa52914fffb882617c647b785f89076fb2a00811a23fce2212834aa37f0a1060c5bc2f6c3ee7314e7d4fd97733622edc0b6485c6866eb53d842335690f12ee4e2dbe7695fbf5e3654a22aebecf06bdd2d987304c795a7a6a6e82ccdb78728c2ff6f3461f02a8336b4421eaff32011e4631ced556f307f4120ade8c6073e236ed302cb225e00447c1601e39f29804cac16ddf2d69b9cdea139d3c80006322a831e7ff441fc84ebb3a802d07165c9b34af110822c1ab2c4ddd319be5170de6795bcc93040b61935c64babe807d7a334c4839c219bf7316fcfba0e536a56fa3414781bc95dcaed42e55e94586001a7f668fa964f7210a95792dc247e3040251e83bc4cbc99f8e3bff9405bdb9af94c7b952cb619ef03aea9e92254ad56a0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"14bc77c2f15a000994ca7effaf73f8f4f092b92e05a84172da511dfe6ffb4458","proof":"a08292f3aab61064e3a363ab069e97c1bb4391058d4740b830e645767c308e01ba1dac2f363cc5fd3b922bf601bd24dd2c641780a0a8c3bb65a4526bb988102e46ed8b9201aae26a7cb219d693337fd0903c3e00f0c0d8805b236aafbd55ae38d81add355d01b4062597f5c36e78291c8e0bc771a2787ce74eacf52c902f06071d02a7e55e8ec0009322e8ee56f9ad3c6e088664de99540c205720dea12b4003062afdafe74ac8e41182e98c84194e9c604e2670259f2730ebbf3fb0c92a33094158880727dda67454e1384fb13537fd42257dde10a74aee68ccfea731f0e009de264e0e6972a508344ca09d72a0bc5d81051f077aa04cf8eb2978c33e753e6e62dd35467ab1d243934efb5f1194dfbe5df41f0a9065afd2c40615b84d63313bbea9bffe99e59de4bf3b57ae40ce24949f541b7f010c5578f6493b2a7056ef2538f3ef969cc8239f308dac6be2493142c288c71b3347ba1abe2ea5fcace6ee7dfc3ba20c5296f49fd85da60ea3696b6712060cdccd993a7e9a9b28e876ad743126ec63621bcd608105a16777bca26e814019148dfaeb7284558c38c5ae06e24a8c744fa7b6f1814951b7bff599dc20d797922273f4c868f1419c04d961007f2fde9b28fd9395d1dd9db8c77ad60db1a267775228085d5072ce1d3bf65aa56871aaa40c48247cb79788df56932c205e326d2a260938d034ce8ad89cb216ebcc173e29763f361cd395dc2f00e2d8f0e5fa5d7e42b41f4ee43f268c389646dfbf64f41e02ab2d69ebbdde49cc6ebd913e137f4f3cf63158b773b6d41def9d549e310a8a9d3047e320fb1349f456323959c36e41e4748fc814da8fa65ff0783256620e10930981a20acad63f688ad3f31af070bb959eb9827e8741cf6de1cb591b052f1c35856bb895f7fd1225d42671366c05d1c3b4549aa289e45036154112c70b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8e05fec746e876bba02f445f76fd88cc5b4d89cc4af8c231a8ae7ca0dec3482a","proof":"7894b98ea63982ed199595be60ad695d2a1e62a5dcbcbfe12dc044286f558a6248d249701297a318b13d29eeaddd2d3d8c77c67306c83da889e90de4fb6b30529495e1c853d3fb78fb3d8975519982c4ce1b530807d8d679319527fd99cd962bf80d3bcdabe5fcd02525ffa4d01e61007f89ed762bf28292344a467464c7d663a07e52d0b8ae73dbd32837160cb343cee8b27a004ddc85ffd4678f0e0ab49c014ff6dffaf6021be683d5152b197dc194093ae64f54a1b69ab96566119d81ca099aef93fc08605e3785038defed83872adb98e935a76adbb6465486f11e639f015ec09b90b2016c54dd6a416e7397b389bfee6b46d66294d80ef76f64d2e0b03c5e897aa463267468a805d02c2a074412ededf59094cf7cba41b0d3321e55027b107c3310dc3979850f70db8b0ad3b78215174a89835d16cec0efb8e0cccc28478031f3125d1f4fd6b8ab384b758834905ef748d4b5c5535c4f67378cb7699e113c68a5ccffebb673f82cf73a961cfcf57796c1616353d0b8b737f2b9cee044541e4e2dcf8a2d04538fcc5c95f603482419dfb30c8c10a71bd3e47a9b62790044640f0692f514bd1685b503f0ec7a965d6a8749730d4662ed3a34905ed1eec43502f2cfde9a5c5971aebae58d0481499df3b4dada2edcf9cc1e4da44da0acba774286e1e411cdff82d918365087f98bfe6c2a87f17bd8dca54cb5233350aa7a4caa7476e0ef47178c2d3d3bf0a902b58a6a7009ef2b610cc56f0e470dba1fac61ac76d559deee5a5853a7613bb8549548c6953b30e5f109537eec56b0d633d427047c7218b6f12aa8b7d772e8090eba3cf9a035d1daf276e9ffe04f2531c53b1e874fc39fb5a18eaea06a5aa2b0647305b8afd74cb236517527fec6c8d22d2b0723bdfb3eb07f66c0e0a932ceef36a69b9849c793933e59e6aaa05692141cec08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8e3d50664b46f08bb503f43d0304f473524a67ecd026fa3f75d9a6266e59ee4f","proof":"cc417ec00b8ded09e5a608b24167e10e367aa1e50cf9813f80029748e8e7df3d00b5f192a33dfedb8999f5ce411b9fd579a95a2b347ce77565eca0c62809c1205679b572adbaf3cf937788e803b96deb08271ffdf8589e75ed854d0f1620446b6623499684b81df8c03f050ca9551324e9baaf458f092c20d5dc2e3fb4c5ca23f81763586f26b5bafb321df20190fae1b636f4fb9b661e514e1ab94dca351f0290085a26d40df7a586da06662d59cd0de1409090b072ce9e297586c7f475230311e7ad0cc4ba2083471262b991e5b67aaac01a7c6a45bd82b393b976023a3e01a095b4f8e670def0eb9efce33f25dd70a7b3df3e6a4a0ec108906b9c4dbad60814bafef9860989e40694c4422e7a6ed001b3e1d977d451b96ce9d0bd835be31922e3b3dfa7fbc711cdee75ebaf5d475347ff8449939bbdf1c53a3d349adc8b2b74c291ca4a32d253c744192472f9187ba170bca07363a34fa8ea023dea6a9f5b8a9814e362713341ed0791817d036c6356cd244af52dbf24a02b2f3ab6bf4e7f0ed5c8147ba2637a80707de25c642c00609dc80cbe6226d49a327af476bc9f5dca40daea5d1eb40a4aaf5ad8f8ea35a2865b22d6b85332011b41d033f1f26272f8f9a59688666d99cda4c9a4d039ac6ee3d3e5cff626fd16eb1b9963c9268d030e35d85da44d6798315efefbd2638e5003eb5666da7983b1e4a45280463d7a440ef2401da2677e20ff201119512d4b001162c54501e314310068a8706bbedb30a0c2f4dd692110dd6da9f97cdbeb80f07b6357ee66f4c4dbd2020ccdf40ed92bdc5df213564a5fd8313b3c9018656cdaaa57f59af2e3edfb5e6a69036f82af777f6181173425a99a7432bebf3da4e6b426f2b7848780010e2ac4bffce12eaa06a385c3dbd6aafc92b00c15a43d521dc63de32ca8122455a367d2335b1b8a8004"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"becebe1b07b6d61b905b1a46845b5e1885517fbb695b733fa4865ee59d86b014","proof":"6a0ae27a68c573b5f144e27c974a02827f6ffbfb223a8d37f13e2a93ecc2b47226919540da0afbcf1eaa36435ae96256877255c2c403d0e989406948e1232644e6c2c318de1ac20d8653f3daf1d25c867ab6f7c65a8697b2a8a89d50041bf3636e9314575660efbabd86690ca2dd58243f76b02809744b728dfcc9eddc41ed408369e3f9d59a6b7c184811194b785893c32728bf2e85e474b026e879aa4b7a02357a971cac05038794e0f940e6767d75f48eaf05490087c0594f85575378590acfd5beb030fb058a64856ddeda9671365a6489d2c9702760f27b3982919d240446157dc80b349d597a5b651c0d6d17974e7281cc996c38e5e98a812452cfa9459a7903cc247ccdd93475dd5a315f9ea5adc504202867e69f0b7ae1b4c844206b02674929d3c7880b5718a98dec134da254a350f91812b964380258e324c4185c18628be9c90a4ade8092a2eb51ca412654ae414ff6e36ea612a95169e2910077cec5e6af705c3df5092f987e7457dab212cc36452146d159c80bc263e0825c5a04f271053e09d005dab3fe3e45f6471a2f3aa2738f15b07bb33da5edd3007e1d429d5fc74d32f65db22f84dc06640c9f181f39aa0b23418a869c6b38b6c59e47ae12bee8738b27d3cc52cdfc63bdccf4f8dc7b63df7711395d20cf7a20c1c7035417d9164640de78b12d4c562fee25cc819ed03d0e41d78142b5ea962a87b55fdaaf4cb9d7ff1db81af5decde3324cf35dbdb98da6a4c8d6597a5624e2c7964c4eab0289994452286ae768861e131897806390429dc73d6a0f42e7cb849efd5b26a6a38570f24bf31fec35182e8604e2e28fc2e7d7b41d3a95aa90f718fa8405d79585a27326a4e4962711764b40cf9eb84c854ee26cc04514b223c65c3e5009c33cb2bf2dc3aac868808dff79225594968d3a9162bb4bf34d1a9c140c3caf05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1c208f5d174d9262dfb5a784e88560afec7134d1e1417105ee49248b59739931","proof":"683a12ff63a947f5a4441d7638b43de72ed77a458c86d90f814bbe69a5e9d237702ebdc5d637a4074031e4ce5e128e58bb4f8a65628c518d3815389cb5793d02a8ec49b3a952b217bc0e8a8d86c35c769000104acf6b361bfd3e9922f293f33d8e4aa9e78c03a800c262f9f371ede4794c6e6c894fcba77533e7884614f2a518ac1e0bb1d6c0d3f0c61f11bb3958f4a5b867ee25ce724648f50b691ad0821f0f42772fd31af3e25938b606ac521eb390492ebbdb515f1e715687cd9c01cdee0ea676ceca72fe1609a8fa329cf4a41081451b6da465b786725b7d31950bde5c06e068b1c7eff951e9548cc4794f96a870093ff46c3cef65fefb06655beba3ab16462c0875e7fa7f66c93119079859f368fead715ea8fe3b99598de8efd8778913f2b230f2e1a9ab1108bc6d886b03bc9f0bf9045d359a1f2fd82f58763f56ce3a76a4313dca135abc059e88473bd8f98aac0f5cb94c8596febdd48028107514634e96e28c933e77dc8ab50ad360d54cf1536bbca571e14ba69443f3d07f6e5b111029a7aca7ecfe9ec3c853530b2195176fc0d29d94c8f8e9355bd1d666e33e0850fd67ef43572cfaa7951121e22fe97fe1a5cf3de63748dadab1c551a791a31d8ce3f080559b125fac79ca2dfc082bfc870355b808b11890ec098fca2023da09d86f5fbb1102e6a78f89c50072da556680bb87ccbb7c2383337ed460f3b96b03ec14478fb95615ea8a42ac49ed498a372518a0135e135ca928e6e01e7fab431b4aa353eb6dd9c54646799916b651460c2fb50e5a4db5bd971216f4c308d5cb4bfaf993f173727982ef3062f21cb2a51c79a38b4c33a85de7904ec3bbbcb8ab43f4d82ac147b2c9afa67daec95686b745495975d281e91fd4e149e102e8e8e108af1e905346be73b39c5a7c5f0adc6b114272ec2a285c6695b15c4d942462260e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"70dabc9286777a4aabf6998d125569bb6cd11162128480c9c6131cb860d73e2a","proof":"4c21251160b362ea177c52580aa814ac9fee5f51712f661cc2f93866b605b312feda6374075d822810c7f325f51c3b20412825b1c3e698dd16bbffd1da182a2dd4522d1c8e01e7b1afd03e19d86e64d134e1562a064a23a1fefd7bcf92cbae75e0e0c04f640bc6db81cbb4b7aab90e3ce93deadbcdbf85d3484d58d42e7b2277e54e34a1cf8f42ae3059cafb93296e88cbbb7a7f41190830b0d8d79c4923a60aeb91ff84945f1fcaf2ae04e7b80598531f3de5758ec53bcfcddb17c1ee069f0bff289d5a88ca943908b0a0108c80680f992322a5f676febad9c80f6ad19f3b0b9869f2c38d6350c116ebad0ab42dfc97e5d13cc9b0803d8797d079f3011223069ad89b67b96146586a82da1718b113e5fc81d4475ddd2ef66429531b00a5703aa2735aa827db6d5d4c1d005157a03f4d6fb74950df85ebc1eb95d19ffd2998773c60308fd1cc0a3efb1d7c7d8e60c6438d51b1ddedd8d1105ea160eb198d416460a2d1f2c65959343403419b610ebdbd065ec250288ee65de4c04606de709807f8a01c07784ecd491d659d28af9b17c15784413f3ee0bde9932a5c159cd0c10b800ad78c2c893cd3996e2e16aed4470eae225081c22f705418322bd1337c7f7af460d5540dc7e155b0fba2d4718551a8e4db60acadf3fd5a88e17a51e5914a3586af0ea7756a94820b4374a0a000fe3661a521cc02b04b04ed6b6c913fb81d01588760712eb2f702a933420c331df701318b5bf578f434db3d194784bf4d656e202f5e58335f972e9d7b83b0cc29135ea326bc45c04ceedb4b842ea2b831ef60c855295e8fbdd4f0a26a07a0c49886f510b747dca35bfa535b9687f2abc1272fbea4a82db675e8d1123033e03d883e3d3d1d5fd67effa5cb1bc72e1d74417d0d612e270b26bbe026e495d67850f1326ec2aedf74485737e26b4020c4038f3509"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e47565b6b68a91b4a73cc7c8eb622d5e185e24c1fbeeb72e18b494cc21c36c3f","proof":"6e7bc3d9249f884ce27824538f4704fb9c78552750f6ccf1d6e3715229bda634045147e37f06507f43649333e894b57a4b396202011764bafcae975b920a724d3a27874915222d0bf05301b17b82c9ca8b3df5a1793838b7254d5e2c20ab085e80c74111fce097e83264eba27e9f1ff3efb55a383b85a4aba3afb738d072e922a0f3f2fa1806ad26eccbf084c15a48892d3d6fb07650f1010c38819ef31acc0d27a15acde9f63d21e58dfcc37ec9d1d42c5d06f1b8edb9df8f6eec7561db1a08efc8223b0d81796c6e15a55a738ccf9c88c6cc4e0de25516d69877285c6af00ad8cf0772ce6b0c705900faa6db68720a43c971ecada517f98e21c244dd67ee177afea5ab6309657df4d88c5cdea99e16872602f7bff165289e056511846317315e380b28a7265956f62f08b959304dc461b3890f2009c827ccff83314940f84eaa6d18bef0dc3fc04a10560c213eb155622265fa81e78a63af22eed6833a596100b0fe9833c6a00f59474a09a4c506f5c8c044c447af60b4485c594411f8d35f389c22679c1f74a7a65f51a595cde7ac6e4fd50817afed8f7471c4c97cd6990074a3487cc20d37cbc42d83b8f51efcacb721023cceecc571de96c41afd43e64f0a8272ed9663411a4242c25c89c006e1eb2e2eccfd3b668fa2bcea7a79357201eaebd86f8b4c8aa616b3eb3afeb3ffacf3d3c371028fe148e9abb0078aeeec5a9a86e172477331115d88bbdffced0548a2d5dca4cd8c2a9b6f23acba85a4cf1bda1e03b169739b1ba75dbfb94b99fd59de6cdcd4c9a8f50a540d40238115a37108e8b59336892f2b1e151c25357e5a955bf2fbefdee974ae7c532cd117a3ee7d3bac92e5fbc047189d443cd592cd1137913f9be22906debc59c7864e92d8940efe57df9c9dc5840fd7a43d57ab6f876decacff5eec5ca2e5eb58332470b23a0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f027f12b351ffe647dbce9ddf44e722913a7c03d1adbeee61fa12c22be63683f","proof":"1844b167991640d7daeccf3df960652c9aec739933e5b72564b0d4ffc6b41a54ae41c5c4a88e497d175faa473bd0de0e4f46e683a4b393b0cfc9686867b6a338d64512de39a6bd72a1868098f7aa8504f75d541628dfa0c0f92acbd4bdd0425faedd5b1d563cb1754d98f062f08ecfe531f47683aa74e7c8e4f23da59dc4ca1ae142f0664930d8b6cc998257c5ca462bd6ae4abfe266c2e11f63edd394d33f0c2f5e9a189e7929eeb42727eec414eada6347512977be3765ad37248e75ee85055a04f81e9877f9ecb8870d9f6152928fd7c9bba1afefe57bfdbf414db9b8f506aa9cf59c4d0b7494cbabd47372e16b6d032510aa029805c9bf53ac94d9f15f344039cf2335fb2c27c6bcd2d5e64fbd0eb5eb4b05e00b98c63b1811fac57b1d1726cbd04af5a06206552480e8f938f83d930a51ce971047b5026bc6693f52d5519a119a385f491c5eb901615b367666dabde5466dd2c1bbd5e631e71ecf4ec2525865587bc66588d5c33baf9c26cfb65f48cccbc78e6a0fefbfdfa2cb44e9d51ada8760dba097f45026cbd0b106d881b0f24237a23558aabaefb3ee23d50b1767a423eb4dfd7527edb3e9a3e3ea7417bda54a20dfc80ef317037e7f752aab7b3cf66d5e93d89294c43f7934ee1819c554442567f2bc40678b2b789784c46e48755c6743ca3d00867e16810c6a0a396c0577319c3b28b6d000b1f6e064002a3568f46957bb3fef66530881651b053fb19d94ffead844cb35e75bbd23dcaf7a676bae20dd906beb9093a0a0368501be63801030bfc2c029a9840b4ec386ce68c82cfa52b51c791fd8022a33ce0269b6f37120bccecc3bbabcd306fb3aae8961285bf987bfc78f95c4472c0e3464a0ff320a87d25f5f2566a3e8aa57ba76ab6a2803da18717158d26689f1f63f7eced2c6f097580c80ad4553ca56c28c16c9604809"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8ce647d773fc8fd8a5e8922f20b93430a7b381214abe64e7cda0e0a541f6c414","proof":"c4e4facd3df2ea7133f27ec5094dfe1a509e75485ba5f21853aed80ead3693211a2d2d224d70b7edaf9869ddc0ef1a61d3e4550193b9fc2efc8249a604f2c5339c422aa059087b2b9d5317a7acbcb4e6c7a63a01a7bc2faac5dddb36fa24f7601848b96679da4b40e6bc1d9b20e635fc08eb4809f216a54265130751a5950a0dbe6e013597428b9bcfaeb9166c426ce2f19662fc63af6a1d718bcb7000df8f00f8a26e5f0ccf2f1ebcddde232de4f996c64c7e2960036362efeb723ecd60fd0ac47e6eda2277ed2826a31f3c096beec673c910306421b3faf457dabd4df5d502f66f14d719abf7a0947bcb212bde3f1b897546495d46630eb11795be05fc5d33aae80026a88372cb56749bdadc03d184ce971320aec28c9bf6e6ebdb680cee09bc43ce8b88c6f3f03f0c3c9dec68191b2538253e173e9eddcc7435fe290cea51304a97fdb0e10a8c4c2193e824829beaabb85daf86c2d23f5ce8576ae3073b4aea13118cce8b6bcef3e7039e72248bf8ca9de9c1874426b795da73c8c14c86261a8d5a7b9bbdb93f6eb0734f66a9dc46907f7f5928fc681100db2d35f8fea52802fc2b9d5c298e8b833fbc077928e30b1707b3ee3e412b1112b085923979006bb24867ecd1607f9e2d7542a6161824f63c71c33a263ccee04bc856ae44b59a303c0adb9937dcbb8aea1f433339e53f6d8a0b0e612afff9e4c0d4526f166104313a11e32694b243e4f61c0cdce534ad8c156fab8e292d3415eb1a03429982e51298e14a13b37414a3917ccc7a34f737a3e1fd16375302feb145900438d2fff75b3e100149f1860b30710acac88b8fec068941cc22155e0f066ed55c7a1d8fbc7b3f025e17d04f7fa023ce897fd2a191fffb2d661ea4bf6f81051e72a9c5f00809a4c4b3dfc9bac84eed1c0bedc32f0dfd4fae3cf5b1fde17248e59ed979f9a605"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0c0365f4313a604fed4f63cc42bbde009d23b6ef70669840fb1e2f1d2f93677d","proof":"986500c24cddb874f61ea78c588f7dc1ae6984e2275fd96936102757daff237ff816db2e5b89a1d27152c7c060d7e4093121da1bfe1ebac773a828eef3052105ee73ba97589137d2c83a8342428949656ed50e49c00205397773826eeff63b5796956aeff296f53ddcf54226583d86b401225b079e8ec99a31c0bdaeba6f2f5e657a56ee74d419f673e1cee75a0ad5fb58f9469e7cbfd0fba7a8733e7fdac609fc8b2d6ddb2b6b0a635fdfc4b2934cdada86bc1abf95d9e99a90f73c88dd2d0e785f18175eb4bb7f9d2718b53151c0c33d45610c11ad3d07643edac7ca8328086047b79a3ccddb4c952f3790e66ea0accf70d7373a1adc32699f5779fcfaf57950d1a689d961d6dffc43e5c36ee612f4fb642d3a048cbc75af534378301a951ee46def927dce1c3ca7c832bf390547e48d39003d095fc9feee4d94334748a4780ad1100e5e578728ad8a1ac6b54992ab84633d700c916bd788fbd79a6a05006580d3caca21d094dcf1f4c95b8d734d66a129ea3dcdb53c9095be8a4e5bc0122030be94b379711e60f7b31a4d79c83b28e2947c184b7a42910be9304764e3202a428577f835b736a8afb852b0b9172f74772f8140805fc2774ef0afcf524c7b2ab0b0f00fed75348488cccd023b805ad67e058c1fc2826c5c170f494dab0fcd3d58c100bacade83d4784639e20d1a9901e7c61ada00238b6fd3fafed382a4141d28db6cfd062e0ad5c10592a88fc9e0cb75b6531d899583033bcc35f1667ded1e3c789b45c9f8b91ab93edd7784989532d87e7050ffad5e124438f8b4e24a50059cb337b2dbe7788915159575e4fd0750b45f80aa199e984b82a3203cae9e8d29f0ca07313b2cf50b6539e6da61d38382001d17461e409f62e7238bdd0e3e010356873c6d13dd7261fa0e1544fc4fd3251902d9e9f09b17dfd5a5750835119809"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c47fa3f4111910c3ccca923476effbef09217f4876929cac4edc19421159605a","proof":"a26bc3d25ccead992020810168629750f0f8f57f5daacca9eb84cf1fc5f3b744005f47b12667174db3bc9a75af3f6b0effdfd5a148eb057732b40e4696c4bb5d746998f8810cbb59bf4a9842a26b2c037088b4b5dfe6b97640e4b084d6376209e4e4f3d6f66ceb620cc3ababb62c2f78744d2141e76ab8e215f8552dcd7a4d62f0820f372f58ad5a7f5fcb14578b0f5248cc0ec1418dce311b3e219803865706bf050fbee39b05f17aa5dc773014db529e309c0ef8235a5105e185477460f5074ce3075fc438f4755eca237670fe51d6c641c2e1369f9966600f8a46f7e1570c04e394e14b08587b82ca1af424f4f7188f57d9674e6ea8e52e8b027f64958e5cb2d8eff18033fa62209445aa2e1d426ec8ee685033a79ecf9fd7ff5d85af8e4ea2db8603c2b68a72dff425acaa9a5d830cf7e57fcef5af5fc5cf8a6eb5c09d4520288ed4fc67ebcd577a89dd66e16147b0a49f2d218eae07910cb5281416824ee0353b8cb2b97cfb7871bce2fca22c3e88e0ab2d513bfe2eb92511cf203729301e1272618f62dea4c0eea5d10b2cfef56419f5cd179079b3c33c472d6d874571f6512e572546a0a824bc19e74ad21a5d87060fe38c75d7994f1b8ab0a5879b5806d3fdb029be8744917a07241765f4c6e8d04de3c7fcec93d606a72e7a8ecb2c6094fe10a77a04de370196aa5808971f983418cf8579130cbe93f549e7a67b6f3252841d8b2916cce4e522bc52dac3f290db384dd925a63a0f16d02073e10554c8bf7a552a6c20b0b0db4399eaddbd93dd96588f1563435cfa1f173cb462587c1a2a139de1b2429275737e1fb6e26167d77e3cf9b7e16248c58c72449a7eca4230734ba6b40f416b590947641650a2ecd2704170597fa30c6cc304c83b32fd0ede27c9fdfa41b41aa69a47aca02b3323c0d2128e59b7cad0e0c61d246eaf0a0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ea0e60de7af0b96c095793a082b60f30601d5203decc1db1a9d37e9e8bc2e865","proof":"d80f92f868436291a8e51fef7e4ea9db5748ef77655fa0b359873b42fe1fed215070597bf371c420110433041d584e40a8bcd576dce36eb21a4fbbeb3c1f896354032079bb1f8f58107a0a207dc9b11963229e10afb2fdfdecb6f86781af7d40381f13f6feb3dd22335c294c32db96c891bb4cdab8d1c41a349c4a1141607e072171dbc10b97abdbc3a38ef8651ac9dc1469d02246b26946ae86d3ddc479590270ae3894bc3d8c4461b750c7c4ffc1cff993e7d1afb45dc9e3b42ef3de13c807aee650013ceafadbfda4dd5d40b70a3d62501aaafb365cd32a32f55c230bdf070a8b723a0218f5adf3d2f5cdc31bd2982c43ecf1f0b23d1836da92782440093cd47443b3b82d0da20f6cce05f2764eacb803d14d870608b296409bee2ddfb70e9c292b560c76a942898cfb9affad0eba04153f7782b027994e8e83ff7770cb7f323e5303aeea9248d8c4ea806cafbb6fdaeda6ec27ab1483d577822a60804a5ba6787cb5cc44143a83e152c6b0f9942318157b04656699a0b11a4a48a67ecd6c340cf4124b4e57391aabcd65bed9681f373dc334e7aa16aaf81b5753cf0b3d5de62c5b6810eca9ff0ffd2d08ef0c7a2872645817c4676785e1caa788af367a3b9a40d98e69ad5679cb3fe1b3399f8c64d7ba00c8bb56267331f29ea7fcc84b28566796a7a4be95cc76392e83017f9d2ed4b7e641bd8a8c17548968d21973b01d929279e737f12d855907e952899929ce09dbfcc74c9c8cb23ed841bdec797e146a5be86cf0d6f62d1e120cf0edfe47bba72e829fce91ac47cba67d223152df2718ecdb793749469dfc1edc4da69cddf49c366e04f69d7509ec1d8c5afe30d2199a44d382dd85dff9ce9253a5027e9bd10f181d204c9d0e9e86fa5329a3036b02fa843f057cfffb13d0f0cf7fda4186e80f62e0ea40e82535e0a12535ffecfc01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d4a9b011472564faeae5f40c05b44063b7dc18619f755bff7a93ac05dc2ec83b","proof":"aabca6ccfe264db99d9fe3d0975b125e62253ca6685c049f4aff80beface003ed00bf6a60f3b5a68f41dbb5957589eb4c1c4621996c9bf3dcbf2f32b7a6b683af0532011730bd926f8ca03f1fd89a84a3a68fe575e5209b3399290eb33f2f33aa87a17ad3f9f00cc581177f49926cd55a8a616ed1eeba1db2a9459502e03c023800ffea561edbed1b946ac987e3e9928798db08a399bf103614d378200c47205410d06d905d4c7961fe23d9747e1c0d59ee9e1d40d6374b8cd221303c7dbe20b7af86b5b0d226adac097c82f37f53b495276f674c1627527b02d93c4415c2d052eb526f39e06b2441635c323558d1e80fd5ba0bdfb2b9e00cc57de896dbf1461a06a19da4a372f18fc77ba9e510c350277d644878c31de73595c66d7b66cc643b821bc3f343c946f8376100eaf2ec501a8f4141d3b1a12ab97884b7ef05d7e0c809253f20fbc47596393de1c724af09693b318f1ac3f6992e01c94785738d34fd497daa6d0503cb8bdbd37b4235226f2c54dfb88549e27d58276878685590255aae8709f2d450353e7064cef7fabba5f932966b8d44fae66147d26309b8c5e55b0ffba4db139cff98cefba7902bb7d94a12c655449ce9035c2c062b9e32759556c55113e55cd25458d1f5cb87dbcc8ce2f1ff2f104e8f624913c69605293807bc8231d92b0e1d7663ef4d815892b06fbad37b273852840d8fe70aaf47413277f860e085b6d7c965aee6abafd2542f61262a55ab025f049f81a08aecc63611e6ee8f8f97f1590f112fa5915423827148963f1844328d2e64650f51cafc847750e8a3e852da3cb08e27070a896d61fdbe56b004f0385b5f4d66964f7d86b3d4927bd6de4ca17d10894a4d68b8955083a243097c2a6644f471c8368068e0a01b0033f9d20f6d31d9e71acbefc25d08fb5075cfcdc9c19b5972dc1bb8984d2d0570b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6e66f679280e56e22bfb05cd6969f1c58568f16bc2a001f5a0554be6b98bee30","proof":"126b17fb9a852d3d61e2a2d8bfb5c5832d326922bf8e2eca9a1fba68d4b68730484d6dac3cb64011992f9c67a1c110f25b62e0f1d95af363c7a6f35a82fd5f5d6e223556544729e00e9da6ac0bcbd495b28ced2e833163d9963681db66d74a2604acd3f363d9f8e5827caf69071aa7a9304e0e0c3caf586f2464ca1e8be5e63e882988105c8a20649fe80132bbaae093249904a74cdb94694a3cf64efa514009d3d0da788836b8f88be0a6fd5268c12c1a4b234b7436388f1d6f1f539d2a5c00f9cfca06ba10681c196fe5c472374697b218516fc000b501dd97f34493509d09d8f26d9bfff0dde31062479fd874f62bf711a892eed4da35414db5ca9bdb6338d2c6f510895da8f4a383064bf3692fcea8e39940c29a8a808ad00f00ff52bc7cb0062d70bdb65a5f9641d12edba484c420fa8bf67ab217e580ca61d932fb2b17fa2dc9122576e2041e5b64871f0c83beb2b3183c844294c4e412118151ff1e5ddc79c20aadf70fd6c18bec9c9c7fe7c49142cf64649882643a901690152b751d9ec007992963ebcbc618b36234978460871a4262aad88d893622271d10bbe601ba56b71331d7d420540beae939adcf4af2cd5c93da0a8f66b9733b2fee08ff7e1490fb65a03e141866ed1fc86fb004d60278e176352b0d89d0877db6b56fbc6f3a17c8e003102e1c2af25dec96e7a4208a3b8f29aa3aaa3def1d9640fb9b34472613c8253a4024de31256788192778a1e7cf36b2120540c61827b2398de23417aea4278c7025b3920bbc7eec0b2a90ceb4779f4a81a51928eb4a6f156d6a505b94a7df9d70dab33ed3aa295a6d511d380549edbbf664b04b4e3408acf68fab40cf4d60dc341e4e3c969fc83e28d34b66a8a28b849fbad04dfc290696c4660c06ed283ebae67d2f7d838236c038fd51a03cd3489cdb903ca419aff9f9cd3c8301"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"523d3885ce5582cc832dad5f0c472d7a4afaabb9b76f9f7d68099554bd6d3f51","proof":"a0ca36f1aebc6e27c96614ca0e0f7a6438335afdab7ee21047ec2ea6d391932fa69000d6557c38ce4dcf36490c6db3bce9ad5cc164869b16976f660b14ee336e04d47526d90646d5eceefb0a2bf7b88b678576eed7226624f1ec77920666942564f4d3359832614eca78ef7595427e5b462263f17dee494a61cc3dc05f0fbc18f474162450329221504ad81a961a6b06100d3ee143a7dbddb6cd79f842e684094157ec15357b0018fe5f643b0db254c09cf58e4e838b53fa69353c708888ae0e941f08054a501f4d93ddf7893424257ea2a2e4e599354e0e937e70ee37433502baf64f3f7d7981ff2de5161ac01fc5049ca0bd64116f8f49dc98cb008aeb5249ead795f69b81b3ce174303b94301eecd0162c412856d0fb3113f78ace884d54e060caa371de42a90685979e4827f45420dcc0fed6276693fb85cf357068482119e90687d8ffecdbad260fc441446e7f9c3030555375565506553256634bc010272338e3cea2446e2567343264604f10184ea6bf628aa1ce0bf095ef90c2a026a86d128f8bfb4fb3e6b74001f6f03660c50d78bc2fe0180d24a3cb4dad5ef0e65ae51ad3bc2924baa950efc554d55e3171cfd1544f2b63436f063308cb93adb36962032161dd530e6408cf84a7d4abb41670932dc8863bda46de8d23550bf716c1425389f7aba7b0acad216511e89651d0ab8be93df958b8468ab4a0eff26d55586884d2d658092729a03ddc8c9a132a45eea39cc8f1a711de240eb537891b82c0c7683a6da168d4f15da2d174820f65cd92689b5c624968560e20676f0966c5de84fff614d609b9cd3549b9aed6c931c94802bd5f5ad662cce37a8caf6b005659fe97d3c1fc65aad189a5c9bb70c5cfc13cd57e248138e125bceb9b2a0b2800bfdd1b1aebdd3e484231810b0b5829e2d000054fcbf05ce7430743edc3b821504"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1a4f5394a658c96d77dd971dbdf4123fcaa478be6cb37434455314c076a7185a","proof":"fe5d571cfca5602f33f15f8247d7844079ccd8a9e10f2b78202afebdf055b35bba9c358f5e1413276ac691f3db12cfac98baf41f5de0cdbe73ba0ba0e29752101ecda3281da81dd567f414576a575e352afbf5ce3731912c9078bfffce5499210c44ba27d3e4077f221ba20db020805c324b79fe549d47c080f5c6bb84ed220af9f6684a6a81c50a4e934c122596f6983ccb5d6694f26dc91c1c4e767275490dd2c5277589b5baf6f0076a5e267f30baeb977dedefa09ce289dd78ee6b47670911be040eff02d1c0a6befcad12bf1f86dca257b76322307707e01d432dd7f10b88c36abf996ca0179a4c703da43de7e21c816601d48e1048e2dbad2824939c4a021016683f2ac329f659a427dcb9a853e9044369a9df1e63dab952afeb098143103653b92b8364105ea11bd637168e3e251aac1f936e038a6a91ce0a87d40c076c3ec02ca6c3c642a7f6c58c5fbee50b8717f53098f394cfca5ce6a465dd5f3ff48275b505ae40dd258efe6010e999c95ea232b809a98d7e30e3c2691145b9549edc4106b3e7fe12fd275e0c4079281d6ebf0780fba2b6630bb390457786ea66ec168e5fdac44fa271ea3ced0eee10f43e4049f735eab6e820d28fa4cb14c869a81bc23f677582c65792bd7b93b4d63ca0425a12ff81a60850d00bea44868e1fa8c70640aa89a8eb76738daa657434e62932885a8ccf0c74dbecc85a4c5d55210a3a7922f0957d64f0cd94a9430ed8e264cccac6d9fead1f77df0d0cbfe34f57f294edbca96c9b2189b69c40b3211be90408057e7750bd39aa3774e7d2d44972dc641e749702837c9f673a68c7e2a00ccf0b63f598e964b0df7e97a9819a37224e5a2785e94a8000c72f02f1dccc65129f062a3cab527cb1c8709531bfb0af05cae749692b94472557429c7df8ea805cba079057c09454d2c76e2083b235260a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f249d392e8f15453e9434edae0ede6f3e133e9d02fb441b1b06943b123d46d06","proof":"ac9c3a1b0b93e911b32c45c245c83ec01d9c2226eee0ba54a7e66e516af1595a224c794d68a4432a072d11cbb50ea15dd88d9934f843e6858f8276a77d0e283cde98ee8c22ed07c8ff04cf21435c2bf5e705a137314689684c9a5231ea0c6a73043a94054874d18ea370b8056b0145221f2a974563add17cf9a8cb686777d3115c885bc24482bc0123ffab9123cf81d78ac3146cc793f506802e2d9b7aabf800dfa4c280e044aaac98f7581de0d209cd9250a6923cadd2ba7a397a4449fa2f0db217e869213cf60c8a0137c52a8c6b34d62a0255aab76eaf0a3e14c3e7583003789749b3e80e38b883188fc1528d89b640908b01c0a42651341b7fd212715e03a8fe518d3fe0abd91497c1d5984c546a3c919c0e407272b1d404fdb8152a8c6122af8eba5cb490c98158c4f0c70082a99c30d53e72ef5aad33dbcd4fc5a8756dda2ec874bf163accc79d3464ba18519dd92fab6a79ce2ae03c970cbe2d5c37552a9bed6b949dea50d3a768e52314ff53f299ba21a1990c77b765ad72d6df8b6e40608e8fa4112c7063f6363d9a53fca675f99b21a7ec4b8b00e1fda8c6a7d21c30ad5033c14e5e31ef93f8b6b90dd0b9af088f6a5fad377536997880e6608713ac7ced9d5f76417eacab073cca483885a541a8b2b65af756615dd18d945f8165aa1232c6b66a75af9ae63d74744a4e27535bf5879d8c54cc64fb60fae0ec364fc46b3a299f1916c2152776fb513993a7e4fd46c038fbc826a37f8336f86bf059e6b228b1627c889f3e8ebc732ff24257a3a70ee6bef7a32fb909db1bbfa32e05066f94d0a6b4c9c3ff42f7ae4860ca99da601ba7b43481c702dd96ab897f596328ba904c1613545aebecb3859d08d0951fbebbb0b4037cc4618e062c4c9a270335def763bd539566f9cc770841e8af38caead8d2dc52e75332e99f99a6100901"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5aa91da2c075c403d1822a9a45ea9d2708701799684c487c1e4ce9ca82455214","proof":"36c2c7c0ce3570cf9ac64a9829be3edfad97215f46c8232f4592d73bdee0461e92af481afd49b7d22f54b070838567b76d2c6bde7699f3e3742718aaaf0ebd47f0072f935c65429dcd7ebf2ed148a57f314f2196b73670f9d190d8ebfa2506749491752ffdb1ccda03b857b6273ca29648e2ae8275196a883eb80d00468d3157afa53f535c92965fdd191e3e280683bf2cd33dd01e36c113f9be77e6205fc6040fb976c123fe604ba615e799ab406739deeb99a832f096eb2a2503ade1081d0f60d56ced023e6af6a33eb4c3e68b1f2e262fd8b8560b3455d07b2fa5632d6c0e781c3ec3ae298594c91cfed0c6a31fb94afe1239a425b6f7b964544ec8050f6556a9bf1d91b56e7d11d4f682c448d5e1b7bf4e71a7d0f0abf145ae2b85bb4569fc0da032487845f6adefa49a005ee15cd16316f1cc0eb2a1c9ade4a2b4e7ea669abd99a23bf7de2296086445c399593d4e2bffc32dd3ad589af1f73fa2228873766616d5f6fb5828a7a80cb24422e3741d1b00e91d244c057324aef3e0d0cb7f78bb129b7041810b2c50753cafcae45ae6df7b5448a8c827659ec60ea8d3c96e5ea4280d87189d3d19d6a60a1dd75fabfa53c48c7f43d1c3c9ebb6860d4d445f3c583e90a507a254b27f0616be10b5f288802000ece30d350587b757429b92200659ff72dab53ec1538d0282e907bf6eaea6b83ee45b0726f7d5a4baa306a23b3ef4cb50ca665c68f45d49871e60f6cc665e31e0f66425f4d8d86f6a3b9e9105e6222318a810405f3cad7713f1f7e8f6f676d9f8a08fa973d513ed92adcb525140d56915205d9c4362c63d17f358c823c5bb803a18a2afff61c5afa53cb3717c162a1e8da5188fc6079cc11c13053509c5dcb5143b4a6581161011e7d1b2a107747cc0100865d5ad93a8a2ae2abaf870e1560218e95d6584e8f0788deabc5209"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8629fefcb872462a9396d97280989b4686227e4b93a8e19cb64b433d09ba481c","proof":"78f8f226476f101d2abff748f884c5f174d2598922e6ca4f6db45a7ba769ab7716d6e47ca28e45b9af7743b077a41957404744872a6f1edf525d0182fc7c800bb437c647f9a8237b9c2d289cead148169c19454d8839f9093ab2b2fe9345fe03e21a7dc0e950ba4434c80c08a35572543b6d5366a3a040d04ab097d35f4be846210716673b0b77f6753ccf37764744af2a67f623fc7c6092fd13380b73c2580d2f2bb3a080bc573cfec989fd0232a0ae25ca4bfcbcb64ebf25e52383b2f3bc0ab5bdcb2aac763a4e93f5b6806b9c5138be1d611ee45c72f41d31c4faf2dc2d042cd2fec6d9ba89df8c4f88854db8a2628d0ecde019854dc5674afb7e3484ff06be7ff5054962be645b35d771711e903d2d846cbe7d928e7c5702ba689a145415a67a7948958a49fe39f2ed4ae2b5aad053a81c5820946b0014900f05f10161019e06cb85a13b2cf54eed4548269bc7198150991dd838118b0c2938de9fe87d4a9c7613315367934403fed36ec1fb4274bb4a6aac6bd50ff2220857bbfdb04e3d721ba83426a83ccdda7f82b8c8b50919a63550c0b30a4414a465f5ddbf6e3f6120f7dd992e02b58dc853e28aa0fa1e79190cb9b3ff80552fee38c7b6e8a69c10745f9e5aa04c03c41269f5bdc5e3d5b152f46b4289c81de0556d8f9790da7d3e90a3c135c9101d07c11ff0e8df4957755106d20b25497701912eddae02a805376ec9cdde6a62697027fbfd9a5e4fc1ff5ac2fadb6a1a86522e6064d99ced870dba4762347771d2dfb1c24808b1033296f86c0574352e290b5892d97d5d042633ccdbc4ec78e3309446bdcb88d263a56ddc7a2d31671029090d94fa5f75e2f15b2134f1cf9d42eb4b8971a9900193850113ab3c64192ee106111a96bea7c3970a9c9ae438e7c40797c244eb91632a913e367e73b20f6affdba8b2dc8c19c4d504"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"22ee20d3e9311d14189c8f57475de231b2099e712b7b0f011a37f2ad3649eb72","proof":"aaeda50be92dea2f12614c0cecb1db1f5236fc1ee2cdbb9fb72375a626f16b540cbb139185629fd392932ce088e890a6103d91ead2d3353f103345de9d7fae001cbd033ffdee970b42cadee8107068bd0350f42be67684073e29ab2d1edf4902b6a8fe7ec8bafe1a81e5686131571f6acd1e9dcbafa4cce2c42f24b461e87c657d53224625ca6f99a08674f95f9e8157b22fca790e437db72a9e475592670c07eb960efe295593817d3d9a3f2a1a1ec0530212401d4e8629615dba7892e819064e15c109d5197d4215dba8c47cdf96f458290491e731135ff35aa543e5d53701c6095d5351844c7e3666e2dc83fb748318ca7c118152002e94ece3ade26608205e204c3d483ba9231b73858d59c6aa85467c5fad2238297df7a60cfc08baf77858745d5131b9f8b4857fa00990f82ad12194d99806bb33f36c54d8a5d4ab9f5ce8bdecaa3651264d32805e598a660c36b65ef3dd4b9cceff70d3aecd83f2204996d26136393385c4e27d2a96045b4900111a4c4ecb280f0556c677c2d0572b5044dc3c74bbfba230535ed2697dbe2538642a41a5226e4a0ab2da2a43982cf00e785873e095d05b5c8a3c3bd064b3a196cda4e36c12680f040f4770bcc7753c0026bb3f1a8af6062b721a3da653f2f4c0d54c49505a347865f2838b4c13406e246470d3fcacb3f9d49613f44b1550c17606c2606fff6f8b9b45e883a12859656926c5e02c9503d5db552e5a1fafb9ba4418b017ea6d96e85c8afd6510c5c85f5cb2940145702c39f2fb772601a6ef2ac1910e3d62c782ab1cf3a763a122fd073900ff81a89687d3790bdf4eb65062558d797d1b5423d10e1b432ae346f70efa2be7c448bd800a1540aadfe32785029cc52c522ff9ffbcd5d37c7331ceda062805ba7370a96b37a30ed8f31ce369c01d5196da2423a27deac62c8e56821890110b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"66de7b73a018d0dec22fa8f5b4f79384205a5dd0ac0deafae3761e9221cb242c","proof":"9c41363861d091ba934cc586bb932cc83a69b29dcca9cbd3e72783f8225e2e6a5e39ae88a40dbe99f3d173a6e0e3d96078d749715e98fb0807e2f20743a64775a25fa18bd6c6a2b7db894b8993d04cecd03fa1e1612ea8688d046521f0583765365f4bb6189c395992c7ed59fc05dcadc619c8b8c0e34518bc056687bd914315aa65ec6921a50a51820a93563188f999548a79744fff326ae8041da8d91524023e84cd02dc38645646e98aaabdefdbdd97dc211e8a2423649afb5898e8476c094a14a35a6e498304d4d0971a4205b63ae0d513aa097a8b2e6bd5ecd2c8e83904941aa401df548aabf1be2dec648eb3ae57d2b5257504fb553c4234304737aa5738ab64f7f35adf51d3e515d40ab98a45a8023b9ad8a4614085336e3fc825ed70a8ae4442dd19d12a990bc3d4166e48d44fc9825d55eed38abe5b26de0fc35e0712f17c327fe6ac9b5243f734430a7361756012b2eab59d20d944263f96c4682300b8a2c5ffca5a0f20184eed86d2e78a3e1c512135e5968dcfd5cfa40d727b1470df303725007a01ccadefbc753d0a8b8cb2beb327dc304a21b4e85ecb79446886b3023deca0e70a5b5c17cefb73934611005053e6f5fe1b3c76275c915ef46f1058e48fc4d07cea6d5d7a731266c6bf75b322e49031f282e92282775445655922224f25d09c145118bc6e968e21a20b95cc741d9af2ef7ee4815ae7699d9742fcd3cee4693e5e0940b44c5bad4c9c0025f2aea9893916d89b32dca2eea96d19d4769e84c04f8e631429bc5b63e0b45d3e1209dc135b442bf9ed63c516288117540241cc35244b84534243992c663840933d4b3f15d9927a0299a1374f59d920080aa9f4ed7035e838cc87d66b7c7f695e29e69c2ef0a3580a87e085ffb1ed017b1ff3c9bf54eb8960bc901d4fcd3a82fb78d0ed9758d7568f5b5faf1cf1b60d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"528c6d660b35695687d37c73a8b92f1c2ae9f2dfbbba1e23324f95495192017e","proof":"1029251f9df20811e513f247026d861b544bcb966dbae4da274255134bd4285a5a77e5b5c0611c15818d0408fe639955e94bfa5a73afbea3d594313897f67f6c4ee9eef719fe6453fe6899f89bbc18ac26f0612f224ece0a2e6dc15941621f2628b363dd556f9617bd88bef330b41a3a0e17f57614ebd980de404adcede20a216df4dd81ed05b7e3ca3f7e3f8e00ff3ed4da1a124a3b49e0c33012cf9d4b390183cd896beb976a5f3330489f22f61f62eab783cfca0644da528b1522cbfc1d039bbde0640e2c709c6aa6c815b19d4beebf1fca010b281bdef78a92ce5faf6306bcb003723474fc5345fc810bbc38051f56967ec367258416edd9d7fefd2a666300348892b7929040a5f3e0d29b3e56968a0534697a788e3741c3d3b27aa8eb0d88c775323007d81caba0f7d6ae0d039940e70dcda3b78993518086372b2acd45bea71bc1ca5e81793ecf18e05ede25e14d9bd0e22f501b5e8941f5623e27921960c445218571f90dc1cebe8593d218e81ec2308c60ae1f4289244cca4f6dde6b2c41c2dca0e0980b747e6e826a145c09f7679ef6c1f5fc74185c6c556189fe4a6e4d977d63ea6c28e4ac6a17877e1096d8faa8b1d2219b0f3097566317258742e4a67ddc9e5c4ee6d27776fa19d387bda6e7ea0b6a2252532d99f22c3656d56e7862a149bd3bdaefd3673680ae29bb26bf6e5483cdcb9259b26c8ea06c36e032bca38d2278482c12c8768d0ad478cc929c13161d4649bc64f9073f6644a9e26fcae5ff34a8d489b3139e9e51a79e19959d5b0a3b32668d00f3b760a73bd3bf42d8e58ef46662f48762681fb8e6c6be42aafb6c1fa53293f1078d28ba0b6c9c41be236a3743e3328bc61895302415fc8367405e66e34ccd343b5e129600c2ba06c559d1d7daa7b4c28fd282588c912f45c2f8202a2591c52d228249fdd6d15b06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d6b8351e11543aee98b6b1bfb272da678828d7a27c09b81f46e2d2e36e111470","proof":"084b2eafeb7d3f0254ecc873b1b52f6c16098b91deb96c8a5c2d26d950145b5d4ef73276418f70c404f4307a589d4fb4fe29dd00946eb1ffdb0cefa0bf69d0610ccd776308218d44a553edc0c49dad9a37e1a605cf85c387e122b202f539d110bacd13898dc6c44e998bf6f609a413d115b16d4f7b7d4573d1d0b710fbc73a5a19043a996301d41f914b3040f6f159222b071b172c7bd1de0c37fdf0180f370e84146cbd3104e4b04fc7c18b213c6b08fc67a18a2c696a7afe84b30d530bf00c02c4bdfddb67c4ed89dba527eb742609632bee34f30851697d7c6f2bc6baac0d3a5a269f2115e013bfab6e3356afb6f995c13c2019f592b0acd5f4999b9b5f72eaa8512ab3e50503219f8b495d0ed0b6858241d24caf2e02689eed1fdef6962cfec130df392390a3f6b3450007fe93302a38fd973172ae5bc62c294f0f94eb577c3c2f3550f4477348e5d2488a73fb8f713c58fae0335f72b5e0acb6ade2c44950b1075b49e29b3cc8e6b87c775ce470ca775a4e7ed78d4421cc745414bb9a6cd860a8b8042f7d52273afd9852c5b6050969d50dbc0f3dfc0d34872892fc1b719ca04ddf412562480e2becb5bdf7901aa992a6fcda7e0601f7dc92f645f59064b624427ba7755f6f400e3313025b8bbd05b3882048aacc5e4da8582a888a3e454efc23cf37d97aafab03e002b3a98077b4b3bcfbb8e169c3ca6b94754cf1077bf42aafef3b730f1fd5f7517a3e9f5717e7aba4248da079bb1be8173d5dd79b24fef9cd10c0329850de3656a4d9aa451608e60df3622b7ec7882aaf03aeed8029fed44a12bd502a100aa94c4cd6a64b7c7b595b5c94ea01e8c90cb8ad7686c81f177eefa9a0ef701c39d27286aa9c820eab3fabec40e78963b20d40b1a333c80184813b0271dd7738d5d2dbaf357b915de89d0a6f2295346b45d3d938866ac00e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ca0c41bb9e5b7075deccb2a4de60910c563647feaf4a9b10be4110c6788e976b","proof":"caada107a42d7c853f7557485493b624431a031a221a1a1e4e63e37ef2c7ad3e562b2ee6bd19e2ee652cb01aacf40bbd4b8760e55314d157f9e67734a9fe26458a17195bbe704e7ac56a50532fb6a1a34bb9561536a1792a4c9e0dd52d0d9653da28d12ced911b0ca3bea41ba7ee96a4c82f92b1d51c2f506445836e5dcf934d144b76324b156d7f06376d059baef6086ddde49dc7d0052eb4b1f10c524aae0ce5e38a0b822668483828595d6a7da4da49e55b3d7d48589674f89857471b5d0c972398d18cdaffaa2a6705f69ec80322a5483a240e5dbc24e45e0fd52a67af04cabb39ed3e256e59eb620a3794f384a4f7601ecd16d86bea93f75f1afdceeb46e0563c56b2c338ee3f0bc1a696aac4786d5683f98ed00b448b5257663f95dd500cfe57027e611adbb543c067a24003ac5ec180acc90836ef77642bb5dbb96452d0b91e03318571ca669573e56bcde21e9d2c7fba42fd4f7c519b8d5ad1becc6fce3df1f95d9272602c126a2f7ed6d8de0cafcef3567d7e59f75c2e406d0ffb6e7e20562e052feeca4f290138cf0aa90e23c671a97d035fd325a13471328b9b2cced8a48eb707387d0541dfcc32fc6d70c70076ab42d44970be4b3ad9afe28a53c0624b0375037ee725fe2ebbf83efc8adfb7250193b0bc26bbabaa179b13f270969f8194b5fdcb58f22eb28cadb76a60f892b122364f38687bf36cc9f0e8d7525e84f3dfd2a868bfc1ffe2cd69f87682125b41309aeb1223aa808793912ace2dbad48f55494f154898814f5f45f92abd50d055d9cb16b3f03cffe22f6334706dd4d2e323880c6985982cac0f32c088a3349b5518b98624645a3301834351e71bac454b9d060bf7a9736eea85e6149de347a8f43cb32c19ee0a9e537e0a70430f1059e91abf1fa78b2bc66534e4a69e579c7e41e20870a737869082789229490e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e057044d01cbf4ff0744fce7c6468dece58ae2a35dd78201d0faf8c6d2efc54b","proof":"1446ad09462d7ad3c34e2fed698daf95c5c863e3b40ff989083b0d8dc8c68b09d6e1a769d20f3f81e9b9b75212aafc7d610e823bf927c46b6e5693daf4927a6802a2190e64cc69ead4d98a0a3d507622e900dea3972c572bfa41ff2409cbf545e2c13165304931e3cb4ece0120bc145558e8a4353e90f4291930cd77db794a75dd7d4f50f30247cfc5d21641bd1fca7583bc8427edab6343fa2fb974ca717104aaa147be23d317b7c7341e41c225dadeb63f9376d2a6a9ba9d18a046bfc4fd081da7f2c9574e34b82c1d5ca1f4f40c7f31daf347f521a3a798e5ffc6d4ce870a6248f8ebf61795b5b4b3c00281b1a4795530684253e101c65582064d4de3df55be8fdda7b3c401d1a010a5c37da7696fb87c6bf9f2a2781e9ce3aa080488a7092e21e183700084f683ebd8a3f5f2475ee456a42902c983964230d154981bb875260bca75c418ef919fb2dc8eaa2c7e73b731e51f6fb86fa76171e95fe24f09704e3a4a74dc113f71bf806f6c8f1a0fe0bde99d14c8bbeda634c3d12d79f6a668e8442e44b885737ad25176f67bf29d638ccbda709e536f3faef8b6590ccb2f2fdc9f4ed1ceb685d0dc9d02bccb13c5531c1febce0a0fb662939f6c4e13b2717ab4b21e0ba761d7f96f8606fd24026ab1ef851d3a65f4eaaff294ef12002433324a2cb5af48a5706afd685973bf548a18d4887d37eef9b5a08161761d1d462e1522d9127cb607123f4be7be002e5d102e88e54ffc9ff1f20bf26c265ba1059d5030bc6e01ac6416dd33bf9f80b8aa65ad0291df53e444a6509a8f3e63b164e53398df069b1b5959b675f2c9ce3d3f8b50c5523a412592854b02c5cf9c3698eb546742eaaa838cc46bdc86f1340791751920fc654ea0a5b28aecdb019e7ef4b600b93e93f92efcbad0e66b3439b3d3729e0df76d759c8a15394e8a0c6515e35706"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3c211053346c77e95abccef6892e5d1d7a25616503d742e62f6ae2a496a49b18","proof":"6a1a033beaa8a1b8872d5bba2ccb93df79d925ea007835f7f154142b7e0743466ebd47ca961aa7f8a49e09fae86a3fd0065fe0f5f38d34234d90e8ebac09f061a62a9dac9fa2ec9ea20a756fb4310a4eeb59d3aae279b3829c261e499b13fb537ee5e3862d04ad1a0686b7f83ea92fd77f229c562a181f63b483112851013b70b64da85bffb13cce7c42b48202d9054d2030e1e7daca1b8939e9c97b8f50a40bf8364d592bcc1c10361fee9817eca40dc925ab74e740d0c0133a24a1b8b97c088b50f700c6fa97d75a8cff813f9c3dca8cf73c42c8f6464da193b3584cfdec0a4ab8351efded156292cf153d59cffb1c92e1add8d5d910bc4e89d1bef77fd6083a8c08a746fe31982d63ebb4d23fc8b7a16fdba41eb8819a44f7c0fbb318026962ceb6d1f8581d20c1ae8264cfdec51c888393f5237369b9b2411bfacf6a231ece1a8954684463a60253476d00a6623f49d4e3038c767b733bf7abe1ceace061de84f9dd94e3bd69057c550d5929cfef452bf07758c0ac1f1881fb2751dadb70dac82d1afddf661f0cdd8067f45c68a6944056917111a0f63dbba1d631396c1d30318a449584ec128ee2deba1b75214218e9a3ac8700d3b712a31e3ac3704326784ca6d610ac5730c33a0adbc892ed5fb31880fa10383f5c2c65c684b5b07d22daf64aba003bc29368d71a07858f5aebc76c3f232b705b96c2dee1df06a1f42b30193d0bb64fe13f054c9cfd6ab63f9a2cbce6a64b4d80623844083bddfd9f03f28e3ef642e4397ec86d1028fb0f9344ef970bd080093edda6e9aca019bc104342d04345dc1bd205da41c8944500760888d369d9dc4859490ad9c67db2662d11cc954a1f1f8369bec04fb400324a1bf2907ef967b7cc0e384be78978cccb6d0c68c6dc67f01110243c9f70b948b111ef5b5643c8637d689daf42fbfa9faa4203"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"98e69a8ce776b641c515a944436f5b5bc337df01f2e36b0e3a8317898b6dba2e","proof":"a2aed6c8a9de262c00cb2b013891c8f8a40c00f5c4a494fdaa7286f7cbe96f25685586aa5ca9f87527af09086df9c95befbc93008cda24e581ad8acd14d6c15cf496b998de58ec2f3b7296336a5237d69e064262acebbc1016638b0cb517e07456670ed9a2d4a264be307da0d97ad9a24b7ad3213b0b7537a40004aa593769332ae99c1d4ced83ed9b8310cfbe331515aa9845add260cf6cbe19d6ad885cf909bf23b3f9d276f34aa770cf8f705c9b7dafbce2b6f1ff1dd80ffd5ae61e1773030237a964ce4f3ea048ebc7ca9721dca8674243064ce36537f19d95a20fe59b0054a197a25e86433a1f9af9624620bef6060e2a74860ce5fe44488c8071a72477909df2bfc2644b44f310db7075a1c1e27b62e7c4bafbf37cbbf2d00182aee444e42bfc43494336453257fed4960f6c0b391f8831bc0eb68418175e061e7b145aeac6220805ead29ddb915ee703da4a93cc2d1f4f8d7894b89a5dd86cb47e3a564e347468a29b91118d1c9e9b42783b75e9f04c882f0cf075ecbfa820277fe63cea49a52d84e828f3db946079491cf43fc31dc044d11e8fce9b00aabf0ce4ea5d04eada665e8f2e0ebc22b682b5ec8cb1644abdb0b21ecbcc509542686a55fc5aa8a991ac0aafd36fa94fac3cab7a37395cbf88f33f66a15a4aeea2f02824697a0e21f016a31053a6a2c6647a855eefa26875a7be8c55264a2a093c8871afd7546e66abde21178c0fec3df7ca12a4e1f2a2a6fd2dd0c65bd5e55186048be7920a428cb759644006b2629bf47dc5027635872c96644d1d50cd8ec01fd6a6d12a604e11e25a01abf15b60e8b5e9ba9524089d6b4ea27ea69b3ef4c0c8592374921c84688315df04395c0892070e561970e6f88165fa4f2e39c967edfa21dc6c6c03b423509802a05f9afdca656bfedf687231910831184ad2467264edb0c417ac0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ca36d0cc61fe08f407c7b8137ed927d79c44946bd4a1f06b5fef35b60818e959","proof":"9ef15bcfadc04c122d371077a3504cab410b9dc00a7dc10be562561b3daf4902609d0ad7702834ff7d7c683c831b0d5c3ed74f537021a412b2d266616eb12b699a001e03bdf662e36731e05e4cdaa2c0818578c5951d634e7a0406360417713d4c9f3e41de35e138cfd137b99549e9c4f92bcfcc1f6e220a66397b74e93dcc4a2509cd45f2617f767253c81fe641086e8519d4a1c033a11af100009306b3d805edf9fcc130a6d79814c19207ccc68591a8c0b933d31a9b2ff28654a8d79b6f0149118f3d4d7ea7498cfe023bdf70eeecf4a6bc40daaea762c9f7e69cbf62d40bec85856f4fc7b6a745d31a9229d212a2270596b91624e0698d80427a8413fa37386a157948f45c6b46272d4d0f0ca2f89529be82d549a511eedfb60b0e00404a4e58e0f3dffee0945fc19ac90a1cf5c530f71ed103e787dfd065f93282c6be38c24a9fd63847a424acc156fb892919e378c505f688e5796325ee03c30a0b5a6b5ec7e647dcdc156394aaac58323f0e356f8a7d85ddeeb3b1f312d2663a6566424ec781d4b6d854594765924ab9a866f77e51a4b5f23f9d94aaa8f3b8963a1a71aa2417910f436245bc6505d0eec93f43bbe5696269e3c5b3e8bf9b10ebbfce724640475f16141863537e273c679ac8cf0ee85eee60477e083dcc49a23b1e5a5a301fcb1d0b843aed852bdd222f6b064bbf26092ebbc4d4277b71caeac11fcf5e900fcc8f5bad99e3953750b2a794de872fdd5a437b8942c0daed24053bcf21017c1706dbaa5abac405682573f31b2553d5137f3f1463285c44920b3b0eddec28168d47865642a89fd60da93f25df734ecda6531df721d9990440ff45aeaa3738a9435adb8bdbd821a99c7fe468334c592db2bbeb4b67ee29747ec8cf844c760910df6d3ea31645e57933788d7785b98584d31fc2e88b28e583e5122a1360bc09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d6d87e1a014a7c62b973d194914c72fcdd6aaf354fad1331f21994a901d69070","proof":"388b93da522915f65625e24b26acf5a9b702494858c5d5808b7c084684b1136448bc6ebde050eab1f2145de69b12e6177dff8a5a94f420e91bae365951171f68825368b80e6c565034109729f09976c0ce7b2079932603f1dfd2459d9a624572be34b530366250dafcfae2f585582c902142f11ba27307d1b18e61430fd70c160b403d39d1fd02d26a1b9d6f3f0f1b7ac2d3ccd9c0cb154061cd452dfbe7db04ccfce6c21a08a47b922a5d2a3632532a471cf27a5a8fe25a0a72fa5c34e7710797242d387163807189f1026a5ec7a1ff02b2cc74088591b88c3d09cc1df5280cb6f014e95715188ea09b6824c9c47aa9f9b39774daf038d5451683fc2a0895467e7c6cd9e2d8f747b0c72dd0a300fb1eb85b0edc0e972336b17ebad2f669f07fd84e977be8c07a8e9cd5679ad8736b5544b2c511f664092fa57fdea1bca5ed17ceef2790f42ed24af6a62f533d94c24a5dd940692107fa05cf6027d60a9feb0b2250875265bc64f61798b7307c7dda738b9ff1a8f13323684eac9abcb1ba6f5c7e18c77cb51d0ae529283e6232534dd9c91900c51c8263c3d155974fd3a36c6c5a7762570921d8202659a3e99f23033163a2eb5df42a4d5d5ba84c0602932003f8d0ab240d11bd032d46852caed0ae5569bb794cf470a01ecbe295b29b4caf438ef67ae760636768536bd158fb4a95c621e5e51710c30983bfb47e9a2c67f044eef3143c6c26ed42998a1d51c19d453ece8e6f53223cacc2c03d4eebf583f66266227619d7687b8b4145c5b1f56f31f779813525e1dbef17594614495636cf2cda8251e43d81c8ffa59b3f5c8cda8fd08433ba9998f3a18daf616f8bbef51069220918117bdeca4c11afd1cc00b849ed17aaffe93a3b28497c20a5cb562c37033d3e28db68b417700d7023ac348cc90ebedca1fa72063fe57a67c3a9266ad109"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8e8bc0722865662d9880e8ec31de5b2b0d5857873b32a1663cfba4867633d24d","proof":"f606f80f789e794b83c71d6d56d322cf864b4656a0cad31045acce072d08de39b238cbcb75a6f94bb15e7dbc77d5d8aa568087519eb67cba69217e57ab77f4054098c0fcd548e47b9f0e8add73f05f4c860e53443c6a3e497ee6820a89fcb30558c0a4a6217c4ee3d6e4f361c4ab743ba2059aa934987afddba0bddbff328a343edeadef031c5a6c2b84bba031b60570b7c96f550e85508514204e99bec9bf0c37601688f90d8d6486efe4e0bf44b50a936b24ca7e0799a5f776bb9c8724ac05da722e1d770cb21ac26bb64b70c74e52f658309279bd13574d6c1a71e1eb940e522e3c0c475f086ea8dbeb2baded89d825b82753f635ad163bdb77230bdbd35184b95c7c4ea20547df5c2586eb3f2af0afe33a5ee09d3788da4288a2ceb87d17d6a83f52a8e60bc0349f994462212ab8d8842401a4830a83eb38d833584518290eacfe57443f04702cf4c96806a177ac498c4beb7fa1be48c6373b7d012be314347592ef26a7500700684abb5b0a1f170bf5d7611cfdf14638570fabc4c4f719846366911c9a693a00240b6c21398cb89579053b861e9c2345a254efb8174178fa1021e7bb51aa0c8cf77fde2f97f1131f58fa2712a607e602bac1c47dff5853da1415cc9a20061d1824db78c6e40fbcc5f32d5b74b50847b18640037e128268d637e01cc500d9f4767cca6d385c7c626c48113e8567a41b7b187599fa6213211032a10913de5c76b01827d3354d6bbda016ff1572a3c8cc21b2aaf8c66bf80b66eec00ad3b1d6c780fe209cfcabaa5d09bd575fa9bf563032a5e6742a983646fa4033841c7cc6cd1c53f69cfff7bbd2306578918047cd51fecbc4d0c56c7f2d941e6dba5fa9f9262bdc8dfd59f6771600beb2757e340516cc382fa749c7660ec8c73cc1edd6abb434b7b7563dfb123051b4484265a0ce4f9fd1eaea1a79e50a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2eaf512304fd84cafe278deb603ad20d117349a325f9f451f9d4966bce328176","proof":"6c7b47ef2b17315bdc3d54b7fa3d0df7fb7f56ab300b4bfd1f674331256293706411877680279c6665fa9bec4b505b0c30e77b674c5a0962d2f48f0d261c7a5ab09bf9a8942212e0df061ded0ec3d946bf622b2f570f4379f9dba9d863b361656aabd650af04a43dedc4f72686e47032ca4b022c3fc725e76b2e1eea08d77466096e3bc03b1b9355f912882a68935f6693fbcaaa36376d1687963d245336fd0cf25a458dfaaabdd35f0c0b48a76b29d47622016cad46cdb95638466b3fe88b0a2a8b730bed4b1c4ac781ae63842710ff2b96e28335e97e7a1cdfc8e69adec902a6e0ea3a428c750419ee6d17f281ca0be492d35dc136a70c703d3deece6a25487ceeb1197b337fe2c7a5081dc3d29c24832f0eb470de7c0ece45599bc8944518e615e63343fae7e4c41fb81a22b20c50eeee7ef1ce643f942441704b3b4463554c49277e6ecfcd41f8b800f9c6543bbd06ec6787d1f4e653364f9e0f355a385df23766f406a57fe10a94f70a9593e1825dda6440db708bec45797bf9035abf640079c563c36c1f9ac44e960986a891e055549e7f978d541df66530d0f06b690b1427d2976cdd38d35f2aadaa25dd150e2115f5d244ab247c20c1c1fd0304d524d0c886d7bb6046b9ff7f7ff4024af6cbbf3ff3ef6e1c806c32d712d1a0e5290662e2ae0c3003ab374bdc5dd3fc9eb619cd625efd680c7dfb23bb53debba0f0397e4326a4876fb8b63e9869e474ba522c9a15046b7a683e8330e8e92a80685159f26cb2543bc69a49e2765ac4a12079a9dc1370c6cabda947d39398690416f15d7697579633f5b9a735f0978dd878e651b6d3321b98c71c24e9138fc6f53ada03c6d594bd449d46f15a52151687cc3670d188b6ca25368cef5dac7d594d93330c6726bec6c9e86efb30c569b79c3093bf18c544344fdd966758381278336ed30c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d21ffa785e0ad8f81a31416e41de0b0214904b6af810c7366c7cef450d7e3952","proof":"2e3759a2c5abcaa9411758192c10627c8b0209d572f501f3f62b011c871a4f559804a125cd9c9fc3d5b0993ccc94d067ab4259d89794e0c299334f0f94c6b6509c1a5164c361c1df478ed35a668241ffa7397afb0bab39187372d3fc1e822453ba7b1a1e99591bcfe3a5a973401cdd34d66ac020a37e5d6478b4c309c6798e005c8861d6c10d79997c9881e2eedefd29cb0a06c1ca048d7bda47fa4d8f0f8b0a01d3e73e7b4d4ffe4ceee978fde71e53f4755cfd57c677d584657fa9d5300d0692c332025c3c80162d161a3d8d83a0e8875baed6f1678bb0d1cea067e986780050296a6f340bcfa0f6428e0e19ad557e94551f528c0909a9780c1e89633c137c184cac1c0bbf09dae3e97b3f02759b01abc5dc9563784b6e4800f4a282781b02ecbd139080a6fba3e2ca8d579c4be57fddb41206227ca86378890fd7d7e391714277fe98745750ab9a8c6a6fb6ad1fc85e91d7cd26cd028eb54a01cfd1df4952243c51db6c18bd153697447c3bf6397c67a41343520a60068adaeb264a75361654e9fcc476394029ad8b0cd908436b3ee4b20f0ba51da94161188d093770d85656572a199bbae00bbb7ccb7a9a8d174e3b58ba85022c278048161a5b766afd502a3aa2900965329cd055ca6b94e02225b1d626b1828c9794bc61e910bd81a272a450b39ab5671b7d5573298f0f03ee33aaba3da476dd4e99c0c90b679523dc54a803ef69686e77bcdc8528e16350c4a85868680a3a052813fb8b7bf80a18c130dae89929e39d261dd1472a4b9bbe416dae770e96f29af3771199b954439cbf2d3a90ffdc8be6ccb99a096110354833d35f8a53d914dec48e1572313465270457b11d826f2844e594f84d4bf6a28fbde05f470231be601e51aec07399219db606d0a42dcd853b807925ac9995274f878a303d56dafc27753b736cb32b1dc5760f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a0aafd37065bf6d8db7a02d6f92e9c88fe7ec045a7e843274127c757f596e651","proof":"58416c29578cfb9f7fda43bee9e8dd32ddd72126eaff2a47cceadb6a0aa62a00b497d48d632cc0702fd0406535ef248392dc0d85344417a7c26690a52061212facfc371165686a0d015139a31552c9fe3b3b0a8c1a3ea6e8cef04d4e689e8f44361f5babdbd614fd845bd4f1ea059776a4e8da0ac3173a21886c9144be4b1c2bad97f56c29639c1ecae77404379eee97fee085e414ea5c2c0c2333db5ad7fc0ceecc9ab9b7f1a46879907a20e5ee5cb6ccb021dbd35ef5f3f35d6d67fba4390f146dcdd8bdf3415714e8a67201882515265ce59c7540dc5a6c00c4f93c4e09002e4d2172d14c788017c5867e94e7afcf3c277f0bb411126039a6c6bc22bea12a826fd9277c93b475ea1fde702da08760cc0f3de2d435b8cf6670b4aac019737278a4737cc540c914760a87a27850cbcc4ac5aabb44c4dd5e8d62c2d84a977143ac79b49abe402de9a12fc2143c528dce9fff5afa8b227f5b1019cf2749ce5024b041a283bdd1acfb9d52c56b8a68f12a0e73e543af0549323edfb6cf7cca493034813f86efc96cafc844bd191a5cb7f474c5fa575464c26eb69ed02672fd393e52a553c1d78c091677f78291403f6598406b962efb0155d8c780e39e4e5ee07754d917b3d85205054b29fb55a3777125fe1ee1f50635c0835a650f8b0d6fc847c4887c7581d0f24c8f41ccdcfa557cbd817f6c7dbf22a80234e19e7d6255e624e8c24cb1efec7b0ba4adc5113839c67024b3d8fbcbe96e4a069b5b76bcf40a2368080aa1f9395d84d74c010b45bbbd3a9331b1490a26d7a5faf4bbe1697475702c3932b800f75b717fc492599ead598a6fceb11cb024b1967603a019321750488ddfdcc2470d8caff5373e656df473b89946f7d4c89773b1d0f91c92bb248206b5214c48c7cc972fba88a4e475dad836b70d503fa765239c2151be1ee69b040e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"10f6dbbc0d04962746b94bd0027b3eaa3c5987d87800487e6f7723bedd936007","proof":"ead0226aaa0e24ffe4a35ad90ae17d476ead3852dd56703286f5215439d5c25e88700ffa218344a47d410b053ea7509222bdf9552be4b524f4bea385c6dbdd32f27a80d1d370e4f9e2665268b15467fbb1953ac30a60f376a49b1dd45414505960e4b66b6721f7cb2247b4ccb7b34a09a39921d02612335a835bac8494eca808b8927b0935dfbdd5c98638ed91ce864b7f97c728daea5ab7cbefe51393ecb002a14e3d31fc5c2890dff9e207d1607c9ce883a199517fe06d316e1ee08dc7640b58d2d41251e0887908d48d933604a18222838daf3af4f4ce3beb17786ffe940d90439af806773053894564f8bc7363ae7195d874478e685c8ab0ef49b1fab17d029e3d92af3916c9ceffbb35cdc6db095e2db48f7c3077888ef0a7efd6cb2d0faa8d0fe9c5e267a48f7463ff31d0f52805d280ab5f7064139994618c47fff30aa2d46d4e34971c3c4a1cc72cb9885b8652c7f3a989bc60ee59964442b022513ea8e32c4aa987678525a36119a45820d1be9c26750e55fb79d0358cf21d5ee21da6c9e1a5c48bccf0c5c7265d82fce4a487f4eaff7aa89a76d4e77c214033a06b0e8e1d8097f82bf3cb666bc90de3f6f6d6a5f1aece4d10feb13cb431b6224e29443c8ba6f3a32a325ff14b0b65fa4ac91131314868c32307ac5ef718d35ec16b9cf84ec838b0440539637b5ab331107105767592f7b31d8268c730a1bc76030c4814036683d965b14aceecb4f78856aec2b248be521b9f72a16fbf05a076551d86ac62d3d76ca7297de2e2a133c4d85dc70f9674fc35d5e23018a1c411599e213eb947f0b95839779301bbc5a56123275a2a7357082ca38582ba7a3f5007dc4dbefdcb3a2ee6bc0bd8cbdb604aa89e5374e1cb7612d307933e8bdbb902879d027101eb3c220e9fd278e6efb58ac5034f646b54518bffb18e1ef082ff2ab40e05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e49a0a1102891f6eaf5e85c7642c4fdfc081454dc0459661c2af4be13712ea4d","proof":"707bc25d48f4745dae5a5549185690970f9fd78fce1bc1d9b8429766f016c94bf0ef327234311db846c951426c6ff4b1d3c8aff1f2786be20dd48825ca9d22022aedf68c62802450ea25eff73c90ab0057d348fa280414c6d86736f9fbb6764a184102c322cf5a47bcfd2f1258466c53dea9b25f554185544f4268f2a70b594d7fbe777f66ff88c8eda3fefbd1ade292866a454c40abc194987099fa8b1aa4035a9b2db57ecfe40c6faf0f44d70c9646355ea1053394f0ed5de3fb52326e8d071e06085c22a9e8919a7907b21eb3dd46098db4afee8c91b354a45c099c79fc07c0cd99ce23ad136cfaa0a24f46b9c13fd8986ed7bbe44a0c597621e1b0708b4018b90985fcd22f93ebffb964c9bf2e8406a0a6dcaae009220f43bb469dc1a05640cf5f17311e47c027df482531d68558a2e7a73089464022841af9671f863663485d0a546ee1fecc3813ba15e70a3d484e251755130ad77c8999c3ef1cfd6734d6103467685ce6b0b9dc824e6659a46b61031b3e6628589aae17215295dc6362de77bc73dde50f42988194f618a797faeffed8d553ee3198a93aebb746c5cc37ba70b8f533361f40a2de771514153a7796a9cc4f752f0531a9770e720ac68e4c6c4ec6080387ebefe5fc4ce14e8c93e6998a57065bfe0989aebedfd258417d79b261e92190976b47ebb887a14fcb7b6d62254112a14966b180069538088be53e5a08e5cba0e78e7226ef3f8b3310c20d5656730afb894d9dffb3334922674f23ae18837c82a76cef23cfaa24cbd7c7b564433f2225ba9b3c1ce35b061726fe3d4c20693cd36a89b8d8b0382d55ac2b8a2bc0ef30c6722ccccf3fa1b53253c57c6257467a117a2ca16510fca1e2e39514b92234eb54f2bdeaa0138daf6e051c0ea3e83b36f73d584a4b6ae7df6b297d5457e5748bfabde40b837ad86766f98504"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fae27b02e9fca56271ce2e5781a360649cb2ed85ec0f264be8b1d188b398d911","proof":"027a0909bfe7fb62e22d2deaa831a67f78f93bec691f1f75871db65b98f14966f2dc985014785ca432847938cf904f32a27bd934265fb558a00d251ecf17da4394c9eebda7f4bdf812a2e920a8904869aa0b9b95d5070b4872c0446a0db348224017476727c5616e14ea71d5a01fd01bafa9ff254559641df4c5ee3cd209454b81f7f60236932f5b9e4a8414d2bb776e0218824614336f204fa94ea6d3984f088b443ef0062692a4abbe6c503da6c74630e7edb0a2a862a7ea9c8c82935fe705f6e022f19115b003bd5971c3c36a78b7f61c36e71b79453977fab3120cde6603fcebfd759f5dd6f18e51471ada0d4c13ec508fb1ab01591044ccd3690849c773c8bd1ebca7b66ed5bd7ac166a5a7ffe8c576731e96f2e631bfb1622587359d0192956641c2474990452ed42f1dd0dda9c1967388891dc2dc3d5c1dc3cbf343071e157f4c31a9def4dca6f45d740d197c476192a96914698144393d1843f7ff0034cef3b4c8dcb67313e4a74d07e6c301249f0d10320ed9a8eb177293af956d21e445dd25a9f795ffe45444df8040e744d8a2911f60a2b53c25eb713e19926d7e9629e93bae1ebeb447e441622d3284bde97f778996567c03af2cab637220333a5e2f9e4b28a7f51ec90197be4a7affa0b8a64c61ceafc719fabe178db730d1335436256df047977b0ea225284b6c3cf404a7455a3375df45b0c172afbf60126278a622fe920c3d8848478943b1740ec42f1c51433c8cb9ba7d5e9132a8d47716145a070a454030f1e6becbafef54e33f9405b01caeaf15380cacf2716116c67db0ac0211a87461cd0af274eb97c50c53f17d067bad277e4af46e07c4da960205496dcc62a94a437ef4fc84f49de6e404b06338b97a3f9076909949bc62887300a156a011b630b5f74b7fbee8f57ffafc9e7bcb1060d9fc0a5327198cfe898f0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e08d5a9c739c5edd2bfdf82bf40a29135d7b90f407e850de9a8cd6a3d3e9ec2f","proof":"3065faa08a12ff6a9f3253479791f20b949b8f09b55721170edec6c3cb8d5717e623476cedc15b217dcea55c1cb43918f8af5d67f24b7a1a2806438078896719403825b8903bb0d595890ff80e92d72d267a447d4db6579a33114a50766c6d59ae58e6f5764aa342ac15ebd76d1fa6c189895fd029a702877d6311d15dd00b395c161dd429da0765e71583cd9e9fa70923a8888e33f4e5cf3178c2299624fb0c58204334d363f8308b4ca61cac4f97c5ec11348af76faaf519ba76e8f43fcd02dd811a142bd10bde8f2331116a1801b20128475c1be15326bdf32a9b57bde70f9c60fc61cdb15742d11bab834c6963279448841736abd2fe19f3805108ad282cfe2b3a03d8dce284a59f98548a380da9721c747289ac549ba8d5299b71306248c2450c674e4b778acdbe9b5638e5ffbac1145adb444650578af82a76f318254978715e1f5ca120ece66b93ab65226e4c925089d981ee8d5ca1456156b0d4ee1472728ad507d5ff1647b76c99f7d824bfcd3a8ba1d1180e975fb92ccde9bc0d0a1aad7ffaa5f347358164ea6df01fefa8334bfd4de10d447d5103a5ceaa0eac7734c60237b067e3aa1b2b1e5fbd8f3a7fcf17872ab74308291ad988f950ebdd3128bac008aba8781450a7d008a8c8c4dc2fef967123bfb60418461278a9882b181a459f0b5f10f1d364a573b8403708c528d4ec1bc4b209725fe5f8128f73f864146b98987da144fc9a07d087f40208117b78040c273868901d40652aca3f405eda81e5c8c86e58fed5783b584930db063a586bf3573b2ecb869c600a3c9342592ed0bf44f06d65c2492dca341aa655a72a37b5b20ee1a754e85481596b98d55289b604ba3a37c77070b3e100cb1d568af6eb611af9cbebebf9e8c4496898750fdbedec554c0811deb818d2f50a224f8f7730df4bb8cc71bd6b81612f4baf5c03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e845ff72bc6723a6d078f09e11608f22de20b0a29c8ecd96ab63f1f753b12d09","proof":"484ef7ee054ebbe6bf50d15b1873bf35b35b4a477e896eb495ff88ce9074ab6e7a4087b5f9d612e6ed7c622cc9c2170dfc9b0a39c70d55c07c447bd18936cb2e8a8fd18dceac282cec49eb0126a49d6071a21b5dbeb699b4d904590a0984106ab2f166f7fcbd43eddffd32f1f6a82b2c155e289edb5eedfcea8f2656938f4e73b48fc9a006f49f809434b349fab639cf77ba88e86431f7cce6901afce265510e804ac0780a27960df3d4da257a1eb0c727ecf4f1c2c3de92039c6e0db2ee500c23554b0ab5bedad70eb94d3a2130075899b81e5de1732271c9c12708b8b1520e8438ca63fc8f81df768b9d582e62583546f8c22a526ff3f3c3c8a45152c5c60f0e7ba134616b4744fe1c9767bfe00d6c5d1c0dca999131ad1f388301a04899282028ca76e7258384193267b5c1dd66e44992047dd1b80003b1c91cc72b442029703ccb1b3191de2556137701beda6420abef89d6fc76de5db95bfc78414fdd0ccea25dd5d8bd4d7d95fa7a033e9186c3842d1373a13d0597c85eeba99a00341a5a426f0e24d1b2f67e06a405cc9c1f6bb9946fcbd3d9e0491ab7bfa309dd194bc24772ca7737307da724af2a056ef4c94a1013ae1e9e343906ba5c79ebb432254e9e5769c4ff3a87341bd8c6a805d20dc5dec68d68430f09c0a1c9f09677572d10eda5a38f7d52d0ee37e2cafcf52e42c6ff14d51c988db89db4499a5262c956f6b3c9bada2d43803dda18eb99f1b139672b111a068f71fe98b2466e8bc468749ebfc71a8a2c22fddb50fea5b357bd027bb2a6be14b0ffa12fd3aefeb8021a7b526bfe2e854ae30b80ef4718c051fce26214c4c2940179c684daafdeb9864f4c2b81fc616ffe0339128b83e09acee2432069b0608cfc5824541e56eeca04b608ef86c3a9bc16ba89c216274d22681b1b482a96fd86559f29a39f7376f094c808"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"98032bad2fef72e2d1ba8d96fc0d348181b43b3e0d689ef608f4d5f74a9c137a","proof":"64e2e8e83579f953924af078e9092e0682ec0c166895c909bf9f65c84e99315eded81c8e84319b7a6642d480c2cd893608e3347820c5d4135cdb488dd128be49b2a549bc52e05c3d6a0048688338dcde5523161c52d9bdff95c9f65c0467bf46ccd427302e019ae15284b49f274bd15d65a2660d4637c60333ae78768f41da67d19c3fe3a674dd6262964b733e9289d7b35200bc523413babf04a03c2c2eac0fab392113c4490daf86f5d41a4fed04254be6a7a995bbf702f47ca58354d8960224b92391eeee525a51374c2389431b82fed7e71eb5704dad9b9a9db30a29500c86b281b67720a03074938d94b8509274b48d849ae5a126b1bdf7dcd000275c40d4266cb5fab68d54a56e4290677b2194f901d2322653b05811a58f648a01473ea8f13c4e097d97ca3014b1d33e3b2123631d7cfcc2f1a30b08ee94f49b22c34fe689dcad3fec1a1577b0543aaa50631f449071b85dd1d245a85a03695b7c5f6ac290c51af48966560deae1206d3b4b03a22571dc193f3c6fe62697381da613258c6f823791fbf5d7072d5906ed82497f377b3170242dc90a706cdb115f4aca3ed2e7716dfb7977b2df6b07b65c4e54a09c838dde60efc73563ea9741f4a3b0703a14d8ab146c9c92d91a30b193f31d20c12c86b70fe49b60b7b1733031e050591e4740029d539bb5eccbfba865e998a9fe4501faaa8c26997849d9b4c31f044db05bcf99020db5b0a12006cb901dd9913408634d02c9f52a13e424dfa7439636acb8879273bf4882cbb9e03ed617f1c5340058e253b09684b490fe884281fb2cb62d698dee1b6ad0fd544b8ca8499bf3d6102942aa7594b597e9f08831d7bd21d6a8ee6711ba015b0e6482a9c1ba75a97d6497d236a827b04fa927606cafe809e3a3dde578fb2d0495d9405499d4f733478fd547b603a99ace89697310c96d09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1caa010834446000e14b57a2925e10c10527a8e21d67ecc2f78bd72a3a9e6547","proof":"4a4d2f985f594ff9a025bb527d4a5cbd11be38230ab1c254f902b8dec68af358beca0386b0e7d4d5e27990cf9991d729019fcd9b1d44ca3ac2dfd9879e7fcf08469adbd483d1539c60a5c7f7d444006a2032266018e533a84007ee9af4bb68222277f3544b5bfee98ab5b078c0b7ccaecdf86606aa22387724e95d92b2f5df4e1d9422b13fef2e4ad1586f8882b4f18796629e979cde3ed29b245030400e6007c0027cee389a9f658153dc9769c2e2b096d6de3cec1089b3605705b8710dab0e0bbdd42eb35fbeb1d12c77a2d7428ad7ab15fb3873116b42ba47775446a3c409428c2f4fa276b3a4f0b5a21f6d9e0fd5190550167056c62b27351c063c77fc3460ce47bb27ffd61ba3c9b61791b264c3f63c6b00194da13a779141f25732044c688b8c6b42d0b01effa8cb8ae8d65868ac8270a34af7abb29d221e89c3bd3555201a173cbb73723c7750473b55361d6a7b387316469b4cc03811b055be9d0046b0773a03a536adacf494234547f71b5a026d06cc444defee05a8a4d1ef46ac37d4652c4b6a6472102526eb14f5ebc2596dd25bcd3425f66a1236f54d4db64e660ef51ea9aa30453e23d556b04e5cb62cc47a42a028177aafd2f9c47940047f0e9e53fdefc5b17e27f4ab237dca7ec06b740116b4976b8d6a69eaf27e433f5932f027f553acca6774cbff55347f4c2efffe727ddcf045efbd28abbd597b11a77650486780c9f229eb50d2aa48194bde3dda2b7e4eafdf10f6ba3cee71f57cb85b986a72f0b5b19e65cb058d94015a42e7e1811248bbd20694bad450dd03a91678faf1644d7ce961f4089ee7b9d462081d7ed2ee2d436d0a973ac4379a00080c1c6c168f616329b22e978509a6a35e8bf09e42932297d48a85f20040667a7ca707f09601cc49d6542a0c5726aeae63cb8d40018286f47057b624e0639223acbb0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3aef8293af5c4494dbfe6ec9de6bfd288c8a725e15cce3af03937bec83971d08","proof":"9003f05e152ac5bd76a2abea3a84fda41ff3a25b3eccad93039511a8d60618718c1896afd15de4d1a7911a358681e59dbdf52db9ec0025f1a6767b338a6d8f63e0c78e06daf6bd32ae68cc7d352a3dd088564872785fd7d52dffc13e347f8101ecbf786d89edf33e79c278f22dfee8daf9ca3bb4aebfc82cf551e073222811142a5b396d03a99877b5f43ad13319e3c8e602275603d63c902b65c0a52c33d50a0d1e250699e3c2db481c093c3f989db667d5c61ff8da71bf2102740fe2227a07fc7d1b41c7e6e06ec3fd971c858ce12ba9fc5dd821447b8b1e108d9dee0c0e0fe81cbe7718702e54c43aa1f6bfe643b44f40b1a4920033ae992920a8bcfea15d029017735cc9bf0f27b9148c2362cba5efd1cb461770417778935404ced3bd74f6635e68c4aeb506627a872f04791d642abc0e98f401667861cb3e7287f779113035bdfc7d7828b5c938caf469896fcf54016a32011e6cc4deedf4c4a91ee178345c1ece8f86f1b649a41d06923a5d849d55d7e39e63235e96da3b306e93dd4f300c136009ab5db0b966db86a441caa63fd616606969f20f4b58c4fb776bf06f842e7b1d84577733d716a6527aa2e729dd632d7fb92b7df1621cc666b71d9211442cd887fdef111a5853d6240b39cd0e217d7afeeb468e87e948ce64197521258eac592bc2aa7449ad9f08badf3a99c5135db38c7a0e1f81f491dbc29d7c290e74f6df33a5ef6174020e97e1e2542a77065ceb747327be1f7ae0338d861d1863fec4c88c46c571981c922b147733d1c184c89f2840ba2f1ed123383b0146e31dc269ef8727bcbf41660c6acfde263247456ec2a6cd9ebf17389b1da3b3890a295187b77622aaa31892456cd4dc6a865e8038b255af9c2c9612ee7931fed5580a3b1d0879de87fb754ec88e59cbeab050657ec376699720258cb3a03d5fad2503"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5ab2493ab262ae37b48fe72349f94dcf0bfe28532691a182ae49134a46ff6963","proof":"0a9d5a0f78c45a3d4e078d1224ab858a5c5b460f6b0621d13afc1a279987bc46bcb5ae3c4f95cb03eb70f1ea4d9c5899f1043bc50cec48ead898baeb2cddb0366ab0502eaded18ec16b66515a4d78b1e4e9f86ed3d79c6716c82008e03778d0ed8400699a718004e47a446099b0e738e92d09f8ea0107cdacd944636904bd857a8771343943f4f2146f8ddfc8d42dd4989f1727f38ebecb8c2c61d1d0f11ec020767463337fe69a7815b4d52ebb6df25297cf2d07f44d19eae15e7e3e56a07052b295ce083c1484fe24f7f0a493a30e7f33ec8085458fb9634803d913cdd5c0cba2e7d102a97b2931599fecfe1779a4e0d9582c4a493a6c82bdb568d6140a324e244df74d31a8ce75218a51140f9772934ee28126ae88e26ee723d667031db238e275426b50ca2f6ea0eb0fd3d2534a0a4b9920c4f4caa1816a45c56e3b5ff18dafe4029dbfb8d854509f6aa2a9cf0c8e4dbcdd2bb9888f0d13d272581bb355bc218d276dd600f4ad57ec79cde0ca75cdef8c9e3cccdbbabb3f48bfa256afb41c8d4378ed64e5e77ec12b5ad3909e2887dbe095fe5bfab836c93f01bb427126eea7e84cc7726f2ab29a604d99e25dcf1662be2cb1ff1ebac559d3301aeb13018ce064c0705b299ba5248e8fef5dc7bdee34981fd0b8dfcbf64fc7f1390fccb6e74447ba161f6aa61314d80e4f1773e6a6c79319fe09115843a882c8203b8de62e20d39373a1a9f71238bbdcf3e1ad62e116f554e6fe9e7f4a8e653b1e378fd1b50867724e36be7a3859c14fb4ff46b0d1e86dc4a4c0f62db9314c29db88af81bb4ca302fb7b117aa54208af0beca213b55eaefe2bfdc446deaef119e04555c135f178919112a41dc1e27131ee41f400305dd5e964a1b67996216a438d42e6803cdcb51534e74a84cc8f73d36d37553ad31962f92236bb262cf83c851b74c4303"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2cde88e6c2dc844620a1d920d7c4c2333c13c34fb10a69558d60aa4520618c5d","proof":"442c3160db689b852253d8378190148f6a0fba5776f9fd952783364cdc7613710c4b9fd352ac563c1081fbd372c72b12beaa4817fec86aebf8eda8f9d8db9113b41479e6141f43f48e5c448b8d8b61927818bf7072c03eb22cbecf74d2856d7c74538e0407d88ea9811deb293737e6508d5d8170634dca87ece8cc5f26469e146a758e93a3d11ec6b36ca82b220bed6b1f1baf0a115aff5b8456a4fd2a605b05b10ed2028676a1ee8a8edda6acc69f2a17d781f8dd7a9a451b5ecc51971ad401460291509e4a8ffb4b1f54584b3a778d58ca57fb48437021f3895c1619d3610380a6ed17ae6145a7a41b0a647f0d6b30578bba58b54796a9bb5b5ea532c3625e708083864c4f34aa3a51507703175356e9cfbccc5147dde37e1c5a05aa23a9023aa28a89ffc3a721adbe1496ed6251b27dfa5faeaaeeb5116402c7c2ae0f9d6bf48f9d97bcfcfe8145d41bcf3424d34dd9dbce9013f7c344f8474111648fa05e84da969be12bb4b74e5394c73ac5120ee5141664bee0069cd09a3cbe5a223926f69ebcdf2433b08e686856e7fcff2f62869c16c57c8ff9dc454004ee73e1ca42aecf62f3a94bdbe1547b6a3850c781f4dd96a6ffdd424bfd75b2f3acbaf86a451a1be5466e97f52d7a22f9222ca8eb1421e748b069ea40575319fb840e950a6ac09af45a3fc7dc05b3588936d57b7a640722b9afad24b8e9e43b833a6a19dd2820be620bdb5ba0554d52ad53f9e4200afb137e67cdc226f7dc969e82b0251b0b34a7a503b918e48238decf6778cb4212e91c8795b2649ca6a1cff96af309b64ed64ffdb29ecb8806d8797cbbf0043287014bd91c44987a28ddf8ae8139ce9d7162e7e648ff00bd1c81348b4302de84411b6f2dca16e95fdfe0256aa1d2caf30ddcc1ee319ab5188be573dad9f67dee9f518950b003faa186230d8224b2b1de06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2ae91cc18b9e8724adec2eb836f6097c5ff3474676b552b80f61adcdbaa92370","proof":"9a51486284aee12c4421cf44ba0edde1447751fcbe5dbeb9f021680a4686e92b022b46b6d59f6d1c4e53b1f9cfa7c0947719af92818c6304767ef19d4e39733f3ac23a6f8848021d4ea856da721c61285714b14de3518708db14f8e03eb5e42932964941fa593b9ebad6f30e970b27882cad9a5f900879d9f57901be8b4d7c3d80fdf1bb32f5f582ad06293c8641df00be3131c018d28dc5c808893400fe890cb0672709ab39a061c86c41d97a4f1f16018ec319ca39e649064db4ec0c7b3402ae450ce17f73d7347c5a58325472dfbfec2b05f793d5ea74c5667df8d8d872001674d33aa52aa78d865235333be5a37567e5a75ce3543b77261e17416ceedd254870499661ab095add1844d9621138f9dfb0617f796c16381b74b917a7a21a5c22d040fcf8f18b8af738606f2252bb8287b6c0cd7ff7567a749b35428555767260a0cf03bc06b22455683d9a5b6359bf55b5df9932a0add9e1c4cc759c76b415405fdefa639887a18f76bdc7e4839c0dcd2c4a06dc0155924346d81ae4ae2873e467c04dd6daa3e12402613e86b142b93c0e9c8e5426347d19b94f2e501ea662c4dfe269c309fdb0cecae11af30e986f7ffcd5f03594578503b8181571a0a2220e31e5317347351fab31c4f19aa8da0a060e8063d52c3b9c85713d03791bb61ba4b203ab10f0f5fd04e9474d851b00a38e43a1cfea9b7c0d958635e6e445e3099a0eca31e8d453579e2b524266ee83783561ed3cf04f03209fd488540f88dc354ea644f10d6a6a2dab902f804ad19a8bb85ad733e78c67c61c15eb327cb96438b6c88ea426e842e3eb2499791bbf87fa8b04f8207a2a1d4fb3eba0a3c7a9c85a5221fd80f45f465a2ce02e134fb2f0531ee23eb1eba42ba1c5c35ef6c440bc05a0dc1d82b4484fe2d977353cbd60407e72416442c77bdf1a3bd2c5bf4534dc08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"be03b29e034d5964530b6562fc0983ab0232c2113fda1717ad4b5b9904bafa48","proof":"ae753668c6edbb2b7b02ff117bcca08167be79180625033f284df7be8c17e110e481a064e806cd2c1fc778ae5a8239ea97d18d273c5224ab141f412e82e742139ac355b4209bfa67150eafc055219e3ad4d74425d4e9086213def5f49208b1610afda57db5e5457d6edf22a655740fbcf5724339ebf65920ee7726e2e2f1ff03016edfd5cd8d79b85f40597bb3366e2aff28b115c54c562b276bff519c725a0a9455c06ee5e50d5273fd285a8847e88257e6dc235749de516cb35ff655829406ac14b18c4e9f2637b8a145f1b171b69cfae54dca78f47d9c0e8547510355960b860b54975f20ff55a4f74a980024c8ccad4ea28cc909e43611a8a416ae8af244bae8305fed8b2674a23708ab479c13389ca628797b5ce38ef96b8c1222437160ac29318cf48a238627638cb2905937cc2a67e336d418f87202e455e86c107b7ae844b40cc0f6086759450a901a52cb0a5f5321eef12e5927f6b3b988d064a4491e14811ece388cd3eb2f042656ae01fa632ece289e5db6801148868bd246122178ab7f846847135040d9a4706c8e0c368eb1b404800a51bf560b989260ffd635f07ef1f686b03870024fb001ccfec557d51a126fcb3fc05a5acf17310c53b32c32f79f2021cacf4ee61cb04f75b957e541ba3c2ffd7a0400be9d7d6a707b47086cf2d657b7d2221338ffcbe4386da2fa3672bcd0c488d50bdb7d8532babfe414b6e786abd9cd86aaf3c244117292d234865f46dca2628120f8c818b557bcf876fa46783ed2288a88d1938dd6dc4f24d1ea3df4836a9456ac51b147c366fba5592a80ac9d13c52d7773f717656b2be87088a8fc3553b619c4a83f2eef1e5d86793a830952dcff7f66b5ae72035daeefb140af637487f4b616883030064641230bc02a224a07f8033d73a81b5438e9b316870d83653ec2d24b515aaca8f18c5602"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"286c90c73818330ca83861807e5db5496c280a237d725fb83e9df066aed73907","proof":"b41087e945ce3e7be158a2fb2fcff8edb5f03569c353af0b484a5c3570fc987ce4275f7767280713300541c7ac14668ccb444ca79030325da214938b4cfdb801d221c016bb37e34815f5e1bf0ecfb928d96b9498cee2112715804e5ead56d55dc23a974876ad2c817cfa0c7156497780b44f9d479095d76b7c93506f36e5145d3e13f2e6ebcc06f08b225115c0c89bfaf2d85afd8404e8225a859cf2d4bb1c0b04511507df5df95ff9bc2afff474074be28955363a9f1f58be968b56fe958a00f77da58a8f0c769dabeb63191803cec9b7c44b376fc1d1268010c9520088930ac0e9b70b6715045a8dcd983d28f4b00310554d6801ad09ad085d830bc535c44b2cad002c7396e9e53d47dfe0819936c9564535edf45cf6193913542a912f167cb2adf93be654130e3b139e8ec8b5dcafd326cc0158e45f1225e68c5b17c2bb4d6a063db059f32bd4bba4e90210ec66a5949f0599fe325d4e6dc02614a299085c9ef795ba4f031ddb7df6d1866d61e335be623e2951b5f5d67ec1abbe06421d7e9c612e25e052076db22742a95e6e0c354a055d4209bacc1c87658d280269913bf4dfd060c76c0791d6a8bc8625ae9664cde3b79ff68bd6614898b46bda7f755dc2fb7ac3b35e9e54b60f575c2529a495a1aa105cce9715862967930d315b8e45549e635557e97b75cd8892f672dcc0d03ca3d805d89f490fa55e3d754c5e2c6626eff692dfc383a009119de9968bbdaab04a84fb13a75e3e14d51eda3c9ab402c0951219c52e339ded658a8f5d84d9ac3ee643d912b0eb16af7ff7bfa994407bfe739fb0eb543614eb1e8384073cdbac04dbc0a72066924db883f1cd7d08ea6ec123ec6f8d20b289d51ba24fd4a0f94d3cf846b6c7d3ed4285ae52fadc962d0d9df13c3f86060803264ea4f60dff2db0fc45baf544aa01fff843d2851a5d2d01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d64442ce805974e830a3972a231e39dd1e7a16ae1f181633e230df8d68eb141c","proof":"4e177a0e06273f6afd303cd5f64e464b0325bd622330edf4dea46ab44536c96f5205eba3f0bb9e126a539f391f60dc128dcc94ff813b40d544f49d2fc0c555323012f94776b0c69f7034e66f3a89d2a9525220674a54e88c1a4060a5a02fa46046eeaeaf3af85116ba7fc91775363a196b862d65f437ae40738c4267a270c81cc20444546a43e16a9f13191189fff715b621bed4cd567db73f874cc0785b3a09cff82aa53cbe6e0345d7f27b18d4d252e6e742976582b89d55ee69b75fb98a07c0879ad95a0c1aa7abbce3cb1f0f96f3bf48b6e657a46d8ece4581064036be0384fb1ce75b9c896be3a3b3acca6d1de6b0746d93de67d765458d69ce635b1c75001ef53c5c39fd8dbe1e56d1471f95b470673c20d8aaa3207c6351180824d665cca53b3ab8a2e599df0e518efaefdab4328b591a2b93aa4b24b308019d3780697001436a0e3815c0c8ddef66ae0562dfb052db850c1371b01fe82b2e27497c266a38082c55a3f7427e81ff214847fa94236447a0cf3ad8637a602f40b2855125a6b385de214a89ac099dede35eaaea995a871bb84024dbc2b70bc1e33cd89258cabcedbd5e4520df9cd58437329f04641fb7a1ad59062f1b0c37bd24031d635dd65fe4e958185fdd8c5c12eab56109271de4dafaa460a0c505aed4493e97464a0c0dfa002bc4465189b8326baf4ba3366d5216a10041efd7c06f195364764957c6332cb2f378e84062adca6c2684539abb78f6995e3178e3a0cdb08c5fffd85bf6431f7f8b98e808e6075a0122fdf0fbb4574db21387351c30604e07722d303ba4e88bba1daa067deb386174538982e7d4166c570966db45983ecf198170d214edd1bc6e2cdf60337f6411bf2e764e1ed48872aad6abd8e1403f70a01b838c02057838412c5609d633debb7b6154591672938af246767d3488e4b4c713216003"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1265431c662f7e4320cbf4556c53d46f3bae6ca2b2eed84d71f670d921f55f47","proof":"8a0e5387bff3a60b7b5bcf34cbcd8056a41e3d67ca9b0f2a89ee598d36996650a4bbacb97bcf8f4730d5563854676bc62c1c671c4d5e3fc461371eb19a64d35632fe3ca5cd768b629db3e5cc92177ee65c465649c972293e6f0103e0ae0f2108c20773f28d15f88187da7495f8a492609404db6544cded26f0205d9b29c1eb28a4d14caf986ca76334ad6d35af7baf63f2f141d78b0e8178181a787b6bf9fd086c1a993d1ab41ccab6de55ddcce3fd328728af554c34411cc61480432ce9280db9a8fc2a8a37811aa6ffaa6c06102ed4b9adb43adf1931d5e572e2cdb2a83f0b82e8915d6d7412d6550c519f7c68d5812010b6a9d7ae7a7497de0cbd103d2977a2cabc5f6bacbd4b52f5009f98f1a4acbb7187af5e69928a9303126d69e477478808446be71dd0cf429544d7d8b2cbcbf6077783322b0dd824304c81c33af203eec90986e5b07b2c8e716da69c5cf4e407eadd65fd27a09677b6aff6462c34769891cd5c3de5ec4ea09c9fb89fd1167d2e706654a0e8dcc1749523edfe92777cb0497f03f6cf505be5614ef3406924b294c6bc46a53209b242f2aef923215b20ce9538787fce8ef899f5620abcfade9d8ad0e446aa582f94e0bc4c0a160d82212ae827a4d5635190be58a0a9288e1651c55b1442fadfef0052543ddb43ba8a0420dfdb48c5e66430a51a0a51545df973779f1cc6e38b1e6d2f16c0548246d05e1255a910de96639cfefeb219cdb6f09c43e421de803b89f89eb9f475baa1b06dda7590112f3298482b24028f426c6dbdfe22fd12b7ccb3d9f4f98fa0d55f5e7b40beda9a2c84bb4ccded8e49efd1cbb046c95529580e388170a6c40a16ad04150861ea0c67ade28858c637e10e944ec7cefb474521aa7494a481333595d2a509b75461931aa000228ca8811db5cbf1f95608373a439e7a79b3ca143262164e01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"48ac25790312e37e7ff3dbbfe7b52daedb385bff78e59a335d36e33d83943c1b","proof":"1625cf4ab4e5efb1419b63d039ddb158e6d0f48a0bdfebfa09d48519d365d37f024d7e64df76417f39ee436b0bbb53e8588517ace8fa0d193d5599afa0a75f4dc6b96b3c900e2a2163e1a81de07927aa958715028fc66a629f34b442a91d095590ab5812cdc1f8121de4327130c975407f7b325cdc40581fc303c827bf60340b2bf7cc3cc56c06eeb1f978f19b80a35ff28e0b6bed5d3743df65592227150f049a7b402d3ef614bd29343fb400067ed9ffc41811e5eb06ad2792c8e5f102a203b6e30d35d934ff9dd620899a65e2134f52a8565c4d087ef5b1e56314d9c78b0f4acd813450ad2b1fa1afab26ed7e57352cd3093be93a300e6beccc7973fcdf6c5a9150722d89ca7ac0c2b4b3a165ccb90be48d82e24b197e774e852ebcc6974e769a9a0f9b2d502d33d8bc8531695ad4976005abbef2f30ddc6533940c8acb7832f524b67320eb8e4525c87a4e81a6345f7c555db9d6f4c2db6391858adf38160ad9675baa33cfd163aa43f8702b2904619ea95939ee5caeee44fc46bc79343b72744d4a83127774abcc2411845c7b1e88d2bb1401bc0d784e5e0857b7d8532316a828923b701b4a3259d438d197a0e9647ced9cb5dab3c300634e9c6912ac141865fa536b6545a2124691a3c3f5bd8cad506af0f57f6b40e6b7886530be506fbe337a92a36c79f39ffc945f5e5f48b108033d689fb14685893c8e87daa3494c5c3c4ae9be5eb008d16492a01c0579e56e59eafad5563aff7f148626804bdf5ca8cc1e1479f95f2129ab59e3cb4e929ebac0b71d651906d67ce9846ad1cb5e47aeb702c73fd439b5c7ceb78d9f58409e6ba16f66439ce6bcfd3ccdf087c1075352a97effe39be43f2590eb7cefe46c16d09ac445193864c5aff621df3ecf850f8280c8e48b7b8b67017829bc2e4193b4cc3732bb2e72bcedaac8552b4f4fd500"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2224d73e73f931893ece853b9db0cf1bc914dac728d4707919009f6cdac1cb13","proof":"78153417f5cb764fe16f194ee6be6065fe732fe09648cd720a02ae48e172197aee7591739d7b7bcb2e0877d133c6736a880cf884ac8b7011ac82af618afa6614f4b20dbb4dcbb49c22cf9c21d0906ccbca7776c375c85fc4008fc0fdb41dbc1a340caf814b0dd6a086abcb1eb6b8a1075b1268fc0b0c222f57d1966e8c26f12fbcf84599826b9d8a1a096c945c371d9e5822ac7a13a28dd8ea1bad9316173c0e9af706827410a91488271bb6edd65c1a2d79900755a03d4a0e191e4a9253a50390caba1b52d49eff44b549d99e29c29f5576c379df40af8e562d2883803a3302a2b82de3fa3df83c604e7e1b424be12db79e0c4ef37f1eecfd5381cbdf00ca4d2cae838473cfff659a13eb286c072d69755d8dfbd34d58e70a14b2f2eb33e426fc4d70af3b4f380ca8843fe62defa49f7e33afd71222a022876d414b2897df50ba1a458a1334204e24587f322f2f217c4c13b0c337b0bb014b943b348ce6a40182fcff872a2856a1270e9e8cfbb5a8acbb00ed3b7e547ea61f3408dc0caea65c46fb3f9942844ddfbaa51d10ae397c25915d3daf44c2bc1fa99c5a7ea75caa7c3a6b5a4d5eecda30b539753201f9d82e26778bae004725e384f79ee595e53927dcbafa57bbc235d325b6f4d4201d963adfee5395d1032c4c26d8c2d4f5373b40bc4b0c1905acdbee1012c17833afea2051690f1dfcb6f9772a26b314d426717626fc8f3156832a1c8ea112d3492e98aa569a338a6f1d89c6cb07070311973e4a68dc3fe1a520495ae9d49595e6c28edcf6a0a25c3f7d683b3c3160a6a553083d9e47308feba22c3eaaca9a9d1268e098a0be565d8483e711a99089e625092f73af9c792e8c66def1fd1f5965b5c79ee6d433760120c79c815e7a5532090d98098b3b569649b5efcce00c9f940d03b0a97dede701c47c533047cb472e3151b800"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"febea17dae28b7f5d759a9d200ac80f103d52dda9813aab9482a3a7ca35d2f1e","proof":"76b1079bc5b54a6c51fd1a3d7589081a777a9ec36dd47e4ca39bfd1cd36b8658568d40fec11d4ed0e151378399efa796d1805f5917d618cd5fe820f12345630976f58e478cec50488de8e2cf2316031bcfe639cfabb4f7248448310ef9bf512ddc34c2ff1b61f5da1619a754f3c09a01817635e542f9e7c933e4e332ed6f37212a2200cd6ec963b86f839e6f00391cccf2339125a77403df89a119700594f70581523734a870b1ca625671adf951b9bd02316a30f150b0319a7c5c87fc54bb0d6cec01eef51700342252ef71c544f09f3d571109b10a3a5f695fd284b56a1b0a36e3e9b1ae15119840c4badf1e91a34f8b1d761ab3b85df17cd0800139be7c44ced121443cd9f97bab82cb541604e271286dbe47c510764e65d0cc045babcd667240f508a97a929a774b61be2b93c9758d1fda2457bf7c775d2ea83f7d64d11608d69314462b0f61ae8106bca9f71489ba9130c8f459319d11ccd68c0c8bdf6ddef812bd877384f52a84e02e5d3a33047eca3fc892b6afcb35577e836981fb551c37eeb71df4858566ab5fb332bfe79e33d25cdc51bb36649b6f8177bea2ab102a508b5d644d2f3186e61e6dd1f546abd6f432b8ccfdaf3a0e6a01907edb1776d2fa31554a29bc065f5918a3eeb80e149df55b69a16aa9a7dcb1658fbeea7b190e8dfb25119d3875216f3225958637ead92169a934355f6c21d0930a0edd060e3cca1ec7f024d622dee53c128b7a32136b25ccbb75bd0005104e227c3aff4b097a72d8be8e4dcdc275594e99a6790dd3e3e2cb5c90515dc4a954061d15b1eb2260a30bdbc912ec7d22c6dc8bf20adf8a895a479df4aae117199e78aeb256ac1fb2ff68ee03aed5bc377074ef3a58c3ed94e17867b0e292ad0b1a281bd2b02b099ddc5999d4d24018143c2585178b812c9d7555c42085350fc7f14290a99b4e01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"046b4d478edb912ecec8f355c3db33251318719e96a7079e6d960af32fbd8552","proof":"1c28ead20ba75b8f43ea529be3b0526842518ec2866963dc65f8da69d0a94c64fa8cbc9e1cb4eee00492922a96a60dd7f24eab14492911a8639fbb79b216fe642279c62dfe3324761989313371d76e9c8891fc9a5e2398f9dcf33b2a66523f701c3f3b49f49bb8d7a9022f673d8143dada18faf5b0ec7965c8c1adf42a760d6a70d335d6e4d1d233fc113f1eae3851ae381c382c001ca7bbb1838769c9bb0c0e93555e872c5a2a4e66691be743cbbb0db6448154c1bc60a6a23dcc7d4f39d2006782415fdd487e4f05e57991d679200e3ec39126fbbf531531bc1000da11af028abedc1342392ba2c7588c214eb7030c751b203f5452aba9234f4dbcf09a9c3a560329eb0b48dd01651848e18d94405f5f4357e58573da424fadcdfc20fe8b4162321fc98ca62d7561ff63579e69d8eb07814c32e70e9ef3dc3cfcc13aadab69425d52bfec81a11a2a86cf4923639de894747b5e5444be21b287d55dbd14c31b4ae5589f2191888e13b170636e1e8742151fb97f083d1a5d5a028e77f5d9732aa4ec27fa3a06cb64bd35574792204fc70cf10cd576394f628f9181e34e91a16b00b80a7cd4ad9ddde8e0b20c329c32403d9ec7eaccc84f25457cf8f73576ed779ceb2de1455c60505fd62b44a1fcdb8ac59025e9c1e619c5c1c9d7a1f815b660246facc283f941e2962ef19ef7f1cc7afa8868110299ca40b81fd8d483cf12273adacc6771746b56bc7f68d8051e863c973dfcb01a862d84bd646ce94543e970c8b29ed1d6d74b7fd985788735bbcab779e1df36166674347eda2472bb798d1a60b8226f99ed2a5ac436635552bd8a4e051b9a66e2ec5f18da2d88b0afcfd03f39c13798119d78fa3b32e467690ff9200f595127a7ac9e08d96bb8f52ffaf5017461e91ea0b39a96697a873c64c23058aabddf6baf25522ad2a223bea818120c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2691a7f8a9def20712572c67294233179e4b3e3583490d6fa11d0368984c6f0d","proof":"385779962df6081a0fbfdf43f8c1d9d6e100642543b26173d9b6317b5f77bf6790a89b39a39af391c13edcd9b446b15aa91b01018190889de82c49231ef6865aacc32341165b1a4458d1feb2e3e31ddabdabd7cf2ed01c73d4cd0c7cd95e7215c4f9f59f77540d67f7b0616e665b93b0c1cec829c5ef44e0c89dd485667add4e85ecfe1c9de022d435dd0fd7ef28fd1776c45626d77b4a443d83aea82e00b2089575b10bc2ed24e0de94358664857d100ae6563e5898bf30cc1f36bdf351fa06b232ae8db652a41c79f7a436ebacba0f4d82f325a294252694b6a8d2df5a6c0704fdf8426bb51e0b718e03bb7cc56fdcbc19b3b245436dcc4cebf206f604da62ce7d1b7c96f7d2f222493e38cea2f7cad75fe9c04c1104ad392358e574ec5b7e364542718698146ac956b4fadf71e855bbc43df5e48a04213d776ac84fd60126a2585e9df5fab70ca9e0dabb5af61c13728bdfd803393c9c754ba6967e81097b04941abec480f30d2999cdecda561dc77cfeb383506b68244338264da6ef94363c286c74deec1062a651c25141b267937352fa66acf6a2f4cd4859543e7d7e15e8d8f1f585a1c4a9fd9146e6476614b4c5447c840cc261630ea7b98dab31a636a2561e834bc12386ae693edf9f946f1b877e7f09d21dac21f35f23c8c0bf1c557ad4c6ca7e260833061b7b246cb87e45ef014c03973b586c7a0cd60439b1e557eaa5bb04a38765a004c1e1703a849a934eb8758108eb6f162771dbe314640e367076c70c56be8e553e673125eb4002254fde913a4a4eb1c721bbc435a06d3215f8406ab0b6f8bde70e71cada9051ba6ee2c49c9376e9036913a2d898a2d1651b8901ca1e725324c3681110425a865d83e6aabdbe63ef50814f2783123dcef70ed18c374683323bd46a02e2a2fed07c113d847438857b2d3d846b3e8025ed0205"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"608e57630de2e5149ad2931c1755ea96a0657aeb1db8555ecbb5409fdb129e56","proof":"1c33496fa3f3e65253a9392e16ff2d90302078f6693f3a0a748b811751879c7fda148f5efbd406e90c84f53c7a715c84b5c4da070602f4209df34e9c931cee2526e392a07f6dc6d0255fdca9ecf2ab28f84ac6f1a4084d6e72813b6c6989cc04d459f33fdfeb251872da45fc649dec136a8b3a1589ceb0f1a0d7ceb734dc0e4d0a85d7f9cfa9cfd8c3bd7e363b381351b35e1e16639c6e763d3e76e64e5640079c9c7e3995ccbdec95406bfebf4984526e093a2d3298486824401f1844d4b109694b65e3b6e66400297c85cace31dee5bb7277cf16441546f457a12ecd18760c5ac4f563713fdc05d06e1f0ae123bcbd159bdc093f7a19443e6590a9b42ac91fc8f96ce744c1541553fee86ca3121712a5b29482a8ac2a9812d26c23ee03646be63e48fa408cd36af95762b10a8c628e1b270a870f9f834345077041c065357ef8fc9cbbc67ef1461be356cba57155181806efba5fec9a6a87ec16bf256fcc7fba62b3de530d54d3f48df50e03d5b39c544c693fb2ced8a852074a9db44b6919e486d1c865e10a52b3cea79123d8a27370808702644741907d20b17b3a69da0c182fa9b234567e636a1a9ba8ee54f2236161eb37039ecf204bcca5ed42f8012102f8f20b5d5e08bf3f77f8f8f39591d3921ae9943266018e5e0b5659e75e8250a8e559ae251848a7cdbaff2d7222447c43c943476767f4f924f57eceb558cf21940debf3c0cea83829167e9f98e5c4786e7bb4a100134e69fe9c66af0829735526e5bf04a136297acf6de5f87856b8bda08fc31d0fe3d84bbc2b26b887049b2fd0b9af34581a696982145da173b10d55172dcc7ee5dbeda1a7e9291582f7202224df37cc9fc82333c076993d92a899317b9a496894039dddbc4c0a4c4af5c50edd8fcbec248b94b34e59521999290c0c1e5f713b21a1da5247a2c87d67cbd707"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4e3a09691dec02563549754a92442c159b10dfc93e3e918fff79537441a1a54d","proof":"a81e83459fe058053e37d1f9cd396d8712d37e9cf5503dbab3087ac6d3ff6d49fc5975e93837cc6148fabc3a3a944f9a7fa6ccbeb749228c227c92a262d6a65790e151f9648d1825f8ff6f24a87ce47509af72b2550ee90acdc87ad3ed63444a8e9c57f58842d9ce0fc439439d137b2ec9b993cd0fc374df15c79c76b790da20133d74c9b8dc65a054851e1922e6cbb12ce05e1f7ec1b2d999ceffe68655d10657b6c3706a3fa66977af5c159e6273ce0f03597a7e9a5e61641bbe0db58428050eca79e4787761c5c17350de36227e497fc18953616d3fb177b4269345f2650b52139b3677a184502228c8c8246abc9aa632301053320441b9fcec41f5a3d66d2cc5405faa64a6da4f81b12f09ce8dd5029e2885535c8705c554cb6154dad27796fa7fb07c09f305b2c4d1cde2050623f206eec2eedaaa79e994ab2e97b8731f2eef500896e83096da51c2bbddc3717ef82686a8fbeaf8a4d47ff50527c6f039e88d929549133e50a8fefd59887e4cc6f9109038147d850686367668a8e068776a28a48c16e22b097d1fa6a672bbcd416d5b9781fae6736db5ae9ed4baf1f750bcef96909a6129c9fe7f0cea5972d1e9942938abac0ea71c1324846c789a6066cef3b810b75446a07d35719ef4ff957439605b95249fe0f092b83dcdb5f93e019aa5de8703e028859e2e60bb473757499be157ad9ede389e2f2744c026dc2c53100710c32e4ff4c1ea26d925ef8428ab041892e31d7273e5a99f47672ac7a05bd8d21cfc87ce84d946ba615eacdd535d2a614f6ca7e214f484d5b4888ed57d7e10dda7506f719fbfacbc9c440d1f940495bc02c9619b4afc7c64c6519745cb7e0499ecec8274e57acfdbc17660dd753569bf0192b34a3584faf0f7e9bb7b2a00ac0e810dd38251d46470a3449ba71e8427af0adbedf92967574677a52072e803"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"243dca9aa18ad5a91b925bf6309ebbfb24508a04e708d13b94b3112faa52d629","proof":"92211cbc587d5f2621af9c1c477b6c899cd62f52773fd9e409f2f73db6ff441eee4b91a3eed5e5858b9de6237b1e7da3346e887ac6362c74270009e7fba0f45948066f9cb1932ccb4c7f5265b369863c40f6641f30266bb3434880bf50f1b9351212b4e4497e500c62f8aedff80dda16699343c8f83f088688b2d0279ef0c109766962eca243d85da80f57c99dc4142575b19418608329191582836796c34c0d7b25e8f65072a8b8174369575bb9415185cd4a0fa8ca8708fcaf973ad9eecc0543583ccfba535253eb90231d276288873f9a40a54cbdeae89a06cf0978d8ee00849881f896dd04cc99e90539b60b144538d6cc2b679a58e2f76b5d4fc2d356505ee066bb441b6e872c6a624e61681887adf0ed124fb6080206949b00a954c065204ef89d002f5450b17a1f2f48e11ec674c20feb171c9496fadc66d1bcd78e159a9f83147d757f07fb2da2d495dafc92ecf53033f336b3fa45b440a5a322166ba2063ffe14249bc95a38554d8451fb20799fa0936b1fc81a5a7d66148155903ccaeba017a0c738f07903bce44927392623b33194ed1b9ece6e511d52e71d717a5a78b8663847a4d420630f8d3b01145368bbe461535d7cf7c1d7c35960390c0c14f0862c446cf9a871ac675de3f571289e02e9e62fbaea3dc6509343c4f885472c47f085ffd9eaafc8158a6c2a722acc149da29f8b7351ee782d4caa4033de1d3cf1898b6d1ed9c50eab5d55f4facb846040871d461141bec88a4a141eb8143afaf720065a6f92f38ba97e8fbf402ce396f837755501c6842c37d30276dd9d2ff2efd63f70b73c81d0d8b8c7f1aa2e0eb13a7c719c320335c4c39b253f2e6404659f66768ec35b229aaa46485a2260bf7f91d35121f8fd9b9e0b22121230420fe7645cd9f4ddc37e0d38934a6a6236a1b4f4a09ee7fafb4bd5c0b895ef6d930c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"58650e3a1841b6121cd7b375c4cabae51ed1a722a595e999223c4fa6a7602e43","proof":"fefcedd238d09026f153035ab5e473ffe1e92c49d6ed43b7d860269c31e7000628a818706b8c42e981e9b2f08d40278b6169a6da20bc42312d60f6c58cb12a01108678f3d9150f9a7d85cab8b4cbe517128e1575bebe2b90e69cb1928034153f8cdb077ec62b75d655f727470c00d27a99b6119f81a609f2eb2fa3eaf728ff2ab08d8368082df3a78b51a824eaf2822c38bb979fdca6c38376bd2e912ec8700435faeed272ecf9a9759f373b276081015149f6e6ee3ac2e96a40e08a6e5bfd0494d9d94703f1d07539322113e39b35c5b82df927165f209ed7d924893f1b670ed2ff08b2a299cfe97296a2ee0cad14ff0d9cf2fae0aa24a2686e94019aa4562dd6ad8b98e7666e2554ee707a73e1d2ae834f3e4cdc540c3103da0e151bca522cf41c9d0d1b25c73873aba7096d82d5d5aa5c975f5d8c73493d0d4338ebdb452df22ef015d78de629cb7f6ce1ee04a4eb5f48799380844468289a71ca58aed7023ec0586122520e0c79e85ee70d78e488b5ae89862edf7107c70c2a386aff68695e19b6824c71d6628de0373a1dcc82534a848b589c8f632a98b18e4ae3d9a14d3ebefd5542d9ebc65adce3371227f252280a68a3bab0dc0cea6713837cae371e443fd9fe380cc34f5cab388ba02d471fd7947c566f1abeeab89bc4f15f590e6b72c1a2bec5576cb64addd3dea2ccb27e2deceef086fe35817528f840f3736c6d96b6930f655ed29b48b702ae97c71bd9627be66b6f2b16ac79add28f997cb61e18d63aa1ac609bda45d5b12743e24497a74f0bb2699575b3cad301ab446f9558041f36860499a18d20878630dafdf8282fdf3b1eb5c73abcb106f78149e3c53f6a4536a0440b56c5ce653bcf021baec20ea5620aa37ac369193ae811db9195009a5825e375369f864a822716babeeecf06587892dddf284e13207a3a8f012a00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"58073f2d1bf5ba1962204958159d83740d7aa88f66f73a1fefa8245e2014a80d","proof":"a892baad1018e8be66e7afe5305cc59707eb82caed2d090db4d8ac9291c0a506607fe0b78db375cb3d2d8466a1a571049fa4f56fece85fdc483d53338518494faa5f58b79a0d0e2bdb9f7306bd7f785aa46fd458d91633630017bf3b71b96f5ac46d11561af6ae49e26a142d0c8f5ac26603782946394c53177eaa6239f4ad06fb1a0497932d79ca97dcd48fd96b43c186c39d1670fbfbf8c7ddd4cf55b29602de7dd41042e4ea58b1ac021cf16b065a79a55041e58844421818c083ad8dbc09ebba0a2ed7bfa6c43c5573c5aacf4cb1d46ecbcdd247b9a79d30ae87ad08c408164f29b46098ff4a2ea8102a853e2a808f4dfece2bdf946c2dbaf50b46f5ef289258b48fdab77027aac5f5c9af145ae0401d8a64a5e781fb2b7be56a6bf37c7472d74eff9d9da0041c465332237a591e9997a469337a508d7801f9dbf4261160e07ebbe95ddabf014c9edc4e7868951b2c6e737ce9d1d37c9a3ed699d902de069c7a045f8b6f066255305bdb7f4878e71cf200f9e58feb40838347673c2e803f08647a814443ed18f98899a464ab6210298ae8d4f56282946a192d64313de26a9adcbd21470fa6d269d25e433f664b44a191ff4641856a753afa5b475c75742054fade5721937cdb802244b63b03bc67ca106264359ff131a27302a96e37e34a5e2732a47f669259732c945108b312d08e9837d96930dfaa74d6a9ee44e0845d5418aa56f851595fea12e480aaa95352803023ab667d5fee610d4d8b2ae1050ba4f3b0271dbf064f115a1cdf172c235c6df87988229ca7329fd0a358a771e738dea0d8e2dd6723b22a837429d8db730e210e2d39dcc0f34b929ecb966744ff7c3d9e50460cb88b58fc3e7994f80de8fce7e6dfd5f24be223cde1e1a3c9e60a0b02c16cd28a7bd286fae894b34887ad0415d2d5f0f22674342380d450a4500f0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7c84235f93ce38658886ac9d21afe1d08174c32faad5ccefb93954d530426d7e","proof":"7ee80e53f347e167a2682f30186e5d5847029099bebc601e70b8bce5b47a1351f09404a3c131cc27aae0cd13b0e0d456c2a31a844f65d87da1090f9274f5944448ad0225f03c63fae9af443ed3aa33f5cb116e3c58a4c17c43d01f09ae8f520c60234bdf8c7f5b33c250c707c6b1920a2ffbacca80687ffc5915be8d6cf7ba24b25adc8da807a701ea8d734fc1149c59a0a4f7674116fd8914618fec178c2d037e4798f489c0a6dc909a634245243317677142597922ee5cfeb21435f235990ed206657c330ecdc698dc70d6f02ddcc8558a07896134fc73346118b7fc04d90be0651286a000fb36b60cace68322ce51efc2ecd94453eb431982e83fb9371c497c9333b3899dc776c5d2fe2211e8aa918f64a53aeeb574d9342b9d8a4e73d441ac1df5701896f94bf0bf85fbce47a864778d3cefd2a4a49722f8551d86020d4028481b3edd2f863d83aaeba4e8aa067cd85ce2736601e587327ed3c380290745f477b941f1609803305af017175a97a98d2a434b655f7ab62acc63f1d3cb210afe553b9181be9cb3f483c5c741efa667b8644c5f72f7c955736f3eb48eee2632e4a780eb74766fbc849fd2b6b1d3f20cec4125b58cd7f5ca9804d7c660b57b1d2060d8056fd4534649468592aa4ae222ef668b4f72e7565f846489c23391d9172ab25d71ec555f9774e4d4490680b84501f466b739aed0a91315658f23a4326170a4f5ae80a77bee801e75a2ad2afa2b9e7b548247bbb30e07d53db3d6d9195c2453d9b646e0268119ccae855126116e02844f5bc32f8c7291794378f2df8174089b9fab2ce86c3e7fe622deb24f8e4ade911e5d75c18de017980b1aff7fe934eb20f0ee595f7da17b193a7bb8f55d3e6c169c1a62e46131aceb6eefb4a60d0f83cf58d831dc74fd5aa5aa9d621fc2f82d455dcc837ebb256cb306f20911a400"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"48739fe87253609d61f34f3bcfa89ceea5392ca13b9bd8ca2a5395dd9c2b6354","proof":"22350a342867229936cfacf70e66e1eb4672c738b194d49e02ef2dbef9fdc56cecc34fe710756d21705eeac1ff51c6d62376ca8ea10d4744bb03017ebd25002ab4ad289536df571789cedfd323d94a985fba4a461102bbd15120f501ec3a222474f253af96e98efc8bd5fc9f7840c28da2a7c212c4705ad1bb27635b5a466404f214890ea1134253b1edea59ff9378499114fe69e1925876adb7997097eb9802b25ae16486a4641fd8e73ef359c490d2d2f6ea1bf3d90aff4f83dd807ec9910722ebb1d4f1cd6552a9b7a79d09be130d53255a2de7cdc3060e1e72cec1a0e70016cdfd5c0b9cad69e622d747e4723a8682d9d2062aa991b3f27c8145f236372bf4d90b9aca788e1d4bd09ba59ce3362d75f00604e43fe962583988c9d3f2d53204adbf793d8749d713ae05d410ad061dcc7c3cbb4d04f4a1a50ade9e511dc777b0afd6fb6ce8f9bd7540e69274aa7af16bc3894f7bf6a69f0b308338d7e1010256ddba6a42e6666fe4534b3fa86d6cb841306eacbe6495be1de41e72e933d340badfcf01f49e11389526d9e88cf1abf4faa0402e3724a650e8dc0c3676a600214299c34084336ecc03c52d1251cdb471b60cefaca1431e193c7359d2de54c731b8b45e7fde9d5f23e03c221a8014dc5bee71cff0d93bd2fa9b3018df6da89648888533ffe35eb56dcd5d7b920bb4ad57d8da36a69372481a801065ac4475790bfa5a2e838fc25219c0398ac4fd287d758e8dc8cb0a6d2d96fe411ed3a0fe745a784394a882dee337bb88737093089f2d79b92ef725a9bc9ebbb529153e68f458c6967bbed9c62281207a896de4936164b31cbda5c8f38d85f67d1c4cf03ab015c7e0b5860823dd2b53005fef05aa3ac7db2df28e0b9e33bb95e5d038195794091c63961ff64572951222f5a86693f164a569de3fa245cca2e4242f88266a8301"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"462932d06e6ce62b70da8adfcb109749a45e702dd494f92e2fac83a386c5836c","proof":"c8745340a50a5c62c54e5c2e32dfbf44c733c48b1a7d917d957c6439f6f7ca1b6e29cb5c08845ba19710c987d10d3c38cdbd5a0d4444dfca2cb2625421ab2f5fa67bab28798b599a4a00f8862eecc8ab070e644345a9d243d818f8a36d3ac159acf2d5543d1b5258a8e1123131d21578df7d3e753e0e0a78876405e915c2f40b6748afa6c386eb71c33767ddd52cb536c09f9634af989d4a675e5bf8e9a808095c379b2dc881f575a383a09cb872953d6e3facf5b95b092b8355ecbf3130cc0d5854394d163c0bc11008817fe29c002bf7ff54a353181b169f78fe34a4cc6f032e173103f6f66aaa8b256bbdde81b0a767771526de3a8b2f4697fd3b12d1db0a02217de408f274e055d0f5c740a8fd434a28b6f4fcd82657a1acf449e9de2f711e7e7a275ff59366ba03a442ed9607fd10048287b6ff6fd4021d3179710de5065e62aa3ca02eb4c70f5ab1b67558eb32b47e6f374899b4fb7de0c92654637343585c5422b66bb959b3406c00ed5471427912cb6a5b990908a5f9e9c8ede3dd65c8b0145a725a700a122d73a446e1397e29ada40ea950de1b736558cf8ae1015bcc9fe95b689468ef7db3228a7e517d807508329de56d9659fecd7008637e4012aed4f53d805b6301d82d40f61757652dd04bd9c6efe99a13852bdfab490d5d3d189b1841ca2f4705428e12a15d2b02cad1ddf8f5ef91c81b60ecf92c465e3e51e298f95e6a87079838e5074392416b2eeda46666c1c0af4d452ff96dadfb8e05082a71adb2e4cae0f3730ed0fe60caf3e16883a2a9f1aa8c26284ce4d1ccaf780ac9461827d4e3d896d315cf0ec4c871d71d09970ef076c19581fb3e4fb14a26eaeac30145051100709dbea2c2209da20acb670f7426b6cbd451e59f724a0100c8396c5a24e6ecfd1f082783897558ca3a59b754753366693c002af2208ac906"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b61af56ee8d9061ea9d03fd5d93416e718d3e32373496a2dfa764f65ae3d846d","proof":"52720c9e937fa313d410cde25322077b47b7187c628984fd658ea8c05196756116684aeaefc33d89d3f923fb99d696e1d8c7e0bbae0cb769bdc7f4f2b59939679c9d1b08520f7dfb5e573fd37fee88f06770a9dbf58212661dfac0f9a410794702aa80b9f59711bf96844a1f063c7411a4abcc20089e51cd7b600348ad1e6729d15324f11b8fbe406682a3ea7ae21446934cd50d55021796d6d1034c18187204c1a6a720fbf867d5dc3f833a33fd56ef9d2be5b926ccede036d2e1bfeb4b2d0c576fd9caf1d22978fdd346ccd65dc192f69cb2ebd95ab0ff42a933c7da52b50b88bc44fb00e267d5e7737d4261923ca7ba016e83db1d01f5409d28128eb732219af576f17a17d2f0dd306a4f1fd657cdff772a24836a4553c5a0dd7764d88c4336a6575f50aeb68240f19061ae1a0346392be0dc3fbf9268781591038ae378459ce8fcf14800727a37307126e29621f0881f1bebb088285a0d97eb73218abb56b0de8704a354211d07bfe22912f060a86aea36dd7e67b53b922bd5b2da9a15079ecb0bbad08b74aedab57ef52d5ba9e73b2a80d3d6aa57dfeab028066cc2853002f9f72767c1be13ba21a01a9889809fb10ec96ad6a3b22e32bbf49e6c22db67cef835ce4fe5cd38fc65d5ff004d0f71bfeeb38cc66abd3daee47612dc27f2631e8425a34b25562cbd9122e45906b7c071e0fc504b8de5c9c218bf81c8ec6727d85dad25def24314bc5906aac18bc86c2961abb60fcca54758df3a44d3de0b7dac0f7d64e189b012b18e35022e4e2c4e1e8875c7f52f73e6d15cdff06b42fd117c596b142a101b069827b2c1cb623249f9386241a03fa076f3e957f95b6b4e4143f7a1e9877bf1aa29f61fbb96a14034e5f690bf74750046d1dea7796b54e40aeda67ffeeca82bb7d8bd9bacf9cb9fb39aa6d08da35821f55295d05e6093ae0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"24aea0cfdf468a5a3e70607db29ba9604f7c79ead499264bb0e4ca10baa3cd0d","proof":"5ab174b8dbeb0d68de54d06e44dae578217c4ce0de670d10ad42bbac9c6acf7304483dd83a0b39c4cfdb0945e5e17d8f8b1e969994881c1c8e122f66a08ee76e267bd5268672f45165471e2060003b599a78fd520728f83da4f73789e9cc0b4bfab37200e0a415bf42c2be734231e1ef42dd5f1a6e608cf151b852d76a7cac7d290708f5287527c6543e800a37575deb9d58f2f01c765b391290b24747927d0fa4a1693eb92abee423d0866938847702f0ea4ac9c24bfb7067ec14dacd8cea0660da467898c05ef6e068f80992cb42fe0c9f7f26d5d47b14f500507c40a6410000c5124bf6eb6b6a33c2a30272047cf8fa4b1d4bb40d9b57333b95ed490d9e2734385e855a07bfb7495f675a0e95ec31f0a53177b83958de04a36ac44bfe6315f48a60b2f01e18fe93b1ccf0aed8ef9f87ade23c9b59684ae96a66dda3f9f35846b9e8819f7ecf95ae150c239383081d558382d90a4a961ab6ac2fd69bf8a475f0cafc8d104eec93b80972bb4ab4a220e2803691dcac8d75f9161ba4429aed358e95c529d36700bfd28529e8633f8e06d50d8e3c24b569352aeb1f88a196e853889df32b2b4604551dfac08f0a695d52c4976bd80ff306bfb18ccb78e5f3ef29ec39b25e4ab08dc513cae5566ee684bec0c363c35a85bad7d260769e556e48023a3829a0e1764f73b9b96a80fbdda76dbec6b105540121671df167b3ed94167ac0be3e471c4f198e6f7348e151b7ac7511855355119f80fa7e23df33628451034a301c5c3041825b0d2839916d5ec222e2310111f117ef4fdfc66025b2972e07a4f7e5c8c95b6c9d2cad8f499557b15efce6ab68772a81c18387b22b293ded067b32ed2a13f0addeb6d72a7c7cfa2e536120c5f581d2bb7b5ade321d1fd4b002eed114defff953261307ffca0bd1d44ee16d4ea02aff907f8261e2f9d1230401"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f8a7a45504be97671b8ec886b1fe01552a0309a0486c0f6b511ba3f15bf8147e","proof":"c8bfceedd79a8521c5b1069f030d16d490af57ce0e1a3598703f9c2317051a33a63b04f041168f6aa8082f62395e170356a4157f289b9d4f0c087a73eb0b4326e8203d1edbfd8590f4b67981833b48769173bd024cb78ace44e92b5b70d576202eda85604683b156ec77d1b60a244d9d727b385ddb9c3f4265f9707bfe31f04923f86285e62d0a4431ed139725102ab6e14bdeb3cda366cb351c8841d7bc640c27175821aa96db736ac45c24e3fdf038412ea9fecde4ee23d34413e9f9a72401474c46e0226b19e43f44b28e980f40da1f0b12eedfdf930b86e5518ba91baa01565ee2f49814ac114113c3d5fb20a1fa2b0758821736a205cebfc9eb9a762545ec20d2af9fc33ffa679e845764d0880b709f665847693865e56a7a134bbac2224cf8ba0e0b9e02489ba0251efe249f9a547fc498d3c24a9d0f06982944f98e57382b3722a83ee5e89aea21d19eca3fba2f524a2e4ce95356c92148eded6f360f063355b7fb08028cd5e76ccb9b25c4f58e26386aeb45e6d04a5fe9882f86ff2166c85d44a9548c50e06c75a9910b64aaf01dbba853546da0e1e1699f9d6832419c033b764d0201673a2690202692379f9b108c4245c6b6010b9d31f1c375921378064e251374838236359d009467c8046df64741b704e9c1e8b01e31223aed334cdb159b15748e2920b520147855c8fa82f2048d1b24a804631ffcae97d89855802df19254cef37a1ad25383f46a9ee511270f776edcd85780339f2baaed9e3768dd53683b687865abcf0112ec751ae194d0e299acdc8134529cf47ed048d30c4a0dc5408ff271fdb20ce318fae22769d27467887963d03b79f1fa3c2c35cb4ff4fcb82944ebb2924caff5165f0cb8f9b6c8490c61772edfef87d226918a5d0174b1001ce43342ac19fba41ba536356b0675f9dd724e87858a36327d30de8504"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"929edaabb238d27ba82737cc9a755f95310fe7d0a4399e15b11b69773da69d1a","proof":"7437a3c97db523fd073f00294257e04bf1ebb8319297b3e4383227fec8e97b2c365f05962cd8c7c160a168a730e0b5aa92842dd51cddf5701ae62a8ff435026fa4e21e144cad4c2017863fc2c25fa8ab83373a4db2029d12da1f52b2c4e5b5119e646c381eea94c215b49d2b389652afcfb14cf142300dc8683581de063347320e91d5c277d287fce4a259eab01a56aedd7f4c2f7234de0f24df02aa90728d017ee022de7fd688324717082cda78d500f9c2ab7c5383273c37511d5501a4100f1fda1050a9e0fa1db3f9b51c03d51e2e582c49bb69db833561542302f8e45c0f06b6c8580b8e1ef773859b6efa0d9a0830c55afbf955237f70ce6982d5163939eeda419e57e8284b590ee52e9c3966f87c6470f3e222778f2e2cba43b5664d0f0c3014ac5f486757dabdbd5c52310db59511d627cbf49556cccd7094f387580fbe936addae42e27735269865503bcdecd7811d525107710d723125c05beb726d703b770e8ede34cff7c7a449633d98b85f28e82fc6a92458daa455e62643120aa815478639ca102538d86a10fa68c58d8c3f4af3a490734c1dae543058de5a334c3d72993c1095402f4cf77c21f35529df9a79d2e9f4774106132559990bf35deaab8e97d2fe0f216e09aa68dcdf4b7c756bc303c70dc633eb645f554c672954b4ed0f6657c1e77cf5e9280abd975df333957c9c90be681b59f5534ee23b5a6a96dd8c6d4e2915e549bc12848af62fe988f65e26fcfdb45d03acb1ef463e8a6e2e598931cdd4a6bd18b4d18c8191d3a6371fe3f732ef767461eee3ea5d65df4c3ab54c8a2b1b75d0cd5b2623a22c425fb28044fd1a2d70b05523410aff50232085b6b147fe139e1de785c20dea8ce7ec2213a7a8f8da4dc9a140e4ce2022bc09384b61af66079044629114a382d63e77c46ecc5ea854aba72b39734d0e82dd0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a623fe0de03e219caa445f2b87991f7977fe7032b15ce966e45202cd92abe011","proof":"047b522c3057dee9b29344ede2ba16477b17cbbdf0611a589d0b11e5d9647b1360e0101aff5e9208bf6282e190ef8b461121f056b0ce31c233382368d0810419fc15e4428f55c714c3500844b23c78f8af61db3a5f4b281597a1c42c5d835428608908d3d4f062bdddb9361718ec79e631ee69bd175d09607512f57a93d1ff0c58ad7ff24628ac46d87794f0b6e00ef46a42e08208ce260dd3fae1b7657b3306b451db178ec6f93d26e82ccd6915037a9f0d7345a551c2d1fdcdac55e122ee09b5a3fb1449921435fb9614893a72eb95e5347602d74be9c9127cc87c2b776e07382c34c83f04576d9ff381bc0e23275ae51d3fff781917be5f2dcf17a59ce71832591c716d71353f9f095b73a1f1918ef1fcf0d5e9eda57c55aa217d3af0b10eace4626a8fb27184e320c1c6212e79ba942b4f4662be72af21e38391dc77be3da277a6b774e0409d379f2796ed4459d99c7041d228bceed9a6e31923fe68724ace61750cf41af0f0c62ec0f21c748021f18e8699e3f7ebecf38248bc1e8b1c175e74acd12e4f52673508a66b4f2ea884a47691bb15dc9a0db7938437fbe33e4806e10a28364236dce9190adf466d5a6b9435272ac12a2e01d2e10e8470f9cc34f8ab091bf06972c49980df1a472d95dd3e3da19930c58685580bb407ad84a419fce03cfd28aa35be99b654eb83b7a6be59972eb2f66c14c25a6f73327f671120241760367b2555c86a1b139f01aebbc2cb17993b6b0e0158efddd0e8c1d5613984d6403c5caffe2c740275cf3295b81e9744a807fbcd6b163fe420d650cae974245b76027cd194c4d5ac691082a8da38898ee85bafeb745ac5bbe6853296f738f119db80f69bbc83ade175b0d114a56ced8cb8e78cf714262d70da2b8cee56044231ed92cca51339594bc3a5b459b453457b6f1d70e433d7bed6fd94de19ef0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e6937bbd52bae936451b65df110c3f7e67a6a55f780c291d9915ba0fe9f9a133","proof":"2e2eb65d8dfcf1ff94b8e766c17c8e5a102a36ff6ba3035c006d26ddabd278162a2ecd4d1af8ee17d7ec86594e9623123f891b4f91aed67cc2aa3448dbe81c4020da8d2be120e4138885919a816b426f53e935b024b20f17b0dee586454f537ab4c6cadab5ce957d5c7b421a909de0494587f9bd34ddef78b828cd40db248e186c01e23ea3f2d3c2a6920ba74753c36b71da539b272bfb33d114d59854bfcf0a531cf006fac3c19c8e780379783ef045285e51f91f5a3fa38850accf93f266086398e33ccd3519bb6ef573ca825c01a9cedf2af660c0e50069d5457f6a2ce2045a1c82a33d21d5cdaa662fae3a221608b775f76ff081032da664e7d3d35b0c1826564299487732fcfd09b11ae9c4d56974ec326d4e4955afb679cac5be249153e658b2b0b4374a08f6d37ac8072fb25f0b9d2040795ef644bc2f949b34342c17701b896033a9c9e48b3c4f97eb48990223d2015ca09552d2afaa34a279033b0c84e8dfb993a3850e94e58067a9cd988e5fcadb8674477b318110858e738c57161c2d93fe2be39c802596c70eef3afa88bd132ed4244b46e0e3b342d462d95b0e88fc0a5c56448259c18aeb4c088f177ad0991fbef9f9ff87ad0f311e89e8e24214318785960ea793d1f73d78f22540c43bf0e455d5c8870bedcf7c4c1fdc7e466474d418d5c5d1ac09e54a61bf1a3572ce583e044652b2ef205f614ce8584d0480dd8fb7b920e643076d05cba728f5c61e3e28c812832c93ff5e5c08ce08296aa0e5221c10a101fe65b6b86a5c7c0b6dae33bb8948204f614676dd8d1e4b843b4ad6ca469f615a611ed3518c70dd57a0751ffdc0e24392566ed31b7d24099b473864db8fe33474ef1c6d411de58c13eb0c8e347e97587523b2fc66bf3d75690b793ff8afa8577ba2b913dd54f07198af2291059d391da7ead5d84cbc2c415c01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8c842683a2b52945596a474602c3ce381e292206ebf5e9529534333e3640a075","proof":"385060945b2ad72b72679a7a0fc979b74d08d4b4b4c05fa1ab82e91012a7195226cb4c1d422aa14432b46b9d054800342042163ed7d9556e854b7357317bda1808ef7e6a8841606fac83db56d29fa9500673dae44075d6f47780f1f47d418e4f40bbf1331f6b022950e95e48dd00667c7820b1c4b608e2ad22ca6b971c372b02b24c54b93bc501652646a59ee62378075a19ba034a88235606f99d75d52ee308daf06f9d82306ee5c20bc24143d192ea66716c0ddc170420f309e8c45150e8012cfcf1a9d48804021342f81a01965bd9715382b67d94784fa4ad9dd6b7ff5700b8c1658ad0c57093abcaa79d7926c7ed7f4b5872d8c676dc5253ca69eb0a1f53a4fb03e1dea372549e5d0de81193002fb4ea8b9f8676979afe002b7823862278e22e88136a3e18e375c187d0b2facc5fb6639c207fdaaef465a7f699e27f942e8c1787908f89b14902284a42d7cb224573d5bfe5d6a562394e169d24962e76739222ae56e529cb9f4818097f43c68880c915179c492d240a6685d30132ed9f52248ed10828c2a2e39eee7f58cd97f28103247cbdec6f4178a1f3ef9d62200d0dec5924d8f7dbff332e969675c50e5c2829f60fe617363ea95f475c07ffe69d699873617c681ef3e1ff69b065691b72a21c36de4535bf3d30b413f552bec7374234455916314e4a670c6ce31cfbe480cc409df914bc08db442ed0fd26a329c57986bc7f0cc7f4795fcf50820d1c306916f44bc7888a9f5b608a139f1bc3378a320026ead676510f0884bb148954f68b3e11241cfade3b9145476df334b9ee7e3c3863e2a9f268e6c2a64e37049ff7d71950c161b320d656b886129d9dd1bfdf1bf73a81777fb913d0c2b506ce3516d8be27ccc3f23544af160af96f266e8a28024f5779f1c3fd333c4cc26c9ad3d0753242d26a6c56f94c617d62972453769d05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0467cbeee0a3eaa387def6d0ee9c86d084637c4f540bc7453164e4c76231f34d","proof":"5487049ee63be7d123880dbbcbfc9f15d691feb90130ff9e99cca78865f800407c546a2d9c347b0db2cdd9d7f8022fb34446bc0fd7ecfd96aeae24954439590974c9d9d8201f267af3f9379b9ceba2618d3d9814a854e9dec63f99224234df3f26e134762d1b6dbf8ffe4bcfc24a648cc96b9ab568ca32c433f538287a9dbe0284662b40e562749b57e198c39862a37f2243bcda0e5de2b03cf7d9ea639c1a0b19d4b02a1ed71d0a1303e949c14648ca200a4305e2cc7dd41b5e1088d974ce0cc9e6acc6bd5c6fa053b1a89cef8a0b00657aecb09419fe6c910662515bb3b403007f3c2b085e30826f506a5d2e1da68f8826980a848a402aa9eb457f718030459e4666b572d14517f460a85b078522e25076cf7ff9ad935883a3a1a690e0ec3dfa97bf01f89c08badad66234b185f9e7dd99d3f653a61e48b550f504ed3c275c38379ff3743642df8b4975b4e8ac799e69a8088f0c68f887becd9f893a1eee784e0aad7eb6f3cba7f3019e6fd532cf0b071679bf0f0bbc6e08678c67c484791a48219cb40bfe701feabd2a1fb688d494490b28e47427840124ab4821dd0e2b17f0d4d258657c56b88e066dee5f6df6bc7e1f69861d3b5c89d36d1732d16ccd28048c72047d788f673cb03fc4e09721a787dac647d0e7a5cbc1bd51852e7da97a5e016ef600ad9799a06da739ccb2d4f1b83d1daa7a584c670b65781df5605043747308c00b8127d5fea4c7c9065f3b30ef7cef1eee5f8eedc7e27f1e9ecf043390d6e2ffbc904907ed81f782582cfdb6a7dc80410912bfb3147d667e11eb5556d6e878f240607846bf384320c355df6ede39e72e0ec64f342cda6bc8d7cf201159e024a0da849810d3cd1856c590fcdcca3eb407246c21507f2d695aa20a64069e647464c8ef22b83c5f9ee817a5a852ea96497889fc756cc0f0ab741a8f6d06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"24fa6c608e14d6dd78ae4fb1ba321f16b0c4f12d9ea3a41142604da085e45e2a","proof":"ea75ec689e978e4525708430b94de14b741cf84f341d0c6d0f5e4b86a97e121d4e77776babb81a8ac98404a45131f72e668839b5e8fa280faddfd35f53b1bc171070bf0713ccc1af9af8a73e1e78f39edf0f5f0cfd6f8570888eec85ea515b47a2485b65420c1cba22fa25f0c3618ecf7b8ed10a334222aeecf528084483cd5ff8345f57b40d29d9a2236cddad6eb6a0b7cff7b1107ae71eeb7330a909a6d80dc734244b5d4008f940e3d196a32eb417f8e5cfa9f4e0ea2193bc747531c62c0e288a2663146938ebc75be9c829c731db5d3931cfc2330ef0fb8245eba3bd730422296395a6cb9397d6c736a05bf50337fc7836f56a57862e3762f30ebce3cb596a3fce8eed4b784641aafa4a44d1f254c4205cf77d1f0ad361e7f6aa2ef00c4676eccecc3b11a5eb596336c395162fbdc06d72f2fdb2fd2789737cfb97a0de7f94078620a0fa92178639a772d9680e0ea7c83c6211222f0e19e0c02f8bbf4d6a349b391737bf6a56662972a9012d960629bbe783bde19ca0acd21f84fcf0840792a127451e8394fbb35de28ccb65afd6d8e16114ca07ff83fd752ec37b6cf62d4880c1d68ab13018ffefea2dabd54639af7540cade7d9bc3e04975f20774a9396c95e967ff2bd5803a18753076aff481e11b18abda1b4fc6d4cb5d016ad0d8421c2182ff9dd2c9b9c923556223d8e2c649b964a2af5d166f611d45fb6b20e113c2102c95f9f17458f6c86b5d3a60ddeedd6e11cc55981a7706144db0d02f49691407b089351ed9d0597fc974992bf97ddc33f90843743c3cc1e96bc82faae8489813e73053e77bf2670279cdec535527c2b1e22a5a0ff5bd8135a6cc613f796ce5f438be7cd8156ec2485dfc750c21f6afdd0bf105278d14e2800456d23b1a0d57b91c8e83a38dab3e6bdfc405e893f9d88fa647cd60182c855594bf07d52405"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9e5e9ba74aec919476dbb6767b5db1e46b7127f08e8b7a54b8e54231d1404972","proof":"4a42f22fdb80110f34b8a88591140f4fd0c0b7c2b93ac94c0a15844ea584fb40a04b07b0e8dba53876c1f8f6eeffbe40074663e6a37aacb3399c6bb3f1da087326712cb33b8e3b46c28d5afa1ab31cc1477f3cf0587e3aaacaeee93346be5507c8ef4f161315c7174775149c695462a1e9feb7958215d6b20066eb05f115e656c06f4898f66e09c814785c5e5a09793d358826b4eb87d2b109c23be9baef460342a9c796a7e4cf140097d0d5c1b21cc522508d545896d5f23c73013fe304c50434335a7cab6ee09a602dcb19839f541f2c1c7e5d738b367c1221b82e6bc5510bde195a929e632a3f14f7011bacafb67a114e078982d00c0ac354babb837f551598763a527451ca01906f0fd42249bb60b78fe091ff2bd9fef3f4d28ca311a851dac1c4d0d4812a3d742008af4bae3b3457b4a488c0cb2f5c8e8813986313c75aee5876ba36c6e92174206e2637911659e4587e2b93ea7005178d8755d7719b53e6097edb3de0483be76c6b8e7d861d3e745175486b153e165ec66395a359e964760e96ca1d86ba52af43d1a32e80c00c49388353bc6844ff62a3f0c883b6b77ec66c3a73b164d09d7fe98bd365ba8d9a304bab41d435e7987afcbd1461aff6616624b2137dc7a682f5d48a4e016281085687c4a4fce31a18271f0340e5f2c13d48b6dc78ebced8bcf9f7143dba2183bd5d21140d0f8139ff8f0a595eb925921658da860eaf27208a144c179e32fe47195e06c431f261a8a8cc7495fbf75c4b3ee47cf9b9168477f54956e1900bbf8a5b08608deb2d2c64dcd63c2756ca47d60c7a0e0f714e11b781dfe82ea4f6be9bb4f9dd349e5d502c3c88f7ed54680d273b0e103309d0886ccce56c3f08315b1581f50943a1b327b59740549d1860dea607520feb00d1a18d0c2ca8d4886c57deb0a1fecf655ff8e9e09f387dcf49980a0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8a5fc215484e899cb83a5658cce47f1b741e23fd588854cd3b050a112ed44942","proof":"4a777b8b23f987b8f8724b34f111a780409ffa654b8e01e557dd2f887ced0d2e02c7bc2defa2c8db81dd9f355596be77ebadc099b3d8a26f5c8e5baa5f246127305bc0b8442ab901cf6ffe5ebf7d22fac8477a6a29e4943b13d16d0521ea731cc44d7e1ddf2469290d6030fc07037d68db10fd940088509e8c009f3e7c805849ee28cc351857ed33554ec4e1a12f291198052f0e1713268a8c6aa442574dd401df1202e9d8c98f2b098a4b05f5471963912bca4796ba7323af832d9610cd6309c744f35a8c318d2c24b987c38a9b6a3e942dc7c61bd4eba2a0ecc729d795750f628ba1e2654831c3c12fb57ee5c8a4cd366a97ce37f7d3ee5e37905c0b24030df0beb32d1913270830efbe404c71ff5bba627145434613232dad79734e007078683b15e15f4ad5ff880d8d79e38808d51550f1035da9727f353507c46c7e400facbb90d39641edf31f02c7e706790270b030378baed5edf8350b4fc3e70f817636e8bf3d3d6019d88b770e0f844f90899f12ef0e131bbe14fba577004430ab0188f33d4312058a97c5bf045e5512f585cd5564daea993f264df0144136d2af1bf44940029fc4c88ae079be218fd34087ad5bac891ff108c1d12522405d9bc96bb498db2dd1ea8d06419fcf4ecd9464ad9f87068a30227867df20a442abf1d62856253cd1b279733b75f4e42bf33f881a0d79e6faa123658ad26ad97c0b5400164495d736aa50dc851c48eee09137f27b0af76df861c45a77a07b44a4eedd1b607ef8f7a41ffbe0470b6a2ed045debffb2254b6e904985628170b6900e141e858ecd262f219550389e488ad00d931d896cb14a8c1f05cd55dd6d7b8593f0ac11d49a8b3539abbc3a926c87d0ebf30e4f814c37e63d6985ef3dec30f7534c0ac005c58a8d093ffdfa2477d1c9024d328a858c316925a9e7d6a4ba91971e0806f03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9a1abb08ac430ae612c5c6214783a6d0091a10d5ffa06d494a66e8833f63d037","proof":"5e106a014b1be6b6d580a307310ccf929351604071e672b0651fc43446a61e668422fcea88e14f200f0af98f9e5a27f4f66a659d0bd28a1abb46f155d3e372674ef18e8ac005f11ac914d0e2a105e80f99f43324e9e5698aa050f5129860092eb256c768067479495b1606ba747f0279d179bce8d137cb481583d9bfb49aa701aa461bcf803133a72243b0487579bf2d8280923a525bca8abb5c7af92dd51103adced81dd00a98acfe412e3a1c252a449b7518cc9a5c159020d132c4d74747056f1b39f6fa089b1501087f7c10e9f2965a576de1dca3d8257473f0f126374301bcc204ced7058edf1a73210cf1781634be5c7ccb33c1d431fe1c3904c3cbda3066765214797b2b68e832a7c0a925378827f669bc50f008d242640185f977ff253ed66b08e3c661288a14a706aca30f4f6aa68998e94e5c1c9afa4156e529492a166c993f0861fe8e3ae00280f249c26731c4e43b059b8a2df73372b164c09246ac34dd4b6ebad1615b779c759c437e0d7cd634f1954f5fb06bad0486a1fc233caecccc82e11c900a015d198e2262896424897642f718663855f4d1537eaea72ec04c121d829bbc357c4df64dc0c5b1f1f0611f0eda6a83bdb780d0151282f912d455669e1b083a758a75bc81b1ae461d101f75d319fc438e6ce8c926ee6970586c7390959d7905c2554fd88f1e9a43d0bdc86b20c489ddf42fc77d921c90581ce6c0c0b4b57c116168eb09d858fdbc2935dd42ce568a7811f5ff96aa1f5974651e9d128e2a98744fb3d11fa899d5620b48a81b622071ffe4c9eaa2bbea655265d6aefba692505131a51550c4c7210280f3295d3a748553c3bfac4d78ae5f6217e425aca30823374aca5b8bdf0c50f609ef945c7b2403f0263b5bdbba0647570366c59d21eca55366d6812cffdd380f5372ca85d4bca0d724ba4db9ffed15d704"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c43f5f3e0c3edeeba2ccb3b05df26cf9ea0b65ac636c0b0afa8d4dcf18501026","proof":"98ad18cc90ea8060a341a3e03b58bc0c978bba7d5ed218ac440eeb9fae7a227430458ae35fad34384131c26098e9cc00c26d8bed3cefe718900e76623c39661bdcd106468d3f294c81abbb39eb06334302add0b1e80ef571f2e36cfba049170db805eee9a67b71147adbf0f14b73a86ca8dfa0b0f7c285041fa4649de7e5821fc0b62454499f0f21786b77480a3a91d79804a5f03bf409debfa5b35660d1df0ae4293ecccaae734874984373faf93318536e711c8b682704b3f2d3a30a3b9c00d80e17c6ee4003ac8b897e4ed2cc8ddf2756fba2fdbe7d01c8280797c149c50af690bd793bc123445400b0e826b3d8611895e1c8f412ad6cfdf2150159f7bc2a4a9ec0c85c1443017bab176d14e823841d0af830c793ae7494a578729289723ab2497312bfac829079aa32ff7ee6977695e4b0b2fa5daccff4428f20ec5c9e49e0f9869cf42525fb281a8f4fa5b87be06dd9d29044b1ff1fe3bed1a4b9f170640a71f932385c3e1721dbd54bb43db651feb066ae7cf6d0db6925fd8dac5f61145650159bba57debbe757376978ad229e98afbd5cbf65e155a9a7a9b4c9c5dc19eaa7c7fd3df97c302cfeb4f4bdb6d5ffce724b7af42bfab46080e72069e270446a1653880a7210be8cd0d6aca9dc0dacaf34d650862948b801125c2501a792176a1f74376ead735c73ef378479e324c6371f0955009c18e16c03bfbc95dd2b7f1eed4819fb892a14795b3ee1be9b23117ce6b2bb5694d691b1bb0bd97e31ef4ba6ee4859ae9face36c9a76d11edbef8322bdac3630b711acb2c4a982c9b583514eaa182acb963b0c024da56c5dca89cfd9b045dac46346f60ffb0a3b5f8c2732a80fb1a88c23289fe11fc7acbbc5655c24d3881f04d9099c78a4b439064a9a0bad187e10f7fc23b90406630ecf0ad98d33c0009217d450dc90219e7ec9c3460c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d094b65f105b919aafc7bec122c66516b4b30d29ca44d47c5b939276e2932076","proof":"3acdb082d96fe5bd206811fd08c7df44f841c755e51901130042e94707db230d8e996309385852b186a42030a5d1de50d66a6ee89c143b8d186dd1296c74c03b98893e5e65ace252da9325b30a9bd3f4acff21289dfd87930c76498e7febb57e1a603abe547751dc803928a20d6117fe227cc692a7320c8cd98a63c14824c23a9c072e1189fbfa7e516e1412a2b29aa8ed01c0f3c174985013580f80c09f9107d9e11b3165f9bb4ebb4e701550220bb71ef8b12b07d9a5c59541577274d2070e82b1c814c4f3e56b4c67dfb930f101cf3ee256ee91cb00722d355622552118006e2a1dd579d51859590a4d912b35155361ac8bef18f7471738a5624f3d12d069c8ce4eaa8114dc816d5ed9631826bc1a15f79f03e761da5e44e51c88afb4303f36c57dccea5cfe2bc597285e0a240b5b39764ba40e881ba0cc4142b9742cde6808a97727733473aaf169d5620d2dbe5e9e95959834c9becd0daa2ea819a4d96098503c7482d514343616d64a30787addd6efdafb6eba0a117ffce3c3d0599c68963a8822d7ffec19776d5e4e9e4649a7c4117af8d6018760f31f0845703c8b2a7a0aedb06eb45f42b9d3eb917230205eaf13c08399df032ba2b8b43f77d51a3a567a2d31ab5473f2b8edda82fbfde7555eedb4b8a0e82b141adae65b8631e648ce420fe41ecff976d97e4c08d70c1a4a563a285caa70e6175a2552ee8880f62c24eb3b356503c584b4005ef67bed3c727b5e539a3a73823bd369dab0a5923a07a08369869b2f71e704de44ef05715a6ad5c960154ea62ad873e9271b9aaf045da65aead5df8b4b64fd057f08dd1f643af62452e30eedc8191dae0b53b07e500b8b30b3d3e0a550fa680077236158fdb728b2a0a32e07f4be4cb817455175140f3928608f53ab9c590be1bc6474f89614f651f3e747e87f4403b6fda06a1f6707"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cec430edd66a5a855cb96ef22d5296076078bc500e9ea70a8d46591d66ee5e66","proof":"b083b5cfae4aa316b39d0caa4b457c6b62b05b1b79d26d571f8e7661a9a3a969f27e6bfb6332103dbdd0b64effba6550b307fa2dfad564a5ab60549bc413f81bcc837a8791edca4291f2e62b4a8f7fe95cbd4f09884dbca1577e9982d7021016846a8a887ca82b45be7f9164f9083f024bdc792083e3bdf7e053a12d513bf628d8c17d54c3a0d6684e7784490e50fb1078957ddb3f08cdbf381b8f81f614b107184bae85a9053dc10392f835979604e42f1b520a8538d2ac211f8c8d051c070abf0825828c585b2514eb298c618c864176a496c3feb5c13a2c160292ef580002f48028f8556e345667c4dfafc3851efa6074e3d76f2f1e0372efd4ac13bc9463eccb3720ec64abf53921b877e764919c191a0835f2d37489ac16b6f20d04eb32f67027dca0d62a63fe6299734af8ab11f0557ea942779548f115d42cba8dc75d4441b55883e915d9eb9747408b5d3a9057f2495c2eb2de718dd2655103588270140fe2c2cd8a1064fe84a61fd533bcc3478344d752d4bdb3f9c8e2af6a4ae86536ec4260e7c18c7dc73c8ccd107ea12df0ee267b43d42568653af6a563b1b4138a3ae7f4387ffd9a7c81830b8648924bf2fa8857076a1d9c59bf138f3c909003fc62acac7752a3b245bb0c4aa436c9563fcab42348ffbed796ca98cc87e4d52718d8cba499912e48ddd04ff106c92dc06afa7360ae93daa5f6569c97af6be5300ae256a6ea0d03f03f579ec46b5a2c0e4c80b967b6e7233b08b82b6c77dfe17ec2f83692b5878adc70be717721c911b28cc23cdd59da92dc959142684bbc99799c6f9a05d65a07041905d5deb5066b1f3da3c210850fab826b668a337f646609ca3c0e43737c4d1216c3339270d1c97d971e90d9920fe5c0e2efbf6435f2e00d59c8b4b47883b21d24272acdf32ed02f9f2997392b4bf4937a4e914acd6e2704"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fea7611842712f17b2a143fe6f23648e4d1c7404a28fed7c8f3f23af64941707","proof":"42a2404dbf8a09a430155bbb69fdcbb1c5abfa4ea43a62b43fdf6362e08e0a3a1cb261e841729dd43bba058786e7216cf94378b861426deebad075a6a37dbb2a9478fe2fbeb695bfbfff6f017fe14b298b4f5cac84bb44cd708f50f148476e3af22e3e94105352c971e0e10520294f01e382bf5c96c9cc0ad32d7b2611d9682ee6f7bdef2d38fe7cab0ad6e5a550e1719458a37663ba38cd32333dc961fbe201a15a5110d2a485a0f55c60f6a2b323180b5722e76d5fa1e73d80b5daa2375d0a294163c2ada700e0a6e14cb543e4cbbac01870d7367f9eb99734e145b9cbdc0edaec86c8c82557e489a9484e9cd0d24c65dfd4438704164b3ad20e6c6a414173249a86534f5c8d9da7b59610710c622829fea9537d6598fd2667df76c9ab7d793eca209f4e4ab2d4c9668c0cb2c447da0f34be9ad360f68d68006eda41ce7e49349ec9778f50999533dad023195bcf677445613710c544c8966c8b9f107f4c22aa560d5d3e017dd7f799948a4ea013afb206a7fcff92a6f8b182e257906858456c9c9074147994b31f61ec1ae6f075ec84173bd7dfa151c6905526737dca56098a0f2d217cc68278aa4ae2639f33a0704331859a0f3a3d40af783617f7b1f15f00eb7dc70af2dccdb9da6878995a4cb492f76b0aa62abb76087413a48806b95c223db061cf0c64eaa6f0a408fcbc9cdc98666391565eabee57751fa025bebd75467fc8ee8514b4110b3c88c6c2dca78a290e57acfeacb76aac04b6aa77b5020a5002ad01368e12272c1d3ffa0479cb528eae3dd9cf42f81afc4c25b46e918c028e45acf81f38832a4836ee1a9882082a0d35ab0316a93aa661c89db5f4e80924c0d16b172f72f3509af7126cf6a67164c3ccaea0911a6688bde64fe4f75dd20eaa00daaf573d2f05978d319539f027271ad3ce25249fdba417d4c9d821cbbd04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e07301fa09c8e874ff63d355848135046e6d3d36084fcf85b26b19c0fe728773","proof":"7a6245e18ae78f81964dd1eaebaec2f2393419256dd43eef309d23a62eaab53bc4bc536473bc6cb7e871b1508658a44cb08e9e76f74de2191cb1de04479f646ff43d73c174f02feb21fd43448201ce946aeba65c10ffeaeeccaefc7a743072677ef4bbae614f63297044c1587159a8d05eb35bb85b3a52b80171dd02cbe69d07cf332d38e61ae8f069adcb896223422cf116ba5981fbcf4335598f4139d61905c97c786ae91c1a8f15259e5af1e073b5adab8ec95f08948b6729f5d1eb5f480e143452a9622c3f5a397820247221f519a6f1891081f738523083f201aa7e010b1892056f47ee56ca1f01733c81ddc17320bdbbc693395e34d775c98a9533793dc277a5948c3cec102ef92acec94877d3c38a4f2ad07875b73b1b572c42c28676e838163e0d94aef4b9374d2ef7a6b41b46fa2321dae772f2fc4d497bc578f25fd850e3f6ee78529b15cb2be1922700713d98f43239e493b1604ae370af9d950378b3308d0d46e4cdc4681b78cf219f584e24ee1ad0f218a9331711efc8ee785568cd2b56e0e9ae212b81d9d76d88b3684759118fda06fba53d2f036b224be21436b4e827035b3ad53e0267d90d4930d99769e7e4fab01639a82fbce6b9ed6e4138001172219a2a7a4a8a848bb8283095a5f8bee7334b485e0d4e89134d986833024c68db8cb774e1262763a9c3e5acf8e317941ca206d84a44ebf0401d1036481a93c86f7772d747f36f572b3f1d84e64da2bc5c3f21472caa4ba320af5fa232e0f4eb58e06e47f63c0390be19f6faac860721068502a1435fa00b5af9b34c48dadd16696a1f5bc60e647993057befa6fd26552712e3a88aff55bfe7a3f3e563de3e2ad9008bdfb44c3fcfe9823507f36d6a122b5146262a501fab81a92f8d057053d4edbf69a48faf09d7c9a9f5ffe6aab48586cbfffb8bbb0c5dc946eedd03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4866470150fd0fea54f90674ffa0474bb696b46d11412e9b4e3f95ef7c1a005b","proof":"3a45d02b53cb4a2f7d06e01011b700835f24d48529efe5122772368a34930130d66a9b608926a90c1d38c908a0d02e087735dd940678e9e7f688829095d34d2b22387bef12f7bb0fcb6252106d20aa0c3f0c5a32fc04d0634d3f802a5633973c0a7254e9e595493046747fc0690a6e0f04982937a128a08c4d5ce5ddf2ccb8253af68ded799e82d5151073ea2f74d9492439e62f650bfdd2f1613902a4d86d0f182365d2d3e0cabff09abba65322407211efc69244db4bd5104a6969ecc7580c92c56db70c65bc98a27340b07c9ac1b50d4ecd99d3b1259cd80e65026f58e402b89192586834b677dc9b5fcd66695e7f8739c1f2586fdbf2520fa28de7dc351d3c384a5c45a637f131b9c8b00a687a16e3e41bebcb337146c3b3678a1bebab0962e702db8890b94cdd3b15925bf04149da20d83576b3eafc5621fb8f83ef4b5d84b1c1db1ca22e7ced069f1310e63dafdbc08c5eea6013668f4a22c179263b019c5bd390249fec3c93c421db2b343d56c8ea592ca2ffb590d2e16900f8e60b726602f46f36e0cfc94a17488f3d35e183fbcf0164535f98da7f3ae393ec00d27cba54c85dd341a48c799c706c6fa43b3c4d9a579eec22c13cafb90fbeb9fb447c5817af2f2a063e4489ba382f5b7aa6c987e1986222ab27888728d68004d2a30cde471a288be0d879a444bd8ddc6cf60afef0a999dd1e4584d63854d13477634a42037efabfc17bd1e67fc0786ea25c526bf1036e10ed74b11502743329c33a4a38e0c9a980d381935841a7e2639041990dff22706ff4797a198880444f13353754ccf8b6aa0f0393e9d53b2fa41fc15ddab671b2401e60b9a747359818e0975adc2d4c156c91b1bf0f68a50d957980dd623f7543f7a172dca3c6fe261f1adc0d204d21babe7a01ad858f5232243075978af6c77e85e41efad6ab6923260c9402"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a05503f69b89473619a6fcc68201faec40ef981a911a09b81289ffa70de59918","proof":"66ed2698bdf85e20e238803c52e56f2a4f7f9b4da439c8ab80ce4bce4af789128646e33d699d559dea1422f7ee47092683be320512c687a2a844b2e4c499d70ff6c7e56881d015bd97906930fce00ad4d50ea0898771c3e51ea5ec26c160355090d4f4a883da5f1a87ebf722531e4809f757b542291fcced0a7774dbc2bfa05ff37e57f0899b2c03cf924c29e0fd8f8d0556523e1e7dcfd656c81bc467c98d0b813b7748227d65b6cb57d3179ec6b4541172595f24f752a8148b207eaf37230a34b39e24b484ca07dc2cb13742c816f097cbd3b8df44c640cda365a38ba515006834324d544003c227824c0ae83a9ed65833df55f8bd91daaf15708fc76add5c960a6e228665a8c6753a09bd80ef7bb03557fbde8cf8fed5e81cebc21ea7fb0d1ea9efab702d859aec382e7425af7966d7a8718c339822801d182404d6566727726ebd7ff129fcd9ae49ce94c5e80c5552b84d2fe5fbf1546baf039990ebe26dea85e721c96441e660585197c98eed49b0b709b80106a332894a99489a6927319ce7fee9c0a56d4a744943a6f125c265d009057812e343c4c6de4c85a990d03cf67082d9bbb441739aaa611986b240a57c5661bbe448930ca49053b7ef62d76604fae0a925b29994eda49cdadd0f864d47bbd234360616e17d6bc4b92e591343e875a69d6d20e2b1270565f175ed24299566976f2bcb85d6dcbbfc3c9dd15c0d36e0c02b37cdcc2c291754dc023009f5e2f8e6b6976c4e13e266b669b4adf94f8c201b11562bc5cdbe708f8f4f8823d08de5b2d239c418db449fb8e58e2d297efad0c31d79df34f7fefa51fc1448f924abb5dfec001b9bb855e606f24f719516fde5487514d1b51b1c55ca8ebb82074edae0652367102a649a9f86c6c683d7043daa579478c4eb2c6323863bfcba47e4d849de1a13569610dbc2dafb0361430c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b240cb83e1ac466103caa5c593bfbdd6909e7c8024815f0f59076d707d8a814a","proof":"3832b5135bae2d28bbc40a0855dbf34591b8514c06c0f012c298a144f17f211c74c913d0eef610ea33e1f16dcc0b071621950ff01efc4efbdd42fac4fdb49c25d2f2d65188c8d041a7f66f1ec92be05c3e3a1ac18d6feb305efdf8c5c2be8902f6cd16966425787280c9956f39c1f367ebeff5bc8e279263886b9e8979a31b2fdba42ac892c1b99f4866a24a237c68cc257e2f894f1f44b8f502dfb657f10d07848cdc119cbde5163da2f901db94bb1c61e1d91dfaabc927463948c0c4ebe40f05696961a022c47cc04612d0f25e579283a1ab70f0186f8f2d800977c5e1f70a96d1de7703b598f36254c52c5db4baa26053573ce51a175ea75624b031261e68b4701c6758880d8f5fcef28d2e3c35a7d0569e7edec4f8e2fac0d7aeea4b7351fcfb68c2993e74fdef4b8322e2186503021c1b27125ca7a9e3d60e029088b85bb28430596626c24d3cc79f377f3c7061d4478310a140941f2c829212f7fe1b7f46a58420c461650a6ae7e26c787fae95e86d6031b7b201d227a03c17506b5c265efa57c127197bf430f59dfae7c5c5402d6b7af9c0f9b9a8dabdcee4f91c9c6decb7c81c0f752567d351bd821f1196cbb781791284f3c7961e3e14a614fb0715b2ae5815514b472c2adbb2b10cdc74c2952aff8af39996d01b729487dc316c5d2eb8b69fb7a861b41efa3d73f2069c4f878384b5c08b7ce32d214e51c0408d26d61cbc2b722f89efccb1343580010a240b714d4a2bc2325a434d570f4c27d8381a8ffe3ec5591692b2b65ab9c84ab0cc564b8251f8df617f52a5b022ef97081ec80fc4009496b1ec98e8f52a416da61f38002972965e99dfe54cb8768980af1a9077c0fa2003d7bd9897c5051bd31ba00813a95b0c238e0f9f878d3a427ce202c1d8fc973570bf26064eb30e1533cb3df83b308bed34ced277e6bd382106c203"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a89ac22a43d91ee7258514a352aa9edf315a579fb00ed1d7ecedc4c3ba078f65","proof":"10f60fc281ddb70ba19ab20bddbe1faba60eb2b80ec52519c1895c99582472316091613cd49d6b71fdec0000b50483998a7f718211c5260b01ba6d7360546c3c10347ddebbdeac2e7fd5a5999773a77882528149748a2bef62e5d7e3adaa9d25aa489adbc95d7d1af28cbc5418ce382ba54cbc6a6366471143a2aef15a90471ae627d8f4f7f7580b67be30bb6458a04fa4da70bc9e0a2b8ad2c5a3528739d80656d9ffad636f20e473c3b6019b378f856db5dcf52fe91ad5e764f1dac0333001a5b9e5112b5a966520d16c313b9cad501a85d6099fb5fa0b6a18bc233be99008f4b80acb38b829e3544661ea00f0a3eee2b2e67ed7d11bc636f76a6e48850517ae68d6ddeb6122ab606fb5593c8b1360a3814275373001f38cd7edd1ecfb2d7deab221597ef6f6b9d4d62461f50846d2a06f2897f9c459a4456d2799e260a16e9e3e3340c9169a54d7411e2fbcdd5a63409e24fa1fc731e419c5f673f473bc2a56e89dcb3baea1c1cd3358caf5923bf1bb0558fee526af19f8190ef2bd82342920d13f27929a25f028c97eeafef3dfd919169f1eb65f68897fc328078284ed3a887a35eeee9fce66d15885663cbb87a61a219f505c9928ad5e5784674d234707f4dc62cc6ecd8fd29dd892e278d38ec8b1d78252f1bbdb5d719c38270428876ebc4b1eb70ae98602670fb300cf7c20f953644c73142cbd4bbac16d28442b5075c42d2b38cb7ffed77c05117948688bb9e50e789572bd98ee51bc62cb58ab040fcccb4f547948557d55e81b4a2f2e0ef1976922161e69c55ff44f5dad96795d4fd499451267b6fa98bf16b015bb3ea08d14b76719e0e949006aef746d55c4374279794de96b49d81ea907d5dda17934bb78dc570079a4a31ac1e9309d0b2e7603c1f47602390583b02e4f597b226719063d941a359a0db7bae1c00ed56ce9d707"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7eb905feb7f9ede1a9e269b02ed76985b80932df6bf5c9ba949a3c7fc16be30f","proof":"ac1a01b94ff495740c6bb1ca4d37c1995a512ee152f873343fd77ef03cbc872734d387fa4cfa360a84cbc9392b7e8ec610bd275b6f3385ee05da6f9809fc4c4684abc293146ae7d5199e6dcbe45173a545d4096d63e96e9aee9f1a07a5de6013a4493c1b4a3833b13a601977ee149e0763b80839185c699ca4f02423063d006e2eb7a35746cbe0cd13d95c3d7b09a001be1774117d1fdbdeea5d736e9eca5e0768f65ea3c11c69758acc02c4e093f9f5d0167772e48d791d19047a9d959ee5004a54d781f610384d54adb77ced6fc1f1bdeee54ffdf08a04929e72ae03550b0392ef158cb415737853591552d04b8364b1eb83fc03d9c14d778ff704feaebd55a2737d62b08448b1763595336a6834192114fcdf7a0bea25ac837ecd3ffcf8772488aab2f5db398ab2984ce76bb61502ba43e6c27473fb82b4fc95abb202d31180231c24156e99f667a4928639a37c55947c8bc0d5df0aeb5bc5bfb7d8648c141c889b92668bf7540f6e11a0b742a97cc63366cfe2b525d104f307e0cb903d4edea1506e87f07022fee44e305d70fb4efa474895c1e3539b37b86a1e35ba847f6aa421df3c0d9146102ac3f214af08fbcdd74ce2e953816f2fb11ed6faff47698af1eb2fd33eb2b276c0e58a9913ac9af859e63b52e3e7e487f39fdb21bf7b40c8038813f612365c25b9f982cf05ed3f0ce0824b6281c8db5332b6f534c39f7ffa0f3cbc2073a7eec78b1c2ec1da780a89dfef64b4077f66e0199a3751fceb3ca4662763019528431a4e5f1ae49ff714585fe5a80d4b286c8400b3d08cc0c851ec69f83ada7b8415431541ca7e85795f78676cd66a15b9c97884ab4c2e85a35764578358d02a45b348ff3ec18584becefb8e26beb8d31acc5c5d0e22d04aed013fa46882333b497565d8b92078b2e8b68761aaf3b485a18193e666e6f08c9306"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7af9b25dc878c7a1419e58783727fdb05180cd6cd935698650e690edd33ac83d","proof":"9c36d7e13622eb5e517236dce9aede933be91ce30d33b9ce87739609b702a07888af77eccf67ed6b12c01b2935e44d29f521f7405d03ba6517d59f77d26fc368c2ef571ac31d9b8538c1fedea5e4efb35e43f4f10c9dafcba2b6cd06819a675d4ea285fdbb9a62ab2ceb9755582925166da033313c2d0d77f117ec531035a32144df3885f7534a1feed9c43523ea496845fb89e18c1cfb56f533ad7240f86d02fb8cf04c13f7d83f92bcfe30419fec0009630c4b2c214f44c62f8d575bcb440821bb0ae29adde2bcd0a8301040b46bb927e201dda581c717c82d0d62271b31056a0a133112f96a967adbcd061177954384623f4a8bea2803c52a9706a45a1414400018ecb1aa649941136f80592b94450bde896480b75bf8ff914918a24df0218c66d7e1308127e753a70cff0356f3423a4f9c0701d99ad39f30494226c2be55123cfac2892f787311b683711a6c0c892c246a546d5d4053d6bb5daa6e643510eeff160308e6b7e56ec0d8b0d5688d79ecbaf9df213fd512db5f34ebb6f6563436ef7e4b6462d0b79946f0b959d9e2ad34d8d29526fe8190e14fc5772dea7c743017c95659159ad965289cb30462bffdfd5d56bb97ba01838143b707b76b0670fabfcec02abc5245806192221806bebcf2de98d9e99e95b4f11467478c137e2c1c8b8dc58186ef0bd04a14c97177432a670bbeb0457dada8bddaf07276e0ad05ea7c672c67f3b5822ed2e960b4e9b59ece70b6f812c6029172732e104210d846be2d90c9dcd5ff3602f97e44a1d2215f18429039b65bd728ea70dda48c7dc868cccfa988d2ff7db786207b44a375439f971f4b9e89b21d9752fb4dbcb616981fb05dd9803c8da0beb7828895fef32908b43df927e42f03b7cb2e55ac8cf6530b3d6bd8d9ce6731614c223775f0b6f8c4ed3aba363fbef51b02950886ae21e505"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ca86f717971be25880f6e78513532b555f16e83d10268d03778695c34ee1c06b","proof":"3ef9691ffc82af0ac3c528b13c6756f9e5756d42f89a6199ac8649cec0255c3cb23f417f447f93911b75c368ceabec60464769ce598ec8d36eb460c82475064cc8ce6f83e83f8d078c76c95c2816123a5b9a6cd198f2b0ae981944ab28a1801ce26245158410b12fcee1d3b32f9502b6ff11e7de6631056644481e8506d1445e6b47d4810143cf4c499d7571454b5ed8059902bd35d5018d1e2a2df6e5ee330cc3f06dc2ae51e6f85ff55f3e65667778301ae6df1dd0c9e90048198336983303eca2390da87cb633a864f3ded3483b06989a399948580d0e772385dc66db5c01f2a40d3edd808dcf7fd2367d2635cf43803393d9dbad30a3dd1987ba8508d448945f04ad4ca280502f96c994aa6fca1c8c07c98493c782c7f9f70145f9c52d38bc5b6291f28a335ed9bd325a3d41769b52ea08dcc833d3d4d7d3f0160a0f8b57b88c1ee0a4a5c2057851ea5fd9013390b508cd99e42ccbc1794b2c45469cf8464258474774c30ab3cf5da3c4dad08908fdfb86472685b53bdb1b12719a4c3b6e0a074d5a7b6510799f414d2f27cb82ca30e22146c3d0d105e66809a74311867b2c00d37052f6d0847f6d0e2f4b620353f8c23cebb562a78b8b3d1b8b713bb671948dbb3fba4bb81c79945d47641e6149026ca1740476f3f0be6351138fd52d6ef045f7ac3631495665d9d3a102dd64612213078d227782a38aa4bfa94df1da2dc49a64a889c0bbdd06de913ca46187b82dae17a8e2618fad1c2e2e77aada6207e2b41f9c89705c277ee363ce78b2ceed7c36d7310087d3715257b7c153a18e7e88d44c9e0812e2d907a88e7192b8d4fc34c1032cd294a7b92c138fd3e888952a4468591b2d112598120425b27c847a0d8cb46e98a10c599666b8440d47c3590740c959c4b6271237e66befdfcac795eb6158a645016edaa1b6e4e9355c5c9b0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"98c0cf94ef6c606fafd7a170dae547063ae6a891aed3de833b5428d35621b616","proof":"7e0cde2d1dcad2b702305c9de07bcd75c187df6622fcfd9db1b822b0c3cf387c4e7a89187c402018b3fb119615c2e12cceaf0a2a14968d70128d34d554069c533c04b498c8dfcdd87fda887cbfe3605a9e82747e2c67fb8e9f90eb9b86c9db49465fccfdd0c4ccaa457fcf70a2d36ec0462d3dbdd2973702301585c5f05f1d0e1ed4422833a46cce3601d32d1864115b4ed730e15b81e36f6f0f905d3d9df20323eaa973e092f10947a925c62f7c7cdb1277ef4088dcd4b24eea7dacd5c1dc0e2567145ca93e1fcf97c1055995c9c6e49f3af921721e4478f4ccffd1a5c41502de07e79cfe8d7f1404487185d70174cec9df5e00b82197101328cb34fa67581f06debb2c08745dde1a322bac474fbf908d1ba83210af0049b1c9976b5b5bc2479c2296a3aad9f9f2563e97b1a7dc2a1d437e4692b1cddb15b72643a97f40ba3350180807c43a6e9a5f2de6a512e3f08d6c71411b57adf94cd2cd525016cbf60bac690c239db05894018828f328eaec024289a96f9b69b46c0154f70ec3e5036b10b580ff8549570e608808652d2d47040f07adaaaf0725b349e85eee3b5b843488d891490c8cac0a876da22ff17339c3ef2804e0ed40039f5c515b0f5292420202eef954534f6379fc80e1ce471ad90f93302650661410eb10a82a54fd0ac40dc0b753581ce390ba2a052d91c73a7035c68ef14ae178af8824dc0d35e7d7803298c3c2f0586879dc608c91320fd837aace60919bffb7dd3976201b8b5d568859fe57beda09550ce58d88f47cb2d0e3275814eb4968797998cc46a005e914661cde6f6589277f51baf504521a36878b0ce72bcf5cbd0d34bc5bd048a9754f843a3d92cf507d14c39a490545d2b9df02bf62a89dd833826fd5d061cdf1f844ec0c1946b493228791aed4a77fc50e12c861019d1fc6649165a72595d1511e70ad0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"041d0ab918c61e4e6aa5970ccef550ec938f728e2bf86994ab1f785aa6144f75","proof":"340e7e9bb960f3973780d762c26aa0fe26facdb8f9cf114d137c4f7ea794fb37cad31ba99987fbf3d0cfe3f75958e955ff33591f0ab3cd55235fd6f6ff02fa47183ef1758aae3ef24763e10e2fc02bbf5073daf5118a6101d06f03406986134276997dbd1937e87d6dd7ae9c74c72d4b06a1786f32cdf7cdd254f5ee87b9321e442f0ba983b8ea76449b8779176e9c360bd3044957b070dfafd3497ab1488e0a1b88d38acf37884fa1778a4de0086cfd59297f2c4d0036303b66d750606c13066250d205bd66648ccf20a237d2b10a71d4b2b790fb6fb206f9edab328f0d5f0cbea379ef263cf2b0cf2b2f95da84808adeb822552f131357712f1f7dc2e694659ab7d67cfee21e4ccafdd0980d767746c86871b7d40df056ffe6c05244e3936daa40dc7f3ed276694649ec6766239f098ea585fcf9a47b9c24fc213358239c7af8e15519005cdc82214a22209bd7079821d527080222a9b856b7009766bb0a34d0f9c24f09957b8798d7b2fe92e8f9f11d80149c263975a1c2f801c7e3d55267acdadc0fea3c3ee12fe3fef6f5d4424b648808f72a12c5f7d7b04e4b1ead413bd857f9ebb4250c28698fff3fc4362f6751c5d13d739399d71a74143083f6385e9e99076da5148c3339a202078415f926a31e17e9c1dfd41c2dec8ff35aec880a3463253957a6f1afe7723bd5ac97c6cd82984bddba6060931bf4452324860e2fb4b2c7d8a842df7d76f3847f9f439766ddc2672014e238e6bedf33fb19486734a05a9482a256068a6987e226db476f2231eca9007d0a6a17dfb391dc7b47d226046bbeeb49084dc63888f1b08c68cb653dd1a9900ea57cc1fb62b72d2a5f7a0d3bd66a64211003b9b143a3a58b08993db91b94cdb3bf33113e4e289d5a636d0b0034257e12b503b69dd6316831d1fb40d4794dc276c44d7074564276e3b4d709"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fe48da0d0a178750744c805637c6ea82c5133f3e674d2e04afc96e32879aeb25","proof":"a49b0cf9bc1eff1204954619fc9b9a1c68ac8681598a2d1be4dc091a75745634627afdf1681f25d833a19db5ebd1b3b8d239c59eb87d7f49942ccbef52594f4e6c61261c13ddfbedf55d03ac78e4b2e61fdb0bf734c562ec0c82478c6d62c91674a299eeb1042bd542eb138eb69e2ec4814ef3d939cb3bc6ed4a263030f08d2f5b1ef90569006304d149afc9086340700733cd7dfbec5e38386e832d799a7903d865ce2567f1b25fa6d3ad42fc3a03bfd8adefb51bd6b4f415a95b4320da950a73869c9908504e0219f27aa48077421bcd1c60b52b4a1eeabafcda434dfb590ffc417fae71d70d10bda4adfc4d9747342428530f9327b2bbdd9045cef7a33b38e2f9deefd921b79b9f22ed6461866e018c1331fa78f611981b5267854084677264514664da83a5e08eb50c9438d8148f28afe487bf5780b96431d8434a3f40037cbdab0128359eef1b2f581b39efd0061940189c35b0cba39dab86ed9f87c44f70a5ea8e73b3639850c7f3d06c70c133e457945b088abb0f391dd99dcfbaf42866ccf1f0809cff0376bf96c0426bdc8981f471b24ff9e7324960e066125273524255cb5b6aabf6e38172dd460a4641eb6655c04141dfe0333ffcf237227fc1171822a2f6a2d4ec260a8158614fe893991225281f2968aac3d601484fe304245b0c19cb9f66f0af55297a1634f3d23920beeee4ee2a529c511f786185c9a21045a643bc7b35d0a5df17a502e754b46a65bdac0606eeb101819ffd79df569c134b4259feb9cf1b78dd4ece765c50abe6c5d5d6b6d5f72264462cb0e21d0a5fb765a2703f6e8a91f5605264d5edd48b80969618ffcec6128914816b3dbdff7c5818d76df0c3309c8b23c5a46700688a347fa8bc8cd4a90153b9aa751c6825d407035fc868e9da6c762ed1469e77dd5157dcdc55a42468897613e076067a24f4a307"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0863fa57da536fe139a944c76d295acf76574e79575a9116024e0a60d5d8ae6f","proof":"16efa70a9112709d22d7eccbb5334ae323e4206d12604a7b23f2990386b6eb78fad8a26ad4efe2e828372ccd01dc38f87fe9b6e6c0eeeeb762ed7c6ef6b6f55c462430adae617c79ff88fabb334ae2b9159a5557cfe630ad0ba53367a2cb6e49be1b27108b934275588f0b20640e6008041e572f7839c118114f3e8770513338564dcfa4b63bd01cf13effe1dd64155ee61f268936a41798aa4234cb4ed5340642aeb47f298cc20d91abe4d5ce53a29d07413ea456af776b92489c4962326405eaa4221a1b2fc7be897c6ce860054ff9c9e65bb2be6570cd61ef14467124890b663aa2f13189ea28e084bfb06079a1a1c98efccbd4b8e8c3b69540a1e01981421c8c14d06b12304b57f1889755d523f7c3250ece68490996e2d24b19b4c20c0390d7c1651be98d0dc4cfc4298612f717bb034f4d804c63d6e7ad3a3832b7555cf475f10b9d69a191fd8fc8898bde6ebdd07b18f57929aa587f2bc9ec7dcadb1fe6d8f944a7c6c4f84a5a13e91fc85dced84cb9b73daaf64e14b5d7574b432a0da09843c474e6c77f15e06a885390fa7b7bf399f491aa4229de73493f1241f250ecb2eeec484087499d41e5241c85bd325d3c683209191c37fdad3c8e84b7252c6a2414d1968066c20b93dfe17c342fffd05f912d7fe7a75d6134174ed76f356b26772017ec251247269002c3d87425b3ac1ebe3252a0f4938a84d4ca32b46a7a14ff22b5b15e1c6e25f9a47f8d43452fd93c6d42418b93eae8e331577f2f3b449aa1956cdaae4fdf4bf37ee967b2583615db14224e14d5ac28c38cf8f058f4023eb263048103e2dbbf71c6eec3da9bc2f567989922163ad46b8327689d2b8f3af1abba436482a95918fe16792c32fc3043d0cb5cff2ad9c1964a3f4e14a69e08936c2410d859a551e2899523c31a13d5a6650f0e24f29b80e0924fc38884bf0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"901e7d6fcf7b29c98b90d443121a105cbaa105256b3663f42b200e002a21c210","proof":"da7411df1dd138ff2d906e2de8236ac027ff17739e0e44695e5f6946165f181162c47b7448e74e1db94f8fc8f8d6e91dc660a927a7fcdb551a9a2ae4e2c83302ee659def68e1e06c233644878eb3a9bd3d5ecd97220c2eaf2044b4786ab31c3c2c8ec3b38f1282fdc0d168f256789894e89c99c186793c5cfc18dda285acdd452ca86b5237fd41dff6e482377a80f9c5c45006becd507b0574e6a2e96279020f65bf11ffb362b4e51e2a7055181768cee54747dae485108aa017de7505d6be06125b3ac895d592b83c78a9a47109a02072bbe2c081b4e230682394369881c30ed0344a79c306f927923665041c7ffedda8afd5cf36accddfda699cf29f8743152060927318dd8f06c9190e2cd86e99792808328b049cb99e218f331f2ec20d190a3fe47b110c44047c814e713d748b24807a94e0988394ac3d38d2e3db38ca0138c4f4a55c015b6b68eb997638f4f058b2567260d673454897700468f86f3f3ee2f2e19f7db24c4620ebc25b36f857b503bf1c4f2972b4328d4222267f07693bfcacfddbe6bd79090f0588ffb668876c2dea76de14926e0423057f6391e93558c8706655f032413ed81f7cb5e1cc88a3e2ded15d801919cc57d3b345709abb3fca99c7de696dc0ad59639fb83a0667556052b355fbc33b98ecf78d422bd96528ea666fb7cf69f0ba39708462cfd2b390aac0bece0a628aa01326af25d20bbf0400f15d893403d98225be27a912ef5f3294a92643ad35b9840bde3cb5fb20ce482e13c9cc75f15be4ef0a67ce9ee9a1fd4d425137989917214216dbd0b4f7d60402ccbf0035ea93f7c656ad826ddc248854c873b4ed004133ca40c2e233a04a1c525aa66d33c22dc2521182d58c386304ccb43337ad0ad8d6451ad2117363a004bdb8d0e31fb1dd8bcf4091f839ae27d8e60ad28644be4c4bc29f030711793c0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ce562172b2167ec14ce8de5c554a70b7db960ad3bcf4c9a23fef91248b54dc5e","proof":"8e74235e94181372d249354e7ce5da70ade38364356fba5b7c37015c7807591d082f186e17a7068f3f3826c3d60636054ada9ef7b6d4cdc9924dd935429bf927025f5291b3687cd26e879ccba14899ea53616829baf1d5d6df3dc80004df79539ce9ad51e547c7dd912663f787bbe4ece0fe27b1d842d6e44b967ab18e7af90972a60f522ec62ad0935c717af3781197cb0245b41ae92a22134ea4141b6fdb0da498d0b654d9747a1816834f6ec201e9a741151e5d9f64f6a89ff480caa465067031aab8022a40ef69d4f4cbf3d33cc3089a98db8db46ca07f51f9ab4e62090e2a390d9679cf17c9a0636e326eaa9a71703447fa2929e2e108b36f3461c1a46d183fb79c3aa05b622f0d9b6c02380d929a54cd7c37a66cff4c6ea9cc0dacbc4fe206695e0ba357e87fe431ec9a6de37378327ce6a0209d19cbeea26ab9d53c44006c0db7e616200811bf03c8a1f62a85e916a69b59ba07e8386d083549872925b8c292d93c4bb6bb014e9f0f7197bc28525421af93fc6bc0e267310d04018545c0d593c9ff2f3685152bfb93140097eba54bbb85ceea01cd01009a1d78d40f7db2e95a8a73cdbd3561255baa1c1adb0bb21e98bbbaac008daf91246b75c8c56c7c5dc17c291fd2594dbfff98be6b1b1ed5b50b62b1d7eafe3f07ef5e2a213e4c26665627f8b1f3af3fcfc1677185094273670966af7948c651e22e4a59cde16428cb7f3c29a5c0c8fda82d3749b36bda32a3317a66e6ea4ac7a473093a3a5b0a30d7390d4cd1ae2d9e577c36b702498649de89b75dea9d4928b56606318721740257013779bcc8441f2e270f162f2bb51d12cf7fafb387f72d67af147a27906692454290c4ec0934142e9e8435edec85095645333d6da3179899bdddb2b4e2000bab92cf14c5fe3cb7bdbbbd269333ab3017cfcea64013b31b9bac3ffbddfa0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fccffb19e1c1caafd66cffe8e5fddcb56bd61668676c5518b586cdb27c9c3a62","proof":"6e6f0c900f0590c596b5babb76bc1741db42cdc7f01a12a3766060f1c3634256cca50006269739569546ecb935f257fd19aa64a0108b25281da77d5f3fa68e18ac247cc2e2c0d0430579e20b1951d95eae85a7879144968c1294dcee82a67d455851f1e5120ceb678c2450b385e07d97f37ef53ce1816fc8e4db3cc01e985b7d18d4c632e9768e590c9c17d248bb5a6779c3bc71bbbad8bce48ca56091b46c0421734b0191aa1e2fb5fcdf46c8398d6fe97c181f3a3417417ae3f5f188684a0c4e3f2fe4181e2736b131bb227e762b37e6e5399144710972e4839392bc2f0d08c28fea25ef055e738cd22b45603ea7244e574861c38c0bd9ab99b6d6f57d37092ce1071c45b2c6f700edbec32862baf628b19fba36a17a1320f5eba25bd1516f46cb4855109d7f5fe1884b5cfaa6cd675f698166ccdb1c30646bac77a7da817b188da93e1ea799286857265d1089233a7c2b561efddb04ca5e659ae296744d76644c99753da348a209a7d76c141f9644e60806d80ac13892758887a41c3ea225307a07c95012e3b2b2b0314e8cb2ec606fc193fce628e2dc633e11b9428d216bf4aae7760b173f3a4788529197f9d7294bb72b88812bc325e0aa9f238dce0f000e4e7c3d5ae88eb4a3eaa69078e4d081ac37b6bf3c38397dcedd2f5923bd446dc6df3648819358e36e8305411403250e86762610c8fa8e68f45c458d8fbf3822c02d91d4cd709c27b1a17d34cc09c969d244f375513549a33dfa5a6d8f2b6f0cb6bebba61db029804a101ab02ba058d90704b6e85cba69cee1f7261ca4120a1cb4c912072b3c5edc00683457620a41c628a13055c67ab3de63730bc87467f64ff112387fdbb5843fbd4e4bc1e98fd25f11051cb537baaf7a6e5efa6b7907fe0effca6e88a516ea519473494a177e6007834c0c20ac06623b8315707e3e0e2107"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4adeb1f470c0aaee815c4c93a5a8c13bb9193e44363fa9fffacdfe901159cf5a","proof":"cae97221d87ce9b8900ba1d91f4d7e25a846aad82432f1cf72244743271b5e246410b5adb5e3d4cd7fbedded7547b10792637b08007fc042adc3b78d42b3d65a3894437d94fe30a3d6a97aebc12ae6e23788bad8d37c031f254772395bb03972d23df9e57157e4c6ac0423da706bf7b8000c4ccd11c538a61a2717cf529c91289985795f5c4be341ef76a496812dda331cadc579b4e2e90329ec261418bf0d076e37d3520467405b4d8df913ecc66fd328464bba4bee18b09140f0cd03962b0a7c8cc21c5ce3e6801723b6cbac818efdb02d540d49519d63dfe504ac52cf6f0afc587f34fde198b29aafb1fe210ae4aba9cf5b9d5e78a33c1750a12095093b450cc18b59ca9903241e7f1dffed7c5bf048a1e46ddda08ba899bf299a5a1f2f040a1a2e36ae81715f49461fa580097cc0d0099bd99cb1852d98ebb046533672730e795dc47fe4dc407d404863097f9cecd7ccb1de4b04844fe699e90d40c32459a430fa7de4334855a1846fc4f83a85eff08ea5fcff036c8041758acc6c303b01762891c5eccd2571faf5b7dd7dc1e8dfd6fbc3113bb2aab7787a6fb1d8ee9c63a8855f13f28f59fa4dd60e2fa31e72014d61193400c47af210624103f28f7348d678c9222c401ad2c35b5defc069e9c8a42a90308dd57c5336bf0fe6466da50abe34cb2e3b5ffea69ddbe8dc24bf0d8e8b9ab24f448c76b3b301f7257d218d53469552d4352d1c7adc3c9b14002e8b988f7d239228f2f6bca771d713fe0ced29e472d25d30feb18c97703aad396dde68bd9ad4f7dd759f39f05d56d86b7fc678c6841e0c20d99b2c8961717c2e52e86b021536a83163123c0c398415ea547c63496601d8a0869a48745b2aaa21abfe52063a6a02f40d74e2126d072bc1b0340b712bc14a85d98d7f67efd090b6d262f7d1377ced0b864308a5c5306c77a0c002"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bc10bf186cc0fef8f37818f78e8ae69bc6a2afa0e7affb5a67f2520a5d2afb54","proof":"d89e7d38454a66c06ab51b3974ac597e806fea3aeec3bec10dd6c2c4e3b63306183db9f2fc869faeac37f93b1f342537095e51c1647878f036760e2bcdc05e7f0283822368dbe282c3f36d4f2c638263688cf4281c53fc62963f0b1b93bc2706783769842417277c1dfe9b222d22eb80e026241c34c476c0b08156be8d4945506f2eaaffc67a3f1ef491b429ac3445468b957bb173820d8a1d0c3a7fce81ff05ba138c8df8cb02bf48d04408757a621248b7f13151fa645b430428c0546a52063a0288841ccf6128252ff01c4a8a73a088d3d133a5e537a99f6c8b877d787505b041b58ed60ade45f9ce26f81018fad66c241f01402db7a8656bef3c44fb45785801be1889416a57459ed7739266fef7884e0dc20260146f85ef93b30cb9d949181183fb362f67cc1fc70fa368c0b894547a204a188bbea1de66a6c4fdfdbc3b5e517ea9bdb0a615876c9a7beec8b1eaf8c1882276fd0c78b8f5536e6b4f0c61a03e2c65808f828c98c84eaf44b19b1d1ff1e4d08cad12346e4c14d9108ec745f8af3670ccb1ff8124b3fb2020ecbff181123a7c231d1d1ceca4ecbc9aa9827e225757a516aa98006994c18e3411a9e3a13728e0f1c2496647050128d8df9357be35a283f35da061f006ccb23d1f596fcc6d8a37ba956cf2f88e6e7a64a4006abe3b02273c0a92041310f17d3f7084f19e0ed86f35e2984926f0c76f3d6575235606af213b945f137e128a8151d1257f18e2105a7f7f5c36e2f97208a4f7d8467c1052675240455484c2878c6b8356774b0f890fb2fddac48627adfc8ff6723ad20887029d0476feb6130787656df1bdd54d24a0b3fb1c8565a2571369342950a6e1b9b6c30624064fd85017635584263eae3563199ab15e260f8cbc7e3fde0ccb3198079bf03a6212de5f79b3e523d9147f16ba68baaa534d5ec4ef52e46608"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7a4259c9d09e1500f8be1fa82eeab5885735d31e1d697385f14867445d4a4437","proof":"d895e8c3bdd0c7543880b89390a8b8656a5ef05cb2801dd4c5a9da96e063fe57246dc463bad1dff6bcc5155691cd974d1ce6284502485559f54a0cc1afc63128e82dc54ef921fe0dea2d1abe3a2fdcab242b9b9af04b62729ffb47cdc879481bba9bb8605b26354b4475b22b9d3985b99ce378149d8baf569311bcec7125be2d17b7edd2b389670ed39387acf73279a7bdc8795af9181de9aa5d01d3618ae1005a0a0a03a33539b6244cab164f7d75df48e14ad25d018d01db3bb1dc366dc10c051b1023befa61214909d4d5b9cca524dd1f913ba18c2bba9ba70b8cc472cb039e00498cce1ffb5acf594b858710ead49653ad8291828d1448c316adbeb17857fa3166160de210b733daee658b6d023f3c00e0663de7b94615b4ac4eed8e64659e2f60d83c37b9540fe3845ae89a7b03fd1e69f3c1a9b3d6edf7511ef50d8d036a5eacdebcca174a89f4c8cbf2457703a13b5868dcc7acc1ccbfd31fa2f2453be0f2d7a9d9c7d81c45d95bac4d9deb14ac29faad4495b852faf53804c29d0c26ae538cb7f791d38d4191d77c33ec20a00cfd78b7cd1e184db30220b1dec3653ee249e3cbf674473c6bc1f64d07175c82b22e4c4b31bb6eab0678bd92c68af51756fff3f43dfdcc92813e2723cc830ef3a692614567ba8b38164f748c8110d3644079c0e1c4c4917810bfd97890c33da8f0465aedc35e3d48020e2be61e810173d0c09e0521d0f1c95fbb1e9b8d8957fde0ef3d1bd39c5799d4797c1ec2dddb0368acf9807c705bc2eb899447c10bd5c997ef5cbd2e64bb48abee1d4c2a3c03764420a4e59d11f31a548bb2adb4aa9ba32ee275ad9d853c39cdde7c92cd4e7d7d37ab16459bc9dac8bce4d1366d89b326e75000aa1b4d2ac4bb1c93809e9e7800516d0fef9ad52c70cec23159b94824abda5e7adf6ab65689c1cc21169fc6a20f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e468ddbfce10269cebd3660a0bbfb59b05a240747239e5bfd4473133a89cfa5d","proof":"5294f57f4244cb5390114a5ecbaec15fa054b126dfce2636bd0fa6c1dec5092860a075f5b7d9910f492c363633517b169baa53c894227fc56af567daaebb0d04fe38799949d619adf2ea13f2d8832964c2fa8e9834f474c5f998d7bf1ac66405caea62f56d08a22e9323ff5d19248d9a6962ae1388dee2b9b6e7cba1e5ece95a14be1c3084d4c7f800a23894c7cbc571b20f9c6f1112c76f014f6dcef346c50a07c6bce3b8f98d8b9bf5738b1d622b4055b9a07e0a4c9ae4d7632e2c75c3b70b991d91c6fba340a7427ba3081b94b0159c1f89b05b1232aed54bf6010199820a3e62a8d89ec7a4646894fbbdcb6e0b945ef03dea8a7c2c064bc2e28d39de6773eae32f759d2b27097d6ccfe858f474f4f495288bd45b86cf4003aba2522ab56e3ac85063c18786e39ff61c52845823b2b485fa7273d54e35badb61e5481ca33614d069eeed08b00a109c04608a86a48deac5f5730efe70c618b5153b3dc6c46186fe4df96e34da3b2385b4f4b596cfe7d37795db2c5ccd18a3ade3cf3c6781138c3c2f6a292d9ef745f92222ebb5791124f0241a11db06d6d9148db03191ef04400895536e570946695bfb13345804e1932ad67d6dca3f77d90c6001ea9ea44af6592cd6b12804c97b3607a78f935b9f6487b83cc4167bbd5862e303c67d362746f602f7700b68b232cfc4646b1dee9e835b14dacdedca145645b36867b70c3eaa65e93d8e87bf87edfd6937884a2b8383dbc91bc6f31540ebe48bbefc1f8d473a6d7e0cb488302052939df6bcf58a71c84e8aa9b3b46200722b3d6bf3f53e46ea2d884972fb9e160b6b96730cf47e016858124496b51474f2472fc15ad60a4099256019b599f8b870b08f1b5a4a32d90d4562f75a2885d35fe392755d3c230d5c3716520f349015e5e4c3ec7732e6c72c784e70d8c4637ee1ab7519f16ae206"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b0d45e4d9610df37f5ea66af485b1c2c9c39ffe7638e4c2455020be254d0182d","proof":"04c26ec40ae10d8436533b1c703157c7499e0c877a888ab07cc4bcbea3c79955ac0221e5436f8131656ea167ecc0e2b64e6f5891cb57bf640e5dab3022d71840f6b4f0bc7ced40e3fc1ab79a55c7e1612ed542f2bfb3d71e7fa7d5290330cb4d7c4f1d57057a50c1d5a42df4bb80c85e0d8713b7b3285f771bd35b591e8a5e21f9c941c0f67f0b79cce920c78700e00a8a25e582c55a51fd32c4f0a6b9c93c0b817639d3a328d9cef3305bca3546f9a386ae02bfe6b7b38b7d734159168a8f0c82cfa74c4d19b743b7f9cb0ca4f08ad41ce575e8b220aa811a55a342752fa6032e61c151aebe4bb946c427e17224a8f783875ff2da5b02706e6ac8e8bdb6ed38c69b89ff2192ba6adb2a60ed5ad49cdd5f1424c9379adf4d0763960253ea7e746ef2119520383b2ddee9978ed2ba3b2b14e8aca0cde5ee95697f5283cfa0614762a4a9ee092f93c372906696d6dd43e9f94b31360f15f1ee1fb4e195cbc5b87ff6f559df318f98bf55fd27a7758bef649f6e97dc9d2b2db037e23403d759e50ff4ce45799eeb00b0d63821aab8d97c995e84959afd85a3da636c5b86cb445b23f219d7523a57290b925c2bd059f575002e7f0f1e80c54489306265017967745cb44367708b3448207b596c4cea8b3b31ecbc4b67fbd2c278c8f8f06a24350f49aeb11ecec439fa607937d5a13f7dbb86a11adf1445ccc66dbed3c2a781522e0e009185c0393755f807d1bdd0fda399adfdd703ee197641a06d8147fbe8c09c73fc1c4267b9ca92b0ed734711f7319feba7d38ceceddbbaab63e7df992bc10914ce9481e80b6e3026fbba65c2cdd8d65951e18ea60fd57ed62c07bd0ebc52d504204c238e1b82ef3bcbef4b9650a4aac5d864f4e3c897eff95167930700b69d06fdde730e974b45cebc7a3b1e769c4d3f28ff3253d9f59f8d8c8a853bc0321907"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7c31dc0b053da968a9583782fa30dab3ed15235a211e124da820227a92b0646d","proof":"c0f3143388b8be795d6ff0054f3406779fa66574c64d011b1af44c9847f2f26bd61e1bdf0faac2e31504879e38ba7cb5f3c583637753982028bd87aa230f47269cf5b7195ea4871d4d7c20afbcde131d91d59b91002fb186a1239ecc4bbf386426695b40f8f68e9bd69828c03cd8a2fedad1e2bc9657313150b012122e1e455982c045c67588f956e8c3867833e752b57de78303521ce9e1165e70cc3cebdc054b1452b317b4157b78003074b3dde85e470ec9642dfbe77f7b6d68e57bb7e80ac09001a0030b9abc0fe90945b8908417115993a25c55b93f015ec8f2bdbe99009a3f4d05b5f34f3cad15d17bfd5a9e2e486986531130d240571888181c43296f60e06e82dc05c30224c5ad682e170770f36c46062697a0fc1146c00ae1d97c43aad14498d6842921a0d3b688d9db78ddc77568b6d24918216577651916d8897950aba80545bc82b50d73be73b9727470c1d79b5b46aa41278cdc8ca8e2008b7842811d3ea5a47668091b682e5f5c54f1fca7a9b71b77957dbcc54f80b828fb0168b6b3723cda1dbf915c02d8f329457e3ca9357e3e39f5671a8e1e328b10cd19e092986cbaf0d23bdd3a6cb770220c2ff73ef45b044ab50aec40058c61086850fedf698f770838a1e7611eefad0ff7a1292122db4ba362a6755e502c1b7b846f2259607326fe765b13214da1fc7bdcae18ac9267cb3e386c1fa77a6cea781746a81afa9fdf780d759d68ee6fac9982879b092526a3eb838a3e5461b9b4353703724eec13ab2cf32f6ad20929090a53c22148cd8a84e31e6c7d8fa465ffe9ca6816efe1476c284339e89570e40a26a4791db2951edb275add999c73b78590054c12b30d2858c84e05d962556be103bdd35b07da87402a632d34c5885567dfe2087ca28b8526feddf17b4936471f7c4ef948b6e3547bfd62cfc0a7d1388ac62600"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f8c71a1c87413b648ee5219753449a55289213dc1d14488138625db17973302f","proof":"36b0ddf7a1a878966f82cf661b173bafff428ec6e6fd4a0a0a7562428e210f6cb8f46bf61d686a9065cc71fd61a9e7f7a7a6b5c99a74c4750e4197861539c7538e59fc30863f03057a70b0b026bab41492006ea56ec48aa8992d8c09a3c8e17f8cff7590f7e97a84215306650e968e6bdd351d9983be4dcde3c1e4f655b47b0ebeda953f6b894ef3c12eda96b1ebe9ef6d3cad81dd05e020953b800ca26f37049104a5a9ece09463cf0702635c1bf159ce26a0cde3ba603b982017184814b900c875467fb98c979c153b8a00b7792ca4b3cd9fb5c9dca327e31c152d59831c0f9c81af91d71985371dff2bc2414088e70ee4de3f02fc0c4349154043a82ca6707a04e5c4fcccd51c753755ccf7195b609259970c6c174418ebd94a835f92f715366936a59934764b67b5223ecc1e1ff539cf395b75c78b3d43b40f72b8ff7a58127e757e892a0a47efd72f03cf4dbbc6a51d86d5be8dc6e54484b74780cd191176861de64902ac218bedb7f72305aa0526769a2b8124d7be4e451a20365b7339cc3caf397aca9773ab8403e5f1045a1fddc141678705eddf9a501601eef9804eb8b2f5c64004e8f27f61d95a0c2618ad063d7610a58407dc37716e31cf175038ce1c8b5bfcc28e873ade27152a93503360064bf278395eaa71d6e54b0b2cc47b5c8a300630071f2818b6b1595c9098c540202900bbdc566940d79e5788db4f3a1c0bd2de08666b9a7a7696ecc0f2f55453d02f8c3fc9759b89e639a36a13e035c204a536ba9580ed1d97a7af571f1929ca83133c0a4e3754bd034ff18f30ab00f61684ce2df4724b40ff4c95e6c04488d3b29fd2e4dfdbd72350bf0d01535d159549b59faf48293d74272d9afb80e1aa75d651ebbea1117c44da87f84a0e160c9f3d4cb50a33421c208286c0b2db13f9d1c8103976447302450f9fb0a1d45702"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9e8dfc5171791b3ddfc733ef33e6795747a450cccab75ee1e57202e62b0e3362","proof":"3c15db857cc0e98eb22aa81a51a8b7ff3d682becee6d7a232c815e9068ef6736c6024cc6444a347dfd85c1d7b1627e79307b6eeee3f665f1aab57257ea8e3f57049ccddd5cc42c48d76e3a51088e6b9cae5d961fe758c9648f638fa38256e11ab6bd7821771286ac0f292189376d561b83fb07c4eef6fee01133ba6d8a0efb5b4de38347def3744677aebf12b43643a25c86e7735048800e4d98292e5782930b79ea311f10c70d2dd58b82f2ba9943c1f1cd2bd1656083859b3305bbc397460305ab053b1cd84c669d22985b89ab99ff913200cbe55c888109bc6b0e796d7e0dec71c16485a3176ec4599cbd9cb3d4aab71743468f8a723fd67471650ec8092c24ce47983fccd75838298ba83282a3c47e5bfe5711687020aeef7b90c4e57002aa09144cadbe7031d4134ef6e4821f685cddbfa9941f31a9c64fd2fb7a7e231b6ca92a04b3e7127fbb615585bc663793c4e42d5ddf86129f779be0558777f42c301e034cacb7058ba00b0b33d4319cb0a00ac37ae4736a673613c6dfd359bd4a980fa3fc78a7056e08f81d38f87f100609506ca4f8cc12d15c2de0da5382240ba8e7c199aed5fd158e84b0680aaf9073c75051dba818c6a1f27e8f790f3e204de422524995fc6e7b373cc19e4f9d279b577bb74782a7c30b82a481f89911cb74fcf18e3ef5a41b372829fa8787a2a4af40ac3e254d6544a6a4f6d1de14753f158ea817c5b3661b57b8a11d2cf818deae145bf783f2386dce24d0b9b3fc6e872cba6701dbf7878050e972d11241d9dd1ebb677528111ffb612aa384eefd21210884a54e0113be85e1d33cc168c7aaa44defd1f4aabf3f316d8dd39799c8d9d559c94c9b3e31e11cc6703573b0a691ebb80e51b776263a0aea23dec9d520ce16097d50276e2cb5ae7b2312999ac327ff0da0c5cc5f651e26a4dc7835992c6c0f0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"426b4015a68927e8524cbe5401359655e56629a3dbc547f5596c31fcf9aaa75f","proof":"f83c553e48bd799a45c4df77d83dcfb263e3d8058dcbeed3fce3eefd3987b66b3a8b6774494181184f34fd7dd0bf818f2fa69f98a1d137f1e3f4ca930d081745c0a946a64b1ae6cadeeda17cdec7a45694ed1beeab05a06f88d06e1efba44646ec00a22db0311ad2017a5b5e947ade78c02efc9a850efb436abbfa4ac70fdb00bd6303c213c085363840a7107601f429b56eb3b7d3cd8623e8e8a8b9661d5f0f7dd3d4a3b6ed16b09e98bf75c4adea7c27cdd3d35bfc4c4858e508daf1e9cc01b8100c30302ff579da2b7a7df547fad4a445299710cda796bf746a1d081571012484ab1f4848b23bb9c1e302a62b203c931261d71ccafc5f15fef91438f7af55a064786d436a95001e8e10e3872b022e8de8b4b7f0a7a1691c17e95082a4a83c9277a00f179d1901c8d1b6b57cd70b06ae1803bd8d0f6852280a047a17e0734038b1266848bda94ed441a2820ffa288bc463f5dfec6b76227a69963f0d00205532975e5812b5490be40221f11a85e740515b291995e308a70843cac5fddeac744c5d4055e2ea9787cde8c684cb4afb28ba8c5164392d814866a78e61490b0b076e1f57c736fcd64047ec693c5cdb974134dff67518ca9f86e6a1b379cb52675d20d654f45feb5f5bbe9957449e4555f173c09882a5107d1d63e666d617c1e96108114fa5f9e49798e65a870a953e265c33c1f4972dc21769f1183627e3630d02c6430a1f0a20d66218e27fb44722f25e60b21f9dfaf326a40438594773790012b213916985755ee8afda4b6e0b6a296b0df4295ff5a9f45f116dc99750fcfd44ee00a145d36d7c3d3782669f9d7ebe79690157b10ca8452014fdd39fb380a10fdfbf0f600fef2104e4bf809a41dc10d7462a0f48dbf3c98f83c1c794c06a570a90704cd2bae49c939bc0e78452faa308f26dc405110c0bb1d63f37143165ab07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"467a24bf38871585e394d5b6038879e958850f40a5c64365320a92358d973d5f","proof":"fa57dc55a8d21de33baf2ea38b3b1f6db608f50f27dea0df23658753d75ad35c62fa95bc9e42d5b078cdce26072b6511d459a8c55cad9d861b17dca3c5d1c932eabfca624506c35ff37f7c922ead95227aa04ad45bba606e37a33d791f8c6c253e5ce4e0c5215331b8974e6249647020cd929bd25d902ea229126d5bf330a5102d546d5eca35e81b2afe2ab415f322bddc2059e81346996e18b4b7b3ff508d0f725a78133e044c506122e73eed14b220caf385a4fbc4ca66f1fbbf4bf095c30c1ac4d7cfd0f8c91a6423a6a8a51ae1f4846e84da9826f352c5b3943560bba9015624929c440f03487b5059913d66e91bae6f4c4340465a60eb7d401d0ea0c06496353522efe816449a77c9867b6db84384cff07b26674fe03cafa4d4ce66ad48de6ea80944cf74d99cd6486e40ccf6817a8e2aca1e707f521e0276909f9e9c4886d23edf8de80aa93d9f9aeb97dcb48ddf8e462be05c43c52e3ee9f61c431b77bede49f15cfd94de946a16be88f4193e3b2e95d8eec584553ab75a18e6184e43c47f974f4b83546da3d5ff45a5d6f45b9172edb9b9a09a0736851ef10f506b3b8e66dc987dcf5f0beb6305667915a407d783a3e463f9976a1e9995a503687c11be744fa2a8f1ecb932c0c9346908e60a8e587ae6c05ebaf0cff9675a62f4b93d7a8b7e54fc0d19decaf803976b718661bcddead4b2b37d847588e9d6358d205fecd9f41e778724d0c667ca146ad6b1adff54afbe7b5b200d193356f2b838887984796706bf78d181ca5183f28427833b43b8ce5ff933d66fed1d98097d014900f0c797fd22b1611ed492a34708e6e9d86be3f69abe5fc266ac93183e0c4dc91ef963b28f275f4d7f3da4f7cd392a1b466693a626ecc4d99e4df51f5eeefd8700917698e9667f23cd809215deacbe9903992755d3d80482acf07477ad8c36df08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ece4869c1971628734389a4423b9d7a408b08ae9377e7eb3ad1a57ded61e9840","proof":"9ed8b43811745be542e7be3020392b84248c154c912ae9b3556b63c20ccb51216cfdc4f30994a61b829083cd5c8c591bd29c54027815e8ae0d8cc552bd936216424c071e1ae8e876b341eb2a9df30f50a6bc120f12f62bea30bb327fa07264638ed31755f5c4cddb31c78619a445c480601e1e7605f2ce63c54bef661ff67941d482ab0bc9f8465d35bc2c63e464451a04d89d67920724cb4a2eddc3170d21031b4a55c69a2eb12ad951d42b8b52cce487f87a6f6764bdbffb117e3bf2c8f90539d32d50a43657ab26c4d91e16e64d5d87c1a1edadbb8d3d6cffc929aa02330eda8cb0e0d5e7c5a3dc04685423e1cf103fba02ffd30455ef5c19887c628640121445de32b793832faecf397343d8df41a238942ff420377d1b5e4bd881686020ae4e7391ed734b3200bb07071556ddd0e686a2b78f6f93fdc3e68cc5a17b26786ef4b0d0e2d34e9d434a5a66a51500817e6ed72258c9f64c2da9cfd432d50e7a34c0ca375f66377b65103316c25ccf019b13d1412ebd2d41638e1d54b4f06a3c82d4d1dd8cca7dbae26c9e7dccdb4ab468062f5a490b4520fad0a911f532c9389a2e602c1432a83fcb50d69f857a411c8db5e22bdd14e910905a2d650748cc6506a67d68ba3c99da77d26e26fc2e3bebd362c41ee365bcb577029e9bcee4d15a64a882163728bfd7fcfff498b9985d36ee7610cb8ed2019a7a3f10ecbd7062131eefb1bcf033dfb1d751e867cbbd0525e9feed16030a3e55d3872ae3b2f1d37c1e7275570e538d67b4a02f222cea15dc3149d90346e9fc01843e7dac6026b0640442e725cb23d69f758f86632e25db0264bcacdca82325bf73e83136cb5dd569c771c0ac715bc41404f2362af20b868051bc72aaf67f6480e95a0f00b253380d82a27e2954e590e3b612d1b55f1ea5194dec933c0c78fc67a6bcdd9b21b99301"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"465b344a024c7a2c7e46cd90b2ee8ac9c345037e0f2e5e917f6eda7444f0777c","proof":"92e33cfd0b3e46459f728f48d792db98e50eb006cdba0e758cd1583dd3810b0b5cdc66cea4a45b471015f4d73dc4a99229bc3eebe16246bbb4929bd7d243fc23208d3aaa00f1c448d230f6f1e91e9a8f6ced0a2c3039cb107027e1731f8dbf0cacf48a90f9ba8bd86f595af10a663e74d241644c86eaa633f1b77379ca94e11c83333886bcff4aa90ae3a4901e75a0f151b3771f6d66b292f3a914d6ebb4c10b576c6fc5529c6c7256307f683f076687a084f075c09cdcf65313ae8ec077d70ec22b3823136832ce097a4825c43553d041d9b6f4fdfc144f72d706d7f6bec40b7aef90838f5adb416aeadb7948e6b9ad9b3a93997a7792e6a35f64b755a99a477e7dd322aa5616c95f48e8c37c3e22c6c0ad7a5a89984ed99ae072abac99a725e66eca694590996928ccda363168cfd55674b1d75e5017499cb80a60f069424f7c193876fcde1e91961b5b869b2edeaced98e73a9505c51a2f74266b9d875c74583ea473b5965bb9f18535bd37c647ec1724ca1635cfcf881d1cce0227484b060a791a05770f1399fd9fc8824c6b9b8210a8bfe5bb671df203482d6dceee2a6e82af421465f62bf3f0b156054d8eafb9ce65829b17744f8cebd2583daa375b6f00aa2ae56c76179e671c45b381a18ffd5e9576df386a86b62c2fd010acf6365a3a15fc7373e5cb3e7743ccac0195ac419471d3ba889cc6870c42d95446992f0f5a6ebb25b75ec07c1ebf190d22f07a754e30f622d0db4e0f48c0f394dbb46e149a890d672057bbf7915993482ce8266dab94ded346b75a203ef93ffd0cf3e533989698e103e1d06c271affdea0b31f768be794f80db0259ca602a56d499da047ebe9a2b63e7fde0215c817c284d432dcb8f09162ec93655b561d05aa9e4e78066c6da969ac1cf3240703b3a78108ff770fb5814f0575f8a3ade2a33339552706"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"424f03828048a6db99977bf797294123c667b50d533253e55e74e2e624ff9c62","proof":"2ef4250715e67554321052b251f94385f65019b0529848c3e5ecf8004927160acced1a4aec1cea398ec1ee1c1f9e810867858fb15cb3a86af7f6fed102390541d28eab83ef03837fe09d7521282832129a78c77a35325feb81fe4af144454d628ce24143fbf8fc2146e043a35152cecead7877a2e41ce2ba7c5434628d6dca6cd668a4a5ea7f590a838f492bd2352827399676c6fe88c8f0d76847ce91e3d506a677296343e03377fad065447a652c774365d794d7c4ec4ff76965b8f3fafc0f0ef0a83fbd975ec2bf4010d1fc98a6ca036507cbc50965c62238f69de01252047e4d92dcaaaa8e77c7e2094c6c61d7484be0445470b6f096ed93291d0cd6c8232037c61bea253308e1dcb2e5002805491179e4d4a6484cdf1b79e852ead9dd4a58ce0a36c81671b2a6c94c855809ac29af4f86d395f772cedd451c33ad3aa16f02413c27012d42196976347fe044eb33eac5026ed5597197ae01e8cb1bf825440401e4cc70a54c59bb833b9171c317e5b60690cf8d3b56951bfd4c863173ae6592e42539381206a243686f6850e5b06fa80dda8eabb8bafbaf680a37916ef9538273c2cbe7b49c23972224a2e8a4b0d5a00d7bef5cba208bd38a29f3b2d1f42324c19727ffb6334a04e0e283c08f8e6e152f13ea4696cb91d48432442277b801f89ae2ca061a898639e8b584ad89accfdfac74ed462ed89a8eab6c1e82d26c3a625b491cb41f9b8ef06abb44c44ad271b59727674d8aee74b20944d3c1032b49826642fefb9dc21a586bb80401d41aed9bc230a52bd4662898091f0357d4f6772ea88c78b02b85d748e9b5414bbd70c380e4823becb59e01f6cf8f4c129b35490d61d0c7d4fcd9d3f1c787115beb30dab52b2d9f1aeac1c42225b78f1e6cdb0025cfb32140c0749a29142003de18f23943bdb5f2c4c40d79aec6cd589832be03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f62ef35bd3a51a34702a08bc9a9e809ebd42bed345bca9af061c20116f9e546d","proof":"acdd72786d95e8dbd9d092ac000c123872739b24486a5e96db17a0df8f6d8d17fcae06d96853d0eee5ab66068efbf62307e0ba2fd39413f049f7b9ffe521a2567e37840a8bc30e80f9eb63f9fa34a71dca95e890445cc0986aa58d885eb40a2e1e358bb60f99e549e7dadddd43270a463c372a95bc4a85687b2102e5ff909a5c4e09889b3b2a92e0c4dce4b1180c1d93ed1d87d4091797fdd9e3259eba965602026d2e835b3f1186d9b5f39c1af3b98a5c16487a63907c23cd5d55d1bf5ccc0c42bbb3c4e92321ecf3026062dc9ca4b23cd4538e3485dafedc7ee4b27db0e90566e7c03bb01ba23d0c9f54fda3539d16af2a475672bce10d1731cc5b2df8df00f86e37912b07e1c3e4e4341fa69d1b60858938f9ac98a3a6d42b9df96051b057e47fe8577ca0b33fefd73225d094b4c1175e7c038cd839fd0195d8f15727dc4356f0d54c2a85b54605d3cf34a725c2aa0e69da4109e04a6081c4f5ea2317fc5d7c235167d7701109dc95e0d2509ce7ba7163d2eaa42d37813c7d630e1576db44e2a6ce05130bde75f77ba57db7b6f3a8f4c92342365a701322ecbb684ee7ea58aa2dc0372ed491af4aa77dc21f106ae080a9192a25c84415ffa47ae0b2cb9273865874bf9168a8424f5d718f1e2c859d5272bcb7dff1cd5b1741528d1ba64f2138fe090bc3805402b768164f9dc21db301fb9ae59192e35b1d005c7a5a2c38131c799c3e637eabe8d6e37426ede6f47fef34cfe456d410f77e7051f5d82d8a53c4786b9fcd1230cccd04529da8c2495e069e26fccef6e888110f4246d82ad54204f656d456d7ca2786b60fa08e3986036c9dbe7e7113ef127ba7a57c20a15139471ee8f4a29b27964f36f7ff5dbea9190f10f86bc4480fb3917827eac083490e624060d617d97fd49ad21d148c368af21e3db989e627e6254788250f91b01d07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ba3aa0006c28969a8c09c5e2d442ad0700bd0d96b2ae34ec1193b9ba99637615","proof":"9ac353e858d3f5023fe64edafe38db12bce19dc24fcdd1c2218e7bda57005567020200f1bd41c4e8b585e059c01fc71c820db9dfc1e26d6479bfcee589d9a10fded81b83f6f8661e3b4fc5d144f8b7106df1308749d903f2f87b6bce47c66b528add75313e79e07cdd473d4199ea7fa472bf98f2ab9db25fe4e0e6119aa1a3377d08fe80bb06c892cf9b366299b8bfc9113891b81ed240b49fab966b43eb730d8990b0f2a7ee95d529fcbd5bd97ef15630ffe75e0a382dd0431c9430687f4809f04e81b4d7888faf699ad08ed178725850e5e96bb176f56e2109b9d4bea3ea0750811fe0a9cb320c6ecb5fe10f9babf2284c03889712efecae77ff3639421646e454b519a0db9ad7582523ae7a8b3f8d8fde059663dd45c33aff0d61a77c68533617f2d3b7f45b3f69e96016ab5823f495e74c329f50259924b4ac7ec9275d56044f5e461a3790ae1d84b57177a981eb440e78aa329c4019ff4bd53b89e0f22a6849f6b0e91c5b03c4f855c2681f254a1e7a8da44344b876ad5cff69e62e5a364c6ff0c5c0b09c040981c5d08a7d3589121b8278feaf3bb28922a00724bbdf75ac58456b43e93801fcb0a0280770dfe5ac0ea65d9169267124e258c4157aa5676641b0412870b2a25f86eb5f1fc62f29d1177e690a395ebd20a789568f8c787efac21e07e39e90cca99fff5406fd1cbcf3a1d2f8051ee7838e211c34ada8a7162050dbd255be8f09e8b75add4477a3582ac6a57c874937899d97217cf049c5436aaf803404d768ae9ea4b2c619bc98d69c01053c487004b3f1147adeb6b0ab6edc66920b1c1ccee56d8800f5550e3230d3084aea2281331bcf87f46876dcbc23126387e3a4b1b538b0942cb17e03dd9b5d823b90bafacdcbc5b94df5bc274a03a29526138251fc496019f72fa432e381df8340119de6e08e28e08a66f8379902"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"da656411ce9daebe3401f12212a189f18298defd485106c2a54e7f32a49ccf3e","proof":"bedee94d5d0fa748fafd27fadddeb3d885b4040743fcd66bc937b13c21727403e62d88f588d7a774f87e8aa0fe3cee222cd8b1c6760c8f75aba4fea82756d70632b2552f5308315e2c8cba5361d5d9ffa796f02afe70505d0fb9a051ca6efc234c918e7047fe08e9e99de6b009011affcb9e3cbb8b835bce9b612e76a3314d1d5557651bb02389f17f3f9d42dcafb70fbe570001abbeba0fb14867670d9f0c0666b82a7b5f9c32af27db6a0b153e91da79962aeda4f8f25192c09d2d07f8210fb2b640f264dff6349190522f71cba6af95d431f5004382c0fa52fd33745df208988b84ea407183acd57de1ae4e69065c38b8d835bab55e802767790fea277e3d16e827cdeab94478013389966ee55d3aa392e97158de5bf6e387c0695a7e88110c861642dbdcd7dec5ecbb4dc8c9d421398139bd8ca66abddeed6a9d73c5bd2b0abdd1640b2af18084ba43896c33b95d308486a2274e185c97f63d0f02218b2b4ea74a62ec524ef1fe0d6e73c49e168700e58c70a95ebfc400b7ad013cef5021089e941395dad2b46700d4ea083ffd07b4af23e50cfaa9d1b70e01643ca3722cde41f9f9fd7424a47b414590120eb7038e7f2694333f07de98dcb32989f98e2ec83627e4f9fd0cb0e6e72f930e7d57bafb6f076df5d78dd1e2213229f645dc3c0088cd9368d4cc7b6f889fa3f07b6f9e0140584879d65a0bd17430e20a1e6d608e92bd6e2b7601b639b2ce559a9f59cb29b2dfc00aad7e52d4eabb9538044f4dc6b13e9ad267d7331312057555f8faeb9e67572b00739779408d13c739970a5fd2539f6f46f3447971a468c5aa0cc533ec853e8de9f4ca04cbb54495358f465fea3ea7f41daf853a3f5d4e17188b713ce88e5f63db95b74c32572621c9e5c3027913cfe00c065b805fe01ba2b641a5804c113cf1bd6506022010736097c2ae0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"04b41968bf87c8f425e753df4c8d69dcf7451fab6f72c1459c3e1c668240a477","proof":"c61f50d6ae26f1a323b45e12b3c30d8c527ad099f6117fe1afc5a97ded37be05d05e9e6dee14ab2227187e1d7edfa9e869ffbd15265d688b46ec73bccfa3953414c42b142b3beb9ffb3ef96127cc51ce47143ff229f4f5f5eee08017d7d29307f8038e4c90df4ffb89cfce3e318f5124f0f071c79b0b23596ffd8b30fe8da651bacf8a55e5782db6afed3f7216368042f8bbf97d291da641cf23d646cf57280aa19e0683a6f2648b5d16230bd4f57e0285d83938a916a20c96f20aaa343e2e05f54ce018f8c3bb1afc2de95bc7dc2674ca163119b7a9f356dcca931987804c03f0c43777fb5bfcb1473b0b77bb20ac5b66a9f957651c56c90e187ceb0c436539e87b3c21871e911ab750833f2565acdc1b55612c72afacda9d51a8e786711455aa8fda6d489e251963c5bd06b32ffefd95b01165ead2190f19cafeb51ac46f3b9af0c0cab62cc2d169d0f1f573063ea8eed8e5cb48d7da2dfdd789fcdae342646ad71c09fe8d4d7de24da7b5ff3228de606dc6414705c9c94e354a7c7369e9464c654211031fb72b57d77beac18060da8dc2ac379fe94a5969a7982c2ccbb878246806fd09adbf4dbc0a5dc7c265388e512e9e8a874ea6067b8744bdb21fc53258b46d484822c4a3d6299abad6975377745400ead3369abf83e3f3758e243c1f7824375093efa4ab141b61aa13d662ac0abfd490df1a12a30a399ac4fc424721560e7570b1d96fa1fb8c56bb2933c458c90e8a5edb18a2160d45059b42482b0c66957ca27aca04fe8a63fa3f834c09ac2373955bc2ea114a83f0c5b79a6f906f28be80797e49608a40e32ce41e152627f11c59c0ec2d9f84ab1f00ff6cd3374b5fe16bf578db2dd58341c8d3f15572a60353325340be80ba2d28062afeccb90773a7ae25625224603245fd3e26a9d640beccdfff7faa9fd2663117bfc7f48805"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"427994af6accf221b770686a9875b0a2b837fa889689998553cffffa2b81ee69","proof":"a20a95267454289bb0b760631337555b1936583cd3ec4a89aab207b6b3e11a2ab89e4b165ad476a3204bdacfe5b61e232e2d4336acdae755c92b297b217fb9177ea819e27587ef7b1818ea4e8458536f8844ab9b3dcd3e7314f3cc1e14b0af404872fd83bbb30f53d5618da861699bba8e3e7d36a96aa6be743b6f0fa5aea65dfd78582bd1ad0c26aa4429a88a0be4d2adfa36e9bb0db6eb5988f0894028bd073d0853c51e99da88d5e1e5b607d996d04c4bf3013c364195813d4dd756601e01b0617b29be23565cff104a83fa83c255576693a9402975c5f08b03bbe58fcd0abad86a3a6ab56bf97a461dcc96b632bf3644ba1203e387a8006d16877796017a8282273b727bcaa7f802d6378c5a478653107c1daa5772255abd7041a233d426b0cd504b58daf8480a236465e8e5f3ac6b054579bbfd10dd7958223e25721a3e800729e1a63ddcefe540508768923c972edc384d2bbdb475cfcc391e6f497e438a65af55e82c35ea881ebe4901d8735b20ed553b6588ff60566a3e30912e072e04e8fab6f4cfe22a707353e31ee04edd5aec9739ffad18a442380df426a02e4850d181ae6bd0dcaa611d3a600acd5184af13fe6c2ff040c68ffd8f7bd59c853212505cd92c118f649e28d893ea3287a93666896ee88243e70cbf84c353a88562fe00642f13bab4decd04fbd9357125d16b339142ff2b3c4d5224a1375157742d061066eb0e003f46f522e42f8884ece1c3295be45f014f7151e289395ccb9623f2534f1fdb66fa12cc08972ca4376ad57eb8c9b9f9feb7546cdf66db4f392550ae967317ede2a77c7a24548742d588e9aeb989a46c6322888ed2cac639be861b9875a24dc6d25dba3b9bc5cd112573093bb2a819ae864ad82daa47acef8e1f0cd2cbfaf4cca6e25ded9e0bed8eefb8c075005e879c5fefb22827658a05c95e05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"10abeb2d0cc126d2d75cc2df098c3a4defa3a74d85a3d6a761484bd2a80f0149","proof":"62796f38eb99dac0d8a6bd2936804021d52985d5d56953e69103017d6a903f7c98135d73ab60cc2108b84408d1dd740e514c000f065c99165c11c7358a1a0e71ac91e2bdaea47aec4693ae979bdf56b7c264a370e9dedeeafed57c8554c97136dead59639bef1b1e1cc301e2d2c7ec5e62ec477daa33db388be30d9fa9e04b65b0b50e6a7fb0bec03ac9f3c526b3b1a69f53ae1f3cf3e2691fdd2924a2da7908985dfdd3c91044a326c5c9cc1ef4080e91c8065eff2ba83862e9d9d3a38e4e0d84f2a50f77c626f96013b3da6af95b2dae1df2a19d0d64d5a807407579e1360558b544620dee96ed51de56180e69a2aeb1e056017cab824e1ca9a2b36c19407c40ca5c5a7be5829a3a30bcade9abb1e4723a691c34f93192ac6428d7887900791028890fb672d3e21fa1da59db25db68b809b13836fdd5f7a1321bba1350cd700eb79e33e10d23cec56626f0fe0eb0f3fa1929b81f968231876debe520d477306239f00d9a8cb7ca28d71c38dede8982ed25f874d8e8a1328eb55c63162c1026b6205bd34bb67123360c00bb682664e01596e52db5a86144560f5f101091b551340c0111b936a6cd768853b69b49b9a9a99fff36f165c05dda58ad4b6e63ce01d26c9091e391c978003074ddb79bd65ba543f0cf2bb1d9ae8851f6d98f9da509bc1ca47a164ec535676afd021860402eaf635b494959f602ce9d5cf66be732259090bf3b576fc344cf557ed397a7cfab5bccd755980747c6cdcd03ad22f4c8313a5d4c0b2b07d71ea30763a5ebe97680b681f014b6bbd8a749bdb3302312df0868317d426f6c9e57cf964ead33b9a2b3005f3d3c565cba9a96b06b7334167c4f698f4467155b180ffc363f1062a06d099958f4d8b8fa8e539a3a91b43438e70128d6032fc85f504f92947cf9d54445d809974a1a0e3b284c6377db315f266e0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"16a9b3dcd7a8718cf12ab9b000c5ef0a506ca0b22e20d2a80ea29db6736d0d08","proof":"1a8802b1f2960b7476de7f2dbd414358f2f7931e03b8561761b65534fb992c5f7220af8f352228988dc5dc21b786f86c56239cc70d233a301492b16fa278a87330d7e261227073f024b6567b3a573ee5d94ae7e9a3ac7b4dc6b040f590cd3a04329f229533d14729b21387d22b55bcd74153d84641b6fbf344007c7edf259963f1839cfa2e33bd3a0e64415dd1b46909d225271a2284c0beab4368aebc5ad40299e0ad7e89f048898b1b7395dc068c2ebac9aca78ca3387b87b43d7ad142a60354645968f4a01d273f20b24d2b8b2ac751564f928fe4ca0fe129c867832f260cb0cdd5b6cedff532145bdcde755bbb96e7347b01899fe8c203bc9b59eeecff009240d85659e674468c3cfad3904a78721c84847516a78a364370bdb800e25a1f98577c4321b353002b66283730b486d4db4edd7bae4ee1a6e54c64856202560ac8dcbe159ce6caea19379367f44f2233213b1631b73cf8c0e8a3467001e3f268960451b88e4ce03e74212402cec941f7898f92e379ea6def8e0b9bab083093636e9dfd716bed7bdd6ecd79551b79852a6b181cd759764078c2155cafff527c5768716f54e76c1f732dd79b6a20b768ea4b1ee9743ab466454a90156ffa5b26135e8fa640036e58a9b9d976563afdfbc17ae15a33efe726ebc1bdca7aa4b6e54cd42ddf997e8660aaf7326468dc415b859a17f498dc3535e8aefb1c2cabd09b2dac3b9f8036b7774164856746478f0f05731d899198f5cc1cb4b90efd8eabec45504d4b1a630515513f3c0beb1d3e4784c62944d7111169a695e30c02420ed537caffd414e66148fbf579517f373747e60303b6a4679508ad4e9612adb3cdc932dc077e4fc1f7421d3b8975c450545b976bad61a91b2fcea6603fd34d5b3dea029f8143d14b451a4530623216a3200a490c31db51bdb9c996a72c97e802a1ee0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2e14c1b05681efe8c7dd1a0e53013be5e04b7d0f2234428387a221554196f60c","proof":"56d9e413b9d9e6645bbfe253c16208c75f308fa81f9965558061ebe1f47b4d59c0078baa79ebd3ab5b555e3579f28b22fd2395f18fda175be514fc9c37bc2f2ba0163d1db20aaa64c92ebd12977d8a5fdb9660a39a3e3d3c47d4ba5bf002b202b628f5f7b3c0ac8b3562942fd56676fa5bd41214cb7b4b8bf91dcf1bb3a67367f42c79d2fa1d566932d3831b16058419c4edfbe4df99dd6789a3c4557ccef506b198a2d1e8062be8382112730800c0b8c6a25a81dbf238b84e9b3194a733d40893079a171ae77fc84c395d009678f89e0c598515458497184fc74522ba9c72035aaaae4aa2db16174a9fee8ac19bc13334f87868f48690365a94181ad140ae7082e6754cc5dacdeb0e4fb7ae62b649d661cf81729d8ff4c1c485be42046b374b54a2e12165de5887ce7da5f920c6c89678aa5680cb48566669ae234966fd9d118c8d84364f7d26f61b507f324f26ae64340f5d41b2a7a7369c6536617618cc499e63f777421288b7ee36fc3729e14d0cb3170aadaa432e80d830c69a37941b70e0d44716d21d49009317446559dd1da95071d4925270f8a284671de72074d07cac8d3721404c78872d6bdea606d56a034461ff12c09708ad410626b374f3550a207fa8dd1f374702c97e38e31a8840bf77940ba24a221ac1509db50ff36ddb0942f3d759b4787337cc82c224b90eb3c2943932e37f03755994ef0e67f8b02a6350b6953bbdf537210224974d8691566b4a6938d104e7a3972eed316e21cf0e78788d36b1844c934bd48cc9ed30324474ffa9306e24832ff2544135186df89954b07d31a166d463defcbcb17a66a2c102461f694c01fb6ac54fa025a39c2e7105715340d86e8f4d0e0e5657e5c42e589899c1d83a46570219cc53e4b227887c0f0a2b2c7a8a5a9a76bed6651d9d7214bed2d40d2214e9c73a52eef7e6d5410909"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"acb9e35512d59a4da78b34b449c9d37b6db28c4746b7a0410144356ae5ad163e","proof":"c49682979f91c3e12612cea0057bdc38d366f9f371a57ba5f30893a70ba5a65452fca631e26c7e903d93ded8985a3f84b857bca862cf5b12f70f1651e916e63fcc27f2b4e3dcc0552556c7a1b10fd844b37a4ef112dc52323d1868fb6692040278865db41d59f59b9eeb84755679ae892cd409063439e2c1a9048d374faa5c2eda3286953f1ac4a0c9a04a65f8d4183022f68a5ef6ee1f02bc0c732138d5b00cf8ab1e413cdf8ef8604f003bbd539614c0484bcadbe997f3e568e23d07e9b10f917d6c0513065664ec448ae0f07f6984c009230de795e826b0b6250636821e0f92ff7c8e34af34468f3995e4bf1291f1b2e473e7cf6e15bdde9cf2412622de29385c1236459dc48e9be5652edda6ce9d6143f2d8ba3f1dc8f67857047c4a9d1b2ed1ff76e670093333a4d597d27700e34f75f2a49aa0f2f5e9cd7e05049fd43c0e2712ac4f742a17a8e4d41eeaec80473a02ec213cb8836975b4b752e42b215e80430852ceac9f0c71510dfc2004350c152b418a3959f4ae64a63bb649d7345b82d59fab3dd791822122752d6754723a69b6b0c610b41701bc91d0afbecdf12bfef3ccf5c546b67acd8fc63082a78a9645d04b1503dd017c0d3501232143610c1a43f6cac37185c2a4079c8e30cafe4b5c3cf8256d355dadef38f308b6932e457498705fe4fb05d4eabdbf80bff0fcc4bb59280f87730ee08da36986b5b3f25fbc9bd562574ae8bae3164e060bbf71a1498a5b82365bf8ff80562185f5fc9a25e6cb08d5bbaf2f2dd3fb3db03e229adf6dbba012e6f102a37906f767cc02ab00de3a04f2635d5bf2dd9b67667fc593c10c46aadefe1969f4e962e89ef463345c06263edb1208e22a9ec5a8d23519301a96ef409eb056e84dd07340549957ac035fbcd75ef4791736a2eb4decfb450c70d3f2dd0ff22908a8657efa1eb773da0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"462f4148d84fa3b33b9db21d181d60f613088426ccd72cd53eee753a3ac81b43","proof":"0a22661637b8dcb5640bf4ef1b293895c547aaae06021a24440b98b0c6faa6549e699996638ed9f548c3d3eccfb2152659c1361ce7536e8b4d2597b6364be327b451cf3c4e2bed210df869deac5b381c6d80a57e63d41bf0e04c94a915ca3c62ae83c03e282c361e8826f835e5512e516e8c847043c20b7e39b8c0a432848020529683b61123e809e337e52e771ebad9648921cfb1da9931056f02fc065e800e4c1f55ef1b060f9cf2cec2f78515d76c54c39e105c94eb8c964c6a6aac2d0e0f65716648512aecb77f4446cd140a9f83a5411b3da69b59ffec6aecac2092010de88b0485f099239fc30adb09d8c8dff782f23f92cd710f9d4dea0a5938a90e1d0c697cf3969b7df28f96d44f3f9259489d7af3274e88bd042ab6936fe3394c7b4279b993238a70a57f2e8d44789692be1d1aa3f227f54580f9161daf8f2a994556100355fc0fd2047fc8c4fc34184a4de31d5e6dd25b02a1e6651e052c32384b52ebb94efe6cab1a299cbc0bd4c2175f52687b248c784d1ff01d7122d4d8984cf4e4fcf08097e35647c36820f1031ab850949fba078b43e7e5834cb1956f3b1866946c55cfa51f4df2b4907c93862b8d9abd750cd65adecc028536e06ac5d4271002f98c6a867059f4d6a3dce2d2fa4f46d570d3a39b02c1e8fded1b1a8e490ca09a9c0db29dbc68373b2c164a638fea6e211695bf0e5df97f8045f85a12054dce5a89a001924a66d0be918253293ea0c772984a07965b5a48d43bd60b78c0627a473d518adec49e6721e9932bff0d61a0c8a40b206833d2423333ad490fb221b644a5a8c650fbdcae1c046cb9d2925253d5e17e9cc602669fb573f21cd07e64101108e6b51b066e35801072396e79512f221c93653d9a6e75b1fa4464b9060b1a4a3afd15fe3550c97fb807d217c7b3c6450805ccaf8c57c4048417178a9005"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fac10c34294fd3f2b3dc3fe80139499da39d3f3271ae20c76344130ca584b50d","proof":"f86a8778e66952982cdf8bed45b3fe370861a37459f2711005e36eef167d3b5f16fc82d04296fbc549f9dc6095f1af32808a12c3688f40bed6ed46d0f5198c54b2b6bbbe7360df212fd9f89003096a356e073f092f1d4072c5691f7d8bf10839209b887ab1ba673931880411b60ab14b6c2de850c391a95707bc141f219d490d763b22c4394dc206fb8bb3ba4ec6fb3bcbdcf54f5d9bd95fa814a83353f3080ed842111f21fa6005b6dd67e4d98c35d7b3067bf6752d667918323c2de4e22901897eb41c9b2ca252912986f3788b931853549fc9a4f28e1b956b7851e7ca11077e7ba3f5d15d908dd558637c5432ae66fba35e5fdbaa5ba9cc55abb352ac4c2aa4dd5317a8f85dd462fb5f1c0da5da3da80d7012ad4b5b67897eb09005262a6470a500c1e6a3ab3cc1d78cf44b4f8017c0a5707caf61068306a5caa18e7f9f7fec8cf3f6ea0279c9e88024adb91fb2f70714d00a598d5567e7a2cc3921d2f86190901f40b201ee98eb43cf7acf7da7e11442362b04b0c6bca5ea7cdb88e883141ad00d705c0504ca35f2885d75d5867e367aab19451d2dde5b66acc2220fc210cce268faf0e7b0cac9f8c229f832779522e72c52426ca84cef1d45c6f924456f0e068243c0a08d52be44c6e76da8249ad3099736a52786d136ee0bef0590c74a4496a42ce137f199a1a56cfce87bf48041506e75f76148d5a997231a200e835aecda9c1dd580b714015f55d7d319156a372561ace859e8325d6afd56a6832b3850eae00be5adba40fce160046ce976d688a6b7999effde671be4b0bd92d2b36ecc075a0413c6aa35efe15ca76c3365a66b80890fa432a01bd2f783d793914b64e0b17b93a9d5526ff7b26a0d399d9f97be432f34bc248027f3fc0752e301a109e09c9134275435548d23685521879375996aad6a7f7b864ccf884e524f6cec07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8077c85cc5fb2a2c14269d4db5bd52974859d8178e411844dbf25d396c61ba75","proof":"1a4ab5fae673ec4d7bcbf61d6345868b9b85de7f666361dc4866415ac3fa194a7e83d0ffcf31c0b3fae62559ca57cb99297dfe111a5998b517c84b298955756f6808bf11f74acbac662cbe24fa4cde6000492340991a5a27aec24a7dedda6c3372279ce8d8b7bc675532ea368ab19544670869023c39e5205feb1d6aa0202636ebc4581e082f7698159f6141c8daa31583d7ec2fdb0b97f1dc2b6c2440c99207174b24fbae1bf3021cfc1e34c6675938d5fa4d059e33567f69e06a2ec741a500ec33ec5d3148de78852e3ff109c5273502acd63a89ffc2279357a0f0408d8b0076f7bf963d399e290f7ebdfd841e1744309658e51adf37988ab40ea89a5f36383875e75882639c35a36123fa3a844feafa24d56d6286501eff003396d7c2b137c6f60b7d90d486eef5401420cef13cb0aa783c1c76cf26a7608361b960e42b0aa217224a3158be4e6c384b08acebe64806a8c1a8bab9eb59c1d6799003d93d250a2e2c8610a6460dc9c749ffd8c8d7bd7309199a9f6ddc940d76e556515fda23bc46c16fedcb708d4d13269b3086782fcda6782682b30baad92cfe0e7c474949f4578287dbb77506a83776212876eb634cf03e0b34637f7427e80c16cd5f5065d433180cbe6389ed89ff3b8b6f148097677bfd31824f32998933f3705223724b662e62e9e21d9d2dc30e45d12d613b147f3a218ae26a3979085ad864c373481dcc984aab16a6214227441c1a0bfb86a6281efed9ec5628d28bd5f425ed8e2b11ccd0aef62dabc51f89e6c788780d0e822707eb50664c9378063589d82d338d0fa2d9aa49a010e85567d5932f9f4bdc49b1f81c939b1312b2327e5056e2f7b222b7cdc01f224481705060a707a59ec2bff0833961ffd8387e5a92060648eb060b209409a9352e7afbe49ae8b1add4502564a1bd17f546e4142cbfa3fb77f6eb02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2e9b2a3f6a3fc8a64ff76e253cbf27931f84325b1da9ceb2f73796e78187421d","proof":"da578bcc4f816a0026b7f48622d4f692883fef9345814aa7a008ebf7da61684c4c27e9f6090f8bfb98c1e17295b49a9eed993a5f3ede90f8f16194c418734777a40f0648abb225e2635d6169ba84442fd9b1ef6cc051d040d012d990bad805198e5761e4e0b1bfff809030961391c72094e4e95667d02c5d20b58bc363f15075f74aefe00ad2d317e37ee972d4324011a122a740b3fbedc967200df2922c1d02af89d67bbf880661c554f8128727ac25cea9c25efb84f791e94005e4930259032a790584dae9fde85f49d02d201fdabfc2c79abe906b82d09825f31abeb969078e30eefa573b8b567c0afbae363ea85683bf539a1565a3167d7a6d764e87857cbe8757df03822df9b2266677b02e06a6e99c3f137fd59b465fff7a7a6fc869298475f42df077d4c8e2ac793df6fd5ce74729fbf60467d07c3f6b3598e740262bfa36dad97d38a64ab671fdc72f0717613e6d6c2e09b1d8d3465d14e6fa2f902b2804ec19ea31e6de47c333858727d4591f9c0ee9fdc10e78fbe42a27665b272ba870ecbb019be9d5ddbb5f840d5a880c77746115c0ff9b2e93465c138fc9bb5710f34687cb943f3a9f4175e030e6f1342f37e22a0160d37f2bd070816dae9234fec2eaa4e6ac212fe33fd2ea59297eeb031b12f863c9194b08195a0a570ef764d632a0253d4b238637f42f1955413945f82968a19f871864449081ccfd71b51890ffa22b902ee701e64b5b9dac7e05942212402eb208339d6d4d8e3c11e43a3e72173c5882b96bcfac618e9496383209acf8663ae7e27f392f8020c38edee0656ae078ae0524cc2775efe9c32f25a819352767f51ec7a671b11281b0ed9dc04b50f0a95bdd58c8a4bae638d23bc39b999543e25944a5abe21fb796cdafb7ad0e45690f045e5d154523ce5df30f8c8839889b6ad1e53440bb9484742cb563820a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7cc99eb310d2255b860cf2881643a584edc39b4ec858e3af94634072ab99cd15","proof":"0e67a739016b80c73987e8d9b636fe2ddb5481ada2e99f93e58f42e13fd25324deddb6a844de06fc6b9ef8a1eff2a43b4e93059c0f0c875a8c39312a7d05520122fa5b91b78d565771e7dea65799014eef772f20ced857e83daabebe3347f3185e82ca76bdf00825bc9f9daf32ca2a6847f4950527d4309e5e85b290a9e07c1adca657b7ad680e3cfbd1ac5b3f9bc5dea1f9fcf0754cd3a32c43d508e256900865ad3e997884a43e2909ed3d6b1bedf7846f89e8255692f2e1f0c87a00534b07762585e92222f2a7b7a868ce9ba5a46ba82b4f4d2cc5d3329936593437353709d80011070ed621e14f6c9198810872b0fc7b9ce61a6402f40b280b0aed100f3ed669c4fd91813d86ed3f56290539cda0aed3a1e07e17f4e83ac6435edd8c3528a4128df06e11444cafb68abbb2501b2c5c6d2342040db854ca5b37726072d759d476e9f13d47f3ab7ceaf4379de7581f4d3f66eb288810130c454a66245c463f96cc947e7931c1e396588a9a24f91cce866dabf82b4dad33d384ce6b82d91d487eb0e105ac371b458eba075de2cac8c8dcbc1bfc71251ec426155012efaba25e40c805596371d94a69d705597789a024f7418de50dc50a0c7879336c907b284c48b2b656b5db05a07cf1112f530dc3604425e6ebec72d828e37c8279dd4d8e12540707db2b031f34e6c48d408f79b27944de0d4814b9d6b568900e62a3dcfd0accd5cb7bded4a90b1d26ada59db678638d0b51df076e8c87d9bc0d7c0714eb4722d4dee7796d70af04d3878ae9b3d93906f4643526e627b0014875e596184c3c1648099749a1facbef58602aa35dc89293949aaa1d5c1ff4e7b820f9c2df2e4067b20750c0ce5b16547b89f5a6bc52b155c98cfe816c44d890e5aa7b07d11600e085dd2c5774c9cf6d96e3be04250ad6d65838bccc00512fea7c6d03e6f25101"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"226fb105c00f5dc4fa3aa47bae6fd814f10f81e08603f9d34537d5017df1ce38","proof":"221004e8b412bc0d38e64446876a6ea3600dca23b0cddac0239ccb3bbf9eb65628814f05320f5374b7927f86f0a326e93ff260b4df00218e9669547daf12b2751a9e095d022279b8eef045edd632b1640ab1e4cdf3daceda0a39ec8bbd4f9223b2e68d6d22d5bc5e8933a486b14f0f72444af35694b237397a9321e26a85a50751edf643fecc236d2e0f6a2d53a3513b958acc617e9c1f512162b614bc797e02b769f6374576fa5faba5bc84c8381b603661c8f0d919157eb85fd9e2a69713026abba1db45d176fe65424f061b42a79067d523aea9895217ca786499dcdefa068451b8e0ac1fa45ef27f4d284a2ebb7cdce057ba32c23b29c8fe78acb4dff2416ad2f3ae9d25cab6e0f76dc4c31cece648943cba4da602526a4fc3ed347b1d2adc8a46e25c9fb370499dfa6b96eb81668ea990b54952a5e215f58286f745c84594f06eed5513cef1c292912113b716fc4e4e2b00d181af6d104daf6d9670a7158809433bd9d4b6705f5200bc342cde813021854d773bd00b1e62c7acafd59531a47f2b79d72467999d0f2486a9f61f9086bfc656eaeaecf88e08c7b9bf75c1356098643ded8f2eadc4baa22559c2936451abbdcb775e5cec3927f13444ba08129e8ebd8d57aa6643f5a555dd5488f0a62cf736e702951a96076b747d97086073980de7c882ef8c362e11ee22dd58073834d11a6a6cd035589fb4b57b635c4d3b7889b465ad6564ae3c543671185aa1c1ca2e80dea6ae3f28d258e2083ad3d65bb476b96128a113c43267074714d1cfc9d187cb4f20c79f0a2ea89091a851413180d0106f03570889dba55d03d5a65687e3efb9fb5546185d5b11a37f939d883045db4c7b7df0586454cd509e06dbd657c5843fa7a04d7f0b052e59fde685a9021a3cae2614fbb0825bc232aaa4081ea46c9e3ee80964c42eadcf6007e587aa0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1007f02663f257bbf275dc6fe4be7fbd1a6887c63ffdb51b5d80d5d5ac04f41a","proof":"30658f0c6b2cfe1819de0f6460e92d936ca79a31a1e38efeffe4611e90bb3d130c6779a084af37d2fe41c5a38fcab065bfef5722596ebc034843f72fb88f7908aebe0b803f4d77e068a9e48b0c204f0b97862b8a14eaa227c2716f346360f90b86bfb3a54cb7f2b7ddcec37661bb0e8092664ad0b0df16eb14b15ffc8a5d270aa84109d0f90c769c199bd7835386a95d50d25b19e15f849de0a9a9c2fc2048018a7f74250678c6d5aa8041a2f4df4ec21aa657604d46ea72c4764ba8af98300b5d6eb156cd278d56f102c2e6e936a7a79563143b55ba72b301ebc6e9fecdc30452bd371bb696bfad94630c6baf807975acdbc9f8edf3f89b38181b79c68b683cfed03b31350bba3879cf5fbf77601b962e6fb28884a8b5b869ce65b9f3f50d5caa7b56d90ee6c7cbe5aeab965d931472f001f7c48f5138bfb783dd8c63ce52766068974b04f4910eaa5c116461a87b9ce4121d4d2cf385576818d83849a58a281c29ce2fa0145ec0a62f36750a9f1e6599846a2a97b82807873cae5175741145ecaa2887f4fa1ad0b96d46f98682fd1053bc7c1d5a0769bbbda90e098a3a8c69b2d3d9a0f5389e6ee24bbbade8ead07651e86637669a2bcdd1a51f68f452984ca4f7ae7420fe6bf5effa8244649e76c7246ab2ce76bb4c84f21ca3a7d079b950980c13eafe343cf1d254942f5b71b398c9fac5376f4abf640e588226f7158a183872462cb9ab9f198b11bb4a2a2bbd58381b998e5ffccbaacbb45bf7344c414eeabae0fb1f93f640bc3dc91d5650149bbe79b3491073dc24a74f1483cba9324aaee659ceef254535e8d466e9da2a21b5be2845b723a64f38b0b3ea676f715751efa52382a14865fce82ac0a1bcea9a68ddb109991f5abf0e3d4605a42b5210047a4414f412be20ddd442af62ed3d81b26d4f493d286fd3f862c82387c1079f06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c6530a0e5ddb5a62202955947b8c47713f9621501de8aa5e3c0316c5aad3303b","proof":"a24e8b5d2f7f8e866cd76c65be0118bb75a8101059af654caf4fcc324fc0d9427af84dcbef6b01295c8f76a3465d5f44ea2e3a53adb63212efb9aca6c892971a98112914339bf8706f530f326d30d9372aaf1065a936ff3e37a8730324d9216996c81c065e7c971053c1874ff801a5beb5e08f5bc1f2e317b2f80eaa713a0f316ce85bf57348f402680b7c0e171bd08efec1f0597f598b295c0cdb27e1690f0f05f1d29b69a92df083eb7eec9e875bf82e9446e99975b46988611f46af8a640a38a466b3780e8066343de086bf60b19719d0dfbda694d6d8306aacde54f68200e041650da780fb78fa43546eb584f5ddd77c1412e6e749192af5dc1fe7cff90f5646bc155954e16e7f4f311b514b627799136796a45389dc2b44b2959cb64e7fccd40abec761cfa6c64d593985846654ce7fdda01c66023551a8271892a0f54f4e1444f439837b9a5e5d4c9dc13e3e3e20d50ea4aef7eb20f236b5cc375de754fab93c816b6f34911011c48df9c736a4a7e27013049392dda28b9f65a46daa1cb013ca6bb33b2f87732ebc1f3c82eb09704475fe73e94b7c536893c58e4bfb0c76cbe03295e6290712604ffa6c3292f1918fbcc4bf5a62aebaae9d3233a6377bc468cfc7638486e1756696b3c22776bad180f5ca709e2c47e1b1a8b3e276837a307ff3cdb98630d861615d7bbc2adf4e7d6aa51302f1caad21f2bfd44df7031a78d6e8a831848e5c7a28df1a82deb7f3695b8bfac6faab8f7ab17b48867a185a6a14bb2a5a538d7fd0200a59b20b9c6160171717f4cf04f26dd7f4f9e5ac537aeca730a66a0407dcdab98196ff86626c2c679cc92b9555aefa580ec26b8119121e5b293bff3e89263aad11bf551d1eef7671e2231e1c6161840493cbac139c0fd7b85d711571b9933ffd4fc50c8f02ca136b58fe27b679d6efa085a83c63d40d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"72198898931ec01d290d21a4db0ecadc9ffb553af9d3ad2239538d5e58799111","proof":"6a0e6e9ced8f186e1174920639d4385efa6000da44c250cc2dde226f886910438ad572b48ac0ed45e077395b80c60372789f0b7be9945a195d3da662395eef7b487d9986f2db4f4f6c49ec711b2f3ec2332b6f8ce7a765b147dddfcd40d01c4c2a48daf91787c93f12da0a55acc767c6b8d71cc31185689086610a79f539b606938a3a220871adc9e0a825106f3693f65de33ef529532d7d137226c7cb868e0de0613ef3ac22db7ca37e8d0ac2e6043ee8d8a21f1ca214d5e164bcdc149b350f36b0a1a274b2350a3ab50c4742a20df92a9b0219a66f50548ba7280bd812e10f7419510edd32c44b6fbd9bf31d1b8d5c935c3781351c9ab435e155a9a599cb619853de5fbc9d79de69c9a5a0ea80b7f4c8228026355bda6707b7192befec48484660d627037f5b4366337f3ba6b76614c22bf8515a103dd33ad99d46e6b4561ab44287075f0869ed666b29976222c59a2277621248210f3256031d857d046b1bc299ce4c6fe3b2888a9e99028e346d6f8f49a27e927a6c9e742df30af7b5a7458a1aecac6826ef61b17f4e9037369b6e882a0b4db7776e1ef9c86401c5e45a00445e3b5e8d2b9dd4d723e4f8ab3269fc427a795e9bbb9990404f0f482e178d55248663a3a642d4e1b487b20602a57bf428f40bfad46135bbf7edd5aeddbeb775acc629bfdb8c9940d728ac39695612427a3057c492cf5979deb225b75795f17816a18c949bdd9e8b0657093a72b9bde969ef2f08f6e39ac25b4f83a5961c310c8e7e72433eb22eb2e112ba1d3dd75479e22c4f6b5b3283c20a09de6c53ae51584230241d120b77d552597815f1455a37902b394088049e58d61878012b286700c56b25a606d89284e0284d9193b21dc9c1e730c00c3598511e566169a2df3d035f0f9875982cdc35a3169b9cf85f40c078b340197cc11a7ad238ed428b060b0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2eb9c48078536484ee112757d3da09ca668b147b21ed79373b0c893e89a2453b","proof":"48cdc8b9469c7f98bb296b5c2e1275250adff9668ec1c03d4fdfd4b86468791106e09f7b190ec5cf319856726b0bb02e64a983eb3dda5e7ef2fbc5c871c0834f84ec5f2d29dda2e0666c03c445446b276ecf78332e0e7c6de3113a808497bd79640fcee2305a9f0f1c4e87d8a074eee7dce92cad884061e8d025a5274029b90295864ffdaeb9cf77680d0b147fb0429b9313d0916a507a4d6541041f6183bd041a3670177c0eb0164e3ebed86ecb22b0dca3ff6cdada7d1a679bc9023e7fb4056e6ace22dc5d7134138888fce992666786e329a4473e5f90c48f9c2ff39fc309b250293b3742fdff20bb876320102853cb9d8b5581e325c3fe991c20224dcf7452eff1698465596936e0952e043f2412c6b85fe19c88d0cfe5dbe2cb21c4ae645a8946485bbf964ba58beba2e2efd7e4b0b6ab0de2164baa36069ab38e3b2902e03e98224c8d5130b1cb73226341b83eaf476a68ae001967f74f29bbdf56f517e2bfe4941743964e1d9c1175ee090b763d1e818cc4b09fd3fb6ebefff2b8bd35cc72723ad03b782b857ca278d4c43c2a1e56b682e7f813fb476820edefd55e0356af180b005197e31251b3623330fb8a0ff1c83b69ab792ebfcd3544914f263ad8a4894dcf4f62359529b627822ef0a97536b07753560067a03929571401bb487aa46db87b51fe777a8540ee1fe2871b8beb958ece1c38be3924a671cd4a73607ef8625a300952fc43ff0e4b302a35d1ef073414cef5e27f0ab1333c1abe4d1a521da09c5690fd3da491927b60cd1325c33cd4e5ed8131b3348c4b3716543953100155cbb4cb973541ef03597feea6d8d7d7a29c0885eca218191f93d39b41352d5bc7a7f9d248fa829a973379e22a0263e4fc8cd1ec505ff3cfc4e256b7130a4bcfb30035bcb2391d699a685ea6373f497f7132ec5c62d5f9fdcbf0cd755801"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"26d27a2d4deabeb32f6a695b46bedcedeae82e25c63e15baeafc0bee9574e512","proof":"689d2ba21a8d2ba1b33a3cafcc8cb0de7e8c9b703ab0805e66b1355bee66ab503cd60caa9a469ea7d8db18b7bbd8017d3ec48ec9aea179a7cd1dce12240f4603cecdbdff2e50d5549f4a6dfbfedbcefd550ce7b4a4d0533abbc3d8eb4ebfb50ec0b81408b64a906035271c678292f630c6362fb80ca261f6a010da1ea9b16b131709bbec40ae3b5a6bc2c84aee646bbba14c986c937870d1bb6e6e235f9f45018596f3c06fb73ce4610c8033bc520c5711ad57b2f14ea056532ccc292325050cdb71fb017541938c757147057c0853bf306db036b117088a3826cf95cdd2260c8e01ee52f143b420ecfea6adcd0df39ca8a347108708b37a05981d3ee613735ff80e7c06b39d78186f552951be1c507d296237b30e064908bdbb26d25aacd129ba6240d6563a49bc71cea647a3b0ca24a2f5e6ee6879390373af1aebeba60b4e8a7d0cd1ed2130f7221dd8ed0a94d95cb4bcbe3b00943d9c95917e70394b6659181497a30f0f6ea3c6357c3e046c13164fe9cf59cec35d30801ae881b29e0214def825ccaa5a979a7140dae0ec6a1f68c85f0b66014ca7524efcf64316c824341a92839d0a458dc6048ad9b9584bea77f6c5d5be0bfddc7fd7476c12ce03ef5376ec38119640c95db4aa766a5db91e74a63100044f4f70ecd7678263667b5241b627175d5fe4ac8e4d939ee4efd44909c00d793829d5b330f1b02cc67232bd3c1cf73c703c5a275fa0dbc07575eb01ab92f0bc0cf14bf5ffc45223f52d5ba537c0b04df42bc02f4f394614c137539fe3d6ea74c0f352a090e1b3291bbcc5b55ab4382e90f9e17f36a34d53cb26c2b9011048b6cbad9f83adab5476b13d8ddd7789712778c004e7ab5d505cd421539df57f768562fb0a3953b1ee96ac7b704e0fdd0147b00e3a29cf5b3620b014ac560814feb42b07cdec8a7f7199a8ba72420d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"80e0685693a0f811ae40e3275a805b7b1f1e5d74653498488f4fea85b9329e70","proof":"d2811f80b40ae2b98ffc286850f88a97a70dcf41e778a98bef9979a68af81d270c79ccdab4b3838323311607e069205754e920b2a07396bb0ace86d528d668130037ff9d3a587458446cfdbbef63506b4e416310488b6d5ec9275d6e95a8325602437c5b568c55b1260f0b5a4dc770cc0e89a5fc6a754a63d7786a3ac248ed1328c8c29db1c334e39deb0f8f4ed573475af51479880cab02f9e7c30470995405dc1d37e11db5bce538f31e02c088e6eb0b558ffef52e2f89771cb79aa6bc780335231ed0ee77223707d4c9e436c12d3096439c355847fbaa2406256ccd552e04aacccb89b5f9012b2e7814ef45539b48eb81c522580a82f4b03640ab7f812d089018a80b57457521d482f52d9b45a57a3bcfa2451d96c7835eff3dc6be49cd4fe623670236824a5f3979e5fe2ced630b67ec560c6b47cdc6d674472c798c462046635238687cc9ac14784e10349d83f6d4710104cdfcd915aa21cbbf11fb78024e118867e51355063b1ae26d59c88e775f5d7e9d2ee8ce3a7ad87807760777309c09a61a4b3dbf3226ca37bd292e2c07296dfda5bb3bec1f5a21cd3b4934ed44e0debe9c70c52200c5ab02768d6ac47dbf55cf19af324330d74a39138a7f0e7d74fd04498978e7bb951830219d7a09d8f4e1fe69f66b2d72ce102e07df262d41bee34b28a7610c41ff2d813bd06d5180165a09d26a744684a378151f859433673c00082e9fe8ee0e501ed32819c037ae782977042029c73c2f78ee0a735e554a9ac6367ecfe96628dab3e0d365c8fa0ad5776826037ae4653518092d2e410e41a85caf39e821d75dd3fad32581c55f415de3fcd7627eb6a6c7df11e82f8853076551f0a04de5d9f8a853edbdfb86b5aeccb6488e3792ca01b33e21283dbc8b038877430f676c9ba60e1876bf4062bc108dd3c3b9aebc73f15229802e9cb8810d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8c0210665527b34ab650d8466e1bdde0fc7818017b34202367fdb57a0965af78","proof":"540433b037ac0c3d75fe01308a8c34677a9a0b81411d600e6fb013423f0222158ad4860decb9268e372b01ee3147a285699f2aae68e3f967b5fedda885249925fe2b32cbc8e9d3e67d54444fae7945b81d6a27afc4aff97da64064e43b18b272aa7296bdfaa80765ff36ee9d94579c52c28ff0b6ba7fb0c3f5be353d86f6507bb29b7f4f7d803213bf55b8d5af77e6435672d5a9b420a291fa2d468958b4260ffad7c1f0cfc702c9938fb12bac2f751793e273095572d6e2ddbba632f717b30cf1a15e7b219301bc472bf3c4bb048f18928019a3d67882b4f12dda8b464cf10880a28b4c41feb9341e8b16ab5e1bbc95f79c295659fb2c5d92c4ae4b51262e48cc470c6f7a267d49f0b082fc83109ab9e3eda23a2d1256e20ca29a33c21d190ef6b78e9988f2b0285dd49513316cc367adcf5c0fd76c222c4f28b58b990e00417c25fa526bbd70b541993ed7ebf04c4cdf82653d897495e02ac6e5462b442538b685482616e81a816e56d85d59fb946e361f71c4cee4e85c85cb07ebaeba4b15cc1c994bd8e35f7620fa8d46b4a9fc79b690d5e39c15e28a9c88f855a4bef2359004136d122ee5a5eee593ccadb8af8c50100350f5b595ab8c6662d66bcc9537b49cbedebea70b79f7b3c43f41b1096c10d003ba0a00886678fab0e0c9238f3a90723a1da6f1d2277cc143cd470cb006ee40a4986e369aa48d6e72d71b093d49ae21a43618a0c59e45182b68563788b0ed70b50a4ad4371f37a42a04b2512b3de6dcd1dcbc8036fca026212d6b340c47e328b266c944cd2e38ad970d94544a7fc848400f7545f5f8ad6d5c66712b629fbbee8470d57dd58d251138b4c3ff91013d0354ec2e8eb08dd0c9067567804c063535af22f21fd432321c6296038877097c9f2e991e1b0950b9a7365ebc84fe9299d6eca62fdcd2a83eede658067ef70c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0c2583c55db6ca829341a070b14f37c1b4b8375e65c0ae6e5e82add3888a7a3b","proof":"7c552faa5865bad45b41991c64401dd2e7f9194e834f996f13c0f64665921d78b4e51f2b0df94275987e5491b4589a381035eed09a5f4979ae527ea016d3496866727c7410ee11bc87cc474d05514a11fe1a13a07dbb865c672d4df652440a15220ec956874a5bd9370cc590c8d22f491c5261002b41461e8e2f536fd2e89c30b91edabd9fc75c189192e1965f905c25832fd89cd8b4ee04df76fa3cf60dd7064633b2f918ab1d652cb3f894ad9dc27593906f32d5e2b1154cc96d202f95710eb7c78009a1b90c7057e39b175a4949a6132d8331593f645647f11ea9ff2dc5034c0ae5d1aa1c627b5d64c0f328040f0c89c20db92fcc469e82280f5249e9ab6ef0922a743290c5b9f2a9935fe1bb8f436663ab80a21199c3bf1368247e71aa09aafdf2a2c7827a9639c08362bff60e7a6dfc64b30f05e93e1fae7152893e887a0ec0421a0c0fbafa0214041df2548b79c3833bce12349bd6ed96b8456e95e80fdc2ce15d5d118d5125f05a96a7a5af11ed750df67a8713a47c83da236eb0fe1dcebeb4a13594d13c8dbfe330def2668e2ab02948cfdeb93fbe91eb91f89a3c6608170ea6008b08b88b74d857454aa401847b1856af48a68c346dec0a246a66102e488f6d2ad5780a5ca4910fc56477a6eea380bd64605644613e19eb84a83311d6cf14b9d2e4ab4f149e5d16ad31027a0f16744e78684ac9e5f0d6a71edfed41e4de77e6e0761946ab347569f3504f8a9b613f875b442ed117a21fa02d44042fc6b3cee9805059aa9130d45bc3b801345b0ff81994ca6d04f7f947ec0a7a3024343e7d6fcdc643ab342a4d11754360accbb459c1dd165237358d0960df53552de769edab8736b01e1c59a2eb9306f93feaf277aaf2cd807fd6cb21ae431f1a04094ecd1ce19e79954166e5a1b6de9b02e8e5b129d9a21b8e34c6f014022c6107"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"944d6ae00dd2761f49810df844ced7d7b6337aa1a6b58daea193d02594546e3f","proof":"7052f59ef35269bb640f3829f18a7c165c1f1d035f68bbf84d0dc62c2730107b727e3bd7deb570bfe111d225343128d2946640672204023e0e78a94002c355045006506b0171022fbdc6121f793618664e9142819d6fec7842619014c016716a268cfcedb025b8dbdcc090b65e68aeb07e42375307913303893065fd006bf93adad61917cbf24828aa31caae95e71ff7545abbf070b0e282fa8cb92b131a3f0ab6b28e80f2e253a6a6d98463c1795eb663e4556dbc344540b0f2d4d4f7cad203034ef5227f059f48ddc797a891034709edc31f5ea0477eac314d7cde2948b20a5416bac9a2ad4a72444da71536acfbc29d4d8a05a6c893f83d3bfc979368e15298a5330f904a3666e633c52a59b464ab05a29373a41264f55f9c718de321f178aca44b1df390a4037af54d1c3e29396032014d3a1bdddbb4ba37579a30f73b0f30ea049dde55f338f10c310e665458e781cbe89ac4728e64adbc06287b102177a85d4d0691da93e04ce189120932ed43d77fee75dd431493c0a06e068267d75dee73b7d92fadf36934993a6964beced88a5371665ffb2e87a810ad26896ee35010a9dc2d9ebc310635827e194712d0e3b8cb1cff20339600d315554fd5b9231acabed85e7d801138c49250d7b1011d4d11d890faa5a749fc9c187669e1bad8518e20f45bb6a9ad8a105a829820232a5345bd6b1aab8aa13f375759825b664c5af857b9ac38985d172897ab51aa024ae4e841336b9d2d441457359d2e7b24057c9c1ae2eb6400cdaef36ddf9118270adfc9bf89cecc28767d1e983ef08eae1646c0023a4b0d8636bfa925267f1b841221bc8ee332065ba59fb183dd07853e0d7956f6c39e03f29673f4024464f807e89006b8a2bf3bd16a76ce3dfd44291fc20a7d7dfdf97ebcbb65960c30354b5b0a114e168b4c8cb310abd4e4360b5e95d60b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f83252ac4080892bbb6b129d9408127a7b2b5574f4efd5f0aaa0fb102f8e7772","proof":"ca2ee673e6b13750ccbceb6089c7c1a42126266dfbef30f364a6fe10b13b4b2e2423c2cecb12c28fb84b2c5b485a2cd169156c9325488745b97a00a1249d2259e855fb9195cf183bb686c0d33e7d7bbf7b0578c18f98e0d68e21c22b3dfeeb18627ed0fe216f11818656c3ae1eb3094192d76e09633f39dd55e6f382dc4638045436e25fab1c53b6fbf273ead9c909841340b2128d0529159291ce6d91a79e0ec20cf95e03c6bc8ad6d871ea1282d1785cf2d970f2cede7a969bf19d1e885406046c9279e744b24e45f9a093b5800872202853c32ae15ded4160fd4a491ebb00c401989a654c0bc90a1be08c017becc3c933fe934d24bd91ad40fd4fcf86376cb601526d35800778cb4ce96e6c370fd14a5dc7c364aa8fe25e589b7c09877c00babb459f9f0e7c0fa9c93b5028abcff7edfb8780479737cc99237a1eabd6975b505b07a5f6625b64d1736305c80df33f880d177d5139e7d93f069caa49f50b5254508ad187d661ddfddae5c9d3324289232036e453684d9c396b7e9b97617506800d3ca885d95415a0317ea948c1096944086356a33ea747d950e4ea474fbd7c1ce6539700d06258e8b0ef237bd048d253d2b75049f1c821ea909d5d17679e7768c917ee8c61fda9f30c351d3c1c38c30269d89431a1baee24133d9e9082b319bc24f149103f8275445fb34ccedccf5d0189929b5c5d531e786705a993078d0016002630da8c951468154bec33179b39901181ae4d2c33a2ec98e1240f30b82d967a1e4fc9896b95e1514a38f01e69a2b3434b0ce84ce8c729e9c6448161f604e214af28c3d97ca1df92e962d173b43a3b0b49faaa8130b2f658a155f96728463b1713bdbe822043eb8aa93dd2dcc4a6bb480ef1fe23080ff57eeb124a8fb80a5c37ca07fa609b7c5ef3593c6f1a81c8ed7badb55f69ca9ea55e927a2abf6a06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9e8e018ad7902ca74692c0279180ac1745dcecd985933bb749c09d154d65490d","proof":"421be1b1c8fe6e36a724505ae7ef36fa5253e9cb7d732725582c458ee299ea6f6435407688dcfc6b579017626abe8b10e49a854e912b4afbfa44d1539b78df6d86baf7ce64e6d80f985eaf68d81976b35c854628a01fdeaf81e8d5a7bf57983ba4d867ba66f3a8f9cb91f238ddd5a0c1f1587b3f29e48564ccf0dde2d87a0100e7530b1e87d91609904696c59ad7f448ec57e8799a55657d6639c15a1b8d5407cab66a48161d82b69059753be0665bcf4aba69d8341dce115f1dcc56c2696207dd5dba472ac500e5887ce5b98c9351f08b1fca5380a766c10731fbd2e13d91043240404ba4c3705dcf0bee6b7c80e01233ac7047b9ed12b93a0556efde9c1032c05b2ed505761e0f390010df75192a1f4bf50e1cf02c5306cc15f4a3cb4e9b6100c3abb55078cb21bb37155054272d81dcc99b95bab507822a722e47d3362c09d4fb441407ca5557553e4a8efe95697b61452229648e533fb6cc0f52b6aa69528288242c3b1ea985c8f79ffc534582ab3d8569f4494b2119a2686d5cfe09e968126bcd28cb93b5c749737792ee2c698b23181190a99d47cc2966d22f241a8d35bc0c702de5ae067c912a864aef0c0262a82176587b6a09b1b10d73915ae55c779afda62dfc684f1ae7b854a00da4266936764a2a02f687b2fe3c9b58a1d7710e78928c9b5d014543b2d54dc73bce65f3dcdfe546dcfa614fa9f0253c5a40e8449ac737ada72bf915cd931e4f1a1d598c46a02b657a1635248ba51a94a9d21068cebd666361c7d3944dc6aa9d169400a9b6c934af98d0962ea5d22389c2d3ea040e97e608e9e2aae9aeeaffc1dff64309598b0db05ea004b4e2fcc11008ad2a48c2a81996ef93fb4d59826557c0a877921e68b879b926b25b3f5e22ecf05dac09f3f9dc37d2d4c81a77d54a553f962f5508f54cd9acd3f10519e0942f35ac8e02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6c0b34ecfe3030fce30853a7f0b37133f94618e442413c3f645b9b4113b33e31","proof":"466029d260c9904b664b0f67ec1cfce931ee1c891218b457ea3e4f5385e20a595ad750ebfd05b982d0337febc15969a647d84dc6bdcd09790dd7c4f7486b121460e302a65cd9f5900dc8e1d38686d3ec048a02d51f63df9252d06455de49583d9ec2abd5888f4eff78cdc922b4d60ae528ec999be6c2286b51e845095d66fa03984e1947b1027900a0a156320411a9bc579fa50c71dbb07e9a2ca82c97929301e152237b4cb86286dc38adf0f6c0fa46b435548a4992d9f1850615f36fcd5d050fd0fb8404a597daa9669f79663a8ab2f2ec0938b3e89d53a3cbc6a3ba4bef0404841ad793b80481c88068e07646f661a454595d019c974b3d638ab4c1531c7c5e9780ad92fbe480492378e6e8269ef769329a3f444cc2a6b51126fd23dc020a44d17616e319c9127858a22bd66b58588a6da4b641783cb1d8dd3276402c5a3e82c52b5668358bc2043956f9749ff767fce20c11b09d09764c671a832d095b7bb26d41593e6ddd832149a9204519cf5fe83d3cc60563e8119b3e26f54e2ead73ec9a52968d7c1f4f3594ca959f7cffd340b2ea5e937c41c644b7f770caf2a577ac122437fd9f012785949f9d43d8a5edc0ee66a0df06bd7f5fc86ecbd697f4051a45edab487afd60bb94659027806b4e6a09230e480a384a626dc2be85245160423915587d23e9536182105645667a351afa61a5378821cfd73062ef1a982c33b015a48f1cb31605ef22f201907de0cb3eec5a1bd78b3010d3e7af4affff500fd8c525cf904cf4335b06859a2bd9cb2d8e6545be6e39545691e4b2e4fe47ad5a5a7873468ef621aac186169080aa1443103f2d289133f817040bcc011dd1432d310250dd3726f980e26978e441dc8dd1750d926a731b62aedf50b1c7ba693d0cc2f3df05bc954142176c516e0881384baa25b0d576850e00af6aff2717a1ce05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dc29f53a117b6975910db6b042500fd9edb150abf842a87e1950b30937c13e12","proof":"a8cc26baa02d9b28dd580886cd9913d14da0d8efed76e0a3cb99b6e13c8e1428061f2e53783d34d4c3a957a60adbe151b7e64c5b4b79d21094d772ca55c51576d4852e3a1c30609740896be8e47eb739b027c27e4f65d0ce3e425e716bd5431672b67e35b07244e20f76de0f65ee2f867e0669b5c08ae4b887927fde67687e33aed3f8d6ac03b7abf2a1a7e7572f495ae7ecc8c85f1bb70a8141f585c2154a0ab3e28b69dd53b27f9ce4b44445109a939abcf657d3094565c6ca701a4cfd1f0620e71948663e357bb84a0ab883d6e89f1167a0e5261ca7c7f4679cbbde7fee0aeefc26c96bbb072ea817ed3546237c93cb8e45360d9881190962edcb000ae5243cc361dbb8b54f2678db9b03b2da5ac61a96d17972f89cd60d8aa02e7646b820be95a891265de7cbdc9aa40fd5136588834869ef0fffe084f9eb07235feb3c42084a7574852d701894ba0b98adbfc2b06e145d45875111caf323b00bc9023c3ccc8b1077abef0b6920541fdd21ba64ce9a8c8eee26451ed911af059af8de7b75968c7a6595b36eb81d6dbd44e6f54578d9eef2117a701a4d4512819e5577b05624a449df245d84c5083e0e675fe740d9e145ff0b10d2ee67a7b595dc70339a394e07a0965017f2750fd0c918d0a47faf1c81afc718b4cfc94a3a2ca03e799c71a04102ecfbef6bc7d5ffc4e1c6a8bc3903f3c4f65141e43e248cc0ba1432cd273e680d2eba541b4418a48d4bd0010e212e30654779c3bad829bc7fbcc8a52a1538902aa17a1254c04d5b9692c95347d966d28067c83f30be2d0a04fe4e30e45696c044939a14b896667cb1eff0b79814ab90d6b3d77ec2eda902f08528aed31814fab435a8973f5d8164e4d3547e76712f40350938d508136ce5b02668f42a01011c76e77ef0dc49daa9bb4437aeb452716267d848f646b1c5786d7a7da6c107"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1840e87aef8993c6d82747c1b5b0ce17dc5fa4f567130b0e46eb3c8f7b74674a","proof":"0820293ec4a9e249e50f1c41292ce9b8bf4fec89f7f35ae8035120cdc8e7c061eafe26d0ad8bb6c11ae5ff7caa564a77615f9acac46bea5b1e4713144658407362572c3c00b2454c5dc9c302d639c4259d0d0cf4c8928d76206462d1033ef74a7ebc6e23206eb847d7fc5773a14f6a21c6d59a45c4330665d17b0f809ebda87c52a47d8a511f8e5efd4d04a3c9a67321166c51a371618e365c143467d6fc390445ea76b4ec77885e2e1a032f0a4b5e036ffc53f57fbc65c54eb9200b6603a001b2058246e38071bbb02c1644464741e47a681a22daa9fb5430a859e3a06d670aa0b7717a6e5e4f8be0da093249236cbe74ca288293cb089e13538e5135db457a28a61b79f97e6819f61cced3d96aaad79f73be79d31437b5cdcc6baee994a51a887e65985af7139efc99aadea6bf72cf7dd42af35c5cddc8b2908e9bff3afc6b6cb001b3f599980f7ebbdda549295828ac2b41f4784f648f5a1ab5df95e52905a610d67a827084136f650cf13c6cc83cb6e36105c2637118e95146d00fade935444170a4105af370b21fcfe08245499b2e8065e48833009dd39be27583f4097e16a867186c6cd34cc2a8d03b799e86a06bc9cf39b45b7779d34aed0cad81c7291aa5536987ac4fed8557d704973a4cc44509af25167b69d777324718f5e30a5ed08528433656dcad8e88fb62c28f467033690c5440406d8ab0e2a19ff9cd0666847aca5baf535cff2c8071b207594139f328a0b922487fb89ae69de981c88728ba404b158a8161fc1a214327ff627ebae1e6336cf8e7d440e3383b67a893a04e6c55509fb34d01ac7557b572b2ae89fc8b5e2afa52fa917373e3cc91acdfb476936a5082e30fe5f2d6953bd8a23e7f2cd5e331b42875a3adba302510c62e2803679d6a6f88e565e9f462ac5ab2b6384249877547ce6e56cb4db15c61b906300c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3c306db18de0f30c80bbb399efc7774135d6c3e0fff5267904851af0c0b3351e","proof":"9074ec48a86db46a15bc7c33d7eb8f83bbc7625e92af15017971af10acb2a331ecb562ed0e00af9f4cdd63d7b563561faafd2680c6ce602130303df7b332197862018b51da9bf3bc9975c1ddc18f89da7325c5bde655fcfd577dd4d7fb09a958f6f2d412eab9d7d6a41ef143663ec000d43fb70945516c8238a27c962f4c103320b64a1290fcd6765bbb76659bdc0d0d9723e020202e67232592c0bb58b59e0de059996c5a2a9ddc8881bc6ec104dcff45428c1258f6e09be122e689d7e396002d7d4a9a97f27c787c877e34aad30106abd662197a56aeb90fa8cea6da1c8f0d14aa42f5f8f3c9d51709625a66169e80a28324ec09528b2177b29f6cc1810c5ad69207ae5a892c52a821b2e5f17de33a44f267023874cefea36e2e654a4efd0a544642e4e3ce7f87658ba92f89b592e5bfa83ff7d9f0db594d32f6888bfb1170b64447d734b160ebcb7671c713309bac3607730dff92331292d03e3a9e07f958123d1f3ea2e580ef7ea6295210651a746a10dfe4c16793ee0a2e244e3ffd0f74de70f8d72f607fe0be46a21e83f521f58d962ddd503253acb78785a32856cd5a6c5aa80c242ff46d1c46f37a49404eef9617d9f24aaa7e8e263014bd1a13eb7cbc81e143e173953b4554f62725918dde6bc2ec4138412d7c0d74e581941ba62c3c2c14b62e2073ff6cee602bfe2a52c64dfadd3bcba8af25db92d949c132ab7c7225db14a410e3571f869abe8649afc7d67825f47b62e54ecb0c29f3218a315e827e76925bbdc8e1d60c29046aff826b107edf86e9c29bf6073cddd762ab836c76f98d8c85785cfbd6890e681f19a95ee93b67b2087661a0819c55840af2d257f76ca2de7867c548125e96a0e81d46c4d8c9b800986c02a5d7313d2661e7c20ac172c30b262302d4df5ea7949ff16c9b4acc15a54a536ab69d909a1fa7e6d101"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"12f8f0a13c5da7f38c3a42261ee38251cba6ef02b635aa1fe02048fdeab75e0b","proof":"005704d43ef46c04ab3191397061ccdee219e8606e7fd9967860d03461dfd05ab2b2c416b2c37f68766f9b348c0a56721bc9276487a96dfe9ebe487a4fe049182890d8a754c92ca10bd25d40008b1990af9f5278a9de136f81cab36a880a3a19acd62d429d216900df24dd285cb7ed4e5e0e2edca4f0f17c11b69b56defaee37aa9294b015fe5d0ff00bec94ad30a315598dafbc9d3c94d77a7cc99de86b8a09134c6a5e88b836face351b300a7773f427e7af9469cfdce43ca456634b52910540632e96e6e6a18e5792dcb8a5b3db5d5f7a4cfd86507feb0373f3133e79130ffca49bed441af84d6a4806b5d7730bf03b187c49c0ea8388833e557365bb46240c4d6efda5aedbedec87046e7cd819e6bb28122bffafadd342fc759a9ed1f274c2fcc39ba38526ae3e19fb224c1d72715f0f194411b825cb529f610870092a1fdcadde207acc0e4a9ecfc95d929f56cc5296a7957be3f7ee680c988d628f22468627b7be0c70f4fa479bc6a1783351b2fa2a4de735ded7f4bea0773b5d770f51caa29df6342e546a14e2c29b568444f1f378551448a69799fe673fce84dc6d030afe74a7a282ea5e81e8408344e347776a2e816a99d225bd279684493c9acd33f6169377e2741334c64070374bb7f94a335a39035d004b0a614e6f36bfa4c134b0f1afb6ea82b705fc0a71e5c65aaaa3da09f2793cbdc93b05997d44351e875e3094091520374417fd2a793e78ee3b9471127593870ce63dff377aa313b9f3761a26564dc23263d9eda555e38eb219820f0b4dfa6665799e557f7d6313bc7f78820c778a5a21480857c73033e590d8a71148a2a8c4cc42c3dee8e890edaddc1031d6de6c90873ed81ad4f3b7e26cb31f462106e5c9eada6fcbd4ebad968da801a562799b691da8e277edd08ecfe20b9aa8c8a01c199f536e4d66f58a1b8cf309"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"98852f8e6c61af21e9dffeca5b2fec7c89f4c87a04d8da13e1681959f5c26a0f","proof":"5e0678466dbad6cc927d2743314cc909bd78f4c4118bc079bf45ed3f4f61e87ea0955e6fbc2e0e18b8713a978624d0c1598dc19f99486769be22136860554b63f2485531a17f3ac1b58ea4ee6878c1d91b18c1d256f1bce68138d47a82d4be71a6a0fa45cbce17d282266c20f272e67d937ccd631bc92920598dc9318e2c0209874d7e73385bef3a3676782f8d729c04bf03d8f8bcaf8b5467ddeb4333dd15073e8394c3cf2737844a3e700bd53769e7ab46d5e9f1584399f24c941c79212a03c13a22b596055724c47125f7750e26605f148c88b8233778c51cec732c6aa901e4d6e08e1461b8f1a60f2c8c9b97c111c61de88e311c13a6a046f540ec01f202b6274da152d872931b0048691bd4a84c0ac94786ebdcf5fcecff666aaba62e15f294fbb4001feb8236e87469ca3d55bd09c498788dced291497f1129f28cff797afc1ee488dfafe1b7cc1fda80c5459b3d472a43d1affd23a36cb487d62b533e0a818bfa9f46e222b7c4e376bf120f479d06f7fade217f948625fd0e94ebf15126b06ce2a54c5f33c17c2094cba233b83be57070d690b59e2b0ea79c929c79199635cd8c96f53fafe238b3e395622b8bd120a059e2a52860ba5e0592fbfd500f4e9722076aaf48a7b59620892e6e122c98b35f1986aa33817e1d06513fb84f6f80f1872ce42655b101c614dae317190c106a6034942e33314646176abf2bae535adc008ba7ea7edd4564bc1317abba28d2e79f122ef7bc4c6ad62e1448b5080e98d64c8c716ff1ba6fc06f55ef41788e4e976145c1d3e63db1f275e20ef66a05b8612ae5ee8248e898aef4e9d7b6e1da26765db6ff06774fad6d4796fede5a7cca16c31bbaec0944d1d29711e468cab8990c2225a131f0d43924440dd665320492feb51cbd16df8f6869efef3ecb54cd952f27e1ae367602b85c464f4c71c806"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f4007344334316f46d50bd91415219db6345d447945a8f02f468049b508f144e","proof":"1202ca693a69e894b4587a61ef6da609177ec1d9e53d5a7b557089576687840960610f91f075acea401700c501c7c13e656f917f1719715e016b8e1169cfdf6eda0184300ec728b1a1e54c0b58bd7e68df96da0dac1f9028641aa104126d9122a2e1430798b4880c14ac1b9ac426847b6da148bede1ed28e013c122323e0487ca643ef9d0b32e8c1764240abe150c2fe0c3c4541bd432d924d5fa55419523a060ec5662c8d1933cc030cf1c57130f2314e73d480c23c45d332f556373454520f20b39f7eab880a1a1ff391bf0687b9ba2101f33457d2e77c79dc1257f40e8002fc407e16ad4deb667b27be5e1187ac4f56999b6c56e6f30a59da97b7ea6e3a1bb06149041d010dd66895ff239c1775bccc9587af5c81479164a57c8a5b563418b0ebd278fb2c04c842c2906147837734b25f003ff1c738e4b69869185ca14c64e008d66c6e3fb1835a991e1c451d820f0935f682cec1c200ce6e125f848a7139d009efb1ddc3332f89296b528c12073f970bfea6fa8f2b00a66f1c9005ca4b514643fe584cf965e9beea27e66887ab2d987d3915a610cc11322e8721e345286790e7f65e4187ca86402b8bd97b6cc1be9857602edead68f4cf8f84417b2a5f2fa231d4118364f8458d69122e42e649df8ea413074d749f26d1b937ca4dbcd15c9e77900ca957a4b67d65d5a495ae1791ca92f68251a71359ee2376e83e904934b01acac20e552559a8a446d9af260c823b9e37242f76d41278078ecef2507a61663c1d319439e1f3b6ad739ab4ad18d591945a67eb82c94283912eac58b382543c5d7ddc18690e4842253457f652ba371028f0381bdbd72ac819180dcfb92419e78a575d977d1747286a4c815439b47faa918aec8dfcdda3178f394ab26bc50435a84de43bd057aae6733526c122da4590fa2cb9c9e9fc5bb1987305e3311f0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"98d8c615525872121b5d79ec92044d4164af80d72eeaa1ec1102fca9629ec70b","proof":"f672c421a6b20858e4f8af2f4cfa2ad2c49a7fd32e55c743e45c1d488d96b565348104f02bdc02faa8d0ccde655f6e3b043a8438fd5918da21ac08d960b73a0c5293db1a13188ed051dcd30d9853229056f623ca5a5740e8ad9137101ceb2139b65c169c8a1f92667ec548d593cfcd836155d91f3246bd27612bdf8de5ffd2374782de7ead02af271d3895dc38f1a392d1614f27afab84c2008cce9e2ae8d3026a64e37576705474b78b0f15fa17925af14b85d6c4527c37bc65a2cb68e6e90832492908f5abc7500f0fdbaa23e05451bf45dfb8b93a17b4e42e1f9c6adfe4088a3ae0335be91e029f719398ccc0c436a409f40831d79518789995d384e88b5790082e3b76e97b6464c18a580101b398268dbece99f4b2110c4c1b4369a7fd716a25bb8410e057ffd1e90760c1eb10819b99c4ac796d685e661b6c03a2100e04c638fd24005513f470d73d463d304a496bf169baed36575faf2fe15e3f1b357b827ff980c4f0146d09e9ff2af7638b978d8fdf2e04624f0ff4b11bcd334e9671e256b92f4325c3272d4f49e28adcbd65e6acdc0141ee815aec728989e278554232a86a6473ba28244b7b0888d80d657b8468d63f22e7946af383eceae1ba481afae7d011efd8c847d8452c9e6ae8218cc567d625063547fd2918ef4a3095167eda39fecbbabb23365b6aac022b68f65c0bcab6ef86413064bc0ab745783a6b1670466bcb2c88315f85a2fd44e1682f7c3fdb40818ab257996b05b6012737281248a3d5c0a0712aa967557ccf2b00072afe63e4719480f0d97e3a2f24912cb00db62a4c4ab9db3f256b25a662fafe0d612477e8841b090ec069c5ced4f8b04a3a84f4f5f0efff489d5d067e7b79757a1dca335a988c1239c2256540db1ca425017da5150d656cd26d8bb63f99b4b3881c2ac11ed0d4fd050864d6d40d052a0503"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"36c60ff8ad2ee16d9651a1ba8349319af26a5e8b5531130829ec120554eefa59","proof":"0c520d900b183f5fa538060657bb616c51509e0c6bd3caed91528b07282e557b36ea6e6a9e82a71330c5bec60a2a97a364636d4dfe3f108b2c120a24cc02a91a34b1383af60f349c164026ee5127520458c9e75b46a8ad4bc5fb89378b01242554357e8ec425bd72ae03ade1fee6332a919e36dd9c5f611979393067d8926d73e475f5880ee151f413a7630a5fc14bd78727a8b17fd6bbb887aeceb774853b04f94b8953444acc626fce15b97225b3b1206a3e9893677d2c1534ce9ebc9e9e069118142b5834f798cbb8b1261b37ce7423eba5498fe3b2485fceb8c03a5e6f0da2dcb694d55db2a73e417d9f7fae87349766ba30964acb4868d4d0736a21115e7e7da4587115a21ca7c2b92f3a0c73d2f4455a41c5ec1c93ae7808e22f5a457a72b90f3604881ffc824c9a9b975079195a55a6ee831b6b7ec2c9219f5fc6e1050e1b4535413c4b4341176b6692a9e313d70ed1341af732b6a014b73103d70f481ca8dde0fc5613143026540a1c7fa16a6930ba7be1251157aaaed0aff5f9a80ca8b5fffcdc58eab740c892dbed389f94b1e213ee8843463f23a437a60c68780f16c453b946eae1b81fc659e0539bbce365ee0d0ccc4f470dd7489d4dec184335aace83ab45a82d658327c32b95e1a9580d96648c9e297b58f112de9947661c0eee3d74160b595afe5d04d4ba1a3a071ef49cf2ef0e14bc3459d5001d03213776b0f76101513753706c941008ca1ba10cf68a76d1ad54589a30d4dfc1f42cc97f1892e34cfb46281a8145ce02853f02169c315ff26cf1e5c49add92d3e93659620630a863bce8923fbd053ba8593feeff804a3bc68ebf12428bb82b83ebdaf04019816b7c74a255fbd2c751f2a377604190e2ee130ddaba11baf38aaf834e42051e2b2dac71817a208aef34ae58380d7053ad9aceeba77b297ffba2b0e33c3e06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"14e03f0f56f0f335df6876c07b6d4626ba444cfb1e7e17b4d15d0af7de345e53","proof":"8841ac4e0308d789aa85d72f3938ba37ceb6e81a50d25abfc18a26678f390f663682cb7ef6f8ca71fab3beb051ffe880b7a2dff677051a84dce09daa2ee11a71a00ffc81931c98d21be81931a96b99359698fbf7c4717172759f9853a0e0145bdc088c7d06fa0d726bbb026ecc1e9b036e1d660ff70590655b76b9a29750452e5ef4755d0e88fce4049916d8c2b2cbf50ff1398603db3e93aca9baa8889e9a09075ad8e4711c8ebbabd6d7be596637c58ecdf365cd02158d11031ed44991b0085331206246f0ed417caf2eba6cd4888cf12dd60a8a4be98d15b85cb73fcc37027c2bf395c36d7780a2298b4443709fbb38a88acc618b5f2a2af0217e2b7360153e615654bf3efae06ad19f55d6e924574675c35303986d2715f9b184991ff55522d4b90994eed39b393839d15e4b63408cde704368cf81a4d063f079594ad368b2c10a358f0c442506e7d39815de92e6e5871c84eacabe34a9d9344ab1053b0c685f8a68fc4021b20573be1c718550cae9fda99597c3f647e1bbed0c21dc33695803b927c7de7239f08c4a1552e713377fc4fac3df73846764a8c5d7a7d08a39345215a9902a7e182e64f52217af90fb56f9f015bfffb7e98e6bddc89a07b052809fa0e0fada64565c86e878677b6f3a28e7955d9893e3374995d615c7e92c524a07fd68ffa1e9e32a6f97f6af1b370a9fb76a3657d17cde7f6f74ef5399877c6afc32aa8a747226c75a911a1b3c0020f68c2cb749fd7d6a31d94410c3f56e5a8e90a1bcea3f4abc396898538259b70e6e9a527aa3f12384d66fd9788a9b4f0a4e73cd45ce882bc667fd6b5b609b6bddb9b6b17a8130a0e7e8bdf7c9fd1abe47548b2503e69a74dd8799f120f6986e04bee5c95a4ca6b90f76b9177508bc6008cedb68386625787f91b73e19108244b3745bfbd82cce3889071f03513daf7e0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b6abed1b8cd9610d36324377bf955df14f849feb3248a7671a8d50686171a376","proof":"a0f4606dfc1e532dc7705f0a6c352a7748c864f115f1b5b0acc291d29bac4a004224c30f4019ceff06211a03c64389ed0ecd82c6636bf435a76b1035027abc13e25d390cc0c7178f115affda2b65892f09f8b0dcbc5130f3b52a3b208b946614c01e68f24460c11196f20cf0868b27f5aabedf3b753acb7648e66da995d78a004b875b363adf5fd34218ef2b8c2e455a5b8903ba9ac6c7334194531cbe706d054e34e24b1518f9ec1c40e6f4552a79b0dbcdfe6c31006a1ca1fa57f7871244046b0b427ca511ce92a877187c980c541488310306072c991d71ca5f2278e0dd0f1a674b50920616ad0bd61e033cb8cc16f2491fc9c7f3ae78f8697dd35644ea6d467689fbfe510631cc42d7e55a8b74f09fbb3794a58d0228c2fe2e242cab135ecc99ae80d4467d626eb01930020e4009ceea5240ed6f09515859a79437c63d51d4876a04ff9b7926df3991d2ae6d51c1f491bde53a22142aff88243b29f3717a78833422b88b1beb05710953b01735b7d33924418bdf388135db89435c8bff28049c245af835135492b5eaa7290b625e22df1ed554e6e68367827e0e3c947252bcaab902042cb82282576839acaa4d3496a1bb22231937a11eeaa16a73eaf76688a7129b9b65c69843d679e3c82373a4dfafee04f7833a3ae067571e7203026640f83b289ec084c8b8ba9c24085776335ea7482a461077e308f22e86fc36dc5adce944900e3fda5a9b61293e26d76b59027074f927ae1c89212dd68f317df70a427cead65040e85d52461fafb50a555399e11b1d33e700e6a442eefd6b31673daef9bf2cac15558f3aa4f86f466e8bbdce92bf7b81372f01cb44bebd5bcfc85e238da20a679d87418781e46415e7f944a9cd0cec4820dcd05fddb143486c410f9e0ddcf92a1d6ca3a4158489e94c9ddedc1840a5816ebd8b460406d9201c6402"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8ed2a0756034707dc7179af436cbe89918bb0e1b5f063158d905333862cffa4e","proof":"82c98e36c8c60d249e76e4a3bffca2d87a4e9fadf049476d37d24410276cf303286844011f0a10bceaffe1105d303ff6267f3e182c670e50f036f0ca066d6d6f88e397955d9979e6a17bcf770faafe5624c1f32de9a0f614324e71396868cb4f166dd4f1fc19019a9ae6aac743f73e5adc68336c2aee27ecb13779335dbbfe1d2ffbb42062ae9bca8772b4f4f1a8488cee7ea00319296db4d73934598cab1c03b07825405a1d42bd72303d8a328fdf0b66dbafd4ab2cf661211cdc6f88b23c0a7589a59a0781bdd8cb5d2ce61245b9968a4a0bd5fdabde8c68702fb249ca240a70ede8c42bf62927d661c3834b6817e8d00387c17359a0452f0bdbf61c844550a6774b3981cb49ad6aa86d7c41ffbf2771284613c2148aad8123351c5cd55c5bfa62f88634355f1c8cec0bec9e9a3edeb72393ce863bcef7816b5ace1847952556613dd87e45a06326f18d811637d0044e4cd735b5b80fba18726b21611d3b2c361afd503b049b364e60f522ad5cb6fd49798dc901143026d1b6efd6f81c7c4a8c52e385234cd322de5be25afa14e16f36b999f1383a24559346862c7e4aa1246ee0fc051aa20c863d85badc49441500fcb44366509d02b197c492cec1a8c0128618bf5c26526dbec4a0b3ad483e64913ef908eed5ec9a1dae6a80ff067c2c0cbe00821dedbf0b1fc5039035b065e5a950f38dda12c8af20e0b4b3ca38621e058e2f36f9f5635a3c1022421a321887fd5394c784e0b1a0e254ba3c0b70c5e5563e4d9bf8d922294f4f0c4d84e301181a270712c7302b5ec3d21e81bb0edb4439606a8f5abadfae830455b5d398eeb1594aad76e2dea4d9d2fafbd19c9f924064955d7673785b145706dd5dc293306c9f3989725008c7d142b3c2ce1bd4b0e400af89678333a7e1f6979e57a81360758fbe5af22077c97edf9d262968fd951d0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"10b74b8126348e880ead9a0c5673f45eb0cb8bdaedff4abb026b7f24074a7249","proof":"8280525de836c383dbe9a01a59cdec83bdf74190e3cc4dd5c7201bb297f7932308d3ef3e516641bd6b1c20bd3252e4edcb7602aadbd72f556e8ca99300bf9e02f2f6056139d058de425715f5a83c266bb1d08bb44c627f9d5237384f03f2b15bc4f87c852f5cca37097e972a5d3dc2cd04473dd858f43b54b709bd7b5816353b0631f280208937ea1b428452c8c51c7a4c5ada3da528d1c3c0bbae4ee4e1f4071d01e80358ed150687578e3ea65af3427f1d8d0b776952cd948669047928760b1470247a9fd58d0ab1c77af7479e8804c37e541daa0248ecb91587cb07f5c20a82e3e204608e5f9cd9a1889c91996af1bc3866af02675fc39a20d8551d49f45d725741a476fda734fef67ddd06d08b7dd0da8c63303969fbfbb1d54054a31745fec4c4ed046774be2e8b9be46cbf851fc27f071073fe1ad76d9df10a9f58485b04078b19784e5993d9d446deb9af5bae7395188865aa0ed20fa3f74f3928430892cc516910a231d9095eec49e0ef321638cb3f7a4cd4b993bf3e7938d219c02a068961964208b763accb946f3527431aa3e58ce348a2ac9bda48075ae44311283400e6045c3f945dfc4ec3334ad2475ad7f3f7e4455f65dd55838769d0c0586e5c3da39613d9f8bee4930eda8b778a82b53b6f848191394cd9e9ce26c9d3d37ee213d8ed8e472d2de69f3255a2f8f7278b882a0efb7b81df81800c307aadaa4bca35c45e746743dadf16adeff169c7627c2f902c947e5a0756ea6beada17d80d888a80b388bca55a4155043f45ee093168b12a6c56f1698740bf2cf23b7de60704fbf2bb68b7bd023cab843e49a362c1fb71169c101554bd6e9238bd785efc3a1cbf13289adf3d4491727e219de3a93a876524ced0595ea57c0de00a2285a30398a697d33f9fbdbcdbed9b3f15d9539e823c15d83997f37af27f48c444a5ad05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3eed6156c86619a7546ec7d5a38bcf52bf2d979fd9a2b8de0062e06d337d5214","proof":"3a72b7447f37635d46c513088ed90ff5724f9febff67b33f14e23e81dffe463ea2740da2279f469c3c28df77131d86c7eed4259aeb8d8da63b01bb433398215ede612d4da905cccc84b7d3b6fd325ad1447eb2c3c1ad3af7c4280b61def4251c6cf736edcb811b9ea59c45a15a6a7843e5536bdd7156845b8c71747f42c0b512a5086a2a7a11ebbcd6c569768556e316293977e123d9739a3e7729cbec496b02eeb1f567a951594c5e989cebd60188c8eec56d88d511b5f112bdb849702a5000c299a4de142e5febe9b22fb56f45f7b9740f4da157850000dfbfe5c3dad65400f251e3b755c80d8c021f3a9f947ade7487c29ae97f00820aee11c87a5c5b5d0cc8972a609ec3023dfd56db2e651969b2428d07e496986f3ac6800210b71b5c56a42c4ac453be56fdca251bdc6f6229e87cf4014ee6eaac6f00be2ccd39d0cd35fe3eecbf74892484d99d0c777813731fa961315460c769408eb5251f96b9743b30b1a86b74daeb079130360686e77ec8c376b127f47d03fe90cfc4048889b2295e3fad256b4abf61b5587269db69261dea6053c58c40132c90719dbecc69970fa248cde098a6ba1fdf7fa2a658e32868792eba856c70054b57ce959235ba41057cb71bf9fd8b96bc2743008835247a0fa18d627bc1c3cd0373474aecb1bd742f988828654f47718d494c3e9052a0e04d44721bf92db7e75bd60256044cd5fb71a8062f2e40a8aa604a6695091e63f95a0be962c5bbb08ff449fcbf49f6c09379e4f5acaf768e6ccb6bc83896a466e0e5d397b183bf4c2e5965b640043f86af060ebaa4c24ad30c21c9091a4a4bb0f9b2a7d75e025bac682aeb7f9454db062f2c2290d4dbe231ed13c81c2dab3bbf52751b3185d79fa9c7abedbc8b13460d7405d9b8eac3e09660e2c4c0535a9afcb858da9390cec82b234061aeff5f8144aa01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9e34df367166e97e00951930c73ecdcca7f2367e61b3d8bae2de22ee9fefdf24","proof":"2a230ca859ff8db1df6910480851c9896fbd2737b1cb863a4267a8611f783901fcfccc403956cc4e98e6121f0b26c01330f967e9d77f055ae8e324a1e2cc8664d67b857b2090f6acd3b398169e4cb343cdba34132bf76efd5c0760b24c1bf76fb63d86a99a376e9e115d929d5155232d56604e9eb1df9d778e732c22c80a654ce6170e6a3fd37ea7c1a8252b5e05959fa37c56fb9b3cfea9a0785256894bee047b914dcd70375de995e9b0069089ff7ac2287f56001115630cb1d38da78e4f00adab73b03a13b2b9b050f4595dd4da7bac5a9cdd1a910934e9e710672ca76c01742e332514f0d480d435e5aae27478580b466b801510d2dd97ddeb20312055245ce999b8f851b463bbda653ef34045160d59d6cd53fd7bce9263b2826e1b42224e87fbc1f5e0698c121460f6603d3c70d051d507c5bbb108685f136c29d6f1597007586b44dfda905b57523141434663e12ba2b71fc555d3e7b91136a8898d70f0c4596b4781c7852a57a2671fa02e9dd6b6816c41f311e60811e958409306311e3ac4a04f789d65ee9d0a6515206ac55954705ea094963edb0f59520a1996521c13833b48805a06fd0ecca54cf825541124d99c13624c48205f28983bd6955fe4809a23773b7d3dd4b7a09ea3ab487e841737a50d7c7ac5244b07e216e50a378630a99cad085c20513ba5605ed47cadb4580123f9890b940e02f44dc70f697c4cc1c7b98e580fcd74837b8908941f616044f4a1826e37a225e0877a38628b1a16a896806f7dd0f2636626afa479d086a9bd87eba9b9909892756e04be0fef4bce82473e2b82e9ec73bb0b05089600cf87f5e32146d460b070ef222c9ff5ae4414b0f414aa9fc765db3576deaa8fae28271003be882d6fca5db8b6b8bf10720c8c0d9444234a9a5dba83efe99cacc5c7eccbfabceca0d91b7ce73d109a1a920e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ec5070fff544bb9038e697d25167bfb0081cfe3beb63a960d3c10b808a408574","proof":"2ee59b190a54ecc834031c135105ebf3a752fcd61531df27195d20a333c7e11c9c276bb2f1fc700d3b11853eb70c3a853a97a0645f045251a1a9693895fd65466cd4d387b86aafeb4a1e98cb5ba9174c3fb9e21d7e5c9afb909f01145734e703e8a9e6850402e33ceda4607fc36967c95d16f16f65e82f78999e40208fcd02664bd45661a76a74297f91fb134c35dc3aac6ec09d0715862968b635d5ff81560e9c7d04b47a3f4a79e2aa99901d6eb8567870d712e5c12bc7affd541b4db79c0702b7587b3014f3346f32ccc307ac6db371b0505d067940a58732d112acf00d05ac4f7942172808a59a6eaeb73f1112c6b14ea1438b665e9ea19579fbf3578615c6c0ac0c4c86e1640f140c3f662aac7e83dbe3fde8481856b19faeef3ed1682cca7b019d3c2db1b16f6f4f7b82a13fe0b3d349d41b0a6ee3b4d7c37cb603e112be36831823ada0e002006f8a77e3e14cc4fd8066805e02137d251e2bb8edaa71a239ec37abf416b4f49eac18b9f3c9e6e548bb7dd6cfd229251683bf1ac9ff2b201f236770ef50cfdde2e24ca07585a9740bb4fe1c9e0eeafaf28925b3f1e879cc481a992ea47777b70e5e1023cad4ddd0438ccd04890d188bcf1235435df03c7e76b671617dfb775c0485e12682c8af8fb9518f35a22dc0b75d2cd8575f4b0b7e10b403a47218acb8009bc82eb9bbf77f6fafa6ec7fd286bf24b09dd2d73c0514803ed660aa87547254317d1a94877c3d04797345f0db620c9da40f63c10c0828c2d9bac8026fe6bba0762441d7d7c529767d393c83b5f1c61d1f045f0e9e6ad25ff7692a8078ca3a07950e43bbca53f960529a84ae408dc3c1fc3893832c3942e1bc1c17414295b19490bff7b179a0607f8d2f4c204cdd23053f3d96f34a0b374f307b6aa0c470033d0737ef65c28a53bf0eff10dcdc64ee2858c64c0b480a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3ee66c498962dacd9c093c5730c997283824a831d187388ec2f0c0c4df6eb42b","proof":"a4c9148282bd76eb446954b8432250805f77b38afd0ec70c706699ab93838a7668a441362ebb9c31944b2c61b3f2c4912137d7ad4c1d669ea83915eca8a001019a743227ed54f646afacb4bdd02a03fe5b5d2acd72f1b5e06935a12104e28f595491e9fe5a3ca9e393253f93480bd3acd4d68ed40e06501f91806505eaf55249f4724213dc484e07185d50bb66134439df9d92e87dfca3d131da101f25a54602131dc3ad32c457c2b14daeedee16ee124cd5e303cd4255139f949d358066b20300f2f8b01bd29f98e01f38cbd21f27bd22756007131cfbcc8f16f09e25f8bf03d8117d89e025fc3d1cee6b04d426a9b3ff8d005714b08c8361dad044b1c26f56b26ca5a2f5b66b8f48f217f2d9c6eb73ba3da6089ac4b32e8050fe45bd70f654fa09aeca5157f9d2c343a1d351c68fc3b197af47258dd6d560a67ed383c1a126a46c3f07d58c19e6d275eca96db3f34a75c9366c07d5ff6b938b4f8dd36e683d7efefe31edda22c24273300f056d20275e95f96e856d8594e523c2fca11e9a6544034c8294f23730db6f5476d20ead0714fa105aa3e08b59e650f870be0fdf3616d3a2dbb9c12d3095825af714d79a9dc2a7fb67414235b980c61c9895478937cc33cb257b6ada6ccdac459e44b81b61b9ccbdefa5bbef6af147b833ef86d12ba63e9c6a7190640844627970277e3f3c7083f81d97f1c16090df7ddb6c723f077c7764a198a262b8bfc3aae0c41006c713938aa33748d7628e21b99dae525d2724e14450713d073a771459801526d502a5c3f595c313616b63ad8e4d8589821352d1932bb690b5239364a9990c3b1e3fe77438d4e12c01d36206f9c8deb9d645a504c6a82eaea6e2dad45c0ef90aec2e5afe86c973d14f02f85781512ffd32002f78fcd1c8f2839b281631aa23d2981c44c9cb54e454b4d24bda5eebaa86110c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"de2788b3d1b9ab6abdba886ad358884539b2e9fdd6998bcb71c04a30f5ed5e63","proof":"6492c781efcbbc0502c54b7a726d16aa049c76e52204eb87ee61e9faa3c0047c907aa30275bae4fdb330877f51b2fcf359526b6a295a3b5f8e0655c5466c424b6222d59571fb5142f11fa1d5d09319d0ba2b1b738541f1bbe2e27a50b4aff83880799117cfc77558fe9b2fd3a1c12598dfc064f526acb7b28b90e08aab74612c4cd658c6940472a414a1e69c82f6b8de859770e86d0094fc8eb2d61828fc110312bc6674c0e8fb2ae8c262fd1bb9f428877892b12520ab746e87e32141ef950cf5da121a193d05b00eeb8af236c83be2c14f722b5085d1654d5cfd19bd0ec10d005a86a4d4a7c3b5f47d4276a093dff316e59e18136c903e6838d60144f2f73552c92dc9caface240e6faae04b1a3286594aaba785b35c1daa4ef179532aec05b4d015c42dc611ef8bc617aa54c919734a05f4623455708102597003e0dbf4650eb6dc53127dc71a11b6dea7e43ef42ecaafa789d44fd1372736ad3337c2a9409e9dbc3e41213bd323c4d6451a1e7f7abfafbccf47568b2ca1c0a734399a8f73ccf5f2b26664cde0c2d6577521e51480e038ba01b1287a0f45b583b29933b05af0b41aef6ad717a52b63132990eb8521303748d4108577cd80dc2e8791a7e67da08af15e6ae635cdf593140e18db012e0a9744ef5347fe3ac75dc7de69c0ae2afc71a72a2af7b61f7c14490550494954b1e6f932728551b7ba2e5b08e51e891d9acec0c833f7a32df4a4e5d558adadf2a56965c520d8909ac22b0ffa27f82c284085b3b9fec9be36a70a087659d6b89f2403469127acc01ae4af91ed446250157c7c58103fe88e6090db29dac54cb5591756467c7e8cc5fee8b66e91f154486b3be6774919dac3c135b723b2eddcea8f85458eb73f8bc407a028f2544cdc7e030a532f33e48c283d6773a0fa01f859f6f87ae2984679540586dd30e73e59bd0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"282c5c85219f4174a8a3f4bbd89d9053f4eda4c49d3a9e385ae93b95c7523e03","proof":"1ed4a1ebcef3a5d390aa8b287ca2281e7aa1239159bee513475bf36dbdb5771c149a516edec31cb1bc966846802d4bf17a9c227d5eff22853c103491e366bf2862b1f3b4de8feca715b2f554098566b5d5bad9a89fb2e2e92d17f165c861a90e2a1e78cb8e7317dcb11f8e460955a02e8c6eefed58ee8c50c5b75752abb97e7798ac3ff14b49c507cf9da40dc1bf5f43c2cc8976528dfff4afb0f9c984b10a06f1ba9e9bb20c707d474c9267f92ce2abdc1f41f47f6ca7e79fc6cf2cd81041023a894cf6195f19a29a61efebdda74d91675ee9b9b750f22b86578a1eeee3a00928283d0e81e055515f49ae9438aefd17e7a0735196a8aca11cd35c32b2c1c26820974f5ce393d59104f1630a0fd4f0ea603dfdeb621fc809db224731c2c4af33784fc94a5fdafb0be815d7642d988a8b955ab5ab6923716144e86913e99d6a5916a6c8b2dea33d138170a3ad08d54d685e999c8ba5e84702e1e4ad284f813a1e543f7700707f9f835a7ce7e59f8fbe227acffec3f4c0402f1316fd51d9222e7a62f83a3251efc92fe46dba1f1cdc00525617b450c9a06fd4d873101e91ee345b40b14598a4956374d5d664e807aa4c5dcadf102808172e75bb9a9274dc206e094e79095ea9e3ee9a74fee9eafffb0b7a0496fec6b97727f9cf5ae4f8b1ef19777adc13d39438f8e867edb28e1e344dae41a3e38b0f7a8a6f743bf4931bbc1d6e10aef92649e2a9fcf386a4e934b61abb7c80d65875415c9f11c9a49898b4e755ea5ff022ff9cd31e02111f4113229eb0d43e781a01e000b95f77209ed7299645fc9c682d4fad11d1ff2df23e5f1bf68d91c5881b3bb856308823beb10cf7154e4fc43e08b41cb8ec51365fef32329d371b3f8c115a57fd67f329e0b95b39630aaa1ce7837661226e1c684ffe79c5ed281bd9d051e2a5a7b135b5b8cf136cc40d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"be5f8c74357761bf9dd48d71155fe149a0373fa0e82bd74494e5094cd457744c","proof":"8e8a2c0fb2b49240ca7ad6229f8920dc5ea4e110f049dd6a199cd4430e03904148ccffb56f9143599441db5749d410c427d2489c96dc66e2106402c7c9b9aa3048373fad0dfbc9403c625fce1b7c02222cf2a28f3fe7adec2cbf5d427043a037344c33f68b12b7452e0decff071ab65fe47c953e1d6d862f3e1ecf37509bf36e8623524925152a14d0c6b5fe4389c83be8db01da7c257f1587b5c6bd3b22f20572356ea7bae86310acda23137ec1e05e9ce3f9865b114bf147a8973317508d04094735d926d5ce5b646fba99e1b1ee3987f137ad4d7a5e4ee4d30fb9540cd709a0e4b90214f98f1e8ba4badcc9a5b950993adfe3983e29d751a323ea62b371436eede21e2beca554bdd39499e758c7b9bf0aecf16f0cd2d3bb4ce8ed3a0bac42ba045777878e6750e9ad5995a6c295800571792e230bd3989fa5608c14b1c35176a7185af5f30b61a7865702000eb417aa8055baf6b21327d018b069ec5075280cc2a99c15b2b5142e6f9ec7c47b839dc56285562b57e3a830b69dc0f035d325208cc8edbc2a92891bd410bd05308e7c36d95bedd3e6d1dd5ca9a09c87d8f6204ace2c9283632c2cbe222b527ddc5d21a82eccbf1e901f9f9b9ceece3c49df48105fcefe52ba0b3d4178f95ded3bf17b906cf4025a595e5bc5c1e2856bed592aa68f4af2372fa46499dec08695632367ce4fd6f20c4da43ec2b735a32584f56c2635d9f37db2905d4c5466355d30350b3d8afcfa5a0be3bbd646869f5e26a42d52ce6786d557ac8c6cb88d8b3476600fb8409aeebc9280993bd462efb861293a181f8e242d0a68d95064bff016e734106cf1a87606cecff84f164240574ab97c9b00a18fa57a8178ddca7d8350671779e27f8ca5d5c3e6597c7e411177bdb50ab81ef4568979bb160fd038f84659302a23634b5804d5401d80497a5c816fda09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ca469773c602d1f4595e533ed8c62183875d08ca4435580921defc3556a83c3c","proof":"620790f317e577ed3e53fb40a9595dc89c8330ebe2d9d07fde69ed7bfb7de913de29a6376f76b0b3ad50f1fb5961015f123f833bfda2b2593f74e1275dbc467c0cac43d287452ea773dd47cbb565de4d50f891b9938808ee786096a67df88d55286f0e159dea15cde421defc41faa361d31cf2af1c4a1b6d7bdbad072949de4e2e6f44e35f67933dcababd8100a2eadcfbf666465965859d5193b1dd717263078975779f480a079a8e8baf2f9420b6630331b5982da1fea782060b735141e10ca2a2fb3d8d93c96727c3f8545f3f8d081371268f2d7cb7a2d21c6148afac19004a8bd54259f764322c7552f13b535888d65ce31ad87867ae12ecb8c28cab9b155ad6b15f4c7fdaae9f04f4682c23c526e67645fef334d78289c170c572cd211be4bc219b3504c688fbffc9fbfc0b349db227cb5331f272eb5d0d1cc0ae8bb852f01781d46edf6ca24adf2915f5853c95f4eedd796f5de7d36fe28eed4a409608b66fd1a86cb21b480db02034143d9356fca6a97d27a5bf53ad36e14777633a0d868a101c80d00b41ee2fa6a886abbfba156190962699f60db90f0124e68b9c474a76b8d0457284f4deb94440c3b562e5b3fe07a58d94fd6906cb8b0ab07bbe6386eb2d7774ca3000e74700e5a9f87ec299bf347d62abe4fed665ce2c40d1ed6d2ad994c9d1eb411b7ef32b0387141850dd89ad52983786d9a2832dd9672c9f5e10d919b8cb171d85c1f115deb307939ae5abdfe9d407c0fc00f6deee0827a705c2502120bee7c9cea41db97edc41ffec2ce57d6768f9848d8afc768109ceb66f62b4aac5723e732fc0d6a02162641dfc6239e7fc59f602bc547d93aca9590a0f1e7c5dbbd6fc8d30e209a746b790cb6fc32de42a7b56a424bf5c22620c028c01ee84df8447a6dcbac865b58c4923381c4337151fbaa06c03a90832059cfd140e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7046cd12f7d4d22f7330c01f4dfe5cefd254edff199a90b9c16f418a3bdfc31f","proof":"5ac5271888a39bb81d16fc53db5b9662c5c5f550d7a8b9bd3810dbaa583c2d67f64fabcce17849968911a678e73c457a9c16728e6f9924f8b49980af36f31e415caa6b0613848d425c95824de1717b26e489478f6fb92cc47c2955523c456a640821398b94ab9786a8e810add1fb4835451e43a1eda6a068e507eaa14418a64c3a31314680fd847710095ffa9d3082e8b457d6c7d6938630043c6630a53ac4092cfe27fc57587da81d6499488374b37f24b165e3876385128f8e4f57063bb803dc08427f477befb9fb06ae570526269f6597cd2504135d2e94462e1ce4f85500a693144de40e30512283494af5c81a71c83643a20b1de0a6af7382b4c3bc652c46d26e4fc7e94ad79188054d4ab47f217470fefcd2aef024825af2b6489dec69aa873005458c3410ef0f27abc9e94aca3e84535ed882c5d68b3d5c749568cc4acaffec673ac133265d562725070cd79a7529e9d644c387cf544439218ba86777201b747677881a50ed82bb3dc082df45d04c6319c8bb2169204657bc527462399c904c06f313ce6eeb257099b0779370059e88f15ce311414522ac39af94c3057213def1ae33f4433a1384bfac9984309261e9d2257f6860c92307215ac26c56f48ace6789e97539ae9b252e988bf889d4a10c1c1dc24541b60e4fd585dc084d34d564a62fae90180f49a55912aa35662cb37769ad8f6ef3411e3ef9cd6b731d5c659c3cd1e2d984dd8d0126a631650992df8a0c04ba67fb4c8a10b35e1fcd4a18773559c0fe9e77813443d1746e01d770f6f81a4cf8d489bdd44cf7e25c4a7b72d294579da336369c39a6f5b8f3cf7d0e2d811f8bad98def02ea544a0380c2b16827dcf7176b16d3c1547b03c5e6110f5a6e22dbd96612560e151ee3b47ba03e0066c5e57e241e6293adcaab68a010a87f37fa0fe3d5c25f5323257f6996805"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c2bcafe1a6f0d5639397f0fa0c54423fcdd59f353468682ec5b1bddf49dbc75e","proof":"5ae8ffcd960bfb52c07909e27c21be632487b326eb9ad1f451c4941634656a10f4643e7468c839513ce87423fddb105db6548ef5e6cef9e013c8a1549ab29e3c7c01e2b2e68d7cb26f5b84e966ffd55bce174dabb8db60d9e5d7a51181aa314292ddcc5700c28abbfab2fd132bb23309acec446338d702daf945d2904a39ef6cce45525858132f23288aaf45f0c05f6858fcd9d696ee79e6a81af85d0f52b20f5509d509e3a735160bc9180f6fc60b12c1c2218e2e2436b9d61805ec9208f40b6c498a61fc84d92b89af9f006df4d4b4cb7f45f99e6f43277aec48c1ce3fe8046a74d84e4ebe9758798a88e0e733e5db23d58b5be86f37a551127b532299d6263aa59936484ea7114004e05c927ddf1da2ed1d89d982c51774fb73d7ad2b5745ea0bb84a83db3f1a9967ccfe637fb489e3aa0fcbd1a60f4c04ce1eba8df84528c2e8934e942e99d9274f3425bb25a1062619c75771f4d47caf74e1e35a2fcb1a66928ecc8ccb9830e62187c91a7981b3e405a0252a699bf8d5a2369d278f1f240e351a469f01cb1bc5b76e3627deeb98ec2a05dbf3745dbf7212faba2d66e460061cb8b4272ab58671f06d9fe8bfbf2dfc06bd6f0b8abfcd42476f563f9fee1becf87d60804b1508d4bd5edca670865d8532600e87b61deb67850c8a7f4b7c67c0ba6e0e7becd41bdffe626d002c29abe484ccd580451ad4c96107ae99cb9c1606e4cbf354975307cded96270876c8ee435b3c42adc94756bf7114d071156957284491d8f3f2b1accba9d1be48069ba862fb820507fa6ed27cc45160c281921b52f0fb476874a9d65b1c4cb70b15e4a41884837c14d76eb2a1d96fe625985a71a228e6a577856ffe4872466a854d7008a019d720037412f9e48d8195be01ba00b2eb43fe37baf4416b8815ff567856be8265ccf3e43ca4a0127895343b1f5602"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"349b271cecef8cfe3283390e81a637a624218c6ffdd9d15a657fe599c5b57119","proof":"1484135c2a530a5bdf5949108ce252388a1b43ecc533d1875e844c4ccdc659774c2ce4dd5c1cdddff96fa2ecae4ba6e51bacfb31ce300d31c45b230577cc9d2d460e4e43bb488b789db20ca435df0c7c55e2bef9f7c5959f55ff62204141313f6af822536dd661b8a6c8df8ca3ca181b0befed980ff8f592352a29c4e803a8689a44615fff53c23bfce589b03430eec64f18c8c9167c221a54050173e158c20406c0b330d4979ab7a38b2394036774dfcf4ee56ceed842205b153e04b7cb0e05c73053ae98c651c64df4cb4385a4c349ea6bcae101755fe01937aaa452af28018290c8a61a948543f896aa47483a6ba5d3a9c1fb3aff9576919b8f92bcf27000a42aab99bdfa7f94415367abc4acc2893ef40b08685a25e81995f80a57038e7a860bb81767214002f5c4ad0e0d61b7c7bd62e350f055a78042bd8de3ce60cd34a85f960a4d837c5f03555305a157dc8bbf6e9813d79e77fc608630b6691c59550e20778d678da5d08348d319cd7b5f909eac30879316be1a2a1dd5ae2c491f340c306f4d56fe192a1564edd4ac520b72dc2112739ca753c8e347ffa972ea785e204d3aecdd6508e503bc347b12a1b13a413102ff8590ea9ef1eefc35db7c470fee7b8335ac8f0d5179e91b37995993f7544a05c564de17d899f6e8c1ef890f278636b55433a5df9d4453d852863d83572554ed3ca9590a7280e3b9a0f2897d68a0c98982a63d78d32ceaeb8e98d010ba3e161c1bd3e28c4c7cd91310f2a1f4163a601e69746749974807baf1e3dbd037eea3968758e43d0ecce8681e6092fd5108609573d0b533b26d8b9eeeb17f4c2ac5ff5114e11e79a1f715e9753169f452e441e6e90779fc855fbb44da931e15954c4f182d14816b4fc424558f1184b002051470965cbde7252bb715f99167e64d259a1d84a2a8e0918c263cb09a05be0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7e74a182da0b0fa28c637eba86e839d71e6765abb54b58b8322e622215ace843","proof":"54032c5b7921fbee3708b573f47548a5996bc494932fde9eac8788a9c7b29f640469a76856e360dbd68fea7dbb4bda7c05892111aa7c744d6fe367266cbf1c71b6ae4fb48fef60b5ec283707163de6d8b8474ba73ab372d6deae4b927e4dc73a3c9ffcc42fc7d7f913bda2081c3cbeaf80ec2885a0b14005645895bbf34ddc0581eadb69c408f16da62beacbcfba6b822ca14f59b8a60de996c7de2128e40f07843b19632c789bd2d6e2893b2265328861783a3329970eb411be2b214b6c7205e4e4acae7db6ef1e920b223bf818d5b8f85b7af5c99131ec39c6a633975ae603748b45db0492e0ea8d2ca668b6347b212139a2a64ca8c83dd8af1bbe0ea9d666c6dceb314b248c2232bd831d921ff3704296827ef6a234dedf68539a0c434c784c584fa1c7b97023e84fa3b8dcbe72e187bea435c4006118aab78467e9a7140b226e28c3d24278c621153bf9c71d921236ceba3c9d93c9fccf64995fd7f70e35bc94170f09f290656165bb69b5dfa7add94257ffb8024b24126c880c6700282c96d22addadbd598a279c85af8ca331828bd78192622f646605f580ccaae6780d4c3c31e24645e6f934f42367f25a588fdb6a9c4865358961921b5a6a9d12e83db23a94578f5df425a9631699b77e673970f546e983bd42c8824d0bfc86724f603caebff5ec115ad08b17ac822677ecdadc213d93bb5bf7747513544018482162d8f3d6bb805ef2aa19c0b9e53cb3ab9a4b0075c54e8d5fe9bd6946f6eca02917a84f2af568cde6ff8b42b68b9211c3ad16a69b6b1f531c2901cd0e8a3dc7fd29e4af529cbb3ab2a6ddd4387d697b65cb1bce30f786a6eba09ed0ed57b0aa6867711d6ba9b00039a48d0152805b6e3379c2f485b7126f6e70b9f074de0aa0ba04ce0da406a2f59d9165f48ccf16beaa9e6e3cc3791709f1312a2981196d9aba0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"72bf0da8c7f9c5d3439e4f2e8e07a51607d7a8312ebd9b48b67b755690d6f84b","proof":"0c5f18ff9829ea259f5923eadac479ce537d10b75d08dc6ec1251dee1c3be64b8489735b61525073f627a00019ba15b79d3ee0d04402dbf739b25474a984e3084c77377761963a43f1b63c534038fe832e3e487150ce115aa0a926fe29dec315e41c562687ee3b61dd1232c20f54bd588417901f5d37bcb2d560a0a4f491f41968ac09029e67defaf0927c3b7313d9093c77cf747132e5c181569ee674c18602a0572ea1727bef5d9bf07a6236bc129e8bf879dd5060a560473a80ad38bc5f0a0ba41db1887a9047019b780c02d1d19336b408f3f22c69e793d8a9986f7e6c04eaf6036f43bfc469e45cd4f3207b9f2d14a0e42d9ddff10545ab7f7faf25c45d0284ee463770ebec6ec42d30ba05c76a0816c83b065a6b8598e23ebca5ce9a543a3dc2eddf072d22642f516eb18928d288f4e30d88dff61c6bdab703cbfb422c82358555c5e6f2587d74d93bdf0a5510b4d999b33480da12b7c3b1b3e4191430f40efbc4bf195116de2172e33f28a3a7c1b98d35e80dc533e79b72da269efc103cebe8e38b1bb5a34079d10f0a9cb49412106cbfa1bd5842c38fbaf0b2f287669e8ed951f9b7e6472f6fe945686bcc7e200730794b8a49d14fd0796021077954b0a6bcc52f36201981cb50f1643e280e64a146496f81611c4b4fcca130baaf402a61cad015b958744ba22e00a5719d34ccf82867096ccf4eb18ebff5663c64649ae839820b1b4fe585a0f216dd635826bf65b7d6a9a97a979e39b0b252b4bf277e5a52b66e147062ff348ff3708a0bcb47c18142aea215d3770c99b610ad017ad8c571b18c9bb26bb4b85c6b5ff209642e6649818ee7ed2976625db89d89a81b3d195b60165c832c63ddaaf26b82d85313f96f3647506012ae69a99006f74e096bae8b6b870ad36c337f21ff9eb89e76d3c2a763460e376844f7f5486b6b1702"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dc9cd74af1fa49e79d8865c569419c9efcf5fe6c48c28e4c26a0e9252baba465","proof":"c272df00c349267926ab55df256c70a81512b8544c3f3f3dae7dcffa22c1cc7170ffe8497ce4897d55b407abe716f0097c57b72d5cf6de1e9e5c18ce39b82a5990407020adc3d389c95faba8ac91951356281f643005f4e11b499818718c054e0eeabc33cb291425bccc7e28d701ee1b24467d30beb87003dc55b8dd21401e341ec0e147d2e04003693dc84ed533bf36e4929766775d3f78af143511d9716800cb14ed28bc42e78d381ebcdcc49ac258c8d88271dfbde78d207402f49fa1a909d2f94814154ab93ae1f6da6caeb69f400878253313d878d9dea2e66683393409e0031d6a3328fdd19b5326ac8d9f245e12c9ed5cece8a91129f8bdf8053ce26308058c84c8824b8d2b2b243cb7250140e917a570e8cd91afc6c21f9057367e19a6459ddd4641cb83d12952cff0e0f007dc65ab7ab3d60a57680843d0f6f67312b47d3216ed4225a350d05a722e131e393fadc9f622535d23122d7358e57a591a929ff2491ee9be5c67a55c96a3945fbcee3dc59c7fdcb0c42de1c9fb2975bd48fe59741f34c721e19e40690b086eeffaf1d340b0756ee9d0ed7a335de9388f1a18f6ce5c4edd0bc65bb66f0cec775c9699c5d1e5abc59a3f28df635790af8d18785ade5395f1ebbac8926693333e93616359e1604c6cf08053c9401f76d21f31ca13d474fe656e5165ecba19479664f68bcc349cfc6462ddd4b34909ec5d7b795e448be4ee5252815b9d7b292263bee81a67325d9bbf4e9aa152c0ae7a4c8402d6bd998fd3d8a178bd81cfda24c2dd5eac3b045601e6ada95203ba707574ea04a2eee93d35b03dc476da38a8e7df03c1daf01d04ac5035d4fac5c19e2886c23e0f9ecfaaf826b6147d6b956bc97aca172485823165092c6b859021d38dd3f3086fac73e3821f0391814e1ed48899894cf7d56416889cdbaa31a9b26bbc25b305"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e689f74724c8179289ba15e91e8546159e82c1afa2f0fdf665d9e89ec9a0db5f","proof":"98ce32885565d0d1ea80b233b4855a9a9599ae70f4374e4efa2f498815e9ff33a2987425bb0cb5fcb3d9081617cd24e580b9e88c0577b900f87851aa1416e218e415a53bf7048a5c628479595989ae1df34ed89b2bdcb01c4c4ccabd8f5bb06aea4a3692e3d143b3ffcac51b6557ef132ee65cb917b3317ab08d394d0b8b0d001990ffa6e874c350a42348c4ee4a2a9c872e4071d06a87f7eda2b2c149b7070311a94f72a28c88ea0d8e0cad96127183ddd5de8e5240f876cf0c741e3d923e0a68dda42a9eadd87c408b4f573cd163fb43d7b63ae7f8eade739c5de5b026f109dee1c1760400c6b59be517a07c1a9810aa5a1a1db9c4703804f23ee4536b010f2ecebba569697ecfe0976290cebf469bdf6bc3885b9ad1f1c1a97b9c595b517bc858799e4a514a8725fc7f6cc2a0bf5220c389d5bc30a17233ca48b1299dca39704fa34de28f492847ede2071d4e9e08c8f9ba02884a5569f7d53d5806687a3e3a6aaa8d9af0b0b93fefa49c9814f106255a8b1562ffb597802d51ab8b2b3657822796b50be15a7e0149ca92004fdaf40902a034894fa66fb3213a966c78f32fa80f4761df59136f40ff00bb3f4055358a14b7afc4c93b42136f10578692122ca650ebd2f97a49ee46d3d1fb76504268bc10f45f8da960dea6a9603bc702ef20b6d52605b6126c4f3dca231dff994334f3ac2f7f4c2919a532ceba9bc8c51e620240dce8f2418e850c71f349ef4241a037a4f543779cc797e5c050b7a58ea55b6cc896b9f8ae0f27e9cf437342be1e7d0af487db06433e8d9a012caaaf4a5b12c69684fdecefd8d6ee4c78e54fddf6dd7ee97c989cff137bd82ad9eb52e26a57d1e84f70d27983c8a954f1820bffdd71d731e56c6353f1ec8cc186d6d4131b09f744ec6c725a8fb1191ce69fa257e689fd16205492cf55e7a9524861278e4004"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"785a978a9ebfc6ec265a620af2b9f193500c1c42d15a8366fc763bfe9f2b2022","proof":"02e9ba9a9042444b8dc4a4286972880d3cb394289b0462cd6466140d8e4dea1bc64f81187a84621672d32edb2ef0789f967534ec0e8917159f6867e8b1d0e2459ef08640fd7276fd36b11496d6528aea1df51caa8b4de215a6ac4086aeef9234f6b1df4d39293d99a1557d2993dda38b9f49e00681c6ebbe372963f73268dc6c4a9f2861124c236359bcb6f7577b6975c5981e3413c416a2f54d26f964e27c097c1a19049bfeb535b1a495b5b71b44f9407304fa3402f86c4ff342c05d464e01a6c9f779df22199d68a9d2a5fc1d8d4fbe0947012226d5c35b5111552e3bda0a58365649b5f13d2f3c5cd57b6da44e272a8571e4efa77d84748f73fae00c27707211b9630eecfc2df9d9b22f52e1a01c1bc5e370d0dba68b4083f0b14f11a772b8188cfca515ec3d88a060a5822ed14c9d93f16b68de2d0948ff78c7f5c2446876d6a906bda6977453257becdd1e1e80f0600a9ae278f98af7f029198050fb387af9a46383c2cdf482868bd4e92aadc27c11e2423602552de5f7e3671b815043024c68d320c306bd934edf74b1e006a610a67221ea50ce1bbd51b6ee2d02bf4472028659c7a5add75c7fe7718c9f5838e7e8855cf09458874ac6a5d8ef616a586a2b9f5c00d4e11e2e2fcdcd3f3c3136aad7abb1a614cb44dea2cd8088356457622a68f7124f375f6e72376a1f32ae4195b5e493de1f567c5749a1fcd29b126936da925a5f1eccf159148eecee785646755c0ca3fbf6fa504f48815eaca08e24aaeb747f5cd73222c55dc00d756cadcea9654058d526b324611bcc3228fd681d3a0a2fe734410241e5b517fca4a513496b1d01cde96a1c00b4696d866f589836e635e496846796358d552b812c5c8139a03cd6e2f722a0a91844970647f7b30ecd1433d96316a15e2ab997030d293146017cfec2896f0995f398ba8eaf571e05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dec552fb72a1f779b97eff30e86c67bc5b29ed1aef2cb89780f1779e29f02a0e","proof":"d48489b7174db7b6bcb9a117480eb772278fe05200c6a90d06540c8652a0d8746852a12d8043414e2314c048b6ed26ab146dea5f720faafe92745410b22e370c520a7e2a9ceb4bc3ace18d96ad6f0dfa55a2bda5f8382a82dc57d1720554e85a383b0f5482b37edfce25853145bf26a842482a0c7ca90808e5f93779c783216fd7d37ede82ba1bbac09982d7dc8b41072039e2d1fe2795655ebcea5375b03f09e1b1e0abe86def0830483ac41e88c5860a8910eb50ea899966ff408aef11540009fc2b44ac1567e690734fecc6ede8036c081987cddc0ff3fe7021821a135e06e084be9333b5ac4a5c2eda29124ea4dcfd83329ab1124447d4ebbedf1c70864830a9bde3c9cf528653fd18312609fc41687f76a1745b2dcff7db84939f638a1914833df761eb9e555c6524608ed27388e18bde0e39ac52a5dc0d7bd2fe5a0d394efb9643035c727f65a1e9ac6656c4c0973938617c3f781fd1543ddda29f4a6cc489579fada7f0533e197aa91327ebfcf4d53f9d61c2a4de9bada5e8c50f087544d36bd3acaf5f0cd1996d2f07c53297bc011f5feb40829f18cbeca0936a3b12c02997735ab5a152e0413ce671f3a1244b77d78a4475699399413676a9eb4e7434dc3371c558e291ba06f3c40b734389178804efa50c6f772cf02ec584837d33d82344edf9af828b8802882bfc0fc9a7fb07c9e7fd117d97536e3b8aaa7c85545c78c8e8474284157310f0fb1fb7298beff5568305104fa5c2e595b92413aa3b7803c82246a56e33a15223cb648c79c1c1182b6bf805815436db6b9450ac987bb0bcd7e0a46a5e5525d2a535ace20f25e9fcfac6ceebd6c5213a420c4666fc10640c461db351e513a8fb070ac853b2efceda97a947677fd0579cb5fff4792e0a19b0be246a75becb6620daa6ef9c2e497b3a037e03cf8c1b653f8503db02090c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d44c766b6f4f4aa1dada7d376dd1bef93a22c691436f73071cdb058f3045a005","proof":"845e360c2adb318192500cb0277d40ca488103d2084ae494adcfc115c1cc6b727af292245dc27b7f790ed413d0241feb1f6d4f9387d98646f43701a18d34fe203472133d0dc67b2fe7a83a9553487b1443c80def8e1d27abe77269cf3b8fea5c06ed35cf60bdfb2e90faa77be357fe7dbecf86b4b9ac7055f9871217410ba14ad93c894d7c4755bfdddf396edcacfbe3fe1f684417436c6effd851f2565ca30dd73113a83691600e29ad50084a9179f71e3c3b64d9c637692dd929bc73a8e40d086701aeb3b49490f5bac6220744c996d772f6d52676b7e4422aa3fb66a38f09842917cc51a92f4dea4813fdf8ff76c1108ed7c6000acd8cf56f347d5c338157cc9ce51e3840377cbf0011610facc91560b44918077b211d5d50d59144629037583d8e7cdb9386b4dbbd2da460f8afda7df772d98934724c0cce3128f2d0af4c5299e67041d35d151045c83bbc344186c9d1dc37577171323d3b409a3f372f1f1e0f1c2bb665c071888169d20aa193f092dd073aa3ccb8c9b72ccedf5b3cb46280731af3284ff8232478ef6398479bba47fcf5b782c18109934db05ad5e76424ce2c13703c61c3fdb2b2260daca7cee57a5e34731662197657522e344e2a8416a8d2f597649a31b9516119bfeae80cc224ffb7676d9b1b9e3997bdc814a6d44d3cd435930877d0022b9307332a06959e23500a9959ef8f999cf51bac066af9499ae48e80edbc6a1612c8de12b47ab97f84e274524d9f9960698b4ce914bf1259d46b5b369003f5a60740e7aa12a577b6cd0128da2c1cd31feb067bd713b655493eab2705c0cd5754ec7384c9dcdabca98c7cc1f2d3470189040c873f24091707a56c7f42e5f8e56b2940bcd33b5f803e2055f36a47654afdb095e5cef3468d06f620f56fbf8cddc2b5da88bc63324c864901d1c151abdc9a2bebf8847f07e705"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ee6eec3f39e30fca37bf4746e951a669e1b446d25e992ec9e687eb791973517d","proof":"d84476edbaa3ee96632ec8ec3a457183fcbbae63db3cdff364fb765d89286d3a64532277344065bd36332f5e8aedfa23a32c91e7110436a1220dfe35fe5322051a3d46157833627709f3ce92c86c90ede1f554c616d567c3131b392503c7342d9a819c07391e3ee7e5b5d469555fd2bdd579b2f2d1f656aecd6023bbc298c031820aaf0a4eb37d65beb1cca10e0d7111c9c211568a92c68b04368d947e11830221976186a69ced417560b5308b80388ca625f645bfa248486481cca9cbd2680eccbd00c03adb9c9d33a2214c4b4ec545656eafa9aa0e6c1bd30496fcbd959105e27d48d30e52a06ccdf689f6b682701ff0102b3fd358512e5ddb5d6b55ec1643a400033b181d87bf0d3d2fa35a24eb3cf2cc4b21da96491c56d9c953963df34e1aff37102c883f6e8d29e354cd9b05431df0f145d37a680bc6f49922b1d793088c59422046819bc1b19adf039c322e1d9631edf8eab3cd74500819508e7c953a7ce493c551681bebd571d90441a20a8b776771d1cee2264ec4a80d7699e45240c6601ef9cae50d01cfc722d26947b22e1b4574eedcfa62025586ce19eff17b64c2a6ed128b6e3910c0ba8f5948bff953543cbd95ed3ba02ee8f19b6a53d3b775b2b97c483cf795d4a3cbd602434df7de706844887885b61c3f33e64cc154ff661e25fcb2aa324735ea9526c7c94044973cf342fd1395fd8c1bb63045f0f6dd7d042571d631cee98df02efef803503128dac7e89ba3453934e458a55a1a01a831dc30e6cbaedd64a2953cc51bb8535e4a079d7c19f6f886b1694909a863e24952c4a479e4f8f7a9be0b37b9d4e89ade7625ac50cd3c8d8dc0ff4f2a54eee8921d22bc3bf579f2fce4c830d6e0eef1027634f5ae90504904647fe1f50f4f716d005a299b686d2e18ff7d3d469e7ca71865b2f11186f3acb44084c3ad23d0654104"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c04e979618a950078174daea54a26f50d85fa89320976f3dc191c3f77d3f2864","proof":"6c8d4ab53ad70b682226983d0a69f0d16c1cb58ff5618299b3062331fbd6022304118e7fb261da7766ca700eeb8a616f55a1c78ab244dc4917742759e7d60073d89c27b0213edf5537c28762507e0ddb3241940158cd54d8020122d89506f05a9cd0ecb678b46b5cf77e0177ffc5d9b083ff6ff1da6f5d2ca286f597336c2b50b4d8d9b34a541614c0b367e8e8ecf697c2e41b6ce5be68bcf7af31702e74e4024a42153f42a0b4a9b86474061e9dd5d14409a2cbca4e4602fca9f3b77eec9e0b22d423aeacaa2932c49f57921e019b2ba9e4ddb869ad2f03958d1106c61e33048e223a31ba8e68e4c37d6e5ea2315a3e6aea2136ba8e32a738764e4ea282693326a31152846e213c9ddb20943fe8cb8da1d904b72f6fd6b20a373850d19d176a0a72691f2e586aca91def7209724bcd4a130d5d3db1ea8f0c3a58d3ba7f00e31c45216f09afe7667580264f6e5c8faee7fd2fb18ee67c020202405182702196ed42a3df21db45d8f47b245848a26c4af5ab7e56f6e8cd8b9ea4eca5b7ceb343a6c9ddaf01c1caf1ec6854ae2bf31179a1f3e0fd98ba820ba75ecb2e06d53de3e1a55ff6e8a7d4e3342a9c2bbea18c8e202041a6becbdfcb94ff12a0d7a4b251de2b6e12270bb48978d7ba90f28ab2089ba55e0c6c2e650b67ad8616792ada548f04db511e419094936d0cfb88f6cd6e2cec8ba92881ec1c5563d9cad7242417b92a7d43c9ae3bcfe264f8ba13239d3ad30bfdd5378afb2c912e75fd02b655647a66e1d9372b7f3f1fdc7ed3db6e33ff8f87661fbb89e78fd56d999c6721ef71730bf4d89c2b5604488265665b848d1c135b11581e7edd696c3db1e0c285e60074f7db75f5e3431a718ee854fa3eef0bce8d6e311a336466381f7620360323801f16261b931867d7cd6e42f204b4dac4c933d497a7e4bc980e3837db6db626206"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3c1c189febc0e96d7cc8fffe413f6e0542a807ccfb501c41b1d56e1cd964b557","proof":"0c8a6fdb3aad6c0dba0b725d62c6718bf6f31c4141a36551e6e79c1565ba54714604037b50a0a12ae1e195157bbc79608ef9fe8204dd10666cacbabb37330d3fd867407c5abcd613c168160b0a7ac6b67822137d84784315de36da744a7b974ce8199a8d1f0e696a07444c5bc84b1de1d910856ea54020e9af5bd1c55036802ab9285a1545cc7e9a9573c4f0b312958a804e9c75d5d337f287e435d3ec63e30d7134bc7c9c3e9cf92122f505ca38d63d29b144227f27552e3960467bcdc69b0e6c5a4a8b0cbbeb9c2fce30ca96719c819867b8ad1c382f5f10ea8f7223f9090d364ca47bebfbec269c5957ee8184df6b62dcbb9ddcfdccfb5edf059ccba8814c147ab0cd82d3ab9302a0150b0ec3a8201bd9354dae0c525964de95b00196ca38aeb7a3e539355eadac0e95e076f86332947b5986f580a564b6cc6010d7e2b007748ea94ccea7cf1e786240b545ba7b5ab18ec543ea3f9166383da7c3d9bbbb5260282ba6cd0b00ee26f0ac046d42c38fde98e4ecf9c4e4784fea969675cd37786e6367eb93da3cf375917f25b8a41e75098586737f6209870c566983b83fa569382d13797a7b81eabdbbaa2cc7ec9a4d40adacf2d3a544573194fd92d66a194bf61ef24c86e0dcb0d748889c69796573c315553197782d5f698b1a2609a48d422a31c58809acc34971b61766b0bf59fec82996996ae127f9cd3ddbd7746e326bd691e0377ac088dc081abf152e91a0cb953d46f75fef09458904e06c62b3af3f8e7131fe943e61b5a524acd4ec718145892daca4a786c3eca40cd4768f0fb944488e7b95cb389601e48bdc32b8abe60a5ecb47a7f913f022f19b71203b28a05d63aa9dd19310b1e8001bd8a5b46adc053579e4475216a1eb6c85457b8df09e0dd3228ed12b1488497ccbd05c43a403114b3cb7237ed381b02ac7fbb5123a5803"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d2ffca5392bc9fcce944915ddc6d88244332d2fe1b0e6d7fac4109a3f56e8640","proof":"188e1eb5747a0676f5cd10337ed3473c17ba4d3be9786c258e44ab14f079c011445b3d2e1ad440a0efdec3a82c722fd8f9ff065f3043a87cd3b8f153ebc2b232528c58c3419a38c4084f697952b4f847fdb71429d82a5f98c2cdd192cf68697216e8c00db440035d15d042f2407c860a127bfe9ad0f49bca6a97e389e99fac3e5785eabbd1caa9469f63c1bcc1a1cea7a7ec143dcb4d4ea6932dba0ba7cebf02c0c8e1eb4f1376401933bf471646404e32c3f267e4ad856ae4f03c718d90c40737e934d0395124c302453f29a764361c46a15c84b1715c15c9b603d242a27805f848d50986f19b381b663f6db5139936150c5ac9e8a35ae5575859c0232edc0738ca5a32d22de11e6a68e15009dc197f2d7487d4997dfe11bfe161c73758c23128c656d6705b436ef1e154f072575349ff47d97e44e030dec48f013330ff08309e44ddbb7e5ab5be550a4fecb6d28912171c9b29f80e195bb94678e1240d5c5aca11eaeedea671af11d8b78f4dec34263375d4e5154cbf2d656122fb84b3777448b917b4761665b8d6b0fa0754e2937492ccda200e1fa0921c9867927d8ef930b4d3a3de6bc8d79f014c3f3fc8582a7b9d8d6dc8e2b7ad9b4085836afc982804f859cd75ba50157b101b7c4525846cc3f47f4c3b2b1e60dd95ff1f33c742ff2506ba40b86911a63b3e7a997e08fee166930c2f4f6be8778b08b574c67ffa5054ea903b95e408fe1a9d4059cf6428d305611f1582bbd8693159418ea201fb1709d025fdbd9fcdc8c234fa445617d680dfdb1c4647368651f213cd01854210f517a82b5da6b0a1bd1753d8b574f62c6c838944bfb2d56d52823e2ace7218cfa577d5bb7755dfee9e51655ee3f22adf9cda714c4c1f9c8856387cafde2b7771f208ece728b101208ff35eb5e8d14956b9578cb984af383eac03de13cf391c1d3703"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9e7142ac91ca487edc86079fc4db3ac7d400e7c9315d34d133bf60e3f5b24074","proof":"942ee03176a8bacf8dd5db6715a55b5e157ff26db4a9daa15d57a0c530b6b00f8eaf4579e99051d681175a3a96d37ebb3e7b1c5429b0a5489513b47e0ae49b5f725a4669a9edfb574b4e092cc4ee8f9a2e1ee5934ae2574221e9ee8120c5dc327805d7664ce7e149452ef11930bebf60ac9e33a6e5acb705921afe7b4d022e155e4bae47c882259697eaf3649748237145a17663c475e3c4dfa3774cc8e7b7029763311189245540059ffcd152998c1f1310b1f35e293c52f304cf7d0e007d0a3cbb4d8c21b5f1aad4e50c739ca6e64c2357c7fd28862c64d4701f0275676e03805cc46aaedc50fd52f69f9b42b1193d73a0b6227ebb17c626b9a70b77520f2654251c05037d754388404e6e4a97f62b7aad5dbe3963988d1087668642eb265ec26f53c6d1b2d01d45c1a1c312fb01fe6c9c6434d84d18a1ed263c092004f301b692ff2dc37b3f7c7020b32249f8cb0f4e5ce4c708c6ee5ac3e32bd046aaba34ec93320ec3beaf457431865e2712beb4801fba0707fd5ab04a5705750553d1381a5d7ae54f26dad7d9a57777caa994d7393f78dcc89a7f14bbc91b9c7926280df0d957d2de1a2f95f9cb3fd496e4f1dcaaf281c02b7915e37d8449e0bbb0e55af68dfc35812ea9ee9bcb762a922b1a38fbc27df3635e55a111c6d57e3ebfe138a0b9fe9a081fbb2eb84b3b40f5245cdf21794add91846531aa18b6d3925c4421e00ba909b41835036234a74a64c2afa9a8eca9584272d5534c7d8d2345f4de775e03d73ac527726029242421813be5999ac3c76e792eb943a823854090b660477e1f6f5135798bfd20934ff7cd648f145855dfeee0939e020d51db10661bc024dcba84e9d02705c0c78c362bde754ae4a758400003512aa9d2ca9dc25e86ec0564878d07db1e03bba9acc69b5c3b0a7aaf3bef232228e2a4585b2e581400d505"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a687dfc9c965f5928499e63ec3e0fef4cbd95ffd7ee83ee37224f7d374631741","proof":"a04b73d73b8093953f9ae13d06a5ca92d55c02444ac448049ea9486a1450c273a07ecec3b143c7e4bce725a4cba90c335fa198138d146e3f860c888b45595a6c4acdc13117cde53e8e11106d75450d0296a9f165605e12a6f4065e527dede4093a9f2dd6b4b570cb200753f8fc1df99c79ed264ee2cfb6d33851670039e37c2bac52b8fd3abec23d52ea5f86623199ae08ee684931c0ef0fa28ab0a2cb0fd902b2a1cdbadda8e6a46807cda36d08e7e5d635b5ae0f58d444550d1ba069ecfe031339ee7306464fce393136d26b6cd56d969568cd567071883fb0ae667dea140168c47dd0b80ba89780b5dd995f11e95de9e11dd7968a466bb2673e9735ec80089ad3f196129955481412581ae99dd508b456592550ee1f7ed3803f7f747a94158e91352d58ad86dc28d440eae91633dd1e96d56fa030787adea487e6765e791ec0d78af352e8120732771525b4c7a8fb211af57190e34f67dee21e53e11b5242d08d2f06ec49046b47c7761382e7f03b6ff8de363937c8a012eed11d307fcf57688438fcd63925192a9eab015a7985ef15ce6ea3cfb4d5046e8228720af90d4732c77d1a92bcb20f5924acda7219c558580859c8fecabb45d73012127de8b738742bb5b8c24329af8f8f73148116cce29169d5d3196e467cee12f63bb3d86d0e48924901e1279933a27c8751aa6abaf2c1f497a8f7a6053bc250d09fd6b84e2bd4122dc2de8d56f18842aff8b3867209a7b115b483197b146fa2815534d7c11512da96ef25bbd72899ede252ea5f842e0251bed9b2188784b1026991ab533640b6b30de96730306341b9db7bc3c9a0cbcc63620862c7cf95fb8a94771cde4f435eb3885002a5fab951bfe95c8b7464a55825a778a2805568a77b90ab9dd1d90179e1cb245e07a2f5c34e7bb2906359a2e68661e72676c5bbec1942819fb24209"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"44b087ac131e4f96d660d965d75ed8b1dc800f47c8a24b05c3c56873409b8d61","proof":"9a3555d49f558988c9bf2cd88c0e3f18d605f8d46c0ee45e8adb0ad9b9ca833c38ce66053694ede02619b46798c4fd1e5b842066362cdbe4ad41eb1c033a3e10aac9205fc663702ae6b1f5cff9f79053f10fd38100687378bdde52a124f9337c1ab467337f6187effdd094294379eb89ae7333afd4d20863f059658f2d2af5202185f27ca74192e788224c3cabdec942ba9de92d09b5c543507d018d212577078004aff197b747f44ac1bc0a8fe86c74d687733c0ed944c1b2073adca81b3d0f30da5647f598e89fd7f30880322fa084b95ac8d16b77a836768c094ccd9dc90f24f9372f5ee25b04a1ed59399a9c0027678d03c3840265993c85144813a3257c70119ddab87c7a6ff985bd628e6f203fd1707ed58ec804e82024032e62e0d4007693e62c1b668022e047e701fb08f28b5c2d1039c2cd72597bd82c42e7b94f662ac2f9b919cf11e46a04528e6822170de4bef332029706862f82fe1dbecb060fe62fabeaa316d620d6dff36f806923d882b794c22ef4a84220dbafc5943ab50660d9bc4212ef3403073f7cacb88dd11d193e2ad82de7b49616e5d2e42d82b60d2c7d175871527ba672a85bd0746f0eeb5dfd80f79d470dd1beeb6b901c173b5b084f7748070788bde2d12a87271fef3b66a1f174e765c2013441bb81f1f6533c02c8f673cf01b5ee98bcefed7247fcebca8b235563ebb8fec0d06f322094bf141a76d8cad531b0090fefe7384a7a92a835654101c6643d9558b5a6788634bd41342870cb6ddef1b3217a8c84d2eb969b6a035fc411f454e0372220880fed441d32c288d118177dca4505c0b245b39c0ee8f1e7936c66794439d6eb5fa219855a9f51b1dc2b7dcda804518f574ef6d35979777cc3fbc5e44e042587accec60a014b3a25b056072de2c607ed56ba599d36125ea097bbd8f7615a097ab8271e080f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b8299cc5f9b38ac3c27504f23ca4b9eb3ee57e1a536372e7c86f01c3ff5e7a5c","proof":"8481b3160f74f9bea4aae5fe2c6f837b635f869d01fb3f23653cf0469b31ee0534450791ffc18428aac0653831c32ec091dc4855c4d7e9d1bb111a53f4bd135758e185d33ecf87b8070dbc17893d90ed169a855b9650f71354632a297c09290d7eaa38edcb98f539bfb1dd2709679f1143555d729081011dc5a1947ac931285de35f01b7680cc47ea2633f853283e68c9ac16438d008497beda0e1f9b6320b02df8149e27a29fd1fa0f44a6e2c423a62d874e47a33eb8af93b78df2330d9ee04d67e633841a3e3f07ed62af2964d30ddadfc7153d8ff6f04377d10b727af5d0334a4f079e444a7aa716dc20b8ba512b86e223e011f9d5c33c7bd86ba984ac402888023bc80b262a48dc6a0dd9f7863a32b2debc80ae1b5f4ea8d4b082fa9491284ab0c18bf676fb498c786983e6e522f14fdde50d55b7377264bec819a619137f0a720cda0d4f5414c862c47ab2eab0d364ef40d4c7d2795290a1bed95fb576a8edccccac684df07c2fbfdf2145e971dbe37544f50da2295092fb7cc53a433697a1e8cdb943367e97d4f08e79c26d35f90c87d53a0f61372518fb27ffcff621348ca58075e40d344eeecb020eaaeef27b0b1692bd4679ef8d8ba38302f8faa6a5ac5a2b5fe1a6bca1411fe1395e502be9529cae2a0c973097c4a2594a88fff24e21898ba76a07c285c7d02e3288058f4df02f16622290f430c69abf2d0afd24542fbb49ac442af2bdc4d051b508905a005fb533c29077df7bef8dab92dbdb56e508d946a518b358d778930e33bdc6d23dce577e2a549630709a25df2e2f4e17a2820a9e167260ebb22156a6f3b7014e521d82bc1b837f38fcbf96e0f33f9ce7b48db36c833bc21e7c428989ad7b4c4384ee36eae0ca5ffb6d5315d9f7d57d00571c28f557b18fc251d9d335bb363f29682e1603476b3f568fbc78df507410009"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9654eb9a2a3a2b72bd91089b179f19fc66fb9943ef31a5b1cf2aa21c26f5d93b","proof":"64d38772cfc4855e3616cacf2e1c539f35707f36492f59c4fe26eae8878f0f4a9ce98b114f8b1a6e662498eaa50b306c2330c39c53697d915a8706bd2d2b5b70b89717882925d886b0c45b4d4890a0d94815c7752cacc0c83211982a267c99373ead85ff40d0686653ac2ba0c021ac104b59cbc7c4030239d431cbb6f7b47124ab7a66f49b04a33a9ad0cd9ddf20546c571456ad51237197a3aa906989a8080b2dabefdd6f4704cd017628b0fc23741195e37da9ade932a4228d44011810dc046e45d8ab93560dafd7460c93c579fb83d813cef46e71530cd5660bd4421b3301c085ae85d1941045a2a1f3dcfe7015c54fae9d9b845ddb703cc82a1d453716775a6c007cad8c92923aa8efc4f27afe6897807510d0396bb91dbf1272e74a422a00310fe15af865cc8453078dfc04e0b8d43d5c4a558ed8deb907f79a3b31493c969da6ec5368cfe5f410cd5d092f0401b883fac7a1ecd38629bb9c2db92dcc146c9c86d30d77bd59a2063fbba452805bff4e5228157ca3d074dfac4139ddc505ca1ec35729abe8d60b75c9d669ad236538cc264c36ec2aebed507b8de9681d2a5616b804043190f4569e603e7356cfa271399ee70987a29c480424031e12260bd6d1de5d8613267cf44f2dcbf645dc8da952f1890fb8aed90536e221cf310d15d84b4ca8825a1340649161df1fa1d38596a4c9644b00607e727de8ae30f5611232e95d0fcb342586d2fe27adea75789adf7a6687a3830f2ea8def9e4f0ad9f78b6fbc345aebab6e7e1338ba12890d572531e897fecfc01e5f14541ac2b14ad585cbf2819c6bfbc34092f8a95c2470bc541f355f02f1de62f4f8664dc776a67669cf427822d0f3a815cd4f5fecde93203a8ce6477b8d8c83a31289a725fa5ec01d0c0be840053397fc990cda7e6ea357113f9fdbe50dc1d5f6bad3d491d00360f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"08560812850034350f71fa2efa6c372c1f3e1dec7a3a5e0eff41e181b4cc1e79","proof":"4e9db834235d49e44e61fcca600d36ce8b38daf61f6da9ea931914b3160802407643baebc8677e4ac5d667b89606f09b603c68e90f438636dbf827e661256515f8dcb10f4cad37af79022cc9e4a5445858640f56542fe6d1bf3d8a09a6f15f0218de856dd294070a09c7cd3c46f184e208af333b2de28b2d9d25b9602386f30a3debc508216fb6aac8877ea42eb2310437c76b55db51a55d98e088579acb940fa07905b748992dc630310997bb3bae2902943813d38747f97aa60fafa9511208704301ed1779e13cbe47b8047b47e0f3465d8266113d0cd4a336992c7ab4be0d7e82ed7bf66ee1b86457af3452a53459a2d835e74c386c59bb1975ccca65b61c32b2f3f2dcbe09303785c58d87a20659068773f9a068fb0db25161dd508dd344565c0715351ac46f70545ecd038686e7e80607c755fee9f56c457a1079a32666def45a73f94e5203708eb794ed5e85e38107e90685badadda8ffea990fa4c1570efbeb6415f9e3c0b617447114d1bb563401e1d3be8bd2dfc5c1b006cdddf756c877adbf3e7395bb1fb15c756ebaf90446030ce22e4ee00aaa2cd2a764e6b05cfc75c899e1b89de2d485eb288befbe4eaae4f7dcbff38f7dc816ae0bbb189e760a12ee72a0d9e6454d50dced3c0c03d77304cab8740ed5ee7844402cfc100c15708e8cc60f28730e25b3efa7b46fea12b21bfb93666e98219dda372f28915351fcab09a0d8a8fb87179934b8647e078d4fa17d2153e227f486857e2763e27242b42f59d09a99d79ed48e0f07a0f3d7936b601add85159967f51fdd31b2e43037ace8920aca81e60416d52f1654e95904852afaabedf30932e66cc8f63d41a416398dc804fe81e84e88cac630cab27893c4fc690e8da9210cbed0b5d183428803575b0111d95bf41af9280b481d2619aacff9ee3f72f7f73fa6d4ca2e7f6ee301"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bc77482f59691f9e5530707b78376423c4132d8d08a34170311fa93720cb0f38","proof":"8008a18476c6d1c1fea38936f79974b463df40caf16753dae0586d8ddd01ca5198e2dc273533d1bfc8eabc1af21057b2a538fc5239a2a9a49075bdc9dad5292a161695d8e1854d77af7608cb5484028a2bd15b5a93a5048781e82da2d501dd4d52465aef7c2ab89a8416e9c9152c6ec99418d3fb7acb05155a8aac5333a7266ee82fd0ac5d5da0004e4426fdc9acbf2f2029d5719299182fbe355e3baca3ac0174d04087b9a140240bee897257a13f285eae66adce6530ae5dbfd3f846631b07e847f5af2745feefab732e7ac80d192c3f7d7c2ef2643ab72d7655772e0836067488ae6f1b72a3607334254e5bb53a486fc70b83a6a8a09b05bb04c4f0df747cead49b8424e1113aa3c5ac5c7118e8eaa6e2d8038fc54e10d7b70afcec26a351b09597b7b7ec8232e88327fba5d3a768d279c462b439ed367b4e5f885549814f1ae76669f6e7fd9478748aa235745cb4cc1d262dddaef0a16a3639aa64ade15b62021af9d7bd68a6a0f6f8e60a82d7b74eb4c901d84902553dcb27535aab4274428894554af380b5171ae8b51a9b68d0bf39e42735a2275956785e9ee7cab0737c0ab9742b2baa987c789400083c64cc5e8a3fbb5a7675b7fe514384bae6a97bb424ed7191371b4e29a0bcbe9a52af3f9f4b13cda87aebf3a0cdf28c2942c32546dcae8d71f93f029179407c6b65a82787a7394c57d74d6220bc9c0f7cd4536ed0df935216d9615283597447d038553db842fe06057740f778aa0a290f7f487064734ba2a7dc8d4afb93ca25f393a316c1bc4601429b082e0726043dc9319d62fee733e0a65ad85aec64e3a74b0b8d06cb35762040b6c0f94b3637f0564fda0ec3aa0cc146fccaa8b52634f2b681f64570e812d31b88b8902e9c90b7bf539e079113a1c5ac3f7fd0cd4c58fe580b186f08590fd50a7a73a965d1fd995b8dbb09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"96763bf4c4d200f98701e457040338bde6296cfca221255abe98bc0868f44635","proof":"3cc368136a2f9be4c6765c8e0133ebcd0cbe97f25350f8c32d6ed29f46f72d237a6b2385a1cf7f8a2b9f59ce6a9a423bf8af5223b813aa553fb24580723bfd3630bbfef44a0f7ea13da6f13055c64b5ac932fe568b4fc2c1ae198c753d38dd526870a852d42799df0e5be75c8ae3a2ec5428775146f1702fe38a4a030c37e13d61ea2e07c1e05e60a3c44235718c1bb26854c90bffd9832c012d1f6d0dde790202422850aa0cddc1690b9c4f1c9c7e065fb9bd645c6a01b266b78b75283f600e2a42f217a05ab93d37723e966195185c780a34caa4162eb2387ee48414c866072a373e0c90addaf2236832df4bec6721601b8f025c4ecfbc948d9e248cabc147ca20f3edc035cd991b5e02936b923b72394c02e8b49c57c51fb688d50ecdbc00c061a3d81098ba5a01ff7c22ddbcd5ff515b5ceb8066d4f48be589a126efb4413a3a17546fdb89115d8f7446dcf483fdd84637043945e74f62a117eecdd0c11e284b1c36b3069ebc09b0ae2dd17a4ff77d9686f5d28964cd39136fef56ff2e26a059434e051f35062f557daaf18ed094e48874f5a6ee3fe012c5b26f01918e13e841bc6f06484eef19c727d2ff9c1beccc21a2c5634651144a57a7f8a73c7429b6fb0c3b00e04f78e62f1552346a3af220b5ab2e56dd5815b7e4b4d247d1947cc25207471d1872a0d609c869c72db8aea89a7b343cb3d5d1f741da9e0f83040b48885ebcd0adc69aae7f8ad13101004d886f58fda13f58bce92ce786e9aa632686185b12acc352f03fd59fbcd5b8f2f0cc122ae820f69c6875c50ec547fd9972f4e1e0d871882e4f5c259d72fc90f6aae1d31d4647eb257f0efa6e1e05bcfa05f76de177f3a549e58186ff1feab1cf5b2bff308d2cb1e9e2a6650c16e75f1b0b270d9d8cc2d064f4c30f8d2307280875d1076044575a16953f35687ea68d4a08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"62ce0030664a73f9db15f33fdd58ebf11e9efc93174593c2b29f7806d6fba969","proof":"e829d1dee5eaa081c405110d334307698ff29476c0779e90a904140854edc212f249db4073b39875e7fb2e6764f4f8897637740ab9fc5199d0e9ff3f7507d12afe081516942dd88dfe59105b89b22be53daf89c8435dc2d2a00610965210265a42a7101e0feb4b667d5e8c7444a4edc8ba568d3a35fce90eb3b2de2624a054037bfd077a27a01787fe2e5e91439d2352b3d132581c9ea75baeb7f35f80e8a30e0c19564cb989501a293369a2c1a13c9d73a12d1f3790bc44d72a377160373d00378839f603169e332e6c6cd4f59d12e45ec9c4c889ad351a4aa6db42442afb0b6a87a1384c843c2d2dac26cf450cdc44fc14826e1d86d8f1fe799a57096c5d2740ae724672d0546d0b8091e13771fe3d7d2b5ea61c7ffbf644e2fee726e12416721a49354dbd7b530eeb6d7e6fbd64b382c03197b4da40d501ff927856d8d017d8064b18b91c4780d2608a8cbf5a7b332a6bb0471ac3093e13509ec22da88a3dd609c5468bb41bd2f68a078f348d4281186de04c3588aab912b871ee42099e489c0f0fdba0bb27c23b7372b9b73e31d7974d386ab160f3d2361e0da0eaa662091cdea36470ec17c07885b39d6020e17783eb92f500781cc02cfc8c9887227d004227844d92f6b8adc9434081bfebfc8665744c65084fc085208a4f8e7c065b53ec110b257c13ed7fd2b61e30567dc7da729d315d31c91a060899a346075d6b37321c7ad704afda6d9db05de34ea7ebfce74063b7e1046ee99dfe918f5e9c9918eecba19adaee92d8ee935edd86d8d6b75979ed457173b72fe6f543c7e8fae015f8880c4f800ea290a9afb832e790cf6a57fd97ec0f4951510574fa6ef242c04ec1711eab19281b58a38d5fd3639a01cafe944b288afd7f7f222a90c71ce5ca0f9ba914894b53afd2ca77634248db7c998fbc51611d458cb4bd50b6cf6e64c105"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"32d03a461ec06744408eeedca05863eff2ccc71afdc273220dd65fa9ce119060","proof":"8a966b34bee323180803bafdb908cd886550fa7c2e14d36f4e20501a821eb5358e0ebd37d854604fe44199a52de80054a01821544632c4cac26a087f5344d8649a63debde39a196b557579a3800a658a2b4781de307b9ac47b264716b1195d47c265e9d96a4388f517db3e8a119f9164d1b8853ed8de8eb911ac05d387a9fb236fb35e1087691d559510c0fc8e1d98c7f6e691a43479be8bb6e4aec69be00504cf222ef20edb1e2ef4a4b6995294558983589720de83be4452f5dfc29c7edd0aa1f1bd3ded23d9aad42cf5194901d3003f555103972ef96bf037e9ee62e56703003e275fd764791d7bb3d3e6857c28119dcd0c9b75335b1a94c79d8fb214a905eeec5db96fa027e0dee368dfcb8a214b61e14456b830714e2898485f4b58cd145e1e792b3572416dc24d42f32dee87164250023d3bd58e8b37894c15131d85539a1f9bb585b0026710a67260f98b37a4f52347e20caa3cc29c527e38816a655914378eb05eeaf530eceb53d5bd9c7b1a2681cf4d034ccefade17309e3d924751eca9027ca639ec50f6212f5d2c825cf17bc6d4e0013cabfbb21124f9e321b55aeea56ffae59f24fc428ac4a4218e3a05ea64e77bd8644ae53379f916708f2d3dc2000673f46286d9357c7c7fd62aba82cee83161af382a0f1c9a999818dd2e31dafab3e7103c020879e4980427961fb8e24c5c593a095b106834a7cdf6f76a39d000b33c05e1474b79f7c71ba3b20f234761106b84160a59c4e37ea92e4e63147cc576d4c697e9947d17a15e8974df4b312a65e0520b410e59cf50271c09bc5cc621ea1306acbee9f581492f6821541408e87d191e540e1c3b69bf4677566049e84f506512342554b68a33d389590cda20737b7e061d9bcc394e20a71f86320c7238c6d37e02f83975a13facf0370be0e11a1e3587ff16bbe619a7cb23604d0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cabf5575d5052a9f17c647d2be74b74d69356a1978d8a80e4c48971f62e5893a","proof":"b21fe4e8506ed44dca6fc670f192ace2de0a643a88d8840b6fecf2701169d01da07fc8e13dedcd9391b855eb4484aa9232e0a9e10741c42147bba76904897a04228f42cff4c6c00d524eb236f98b923ce8203f02065780a9cff0af0ab349df4faec86d061a3691d64a08e2a7abe61cf611838ff490538213f785fa3a429714218bd1392bf71a2c627cd3aa1f8b84500c9b3640e40478435bb0f44db37b629f02330c69ec9f6bd31d23c6d93a89ed914b82a50eae99e1329deaa1371c362a3006552d6d8cf8d9eaab74955615ad4bb7fa26d9033098bba09c07f1a131f16e5a0336dbaa40f7a45a8163fad51e8bb6a03f690a6e512cc0fa017f90674e8f01707bb2b57b04fc1ae111e0c79a6a022cc4a29feb488c5b65bfa5067945ea93be067332b1a572ec12eaf4a0dbc78154ffcdcce302640fe312c4e7d0e32176cb137e5d307a3f84febcd8380c0b218aba1720f9077ae932f70912ede5a36cf56a92950734615340b81c112707e95aeedb88e493624849cd7a250fe83728d3f3ad4c8a1bb2c21b15541d714afb2a72aa77a4467078bd2fde5a9556084a6afe5e6395541588d86683d6d6932d9bc543c0463bc2fadb18b7042eca8c655bc2b111ba6587597c7a0eed073c0be71d4f0bfddba8b6b864c0d465627d7e45c082d43b12d1ec0fac6f9c08848a489f194ead3ffc67fca9b61b3b79b797a7c42f20ad095e45e80ec6b618837cd296aede8b793a6d850666b10e9f4e10f2bfca0e101e8213c313233af8863acb11f745d536a733466474a94087d35788e50daebb3b26d2ec10364c5019a95e53a16d0065bef440b220a3edd416172bea10bec0f186e91afc3d7a43acfb382643c15d69b361351b02430df38e12b7db204655a8a18d8ec842945b00a694971a21527545806a532c040a00d20fba924afa1bf2ffb9a303f387c3bd0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4ccbc95bcc772d1c0284c972a0e00d0f807237eb25871f1cf2df2f924e372255","proof":"7a0bd40ea3a0f5c4938b1f220fd5abf46b2c0f4b9a319edfa247ffcad02bf329007d9c3d06f9d3b5533af5aed8e10eb73bca1bfef5ddd57707344769dd23067c36b7cd980debde5fc78840b4df30edf288d845cfb8eeb9c43e66c52709f058028c78476e670c54770369fceb1eaaf26a8c905d16e38e01badd12162fa41c4865aa786cc0d595cc22e1adbc48c01f778af084ceb55775669745eb332a7978b30b6a2f05e3368a3c0e4e05a5ef0dd513d9d5be2950f96f41fb9050793d6f531a018a23a99485d73a9272f03585d3b711688e68ed9d800bf2e28a4db044f50d5200ac6acc090447f90af386bf8faaaa8ff65ddb9ddb186460f4061625a1daf95b1892abd0b14bb582cd14024c29fb0c8fcc090717183831d32a960c6b84a61d8b164853c04f9be630047e6358b9efbe7e795af9ed1c3ee4608c986e587a9bed4b3ce299f4aeb9d623d8156d5c6c2ac47485b4b3fcf4ab3985b21d75e22eca3f2610d00803bb55e5916e39076e9a19f92936ff9abbf263991413a9472aa06f0978753455f4188bb7daac63391bfe0ef6ff7cf498907586eb19ae48890f922114751e6e257e86b1918bd6ad11e706b26e3c74a96cd475d8df9bc50853bbb5e411641aa0533143f278c6c21d91ebf5ba7ebfacf569586ee7639f077c70fb61d1600c08f687d6e59a180b6faef23d1d03056bbf16d81d7c04b25ba3f465ef745a6d731a0eeaa593bed74d607794a6b01e0b9ddc710b5f4dca37a5b0fafed50af416b93890b616b1141f94a270806bcd8252b2b8e53d482936746466d3cdb541f1d2665c1cc403cb1805ad4f652158a154530e5eb0e82d3985da8f59835c060fd6aa345e9720392a04e99ec90172b0e93c970adea482a271d43d72b30f50189712162e0c22cadd81f1bd294667088e81c054617c450564a747485160f5d32f0b0e1e1001"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a842d503576afb6c006d266da79341ccf5a231a2ec29a32d12c9fec6e151a85f","proof":"0e6bd3749a98ba673b22d9b518ec4dc404940065c137bca5ea08c573caa2fb36e8807ffc2a61730855260f664633d2418a84bd7209218d2bc6b2f32ac9fcff638433da3ea4d903153d71f0f96971e7100300b1053b7bf90105c7036cf8f33c14425a6e1af8a56a36d54a663d77330b0cdc9231466e9e27ef83af595e48d03d249c3c0b8b2be96b5fb9d1dcd5150106f8c4418920aef5e6cfcc248e06b42ca90d3c0a62ae0a337fb4aee7681d55aa4443a1d7d57431c59e43b7eadcbf0523f50a6499d01a953de2c09dfaaf4ed8fbf17f9a12dc169ec0850992e990d57c1a1d0a72d05560a1a07a6d874615d5a5ebaf720fc3918ea739ecc31cf145d561e27a4a585bd310bb65894f69b08f133837240e9322f6ac393214acc7d581b40008e218aac7d92d450eb4abd3c121decdffe75a4c6b8de53f9f9f3009979d1556adb41b3a895ecbeb7db4782505e92dc78443fc6d6444bc04024768bdac9722f821835dc439a0ac7d65b7f56b23ecf073611901ebc199ff34bf7d6d00e1c9f890bbd1285a110757d6c808b5f1deb65795cacec6b6dfe3a3f243471ca0b90869f8c1631df4eb28006b4e67b56344a10965a792c1eb20ca45415d853af347b6eb7a50813d9eed82b16e531c16aaf100b7834897865f52a045228a77a566b10c79adda755fbee757839ce3a75777bc7a1be978a61198d0284415ce197eb96613f881735e4c988c9a12111e709ba28e561cd3d23942e1f32d9740154753b687992a54a0cc7c2ca6e16c18f83a757558a10ce69260939aeab358f5e434b33bd64ae9d7103c115cc2ebbceb0ff2b0522a6ab3e96e88643589f34e8afce3058bba14708468971936e110314b596a8a15a20c0f9f4214170e9e26e2dc16f7bceadc9f29de640007f913f36b9bca00e1b3480bb05f3e171a9da2c8bb798de3abb66ca9f1a2c3f10f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1884a6f9de5f3c59dc01f8eb1b15e9fc7341b51cf518b9c83289e0e43280461c","proof":"04bfd6d644d264f12704fea00361b5705503afab12fd5b0999929a1ceccf5a51188676973982884800e2640d29e6dde842bfdf4486c77a7268b578feb7b17c4cbc6f399827d6a68ed1c8db7bd11468b3c002d9cb93e81bb29f88d80323fc78681240aac9456dbe8646e7406e72dc294f8eaa8e4da489a8a19ecb3fd8deca4e1f468a73c18798b9e382f271ec1c94ce00d24ff052c405373e62419c225ac4110f290d1e0746de5876654b3aec07a217fd0e9e7b3285f94d93baa1f5b931108c089f5fb7063901f296b1cefea3bd3d5a1f61be8cd3af973797136663ef1b266b039a6d14aacb5f7c27e3e73f70115ead2d5c0ad0000577592451bcee889f89c832325a1fd9a5f855bf1e28f0642fecb366d7e679af5feacfc6a2d92e9715585c20369d8b5f172c7f92c0a43198e1ae90b812c50085bf5cdff849f87af2f509322e52d9c277954ce1c919a377d06cdd94ea610859948b78faf78bd461fd090b080f8a5980cdc374a5b73d84138558b9ef7d6a9d1f374146508ea57baab7ef84b12ef4667fe6935c9030b640686aef8a60092074a6e699a13583ef4e2ac4f3e834776ef36377493a5c6dd6a01c53d7300d44e19b0233cb8fa3267f37357d155fb121e434ba1131dc94376b3c6aef12b925d1e75d98865372e50826f62757849bed1a64ad45858cd6aed62cd49c3752690cabaf6f8999fde155ba9ea9037340421f3dc87d9b2c22ce78cee002a367d7222f85f98e3cfbc2893f60106e3349d204fe247c69f422ebddbac2cfaec57ae1d922c2b6b9b69bde20ad966ab5aff1341cc13efaa0b19a08573ed8c858a5d01f5e7509653b8342cb7a7f13e28a9fc207c75d3792972609da13f27c8dbed0a8557acbc4be2792bedd0d07b2ac552ad7495f7806ed8e6fc45d86f957d517e29c9dec2244915fc8bcfcf6f8c04baa5f883531930e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7e8d82f0f2ca51e1d3fc57584a17f0530b4ab61a8c896de9aac27434dbb71966","proof":"34dbd0a07f9217ac02c1badc3c2ac5a6be9fc28f1b4d281ffb1bf05eef8d1776f885b50442152cf10f96f02a20942faa9629aa9a9da18b5a82f40fb44cc289381c14f95569c3eac1f5a8904093cb68ad23a2e0cbc7bd5afa448f0fe7c7d444768cc9571dff90b02e1635609bb794c1df3a048089d94921c8582a435dc0ba084189be35a29ba8eb8c8370a1f79db6a23ae73c60471e7effebdca50098814dd008c8f2295b6bac3160903396f0d172e1634d0265bd8a56099690e1a2a05268ea0bb0bb9b780c715c909069e29c8f02879edf1257dc2f371fdce8adf64bd12f290bf049a6bf66bcaf0f7574ad057853e980ccc4ec7545d7fa91bf23a2d221104c553245cb03c6d3a8f1810e9afa129d892fa44171de11a98b8cac4cdf49d41bba4a820936ba4c87bc7afaf2de4ce55a245cc370be0e1677a5042ab1fcb5f9ef8171c0340c95368cea8692147948c3a5689622322fa2931043b493a7400dad957627e845c64a3014b1b96d07cb743e0838a03b20055763289f833baec459d41a0b68103ecd8559986dde4e60c08fe9bd0d6fd59d2141a0263ccc0434390a24750848ce032c77a92f0bcefb312967988f6582819873f9c2a5b4b3a5570b40d1b46a3eee578d22084676feccf4e87bd65383ca65c19d0b13c940013255caf857172410b47df0084da1c503c6b7b50369ee704f41385da8ffc7743885e5d21539a813052a74627693dea9ea1dee42436bcc3798656778ee3c18a00a14dead1103ea666264575f5bc22aa150fbaab2aa0da4c5eedbd979abe2af89df1f93b1ef5ec18e5a4e1ad284f700f163aae4575fc3108b5ebc0e67fff92266a425e353e9af07bb1cf888ad6eb64476f44db434168898d87225512837d41a888894e556957822ee0696e2631c08c70d242d85c898ca326bb461fc7eea48ed8b89ba166df95784c309"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"867d89442c17239359459a7ed6ac73560c24ddada271b7cf00384eaed0830f61","proof":"d60595d74d922c0629d4027c3c4b883fe5266527d4024419a3797f986050af1f4e288fde9ec1c70cdf1e4950374ddb3fe43342c877f3d22be75af2606ee4c20d9adeb1473f1d3f41962207467764727d96665942334dd37e17576f24593b9e146c6d7dd9645b8b819c3fce3ed02f991d4d75da424a537b2316646df0c9c70426c6d8f4692547361113e09b8e7dc4bd9cd597768e9cf634911959006b479eb0059a3ae7ef5dcd142c285591f1d6cfa58c5cf1531a2ea277b05a7dad7054e7eb03369461f9fd0feb9021886a446e8299222654755ee3c4462f1e0e46ca67f6390154e0612267bde50448dfe3f893b9370b083d29534327d26768cd42eb820f2b7572fbbf977d60d3498e87fd39c896741a7db39a84cb0d073772621cb8887c906b7eda9b71d05902d38f5e2bc46274e1707b87dfc50a8c4ecdb6e803826ed60752fead6ed014098a95e603062f1b29c47563bf94a1fcaf25dedc1c2c26840c4517706495a22ad4580717addbfc420bf5c592ed12705d717978a1fa4488ee888968caed64940b9746fb431139a44538debfcb8d0493f003be96d607d329c4755f7788dcacb8b173dd4e36156eef52924c854d92c4d49483331e27df879d6517062af2da46c9330b407984f70287826b5001911fba583f588fe92e665c12dd88923194375bf8f694c2ec83f4e1e8605df9c27f416240ea86951f4bca0a70cef9761d8c94de0cb9649508ffd90d0a6c223225934f385e51dd6e498490633700ced12776a20d3d0d8ab2c72c47ebd5554681b780085c996f35654b3b3272ca254ffe62b8aace85f9a4bf46856d590da61d3718c9192dc6f3e439912a2300e6d152b2324620bb6ecc8f6157852dafecb9522287ec786f78eae366d62c82adce587966020692117487e16c95b7c1c8d1bbb037258b15f61a2580d0f4c0b77cf9977adb0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1670f345ea8745070809226b88348ab5490359dfbfad57311b64823005221b00","proof":"b8e6b15875c49080383eb8fb1e1754fe8c2097de5a87392617bfe24a355cfe2dc414d9daefb7b1d9f17c1d0d3d362411b34017a76c63bd9cc6a11b5fb76f96265e6644c981ff7c1dba8a9f5ec9dbb90e059d1cc336b3cd0aaaa6a88a7fc0e664eaa052b61f00399cc75c0d3a4dc36bd665fb9573520ea35bb3f1bd5a2cc1a402a916c2b10c8694d65cc259702ee8064055698811db157508587fda0de7d0530c9dbc6907f520b6bd5b36595c26a5c62f516fe32b54bbf2e8a1f3a0d54d5f750d206d48dd65e8abdb33a8c5e7b80dd271c3706a545df3343514118e3e0404570efec9ccb5a45535dd9967665118467063f36b63bed70f80e15c0aa6c076bed830fcbefc4351b8017e0518ad35494d5e62c59cf06bcf4b633c1b5de3fc066f247b66a929205af417b0857e9a8e5ad273829c5af469a71c16cce2c38b806008b368e6615aea7c16f61c0e18fc3e2f9cfac41a77207b0585a7ffa2e04b4c2592e4303e21d3799251d6c2640163ba34213fea590182decd99d009cb1ed0d64eb12d6cb8a8fd89495be3c6fb37c627777c95b412a2fd3051e195a576a1109344461d74da96531c5898a8740f3302b0ca757c26916eadc981e99fec54e2a2d1a09ec370f4832c6c7b8bad7e94beac598c21fab202f91c6df70870f758454fb25f81bc2008378b047d25a4dd7570e91916acc7af1a7643190aa71c441ea0c1f621a70246668c7ac471975d29e23365c12b7efa3dfcdc87d2cdd9a4fe1928adf8cdd71443424e0b88cd9e4e0853a55c1b68f1f49b6cac36c81092b8c0a87462138d71cb538ca0049f784b1e1739948fd60d494122094221f697601a36657267dd8b3b1944fb98796bd9f9fb1aa9f81006e995b8cb86e6ecee351b84360bd9bc08e92d040da56eb9a8f05eecaaa7662d23028a4b8c769afca8d77a0cbf9703c770bc0c3006"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"06d18a2ffb587f3751863e7dfb684e70eb25f69dd511a0a5d5b8a1f0fc984726","proof":"ccae549e96def86863509694ce7e1d9f510aa51ffcac2f7f04c5d1e3c9f740160abde6eadde9033c8f313adadd9f895d3cc93d0d95eda3170f13f01078819967627565d7fb4ac26f6531abf0205a79414fa5776b5de3a9d4d9f2903c9d84190f861f12f81eccda19e7338bc0399d10987adf2602d5b982dfcf8eb8f34992a6308dda6614efcb0aab77dd420308dd1b65e383fde6061e5d7243c6db1dc179420e852ce303e00c9d25ecbfbbafc41d1e3aa4cdeb084a8f552b94f076b4ee0c7e015adf629766eef8bca20ff0030b6472f025cf42ba7bcedc8ce1c6a058f25ea10c1e9d9a2d81937b321c4f5d7ab2ca44de9a6532ffa3dfcf016c7be4b209015d6932b704190deda3f53cfcb60c48fb3527bcae3c529ab9a86f4813e0aaccfaf86c9649f5ef996f5ca207558b06580cad3db553fa591ab217f64c3f031e164c575a9298a525ad967f0055e5c890d20a8360c4405877f9a305ad9e2fe308f86de60b6a2dbaa9e99553f3c0d150201de33c3a9fdaaa72366b6f5106eb84a3ce867f4aeeb5a2d810cda05854c5ba79bf341bcc7fe2f82bb0b3f61ce9289ff74ff88a0eb20775ac537ef3f021f7ad22e4787e54a01eb768abaefb3a4d243b622256017b80c789cf723e10403fb1d236e059ffb5f42d18a3546fc2915b74845504502f78aef56bb4a0819393fb476c317170422e7ad674e41c8c020d9d889c971735547cd24321bb68deb9c10a8c6f0499d1a39563622482f0760c163e7e0108da42aa01067f84d6232996cea42c186bf0d91baaeee3057655aa2b1ed04220d2194d2b50aa1413f782f272eb6cb8bbfd344f9ddc6445bf77ceb4c80fa5483ce5398c807d79a40f1b5f32cde46be55bfb37a65e6f8b35157a3a18da944344ff4b72f9970b5a37571e0bf42794eae27cf82d6490845c3f8ea2f968729f1a4cce2f0e6e6801"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0abee252f032d5fba811136c10f65fe626acc3499200b42c8b44fc4f6fe0c541","proof":"a6ee10831be0cda77dbdb2f28222a32b68e075203fd1376b0af6d3368c639032c2165bc1584a64136ffcf1cdcd593c11d6fc6cf3bccc929ab1454ab6c373b84b6c3edc43c93c46eb722872cd35e24f6d0867d228e4c9aa633122b1cd16272047fe978b1b19ca6c3566e2b7cc02f6bc23c52f06378439492f97874404c8f0732ef0073ba447b0d61705f7e439f717b3ecbf7e9cc9473af5b6c5b315e4ed6cbb01e158996d3e5329d399f4cb78a71cde25724859a591699cb04f59fc5b09ac9500663b0102dfc975e044412391a40bfa02312ad7f4550490d7adf6f085e226770f228aa3a7128b9c984ad1237f6342ee2f49adca42cd409f9803a122af8e9252337624921ddda329b509ae5eaedf65eee3da6c63baccfb8f5e78e5c84862f9e059988dc14420fedf5c69d424348724fede30b99335d31e552255f44e0bdf0f7277889e4058b2a443df26cf5a915f6054d2f573099f7e7ea256e29cd1c0e8dbff5956f76e5c927efebb05a8633948470f9727d7965c4ffa97631115a4befa01bb6fc8cf08351112e31f5b5652ba0c4aa31a2380c6680be17dd23f4d261bdcef6e2370f7348657b8afa027e55e7fa22248a40f362b9cb9f884100844a76f94bb8765c40fdb1d16700df5073d5862122f7e7360e8c7e7a4de78c49d051982c9a45f165a04e5db61af317b3db1f005f924de74363e366f38102300afb2692020649613823df4b48337e19bc8795d8f60b2b3ee8103551757e886af7a5c05235f73dd4d6e0631ac2013682702f93e83883941709dfe3cbe41b0bccd90dd3a0c966d2d6010f7a17bf2d25dbe9833608b99d41a8cf6e11ef8dc8b8064f61fd0f420e947252a938d5417ccb043de0877a5df076c28d85956b380a4067b019f04d523bbce035c5e3e03c9c77ec30753083fcb22a623ee2b24f152c92e69cdc7e7feeebade07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8010a2e596bcd2ff69142131e110dc08c7d0775e459b7247f2a69342e5d84470","proof":"a2734117ee4bb672af987bdc83362a77c1e1b4970e93846c861c06ee884181759a0208172700bc1f9cc45ce25442995c417e22606278a48b9e9084d1fea1024746abec492d1cf2f81a33734083c7238ae9a5bdf05c18715afcf601bd6dfbff21aede98e1951db53d0c6ed6a843e8b986afa02dcfe16a80428c364c558d1a733ba95612ba3bf60fe2ebf912cb7ea3df6362e7aace4fdf66d5fcb7f81990cd890aba3af56a17b29a7678bda123ab330599b7dc9f327be5095ff3c53675f74aeb09a49c1db708a5dc60857e5881afc0e4a36f1447ed65345b19ee1c01c06f21580226b39f6563f37040eee98f97b1696ada76606b82bc29f7b1349eccf203737044c63e073f75d6a3bba7bf4c2f9e41389a1952a4c1fe472bce893184bac60d5251f8b839d0fbac534021feb5e05bace9bf4576bede8273a188e7ef70d79935ef2e6e2038b4675191f631cac7b7aee6c7d2f6959b4d76c4057d129520b9b27b3029ba08fa90dac3755b448247da58f56cdcbcbaf548decfa4cb502708ba8cb076675ae2f185dcbaec97968b89359919b1902d1e9510684ca285675b241aeb145245740331dc48bd9c6a81f95c42018c3f2cd4527d0c077f0b7e034b857bb6be217106af70fdc30e360537e681de0b1992b451987272d5f4d249f53fd45611315c190cac0124b26e30f3d105348314236cf5c4c2973167599cfb85a33e0f6a43322a10d4a6b6a149de3466de2b760acb4beb3d7bd196b1896aefbfc47349675a8f3f5082c6ed979c814d3468d301292d372f7e87f1ff7b152396c6bb57b4d47ca45fec45b343d1a87c692b9450ae7a47afeac81ea83804669d6085258535aa35aa1fa0c873a9ecf28a50d2279a037913cd1f20521a124782e4362f5eb6ed7eb9b2044124db409d69546f9b497fda005d4039cb9c199ac9b0a6799af13f8c2a95f809"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ce1fe29f9a6f1686b8a722b52771a7610d3f6ffc617adf46dbfbc991a150e430","proof":"2c64d8b451bb14598ada573c8bf7364bd7bd62fcffd29dfc0a891d2ec0e7c67474a02e5b5a692a38d9c906e6bea7a33c8f197eb74977d9f4dc3dbd669435063f40a0180244fb07062987e5a7e39238db3dd417833b1ad3ee24297f28f3bd5577da91bc0f4706bc48e23b9bbc34c074997aca714f5e96bebd06895a36ceab063461257af21a09cde08aa85a1c4c6a7baa8f7161f478bef1af1c928a6c5d81ce06a40c6a2f7529e41588d27fe19ccabadde5df1d047c524c00522065847fe1100f065fc19273da83d85aa62fbf195fce4c13072d32f8ce51e484d6e04a1d97b60b4e370dd77c2644c4d7e5ceedfaa35f8b82775d94d1cd121ee1ca03c1ecd4536932784792e621fec2454b666304eafce55c67c0205925515d23317e37103c2a0aeaf6a8cf2afd3166ecd9a710dd75a5f25dee2cb92128e5eea6125f93fea7880c622d67e239a2ae8933599d90f8327e841397c3bf9fbed87fad2745530c6ba328620be8b688ce5ee50d67e3585a0eacfb3b4b417b09fbb032cc8077c5ecb2e64f2428ecd596e45a1e06616727788c8019131622bfcc255087bf04823683b1cd506c1510d5a54799847a27607a101a4487caf870c0c5b73c0494755fe135f5bb774e40045fb85fb82db23c6712dc00642df0f293c0d3d5bef42381c5aa973b09077ee982f247785634d251c0d090fadcc6580b47aa3ae8709579400c2b5b359c45eacee1230ead161f8a997a276950e91b0dbd7dbb360f8b47443ee5934346e71320730837bfa442944f675b57de1e7648dc1bb093590d54777130b06b89d64b78ca1d1ef2e50cb7c8e9eb23c4051655c6f04bb6932ba4aa5d696e75a203534d071e79f89c0835cadc994fbb5c774b9a2d3477084125fbd921941157b55aa0bf09381aaef4c6c8e0ad0cc041982eab9257d3c798719fb640d3236208f92f737401"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"82c7daf1b6e41ece7ca00d75db09f070277042abe67191fe1e68fbd180a32565","proof":"ae2fd85a4ee2dd6aba988a8cc5f2bf30e25cbf9ad7f3a04ec89f8d90ad4181627841063423423b86d578777ae7f019201d8408af2578dd997a398c1d59900b3028e980f7187b3ec9f51569aa866a2b435feb704539709cb233e6ff9a8c88274b4af87f2a5af2c6f9636ce93c6037e4ec0e1e559f4adc7bd098230913970e175f0a4cd8f4f2866397c7e85f9fb167cdb427a60dda78e72b60a891ef3d9850a80e726b4b71aff84bc7a8abb8e7098d954e89be9a9ea000ff7031815304188fbb0c8aa980cdd2a6a716f5c18fcc10de58cda9199dac7f4539212dbb4f2db682450e6e03a8e43b04c8e60ad92e11241b42c65bd54c6dfe090b9da4260d350262a648c6a1defdefc0a3d6663836c7d76a13ea46c8ee7b6f4f85651342bb1f5a780b0cba9e2b95fb171c5371b48dc4dfce3643709bde10c951cc204a01b8f963ebfc0ff292c03b68d75c1fc0b2586744feb65ee9ecf5041ea31b4d502e1353a294680f06b549a2142a87dd0cb1b192224f35babd94859c98914883bdd0debf20839d5142e8558e494b59859068f42b99eac869c961f0e6d431255229f3856e39c184451e100f939b400c638560fd3574bc8b14f9674efd93a1d047328e5d0bc91b492b7c65f0e5d69f7eb6a1138274a06c5eb15afcf4261abb2741d1a0197b3ef791001c052dce27ff8847058452b244a3e0913ca7e789bb30211e596e599a11ac856ff011a37cdb413a1218a63fc3aceb2d27ecb26a929d7303268fade45241ed631594fb698559e977ddc004ef620a9a5115fe2c1b90ea8b57d60f71dc52ecdee86c6cec1df8e785ec203c40b4676a369c463a5b6dc853b9cea742ac684405667552b1d7622ba4cd566314e9fa37bfde9b8184e6768209257cd27e9b2dd0cb9fd202e5d8b634ceee26a7eb45ea61b3c10d9be0ad96b1cd0527588696d2fd64df550a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d49d60b5520e9b3cab7ae8b6b988b7169096a980df04a70c28ccbe5fe07d841b","proof":"de9e6c62b4ed40d28efff826c76d3a56133a2f26476d428c8c387c3fc7d57643baf820a8c94782e3cdcb05b57532293fbf3e878dda8b26b0db9b49c923d0463f00f0504ea56fd50fb6a9f1161c00a2912cff9c4658c95a46be256049b21aa132daea95a4ddc6d2aec5afe855d69ba7fc5a04ff47703f4432e529cdc6e9e04d76c141a7751ba1e058409b97a02a035358da7dd9cee9ebcc0293536e0189ba4d0ea5df2596cea8407a5adecb87cd174f856074a5c248e7e7468e7a2dafa5191404622e4f8814d8f52f2d16c94df2038d71dbc53737a022efaf311201b941e88206bc766f96ef8de93653a26969cbd562cb1ff1fe9a0f816e8808432a061e01901f7cdc5579fbc8b94f8b596bd8ce5afddc1a7b7f0bca9fb4786af946e4a266830c760281196c95948330fd59d3a6e791266f111c2a80728aa413b00ac87184426048bedada41bbeb47858348b89c7ef3ac5ab0d12685f1febdbfd48ef4ef765a3e24af1e3bcb8c5cca2995da2cf7a418dc4771cb32a5edff0b913693b332b3b1787aa6c4db7e22b04f52b1febf929737f3303392de0359c8cdfd0c8c62b50ff3516839d18a4be6667f0776dd14c97a1b685d2352da1d6655eae6414ad2a7554863cc72d4b0fdd585718609eded3812def567f3b5aa5c133ca71408e17fa99e9e72a2701d948f3200ba44598686250c8a2c40923d459b8f68c207d5ce1aff6a5f01a8817ba31fdc87c7121db25cd59f641b3ee4702812e72fb7342bc41ee840f74aac6d93c6c8a00f7e3db0a71fe1a81883966a74e7f82798a90c384109eb58da72322f0aa7edfa246456b50fe58815c95b7391bb565955a1f882dfe333b0ffe4775398558502e8a377d4e20ea1bbc6b51d7b54e7ef156408ed193f9c7aa32dee0c3d1633654f036d011e438e8e481c5d5118d879dcf422c470a8d1d1eaf0185b0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"30eaac13390ce96825629b4d6a252478abe60ed54a489b38e772a3fd10d8665c","proof":"b0eed655412c499211adfc0964183dacba77e4bde25e2d4c0cad2ea5831a42652a10e885fecbe7e356d3d70292362c2b7b364bcaae5851bd3829f19220c862221ab0734a1792dd67e9bbcd24504e194632ced6efc0ea7f7d7685b126c0cfd6516a9e3f548994be48df60af40e10adde72e6c375282a455ca433a742eeb89eb6d99f00fdc629a17ac07a4a89323aa6f3e3846a05328f2ec16654d91df43504a0ff47c765433c94aeee37dbb503be308a6735855d224f30d7de56576aeb8f15a03b0b599f0ab8e001ac1ae26af347b8b358220e024cda94fbab2700a77380560027a8af3d52057c8a8b80fde5882e734895756039b4a2db41e2f009b4745c36b60ec38c7aa2ee1bbb43b6f8d2c70e2fde893a17a1f41250b2f0b0044aac3e91832b61dc71d9ae860af9f2efcade263eff24ed57e307d7c65b7f9af11d9cbbf3811bc7a874793f01c4ea0d5e147b43d7f1b50b29f41454338515b456831a519cd60065a4c55c6405a66fe334f6f7f28526bdfc386e2e0440cfdeda2794aae6cd720a410b2c14053de59ff5461eccf70a7de0f388075f8aacb08c5db90e4d63cc3567c2f3b8b4e54909fd23a3ebe96919e2a480d484295148eb610a5c5be5cf1e61654025d692d263dd347507257bd494fbe187f2d79b29d7846a7955f4f8719d50feeffc9f5557609e82816a08a47b5e1483181f948ce8bc738e8bd48daf54aa83d28f6499a089575a9566e6dbf593d92d90b68fe082531e902b781d95c5acd491e90267892903362086723fea18a076dab71a85730e5c866842e879e6595e68b203008fa92876b52d95bf3154deffbb7346bbf630a13f2d29abbf4ebd173480753b1a1e0604f3b67362f77b715e3873b5e22f3e153b14a3ffa18b3f4941056ac0bf2e9fca773769cce18edd0d1418800f0b032c5d60bb89db1b1157cd9bc151308"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d4c5de9593f1e30f25beff5aea4816b49e5fe3580b68239afaf59bc35c542172","proof":"86e9c45acb8f4aa7c8916ede4f7526676c9a1aae9d9fce4c2406c440075eb06b72f39c3931cafd11887693624003deab9c18c513e59b2bac366b4e85ea7d5d6cbefdeb5e01e566b1f30a685d9b03680b3d21fdb59da77028e239b1ac6730690cae927f9e77f8a199b9c65815621c3986356ef3c4a8363e3824adf4863b7a65239725d6c6d64ea25a66ee4bc6575b2e6b32b3d0e50644c7f4239a7ffdc7ee720b80ccfccd2520e9b02c9f2d0ed1c1fc6e65804557e058a0ac1872ea0f0dee2b01a560ee66a4a9fa2a98e683eee2ae47ae0687defb01581c89310613abe800c00a18f58f4fa1b75fb74ae2a41f38024084518deb6eab58405f4601f0e6b9fef851c037c795f16a723c96ab5dbe520ce262bf0ec802f243ffe578b46922e9ac7746c6ae6ec162243f913b8395816b9a352d18a73080f63cafa8bb315810815f212e52825d87b2ec243264f2747e35a795d4427c81b258df41004d68d9036c6675449cca6cf57a7730c19e9efb265e72d542362b7f82b18e6444af282da1fe05397bfe74e73358c8360067c9b6f95ffaa79a77bafc56a63d1337b06f579e524f546c0a69ce49fb8665d6086828bc21db36e35e9563db24df19a14c771c834753e61b12a2b271b0cda557920a784b3760c3b97fdb878373f9f54db00a98ec4469cc15ee09c52f3ba432101c983f6bf68547b0582a6c271722124cb4af7a6b5c324038e82f030acffae2b3d98a073cba9f73365f7b8d7cda505d4a1622999f4ece901c1832e977d3cbd534635bfa6fa76bf706adf46dcd91cf169cce4dec507e5ee137605283c6ef8b8f6dd365236e750f8562d57fde2c2ed830626c0dd2eadc3a5263a20bcd46276b35861e554bb38e152ccfd56462acf9aab5661bc182fd4f43190435dce0f9efe802885e4164fe1fa371b484d3050fc5af57964aaea3fec05e3000"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"463e65516a9e7a88d6353ddf62c43595e6ec2cdab704a79044676d50cee3a323","proof":"a02c7a45000d51c76231f2b78d2c97d2cf0e38a8320d86842df2585a50bd736baa5e2cfb63b6a81ed1c0537d5326352bb2b06de9c8bc6871e882f5ce301f5f70e892e8b97e09b628cc360e2d4a808c352c80e43f041af4a0e81b7a5f838933006ae9150e5d2ec4540d81276429d0a29b7e5eb8fda6da75d58e78f2e0f649e83452c466b722772ca1a0320411a0c77596c0d346427bee9404acf6f1f0b7089403b18c84d7740b3c034a9d9608439529e80090952cba9178da7dea203bec7f52076c77450a013195ac3c681bab5ffa5a23f0487c4535197589dabfacbea4e72e0a0cf0a8d4b13cefec39307e906a304855c2cf1951e680de4095b811ffe4c7a5146e81f6ded114afbba6e799456c1af267312e21093df42dd9ffcbfad6756c62223e86de83a3c489ee89f9e08235d7a6714e4fc073b7c20a70efba8e0df37f5c7ca462d08d9cb287a1bed48797f32eb13e12825e9ba9c9e0c1025abdb42ce4636fe6bcc858a9af3d012a1ff6080375b2b04d8d20da9439aee2faae98c824711b03d09a74ce48189a5ba683892c652140afb89c07d66d127fd6f004dc15f594544668acbe42b4dbd23a55d6fb6566ad7d55d0fb8a0e20b084e213ee5fc8aedeff1c6ebe3ecfad667cae1b733dc431ac4d9fae64f5178b888caa2dda7de3006f22230a01458f806bfddb3cce0699a813d6b4d3ec69da1b057e9df9d1a77f06417b0b8cdfe8f7044d8c0bb6711e5ef875a17c67f0c6897cd4074e53c525a596d67a4f8c331cd81703079bb9c9b1c8313edc68b621dfc2b335052d1ad817115fe01179de0f92cd2456602875f62d89c911f534fcc043b7a415fa91a7b6f63fe3499c6fced870fe892d8be69a63a7981938d0dced040ea8ebbf1952fd5938e840cc4a0dddb2f8298cb1cc9c7d6503c37ce3d3ae4b69cf1df0bdb3279435d267e60be400"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5ecfff87ffd300e6de87059664f34ee66187d54bf9d8cbf8b4988c698cf52742","proof":"aa80a8a3bf28611c1cbfeff299de1c67cf494b1495169389d0e9656419d48033eaf170243cd8d52e7a41e48847badba944dca2161e15f6d06d1b81942b1db84494337b1fac552dc4a242aa66718158537555c528c0861a36e34982c7de4acc157ee597f092ca3f2dcca4d8d52c08bb91ffc3e08ad205fb49b8b59b9ba8f1da756d207060d1324ecb5910d82d7eb7354a69b31d44a5c3be6d130882ce5a664305964bc45fa442107c9d9ceffad6fcabde54c32e7df277a94472ea7f6ef539650c1095ffaea449534f3415613266446746692dfc69fa655fedff50e9cbcd240a0ef0aadc0b9bb4a87b36f79add9a695245aa2a9026d68141ef4c3a06057cf89733e8bd4b0b82b3ab501f80bba8b1a621b340ff97d379180616b9643e67905f7c431c9dae74e3a23b892acaf13a43a6cd59791f9b919944e156982093edbf3bfd43bcd2c2a63afb2f0f1f99f47afaf743318b6bcf4deebf21fce63d47251c24307a5e2de053a368b004d6ca1c868e4d0413bb9c4947d2d7cb641666c77f4540ae4d186d147d448023f7ae1e86dd0bde6f918d70c2b55a2a29880d15720019ec114f661e75cdd6a317ac547401cbde11fe047525020e811700ad81ad74932870af149cfa3a4d52f99a0f4945d2c2ae5699d2acc3fb4f6606c9f500b6380b91b30e3caee4a3483b55120b8cbce835a41e785bd8c90d81d93a64ec5ae9165ebee5ff182e732a10f41bae9a39fb121831297f430ae5c1d5239de0a5b8bf348bddb04561342a59f9bb0086b11754672a02438f539d8402b4b50bc4f208606993b79c3f017a9cf7d0efb7389e23f0e416f1ad7a018ec8faa0720fc86c4d15dd12c230043282415199e1f935aa36e269b4d24593593e694e10ca6e3fcdce970fb71c1cd30a197a5b702d65602c413d474dbbf41ea648fe529093dd9163d5f1f5c3efd31601"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5c4f0bb312018fd2c828bbd0b6cd2f02efb979f548c118400c7e1889b544c249","proof":"905538a645a8390498498a2e642653d51e4da860ffb61382ed069f534c8660287816dfaccf36ade68619f2823a1724782625b54960d6aeaf56c294f328dae4269a8364391795bcc9a73c8c4006774ace93ac56d48a6b4f58bd0f136916b14909768daab6d354b2ac57387e91683c54e398c8848667dcb777af7950e87888943fa53ff4c9768f76e6286e2a203f392388cb982db5c95f04ff24a1cc783b8ed301891f756f0b8322aad921da2c8e6a8890a26827dcb1bcd10014c1aae824d7d102a685d0d11dfe8cb8dcc00f91e791dad2284fb21319f01ecce1bbc8fc637138085ce0b7e92553b8e9422054dafc808ebbbfaa7251114a3a35e95e9127bd5c44303cfe40914394554db2b1827ae3410d6c3253f4a97cc7ecc2e84600ec230be15eb8942bad22c3946e4f2b468fbdbadc76752cb6dfd9108f4025c0f4c1d1e36315464279ee1ba4077efbe186f5d2764a6c633a9657e8b8ee791a443913b7d104748aecf500f2b591965f18edaed7b5daebd184d3a01f084b2fed5459ba4f0d844fda167a1b4b55d337c47de605a3e0e30f0793648e678b4e9f6a75486e77b7e262fca5a01ef7233082280224bc168d23a123b20fefdeb60ea3d6ce678c8f8afa7eb4f421c6c6d6c80f98f98d6b67f6151e73ce37ad310e46da6df7a8c30d3932263eaf21be74940bd42ffa23977f1c07e9685652a762dd4458368d42f8bae98f704e37ad8e6fea9cfafe7a13aa36ab3c3fe50e0b367118381abc563838957e344e907ad53f3f94701e2b1e7bb94249ffc89b917961f19305310afb7e1282749551249997cdca057309747c396b8105d0811783a2186bea830388f9a2de17cb750ceb2568260539562bd2915aace595b3553b6bcc54afbcb97288d561a09b5106097476608c6145ffea2ea9cfe5af9d771847b7f0fb9298965cedeef3bdb651da01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"acbc8512ea710dc87119e89a475f2dc1ff163331ab39bec62a67eac534f5ec34","proof":"14b0b3085a281dad6592e6760bf205e91cfc61977af0d729d7027b933bacc57b16e8677a655ce42f996347c8eeee9f3f9fb36cb3965b6bbcc7c31856d072d31358688b9d29462bf0e3a189edde2192d8579ea46d7893e63d9a9e8e16d4eb1d17d6818eef872173720aad567f9bd3113bf4cd6d828416a5b345b137edda409d59daa8f74a978ac09492c10df8917b699709ac2919d52cccc6a15bf60fa741800dd60dd5d3954790fd70f23ea62385ac55f6bcbe7ee00ee5b9b6036948975163008b0177e85aaf2a9f2062526cac5d5da205dfdd1e2a1a922c2aa3f73a5851b30cde35eba02fee38fb893f94bc94bcbdc7dd15206ec19c00a2e23ff58425ad24129cdea75f5deb0b142599a311f39e04bc2dc49a83c4c7af9c3bd31bbd7c3aeb7c3abd1369cf648c85d174d14e4472e6c8083095502641a854a915be6cf6c9ba73d29f017993009ef0f2813be80456b5853e29560695b742f06ee70b6ba170e61db4cefe7c6be92f58635265b80e4775a49bf98bd8e1e85f13a35c184ecca48b740c34940dfa7a6a2b2c449866a6bbb5ac354b4791db12933e852637d8c146aa144a3292461365d8439f4aad1cdff76f84028471d4d0d952208e99ef83ed27c31ed012e2e2ff2ddca619ba3fdfff1146252108cc5d3fcd7a0cfe6bd798d0a8701550bf0382ceb387dfc87bdae96aa2d99958836c1ea32935b8b0d4b66837daaa0dcee532fab50783a890702d62a8f025a068dfddba092632373e0d3e90361a2c318c500e0a398c3a79c9242026ab2ef15ddbc15f05a17c01cfc4ff5334dafb5a68bae761e27ef3887e2aa43cd9c1180f5988c0311774a5a074634ba589bf73b17f7e3274d09d04a90aeac96e994486698005fd7eb6eb231d324b8801da8de9f8010dbfa8e97cddbaf98fd9c24c261741672a51165541e9861ccad0fb91b85ac708"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e49bfffa8e3faf2c43263b92e8e6d9283c9625fda08f18278b842bb43d5dc474","proof":"72291b7a1970fc0a7bc13fd417928a1665115c13d615b7f9b68aa716b14607715a7f83dfdb87138e3703ac8d6f925972833693f6e38de238ab9a1cc77bc1365a04fd0d5c6169b5f1cd0677873956ccd5420ae8da33d5ad36df0ebe9922d3b87d7209341552c45e8ecd5ff44ea6ef60899312968e76186bd7c62cf81ed160fb2157fde9b4c1e3bf4539112a317350ac1bb50a3856c47ec10925cb5c29e61be402f1d4cc52d193c9e38b76a61b1ba9a1ed4e5a4366bceaa79228124a66df222e0960ccb65e416c77f7a6ea55d37640b990bf4897fd15df25a049dbf68a592f1f0b985d2bd326255b11be12a66e395b2efb5288d2db52769b60f69fbfeca0ef2c4b8e314b624095da896d9f73f855fb4b18c4d991e1ca9ed6f3155d7f81715c46483c337f0e9120911deca99d5021f18d90c21925fb51903eee14d829383b7bd275de8ba72f334f879f8f07331beca5b32d1efc628519586320ee58f3e6b92bb22b9e612b9ed4e67dfa7d58780c57ec47d2ffbac8b80228a2b6a783054e97efd47f50f048be4450f8a5181edb7b60e8f00221ed848c0e3d5164b41967b8baad3530aed98ffd38996055fa7b3532cac3340324aa22af65028fb4cf40560e236f6916842dbe22ec188f502bf5475eeebaccbb5d152524a4c20ffa853ea7b5c70fb4103695fa0d71f8aacd9f62672f439ece1d783b2cc594b9a80a28562d4f7b9970743aa10cc247f4a8ea234824541595f4ab97cee798bc7607b7132c84dfc99be8362ab803586d64837c932e632b579079f24e12ccfd2982c54016fbc6bde533094368ca0bfd51ded26aeb85413687a5fbfa6f9c960592c50edc20effd19d019385fd0a196cd6999422ed693615d192bbfee08247b33cfdf661064dabc8f4bef8309b417b71e617530779ad82e1c65dc10b4fedddda06c57f4092e822e07f8512a05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6a27bd4f6513f74c33ec30ecafd6dff51586c6bdd031c682f40be2edb6a5b51d","proof":"0ebdb4bd2e743a09d2fac1902944ff75a7398df3553d22f96ded364477a43a5d46163702160e417f07bc7cbee37f2753500c395ffc51d7803f8d959cc9f79703baeaa66ba3d8918a6418a5e32819b9cf8cb27e7845e8a0fa279d8fc42f71325b14427d95352dc0495237c758bf7b03384ec5b22963559ac04f05d066266bf00496a233f789de5ab5433c7cf0c8c3db457ce3f1cfc665dd6fd937f1871c29600e0a7e097e76ccedbab5b8a7a56b8b542890e8d71f965d7012d96b50b15cb26607a50b20f55cebae3a8f68c97bc74780c95c323a1f521e87332f6d782282e6bd050298d321ccdde0e0b121dcd0c01e213c6a944edbabd3c4a582777e91c10e420996454bb617acf11906c6b506f441bc0c7676965640d3242a2a5b1dc33e11934944af5022b6fbe503429e9ba0195b6b846d22a8229a0273a915923a7cd84d0c426c6977c7b105c6e4950741291952f23c440fcd7dec8f404d154f328fb2d16c051cb4844d8156941924d47da501a9778a2515daced875058765c5ce5c786d777138bf9de5db8e9d8127b9dd9270ca7ba1b4e1888cc8978ce73e60b40ddb399c0c60776cb6208b4e715a6a8b5d10551d8f81be7d18e33b38e80eff0841cd70a826f4a1643cb7a498a0da23bfa57f9e2c3b96da3757caede67a2a8183182777611b9abcc7bdce7249a2832ceb3ae645ad62145ce9d878f5f42b4bcc8a691fbc6e391677d1e6a6a0d7b14504ba1aac9d630e67a03876cccff961c040f4453aa3d263744ef1a2ae068a01fcacf2fe85d1abafb50bc5ebdb9e1cb5e9b67085d9d06161c8a69f524e4c48cad271e42b16f0a67f02c9600879310eb338654e14bc60e23ee6bd3edc4edc3f7dce247683160cac4605c70bbeeb044cfb9b4e796a7f5df20b3475af9b1c318f0faeaaa15535746fdd4c4472f3d9d7817ae71114f0c7fa1c08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"749e07686504a0d9878bf100ea7fe284ce22e66bafc3b4290958bb6501fabd04","proof":"34a5c98113ffa8713e11a3e043b2b2f0d3ee7f7fb263a53f465e5d7821ebb506e22e2c509037aff07d530126b1d79a742995f7eb60037f40163db6d688dda5514c6bbe12b90597a154743fd4dfe986da1d84cc80f97f15742fc91f488dbd427368d3c3324159964cd3ab8ac7965998534a85cb2d274e06561f18f50bed2fc72e6a0cee42313dc5228da6eac8f2cc3807b677b4c6e125a10e65c59da2b357340779f727b62029669deb0ce26174ed8fcfee7491acfe0661d7c0af6372d7e7fd0baaff9867bd19dfa1f87bdd3375cf593990c6a1caae57c8e3e00da448046b0d0342cb871b42d9eddf93a628d7fc006d8b2101c471215816f5efebf168aa86a37b466d868680eb138a67772d91e4bf622734407c5614d0bf777c0387b46e523b3b64a8395fa709d6a371ec58275892a872477f2192cab905475e25daa65dab71326c31e184de221917616961523935b9d2a6df7219bbf946d2187900e9738dbe45249b860d57310bca2fb4a38f713c4177ff79921f110516ed1f1cceb421116c543a7e3d0f9c3a6b159b8c1551dab68e3fcfcca33e6c84fa11a3d598183d3a10282e815e10db45e6eb715a0584c2f817268f44a269164b4d4ef97126d44b194962124f328476d4ab1bf6cd0fe937ad49edebc29209aac6be55323d982aef0494281442b58623603bdc2a4e4290fb8846cfeeafa892cb6059e4da611b617703cb3abc17dac5439065b5e23a07894c9bfccd7057a8053e0b4b62e0af5ddb7bbea05e3807c9f74009b9fb62e1b8f91dc2ef58f52f817272fa79ab1959b4b383c01d20288ca7c0a3b6ce2c34a0ff2f71398589a9f9a6289c07f39c7a6ec1b484b68d48bf3a209936db0dec22ad652196bf0b543d0d6888c9e23edd0ecd6295a9735b092f5ca7ef964386f8da936e9e08492002bdcc0fb424649e74edb6d9ec370d3e0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"46ad86272f320a45e00a158ade3ed21aaa5936d794da44e4383bfc027ab87737","proof":"5e7d8ec1bdeb8b67c1b4dabd727cab3f1b1e20b8ba060e4d50745d11858cce2172a8fc7372cc4f89c4f8bdcae01e2dca8e2953e597af3a65e4e6300ceb3ef567ee5cd49d9559f18ec2d1b1bd7c35991c32e0e5365d28a487e7da0f54f60e5c241234c157388e32ec0468abe284d0cf0dc5ad08bc17d85275e9ca14a67872b822f4ca1b23fc084d3ec276f8220247ec2d4f9986dc60bf7ff5f1b6d5f2f4484207c1d50292f78eae2fd511372d54c5e04d809a71b0156a079e739b89c02ea09f0fa0e9907dccf0010f096a94f5ff0defa87d677059e746b4671395af3fa925cb0838ac777d8ef36d65b8cd57e060b5b849669b76069a0d03c725465204120b523a3e2dfcf2790f09686438fbd39d94ac855350f89abdc6b626c9d75540b055e70eeead1ec9f81e849ac4cacca7a18c5194e4f1c04473b1b81719ac76cae225963e78f22f5fff84ee949fbe31e46fe764705f0ee7b221afbb76d299c7e1b75ac732f498e3ed263f5c628bf54904c7f2ad1adc13f67fffcd781416d97d908eedd854da414542c1a6de7c90c5b90c71e4cac4593d28f993146863cd727af075348d07969b3ed6ebbdb36f0bb71a9c7f48800a6f88688aeec0defc174c71e10563987068a075927a92db850bb40a5850dbac4fb8fcc9a42d82c8075c5151ceda8c0850965592eb5908ad8c7652f8d1bbeb6d688d88d7aaffed1d365071043e31f44f3fd825ca4d79c4f4450ad7b4bde49e6227ac14ad39d0daac00036a84237895fd0694ba5c691ca02442ca1d99c960f2e1cbf470136d829e6caf182ee9f5d42640598c5c627bb34eb30cd8e3592387fc3794701636c069a0c68a977b1ba2f40099262f529c8f247213efdf71699c34ba69a766c336799479ea4111ed274ee03bd802a56aa435c21a13171cc4c29f9abd190a9ffb922fe6d4dc5467f7b744a3b36304"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"62c1db936908c4ebf350f27920eae1d9a5c343dd73d4e705ecf9a8a7b0fd191a","proof":"f67ab613adf5e8bff66338bb7df2966cd8ff6893b1724dc1d1b01b14b0cf963040b849b6e06232ddf08c3b6cc907fbf6a4a3262ee8513a56293ca0cdaa1a3e073265da1c3e39bb7197d1fc996050dff50fed17581828ac74b0dd46a51aa17f408afdf64fed5597badce515baee74660cf97dba622d2d7b302aba49bc5ff14553eeb3d1518b9e85b4320350dba6c787f9742571c69817d1d530917f1a3ce1ec082243b5743c433b43a469984858f3120cdce7f7b94478f67cb2d48f191246f30599595a936473f2780a046ef40535a4a1304aeec59dba0e32f88ca73a8d9763066cfeaefb9251fe514c4a58160b06c35ac1b5fa2fa14c5b4b3d392f412f55cc74b8c5e0dbb2efd0f80369b3f39609512e9431e3971c894086094dd3a820e5e855a6cfc38554970545141ffc3d99dae4880129dbe30e1563a1757bd7082cf3932d4aeae252a1d8de4c474803fd8216044626d0d861b0d7fa2889070be885f9fc09a01c067679bc8421ce619979cf0bcd2d1d4dbc5ed25f2e1f15bd5cee7ae5ff05b6930e49201455074d63a791519406d75f763526c968d6e74a221c87fad6031aeeaa5a2146c29d73036c0e114e848af031b5064beda65b6030efd9f9e5f1e206e62d46a3d60fc2664c039550e35196d0854ccbaa8e48f5106b596c06a1ee20469e29de83eee4ae919b75e6bd048178e89d6e4335ea872b8bf096fa2a24931e45b4242205ef25a0ca7c0d9daad2ab268560b56aa068841f44a94895f8c2055c4bf4db7598b39f1653d8f8db20255494a4fbca5220bd56d7cb83cf4d99b4a308756a8f4497df8692147dc3c5386d3ef9bb0add7c9633cfff3a5ea3834e9fcdba65ea4c3b7e6c7765a7ce9a76f3e789a3db0a68008b98ffa7f98d5f8f0070772705c9e386b0ea0516c4e2ebd67dd2d56d10386b98774bd4d3bebbd027bcfb65590c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e63c809acbe0dd83d5d4973810120023a6a92d0625ad2431e6f5a286e21dea14","proof":"b06d5e106260cbaa2a36a9c2ead8ee47bcbc41f2d09fe47205e9384e3720d316bc20cc5686985a7aabaa693e57ba6baf4620d5f12ce4ecc4de1fa26f65511a77c0a85f94d69a08e438e1914cbd8f32d07f679e00bdba503f9ad7fe0cd65538396a6081284dd5ada25e8a8f7e2d2dfdf427b08fe4d8016f74b6ab791bcb164a1c1335c3851f3c66e66b3235d12f683b60612c0407359ddb9a84458e4c4029e90eaf26e7115ea265115b5210acc3ceaa27fdc3fcb0b4f432ae39533532b7e7a70b4e32a31672a148585a60163c3c5a50ee56249be9bc1dc3d479b70fd78c5829098259550315c90144fdade26bd613a7077ae67eb760f26da4aec000942f994b1222c613a261a466bad373edff55feecfe031c48b3b14905a6ec8627b0a1c0e52ceaa0885d6ad41b41776316b4f3dd981025c75a24d7f47e4288d007a1ee905859d0ab91738d69bc1f0223cf0ea0d28d41efbefef0c681edfe4054baba87cd56667c6ca0bd0a22015aeb02142924a9cd9bdb4f513ff55a20303a291ec648426b3818f2c0bc876b361a67606f66153454403697e61d4b607ecdd7d1f183d70daa7052c946a50f258f13f8e76f1fc942441e5c9926b12674703fc254120f268d761a6c3ada92c1ccf6632cbec0e226c090012d978cbe2821d7dfdafbe7c554e7571f04b4f13e9500f7e62017ed4f1c3d81eadadbf4928ce33f639d203cf0ba007319102659bf4b16ebc6c104877d09f2800ce32be77e3930a971dc9cb2d29867d40106f858332507807c606574396007eab76a892de443a24cf266811e447c610320dc5a83cd53a12c3bf4617d1fde4c1353a1241367aba3cef1e96cafc77aa22d6c8101edd5d3b7eb4e1431e80c0deb6c24ad6e5d238b1e2e99477767ae5c8c0306d1aac4b53c7ee22049f36e2bac0f74e2561915c8c9b746cbe30efc809b4d720b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1a9a9c2fcf5ee27a207c8856da9294c3d077977c3344889d7e1dea1d565c626e","proof":"f23ecb5b6e144172145e9aed8d94b427017d46424b427a266ca5eb92c21ba0295873f76f69a49187f1202fde98ebedbe04c7f4d0c602856c48a690cf4e88fa57e8cf216d4106ba514995f4da76f2afe42faf9ec3a3b9dc2d30628384384cb76c76d2e1d9ae23c1b8aab44bc0b0282c8770102f4225be59e6223cd72009b1c36753c15a2787a7dac10719cac4722af64bc03956e4580d871ac07bba810ebe370cfa2ca461f7a8351dddbe2bcb4b65e9fa98215a62c68efc7d3bf6903a8d3ca907c49735bb6f4906baae9dbacced356c2453d2f3bfec69c1d0d007fb0aa6015a020a75def9047c0716e5dea1e9cc4c6d4ca335d08732aff65fa63cb1db257dda48264bd88454656b2c649c526d3570e42bdb6ecf9a45399a4c8f9d67fb8ebc6d2112a39fb0dbd5c0fd76b8a1e48600828a242c3bd5e1c54bb19b4f9fa6b47d63193c020502736ff3d9ced1bf1db66402f2b4ecdecaf76d442ce8702ba18e41e26b0e6b65a48b9d46b6fee2520b9cad271ab56a061c6772439e73e09bb0292cb9315e2411ac5dcc9b2e6bfa5feea2ab8c3eb3ec4195ec9675d55217f7e992c7bd5d00175526e2c0a5a5c5415950b1f8a40fed76c646744f9c73d9919bcae829590ba68660dedf6e2b4a3ca10f8dd0bfc7df8282def6ea35c8401993e97675fdcf03ee43c772256858ba368efdd31a2d8c84d07d51cfd8e0a71b23e6fe15e0a87a616c62875570c4aa045e47d6f27e81352193d4fc369be09242d7d080b3f35b3032323df78b4ed3bb515b3185e96a8c3e69641b159b93ae1841d9da713bae21976c7c3ccc881ace9092bce98c5f19c0d5b36ffbdd79e0a39dd31ab51621e58c933f9fc416938bbcdbcc067e5c269c6e75bba39ce6640edce36e642bd3fed405d509824a8e8e9838faeb7bf30a724ce633f2a259674323433eb4f29a70fdea56d30f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"902e0dbf080f1a42e96009ee7a1e7c8cc8bb37cefb0f3016a1c463ad72f0812a","proof":"1e14a1ffa05d78bb818d124e6293da571602108a7ce80306138a7c6d157793321edc5d0f43c3c12e1ed952bffa23ec0379434fd9381583a81af31b496b5e0a31060e34e7cab06cb21a55e962821304ae769f621fa4d6bb159899624c6ac2fe536ebca656a49eeae6b3eecbc6deb2b8970eec176ad994272a61f167945d82395188ff7f1bb320a76256f804c55149cb006baede9cacb3fbc0eaaf72170cd3d509ab914729e20a5ce83e44981d08b8c65a0c4c318c756dbfdd89f37db47087bd0e36cd9bca691f88f8bfc6b3475b12c200ca30a5f45158206ee97f66b88be8000d3af4ac0496619834a680588d4eadca31dfcc0437080618e74c900f7be0f04e487863cea400e49c4315ce33681f277ea7f87e7a19ee2d728e927ac25d9d17dc141893bc7db6580a6885e1fbbd58d0e5524a0cf992dac55e595f85048fe41d6e5790bd638bb35859489530a3792fcb14386e5a532ca8601fd72862b90dfc6d312e9ccd584ace7ae038008b32707b7ea49c77f4ec431f2f99c22c5e7ec04f324c5268052c14f58cac02b5d0312b6fa8cfe7fe2c728da1e18f02346e670adf35191ce064c61d52228ee49b571bdcc25a583f0804e54ca4e7a1064014ec55021020304e789e2c8b3dbfe33e611ef7f8278dbb92dc1e22c7156733c1cd15d7270fa86f94ff8c01ffdc61ff133297b9d056af5324a3bf906fe5c1fb91bd27038243a81214481ae20bc7ef02c20ab791b2384942e887200b9cfbc69c9c89e016a5a85804c4e8f9089c68be4835b7542bd65b823e8135b23c0bf5f275e71a4581c43b3f5282808d61a62090dac868f436cd6ce0ffb56e6425d0678f30a9cd8396a9a2a22221cd6daf7da2c9e1b3bcc1eada373aa01227d4199908dec3c2bd5d6f0016fa07114694daaee680d476016990b9ddd19364975c32c315d7b0b1d82b9e43b0ca03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8202e9a4838081cfe7f2997920ae34ad3c26667e8e16075a62f5278c8690821f","proof":"2ae87de8fcc878119905858e33b7b2bca7286f2ddb33bc8912abae650ea1d0086a188d8d4a7d219215a129b1eefa85d9d3aebb9fdb7cde8a2572ee990f246954f2128d51b21fbcaee0cac789e4fa11c4d8f53dd8c14b1e63b39b4ad7167a3949747855d7e23ebed605e390c10c552c7a5d9bb92b9ff0569c7e71c09d5419a728a392031e6954fff7d97505091e628a551cc39ad3a652c9e49dd019be76047e022c53b6e6c969f3cb14870a953c31252ae38ee7d4073babaf82dcc4ee1c131a01eb10e4531ce4402b35f76e46fa1ffe44e490685e3ff60527dc0bc4449696b3073437bb956aa6dcd8db0909299e9609bfaf514956585ae3f7bd7ddad7141cdd73c8e5f261034746fde923bce0197ca239eb8048e8b9e94052df749c53181a1532fcced0c7a209b451894075568eca31316e5b220284d3a7a94ed702e621c9fe5da689dbff6713540187c270644d0e0f8c529e87672a677ad5f5b98d904f34a531561f69ebb952f56a990092aa440376b6203df88089b2911e4c0a0b7d6cf0ae6560abd3b339bfda1931b8d922b16460970073156b9cc3bb9c640338957ec7b0616c2153cd0adaf35ed5c6eea9907cdd5eb1e53394f346408d9c05754dfc989604aeb40ba1b86c7c274929ad9af441acbecc352c3e022b4d5dc38be5fc1990b616d2d76c416601fcdaebb8aa73c278f151d018164da03b58f69d633c7df715117baece40ed81f044893085acd7dfd6328cc14e4012fdf8b805e98f4f4031340624beae80d2af6ed3774ed656abb2f5002e5dda235fe3ca57c4f304fe44b54e5b16561195bcda97e314f64fa4c9cc609f96e832a040fee704820c32183503f1a3772bbb0c2ce1ea9daa93979f248328fd4231bde731cffc57bf6c41e17b9660550ad743f7ab95791c8d4a57463f6462363a12edd03bbc9b453a741f41806c564802"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"400d275d302b6ff9f78a3ce11c29fd3db4fd68badbe29787f13941178b1e1270","proof":"6880e9ed55b83986231b6c417f48db0e53bee684492b4ac696c9cd0b3f77d9602af80dbbc6791e482b71e4162ae8572e5bee706fe2a8f25e7ceb52148cf3e55d161610da970a7bde6bcaa0a1790f2e01a68aef641248d360ce4ae9955215897cd0a4babe2e97b4f78b79916c1e60e3437964875d5248f74cd3f499f32ee208457772383f59631407604818abc5f1294ec2fb73cbc5b3b738259d2d146214a5068dea3300f52b99af4f5358082e1ab71ffd7e0203ce6b5f1917f41bae47663d0cf29a336bc3b6bf53769e8e2e2326f08d41b95440e27a5ea28ebd79fc96a8550568c3562d21bef8733638b72e9c24ac21107d6807c636597fd27db333069f7510e2021c4cca258fbca66700683f9a90539ae9e396bfeb8d2d29e24d827389e06940846d576f3e3c11964b56f2fb2fd53c1db075d2c257cd66dc25552623929f348a3b659dc5c4fd1dbcfb14f0987b7657190414092cae5d55fc30145e5c8dee35a819c3fffc31887b8b973db89358bb595547be787e04b799f0c862c08bde857150e40302417e92c5cdf7553180aeb21214e6613deaf0663599e87bd1c71baf4976132dc89b96327c9d58d7fa0025cba7463c94afa24831287ae011b58acc4d7f0227ac141c6d6a17edf118546005a54ad21f997ac600ff3b1d54bddd49f25a7ddc70227d15b75d30473d05c4c916599ab88bb7ba61dfa4c14c02f717568ca97bb4429cf8eecef67e0be1f468902cef5efe9c7ab875b2047ecf1278c483aada703825866bc7050cadfa914b7350f62def4f2c9f7ff490c8cd2f1bf3ebabec3419f4f9c8225865e50eac321b58cd961ccd9f736f22a998b7b6babeae87fe13b43b7e99b1fb358409cd20fc8ce26aacd22f1729f86a218347943a571f3b0bb11200f2c12717599f01018c9ad44ce526bb28896903057627bb5ee6c642f4c2b23809"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e04d8a4c548952ebb5d7762604950bafc3cf93cfca7935ea482ff99812fd4d0c","proof":"28da8c5d4ef400923b938a08db45cd496da5265edcddd70cfa58636a987e8849a0b91839dba5d1e0acc6aec4883e7f57ca0029d21bc0615da11e1a42b491272454d646a04fb50ff057944d57ddfd34eb190cf38c12584af34a83af89a1468747ecd2f241accff84e3658451b0584cf5ef1f9611deb66a38971d3a78492b63a230fbfc9a7d65ef21f348e63afa15c275cc909133672c87720622c4335e19eb3079c227e349c2db6b3a011ba7be2e41ad7d09d24395f850113b1aceb5073d5c306a77d6797952a9e5d22322d617bd1a91c2fd1f05784cbca79706d88aed0fc330fd8e768bf0c8bd5ac7c6a172b9a6e6d04758778eac91e8403bc259e4753a3ec20ca88897521a4650bac187052602e6391ac86e80f5e9c9a3481c34e706031fe0deca103e0efadacbcb6b0c535998394a4f62254c76664f1d73d3261370c9749251c41f4f0dfd3a707b5ccc9fe06bfe165d61af443456404f143995c28c290c65328186c3b34c0daafe04bd17d77e072f42db8b63f3b861dc75d5a0029eae1ff03c21efe1a8317d5904e7f6d1f10f2b6ec8a93b143abe36d2cd4cf623c20e7d471aab994217d9aaa65eb2faa6ae2be130e4a63faa17d0ebb0f93aceca70749c848d40102294e6600e04adfe9404849fa77c813c28da8343b4358068bd7166a77417ab668aca0eca0addb79a845f6f8f8e33f0bb3e6321e690b15f6ad06a4b62669801937ba2813bcfd2805c83c17742c7cef64d061d48f0e9c517c5689a2767a471222678017c8f073c11e4835daeb2e18771e671a3fbb68e8091b6d6853bda013bef47f915daa9ec2d830c25dfa0f240336708ebf86ca74133e2fc1bb225e823ef6225464169c7f0294de397c6eb53a654f6df065db19f5252ce3cbe19d51500526f1482ed6e74fdcbab81b3ec49eda1cda3c2feeaf13a67f01d3f9bf7aa7d50a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d459d986cdfc6408f821e910b429a385b1813a574ccbc98057a50e89f9f45d6e","proof":"24ffee5fb22601c1e9779bcd92faa09d67b70b07836b77f7ec4ac772c2868e5ca00d04ba43664c91f19a97f3e782b61ac206c9141377f811f7c086c19462cb12b2cf9af60ec5fa89be5f2111f8849f703c8970ac21baf2d85c095d2f0024442a1c33194fff3e5b2d6542492575d4b18ef3f8893b10e0837c45c9509abcf85f5ab0ea68940165b0cf5eee4b04841da62130cd9006b76c93e922833c2184c4ab0b8386c3b903ecbd036e9bae1c37f73696575dda5c3bc4268c9fcd46ccaeb7830bed1f8f73f34e63c95a6ccc62ee7db2b48a568cea546cd482860f0d6b9bc85d00bad09643566d47da1decdcd32cbec88ec17f90582f58d9a0906f30ee176a1c00faa7412bf39cab18b182429000644b56c72b8e6019dcb8942bae273cd685a602700e1465443cfb811d543a5f746b23516cda8f4a3942788edf794b4c1afb1102542a83f23f1750e368c2d597bef7fcb99e92ff0791d45cc0ec4cfba9357692372489cc6bf119a2dde0c4c9eab59ab85fc4f2e9219e759e8c232ee42044ede5354aedadedad5f815d5eb4ef658ce356648ea52307476bf205f4697f819191f033087e5a7a3c4fe580575d41061a5b4623c5b6c058e0e2cc54669f7d5df98d105b0806a6d3a31306539bb2f44d5b81c208a2c1676cf1a4981bb000c89f347a9473b42afdb51d3c818795e0aa918d20f5e2bc246ca98e9d4154250b479c9c4e567eb28c113d28223335f150c440cd546ae66c51d137ea0d918fec5a109ac63216681eea1569d403087c35bd925b1831fd7e9c9ab9c9fa086c15e4960416c9276940aa7bb0953ea690eea2c4565a371b1a47670215fde941569a28c150d59baa36627485200ca6e3a9afb86e51433ae2f73c965989cba12bd77cb56edac0fe7ff50c7a7a73b9b134a5dbfd39e248aac6bec492203d950145aac2047e2ef666cf0506"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"62078747c8c147da28d7f66435d3719741f012833c5ebacc4fea7952ea593838","proof":"72f6d366cbc724b7c91aaacaddffc4d9eefa2b283598b3abd66931874525c668ce7455ab722683008381d290518b72e994b8cdc5518851831dbd95ed4261453a1a610e3ef787c1a5cbb3214b05e9357280e13854c8be326fd288f07185caaa27485f18b730286ece16294c71de599cbc14007bf4591f86389b49ba7a84a9a21cbc3cfc56cb950df16c535b6fa2b3ddc539cabb01f8dd2412f578693c8cbf260e8f27042c8c3a81078bc75207f50792ce50701f03f05a9aeb467258b2fa20ae007e24c121151846f52c03cbe61bb9496f51ddc6b5fb36ae186f479bf7e00ae70a28dbaabb9e745fe9b6f99f660190baae3371814894e73bd69fb2693878102e6378136060e1dc5af20f99aecc55185a91f1976585e7460cfc92cbba37c92f1d54c8922e0f4cb5e227890abca7cb6f9b2612848010bad51f7ad962e4b97daa491e2a12aab05dc39ea7d9dc4fcc5d3267c283e20748a95efdaff5f82544e9361a12385356338b6ae54662d80c0ea822bad7664efcd094e4f606786ed1848b57d5011e8a9b78f5a34836f36552079700a9103025019f95471e8e718cafb73b314d7a320cd6c362a747a76bec7f4e5b9e6a642a250330c3bb3fe7c4a3d5bcd2733656f8da9caacd430a57487b9af3d03830ceeeaa069214da08209f3835e9f398bb6e169f035415934711aa29a9329a2b67ba220b47578ac9b62f510b2362667e475cf0712e336f1118a5eb7e09a85c641db69ee8b2694150fac7783dd83953a2332a188b09c795ca02615c68772e78119aeb1b73efa825d2abf369bfc5e365ecea64545bdd5f398e1541b4cbb9adc57c3a519f4413d5bdf6fc4211d695c637b51a2d2d619f90b48d7c941c2ea270fbd1aa62e6cce731ffb46ded628d10b0fe9c200eec36a87374f17473824a5fe8cf9244a0b1879dda94fa8626943ed8c2938cf60d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"90109dd1b0e96943eb52e4fd8c0157e11da8d8f95a961cf556b97c9c9b42c82b","proof":"ac071eec3e50c9deefffbb41e0c41e0fb3b1bdf62b4946fa708bad4a1ebf6f2b6c28b7d79521003adea64065ba11121096d45f6de47cbb7f0e64a5105da89218dac9273b817b291253b24d01d4c4ea18216f0b4097ca2b1eef7f56f8e534a97d9e0f5885697a8eada39072c09d132749a283be5767b167ec7039a3b22b1eb066dccc3455953ebf060b29558d4bf77e3c399264718b995ec60034114dafc80c000009717aaa40c610abf0e081555d2ea3ac529a74c976f1febc7daa96a9c1d40221257b9d9a795d3b3c43cb43c8894f43a9b748cab3ae77647006b2833db4e00db697bd7177b0511a86786ee97dad69ae636ee5cc40aa37e8fda075b55e9ffa46184cca5e7bba0914670e76144f03e7e96cd177cf8db6f6d51a7235c1e9708f4f36925ef9d34089efccc80ed9a659af655a43e88b92550a15de8d5834c26d9d05364d016ec7493ab7eb4a2453c97c53ac3fca21f2d8d18a490eccc6f58fc4a22e5c229bf70a0785af09b3508c31318c1557020d63e63a017ada19e4ef4210ad6b707ef93c81b02aca9df82f038566582ce5d94758cbdc0fa529f83b43a9e5413fc8d5c4f1ca280f2e9c66d372b08113530cc4e1d9be906b5ce19826b5411b592ffc7254d4cc933deaab57b02733211b1d6d5039ab36905ddb7cb66eea9aa2743a1acc99506d82567da44418a6903ab334351a86d90d38bd3f87b6023b9bdfbd5a72c318b84d161588a5ffef86570053c34d1514f2e4f920598e6a089fb66d38668cba4e4ed99137e52c934bdfd9dcc06ddbe8e7c2c9355b5d0050db9f1ba2b379686c2df4154094f946f7f99acbaf1e099b724a128edd60ac5dd035675718de7fba8012bce3e60e60b6c9740e2030f6718391e857e9b210c364b2fa4ab963df08be9bd36e6ad4116f6c417cd357cceb13690c949d7a0e04fc4f1a4a9ce91fd803"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e05414db3e58e0f3503f6dd0bfea37f8e36d12bf2454b60075aa96d1f1a96442","proof":"a4de2a181873dc19ed28606ad6d64589ef10cd1088a2511c118aa36c34f87a1fae38da92d1b278ce34b3f629fccacd2e15ce86759d05d4bdc1c0efd30f6e2450c8d362fd6657b825b7d8ca5cd013a60284bb8eb13a3c20fcf447ff2be27a12783879fcd523ee61aab14aa752e9c1403ed3012c741fb6a91fafe055b130cd0461faacf145923ee45657a5cfcf048f8af08295980dfc133faec2692cb574a9ea00bf14d7058dad04e7db21d69833fc94b7b83e6e1d38764b87e58f7afb3cfd06039bfc26fd3c1e8d8878120143a99a2408fea64f30cafeca107c4856c1c300160cc2300d82a6c36fc2b57f855f7f07bd210bb3a9d4f348b6a8beafd5cc26bd82425428ef2325e65042b1d7aec0065cc67ee45d625a80b24d828dd1a806f0ba586620e638a4d371a0925e1edd68f9fa4b7276c789c76c1ac6cfe3c29083e408882308bb6a5957696a675223b47e1c8187856e2ab3021daed4a98d8ba601d7bc855080d92b4393ebfa9e8e337a1cf97b6fc28df39ddc149c7c8b4da290c1e6e68a072692b93d79d25a700616ea1433fcdc830a27b8488dbc4a40ad972c70472fbf4882756e63c3f8560492100fca85809ebc969d06c6ba8da9c5b3c0417bb23df236261cbd4647d948eb58db7dd5bcb980014bb9e42b00b7164f69512afbb7fcbf17b6b1aedcf8a3f840a5fb44233bec883fa2cf93a863a13746ccd539dc192c3e60e03d5c88cc5f020865a0d8e0cf829f3e2e34a48fc972879542f3efdb68b9265fd6037fa329ac8c2f130678140248a76692700b5a6a2bfd1eca97b1c7bbebf130be73dc5931609c3ddc54def15abc507a781fd0395e9e8368a0815b787659a51346e8d317d9441a3e5493e1a7d10a8dabea624ed8cb2628a3a77d706fbedd400eaa35620b329091ddc373d477e962d0482d34bb24b7d11310ed59be63d9c5a20d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"06a7ad5e607fe0d6594839a32fa3efda204a764a962e5dc1e50c0a45decbb47e","proof":"28c20a54f89cd3ffa339623ba5668f7ba53d3e47d59ddee5d67bbba7765e282ca00092ac2ed3e07dcedb75e63c532307bbda88770317a8c8b254c8b5c7814a17025952f7e3f1c068d113003a98c9bdbc49ed8ecd3804d16684cb7d8987957045da4116cf3027d91605af32ce35d821b4d5c74dd68914358ebc61d8738297e83f63dd59150101dad16c3c76da5ef24d4b5a9786a48b5848d3248eb09cb33e060020cf67a9ce3f66ed2c44ebeb881c716573e770d35cfd5f2d347816504514230bdabc506878800289982b571b851622a409da2d0f2a7fd800b2b7a198d7f8b90d5a018ee93cabb23b30e01810d2cdc8f88a3434449f633dba83e312f9c5f98362fc974f4fc7f2938f87e39d043e90f45179d5b37a14db19becd191edbec213d671214d49427c9740e8a6152906e95dd037f228428afdab5569ba4b25fa3e2fe0892455e577ab2b6330339769c683314c0bf82d1c1eb915b2f2287b46d931d2b6bb0a2e8126555997208b6504a347fe0546a4daca7e3b7b5d2557db4860fe7e959f690034119661ec6fb61162f2b0812f189ff12e71a21e6f457f556b50ad17f6b54e4c12c1aaf8338fe10f3eb676c07181dadd5d58b3a7b35ff43078ccc5bc0535638a61817f60628aa2ef7f5e6494bc143ce9a5adbabfb2a375d046aa0dbd14294519dbde6f8d038c4106ce1b66ad2c4c24ff1365ab096ab7bdb9a436cda8f0ddee4690772aa9d01bb9d38e175108ea71f7d883c17f1fe412e16d0ac97a31a04ec37fd2f2d0c6078992b2cc71641c59d9c20fb64229ddd49259bed60925be070f0ac7b2272cc8dcec4d48f4f94d21d84caa5c8c734a6109b66b653b333b68d0c318081758d859e8f2fe67d17d8f4fdef22112904bada9135556fc4d173c52608cd0d61be361ec4cca9efb8897e08d536e0b497b724f2e899e2daa5ae06a9e603"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"de5d38faf15aea946e00a5cfb231887c01a749a7f5ef4b6b5eaff65c1dac494b","proof":"4c4612b2adab1c570b1fe8307156244f4b00bc0f63bdb818fa570bd66b3e4c4028733dd6d6d12a1bf64c7481d9b6fc78afb9399254d30ac712406850a4443330d49eb03a291a301b589480ae47cec484aae1845a24e665ff56b899cc7df7c679dc84ac01c0fdad4183c6dc0c14be3828ae5482b9b41aca2522fa898a7b87291ed1e2dabddf4c4eaa4afd748c26969cfb40a1a391276d4213e767ca55f3c0de0057e24c0db1cc08f94b6f6a916f0e1d9514aaefc37edbd8aa2cbac83bc9fb4f0cf98a09c791682c50ca663390b53e59c914788ffa4a89518c139f60978f250f05501743fcaeefbd9728aa1dec7c20b6286399d3bd2715a4b36704e78c671a683004814d4d6dc73c0ab0b339a607cc991bf91310fc305194882863c8f5851a687b6a58af1dd253e70bfd8ac21d6fb71a4af248c37497180f49b6e86195faaa9074f8375ad08d1ad3c60c8ca33d55f09b9853d3ef641bb880621dd936c82dad033424d3a6fbe09d0e8b76f29d004eab6a5813ea410086b5bd1a7d84bd0349cc963e8ea277eecbf47e20a9fc814e2025dbec5332a8436155486ccce14763c627102056a6d6ced4e84bc5f59e61e8e1bb4a77fcb1205eb2bcd25d89700d0e5d40c739c8bae032b7ca77d17d2143c1d7c7f81a0b60b6a4b94983337efd663f16e881497ea3ff6807f04e1097af93a62a87692a024123d26dd52e0279b747a8c62bf1521cc0156e7a6f7c8201edcec296f38315b31f507a457ffe7c929616d7489f5233e81bab629aba66804aea168fb8478222cce273ecaa70d34c9d73356677ccd145ece6d9c45d4a0d1db2a00bd3aafb5dfad1837375cc9f84a8e8f39333bc9bce0e000e9fbcc7e284f09e5860b14cdc86d5af7ad2d78df983e28c88a0b17abff90bf985a5477681538860d61e7ed2ee8a8aa2cfa6e0fc0033ce83d2e9d796e19b06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"caabe3cf3e187be114190161ba514713fbcce137aa4578923a13c3c2b2752119","proof":"5c7af5a9ef4a97cca0dcd73aa59de0aa4ff52e69fede71eedeb0a49089c8381018b487e15716ff5b5771ea819ffdad45474e8f34f8023e28d5b09a5d67e85f394c25867f51c4dc3414c972c8fe7ff36602c55f38226629cf4b9dfc9f936f2e1246863df9ba0c41c954df4f21f9a2bde9047afc01a2019b7d769f232d99274d077b441fe2b2e22df37aa169a53b50249a9669d8281030ac0e256b2b565dbd6c0b141071e0d9dce2ca1f9edc092745ac8c38dfa88b430d2db89bda456c4ca7e00aa8fb4d7f26cf3e46a18a990628c37b8bdc75e9987795d50cbf4c6e1c60c7670a1c56117ae0e07480cbeec0cf26a98e8a27a7618d8654bf7035a60de2be801024cec9ca2691e16287651083a514035217544332fc6850dca333b1fd9525f0197166446258db3f116700dcc4eef35234a38abbe664586b2520a429baeb7c020a4096e79a4eb4d52bcecd0b6f371d0e3370310b913908f21e120b5925d971a943133a888093b710a49f422e73f5184403f4b61a447d54dae90803e8f5f4b021e41d6c1b2f8d5a104a04c05c49cde6f5140d49199b270e3c2fa456f998d99598345af289dfc389c923aacaa067bbe79cc7aeb53e41f191079bea1b5b573e5532d05a7422455ff5fb9b71db32420c293c3ea37a97557ff28d026e7c1077a724549c1f0021ea9b13a171fe5cd24db945fe45847c29c781f6c84197a5edb883693b9135b89a68b785a3aeb8e7c8cd516c60eefefdbb0e78a58e18e4fbee18bf499c081b46b3d97895f325e3aa589ee73e59336bb8f7f281ef5f2dbe641b01576d7c5a60ca5952848dd275da02e869d727f140e0bbc92e8967d7ea8122f7e913cc5ef6735dfe7f441561af1e9734584ab14f4ab5e30a02c079d99288cd8b535881cfac0a4db17872e444b530dfd562b9252d3d19298b3bbabf2ae5b7deaf10efc2b69b06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8e515582166a49326ce2d865f454f8f913b72fa7aefba00258f000cd41fb701a","proof":"f82e2a1a3493a81f5f7c632aca8fd9413e0283866d983bbcb32b46beeaf2fa47a69d80865bc7ec5f60fd8a73b746467e5c565c3900023aaf0567bea54add253c5806a345bbf867104a4962724f941fc1b78061a51b394858d9d303b6c9069c5bc6ef1d1975e1ca1793b732c09ae7385b6283b7cf75fb67e1a3707fa1b5af0e45d7d53c1ec8665a962caabf7731f6fc53bace93a186976217ad5991c3bd6c9b07bbd2e45760e940a4697cd1e9fdfa9c8660794a21f8e952d888cbdfa7bb20cc05027ab51d931da301db42b8e973c89e70b20286ff9e05968a6f38cbc08e1225048ae00deb41297611040ab73349b20f1db82d8e7b24530d74b46089466fdb8465f4e77b71c82c0ad803508bae3528eb0e3141522b3c776dd0fc6574859f2eeb4692d61bbfba38c4478fee7395cb28f723dd9f0aaac59ad37841c3b7ab2b592273eada997d036517088ad5a991bbd8ba11234eb008454e12e39dc786d8ac40566e0c0e2de56bbec8f6022edb88dec1232579c9de1b8260b5b7b6deceeb9088a315bccc8a521150cbb8da710a9b669a8796c1626122453d7c1ed9a3b10796877455b08c13769d92a91242fbbd14a6f9de1841e752b4547581325ac5a6cff3e4514d1e83d48b88b0dca1896705dadc0d7727c798b17604bd220b3847e55280a7f34a701e295c9d2b36fe776a2db1228497b900e92c2635366cf049d35411ce6b5f0a16fb45fd5277a3e59a35f717377751b8db53c4acd33abb117363b60c84c29d3f7a105bb68e55c45db64995757b149b8a148f468cbf19a893242e767d4cffac05bee86bca75711d738e38ffa98e7dbbf29445dd03b3466764bd33f3715be2182e67719bdb3f28366c0ed46758669a255057dd24fdb24070ef0b1feda04470550c0523ef09297890472e6a526963d8a6404958eec334bb41658d4089735b925208"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9e02c7446e7492426a6f67523819759998a09bad7b985475da21ee6c7488c851","proof":"44d083e9139808239875c7bbc43e4f20cbeb814980df4700ce5aa552a4d1600b508c38519047ccd5aac67204a62be619730bb3bd32022a8b6ab02ee83683a77d44ab7acac8bf87a6509940a11c81fcbfc8b8e17dd31ed5247798c8e7f8bbc97ed26fd8a268a64193842f8e13b2f238c0edad0e8371332f95e2b352b2205ed5443f2cfd8f52d04a2226d6af5c709805e50d95a3f2eaee3c2b938ba2b81c8560055aac3b0236ec711f2d7fab75bbd661777f9dee586210a0099aa5a3b7c79f5508b3444ed151f2d943cd50516bf3f448af5b76babb839bd1e1696f09666cec6809aadad3f9085d5e8c8de93cc2677c044707c61de02c74aeb6c08c71ca7e3a017026dc9bdc09508c9494ee805fec565ea3e7d121d4b2d88b0642ec775a8fea96218c0866a4325ff498752a4aeda2e0621ab58c49804abedffebb22034b526883149260b237982553d9b40a975f02e59636a872c2da50d4d303a92cb0e815b4ab27365236420b7bf6c6ff086636ea5bce69adc24304ddb22a6471408b60a43f6e386091da2515b87d3938c16aed0d9403cb6ab5ba7a8e6ad13d7c9211381862493790733aa3cf03a6e61baf6c8de4e373640ce285d52d079abe1a309723c2cd1543826dd56fe11fb8e6d17964b8905fd14ad4d8f4363fdd0230260168eaf05fb223462056d3c48b8862366c8ce6066ec51d7aa02615ec4c6b3b6c7f6ecc8a518f0fea260c520d8c04bdc4d3cd8f233e870a4a6899e50d12ab533609732c22b54a7f04602a19bfbc9612a84769e76cb9ccff1a7379df25ba2ac4b1dbed897fdb9b562a79e88e24891f8ad6a0034d3f76c835ffa6137901be53f725e9f9911ae1ce1e2799ecd63e0dcf71de20154f7a9ce48f7302042e0ed445af9c573865c6c5b50f9a36965d160e34322c5380c9e01c54a05c2ec1e0e9e0ba9c87673296e975a80e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"92f96223340cb534ff5ddb08c43e8863b2a405f654e754320783b4b2cf2c4061","proof":"428a25e5e8c5d40335ab9f96c77fe46c0554a2085f544a607d61f4e7a5170c292228f5be3c91b7908b46518beb43132d7ed7542b0010bbd41abad0fef56aa14ec8649b005a229524e86652961dc6755ca6f4bc95de71a9db6adde9640a15c17e8c2dff1dc2d54c8fe074b51dbd68bae1c37a46f198e8f9da55246ace2be4f86cec3ababb4be0d93e2727055480ec9c52d089f3caf31afef5e2dccf496c62370b536e4891e1c35b1141a6acbe14f1abfcd671c5b5d72b6442967eb5eacc219c06e93793567dd345ebb26f47ff4a9bf37e5184baca5661ed56aa833e9bc84fc609281525409a40a5792c6febc7d310bc4aebfec293b6a5d6cbe440f380290f210e8caab2bb6376a415a2df01f7933d4bd75ffd60e951a6ad1bcdffd5d1a7914945e8cc08ef350846179eb69d7021d466d8dc272f1c394dde1c1cbf2c41aaf17d389e83f32cdfe17a435f4e7140a5394fede43cff71640a0feac482dca993df097932b5dd80b02f209aea6f289023fa375d76f3ab8174506cf2c012eea6be523e5634c393bfc0aa03011978b19f28921b70f1d8a97d49c2b9975f3ad46392f5880530094a71e2b152143516abd192dde4db94e04ff36e08f33d77bc32a3070dcb4578315e030184c7d453e55f6b335e129b4e0da3b5804dcbbf6c9b1014c8ed847730b9fa9ae0bc7e4ec33803205d213cf6c79a6ae14edabb2172e1fe2cc270210d4400a1c13afc274d38d99ca91614da92c8d231b96b8cef171dea4710646b1c1ca46bbd48fd264fd7dd28087f297264d96b29349999c676ea7e2861a7dbfd4a4d2027551f3326625958338ee9136e7613aa1cac71aacbc6d307a3914d58968566bc9c006d6226bcc1c2dd8f3c8b3232bb277e81b3856cdc3f9cef00e55f1be60c6d94114f6238a209f715b4c57f31563aa51c9bd82c3dc22b085a9e639be0ab0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"68755711fbf45308531d7e04c2dc88a712199bc53e2b5903205b5421b82c6d07","proof":"be35a571d2bbe52327abecc99e3ffba25171497d42ad91beac8e683a04256264b47362a247bbb4b5472a3893750a80cedb2ce54ca712b670daf17e064bae6539e2b05cfca207b8c97f087f4637826b03b73c85d887295c16fc196cb9ba55fa39fafe1222e96d74546cedc18455a0d71487060c76baa399025650019618ec434dc4c27066a5116b2050ecc03c3af117488bfbcf637ad729f9f1ab6b8d9920380442924d2c8656851bca5779dfeb938135200467068cb4a784dc0e09eb614e7d01ff03f1f5c6c29408ff8ef6fc1df36a8143ecb3f21fd89fb2eef65239432d2009a8eb36fd85af584e57d1fe9a83d1c684c13a31ca6a2fa0ac86312090cbb8706d8499640b1c998216d375f9835f7eecbf350934a1f00e1ea2fd86fadfaef2c4245296ff4f65bf82d027b7325ccb364af907f8fa48f24db9850f60d376c2be0a063011ea8fc5d912aedbda7823a5a0e58e17fc244d1f66f3fbc7b8269b1d8d4d22b051c3b427df040922d9f344c9153e1ab4b8028ba440da10eec98609be81462934b7fb296930ea5b3afd320f62e531007aa8352bbe0e35e9b4fd645f3e688a10603621822fcc5dd5e5f5fef18d9db2c96454a545dcc4c2db578937c84abd4807724a33eca3580df14b43c5cd9543bbc0133f3fc72d704a0b3987345f758bcd1004cc2102adc285be118ce35f316ca8ca9f2812fbc7f373e9ff5b016b31b9cd261ecb88c952a03443a1cc986f2eb16802a6df05747b488c6ee029288b485af142dccb6fe597ad142fd778aebff3f655a0518451a5f89878e4f5aa48a714f7d75d42115ab837d196813eacdb22a60dac07c9ec495cbdd70d98be3b134539137d3ad6da26c7ab164abcbdf3aa87a3f180f6fd8601cd0b4eb6e935a3379cd891650ed686de9870ed2fb03556676ac40346169605e38b0ed8d2561d4d0a31709fd30b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"420f10ac89b909e0f3f56b3ad375318eecbbc7dc1e7aefba5b7a677585a53337","proof":"987e81fdae2172f3e5ec0be0f54fb5ed52c6a23716d90846394c23dcb701ae16f68010356cd800cced52391342bb1ce80e83833d72dbaf94a20652a6be5cbd4192258a3d82756db9cd5b2cd895513ef63471e1c429879abbe9339f06b28ee82cde752e92566a81d4edbd59599a488cb4ff77b8bba301842d8f2108453be5aa1351ced17af9eccf1fcd0da44e3fd1967f2bfa2437bfb441f054dcf43bf8c86002704033f82417ea23ea76879e5405c19ef983669035b03921fadf1ee50c9c52003803610484c4b57a8dc56387c61a76e2971378ef14103b237b468d0044d4e30dc0796f849c28270b9d266bd5ec45e3b5f102e1ee8c771c4e4524d4630df2330a98494784736dcea87972c1d52ed3d9c6bb653db3c16d7d67ebb9b9e542c1265d764ade0c1c212b695b5855ea98706e002f444c5f25877ce1085729c2ca9300503c912a37f4be4cf0bfbcab03c88613af6729a51871272c274bed8d0772c8576266e247fb45a6fde0825f762e0dc96a784cab82f71d42be2f3d8d8d9cc1e1c4431210525cc443b0fb04ce9361f660730438d751fedc1b4b7a4d3ed1d00e73dc5eac8baea4334416dcd8ccb22f74bfc4ebfe0afa846242889844c9d8c7794a834fe4231c7896d2a1020d9a888e151fdd9fe7be14114ec29f0fe933bde96cdee229caa34a4cfb92293736b798a2089821e6bb9aeaf71521151331b5240af0f234732e4da269d2d586b74295fe220f1351469feab7e02ab9d60e3bdfb557b1db417f3696d88d2e37bf9cd644644e33e87cee49844ede7e13382c9cedae715858971dae5389529f2f4349fa27eac6fc3bddc078b82510b46cf13ef4b2cc67ac3f692be34a5f21a786cb30f99bd0e3c54e61e7dd3993268b1239e3e1309d11f77f3f0acc2aea2224b512e12928eb0e0c7d211bd64fa48fd6a82bde98d6e7a7ed068c0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5cac520599db2422a4b6168a86b77d2e77a47b89f73356e6fd7b0156c3a0192d","proof":"3278b9761590d240cc4e5a1b83d365b47d076e2c9eaa74dd049ca236ba39b37456e202f74b8b80b6d439857aa2421031ec4571fb5cc60d14519ea0a0d745c04ba2a0ea020e3d4912ad7df89945084a948b0f85cf2f9ac4e21f63915c4b581a4042329f308e5904df20d0a9f19ad9ffcdbcbfc6f465ed4982403d3f9df372ba6884784410450f2052c78ca6cfac0fe417d9f95019320bb3b501a95044fae1d500c4fc3ef7a52ecaa2d0f728a9d8003be3e764887ad72390971aa07979c7a8ca050a12438088422cabf004d266b79e70e266f9aa285ad5e30918017cb1f2edba07927cd95ffc58df4c224758e4f12ff298b4887d08bbb9b11abbef4a9225e4db6d9265de16de9e0d9e8e534ecc1bac6dcf894eddd3f548bcf12df5dc7edf25531e767ed7770b4ed349edce2e6d09dff4414d8b673143d808471fbf3ce8210b3571381d24701b1bd403819123ac906ce66394fb80c248676fbd9da3e3b15048f55f12a4bd4c14fb21f9c794d742dff25513a81fdf2d07d387a9668215524c9caf7286a71c95c7b5d2544350924ac64755c413fe2d5d7918f20b6a2caa750af35e3978d5f58a437b46747efa3efa2483838a85f06ae69810fba941463fadec534a1fd8e1e1e4deece81a15df6dcb1c44374b9d5d808e07970a55c2c33cba66a80f158886db5804b70a2b7729defb6c4df256dab5924e993b84c1203e8f49c0b5fd4378914b793d4a8ed478b9cda25d7bab7101b8d614157555f2399181b2148e9d7d8088fafb4cadafe689ecc1f41251dbb2ca14f95c8e77b3cbbf3c98778dc0342902a7ed52efe654c2e2e41bded5613a590f6283696fe6bdd6faa82fa9bfcb262fec633cc01a79aeeb02ed3deea226c5b7a437fa59cae7215617c6e8341347170049e49dc3f029374aacba5b373d1192e38c41d9a650f88747ea29ab354a5b5609"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d23e2996cf59ed55ce8e8620763c36df370a3358da943e06b4efaae1d61c3a2c","proof":"228f4dd580e8fd76cd615f28860a280ec7ba0c94588d27f25f15ea7aca61692eb6808b47e5b56d6fc2bf6fbaf546342d1a75ae1b3cd861121164de0297f0e87fb6c3fd136281294abf69dce635588a496e1702943cd243a0f3ffa23ad168173dd6cddf20e2cf482c4a18e98b341a36d8ebd2faa81a502bde02e9cf7e04b9bc5ee4cad9b8c0f801f52c64afe76d7f2f324346ccc5e45e3ae4cc13d6c994b7bb0477ce766cea899b2c68994323a27f7ff3ce6e313dd248a473000f390359ed2b0296ba87822e9cc7c0d46641c3555395f3cab98a16804cc4b9a841b24593991c063c186f062103fdfe0a62f91f84ea9a007046d2176b5c745888a10c2f57f16a4ee0ce5d266f024c213a51f8a9c3df22cb06c24559ea247612bf26a1588c57e27d5e53cf98f9be12e6fb02e23ef7e501e9af4f20a669f526f1d1109fd9ab12bb4efe4c2faa8c34ba4eb1a929ae87a84a73b0cde2e69fafd0e7dc3e20c041de18402efdeb334d3c04c738db975e4b8a187cedea014c68e422229412de128e3132447e3fa6e0a0335715caa63d83304247eef3597e6bac63f8a314960f7b2ed6ae6a54055b12ec91f51a1286595cf60578ce494d4ce460613e7957fd95846c33f0008428d9901bdbd4b6664b12277adcb0e35952192f997ebe1c5edff4ca1e8792641cf031d3116ab18f8ad0974d2b9f0c3554afb1e1759a11abacebb9ef9b157c2a4012b146792fea18e5824b2dc70d92575bc7b23881b01f620bd6e701ac2f873a26711ee172d1f1d1fce24728c318a184791de41753d914e34acd8cfae76b653ee43e7eb7492cf9790d8731401fec288e6b68a7fc971524e4336eb898d765d91665131d6cbc7846642a7f801a640a08ae770ce2436e5c8dd9d98847bb05f4e509429c6d78faad1fb3dcb3be7945ff97c9d408305c7ae31dd2c63c756d6ac92f0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cce92623c4b06bc16edec28990487f7cb8399dba495fbc9a139beae09e11cd15","proof":"322f0a5197e3a1a5c9b703b1cae08b7c11511634a62e6b7fab78ed770dfd993568458db9d8211a8ce162564eb45f35c914ffd08276f50f6b915780cd4196282462eed22b59c66c4d9c8032ff945990e9970d309b9e0bca889aa0603ba6f0d07c0ac40d53fa90a37b1e0f40fa044a41c82baf925ba9f82d99c371774e28b0e668aca2d58d5dcd34d5c95eef6187c817f1ba984ac4e99bdb8eb922ecdb26ad220f008267d4dc524a4190a12b77a377f09fa0303b819c34fb491543ad7da0af110a4a65a441b828c5d9bdbbd7e5730bbecae7cd78ff77b2a1f1cf5c8d4900798e009a36abda22ea16c0176e9e3acda10e1dfa24308492d8b0e8f542b547c4a4863da48dabdfddb0cc53680505e023a9441f371404ba35d11a177eaceb07ace4db4a34017d257950ddf5a5d22f4df5ca58f8f73905428b2a08bebbb694b99b23195c28ddf7461af05be918aaf4fd6d58cc1351aff202f12581bc6343eab5464d2a13b063a7a08916bccc7d6d4410c0216297e942533bc38671be38fdd2a29bf82423f0f617870635a3b678cb13478d87bf78abfa79b8bf02ba2a930647b55c268551e8b9309baf7ed34692da9bd04d37656b6b8e725b8b36b025835d09e27612ce11885955d938a1b423cbe776fdf797d0e3b8b2ab201a54682e79ef2f07db9f67058c9cda3c871a920ef7dee02ffc0e57555576e09838f6a5bc474e5ccb5a62d66b2247e1deaf18e433701824facc38f7981f0fe5a6d092b966d0423bae3de1250278c7541f93129bf695a1171366780b2385f3a4b8c60fd68ce04ba5bf52c759640ad4c0b8364d6b621f8edae7fcb33d2611a14fb2c6ea6a6da2aad5f2203b29406727bd9d1eacab20a73be03fc8775fe63445ee5dee5673e22c973a8b8b95bc0d7755cd48759fe9bd78179bf1340a44cc856087b394a103a41bbe7bc744d5350e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3a9ffcd1903f508c32807c6725100945b995e16de38c3551c5a3afbbc874eb5c","proof":"fadb770ff3418c9e613df8ca2dda35b9b2190325183bbda2dab8021b8bdd570e9e32fc57880aa725043af6a5060730cea6a0c26785e608adab92064a7986002d12d56a185fc8b220f14510afca369d9adfd94789605afe9e7a4536282592c75e08298b823a27885d7d9a3424be66d266e527daa6c8e8ebc5e15ff30b26ca0224664562b9b3fc1916b038275dbfae458979fb0bc1cda9f63d084844088d5c960749cfacf851a3664423e2f04de516e7ddf958b1de8c4c98fe5b68c4f913dbb50e1ad07019d17210208ae00cc519f4f4cf9d8bc98c5c06eb200c284d09155ce8004a31e8b72bc65bff408bf141b26b6719f6cd0d1f1d16c5c62882e6f5afb7d60394f1265f3158a2b2d3dfc102e7172a0695c21514d0d287161099ee434f2d851ae4da998dfa2d5d21d39d1aae9566f8cd69d9bcd29eaf658dbfcc9461a1ad1540ca32c2beba20fc743d91f705f81743561718cf4dc3321344487cc2a6c566b12308cd48808510cc64626c56c82ada9e60818da1743b1b764f1f26aa1946467a311c2cbad980a59c4885f97032dcf4054d11b970e09b0847b7bc1853da3870f428ece6a5c268325286fbbb73ec66240e46125191947fcdc49a057a94fde73146508ac02a9c524cf88a63233c747f56f43ceddc5cd4fb2d63d59727f494287b533b161d229c5bc7b748115f35dc8a1433f7ad2d860f05ae39551514e0b1f4369356fe4aaf189ced1554b3cf939dedbccdfffc29ca10a980a179bc1843363439815d2ea9d382e6cdbc0ebdc785157b3b04b10de920d1dcface074785b1823205ba2124b786caff8aabb6de6050d1cefda17a4dca5c0684a6ab315307db106365736b1d533b9957fe3da7926d1961de436e2f6c95e94d8139b947e6b32f87d51a7907025ae368ac72424152e813718561bb8c707b2aca25d2bb686e1e0af19ada4608"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9c9b6b4d5427eeac0724ba33618134a6db4786cbcfbf5bc6e22569e2327ba13e","proof":"04b8c58da071cdc3dc5c6fe32a45e6b581a52bfab393949bf457345ec8244d544af44dc683cd6fdd18263179c019b3d7d0afe6dc1b9d9f17ec4c815709b7e8794e9d9e7f6b099c51549f6eaa9fa2611682f2e049ee6d9b1dfbfcac529f56e3049ee1fbf4afd175e79f9b004851949936033e2dffb2855c750705af1720a149201a699b07857699a799517ee795a643a8144a2291c986516eb48f234e681b590cce4c8d641c3d3461b1f335f416418a6e59b368286f69ecc642c75418676e230b72ce7ca9393ac51eadf14a2126fb930bc91d7a155805211169d5226bd87fba010427c0a0767960240cf7514db12c4924fc547c3441d320d4cdac6381afb04f7dc462d15625c6d22249306d5c74b6f01309df15df4cf8968a22eefb4edc4f2177b402a3b5a4db0afb281cc48dd4e6d0f99eba6aeafaecba6bd48ee923f8c3cc5076d006984afa572c59a3a1e07554d7c04d7640d09b7d245cbc36f3c2eb95d716eef9c9fbb83f34aad6af1a652159b803bab055868569f3538810d831292404191044fd782b3cd6469e4199c5131195a1cb610eeb5cdd2711cad0ff6776fcbe289c1088da043a6936f2124dce48dbe2c1f8a2607923c1188affec7e10468b5f263ef9cdd4f677bcab87f43a3a127295ff62debd5f01a886ba19f5864a51b66d6faef3a246723ae91b9c4d448871641edde445b2b8437a7db27972db7a048de85206384228875e5842ceee9d38587a8945c8883ed56dbc18ddd99b36703acabf5520b63ce327a3b969f4f427062191a26899fbffd8117dd51758c41498b55f7a2d42faa2d91c0428788f4c0331f0ee2f7be498b734b8a13ba7c2091de301427b38d4bae4319632c4e18d8adcf3eac0daf46b6f0577e6e2508ace8026d8aefe1701cf60e7ff76dc07d953ec7508eb9ebe5c79def1d455a2fa3116b91686c7afa101"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6090b03b6a1a05be8038bbb8696ad54c818875df788e54cc304f5d0787d70f47","proof":"ba02ae6110de51ca2179a07de8b623268993b9ad7c7bb136c99d38f7b5f8fc1072acf9fece0c983f2ab59fddb76da78266c017a74df4a252c707f918dc7c846900a60a9c0fdb29b3c2a595ddec8de72eec7620d87e374654978425a62b59733b162d70d31d71af5b5687201627cad79420603fddb01ebab2d125343c2bb56535050cfed15057890a4cb46c6c5ccc1cb5274ee73564a8038756b8edfc450b3403db377927f05c2b5481967c21dd88d1ce6ba32ed1fb3ae041cf643449891af50f143026b6468c1f54ced684bcfbb786154c404b1307cf6c5c73f0f5a4f98b7008daea83aabd83eafdaeb73081668ee006258e5da34e2f2acba0bb8b9d0a441735b4a6517b38d447cc213e597a00045cf9426affe3842260435815e98c3a1b1e77d446903b27f7f3d492cb41bacca820adcd0ee1620745165f0bbda749b1778037f0dc4c3f137b8c69ec424787c4915508255ce89e0af8534df758fb50e94132485697c56d7947766c1ad5933c8627b7879f1994007ff01fe4e1e978e81aa7533dae556706651a8a99d91e1a3c38f1b16e9249a30180b1941b2b5e26e0033d6014169cab5d0e3807bdf5645ab56894a75279f6c7d67acc7f815d6e6e1296840b06ec5d727f7bd1e3dd4caf0b9cc8a850ec3300ee8fc2ff88ff00176d647c287c596aa5c3e235114bf68cc0e5aceb7fc09fec5d156ed4d93cab3fd85dbf4905bc14c2f60453d2f9d4d25a4371767aafc2a55367b77b56de74c797d88c4552969f2dec53af8a769e9c858329d1aa01adc23f8d6ca165af21968e6bed92da1d1cc9218630dc3632233fdaa1e9a3c3fbe77b20e48d9cadd59f1c1feda1a21f2820b97b5ff00d73d35b4824a8a71c9044b174030e88a2949d96d4bf3f14064de0484608dd14affc0fd35ab410f590760d0df67022cb1044a9ef572f066c4f5057c75c0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e6af5fdb7338cb6c287a833b907583395613f485f843c02570e75f31e0407b37","proof":"d4c53560c43daac6cbc4ca30479083076debd9aeb72abcf1f52c02084504ba108eea0a8d3da901a2e7840f1199cfc1ae9b22eb919c4e2be53c9b50cc149036379ee1c0038ad22e9a7c95980316de7d3fe15c00be408839a51b45c86848d06364f696ee735fcc17f2538e2499880d2d950418fca54280b4f4c0a8c608e5ac6170954aa02b2e369a153eb1834fd869bb81e54d66de58d5754ed2ddf32b1c74fa02fa61df414e9ce7fbe74d860974a5b26d9e1f47202631d4dcb0b98bc7a477d1091d62f97dcadd3802865254b4bd5b3cf451c518c772730e367dfa3db848e0bf09784df7f297d2629f83dddf20233846ff6ad783c4d9eab770fb157a20fcf20b2768bc5f643decc9e5f40ab17fc60e5fef6f40eb52a1a17e30c86761e647d29865da96b5a78950ddde397bfb7dee5e0e2f0e0c9e8fbf921c3d9a62a3a3e040e065228ac2d405003d1c69a195a39a0d5cd480ae37471a536f9cc75225c7a54018745e1f90880dfa94a817a6c3116753402b271989d0302466390c7b02b29d15e7174c677ed6b2ed06cbcfcba0a6f690ff5d319182a1042d1c0dce4a9a5426cc3b41568500dc5bad20f952a9ca7fe7bb0d3d782cb13b4bfdc74673ceb0dec219f546b8ed13c96b4545fb331a22e770d9d02e3bbcb18b9fc65eea0cf9ceccf7cf2475a8aa372a999a382a82ee3c702a61cf9166246104341f3d198a97119ada879b4272895b51b939da231f8f6aa24291421d40d85c2ae25bc4e0e8d862bc82fb733144e8a421e830153aefed9e2ea4b5446e4e0dcaf4210832af606e1049dd304737d87fe97a9053c807f8283f6937eabbc66e9bdea712289b54bcf08429935a316ce7d6bf2302cc2ffd436dee4aec957e2c4d36a1d0f73e6551095cc52e4df95f0c8bdd317f3beae9b497895a6d8cb710d6691a9aa14ad1c9720b9e694be747a20e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"acada60a8347c380b83aca4fa4f098b77eae5da9cbdbc7ca938662a0564f3062","proof":"d4580a2bc3bfa29a6ac53665976df8509b68c1ae105b82978bdd78b85342ef7ca4d1137fcd7d9bf4821a125086a578fd56a335e80a7a3a9f57a9ab2ad9b3eb160423a0c062438733857f9a17cdddf99fd23c2d72ba9a88d136763e7e60905f26c8ae13c078028f22e251d2e3d9240d95c3d29071b41ad42ac1633240c7ed4f68bb2237c1ebdf578cd9cf448fd29ff7ec9b2812be50a40f857df7480e9f1f0a064dd1be76a1c18d4ddb0c36342fa0c73bf2d30bdc11c53668679d7e3523cb7e097a7bff718c9f2b94500e1b9b9d45401188bbfcec1fecc6a79937145ec018d206e69b34e845841c6b2d750ffb919e86f5696b4252c13f68cb5f5afa2dfce43f486615ec93691ae09609bf412b2f600deeec24f2398ae77be135ae7366a4b02c130e3c28a3c2517a64696349e16d71df7144ac36c295282f7a1aa375405e23d73dc2244199e0ee22de8571e89dcdf99ade4596dc65ac736d5676bd4e752b1f7e3ea259926634beb8247f610c38dde19587fea37048447c4a445ca7cbb55daa693d0c09f5238496fb1d8e0f4791e89a3e19e779bbc4c87c4a8b21fcf6d0179d1701888559819ca4e2d42be07448ca16ac88b82b6aadb560e1b01a88737b42352b2fbae428bb802e31ca7d655b2e9399f22f768d3fe5bdf8e1230cadf88f845a4b116e41a10157d7458d9bd3232e5e990eb2db16c7e3d81916a112984bac7b99e713c881d20a2e3c574783fc1bd5dfab5b8e32b4c90fce9df51051d356345dea5b64e80dff3560ea0054f13533b958abd7bc878559c4a1de6cd19306f965ddad9b1aa21c64d21c9bc32a8607cc43651ec1be7850037a00960f628616c594838ff128054ec9c24b6d3f3d967c6a609c5d5c46c64c00d2c7e95a0a4cda9b196152580a5b99156690bf6e0eb385c9f25b6d28558c44e82618482785a30b490ca05b3007"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8200fbc4b8177897be884f4083ec751086bf22d8f2f7877b8c139cc90a423b0c","proof":"7206e6f4a89d257ba29f284021c255750433c7e23a97d642bb4ee3c61b50e8530cda0fd3bfe3a1a9fbc9e39ece2012f07cc9a7357b98f0564ff881762cadc318cc6188dbba3a2156be22d651cdaa4d5c44ab4f4c3083c5ad5257831e1849052558fabeb83ca8b00654d202137872a8cd880043f60e622ffd628cf3d86f3d1e71742e29f8319377075681fcc21409ed990ba20020ef831051f4cea36cea2d5302ebca5f5a7f7a78ac7b39f16bf3d610fad75778007a1cf44d98577ebfcc6a9906d236c16aca2571b15ab7fa3cc1fb45c0257db65020e1e9f417d142d75e4ae304dae8918a17db5234456f9d084f7c1cd23c982756351983f461fb243cf29e2207089736eff607290adedaa3dc51ae46392272d68e01a562d26f438ec8e05fd63aca513075ee08a8d0be30eea31af89e39897d889b6fcd5c42c65d1ecfd067b44398cc189927c14ef8d103c09fae879b8e29314936ad0b6335d11cbe6509770372f09356ae0966385333f0d723d1f489ba5511c266631c8cf28a0fb8bb0effbd4716d2cb28d9f021b25735b5ca677a8d6dd288c457e2978db4adeeb9f2f08d6d7f76d5dcab3bc2e877b9de7c18a7d2de63c7b84230ad306ae620832e62fd61ff705e533a3908f8cfd63ebcf1245759677755ad68c1800830f79d80eda3d7af7432e00ebbc99802f2501faa3ad811d3130359f92aefaeedf16a989c978b2f0a5b7c0809de65f4ded8e7c4c44934c1864fdea6523b27f89b2a8662ec5335c31c012dd0594b8862f7e33ae6129ae4a813773dc3245982624103cbd46ec3d60abf6b7ad8f955e77c3695eacb9f8808b722d4a8c6e163304b2922176ed9b1ee8aefb618ba2d30c74b43bfc2aa7be9ee5eed7ec4b0a4b1abd83e725d356c9eff5762b1068d759191954801c6cbe42a175f121bb8e8c14d654f816e58cbbb19e145404c0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"eaf3ec34486bd4583999c50b0bade6bc11e93b4856eae78f66df870c23fd3247","proof":"0afdc68330dd000f644b065b47dd9d1151e61ca8d1d074d1c48d5f4c5b56477fb2af77886973fe89925040882142c07a7143cd7fccc4f46765a6195494bf94506c0f659c53f8ebab26d93bde070395a448a9389d37cab1dcd355c4c5f7352d0b7ec2c2ea6fedf2f02cf7fd80c88fa23a85cc6ad626030c23e9e7eb27aa86bf5f27acd67aecbd864bcddf3cae214e3ab72633d3c7b45b239ab1431322465cde03383438fb395cfeadca2de6a2b001f4486650a35821726fcfb5bdcc53daf97f00880d72318c9929848dccc15edb56eec999b513e71f37adc864e2f5780db64d0aa0810d943dfdfc62881acb37fcfaa278259b23ed3e26e8b2ca8e48a59cff0a4d54277184ad2e9a6925492a7659b5f98ab76d3dae068c21d51aeb6c01a1c10d180a90a396ce6a156cb415b4b7cee44d58386e6e4ab52424e9b51298cb991fed5e5eb78d03e454ab49b06c0b4b6e318350c5d1e15414c63909a1fed8ff46ea7625521c5b776d75892308b61582b41ae27dcbebfac2d0f1b272d49b4590f5319c53f85564614a56531f2d099583322b20338ee78bf61618db4f915f55ac660989690a7214b7ece3e32383d019917f61221b93f1e0d95ccaefd148dc4d230d083021eeda66552200430148adeea1d9aee51d3945823abbb7ef85dea70b3699168f7bd2add166574539135f4f18a7d68a6646b5c456967bcd0916dca5853ac674b05076340f4bdcb09cdb776d84509a17774e67693c868797ca53fd86bc4b8245cb77282393a351fd3055a28a6256af4be82775d651d76ef674ba540785536997be7ac8e29a69f5d9ebbf86cb1a045d0096e7981866a1afd6a6ccf813d878e3742e5fb5dfe05c1b76d2fec773d2fb9c48a0a0a118b65b1e06f16e026becd1cb3bed0103a6637aad13d00b4682926f32450e8b6f4abb10eed87ee8c5d6b52ace37380e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ea862d1a3a7951b8cc35ad11f35cc694ad3055a281d037c555fa4dd19de4502a","proof":"866af1c45c0876c2e3533bee317530e5374d3cfb009ce98ee9a750675f4dfa12d62893623548f5d07e08eec7b296087ea1b11f73494428f57009d40907a44c6cbc6ebd5f5058bf6fc5dce7fe9b592d32b76055baf4a1d1c6d026ca3ac8a4232dd6ce2a13dc1bda4dd068ecc36d3d3d27ddf451e112d12fa9e8b2a1bf50f9903567453bfb0ed82992c8ed220e444289641145f298555a20842f750c37c2c5600447f47ad2d57491f5b01489b4830299e8769347bae793455f5f2282a8a28e9406ef145e096db890cbb4700050b56dc9b5bf475f1ee3d8a358b19346bdf609f9025cccc56313c796d8924cc68c26a6ab1a1ee3cc9259b4bc492dacd82f27321f64bea8a0083e6448f0e428da494f9daede6fc971548635ad6b86d1732153eb1673fac559ae9d54b3e2d5f649474f6e09da9333b0e9afc72b2c1f89c3d766044d2e24df0f9a6ff188c3b28c04f514a91303c7c2b14867461436f5198c450bc65e56461d2ee0cdb95e0239c7e975d1a00c9aedaf682dd190eb68959a3b218dab072f2af60eb585aed3defba8672007728658a59232608f8f51fea834436d65622d404c8f20d83f174b0bdd2220c55bf3d9cafdaff89f7658c35e816a768cb94adb6aa82e73a7dd2e02ffa16b39ec826e53c2397ebbd0cfca928b6e0facee5462cc0a9ccabeab5cf0e6ab3d0877e53dd35506cde69d39b620033e2ecd314e931f9a3f960426edee8f846c140e4c7fa1145c55ecaa2bcbafd27ab44ab896324e4c493d4e220314564d8dd156882ce3f3de971a18bbe8c1dfa275f36318708deb496e1bd801cc308e0aff5dd933aa6285bb29eaa48852d9abcb300a8cf66a630d72cb7154169de3aeddbba7fb03577fad012eac872fd74a5f7dd1c1ea7ad947be70f50e60643317f9bd4c880dc21036d8de6c18d6f84e195efcfdf33a77bf6a027aab02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bc688d3739931e24b8a968d60eed24cbfe8782775b6599eb26a6e8007f497f65","proof":"fa55a9538a6678255b12cb5c3a619ee3d2d80eae713a3289513db9ebfbd391138aaa908d7e3714414e78438cea4f885afe056fc1691c448930f3995432d5927446902cb1f7df3b5c04b3079932b27f72a277fb39ac3b0d3743af4de8cff51f2b6e2a14b600268f86cdc19b7b902f37e78c1c9f78e91db00150a703fd0424f72b3ba2f6fe0cc7013880fb79454baafafbd03b078c6ca732c78f7969cc0970c601e739b07c5182b5c04bf4889e2d2a8384c7763ab9bd872edc6cb984f05940a50843fbfd0a0ccab892e6ab34edc9f7a140b6f6690494586197ce3a9a1b697808082eb26308bade97c10ca7345566507447588a5ddbf6543f0140e1835e87eac56728c48eb6257d80dc1c2dc6d9262c522d30d65199a84561222fb32f22555ed139a42e57c681b2dfc148c5b05754f5a8ca02beaf1a39f38f121dba4b21efd89d757e3273b9799201a3dd5532e21aadda68d2042b7f91e71954b3647e641217031de2f68f8dd0b9dd4467fee7af3d5ebba58e55e967d5aade9dc9185694d6fd6f3c10b7e789ca4be22ed84f999dfa97c743589a34aa291ead5cec479bb92c8128601609593b178ca39d4dac83ff221a277980e858ed6a066f38c4b7c22d5fba5d2820f7b9491caddddeb90368459e9890441edca1198b3924c4bb23d904be727f26a680869b45fda66740ecbe081f8407fbbcc68d340c77572bddf5ad5812cfb96c26881dcb11a884316df6f434dcfd8a29be003b91f902c35912cfe35387d48e6f9ef300646b680e7439eaf718727c97609273421203f11dd89cbcecca8164e620100a078f486ebe6dccdc3a6c7a70ee99f5107dc125514ae583d85318063a3612492094fb0236c02207a64aeda497683f4b07f7b1285a5628caf6a5f838e99a0751410c9fc94c7490438636990b07609975cf1456d127bde906be849994904f06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"eaaf42daa1af395e975e91e1a71e13050f13b84954cefb3d2590f25ce141ef0b","proof":"e86d85c4ec79440c83297e07cbda6a1e85615111325a854d3fdeb769cef0196ed4f9ef3f5750311e9315b34c367d75b0e94f6761e052ecbd75dada7d6935ab706829d15c7244975471b7671f17c290afdfad3f4cd334f02a69752553e8dcce00e0208c2a2fd64a5fc1fb695ff10b871d4cfc7bc32d0bf5a75f168f44812c8023f0fd43ddd19a15863505c2df25f77988bd0f00fbfa8fd8f3f9c0e13701ca220849ed2f4523906d310d996ec483044452d0b5d739492b1c18e3c60e2d6a7a190c2601ca4e5b9d18352499b2eb04a14c07be508316bf07ec3ad2420a429866dc0f2405ffc0ad1be8ced83615e33b529e54589e845769def728c8d9b223b4105d199e186e63bed851054baa428ea52bafa367d0cde1eeed0e910af46f4f9665b01a961827d98a8576dff346342283a2f480a3eabca3776845bfde0a2d4f32311f2a06baabd24c7d27734dc9863fee4a461245f4e6ef4d7a1970d918a8333938ce35ea5c5b02f278c98f197af030e33abe626f7b57be659d036998afa5b48444c01df4099d8c9e376a49ed04f92a973f632fcd080306aa8dd3a0dbb756ac7129586a90fd0d7585ed05c238c3bff0f5b5b398f1940707d971e3f595f093247b5c520c24f4ddda3291a45b976aa8bffdb9eb01c2b725fe0dbc0941812e20182fa8db57a6ad3092c45ad163b44c08b7fd2f221d52eb4820ca6f11d8f0381a796aa3206fd4634fa0b4a84e8b2acd48d4b35eae864b8b5e60cc4543de3af5c28b4d22310966ff44ac2c63ce65e5078b532013b46581775834c1ddbb10c1bbdc7a60f4a25b9ef80ea57efd4c3567eafd6e5873c789adc1771946121692f3fbe6266a874723674eab5ada0a35009d6cfb0b83ceed9cd7f4f3c4e7551d6453cd6488cf05b300f250320d99bd53abdd050d97aadb3a16278f001fb1634f8189167cecd915a206"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"028c83974bba67ba177261ab5b1b7ff1a2c0c108efdeaca5997bb2f6bb4bb211","proof":"1059eee29726c98afd83e7dae4595f6bfa49b1fd773ac941cfbe4c49c4f3e56a3c7e67c20c7a6ca41af844c0955b20bba56a5c4b5bb7ee46b94c36906c3489347a600c13f47d0fcda28141a11528e54c57832e4118be2f88a06067008dc93d54c2b6436d45fe35ee443f7fee46c7933c408b2a2326195c502bef0ee25e28065510cd142795e24110c06293210d1100d5814f0035ac102c18a4ac19015233c90da1e0ec6fa705f40a1a6887cdd90058435dc2a652246ba9a4a0494c34b21ac00ebf498e104a5e029568bf49140dae0288e36404cc943bd201fabfa8ebbc6b7701f615b527b14dc57441279ca64535cf022f1e32020df42cbb51040e2c6f20e32224b909dc2fca86c6f44ba90e1cc4e4f9869f8837402abe442854f5c35fa13b05b036f9aadc7f333eb4904b1c1408961eccb229be24f5e9d28248c9a83dd8c87f1e2b440b1d0ab485e4e585ee380b99bc779fa8166741d7769ace871c65e12148cc2ab7f2d1b78de615416ef74539206f76355ba68f2b14517f0cb4b9451b9333fc4b30fc5654466ca18b87200df55bba2c6d0f1d430fcbd255cd202bf5c0e11a22bc7614bd710bf04fa5d2d32a903860e34fea975dcc8adee3c398052be93619d83fdba104c599da5ee6215ec5b7573218578d899cd44d71b6c22d4907e43c3336a143ed8d6cfe341309bb203d65d87242c83e403b8e532b63ec87135f40f82550730e5565f0ccbdfce95ad23e0e9917754af612a76668ffe9a134104b824b6e3c5d14002f9bd321170e46a0524103c81670cbea7e51e2b714dacac6f612d448a28b8ad9c1eb4389768ccca3f76393a4ceab452d3ec2187d6e5946eddc1d776c52d9b133d7e8c73ce2ce9c539bf0077f5f8e6e82196d1e04cedd0279b8e54505aab769c944d4092608a582c4ea2307ae1a6ff47ab22878a1784f08bb2288b600"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a810a6d9f08eea3553f808f6233ed9038ebb372bc215e39f8a40dca4c2527939","proof":"7a1481724ffb13b03080e6b43207f66e511327d1c3f2a952468313397edcc2309219adc0470f473ae0bd4fbfede3a662bb5b8291ecaad3bfbdb5fc09d1759f734a40337ac5ca47bb363f78c834a6c4eabe155f59f5b91e11508ac5c7b5d3d409e0044cc6067c490e6d316c7678c86fc197f4cc33b56cbaf60faf2b0c336ce41f93e558c3b89268beb52ca923bfdfa57597fd87f27988fe40d50bf533f3b86d07324cdb46781f1ef7fdd0932b6f9814f7d12d645d24ddf84e330e949f823a9508b8eb550a941c9aeebb280dc5370b2649472a61a050d2c10bebe69e836f02170c90c36c3639512fe7dcb7e2b18fd8300c91feb3af2780f4d7c41bfc154a183035be73ebb3cdda977e08101c410920e164c39622a1c0ce2063e76826dcc1be02145a5fd147cf95b4f038dbcf63fb53f4f59ea0edb2c59d8e5877975e1088a438763c218fe15352b3e17ec8c2dcc5bdc24652ebc4ea1a46e576aee0f8f3aeb826464c307cf73a4a5d62d46f230b2e7f7dc733dfbc992b08dd7f06f5be973f49b26dbe6eaecd956361481006c99b2bbfd1b0eabc32a2029b6afac83ff1a4567fe71b0a95ecdf6f70650862dcc27ab3527a90c20c5867631f146cc08e375841dfb9116c430c96587a26988c01604ec767cf5eb79445177f6b5ab2cd78bc1aff448d21ac9007b9c63c2795d361f555879f6705a63069f2b02d7b6a4485ce11862cd84ac4dfe498cb11f294575c135f49688b9163be42b787f3eab340590d20d496e5725628667e42555db99522e8fbc80f251abf0caab10cb8da5e76b75386a08542483caf53e92a42738f1e70e174e35bac8f07aa035a1af1e984907d248aa98ed56e36acb81c5b9d7bd751f7aa3c2c5a4e8ad84c458a5d15368b04eb3fd77506350c956fb542f013e19d4e77f12adc89c740364396475bfc870b33e951c4d7803808"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9acb2c366ddc38a178ef1de55c85c2ef01e868888d695b6863750aef5dee086e","proof":"2a020b5f41849d053c0562a35854d09338cb5f9a9c102474d54a725e8ae48f1f42458eb90d28e82d8935ffb3a529809b3bf8d0e2e729118a86c7974aa3c3f53ae6ceacc2650953f70aed881944fce162b54fc6f20c89d76227bece8d6471342052230ec38ba4cea0d90d9491f545195d80fd756daf1cf44d56cebce54b19e63c1f587bf82c0a988f9bd1aa4a707ea8937fe2b04298d0397953c08389fb83f00b8a67a65f3dc8c3df1f72567ae0b32612dcafad3a867109ec13fc4086596994098f28b380161ad8da8e45fac9771d0108768fd8578b947e1352dc8d38c976cf051aca598cea510f295262bf51f037a2bf49e57a08c73ef626e6fe425ef11fc43b6caac0addfcb33c15affdf41c51b84645ab45502c7829e23c4b4066c82d5f06b6e285318cb0eecef5963ee01475470352e658eec417c59d5629d1468077ae84710e124ce4ef0c8bc622010600a3f6bd6651f97f20e3b8b98618a1f8c6475243f4a5862a6b07cb8ee5651508994153944d38c93a825569830de578ac5fb6b6e1e6c2254e95cfcf0aeb148584def4faaa652346f2a4b9a84c94adafcce6bbfc233203d712e64d50ed257cda81c371f087fbb1110a61fc18f9113e4b6a634dec876860210c5060239660d12cea019d084e2e84fc7b538652b1d3f91e2f31c22d872382478051bb8eaff40813388a6c56fcbf00c5cb1daba3a674f315c6d6bf3cf7352c4fdfad252c12c39b582869f32a518a5afa5193fa7bcffebabcb35782e6e2d2c3be87452fbe5d8e155e665e1006cb47f4eec5aa4fd01e8dbd840f8fee0ad1d3ebacf3582c19fb35f56f0a2971617aaecc60aa1b9481a448c59933bdff3bf600bca26d004c396d90f9c3ecf9f78397bab1eea99e60f6fcf4e1d7f6045322802e9c9a46aed1da93d7f8efc0b19e85d2e40e7bfaac0fa47f7dc7bcc3e706be304"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cafda4754e9cf27b685b5934710d891676aed93d0e4bef34472835925d9ea87f","proof":"f0f7b83f76debd91932ede776c525bb09285d6abd704cdbb3bd3272c52da2a1040522b14f5e1bd0a82e4d6e09b031a963cdbd75037a19bb7ab87eb19a4dfde28523df3dacccfc4cf47609c94858040937f2854145daf099f463b82a1e8159649e624206051c1ed3504d4e32f9c9ce5675fcd1b73fbf6f793b7a622a654a0f45849dbff16c4f47cfd1ba5226931cae5272282955eedf0a362cb6c512ce524ce016f3c7c362d0561ed7bea170d9ade9a0c62ccfa259aafff7470ff6acdfa23d40352dc31851b7ce6c5e4b6fd729e1c5a0003e79142da36dc67d22dff58a3d60409f8bbec87a9447433b963424ca9f8c58e29d32d09b110f7f13645dad78d0fa23b9e387a0e551f36b43a9179d725f9f103ceab344154e3d7357b81d53685a51668f0766c0fbd2b91c9c45bba37612490ae17edc0aebb5187ab09f4877568e9d467364ac46ca56b528c385701dba5c937bd4947022413fe07a11c26835685e720287240e6d033c10dabe087fb9954024acbb3a1faad0f0c0f9a588a699cbfdeca6bbac0683901712827b0752b40324acb2f9829329a224c1f22716707315a74f86f4e487dddabebc444d5d26f7fad9483847786b5ea6599fcc60c9e6ead6cc6e063b050daf874a1d877f77d06968e82df23818cacdf1d45700db76a7f04d940440fcc7985a60169d3de74792fb6f9b3a81195cdd4ec79f33c4392410a5c378f952c2ed8f20c6c1715c9ef81772fdbb5866d560b629d29f88576630a55d16386f4118492dee28b9e596a7ae91093c0b6822ef413ac417ea6e76c21b84fa6198ddc6bb4db5f696e1a722b9cdd854c92453b7126e80aeab407eb6f28135e4b0b963b0129406b4ef9a829e280440b4d0f0d93ffa41bea33b96c24577eb7cdbd2759dc0f8ef046a46c34d4b8781367b2add00cd5407505d1f4f58f764a636293649c940c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"268811194155e68b040b80b962dbed25e7016d3bab5772f9e2c719ce77b3c866","proof":"2411a17e157337c0300f5113a9155127578d7f799465fae8a8bd531f8dd8240022318e05ce9880dd5ddb5d937bceba992167e7f277ef1f11d28f9835c1978e39e457ba080703edaa83d787b23a300d2d32400d8243b5cf72f0c1485639bab05eb8321a4b5e3f331281410e89819fcc69d5e736b81122baa8774a1912b7c4330d433c5d5ab354d54aa9e5a3445584bf25c724ac599b43e92388e9f5fe0590a40ee582ca18229f19c66ca10b9de4d5adecfc70fd1cc75bc4d64d9d8730948c9d0007e5b002abdaab3706e5949614d1502eaac42dafe58055b6599ff9abc434a3042cdacc75fb508cb5eda058a6b5112fba3d1815ae0bb8f1dca470644cf0cee668a6928fdca54060a0160d2a61be944c1f48e7ca0a46a8390c72e849a80f8b627a5cec60c6e5cbf81c63839c6540da6e3d114c7ee39925b4f4f800e9b3b0ca6e42a03f685d9c84a2deece9c6f6cbfd20fa1f1d4e42f992f779f92817d192e46e1eee827b64e6cb7daf4401eca37cb4ca712ae1da0b3582540e66dcef8b3dd1c92d5e2ff40bbf5a2ea48da92b2bb6be81bd412d61201690e2beead5aad2e27e1c7306320065411eb6e733457701cd4e5cb864fcc1f73b7f65538f20488f871fbb649218d9f52d3a220c475c2e9ea0c23cd2265d20e1c9228feda3116a7ae6afbc0a04711711e8dea6f74ebe34ca492d21d0128b1a5d240a9340c608af399e86c01ae47cce53132b45cb6a9aca7dc72878394d4c685891f15f909a1ace9b5cd0e2619ce14a1524864c6b4323a7490bde123a8367fb68752e05c36298df1f50251953ae85775a8b1882e9d177ea4e871d6270a920a1f023ead7c88fc85e6f456d1b5f26352d778d411d7ace04b1a7aa81dfcb769cb11c61b4bf84a77390a1778d7607de0cca4c3008c60032ecb32af5dcbba101f03b0cc25ae744107966f33833340c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2c1db059e1f5c6db532f3475eaeac2dda7cd242ce21e4a0cdfe968ff4bc3cb71","proof":"6ed5b1e77932b7224995dc96d332cd50c23cc37e81fc2ac3e7281fdf89d4121b22ef1a1ef9a59feae58a768b6b30cdf7b75748f4ed7d2b361b93657cfd5ff14064d657c42d2ca3cdffe7db163fbec40062c547a971ae4ebdebb382147a863b624a00a942db6d314dea88db5ce046814726d2fd0df56b7f6543379ff5472d0c2006ce8ecf7404537b8cec87d4480d3989dd1c1d110c13d8f3ffee05730d7378017382297bb9a69de833f129ea6bdba74d5d24d5336f183343fc5370f163e0a40c51d50f45722cbcfcc38370e2acacc6b5c0b3635eb6c0d318bd3a75c87c70b0034ef73a7dc76b8f6bcb9574057768943d957509ce70da75fddc23c98db548f7164894dd5f657446c2ae80970a54285681c132e9491fcb6a475e1be683276d2c6182899b086c8a0ead7a4da0309b1b2b7019f4fbf2a88cef78647116723e83b066da692e1502250cfbef5af7d6c9424c5947773c1a53837442919e2244ee1e70023a074a825e468219574923e4d3898a483c666b29c96503d2f4f5171e3ba11d34cc5d5ff2e1a792f39808088588d019867534f80e96dc1738863cd210c6eb1e295a73a0ecd3d6ebc7ef5d7a890c4f90d601644aedd9682d866dc443adb6fd374e1e0b3bd08808e1b866409338cc7705e0ed8169809343d0440b4547c8ff45e94a70759770b344c415707142debb506bace697e066c8253a65fd7d41828c5b5546f6ab9e6a97a74fe4945185de243ed80b0b6da90d60105997648266153fcfff424aea558a6f89983d058d313cd5d5c13f9debc846a22f822657401ea9ff5db34e48ad87acdda8ad8d090bfd30191107c7572e69232485df092db10b779b433f3ed0be38c08af65d60b66c802088c65009ac73dc6d92df4818f61d67bac887aa0112869c99430443543137916ea10081fd56bd2a705a3a846be81f6df3cba7be0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ced3325bd86723df8ad4199fdbd04208f1b8481df77d94524428c41d02801b39","proof":"4c5d9fc77e4cd7f7291797023fa1d9119e02106c2645a06236a002989d1a316b7869dd61852e6934465fed2e8e2df76a5bb69fa8070e81d4acb3ec14efb93700aca51da1e160eff8d959715f2cab35c8fe2dc0bafd9f73bd76220559c49ce5692c5b31011da669b03beee6e813e2a4705a0a2b06f56d5ffdbad3cd4db54ca04b739daff944ad12156afbbc10c977c918ad293b52b7453d89abf36289c137640a5e80bfe31cd88952675b605364d3e2513b0cecdd1659ebf6dcb12118ef467d055a7bcb64b9a140f94873ebc32133a915e05d2537ee542986be4025774bafa006706cd132c7809485aa4a1b2a3c733c9ada0c512c288cc57bd08419c2be63ed3f826b3ac47e3d6881620b1c483568d22225e3781ab1e02265094c6ab6f08a60748a6734ba0a88a6807d51fdd52c684400a25b5aee4da82761d263c22fa0c247598024d1f6720d818f92cf1c2c14e09ccc16628c5812a82f7211b975bbbcf7556292e0cf5071a31c83fa9381614f9e550c11c82df98fe85e2b9621100841b6fc748e3ef935b625efa6b62b9c3b77e95ce5015d44d345b05f4ee29689909990022638ff71b3e5197bd5ec9a36d1d2b559013ab742f51422d2edd509251329d51d584e46f991c910332d1ae75c7cc829e886f7d8937be46e40ae80a9a6974b53d31c6e870e89fd27e027bf150cc2adbf8bcd6beca0fe36ace90c967d7fb17bfbf15a04f731a906d4421fda84fdf4a4fdd3185dfb1d07a4ee4761986626e551e89f33a09f812b78acf4f3fe4fa7fadf973dcc5cbc1fbab724a905901dfa0aa793aa1c641644bb711fb3ccc3988c3a85d906a20beed6535521c711c32991df2c33ec3aca11a7077bb7e4bf18beef2285049682a1ab0fddbb0dfe4ef1a905cdbe1d130440c0cf3c3b766ef35a00e2a3948f921e1ee6a9c049a1a1ef6eaf6dd0fd9fdc05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0e991c466ac662b359ecb4b91d23853d085bad3b97ded17d37ec77a1f5cbe565","proof":"fcb45c6506c0facadbc3f72cb690399940920af028165c994c51269c659ac41c1a191415cdaba23b596273018259755759610b6ef76738aaa01a6e88e4b484611e17689cbadd33e3b7a3abede08f0f269f12cd157cb1fcade6392daa66a0110d10fb1a438e0801136fe88c6e6da2ae99fa8b4de9abd696c5427156903080724430df64dd183877b205d6353a87bb66764ab36dd978017455871c7cd26e3cfa0de02fc4202274f7222071272589aa10e0284aeac378ab502da0c2d6096557fa019c35ad69ce184e64ad5fe703e479a7f8a0506fdeaad0d92c9288abe34876ec0858053668e33c551ee2fee9762b5cc4ac4d52798fdf1d8941e1f7a4597921d322d210296226eef3202b8a2692f87a85d9efb27376edf67261af7864f093c22d61cae5940d7e836062d27dbf6f1c35d8745784133db662344a8c2fe06db764da4cb2ba88d038584bd9b74f3e1f6d231fef3d8e69b72d2b8860f8c10fc9b386235700a5c7a76d0b3b94625dbf6e7e12d727c3881eaf7cae5149eae6d267c3d0406194bd65a25cc560b16e8323df6b031f29be364325c21ccacaa80ea52ad63729452ec0b1633f5422185155c82b526a6a656fa4584fded0eb7c955496c12ed27f124cdb8dd89cfcfe0f11dd1d50ecdc39c78e2e50c928d0887ab5ba47ec2769526d7c710ea55320acf75611e96ed2fd0b32671ab1fcc66712e9e7ed106fc26e9a49ccc0881762e0bdd14053fd0ba6a53d2192dde824224d242992270a3bd7eee40d4cb27aadecd5954dc1717db865cd2b1b7e4291c4dc43d1c42ff582ecbdd321557a7b2d1646c495dbf7f61822712d200fa5f6ae2f02dda42c7198ccb47979a83184a3a8453f004f98e5becee1d3c3cbcb8862883ef7ee4c0f70e44f9c1290dc0056a4c4e664bb18c482aac52bf510695786468f0fdd2f8b8f6026f9cc48f9bb02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d44a3000c583a1332b5a61e92bc2b63d37568b33147b5213b85706c0dca89514","proof":"223bd47d3a3f1835a5dd92621e2492a8aea095835d18905882a4401eb00a804628044ad6ef0fd80710d24b97909a776d78842fbf1a1fd5e25d53a773b4232770248fb93fb4c83f86b051969fe050e3e19a5d72db6580edd87e85e43333c10e16ae7346b27c82ab40e4339a45e4a4ccd486262df159b11d9f370b768a0cc35b40f774c5e5b0b3081a689cfeae4a2b7093481db54a5b2943b002842af9cdc14703aba5cfda12eae5a4d6edf74c0c7543532c546e62e1b26c010a843b0e11797c093516a887ee05fd473b7abc7f9b12cdbeb6048e31753a8406e7846019d4d9d707c6a2db37cee81932d1a20b9f620d09f337f21c7f020553c465c1f3da86df9a6d52a63e4ab1f18b987c6dd555fc0e2398c164a603794169d116d649344e50b12f54f4e199f9418063708f3739419f617caf1ac9ea7178b5bf17c72bea738077108a1adf502590c98af060f01eff6dbe67e34affeb61931c704d77e2166c81246ff6b6a277c05757787b03f5580fd2fe4f1f03b384b89029254f70b3eabb91ce6066bc9b2b7d00153929ad3cbd3a50d76f92f4b0fb53862059a61bf94cba9d6047fab2ccc0167b4148f043e9d7434b325dc2a15a7a164c170b64e376048cf88d6c8a955e53bdc7373dbbb6f4eb609f018904b0fd36512ff7acab2916443fdd6a33b637e7a00cb0130e4d434ff41391df2b6835c8afda8cefddc414fad516310619c047e32c61d945a6e5ff26781daebcfb3792001a18a1f9b34b56aeab5892a809d0fe64c8ec60c6b9ec144d627925ccd2396ca111d6a73f2744f66f8a92018e3c6c9710673f2348616209ca9714c19fa259947af65a21516df228473c00f7c5059b48ba931bb764ee0bb072c56b369589afe338ab064755cba4be3ade73df290253fc1b89ad006986e417b5dd33e003f2f3d4591c7ab759061e06da6086972305"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7cd21e312409ca6eb8b91efcbb54aafcc9f86046ced8f737bf06c1e72442e849","proof":"8222097037455d9a3cb304a77a8cb396773901aeb3406a0e34e97e50c1951825cc802b1a2e3dcb64f15943369fd30f9a1771bd380743f5e7627782d802fb8612d04817bbd54672ec4e0dcd3c97e914fa3e5b5fa011e002621e13d0d24a203d6efc4014dc8cfe2a6175f57bfd7c91c3eb0609328cd46e93ddd918883c9b337e7d5b7bead1efeee9c79b14355957315f7e29c6a37a7f90def9b286cfe46b61e70e9783b103f87f6738fca9e5272f793ae0cbe02df4cd8d40706dff0d152a7fad01ff5a080d7e70c095d5fe0b1bfc94b9b1747782739f438248c2aab885e787fc0cb07d3f5dd75f868ae1945f3bda1f03507c018309dfd44992799b78d7e8984570108aee2b4aad46bbea6bca8d7de06a01e522622510347981bd26affba2a82f0542da8c1a6d7765931eea90ee298860181da7c3a5d1dc040d8e9ce51ca250da6bb4b2cffd1316edf4ead02a8df2404eb0edf17d900f84f3f8ee577e50a8f4d4579a1933ca84f8ef443adc4c858d0b2cd6c976ea028ed6b17d6395a3beba2b6a76c4d0d0060a9b179d397c6aa7a14b309c1cf85d48e972d8c13f3385c4d0318e5132d75cf1fd45ff017bbc33022b479c775297e607217cccb8f42f31bcf878ae2476232be7c0324b0853b04bbe944fc3f0758ce68710c84f2e1b6b89960bbe442470d7c78aabfd20f8e0cdadf8060cd5820f5ee4869bdc64e624442ae04b1904305cfb45a2188a4a4a7a72459ce78d39d88c8366f21864aabfbfb655e747148a209e99c178b05aa6a84aa7abc79048783c57dec9933fa3122ed263f65458b8bf74641526a91b99c8c057b1f5c438baeb91482190102139ee262106e7068373b000c6bb90db4c27cb299f018ede11872a6b3adeef6d60656239428477359e2d370628b2d9071177fde9e706dc915c80dacfc9aa4ce85663be7e2d632ee3add57e0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8233cf20db13dd47808fb384d6d193708a3eecf48ff24d35b069d55675483a2e","proof":"ec6ef38715a85ab566edca9db5ece772196c14fdae4372fb4746725186ab594112af2d9a945b037dd512b458514a1c6952995675670dad534dc4fc7858641d0840ef45873cf61fd654be7548a4c691e882c1e267b75bd5be5c7ca4c9c6cb70622efe1e5fbcc2abf7110b9545ddf191a579655164a594ab2426925c3c039f255b9c5ae30ae25f7154c1e0655ec383d381e3f28ebbc61793f7e4d9c28552c83f086b1f7be4154de3b8003524fc2c5077c5c3499629cd564e3f4a42da88710fd9091f38bb3df3f5facb0a9f91cc24f56ecaa18d14785c5eb47266784eebe6d33800be826559346a51f192eaba16283867f81030b0a5d80f20b29d949e1217e13f0444942593a593e45b18166cf520f9df6252fd4aecf5758e53fa5a1ace50fe08449a6e8a686e742e74792eb8421b7646dd3a77f7cd117f247142e79440e128911af293951c75d28a6b962c25e2a7e5abba1e24ef33f2ddeb6c620ce665517e30273428d3f52ad3bce8f83888164cd6410dae11c361d05bed3db9532bd9af33410fbc752503b09e87c41d6dc40ce3abe0e60ed9d13267c4c4215006409725b9a9309c6d412dbbd155007bed843987a4c76791bdf356e5bdabe2ca4e480262b9241f76263a7ec80eac0f720f708bddb0279ac23dfd43acddf961122f89f715e7a02ade6333b50ce4b0f90a08bf01fe47bbc7d319d01e1503ce80ebee7c65b50abc68f0314cfa1fcd54d29bf91bbb1b4eef8c447662b62a093d7efc039d6061ceb73cca30b6c1c4510aff3efdc6ba6c9cec3431d6c78bba49ed5ece4cbce3a9723226303b0441d6e8f7383303afcfc5f18de1dc4562052f5ec0fa1fdc184902803a7576fd7dfbfda2c6224b67b7312a6fe868ee7e9f4e1764410fdeb295b3b75d3c06fc6d1a989d46d62640a6f460649846e935fec8f5e9cbd53fee92348b4fb71104"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bce6324dcd87c42adde06c066a436cfd9d5b09287474b78719ecaa52c19cb256","proof":"ee29e1633f86e82fb8060c8141ed46d8fd9beee0387abbb8a997f4bbe7317641105284724d2cd8e9150909cf5f9b0e9a924ea0a3a7ca2a9c8b892b1d92c6ee32708b1cd6e9073123c69a82e6d0295b5c102bbabbdd45b5fd3956b159b933fc6096fb8fc42d679f78739afd66a9951f4b92e5c4c21a6a409e2ad0857e3194a64276735faf28f51cec2b8dfddc10e591df68258cce21c2e89db959f4b83c1f3901216918682a7ffda0650b90cd5b974995cb719eeb16a58f1fc067dff517f9b40b9e83559ddcad97f01998479b893283ba824fdca7e4a003a2337b24b50bacd800d41dedad1259e043e8938456640b69e94bf3a3df715610d450bcd345c851227ece4fbee7e1c810b434f337653613eabf96abbfb56d07f54447e38d9b5ba9f725722554f0df2af37fe43dea90bd2ecfabd759a1c60cfc7cf15ae0d0a742969e2e0aa6f1e9b42e2659eb4986e8b7b217f05f7f29d4ad406d7ea5d17743e5803a2a12efa2903df9ac5d790ca51d50e4dc5b04f6d9b673dafeebadc5cffc4330b634c68032207f883b530e871a5ab8414b5d8901a372da36384d62b43045ca908a18dc4291b9f9e0c981afbc38f19697d96b0f0ed25b8ae98caee9bc2c906b0ce06b5878b9a8d7df64b9d853108bdbb82ddba69728f31c68241b08c6ae23f9bfd354425e721810a7acb0c7e981e8c82eb5c3cc44d26950f4b25c9a332ad3f300e543a68429c8b456be59b5063f646f0747e59e4132c85c651252763666c6ed184703dc670c3f5c5f90f222a3022aa598befd4270a8fbb24dead04b43e3cf4094b938bed789e959344756965f65620c3470c2c9bb883890bcf9bd495f3624a94f66567004c3d52e8ecb5271d3140504da9ab5de3908f265d491ba9d31d61abcfbe406b1dfa97e238b75f1b202c5a62036ee220121003129331aec7a04694496c4de0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"741d6044b44cb2aafda91f5d512600e861118d2783edea4eaaac23ccbeca1f2f","proof":"c6fb3e14b65f6004c1484152607499d60c01564c6cd73e47d71a2e1a742b992f7629f1976b60bbba07a1414ce6911ac193bb429e408ac25315a7ed0c769f72195a1828978916768ca920ab24825b3c50c19789fbc8a3724aa1f0d1f8724f2142d2480744b43392898e50b3101401c24ef9f967d302f5bacd6207b9118d34811b69cfaa8107f0faab9c47d53216626036d81396cc147e89acbbaa51a1e762380ffd3e69513eb68b8afd0a9c90015b35c4ea1595ef929a59ed8fdbc56ca5e78d0fcf96d42ae1fb1c0e8bbc8907d942de5a71cf367ddb608d17a4d211f8a915a704b2746e14b720c201957cd7c7671bb6379fe3585fcc165af0cb07e6254116762938bd3382f97e67a44bcfb3fa3c18eaaf6231f3ba57634df4152bb64cc414021bdaf90a0296d0f316afaeac0ff23428b7e42ac24957c393a916c5c2256b800e4400626f1af14b6be3ff739326badef2272fc039253000feb7c5c9f21334321f0814bf02127ed1e81b9a6ce39ef6d1427d6d91ca827db3bc7d1e1543189193f01720657c27d145d9ff83a8818e485732a202024ac6429048441827284c3d73d729e4ae190261c8e49624a14db02461573b965f3e2bbff018f229b5637da9e72045749880512f6f503d28d88cc105408fa4dc94db6312bd5aa8cd8ffc80e801b24484439b9d2e7c27f6c6ffe5eefdbdd4236782fdf87b7d2e8d8f44c04d2292882f461970c3ec9489bdf1d40c84c5fb1ca5ba9516f173919d68389bfd645fa34e54cccdd25a640223b4c6912bc8d747f135671a4cac9dbfe660c934d2e2a667a313e49a8760c8aaa482b857e6af5eff88b49b9fd562326940512fb3c0ac09ed93577f664db691caaac0634e20e6d7b3490843ffaa97235dca20dd29cdfd97c5480fda6030f72194fedad1dae81c41704dadacf9d29d6c25a110ef801bf3cb78a00b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d2672fe0cf741e034b6475a591e89597be865a1dd2994c3e558ac595be06947b","proof":"3c6789071b19b0a4cc84f50c35d0e4ce5c4e03083e4c2f45f4f668bbf91d244d141635e52ef38f8f56edc9e4cddbcb77d0f2bb40760f11946582ef794e929c1708679585c76a5a9985fbf188328ca74f58aa55ffcc5ce56fda5fc41aa698da2792c49361f1dc14ce5d9607467748c7dc3bcd839dea65e20f9427241b535aa543f86cc4b22af5a8da56631fc1ffbc4c0a7a01b4f23c3e6e80b87354b16979c60c9ea8f26526a34848d238d706d6df21f717ab03b56c61f2f1e57e23be7566f103fbf4252ffe0cdd12ecc2128d573bacd80188555860f2b62f93afbe74ab01be0e60d1710ece04c1775c2a1377269aa72b97097fca76ea361d664f1696a3579a69eede62af81e49a6ac4cbbe508111eebd3e6b2e497fcde4fe69ac0e1b848c362e065f797ded6e72dc4be1b30fff50fa28c387e91e74a9b9292c3c132c3c169a6d90beef014dde54e5b73e209a5187797c4f874c35702e1e999fee10abff8f8c4a7041e519bd809d2569fba1f7d36126c224321321aa7e5ff87759588c5c91d564262852f90379eb0cd2e0af108a4ab0a91af5d548f5f4f815b7dbbf625c478564fe72f6f043a1c2df4d7463c115185fefdcc6ef6c484a152eea1b386ae53a641f60c4cfee0389387a06f28c99231c4b7c6f27eb4ad129d2ac7354e57e5dc1e50a06d472662a4909ccf28f95288da9e6fa889cea69aca487470eabd83c194a23359a5b70541686655123deaf35bfbf84aa1fd170a958a753a64f4ac9186a3020545695aeb4820d4ee371676cc7220e8829e4197f83b1fc54270df346bb6b878d44a421435288a277ed147b5e70f8f3e79d584cde9e8ad094ed25298a2402a38d4fc6853286a2ea8458fa7d8c2890d173f152fbee978ed50a53067792bda9cf5605fdf3a3b9dec82de944cf5cefe45576371ca82fa52d52445ab8979123bd446d07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"90e29c83724e0c2c06f8ba1f091383939fd79a89ac12fa823f07ad1668d5ff18","proof":"b81de37479a46fde114a26c63a2f7573a86fd06b4ddbfc194402102247cb2506125d07bdeecf47659fb758590a39bcfd8849941c7a98e499e8122b8a944eb651ca12d2aed8b53ed9f1b9df5cb4632579e4372161f1c0ecc41b0ef7cae524b454a4aed3b0d02d49f651378ef34c0f50e74fc7ee26ccf1bd998ddee154f579540184dbeebf021164086466f0052dcbf0c3d2f7c6538093e53c32c5d0141bf3f105f302869d895f043bc8ddfbcfa41d20b2691a8ed26e428402d9bc5c55519c94042acd5eba68b41d49779de1c743c5247860adbd0e0873fe31f9781dcb05a0740e005a7a652f66419cb7056da5c6fdc993bf509dd9739c7aaf40f53a4fdb64626a7841515136db4eadfbcba37a17a64b226a5a9c400f4a7a59e7585eaeda76da5368fd36a93a221470d0ef0d00d34f543708743d4046ce9fd3904b9c01237df26ba49fdbac0b11f0d1d08cb0098f3085712951b88f64c0603b29cfe23e2cbe421666693091c11faaa992d1d196b3463655d59c13543623cd1543e3714467ecf1777487a6a286757371afdc9fac6e5928ce75594774ab26a948697604eb3444cf06b678a3866d8053f9b87915b0276b7244e60c885275ffbf539c5a97b9cbe79a2d7c0edf451cee8d240a435e2327e8e1ad92bafaf1fd41a14a9e92df53608a3926ae0c1be47235a03eead388ee6c7a7768d7a0ad8dc45f6ba4406cfcdc9aeab37e24e46996adb228441d7f45f545398d88ae114a296e817beea2b0bf76bad4ad21488cb72b8b476b6c29cc58cbbeb72771271c1fbe342955454b903e49ff84202b5288918e65b409ce10eaedae5cabe97410832967a2bfe2f916694d5b09333b6ec7af7f4a7de053c0e4a50bf3c87938e5e587d51fdd36a86b73de66d3e415fd09d391eb34c86aeab9ad155e58f179f194ebb8a5963d477102ad480e0a42ca1a0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ea7a209715a2efc4e1920ce48026927b8cddb0cd05b09d3b3a50ff841bb62700","proof":"a8bf5d9be625bddca8e6f5f2c09e316f124956b4841c08b3c134b639c2287f5486e7b358dd7d1981247ff415b2577f6b579511010fbb67192a8289c7e0bb125928182ab131ca89061d4881fc8962bdb46fcab84ccd2a89ab1870438e0f60e92e98b4d6bdf725d283a3f48eb51dd35f851e18a9d356ac5b7c990df3a45900c83bdb6da357c25537f9ca531645a3a1471c99631139ddc2623eed2bd8f18254370a923d591e71cdc26b305202efadd538d9fa0a93c571f4da2852a46e7f012d1d00fa30bb8f22a7f03de8e12e35613aacd4d27e0fe9172906645ed35bd53a50e60926cd033a400b4b182697766eb3402fe9b40cd4010ed3134c3e399f94db81eb471aa09eafd928b35c2681c9d28e363ee706eac1256c04e287be7a994a9a78d64f2664622011dd214ea2081685bc53dd1dc1af8a33bebb68c1fae7276190c84a593c66cbddcd04e2dc4fe110cc550077f29bbde962008eae8ab5cc41c8dc7865242419e50bf375867cfd6f9af19241adc90dd12d44876100e2860a556eecf5dc14be894fa661a14a094878adb33775e7eae6eb95f6f0b4469e17ef1b3a5472b11796db2f368332d9fb002b570c2d1bec5e2d675c18cce188458af89d30a5f88b44aed5e94125e275afe9870afac95b6fca22d1ada1fdd585ca771c9adc00cfa748968c720418e254c339b068f7964cca351118c0b6b37b7dc3ccb6bec80a0bb72d268c45e8a64d13110769661d48be9ed68a7805cd51134856be0f188ba24c4b620a8d46a256d6640a7e9d4e5d01ce1b2be915d9e74b53b2b41c24f5813f23d93184ff8f9fbbc4d688357e988b570e4d0bd349bba55471a8bbc915addc86c5ae21768e50f0635223a348047846d80b697eb633cc1119db646cb4d318f7e2f2d208a4ee3ec1cde2cbe20e1a2f32bc32c6f37133f299f1a103128bd43e9a94078c0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d45b5bd526dee5f71de1f68a196fe2fcfab9e5b25cc381cbcdbe7b2846264a38","proof":"0e09f122d5b9403bdc539289fa36db7e8d3d58f55c983dd3868ea6fa4ab21d5334ad272bc31f2159aff23b698e772d801aaffd6be918bebb1554d2d0b9d15901eae73c4cca94f7879a5336967a6d95b4f09d7de071865b746561d9e08f657d058eda469e1242b1bc22bb6d27658abf4f164f6183ad9afb1f869c5068823a31734670058f976ac81cca2128de014c316f5713f1cd014f03eb140012d8741736016ca1f608fa2733e1f673d531b0b58f050194e2efba351456b39194af27f18306c1eb035585ff86bfb39d9e964f37d5bbaf82d96ab6031f3d8b0812fb5553c2054444386f245a93bf90abcc8c8760a91cc190ce618ba97a9aeb8d93f097a83a6aace00618a15f3403765cb79127bcf05b64cfb3c126f62ac9b13ad0685cc36228529611781ff1745a3b4ca3f9990f71e2d7fbd6ac86db84aec77422fe16ba6c20def1f7a0d71ab104c64bea16f2279b4c06a24ed2a9f7be25c25fde0777e532739008f28d8dbd99ac8d2b59d6d3c4a43a0a97a6c312ea7d3f6d7e54021bd2402d421ed6a1de3f2f284e497374ef99fdea71b4f2e301973419f11b657e3d1b1d38dc425f5b18796897138e65dd86b054ee8ae555a50e27e2f503df24e6fd651711080cc75cda3b63e6355222646d851b8f211e90694eb7d39cabf0c60cf9728f35cabf596fc943fbf0bc6ec7112141054f53ec1cb12ee0985e769e157fb7beb74a3aef731a5c1689ce914ef548411a6e23931610f1e0460c8ef0ba5f88b7fca102a45f7d04fa9c5e3ba515e180a85a7a3f7aa4141502a81a86c1a3177ae7d8cf461a30b0b9bd7d4044107dbf7124bd0e1f82209152f52f6477eacaf58d2e75615498e43d268d067595688d6ee0779312c35a72f3b232d43d708febf0bc2a9f7d02669edd5844985ea9c429ea657d4f6b3c1dd7c1974f506494ece55073fd9ddd02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f082be78f597461e957dff049809e4a1997851e1a5b5db7cc8d62dc0b4f6c46d","proof":"8c192c8610470b360c31c5667e9279c68dea1dc5862bfdc771d31eaae153214c8818b028389cea6a0e029d6f1d59af57ad58340d50d49b56083bb8c2c8677c3ae4e1e856babf99c222aef5cb0fa2ed2316b76aa6e6f65c979d521aafe2a26d5716b500bee9793be9fb9593fd284127384fa4d5eff982108e208f0916060cef325bb08e6fa872a1c465ba8a7c662f759fd825f7c8061bfba210affda0b818120e7209cc3613c9c4654b16cfef4131fed703fe543bfdb8d6102d73c8c1c52c660e2c49e885d537964f04affc734621058c39cf5c200660ddc42e32fbbbe10d1c0a90b2c5fa9bcffd37c8319a8120da32d778aa5aacc369d170bd113a834855334cb22006fa8fa5c0f20044138e32116c4fb600d0e6cd6eb57f6eac0fa3c7e9127f344a2789bcb2e6c168dd9194de0400820df0f605c2fdeb54954613e0e0d6c8649e30883d473366754c198761e843807223cf9ea61c56f44c02084cda8eab8a42e2177ca34997f15f4780be59ed259a61bf9a9b117612eb5923dbbcfc5949b86bae1c7fa8ff94b3c13e399f82023c3a0fbb29c1841d1465b0339bd9dbd119617322f2ccbded784c7d2ecf68cbcc25f4e429ea14a2f342e1f311c80099ac21cd619c49545503edd83444f0ff4d999ab34b0e6fa7edde44d62b20b7e7c9e1d1ac2ca84224681e9a56408840ff8592943879a8b10a3782acf9d334fcb022edadb448084cd99ac42ba9c65857a2d0579481e3cc7a28da98842e10f8a63220308c6e73226b3b1dee725bbc02370b9651ba4697a402ffcbf073af9c84149fe0788b0652de82b9181afd2e4ad327f80a7eec72cbb5c6be31c9792ac1358d382c58a624449d3ac1d3081d13126f8f52afba2b45ee4df3578d360bf7aee0f6836cc2edbe06a787b4c6bb25c3c4b3c5fc83dbfbbe1dd165a339e810a1c4b28a05899ef51e0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c0682a2c0565c151c98859a4b79084d33da8212b882d2777b3da5a950799f61f","proof":"b6aa8f4c0f1db2a49004715fe7f49f081d2f620895f7515a883ecdb77dda3f2cf4bc6dbe77fc7fcb7ad9422380596dbf8b08ab1f9fe116a40093d9dc20beb9172c5dc6f9d6daa3e18a02b78f295be9669e7133bd91fa0aa0fb5b3954da33d8262616c81eb506f14dee80a01d6d129a71c959f8de975455f340a2a56ce0f41c5e5c930c4b7f7121d6545a0470265042cb36050fae583ac8598076bc61364b2605ad1746e5a1a36e3e770c282470c2c195ba4b8ccbb15b3a1fef4313278cf99805de37b662a8daab3792926eb8e1ae44dd157ea463e48351a98cc6deaa7f3f420006587e503167750abf497ace3d4a3b90d4ec90616a57dde6579db908485d595bcc81de51396aa1796fe9d69491e0975f758adbe1a01033f59ba61a31b5b71e020052266aeb9d4b742c94494501b2590988860c1f8272892ac6a1b21b98a5da4e900469b04330a918d4067f96952304e6342047b8dc9667a626dd6b204435e201e037ab1fbb282425dde7bf56a181c090a5ef992ead972a481d7cc05e1daf6205ce681b4cc54a0e9cd148f72e02f322bda1cf3eec24922fa7822e830ec3ae8056b846005a0e88a344c8e5a9658cac925a52fa00e9e48488458cde9c9b0cb31d23e2df68f2c6c80fa0b4c05573e9418539173a17596177d93c1c8e9220a16a2e51ae65b05ce17d934da06e49ed35b2e69b861697045daed365d933fde3a0d8463ea0fa6cf9aee8333006ac56a8dd545abc074490ba8b80cee224f46f06ca99fc32eadb475154b1f94618a2813c5eae37a7120bd275d12a0c170f97ed82a0f0633c1686ed1d9b953076a5c749b2d5675bee3944c0596ab0112b913c8f60952c3051c3eb49ff8b3ad08e5d85f2e37f00617851b615e3af87ef0a7fcb27e6d37d70074add6cdf5e92ef2a733f550e467c43332442a9b5c9c4b72577312a25f264200a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"628e54e9e36d77395de741cd5a9e04898229e1c7193a2dd32d8e7bc8defcdc6a","proof":"4c594a8cb060365210955e702393b5d8c0682dbe6c4c73374137aa9b83bdc722343eb7f8aa3b5bcba04495886850875d7f4af4f7a85e53023b1f205523ca09625af6f117867b337695360729501a9d53cad6722a4c39a4cf8720e414157c4c4322fa9558ed29ade1d7ab8ccab145b37a411c4f57b88504956bc6a592a095fb3a919e8e0a1e4679699ecb2cb2c01c570b35f5230482945d5bd23afd0868daef049afa44401aa7a61af8c3a7bf1219fd6d8dcf13a6c21bf5c0b07a77f045993a098c42a14661eff4f92968a555bdbc682b43e108af42d381e283d84005d25d4102d8356c330068b384107477bb4575da19b5517b1c6c144567e2c90caf14a7a14b36265d022260c0cb2e9e3d0ea09e0cb9ba12d65a3a5fbcd668e4d43249cd8c663e611b8be8b890f9c36b953104b1d4ce9245577c54bae18e5c446acf7001b35f60f1462460d756496464d28b415884b207ae39881d39af9735c22722028c4220ce5370658c58aa198fbc0a9eb3d6fb6f0b5d83a79210308dd9dd171a7aa92b4656601bfa06e23931608e28607f3fb3fd37809951b1b0896d380026d904a7be75b6803af93857aed1fbec5f32af551855e50d40092cbf10b8a1906916f262fd3110310e87add7cd022661013d8a36e2fea701e1157620aba52cb0593d8ea71e40dc3af63da041622b71f18f5889eef3854759daf306918baf70017e84bbf69059f804271ca5a456143bf35e4c244e6f8f9600b79a62cba34d878252638e34e4184c8e5e98d3de1132db9e2e40bc5a53bab097897e14a46fe079923abac223c3201e3e6d39d0e29bf286447ae68a2e337285a17b0be2b51b05304b18222eb3fa70f8b9ac97a3105ba55805c7870c70ba7524dbfeda875322497b9c55e8b9443203c20517441a76fab60baa7518f83c8c68763d5706e5277468efa9b41981dc7304"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8aa54c9d6ce1feb90b958f2ad8d3f70255298207b674a86cd31a98fba65bc961","proof":"f08f7c5141a01924c2f939020a692463c995cd489bdf18cfa40969e85e6a226404c2fb5148565c4b3b8fceab6ba0967511632e5c127816265b97ed2daa245d76aadc70c8e39b06349f2748db7111c3157b0f5786390ec4af57188cdc3e95a7688253c02f40834d6bc8482dbbea70523b2e1bcd0c08160c0fcb2b8156b825ed14e46d3bc5bcd88476e12231ffb95815f7e7da68329716feeac3cc3b2fbaffb70d03e4499f9fde4a099a7084b8dd8d05c2bef711ce820b3724ce602c29d8924e0b2c075d6d500f06e9e623c63c9a249c8366f2aa86fb06843ec28c925d0db0990b60158896fb914d5f4cd153aba15bc9365476cdeacd1aed1df944b2c03907eb35defe3c66106ffdaf8663c1204d196ed63364d810732aa166cae84dc3c287bd6c9698af58694ab081487f4fa40180f7aaaa90f6c5bbf7c8c1b3002549849ca6776052e2ca8b8df313c951116e55978ec7884d00c9669fffe18f049403b4830c3c30cb961e050bbdfc131ab51004a367dc20ffab1b0fe2089def1a6ad96d75372f5e76592f98d35c6adf33105eca0e7dc44a9ec3153a671b2b2c1718b77ab68850b0ff56dc9d42a1c39d1f8dd90d3afc00d8548627c6e3b4cb05552a86a5da84037a61fdd32b4c8a267c37ddeb9d5b1c76dc401d5fccf657c8016c86195fa613625ae12692fa57977e3337539d5a2a46b33e68e99fc8cd99f8cbdecb6d07598716bcfecc6d7a62abaa3666c3174b9670267ceb2846bb9b159754eed85f2ea7b447f6b43edfced7561fd5dfec4cff6381a33640810289266a7dd87de03da09bef241e3eef237e5c0834dcf838ab3ffb67b44fb335cc9c82b2a09fd18e5ecfe9ab16f1118e396b3693e055173094ef050e0c43ab4d8059fda933168f86b8d01c1a03a9d06ec7be94c27ec982c2c832269c20dc193898495ed496acdd36461f5b3003"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b6a1d0003808b7dd004c33aebf8ee7baf107b2b2eb0bf349ba3bedd3d6177403","proof":"0cc6f4acb1828d96fae81efc98c05de51185b3bf3724f53f3864cbe6b4c6633beceb688c8d393d3527c19db303e5c83aa090a52eb9facb3645b42950de326a587c66c3cc643396789cc3a7400da87f2964f8bb63af4db9844ddd24df70fc3a6c24c70f2ab42ceb33e11f30453b8fd7068ebeaf6bf0961655c65822939cfbbc6cfa79a8608b9dcff004d6dbd84379a445f8e0f8585c1789e5eb0f58c605bdcd0728613476751624c71c05b62020948a11b384f1e69a331e574f464747a42e9f070b09737e8d79ca4a5bc734c207aad33931a2124bdd20819ff159c0b461769a07a81435449af38b581dcbe42a6b4ea00a33997977abb3cff09dbdd839688bf51b6a2368b0c9cd728af809b2d6344d4edbd410a2e710b7e4d1c7ae890225cc9963329065be684cd1584ab4b87bbfbe79bcb9d08c607ddfdbbd0ccd0f4f78868743f80dd8c2a89af7b2de9d305bfc6a7681619714849a79709022451a89e854bf15383018b52c042b63468b57ea07cfa2d6fd552d178a9ac67e7f0d0633befca63daa2613f9ee43836ea265be8f2455afbba108e9dfe7b9831f8a042ff47bc33751ca5a1e617d34511b60464cf42eba481ba14d97afd7dc1a437278171416ca6e1906185c33372dba3d077a3e3b789877e854400e8c2fcc980f517adff855e9c415ca325c99b69db3d939c1e3cc529ae7fb8d323ffde6e77ca5d35690d1de04c622ec95d7a073c009a73c3a02bb0f61e7d5dabf30f964aeb9078dd8718cb9a15b7e7e3e690259725bf8fcdac28046e929fb4190f1e582f754c3c5cb843a8fcce31e3853bd42772ae6970928efde753afa525beaf6eaa871182b65924c056185962bff6506379a428146d041494dcbbcedec98e0128d52446d489886d37d38c0c50f5e89c15d3cc5673837ec81d7027600bddc936afc3f433d3ebf3675a5799da60e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"88d217988cabf54f16cefa1edcd5ee5b98aaa6e0a30d039b20ab8aa00549642f","proof":"521c961ed7b9915d49a37ab18a623037b85008b5e4a59c6f86060c80b87c697bb835f7dc0a5da0856613eefd9e17b72edc91995b442b485bfe997ca9e7d25e663a16baa67ea90463f616162a8e1739fda3bc9bf422e2df5423b4990d8a79993f7a2961a7cefbc9a977c7e9d80cc2089d345d62010ece8dc7918f10ec3f72b602063d35c6ab4f9062b5f2cacecbbc9d1474ce9ed4d5c54fb1c4e364e1e91b52051a34a4a75a80e26f20177d3c6efb4daa108f94b4682c41f1d9e3dc2e424a1a0c840c94a218c7c189c9192445641096a1aee6a949c06802ff8343928d810de106bac2d8c56664742f61c334806e632f250522ac5c55f3d823f91294615d76c61c82bbdd04aa9521e916dce237456cb437e8d6ba3c933460154ee447caca86be13ccc243fac635933db2450db5a3e4068918b4e6c7c1845e9d5bcf1e07209cb100cc06512e40f5bf26226a3982a80c8a92bebc086acfe2442afcb00bf56840295ce69bd226d725b2fbc1070a54e58d45ca3c0b7f2a8d86052a7fe3325572e3f457403c670634b016a2e3925effeb6dd4bb765c351e55920b78f4b734d04e434b394ece086981602e97bf61cd708a871e5cb394d8ae2612f95e5888034bc8814c1ed4c15359d62cd48369e33abb7aac8f24516a2ebe91db3d75890693a5d1c41e045ca5ae44e1317d299f51bd74580980d5e732baa4da5a246a12ffadbf564a346fcca3c68328830b368615edefd7fa87f1b27d237c03af379a0e33ccb5ea4bfb3f904c7093e2519f7007d2ebf15487ed53120da4b3e7ed324c2614e9fe55a1a50564fc26ad2ff3a91ba5fcc9050a61b6c0f3af3a68fae9f65fc496d0a5920dcc5d1e0774a7a1bba49656bc0d38956387ea1194b969c46dde38aedcdaabbea40d05bf04717eee510e1ffdb986ba4b79588cd51cce3f32e0a98a1e812a33e0267f0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4e16146435fcd1bfbff04064e347206ae73dbcf2a399b8c98eae0718f017bb36","proof":"145f2e3cce64f92f36305aad1b5bfd5e6234f0dd6d60a6cadaba948358a7b37970343bf05ca91a5eae5069f88c34c995b1afb9cd543914d3757c3d6edee7f93ae4363263be28932715d88b0589835f3781e9b84aa6fe2a840f8bc3c1f546a60ca69a9fa9f460f8cf9e240ec4fc85dc4865c8bf63e2d8a320afd5788ce41dcc1b49be1d2a487408931d83f4eb2f6325905706986716606828c0443e8b4634ba02979284af1c6693b0201546402fb90d5e30220e19e1b3deca0a67ebc372b2b907a3e3f8fe2c39acf991221d8338138e1372b44bca50b50f86dd31dbfd5153d00662befd0614e596eb3a4f8ec14ba48d8b3d7842b54ae8e206627bedc8a58c8d787a52c2e21ff6de57fe5851a0d259438270c64b685a1b909bffab8a8dffafbe1aba1c25e3a3a6346f90a3e9c6caa4fd5080d0f22f8ef36493d411f2812a7ab1063e5022cb2f1508d0b94a8ed69fbfc84d0bd7c54312f08e0e32ad0a1311e64e5a4a745f7ea9e8a545c3d1c69fa6005b79384423549ccb14a9df432f44ad7d9b0e90301de3140f1c90615464bd66bb29d879c79ef8c57646ff7246e02d23ab663f3aaaacdb2f005f1db824cf3f3ad184f32ca3d39c3293a43f9d7e18fd0d22e835ec06f389a051df0b568912aefd784d114c829a8d7f91dd92b0b881fb8872b85e60efa1eadf3bb241f218c117f051e5255b0d6708d5d068676bbbe335a19a1d4e704b90fde10a185a725d91c077284fa5df2550ec58ba66156e6c1f0840f842093c83b0f271c5ef29d8fdb03f83eccee42bc5de1e966e27c90367d3a007c7f67f78dcd637e2a57894b8dd11f047cf69ef3092c07ac501edd21856e390046a147d1ae5e5d4039d9ddf143b46a9d92be0b3443df3e9bef0b58f5227ae665738f40393a9af99fbeb550c7d5ac4b864e02b08902ec83c723868a558a6070000f5800e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cab1fb34d2f1f8ba9efcdcc4843ac35d3264d11f4acda270c8130dbbf8fc2f3b","proof":"7a4bb984b45686dcd422addd41842d236fd0397c3030884ba166f2c79e414c63ea0be33355ce357d7329524044b9592dfd71dda28737cbc72a82c2e15482dd4d06b316e89d29db03ab34378e330ed649313213e617140fff46ef759df1f018359a85cbf0dfe19e1ce424a0b8cb9fe7908cab5965db017904a5ff789f79ea4336bece77e6182dd4794b1b084fa729a5a333eff3b11c29c19b43cf78597c63e501af870d354d7068eaef6e94032d1083e1101426e4cc73bcdbecc8bc41612ad50cd2dcb1b5322b124c6bf77b3cadf4c4f18bb6747b14ff8e9b17ddde55eecb980a72a3ebeb45a3e2444e744017c6ea7cdfc5094ec0f8b2ad5a1eaeba51da1a2e2f70cce1fa5a17372050b609e7366dc0a5acf27f2e2fbddf1c2817728f2189721140aebe92c27e319db20c762bea43f25525cd53b79258bc5dffbd759bec6473185ebaa1129963a02e5f05dfa455dcdb951372b77bc52497858ee2b7d3bf60666c6a554cae7a4d7cb12a563e4279c0cfc5202fc31c20082ced22861bc729ee566ed86e442b6b63fd1e465759a7989fdf4ef699db5605e2ddfd20cd21a69ad29e00aa7f501e2529c34f0c44c905d053c8ffbc573f190e4143b7f2d34f59cf2f532e2a19fd64132050cda15ae17cbef577e722ea3e9b6c55225853f7d2cf3124c97dbab14f92b28358f4b9d3d23233d08a69adc034eaa5ac2fed2e976a5d1264034fd2da9155d9e8cbb6677458d272b770bb62d7da60220e809d7397b7108f2d5a6702abf4ed7de632143aac4092df5cd468db8bfc328d601be1fafd0bb2d66fe02a2c5c3cc69eeba0161bae936ffda5bc9631a1a640cd7570c61dc704bcf7729d566f2610dc0ffdfb13820c22b8175ed7bfeffe3da3ea8bca367f635f0dcd97780ddbe30b87c4fefbfabaa96ee0e33aad15a926d9b2e797224926fdd8160e4b1401"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"76ac9219f09bd5b0f4f75fbba8ef6e958c60adcc3b75a6214b1d22781a9fc371","proof":"52350d280d6e264ecfc0b79ec331484419380ac535721ad3c9daa111bfde57542015b2e84f8f930d88e8085520b2cbb50188b3ea92f219727f4c6c3a87d18043a21bfeab75ff836b9465f98d9f914dcd29637f9d714edc4615d39913010dce5ff693d5171ea2652594c139576dd7b824b163b53bbd2ff5cdd1e70ebb14be4b3785c67e2ace8efa37096a1de064c30c25cd9a2c09f20b174c2e4a1f003b4f770e5016ff6fa0750fa4afa9f7f330e92efbf944db3f22a2375e66f3d4703771ea0a8cffcbdb009f0c37b95a8cda207e6f29006edeca75974114adaab08d7b949b0ea2dceb83c6ac637fc3fa94e5fde7c0a1727f3cd1b853c1ed11edd4016a0b34323a883756b7c7f52686f3a7606bfc1409774811a363367c71161294c8d56d4202f86243f2a6b4eacc0c8c9d07f3b5489ccaf58fd1ee7f5a856d5c8bbb6df54a5532733471a4bc1d6cf1fab1033f63ac3e1698ea174ee89fa89ea2b6e782d2990cf2417586a1776befabb24b5bd42106eb4cc75e38dd6fdb4019b720ffcf4d124774da94f3f02ded4e8cbbfb7170f696cc0288e7eef27288aad96f6a42d71dc208663f3a2caf1b68372f6ee6942d8d5d71e4fe9624178c8e2a62f86e33d182e8351a398f9e62f5e4f586a5bf68942b7be9a6acd0898911a68c5f19bb9f82bd500118814f5f2d599c8908639ced0616d4beba5adcdda8014867c739034c36aa8006529f67abb080c281ebe3fb93bc9daedcdcd515dd687e331764c2f8d7d945881514216df8b8c3e9a840851bb6b49167e435cfe53acd9094c55c400f8f81d70318160cd4b2487d2919a1797f6480baebc40393508800f6acba5ea44ae03334817ee6fad5d3cfe3a53df80b1ec7fe36a41df89cf6472a42349aec94eb99620c400edd356abba0c0c279349d091e1ac97c1efd7b425e3e764ad3889bc6b5083f3404"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0e4a2c59aceeefddf3a638562988543b6881921609d72fb75e1ac30274739d1a","proof":"d2f12a998ebb0e5e87306f90dcd800e1d4bfefa17776e4c75575471d3b155342e29a25f13a0933eb018974ff0cde153c78604aa24648e208f82eea6bee6358096eed310f2f9deb3294f9b598913a9feb2e3e0c20f67f2ad90026153784a19c00600e101bc82fbae19e34bd51f41b992b3e8fc22e87311387e53c26c456900b29688b4818b0c98e5c3c284ce340c4780ace8c832d0dac02eca3006ec4c11af7023755aad1f14c7d7b14696ed393342a0b482684e96ffd4c79aa20dd7d8c713f0c5fd7ed9632807c82dd23bc3594728e9788f9823fe3039f8aa9d60f03a93844011a248312eadce38287b477d9d6dd4b3acb0be4f5640a3faf70459f2fd131df3432a269e8c3067d04b55913000f823005505ad77ae60c8b6280e53c5e1dcae549d057b12cab6106994157f6f865513b8f4a524c6defb8b7788a6fb5f2db302c25aa4bd1896880e1922bea8f7e8825b661c9d2c65db7646ac78473a5d18f29056080101a6949c4d18b5f2d02c597767e670286989b206107c9cb44e488aabbca7c2adbdc7c311863a0638d8a0faa5a5b1be912d863d3978184832426bae252de13c63924107e0552b5cea6c12374908ddc2d0c1f8ebd6b1bb68bc58c9f08f2b87b188fc36da328c3d5237ce325cb4c0eaf6a88e0feebe8a32541c718962b6781115e1d160f3921264ec6483afe206455db3f971a4d2ef7d589204d23a3a4b100517c42043da58df41d21d5698e7e1d7387b5a0973ee9730849c6fb0364ac487d739419ad44b7390baba9f939e384ce88e9b327283cc4e8868609a8e184309a2c6476c52a724e72fa22676fceb511f40cf1a9a4940b074a1f11742adec97b926f7edaec1ddc2b09a29be82897f8b4e933f920007aaa2401fa7f7a151618b3392309db70eba5be6b7e9d4176ff69338b35c90dffdfea28c524be493f91ba73fea203"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"52a7ab82e68aa07794da083a0b43495c0a27cea6788c426c659cd01235c6f731","proof":"58ffd7fadbc4decf236cd8cd0efefcf72be47ed1a108e3ecd4f72f32725d882316f667f65af05fab961743d5f537b3d5421fc8ba907fa24726c08a1380c58673d0babd6ed7ccb81044f0d58e189f369cd49eb70a9442685c3692cf4e5e3174599c12162f7bf7c668f8312ef93566fbd2e286387bab27269e87e93b520454f05e909eece48e4d270539c6eafe67a4685a5760b3cb912dc70fc290c07f18680600ef7bc51cb1f80535a861a44fc2eb3d46da3b2836854a5dffc4f89c704549b60abcac12d97ea09e2e39adf5245b3f02d265f02cc8d0d658dbb40b8a182731b10e66d369b5a42b13e0f06e9a095d8aa90ebaf99e9db6a7d444ef8b68baa5f4d570ca2b2403a05128767bb988a04dd8cec9b3886e0bdbfcfe54edda12c0368dbe6b4ae22a403b0e4c28984595648af7550672d2135b49493d86fa118ea3ab5ace42c236f4d39bc921c8f239bc0a5ef26a3be0f3b3e16146f22f9147afd2eabd80226a371e73c8951abe8bbe1ed247b528524cdfab0bb15f2f24ee5e28057804590e58803a099ab3f7d39823adf3f83844581eb7f475cd9933094011fe72a7596c63ca380778bae6b5f5d077a0068264ba3ec2454fc3de17d63056612863d159c20cc0760eb650db7a7146416767f8931ae1d5d0edc17d5651000c7afc9988e91e734cdfdf954ecb0ff18af7a03fa0304676e30119b5066b9487e3583b00847a0a18a81271afb3f250fb90da6d5a7c92247d69c2f02276d8f78c96f57906decb426696d2e78705e2a81ad90002d61ebb2b1971a9383bb1f18ca58b08477ee099526c983c99389c78916270270ba533467121c199c4b3f4867a8a579d03c7b248d01e4f45746618916210d874cf9b9c6fea5a331a6547c1b07815680ec73509374807cf7f0458a3c7b24a215cad1085a73abe0050a7fca3a64cf9fd797b13e075c908"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"263dcbe98d9ae7b428f1a19df7db6659fafb68a18028b30e5e5b04470f33d420","proof":"a4e7a4b4c18826935448eb94124fc18cb7b2cc62b8b4f2bb2be065348e20920caac4667c859a879bc288337cb62b9b8e6e8a1989d238d9aed80c4d6df43c76224e0e24090c75043ddbc196caaad27a207b37aea31d5e4d4282196e88a84290388a69d5aaae96292124d74f5fa46ca0c54d3975460542c8e3139e1a537c3b176cdfa522dc083642d87102c9a98f711a6d975a36feae964d623faebcdcd02493069e6a1aab128140939b0d2ff0efec35574c65a3a12ee29ad90109dae87da56a0c4ad06bfeb65161e79b153fdce36dc2e5d2adec75f7996496b43772a159dd69078aefd7c162a0dbfe5b57b36b1188fab629cd8906c50b20f944400094d0c33b55663305cf3e970090c752d1c5ada16897160eb834c117dc76c067c9df9577955598f9a2c1dff80d3ed4c0e46ac6d95bb370f6fa2bd2da1a8c858cb991b95fe524f4bcbbc017f735f8ef0761a9ccf73a6a46cc5cd296f58c3451571a168de9333e0c17d109d8e812e0899aadf2b966131b031e8b1d7614ba3a17084a3946b1ca1184bfb816e4555a009eb32db94fc49ddd5b22c65ca78e4b798860c35e06483c2a96d9f0274574a9d6308be929e562c769543efe0619e1d397b5f0a6ba94eb4b09da2fc1f7bb413cfa17c24a54c0f1eaacb91865e13d53ce412e1969cf15e3366a887eb75425624dd234bc25b37304963f1e91986bf912bd0cb887bbf8db56c12af2fb49625688f8c6bee95f0291066824fd8c3686ae1c9709d267f74e04bff9107a21d778b48e8729365ce6d70a42b7523e11e0df39c8e72ea01486c655bfb47c98b3e36d4548d448cb791612e65af46a1566be27a30f862507e11adb42dd083026d6665aa5b5938b3e3c0b25a36bfa613e2bf65f415d884c6b438036d44949011d26d09bd38f689343b6bc1b95947754fc225fffc27a3061c53ae70d167cae0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ba56f0f1e48512105709ea09aad8afc251e7e36dff80c703512445cc0b204e7e","proof":"e6379e9251039ecc9a3312508e10894b8c1bae09cebfaeb875ea625b7826606dde826b7b2fea440551744ac0df730150e3bd9d8dd6af4eaa3960a316a8f9663eea71dfb5a7a8b046c8702a2e40c2cd871cd09689f2b400a4e76bbee68921ab21604127f4f36e4a6966a92d72740cd7c8114b7584614586e65ab60b4190671463010d3643a6809b96611414bf68e62e98c754254de62ce1d2fb3d2a081639e40fb105f9388a9d3ab95d83c86aa1a1943674eb77ae59b8abf98a67d0536a351e0f456babfb14be6478c4c2be9f784df726cf1d0efa1c3da430bebaf9feae66ed06c65cb6a117a79c71b8f7f92ac0b629e520442924d9c804617974a39fc91e302e4234ffb8ea74deedddaa53a46e3210acf7bcb1b523c1c897eeeffd820c571d523e1e9177c4f150e3ae8a5d485cfe5863f44950d53e8bbbf32a9cd420ab294378ee2fb07f024d9bcc369000042fcf01b9f5f5276bc31d4dacdac6632bf3a49e6af0e693ffd73026887b0443843032a7564eacdd880166711c5e0ece75673fb81d485ac79053d59450aa2414b639ea35d590573a43bcf56a0618985a08b72a300c80cbe9ebc5c93f3426014f58744b6e4140c6f70b58228335443242a2c6813a60e6b5345e344fb66b40595af8f79e7b7614577d18238c752e9d7394eef865bf4d2a97bc613e9f1b2aee05a26cc392ec47d62e4b3db01847e0a6cd6a89a671a16fcafef31902350574dcad2958f8275035d9835baf6515fc526bbb25a1e094ba79ec2a2eb5a667d217d059f8b62618b8bf4c8a74d75ae8eae603a005f9209b8c21eea0090e9105fbecbd209d2277aea71e6d31208125e8f9b28da138fb408cd35e1cd4c19ffe532b7e1e0ed7f808406b05c8321051193ffcfbddf3ce5bf3144a02171c7027748a091885ee4dd16fe3ee2035485a9ff6b6e5d9f08b4f72e6d5d60e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"de7085116f129576fd56799ab0ef142bf0d8de27ea863119a58b4c37e2593e45","proof":"e280eb9862281c134d1d4fe466029f9fd905b5aacd051c36d17f7ceccabc59305c649bbadf151b28a0f708f42cc29be9aac5a4df5390c0fc5d6fd4eaebdfde7e046f77cdf85da03b712a9a6c42fe0c29b961ecca560378ead6912db363fff76a3eb431b8da135119b8f490bf2e56cbf4090abecb421c24edf016044c21199513be81acdc109e7902dede89fea9587c6cc0b038c8e50dd9643a9d4a77c0d0d90cb4185c42dc5cb475c9413595b9f518ca290e3fb59779e90a3b42c3e87c98850b6110ef8265264c584bffbda8b34f5b6175bca8363d99024d65b2993487fa8e09c4460f87a271b305f6c6910cd81e982ae1b97e10dae64dca0139412ab1737f539cae82a78c73a9c22f9205b0dd9b8b5bf9a190385b96cd2b8c060dbc3cd28f0920e0ec1014c54d9a0386043ff2834ac52cb26628e3e002df1a1adb17e0f101346ea3e0e2c1a84546ae164c46030f4e60043fdd0dfd48dea1bade858684da614dc011ff6cdff402ef035f22bd9a4be769653958b6b8f92989109eb3b034d17967e61bbade34cdbc5addf682c76fc587947809d827d53ac4dbb1ded1eed353ee39b00c7c301b12b1e23005b5a08fc9d4ff289b1f3e20fd07deb331e5d2b7570e5ab0cdfbb87bb2e6ef854d7c3e20dc945f5be7fee359515600467f866717ce5f0a6a9d871d1e9f0f88f42cc172fa124deece4ff079d63d428a07406a7154485f56185bf837e396f6dfe684f0018304546b0e9cdbee0c74af353261e82fce2dcf50e0fbe62231f5a77ceae437e3328773076363903b692d89489278962714850600ca1b4fc0cb4df56214fc70eba10abe0d80aa9fd32fafb154c00e370ae6fc7d513a5a877005f4712eac842b55540e830825c90b29ba2d52c4c4e5af3425c773002deb74e4e592ddaa27cb7ef3f002d17d8a1a2945d64d87cc529ee72503e9c806"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6abad3f37f6a46503fa10be31654f16c0dce8dae5c1cd89e8a414dc5e77b4415","proof":"88f8bc6bd2a36d52d51a053136a151fff4e3c9f494286ebd1a320ba4270a096cf4c4a675b4b4d2f141cb69b3c648d23f4ac919c72b0aa6c27619cd915dd6f754a40bc9d6c2204ba356491dbffef6fe89ffba012261d91cc00dbddf0fb3a7062d3ae5f4a096cbe7e3c20926c22d3b3b25d160875e0bd78efb0a71c92c8b97f23561f3661177b7efa241905de0b4e376b1ccf8fd889c793321537cc6f13bf6150a69dc2b31b7473ebee7f6f339453836440e12fc0c9c679c82c19201fc7b84fe01661966b1f280a2a2bc443e6c7f556dbd220e679c9da9bcd29a53802c5647cb04987928ae8d0f469c062b4393b92e4bdbb234225d5529a0f803fab2cc1949295330360eca58aa879c2316656a16eea5d554f15e8237c12a5d0a88c2298cc1356300495fcf99d2a827ca539c9942e1f56a805e61029828ccd64669c497595cb824fa0c729543a0014e2aff4f1cd946576d58c63c49b82f71b0a39d2d1441d12a00a4579bb21e0e56efd4eeef5dbb5db3ed410a8910188e95e2a9c5728f85b8cb5c927b39e75ef91e5754a6261fc987919812d55cd13c210752e3122fe3e5b86b571e0e77f3a5844543f9900131487ff027443ea45938148910dcb7de11d32eb51606d13d89fca23b105e1481e8003ce25f6ae2794595b8bdb03bfabfc4ea829b2ed4a6c754ac2879eac81372a356149c5e8ee84f75fe1a56483c9d29e2c78b29485c962ac9f06eba54b9978cec6fbba6f3f6cd15502792695d76be824d9613a26540752295ce0375b5a68f20df8e5e6dc53f56330604242a2e9289fc589005bc5d9c30937a2bc62eae830ddd5b180e594b55a33b3a7a0460c5710f6e6d8348ba1a6cab0cb878fdbd9b7db0c4dda8b8c66e294f0993e3f77d906d08c3b15f334906a85e30ef27c76d3765454daacc8f4374bb30702777ff0368d7fe3173a7fe870b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1a989293e57f47547d27782c68135508804fbe368d8e9d381d7fbbd244dd1262","proof":"5c215e582449b7b6daee0a17eaf8590f78bd01a2e808cca1bd8558a9463c101a1823f7dc5879e9472c9629d8d66aa05eec978d38cc6aa8d7cccc2fc24d707f6cc640e927acfd7c702005d33b65c74eda05d65f9b26b6f9e58e8bcd05238c8979d8e9475d456c80676783107081eedc9dd2838756e273a599dee687e334a2ea322594e865b0d1e2ee72c370b95b81fc448f3871ac8fd6a62e61fec9c70a41e00f490dd5dd1318430158d118340f9d03bb3cbd4fb3c8496cabad13af98747c1a0439f885db2f63f6d984b54340e3d8ba0fe638baf730631ae171b634e3679d2507c8a9a4ea6b8f32a37c2cb3edb005a6b181cdfbcdd9222158639b1269477a5e23a48073522d8843151e540aaaf42e3d89fd0927018182e94bf68884469edcf34da6a60db004304682665bd2858c8e4905c42a9b856a8a7140a777b8759c344747147dd403e14c96656e3262b2fa0822b6e26963d0142a63e4674e1284d523bc4440c1b9256877fd0daabd618c6f0e04157349b370f78cd47f4f864f793d0f7e215cdd694f2e82114b0011acc53082d753b1754896747ef4cedd32e5beea6926134e41ab54dfc8fbc17f6b6d0c2b19f7a82fa035156bcf311fe3baab97e428656fd099d70bc7311182e2085b706606a198a9728b56cb38203c705a499314c585604422c6f388daf6a40ff579f735cf129d71a5e439d438067090a0eff246efdc5d220bee1b302c5334805b5a7df85ef83f2992cf95e37d69600adae1ebdbe07558128d684663614fb220e51da02c525eaa8bf9dd4f8fca713178a125e04cd5da3e367d29c64eca47bf09448b795fae058d26890b2c9f70e34f949f7f338e6629596419c421b0e8975181fabe5511c84af0cd8cebeb1df448d4b516220f0731790750f2ae2f530aa5f1c73d011c95113ce557f3c55b58a9373c3bbab4fc63053f07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"92ce6186d468b357f1b5c4b92cb5d4734b85d135b0bcf902c2e0d138f87a5c30","proof":"a29305547fa156bd47b81f61554270d384df371808ddbc8be7448c038c09f5598a0ced21a140b04b8512b051b3f7e1813e857b81828129d8bc5803d027acc11de0df39ca9fe6e8503bc2b8fd117deb2f66ccdaac64a8f5f09d001ffc2bf64970c0f2517261ddfd9fc23777163908e3db6edec75c883253c60fa8717a02292445463cb7de9198289839cf0dd861ab7a1b4ce7def104c42c48491565fc4f9b170a5c80d16f182bfb7d1e79cf2d3f6a0bfd4c27a27ef631c95885586cf3985c9f0d137e84b17f850f2141dfeeb02c1aaff732ad9f233ed72380c7b675cd84da670fc25ef1b75ce7014358e751e79faeac3d40d65c724de72ffc6cf0a9a4ba9c61429eeabcb2eb5e8c0baa23dbcebeab34884f7ddb13e379f54cdf513333ed24fd64ee7d7455d912c89a70ae5d12141e772e5eb3d187dd348ec97a8491d6e395336a7ee887fe58362c81acb1e290c8db8d95086c0690936422a197733e602379ad1ab8e5c4fed39b902ab7dcf25f6801b8ea089f52a1d81e0f5613faa3b2bab80d34fe758ebda50e4a49e6e3f21f01bb75ee96e6a01b003e719a93b9639650d3145352d20b11832e39739829b6213e0eb9e4ef686c4cbcd669ee5b2e14dc3e510d5f34960714fc65616d3161d5041f6c718ef9c8b946d3d82f6c4793815a106e433cae8a6235b3689ec513ff229562c069bcf607e652bb14d6416026f96ecd78574a5c73e31aadb2453a560b188d12eb62ca8cfef63a722d59fc17c758f907524803720bf35802df4520df41b67aef49583cbc85ba5fa4472f18c2c68c291acca3168a64d3d1eccab1c0afbf6059c4da05fcddd9dc0afd513f260c84d938ba15c46a726d579a2f4d5379e3615a93c26cdf7b9a924b0f3667910332084958f75e100aeb41b923d51d19b63aa4ad6fa8990e094bf54c4f85f64b4eab1562c2a07df60f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b64b3ed2224d010a997eb31f8a55eeab748a6de7c3116092410edac4d341b924","proof":"5ec889c2086f401f50af8b47ea061064522e8af8be68ec912627fb7564c8e01b5a7bd31cb7c7a0659c7fa088164debfa388144dcea4501224f9269f435d3973a3a85c4f545e2e57c0bfa7990f7c4fd3448a6fa9c0d92e92697137d563a738576ce3c72c38ab1777dc0a4556fdd09ee992aafbb3e6bdc943ca93f1cd519e60e31167be50828c33ae34dd64d6533556117a77a9b03c562d3479b3e776fb257370241a641293f39c7c2a4fefa130f0070295c807dd3367034a2ddeda5653bd50b0697878cc7281a23b97bc308491cb5f5eb3e4c7cf495afc419dccf30f5ab4c7804aeea0fd82ee6ad78ed4a9007ae2b09b505a6b1bc5679a002721d567b04053a1bee474fb690ac3ee1482b87cc2628bf66a50e6f880de8b12a7bf0c8cc8d5b8113d6958fe05df38658ecbfa53992884d5015e3e20b985c17d3d886030d8e3268628aaa674a9b3fc1cf13085b6c9391a97a6af93b261d09253a1112abf33efe3728d28d7305f856ec0254ce2fe711021495a02d33cfadf45003a87f7fdb066b7802da106df41d8612a9d8264f06b19636385e75f1cec4fdfecccd979b24d060fd19b244fe504ccd96e8dde4abe4b11e5c7c931a82f68d78e17ee42f6569bab15b2c84118944c2757c3ae0dd66a53a3459b89c50f57c2c12ccacb4abde1f94db410d1a1475c612ef9f96b772ec6228460627810f41f5df33a22a206d0083d4b2ea5ff84f836a9a0c9ceae28396bcfbd1165ea766777faefa2372b113c6a34c9fda720670f2dac2734a3c30fed7dadb964d0876a5ad8d8e7b052b7b84c6024711c861ae733903dcc20dcacceb6f4dcd790691bf54863cacdf381c17302f88261e0948e617d7924dfff84e1f646d771144f703a20eb89972f06c3f072e7c8b416b9304a56e456b8011ad3bf40c964f21fd4b18f51b7902f8d6b30b340bcf49a553b309"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d233cb8e5c5bf2d70ae06ecb7d00f13ccfaa4ee895b82c055e05b72db37da978","proof":"26d97f0f46eb1d63070ebb2bb283e61454f3404b57538282aa9e6dc5549d7c328a86df93a2892ad2af6e0b71a6ae454504f70eb858abe71771c84e5483998c198c00fbb105a995b7e6dd5b527f149e19e550aa4195990697f86fb9967550f17ac68fbe7967d60c257ac9f8881c033eabf10f54aacff7a5ea4238be5e1fbcba15b5118ca9fe82c31f020fd7432d9673107754b861e6c7e3c828087065b254d500cc5370e812363c2ded5c66c08c52c0da1f5e71e23bbeebacc989e056a7a0c000a647849046d64a3969d3cb79ff213207a3ce1ccd06adde5211abb7821cae880410f35ac5262d40562decbc5e481f56e5969c5e1f83eb9bfac904c843b7c852599641bc2a2a1fb53f50538b3ed3f0c0892dadf593979047b9399a77fb4edd9965f0472690b62a3a5652fefd038e86ea1e47bd715ee0df96b5e8570e280ce638476c3d2e68f261e975e61decba7ba6429ec6ed5c6cf2ad3ea20c96db10be5fed332057d2a741079390c2a36378ea861dbe120e6f559df953ea846f1cba0482060560440a3fe5f016397bfc1d9a790521488c7a6f62caaaecf1f02c8dbb39dcca7f728e7c1e7ad5f8b3b5e2f599a6029ba47bb049799e649034d81d20534cd9026996a7d2a2c8a59579d8b8f3034f3ee059f911e02263e0156fbbbcc58994ca0249067cb2a09bcc2555b67b84d402b71892e35586fbfa1c31e3ec1b8e5ddb8a297ba0036bd26e4d5b750941e60d5b40124783230612098bf141242afdf28ca76931d0a3b50bf92221d53e10a41d089b77d2e13ebc8e3584a741447a18203bda4164eceb250b829ea68e9899639c0f87267c64e1427d72c76b1a1e468824e06da034eb519dda2e60449623c22fbcbe53028bbf09656fb350c49544de3b572d394f0d3765593b0bc352a4efe03a7ead5d31e442a6d91d611eca3918ae0c433eb92d0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aeb0606d4b675d7e36d248baf996be48956a896c00d21a6b79e9383d881a230b","proof":"14e075a33c57d6a54bbcce29f5ecd40f5935a3ae25cab419ac03ed1012aa2b3d8ad861546cc86486d0a29ef7c12843c9b1c8e59e8072963aae77cb947e03b128449b27ae181afd8bcba261009178faf01fcb63c82b72b5ba88ce5506ac97a95092e12c5670d5084de7e7990d4681898370b13b67cba9cafb5b5e512c705b123f0469ed98d5200f2e36b59df844d8abd5b4b82ccce8490943157a00f853355b06ffa2ed6e147a0ca8fdeef203bd96aade883e5f77054a3049047b3d9cf4da3708f92b9c3299cdd72586fcbcac7784bb867f7c096ba36c6418ce55e3b403b4c406a458309d436fa70b6f8a3285f5c788b81a177934483bc41b10aacf4490baae3d285edb87a73bd747ed0aacfb45dd2ad6d35d169a249c861fb5223ed15459c61d5c193ea75be2481aaff590ea9256f496d9e23362d4be543171d333285156e906e0de395508f0577186bddea9d60d31ea074f0254ac4d6a9f16a2c01268447f24be1442461343d07345a03bd9a535ef2d60de9e92e8afe9bfbb5375f8957e772ab68e1b3517d0ee6466a0d62d3cd8b6f58b5f3c78b24bd9ba98d0346adbf04d28a0ba01c6e70789de5307d4c82cdd1b35644ab6626957113a3a8b5b2b0b79951fa0b30a076635b844bcec4ab627d6e478a42ffd2ad15cd52a00f9d9fbc867fd0190201da8dd9a9a10de367104a0d328c127318a094de9898ec8cd33c9b57314242859574889ef58c71b992410f3fa5a8d1f423470386e822c511fc873d72da3236ed114ca8fe07f7557afc2641725981974aa456cbc0e1a57339876781f27fd39eaa33a6e15e27184901c4c9e9dc5c8a0546f3e5af11e9f9ba3e79bfdfd49cf5cbdee7e0c9c5236a2a7912fd25988b88fb75ba28cd81af517e271259f1c7c2f07679e36993a6fdc9921530e7162b399b0afc3914b5a260376e89c77e8c3805307"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4e2522ec73adb8c3c8a251c4b75e2c4050e8b7f1af28fc3a72b0e2ffe81db074","proof":"183f5dc7610bd428b390ca87e619b82a425a63638c971e46725414f274c23272383d61d38c1247e50de0444571db9ff0a0692af4a711d6b903b58f906299ea02a2c2d27dbd00ceb15fae42ed5374de34bc26bf7b55204f89b2ab00afda0d290e9c148a1e5fc027b038954eefc4440a46bd3a70c45192272f03e73556f2ab5c17ddab280cc263959667e70a2600b2def3743424d08b4bfbc0f749127b9fb988006b2bc1aa03d9ccbbcaa27758ead5bc652a9119dca323e7933bcf84c3d8453801ae75da6e73215759d478e4fb0cf6aacf62fdc0ffae08e19e4697c9ce429a1f0f6469883991e5245dcdfce80fbfb0712a09c9464feeb3266ca19cef41e6960e185011949e9a98e1b4387b63add3d8aad910a315f78f0529e69f26c5fe9dd7ef4420868024e37df06a1235d10722a2e6362484b3d716f93b1efcf007d1090b942e586147b15845ff49af428f7b22fe2a55a2f5f7be63c1fdc3b2172c06d99adf0ad2d86f7f3d086fd79d4a3708f2fe7c948b7142807eb626c561fbb39a716e244e10bf6a41703dc8342606e123dd8a6d9f1e0dad0fa07a123c36596153a1d0a6765684768259b05e1d13a890890c8fd665ed2194c9a06beb3dca64fee2981bc84516d9d33ec7b29d928f0920f4e075d9dffd149a7ea7a88471f986119448bf54545441a0e4a1250974250d96ef43c24020003e0fda384b1a5f1aec2fbe6e92a9087821f8c2ce50ae5774de649f6412e4e94f3f71c068a8303d4c8aa1d90460ed4a98f5d46ed908a65f35c77a38aeca6ddac55b3d93bffd8e4c7b2d321a5bab7018be00690ce1de42bca514fae81135cdfb49e3601f85c989133c60311098125240b38bef5e8bd34c869109d0d2581f1a28fe571e9e7e2d285f83a30d9d7467c70978b8e2fdbe8f0e4df87f5487503b72a6bd9d02fa9a494cc54f0130ac51676207"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d04dbf1101a2fb45fcff52f4f34e6395f793baad74a5636a2f76d7773aa7440a","proof":"467f6ecae6f288d3bf2e51af931ae518356ca5d83546906a394113efc5af865f62584d03816e9a234f4b24180c089eb61336d289a9367a0e761ec0702ccd875e1e6d9301624613d2cacdd6983badba793221554e6a2811aab228f8e36c79c50084a4b10a745b51ff96f4dfc0c1a12c131bc3a18550dd97a02f15c3e6b5219853c9ba5482916853b47b1724bd7990cda0eaad3ccd001fec44f8a5c7237e00a3007fdf22b0595fbb1dfe095a2d069ce0194f5de8776de9a947e6e464f4ab583305b8d8427468bf3059e9c1156453e2e085f4957c5355d6a5cd70f2ac4691cad007d448c8522299e9479c3b69ae49adb9eda1d10ba6461db61f3320e3929b41ce3206cb84441173e5b05a185a321b3a658ebd9c1afccb98fbc172dc8d9860e777361a396a150183072dc056a337526a0de932b7f8d5a167727f82fcd0b4253332554ef53a4b258be803f3d5631f4e560276ce4c6cc735caf5b1a1de65d3b0b17c4e9ef3c32d86e7e4fd3179c2b8c5836423773f9e0f6b5770fb9afd0e15c5ebd2480a7cec606b6dfe447f69250345fcf17890aa76bdbb3e830c072155cd042a0543940ab16d0377b619ad318a8eca8a6c0bebabb6d3981a7f05e84daf692d928652f27de75c58e68cc6a3f35942ba6dc343e81cb0b1295fbf7e0badc88c92a4ea005ead6db16d54213e36e877cccdca77dd576f02475000b7ec9572f8845a170c7ca6a9e1a3f2fbb0e07b665744bb4ab1cbaf8e765915df37e9d5c173ecbd8bad0bf6042500536997863161279b4ec51d97e89189b241a4f1a5068f4acdfbbed545ec4e2e6c2cb80874d5937f761d9bd0b436eb17a57ee2c9cc8ab15a256524ee0856bb64e02e09994c47bd926813938ccbc556a49086e51983694cedc4fa4f79011b458b21f68f847ff62570084a19ca1ea548837b4acd6169cd2570f78bb3700e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c21bb2f99797eca7712693305c5c3eb754c4d393db6dbd24e02cccf4fa121f21","proof":"3e7c854923e57971a8d584ffa5526bec78b15836d51f7e5b88f77ab545da4127264474af3f8519899f734ce09606e1c39d03081ca81e00a471ad259a17eef94280ac86cfa55ca2019ec01c944f74bce8f9cd2408a49700c5424b0435a7b24a5a7c4a937e2696ad542f903968f43437a664bcfd1506c462ddeb84b26d3d9d425e3dc8d96da4de22d0c0c6420a236741a6545db8af023c4d71f128798cebccd602ff311c23c5fed60f3d8a7c3bb7dade567362b00ed09002ed4cc4dc9cc5a4fa0bb36afbb978d08ca67530eae56614f7d7497c83d8ffe7209793f5c8b7f76e4709ea6cf4db0bf3eeff9e352c11d6e98c3a318dc048cd0666ce1eb275945842547e3206fb5c0b8794a4a24c9f019e7de508477608ec94accaea3bc5657b921aa7582cab959cbdeeca1bf196cd6f1cf339be614bd95fc5c7b3284e13395806a73d4e14fb155a195893969c5bfe5358d2410eb65c8798347feafc9e19c6e472b5174b06d5357ca33f27cc000d94486a13ac3068fcdb9b462b4b4b087a943a64ce5544421e97443a2cd81398df10629a8ff3602a8e00ad89b1e827eab4784a769ad94f76d590ad64376f6d56d52d21ca15972bfc91b18fe2f78d4486b5dd769f182e642a0e0616347e606f3494cd55c8b68dbb8dc52a9c6762ccadfcbc9defc9131a4974727dbc591efc337d7cf9b6b8b0f75424d0cd0e886907d45387d931622a255d947bf950ef11e4da9fbbb8eab3eb8019cc8e7eac7f4bce1dad1661b3e29cbf6310a8e2f9f8c85005e91a631b99421cf01a64867228bd8a2e477f0f4906674728aeab55fd8bc2fe7d0499ef386510865ceaee9cc372290c8cef633a429cb4dd68152c3708f0bb929f3ee18f3c08362522390a0e2ff3d180ad627c81c526a6180c11362306ca092d765c279b391f6e9fe41090bf7feba0948bfcd9e4e953a48407"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4e3ca57399ee8098ee9436d8d42c0c203ca82655f68ca1bf646f82a8e9ff671f","proof":"c08227f6e91b4ac57ace243ba296b9dec517659f32c4cb553c6ea099902d917d32c4197d5153ad26de83c53e21ae886bb46b465cbc931ed5fe84a661dd7c0f507287746fc21163d8a32b9e239135795fa102219b980241a3c48bc2976a82876ea2fa2cd8e686ebb9534f8b03961d52cc29daeeb48e48ec34d954bc2aa9697f42ed226c16a42464ee0315bdf57ac8fbd993270c32f9e49c5db3907751322e780a2a28c24095daff3ead16f1c4c1af721ee948ab0661f92e99d2f33346e5e304004fdd74d6e4086f8c8aa865eb5b29af7ccab5d69a313716d2a5e8476facfb5b0bde6c2f298b962ea505ba7dda57cfd15ffcdc04c23c3bfa81f9ce82cec394e57cb60f3fa70fc3a771f9f45e936944335962b1fcc7eccbe2fe7e7024ac91ba6d2e707e2fafd9ff813e71d528dd3ce4ade3698f0980a9e14b85a8eb276e8a2ec41b829ec7bf6756cd566c19842f3744452b26305cf346e3b64ab4c1865a15daa86bd6f364cef7b2c5d9b9d4576edcbe8bab95d0dc3ecc2851808117cf93535afe6638f62ee91e80e6af98aa2d87bc4945dcaf87836654e3f9daf5658d74cb2e8a380029875c8bd92fe5c00769236621f57be5c3ea7dca7f1dc50d3d9fccd13fee6764586f8adbbbcaca5f9803474de655e6ff0291365f245b795b0bd7010a56963da60b732efcd8b13dc542eda9ec43e5dbf3badd23a0349a1f92a64a4f84ab7e0632f6e33891d04218b33ab7c0b92418ae5ad4e552667081e2a6b1b6fdefd57b5b2a33deaa08e3af4597de4c41a14a161e5e51bdf8961e70a7e44b1924abdb422822ffa9331cf11db253f6a0151eb82a23983280e1e71dac19d65b59d941cf240e297b4faa6ea037a82ee8618a8b6e7b67632262a8a4a8da28dce329ce89142c055d36daf2e41e8b55c90584fb56011069bc4d0e0ec85e406c3e749f896f49100b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"24e2adf7fd71e357bf0b589f1425a48394db283a89f9b77431f97dfd716e0f29","proof":"2c04799d115120ac6c6ec9c870bfaca985ef48183d50dc02127af4fbe0be367baccc8cd271ad2721131a2e42160344bc2776c4269b52c01cc0128bf60dfc1f3574a73c6080476f3283236a59ebeee1faa0fadcf269703b5f0500049ef04cee02f0eae1ea89d55b500c51ed91210a0574af099c15480683ffb31b9f5616e0b8696f3d813cb7a184f510a118f893a1a04a2de3448bd4f7a5e20496f3f1fa0b4c0ded4eefa5d5a409c068b7db1f2c82a3d1b6c91e8050d5f136a7052e33af6f7d066b4995d82cbe62e90442c02bb501c6f45f3f3b87631d4e83721abd1c9b41ca049cc859e8e0ded2e74e9a0857429bc35ba42c85ecab2461be98ba2ba64a63b463e4808daf42b0650a727b6ed509951752e57a7a7c150ec6f5eb79b05033f6bd3c3ef2c86cab9e6c90c01a770906896f6640f950460daf3a49f12434119026ca34ac0d8d8232cbb8c999b0868ffbdc964077c27a31f34f4923064ab8642d41a9058ed554a9a05120e9465373bfa903a21449b1e6e2f2191d4c1cd2b30d9b1bbb78de980d14fb77d77aa7c235909d0d70df6acdd4bccdf06307ba7ed6e60dfd39368e3559c8651ddd4c8edbae7a9dc09e5095e3c3fd7795512167c25858e85daf40aeb55bd694b957d291d9a8c946cf9cac1ab200d5489fcd9de646101b78393f4f7235ecf9bbfa242b2b224e0343e2be6098e2e731f14bc8ec425e66b12ce2d627d05e4d27c09efd340ea88a530b1806becdb29b9a329aae1ec02da7f3ed56511576ff1145f9e7bfa85101564a73337caeaeebb4d0c78828b05a13b74fd6880f0da8cca9eab1bf265b88691e66a00b589405f42c139d82e178a7c5bea8246ef05df205fc4fe4922bd6bbf1198ae592978d8258024ad1db77fa0ba47d9516b72a0b03d0f277a0b3df120adca09f9501a8b0f736dc9a69ae72439741ac20bb8ef00d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3811a8648353506525e3e4c7faf272882ebef0622b0312bf29f575274340382d","proof":"0a434f605d8307e4d16263e2932003e142d56a21a3b832fb13617b70a3606c50401d2b60ab0d7039a325bc087a0588758bdbbac7786a962cace50f35393adf6458f4b2d0d99563f50f3bbbf489a56409b36589137db0f22ec09e48fff966140826701e22bb6098c6b427956d3d0eadaddf9014f79a4e9a35cc56590572f7cf6649a011d0791f8babf4577a44856236f4b60d28351cc15b887abc6c9657702206b0e6f7fe9bc3afb99129b94c4162ef712a8efa4c5b4072446eeeedb5953bdb00891f3fc717fea828c70255fdf8fc3c52183ce14963bd7d15d2cc1ef6e555ee02aea6de1629e2de50fd60cc6f3ef5e9b96c0bfdfc2a2611a2f473b8463fba334802dc43c73b7c35646667afb32b211dfb76c9218a9594c798cb06c2423628ed33ccd90c15f03913558e1f61a49c981c684de40d0374eec453e1c39565781a4c0844e69265725e6dbb54d141f4bf1d8d5d19415b7b4ae6b8e8ad1c53af7efbe00c228e88b27ee3499f2f30e36b0e7e096d20d11f1996e50257ca6e55d5035caf3584d633aee4e44a990d6c3ff78433bf05b66445fdfc5f846b31b5a97235da993826dd1a1689a3c1f6c106181ab49617e06057beaad9c4516359de6f08d90e8657c01448bf7def2176d1dd8e0d67c0a4edc55f81f7bba873335885865f9aff1f0070430d0d17176abc3acd955c3e7ca008c24ac311e0cda4ab88c38a8cd6574e1aa8a9cc2d05490e0bd9b59265da40fe632f5f16b42d0089d63be69f96351c46405e147f21cb73f27f129cd956532755016794dc717cd9b383bcce5f615bdeaa6592e19e963191ee00f1e3b83df1a9b16ebd637daf68fecb97469cd6dd92e4143211cf8dff2058d269031e4339610b71b3427cd11482735e72252c22b616960401a6615ec4400c9f7715cfa327ab28f57048d0a2dd0b5abe13e07e78840d776805"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"66af8691c4f99a2e733325e35b0a7ffe3901d212107cc057e6a0e7cd40658039","proof":"f41cae5aa6ce0a9d783f85e3fd2c29b1cf2d5211ac64030860924454a1b7f105c0ac61485b36c307d24a569292c1485dce173ee06dcf2a61059aa76847c7fe60f4f5a69f4c62b21d4cb536e25cf8c59621286d59b0bd7c2bc343fea4f3fd1161ac63943f90f9d814532cfab47d2098f4dc7a75ee2e7f9f1934e2d8f5ff76ee28509118bbb19693779aef20cd1c1771ceeb01b4d6fe16d708ae91fb36a9780e021d544261936a697feaa194bf42b151c03a94d33ff28b7cc26ebbdd1d851ddc0ce156ea50c162397fb7f6f7c4ab1975572ebf465d0102611881132b2e492a510aa6d06ee9b84f47cf1456208fcdc6b70d4604deeb3d47e2c90ab14a994677f072d690d156e7cd1963f369548282bd336aed4a6f616f85101bb26694988605a77164f3223c3b70947a29925e5f6d9f48f901e91c4ceee96633946e8104d4d7cd012675e52eed2cf91a842862db7403df60b1c62a6a589d2a49e5c94b341022284ff25468a059b52651e53442afcbbdae2c8e50700d1a6387483b670189c3c34e0f806adcf71a52f8b635da4651ad3dcc9c52fb88b54c7a29af9da7718f98847760c2bd6bbf0aa3369a53fd06a3b1e19a7864d4e789aff24d87d1e669d2344d8779488aa48696efc427ee51b8252bf7a45acc9cfbd48460acfe2a9e885d9171517edaf78f17dd54f37e5c027464fb709e52f17e2e3cc637efbcd4247e15a084437a80f8618fa558d90b7a1b99c921ca191c5230a5b743093ea99fb946bf1e2006107ece1724c88d544f07244fb910f7a65fa5cbd25b0107e5e27c01fb37b0a33c55d679f95e9f4ed42c80bae036b86d3f9ab59a13dfe5e387cc65bdc66694a61561722d2210c4b57ba1a16e9d3f6cc8c9586801c90e400ee5cb4c2feeda6df8e80ccecb338db08695b276e85cea5de40c2f1d1cacf284561ca002628a5b36adab05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8244c5da19426065f7c03c1bd9972fb1268360585f3f3d2468283e67a245624b","proof":"fc02a7accd540ab57a4389c5f89d3f1d0b76bcea7a832c3b283a6750a778f92158b9203427a582e4fd1ff7fa55d042ecdcdb04952c10840fdefb6bca706e9d5e64a4750ce22d7cdfe819280e59eaa4ffdea382229b7d3ea53303ff5b3658eb636af48525243b5ea3496eaff581f4ffda1104beac05667d027ef90a28a129ea5db2043b27afb8824557ba44da5a2d30ac06c9cb6d1f787437747522f95408cd04c58c546c0bd20043547b28fd2daac7cf6453898795e93e1a6022210c3576b500758323ea8d3262b2af79527c6766a862364e6becf0b3d98205b42da63e4f7600c456247474a984c0c2be5b9e3ce1aa5d8e19576853300520da7a40559eb43c245e6165f141af6d665cd77f44b9e3d93c0e5ee2177cc964341d224919ba909e26d05adb9ee28cbf7311dd396ba77c96c183d9aeabc4d10ffaa50d6139285bfb0a8e2a85e18ffaaaf8cb8b3ea6951745364d96943d97a9e725c1560865cd724e1c962f831ea1a3392afb30d23dc97adec68179d9aa5eb1eb8ac8d5d51ea85ab26be4260119d2d8ae6518f1fd4ad4c4b4b6733257b623213d765e7dc4a0eb112a1b5e830dc810786c379dc3eec11f5100a4051f6dacc1d7b27908f57e23c3100a3a3e4d054cc42ec68c580aff7e02c8ad434c8777dd98b0f007a9889137077aec4aee9c90367e93854a06aec4398b6721909753cb9281fdc6a4a42307e81b7a1e1d5e5ceefc01d49265645b563e9792d2b4f10ebc2cf74c87fb9aa83a73ca15b06c046b3f99a73e8e8f268397551e378bc6f45f3fa0055d2a368fbc5e126413913926ba8732cfa2be909f0cf7d58a31821306f8527c500c98b4fd1e29cdf9c064577e398b2b1440dae697fb542c1bcbb182eb2b99c9cb3d187ea5ec7f45dce7f30522ea6277882090a7c71ef98b32e495f77d38ffb4617a1ede902083d9f5460a09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5056d762de7c3642d50ff6b02028685be2d56009fed862ddcc30796c417a0450","proof":"b0440efebe07b76017f12b29f908c64b6ed69c6d43a3d44618908890b8b38317a0a0f661a194e5c30fc7df647b298a60b5358b1bff1ffbbc98fba4275d86e262d0edefbb1b58953b22cb5c6d8e922beb57fb5393acc5173c1b01442522c0b338240250ccf0ae00249583bf2a831289901d1784bbab57ea0393077439fdc50436f9c9637f02b92a44482e554b6aaaa05f3ce9dc78c2d0bcb30210121dd596350ee0b9b9889cc2f86e0e80f6f44e0067c5d9f1a9ac2199564d44f00d13a1efa30354b11835e6a7e40f118ac1cbf981664c7d081dd0a1e0f7bb629681df336ca70d2a73686926335962c7cbe539fda2338d5bb0b0c262333fee29bc8a3b7d38b65ec4d2285c98d80666ec80fed54f4d065d8599345c6b8d5ecc1a3565005e625f5f5057e66da7aa0180578d6b5f2dc6287846d1e311acf7902d20491b54b156627230193e5e37edb9519d3ac5e5e5344d20679c1077b4711b693a2eb1367a6be333707b95bce1d6658f58a1feae848355878e903e2927d6d394f03c5b94054b9602686a31068065068730cc7a7b759a38766ab368217e90b59d4412c46281665b654290c05f03d5738dd7c2091c4aa4096e25267c10c06a905727ac4e74ea5f4b50b2a2fa1be2653f57cca733cbe818ff6913094c79174def619e1bb4f09d441531dc9b2aa1aa90aeffb4114bf3d6fbea0a0ac4f4ec8ccd6d82f7001e64a85f36779229fce1d384212e39f470593233279e5c68592b70404d130a928fbbfc47823b961c4166e2eb278ccd56b2962d01e2d8ca124a964c7b7b40002edd1126249a7cea60ee48f29c26ef2e728ba258bb4e31323fe920b69f69f10f331f37c1e2552b86ef641f5089a63f0992ed17d4056b8d0e1461e7bf259038881922b31f301502bf7fc440e0223a0979d4d91df8d28b864614d999bc1b428b73d5154c66d1b00b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"22bbce9a2148f70bdb459c996cf15456a09ea5a464db6a8f7c3be48c5b70e266","proof":"08c2d71b772ce987baa8b8e43cb8b575dc8283cd66c8092a682f1cc2b405c82080469c6283434a36e923534d94e78af238a7d3e1e00db13e063cd4542843fc42e8e4898a065afc51927a9c13d32e3b26c2ad6f138599ef2b393b40f5258d7771b0d848fe2ca0723e35721f93ca88992a6fbe7f2677fbe528044898c69c57f70e921c7681b6ccb03271f1f6d6bb09c74f37e51b5f332b86b5f8db692ffb70c50cc855f6ef52d591d65c62cf0227005bd9d440e052040eb7dc7dcc152cb683910ed029930f9108774ac26163b7a05d23015adfef0d2adb7084ce7ba54893df830dfae6309edec9b2613cf48a290a8b619a47a7ce20e38b5e757fab4f313b49330288b3271f15256e0825cc745b915ac5f19c2aa449c9047949a2e5eb12bf2b812edecb5ef2941994057c42ede2c51e3711337fc2b8ab9b021791271463b3eb663cb241e39e3a5c4bb297b744f260b619792bd49d6c8f7734583cfce05bdf0b424bba5d14b16443bb34c1bb38c58a71ebac0aa04e41b494a56473a242881fb0c2455c06b207136dc6715acaee9939601a49e8cba8976639ba7846e4a61535e6b2631ce292bfe438daccb31793c6337bf29ab5b21136d77ae6ba2239f06073f09922b0da4a05c2e52817f56390de498ee60e96f0905c03de7b5ca96404f5e02e9406d0ff5ac680f7dbd3efe02be671b846a2ff26f867e45b815961504babb194db1604fb269b88c50a715631074f0f9eea4e23df0dd5d6ffd3823440ea7df087fe23a28336fd0a78bd99c7c375adc2a89140dbe7005b2df7a32f900d1c61eaf890302cc83987b9bc82fc297b32fa9e2109643e747638f8117b4273032dad5b5b5f7b81e077d409e224eab13b369134d06e021d8f7d299cf0ce21ec703ace6bfb7f02d8b34096a71a83831153976a87e01b00e27cb15c50e303406ba9f1f0236a1f0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9e499deffb1a5ae41ab1cab9e88c6ca72a0e4103b0f07e73dd47b947a8f3ad37","proof":"1e104b69c948e2a3e964f323d1393ac9d2e5019a8833ea901f554ac4b7da1566da7211b3e5503eea6042c4061bd6f9a5749534b0d1e759d34231eb164b8ca151acbd1980b1ec7ecbcbe94d2fa2c47bbcf9f5150cf115c36c9bd81eb69f022c5f4a550304a3585d0f0b821479b7c01aa3c8dfd807b152d0bbcec69522cea12214ef7fbc32446fbaf920473e9ccf806cf191bb5bdadf5b7c19ee7c36cb8fb917048062b89304f109359a58a42b15184376bff3663a10469efe354ada146cc8bc0baaa403c252614520595fbfbcfc37c64175d931d2f0000108e46a808108718205322eefa4efcb4123e3b3f27365ae7bd256918f8b93161d0239829458179e6560eeb24a87f6833211bb79a9342375a74b5658699f11357e045976fa1aa2514d4ad23b73354f1588b962e12e976e4925690cfcfb70d9ba61f733d48b4efb7efb060c58d57a7a65fe2e097a6b8d2f021e8b469ac42c58c7466fc44fa1f7611b170972f9cc22b06d15674607d30eb528318e2884931d55c3a0674004cb931a2f9b686aae7a142ed903c1bcbf2d8c3326af828f28a5ad31b685f2f57080b2d71b2e16847317404e275eb4e9feb36e8ec5feb70d2cf7774d6ed9524f1f467540b07f30964a00f26b12b2d45c1d35205a905339f3cfd2bfcfe5ec8da90a12b25c08b236a06acaef0ed355e0f0b6031502b705d291257f98c779b08126df9f80a0663d095240ae5110604046bb9eeb6b5b58097b47faf2067566154ac4d2f40f53d1d956cc2876333960101da0f08582ece21094ba50675e83faf97093c09d40412ec84800efd6b7389d19555036a71b05963810f6f7ced88cd8580a97a6b525c5565840b7795a0fd7635b8aafb3bb4e7798ee11d289e67e275efda44c87839fc8a50e00669f3ddad98fdcdad009c5e9438d120aba847941e4936fd9d86c7d4d66afed0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"94dd63207c31907b29d5c84a3b1415976123c75d2754e35d275c283cda5b5f62","proof":"86747c225b150728a9f4a7e354ee3e4f9238024d11511698b3fe41d4fc9f86186ea62080fa3c1dc5b7c557c4300eaef78251fb8cd23af70ac7bc115ddc07400fa411222972d0ca4e2f57196dc4169eabc18ea8fb4e9174938af6b8d02cfd9529cc0104a295eb14ade444c746e76f02b292d97172cbda155599b09456bfa7434b9921e4555c95c1684769f6b8a117f02f48d1944f680633428a8a2f9c1e269f0be2fa80a4a198cc46be47df207f41d823f4048e02037418021618525e287a89037e54e21d2c28bc4adddbf0818785cbda6addb031c88dd6f2b3a6c6c58aab020d28a17ab185d9702b12a70c5ff421dba329e3537d000fc2d546c0a831bd23fe020a5f6bcfa379bcb61a8f8efe66e7f5b25163369704a3d3fb556caec948ab4f3e7aa379081b9701c581c773c07a16f203d9132b58a677b680f9b6a0e0978a2b15cad2b0be9e3b9ff07d4174b8df12a4474c3019ee16018d3f1cdb5e18302c4e1a846cd964ce6a0774e03d1f2cb214d815a8c609042347500f1ca38574837fe55db0af81b57702cf66c18c0e618be177ff17f88d13c4ff57345ec48f9ff59d872d8cddd53c6cfa3af2c0429b12b2c710a452351187d558bd8c748eb3c14a5d3212f893c45fee14d35c4d1ae24d40409403c577ff2480f0885cf06b37806a34756b9ed020148e1fca1fbe43da945c275e1f70b4d200d2b0be39425849bcd7b149341cac5340276ccc6f329ba3584b32bf623757ec368a77ee215ec7a086bf2e2425d04ef804fa8b1946e71f13e68b668e69fb963f55721818ccf2add427f8cffc3888f3a1809e23b37362f75ecf2f8f5ea5046c99dcc3f335971a6063081366416903da1744ea35e9dabc43e357007748daa97c3d4c65787bb85cb0f2e7699faf032198776195f3a0319f5305a261f83340e88665d2fbe440c8f28de0c7995c7806"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"06c68a8a4620289de189cd26fc25f49f5c1028ddf0fc6ba9ac0484b9abada41e","proof":"0a82d45050b48b3d4c7f264c16cc39c1f077e9d89ed249953a68575fd67adf5a94dd2eae6363b3f0f856e39f90925cb2276fab8b0ec2a911de2c2483aba250609ed9c0cd4efddcd3afee0e3916273cc24aa4ee0fd4c00146960342350a89712ee28ce6fb8eb2a5647d01ce6919a70885ddc08cf47c0a48195a3b6c305d2e7e472e46a54b5d95e093ddf85543cc74c231664a7e7a43055c2080ac1b838169960bf7ebb34bb96d3afcc02dbdf93707de077932002533f852b75b83cc2657e60b03bd5fd0160bf2ec83dbc88ce75ab8d105879da2280afbf41ef65a6b296d6276097c9bf8e4d8040baa0a382b5fbc60c6723c23e0830b188d4548e067bd76beb90a5c5708e2ada87faa987255479eb3573f43e218a65b0e04c0fffb9ea2e425f45380f63b5af42bb2720c17fd4c55b1b05608e19548394c1e782db27f4aed706f3f2622443e6e12d013ba1200027fc7d10f353499cc288a3e0ce77defbe78cbd21e3c7eefa0e3e372f5ccccb53729316187d0271c0ee59c1e2f7af951562abe867cbadf045d895c980f93e8d84ba76e6ca837c6615c896c38c9fc79b3c8edeef961022fba2034ad3691f67a5a89e24a2c36b66b30bbf9ae7aecd9f1743e8398f64e82668dadbdc1f73bc60820eda4a840a2a3e78be5a78493b19be83af69a8b376bee69a20d64d1d8feea90ed0e7b719f88805d842283172cee33b053f04e207c7f6c0ff1e0a955a769089696641c907689d835c48a642274d9da24bbd83a225f444c36f6847438c4a0f3cd7ffbcb014ca0b51b438835d9881f7fa9886c8d0c4f0b220ad653c52d4e9fcb94349e780f3f6a95f5da4fbc978f99bb01f2898a8f963e61b3a20fda6fd385595baf43180690486fcae4d7ab89fe23ff0d598428691b0c1bc85b5aff4386c3165e385e79b492d60fe600215d859903ef11113868a83802"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0ef96dee68ba9a5555c26e6d6bb5ca1214b1d5596d35f9c04933bc9e892ce229","proof":"900f33a41efb71a82c2aea5d2efe302120923def6eccb7fc2821a3dc306b0117cc4614c1019a79735bf97c3c06d708d23452a409bbfa489bcb98f995893a4d3de63ce0b5b38cdc487586c3c8b7503f4a64ca9aeaf394a1ac669d4047ed50127d9241ff2c03103a953820c5b9c5af06ccbe1b2bc48f0538572cc933ff838030437205dccc57fe48b6742a914196d3d6253baa0ae8facfd987fcd44ef56ece9f0aaa30476360320c4032d5fb8d90efd475c0396bc449373a1d7f60608161eb4109c95895009cfd59ec5c377354fff025a7610123aea28980feb8b0eb20598c750810bdea32a26f632f2a6a9a4deb4484d70df47b883f26b59b4c6493edfbee8463ae27eb1ca913e7ae3da989d83d03d89fb7002462f57f040bf33c67a95b96bf4a4e60b9d20f05aa26c45995e574d34794a1c71ddcb473337d323bdc4a9c439a279e7b72958546fd49721c7f83fea692c374097385acc8cd8f12eed786f8e02a5b248f72b5e26d6ad1b0a88ce12412c43ba33fe1bccd6b2be2e40401c0f98ee02e5e4443c286b5c7ca591e8e523e7991b58a7b803ca64fcf4cbc731525f7089336f46988fe3a58bec16f6b61d165f4e9d7d3d0c1fc5ad25c5f9be235d422324c0624b140235ee8ed2fc4ec2a901a2d39fe6ceaf946ccd910a295f9ae9d8931cf2d8e1955330ec850bd54e7baa8f2f9273326fcedd5aa58445243942e23cae5c81394bde93aed62e8400e32a79f52c657ec1564ba8b9e761995b1b818a6e9e67c768ab7e31be9c975ef1651023470aada0f8e6749ab83e3235588a88cf6f4559c09e8ed2e631552f1b88de42a1936d53bad31a06e1b310b49e2ba99634085805a18c1446223d179da1631ebd6569554b051aedb63b44d50ff1d23546c2621c112076ad16ac0117a613c39d15e0d94037eb36b7478373d5769180b984590d064470f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cabd566cd6a292dd2b45b3608ecb3385ce97c08a47634bc4960a7dfe83342907","proof":"6a60f4b005743d65c998d1ef779dea75c65c761d66ab648809241cc89f43fd72bc886c3603fc56ccc7f957bbe78f4b9336c9f67eb047217fbaa013fb9a0b8802a256af6ce0d92541693b540ee52c0de8ed975b37615b69fe6e92de91e32df438009f9aa85f0e706cd7e2643edd93fb0f7a36f326c62681937dd32f3be971435dc2c073f9ac4523912aba0faf3d1dd1aba6c7112d76637dbef8b5ace61cc3820e1c9b6c0cbf8aea2b1be82d5c790606314c5b04feb5801e0bf75d95e49e1100046bad6baf279eac560a9ab96b8169402dd6da2c7cd123635dd9eb56c7e37edd0524d3eccdc3c13bb6e9b361532962cc79a1773cf5b1b60a1b998a18f713526a46602318dc341207ec9c22f784e19e484557ad6c4228ae50ecdf1ec8370c5a8a48dc55979c55fdc1d1082ea0e730557b645d97a472cb1bd8eadd119238807be2346802b4ae39f94952ce06517321eb7bdb8465af8446bc1b7705a940bba7ceab01fcd0b096d4f7e20dd51aab8d4561cb590f69b34d26ddaeb3e563e1405ddb0a0ede92a03f270255e9871fa1178d215e0d1e7c5ecb60edda78403015e6d846b701ac727b2ea5b2ea5161ba7669c4dd29a9a17e535b7b9585eb1c277e379351734d7a9fc6c146f61d052996f01cb57b69d499fd8fa1f550d6c7093ca3db2e00d8013c979e95df47c5996e51276e10d30fc343f6621b6e139cc4a50f873eb0264879a6c950c23ea024d70c1ddf304406fa96412005251c5590d9813dbff788be18081458147bc3c82003b22121ea15d63e72c50f89bb0ddc6fdf88b58ee821c60642dc1a79d7ccf31536892af3499d08919cd585de4f7d30aa9c8cf0634472e67926a0fd50346713286e17e8b60a8f9fa3d4093f47fc5d8c0f7b2c18059f7e14e50b59704b9cf7333bdcb77b521afbf0a1d54d5d486c8ffea527520d6083784ea200"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5a63de603d5be3dfe30afad94ec300f037c4cda2fee6b0ee6b3278c9e67ed037","proof":"4458a8ed37f161e451eabf34b4afb575a896dec74f3238a15efbbfc78827e2451cc4f6fe11dcb6d99eed78109b037f7d64ce1c0e6ccbad08359656debab1c7406a68eaf865b335321fe61d20ac487bca399bf9fde95dbc7b1073596560d01a4ea6b7d4ded1aeaddbb3e89b62a50166faa53b2e0eb9dac30dd0c25bfd62cc0071669a2a7bd71bc820ddfe81f45168c58cb46352ccec338e1174179011458e940869ae1263b6894b99b3f0a085a666c7a9a15dc87ee139a097c88babb2092c3b0735d31255caf5aca836e52b54bbea750add80451388c85dbe4ebe639476499c07f0dad4c8785c4b2fdbd1df6dd287d8f566aec3696b2ce90f57c1b09240f28637faf1b607a5f87a8cc7f2966fc4dc7f1c3aebad8fa9b130c09917873c58fc231944f897076f511c9cb6b61be13fbc1fa238cb5008044eb892679654531a01e26850358fd1f4e35a8f7f10443d4ced0554103949199db9779dab6981c1dd45172c1647aa42ee49243bd6a2b7cbf6eb4df03237db41c63cdc8846df1695c8fdcc27c4e0644b5ee48cf702690022cc7cfaf62883c618fda5cada3967951d9fa3e37b28511ddad0697524b89ff9b2ba1bb4d171f44dcb6700029eb7b312ac52fa1a22581685d8b0c58223e6bdd541cd2e9f6b7f298740d900bf05afec7ae5b850da6a60eb1cb3902ac3692d283963c2360c5c9b9b73a3106ef7f3ce4b222b3e71775e16bc9d03cdc036b312a280ee28861e5b841bc8fe72c352f7140f5039ad3755425286d60bf7ace0b7552e035368e2b980418feaf429bcd72c39e154870707ee66b89caf23feac43a96c9b0a56eb7488ede925f2941a3ca9829158fdfb0d27233cd88e7624095465a8b12774ef551f12fa96169b0e5073859a48f2d72db08b460d28ccc2e103b0a43bc3ff02d2f53ef168797f6139d4a77cadee99389dbdee1807"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"12d371d14634ec0378058258c497d8e1b8e96235776f434724c38560a1d3b56d","proof":"6264270ec15ae458d09b77f587c2f5b044c3a8255ad83d69d43870ae3697b313e0349d3cf492bcc513cf10a1b0d64be9680299fb6b3d3f4e3975dc149be80e41a4df58770f749a5f5ce00fd387f00cd9a24051773b381f6c96fad50ab734097e58b29bb346620d747d26d385c33d1c947781cb85525fa61e3cfe652b751d9766bb10e423cd7ddc34b2efa1a03eb9a0566e7d678aee5027855d0961fa70036f0a367c14a69a79323c73176bec9785b0ccca4a46575d6f76c2f90ac3f3e0a5020311c3dcd2b2d130258ec6cd0fa403a7434f72822692c24d1123eefbf1b71f5509685d4b58741ab7483bbefe6447e442ac71fdb7e36645110798e6025be149157490a361f086f28aed8f7c18c142566005e339c1b5750cc4cdf58576d4cc64691e3c09c333b849e090e1314d6490f7f44ae171f6c5362bedc75d8fb1b7cc5ac8498e69fac5866c49b73b5c0a5d4b8a3b85a8e395d836821291f9bf867485fe1d0a4ab533deb60eff5279d6a2b3a821cc5c988a15555f471f7eb8084040b1e24977f41362ce2617823d8d497f1e75ec162304c64f1f60a89617f34f3deaa2fe3157ac12cc299443f2a435f40492847d5be9578a915dea536843211231ac4921522416564cab65b0e50a390d75f8155fa0fb69b04552ad531d620c4d9aae3c24c62648dcead1de1a9c9650a737dfbdcf9c65c6f42f2e0a8c05a8337d544360e4602f2a04825c41e553be4bcfaab7cf1ef8a54c59317eca388f697362bfb285e0dd2eae3056de5779f5b09e037643c7726e2cb7034a29293f7187762407b96ee5226be6fa941af88eac71a482fa90ded072d0333ae9d480874cdc8c7ce051efe0c7541e202f7655c87b3f0ae7b9db3887b4ab8017b8565158dcc27268930a77c2a70cc961e0949e642a80a025ae990d9ef6269ff00d9f6e71673b2a051f92b4d43b0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"68d8a1b65d87c39ccc3adc18b223cfbf5cafaa84997b372dcbbd678edef4f000","proof":"ca20bf89f4c22006fa1d6f26b4b75bd6cbb062ca288294c8c2c75e36d3dbad608c7ea0f95e0e0ca6799cde2922bbce51c8f6dd187bc12adf46d0e59fdf4fe14f0a3e8fbb5002d263ee6d5be4727198c6b4d3d99b1c3d86bd5dd42b949c16f85a863c9e5ad89b16e33b6f1ba54ae2221cee5690b11be2822cf920f7c4ec4cbf30a660050050c10847f41b2ad41ca646477554729f12d1e2a16c1fed4d51b5eb03474d0e9b7910864056bb81e42a7ad14b4ba0dc175fa294c54785a19536a8b10d96fff94c7bac0aa990f18b273f4d82d59316c971a25e4aaf6072242261ac6e0f78b356466fc37611ce752907a04b895d2923f8c63ed0399339011026512ad166fa1b440475c8d496d82d68dd4038e3a67a370637dda4305257be6a29011b490d3adb8d4bd8b8db43219dd39a6ecd632c675e5229b7b10ada447dde8cb1c84a032839e36918725365001c1c943dac3803ce05d00dc0a792242c33657d962629035260f690f9e74bc4705aea89129a06fbca8cb46e175593cbe12bdf8bbeaccf58aac4bb613206b118ffa86fefb8e8221f0e65d81db09a98a58a5dc6a8bfa22f05c09cdd1d40fd003b9f28b23f5edd06793f54aea1d9db25f804e2da9ab047343b72daeeb9f5e41acf75e532f5b822de23310fd2a93484dd7bfbebd786edd10e649add48d4ad2ddec641c121bfc825dd47004231651e57ffbf12ff4952a3462256f47be26ed02f189b198776d0b0f0454576ee1cf0705b3de32561f9edb1e8fa1c5e3cbde08f56e3056ac025bfc8100ac0ed5ac305cb678ca14871e28c84f4a31e803c049de7ced344dd6b19fde55d0fb2438486bf08a2a6ccdf93d71a39716d678fbc87c718290ab9363033f5942e320d150ea55bd618ae0ffebef501c9f8ea082cffb73290fe91b5461bd33b6a5dedb1f97f177945d9241ab756f462853a1002"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"46a37ad333ba570c31a6788b49335eff092cba14d42b8e15b3a990bebf633f45","proof":"a2a49ddb155ad7e40b4950f3ed84812982c690b3249028d42447416f7fd4661ea2bbc5906cdd1bedc5dce99466e5e97f2688f00eabb01b5f8a5c1f68a6bb2b49d61dcc6dc5ec6d5ea45fbaa3dd385fc88bf5dc58ffd7387d4511569843bf9929e8d0ea5f362ef93845f4c1fc2f3a1d15aece756edb2c367f916f881645230a05919f6dd46d072db7a04426eb201f9e066c3bb6963d2c35413ca0f2adef5f460e9924555ee4cf4fc87a5e1156837559305f0f6e2e57d0cf9b371945155cef310f4c586a4beffe63a53afadf7929ac5503569eac895d2a38b4048bb57a29d8a60c78ca0b2c21b90932d348b098823a36df25ee9ba59de7df742cab56975d78b710444feadcf01d1c2275456fc43495f3b1f6f40aed7752da2917aa1fb5b2a502039098bf37cb1b76bb855567454e5db452ebf68adb22312792bca8d1c8b0ad225806a40ebb82eb3fb2c1b7ff897e6db16de14f0ab6054ddc73a8befbc777ef74066cc0e8526ee823edb3499dbd9fd650593bed628f4f5aec09063bdb439c4cba28360d75909dedd339486fc9d3b729ecbfbe55300259a266a6f988ac1ef43413426cc878d7791a7bef2b8026c94084bc2a845e9fe67bb1683c016edf78e4b97500880896bbcba111532d295b8a2f1f006c0ec72f684dfa511707dd232241f0854ac4aa232a87dbc7c2d4c961aca9c9f1d9a67579f8a5fbb479fb6e9f1c4ed16a66f8c735a779efc6ff4d85761b660d62704664da0c2589eec1d571a4b16532d854f4c899828cb46d91c4504d8113751899c957b9f3069ce233350fae1adcd7644c2a368eba3b82d6b00eb8a09c3a65ee8958241607fb85ff003eb00634a5d038526ed6d709ed61a33705e8950614d046458e892bf5c1b0091fd43ad95e7de6cb018a0f3ca8057c105251509586a053f8c1b2bad777d974fa4fa12debcc9bc25f0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"300dd23485c4750ba96b6087c4c2013c81a8144eb094f10ba2283a7011849d3e","proof":"fc79dc984682bf85222166d3478eb946c676c6c38a6c42ea05a36e17fcd56b729ee07f8e080a5718fc81374fad7ad9256655c0040b10d62749b7014b59b6a14b7e67cca38d24d7a9e93d519394a1dc3f37646b6dcc3efc8b59e0cd8f8008685e88b7342e9e0a0e02230a0e678ff377c6ae224ffe279cd42c5e131ba0face99291dda482175e9482702b2fc877df6e7926136155d61dacd58590f55a5da48230c10771b616c325ac121d9d12756e7048d637862505d68beef585102fa6effc90ff3e569f1c957e8fb83c859f97c2199dfb2bd4752300dc3e71837a15d52c9640ad4c839349e6edbdbbe81887f6ad1eadc393c3134d7dc2ba8a9eac89dbbdb9a7d3cbd4e05e84a786264d1565a1dd192b3d2f5a99f9d31363e798c3d0ba7fb5f185099fb6393d3a24e0649fd110f71641a76d6ff4b71e7fb023e226e47b63bb35a34805f5759eb77ca1b4dabf1a7e7ad5ccca89212092eae8123941cb31fb12f2fa0479f692d70a81c1d11046a653eec5ecef6de45494a9aae39c032a5d27ee75bd8e116f3d2364fdaea6ad2a067e1b0e625bcbab27709761ceb97ee3d0867f2262887d9da2bb1d529c30a68664c370b3e1e826492d070433af55a62bf03c112471ec221e5b2f2106df1149aa611653d4ce3fd7658e12ce6241a175b26c49eaa035cad2b69812d8ac11401af7acfe7eee8d38c61e46a04ddab281c45dd38fb9927f4538eaf85bffd98d486ad30928aaedc2a34896e787be7fbcccc0b207f0cd13ad06b5d7a3a1e78abf8e040ef8a290b0c56636aea94d349265957c2cb5ecb333c38418d610abca6185309f0d3041150057cfa8aea07e34b4e55431feaf636a841b62f8b4452f78a85d460d0726744a34d56f09804c1f56257f448b5a550607b0a87f555b19aa57e4e555a359739167df1761672a0a4e8d73ef62ad004e63b830f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"50a474f5410051857159508de96cb4c5c57c52efd2f0f6e75450a7deb625ad3f","proof":"78421ad1e2efbe5c3c7fea3f78a1325c3e45a110b5f8efc4b0449e30d8cd8a39280021506c33bf07ee79e1c3b054874e1efe2f0870d781f7264a1db1fb10d16f0ed81f54d2266b9a7a53f1810b16d5e8af4d1c4c3ad05615454d1923aab8404e1a4e5731499ad2d9b22f4f70c2258bb97286b389f2b1f15ff528d3890a18b92ca5494ae67db10cd74713a7ae697a8189386ecec376d4deee9ef461d3c01444003d12ba8fc68134478cd584eeaf4ca14782c964f5365d2004b2e86af8b7969d01c1605f30988ef9b0e3426ef5464d45cbb560e3a410fd66f5f2feb6f6ae9c4b045203294acb3deb111902051877a15f34d938ac32e51d577d676a41e1337fd9412e31697acf441b7597c745d237568673bfa71ab0a45a975a61ef53afb911005fb2c711a4cca7079217d90fed0c489f2d970229c81c760f9bb8d9841803276237242d281afddbf86de00973e04c006caa58b330241fae6a0cf12f770c2bbf0127f000456090833c6c71d2cb35fd6bea451309ed2585f846ca59629d6d28d63605505ff1b85b3292b676c81f87cc248e8f825e7c637b5bc36e2150f4ceb145a550985540e355d2c2624293a735bf52353483779729255b4852dbf544fe26a530015631237308cf36a5736fb806e25277479f389acae40856f0761117daa16b184ee6cf25d68fba84def07ce2561e7fb28f7b6f12864e2d562137b08edd4e9c0315d6829f136ba6cbd804fe5b855cf1e2b3898877421ac333396b72805ac8dd752b1a3ca57a806cd555685cdd9669e4f0807b6e40643d25c51ffa5f21ce1d5c1c24a2be146146b032bf6e04de1e6aeaf0b7db1cada55c96dafd692459aba97fea615fa186b24b7df44187cda41c770852a3aafc114fe2f16623586e91c4c94e2202d4f8aa8218e517a9c03079cf02ce367a8369ded119f7fb950a655a19ce012d06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5a71e40459916de8f0d1ecd94b1d8a0a61ea2e724c3021166fd8d8c18bdf2723","proof":"40d748465957dc907f698d1e995353d3a53316d51a39d743059da793d115a53b8ee6cd08fa4fddbb6d975b14c688c4f103d6ac674371d160c7d2c89baae81d78201e3eef9ef037716df0e6c3369f8a00e39d300e21197f50dc3853752cb471225c593d131e49c5fa50dba730d78740b740f0d32aa844c850b5a886eeb0b8ca66b9369a2f3f5bc2abf45c1b2e526f4fd7629519a36d8b1593aafc8b74a9e7a60d99e83915ac7703377a388d1ea56c5acff34d7df272926f8d7c9b8a65f6d53901304bf4f85d88795e13a289ffbe82b8255b60b2bf6378bd6169df391cc6f52100ae682971e1141c9d94c63f76a27daf973f5195fe1a565255da65fa2586ab5a7e7ad21c7cb45552e4bbf577c92f4c5c7090d15968bca4245bf5b4e324a7c1424a6c5db4cf823728c680fddea2f9bbf64540d1376be416abdce15d3041de26f11a3e3bdb56eb4a32cd580e4c6d4fc4ca9cf1d6a4f4c7e729318a0cbd66afdc24094c04ee4db272feff3078c304d7d3fb47c237a018c6d4d1fdc2fd18c2aab6d276ce95e9b9665f8bc27ff7a3f4b7c248ff81f374941e1630a1468bb9df6932576dcc6d71a9d88d8b40613adc2594da4ea2750809a93ec372a318d344ff35f9204ff8e86a03110c3a1c949b28fdc0b51ed54e573251fd83877e698cb8893e356665a8b40458f1580b7886864015d032e99100854d01f1df7a13b2a979f152258413e495e680ce942752211fec7a1d1338ae0e0c37b76eee2d450c63fbe03555827248282bc5e7be64af8dc16a3a5b529bc530df255eead8fbacd158bde31845622af0974545e452af1c4b022077280474fdb1c9c72d7c75af5a34097d532bea400d089b1f731df6352985082c37f12118bb7fc25b971323acca839536c618583904e076c93aeaf7f5fe231ab6df95a7bcf6a84e0daf9acadaed40520afafdff8306"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"56dd51280d1a252a09438c1f7db479bbbdf62696c6d930b7da00e97646e69569","proof":"a820caeed07561e7721becb8a3a68c4dc3e399e2ec6196fb44ee57f17e840f159a8e8f7da053cf59a7aef82fc26bb2e9497891885d56a7dd06594b04ebd2ee69fc2339c6510e1c13df0be9718b44a52d9774a20fc8f3887a2787f07e8439d54fd251a3c770a9f3816922f991b4048dd5b8ef7472ca2ef36778003c513699d041d6548eb8d416a15ca497fa0d1a1527566ee592d83f238603d68449a6c09ce40ef3dcaef74cbddb6f4856d5a19ae303bfa2be2f34acdc9bb89837e4814a6ac20b18173b37dc6f4503a278d0c29b5defcdf6646ae53676e555a080e3359884880b38d2cab78f89f1a77c0e09c2388c32bf2aedb271227f6858dca7c93fe2ca49484e62da40e9162210b2e60e5c51ec683a1161efb031c97236b7467ca2bf635915e62415b22d2e33ed50134b6431078c5601438f05450977f0790a5c92c5350e023205995b7916c91763cd736a6d5ccfada9fde6c4f4c8c7eea202dffed4b0284f1010a8fdc80eb8cea5cccb2049e5d7bf8593b829b458cf2467bbd9bcf0760c19ee2490290a97107925eeb63bb7e854b0a00055812a7f119e733474dcc7c2451388ada430ab2c1f379a1042ebcc67004bda5a44d94ec9cf8aebd5ae49a888d000cc2343365d83f01dc8ab3e1306d1b1965c2648f92d478e8bcb1bf625994cce028007c4bf421be0b55bcbd78ffe0d7c957f214f15bdaf80c31eee2eaa788db9720c31aa91702a722b18ec0f75c3333574ef59afdb63e931b3a810cc37bb5c9227ac3d1c583da8f5221efe36123b80ae21c3b8756c93e97eac0b38dafc3716e950949a49a96c705dd2937349f00673d09c3eda212e1fddecff2aaf57375332c1248861b63c5fa8217fa3eba38e8e296fa6cd8fa64112a21ac874a90958c2e5960517eeab8d73ff3cd641c4ab2a48290e855cc0eb56493f490fa9bfb98328b35009"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e6628d629c3adbba50d26f2cc909dc5b94f06d32aa714c4beeced77e4733f10d","proof":"02fcac67ddfaed776a4a721f5819f0b24f357881b6c1e2d5df3381b40906ba1c327a83fbbc6cd8c824437cf3a1a78f51af2860eed1c8b5582f580836eda5214726d2fc0458d3f60d010d3ae9e23700cded488d2480e479285372550c5cf3b602022f843403a35fd7461760211a0767eca6ec49a365ecf781ad24dabc15dc4d677ad323a460002d4d8f936745cd0e237e64c0119bb592c53d0412eef80c95790e9154ec05089ea8f59d39550c9779c00ed89a73dfd839014479ad93f9f2ddcd0f3a13d1b63f59862586212e50db9bf776eb199d20153f2002a3d291c28ec3a100022d0c2536e1755a6e87051e6067a949e033800860a8305db0bcd23c1c158302a23c23a8c2deb6b3c8566912fec8e3d6b14ae312e58f7f3a3aa492af734c52555c6eed2c1821816d9dcf4b01b663cf5831fedd5345916b94a771acb5742d505c4a36d4c98370c6dd9928ac6731313cf2b56bc5a3d877a9c250eed5888fe74650283a9af7060d82ed4cb4f263b79a9f740c6f553065f74043dccf8dfadec57b60048d32dae1182fceaf3b1dd9e8cd4a896c5c64eeeb57e240e00b2d4bb6880310b82b87b4049eb9c7e3ccd9606a6b32c0855ff6900a1ccb9e9b2d255414dca643a0bf37aa710e8e733272bfdab39483b75e99efa4f3cb20c8292962d1c0c0c3326ef0e3dfd64ec709bbcb28288426f55e1b42d8ffecf90a7c92f69d2540b7351228f13e2e7b573e2bcccd4baee0c7d8205366ecdf0285211be18a9a945b0f2778720417686bdc3446834172935b5bfff59f5d3cf0fc6d79cbc9e634630e78496e6cd6e057a112d0608f1ce6c94ddb5c2599e3d5b2b100961f41791368a3e8d27a3bbcb6da33205d01a919f09da2b68fb6e6247c911e3d94e65e5ffc52d1dfd504c21b984c994f6562f7b8ba4e039df940bb01ea3a63e3a6f480b6f5a073e7580e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"301c5f6e1c0d412594ad5a2b9d80ea650486a50173dad6a60a2eebd98060fb65","proof":"387c375b80475d36c9051f57060bc2bb2b324a94b11b55fd97bd89cb27abd44a02de389492e23f0c3f5e4798ec4c7b7989569e991a8f370d5183304b0359386434cd0be6f5f58a25dfcca181a1e9dded59c2860dd1a26ff9570efe2d90cee11b0cf1a95a2fc6f75e57d21fdb6866e597b9614b7468d12a390380524f95e0f70e6741e1ea11037dc9af1e8dfb914515e611c720eec6b22a86d0079a4c58c1160247fa63d437403b22c04e226e3b667e04122f79fe362119f0314f44cc33822506b7251d354b3a0983760f6f64907792c8d4b8bb55632bee4d78fbe3258af8ec0ca270cc1c6b119d9eb7ebdd6282ed765b54ca3f37e6ae842f37896a5db52181345267b39e2232c1ea355672129ff263908eb8cc6b1a37f83e42a7b4f8b8e1e330367bddb7e6f63e5ae6cdd193f97508c0b9cf5994e1ec763a6bbc78f47717c81f2a9004b8126941b83324bef745d05239827edd4164117e69734578c5f87c3f3dba2f159dbfc684247aad85a32bd1fe5e8da9fffa6802dc8d0ae3e478d78d682bfa9e132c293eb7d570c106c0ecaa47990b275b330cf3fb5cec68a20d83cdbd59aa5a64f12c7f143511ecc0a9a8a1bbb544a4571054aac21f07dc06497533e5458abdaeba88fa3de4f38aaa4bb77a364fc7fd66e2ba1412b7916bcf5da645b96318ac7f1dcf735116cf8fa9b76af8f5e21234c17dcac738f96a652bf3f067c71bd233a08a7003e964537ffb6ed69e7c1d65d9de97178747694c40620d70ea2f291865c76f88c0ded77119effa77a3bbedbf4a36676b6c42350f7bed3de8ee7c275ef3259b17c9a18ff49af2e8659232fc11ab2d877961553de6507c21cd08941d9c4b6aa6593098f736b852e2be84374b49c873673276ade45cb5afa88db51c01cdb578638235f910fc4e204eddc466fce40f5f0342a01843962911af98058e07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"80793533fc9857c8b157c0e0c7337c3e76a4fe5e58583715b9fcb8c13c702c18","proof":"4096b1e8ce3bc43aad068c431966f4277728d9b7fcb282c0a484621244638e4da8e986f6ec82b79f1bd5ba9a78da61e7853c47617765cc49afd9bf57c9003435f6f3519ff49ec9a86ca3ae294b97ecf55ebaa35ceae13ada30d49a8ad25cd66a3ae0f8a1aa9c6ba4dbf202e53eafc4a86288c8a503b4da4577561ab53c53476844f7f7cf5548d3793a5e19ce136ec632f2ceb288dfe530223e646dcae0933f03c555101823270c4a2f7c84872f0c87207717b02c4048f6cc5b5a489ea63fc0047c44b413da293e482f8c1c99e5f127454ddef92ba66e649b96611cd4e8f2cb04e090847bfcffdab40115134454c61cac09890772c5e077391e2bf3c7ffd6f47e7a7d6dcbf404bf23685176b2d3b1858738adfe4b137c98437bf3ec7dd42b5457caed18419f56eb84c6a15df597938691d1923f556059b57f044d34c0076069093aae6c4fbbb9fbf4d06fdd2fc3bc76683107e2e394e590226c3e66b6afbd114408ebb30a05b404b1788b12c31ac6d9cf21e5a01310357dc4570ce09126d8570210b7b0ef94d087f179d3091972b34677053b972efd89b58b9a100d5b98e0db4fec268a8557ff317d6707561c87edd6087fb9a12d5ff87dfa1273d08bb954a11ceec178eedf5bdb9a3bffecd4aa7fabaf35c94d4895b4ecd3ed1274345c1cd44b5037c430a8565cd9e806eef2e354c18b99f5c9284b1c661ff106d436fbeb881396a78b9f5ce162d25e12871f27f22bb8d8f095393fbfa765ab062d6b35ffa33c1696be87ddef7b522cd6d130fb1afc98baed70b898c0f3e58fdf9c431008c86e58e829092b5486b51a7173a66535901227ec4ac26b4181308323facf62983837dfdd7b900ca101cf54581545e66b978879d00611d4eeaa802fc5ba8dec5b6b00a19b6f5e978e5e184daa4b2cc01ecdc10901a583ec84fb78ea946c9c452ea706"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a6b350371fcbed9cdbab9403697bb876500284cbb39867c51cf2ca9d850ac32d","proof":"36b4ac7e468ff96a400252cfbff6b1f2de24a6344cae5ad307793db84cb94647e067e739294cdd221d455c4151e30f2f437aebb77f47b19b62841ba653871f491e389db9a5eacc704e80c0e7c5bb01aff562df3825ab30615e3ac6daed2680563ee403dba9ed01317d9035965ccdb3ef5ff53e2a6fee71e2589e690cea69c14e42aa3a55413ae7433ceefd97c26993dc94118f4059322194cdb05a45947ebf020b7573f46e07e642be54c9cb302cbb02d7b6de50340b0f517d9f6d43bfd8460a2c188eb559d0effd44301aa0186096568404e5b1069c706407fc481eb1ba410eb811ae8e75db94ce99f95b267845691617d7778f99ca3dff47d5725f5451b80da4befc7a6d03e459f360c855657ae7db234fe4b7b582c5262e853c1d5dfe635be80d877b4c07fc844b95edf472fa2f4282871fe14175196e18795189dca32833982489247e124bb4c5b8394b06747de308b67e6432474992f59e61067e4e0d640417e51e666777ee702460bbe603b2468fffbec2ff8c86a21fe96b5b494d1e4b764818d8e0b528dd3fede9ba0cbf27e8c018a1dd94652a7c1c01f256bdc3ff4770b63e908b2449b9347ef37545eedf5cb878fbc1f59ee2a4414ce01d5dfa85161a2d341dd9823a38998226dfc8e1b06701de7ff4027fb8ced53da2cc1082c53aec293ce00d95c2e3e31d2227ab51b70ed23f6611f34710aa70fa1ce756468e7eeeaf2ce2f5205604c61e2e65eddbb5bc2fd140814905a030691aa4e9c566ad42264e03e4dde81af69a1c435f6a7ee9c0c11ecc91e8e23f8afe2e4afdcd89d14262fa7cc1a72d984802d43e5433f448b3931f837b6b1a270b5387f75fc309cb3fee9c5cbea0959b749402d449fcac2340e59784bdbc7880d048207eebda4d5e05d6bb472c3c9f9e21d9df88db09c7908e9ec6439ed8528fb064ef9c16f8ca0903"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"88965e265d7f05fdd2e90217286d1785cdb83f5da29db5df91178c2b83b9c52d","proof":"f006070951dbc975e5d957c49d769c73c4ff949730886ec714948c19fedf531d3263cf34eed1349ec77c549a0f26d49c248bffa5162f1699acf76e136ec12905480bf09daedcced91d0a1255f3841ffeb7709f3edbc823d55e246613cea4bc5aa4a82fc4b7a41d08b915539652e37483a67e14d9f2e329e08f9c6618019e81608d487630832f27f1ce345eca17176959def00c525777e51f2935c7dd0a09ac00a0642120e7769b191a795687b3033549099a4c7b3bab83db877d0c04f5bcc203028da6ded54b41569e61fb397662aecd3e696b5e6a739649cfadd335bbb07f0b2cd9a3860ba833675106a121a5aa0b25268a143ed81b60e911482343a441fc0f32f87aa03d9fe6b186c09fab4eae5d913210185c411e291faf2630ebaaedc245706b1b3744bb6e002dbc9b76bac9026b097a75e58b4797d6171a53e46651970c5a21ae365e372da257ff9ad98aecaa318a5c1869f7894809567d2cf77f0a8e671056b57ef17d448fe1e998bf5b070e61485d6b574ea6019010b273d48824720b642ad0b3e55afc0faafe7d5a91e6b312dcc85a04c9697fdd506891b1d8e1b51ba45c3850486f6bed76811f8254e810b5e3fdefdc14c900774cfd7ec94872044afee7beedfd0e513c73264d4952c4cd3305551cfc8dccdcd05ad31a58d0fcb21fe20d5ee1341a874580241faeeb998fc384fea031d2891fbb6a00f70fcdaf1b1f4e44636067e8660bcec8cdc918c739795f9faa2cad6c84f3d39e0dcbebbc4d34cc5a3c30d8781745d9cb0c20415e7e7ac4797fd6697f9ca1ed0697c8319c360372bcf6c741a8fd1239d29d947472a90cda672e910c23e55f30e36eb823ffe61ee4681cdb010317f4b368144a609d9b61a52b880c3f024681eaa1ef4cfd80a7069eec8612ce3ed771af6e52af96c6c52c0868248e203cc9be7aaa88db4e96e40a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"eaf13b5c93e9694ac9e9bb4d99c153579a940ff78270d973627ad3d80b211139","proof":"c0b7bdcc2d414b396ad98728c6c7a20d6533efd2c3c2738eaff5a1842f1f39707eef3b07401c2083f69df1179350b298085022ca95aa55b7e48259886b5efb21faf458d455b1203bee576689bd12cedf3dad2769bfbe10b09df90133d2c7ab619c0b1075d168a61827f1b76fedce0d0adce771dc58d48bff579558c7183b7f2e96c6dd6da85f542ea712e1bdd6ac954db1f055eff0c00291b476e9ef12c70f04914f7e8660bcd376018bd2a25227137e04008e0da6f27376b914b67e459aea063d579c8d2755ec4388feefdce17f33f0048614d10b727ad0edd6b63876c0d90366fb929b8e021321e5b1e1daf6168ca445391be3bba25e8b928f99008eb1d10a468d1150bce72812510a6e246b292fc3205e25b553a3d5fc69e8181b9f958017b69b794e23184655a2e168804b23aa118c83ee35c227b8a5e2d4c4023c4a70774e3eab8054b001808826deeff3a20c7e1d8b4dd4095017dd554a6807349b53069c740857a1bf169edd55adeca0d182012ba2e7d04913884cb5a727732e49f72ba6da185b6fe40ccec7b7137f4a4cf7b9d5f442257f266ab53970a7028c119b13905399b08623cc31ea73603dbad5b7b760616f4d6a14015c35d7515879a94f2ccade4d1fad6dc03e884e110a671f6f8104bb3f854e561cb648ed08c4cef3b47efcc80d968d04555bc46d2e93d06b2a7d0b310b4cd9c235f6f448d2adb456300dd01fc1a424e92026f61417078208437b6c1e36378460b87f63d0207f1af1ce490a24d94de9315cc6d1e8e018f56e1a0cf5aab4e70b42c76d61e1410a5be96e723e82d4378d7b67815d7847cf70d01c5db73df84aa6de5098f3ea8019e6af6a05588c3d6a4cee2556cf689929ced86049a61f226a63c7b9b7bb48a1d23a6dc10a7d9723390d4daa1d2f0633b8f8a57e51295ccab8e4a002f94edac33ab8b30e0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b85ad1765e24a79e5a2d9086b34cc8a559d72a6ab0b389f64682807331e30139","proof":"5c2ca0abbdd8f243b5fc156b498e0e1242cbe7cc19bbf78b69b5df4d6393f81d6c367812335b8c1d40ec9c5bfb033177545cf0d0e8f340ced03c1d7ed10e513382f1a3c54e38bd90d19872461e1c2dd78baf1df64b988d93ac7cbb6ab5e3686ec2afdecb34db3fcd8a093ff64ca64dc54c2445459053d9ec3077fc76ee904c114e8c40088f3d342d119779fa5202624e20b0e7b6fea79b8f6d47a41e9f77e50de3d8ed9e4409b7f82a2ba5d9c6dcc0700d651fa9858751680c1b74162492420fd3b7e20c7ebe702a143e6347301954af28e444e849d1a29ef317b1ed8df9cf0ee6a16d3d5f44dd0a6ca779e721b201bf74be233704b4d42eba20da9035056a088e4838d00878f95b6ff4b640525ca1cbdddf385e1eddecf18b58904603b53e50c62d2c52da23cab5ae57983d2693f3cfffc2cd20f80cfdbfa301adc7ebe3213464114587b1342e296c9839817e1af284be452bc6a18e52e6ed55fea1073b8210ea65b066adac67c97d94a830f8c700d394b06c06e250dcb743f07695578cd5403e35f076f053cd127dc3a6c52b7537a3636c86afaa9e83795a1872900e1df242d818316c4ab03d81ffcfdb7a3c6182e2482403ed4d3ed7b57a719730925ef41a80ce15045239ced0b4ec2bcc3d39c71803d1129911345b1805cd183cbf1bb647943d0f2e90420aa2384e7b81b615ed6e7df64ee631290e7961d87cd523958b1a268a03031ba3cc24aa8edc27d368ea487578f8e53d07eba973682838f3b139557ec1ac1001317617ae72e89161275e8182060d081b2f9cb1b4f8cbd8347e6313dcccf87332e5bb51db1d868e2d42e56527067f231c6a5353f97c71e289d83d74c36b9f85bec5a1ba2480bc0850e1125bad7668b56063d5b8632a2dffb0ed0f040236a96fd01e03a7313e65d7a7cabcc0d2e932017966d24200cc05c58db01501"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"68c6d4344c32d19cf823e1962b59b7e434dfff432447a883d4249157f18c1059","proof":"763711f42548b3b8a15c4cf2fac473724b53092503c1e23279caca61d38d9d14ea5e76943939103dda8e27671e39443e0984969e5709925b3414a273fdff276c8c0908b97d0893c9d72fa5cdc4337600152920548b5564617206b69f2c48a81fcec058559c85448d05bb7da6d34843bf7374210c733a7899e253e2eee9941e13936845c4de84f06380ecd7c092124b126e496a43d0c1d60f68926c475645e40bcfaf1317668d4df21203f7d218b435fb9856af7f61e8dee748f3791a02ee210c9a862670ad0e0462de11de39f7e6ab77f3d8d3542fa6277f8082de3063899a0882d30acd221af7682ecb7a0dfa51d77c48c3d17c5d239dc9da55fe1614eb6a37702cfee8117667238338c74dbbfda6a626e56a58b63c80aceba8cecb7723bb36b06d5ad91c6b8d8ae80c08279341ac6605d668245ed43ed11249763784651d27664a5419ad2c070d8d831c6d9ac9f1f2c1c94991672b772ff10dd8af692294019eae595b9a25b5578b19dcf0456ebc38e912da3f751ddf496cb9dd77eee72e52caf8c35829889649fe227016a8f6a8c394eaeee9c62620ca1fb474fc6882820102d3a260b92ba86bd59e1461570c0de321d9721f3ca4851294d18fdb90269f4cd22074a05148074ae8ef54b2d77a7b3de900b2031e7e0d7ed05c6295972f662c8e4b542febce5ac3a9b2a666e480a901dc2ed19e2fb1c15a4010d3180e8b390af4ebea6ec5f4316a384f688f6c90ad04e91a234eccd60303002e38b7d1793854faa7a026eab3a614334ed8dbdbef44af7362f620b76e6188453fd9d7cde7cf626603b3ffb79b91cf449013bdd92354decf6c66deb4818f0e4d0086315d7d3a0de169ca174194ebe15d7944eed2ca87a509c8accc18a0790c0789714b888b2d055c40cbe042ca4057bd2ce506421b765005d1a449b38e87f7a8ec39008198a709"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f280b924d27a22336026b9057ce15cb8104046c88f4ee43891f51bb345acf944","proof":"e6f7e9d00c596c3786d789eb8b7d084e8e2ebb7faac33a924a815e88d0906328ead552262b2ff0995b598a505b7df5db0f6403ab23fea7b855cad6d5a1ac18262e349f3a4624c28e211ca8f7baca7c6f13f2cc25560d8bdabbb2055190d5b75f5eb7a93a84a677ba0a54219f959f1fef6455a537d887e1b15d76e2fb4d26775e8ab6e54cf1b3037c6eedcee797ddc6a242b2dc0c721a6f80f0deb945546e4309bc855a1f96afbde615ec135597817f5fa951fbadad5546c1609ad8245e2dff093b29c4f540080d1fe525d50d21b90f1fd28d0d99ff144dd1d1725f47e868590d00faa8cf4e71fbe333a0dc7e4f62f1c71fabba69308820d6079adcc8dcb5eb4f381a6402a87316a6eba58657be824c9f16169de825fe6c7113f820475f01bb715a61c642764097effa714b74b2bf92bae0320dafc638ab4a6909cceb855fd02ce281bcc95fe3835a92aa74e28165f37c8a5fa2f660ad4edad0c30f4a7f5a885956e0335a51e362e65e5fb5e2d49c1d4c3aef9d655ad693a6b9957d9ccf74e25094ca06b4617e996025d676b69368a89d0f18eb9ebbe283b022d7cbf6cbd00c34d02400ec4ca95214f50f2027207456ee7a0f06f96d14c3406fe5338510c6f45e52fbf2c523fbdf0f08347457259aad744ea78c981219c2c3d92ba3f7abb3c2347002714356a46581cbdf41e7adf9c50df2d7bdbc3e56bea35e0ff15c6111a22c162159eeb67b7dc81b7e9ac5305e235b225f56ee1bf17a0bd9013d3d0e7c2f03e67b4668482532ffcb70a7a96301c6ae2b9bc8f0c287ba4e486390e081130b79a2e7b5cf4bd822f39e0dc0855e38baa3d31dd77eca874ea4e483525a842d6c0d23b2060119a17c10b21494a9ac40edaa55472e1157b97a262aa5e2a63cf8150326c1882414dde151550d74d5d68ec00b6c8e51fb63a8b3482481493aea3ce600"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c4b6eeaf81241b0fe0a233f6a99c00cab05b9aed529fb0d2aa99b8acad53b53f","proof":"a42b6e1ed44d19449578edf8f150502d085cdad0c9b1e364ede6f04ba1c79031fa58374d55c73abcb3956afd936966c16c29b54b48bea7db2056c5852e819461d02426172724121697cfb88740e45f74de392af6357dfaeaff08d9d89a613346287f858af308e3c0543437f1a5fc16160445d4a1c39801ed28880f559dc3be371b00aab63c6e0b70f435a826c1b8bb8b1197eb97b741eaea56ee4bf2c6c18f069acaa1a24e0b237a521ee0f1bfc4af45babb8ea217ad08880fc2fbdc94b7570a2613423b2cf518d1f394a56a85d131a5393f7060eef019cc68e5c536811eb80092f0f512a2c3fcd52af57473d453cc5d439fce9c29fc5af69fa1d1aac53b586e3a094dfc73da214242db347841dcd295f62d7a765aa5154c3b6de034fac33518dcebb970f80fb4c6c7ab32871ed2d5bb49cb06fa48bb61a1aa6daf9e6dd75840f88bb32e3e3314ebc7e124a0396d2489c382550137d698e781f605336151f00ecc45e673ecdbe0690e0667ef987bd8bdb77c6245f721218aa8cdcacbe6cebe6b7e92f8d97e349e38c6b91c70a4cf9c43ee56dc6b865f2f36f52be5d17145414ac27c6b522e201b59ef458993a2e865bec7a01443f8400e0279ca935a16201c4bfa8e18d0071e440b1acdebc0f69cfb203e385bf09d45c95750add09fcbe69d00ee1a790b19adae70b426840792709487e909c964405b9843e634f4560ad57d600e4366fe6fd8c332fe42f8317ba00559f12e9c841984b860fe69480656241d7f0e492af0c5492a410e73fc58c2a131f61281229d04a289dd6689a7a93d34c278d4d8753b25c4958935d0db38b855c43146e0a9a973f196b699c249987b17a47111d55504d0f5edebcd79313702a674184f1e563f935915f83e63da639c52c009e98615706c8ce1b0ad992c31d60b05f404d212f920826dd14487bc6d0039eb0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8add1138944dc0952ae7c613da4d8daa4ebda1fceebe3d860eb698d0ae53c045","proof":"f033dd354b5d82b8444dd2d87b6fd7f6f57fcc973ef27c72cdb631d449df40264e760f1bb7f9b40166e415915b0a0565047579ee85084bd3a3572ff73facb8237a693f6bb5c98736ed2558c2122b77539b4309b8da2dddac27f55c0465a2b97d10c72c97793245c9380ef634e596fa495abb5aa6e943e6e0f2c36b2b3686e079c8327ed8653b15e92a216984e12af85a8510bb97cd4405a944a4e2aa66309508d627a7336a614343d4a0946b624752503912e80508d4eeb22779b1791a5dc10bb5bb7536df7f2df3a6ece53a759b4421a9c1c576e19fb21f4a0042e4b6476705f21a52af692817b148e7d7d219f095345bec30f1b0344aafc05d1c14d3f7e510d0dd8cbd62b8d5bf05a21b454d9731e2877e4b74ab64d342d5bc3c0662d8fc3ca0f72f25ab6dc98c186f0066ee566ba0bb4e000afc22df976ee08ce4fcde265396ae136ab7d52717b308207f6fa179b18e46f92b9c87fae22c490dad0d66094bd4bc8faa6c1a62e1fd03dfa2558e1beba6dc37901cf69c3a9c8dc5ff00a5b85542ae8d209a072a736b65066c664c6d870b1606869bc2bfc2b780b33d2df98b48c415ebee0bde5fea5be6775f90bfee03f947ab2127ed788d2881cbaf695d826e36f0a1a2371ca19605ccfb702d8ed98b1b5ac4836211112ffdb9083f7b7c253acadf75a5cf5eabd9884681c07498ac89e1b23d8d69d1fbf81ee6bb11e72161769ea58f52ac8d9107fd376a1de9b0edb913d44a07b6854869d7e001f1cfc097583025676315a9ddb9786c2e05481d55f62f7ea73dfe238bb3e7df789060c7db4dd2c5f50c056e50bedab2eb98cb2a9d2a44a58faa9f0a70e0306b4495ae2d683409d061a8f891016002f05b71b95c70a73e2c12a699ed5cad26e97e45a5d40f095987ab45597319dc38790e0c7e04ad1957907ee681f28ea46181422e37ef5f03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3a31e84da26afb607602516ac207d1deee4fc1ac432eab74c521de515464f906","proof":"daaf216f28499078bc1e7663e503acc942eee3321153d3eed04ea52ed9eba6181654d424c367e7fe52f1f44b50fb3056d3215951f2770dc897fea2f3b7a785567c88a53e74abd8c97f44a61ba1367ed8e3964ddc5b3eb51ac041dadb2b7a73389288bc57084aea6b3a6ccb7e46ac573f589445f1a4513113fd221a13bce7ce0778dc8afcf5e21afce7705a053570661d7ae8a062065b2cd2fa488c29ebe2a7073dee28c7b41780bd3e213b68e10c1a5f7d733dbeb0ee91558dc60183dfcdf105d12cf618e5370247b3c2ff36924054093d10603532a4dcc18c940ad3719b680dca4b74e71f2aa91e6d2b6bf1a0e8d0c64076152c36154e6d7a6de70e675dce03a601079a5a2c68749b5fad59dd6bc0bfaeb40c0bdf80662648bf7021ce15b35a023109d2cd1b8a31bfe2176c642531ff416413e71ea4a16aa925fd99c12bcb03b43f5c4ff257430ea505773155cce07fb658e47e0ccd9a8c4fb1f67334cb74228401f83765f20c7f39ad5a790ec4c8d5cb9b54ae024a577136f59e9f3a334e34f412a315ff3a0e2e476c127e2dc0baa5b73c5570cddc69a7b446780daceb3b4f3817bc3c13d561b7de463ac7cc8d907be8062efde27c960fc16557cc84478f0dcce33f6318027f9172312065e6b141e26e42790d5c077c66294d56d672fe20374a1350f277381a6014fa8ab954427337568080bcfa2a3b9f10fd66344fca7304025888eaa31838aad6718d993c5d9f701367658329299a698cc8b4d266978b1dec10a13029e502a602af89db9d14bf5c9df3affdd0b80527cd597b75cf599b395ac4032c5c4942b2f83c1c1b26d60730df34398389289b0e29d48f98dd6c9e3686e89c0719bf9e0678a4d2d8823e2dfdd441ce80b04ac266899982dbdfc3db0ee419192305746dd1596490efb8629150331b5240de99788ee2450d8237e9b90a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7a19ea61147fb11b8689c2fa5bddee991e47da968fc984965027126aed100e76","proof":"706775dae4f6f618aaba168d6ceba57236742d01461793f37a0702a9dc3d903b12064b8833efd4433273f6aa9198abc42930a7b82d3cf0bd102400abfc2b3855282c63a61a237ce7de09ea4388e8729d33653027a718adbce22b920d5329c43980e6d0b0d4d888352a2a064ba1abaf223bd42b38b2c925bed8e1a3591e454c616b37d07a7438ac53c8b20a262e7ad73fa3445094932424d0b432126eac548d05a93e586e3a6f89d2165d9370695b0482554b7f26d6419a42fbac7073d65cbc0bed9d8947bde8acabe62942a8da7a65d66d78041c4630145f6bd2bb3feb45d4006a08224183661506be5f518a3cc55213d9f3cce083841b1450273c0abe817c0e58c03ebac1a2e25c28068fe6a3d41856173fdd0ed5c1b0d23ec4418969f95b58b0f75f574d38a594c0b52f7c4dcf658a5d332e5355be555918a4a8a675d5c572ccf33aaa22fae4a04248ecab25a857841eb8038aa51297cef53ac42933c17a5e66740069116c6aa9556651130e800fa2659209e0235fd7f86ca40a071a7370656a4c8ece87c76cb766680354187cf8c3fc7ac025d4ab2634ebee0d465c823873ac7fdde99bc6ccd5dc0761cdd7525b80c31d99cbdb0c37b14ef2a91faa8f965fc25c1cde87df534c145d16895fdbfe6e36e1c1a1abfdbef1dd761b86b43cb74de6bd69bdb8035b8deb02675afbdc1359b1f111030954c0a4ca697d1076fdfb0c16c0f8066fc3fa2edaa670abe07edccea307b9b7157421d72524c1d6d8779268f67337e578f7705aea89bb7c1c541ec6fd673ff6d3a4faa056a0244409dfec1aec79f4d7796c32cf8e3f33ef1752729a5d917a5cd688a4e7feb0494ee25aa36f556bff0689659019c9fc2e6ceec190d611dd707b14ef3e6b73245040cd01160ec4a6ed3999e8914c0bdfbdb4259f7ccbf025eb4e944b5bc6409ccb8527aa610a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9c312fbf98fe883d81507e53416cb286a0fcf6184a0d31589cde94227fcc8b51","proof":"b036440651fe233f7c35b81049fd4869fe431ea1b8d336f57b1dc3e402a1165adcebd1f809d7f4ff0b13abfa212a56535da773f9da497b4fa118f732f6ca8f457e0aa34b7387c25684afc54f2c65c6494fed88caba1988f0ab31c8b01762683f9e1f9d1a104d643bc44f32f4ec14c0d109f4db5bb6f87756492b325181ca37702ff9c1acb42f4b33d938490e008a5fe4210fd2ea498f9c93cb379de1c7b5cc01cca9ac7a2f85b54db9bbfd67a0fa236f5ae56b32b8e743f557d6d84919dcc80a6049e2c0a5828993f16a46ab48c2d3570a85d3a3ca265ddcecd2b2bf717090005e50ff9ae7214f74741bc62786b4c75b3034c96facf205ace97c3410e9bbfe5b12b264e9a2ede5abb345aac275a9a53a8286d44fb5a574f5c7b7a07c38cb3565d41f4cab7bfdf1487f237c8880efc66cca7c769c9f8b5de4ab8c2fb4e4acb7400a98fbe3f7eda5820bc5f5ddc903fa247ac873d96148f65d7e43e194a91598580669b4b2fbd55e5e6cc5387545cb60e6048e2d91bf1c3244ed478fb4e5702e3d6a3cc4843fc3dcd057a5865a19da5901140ecb4caf63de95d97f8afd8624b7099a3c1f4cbf3bf72b6c5406cd8e052b4944c54db7d10a4513f8a8a882c819e37d28577f5bbac37e1a9944a936567b63085b88ac370422b51880190c86b07de15aac7f43c3c75a30f8319e0d8b5317509f26fcdeb413b533f12da98d01e23f0e5458771987ebb09f640cef190a96d868a43dfc051ee086e2f2c1497d1bc03b1770407db61174cfc65a517063b4e7cb31d10b9d123fd5a4e8164170a6735a9d7e53e48eed5c1c36eb056dd9b962143c8018cb73ed0f5fa3c493f027d36eacca8c294966e8aeda70a9386f9586e97c285522960eb1a840a24cee3b0c044b1fc75c00979fe0072fd59a8435d78d4690bfaaa8e720f06ed16e2a62258010e1ae91fe0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cadb0c7fdc9eb3412f03196e26faaec90ae892ff9719f6367f6fc7ecf1833c78","proof":"f45e4700c5b75204022ce712aa840a5feb9185028d336c20f7847288bed5416424a7b7ef9bd0479c9292bbafb2a791376015698a82a56c483460541e52f4415b1eda693de2c8b11e7b135d91adeac5c2f9b6e7cea7c8ab247a1766fc8f2fb15658913906f17b965ebaa9673c296bded8e69e24192f0863a7450fc6da9b0b6e25c52786fbd09488b25f03ce726bd91537785fef9167a1d29773a5ca821fe2320fe87268bb120ee5e76757a8f212842ad46e78faa166a12c1cca347929812a2a00bd3dc7562b0e47a2c65a56112054060f411562ad0260b0aebfeea3635a05f002a8c220e13cbedda79925a81461b43b8bf4658fea72784fdff089fc600461f42f6adb423f73a120ed4b1fdfccca2f9ce4a209474b7272ef8803af46e51c79e11772db02c01372be73ebedc5d8716d5a3120d210f124433796e6b34bfa0e5b9563dec9cf0d4d46fa45cb260692d29c9e6d957bff0f671abc12350dfb08ac336a35bcc93186f9d9f60e32f21cc315be8e3bb0d3568d880f41b6d29fdb1ea671fc2c96163f1ae42ea92a80d324159531317cb53a0fac31e2bef46267fd0ef8dbeb3e461a026f26bceabb9f833e4c91796a1c9d9b836f61ae7375d41a63941ab3763c8cb969c59ee2d991f2ff3cd2db1f2db486d3a274305cf7dc91318761559f757782d8440ff3ca3e653e42e1c161e2dd8a950539f7e1e0f5be38297c6c0138e074f86d8b64a08e311fd47d5f9080ad526f8745ab9167a5131f844040ecb8b0ed0cf2895c42389a435ae0ac9d3c2924808e58c5f4f8754c165f90c6f2f81b367e659c22baf0e71ffe7164ad7e13b0badd4a85142bb3b093405843a00846830ac457f0b19993409f11115ddf83c6b933c9bc4ce28b5a0694bcf2a3e0e6869151dc00b58f5fe6c2c08c2bba03dab5d962193e6d75c08a46bff82d10f1f243bf64900d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a05205fcb2f1134feb5345ca7b63bf42f0ab168834c7e1d43ed6aa4e6cbf9b2d","proof":"5cae67b906c266b7218a945fb3599a07a25ab2e805153696f056d8cc5dbe3c156c2a4183177536b573eca0a7679aa1c009fc3b87d870f3a4270e14627819bf47d606d24fca15474d39a25e280d921aa5a9e13e6f8166be94f1f6e9ad4cc6d85c68d406b3aa0f1f5f7662b9d018c6a2210cf4fddeb975103913aec1800f99c2509798a4d3ec0d09989091eaccc04016007b766f1894a436ba717ba41c9242640fb84cf134cc54144ab0af085d4d6832357970bba6e47d0f93b142a80ffc9d720bef2de68eb6c234990b935f3a8d0ce8699940f7d825f66c2768eb0833b48f780ebe97793643b8f6241a2dc184a624f4217a8291ed7fce15352531caf5605b4d3eb859e3814817ae764c31fdd2189046ca3dce294c13d6b59b0c0321ff18f62c79fe88c24f1882b0b5e9cc55bc95d58b763bc463a441b56331e7c72d4fe7371a13dc4edb1040fd1d16853f16bd9f6c92cada010a5c3dafbab3fa44efd7c1f16171a8efdcc30f8ef7b098f4726ba1c5a162ffd3da1041259ab4f5cfab4830d1027562af2f7b17fd8a25c07196d9094e812f88917157ada823ab497a0da645f3050ba8cc201eb0b830ffb8af714fde754f03d98176ed19303973f584a8a92416dc4c7e61750d8f7adc7b2490a9cd80cacb8e305e358f4c75bc5df265c20040f48678ca8b51da880962c53a58d0f0ea76c98349c2fbe9531b65416ec747c7993e5a7f8ed735e8aa82cfa37db139a2609eb628d317258202f5408b028c4308d914c914ec316b12266162c3a919b6185c2623a82a4afaa99cae5c515b366095adaa1478a6dd2aa9b1565a11c184af96ea45f5430db4262552defff9c46c7204fc715e4c70c3ee886e5c90bd5e11b5893d56fd601a9569d1ed08243bd51aa41c9758a201b45a6df2581c02a5ffe0a655a28d13c3bb43529995c03d6cf6830d530c4c2c01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b81460913ceda23060d6f1396f64e5c0d6afc0e1184b6838266cba5dc367ef47","proof":"a670586460a5c46291cc900c3ef18b9a2a86a4c9f9fdb858b67fdb4503e23c5e546f60df42156b56dd00d2bd5da9000b23ed60196e56421d6116a1c55516117c408cfb74a027406a40f786a83d683192669780049e9b869220c5b2cc7b30102fd2f191f4b4afc63879469172247fd98c6cfd93f414b45f38138e4b916dbf7d140d589677c28576597a61623a3b3d6ab605ea126a06e2edd8ffb0a4b15c0697085f3cfb2d50768de0dec93432cbdde1a153d2ed02f3f9686bbb05daf433dab60e90b802bc2bb11a52e488e20d80b4f664d0b36d985c5339bc70a81832b23e6207c42e986e18a5b549670448fca10bc6e1c54614ab926265f693e8c9b8829cfe3a247115bfa08701864db76774780a590e02aa74a1341189a8685cada72d1fa92a0276e3912a41d4254f3eb43ac51bc4e7d7cf6f77a5e0e9424e43fd880e09db271a633571f81f79e8d7a7e625601596a74e9c4859bab43ae24dc2b1749249b61c3aef914244cd49c873aec3d7f19dd0315d26a5ff4411b0bb16500057e9f49c714c02647be55e2a9641c1efdcdcd3c30c1419d5459f5c2ef933e66f5c8e76702cb6cfd982b75522cc8944da3a45886efabd46ed6ba9a8b8dc78562b8e88151e5a3605df02b6503b771598ca0cde539ba530db92f344a9ff99f5a5833a34aa13710664d34b17b7be4583a933bbb7917d2e7d9a076d701cf0cc41746190019ace413ae78d8784edb60254d68515103dd4a15635afdf0de400a04a35d6e5afbc0046dc61024435bcedce2c1ca1b156e3833ea367b94b1c659a9f8d53760db768165d04cf3e20c7e5a7eef322bdd033ed06ad850fd5cf43096d3f3cdb3390d7bfd61226930db5aeb32a3241d2377a14d59cda801d08c96561dc0cf8654c3e42579a018986adbdd9b94777d0715d489785b999c6368dbe2fb67fce559d778e1f140309"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ac77562c63879a6098e6072cdf689bc995af67c9c0bfbf3b84339f7eaf1ed33c","proof":"208cb23a2d30742b319c7240c686ac41613748cb6dc06538f69006fbd0cabd735c45cd42daf6fc049f9e638990fd914f94bd8d0146998c4b709d93b223c24648deba191a4c9952cad18932ab38e36bb717ae2dce7519a9aaf940cd4bc7afd8646e8a20854d1b5ed1069d8d91f0e1c080ac6ab068aae962b8e8d37b734f2b4050afa5b288599ff0e0d1f6a713611dcdbfc86b4a285c3ad7fa8681c55dd4e80c0268205e75629ca40f9ec888678f02f24c0838c362a297cd8d127e985876d0880807105a528517fd2f2a4a13f3669f1ed477e5f62ad25cbe6faaeb03a5ccd73c081c88786cf0d5a70d0bf1f0bd11e745e377d6e5d299df2f75e75660a35b7fe4797086b79dc9a06bf453216cd7b3592c06118b1bb8b22570e1ea6f5d11d958c47cd25ab3098bda6c1c0c5e2b726b0be821dd974a5ddf9c9acf14c74d509e2cb968a6927a7e91f6caccb83db17c913de9271bc6da3b1274e8f4aece055200959305ce346de71d865801d2e991cd24073bd443983c30072966f569da6df45dc2af46fe9c6d8cde4a88678281a7a16c28afe7f93dd6ff029fc8fb30d595091554a817686ccf35d6942a187630c55a3dac75f86c20bbfff37ca235f71391db4bee5750e82674d1fee05a89a07a7f344d7162bd807bb716555cc54fed4479f7a59b3d23402d0519c644b8ffa092050722f466e9f30cbb1afc7bc48d6a6e87a4267ce15f201c95d86b0520de7e1724572dd60be13ae4487484e82deaaecbe9770c629e3f3a264e47341516d26fd7f1f57aef32e1d034ec9a393d7fcae65bc04225ed950ffc4de192bfa3bb33eb761f033fd2bec86751cc253c29d5f5e8fb32254bf19f23e73b1e18f81080307876e8af8cab57d82f730a66d88ae087d3ad5a744f38f50fd3ad143c46099cb601936f0cc9fa5fb4687747ebda0fd975c29a362233831a0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"00b50d8bc07f9ce98542fc6a20fb4c8bc160524825d87422153f057bb2084804","proof":"46a0573bf183c2305958684365819c5b23f228f7de25eb016e8b741829fdf44ceecec00920b8ce99bdd9df21feb5b1b59e4e09886fece3b8da3fc5ee91c8d13f7e0e655da985f5bd6ffc47c6e83138404358737bfce5a01e510d8a4c12cd3c46788dbc3f4af54f79b807106dfa62cb39af3eaac420bd14c835268858fdab6213fcb2ec67ca29b88a47573c55f7919adc3e24f63bc78cd1a42b0cf5185d8f6c0a478daa18625f651c31796ebd911fb197b69b379bb763242bc7f4d3aa5525f003691df7fb5e3aef3aa046569c880484649bff541635fcc1e4e3095fbc891bbc079271b9e91d6a45f01bb088b7b5a00ce1e590f688124b55a2ded8ae8a15afff2a7c61336407fad8f2b50908eda3db59cc96ab8d697fa1b293621efea1b87b8e76e2a66bb53bdfa01ab1a55fefb5fe368d40aed8c18e18f5577a433cd2b055de4deecd1ac02a0dbc90ccb619f6bd6933b644631a4a6ee5479ae013ada28550592738ccdc935cb3b23caa54ee84c35fceb69c6ac513b0a28fa77b44db6a26d17124c46d4fb1614ec09c3fde0cca58e1af47a37d35c449a563b241577d2451c2ef37080fd740524860cc50a079eb1076812d827198ddb5d3d685c41c26f9a4707d3a1c30198970805428589aea346057e9d5e3de4daeb11a5ddce26b7ec13ab68f5740f14b1b25410a2dbaa0ef6f823664f26555c563a3cfb75edd96a09134eeb46ec652335c3fd6a259395e2e020ecf8b1310ecb0778a4befc44d4de1e230e073684c9b36269a7c447cac86faa9ecb17ea9da15316ae6a26aaba1badadafbec0f3162eb962dd668315ede20ebe5e59b2494f0a825b5c4edca18e726ee74b4d5b439f37b36012643aa11c4e9c40a493fff0b25f31be06a83de4ac8f1316db79cdd0839ab336a082e91bd937855253eb36a0b9aadd936818dfcc7c4b1c680a1e30b01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f0a1b82fd758427c1832d8f00c9950b5324f8f5b49474dd5307b55a91bd89a75","proof":"aad2ee03073a4f532176dcc7f8dce78e4da544847644e48d97e712849fff38596634ce96142772580ac87bf544d74f6dfe042bac15fbd741c8943c119d59185948720fe1a917d69faac471f6f57d3c56b87869748f897a1ba0820ebc91911070ca69f688f9f9462b4b69f37fc3ee8242722df6ab42628637803249a77b753d4059acdb77f0138315bf303554fead85192771efbf83512de93bd5f9784bdbb40f8600e2c4617b91ea01f31c8de232b16704dac19c0f3c0b14a43aaa9afc3b180d8694a5541345e334c68335258aaabb51b8ffd0137fb39cf986961fcdbe2fb600aed8fd9fa55a2d80b0523406d8c74bfe26ec75665eda5fa1a91ab16089dcbc63ee38179f34517f12e6d66e2ef8b9cd48293a2b8f627880c7dcd4cd133c01c511566d64260c561a1d620a22d0de2d84cf48826202aab9ec5a1d6a5c3e40f05d7da071c3668b2490897f038816e822d40617fa3ec289e2f2165f5ce4c79f3009200c96c4b4d8e3842d22320610186fd4b11c9d2af4f4421abf1817cc9ce559122f56143afb30ba9dea116fcbfbeef6831977803db9b055b2d7dbaa9d0c7b7fbe6dd089dd6c5f79c44cf825aa7ec871f834c0a189012608d708476eb76d1112ff20f4bf221c36785d5f1c70a64c3453311ccdd9d41bfddf2e0fe6d21a4b7ca48638d4e9821b734f311da95e682f3735c9bc480f8474c7b07d8e37a7cf2c1a017c0630221d78db5c02f7a22de6f24771ec03c0840a826ca9a659553a28eed408b072da27bf03c43636883664212bded5174854f29f76b676deda1443b5d278c9fc69644392a079e1bbb94fc1b75f764503f1c2aca5cf2345735ebeaca3b1efb1102c6667d46fdd518edddd9d180d9df96995a4e8a42c47322b6eca6bcc0b28b2ae050d35da0d53ce2d3fc40767a1f8f5c3c18326dadce09ef770a52f9922b18c0603"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"70cace06decbd3a861b00d73f13bcef40e594b23e6f19450baa18a4d4c93a146","proof":"a2bb66a3b2782ce9d1a85cf878e9576599e546165b625bb6233c354e4dcb024330091578d873b12155fc318aa4b235003a1783ac5343733c0dbc2eb80f26c716121d5bce781b093c058c28a55e22d8d04affdec2e3c559700df888194c8c4f69bc8c36f5aa2c7403c425441052fb5be659ba90565f9a0415dd8e371ced0c0c3b49daf88717af8d35c45083d028c873bc4e036e3b1039212740f2b3c71bff730edd2aeb4904d81bbc60025b2b1d77ed590a4529ce51ba36729ef0857bee43590c0e0c75a8565a30804697522d8e6553981ef8f84b1c1858feaacde69b31b55c03a824f151f99d32ec2d18f7c11e893d88e524fb25b3fed77a62b0a45cdb396c3970ee92e6c4e8fac40f4e53a0171ae8eb6f8118b84f012aba260a7c6b26eee3301e61f4bfadcb8d16738493cef53e73edf32ad2b99a00082e8f21b052ec82c2106ed52ee73a613fe2326ec426410882c02c9f000576c0eafc4e1d4b960f5a4878f4a353508dba0f8d0818b2ca0be5029ce512330029bd65a672e039e80ec7607268703518f923a60349b59e978e820e5b5a781b216484f32fb39f174e92c0ce796c3eb33bd0d6f2b14b36c73eee609c4d61fef4c845e85ab57d2a936163cde55cc6a2c52c8c05aab7a80afca34c09b47884583c20c498759c9a0ac3eaada63918207417281a2b1699934583119b8984c2cbddca563996fcfab758ba2af15d24604033fa0b270af8def4931a12f9db126e82f6dac3a0a8800eb9a70a883d3d076fc0ab4f8c82c44b1ef0c16aeaf3738bb8bcdd3f564ec498a6dd864fede9a52d04fac218e1323bde07330d1f20fa46e36d7396b937a97c33586d32bd63052f6437409df76842956ad17ade5e67851ee7b811ba859a63ef5bdc6c0b513ea1ff420985073982ce4dd0c7aa9996bdc1f964c7171a263aec9cc0bcef9a705d90c88804"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"58fedb292a356f0132da212ea7e62eab945e85579dc5dba34dcda81c1b928e28","proof":"86604d9312b939accc6edf13cb2acd62adee773e5b26447947851ff9f34bfa02524c9f4eac5e7e158f2a3264d0ccdd2250e0ff76ffd467e541b171a82cf9ff36867b6807cb16cb3eb01ca63949c3fbb8a06898400a3e71fc1acee5bd4fb7065b54f6e1d03452cf56a341e837c633f1898a65ac813185eae2bbc6bc2a9f12df2f26a124c8df79244f267ce8ab66414bf1d970832b20665a33608e1f8ccdf1db068e5ab866bfa1841fdf1073d5bfc9074c2af64d7db786291b4531c3d1b22db7004ca924e77edff381d0c0dce7a2f10c66d92dea0f5bc3dd292c6366c382a5e2014486265f9449459c654d8337b8d269b4a081d1539671f02839e6e22760be101740019c87bee30241cc6a9ddc4c4e1807d56f17750c49f7106792671f0c51741f32a4f67b3661ef117027c6654ec5962fb6f2321f8deea33fc4eab012d54a2730dccd2b0b4e1071950856a3efdd88201e51c51977c205b824a9220a6b7588bb2a08f2112e362e247366c74399b06d0de80aac8ced55a7a3b2de974339d2b23919d4f266be2355154a9e9066f8894b350411de7e620b28d997cc364880297bd46eb4206c4e36faa1850de24d73bba760816099cb7bfa388515d8030fc53890c4565af642fe5b616c67832e743bb27a45372a7fd6b8821e02626eed3a33349fd872dc31858ad4d78eb3fc378d9f602a9c7adb0db3feecc80aa58b10cdecee776a57ce649f742870c338ac0d955560b2063ba9f3536db89461649226cd5a289baa070624d1caa834d62db9974b0accc5f2f95e7ab3749598ee218cb1cf76206f2c6578578988946702bc360f12ea4110bef0fb3289550f3bf100b72c2f73167a4475710f0dd6ac8139c1297eeea37ae949097067013132722c5eae0b97cb2f8bc10bb7c496bea4196d4d49ebef8afef012678b79f30bc9489ae36ff734686978550c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"145dbf11aa888817ec46d4962e94577e1178f437f637161e8a5e0d507ee88d78","proof":"fe9a0d6aa3adf9c70b560c7eb4a50f7567b4ffea8669b2fd84051768e06d40560e1c43402b7d2743b54b874fbba919ed606b5111e6d74f2afc8d3d310254e066fed642d1690c613b63047f1fad1070389a72e5b48e01ef221f793f983511d4183a0218a29a18ffa77b117457b8e338df94e0a5ea82e06655c32f538d9502495f318ca0b9d2af2ffeb204d7958b5a21007a057af8ee435e7723ac472f52427b0ab92de2f61133a63b4f785651730c4bfb18b7266c08cb40e8e3cc7e22f20a3606237c7a0efc7695fad6ab4a63aba1e16e9277e1f6db50dcb8c8e9abb466604503f0100cae19507060284d05a4ac963cb027efcd71b71b546a971687ae56377c01e840980b55cd461ee73f2b0cabb8812d81c6de6d4c5eb9c5c9c150b082b9d03d5289e86aa3b7994cd5226ed61a1e50127452ff1ed3022c498f92cc4256d9332f9e844d5eff16cade1f67f4de54635378d8324c127a20e6d5556b9fda68ee591a7257a40525571fa9f0b04520c50c07bfacb96c991290f954ce4a0e4d863b51108c9570776d751888e56f8f896e2ba33801099c7424f90ebb6c2d5571c6e48d13f67f9a3d8f2344347ef917cd5359072e58f40f009585c50edef0da0315e7da27724714e6a04ac57701d9d1a681f703d5f0696e9a9391c411d747e9c2506c9b51364a156ffd0e0cedd465f2f32be4dc0756cdd9aad6dec8400ac0ad8ad832562230da56c8f8214460a5946b74d0c038fe18549698a8c7b811d2a07ce94e706656f4de37ddd389e49a90b279c0316905819a8e475b1b8a2df6be8e163ba6984f3b76d890691d3d96cdc52ebbf1871f0b85f52e1f884d0019fa4598cab922a4902aadda8e107ddc0227792cec5ced8375cbd1f80ebe7861deb7777ddf77bb6911061aa0bf4592a26d5a93ebf1a0243585773720727da59e9ad91033b170b277cf0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2efe6866a459ed64177909f8ef3279903a966199b79f5864fb4dd5f1472e5b55","proof":"0ac39eb2999f69eb16f50a70998a3b4c6d874f747b51c7aca072e3d353db3421b831f268eaaa5871f70a28611b780b565321a19e78aa6fdedc81fe2985bc116368ffbb5886e6830b8432ed283fdcaa3a6abb0728ecb65a0209d181cc1089c131ace80483e9999a6ae33ee5a15f1b78a7cc4e6d9590d856f908f92e967634953a2e752e4f9e8797a911f41609a960635a291a4e0d25fc50ed6a23cfe9aa4cb2093090af4397d2eaf873cd83d4f4c291002f98bb0eab4297c778131863c123a2041226fbbd73d0ad87f35afd791dd149f600b28a558265647d31e434ec5dbf4201cc5364bf8cf45b06b7c2491d1710e26efd439b5ef1fb1dab75fcefb43248a1656aafb9611554122df4bec56d919267d0c5f091a6685ae92a18bbaf2e8e85061c20a4157d3bbd8fa1086a2c0d39463f0abd616a3cadced1e632fa037d96acb92b882459166d49516aef96a712208267aad4d50775c57170ff73d6b92f8670913a508f58608a1383c499d35655e900bd3bbf5ffeec58ab3abcc3a5cbc57aa50a64a2f308e945958150d4c710d0395e2cde03e6acc463d8aa06459e90d38ff0cd50e498e1d18d52270d19143d58d3241c0918f53d0c1278688718e2a1b7100c1143b6f979e590398af371f77ae166a8064d4761c6468d1f091c1fa4761a128aa724524cca67fdba1c68fd2834bf3b65e2e5617fa13ba35330462ad67eb1262cac4052712f5c6e4a63980efccc3c02dddb5a84aeab9a107986506bccfc1e1628af68cc97ce037647cfee3e03b97bc2f6e71037d381f9939f0c0aeeb929c7011cfb260aeadf02266a8aa0c6207ebde12dc5f27c7b352b8546cc4f7d5777cd2cfca11395ada01177c58bebe3df6bfe0e1273282fe80df19aa28b1795780e09c020fa0047fb88d13823095f75e52672b28e09431e5ec2c88b130a7e9f9b61f8e41b7c08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7224cb7d16c1557614adb32a7407979543598fd8c5df32cebb00db1c1b97345d","proof":"fe028e8b6622ae81be4a61c4a02fc6189fe333b737f9fe11f7bc9255d93d5669b4aa603e30d077daa28340cc727ad4430df953291683401201abad64b7c7c05908a44de8778feb2d461d19fd056db269ba3973d55e7358367c2f20015bc69e7f303748d1b8fb53e157db85f48cf3de543ab783d7262a9a6b335404d41ef48649c9aab8005ac19a52e9836d14718fbf390a88f39e23ea004d8e73ed83a0f17b00cea7f80fe6aee16ce7dc072621c697a2cf7bd7ef63f420f15cc9b046efcb1c07560da33097e870c31420508ca315cf2ab52448d932d49fd78d79d848a6a770049edaa23d9a9a1022da6eadead9185a0ac2d4f3fe4d8c02fc8b3ae5d8dd52074658fb3329b878cf0a35a5eb81623eb858ecd2205a76d9e38edbe7450944d1a06c84462a5152eb124ebb73adca0aea96f2fa10e42b0321d940e0aa5ba34a8fd7192c420316c9a4cd6c6108da04c8452ad04ab180bec8bf6c57ea63cae2702dd91c7c560515e9a6c10b4097422c75ec15196fd149dbb4b5c20bad16daa9024b0c46de00411cf88ce323144c3b8e0b2a11e707477a510b9fab02ca88dd65c58ff47f7824c2e6f1f597c4c60a1dcfdea41fdd47741b858714c1ed640569abce60a437a8a45f4ca8f3aaf6634974186fcb8377d5aa3f0ff54eefa10b25f2d3129d765d7aa1671f45baefd3b4d63a53e7d8ac383deaebb8d729f0a9c63661c37ea96474a83fd8dbb5c9d66053597449e6fcf81006173a2c942d8fbf88a97c8527a5a2245edec612d30181725e72774fc6cc050e4ccce2ad74b37c10d967a68246b5bd6ef883b4e95b4705e907aca00721c5090747bf83a4f8ddf3ce1b4155dc780f191e7fab39269224b7a6297a20eee689e904477e5852767cb9cc1e45497914488103373473ef74f72dc1825134e7a189c9596405ffe9cef2ca9f41371ad494eea308"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9e9d08acb86913befdf69bf622affc0794734ce86fcd3e7613b555b399456840","proof":"ba00c8cb1ba79b359ba4bbc4bf7d006d1cbc743fe87be38f9cf9d322e027e360a4e768e265b08da00ffc971209f01ebb24e853e2d31f1373c41eb850eda9411ce4618299d23cac16a09e50daa93f8bd991f4cbb0033dbe3a4bdd36e521bfb14158d8164df5dd61780df7afc165175f3fba89b331441034768e1b1b844bc325208dcfc346b4b7934e236f346c73d680f1c92535aace66eaf853699d01fe03380e0cd98c1470761b7cef205d17a28261b4c9fa28bfe1023e0b4497f5523eaf3d033036682011811c802e33a788986a757a383a2f94fdf86e16e157f454864e4c06184173d6d142ccf06c727a41b0432aa40aef2857f3c08cbd3aad1cdf01a76f146c850ac2ce37e570772a1c32d96a7076b6f414ff6ef64a48678de957862dcf14c696afd2d7174b37e1d5ed58ae40ee396a21048369d49ab35c0caabef53d61143ab1eaaf6d66226873bc87ded13adef7565ebd51a02bfbfc7a43ac32246476034a6c2ccb2beccd2fa11a0695257642c8442446124c8a01ce294923750b3192458034d82fe197be805b509b1f3e1931a830524df52e3edcb65d11b0d2ca33410cbee10a25c352311456023bd4cb5e24083d23ff9f256b9369520cebbc5b20b83c3a5844d0ac1de113850089bfbc3c0927e4146c58c807f38d266608b2454a9d4e32cd0952e87b8f8728cbb96b9c43409f6ba86b4912da4aac050453c091e28959d67cf33a0454cad98c3f22b7043749abe153baba0ea5b48f68e2b8c640d89b14ccad614db1551ae3c553813c9743d5da45a2a01f485f5697467aa41328663479220b0bf409d0ee36e216b539ac2956c3520ba569df68ce912e2fb8cabbf7ba1553962550f80ec091e48ecb7453410362cb5557ecd51b01eef1ddceb310a41608924547560998600784a09231c563820734b9fdc5a49d87f706da2aa03eb35308"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c2483e2eb2f0f80d4016fb3596213ecca511c8cd5ca1dc3c257efa1595c39139","proof":"0ad46cd284e6fb1affe00d1a66979d488baece1f1725eeed546323fb3a41a8451439cd8b8b8329cd63e4b9f8c36833de5cf1b9a67d21dd122d3a02bc78d93a240289cb388e7530a01429c665fb5b849d4c232329570a30b20bbac3e1c0682f10b65ac5f28c6645b633e19cb73b136bf28a4163742b9319a32b594b3068def82acd0cd7476a79fb7020b3fa47f551aeda541e07a9a99c3a4a2fb1ebe92d98d80b73ff166110d8f9f201c1f8668e6860cd9084589baeb9b26bd6c15e9ee419a403f30ca6e8110bffd57f01c66503d56be7ed641cb3a19146c85651e2a13286630eb2f5b90ce90b4740c7cde12d0141e50ef75a38162d31ddece456c1dc9b61077ffeaa7f3aa4bc4d6f3c568b1eba2c8197bf7d59e1cced9b7b345203b8ced2fc26648cf21b6ba4a12b60a9f1e1ca4abdcde4f3876475d058942bd0c9f57c86da7c12fed09b55991e6940c49376d941ef22e927735b79fe35c6c66add8ee40bde54aeb7ef3409d373f238b3d3e99517d70ba87564da39ec232f2d6b8890efcc1a7418a222578f3f5e8699f645831df0d23c93c03a17e8493b3b9d26699141b05f5d3efd3e99f83106f462eb8440016d9f1bc060e163067e7fdef6149dbdc1fab3122a57ef6c602a011be01617a3b7342b3b93d7fbe859d04a365b3235bc4b55cb3c0c723b888ab0fd8f32a7ce848ac65f9bb0103a0e0fe3965dd7f964f5da0863622ac4765f0b472391e9d43576ca3a013d25dfcb85eb77ab5ff93681dcc2ef190c7e23cb9dcb1b8e1c0661b095ffac1479a30d12255ac762d676d31f2214a4c713e8f0a7fa44b2c968672ac0a5f680d45923f68f08921fd5385c34c48f5ab699715aceb9a3106e2a9651ea065289f9a9af74697a46e353c2cbfa424494684b0f07da40a1fdd897423213eaa6ff6dd04da18f1d8a6b890865ff4b0004533e284900"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c859e74a801286f6d472fe24bf8336bba8cbe02b0c5b132e195af99c06859212","proof":"4835bfecedc0cbfc0a37d6111b7acebc111dc6a436ed05e25d83ad9deb02fb350231defd28bea50c06c71a4150a0e905eda399d26b495a575e458feffe161174dcfd2f164b0cd73841b496ae1e8c481f03ac63e743be76f90b9dd005ebb71903c453924b5dc531fc50fb015c682c4a4423f4509ebba78d060317c954d714e02591e24591c6c0edca3ddb4135312c994f219ef5af39e197142b9594555689e70199173463e6fe632380ea0b7ce3d3c33341413d7b806562bec6c0d938a433c90402cf7a8e5fc24f92c37b106376b4d895397444583781e739cb4defa0644b130e64ec5b03f3932af31ee27f40412b397a73c4bc3f3d18efe923ba1f5ea1e37d25b49fc67f751105f0670f05d0124feb2a8530cadb2ab1db8608355f3de65fc208baeef9500f726a5e00d70dfecfa88bb54249ce79711841abdc9d6738b4dcfc7626cbba2435a7b2434e2c84af098eabde0ae3c01de2c169e932a83ecf5346002086c31d8e8d45fcd46fcd7fc860f3ffa4e0cf2e634e7fd587da3a806aa9e67e0b2e765600bc4c83197a56be2a7bef7f16b494bf193c188ac668ba578cd2bb0433002b65ca2662b7596a25a0f948e67e6216b9ddda078d85d0b2f9dd6faf52302d34ae6f18bbfa1b6ae9797232eb1600beaee9e7313d2d2382fcbc66984b7e17303cf03c5a28dc39c103a65e5d83c46ff846bec05dfad607211ef7a0139da4a14a866ed8f3bcd8777f70953f324a17b2c213c6e3b26ad2412b4610a66ad696ab6b32012c6cbe0d352e6ca39e19e2b9da81e7f85f76374bb3a501a2601e6f816728b05d643c53d4ddbe8dbf34b8b26a990800e572073c095cb7ec8d2b46b60f67593b15c7d198cdd5464e02a4c8bdf4d8965081460291e80615ea499d3183a5e407258378cf97684312069e5ba8b1fad79c709087f299b57f23dc43c8dc53e9e404"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b23dda69141ff3d3eaf73a662903d21e9476f0da24091055778244cb7400594d","proof":"0a008ee60acf70e8a73709d7b899d4ef8e75ca2b344a3adbac7975f7d5620e1fec668eec29ee3e66564ef9694ab583eb3df01dc9cd4bc96032c38f855513ff2e10b254683b6fd9a0b713bbb495120c436d978ed4ff722a270625bba708f7616cea615d6e0eef14b28f143dde960f93499e2b73860ffd67e299a7daf9e21c5978dbd1f3aeaf55a36d16a60955165190b3065ffbb0087287d83e43f98c56ea1704e923b986e585e248fd9d514fcdea9a9a2325775594b94163a278751c5db31703dc5795614ad1983a16c02c88b61efecd60ea63b6aebc6d2e8e2d36ca32c8240caed3ec5af7d145408f245996f0dcc5b0e61faf19bc5ab9a86f14d1761ac084253ece0400f0987c5750d4bb51d04d0d03c71ec864cb6822ee620896d21e9e8f069e8aa2ccfe96e1c704c35752dec06566b420977963a36ec7275f65dadc6a831fbe145402721065ff85d8b9f51098593dff75c095f3b1326451feabe9ff09aa195a7a3f7f84299898bc910674a64e3de1dfc48d5c3799649bf60e0b997f80de3ca68cd364ad9a1ce5410bafac5b526b15a25e42af6be1cdff3c92127129c4cb4c34b100c6d3a67901c51fb0a85bad97d2240282d0372b2797197359e1ed1d234d08b8f3d9fac13031af7d73aa9d49c21ea82c3d207f7e474f80f617577251667b1e7979af68f5a6c85e85e4f5031578d11f65c341b976e6e002a96292f1938a1c0040ab7f5db52abd1e476bb9b7c245be851530e8fac91ba6cfd8af4cad6c2d29280d7259a781cf3b674879334dac3f65848f5e66bc63e5a308fc41a21fc68e124ae295656d23fed6ac36aead80545f5d0cfa607b7b511c395067144268874a7d7e3b582bbfaa538d76d4e01e15ce3ec271c4fa8cc2fb326aec4e79995b724e0f4f1ff8864ac64e9599408df9e5dbf424dc3f68c9ef2dbdb82c4a764bf4731e0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8ab3010d69ae971607d1db6b7edc631b976223704d41ff062d06acb43ddc8702","proof":"e0d028d3cdb5c9d76c30ba3a094df30ec382abb5e3daf110ee47ba87c9b39f072aea543d0fe9f18c5ca340586d0a412e8d3b6afb359c5942d6e6426cb62037360e42113a7a68d90f53d41ee1c1ea93acc34a3eef9c5d6d4987223294f3520848f40616f9c9d92712d9996a923b3b0e54de41aef12a3a931974a68dbaf984ac0a3508d7a25f30fcfc0078456a87e3aafd172eb2c6e914e29a6a41e1a71f5ffc06aad3864e5d3a86497a044715dfced5d6d04d4586b207b6b80ff99986cd1973040f88b139e4c0cdd90e49c9375621b28d5a568a40e7ce4c6533f6df45d61c6309a29ad652f0fec11d0f16170ef3c3c462419f4abdec81650fd952518dc735c602ea526c3b3814154eed8d6f763eaa78fc686088e5618da6284ef5461fc8857f0186837cac3340e8c58f54159af03b08deeaafcc6532c9e98c25898ea3d2a23b3e34bf57e2fa36d93fa393dcd2ecd956bc1f8956fca73d2ae2f68b44f64476ad2592e79190c11d9177e84a1fabc263f673b6551de8ad62c09ea78092908009722ea627e787abdec83633084c92ee8983e6d900fd14944dcfca696eb7f10444f85402d5b261f8043f53fb77275d766ddee6e3d40c94b47bb62f858b50ddeb7baa1f10ab5d9f92fb207e5250cc395c808a5b3173b371975c44e76847f6fa414e014c3a2c10d758a0c2dfc5e4cfea2c06a11d3f3de1ed68d618e073d70431279ea56a284da607db2a7cca62444ee322b50b4374649a7280a35938755738875cd9391186f6c04ff1854fc0c07834284b402abaa6e8781c60d1256b467a6da4f729c5596a3b4454b1f0af1ce242d2e6465f8adcdafa129328dbee00ee0f54cc85854a2c2083c25e701e3dca2b87ec957f4c08739042d5160bb37f79b95433921ad3100eb12d7f60dd746215d1132036a27a1262a577de4ab4b9dfa61b09a28634639a08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a41e9e85d9629eaf1b3ab66e54140de5049c97f715c4e7982a21051662ca1370","proof":"54a3f2a96f17798dbee9f93ee24cd1e580ce04a42662ed37ffc8832828993f4b1c662d2ed9e40f3b91e0d748fca49abb12a5f3d83f8af3e1ffd34c87999372376239b77c85f44e5fa82d984e91489a78643e936cebf9127c3a8b4eacc0f62600ec5622d24b076435479c2ec8b07b1d4d1d87958bc4cfc3843c6bde7c38aebb6a4e672ae9306266f29fa5dc3b19a62dba65b7b248755c13da4a71127f13f0510cc07ccdab8e8b7926099eeda4d9899078dc063350c7d6252a5190e8a0e6097a0f3e68b369c24a0d7c4813e83838ca1fe549bd509155a5fc2ef9c55e20d700d701e009a0139f7e2f4054705e38948cd79381734c186fe8bbee6cfa7a8b8598ae252ee88be963bcbc6a629f4c96e2adb98e75f6fdc0537f08da6529e9e1120c2d2c62e16594c29613be6795d3fe711993012378866aeafcfe2d7abe65669f352f1e54dda3e3a48e5d38ae5ba7f38863efbad0e19127d5c082130ff7a4baefb11b5c2a61f4a53823d7971f5a839036fb9134eed784393ea6dc108417955afd96de4300f4914c009b20371725f597f6345fdcf34d47d0665b67c99f98dbdf4a2bf154fa41a225c560699ece63ccd02d9caa63af7ed2588c3a8de927d57967074eb2725c4bf8b962d8cebc8a6da48c9c6d1b7cfcb8da8c1409a17bf903cf242fa48334f0fe9060e6f3a35beb9a90a91d2210ac68a8e4d10163b0e53d29c15e5dce28183640e30fa13cbcb34c26f6be20963a115923f548430f3faf534aaf692184c85f0c210a129755ac03dd8a3cff83f077a35f9cfe4840b041bb5e5756a3b8195666ca0c05b64d71ea556eed6ec3330e13bf4753fc92aee6285ee25faa4fe51b715d43edfede575ec9eacb934d25b202268f35495ceae8b160d7cb700c1f70387502314566f18ab809f213551aac62785183e761c526c51255d1345b7f6031d43a0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"60d6059a017df8da7e8997d708d3aebc66fecb8a1a777af83270a4e45a38e950","proof":"380dd77281ff47b080d8521765c53b279823c44e371f8a8c48953c279b6c9335f07f7cc1a2e40c4fd4e3d5c6f340fe8fdcf77a0d20c37e8eaa6f82785131742770ffda58d11bca49ac13dca193c32a3a568d12e59914972795bd24baf9930e66aa488e3f1e65708ee8604fb068b0f152b4b959f7c46cb7c28d0ea9e6628ed9024e022da19265c2380162dcd1af3135b6ad52a06631b96140b9c7c7e2177fa204adf518942198badfcbc246bb878245064b43bf3a6cb742b1f928d43260ecd3074eae13ffbd2b4ac5202f35ccd07983cb5b056dc19d987d49767d8fabfda4420be04fb56e74e15247bdce53f0d4508378ac4f2dea6909e1850abbeb5c6db9e276e877607b4d1e014c2e3aaad50f6a529b9084a19cef48fa974a4cf98617cd9e602a563f894356c1a8a6a0b16b5dd466e306f87e9a8cc956afe5ea0f14e45ca73d6e34183828e68664b70d9a391199ddee788969611de595bcc63b867855d88176083376f4aad8eb9f648c8e108b949221c19ed94c6123fb3931a3e9bcf62d3c299644455dd25a15242e5c60d7c0f525a75a6ee288e05d67553a91540d8caad90da6c1cda67de87f1ad8b92dcaad52bac63e0163382ed3e64c9e801ba14120a15e2846263c91081f708a17496e7d79fd2ceaf85abc2677db12e6eb19f316edd33baed9def0e7595d08d8e23e362ac37ceb9a9ac33e67526506b7e85df902484537f8ec67e4bb2c7c0ce9232a999d4638df9e00ea2546beaafb7fb5085b91a909411a6c6451151ce385b8f9fc61975886a914d0f1fb4c9119363d138e4c9813d953ce336b7e1b85e92abb409bf30c4514c2315c9f34f161d1b010474d682ee78b2cd857c5be2c55a676220f4de475e1c4ff6ecb88050e909d5be194ac7bf682510b272e6014649ed4f25b4883ef0e090151b23d4e6ac66bde2826f4110e255d2d03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"46a780db55188728c3b9a25c76d87ca6e6f51891f8b764dc407c5334dd00c87b","proof":"58cd7c32d5e8c670bf2814b29af1cda043a43a92d0541000ebab6abd9b0acf4af419dcd3c533c23996a791a7aeca491370c9f64d2800ac60b1e2941b291152431efeaad553ce04de9f2c417d2664049a8dfc24da3d3a0393b21de15826035e16eaa2040c4882875a06ff867a20ec220e7d44452f9c554aaa6065079ce19a5c1e92bc57ad21cca54f7fda70ed036ee84c49879d921f3d42bc1f0609d38411680064b2c882e4b1333b22a1c9212a8e3c48866514d7bf9dde4780122c99cd1bf90b1af2f6838d3d656bd8c6496e0c2f4b1b0dd9962f53fbf20b7a7a275bf916630fd030027f651340dabe8da72cc498f0af6c79c87fbbcf641f52d69b5b988064116e5502b118a4c0279a5fddfd191d6ee720398abd11a720d10065063a95cafc6a16852d5bd43b6db4d07cb6b98fcd5b2fbcd07d39bc347d7ad520bb537e3ee13992f69ca17fb460fa27fe93dc33ba1c57cb3a6626e50d4c3194a342e87ac8b46262694f3ab090e4986232b0503b79b8de5061818cd5e86f5e95abb151a61d07410239cbdcb6e91cdc01957b3849b6465732c37ed2ccbf4bb66773d1087ff7883bb8b9afde0fa867691ca561c08f531dda015938e099b5fb5dcf59fe94c1f5d15b481c7ab5fe617ca07db51b8fee5be32c8c4885e6bb83357d523b7887daf59847ea648e589c04dc8e94ec7abd8500b2322e518a262f2ae1a5024acdcc994cc400fe10881521d5f2c507d339456624df1825c814690afc5ed6382e12b323361d37008527295a5893fca6fd3f6b12e43cfb6e342c7809b19010011ae576ccd79a715c8d9c731d582a00e3c33dc072d20123c0818cefc0ccab0ff24af9d92bbf2631616c07fe7c2b19955f5476a38a42837b5d32417d67f2d44fd2a24a2f79e2cc07323eac0d42a9b31322087334f8e67557c830de799e0e23659befc053535fb202"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"72eaac8961e4406b81bf01044fa43672eca4e89e27cc937175a2fdbaf4d8c015","proof":"7021867fcb7f3d5d3328a1b16d5c8f286a32ccb4fa74474ae62fb38b4533d75a5809b3687821899e486ef8018cd56a924b078e322a4de6a30ae09078b93ce405f2d51cd3909553d15059c5267e75d06c57254f37a658456350c131f720d2b417041ff950180517276332023da9b5f72bc4041627b667844aabe6a957cefca315fa76605c292276b9472b95a04b86badeb05a2a6b2f98c536cf309ed44fde0d04d91f9b477104d8872525adf95453d8f1a76ca7c134d3d9ec2564da5c32006f08e1c1940afc80c997b07ce99e6b40351d5b8175c2b996aa9e1adb84f05b898b028cab897dbdbb27a0ddd97d1caf6d558a3530a2ac70f37d85050a5d4acc9100602ecab3f705ece6a525defda0a5ce76bfa100894f4cfdb39fdde275403847da2e46a0af64297ee27f443feb4542ce8ad8493b69a497b421868fe5a70cc200877e3ce7345798bae3c6f331f4a6dff303cea39bf5e39ff651eeb1380b12098b3662ae8a2e6df9db31e2cacad60f73c6b6c67d1ab60212f1660d95dcf7d6cc5aab49f0ccfcbb8148517476491d145673afe48e33de31e2fbe2fdd4e1afd2c0b00e547ae8687619824f37223528e7d2cf7af3b3720c13461ca11493960ef764497641cc60e024de6e5f9b71cd48587a659b1dc1433cacf1b53fea43fd7bf26e555f2c3a94e0a76049dc77a2d5a5f81c5d7114ed54560fb3624e83dc5b3ef2653b4f6de4167ff3d7c60c75253aebd8dfcaa6e85473981c695ab29b930935ba541e2f4e6c0526669e505c88e47c44e03e09389f58e4b8d6e1de22c85dc43d9b357215416ac8dccf17a5a8af3613f7fce8f63b45ca6bd0ca9e740dfdb6e1939430334d098f912ee48c4428031d8182a84d1a8c41cbfb23f37fac97d05f0c1c3b5cd30e0fd8438ce99c97040ca8df70cfab241c7491fde46e23f01ebcc814039cd2935701"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"54bbb6a6d1d99813ba7c431f938e1830acac9fcd33a4fe78fdc43413e489c025","proof":"1416d2e338c83f6186914be16eb441bcc755d0ddb7b2e3d66d707d02d119df0c8c9cc4430a8db528595ba9ea2216ecc73602530fca63d5b2b7cb3de94b138902f60c147ef181ec20c7618817e4842127004168bb6e07f2abaf9044230197a628b00f73cc1377331ea346f3a93bc58aae30c06e409e9183a93a825e734befac5ceac202253f0769a45df13a2ab44c165a6b0db9658c5ea8827a6a68024c13de079fea655248ac4b69d4c732b6f18238b85015dbf336042e223c227f5274007e0e97e3ea9db67e099f8a85b6d91383bae0d68f23f6b578c596922e6f5b2c807c0714cce9006114a50eb20716e1295881f457e3ac7d855a5169e5ebf69963ef271e122e4e218ad3c55a4897e5d00cc888d9fde52091f5dd2b0176fe543b44f14d6a66572274b0bf49c59d49bb557020625d6f6d2ba481992de26a9359cbd437e671fc8455728d10717d62732c9563d38d4bc31419e314d0d8f6376c9e73cc10895716d6ca664ea22497410130a8d1fde44f48d5ebe1a32f8d726e58e98959dfac16741f97e4af544f431c2477ed45f1c61be23bf1a5542f5bc6b336246f0cdba7726af839d1268d64d14c3945e625ef6d0cbb3621815a18584705d51629364cfc05722db13fe5c8dffd7f47a17c8fd12d6d5148c3b75287b6d3878c24263df54e2ffeb2fbfadf4bc5c145a71a2c88cdde05b655724c3dd7b29317933ad58a15f474cc2ea1561f58c5e9aa1bd150e65e9e83e0f80d6127a818dc3d5c4cbc574f1c4eea71eea398a9e62d0020e6567ec538dcb52ee1e0d3df3eb33df2ddd2726aac2180f99032c71f91959a819754de0102baf3232ce4d23cc46fe580eb04ac965c1b40504464a8001a40994feaf0ffa7b93ad02d9dc700572771bcdcc870fbbeab00e4ce8449702dd951cc6d5b9c8473f8833f3a5a9304a7006f74df54adab718202"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"56e60c1b17793428b60bae710be228897ea3f5a1be2d5d78a11ff67fc594ff06","proof":"c21b7cba08b830c6fbc217175d0056d67e991415952afd86c90402a895c9113a026a2c8a4571ae8d5d1e163c48345360c3b037cc82c1184633dd70feec07ba7a02986b22f27877f04b14601c27a2fdb7205e9de5b62c35ba8348080715973e3d228ef37f84766ed98d194ac959cd065be134bff94ff61047cc67283e8f8d54387c633b2bd32fb149ed9330f7389631644dc5863bf7f730fccb7487dc23098b06f7946b58095e1fbc9a9850240fa9f9a03c64e64db47370302ee72009cbe9ec0f14449f6237445e25c92a4c7ec197d34a7940de34f1eefd38713de7127e6e2908eaaab2169c2872be6e579ad4ed2fa4d791074ed0541452b1c353b25de73c40430cfdd494c0a89a8915bb6f1f8c8bde6c6a704d8d769a2bbe860c01843eddac1344aa4c907d64eac6e5048a67c5942a16140246632182473693168f2133cb6d3512a4eb5375d223e87cb9879a5b70c4626482fbfcfd01b1e53a1d593504c45e08fc614129105deb02c461ff24ebe79c43b1d28b98ba5d556b028b778dc15fda119e80bdc5da0bf814bdbfd155ae2626e20e54eced24ab2add9459f43b0ffc0177861f82f43eac1e4f251c79b19057b6282fcac52693d7422b2d7cba4986ffde2902e504c7e9352e1e2d6f726b0a2798f2a581543ee66fdf0aa3cd6c1b1584153e3c49ca384a1017069cbf9138bed960bd05239b3a1508e86163820c6fed4fe662a077d7bdc9c426675183bbe148c4cca914cbf47531665fd4fe7e49120f43f50050b1fe8f7e79c13f25bd4cd2a939a867865d888fce5dd99417c18095c3e35a396a9911b31e9b1a335b683d9ac462d37d15f5785d850096a8fc7105d65c7c5735a3e16e0330b350c3048a99ece5ef9ea1a6b29ee33ba0fb5716e2a4caf553830f0416a48df370ff9db2d15a2e2dc2d93870d5711d599af66994181db0e19a0202"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0a71c734cdc47412d35a16e0a725dbfcf4f00f9de77adb0508c4678a4cdf845b","proof":"b0af6c41be470a86623d7491228a3b88ee92eb6f151aec32fdafcc06edfcca38c43e9c1cb46555bc9ce8386a6fb12ecd1ed4bc35fb010d2d0388cb54b7be2e2c1a5057bbd89a1e0c15a08f1c1d82efdde05bb63cc7675abba6c11aa48f6f390baed27d75523224e67b6007342042d262b03e7c055c6ca997ba90da64e102be35c1c3db57c238f02473c30ab2bcc94476d6468e2b00e6d4443eef64352168ec0484a4ab1946700a21ac50b815e6e0e6fd162fe09b6b897fa07b2a4e1c0dc8d4012a1b7d93bb9f83772e05b9ce847f3e993a53c7d0ec8f075df5bac18dd32b28017008452b684a407fbc5cb5280c44544dd1c19b603aa0863a0e9f81af0b6efa3e043d84ab1b4ea7addaf3a34ecaeec301efd4abe7d94f0f2f0df8f8fb96b546676afbf15b043295b1087e7327de5a14236ac172f113599b5a23c32a8e1d8948208c6b9faf26fc8a5ba7eafd2bdf29d2b542fd876cfed97824d3d6d7d4b8e79b0bcc5ce9e8b344556b56cf9e61f14e32d449035f9506370dce2d9c5b1c1e38745cb459939731969b8c67300c288be6d18e19c9c51e386df320b1d6dd66e1381375d826649450b74a20eb96aa297fb3b9f557d032a88b864d8312676e4cf11c811212ebf29cbc1de419008e55a393e94ea6555524b2acbbbd859204f5164d6fa51e52ae57e3c9c3b15220bd9b4a6afebfe78252fed7bfdf26f87f4a2c0aac119945c8c9a156a81eec820027fb4b1c66ba54364b44e7340c4172a05fc4af8146255a100b9a9ccb124131757e9b3667ff1fe712b89ce3fdf12372b4c63299d2626f78f87016f6c1fce41e08a7c9686a7760ed2e3599fa07819ddab2054103f7ac460350a4214fff38d5a9ca6080a43cc3bbfe6ebf31e04712d4a0b1935def9c1c5904896217ed6c66cb6cd0ee8165dcd88d64be0f47eb7c27db9ddb924e4a3dca6504"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f2e5c7431bfa966dc13763ad262624519715b68c3f5c7086a485b2717e31dc57","proof":"8a74c8c8982fec7dfc2ebe37d3d3c1de7be2071efa382bb8ef1de893b27b7a496a31810023aead2f6d5277f93c1572d11afd641536f87c91cb0b30e7ad7f10627ca54c729691a067f760c8c6311aefb20d462116701897a20f199e3e28645e3c8c1f4e975fc18d9bf1cbfc01b37c6a47f7e68908023c9c9c5f3999570fcd25074c5aebe8ce6ef0f8a378a97058a584280fbd9783549d0a589d566545f0c45a02e7b951fe388475e19dfa009dc59e4742f0238071d2dacf16576813a1dc8f8b048c0a21fd65ccea5276bcde0155fc8582094bef9a2b7312a0c8c3dafd1d530007a60c88005aad939c79698d35005c115b27117770dd3a938ae6f6d88d6c14694d821efba31750a58464cc2fa22224246f776827c89e5fd9b2ed8ba81053940d627e53593cf837fe2f69975ae5ff5e543c77248d01b9845549d5750c32d2d30072f881e8217e99ae86225b5ef5015a3ae7231ca1e1f3c1e9f3fc26dbd79a5491738491dfc3e66fcd330d3b2065cd1460b59ae10924bd23e31a9a3038f726d6d7291ca6e2fc39ac42ff19ca729c52e686fa929f7cf2f9551b17d7c44064c669ce7d1ca0a1c249657cb63e056162d72b58b7bcc339d7bda90cf202629eae8df1ea4832318e128d5998bbdfd71c45229c31cc0644343de61a7664c19934b48370bd5822bb9bed5945553cc0683810574a42add1a828954f45624c49264881e240311c8864e303c5fdc4097495ea1eceee49746d9a5c26e0604ddfcc3f5e3467cd5e0c56eb815d534233d4abcb055f256c17af1d587364c62dd89c55438601b7fc6c772ed4a4bbb8839219d5295f65c550c30112fd30a83f04f416560dcdd60e3fc8415ee6065ff79bfdd67d3d94d231098d16a65bb13c36d07af90ee2c22a085f8a0d013d794a4c373db84dd87b9c010a5d9c401de3c9186675282cce29bed90dd001"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c69e5dee3fb2cd330229025f7670e3e7d5611e887da3a82bf62cf74619e45745","proof":"d419ce8c9aa662435f22a8ba1938193892830c207f981d12b6c40a38da31a325e626d6e9fe1156e767caf9613aaa71963f77839fdec031ea51edba543fb124352a31219285861769aef1ec66ad7f5e6fd19e7ed4a1c8134aeeda9434e5a71b14ac30eac6a7104f620fe8b763bf9d64c07472685817784cc4263fb9b04a98824c728683d5f5daf08a46794f6126515a9a7e7d91b8728641439b72e70a6f2b9f012c635aa2ac9e255a1f402ef0000f8d7ff1aba6b8e3590b6e71077a3a5a559300706091d83235163f98a24aef94451b84e430a8ec9d08507b8866f966dec0d9050ca17c6d7db5722664245df410f64b6ebe367b89f7c900b3b0bbff6682bee418925b1785b983398d8af82a124837a0a982df64a407d97c572fd9fb1d64a5b172c01db556264613265c2d01fca43437ed56b4f78dd64336aa13c19ef58304816816278b2bd5d6e8e4c3ebf0aa8702019beb200f5e432bb8da8dab7c05601d6a29f83318f3c177d98304abaa23c92c85f37189feaf591d12ee7732f57fa1c59057a809a370f723933d8de573ea711b886c38e78e1f1c16df4cf47e98ea64b16c1394f76b85956d4974bd6ab8b5fe799bcb8faec3db255b7d2854a789bad98d9877c4bf8652368cc58c46b309deb1f9d98d033f5a6f8d6f71ecb0e9e9b691133466fa21680882577d3aa34fa779f77e8d17e941f8f431d9edfacc3ba40314b4be13461b67127894adf503c3c9ee1284df1ee68d9248080cde5c10cd36fb603c837bb41edac2dcb9344f3a46bb27f8bfc4313927cc70b70dc91c672513e75200bb6870e3edc16646735b5eccaa01b90d938bd91a602509ae890e99d2e4fe0237da2b2fc173d0ea0d4b66fd70f5601f0f9e47562c569c3d3c0ec84c74e4ec2a459104819454d1b4cdfb24860e639f1cfec5c6f4b691fc99d2f8908d6438b028332a0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e03ff6147afe688be20dfdde214e275e9645f78332f7a4431fc9640d5574877c","proof":"0cdfc483cb15c340eadee1d4382f17fa10e1f47d0e6f27a54a4fb1e804e7ab1cdc42c3ec011654a5a10e83fed208437817aaa43c4199c78d1d95817c750e4d4aacb1a8b83e24f658d000b6105845f381ea44c8a791e47e26a01877dd749f213f4631e234ad8d345ab9f80eb1fde68598d5cf1b1fcf8cafe3cc89da6b6a6dc65b7a6bafcf89681377a35abd890b5bdebfe9b7cc30063c27c263d42dbb3053730d1347051f34c50f05071fb556bb2421eeb63c2330b59b50504e782883e3c9d40af452ab1deb85e5764138fe5c7b2692470d55db792aa3e5023ef22d84e8c496058a1e2c0d71750ba11cf5f8d54705e1c8a01627107136f6dbb368335ca973cc0e344ef5516e7b9499c9708893c58063515e940356123162d395aadd4cff8bdb70c40577a119cb61a2486dd1017a60560d839d4e22e51e2e818fd3ed3dfc45fe43e2bf1e443e10f0a6de5871c133da91e4d4e4b3cbd183563117aa7d3ad0f16a741a2d95d863bbdac23a653a3278b4fc110efc123a4edf4239c5b6a7783c93d76f66534ba7c2c6b411e4897f38e13c34d3d493a1bb3f8fb8fd988c1343d240410ffeaf2ce69e5916cc6659f29952f5a48bf1a75fcfb21b3e850003bae8be14cd28489834a6b667ed8039a2fb2c31b680d438c69819ad36b71411d806c37b5e5213fa14ebd85b6e7fcf08a4b54ea3727ad95775a794049b6b421f82784673ec7d3ba037b82250410d41e706c849817c4e8e1ee6c281b4725474f3611e8e3855c72c8e500db272a73ff8593cc33ba3ebb50a974a89c970e2121d257dad4c6b476e75109a3f98c7ee84b930ab814c7f929b832163b943c253368e2163021af286b0115d8d8703ec27e53b9a41ca10b34da9d9dffb7de3dc7fb0addc79869d790703055b90b55c6a6641ec34b50506d8091aa327a374616e98b1b03eb96157ec8c460f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b216707e0e019e1fc38316930f0858d14489b5748114c1c13062dfd58a2cdf1f","proof":"6e705207f6457d603d0a2188fc3ac83e34361bc0fa4e38eaab5820659c53be24dc494c52cea5d104a56b6bcef5a6671ee6e670e94cd83443c8bb511daa43a91b0027a501112c69d209954fc16711086dc8553f708139a4445cc16f5145539b7c40a8d42a86148d7ad6dbf17b8f4a0c69507b1fffe2d488b35a5633eb20b0091f6d4c32d7ca4750c0503a60257cc8108af2d6ec4b276da7ee65e0300c2d371f0a93465861cc261635e75b237c86c7a9db6e56c751070137d36ba8ab686f3c7a083551e3fb1905dd61e859b9b38ea32ad0e3fd5b504f44f0a0d0c2a65df2dd210c0c162e85fc6dc6d7aa4ae390398c83d9ab64db30dbbb98d9d2596cbd4962f96e747fc021abba03c7dd5df8c8675b938420e2e2be676905ad4bbc41a00bb33f0296e6514c4d3ed4f80ae5e77240e096d8d70384dfdb4fa83b44a257516929f45bc8dbb3b8dede777d9c76e7ae2688253173f3cd1afcba86ef20b989258d16d81d8e1f04ff3731a8685f10d9e9357aa9ad2f16c828fd28e3cac7a64d58248ff17c5ccdbd1132b3d8082b2d921bc37948f5d29ba20e22f915cb17c78fa6e6a4fb1c0e76b2b355fb7d5e48089a7efd1657548ca4144614699b0081783e773047183d7e7286bf19cd9c9a91433bbd03e7539df8ebe335c9aab50daeefef429ac8fd71703493c222b48e2de6a6f08ab61a1ce0f0b18ddc203e3b0546ae7c37bd68596106829bef35e867aae0de41b93fea83a3192cc304519ec576196b3e078b10652604e102187ff8a64a470d14f4fc31877b7e7f950460714555f70299fcb38dab5772604eecd06382ffe5426951ad1b0f2a5c3aaa47288d8c60c322b72f62f56733e06d740bd46c09a9166638ee2e6621ba00f96f703e02eb6acae57ec747daf603ce78da299c961ff69e303e01370388f81b6ad50302ea269214031b1cc1011301"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"86616fa0839469d12ad3b44c207cb0b8f745924c3abffab01f7cc8b361454a45","proof":"746ef5ae66d9fa013b3fae3dec8047a9a9ebb3b40853dba2cda1082669a06d4a88f262caa6a786b97f5dc966d4e0732e4de51a16e1d3b42a0819e0d90a9d4f0a3ea5aa973471489d4586930185d83ac37034f6eb12aa84ffbe2e1b48ea8cdb084cc3fdde99a630b29e9456865724199016db66b6f6f5d06179bfcd15f4c1106c6045fdb7bcf6bafd5b574e070883c36204dc0ac4dc5f541d1a3d5edc356bc10448b778aa516ea81b07b172ba8b8713a3a6903d9513f79cfa492e445cde70df0bc7fb71b1975e4fc8f69053db5f4345b8c98b2574808a1ba0dbe32886a75add0b903a00c4eddd4a6e13aa6da63208d33bd5aab0c41bf1ec5c435ff95945dfce37167c3c7cf90ae42b9b5f77c539c9516567d95a6be93cd511239344b447809549dc44384229a572d5788deddfe5eb35d6af0f6c694cd9356cdfffe0a12f459657e2ede999e8022a56e04f24f74e0a1fa6b71aa004ed53ade0e2ee15900f3b7a65ec25c247b5a87729e92726d551ab92905ecc1cd65f1e9a5cf39b660a34404f222c42899834c1b64de7c961cf9f374e98678c62a590c90b72188bb38323910509fc025edbdaedcc255c5310f82d4b0ff8e3690bc13388e831f00a5b87343fdc773e57d6255dea6bd9964a264bc991cd4cad575b246b2da9b5f9d47fc7b357da77e6f10e12e39f65686bd6dfaebfdf0373eb9d6ea681f4960de38eeb7140f3c3711e9667defc45d9c1b98aefd51a9dd918b86b3050d0a3e48f320e876acb60d41920aa09036a7b17a3b304afa3ed0658bcf310e5464f5d1cfc3da6b837bda13448d8a1899f6c83da75274dbcd16316e2666c378f4b36ca5e4e2ddf91c6e4504a3970493fc8bef962d4a468b8ed0d9e288d6222905e90ca4050beb6a9cc9e30780249915f9c2f603a1a60fef6a44db27282d182b2bb39deab0737cf1666acc0d60c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b0ff9db3f401de309f0048d1827276c06b14b45a5527363f2526e2395b6e5503","proof":"d0ec091fb109b795567b9d16e8301b460059427e55e05a276743aeb64810e817c066ce24520cffa2d22173ac0f1e1133b6a42aec296596b8e72f7aa5a261b551960f6b4cdd1229cb3159703e40a03c7c7796652eb00110fce42b2744d0794b31ce09a6bf6df4c67e57d9e3713c70a2e13b2564466c67040dcb6e8b66d70a8417a17ede40d3b25d619d49dc5d2fbce216648ff7ad79c6d67ab68539722697610f483aa418b1a149e582076b23e74ac8e5de69f561d3d4c0e8fd8c003b6bb09009094fca2e0a24f83e0653e4be0b09bbd8fff7b85c4c98b32870e610a9704eea02a6c7fb79a3a55d432664fa86697ba2aa5c4abe81e83b4954f3e3f099e0201861823557667d9670530c2a9495680a6b4be307e2472af27c00100961ec54386a54c8e46c7f0accfe86047eba198a8fb1d7c6734dbc31df74e28d27ace8855f493db2790051cb01e105c08ff6a544d09004fedc5661123ff269d32a2346054b50550276c51a8c09a58d84be8a9fd83cb9710a5313de8a57ca9baf036270aa071b356e73339ca1afadeea9d0f4de258eae78d9b2da4eb96b51a1be88850eab09fd498a2daa132c9d7c97f7b1b503a88d8c4ce51e8d4a58a18858549fecc9cef13259a24b2bdd364c5f2e8795c7fd007bd51ca6d3e354bbb7ca952b9aa8e3142be676dacd69cc39a20a22bc387486489fef82c65ef253e118afa72201087272e0b1368079921e7629f6a55de881f8a53277f52cf055998dea89cc91d98da1c851c3117c0ab57062f162e57b67864219ba5e0ea4dfb5afa08b0dd8235ac91c47cc597ac47264415778a42c227bad9b0aedb74e14c59907bd8465511eb8e6a70a899e45dd4dce7f8b1cbf93568680529264e84ee8add91f1e47a904c1b39c7eb672d5037b7da6ba9492a370b9ba046278d5f74e89b5e57d6222deb0ab0f28208296b20d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1cb570daaa40832cd3e867c808e2a3a1d93a5d0e10cee297782eec97bc00934d","proof":"5c5461a68018c782ca199fbc93ed66f840fe41164538d885b53b45397e674728605a9986b9067047f59f40deb177de38861a0d40e1a8cc7c4754d01b56cdd172120be731af8003d5e5fca52df8cb043a11bd2c900386fd030421499ecc5e75698e4969c052ce79f16ffa7b8bf8d019867907fdb678bf20cf20d2d406e75d781e2665dfc4713928993691137eb1bfe759bbe5592600fb0274995e05a7013f1f0cdfaa45432e22854d91ef07ce2b60c2b8b4be56f8f6631f70b41b9f36d69915090a5c7c233a0d4bcb7d4126f880ff764c7d460b68c75cdaeb97651c47c47f01002a8ec30d4035280f8bf185c6ab4bb57589ed5886c485fe62e46f77e3289e93448efa280440b70afb48f094c1d1cc591d3a737816ff6808b473e9cc03464f6f40cc888c7ab349fa0f36ba89dfd508c5ccfa47bfedcf08b9d894469d7ce6cd570398ebff2743ca8fc1e717e6684a3de7aae85281d3da92b29a0712eadbc0d8283accbba0d9b3f2bfeca06bb646cf662bf64c76d86ef6a0dbfbe809469f1cbc6152629c2d7e17339921ab2946c89d5cea5bcf39eee8b9137bf5a6debfffc62f3a472857f3d2a985b1b8e263def6227b1f53a3bc2af263ab7edfc51e208ddaee0c5176b7110abd82ca8820cc063a767df8d468bb11ecab41eb1933b33aff159b6829501dc98d4153c07bd88025c49bd461b43f40535c5de3ab1426638494598a0a7d1cba16684c7a35fca19ffd14be3c1e7890dccc65499b9c1dcbe1900cbe05387e1a2f35a30b2e89f864baac63d3cd07939a0b5b03828d7c0269c07696c703db5b3ab10eba4142e76a9bd24d187fcd0d290f3832a6b8811f553a244d1b9d6d5e29c58adc98b07588d1c0ee9673f1df635d29c3145f578a7432bc552116f977b205e20af1fd5ee327c5bf3dddddd19d48fbf7a4378a4a07208063b59ac20e236b02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ee0f79c95238ad90804327e9e991d2816a018f93f585be1df73c93a60853fe45","proof":"360a9ce681097e3c3846f7d0161b4f917b46aec39506571d6d593a44a032a652f8bb128f6525e03a21b6b7de9f9daff3442428e63109e49ff0ca74407234831ce6c3e3d3260e5dcc4c6e34fb778b41564aabe8f1fabb162b7377b7277d4d152884d59cbaf3a6d728b72bdf141ca032dbc5c0391441237e46f24d32746977c22e1428c7a51bc956599ad9d2ffcf6606e6605311635592f88e29c26742c0855d0d361702b10851bd7462e532f359d91f5f64cbd11a058baf68d3b33d7af5fc410537413fa2631a9f5845f8dae5d6dd52e71d08da3612654eec7c1acb2efda4c90500bea2fb3f355849c808f5acedb748bca102f6eecb467b8d8aa4130cbeaabb22aed6adbb00917c8e78e5eacf71c917f0f5c9658c5a8ce3ba2e9f02616a42f05ae669ea56a07f87d72ed26e6ded7294409cabe38258124be1c7e1dc60a05c014204ed302b11d43734b72a282d41adff07e362424484ce895ec90ad5e7a774186e6436aaa4fb20c9301e176a2a25d1ef5f2c59058761a8651b9f1ed293f299191778dd3d5b3cb97ae2eadeeb72d7f1758dfb4bb21e6622847a92eb18ac5cfcaf6a242b1e155eff83e581beb71b6dd3e48194f1788152f2a4d25e7d4d0013e113115c484c4490534b996c561f6fcd0702d6700b5c078a0d39c3fee29eb270da962da80ada02b5c08a3b83717f774bd934c10d55d536a9049927c6ebbbb26de9b938700696db4246bff379e61bccb35c55ad1494ec3337758b27eeb06ffdd9c65a72069ae364b4198ccb89f515ea3163ad001ca430d9529f3b047a9f4a2b5fdb18537859434f031bbb4287bcb7fdccc301d99abd6977e00223d7391d2ebfcaac5f7dbd585e461a485f1072796ae6e522038b11eea013e1b40131b520473ddf7cfe08a0d5be753f412d076c4aa463f0cad1fa0e4de8dea1cb07d07b109e2319223a04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e49887726e60906b7e8c4e11a475c88996e0fae95c699a87fbb7cda11efbe449","proof":"38a469e49e3566c5d8e63323eec77c62934db8d62c07d0b57787274f86c47c38fa5b3c5655a7f0338c76b0fa72c252506e308f706ef1e08740e72b16cd836e4e0c6ac500465cd1bda8c6097907b9a470d56d36f49bd71bb759e708d8570a245144d51de5b2d5e660299b4fedc4a93b1f0c167eda02a686dd17cd03eb719cdf75380e503b5981275f3b93b646047e2e2c9f29936eb990e8a489f9bf961507410fb304f7b12f3a6d2802a0f1e9d3ac43a9fff1af7dd0cd3d3b64601d98e111110b6ffd11a5df6fa9697abbdcccd36785c614b5e0c1595441cf9664db41352f3f0832e6f23f61de46ccfe1cbce33b4082e69fc838c39bffc5c8c38c4af2e23d106aa29be22bc9425fd6d4f2f7cdf5499344f08079498bf816cd9307cfb3fce5510eaa00b760c03f53a10313d788fd9305eb4bedf992a4436606ce87da9de1601c7cd2cf5049715031e6bc4b82bb4b7a61cbee716ba172303cbcf78b194ac575993e404b287f3615939c7918dbbfbd063fae3b7bba4ed3fb471eb1fef2fb23e9f6446cf61afee625a9bf856f0f2b0537c62164fc9dab9e02f82961a4a5f586056910c62f1ef824a042fb7a9f13d7e7dd7d3fb74c2b5dc54f06124e4accbcdc66f07f868d21afb93ed229530bb52067dae1afe0c1d5f894a7dce51bb7716539827e7676a1fe8931a05a57c049800f9c4f2b7add8563d4e5640b4a0415e76422a6f1168a5082a9a74e593a439a633a60830149a1b6475f25b63b81d67b8c9fe448180ac0ae6bbe2627e032ca711124c63c114cae85a2fe35b9f1712e6a99e585f5f77dea5e23331b84181f7bd1b7e0ee3367ef4dd3173b1735ed125de2978ac0dd676c6efffb429f40e86f2262c421d30fb6d3b222fa3f6533dae5bcb2f3e7bcae9503d26ba26f4400e29a81a0ddc46253b6f39f073cbd264255017fef9389d88b1f0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3c7eeee02d58fbe5599761108b5da0698ebb6d4713163cc28fa231b70e0a4602","proof":"38e23170ae01a39c3cfcefe07d33b7c1b9ab294652eafc7be2ea1e9cf24c730f0c83235ab874dcbe0306fa9512e453ff1cc024cd132c2766a78e507140193d4d88bc8dd6bf759ab7ad0fa3ff15fc1a04c2dbb58cdbd1f2486e6833b078e4992864a4038da22367d8be1598e6543306806cf4ef828d7d0f7876a7fd7952dde65b762a1361d6eea778c3d5116337ef11949cbf3be702229db6b3fba5b1157de90763fedeb1977114a09b07158b71cc5a07040c559eeacff2c0f4daa4869fe2490742237fe424ec48a113e5735e9f2f0a67158672956d34bfef680ac3f3a21a170ef84b252c7b897b68902e5e883c07819a7a024a684675c23c6d47ec7cf6e554263ac37c751a6ec86ffb72f58d1ef9093c7ab2aa2d6dc0782ccb34ff4d9129bd6a52ae2f7982f318fb55369646461815ebd7b7d741ac943bbec7bfe0123037a411e675a4660cb13d305bb281e95aa8861a202c4519c818590cf8a174aef0b06f1494a2fa6c61a1500be40f81bd94e18b58e0d56abdda4e83422cafe6ac6dafe70c327d7cab8850bf757e8015f3b1cec98315e36a5c2ee48815997e90cdf25fac3b0614985541e63a0348634f35d354c2f841b6e1df5f4ba4e49fcd8cb4c32a4048304e302277f2949de4414b6398ab9b6bc13eb6483e8e4feb01fac2b9b3f58d5bfae7864a1f4c8f9b181106f2ddf575dcfbd0b35ded9ce23bc23c348a343b1452585d12799093c457fb73102d2067ff4aa984a6221cc188bfb4b258a220255774605ebccd1e8519b28fba620022c3f4b003141dcf943d1232f0d7fd505e494a493890fe690b303a84de30dde3b7560f0d6ee100c1d7a7be09a0d0f0e03cda0333b3159708b2bfde07f64d40293c84cf8b158a583112a28a44836431b431a5740ad80822b6cac45ec51c21a92129a36f1c833f5d3aaf9bcfeb3ee2d8e38974e902"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"20739d523f8d0711e0686bda494fd5c9f4bc83e5aff761a7dabb36ab33f6c86f","proof":"d21f5239f0aeeeae6b46cc4df9603e61dc34525827725aecad3c1160525a0b76f87da57dcbcd87d2604514c6718c692fbe40eeed17daa24b84b0f15a4bb90547a42f2605e85eb1697fc064486f2139d3fab8d0b864794af76db74dc68eec4001b4031a504ddaa83293f70fca212a665a6e8e8fd75ed5dd22ab5bba324ffafb6209ced6bb90ad43a046527f0f008576af8ec4cfddc7b788813f7f635b0ac215034f55b674dd03e6bc0ac819b1e81ed8fe5272aeaefc13c28d9b487e599ee0b808a7b3c3fa6a5945688ed3211e1bab6f753ade596b4f673d1616a121e4d31bc50436b734ec3db13e41572b04729a12f1e969dc17d8af183256e075a63f80961d01ee8ecab7ec8ccb5f6257fc131856251d74b56e38007ccc7ee16bb074cf6ea5359a1ddcd8bea7e59afdded409dc9393c508225949d9c74b1eaba979278eee6c25f4ee6c451bc3abd65d2e4cffc0727209c3159ff175865ba8aff639dae50b2f63a45aeb870acbdc6e539a0d8cdc93a2e8cd98132bf5eb4c814cddbba603c01b42c24bd76522d0a7ae9f1828ea7cd07c85b4e20c75f875bd1e5e419dc7351f510c2654cae84a7be3490fd6331a4f0ef99ecbd38e4e277115f1d997f49836231257d43f4bfa1f6352cc528d337066c4941f08ecce1f42bc179d2e88adc659bdb00248bb3126b47fc6c2e0d564472ced5a167dc9a5e0852df93db83cc494e0671733faed7f60bc7e6a8522be3e6a1641e8c612a61d12b3f708af8d1b1abfd57cfa33b66cc2656921cb68c37fb3cb8b7a221d7f4079c237ea93ed486aa953ce666f17721308e35d3f271ab90d20444cf059365f80c317c550c3e48c2453920f349f6888475a66c3e27dd3bd6bde406b8a40753702340425d47168a9a4e26740139a09df2679c2be3d9a4a7f2a690ba2d6ad18d99fc3e9614343ef3847d7e56347a902"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"466a40137304c1831de57e73a0daf64b495fe5feffda6649690fe319cf1aee2a","proof":"f05b25c1e2a1508d3955a50375d1ec2a3f51b787e48b63dae1aaf778494bbe3b56696208c62e3d6f6591a52c104fb3292841a4343362e4ee3cf4aa1aed47e753c0230eb5b9c0d5fa69a3e62cc8297ad16fdb120216d1f78ceb522f3116abe27e7ade89044cd15033eb21204f497824405b6fc91b7771e937693df5e864616554f6d62df968f19c6634a14cddc5f68c46ca2697baf349669843fb200f3008650a34870f0080df511351c0bd8bb47b8cf329240e4fed82b3f2a8eed26e12e02103c0d2b1033a33fb248fcbc16914b2ae242f82bba5416663b5782f59333e05f8095cd0821ff020093a6a031ac17df205fabeb86e82853ce5735f96fde6476161682ad8e6bc777a52a46ab234cd79c94af6d47ec82a28f3eaad06b32897ba779d4494de07dc4f27441eb455aea73609cb66b6f593b19260214b11cf67ca6a48453a56de875a9c50fc7e8d500e6b07892e4db5b98a30a2c18ecf746cd83d296b5b2424a6e74e054ada6d550114b14e1098a1204ea49e9c230b9aff0f4b9780e0a54fda3090f2d2a841b3ea560955f6f00ade1f1bc81adf91eda261de8b21e11a5d66bcb63577ba8402bbfe78609d1cfd8b5ffd8056c72b561b936d689249d9e5ab0dfc3e9acba50c4e61c6d6b21533cda267bc0575ffe8797d31a54787914fb8346c7e203d8b57ac94520e2dc3112de2c886bd161a2739bd1bd1435dbd3b986cfa538c7fbcf694fb5119dead74ac041c855815fc1e35257d0b86e0d6633bea27f25e66fbe9cf298fefd3cbf92f525015ffc90ebf9a1c91a10c3632000e4e13ae7738b87283d3a6bddcb431117a8d3201f79671d120bb4a3d3231ce7612ab88e0f1077d72009484c67e956ee0796832963bf0bc4e11103d46f9516fce76462c59d10f33174d4814c3ff08498d469403cb5eb26bd651bbd85c5ca2b9a0819a6aec3704"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"38e603ef4b5e72aae78aee5681ff6049d2f3e21ad3c6cfd10931bc82ccdea27b","proof":"26c9c306832053363bdace909cc7ba99991b82b7c4065708b3de439bfb8cd7643426633fd9b59e0a91299f63237669037b99eb0f672bb29c43808770d377c215ecb189f42462b24a5139ffa4a2fdeb2f1a1be29cf613d68deaa61224dc183507201a874ea33809feb8b9995e4c8c591dbe9f65b1958d2d9132c47d455111ee168e744d2c020391db93c5d7b5ef561d6aeb9514759358426b46c35d998b62cc0a911270e313b04e8115f082c1f2464489027436efb6127bea7d365f5c33c1900932d88db5a264982dab40ad78a15a2de6da4a5878d745e3dd9a6ec370ae8b6d02de929be56f28b5fb8b57f0eca2503863b3060dbef929a359de49032837c58617a26efb2574fe327212f08a36d7d9533c687a5886794ff028d79e963acc6f36636e1790303fb7dbac4c566e84af9c5a9d57219dc845cba72c8a4c57f39522da77962dd7abd9636a6dc76adbeea19b69a8c95c401a8715d369daebb9e261546468ae501378960ee77e1de1423d4a7ae6f07d2451e5fe8bb7b0c214630f152c7318b4ac947653597ec2b74e43b722e651a5a8b1e5a2c21ca4f783f66552e08420729887a5fc8a4ef0005913d458921c9cb287fd12e347a5d0b963d9f96b895bbd5022564598442e454d6f800d340ecddeb77249a52bce2115f85948a44ab4c9d70756a73264c648123427517c09ae6b0518c96e1a6d76ee6f210b29e9488d287467e22bc6c8a83cdb0edf03f20d40b7fae345064f0ce71cf9d0db66939b8f57826c167a56b965571d0b1821e1890359e7061bc83133fb5142c582cb0b47a39cbf0cea7512b5b5cbdcb522971219b5cc690433246a84547efb2114867103a024c37d647c14f7f9b5525638a5c18e49d5c0ce97d7e1d64408400f0a3e518503ec20000e7402411a6b6d5373142e19f4ae42eddeb2243728a80e3426705dba86c81006"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"860bd35f4718d1d8ced811b0c88c1d13abfcb20d1d9fb3e04515761111eec13d","proof":"a006fb273c31d152bf7a60f598bd69a679a14ba4346f4e347a12ec15e0b730139402d4c143e0eea277949460147ad2be54f5e61502f047bd649cade50048c160c0fa215a2a82eaf0f3f4b1ec0baa49991d3a35bbc1979b95c656618e4b74e60c386e39433df2c44bece730938e42f1593bec4ff8094ed7d5b69159785d012478eabd73255e133b56a3082c13b7cefa17171f6989b13e05839352fe951749a8036ad8d59d066478d65ab8302cfaf9e48704a27a594738f14c3a20047c141a2c040a821257b82b8beb9d231136173ffc06f2fac2c72e7e589d80c7b344421dfe0b8e70fb3ba90f217bc35e5fc6190256e877c233e17d28e4672bb86334b323e570729273ae24783de56109f741bd25008feb2dc4be1bcced614bc779831ac1503700c805d06bf5fd40b066f1becb5af8612d7bfe710c4e3276ce32a2dec483fb7e186467d9f15adeb789d4166d2a80073fd3a78ad1dfdf6f5c835aa20bfa8ed64b2ce669beb48c4499439b681040a485b05fb9d048f40346aed36cafbb4ea2c070f850ac0611fb467822e696da7172ae391b0d6e2fab69c202dbc6db294fd6b45a50b7fa029c2465d5643bc4bd18d1df307b668abc96361a2f3a741f5d14fb6b37e87f85a70f791e2ea71875a9d250425c10dc10d604f18aacd9049f05ec6096043267bc4384684a7a51a69eaf0dc14e2233e00b5afc3b8dafae3435ef9a85e54ed4d6c339fb6752bc7ee3146ea2a785b66d207268b78f5c848d50dc0d96ee0c5bbe327e682d6409ecb239aeb30a9b1bd84d0f4735a784e29f0d44a02d417cc77870ed319065c57ea6990c4bb57d992873ab8088aa6a5be9054600879e400dbb687eb0c00cb937dbd473245b6d65adde3716bafee8f048384341cc0d079b835906a94698355ff9b6d05d780b79c816aae43090adb09a2eaff247e6714726139908"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"78d5ab89680f1ed92b7e9309698527dee6e824731e657594eacade84bb926037","proof":"1833b30ffdcc047cc531b10e7364a09fe85d1feac6bd06a4c3dc714c8f7ef23f58a5c49d43b4fb6280d341342f35f77611873fee293ae15adca405952f9f94583c265a603cf3923dc8b1cdbd1a0ac96a694b353b877fc5079553841f87908c5066aa9ebaf59aaa287d7499032c5c29bc2b382064083673e46a9b5c793ccc887717b156e8ff5fad9431c3f8dd1f1ca95d45fe099f291509ae503db769f2cb6b07b1fe0707298de4a8dc6c19210ca823ca5c763dc62f66c1dd53bba4cdfe9f450b3b599eca140f5bfef2d924e6d24289ec0c5a87a6beaa164fa1299f926f2e8e0a065458ad30b549a8dcee38c0aabfb0d71dc24c9d14bb7673789a833958b6775e7c095df88b404f1f963adb95a9378a7fdb5f209abe53b21647945855dbfb1973dc5101a56a1b850c0674a4f993ad4d32938f53bd0bb238b9025b4038d7ca9c2540fe787d2c61f4effe3b9e0188ac861747dc14bd52a3525e0a0353f3e0b1761a66358b33440c5ddcc918f3eb075393a5a90b2bdd35b66c12b20ac10ad42e4e5692600fd8a7997005af55f61991e730a9884a92aeb3e3df00cb155d02b7bb7c0a569528b6473ae64e04067f1df71b0b2ff84038107ca6d488079690448212cb13ac17a7bad2bb833b623e6117980fc72b7e06137a1ff3e259037a7ec48b65707ccc431ddab9fab8291fc3a413cc21efc8efd2f60cc9c711fac1f34623c2d7e848905e2bb525bee6d78a471f17cbe030cbae48d28cc512d6473acae4d34d36a8429c26a67eaf2e5c5f7821e526d8655e46029863721c3a142649bce52c3be4731ec4d3d76e7600b0b4eb89a282a8041b2ff29d3a059d04e372bb3080f5fe50b744e9496faec1f2b3503a17d9268becbd5ee2d10be9e18d9b0d39422d9e020c6e0bea5e01840c3ee342aa54f0f6e368f81757d413c8b462d0968d1c2aec5fa0b808"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9e7c4b91111a9f97247bd6bc446e094b007739438e41e386737f0bbdfe2de344","proof":"b673497fd1d78603868e451a2bdd14dec8a62a1551db6f5feec8e5fe9073e52ed8856e4ea47e0ada1fd7fda29ba0de74be875e630ce8803cbef1131acf0935375e36424660d2ea75a50c7d84780261ade445af8aa820466efdf12bac326bdb1bb6c78849a53d8080ff8ed64422c0ba37f9b30278f33bb236914e7e7d0ca38d3655e88915a567a8221350816eaa8e9be99ca8d079f78fcdaf4d95a5975f26fd041b6848e22cd3022a25515c2173353343b6ae15d1feba640ec16c3dd4f1c1390aa01d8f4d81e68504a2ded4a4ceae6ff9d083dfcfd1f5b3c72634e79592f4e804a22839f66d676ad5245b3835b822a5d37e4e398c96c8f977d1ea4befc42d130b30abcdbca5b9b402cc1ebcebecef21379372fc1a4ed91a5fcf5c5549b738c0578e3bd7ffd73306a15e54a30cb9bb7a76a32e15d3c8035e8e1be5c1aeb13d4f47c2437fcee72916f5c82c8352c2cae85e3d989ebd2b5b9a87594289708c5d0f47ca5913e76158be57b01f3d2ffab1ee8e4f6942c37dc0f72e2fb4af034944226f7c349f87eb61b6d7f512fd9291cf82aa2c2ab26c054ecad22162ed1fa855705bd03c95b228e09c9a82f747034fa7ed2861cba9b8c9070e90859d5498e752e1400040a16e9aa80a9eaf639c03b85c7674f8efc51d4feaf960c4c854260665831e10df438990fe79b7bec49224fe7e36b7b758aab874b8b32f384d2d6000f0c722b61ef6cacc25c9d3584df89ed8c4cb02f3e9a0211c7de3a66e7906a66a35cd513a732eb3fb0f418eb0f168ca28eb4d0accd946bc08280b82b3d53ab419179e380a06d0befdfe87751a9e83d64be85b2c6c1c63cde4a470c32f17542bf5877b15804e9eff4018349381c6c220019362b50b6dc679da71672a83d5976b3a38d004b00ae438c1fc4ffb0493e5cd77b7081c7f775ad10ae79a45f09d9ac22840a40e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"749d3ed88b8a46d8e459d3cccebe9cce78335a389b9a550dbd2c9617da36a019","proof":"acad3e16c93726054181200ee7d2f49061d093af714a95986e3f526afdd9435cc2dc14bc28d04981ffe1564dc0df91d0e151385b1ace7f8f11c788fbb3d8d63f940a978e8102b89890be73a7384cc9ac03585889c1174252832219139a09fa58fa8237fcb8c1eafcae30a1989a173b7a42017f827a80ea362c71f1bbc6eb6c230f15e49cffe0f185adb03b9e55853d98cbadb88bd08f6293e4b1b2ad0ef1c7098afd70165f249a898a23b540fff61dfb9edfd36aadda429c24b1bb04d569d605a135762e4ba84af7c011b4f0118da9384ae5fc5d85382926afe9657630d01e0024363758458107b0934478fd7bc5f9d90eef5e19f3ca3d6fce7d865ab3a78518467a9ff8accdf1b53304243de0f187f22f7268bc157549f4e2fcf4313628dd5fee179628f48f48fe85563c085c0f1f77bfeca70b2cd58185528b866cd52bfc07aa3bd46427e365f13c2005e5753dab3081115a2a1dd05dd8b3a6770500bdb055a2556a4377f35fe78b2063c67332638952c7992389af193b82236c15f07470718e9ef856cb761796603971b0db4e75d70d6dedb4a87bb1fcb21444affdd9ce57723faecc68634f10cf4dda835076816936a84c81bf76f8a5e6c87b327434607cc0b229b6816fd4dfaa39b86da3189f0d3d26e8924e626a8a503a18ef2ce66331402b468b7457138a5b5b939db7aaabefbac02c85ce84789da22db730b9a6cc2b2c05552c374a4c12b43068211e0ef15e31adb25928edc3ec91d559d7acc587528cf8fa276f259508da9807412e85b436e36f531b4d74f5df56c1026acc3abb23fc2267b0f07a4306b09d551b2cf0d73214a70d4758bd0ecf398dd24f1f67d52c851cff9c1e49bef1461a74965c16a5f61e9593e1b9d7b814b11429bd09d3500769bbf7df0c312db74dfb6cc0262621a38a5ea2b381c0ab3d9dfb15262a23b003"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"90cd3882c480d27ba53ff629a273175b82b7e429da379dd1d12b29bc887d126a","proof":"3673c036b2303ac355f79ba35c257696b2bf0ea8f995c3e1699b80fc51856a6aea60eb2fba2674ea1c846d7b39ada4278cae05a22b6d48db6a36d7d1a62432370e2e6e2d55a207d6f81e32c54f790243deb819e1dfbbb67d5643844d46d47041b89f7afa7559d573bda0bd639ed0e19779b1d2f09b221f7eeb3cbf6cca6a1d6f97d969e4a701fe0181092cc682e0d0e54c88646031d2956d7d254aa98b70590bee93d0313f48de73473a5f599d97b084acf2d559e9406a95e1bc9899d5a1880c61d66da718053d21b798ca4cf8d83f4c8590b886a54765953f6d12e258d5f30a4269b929580509968629006de94a51ca217f3d924e4c759eb945ab81a88cf426da60bf48f2a0d62ac1bf4038f2c74a6454f96b28c62ac740d441f78de289b6115c4d3a9483f768aa32246407f5df3c996ede77c05ee2b9de7a81051890b3b36848633e71e9234b0d6ad06ee1c7f444b6b59c2e15b2ab3b34f725b9fe6044a7362a0bfdc737a319ad05f21cc522e44f572a67ebf28267b9b1e0e09d00d0e19834d4a214f1fea8eefc6424b6d89ba7fc3d95060a53536dcb8fa95efc4662b3a22dd8c441ccf281cb2ccde841819978fb3b9d226ab7fc75181e7f683f43faf413693ab8329538073fc790cd459413a60d19e661f4bdb2a32ed89be44058cfb5852c5e53b228ac63eecc200f9f45511424dde4486ba13d559bc4fbccd4f0db5b4429061ba2b0cceb1cfdb71408102e5a7d659c4781f0db0fc61c91369fc4ea6cfe0b7cefe05b73af2cef0d0c7951ba87031c19f820670ad0f565ab0cca5d2731ec2a2e8145da9d4083d9fd057edcb38ceb0321844869c90f4d3ccd5fc6697af408732ca9a5a8ea28b10538e805abce28fc5c27dcb3032acae4452aa3c75c5333b60b65283a8c075fcd4abd0c0e219ab623802c87b28dad1457a8332c49069920bd09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1c8acdf8fc1c3356957c86942eeed589733bf62aa2c1665d828310dde1aa9f6b","proof":"22e57062a4eca284e8021b88f6f50cc928d48933bee2d8b63e9e3c1a419cb21df8f9e8a7d9a07f4e651520d4317234c49b37eba59bcf6074d343a803bfb7174fc8bb0938a59568d24ad423bc86c645123745782d6ee03fc81c78c3d967c70d44bead6b57c1a90d954b7129d141f3053323b0ab48b40b44401925282d8e2e853ac4fec4f850eb7eb82af586fe7535233765be8ff9837ab511429758f5f21f2604f12207f27f54926b937cfcaf602ec97284f746e455f51cd3937162e905d19f0df15c4dd9fa8a39cd2a00c5b08f17452637d9d2d01e4c2b9e639230d0f07e7d0adaaf7c3ef1d0a35b0c251b86afe90c96b1968c487103849a8e5038db2ccbde0f34d826c5cad08fd4dfe29101fc68c76957e56fb8026b56eab2547dae7b16746cdafadf906ffe518e724efcd782894055a20ae217b6a6c72937401575c93b1e02da4ab03bb9bb4828bdf0b856949c65074cfb8f59afee0a86bb2e6960c1c1a83102212a116a4177e6e0e247d51c5b5d0fae1613c06bc53545f5172ef2ed50485124e743d90ff964f9834cffafd1352a8b77f251a7d953dcd8bb8944366a0c6473a448d2aecba31d2cf880e5e69034fb45baa63c40ac5d2a6016aa0eb8d34f947b04ef0fbd7ad2c3e1d6ba3bba0fbb83672a8bd321c7d34cca6cd5b79c6cb69a1efe0430a338166ffcebfec448fe3827f319dda8e6d949b88640ef80489466e14a323902bf4f40a6810bfe420c508d5ebcc408feb97b4d87833e248748f26359180c6dbcf3f98e4e05b5dae17c222c797297844c4c2710425263ff6bf0d265f7099ebd30d3d4092c11f66af6c9a33192c7f469af3870b1c269f3a4ebd5af6a1a45cbd05d4a0f5860cec07b835ab674e6e089922876fcea9f36e6f8ced4515ae901a515e53eec910fd6b668f86857d408fdbcceab6f307d3268cdb1c1a91421100a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5cd1a7135e9f94433f7050186dfed66a66458b18ba237bfb9188ed90aeb30d32","proof":"9608a1be8bf4f353ee7c0c60420094cc0dac7c34e3bab1af55bb6b86bd266572f8059c1e805963d31f496ab4a635affd1cb364a4b5071162703b0d146f618c7b8ac0ea7c10717ab76e9c21ba075b878c59259b6bde2af0416b93e75b3d68b4367acedbf970aad283ca6bc2ea3652acb18c6e4745efab176583cbe3faccfa030633714dfd6f8a57012cbf93185ffde32d15396015677f9ba845f9989de1b14508d35086a35ac2884c0cbb3fe600f1e8fa777c4da985c9e7198dac8b9c5cccd70fcc03add00650f70bf11d1958bb603785633e7e7ca5b8a9bba91bb7b944a38f0dc8dd0e18ac347df6da0e336f087f66340853b2729bbf6af55ebb95e298a8985050b84c6a8c355e074b3548d7184f0d80dfe14fdf7e7a0f7ed47e6385f15fa524b6f4b9a38993e97aee3d0ef89d84d66f18dac76802494d510e4a70ea637a511e94f89b8fffeb86948ab5083e5f368672b9cf8027a82d573f36f3321e90a8ad30ec104d9f90cf6215a4ea37c10299b33d2fc9507befbcb37a321dca82d63d863a5a1a458f23d9501df3be1b329e64688542165720e588336f9c6c26396171e113d09a4a96c358f7dbd3de071d3ed285711ad8f8a02050cba65870aaff69dfdf614ae30bb7aa232318b6cd2dd8fc3cb34694a53044f39dda1f8bcfcd5acda91d5b56eed71044ebb9962104c4c42497e7b1d6154b71052cf07c51f5d224eaa8d9141e60e6fdaf262a46c4f594848e65bc61b224742d4c0d5880850559c4f70a9e06a4a8b2e0699b60e21ed232639f5890c4359b4521f8d59d3b46c3aa15ca84e13e5e59b6555d30080507827ef5d897bfb07f92457d8866634a6d6fe5e22f4d3450e7c849cccd352e30f02d6fdcc8426c273f0d270c94c6d8b9518e03fbcc418a0568a3efe40a225a6670265532772801e2a41b1ad0dba0e752ce76120867f4a50b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b2bbd1d5daabd2bc87cfab18a16e2aeedae56d5967c6e1d77b54656422140466","proof":"107554e674c89d80d229f7ca269e636f3b2f25c8b30b028a0f32bf83bb7b88004226a5fd5ee394f22e0203ab5b1f842b1652b6cdf1062b8d1c993315cf928140c61cda387b1448a342e84afe4574c1383d3d79b13e63c66de49fbe66103c9f4ebec403d59f9f75392f2ff12a62e216bcbdd8ee475432ea92eb34269f02b9414463e43b2ee7a00728a2cbc63d83f3ff1d8b08a49bf5a5856a5422805ff58cab0188cc6d316e9286c0c3e43e9f1cf0dc1727fe0653e65dded210b42fdfd9fee604b7f8aae129765d51f754ffd20454ddd7abcb6ab81e02703334b9a1cc3c0e6b0bd62726f0935110517d929df03bb3499355c8aa6b7676ca080c4914cac0046a62427045070c2f3a54a3b4d4f3be5bdfee0ad283efd99c1059ab7834a78c41f9526ead7903506661a722de15ff9a24b617a730d99923ebafcde965354c20c15e0d343759bcf10f738b436be9ae38e18901d60432b9b2fbc4429faa81d6543587082cc9b768dc01b572d9e33cc98299786bdc90ffb703a38853b8b93d8c74e35440c012651c5eb8fd1571bafd34d7837f8c15da4bcd0d2c3f823dea25c5601d335e6ae8e6f535b9b15a26a8222bb473dbccf8c6728314d3d6c83c0fb2dc6b45a7488ebc93522b1a27fa4211868f72e9102a03abdcc822424096479ac6bf4780727b8060a9dd07dd810b6d4ef9bca38b25f6d86687ae2a06934992205d09a28bb4799ce6c0ba9177a3a0467f0c8e79715b302cb8a19e74a3f0412f8e3604df786e0f0c1d8277c57c5a4dbfc0ae1184796b9d8b75c0b290694eec4d6651e30dd3472cd83436e4e8fc8f7121ae0a36595d86bfd667806eac3c29150c44c999a9b79f4fd86e6593cebe5a5c5f561463f10bfdfc7a6f2f0e19fd8197ca93939c01bf190485625204a779a69ab03b97ac960d4ca727a66f5c289cf4a9b7c4426597386007"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"344a7e0f671f5f2b44256f9a5200dfffa186f5b5c7736c9ca6dc861c9b29080b","proof":"a4166af24700eabdecc35611558e7cb3198513457d8b35cb665fbe7085f909256075de670e4a823600dc032d89fff77aec3956640d9c6f1996626e51591a0763b2097d582ae4ff5b560535d86ec523f280950bbc36ffb333483d51990b508d7d54ec9fd0a758626ec3ba4e104eca029cea4e657ae3962646aa2e3fb13bdce445344d7c2c30060778748b7786141a22c3a7d96322bd407f50543a7d9e0d568502ced6576a0fc10a5c120ad981aaea41920ae9bcca9867cab13994c42776fea005ffac42c95f2afc0479bc9875ba300f58b975db288a9919641f56a78fa3bdd606e228901bf75e7a7c3c859d734d2e8291562cdb3adcbf739d9cfd34e611db9a5b20d64e3fdaaaac71ad11e9f75f685426b16b269a23a65e7f8ba6f4756bec5045ca9a65ee1fee7534db7102108dc659788c5a07ede10b1e49305047f4eaf44c7d8e319f453221fd124dafd2258a02e3f59385d91bf2a125938b0b721c9f98a9649c92484398cb3347d033b2184b8aa604c95e138d9d75387ab973ee16499b92609c796b12e5a0a7a53898a910eaa1bce8fba6de210d463c810e571dbe1b0f9950a29466aa993f8b0dc411eebdebe78afc7242b6a1c8f862e2e8a31a5e24659a06768b6afdf5b25ab0840f04d6c518356c496e6c10b5bb339b3e48f1ee366fa6643ac5e226f5319d44435885d91e7e3f56915c8395256a596a5210d89c380a421e9241e514d7677a279e7288f33abf6fc95a0c679a82d575ef7f8c4c4b29812e4700f7bcde50905802cac13262c0de8c0ccbba24ace5bae3ba1ae25ed996373d5ad6889c0b5540a5501a0d7e937caed39aa19781d47a5034372ef1a28ce741ec3b71e247b78cb1fc085054c848db69b516856afafbc083788da2ff73d773e2570923e7b8443e78f9ed5b4359e129274644e70635bdff2abfc8443b407595ef560b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4a675c585ef2d70ac6aca9850ab128638abc769d796128351a4c30b8649d6676","proof":"4005f34785402b84742b2a1bd11881dcbac63d6ade071d007dfa6b3cd91f0e2830b9811a62c83437dc96d3ce10355d7bc8ddd11c0f1d485bc7c82ebe93b6264b0860f14030dff8dab718dc805376812c2e2470f80d6abe40d87881a01226bb51f260d62d02229282915e7836c1cdbaef115227b3dc2cec3a27c1ddc42ee3910928aebf5469eea4f7cae439acc4956decade2bc84b94d36527988b6fa37fe880249f75fb8ec70370df0264799440be45f5124d1a17df7eb9e518779ebcab2ef04b48aea48886c9a25ee0cf9613b87b68a29ebb57e0fd97296202232abbfb07d09e2366150bb5aa1fbd464ad83cbd7adf646154e6e6b71bad83b07196fc442330b501e743db60a11e60547373795cd725adc870b7f66e27fc9cf736c7dfecde476069337f0dd3e0a1874c9376fd9840951c8777d94dc0339351495c3e0907a7c4bea5d3f302cc3867fd8fa80d13ee82ed8938f0181ad3be308a980c56991ebc672d86acfcb4699b8b29e5dd2ea7955dc39fff731cf9df33dc61ec8c0d604032d40fe69c2ae79fb34d2271e04e0de8938c58afba0f398cb71a44484dfaf22f7286bb857581b2271248a93604a5a69b68933c2635d8e1dcae25919edde4e81b3a225dc0bbbbc7ab2acb5fc5d99a504b5efe40c709ee9cdef1efa524251e92812773a24aaad2ea9bd00d951a8f69e69628de40ac66ba19778b42b167bebe86228c427e61ab819e04aedfa94b607e2f33d4ded8b180fa2e54610e892ac2bd7f464797c36913cac9d3dc7a16305784c6d9fbd03644ed61bbb24c684f0d474d2258e1f32c6ee48a3d3437abdbf2654268c41d17a2c5888dd8a63a59be4960d334456d7175186a75787180dc261459e3c7934e2c192e054c76278205d0f3947134d8fcd03053ebd92383987de9b43931b9b5be2311f8a8141fef1ff7e6953ea8c243e6c0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"96bf7e9e364cbbea7921506449a466739a8ff1f75c39a81ec7ebf9c4eefaea49","proof":"c825c90598da119780bcbc02d99eccaf1be20e7382daa11854b9afc7ee6ac5053a880ff24f3d354fc2ba42e2fe0a33b755f506e53067a3b291877bb13145cf3c80de85b1a297f98bfddf69870b32bb2e7dda6ebcde314c3964d77707fffd3c70aea256b53430b034a4ced1e1022c07c9b68fd899f2c03d3b3f054d0bd7a60535751f045ce7c693002de5185f6e59cf4ed9dc741e5d10e3aa9e87e4830384e3071abcf064ade147a60e93ba7b15f5be5052ccf5016fcb4cef198f61281ac183095e25caa828576c69f803dbf498d39a45a630b38242a7a51426e7d42d4f103a08363bfa16e946f2279e5ae5520a06f931af8d446e66d563fe7eda3b304e629123266666efd39bb01f8600db2d6f34f0400eb80619b0a203837e3ddfe34edaae4c90479b695cabeb2179b8fa3921b180715f88a2d9cfe4c0d76c6aa14321f8c13202205997102a543a95b4a42cb4f18a5a194c8cde2f07b444f9357483653f6d50940dc608799d0774312321e9b6e4aa2fb8912c92cebf83d608a66874e1c20e460e42203f306dc9820c72328eae0615c5ab8f274a5da424cccda051faf95be715c0b2db495e2f398e7cedec9e0bf16095398f9fd8daee7b0cf544ea0dfe7bd437dcfbde452b0d1a68adce48ded6b0cfee4c35557635b1961460aa5d420a71e7130cf4852f6d12155cdd9cf2a2f92a8cdcbbb0664a594c30f97ba73d1e5d3e4a0aa0f555555e47e82d838d912ec171cfdc7690a2b07304900315ce3346e868a24d8ec377534b3fde7bdcfe9061213773bb214dcf269df4c6b8a75e429c303a27020e5253994dd3cbbea8b33339ed2d74c1fc52aed6587a69e755b466a94559eb2ccfc8a4545b5327400cfd4c966d8f477e40711ed556144c492229a71ef993470094f0757d7390af20aee6ec5ac01252d1ecedddaa8e21bc328a47d3220211320f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2a8d5c7b9167cdccdbfe4f7be816ae1a2a87c25e2094861c27a967f2a45c5704","proof":"d88f717e91a1a0d9aff69002c18b8a11fd4267c1ed34a475af6b692b6b7d3a0768fcaeb084bb024e7d9132e7d5e37aa950ae8bc7d4fa961534b059996d6621436af46b5e33c2f718648aae266df2b0d00bdd88a02010e4811428a9ca68b06e3b96af9f0ec629feea496e14c4cf352fbbd63c627047248270f2b1c9e25b1b2913cd6713d13c32139e85e819c636a05d78327237e76d706927e86fc88be496c60b5f4e183fc59bd34ed316df1b1138a6c068abd36e000aaf604ffbbd94600b1d04d0ca5ddd9e681e1e9a68fef1efc8c3875fae9c3fe67213d6997268b07e747e080269c342de3aee92c9935a51cbc93344c0c48e1579a589db75e77972e436ab29aa85778581b5a1ae2f1fe36fd7643b6c5ee494aadb9fa943de961c8e6444e2352085651ada33800eadfb7bc3c3308f756dd7d65b028ff45cb174bb3dbf14a761984c176670cbaf6cc10a3dc87de65caf80d6df67752b2679dea39c6aa703f81d2e28b1673356579b8bfc78a4b0893d4bff5cdbffe1cba13ee658e377d82154245210da2bba32b04a2a872def07045dd1e056203d3da0c8478b49574284482a79265d3da76ee85e4d55dd3e85e3781417e0280de3cc9e6b464136f34fd2ce1816a46aca49001ec64ad414b2754e38b862b0ba7a080bc01f0089de4a051d697f02665fc0778094782bd6e50919bcbee38b9a98f012b8992dfb635636ee88e657482cd79a895c8d24d5a6501ef6787bcf77f4fdd1c04365886ead3d90117ed3c70ec00c84ac17feeb73691dc383d68c9b9f3e47a91c8f0ca1358ad06bd86439ee318a952dd56bba04c88ae408732da3780120d1a7d5c01cb0dc2913b4c2a0e5242183860a5e7e8917fd093928f7898cca2f89538523e337880389d3048ab7d2930ad71bcf0a378a1ed95b1f520711da5044f2d073355fe6d884574d6aaa2e1b060f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"70f81c9dfd2b154b3b3a45589053d296acd3fe408d330ef127837aae75665f1d","proof":"7e81427d556bb864b98d5f472254d92d28070a1de792bfae2a60af080ee5654eca515c7955fa504d3a59c8cdcb186d83e750a690d188ce4394ecb9769bb0b83f569b438351794312796c31d0175a3217c0ac6952e12e7fafb519affd84397e4470e817173ad0bd862fa05a514d57ba67a61d08e7902df94dd209d8fc43a1360a19acadf63e0102391aad3696cba5d79c0ce069aa2b132da132cd9a65e27dce0dfb537ac2ca94cde513a7db2eb6f99e1ad4e4b82dae68525684e10bc8e935ec03e23bb4512368f8ec772c4e651192e53483782a707224bf20befded5d833bba0e8e3192ce39ce237d83d9c296b9c502717bf5cdada83b3cfe2e5dbea2de9f084c8a71c56455d437368760b21e8523cc35d08c9695cca96141b8db412a2d515d12804aa968e7c1145b62c9a24a01cecbe592d6ba01babde3a4d0631e812a3c26066cb8880cfad8ca9f21578a9d4ed0973c9302c0285ea94bf948394f52a67e0d3df8ed83374750f0011bc9e80d2cb97f038da8cfcd4c9db55bd7db73de461702781ec8642cf01b5776f6197c1b4fb5fe8ce7dcfaea9baa9f336a49dba33b8f9c4370bc0de95a4a62876e1782e2c81b127a012838acf5fc0537bf4cdd5d4df6c872ce5afbe76f903a40043d30c2fb53022b9ef492eadbddc228fae8fae321b47c2f7c2813a9c6a0b42e390ad89f9a262e2f720ef40c496fc8ed4868714c86348b0e3e9153d968939aed7ee73553f33474c47108fdb1bfd873341445afe6306d6f4cfc97b2321ba57659963b1687bd5d27b2edd319afd5886d42c25e8018aa680941fc103450ef93c089ca66934c0f3e058a6f9035a3bdbb4407c716cd43aa0dce044820c5e0d91054965c0e0d1db03c43c6e337616425388953748a3dfe13e68500699bb6ebfd93d8d1b13c1d26f1d772b947b867be00ed9262b70d87eeccacf107"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"043634a33b1b5e6c48d55493272c24e7d87f762ca07a3f62b769298594b6d64b","proof":"d4fc7d2709a0c70318802934a629c5fc8627e15c7934dca419138076090dbf12366093cffca27efb2eb9513f47a01c48c772c7ccbe4e49a3c5e2695a12e2694fee425eb47a6aec7037397042c9f0f30be4ea524d3c8b096e46c1b1443e25ab46a467a6794ddb266c925fc70477259ded80dd25557d9dc27fb5cb42f3b0399215c304fdb90c9d4f2fa72d80dc649d0209a987071b6069376b31c6051f92a7d707d0656fa6ac4d8b7082da07a21b7d14e2512d897da3d099d3b1d7ec12bb8b9706258ac13d5c0cc305e248275ec92ca9f126b4da033c17292cd2a53f6aec9aad0afa9bd49f4aea3a3e3f5777ce683406fab00c319396b1244fcbb31cb76fd072195eede974cf2253491f9d4209c2e542faf5c0543c51522a7bfc49c83687043c3c3e5f5528533113e10d25c7e13dc835b60e0dce0f52dd86957faedb9b63a8fb7b9475d1a682adc4a865f3893137fffdaa639a000f0ec1767faa8b5fdbe483ea60da7b3153eea2b2679f9e85023e0ae244679527f24402015a17d904491f6e1f5ee89453ceffbdd7fbde1312e7868dbfb65dcadfa5c0d73ba4ca939b7b7e3ce17c1cee23726f6f237fe956d6c564f4839054d2a05176461ad4da75f269ce965a5f8081986e36150bd644f75c852d42a1c0c2ca28bee2f83bedad83e3133d8286161c3efa59de5016cedd706613198845dc6420526689e8fb2a82418bd791a791462433ba17f94f87dff1a60451517ebb6fdd3678bfe5ba75656515eb50c6ae7a50d04d2948a016a47fd8487c75870c8e89c5daa153b5e865f1156bf9d0e3de27151c827fe61d816ac43abbb695186435d8b9373b9454037a5da23165e219963f4052dd6fa1d6ccc40c5f3cc6fd8d82ecffcd80856560630ed6f41e52765bfe920c6af35c2e17ff2d5cb6ec16a00c407d3f4fe1a0b1f254d519df0097621c488a0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3e9c10ca1f36b08b41c1452d2a8c1932b62b35dccbfd8c536f3c55f0b765d01f","proof":"b853aa6124fe393db02392fbbc24e9fdcaf9e423ab93fc20742ce56e9a6f734d9658ae085936dedb522ac315d26f85ae7131dc97c9bc9e3bfad97def33065e448422dd8d6115b8f62a671021f833e61c76c2fff6264941c53252db5b682d3960300c59e059ae7c9ef68fb9427561b4ce652b4cb72c8d85f5a98d4fec8863606c5d11dab82c8450624bfa421883df2c1c9cd664ba1fb97a32ac4f3d1a5bf2ff04a98485cb692d9397d210c8a8046a74d90744a20b01848b3a04080d0300cc830440c25f96397ccc994e23aa51cef0fe13f1530e0ef55fd04c928773efa3b5b3009ebadba05e0cb57c6e657f3d07d6fe9db00479888188700c1ef9d7ffe13e105c34733893e0c2aedafa49af0bf0f22fe65fa985361cdf6d03662b3c22bccff34664580f442531460610a41813b79adf5352f8b1d85534ad5b1817ab47c73d4d7a5ed33cffd3da2e3aee63ec137ea1ef6b745a3265850d2e2ba0f9f408fdb9c54eac7d05d7daa024a8e0b7c8891f2104277ebc9f78f482dcee44fb2c8fa4c4076f82c245df828dd889672f0fca86e96220a452be1fa355bfa81079c319f4659b240ae5d043ce59fa45513a2fbbf82877c5b5f064a2fec6cd673bb89c7e0ce94e3a9a44b03e07a0012a19523fde73c055c4507250390a66550301b3a9ff4b2c4d4af28e21edecc37496641ca07d5a8c448a60ef8d4e173d93071c2d4517e676156400fd0387e39cbe01a21246204be02030232bc256e5ac97d156115097f5fce67988536bd4e35e63b42ff4e84ae607b34f6a902d1a64de952febd923836e536b7888d0d225cba7a77b27acc9fe19bce9e32bfc3f184104e4b24808b750f1674802a1823a628bdfc68150c1c6e6e8ca309c010b5e12a005141b4ce794e181483c0aa7ebeb3946d42886e6b61c2e5b1fcaac4162f8bb16e6adb92966d34b9d1c2909"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"90ee65d55fb805fe280dc234f9c8a39d0bfec09991d8f83a2b3cffe7b19eca2e","proof":"c08218525165b348c97564bac72e136a619443a5f32e0f48c3206a3dc1dfd408f477b6338b7de0b2f424f58825132420486e6d1f258bdadbbf7a19e6b2986248422b721b90f68ad588d14df7120ab3ad20ef670b0bd722583fb872a19be8555d824a16fca7e328ccdc2fef030cbc2d541ffbe92f4607433bc7edbfbf58f0aa3c150d943fd4dc639cb439cdea7f1ce1a8e23ea4283bdd6a632c01d5e4139eff08af68048650447910358868d156c751df0b1a788f920dcc1640d6a21bd0f36a006f20b9ba8df311fe41cd350d21e7019c7ce3109c357bcb271546992bf238f10b84fc9be0ca483b6d538d382b3dbbee3203c70cfb86559663ac4804d503dd8866241f65927f7fa6cd91549779ace3d3cb39162d7dd8bf0cc4299b8b664df1a1721613ad53c4b342199188025c41f326ef82c609fd7b502d6993b97773493b8c6f285d7a107bd946bad41c6c633ec94b0085413e19eaebc5364bd0b1a8803f2412f026b1420583ad2b592727a60f797654dab9ca28fe763865ae62cda90943b748944f2bf1782b3a9d31efe56511e47f277feb2f36c1f7b627ae45859a81364b3cfaddabf7b5aed07d66588602f2d5760b3eff1d79925963cbd68366bf8fd59b312eb378cea4ae7dacedc757146ff182cd2007d1c8307ae199df711cca0335fa0c62fc7b21a7ceb671e3a0b4bccdfb5e1cf48b5881d666f70aa87a61f45a2b5d30ac69516dd59d3bbbe210703121d9af8e757e7b77a3bcd7fa9e5f29636d69345b7245c2bc97ea5f6d842227a00d69d66d5cc371a9b2a10bbbcdd8ece384547a4c2866d541e78d551532af8b74e473286d472783d220a89f5f501ed63e6b135a07d48e0d3cc64c58fe9b6c3d7090f743d373c0debe94c162f635cafb3d5415b30e59dce0125b1068c2e5ca0b412c9e2fcbab8593f56b5c51c0ca9ef094daee4705"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b6865121df7febb33c993ee460781173e97969d3c49620e8248b371fb8a2e608","proof":"ecc513ff4a1ecf7a70851ecdadc9d3fbd81cada8870d8412f21232c094488b2892ebca613c812002ffb6d2daf948e6c57ed9673d90821bf74891b6698bf3d47572d0799d1096e3c81703108e411f291384ea8936093474304b7e7ef5450dcd74dad5745890874053ec37d58b422806f51575cfafac8519370b87725387fea52c4356acd1c59b234902f97b44ed4834b2f3ae3d781cac652b86ce4f23e92f08070839b009c887040a23de314c245617a5a8342faa96001ebb1c5d1b39bb751b0290b754c4d97e94443f8cafc26c792e74741c766b607206ea94f5f476d2f2ca0e3aaad2db55e01b2083cf30ccc30df233c7e2703764485550fca70cc99ec13e48f8bd6cdf1202984308970e7b50e6031bea70bbcdfecadceccb9dde6f3f8bfa4c22c4d0e1e10ec22455fc0ced48ffdcb2ffb53e560283f2ff0eae401e8a5eff2a7cb249c68725277452c317a089547c966e236721ea3df64419487cf461f9a22ff2a0c40b325846942c57f2563ad780c8e2c2b343bdcf85bc4212d08def500761560e6b5db11251776f31ad3db5bfbc153c4ac2f3a907d3c4758bfb3761dfb632b43272b7c1e77650c4fa91e3bdb7fa5056ac76d35625cbb9ef08796778e2152c5446a8f1c0d2888b71d56ec04922cf7201e9c8e1a112fc2302d55f2342d46d40fab7fe99a9da8b5555dae03b96c7086a9d4017724693a685c0bb5267ad3fef0b10924f90a2203c0a80d376b3b5fc3941fbb3e67cda0f699fd248ba1bc99a801692dc38e35b37d694a7f0bbe2c0a8b2f85ed265a18032f2e89642569508080858d6b954744a7951268530f9ad3c34e200f375bb316765f4e0cf22689793719700e536fbdf6fa7f079fcddda06d940214c73e300533ab2fb24cfa1a193bb4d560f67e52676f6c5f8dd5a7a431ee9464a0a32cf5f9efd231eb93c63fd33c9e9be09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"643ac78111327eca5a55d340fa87287c0a2833889cd62afb32ce7c611c0f7431","proof":"ba93e6e39ef690b3b284dbe628ea42de190416e8ff89d03ff42935739abc4b273a0fe8d39e5fb99f4c79ffda2b73dd6540749e641edb6b8974d0b89dcf45e440f01371ccb693f8834cb636297f42601493567f635027083f1d6023173425bc097e68be3f8fd9e88781ef79e455af2c93d8ac2a7b6a11288b371f4aeba8f2ca0430d859764fec635ff822ebbfbf4ee591e31b3663ee74926ba88768eb90aee701d5071ce0657ca62d2c3ce30e0d3c214c6e1c32f2ee75e127da7b524407fbc700fc74158150d48ef40b88971ca62adf88b650f705b6f503101a5b7bdb4c9f940790dacfb10f6e24a6d073ad96906f5a86f51bc7fb09d6911dd95a4270363a924c92a947314fa920278856c54796944077275de829c670fe70095a60b5b8cbd55b62996c548842b01ab5b291afb80cbc2fae4cc7250ebc0ccb8fbb955cc9500a6760739c8c299d4d25ed8814e51efd162f9813fc8647e5c46aa8c13ea4907a6f2804cc1435a9609a2adb496444bc9167037c2d309d11c964fb06144270f76cac333e6897b3506b39478d7700000f8a6ae7bad8bb204a1778d31417bd4add5b3f1d36d81264bfd3d0554bce2f2bc992ced05a389940d2d2b0cff928a1fbc437002d3c99603e2f4244618f685827c62318fb520ca41b9e01a1dc020384cc6a1464235c3315918985118ce338929dc9840a9e93014c3ae39d54ddf06dc17a3afd133742847b51460b30395156e2a73050c7cd33bb03f9b7de1bfb0b66eb359a0b300908c1170743d7bf3c7e3ad04cc126d821592229743f85598af1764471816ea87e9476a212ee6f04ff66b9e5eec7779dfb7095f7e433dafb16d9e1dad3cacac45536b92938fb2977dedc6b3558cb076c08241550816ce8ec3ae8aef838e28e800b36af28904a4c2590084ed7edbf357e94567a8482946594101ef0533f5395090e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"74596ade40d919fe54acf76cd4bffda2402f7add9eec4bb2f85ad4507fd79943","proof":"8613a2ad7c68de2b72305c52b13269226a4887be0617c43dfa04f6009df74551f880421ec681df5754cbe1daa8b40cf55bfc50db625c2d5774a59b2de9a8f65f86e259fe6a54f3f366f47791fdae66d506f56ac758b0d6e36bb5f1128fadb8058a744290ee33d04927b12dab8580e767c76c97e519d2fe8461534e721ef6ec72980fdf3252f87cd1dbac20b21482eb110ea7841e4ef9912e2f2e6ab1b969e705f9f84212401dd037255a30de7f869d686a183cdbb774633e4e78c02b252dd90b9f9c71bd4a419d3143eaa22c488cf69e3e5ce5ed6e45063563aa1d795b160609800d7e1d433261349335ca87a58d5eed3774eafab6fa0b8ece52b43457c3365b5080a4ac0caff7193242ddc4e25b326b306fb3817981020eceae7459099bc1383a8752839cc9445a878fcf19f0c0c8fe7224e730d5410ae7373a28d99bfab37762801c751e50df4fbf38b5bc76d21d644dad130259ca9c94c582d5e34c2b8e3b684fadf5fb61a751f8e8d059ad89065826513029a57b87d24a3d6c0ee8aa9d5a962bf6e05cc8a61d426954efbc475a8d8c4f7a40eb7f850beb40aec6dc9c2631a097ee930f9ed3ec028bdcef96f9661a4344ab532a21af3acc4972e6be9c68749efdcd00e547b4ac777575b9c8ba4d19977924d2deb276131e026e13ab53a05eca9daf03cd1f4dbd13ad2749d19f517f31a995e78a9e0f47872246baa39f775382b30792fead93c4894ee2f3280516804f82be460d933bd11054b6c64498f14632f0850223ccc81c51cd82d1f829472244acfeaf1a92be998ef27eb70b14712c12b5f06ef2c189d5422e092914f30bc30d0dd17d600e50205e83f5dde3e06b53c950e42a7671ced8088702b6009e911846cc8c87d5a58f3b0b2671062e6fb7072a1751d6d450fd366ba114284d2cf65717df93da0a94669693ed1090f2a13004"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d2b20c2a75098e246e486832a89a64350f8bda7dea0a8d0b79016bbafa189e18","proof":"32f2571f4fa5b4dbc062b14c219a824d2fd14b133bd0eed54ca55ed0f24267051e3a6e55bbbbbf718369ae1b645898727a7ab5e269856805c845e1378de0e960f2d09a07bb61fca837ee93de5ffb2d0b3ab4762d5cc4cd1129f9694b2fd9772016d3180246c410dad6a8c1dd033500ad1b97e6e3b9b66d1ecf9353f9d965796efa832d66988ff18c6d7a51d1b31841ac37e08c597e12fff79c05d66e2736340db4ad7c7df39d14aee5e281eafad2ffecb2b4a5b0381dc6dce29ce55cdf9ef7056c72009da53d85b39d2e5ce60285451bbfcf96d6d766e7ed0426288165f71c0ae42294db5801b59ed2f3f92daf0f077e59a7bf44dab8c8683af027bfdb2daa4810cf8af6d7cf66043ea7509fab74e86645f0b813e4fb1d06910c84e1b41d10475279a3a7e1c137fa02eb2e6af1b4d6be611d1f5f7198ade27f1ffe5b17b4c221be593c3c56e6ee9aea898591ec7b6134ae6b0e9bc566bf0a106147efd724b213341c369e26bece0f09d2980a7687f4d504086e6186e590c4a58c94a6f8f2d619561fba655d1965a4695b05e30e6220e884bbc496af12f3b1693a352a7b6e426968ff250820fe54b15d2229adca4ffa746a3f779c1a9ebca7da7576d32f556833e4501a96d588f1b28d1bc5ef2cf444ebe7888dd53d46cb7c19da32ed1cbec6108ebc387d6ee1c1667ef55a4cabbbe2ca9c4736afb152ffc33ce8d609ebcffb131cc8c9568adb26f7bfd810c0fa7fc22cf9bb44778eddf441ae8b67a300a4866a04f6780edc6b39ff40787428ae469d15e02c28e6dea757422a7907026e518e45b0b532e3a7808035ebeaa7bd0140ed997851e542681b26b9f137e441cf37de4c8d19f9f31404bd040163eacc6007fa9ea577655985a0e08eecec674182e951024af34da89e91932d8e7098684d98d52f961ac15049a1ee9580a1de5f1c95e90b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"58aacd28fde00167c6b6806604c2a9a762b7d1bb6614786dc266ec4107c54b24","proof":"447ccbe9be7efcfcc3df1d40d62aa332bbc3d8b315ae0272293cec02f025f076aa9cb4be2f0a95f1c106310b7fdb6dc148fc4dc608d58ec3a50cf857bfd03b6996779d26fe1ac7130cdc7db4f791f3e21fc90c7b590125a0223d9c5ce3757e099ac46bb42b0c27f6c973d643eedd0c83cb59f3ea565fe3aa8f4944e0020a1f5e87cec60883dbf663686b5cf4641089ef2cdb646afe669dbaae1fbb874098030c24739a5d424e76f51bef45eb1afba00b5c872f39ed67e863ae2b2cc3d27fb100162d343e0b07cefc7cb3a92255dcd06ffd2b6515c2c9397109e9f4f9ac21550abcc8cdefdd78618003743e25910da49ecc89219cbd3441695fa5fe475737090404ea0db25a0efc2ebe4708d945710208878c262d96da7f62427005af012c0056866416ac01ab4f761977b2e5ef1764d81435f71b4b4eeb4ee768789ad12d1953121586d6a30a34bfa323746d0c14f98e4cbd5d7a51407175623d9d9979102b2dd0c4b858306cf9dcb920522ea317b559d89f7c6413c642f25ac190bf5ecd63153632ba4574c65201a0016f659c101da7d762ab78c66ce01174157df38b8eee071eea3574a272a477ef879cb4164715071e1392fd9105e6439bb787911143270ae4d096eb779c67ec36c40a4af72b43a28b2c9ee1aff016f00522771c3e69935374404b423034a86c44cbaab3c927a21382d2e431cd30cb3b27250d93d678b5697296129a1e87a9457d69082d1f3eb44398da180d8451ba7e38fb9aa24a068c0bd21341bfcb5dd6ba75c2d753e3d361eb892cb5d215f8d078d23ce3a41b1b12379e0964c7fb697877fd037fd6396f2d237de38d32fbff2567eae1de348792193f093158c3056d0a9ceebd7a465fb3453ebb7fdc564ac8e111d966b36c4b9a000bf1e5cdc6dabf3d5e1249b25a288267a03b0aafcf9788fedec5bdf7b73c69b107"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3e7365a88baf920c6c06acef6849213a36683a6e44958671e215ae8b1c25e263","proof":"02f41565c7891ce62da121d63a2be1ffd57147749dc0b1c131d293aa13780b27dee76d40e0ce69bc0d0f45e9c076bfc11f88fecc6c50702fb4e334a78c17415dce85a67674c0e6dbe3d98d0ab8d418b8555e378d488ae352aa7c767e50758d4a3427f53838de1ef91eef2050e3046e4498173aac405922ce1ab8ff79ef9b3b654852d6e24fe71ef04100aee5ebe9a60a20113cbf92c155b31ea4a88105b681090003862e0ef8cc61990e385aab6a7af7a425b4367d13bba6744389500104ec09a4d0ac4b678f8668de7ddb0c7e1e3ba3b32a10252819a9ea5130c7f581848a004e993a698fb73140726fd46861dbcce8ce46a1f5884b309d6dc6425ba49a9f4f8238a955e26e12a9e0f3ebe3b375acd66cc2c599a7dd56385e18638f7c64d00548fe3affea09bfd7cfb697a53a022d2344367e8e5ab1df4d1e86694ae966fe0a149209e1c310adfd89808682e3950f33622cbff70ed983b3fbe455a1b11bda1f8613794fa40c2518dcfab3b71c70154e29803c697d07925b7235fc983365d6487099a89f52512b686e473a69de2efb2d12c1a81b562a08bad9b8d5c2dadce52fc63df7e54f4ad2720265aeab52b7dcb236e3c145992a9f4435eaf237a38ea16a94a8fc34f9fdc03e71f1a34779e7da5073855e6280a5a05abca9f51168838a1e2ae605cb8c1ef2777514981da061bb6839adb33995fcb117705223c360a6f758c2923c90249f8b72cc1ec4063a8467adf35ab71ea97ecb941e1fb1c7eae0552bb421c673c230090b3c6410d8f73aac95d9999a5aecbaf164451c91939e89c00c0eecf92abb5364b860d7c832ad8548a87f02381f70522da55a5761bdc42a691267d6918426be91df13c7ad386f2a72f8d5b82d964a39f1ea913c78f2cd0b9d0845a04d4f171cdc22d5c48e82c42835b48eb23d3904b6ceabb3a3ca98fb9bdf0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2a212a65067a504fe5d9df076e8d36400c3ba9f13c05b25705d5a5ad12fec458","proof":"1236dfdef571bf16a31194441a3e1a55e45c38591858d8f8e54904bcbc56265e1c1efb738d5ea9f97f1bd7bfb15d87cdb8967c1322026aee9ba3dc9e359b342cae0c761c4b9ca0ceeefa497ba42b96bae361f6fa648f9804ce3aaffa58636463ead66cc78570f9f9703da1e7acee135e8ffd38c53c556e7b3b1a48bbc201d770f28022f9424b9ab601489cb9f0289c6feaac8c169275577cf7fbb937db979a065432f374fb79dcfb844e4380242556def5eb586487aed5a11fb16de47b19890d43a35d41b941061d104d6fc6cb3080201f6c7418d933707172a100821a911e0e12208fbfd1604ebea6705fb166c80b7664ff2c615848b8b9e7a909a7d8adc44d3695fd90c505f39557480523d940923f46e52e12ce05037e521483bfd55b6f6c2a7586826c13d934fc369a189dfc5e1ac122bca3de5c530b6a5dc0a3b469157efcf3e3f68c795a4520d2372ee1710d6e0c71a3893a53fcd19849c8b568c7023398ac40da7431fd73dbd33ba483757714698d114ecab323a45c3b9967a6b9c673669aa268e53764c27ed5e0e4e1fe30891a52af1cd62f7829b46f0a182c18e31dcefcab0fa4b5e8f91adbdb8f8c7f9d8d8e27cb13b9dc1d3c55e2c3ad2cd3217e227478356b369b91d25b483a325a317d4f01593056e6427dc21bd3a0e070f243caf344e2f2ea3f115472a54d323dcf735eb50c1d7aaf0fa38d1aee5767829509b252e301e8ab7f9d0bf679541eef9d8e0fa56909ebe8cf8f469470a0f3d43107544d74cb01f32e51048e8e949877316b2d310195055d43aaf6d5e4ec1dbed17910c2e0a5851abccccb1840fd07c95f153886a700b48537c458efb65bbfaec72151c3abf0ad5c23dfd2fbb25bb0a0e50b17a329645099cde8a021652103e1450cf341df509639ffbbcbbe73a3bbdc84da56401c9c1ed4ff48f177f9c06a084d0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f8bf8a7be635c964231b795124243c13d0a0935ada0fcc8cdcb33b640821670d","proof":"8aff759322fb9ad5512dd4b75156148e9f4c8f38e202c63b95913d312c24f4413a487ff74c979392da2b4d48160349bfa50ba3bd9d7a228bf37f782f961ef303a29b13aac74dfd9042a9fabad9ad557618c3ead7208bcf7ee1c8008c6e3b155fe8e260d7708483f037e0af62897f1d7651c770b4173dcd5ead54e12fe72bcd5d248d09159e34c95d9183a06672b6aa8e9a5bdd363a55c6cdc1e215e9f26dbe00c0096fe8d38d4fb7e4fc99c50064c60d083ac8885b45b62bf37cdf7bab94d8067cd43a3482e9325e65b439fb42fe1792921f3d343e9efee3d9edc60fdf6b1403de2f21f84fa31a7020b4b75ba17e0eca51c364b574a0fc9c72d846d448b0891ea85630a904305b3120c7baf8aaccec5c5cbf6d23c6547a488f127de32d1d34199cfe9302e4b422ba902f7569059527d55d5ce25b690ffa653ea2958d6d7ce1552844f16a09db177b7fe356c617aa9c13d75652728899203685b1f4351ffc20246696aef39fe5bfc16c0d536c795e78025132a0a2692290aefb3ac0678eced62386e099ff8162c5ec3d7856f95cf4f675206981b53a78a579ad2859cbf0d271664034363c3029ad38f7fe91ed28102e81a714bd18c0be6a0e1d6bd95f3ab5b65a583a14aacd2039b2698ca980b86db21e08b7efc9fee7a189d8d76979b0d57150ecf77147a63337d69b49ae3179afb084f3d3bf66fa674ae68fda2cf05775fe2076408750d3e56ee9b70b588393bef1690ac2e54ddab62944b5bb036aaddb632cc4460d36ed763609cbc61665442b66ab854a2332834fa9d34f8f52c1df89996f663b1b1b98c89de074f2248d77ccc4116977e246bc4f6d9a0726728d125481797927b7cfd2293b889f5a98722961ddc96a2a90874d4d02f9e6ca2827e02c4f0180cbf0ad919cd69be567bd081bd141df3e9ad44525b19f3e4ffc26e7f02a4a00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5e05e5e9ba94f7c1c0ab9d512a2a8d60b9a1335995e3b6b55bc223590b793c4f","proof":"36db29a11e395df54528700f6b181d2fb07a238f4db522af36cba2f82e13a030fcf41d5f66ea0d823ebdd4c99fb8439afe6e0c2bfb9406ddb22733728a8ab8525ccf89240151855e53fc8412d32ddc3937bacc2daad37362e4410fd5f55ae10a08eb6e4e8e85455a5205e521f7f43149cb0859922bba8c51bfc4f722bb92875079257d1526562219fd74703c4c3e4272fc9c31603bc0eaf09ec34620e723110e3151ad377d87f1a9f1b53e842525e711e5bd8c47eb6806d188101d043d6ec20d5d25b7b04888773f3b514590aaeccd14caa58053a7a572fe4349ca55ac00c9072842bba63c7e3d9f93a9c4e4c192f0bca78af8203a56b1217eeb0012af836201b6805080e41f105bc67859b0c5b6a9811cd3ac4a63f88a970c65017ff31fcf12f0e8fef2e0e4a744b0349f24546160d11eda71c3750820eaffd5c851d616b85352bf1246bda5a7f462b65a554e5537a7723a42df01dcca718b8ae28f4434e74eacb2d4d1fcc6a96b18ec201dd3b78129eb48ee0eb8f528205f5ca3d42d281c22f6889f560fa4fb762a7676f480a8bf0a39fa1f422b9ae8791f7253c66d92d2120c95894da7f4c7ef2a22e89689318a98141a37a35a8ea47783b05501ebd4372e3c315d72cdc2bfa21b9d3bac7eef84e31eee6eed953ea002ca66ab433ff3f97960b1c33002e298f76fac1ce2264c99b04aa444c803deef691da4506334130427408996f2f73c1bb00aba870379f9b02025146941860ef003ea12ca9a22aa757fb4c30aefb55179fee5e3b911058f6fd55c7618ce90b4a86516c68a12d135700836e828cfe6d719c163db93fdab288881fa48e37f4fe669a33f81322111666a4339d583d1cdca8214a9ea594692b75339835522f3b8ef185347327e24ab2fa10ff718c41ec0308ee4ba58903fb174695625f331c33559f5d20fcda0a740644901"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"eaadd0a54c57a456adf81055bcf0fbf3878f9b90ef86fc03edfc7d8c72bb2f70","proof":"5427efb89e3639cfc47e3e7aff0f71076a1597361163b299eeed4929ee18e95a98aae414b779aec450dbb393016d958115d7f7f39cc3c6ffe34fd65620f1662e38579d63b4e9f8d6ee953d6ddc5a9a8dd259cb545763c8b324bfdea4ae931c35c80a9f21f07614fb973b05b764d73e4b3c8fa0b52803e49beb352b3a271e2b17a33083ce326330b8cb6c7ef582cb9ce6e3e36dce30f41e15bc2818e00df5a7061ffbe572ec66b4e1bc694e0812f1676d32747755e0367b6e97491269210bf108e8f56ed40a1c9d355a2f932080a374ed192d25c64fbf2f34f871c67acf8372007cc2b2be352df6abe4a5809392bac0036f6a1d7f28a02d40c5b76d5837fbf96244451a0afd526f307a6440becefa22dcf6dd925666f6fb5942aecf9294a0f6780a672ff5340ddcbc33c9bed546ef972eb322f239b1f37332375d5e12ba67bd1c8c7d2f8b2ebe860966eaea0d6399913f7f0fa908e9db4009605c494ef62cd9108242f5093842c91b29173473b82bafc6085524175b14c6f3a944da5e9946bb2258b42d8ebf25c0db55c68100d426f3082ffd531b7bb1b3b8b732811aee7dd3721a2c5e53a9fdbf835ba465c3e3b104b00a05ef92e87e55c8ccb99b14b710054880b017e43531e9480f1b8306b40c90e8fecd53a34dd3e62b174258dd1a372d3a60694d55c0395c6a0d6960c82f60af9d8d659360029796b40ef178b1676bb14a0aa9c18495af818275e7ccb76511c1f4cc206acd170d825a8391a894e31dac5088e204592da4ba62f66ce8261171b8007f44ff1b1e94d465dffad7dc0ed305484207648c79df4f8dbd017598abddd15367021bf589457434954c53d31bdb29161ab3c9108afc2787956df6ef36cd65dadd309d82e1eed11fc7b5227b328d530a36b358ffb95cd034afb35ec9790c1e3e2d95d138c330f2e14cc26ef3400eb306"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5a0ce25af642c10887b52d1bdf7162d9deb355534301f2c7d293537f7c858f3e","proof":"747a0e4d1f4c5a6a87daab594a1d9f4a7a06fedb1b0f975bc98eb17312802d3224ad98457aef5c1721c6568010d188fb3a49d26f7a92e31e74a4aa3853aa0e2aa031e22806a55b85b60f22089575bb166132519749b4afa9c886057e6c49cc67f4951a82a2cf206e5df84a72176b20e6966ce25307d9b126418cd26af114f724191aeda3b902d962ce3eb16b1a856e4ea504ea3e3e8c1c8f5ff30f5ba5e03e0083b2ed832a3a840cf6ee8482a4088017ae8f7329143129977ef4093049c0090ad4f6a5849381ef7627d9947fbe9385c629f1ef5551adcdf3af5db12d551b76036c588618dda9c238823e27e225f890e1cd1dd5bcf67adcc033cfe6952965c72bd8c0f607e9295374e75445ead97311e757d7aea6f741ad49762321c31bd1a82092e2f030eedec2ad47e540f4b46b86b46d634933d3564d1cad06d26e2e0e352a9a10e8bacaf95877002773a7ccf67b24b207185bdcfb26b1e4ba1dbe7519c044e0f734d80a0ed406f36305d890f97975e1b28b38a10aea92a78ce525d27d6833c408efacc2dd1faff2f1333101343a57341823b750c9aecfa1937ad711d9cd3f1a167aca9812c6a2d623bf43baa352ff822af7bbb5399f7293676b250cf0ec64c84119a9323752561d6def5c51d0bc5a240c20b96544f54545cc430a2cccd645427262fab87afacfaa8336aa1294b6d954b1b3021969b0a2f33c1d0d7f617a6fb21989c6b75af098a5f2e7efc8104896b1bbe53276d053f31aa5130d5fcef75c0c18e7762c1c8343704cf3220b8791a5077631f1c51f7136a71edfa8ac3d5e3e2678ef46993f8fb24a090c35fc8132d48d0a4df988562d3603b08e7784cf6f5334282ec1084c05d57766657dbb0cc2c4e133a6861c840fa8644a59d2526941052d997187c7e407054a5e64b48d0f02d6a031cf2fffca0b587e7d025d5bc7880e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7c782d761b93763c6b38b94f548e5651afed82067a32cd431d5cb8a9d7a52e61","proof":"3c394d2f41e97d9c76e4ef0b8bebab6fdcb62319908451fd2ade436f5bd52f737452ea6802422237b6e012133ebe65a40807c3537ebe6460425242497ac650691ac0197b1b4ee0cfa061f933c5ec6416f72d388a2d9c6e74dd29ea4af91d4730326d03ca113cc2407b02632d6c30cd036a56c900e187bc81deded3673f81b154a6c25e1c2d9d83ae79fcebb33240b457b569893779a064ee3357eb21e87844077f7f8f267ee531cfe96d0fd042877a5a811722f1219339321ddb92d1a85aec043d3fffddd51a237cf14c6e4d40430efcd53f9b1413970420dc02b71403f5c60522b928e37a63aca34fef9bc5bcc6181061c40f0ebd0875909fd6c191654d5c230e29acbc73ea366627dc530971152395596b3ca8e4bfce4786842f15316c006f8e99d9dd80e342d88d2fa1c026d4f1043ae0a3b91f60fc2eb97923936e9a297d74b995553144e15814544c76cdc6872dc2f81741c245063a6b8d6b39875ef8768c843565da5c74d11aa259b23a5f13ed984dc9c1ba7939096b56e65f2791b161988992c67a199f616fe54d8c5e685b29bbf6af5deb463d55530406423caccc0a3600560cb7cab61b50c8841fbe46bc350d29e3711011379851f139cec917575c64303a0dbc6b6fe2782c70d334fa691f9a4cd1bed87abd64eab64d92abb9d237863b0c7524615bc981533c6fba8233e8d7ffda9375751c4b7a2222672d23ca28c82451d1e1c0b2f222a36292a0f8a93ffe530d16b65841bdc28dedae0fd36f35985ea72b10f0201158b74eecb843e0fc01672f9f8a4ff114d217f7d98692626516333ba90307140fd2f37595ba57738cb8e021951c09c627aeefec6254cbf47299096ed8a37d975330c0f8f98a8d73e6cd5e9341de3e39278e5e559495ba5608f7c3634c6b4e2343a44b508cdcc0aa704333ea5422f239b5b664f396d753c30f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e8f430784409a9b0c2fedf03417987f81a3c414e6b3888072f475ef689d39051","proof":"6cd577e4e3fc889143e8a85f88762bf8eb41c2757370b431f51d1e1ae083f918da5618862eed5f37a2c136e2057471d0d9af2544d1644dc50c08e7e8393dd51318d8aef686ac707a6482c1a050ec31e5522c4d7bf29f486d20498d5757839137ca626af79eb888a4f89f137199bb04047d21a2586b98a692b80820ef526bbc413cd7f6fd0b8b0d7c781bda4121023b56a36d194b4f414f4babb03547202033043390beb75870b47bc4235dc54c7bf51ab0effc6e9708f5d151c5a4dd7a30850ac70e55d908c29094e0a1c6ebd3eeaeb1f40eb4dd6a0332dc421aaaf07afb0f0d70be98f5d6cf02c4917a894e8bc42003fb8f970183151cc9791e1b0d78cd82093a0c071ce9d9cbbe142ba3eda6228e8d8e48a8aec6135b5e316ea88e2f4560021030eb44dbc647943fdd97ae6575414898e5b32b593c26da0e89df0f4282fc4a3ade8a6b97f6cf17fc4fd5738de94109995dc4a7047248379f6fac4ba22aeb08708b4e5c82cc7f77f4506dae84948f0a86c95208fb3f22c372a400d26451cd2894022faed775258c28ade095d5946b5b123321dcad0d86c307c862b79d42456c8e3bb8f80d041e7606fa72f064ec8951b11057f41024d360b85ee481af3c072e54d01b06475812d3b5c3d7c8163848e228378f09980601529e9d4fa4ebbdf41fa6be0d7a634b0b32a29da705ed6d5132feeb5a22b289ecf4f791234493296a5458317eb20118a70c663e683fb94b0a4f474d825066ad69d7419fc6f27c147528cc804ab9ba2773958d3aa862ae3af494ac08664997a5a0645470cf3b4595f44d16721b2a772e1c3871e09bca90e7dacc68dafdc4138e2f5eb0e87a2b236153766c1f3a5202dcd7f1b8c8b109d3423bb588d21916ff72158ce5aea4fac9e3c7089208514eef36264f9e06067dd944d8ea1fe532f43d49476d6f14b1a38ec6e308"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"74b1f8e34d6b0a6af0795f7f1f29217aabe376cefc1f46265bec3241035c1566","proof":"44dde0496bf7d0a39f705513bc636105e878e3cf44ea7f1c1476ace790dd90254813bbc9ac3ed7eea827973c9240ed08b2005e415d88b901e19ad479bb93c76e8a57583aef209106a050a2a368258d7863700f9491828eefc7fae01adfdbd342a69d5cac02574060b41b772df950ba5527061e3e51220865c86b0559b848c57fd0b3b5d88c201d786d0e6a2da54efddfc0d73196d6a26bc9610eca1640437b068448972881c36ee719d9ad540e7a577e87078d23ab87f8093f8def5df0781b09e9e3aac4bf3f0f5219e6009cf27556d96b0f00a27c12978a06a0730dbc877305ce43ecd1d80b94ef0aa776c5ba4c96e5c4e73135c622de67982fb4dd063f5d3520e12d0e7f215b97775305fc50b37c152be42fe6f2ceffff990f61b18d72cf4b7e02c056554fb84a2bfad3f4bab6db1fae3c214423fd0948a48e46df21cab92972dc7535d859e359a4f2fbda2e69b9c16d43a0a800468aa5a963f36215342f2bc60b4166a70cc66b6c94067361a2666d06bd37974d6f5d96d58cfc7650c7ba4768059f7399e53cb2e0c6e5988fb54c87a93bae879aea678ff2f0e16fdfc3a368b0154673b75936111ea83af7bba0caeb6ae71886298ad5f5e900d36219910530c66c84307edac85f47f6a2c9b57cf68e83b16dae3112c2647fe188f0f1709721b0e2ac6733138e77ad38e7d3e0f91e638a58b85a4d6f17c0c9186da01d77c110bc844caa0ae0c432dfce02ac0cae917663e146e93555ff1d354d230700aa027c1420e70b18d88e580717e2293e46bfe1ad64bfb4677f8900ce082e6601592f1ada260507efc3249155b3bb7f25706383cbda36b53a7b8f2e5a1cc3b1396bb9007cc969f660c939a3ca1865ecfc28238f2dc4ea8323ceb94e073cfb00bd98b202ddd1ad7b6be04e39666a1b03cfdf2e16477d32b6d3ff77cf2e104b570af51f02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"58ccc8e61f041a78f7a8ed93adfe808642b88270488d2d58dc2d862340b85c11","proof":"54c8f68a19b1e6415409cae9dffec4697618985835286d45246c359aaf1a601cd6caef4856d32c56c80ce945fb6199b4a1726a0c76544cceb534b0dd7f716071661517bdd58e1b61e5b5d162310c34e8c9d3bb90bdfcb246bf75d83e1d0aca2bd02d21fb017eda76c299a2434f69952a3713436ce9c9db76734db9e7a99f5922ab19d82c4f2b50072612459fb90ba015a05d03307a8a63579c66962741848005771b03809b92b0b371a05aa0d8728fd0c6ed94b5344cfbebf05fcc90a32c770e158500d6a0b82eddb2cbbe03171780ae10a0784c7ac14bdb172eaa60a8aada04aa9fb5e275de8be7a99b3183c361883a8f2e08bf01193f04e2135ccb5b410a0baeca7599d6d3fc317e4d9e281bc208769d0dbd0245521d09dc6fd682bbf09f4b8c3f226ed5651b46badffa42bf6a55c0146b8b8c63829b80cf817348c63f584aa695653ba32162e93af5d8efce065d158acfdafc82fa2ea0a40b63b5a7a28d7e243d1bd7a186ce0196fcc4654b4b2a6bd40d9065b2b73dcf6bb7877e617ec268d451ec971147fe79acd1823218525dfd214376c15b4d13a8b97b3ff262608a0c3ca833802b94b7232633018e0b98dde2714d1dd2158fe7a56359e7cbd69b3d6fe25c7742695d45bfebb9ccbedea740d22c260f62a8c1b44d88262846f9adf21cd6852d3a2814979b3c057a9631e9c5d4a40799e42df7ca2ec9ccb4a8dfd67561fc19ff6b99650f8a179a47ab79933f7370dc5d1ff17d2d4e68a7986c9d114e4b5efa964e23bb3c3060c01699d280866bdcc80a1fce561af9bdbea940e8efb82114451a14bd7aa7058b05b38d288b1ddadd7c5d5ca74e2d585f0a1746861794130dab2c313961b36fa6dcfedce53cfd52445405f62355137bf7b9ed74dad7d906dcf06b5b4dd02d2fc50351044c2f3018c69cf5294cf0bfd242c3b8e3534ce607"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6a6d4649d071e4e7e6b7f92e0ae0245cd259fef7a66ea3f7da62e9b9de72611b","proof":"d4fa9a23e940e5596337192ef5ad7c1fb210d02c3ae0d42c682c58519addaf7664614a22ea44c30526a83f62d1aa8c7e17d1d5012a7455c8d35cf3804abe1630dc7c1ee1e91ffb643cd963f50e0461a2a5eb2f38ddbaf2da1424be8b1fc1ee6b6eb1ecd64752d448be693b7ee2f7b72f1d542b2d926c9f6a3ce37f466ce3127f00bf3b3aad1c1f78dc4b069bfff167c2cb7cfa842701b9e60fbb415ed992b40ee6958e7f19a6cd05b106672d52ab7ef47d45f22184cf999b3138b7aefcf22d06cbcf6ace29616b1baf8922fdda2777bb18b34e034e97b19f952a7f84433d190d0e4a1707df5d8b00cf93cf4f12df64d53bf1a38431f0289c422fd59a7d6eab7f2ea3e9af42e14df8e0170e985f2b60dc5cf9c3553a34edbb78c75f124ab8f011e65d4b1ccf96c0f6d385289b4dfcfbbc8b19154018905576abe8121e878ee901cc83ca33da040f86e2e8e9a58d95d2457bdbd37fb5951e63c1825e182416660e32a0c8883976013d5eff7ed1decdf60d8baf82154b7cd68fbf37ebcac8603e495a4bca082ec3ac80c1e8de9ba4bce0e5b62a221e3e903458661f4fa973040160fa58ba1206dc9670c3dd3581a431acc6110d01ea3dec475cec1316cde7561a1d06638c81caea589bbc0d5427c830cb8c8b0a27079ce47d6bf14b0bad1d7cee1a888289c7405d44ba55c11750d24663bedc50766791b2c5d959948425a6eded7ac80e17580133b733c2283315971d3b1d0227df35dd879d261e2f4a6c9423e91c66e684bc335eb3310f9025741bac3b6ce324512209ffba26a7b0225fecfc914e24c41fca5b01ccda5308ab6d0e1bb7587ab80b9c935a0d2e3e9da3404f66183b2b00acc30b80c74d0641d89e52ee0f88b6a17572e5ad39a5a4e1d32815affd0e1bebafe7ca53deb5bc2c2297cbcdc26e5598d0bc89e51f515213f3739093de06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"28f51df39d66c14225a021542e8b663df975ed46607a1c55ff4d32bf7030d746","proof":"2c6a461dbbdafa1ef723a4c78c439cb18193964cb85b69ab1427f25e2829f62864523ba0aca830f1a6d52e8716dd34c2ce51f4ae9db9d5ed0dab05746201d762d6341ac16a21e68fceded17615482b490c045ef76b82e1dc2158b2623abb285352e026f1604e5f61fb5ab3ef05571ac3a16185838f13075f1173c45d97d8e24892cddb06e0b0c0500e07ae17a524663816baa9e1b5c9b899faa8848c5544ed080ed979d93536adbc46d81565ac6adaabba257aa35a50558fc5c992836081e60ea0959d2e78b2a62c6aadcc7624470562af6d3359367505237fd7272e90f1b205a4c69398197be8decbd875bb36d8fa39ad33660993785036a978395e919cd224bc10a0e1fe83555e4b0e54d7c0e4d4a223444363fc198ca0d796917975c0b916dc239cc3b00504ad7f34cc3c258721b066366740032ce88b75877233994752244681badff45a1928ce0114169ff064e72ec6d292beebf4fe00621f8188131e4320d008e5ac7fb5c0570b4d2bb80079d115ed8c02b9b0945961b5881d570da90d828602dc67446002fed75ff162cfb07eed8d05fa6bef1128a6832c8f380fea48e2df548fdc2cdc30b479264082268588e1d17a93f685df9485c3b836a1b75a214a57259e680d9f8d564d742415e94b7ba3554b6276a983a7a2341ecbcf2ede3c12f2eef75dd7919353a4faa6cda036ccb84694e841145502871980e2236abf573057ef4a1b91054957792139b2248fef03e714236c2926380efb3717786e077a40016177369880fa4eaa3491534e425e4305fa4fc1b11be829bfdbcad5abc37e54e8111b7c53f97f6cc9be309ac6358c7d4b9d4f41b4aa6728144880c7f0ba24008df0f0c3ecb4ec370bfe19b9cb736203c0b0d3cc8d65d404fbfea5ce0fc90b8e15dc1fef6df33b7e4876e3d6ae47bf196449fbb54b7a5ff7690acede167a03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"58520f969d3e17c71e5f60718f1c6ebc79731591ba53437bc295d294cd4c4b0a","proof":"36c3df446061f2f57ab0be5682e0bb84002f559fdaebd16470acd5d9640fbd2c3eb05deb75c39133f08cb4a24268055313231c416830c8b626539c2877f87869c281262b4d6993736356031f874bdf24a6ea1bf21c174c255f1e28582c577c1dba9a32594688ba38482fe363a01b7750e39549766382794da75aa29dc5e96673e6bf84907061c35296e0e23b731a965b3b427e08121b6b944c78fd6c5b6812080b1c96875c6012fbd10635316ee43c021aa0ce343cdedc493b225b538d48770c9204979f5e7670c56f8bbf95feca5e896f421d4526705f54024eaf2fdb5c5409ac7a35adee89457235fbf1c7c40e4c3816ed3a5abc5c9f9ca35678289664bf2fa463ea580f24d61230d09f83691e10dcca7323efa45bc8ca0d8b452280c74b49f4d979b8dd9b10c82d4ea00ac3aeafc4b48f286582425b914b79a509fb97f8217222f3b23353381d80cd75d664f31171b7b467ad6fa24260a9d895454edb9c7980471d56cd0798cdf4227c1fd30e467d99ca4a3ab0d0fa0ec26e747f5499407d2cbdc8d770ffff082cde87fdb7e840530a3e4f8a727d492a565ae2d856f6cb30fcf93e868782990278059833d8075f1a655c6f96e25762c6c7ea8b60a73d3555f62dd8b886df640596551d1733333d1a41e618edd20f52af14f5193dd5793f65b68cafb1e3a2dcd8131959d27cefc22a7b8f5a251a2aa90986321cdb9b0a9300308905f7fda540f42f0c9f21f98d2abd03f2f37c341acb051f47631b1ddc5c7a62b08073e3cabddd0ff0f472a3b468f235edb42441e5bbaba0bde99aa8b0870f2aa88eb8884a990871d6638b783f29f6af923dde8a07703f6eea55e58c7d00008dec449b808f6642bd1aa041e9c729b16e42fcd321b3596d11395fc097de140a972cb72079cf10e6c4948479d473826a8f6271869064c92390057052f6e3260a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"da1e8e4a9ac212eb63c73ecb446f95efc11cbe6c8bdfea1ab1f80c6ba1611163","proof":"62864026baea8f2c888989a08caa5b3c704f303b125e8c7d98107801d89ad8375a685a80a32978d209277e0332c3d3b5f0efac0e97d25c9494afba94c367ae44ba0b0033136ea539e66a24702338aebf825827ebcb8b0e86b8173b1a7582ab0c5ef51c37f536f4e0d2f546dab8ae22527671c3d8a4d4c66977d7c9397b553a4aa08020c04d178fd06bb4420b2532085ae6a77fd28e2c5b2d0784cfa8ebbf46071e6b753ddbf4e648d7476a3bad8f2ae89282733f3265e14df239b3fead3d22073cfebc535ed6e9c809c17f74e7324bdc4abcd02aca11e7f8d28989e79f071c0bcef8eebc88c624f41a254b7115ef34c3cf1c66f26348cc85614f2c589e371a330c98d5cab1451f3fd3f1fffdcc72cfaff620cd4a3f92e2ceb45720281681f47970d2e5afd78789bd33d60ea2aa382069256c5c86d5a5ce03064b5b34c44b3d128a870014298b4c0aba26928c5ecea3a3d4f4c717081898e0773eacc84d521349ee5107177f2d7e76195d5452707914478a123e027c432dd9e3f54638c4990c44c46748f40243d55f9e00be38d1b7b222dd2dbb53e22aab635546795ba0997b22a620a1bb241a6775e30bf5e8070de472680ba12d380c6a86797226399195fd7bb0ab7975e2bd047ec4105256339432963d9e68ef69a24a09a68834d72b5e57516a24ea7d3a672fbadcb42d6b19aff7ef317d572d90879fbd5a0ed7d1180c8f0970e9cf4383d20d62018ffe98cce540bcbea13f2f5cdac5ab541490bfb769d9003a65de236c7964c3826691b0990c54dcd13e545b6a015b32588cee23c3961740f0d117265bbba42ab979f6c135fc009369e5d9cb1211978fbf2f4a3550c4fd30847362111cdfe059a38d2a56a22348009d9d8054038ebbe601820c64aff7f10476bcc3ed6165fa18551dd1db26f4d2352ce549e285d579ebbb9b397fda637d0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"44688070be26d70f008d7b8d2abfb7c5732c6a21655b4d6cd881e41ef6015a73","proof":"46e50e0883dc722da784d3c76c370877096fcda3096445c00a4bbcb2e4c5b65514e7132b6ffa8d3c17c60c849adf778b3d2ffb11b154b8f6ddb06cd13197d41406607f9b4848e5dd628512e0d1886b5552a90ec17822309dfc0281ebeb9ecf17ec48b24c96bbeb9a02256fb7e42cf52109fc2569559aca777b8de4585aedbd1f4964feee4ca7480cc61c094b611e2fcc627d41cfa48491d6f618e8ab76050c0ae5b12de8f1e84d88cce5869726208f64f810d40f86933fdcdcf571d09f44a5094a0eac0681977818b8310ebd72aa3a0f7ee099697768c9508edeab3b62535d007e557552e1d26cc432eaf9b32cf9fcc5478f1dd2246c10be4fd92ed1a3494a22f28739555bbe0913ea92e7948f5e8158b27a77737c367d9f34f43002b171810d5a9cf27ec9e1e71efc5103f8b5e1bb04054a7ab5293f4b22f788bfdd486eb724bc4d60e71843062a9d738e394aa48073c77d66318e134fff5c87158e8f41b93e9c09153b10a4941f4eea64c3e96c1e946ebe30ac976a29ca33fb4cf6b8cb6e36be66b0d2563ee183dadea352962bc410ab8daccd27d14f9fbf5ec8f87524016f1279c2c2eb001780d9aa2647ca4561e5d03580edd36031d4685e1074908a8f63181e8c2d05ca39def7b0fc7e7e930e875e3841019d05b74e70ff1717199fa2611ede8e94b317b388f7b228e9dd66bccaa80ccbc39a3fc2df3349bef8397a9047643cd37f31e7c8dd61ad1353d1b81e79abae79f224cfb0d07ad6acafaf755d68361898c77a5587d93fc9ab8e7f7a4585a0753efa26f51eb4daa38b8207272e1906d6b555f17da7ea66a1101044a299bb845a2094b807932662fdf3006141d808d63f553dd8d217ce1610b3120012c3f1f107cf346e1b5d3891868e91b30d4c0edaedebfe3fbd8a1dbb538d04a0e6305c6df812fbae90f51ea1560669f1aa3b08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"660bb3cf3539465ce71dbc9cab046362edb0894480795e76d04c0da92ecf0e69","proof":"66d82b9a93208f009a25fee619ddbd188f2fe6809ad6e6acf3eafd1e81ef3b6ad8465e33875016ab7acbad2e81bb71eb5d43c58bafe507c3830d17903f55d1389e9ded7e401ee872cdbb8c1cbed27c509fb915fd39d8486977bb168bb91b3011840fa4fe6b75af6c78f0eec02e48fe5062d904b5651e84054d41a23ee7cf526504aaff89fa140e4b23803922b1b831545a607ba0ee6e72ebc62f5d7fb422ae0b88dd2192fb1bec297ac8b6c8ec144b87bfd23d0f5308b678d6abe83d9be3330652a55aed8072f8b099d55c1305ef0b06f25c0dbf246ba08f245c67b3af393f05ec4ba3be1a01cc9a5a70d83e3f79f0fe4bdfdb82ecb05f5df65374cd0a639a3fa0ab215b619cb129367b79bfc456173e20ce4a5d69fb628f5533fbd7bff8af29ba665b33e3cf7041e7cd6170818b74a305683ae500e4ccb5ff2c65b5a81d047a303bceb8da50d59d03f05a598018f4762c94591ebf89d0b658a485091ae4906cccd4af3a58129b29735347f1e159c5043cd5d46e7deeb6e55947cd0ab96a027096609576cee45a1fa9534ec93efbe2c2b96a2c5928b1877baee9679903806971f4c628b993d89cf6a3272a8096c6251662398060554281aa88204d4f33d9675130f81cba8afb4fa6e11947c09296c43722880d310496f9e5fa89e034e3a64d08b6a1ebd618acb07aaecc543166a446e95f9d16fac5dc6e4763124002a73751351a56f21bcc2b6330910ff8b522502ba7ad5e4a1c175e8f4051b5e7d2e9d3c347563f5f2ca4dacaad93441cdd885751741267cb4ef70c717dac47c1c7cb8253163c64150b524c7a8d4d7e76ef37321d4b293d3be76961716d9b4f1415aebd3f3e42aba564646b927d43a47aa93c519c8c64ff0707dadc9addecaf50db51af5f0a1b3a77c5749131ce299f2ec633557ae9ecb8f26cbce53c69678db5a9fea9b206"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d023f9d3f0271fcaf8e3067bcc4335f4a711109fd5a53c181077b52de2471d0e","proof":"ee241f5827022cd9c6e4f1bd5727a31587361eba4a71c4a5e5c73a69c2520a6338e4d3b679519f0b59fc5fba29b6dae9e20782507db7b06992975e665b022d767a1bc023fd757e378220ec50aabcc392204409199c356fa97d075cc08c6334428cd92a104423f91f45556f7530179af2e5cf80c87b5fee7c0f5a94c9ff7159097a56d2ccd98b2f527169ad7b9361fa30eaa7b82257fb58329b9bfb5a63a9b207a49c745d9518711944ae071747521ef69194b715c37ebc56dbe4b768fe203f08a8df411f6adc68a8af3d6b2982e4bad67d7779f43d2f53af37b7f923bb14bd01342355a51f489fa9303408738de59b7220bfd1e53be096982496fba27aacbd1f524a59f71357321bc2718dae32e3911871022d2e26f284069a4d7e80ac4b1d493aaa004caa83c3c3e11917767564e792998c6e4ac168ae7cab4957a85943685e7afcfe1089dbde97384309b0a0ab4d383808937776157b685a62a5f395b7ef37a0cc47e061c992f745a2804d632af0b9d14ed6c56e4cbc2f414aa258bcfe2e4d32c40a0903d5fde2b3a279e167f11157bcd7ec172fdba6d06d52b45af346dd6b94602aeab196904f1c4ab3efa783ab6e3d40da6d7948ca638827cfdfbc1de6687663624a0fa8be83c699a461f32899624caad9f73665d90cf8548e59eacf883180dca48570062eb4ce1961ef4fea3eec49bb49ea6598673f6b820b3b00e39007fc2e7dd551828c8c64b5e0ca9a0f66e9a97c79d775d134d7c0979cc55a6fbf1f1aa4c33ca07f147a0b8cbd5dae1163d808c2214935c6d12a36f70301be87875934708e33fe75274d795823e45b1b6f2489a8519dfa30bf91d7a7f406291e6845d7bf18b417f67f0bf6cead88532336554e2033079c2d5e70ee9f93a0ea5529025b79167c883c796e1d075e285f062227bf8104259ba695abf4a44e694d778a0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"601d9bc931f9fae0e4e79623d1702f64a51b8393ad7ea23f3b6470f7d6b6b045","proof":"9a3ff564ff63406d1b7c08cf81b26e43938ac6f0a8cee5a22c7e45b5731674360ed56076de9f0d0da4c19d5e87be043fff89a8ae0cf67dff911ab8275acd841fe694b4cd517b5b8e32beea2b253bbd1d1f572660cf2ff613c577dbb819b5eb26ca07b449af66106bf0de34ce695b2c5d242af39f23f89f6f35b7fb103481ab376e11c31cffb750545a2198c0533b42ba207b0a29383e715b1f5cccdea8b0230a89f779d470d43186b6344eb86b042db032d872ec79af1684dc6041f38451f00248e1ab720887df3d8d038cc58df6d9885e7a341572642bada47c81f21fdcfb02a4a164f8d100e980eee13a60f901d24821a83cd8b7f1fe6b5623d5c1d19a1f2798232eda933a8ca0d5f6383a8acb8de91f1bc9deceeacd572be8325072eb8a69a2b9b8545418cfa19f60b875bd07d0b9ac09c239d1acecd17fc2c7a1a3f0782f32b2d9bdac6ef6bdbf335f45efc0846c81333b791064847cc9f087346c971250108f73e79063dea02163886c5d5572369f24598cde1713077a4827d7111de134b408732200a303a107f2c07179a58d95a9ba7a12d8b390065166ecbd63dbc93d6c6b429ee934caf97cd72df47fe7595487c8da1ede81916a4a4cd494e04d7825120bd1ffcd6572e8e2d641d9fe2ffa3bdfb27bea6116088260eeb4ca7b377e2978a1231f01935f974f93a84f987b2b87620509da4333231cd8bc7d9db805353a4a458a7ddcd4edc11fa5c284ae48213d3e14186215cfd6c0148ffa8e72a6871630bd200f72eae6da2b8da29b57332c5aa5fe069b3b4a1fced5b17d6647a9c36a524382e1512808e65c4a5f020edd0f3510d31a95d427ce9d303d41b4f6c8ef53b6df156a510f0024754c8e601440fade2754ccea5a50515a44b3e5cec71dc00eca4a531d868673deecd48b5aaa4b2beaa5217b497d82a3a83bf746cf87931c02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2432800924077d3a1c15ea7fdb9b0240daf30d29f06f8861396bccc354d0fc76","proof":"fcd76c8407eac2bfe222e0568749db570e850d947211d43e64e9d2199be6cb426875d0560a1b1192a140a055d3ed9820fc771564df90dfbc86c4c0d0b16ea735fc16bf1808cc1c7f0da9e9f3d11fdf695f8e589ec6995533c8474c6056a166181c92f5b36d40c8ed75ea4235186e2daa3962888bc5db7fef2f04dd8053d73c693a1fce99e5d543adb9d54d13e5fe5854c04d4f90206f3605be1030801288590f63aba35599aa5ca3fbe8f4f7a053df1b2810d9d97f4fd0c92036d65d3b19700285ebd01c379bb3591018a72499806a034902cdf538e16d3fbd9a99e46bac9b0096ff4163afc8be5037f7b929e113082461fba5815c67b3f51943c2a776e4bb64b63c3bf7e06788bac6bab4cccd8875947aa8c29b550d4c88a793103913691a22f253bc069c57a58a15d40bc4afffb74e7ecd587ed0e27843aaa5c588a3665e5598efab096b31d99b6c08fd34e827d14883e9119376eaa366cf9e9422827a075e7e24e03b6ce9fbf65a54f9445eca357a0e1ef8fc77a6e3534abb2fe64aece83d12663e36bd9ad46d9a25545d951f242f38b4ea86361910f18f6a253519aeb53078a4dd85d6bed4279d506588f33da6530d1fc9e19fa1cea5d5a15a7e47a4bf651e4dd37be0353f8e2468d13be9e79d54b13fcdf3e99c0780467ad430d2a8b24444e95301d7e042839e5be27ea0d41d768689483e7968d5f9050019ad420cb331e8ba409aee05e14f9ff46b61a4da6af384677173d51525a32936674478271255067c2c50b87408999fec0a71dd8d0955476f27dbd820b7b36b2881f4f181fb151c9d2ff14dceab4f38eeed546c67ae3722c2d5d295bdb624da3d0e9c2fa6081e9c14c1d56cac0a4b40122419fdce0081216a431e9dc03b91b741da1adffbf40acab3e0f9016f4c2ab0b3a0aab4f98d41689a29b9f75a69d346413a634bad1a04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2c5a753f8a3b56aa64d7f2230aaef41ba83ca94f02ec1f7efad539a8d9c92324","proof":"5e26f7ae7cd783c669a1724ae79a5122103324e8d541bd22442528b7c74a6007941a665871297c58ec5cbec2821d746303550b8f5aed06b960b8fa90f5057731c6fa9997169fc78577226090447ba4724bc38b3a4885ad8d28ced44cb5337b0730f6672e6c48581c86b368eedb7f30e0a7d04fec820a6cdf7fbcfecd7189aa13317cb1f7061f9af501ef7c220d379d4c3d157f5655fe2e6e28e07949bd06380b69aea585cc915c223961784c5ac78a433cf2d81f53a8063b0f40f37f4ed259086d4da99090da6e1501e35229c4bd80f23a5175489488b85d264b9ff779a19c03d2f99f5e246c328a0f71966805262fb5f20e20bed651e50c47cafd726b6ed071aa7f47298a2c51c6fbde0d9b24ebc808e94a2608ceea381254f66c043328250428eb1235581243015691d39a4743fc85c4172d92c90c0d10412fb1a59d854a7a8874a787191be9666edbce2803a96a719e31dd81c87b07e72203b749a4c4597a62e5bd36641f48a65d8ace9b83aed75d09f97107d19b57e097c9f5ba8d1fb966e44577a4e8aac44aa1fe311be21e34aa49a0941601efe02363b845625858136f6aa815de3c51ca5b3862355b97283da151e2c8dbb0fcec40127bcf9dc0207032c88b21cd666186b4e57b86a0b90dbeb7d24dd593c4f9c16092bee833e7cf5e770848aa40e4aacf5fa524304dd65301e4da2ac10578567725b73310b94ceab371e213c22cc3b3c00ad4acde1b71e213c1d561e564d025256a58b916f2a770ef010015028ef10ba246d1b95c6c84fd3f22113b60a7694ad6f65e190c938845a35ee4194f9ff61af2c4f13e4e101de6de2f7fdcc734dc11c8ef98d734bfdc95ba1cb392799a84bfdc058ed62dab74f1ee091ae3d1d0418447453172f2c62d838900d908a7a72b67b575687dfca5d4c5c1e773f6d014196c55c2e256fa3a7267de05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bae98e39aaed04c0cda9924e804e538c863a44e779efef4d5aa2a3b306158720","proof":"eaba6cffadce7887a066f93b2eaa60f68dd0cee8982b04e335baed53a3a59644c4b3003b05611bb2be226e809bc75c4390ff79550fae6e64a6912861a447e46d58be6ef782c51edd746a7f262a4817e13575c107389c32a9357b634a0309b654548f1fe3a338b2de6069e3a91a8325f93eb3eb049c0143b349bd670216cd8e0894b3810a2375946ae6fb9f5c737787390f5a479ecf6160506e840872519e4502aa548e7adc8c82294cc6a339580f04a48933e91e236860021d8437ea83a69b03293dedd10b3a09710dedf79bfe119e89e7c0e2459d303706f348a2bd23df0e05ee03fccfbbeb676c0ce429b67651f5eddd20c98f4f42ecaa0779393c1957b37eda90ffeb14f68537d5b3ecaacbe920dc3b8b9867a09ebc22bfd9bab5d633f9212e819b7f575b945afd0b88f2af12f1f2f806abcca17864252ac5799ff438f35ec4724e207e1a901e7882e1d1528521acf826e61b39023954e93298902f3cc478f8135bee7ae3a696d842998c3c7c995d9738fca1bcc1b9a8a78ff5ca4eaafa148a9a83eaf38f473ce00b6bd66b7dad34a0010f82ec7b821c1782876722b64242885c68fcb112d3d58d4ec7659d9038e40082a7a543a72303dac7e07087e78c295eb837123289ee5af1ec33c83c23145daf7e94bef5a337325d5b21f3ed19f45420bec3e2593e43b4ee9f7317c968db10625cc2dc37b299b91a45991fb9c4ec0ab8059e1b22fa3db0d325f484ff086773b16863fe7b95a0e7f8adf9fac03b2610a8d0bcab72a44f1829c204244502c572f8c9a271d6aebeee9e4da2a967fef07e9ea2c367f74ea47d5d04664c5123e867a1bcb4ffe49ff99b3edcedc0721bc37ca2ff658e6c3db98602cc29137da6c779f1ab3b7f6e75da20b22fc9d37201840af3272ea0c0228c2b853130f2e24c8d7399bb65680c8069dd7d91f30034386f06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9ce87e825871c3f79dc22d2129d6a398ad86e5f432f2ca6265a292e60671a851","proof":"c8a8d39b06dc9ec2d9698e2748e0282b4855277b1e2fc607e8bb339a95621272867aeb8b4e2eff33b1841cb8d1b0b79ddf9e71259b213f04e0efe5ceb62a98073a71cd1fae4845a743dba188caf84acdab754941542edd6787842dda1a4c38563af90146b0b274ce660173fc5163bc291d3e7278be2f2d3d05fbb9199d10296c002ad86c1528acb37b86231c4b2f08d15716f454ee39b6c1e828300f68cbb30e0d37769e7d40973c44af000284a5b7f3b5032a4687799b664d044df03979e806fd40e533805d6c6e3931b4d1e8b62ba9068743997ce9be808ce6bac4db913c0e4a935d266799b3e0b0b843b56ca097fd33bfa98f56ab3380b5bea938adecc277e2a9b4cfb362ac3ffd98a09763f15cf60ca9da2a4bbaa28e87a2254b4878f845a6fd59c7ff7c965bb21c19db20151986d8f666956077456cb2cb45b2d7049b64ca624f25e1328658ed47992fa9f8e8ed0a5af5afef2d3f759c01bf5ab0ae5a1a1ca6f5ec02c8b625efc885c8aa03b0304f236d6bc431b0b798df4bdd6f2a77567cb3e321ed182ea10a2bb5f2934432e47a5ee3826f0b0adb495b429c856ada00b43c92cc6360e049d325ad06fd2b1559e4565398bcd9fe416fc44bdefd5e207d782412845ed6469d5b5676fc47740ab31e499737eb46a1c99c284804cb639d7fdc2758d7ac5ab1a4d02bd20b01d824dcf7ea6dd1b6f2b4a36758faad003f572b0cd2c4df14b1811be789231afc35f963e3efe61326667fe0784a78a8df433e581a6a9285b7be1da83cdec95d4b33e25ffb12509995d6181e14376322d2d5950d044387f1477185e2ae93ceb48c81dc161c3040b9cc832892bef217bb602783014f46e2276c475aaab131f67243504915656e03b979a8d7b19cb598f9d270b4051d5ffcb5ca5a2c25a9065226c977be6a47684c948e7c6df7e7b77e4d9cee760f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e09225d0541377906769d2257877f1924816a8945d1dcc74b7b7903e68b1fc62","proof":"06c91bf8f45ef02ab2b0c686a7c3176bf58f65548f5c8a72815f083bb7904214907bdb949ddff12fa212d2aa83a1e563e6fd42acc6a281e300d60f136dbbc67b42d8729f3dcd003ad368889f6990c53df20ebd0d687dd9a057c2accad15e991ee6f6cd837c26f076a7ed5d3245364703fd7e44a42efafc9d546f5b454ef82d411d282e7e7b5778c78c61e0cdf45a6f208b68ed7baf7f140787c1626376c9bf02ca624afcfaa84da9495b018d2ec247ddc7342cf5fe7518fef46112fb8693b70731e05f723d5112075dcd486e2c7b8b43829a5d266f9b65df205f625055863c01b8e0455fe82988c118634e7eb7b57b0b0f505d2e13badd962a7df10ed0597e3e0ea87e78e080ab70f82468b74db215b75055f05a1d06cc67588cd7283ed62b7dc4cf3c33d53cc41ce76a9909f2f0ed7014be9e3142d059e9e5f96d6830de36016ada243b3520ddc411525c111e60baa84ec2507e9cea4ca84107c522e3191f61824496613f47659fc0f888d25023eee034fb7c5c2bec823040cfd64820ee3b3a70afc1fef3b0c20e9312011f17feb7f99cfb54a8e8f526e154a5c98898effe51180402c675e212b52f48e8145b4a03faa04de85c360820dec50281b684eaf93d6a6c9f3e7e63c0612b5228f9b0d2635a77d91f0a24ae5d2187e10a5e221b2127f476cd1aca342d7415666d43723d552a94193bf3d6b2e9a806fb500f8212ff6f8283e34331b158f33df6866e8c6102efca45907bce32e50a6289d230b72f4706682db2ebf880149e7e0f5b11ed8b8bef4ca8d97b3bb119bc642a0849822e6d1550be9548151a7158c3d8c4e7b3a766003cee5486f252717ac93c40f276c8ee2301f2ad777e17dae0fea713f4a553f3656121801c5f1cdeb847b432bd95b51d0609fd7eb466c4aafc0a9051354db7504e84b976daf53f1b2edb26500274d5250a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d624f1ca7f340e48a0411a5465335f432a85b8869624250a8e8c3d5c6542c757","proof":"78c4805728697fc080c476b5a1bfb007677c8337fc4cf2c89444b842ac61f4262a9286d02766b7d37033ba23365d3f3aa83b1063d5624cebe19856d6f5018e28e2680c468856c6c271b0eb69a6e320622ff37684339bd7f9d29cd2210e3d3b544a9124e5019a06b6a56c93c4ffd0aeb7ba35fd10acffa6974c2c5ca86272282f2817a02a1766581857ad898d23ca8fd962be2d60373a3bfffc234ed9fff0210bc28cf2426b2cbffc3cb5ebf092c659f616051e0c7e861df3a847e2a41bce5500ec881dfade1da3f3b06eeb3ee2fe999e0112433d45010426419153260d61fb0e22f01cac720e0bf5f5e93777860b0f21fc96c41aaf6fff3934194971230db91f6c9d05ed65ede8d6d2ee9d7925cad2b6762786cde1ea216327648839f18e4d0ace8994fe61d08462122e2de685969ee08834d97b1d0da7ce06ffe33e6d47b11d9a00e8cc5bd2399033527c2e92b9fa063d31c93ef42b1c440714bce146275f7376bfdfdf3203da03601925f009cbe2778dcd2b7af104064cc0e4dbc06035492264e9129b07abf3d0a6994e690acdd7134a5907ff780cf9206d38158287d3026f18a3cfcae6e42065d03a33c61d0dcd691a238e433e2d7df9b43c5079e5a6340fc8705687307b2eb13a5b3cca65d0f115aea54fe53d8db24204d32da9562b4c1e66befcdedaf202fd8f642b2e908af1ec4b760fde758ad6d6a54822f05d733a0898600b7595963f2f8ccbeb90f44cd1a5baf840bd92c1a1d9651d57b2d25e6f4d3a7f16a6e332f592a28b9869c2289efb6fce91a3667ff536fd6deeb2268c0f5864684b5db4b83f74b2e76b91d47dc8de3bbbbf63c8712a0bed09e1dca4f997464cd374567989a5dd18a2c844c011e0fc9708c9e4e01c30505536246b084337030d725ba94461b59b27f30f53e8299b72a05931f18630c0a41e26d1c2ed8ba704"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"44daff162fa792b5629b975cfb9ca46c4b135660c37aedff15a8b5706299b654","proof":"06bf91e25467d60f0e9be03096634b0585d9736f5838afd749056a5cdd00c4126207d9028f554ac650c96d3cd478182c73748bf989c2ed869751d8758ef4145c90228a20df08383f2d65cbc666ffd30a01d18f75cf60c89e5f728e9567665442e400a3b00ccd21873bdbae9f85cae3ff5fac3a5410130d2b8325e5c59ab1262665f584a4b68af8ff92ac4dcd7bc17e84bb44f38d2d5739effdd707d9e4a4f1073d45ecbfbbe7b229ebe1fce48e9394eb4143858fc9d52fe8e8e0a5c5105b4608c5bd7dd9239814cb6a4cfe2fc5673dba65d08373682fa8bbe68eca470c6d9a0d5633aa38bd149ca589054258422b8a7a6a860059d10bb9e199b95f42c49c7c562087078b9b42dd9527f84901b4231d60539c3a46c20d20128fb7d4c1865a43158e698687586868f140d20dcd9cd8f6c713793aae552df90eb950c56d10f42302ce0a42646df7b27844a32e008eb568b0cf44656c64d5714de4b3b8e424ab360f28d757ea9f8e6659653a9fac4beee047104c01267bf6897079af7ff8225f2002cee3ce7a2a70ead605b996b4596565d99042e6034b4a81a373734c4472948b55d0236681d34de35d084a137a4813f9836892f7bfb43c6e869568529cc5299537e2624bc1ff60a17c163af326aa056d8eeeb1dad04d28562ced2163795b4c8378e40901798af221b729aeca5d0669a02e7dc2c83cb856fba30f2803d229a17c0f4a6e0d45e3312d15dec749193a743ecca7adca8361a863a23c1267051a62fb7f3052cd05c589d17ec60ca6905afee50e605fc950df5cc5763c4fee79f0732d62b217ffa6593facfc7926606f374cbc64fae1d217d6831c0573b617dd1eaaa952fa452d55f22f92fbd19e639c6c5982762bd9509b993f3757189c401290c0930a7c0ec38439546dc28a516597d51b0f9e4b6bff93aef3d859ba4a267c3da5910e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7e1d19cfcec5a1e8e8ff849736fec859fc22e35817e2d356a62c99c51667e83c","proof":"b882e73f7a6f44a76c970c31a59c0005d998b8c182e547faa96f40f3b8e05a1e42ffa433ae5ac2ca807bec86f78a29577b00ac3d109ec0058e6272ada5702d2948b1a297fbeed9d38e01c4e5196e3306b074849e8973e626fa063bdc9918961b7c2c3592fabf051dab00c6a604b59207741e28e44c72b2af936625206f21132ffbdc69c7094e7b497b3c8fab4cc98d28bacabd185378b434efb115edad4dfd0c16866909e82deffbcf6bcd22b9de1e244c9060b8664088a93d661778fc950d0c0cce7d7bc57b3cba229009f256027c007f656ddc7cd89aaf80de30a8e439c10e0839f74736f85a51e76e2cf6f43b01d787d4856926a4367ac8cc6d7272abae35b2b75ea61b09848ded3cedb127ad42cc24146c1213f808fabd4d384d995f387f8898c0bd5476ce723a2701e11bc1fcccb835ae2afd23dd3cbd32ca6a217d7d0b4e88fcfc773627d16624198703130f5adf437814063f1f280ca49a3ffae2e42ef4d94dabd561cd665adad6d35d62d310eba6014203d3eb778c400f995cd29037320a0d8554f21f55db0237279ccbcf3968156a5c56b81c9d20ea7f214a823a6a72597379da2e9d6119ff1b8cc17b3951872a9b29358efb596e3a0a5d78f0ca1cfed1cd8f5880ba08920e710f72f2f595d8d42f0b5ee89097be0e95dc8b2a8231acebc4513755bd1c9e4d848ef5ad3933fa38632fd97ac3580f3ab726949b313b2288753ea673628e4a4f93aa5780d8e044c12c92c0c9bec17302595f0270f2428e0abb41a04aa116d8a29e0a426636d993bc8266e19fbb020b306066b5fb3c7eb478704cd2476644138058516f42c85d019b6ace4883078f04eb3f31e143e84f5ecedebcd63ae179a741508a10b40696c51936342910d439daf7409d27ce2e06a6645bda8b753cfcab974e4345a9ed040945880ce91b89f89fc651b95f4f2106"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ccaad0f3481d4a93b400fe426066875d3c9554670ba4afd534edbfaa8fb5a217","proof":"72d4acb55c2ca4958f5944fd3f36293cd6126f80d4077822e0607a59f93b4b3ad86b2d295ad62cfe8efdf284cb3f5e64363c09053a50e92f7ca9e4e28c17e33f44953816b53627d066ffffe3a4a512c0acca828f1b04862a51a91c747c89436e861ab10f0e312e49e589fc17d6c2e831e10f2fc92e28eaecd167fc9436dc7b1af090175c363a365e505e1e94831ceb9cfee78bda182599173c41074d8043bc00b593415e730089b1f9eb16212f2b7320e25a62a450ff29b6b8ccb7d309a8d200e3784cbb89439eb89e81bd233e5759f8659f1300a9e5db38b63e7a6455ec2c0f5a6e9e81d84a2150e9ff95807286a2e5fc47fba6251d904f25eb253142da3f04be4539dcd37f5fd5d211811e9f353f02783f876b54338b733af9c48be2a3b93c1c632b974a48cf20695c2ba4ebac19b0e2c4bfae052a0e54175ab18a0f491f39b8401a8b690cea38522e1400346c0a9bc41655a86ae542ccff2042d827910a4d52c6ce2bce6c99ae9ad91f53f7d44623619d17d44d956da3f3271e453344977988f31dbe0483b1ae2928da186b8d0bc1efa690a7238e754c3d3ffc39190b996540a5c6a37a147e5683ef97469827cd51246014e196e0288ac9ece68c1f6a883a58bec190bb805bf1c02a20b927f2321fa3e2a9fc713b8bbc2a5068004deab93bbe30625dd2a4f1e80dfa7e0e5e5355efe5d122b97255725b862d6b7c4186562f2ac1be72bfb9e50cf91645d0561227e93b6b17dcda3a0dad8b53e35718d0f25c0896c303964341e8dadaa679343d7154f20695f9cdeed6807d2abfbb5a3c2919e4acc38b9ff01ff91b159f5c2ef884f1470428ece59099fa0959c5db3d05792e46f4b815ec6bf2f19a907839d4a34388c1fa0e289b456bc7a92f3da63ef42106c417a940ca452e2ea9700ec810358f035b682405be12b8b9e42caea374f2da0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e4c4dd03b656d1e5573514bddf862041e6c6bc803856ca47f7bcc03fb04dd63e","proof":"c65990a4001586c4252eee1b07987e87832d15e3ccc30327c0a339c379a95e7f602f016e50e839f1f3a1da4877e1531f282f830eee6be53124df9c45e89da7731afff4424b9e13c06c62fd15ba9ef911d9f17290a44a976c6d7af1b0a626c77a0aa6da50a4548d61671949eec7616c0fb6bb07c02994c2216d2586fb38aa0f2f12c02c00470f7ceee506253ce0e39df6261347cecbb3f0244d99899802f6d80111d4c0da0ebe1e064bfa205e0d673990f14fa9a7e5a61654547d786228359a090697688472ec3237c5b657c40e33321446939ca545ef1305d468a8f55e687b0d06691a528a7ded24e77c394a43ca404d307cc3514b91f4d8ec4b5a50583bb54d2a2b1f3666f5f2c31ba44da483e4b13d046723b13bfba08f3a84a6e460e6dd1eb8fb9951284eccf199cb2ba82155f310a39d1a1a5857e71f721c4e24fae40352e0fe21f02fbfc36c5bb947632ffc2a4ea408212ef8abaa79610d56e252a3d564fe8c201d13327e44fa885ab6372391d5c20ce679b360e279ac5ff4cb4837ac7b260f777836011959188ffc9e5f04c78645701adfc5b4bd2f15d3ce2e6058e66470bd36c2087bc8e66c1455d9040860385f5f2d7e41fd59c5479d77656d6e253900d56709328f886abc08cc8844266dd58d75b43b3f2fe97a643bb2f6b4a3de05ded5354b2b6454c4441b80f689aae183be4db44fdbc1ef0b30d0d4c66145931090f2d5d5f2b0c4bf4a9ff70d7b2ae3e029ca3a62a66ee1b2f4344f706b74e540767a5a1cabe8540db284677cf31dcd9599efa9bd52cd3bdda26491557d00297dcaf660a4f6ed02f43974d032c115c6382a7455ff87727507cb9999277653b22e71ea3a359d3f2b327afb1da48714b8372df43af1b5562f89744f3088c38091070847a63b5e8a6bb2147a6fd5058ebfbd2b51c372d09de3114d3ee85cbb220003"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cac30e7e36373a3ac5b5daa0cde400983b9351411a2183713d460924bfe09216","proof":"44975b6074d579f448f9ffd5145ddacb0fb796a27f908c54ab8995a2dd10b3729c5ffd2b0dfc2048c9224590d128f85d99eb3070871ca54a73a979adb620c811c2a02017dee825282a1155dd27b2ca6841977159f261459679b1f3eb5791cf7a7a515c558db02330b08c3676fcd0b9c6e5ed938ca954783223a7f69252464c607af2d8a607953e8ec07e622b4020ff216941fbde4d9ec1eb640d262e439ffb0509170db1791cd53c9bf44df86aeee1694f0f594815667c97cb51496bcf13f206ddffb5a6883c23a6e918d8855cd539d721b7ce53c84fffbcab23e3f388d255012424794f1e2b6e930c4bbf601cdb3d26bb11f33a91115cb748888cc3be293a5238fedf58d1e4071860f7210fcb5430fc86d324cc5a746419a2297620318ac615967f2e534499c47b4597e797c14f95f57afd5cb6bcf5116699f805e60d893738d000b0601dcaa1ce7ab8a2ff36fa17a6fe0d7a0c5a17e5daae51805843024d5776ff7ca18ef513a0edee26d19319bd9311e9a789a41ed84d4381e1851032bb03f81c4b9f9327f822b39b339f031da9bc9a20ef69ef8748f1fdc023172e1b5b3824b1f2f88adc6f85ac370421084856436e1c263f875bd088a8930c6f4fc06729004f3e11ad37a6c58e4cc1b457a5f5a3e8a85c1d63246e9396e8e66f2f9c4c25609d0cc63a8e55ec9f1ff0f70fb045c847a5605a230d8123005a9ae2ea8b861cf25f383af67fad6aa3ecd18bcfc7f8a16130719256a9b8757c194f998bf5dd392088cc0f1de04db093ea30934d08aa7bd808bf51e7e132f8683cdb0447d78263164b21e50b5f8dece772193df26de5bd087ca939801a7e810821c9071c96da7cc1af82c441de152948fc74bddaef839ef094eaac76f188df986f01fd6052af0df8c35a5b500f56a1a4bfdbf6a7a989fab0243dc177a2dba3de179c5059395f02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e03abf44bd3780a058c054b58855c3e8e86b1532a1c5d1f6de68d8af1f49f71f","proof":"843ea9b6065d355b93496d5c6185c223ffbcf79ce0679913f552f1e5a60c3a23baf1c48b66974abeb96c3d93df1f8b4ad01f808efa86733aaf6db870e7aca7446c980312b14084f747ce0a2a46056ab96c83ece335bf6352685e7bc1e94bbe5036228110270252f94442bdc736abd624bd10a12a1a45dbbbc0e1172883ff800ef6ec9b6204d2d8e319098ba23a0ba982dbd1ab636633bba548611253a2a87c095b8b0e47495bfcb97b563d89825a6966278b314faf8ac30476c1ca691ea9290545b7992dae7188b08c46221209bf06e39ae1895fabd80889079dcdfef7192007864a36b0b893c1735fa132494969a15867faadacf5f790c3302216bb4d6c660cc855050b335303dd9e25cc51e4314a019ecf25c876ae76e6347cdcf9f12da52a22ab0d9ed1a8617ab9ab9b422c1ec686cf9865fda2424d01f985db588821ab7456e41c4b1ebbed1cbcc2abbe669996459e309be5e8fb73a24ad3cd74f1c66549e85f8e3fb1fe7e0be880ffab622133aefc5bdaffdc5a931d58e7267f053adc45c80f42bceb611fe65d35cd9cb8099b891e41b1a4fe4ca9430eabff8a93964970fce02c4d7c365f52f39ff31afeb90036e29f1e6485c9b5b1f77a3c44ce013a0d0474ebdc1072a01582947148884245fcec50aa4c9d808496adcf95c3715acf43c472708ab2efb4e2b662ee1879a980132a05e23f929e6b4d98f32d2a858ea92202f139054913a51c35835b798b7eeefec2d5a8c4c198f45593c862cf96b04f5e6cea1ec47f7601952028df4252432f7ada77692470bcf51ca845b259036c45798c3a1a11e2ac22cd08abb6aa6f994cbc91d1c94847cbf0e59e4e1e3a785bd91a766952130fe5774404b69e441dfb5277b923674c7d3cdc5dca3e0897fd667305fefe49b5a7da103644b849508c75c30003e82712f86669c0024730e64af4b001"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6a4b1c390b6700397ade0a23e6fb80dbad085f733d6dd42704f2cae1f8462703","proof":"6e2351335411ed0efa0e306177a8f87f5e4cc84a0a5540025a6326b8257ac30114ca59c58d2a80594805f3c9071cbb43dfaf8f8590f3b5352dce62c9fc416d1f604a6cb7530ae32931300710bbba514f8159831ff61bb0346c030096e25aec6dbae69a869a87ad54007073c626343c531573a288a9cd88cd8bb73ba41f178423039c4cea249effc45dbb06ecd59238b6effb6756cb055fda35630ff2cecdcc0b76ac072712f7198074da60770bb9e3495249ae0b1b548281b83cfde69fd8eb02283c26eba7ffbb6031a6629c2e6e87a54b0f9e9425f346d6a233b8626492b40622359e56e521562213322c0f0810c49a31836d64e449ecefb5e665860c31e323f00dc27d101abbee44a5f1fedba11ce920c04e2b78daff94d60491018d30860e3a7e8eeb3316a9bf4b05bc2559c169d3559d5530bcb474460daf5d8baef1824d1c2ec0d889d11f88c4373792baaa2907f944352d82862675657e8cc0f470a8182cb62b67681a24e471a66d4b4dae2579c5ca74ba3e06e604fca0e110bac9914100f5b52b00e88707bc8d1d24b11a4d6fd4d5b48b5066fde57e668a226475965ec08e42500e93f9af295bc673ae1c2c824b5c0851e8ea1917ad95810084aca369de7c8e460da5d21c5c64fa30a65a4f5c2492e84d527476ac8e9f82d2da9494686481d252407f68b1a6ffb2c369d4d8d7ead24787ecac0b1b7c4e5c5f44db553898c1f35cf6fea33f428f44b062505c08b6f624221e7595f901ee443022a67163a8f9f0e8bbce1d5fd838139ed27dd7814abda4d9d67763707c3c49d6eca4b52a2a0353f9a065fa49ea0a3292ac3c883f0088f94b56b33236e422de40c313660cdb8f7951c7f585279fc1892a080ce0174b1674157c4a066260ab216baec51c0d87b95685b4e6fd1a5b4539205850ad7d4e3825cd5d033cc7130d5ba162dbf703"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a6aa69fbfe8139ddc8ecdcdb22bd22ade2fba7697fc0222ec8530b7c2f99f36f","proof":"561928be8b43825be0074a6200bde30d99cc723f45554fa09f14c203c9e36f15563515cc3ce29c069ab6403452f6eff45140934509982eb358f60a841a54ee1054c778d2c10f0707b2a3dc96ce6be56a5f47684c983d835b3aa4f8c436264870027c2f06c3df261868043583ca2a4ef9758501879bacf5f3504d935244475d28a1fde3ebfa5398aa8553a9db8b5c6e03ebb5f5e457715966b68753e956b72c07df5b54a76e7f1d59bef991faf4ebe8af8090e9da5a8346cfc36bbdf0bcb68d0ed5add4d2e09249be9a1044265ea66547004edb725950770e38a020b3d69eed0f2c6de25cf339cae0ba02e4ce5f233d498a5e99b74dd1a25c8fb5d9284a38524356c92f87be090caf8e6483ac41095c9ecc2919253107dc9798ec8f7f78b2377faa742e904eb6d597b4afa7685b2e71b2b895d10b22f04a31d53ffae1c25d0166961b9039f52fba65f80e1ae0c6070775f0676f7799e770e1957ae16940d2156500c7f6aa554a719c328b917f17eac2b2f5d8f56da0981aad6cf0a474a96a4b2644edb96cbd8c710f3940ce334ffd531992847768cc7eadd96b5130ba9440f072264e27a98d3f1a14c01e02f4e0a3b2352779187bab7e5c50daddf191c3ad385f9ebdc672a2a64bead7b8306d616ae1a481bdc4583df203871565219af194165f4a1f46158d2453b92eb059b12548b9c1b7a27aee1711974ab6ea689bc5c6a4643c2199c0489b46e16f6a13970534d4092b213ad8c3606987383077dbf58ee37ac89d1c7b8cd53e4a02b91cc65ee86579fc3241af13feb8deb73dd2d7b59e35709acd1475157621618686572c8d418db92859fd5b0976f4da46a45e00f7cd707727553e04136380489c90514b27a474e1475d66205ff259392857c884202ad908b019728d1c423c3f74f543049220be1bc233e1815ad39a28a5c11a1be797130d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cefba05bfc5618606a596bcc9b3be179f43f0547da31427a3e0a7d51fdaa4264","proof":"82c5380bb27db315422393288fe907dc940e3fa20c8a5cba0f903af91486fa01981d74515e939394c70f7ced6272481e15e9bb98bf6e24e0d6173a9744219e0a1a74eeb743adc57081fce539ceb86389d96db9ac1398884e4afc7e173650026828f88868dff61ff4da4feef0507c6354a46d6ce0a829c1ef8548731b60b93e6fa8facc4cdf35b299dc2cbdae123d7e324ef9fe7fd547947bde1af4d8a9815305ee23990fac71c727f8ba8842e8ca4df5c6c5778bb6064a3afcc8228aaba2f204e69f14013d338c13e08f8325252f66c2b9f36e5015df3417a9a9529621dc3e0cd643c4eb46a3da8c3d4cde25f0a01b689e1b8f1a2fd2ea8db6fc6d63f8d52e5f4ad0d29a0d3062c86210a96925d28f72a9ac4f9513b6c108780b24a038fd8c7fee0eb7b76930f3203bfacaa057f457fe4135a092d36e6cd24716eba9f3d66969544991e4f67c498bcc0672111c01a53299a848bc364f23619f4e0f375ed329449cecd4f97d02ee6d9cc160bc166d73c8b7645c773964ba16057b8afd75183137c421072b6fc790f46e3df66c318460e4015c3d63837e3d334f1b4687e6ac0f3bbcbc4606ad6b216854f2436e3cd6c6d1ae8d4efc4b68f0b7de4f694090a9e1768c38fad87ca967c8a7d978394b95407cb052ff72e9a40e6d55d32d1f89bb9131568d876c5eb17c235c94df68cd6f15f07f03b5ad594ca5ce7b15f25c1192b52c86188ab2e468360f1a28dd06745840dfda0fce0ea4f480a01379af6922ff05447636e5b45de037167de2e87a4995fe000f1a59dad2d5d9f79297e99b0980df6432803ecfb8e238eb50480ef2b633c98250711e9e1b01cae2b8ee52208b3959788afa4b70bc80d1c048e2a56a7c0c8c7a2006dec8aa1571eff0932e140ec800004dd8032e028eb86e847c61f72bd087c44c084f32c4f22236d903b343d0d5d505"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"eef5bdb7f527cc90c039421e34df0cb7e82aac6fbed43baf7c0fb7b780f1356e","proof":"0e9e962d9f837e08ea70956563118dc83c76603b72fad7a2ae9391bc76738532e8d0b7e12954f78a79e5b15a7ff214311eb487785d9ba0f78d411b2cd84bd96bda0a50a3af2fc35b753c9e45ab814a780811d3b47c9ff43a7199f3c5205bcd27b06047c128b2a6c3e9628b881b28fc0363b52002ef015102188b8a562a52334ff756cb5d5204d5089444e47a5071dbded5c33c0ae86815a87d558d4b32e65d0e3ea89340f1c809f916b22b4bac1c23e96dd919123d8a14f2ba35683b89ae1203cf1c2f108bd9d7324ef6534d33907ddf28d948899c3a3f6eba80afbcaa85fe0016587fb1ed20f3811ac44656b06328b06dffff3fcf1b9079da48b8e3361f87104eb1fab7e9d87c0124469114e422111e02f44a7e2ba65159ccb7500ca4299c5972832aa808b9eaf8eae7f95e5835edfaa5f41acdd61597244d911c434b2e9a28ecd0895ea6d8ac1fcc71264b8e191bfb1258f5cfdc4b6584240a0f22c2c5952c04cdc07bf01090077ba5dbfbdd1bbffd7c496bb084dad392ec9fe5436e2f502e10ddbc2431fec2487981f70dae5f7b63490ef696db9f017841b857e3958c714968348b6e5d748aa235427b4d64545f5e2c423ef4bd38aaa8fce31abb80b2681d1cb1a109770936c481b89f03b14c68044383eae92cecfd801c0cded559e97a34c49940e260bc1c7d3b23b3016a208e1f6be1a8702fb07272de9a35e061147b3c088dfd407e3196ed418691ade9be5bfc9f46cfdb40ec2d34e766e152a4629b5e4888c5094e03305e7b4fade02eaeb3c4093ab118141e3b52baae22264c78c5149ca8baeea96316d44d879caaee6b2891691e4011cc101e5f0f7a5bfcbc090a55a9c5b041e8fd346213e8dde64fdacf1f42920d066a620c5e21f86cc867212106e25cee420a7c405ab5b40d202a9f4f6450e3fa6422b943530ff5898071a9a906"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"70fe888895eca503c591e934c02a6de0f66a38119120d91c8b0cb77a63af486f","proof":"02619a08a2f6193bc24ace4576177874d69949a37c9ad1c072e65bc319429042a4504c28a57391af31e20a08f5c664e25365af641fe17ecde959b4f79735fe28d8dd68a8222fdca34f4003d014a6542a6e4fac47502da8d6a0490632134aeb26d0a53e44e0e19527ca814f8105c9da1852939e51df34a426c2fede8bf68e7410d548dc530760a4df592bd80b1e34e66616508dbbe341e5097953f569e9b5d80a062004dde2658a411c49845f6ce6d9b9416ebf9874fa5f426e6a37f7a794fa0045a73d5603ec88ae01aa9752430a54e4a7ea0f0ad0e903eb01d8abec6a341b0e50dca4565974ef43bc054df11110005032ba507f131a13c2773b4307ed04f401f2dec8ac44edb6d8e31bd2400a64ab375dc9bc12b487e98e264c6329a8c4840fc0b8c73719daee102566217ffc45b8c7b3470574f6fff317d351936f5ff6ca7d8eabd8bbc00ecd88a955d029cb0e54ceb966843bc75a4462a967880f4b5c6f6536189139414fed7cae67514dd6512b77cd647176e10c7583401113a48671127d72fc3d7b30bb2048af387b34bb63bf7074aa51e2c25c749116ef2a5ff7426d0a4e72b2bdaace56a5ad50001c50075f723abdd3e8197fd5c0201ec2bcafa0b058e247d46432128959ee949e6302677173a0b7fe4c3eb61932bc365fecc7c3bb095cf7f9e358f0a303404fda38d8088ea4925afc4169fcfc811d36878ea0db525890371d4449385fffec1b7779b32d47ddca5780299182fa5ee3abc9fe7aea9d102a56587ffbea9d33ee8224255a3737b236a4a68ad75845bb7bb4f4dcb9afc15bb65739f4377277b140bf75fd0665cdbf204b0a6a45f6aa58c91cd6c518ef33401bb8a57fbdad8e2c56034c8edd9bc54590071850d04d19f332723aa11117dc0c5ce5f9af23c3b406d5ff1f15732b85b017ecd626db6e338a9867d8501e9ecd02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5e10d25b63b40a263ca985bf2062ba0d845387b95a3536f0ba255081a279de2d","proof":"e6cb9574975aade5ec9d9ec0529239fc07674a6e6dadd8caae6d5b473395921f6801d3926eeac33001b168bd7146d67814ec0dd41a891dc14a5b5ae3c1c0d84abc4e64013492f96ae6e3546cbee02f726b28803bf4cb6da50ded6df6614487377c29128600dfc5e831b654284655246b02867d05ecfe868a0d89e4afcad02c75284905ab0230e95d6d4cbf2d3b8fc16c068ad6490fa277b1c5b73462fc0a4c065e63f8efb017f7ac7b5db3beee7386e5cc1e96e022c6c769b27f01a067e5b303bc4e9725dfd6dda8ed6629e0976bd6ddeeb8584ce34ae736b1f6d918f17c800ec4464f382d6898625838cc556c7a6f574f8835257d8e905a70925a65fb29500a3aac1c6354b0bf368a1de86e393ea03ea09a7cb9dc8510ed67f4d74aebdd98000cc748099cc4ec376c1a024ae1312fd86cbce44965bcbebf3ec573b08ca05c6af6e5bd8caf69b12cb9e52be279cb77307ca38f1f5deb3469b84b82fb7324311c6227af5ec47d9ad1fa22c637d9832f9f1261fbb70be071790b7c3c6e2908056d42655971b3c93e4e6e251758caf596009f5b2380e80f5a23b912decd54aa561b4a44f7481b40910d6b5bb8df1d5c074c24f5007c741a1ebdab84bb17f8d284221643c9d68c6e931040a431cc5dcb2d95b5abf9804fb322e574b80af828dfa95c1c6e8b254e1ab6da947473a1ae8301bd5a6cb6852705c96a8c449913fa7d551b2cecd105771c889c2c42398c802253d1516c08118e2421a69de53674a23a1e7bdab805b27614dafb77f20d78068e19e0425779d52c94a183b7201a55edaad32b2c158fd672c27d97738db1d041df35a5b4566ac353b05de7d127aad71340bd02ebe5cab3bfceb3eeaa1cc272237bf94550859f52ddaacd95bb594965f913d007a83373cda32461aaabc443974f6b0932f17537df57a50d109740e204b3ea8e05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2428e6e7487c1f1de371eb470c4e7867f5568d70be77cc197e00a4a42423ba1e","proof":"d24dd2d458af31f86a7e411500120aef82ef5799f6a9627feab7b3ebbd5d624fdcd24c8b9da79eae1fc5367d753f92b4100413ca9e338ac35314ec8ad284111ab6368ed520671c184c7830a8fadf145cdcca2fd552c76bbd00d51f12e6658e1ab60b4c6371a387e47526f3e4fcacbf2cf80d5d73e0c67b314574ca486b158f70e3d6b89aeade7eb5e4fb06eabc26982ed24482260c48bc2a6ee635843e39b900142510163e85520e841e903e27e3aa0b1c6bb2ae23df9c411c83c0e2b464660bb42f2ab2a2c0034c9aee06d9aae7a742b02331ec6f4f13283a112c6bcd51550d1eaa3e12b010bd6f6eacad5d86cf76eb1556e3ca22b1e6bf1162e4add80dcc2b5c417a7493faa2e484d584329129c9ef547cac4b68f741cb8f6924a1265e784f522410b5aa5cfc68ba0b80897ddf2a4b2c1ba4680cf21b4910f4558c4ba2cd1e5c17b59f7736c294dfc9671f059e2069d0f4f4b4e04bfaa33fa2a6738c66d222e69eb3eea983c7198d0f99d5f92a6522bcc92c74cb83adbba7e6face48c1852450bd0ca960ecefe7c73e295d8cea9d9b55cfaf4d50b10224aab07c76785bf553660f9868b2c97518a3707381d0e618a7af874e8bfc42aaed577e6a61a7698d09ece282b7cf0280e3407cd2d9f5da988fe12f53d4a0859bc83240748866d902470840a449f88b88203483788e403cd7d2df1137f10a1ae7c144a2fbe2011f8a1166d9ee5dbafeb14a827bc1ca6a8fc742069e487b8b736a048d75c53a29c08d60fa499094c7c5f264020a12e0c139d8608a9e0228703623503ed8ca7ec9b9692c7e5281650b37188571f80ff1313cc5f97c4809e4a1e03a91b88ba8afe192ee6dcc5853645f1c5f94797933bcc6024fadf6045395ff77755f5be5a14bfecb520e8c28145cf1f0161b8c6f3815a010f44811f0b65a437b6bd43b74e9dbed0f4c04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e836e2f12c61cb1abca9b90895eb88cc2f7abaea6eb2fb395caee6b2966b6d5d","proof":"825a9099b5ea2c96260127252a17438e1c239fc993a3cffbbce66b12513f6126546ad99a7533f963d4d69eb90d527abfc97108970eb34b91bc48dd1f2847c4441cedd26143dce220a1b82e7f9845255918dbfe96bcf19038e73cb01ef1f2e4576879dde8bc6ecad76fda6b00d87097a6364cb7a67ff6d010b06430bcb430a965125d5f167de2f003fe1c3bca9fa0c29551900f9452527ccaa54cb1f1a7469f015e668a2b4bc865293555f19e47e877fe01a2e51591eb4601e99b6dbe08e85f0ea567367ad7c87af6885930cee5895358a53a569ce2b31e5821d3d957fbf0780f9aa9ee2114c04c621d1888388747d17947a848d4cc8e43cb1bb83d3dfa6c083ad026a580bf26d480257ab8fcb9de124813aa1c880141f219320e5e4f7a9ea81a70b873be3880f9e23390455326ad97eb3b41c716a1265867251da0a0cd083f380e7cdcaf10df1a68c8ee5bde21bd9fe9a8b110b5fed49d45611bb86870bec853c61d5eae97c713e8b99e97f3c67a51efb677a1336f3f2275aafeab0bdc1bb976cafcd6305633f8dd9bbb5815407c22155df05c2126b1966b48fd77ca9aad0868a86a1a426e9a4a434ee3e735191b9de907aaa7001c6f6122d029b02232f9f3208a1f29f1133d7f2cc1591e1095edc227de5efdf455863c5c0234bb06ee57216faec6805ce6426e6ea8ea448c1e0de4ec02d95db4d25f9157f194c4a9010e6223fad7927327a77b0494b5b081a0e68dbbcc39bc72d35a7f83400c73d69779d276fe0eb77bb5d54c2f13f3a9b16f4369beb5fde4238f2ad0c5930a0252fefbc818fe198cc38416e329c449fd6d0d4c30279077a98dd3a46c2f34a4af28cbae4169697eb51f6f090f7b433ef7830fa65f386d5ef332e6504026210a50e73a9fac010e23365a549a80d9ccb4de839ff42247a012822c0d6b9594a59080bcb6ebbf0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bc55661ed4169908aa4e961f190b19b630f33568409ae0ba324491a09b7ed45d","proof":"800be51fcebf156aaa8281cd7e4085a4f2f56f285a9b21750c332aad93e1356efa17ce9ec7b588a61818a445215d550d95e893d3eae84c27a9a53c014694fc64ec10a9ea55256c07a07c028bf39247834e40241f421637229570e3f971644c6ebc8613bdadb6498d641bdd4f47a7a10c659f32395ae7fbbfd0ba99e5ec467e32c98c843ae420be876279580fee6383fb90c60c091fcc9e8ec07601c8f2f7070c95379ae26c7476ff2610e844f3a91ced11a0aa104f730d168a737a9a51c6dd0ca257bf786296f19ee6be546646b97697df280ae91f7e6c6510099f761395e008aedc3873f3798428b7ef122d62efd067f7bd212b981bb2b763e65595990e2924cc8d1a298ed767199efb25102060892ed3acf76ddf08239dc83ef93adf21fb1c36055cf3251e10b127ff760b4228b00027ccf3d679b8e3e04ffb970cd7dc3762a26c3feeeb6ba1c13af3f858854621e089f8473d48bbfea4de24d74423f30e11682347ce4f3d4ede7b07550d84d07d2ea3dfc235621f3304e2ff1f9c0a600558da5a7a9ede5252019e5c26c50969dc4fdff28a3d2ab456f254d94ae8fd55ed70101fdf30d04fa9f462db3fe611fb1d198f7687d5c965c37544bdd0554e514b59c4100c4e8c7a8760b584819b6a8a639cbe9b51cd697b0b0f8e265398340748120a60730a936531c036491a7d125672ac3bc4bd4c2714d7c7bd3bd213a3ad8471a27b4123cd883af623cb631a4d9afab6b4917cde3a26e84c9322ee4fc06a444eb42557bf38d3da6bea62a7cdaea41e47a641161f764079317b8e302d3b15f3476e3cb9f78d4b6d8df314e2ff66708b8373cdca8e67776e75200c95d42bbdbe57da9af679f0df9592c24123c9f55b069e8e6c7111bb970ad735a2d68f1c7d70071506f0483b03ae7a3b83597a0750f541de8aca2a26cdf802c2d993f699af020a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"383b89d93f29a46e1136fae6601bc6e8f710a6ead742704453af52a6ad693c66","proof":"820f7fc7c7e042a557c536eef67f1cb4f57cc567cff7f242af8296ca066234531ccbe8a8c9e05b3e87528d942231ce3969ffa9863c47a425ef2846d7cccc6c69d854ada6c77f4ec7257305a9b12eaa3f9695f0e5bc09bfbc8bbe025b94051d3bba37f95eae8e6ed9403d229abfe5ad3da6a3cb17fb737a29628574d7703f0f0efd5d31c3615a8000a2987e2200e94775679f31752abc433ba2d3d8ca142196064ef95f8a7c5b73313a5b590745fdb5453b93df1ad5e04898c6620d7e9940b004b0e4375fd4e01f2343141cc110ca1b357d280f6f556e5549b12fce716afa750768df267d58c25ae342354bc3f5a8ab97dd290426f55b79ec9cbb18d6a57ea569524c9e20409bf8633ad067978e15b1161048e7b3f0cda5d3a1b34a127a2f456cdc737e0914b47977a4540177b023e4e7fbf3e7a9e940fc65071618d77c96c35ba487c50e8d72a69dbd6969d4f64f808360539e7dd0fbdd828da696a92482bf4ae0c3456ed64aca5ac0949a0fab6658281e647b4ded2fbfe98bce2a005373c15cead5c49c4840469828ea8d61159dbf23108cc6ad14f18fc31dd5f04aa617736ba698e7cdef1f623a70628fca33061bee2a1e072f1f3a14c7675ed11d8a70c671688281a62e5441f4baf40e23de397ce857239f4c06639e1127cd2427605c0d5b20a16a6c66aea5a39a397080a25c14be13eef91a67fc506c8032ab6934b2353218cc9453e5e625a712c7e90ec05aef18ab0199b9929f72f7ccd4626ae141f44b40e50fb434aa80c9756ae8e11a9c7cd3a2eafeed0bde2c3eb5305933fc78ed28f6d253c17f3e8cdba3ed22b574b761084dcf239aa4cb6a0d49e4bcbacaeeb3372b96fecc0b99f51291d537c74f3500cf0783290883c1f1e15fce1c61746c5401eb25d48b329bf448cc9b7d0e8736ca67d80bf8b1e8db8a76c7c4317c1e111207"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"62445b5df377d7e0068eb63c24a2c0c17afff7c06d2d1c0118deb9dd10574c2b","proof":"2c0fcae7042456c0b030e1a141c9811f235eae99400d0a8c75ceca2476fc7d26dc227b75e5821f42afd710991837887622b32139755f25ec356fa23cb8e08e7a6a1358f5d35750bd6e827a09b5e7723b3f6a59deac5e7c7ceb01a358b04e1547645c56ee05b47102d1dcc7acc2cec2f12ea6c0e620896fd26c5165e401f9726aa6bf6be1bafd05351ab80ef485c37552d145bab3df5faed51e918356814df50da96777c9eb867b4692c50aa30af91283b32b582bfe3da35e6d6a5d5022fc8f096342c456eb3cc2cb6451212502827530b33a18bb7abf35129bb8bfda386edc0a989b09c9533dce76624f6c776195eecf51d7226ca0eb4a2cde9cc382c3c22472b8346449305e362b939d1053baf1e23e6e275fe108b101c49c84832014422f1520feb9b6ae09f83ed5c6d510d6aa1fa1c85e3b80f4be736e29fb3338b025d70bca727a2c358e77ff076534d17cecc5e3c28345ccce6eb58fcc2c6008f9af067efe7e85b69a00c0cfe73b3d8253d55549946e2b3f00af92b6afa15d21c922677ac2a6731186328757d6c94bf358f0db1dedaecee26f67907df81a76e9539cb660e65ca4f1d7f982e9c8b0a861526383ed28a79c4b5fdd25890296706dd0ac161a88619d35dcf9c901ffa6b3c931a3428d458cb64280034b465a3b5e055f88e37fe6afe7443f1986d573945e9b420ec3f9f70db3c302423e330e64a6d6b4826d7dcc96bc69f4bf271f2ae6772c0c547471890c8866f5972b9b17b69dde239f8c2f4ae3ae5b85e05007f3f041850e3e3d402b3191d31f6f78155e7ebbd068eb2078fc6c14e0ee69c65b5867ca5c08fbe8811cdc5fac9cb335d67752c85533da757cbf434c385218b54733ea03fbfc941a06b8efb72bf38a65697ed2b2aec28c89046ef8c851987a7d2cc67e71940c91dc5f25e40933355b605353a0c5f2b8383008"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c65f40bbb12cd6f454537b01f65ec92a178ac0af732e6bad5b20dc972ee8632c","proof":"42e6a9a37c149ccb6cb36fd9e5560bbfb5eec7e882a476644f2dfb1858f3fe44aa961bc88b9a3b801c41ed41e8a6ce6ad8ab608ec748d6b8ebad0094d77e630516d971b4f3c1dc3e76ccdd2b91a14812306bdbab3e6e3e532aaa068ebe7d93554a774d9d38f70c964f189ecea6fa72ef9d8ade0263878a6bc6555cbe61d5733a8dd35719cb759f6e8e5ad449456379ac94fb8f14f4d08babff1fa2107252ab00f8fadef4af7fd4e90a9385d214e30d6a6bba177eac02d7c5ea5b86bd72a6920a38181bc869ac10bdd086c47ce15b7934ddf10d9ade0c66b760c0e4d705d511002c8f6183882f6077cedf8cb57ce60486dc958da15da7c1a90b9495d18ab6675cc46f5801d2339b01a51ece8838818bbf2ba6338d541bd57e496a83f176732d7c48d620460d4adc5727464846c59123a309706767a0ac6d5196f230efee5d565626ec9835a051614e52390abe989f531837fc1904227c6afcf85539bf7afff74aa8f2e816154db9a43d7f5fccd2b12f011776ed674c408c27c3c2b1801f8547649edc729ffe31d06b78657145976a00b6f09e92a1c7eb4e577a695b7f6fe8663344754d499f6025dcc09bf1bd8e234a425acdfae6f8c3d189627241fbc9e8fb450242dd39975fe66709f88605310a7bec8453219eb555ae4349378c065f55cf0dfa95944e76f953a84c052510caf72ff1f32495d3adde190560676e8cccd004567ee88a811ced3e98ba1be5e65eda2b7ce159e3703d45a2db085e647e11a0b306b41e90cdd24c0b91b4a86b51eae55ac1eb2c675dec17ff4ee38ca5a4b93492159e1c767cda7413c6592504ffcca475719f555714d08f56fa9df890bc1a7c2677aaf8041365ace798e2b678be16e9921ca8ffccf39168ebc5e48ad976aabadd00aa746febb13604dd63cb583197a03719255a4c4b61ca398ad398032b0c13e60b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d06b73ec78eade2f0424836ffee4a248684c3159171ccb5ce7e23a1c005f8720","proof":"422e35f750e5250c7ae3f541b5b6e3c6a01f89f2dbd7914bddb94ce201c36537722e2782f95b7ff0ab4317678713c1f3ebd59685145d74dfe36157e71ad4076e342e3605b7a973bae0844bc711cce9f70ffad5b0f7d874cd7dc2ac3b28f2f95348d8bfc14d2422963b7d2565e04a41308cd618991917b4b60f8660300e797d5fb55e0e75f6e0501558abf2af3e92021e6d7c31247ca9fcc89305e9b78cdc2304a65cb9e956c68e172bf5ed054a7aeef806d9831b295d3d37b1f81d3fda30010ce4ef7568045d6e87ce248b988498a267bfbb67c0efe80843a9317e2bd43f2b01acb9131ef881683e4e62f26610f15c50cadd1d81c79562d82889c62004750f301e1c22dd1da97502f4262e0f1f9c6077bf8ae152b4f0a5ed25969dbaa4fab3502cf3ed79573c352c451fc949817b65ba471834cf3d50741f02bb58acc397cb4f0e3f17245574e542de8ee247be4faad3eab58acb7fc66d2a5c0ea2a547d659445c32361aeca8c1e6a9623b1dfc2662a24a6c9e6817f2b77488162d467eb48018fa1cd8c05d3c27c2a81fb32b27c38f1f99335e0cb8ff5c33b24212d662c1222098d4f4004797e41b63f0e3a966d4dd865277f334f6e1d510834cc4a0436a8a4dc89fdb70a739a74f3652ff89aaa46c0fca4c9cb30ba03c6b0c6a573908967720d8f2a12e0be06b27c9996c76558b781fb91c696ba4735a902dcc4133f9520152009fa30e4252ecef12fe4f13a864798876b360a1c4e7a39683ad53ea57f4231ff08774352c51b1e8cc028b12fd554afcd31850f80b9324a501793ecf4f6e6e51869b9b513afa8f11b93a3874fa2f6375da9c4031fb36d39f366febf9ec2b1930f0b18348cdba7b70f15927583748f54213850d0e2a1c9cf5f19502775f0e310da387804eef59a3f158f283d7a46dd623906d82f06e50ae7543faba5da0badc0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2076234f3682734608e012dc7e87a3e68c14c2adbfb573c99d9183f135b62058","proof":"24e78dc034fdc3160063d18bb2ca4b4629f4734792a8a4696c4a89a622c63c740e4dae58ab4ce51a5a2b99f19e34a85c8bddb1ae593e1bc8bdce2186b053e47cba2515c034554c6685844978833e4edad9d2f86ffd809d33ff345287236f293ecabce34f1fa152ffa4afd212db580c85a38d0e9571c18ee965cb86805cf86b31a8f5c10aa7a62f1e44cbd86bc1925127de5114e82a061052b894d255e9d6ad0ced9289b8635ef2318d41ce3d19b66d9fdb064536379a44eb84d875eaa7f4ca0c25f59fb331df5304df933ab855a4eb3c910d97fc5f4258eabcf6187fabe38b0d62afca849d62f0db61af33b5b9cce520f46625224a8e86b47784fbc780a58507649a2005f18c1c38ab228a596a9bdd58da8d0a5ac86f0ca10f6fb0e191b282041a9b6e740ac29888786e9b33a78534669f3cff348f1e242bec738682309cad7f144d65d940f26c6f32194b241ab8d04635acaf41c9afd96e9f24cc8ce7c9242134ba8e6b180e70fcdf4205648cef02c1c5ee5a635067a66617a7551129a6723f1438bc3018d683d87fc24475544c96a144d1668cc5142de3eda64a047a7fc574a690386b716c212d96f2f392a41f2d8c7f9493a86cf46088736e5f732d459f1f5ce684285abb62292b4d5579d0a80139e0fcf0824d1bc1e1c13d909d62cdda3324d3997b099b7e6b3db6d726d0e244a92384bf27192ce76a49f54fdb7e9b9174acf1083f770aaad1c8f0bec886ff4ca482433f8d20af426da7a5c35cd5cb3a7052e6a1f9a85e8fd02a712d1ea4eb8ea0f3d67f6a25fedfaef4b5c0ec60d4813aaa32034a103924c6af969dda450f7622f33f7844df7331c00d46b6c3f2cc310a32156e57780e911587ae8ff0aa14369922656d5f3bcfa95def1b8fb3abeb8c07bd47eda807473da3085a39400c2a2ae2585717fd6ee47ed0aa363fb8697a1e0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"30ca25bee325ff513d8c15f43597cb85741a5da00471bea75d21a7717af0aa38","proof":"64168382c2622dea586c2210830f5df79579b91cbf2afff589a98ca5295c787bbceb48d3a2a478071b37472c9bc905882add2ab86bc2b02432089a84138e9d73f443b9043b51b602e13786da5a8be68d9032ec9850efca6c49556f1d3f2b632e5a635d8ea9d1fda632cf3d64cc17bf600512926987523189e091f4462dce07000ebf55c6307fb9fec8fa8e733ef089853853ddbe327edf32cfb0fc7daa7f820d64393415cb8b741b1e37f50b9d9030e3f70bc894b9b34f4867e73fff8f73520f9d52aa32e0083c2428739306313d94410fdedf4bd79b079e322b0fd7e69a430298b530642f8fa2c52bc0209d0cbf44ab37b58ac9a2294a83cc649f81fe3b1c1eda3d16fa07e17e9ad25704c9ab764e7491af51d59cd604377e2689a1cda87b18be692f57566ee5e478bff552d4a63d5b0b7710bbd2b9d7ac173825ca48e6dd1248871f4443049b333d31aad0983ec6c0fa3ceb63ffbd12e0d294235ab7ad6e19f60b82fac6de9fd8a1684e0f1f2479716d253ca6ca777f7bb3fd94cfbe77f80fcaeb68b81c13b448d46b972bbe20539e180ea6990db775aa3f472d2cfe44315f205db6d9c8eec1e909742dbb88b0d05ed1f4a7043e4e1da6db6f4a4615f7641198a4a88c5a2b0ffa0129cabc1cb9d5f6c4e3a3a81a0fa4ea0f161107b8734671b01357ed3e192c05aa626968e61be6c06b264ca718ed3f3817f620fe91e65302969ccda4ea4aa5bdec380d61aff6d5f827ce55e408759adf3f0ad9ff8f180d766c45003d3c7ef6d5db410bc4d13ed4adaa82e6d5dbc32c14c6fe1a1e5ffcca657c96dc7a1a1e002f6f723229c4c370c0073929f1efb2972da31441a55d7d6d4fce84807b40500f7ea2bcf193c7497c162241f905909f490b0eb4a7cd11bf95078b875fd36649a3068bed05c1e1da850271e5921e69d7d0ff18b93b3c1523f00a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"12ffc56598bf341dbcee06e85d41fe3f3e014168e73a82162584233fe9793e4e","proof":"6835a4bdfddfd9ef8ee5deb97e1e0faca29ef50e365bff10682f157452d41e2a268ed513f4f8237fe5ffa5bcfe2c278b7a4250e6b8517652a583a271bc6a8263c818ab6cbfeea18e4946189bb9bd4c48e4e5e322cc796431799cc0a4f9982c070c49eb04b2c1e8ab97d2fe9dafb6da7d408998d6ccc6412a3284b8da2d983b65c042dededd4750b4a950fcdd2c9a81e2bccbe302a4c7a60a01dd1d4154e4fc0a7749de3fcce1220aceeb9709a29ec28367fea44f2af01c4c63c11bcef6a0c30ed439e65d9625f9a19a9b9cbe45ea78f5373a69529b9972288add5085e71e7d06f2593a14b8685337a2ea6f44c51a6fc4d360fc189258660e93e5e417d96ea70f06dca1726a502ae3a8fe027eca7e28411e7ee961e524ebca9ce3bd712f2b00644c4db00263aaae694316150272705d30bcd94b482907bf1655f917e3f2f6235c846abd0a81031a5c8c575110f7b43447b26898af58ae1209eda5dc48892d40714c173ec2eea3df0a2894ed8b3e5353edcce90e9a379e76c7528a32e0e32e196f788aa3bb73b8b3215f3538a75ff7e93fc49f971d919b3db46d774a379ff31002d6e3f727e337e803369704829db6c070cdbc48321efb9b248a018a562424d60c04b5997d40273beb01583876cf3ec28420f80368707f99738f83d107a9421222b8f613fd9a9131cb17a1b2b89b566fd800b5154b06054ed8a1788e0e0caa142bf61781f5ca9ca826ab262c85a3bcb10179da2806bde0c303352c5e8276a340782856012653ae9ddfd71008a785faef528b71680eb175a56075eb0b649ee71143ee64258c771243c358e2c7554a86af085861b89a41b35c0e0614ebacbaacad1acfa6db0bdda3ca2345e1bfb1f618bd30197dce72da6337f39fa05e15367d4e0d4da7cf1a05abd7bf486ef6d68242a0f39762a5927b8790118d96dc2a121f7a0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c43ac3473b43341060aa8d6b3afdefcf83b31ecc65ae4128dbc9ec50348a9948","proof":"7a53db68f6cbf135cafc3d864e43edd33baef80459b5cb1b6c74d2c76fa512203e59c68a613ada1cfddcba2220e34908db64171ca6980ca98c6299d0b1e77d36e892a6625121a53b5655c7330e3e770eac92dec4d8122966899cb6ca609c7549684d40f766474d0645ebe634c4d6a10b572d25435caf152dd80322efb0090d093139b4c5b2bed0811c30c5ceb2fd7a78aaca210f50ede70630e14a8dbdc4f8045b61e99b7259e77e559f869e1515538ec4368fd0e04f73af83c977e17652c10258f0ce520c4346da20da78616fe55f42d6b20e5e6e1a5e86ba4b09684f5dfa0190c075299b167142328705fe9594e9199b2165f8a9fb2d4f0612b0b1a8d3e941a2613d85af4e4059837e55256058e1b079f3b7e14daa584b92244d4d55eee1180a00ea71040197e38a58af3df2dc625836945d25f930f5a0655ff4f7e625e27850dd5d7f0225a4b31b0e6b8b4c1a7f91de5ccbc3d005ef5d0f275d26050d963480a9085023dee0711a7fce196807e51b66985f30cd92e6e729538a2b9fe44b108ae445cad7096a6b3604c3a1ead6083e70e0c196b18c3c3b0ad4494a02317e08ca17a1fdc17b3c204b125680f81f8c592ccdd9b6795423cf70378161587e866e20d322aa3ccc2c075395e276fca4a49e7749ae1a46b9b2e212fde69ab69fdd07f0ca28b2064c5128ffb195f02764a3409b7516015038b87fbe5144d11fea71108ab65d5561cac5583420035c0c82e2296fed84be96a823191c5108dd2322ac35dc8315274b78a056254bd7c4fe90f950a9f442ec601d3b4fffa8b0e42b2464293ef58e1b96827e854a0a575ab2bf69fb767b76916ee985c807970074577c6c065ba2db1364c30812c0db296a7d8886a8a100d0450deb3e3373e949b1cf9a9808e1de22da991bb48543472c5e37b383d42492510b89ede2fb2adf944a858f0b06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9cee5237cfcc99d1020ac1497d4dd37a3f34e83433600d4f07dd0843daea2708","proof":"de7f5493e3eea035c0218c27a4a9b33aaaa73efd0178e2df5f97f2397ac1ec6964157fef6bd0afb9a2b140a0f54e596a882587c18dcd0ff7152d5a80dce50f5faea3f5319469fbe48cd536fccc03864da7e01850fe83828527cfa034ff22cf77b8e368efe30e501fc23ef3e2b9e5fd14e5a6f4abf0dd26e60e2030729855e30df080b4cc38831ee4fc7f199a8f9d9df7f8b966a702f16de6cb226669ceceb90339ff2f7a495df2d85d6e6c57fa5857fbb1c1b4d170c02fbbcc11253dff0ec103c9d3812768be17a09d2fe2b404bf563427efa46bbb93767c08ba1841e073fe049aea98b23d639780c1af18d6be36f15679d5005d8dbe20ec61172b75fe54ad4daef6716bb2c14b204f4c84841077ffbc9df292db432c3be4c7273050ff09b807ca0531fb47e0f6e45f489912df27640122efca14e40b9bd56c024f56564c2022a08bc820ea1347ef8beaeab3358310cd81f972afdd2ac34a5c0d2331fe97416dd4fca9a12031c428058db2c21e15c4e177e1323f9b325c28eadd00a3e7224d5ed03aca04738cea5161460d2d068f668984bde505f1944736417cd819d28ec60b3a5ddffc744cf4319089f279d2f7aea468143221a1012e50ac622ce1202e1d6b92ccaf8963f45830c4b6c05b21be8cfc89d062124af4cd31ef725eb5659f155414a84c203f3353263e4dee76f557fc5d12fbadc4850a00fe1671152472d03f1a487530aec90fdbb5e8cf38e8606d8ff220d101587c1905349f3be6a7ce579e2cd68ce5b6e912286e48a7674a91f409461d85363ca1815e5d4cc1d38b9b234f7e42a0593b901dcd1df599c992e27abb665e9839908eb7c8dbfff300e44afc1807e603d6a3b81cbeb59b3d95f6aeaa1aa0743b3d359eb35db0ead9cda8c87b670c697418036b47b6ef8532853fe2823a41539bcafcd4e6f2b63c8ab464e235b90f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f8d0b4e53fc2caec9303a0b90460948e7860b18c4fb0a2165c3370f8188bed7f","proof":"eea7b7cd29fb53de7a0629555dc3a5ecee74b62b270fd745f8d50c9e5ab97c202a980d09a46f68a4e421b272bc3c7018442d5b45052b118aa41f392d31ed4c5dacc010773233658d26d46ad238f11086e914d82cff11a8c2a7163136d0218223d82fc53c6666f5595c89dd407f6ebf3f609d95b9fdd9755df08c52bab8bd3a596555cbe7d4df794376da4f51bcf1350c19e80a3ea8f7eb6e5a5cf02c4324bb02ed9c9bdf4d7cc012550e0f50c4937b881a75d2504377b25bc6d437ae0866d10eff4ba7dca4fd984add56a1a2231494da4706fda017ce96f1eaa93f5ab2e59301d806df76f9de5a36106eabb30d87c48eb7ec438f18e327270bec6863883b8342a21ea71134dfa755b21430314055b11fe222badff2dcfde3ce5e83549f16c525b2a41f15bdb134b42532c7627fceaf43eca6cf746df4db414abc5ed6268e5a37580d5bd2ac45e8e140be1bb2148e3e8b7108a0535f89c4c2be5ee3bbddca74265e589b7bb4dd7868929c7737560b4f96416d948fbaf5e3aeddf5e2c301abea34eebe8cfae2a6a5a749f42659b75267a4fc450f612c8f1b393b5ad9887cded13d20aecc4b9da25f39e94f2ef449dc53fd7b6ca1b8a40f60fbf1db3bad06510f40dc26de62a9db44d4ad167e94dd30aa4af7ec8573a4fcf8bf709f57ef40b2ac10de92131f351ed7a35f6be559f2567dab2412483e9eb1f32e04369bf1903c40148c058055a85daa1469f5a9a1fe996ecab69b37cc53bda37e6a1743840417150b6ee964d46a76547ce6146cdf5b49c15c248726899f0141a213ec67f4f18f08061adc0b45f9c38bf37917f4c1be4260e8fe339247633d7a56ddefb368ddb4543c9887bcc4b8b197556d802e707f1b6df83653c890bf2feeae315937a28b18910c2fd3749fd5732915f2fe3b869189ef34d915d0bedc6e1f308a25e4cffc22960a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b0a0b00f848c04824fe8435370c748363ae00a4f39e9c70b87f7163f5bad267f","proof":"ee55198566bcd2dc12ee78e173908dc5aedaa472c378330a49822d3383818654facf9c1269e5191ea2a3ca6c5ad2132b3f715a6131d5c692846eef2d729f036684e0ead52bad07da46e8d519938f3880735e1af5cb75d890e66afd3ef42fa2484edc49c2c90841cd6a8cbe73924247d64d53d98c8256b8014c8395241d71e70ab6f8898dbcfd360fd4bf824eafc931e9433044ecf5432ac895f2c4076acc3d0af026e74dfd71457f7003862eaa28c62b80c679fb10261b195080d12a7de32101c6320c5940f2b8f28eecf6f3d7a757d4d3bed031a40f053b2d829c83cbcd670e4c4664fca398ef8bb772bf02e829599df1dca02b37fe16810a6bfce2be0d1849d0a608894ff76e0f6d942934815449842ae90b8bca53852e34521821677089580adfe2e272d7714a4ca884f266d40ded432abe8d1f2b47da704006e44579e5005cc722a04cb030666918f36aac76bbde38a654a7e53f27aafe201e2967cae601a0685d78691069df690f62b2dddec83bcb322e9766f2766075a5c309453d70757874e8382cc2950375b4a941a82bc2c8250809955183c71479a872cc85da274a5cbd42474f232c85adeefa19a333103b89552395759ead310c355ca906e5d25d34626b79d900e928420748e60abf38a397ae178f1499594956b2d8629d17782824d0bf05d1c8c810ecdb787ddc6aedcb30a893104530940e5443ea33d0f517074c52b640e111468cd2205bc717ab03676c89f98f9a4f8a57aa3f24738a1d6213d252e116aea117adda2060e3041804eef32ad34840737b360886bc111883b37b44440cca10c1a695943d7ab6a2ba6399a971b686c40bb7f38f8e57cf0c2df05ae3e16d763f81bc9f7383367ee3da48acc1a0338fd31f152a44c7b05add31dc02ac4b401c14c6efeb7575990a64422583d88c03f850dd48483961eb820483f80e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"285dc5bc96163406c23a628ea90195457a13659f0bbe38f0e4406a3f37dd9146","proof":"c8ba000ec3abd911a876f8c29261aa551ae10d1ccd40e2623152cf3a9e8e32612ed4178744e3c8c3b0c321dcc68abdc2caf631cb4ff4bb83f5453d5f9d14d867d6ac581a51b4c4dcdc386870967877096d59bc4e77a78145ed44caedc0a6f22d1887dd45456ff47a02699f4b02ca4c60590465e903baa5fff8b80de12b19dd304dfb47f722a02e080002f16bf88994a99d17c64fa546ed6b6a525fac64a32a0fd51d316167b1992c421d7e1db48a32f1aac39bc7b13fbd1e405cfddc3288d504da8f93c71efa668d9c323a19375a00754daff11414500f43a3b4ebc0c190dc0c62e22f8f6720551058f98a5729488307265ff77c8d07c8fddf8cc9725ea65448a29a3d504a55695d8c8736dcb4e54b9b3ce93d0bb0162c13887d3fa241a1291dbabba00f362a0699e20bb4b4a176e7eb76a7762cf564e041047a0074ad86e64c0c33da2af950da292a7b3dfcedc9e62d021dfb1e062f585fe7ae40749ea8347a2ed3329bf83dc44d17af72075fde1dcda9bf378264ef6f4c7379f6b190606d0ac055e424f5edb06895d5f013c18ba266ff884b76a54da34729a88d0da35d625fd0846d981f50017cccec1bd9952d24d874c149f40da9b0db21347df94312d632a2593344267a52ff1e0d6b663201e66dded0e5cc6fbf3042e1708768e0c709657e627be226c71c4e27f0fdc917e5d8b446ecbe1eed631377365e088a2ddc770ed235102e07de397bc0fcb71205ab346b42f6414185a4e4d2b4e6498d98864044ae6957d33b1ae96a7bc38ebc06bd504b54c7042b5f38dc44b38d1a3d8fbcfd0caa1bde7663f818020dd563d875a87c2749b784f9ca97aa6d1e7b80c1fa2d3278b1b353d382199559089a5851fa7a899910386b59bfb27ef048b0d97059d5660614d95f6b9af93725b765ee1545f56643ba1e120842e6d9c791d1e80e5388e401"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"80eb18ec735d9a03977bf33f3f18e68779f69015b4e7c9c25add52750f4ce621","proof":"10a6825a64b3d1d20475e8dd61a6d4704868b8d505895c9457f2c2edfdd8272cf231ad478888b6ed6e34116ae9a4f64ab2ab777c450751cc5120c1e61ce0f53ae06ce9d75629bcacc96813db3b7d6cd8b2b2872e363ce1a061fd1ddfe63bef0ce432bcbaf5a80e34c3d0bd0c86572cd1c1b79acdebfc91e2ea3938c736b31a0d2402fc711936a80b66df33c80639e4c3349adc670987fbe9487f78eeeac2ce069df9c5cf9af248401bae9a6c4b1c289571ca2acf3a07d6fed1b508b77f9df007762195548ad1afd43226e415f49741af068efede88b8b8de221436a03b489f00b660d4055867b936cf790dd8bbd675491737062eead23c3ce801e7e61425796c5cde5fe2d5530d58833ff0c093bf70a368d3b2eb757659238464bf10320abe1b4a679f1d638df48c1d5a481f2018c5141fcb6c2ccfa584702e601530dac7eb76c8f16edd15179f8d2890a0df4791decdf006a5b49fa6f41e8cbb50f70d86075f22b7e9160c308d0adce0ef571e6753f91905baaa9cd81d74dd29ebbc8c530a1aa8e4da37b049928f592de4a607850b321e5f78874dd9423a22b5295004fb7968be4081ab319018854586f7dbe7542dde729219943d50457c73f8cb86c73b21628886616d8a38903f209e5c548d7dc44e8f98e56704e9ece2bb3b837ca4c3f3009a652f6b058305f28f6d2f1ee51e820cba70204fca9bd110adac23b222fc803d300ed38919bcc96fbf49e13c82f6db81d71a29acbf2d0b2a5923bbb0c4024d2b868523c4f2ff06bcb0953aaae01eff2e161b694f5e217a348c14beccd380e30e500fa653ea1a03fd9ba2a0e3d1af09cbbfa7570df2d7315cdf5855a89c6f8e3e70d9b21887402e722d12288115b567a314ff18b162d44cf6c3fbc4d2cd48cd0e061175262bd55d7cb4957e967ae53b522d78b17dbd8dba589ab346afb276df01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4ea06b9f32034f198e93f0299370aafee736460b6f8eecbf07ab5f565364db45","proof":"f8405d4974e29b4e60a003527bf1038afc20a1714ce55d46d639e7a55f7f1b3c12f69b1bf54a046e797a2c18dfd76eaace7f3883b3cf742da9cacd6f39bef60524438e61c7643d52df7c5b7aa175f21ec5fdd22685680a4319f82fd9d529c060e614c4aadca5cd99c09ae1d7b778602a5e13b0d11ef7a732fd6bbaaf48a71a483620ce7f1d235a0e8ea08fa28391bce7b1bff32175af34ba012192a680fc140d154219dadb60c7c02c1b312b95d895a5e2e69385cb312f5a0903792521e85906ff44dbd07c21cad7488b448f21fa15cec98330671b1b52be535db2d4bf9ab80128ae6bc2b62fa74a3a130f1c4d07bf821485faf88c28971782dd767c3c4f8d545a235267bfd5f0869ae12aa4d0b913a9d4d6210401820130a3d9554931afea44ac5370cc4e32a02175fbd5045c061dc0043a9c3955f9c563b677a7343fc3a34708551029e74338dc72220d208e4048c09600c8930db11747f78dbb166e49086d405addc9663e58f22ffaffac028ec2a449ad4723cfbdbfda1d71872631c96f2718351888efd69e11d36a3ff6b16cede23216f58ac48fd41f7732507e8a8ae51264257d590bdaf9f2452b24c5e83dabf13d71e5734ebe3dc69eecd19985e38b2c7896fdfc638d2101630c399611049e03f34f4f757870dbe6e81409ebdc1eb32640d79cb5f2a59e4040357f573307a59847193897b7e1a948d77c6f950d8bb618041e76367083f30666c012fb7e725bc2ecbfeec5cfec2a4fdb568680ac34d7280857fff4da38e14e3c12beee97934a42cb13438355e28ebb5665a67916c12108560182842a48590a1230545d06e3e7b378f3cd3894392f6fce60d6ce8c1adf01978d5a676b52843d6eaa256d881a39987ddd02b20ed8194c2c32a9b91bfa160899a31e5a907a4a65fb8214ba427ccf37cc9a6d6ef5df29b1f3eb7debee97ad0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f0e88908028b5deb7e0dedcfe32a7af4979d4e2f3eb3ceac4ccebaef611bc710","proof":"1a9d7ea0738494763a19800d83d91fc42df6863b8391c6792d937df9f403822ce6d64c73dd39af41008c073e69c365df62a5b6409c54a5a3bc40fa7ffb1093171a7f9fa364bfcbeea15ef40e65996c7501d5162183af6ec89146cb329ccd171bdcfa63983791982b0335267ce85036bb57434b182fae71933fdaa6a86142d173d4cf68dbd9855aa40492445822358367fe38b5732463da6f8e54704894f15d06f0426d1e5fb64f10f599c258fee1de34ff76761da464619ffd25b57114ca2f07b4f867fc298f402aa944c0b28fda3f4ee1a5a5e1a92632b91303cad4a3625c0c062fb1e1d2d8c857a54d5c7c7c64887c3024370fb7c6bcad7cd08ebe82c75e4222b2e01e0d6b4bf4472fa0c42d67b2b70e1522776b90dff597487ed269c4d268a0b3f54c6c325eee8375627218a9cea5ae8cba7db88d44e0316df047c3a02909aa71cec71ecfc1d6567b653074d9b5640b8485dbb34038c49ad8a4f65ed131357a921803d53b775e6f1ab6b5592ecffaad285060ee3fcb3c08e07f3308dbea3a2e5e73b51029268840d910d6b9fc40770282400dd71ac46f5d2fb8b9b0ba046d0809d81c6b1869552547cf545f0cf65ceb5b2878080e256c99ebf98958852f1e0c34abfd45e378d38566aa86518d29ce91e712e87f4df199aed8c6f029730f57022fdc272051588c011c90cdc4589c01cbe371fd9c631677c23556faebecb159aab8ffe04d81b55545df0ff1c833297ce8d470d411da243f3a86b275f33f660a509cd6e624305d12685183bbb3a31f69651e9a10027f68c88cccfa309a252f08b40d7b9b1b0ee19269f237ee73cadb1e7bd2a57e7c18e1fbab15ea23e3eacd007abfa7517b0dc24357e7d029db0d41df6c9eb69a938764c9aa8c3a131344c80a7f682c087ede9f86a4792e5866bbd4b657c477850963cd6f84bdb5ce4d5bd402"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3287a2e676bcaf87d6d301f6eb9ecd271ea50a5f201187d5d034296083951e20","proof":"78dc086933743fc74e32e97855b0e09e50ba332c2e3895d973c16880c7eafd00a20a27362b2da2d55da87c168682ca0471b3b69f471b203c10110e78cd71c2546048df8468d66eb6c4e7b3709c83cf6f64a7eb195674a0906079f113e1e92a5e46a36842effabc14972a086cca1cd0e99800a9a69b8cba36dad1089ef17ea8658ff4a668840a29fb2dc274e7e93783892e5a7b5ce1279d63c057ccb3c0f89f0d57a22b17777d99b5f13517de0a3d6f37872a3fb3145c6794565324e404acbb01d564719bb23d5a1eb7aadc698463e903b708f6a0f6f918be3c509766ae50ee0f140c33b395d2414e51840da2ec9df1d49c2ed0f4c872b38a92a421741462f03a8e426fc39a88d07e5f710926c4e63bd9369197a76dff862c95cbd98d03f57847d8aac13420cd5d34bdbc7a18a24b2640169c999c1fbeaf1a4d17f2b47001e87f688054b2bb771d747426fb8bacb80c10f6d28565c9ff2705f099a825af46455e766d4e6c9ccaa07fe35c9126b5a627831770fcfea987484468d612f6af19d0371e51ba85de617d3711b9c13aed6a056ccab78ad461c155f2400074d190e7164208ca0279470478769f931ee20be416a7e9875dc6645e9bc1a0491ec10b8ac957946c97211fbfa1e046f18273c2e39ba8cd40b41fb6af7be9d1c8b96e6a33800070d1f819b52072a679c587ba1c85e1699d5e2bd350738b577870ccf713115e5b8c0e26b7e13c27a08f556b06a2369b08ce545e03b5ad9f09c1291a90ff02023064d873e368edf0ba4c5e39b3ef9248c277e60295813599de1e6054e630dd4f3302dcc23ba58f7affa4bc19bd7bd49fd1bd5db78dfe5ac195bcd95b4dc9726e261d44c57f0cbe8ace41515f9555d2b5960db78bec41962a20f347364e64c91a0f49727d89ced7e688b0a3a374847012e62fa574a08efe456d3a140c24b5164209"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f613b9f83e5dc16d7045dd4deced091a16d8ed45fad4a81ce12056c25187e401","proof":"6c975ea7263244e6779d82477dbffd6df0677a195f2cc67adb0f7ccaf949f03408b92b9f0f2694dd058298a0f843af1c5dc92b25974ae22009cbd932761c6070729e8f8002a6ee25e2e8ec5c3464d4cca1236d6c52564523d364769d13a6d661463c888f1ec7084b05af48a6919df558e13363a06a3af5df075009d2642efe39804be1d68a42a87b49eddf720ef55385823e75a8ad7cd842f59263fc89abd10faf159376cc0c7c224460e182f6f5c0a0ee1436e767745c930cae1a41b7674b057d0874ca648660c263c13e9689a6392cf2e8f6a2cc651b225719b2e94b6519084ebf2e27af3123270ef52fdd0216634987fd53fdeb0acf393a5dd380075fd5773acbb5cecebea5989eb2e7a64bc122edf9fdf7a16c6abf6220fb67db1a0777241e76efca5a26154d5d3f4563627ec501148a983e8830065c1543e50c1819fc68128548942eb12edcdb6f3157094220045b9a17d14624bbd67199d06dcd031b61ce2a4204959fe5ef478bcb28c5bd22afa8c40a6c9371a1cab1084bbe3f0bf74936ecc81489bdba2b2c1dc906464f0f9f7cebea21335ba0988abd8bd4252ca36af2388939e675d963f57bd3f5073cc8ff7d2714b40e90c67bea2b858cecd7f552740e16d72cb939fab9ccff9828cba1cf8c88452d79263bcf8254c9ea30c4e0058a222e97c509a7f786f25eec0d477f23695084bb6c67ae1b50140166e51c3d56d6db0992e0e682afa0985c0efca81f819aed5249e2fcb43566dc15f4c1db626ef400078b0da5cabe96a218496a9d84a69b7f243440a736f149685789ebba044a4eae2de5a695e979874bc96c3a6f749d6c3b24cf0445cc773e2f2486c95ad12f58164b592f4e3f69f6a353bd4d129b1e436eacb3f8528cbf59415daecc8de70e69fa74b515cfc97589641c6a9c3c6e647b0e1803121ae69e59914251621d960a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"de3d6ad3e50486703c963cfacc53e07ed1dbd9813d950cbdeff7d1c3e4a9357a","proof":"002ae8962b7e1ea206324b3055ad64f469e0095caeeeecc2999d3d20ec4f162d94ea9652b08eb78f1e8f8f6215731db3ec30db620b0e72269e63506c59a4c22fb2adb207e84a7afdfadf44b4b02e303ae5d359aa370bf12608c6e9af6448c32d64b0ecfb49c52dfc4af4f816741f7c964e8e98ff87a19dc3d04d530cea090c77ee2df6ae5271e30179bfd6a035bc51bc72d95bd356efa0437a2e2dbeb80a6e00a8e8b4cd6756b66baf877a59cc6a9a0d104e144581f56fd82c76670238114e0ba53036ad5ebde8bc877111d5bc3ef8178a1a7c1dc2aa0acd373e0e74cf6c410ab8c27596e545dfe520623e6ad6731c28ba6f3ee7c5f37dc3c6363878f774244548de5c7b17f553ac81c591b7c90ad7dad9bc27697f84f3492c7ec0280ca5164b52a7db932bab71ced84a08051f56edae1e2f364f1daa360194ef28e36348795d3c4f1726bb976c21e46cfee2c81f23bb5be157a24314000f86a64d690500ba79e809dc583ac78b6873e130fc3db07b7906f68e8e1cee14b7c28797eecb5ead120c1f1df5de710ea34f5ab4ce82fdbb5385f4cbb417464443bb214f1bd78aaf7efab390b3ecf855c89aaec4761ea1ed243ae04ed47bb6a2ab92dc772b20c4395a18de99b8a106d796d37b9a1af4875251b8bcb164670401de23ae633636e29824225722584112b4373b6fc0875c4c7cac91cd15151ad758e163d29500e718bc0956b442f139d230972a74651c5b9fd3e5f9e4d3c11ef06f806bfbd0d4a577083e1240b4f2cf937307c8ee1f095e1e756c97909cb03aa6b959c4c9157c96f5023d7eeb5b20724f9feeb6ecc6f28f19aabf203df5df81da77427c2a3dc95ce90459cac3ebd7e37596f36c1723a8bdb0efed2ac257442803d8408d6622b58603800e2f43d4f31a3147662d6d92b7d9187519d055d89535a23f336e36c68d448bd708"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fae5cdc8d5202da4570f4e5cca4dce3e698b5a348c706f2c12c2b3be660ef876","proof":"26321c1c42c4a66a0c0ea0f2b814168752fc31a9b9923902dcdb42c329447b319e9ecfb2d9a15ff8ab3fe2c6873cf0c43567876ffb2396b737f9ad540e87b46980891756099e03743d4da96ed0eb39e632bbb51d7b9955896e9898aa689d00274077cb845fe6d465b110b342e7424b8960f86e7ff3267751dff09cefead3d15f6971308faf3ac70114739f741ce7679fc388a8f0e2e55757287ba471644e100af12ca26dc91cf065f208bd0e85e34bab9eb9c02b314c19c2bfa9b6638e2b0002bedfbe42029842f316797063b38d12d456649a3674d2accac143e444c3e806057e89fb9192574856f35ea5535113a12d35686cdb645dd69bc7b4e58b710cbd502a8b982d132cd8b748cc48914c53e1a746d23c9ddb9fdada746f757703a7202eae36f1e171015c5a64ed5f047100fbdc8c7feee640ad93d16f5df0077da2e40136b702b55a11a9ea1d0ed38b7e71847ff144426de5e1177094e9297eb145640c9ae12c0756461cc47e8cb9cb9cc4db1e925376f7530b05c698c2d1f6094aff0b3a5e21d142324989f31ff53c2ba708ce4623c80905f2c331ecbbbe17784e5a15cad93165f9305089bc062f55b7fb9fce6b1114af4bad67b89c065a198610d475ea3f67897f30c5783df3c22ed24e786695ebac7068e0b2403f1a20f253bd3f1dea78f946a2064463fea7e9fe4331aa8ca1506c4b22b7ba82255ec34af4eadf43be38fe1571c8bc0db71ba1af3f6b199de952d4bfd191a405f2dee405a141ec7e808ef2f2bfa5b7040c2ea03d3082a5f65e23f2135a25e799bbf32527435967088a0fc57205f03f2e31d418663a0fd8857bb0c9a7b91c67a704a13422fbe30129f4b27ca4a7500cd5e29ddd1c247049d3585ef6e22b17b6dc7c84b633b1dde80c3b9ab7dc272f52af6ea59b5463a57d38e6f62be50e9887e2098b5b8527851c05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a0a9a88c18fd675deb8e2adfff7cd977b01c077c27f6291a4a297b8ef86a9f39","proof":"bc38d017a9300580056f8b3c1d23437e33fafcda96ed48ddc9600c1137e68c4cf6923f8845f9264ae2111f8cc040789110f479a4e050c41081c87ee98f988b58eed7c6ee1a8725959efbb7a10ecff3aa07e17a3da4222b677a04d0369857287dc2ae2c26adb8dc2016d9ee67c8f924864ab9524213c83e56babbdcf24b063a3c241a4af92ab1da8e52351b8fa6ff19dad976ef66a995dca96913a3f390bcb706a43ef06c9b7e631e12a48b98c83601aa0bd222abcbdbc198482f4d7f4b5b840c89f8b8d125603b7b64ff23a8b4158d6b62a42c8aa43624df2be84b3d92252403e8bcb01cbcdf323795fda34ad0d94bfecf732590821bcb02a78f225296c07d6eb4f154a6e0c6d92e266a2fa4e55427780c186be4147610c1458c16ba1b20483a60a02d610b5f3051a381de984932c4d42cc60520f3bc11c2d62b269bf83b2a16604cbe918739794989d591addc2daa47ae71a4c097c5706be87afa74ec3add38aedb85c2afe2f09b5ca9aeff1c405fa5e3789ecad8d40909b3a7257da961481a2ad7a74fdc936fcf4b0d024b2addfd3d4b60c000e51a1ae396211b3a81ca901bc291e73ea412484c95b3cd3eedb0b6702b175764988149eb59532f54517c451f1ebb79b16d0227277b147942d595e06e6ac11af070cb8c9470e0c9a1012cdf1556238f7371598ad5d31d145c7524135b011bc28b4cfe281247bd7fe93281b24aeebbe8c2a3671169a3fefd2b7777c2a186d81cb9872dba1320c711d460f3ee0d94bb0ed72b553e085965057a6359bb8d33bdf2540e9ee7a39bf43d3d63cf616a183d08cee380e8caf4f4835427ffb786a979976e958ff7caa2ada5bb4ae5cb3afaf0e43994d643da6923743255a2f879943d5542a152a7b036231aa884c246042bf9eab2420d5fbc4955fa309d6fea70ef27339dc1d29c306a628062aec36c0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1a311cdcb240b0d4a01ccb69be6bf00efe8698977efbd8919381973b40bb126f","proof":"1a38094749a0615aa1cb8eadb456ed75dee986cfd4c31ba47b55f5e9ceaad22eeceaab5335eb707da201ff76f521ba48a1caea3b18cf1eba8f55b8e09e66d776be4a11842849c70fcdeffc4ce407f5e7d9f5bc91100b328a4d007e63483ff3026a7f504ece7c0ef57e5fb34ddaa29d2c47b9cbdbddba0ca4186eca3dad09c06cd18553988d092faac7958080a77c71698a299c12a666beb21d6c6604f91ba208699e4e9bef654f9f48e30bcdced116e762596b0e5dbf980fad2bea22e318d008e9670111fbd8d681df3351203e77bc24e1439b969654a7a9865aa5172754ab029a1d497c8116d6208adebbfa5328e38735f0e6f74a17cc5f3c6511ecc8f851106cb3c944569fa90a6b9902fd58e20c82571977d4c94c2479802f92846290b15da29e149e25ec7f2659731493b121b14aab19a2dae364c76d6ffb25787a466d3956c5f9bc4af4ead5c0f167edffb6e5d0eae3e21f7409c19c25b5259ad6f66e71f2f8dd60161256ed8d221f86b24f94aaee27314e0d27ef3a673191d11d6c646c021fe85444ff7bda70f80332c20a4b957e1a3ab92648a7bea5b9a6e088e71e0b60234e147c936fc603f3b4d3079c791586804fb73e50400a14ea6db52780d173d2636157cdb0d668962df223a5421f7dad8d734ef797ce040dbfef4d6503cb3858db91c07a4371042bd2898580a262e6563dece427005f48655f6e33bdcdbf6ccacf3cab2ffaaace31b71434e92c7e83576f46e4eacecaef2e8b62ceac70547f44ebc97e3b49dfe968b0034f412e884ff0567288ca87362144691c1bccac282f2a242a3b20bc056fda5cf8991a8599cfa94a917df55d32ffc946cb7a320dfc005e69e3c3c4c28c587f3821a0320a17f42b306a5f294d6ea2e2fc7ceb0ab33402ac02e248a75d50a0b66208e41b729051797d842d8549a1af6537904a214c3a07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2c9ec339c4918742262c6d17f862d6bc6da32c1080a9766f6079cdfb0a7e0a01","proof":"7e975bbc6b9ce338aa8d79de92101996e94f90c56d3aa6421c9c832fa3bc82260c7e8072a9c459b6a726e6652de636e2af6b89996b69d3f39b0cd6a1913ae477a25808a46d2a4cd9cbb66ec170baf9b510bbd42d2291aaf72f1037c5810fa6570298c76ea6a1b4424e9330511d33297c60c39649b4707a1e5dd77909380da95f34fc47ef7a66406c70f64f84d5055b0aef37e5dcb4b417c03706fde279c7500a4f8080bf9e8734240f039d887d3ef2d6330e2ad62938f6fe27210a59944e4d0f75109dd518d773310e0b29fcd2f1d4cbfe491be030240590d18053612229b30af2252da61ffeb8defc2de9aad4438d51815af30d227b58f6105c4ca3f3273f6558c90aeecaa22e914b22a0a81866d352db7eb2063643043dd9a02677475dca59b6958d526ac4f680ffef9c89c770a23966a076238054a361a45d4a365c23fa66c6689aaff67e63beb24253e90956c2fe0f90fbecc753dd679a8310895dfc451488befcf82ca2b03a98f534f2c4f93a4015b16c3e77fcef8acc38c7eab298eb6e2c7005f640090896c30ecfa4c46a682c18ca598427c9236e2acd0a671e0b693714bae80161e80ca2bd7002ff9b76037b6f37f2f2e8a0d6f25482c5f89d6b1a0902ab2157550d621ef6717336b69178428e20cc423dd947bfab79a404aadae16a486fd043560025b1fe7383bab6a8181f55cbcaff082280186e1f8cd324226b0414d0335e7195c8dc089559df54dc6c294f00e3ec244de240ec1f4d78468ef86a50e49b7cdaa504e21b4350a6b2c01a44127970c7920d1a8555ed75b7f095667d5e2a375a64446f571644f011056c6c0cadd11bae013bad53181a506beb0c4e0c830d842e82ad23cec8414245363f68bad1c9f6376a209e7df7db9b28b15d4201fb1368bf3920f8bb4ad9787d945f8655c79c579accf4f1339c201e5b9d9cd301"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2cb612fd12721672ab91793b925fa2b4f737c668acce12bc229d22a6c161d514","proof":"a227fe787fbb76423b095b460a9d2f8d831c4f56efdd49575f4322ea35c507389cd22c111ebfdc1cdd4e76e10e2be1ea15004d705c0211691d5e1fdf019f1c383454c97078ffe642f6a999a1ca5019d7e1f638c86cf252f360def9870c218f7904c802836aa55d60b786b514ce11920c713bc9fbcc6cdbf155ce9d7b2233264b6c4c944c02a423fd423eb336c1a197ebdea6bf3effdd9c70480aa120466aff033a0dc663ec2ae7f00a909f3b665167c70a24442f6d01743457509772dd747a04ba6c4fc73f0c367c8a0735194ab52a5e1bb9d4293d3690f15dac123a261f180a7e2684d00a8f3189730de52a08294f8a835f968b975b164f51fac3f95390b656a617480d591a62b33b499e84924bfb0c2584d8204b3970d7969d4ddf9944fe084ce7a2fe364fd2b0778387ad1bddd8aa965180d3c597fafc4c1239f2edd10b019056475087914f6ab83b59b781b5225a8f9e78dd784f8156456e98b743ee68115cbbd691d231164aebfe84956b3aad99e0f8e2a3ace22257cdc442f07c642920586297b84952780cb763163f16a4cc0ed9fc93cb2c0db29593127aa6ce073748bc4df69f5e1cab9d7b49b22b2494cab06bfc52231882f121670af750089def2f324cc603f1b2bf870014a3f3b0b04324a7777b8783e649a41f4c78cd1840a72d462be652841f7a2ef6b7f7d8a4d1a49f3d732c78c1fd3c836fee68c1c945a644d8f6b738a3040f0ea3e27513691e1292f8c64c26483ffcc09773f08d2181c604769672bc277bd3ba4bd5e363a830ef31ab69060847232bc73c23878c6d5d4904fade7bf12ce5efb978e0ca2200241c5fc67734c69cfef5841c2373e372cfc5241c9db12f7805e4a0682921f28d2ffa25461ba3cb1d0bee47642bc78ace997b01c81b8e446644261a91ce34001a9c9b572208531f835005d9522f432118f83609"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d877f9dabfc1fc4a2dd14a668a86ad512da897df9c55066e99282dd7c63bda4e","proof":"1a445507cf8527027703203ceda6db95288faaafc2febf54cf7b2dc6701fb42a20400bca86cc8a1ae65836d9c623d3400da81406ec7a6c90da6187858c77de50f63c6c091705a15822fd193cc798b22e83d8c0b61d0afc8b4ec9181ab7c5b3177234c541741baf1947892ecf054d1e01c4ca69307236b744f61ca71b7997406b25d48b75d75a5ac938839707027220c99bc4d5462273fad5c4cc46eb8ce5be0fdc67682dca2f0b319b721887b9b3896cce65a87f5d87198bdafff69017c7630c785cfc8f4ff9c2428472696cef94a309472d5554b9a479dd6fc70f9b9dc43e05de8b59bfc28fdd862f9be02de7f5fa7e05abd6101e3bfbaa7d5cfb2cfd89725930bebd16d580b5d7af28cd74ad368cf1747e277869ebb70e741d314dc850425916f3f197cf36cd751e3036e51a694a5585804a5553b688f9b52b629474332626f69e0a990e136528198aebf77de75309a92fdc05fd4b50ec954172be1b8e9a733830efcdba08617dbb7fe5bfa017a315a1b9137b893e7cd7649905d9b86df81b2c3370eebaaa9b1b8a82a21cbff9944f17a38216188d628ab983e1264d76017d4626ba4c1f45624395c739277dccb263ca355cc89b5c95601b73e3ec0e42ae019e0d6eff1d88b8844fac2401be5d5aa82243221ae8e23e7512b63b7aff9e963de2cf5d1c5c7321d4c5adb1943689e8993453bd36656ef340bf4541afae8d920efce09c505095bd8ec39cd1019a0f13f5b0999c84eccddd08120c8b83000e4a127c5d8274572ca10a0f357fc330f7a17b525e5a0e9ff4068e3a1796c626ddb56d489310af35975d6d75de7184f5985f1466b744af45a4e6ce34eddc797c20ff3b3cca42d8d992e4f8cd6f83892c3bea679c1ff8c3b7934c8e02443815aa36c50781f679a49fed7c210c746fb86623abc1f039116b77f03a732a3a6e99f0c2340b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e8a754a2d4251482f81371444a3491220fc81cd777916913afd69750bc86b92a","proof":"fadba903e11f647a906967e1490905728b2222259c6610c1d3c6017243b47954f6045314037723e4cbfc74d5d12ba09e0c325c1a69208c39568fed21d997325f36d03ded314c90fdefbd35b4ceb794a79723603cfae4b7c72b7f6f320bc3ef1db03fe4baf5900365e0077c9709f7c769c129e67e3e57188a665ab3f9c6730419bb4b12e7e3bc309157d1c124be2d8038cba23fb2e5029de93e6e031dd5543a0941cc88de2715be3e246ec4db436c786515fa3a32baa1516e549ce791c2f53808504c06c02a8fd6c6ba865cf6e3bfe8a8d6bcd987e0df64b397f1946af70c94031ef8a92604ad6e59567e6ad5019fb78ff3c846a66f2f6d42813261d3bac2a56a58359cdf33350c7ed944b18668e1c5d7cc5a91934e71cac354ec6049c044965ed018a06015c51240131906aa5cde069ba4d530a3d2905784018f56ee052284773af2dabc906c3fb647147b651f288c57d91868fb43d1b5431c32600f5b17086040aa3071d7c3720240d11056958f7e425c612927bcbb67589a2eab54a946b5760ca98b5eaf98e000c9b49050a91a5b1cc776a19194e3aa2888d7eee0df09eb3ae4174a8d415c29d44d72c18d403619deb532610cc29f45228d98e9000431be12369e61ed13d8a79430c08559aa88e628dd2a750c7cb1ea55b79e82241658b42fdc014a822d583068f909e1cc70f5768484a99145e4c0e39a245409d37220b32e14b9cf10a6b295510140792ee7b296a740190d5748eae7c4231ba0021e7a807a46bad481bbb7df6666e70888917aa63f7f4373ded433d87c5009161ff9dbf66b163f6e4de54e1512dd4597df5da60f3f1ff0ab139e709d6d960f2bb4e688c152f24bc8f570c9c63e8f3cf7a71992c30e0b803b149d27f4691b746b8f87d7a0097b046c1b01288b37cf4b2259712e3f7cb4d8cc97e7cbed4ec007b363d166d90e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fa875fbeb5f91e222a573df235be4211667c5a2e42bbc6526e35a819a56a8862","proof":"f431aec79fbf297ce1056b88e2a671a26ea9cf0eab57a0181fc7005100cae4551cedd5f804e2466464c01398888e3eeaf99d7920a436bef7fa61129bb3a257676ec658bf2fdf49189ca0bbe89f0ef5a5b5fe9c918e5f6f1f1176dfbe911689378ee7d11f20eb250550b07375175b82bbabdcdb46ea0c439533717f2da7f3272e3818c9af066eb6b7ccf8d5744499b84a0663fcb93b876d5f80c64ed529a42900eb7bd446dfd19a1e451ef9c9697439ba87a4c29759ee92e86c133a196353b700f5a0d47019e9b293381538bcfcedfd59b88eeee03c52d016afd2bb052d11fe07fc58b749a76d1a7f1e6882339332ce42c5b5c5fbe38dfd4e41425de613aac9441017b88d873eb6a8c5185175acc62bb51b5eaeac0d468e4212bc338a537f456d9e6225efaa82be5761578a3738bbf78f5fb54523f14d951a4b8d13016241cd62c4f68b44fe569bee821dfb506a6cd2bc2c0b2a4ae171e1dcd6c3a1d1134e7f673c3e7cf0488233af8157158831b2d63760ceb397b50fa9e89b386cbbaf37562c7abba58655a454c4a99f313e385e8ab79f1f9db807cf732dcc2fe4bc6ce51c086e6bfe8df5ee2ce0780a038a7940ba2ebcf00b80a2b28268fb017e3beba2ca6334db5b56b8b34b70cacebe736e6221f9800382132c7d2db1208971e8a3da1f61ce54dfaa393e47ce0c98e981acebfddaf21da6a4a36c830eaed20f5ba3d7c045e2026219ed93fcbb791d430c2283a78eb2aaf9764a9183446cdc09ca1f28151fa87bbfbeb485dd00fbf1db5f7ff62be1d4b76a6e846b42ba18836a8350f80973b42113b6d58c207f5dd97165cc2477f7ed29699d9770a11b17f5a249296f805ddf7c18a5c4c8d8ac272604149c29157e21b2093ba3236fcce2d8ee1a41e9cc0434f713bb1c8420d2663aab69b62aa9bd37147cfcd2f03704912de2e51ff70506"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b635afde5239f719ac566b982c7b1099960462f6e8d33f7bba137cadf61a8a48","proof":"b0f2e2bc0f83c38d262731078c54ec3dae18aa582bb8f8b5466a8fce5f0b9f1bfaf698d457b391e854bea28b668c5e8135591fc675ef75d3fdbc03457f1d425ed62c67e5b388800625ade33f7769197e2a895dcef7ea413e97cd625cd11f820b02d4d7424f5fa4a714e4eb4dab5a48921d74c7a1f433b1daff202296b74324565d329e529575c4dea828edd462ac694133b8008ea42699af359ab1f0cc6de9001fee0346d580ef0816c80d88901eb7ae9313b39600576ce6939b09d9bcec3e01206504da14916d6d2cfebe36f8571fe7eca44c3863c66355ddc5fd8625a9110ee069cb7dea0b43d69114f6b58658ef11da513ad7662b65c2318c2a66d257e755947652c46e6f85b2e55c152adc02b23e96d1d9e02ad78bfd8e0c7e648010656dea59a42f6ce73a172ae4cab15f8329192949c3ae604fc25b4310f8263bcba241a650eace3e5a55566bfe2b44ba4b8858f59f9e2ddcc427f7e1b8c3563cd83b6bc62d5948e25212f60990eda33b4db0b4d43840a17b78e0464c1a256451b4423a8e07c8f84a0b7663123ee0fdd7d06bd57a580126f722dcefeb8403a9d5a66c39de841758705121404a498ecc624a8fc1bdd5f06aa80a2788d7e4e6105ad4fe3de87ea19f11cff5bda3e1a26aa0dcfa3f15522c679d5d79611d81d6a17342b81540f50fa7b33a73660685be0eab7ac98500c59b2759704aea604c300164deb6512471cb041af4906d9ac301563184eda4603434d6bd5105ef46278ff5789a8144603feecc04b071e23d5e5eb3b990356715b62552a4287925163dea5cbb13f6476ef13a4cb72ebde6010277e6f9ba91b62abf2666cb3f1442f8ec26ee38164a6f91cadd69e97555038cda163f0e9ed2a50d1de0b89eff370f8a9ab732a4e3fa0b9b8e80e67ad22d3497ff6ef5a07da0bcec461482f983673376f1e2023a426e0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b2071817e82ce913615e532a9439236d9f5afaeff5770f1224442c82bb903318","proof":"24501eb67db58a79e779715d7d74d8a3d4fa5eff49f56ad2ab1237f9b6ad2c05cef0ec3affc4ce4ddf1556d34225f6dfb3102680edcc8aad5bcb1e6ecaaa8f453610472744be343637b3562c69c4cbb8bac36327487547c9a609370c053637266876e6bfe9ee4d941890219392a5bc13b1026e34d76bc094aef327ed2b36a0558082a0e161452b7183740897e66ceb0917a5497afbdb85e3d33c0b8ce28d0000c4496f4ce60954e9d099ad231ab2b9fa7198a57ff6be2bbc5fdcb0aa6f09890977298d47ef8386ed393c150e3371c1567b1124de1a10f244a686427575a17d0970e07b9d6172effe04e3a40132ee41497aebe3276c4ca74ea97071bc3bf93e5a0cbb587613cf72c508900ff992956c47de5207a2fa64cbedba2a49ac23c5cc2e2079265836cb983d30d5f1dcc9e6b3fc7e92ecc7c2fbd0b035ccb74681e0d72e04e5ea1997d641b3dd22313c43a4d1de423b52fa8730e049d3e73381c23bf613ac52250acde5d6708917611d329c48ff582dfd7e492162404bc1afba44bd162830cded6b3fb9bd1bdc51f64d347989bd2d0a64ff6964a8f669d026c5662d7b4ca2d931527bc1bdab47f80d5e55628aaf3845aece01b43515640259813c363c6b1408acae1a8e6d4a5cd6c2d567f266f5232fb767df178a70834073306bd8bf34ceedb8301e26de25fb6493b7d954b05f31c3744895b910d65180edeecb3be62d180614e91918079bbac0fe7e00b37789e2c4093ee630c6243221f53a99df2e5cfef2415aca24f73dccaa92f39589f294e8162f00c340412d1ce090d06f750f07103d1a52a82883c90229bd84f586cc42a831b27b93b55ea3e369caf1815ab25038d3c6e583da17c2487ffe45edc35ebff9b88a5b0c74c74b2deeea9d3528890f8fb7dce10361aaec6b740a960cd961bb324d1ab710511f091646f53b67a55002"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6ed478b5bb7a5aa819fa2a3edb8cd8664893bdffe5652a43f49d19946e087766","proof":"ecefce516a7284df5c1e8be88062a8f68eaf3332a63e8d25c66926e7e5c45825a63adef4550ed9cd33a1b1fd2832e4954b53785cd90b90c7d6744a30833c5508728c7ad2465b175ce67ea6018571b2ba3a2a1f673a071fda3cd85a0a4f3a3c17568f5ca82b2abaa65365a28c60b50580e69a8a5d7534308cafa3b73d93b40f7b427e21c9199ac5d2783d1766f53c293c76eba423a21ea50b5767e33be43aea0ba02e0d2b5f783f09ee5c2759b36a684fca6f174e31843d1355666d11ba715e04a734ed8312336d08a2907dcdaf2c6a83b8726aa5309a0e105dd99447c7c43000bc0c623b2f8d19e8753c4884fec8893c8d3a3d3812fe8f1b297222aa6903fb1ecac2385d3f60e37d0854d1d68772242a2f07fb45ef21714d05690cd324a8f57abcd5087d856168ef564a50c7bc70d1034d9f081eb4bf668f9019846083920b1302f3b39954a6e0fcdc59289c0db456fc2f46b5263b5823290f7fb86cadf3e2635c4251687d585c74de5169c9a326b00115d62d88f49f681be7a74a89595a3f15746eb9b4f26371b4ef3d0bf868dac7a817c346418c494db5dc332c6b3b303823d837c2e4120bc68d65a72c2b12b95254868c504da884d17fb261f8d6b69dcb64bec4fa4221b07735a7894a3621f7460fe405e11f9769eb6332ac0fe2c085886126cd3d9ef99fbf06187f75c73f8bdc509ad07ebaa68af23b6d5586dd2976b27d3e0b9a85c62c3e2d89a2276bd69479a5b816df58435966ef66290e7499cdeb6a70cff79f9db2ffdc3fea21e7b95a724f0eea0e1c8624353646b8eeff5867cb0d0ab59f2aeeb50a7bf3350c54758bc0cc478fcdb8c677355ca9aec2776f7088540a19b3d69a50c48689b4ad10077c1035c36649b1f93dcffac7c6f36ebe326c05dc0d6595bc478e1a4a2401f730f14a4a5f2b640061d3a49a4e23db76fbefe70a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"82cbe26dd646b6465cf5fb416d0bf34dedb4da6ef6969142cc9a364e8e13ca14","proof":"b05c7a0685c454fe59b3f831fefb040cbeed9d5f098bc45b3ab21b7de89b4a555c46cf667ce512de61d3bfb42597c34b1bc6e6480831f0a875c249614defdd52dc9dfa54156382b5f72e84a7200c5c935e56dac76de321865898f3befb623276cc9ff44cf6ed93683158bf5aeadad1089b3b3d5b03298cc527f1b2ca077800029ad31b544e531e12027f5d6e574e71171d574775f14f462bb51263753528540ad59abbd25cb8414f31e949efa4ddbf0a2f120db3e6ca49741681c3e6ee4e9a0563995d34bf4b4d37d6da7bcb03cfa9013c60a2402da2e66b41a9ff6c8b68ec03ecc6c497334a61b8b2f7a83b69aa78c8a3ae57b1a668403b4818789a777c2f53968c430c5d691d2971674d2df77d527528d145c2725baf420f666621f000937c2e455ac7258bdccfdd03c1f29ba4be0221194935cca96972ceff7dc2301043519671ea1af209c9b20529da0cb701db7debd41d3dc827c0c7c0caecd56722c473cc733a9706f4c0838a6f42510923eef8f49353c6ab0f85a84c24723a67f643153cc9ceb22545ccdbc8a8ff83cac20174a38ea8b814f1dbe5ea696df3c60fe0687c4710c625fdf6855eb2bdc2ce3d75b0d2977025372dafe70693c32ad2eca36a72435261ff58dfe79df4acfa34c334eeb397f362e7c9543bd5ba90a392bba57146922748707efd5c920019be5ca24f2ac03710973f7bde3c46760e3aedfe465fe4248942cc42cefa7b0c472959647bf66017c30ca4a43d9f7bdf6e9f77209f698247f538614da141106528de1d465e04728af1da1e67625e878030243310b57156332e01dc90aa0647e14e423e576e7e41b922554905b56f1628b3724a22253175b9b338090e3b750fcb32fa72e1f3eb9dca7af5e01018d1782c76eee797ee0d1266fc7e7b31ed8957879c22f1c1c3f5516fb4da724eb0fcffc5ced4b2774309"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c4ac7e985dad0a54abbca2f322600053f2ee1d419229e045d888b70d9163cf26","proof":"46bf088a602ccece98b603a58cbca83c19d75d8484b422ec252177f78987b55dde571529c142d476cfccc724dea22bc9903ea8fa80f088959744ecb6e0f89f29c872cbc6c29a8fbd35882ea8d27a30a17ba296989acd4e17a1bcea200fad7f6eaa9648a6cc40e05b53d1b224f026ef27095e1d186078953cf94fb8cca83d2f4822749827d103a68763c177b2c154298ce94bba4151dfc7661a42e08c87e5af0b34e97a20b17c4523345864da303509034b27027d7041fa354182c669a5f8c300fd83fca522c9ac2b884445aaa67f4069bbd482cbb1a46ed35842719c8d6b54049022d88d6b833900ad03ea6c034eb51e9f0b3aacd65de835e253bf0f59a1a23830f985a340762671e853d6bff1e54f2a2550c69c81bdffaa030b6e8c46c64d0c50bb195e707e1aac50ae16da92d02e07b8859b5d4cb367a28dbca9536dcf0e655a8fb880104a0c62de59de2f1f001baedbd23f3e8a3e64bb2272da58f96de157e880e88044963c6670536eca32347494381510aca1106f5fb233648e9f800a4bea2245cbe4a986472bd5d1bebd51c01fcd7b42624a1e90f008046cd013e03e6b74bd50a072a0a8823f8d2e36f883ab3ddf70f97eb1f600702cd99c1e25d9f30724d2a24b22997b5e5c866ea99d1b14162b61dc76351855cca863736a43bb381d18ba22e339d6ad1ee95bd2629e84ef012aff009d707a2a5d864f4cafd5e2b60814febd225cb3ffae0fa70aac38b48accfa8a3df3fef848cd2659008d94d1ad30162cba322272f2d04e33307849a68d6ca52791702a4631fa3d396b64b1a23d744e948310978e68b4138f5591853a4bd20e7aacb230db406c77ce0d0efef4e22e4fb08e123c4f943ea76b2ca5bd9ac1ceafbe87577cfb99b0b1c9d174775c620c8c2df99f65a204e563b7f6eeb1162f2675716ce740d18755adc8bd85aa190f02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f6ccaa99ec9688c7cf561561e93c1c48032dbd621209e9316dd143a832b8c779","proof":"4ec0486f04059ba65e3fab027808fda8b10afa3043457e3ef0c93ed8a18eb31ea6a9f3442bc9876235c51044222dc3c84de391346bd7c226f7cacebd2f80aa023e3bddcb776a430d4d02fb4fe5b81f202620f5acbccc22048c1354513cc8a2136ccb9d1b7d7b8f7b64ef0b0d2b1e7f4cdadfeb0870dcdade9cb1c0711efef5070133e4735f152348dc654c03238d82006f29635672cd4269e4971c50f8f2790d321c9553d2af1e30c7831ec628b61409400a38a7df0e101476fb2965e3d0c40bb93fdb08e4587336e94f785ab2d98ac699289eea8821214544f712b6184a6d060a16a8ab1b4edfb3b4258fba381fd8212eae06c8f113494b3edcf532ae059b49a278fc35bf869c874aabe27b5b451601c971469c6534ab13dc10c17dacd0dc2c5ca1c0750c79f06de014bbb51e846d6811a2657399e1dd3e7ae113ceb16d7a432039c0ff4a15d119546a9dff74cb20e2dfb54de8647fa6fca252d952fb50115e2c9c1acb33146e922120f836daae8789ed92af23743ec32ebe77eb4047ba2e37f0af3188d5e1c30f05291acd537c6809513522502aee91378966dacc2edb00510230d44633f2be5049634aacceeae9ad5c4d5e9f710f09acff5afd2f223ca95ef6f9803436adc74a72393276f99679a6aa9ea4abb1c692bfea5018d8d104e85aaeb170a95f6ee4b8ca12948c03837cd4da364188bb36fe7bf2935ba9921236367c57cc9ff301fbf2fc8a66b8957c510db967fc7e5796ca12d688af05a8a5084b66a20e996e7ad899dd599b2388a0b151294b5dd63c5d140d53be629768c7f46e02904cbd49b35676f0574768940b7bc4c1271394148d509e3702dc5986849517a5679b73eec8a46f82f8c40164160ae4ce678cbe0068dd9f797e3d734c6f270668705d70f86b8ce6a8ee295c45be2a6df6f7ab8acda280a7f5cdce591ec4a70e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ec891e0a8c6782c4175b2acea98fc4660bf71d60ec5cacc60f38ce3a92947d26","proof":"38d82540ddf0ce41390b92a8f368efa1dba98bac4f81acd44e58da882b1de103e46ccafd41ba99e10ccd3cb03d324b82e7508aad36821bfc7b8a12bb5880ef3434a0d08913710a8f77224a3e975d6e1cd13bb4a2cdac006f06cccbdd45b07d1bc8ac386953f8172fb9920e53e65930e7260d69db37126dc27f19c759b9d4395d35d11775ee1cd6d5b35d5ed25b32eb235d3906b14908cfc2e581bb3b22306f0cd5370010efe24bbdd91776900465fde88932be083b24c5351d1c1b37d8b4820d33c09cf0f91a982c128f700e816c6ca7e0c28425d3f87c65ff528b47ee994f0aceaea77652261ac46af1feb71b7a4cdb3ebcbdf8ba19e88429087974a1e42f3dd4f31991fa676fa9e09c7f07046b92b1d4893602bd03790da576a7f36df5b60f4e41d96a807d38ff09d82402de3185f28f44e0928a845dfebdd758b0bea50119d6ce69be280e11b181b97471ca74f6e6a20c53ac708ebb714e572d3888453c2498d33ba092cc0666faeeba72f6c918d1000f9ed2589ab478d3602c255186c05282d1637303c4c3c174edf46e9adcc9de3ddc49b56ca1eb69f8ff544b0f36fb490ea3dc400fcf04bfb684e22e8c2678b065a4c3be7e9f5d055853a74a95d626477a1d39aaecabc97c5883f968c009152ffbaa4ddde961b0159f6f85b53bf7940a5c0cbe4db1113ee6aae9a06b6bdc79930c5624d05ad58372d5776ead0dad0a7e2abe857c940fadac3d648240d920502cb5370b16e0a66b1c859b5b3f139b194a4613e8c094e40a7dfb8cc70aee41c5eda6339627f0aa8f0f934ea448a3b5937e408f4c794592d1cce96f8e60f2ca0e059830d3983592f40e28418f91a4dedb5e0bb5b17d5ed1c84dcf90b16ce9aa3cdbaa97e550833f873b18db95de32dcfb0ffcac3e828aeffb36fd4c7a1d28ffa73ba93463d7113bf9729220c1f91c562608"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2a0d855eead175a7380aa9a72b0b9fc975b8b39b86e88268eab7179731fa621e","proof":"06f566eeb6e6b990ce2446a28c598fca89a100cc98a5e221342bcee119688123cc3ed446101a2652531b29e47f45523d37ab5a020df582d704456ab6412b6d3f4ac9ec6ce534971e12cfb91c9c325be91852a79ab3b83264ffea1961a4b64e3d8234f09720d1223f56427e867c2ae74326aff40d1c72929ad5604969e8bdb127b9f7c16a417643b498e54b92f527154dde256a87bca8adc1c4628b3f12efa101c036112f0505fc0defeace4ed18909aa5f3fe205c2dd72f43d1e57a7573d720615131aaf7a355c9ef28f29a8683880cdfbb1601cb670f2c5dc1486dea1b08f0ac89c7a374303e1d6ec16d3ef16f52b72c5a165f5c7e2040314e8c811222743537ecbe868fc6ff1d376aefcdb74bca170abc2c56c88027032953efa944418ad2a0e47211713ba0d60d8c1daf0f7910b628aad51b32b2e53013b862c556410dd62c0e8082573bd9faa0e50b6d0d9da88e4af9cedd8ea4449f75ab16f40e17d155570abf2c455881ef6030c3b1213c0ee62af665fef742b756306203d1e95511f1aa4c3174c32f692a956011493b1822a7e626e7786c7762ccf24d83c6b9496d849ae0d62c6382386871726dd046e681e1b36c2168d088764d1aed0ca2a2356cf1de4ba32ced3c2127a32c76b84b5eed51cc098e2efa799c432560ba0b44399d574be2d538c3211187258e043c84ed1fdde4e51270d79f3730a4cb47979c4dee736fce2c82132f06b64f1657fee50ec4ee663ca16422f5222e1e1c7916cb410497dfacadc56361a64049cf62996f98665623eb0bf0582dfd36e9d1e5d94b30f6028a44c3b06d8ac9cf36a97be41002152aa5db7b1d8298be970dd377499b234165a6ad29a8304123d0769674bcde161394d27179c3faa6135d76d9b0e6f50de1a06857fcd391764b98bf69382db4baebbd5e8d09cc6f77343f7a1a94c0e441b840e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"74455230a46e9312396247bcfeec6271e0c3b7a5ca1801b782b8ba0f31255b56","proof":"6230660b680d1386df83b287fbf374f2ced18685fe2bb6f6a6a9701d110e35253a7c28e7a13bd4e906307a44c0691e119bebabe43a7e8cde8bb78d177ab4d745065fec4a1d14be83202a65adf072d03f2a147ef0bef88b3dfac64a2c187bf60cc23e3d54c649ae7ff7b00da94f2adf672df0f859806407ba2e671feaa83b8f07f4918ec54dbd9b38f5e2ea175d122c775956169ac2cbef9c6bf0b85ada68280c0988c04a017282a9cbaa36de89df98ab9d252377eef9e0d0ced4d13e2e6cc60a41b167589b26c3cfaea90899c7632ff9e55ec30822e556b1cb726a57b2a4040238811c2ef96e458fc333db4ae5e4fe7fae3bf2887b27d1036e4a2015e78a741f508433c6004604c51215c2683f345d57caa4c12edf652a6191ac36957cf969271ceeac75a9a706f45255e929a55b8c9f33befd6a9436119499e7e134cda5257a68f968a5e6665f3fd723cf97137f9e27d4a7ddd9dd573d588c24b9063d60241be6d5d61f85f3b3133dec6279c33f6ebbb46b38cc523574c60704db75a48522614ea94d23f8a8134f318b60360cbba252b5b2c5b717bda6e3db9dcbee7ca288061265f3513d8c16cf7efaa2b6034c43db92fd135019cb3ce0b13629975ce4fe4768b650f6b5cd476f310d0d4c7a8c02547f649e3edcd62db6a4ac2ea76175fd11e8b84326445891f9b4f04155881ba6fb22a5e1890c6c250270906007397b393feec82f395c6d18d82904054e2484eeb29ca6d4a5bf6f056ba36326f7b8b8e50290fd4c577a9b7eb8850637121eb9d178821baf153a28601ab4105b07dfe136539403f217b95e2d3b91d96f287875ab3b8931b0da59816a4e5ed7bc31a29ea81d4f63bd188471a3d4783be94fc52617bbef0f8eb217d3fe3a1d71b0917067400e80b20581b5eeba24e94a0e974343430f0fdb635c6a6be6aa93225fab032a6b02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"36611dc999f2b0ef3d957e6f1ff9cdca7b9f51d1787038d8eebb258db0515823","proof":"f61cd576823f93858ac87e2dfcc642a9334057b299604da211665d3c5bd2d1676ac5ecc6ce5608f2f72808ff90a8ce2f23d514f1dbb9df42b0657a467620dd4b3ae92c4e502022f0610f0ce6d0cf6a53a53a12a67c0ecac0a6f28a076366f47e1031eababbf117f6a16b874b2eabc6d835290cfe4e5af837f0de10fcb3612f3770150ef4da02fe17c84439118aecfce2b2dfd8d4713a49ae03e00c18d6a5c30d99220e7de83d033058fee365e2e678bc6319d87868c19cbe565a1d0308f8b501163f73f966fd61556eaca8c27733f2cccf38be7dbf30e389b1a6721dbe7cff0dc2de1317ed1debe2031b68dcf508bc16efa7f79f855355eba22bc10d2c989972bcd73794f8ea304c95da565f2742cfb3f858dd36a0cdf220cc6ad1ffb846db0a989352271dc50ac5e1b9742f10c2e21f833b76a058fe14a4d91fe2a4092b32175ef09d5fd1d9b2e7702902e59288f8c35b50522f2bc3be539af855eda27c9252826c2ac48b1aa792001726b90e8deb946fa724b26e6e21c213cbbf3f097efd009eefd15dd8afdabbaa40d45604f2928b51876c6f2ae03bfe4a1e0b5fd1ed5a787cc12fe019b64ea913d152d7ca8bcd16c66e078bcd817a856192b191fc9c181d06f36cf8ac2c74bac947f934579adfadcb15ad51ca039342a1316ac3be1a1d3e7218ed8af31e27d5ab49ec6464f6723dc4687537a0dadbc1b5b6929c09ef487dd033c0b04200a80ca4e44b95f6c3948405b4fbc23e85a261fd371c2fe1c8076b9282c16b85633ff9dc7441ca087613ea21eb4a3d93ba4ae2476020ff0deeeb7d029eb1c983b33cdb42d684d3822b48ada78dfbafcfcc5e4fb561ad3e01a69b2f240f6f8f31aa970f3129a534ae2da8bb7f0dcc1ab6c9902f780424a7f58f240f22e3fea468603e8070c448e7a84dd080bbec04d1c983c59b4478dbccd6e11905"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"822bf5c8a6c06dc6c94460afe331259503664463446e060dc3279c2df177b10a","proof":"924ec26a1ad75bdd6eb15e4a9463d5fba077738465cb2decc4ed593164c7cb046068b3cac58383d2f16561c8ed9f17eb4861c67c873dc355bfdc7b8489a785054aae211de90420ddeba9e06b77c8c8345e686244b8647f784187fca75d158f04022e3a35aea877cca95ff3c7b0afd243dad96fb04d32f45922d477af8964cd36c1ea11a5f3d1335b6f6a4511ca7c85107e29679d8d0cc9114a025598680d5f0e0316195314a3a03887363ac350ad91f8b2e9645b7d86bd21d0bb8c6390f1880c08a707ee68f0059a056119ce2fec862718609720fe16a1dd90870d4cf24280079247d40752b00fe942eed2fc47643bf76d6d9867128e1ee7dc8cfe499b15aa026c840c912ecff90c9fe6794735310c98adf9023021bf25fe7c6f12f918a53e4212211f74fcea948fd6e983c61d9d34829b7db680c996f510a1ac5d42b0c33a3146f3a154264cec261b908756177fdc0736657bb718f2d343c61577c43c1c7e0af8a9bde1264f9864470f0d827f43bbbaa9540dd3845f4102a65238464c8020664c6bff78df677a849467eff3fd54b2ec029b7923f79a127cf70e862f1b441f1f68158d7a952aedd2f58da2e24b6e795add0da65694d1fd2dd6a9b886c32e546f58076b4a0b5654a077b79d7839a2cc549fc5833dec98af6efca6eb55985f62224023ba99d4fcbe623c7b35ff7cbac716a778b3e59d4c74f5df402b24504a27286e91901175360209e3a359e1a3a042aa937aab2a2361c270a354af26b9ebe524da21185ce2ad8799c2ac6e3c7c6262feeb97d1a63d1e41536c56b5ad7c0c86718009c03c92b79941fd71af556bba2522104f428f3da7faa176826a1a667d5d568f3e8b8f4a29e7d7644c315ef4d3412e4970d08b91ead8271736f2e4cf02d0061578d1f928fc7770d739da90179af02bd7385ffe41991b9837c505b3083b740d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6acaeacf154fbd5144e982d560c3e0c30b29bf54787dc368c232e4d067c6d87e","proof":"7c72a5d083287fcbaed5bab121e56514ff9b99b570ac08e77c94debfe6cbc813a22d431393ee76c56cd6b0127c445df3644941dfa7583e317222c0db5c54b35c96bc1c3d306fa1fe67d6abdf022dc9a871955a39e4c29e395cd68f4a3381876098a09946dcd2dd805a84bdf65906098a52938705931b84a5154db6914320fa1fbd307983ec1554633699ea060f72120bb2aebab42c006b8d1a62ab7242e3da0960238aea64b3ab890d5aa1f32d7757ad76680d9b3d1359f37d8e2523e089e1090eecf9087adb94169720586107ff97f3326bfd03061f27f074727f7f3b26750948946cc39c6ca18c3edb556c4522c20f6901d53108cc7e4feebc420febd033172a6af602416419448fb97225c33869d55ba5bc34e388c6c0132983d1b2f320095c17a5b6b8b7466dc90c6eeb6c361627f4bbb601e6b061ccc9b7ab5a4467b05a3e6b2e92585d84d79ca58b4aaee47a7ea7100b0d87a19857653b46e885016a645e5119cb080b553e1252d6ac3ed3c975a4ae55f8669e2f1145416efe92818d2f329b430b8cc04a5579bea096decaad78e88dd666f4414c1836f30ae1bdb48879446e84ff807ca5f70370ca6b35e260000184d2b4b38efa72ed0c1180c6a12d07fe572a80aea23451c0eb024e2a4bdb4808ae6d690a7fadc2a3b77173f6afb83a662726d15bca1a9b161e07212ff88c6b30b02fdf9ac306e2e30dd67758d3835ab464774498b94958446ebc05f6421fb77d853ef8a9365ec343e72b1f45e68c49f4d48cfb3ecbd56a777088a6ad29acff8e9ec66f314e46da58246aaa0d20465afcf09a98cecf77e7950e02c851aa8e89dc0c912c635f7324cb9c1b50db91ea3effdeb111d9d20ecb7f4cf51c7c43ae1a704361543f0803f1a1e61674438d5a0573374a113d5124e0b65b9a1798b44e0670f90571ccf6e5e6f083e1a088d7510b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"443cf8d2db3096d4dc49a303e5039b91389588b96591b8c354a7863ae734924a","proof":"cef0b6f6a5c5140bb11da2e16effd0220be3bc03691a051f51682b377dca7e72523bb03bfed9aac127c65ff13a38afe32d1f2586e2dcd06a1b889e74a486b51cf4315c2d51f8f70cbd76dbda833a9aedbeaf9276c22dd2b8021705904d60d521c2bf596a925a9c59e56c839994511f2c789dd836aaa62e8b1313c1b33f9ef91f402bbd32041687037ac3bf40e93d6b6be56ca6db64863a274c2fb813a815c0026c45ddf52e3aebda29805cab77d94adf8feecec5d061639886dc3de341aaaf03038c71ddd20dd19cf2351e0190b93116748e5b28218e928d73cfe89115d05c0f66f88484248870818d0f0cd4742f54ed73deed17281975c2cb47494fdc0d132494f471597149c42ae27625b70ca3ba7f06092737c2e658ac703a331b7e7983506a2ca808dde9b52c7949c6e47914f7a6ad991731637105c5064892260017766dbed6b64a7225b26538a5d594909a51be58df3143e550269d56a463a79b25234e509e070f7d022c26e98d990bd413b4127c3f9e29eee29fa06d5802f247542138e6de21f913b102a7ee24c09330f56217615738e0d9423fdf426c937c33327a64023f90ec6a77144636814431ed05f5d38e37687357e3e3f6fc87afb6bc96df4742c743695f7fb76ff3c4fb75e6974334cd267163a90110922b185a7f92547e1b68ed5afdf4fc67835ed873c34b7cbbc9f3a9f6cf81445a8a7606a307337e2a77ccbfba6ab4a246c3c4b4f5202f4013dd3194ca923557ca33d62478525982fb7c14b345ea5ab6962c448fc37195adc11796dff8a095a5428f679f757aec993731c66cbf763b5582982e417732c639c0f7b0617fee2384e567489873d042cd8c68f592d7cfaa9fb780664e98eea3dd6bdd46b1d30146e43399c3ad75693607c70f5cc9f1da9fc28441e56f8459b346133b9276e2258be53e23a888b80b49c62d0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"041b1e756e35bae7b2567ec72e597a22e6dfbfcb96e4635ad4bdcfed1ee68f3d","proof":"feee33639da2c120f26730433485d2dd3ef9efc61d0f84cffbd90452cd8a0446fe7ad45d20c60112f32eb5dae5e65e5b25cb5146bc403330fbc2474c4eaa012be2d6453bf44ebb8609edff5b797536d08ba12bf655b7926cb99c80ebe03ed23ebc5bb4d5f52028d13b2f21fcc66eb12b21caa9f8de491679944e36ffc411530f9a5d103d8ecd99daa2923fbd41a9b62f3b1f85b19191aa1958fe15dab750f806e08edf0e31b1f732d7a0f2fea2b1e689c89cda7b03dae6dc7dc78f0eaf540d047ea712cc04db2f272b0b1915a0b5a7abea40349718de48f0f12a653f1bb8280176e864c4f5118dd1e3951637fd8184a8a8e76ff8654473056e103b3d40bd77392018792c475dbf3b45f6a671061c6ec4fa1b561df05be9fd56d88f25190ba8138e9447b783ceda45a66c20a09d5d2a78d40516ee2eaf718c008a1531f9a527349609154f454099a55f5b866ee753783c29a20411d8817f45468c4c8f3ca9d92d6a35b857f3e894c6369c0c84a9ff24501060e5a2f5f0671ea0324cf7a4db915874a3780c1f3631343f1c065f27e6ecf59ff876eee165178d4865c14cafac0a27fc223e1019c2289a58a98b1569701f0505349b50dfb0cc705388960de6e43a22bc3d5e70727d8dc8280b5c58c4730b414ab38b99b0c1152f91f9553882152b140886d65d78865f048eb150d65e0188bdd31d95dfa2ed13c5185e4643c7ccc75da2f3bdef8c0bbb5a8194328ed865fbbe28d9d2786bfae4f23bcdacfef54ccd28b014def2d148b49f3ebd006d6d0b8a0f6e8fd81b48ddd0a64036becff560a81914ce3e59220b4d9bb9a8686cf1d77ed38be7d08738af7a7c3976749808644b6b89a5c7692064959bf30c19163f889be594260b8ffe7a473f3fc9bd5e175645025b8d31b5274e82f63c946f6c99b9da1e67399213831c2a28714fb5a7cbea8104"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"48641a8f9194086cac2a5eed4e2743b052cec2de796f227ec88622b39b178827","proof":"2e124d12ebc4365a0b185e3e497e0accf0a612664975a470b15457b55861e90faaaafd3c49657fcc45acd4019b0b7f5a674f8fef2debea997b9d18703653dd126a0fbc6f15407a057542f4cef57a9501186e07575ccb5e9a794ea2cbbc332c1572fe2ef5dfcafff3886a2e20d0d1054a4862a5f67e6c339b8718f1bcd9deb73f51d1dddf2d0510a39bd617125a7c9cc8771a8e7d9b4cce52dd4e3590b3ac7609468acbdb4af520e3436b9a4231d13055b95c1ebec77b82bc03fc30ba5234c00e9e3700eba7de7c991636c1f66f1320b3f8f99b5ddd5fdf9340f77db4c56b7d00685b78c3ab1a483c4447e9938a0b25931af0e8bd85773e5dd202f2d05fca2512885663925148fb4e5a3c1dc465fafc9401a5250651ae773c2c6e7d9ddcade976728942618dfedd39ef65ee0628aca39434abfd1683495908859cd3aa733e745aa4d9b06442e8a134e9b8a8316293b107e304a27e8589ea94c0f38b0a89a02a1d9e3170b931a66fa533ff8b6a08deea9b7ba7272914db90a911d8626f8242296bf8db0d66c40bc0e5b7c49d6ccb3567765546cae5d30dddb95f64739fddf7f359da2aabf3f798b58ca06a41bf10c4ee612d94301eccff8e31642e22d4bd745900ea693fc401cca1e81a68dd38e8702340e26f4ad0a1a2198005203175c8219842d4e1ca8a0df889146774d068300fbe5bc5629b555db3d3f4f879ffb1be5edb285621e210cb0b5101c9c5cc10560da9e2ddf54f7df7b3453ed37eb774788669464ab1d3537cefb49708d16981295a851c09d94a1f40f1ac36c128b9736015aa79c4482d30e1a48b958f313ea0a64ed6e3f7b1cec8eb4b01b0fae1b9fe1481d729523d1372b03b338e5896d46a8af9e0088bbc6c76c6ca32978e6c27619cc6e60605a906437f9559838eaa65e492bddda421020fb43be5e4d6420b130c0758fe0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b04c01128ac6bcb9ac1bc39c4a02b0e473ef215ca0cb7fb4495e53fe18b74a04","proof":"601cf0fbf3ae27869321f9fce562ff7bf3ec782367b8422b9e413edfa5616450260c7fd0079594ac341f045bf6e29cd4169c8cc1bbad18784528be1f9a5580610ef0ad905e1fd96fb667f51a343a81333689ad55d719edf123dbd9cac7a18a1c66060f69634ab1bc6dc81ad6532a657f38ded2b421b29a4c58f2589814443209cd1bda76592cdeebe0795b4116aab3e776ef78a6011b8134575fc62dc32f1e07aab81de1bb4daf85323650398db94d67b5e2f75703cbf3ec34df248dbb3874099b044e858a343db8c61bb49f2b042223864ec18c121fb1623d60eac1b64e4602186bc7d5ce9be9c40c47e7eaa897d6afc29263cefe741ccf63c3524277cb64138649ac795c0bc01874e90b53e1903f553a0d1877fc64a317ff40fb3394fd001560c65af3565888cc1fc1368190c26b4038e1175a38081d4bf46ee44a1dfe3046b69a8fb204644d694668214790be6ec8bfbeb0c97a3e718f5707b859ef04a77ee2af8d3dfaffb8195f32b8552085ac8601ae30deb680c0789d7cae6d6b8ee43c145331dab814e482eca722c1484660f50bd60cfba84ed32bb249534e99b6864f2a798abec396477d14aa9f6107ca5c39874d52fdb169774fccef57d5c506f128c8c2606be14444ddd6591e0a0473737aff300d0505a6fc0330127aac5dea924b54236cdf9f827f6ba68de4b812c8f16dcd232076b03157ffb5b201660aa44c2d34eeafb44e4922bfa6181c39a284ad1c1dce7bea9ab0103fc9c54e7a0e2f084e683f91f4f76d8c93d210a0bd7eaf7c26570f83ba90a2afeea38b5e17bc65852c5a178236e6543ce8c8e342aef76b1320bd56ef44023dd89ea6fc02689fa15d6690f299e8748774561c967b9f0b0a186cdd3c1cc8a977b6aa31b51375ddc94a0c07088e15137839b15d58d5270dd67b403e05036e6bdce7580639c94ad4867d07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"06b3af1a6bfe2b591fd01c4a041600c0c44057ddc6212f9ed90865a1810c9403","proof":"26564c3ba8f53aa1930631a0fcf8e31e8067fb160a73b520a6a7be16917697716cf06fe28ca583bf20cd8e4b37cf4907bb5ddbf9c097ab2924fa71d67952f77d38e347610118794d2ed9d5559c1e6638afdb68cf7c5d27edfdfb113b2655515c108cfe24281d282c4d8fe8c577632139beb67bdf0bdf4150733c6e07027866760df24227193155e6a96601f53d266a7725c6d2a096b3258e021a0d2e26000309bc77f92b5c58749cd16e7a7cfcc0b0948e9b8c3645b51f57da119891afa3dc00405303dc5508719c62b5a6a7f63d23b3b8e119235335ad1502e6c95763921f0346b6c5227b155acdc57cbc1c0ba393d5d9146d67a3a4a6ee26540ae2e334142dd4507e3c3c076786b999b409de2d144bab24395d76466d7310a3130f119567602697cd6b72dbc2a9dbecc2b99ae43ec84cb8e013456c9571147e8d425c34bf33c470dd28ad1c8152a198ea83734f2e33a1bd530d79f85e19de03e60ffaa9636392aaffab87b9d3276a47511f0a2df0c6dff871f0e4f43c91c18bf0755e62653ce8cedd773db22224d815e9bb75ef76eceda320408ca3a31c9be4097ecd0745452c541584dde2b1d5cd92ad47418d1468751f9663d4d81d47f3fb34555cc1c569ca1dc3e4ab5c6d96858f7cb38454d2e61032bfe3a853b35792c10f60f7baff2e98aaf56dff75ce3ff49698dd1ced2a98194929778e17d022fcef56d036decd364ad4debc251ebf4085818d8f8be60a64b4eab4e2b92a4801b022206922f77f201e983ff046ef8d886ca1177f9d5d0fa3541da8ed377ff5670df29186b2e5ff26ec0cf73e99f510f67530e981275e90957e335952414b40fae8f48c8db02df1762112b2bc78cef3cd276621223785865e8283a6857522c72623223bf963c60e0addf392d83777e87970acd94386ad64a461649dd3f36d201d991da5efed3f1c0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6c2977ebeabc8c069a7eef103243dc500e2370de3184dd3e9668c41079e90f5a","proof":"acdbb35142e13e51fd20075b14948a55e656a03a6c9670c3e9893f6b3090755a86a5797a45951a112f3f57fec5fdafc6de4f986be3a90786d608ecf1bb70f33606de0407a738f4bd6fba46e673eaf699eeaa1b19ddf8a95bc17e80d077f486067678b5d1aa1d60e5182e7f87c2561a4110fcd366df6f4f88a6a09bf581684a33268f26d75dfd69214b351ab9c40285f37dafefdb486a90073b95ab61ed90450bc392bf031ee50566655df6ed21cd59a733711afbe8f4fed618b7d59c8493e008d75a81f5267cdf7cc5c5125355c8719de8be54494522bc7510f617529b11ca02fe9e2ba98832ffefe5887874b994a07bc6c74c94210e0d2302ab24d79ab28a30e41ba2050b5f9593943e6ffa9a6c22180061d17217a204acbbac3166e7caec5c589c1edff4003b4f42029445e957bf4bfdd53c325d4e28eb5ce21acf8a00327ed629e4900c3251013aef4c48bf8882bbdcdf2ba53b5bf628d056dca8b094c1545eaf0e16c8280190ff97263c83aefae7bc2ce87816e71e0bd927147674e03956d8e9045eaed85408ad32fbc26477bf2abe87f711feb17b9d94908453c95bdc6742405d73c9ed804e7a77ea1d0b963e5b8a226ef74511f36a6b1c3be8c8463253a8771078628ff99ebbb36974b0504834cfca05ee351079e2dbd30ec9f7ad4d03cab4c3937659bc094b5653a95e25638c9dcaab60d81ea3f9f0f44643ec4a5a3f645d6b10a124582efd1faede7f16439fb4dbfc86c94bb43542a50bf5a9229b1e666545be581642cefa2d32341bd0a14ec934a0b769c7c76e48b20dcd087d122fac4b5131e5de8d79cfb6a60ab59c544214e6a4f970054119ec506acc72cd640f897329aaa8f1bdfdc0210c021ed026396210866e3390d8a1536a52b334fdbd0baf76ff6e5233d30cbc429046ce00f693fb449f37e7881d22e331ae0aaeb3550b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b68e0c185e3fd5f67e0ee89d5f5b1d8fd660e8e6f7d39428514a66b660e5fd7d","proof":"5cb6a73ab482a3de89df741794b0bafb5dd34d21f6fc6a5d05a13a25a01c60072ad85b90dabc90f757dd9f74b9f3e48fb064e8c4737d00dc4112bad417747d2a32c0ba9d2528be19fbf2fa17e803b4fd4e97ed2d948a2615f9f7bf2425e87451588dab8cf3cf081c720d3449a6f13d1f995908bfbe1221fe403db5eae3cf756b3d3dea4f078e32395120a1ec8408a678dc11a51fa82ee0464bfd7fc1db8b3802d314171ffc8685ed86c50194330ac631a431882604c7a1727650644b9b2d99067997ada0234345812ee7c92f7d9c664ba9de5828f1a92a7eea530d8d893c5f0d1061fbe41c49c2bcceb86d366534808e1ab275010db8c9a4e9ac1c2bb0f98661a4571d1d0d1dfc65572854d515a088619aa65ea6a07a2c1d4bfb69d78988c479485a44907aa63c11b376f0fcbbf3ecb6c52dd5608cab51e23901c7cb9b10627914cea0b586a1a371539030e642d624ec02ea856df176f12936a2fc4ff60ffa24485983937652d629e084cf975366a3a895b21b2f7b1cb039e603916b5507184684df7f604b94f35c475afa38487591b8584dfb3c438339d0eca784287a71d55f6ca849f755cdaa91cfa9312a7cbf244500415426db09806ca01e4e2e7d71ad69f8d20bbb3edd673002a192483b1edd2502231de2acf8aaff80f67783af70651a9c1f8dbef691ecc2a12356d13cd51d5c648bfcfcbfccde158d2911b63ce22176f021e6878547983415715eb0185fbb0590f6a8ac67241e5fe6648a77fd96840d56baea38260ef94d75c3dab84676528b3766b3ab35b0fc08e65dceb111013151022032cb9867cd580bbbd0110014a55b6fa801fd720e755f0746ba0e4251a16fe0bf6ae6473fb55b786c135c750cf503b0b0349d03e8edf18fa2d14af023780569b83ca100d9179102091442d55737d8890352671a0c94454536955c0126fd08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"124921ddabfdeed718a101acb3655a09796e7668d526a390613e8915203bf756","proof":"0c384497d1f72b330526f5c9aa757971b4607f926cf4b2cc430c56eb9140377a9c32889f83c7ecf591cefbc8059a7a8715b416a3925709ff6819e47fa02a7e6ecc1cccb6969dfcb8132cacd6408b8886c2aafa5c4496555b50602ca063b0e26916b1445a644e54ca19a7551f5a5c2b3b0c57f12074054de7e5342797f3caf7079583fb5a603f6a888c2680e83ed747d8cee02489d9c320d589a5657043aa2b04b9724bfb99f744799958105f48e059495d8f85a83453902dbb66d0867273f601aebaaca99e104eb1a28faf7761db5c48b0755eb8a2921aebd328d18be7f72507ee58ef29da598a33a72d4c263f6885b93107151173207a822fa43e2b37f96215f47f8e39786626e0a60f0f5d8e5a52c2e38251363224025c0f9f8cea8045c902aa5513443faaa3922a7cdaadbda4e2e4b5aa94ee3c71e9e0e60a0b704b05d841028e87d6665cd1b60c8f9c19d128ac5d3d206f2f3533ab012c5d7f483a2c67222041ee1b4d73b164440540ddc54f087112e48ffee95e7545daf8e72a4c565f074e41f8b31ff3cc096c61679a8e6f9986184ad313784b6c511ce4b0ea8f61b154de97919b10266ce079f0d6823adac3b91007c134a3d1c2e55db3c7fa8b33a63d66baf7aef466562b574b753445d253121e2ffc2b3d423b05054db07e09a37a52b69799b0085200fd4c136852ca5cdd6d98fc5f0853b9f35715623390575d88532ec08c09fe27b71bd2b2c7e6ca92f72880b537084b95a672bee92b5c603ab65deae02954a348bdf6af6e483c6910955b6abbaad1b4fbbd3c4b3d64ec0306b27e8c2cbf6005563d6c174184cca9c3fd30e1c8f460ca5e910d3da5d66993f5dd0826a606f80dedfaa602cae429ca6da05076a72b2a1ad78503252d9d21eda465065e733f67f657e416c9bb083b72811062f024b6508ff2d3958ee2245467c40600"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9a368a26ad95652398fcba6762e460007ca7b33835a679179234fa01721d2a03","proof":"b278f13dbc80660a85c40c4336b0e11064eb606604e8696e8d29b14688d22549ceebd0e4da6dc358d2105d2ae2ea9fab0bf8938e0217814232a14c7c878d0f43f81a0e305998c91ae5a24a4cd776052b00d41e1703e3f9895bc5e6fbb877b060a47e4453aed97ccee3a9b76ac4d0d7895d97dbcbc1bbc1a82bcd98f75c6aa5157d050484b0cad712e48b35d65715bb46d0a26f7330e703d51bdc3c56ffc5190bc1ab09eb0873f39fa19d40bb2bfac4f7ac632aa03eb758fcb69deaf17e804401c4a1ce7765d9ab613f07056911830d739cd29703852a10d9fe17bde89e89f30c843f0695dc0cf49c80922e1a0d769bef6bdd21e63968d3fafe26ddf85793d854a0fb16dddb88c6f9d898a927e5efb3e312caadf52b20980cca51cfb699265a539cde6d8a911db23aa07f923e4976976bf3fe0c9c05a0ed32933830df42a9e8688a0dad38df8cc0e005bfde308265306fb8bc7ce17e9a685827038f30dc41836638bf0c987407216e665b59605cdd7543fb2d64a5dc4fcd5130bdbbb3c0b6cc101a4cdadfb4c23acfc6ffffefdc74933b8b732d85aff0b19db50ae7ee32fc8b14102fd61d1f287b180cdebe386ec50a7c4e1153c69cc90754dccb98be5107881ada2ff69bcb2699a9fe1b026f6b62aea44a0cd9b8e245abe7be4bc1549a3a48158c9e8692fd2ae11445568d6287f34334a327212e564088c25cf0b486b1050a74bc8d9f1d350c51fdd610c4969fc08833b8deb2a610a51d012cd51cd8c3b8bb183e6752e8759873c4b03548cbfaa9a16b2b7954e7ee09099dda6815750137f34da2682dcd4e252db4f4f59a4428e0aa6ca5fed5964a1f2e45ac6a7aa5f5810f2aa0469158c6e4ca3ad9ff1dcfbd488e6d53c3c1d5dac74c7cdcd72d2921a7fe0b4ef9cfd9d4a731d9c33da79146bb5a9316748623acd8ed1cf3224df9851ed404"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"acea9d6d492956a4d202eefee7321dfe0acd1d975694a0ef5c318a600e7bc31b","proof":"62aa4efe33737f92341c53e51f6369066f20e1eccc4186ba0a11db5e66d31a5036908ed433c87d253311ef55456c583939d77e3432fa240fa4624ee9e6cb975e1ae30cce8d68c51b3ba8184e3a8e2a9d72df02ce7b1c99ae42f81458cedf3a647a5ea6f827f1fe7dcd6a03df85e96849431a549c5ed198de329de7615b896947c6d7d924764c28badf50c2f5ef19631e9606fb8b20947b08d328d8d6790822016cfcd1f9d3b894600490a91c3e188edae438808289fbe9752672cc291bfc280e11e62e0212f0ae2879221c45a63d23e8731d475091113a53d0e2810ad2f50105d6bb12de5b9c53c74e11bc97609d330648ab70b74e5337a495e3ca013863f10b9cafa87f22c7f634abeadd7ef191b8dfe8cc88ff60f8b87e8eab9b950b01b2581812d5bc3ed505e28d0a2245193436987da82cbfc3ade5b010ee790b2e2fbe2a54344366ff574c6c299efd965093bd3e865626ecd8f8898c916b60c2a3d1bc20f62c807cf16c154ef3870c725cc81868532c30a54ab07e82c9f0a7a16fa77f0214c226196a790572a02b8bdca3e93ab6ddd3e93f52cf5b6d997aeebf82b5bd34cade9105fc1a2997510af80af21178372aba0f4d3881ee11eaae85848138ba525ea24169ca553064a8f72d494675ef8b0c3b9e4fa50cf159e083bbcb345e5367ec4ea8a420a0474c958d6938184b114897791e396b2c800d96c076afd4ea60569a498caa5de33e441d8de7303d942267f97972995638dc660df4aea49733324b780c41c731154ea7fb37dbaf0017988bba5e0a096fe6ab0fdbab8937a8a26234a252674bd7f974d6eea79ffb24b1ffcf91ef52d6d1ed481fad30b5f1b998eb39f79d7761a21a886c49e5aeeca8b8f0dce47a3617c9a823ef45d991d2f37ad80883149c367b5d16acf3f31ca2b058a8a48417f0f14377138d9b5cc8f39c504902"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ceaa40b799cba02b2d6d20749f02beadf4c59f3c5cb46ad9da8852a97121543f","proof":"8c7b41fc80bdd0e57b6f582d374cfe3191816512becefa55d0dd475ef4ded86b2ae80cada14bd8d4acd3831cf2fb6565e5532e28739fc553a3cc97245d263072c018b06a7b11bbf528956fee2021e8ff1ca5b6ca319fc79377a3c3410d10af456acccce109ded2246f463592119077b09a9981f63ca710f59343b550aeb8e5096c7242747ffc05726a85dfb097301f46ca54beec7063ca24860ff445abdfb90e086a864acad9ec8401f4e07140b61eaa9041e47523692ed1355ba129edeb6c0706a3836861fa7700cfe97f8b2639546dca67d38e45acf873d6f71d054d470f05a8ee9bc97cc9fac7a709c177ae8c4d9c59fedcbb592c79a576620fbdbd020f2c66679d47d49916c8220a2e644f68b62d915b3f710d9fadba02dee3f07e31ce086e7f12937413d2de62ef30e10b57d87d89e15599f4cd34f0d58a5436ed4a1371b4b330b08e42c96f104084fd0070c208270d9909d051626466c3e0d932b3f972706c0aada142f4aa26d724b5d49ecc493bc502de9757ca503a665c7e176ac114fc6b2e965fed9b868438322d81e19deb69af3b7785e6f2de79853e6972fe89767c3042b3808a0cb28ac04a2509edd9c8d092a3009bef714cb70b1e0ded457d079a1b6fc26d332830fba8d95d8a1d5a2c3d0d035869d405c8e73a8bbf11b03f02228cc4aaae8700875311cefa8b0f777ea0d0294ca494b63d4c67d0329cb22c5e447a6241d7a843a5579f0cfe61438593ac1126d94c28f5a2895e57302581db5b36dc18de595b8a2167f3f60cec839f66f84bac0cef14e7f3f658c0c3ab183462f026fb80011127130144492b42d9fc865017b312b91c3ad92a6b43c7aa321030e9a6ebd9d3d46135a88c5ac4d6743ac98ff60e50d5f18cc539f8893dde1e9a07a887e6adfd625cedb2695e0b2b49d029d02a82fd05131063e026823e316f010d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f66a9b5171e2bb48658b1d6c5393f6156a5663a92aeadeefe984d7a6358a220f","proof":"64097bc69c9caa02b9a64a1e8c03b50833d47966685984bbcdaf2e7de5e95e150ee611cb0053eec789e5b4c3b8c069042b3a6081581b40d004d7cf8c1896c853c65954b32a060065b041e93b1b6b770c08ddabf406e8638b2119e76c46906a3088e4346a40e322911eaa469351bb87f5853f9d000dce26e63349a084d084a17baeb1da6bd9ce3e5b6d22d82f4234817ad40d3fe1c1e95f7213799667e605f206fbb6b98c6480c2bf3fd40bfdec56fcc5e974e8cc997a0e0bc6377d3ed3df2a0412ad259cfc79f7d01e25dffa749b6f74d77939580f0c2ce0e2b44560945f8b067e28deb1cf2a6d71d48fe716f967cd6ee18f22b23012f12fab3c24a88f1f723990845264e1f919ee31a53b8455f4741bfd55ee5933bf37d15eed2f4b9fd1fb5fccb8f3aecf810fee093ce4ed35ecbfe230297e4065b7520f58ab22e0a95dc2350c45990c936986f1b09e083d40c1d55f92fbfa72dff9d80dc3d5f6d6d449695d9491217df4c76c59559316176db5a138dc94d324e7382380f6024ff50a051265d6c046a8c36f3b6eb10e2344b7b3ff976510112aa515c50c14e88c5e6771c854a0919ad497fa02e51ac26c6047e8c718e5a4f7ab11ed300f35c9582d0d913c6e6263a9b1b857d840897b90da0d860f4bd5e148a40ae08bf8b14dd8f85645cd4e92b1f3f8ac0778b6e568866643e6b6fef420e43afb9041e02270e08dbd6e9500aaa682aa790cdfe6624bb34b6975e6a5312084f6659a2d10cf8bf6af834a1a2d62cce4923aa3a69d4cce956f73431c6365c57a6e796c9469b5e62e5707c66926ac88f91f5ea0b7e52ee05daa18d6d815e2e55418d40d2a96c039a0843f4564345f1b308d5c7cbe3ecedf204a497a6d5f1f43c3dbb2da9716b99588eb9c008e0d301be6e466d6bc8b38c898944797eced50b0fe1e96d754f90007dde12321df03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"18e3400957c059bdf3bf5383526711c29888e61399302fecca42ed46533ea667","proof":"5e0a29c6054e9c9db2d5b5e1642131e4917e4c0e3586e19958f0ab6e912fd138e25fea6432f58eb74c023fd31e055b6309b2bbdd32b23f6d6dfdb354dfdbaa02504321dee5832b384a4d3f669df515d0c968c4755f3a130b6ec113d5f347bb64dadb3a60db403d9468e3102b3582447207d025e2f0e3108159a90c5184207938b18663d8956539bbec0dfdf7debcd3d5d240babcb9280f131d7e2258db0daf0b1f16738d97176b0383424fc4407c120ed84bca9da04c0f0a0db717d76322bd0d7952bfbf0e3c61d3e559ce0369ae37303ea373d315459ebcc7edcde924c9a80baa63d1c18381242f410c219d90dae743d5f5bf852eb5a79fbf253128ba59134fae2d89801dce0d39ebc102c7bfe7b2e680d0d8c02b8709224630777e9c03f55218d00190b164479cc165792ce1b64411b758c5876f46d6685d621be36e34e70e903182684d7006468c8ab2ae53ded2b2c13b5b94f37a2c4b47fb76231e9d4c40d607dce84958a5920b4442a6f4c944c50aec910accdcd73291796a9e88ca906f4c6f3c034ad1e2b1d9a1c7655d48fa54132e35e8afb2fc1faff97f6d36cdcd39f25a0862b7841bac44820e72314b990d8633087a6eaebc967f286fc769678215900df4ca6fb2140d494b62bac73279150774a9803c07730cb06493b221566e1328fc58cce806a42fb5fcea6c49b65c63c7de99e2fa2d87bb688bacf994b10d30eca17363471badd241844c62cd55dbdf5bb37cd03275f83c78f81dfa2bc09150b2f16d4a608be1f7f3c0dc0eb7166efc1d8e235c1c4b35fbd826ace255a88673ecaff67b495d210a100105bf96340f6b23766ffeb0b3fdd3ea92828fd7bad74c036f6b99c07a1aec8ef04501f84059fb68575bcd56642dc16953e4af898f560da8d7aacd182d1b401ffe329c2ce09c06c5284ade6ef89cbdec84379865a8df0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b41c31902eb98ce814fcf05223e4f76327b17ccf8a6aee548e67139386e4505a","proof":"08627e27ebad1a966ce6b757def4cbebba163932590cbeef4cade92779d3bf4a5abd52b27ff7e340bd7327b6dfa4fbae0b8c5bc3ab0cca7809a0a0bc9eb440492ec2a0851bef42c3dd32abab0e701ff02a44d1c96f8d8ddc11228efc315f68155e105557551be3e9ffd3ee4da0e981b9ad0758b3bf48301b2ac2e4975ef3030625fe0d483d3971ff415fd42044917831d1e60b73dc3ce63812bc3f77438df30f7c5773f28e732b14b2b768d43fb3f92bd1ce299d699740e9be5014bc601a62044b3ba4b7da5edb58a1fee3ed83833006d2a0111769d383ca6b82fed8264b580328f899d1d37482736cdb51d228e07cddbd647c71cfb9c6773a6e6ccf2093c628b2bc6c8f4f91e3dd35c38761e8653910fe9fd28462cca61c2bc9e2cc45a6e43d8af0521e25437ae281b87f07611f2f516513bbe115bab73ff67d93ec79f1655aac074af4742f09b39de2904babb0922897cee3eed8aecab8b293a349e9e1643fbef89e5dd2c1dd9354c7312b2dd3803d2572add46dcb45a6521986db87c1d92dfc6bda62ae3f74ed6f64227098dc6646dcf2b3126ac0cdbb4ba07e4126ff9f4b84531d6be391a169881045ab3a4d602aa3a53291f43e797c54a296dffd690b2e4a6f04c06dc59e8706cf269db41fbfbd817793289d2eba099c64aacbcbefc20672c3aef594ddce6e31a868498793883c12f310d10e1c7d0fbb1c0ee456e7dd3f4c079ea2eb5746e376de374a997534c0e3d1a084f36a368c3cda041d24249012c448213aa5f0a257875a5c3d99efdd79d80cc6931a37016c771cf83a50d0185eeca3a09ff2b8d66af12ea9ce079823f7a8044319c479640d72f0d498d4b771488b27883b9ed4103c448a51761ebda10fbcb3d5c3894663956c980cc7b0f8aa0dfc4594da9196912b3ad396681a4eb070cf055d5ddb58236c3268900da4063b01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fae3a6bfe4734c50906de921f9e87d6d321b3676e7e45d9e17a90d0cf39b1774","proof":"783d5d933cdc2256b7d9c53510e6e6ff2cd4b375e68e36138ded261080ef0a05e8976ea339b9c2e573c7a6eac2998e1f707183cacfe05080586ebd8d9847175f2435dce750f581aa76c4da7b17c57a2fed055896c2a0faede0cddda171c22809bec4b0c9852c25153408754a7cf1e8024baed11b9c15b6d1361c8f717d932131f133daed57ffc378ef50bd6ac9f469bba6d24e09e52ed7786f79af8b7927790820192c7295919439b4e24ef2486242163eb2f5ee8a7e7635a0fc32ee98a159090f1728552761767e31fe98e857ae00eaa2a3abf1365b46cca67724bcc8644203e02afdfa81849b1d720a208d9b7c01d36da18d6ceba2cb8ac95b67b8370bc9559471e7641626891ed5611081ca1a5f73d6c7ae21079dad33e11fe955462f712b9cecc8a9b135f6a4e8e6698290715bc9b8719ecc47c6c7ca633c2621dcc7377144bc131123abb6bfadaa8ebc5b034bce52c8b2421d570a396bdaff9c3b50b4230eeb0ec204a7d1e507bc152f7acdc9a87dd6cd105e98a51279267fcbfc49a35a6287187ee28e3fd6b96cae0ef40b2e074839a95267780bb8d17af1b7d3b9c763faef1b174c9e633e6a0e1cab335cc1e18385b8f4a46ea0b69caa0bca165244659a21795633103f7f0b159ba117a84c63cfe1c664e67edada93cdf21c6037611548054046ad2d8945bcfea5f23a66b28bae821cb530d1d36a206e8226ace3bd4ae879b19611fe66c88dda09ba2b6491020d73d33eae488e7848aff0337968042dd448cbf501883281d27fb5453532218a711de252ea6c90d568f4d9979f5938791e190327e45e663ddd720f14e5bb900d04308786b65bc7c4310b9af5dbef265eb2f75d56384e8ddfd3080505649ba6d87c08237e4aa4401a9fe3b4170d558e09bd1b7173b0f03f81507f9e223486c9c59782b707a145325aec93af73e3a8c30a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ecc3368913909a06385a727ec881053b663a03d74db5bd6ed55215aff1c11470","proof":"ec23eec0735eaa8ce69ae9bc438bc8ee26c894876f821225594256d4364b9e22d2c8f6a961b8bbf09c7aaee3f2f282a79b5e6eb0eb1c72527dd0ab46d3b2b15474c2916e397c7526638b08e389be99697757ff9d6e50eb7a67ee3dbb5f86324510d2668c165fe6546c62bc09b1cc441d3c1d370f7782046e497b8e204cebbc2be17ae6433718251e45c3b28e8861e06734b485b6eb93700a6569b2a7be92690249b1f6dcd4630d2fb0aeac88e7661f2530955b6316e68a936b73c97c3b2b0b09695e1c4251ff8a5905a33bc87b0a23ce2970cb1813d059b9d92f76fa15cde40efe5e6429dd5ab3d6cd9f350a78e5163871663addd9580eba47d6d091c663e95de87dd8a3c4f8a85f7bce5e32eb603071da4a7421b8450d8235b6a93f251e0235e0148274181e08d276a5c0d6c7ce4acc4fab7eb906c86f3b084cf2aec94dbf754a6ea1df71d97d1d11cfecf7e75976de4b0963b7ff711ad707c929d00e8eed4098045940e2f57624cd43c9ce463a9f6b22097b8f0d62ff2b40d1818fa0ef7133dab238ec93ee176db15730017d4a8325c06dec8cc981a5383c79ac95c648d7710e3be96432e2686d84b58f85ffe6c3bc6580ec224d45a3e0d924d6367fdca9569655f7ef4a58697fc4487873f68a6ffa2d8c35cee7b7f686b3fb7944eddf6b29e2ca6d25eded5b8d1576a558401b5378dc0c6a116bf004dfdcaba8c34d8f120f64542cd4f8fe9a95d23d42c824198e48071d95c48c2ba4a750c123f2c99d0d47568d7214389024dea475557e611d9efec1366fd1b73f1260394cc785ed2fad756696f3544eb2ee964a28aff6fdb82b4d19b5d90b56e760505587194e735ac0089cff0e3e6bf87b7241194862ceee365a8f6878c1deec5c20d4abbb6f3c37c5043c49c8700e3f74e6e710c70a267a3a885cc44bf776bcdc57f99c2c55a1858508"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8865d918d9a51c06e85b2ec980f750b07c4048d46466b26171f9b27c4850624b","proof":"4e2af080371f275c5e43dcbe50668ffb47fcaab8bf6cc22c2147ba962409d51dfc8148cdfd74fe9de3276f1d7cf9d4de80218c283b7fe7857e77d769a7fee951d6325505d08b7600987defed78e713e0c2fc9c7443bb0ff7fff870bd470dfc66608f74cb2ed48fed7b93a0b3cf5a1a24a6ade1fd8d22bbb19c2d6e71be3ed13c86af2aada1b63bb5df350d9a2ee81132e6ab252b054941c335b25c1cebeb5701463f53e2db74977ebe9f23bf9e8842ae113a753a3f478c3d19084c8e9862870d7a1758c9707da648cfaab5c2479dacff8de83b750434092d22ca53ab3b426f01fa7e61fe86c5b047b5d719c025afdde57196ab4c55b5883d22a12a2ef8fe8a202c13682fe9bfaa64904b1daca3e1bd8f81ac5674f31f44179371a0f76e72fc738abea8183e2535423bcbcd7ab9be07bcd8cbf5de5ac3c507a241602fdcfe123cf6ef14d28af7c12c3168cb7c6d3cc2e508b948c958d59fbcf84248d28772f711d2218844436c279d40846518aa16307ba8c5a8da806e00187452d783a5d2481172e036d4f643b16167aaae21aefe3a71d791ef671e9c53a13e9a32785697ce233851f4dceeaa48df9f5022bbffcf65bbb0965395796c533f77120364934adb6b566866d9a25f6e1fb3e2cad8046541b02ae840744f52898862ac8accda7b0f7b8843a4008e7590e58073f5e61afb6e040f21d1318082061a937815765eb8e15638c8c76cb7e21059e0ff14180a1c530aae3d095893963c86bdc2185cd19ac0111e22ff6bb8743dc79117120932ab1e43b108a554e6af44c815b8889f1fe70543cc4d9c026907062cd9f70f8b04cbf21a8a5c43d645965e9b9249db71350a7b2348ee0f520a0258435109c8459b075631edb831d16c940a163159c22f49e035010e6bb74d78d125b4188eef744d22e5d94e5145ad303b6a1ab06ce356239ce50e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8a8abff105c5b947f7d2dea744e2ed40c5b9efbe7cd4211661643ebaafcea00f","proof":"92ce4ec378c0dbace242ef38377f11be0caeec757c1c6f3459f9059f7e2904187a0fe2e517dff20f9dbb347c5ff909f26538b4053cf618a774e6ebf51e20f71c64dd589a0b0b003197c7e391f783005b023fa8cf7e531432d00bf799230d5c63062431a497a0c729d162e847517c0090a2e841143dfd31f4793f7ae7893d845568d64a84f13d74189db67a0bb649fa030fc87df5cf75d9d354fa8e63b0406404ebbc81913ec8de5e285b676959fd811db41b011e23941587050f2fe794ee090ada4702f73cf86d9c9b7b1883c9359bb475315595e9f2484b33b103fb9558c10cb4cb817fac7a0003f7b0ad0a07af8f622455d6bd0c53fdf7229b80c532352f21d0acac1f853ef1cac92f58863de46722fa4213516566a13d8e235ebc2bd200209a7e2fde8457e429152cb92b4d4786da464ce220b4200492da39e7f2e0b3e450e2c9e6f5d40798349981cfe090308e09eb65f89c0b39b3c9060967429c8c75180a857532504b2b36f0c778784f902e2169403f23436835aed0bc9e7e2092e93760cac297ad1c26804bb1cbad08cb02de31d24b421df46b4420e6eeea3473394dbcf49805b05b9775cab06f2de97d877aaf79c8dce16ef466ac3acea76b3a5b34d4c27becbddae16bc7fad9f5c8dc13a2e86ae980c43aef5604f712e58cd44b52fe23684c48f04a4753cc0cf868791330ef9eab5d71f39cfca626f35b4d55d9721c533e8b7eb6e1cd293027cf7d4d7b9a9df1be6415ca138d67f92dca7de64655ae6673137148983a8380d8d3b458dce42b0f574d1b22303bee664311a275b53e62014f174e8ef4edf233f0ab5ac50e12b3f84e4cbbe2b9a3c86c8a1f73b18d5be9fcbba81c501741847e9e8d90d97b4aa162d1fd5fa87e35dd1032c67cca3a0838bae1f4ab4e04bf61176dd76953701d9da01bda95dd1b2cfd6138b1a8528405"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"30c79248c8121eefebf5dd33dfea528d32e9a86db16de5329c0cbe39c2d27657","proof":"08fae4bcd1ec88486a624e444bce1e9ff8c8d24e17a83d3e28ebd720a0f43a7176f74e6776bc250b1bceabc9e57a1d8af21b7841ca4099e9511c79a6c3fcc20f800b0d710a7ea24dc076b26b068b071d3a51c9a239c00d072e19625cd2908620d296a53b70767d20f28444b03bda5070e3e81eb852967822dadd6646488acb0071fb0d4ec89eb3882764c1de215a53fe7173260dc93aaf5dc7a8215d8dc962098b8f90ab023059ba3870c76794ea1d7b67b66e044c455f8d178b86d7681ca404a656deffac19c86eede3a966d2d50e4d56f09055c9494a976c1681cb709a1d025c3bb296ac60b258b4c880317ca66183a3dbbfd26b83f99bdff52965ff95222dee7a3370a493102e871509d234fd334246305f2227bb22c1449a84657adc5b5a62d5a3676c87d9ed13a9d5bcfc579eb4f9568faf1125065ea87f7b7e9dbbb07ea2bf3edf6cb37e3d70c690ee8df7a532ff391f67bbbc138a3e0ef6fd758ea42568071062fcd6b0036827ed01b26d971ca6e059343e240b9aa6fce2235edb587550bfd35a38f5a31dc8a294e20fe14aed5379825bc956b527a44fbc041129897cb004dd4322f6f31cfd8d8fcc89aa3a034b64e4b604754e5c9650b395436c3e4a0839cd9fa17dcbec0daa903a36442b3393de7ce7006220afc435976df759db19ead944514b93876502cffcb059580b008bfc7cb83521fc059664568c46cee743be31644e4dec2965fd575af8477d29c194fa3602b9cb8b1d7317982aaff96f738a78599d8939ec69a4815647035be3efe3d34c5cff302e5439c97ca95956cc1c1c915adaa9992e2ea200c460d55e19a9855f40c6ff8ef26f7023450e803ac91e5ab845f513c93920c11223dbeb47b0fbd80dcd28bc82f4b86a7b09296e001e02c306e6d152a2284a70844506b85520a92caeb3af0b062a830193bf0a84e79009"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a495237f49a64239e3f2e59faa1a331ac68315d2bfc12448a7f9c67da0568b07","proof":"4212538bdd0d7c207e9561e80acabd9c334b30845fbe6b4b3cbe5b817b95440afc1d67106dc5dcc225bafe8126ddf02e7581111bf238e63d26c1e1cbec47f118d8b86486da6b71ba3ae1723e69ce0c7285d93a2fd1a00e03396fb74fde929d2f662b902cf6909af31e0d2f319238ad2932868e6148d7d036f4e4dbae0a48a775feee1b6b2508880e5991fa6ffb8354057805b0a12c15223d896ee4c7d921100f61e64cc0e61c4c2bdb6d2ccaea2e0ebbd3eec2a188f888e854c23babdc8e97001520dee812548fc5680992681095c616da492c4dd0501eb5f4ddbbbbc5c81002446dbe5c2cd0a0e4744b2bccc20773e3269be95c2cceb42766bf27b4d4751c536c9161bc14d20f16f6d266526813a66b8f98948b3708e8c45dd5992c166c20122cc82a2619ef80386c7871baf4f63d429f5a7269b3b2167f090c45e8944cb248942706a4187ae2305627a6246dbed1478338c95012f88fd79ff72e66d75bd906f6e0792b6009704b7033ce905bc78990bf9a98b9f3e032e35f66f432a5875f25da247f5ba0ae205d35b4dd07e50a3a73dd528eeef7f1e19b38dab841fe8e174928de2a5caa36e510964686be92644ad8d665e2b4cecd7e3c3c79bd94e2a2d241804ccad7b16418d7187111dd852754fc90533239a27b5a56c1fa1ff359ffec03de3e5421ac182e511558ee3747fc79521fdd6ec5ca7f6e9c60aaa884d4278042687761fb0cca1b0f8b3032e9d578987c5bbd1a5bbbcbbb708d6b7757b95d357f6e8155938caeed867172e4cdbda5f4bf371ac8550e1d2b27644df051931b954cc4ca5230f03e43124725428fa1fc37bd9f5c420c2a9e14ec0800795ce7e4606fd5b659f559bd13deeeea86e4d857ed59f707fbd4adb660242f6eff1db41f7b05fdb01da35e1df8eb89bb9ac73d50aa902d23f28a6a122d51d2075b30cb597501"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e46570434ec51904b0c1fac2a66fddf9c7326e613adb5125caa5f0ece2a6d549","proof":"781abd8b6d084c17fe23b2588db3abc88778859ae138791ab63235b9698a4578b474e69d21c964cc6d7bd4027e1c7ac1b08ade285666da7ba3c5101ceb231c76887e3c61f0b6f412fef092d8b15eaefa4630e377885db94c23d3ab24eee188724e59630ff2ff203e7d13e09ebf8b43807f516bdf68791341aec82fda6ae6535ca20d400eabb76494692eb07cf742d5f1edffd53101fb968957f7e90f8ec8d70d2ecdff1d65ec58407039baf30af4111295496545e86989e5ecadf92a820fa70ae7baa77a173ca09b2c1ba32aed449482bed4b6d1e9d00f9baf5a799de3ff480e085797f642643823228c778efd2b796035e3efc164410ade3df82e289156ed7ec6c066d971a383d3685fe0edbf88beb1573bd6e778aa4c6aaba8500c972148437e84e348bca54c150b5b6d6d0d3b5eacd6bea72c3489a2db85345358589fc72a94f1d373a96fa1b9434502470c5f2b24a06413a4efc7923e11e683ff850b12718a76d35c00676c8302c0d8754098b2b9e404da74531350fba98d3f1b7090e426761bfa44787cb0a6a73450a3814fc2c067ea85e797ee93d508c14987ce1939032884a59d7ae46a81181dc067328768de82fcd2be2ea6bb8421bfab91a426d23b00faeb28c942163887ce7d7b5ad06505887bdf5c257f172c7786c2ca360adf4f209125f17324e6a1c23e6796914a39be68166d5413cba0f994b14f08f5ee7b63ca839142a4d1b6656f9100b64f4e400684e904c08adb9239b75349539b897e16be5ecae1a8168e44d57b22bb4d1bdfe028bb805e2fd5163577284f6e5cdc57552a0f303a07877993a1415bfad11691aa141da7f624a43ba42d4627f34326510bcbe4cdb494d2fd5559f38f260a7ab6b3d33975d61698a7e062cdb19484df570b36ab24f146f6343255f20f2edee7176a96291dde5327eb006524b1f26eca7f0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"52365ae4775e33d5dce28d6a80f8d46df65ecbf6bf77470076a8935c189add79","proof":"7a0ee5e0c4f6419340559b1a5056b64f0dab7af2bcc819523ee0db2cb60c794ba87da5343f0dca6ea0171bf0664061b45ec9d8c6377d045deb17a16e93b1432ce69388dfb6003469603fc2eb6745365c4e865370185634b8178cc331c1f37c7ed264cb05fe3c7bd0685a98ab792aa8c0dc6beeb9281dd85a0b2fe44838564456105c76cfc703fd3c526cb00d6b1211b30cecd50ed5d37468947c803b2147610638a2fc317b79ff89ed1c03f4eca858f977b2ce0bad2e3900fe1acca0fddf7103eaeb4d416286b70ed6a4656b5d2e6624889c339e9cbd95f4263dc18de72a5302b87d454f806c561670025badb05256e234aef583b03d072609b1aecaa18473572e62ddd1995ea22a42e461071e04f05e139c9ae2e9261c74449b1eccda9df76a1ad644fc514b75639a5fc853c878394fc65f13684ad05beaea5eb72ccb70bc265004fd1255fa0f803faa46102b2d91623e9b31c021c3dc864da73f81c2ec3b50d23e1309e82b70eb259226414eeee286f2906396a4d432c376ffcb1157631e7d4007fcdbbd861d6f311563aea02ece106c60eb36d69d77595a082eaa169d7b659cac92c3d131889afc89c33013f94f6079e6b9b82d63de27f7a4877acac93d354af212b58e6f99b34eaaf9c6c29e896fb41e774ecc07064f9b33dee15d90e673109420265167cada13cef7cc03112978c2f805da2405eff32375555c02d0a433061a8109bf982acce174b944704ea24dbeaf4cf888fb0f9e0f2b4146700241288cc9b2a61740e87c1640547fed2e39e9e86cc5daae04293548ebae05d018b90c3eb6784432e9c9c5e16b1bb6b3bd99c0a09656e4f40f86d4b050cd967c4ff94a230e032e6b1c23f94c1a407f27e84a931d11a62bc86e6e0077a5393ca57c5d000120fbc371341dd4fdb45520c011a3b2eee7f320a59b74a669c74d6395298a01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f67a2527f1a73eb1e02e1f5ac60ac924fcdff6937a2460362ee568c02328166f","proof":"fa2cd1d47d9f763c0982eca2ee2440226e0f0b370aa10cda7f65390962d6d1339a9f8c038b78aba1de73afc51d8b68494d5d1f79f612a5a51a7fc6359d715f62e882b77efc208c96903aca907e2864f64337bc234d73479d8899589ff4e7d27d7ae2cca27743f6ff89c7861f6e9ddedfdf08e9cd3613ae5a08e31685614310233a95428d3d4810bc3f7e12ad708d38d9f6a6b59acd37439d5d2478654b2c39079fd322c7840753d4c25d74badab2d7773d4ccd0bdcb34ce658be00942c1cd50a4d8dbe426b9cce4d9682d448a4ccd8c92e16b779ab082370eb9b561b48388906bc16fd9aa43951e25420d2061c85d5b0b3e58573ab8747ce404999522cd87f3d3c16e9e5c50b5602a2420442067b5d38f3fce899cf3ef762917ca807d417570d38777eb3b5a58ec4c1463f97860b15e638482e7a07af5e0fd5462b33cde9115fe6c52576fd00849b8b90e49eea79ba827ab6b0c99d01258b4e8f410146ff15007e6e651921bf92a0b58e5a71c2efa33c3e8e5f3c632680918799d49e8025567f742609939a6f123d66fb13f28c2b69a6a99698a7d842c76e91c0b00677507f0d2aa68c8b7a1791bf7f264ddc522a64b0891f34ee3e0117611c53cae48481536d28ed0057c3bbe619f64fdbadbf4f970cd141a5b5839721a9aa36a2db0c28ac201810898e36803ded4c53260e2eafb2f3270ea1ac28a11a293d8299ea1be764669c3a849654d5aa16de18146aa0212beed13bb3f3d65629c166cbeed943080d73026d10509762549879427335af1c5f570e688afaf73fd98b755af6bf30964a76a4af2471ae0dcc9eceed76ad5b404661befd1f8392f033910567ee2b19f13103e50e81fa64f76b2017be25be8afc2621a485bc9c0f0446f21a1ece99325c54016f08ee11c2caa147b1bd12cc5a967ee866c088720f3502240e97b491cfb6020a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0efccee2bf1c4dc861ef9ba651f95dec5fc8cb09eef5e8b11be430e01f460209","proof":"da8ef1810803e88b07dd9ba5e12b204324f8182b0b39c6c4e2ba22054df0af6fba2242116552fe1b94cb06b58e091a23da2af9c60b13a9e067986f62f9a0d76d6809ef808083c6926a0eb7408fedb83d5ba6a72bb2f485a2d01fb65f34a10624409d9edcd56725b2193e2f1883e08120eaa0ab9cb0f52e234f12a378321b377f5f2c9a02431f4e814c9cd6884794199ae325c5d9d263664ee30d25167c35330714cd58b354b0981dd11f49a9211c2475dfb31b6cd77cfd141e5c4fa92eeb3a05a154a0b93ee7c9b61d6efff88273f6753ccf35b9b47386da66a04e3e2cbbb10a3265c8911810c9a992fc165554769e3ac7d68da4152deec9bf421424be7bd9642ec6131efab53956d09506469a04fd529b4234343637ca3d322f29af391879333a2f1e2ace4cd95d13d1ac1cea0d347c607d08c7d458d004a7b5f2f5703d4070e2c7249f7b2a0ade6dc9ff86ce005b1d0dd18402a844813d463c4a0b27b47529d838217a5f22f3db6412cad0bde6fecb770734e83b44476364b0da0e13b5726fb4ee6b9383889e38fd309dab5430fdebff3816011773ec4020082163dbcb887a5eb6a28dea04fe44c784941626ec5cb509bffda2bab25b40a020e661b8d26e3148ee578da48c1bfab35eb78c3945df0a7d25c4890bca476c375ca1d5eb53074c0cb3f849403f9ad8773bedc7b8c1c626028cff86eb0df4f6ac938ee7261f01271cdd6db1d3c26ca0ff981e856a49aab2afdcb6c10ecedd835edf55ba762ba509a8ce5dd396a07464e9eace5544c92664b771a44667e63083b2432c4483be9f730a4ad4f896afc9df316148dc0b0b3de9b2ec478b4a4754981e63c641453c960ffa160b4a4dfe146aa8f0e9773a44fe97b452d095495bcacb26720dce49dc9d0ec5b60faad8799b5f76afd1f62b9198b093c5ce9899d65710e91d7017930b3e08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9693364dc43afcb6779ed42c4799b3e979b304526cd5736485e3fbe25791532e","proof":"68e7b52bbf0a87a76651601fff61319b6c86532b98785e66d61dbdacd8dfd512287007c752f9515b9118da5781f423894ab4b10f1a8390e8a40ee737efc5175820497a2a9472165f8e41aa78b59c066c38116b142470ca4b22d73bbed0b4e43950b40d4f9be2c1c6e9f8986a6137add3cb20de414e55021eeff366d01b71700380c7adf359fc2b0d819207a05d00e58934a5b4a502759b4770a282456a5a1b062781ec508f9fb5e087bab23512d9ea79a6ef1bf2c93207c110e4dbb26e20e50c1b3c517ff4c64138b6ddc3d3d88bd304babc9a5ea3676d160f3e8a0790169e02306698084d9e59e9fc490511a42d458491a808c5fb49a9ab205f32af81d2fe719a8e95fd4c4601038ef5e886a98b53c87e0a4799b57bba91011884df7a3b625388182497ee066fcf86db34be63f03dae81149b2a90d3bfa26e113f1f4f9b8f447acb3cb87e75fede6e98bdea8203d7893faa2aed653bf860e900f3620c9b362e1aba7fba0206e927d830be9d2cade9d2cd39bd456d4df27eeb18909f9cb9ff4f22956b6dcc5052746299b5d334b9b67d764861739e7dc6fa4a563684709489011025121c0d9a0c69159e48dd32a97aaa90c58df778b1ff28b6312a949bb1f6346ac89e3dcfb321c8127bcd0821bf9f3f278907f7a002dca0cca6150c313de60590e5d414bcf0ffa4eeebe2b0f9d47fae89d06d6b5ad8ddd8a9d064e4de1b3508c2e81313c2ee9edd39e4a19c0672d5c642d4cb2ec129704cfdd78b2b10038334b0f9035850b99e14e6603dadacd4f1682e0d61e2d0417490ff7e296651a9d9760e358db8afbe15c87b34f28a6a4e5a9d458c6c777cdcaaac034bd7fad89f586f3a3ccdc04c33510c2a1dc1a601f08863e94950e76a208e1edad7aee53f62a907abea3d53e96d57ac24b4b0e81008ac4f9287ea9ffc4e7afd6c920e15933cc709"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"36ecd73812a11b7b7ae79a344096edbbb60e8cd6f8d4f21580f6c127487d3e12","proof":"e460078a0a406701b2603636142fa8e7eb0bc35e56e2f81ab9a21ffb47f242521058008eb899e2e3bad79b16f5cbb9a01a4e13a960c74738ddd609904a573c5baa0b088d251f27f91dd1548b8482d5eeaa8c82bb9b7dfa04bc4a90dd55478016dc1999e183f5aac1bb478d090276a3b19c025a4c08cb250b1fdd85fb107d6d3aef9ad817f17c593ebcfda234283a43b6b76c5ef4d062d121f0d5067c11c3930b28fd23e605783a94a9056ca43a361bd317c5c007260890a56fecff93ce959d070a2d78d12f30c7e1df094e1c3be395b492e7d689fe2ff9168527660c3b9f5c0c0ebb6f7b50809ac8395542a3d4395708da5e1b9f6e46b5ec206a1529e7e52c2b7652f330bd1e49f1d9153cc9b81b26e3b67c9382397d6d623e72fb86590e9d6f4a6347b8f9a0266fcc86135b2460686b40159f751a5672330c7a77090524790598ce711f2f8d7ff614b18e2ad1499d3b65a4a39f4f44455f500d3913203f1207201245015be59329bbfc410def1170d93f06e3483f2c60fc4b08010132b96043befa6c189142285cac125261862caa3b377f7a550df68e1870b15426397e0f6e1639017458c925eb9a61aff0552908d971287f42a6122bc224e868da6de7ac27dae62fc55cd286c176a69ca7d6cf91d2e3e1f7d00ea873ad71deb7ccf9550169ba9f0b4c2c7e4efadd67ec85917ac87564b13cfeb9232738008994198d81fc661c44f9649d996a6a18efa548cf8946dccebcfb98f3ca3220377322951e310d047c71544a6a69fb431937f58b2a7010eacbcd7417224d85176066a56f8e71cb2bc4f54c880d27bcd2ce32c4aa6c247ee6c4d58b6b017eac684b0676975130aa2d90ba9874a1e19fbec7c53e67dd853ad6ff554391d48cd7f779a0f68943ed73038e6c324510a0618437de354d8eda98c42abb1f0d48c30e4649cd05492291ae0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"501aa9733df62b5eed08d95f31d0662e7eb115fcb54cb17edda86a884207585d","proof":"aa644dd9a8a29954d63a2a6263469ee91e86fb950df9d5989b8625dee44f636f36ac6a5ce414e11c070f2cbe16edac008dd7dba154903da869dc3e26a02b5c07fac6f589df1f2eff72518799970b39c8904b9790b54af8dcda17664d24aa0550ae716a6d1c2785f99857bb0728111fcd61f4bf53b06eed016f4032c05247f602b4d560e48214690b16363ee331c24845816e334b3127ebb39f410c59305bf806d0a288736baec4d1db4b6df60befd2d51f747c3113b0d0ab3cdddcba6e7dad031dbd58b98759e82afb50d25d8a522103764ae8167d4781f214f9aacebe3dce054e194ea023241549c0c919bd04ae8738585bcd3fb9cff99d49e6b3f36242097706ae4415feab5492ba860e08aebb16b8bb3e34b512196fb57d0dc508287ff161be57633dda530d1f86fcf84d2e780491105f5100a2a3f6238fbf1b534f9965349a7daa243cc210fc64a8cb34dd5f40c34ead5c6b571d6e585e28d27a17b13534a8358ab8e9edc2e0c04b9030e8369598d5d7821cb8c4016c722d4a2bd163b1553e4974066603d9a7d1e7711ea6cf3dbb4415beff7d0b2152b6cb6a979273dd4ca0ef3e43b361a00b4eb068fe5e79aeee34f233bb8b9014a28dea3e66d6be0d5a8822c10bc33886a5f1bea1ae7e26b046f5852168971cdbf1ac3dd6e396d7ba099620dbed755f868f2afc51715597c33070efa0d26a219a44c2ec18421e43246d0ad8ba827f61b12280bfc7645f9e114843cd533a10bceaa3a266f37bd8455f5e844d841f7dfa1bb439fa82b13f9441db70c22335a6d128ec17120aa64a051f3df8b2f59224b0ea6e51d1bf2a4c4f4d91304561405530534a0a32bc92b931e331e1b91cb33ec92b5430fcafecf842880d3a367d8d7e400a7cedd977224f7c1809c476fa2eecccb22750753cdf199df875413699240aab79115cdd605dc4375508"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"801bf03d30dbf500d557e359c64406502c61e153b2dc99fa0508552ff8b8963b","proof":"4085e8ad23cd765de7ff3d699f95ccaaf17c5720eea2f6b180515a6e3fd3f722cac63d2923cbd8904c64a16a4ce6644ed6c004bbcf64eca9613c48151cbd096c46cc31aa673c034317d16d69b9734f48e3df1bae113df908ff5f3c8e10958f1e724a52d9d77c38e849e6d7c75bc136490c6f63c860888e7f98ca99eb106be8751192fb526172ab05dc01db028c3254fbe1df7ce7faf88a16d9346c20fa1a2e0d7b1a5e74060c3b3e8984084f7c2740f172e1c2233f44621babd9c199a0c72002344daedd4db5f3c12ae0865b660e53b59361be74e9043aac8269659d52e3f40954e48a28a06af4a56e0918dab10893b1fe84f0f484fa8c9c4e02e207b9f9bf5b38e2d0b84fb5fd4e2c5732b47f94849ae8fdd7f51efac4f585584d1e9b2f66656614a9d42f5e0451c2ed56aa083cb24ca1cab9382507ca80b9bb4f7fe8a14a549ec035b8f3b19deea72cef17ed9525ac09ebe2a8830bc486176ce4941ddee413d8b599177001045eb027badcd708888c439982c8658e715f16be0efb1c056c07408e0ffc68f74523c40fd47b86721a34be2bbb1f48af23a7c25160355a7f9566a4ba6cb9e2e98c78711b64cc2c897149bb9f20ce12eb0dbfd4f994a4efc037731c0fb154fd6b464c9111a00b185caf1a6adbcf48fd60ab036dbdbec0508156097081405dbd30fbeacceafb4d3fe20f4c681f7d869bc41adf61c5943c6cb7485df2351b82c4ca636fee819008b7c3b21c4daf9f3a262271bcff7b2c023503e017f07960e97c363006453af472759056e9e4c5d1b9134b1e4272c93df245627f62daeedb424c2d6929d00da0b500f84191056c1194e0bcaf6d9547eba1060bd2796ef916c978abc7b8f0f950e3876e087f426e563a8d19df03137fa54fd4b31304f968209860ff27f5185cb7e1908d47dba3eace7294a31961fd2f85e78a33df0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"661e124984753ffc01742ecc134ec0a0ca92b40dee6ccf283e0872de3ea2bd53","proof":"2c4bd5e688ac84af15a4986851ec4a3e6c80addfd12fbc5446c8cd8855403b346860e77ee5d69431c950cd5fd225743c2728e4c68bcbaed99f0cf350333e27249e21a78d0b80c421b98df3bc09996dd53f11332d102d53e11bc8d7e5abb10d531c50e8524f248b5fb2f31b6d1de7456dc5dfcb72580139f5c34e4a882a35443798f77a8d744bbc479ed9362e00e2268b6ed55761fd025e5886bb7eee42c429015115f0dd985f0aa74d21d51ef4801a04bbdca0dd1707b2b73cd71f302aa40200b2803a291669ef5413b92647c9164ce7cdc09d38c080511fdaf3aec2d4e4f70b68de233976729a75a62d8c7dfde6b9f2d1f7e98cd3375366f7f204f15005417fc02325f52dc53da22c65da73239f49db82c7c50bca6fd954b2b1d20876b5d71c04c8f174ab948380c2ff73bb36a6a78324eca5ee82a92727729daa3f32496453449cc28026d11dfd274388447c0f51324c08a81f0f9cdbd6d9ce7c66aacc5f7b2c42ed0f124cd847ac0730f15a272f49ae37340c2bf3241de44feb9789c2bc39a6d1c5c28cfca0c8f1888bf54d65c2cf4628940d526e6657ac44a6fe4def210b0067351c1dbfe444fba49af1f5a54e5a8fd6169df63758d1da029b0d6d66d222e46782d7517bb78c2a3ef1ad2cd0c322e3dd64add5b9f0f398e3fbe51d3d754108b7cd2d3a235e5d036bf5938db5c9035b851cb1f2ef54a3f38f0ab92827ce701cdc1a28bdfe18441ddd0010928b1e9ac6f33eb96d8be0dba25f19602c85d579de2edb31ca695e4f873f5d53425094acd8780d848f8359df755286ff92dce65f569111eb31702d710ac54c9deffd745291cf0d377327a2b3e64dae72c5dcaf587e6b7c6776b28ff2821448d1a4740191789b65eefdd8a4d7534b403fa8834b0595177d1e06a3117ef2d75027ac119a86ce41bf47ab2493915f15bfe33d35bf0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7e6110a7279f50754a7bb34a252ae110af265ed67757270455476c9936087f7f","proof":"da5c3cf907eabbd3a90c7c55ede8aa600708966097210a3aeade30a7e1a9c155b0c5bf2addcf95c6df991f9592f18286ae5c81601d717e0c42bee76d31f55e43ac943df79c0a12c4784634dc8d32dcbe0d45cd45054726e6294e65fb7c359e4f94d335141e6147bc8c129c9c6b56e0ca5627d97d4e376891dd6954fefa8fc03c6ab4b6a2db9db2c499e1cf108f13caf0ce6585dd2689ff4b9177c53ef6f16101f8514b37d3dff19b391b0231a5c5a3927861d1313692e043a3d1f4f5cbcd6a0fae8fbc1dacbcb746bc40a89a766490c0c11faccae8d4c42cec146ca41749190adc6d10e263e21bc69a43e42a28b530c2c8e98f981a49e87e9777768c2d858e3240d79b7fe4b99c4419a09abe2da166dd600a042fa491f9b7cd6016f573340228666afcde6dd4b5af405367c15606b6c63e51087c982ea9d43f92379f3569f46f161387d980e0724f57558ea8084cd5228fefafd20f825717d8d7dd1050c4e120baf98c4cec4b4535a78fccc875dac9094bb8c1d1b33aeeb85c98654851325e37f612154fd7b33a573b461d7cc6b2ccc9ef13d4e3da9f9fadf9f3c910dec1a71d268177556161aa9eeb93024953ef5b9ff5511e190a7c78227e06d3c5f7c5877a9e9b0ed996096ab1299e3e4d8076c904d019d614e7b50c56b9c64e987f3e296dea6f080107c676dd66d52bf0a0c065cfaafaade676824eb7146bdfe7589a1e04c284dc14d4b5fe22b8277674e3e0abdf961738ce17988c866f77cd9c33e3c849ce4fab25af39e99a3fcd87109b928c865ccc69c14fa8920dcf17b06a8a912971ee71923d3bf7f1299f84fbb3304819f9bbe32c1d8adfc0019a0194347ff67a64bd326f6c2293e520609a8d91f41b5676e94c82105864a3298617d800536a19014cf19bdbafa8a07c5f4a9cff5666d4bc1e4c0de92110aa3e57f2ea9b78256b09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ce582106fb17fbc387dc6c55b18b49a3592186d1b4f4a79b9cacd46d9f43db0c","proof":"7e1398029df55f5ba36d762b3af9afa6d183f883bcfe981774e86deb76335e226c65ebd2ed3b74a64672cb9df5f781533ba9d55ed5c0684818476e1a567bdf1cc6319e829285a0912d29d99b33da30441e1bb2389e5fb2431719f02b1255716c3a448d0a2659c1165ce20a467c65a5efd5d517416ffe32d15fbc93262c425c449eb223663d8c10ef58d62865f395123cefb7b4f407eb5419e117949749dd410cb80d2bbee86de2868d9b9da5f61c0add71b1c81d10c8cd17eb1a91b304835107f21b3d293c3ad11cebc36ca6034873b262e89510265eac27ab3a6b4febf1a5039815439e8ac9890ae4ae200629d32210930732a7e1f24a620b3b5a4e510b4a0e82db481104cef4c3f2205691a13e38f7899593d325eb56f1662325f221a8d22c82f1227bf5ee1539e2cab6cd317faa9c3f1ea7cb548517331582e57084626804caec7236a0221dfd195e886a17bdf65efea090a2e8cbed110175f27309db0653e658add70363d5f1450074b6fa9069fc1cc7f6802f08463b4e517a87a9fb8e324c87a081fec86188ae5778506fd4cb454a14d67cfafde576de75f3060b69904006158b40492a6c32bfd39ed9eaa98ba309716d6b9c0e1be7b0a2fa7aa2303b1df8cb5b4d21dcaaa6faf48ce6b95e540d8f9c8d38ede8d68ba653b6ff8d8cea6d806835184a394c21aa9d450ab09da3c50c5a6f631d372a567dc013bb8bd0524a0468d8bb9d9b016d55a7012257cf93120325b1c71cea0621728eaeaead2b5e5114cbb9181e4ff8461b91ab512f4540fd1bd0a8919147a4f5783988c2d88e6a6072290dca925186bfad4bc42689a0ff1fd384f6a99f9fe3661d9971401c55244d676c8e8bdffaa09abafe0971b52cd1d0d3f2aac3f79fb2dc3b9dcb6787078e0eceace568345ec96641753612ee2ec4d2a9d5a845f087e830f9222a6d79d48f00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f0e63f2609da3514d3b793607faedb63fcfba959738c56262c406f7037023928","proof":"5425a52d5e79eae4ec8276f67d47eecc24e88bd57efc66e3bde5ef4ae895fd0b4c36b4b8dda1f50bd88d5934488aa7a82b794153bc2bb542e1f40e8a93ba505f180c5d4e70c93785f2f5591707c87fcfc4e997cb59ea715efd2b647024e3c14b10be64758adc79778cc34c2d99deff09fd737205b00567a7826f28cbd7a55170c3a7f43195f475006ff707ad9257ed9636a8f557f7196044f8a288372968850a0ae1673cd53959edfaf76463c94a2fdc7eb5ad00f4541b966063157d6e397e00e6ef7c9545bf540fde2685dfb8e42dc2a735b69014e21ac06ed1c425b1ec9009103614a8271b030f2784a9fe54d6582c9c987491ed3a70ca7364c305a0dff864ec26872e2cc42707db8bc256d103b790403430f5a5ff76a14659ab0257edd721e67fb3ee82b2b3c6671dc693a9c3f4ac010d1824c3d1f8bd6a2db0afd7fa6d7d16aee440dae12c2c1e9ed85c4c4b92ff3b02502ab21253ee5449997ebab78c136254326305a5384aa5d0dc6393ea347b9bbdb66ce9b36d863936df32f805eb76865eeb566f9d1e82fd4d7c4d32ec2b2f595c3621e5a6d597248adafb8cca3e47e2db57fc08d5d906a8f31aee4989747d495548e872fc9f699b2d156fb55ac459789649ade467619695ab22a33c6fa0cfcce1883229871effd9809c7c9029e241a43ca3d7648789173b299e72a04f39c8e1c0bddc4e3fad3e0e79192866406677eadaef657cdf4ecea441d51a52d7b447858c93f4de605a58d17030585fcef819b29ede92b4c4404ee2fecc0bcd2680f269f8ac37f85d1492926b557e4bfbfe28369b4f0b7b6083018bbc9810f86ececf79fc7170edc4d9c89127f16b6b6b860a793163b624679283cb433f52f4feb2bb0b4dc7a2c93e6a09c3e939a96e2aa302a847979790eb5378d656f05c0eb45c6f55c1d2193f4a46277fd76fd44344480f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"665f53efe245c5714c1fe1665ee9f98c0cf20d7bc13f3bf179228cc3fad80044","proof":"64673a0db4d7cbc983765064f5ace527d5d11879b50e29b01a6386c700025b184a3249154208315c028a5273bc4fd528e8a131aa8954709ed81854fc49354600eef6aaf8412af3fca0ad324f01b4cad1f06ac0254cb1a3975aac42cfd62d6d49e22c437196ef24e3da5f509037c5abade712c0a0cb961d520582bcaf34519f3e24254c4f86ee1108c4c4435bc76e614f92b69091f41f3d22bf8615d29ae99a0bb08d7e56bfe03db899ceb080e033de71fe162807b82edc7249b65b8ebe5dc20d7af92cd90705f665fb5cb6c96c316e1da9dec95f1fd9417b665aee85ba3ba90b3872b5b2b434e96e68042f6883f291c736b3a286643f81f517f79e6ad16acd25444921072b19744208cf2bb1770f2fdc55b4aee4fcde2adc54feda81f0da1c03682295bd1558d16835ec7c2f665e4ca7a37b366610d50ab23fff5bfe64604a6c42deef77cdc08128842adbae4ad52a257d67f731328672a0c5654b31bb223d1672fcb047fb6c4e10c56a4d6ffae78586007950ee3fad5f32d915407b1204283efe27c59b64a1bf66653a19edf377596dc609bfaa9d786136851a5203a10b4e2bea39221e9f172ab6a7d9186043d71182bb57d260d3fbddd488138c2cc541da2c1e4f9a68dea264677e8bd28269be78057bd3a0c1981a5c58f805bb276bfaa22778c076c841ccfa6883094a9e8c5c4fbb17b6f377b89d196dcf2f1622610e5a2592b3ecc3d4cb85e6b8857192f233f20d5ce0d2c65e2ef2dae06664ecd1e5c521d84b5bc2449877275146d87d299345a5d56f1f91375020e552cd34422f923b572698094345982e38081a344cef4f0a563cd4a738afcf280bbe4be35ab5e6296c779914dde44793835b7a2c3393a0ff27866d210a5defae67c7ddceafa73aa10bf1c55ce6f23c6cf6172d7145d9c2477c7802b6f41ca1f13bb0f438373a996608"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1c9a90327ec1033a9b1f2372df0ab98eaf847f59b747f7fe91e6d799f1164b0a","proof":"e81ba18b8199a00e9b8c4c6ce6c5a8d54d66aea8c2caad41e36d8e5f4d161455dc9c8a5dc67bfcf628335d8f28b6c0155dc373a529c2d7cf4d72f1a59f0e6d4292c4a55a5fa4dd0e6eed292aefb2298dd9bccb3c2b9ea26c3954fca615655e7c0ef1242adcfb34b429e8d9aed2083c983171dbe490a54cd73c7e7962baa73b0c1c2bb20357afea795ca3d3b3ce852eac191dd3222915f29089b662c06796a706a3de9358d009b5df506271fc72bc52919d81976ff49f7e0598f6d105bc5f190b51b2e0b55e647b13678ff8064a850ebf0314e9cb1def3856c83c26aab7f1c50aa00ad1a6ffc771624b5116215db8d2cfc9934637274d9773fb79584461924933409e5785584b2fe150b918b9a70dc9b13f5b3e353722b9d8bfe8ed300baab62b386f76d32259ff0665cdd34a824d9ba5a5f99df4d323f5dbed5c84c9d3c1d672a8e91f87553ee77d48bf5a721a51e7b57235599863436a6aafa4f95785887e6376775116a39261ce4b481bd03a9dae03ca5aa6d5d9ddc82549dd93153c0a0e56e48ee0149ab1337145b978f02c938d9ca51f0df635b6e8de542a9612c0bc1d006807880c85e8e735f7dad275c0b645412bf5a99c1880caac7d4777a7a4686462b67a2c48fc30c8a92541cd21f37c1b5243c09bd1f783d574823ff2e4912bb32ee478c78ac792efe9d4ae88dfb58bdd7e07a658a656254474b92bc3950ac6452bc852f06ca45aed3ca57ab860b2e21341a03b8573dbc1bd98923e914444c0d77354166b3856b0a3f5c664c32a0d84e659d156693cd049823c0bbdad72d3905e64ea9bfb954467c2b42e127ec4ca27871327016cc3eba06be1dc3a80c027aa24113867c0462b4a0e00ec8d658804445823c4d5135dde169535672c6e202072bb066437ee54b5b3389593d791ac0359a61d8025c59aaac739cbf253c160b5ec8504"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ee8ff6d1966f1bd4394eec5114ee3548c0133982bbff12d00046e5636ad3ca77","proof":"b008be7fbfe803cce73f631877743d6ec719d1fcffd321569fc100ff92c30365faea1c968ad948c513a42dd9319fc27f3aa22a8befaf4b93c0d1b77770798456d0525588f5ab8ef852a3e21905f815e322445cd7b5e713b86c63b785650798322e1c89af7eb98f98aa0ef8f8738211fbd093de84eb1d90bcc7a1e4e915088977005f92b9136dee1cfa64c2710c85d64606285271026c484ba70f6b6b85c8f307fd9b153ac4cdd4f414b83468238de1907e7713116a7ae73585fce65b63e6b70681e8097e247e813f1a688ada1577e8a45e80daf246a32f03cf66d9fce9a242073a564a3ce5bc532441373da107ee9d527deb439a54169c0257ef9a9de3878406c08eed4cf47d1bb65aa10d6e175f24aa26e04b32502137a2e8c4ca20a871957a80f09e6d8643bcd8447399b18ce8e9b30672eebbd407a973dcfcf7d0aecf1e3f762da579be0874a094555f766c12f80e3fcf597834b6f5c2cc1a98bfb64f7a37aee907730c5e49bf3fb67f5db055d6659ff5d840139838e8eaf25de3ab3bcf6a06f9661854f06ca611ef26a868289aff48c3c00cee832eb59291890fee74597146dc06a984a4d01f1822a4840ca1177c6586c48c443a6673daba7da3b228042cec675d631078cc34a22227a902f33756a9ac7884f4c78de70f87dfc08bbdbd2f5ab6980804eb7c0fef9d66344b3e7c42d620f138e0d9a4572edb8b5c7a6ca93d5ec9a250285c20ffecb7bdf4a4ba6b11fa8478ab5cdfe7b14c788eef4c6a214abc47405a7b904fd0f814df933b29f3558f368c2aca77a9607910137eba8c5f089a2c94b8313b071caf0831a216cc485c7c4786fcd965501a29ad92065315331af76fe95a551a187b35a601394b7bbc4a1916cc3b79626a9b01cf10d11b183600932adfb1f0288a4ff65a43e9322ba49c42c0a83be35f8cdfc0958d9ea338db09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f050602f9f6171fb8997cfa0d5941d46faf4fa8ea303d0b310fbb2f2dc8cdf3d","proof":"ee038999dd7c737ae9792e968525175e2bcc243f494ba954e5da5fa3b8ae320642d6e5999d6ae7276e3f271f7505ba0ee5d2dc156b32cd2e8c370ef1e9d09435cea6810f972515df007601f2ab4cb0cf22ca3e7d8edcc1f5481559dbf5836d19d0a7b5172ada691f7114ecd8a9213017ad8d1d3864778986c2b4718dc948fb7835e8e1c81fcf9b5a6ce5c3dd66d789825754201bb23b57ed0a88f2c5f46f390fb0641e096efe9058af3ec1e8b8751837aa13de3e32a5bc0b7f3d5a78a42fad0915cc90199db42cd8f75e3f264939321c2568eaaecac6ac20281f490785cfdc0b348c91a45b68f63a0e1b6486916e62ed606fbb0d252e1dbb4c8eb474b2eebf7ed482716d1dc9034f2000de6eeafa199286e2a66298572d0347facaadc961d3443ef95355493ba37afac840b73a921d7a86d9342e4e3cb4527a1f201b485ae8587a7ea281d715cb57ad7816503b1a1710915a466d1be563f14b7daef5e479095aba50c28b8b77ec73139f4e9c62e9c137d752553c6daeba9cb821d01e0babfd05a26d97b5828315870106e99858609a07486610c673a1cfdeca6b8147ccf67f05fab393e3ab0671c2d7ab3908a3e181170501dce96d201bb1f953b246406fb1395874688fd330d34772417f72017155ede47fec74aa095a1d505a9d93fb11e00fe4bf44e1f04a1367fcf1f5cb008565b202264e227ff2a1d4c958ffadfec9e61ff86a2cebbe48854fb5aac60f20a28aca28b06efbef1e409bbb6ea447ac2b5f5bc8e0b65e1d9fa1e1f362aab9787584a009b93d0d9f2553c7fe723c02c111827c5c5f024554b911b49b3e3d021ab669b6cae6ddcfa69f0abb04e870b110cfac3d3328a7af48d478a2326e371c870f2baf78c8747234b77a483e04d6ed4f4c2501a357a7e76c124fe91c3ca48e06a1ab7d4487df2ba4c5e63bfd3cdefa14287503"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3251def5a4d379e87d58f04806b0265154e3feb1309fd96191e165133ff9f175","proof":"80c8f0e32d3804684a018f5e76955d71c967c048f278853323e2f6dbfdd5051028ba31f59ccd64959f88cb7bd1c1c9b2fd21e6d52c29a883ee044ee073046a1c084e7f7b29734859f266c58f9a27475a19bdd625736cf00f9f76726447171e73c6f781b07066ac027a09bb06bcb9ee677f7bd509d8e37a48d6990647df40d905ef2fa11ad831dc5906beb36d2e6ac2b22b7e7238272c3f51f9c262239b57ef07db60c96c7bfe3b0822fe86c19b1ff3c9ded8423a633db0768e551e2eebaeaa04ec95bdfb378e1c957584de7f650a366c976ab66ba45c9aef547eefbde246630094fd62889530016b06c927ea0aeb64053b4b120770a55cac235f629b1f329a44ea1a1d76adbda9f2d6f15d00fd2f917907b5d6c361b9b518c2ae373c5452481bc8ff4785aea35862f291e65ad18b2930e635276c1ffdb044e45509b9b21e1c3450af69298bf6a9a845ff1099ab0a4c553983cb43dd417ec7e9bc6a8ede2dfa7756cfee275bf8eb3c867f18bca34d832f1aab63db0303a7cd99c7328b1e03ef3d02ec0dcceea781cad8e319d29e67626aa500017ca49c04722d4dd1508c2cca1b2c6892968d008f9ff355300a772a5a1ad60666e7ffb4d69ad2965694a966d34ce6ebcc3f82862710a9446779950aae9be178d197d40e1f02e2dac45cf369e24dfab3cc50716d8c0d929cb4e79a6aa7cbddd90a5ce8b2f2e10f89f24d4842c67dc4f0750a7d0fb337460e3f5bfe8fe009d168523aeb6aa7a0b78ac65929ba7a2038e426c00d74b270e75b38a9047b3c984f1a91a1932401195cacf2ee0fb8372fc0f8cab7fa4f9d4fb106e322f87339a56578d57aa81e9985538a1177d4c52409fbd418b70d5fe4d7d9040c8e5971e71f3f87f5a6656a0d26ca60b70fdee91805d011a9bdf9b60f3c3cea143e81d78d58fe418c21150412e203d023c927478a02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f0790a74852e0f327c82da0a11cf194ad57f136a4f8ac9f7052fa718c5c07c0b","proof":"624c3c0c46300851de38de3fcf4d7977796eaaaf69558d4f7dca62949311d073561b6eab0fe15afe8f5989377367db89d1c76e8fa50f7d03f3930818842de76f006f24402f2878f3d74ee01e65115d232176ef619ae7b73ec8c501a9db7393226a8fbc5dffb8e4682dcd0376df5c199ad2bac130b917c92ca487cebf910a78017f497f3ac24a9554060fb4b34f70c88ecd617e8268bb04aa4421ec93ed4bd809ee0f8442730984b31a76dcea4ddd13e36340fee9553544473e9ff1e4075d8b066645893069b8d5f7ca8ee4fa7ddd32e2597295aba6e7461d55f8814d07700601f410cfd2fd814d6361fff1f01d12e5df9b0e951a6dfe879f3b24d9ad05f7b40b3ab8ebf6744bec05da28fef8896d795960eb63f9e6877747e85d7e451122845df609ec6bc065dc741eece497db8239836110e0feee695c6d30d6024d7f898409b218a88e8c0e294fdb18df7f8d5aa9cd6a2d0f0f095f2765c54275bc9977da67de7064d5914b4527353e075960f5a1978b37b00c1fe16e258d69ac7401d62b541cd3e4b4130549ea3b8cd20e1bdf0a4df8b46273e44c2241429219e1937aa00e0a617aa0c3479719f3fa06048786fce51e5ab9ee91a8828bc9e23135bcf34b7c48012061eb0c304f4793a1543890b7652e5e47bdd92c3d1620f18acb0583a561ec81a17dd2bc2dd80c192ad1da0fc76a1d3794fa2298d9ccb72659fa434eb04616ca3071d22d420538113ddfefbaec0475962da3d7abd018d12287546337b866c054126dec4946bb60c9eb778c275e51f7e11b36dec0b02eb0fe46a5b036a85678dabc7b7f855eba2a9abb0edc6da6ea500eb90ad1c5b1bf3f7bfdb485ee8873b12e97a6d43e686d4eb38ed43343d9b4676c0c3e856c34ff89f750eebac3b1083fef8cc8611f2c293d376be5ef5a3663ada735a66b5d6575deb5df16d193b905"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4e8b24d0d957224663c23decaaf66ebbe56d31f0de8f5dbacf82edbca2f5dd6f","proof":"34a715e25f5a1880d39ebde660450b71462bf8fed81381e06ccf10b1a2d57b0efe2905b1fc8fb279dad50973c02f866d8a20cdce459c32c17df2f9069340d94fb8eff9a8e40b4cae0ddbaaaafe221c5467d7834a4de75dac67f94e502b71256dfea1f430c07d62348a7447440aa6f176e0ad56a3a48c3d1a8a0ca470caa7f638eac92ef9a6558535c6c59240405a2ae3106cfee78ece99d800b38a65324daf0a02d804723ba380a9156e115666f8eda2c4bb35dd88d76c9d32576d96cd156a0aac263c70736eef9de210625901e230854174b996650e31c66b34d808ad4ab309ce9c8e05a0db6beffa1656dc7c6dab950b9a196141ea0cdb17ca385dadf83a6eee27e50005328543b13b9074300f83506b9c07d947855a2c833f7f0c0580767abe52f903a64d1448deec9dc99edb0c71916765426ecb8014719ace5e884f8c064e19a425d111bbdc23353d5a79e68652e582000c183fb3fe024ed4e12402721cd8177471dffdc5b2f8b50beba80f74f5b4ca17ef866cc4cbb728b8906a36f612748083f01b4e920e6b0c592c73ebdbaa20c7c89ae9cd97a696d6f6f00efcaf4896758a3b5d3f06598f880725358935d9c116377386722882a1e78d8dc95f9616bcd07e6045d5d98bdf27ae435a8a244302cb93a3373893a65660905ea45a2a307a03501b5b6e365a9b2401e9b3db9de2e2edfad4a08870cd81ec8fb598d82572d0b6cc93572670410d04e8024c041b2fed59bcb14163cc4b760f0c5392f6c22aac82a6a2e9b36419258145d4eae77659c0df1d79ef2d26142e5d7afadb53df74bc5821d4c5744269c7c0e600af576effd9eb5b715e73e32ba1386006f250c72d10d99338e0cf9c95bafb785fa29e25a43f609761f7f3e003237cfc15a5812607e1b1057a6ec10b7ced80cfb10a81154f933df88969dd770ea5f65958ffb4fc05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6e9fd22bc3a438d483f89aa12ff49065c8d281caaa5f65941919f9837afb7429","proof":"da481b3ed0415f2ece7268fd67d701973896a97f01a3e78ff952b1d6feb576503cba21647c31d4fa120b8c295d89f5c779864fdc934092921d36adcc0efbb31e168e514b5f8410c93c34ca726d4b33671fe842e4631b1724a33826ce04ff3b49d43f50b8b224a14aba09c88b20d3d04acc00c4f0a8df9448b176faf557204b6b9187a379708b4a8f15750310b2427b68eb509a40d83e5f796f697b88df8c0806235c1dcafbc16fb3c7078aecb4977d30fd4a1596dbd9f04dc57a227f9197470f805e1313e56bcf04e2d41653563086141cca00d52d761c3556d99212e0e48506cc6bfe775805e0e217fce6c0033ef8aa137ebf02c06fa8c44a46d3d76cbe616ad84c26a1c8347161ace513b6138425096c25de76d3347631756a24148e740406faf3da19908679b27efc89e0e983ae5b2a6808e19443c26f6590db965401fa74ae73b965d5e47c5df9a7cf0bee6892d9ddca6d2e818eb03e173893fa6cb8151978dadbb2bf18c884e9503a16e40d223b45429fead6b9da8d4432a38c9ea4943d88fa52bcb5a1307303efaf72fe493fd819cf7d20c9693ae5e904ecafa256db66ea0f88fb6d5b49fb7d6f2fa2b04e27a380e5db410154d95aa0b16ed8daa2185ba851285f2248203f444766f4d9dac7e5a1e178c76ad7f0524716006131a5d5380ce3e2b3e07b52ed01b9e56b4a5eb411137dcda2da10ce73f044f468ec3320351c7c6a2def107020328251a05d4cda72f0b256654711d33ca7716a8c9e4c6814eecfbea456fd073bf008185d890d752c0cb9086d3a8c3ef89459085d9e95f300c234869d265fc01b8b2b16ab1aa29c5c15886a69f157202ebe2808c9fdff9f4eeb9c496f2c39fef2730f7914bcbc5c372dfea03fcd0530bb38ea6960e5ccb70e90dd188b2cf09c27870933b7db22161cbd277a69dc2cd5efed0782381169c90a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d0f67fadfb56d9a480b343ddf5901ba27afd093640ccec7d34208dec14980930","proof":"d6ce20251830f4e7a363ecacbf4f05ffd331ec4efc3f173e2f1b63aacfa6af191887c057ac3002dd820d5280c0ef8c0838d9c3a0e7f5079043d275a653124d198812b4fa8914ea6736cd67361feaa9bc01b2c84f0ee8dfea66554924d46094066286bb2fd68c8643bed3b7fae73aa6dee268df58ca8a18e2664d9faf6ed4532d28300c712f66af31bde7e2ab6d99b1d7fd98384f420f5b4c6ebedbfa9412460a34f450c73d18fcfde5d6a1a9575f86140036afd62502310233d9db370301720af1a1a090670fa4b68db382ce58617c84f5967be18087387cd2f3b198db8cde00faa5328b94a9f56803ea5e56763503fd68ad2caa25e97b7f0391ab4f12e21a7ca20d912192993db38a18f73de267ad9e7ffa9cae3b20c0e5b6b219432565de7dd6a082b6e35b93c85f30bf547776ba3dcf0d0365692260c382aa571fb2f59064446a694c48997d23602c35117ae1e8eecebdc19931d546a78d41c66ad3a3e72ffee472792d37313bfb43a18394b86f2d505905a5f6bf2652677f9b4110dfc03a6e369c8d16cede21c6cc66d75211275e30fbfb732bcbad7b3093ee2832bbf1535ebb2b639555520ad721c0b0f25f81c62594381c32e6167c841ce9d1985ba83da877126291ddd19736c444c62fbee22faae8805ac62433b681b2247d2adac92acac4431137cc317a7cae0f8370e890a43f1e70ac555152a594544821bf1abb55b6346e430fc6ec8b2fb64639114a3485c8d1185505e44eefcab3015f9b863603bc5c2ff18b2de44729fc08861994059c3dc644d5c6b05f9c836509315415814a5a7a9febdd9fb8eff00cc62a6cfc51166ec822a23f88dad098fc0d073720e805c42926ece4bdabc44b428adb9bc44e5ee3dfb2d4c744cc4d5079783c8c490707951a5a075ab259bb81cd07a116af0db2048bfddd8c38edf3ec6464e806877600"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3637c7f2e01119d798986d3be8e0fdcec83140a8239107f50fb8abf87535bb1d","proof":"54605c317bcbe24331910c0f581915002e2ce2a7b9060a6721a1f61aff520b5856e134ac6bf2211bfda12ed998a38f4423b3b89ed574a59c42dce56d4f42d34bec7ecb10ac57afc0f5ce05704bcca95ef654c689deb8ee8ccf78e4c384945f621cab316363e8c020377c06c54e9ae917162bc90de22b37cb97812d511eaea371f8b3707da68308e0ad5f380bb88baabd1d8bf52931ea60109ce9ff6ac47e150e411e58ad4ef4ad007cdbb685d1077e61ebfef85c579f034674c542687d20d8075d0568727be72578183d0d4eec120f9d9baac7eec8899ed87d164f4c5934c300620a357d2f622fb83db99e92dc7b95fa706d9316181d8b8f0043ae755d7539565e7314157345d39d35a21c3766efeb97dffa9e00b85603f52c93c2e81395d565ca08716eaac8626f1e93e14a11be92c20fdcd9a30a90f023c3cf48a5c756b7771e20c00e6b52a4dee0cc194f46cb37706f3d21666cdb9fc4cff3b223b5db1261006a746135bc1923a50a99c371bd1d9000838b244bd07d024e54246eef3ea67f62386573ffe20802f011ebcc121e5e874bb6bab31355bc0b2ed1a2aeb3657a317e850e73edcfc8b4218ab2d65e9a00b58d118455a6fdb9af5979724f05218a0f14545e5ce791878a470f583d892fe50e216952861ecb90ee02d030a0070bdc5972ee3ab0ed40cbc4daa2fe3522e1f61f6565b28fae6df00fa8f8b6e3f3e43034b404e079764dfd25c99b541ed773461ab156a56a838c8444dee2dbf6a8f68b5702592269fb0d407e7930de17712a47a3ca87516ae8d7cf8ee7dd95bf9571cb1cb07a626772f02152100eba9a23611740790b1e679f75d0934acd562555f4f979ad930b7fe463b429af73d97021c9f23d349e5e01ee54b7646cf889bc9a1f1807b09a9934aaf1e9bc56c1ba8f620af9519d6be3c666eebdf40e150abdf3e1a20b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"240a4eaf09cf76b4c68006745dfd88200cecbdc4b0cfdbcb98329b99fb1bd677","proof":"24456a7ded40b12265ab30d0ae6cf87971722ceee1161033037a49b9e120ae7924cbd95c1bb3f282518df1a8e1e24c0b7809bfa1fac82751f7ba475f0c0a38091029c163a2ec47cdaf351f39cd519a4efbbe17c307511a9ccbd42e83fa24d663345849ecc053b3fdcb6d2f33afb1864dc7905839e451efd6c5cc5d797673eb23bda7b654491cc7260e9994945a2c1d125810483ceb7a03d895e34eaca4307a05f1e52ccd88e40d8a332a423766bd755b3e7cae289248541058c5823a94da410e7ae43037519f7a5467db30e62d720575bf35dcb84d99e52b475a5ef576d4000588fc1483960ce95edda90a6e56aed2b7b6f5707270ec9f756f3ad27858f21c6fc20b24a6db1136a316922bb9f9e23acb803499bffcab0cd98400f00864980e73f4921b4c10547547e1affc9dc641fd4ca66bbe2e7e416e4dfc7be2f5194db41194b4b96053cc2dd89a8d56fcba3a000a36f69b8fcf2c1027ef22db0a84886a09a4342368cd05f58b53585c8518ed5cca582867274a378a0f016bf7dee39d25671a878749fe1c710be076dbe6f23c710bda0f78af1a62f0d24d3c9173121868551264ca364f1e5b50d09af2fe1f547d291ccdc2f86621bc640d525d16ff0294395e0002971a66d8b023a700d7307fb619fec6fda6f9ac4cb77ae40f5d3ad2df243ada704902317e7efa5913f55cede92896ebe0339a03224d90cc555ec1d0ac117432f1e4bad920c795b9c09858b881175e1f063d9243314b931723170998886d64f511bddd459af6a506aabe233890d0da580184bcf69a176d5a0173627562241674f8d9b3c8d9b33127db3050aec6dc0621be892a8a1cf025f1a1db355ee85ade2fca6254326efdbb217f2fd997b0915e621ec2715e417e19682c8dc253b307dc047d78dbe934dabc0249c5444f89392585f148260611f672801b36920b9c0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8ef87c7dfe855937031e0d198ef4aa543212a60bc85cdbf8e2b74722100c5d60","proof":"de01351d6184dac5d8d6d8db0e0bd8d0d1fc0c8737a4c0174b5ca4fc64e51a0e486991562acbc369b84a423d796d942d439b44d889f5048b576c4341a4c96522fab63dc61b3b4c46af2219aede9789b30a306d3b081c1137a4e9f87f0c7f897c0ad862e7a068cf21eafd9a823109f5bd2404f57f4da9395646dc2baedc316a69e4b1a902950707ad56821a4d86508fca8415c03f9006faf03cfc898b52afcc0efa03bce3df16ca1908ff60a7684958d14b16eccfcf47a92d55ccda1c4c28b80f788548f458434c44527c1102b72b994e9bce573c5f111ce8c5e9430896caa30aa8350965871cbf9de6c2ba34d4921074856f03d7b8abc26a9592c99f6fc034454e5128f981fdd11a917265c50d2c27a6cd8244943bcf6ff1e38a099e21f9f0446e5c527c326f749ba5a2d47176d5df0ce2bfacf6e474a4f8b98fcf9e6ca45073348e60bd9d855340d38351077dae23469a68457f8b29452f2a224f6a045c5d29a884aa4aaaccddf0148893de0a760630614033d985c5ff1877c9ea09c647952dc89897463aec650db363e7b154c7302f3be8f1395e9ba6969f3b628a8a1924578838d782716f54070d87f835cd13920f26fe549b53caf19aa5a21000d75ce246068361bb9f7129e48b35e4899ed2f9d6d8a2a86296592fb040a436ac62b8c40bfc3ea918dbf5d91d9e2765ca03871a9c30041e3917810cbdc6474e4fa9a9a930a4f22691878dd34ac8844bf0e1efdcf57fd196f17af477a482e0a5fdeda2471d4c221836c9981617a8b7ce9c23884a57e2a53472e06e30a03d45e51c9ec67f16a0e66a64132b75ebd8deff192d63f481111bf2568e7d55fb8cf8fcc6fec976090eab1061d5991dfaea78246a0bb9d51f7aecaa263d92119be02bda66d7c5d901d8e327c65a85e1451229299fc5562ca15eb15f03cbad36b39374e6e4218e1402"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b0c1b66d94ef530044a52f587ada71b4d5a6a2028a0fbc567119fe377d928118","proof":"749d41b0270b986def2d953f832b68424fb289152afabaed7290f5347d0dd365340c1dd05e710dffccaea058d451807bf1d61212210c9da9d212420796b9a476a47033867899307a155e27667ddd46a7a30c6b54884e6fc64b59f2fe98400703426e85c2d65da134c1baeab2c02d98db3164e7a0c1aee5839d16c3e6e8201657905e0616b191e8a6e144a475458713d83746f77eaa4d848e370296a4dbf36706b8511b7cb215387490a3fea19b4861684a8e7ce0663f3e0aea0e1789676d180a523050c7b0863a2a17c4d4b9c0a96f42761aa5ebfe4bdea29eadc6da5718b60a9429003449da3eff50883a639c3ff115bf64eb8a34877db6fef89a73dc66a10d26aa6b57ea46f3df57cb58d949cc77a69a1b4f82ce1c5bedea6e860ee18b350712a79dfaaf0d4b9003160dc903609a2f769486539b8f3967d34fe6c79923923f5cbafcf75dcf5f54669e730bbc011c2fb21476300cad7bd056e3468af00eb47544673985d5bb6c2348d504b1c0f6c58d0c3f2e5d6c7435567fe093a850fefd654288f402f12fa388a8d00ecf6ab7793034b8bf2a127a64172a30389c2a51f14c54014f16ca0cb524767cdd63b9eb1fbf029b140824d8b913eb555b7ee85ad93ab4d155ebaade84f8249c57fab59dc81bc6baca084d54c24a7bb31bc061c3076646afba4c11c25d3e280f6304d315ce420014ab28d3c5c8a7acc1909aa6d69f284808d0cb618050ff7eaf0621d7b2128d2c831856a7721f86b5b7ab8291398877887f639a30ce20e3989c96b5342f4b9baeddb7ef7524197096d0d19ff845f12aa4f54f8cd739d1a7e04641eb36dee8e800523f8ceddfde7e39fd5e951177c9127fbd03edb4a1c46eaa3fbc9798c7c779dad68d44c46ad2e2c152be48dcfc3c01ee7c4531c2a453db3e27418191886e64c5151986e6a4e462e81b97b526314409"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9497a23aceb27a1a5f035c76935f70c6c22249f7d97feaf7ca0cd4022fb2195e","proof":"fc88aa028cd5768db9e61b4ac11a8d311e7504da9eb1285082cbbed71cb8fa1704498a6711a6be77f84acba6573ec33ea430f864edf62c44e3c29f4c2c0e57550e38d9c0afb1c6d3bab8d8c659e6720efc4f6dbf7969614047d7647c226b30178275c72e11a5b0a409567085d3a4f8dfdf4283f352a075b0a2d1f245d3f6e475360cd0b8713f320369e150968ecfb34f22c3da6dae919a02e0d29f7a6a8f32042cad1dd5287cbc77bece3b7cadebd0f59b9c0b9d121f13f2050aed39cec4610004b5e26acd49182b3606196139e6f94c5199ee3f40ace9930df8652555a0f800ae136338586df7d798cf87666d2167a117469799df8e53e40e7d9cf2c1cc9968448c1a93293f353a8143316f483634b0a21019f0376e62b59564930801fbcf2860208f5ae179eca07e032a0ea1e961afd8642d208c3a6706cbb5e56ddb91ac4d3805557261dbb74e0990a30c2df8ab5c87a4f6340017cba2ab029834fe1a9b732cd3d32779dbfac6670ce0c4d72b5600e091059127afd457dcfbc1916fdd800764522d3eb38179f2b829a6692ab46e463cd1aaaf6cf9d491bab5f63c4ed5b5174eceda93f3851524b095c0d5fddf638851c57d8d41aa1898cf6fc55a76c79e5cf8e433b0425d3da24bef182d4089d18fdb77584a80c9da50f1d3893c8b547e29049b593f31f6d486366670153f70cb419d6688a6bcce12b4006f570822d4e8699eeef52aab37a5bbc94c115db9bf458246122c3e006e177742c7c3d23884060d3c61fcf427ceb1205e303a7eea899df369f6e46b6f45773bc1ac16346d697859f67f66919a5556f2a603d4bc5e7fca39e91657516fa172463d5f47c2f42f7751f19a6065afd977a8fb7443be89ebbfd8b271f661f30e6206ef2307d42c195504eb2116624da7ef8a1a087a61811b8aeb52c5da7387932d2a3b59d8255e12940e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e6e303ab000845f35d60bb860d720cd0f6645e83f448d243f6294e3ae703f85b","proof":"d6c2b29254f400702af41f1ebb4dd59ec1d8380b9b4d9b821313f8ea4bd94a58a630102aaf7f2a80b165624fa3e7a98685407bf6b650b8dceb1d02236660c440988ba74352131489096d8d41f996cce0edf05ac75a4a534b2efe0407c2ec5e338e69282246e0532f2e99376220d6e99bde41ffd46ed84b0ae54ed7a98ee18261e522b9d8c473e379098a43ae85f2d0507ebe9bcb545fb65bc7f134a1941da20f2e4b84417583ef4070316bdbfee3d870c5f7a4106c7cb81b8e5833862f0616019a9a5a8a55ddb376b8917955df8d0b6f31c3e3927ba7bbaa12a491b705545b0ece50b827c2c58f18ea1dbd096f9e87c6c14a4f5f7cb74bf10738606c63e9447c6429a052a8d874013caa6755aca5ff41f01779499151c3601c7f3bd5ab46222a08f5d27018264f08f607044ec5fded8fa8b488d1ea1ba3d852250d3a688ef362e2b72b2a15b54fce4c165649d06175935cd08c9d9f2f003e03654015cf62b00a9c5be5a508191e3338183fffe8e7472a05ed537584553c631159f56daff88f343accf30364a5d1483bc8c6e9cf4d9e207f587c6958349defc9ce0e733050731586bec9a5cbe064aee67c2004902086b2330ab3a4c07ef6bb30bca14f91ea4c0050e02c2857de7aaa485ced950d2704d2bd625ae41a976b7adb512e0c6424bf159004d4f4574eda895fad9a3d8519e45135c6cff406cedf40dc6f44dbae84bc1f12932e5d6a219e8f68e98286566f1e1805cf69741051eeb9df573fdc1177fc3a32bde93a3acdd31ce988aeb164991f1ededab844c52f26fcab73880aba14b1607cc627ffc2375cda24ac9f67dcb5146fb3c2feead2f162e5f161cf71f820da1ac9a6eb6ed735aaf9fe296a2917601e0d0645458ad8cd3eba6a1a4700a41630072ceec5a962d70ab1c6c8914b0598c539d1c23b53577b2e72a5532327b8b41a0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ee49fb4fbe7294c70fb1cec699162323e7c132ea3d21606521c5931a396fa024","proof":"fec28e893500e4f0be9cfd33fa2ab35affcfca5b68e3721e4d936e78c3684d134043b020b85d0424993e7b5e7c0a6da7ddc3542732d7758746ab0700ef7a5611e2cb86b2300ab73791d1233f7fc7e17af8e53642bb54309c4134bba69bf7b33e66247523bb6a50a12a4a0f0fb43da6bbe7f8166e7cc16bb5d7e4ff457b96452e500ec73a76d5e4e2e53036f2a1b43cb6bff1f0e2b64e5b6a2ed9b4be8803750d019e79f6c1af61a5f5f9af3a0087b4b7d1d03609e957b69fe7696f113fda89018bda06e347243ec34b29322895fa835597b64fd33a6759ecb78b407b9b42a20ace5243dfc3c5801c758762f09025fcaf58c93b89fc0833ded50a0b1c3c9cc45a88cc6c6a9a13f7deb7872ee155b6f66af49d5e20347314f0d9ffa140c793137b400499cc1bfc869b4d9bc7de29aaa4f2c5d5166a0d1f0e5be3e2761938af926038e5153eb4973270ad237848052089e10d889f7c4b5742fe27a42471d0ec2119f284ce5295723cb38760b16f306457aab65fc49e1eaf5f25bccd25dee84edc210c9c72bcd55727293dd9d6f9c3017ed06f0822c6f27d5d9de65cdba146f78d0e6eb84ab0fe590faa787c5a68efa28123317d984304d23ed6463fe823107288402cb8e5109c7399028517e66ddb192000d3f0bbe6c9ed92d7abdcec0a01b88664a48edc8cc40926b380f1b80e147103b2c70f780f1a7f05ac6f9e81879d40c9010efcbb9aacb004d71479d58a235f2673cb227d63fae1d2410c352895a32ed04916e1f7aeba4e5675a2088e805800c5986dc2c7e14557814b2869fef469803b4ae00c4135527b5e5ba5232724e9cab7394b5064870086394e7d80deaedd1f8922f2bff0b7e9f9eb5695a3b1babf8dd9fc42b056c73a91b4e7e7219d614ca65002ee185ba341a2043500f479700927137ef769018a74f67dacb8f21679856b3907"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ca21d24cc7c53d43b9e27b4f899e3c8add50f13c8cf47402f29e9714a7c99065","proof":"3ce91db35b62da418f229bf02011885e0276708f6f26b71dacb6bcc23642d41ec6ec5bcf671f3965bf7b42c7c9f2caa7f451d5aa7d2409738aedd1c6cad4d708d06f6d00d6cad60663d35cf4880eeacf7dd591a40a6137c757623929f400a04b82466d7b1469d5a81b2c13e641a244ae3b0ba48c9ea084288a54808f56303c457d51bf0dca18416221b3dfa04dc92bfc264a5c7c4deae6dbaf91680678d7ff01a90e7b1fcbbe09442e98a271835336458a135c022cec086b28325e0344f9d301c1a3260965d3951b4ce952621fe58a18adf0cf513589597bb547b0733f629d0b5290e19de7d2b9a2060125e42e856eebaf87ef8af344263b7ce62825659ddb522e8c639c2d5d669e0fb0c1b68445790061e5de5e129521b9394b98a0317dc0009a31aa0ef13ff0e8abd14e3d25ab375f09ab4da7dd9c222113b335bad43637452a484eed1af6633b24d23c5c427bb2448a78b2f7411a753b3d8f302f191a357c40c78fc024b11118248d22bd01bc580f14b2f5fd68cce531dab41cffe806e230c6d882d325c7e25c3be2d8bc8c9c784540d4729e9372aab82bc5a9cabd26c554a2386d0e36d4be77684d86a7049bf6883b3dcdee1229ade567d2ac342572ba75bca1583415d36db00f80b81c15adc1a3b7c30474dc617c7f317a9e147878bf7fd6aa3b160f894f56e2218dbaa5224e4c3eb08d3502ca12b8c663900f87e8bd1820ad7c905155b6254d5eaaf5df7fa1a1ea33495a6cc76d49234846da9590230006b1178eeeae42147ea4163e7f826558178fe482bc8e6128e9b991e524ba8f6f2619f9584356019abb92aaf30f031dce648030e42492bff23fd526405a35e10b56d5a3433fffb22dd114537a39870d94b4b19b6869f9aa85daff112d35a8e70a008a5d0badb26bdf892059d23ff0c49bdf8695415c11eaf239ae9ce61fdd2009"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0ed4a8d21aef54008de1e9e9acb9d3417b1b055966bc7053a7bc6aa5527b4e24","proof":"ce8573c77839e198d810d28f50cd51afe4583715b40cc8c031d2cb1b82b3052054ac1b4d7afe8d41f1236b09a0473d5a94aab775a06416b646711859ac91674b30696143fe5f72e8f1b73bc903f240c9969c731819c96349b032eeddece2343e6c66ac82fa4cc9a87c5a41121ca965d871b4079c4bd2bb7c2950b45315e33f4ffbcb2bb2b12b28eb3f51b14168d01ae62b1a665b82c20993ec7a9985513ea30ed9e7db804ca9e9cfdaa92fd9b1bc80ba197d2e1a002509ccb4a5149c83cb3e05320447cd9389f49bc36a66ef1d050d3de08197ca93a5cbdd4f71f027aaf7af0d9c654a882ac4f8b5df595cadd2cd8e7f576131d22dac00bfa7324ee48bb6cb6cb686e3881d8613776672925fbd504663a947fb15cb93a92da7f1fc6661b2bb04d2902b274f44c36813c2324b02d08774f6fcd22cd031e8cd7069efc512061a4ce08ce00d64eba4ef80d9f74d1544fd78c7fbb1d15251758d14231c5114301d5f8028a62eac66730d6c7b5345a654c833fbadb7d9bd2cb2a86b7afdcd0390850810b39675285b3636d9958d2b0666192112942e52bdaa23959e39d8d51ec2953f20154a585d444532a89effd50592de7dbd1ae47aa1eb6ed371cc87ca3ad7f65bbc32e65602b9456b54659448e37277467bb381f44e62bba7d77fb8fac1374c27b44056c1d2fb6490b89dee3a8bc405df8922290f53a51af5f5be7c88eeb22922ec05bf0c20dae7f35e6fde924dfd11f386fda2677be6003af52ff4a5a7b837338a286e74ac9cf3b858d43639c99236c6f8a95d982a8e1189e7600ef542e5ef6cd40f00bbcbc3e16be625a7fffb8196db080f2b0972c669f99a1e2881488b247054e7510250c8b81fe5a70c5612e086a87b7dcef2c6e83d710fb648b8841ad20f871ea874e867654a2f4334867165bc938e5006c42bf2735c0af542eee8a36003"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"260a27de2fbd470e04dcca248b37c1c0d326ce2bc2b417d4d6b8fd11519e325a","proof":"32bad28d4eb52ece8c0d335a91a3d52884dbcecd94cdc5a7410d836591d48e1c86b3702c2de3b55e671b817c391f5be2ec3f5dd4071c74aa4608e2d0cb5e8f18aa2da25f598c1701ab588ff2b440deb934a75e46c42e44961558afc3fa4d351c40ed31a0ae52d6986ff664650412ec0dcd85e12a5466b4ed86d5dd81c61ad679bfca80d13d4f2153ed91ddfd3ead92877410f9c446cad35fdc43a6e4db8bf50bd8d9c5b10fab4045697b0cef4ac307d90233b57d0a4e581fd1aacbcdde308a0b1bba28425345bdcec77828357441fc2d17dda4696c63e3dd4e5e0e0a5004fe043c7e88a5735d886ba97355eae2880b9bfd82968d464b2e3919630b457c42594d463862ca86e22d9e0fab52a61d8963fc5707cb09af8126bd2effaf6e2f731a1c56ee425ca54b29e9404594440e46422ebd2ea0d0316da32fa604cf501723116ce2c72740fca3498f121a0fe2438e6d20ad2fb90b781b6b5c5bd7926537f3f04032a3e15673ea21fdbe6d9c0dc3c3b7ce654d9ff1da7bc79738c2f9c815d0b44a8c469af3933bc096480cfead4d9ca12111d558b109a639351d359b494ae8bd1864dd4238ad1c675b62af1346e64091415c49e9ac47367218123df17c2db3121b44e9c72ffd677278406e9fb6e8ff1655b6ac275dec7bb39ce2501c3b0ff30c117c93890fc03c375762a43950be09dc1e566568cfc75f88b5a699bb69697c6a7f54a08e5bfe794a39d6303081019e028f31c83eb64a0140b6c22b5f1a8ce3ec6fb4f70e363363593b255e555123de86daf81ba4b8dcdf05a46bcf4072b96a90229cb41af68e560e3bf733094634ff3fa3d9b061c58b73dda3263a2541f0a3401977fc894a65a82cfc0bb8d40ebe3a90f9c9c2ce297b9c86053b296ffacccb8c0bf4dbd39ac4c9eba354f413ebd84205ea40dd02344084f982ddb34c07d29aad06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e8f2d6146a125ad09c99c392f88321c63168319e1883ea2999444729b480c445","proof":"28752c3d9b129e306c6e576835612b67dee96edc9037db89511b8bf1c5e5cf255c86e00756724bedfc3c00f2d0f576f94541b12a9f1898f3635d890e83394e44e6d9c0a95c10debca4fe98af29cca097676dde01b69f374f83abed4575aebb62743497547226a95a04167feac87b40c8912d5e50efa61a80a2202148446c852697c832c86e9bcdef70b60fa90a7e06a97da41e07f1f354570fb8aba38edc570f09c2af146b22b7d85956402dfcebc3c89fe8e99e07aeed565e573b40d5f2d308b5dd1eaac98034e0a1220c1320da7ee37d26e502f6c4f660c97d2ac13f39ef0d4ae360404316ecd271693738a99688d3767d3662a82dd15a2f3ac85e166af655546dba6a2b0b2c20a14feef7fdcb6229daefe032dcfff897f64a931114cbcb3be409515027f94f7179dcb4c2b6e1b16d81db3c19919038fa2781cb52c9099445927f5863ca96a85070ca31fe541ef6d589efa62529a0930b6bb057e0fe81080a3c9a6866e4ee45d78962288a306d61ec61bb5b205823075dd041bb79052d723596ddf326ed7ea515cfe2bdecbb64faa6312980e2de9642073042cfd5d2751d13daaca3c98e0080ce9066ba85e2848d72ddd33a8325306b167f9dca84429c8e6e723c2bc8513fde22f285abba4887a47b19810988e0694a327a0fd3267f11c257ca6e89e00927e3cd0fbbc9bc0a9b52622a86a2b354a4c1f8284d4bdeab05de79c0ef840bb77864ccc5d109844fdaf81dd3907aa86dd7ffc52e28f1cdf9feab180c9ece8b528c104fb53617d0250dc07df138d6efbaa8926d01879303a98da56c5a33c1dfa11808bb84d1f359bdfd3ce2d3e8b01fdaba6453b0d403ae367f6b6184bef1cdb6a2ffb78c00a107aaf958e01e82f38c05c7d082c186531337b30304fde3834f47456967ccbd74e65320a082b824108922567e5d84ad0da27cee7e0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1829994c165c87ccee675829254d7e1ceaaf013deb9d780b54a64edd54037c1c","proof":"30c7499e673dc7d31610097ff7a5dada79423fbe59b3dd6febb85d5a5e0f901a0815c348ae0897e1de3c0ef3a898a7e8cd579571a6a16fb4647f5eb80158bf06fcaae75cf20875fcc703022a271dd69e324cc8959ab59f174317ba86c115dc1d12c0e0efa13c6233d3c68ce75e78bb61591ee77c119c2adcd22e0fe63492df2895f7b686a4484eb0b22a261684994a299f4b6ed10b317315325c639106a0e60f3ed40a1e81d9b3f3634384de38fa3cb62c543d10978bed10ff7764f907589a00c41e1587dffbce771e7c861193668f1f6b0b00ba7293893795929c3ef671b30ad2cafa4018dfc8b60f09d44072c63322059028ea13cc82cd614f16106fa2eb478e21a1aff1f31dcdf7041b9688f401b8427344b1a8c77472e483f0f95e449521a88ee639e191f040c03415b91b6b6c528a025ca343cc47e9b1523cefe680275bb66be6244c5a8fe3d50ad814f09037bff23429856467a77fd154352e8303051b424954435817851f7de4e9ffda7533854ba5c0f72579b6d56877b49094e62e45e838df2badd666de8acb0ed8d66d40889dda11c7ace571b21ae27c490f254d6a88fde0fae364dc11a33599ac8b4a1208ba859d4449c6677bed3098e7c56fa050da5b7e6933217e1106449242221cda18ea81ee227b75b30a74a501bdafd49b41bedb5ba7771e8f975a32296b5b39f3548c593d009788c48b99e06d8bb420ef526050b77e9c46001b5c93db36bcb90392141c64b179540f92a6248c988a19ae2a1c5afa53db8227c0491375fccdfa5c831cebe1cd5e484956c5797a7d3807673d66b81acd12b8c52821d88abdfca363e04dea4609f3b7ab9f03d663dac78d985c11e171416d7355e6a3129f5ee9891e536451754165d4bdb30c7041c3a0e1900ca469d6cecc04d45b30e614b34c2f6983efc17523d6a272a8cd3e4584bace9906"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4463cf6f02eba51b2ac02b082bce415ebfc040904ce48ffd8b3a54bf3cb73b39","proof":"986324d75771bf303c950b0f4e2695fd5ca34fd86bb2aded52a930c388d44e0bbab75eb4a931a180cd0edbe211fb127eed94f1b376fc4ea3918a2bc43c4c4127f67c415719a88c931b6842a2ad8586659091240de10bc038e0baf9a598257f3e76824bad15aad5d14354f125927804ff06b35051552a021aac7135be79cf7549f279ba1323c3b808bc8b840e9009753493a2829aaa7ca3ab0cd4d2ca28b984000cdb1946a6a33dd03c14958b3a840b75fd6e463099d3f7ad70aa280c6a651b069a81280f0aa447bb4b151930440dc8f379c78b0e7e4337190b9a0d1940d27406b8ad0584a952676134100b3cafec76d6617de7a24bb3154ad819ffec832a9a06f437c7f43c039bb40884c4d6de772948bd17adae60ac7e6729d093605ee5cc5f684fbf27ea415e50cc9f44098a9853d1c13a1fa02f663d6c268aa7ed4d1a771fa4bb38a6b58ca06c7a79684d2c3d32e39b7024f80ad3c532fa2a58959d5b4353245e6df9b053f914e9325865b1636d5e0b001a8a0460ce6974b794217aaef50c548343c059a0f92b02b3104d2afc5d28419e9e375389fea417ced0060a07f529120bc00777810db2a5d1588f2e5312d69fa4795afe846bd2acdc722fae32cf06e6ed3d0c2b9b6fa0b890e796abff83b190501a8ce4f029fca9d21b5eedcb1114c8fd2b80f801a9ee04e73c36a1b77e35220638f84a841e49ab80cb17eeca7a1e88c4d17c49eff3bffd2720d342b932a4cb405923a47f2b1723ad82a87398ea0e6ee573b352a21a4453ba4949e36536b40c3a11fc4269ef31d0834a2ab741d659d46457505ed0d7eb2154537036f30b790240c2b9263b7b70cb6984e791ecdc6ca49a9774c07028f8eeddaba943578a183b392f146a845796f0e38b775dd9fe0a55dd76aa5c175e8012c63361165b9f99e1050dc87e02f211dbc877a51acb5a01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8ef518c0bb4ea35ff106dc479bcf6d591f648481ce97b8f0ae44f22519a7a51d","proof":"86dc350ce9271295f4358560b9a6550410ad929dc5ee27a2df7e4b1bb935db32da96a1b1cbff605af0b0561f255e9c0495a503cb22954b4ece164efce2730c7df275575087cb7d2ad5511b82fdbc3e1bcff9e1c04f0fbab1a69a96b20f7f6436ae8a8a2afabaa1dc8a907a376be18a3a4a805ed039464dbd6818e2e2563a467d7444b3672ab047e68d973169fe42279d52adf2767e510461d8668f55e77a9301332ab7362d4855b709833846db79c121053d74d58caa598e55984cd8e4d97302eff27fe70c94fc057476b5c4f6240ef7200e826ce03ef05674985955e97bf40a1c3e90a475f3a70644a89b80c435991b0135babbf66a2901ca29beded05cdb1f72d651a029048c67d484afca24a89b382b77ebb9ae8a94556e049e457d59707a1e3d5fa438f323b1ef2daf39cf53e49d96435d3dd7ee6ba30e13776185f1ad615a0ceefd932168afca85900da5793424df85ce92faf7981759e0aa0e748d9561a64cd64101c81de56dfa93a98a375aea679c3a43553062d50cb1a80ececd741a64f24c408597616d0c90af19aa2b1476649705cf3ec2b0b0cd4257c34d8d3a0520550f8c0230d97282fbd6dfe7cf81d273685920bfc7fdcacb4f477608bc68264289e88f5e85c8c5920ec6dd28d61f046c53e1a1b006ab0e2521cea0ab2c1014728127044a6edc68f7f234d79c7f4cda994e85ad2a9db4585d87329faed19f39a205ab7099917732bc066504f7461f8ff710c17c4e0f2a6fef9eb99b5af48a036c0add9d97592b70d02a9a98ac3e65166616e3b0311a3d1d7e19e241dd6320168a6a1541bf87235341b2df5ea6c54518e0eba7fbe43bcf11fd496778cb2b7b300fe5be45133668ad70b2861fe513f775880f356202c19144ff01430458624f024cea54c0c1d136a9a34908d279ca92f926168add3117c84dfe83a158320aa602"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b6c165eba17eb5d82604bf804cd7a3c7049cc4ce23c16d20a0be08e03e49a84a","proof":"8e333f7924b3e19d8377dfae33634f8ac77aea6d8daa972a168031d3d50ed1110495f00e1f201d1364cd63542a187d5a1b220a1c9441551d88ee7eb1e13c1320a01919a3a2dfe3b738d1045ee63022fcc203d51f157c8041553fbab6b4669445b6ac85d261d7be5aee1f16729b6d667d56f8105d543850f0ac9b27164f874c03d5d875603385f51b301da407e8f8c844e8ff09a148b3d8bac423b0b5980e2e03732b7b30d1bf4fb821b62d0aee8b34c281cb80826edaf9838a11e8d3553cb70615a280c0d2121811b0926cfb710cfd2e194df12508bd0086587e91ee6276d40d7ad55755b43c9f105a42a5110017e3022014c3f87d54997307b17b7f458913442c713fac096b306eae06ae73625eb101c54e6c84031f904e261156710ee0005c1a7ea7ed34727f43dc5830f21120d71b15b421e05d07e665ff6a32c53989621c36750bd4762d31a08f3815d58a6abf3a83502ded92ad4689cb30698f8a957738b6beb4502a5d62edfd0da31d468cea09bd1d3f8ed1c5039d1fda67aa42ab157162e2feb9520a831b3045e9ee11e68adcff58e1a1afe52fd39b555f624e2b37510611ea5d04c3554ae5a2a91424305c7351f2066d1b2067c8ece14ae9b6c81102aafab6af54127b3526f02701c54a82da4ea290526b6e49fb0bdfeed01802a75fc4173d8ff60a569699e0ea9ba010aa6b54190426d8fab70bbe10cc201c58e2430a7c96690e14f0ea64a0267d9788d7ca4dc17025186de5453d430a41ec5ab33c60812662ffe30b638a0e8d6fed7301dd88cbb3dc2fc775cc26d0204e7697c440540ea934771b8c9d81aef83f40d01078e11f49f9bb21e869c6e325b1b993797fed3f35970614d960fa9b4114080334cad139254d2541714d3acf5161f73df00a6123eae265def3cc09fa140cb7365c6bf9338eace508d0ff35ce6753ea473606"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ac780b039fa93f871bb6f6571f09241bafad535311029d00014e10d0bbdece4b","proof":"d8ea03f5f40484674d3fa4a69c4a1d39318d858fe7ab836a7ee4001ffcb13e2f302b740f1096bc7da3348ce44170db132ab085079d897368c942be6a0533b04624dd5431f4b143af6c0b2395e8ddcfa4d87c29a5efb7cee7da6b262e75ca6550a63b3aaddfab7a82b6acbe7df22230d7b068e4c88b8ccf0ec59f8474ff6378563624fd55dac60f0bae8050db0a49c11f5b6dbc8a3407dd259dea62024727c207ce9152dd2cd31742b45db04af066b522e83a475d6a9c4f213d25c0508d7ccb0c0c66b09daa03191a05a84d80c07cb33c916bb47b66a228ade7e2baaaca5f65048e3d69aa8cfdd4b6792961355d963831f5f388dc639a4c2ea193dc80e2a49756aa43324fd0081881ef5ee2a5731d661e8bba50e277b4366fafbfa9c3bcd265607a63bf00ccd37510596be74478a0cf2973450e24fde40edc2f7cee60f503e10a2e1d8842c90e7b2b7687c3931818aaa843c801786305cbc58dbd593b2f279272a001f3200579938850f02df79007b28c534f0511010948b3cb226ecc74c81017e6564ee93724ec889a1386a7ae1efdf50a872714205bf7614f9338e4e7b66a24cc8c13f7e89ab436907a5db00058f55f7aba54814e2b9d22d2b2caa453376f2c5af5f7d374384bd8732c64ac24d59b3b0380ebbb99f1e24745fe84ff4f095e3848fc3cfe08ea5fa446a9a91b9e2087a3c8b4b0a207c09ecfcfcb8f9758966d0dd2251b2c554547674b556705d92c5b472343f2902099dda06212559ffb33e80968f85572056f4c0cf76dd1b78e3b048910ad5438447ac6507647c65bccb3842c30d873652fa5bfce4189fa34ab4601c0c87d1f7210b7e50c21e7eb279303914fd45c387d5f1ce290c6fadeeb18886deea8905a0632c8e1d81008f6e5803cf105687f74cf256e5319fdad8b2c92b4d4b3eb307d690ef1f6bfd6a11225d9d96303"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b4c9166fb9d5e8b822d37379cf7831551fdbe2c73b5916b4a79dd16178940316","proof":"c2462c604b31095b7d64daf28046f3e63572fa5c8cc82e3fd89d833d639dfe575e6efcb59bf5143b26c4687c033c0ead820ba835582f859221412b2c18754654c890c6a0454605e2511bd07e6e17166401d6cd318882ed4f3d04f95cd892135d86965bd156262591675ce87ca586fccbb0d33db074aed4e8d1eaf0cbbc56217d157dd59aeb0d45a4f08ca42c8061e150983b6a7e8d4fd5a8177492f75be837009655f85d67829c975619aead2fd48579a29079f9a9a38eabfaf04a18feb4e60e25f6f0e435443e00c27d2b1398dc6f682958671ea05a5479457485ec4eed7e035200e0651a87a7c756aeda48e13524417c3d4281dcba994285c8c8cff85c7a1e5e3e1c9dbce1a5e2aae2686e550f6b4e6ac952b97995d4505811cacf11397750b26aa7c4c59e666ac044aec5f1d811d126d213b05c89f88883a6dc5e83ad8668b6581fea7c14248574edb727e64e4eb732118be607e94f6fca47c621435bf32bbe5015a024fbcf2422e6c8ac66a947b7967e6a3daaac9e9b81a113dd4b70d4637ad36141ddbd53b7bac703534d08c85c5909e3bed66c163706f041d486a94928c04edac6069cf011af81f83a65f51a7350117912e134ba70f3f57a4ceeb7f722d0fd870581b7718ef98126623c3093bd881c4f7f260f5eee7d454462436d234b929c351a86606296896deea7bd910754028a4f314cf45896f8d72d7b348c633faaceaf5ea6cc98f55d3698c8d4ffeb57c2068056b2d77c46996e0d78a3e59b39de88d78532a07e2286772885e63f7ae1061b696d31416e6de95ff8020327c820b45040ab9a14af72e8145eacb9fb56010bb94c6f54527b4d8e023cf4bee27b7f298b143e5c97f929899b8fafa1c2832c246f10e3a88ab102e4c6f9104af669017b5f75c4d77b156c5c3fa5f51ba61bd4807ad8aae4c048e3a0d323978f557f09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"16c0269661947bfc819723c203bc40079e40bb0bc6b56ed83dc50f8322540473","proof":"f4d8db4d626097bac760dee49d4ddbe63b20994078d98b752934c3e77b023f4c1647a4b3c618e96f84237c0d5352dedc006f37edb2bc19c4ca32f578cd4cd161e8b17e702206126ef718f206c4efc6cb22638bf6c7d0e057bfc93ee1e31f2b201a53cbc1fe6ea18fd14ffbaeaaf18c63f563de59edadb833af117a5770d2cf75822fe9e4cb5c83a74561a321c0cd02b49bae98b7fb519f38c09269045ff1e9044bfcc6892b687ebf01c89386d1b20e25a635435a70ebba882c4cff398bd2f803846c1b4e0d92c1c840b2c2346a9aef6026b926054d2829d4f460566ced0476004ef1a13fc884fceafbb2c3f29dc5d92bf4914e19db0f9e41295949bc7266545a7c2040371dd7c24d7af61ed543411782d22cc23ff286e17a043517fc924b77694640862ea0cacc50155af8b605e5d1b0e91938f8b9b56cf6e013bae4207491008e409b0c7961086c82b8a0ffd66a3b974bdf5e7aa725e853c68a0759a0d91356d0390b90caeb177399e7e708880599ab54976b2f392b6b0287a75e5a1192685d9efe9e8ffb3fe420602bba46997ea4cd8048c1ef1dbba7bc4e4ea173b6b0233282daa9a290392051a99176dcb2c3884184970f5db3e0c8def1d8c3918a8e087b8cfaae801b95f3981cf4a3844bfefa5d026a871b69ef6bd8778ee300c443a14ca44227b6b00dd937102bff3ec02dd703945218620add757cf73cfec45309d756c03bffbffcf6cc2b2ed16cc818ec2c53b6c1bf780266340976c6a4f8282ad913e81029acb6b32b8e28eb9c6ec22ff06b9e03677d978ea6b20f2dda89e62bee2c90231f3096931349391b3fa62f2061cf5bac96c84ee1fad9577409afd36e6011e5a3544af83696b8f7826956dd4d4a0eb8325fbbbaa1873419c90fb5d25d970a99b8f749c0ef86fd2ea18461e816c1d764a8ebfe5b90bf6a7064c9941b14d604"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3eedeb8444f0d9803ea3843dc6e40f3c701f8a16439c2e7dce8bb4fcab605156","proof":"76759a77062a657c3bfc65391c6d9e4cd97120be5ea1f88fee8ae242c11c3172b2611d55c8272089e288cf27679e692b1dc869a282adeda59d8912e07ea36c353666a08e586dff7b1c95b352f07f85666179eabf85b653aa9ce02366382d176d4ef8f81ff5533d8b7e36d0f6da4768aff5ccd8e4f585c04d0832321216e7980658296186c2f0e0fceb9c746e5fddce85d403deae8326362fb271b296d9a44b082383f19217e88da3dae07a9faba221af39018faf10f05abac1070208e329860b94c563d81a528601fd0f5d02f3db741a18fdbe29a8623b8317e7322b9efe64079473bf1d7137eabd42012ff86de93e03110ef5efc9c1c1a4d7fc5343d76fbb74668d9ba70293a53d8be7dd1a9bbad611137d77556376b1a4671667ca3f67cd19828e85790d7df6d1f4f55897019c0f3c2b6455ace6ba0f52c23582224b0b1434e826bd7df298376ffc07f4e6ee8d6ea3a7123935c74216e55b83a84857687118a23c7c36db562807124ed47484b52eb1ca82b616016a3e134bb7ed24fd9b7a199ef4b02f215a7a2d89e91b20e8a9de27dfbe58c936c64fb76e98f068eeb66949e2c0e561ba30fa1bd4123e07042b7edd7331292d387e969885be41f271b1ad111a0a3d33e8d02cd0b2850d852983e7e55082ed4c090b3ec2b23437c46a71b06cdc90328edde70db62be787ad4985e420bc3ca8b7f029a50b3697e683e78a3d604ab323a53ac84cded49e5073ef9cf67209ab78921137ec6aad6d927d12f9f3291a7df2b2f425189ab24a9c0c5d1f39fff44f5ff2ef6865d44a16257689143a0be20fc295e2c6de46151a790abc96f08bea6dcddec748eaa92258e2461776fb06b184b62c4e7bc84b5725f9bd4bee7afcc09ab7e83a1e8fa090ffb4c321e3dd0bbaa999a2fed673c8815e64783946da0e8a053ecebc9105b03fa91697d738a70e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"307bc3c08ddb87b5fd21bb4b77fe5ba2920910a65299fdbf6902980ed6501d59","proof":"22f93674b38bc576b8a871a63394b71a6c7a45a6773b9132c21944fd118b58124e562110d6d514d4fda6108981ad9307a6c075391855a9ec371a08ecbf0e65709aad6dd494b8cb80339a42daff5bc119f5e976f33aa7c7892e8cbceaff4b8130c83f3c67efbc4af6ec04d82586d7d28de6d3d860c967421736d1f370343b0e4586704d9543ff11ca7d27903751b0557c3a2ca513fb9de5e22ba517b22aee5408ec6bd6301d5db8965f0f908ae84155994370d85d792a990e3773c48f41599e0ee4b7646fa154bec1e7bac118809d10397c90b1a64ac63f4cff7db6a1f8f0700f0eec3ea2c8d2d1327d8900128753e3c83d719f929e2bcaa7beacd2129ca8b21f22871cb3f852145cb5a112db2f2fabf9bbd146eebaf22468815a9e71f432ea3d0ed27f9a3f751439e27d3b42d3bc4b46cd879d418be793733c4441bcb955244a7867e5c01369566ae903bef8a030b0c735a4d20930f520a72a6ed588068db124a449b1c46ab39d4e73d83804c2b34d159253a65a2cc10be21f163b68cf16a92f1a2820943ea89212d7f01a8037a18ef72ef44867acef1090fa1a9857d6d52d402a5fefb1e6e2a0decbea4fb5b2e9a1177e0411972f8f7fbeb655ca5ac406ba1a6ac3222048b3818164529692ef0914d23869b3ebde6e6ffe8fdbb33e0444d013c8910d8df4a1d30908eb1165f1eb6ddf55f5b5401c979e95be02b04f3bab9d2f6624315e9f2e7c38873f20aa70ab4b7abd7214e302baed38eadea0752c3e112a1872cbc8c2651ab4693314e923b76ab93dba9a77a412d32d668fd2dd4cdefc39d4d31ebb2a4453bf604fda27b28e30425ac0b5dbb31792c3711927569092ab7bcaf43d17603a2b7c5baf3d5e68fd534dc0cc20774735a57e7f203b97bb6f470bc451ce901ee9745c795040b599d9413810872e1b0475da1ebfe096e819f48b09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2c3a3f4befa9ef2e7755dd8661ef285d4b438448ae52dbb73265b26d43545230","proof":"78e2d63b6b01233bc8eb2dfc9e9cfcd783508cd32b29433b30f540470277e15b8820fbfccc933654a5af67ae3764e595483d61e2010bf6ec5c506647635bf477ba43b9ae2c92bfce35247ebe4f0e5b5103f1e19561a8ac4a87dbac9b95d2db334c6878dfa6c7123d884136d5ef5759d82df54e5af3501146e616d5a80d8ee95c9a72c464db4c8551914674dcd5b0174fa57babca7eb9df14239a308d1e01700fe61f4e2a349d8b5208dd391e8d8f5be5d327669313f8f3a2f904eb7bf4080100a4c83782b5f3dbae7db8c4648aa1508d13fcd12305e992917a6352aee2109a007af850375212f97956eeec74777888c7013789745f449504935c8e435a76b6346cda549c75426db7fdf4e2c859856034cc1df41bd6372acd1f864102e9c67a2b44cfb0527e2325a495b034850cf6ad8d150731c03b98d89211538c2d2e5b2e24e6578ee4648287426b879003e3783843e7f994d947bcca908eef1f28baa77d39ca233e890580c0e587ca7028200b6cc723d4ebbcba6035651ade96930522f706241895725af45b8fb94ad393afcefa7e6f83c22e361a36dd74394fac98d1e35b2c1c7fd5bd4aedabf75b516df5368c4dcb35ae4d022c057d8f9141fa80ebaf48cea45bb3120bf91da78c0a7e25cba48ea07f348dbdd2485a476c14e6254fba05080d92ff736689d3c5e0ce1aa14af45cf23a1ea435d7782cf827df8c4353b8090cde872549263069b534d2962c3d6922df46596b7f5d6211ad9c81b29e13224cba1969c3421939e1c0557ee37b80092fc508b4e70f28d459895213a3acabc439cae4b75e6dd8e9e80d095a71b0e54efaadfb1fc805257b89406e7143d371e56cd6cfa4d184a2dba4f4a2a2a259a03dec61aaadb336c459af4a0bb968c9c1220e62f8429feae878f72e362de5966897ef1c6285a5a056c7625b2b0896af2a2a00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4a31b77a596cf8a0f0d089d5369819b23f2099d1cc77d5d37586ce1da80ffc3a","proof":"ccf663b6b41c65b760a08fa1440179d361ff1eddf4708583c53ad872bd43ca6e2afdf15fe1a479523bbba110c8fd24dbf1690b463007eb3f9a1433232c5a337b9c293971b0ad60ceec0dc701d9df8e9227176a4ead475027d33da02e11dd5277a248c5a9e0463d920a0d169b921a74b0448282244439c5c79ef86936cbac0a4f4041d2ef11c1e5b3d9da0df7fb91d09d2eb0cdf1eb9b35787f8e326c523c45096fb217cbd074ed23fedd30bb9a34a8820b6037475f63c3b2a14fbae441b05c0d6c12ebfa2e1e04d5135eb0bf401287da7ec6edd6369ff5119c2eb6f65047b60d64cc06d90641746cf7f97569dd9ac0beec3e18d0be419ffecc731459fbb44135a4d0cb83bf0b7f556a409286dc80076f4d8ced86bd7e6c32951b57320872644d4cf073d2edc52f2421ac58097439af54df72f2926729fcaa60865412fb57ea6a7abab1c717d06dd060a31500716d029996d2ae2ec4de11dc10304d06ade6877b804a5c84916614f723496d8ea4570d7e0af3b3d4f466cd7c400b3020a41a747d5ee6f2f27012a0d7b376ea59268b7f0bf7dbed3de5459c8cebe6e015701220432ed9b2082caf27f3ccd35b8160964ad1a6a4424a52caf6dfc0031a5a9cf1a477ecc2d8a8ae7b94e2fb5f1affb680a79da3659fe9315bf927349c005dc5bc9835d2f364d838f976e7a95108c76d6b49bd2f766e0b9ca34308896c95601a60f53d50fe8c6f3a212ed94f81aa2c2e39f9e5af8a0ba91ec9101d956ecea3101def7df8a74b25a8c170603d4c7ce81bae65b16e81ca5098fed5a4f248d5958ff0ca314ea2192edf87f441840ddd219594ffccaf40f5db2d6986f02906c98868c80c6afa1f901bca70d53cf262f362b4c438f9cfef0adec9cd8d87fd2d3fe5fce10d0ade95c28fafd0600578ae19b977ecc8062391a9599cb5d05ff6f1f8c5df5b8c08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4ae69fae0499604855c1688cd83bb412d8502e7dec95f145a35086fd93b44156","proof":"e6567e0f0cd3d36bd099da51765c1b122aae709dcff103df922ad766c11b9474c08a2876075c411bc8806cfa5ff39675843644721f7ea1602e92f9436ad90b1bdeaf809ac529c53b5831908a5e7245514d974deb571f6e829b5e2c3e72836e422c40e87f32ba69c6dd2a9d4c5f17b68eeef281e929eba15abf0a6c44113c8c33a170a24dd6c95c67c712ac912baf2c430673b4359273a710b0eac702545c7e0891f7ec77c42633dcebd03d7b3fa62951264abbb6ba4bf4a3511ec191335a640f60d424d5eb16647317df4013cf4000518b1b3f642c0ca00c793c448513ee8f0992a4293abf370626a64f102b0e13ca62bb6c3e50da18021100f6c342f3b98365ea2fd14f92d6caa7dc233b65e4dbb5514c20105d7a81203e66776fc6b3e27270e0f42a1cc5053acc40c9eba55a0b0e32a43d2382f22c1e3bd569c67b2a71293a82a524dfea2ac0ca6dbab41073680e83eacefa8c805c8db737101023aa37d6106eec2ce07fac5075c5a23534d9f71a1bf4eb8484233853791d65d8fa8876ca6aaec8f0c6ac1737976cb3e5cb4eec018571b7cc832e62659b2964269594b9a344ca374114b1a99fa7ea7bce270dc120e2c472662d9f7d432b15cfb41395a2b83aaa90ad8c62bd7ba5d2a66eac2307182393f2d2f24dd6077185d0071551fbbe637a520a222fa064a338554b370400436bf1fe610e5283435bb39a29b4757da8513a537c09939ea17289cfdca82b08c96dfc99b3f9c3382e98a2696500153d413fa8424f893ee43c90338a3f056e3a0cfc79476a0862d1748f18f739a72fc2c07e745227745891ae59ef984d87225e306e994308e7ecbc06989726c68d48f89c0a373db45ec61577da4d790bf973a5645e0b8e5ec0e4372144691994dbd1803706704df8ec3ae034abfb8438ac2f0708982075e60be54c5f702d821149dbddfd01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4a49378387884d4837930641c5201afa49df3a4c7e348da6d8b4d08d056b822c","proof":"582a2efd13c250cc3290dd3ea92200c9378a298af36b3341d5ccb82030f1f806c803f5299f03d3a606b6e0fc9f2c063ee47e626941885ae24a14d650f0bfc27fae7b99510fadbf49cd57caadaadabc09e0e46bcd6d4e4b8353d0951489271a1fce3a96abb41b58a5067a7ce8fee90082955a73b6ee46130bb01cb9e79c467a2ade32b9db5d79a3c9e90221dabd5a5c6b188cdce61f503c4ef0c2aea13f59420da981ac56c8a8f7beba97603e2e22625b0c969dd808cf6ffa28dde974b592b00f0ba5dbcdbf8115e488aa06203b7344a55624c11db66c6e8ca651e78168decc0e0c2db48354f2de440d8f51ceb622b490a519c4f2f98539de72815a93acc6b84c803fd04e3d5a056d749d471ed5470e78cd6b34284b5b85a91a41a3cf16877408c2aaba36a6721b2c6568b4a40a78a98ec82c60739004aff97926e1cd2d34714bb2da483701455b52fded085156ed9dad1bb683fb020d2f58fe6d64e60786076f5474ba35ac20bfdc37b6402cbf746687c51dd0408d30aeee073db61be224f81a682bce99b6ecff101c7c483684e8a5cc2b79f1cbe5ec6e8aa3a267a2b54b654f047ff22a47cb6def827e38c197f4907d5030aea8b4ab688711e0daaa24459969360feb80eb4dff9f7226d36b1669ce533b553a5459bafd4833699c8cb50d79042ed1b95b080d468318134efa764fdb4d3488b8e8f58e1470bf1e0b4fa1905c41da0eedba42208bf937bd13e0f364b5407310252ddc8a9ec8f9d1cbe975ff2108a692bdf9d74b2628bfb05466daab8a336b897a7a9a01d456502c14a1995dcd2d6247c67c752bfec63114189189769faf067a97867a9b82fb644e29d534b88e3afd76fe83b8c94c240345bd00677731231212da7b55ed24ffd142b4397bd54504bc5d970a2261d0d7c5db719bf89f541d857b08071978f805d959638f5d25880b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"543ad8747b36eb28a868dd9bcdae73b36465b6888bbe82dc59c451e3c1ded926","proof":"1a363e3ac3b5542bdb7d6bdfd02a222c7fb4be3ea35e98f634f53db54e8eb76de0dc6e1c5e6e2b37f4d3e20ea00c433100a49f9806cec14efae867847cc9a838b282cf580f315e016d02a5dc0bc77eb6f8dfbb87ef50c1756577e783fc55e649a2dc271cb3810b1cecd3324a5b84c58d14c7e85c92bdd40e8257be070fc90d11a8e11e30b80f49400ad876fee1a0d5527e365a64ddb83f64c92f8e2572f0c8078d883754f06ac40c3c1f37e2eb5e488d2cff15c9e50f36d75cdf3deb2555b10cb66a467864cbb23626a83aa027051577856ae5e77adf74a066f5777b85ff6605b00e6229991da31773a9ec579b234432564d00ef503a1048a324bc3c640efb19e25dc552069ba4b9199c4d1d2e5678911e51a0d819b80e8ec0e4f365063bc73ceca3570daca9ae80bba85795c1fcc143c6c75aafeae64df49408cda531629238e275de8133122e402bebe3cdc9f1a5a68c8165249aa4fd2dfcc8ae8305b3eb310cf24d491261613a8660f20f95268b7de328966014b8169faa9ff9a781bce2010ca2d0e18558f573f66478892aa08147eb6401b10f0ba00d76a50f46d48d7c174ab635022277c30d273ce9d26408850aaa7e65bafcc276f2d5f10bbe7963f904ac4e0f897549d46b43adaa870e1729f677803519651b250d7f2714c9fab5395570090490bccb4aaf0e76522fa8373772185d2f3f8276aa5d025f6ee67257873390458b082f3e2398f75084e4e40b9975ae237d4f8bf9b7d4e6b3ee5e79319a12de6fc826d83624f63f2e6d754a427fd810f2259919416978adfc5ecf2dc548298e9e6d41ce1a586afae4124f4aa922cdc9bfc4442c740e07a20a09228733635747f77c60827487b172e8372ab1ee33748b9d40c756d782bf9be3ff45a2655402db616c2b7da0e37e4537d32e5020c1e6ea04622823933de85cedb58bdae48407"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c24ca971cb9010e4cd407e1c3cc8041c2ae506642665d3a5f8d1c1aeb4413f6c","proof":"6af185f338b7782c13fdc8e8430e7ec0b50ca055e154f4541a04f4424d8d915c5cb03ccee93c3132152e42860d1201fa49be9b389a6a0b9dc779a9580963a64b14fd20c298cee0a3201fe82d491ebda5388eecf33bcbe26f3f030a3a01ea3e5700379347f84de94fb2d05ed8f828e370232459fa600b992deede2305a40e884cd4f3efb13bfe826d34aa57a33523840a55ea3cf1f2895c1a5600dd3ee2c2f8075b8c983825be6bfe7c2091e2fcc4a52a926bab6cb590cd55a374087c117f870be50fe025e915e044071fe09e1e9b535bca8c616a972ae465dfa8b8afadeef501cc280ecd1f677204b08d48f3b9659ba287e48f69c55702fe20d572d24c3ff35c5aaac5dc1361a319b2090deac204547337ae21681f3082cc1f763818b9a1ff1286cf3521be70003d351c2cbb83802e8337cf21850e28994b067d199a4d77fb02fe91ceed8c14cc6cc0c09b26385e8b247e5e06a65154052cea5e5178aa438c2f3854604ca42438a3f914bddc54db7f36bff6aa639fcd55f72f3f8a9c063b381e6ad24b97339ba36631293088c143d00d5924afab1b10c9d7a82bd7caef45184ffc47aa00f635d1e95dc7887f7d8f4a9d6b7b908bfeae08c6a9490ee74f475e7d24707517ff5884bed5e18f18225d32808dab9e6a888fc72cf67fdf541d57662f5a8c60eb7f6efba37fa7e1986bb0e73bc2bb44246bf132a82e15d7a1629b383412920e7d1e3cebaa658aa15cbf2440fa56bcf57b34ea52fbaa82440dcb25200c340b4a43c7d826a72d0dea7c6d97fd8f9f1cf0d96b51e34a041d4b7bed2fa96e888eaf17af2d7b8c2589d79a2b9761d5f74746092f9e30dacc2552eca5874f38d02117c685e4b01f1a086769870dfdc238e1bfd4f86fe6a32612df104c7c4d0a4e14c46473fe7ed71064762b0586d2438b48cf84fe42e833a6360a2bf120c20d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5e246aa72088b66bca91869bde9a14b8ea5982bf1820b5ceb39065a04caf2f63","proof":"7cd7788e8a43a2a1379dc1d10ebc8cc66fd6876a3e90ab6e2f3a6eab67a9fc59fed419e47a82a6af4ae5a61966d57df264da36e3bc4d9a2693ed63b80598461a480216190150bb8ac0f6b01eed9c0bc9e14317f444e2daf9c4bd8ac175a7d548dcae343f637599ef8b7e89b8d654a70390bf254f6d63da856fe4f5d70bc50111273760d32bf61b3387cd8fe259496f0bcddf2b384381ac98f7227579e892a305cf56ad72d64880271c177aacd9b7533bdeea2baa17f79d33269931e6253e9a03d265701040442e7f62b673231252e9c50d834b1a22b2303bddbc6b508b092202f0e58a1386954814cab59d5ff6aed1c592b046c963bdf9daf781d279ac3420482e2f92683972bf8d370ea8364d96f95cea13fc93e2639adc111c9eed98249a405c4e14d2855cfb0f17a68da465fc0878d73b5bfcdcacd3ef8153322a3608eb695e5f881f8579c171ef5d97954d8cb1399617af56caa9262ed2fb2bbc83b6c93998032dc6fb9567125c3ca52e0624a284687a35223a357144951d017e02e37927486cb976ae650f82a7ab47378fcca3c4926ecb7d820999e867e55ac4d0f00f25f8938fd1d00e7148875f9f292900f15060762aa4b8b03c07dbc46ecae8853b53784e82938351c58f7e28e3855c5ba15ac916ab744f5b2f3276e9545db7f500766641f46a46e6af8baf99614771d781768e4534d4d6c575b2f9a35926867ec02fb0c6ad830e03ac085ca358cfa0ca5ce70886b6c94c2c8e6ae539f18472d2172672b5791cfa6ee80a5672f98340b436ebe51d91b47d9a5228f9b17e7778f0925506c1bd3b802b75e1b6a9de118c98673a85e0c98af4c098a4df447a010d00ec3a0dc2bfd7f4374ff53ccb6b05da5e18c2fbb25dc76df682de7b7ffcdef40ec50311a4cb44a73f4b74c4d2ccb4c616edbf54718d79afda7622683a4035c509340f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2693b1b634384002b45796e42ce745c44f258013b81e575774039431e8320a1c","proof":"7ee3a4ac6611943b5cfa8c928a3ac675d38fba7e32ddc22a42f9b086bd381e41c8e93c6a07d67675af796d3e9ad77d3373ab81fd9fd6f641561e8c44d449c573705e7fb187bf7e443f956e9c2d2b4e096dbdabf02b2de4eea8b259d7cfe78b2f080b03f03904b4b52442c8d0d1a6c1bc1df591860c2302b4058317d49ede474d82e7c631338bb66d20ca2ea3b4014b32b53b51490d03ac1d5c311dcb2149e804a2eff125471fc1beb145b23ad5ff27bd142f6d202e62876bad607be081b29a02c38fa70e59d140561f1c53108812857b8e7d24bdeb60ceca54eed068db323009764b90860b18473911e6f403d49e9e1eb1d0eac8070319bd312774862f15707bec7208f3ed5bb736c83bbff757b1376a8a76d455c92162fe3a88040cccd4ef1e90590fba1f3669de09d868241083e4adc3f95c700c3319ee2b6c052cfbb0522ebc9dfbb00afa83eb34d0bcc57f8f070e969100d15f3d146d2b91e219f4b3530d78b8478311753fefe2bd7693191f720c48c54f71e0bd22471c0bcc10c6d2040a9c1a2b4ee32e0416aa3618fbf2b8f3cef67bd97eb416569c09d038c929d3da71deb996e3ac0f29a8299be34f43d55fb6e5093babbdff2ba099a48e36cff8cf5520e72d7892e372e23159fcc60713a629d380dbbf7adcb99bf0bcec761842e13dd690b1438edfc7c64d45dc13f9bb25829240e503d80d7fdd3efe29be41a8d571c67473ffb258ca0ba7d556826b1a8938d417d1e7d5bd64c423a418d57592870b58b9e4480c221d6631954083e98e6c728a06f361a5e8bacf9b77a57e617f001e1ef0c400c0db232aff20ca9d2083f696d306ba662f7136e71aef43a39035425066d7661a1397381d4371334c01b330377a065993f4a9a98b18f998d62a1eab04384ea8a6b8a023e82aaf94247a52d307c7db4d6caea5e8f148471a191bce5a0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"641a71c8db3982aad7872a8f5991f9c8d4c9ee95ebe4759ce228664f43808e62","proof":"de5c4327fdc8452854bff8b69370647fc5a61ebaa35f72298ef1a3537eda6a79ee2b8829a311c165d482ad068fd3c738e59694e92fc027a6c72f024a8c991d78bc20b6f52d651e80d8ab011a7e0dc43170ec016c672f53a913a75dc6486144269ceede21cbbdafde82ac378f6bb39f99a0f1d973d2a8e250cbc01852e029d96811760e3db9a8ebb1a901c5ac0540c0d4bb1c8afd2dc2b67dd663ef7c6702dc045d4233534cd7d16ed862c45612feff0bb17e5f7e44b0986201fb2abfc7e2bf0bb79d97247a7f95fa430523eb3e660b3f63c130d5ee1e80b18efb616be0970e082441da3a69d55194117e45e000d2263969e4a5ed5d09ff5b500ba90751a77b64121d5915be446d481362246f0e927a43afde6b9815b74a6d06e9e99803268c5158aab5f678675e270e10ffe84ca442c73db9006478230df5d1c7e49df38af34076dcbe7b476a71e6423762ffef310bdbb346918263cf8120da8ede7b44fb74163c761cbea58da8d7d2c8626f2b4f0700d563bdbea375436f36a2269bc041d7762020b3257df555bc9843c63a17eafc61fd0fc331dc45957680a7b77d17748801587280479dc6fefa07392e8e062b0f7c307e4490fc8570fe3861cdb7170888646ecbab3cd1e7354ab9378166cd841909d321c44bd4c7e2d9037efc70df7f8e735a76da9d63c05df09ca75dbb676d02864f96b3477c2014ab29fff0d1b19a9e3f82d54005d920b01907bd4d9cc7a607a29c0a536fae9b31ae1a67bd11326a130ad6580253ddccf44221052cab3199f5b7733f083b8f3e9643ef800246a09c564b7c0e905b07c6b206287a54820463f815403c79d6e3820d165bcc424a6155435e6cfec5f63b6e3ca0701d1125b26c32cca38db93c31c6c8e53ba87ffe0b68270240274d77820178e28c316f8147d29dc519551122be91dd86c750fe14e5aec20a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9e3efd620b6debf8085a1f4a03476c85493bb5504de6b5c98107b5e047099420","proof":"96cafb23171b527dc2bbc74a305b9bdee85d4d4dbe1c358125adf1c137c37363223fd3b3630e1326f7faa6bc7f120f7be255ae3b1900a91a395d422f1f00fb2c62b90459631bcc502d723a2068f55d937c0680ce221726b6bc54fec5f20c4b094e635fbc655ffb667d5aff54c58068a93e9da20575d048ff20729d0955360160438fa21ce36a0e88fc400a25e09a587c669914c7a68ad77ead84c83f1ee36e002107f405b6868054f4e90642c5bc276f2465526d0a50c70fe4984a6444dbf701b191eb33d0c3255a66afa09ac53507530283b9422dab61dfac86c9666e91b90f542b6e61176c741d2aa02655d166f8b3cd1b4dc7bb4fa67a463d8ab7f0d8074d880a4c4882bf4957869ee42ccc335a3b05d0d9809e6c1a0d502c65961467254a7846048b309ef69d5c4462c16b7a9a603ee0f5fce7317e3349adc2fd5d9a535b5822d6aa8867e1ddf8256bbfd9f25bcf3cc74aed477078363fe20dc789bb1a2e947435687b0c82bbbebeaa1251410d8238ecf841e2fad203dad99ca0c42583307c8392e5f352163ec94337d0019d00b37c3a4b91dc1e7670dff4a17479ee04108c01f3807d7339a9b797800ff92dfd7b4ab00f77590544b08d8e8ed1e63e7666b85681c02c775e354b4bc29e0595578eca584083cf5ba8062e96a164cc056842441fa0358962a53aba27484f0528a2828a98057ebf8a4edfa24b5f431e9f0e041c269f6c335049c0420076b0454125b4b35f55f8a1002808fcd20843c8a9d20460e835e12bc2aa2e3e2365937add5121f4b2964192c41d9ef105b3b94ab53864e6a8960322440553be416088d7bb192e1ef757e215413b80cec396ce01c99a060c44556199b16759687d45e24448b75cc6e6c6fb6206074bcbcc6f1b8a30730fdb1f77bc96bca31175b752910396149238cd9fe21d263b88c69a07d838f8c704"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4ea0ead750827d45973a3a9163b2d92ac6e1888a168991026504e373aaae8762","proof":"b0f533c71aef19c3d9dcf4825d66073e2eda118e798a923ba4560dde4953ce08120cc6627f8a779a3657d6d916528a18777097433577d3e29f806853d3f01349a4e9ee0dd42c1d128348e80c0a4942e731af3f156c8294e2d25ee3670568f60ba6a65b68a08f73ab58eb59f0d5d07a8cb02a1ee13052e099c022b6af7f15fc72157dca8c1e5f74dd40b9c0afa2190ce23465ebb2f81fcef47bdb735e0a6e690a59e81bfe61161f79d64d703a31fca797a636a60e9f35dc88fad91b63cd7ce504a2d3a7307a49c0b8f7a699c2a40091206892adcc663de8b91f640dfdd704e700b496133056c86b670f8b55d359f43454c6a235f5a93bf53666fefe4c3c911c394862be644a7344f8d3e109d5de2cd8c1833f19848ce8eb1438136bb8e42d146cc88f226c03db848311f46e179083df6c16f5213cb5a6e7854d82a0f3a223e16cc21cf13f29c11e53d6a3612a55c76f3b458288af33dc5cc45cac17e9fbee3b378e42d066395798fd55e8a648525d68ab28048c6ba297e5b178f2ac046296e30d02a167e6365a17118517da99c369231a4f98dd5f43d00d35649d349ff68aa410a4c4bae6bffcd67f41cc09c5f6b8532d2e01afd543a974084f9f84a0cf1ea96456381ff365da175ed3b447bd20e48e9d76d193f61f26e1633be5461b4694034056610aaa6dda64563fe5ca39e667bf95024c3f5d87a564d5beb46677a6cce228768671c542778464bb891859b243962115d608858cb07c9ac97fbb774a4070425233bee55e9950b875e0682bcfb761f31571c862333e6926fb464a46081bb7614262695d5f9fd3bb7d4e74e183944a0defe578adf8714fc63333c7a0e0c14e1e2686950e4ca02d26a454cdea896e4041e54aea0634400b2b193a6ffbd2905d0f1f21041391df198a30fa20b179bb70db4bfb5b109580e814d99f8add5cd68f0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4a56467c44c1da6dca4908ddcdde4cbda9b02c6576e1284c9d7357ec4cf3a82c","proof":"3cd27d5622fda3b892b7b5a6992a4b7b74a66399552010c780bc8f721149df0022c62f88f71822266d4d52ca88c1b1e1ef8a12bd464e5b0bfd1742c28309ea4df4b3edb2a9a506b7ffd3da11a901a3fe4b28634ac8d90d45c5867f4aa4ac2e3b8adb3bb0da0952f2232d791bb7550530e97648f8b323113a3691d2d61975060c8aa40ab8fe945a639ceddcef89e34ac48e4de02d73f9abfa5ebc58e8331266035537c556a09c2428504f3bdec16f3bf145e2092e1a6189eb89acebef71637908c3c4b4df5bc45071778ba412e5a5f518221651a41497da55c805674a125a400e0a11a8ad879198511db71596031a504096a50b56fce5b126890b911431d6cd5e8086f3932919bb516b7fbe859db9b4a406a6043dc5ce639b3136023916628f00282ac3b75b0647fb295876263bb74c982900b6124c657aa4a6e0779d7c153f7712dfe08d3ae934d9cd46274ad9b5c4dcadf8d0b3d78f6203e84c1f71e2d7fc4c1e0f104c620e7391119164f659e08fa283eefa84005b00d767394d1202a5a17262c3780c800eaa1416b4ea85242e0a6fa79357909e75a1c403578a877a8b4c75b2b429b5d7551518dbf4057186939e11fd88bd22b12ac24b38481b83f4caa77a5604c530760332ab2cb177fc69a29453c1596e445a22a7967614cd19f9d58659ce76289ea5476f75a070c6739602fc6496bdad258da5005ef9383da641f93f2e1eb168a47da8171c117bf0d36fa4799102b20451e28d04def8e1e06aa342224bccaed0c6c4e2be2a7a05a43d7a7e43face79bd5fb0f36c8aac77d8dea5f2f212bc8e1f247701d34cbd14e379149db2bc7a5694b04eb6519d82b7fecc83cf3504424275c1d05827e2e68e402e9b5e9a481c10628f50d893f8d0bf5156fbe0b5042e2faa463b4f5de63724a7f454e82672f846db0160c8adc34b8b21ccba0f2e0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5470c0a6962e18848efc7b4719242b36f91bd68b91bd353b3400d3ac752c6e10","proof":"804ee441426fbf21aeb85e1f7afdde0ff871593c4e0190dc89da700ec58bd7076e4bee8019c4943a94cfb375698b18487c2ef6537835e15144a37d1fe6ed7406b0479c7eca4e5abdb2a235de69e993d46d9ae881a1120284f7183dcc2752990412b17b2fbb1e449165fb05870abe086baf8f78cfd959bb422c0e1475ac8fff5e3ed2fe0ed5e11e61b4b15087d9fae7e3ddc0577c5b0d89ddfbb5677ad80e620294da404b447e8b5c56578f02e8a2d1ff88af39ca7c63c83c90e370008d8cdf0d61ad944e5bb382e209c9723908a31505c1852dd16335c2810f4bcf9cd988340f2cef15a3fc75837e5abc96a1641d50b02ef8804b06c79f329b2d0c733326e011e2e4eea3598b9e478adefea5ec1ae80eab1964a722387e8b32530eb5f943a12c00de17a695a2cab4a8080e81a69a5705659ee1f93df20effab2c62b31410580dacec9283e9966a32b7d51a44f065565b8c7d3443da6447af28f08c728727a12a98c5a5069a4424927ae5ff62f9732851253494d7e102677bb848e4927532242284ff41ce8dd175f747efb97e36642854216d817ffd13294753224d7e4c7556096c4a7cb7e0b42bf5c98a2ddbeeb21430e522d4433d56a51780ba4f6952aa2e6eb025a870479034effe7d99018f1b6a16d700bc8104a3d9c49c02a9d0379d7c1da62d3486ef4a8b8e650357ce4adddfdd848cde70494da5b48a9f802a5382d375ba57fdf828c7fdaaf4ae72daf7167546bb57310f1ddbcf81b1b1ec3d898f6d6d5adcdec2099fca2785cd261773eb4bfaa381ba26f4189e696cc76ac4e48f7118847852e6c152829a17d3baf907798915799544d620ceb19cc0bff886adcf8c5436bacaf38e9fefeeba3ad5377195e2276e919f524c60e4dd4b2ec4e46a781b012ae2f2f1103c10f3fccdc10a60f1a10fd22d039f01324c86fd2911e5a2ae9704"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ce0e734d02b2ef914036dc89ae5e05ed5d5befa2e22cbf01a579c32dcc9ebe6c","proof":"fc68a1c299138dd7e23ced1f2b63902780592553ab0944aa5fdaf523e8c3f129f4088029800ac2be022ce2177a9d96c3a8d106a1ebc75ad6296714eccae420743443cdbdecaad9d2b5249c3154c9cdd598ac80cbeba0dadf7b29e3fed73d3c0cb82578e9c3a7ac2a4f92f031efe6c64e0cd66cd50fb9ddf4d9cc9b81714e1e2804d7c60ed58482f958fa6037a82b376f6e1c5816ebbb3829527a4bbec30280088321e7949a4cb5d814ba5dde6c65d6a8f1e17777f3d8cc4cae579cb19384bb04120aad41c0984354b5fe77b342274d896068a6d6fb2daaf832f94266fc58d9023c87a87df2e9c3b1a109614ee6d4918fb761378499fc00fb4f3f7afa0dab836620ddede05395d324f508c7ef37951b91879a56a361f37a40261eb793b5c20714826e1335094d0f291cec87ed48c6d0c594f97e956fd9d6bcbf5fa62909073845d0d188ada3ee45de44842f8f8b8bc2e448805f6dbf80c5a09bf0d1360e1530510c1b7b90765969d2c97fb0d855e125572ba07dbe8879468bb8a7bdb149f12a26d2139066c6df1beb53b5e3d746bd519546c18ca605dfc677a42d823d86dffe6fae0424c13f0b300a7871a2ec3f18454f5423a4c811ba8e26e09fed596ff69b3508f4462f90e562bd9b4a5b0b5b8f5ac950444c850a9de865b586e7749aab612e4a0f5ebba253f1adbb1909196ca12d7d8df4e725cbb2cb0c06116f53b7e64f58a2c52cef3d51636296d8b15e8756c42172601d0645e61511527594d4ecac7542ced6bbc35fb63fd0f58674009824986bc105ecde0f81af31d5a24022bec53a0476496dd1d78133187b4ff430853b4982bd1bbe7a85eb9c02fd292a062821350bb51817f5149ad2180d055f8f7142455202a9f3d07d3097aa805e84cf278f3601d67542d2105e067db1268dc3ba0a7103aed1c4812f5900428ace6ed0d3723601"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b0281a4ffe8a9c9abdc3989efa749adc45ef1ea144785e020a9a6c11ca642439","proof":"54cf3bf46381a3291fd4b0ab9960abcfcab8676627b1b750932ded5b7ae85b4e50accfec0545d2ec79f4071595b60df6b0b9deace8ec1f55cf92dae3f852e96e282783a1b79f7a3762c22a821d810e98c12d870129c6f83533a4d4f933fde7056eb145c2e0d6a594ac739ea8bf073b5ac1af79d5ebbfeeed4760d7161b6e2b095dddf4540126d25d1e64bf36d2b5793941fbb1cb6118f3813901162e7e9dba0157224418b22675f7a58c40ec2e9316a2f1ee569b87b7436a6110ea369a8d6a0c43540dfbfc98df04eba1c9cedcd56d47534b197cc367084c9350a066e2db2f05f0aeab743bb93198bc9b42e2e199fea7fcc1d5db608516582f0cafd80bc7305e8ad1961c5e90bba5030e2aed68b184b1c67b55155762b3e5ffe47f4d2e3643369c238f5f3d1b7267c55e9c2aff8de9499413afabd0f0ef62a207d5f8b2758020443114afc044ebfebfed6efc6d7749978a3bc31290dc6c29971e1edad0c96d5c842468eb6d50b094dd6dbad280208253e64dc44794f18214a9b83e3d3437622680862787ca1d16988f94bb4fff6aed304595331b276a2c635bcdbfa1a5db627be2f37a707901a8b3361a35d525c42d985d6b76bd2af32642eb2eb3dddb4a327116d433a3c5dbd9e985a3e6f61c4fd811b3d8ac8cbcda5ff49180f31f24f11c68fa8ae8fc4f54ce95f89bcdfd8e153e9e0e483d63921dafb73be28bfa82c2a003cca9c1297a8346690932aadbe3b5cebe96f4d265d8bfb01738f0f8233e945733780fe0335da5ceef256509b53059427a72abf95b1139fdb67a801464ca0d886136b0f87fc6384ec7c3924565609d5b90e0b132d72b73d0006601cb305e523a4c2c134e345dadd437ebe19f80275881b67da8ab7e92e2a688673ba62ab692260e458726d2eec3dd0b4cfecf46151a51afb3cc00d2b5053f53072f28d0ff259e06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3e0881ebcdc92a8e966a00bb39cbd2922ea3a3d1811495c802c462bbe5ae297a","proof":"148f346db53154d0c82a627d6eceb39d6811514825af6cf4d5fa7ce64fbf0d69b23c1c619e721b2c01f1e0b967b7e4d96a986f263744da9eab58b9656323d27bc625c8af99fb22450a160bd50f33d8a4e663596338e3f794902c788c670a8918a49c561bbe4dfc8b694ff085492d40f9d89cdce142aa15c1afca38a1c18abb782e3b8431c475fa4ed6051908b7ca1d5b11829fcc0d843473c6fe12d712a12f0651aa31ccdcfae0649f600c91daa633bf2a8296b2f8b3dc4c9fc36034907dff0dab0381ae95bf8b34ee5993cf645e352b5f6813f5e12a2e1aa67495fe5ad7df092a740a9a7434be1450287152349e134cdb59d4bb95286ae0142dd60ef30f8e1c96b6098d70b9f703f7e51d21bba76d8db8f4556fefe708bc292c95a54ad2133906a7a2fbbb7a682a6e7caf3c82d5c1c1f4265a797a58e97dded3692d0189fd7a80fbd47d32d766fe649dfbfcabee965ae1d0cc49b33b1608876074a7d4726c7f06402327dcd7350579db6c75d4a86d70d683afd428208efa033ab94e59a18952401fb3cbf61acf2e9fdc87f6415f713d3e384470393c9b9a6717d5b9b63eb30094cb03c3e3d551333456aac402f9eeafbf42ff86cc0971f0dd513d2b50e7536f28d3703cb1666bd43cb96d5f8e84d349b8aafa04f87757aacde3d449c882b65de08081baa1e44c3e236baa1343ae660e2160ba46ddf75a9b1518bf7a4cf38f36e0f4424ab768b890d6a85cc5044531c657fe5fb07a52d8a767a9fbb81beaac7a1cb8af94f5dfd2fd50880d00a861ed27d4aed4104fe527f1d744e3502ff8f113a8598c134302b8c3a0160076dc664d68de7be26788238986b7010e94ccbb1446e4837d62ad70525dc16f5b5656eccba329588897279becb6b067ef435d85f200ab82e74b0e2231dcb58f756bccf1a0e6dbfedf768d7dece9fd7c561718186807"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6689a428f8e0f772b9209a1cccaef3e5d1ca894ec3cba1a44242a7812d40d25e","proof":"94aedb73fa3937dfafdff27cd61365843484ad97ebd0d918d61b7f79e38600012690cb989ec214725cea78738026fd07bf0b868e31648967ed5e24f8a8921421dced6ee7847bd4844b13ea30f4e5b4b9f5320dcfcf61c4611220d2028a5bc20e0a8d8530f8be5b64e15ccdfb4a8c6b56e38a3f4b89ad42701c618d1a396b0e4d07799d91747e428751bdca015041eac85966030f5cdfe8038b61839141e5f70440d36719a5750193a428b659a9e7e53d768a74c0c3458638c7ae930dd8d97a09b43f7b097d50fa7b82152d8ab4a4c3706ea7097d9926af00044741e0bd9b6b01a44a3a6fedfcff80dd9663d2940326fe2cc25ffc4c8c9812f6fe77113443f30152dcec6026a5ac937209e5e9dea53779c6771b80c50bf71c2bea2bc05eaeb23c8e310b2efaebd0cb45a9909433bc2c75474935025625e4b9418cadc3cae1f468e61068942fed412d032c1b71c68ab38092e52d58b867aa210abb964f98293123aa53772d7587e4d1da78616766195276e88eab1ab9643b927ddbb7e6e28f4f7d9404c83025fc1d00054d6c1eb813605bfc8c4351010a6e1f55b1bea7f0ea1b4348038dc3dcb021716d44a6bb0d377b3adc57cb9f011fd0a7d3857e1d2338520498911c62013e5ad35f2d1b3c1a6c27b00330c46caac8569c92a6f4945fee4615ea43fcfad470496d4f46c5dfc0660cffb56f0eb9411b0f6b407743ffc1e8234e6661ecc793d93b372cdf4806f121bba547fd17f0666bf4d42b8838ecf006994fe0a42c85c240c7783c634f9a60c3c1627931073570793ad9429aa5fc2bdc5c631edb73d882dfe540b748b1600515ca250ceb844b5be03a115437ba934f96c1354348905d0ac5d53fefd2f7fd188d249be182b6d70c97f32d93da78677995c20c2c3051dea4a37e7422c914805f4f6ab8f31ff578126eaebc58527bcfd89e5505"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7a761e97d2d8efe203a2d44eaed908394636c1c0ef27bb9138b32715a654ee1f","proof":"ea8585c45167340b2c2264ba45130f925e2587ebcd90ff9441911fd55b2703305472010dafd3efa6d72c47589c4e7f3fcd13a13e95edfdffa5d4128a865c695dfa5dd28415ba34297f2cef1e8a33154354311506c7dd778feb513d28e982734ae45f9bc526e93f56796efd4e8a41cd5b144ea7c70c1f4e40c65fdda0859968270cca03d277c8e2687f9f0504905f613e29e3e9e76a4b5f3787dd7910b8394c090cb75b597048670a1d4f5671534abd5bbabe59d934d8568c4c0d333ce696f70b98a500635c702e6d0b18ad5587dc718142e3caf036b1a144a393e3b84c9af606accfc7795a5ba438b23bab5eb65be123f0dfca1b7e0630a8ba12691ceb60350cbec91060a966a8f00e1f85656795b33dc06bac74839efb1a2b8286fc683d27424a480726e798047466724154aef350e546a81f5c79115919cc8cfb5f918f672150af00c6ce7d5c622b8193a7fa8fbf4509897dde531c4ce228f1a4473420064e481eca5bd71d961926827f418d8a79d40b6a189f6fdaadb4639343adce758f5252fa197ccf0548803ae7e19c9c0f049baee88f5633e24a39b681000d93f7506454e3d68db84cdd569bcba6180b06352a8f6f072afea99ddba7d2896b1f2cbc57ca8efb1287ba7edd6d1a4709a6f05faa95d21c434cfcf5bd7e85dc3f2c336c6efe804189cdbd27146676737dcd607452b946aab455367a167d604e14d9347127a8737d65928c7f5f998aed8e30f6fd1eea6cff9020880d675b261504af1a2a7af2745369be1a9d238e8d30d41c1f19b78cb3651443c31b39b906563fca32df7c40e3d4bfc8d49be606372b498a7faed7370042d73d448c646845891e0fe23e4f4bb04ea7cea81cb23ba24aaf08a9815601635642ee5e7799fa1918257f54d302600c3d42b38af01e94962f63f71cd463aca9cac845ad777408090b712da1f202"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f4cb5735a36cc2766672262843f122c467bc86f7173b5de4678f30d13bd53c5f","proof":"ccc7315b68d3b4824ed0412a01965650d7dde5d1ea491680adbe861faf2c5b1f52a4b4a3f1e260e2bf87d8bcec266a08f80920bd899fcedca09523dbcc9ee874ac716e99cad91e95b1d91243ef73ce5d92e9ed6efca27b8f53ac4cd0066d8a101c2f91b5bdc24ec2f65e019e74d8a08fd45cc2924db6bc75fbb5175d5b6177270a732efdce6106aa43c73c8bcf7078a4e9eb0467d61e617acf0f5990ac419b0bde48053b0d7f44d69d103921c95af3c87ffcb2d2278aaf87a9a120063ffbf1099e529085c7442d328ae6e15bbe91dc2e512243c3033ea7ca405776aa1f2b730800d543e6311d87a56e8898e35f586fb1290c5d0b08628f72f4ba4f961a52c96a7a860313d45e196ebd181dfefc5b7a3986bbf98f25a259f72f708bee7a8fd955ec8b92722b20e7d38dbf62a62cbfa797027f26953e6bdbd8649c23156fa0ff42cc80d213b6259ceb58befc112b72439a1d952f5c3ff3553d387569520573227a167b47bb54f1866cd584760c96ba8d020bf4e99db26ec02f4ca4f4b886c5597c92aa8e3d11212b4e436459aaa9541f44e41f7dd328ecd29536164788f74e6064ee0656885cdd5a35cc15012c7ff0050d72ab1de851949de099926edab3cb9747408cbe6a84505023aee3e9167eaf57906961ae916fe0b30121032ed19a0cd06470c798773aa698e4d15dc762f67880eff50ff91057fe279a1262bc329f25d00be25da16f9cf5d48d7e11cbb79730ba57a315468ad4cbb97da668509d0606227dbcd6be67c9b17fcc5b189a820d627955981e4ee6c1f92a1c70de7856e558da633a24ee3cd6d7ea9ac95acaa770f1bb71f2e370639243cde35ff247ede0667f1876ec37155550257ce0087a108b1867871eb84ad3f89427478eef8eaeddeceb06b2158b3ce40c4ef545bcb60dd94c533c905ffddc3bb452cbf8252f4a86ddba06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fef36a8e8d8b6ce62c6ff31153ecfad754c8c5bf98899be76655ddac899f2d23","proof":"bcb2e8140d65ee1c94f87be5c4b1874ca121a0b7263657b6c396df2b4046f54e28fb5c6965c400acb13ba3fe598a37a84cee527329036b016f26d4686c3a295f74255a7541cfda074c7732acf6a1e40d2b77c42873448bb91c89cecd086fb711fedc00289192f9fc4789f720d8663b7a77b1859bbb6d635be8a409f533e7b64d27987b3afdc3ddf851e5a878f85fe4fd924b00445dd60d915e0de288ecda1a0e7a589234124c7e5253087b9647aa0de5f70c960a31298e3f922a39238009df051835d1affbf805200881cca1ade260b31772e8c55651b9fa12ae6ac69871af0326e68f7a2c11cd23157c286ff8f8afdbd557a5f52f5f44e7a6bb35b01cc1e26714ce64ba352f9a3b3d349d207f8f211a1cbff8feb4b488f74510830351ca6f21c225fb8e1e82dfb5d333bcd3433131a12c32b4faa3f7962dd37d09b0603d103308b60350a41e15eb3f27661fad83a716c4608e8076cfc3a690109d33209b8737d084094824b949712af0442c6e079252e9fba8418e7adae6b6a7966359791010f60a4477d309fdfe217d32d8396334924703b1017641ab24021ede3f3a1c930b3643749f14e47ad7b280012caadec4db47746881ee72f59f1216ad435163f32548fbebf4e60e50110f053b9614b8572049ccc84898da5cad48000b7378e05158c453bcda53e3dc9c117cc6f89bb95f5fdb622ced9ed68ee44120ea2ef0eb997cb4bdd09a9a9135953c6266f8aebb828ead89d5c5f20211c2adff4f82f3e3951ad22adcebb8a7c365f56cf260cbd413ef1320890558c318d5ed19a3741d7ad958780f5e2f4842154b3f9158177b0e8e362c9a24490db07b923585a0a72fb59728ebc88a357770aa70817650d0f4ba3bee6491c97462c6b29f48a0c59980774b03a5a4e5b791943d3d8d2c9089a32d362a24b593dbfea9179928a6c102c401f50c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"70b051f4590791f528cfe06b83f24c641df91250e66fe87a6da9c6a40a01ce12","proof":"4e0763e3508dba6b4198e7a859da65effaae17e4580f271b80cdcd4f3033b61d3274280533f3e3ab861176e549ec845b918cf3d34af3b99c84acc9de55637105cea464f50a2e77ac91ad7a7599cc125e10298da914dd18a4d973411e7536822d7ebcd2d877c2b8c41b7f15f2540cd997630025cb597dbdab04ee9f9325c6c172b7920c446fd00940bcf4acd6188f5258ec67f6b28a670033cd6ebe0f91b6510d33feaa1153c1ff623a20493fdbf31fd0ec9d8052ea5e34530668afcd5ef56403b8fa01fb30c17b22553c584d93ee4d84cb6798c71045dd8b7cd4631975da1f059e0fc087636c4a9122ab405907b23d4f394ab0807b25405816a1a3da3dea0b7d6854a8c8300e49884cc11539c7caff29aec7c93b0ed0207976752518dca3ba5a044569a8d4e1a97a72adc6e8573caea6023fed2690ed7c2cd9b1054bdc61f524c61c5b562d3b05df6466dee111454bde4a7f10f4aa2754f2db5af8402ba5251802c43675040fa07950f33a818bac3d20c815c69fde852149a327f6685882847990c3ca4d4c9c4a05336dd38d70194b7840d936aec9a6a9a2ed725be49333d870822d6e068f709570978d123c178edf2cc71809e64fe8ee6684dc1d425331204bc6be78cf8f92f532b01d9eab3aac96511f5e4c05a43f481357f7e62ccea519596e5fd8c369f773e060056c86b808cecd841e3b597c1b88cfd5f93a246ebfdc1a24c4b4dd6bc6f71c7e24114d73787e4334428c8393e4ee79dad11f9297d0690a16576ec3ed8b40d687963cfbe2efc34485196f1b465187969ccd44c68f1de42b0a249f3aff0ebc4c432964e5df25cadd0d10fd92d3443bf1b4361a7d46f50b76eb5f1710cfdf46c2ae481e7d59378d2053f16760e39fc8fd761329ba1a93d403a9822b1166dcabb6e994ee2a49685126926bba6e68380cdc3c64bff6dbe5110d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"72e93b4eef3a8bca4ecf1850027da526496ddd53f717ffad4d4a64b979441d2b","proof":"e670bb45e187103b606acc966998a8c89dbf8d514d43bff1f247ab42b4f6f438e25ad63a13c3ee8caccbd291896c62301cac15b3d24e443f34adcfffdee261687c29b463a8a792f319c516ba3b05b5997b572b212d3bf3ae7fa6e5b6bf949d2d0051318098ba9eff270a65c42ada7e4ee8ece6f3c093a254f09f4c0fd4f00878c6f46ea29744e2f9e8b06c36e3a6ad64daa83ab27cfa053d89bcb9059a1f870ef2f6b274bd78380c6b225985fdc342f6fc0bcf7f6b8e259b7bfc9ad454a363098967ec91c79b4680c0bf5389e5882558bfda4d1ddf237c23452671d2e7d62e0e24b8532fa8535aa681995bdde10cc8f65c04cd6ff160dbeb30375ac438f77358749607fc81753e5715e4d75c59a91ca6657b73246c4d56acf26b6ec204be7f7296f226811a07dbd3081a1d75225f9911010e3f6ae34df2d9d3e7eba56872d0613c456d5260f909719016c16d4ff7865be4b2779a885b076352f5111bf4ea901faae2f8328be0b7b49ff137bf88a5cfbe21194243c43c71653a591387b3b1146440af72617c31b5f5e9ab02d7ff3fcefcda97f4b4628f724ead2635450469b069827bdb0f3afe490e2e7644ab384a3d20fd863b926f4943ff1b0b28aef203fe58080385cf16e1205a223c3be2a80897eafb4ee490902a141ac988536dc61add50962786e41c341bf6dd8cc3cfdb5db3f1382ddf4557c1f4b42f805892a6dffb40e65834743a72ea74ec25f5c18470dc5f99230333561b8210949484fb351f2969c2288baeac5c8256256bb96073efd73e6ca23f7a4e11478099d6c9b7543e3f738419975b8e60edd2fb83803070a5aede467e516505fb6f9dac13858a13306b5f92139bccb01e55624d8ee0036b93a64810b0063118901c93d7ab9a80bef3d207e1542e56a81f51e1fa25332100d4e72e294227f73488cea46a0084c43c64cc0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fc97b7eaaae9a341811a14ff934c7e33ed28cb5b892a001f50ceb9919fcb5a04","proof":"c6deb5bdfe05785fc159b7914ef5dc25cc65b8482c245aa187eae37b4ea9f3067a706640362fa2a0553625d703c87a8347db0715e641b281a4d9cc712df4e91846f0b215a29c63d9c6c9175826f9b63649187ad1db4b590a338d85dac900f3064c7b0570d5468a065eb23ce0bdf01343db6eb382d854932a901755b39acdf11c8bc735829fa74dadc8aa5fda3b3715f88bf5d379ef2313d00a2242d65c6145058f55c3b0d9071027ac3a11a8ee9658cccec0ceed17eba24d62a6062e086e6e0996d452704910c688c0aea09fb44de85908fd4786cd468e334b658a62a6b1de0a1cb4a85d7c49b37b8cb96a4675fda052ca9d4ab5699a999d95dab408fc5ac3578c4d8773a400814a99f1cbb4609d0527986fff5a5d229b625e946eae6877e262e60df6617533ab528889aa2ae202cf7b6954bc858be7dd70b470021a676a426066b2b60fbdb91316cd6435d1bf671a6a1f20f8c450b664337e9fe8b1c3141f2928673fe216bf83ace478a2649ef1e7efa6099e0b67f0b41f38b77271b5e4512c34af41487ee2c88bf8d465e8de431a0972dc095946a8d365cb1d6814231e19371aed60a02dc1e771cc6ee58f18641712fffe6aff6f4a34af2c6ab1fe6bbe7607dcda6a903b6cd88bdb7d8b032ff20735ca16250e1cd46f03df26b6fef906c702c0f1a289c38d74f4bd6cc146219d68ce5661942da1a9ea6ccba073f14dcdbf4a3247e33a2db76d43ee5cc4b13c321c8250e3ee49e6bf97b692d456d92c64bc0a0c4f06bf1813f86a83ab80275c0d6ce2c5db62a1b3888956ab422166710ea82f809de2ee065b79e14b57f6157eb84d2e9e6c965f7c2a5f5d3af10f4877d77606aa02ea3a5ed01be35206f9775bf178a3ace250d56f3d8d7c66aca3bcbda752055c7be543b9e2ea03ebb5e06da91241d6c0e308bfa94c66de4c94163c0391ab0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ea7c93e2ca08c0563d281271daff9f6c04810df5596837ab093992b4077baf04","proof":"16b1a0e91cd42dce97c344d487fae1d51e088691f88bd0bade38a7fab2f36e282e1ea0e5d72d63a957c36fb76018012d16d3189f2bae5fe81be6923d61d19e4e90a7a79985507587981ea248849cb11381848490896bf4a8b76822a0bce40b0c04f17fd695d02d88b5764e2feaaba8682b675bd99fa55f8499d630a4e5479b1515c0e3553f1a72ce8843851eb47be81ca775036866c886abdd7ea9f3d8383b04caf606d51e0a9200b2c4486c2f742b62c5cc05d6070bc3b24b6975b101177e0457eba6198acc476cad64a606bf73646aada5a3ec8878c7c1db82986ab97ba507cc6993c408e1b5b670843978354034e1d5158b12f40596a7885e82ff340b9037ca300c1c0ace6845c4c63444bf018be4b55e825f9020ce54ab067ce03bed395698adeb3ece42b47a31c1869f048c199d39b700579500c3098f2ee8f51e23b876360563d50862a6dc51171e603c9d2f48d4924878d02176b076e36164a7280a40acab3ef65e04595e57a1c9a8c63a938c6c80a8c74becabf40972d6b2f8e24d095ae746c5133390081a5a94a5f7e518e981728bb1d6f058bc631f9cfdc7a7d67d6e6148f4d81083d8e5f1a84764b4d0eb2f2950da979a7a11b9faa9eaac28c41fe49752616729a7ea0931ff6ecb2b3e3d1fe384a31bb2e938c84525aa928f7c0ddae9d5b30a40533dc12e964fe5c42e4777c270c928f4770d373908718c68a1116a10f245619c34a9297a438ff544de768abc7109b0e0f6866ec2a82229cd8531149ab8e6dd83ed5b9d7efb712b18bbc4b0ee2092aa37ba13ed00fd5c3501503d04dac40037704101db7f71f0d37bb6f26bda104cbb3bfdda667a248236d7fc03e165c1524e1962315f2998749097c291687977c1e52a14e5785c08e3504b41018467df43bc9c51a657cf27d7dce9bd3264e75e6565b70fe0923a58e0a8ade803"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"82391a8ba239ae1902c05077cda98bfe768a707fc35d3193fbdc135d52d34103","proof":"c2c6500664f2f502b9813b71ca544ab827710052c2337e0d9449c1936809381adc749f13158c90e3df7cea716c1ea4f189066dc9d2d8db89c82baa30da567f13b0fac6e7c6d435ab489e7e5ede1d1ed9a76905afa1a05afb9da147c1bd02b3153848d8a59aed8e50dc7f08740b907db6d938ab91c545a650e7fa8694a6afdc0fc1f22ced1c2135383d49f8c3513d6a74ed045ff013fe11349ba88532a784930e19d6688c39abe3dbf451099f1ee3c88e84d233da57940fa3fef4a8e873434403c623ab0ab80400735a4235325ba1c1ad0191a006c9bcdc959acf7bc42a40350cb0c9b45562447e92548b10457fd03a2e1fdbb107ef4240854466fcfe5cd17032de597d73ed4c10464b5242d8925344f5a8c852399a7fdf8a6761bde44c8cc36f0833308fbe783924fd48e50166355e28b653d79d4a1f9425b098a447380c2117d2ac840c2ee605738652cb1de354dffd3ebbf4875c80e4d1d30aa0100107860a306a340e744270b31fe4d1f09f3c131cd9838f23d21bb2d1f2f91dcd95432d7cc28c6f0293a17d5af80010cf8561d5bec317cf1c31782eb72f3775d8a767420fca9b7ecd77d7a01b8b2c53522406744fe85e3219a7c454caac2526d4d119f7711e57bd3914ba415e06b7724841cfb4eadcc3903f89ba3b906526ec207cbf9c15185d3c26d0f4477c00219e0c532bb50129870c7540f7df23e58d8cd274420f79ac997467bd14c4b9bce34ab77de6f05dd444308e58c9b1a98714080532446237eed18a0f1b0a3371afb3924c4e54c2d363a8a9971919bfa0522566a3d6e04d12d03aeaf11afc43a1ac059b1c1042f8c03e8974f4960562046e3dd24bbcafaf2f8596312f6aafad385eb84d7a03af254e50bdc229fe309c38c6dcdd900c45ec0e9555102d69319d896c9795ae5199d5a07bb3cbf5ea9692a7ccd28961fc7faa04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"787d3d8e9368971c65be9f3c81cca9b3dec1a3c390789c0bc22fa5097d144b44","proof":"16e4d1f363861c92b9933b3b522fccf2971a067537a4aadba94a1733b0fe2e17fa35841fd244d7606c5b929e41de65268953811e43d49809882770e160b04c46022e992c61e1c22a9918d9dc19569aacbe09e270560b224192d06e295364e90ba255516e558f0930a37a214dc4690c1f6cc1d93b8ca1a1be7643387af49bdf1f1de7659e66492b152d24888a15b099b6e441166c91bee0fad0c3c2fc046b5e0565419216cde86aba95de35f792920eeea4ac96a41af8f1fd9b91a58e32710e08b57356c6949ef23f0b741beaafc8dbfe8ee25f6e55ee2a27eda6e90f843b7b09c085d1ad90484ad29cdd837425264df50dcb531d4172035b95052b3149e58b6f8c456f9651b80d5d1dd2c75d81214c1aa97f757bce4e57aaab1de299033efd59565dcae8c221678fd038a1cb7aa3bbc717bacd3d48bd4108dd88eca72ef8962b6c41e5d92db03b0e806c1546dbef7aa0829a439681ec9f4997d64b0a62aba6771297bbbe790cee271d2a3b9ffb5ba37a8c0b1a3b56689193bab24e8aa96f104b3c1ea75fad2f4118270dd5643702a04ea850b9b1280c82f8ab1c56ba40d57a0b4e2672814719c5afce4144fcdb396079ea08bc8186ec1215579fff14b7242d48c8272163ea275c4d05701e0dd54f41839d0812d0e6cdab2a7e8841370f13c671a064707025bddd8124cf210f3df28f5ce826604c070d9c5e018321f4e7c9ab5e6c2806b8fc274f478a1e6fd302fc03ac48c0500f2d5c30752c82d4132de95e1d2854a0af60159653a94aa27f6a5fd3a4897038812b69dc07c9c85a0ca22e1a1e94ffeaf7ec1c028aadf5f1ef9adccce44f9491683682033fdf936f3a7ec6b804a4703db1f2a9be2daa18a5c497725dc7e80157733cac557be4540ee1213a150ff43174806b5647b6eb3fde52aad450ae6520bab4d82eecb5e71b0c9b69785a02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c47cc7253745298c654f1a39bc4e56b18811f7850d62d1a1948f40b8dd904626","proof":"96f2c3cfe6baa31c6a1fa9dfe341364a88bfc7753553611c449f9336ffed66744016a21e46159046298945116e2226bcfe58766cbbf0d8d187886b5fb4563e2fec102eeb381187c5ca4baed978e8be00ecd4e77a76ed9d7bf69fe6fb268f8130701ef04f6f788ba77d10e0c3374606a05f588f37e7bad97af8a72c6ba964f72e97a89128119ca1703ea02a0fe08ec37c5204689e2c939575d73ab5d6f976db0c1ac94f367881b6ec1b9635aeccd5815bbe1601ccb28aed6bf041f2e42d9aa406df6b49b4d838ad30f592da61a2319f530f6be29b9b59554ab9cfb7035e45cf0084ce6bc3231aa2719bf0d57ff7123521c1069fcea5a2f583e2d1e5cb13d16209525a69fe1fda532fe4ad0de784737cc3deea233d824a9051598887b18273073472323cffd704eb494f371322b9c51ac7d97ee860300484ff07dcb493ff6a5028a231cd1a1b3ebafc9105a71b9ca51b57db5dad2a330acb9639023c1aead12f0b605fc2e4ca6bec9a5fc80fda899e0b39ea5c9a81c27dd2adb0d80a17b738b82b00f37b9401df900ea216791e1d1de47f60d94b83749907c59801f49af740c978e69ebdaf6d93eac551b58ccfd596edbef414c15c1a3fbf5393078de72818a71cd2961fd163009cf6b5e26872cebc8a38bf4b5bc09b007d4ac0ea8ebf2147e5405e3114a8fb14dc72310fdbcec57cb152d9feef6a5b3d55f71b6abd0bfdbcb92a6caa9846b67fea69aeb459c980367c0ec2e70a10b0a0857442e133858163cc64ae3831fafafa3ac2fcf9cb953f58e9e57043b3ee60ee2671910a894b1dbc4f5f7e1ba53706f51e7e4ee97c95def8eb42da42b0228d88c56b2629cfa9d740ee48b8d197dfc4d99a0cd54d6f020477b3e9e6a047c0e0b5559ffac17af5610a610d9ddab981ba54f5b3270a6e2fa280daf24be1507825624be450a23ab731e81f0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e466c6f7085474d600c6792e4ada29ee915685dd8e07d7d8b31f389a4d91a11a","proof":"289339e82fdd9ce7118f269dbe23377e2aed50a00143a66459eb8d1ffdcd1973a2e9d1c622bc22ba242624eded3cfbd48fe28992a18e092bc865d80001722137168c98b22e11074461463596ffaa7221f882c0b0ab0839f32c1d94d3efec6b08828258d2085f69a0741c4c2efb0a3a688ff1eeba03a05de7c562d03fc555c144811308e7c93f4e718ef22a127830f3a9ea67ccd93ae667267d555cc2cde6aa0909a3c07a799d4a2eea247fae897b61926441a530a56fa40b6f339155dc2f8e0cbedb22a77531fc8b4cd5b29dce1118b21a6ba0aa48e6bd5dfc8e9679d4b7920a5a8ad45be5a7be92096eff1e0d931e22cb309759b1ed214136b1a34aa443f2512cabfb3b3281af8112511c90051eb27fb86435b1e5b54e5f79d539a71c1d094e424b655e2941e5cfaac9cb941b1f2f5d26f17fd188ac55e80dc0e5e45d4cda417858545f8ad4ec7921c86692b88f4dca83648e214fcb8c39451f2d26149ac41cf2e2fa961f1f639e92a27b0222a7368c1e68c8a4529545e81f4048db5392b97f3829f639d9d90b69015e4dcb6fc95b5e1eb52a5574706e1e9fc49a205e18537578054e1947af906d8c7853361860eff409d2f4117b647a6ef924d761d5e8cb033215d122ec78ddc7781d1debe207e48bb2247fac4d4e0a034c72012a680271797e9dec0884d3134f262c0c10a231ab678c9ad8c7ef0a057501545100566a9b77807ce3e93e5c134805be3aa57038b6cd65f3ed483f945d56da8b4533fffe8810523693ae4e70a438d9ff03d3be55b4015d727d9aff29f6c71aa63a7c7b10c171c46b1ccd01a553794b0937c48957d2f2e104c06f28a5d9306d14f97385c66728bbb33c0fa86d0bed56a96288ddf4a3a2c750eb1bb29c29819f43174c6c61e002cb4645bc2409f4e2a868f68aa81fdba71d2cc6f868c659953515be8cd082900c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"02ae03423e50bf4bf4421aeb1883ffd7e9f2872ca4e424c7e121613aeb8ed26a","proof":"b46c25b36505545afa1cc4befb4599ecc658f307571921013d9d2dc593f1b75754bc8706aa7f90e6f4f39b49ed67854211c9bb81bf42343e7c1ea4bc7b035073ce7ed93796a3bf6c5b7a225f30de91e2860855cee01d916aff610d99627a3425603a8488a4f21bc1cd970f7f3df7b339818c728541302fb6172de02af3ee5e6a9ede2af172ea47dddfea519da4234e4eff572ceb3d60e9f1e4f44ac3b2d86f0a4cb0ec753fbf60625ada46ea3261b249fe903ca43a0a59489d86f4694d179d0fdeb2802b6352e119c0f14b778ccd611cab79a4a0d87febc57fc15b190308ca0e6ef56ee50a68e14c1265df15894790e709138cf7da2f36b1b46cdbcac4256b7af4322db5e9815d7240c58ec66ebfdae00fea0c3c1fa070bc00f2a362dfadd577b2ad718cf51c4b99d6fa65582fac7346016dbe654bc580ba7657596d1a310a4fbaaf8e3bdd4ccc5cb28585b2aa2f2dedf312e16cd450b6c0678b9548014e58062cfe3ee9579b29b84d7e314d6e0bdc119e8163b24f1d02fb395cfece3f8e097e9ad6bcb48e993dca02176fe3de7a499f879cee377673f291330a64ce9ff4901b2e46d390d0c007aefa61a5478ab4eee14c51badaa465071353b29b3de7eede6fa8fb305a9d36e4078006c964ead8f1a16009267b87612f6cc1f88d645f41803f8a38ddd2d22237f0bd454c4eb86833acb4ccd57eb6435615e07e4a34a345942cf6dcb0c51ccf901299243048a07a6657a83337c0363eadddf1dc790a5719332bc861e538f78b602bf035b42487173b84ecd83f7ea8d95428f35d157b8322c0374c960d5d408ff6fcfda47f4933032439d08b0a0f71079cc07e37c5be201af348f2bbaa864dfa5fca99cae29026a0edaec0928c63837c2ff6b673ed89bcd2a80842cea1a90d24a31f70bb109888143c95fe6fe2dcaacc48ee5db82b94b267340f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e4f2e06e50035b6ae76f77b817e379d3f4be627023edd8a6c47bdbf8053fdd1c","proof":"6a1e5f92f01698abe46537854a7ca4a52b336127b2b5c7a635164b7da7b51d3f30f8b27340394b4d760af41aa19456c9b13118a378ee73ac4ba501093390e158b624143f1467cf7ed14d7dbebe659c7fc54274314b735db3f1fcdf4de6ed732266ee8bd1223b89e3807e2e16bdfceb4b4e7515d49ef8ec899c0d8f3b90a38c28150ed4929f215a444c5e2ac582ae3f3ca303a5e7243d00f280440eaea023c202f4030f8cb4865608989e12df00b568c11d564031d6a53d043afab63b2e1a510e7f9427e7ebb7a809827d33d87126690651696a56706e8960edb74af65b01cb0578f8156ab582bb6f9e0753e030c60a8327d2ccf8496c83d9157a794e421a624cc24f747e4ca6ea5e83a6daabf2c3d24dc08f0302ca76377b55fa4a7ecf06ce19e819a8f485ad3cae720c9abfc00f5aee0297b08d6ee034eefc25c9e22e1b7836a28c82bcb815a3fa07d07b7a5c85a9b24bf5d717dd904c5d3f24bd01bbf151161481b88871e34990fca35d862ad748ad3d53c971376e166b9b35ba822685f57764cc4cd39e3b885a0def172151f5bde08baf40ee32ede05d286f6aff95c95356dc851be0e360ff47aeec6dc90a0c6c88b3e6f4c675b590c9666382e1f1690349da8564dac629a3eec462e4ebf636bb3fa8873055c09090fb187886b691b02c0fbe82266e3328cfd7f947c86b935b5b281ab1314f815d73ef6c2169e7a987377ebed7003d59296e51cff6b0a2f3935a3832c26205e76ad4c9b44651ab5eccab46384b70fcea72c4c32865f468dbbb60a6061a45dbfc89966ac0f25ae236300f627a08f3d4f7decb5e7e866f51dc51b85198fe5b62855e110e24140a4ed1657564c17c8a5af33738ff3c0514e39eb58f9f4967736e2556196eded979df3ea2d30c1e0fe8dc6d49142594c8baeef149e4ab3b686130cf9d7edde3cddac9aa03890e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8c49197a2a3a835cbe4ba0eb62772164a7dc08bfd265b55513810e1a3345ab68","proof":"cc59f78582ee8ee1f06273848ae36e51de5a22641c49b99b5af87be07e447e17622e508e3c59c0f462a0920063e008d0eddb816d980c56856f9476a5f8a72a65f86a61b430ca381772866f95d8ed43a5a0cd0a788f31da292af4a2fa09fc4c69a074591b68858a732a843058cc3963156031b5d097afbb5d6ba0a62adfccdf3e36c4616d504cdbdfeea649c13333da18985aeca5273ac045214ff09c2b6eda00e9c00fa993ce1a6fd4594aca9b5b7b1a419e927bebc63a306e3e41093b0cd20770973dfb6c42cce819ead0c945d42ba4eceeedebc19d67dd8056d35e8f2ac40ebe9fcca505d2c12ca66be16afcbe45c090b32f8ad960703a0cbdbc8f7852690e14c47fd632d05784a4ded44f936db0173f33175a84068aa2a7a5f20600c3d6025041f0e401235a0ec43e53240d9972d5d9a7cb5be09788afb5c74fbc6c741d3c7245129f670e3f7d0a6c781a64348c0d8156158f44571587e0cc98adc1923411f0c1de667d14b7ab3c472d428a4e67f1e060c45fb47b4ec5419250bc4fe6f2133475365c12dfc53e522258dee961a6a7077bb64e7ff4125233e02c7cb7df9d6e14d492178707b24791cec39ed816eb654f8eaafec185be2d1591b569dcce3004965e7c876a69ae418cd7a214daa8a5871d36cfab0343eb5881c65c9a082f7b7b84dcb7e99824d9d85a1160a5193d3d600b12242c335221e78f65a8aa9b781f72f24d7bb2a096ede32ca3e4906673a9e448e4a0a123fe7e59ac53174f27b37f7c301f29e526d897352c64104f0373e8a43f69100a1d8ad56ae4d4e8d1fc2d126b1ce76c49e60a9fbfbb1c31cffac4f78eaaa40cbb65fdc9d2392731fd26820c531c6161f567567177662c800083d172303d322dcdc5ca2fc75ad4a54b4456eb05f6519dcddea5ab261f33d65ef4248ca3d3ff39e5dbb6cfc530b4a85274095004"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ec3dc5fe089d76de04c68c5db551d7390dfe5985c5217a1492b44496f0e09423","proof":"da24b64dbee07676d941d603a23c7ceed7596d2c3da7c45a903f16bbcf20f00d9a56b5a5f928a9b6000e61e1b808de4b170c2a5371bbd20a869e5f44614ba47606a41ec5eb845622cc4583d238913ea09b15fa612bfb343b2ed3cbe39eb6304810fc645ca4e05482ed45bd7ff55b500a8fd787fb6bc64ee5591df3003f91637721912a03989f2e94ca643805a25c8dc6ec23546139ddd55271eec9a8e678e509aa14a41bc30e1f13ebeb7b391b68f09833a80999db870ede5eafb998ce59720fd6163f219344537d7cd00f083686844b799b14672a99c55b73affe2a4fdb750164327fccb88b239b99ee4f5c2fc572035612bd9b7ed0a30bb1db89be2ba63c38988f449276f78fbac4d4138f1d9a8cfe3896cfb347f3226b8d16942eaa6e155e8e82e1e20fda6f83aabb9306e5cb347d11b7b86922cdba7f8a6741c02023200b58fba987ae14ab1fe5e9f51b8c865af9a0a12ba3b01a171df9c2a527181e4023a2f945032ac62a6138a575c346b05f4b71edd29e88086f9d5ddc85b96217555e58b6b51246db08b2aecea08ed58052b5d40e59b9aadbe6c7eb5ffb60464ce3671a2d260920303553493c4fb9ca3bb0fb2f2f5de20da86fca2ef8399ec777505d964b4561e280446532b8d86142d0704aada5391e7a1ec2527c72693b644f8347be87d84cd690caf875eeb04f49cee7a17054356368690b1080c7ba75be5b33430c61f8da3df88192e7da36afcae43113e0c081b7463a437d41ece65e6e56b021c6b2223ad0216b77423575180df2586e156ec8d4bc48f4777f16e07eedb4436c4476312bffbf7f9ba3a946bec6ce037705192cce447beb9d047e97056e15a60ccc50498d926f6baa312a8a3ba99665c001f25ee32d7345af8ca3ec0d3834270ae900173e95d2d788475b832ac683ba6d826e60cbc2d6a2e549074e9e1973bd08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ac7525f87a1c2115f4544dc46624a3ace266271a4608a63a51b4a5f616104f29","proof":"86459a7acd6e2556264969b82897aee66d8667c36b49b4ca76a7a6dcf3cfec3fbe83216f2a272b3f959e033ed2e80b28d49ebecb4b6d9d3e1b87d18c27903c12e8ea52ca3f28efaca796d191821fb95ccf4fcd8a3c344f5e8f4915ef9a9815397eb4b328dcb10beceb7f71fb57753f9fba46bba2cdf5edc55923bdb44bc38d19da4c073e52ef67ba2a68a81776b2322f6ecb16e8999da4192c692b4f75ba98060baba8f87f7f44efb4551c032fddfad42d408b80491f1f7b1dcd1d1a0e6c1002728c4ca58026cb21c41ac20ed9ee5eb6a6f2f2713bcd1637064dfd1acf10d4036cb890176a517d411d483609da54ecab08a4efa224b7f370bec534d4ebb1c64770095e72e0a24db51f1a29e3150a5239854faec6591d66d51d08a9445008bf73ccca865326e01afa53e8485c9454a0e894cc4f27b949c2300b3247567e6bd46d62e72135639b5b7cc1b798496b0af7ad93e49df0a54fbfb792ef11a32a3bf147b007ae7f47272fbcb71ef9fa1ccd14d856d89555c7c554d346702a352d32711f3089edc2a551acf39c448dbd4f801354150d07befd83fa9b03e2635934610f70bcf7dd1f8dfe786b83cff12669165b542dab8ca4c090c44a985c894f7bf7225178913f27c756889d22d5ae33c53907190ac02099018245b26e588244abf38424547b4d173dd36cd3f71a99c612cb2fa3e5919588608cab644bc918d3df8922058cfdb6fd594d2f8dced7aa0bd49e94dd017af34aae403d0e04acd10d76f64366163b8fdeaa572ee46c29c1235abe1a2470d0560539308ffc373dc7878f75d848ccd38ab3624d71c55a7c4877b71795415c4c582f2e702440123beac33b629661294cdf2e9defd3290446365f787c76428e246d6b5233b5e65593375fde6d4708cd151d13c3bc132769bbbe26dbb08adb6472d6a6ea728f570cc20097ccb01806"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"40e48edcb2836ca9f14f52c87db148a148ef94d0929c346bdd2318a779e2c239","proof":"ae8bf72523336d48111bc29704ba1207c473d15463a0d7782df8bcfdaba5742c3ed17afcfb4b7c054bae9c64f7e27e306d9259a4b8388404b221abdfcf613e04c441c98e8ce6c130859d097c0f23faaa32197f0623adb650ea9ca09ce4b8a50b3e108fd58f4307ba631828729aab18fd78fa985635f522f5a4ce25fa26cdbd4045dfad56f7c032cb2283eaab15bc57410d97d7c45719b9910c700121d2dd7f09c9274f1c576a168262cbc178788501dccab524fd92ffec5b5fd4299dc1f59b04e6074892b186c080b8342610a09ba0739d4e6751bb8f640ae564dbc1aca30d0064d48be140bd4204fff9cd54ca05adb85390bd12d7d83688786c3611230fca6f48e05dab8aafde4c1117d5ecb1fca4bddf65f8cd90d1f057ede9f186822cc4289edd435c25f204526c339712f648f09c703bd192d5fa8ebd3dd1c9529e50772db4191783c26dd5f755e351a25fdb1e74d7ba2b7f33e39185dba3668e4c328641985a869d828221a7176601874bc64608172639240747a75292126cac7e47be243c020f5515679161a01cb3f7d6bccad668b586ebd58c631e45f7e6d3f40bc52dac98b57c4abef0ab308f5a4d24bf42d01c0b9b46c03199796c60bc1173b88204fc9228199e73b219bf55e361b3fb557d3945b38e9fd671e2a324bcb9b54a851e52b85eb60ff6c6191678b9ecf667ae9a0f80b0c1dd51f4c636074f86b713b500fa86985052bf37652f7a2d6041cd79dc7be35c60a4f553169b5c58b78cec786312e74bb6212b901d67944a92759246d2c93a2dc01380d699a8661c6ee98b3945ecd8ba7fad70c70d2188b52fbfce4495d9e16bf22582200245a9aea0c3b568704eb74adc4a77f08e0c61deb59ae56647c9845d6d18a27386b61128c2b407140ef73b047fe1b2aaee8e0a645c54c45520217680b09abf06a8423affae50aac40c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9a45f1e13b256a4842c8a10e171f1df1144578f1eac16757ecca4d3340863b1f","proof":"f2ad133a3194f3a2f7e2cafe2c1311294d18d61e04bac9b63dc1696b0df20f390458ea029e70330aa5402852f208af4c342a2f67f804debac3ea3156fd814b1ece030a72da3c8344f16c1f5cf98f2b7d50beb4f3857b3f24af14148206c19d4094abdfde3f45b1d854488b15a95a72cfc8b6f9aadf8bdab61b882fc7a38508646f7441f3477688159ccd05997b9f5022c5e03f82d9e42e386cd75ef255ae0800ca7131934fc93d838dd5a88a773f010987b1425299964130b37bc831710a7205bbd16838723a74e15ca126d18be75c19a7462d972623190360bc15e61d3d31051aeb6ebca7ed7cde77133ed963fc9c838461f41c94e4e89640a738e096df0a119272fcc6cc18f67dfadd52297345a8f2e3420d6d2679bc64c6abdfbbad85e00900360836d7ffbd5a2ea0000b2a96088bd14eaa08b428cd5afc798bafdb48473bcea8210ad50c33445d1faa017a364de342692110d0dd25ef068ee3254fc7855c7c372bbed953aec2dcbc6792542ef9d6a3e2a2acf1eca673513a44fef9beba03cefa7f552f1506d8153f98153a664e4f11d38151d7adbffc2d3b0dd8ac3b7c6112bbe2286442edfe488011c10cb31b30d864e43dd4e9e93fd68100e3840aa157e00c797d62feead07cc75bc7359b4a698dc21775645a1985858d233ce9167c7cd83c6d0083310891fff96b52209219d36abfdd672c65f49ead0009dd31f8bb385846c0226ee726a532a6179c44aa62b975106b86494bc108d9ae994d4b10334a42fd0c129aaec3d1d5d8908e12fe58aaa7950b77727c2e733e69a1cdd697221b629b4bf6150da510b98f78a3bfb491255a5fd464815e36a38448baaec8bb427d900f1d0bf2b23c28662411217cdd2a9a2234d477ba6f17a4c2fb4bbfc5f33b0750452744c2dc87834be322ad9d6fd643da7495ccbdb150b10d0376e5ef4a7f06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2a356cccd583e64a78dce656389ac3a038ab49c8b8fea5105b0e182f4ed1d231","proof":"54a8e6027995e0febaefa7679d600015d64bc8d3d4b24ce67e6728d379fcd3496ac5fe83aa711d346fd2aa3637352861d0a2f54e1126e5e4a6b32eddea56703f70b7e9764195104dc27c30e6ba43b8219b0cfd6edc61576460d6264fac6cbb2c369aade43398af9d9be6c953f3502514660112d003997fc0757fe56eb69ae22239e693bd535d04f98ecdc0caa20233536c0b66bd31c14fadae13d5c4c83df108073e361e2d9a6b649defcdc9b5555a5ab9cf30949c0d73c5ff3c281d27f4680f5ba40ecfa8f73386810fa1a0ff81fa7d8c00a97494b87eb342ade12e6b85eb0f662a08fa969b55ab9825a05b7f4a2afa0b8fa4c917faec0488bcd92bf9ae45467ea2d0173dbef9fbb68e6ba1e19c36af2379641573446ffc1a0a181626a5812cf8b5a3dac63fa89bf9e2a6d11aa7f06f05dde5dc9a0cf757d2035ecf0d528b02903a34d363f5ada45da8a5f37d275176f7c26955b9b7c514c57db34e7c52b17d201552eaa59fe8cc484b7ff6224444196281d9329e6f7ab185c15c938585847284c45b5e57fdc61ec4e88ec6149d109b32da1fe3d1f64cbc6d53e779020e63767e09898aa7948988f55839aa598516397e358a57f016243caf3d1a02c9c93469faa77b53769de42a082f56cfd5da5822c9984e9e80f21e3ae2d7d7a8eb381901dca12b7651ad1346af79ff7626ef23a14911f54246b12aadf8328f4b6ec1584c543da8c5d1b4965436a5d21ba0bb3938169a7b7b621a65fc919204045e5a4324e609475ee9a7738b768901adf21e290e3c27e57a7f221a5bee2d0f6ae484c46dfafa94e9dca8fbde0df8d6974eca8c09b31b6eebadd70ae3f211ec9346fafd46e383f92a7dc7f336b8b1bf378222ab50d2348048d7183be6499049a4b2afe90cd8620f1861ddb782bf5edf3c233f2ab04f1d85051fd663fd2d1bc19235c0790f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4e14e8b84ac442bb4829824ac598aaccf96b939df7a13ca243f8f6cf3139b174","proof":"28ae565c4e26374fb3efb985c51a3f0a98e7281960cb32d6f3aac8d143397d21a689a20d6329047794d711f7897f40344515e6061f46b0196441d31e2adeab6414e38e6be2559759d2cac6e777ed2390dec442de27b98a3584e7fc6e90669975d831b447d331c17f332ab448bf0221d1514c4552f2f8b0171386b47bf8359e3aeb6067321f2be9aec15ae2891767a9f23c038a267b8e75f7748079fab9d53102c601a92b7c80510268284ff11b74ee9db61c4621f8c9c52143aaf8dee156e709d5d67ea8ac068c94526a3a18662f4937ec993453d79a43ad4721e39d2eaed60830884cc6c450c002c9565c7c682934538acd24fa46d289d83a306afcb6fc90024412ee403d5ecda4bb1faa0b85c2ac07f60de04f5aca7ab7c561597a7014397fb45cf6eafa0fc8a79610299f157f9100fc1d840bcbfaa664590d7dc2019832296860ced5d98a29ff6ee2a69531d02e6eedba4baa33b071ed6d8e62e2bd287139a80bfb4090465ba12c85d58c029ca1314d24e5b45561d64a850c18128af37836325dcd13d107fc0ca1c769486f1d6d59fe228aee89b9aa1ab06dc0882f5bdd6d5a82cb186c8f4322fe6ce013f819c71169aadbde0e2bacffed615b19460cbd4720672a00608ed5fe63a3c5985c3b219d4a93b95d73a0226b129b8f8c02841211989a286babbbc40439b8681f537119b81a17f66583c0c0214c746e83b942d349de61c961a6d5c309faa1b5f338d76685090e1b865d72ebae3df7e711798a996d1a98090fc87e4a804113c8f3b035fbcf785f1b80dd1c20d850a73b5d09f52d7e7cb9dc82ec1f95ca0c2f3e06851b846cd0b053a8ed1235fdd90487f91db88961e4fc67b9c01dfa984bb6def346b1492a1c1ac6c43ee02de8c10b397688448d04e4c1707bea524bd9635d7ed3b0624a5de62c4b9534f16f815e03afb028a74202"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"38fb6947127d6927ab6bb0ca04b13934200e4bc34de103e9e7ff1b123c699d7c","proof":"78c6c52d0d5ceb473be0ad6f6e787d7d491778de655ee881b716667186275309aa9b591560f0af330a84d73dc391d6f67e34ca5b96e4d21eda475bf5c5ddea4c7ccd6f0d8cf3169963dc37f2be800535011cf9da5d8fb6baf156b8e148c254310aa9243c78fd29e9e87df9436926cf6180fc3b1ba35920701a8aaf833e944d4d5bcda38880d6b0f8c604ad0c206df1334c726cde6f41cff94bd9c76670a82200eb75f51e8c078f7dc6d48e15adb8a5c2a3d6ccf9129940c7ba8cf9d205d9b6074dce3a580c06dfdca5ed23766502173786c571ff30f5102afbb485ff4ad77205c2a96e338cad7b79d2267107e54cd5bce6687358646cdc1f7030c9038375ea6b2274860c26b6fd35f64c1efed149c6ef55691773a80833e5e62a2f082b640101e414cd5eb5fef6ec588efa9d6278950dc17063ab54ad70f444a2316b36c4fd6c3848b4941c5149d614128f0d9dce6bededcc2936d48c063d9268439966f071038cbc962ae402de57ae233df53e0928cffb2125ba9a5b139f02d0afac3c15e431d28b74bd21fd5704deb5da1ced0ebdd9ef64dab717c1a48ea562986d5002c7440217c89225e2b54b6fc0bcf1344c7772264bc731aae4d0635a9c2ca6959a905230d94e845c4d19ea7d48a92e0f956f9141442f27a258f18014d72a7e1e82417be69d2358d995b9c46d3085516094b14d73ec4c21c1a6449fbd7f1a5bc3b4915b42f87483ba101efa3b81fb1821c1da43dbd5dceb84bb17738df52a86e69f1d4d66d95ef26537f8bba818e875e68c9fdf461d696a261366d1fb478af6fc6b4a466204483e7ca9e56f75510a05128d79195c6210744641a7f94904349159dce34b4709d410ac49e77a3cb28e573eab9e7b499779ef39b070612c9be752afce120fea11361f0d4e63583026a95e7fef45526fe1e5a977684fdee9df76573c52fb02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bac63cb3f2ae15f4e1be11570092e967578d9b4f861a35d0a3a9342f96ae550f","proof":"8c097ba7fbf128a463626ca344df473d9e1f9eb81dad0ca50a8f634ac3d2e91ddcb1fa80d35963517cf2b3ea78071e658b91fabc20d8fa305f34e8dce84c454dd092ef824e0dc27a6710184da9cb2eaacdffa4d3267fffc20545cb882dd99a0538169fec090331bd2502a783470bd7abdf5c5a7dd1effce416d0fc0936046e524639a1e75853e2e5bb747ae07f430f43d7cb52d149f2083a70ddb8c97df7b8016f39a519446ba4453b7877be55b1d3898d8deac3a213de21d9945adbaf6d650731df2844a0890c0bd41c37bd02de355112f9256d89c915fdba22be4ddb912b05d612464c0c73fc24c5a6306c64901d0f606def69ee8cba3ca08ef5675c33d47652ae13991984961436924aa3674a405d783a2a69b85347f6eceaff75d74f1341ce0ca71d19cf4dd2c433f98f4bec53509f706b6ae7cdc926735b0e31ab9a8a1db2f4d0c3257643c0866ca6b1ed04a25e6a6a27b0cc51bb9b020d4ba99f15381a02f674628580938882b6f78715b9ca3e896d00ed3784a2c9288bbcb7b77ac32b98804ab6a02f0842b494e365e018d3510887dfed1994aaa3ec8713da9955695f62d69c8c66be89c56d8eb799b8afc4df12b44a255c23417116cb811ac7fd6e72aefa2a183fffd44f6fa623d3d947cac655c7666b52097681a2d99a7357404a565421d363d1c549322bc8f8b13ffae979897082fd811231c8dbcf329e7755f669e610dbfe7523822f14cffe5549ea28b5e23f7fb05fcf3060ef935639045b063f0868941734d1f9e9f857d4228417889fcbd8661d9cde2bf884e31779ad603474b6ad30390eb53edd4179a713a54c4177ef7bb190d576b3fd79ea30865b87cb27d240b76c40ff12cb26844e21928d4a0648e132d1fa9e30248867e9d2f568cb0e3c3f20dd38bd4dc6e3ef38b8bae8cf1873c2e4ee55e04c4d79111682c9323e09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1201d2e4efd90552f325c915338e12eff04f3b5015ba0a376593b2ab43c90452","proof":"84df00a306faadd46f68174a9abc11fc26b9b3b10b1c76c64f6a1b1ea21b7564e231a6b69a58032c35a19646a3f38ca3cf8e9327808df85483058c08f3f98d73c20cea11fe132246d06fcb2bac8dbac4deedbd825b80ff4a563fafa7a2fcdc20ee8839b02390ea91dce6d0a0cf4458c7e16c73ee387fc3c58811a44e33680421117cd933320f36d4ca9875d43ff8e6cc63818857b192702dafe3614620d3cf04688bc1f30b9f030904bf9d58adea1c21721d37073fa17d2694ffc7eec9daa90816740cf7dca003e327606d4d02b78ce1a5e4b59d20a71a6ec928e3dae7bc4e0d300134fe975af55d93ee26f7a0d6cf302a3fb354335e71518e9aca05c3c9de25e65022f1247c01289af408b658e2a1152c503348082e6cc0d4a9598dab0d7278da98cf71ed149820b492083a631055b97bfcb2637e5aa9e8d4aee6876b388709f6a0c4454a3cb740b6aad3e372db7c7e3ac4879c94ffea71a98708e48835c84f4e7693160572b168e1b662921c100ed1c1767a89b9e796c002518ec76c559115cabb3c5ac17b623fa8f93e8cb38e49a864fc8d2fbe786c6671ae689d01983551f67ddf1dfd29a4b85a4422fb51e342a0343fb4d482905e0744b1515f2f76145e1a1ec84d07b12a6aa85bd26b1ef706d7491636e66c0f4c0729881be13cac536012b12183e271290f03d00994e97c31cd5e1fde7c51673bfbcbd2ed1d6c375667cc26879ebd14dcf0dbea2d219171cb76ba92edc5dafb0a1acc629084da02ea143e1bdbcef9bb2b8c1bc766efb7b964630bf05f3d4a42a3f749c34c90f3d32636126e4f7c95c42fad129d4f2d6e0dc2b0d09aae04fc0b7ca2e3d65a08f37876532eb09e77031b1df13c50d366d3c178661d8231e05f7b33ce6a6e1f27622019087b410d4d4b1d3f8b29efd6587478cceba4cb76174b1fcc35cb5f3e3b49e7ba0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f2576d4a50e3cea919fd626cf688997114ed6b728a92e209318d943fa4149072","proof":"7cdfbb22bb191578d555c53ddb511d3fa317d74173c8a270c3496a0eb0bee93d2ec763558d2169d7250598e38ce68744fcbcd22ec6551a3250aeef5b1ddc1f01a465b257c17639790288e8523901bf441f1cbaf5e902a43eabaf02891277c2414056bbf12b7aa984401523f19d6671862d3d701137fecb2578c334d421696a0f141fbef96a70b159d4cc1e0690fa4a06b74a698f285fc6519b55889f46ce2901308f5e4f47703b7b5c9339f0ca56fe46c61c5ece62c9a85d37d99cd7ddab8d01fdc3c314389f32561fe65e3ec4ea7ef5d8411124d26161bc60b1db8de72d08046625d721de53c2e38488645b6179e69845a4aa5070a478cce0e86224a6867118dc177a5a4618dd73cc0fa5e4f0422ae3d6b544f2c5c78892b6ecb69304f8d51a24eca7d1725da04396ec4eefe80bd588a441a7221e9e41be9e6ef4e8885afb6d8e494535600459413a54298184ac4c46b762e047b7f26d29719541f2340fa23660c6d85ac92883f283279825dc43e5bda2eb4801eda693238dc1a85e26ab355a60d3a0aa9b2b992fb4d906ebb367901c836925fb8ade4dbd0a1f10f1f732d77ca09b578a681df5a68c6928c14e2b3e141eebea6ae6fec341da0149f21c009563480e367c28474995118ec646ae6cd883b1f4322c2276bfd7d8ae1ad36b2e5679be470f02d46cee194f3265992bcabeb511bd118078525b0ef2b63bc5c3c23a620689afd7429a3722acd7aeb8c1a6204cfa64b7a36f9ce6e7595e6bc75420907046e31e61a168e9c0ef006dded7b0fa6cbd4e30ee2343d8ef7da268ea45e4612cf4a4efe14835f6d94889107ab135afd07621cea05c30d057230687e1bf98b34844092862a8ed69c5c9223abead88c66cf1d573d6ed8d77914fb096cad430760f675317a96032e57db56223a179b773f97efe5ddc3c19f04fbce1c61b86eafc06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b235f185297dbc2b5b48f034fa5a8b1a8bfcd78c37558eed009404661b48a25b","proof":"80b8d5565254bb932f3c443a338ab8d0b267bf108a682a12b07da95cdc54da5b562ff8a3614820faf42fa72a4068b0c258bb80db2386ee8a1df82748535d3a72ba2b1090805de8f035cc65e579e1e8b5d39cfe03f596668c90b8e73214eddc4c885c58669faf62e9a15472392e14a66a7e970dee8049509f97625eed6942b337103caa84595bce1902e5e1c15bde0f133425ca7961076e65ea80b70a69b04c0f0b59019a14b6d7d4073da41057b447a57752961e2b57ed956a5d3ac0996a200b6382dab4c6f8e219a111cf43a06fe9ac505927cd49010a08489ab5575fb6e100e40dc9e802fcccbf35a3ef982962ec6ffbbee5d06f1f54ee29021781a67f4c0152df61e4db0ea57f30e95ba7cd0e5457ea87482653aa9b360e3e3bee7b5c2e2b586548ad93c8b737e3e00a3eccfa140fd1fa25894557ee3f8ab8f72c608ffa55380fef183f9105f8722c96d1f7c1893506c4ae3c1bd825454cf8ca731820763de6d9605f0a851ccbf034162176a2e8c958a919c3b5f463918765f4a7c939c22df086804ee11b445d892deacbdf3193d01c16a5065c63006eef7843f095298508963e768c8234112fec649b441b4c740dd1c326e04840135562690a3423e5dc326cbdb9e8ebae4933d752bdd740d96854a83375c3b4751730663e7152ffc57b3d0656d2d1f7471acf9ebf9b14c2c4e40032531471d2a2b47646368b7abe123d5c3e465c5df23afc050a464a75e06bc8a08cf7d0efc90826a0d26f90a3a9098f3f425de56f53210db5782adb020b8c71151efa0eeee78aef2bf781db6e355fbf78e61706a768948deb92ede09506dd3b51a5140b358b68ebca5dcd81161064a67f679ef521b6676a7f8fc8f5c71001a657fbb6effbad7eb9be1156e7c1c861ba0a29569c8cf2ea625e0c1fcd8d3255e6c008358a1f3ab9b74a7c3dd2cf1df83f06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ecb1d594ae8cd87ac24dba721947d6205568aa2b1ff2e4cd505c37fee58ece36","proof":"f0621443e910732639c87ec6c621bc28e3c324f22a06bb806836261de9d3aa3a566c2f86369cd7bd024651fb8147a86a608afa735a669b0a2585c54732aade4d1a48303404849e13aa567866d465ba82928b6c7469a0b1aedf3c4720f218ec185e464cdfc0418e41f34dff970f74d84141ed8f3cc1d543ed9d287cc95bc32d69be30c7e090461143a85687564b10f2c1004ce8f1ecdec7e6664fa3f423b4ee029b46495da591f5615b8dd559ea01c31548b2fe724502e6a6048cecc68be6ea0ecef717eba8ac2b8a39f88e671d506a06e6f3e46f7677a0bbef6cf33c7ff45f0e7e7682adde5a44cd569cbb8e0d9970b9eb80bb86359135523fbae6342887542ede75102a4493a8b2955cfb6086138fa4f92ef9bfbbde7689c523ff4ce243041fdab220cc2e8c54810e2281d9a9adf105e320cb07432a9da3c509bb4b6aa5d64a861a9dbb15863e581785518df7e2aa998a936588417c52590137494c3e810c404e04e21b7505bea65852600ff082cf3229f81215bf9353869db97033f460a94380b5a1e4f35e1b9bec47a5147b8007443c7e9443792a1ec76a1b04385b2b8e1290f843a369d2d224a81ca60f963eab6f3e54541c10a4f498674e718b955ba27536ffdebc0f5b5238a2a8104647988694e187948a5d58c087bddd60a1e0eca11786b3ced425e29e93332c4fa9b88fcd718b8aa5173e0019ed595731001545273a8a0250db8d064ea1a6ff75ca4aeb35667c6120963fd3d07b3d6fd8c201e1793a5e57c0a9dd4c50c0713be30d4d49c91e5a5e17501cdd4ef976bda874c2bde0756201200bf5ed76e2055ba7d5118bc445355605aefea8c2b63f735ae65d94d75c766d1efda6f160762b94568a79bc518ca0078f81e8bb67ace2432870ad827e00cca4b6d7163d908074d067cdee5b02b5d27191cdeaaf5e5f74324b6cc0de8c05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d4a9825ae537e74f6d382f2991b30e68b69751138d494401897b728d98448014","proof":"16bf27c34ba507d327784613a195ba58db32bb4f5433e9a9526dcfb85464410ec6407e4d6d078034deaef845d6e043c716ed509b44597507be7668f1bb7cc14e44c988ebaece137c65e8f421d93f6486f0e80b8632428d76e677ec06bdea36699e454bd18013936c7e881b98e471eda8b2532bf8f58fb2a394da876b45afe06deb4a52129e8ba6890fb0004d385b00b5d3c7e99e4efca17e3cccaa1758ffc901d4c6a2efd5386bc3598735cf6688d22b4c9db33ddb5bcef99d4ea1eaca35040519b522960e51774c04ed9f4431767a5299defccf86430d412882f6675ecbd007aecdabfce1b0da5984b2876b111490a728d4bba3a11a2c993c52e7e6100407364ad59acabc3ba0c110561601e30631b44c2100dbadb7e0f1abc3ed3fcdb31c1dd440590eb385b6cb9f55a4911b93deac086aceea4a0466a5fae671e4845901629ea3b0347ac61ed66dd0acddfaffc62217eabe89946bb3d51993e4417cdd553a7067fedb2ccb239e2346e280164cbe9f4f9b23c9973a32993e2de10e0c121d581a9d8a520d3200c61cfe3b9af0852c9d3e7c78610dd47244af0fc668ae9a7b3ee0a6024b807f3313e639b18398990536b221f96030d024e6daccc3c2da5d8d6a6c0c84fbfce7c1f7173589ebec082e383facdb65928e689dbfefea05af98205b88a0968af55366bf7f7614ae6f438fe747e79033897a7a6cb1b5bcf96e366c3a0451f35030443f6aa21f9f5a4140478a52b2c0cea9f75baf991587c73d4a0f0dbedc45363f333cf226ce506dbc8c576f6a8390096b7b8e8f9ef40ec5f047ba7ce0ce781e10b7280240b64bd40f52f7c27e1e763272419be8a6f9f531674c5770d37a1de8fbdc63d0817b7bdb1097291ce345194f1f1fee1ec4dfe8e8c106ae0b64b30f0be949552dab7275b9fd1d8a3a0f880a4dd04711bf4113cbf159fafa03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dae625a61f7dc57bfb82eae9c12b9fc1f265a42eb5ff9c65bfe6e836a403e71a","proof":"f873a69ed26a785410446288dfa174339bf2957b9aedc84eabaad11ab72722211cf5e85d0b741c21a409e386396c16a268e2995dd705d8d247ca4c48a2207662c0dd9b8c3da35c6638193c06f90d2e5a5db13eefc989036101317870e2dd1812a8f1b803feef2f703b8c4c2aa4d83db552358232f802b9faa0fe120c9a57a37fac3858e1c055e946d3991fb3c01827688461e61349a467063b7cfd58fc8900017ed5bfef2e6b829c9275bc92d57c52df6edaa563a89b7f14952c6867d756aa0f6b62f36cca140a6694b4951ba1143e0e0fe0b528392869a7a8dc51ccf5a830060e1a50c81ee91a90f9bf0bdef263f308d9a7c01b57bc8ec98d0dea46001dc33df664093bc8baefad6085ec59f27b55893f4cb1bc3146a8f73e57f9e88036c32db87e175e3db59c7b2013bd5d6763f26ab089dd296b8a0668ea79bbba8a45b153e24d33b7e46475cb814ab7c9e4d31ec9857be5f1a60207e339702eda39f9e0437623a8efb8723e78f5e2d6fec304543269265c4c0a0e931df230901b9287165fcade25b0b96c24ce84a1886782871528822dd427e948de614e3feacaed792d50ea4c1e9e4ddc3069d21b3925a291543a63dea4aafb3656ed618d6ab882ab610e0457f1a092d3bd95396c5875ffbc917f09a4dd375749eed0923389a781918b7fda96f57769b3c2ff0fad9505b98198cae0e4227407b51473c408aea22ed7e9244a5c6283d7aa8c45d42e0936245993fec05b386f567f250e8bbc4d1944a0076fbc9ebddecfdb11a6a7bbb59345c377384c97ffecdf17b1b5d18f1e613ae730346a76b7f83a1fa5069bcb8f4e0548d3acdcd01c9fdb8941673dbed5fda751877dcdbcbaa1358f85a7dc90564d353026feb1bf9c7c1d413296522293a7c4de8b087e84aa9f449d8a0fadfffa16845658f3d4144c420cbf929f37ea7d03a87d1704"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c0a392b570727bea8bd4036400ce9613c0ec063891bdde6d3088a218ba1e210c","proof":"4c4ab36f07381206e1b939cea75c85d37fb34796a96d757b7b99655ccc12a170e2fac2a5ec2cc6fe79f44251f4ce38e7632cea3c0c3d557874029beb8c1c265ea06771664be2dba44b2089363083e78331908507b79903f1147c3a9b1cf00b79307fe6cf64343f35aaaa09409e30685e9618007353c0e9a759c2d42ba6612e5a56557ab24d5546fc5a4756cce71e17e14f99a54e5e2c1cba03c21ca3b5c94f0bc54421a08fae2da891a26639d31f38509d8d96a66dd5b77680c943ebc9470f0884464baabd738701d2ec2e75f0605a28837bd47928cc86170f2bce990dab1402babde52e14812b298ad97f9580a6fb5d1b1d247f51c313cee00d5749f43501471a4a320b0ad0913b8ea900db9eaf34398b4f9e2dfc98b5f03d20df6b55aee62028320d7b8f85d636ec7915395284a184606a9786bdcd0649d2c383dc8064720bfe4973e9aab6734d8dea483830963fd6d57c37dbec54227ec5fde2b9e0886e4d82fb3972bc7702752aff5421e146a4fbd499c02cc24b4e513a2e9ef0f795ef2a1846025ef6028558541def0422847d234584ae10cbce1d885956c7b1aa8a0966dc4491f7f85aea7530eb1025c4eaaab0d2cb3099b5e60e03e24274cf013a940fec7d1dedae8d9725d6bd4ef9f2d5dba62a1d7554d07fe85bc1a50c9add3e1a6f46a4d6d91944cc1624ba63a88b59831cb453791a65dfd1cac8c6f906eb93605306139fba5c2292b1258ba9c92fa2296ee4abe137f9ca6a8ee8cb909b4496b94770af1889b10a92527fa9a68ae868dfa61144f73bdb99e9df06929a646d73526f6e1efc542d6148ca9b4bc916f582d29aa5164eaf341029d1d91159ab06ec547b2d2d9ff42c9f519fd5012fb8ea51be44cc53a4d876146d33617eeea415502a0c00ffd6d2d759a39323580212f37cc5b53e9358e9dc0daa7ecb2230f245ae1504"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e04c3cf8b111bdb2bb3a5291ef936166899e54808703a8956ba7907bdc35f849","proof":"0234c909076a1fe66201f71422c63f08f83f449b3c94cd2c039ad32e3f645f40867de34c64e691b76e053b541b6cc8e2971c2221a9486a7c73b735c4ac4f8a551a68e7e8d29bade37a7a92db2baa0b7ea82ee9f2524e7126b4e82ae06d07d73c44a30a25d47628779dcf29544db97370650be636fe26a983fd4784b012fbcb6dc36671376f82b61522a538008a6303e1521c29b9829ebc9856e01382a7fab606e0fb6d12b7c3725b37e9b54c53f36a4673a867e2a53e0beaa7020fe3a2195608867c8f5e927b339d7ee604df7f2621c4815f99f7eb68643e21a1f4653d2df40a584b1329c79eafbda51fea231025776cf4c6d25b61b8e6b12db0ba1331b4a2138477cad83198a4ce6b81e3df53be9c89bb2e077d646871ba2a689122bc6a0a3dd6c87d6aec19e51aebc15c62677307b1cce02ef1633f58327a4dc4aec295ab35fa4e5a68082058d5248224b47c21f96db6242736b8bfcad57b4d74b8f35e3c07aa8aa364c515ae37e86dec2475b93000495dc4d2116fa84da5331277b86c094e603e586e0c082081aeaa2693d3736d33e38e4a4a28e257f536538f8b729c057c3e0c3d4113813f44b94a7d32582ddfb93278544acb76a1da0813c962e9c5401762bd8086fd56dd8c4a2ab07cf189ecb18f33844bbd9ea4b5d9a42e9828f555494e0e39d521f2d69634bb55a9b74f93bf25a6a86e96d9c484a4fa381f0d5fdd159860582a919fd164160c1d637b0578a9d1af629c4a3d9ba7d7b51b2285bfe67a6277d4f5ca80ff646f211918c1e8e35fcdcf445a5194975cf0db1eaff41b1d0fdcb87032af2396a525a44dc305a741881370a9a9f4fc27277bdc6478ad418d6e14e6e87645957397a5ea4f4ea1d1dc5ac96133e288a2f8cde38028a5d50e1805e691a1d3bf1e0f7ec8441feb63947a30f1827a9a62dfd61122275e5e1483d204"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b0a85fef06dd3476859b4f0f79275267ea50f1950f0470173e0023141694374d","proof":"ac156a4728ac8343a5d6ebd67d09e7519e7804d301b20701c305e6db82bfcc66d231b202bba0a05ca59007127553e68ec5e38fc0f9b74c0a10e623520e865d767406a81d1b654b418fe269058cbb85c3875aa2be8552368833eeea0ff7cd86474e21d982009747873d139957e879791a95787d92edeabf6a7d7b5cc78df245350425a9730b95977adad329d911020122de46d9922bc1ddd7b3dd3a8790cf5d008f346fa7e39e43f9bb2e1e05bb8c512f96d2406d4ccf497ac11b692b23f8670a0285cd222a27f655f801c8faf494b2036b6988c14b804cbab5004dbe41dc4f00a4f52b8f94253fede492eae4843291cbe227c5a916dca042df00f05acb5bc44fa2731af4cebcc532eb90a6aab748b286c20606072e3be2e5b67944b4b163d154fcdd276da985af1a96fa5a6a475d625d8ec22078369e58565b505f906dfea84612a10a563973a746126d572b0abed826318e3775d33faffeaf09a34baacde74d50c763f3caddebc413ef092472216cc94bd111e6efffb288b2bec5ff35effa7dd4d0bf64b7ff2ea94496a8f1dfdb3065bd9eec7397dd41489e4c44306edd0f32181d6dbfbf01da576c4b67ebb0d9a3e92f72597ba327f12237b098210ac4c84f8e54448aa259d71205518c20806b641a13ddec8134dbecefa3c88f191073a22dc68e9fb0513b111a46b44c00583ca9ec35d458c1b096d868dd369281dfc0ae382e9ebba09cc3e7cec16acb0376cf283b60f6bc6955be2c632d9c978edd6eb94efcc3c162c060af759cb3ece2c324e34bca53acf4d38926b0a9b3f8fc17c81d59e8c4faee6aebd57af2040d490a0f33095211c197bc6b54394bf08f7531885706a149d16dfe5883c919eac5aa0905b815dd654b36a6be73207bf161a7e6a89f0c182878c7ba92977afc1605029741a123e779e615d0b02870d9c466cd069cdf06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"56d64c5b08e5c36d3c0938a1b39e01aa2afbd4df70cb911d7235a40c33488e7d","proof":"506ef0b18030c8639c71375eba56c29bb159e564abd2b443c22b7f8a7bdd6e75b4710d9a0e4b675dccdd2939f47da39604713c91c1907ffc4efc24c123511773faa4c4122655343e06049ea870efd4fd496c1b272d53749ed615e2b8e8c43116e898fd96239d602c88d263deca134888dae3f17236eac668e271feca46e69439ae269d6f1f8fd61efb4de48459aa149708a6bf9dafb71943104b03bc623ebc00ba7dd294b5d01ef79df3b92878b8d5f0dbb90898186a01dd9ace21684f4a910ab089d5ca81a10ec2d91fb227ad97931f478101e530d79797e89a08b4e87fe00012f2e99c8445992f63017d90de7bcf18f6dc53ba079a7a23fa4716e08f93254cacfb8a6d286782c231ec06c30f74aa70681cf8f7b8c32e29b1fbb10d2143ee6fd6569157a7290e60c66553760f9d071ec781c7d7bfee556c0e1f0151f7026b5dc0e64beac6aab95aa5c2bca0df17d6fd6b64e1abbfa31c92de5d6c77ae2de228ca24f2e9a16322788f7992f64d3635038bc86a04788a1e5d566e16eeb583fe75fac9cacf6726fd9dab3241034fffe0cce64c8e73c20994bb1906f7cd47f30a36101c892832df0a57f404e25e19ec9b37f23ca8231cb5d4c0e658e437083d770800a6d80d9fdc8e6bd7fcd070dbb713cd625c7fffd937156a4c62e577cf83176ca2d4f800b5d6270b4f8885671f11977c85818b8fd64db32391726d7e0488f723908b9a224a159fca9c9002e4782797972a9800ca1761d8c617e817654f1fb250fa87e70c7b2b29285f40ce7235bcc26d008b7c169404bb84de3987da0b50536a3cfde630113a0975c5ee332aa6ad5e4ad092170f1c3f34c7fc130d82b8727d4f5b9a9d06adb10bf02e4980683784e2d93cabd652716e04ecc7438f29a4367e0c5c5de9e76c9dea6c08b23094ee13ad06dc9d8435232a99985852459dd1d7130c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bc4fbc48cebb1887306df67befa54ae05aabcbe840d1f591b34941c18685687c","proof":"f0f818b7e1a83d10ca83b2e677c0ecb55337d41474488973b0084d178878571790beca362f0c88b282462f4325889cdf5c6d06f6c9119ac4eb2b667086117e570897296be6c2d602241e5d92471b00baf60813eaba0d2000f884adfca9ff1d7eb440156db60d95a15f6609290ef116148a1ffa3c1a1caf05acfcf88eda172072f4fe1ad6a8d5af419d7ea31ec3c2e4d7d51206dd233f89e4b5881ea2eae14d0b3b869524105a3b5c72453f695a02bc18c0ee4af403f9c428a918afc1852bd20cd206cb33eeec44708d43703a39a94f68b1d95f8430dcdbd72e24e46c5b3f8a0e4c8c7662d16d5bcdae6b4948f2cfb40d7f9c696bec63186e08ef6c40a05aa5497a3409cc7c83d71b671429cf4281eb3eef7ffaa02efec3bf356d81e94892e92c8cc17eeb74125ea9ab6b4a1b5d98c8f7358cd8e82df6ec73eb77973953c9ad1ea86dc27827cee23e87b5f65320f6cf82dc935796623b996563602f6e0473ec3888da50c62673ab8810122a167a2c17fa5eb4b148af80a1d6e22e4709be30586ab486a58483d847fd2271593c5d4c8c2f67267337db11c5762597e89a0a2db8738c0e74029d0245aa6c15f827030825c3753e38ae949f3120fda34dfd01a15a13a2ad17ce2053ae7520073626834d1258816a143b24dd5bdd73214e9beeebba5114524786d2d484d42480f417a394bbf4bd2278681d988fb9b9446560a605fc231c00e44b0b535131d3cb92687333411788fee7eb2a3078edad81cd6ece947551e236d9068f5837818c0165ac273663754494717c5d9ce5ac945c2dcb82cb8a0620d2de4ef676181475efd66aca727ca3187ade51e69c60ffe37c217770b36e2c12a1ecf23ef0ff74dc733796a3c7f939265042bc3b47f68827ced3e8eba9db0e322a361ad40054465c3f2a8130ff4ae2d3bc8a4f5ec3ef03a61fde6c961b1b0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c8f06c2e9aca94f9a265597daf31dc3417ae9cdd43fc03c276162a1a2dd8d346","proof":"d69dae93418aaf80fd72d7ca4686c9909b496efbdd0238330e4f2c8e24ed096aceca18de87e6eb239cbdac9def9a7d4505d4b60f6fba5b8dcc95dcb7caf9886cf2d7dc4736bdc07edfc71b53eda1571e056d5be45cd7c9c9375fe02193946d75d6ebe6cb7e53ece9ba7dbdba059e0a1cfbfb9a42c01557e939707984514e4874698fc362ab74cb45c4a6f2209a9c3e9e9514134265049d2f4f063a170d788505734462f860145110ebacfcb4a32df37f700f092dc5b94bde19a44ab98a1a300292059ffed55350170548aa4224e7dcd4f0e4811655af28254278353611540408cc7f459e038a9dae51ae867fa072cb1f2ec3a28645e0978cf22e39a359115449ce4166528cdd588def8e3a48326b2b519c2b5536c904d85ab59a76bd385bed35b8bb3a6349c1b348ee4b4efe24d51304d7354c7f5cf58bf80580d91b0987d040789f85e25384e4106bf5e8ba9f857f6305d85f247de97cdef0bc33e41b21ce179ad7d89382068a2cc4e203cca504fe41e441ba2058f6b72662c07e73a7bdab618461081d0ba9f56a869b44db3ca0e12be1834742824462d1ab05096dc919b7340e82d2dd66136344792ee099b19965d6927ae3c73bd7566ec793b9972547247ed06f8cd101e6017d15669275e913a0767ea8ba21123f59bbba67320946c87f2abee8ddeb5352bb120124f3cb4b2406eaff98f745f6661a7910d6e5acc3ff9c29f2e6a2c9c4d668ee39bec0dfdd3cfbbd1db0a15e24d7eff902334e3a0b3b02242c9b59292d34f0f10c69d7d371066ba37eeac2675beb1a7b8f4319293d04ac1d94d93e015cdc3bb57b1deac26f5989788330179d2b51ef8b6a6f73b848e10a08d0800c27358cf29107e4d104b4bd3a58ea46567fece1d54704ea9b569fcada03f4c4d29f6cb6569c3884fea6cf84461792976f0c27a244cf3436e6b7c3ea7a09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4469717b7fd60544426549952192c1364d4fbfaf97ef3f12359ca5f9de28727a","proof":"42a50ab06b0a1418ed213093fd1b7e5f5312562cbb7017c2ae1d4cdb0cdb83584e6db7c28926c7272ec79c6aa2a3f109df65925e1add0f0003340892a4049b0fe604ee1d136427e183d66223c727198bdef3d2c978a1224f24d53198ce34a73c8e97770a3cab9835e2f34b3584243802b377be241fea463c7cce82aa682b4d113ac7e12495deefabc1a15c4e7022dac2de48422aca6ed7ddabb24a9f08836b0b643faa6eff077c53cd652bdb2a9368347f9ac7572a470e0317e73a0273c3fe00124f8707dc322bfb3b7a10f895d0528dea96f6a4c3b79cb367618ea472657c0f7250ab4932d84ace9dc5571c97e53afa4ba20f3445b6eeb1788f9844f0c9d2370470be9fcf5de1e2869402f42cab6c7fb71ee99cde117aa73c03019a1204e829ca7f5fae3deca3322686699ba187861d559890472ebd261247b23eec21071453f6833caf55b7e98027ec27366b41ddf73832ae9cf7089a1a9826ad1d2c51e12d0009ebf7cf6596e5adc4e2234fcfe88578c18abe38fd02a7294f7ce6487bd96760aa9d168d1d775caea95dec665cf9c20ff2ec80f3f799550b6b8862adf7a244e09e575ca2588264a8338e259f8e5ad3b0d076e3f47f5634e2ad3c6a9905537d249e9176c22cb7833dfe548acb4405016984464fb9350257eae1d9d85c50342ff25f94e2b553e3932409553e130a6d642d50a1822e9962604e5b342628430003a23c766ccb43b0b26d86f540680e9c224e6af83f6a94de8c322ed1fa1c2d3553cc405ab2532df34ff1b28db6afe0b76fd833fbc4466476f2ac3d422bbda90e0a1abf5e9bca837519b6d3caf2ad8331c0d290d3c29c2bf27164799a81c5ba36757a56757c3c52b8b08e606c4b552f72c24b03f53e601435461f6e734c7896b008c2175749342c41c4bdddb22337d6e1d040eb313ce354b70224312e5110d31009"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"62dcbdacc8e045d151acc93fd363eeaf6660a94ce7c45a6b1de2847b8191bc68","proof":"9c72a02cdb21e9b81d4542e35e48b32697eb73e1b6b28ffc767a331df6050c7e609a37cfbbc535a0700c83c9c48432ef135d250b01e6a4f5881e497ca5611c0f729ceed45760daadf7c06b4099cb1935ee038391c53526baf1bba93b12c9ef072c971eff4effbed90a3e4bb8297c07a1a5b1c773f6d22d2f99e98fa08d0ed120f6dbbde3eccb2135baa31604ee5dd0d46fd6d377d32474d8b190dacebc50c20698b31b9320ad5700e51067452bf51c475ad95a3bbf265c454695177c08eaee0513525f96b3d49a66d7f8577fad32bf7d58279c05d69ae46af75c7114bd740200ded578d3960fb625ff04093cbfcb3072465294a7f5bfd462eef76af417e97e3af2998bd2f68d4da0ddd0b99551c66b4c9c870341c68ac396f6688b2a3254e81b1c3f7cc04d5e9054b8ff21dd6f1a9d290e62b545adc238b45f7223d77d86641f58419207dae41a7a61d516d2c75bd942ec117040a24ab21bf817fc8e89b4da184624c436078c468e2570f7bb31c5e175bf959ff50066884041de866e98372d0dd658db989fe97bb2b86fe43f12f5cd71c5d95489ed7f5bf7997adbd7ecada17908142feb1a17d2ab7c93dc320a94152ba145c2f7bdb8c064cd029dcb06ab5646b49f5e5b232b1f30817f41b7deec4632ee75f2e30c4341402e63619df894fd242a9bd62ba367e8f809b2a3fc4b8678a9f3075c225dfa0a496135ddff53275b15aa1c559459f2c5839f7bb9c63300b71f236ef68d487e216d76f29d770952f03fe0172df08752c87bd043784e5fb3e47991347c6167742ab95cc34e02e02f6f7052d1014768c70bca3f0f93fab0a19439e18146002c9f894c15f21fe06b670f2975475114df969bae74196c641e1f6af3c892c707b14976fab9cada418788da02a0a61848248c928a1c99219ef39862d9cd6d5fb08ff8484f7442fcf101b01209"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"32f0f2e3c78cd6a717386fa5fac72a99687a103675c46d7117539269bf1e7e2e","proof":"7895109cf5bb24e2818c03e068f33284d580cba04e7487ed95b99a709b04412ed64fd4c00c9112c5c661a67966336711007d85fa36287df8a5a347319562652bfe904f0f76844d97ac9725863ec170af0a2372a9b72c9e0921366e6ba35ad629a848f5a2ad8379ece3fba988551526813a1e0a67966fdbe363de97276f86a502487a27d67418da17fd2ee19df50d72fbad703d1694d679b6ad559e318d4e900ff00aee55c73061a3eba09b2e931335e599b8216090877bd5bf03aba6375c0c042b32fc3c0cb686895bf51bd7b665549739f451c012e8066744a1a9b9e7b3ed085e7da939e477d1175967aea0c1fd22116168a500f94e95e030243fd4224e7657d884a1a6a320ed86134d5dc0dbd1a806bdf63e6b50b683413659412722f51e618c3c2b7fef47272d3d079d993786d35f50a5781edab6f15d1377d4d4cc941c7400ddd0b210180d63f9ed5795badc15a3856cd33758515422d8fcbbf53181227f6e58179efe3c42eb75789b1030f8a681412ba62a20edd8bd757b51ae5aaf2e24c6924b6a23171502313dc90bde9c52603c5c45df8f091a6b94fac9be7329f85ee6e4bc6601200a4d1e1dd47f10fc037fe1c41f4dff2a9f09461e08336891b404caee8e59c82107db71479b5c93cd3e2f062c4e385eef4dc7ee6331433293354d04011b18939b92a3b87150df1473560ec54b96b8d5717d4fb5e43d6bb51dff7146f9174b970d786f7bbe85de555d5f944f8ac617dd45ae36f3f3450f1101b408e0d73ea358e87425e98660ac4dbb5375e7be08685ed7d72f27b09be5a72550601c7e904ef617d531e2711ce1e33e73b7439579275c1faf4fe48d91936ae1935f9166d081d341572c7ec0e38c35258ee28f85caf7515b3fe92620ea65d8f563060843ed89204d872d8b04503ea4c78cf329dc03b98fcd0d9f533e61dac6b13706"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7c25befcb9361924d0a2d085570d410990d2b534454a85a916aba2582a9c682c","proof":"b03495f4fc8eb655f0a4381ce7b619a1d7564b8cb13ac208cd2b45bc2a829466a8939229bf19a39f8feb6e3f871e8a147aa4585fb2f80b92c0d9e7442ca98d7e6e978859b98e060bc9242665cf4dc75a2a9288564f227be3bcb7110f64aa7026a8100edf81f9a9338d5a226896b8ffd014f683a0da354ef4582d92f2c20462733c322d6fb45122fdd66ddcda653dd21744ae382e5aa7e3f5b2bf62b5eebd920d584484766feecbe1014c9795afa7fd254cd30257e0808e50e46efb6e4ee74d0ff398ebf0059074fe57fccd3a79b020754a83fad658d8fdd6b0d380d6bea42b0bc4b4f6e3efba6e78c388594dc35269ad42040d2b5b465a37184a483d3eff317ee05064c37b439a7c4c74cc60d87e677872255f67f22ded99c8d4eaa8e4244903e641e5077105980c1a919c7cd9f4cca115d70d272c28e2587d5b3f81514bc611963428ed0163541c89f05d6457abc53adc270105da1dca41b3e8c7ec7ca2cc19506cfcc4397218c74a1273d297ad3cc08ca8ca94d51e3a93d09cdb9acba53219602f662adcb501760c2bc7eb700c88813bd20b5abad8b409305d6433ede5695cba45977dc5fece55988fadbd1a317a20286bdd3aeb9743ec117c962f551be75cae3ee14940b22adf25288bdbf6b682cec39bb575bfeb5470331679bdf79d0f7a1606723ea303b8946ce18f05651da63dde8869df5d9a380bea24c74f239b07575646ef1896ab6859fefdfc5249d9ce06b822d3edda3131b7044ee8a6b3a93c735e04c1b210d69d7a7abc46122c7962125b0ce1d54b09c3c0d2abc0522a38ca2594eb58ca0e33494d1d686500e6f6728ef9d37ddbc4e32d346aab01307a30d3060d9e80af32085339c64c6739804cb71a336b240c3fcf571e44e2c35b0aba6e0f96339675799a92c80818cc708f354a4b6e7b41fe8261a6f3cba8af3ce3e17501"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5a07b3ac0aed6ab429bb1709317bd499e9bfab973d42a77f41ee13edfe3fef1c","proof":"a8cead42b87b6443144da9c9450635e78aeee50d691c15474e5e87866f5d121530391709b31f628f9a69f6e4548e31a78e503254ad1c82caab4fe16110ca89471e2b4942b1068bcda3e04f8bef651d282f697ccdbf863e5aba5dfc1e1b9028223265d19feaa8ba02dd27365112ca2fb382fdd9e7a72f9ac7b6c7609f890b5538feffb1b765b7f0aa1a43cb89aaecbfd20ed7359292a964d31a3b2ef2c4801b09dd3bc5cd51c8a0e05d6652d3dd63daa3a09ca92b0a9fec96908ed5490cca8d0a8263ded4d550d3b7e2d40a608ef4925150b0a5442516405ec258231c0b311808a8edb7254fb78491354587d498bf33ef89b6711ba1a00479ada3fcd7d0af59284cfcb1f0989afd17a15b04a041f7899f8686c72a2e3f2b166398868fd8e14f1cc09785825aeae12120a5729c51218cf142bc7bd2f15ef5a20ea301ec9c702338e6ff57c2a328cb2c64b5e185bd93c991db806242ff00985974b97e56da9e872a7091e8d524e9f6a3c596c14e95d41cb9c62cf2002fe95332ef8e1dc654ac840882ec727f0004be522d793b188b7c414927856ee0878b7710202154e9423cf748fe22c2e9079b748df548d67eeb7a3913dd049b724ea9f0d58538038b6eac7c0f2c8f8e08ca53bd539970300b486e389578dbb32e8c1c8dcba5a4e74861cf8830ba1dc977ea1daafba7d080880bc4176b759933b09072e07b3d28f6af1e129f70ca4ac5c12e789817b0d27eb1de4c1c196d0f0aa81dd0fb5a0e34d0d81e8b8d26e46f2a0d0d5fb07d78ff009bd0e423a7a26f31a5b8a073b14f5372a0c84ec4531c3362b45e1eb2f30b11d373aa71c2a871d3e33338c36159018ee71a102ce842857dd7819383535bb3b8d09035453ce84601accfbc5b50e0793b1ee9760072032419ec980b0447938fa5ebfab6eb73f67f88faabec2b2dd20ebedb8eadbd7906"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"60d40cd6e6f8c3576b4692e0eae108ab99f46546c5f9b9b44975cacec561d70a","proof":"1eb3729650f4642a5307af5b3a872e2605b41b51b798900e2e73658d5f8b7404bed788ebfdd769ffcdd04fa2db9f369ea4797f0b11bd3fbaec9e7d4103193b14e86bc98e4d210ab0b80a592bb31e884bfc5023bece5c43abfb59e5ed7da1f467a023b96db4827c6314cf3d6f542e2059644c9ef576853f6eb39938102a62b332ef8cf1158d270c5d58a8e9153b84fbc7d9d5813b9f9cfdc2e7de06c783ebb70d7dfd8c5141f49fdf3cc0c0252780c4c069626f21b074d9b65bfb6a74994b3a0c51d6487ff9ebd7a1c0b3c2365f1d5f04b759d16cb3b61a501ef85b44701fb60502e138187d1d086e050174d91e64e85c111307e18e2f705d70d0da46469fb64c80fda0e07f8048917a760088b7e8afcc13ffa2866cf8244a0cd2be9a5743597dd8fc4cbfd3cda325b280fb8ca35a5feb4aa28ab7a407b6d8d5a064b5ea112d63e4fbfe8f03163400796e94e7f7e15bb8a2f270bac3207f5a91e36ec04787da1cb6a3cb2d70614e8edc0daa1f2456849099164418b50ed1204ce1756a2b981276f6a5c4885989d05e9086043a0d2d192d99b216301f3831f40a17fbf6b032fe2ee67c0f5590acdd9f1141e32f14449b607e8cdcbe2bef8bc85ba2017e93cd07308a8a85f16ee5bf84a19330dafc02d0120300579c3811f3294df3a5c5633f39188e2613c971a3cbc7b04e07945ed1f01cab8c4335a858141eda765e1e3adced5dda79b9fe0e28c94c048c636a7818061229d2c88d4e029000e69d993a7e0b91638090cd9bad4b1cb92c536cbaa0acdcd0ef332a99097dc5faaec2519ffbf4706c9cdbdef220ec7f06efca34732ea76e9a1c3177868dd4310e0c71d1ef02e2f96258aef0de8e752c0cb8a5388431e10526babf26f2286f9709fa9d70c3ebb3790706c1f74299c35ea031c61043c042b04c0dd2f53ffa0cd5907c47fa81636e270a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b4d68957332140a498316d91f604f1eb8d4dfa28d00e6d40c173dd0e26591f29","proof":"9c3b1aaf293b7d5e052a75324d90cb4a6ff48fa3b1a215929d2dc7c5b1fd8c383edb7d371e134cbf51b3fa3b22e280ee844cd57b6e82bb5b4b2c5eb0a4bd9914c6b03d477c61cca709c92d550519751d980cd3aaf6e528375863d11c6d02020d3041775a51af8270e9767c84a3356b0027b76df640afc3edfc7b255b8221e607916c8e53303bee4c672734ee8f4362f3714d4d59a32a029a7942bd0a3044f403f921ad56f30e302fc0214cbe89edef7d3c43ff231aa8fc029935902a8456a90a1a9aa8b902627612a5897e78f34b67cf0f78218127c8d48a5dad93995ebb9c0e524d9f9ae397aa75ecb09bbbfeca4c533ac22a2ef97c0cc4fe5c4e64b1b14b007805d10263a6bf8c69c7129e687780aa9b9343bc4615e414b23aae5291141d24604c5f2d2239ea8bb06137a2120d9c37b0d36a45a8f1cd9ed56a685d3cec9d6376f7e7e01d34456847afb11fc7b4f2463f552f74d9e18df4fb7bb6e17f867603ee8ef6a449c5c3ecaa9f8a95bdb12b3a3ec48974f340aa2e80ff82f603520971f8896591ed7dadfa04f441942a5745f9ac508f24536477f1878c72927a31b8498a4fa8613d2c4c52731d458fbe9fa1cb8f999ecd072bcd00a8d5733950e4402c6202fba77f57c37c9d90f4c0693f41dd5a451c3ba9e3b6e49ddc8bc0cbc4b61b9ef58add9c30782b59db8ff0055a8a131ae1667b22eb3a9d3449a2d1ae9ffe5182027feb7970ca34895ac34470eb14f3ecd35647f93c7b536277ce0271733d4baafe6fc95aeac4182d6e37eaa2c64300d5ceb2c0a3b431fe51ae60bd64fcb902081c596a00cb8f228b660f14dc65de69a19703e74657d10e5ee3287a4bb54069bb70179d30f2d3894f8f39f68a2a3a0d13897b96464cfd7a0371e442311ff40f9d7ab00eeff67a994df1d79facf9899b3e7b53cd54a9825691cbbd2b5467ea00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1e1b4e14eed3dcb3179633d20fa7afb7cd43e9f3346896c74cc9975d93ef2a30","proof":"4e90ca47385aaba1ef2223404aaf77f781cf5bccbaa669bf8cab4d2d0bbee31684daa076fb61e44fee62f9f39b651c354e4a0441e239d547c0fdf95bfa97d671ea803292dd76d78a575489e5cebe0c6d199df4f342ea7a13f5e1cdc851218e589cb7ad4c9f1ebff47432c2197a59fac61a55c2d30bb2a2aa0b0bd6087e28c75f22c22030d5a133b85a4518640a30dd39629555c86d77a5defef19f8ccae12e0504c39a64b01ca5f6b9eb228edba26399eaaaa3dfec38d8944b86c43dd446a00d214e08b093eae111ac1e6f577f55b1ba6ba46ad0e8097de9db00ae3ea8eaff0504fb81a984794bb16df4e32f8448d45a6363f8d7c61a73cce2035a7cf20eab573cc1114a9c8a1675dd508eed866080e1a71c915c13ca4a50cd7ff4d50a2bb75b1ae8d38bee4a1f89a48c2adfe951941507efe567af521fcc4fb3b097ed80393fc04f50bf4f964245cff24b763eb94957a000330ef1f3e9cbcbff154fc0062a62c0c0da96c99b395ed4b357d5ac30356de8dc35a95e2995c2ad0ced42599602345633546dcc62a7ed541d20deb802994e35c302032d7c264d026677f6245e194fbc9e2297f0e0b523655154c851d697553d47beb9054588fedc462d7ba4ead46308117184eb5dc6d56275e0eb8e10b84b8a9b84137951928e8bc484cfd5183f6bd068c47900f716d14c229aaac790bd9ed970b4dfc02041c3b029196bb8a25377e4026ed6bdf658e0b9f9012c387ba0d8247a1515be8e2916ba67daa53e440d4d04165d8803916853a1182e2e8a7c1d5a91696a17fd164b227f81a5cf1b950d5bca944b0b7da39ea8103b88a262cac58a6000cad74a9d2c2bbdaa2e0ca2124737d88c8af6c0137955ed349e15554d3588301df9c8a67eb63f15e5a39fbec58b08eb1de0b66d1c35602a6d5be47502342ff0f76c96610dfb5b9bb80738b93b6009"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"145d3ffde905e8736d9de362e42cf0363a5b3d8cc1fb4cffdec54e9f31cba77c","proof":"e8333e22fc961f08bcb59528727dc7a343125bfd13800758b666eeb3aca7e9169eca7fd2b800f99fc8e1b7db1e4902eebf2df953a168e5e0029e8b00aad3980d1c19df5129d4b26157cb8a6f6253606014cf66ba9c4121e7a7061e8fa3e1fe235ecfd0214f2e5e1f6ad33853dabe14850855af5032f406e691b6c164992d8c7c5971c17314692a4e39931ca6bc774e72be16ce9a09e33212d030c9e89f2ccd005f66f09b226a6d87ec35ad95416574e540ccce52fe7a559c723277888b28fc0afca30e8a4cd16e33fdf4d8399f0f15151cff7ac1c45e5e4967e08d1aa80917006411ecf68a018a2daea3b032e61bbd272d049ce9a24e5268949861a70b00257be2875ea264bebcc89ec3bc99a997f40ba2a3a84b92064676974394859d83153f6a541616a4da44a4678b91d5136808184ab743e18e9933e004d82d4bee31bf2ba6e1e1adee60b7ef75f3fdd020aec6228903d9bd0e33794dbaa9bc3453e3361bf6fb7a7b87884e6593efba2418e1afbac48b57aff62e6beca144f122eda67a302a3c3bc600cfc715991054206da85d39ed014e5ecbe6a5215e08062befceae06f0afe631e5408bdef8038ed1e187ae407a693d6cc263155b46da1a733b5277712e066f023ac1a9fac20b8dd3cdfdc1d3f354b5ea37244f6f807c830de901a36998e252e07ca96d0dc1e2cceca39a1d93bccff9103ce98c75371e1906f5df5524de525a6576393d1b2068eb0820ca7d91638148b919cad0cf7a27625027cf6748f6bf5397aca4a6200f64c6c75a2999da34d795e06b8af80c02fc928e2764ba34a25fee160f0950a637a91160eb372bd4b235d5e12cc14ad621e0964f8b83b27032bc49a0b764d2e5a69be35883572e74ca413c04fbfd2b3c2789cfb6eb5d270b06aa077f38676b38da2b0ac591e7923543c365eac2fc5e4dd236b8cafd847d04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"34e920732f30df8d1ea613289d92e78d9157ae267ad1520946fe094e28667777","proof":"880954381e19c44ee187e9556f6d35eec180c63823e879ae94e7c4cd8a58c84ad6b3724820d3fadcfcfde1b167bde29943fca391678266a22eeb5d66a1ef7f69d80b1e674466b717a8de3d88186573a590d828ad16198006e22fd15044a798469851653355aaa95b860500b0e8e45505f3c213febd4a61520cc2c1d7e9104f158a36e6e8e4e84a33518afd7eacf82f87223fada2b2be06bf61d35e46e5e1b0047fbb58dca006fdef602aa3e92badacfb095c94189d2d435710d6d08439863200068d5ebd43e4a48eb89264d0e0762414eaaaf9e0cb758a986ff5f492ee4672072ea5770b521f5b233f5fbe611db01f66a77e251a597debd1a0add204da2376466ae7d529146c0b4556da6532760e941aa16771423b78cc87b6533814e10f5c7230d235fdc4897084aeff18d7ea5ab43d5a7f717b68b878e2979ee55ac32f244bcace3f6c1a120ca6cf9d47587944f052fe8c7b1588c3877a14b689dd102bad12c09d9de95f2cee194bda1dac30cacbf663d809948f2d078a44212abb98d94461ec4017dea211c29f4c6b1514cbbb77416a9d7a772c9d5d6bb984bafad3bbff76c4105dd42fc4ca5623f887ac2d08e790d904130cffda9125ee3ca5420fdbca26da673ba9e5907f2b5639e4a8d40beb65b9910bdf5831e01eafdc296ad4586a663679a93fd0b048e78a08114c50c0b18550e3ee7f7c7cbfcb4d372c4cdf9b106d88b2be616e716ca1bce38d5be4e024ab164beb4c7f5de3f1008d5dad1a2122632c2b25eb6ff460cc3891471cec16b9902f969c3460d47cf672f407abab0d6a6bc883d772903723f36d9df46ea62365106215cae9114fe783a28c27199097282e9dd18d2c4f23c880a45609b72b8687f334573b5c710f884b61c3727d967b0a0931c721a72f555c6c834756f1c7a3ee910ddbab6dc25bd3453c61aabd42b54a08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4a3750c5ff776acf17583972fa3ab4482cec1c00eb5501d72fa97371a8287559","proof":"a4641a27235d558ee740d503e95674cfab2c3b439ac79f7c5d992e6e884832787441118df91779f9a9359a3bcd431effb2003208b5fe15f6adcdec4cf373692be27873c0b4ffaa6517ddbb65f985d2f0b536040b1d8bf43cd998c505c3929f62ae1362a1e18a3c543e9ed64374adc374ce356a430448a1e765339241feb0a559e8d9fa4c11df0f0019d2202f771bbb96da19a04fa54fa13aae6779d44542360698e9cc7c897cee41b7d6d8dec3420399cf580918bbada9b094e8d38e82142608e7103f0e516fdc32f4d984a637260c5102c28942c71702c4a944c2e74c14fd03305f4589d98c5c3f32e0adbd2ac9fab7a7cb44742ef6c71eae2d81bb503e0e6542a6f94603ac8bce4dfcd09e7bffb38aeb1da90d91a123a4c28be31a175ba0057e86bfaf0ba6bff5e89a44063137e8a9a8ac95708da4c5c5c9954271433a0c05c00520994c764c3db26ed2bdf2a73b1ebe218de216a2bfe1b6555e3b34ad2c284e91659fb9d100e0bc5b7b8bc3759002c5d0fd01f38df3eb214502f7aaa3690306837bbb2596bed5d4f6362cbc58909fd78b7d13cfa2493952565e9a7f6a3d4ac2eaa4d02d3ce72c9976e5976c43bab53b77a9d494859e5084d6c26f8a4951379637ac59b29827b251e3bbfb775725ef9fc9b4f72fa2b6b2ab9bedba3b209608f6d1db76875f0d07d82b72d76897fbeed95b68c708b86e035a01b65870cca62a8a8a05ad42647a9965a3b8f11fe16d350fb379d26a671540dca9a5b83bf74d42e22f24edf47d00c9d8a98fa47a9dfe83d78e910cc9018d9e503634cb54d1fc45428d2679ed750e35bc58ea575e0e73e0994943831a94bb814c318166e90ce742c6fd234b6864c212be8081967e376c71819865ef27f8e3c64acf62d48d3f0c0fe470d82deba8f43082fd5835749e2705323f36defd2ba8f1e9047f5bffe22900"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b6330a1218c1d297f0b8f482c6d12fca569f47edeb8dd645b92b45d6cdd55122","proof":"14570b19564decb734ebdcd09a2789511058afd464d56e8a1d1edc91e4bebe27d6c28e9059d2cdd462f6b9923a1503bc81b155c7fd678360b857ccaaf660c55f6e29e225543a348e4320d7dc813371f9af33a320eaabaf46f115472473fcd155ee8b2d7a20f3d73b7e8e2c9d8c2b1ea52e3ea69ba90d91ecb9c05992e38de33595a6c308a87e3c7d9e2c1151ea85a8496a069ef9ea1ff850130e39bf8cf17b06ec9d8904ed542ee16d9be5349fceed8144b5e0145d6b196efab57620713ea503ffe7bd0703a7a0bd15044a7c094c8e4d63cd4338a777199227cc2fc9735033086af402c289a1c0b2ebbb53e4e7af8119d8ca9c3b616251c7a2316268c857803cf6763399d199a891762cc90c7bb1b61f440115251606828677a795f554c11135da7161296619311c30b881e30289bc360b6cd42b84fb9a302107547c5e43c636d68f2c6888775154b5089c6fa05009be23662f58d0f84bfbf6e2980600717867ca0d65839b9219224ef5b5a3c15125da19cc238d5e9bc7adf0b98f1ad81f204d049e98b17c5258744b26a02d1238c9775687f58be458bc41ce15bbeae8f5334bb0164f4127a66dc2a78f1168a92b613eb1a6a92b9b45011c80018a679de206631e3d616e13d6b8e3877b8258d415e03a86e5a51e799d0c71bfe2aa886e32582cec0a8ea82cbbbe5a2dc368343c83643cfcd9252dbad5ce49e5c846a1335daa227c57befd911fb820dedeb1baea4d6ff9d9fc2f745fe8a8af1d5d596b2015e56054029dd44104bb40df479f3596c052ee5c4bd457430bd2debb113f6076ed53569a1011a1af45ff2033a83ac5d80e899108c1a759a5b7f83fcd0ef2204efb3a6254d6dcb0a69ffe5a49384f48e8d374609163b1799cd179555557464b973ff0097554da196651eb38633d90448999d465c571419ac9aed80ead09a6c402f68a07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f681bd845d9527fe7c60acecbf6e11839e9f26684c541b9c55189b9c9b69a70f","proof":"043e1cd47f5307afc8131e0176e77da2c9264afe3aecb8f567a7ac71d6138831b62005f2155697dc09dc8e609efc6add9b75eec559d7e94d5a7d9594e529d47302ec25dadf27e311bf8ea94259e880b32b8a4787f600a840ca9c689d49112961e251a34760cd9f64beaaf7efd69586715309f36d9d8e9687301c0828bb01a83eddcfef070f60931ffa1c59ad37e81c95cc2a72c86858cacb9aff22ed7883030039bd40fd14d019a3dfb2879e07fb4b1e1f2efab9d0f9d1d29eac65239dba5d0147fc92c25f1d178be0a6021d9f4621429912d4a32c38dfc56d5048122dc8de07aab8b465daa4c3dcbf705b4fe92e7db617f7013f997519edb7a7a008df559420daded4f1dcca300214ddaffa6b268dc1c972051b207ff130ded17e4c3eb0a0791a5cafd4b65d94f79e91380e191fde06d9a1ef53a670efb00411052174935f09262616220430698320ce4716487c3738c36948bfa696cef10a8950ce4121e2461606f3120ba93112803ca3822e651380eace0ef3ef4bbecd1910bbb03a12ee3b602e4a15428566a9996377e04638980fb6bfc7525d7d9e2788aec1b96921eb169629ac5b5201815c65140db27a8c126bd6ffa192fb4fbfe3861e37fa27e9aa0ab889daff16584ec4ead1e08941ea3ed832ddf404c6fbce73ee228bd7d813c638a07b357b33ed08f8ef4c95cc9a9049fe45d04a3091601e1dadbf67887ccb1a060628812dcd5ca2bc9ab79e20095b34e23df29665b04f53f5f8a409d4228ce00368f9ef074db7f0855c9c7339a05a2e379a3872be0e0af6bf46659922acb254387c06f58858622cc6f084edc95ed1fbff2a97f2f809d150796e1e9dc1470e4a78688b08d55395c481f51851c59e3d716203c93da18d7cd650c8d895be8f1fe603964cf57193fefbe223886f7f2a52637e389a9b013092d6dc1c639d2a2c6f7e03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1ae8d62d953cdf100fffd7db3a5dcd0ad6196261296ba33b68d4bf7216331976","proof":"2abd98972761c1066d9e7b995e173ddd10e9b38d82e02f8f5a9688158f399511a84e63690146b1d2a209d4e5104fc7b1400e9df06150291631ff588263d99c0b52faa829cc00fce39d33785269deaba354acda13c1d53c1c9e336938908c1f0ffcca6d18bd938ba5100d1f13141d6062cdb600e8d8c1d8be1030e648ddbf2f5fc06f78faa846da22fcefbf58680f03e1e5a0002f549322c8ebdec9cd24e3cb062db5a11bab6ec66b558e6dec484d72fb7c7f6f60644ca504ab221ca193d6d70b78aac44d3d981960516a5f360f18167bbbd446c4cd127c9259477747ab2a0006b2922e8490d1a7087f61d2936df7f5e68f643404d21eceaabebb1e0885bcb5118245cae4d6dfe9fb3c0b5464e789d908a0c8cc508e02b00902ae2a668011fd5fd8771e4c851128220721bd35916b19293e84b0f528cf5ec76d09fb3b6508e45de250435b187c368756920247663edc61d849a7ef2eed37c315764b8c7076102d9643f89cc738f6bc2d66eed03d7082a649e4a2df2ef3ab6fbf9a124a545cf1713eff7bc3e0bcf573c4256d4c0c87bee2a14068a93adb14649b778522889ce77f9c4abfec1b73526e64b8b8ff6eec4778a2ccf2d9f894b30ae3236559523b3f26b8ac14e4b390afa6adabfeb966f2032df6f80cc0d2553f63cc6f86eae8fce9416c38ce345d4c99e40933b347e3d185ca2de8f141d79a5f10739c19aff0b4a542dc95703cf330125f479988e2420f81d12e20ab2da99af0b2da2f8f660f7dc34a4a8965c53435be4eb273ed8e3013f76d541f1c9a8d52d041d296c6242e9d473274d363f55aadd1e31bdb050913f6cf66c0bdfedfddf98ca8d490afb310202130b36b50131a9c42bf078e7f29244f6c5b84bf9554361735959c4a3fdc8cbe2f08994f5e599c62bc0f25e487a42448e4a7dd288e4be598d4adf755420c1adf8002"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0a0db0012c7d4f4885f36452a4396c90732439a4089b336a051fb20e51eed545","proof":"6a86247ffbe525ec866c6e704c5f4443f9c44195d2efb16cfbd01aa6b064ee2020678d8299034c70cb9db6efdc61b14c034f534d6b5acfe6fdb5cd6abd6f5922ea8ec71025dfc9a3729f7d98606e7e321c556b32510127154c60652575ac33409027bcbcb2c15c2ddc0fa3fe06d782d893dafa88d46ddb9ddfb34fc0764c2811c7f1fb70103de4136b294894ec1512ccd9241ed977ab703429a17d910b66d50e69d07d75e1394a520eb88050aa213b7b51f419ed09355a9a5c1e4bca2dd31a09688e58690a78986b1cf4d4c3ef02c6212c450fe4bbb2e892e820e189cd652e037c17bc0e1899fb85982a0973127ee38f6e876bccbcf61bf401fe108891660126628b64978d86fa90b0ee7e825942e4162c4435a9a2e7de99c89c22e169197e454eb64c1ada6a2218037a1efb9e73d6a2eaf1cf9139fcb5ea2519e221b5b2cf5918630a8f5d1b665a9fb3736e320cd77ddf98116b40cc9e8aacf72205ef06c40d0e5b0f443708ac9d6bf5c86911e15f1fbe415a9fd9164bf9e550a78707afcd2c468e82cf9274d1e438d8d373f558b4eee8ba263e6c0b7b2513cf2631ac642c30988fc0c6d20c399b82d38b8d5a13d508e995802c47449389c2077621721c124040e09eba0ab7fdf62a2e076b22f16035a7d9974d749f11e57cbf0dbc1d03ce5a321cf69b0fa23bead0750acaa805e4870dbfb2dc284d61d198fe9c852469e65746743b3fe64177eac31b0faacbdccea4aef3511468a3e4178f1dec84389e8e3f643e80ed3f5942faf32192bf28a8a5c7ca47a7ff402ad0fc70e93a578ff68862f44255cda86a93052528a274dae644673b94c57788d1b648be3e797814a42f32408776a61ea498f336c16146beca50f24e88785ed796b564cd1ef385607c4d02a1d9339cae0c9f09074c469f2366695e0594433362eeafeb36d030ede4a7e207"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7c855bf22b5fe63f2382b768f3a42e8ad856435fb4bfe8c0de897c5fb1d64660","proof":"e848b5924339f6f835158bab3594d2dfedd4365bc5d9f02bcf92dfa19adae627aef0a912dc88a1973fc1616e8b18d240fc04a4985a75a918b21c11fe995f043096e2555c420c4196eb8d1b34fac57841b7ec7414c4cca22ddf6caf4d683d633f58cbefe8cbc79ddff6a9b3aaee922d46d0df079b950cc4d2b587fee138f45e19b1ea1e4c1448a600d2ddab31c1a354f70624343534c94cbd7cef10e79dff4d01d6f9bcef0e3b4bcefebf657b9ef7ae7650225c6923f836f8f7e9aa83587a1403bc61ca0b0ec631d0b6f8049a9a0d537e026c4897d1001b2435e6e13fef02d70d3c413c989bb1e1e92ffb297bf648afb6d150a8bc4d853860fef5288b6f48ec052e67aa73e69cdd77171f8d0c91baafc87e12bfe72d5702db9aadd99d4c4bd850a4cc253965ba956e24087d2e732708f8adc765b245448265bebceaa91a513a78faf988315848fbc7db5095f14fc48c53256131725f10635859337ae1c1b8fc2084329af55badcaac6cfc2ce754549046bebd51e0676c2213bcfcc30751ffae4b9e2c8c18411f8e513806dfb367ef09de7b736f174ed316b2478aceac8ec01014a8c09ccb4345869c4848365067161d23de277bef4e39ad7e49ab36fc0a1eed76b0bb3f0b6733775c21a472e34640a08c277c12abb4bf45f2697c4236453d7709c0f5a535e58aaf6a3493636411c0b7e24978a68435f75b232603570c3331c764f22c812690d7d3b1a5d29c040f58a2f12292bb6f71ba1c7df0c88d73ff71a36f123320c0048292db3839bbd76aeddd011abe6ffacfcdf3b1118390d211343419a820b3ec6b3751b39d9f1ba0d4697d405384c2bdfa956334e10428de02c9bc5ae063da8389027b42c8d63773f393a751c989a26887249632386d73baa3d5c50e8dc43810b2be5e90f7db0446bcd0a3073b25e1e1e2d5377267e4994959fd3402"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fc860e23432f13e2208247d785e3dd369aadd5fcc1aee70127f3a4758c133231","proof":"084d12441dc31a265977101bbf43bc7c94748858c8236e41651b7beb5e73cd3b042d765ca3177a1d9a0936660b3812b047b27cbc88fc1592fe0b22aa2e02e073809abf4ced72b223c4b4f53ca54880a2850fa1b7ff4924dbaba48c1ecf16f67736f5c6e9b78a2b62701c2597aec7dbe9dbc2b8d7acf490b30302f3d0f4718d55980add495949f96782776781c0a7cb60e0ad019f2932ca386826e78c7d89a204c80130d7c45663eac42f580e8692fcb012eec465a16241cde9f23120794cdf09b1f1bbbb49c9089b34062d65e17fd00ae42ca70e1a6f1a65dd9f6b97d2830e07bcc7335b2215ca5029dabb07338a7e19d4d3a0d7e008a0a18fe41843cff47162a62e86e5b0f8f2a3a8defca2d952b28835a18af8bf0e60b430f6e9f0d4870335a696309df9f68347e8a3d05541498015bb9c9608d517a44914e1f52bc231ba484a347707182f0e71e02dc4e3f310500e6437326d35d96a151655493889fb5045e02b156debbc5ebb2d322498666dfb78740d66e21d1a6488b1be45538d94b533000d3c81ad47316e4a54f025ba5fa24f021ecc08474e01e3cc1b6785ec103175c4473796ae8ebc1d7ece33d902a79245ac87066ecdc12aaac60d18db15817a7fdac3c9bd39b23d43fd9e7ce1250e49053b9d0735ee23575fdea2c1095439d476ea3b6477cff18b868f0ff09a0b1790eb7aa75a199ce7e8f887410b8a5b7bad5968eb097815dd662379fe5763182e1a7ca59f41cbcc607a177402cafeec6b7a7cb8fc2fba4deb3296c6951dfb3c224eb4230770ed7a6b3012beb532460e6a7d6a68f7199b1f0c09a31442024e62bc424ca87c95e98fe6fcbd50402e70a0a8e6663f72a4d3e649d5388f4de40abebe12a12be7c3b7c8ac51e22e2e30865cb36a0c3c48efd573b810296f581418c874c68d5cea115d4ba949978e97ab5e9f9a5209"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d8157d0210d95b915e2d9d7b5d7ca770053aa55caf579926654f16037751e50e","proof":"f848be24f863fdfdc4f9c45dafca303dd50aeb25c8f602860b460f53e5aad82dea1e00a70f923ead6d120cf58f95acdc7cc898e3a50366b8e25f25c56b64b431f6aa33b79d32778fb5860264d3bfaca71a5d502c87852d50c64be15efdf2f565a08614e7893049b4a46226a5d6f31bd0394d697947dbb72d2136f546f6116b296d3d0605b338325fc5b5e284dee1487dcb0438e2c0927ebf5a2a7f7da63ee007fefb38122e7a0a31c96512c108b22e2b7703793fd543ac0e66ec59747018c000b89a08b35db70d125077a29ddfb46a350016167d8adfeb67f75e452643b34607eeffeb0a0047239b442cf00e4810e5079bfa8cc5964aca037a269a63f9056d34880365dacb8acf91dda1725aa1fad34a5384a37c78242b436d97db12306e966d76f4c300ab332607e522a3c4e17a959972e646593ffe408c124f70584b1a0c40d89da3530b79bc338981bad367ee154c5be0e09cfe16e510a56ee3a21c007560aaa941a99028539e085c47f95f92ac1a1e8a89b5fee384e31fbc7b5ec03e4d03ac1ea6ac853c7ffeaa5563c61c20e6ffef3bc284f08205336fc02a9a9bb10455acbbf49f132f701cbd9f393d65842c7a8ec66fa3989c17214fcba9b7de85cd72b65511e3e3b39ec526a5d2c47a4955e3623f1b889afe4fe0e7854dce6d4d6b3946e1d06cfb12abdf962449e2dcd2b6abbffea2ad414fb6a6719c61afdd17061058b145e2c5e10a316d5d74856565f12279889aea1429516d4b6af565827a13396207b15b4977f5ae261327cb5eaed9592c3fd457931c56574fc836a6762e3c69448c3bab8e25caa4ae150ea2952acae96f8e0b94cc6b7557d9744eeff3bc870f6d644b6e531b02ef31dbac5a07d96a07b847bbd65b4c669bad48dac155e64e08245779f7b2c88e0720353c82b03ce71440e5ba99eaeff28c3eb1ec0e2372960e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5ae05b49d3bbadc9ba6b47eff7b5a815e1313f68b0fb5fc0b7ec0a306b654809","proof":"c2632df339bdd08cf024fe1d49802796ac6a7e9836c7eed89f29e1ec39e19840f86f582d1f97daecc45edb07a016a6b9567d5dbccf22bdad948c27a7a19d092478c427865a3a1ac633f67e05ead6f5f8c88fb8deb424a23c337454cdfe8ce937b2e62b3b663f9cfce2695ff73d91e3a4624ce47fa57f2f674eca4767891c383221cec52e81cbeb575364b195708fd49c9668ed07a7cdece959bf69f69527320e9d3d78e4b9621fb91ccc35da448e4121a837c4b3cc9f388431e3e4c7d6983a0a2b97f66bfb60207f4014c6f3be27d66aa899b62bc75748cb8d73cadac21fa20c6676cbd9d2c9754d388526d05a9d4e00a2156f0ccab1b14b488ca6fb90a11871c8c56fe6df8a59dbb85836e33c5a0e8387dc29f91b2ce8653a7e8d81b57b7331be664ff9b198bc8e1ec74c0288c135965e99b2a0e3d1120f8ee063974a00a4263a6d26e86747f3ad2a89184321ae0c1ad5e75659a739a2ff90a6d69a2dfa361d30d60db6f8aa0bae3cf2f6dfd31e3ee9893d8f5b207ffa9e433d752be069266ce2bf112f3eacc67903ba7eed2eb76867ce9ac411cd76740cb2fffb620a8ca77b3e06f463a76b06d9f9fec90689f0c5b4c32dda3a7a3007ce81681e2e6e6f7050b8a92dbee99e98be041becc620378e5f53f361047f588c5b6c594bd0b61ede0fa8b6c7dbf291e67f37743aef2421ac068d273d40e18c2e93b6eec35ba8c15930a6476c704e7f5e078a566434ab7cccf7e8844a0f0cd87c525238f27f321911396e93ee2e1a4951e1360d357bc7c7b7da36d7e7e4f00ceaa734b7f6082f8ea4396ee97583584b5a65147715b9b03732cb9412143e1f05bfbb83f90edd07629375dea3096f09e320a1acda1830f5ac46e4e28da925a020916a4fe7b3bf9ca8ec0d6e131300a5bbdbbfaab72d3095ce67dea6cf5aa60e67107f97518742cd465d08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0c79189964d5ee8b7d22b219b312b684a5829a80bd609b84a73316427790d729","proof":"80fbab2c9a56085c6e450b5c42c4c2ee846e413c7d6d779c338e050f850b3846dae561bcff6ff430f0e51fd5f7f51d8867ac24f22e7aa583e8b0955c75a6235e8ceb17b603054a5e9712f7c504616df7bf2842682b6d6c3a5d5ffab257ad291b20198d547bac5873fe9a905c96bee243f9c090893ad5e2159295d46f3e09de74b14a4e279384522fafcc8e37741238bd782cff719dc3ca921afcd0283ce84f072a2a2e1dba5bccdc971049061bc0bdee94b71c7de2496edf4197418dc9c62009a618ac6833d8ee5a71535f8b89e4276af995575e65e35155f8bf4d4ed9496c05966a9490fb7d615752ca4d8b34298ee997ac5202571d5548b8f9a68531b4e749205498a6bbfc033ebdea5931c0d4ad0d2a38ef464b80af51591241f752800f1cfc3dbd1e9e1a60dc161fc6a02bff8f45ae9cf415e44901a0338ac34bc80be237240ef24032ec9cab3e5a51b917845000fac03b5d488f3e88a9bdc93458178c11e0d431b05a03958f5d95c832ea9904665c6bb23565a08a089de6da809bb88578404c980fc85c9465f26d58f80c3acc2c7882a00d48a2559b7ad0dd2fef17db560658b9ee93ef8887fb730dcea5563c5f5327fa5ae58ff1452b0f9e14327abb20460c037296039c51327160df22c95f4afb0e01b23f1f75d2899ae5c5ae39b052a2c16177d3210ba363a22c8a3cb8d3f2d397ce6767f3b5ee82ef3a27dcd8355406f689d4304a2a6e2e4793985bd2837395a46236bda4298f173138133aab71529445e1c6f8e1e0c661da0c9d8c9d9bcd7cdf485842f75b3368ae70d6727bf65b36e912e4050b21154640f7a2296a24095f185ed7c2c190864e4a64eaf910541c3d04b479a6b0a1ed854f0163bad3d0f03249e14503418a5bf85d6133b60a28078c888fc9499194efbb380aeb2125e1deb6d8ad209cd4002590fe5c3b58403d0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e8dafc8ca8baf8e53a94033fcd476f4a654a26c508305bb2c11954a051afc329","proof":"985ad1eeb6c83545b7629ff99dda1e2e4989dc5c916de46004b811153031c33fe05c26af6618f85cf2990c1c579f64334198b1bf7fdae8e735b08d9ad50d9362d268fc2d4fd8c8370c86eee07e09aef8a424836a4829cb6e9fd40fec38843321d6a9b24de360246160fcf4c92af0d43c083916b9a7548b06ee215c28f8df7473fb13d71422b595cd26801eb2fabeafb619904aafb32a08ddd039dff95c52fd0cd5a0e29ad9a1a8fcdf6d370cbb0e5961af4a94fc48eb191af7e75a715333e9094f7f9feaca8d9c5bf2702f64a66b3287c78ba55e9017c4e0c1d6c2aaef6a710d3a624b13fe9bc4671098d694662dd30a2dba745d8710ff0fba77921a4c52fd322e23d7f81908b69f56a1c7c54e2ad0978cd97dfcef239b3f208f88618c00e00a14de1d5fd5b85afb0a0f409b55230085c4e47952694c35d92d8a4ccae0dcee28aa28fe4ae2bb4fd9e4f31f3418487f965d642a8d8e4fc8e1ecf94fe544ffd25fd8cba575479bc5dea71ac14bdc272dfe4581cd5b24d9cfa8d8b7088e1d11851a18302ed00a21f6a6c0dfdbf7113f51ef68630f765784681a9153ccdcfa24894cea48418879ab1bff3a9f221a9c124e2ff5591f064c27a3c2b5252f539239530e345efa075cc813897a9edde2d5ff514f19ab45945901c7b7aa5bc6a19ae9264beaa2bd025cdfcb551909c74c653f62bad078321b18bcd6e1a6f1063b7882f6096c3f9260cb1d9b16997ce10eec77cf523d9c4729aea292717c94b460dcb52b7a8812b8ac063ca6fc231c2942a333550f9af10e8a8f4c4b48bcecc530bd5cc547ac2ef63373ade5426c22bc011c3c9c4ec0204ea29f5b0f13de25816b0db8e826364f2d75f0a1cd78fbc445f943b69135634a9e2936b75927247818de99ec890e52024df3b176371c658a38c5c8d319fbfeb23f153afcfa621a3a24cf0fe21302"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"948c808b32654e7a9ea7afe02ff79dee2bf7b8397d881d33052e630cc6da6b6a","proof":"feed30b09aed78b8383e1239c71a2dbc1d0f0905244468898c5a59695c550f183a73600b6b5da97c72737d32a181f5f9a26bd6715f46d86166b617087c5cb1772856039f2b36c939384fc55d9d953e26c0e6cf8295f6db04e4b7f759ac456337b4ef124f68403bb627088d3d900238b585a98163abdb2ec1ce0509f238514405f590c2eec45f88f2aca5076e6990a2383220e36ad2abbfbc15838355afe7ed07403656266269e5ac84a90b797f9c0460815a5aed58a1a8823e983087b07b50003882869e1601c79119245cf8fb24c9ad73b209b446279c8ef9a53cf6a077020052b6e1387d96b7c76703ea46ec505f9126673ac341dae6080dae7c6e2d0ed23a5a895dc7bddff3f150d3edd2bc5d8811a9aeb841179d9273a1815a3ba995f9508acfbf3812176cbe1a05a02852fe74387794567935435cc823dc7f4cf5172d534a2132d598b12160c31faa53c19437a79683db90148a881aa4f88c08f811832d92b8aa30069cb6d0e4757e1537545994b3cae2fc2ba6a13c68e427157dd4364fd08f4abb25fb6186053a209be742d5230288d2f77672e91048871c83059db43084c6104fca0c4937f7a92185915dfc3a0386207ca440da6e65186d5e69edf91380875e33a51cedd61cdbb3ec4477b3dd4e4fe241d7f3e4139fb60bddb60bd42256ceb5856ebecc24217030ecd3562cbeb628575fa7f2934eeef10f3d1dcde913ec21f846209d32fa2f205bb027a2a8484485ac71fd5be9cbd973412f4dc8fc618ab2fbdee6ba434b436a4c8add2af0a2e82b852ca23cc7c30a79f263824d391ee22593dce722dcc06e3d7cdb92fe16f4561c4004c0ff943cce3096651c7b124babadbd4f9e8f5a7dc3f2eead2eed6ba204e992583d58a763528e9525ec9e230e3cae58b6edde15c29bd4c7120f908c36af63f3710bc3dcb14ee87683d594d208"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b0d9d38dbdfcb79e0da00c8ea28b682bc14fcf1e4f44e41502780ed0f122012c","proof":"867f060ef6c323fe643de5ed7ae4e8af2a2031c212f332e53dfd88adab5e65043a32c9abb6d4f08d0dc16bc82effc49d1d51ab12a6e66e4e198083c7c04e2515447aeaa050f6960be2859eda61546a86d1a4f37f55b8b36967db6f6a467eb4563844e12eab2a0cab1b1e11e0c9ded63b532d8f94f536f25a40c57bead857e7291a3b97e182854ed5a540309ed2f89853329691226925fc4e71a056565ef6010fe0a7342f532cc441a29c622089283d23b90fd512721bfbffdc1001206f77e606ade6818ef828a95126ae6628139ff24738a77810792668316059f13e55ae51038aae68c6f77f14b4287ac069351d419f7c81baa12722e3b9dd5538fd644bf50cde6b77364abbb4adcc04ca44b842d905800be3fba77afa34aae75f445d8e6c6d0044f58a61670fbca4299ff12c51429b80b7bb9d328f04838a45d4a61906486b4ee52afd7f9aeed086fea0901b911261b422121a8f9625269f9094554fc93b3dd8304cac3fcddaad3e139c966eef28cf96a8d4014c79350706c3c4c5e4767b0fc65997c5417658c4f5b1a385364409476a3d52ee5111accd10d37dd9e770cf7cc4ca00ddeefd5420d7707996c624c982c8e7b9c522d414436fe0fbc6e1469b1bfa0e35f1898cb941f09665b1364551958cfd28d3f99ef67f6426e4768b7a52434893bea7fbeae070a0c391df0a09b780bc618be95c23e70c801dbbb5df476e689487e2637854c8174f6e2c0de8aab8dbde2a06a017d4bae39811dc90a187f72ef8893d7a3e66ba2ccdfc0bd5076db4bf42269d14808069b046b3e036e0b3c95fc4d9e88313dee8390587912b9d8bd044fa8aa3b73b233381dc0ae734501b564cfebb5031c9762910fd69bd99727d6e5d3f845a7f84b5951df1db01c84bbbdd04105cdc19c97a92747403bffea6d7e2d9f0bc0df0877efca60cc185de6c5b100b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4e8d31364683c42bea7ab4d3c458fb6fb80bff37dd768ef951cb3dc88ca3e172","proof":"6498db1a7ebe91156088a32b633c20f0e226daa9ddc6e0a5623f4116ba5f3c4f6a2178fae2978b27ec0237f65c7552f72f4337a58df03332e0401b1de5789370de755c0c9d039137e69a7ed3c6e893e6fe7c6d82e579e9eddbb4a2fbfdba55635ecf7f49d67bc636192e1b3f6250db618d3848c9b6adb2300e830688baac415f9004235e5afd3c068c225e5463afed66061c64282c8bcbba2d5f2a156b7acf0f5b37271f78cbb070232b0b7cb203bb31d0439efae41d57e46586a631ae81d30db1439fccf306e843dc23f234b319a8d8db0930d57bf358d2873615861e5b7607606addd6cee22e580d9f725afe9a7631409c624f7f9091b5ae5df882fe554723366459f3dcd4697eb647981486b9c0510dce647a6e56e8f5af7ae31d67ba2479148cb4ab8e1a5c545e4d7a4dc07f400997716b7cf6cad172a7a5f3559a61ee7a82d63cc3ecc62458fc31cd698f18d9c3070350adbc610a50d2892757ab6b760860c7d0d17e45fb2ae1f1b9234b71d883665d2070aded11211fe18c678c77396f98f28e8815664ccb93fc3b7e03ce287f1daf0dafb4fa8bde89ec6ce5c33e4c09f4483c51bfe8e327020c8da06e5e93431ecfda8eeb1853bc5cf7f7730ef0a12c86dca042334771615906e2f056893d6f2559c73e7b5ff5cfee355940ed3690021c182a2b9122b6988821c5ef3211956d5e7b60342373ad2917fd9834629c102f2c782856593bcd22e02e13a6de3430ab92dd4870b8f214d10cb78771f714670d28f8dead2420fe40cd051240ffa06d2288bbb884950aeffe789f4a6707f3471006a01108a97b9dbca0a719046c3754518366c2e89841d07d346190cc74f6941b1245ae23f231e0a8c6b3bbdd95523b2116da4f2f4c1f5171c9699d11c99330083f4b4150f94fda169cf8d344c2088a8a52077f25d0d49c45ba4e2c8671da6108"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6a47b38f7e7f53c89e79e4671a32f2021e600730da328d9be5482a6f920ecc68","proof":"ba28b8481ec64253c846f03c914a58f5c8062caf67c9b23fe5542c0c42151e1bb8555071c1c26a1cfe46799f235d5c8db498a58260aa34c20fef198ad1001e30feadcfaee1f88db0ff48013add9d4a9f80237aa183d2cb428c7f6b2f0b54994d8222faed2dec3120c30015a93827848a3f0eceae2f02e09741852408a58e5438415f245015ad44033095a22458cad45d7bc45623ca4a8a3c7396bfa2d711da084d1e5970b06d97acc4826992f151e0f156d8a3af4af00a067c0b257707f1c909852d6e5311757486f81b5aceaf9c5c6fe15d77e65c5d0def8421f0acba59d301e25b78afc18bab47aa281aa7348f9f3b03ec7413af4e561fce8a336e49d82f34de2d6ac88d99cbe25ff806a9ffa332682bc62b5fbe855a9924ccf659e1b0e77540bbf9a008a85dc1a4b647b8482e29b17ebab18b44aae741481392e0df39e970165f06b3d497b26b6a747abacacbda064a867edb848c5b7b5168234308cf0b425cc71733b42b4c650da98af9cfb946f1902b3ff4da851eaa179e895f1f7f6f5350ad2e81d6be0168f3f39ed8ee5477e31d8ab94adb2e3778442e43d4bcb0cf0bf42a904e45b7c6b7c25b08cc9d8389b28ecc8ebc292e27b8e2bb2682b034800ef25de50166dac9b571247f466797989a408d3d2cf67f410470ab04925fdf90782ae2edca5ee0c89a0b3d0232e3874e0cd174ad2fd42625e4ca4e5120737cf821c472ddf4a0c44ff4c17cb55d50bbe1ccb3794ce41d61755ddf55908fea16921488d1e6c8b83b5e57ac2b6fc96552157805911beaf778daa2441669ea111d1e043ca3b0af7d52838dc56ba130a6d6ab24d29fb25712f9bcee564c412126ae3048529c2555555cebd8d36fec12000b528324548e1300f9f0cfbea49ed82b6d9f0d0233bf40739f666ba56af7fa31a26619ec1fe9582672804a4e55e7e906ab690c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"12e855cc5cde3b4fa62108aa1b0881da3a7a713472c2a06440364d411eb9dc26","proof":"2edd1de0fbd9161e422c6325b89e427a95e340e0bcc2f5e87f7c23d98266f71866a0d614aaab8894e1663dbecbfcfb348757b14a6619477287045000ed07d37286f85512e451de53d976e05116ca9f722694c5b5672797805f7d792861c14103b07cd26b64c8c1974b2ca7a16e0346249292e8671a0523dee79157f2602662712d369ff745e6103d4f19840dc29008d0c5de16c95ed62acf40b383ffe06c050a35b7d2df15f882f3d957eedece124620eafadc7e126d882fcf6ffe0001195b0840adc8cefbc890f563194370b7af3e5887c1bd24101f4dbf3391147d2437b109e6383d952e9c29ffb79f3ab46dbe1f6e3a5de188ca73f817e37c63bdf23d97541026700af8202e4a3fe36ca5ac316cddcf02802fd0a33ebbbfb57b692e000e4ce8286d0ba901187c93659f6f4a23852894adc93083fee428b0481b38a5514f7840ff62260dbf422da14301b18464cbb3e89262dc78ab932471f83c7d75cc1135f265dcd6132c4c13864c7067c91a994de989b2ab1ea95b2e541eb39ce4aad015f80093af103d0621d0c76cf074f414e232442583d883f716b9f6f7edb6112d28bca51a796b2e4ad25bdd69e0558a71221da42be7082317a066cc2549aa108649c2634a83d5c5a726b59d6af073cb223e1a2da2f1a08fc77dbf75635b46dd2d6b50f79692e492b97daf6e533120ea00a2e738e0a4093bb0a973efaa96aa824b013e9f1cd2ef719be8e5c4a30f676873ad5b689fc4c4d9cf3eeb8d6748a092f67f66727403a1b9975fb81706aad97cce4d3d40cadd96d078d02eba317910f0772b18a07f020f8ca034742c74610ef57d08a4c6c312b7543aed10a7cc7edb582e773370ab5a5f890c232d05c46bfeadd5c315f26114e62f8e3c1f25d7ba0be014062a75273305cc8ece9d244ca63c109f05ad8ad744a5188c799eb61ab5f187a00a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0075aafd034b2f799144a99e2c5586a15d839a1459c0dd5601bed10eabc56707","proof":"78e22ab11bec75335704bbe8e55877548bc7192183934725c8e77cdb7497510fba9c21c8cc5a2049d96ae6281500e6541767100743271709ed1630adde5820349c26d0c1d8b4732221471fbe62206efb26d2f45bae085fff3f3af070a007474efcad57a7f449bb464108ce2154164181fc1e60904fab69b95658845a2c46b63596f2be662bdd318ea22ce7cabf136b78ebfecd1feb33c2fd068620636b224e05f67a59aecca7c618b6321521d1fc2a0c682ffade8ee73c7993ecb7665a45dc0b5d1a4dacc1eaa9e1e79526020be51717ddaaf381e784e361d2f8824b7798d40b9825b2136fbc95441e5c751b16a66efffec1730bd2f053d1482afdf5b3243239d0e0800695b31c1270f77acdf1b76de0aab23a92485d78404a0374c75f4c0a67d62e9840ac9786c9d9c0bf87543ec4c50cf7c71e748c3be17d06f80f074e9a696485d18eea4e681486ab7e54d059c6929cc92515a1aa395dded389eba2dfd831c2617a99798f306d2b9d233bac561cb8ea29ffe8eba8ac1637f0d7f1b9c8256dbc4e88b222711aecc69bd5bf35b4a8472fee10319862ab9a084626d11ede77733a6fec8e340d3f82829fccf9c2510a2806427912eda85f4c96e73c3b881ba255905119e427f2e5ca8070080aa6f57c7b720b0e17c33fc07b2fc27c0ebfb613103c11ab6d901ca8fae66a01b1a9031b72bc46798d51d7941f920b92d7ce4f49215ef1c9622b13836d69e70607fe5bca7e73191e52526ec9883dbc446249f0987dd8fd3166d4dc898679b6eeb8b2bb631cb1520a665a98333071c13ef1d127c67fbe747ff8048e9d6b38510b9f146c0d03235227506f9be8b10788078bbe6f552a5406270bf79265b5c9b83e89587fb5edc55971420c16b77d7309c6956600c506414271f73619d82e3cf343795635daccf7f4027932cf72cb74f1110638512d0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2c6668775f3f53d163572f68a26620dc8293fda8c0372184ec82857797f8de40","proof":"b21c634b8bba9e6fbc1bf52baa1e29a45abd8c0584a0609be0efc1076686a25a123d22912012a376fd9b6a1c913d0310919ab5419aa41bc0a208a81a6762407498a92725116dcf16326e080c44f6826bd15f783bfbe91fcc13e9ed2dfbda827f14ebf34b1b6e5349ab7b6705df0d33b0f578082e55a1e8613f215a18a59f7f7bd9763fb1e2d5157733a43100733cddfca07db4e3774e46d1a305e4086e35400d0c08bb50b3a84ab1ac37a3e9320da29b487061d2d33a809f99bb60c497eb3e0e5656de60f6f2ed10cf1b548d5ac66c7931070b7b31789d2c2ced416905936b0680cef93e40f3b6e816ca7893f7202a1c7e251c9101968ce3fd6d14be6657525cf839d8f02cf3e5a75037da47b12fde5280249d722b85d5321b40c969156c5f40ae23ab7fb4c688b5a35c34cc2551cf43be9df2321fdbd8f158bb1fdcc762ef11b05c550cdc977c5d849e8a4dfb5bc478f58f4b48aa9677e13ae110db36035b3d10c30d8ba1b59b15e5f9f83aeceb2306741c59946bdf68cff24acb027691c34d5e455f58110575b5e9185db7bf515ca6dbce4908288a9ebe51bfab2829b04735208ded342d72b812496ca2b5660a6e9f750ce727d8e8615700fe687c751e236900fc95e5cc839f76d277dae7c6057121a7ae6f5743b5474f5913d23f8f2d0f58b07d7c1268c1c0744e75171cdaa0eb61611e686468abb60ba9d200a1c9852a7e008ff03c8df15055f6195964c7580080a79f3a007ca731e0dcbd33b6ded0990d520340286c705c447ac42931b912b128ab70bb2697ccd7bdef4aa7356ad7f2299eb4c6cc2a210d6fd3c034546ca93e4535fc75f67313a8c8c489d0125251412699a7c4d4b33c5e7484f94f2a145cd0479e0971a1866728beb6f077c5d957f502f4f7d084fff88e5543adc35cf2be574639d9319bda8f89ec264b0bb4bc419202"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d88a5628480b9bcedbb190e47c301ac7df859dd20e77a3f543580c843404065e","proof":"92cd397113838e542b3f3daa0f35f27d6ef3359da7e01a87d0c87b0e44f24c1e4afe1c0239a7da8f7de09a1a54a9888c4d18cc6aa03db753ef0b7ffa54710f3b789b0be48c866b777ccb361a80d19ec5790911ee16ec3891d874283f1d280e4df487178d483eaaafaf746165900bffd3b5c124e624194cbe51720edaae300a35032d6beb0e1ce3191e54fc8c85ba99907999507c9c4eb3f3193204a47f915402000aa73a5a165d8811184290524e0d6da0804fc849a4979ddd934b3e875ec201836179c2cc862fdf97c04e4b40e24ecc095121712c6209010b9bdd5e8b00b00036819af8e121198615667a2e5148347a76a2c68ca7a917336d657ce90f840816d41627748e759c3b47d043efb363b39f79ad487d7aa1be869a062443a2507571522efa4c3027cb84e1e50fc6f8ce00da46cfb7498554e394e4d304e9745cda671e55f4324e707de4b29ff4839823646126c0e5230007eb86418cb6e34492ba0e08c4dc1fb9c4538666e84c8667e7b5d8390ce3cd354c02d72f05e994aef8a837f616caf1e9a016da0581e0e1008d9d0c0a2005b475c3a2a2ec81155d68e59621a8d3b9f30fb78bc2ee13dbcdce200094b70a042e50e3fdccc6954b15bd045a482e722d4afc7ac61a80bbf99e83715e101f1f341689bd1af074c3ac456a72671f68966afb4202494649a4ec2c295103030a143339db8ac1bf9b67c2bf2434162a42fc4ca54b4c0730a8b18444135cc453f68d780be02feb372ff43612a6449c5c049d2ea4c3d20e5599c3421132287172b696d14a7d7d98708c5e260603749d7450df0c2f4d95a0029d025a837a6c41ec5d7e36ed48a59701e986824247e63329355a4e66a549752508979fd8e737cfe35b2bc021b664fa4faa61ea363160060076c2ba334d696017126f46d197b5bd5c075614a5da25c906b1b1121fea77b60e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"20c67d4b30838f2814ab1c7597aeb530b823a5c62ea007b3c73cb1d6a371cf52","proof":"82f2aa75be8160b868e4a286ce89fb75cec0364800dda308a5bb061394953d68a201753f33710fe89a9a9940c1abc25f01271ccaf9466347e2ab057459a4685e6c791ab918dd4d4aa3e4f463c5c947e4876029597cd438f8cfd2d5497e6eeb332cddf1f556d35ce811678df4700c6ebe796c031050b6d3331941d60f8337a42b5e41c2bc96cc2ec3ddac81abeacd8ba9d985a6523ba38eca6437525752619508260dad8b91dbd3f5a1466295ebe7dd5ac9bc063a68fb99d90977777b199c770f6633a982e2b3f601f2eb3f147cd97438f81c9539884824c4caaa2dbb1db127033488486ac73f94ab5751e3eb5f4fec94a8984ff8a9006ad824f700df7bc8fe5e82e09ba74443c729560352ac05599010665a6398cc1560da5e1887c7c9a9ad18287b723eccacc4088f39271e736d8f8dbced9b3e8da59e19eecbdc7e22684b335e256932da64b65ac8e87409f624ccb371c5fd67743e5453e910f51c6d0f117628d1aef95e1f1dc392507ca673699208ff67d407b1041263fa4d618b882a7f0d52275fc732c4b25676afeaaeb41645fed4d67e523f1d6207f880cf66ebf22c0fceff1a498ad22e5e4dee26180d9eecc3ad3b0e5f251d9dccc5f50cbb6b6e3935eab35a441904b69bb913d8bb66468542f60a02d204be9ef75ac4ac1f3e9de74d34a37c0bac693b94b66dc5aed3bb1da1a1a0486006a58223d32589c680e1c45e7293319e31dd770fee7cc0f4a4bdaa7f2ee643bb1683512da7819ae4af3ae026bcaf76f641be198f598a51ca755fd1cd24e94b403e674d9fb71311c851375a37b8652d042d58cabc7e08b52c5b224e8a8a21b8a67f8f4dbd066298a4e31d9e7f6d4fe25ebe6afd3a7b59d64faa323b1a862602bcc712764a2983f7cf0aa0010e03a7783c2fc2753471869e85278e67d888ac042b76ca13e3a8fd1ded868f830b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9e212b8adc5cdf59654bcc93e9a1faaa12c463b95adfabd26cd47277cc3a1c3c","proof":"9ad9a06e7c72c5fe6818fb92d961193f4b728b22c3242189e7fab18ba6e4112a1c5825ca252f78e5b62f7c90ea6015cee63b721f28df41d364a1bd6f5fed571384d9e34e22283aaf400c612387026f7af0223315ae3a74ea4707f97aaaf27b58d8e4baf36c764425cb12197f784e2cd470b108e59992905dea3f75c5b05c9c73744caea35f101a498f3b90d6ef0af3d891c17efbf7e30eaa0483d0a87454a703602cc3d9421f9b8296828c0ba8ceba2213dc4b8b0b8d92143b6055f49112090bd0ae354f42a579ad7246b8d85547f74f62ed7b66ca1240cc9552d8cfc9357603f00e6204696e8c94765ba8cc82ede3364692abd0c276c85cd53fcc840a02660f66d207f2c566b5f8e086d652d870ebc94089a43e13201caf04ba8a1863661307acce176bdc36d5ca5654b9fd85c486f4246e4c690a5ab1af4123e905b2e4d714221be13b4b53ccb7b7c03707b2c25001e719fd471f1aa3199a2284057e74c578b286064464174eb217506d9ae0f7812b10b789d5c229d35034a3a38b80c33b26e2103293a36f62cc4b153565d50942164e9ee6028cef207876aa6be9f0751e77287fb1982281e6b0e144d89c5a68d8b1cc0bb8fc66e27c1f3869c7eea02dfb15ae9b18934b95ef1a1f919eda4d8dbeb0ece39f9229a15e09d7902723e7aafa2742ac4ab3847377b6d1cc4d9107ced220a87251d7c4c5dd1e1925b042b70d154c703412dbe5a702867c5a21b85ffd43d76e1a1c6eb2cd8613f1cac8cfff047b39f8f5ef328de4cd9754468d3e24674a059ccfc8f9e2772a8f7533d9346ed90b10d2d2ee5e16a989ae281bc939cf267a254eefd28253bb694e51ce5d91afd0ad419b15ff74d3fba4f7c364a77839408a5b6e05bdd5a15e34e2d30e29393287c30e9b873a6a3558f2de7f393fdb3cae1e9560caf9474fee624ea8c185cb9212190d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0ce7e1c4bad481f720a8b71fba40a255f450e66a7f6c92ae0402deaed6a9db75","proof":"b0b5d94dfd40553b067f35b5c47553db8ce59d0a7caf77189c38953cf45fec20ca1e89f4188ab074bc11d93f8e9878b7321b590bde5cca21be673f3bfbcc497eb6434fc914993df848fa866672be2351ec643c2b47cb33f396efbdd85b15f76624b5417db56cd71864105ef3324e946c08d13f87e41518543e381ccb874483545f574189c72edad91e08dfb887d558b3974c8a8b9b63f53b1b9906451602a60a85c6c24f66f3542300a2c8a06a23f405fb2103e6d88c8c82b4547d6c2df1ea022974a7a6be1b66a8458bca60d4b6f441ac3a7fde3ced639f6979ab407f1f3d0ba202704dfdcc5deb1f25d6ad4da57706a293a49a5b2325834d0e0e06801f802be684dd011ef61d58720b74b3d53a83436ca1aa6f5c6a17528684b22602efcc2692c2d1ce9c309dd1e760794544e357f27d32beffb8d5332c0ef27d5368e8575e700eb6555814bdbac04d6068308b75722726b0a96b34a3c1bfb39d6aa8f1755e66fe599082285187dc324cd8b9ff88a5266f66f1650fe55dc0d65ff53f9b696acce17cf162e09c27003fbca8bf0f2a190a90cbed7014ab06e8e2b14aba2d123ac8261e15dc3772df00d664cc2c53439d5c403cd6dcd8184620de622447b45f6a0e5ba9d3c8c5009f5ab36297fdb13c62dca89b721834ba968506f8be485f2e6d427b49c19a9cbf82f4450893c7d84a436b17c275969f999dd2ed9ace4cb06b2e92ecb38d9ee959f4c31580d6780b014c117ade16e278460d7b3fc87a9265f97fa4b2c9a0ba54b33a47113f75d131c69641a16cc9640fc46d16ede9cb0a738b5458063ecd6f3bae238aa5643ef7dcfe6093f52d67df4b104f3841703150e77332274735efc1dde7629990720a605c62ce50fb71cfcd2966e960fb19b567d7250e52e3ffd0b2ea7cbd168d2975ead50d6f78acd283fb6a77417193449928e9a502"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fa8f1e42d45a393effed48c702724363f9e67d59daa224d014bd9441eb1a772e","proof":"1670464697e138957e6ad6bfc622a6a651b6da86cc4a45d7d9abe05dc79f0976321d0f3ce1d89a58e4c7958522f818cb3fa2e141885c3bbd23413fc060ebef64ecfc3c6a2a0fd91bc60b48045f36d2b8839c2cf45bfa2ddefa519fde8eee54471a3aadf2950a0c95e5df592946c4d05228a52f5f2b0e1e7bdeeec16612b328761cefda82b8e156c3893e1f112ef6ccefee054c304d6cf7d987081515d34059041883e2c68955fa378fca27adb5252f1ed089b86b4046103e14467a8dee85f20b60300358ddbb5b090d22e7c264a04c9e14f41fb337ac8117fe07d5aa1b58ed0c726203a7c8256e08af19f38e55b62c7e25e9f50a4c9b238e5fa5aaa0fe119b789a35782b3a432b95d74b6abd4aabe1d3432b6840b2c557feef3ab3c2f1bc9307bca45a4c8e4ed34b97aa6d400d14eb9f43afb97c3ecd77e7c2cb371f6ff3b1241467308badc2758ec3a51cdae9423c5b3d7084455ecd5c5c96a3b33523cb6c6d3a879ddfb4245a36a354956eda4af4334bf4b4417f9ee8684e40ac4f9cb69614640b6ddac0a4b38b1c75db5cf55cfa8d1759d41fc71d7242d3d74f8089571d18ea962ca1a67ecf21a564f9c034420b43dde44c08fb15580ec6bee0f7247f280cd812cc0a2eba974323f2762e18cdf04c502a9f335e22baa1c22452dfc75a1f5ab0c61cc4df054df9f792a772e20671700190765ea80df381d277f3259a872334ca3a5c9cf1c419b68cf099b28ed5eac2abc12222f64053d4454c04ebff967873069a2a3c3aa9078d6ee7d211308f78ddd3355263fde1a12decd95eba8f69ea7d9083f54ed95cd2d331660473996cb243a211a86bcdc9afa30aa22da5e7bf384389052135d563b9413887c07bd3a7bd062c4d6d8090bc106e50703e34bbef18072968dcc6a109595228ef56de25ed4c5d1f07623a50ee244c59a1782cb88a4a09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dc233ac1e41e310763602924ba25abd367de6d77943954e96623b9c9070e0324","proof":"a4933ae80f66b4ce41e9a921ae3b8057929a1a4d4aeb3892bd95cd9eb9b4ef3706fd27a984f3b7ea41e097bb40eb23fe6fb3cc6a824bc3a84b6b314dbc007b4d24571c6e2abd2520bba3f89d579dfbff402c98097aada67b23a5d995b48e843158b7955a3a50b7e0d37b1a703f912a99c799f1a8919ed183893f1cdd094d0e6d261f8976bf7398cda8ae789335ddff448464b5115e4dd5a4e982bca6dfa3e104f1079390e072ec1a2bf0726d9cc5fd00b939ac4c71f240ed9ba46eaca3b4de074cf96f9c33088f7c426fa9324f7899fcddc01a8fd14b743f6f613e76f1870f0f784c109cc9672a55c9198cfe7c2f3c20559cdb527510c40d765d4ead7c1b6161aa23da8f4d806d352869343ff806287426758c56f7efa0ebaeafa9671a472c3ea2a171e37e677fb36055674c30780faf4194a483b9f5fec6b37dd2c945a7c1739c40eb22f3ad5a198c922bf336861a9f5ed3442281b1c260f4e7182e129bcf39020a4a157f2e9dce02862bd8ed1d04ca20cdcdaff62bf30825160c01a9516e2a20d52848e81fe7cef55abcce75032c6c1f134678324c3fd898214271dfbdee01e6063e7f02ee9df65f86d9f9a776effe0ddc9a16c191ebccd0a9c3df55b51e583e29881d7c22b48bb63b7b0beefb9b1ac6cd2594054f140f389f89447b6d6401fa4358e36bd719403bcb599666a21cc3b5fdc788285bbf237dbd18b17198097c547213c7a9f8d2758014fb162b7b193b8964f20745dc8f44e82bd05757dbea05cee3675bb55decef49abad1650b9288a47fbe7033f385b6328ef1cc9693de47034853fc24eaa8035e542ebb7b524171ebb3036c2507b81f1b2b2e8c2c2105b75a7133dc932d87f8ff0d6aed9891e51abcc235d23abe9aebc02850f96397337057642e65afba4160ef2a6a629acb99f3c7c20b5ae40c2de1aa441e13e49ac8c01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d2949698d31f3fa80c9f03c159494db6911fcebd8e5eefd89234e95cd2ed1756","proof":"5e7ab9460a1185787c586a65c8cb67dabf2a7fcc14bac7587ead13fce1e06e33e0e9946d995aad5fd20de2eaf65e606a33444a9e500eae0d565dff7a0e013947e8625ef4328bc5c94c8f73e55a183832644b356e95d1c9d5a9b790f991f283660a3bcf539be6e33a169fd617ba9cd99c9eacbf828c937f6916f32f5f558b2031f45ada045601a9cb9ce04998735b0b574a7d2a7f3693d2b6af049d1c99454e0ab0e50cae4c121a0846ffd4c9befcb557c79e2bf1b7011f6e442a63c95521fc0e45706d3a9e1f22bbb6b5045ad8f882b6f04f019fdb04346a0446f96c6d17230298ce91dc2f17ba459a1ec8a800ae34640b2590ac8a479e1d4b772209728b212612668fb22528d43e8100d10c52404d44d867538ff4d0dfa5c66425326b180b55c442f432baaa4d27713c45b440e745592e604d9dc7ec74c094c039c1534ec63584c6fa8b48a8bf9c73a017a8e92000339c5441c965b4344ac8fe6fa99bebfb7d3ea7edf996e93825a36632341489c941fb006a4725d14f2d3fbb3b7fcdb78d1d285e2e97e78156adec058d8eaa145ec3d5cc20e394c151f3a5a7b7e46ac8226cdc145b203b771318ab09355db8d01cf538274a43eaee1d8ffad3adf25372a8621ec85a60b199a67a3a13b161764851656b72452344352e9327886ddb777d2d1aa8feea5d8290dc56a66097273868b08b1d5bb8c6d497a9acf77b82d86a7a641f8a51f22ed2d8a3b7e55bd633a206d3df91b109487f6838c353e90344dfb4ad6a6402de7d523a3766826802cbf090d7a9678011be51e90caaf63a94c084405b3fc612bf9a339bfdec5a2a70178e80bcc3d8a73da7b098bb28e98722a799d824377d44ed01c0716be3de504b3f3334b91738ab2edb4d8416fce1bcc31f72234209fa7be7cdf55302d05625b6337147e2503fbf30849308188794cacbd1736f440a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"34706af2e0a04b453d9f7f9856e3a659cda411afbe4f5a7863cac0553f0ef14a","proof":"f660cc88a1052a0cefa224393aa6bd2f8b14bdc0a9b37c91bd3a929a5c43bd7edc5f39f2a63fd2d4046494f5424a5fa8615e3d2b9406cbfff85390ae3ab35f2354b1878d8de90853c7ba2475133988e8c5f72fdc0e40e5d83d593eadddd25c47ae114d15743151ef5600270ee0de8a6cb0d9d654e19bf8cc15c75701e9e6075f9b32d0f452f64fdae2abe99b921d0e22bbd8522e5b6c4bc541b02ace52746a0281caab624dbb190b6bc1e541aacea3e8e54961d15f449c814c290fe3ab3932019656b98e091be81f8128d9e5b08b8639bec921562662a451587f89a313f1900348d7eda4bce3e51c22c511e29af186fd887bce54f177d1f00e64a4a320a6c76396606ed8cc4cca93147cf05c00eb6a8672fafcb3d693bfa69856e7c5d2cda43f7c8d0b67cd1f23b6c09b9ef10f6d226cb21ca439642cba9244d0ef37c9dc2051c01cc8d3791ba1394dffa0e4f76a7e5afca34fa574f33a224d79d4ec2d4a277b44c735a7dfd29b575bb5a0c5218431339e1ef58d27f53567a63273497d5c402eccb37123665f363e67dc294d2c5155bae574e43bd1eeaab8005ec4bcab7d3031020c6e71c355179c35e434477a67cc173f735881ce8cec384d26fb6e31ce57747ce375267c5d9823685b5b9925f7d82cd6363b8e9cd64b7f39ff2db9d1656c4d34b9748ee120bf9977d092d8b5f5c330543911d9e0ca5fc9aae8582f3cd47c5b2e726e63ac78f646439ad9800e8e5eee6250c4531c863d7dcbacf58fe6fae147bc484c5aea3aa4b30674d561a4229dd36962856f848ba5fb14124a510bf6e831acee8d70c9a8db042402d401a7fc852f3b5ccf8bb02aa1ce86e36e45961ccd300b4ccf4e7985e77de5b8475839299926b8701f767727e91fa005909854537e00186131def94459b17ac12d06ce196c6f9b7f7a15a38e987a4e61d3fc59876201"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7a763d25a60dd0723f386309b4e5832f16d7a20b6d0146822c92c69b68e9ac7e","proof":"e8fd152a635d97fe6ffddea4efe38a9ff0b144a9db319c725ae35fd49880f34df4595611caa36447382f00a6ba7320c4c2c9bfe25da9095d040ddfe9a65f4d001a1d1f8142527422bedd827f51467aa4eddc64aa5433430479874d912bf7b916069b1543e7e3ff54272530f7704f3d2a263693c6ad3411853edd28fd0a481f753144ad5a731a37831f9c8e3056b8790f95f0b9ece6dc2e375eaf1c5d5fa24504073dc3f4ed398f5a5368530a48942363eceacc1575e741e9450363fbb366a30ed68c1ec021524882babda74c08f0771da18141123e82be3d55f37e3a4be49904ca5a966d015421a687373e80788b91fd658f450bd40907c10b767a8cc1636b1932ad48491333e4173f3033f8af4d5789079e2714e8526066f4dededcd97da77be658d69b9f95c4ce93c87315d6b4453c7d4868d070267af957d0571ba54da46484fd8bd8de6ec51cefb6e277687e862b5190514765c8b6497d3b4df746a9277ba802931fc4d0caa13fd7d81814b39830b32c1da2f265e62d524bdde49b2f3e3df21d6d4f66d885ffb28d6fbb94e1788a6274ae48b8cea8743e94e49cf23d182b7254f6ba9cd5ca92f70c4baa1349578f3597a579d599e4ed7e01e8df35c62a7a7040ed8a3abf3b699c5b1b59de799620e275a3aacc1cc0273489d5b0ad71090b1454b507872bb7183771cec17281ae00fdcf63a68835a0472f69547cd6d2e11468cde8dbed8bd150c04951eaf1646940928e7a43aff64aff32351233b962536fd8c78ba5832a839c39826469d5b8e695d7d6cb9062b06ac665ab603889bc5a44b6e6e4834107afc7b6020d6ffc88a9a5c597953b647fa18bc50617c16e450f07cbccd9129aae16630ec3a1f8aa553d304e816c9e58a35e432c9b4708f67add0309f7e128e1c41a0f86dff2616d1e18db83c83b48196d8872079e390d49e28109"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b20da3d52c5db1ff7a51a959cc9432cb2949caa6dbc61a98335f0bbe3710bc30","proof":"4eaeb3628321f8c134cc8156f9eaf016bb9f0b0c74bdb5b69bf4ce22ab8f3d0334a4c2cc0f438e8380161b9056cb2dd2b9d0df7fdbb74cf5a21deb3ab09e9e34b627af08aa90c12861c657811391bb63d64f1b6b0c89b36a0abb50457585fb304abc0ef5d9843b64535b168a65f133791ba4ff4a37a6bdd3d5204444daf4320a3743e70b2c0874d23e55b709e8cf68271ad358a85c6a04d839a758d33128e309822a1c1afe7779b294576441452151b01aed753201b6e049fdd30f835de7f40125ea2be66f9b4a1f063d20ed15d78ffe134f2ec7c813f055307abaafebae9c0a1027272fa038dbd70fc61d0b44743c82e5b25c375556e1c742bc3954591f4e6e14b220a51c208dd846243288b2921b5786171b946a98b7ac9209922f2df798498cfe32b4d22e5e7d26d6f1484e9ecb2ab6a601ec2243697edac527ce83fa3908283caacd4aed12e78418984be24cd85b54df80ffd4053fb9a2cde3d35ef3343ecc02bc9bdc36e64171589cce0732e8b351608470999bb80cf9c9e8bb9457fb56ee5ef8359daab910ac55303afeae5b6f4757fbd4cd78db5b4ce0638033fc6e6884248ada45c8edc0ddecba98537432807dc7fe44be3778ee185de49cfda6ff2e7873c3612753b9279af6e2bc58c966c9c61a9b3b7fd51f979d5537ea4ab01b788470a06030cdfeff9bcd580516952b3b2c89e2279d5101b3747591e4f5cf680212a7d8142603d38c7630787eff90f6480a1b8e7a0255145f6c498e48b59a6f7928299cab03c76ee7800e2f5d61640877812b683426b63cb4271d243fd219fc512687e78225ec2a530d6fc9d5f6597d48a6d1d63c1c07fdd39d7a15b1802c697a50494ad653030ccb37cf6e1f3a5b50cd9b41f1f2e146af1217ecf1358a7c7809c4c1ab9455c8eef1d891afd86c5ab29a2bf2312dea1e66248bcb371807f42909"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7a07a82c6db869bd4859c51e81a7446730622b39cab1069ee987b3c759c22d29","proof":"a4d85b1fcb86e95e3e80fbd90f8841860b1cb37bd4e26e8dcf64e95e45fbb921609a35755a6b28b96eee6afbcb20c9620e0598d76aa7741be23fb54682de6063126cc803a43685fdd69165b7d0f7a00c36f1b2f1940b800bf50d00be402aea28e28dddd56ac801dee21933ee9e678bef826e395a2a8c9f8859b88d56d4864a08b31dadcb7e57058316dba379442dfa9da4a3e74cf16901b613735f2d5a8b6b01134022565f6a7f001ffa66ccfdf4ca539d53c39c3b4d5478aaa091bddeeb910cc74ea4b49959d10f50fda9c60c4093479854a86e8cee1274880f385623c54b0d06e6331a8d1136d2fd1775ca0f793ed73edf26326350194d0219c17e495b7f6d408a85f0d059b57aa1b876e4e67b6e2b0a56c64c9995f6c5eab93cb2427c6f787abeadf286430c04bcb0bcee6585b54f4b1f271d9080190d996584905d299b38bcd7a94fba6df6f5ccc650db1db3a5d1f04a91b0b88b0aeffffb6ee6e964bc16184efe8767b2ae37e40ff46b271b267e22f3ea0ea20d963590385bb423d4a827b470e5b1fd421e0a7dec41449aebd0951c1db0c92598080555718d2fcead576928476db1f6d4e009b8b34cb89382a9f2c99674c31149c89d9664d5f220619038dacc7d7e073f364e3829c603ec573a9656d27e5e4b31b78a768af58ca2a50e680ec7f02e6707b64261cd46de94aca2855b310bbd89cba2cddec318d911283b5bfe92325c591c153e67ee33ae8b85ef30e05f39dd0da60b4c483a0ac454810c15e6df0cde832a0715d03098b6da2905d6235b0df32abd00c9a9e9af2ed0a2b0701ec61bae450edc1de236a4a1fba7cddae1910a64a07b1421b71e2a5573f2514a26dc53623bdee0517b26d45172f5578a192dacad22a844c99fa614af53fe390d82ce673a290f682a4ee2c856b0d420d29306d5f6c28d300bca9996b13b8ac40b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c680d4c2c9516bbd0d67340a4debead256e124babef72f94de9ed5603f0be171","proof":"ea9866cdc8363c2b09a267c265758348e61503ba5f630099266aebe280b00365a4048530310d0df7e26b352f61c8ebb0b93374084e69cedc99a3e1c24f971573f4578f634f63288dfe86afd8f276c59d5239ba93c9f32886659ec8df514573407a5b6615d48368741c0d9a94c5ce2cc6d20e8df7faf5e537679396c7a0acca4448a804875ae8c92647de8abc5a56e654dd3672e0c48f37c802baed3c3b9f0f0ca219f308631ff917f479f47db537ad46bef9f7fc5ebdc253412083f3c771cb0bb67bea9fdcc52fa78c63f408ef674d658306d9190ea33423269812cec4fbbd0212862074a8a9c5527c8c467a468d758d8b3b38fc165d678a413e38f489aee267bc6dbe0b4d592068f7cb13dc244e64550a4983f0bba588865d1ea9a8d3a7586d3c7905bb7eab1b60bdd0e76a394669c53b0ebc397b5e734514e4ae3b1c212961e40db49a607b71867f24a12a8c1fc001bfbb54ea2811bb8ddc8edbb06f2d6577fe771984071fb5dac1c34c8e92761168bbd6418cadfb020c8f7bbb20c38abe319ae29083bae73d9116c34ff65f9343eba313362ea7fa53d7d7cf7a37513104092453e52707251aa93ace080c6d2c531dcc022e4401a16af7a298fc4ebfc59b26c83c8f0d3f472d5d35cc64b697fbf07ba8ae87ede2cdd209f206e3015c7edf1e3e78b2c941637c1705f744853e33b9f3bd4a790de9012a19085bd4988b18e40ad299f4ee5fee5abea1a910ea9c1b48d772bc006859471a62c15d049cf411eb65d03817c9f49e8dedc7ee4e6afe27f0298c2ea077cc00386b95ff5271806a42040a301adf9afaae776c1379fe6abefa6685b711152528710b9650a2c2d61fff03f1893bec18eb91dfc32849894e4418f201f5996b3208265e1bf24e1068ab670517a6959210ed4360fa34f30ce7b8afadb2dd5c19dbabf823e4e6edf2d9e68102"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b2ecbba34b9055399fcbcd2a7db503d9a726f5f47f661ea530e14710a24f8738","proof":"4e48d57a729c8064df883e8de799a0c5b57772cc8abe6f10ddacc59e63840f2b8047995784ee68c9d3ada19a497f877d460546dff16f2b5f234740c7ea7fbb55c0b48dc43da7e131c6e7c4fdad35ebefee0a489061d6aa6d4355cefadaf46e11dea1c3b3113dc5b712eccac638fd3af57e84d1cb81c185e633acbca6cf82ad44674ffc8c3197e5db76f2f47c3e9291bc9ff5de646a7979bc75b84e357e944d0687331c36732d43e9b3b1926f82aab139ddacde651f57eb6c5bbbda3c635bd60c0e5b1f1d270a7ae0c30abf2ffb32bb30222ef169c7110c6d60095758f3a8c80d88ced4a41d9433babb2f033e5f6e6f42361b8bb655f6b7f453b7aff7855882244284b3a97f1f0f6ed473916aa4fce40beed2801619033453ed565d5799402f40b81b6f80965e291e6fe1fdcf9b9667929da48c15516f0f6fbf84d53a8e389e0952b18f2eced44c7b5f5a2360ff0871787028503ccadd7593d874c44a198430058eb35bd13ee4760305e484f16170b3591f479ce1cbecafdabb7bba8eb738ab7ad6bb5aef34189464a5f86325cdb09102cf394292eb6f6035d94b8ef25533514e52dac85d9cc6e92995a83bcc878f31149a08386c20fd62f07cc6457fa467f064b088f9fbcaae5d44fa2b45b4bdf0ae22caea85c208b75a050828ae3fcba26d0cc2af9a208c50fbe8b87977a7ecabf8dace0cb202917f11895e19f7b3da514477c4ea026d4d89be699cc2104de186f2e8ced870f1898fa4fcbc78f5055706b243a207b7fb1190229e01ba76740b53fd51a68d6732da998ba590e0082c45b8917e9c836eced59b8c39606c7d3e939df010816c7a276b2ab4f4fe7d60e4eb492a43a31a9e60457c878aa2abd0d5bd3593b5b88461d5080306574bdbf6ed7021040a214d30f31d9014018bb382453890bb1ad0a742a75f98b54ec3bfe90928d0f807"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"125eb45622c50b8933c29204bd13eea6af5263f016491af2d263a58886864500","proof":"2ec26a9902971fbedf80e0ed56795b8740baa69ae202e723aa4cdabbb4a5a17cda02311d4aeac5f61844014eccc065eb7625e38b76cc3f73bb408adac036770570c2af1ce5d82423090b574ba09f1b495e23c983f96d15987543438c2965922048031c40d7f2db571a140098de6e3c9acf5fca9d1d6697bb180d0337c52c1944c7d7f2f6724d1a835bdeb615f5efba3aea6c69bf571cc95a85c709903f4ba00435f252e97123999e8cf971a93317c658b42360ed9542f8681d799a85765f4e003d3e81d99e55cf20a091bc6d2d81252a98506009c47ce15d759b87b73b410b086eb96c6e9c537a8c843ff9215bf0204ec6b0ad321a61b3103fa607a1efbc8b17ece1f0e35fda7af460109f55f9532867c3ccfae391f2bf8209f894407334ba4db4ff2773acebec95198ea8ea458a043e84b74ecdab50b5c953e364a77909ed5416986f0bd6d676ccd706023f09004c8216219f72803f4c8e11f045c4225d9f6c98c2d081eff9bf20b091bbbf19ee473f82c3f3183dc11714de08cfa8b1d6272a08eb2b7cb1c12d949b80be84e5b5fe4407e5716261ac7e7764b76288b729ef508689f2da9a9d52f49826b4717cae434add09a060e98d058a3bc5d3901644a46eae0ffb2ec92258f2703b68befedec29988c39af42aa4849ab5318627534302748468980d7210a3a4ee958cbbb109317c67723a6071259789ed0656d46574934cf80b35ee812196b3a096571f72e623cdd2ccecad1bdaeba750d85e7e5405d21e4ebdcd5086a4beaf02f32ce351728c9dc3643766f5067996601945baeee205537071c35b2204c6836ad9205dbfc82b3c58fade61d2069876e71ffc31f24e2132185143f6636cb369b3a1ce8be1d755b95b328122bb139d5883627c2bf7c221035fc73aa833b9e7b04e8c1b6bf702aafeaa5408c6420a4af1671c42ac8d9b0a0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a081f27c486ed3fa9e4b378956f01169d1a3c889ab4e8512973f69052acedc19","proof":"3421570ff638766ebffe612b3fd8716bdc3553561b3d720410e7fd2be23619107c3b42f49db4b358a281740e5bf9938a03f261d82c95f7a7e643cd7de6f1fc46b691369a4f3fcacf932a2251c8c6f57e559f8d6fcb72072cada0295264a3435d66f97e2489b78f261535b17c7f70eab7b72fe0f43c5536c23f99ea1a540cbf525255f4c1629e842b8aaf848c6e8474fb0164f5084b2f7f0e1fcecff959bc5d0ebc99c264ce5d0e37ce9bdc6f7aa8010683a516da839b58cb7363379bcc0dde0fa91ff02086de7e799ccb863dd0d776756cecebd8c473b367bc97f0ff1356b60714c763004c8a71559964820b98f272404d87b072cb6266e8a0c375149f25a93c7866f14d7a3c5b3d00b601d2eb0aa23b4f0fa1a24261b13cf7c1323eed389f72b4e2d9e8107a4afffd0c5cf42428dfc05b6a005b9dbdb82c302b1af8887dc829b689167eaf337d6610e50f85c60be1e883ffe7b8d987987c2e469d60e2e32a195cc5f7cffd8f9a6ca49f24def519029a98fdf8588fa45df948e45989b1eae94168194167c3bd7b617cd48c62cc0c977c80b6e17929684de78c72075bd9eb546252bf496cc85cfec5cd44164a234a885e2d4f662622db843cc7985d11d254626f8298f4e11e65f6e65609eecec36ad37b2437399d87f1b3a94cb0f2104c2adc6388d534ba383273eeb7e100560934ac947c27831b519235fcfe27731b0bd8f465d490c167bf4a46093ed616e0bb3651777c7df30e691edd63710d0fadfd45b55be4c951350cb59147ba84a8a0448a60a0b7468f25f7a2eeb3666ced7f3e5d7f41f2bc81a761b79993857a6c992490f02c51fb8765babafb9bca8e2518ecb1e87ad769014964ce3bb24e45151ceb5cbe6171b38e5edbbd0dbdedd6437a8e1cf3084e9f59a983a70bce7647e4c02cbeb9cf8aaa71975ceaec4862bb86118fbc7b0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"661a8a231ba1478dbc02060a89ec32bacee69a5f948a61e35cd79b14219c1f66","proof":"84c49679a4b5c115319d209ebbb9cfc3db90ba2675ee6f497109de052ac3c37f1a6843716e7ce894d7647308aa638e4bb698357032a53770165b195677480644cc52f72ebc5fe15b0d639ea1604f930257ab4bcbbf3764add46e7ccab4a1a33522f22a47e08ed469a0e3c8a594d6e1451a78f79d51c2fa442b119c001e3a5e15ce758025e44d9f04db618e4839ca781f228d1f0ebfc5dc824bc0d4787e100a04303ed61a5a2715b40aaa62e89acd27df8f18619b01fc058a5996709514f2b308dd1c9e96c643bfcf5bc122dca2f2f8969153c1d8bc25b594f25b93bd15091b0f1c7b2a39a2c16d4abf59787f9bfdfc513663a379cd3e1b146834db8a52ad520e86b6c10ce1bbc392ac41216e1726621d9f56422c2da2359fc24d2952740f9b5242f3d4b8906716730ee5929a00384ffd52bec95fcdd6bff067c37a696557993310fcd030d573c60a97b851241b4088b0ce09f0c4d30cf4ee1fea90d207f92260b4b56a3bf2a1c059895262985b163f7a6ec0168ee7f2100fa6235270248f7c1146aac13483391a70b3ab0a14659308fd7e2dd352ad23c6be5d352b2bf041f16b9e3618b5dee5d25a784280d6aceda8dc585cb93d29f18714a3d40f03c1bbad3eb436d15c3daf2b60b7c51d733fa86d4ebd362290c969d287d4ce6f3c3b7e2d3aaa858dd4fe757c985492e2984ffb688e2ea27572e0bd28255d16cfadc7d73359bcdea28d7d9107936819636969176a171aa02148239dda1937296921bdac555c7e65b20335820c8c3bb10f432ccaf5ef10f4055bd5786d054c9f237b60c3d86f30c916951be2dfc0b48530ec03113d0abbc3f3ab4b84a31f7de555fd9bd6f62c3146a4a90c4f31583e605f12b91f6ba6173d1952d316fd5a3942fc88bd590c0e8004010da83a6380b572a22eb990df6f84e80537262649d201f9254210a36a0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"422b9b8a5a95d2c36cb8df0c5461285c5da8a3efc9293dd05be11ca5f03eac11","proof":"5c33a2f62fce353ba444d6ac221db7be641dfc9606a05b2583a758702e00813506b33b145e543ce38ea0820666552d56e1cbfad606a0934f0f83755b5e73261b1efe04d71a7bf09305f52cd101524276352e5a71af074c2967bf8db71bb009617c919fd68283e16cfd8612db6d8bf4acaf53184544034342925dabe14bf25967bbfb06a1a59dceebf581b09062e748aaf3d133d717ff429f3cb628b45eb33901b9d62e67f9b668457849d40b383832a3acdf10db4e0dd552470127a8dad4a605ea20b2701c5e16aee75e4ea7782eff4137ee858bea6c9bee7544cafdc9816a0bcae683c521a4442c2caa28cc503430870a7314da757863caa452f00c5b50c76714a3fd5f83769efb62043a4ed7d51d502d46137a4e21ee47bc44a03cd2c4df7504ca9adc82affef3d3d1c02a4ccb9a7d489846c643a58db64ac167c46d7a6835b49849ccd4b1ccd5d44fa2027a5b9d357f0f0443001f7458f90573e35fab0f17067a6065e9186d03ac877c4d1a50028f5ada4c08ee560b7436507a732b43325f860f1ba87e270126434a9e879b1dee052092722e423cb5c981d39109f0cfd0233612f87f06c0de77c08d21126854e74821acca7711cc0ce78cf41187e8456a298e7f8ab93c2ead3f556b554981c74e9721d39788af6b4f223cd4704f30443369bc93e1cfdfc19ab323905f09621c991f087bed5688e042d1eadda59b6e31877e1a0afb92eb710862ed6e89dde76e3c938b284989278b79c0deda45e31dac0b5d627084b3bb8da59e383285063db04f27faa1cae33225850bb46853b74db2bf7580830c64d765538149fcacd620da3a1c85278e00bf98f26dfa48d4f87724a7092245cb644e0c0874125cf3906a4242e5551388f5f7a0299d7f84b53799bd9804fc0c36baec1c9aa72ebc4f5e0b1e575c021314ff9edb0edd30e9b5a581acfb0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8601dfe50447fb03c3ce69c9e77040dff4f6337b3ac36d5987015ce7e477125d","proof":"4eada4b852a18e0b476699e84fcbb5e1412e012747759c7950d325e110898b0ba605e8f628e85befd1357d20c13af29ffbb3c6e28cafb5b85f8082edbe8b3030b804e98e1904c91ac85dbcc7533067badc24a862cafc1f07f5817b2b463fee7e5ef83751f5caf17e96afdcb4e3b61336c2e808cd1ac60a7a3a0cde536e39221278d9a5571f1e1986d12065560148fd82b99c2781ff612390380ae4b60c20a500e2f967053cb00b9b5beb344141dbe9db1e05aa3db3da50ee2dd40cce723869056c5be18610d9ea78888858c6f55c7b84a6954e6f3b2035811041c9638514790a0cbfb39c1c1b7effd87b2a0d0d4cc8b04ef569db5ae49fa9a6307cf701cc7f2c027dc7410a88a0a6760b844b16b0d027e5216afdc8c96844253bbf684db947253e01a15e98157af00157d48d9660fe3a0b71d3418e6819a3c5527ac8235b220382226491b009806dac1abe11c2bb8cb755929228203c6e94cbb471865e470822d8c1073ca0ad7c6ce67652c5492bb8f1060a1f94ae60f99720d2fc2417bddb6c10595561f9b49de27f6d5b307e272f1c461e0152a34f7e645049329cd5529c49fc5eb63d536d9bda710aaca245c901f0908f336748431a46a0de39619da26c2a940e901329ae5566e9c73cc37d3e4095ea010eaa9f6bf70ff5aea9db0e285c0cb4b17cda9f1108cef995065ba76637a55a1c025a98630aa6df2ee2dadb056a1e2a2635d24e29b31d68c92ae00923826e6ccc31ecf8ebc5872c1ce8b7a445145eb0df388ff18002d2046e72f00c42ffc547a915be5684092bcd911bc71595f95c16d789edc148c44ff336f1a5adb9cfa5e03fef9fe4c9c79cb1a12bcae1d46c786f56d40d7f9ceb8d714fe1387fa0ce10ec002c44471db8ef410860a6acb2620645153e2c2f717cb38186b380e7bd4442f591a26652abf5f8b5cbeb642897f508"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7aed436d3486cceb55f2718a7cf1b5c4b56349ffd0a8e3b8cd34981756bf8423","proof":"86a48a087617531d000a17a9cfd7f80ccc17159a8a02b0f45d622a0d5eb8eb68c4a181a8669d3c5479cc76c1289f45939a4b62ccd4c46abc7dafac4bcd65487038008d73b418e19bb297e3868d047c8b0b92bc624cfadd73efa196e18806e3481a7e6e386b432fb9aabbbe15724aa2ad804fcaea5662e6b67c3f56eabbad840d382e5a1cc7ff5733686316a3e95bec8be04ba9f5162b40f456e987f728d22807720cf82ef865767b0a9758b12a961041c19b1be95133619ee4e8bea4944077069198156099c1ac7789ab97740079b0097a1ecf6cfa0b290f015a42b063fc1f00caeed41a9bf96d8996f5763b0bde82e8ccac16b47b97a88e38364f1064023f58e4762e1021c7146e84a1d6b056a45b3aeaf3dee8e9ac890bb49b2559a47cb450189cc11adcc14025548dc5be8646d863aa73584d48bc2fbaf643230b0ddaf21f88723b66e0d91ab8d3e2fb7ad6e44fa2276a177247f526300a7d34526b5b653e4435a5ec61fbe2bc0ab154f3341d0c5306e7b2367714186824b7f930ef8e5f679e2865de3f18e17ea726789e59dbb730971bd07396d377014302ae5acccc3122e02e9475fd1fe075f71ea79f18d990e5736b185df51c963406e7f462c7f92e22c6b50e637dccdcdf7eaec7b059e10284602e240d56ef514712c864be3d2fcd24c06913dcc255a0f6a2425a7d1922bfb32390ac15eb57fe6bae3dc420256c5903f03f33f50f7ce344362fe2a3441f1df50cf6b29a7673a5cea80da44c0ba50a523e84824025f90e248de67184ec7f5c78c7db3e15c7f3a8dc39444089672fe0491ae9605e78360ddbd59dd51b882fe86276885e38e88f430c3e3ddf9ac2b8a159040865889c0cf5e595b69c5fecdea5b55e55ed33d2cf3c99170a814663b7d20f50596ca796be5766fbde43fcbd9d1d6e2fd6461ab8fbb1d99c75b1c6a3878500"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1c307b6bd5e4d213b5954c377ff2abe1c2f80de0c7d5c2d4d8e6459d8ce50103","proof":"a8a2cc35aaab48ae4c4f804b0393d2e4936abee7bde9671505299009bc387702c6973ab25160cdbc43b3a6f7699cdd1106ad24b3aea3e1493116dabbddb5a115d0e4307f9d813b21cb97021ae1a5c9d939e1975efaa77485e04328fef6e42748eabf72e4468703580aa0cbbb89c79b7abf5e9ae3529665512a5994b9c2d76e7c26a226292a81b60d72627ecf96ef23aeaeaabfd44f6347daf834d08d2f0e240642a0d7c21f8e8b8c76022388399a4f6c43a6444d4b69dd242ed92c0307d10d075083f66fa6da973df963cee2b1998b5e1159e9ae50cb8752a54893876098d8072c7da0b33beeaccecf49f1daf05f55a4f4eaed3bec9d3ea74454594495cd0828787d27da700955db125da8a0f62cad06cd4ec1f5ceb9b094d151c8f00d7ee44c72932ae2283524a42dc6679d0091eb03861f660b8c0dcd9a269c7558cdc0a21da83fd04a44a52278eadd07077cf27a91ebea9773e6979d7a4920aa87d982c7227c70c02069204df4b3389836a0470faa82355028dd11babdae6637092d05272378927ecbe5d57cc5168cf3edc3ab42cac3ec68731942150d6cb4c1b5675ad269304e64a13d90f7a874ca21abe2a1354889597c7a3d9d9c48a4628c1dd4f78a108cdfd87d81728b2b54dc1a12df593d0b14b74e210c4d376dd4d4e259fe282c0c246ad18e439daeb6feebe7e78eb7766fb5ed82e98fdc86cd8ca3bdd9a58e2160689429ae8279bd01a6d7ca7d5e08224d6de0ca7bf46fc68704fc69009703a00bc27e32294a7faa0b155ce585f65d52c0f896cc04b9979042032d1a596b07ca266e74c95497556d6514ef17e35cea62d9ac867039cbce90598b837b6719739c39960c96a2b8a12addc1e41c626c93ae76375f24afbd986b09fd3acd34aea03a01d18ab1c34d3cd08c451aa9934e4b626645e35fc21f567fa750d6e548bd1cd70f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"20bf1aacd5c19040e09b58387de795a7026dcb073a5fc9587e5826d4f8b4e63f","proof":"109003999838b4498bcfda5d47406077aabf6f6870d4ff50493eeaec118bc152b2d43ddb2d97feebe9aa2faa9d43efb34fb1b30dc6c9094c9730e22e203d1a038c1e03d1535bcc6f2327b49a4317e079ed7e40096a312280d8200e63e5f4e057f897c65ae8767923f5c49e2d0709dee8e584512c3c661fb1c561687c9d53ec5c298978d0dcc96511882397adb6771fd544c580d0b371d3cd1f489921ff9ed60edda9fc6e818436a5c9377b27fadc86783c280c02b30b80cc91e79bec38643a0ed85ac143e530304672d68d466b09b869b68459b8e22023bb882f3b2a0fc87b03385b7770549aaeba9f6c090ebdcb541dedd5c729163e72c7ce6be6964e98de66369b20e137b9409dbc88d18cb02379a20fac6907227b8854005f1e43e41909359081e331a8d5b1f2243db5806de1f2e205ebba736e7111ce367cb4fa7a705c482491b68f78b0100e94598c67fd077d9516a5ad439ee98ca6fa4dcde4e6c5a664baf202efb5de3fbb140aeeed89863a24894afd24fd1b9eaada4e0f36f5c8c8438244cad2276dd77e1474992093209eedf67c33d1515b0dadea07b4d58a126033c464dec36680e51dbc1a8d9ea9d8a3938dac4ab501bb7153bf8edecd469b7308d0993bfa1c3c59e5b348abe5cd63d06b9cdfa6ef3ee69314fe6ed860bb2e1a210cd0a80d66c01a84f79f58498e8302d8c6e30917bd3f1e8018b718323f3f3d2df87f566bcb8226c6455ef83d081880411cf4054c266c89a5d5655a7f801e933eba6815df2867ac4ad49b95c01f1b8713e1e1f02361b05efd400759463c571c48ac1e6fb9a769c042abbaa4da395e6c32a18aa095e9e6e52be1271590145b3e5ce1e5099d59e4854821fa2c6a876c54d064a5766077a97bf20f410fb1a47a500a5c240874f46abac39e63f82f349fd0cce1621fb404f8668119d2d5146ad85201"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0a76abbadae01b97d66d8af0d15c552a40eb825d6ca2a2f3364f60156791c564","proof":"7685b146373d3d076617e3ceba2b52aece8470050e18c68d30ed774423b579095e5fb2be32921b2c8ea5fb91664553e12bd9a23362db11d4f6fec021aac9532bb680531579729de16a09517a177cf5f447a23f36981f5180f680878ee8218e2e788d37aefd3371091a3e456a053a991035b3ac6b3e1988695dadf2e9f2750d4b1ea602b456fb5e0c80cd048ece7dd9b91705b4cc6c94ff1bce44e6fb089e520d8f92294cea6c9d29bf2dd54ae7eff7f5a563b0691c7d5e1e1f3c03a027818e0b9f8ee844c73f79274fef548ff4023fb29b54fd93667843aaa93df8cc4d845805cc16615b6b567264e616c90e37cedd830a0128f7ff5bbb4a27a0a1377113e979ce86843e685c7e7a1c23c3c296ba66e0f8083cedcd6e7784813fc18775efa52b68d4f867f85dd6cec67e7f8bdd62528b720c2bd5d6104d86675f96f32b0b6d140ea5d41d01abf73684f37c1c593afc67211508149e1452f8ce326557699c6d7fee44c25a4af4646e6aff8f6496d01c649fc4eb3a36302976ac16778ccafb0f30aa9b2fbe6c32a79c6b645501378b039ae8bf6a3674e64a0b06686d0f3edc4974e431d716dbc84a7d2a764bab6143b8219889fc320d83390c3d1b839e1eda964b5c55d8abe6dfdc4b1356c1046c669c3461eede78c349c522859dcaff44ae3019ea53f8d284b126f0fc4c7ca8538e36ed6e25743d605bac63c13fe8edc4033b0a346548cc173765e145b5f0ad3e89ab761e6399c25425ac984be5f35686994e22ae0fae380654df3b4f84efbe776049482ec61b5c85042ea6af09dae69c477742ac13fd9db324bd727af2dbc6c5939e168d562f4523f49b71e703d9237157fe36c2785c38c64c8732cb7ce5973c88f0bfa11c386f4dc46065a94542c2ca8828021d3e1f3ef6eeb1a5b0fbfa6886fac35191b448d2694b292f606a08943e617802"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5a40dc7bc77bcdd3300fe3ef968403df81faae506cf43c48c362ac7de2ad7171","proof":"465c526543174bdf1fe4dac898f982c279f8dbb1f9f41183d097d511cb46d436b20355183b70c586c3720c151126ceb877e224e3128d4288f1711ea1e2bdcf4bce0fb65e439af7858a26513e9a1297cf356a28134d4c1f85641f04dde0e50a0b3aac70395ce341fc3d67f461d65307331e46264a0c95905589518663ebb0a1112481fb0a76f295df12201eb33a303b7a6fbeb4c1e4a418fd4268be39e0d37a067f8ab4753d632c62c2af9dc63591685741560db93c79e7bab67a59c98978740991fbaec5c60d2d67e80e167c2872d7a5220664f1d29e56c4c679d8be526329056201d8f2982dbcbc0a644bdee1409a92f1bbe673675ceb4d4c157c2e60f3f87d16ccddd1cfca96df099f27ddd742d7c8fa44a6fc20646a9c67423f99069b772c4ae915acedbad1403cbe998beea21b23c263081775e70ae37130ccabf48fbd667abc5776411d331194e055effe0899d3df2f503c0ff7a9d1a7d2c4e08fb2ea7700a07c27206ae95f87c0c4e67e2c23034726da7ae5fb9f3122a1668f6ce5aa2d5600bf1b55389ab26b89175eafb6c1b1f3c53b647aacf7e736fdf97424bd687c2c211e037ff0980225800df9fbc53c531aa4fad6fe0789cc497c63c146650c5ecc7922f9803cb0d72995b165915b01465eac2cfcf5a3a51a1547d7718241b561e42e60473ef435d9dac77ec17a2a423563588d0e60793a845960cd73cb196f46d8ce6830f93e63c6708df180c05401c3d37faa99406d44832c2fa71356e3865674984f24793aaa9e0919b02bcdcc85ab4da41c9bf3665de8112a3bd6f53e007156f01ab45c228f538c9eff846885633ed5fd9f5b677cfac20e69a062c785852aea44d6b3666258cdbc3b9e254d71642f381219821b2c3f7c4cf98b42d69bf706bedac429668916e459a01dde303385c461773c1d9fc6e3b4481e09265653830a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1827c2bdc8a39727d3684b37d51be8e8efb7830da7f4e8fcd6779bdd4b2c1b4a","proof":"fafe9e2876565d594f399dedbb271100950fc3231e3f8cb687bd60cef27c341c7a63103c2ac1edbba208b912e9ca1c5c7b8fa43cda78078bd21ee436ac19f8722a1f3cea2106d1a0d72dd09cca3659190b4d69d0e40e2d6aba32f97dc071776a7803adf1375df3e52315b799f15431f85526d7e5a0998ac01f798991c96bcc38b8fd561fcc8c38a8e4790faf7f6c90fdc96e48a5238b865b159d554369daf40620e9c74d558cb187459242163d6c414f7d394d482c25750516dd9a6dd63ad907159519373c8a8e78bdb6db18654d6727154adb3c7ac6a6442c8a1109a9c77c0fac9a6061f730623d3846854a730c685de81111ca0eb267649c9860828e43c2055acc62b964171e82069e07a69ff33a4a98ba1c9e9913312aedf9dec3d0f7ca179ab340d0f3104537729d54b625ae134d84bb91eb571972b10933c1851298eb36562ccd6f273faea6b899d5f500b9fabb3cfaa64bdced86cbec5e4c2db9f5e1171821831003efbe5eaf06ad82023ad2a17a6211d4211c22bfd219b16230d32e3be659fdfcba941e55151dd154a3c1e5f6fa6f17923735ff9268c0b78ef15fbe04b446ce18495c3c24711e4924377022ccc397657789307436156582291ccc687bac3c3ef73de02284d4ff808e55ee6b99734de5e9e4dbbb57eb6436af1300840486397aaa6b66879a81e77ca43473ddbe8b5ded9ab30193f67d1aa135f5226b0034255cf1937ae211632c7c2d78aeef0d09fabb4cdfbc8a16f2fb90df187cf13dd6a3f02b5f4ec184f40347238c3abc9a1cd641741d673b7a4d86990884e3db75a4c2e803dc1fb7c7f2a40f6b9ca755c0ccf291b54d1cb283aab8670db9e6ca22d74ee4e77ea8ec7f142f72956997b4546ebc8a4d6c0ef7fa387b12bec84c1d047929311d66165e1ea9b4e7d4648a3b1f8203bb2f7f7d3e70e3f61ad07fdaf201"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"287afb53b87b47597bd1be34d54e01b38318aff58df361f1332ac9987e7d703e","proof":"78117937be16f282efb15a331e68ac5021b530c8924a89c76c6a3128ed43d1353cbd8e3ba03305d7115926a26f11edc3c88e92d2966b90aab2267955d85b7532ec8d994696e70806830f8becd2bb6fc0f42c7ac204f52df0e546c4bab3baa31f22d3a5c90b6f487ded494230ea4d823f6410028f121604c8ecc5dae2806f13632ea6b47dfd87fcf2a972023dc07a19f6f65fa28b76e4beee572f7b1cb61fe80cbec252c893fe32ecc4beac49929f7c16d9a1fa2bc47f6e17d75b3a4106832b04b0b134f2a70dfd519ead7400c1e477e39e36125c4669d0673a1adc5e739ce204aab701d52555e79ae2ecee56b3dc432a4fbc468b65979db6ee7acf74eb1d7b5152006c20d4d73bdb35dcccdaffdf4e72b33e653a884e53d0e49069165b4ab212466f34cdeefdb2be72e265f8ea67daa8d59ea014e19e3c9ad4060c3b57d01145b0d8ae676d3da04e10f44826faaf52636d0a4531c1ce01e5a35ea96275a2811bb0659bf18f05e3f38f52fe26b00d96b8631d0ca7668fff9d3aa73d535381f30c5610fa78422cffda1a9be048923cc822d626eee5ff78b36789c9faa1f07ad310cc02a86f8150cd00417f58d2e6b9f8d7f243a2908b3bd6269e8e1cdea509f14940c7ed357badad8672ffa31804f0ce73392c29be1722153aa3830216641b6b276cebbe7bdccf70bd74d1167a3a3f925598183b8266e8a3a45be61e108647527ab2769bf2f8a683c7c08255c66a370dbf36cf06f9206a530ad52d993d98bbf1318632c8bbbd09183b798e53c53455ea65368eaeb415fea7f393197b634c649b0440b7e0fdc04c86b8cc7adabea93374d7cf9db2ea73cf9fc13d97a9ff2672eb75888e55b7d272be19bff96d33ea3d999dfe1d270db0dbc2a14f073c2a3d881b042c2c8344dd5ac96f93fa45c136785c6de7e5fb2627a3fc4301b65d93dc68770f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5aac31e055d424af8a7a1a7371b28cb421768f963e3480ddfe069ef1c7a2a142","proof":"d6e1768bab8d513831287fe49e8b286be1ca85b39774a5a479d3053bb182f770965ec205257bc623f581d1cb1853f53ed33344484dc91dcb19c748b35421b26934c8a47d1649144f910a9db5bae2b3931d104cd2959e47bb0aee6478aa41e17a149cde819dfdd643fbef37fc0cf5d88ee180679bdc31eb944026453cdc29567dd08ce739bafe024e277d4c58beb1256a378cec9bb58e517fbef3730c01b4ed0f61367fe770812c80090bd1e534aa8c932a99ce72374cb7afcb5280d20b6ca701ab127085938c1be8dfee9a6fb16c31b4a9251262a2f49508138789f324c7b001ee0d7cb8c312d645f9aaaa685b82070a40e11253838b3052e78a96c5775edb4148bdc25ad4fae355e29d0c81793f1fb1d052c5b616b52a1ab814b2271ec25a24cadab1c44ed3a9c56dc6222e250fb5b7ea20ce1804e3971ec13ff8712c3d22504aeef8891045c310887dcb6c6240b279060c44f96bd2adcdce8957cf0ce96f2482ca276bd0cf2064591a56b9916eab4195e3cbc504eadb04eb0bfa492b025223f0480233a0dad7fb76e071f7cc9a0f83139127025192e4efb0388b9cc44a3e6364a4b5a4e86f05e139de54c60e38b32a41a73eedcdadc6fcd562eb49f7660a40b2d0d51f52fa05635449a3af018674fab5e8a94812982095fe007062b2f919583071880b56d75d0f61194d8cec5fec3b0d0e630943ac0f22d0af9b0cce100a454ac523a95821dd41370d255e0c91f216a5f6701deb338e06838083761dfa1d1c4c16d54c16abdefacd200bf24cd6716ce4d8376c9ea0055da6ee69d48a049022a8d559d0ae97fe209eeedcec7c6820734e06c68ff636eee15debb0d1aef509730fdc041ff16a3adf98270d99d0a45602fc686819deac4c6a9ff618268953650f07f90173b15ea7cc0ec4792ea230154bc3ac755a79a8a5f50fc1dc34fe6b7809"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5aafa02f8b642af292aaf0dd798bf7872c272523c51a40f8dd0dfd8326857c60","proof":"fc88553a561d6ba4ee76ffa356130f30448d6382e4a24eaa02bce34c4b14e340201ca393e8d3d20f8c7f277b73578a27f3e705c43ab2571616fb2bde6d7b160c901b6bdb86fcbad94f1c83e25e7d0f1e67eb46ef8f70cb93bbd42ae771edaa5dbe27b3c535b54b346809962968ea02290cb8d662c47215841f382115ef5c2a53a09e5d3a192032734214ccff250886182f918f8aca9d3559d84448ae5c7628049fa8252afcc65c13a9df6a5e40766928276236e6eb1bebdc6c09cb129579e20761ab901cb6949ca5bd4547c7d7bc46aada1bbfc7233ca4bb2d7ffb4452d99905787bec8897c8750604d3373dcaac35f8623ba27fc852e15d4a2b10e44079d65fc604ad1d4abdeb551a746a0671dd711459bda755156778c4040783a65d6bc515fee60108c831bd3d1d7dd43db0086d1ab0d6d42050f5d2a6a27b8d6985fdc269b8c2edfce830067721a1ed59855e004ab489105c553baa1d5c6a85d835dea706b8985cceed3ffefeb19d58c6811e2bdc1eef94ebaed57661b162d73b98e4753418d4e94be700d7487bc96972a5820a9a935a216384ae6f3dab2a11a0494984373a2bb8f87ff0cfc7b8f0ec1b64cf130a4bcc299ec4b4601ae5ef36864b41c315ca9d14428fcc901418e1bd177c94a328f9a29b65e90b8602219a56149d710b2be2fd7ee523ae7093f32f09ab65799ed17d574c4a46edc441561418803fa19e100cd06b954c5c409874f5a71f308152d29e91cab7a99930787b78c08b09bafd63f464ab4c2074142e07d35bb232c5e391daf13b03421807aed206bead6bf8e735620a087746977644d2762a615b418fa7fbc5e261906ac99d18f7d3dfe9614b6681239913b53e5d9bcd17d211806670db6fe0755aefa6ce2da86bae33e386fd0083e12673ea6b6d5064f1fc49065b047efafe27cd8afddbc77bfa8b34d037a500"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b01685260ac998486c59b1730c3ce7529ac77b98ba7f2e169533a5045221b305","proof":"2c6847a1d6f9dbcee230c5869c2ce92997d0871c2df3ce868a402ff52b779e07dac2f93061888a97c73549af8d081f672c5ceb6a3ad2b2a35d2b829e6b05e2715a5ffcf649a6f21cde11131b5be104711fac72e24f09085d5b4aa9724a25d65e58ca07512e8ebf1518d4981823cb26c0a8c3b1c7adcefc670ad84a262b354404325b044ebe658089bfa5f44c747bee4cf1fea0282791975b582130ca1a290c056ab58a399e76b0f4bda3d8ba5b4c58e153b5d8ab80e220ae6b2cb276ade4c2042fca2f43b0899a52b73621e18bd62e7450ab1588728597e5e1dd9450c898750cbea85a3def6ae65a14312d7a6aeacf0f2d440eca467989675de08d76be0aaa7eac32129ea3719b8903f4b434f785743f42cd8be2e43c8a51798b890982156468b25f5f470b9102382a6905bc92126ff5c4409b9a7b6ee9abe5be9ec15771a07fac0358ae7bbf0b1b6bbea39c542e9be374ade1eb403756134f6ed7b4c318603bac0690cd0fe96d84f646a97045b70e32858b12dda69df945ea9e70cf9471f91556006bebbee906447fe973cbebfdc414571ecc0a1530e6669dda6bbb851d71404ea5d9b690684aad1bcbc689fa42680b25d575ccbf6743ab9f52e6f0acc16b755c4a642b44394383ab9f59e0e2bf26ee189f8df30e9650f65722a4cea4e09717b03a1f6caf7a8caebf9fccaf09c7a24365c8b46e1b9e4fac42be65e19fcf84571aff3bb230c523b42caedfa2048b42e58dad52cc4e690631098d6428735e3a14f24012e4c454509f65c333d906e4a21fffe01caab84f17cdb517e99f35e87c53d4b8ab2693b3536729a83841b7ac7a0dc39a5e295555efee9a875604b8bf577b5ba37476722784c4290d29c357c00b14d498688a8b025e00de946e92e89c19070f28d9e8d4c57a4161a729e2ec48aa939e4407338f72d0b2228c75bf6a74e20a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d886a5651811ce8b3ad1cc394f79e08edc137ac7cfd024fa14c2b274640fff4f","proof":"40a2e6adfd0d7047a600fbd19c188bb3fabd91bd56b9b78e79a8eb52a1977f022cb9178dcfbdf60a1be996c65f0ece30d9f254c1321ff2e8d2ed01598ec6854efa16b96889d83a44959cfffaaa1e4996a8eccb9b360b91b7bfd86a5d7214854432ca510ba4fc1303060c67a5e083954e123a249cd2e191448d2a14d16385c523e0170ba533529990f972e8357b7639d39009b3a06acb0970731876a09e466203c7a45624af3ae306cc43b303a1540f4f37fe42b99412a81940b07c252032940ce14b5a1564074567db67ea9373d0cc7173f6052c5a1b2ec0360c62b23d59800c16fe6a5cffb4c9a5ad3d3d5e1515881a630d326f6e61a39bd6b55efb48e1013da47f0eb1022cad27319d2662fe46c327274c31e98de579e0138b437c3dbf1e228cfe40c1551c4a30a56cad12b2533983043f1bd7953e5bf07f84dfe6e2671e7ac0d1f42a34266457d00bf37fa0a80967f8ae5b5c1c6e237161aada00a05fbb032e5cb19659eb709f7b71f757ec74f18247d7ea9dd234ba629827c06c4bea94387a382c53bd1b7f0a1bc0e36c5343bd99535c8b86fa4b45a188d826c5197f8207f44778821055bdbc09c991568b7b78b00472ca21ae050e9822e5a37d5b2b80727e67bc2f502a037d917c66198a432e3a1eea23619856bccdc9ad39e958a97053c07ad5f017c81d74c32b2295e02f144c74e21c19536cc71c19f0d11176e7fb045834105c818c96bcc8faafa72e088164124549c8bf57c16dbbade082e7efe8496694d961ad71e252907ad290f2ba5e3fa17565f98dfec9b2baafa1eb6c84e82d80865d7b995ebca8f310ab123db1a2745561385ba506542121720a7277bca10af1000ad923c6a60e30a6acf86af9880cd1a9b4af3aec7be68e125cb024422f05265467f3d194101650bf9a0224c38b81b49d0f86c76b7a35a0054e8ae2357f0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2ab1f4637cd5fa4e5af77c73bb418f8d38c36d54bad264b61cb6486277345c32","proof":"2029829edfb3fcb76488c31db3722146506a5a028c226dc04382980c638dc70c0c32d956c08ebaa0d4106a0968cf6f5fbaf5513724f5367b3672708f85f414018072c3ee2d2bad041cab6517108e47921d9975a61e846e1b6c9dd031336fb72f003811851e764f5a79e87375bbcdfba34542eb40b7f5b64d3d4b3989722c0b030eafc13ba821a4f3864cbbdb0016030fc93a2941533e97f0696e525dff9ca80c5b045ca45b28b70ff8cd7e3ac3430d514032bf9a251e9cd7eff5212305f6ce0a8e9259fd7b1218802b739cc8afd8174c620a360d4de9c7820a6833da4716db0a90250746c9af955182aed508eb6525c44ca00a7c4a1d9649c2f0dc7919feb61ee61102352b3aa517e4140449e13b74231abc632b38cc1d3af922be51b3499d10c2181e1f75bfbcd1585e6d4b5ef8078c3dd30a9a6d189ab6b014c6bbce5b5c4ac89ca5ae131c79152efb2b0042b493216028564d4ab7072ee7c34cf39e9ada10bc29328f3b0285956ac47923f8e5a61b456ee7a81cfe971ad0cdebcd8837de57048d60f447410566d6d6993874b6a70f3fb34d83dfd44e3826f03614bf2f6c1de2d5cd9006fe6268f48d7b11e0fe605b4d614f6681287ababc3061d9ba5a684fd21e6a3e7ff209bd849880659216236b39bc7604cb1165aa77268febc396ca62a05d524411f2573df97395fd8b80f2d0572f2e8d82ef0e263fbd0b7691b00c022057745eadb17a6cf7d244b0e36b14cad82fefdc9177bdda6ae903687e44a417003fb7de5e7b324f0d9bce8b859374485cc19aa198f1e7a7483b754b8b8e565f3ad1c2bf5848f42b8bb3f01a23749787b6492d660df3a0ef6e47394f123de8410088b49ca70d71d62bd72750083fce8e966c966841e8110ba17d6b1d8e682a0f8886972db6faa16a87732a909197a679ee4d401235e4592eced7e066dddf740f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"38f264694cee7d9807704770c87733ad4304c659252e88eecb0cf8a87fb8203b","proof":"36b926c3dc5bcb404a0fc28296a563980e641b025c3bbddc883710d201d1ea788cbc5d7e0f025eb962fb5ee75bbe61184cb0aeac999b99955398529a2f56fa5abe186dfc35bee121f4708ad51f59ddf2f35f143cee4b3baa5af3d5af5914dc2b3492730d63be27adcc68d5539e462559e265db747d1792c688e99711f1ea5a388f9999c3d8e4d3e833bfd158cec1c934327c86c8f5022b02263dddfa1af53002b7c8dba861e2b4037cfabb69b50b6531c72adfda221a06bb213fd8e3dc162a0e9905e5319b2ca4a98c0d379e0d3be3c4c934c63b6c97dc6c43380468edc439006eeb15fd4a551f272f3f69bd2c77ca6b0bca46f5d2939a8aab9602768b26405f068892ea3d9ee8df299e73d8cb452a119b7e8a9ee5ac13f46504a8dc8319881a624e65a1eae06e4ef58283c1b5766735f8f84fc43bedc18788487815d773fa24904bf962dfd79a8fd460ab6cecaf93baac62faa08ebea32cc56aef911a5f5c4c4cc7749d43240e6991a837505b4f4226913a492bae210b31512e5a5c81b4537b124570185963aeeb2248f0e3bee60b631d0dcc3a81012ca24f8d748726ccbb4c78130b6ce085727b645696c7fd9ded3c0ca2d1ef140083dac9e02414953c040cd4c39b1f0d129bc7a0433a42b13a0f69713367c8c20642daa5dd85610880545ba2f4eaa0345545be21242c5664ce11fb0090849d23f3c88f37582fbe9e961f58442ee94ed08c858f0fd9a966febeda217bee8ceb08f2128f0c6196ce9d79fa6cb2bcc8a38b0ddd264e932e1908b0bf8f8f0ad526bb8f0e605153c778668e702602fa2b1d53ea0bdbcf43d46de9ecd86395cf2f95b8804f06ff21cc9055fd423845450d079970d3339d2b46bffdab578a76aa3a4f7d1354c2767f8806c4d01c0dc5d3463e1d5f8080d295c26b41fc47b139a2f9f0d8beba9f14c7526e740be905"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7a453388f31d98676d957ecb814f4d6ce632fbfb49300fb6b5e2ea4a727a5b6f","proof":"da2f2abe4a979d0c1dee3ea5098831c01d0ba3ebba7acf1477840d0377c7962c48a194609854380c30e4ab463a7762f10bb588b3290bc8230624aafff127f46b32656ec5c56a8cf3db2d05e3277d10b35b7a029bc73445112f583a1875bedb7fcc4798d8504019c3c53558f6e9ca095705833d0f4795ab1f682a7e5a4aaeb234280e61d9d5bb0d12504c510301a72ef182292cfc12e7a07da466678566759e096f008bf077b8f6f9cbbc3b4195e217625c4b4c326c1614f232513981a85a5c050deaf710a0c2118c6a0e4d3e26c16d84fcd89ea35b6c0460ecbb179a3614960cf28466e7d69590727355a6465ee56c217bd86f22b57b7691016aef457b45392918d1d8c68f82bfb2eff2b161c569cca33febee8a78f0d1ff8b645ed5ef2126596c277c2192e941fa4a5f2912405aadeeda41043aaa8e8876c9ff1f93f6f2a44bda044883ab6671edf732db6699aa1dbe9c5ac49a9013ce3fa85a3f8865713e73f2737eac13b0fc44bf45a5f7b4c97950e1e67244bf5212ee68fb64fb8b63b417062d06828c0b8e6363445f82c7d517656ec1c75829207eab9641b8728349b518868f922ece33bfb1ecd67283745697f43b6cf46000e9b21696f117f472b6b900a49af816a97c4955703029a15f31923beeb95bd95c62e8a6f5b34410a4416c2ff00710ec9e5fd42df2164c37a234cf4258c18da38c4ad79be9b97ac4cee33036888e1091cbe9c91ce5a6a7b458fc0287e9b0fc075a47c2450afd2b792da1891e3c8a057efece99cfd9288ef1e195706e8c57f9a04e41e74f6bd2df2141d8633030d8f18f79bc1ec039be07649fdec0a96daac83ffb634d2b31fd0f5c46508675afde52d64173f3896c2104d3d156b0c26735353f3c0993d8c4d983742505d20ab9072dcd11fc7077976647255a3d1d7dcd277ff05ae68aa78dccc63c1c112c0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"582f416080baafe0a5eb78b913a576f21c86d2042c2f5af7da99a2452a9a407b","proof":"8a571674a4563d27118624fc187be1824be64032ca140dcfc8c855e52dde965b22b0785b15c089314ba2228eca6764a0f31315b8f11c112c46a8020afba4b1156270d7fefcc02201451854694195857917c26b03f9078a774e6835b56254da10f40a89129e957886b63ae0353869d2a753810dc6718693a76d1488cd15b5947e5d13f29a4789ad24441735b1e96218bf110cc099f38bb2b71163caad078b440e14ca82ef38e1f40e3a061ed5b8b8cbe36d6565ad80e28b478ad909341b262501c78b57cd770383245536014ca600a79cc4a8d632bd9c4b8cd6b8a329be1df40380c2290c457aa78fb6b8f5925b71b93ae0bab92e8e2c75b9f72b57632c2da20968178dc04d4a1787af43b52f217e2318004f57e2623c3c0b1eb487090fd8d579d48af68dc0627100d4291c950f7ed65ad32fea3a2a5a757e1d4511c5cf838612a0ae5eaf589133a75ccb319b9e10340fe0e72b5606e066c6ad88005cf4cac15640507190f068753c5a40c6a0bf390c1bf4e5117007523d950118aed21d71d6692020846d674aea76f2d686ec2f2cb4f541f77ff995eae5d2f6f3e44cb9323704504f08e078403b77ad4c9da4bd4b330c4d1d44f0156779500d238682895c8e0e42ad78ae933c5b20f1726b660740f5906b851d696dbdbf87156ec39e5d3f6c493c3369f71e4a53cc3a87d41722e7427f9ee113d4ac896be7cec93e0d30db671a9c1b86322529e8f8131cb6a1242751c5c59ec333163c9f6e7cb57fefdefce3321041e2f5bf9c94e7f00ad2c762cd424b81483c98f7e836a1cc4b633cfcdfae1a1ceadab1317f0f36b7cade202877901d13c42705c633ff2df0aa18d432d040750aab931d76da7741d0b49d3807c5b63d3de24b32ac7bf7a346db1fbb30c9d104db52a9a984820c099300f9840fffd7f413b99322becedbf72cab4716b8e48d0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d66e155c233da153cd4bb7a0cfb220f10f80f5f74f7c961e15eac0c030c21f22","proof":"c24dddb98586cb78c510de491f20c5e085fdce1cd83dba22e90659de8da8a802209dc0750f93cf5d094f85677a07e5d899c7a520a0f24155c8c7b5446fedf855d81cf116054024f2a12c61568f96ab5c16ff3a6edeeaa113f842437b7b14d7300a247ead25ab494e7d16defd7d6bbae7b1d650c97b739003a9bff36addfaba421127ad8514c014cce6dd48a596a3d4ac6a781c032cb85ed5ae08ad39e0c38006616fa61b625af4e1bda6cbe2120eefd76ac52091c77d99fa6e0bf1b6babb53072fc9768f9a2540a853eca881fa01f5894e5dab10fe59f18d40197673ca2508002899da75c662a23761d3715c6a94b4dbf7b76239ff73785c1d497a4f31fb3b668027ad21cc957d0156052cf983f1f3114a2f3652411a8500da875428368d5b09725028e4ae7a352cf42f41479062f0c23b7aa7f00b0dd9bac58aa0437119dc73c29be80a2b47a80125fee16d68680a11069ef4d0ce13a8ff0d74da58b3912d67f8c67f8f32f881424831707e50aa0e7cecc79fffa00b1de8b35d5576187fd2379ad333025bd3c897634c30d326909ee3382ce56f9e39d966ac5032c2cb7d30120235c6ae10e203109d2897b6092087f604afbb0681bf424bff3917d230bc9101482af47401099f07823cda7930e020a41770909cf030fe733de3ab07cf874960c8adb3a96171317d645c1bf59db780e57f3b5eb6b2e9fbc88ce3a92b62972536e699f29c070c3a202d81de045cd60da121406da3b45b5a10990374b00dccbc06e0fca239701d9894a0e13ec7a806b73fceb462d240dc36867aa67aa7ba49362b041215b577a694ef8299cfdb1ddb5165880bc03bc92dea4ac825d5124ff3284e41e26fafe4feffc39de1983c4cab8cd082f65b5571c0894bc6ab05488685c005cea7c81e14f469bf6cbda8ef8d79bac7d9d9f22b8fbeafa0a88dcd68f7e5960a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8e3c965f9a65c470865b371e9076262d6070b405ca54f05a059bab7973753042","proof":"92b83898ac3a09b400a222a663494f705a4f0fd18af876afb393451ed645de01b45aec0c9109b44e9c2166f0a604ff8811ff905aa767fe5ee2102ae60b764407de5a9a3697657d525b83336121b615de9d362c82faad99fb6ffbd93235a7d95d1ca9014acc2a8e5e0749c81d892c86877047e4b1421adfd369b86bc8df64e150758b14d5b0433bdba05d8bc8981a3a7a6bde97d6b084309105491cee9b979802c8bb32c99a40ea0bd899e2a5eea3f6666358089f11c6f51155a2ca03cc3a7600202b7ad02e6e4801b216ad28fd310dad453d34c4bc83d13a53c52059db8f8c0d5aae876177eba859a206a4934319cc9fc0fde93f613c11313bf7335b00b68367dea535ac49b1cf6359f4ffc86cab3c510dc76fdd301ad52a57ce7895aa76b2617e4c577e37bb5df48140005f44e802b10eb03ce7f765e9e11e2c0efba3d6fc01626ae60a39440b6e3af7fb208a456ce0f48015c87cc04c5026581e3950ced3616418e7fdd250c7da9e88249bc609467b2526e99b9252236b1f68f1ca7a7ffe4ef06c778161cb2de3eb6a8ca329ab3529f41e7e379d6382d64d2f275c925f23605ce613a95b3708bdf21310aa45cb00cfede442f306ebcefc757d69effbccce66c20a44ee40c7409adf5c355ada5eebde1701b352f1a3339a95c06a6385a0461a5e785eadc5001b6c3d11b4e19287364f745dc08c23d6ca022b628c20bdbb302a2442ba44f8f20effac41039279a1233e9566ad99fd51d6325547b144cce9f06188278e7ebb05313a892c17e559f3ee753d068fdeae279179480b2a98e525ba095e70fe1f9e5c729a9fc8b7c6febac5126d573661d7f70cf355286540d6b0e624c5e9f92749a6ed4f6ba0e9849d3b536eb793601a459cb1ebc113c159bb7b4f000dc75e944bd0cc415ea7b6f66d4c165ebd41e17756124700795694cbb5d9a203"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1c35132259d99c0bb76410311adb8bfc0fa75c3fb594a0f8c25321b3cc878433","proof":"50dd089874d811120e18f9093517b6b9f28c2e5b29fba70cf0ebc7e84c54b254705c3e13591475717e7598c9937b508b83d97b0f0fe07618127df8880dd3ec493099706d83eb4e001ff1546e8b4dc554e9e5763ff5cf75e182dcea30eb3b2102829a2b076e7f0602113c4e37d7ae5dbac14410a85490148b5190167065cb4250e986cdb46517b2bc1dbca822afa9095f1bc75463960e9df402716fed71b6a70304e5ad3b609c4f4a7a1ba370a7faf6ea02d94e83dfd7f771380c17b932967e0ee4670d93e93cf1028f9f9295ef4d87e6844f731c5f31d1b8002f8460cebfd60f1cdc78ee959e5218f4275b15ff53d3e4b556ab01492824f29cfaa960a5cc4048a4fb07d14cd73ca995b9bb0cf42457055b36b5a09dfc157c71385f042fe2581d4ca0dcadf5e8e3fafd85eb6269f75f422d8763571381239deb7338fe64d20461da516a4f438c3b46f978e264939cb15237b3af72b1ddc9b0b349822a43944c391623d9b5d253ff5158af3f88a177a438115f76062f627788547016eb83f9285d0c0a095d7250b0e89874a135ecdee75cc8bf19f4f6ccc1816906ba71f8dfc958949536e0119a182e50759eb84329e9b94ca39439f899e0bbe0f6bbd4d664cc7ef68c260aa58cc98257061e87350db9caeac6915f24bce34e717554ea82b8a809105183e917282e2118a7c880b9f8e290a380dbfab1a9d38947e90e3c6453e679d45a033262f4c25b4fb14b927322c18a055968a3cf423fbfb1c4cca1c77ef5458ea6abd99b637ec3e4d0d3e4e861bc8c33d089a17c376bb3a553eb507cb647514621a8629b8a7a4082fd84cf77f8ed5b53ca98260efe934145dc0c3df9bdf60890bd36ab911de0a8ffb6dec0bfb0edb54fd4417e37e9aa980bd07719f5401c08c9c89f1c6789b5846fe2a1f66414f02f021d1007a3cd85768b6e7a2f78c34b00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d04a4224978cbd817d350023ecde741764e03afc6f6c9521258efbaf1eb48e7c","proof":"7af7aff0e7f7a0ab7c54425b19b0cef4989dd3905e8d42c60df689d17262cb45dee9d4ad28839343d6b8b6682d471792ec5a35725b15920d184650164be6f21ffcbb589849ce3f7295e2b4407c3ad80e78de3356d52eaf61fb06271df2b7453468c009a918eb1660fc137096d46e706043ae90b75a358b3b7c8e1600685b1836609aa09ef3faa9fab84e008f2f2a354fd5669619afa92249b98014cc79ab5a0030e6551522305c49b3e749da8e570a13aa4feecdfefe67aa2c697c157a73c90552f97975254ebaa20b9e069d0e1ddd060fac23bcbbbcce9e3f4aaa9d99da8c0efa35dc09bef8bd28c709ff3ae9345e4510d6b3fa98d4a607cf1f0d4af540f4382c07ba7441040f61d1de916cd59bdeb4001f5ae0bb492161b6764f4fed5ce24d40063413746d0873dc431e1a7d8c4ed294dce43286babcd25945e1465204af407a08522dac0d7ee189f010cb8bf8e9509e95c1cd5fd7e455393ce477bf6efb5454cc2630e0c4abf7c03af43ed7550f493d59666df8bf3f6bcb281ccd07a16132c223fa01106aaf9d44783d9a3ecaaf149a4a9243f93ed403e6393114b43c9e34ccf02e29d2c7faf5a7d37bdc0ca16610d738e0acd53b1691cf6c36d2173fc27d30a1ca7b1c81ca80e27d474781a68f7e45fa0df53cface5f944aed04c0ac2b49a0d41acad9a87e0b998b3e8fca20decc86920f2d977de051dd9e6a6b30d212677aff82587cc3030e7bde0c5939294b613ba0c207a1a9787806bc8323a649ad6baab129743b5e62700d9796790438a453ddd32e7766ae94e056a368d7a91c4262547121f99d48c3b13ca2ee402e2e79a69a5f5cfb2dafa5f3487576851e6f5c66c1a187f63e41d67d06365e81512fa6ca65ff3418d42a72148749565af6917701e67762a7d702940513d9f3f720babe0568a8c47f500a71b6f67d163eb6416507"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"908275798cd5741e40e850083adbef716f2972f7da2567352f257eb3a9eec95e","proof":"92047f818a3cdff390a8e5ff9299d98e95769154c12903834b7716e1f324507ff6cfb4c2caccad01cc00a50efb0d2d7fe26b03c0b48eac429d228fa1be683a18b2d54db3f8e34251bab86c0ac05f676195f137283f4bdd46ea98a2a758b2ae0446616b239870b0938c348e24ba702459415560679a63dd37f78f6a684b22cd425ec458a17b857924e51ac68b20b633ec10862227260c75a79ad0e88c5f052d09701567710628b32c47239d671257dae5e1590cad47ffad3528b86fe8e0d72509f0f9189d3d654c649cb4a5f80fc9ca39083f9766c4aee722d27b031d415910022ceee77dc1a6dee134f12d80741200aa294e8ba4e9a61556205f1ee73f3d985194a1e99e9ede94fa956869e60e884eca8fef3960640ed09dbbc4819e57fa322808b1441c3349dde9ffb28b97f3cb3cd7412fdadf2cc7949fac195c7c7fe86c109cba8d80aa11a629715f907a7af43359da54abe1329050a89c56044e8de0cd1e78922f39771253d7d2a3533573fbff711a3b0b1d4346116ecf3271ef14487a404abebbf3f833ba629228de18ab7862b020082b77b119bf74051db45963922748d429ddf93008313bd020e9e977a235f28aef781886c3faec9af89d48fd853c13f8354a4048e26f8df2e69d94e8a3212ae850a015efe052ea1ad5692b00228f4c601c47c1f180bb4a97e100efe77dc2be6b53c8b3669cf652253a0eed4b93f104249462bfc401671961c765b775e31f9018e1f92bb7f4c0d558a276431d15905ee2d93daaecaa2097471f02de9f2c9a058db988de709edfa374a9ff68f8b05e3d7a5892d0be777765d788de1564ad1caea4bd284fcb649c6ec8f39a0120a04c2d22a55727589e36b1fa55d928f793495caa709d93ddf1d335cda87e8891c32009af4e36a256ff410d3f856de6a2ef023d0807f50f4123240d96a25a67cbd3e800"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f27305f14e1622418b0c7badefe68e34257cb6b71b5d3f801b5de9936f9eb866","proof":"2042e5254ecd766ade76117975a6c18a1f9b9eaa2507f76f570fa7f504c7f770fed029fce10353eff1e53599a6f6666ff41bb5f20c38e75d348572e39873523f76855d53a5e81f54c9b4d3abd664542ccf248acc4ec14a0ad14615c02ad8cc5302b0a1fd7308c5af232b2659035ffc398dc6d04abbf2a923202a43e3d17a1631179e46187d59bd91cc8b5f1b0965e85d606b801d4c256469e136fe3d7368c3021d07b1f0db6790d41056e248a2d7e164dd1b8c28ccb5646ee6e42b7f35901800a16423110d17a20f714dd9f05d77fb1f0d16b13cf67a874f26746e37d7f2e40a02c96d9c33c11eb50f9f397b4485526c19e61a91d0f1739192aa56f91774a456c8795fbc44412ad8677ba06736373a39370204d72243f4352e2c5a609e228d571a0aa16d941fbf9a796d2e0f9f7bbeb09f10203eba92905f76c39b9efd45913a62e7c56857f61083092f57e2063ef0b03b108c0fe727ff6a0e241daeb51e4304b8c4289fd1022e0fcfd3c7143c00ad7f87d24a55656f2769f4719ca5b29db869a2e2850908e5bde455776481e7ed2a15dc4a523f00da5217c2a133586fa74402a01c44a0adb29721e7d62aadcc9878352384b327a4862438f94f20657737e20dea519a9680214b6989cfc02c6d713318c8d5a960875343b7ec536f8975453404aa646e2b87c8bc26100713370c1d22629ce906cdb16f5ba9dd1c439cb2835e30fcdbc23b3109b141c51083b3ebbec6bc2921d820a1d1d343ca8a0b098f49f00482e3a90204a699ce7cc5cc2d514537f835f7ae4daedc50f2ae3c675e3acced4bc01ed0134f849658aea3bf62cadf8a7cfe617766b516d576b90a56f867a3010518e821e047a9314749f3722a3d23c8adbf92a2740d963468b2b5dc3de644ad08df89c69950ced82b9fe023bf65e2ed25bd348693fed5c6d19ac9b6460c9bde0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3414d5a18cd3c7fab1d088b6419a7d6ed4b671ac6e1b1bde7fca20dae666900d","proof":"90f6aa1fb59042f597a1e828e0d7034d86819fb9e303bafaba797bdf2cc16938a0a18e3e24a4571c0e814470c1a15f2b3b5ca7800f5c32eec536ef2b4fdf7e493030a0fecb6d12e8362793e4db536261061f56891a7ee17f148c48f371c787344a4a7a8173e7396dc042266824be850349dae6f87b16170b8f5b7ce7e557fe62f5658464e106972748011861244292c346917922a323ed1f562ec3422c5b740c00b981512cd0362e5c99b902a6864ad549f5bcce1722f63bb0759d8d3b9d6809904fffc8e5e079b829247f0f47e5f5abeaac04052c9a6bcda6a8f199e5a62302802a77de51e2c041147822e59a577fe42df4b27a3998b6e0ebc56fefef130329f4266c2c7ef93f4f39c32157c44c7f02f02ef68c60a40e4246b0c104609be87a9816d5c1ed5dece0908506022254e35c763718e9f74e4f4cd969a7001ca9826f0201e46add371faa6c46d014cd7acbb9328738cb9ed0159ec0d6aaca42481844ba881de67cfb1903e17959a16f57c5213bb6370f390e1c32f4d00160d0e65f5a565c8117d347610367b5329b0555ba6d4df6951eecb81c96edb9dc9782b0187dbc177be322fd2c45bb49f58bc39fd54cb3d1b10adbbecec8efc5feef83e9ef2b701386793f08dbe2b66abfe316beb954dde8b0bc6a5c44a45c45382309d50503aa610f3e5fbafa5a4dc0e701cc1de2a7602f2282097e2228bb29e79bd24cc508f467c91a4d32f89bccbb043a7ecc03fb112270a099097e9217198d639b0fdc63601adf151c570d01422610ba89e1c824b7a6f58ff7d479e6333eb392dd4e4d779838ce8d631fce7e5b049df551a298c443dcc0553e92997bf096ebaa7f51b354ba2716e3428506aae60b7551ee3c67e14082d995ea5d32396772d65bd3d67a0055d791c7d8fd15e134612fd2de2b01dc4da96312fdd353127a092ce54a1a220f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3aae8d0f7e654fd1d83ab1bfa9b1aaa76b5091e0290b32c80711565e873cb840","proof":"460ac68a1b3d7768350b96e0ca50796e4779e621e2ff9bd330abacda07d6dd4f6a3d61b63050a2a257f6e8bf83621bc5161a3e68fd0da2dabeb3985d6f00c24c02d417acf31169cbe1283bcdd430cdcdc7ec64a70922697e56f5d04d5103f137e8a028227f22a798127531251e9198d76d829f70d7bc4b4a8ba690ecd4bb9b41217cb11f2a054ef8f41fd554b7d24f2f5e86eb6cc0e62edd01c074206a63fd0440d2b9e7005bc61e4a5a87745acfb54b7b296219f7ba1e6d9a851c4a47db03090cef06ffe041c8c614f076df67f048f100f06566d9016d760ea3136b89420e0ed6f62cf606804955020962c44d41b919771834450841e4cbb8fd369ebdba785dd2d117fab763fa03426d5ef66f44a229620c88f07186a9861d6c8987ca71ce3376dbf6268835faf1077f576037bd1d8c7faebcae64eb6562479a302b13e96005d6c916b31141d13a9c12b34eba811f99c1ca1e1e383ff5053d9d7df68f306f6890d758ea023bbf8640168c502cf267b64bf0d3bfdec83dba37ea6a77ac206979e8c76ca8daa3197662c834ff1667d8f3ece6df6c32e738ad32bff80c73904e0e3a597ac79ec714b41866096cbed9bdff7660e54f2ad7f28616c1dd889250324d986b0b1254943deee2c6eeb800ae3d884f0b64122b48209c9486cb02be3135289ede13edf741dfb1684b75d457b1025b8b623d58d7847d8c8eb0037207a506460e15f7807e7df33bf0bba51aefc3b1525f3b034f133851a7d8da0b9e807043614c9c464c535d9e6e42c159a7244143225bd0e1e7f0cd5ea11870670816d3064d7c7bc0c751550826d8b0ab1bdd4bb81142f857c25c34f591d8f0c689df44b91b190cfa86cc056aee09f8cf769706e4d96a3cff4a935fcd87a17545303eee9c0eec9ff1af63d58890f3b4cb2400370ae7198cc4e13ac6fa9033eddbdb8496cf04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c4ea60173aa438fcc096ee60454678145fe894d540c99086eb46e2cbcfdc1d11","proof":"f06c50a86cc1170d3db74d832522f9f8a7b8410fa2cd3f4b6a0fd38b6b8a4e3a2e0030af58e09f71cd104f445a5aedea0ffeb2e622ad15e80761afa96a973251645f434b829b26ac941baab33003a25cee1f870948178ba75d201c076099573eacc6ad64834c80837eb9a43fea937f27d8aa0e3942f13136e322ad906adba770889074080132632f6fc8b0b55974f47a20be3d65d22c2ff999097480700ae703659aa9e25d97fff3131f6a30c3baa45864c1d1f9e53dff5ee14c73322bf1600095ac6c1f75c185e6995e2868989404f50301cc3b08287dd0de6716871c1b550934b74b4a31c41e961dc976f92fde1928afd0641fee27552ae66d9faafaf42f323637de65c3ece65ec23a8cf7fa5da8697aa5f6a10309205fd243b0ac81c7c81fe6ac28c7921dfe1f0bb99207894ad3ed4b391a019dcbb4b1ec0eee324595f04a46c0fbd01f4beb124d983a94b2f672a15ffeac12c29a24e6e19db43200eae97a1abd740e1987cc3b4e9e0eab32c765b5efa9a9d067182e892e3f316d166bec2910452e0f9e8ee1f13a6a24bf64883ac9e3b135cbc53357f9c9b378da7c6f961f2ee655d13b7337c717e9b0f75fe498dbf2b20622062c4990af12785c73e7a844400519fd7cbe26b301bb4f8e3597d9c046e27e4f9bb8b16afe5e201d4455704528bf487c47a90930d4bd6a8af33bddac556e0623681dfd1118c64e9807ccd56f9081d0cd47543d3d90175e324bd8e0922030741d0e34a60b9cb19d4b5e86ef4a08b74c584b5d33536859b5fc1486608fed821a142db186ef931a119774b30b3a6caf1528f639fc9ead38d42854fcd8ce50aa58df8731f144edcacdc519715659dc79451d59ef56759aa3a87ce93c0a839836b57268110e894dc8c8c8bf7c0e0765f117b71fc6c5ac64f1faab44106f3c80c5c391b7048238b4a16ceab1e1070e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a2828fa31e774e6090204a0fedbc2462c3825be38d6291ab1721c68d63562162","proof":"52eb43b2885287655701700cadd609707fb936b36ec151ec44a95ed227e747493434309b9a078e991ef7a5de7574933e3d1e0413f79f8667735d6203fe869e74228e317883bcc862f67e5d8e4f9ca7144d05a5626011ed7136448985bcf1bb04f2acc604f0c12bd10a6c591c76dee78805c7d2853778ec3a9fb7a25624253808f31e21e6f3081b89124fccc24d30a5ed083b204746a7aaf7d1b09e2dc8c5e100a65d178488a635760fea7d92061e9858bab7a1896fb3ef4e2dbf7d34066b13014ab69976d8b5cd1f0c3cc1b99642ef59fe1e216267f06c1c171c56e80ce6110030409988ea3fd4e1bfc5df713300a4a605e53de51a48c135cb8b643f1339c85f262c01e0621f29f90266e93f66c5e6fc39e47f25b911a6b03f85339815026d11b681809a713cba69d85eb20d3cbad2b5d12a3588de6e95dbe6110fcf24fdbd63f0d4f36eea995f525bae41e2de2a797fdd5522d6eaee9846446230107ed1db2442d9d1718c44d3d7136b040440dbba46a9145fc395954075add51aca8789d00930cd2a49269bd601fd0d61a1a0b24a72479d79ec2c3725dfb610a5cec60b48365c75caf1eda07b20f91835640736da05b4ee2cb2bc62d1f73baca8e17ed0147278ec99a64f11254e7ad3ad15ae87b846a3fbc024f25cab1b84c2aea7807c6e659a25aa826ce4daa6375162f6af3e0c4fb3fdca2860450ac45e86210d5e58216af6b565f5b51d1b79e5c065548439674a386404eca5564bb9950545f33744c356708885a35beac51abe511989fd1dd7dbbfb818497f3c9c812bdad9e623b2a4461e6e9211ebbb800e62405aa5792119d905d7a216b1ce12954e15b3ab2bc80b3a09cedf8162ddfa6c1c7677d617238f3df0737742aecb6e10768613e9ec76f7006e2e22f98cfc36bb2bd1447be35b0806968eaaa603aebe8339574fa0ab1dd10c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bc8f16bb82bca37e578849c8511c03b3e8060fbaf1f57dfa2f939bbd93e7f020","proof":"727baeac01b8358f1d9be9fe7bb4e45a3d06bdb4aed3e0e24c1b2bde7c0fde23ded875b720080fd21ec552d2dcd1ea32afc549e7d4e8a558538075dcbc5d655478ea28f32276c109252fd554f943a45c0a1bc42f74e9b182182c19af58d70c782489f3d005681bc4ba947458cde05ea7bd72db9171dedc00d0ee7fc489d3f27f0946774b51f83589fd5beb85cbb2abfd890acc97199d39eb2d1bd8a88dcf3b0be95e9d20243607348b74f3b0332d1437da8477bb1416d8ba31817d118b37ae0a4bf98049ebddbc8f05daec77823fb82373fc2a50bb3eff32cc606fbcd896f80664eb1570be1bae2fabcebac227e7e2d7fef1340f22b8c4ea4d83ed2d8bb60970522e3997d01bca18c4a791dc21bc2509e965004eeacb9925921b32f521a484335a233b474ae234ffdc4c525f5f2affdcd18e3e4d52e2ddc0034a563eacb57c2ed0efb994d384e42cef418f776d2b99c3a4904015f37f53823613df09b6c14217464982c84457a83f238097bdad6a14a5be5791062c2bc55b3e5025b5d2b36f5d20ae597187f9c3fd305d5a159df3a81aac73fb79e64f1bb742fb290860b4c559b0234dcb5b6855b84862c5016e004bf306c965f5abbc0f33faf2ab46d5adad4c5a427cdd1cef6572bcba08e6051038a22401a1aeb68e06ec4dca65ac819fdf151cf37bc1934a738a7d128d811b40e775fb4d636527085329ffd548a78054657afc15c89f3712414fc8423ad2f000a06292a8d8277340e84ffda27f87454cde01f27ac2fc7a35ccb640961fe3ab1e4220e32bd2c0cda269e166132c1542618b218c29bb76542911c5d244627e81a8473478622fc1ac0a49c46afe2a1af0e075728a327a5f17346d1538d06884c8f01eef5e26d46a1ee8ceff2a5a98e170eded0c0f6bfd77c8edf4b895acdd6fec19d44d1f5f4705a5fd87e5012ad45363f92604"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a8e7d0335711f71490c2c2f40bffb3caee7469d63751ee51f78de99d30aa835c","proof":"56388c6df7c57643f5f9e8875934c83051859c155154cc162cff276abd479031e822a6946f189806eb4b58d30664c4759e00d9384c4c558f487ddf77fe28fa5bf8770649cfd2662cdd2d4d2d2140c0a56fcaf8b474422c6466c35519ce9f171b5238d92a487edc9c32d34e0a9fa6fb1a8b8dab2a71356ef4bda18ea8cd62e828a52766a14b5982fe6c51480984006931d902a5214898194d12dd85424547d80303de979df894f258be192a656b51be5f720396d80fb8430c1cce667be5d22100d944818e217b2f012da707e002406132668aac47acce96218ca856e31fdc350d2250e053cb215fed24c8b69fd46ac2b5760f062227728fe68561c68c9aae9d44625a10d6fc0030d96fe0768b3594b1a03f740dc9d1f9aed6a9b80184925b8307965889f12647183d7c261722dda69484267da9015145986b327da90c3c8d2d66429e4f787a15f457c5a0858542f0f0cea7ff2e335e3c356ba4fb481e393bfa773a897fc3eb30db0e0da079a1eaedbc2e1c608783dcb74a78b679244e0bbd2d068c4a75d010c4c18dfcb3e2ee0b9c2f7254952e4047708d5e3f1a04ff637ef90a70e3ed9786b0ae8a498f48d48e7287c74eba8f82ec8eff9b20b3a90a1f84ac5a06ca2d4ac65508eb15a8f3f5cd1c08c890eade1f7a02223b2d6793f1289c3d4de4a2105c3d1c2df8eb89e493b964466e7170a5025fbab393767ef95c1c26585bcc4f86eda9cdbb0bc4e7f75caf1c9e53ea782c9542bd04b35a84078eed27bd2efe1701fb32adc6bf1bbde18efbb18fd7f16a52e256bbc24ae0dc75cc90333744c4ac97068d9ac806224a1b9a860b6b024b2ee945b50b922a94c29d8a1317ac4f640b7d2206e595c02ffa853f3a0484c93c6dac3a4927c501c74b3840c69bc90d2a151aeaa8527c45345ee06798d213f15e307483ed39433b67404a1d2bb5af0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"44f680e0f3af31833b1ce340003606db53efb77de59a3868facd1d4548d9f665","proof":"5abc64ccab81a498dd8ce3977e4dc45d58d0fdea8ae456deaf87c508efbfb2198461f51208f3dae06f4892ec8eba31c8f6a8841609ce8b3ccf420d687fddf50e30cc4f7f158f5ab25b21c4694fca00baf51f1815ed6fcbb9a9b989a4889f6243bef40d91c9ce802ba9b2652f7d40aeaaea503db0ff7bf2e08eb7f2792274a9632be4642b5278d6202de2614f9ce026a5840825ffeb7a033175be3d249e1c9b0d2214cfa3abce03dc95f1e57d029fbd85d959eaa437a2341a561b0025e1eb8103be0dc6349bb2bcb9f3574a0c57120b0fb443532e0d38ced8085ef9c23a84980756c2b633b5fa8d0bf1849a892f67e29fb2e962a92f8bb509a26c7ebb83fc0334c27dd1ba7213f36d3a13752b734e169239f88a18ea206d2b665527d3969f26174ee3a74007b4cc9b52b3e3089089fa701f065b945181b0b1e688e772ad25213b280701c5e040363c398af906ce44c188400aa3b1b7cd4608df42521bb25bcd740c23a339d566436c9b3a1c85c144aa37143fcd0933950fd1fc706beb18ffe13af83045ffa6b872c8459aa6a206bdb364733edf0946fb2de75d26e873828d702aba49cef5b0c794325e2f136a31de2f26b701e4386553e55eb14a5f40ab89a23580b616572d68f6c27a93897e454dae49723349156c4153b4896e6a88d98d5034a609f6917262ad574f605044b30a4858ee1614fdb435c61ff7aeef2fd4a1c21cc6612fe245f3feb98fd59a98e4e134a9b64b59d8010344a93e7253351a95551010dbb24bfe94329fd060b1746fa0fe04cd680addcef989e39770c8788270fd614c109745261829ec22fa77abd3e4f128800cb059f1a884d0431eea4aad5b7d12c3914c75d83626bd70fc7aa281b6cb3cd72629427cc790db1f9e34d62c476b01f92cc003dbc9321cdf3847313ea95db9a9af0ca5a81346e986b6602fc0faa803"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"965b96b11a941ccb1a100ed46528ab6ebd441cc7a50feaadfeed59fe26fa7402","proof":"c644b804e1ac0aa72ffdf19955919e111edd878684329304b366dd1850b3942e5a42ae9c086d394262eac0e33eaf48dcf7d8cb58e7d536bfae45d9fe11c257567810456b641de31e90a0014f619193f6475339f23b794b59d45c75015e8c7f60183c03e4562dbda78ecaf0ccb49508a93bc6397aed4c73411d6d001a0e6e61679a78af59064a208227ef7cb3b07f50c22ca398d54debf480d4dc8772aced8d0b5b8642cd38a8ffc0fba933f1fbdac81d4720ba26538f9f7894a2aba819d9370ea8955bd6f4c6bf06525e45f0c0632eb9e33ab1f2aed5f6e65e06577e270c8c053e8a1bd8831fcfcc86dc65cee4f4a7af6f453b0664e66b2d11ad331f0b8bb71b74a301d03e07e225cb58136088bfdcf3618445c9c073de68cfa4a592f1d6f626da788c71e2db51b75c717186e6e14699bcbbd03ab24fb91776f0ce45e74e7419b27f3d7aa9d706f1cc53bcd2a69b1897c66ca78c20698244a4f25dd80db95b52ce16b276d395b5e981a64c87478e57136829d236c088f06a1e35c827596e7a344a9566ec8a627b05ca982a9c2d70e7e6a268aac475232ce2dc5a5c3bb96f9222b0632b722dac6c17358389e2bac27cbf64a3959b47bfc93c2f9c8cde9572234c569261a3a37919cf0eafd476415085140b55b0cc989c7a2b1e01d5287d111e330226b8381e3e4ea997de900ad6c2afc5267b7eaa6f1c4081d8bb9f27223ce93c70da82dcf6c5150a17152e7230492ced8570f20b1421e423a5f15db043ecf86460d07cc62c61e4c6a8db603e8c7e3b20dfebb20e7995986da411ca98e9b3ad377eb589bdc37aa1341685c54b1f570fa50660d47e627a5fc681f1c693cf35e747c9128585d057cbc51d992f287dbd39e553524b5d35f94ca6ffc3db4dcf6985088232604a1dee3751d7de282126273e8986536da1b4611f2fdf3d452966128b00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"089e4ea56b55dcf37a4b11da23e75aa6d8d6f57091e96736544e32f02445a63c","proof":"3ac0aa66033536cb9671761a69b971a79afbff649c89680315eb13bd130d442e943dc9d55ca5606f787e8c59bf066e8c2c4bf66fadb3a9af67a8fc8d173fe956542141675bc9f7bfc4571fd82885bc9ee576dc8a0d3cfa63749da34449c5953cb089237a736138bc9e85486c5365a6ad961a1d591040d8af1938bc133596dd4974b534258fb88130db55207a4603c4e38d17f511d87d74f2024d72589873780b3d3d87193c6dd643146512ec834dba0e59bf1814184f3974ffd1b6dab5f58209614c4610de71c42db0dc53e158cd1a09950b098aeea98df88684225e63ee2f0a4ab35c83cf9e0eee314ad3ed5d02d0dc6dab46c24899c18a62e5071130da6d3148e23e993553ec44694de0ce26f0f39cf3c8e7c13236a242cff911d8bc6d4e4dcc8a7f94ee6289ad869fb931de68bcced9d1980fa3bbef7b8bc203e5af8a2a53144c815e89c2b746b6f5627291c42c82bceafcf1a27a636b09820e65339c5c6b8626295cfff61d0585cf45af314cd6805fbf071758472ecfff0a8745d7712a5e8c005bbdc696628c57d133e997cb918a672ab0011ff6fd5cd4c8c9d7d3a47f5df2941a11b642ae9723161687c7400c7770bd25c79a0e26e5afeaad84bd6401312c418c82ae0f247b738cb260284ecdc2be99c6a15caa782405557f2e65fb1044fe558249f50aeca6df8a17355b2181d7e19cad0fdb60778e3b9c621a3f3bfe2a6e360b3544a9c44d0de6a7513a2e332b56c9220b9d5d07edeb4c455e9fd20203f826af5b3226b55da7751705d5b238ead09f28a0d930ebd8a808de87f656437fa2a5292b6e557b50bd1cd9cea491d4835e5f6d1efc850f5f6bf41f8bb751322b3c12b474584ff3ded6190085aee2716ee9a46df6f1eb24cdf049c57085205d07b4e000e52d0b794524590ad67bea369928ee3744d027e1be3ce965f501fcb409"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"deaf70eb25458e0ce93273068d4bcd84f405bf92588d407751ae388252745306","proof":"e2261765a1f88a8a0680a6ab7ec66d899ab3a536d7a8d9f608a8ef66605ac573fab1df26174ad19a95a9681a9c5de776f0eda060d6f24942ca59d6b31087c14d549f992bfe04af2472f9d66f1b3790482703c15625dfbb20019fce5c49778d47a4de8fe52d097f47747e3443e2d966d98d373c88debba088d8e93801c87d3c4ba6586739683e9321adf56585ac1099f98a19dbdfe8b69ada148bb408505a7c00535de583fbed07a3c8746a6fbafcb69eff2aed326f2ee2b8aef0ffe6b0a5f104a1f45fe0f16ca08a691596f40ac8017c5099d14f2cb5c19877d9f5e9fa1b040ca2c59b56624b2b4ba55490067d3c7259d4867ef42890d4105d4a952c3bd4427d7e613bd0fbdf2bd67363f6b657afa1078a837f29f8acdb3eafd967fd028b63148803bb51762cf91422373261cc9194cbd8a41bcbcaf4096fef35ecfaacc464651095bdd7b96a6d279145c88dfc58e1e5a77506fb48216b226df5bed3f5a70c6d9e44483ab3fb4c46b1bc353ea9567c1dd2fc539d01100bec7628bef0eb26aa522254ae1c906318582c4653f2c2a3457a11fc2e9ad297d00995181b69da04ef2354d7bb0f0b9985e7ccf794080edc7e354430ae6657380298f1bc90069ca3aa38969c77006ae557c538867df3ee680855dfc7a483a5724687bb92b36e5727fc55aac7f2ff8898a834eae92e822aba2bef186a7db83019a1d6865ea9e20f231b25642a297171c006f063b1eb0c31fca238c657c06c669d192e458a69bb053c3760109eae6b9b67834c7dbf2c2721ccf31a7ff99e9955ad05dd6b2c5fae743a9d6ae4faf71800ef9a70ec92b5d95c0047e419ba72491f23c5cd1c7beb076ca90172638d439d8a53eeeca959b4161ef040853be16a9514bfce3c9a0d0af1be67b0081a7518301355a252e7de893f4a4368b543b609ba6bac1285b8e23567b5c70900"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"84bade72d720396480920055499f4254dd21a2dac27c4d10c3e7860b6ecda111","proof":"dc2897b2bb22d09773544813c7ea1666deb26ca5b2cfaace99b3e8c2050dd520fa1208d186f3835e44836e13d0e1b0ef1ae5237aa3695da2b744e1bcc9a2bc5f5e95905ace66397739d38578cfbbe51fefa2bd989163b48feeee77f4fd9b6e2af6d0af0b24ac7f8bfc773ed3acd21354ebb9ec084e94f8b0426ced478585705f5fe4ac204719ec97f8cad34ee2026b41520a234cdc8b7fc7a646333a88ef36079dba76afee64e3e8d2522d5cce8751288bcebc1a205b3d53d31792eddbee080c374f5d3dd00fb658bdd148d58f0e8a56fc43d5cb2ed74f26ee6c2789f584400a52b0f5275da99ae010cdd8eeb0535075a6fb53794d4edd141a115ea3d70b591434c671472531c85fab65693d4c4f8ebc9ee5f5a889682f2ad3336859ab726c57a4986d9f843a2aeda9079172a7e633104fbbd19dd9dc1713caa88c590b0ea019bae4a74cba8f68be1924376411d041e9880117894fd1a5b4e80021a2de058a0a02b3001cc27b6beb04bb91523a117e2b62f9f00eb92d47da56a93515641c33440e52912ebd75bed83b403f06c88d86c73a5f74f94c234339eb528355b7d9034114e453799b04d6dab3bf511fedf4d001a15b390b30b9d73d8ab6602135d2a13b02e1e4205a42bc573c411f798888afc6d08d25e858737657c36c5d06068df34230e9900852a7cfa2ac038c37e16d5908c5dd27068a41470873775118f912df6bda490d5f78ffdaaf22fe6918140208c293bccd0c49dbad186215982db0ea671f540489571ac8686bb6dda4988cfec3519bb5fa44072f9d0ef952e8841bc8f046c6bf932b97d7cf0457a334517544af8c572d203df8191d2f5b709ca8b44a52261a91e27cc782afa919501caf810c87e0f668be44316597bad355788b8e7f970637c69f3e763301c5bec85a89684331adbee0a1815ad6a3277f512c6676e1df05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4cd6b20a34ca9df107ed86fe9d8c9fed82c4e992070ce58a2bafb42ab60d8b15","proof":"7c58a05cde577e906d645be748669fc9d3c5c03ac564372d76149afb2f50f042b826fbd8d1ab0b7633e84f748a370a8084b2cfc2118a4ff1d4ac416662896a021601aaa986318aab2fa35c977e389a135f45905982e141b4ceb2c4efdb4ac82e8ab1a3e256e3065fbbb86da53bbfbdc61a9e99319a1f90c943a0596d9ff50c488d97483785299a9ce28e93d8e391e207ee15152e35d9cfaf47805e595475300300046842daca7ba0e29952f34067fe2284d849ca00be2bfcf81aeaf12428020db1dbea22e33fe9a293d71d03363300677585a8e29f3413642f533dbb6e9aaa04746880454b1a2fea35a1127176942c443e268aa50f69f3e0fdf2fd99bfae353feced46077c1690ab8ebfea802227798a8c44146f2fee607ee98853bbbb989c27e6fd6127047ebcca65b177ae7be648021d27a571cc214412f3c72ec9d402e1665692f4446bbd0eded5066621fdaecb3864c409503346148f503ff500e04d497b5687767577df568818457977e4737e8956672eb6df4fa33a0fa3bf6cd0f27f799e3166b935ebee47e0475e05799f97ec9cd43f444907b0f2e10c5ecd3a94c43844fecaa2987543ce647c633854fa2641a34bb6634850fe211ef5bd3b625b947dd4e104b1dd00cd8934753125a9a14e9dc870b85c42c5e446595d3f442e44d412fc47850a93caf95505bb1e485ff81b8efe54d38cc41c3efc1a1942622532f85688280a7f11f8139ab5283056d43498d72644413ca0791bd5cb19cea70a031323649b9d73d222cfdbc2a36bb065fea527505eafe964e091da49f1e17250eda5726c964b0adecdc353253493ad6ad4b0ebcfe1772fbf2905aaf8872d3bcc8d1500eae13b385d9007aae9ff5dcafdb81a4dac6e288488eb9f7aebc376a12390360eb9c87a0323d93f08ac95f39492870193ba82047d950d4d90e17cdabcad7c1209"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9a9b40f4c0e71305c28167e12d100d93b1c547f516be1469bd5910190dd98405","proof":"2e68ea47ff115a76c7367fdfc4b16714b366ef5337546d4357e4162d0340f67b0e95fb9c732799d3c56f1704d6abc5e1d0abe041f3c2c6a5b9cfaa9981de4776246c2b2881afa3a3e4edf7a0e2cd5f1b3627e7422cf0c680199c13dcdff4534b88ff1f20ac169e07c4388182e4ee8d8923b1bae14dd9252a59e36176a9f4ac40d38fdc4a8069dd6334e529fb07e24665a703771ffb65af3b519e8278036b0a04e62d6cf97e4c23160dd6d20cea13459735d7f931dcc0660a953d0aaf464c31072aa845efb61e018e3951dabbb9cd49646d997f50bf59191bd09bf3d081bb4608c4fcfb8b5322c6f37e556612f6812bf9c5a5a3a7b147b6db10e495f632ce9d00aa772cba8cfacf4f84fa542c1e25eda5b79075d54d80de175e05d74f23d96110144d8dfc0f914a97a83f3644d9d14c911a6dfb8d180d23a8d9990c99d074cb5970cef2e9dcadc3d181ceb3a7d81b31ec41d324eb714a3cb2ab6934a9babce0731e708823c5d75621853f002fc398a42fc407ee0073f8e361603262f8eafa996cac4908df9782afc62feb4143fead6da8b84f96202ac05ebfbe495c29ee5f80423aaf2e96d0a8d1a6e0f8925294bffe965bc78b2c4657cdf5541484fe6eb1f21d20f5dad06606f960d9d6b2a7ba18209539afbc1f2db3a0f97b4172f865799729f05b60972e570b081102770bf534e3f831daf41fbd7b12eeaf22fd37a4a32a46c8056a37cc724dba8884a0489d68e2514471becbcd0bc6bb18b74f894fac4c745e3af5de9c511dc3775b1b6101482d2f45379ef19365ea242a08030b7f0ec73baefe5ae300bc5d19c6ef287c03c760481c6e0efc69d8b513d120faef26c57b4fda0871500799225a611a66e8ab05174f8a83b2f09e739d5cfa2ddec941515b07812f0ea043e54110419d90643bd742b834f220c07b87564014584bd63246310d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"eafd003930c02490b9323989b3cd303f9c8e75191d8070a2ea5cad48ba3ade74","proof":"2a12caef6448018412dc879a56e462eb0250c399467948d49df0807a522a33543a1853d98bb04471e5a97cc5419d1b546fac9a6c5e469ecd5aa1321a981d3d6b783b637c5f0c73088f8e22cc3bc2581748772e70a60729c726667391307ee81adc8d7ffc6a031b2b7edf22078da985afe39effd41e93a3f5e47e4db425fbf33823bd42bbd482524ff907d863d3926ffe20be729ec2fb70c1cb70ab1bcafc210da59352bc9a6f603607e4501824260a4f43f9dc9ac1d3801536da4ff1891d340e18f58d35d0e09713362fed4d8c3208d607886aaf411f7d87dc2b4b245446e50b080eef4c9633e372791ede6f0c959bfc2a03ca76e42eb5cfa66618c607eb3c6bea0f258b75c73803d9f66df1854ce049b1c782314f6dac44a8335c3f7c0a9072f42a042706985bda02fd188ca817d614cb14c22de51f8e5e44a8d94c0a113e0d7a5c906f58c4f49754be141def6769028a234cc184bac8514245cd71fc406e77fc198fdeb2b2c6d8cc458b4b28b380c4084535194089a3ef51e1d298c19a91396ee2687d3eebcd0fc7c108bee5f33897c2b6c941c37a9b03390fadbf23892a1bf46ffbefd00077649aca411877b415c3a16d502ee312d2e3ae6091b19de0cd0630b2d1b41c3c5e060b5fe3813684e2370c284bc06b79da58e57db1661e1be10b28bec8f719421471fe6e41ae55aa48843481f52745986e81a287337859119d6712c27c45e795258bd479d2a4d6d3ff1a1126bf368bab616f3113a26cdcc83b74e03918432ae9da0d9b485205e429fbd7179f441bbb4e105767a08fd25df66c3dae0d45798edd386d020e8d935cf8bafc62a21d912ef2dd582b5ee2d1e310a31b0d4963adf16d4a24a5d96021676a6b79738f433c7f712a97e0b23f5e6ff2680dbf1ab1c550ca7cfe679c68b555d5447faf7bbd13fa83e30e2e5d1487e57f460e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7e9f987b7a46ebdf8e603d8c867addb4f927f180ec43fcd54579c06fcc7ed86d","proof":"f670b713affa33dae95cd0d920e78d6680d45c55ae3f427b343ad4cf458f174cd2d89503c80abe948d13bec39a350474ff45026010b6c510bb30d3fb9123867606229515db36b5edafb47d011067c8abba3584ab2a7f09a534d6f81f98cbad08eca8e8bad01422236e086a20eb111a2673de5124bc45206e6821a4478c2d4e3b8f5ac6ca7a4970722e69a2c7298b56d688d324feb18f23779b07a6161071620fc9db8b5cc0c4a93382d775d851c5bbdbcc580f1bd2e13dc26059aff50d6f0104cbb36c29652c16e7b24d0e096f69eda753bbbd727f0cb732c20326d2560204025e1cb5cf968287335cfc00815fb0758af36f31d5e7bdd7c4a622cb915df3d0146e98c7ceb51a1b512c1ef605bf72d1a1bf056462c0afca3a91ffdbfe9e55a51be25140e83b307005fb626dd782aba6cd041ba0901756ec41274a9fecebd3103878e74480fc8a137beb2a26bb2c90d1fd8ee095692860c543b739303023a94770840caaec4a8af6387e64de397874c2593f66469daff0453df1cda00a9641a1740035e2b4aecdd6945d17e70d8bb479004a35fd320c7cdd6184defbc2e60f5436b2e42f866f434152a87aa228401f4f4dbfbe94eb2bb7c89f1372ea88364fd44a960e3c2d27fe0fa94acdc6ef9190f0a531217ff40c6b813cb59a63911271247f9c69b9f7dc6a06b3c7661f20dc16b0903add9636d5982c780ac62e1343fecb2ad6f4103b08b87d684b9e13941c8bf14554a0af64f7397d56919d0e3159d6075d52ac504a7f40912d7f163ae239dd3f58beeb523847d9e339bfe01d8d290df46f94144c7d6b969f53dfde937cd5e665d93edcafc7dbb541bde4a68dc31739515ac74c8e31ca5c5782b755acf48a26275ab4f1fdd2be6cfd7b596e717fd27dac06b2e7fa20786f9147ae65c803271682f07c22e7bb78aad44bd00f43f932fc3402"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a4f547231a8a95eacffaa735503f64c8d0f28a9ebab123e6684646f6c7ebd63f","proof":"8066f37f5da1fff18f477740ec3a779e7d3e93e371feb32642022ee22de4d62b8edcf88d0c95d05f88447cfbe34309166e55648aacbd6642b3568cb1f315b9258ecfdd41f2d18e2421a9f424a933062b3fb538525e387829c165a00b238e0f13a60615ca7b7de612ebbfcab0d68a3548f6cca835a4f3b98326bdf46f7e399105ad3ba81c6fb4424135eac4b9bd4dd30e369cbbf98b43a99f64843f0435d9220d08653fcc4da0a5e1d6f2f11cfa3a5fd482eaf65352926d274392b113238f630311d957b1ba31962b4935ae5a5000eaa38ca61c2f6cbd8fae1049fb2b19979004566e073ef76a81b12f33b8dc04e9f5ddc0be8b431a9455e28f991e95a3dd737e2ad0b80674b3112446483e7bfc28ae9bee600d80b5631f6613d5042822ea5127c6cd5f1a47321bfae7c09789a206f6237276bfa6e1cfceca9d00a441c1ecc0259e77dabfdd315dc6c30b1f6a158379f21af1aae5f658ded481a094fa80841d59021ad4e4f39931b73b196dcaff7261aabc8619cc72e4396c86e8dd8057902605849de726d700e79a10de57e4fe42d94043f3e4cf51586ea4bae8f759b8392e0a16f4f50b75d0fde130ab37073738711711dcc8aed97a2a3b8d501c60e70ad931c4fbe8e7a679348fae7786e8bbf4207884ae16c4c652b9ed4e05a42f5ae515561e0d80c9d238b0110fb36980c0337243b12424ca72556f950b8eabce333f9a7f320c62094cb1715f9619ab64362921eb62517dc9145704ce50a7e4772a168a2d36a3db7cbf6bfd23b1779883bce94e3ac9b4324a0a201b6c82c46547247a5e60ae8ce4faf592c5bfbb77095ef40f2a832913594dfe192da7398dba86cf807c654ed00a328ee5c87a170b4147db1947581423331616bfc2ddbe99d1d493d8be04874ed4bac827172e6a0cca8e645902f0cdd0e75c09a9892b70bdce21db5f4a0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fccc2e2eccb0551bcd544c15bc843ecefebb1cf9e849ed43ca0cc234d6da2318","proof":"f8854ce1290023acc7fe3b4daec30e2d755e986664af27b315169915f959b57ede12de9ac49a97963a06e598e798b241f1b058b05fc639c3f85cae3a2a9e966190f3b2befdb955ec6dc4578454e9b2edfd8581d5d3de324b76140e44dc01555d1c72c363fd86392e3f7d31503c14023fac632531da4c772ebbbafa09267eba0075885b96e3bb3436acc331e6ed2de06e138ec40a54d57f879d28990ab31ee8007f9dbfbdc21e551f98ce2d7d1e5b2ce3e8818631ec251ee7a4a5c90c0f4a26098456902506d5b99be6b4113ca28fba3f59b6d14a9abc55e2964f042fd5915b0d8228203a3085975dbbda03c7e678a7f2eaffa90fce9fae9411ffd427e61d483e8a2d3a7124676820bb02e02b006fa85c30f6813a231ba0f0d41fa0a588ee4320b2850328d371370d6b03f1e185e81d894ddb29c3d53b3de1eaf149554119a01efc50ff3546c8df346b2bcb24d4f5886d7565189fcb5dc6ec4064321586357b5e6aaff5de655100009342e06bf300cef05452f8598c6f9dc86d9ef0c76e105845a22717b3e14dddce963e75f0a49fdee4162da3b935ccc34ecd7b5d9eb590c152a239eef16b49a093eeb68eeff9d30100e9fa8ea42f937e3cc511f7121a94214fa0f272476971bc68b533928b5c4b561babb794be7ea41051acfdebdce372fc0fa4bfacef3178cfd07efc7bf5a21e95d79f327470bfa2caf56357c2884966a34b923112290683a5ebed144ef06e820edd4bd6a91081b4ea5ed164efd8f379481fd4f70430221138b1d44516e173e04da293043895835b938759d4fcf9ee50db56ea9f216d9698e0c370f7270e4821d8cb7f3efb574498978d3bec8543d117c127ba8c82f9e42e04314845e932d3ee868b3bfcaf52c8edf4881aaae4a74f28f003f27e9d553d6e3d24b2cc7706f3695048ab4c970ef972ab113e3821f7d316930b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"901140ad34c0112a27bb7972adb28942f14971d176c9b0a66f27e329da902912","proof":"3ce9f3e377490ced2c91757a6de3f155b25f5e770460243db866437f800ee855f62184e25eadc79d8fdc95361721f3a591060d4ae53596792057cb0a3600eb4bd66eeb686a104f3c979f8821127c63af4e6fb5d32f3b1e1eea1621f31d2fa533e4ad930240123d9bf48f8d959c42b22b04b99afbefaa9bdd4ea29ca29e0c4b29708f8a2b35e3d8767605f8db822953ac61cf58888749bc7fa88025e5be53f6080aa05303af478a3d7b7ed9b618d4c41674939a27c7eb7ac87a2831d2e14d6e0cc9f691fb9a859b1615fa2ee000540dd7e5e908a735c7e687914454173a538c022e4a0c47988710d1320fdbe8f1c04f2a82af608d4c977c454af3e1acbf5b226a3ab905803146888e1e3eb4548e04f21057f341b23ae66e78c02233c4441fb85bccea5eacf72f41944a9cbbb2104585778679ede2884437cdab1a4ba9771302468430ab93737590770a5a661302da40a0b6e2c1ae08d692ea0845c315d339284d74e805cb272fd3212f87bddbe171283c9a152b7b582949a70b7f4c40ed27aa55a0c3b12c028298b9c5748524158a40ca6061a3dd10c0e2de2793159074882650887ae18017e29cefc70a4b4b19f62ef7928b93ef15705534ac6d3f8c1d8acc78622506c657a45d6007a7187f265ef215948ca0829184ea429686f343d212d629de15ee31c0afca8d6703b545ee6b354cb64c09d6c14a425f27f7489e69f1aa7c4c081edc01476a55521d098f84813a58798445e251fc0f9150dde3c8022da4483a9e40c07d94ab9238531d73b56b03af88ce3cad7bb5ad3c9bde324eab943172f2592bbe538a478f3fadeb58f44bc13b39920663ee894f1fcb0af3c474aa280bd078154732f9b92b080e28ba75c4366afbd8dd281562342be50b3f056448ae09c6cd0ecd96ac07421490e4debb69dc31ddb3ac53e7137aea25edbc9eede95304"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d44299a58fd488e223af2a5c8f82f3c956b65cd6e36dc02553f3020c691d5847","proof":"429e7d42c05ce4d0f1b818ba703a879097b0003142349c028c8ef05b55165962ca535cd0d996d7e5c456c47e08c2774662c36eb9d328888bb43d35395bfe05276815c93b715d8ec897b7eb4871909cbb5e499d302457316c3d27243c5a359176342d5b3985c538534878de2a2fc8d5ac7c6b00072c6deebce8accaf2bfde106f2af92dee75851a98a25f62f543514d833c60ea57fe51ca229aecf00e5e27bb05785c9b59f013c1239adf0260bcf260e1e75d2bc562c7312c3db011651c30720d7d470c60a317edad27882f7c3b1399711a00564833eea19fa0c4f77930b6bd057e1c06126e3052bf34bba98b797bad0cf5ba3f2c12020ccbf49bde2ed34a0d055e7e9850cd11578a9bb9461478688e97f86f3db64765300a6f48b4cfe50b0c22286023deab0ef244d53e5f1c6a6c784f626cadff6416a92f91dc118bf9bb4975e4e16deff8ccaa7822036e3a847f89e7e735ff896b98fe6bc30bdc3f28bf9f23c6aabbd57bafd97ed6590b71187fd4610e006deca05a7301c6274d59f99ee2606ad2e729bb116128b03b6df605402b166a878820ae1183b7ce400c532a7c79394a96c8915ba16b912b76c6195213013d686f982f7beb4914e1dc1d6b2c0b6c79ccad4b15906514aa400667a2d7969aac6b61c7240569f025d2ba1a8d7d4ffd3f1a80c00a839467ee6f724c7c2976fcd415de3c6825e50792205246df8b4dc5161435e5c4ec4f3c5ff03c7075d31244a53aa07ed44fc5f6e63a9c15b6b1567278286ea812a5162a3bd71b1c677d0f10fc6061490910f1ee72362d57921d407b188cd6ed534c3529436d67a6ae5902a6ae9ba709ea816e6cef5b92047fb68c743d4ba91ab3d516fba09f71dc0b419c733c047548f62d5d3163fac52fd15bdb3f0eb27faf8ba82f690300435dc5bf5751d5c3c580ec62abbaeafd8bdf0d7e1b0f08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a0314fed01d2c88ef1bba75218201e84948111062d9847c24926c0b84a0a2153","proof":"f2a686da25c5383149812c90eb6bdeecb13b7504e392bbeb64d9b7102720086d705767f53533f54f9ac176702e090dd04dc0fd2bc5731f961a3b1f6755430c6504f333010e2c91fd17cfd9269799494b76cc37d8fd3b1f1f3e85f4efe6a32e3a78f5f4a2f3abc61e5ae236ad630f5bc5a90ba4c07bea79c4169e647e52a5167a87d6a8576fce2495da2b2f55e9c75198cd39c55c8db64d14cc69bc6314ced60684b70da2ff3724258254a1db91faf93a511718e5ff667e5162a1f230eb90c10ed727ad406493f5be7cb3fad557235505f7244d5a4eaf4e4042843d45929b8f018ad929fce9316b1e5ac2b29f00d2e4b6baec8608b924fe4971c6276b3c76dd066e2990d0b615203a82bbfb569506a2a18e1807c7fd3a78a906d03cc948dce42ce4d6671409eab9b290fc73f07b2d2797f86acc469830efeefbb8c1ed1c43195456c3efd08aceaa067b7d6276818e8587436e2fac3a450fbfa501a280b8d38e0a761b748218ab61310c5b23bb7cbff4f656a471b49dd4f501c806285223d8834ca8202f424b3767a42595d21cfa9f042c36e38d23bac8b39db36bfca2e26c745200d1003a4ecc01158b55056b47fdc2971f80f7ddde9764f8441679c5f7e87e018ac96fba391f720435aefe5ca0a22ca63f5978573bd8d5cccdbef586b3be3257c6f3fac7ef3a4b6075fd48270e31d1dfa39a59d79f6710d233c00537c0d8697a862a5f6bd047e5c5f30a01f2596fa504a741cc2609e5b1daf81228608696860f82219d17820640a55c2a3e648cb71350d92dbf800f21c0cafa2a48ee42c67f03b0db3e24539f0c112dd05378201252e9a684615225477c53778c30da7b3bc324ec5e18c18d25083151ff46ebe3a4e2673a716ce7ec29602a38b6b5d59e05450c25d6cf3828a8c9ba41f77cab978e865a8b2995d033166baf7e1f20f335fe690b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"16c81a6c66b8b390cb59e95038c5493dbdf4e68ed6f010349a7512dd38ea711b","proof":"2e886510f48e410ed19dba9d58be10b47b793f78f8fa27f27af66a5045e62161e8d054eee9bd0daded6b9916c0e2f16a322acea1d69df668d992f7362973c02a64db1db05c58570f87c01d4b7d5f7750835944cff2c9a1093ef51b880aa465706cca1fe4c6b42fbda66109ee6adc28ef879b28c7db2dc39519b8ff29b83d326bdbaefb33cc6335c83b457d36d63c9665c716fd18bdcfb3745b4feabc8ddebe0e34ef03780f4fef526b9326b59764a4a87fdc6ec654ba5d85938b41b04071c60b1d036d2595f90ba6dc312613f2360e885bdc6dcf0866e09a613e3ae94db38b0978c87fa072553e03f608b5f0496a55691093b307adf4bfb6d26811ee96daa06044a17e5bf3cfe54085250da9cf64c53db53b1412ba100b95c475897789a8e679f0e949ce90294d8ecff7d789fe8ddcf51ec645c2c4b4803b20373aa191df2205082c749eb2dbccab0d7298d289ceb8871970ee65e7f2c31f505fe2efe36c025adc5282bbf368521ffca4b4fc75b5fd37b30d64c5167f173a45a0e7c9d45a0b007220194671d21e0fbf4f28a6d253cf8046a21da20658b82bdb742a8659125f7a8220fadf398e2cdf95102d54f1159954d09f2a6f65ab89864f107463c110bf2da019a0bf00e8a1e5ab57401da514e15f262746e44f3ddaf18b186c6d8ebda41fb201c84125098addc8476e74e28b76cbee3d8b2a04944f4f3138604cd5dc90524834758403959c04b3fba5660fe8c320e3e652aa1864f7557a3d4f2cc6e99e13c24bd371e1179c62e512635387b0e5f1608d1210d324d204d204d6ef2fb8bf4e72c2dfbbe40c55a044dd9af677a72fd0b1d9491038dd2ef36bf89fe587240960cd7fa36535eef4518e5e82fdec0281cd653345c27cdf5a91a31cff474e56bc0cf467e2757ee8779ff6bba45bc93ad2e13d7fb525030e5750c8caf7fa3778c209"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c20f42a31064d625a042f51d1cfe65a57c89b0290ecaca9c565ca0aa3887fc19","proof":"14b2b445be85c4c16f6d86de7b128ef224cc6f8880dfc9d3b0337051db1ec714004a021ad5cfe72dc75f7b7de6a94447a5bb17f1ab305024464c14ee7f4d5f46eee2631eed3749dc51687d54f36ef1e48788b70a86306854c34e147e0d099877fa67a0154b41f1a513aee2ea041dd4203ce579ef36f028d4be9c5db007be9b1bc1f412bf6051d19e381866c6762b0a662773bed697047d24e19c77ac981c20041484eb97ddd240372c38e367ed3d4bec5405ea293bbb4749deab4e0918985608b071cb4e5c42947e27ea77bd62e81e966616562226842aa8bfc9173e4fa68d0376a5abef8a11cb101b856786ebc017b5087176767df969ee5b16d6a0ff5b781924c8327115327a06a6a98ff4d2134d3ec3b5c1cee564539c58c87924810a0832d6507caf650d0e6af2f9d8f34435428282b29971edfd3469621e5f9e76d1af570a60eb5be8a2d9c08d6e8ab26b0c3988dbaeba9d0c2b1b0c6168f00d61bfbc3f3cea01fa2e24d689cfec088dcff32851c72d4f81abe530475d2a580fec1a58123474331f8a7a23cd19758ad272d7c10663785f7887152ba5b1a930d99a70b006a8b325ea207b110d294c6a3ae21ef6b5f5ef5432a236fb85a908f4dab0fa5a056272ed549c49bb6305491172a637d252f7b413c8a7180f66601a612d08faf07ebc6116e052c135553b404fcdfc429120183b2aa92bc979e7a75329a561eb652c0acbe469d6b85c4daecf06058c00a2f68901bf451afd26472100bcf56202e32068365c0c22129bbabd012a0bc8cccbd81876fc9dbe2c20cb61b47588f9d35a536067da9b534a1f9eee342f6c6e62d441265f2b9518a5564398ff8036425c3b10712aac2e34ecba3cda7ab113da395d7bbdeb936170e8cff2e2fd87840db1e30198c2af0f61da55f169bb5d6b452f495aec14651d1ec7802931046c44fd292b08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bc9a024b1765d62c9b195df49c898a435f5993ce7532dd1cfd56db7a2dc12e65","proof":"4e4d0b5fbfd112fb064eebed4d81645c6db0a3cf877effa4e999e2644f70ff482273a3f70df75506eba5818ecafad6cc0e7c649f2631db9528403a3cb92bf74552f4e5e86a54820d746602384ec0a2e9a97cbd78641a956f1401b629b9c9f729c6031edac20ecb03b90c24aa936c2fc0360d1035709079edd57d21ed9676ac608990f86cc21ec3ad560e218b3f34560b22b66811ae27c5548382047d2ab4380b92480389cec98feef045f5164fc4be2f27a1c049e4c774ddf676d8c0a3a80808a28f5a4995c02a2a088597f9e2ed3c3b20b3a868594354c18f6f03aab0a4490e58849164526e5af5e908d75e491dbf5999679c1324896886c281bdcb719b2402f0c468fdd89168d879bfaaa04a4b522a2ab121e152d5fd4e64fece3fdcb382281291eb82cb9721b5d6be06f2d7d78aa4b30454f7b5e2ad8615b6867367b5a64ede8e2d6c1994d0ad049af6edeb4cec6ab989292b8136ab3f1eef9e3c6922dc1746104b7b45e69f00911333dc54aa58422f598df9a90b336716cbc7ba38d81c192e2f5427f3d9a61d637a02341009a54b0b90fc0cc790f7845f370db3a293e94c3e2e5d0e1d1c5f71bbf013228b76a5ec4c2d67e44f746ac0f49fe8088d64094a34c60cd0f790ebcea08b976c54ee1140cc6b4e45407de3dc2c6d6a4a857a2355a469b041e33e30174eae0b2666ed67789d28343c1c954c92f55caa3fe02cc67f007caf63fee051ecf24cf9dbbe1aa45ada98e02b3e33cc27d01a5fbd534bf262de7cc6b198b841d758fd083595d1b91848a58cfa3b7ca20d01fece8a3a16316fa29e396a1930b6ce1b15c0f43750ee3051abda009aa12676a1e44b99ec182d6874049a970b52effc507c4aa2113a74307ab30ff9b9ad1ad20e7021a2b35dcd0b772c492c58fcab360292bc36c693b87f7b7a38c882823c3d24fa9f230f663c08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"826edf364ff0418da39e26dcf8e6c33eed9979f27558f802080517d7492e7d0b","proof":"8895bc2e92d4f251841cdd7cf3bb7551a28aeb51d0e9778b9eab43984d43db1ebc910762e4f93ccc0cf8778f4c83329fdb9a0598e3e36203cdcc37f08e155c64ba9c059fe86a7bd1baa367d3bb86417d63cacf6df83ad0f7da594b74b33a1805e6e40dcc6df23a270145aec240fbeecd7998cfe2abfc76a1b28fc683238652386b60ece97952a308fc10a2300637406367dd7f865f83645ca150094cafa52103c930879b40ba83c7d241f85e04bc60b4d0973ee8b44b7689bbbb32b2c7c18a02bc7236a4e56b27edbdd179e0ba6661d541b00ec2ffd292d0164417fe149cbd094a64886f6f77050adf57f1e999998afdefe7b5039c8e8009f63a3d5a3dab25093e92266adc37992cf23675b63a1647d447e7bfeb2f70baf83d6e0b70a100d01f54a24aafd2d250cad63d80225109cf50957c92fbda2d672e166a15dc057a195f9cb7e4ec3f6516dbefda252a9de819b4cc105899a818d45e949845167fe2ee7f5c82f20c68274c6cfd3a5689767e10b006f876bfce00a98d2215c622f53b34349cdec4058f6a8988074e8afe4016ad35ac46b01478eadef354dd056acaadd737a6f1394aacab08ae822b75abab7b85c362ad6b28def93bb43d79b51ae4576b1c80a6d5c8e716325d0f1a514f46b9b7748fa3a0f3b1f62c56c2466277bed28b470c62a69a2e129b08725e2372ac863e00a58679a15dd3a375f72569ee8893090e0a3052eb753977375a66bcd597eb8b43ea579a7789a09699698a8293d2e9df6c34576368366e1a8046642c35825ec92d7a5b8fca78f5635bed7ec46a52e47e6e4a03899341821ac35fa2a6b02432171cc8951e7e5cb73bdf0bc8669af2b0663d6bb6f41e7f41878b9eed9b32f27807cb542b44c12dd44da3f18e9b0870c99209d395ff163952a0ceaf13412abb924c5f4777ace0f438222ed5a5ffba04e9b205"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5ebf28e7bd096c4511eb170c6ca6966fde3bd69bdd85a79023f3d576c5d25409","proof":"067e1c004283564bc3023e90f5e2559d63c1c164822f1c16c6c95cc916ea8b0e54717ed4ae5772a857c212501a038d37d3a2e319d8e3e4997644d24c93d1865a0a588651e045347d465b81d76691d720f8acbffa9e28d77538a08704d622c6657205a3058e472727e2ddf64eed48e703912f52f3cf4cd98f1ace4c916215cf3d04dc079ad0206df092424d5461f0bd8ecaa1efab5a7749a0ebc5d44036e2e70159379c43d2f72ee7b3260f641f297304dc362e53a629fbe2660222f4555f2100c73ca5200b6de6bcb9a51f9ed0d88407e81e824af97f2fcab707eb7ed9766c0d125a48469246a70004a758c9a8f5ede84396adf642c16ab7fb1f60378423d023d440e09580a436527a9e0cfd54239a8d4b50f0dfd4ad278f5f1331e8fd6c0a7290b7482a6b7fa86cc1f93b5a38b32a75e1d69631663ef0c36ee09e210116697b36d9d237d3520e255425795df0043b17a3273dd251babafe7f2b28206da4f61b7ea574b073fbcaed91e10dfb4f315df490153ca7cc65c641192df81934f0db21c8a6ea0fe6b8ca0570a2ff9564fa2a675a256d9ac197207f87d8f12cfd1ba77af09a2288ccab35c251c33b03e2101adcf74a2ed78a4161c061795faf6ac35823861ae3cb0198412bc5e42d23580dbb93e995840e8bdaa9bae8842a6820b8955ef242da716091e8f20792ced252b52226732873d2dc9344251b583765adf82660d00ba8a1896c38ccdb615b61a08001833512b47ff5167ba661bae56f0ad74c7d2ec68c3ab1da72cfff7618d4c843581001058e6163f862b8f68482a1068c314202574fba77eeba8e34750a51b43779d38b4d38c4cf93571c2315814febb4fb6e75b05a667bb2300b9be409fc59bcd5d527c2e48f188141639acd00bbb7827b05fbb465af6ae93719e254a1d1c39c62df7afdd77843b9e50968ccbb50bb4f7308"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"187632c44d6b57fe2007b27d2b350d00f03cc717c6b2e6405024719d11c39273","proof":"9ee90c1c2b41f3712613d57b757eefc88de67903c5aae9b392fd320055c0786922a19e6571190bbd7b0f0d180074c2949862cfc25d760cf47993546c2809ed390e9621ec61b569977dd19f6ad7a6df910a686a0b83a2b3b9cb8ead7fe52d0b5b8c6767f45eabfcb3f80c2bfb184297d55faed6927dc60ae27fda01bd5c79ba74652c6e16218fe36c3e31f545066034d1d073a2e376ce9fb9981887525a41180ded0a832fdac5caf7bf1e7c1f1745c6936d65fa7798ad01c09abb3e030b6c4c0b32a55a85c42616d67e1823f1841eaf90319157d4ae1dcfb9ac4df03362e44e0332d05eefee71bae6c4621f3fa71f1255700d752420eba8cad7fd5ba82052762a6e473725acd12e311b59c045b81ac0e462050b9e0161288f653f52adcaefee067e58f3b8523f23dfd9baee3d9ecc537245a71c249e9c729c1995bad21b43c831be4d09493f9a25eae08fa3d5ced14f99a1d66d29638b2e34b1954faf671eba53427656817af80206d3cf489356cfa47e3a92bbf5fb149d12c8c26e6b3d9c0c10a271b29ac87246224d2b0af2aa54c42eed7fa8925907ff3149bfbc19d796b1255e12a215e85187ab9d3737d804cee8ff69b5bb4a860f2906f22d6e02249ecc21da5b6d1edfc128236ece6fcc6dea0ab58b54e468b77142c916dc9c5d21c1f353c4d6f67885b4bbc569b06de7aa98a9060dddfe3d71a7bce9f45fd3fcb518b710c649da0520e37bb8e82a4b947a2765bf894e1129616f1b619faf8210f3c49f7dfa07df229443efd8a49ad836220a09710528d3ad0f82c7cee3966be0661f6f3ba6fabcf0f04bbafcef49531be9cb91240488474c9591c4b2fac5e937e4f4dd4419146eb7c32321f056f6b2df07386a2bf720364d609a255d778aca2a6ab94c045ca7f3bb0d0b676bbeb2b4a00985a474559001770526551e8685d29f1695c40b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"10ddfa2fa0e5b95dd37c9f4ef2f0d42452aed7892a46b111f64e75d1f6a62c70","proof":"0677ff0c78bed32e58f2d80a7e305e63f5eaf0279383d08fb51149419bf3d546c0e7d9dd29c5754803c35a7f665023f93d538bc50f6455dc933512f05923ac7cbeb1561c2fc552007f89967b4d2b024d9bc32439d51bc8313e17bd61303a871392cdcdf60c56d07557793ceca58a9a7a34aae4cf9ff62f488431368f6aab97734bcc013e0e95f43a98c3f65a87edd021efb01135dd7448902c7c88e0a19ff301ab769199296cc5c5165c4d0be0f29d3805d1c82ce0aaa2d9e71d0db747aa760227125eebd320bf131d294e44644be7a5527490cda69c767e692918c287b09502902274936dba1510a974f91353384bf296f7986dea4af43dbbbbfa82a1af492f30a09f05068bb39939db4c7d5e200f242cea42cccf2c9c248925b177632ac47448a43a36ea094502bed43567e787bd71e1966d5e9dd31498e672f0cd2ea7364dac6a4d3f75012d39dfe0ecca69e12c278a995fbaf4385b9936056206409b220dd68a38b7bfdd63f786e71afc2e081ac0137a45c5298b8a3da30b963d69b29b2ecc5acd77ebca20075ffa16b04410dd85e20c810f65a5e6a59337eca1ab8fd642269bf723c2f830bb382e12d788f1a6538b4def13b004526066297a56001abf23b061e40d67d4d64727dba47768e3732cee8e7a7afca6f0d7033a7326d2744330885aa7ecd9abc21f88bbac80b5dd379e9e63317d64a31b071e1e63de5b1c8252b2cc9ce5222c7129b464c2a68ca4bdd8f144625eb6387d0876e7b29842647c11c8ab0f97c4c00014da009a465e08f9dd8edefa4d3cf6ba3d86872818bc32547ec6f597a902c284e340868515a032f62985b11134ca9ea4045d1f36e6acf4160ec07835cd56cdcdb88f6670aedd625826936b091d5f6a846d17793ccefa68400e6034a800adc3e50ba5b53fe4af24216589bd98b3de612c5265c89039e7eeed00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"948e805a1ae746d9d61960ae507ba31cf34bb5a0e8124406b17a806cd362a178","proof":"2eb2143c5f29f33f63eef103fb60432ab1caac03b34a5d08170631e659947f5ff6ec3ee28cf327d2c5c040abc5c1b86e1a18047390f3c1a5042a7d66f4fcff327cc84bd88b8d7b9d2d6d5de2724ba4f6fa8a075a9bf76777976b0cd8b3e26f7e9861c47b8f2ac10023da5e3866ef2264e37bf541960bec215dcda31d87ff91057691b4ca5f5b7c73a9649f41c0373870485dab9a9db50f780145c9fb1f1c8e01e98ff4eacf7cc6400a2d19678ca90232f6b8e270ecf3157374605a5aec759904eab0024d00a3aba1d6dd04fb8cecd6b9b81395cb6c81dc1f9076cb3829be5f0698362410fb75bb55f2204d2752563e7783c2fa2d6c1d6a9d2ab560c0ce571260ce53dba3187d7bb978949407cb3262f11fc5a33fcf197aa188933ebac5123f7b669140fed9f0814451fb89bdb1d683701e1122d63857b1ee4c869f0787b9500ab238eb7f214d176b282894d100cb15e7b494db2b1d22465dcbef16b8ab596206781de7e125e569e9cb6eb9e4161daeb8e896637863a9a0329622715354c1956e3c9b3a1e1137e20ffc2cbdf8d70f8e810aa9186f87f20fe8f7e294323843ee1e36662597376f0661b16c046434f7979e26e4adb4c9a831d625662dda3dfd67749c2875a2f38ee78828809fab4b2785cd95aa8484b9d7a35de2548447572fb827326c0c521dc1347c3af1ae227a4f8bea819dea6ae2642ff710a2c662f6be985630bc3c265cf09320bc75d0ca373dbdf1c6f7a9c7a4d7242d171cd235686033510ccc8af62aa505e0aa6c1140128fa32f13506005a149a879f9f896e22c8f36627ef562648f4a731cd480a547a40d2984035c64a2a25cc34651a53ed5bd2baf6b4a41d0c9ee47e8296fa94c84b9c781265f791becfefe262d3c45205cb1afe30cda46337ef7f5457b367e63f1b3fd2e174d7b43130bd45697bf1d8ff48567c30a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4ac36f61b927fc7a6af0ed056a69b7ea037d16bdf148c6e8f7a130008947302c","proof":"4621fbf5e34df64f96065ce7816fd5acce9089f148b5d541b8ab6cd01a13140ee4a3c961e3fc5ae000d5b897aae96d2a2d86e1eaf3706414425bf99af08a452c62c667181f3bbd962a619d0652154c3c137a25c4baf66b537cdb65b5a0494e7520b4b159dbaeee491175ded273d114f92a222028a6266b79149fe374212e6a2a2ec97d8b3152dd09b0e73a7755ed3d3ca02ce6211837a5318c82d7925ebcfc0df0be33e8ac7706fe23e5b46301ff287e2ef74d73de023e607db2478a55cc960d82819e987917b20d1aa4c6dcce9c5daf5251a66542a85cdcfa2da59f36f8e50e84064410904ea904dc1e546aa192c72de6c34e268ccbaf6056ee18491f8df81188ea62c583c07fbf9eeab4a4910623a605c825abd5dc6808330a804e6cd030732693968a855441136ea8799fbc0b512c8771e43b94e6ef4276e4b05930d1550688b31876759fc4da71f6fcaca9fd7a3c21a7a882a66b14ea67c6d408570d5370eede337a6ca9e7d270a78e7ed8717f697be37d29abe8057e17d201bfc5b2ba1cc44186df958747953a88e1253d4c941ef5934d69ea379112ea4d8b681fc56511e2fa80401e15c5837dbf42cd68c9c772342cc5a46736b0718810a37031782637ba409ba41f6d7d50c35957f1d5d9b57d2b0855986e12ec95b56e0db78ec710066e8c126e6ba82809334c97f528fe99b9ba6fc114418fa63edd469c993cc03c686ef8419e608dfb42818d8858da2efe7c51522fcbb57f0758af79c8d6c6cf0f5f5453bc8e43cd6d46e9f953d2558fe8ae1d26b7a6a048fcf5270e03d87e71bb12325552c429c6fa6c3b2f4cfeef6e9c7fb6177aecb35e09c3d63c3031e86cdc1b64f0ffab1a44496ad17caad7eef8a6ae61edf76de553808f7cdc3706b07447009669af1471d619e073f8b53e7e72f0f71e3c1b3a8dad187cda77b86c754f6f01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"98fbd3ff8ad0ce6c9b8b1e8ec58217c9b811543ede484c6c1993668d5ab9794b","proof":"5cb4102d449e87081c38536f371377477e898fe6410bffa0806d554a3e22ab4f80bbb5303ca7e0b13a05394ea642694a86f5d346126e8f48c82ddef8404fd8387c79fba9266f1c2a09401694b6c07d028ca30491cc8d3750674c5d41d91eea012a1f2ab4fcfa8075dfc2419eee162666be7b87315450568228f9228fdaaf0549939a95b45e33c0327999acbff3b550205e2d883fb499e2a4b2d96ef6daff2204d42529edd57b2ab2270ab927d2dd11d7b1deb73483d10cc129328045a92ec100f12a32b3d7521f2a8b80a28c8f120bd09c958000f35291dcb8f16caaa8d4a7016237dcc30c258c8995be00dd55b6658254b0e23d3599ae113f046f547dc7a24092a33ec1bdc8b3913b2496bb45386cc2b7a6f36714f4675a8eecffa2437faa6d4c34502060de348567ddd67e900115c9c5a3536ab11b33f2414905f647635452ec3e2879783421c1c4af850b993df3e4cbe845d35971b533030adb16fbe8821e06574b8219dea1e79f5ad49dbc72fc029a7c4951f63b9570d218adbff24aa75b0a461bc2f3afb0c78532ab26c6331b69596fea2b449eca67a2938deee8bc3159f2b0b6539e05d1a18c9c651ba1b5ad16d96e2dd48a66c6d1594882cfa7012e3b9c3ad65b87f0c2f5e227ff495443d07ff2cbe64b87afbc4325e3f1e846c89b04fea29345fd08bb8ce798ba1bf377949b155983904100c6662e7c76db59c4501188f53b0c7e23694ec06f4ba790a1b8e80f5d1d062e8414ddc9b685998634863e66c8853612ff95bcde98b95e99fdeabd7d29642549b6092937e83677def39f3ffc68fe2496057619a99fb89ce7e5a89b9914d95d73573f844bb24f85785f277eff9b54cc272ae43753cc35c12e697463e2d56220bc928db697d6fb168dc656028f07479a51cda076810f02873e49748d149799103dfd9784faaff16a14952c0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ca1eb40986a2913eca6505fa72d64dc61241bc8c84d4a73c95d139fdc391d06c","proof":"74a59b20cb3302935ca727369728f101b844190bfaec286aa6493b70d3685930721bdff54f338dddddbdcd49922505aa28e0f16d951c7600283fce032e9217017a344b16d2fe225501f1416c7a1b8bac811f8fe07df6726a608d6ce32b51d134ccd2205ebc72cfea6628f56132d5858d2ad24e2fe1690605be3905be4d3f417f990ca5a8a9c07f5ed3546458009a9b5ffa9ea493e5bb6a2d0b1d0e3aac3407052278770dcb0510a82eb7d57ac0a92db9985a2a6b2c79f215e2463c2583241403d96677d67d2bb4e003f8f4c8b23ecde8ec436dbfb3065ba6c35f17f53c3b420e325bebb8d4f0ecb1f1730f4985fc5a3128309e16c381cb814d3839cf8cbabe69bc36acce34e4472d9362602067b48c2b47fb30ecebd24e48b020045eb89505333e1629c2bada07f22dbb6b609b82a316fcb17631ac0aa27b1702f2ea38d0de4b76bbba42b690a250494e2d9a649b799ea3ae8f74b5abe0f27c80dfe56813d86c4634b9ed28a2a0dc23f528a65a3d00150b367bcdef895c28f62dfacdb2130128fa6b8f0339a545f66f450e8aaef98e62d5fcbb2704f447c31e3d48a27db17e3f1ea9323ed147ac532702a5550bddad04deaf1c3caed83ca50134dd01feb2f74fa02828e96434140bd2a95b08ff8d8176ffa12dbf510ac76d4bd9da27bc6aaf1ea204af67733f18faa5d896e989986d88a5e4daa8d013c709a80d92f808691f7b90888dcf42ac642d98899a92e0218dff344329fb0583a979d2d4c18823ae091a94aa5af06a53458bc8f2dfd43f5346e144a77d467a83aae271a6264255049f28aa20991b6c49c92b6e5214da04189952ee62c3dc1591cd4cc4738ef4726db900b9c3685eaec7459ef7cc586204de635a9c98af785f978ab8c59423d5638f0d04a11870271ee94682025ec0f5b0d7596245ff2c5fe7e63227871b1b5d1a384604"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4c6de6cc52a5d341dece1fda8ca1c3ca3c0144a8942a62748d5bc6a7402db831","proof":"7848a6d48856010f32edca6c3635b0ba87fdc50901d6b9540fafd0c62a39166cb83c31f1aa7596880f01504bd6c338eff78e924e6a0ac37e6a76a2160549136f16dd8dc4c6df9dd742dba358d29a75ca412a620bc3d065a60cb58d0ebfb560219e4fee4b3f01126285640225c6d33e2cc4c0351c62e5f09d78597eea75ab3f3fed61384ccc872d1be698c03866a6ccdf035c6cb62a5b9d23f9d9beac6f77050bb7249cc64a7556dcea5f80a7c66e2e63f0104e6ff4fe1c649ec181e63fc238032d360d317f69acbe02622bee63b84ed240e77fe752960d62aa22681a644503019244466b0e14cfe95c4db1f174739647c8b9c661e4bf2afea9b753008564104f8cd5d1f784b7f06b309d6a45f613b24e7c1e912bf5a08be210bf723a973935108843b2ad1a67ade0f2573b4cd15c76add262ff5636f46f53a8d59c9548760d69128351e95f0041f5f82d07879337f586debbfb069d31a6351e4674f93705410dfad6eab6e6dea975a5f153e4b823f334eeb40dc25a5fe7ea09470ebc1c2c997ecec970192baaa26610f686a9e0dad26f03d705f892a0bb1e9e4755c91ea0ba5a8487f411de8a4f32365adc49003e9e78fc507909a9d64032106b6386d846580060ac3a14f2f1c60065908682865bfc9a96ffd35c7f0e1a242289197f3ad3340680c28d1ab5bff8fc7ec849991504c1fe5daca67c0f80ec2525143f3c8aabf64c0e374f9d03b1649ef0ff1782a40d4a6281cec919960a3ffbb893295ad908500d088f997dfab54a5c6003631ab6a7ca6cad9613452e45380094ef36d7367d915e34e65ebd0942c42a2c41b73b1c1db7e7b82afb7d33d04fb4d8cb21c48de48c4c8dcf0a330178653437036b8e49174d3e14aa4e4004f3a00f1a9868db5823350d2cd80d08cfb1f4fd698ff319edbe15fdb827445549f2dc4df168de35bc327906"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c01e52d0a4f36545c06e6f62a92f5d42a4ce225762cb0b24c54108d95122f941","proof":"520131b47dc989a2d81b6ba1bfbd448abc81d60f9f20d90960f238dc754c4953fa88dbe3dfc79182e29c360851442b462199ba8c61e5760301fe74cfe4ff0f5f4404183ac89d29399bf122365d1a1c1cd2d43a8061a433da6fdca234a42a1545a09b189d630ef2c0a79e78043b148268600444d2ffa8be68e39ae3012fa4034433290350c8347994ab17bd9361991b956530ef832e59858ce7395786d3c10d0c1d6c658ec5c7d9341637d4bf511f081862af879b0c32b7eeba73ec2f0edbdb0dce7915f8e3efa3947f286ad582b1b4fb77166b609e40484b6c67c5e7d04a380f1c4b4012943101e48541fb764d6728d0d805cd276cddab67ee6bb0edf5abc9592aad8aa63acd293d737b5d4f554dc8c39be56122cf276840b3f124b9a772ee57bcd62ad22268e689527f3b116af40d758202e7d4097a02af544ac6da47ef1526643bfc732383d351416dbd38d1531f86731b36e427589183e7fb63405d2b9062e42fa62d037853aca03804bed4a1844d648d4a92eec659618e772a8a9511a53c8ef36510c3a0c49abb6e3822ced871425f38f7f2049084bf75d32e24fa65f02dca94b8a4c33a33c0f09cd96c6ad3ebc4b260cfb589f8320d68996933634d336e74b0a9d88e7b8fe1c672575aa17c3e4129c8d3abd7a379abbcfb9602aa378d40c4620001b7a3329c97ed56275c8feed921555d2c73c7ba4b6d0f0fcd9acbce4f52606e2dcbb90f58b83bfbf0684bfad05fd3010a9e52b18d69e9150358e1b3770c67d8e652f69b7faa04f853e669057a21a6765820414203c1475e8ab130617900a8ee44c86872802512bdc59110b93c9c097110209b6dcd67c222a7be05df12ad87e50ed81e6af8d11a10d4d5dc721b0735fcd34b920bfb762bee029c3f300839e5f5b28e3f2e528286aee57cec50d2c3c5e06ec800b53d3f16fb8f59e4c80e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"96a23c075b7fcf6336542770bb40a1e79d9066ab59a2800a07ad3d8d897dc703","proof":"5ac22e379917bce3795ad2d3c288f76928f769172d6c93636f3b20f4a560797dc4f468caef8a39077d2bed6fd1f0b1afa7e59595ac39d50ceb1a9a442ca4365ac4122921fd81d102ad2e2afdee086742d651a90234b337919bce0aafefdc05325c417af031aca86909462e24b3dacf2ca21d563f3c59c06066dd778ae14abc6e99f5ed60731c6f806b81b3b4f12e28ea4ee0f8790b7f35dbfba20431afce9c02984f32d9b94fc0425ecd7de76f47bf58663e633c7dd4fe341c92cc2b79b5a900b65d648a7bfc87adc3931bc9a22b41ae94950d2a74ac3eb4cf2cecfde26a8501f4467c4ef4d1f1e72a0928b45fb9bdf3180f708a81fe71ac34d6d1e15b20c6497ace101b8cf095d65ba47d17d72750994364f29c3e1ea75010dc3e5ebf1f5e2d365a359c9d6e31628351621aefd6336ac4c4473e48b6339a87d296ed0b0b77764a1a1dd541cd885cead757fa5b4b80e99aabd867d76b75d16e5044d8d12127782416236ea80c7c566b4d90a3c6bfca1c222bd54ef445a6ee52ce7f876626b07cb25e6670a93216a7c6f7ed9e2f8257208d3123983c1f9eaf0efae3b243e62a721ac1aba2e53e9c4e8d8d6b68060360c17f5db30fb08d90eaa14c7021056478117e0f41224dbb2046fb3fe8e90eaf886289927528e13235abe5cf4ed665cc3c3674e4ab3dc4260eb878cf2e2a9ec9278f04f5f755b06eeb332bd3ccdbc08ede3cec8db02ec8c08a052050d1312190b38d5e513f4f6a0ade127dcb5da0986e5d6b9cc0a65caa100e904d3380ebd5275625d095ff0f15c491d022d034d99e130a4a04706820919f50b3f09d3e5545c5f4187f1395978092ec9a8a9684978219be013072a3aa39c02e23fce80a9c061948224dd23ff12779bc98b964e7eae5f7a401ca6d5552c719b8152562403f0a72831aa9efc94aace2a2d1e1c4057339a49c05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b6a5db03c1a256987ab7296637e7b57bac29b44715f877c25cafdf2e97c00768","proof":"0869402cb18292b98163f93ae5126d82249985c13733bf966a22cd9b2257e8209819b4ce120c5bb301282a56dd3c7adca8cc2131d72355a89b28069b80a4ea277c028d202414bcc0ff67550ddf16802812a00f8d778c0845e92da6bac3df511a8ab5a20ccbc2bc53dc508c1d724ae25507bd7307a9620682d8c2b31a0f28c25223b19859bc5336a78375b16aebd5ed149e482d0c4ec023f01394ec119ddc78075cbca55b75497db04ff709f69bef9228edc021f792282a491c7f95b3e4613809c6bfb236eedefa74ad366e05a0dca51f9ea926d3e51a65bd31af96d90bf70c0db6606444635e8e8362efb6ebed14cfb63e1b262eca22f580424088229d96ea36e41d38cb287c6db37201b42f7a449fcbf84c7c809a7d93e6c8d7812a0c260547c01786ab03fd5e44b8ef3fe959f6729d91a0a7f6d04e48dc68343ded61c3da37aef2625934ee6ceaf51fb64b8e7d3276b07cb1d72ec8e18bb8952e3ef7f9914c7ee2d9284b907bab8e6a7c2d04da853485ed8b7a7420896df9236325ec48cf117e63f32749e9b28dbc8e1ee8a1fbbc91a3527604aa1e6c0e7b254034b64f6a1c72212401c7b4c9a24a86f92ab918f7ac654c005b2a21a0eaa92fe79e097fa4336e96fdb38cfa2367f81d13cdee8cb2e9169d3bc180ebcde368b34ba473390022629ec23ad5eec9978de995d680e3b100f82692cee1ccfb46f1515bd75d80426d68ebf09cb64576071b9ac9bc80712bad211499d79d348e232267f2a1e83aee62546510c991a6498a5481fe2182dd70c09374c4af6fb6b8564b86b0fddb0dbc4ccc209776562d0e07ce2a621ba670dba1b0197c11bed318a23d1e867080babc0f3fc057e5ad36c80bdf22d51ee39e921c2d5b3f165d31b11160c007c8f56a7b0f79c90a97a387b56dfd6fe5df9fe371dc6cf2fa089fc7562504d3f68b5b39aa0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"06dde2aae5e4fcfa20a40a9abe030bc4ed52cc3b9699fe449623a2feda0dc673","proof":"b6f946de2703021a30f0e86d5822d66592215de2e6429173e4a3210dac6e4346e01dacde0dd04c75809e84d069cf5021c579b1770e6a7a4b537b22b005a57d286a16716f3c5418c3e445ecdb3f1c665d6eea1352e28df3befef1691f69426f6472d30c1c2886c70c2cbf907a77af754eddeeec3ff1972c303f1c787dab9c9a20cbca47a7b5a416fec3401ec37ae7ad1a667abbe1bd62d46b76b4b6a8193db4058dc106bc469321a6cd940140d23d088d2955c0e8c9b1c318972b3097cacf84054e7dcabc1b74434af1659d6a3fe411e18306fc868a5253dcb72e3cac9abdc202083477a71e31216b5bda6329c9bdf671522f6a30d66edd94a62d6f637f11953524d1185ff1833826ef26486549039114d37f8c18b5556c48a5f836dd82d61a027a24d75988c80ccc1a98d2464d3e5bf88677cdeb0cad155f476e2446863c864d8a1a620cf086966b98b8fd35f71c9f53b40b74eac00df44d62a3406d4a8db91804526d700fdeb1559df5a5c2c0b93cf0c505916f1f8a8db804fe4ab57f6f6c558ca88cdcf890529ae35bc152318163ce034e5c4d3ffe569ab7926e8649a07371f840af6dc4ee563e82328bbb85f5de8b4e8a512ac634b1bd6bddd4ffb262f0198637dd1b2cb4baae8297224d9ceeb2df00cd617e0212ec7cfb354f0ea3e7944db49480b5857ddd632f69362ef5a3cc147fb5c7c87e2d0cf865b57d2bf089461d9a2c9807b6682c123cbedda8f4b990e2ccbabf87b0eff6199254b282f17a6676b003ad004fe3f985031ac94194c8aeaaa33e97f112758b13750a33a4b2c2864efc2d4f14b8d4c512b638c854dba66c428ceac16fb887eb6cd80c4a662b14fd58bdc4c635b66fbb28f18443582f8106351354110004a851cbe7bc5b061a050404739b608a530f53f2fc6632b7194bfcc9ea5f88d4cccb0ffd51656d1c75fdd803"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5e054d7275d63c3b7e1812b2a11695f51fa158b06130c11dc8518451d01b037f","proof":"4e8a114bcba9c701ee72ba5de6c0815bbe021a13dd4c94027be0487ccdb9a46f8048fa4bdc32a5a03292d3b43b974e57c53630aca01bf78ebd13b1b9e242101366893c601d346e3ea61604da8c1a7a5c118ca1b58ffb150c3897d09907628505faa6be02ec678a4e8cb871139bdb8e486f37d1afca49a7f6196690a68b446d2a26aa4f53739d2d359b896dc46c8dff4586b175ecd83619ed6758ff169a01600c1030c3267d6a12f34f9e1c50b0a4b9525826432b86706f709da9a5d44bb6670d80f1492be132c2290ffda224f4632ecc3b4cc82e96abe21b401e2600cbb40605bec00733e2bb98597af4f89a70b6640f1b04e0243b15fcfe601cb3f494934f36d4de7ff4236f97d09dbbb11861b19d67f025d1784a57f7d36db0db7173f50a556849a9403ff4b895214cf9d9396ee04a6b0b7b3443399ee6f0c23e07d8dd7c037c19adf406c44ca6e33874d8b4f9bf70bec58bcceca48a29e451abd5b07d2a46b6a7d8737d0935d9d824ad847c5d181739d035fb03e5c712408dfe83122a1b1abcbcd11059d72daad15a295c12546339dcdb5c8c2dffdd1094d096dd707f4e2722398d5162e57f6353e9ff5011d9d108b94a3823199cd525fd2ab7147cd49d2fd4001fd000ace139e7383eff38f644799b84aeccb644e2b577303a9637433c79eccfa93d152007268ec539675d0fc103344a41fccbe7ef2474e2945b5aa7453ff6a595ad122413f0877d5463afbb1cf20fa9660ae87c8635396426795ef40c5c9203faa759c4e9b492f6485a78b3dce16a958e6c6b5eb1f85fa1614d31ed1833acda0b5d4166d07bbed5ef5d91870f7ca173f07c43e3f67a739e1815a7e53209c6821aeb41d026638b611d3318ea9087e89a77bc3f6f9e734eb3458d8d0ead05f54c954502cf032432b15972878effdf578683d39ca5655565356331c65a0b07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"685f95e3787bd936aaec156dc1ac49aec0e6b4548b457d30a144d2752248c753","proof":"c477abfce641a9031ed736e3ff2e70ef40c8a56f7a45c7314ba3038c3b41bc65a83d94ceee97036662483a2e0728cf567baa1172eba8018430a289ab2bc0295ef28166bbee8d3852d3640a17938a39ba42b4763358d9278051d1e3615ecfe3233021b832a8cd7deb18bb3cf7ed8725b098f8091e5a3066db41a7ca2cf573ef46fb8fa2e805be812bbe5a757add9e9fa2df3161956d9ae989fa475b5edb2bea01c06365223e08e8a0968836074bfe157a41dcc5b88c2ed3f9a5bd22e732a1760d80b887cd1db70ac37616c366604fedb10c21944c7e201f9ff17afa8f407e3b0682461ef89f14f00293efaa52304ebb88f359a6bbc864e2aa861ab427aa6af540284ce2738210c141046ef17dca309d3123cf88d024fcf917b311655f9270356dc69c5641fd4c4aa7a0763c5d326b3121dde406eadadce8204a25568bf7e77d234467571099f515133e4be7bb1928d7b25f7c940989616aec6f56c44389162b174c9a599df0f9fe66d89ab76dcad771555f118fdae0c4c38acb85f1e5a29d5546069b764f3e2e58da40537f464187604a6ab8ef7d05318ecf5a41ce8958712b747a373dc80e59b5c65d05939875a1a4b904d9fdd65c134234629462512db31f6692958f6c54bcf3444e1acab99e1f9324c8c88da649647b0b78f5a5e97ff96539aa4f928467c0fc88031fc951e8575edd4aefc24433178947080873b2da80927c86517fece421258a68893177bcae284623b6a4514d8b45962bbe798af4d1151a6670900b68f0b4a2b7298ddf28daac3c1610d104a67de521def0ab0a392bbd102ca2d4dc1cc4c1e6183f91d213bdb2d27eb7d909b0ebbab6d57078f658497275904fee3016e22f95563948d7f6a4a9461c96a0643b1fae45dbea2126e937d101f0ebaac197106cfc0a4427ff76cb9c75d21b21028afe5c8449b5300491053202"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cc7e44ce598dbe725e8fa5c3664bae8785e018575c8c5f4b5662023a84a6f336","proof":"78c1ae389204ad00a33932389f7df7c0483bf0a6c7898c11d1192ac0728e70078a2e81be907c9d989d1816a0a3863be3273798038194008d88431f66225c07411cdcb51a775eb0a95d7e9a49c2c6941ea0a32e94e166f43c50b2c168f2bc9c08f48658639c5dbcc6b726cc2cb4b752531934913973a998dc6c87da60afb0612d15d78a4fc896c6171a1c6d2bfd17673faf13beb9eed125ec447c60ab5f92ea0ccfde76b395eb92d6c396705ff1f9ef51eef7923fa3de6cf12c96f19d0909e60a0a8030f73c2b08f45f1dccdc2c18a8015097bfd712fc0ef8a89c9d0e4484070eb8e090b098341145866abe4c7d5ed62814f4170290dbc30808f601ce3e67991418dc8cc3f328dd7822b75bf570be68c8cf257b4a454cb5932676d2ae1b7c6f41d27473f9462939ee494d8df67979ddb1746dd588f62cd586aa6db8a2d6917029886e82384b3d384a583279f85a2cf6509e2e06b4c6bfff089e69ac17afb7d361c24747f57ff73f25185c4e849ae19f1ff487add3f5d694a70614e125ce4eb62820824ee5c9c1f17849a53ea68658db65b06bd9d74e2eb2103ebea1a3c19c5328fc281d17dda822260e267a9a93144041eb83527e4e99359293210512bc65084cb0f5a54dd4a10fb444caa334a087fc2c4438982f08718b972adfc552b9eafe678697efcc5169f06b7aa21bf5e78274b275100aee8c555d5d74776f18788a503eb2688fcb91d945861b63d6a66c6b6966d1735192dbe662b8f4aff472130a2a406c12236a610df874b7f42beb3bac50d96e1f69da75f63f4604158e44878f0d595a85fe7372d07f8f901384db084485f01acd242e11f80786ed0f14756aeee53ef8198d9cc155a01379a439084f815b62a8d7fdcab5363ca3f773cea11c705906c7411d9f0636c3bf6dd098bb058437209c56d225ad507a065a4294c230bacb0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"36320be64b39a98085125ac3db9a40604e3754a67d21c1e32bcb4f5a1f01730b","proof":"74513cdf993d3de0941f750dcbf7116fadef68e5a83ae7f4701a362b83a570383236151777676817db2420fd2cbc57a70e49f20932d55be244f648977c4bee7260188ec5ddbf206c146969dbf7f336fc5bec2084e6afc608a3ba9165491909471e5cb8a52a8e5021a7e1b40a2b85ae7189fd47f1b292cc15a396731603a664011a3c9824cc650b4e796d5cdd98182eb4d5c1bbcd510be7d76d0770504b9d430422045c9bce5d1b11c21cdf4104fb28ba9672cb7ea100c1758fbc025c11cc1e0c9053d66671943f5e222a56b82261246c60ace204099a8056b713f7847454ba07a675e56c26219f7cc3b81a5d95344d65681c1d76ee479a37523d9f94f673736aac14a3f274af74429758ef393ddf91a601e52d9c21184888576db57ac397cf070251c7ae7d02863b9a12f395ee986a819c0cfc7413427a90261bc0b6a7c4626e14992329a772c82f689339cfbbe10413036876d177ff34c2a8ff957340882522c223f5767da67c560ebfb310c39a35f91594e22ec9f98c7fbfbfe17bbe5bf43d428e1c91e7dc7648251d7e67b523854a13ebbc00991a3bb625f0933ff685a2079e604c74bd50e60ee6b447cffddc18070f3b7211ff2ae4196ac14c4d8d743f65d23f1cb530373a2f4b409c66cd9f7b7472b399132ecbf405c78fff7463b0ae7924f25625e351d1d6b9a76502039ffe3c1bd482efdcbd5dfc987925fae7ec0259eaea8022c86300b0783733f7d855c65883e6edef0893b84e05584826aec742798e5bc0b8ba0426da355f1fdbfb092b473085b0784bdc2fc86902000d25667a26d0c1ce4d1bdfa45d99304c5c5c3fbfe2c8ed46fa32db95c0ea630da12448871b354497dd651d46542f7430fb17eddcd92ee5c5fdfe3e8d026f926356204e0709e17cf7d83887a3ddd2dd80e8e21141810eb98c89de7f7aeddc1606e1a402840e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"44ee9ea363dee8693126c367112d5da0ebba839365c0e5f12488e15a808d9045","proof":"988a92d53778de2c16e579afcb0ca9ee9b11137c0c7d8b0f5273b322914cc3310828f64943569bec98dacd8dabdb8870b4661e25511a7c9440cb52af65b2101a8e81b0d9764ebe91506777ae9a4cf48f381c3e575cb46da1add38aaf376cc95754309f4c3f66a5807037bf542912db83dfde6e6ecfb6b180898c22e9a14a3b0282c3fd7a7e3769d4b1df8ea97bcaece8764fc0ab1ef4a4f123d316e27d167e007fb08558d54a94bbd889eaa7e1fab03503c57eac7965ccf34636a9a11caee3055b0da91bd783cc91f91bce663df42df4268f952b6643192a094058fef555d70cb27c363a7e6c80096c8214c62545457e97ac89bdd4b491d9523aa8cc9afd89291822a9b6e2a5f4f9787b094fc0674c2c86ae80bbd5e6db4068d63350e1c3db076832f747c3b807d1ed0bd418b1cdf2e1aac97b803685971af8d04aabe988b341d8e0534f8c06cee6067a7645dd0e1b8461d299fec0c0303d0b738922fb4d6e611a5ed3d082455c42e2cacc13fc59e06790aae1face8a32665264d8ab747cfa5f6a8fe1283d81ddff411a7ceeea9313ceae1ead0c320caaa46a89377da7eef634ea66dcce346cf05df206399d34e87aeb89cea7fbfec48e28694c09597adb2546d0bbc6eda1a20b5d91316b1151773e5e75f8bcede3df6da7141d074eac06243fd8f516732fdbdaacdc09e92868a20eca9db9a048e0a7e642357e94730f6fcf7e0617142bedee427e8172d64bd7a7ebd648e5d8750f7f60d6c5f60bc2ea433f53ecaa4b9f8652143b3dabbbc61b1b546b69944df0a2887ef9f74318c087d5506ab600b4ec2b9cf88d3c5ba531810c1cdd82cefb8bbece65a5ef687c2dbd05d1497c0e089a18f2601d086d87dccd8bc16a6fdc0193aab9696e0d0312347258f103f0bc1b8e7927adbbff00f7b666b61ae933230522f1f30b23538274c28aeecf04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"306d8054a4eb58792f2c0631a08ee3a1df6426969996331d003dd0683f051424","proof":"6489cbc827ba686f6a156368b33a56a7b9c9eb764a2559a41ca058c56935e0504a2b7af8fc387613603e8ee07fbb29ce25ac4d51cc0ef72b90c009bf78c38f752e241e5aad87e31601d221a309f49f3914026303d60ae90e69d5f0a360dfe3259091c93541ad7947ec903d5284827b132ce3dcbbb65fb7ea06858540cea90a141dc12dcbbe95f774c41f7ad368fcbc897d434e024d454f6dd8d33a06450f64051a22c62428f3774e938e655762ceee171d55f30e7edcf2f5ab9ad0091a4f7c0786c7361f3abd6c3df4f77cb8378aaabafb0d347297ebd43a3dee3d290fe86800f021e821801d06fea85b32b2918acda598b806c629f02c9e9e0ad250e298d025d49665803948aac9b3a494909e47e1075001ba27d6f7a7c2f70f3084cea94d3deac730f0c981e3072fda9606d1717ec65160e8d4320f0c3d394a6031d6140e64fce58e25ee8e56361e0f9c38b8e8d19e52bd55891aec9dac8fe5c6020b61af2cc6d927328f90b4e984138be77d89c5b278ea46deb6a845edb9661cd342f83921ec41fe5bac7f2e1efeeeeb8f707b59b96db0bd36a0f691903217331982473301b0a33749eb4d402eeace9c464fcaacf30e341179c7d7cac594034e72d0f5cc4d381b38d3ef70eb1d2e51f3b1783429222333882c10c95c13cfe15a750e61743292e5e884ff625debd9dcf291d9788ea033057f73b73c4a9b7cf6d2d04d6f154a5429b7c2ed3a714d29700c01f96bfeb98f164eaf428de518c94d92c9ec14511c520060cb3bbbe58818ff20a617696c46cf1347626bbaf05f22f1a5142a264b48821f2ce05b9126a257754922d681f459c7855bca26872c2490ddca1220866f7dd68c7d9ac3dd685f35809aefde19d2bf93ce89af814b932312faa69d6b41ad0cc76b737d6e8253537234c2984b343a4270a22d29cdbab7ed22129838786a270c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3e78a628c895daef813f05034693a49225c3156b3cee426231ed470c838a8240","proof":"7053a2b65a05b607f6d4c0809f3abf5c9a0d3349b0eea89ae80b54a05097787e622f3a9ae0c75701453e94634eb8577ee7f43d2092360d0fc36be58393c26353242c9f7b12094a23031441a4fc69c49c3ab69dba4af65df99232756f382b7322c2d97cff9d6fca56ee84010e2462a3326f58cae28cf3dbb745ff0465ce802e13bef77e0c010664a6f9862cfa85fc6a59e1e913ded6cee6ea2cb516d723834b0a267b6cfca10d2f92416c593f5ec8d075e77f771fbd5a724f5e2511e116921103e379c7987badfe380a1921d480432fab5186f03cef0967d82ad56065fbd32b0f0489116f515d9f48a0ddf8886ab47c67f8c5e6d241f98124355afef0c927281d7c87c2bdc96be04991fccf34af6205507cb9b3210aecd93cb4ad83d3a871c35ce284627f51a4cb0cc059dee05f5b5a2a62b5285981b76eeadc0ab36a600422519cbac79f9f3cb07fa9e7aadcfb350634370125a0944c2e74b969930da69e71049a97f691e788b4350f7368a810890187ebf5ff6e5d5c1f852bf0464e54cd3f10cacf1c40394b0e5fc35d4980a8c4105c86ffa2e93d993315ba6628010fad482e727de7906bbc28af265ef08bdfd47a2054bacd11be4cccd7634fd735ba405e0306f4fdb0aebe6a6e7af06231fe9f1e77e529a2d12798569e8aa0ed699777e040344f238d4cb62d8b0b05d5893ac2254e89793789939a30166eb2004380006b0bd6c921fddb717b4eacaceb11194afa90edef140f3eaafe44357bc387617a1a3cae6e427f88c6735e5cc6b22ebd4aa9d1487175acefde9f947dba7c63f61f7d7876554c896f197c0db7c3fe6834464c1b05558a1d56515f9d21c60e5d0239ad7af16264aaa5fd0f5d5a7b7ac29f755d41088eeb5398c109bb9677e99695596f0203fd481350a4ff4992178966b73ab39c4648d3e539449b0dbacf265be65c5e08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0c4df2fd9e046312009485110dff847e673b02305181928cb1c3302f205a1647","proof":"2ad1b16668a64ba7bdfca91535a1e456171e7ab355974e06957a87ada6981e661ea7918c9b11898ac0eb25a24a7daa1ae13db97f7d3cf4d89aef8024919b2b650c17322d4956c8a818a59deb2732b5334759d65e450f11ceb6d56c94013e66539ca4207f4d3f852aeb7840043cbc4dbd5a489d1db00139a295e3fcebaebf3622f2dd03521fd8562a74fe64350fe63026afa05e450dd1c126184554438c725e037984936f66f34f64d2b5eaf7d6a83b9ac40779dd0f7eecf8b56185dcb4b40503b80e1704c54f2b9f5dc661aeb18c8fd356d6497cc90bbf19ac237f1c873aae01ac8408516ea877c9ae797c08ee3d4cc9acdfb53a8cffb2aecdfa0ac5ef04642aecec87eb02d34d261caa201adf1a0f060923603945aac71c88061c34892cde330008f37dd1ab25adda55228ed926abb9c02157f4040977c3554e4d8769f6632f7e620f89fa4fea7521acb72a2c71bfe4c30eebdcd4480a436f1f322f7b9ef84a2e11b5f36eb81300b352d6401a6039fc41d4fb44e4e7aacb4728a91196ddd66732fb4f37e700c41169eaa9f4b9681e28a86db0fbdeee340854bc8ca5a469e16f2a1c403d2b05a8b0efe76e438229fb70e267c5a0683bf5468e3ff834d71ffb5cc67bfb84ffc666484f2a57e0db5ca551419ff0974841b51a6b88fbc88f462c10663d2737c1de7f34516b1564a744212a3627fbf5b9a35b8f8b55a4ecc0bdbe24e40e7af3f7b69c2fa95729bd1319d57f4fa72cd0bb40b395063d0e64da2c7871e0cd1ff4a4e97e1917453510097af40fc8b9d5726dd608fe5a849a200dcdf94b124213543cbc2070a62a72fc984b04afe4e29c688a660e3532c945878413101b5e811e41ecf07722edc8ef443300d2f8503f8c073844fbda18fd45df5f0d3a09ac614249573fd7572f514a7924e47ada5beabeb9af287642a4a7cf223ebd6b0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1e651e61abbb986d250a5dfb7b6a750827f4c9a74a41cd4ac3bb45436a340a64","proof":"12cc9ad932a47867ce45b81f4ace28fbf66640433365fee66ad9a0e0cb213610be0867389515af1957ecdd97de0b6e8735ea43face96d8dd41dd00a79997e62e1c401b25707d20be71a07473cfe2da84c3f7d025db4aba364f6c75a0ce0b2a65ac7de7c6a121640c04ba9195a0fdea2f3f9e98ef84ad68a96cb9aa14e4731253f99acaf4f123a78c5bda75f625918752becb46981ea1cc4e5665a2ead795cb0d6adc4413d7f0992fa2d212f843de869b2a19834dfce0ed21a7ec8788ea839d08d9aedc0253a95ea8a95ef9ff600b5d34bf3002eacb68f8da622535fffca957083e96a4ac8701bcbfcc224903e67e193be94019fa619576cfaabe2cb6492ea11e882c0489def50d3510d93541a8fd1f0f1f2955758b92d4c99315006aaded7e69105ffa6d31d30124120cac52e8f6d7fcd66fb02deba7ebad7c9fa2717fcebd0814b27bc0cf7652fe4c8fd61f8433111801496d1a79bf3fd9429be3fe1775dd1e46930b3f64b2867b89186abde645315bd9901465d55c4916ed08129fd61192733c06435c0e24083946f5d5bf8002bcaddb42e536ec709a1674499b9cf0b7420f940ba63e7f97edf24ad342a2edd48012dc5ce876364c9bb208b231b4aed71e5ee249bc4f421052273d101bd33971865749b4acf5c5e247de5930c56c0b1e0d0e9e08f104d662eaef4635b242fc9a93463215b7663878bdd170ab7df208d30b562ab90041a42e71315f1ca864f3b86f53cc89d7951a11c33317850d0e26765c48a00ee2309e997f96b15d3904bb5eeffa6f4b00cc20f1273e00cc20cf3d09411b84bf1a436c5125c89cd616116a8ad16e95bc6147eff910356b91b04793c90c79d6a53effc0b281dcfd754a18e5bc8a74a0ab453e3106512ad7d5558bc4e10e012e8ac02265fb56f222c0e96c8025e40f0e21bdc39d738446cce247fb434b4309"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c23dfb26d469d15e704e009308a8458ebc0da59a7874172c362f5101df0a5d40","proof":"5a1fcd3f113d70d88acfbfe6c5de3f0d2079fa96b6002fe28455830d34bea66fb2351f1820dc203c3c2edfb71e0e21751d5df7f3046b65af43edbeb440ff0611ba3e69a222185beb04450b0bfc7f9c0cafdc3ae471e5292c7c886db570a8981098bac763931fe4d10bd8ec6d0a097cb3d5f140cdf7e67226445461196d5c892b6a14cf1a8c57445a9736109bf7d7d36f3618694bd211c0a0603fc71e50ca530c4b45069132f2688b7d6e41dd93b3757907849d634c61125bd23ba1edc48c10044df3a3355992a08d5142e2f6436e9aa37e06954a2bdce0a0fe1cdadfb5ad960fd805cb17112f678ac19173d91740c9f87a232387064504806c419f3940fd9e05d443fc412458082524eba6133118329224b522af6a2eb7275f26d7781a1e772cb25bc6746966669709839a820b75944e85f52dff5dd8856394a58ac361f71f4ed8c14dac9fdfd91adf792c6c417d917ca1d3b25fcb76bd934efedce7e2bcb804d0cf519308e067f2d3ab4ab7bd9e390de9fbb6498b560b85f5e18e05a0fe5f13ca028fcdadb1dd3a713b08a4e80125ee38e4c0eb919c59a47c84f542a575c80f3a9a50ce394904a0ccdf7954c787848621944efd57595720e5666c953a6ab428c8f330dccf7bd296e2a36c62b274b2cae60f98603a01757205b378ab3e20a731a60b3f70b43c216711126715e56f43596d8e96d84d67b26585c6af0e3795697b5c9037ce195d12da5ce54387c905c9406c7597e3f3fa9490dde802befc821e385c62cba9ad899182d69a830f99bbb71e6f8802579950b3cb56d38d9b2b951764f8cf60aa2a53f80a3cef7fa16abc1728483785b587b91651e7395213b8708c059b0fc4cc715671391dfae24ff483c773b0a94b151c3455022cfded152f74f303cb52e630fe3d31ead1b12d8b4c02229f447516940861f86e851a84a3b4e5b10a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"58dd2db1e735db17a983d3ca9afdd700e4b516d78c8ab3411fb1ea7d6f62e237","proof":"a05e8e66f4e18ecf9df1e4741e631cd0edd9b9d33bf0cc478b22fda42e7f8e7d9e6d1095fc67433bd1033dd59647bdbba1a55f0db56a3c25c6e91766e0388a00a011140d8eb79b2b74f714f877a25c82b506270407bd8bff849524b32e45496c526c607d2cac5fa023d1417abe174b3d42fdea9194beaa2c43acb860bf33125afdd7020d9e948dfe88d036840acdd331013c4dd49342979447d20d010a1c890f30abb96ba8a2de08a3f313722e248089df654f34979d024bf828acb85458310b90f2ae368ea5c6b0884adab5e4d32cd5904dc3e4dc5ff5c56b0f7576f9939d001c4dd3253aefcb18a702027523839f7cb1d3df0d5ef1a7e6b57f8bda9ce8321ee057e4be047d05f56b548a67ee27a4e1520e9a9630866bab97e95a86ca1dea16fe46e1fa3b274a2d7571e8f52ec412359ffdf8f071d1cea6d3c6cc15bd06585b804980a2bbb2737cf9258bae2393776a53a39282a95c1b57f9d0e0a30f61f747902a8314d0b32cc63f6a6006206f7d158e15ac10cfc09a609c3ec08a05df147a6c5c16e2271aa125ebc8405ad97e41396b954bcd042da29a108f7bfdb392672bd2f193a081e562b749da8cfddb6cdcb5ce7a748096970bd5f105b32702bc884ec0c6327fb0ccba8e76a9d519fbb7b1ec0694ef3ef39238be2a325b0379273e2f8a731c433d5d2b5c3154935bdabab5a5bea74db34c8dfa008d4d39de5250533a444a1657736351014ad035e9dbc8c12cbdcf5dce355b0fa89185e29bd7c4d45566802a1597b4b070d3be51c62b2ea71253347c25de3a941f84da6eb982ecd77b525ee537e00b3b05333ccc81105e1c0ce659ffb01da8fade03f33e535bcf3f6b29b195803141a1c0170356f780703803275c00bbaa670d34dea042e7250dac0fe13bfc793527e46c90754f18c55324706dd827b44871e6f689bc85604a18d20d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5014de537ea6e2ba9d9b5816f6fa9e69916642d366fb9ab04ddacfd3b284e319","proof":"7ae053be55774da921f7c0bd90fc25034460a0beb274506d9c31dc0f8b4e891e049e7534e95925d16334e84e951c8eb9a7417323952f745727ea7a6a7c37bc35185b17f3e249524c84fcc6fdfbca22adf3b21d0a11baf05c51878adffaa60a26a0ea2426ee7b2c8b26960986f0478fc43ca284ed1f4e7cd8bc2fc0ddc9525a05ada8c0ea595021a4a4c617302f412e82e262a55743db8385f266fc1910a7c1049887f80503bc9a7f244b2b319e16e787927616c2f7fb99e0b2c167d9acdce60a78429783424e8ef56f716c5a7a9aa2b5058b003c18996eafcf22fa19f4817501382e3f0c09f25c6695df620d1b81ca85db9b12ee82fa37e49877b4de5e885c08c661df504d37b0d9d7c87975bf0abe9f646791c3fff3e25e0ba348bb072efb6a2ab1e5e08574475999f5103f618fa090f80622f576b50db5fe0ad7d59fd656738ca688cf7c7c064983f46f984f6f291a08796c5f01af6dbcbea35147cd78f50f0805d4c266203c0d4552da06a5cdb8da083c5a61af195cb8dbb1758dc612ee7d1845f103c7e713095d8d45fb6f655cd6dc78fc26e55e96ef2a7d74ad632e1b587820a6e66de60df6cf79a85546085dc8fa13589bc63966ce492ede79c37f1f6cae5eafb786606929dbc21315799a455b06c10d0955e5ab0368d121765ef41b54803f97beff04b1d6ca83386ed1f60c084de472971dd1985dc04b1012c0acdc61c2e71006b1a9a2fa587e9f563d6b6873898ba4e31340cafb510931a5466e4872f6f10758cf5fa8c084fde94a8fcf2090e9c0a311d1e39656d563acd4fc646316764412b882d2e5e5ec158d8ea315c65cce06d107ded627b63b5216489b204858771981818a22268581ab5db2db56b2a84ccbd7952b6b27b5d922d9cf5c175f0245d2389804ac85bbebf67c8a981cfcec4a021f5dc7649caf6c3db366e9a2f701"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cea39e5720e64f38bee969364513baaa41439b448a7ffbf3e5129d5fa4239d22","proof":"56c3569cf5939a2e6d7c7a1494a09d463c79abc92a841dc42efb2609f425c41f44cdd01f6677fe736975951912009197111b2e7a92e9a61d8fb73bbbf3b8364836e2ba2a1ba8e829dd4c053b13e470a85cfba0925c820f8078829f710a438202d80a8e3ddc874ccd6734ac01a19d834a5d3435ecc13722ce0f741a6a7be954379f7d4faf368e6fec35f683bf45a6c4d276be7599a0662829fb4772897f69ab03afde853b5e3f1851d2a79a71b0239c6fbbfc345979d4a54dd98263075585750a892cb47ded40f6aa794ffdd7b7be17d4549b629ae7cd1abe0aa25ce707eb0f0ab467a50e703a20fc6aff54a7a16a7cde0f981e9b139b48001c4f47213a0ca215ce4e172f865eeafd362be4e92cfc30bdb6680341a7247f9b72366c70524097776004e43c0785a2ed1185a8cb07f1ac945d537e8c877eb74819dc0aebd3407603a8632d0eb398d65689e3e0bfe27070cbe1d5ccbd74889b9110a159c01100991bf64f5f75e6d6290a9160d190b0e54afd3334cb9dc03e28473352468ed7ef114c589c3e6b7e16cf06df5cf6fa4c2a72436eef360e19d8a35deb613822f162ec5d58c901904306b97a59c00d89d79dbfc0d3194b0b46463ab910ed44e9a44c4a52629e7c100492c6db506936902e435cbfafa157a5c4d8b01a3653c7f82e6d350542d814070cf0f8d4473c7c184b7ed9ff030f77ddf0ad8c7c32ddf91068e7ea06fef346c5cafa3bb40afc15d6b88a507546662086505aa0e6e53d534593fa4470f8c7d70485d965d2ce69c4a03833d92486e11fadc1f60f1bd262553958437f441a9a0a289f92761ea8dd99f66cd88a36c5fd45090993b4ba82069c0f86796338512fb42c76ea162ba5b15f2dd5a5e8170b46808cbde9c9ef5d54bef26a52e2054eb2dedd5ef3d34db57a9bfbdb3c5fe7396a087af9d0d62e21c5cce2581a060c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f6569b48bbcd7d40c969af25d65a7bfbf7ec880a15f3d0b29cc2b346f7aaea71","proof":"ba18277f3e570a84c32a92375e5b61103eb791336f7a3bf5b8d4e8aee7c4370d7436650d661252d3857ebb7932d124e36ed8bb2324805fb01262d5a4282ad627e0101b66c001884646cfb0d3b4b544d77bd739ccb489c42118968ec6799db225961967207cc0ccb009b41c64d90ae2bc52237c33c030adf194259f6be994cc5ab10035e2c3a93e2c6dfd86ca8d4a70c707df21702ecdf0a0b74c10a6d4e0c50ec59e6c88e2ba7aa7b9d985a4982b6ec26edb7430a0357454ad1d194667efab08c3a4524cddac34195359c6384ff520284c8187bae7e737380e2725df016b9501267048809e92658af5079735036504f76f07a3ab871164c7c966f6ebf92ac95098119677e847d21f1c3042c05d9d442d4a2b88ed00d576b2403a9556761c5103502d47cd4f6b50df847664a01ab5328d4f58130a3a41914c9c097cadb359c449ccd4413866faad628959980663187cb72ae7df14f9147876f08b0445d675c71fb88dc057f205fc55f11d4a6505cd037e5a096a40852e82d5bdd06e90a842ac6a5cb5dac374ec5d73e223b4dfcd25a162587f8885b4d863fff7030f95408c6f746eb6b5da2c06592a298b93efbf8d488bfa3a33f8ac18afa1d075470e0a9a977d708102ad42870cfa96d071b68897d5bc30bf86cf53e552aac904a0388cd9c2040490d44d78f2e4df0ab978d74fe08d703f1b7353c6de94b31ddab3a65ce1874c569a93054bccdee84044315fdbde63c7bb8a5a91b8414e48d630e730c2fa843cb4860577996fe00a16201c9d0645e4ddb70f8bdd70fe481c979e8f3b6824630fe20849bddbf35475d1e1c434dda40e3e6016aa5d85488deda1e61b83a81c63550a3c0d8f7488a8380296a0565c7a98f03944092f70bab187a442136aa4e28e02f2f84e6e59a31490832ecc4b7fbb2f83b16711a04323b01f36cf7b613af1fe0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"762a8c09520554dc063476f4c9b5f05f916273406ac5249b081e2c7313155539","proof":"5cfa06d268695a3a317fe0246412f91f23d2d8a2c76df109747fc5223e82762a2e240aa8a0ee8758d1baaa10b847ae93f4d997c92a7eb6d391fd8543d002f90364e8ed1422c02d8a6d8882fb31dfb959b88044a1c2c4cd8fdaf3d385cedd5123c8b4fde4dec70c771f9130d8c735cc806ad3c3ea71d58d9d22911541e39ce161c4683f75f9cd7267e3f4645f055bc6786f8caac21ce6bf3c715266627ed1cf03b1372a114e704852c1e813258ae47b20636ebb30cd8b3a81f88153cda0d1860ae97328e03a6925b40c9ed97d05a4e29338f9696d47e9f85bd7aca83fc2eb200520aa3a33d91ced595e9c37b8f97d1853c64ccc6d0e9c0a6fd8c12f00d159ef410a1a7c427bbf23f2e17b8dc32297c262884d23f6535be8bb3ba6b924e31ef32872dc584cbf3770269ed6ec10e5950e4b727d3d34838dcc6bb45f3c1fd9bd8d10f2f17cd95d6e137c78240642568be399da7aa72547d365a0f9d7079c1048252fce07ed6e2577839506de64bbd50397fed05624d0baf3cf7d6d1cebca84bb7e141cd42cd4e0b46705343e0a72a8c67043fe56fc49dc7d2680ce9d7c75d849d715d66daa341e0a21a04e56cea0cbe8931eb1bdf1853dd9e6e786c8ced3c91cbe3fac7bd57c6cb07b6626e18392d27b692eaf6056cf1cef970fd346e75f1bbfcd07b4240cfbbc377fb41ec7063056177cbdeaa84cf38cb61d8d9850f58ee91783709a85af2abc6a0ec5b3f9823efd0870fd74de763d7c1554cfccf2174f2e675c3d6c648fef696e570049cde5916326db0c927dbc9e1bed787f935a81d464fd7276a059c94cf597ecbd5e525b120e24dab352186e33900e87778854718cdfa03d392549213f9315f926bbddfb97878a242abc45dd6b26935c2fae2fab9fcf0dc802f7d02695b7eedfb77b5077639717467796334ce39290a83ff562a3934247fa0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fcbe5cd3e48f6048bfd563a5c8ca1064f411a26a84a51911ee7f58d1f304693e","proof":"1626fc971413aa8d5ee67da1d4326d47f34e8e488a5da043fd4ead414fe53219c23d9fe664ec1140923be1592b87a5016b92e21c25d9f0fb2574a7b5509a7f7bdc20443bc450c4f2b795e5ce34cdc711f2bc680d15ceab93ff893fd56988b765884a1d9876d45c013391f626e2647b7c66062980e5e041a1c355c8047eb13c2d8da887319558cd7c551fa51b2c3dc82a2455dd0e5e69b6cd78bec318f8339a066a1f34b88ef1431933734fd43a0607f901ac6895fcf7fcfbec7a083678319d06cd9ebfa92f5e1638b8a6b8ec577b2340a05e925270f0383686249d0621e44e08eae4f076fa5d7cbe2c7163f7ba671ac1e238e6d5686210b1894cf5a63fd3ad0c96de25c7ad1eab373b008b0af83ad2dc8361297ab67ff202030e372a396165202ad54633ba9abcb6085c1a523d6c14790a7dda1d090536c5ebb4b7c584d790412c64ef15d37ebe67e706d13446fa3973f1235b9eb2a30458e0d3d5b6d6877c682c508ff0974c03fcc32a46265fd9615f44ae4c3b264fcc4c40595596c0716b2e5830e75b128ac1616ce3d34b444c4bdc254ba866cf33eec74f4fc9298fe7f2560655ff9b124368f4c0292a3c7999950e7ff9215fdb7995f4e70f5390f00a5f21627ead9e2ed3c47c663ca6499045aa84c8ae061c3073f235715290437ac41614be58c8997b2daf9ecdf814cedbd8bf7b9ee63ba998fdc47a237f6d777b3e125a8018788131e95adecb485e6547d1e867b30de0dd64cf8ecd30978e15ee269c459e1935283a784df0ddbc61195130e6c2bacdb8693c4e60d806105477daa301579e160d3fcd7c81e4caebdae33b6109685192f5cb718da592dc17d1d1377d8e46740f290d998563fbfb99fc684f9438dc503befae30736c2387677d8698d8d60ab5c43f7dc5fc6509bb3167d0d109f0f4e980d93f66c0c9099a8fb219160d9a03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d63be9019faa4b082c629618e6a1ca6d8eaada05e23e684613872cccb5514006","proof":"c4ddef64c15d9b8bb4a4089a87be6dfe221a86a904c6ef59e76bd09bd3fbf671feff74f9d6a13b7900d72164ea63924e968f62131f8c88d9d055fa913f05031f269f382bc8f98fc34867ad40fba0681223910fc27f5017eab89114d690001e7194dc3fd151d8c50cea977f01a95baa1704440646fb1658bceabcf2b30515a015d380d8e53af55960d7a84be50c8ae543f83b7bfc8ca9d5a54ea0ea3f042d4e0c4d341b3f778db28afcf4bb8abd1628359b2f7c1d0056d38757f7561e08153b054395666f5202b85871ef02890ca3dc894c15b68636035087ab47c75d5c692c0dc0302edc360945ce4065e95a3f2d3ac72a155c4452c17be4c05982057c642f57fa964267853222fe000a0c73919484a11f95cad1c44fe130383204402d690a151899c4c4c1fcbd36166bf4cdc48faba81f2dcfe59423858bb17597f3b657ab22fec0fee4fc8a262546731525e1854a49aad7df4a5c3c17c103d769d73af605195c4ad7c5e829d9de95af2c2f5f553ce684621bc48120b76b7847b27c10ddf863f8eb6c7f9e8e0b5fa5d6e462056ef3dcb92f586e27273f3267229c056bde441318bce09763368f42aa9a91f58c475f2399509d23bc7d21aef033c8931f072c1750d6b19526c6eb6127465863dbb233bb67c6d76895e8ead614925e3a642f1a4b0443c5fbef10e6c77f5ad00aea10fa1841440b7b89766c86487e2178982b771a6a9161086644f58161bcda9bdae68471cd6b55fb4d1273583489ed26169e2c2ede26e15db83d62f25521853a1a3f746e40418647b60d2c41d700bd9234244356da4e4dd3baf993e0886fcc688d1c01a31a48fc54fcc677949c5062c91cd69e6632eb1d34d8b02a1342fe97f7ce6835ec2bbbc4b983b610d1faf2dbc63c11360906563f0c4f4c0742e87d8fa89aa8eb4757eb1837e74cda972035d89d455d0f06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4045150d22c81dac21082e19dcd3a3ea12dd110696b281b8dea83fabf6e77e0e","proof":"3e3d7d4bc433a71009f9f6b201f62d81a19b0526c675d83767abb05d355aaf5726e7062a01f5c379a1129ea8d89e3395e4cf4b28e087c147dd967dda74bf342812de1baa7325c2057231bfb45cf6dde02a2f0f40c4b92aa880a037ded9e29a5e72b428423b386a79222dbfeac644588c1abe7af02e9d2203c8dd47f8ac9b2b3cdfccaf89e39d038f623a273f06ff5bd936a511608dfcd35eb0e97d27556d5801af67f271c84d20212d5583685766c1eac5e7c703b197f761a7be567b6d84e30d154b0c5c091fc3aeca5a0af91dbec915705cf3534da1bec18556421ea0816904beac99ad6cfee0bbe020024a3895d34cea6b580c5c2924df9758b698093a0e445051c1bb5b6eeea3198776e7a9bf46d7c46d1aba596170e639a1b3b049901232440cb544e2c4996134269b43400d46611c0ec20cdc51c0fa1512a21ace01ec0b443c78355cd67826ebf6e62ae5e6c3e459c8cb91aa02463499ce6bc5a746eb7f989380ed68473142007508efc7604e2270bd68a538f98b52db7da6a820d1f97c12fa0bdf0701fd161c6464891790d9d2604a57cf560b40d09bc367b9acfd415f9c245d119a9cf9521c8a97e009effc918af7976ee85f9e097624786687b9570a9e7e5d4a92dfd38f6c7512d89e26120ed90c3486d532a4c2f28cb1f97337c51cf64e5defb6fa27a50fc285ec8d7d504aa397668a749ff699b91519fea0d03c016ab156afc202a0ed28d01827dd77748015c71b4f4ca7e4992a8e9eae25d00c1c3cb070aa3acaf77aefa5230a0fa295ec8f291b9e009d00f4d619bdbf1b1ff86fe890685980487b907e43b853de9a76f6b76962254210fa0eb02b578b57562e55b4cb697d7033694cc7d9d74c851ce5f165efdfee18897dbc0e17eeb73d63110ea210496189b9b939f932a07c024d7c64979d427c8b92023941d2079f3175bc03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3ea0ce96cfb866ec2c265d2ee1650b065d0b06e0bf45a2d5cc13bd61f4c98f37","proof":"ee1238ea1906fe05534a6f96f7b88d665263793b26b06b51f2932e6753aeb1166c3cbd9203fc2c3f5724467042303c959fb170cdb452d5adc5b9c731fac74230c879d186dba48bb3970baac69803a1284e987342fe9ccb08cff412bce54b4704b83aade2ff21d8f6491a110cc47de122f6b732f933aef2cbca9ef42888298c052fdaa255b4c046da67ff6995f1ebb902f0cbc5ee8839ccebc361ea46eecf8203f09b01654242d73bb39bb3f8ac3a1028e28da34f1169accf666b9e784c0cc304014d97173739d2a41e3f4041365312ab6f3e3655c9c7ef0f9e42f9492018c301f4dac5a0a280174ab54b9447b8451c427b0d098e55e4d1e359c9bf846296391f72988cc94b804ad9e804ab691a8b72bc6f5d57880e3c53e6497b55fa27cfaf3282c04166ca6fa1ca2d9bedf58e6c653b5d6cfe3d540f4a6000763fea96981143644331edd90646aa11414b4706107cfddc5326c392a2d9b35b139e242f3ee127544a329a2ec9b5cff451e2581071877ce32b21d385380601a59a7207c45d096adabd9341af6a2b0f7e424d687e671986dfb7898fb2a880dadbe3587c9dfcd91d9c3b2dde0f6b6c3b7eb2b3def4ab406c07fd5c238713de5e01a64ac43d43ce68b4e7b4757bc2c14a0b96e7a3cb93931e049b8cc1a4c621f44d017cdbd5ea21609c8bcca5a56bdcc85f2e3f94a89760bb40ffec7fc1f240ce8225570d2913f03938977e3c71d6d63234ea8ac0b3f47c9f66fa97ca0591d00abe043c5accac9c1ab4f6a39aba2489d97eb33307de33289c6a7b4fe34538d3264a364dbfd5130747865142c3564a1aaa1ed87a3ca6ad19e6029ae9816e0043d4c9ba89b029ab793f19ae1802fae4fb187ee6c34f13839ccb4943d2dc3e7ece80da0ed650f250ce0865dead2d23d02fb60475b3c7addbf3ca9d42329b48a4b3168bb8412f3b27470f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"84109b23119872f73e0d6b6aec6dc10e6983eeed47082c1d41292fcd78e9d52a","proof":"e6dd8544667190e2b8d14cf363f1fb58c2c84d429391a97f4159459dece473077ae1dccfda7bb4b5c98cb1eeb7a692a3a37e82092adeb21efe3721efe3ee2a592ec8a0c8baaef7ad1b744f3cd92e62463620b619478dcdbd6736c5ddc4738c2306d9fe90b94d84e83ff1f01e7dbcd6e1a4a63b9ac064d0f2acb2dd6a853162780a89bb9e3835681be8390d07deafd994c9d735d12af71100bfcd5e16f1840704c66f5ebf7a2e3338850e16e53c4900fa88a86732b694e841f4e88e7c1871d30fe3e9705337a56729617625f8a9c6036686f978bcab27edf1acc31ca79cd5890e540aba58f153127679af00c33953d0a95d530b4d3e6167f790f1d79eee0c623dfca6c90e592568c8e311814edd414829f22fe087e36d1ff8692353a6ea6ea14818a18795396d3cbd8f7d1c467a4e8e1155e1a775136c3545d490576efb475e1604122eacf412395cc64efc5b437e4e66abacdf8e10db11b4d884b8422f5ab7542a0bb111b0d141c211ab04c540bd6772f170eeed2a1489144ac4dab317ed056aba13d5e037d8fe841b5d860c4dd8ebd6a81cae1b16f92437173230f76c1dba3ea87e9d2310acd75e9ca3fc8baff1c905df027fd7f1b5baed5b70ff0cb96523674aa9c3d18e421eb82da352fc05c821fa1b5216f35e684bb07d5960fd685fdf09b4d1f9e0d0dad93b095eee49d200be32e25f7d66a23cc2cce05a59e838f0d614fa2f3d2e03b87add1e75cfbc42d0c99ea40e52d9834c75077e584b532fa00655f48406f171b521fee08d4ab60c9aae90a88abdbc2e45592d9181f4f7691c755d1403d027010a56d363aeb6769201e898189a56dd04476f310c97ab20351f4a53483b98a7fffc5ece399d9ec7ff517321d77a4f0bda9c8cb4e3b54ff1c3e700037a53b8eb8676f02ab4de5ccf6ce95365606b79628b1bbf4683e8931443e2d60f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4e2ed20e284f80ca479e782244afd344f063e676044d8da9199df0b2da8b7d3e","proof":"825fed9728eff6e9f199faacacc88e5d2aa55e9de88cface76a58b72f9e42641fe452b9cc3023cd8767aaee1fbb5fd33ba19a49a23c26cb5cc23f8de1bb1c75148ac0f7381c18288a4b29a171684bf295b895231190a1c9b8e0fcab6739959216011895f943d63949332123b24742e9fac0638ceb018e23d387c0dcbfe526a7cecbad9d66ba2aacda2996212ab6187abb18ff73f3b252be141b27538bb5d2005bde919db7d975d05e8884b1891a3c32175499c1641ef6a435822a4bd24d64a09049e2375412c45afda6b7bdb08aaf0bc58959a6c71586cf003bf5b9945c4290060803589ac76b8c17cd74f2ccf84d2261c753b2396a6205361eb4a163e9f1d2d520eb292bf347b7f9ee36a9370ed5f342ee712b4792d4dbba4cad057e6c7e3578c39602a8fc9245ff357812b559db65c8ff4361fa58828ebf6ebe33e20b3144d965933685bd207c6eef211acd1e6913a2a2bfa5116dcca52bafe889c8ef3634c844f10ef0914a6216c77e58d2a204de3ac3de73b8e1134c744291fb83c7eaa5dec330b2416435b9d72590857a71201a6ab688e97a0774db351d023ac9f47ae78364eb1e73d9650f7818521dba45494f8ca9d6015b7631faf941f52fa8313ec1486d19b1a57498c7a225fb3ab69f6b321f57d1de520c79256150d41ce5c834e0e9ee8089f0d1ecb6efccdb5ae5f838faa8aa43afc3b087ddf965dbc36f624e9249098b34d0f35f3b9415123473479c6b684ebb646309514ba31d290366e97595402486f47914846afdd01772c454fa1d1f66fea79c0023f66120000b7e843e8389007fab3df1e6b92b6dc552f2040335a2b9674adab28b282efca17203dc3a1497bdffd4658f7010a350b90640986d842219e1ed3c93ca00207395a07ecf82e0a5dab10ccb0d85f698e5784b05f79ef2d9767713544a163d058c43d5db241420d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"84b30c71decc94e3cba45d0b3999f02b0e6de824c6c07291bb556f9bcf88a313","proof":"3a61f1d5901dad7baa62df47c14eaad6eeb02ff92c6027cad1751f170a75643c90fb2dcef376c459398a291079978c5ea094f64d050cca55030a6fa77b6e2a583869e31a767ee5e310934ab0a349dda801c142e4590e10acafdd6420d1b977198a7148bee345bb2738eae4d557d1569dacab020a112e3990e131d2ba11f95e05dd16cb98e869bf8dc36ddaeb41b271ea34fd0e56f5c8226b6bf58b4aba398507ee2fd07c13654e894e8d09efa9d145b0751f40dfa8f39a6bd87a244da899850e98039e6c37171529fbac957727ff07c5a420da3ce24da6d37aadfba08ab8070842951e5c612cdc4aaef2c738eaef5485607229a95034728bc679e6d80fc7c9260a6d6602e82920823d1aee3c3ea1d30402ec7762ee660e0619efd39916f9b75e04ab6973e6ff6556f17898a51f8eb7891997fa14d450a8b91af28a937435210118e5a66ab7ed28640951983ee2337d201b44c97b789d1c54fab24187eadbc44620c7827dbcbf98ea7d93f43f42f15684b06d07cd7eddb2fecfaa3d503068722cb033a62bf3c1be3b27cdf2d20a6f474318814727f8267c996d930208e59bac21fec772e0f4d07fd54c7aa16466241cb84bf2069b9dd47e7a077d48055e1a434becdc62ea70e2f4ea456046d8a64d55b730b86f3bd55f57b56ce06ab78ef17078ee8f7353a436342610b3c340827152a4cba60a0d43d7106068fb51ac03ca0f6ce227a0a89ebf9591438656806cdb0570c8d1bd9fee7105fffe500c78499ec1231a66e2c7b20e78ce15dda6ba4b2e16742dfeee0b477aba987e05d7687d6d640a180a612029b212bea834800bbf3edf2cf7ab111bd119071c7b0c8436b050fd6cf8f5b95e77622c55057e1ba0481208d0c6e7a698a20a5b2ff8b49fe74a04830ed222607ddc25c5d0a8d30b19b3c7434a30f94baecba6d311297746f83f215c0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"24186f54857b0852fd56ad5587d21477bd9a1a1a5424840df32d882675069453","proof":"10de345a3f610588de3ed4c6f5cbc9e24973e0bb2ade966aa357424cdb588b6a5e57b70ad4d5e08a282733651e69b1f7336743a8ca9fbbed18f47d60fd8c36015083901ba4393e112b46d19353829ce1be73c45d1a5220354ab472a1ed7dff0f787b39ef2778f7014fa7cdab96af5ef535293d9945f8bd1256b6a58b09dbb87e86d1ed594738572669d36e5e67be9294305f0436f795722639f31339f9c62c0a70625de510a31317fad9307a42cce3d7ec34766fa7753586e64beef0d76519059ec76cb86e7825a67426fc8ae993847dab4e2655888b253fc589c241e8ffac09d4dcd87d3b06face913b836afafc2c8fe92904ceb4d8bfa6f88d3d3d4abcd46664b15e85d48ea6162f20521e19cb8a5e0e0eadb0ae2dc2197a455a6c6f9b77298aa96d095c4d05d84a66c4cc76d64beff8c37ef2fc5740341832138c4bd3c972ce5d36792ef7cd2f4dabd6d1ee00c308c84605a7ad499bbdfc9b431ddda2f073ee4c1c95a0f825364027397bcb879f69a0539a8dc50b415f2baf7e8909041317c47c0647c162bb7282667c3b4013bf0fd19b7cf27f1a5b32953a360fc743625b8c239f45ad3718bf2210928319e41f216755fb0e0a98acdd259b0b9c94f3557c92d442b9e1011905cc95f08776270d02b0053020b7753ed4dc74ea9af322962a9ebfd4af0631ca6f6b634ded6413c34cbc3d804e6b50674ebf4f51187712e62476a6b9e1653e8dc73ef8b03ddae6bd4fef2f2dae4b51156eb0155e8833739949e8266b013fe439384a50370fc2e4964dab527e34ab22387102b0c367f2a9c1161ce171c8dc2ac5198feb4c86292dfa1a44e6cbc6a3482f052a5c55b4d61e19000b6619ca672b4532075f73598bb4a7f4237a07aa362be48359bc0040ac3b970d102264a4e690c74c916ccaf65ccfd106ef720110049e713b4a03a999cd20060d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"20276a11cbd9197b24ed8107a4ced35111cd70aac77cd4462f3449ca1fe71217","proof":"68b67cdd3ad54ccf5dbb3daf80c205709991a0f9a4cc3489a541648c6287344d423e4d368ccf03f73d045c6e4feb1faddd4a0ba5f5a1d262e1a4a19629fe8e38d637dfb42fd941b3704ef826fdac791f585077c04f52bf6a4621538003b75f6d4e19f2c7c353eed0f9ef7e69434f2b1d5f66ff4ba6bb6aa631a5bdea6a135e1de26f30166a631f8909bd90069b60c538b365021cffbcc345a2573469e9009e0e3d9eb00e3ffe0d6d806f1eb7e613203ac8b69411be5ba8b21cfa816d7d98bc0e8ec2224a99321e6a9218940700628c088870096a3fb78bb77a93b11c2f3157022ae7af502a62b2101ae3422d05afa124bce85afdf28a824bd6230376423ff83466cf0f6f3a1def4da5521020dd8ccea105246ce186f2d92799cbfd91a80704363a1d434b7de1ccbfdc4ba21342356063daa60f88681cdd59c934f369af81812d6ebed68a1a4d696a83a5d9486487f6e666258daa8832b797f7d1ac56bc954e340417fb2260e934df269d5797bceb2b181cffde133ba07d0196997acf9d9f596c22dd07e52b1a9683f2ebbc0bd56200e450bf710fe12b35a651b8c88acb1cf6118a3f65a1ff56c84296950adcd7640f278b372fae69ff34d45f97e2f85f22a24a6cf67f11d363771b2c199150758928bf27860b9ace1c86c1659e08a9bee13f6c482a4a41101d360698a3789a8dbd91225c180249b8acad97489235d903e7c033ded45e72f2f12a0c298100b95ca9722201421d29ffd05918351156e76f0c4a536a94ab02b7d7d49dfcd669f868eb4ea2d8bd88de6966dc805eeb57bbec623c79f25c5ca4c6898229a6a27c7c71f090696731bae05124962b3f7508dd3a122c257664c5fa5d2df497732c33be256f28550160f7fda7944c65cda0de5f77ad4d09a6a70b9344a93eea23096c9b2a1b8098d0cf4c4febaf0f82b1005ea482b8f000"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f48b00b6e53e213fd39996816fc1590a8d2c0a477c7f7f938fe0014d0157436f","proof":"acb2c6679db74de2da9c12496fcb9b322989820a50a191efdccdb508ac94786088f1f72352dd28c80f113e4bcd384272f47a31e3916319fa017263bb5ff7b477346198d9fd89d42eba2e78c025d610ccb38c20224cf1c6d16b71c05f6fab27455ed631ddc6b76580229b0171ab712ebf387ffae59a64f46fdabf9871aa4e191526e2865b16632abba3de2bad690f6904fd8786bf25b74dd98952472ca9f6c3073650b2c0bfad4e8d965ceba28de709a8bcee7670f4c35bfa67cca3e8838a2d026d7ab5af3b8a5a02a64bd57f1c0b5b261055230cacc5a57c6413de5debcb410e100561a14548568fe21d9246875325b9daec3d057d9a13448e26d1282aa84234ced5df0683f1ac8d86b9e8098ed0f303268bcc681e35460f7a1ee4fcde703d38a49e498672f55dccbeae3e1d2b15157efdcd4793e272560d52233e729e05b80354588f20fe6d6a429c285e3bdd8e5803ac5c9b9b8052e96864ea56d6297a0519bc285dd9ef42167696f66f80146a10087d299015deea9ad7f72ae52434af631d6215d3c33629f91aa832e9d0faf7d2cc5420b0ac422392fbe589655f39cd6f2dfe68d2252c3577c09c7277e0e0c8e384c77a09875f83f389a9eae80e1e90107446f079fce32db21af887da89fb30ec9997893d8756af3bc272230306b33a774efc8ea37c641b39d9cd9756f67e019c40d15312928274d8dc2ab0b1194d05452ba2ed1e4f535c9740254103bc4f6801be1b791b82470ef4ee423adb08b9ee7b5cca30ef313623c6e50066f5b606a44a49ba4ec76e768450050e958977d5b38c2bead1e0735d4e6b5f368c5c402c9c24a824301968f2da7ad558d4eb6bfd492b4efbf9179fe1174d93c17a0e61fd5f6e0e79eb54e0f88ed3fd7291766d98e8a60bb5546bca556ee0e41ae79e63ec5ccab77286fed9c4d695e72a97674643ba6a00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"26c6f26e961a5372d63ad46a9ed14a5a620c2e37c3aa555de55ebaa29f27a561","proof":"3e17bd710a16a8bd3d78e36b6ee758dce816c6269ae0a40e9b3f53bf1a21771a382b8e693d38883ced2ac863e9d7d527b8d96707a943d1bbec57fb3a2d86967eda1c2cb5a64e96ac7b96b34618e074318ebad2e2bd16a576fb11b13a251be86e2cb97ad106a15e698c5e63e3b0e3753be986c570ddf64bf19aff5fe9e0b3a720b821d7f65b14544b236f96eee982b4d9573ad7b2187b44ebe17c9b9ed2a9990d7dd9fb0bd47702d709c72079afa6b980afa3439b55d24011ba2c380fcb9c68039c7b47759c2e9dc75c9464c7cb89a099c1dbe4b9afe920f898d398120c72cc04eec206da08ee909e86d290bfcb150b2a5033111df00e90becd60b468b37dd4445c4203534d9f24df6cc3b3f2c9ce40a5c91bddbe3b263f274561b784148de050b0e5e5779cf0b5bfd805dac3fb05fe8522364d50dcee66269bd6e71e6981661f808ae1309ca90ba5e5bccdb1941c7aa6e881dd03781d506d2799ac58b83bfc287e95dc5e4ea866619124a0336bdd1678bcb6228b18a1531dddee8d4439e50e209a70f7fecd9294cd4dfa5ad45ce53d0b125d3afaacc0dd239c66ff23674f045dc23c8f51541af6f023fcdb2091ed6b6a0d4214890217b37d080b434011e8fd7e6aa44bdc8dd4574bb55b0f862ebec81ca59ade4eb23075532784b9307896944e886eb4f4167637fc807aea743d1465d1e1626f11ae6cb6116f18ba27ee302811cc84fe79c420a5694dba3cc3a8d1a09079bed0c852f1187f4b876470719eb6594c6bcc5cf1e24896134a62a5c299e7cb62a8c2039a11f2ea26d64cb3c07320493eb74c18db87798191cec20737021cbe15541e22bd6fe719a33b91e28e198f32e0ab1ea11cba5b5ab66874d4c111d73e38c455a6ec711539cf60cd4e2d322b03d59f0fae6e708dd6be88931e9d8f5a1b898831bdc9f27065d8453e3d8db8a10f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"80177159b04fdfa4e1644156e405151207396100c8526cf7538fa780a674f354","proof":"f4d79071df64b59b7b2fd343764ea6d4f51522f0c7748766d364425d9ee34418244dabfc0d5ea3229d9d1baafc3a0df68dc4464fa0efe512b6ba1ca1e78b81365c668b1004ba74f64a102dfbb2d3194abd30da44058b8d6fe5e2ac7a45fe5275b41d5056d46c477c114754d91ea539a781495dbe73f826dc001404f263fcd55cc0f873c72b44db8494aa06f218a33e811a599611e8694487559a5d9c1457b40225a0f4e25d1c286f417a0df7e68a3ebfd88cce7a7bb9978df231b332dbeacf022d6a70d0ce6cf513e72aa443f334a47861e16bdeaeead2e8ba471404bf29be019c3c9a130b5e602cee7d06395708b2c7f6217008a7df6c72361bb3bcc53d2a6926ae0155bee245f4ea9ca8ef473dac896b53d21a1241545d651c0a8b14a9a0373401f5205e5ccf5a33c55649754696b31d2d872c045b30f2a3a7dfaf7f1ecb0bca6825c7f199077e6c35d96820514ee72188517319ee2d1a0255c5e4c446d4781e7346d76555f5cd0715e2e43690eeb33aa1b9d752a3ea21842ffb93223b2a0e0ea50f192d9a86d89b1bf4d4a94baf03fb32ab17137bc55d8dc7e2b665bf0b5d86da85e52cf534934a3c79092f0daa2553ea98fcf21e53464725f98e0b980a567ef38119e28f71b429f51a22815f15d9fe34b641f834008bd935fbe8c0fc6c5b7ac24b96710fa9f63ba9568e8b16b923a103ca4cb3ed99a08912eddbeee7d346d03c4cee29dd1288dd7289146c88e65e36ff29fbe91926797e7ab762c781f07386e057058d53ef5bf9a43b3509b11e9cead386ab5c4172159881f1c1c8438a11d61ab1f696756b3170635e3a472f2cbe2e7ad5b482d52ec69a1586c82e24441089517e58c32770269a692393f21c93d5ac53ab3c545e62fba1480b853909160d1bcbeabd5c8af7d66e271eb1de879bb33914e8d79ca6825df6e5196c6149f709"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"10874627871d8bc5c9bea12c7ecad20524f889a6c349c949d5dbac4fb71b4174","proof":"946c5be728cf10ffdc771057ded02fd31d5d99fe5011bf34c6120dfeb8dbcb12a25e5645940612518735c5d1c7083f2e2884c7b150399b9a7fc145f26ab6ac1618e5316a8e85c0d7b6ba0434195cbda7172a95a2db7803cc8953eeb93301dd152848697f233e942a428dffcb4f363b036f56378271957921010d626a1c6cb57a11feb85cb36d267b5d0a6155d0ce77deccd07bf500ff45d3f9a7ef1ff887030fc9999dd49a10828cd1b255526901eb7d0c02752c8675365877a529906684910a96c2bb609e674e231a910b5f8547dc767e8380637282f593bad33aaa7f6bcd0342c8ee2f65a35c1b9c58bc19937206aba265e8bc2e01ac085c09d2dc3339624f50894f907ecb3b5b59fb4db46fd9d0750ae137954ee19376aa369690f93a6a155c26fff698b21e9c8fe3086687d3bedb7f673669d2adde75f9e37385121c727bec28ce4330025e73d1ec06b8004bc4d9b070f6b6e0b4d82d55637702c7dfb45a129f79051a1fe29e888a754754c4d7555b09c70a018163f9404cb54372b38d193c98c3deed45a2057e70d21d31eeb8d92dcbc0f3508d08f5e6427bb68dca05651661da74adaa54e171a204751184d0d6c5265ad80c3afc9be984e569a398ce07e62d374c208a4825548aae6413fe8341b05cad553c87d61fe76749cefd197803000808de35cdba68419f9d10c5e22816e4929143a7f6f1273a0803766a692c6f04ed4a07333c8b34937910af6d61e6d9e0092106ad386a826a026c6306c4f25356d5e278aed3e11652e299b1cb778c352fc0f0b2b76d17397c86d84f23276148f6f854d59c7ac8228205cc2f511737c33dde1f97116fa058c4626ea005946a50193c7015c89ced925d0d2bf7142aa257a3adf230c448d1262c19d892572ca30e62d6cdb3f7be3a8d622128f3077852f69f592ad18bb0d8912a2b3e33cbb0f100"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"388365adbfe27c53831d1dda2c24e7eb788840dcc299164507d6dbe637f85441","proof":"1a41206646448f934149720956c908106d471d2ab1413c4b7ed064c055d02648b2601fa5cfe002c6f2c3938852ae763f2c20367e10ad730070d53333af7fd52fb23390e29b3c36312d1a149f88a621e4b27f8821ccbf52d18edc41590326535cbab76c4938b7f0eb6c185df907edfe0722868684bf079cbe98c3515392ef917d611566bfedb2adf8e1a3cad7db74e500aceca13c5050926e167392bea0825d07bf9c23171fc073478789622ff8d6ccc8237de35eef3fea40cec3244bb42e52097a0e638b001b72e497f50637fa5ea9931ed2c148721639d98a656f6146286b0064f07569e282cd68b97170cc1850f490f3c8c16ef77d3ec701ad33484ff8156180f6b9e49ec49c7daec3908d4ed6290738f86d22c180518deec330b575636773702fa0306a44ac9c1d7838c80c6a5eb4415e152143166609f91ed2a737d04b39dcf1e6d597a326b9ed4ea6e47c52c2e3f430e8dcaaca65938fbe59886aa70f3c789a5321c28ac49f2aa75fb72fedf85b98d950324e84712472efb0af3810911a7c31ca7613007e0e7f5bba2ef53200ca4280c3fd96010fb57fdf9bcd0354b875423d31810642badd0cf5d77bfc359b6e182e6acd2b22ad7a782c26da4aa9fc0bea996d536db51bff606143cc3ffbdfc6330cf38ab19d41c9c70c75dfc8a96d37d2043e487510bc4a753e0001790ece985bd5c859f4ea618d809a72dc37709d0e9a1660e52e705e5cfda1f74c2e5f778250995d9021c65ccc5f82c799dfd529670c7594973559af75e5462ce09ecdad0f21a7d2d3af4714a13071ea8e825b111602d6734d8471e82bb0c03c019c54300a51862f411b7354847ff45932da38b55e690c062d183e9f297df9e34e83b5b2cee308ecc444c7669b8378a22a47f849005c83e5c20f01fe197c35e9fb56a919fe708e5393e1aeb9e625aaf41acafb9d0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"741f542e20027bc19e4e78a60fd6e333cbc2a6efec1e9a74b65ad4e2b654470a","proof":"ba1ce6835880fe015d0f9d1d6c49dec4ebc2bd87ca0570fa029e739dba702276b43f9690478bf55a0b59b91adc0e06b3ca07f03f17bf5504424eb9c884ae96122871378d0168acf717a1ae6f3ad048144567ebb48316cf74d58e855d5b319c60de895d56480949e49010271f7d2ad22392043cbdfe99759a154ec96c589c5a33f8cdb6031f0a83e359e837b7e15fa9a0416cd17e73d0b3dca5f950fdd931e00726f6dce60e3824b60a971c9fce479f5adaa3fb29ae7cbfab462c4944622d300bf14cdd55a21a47645b6455e69edcbc8a340cb245dbc5a08d9b1c6a3426142b065071b5353beee5c2952b858efb00ef9d75e5497707919468b2a2047a750c6068747803847356be489ddc683675f324e39601edb4a986b13a3fbd8a4195c0f152dc45f790523283e3d56e993b203c5621ece453cdc670d3980d3eea5310aebb3b06c31cebc55c766d3d2f9b24b10c3660f8613993c21354be294bc8b66ec3cf76761beab882d02fac481b00ef64210234f9b631cc411aa06d12038adc6fa9363d5aa0a4fa92dbb8a6e88409eca7532ce2099ceea035bdc5c5aeaddcfd168f567e5aece8f1c1343a86a9cbaa610414a28655ba8ef59f91d191f851e602a9f59348facaa5c5c7843e09976b1fcb6416f6ebb89c7eaba4ac24deb791d8e3215cde47407be5ca38efabd1c249221935f4dab53befb991d38c0d1607a217f0e319fa2cc287795230c08b4ea11941d5060ab1b3def4e992f29e89542a3a4ec2392b9b5898e290d0fb3372bbbdb9c860e03c9983e91945cec0e15365ed9ac78ff78c10248a35b99a0966e3f1dec29ebcb889f4abe9d29a915b638bc8a99c2cafdc45d965322a5f558f7886b1d5afa5886060530705ebb303b8f88c90d6aa8cb47f57fd0201281e082b2d230207261fda0d4b10cbafdc81a2e606b3a312e9bbd6d9b17101"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3ef042cb1828ac8808b95e008a4b3c6340b1961e237a95b0d2d262f6011beb57","proof":"74dc878dbab4d402a4fd7ba2d5dd696e9b51fe32f6e9675117be46a39330e949b0a5f445b3adc7aefe54b459d47d7c56d5cfaad0e6145b914e81f2c1a99db015dcb7951a3f26e038dcb903e218b0fc33ba6bd674b33c64131b47e4dcc982f87542c4f34a88b1285bc697ae65f13d6057c9e44fa8be43ec2714c08e0fb18d360c08f44a27e30c24b0c75da7c27e1ae43cf253e1fc641171e72356c678d616080cbc9603d68f45a3db177c16ab33557b6077964541d4b739803737d7752d2e47048fc3222aad767396836d28480f6d0850a20fa758a90b4a4070a75c461674960ca2f66442276561c0c3aa94b31865afb8b3b5641601891b2a92632ff5c7135817b41ca5f94c257453a33a2658719d77f1fd52ddcda941b8c224306df6895efd05ec9ab9bd3d9c75e509b06aea2e7a016d70ec770d5a1ebe92253fba0bd5033620069c6d853f614a2119ee40a79e168d2179912906b49d9e74cc60f6b415566a7e845de179e58609ffb10c3820a6794f346dc9ad0a5defe0fff05ddc08577669015ee4db85d629e46c1441218ade39203854dd41ec931ce8231e79e0988b4f003002aa0cde46b405cc25fe345dc42c3bbb4c11ba31fee2a17b8d3777abf10ddc18cc3764ca01d455c3f21ac77e8ea84ccc6071378105ca5edb60d61c3ad4fdd841dc881b7f5b3dbab3b29c17c90034f94a77eda235835ce485956ea8f3d4a8852384faa5e01b0df61625ef79a85e1fc3e7bc9e38605933ac112c1c4efca0146869a0b3dc166424c38c066061e93a2a9b37730268415fa6f100c748ed67d2336d43fc1fd7087b0fc7ef714727e47f41355f3894ec53a68c012765e39ea5a67b5b75b43538a994abcf99ce2a70323b998b2c657474349f79884bb5c97e70494a280beb439716ae12766cc5cb6a340eb5f5ee00eb1a5e623eddd331b3100cd8fdef08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"600e0c1150b486ea45a06efe4a32414627d8f092b34f11163f198f1db7021541","proof":"fa889ec456457ffd46e215ec1b7ca7f881a8964858816b45b68fc13689c00178cc0f88daa16414959509aa01ad507a7f499572d694987f0a84b31d19bf894269066da5fd778b03234ee5b0ea5828431afab0d232bb09c11bb7b3f52329875f1cae5cce9caefd917374c7bc01c981656b070b9e6d0327802b1df56eb0ed0edb0df3a5c9bae26b5c07448d2c8d831ff55c965bc3231d6eaf9830ddd37311635907c2c1668fc65609cc31e59dfed03c455c93a00a2e5a9aabdb7acadc14f72bc305ee42e4b13e441278a7a86377e6dc05d88751d37bc301eba340aac9ae6cd363077cf3f40c33dca51bc09b51d83022e4c68eb6c2304642d23a96a6667bd237af6b763c2109d5af220058ae66b6a1471b32bc0e5e769f6cf3a54cdf83a2fede040c0eb1c42276ce99ee7dffd9c55023de9e3eb344441be2d02a17b8d3d527e9b52fceec94228461a381562c4066f7727a431c9be4e876f4e46625a86a7cc663cf078600c4ccd562bcefa355dbe44b4fe9926f26b5b536fb5df4ab617e718e5e4d7db477d54a1cc7c80519be5a18c1ec67611ad7ce5d605e37d346241b7604ccee7b1809cacb593d0b73cb4b67a797d00e9407e22d414bd3370368aaff941fbfea3e249b31ab522dd6ae0ccc75e336f23242c992b3efd51415d608996ce274ff6f3464f3ffecd380c48d908cf2ac127c33306ac613a7a62fb098a92f8424e8eed525f498fe2bb44a8aa2d458a1aca04d9846e7a9933a12339a382951502f1e8da90b82efcb63950dc1866edd90a99e9aae230d4dfdfede2782d238675903c5035d17267f6335f8dff426e731ccda290fbb433298c17376d8c1c71fb2cb6ac85b463eee8aafb70a90b7b43389a0e20e3ef62c1536f815d7529045ff79d42999265a03630d31d9bd7acc18b4a7ab4e618ba54c4aaa2b71e542d94d1095d6c3e04acd07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"be995d58343a263e04195e6ef362e2fc28127701f2c692ef94f11274eb91c909","proof":"cc3bb1c3d5ffb92c168c1b13962e9925d351b12a696249c9c91019207f19415f4432c1c95e3c991f9dd6154850491543854b8eef5648287b07e1b2f759fbe373b4a33fc1734c5be13522357c3c3b1177b8699e92b661adf7874a9d03aa4aff6516ba602007914aabab9e37b00f4a03a4b641c55e1130ded222b1e3045972c7681ca2ae96bcd06baec3e6451c5373bee3f539bfdeadd561fc0028b896b47c410c1abd7a1804fb1dde902ec35ac9bf4927848b98eb796b920463617f53d286ba0bee018cb6f2f733a0f5fd49a7ee8643bccf9d1a9d5816244fa41541610a35a80aae9497e01f9f29468faafbd46e9395e803af795788bd3051dc39bf036e5e5063a067f756a04e98fa75aa4ca9b159ed00a15cf09b531a66870dc9398daabcff4cfa164c253463f7e3e34777a56cf4021eabc0046ffcafe4f008aac7a95122ca00a233fd94f08e222c01dfef7451bbffb282ea06a0fd0e1ceee1c934de4351bd5648629099db1acf23f22bc0ccbc3bb0ecfba8828cc349863a4de4288a4b5a0f1f42cab3e99c179b99afbe7669f18d6dd3838126a75c4a465838cecd366cf37854a4f3006f8dd26d2f45b4c973d65704aaa7a40becd222ac7587a5ca3ab0eddf598801d2e89394669bf94144e36a0187335a71a441f74d30e984173568e8d4182d4c3a083fd0bec964a7c793200d304de5a2c6e1f8089bd8c1e0c10115b64e7000267616ac6b97922c37d63dde57ffba61aa183afab828ed6e4ee138a65ee26f31bc9a171f9976c17c4f6ea3ca902c7fb920b683e9725f67222bfad24939e6c2325c98e52767fde873aa95417d1652684e9155bdbb2c04f6b5cc1a55b77b45ee70c97b0595f0c3808cd9de52f1814b14a6e78765315cfc52afccf2cea155f7000db98a03babbe559b7cd25bad78343b789162fce1c71e79034c1ff6ab825a40304"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ea25ad6a2d2f65b22ce425834e256ed3a2e0aba9c52a0e055a9d710b548f332c","proof":"f24a27f7f8ba3f888237f8b75c84f8c11cbba895b43af00e7f9ae22e4174e35f60410d77b49b3d9dd66e0197f12e3d86f826e04e84a12febc191f649126cdd7ddecc77ebf35fbf0d1ddf94a9971f9e43af5b28a668f99d08b57d8b4000d4fa1c743b0a72acf13139b7d32a0cc1d97a5b002bdfd125118c3b95f40a8d7d56f1314541fabe063569b215971368d649294fc7fdc58438537b90e42699b7f250d10fd1338a324c9e951bb8d13daafcdb94979b192dabfe39fdeae8f235f5a8fd980abd299ae554570dde0d55f942b4eacf7fa6f7cdb993e138b15ec256bed65b6403ccae0846cc1c7949acc8c8327347aa82854ed42ba7e528c14f713548c7716b36383627237879423939f7dabdc03e77f78e0c0052ff4b5202d5ec14e6df2b371412aa9496a2ad2354264da41d245b037a8ec3411e7631fa91d327de2891fa18197c26d5c1cca35e277b0df198aea54aebd831d59c3fcd6b1e9914af89c2ab9c74564eedb35c26a7178ba09fa7127e72feccd5c9d054c593b0d63d407adb81f9013ca0b588ce7708b856edfd333403c596afa17e2ac84954e64e5c5f9bebf5e33cc676083b6d4c914e75017dc4101398fe8f1c590a287cf3444af459cb35f8da53bc75d2c652d9f9277767c6979005f8b0b700d9d99d0ecc9578deefd5f56abb67a6463e98cd2d5f75a7fdab0dea24ac01747829158548c74082b6caf1207fa77b529a4462bdd94e936e8ebe8e032ebf14e50dd6e9d401d22d4bbb1ebc809bb2345283faf021d77f32ea3404414728c422d56db13bae4fc99ed1fee6ca9602f24d2ab766d4a43ee9f126f1134a4b781386af6a89af750126e89fb090f2af6c81726222d24693a38e2b1a12ae75c710ec6fb784e9824719f103e25b7dcdfa3db2028af6a3f569526d93380b2d0e1addb5bd6ff29718c6cc73febe20d179dccb8104"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bc586b36bfd271beec4d928c28be31a5f41054fc00de9fc9e62c90376810ad54","proof":"78efb48b2c979812d8a195680a8dd0831231e8df75a79568d3229a7c836b8c647483e96918d0dbafc70b356b0700fe8dc02334f4c63e6a31b6d6c73d3bcf8a6ad846886e7991080f3d377fb4d44581b22cbd3e244f39014c36d7b421036b734f82f9583f3b105d669628904b29fa26c7786ac3b606df606eaada561c31ba8168dcc19a211cf0ca0110490b4752184ddebfd5d0b90c6dc86acf8fddd6a69e2604287b1e15ce188d46f2772937f15b0e3464e2cc6eb2717bc53768f246fb896506834a92b18dc7e5d87141f6f88da8eaccc5424da56a3c8a19f7f50cd9c353450cd2f05fe2dd46b78bd55f4c28a0aa06181f57b10478348a66e7f24a87024e71702cad6abd4f650b0af2b90078f64ec48de99248ddc21eda8e7e9e3e3607b6262424aa727decc07d07828a5f1029372b182e83d4e45605b766a2f2268acbccb65dd8e60847a306365ffa7a704217af6c6f0e1ce1bddc20a520be77c037edfaa4028a49b8cc61e0ec90115f2e7bdd891e1f30b95b2f44e0b1a20276bec66239c45be6df4e965ba9ea04e715add42bf014923bf8b590a040b39a43832675bace6277e27995fe3b68d3838c45209932e92fecd0f676cc76d682acbbe2631e7883a402ee5c41225b6bc6492924d9b227cc6ba95cf869481c435dbbbccd4cace4a78c3dced2a6995256a401fe3d19f2c50728057f9b852e22b51becfcb7fdf843437234d8b0f99d2166491ba83a895357d14aaef2912a18cfd1600cd31012026bd5c03fa2af3afa64dd4d5537e1aebdce4270d3f940fe07fea3cb70a201dda49a26303a14c8380e64d99c2529a9b40746daea708b2e8f5c9f3248b392014f3ce335c51633f527fea8ef6df7bf6c837a3c366c7f315cd5c2949d25a065f1a533728ec30576091617acb3e9695c3f94db39a19504e7a59e1890d1374f4059d0fda5fa0f00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f82fdd2573473541773e83e9d3fca1c33093a29752dd1ab02211bc0e6f83201f","proof":"443268cd589857938a5a9c6b85af6314aebd6c10acf3aa8b8ec893ae055b4941bc5884230af3ce5b7fec6c9fc667b6251f8bf1fe232a9a8894ab1d4825326f72040a5dc3eec2db26e08e544eefd1afa219d29e4fc72aca9bba40fb90b18a0859647faaab407501b32cc1f04de40e3db20bae92b4e7699a9562fc9372df797e4fa085c9347d8af54917335cf6041b2f51f24378b3df4d139fd1dcdab481509906eebda48ef3dcb2aad21f00febcbbb20a026e010231bf369f2d05a18255ed960058a57e5ccfcea2f1e28fc5e6d4a845b01afe982841603d5891b063ccc9c7590a68d444c929246724ed9da63c5a8fd02f7970323b31dd4edfdc8221957c9481394865a9109e04b2f4278ab97a21652f6fd2e068ef7b093280ec757b130f17940d9c27090e3d9a8cbe6e52685179e7515af9ab0f48dc70f6d0d42be4e597e87d2320ea505fbee70c3dcafc03a183fa7bd925f2a417ba4709f75c719577ddbd522fb202cf001b5d54fb38ad99353bec9a97bce568e1921868da1b1ce7079808376ec85ee7d2f6fe6f80eebee03636fc0b7a9a87057e0ac9fd936fa37bd5bea38667b22615bd1469b73d6c122912d2c802e141032aa971c265e604193fdbf1cbee42bc95f18cd62a45f7091e4b0fbead8710c8b1312485a1e248aafd9bbf3b230b0adedeb004416cbea904cd99de396ead5079079b91222f28c07c7f7a585738a3064a823f8a8323403d49b867107513f4065f5fef92badb7ce6bb940c8953010d097e42bc36e5a500d8a73c799b9d0640b253b771860cb20a9d89bfce9aae2991657ef2a172481f8fb050fe7cd5b3f5a8a26bb6f9f80c3b5b249ac88a131ebdb6386ad4a47c123cba8fe1e238d46bb97f46afe038849f8018a9a349f107baef6905a03995f5efaa2c52d2372442905ef3ac2eac3758d4915b33034a1a420b54bb0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7a0b3a49b4092915556eba3c3c595017e4f505183a0d9918fc272ab730a1586e","proof":"c60922f48f0c843c4aae687fb220c04fc0abfd76f7fa1a8d276b9af860d33f580abde7c57a096d954f52fc36cac826c8c7a1aa9389a2f33975f3f3ed83708d2d7825ab7b5b4d36def1e194517a648cc1fef5948886789c350ebad36c1d76974f9cee70008b978c44f2dfc0c5b0fde07ef57954e0f1bf26b2fc096889c05c16097c29d6d30b6901957f1fc585c2051b39d43db1a745992ec3041d0b23ffa9990f967326e6bf354f693ba20d37b56e73b6583c4ff4527cdbb96be5619ca61daa07fbef1cbea7e51799675767164a097fbd776ea04bec6ab9cca49f462b7620c40a34852412484f2a91dc178b4b3818ce5ab798fdd1a992245de11035f87d8d78289a82ea8b268b714eab787c996534237fa0803c3370b7bbaff322e6650ee3d10b70ebe76032e55318e3346aacaa88c93f5400c83db71697925a9f116b43de821dc84c66456e88b808b0d3767eda92c635479a31ce544b7a38819ebd072440c920cae883a475217d11d1a109ba3444538069625410b64d296c67ff478010ac56310e3ecb6aab23c31f677cf1857b318aec85a4066130c078fa5351a26cbf7e23724846396d5bb507c2f86fd25ae699847a38b4b0a5ef2f5f1e5ee6496b3428ed73baf5c3ebf445aa4ffe6d8df62c1373af394272d7a34f5b0d1eeb4abb504bb21622385c960db154f25dc665fd9c7c129dd60fa7ee2aad9f018cedadb9221b254f980b39df15fc2618145e332deab1ee8174ddd05f0ecaceafe00f98ad80a56c4b766a1d59371b461973ae3b94c6668fc95e550e5f30ace56625c276f1c17e8d1acc29b6b6b853cefd74cde83265180813efc0bdb3621e77a280eabc0d6e813b1e330a8c5f9830cbdeaf342acabb48d70ad9785ed0d123c70b38aa110e867eb008c7f4954fdf9c06b0cb8241aeabff3406ac7f91843645187cc8da01827a96d105"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"783c494cc425f078a23254d660db00e2c82ab66bd9d7c66829da4ced8f34960d","proof":"2e5edcff4d26b4871ceb14471b8529dc05b94fc17b4497536ed00b0c51ed523830bb474dde8029d763390852653c9a71edb8631f1d08179d47b9a470f5658e754e3ab79302e389f5a2974bba7d8e1edd23d8b0a35b677d185a66d970cfc5c74f70784bd68e01f6365394057bec82adbcaa2e075ec0a7c560529a38e7a871514c458b287a9f42a77bac16e0cd020f541226782659f163c7cca0ca8a0dd481470fd5eaae3a38c1e5187c46c33f601ef55c3cb2759b5fd70dd267478a1b29cc180b739aa766e0d6515b3f746a82f11713702dd23e312381484e54658fcba059670312a0b7ddd7a9a64374f6758d1965ff32b2e76cae86d5aa493ceae55d545f1e07a01685f7b346b2436717200dec36f6e7fe32d0f6a411f5edda8d04e6ebbb39547a6b284fda200dfdced63a069fb1ada2fb88491eb8761d726f5605ba637a393f143b0f19beeea20dbc75168a23015300259631cd5a94aa6d87d0fde45b6baf28b6ed0d5b097325573971dafebbce08d5f3044f0940685b8bc9f727f035251b1aa267ce0a8e014c8fc68748ae98d17558bd8a05755abd2257cc973d77c7b50579b00cb724557b522f4c4f622754ad4f889fc3ee28e96687b710b68f0da140cb4ccc1f575521774e21537ff8a024ac2dea7b49cb4a5deaa50b8febc40000804921dc1cf8cc4b4d683b97aedf955378cc24ddf902802a66bfcc136a8243a93711076268d020405d73a4e077b0633d74a8169bd8d7f18dfbb925f813824cf3fd563028287c4b97e06f1cc46bc2bcb00c93dc589503f8c2c45cc0fce8d3dfe6133814364ffa3e5099c1370dafcceb746838f2d0722fde2455a413a3ed89d59884897d4776a4d49582fbd6749940e1c76186090ab2fc6e32ae1b62cdcdf2d243007708a64cb245b60c72c9a983e23df5d6f10b42681351ef5843b6950258bda8cf7201"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a2540dc00bb4f612942425aaabba217beb5d16f4c8d13ad8bdadfdc42356733e","proof":"3cd16b60ad959da77c93d2fc249965b8011e8395d5d1f7a85c4314b558635c4042a6baca02a099835b77caadecac1c2c91bb209b05ff040a158bed3a0ab84b08745a00c0f2b47a80386303d5aac1f85b2f203125fa61340703915c318941a37020557cade1517b4fd0b65361644a6c05c58b58c099592902ce6f7963874ba51d3de04dc1f22b7aa1d64f6bc6170c809bbc4fcba48b0f4c163acd487d1b5242096ac5ca1f1802e3f038c2596f61cf165c3aa2970ca9095faa757288d503c2e6070689c57506e37388eccb9ad56892659f376d09da8a320aa7a555b18f2d07ad015e49e04e70055d77fd3633b7cd69974c8571816bdee6c7c0305649399927431d3e65f94e7bb9fddac9807954d4ab4ee942c7d1894f26afac872fdbb7c02eff095a398b0285cde438acd64b60f8a21b340d5096b09483d48fd45296e4eb3aba79385033ad36f6432239086e43cb77df323f02941db83784009274ae6d2256cf3402b57c914e6adf7d0e42294f1d9c952749c7f740ec616a4e25b9dd46f7ad004e24d86af826209c975aa9a890d9a0b2d00fda891a34a41f93c2b680661831e93142a8c19dd1d113fb4ff3eac0ed1a5c7b7100b6beb606c400d4e760ee9e240f6b54257f466e0896429b9368e54547e4986fdaa2ce133614867b8ca6acaede3e32ee4ac91cd07ff59f11f5c53e77e09cffc4d41359ae87286bf3861f4c29e004410863071c839dd02bb089b0c2850d01b1f333457379a4312a248c6317f6636c1c748699dacb4b2f7a5ecaf4620fe5cf90428b29294bc5e4f2506b02df6f931445dec5bd30dc262fd432ad7cec6aa7aa51e35b81859e4dbbbd8f7225aa6034566a0f592cd0038f72eb66a32a909c07bc3bd6a55a87afc94d2c4d2ca6db7b2da000a9ac67354483ae32a7de79e75eb8d1036ac56c50035e2ae6104e08cde455be02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c028d245b44611a5af7ced11d298f843d9180f9c35703f733027af7bb9fd7918","proof":"0c6143e2213056af0b7d70e4303323d7f0b67d2987fc88cf5e6da092860244205ab47294c84652792e07d67dabc6b5f1a160e115667bb122ea5de55d30706059aa3b2fa8f7d1bddaa48cdff11a34197cec6f17b6588db979fb450e2bf75b8c5ad800d38e61c8042ed0f956afb798748d9e21b3e470fc620886b5b22727b8cb2d78500f139188a5ec8a982decf2069795ed83e4654d0f7ee27a2ffc9638bdc20167a79459c884b477aefa8cea89afac42d9ef51facb9f3b67428068ff09e11c0c9de3ae3a19762b13588582a89f07eb82c0871350124020b78615552ce819e90f9c29c19fbc6ce692666a71e5a102fca4c256c6615c9d230b4ac0ee6b1a87d663e80c3b9c4171ea9bf8388a54164d45c49b0d240964599fe5cde43eabc1d701500cfc40f8cb31055bad00441b9a11bf5b319f5f4672987f1e03df8ac8470f691d38815a3bc9c33f0289e0de346764ea59bbac4d1db2d52cd3242cd158c893cb3be66d3a97feac3d005d34eaf6f5782038955188a23348eec485bf40d29ffd9d13820e1c59b9f82fbe6c4714319cc3d0a846de871ede8be7983b87f019f2142e1c200110c3f3e0787a7dc574089f442dba49a01e952b9901d5d307293747e5d46478b9904b2e59f5bbd21addb833002a5f4cdc9b0172da3d0edd9e286c60ec5322743b24b02ce2d638ab5d92cf756a6fd27bf41da1e0c4200f025465ec496f673c5c4bfcdc13e30bed1e2e4f71c9cded3a0faf4c6c7d8b9bb68ec734fbfbd6c73a22d4e0e07d6afff9650097c13e14a19cd319a35364dbda0de397081f883cff0b4006c170c781d06876ac83b4834e34a06babf7eff19d9cc55ace8c6566d30b7c8fbab9ffaa2c01306b33ffe8b5fed3660daf8eee0728d85c3ffa2208b1de120dce832a7e4a09cf5fbed8997021fc4fe5249f2aa18536bcb3aee2ec46a4f56508"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ba08e4cfd3c7801776660bef1dffa871c96fd1d2174e178656c530a2a46f2401","proof":"bed97cd1127fb5beee0bf6dbc5fa3d56d01e36c45dc83ce52c47ed560600842834d4ddacef0a45b815c314a73620f097573e89f81a158da164e5eb4a3dd6181e700c0399405a1c053d6501933668c74be20fd52cc9a8bc78d974c57dbe6f080b08470bb3cdc04088959edfe26b00e5a5ae848528b3fb8ad93f375e88355dea2ba6b32a5ad335bd43e0475f3d5ff0ab1bd2725dec072d05437bf8d82e02f021009ead6171ce716d9c6ce37a701140ef4cce8a65f4d3ea4821faba9669e5c6fd0a6f48b11b7d7bcb35c06f9c0ff263888f88dc3a991e5b9a857b4b0d562c732803c2b1efe690f8dc87f7fd369375c31c38bb61867b52edd29e395cac0cfef5e442d66bd4da7505c623e0caca13f94602333c75c8f777159725f092e85bd90e02235af89421daecdc27ff4b17f95713fc216f0acaaa7f78178709303479251a3849d20deb637802dc8452e7dc2a4843d22994c0e9dd3adc351f275119ca18ebc7095a777f6ae2ba4877633cb2f0489122025d1847326db6ac9e05cfcb426b32bb74be5f08c8bb9305fc06e8289557ff8d56b3526186460fc7eeebe020869712d23bb2b6d23af857384e6b7d14d57aed3a934f2994813651c0045e62919a2bc6d1405ec11292533997953d47904f01d01bb646a6f286b923d0deb79ea499927c8d4190bd4b61ab224882a28f3be308adb6d30c200c47ad332d7c0a7d33fb44f90b1534ea4a53f7f8e7c7b5ccfd80d87c2e7dde520174c8aaad26a6a2deff13a5a4609c9a7bcd5ef4798036148c0189464bf900475e57344971ccaa6e9127d75ea44da6e99d262acf07e211c03900c2aa097ed7c705f5f063d334be01680f1878db0602ef3122978db6ef6a5e1e5cf3b19069a2b4a5a2fb772b0ba68847a61d16090f607179c27f67bfcf71c2a99260a70d2eae527ef56ccb54f6cffd2b111f319000"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f4730d8dd291fcf284b7596944b2f84937377dbfeecf225df11dd5260e00e67c","proof":"ecc1f5e7d6de4325b1c13975fa11fb8edb2592afe4fddc1b3aee3261663a356de003e95886c285d3846dabfd61276a400783036659247566e981327cdc36545d261655f9bc6a14f7573d93f08b059321c96dc3b08c126137cc9e2e87f1344b3944ef750c597fe29099c7263c867d99b949db301db238590784b8d908b714f63037dac0aeaba16eec220333acf2c00dade2c41415a25b179d01142dc9d925100002ae92462d0ae61a6f3898716eb4630bf577beed8b56124eb731525c981cde06f0d856889057a0d527ba92ca6b47b390fe0594adc4bd339996c2690331426208ca2334d710da06d16dc402ea9305070a42291af50bbd8b52a61a5b3a13a30806362b7ae91808e54b1198dc8095cf25de5fb6e4ea4ca32170ef693000ae4519399cf76333974f131ca629d69acaabcf33b3787c749d9571cc05d214a2213784689433200ed0fb23e6adc9ec09123f93bb58d63604183191b02075af653194ba2bd4d78f66485682c865526a02121dd89ba3e460aaeefdb255a54e133919865417a41b5655f0b63d7b632650d47380cf57bc94c2b74af38e645724b088d6afe528349728fc4641f5240bcab44c3026aea5e2e19b4bf742c69396cd23eafc37865932993429469fea2dc787ec80404a990f93c767732e6eba1e609b4e16d973a81448d2004ca8d6be7bf76916e63dc8cf14887e8989efe81f17ef5ec11d202ba27d581c943d1a87db7b8957623e1b9dfcca6f4502f8b2b54b422657d72a0fa0d20a82c54cd1684872d9574f767196f6e0d5fd728cfaac3eca09a59a0a5ebf19c455f8053a6ce97d4ef90159dd5491e097550a85bced1cf37b433ba1e39d3a5959384c331bacd8a0bcad2605fe1a203e260b2a94d87126446247cff686351c4fd30481b37fe02967a7b8b4f67b578c9a5259c209efb82594d9a106adefd880ed5f07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3eaa8f4122804858a47bd03958a2b985ef7a4e3418d5dba91906f73fd56c2c30","proof":"e8c48f6491b28e0b05e9b19fcbb1a33b1a1c4c8f4744d79b6cabe846956ba73738a07a88bb89c72c455ef0998aed3ec6a8287d9534d0454d4538fa4c971c832d04511ca74af647dcb624503f4618f2e955868c08a0eb7cdb74a6efd67ea59365d4d0ae61695e1512e445bbe653b950815a9dc76409a84891263465f00e461b6ab8230b899fc5b6bfb200c9bd8d4681abb6247d3226c059b3a1a07bef3e7b290022f159c953324797dcd9d7dc2ebe40b0a9ab29746b5927037d9a9fbbe9d6ea02cb9414ed29d0f3084f5ee8b358769ae265e27d9121c1764aafcd5b277da2ec0346ac7866894aa8ebf6f0812d30288f34b7ac1374c559b9896d5b7d229f5bec1bc277cffa9c570a64a2c70f4c15c2225a8bb037c37491ade63e77e278e1712f001c4f5a174a28ffd13345d7f8d84610d6449bb639d9ea92357758e52142a0044ae6cb19f833a17605c78eae3961d91620f4af048be2112ba914bef71743882c2fc00038e923ca135db1544ce80df1c72f1cf47e9c8e6d5a86c4d0d9188ba0c3013c4e874c8496654b20cd10cb98a2a5c133befcecc037da571a5ce8f5470ed5674423a37fc6235fae673d009327ec82d8ec9df4d25b60a32448e1bc02643e0840ca0bf8b456807715da765b5ca96289e9fd26914645b39ae5e4ebd861162597456aacbf74cbed407c2b36263a99500a1208698bc0d1a920e992c65d87ade0e572d2baa144394ce30d312aa3b7bebf3665a72cec039b3f85ac81fd503191135d6c268ee59504ca3418b02b8ccede1ea39f9309bb05edfaa6fbc5fbacdea9b5ad7dce9017272084ce3ed4eff2c4f58fbcb13c35fc52a3ff3ded99bb36bb8f54a54119a8b7cb53662877638f205bb70d71074b798d3f089ed648b2ca179b28acd004b19b6e8fa69c5e8f0856834f98878cf3bb41678a1f3ffc3181fb1464c2227a08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3c082fd5147a6f7b7cb0e7b22cb13ac099f7be99bce3c397538a422e1df1521a","proof":"2ad3921597a3e735818f3b10b96fbfe9077f7b1dd3d9e965b5fa800c8a2ffa47cee83fe7e0e8911e864fe56e49d94050af389d07e1c63587d2f9d046d2abea204a27851628629c7842cd4a0c0eeabb00c28a4ab6093bde10142dea776bba452750ef72c4958f9f9d3ecbf0aea221be55e661293208709d6c93aa7381a9151f13739b68c000f5ff88fca2db5c29f5325512f866301f41c770a4ba488bdf23dd0aae72c589cd587430a21af06ddfb2f9465d8ddfa5e41da49c0f6cc991fdf1ce05e4fa56dbc03f4b9f5b0032f909668a96f2d91174764f001fb859e7c3d5591900c64d5e82ac471ff442539c4f20f343248cbd09c83147a7edf04e53135d2af771f412fd67091dc868afcf205d438401aed8c82be538872d09a44b710e01b5636e40f32fb674353d0ee7279ed4146d78d09f1de15ab66b1b5be12a7c026c660735367359f8263feb38017bfc4ed72309f29add07bdbce36c97049e2bf1684ac021da0428bf8b75b267810745848a64b167acfacc4effebb005008088caeacc945372392d6107d70d70fbad05ea6af7520a38e5ba0939cc2cc3eb349f57993ee70c345f995c8580b575efc8535ac8cdf0e526103dd4c6096562cb7af2e8eccc6c29b406207df4915000d422e098dc3e51f629c7db2404bc2eadfeaf10494643846ef496d74a4549a6c185c18a96caba835902db341bd4db7066702cf845344f2d1c3ac93affe4056f54b8538a873b3e9e6cd7836aa4ee227848186b43d93f5d9e13d210a5b3b57f7aa307c8b06d351e59c3566d503c70fe12b1e9a97441f3a0c71bd4ac166e1fa5150f5ab52893c38baadee003bc9cd5bf8344215ad9e3083c831fd9e47de2a5f531994e51f1211b2b7a9bf6b37aa1c876b711d72b8ed89b7125050f8ac169a5e2b8b571953cc0d25e8a96b5a8ca7a86f2e32fc09fe9c61616df02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4277ed04edf526e22e6100003958926be17de495df9935604869d2672b0c480b","proof":"687cbfd5aced102f67be5f4b3c82894ad2dfe06d7645e2e73d7b8a52a18f69697a1bc48f933727947f241d878f7b3d37b8e5530d95f1ae48f84a39818cbe6c34acacf1eb68e7ad6037045848ae0c6b0e0c07167d33e39045ad80fd697b6fcc17444318212cd6b896b959b8d7f75fbf0806c88c83c5b91ccb2a337a88a39e7107bac5e1d3d1a8b1a6bc60808e05d379aa56ad95ab93120c8aab32c493495a3a0d0cb1c8daf87a8c54e18690d252e7f678f9fd62d2540ea59a0af1457676f7200a3c686bdbe0d981217a9f6446ea4b022c937e9f800ac3e6cffd7a78c621cdba02d46f6f9739c8deb3883a5726fae89f5153eee69086322c29540a77f777e21b44c02250924a9199de227a244c22990ca8e08e046f76441bfa54c0310e4b310253f22ef487a88ce16b193bf5c6ceacb7fc1e51bdaaa7837894b9a1e5ef2054377b7864421bdf0b4171f9fd0d18259d3a016afa8e1fadb2cc63fd202140f8e72c408c8dab22f86baae4febec24273ab21bfc16f5b45a2e27b6aeb6e8f2b7ea821118c292cb0c1ec1a781e5afb24bdea45192fdc3eb3bf1a1c596dfb911f00503150686111808ef39dfdb7caf94739e1ce3adcf26a8f5ed924e197e3ccde228f7f083090e9476edaff834f72ff55b093bd793feb9960f18cbc4b52931ad9379d545490fc099327186349b99b91c76532eabd36d4c49f79c4eed48e6d16f4bab63c7fb01ce9408c851e3f0067070a5c7056a6b700cd0d9c6ca1f401ecac10fc49655dde76a763680557688c457e6a18dee79573459663d71267cef2a81f44ca20c62382a66636f4e0a56783f9c0ba81f2345bdf280fac36d9f12d59722d78b7fc831c07cbbcc3cdd59ef756ebd277b45e7ccbbce35a30e5f9a381eb7aff06357602030af566e4a445927fdc692ce0da7d7df42502723a61c1ae267ecf306209460508"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a8dead1397faf4c09aae8c3b0ef13e9586a09ce23cd71f744f388f4cc087dc6e","proof":"9653b72e60b294a773c8207daaa9310000064b3f32333f569f68585098587b5d74081f374a7bef4aa947fda161f7981ffc2457c525998e8c3c0b078392751e00fa3f12549c252c12caa7c8d4bafabf4a97fb242787293a0345faf4d75290433df0696cb48f905a7fd1cccc35c86e504530a2ff78853c9d8fd8e093018ae2b83a2cddab4f6bf65e86f0059573be6b495cb6cf13ffe1e5a2d904321a1d8d1ba20235af58a1a8cc3234a5720738fb4282a6c73416902bf9a808e7d66c8635cf7a06366813e96f049c4add14a732478afe1572960badd8053ae72cbd7451598fd0080af3ef07041dee628401289dbf26f2294571081b0a5bba4c9ce93804d25f2539f0c81f77d26acc92c8ed49820a583b6f9b94d4e7e7d41b129f5be5dbccb1b565a4fcf1aa4089b927d5d6f8bc24bac563315dc81307733e54952009321800c804d670f4c6f4626c6eacfc82fef56186b23cf6b035ab06a16a662e4f7bbe82df6bb6779b5798d0e20c5809f9c4ac8ec844fb65dc51d1d35a07439a1680438cb676ccf282db082686f80a5b373f98c43f3de0d769a2cb3a727a587db4835f9fda71f4a71507dd56d3a0cbf524899c6c368d34bc9a4d72f07cd4977a13811b2a056964e5f16f239464ba318202b3860f9cc1d99b913546aa023dbf17b3a90f47bf063a6d0f317bad50f9c068a17c1fa1b024a800899214e6d5e8280fd99dc0342a58dc43ff15b730cbf075a0ab4a3ceb3a5304ae53c4afd900d5953228a5757cab671c1bc8da1da2f3e633800483b390e24d456c904ee21f15b0615fc0d1111b860704899f1c52af9509e5c1e66c93925af7caed8373081bc70f613e3c0d4a835b0fab0cc304d0ee7ebf374955fc8b93fa17f4644f69e618bf7b9991b4115cd02906c7292ba69df48db6f041de07002401b92504e07201055261b5a8785faa6b4201"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c28be6280c725ef040306eaa895630342ec43a43de6ce1e404102e4aeecbd605","proof":"9e5c2ce58e8872b54ad4536e791c5ff7462d17a93796bd0d72c327f70294d003a264724dc9186eb6a105bb84e7821b0c536d6b7cc9a8a0f77f4c88cef0f4f231c2ae3dea09d359b50dee2e27f28b6b84e762315227227848e0cf26e5ae05fa3c60a2f3896beced23311c86d15463a729a93abe0d8b78f74acbad1780af5f4f730aa917b4d0a7f11b8a3ab192541e5115f9bbe0bc1987be4be63f908cabdade07277fe16c25573d267452c2467fdba02b53c6a9759236cc1c1c3e4fd3e8a6df0005e48adf60391e1e8ba7318df9e03a85a13021ed88039e5cd827866d3a2afb0c8ec674cda730349f070a4fb3d214e0abca1b3373a7435a096e5a62b27e0d3275f200ae0f1b3444d07d737b4578e734519ba1e37458fc417243faf62cbd95b902aadbea7cc33cbd16fc79835d296a6ce2dcfcef195b954eaf56d711dd9842c50308b8266862b6d39d9c2528fc41b91b58b708cd2c3486ce2d40cdce5ae70f065ac83e5e2630ba3a94d929c2725699a243f005bb0865757fdf3723217abea443144af91e37920b3f83869ea49eb110758cb94faf84eda9de59c19801923cae3029d8c96c979a2779f6c8b59d813fbd5417488e561c056c256b9be8e28e2d26f70fd6572aa09430d429a56b8fd5bf60fc9832c47ed455d5b28ed9525d2ce7fe7d15229c24ea975da5c0953e1569f2b82542b19a40bdece8d131ca8f9e7a45b3207cb0a194f70340464776b65412a70881e548c196b5cc8768a1a3c95b24f2ccb716d82d2075dd8acbeb69d881bd2c0035b70babdc1f2cf13022e42b56035df4b166f0eac3c1d829641d57098774e2fa1f1705ac179559a546a297ec2c22ad459e2c9f93f898997ac0314d33e60f152ca649fa5cc96f5d7a3a498e3ddf7a9df687029d71431a2a7ff85a1d7386a0858d3b7a42da7a94d852ff93ee23eef7139e470c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0052de069fc4165b44c81b38d47959d5b03292e4b97aa7b4fa587750ddb5427e","proof":"80c11516a7ae0279caaf56958d44a8b68d271dd77f521612e5fa2a034ce96219c071de7bdd5d6c02aee33c6c09aa1e184a71533513ca70b289a3eb98fc324e143e29749477380221bcdd3a2db3634ff70b7cc392f5bde306f76dd789c0a29336f232380205ece7bdc228423d9ea35a188c0432d62294d962b13928590d6eda46de43711e4fb62789d4e4947bccd0e11c47fb2d27981a1fd591527b07484a79006ad311d72a0fb21e9fb0f7eba1d49568beb67d41b91dcc3c5d4a207fe130f1014833267a69e095288148f0acccb069127e9dd97302b173d94870af884f5bb80c100dce865311c954753e02e6e9cea2949686f7c664b1ca3e90cbfc46b0095125e80a3295f7b4616bc2553b349955357ecabc7537e682c37e0864cba71c07a4055cfa8a6402de04312eb4fb0c23155f4726866fc3dfb0a54c3a4ab6184d841605d011df432a33a23a984ad1f9dd8da4f5845b7f912711033c11901f28cbb0932994eb0c346e513ea49512883b263c4143349aae51ce9f6c369f32ee4ed709c278d6e88592d53561ac56c252271be221bb27b5c4338c57bf3824507b315f4c7f49e895e5a271a30cb3683e912858f6bc02adbed5b6165fe8efb2bf14c2df66ea25fa1e48aac588b5bb3ca544b33a4f90f88883b6c96738b3f08c3486ad2fcb7703a80fa868eb9e95626e0cca072dcacd64888219fb095157a6bac706f90bc77f45a45c57505d4ff459913a9c5e9890f625fd72a0018e0ca43952aaf4a29f46a5221ecd6a730e563266f1a6c44e823895773f39f7bbc1c895f642c2e2e86a515469ae88ea3eb13555b5eaa4ffea0e5aa9d46c7beb832adfebc615bc57245f6d3b0f600a83146b8bbc8967b44443109d740eeea632958bc453a424afffe0570dc30b7c517a9e4b9559aae3a586205db65269d56b4d58ba0173c724e3915a00486905"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0a9fb66adc2a96e5960fd29a12ff39ed66f1d9d8e75f74772c3326e04279083c","proof":"9476a75292ade9b9b774777a430125438d3d6414e50b9a89f668439fec445b5b1aceab54a283d7cb99ddf102b3577c0eb8df37c17905757c0586bb0dddbddb3418480d93f243da41895154ed8bfb1d3fde35bae8964cf1ac83ef5089ac66f05fcaed0ce5027299a06ca0f5b81ce4fda5656c799c1ef05f1e25d77f24ce4a4428661eb043471b300d3e72d08c4a55bc60bb0b7e045e10a0641ef284cfe0c11a08df445086b2f1140adc7e3966c341057b6ed010352b2d5b9810e09b4e606f5b087ea5edb933de8c9e730665076d86cab40ed72bd4d88def1dce8ddcd9b891b206ba81c4c6203468763cae6aca67e3bc31c811db5d10cb7bb4ea73d5527b59f56c8af77fdd975da963cfe6c6a1281083d7ec58cda2cedb2e36b60ddcfc8cdb00469204db89228ecbe332c7d8bcdcc29dfe941192e627a86269b97ec46dbfba4d18fa5bac9ba6588881e176f351f8b207b1a7937ce97eca4c42ab9ff107e7045f12e45dcbc41f12413f18bc042b0d3d0687470d69af98c6f3309270d089da36e562ecee97c0ea32c3f32c74bef8e1369737d4bf8d869c12c9e73dfdabf82eaf6720c26b57b008cd4dd48f37ed09aee16cfb0363ceae25c923ccb3932bf08c2bad15d41b301bbcf7938d687f059dad4ce9afff9913debcdee55be14baf35ac8faf7faef2b8d8c10ee148f3d4b3beb36f076c50ce8502d50975f97114737e05d32b309ab70b94d2c0e8219f4968b7e5e2ac40f4b1a3ff38099fc4ea61b6eb7f560613785797800f740269ce6ec637acc38706a2573fa2cdc5accccdb81f5545daf507fe4c153c712c740024403da914d7bc3121a6e819b99b27210fe82d8244025333f070858a0b266d12268d4e4bd8bd8f700dda8aacec910eb503d0fbe64019d00185c73130a0d728667edae64e2cb220e03848536624cd3ebd53e7b0df1048f806"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"000bd927095ebe5a4d12a98e753be519e55d223a5655067bf613cf66cdaba434","proof":"ace0ff0e55a10491f50dedfd2324b648bc5ba388a59069b201574bbd2ff1bc6f5c852f6c0d8ed739b550c59c8f13d6f18ebd238a83a8c7dfa367365f9a68026af0315a2e50710b5e6c9655d7fadcdb5629840b3437dcc2eac3ced9b6cf04e800c8b159f95ef92e4366c1b4f04a23d309ae28bcb357cee1d20ff5b38f9824155fe49dacbcc838407e866a181a0dc68013bb3bd707715254d0e922f3cdb9d869025ecce402b5d285f4439ede501cf2ebedc2ee700ccf3f8285cbb920fe3a713303efcac95326453ca8ba0e77d212dd640574a62b01e4087a062e97ca19737a7807aefd3c24efa49d8c574bdb0bc5105bd4013245a7fccf536d13d32ac103dce576782123ae952c43961194752cec3db01b193ed5458e3f2eab146767f2d3cd9f45b896c2959b3c5b56e624eecf98421607799b171c5d8118ea439ead1e945f16512a042add63e92b563e161004c382d71234255704137f67fff92ff2ce5891e175de9c9f2260f6e45637186647325d9aa2e03bb314fd1ad93bc2b253aa6a271e20da692c3138370bc0be0d48775d126362b6e76528abc896428401f8f38c17f80a4c634e93b8ac5d193d604c1dcb8de0d1b25ff71692403f895360fc10bcbfe90cf0778167f5e9f8bd0f8a43470b840d5617d8a54d9e7f643dfd00c2c92e986c3886191026c9e4399fb6720061bb9f1cdc8e871cf8ced04c567213edb8a3f99a30ac9a50a7b9e37fe0923ae55c057c74a2a875ddfabdf7ef96da33139859bdd84f1a422f195f8cf059ba58d0bf956c79487e0a73b9f2dc4802e001eeba02bda82e7c89453fd47550c7f584ea0dbda8d4e941feadf8b47d50048bd1538447dd4328e4ca47ade3a762c5f04c9e67511cca5a2bc733bef5154b80a75cc19c9bf0ed0563edf3c50d1150ad8852d0a47bdcca1aaef948ed7d12f7a76c6129beb79dd40f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1e4edb086e7abfebb7526f3735e43d929fff40802c2aa590b97ff74086c1b55d","proof":"0a7ef93d8c9618ce5ce507994d45285300979210495b2cccb0cef53c6976d46cdc89c04c8a8e8946440becc5b997d36564e32153ac17b459d09adcc407903f10fe007d4492cdac9f1cb65cb091622f2a4551ec200f1d3cf9e527386e21481933182c9dffbdc2d154948b5c59558e90a75436839971e6128ea555306fa57824147d54a07627d2b72f644702466a8b2960a85246390959d6281bc42ae857b61f09eba54b61be607c60fb8e40fdf7470c0fc541691464e3838734c9880acb18ed0be563e620b97a12edafabcb705456742a536dd84b00aaa8a2b4eb7cac257091060088e4ff4434552bec28954031ad80a4b29b7dbea9d87d4e72bcbb9916d49406e43f6d99d1ebbe0edd685ef5e8d6bd8a0ed01180c592096efc42826cfbd3b57c2e0af8bd9b9ab398db95830572ffeb34037c333d5b5f2c82d4a54fdc29f93a190646ce402fb830e51fb413d83cf4203a19b195c3d0f163399ee47cdb6589675992647a7d57652ba79737df9f0f39b9fae689addcc96e8733eaabcc3a657a7c257846fd3be776ed42a3f6cd4ea04b98d893747e683c3c82bf257c124b3dabf752deb1314126c14389d3f90ae4f11fe84d516e314c8eb1f3ce9601cc093c349d359272f4f4993af62abeb88ca9f829c788082ea6f15a472dd12739f07ce277171bd868d8f814901e5ae8b8aa170a3c75aa9a108b660d8a13b06f3b43a184c6180da2c1f58eb4e610bdb8ba4f6a604ee17214ffbbc1728da12a143415f7eb675c0d40b92e6f70d3323e77b666d666526113e6b365dbdcf31a046241774c85a9d42ab09cb21b969e607f19d1e64617707a247381e86e83cc01bc1d807d0fb1d9115a54bb71163bad0d70a29c388ef32e6f4c2d7f198364f61c79737aa4729272a903ca334854510b93e007a89f43062691d92f0336a454a67ba95de40044cdb4e706"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a2679de57bab136e150fb735016bb72ccf379a7b63b3fa37fd28e515d638d135","proof":"6242666e7863f85cc01b6924ce57acca2f53f5534e89cb6e7ef10244dad78a36fcd5c389cd6dbc74c9dc48bcc494b708a15a150405bf9fe779e9cda9f5b89051d0cbda08986da009b42a0fcdb172b1a0812b559b5a7edcf369ec386640d81729e6eda6e303ac52262e734d0051e7d4ae0c044a5c291fae6957ff1b4ada339a630518a88ffe3eeef1b93cc70cd3ad9db819ed902e7d562e4792af8f9fcb12e70d484c8491a73dc9f0da8544c312596252ec3a479bdbbbc9c07102c7f122dfc10c2383ffb681535864b8d729785503ec6a14faffc2f4c8afd76b0d4da9ed23f00d0a930144eb0f237818931afaed7db06dac78adf7e88155dce7030af4858be073de444ded2e04b542309d20be722751e8c5991cc65dbb69004c689720f6e64f24cafeeee8fde2ceea68c4ef7e445d759e1489dfb0373562b85823e8ffc9882d3fae711c4bedf2db9f8dc8e519801ad75cc0445e1bfe06f87543e70eb4ce9cd65e14873aa52861228b2df85571c47486a12f35fd512f21fb5dea0f7244e843f5156e777cc86c877ca03b15c5f8f707c222f6a45ec0b2a20aa234b32cf9a070276b561199d42c211da977612c7feb7bfdf4489bbff50b8912e40351ec9ee1ca5e5d88507af611d118f7c08fc35f22e1dcca008259d20277c5fad6519f202342d30fa620862a7ced38cf7446bfb2778b4ea0d2f1d2ea6fb4098576bbe15e2c052b6592b09d0c4e5a99b089c3a391b76d66f2b927fa4dc0d7313b03e48cfba83b17627090faa444421e601082d1439341024a4b22ba00ab713a3b30e46381b246bd32d8e41442eb82341dcffe02349748d80ecfae6b02e67d3bdca94e99b33ddcea73ff61fb24661aa99a4f695e9fcf06aabce0cfd48d0b11cc89058a82c6753eb60a9d50ec7aa4e43e265a73f08d35d098d3ba193117dc42ac0a1bb91ff44a73c30b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2a5b53623106fe2bc65e3464a261d156fb542da131885abea161a48d5d64844f","proof":"f08bcea26732498cd94b23ac740c4c19e8f9059173a4311eeb17347b1aadc632f62e416cc34ba774f7f13baf39d70ff9d5ebc77251a0554fe2ea951ae9f6a56d0c6138d580e4cfc0624022afa8e2bbe54a791bbc259f134614b8153d1ec32f5b023ea54e772337477e1272d98980d6b0e9bc16ed0b99e5834856e3c5e73ee36500619a923f8cd22c2d8202d2912c5135cf48a706b7ba8bc8e9022547a6a12903e00cc952fba688015feb76e28b53f832c8d108585c81223cad96f32611419d0052b134df156220ebcee9b4f02c755ae4243eb24e998cf765f09373424b2d1c07804d0d6f0c0534d094462b663aa6c66915a61e9d77980de786ec641b237023439ce1c847b5533b04d5340ca2a9e9fb40accb0b62f6f69b4e0655cd962584c06cb43d4bc6af5ec7e9338febdbabb70697ff83e8458f216cfe6b49d9204fafd74c58d6770ee66ec86dcd26ba475d3942f224ff392d4cc578e3f62d0e1180d7654554ace6ccb01a445e0dcb8d94b8987e06a4aca019cd5aa8a4627bcb94ac69de3e727352e0fddb0780e6f4862ae3500c1625a6e626fa09d8c6384f2307ab318e78acdbac1d360c96e750180b52331a8e9cd3a9a138b0e63b7994d6b1f978bd0c0368e6d66d2d109b594316fb4bc86e2c5a038170fae02f69cad59056cb6329621eb4a7ada59167cbc4bf5af8cee0a9a721909da5e3360206b39a276e97ee8af14000a02e4cb0ee7f91c2f4f7075bd301d9ea87928c87a5d9ecc72932605934716d4a57bad0d65be737b7eeb33e61ce886293004d2984772b559cb34d97cec9ec26d4fac0b8d86c835364f7436b53afe8c050c6507d485a898a7fd2b1cca281b5183e103d964ec778a4be72694b4dd37c2351fcf00b73a4c93fc027a4a9f7eaf50138b943dcf7009fe6119a99648f97eeecc625b9b17cbc46ba9e3f16596e7f6e0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f0f617c2e155a9f21fcae96ba326328dd607f565e6a8dbe10428ad31b58f0c06","proof":"062d0c97c577f036d50c4e04965e3a1934648fa507eb6123de5bf37c7c98e654928540d2763af65d635d5c8846df2b33a8e13afc17d1c528283e39543170e565d0d17e235fd3c78306fe3d544dd79511273b0f1abcd6781498a60500bab524213e3852a6310a8617cbe947f9aa55c297991efd0f581aeca57130c789564b25207c50b3341e54d8bc6f02407486867c3a66d917d1f9b9b2982424f26eedd54d0424ce78bc688b17a29e94cf9518abce341c6541f33adc656d0c5301d420a80f03ed853168c60a80261fce1b122ccbdd83b0c07c8204bbe783f89ec056912b140e4c869acdace99be780aa8e5e5f0cd8778a335fb2d904d61857fdf03d53eaea30a80422529d5789a43f661d1ea7944cbc6b58dd3c9d095c9484a654607bb9bc631ef1f07628bc84c61b453049f18c0edd0195cfccd5ce2eb357c34e7105a58422f616b32b72825aa9a9339d2154d9a19b18d522cabc10ebea8700b968a6e13b12e204e47d9a62a5bb03a9755ae063268a6cad55b9887669fffd25509e997f990a80d2afb24dbb8c647ae78a886514bc17b6fc58a514cc5b8740352ccbe474dc6a5053063744f8b93aa25195e6c4738887460f05df45a8520b853d334b55f7712d342ef0aaf24e0a3f537db821927b7600e906cc7bb09bf83067befd4720a1a1346c05cdb567c49d6ee31e46111bca50c8922e2455761ded8ce025544b79f1771cc0ef590fbe36affd8e9fc0085b5345a158dd87dacc012653fade5073b500f32f5a6607f5951052f4fad2f512dca7575912fa25040e78ab5df14c31432cca232bfa31a005a67932774c2cded30ab6a65c2c802e0cadae1091194f68b1c07ee74a89f87ed10f88764911ea10164cdb482e2cfd757033f5a06de2557e786d18ea06bc523ece6f1cf1d0faf0dba9db7e8516cf3a61f20022dff92dcca3873a6feb00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"60ec2336a4e359b3a8b9899c6c987c3c229959173b035371872816744fbed466","proof":"7e4b4a546ccae9b9cfc20daf63dd086d358739cd2603e4bbfa7d5049c7fd21324cb44c63a356af461cbfd2a5f71df41795921260dea7f7267923104ceb0f1c003e228f0b3565449dd4d3884cb26a6ed7d8822b364671f9f6829e92bd9dc46b2080d338272caf812bcc018e25116889fa4ebb553088cd557b69ba5797bb76cf42911cc6c4f621d232a2d34c64a865b00f97cbba2a91ea7a18fac283f39e8058071b5b3211851e5479da30f457e1d641d77008212f64d010d6d384fc45d2d4d30b4fe9d3a9b0c4bd4f6f0f687f4ebdae1658c68507e9686b59cce88d0992c2fb01ba57154ac6e9b2bf852bfc90caaf773561c3cafcf3428bd24337d7820cb89121ecb7308a70058aa95474a0ec0fbdc7dcc9e1c7aa5d18a0a7d057a6c034aea6573e0309b4686617f11d448580323c2a23e6a628a7229bee17716808ae3aa64414ce5783b140ef2cf85d3ad1b6ed04b601ce7413d7dadd4d88d431bd40b21a2416c0b8e71398ef3f0aa50f1eeb3f5d029c56b5cfe346060d8a29bff6a40dff9b59c486841283ece533a74a174b739b89fb5daa6b6148cd30e3d1502b2d402ffe094ecde6927fddd2e173abc100f198a906ef6c12c4d9c93e642d440b86efa0d00f28c6de173587c5579fa4fc72f4e45776d243cd9637db9b69a60aa081c2ca7f525c787f1c65fdfcc0575d483d72ac8e742b8a09dad5e723160a76808297d95c1a7efafd54b614888f50e8d6e66ef3ab7b827c5f5373c3eea008af2b5f99de87108833280ee7b79ee74a6f5f90a16af40c020f82c8f4e6dd18426909c853269947de232e67cb678a9fca61b9f66027e948cfc61dfcb695433cd4b32eaab7ffdf7af7d1473959c783f22627c77f1869348caecfca63a821cadbb98b1baf0264d10680ad59c43b49da66586687380eba36efd10c36bd6d13094987dcb7502a348a0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7091227afbfd8150054763c32f0dc3d9bc85e5f1e4f16831cee609a5dc629b55","proof":"eee1ca9e4a9fdd2b12a01ae559952a6eef63b9f02ca488971cfcdec2ff2a0b3aca47525372c264a0be2d7bd46427d22e223d120a09565e61fd68cc41a4bfcb11c49ef26e5a4411909d5babb8b64e385eefcb393946640d5fa492c816f80d4b0bea2b2e575cdfc82ae39b23eacbf15dd941a9567ff18a120767de5364bd94d17bd30448a77e36157b0cc48d4bb4fcf88b0f8d1ed0c2457d4a6cda1645ac5dc8070794a97e28b254afa4d27463a90b98f5bdecd4d6b7a4dc8ac7653a26828f2b029276761ec46c7f75c30e9b24d47647b69bc3ffe694db3d2c20b67628e196ae0c34514d24c5feeb2c71ebe4a3edbc484161cd8d961423f3cfd088c5cb3d6d232b3e55d5371a4462a7cd00fcb06b32e8acef65c61b5bd51bf9ae7dae545caab2213aff257d12769cebbb5f7be37073a3001efb34e8f60a54095fff39557f02310e92413ea3aa4d25899cff6eae957e9fc672b53b7aab2f74febef9a894b2eb20267ad3f84c3071ec82dc1940bbf0a5d8210886e97d7ee24518da6c07e105838a3a68c7c710c52668356d63f6130a03eff2dfd7f80b2b5827687fb32fcf4c4c2a57a4951b3151746a3a82b10b637347629b5e435e6068325faa29b91410ff6ab80eba4d80aea785571639b1f092b3bdd290c4f46f149ea016b167e649dbbe6a48092cc471f7a7b8ffb4168c624e7e639415e310babfcc12b5ae503cf61137d4a1199604bb68567abe73ec7a3c91e34d879c1d3310d0ecef74cf4efda245349cab046040c332abcee98016bc90c92ebeac54925623d0fa23e145f43f93fa9139ee0f92a8766bae6b42923e3c9e3e0444f9c3662e7bd05071b330ac02972ebe78e84e6c3d3d2eafd53e970c68ab5bf7bdcaef0bf26b69436fbc267c4c7d98d1024b03c1d83742cc00b70e85cd3566e721c35dc59b06edc6141ffdacff3ae311583d05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2821d236fbba1ac4bad8d145ad519035d9c9766f4583be1f58a523084759cb72","proof":"78a81be1f8a10dd75347f8f63458146a7314bb8d5c689a00c32ac56bd364b13706140b179e7b1d080b4fedd4cda6afdc019ee9f7d402fbcf84594c24b104de407ce79fe07b62839681cc4ccd4bf46b3d7bf02de8c615f540c1b37682e81fcd5dc0e126b3aac0a7a134125d6dd9f487ac30e98c0c47fade03549c7137f4fbb546548503e04c326a08c432b54d121394d378f11575b3ae6c29e612b9b882865009305fd932b1b8609e1de1cbe46467a3dac5b4993b50a2443c40c30d07a5911606f2a6cdfb6c5573b96407be90e6ca77d2113568033c993f229f2db74eafa3de090617adf6efe25372733ec2f3ce690959990d729144b3dfbedb7ec4cc8c1eea061c2a9bd38c7c3ae8380153536d2edd7c7290d2cb162efddd1ba520573e3c131314b37603e5744912fbdd292b79bb29240c4ac391a1c181908bf8ef0993bf2f498667e6a7cca017aa9e4fc9c6c4f5a525c09c3663d27e1bfccd302e5f1803af30fce90e12e11d70d4e0d016f4758f3d9f51acf84a054e40882485d0ac9d71622bc0e8b05373a7bb7fc91b30eda46ed53b01df99ee25b4b5fed0005a795a7a51563aba61df193cb98cf7daa7d539bada6a71133a54c3dd9af4ea1f9f8adbf8c303d6641ec6d5fe0cbce5a140c1d24471456c86aaf91b558e17f756ba884fe2aa7970f4a59fffe38ba9ece770b59f05ff6cd046c72789becc0a65231c8c39c4ec1944b804ba3288a9a74de0e132a5bf73b9da51779369022e2c3e41aacb0c163b42b815b31cb0f52783d1e90ccc025c8c2f28836625fd1186e0a12694775dde8a08a20a8f44abfb963095adeeaa792f744279f7d631d31142bea65e92ed6440150dc146b620ae3c07dded4e24e3d82ce57db62298b3c219c0d6e3545ec7815ece09a26703394009f03b9047a8a16d7fd31a39f82031f6e897d79f91119bdd765805"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bcaa4f92ee1db0bae1d279f32959b28c0c41584f6d5f161c95abca6b2f155d5b","proof":"5cc964a5ecf2abce43790fad4c2208b13f57673afd7f74637ec75cc59ed9a52b08ad718286f60e6a02ad4bf8d14675dd0c92ef9a895f587aee75b307e840516576b78537619d2d20fd4aa03407066d0c7c23dc33d92ba9e9dfd66379f03bb5410ab937d2c8504beaacc364ea2d062312deb43dc4f17df8e198812cc7610af404031808da24c43031bae6cc5c706e0e268aaff2f0e06e6de40d9c3e213a152d076284610a49846ed13ec4e34ae13a61de6751e39f72c54aec68f27096a9d28b08ea8f126f149ebfe12192e8da44d948e9f5904723803a2f117973622986f1a50d408ab30d29420bdba14cd4735d5f413fe4a7f7073575c8431248819da88d1d1cca1c7910a00ca6c2ad972d296ab0ec0214d85a7695caabd99359a554b8812878dc6423de5cdd9d124dc68dcbc407cbe53060719ce35cbc6b147b80efb4f0663e782c29a8e614ac642d01d247bf97fd4e0d9db400840828c44e1c95029974c972bc4c3e67863226a6fec141c1a1f7df09b6796529b958c3be76589db188b1726394aa3c05245f5ddde43179ce647b89b4d0c63291c0f0298c46b11cbb46f3df6ff865ef1295aad5cf7e4f1fc58d26fc590169dc06dbdcb00344cf3bf3a1772579de5ffee2b752ceb7b5acbf1553aae41c9a184a1cd3cac3ef4fac3dd5003dea37c27560a1c9b66111fb5eb634c8f1cdf16cce24d545d0f39703708c118bb4775fd2c22fa441c2eb737e2c1abcbafa24cc1bc6fa9d8dcba5388573b4b85ed5695e1c6dc3ab00b382262d38db4b6ecaf20579f44745ce2d8a9b16af35f5435d8c6fa67dd1b4f3885aed371cea8ca08ee8807a0792c769454d96df06974d19a41304d4afac99bb82038b1da61597cbe02535f36f132e932b28a95dcc4589d0dced039aabe29e254735f41bfcd3933f523a61384d259ce2660794d9f05a80550d8f01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7002308a78f166d57ca55f0015100a3b0c3efed51be182be26d93e29840bbf4c","proof":"d49691e52378447c1017ef268f2a8155ba1754d37f5411676d1040e14d84eb000ee3abbbaa455bb404efc0dc987052261e9b34a7bc5289d0f5361355c284b75db6c84c5da87aa9ea766648cf29ccf2bb8a6d4b31783a3ba8d6c35d9d0b75187d26e26f78792b7eb2dae8e099450dbd48477fecf40a5fedcef141937bac8c147681ba95b93ac7ba02a0b6187ce14b070190b8ca0b09e5c7f3e057a692fe50c00ef4f5a78521744a8c078c4e2ac498411456808342a0358f21f8de28e34f75740f5f4f0ddc379e8a0724bf470763996c68e8b544f4e4b4a547e234e16f9a30b40024b6c9c842da4b3b3404578ef51ef25fe440d02b9c43ebde436f715b76254e1e1ecea22e5098f4834864179ec461cfede6f40857fe6f107bb686f9139f0b4a04b425ad0d147ae0a792a16d74c4d68d0674f69c0c9db63b2eecd46e489763af067e9289ae428b5ffaf6488d3045c0cc11ae0949ee34369e2a25cee740a767da0016cb4f334b0f1fd45196c82608a00be6f29550cd27cddd86981154cf78494c2d7cd8dbacc71faced07a3cf46c4b550c2cc1137eec4623e701a88b9d84fcd0063e8e891f4aeee58140ef0f9e88c9f4d9f5eb943cdc40c0b18c46830891254204b6a25859be6b47e3770864e8a35a90d70227735d0f5523e9c7d83ddae958ed75192ba9f8cd7a20a40a844b120f88e59f33ca4c6ae8098d0195bde9e6fd6cdf4788ccb86541f888f6aaa39a962dbaf855e9fa4e80dc3cd9ebc2a9ec6507cc8160c84a2ca0d1e7082a4d25391ec20b568b5b6f854201e0012b086457ee9b507b047a685fd4644d74aed8b03d9cb1e7c4afb373d8b31287461aaa7b47ad6751ca851f12ba5be335d13e42927e650059073a2c970553110f0b6ad71f5c628fd1633025e5da88c8618c945e9522ab6ce7c9f3d64c275e34fcc54999f12b8a1cce6e405"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fc4edf303ee1d51b525888ddc03cd4d69dc5960b101b237b6bac1bd66aa69c02","proof":"1c52f9b8e42b4cde18fc4b100768eb15d204f3cba4defdfbc38e7a78ccc4f86a264c748de6c165a097b927b02e2eb6718c02aad2758c89ff1d4792c489b27567ea7df47f88ecbc3bb02cf32a47986716601c3d48df0a925ea07caab3b83076667ef55888d4779723c6afed814f1734ffa8c5604c3a208df20877b1530f0b756bfaed7470842c6653f3f9b70d3e67b7a7d2c3da150af6377b5efb169844e49907047b9076a886daa8ce8da843c9c7a26a1e77fb8dddd689eb659f48b399f17106092e78ebdb71469774a9b6d8d04a994c1996af537a0287101fc2935fc6e57a044a6f3b2563b57f750c5db64cc97027e9372146ceb343f671868465d7c9ee1e0b7c3b8bd78beb38f94ad1b7b2ef3c8b33b1632e222db74d9386a3dc640440134a92521aacbb49243ebe1e82dc8fc303850a803265a5b5751bcb95407f44449c428071866145c309f3f31246fce7b2f8978648128935ab8cab97b2f27db7d9dd634c0174cd677fefcbb3ec3442f5416b92836b164fe3d167585e33f2c48b0d980b28575eb114d4000dd7f5d0abc4beee174433cbf11eabad1e044fc432c0c556178ef10a7fc5f90f818b603c557b659e9edda64ecb5b2d573f937488cbbc916f20eae011529c2097f7f4077a92ecbe8107f8a04b4e1a9631528fae43eae0a26551f00ca27d531c0dbf4104ee2592b311e9544027ceae52ae933b62af4ded04db0eccd9a8ba2eb55f403112a4d07bbcd94f22b5a756fdca80084903cbe7daf4bd3d7ed22566e5205611ed9cf4c6587788fb8641416f48d9246c2cc2905838c1c3336a7ff550f6c8f83b693f1019354626124de0b642f83e8310d663d84de66be629f5688f3a4fb0f8f3e1e53ab63a006da5f34e1b09354b99fec6e82a485a91ff09609aa98f7120c76916c824e4239b561e9d4ecf5f2f5bb47e8aae2650dce53a01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"40e3be4a0d7e2516707c33509dc4822e61a1716398b77aa64b4bc39b87419847","proof":"f27053f6df7ae9b696cc6459e034f28554ce70fc3e640d644e6f023864bee360c61086049b8279ead1daf5f0fc55261f1cfaa2c29c9e5bb4eb559420fa2d2f580c45193b5b9d044639ac9713d7a9f5d6072c864c927fb76ef098400b15ab4211c09d918454eca33d167d161341cc42f10327789b55f9f19fa4149c841148284570a89ba035f1c3956738009dad0b290ed6d9e69f8260020dbf2b5e80dc0b4901664c5f2fb2dca73fdefb03d8635062189a0acc68d54f1d8341f9e875162ee4018dbc6823da5a23fd91beb8f5b79e00c637a6fce4c5563bde72437161844cb00f9c45dd8263e81797f9de56533582d60188dc7c1d085bae453ac859b010b8966b761acb444c3dbf6e816f5b91610fa37e159f270de0a38db1ba77ffdf0e0327781659433333240a000d4ac3f6108981ff4b38d095c123abd89eedbc5b3c34412938b58ec3f0cd6ab6b0f286ad949c9712085449f77aff9e0dda707e2887b6575c7efe56b4aa85e9470af0ba9baebbb9cb4c69518e32f38487f0452c671ef03c52eca8c4e51276f22265668e9179465562592cccb9f6a90340d353f2a212bffb737055a960ccc9cb3760a31b068ec24f16d798c068010ad166e8e4862cdff6504618a75f96c7df209233af28a8ef130f7e23b2e0f28d2ba98c2f2e4bb47cb7b451de01d9a8c9f25934c90cb37082fc92b9903557ffd04fd518534474cfaf9a890d58a16a9df72c4e7213d96becb6923521dd108c04e205df554bad6f7e37f89613d6dc73b1c82db8ecf07e4b0568f394768d758f2bbd4bc33e98883ad6717f524536669e936efef6b6f1ca95f287bc422f3eb35632da682f91d22e67642b24f1607cf49108a885ef0b54cb0108dc7d26be0e39642f92bd1e00ce6f08194e12a504fac4ef36b752ef674499553192f6134ca6d2e3c55b3b7f2c1bc8c8dbdc09850c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3e762e6e3956381504cba0cabe9b6b2e03ed8a96bfaef62e0e6a151f76629a6f","proof":"fa9d39fe505a3301744cefc03a5327453db5356d3be86c786f24fd14697143372c7774fb4d78976d1396a786890a713827c79d15705d88e80a5f8c2d5a09202a0a80646aab0285a670a47bc2fcd203c5029d9586bd256cbab6bd1d3f62d16b176e623d5a612d75f508a6616010941f831a4b0cbb4f21f421ba537830e624b3217586bf4fbf8974d5eb893d26a73d550f7d2eaedf17ff871a6223f21b1f22a90c78f71750a22b980b3554b04a13130bd0a8ac3e72eac577c2adbd4100ba55600d510966bf5bd0c95b4b198c80e8116eaeaa6dd88796301b64d83adab6efe2c4002c8593782c76ccebd6c689fb4343f866292b987b823d3828b04ff08138a14f3c00f20223959d7c81e2673d23792356c168f51414434a57f87245db3f1567e07792b1d23bb67bdc07812df92c8f889124d60a15a1a925c6cf166083b7ec04373e76ad7a1426303d5ebe78be04cf873bacbc21843ba446269fa8d5c0bca133b31ab4a6ddb55aa19162e3a5e44ce9b310c19af816f1d780e8d18534af9517d8261cec9ccda73996f6806117787f788bef98d1b3730b175b845d740656330204536f04eb8fed56a93d2525bb1295da681d02e23cf183ef9aa654dee9eb4baf78a03986ef015e9944f80c4af2cd6d5481c8562b6bc029bef21f3d652298721ff37111cefe4d2b3dc2665987845670c52f4ad773a23d0731e7a55d55881123a365c4719211c8373f839dda381777173f728f587d7f72df5230078a77b55db75b26c9578c5f51e59d3db3ad3ae503759c82592edabcc56e234adc33d88065eb5fa8027ddcca9c9ed897bb3aa5f43e7e6deff405f6871afa778cb8c301fa817b536b1d4fbf3fd815281f658bc794d2b696d5f178e2c6b3445647be404b655d783d475c05722888568260d44c8055b554093cdc458444881f1557588df441a3b4127e5b02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"44dcef10747b58fc6c703d0077da4ec246e36a130b5c877fc703da0a1b78832b","proof":"26b90e8c55cb13ca3029fb1737297f7520ce1327c64fe1b9711f945cc9d3af32ac7ec0f90db032cd8f5409330d862e49b16c18cc5111c24d3b2c78e85ac5b5737ee3fc13b958074d8aa97af30cc1a6b3a7924c2d2bfecf1ca17fe794d2310a73cccfb0a205e6854932d13828bce7830a13d2f07840550331f523b75b280d9f207919b51edfdfbb9184a0a9f991e549e62a42e66d279e0e6b1b4a9960e52b110ad7b7869287ee36913e39ede6f019e81fee1d2e5221e02aa6078daa76c853960dc9a70bc7e98a593951a1b4acf12d3ccbaf4267e0a8796cbe3bc3557b73c1fd0e96e8bbc8867f24d1dfc3e800e74e7150b6a4c4a7609b1510e075f74b22feef1a7059464b66af1e1733f10a48831431f2df8756a28ccab2db3dd384d78d7aad35880194037153573e79df7ae337561d7d9b13650ac57f302ae57d5e6bd504dd2d3230bc4460aa914fe8539081ec25344c3071c428ab1058242ccc8cf041f4fe4ac6edef9987e3e816d22c920f4f1ec49c581cf870dc8933c32d05f6137cb1fb03a804b2cbdfb3b95aaf72b2fcb437355e927e1c972a35f43715c41e597e94f03da4c6fd2c5b4d1075609b100fd074168c57e6a640f54f0b677310af081786176ece95b754eb75b700b19faf1f5e258c32e14fe737b850f41ccc84029d9064d103b02c3c7836e62555d05c21caf79bdd3615c114377b97d9d9232a6ed46de8916dd8d80d3f3c0aa98d0fdaee020af0e5d984fe3ffaf25ec9c6fea70a8f466cf356600f6bcdbdd3a368779bdeac5e1eb90e3a473a5a778d7ab3bbfee42bc11d5d25ac044f12fb046d80fcecd1b5e52f06e8658d06fc02b9149a6c81102ea16860449081e6b3139fe7ee181c3a2db8cc3c72da1240c3bc209fefc20a40a2f4ede904acf3c0890b5e6824ece52c4d2988816e7ca4724a790615253bcc7acbae435c07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"96c1ceda4796e7282e4315e594396277207b5f9141f7197c0536ad5537685711","proof":"a87d37f1f24bdcb6c3f9c9e706073e1ee1af04335a02a3d6cbbeaf2f0e0128641a3175dac770c2a361d0ede956dd0d0be8fdc924468d3f1e5b1ecae09539d353840e59d07c8a65fbf67bec5896123b6431acb3103d97e9e85c0cdfdb082cea593848df9de33bb5779cd04caf28d3bfc98106f85d75802633fff6df80ac5f33430c965f7d64655dc756c94f099b0fddc10c6a71ef35cc7da6f813da363a1b7c0af6fe3287d38a1e35426238898d0144f7447135a48001453d182eb270b6918e087b018351738c324a8d36f521417a8d90f54f76b1bac5f5807c239c01262af30ce05c916d3c14e2864e112a352378c016ee92c8bf0cd8d5e8c74d1538565b0f588ccff84067ff86d65081e210f385954110a546dbe236f4be6b07b9ac48f8d9406ecc111330da77409301f029bf4602963546ee4766b6cd2ac2c815442c3ac04aa2672c5ec818721dd3bed7ddf11df32603e89324c51f2e0137cd101879ac686ea697824e15ed93f100c9a7e17c29996103e173344d9d0fb84f93583924558f10d2bfda00f7b8164f8c3456e69478eec3c230f24993d8e4518c97be5da849c86db27498c7131942a3ebff7faf0321a56fec1c7eaefa781ec1a21b4425676b062bd4465141c7a9aee79099a1e2843ec1e1cc1289895be06899b914572cd0a63b1ab00a1164549ae26d8e4e139b097f5e621b12727b05322faf4a283529d3b1090ba6d1593974aa5a6204ae912d8a234d8498c2106c9bf6bec6979a2af75859704c2eb76b5c5e608401c509b969b78bb42acdb5c06bc353a4545debcc9e714bec329a14b3c1b65379fe7966a6a48962a33dd8e26b2a21907aa114da27bf562003477e5c1112e0858ec98ba8eb7be48d8305ae7d8ae7b66efcaf527c073b1f8fae06401723b5363c8333d385237c7476177ba0e3cffe49cf973fe15cfdbb3b326c03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d44863a9f7d5dc98b39035a5d4b9ff237c52dd9d0ffdb04cfd7afb93c5409753","proof":"9c418600f78a9861f5801512664270957a6862808b9b08f182ad905fdebe1b0f305bbc87bfcb1f60446e6a7c147a16bf85e835b2fb8568440806e832c276a148b88444743ee9bef770c4d3ee501a060de11c7cd27a4843032da2a2739eaa201f96384cf3625f0a6a4671f955e1812a76a184b48561b4a36daebf9137b9a53d4a157121e3f86d377557768c94dc4adb4a6b7af0d4234614b053c584612e09b00e133300488861e8eac352f638937b34f9460f55e146ada02718290b6f155bf00589c5bc02fe3b9c8843223d76417b556bd0e6f6969ad23a96eac4690383eaec0b96f9fc4470d8949cfb42f617f2a448730b2a34261b5b8583f7b11bdb9a348247483dd004216139624e94a4a70ffda4ac4990fced21074cca664702105ac5c766f416761c890562e0dcfebae28a9093aacc2e15a8b6b9d1d627e726242649b74ddcfb113f0bedc8c7219566a75dcf19df3b5fc93819c00a69739bca03078cf200961ae9235375284eef25f736f69ffd60e5d06243efa822f946ab9a88e7d8aa4f2eba35d192833008fccafa32ce06232933ad053a90c01e632274c05ecab8be4c6eda5f7efc3861e8949da8c46bcabf06e6836a9264f5f1e257a0f370eb92fa23f4da7be40d2d7443d5ab127bb2e53a74cb2a01fe7b582e96afb31b9e3bf38e018486343dc55d993f719392c00b2a5ba6638afaf463c3adfe6f5b6480a77e7d1832dc8878761483fe97b1170f8fc7aaed440f7ec14f27d538f2b610703895aa3d48a66549f7eeabde5064b562fbef961fd8de833befe6643e78cbc73320421e10904ad2cf111c684875eb5f4973c2cdd922aabe62eff66d76e28907a109d7f51dd0394c47c3c6653889963ee5d2515fce40d51cf7b131f5d23c3dff469b218209611107677bfbc73067eb8f9be5f00a9ceb0fe877bc58506faeb2e98b3411fd00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ea7b0dcd2b8941ed32c8818de34ca2489ba15900852b8d8f2c1a49fad7ee0457","proof":"fcf032125e4f09849bc2878f185bdf3d484b52ee61b6f27a698b625980dfd164d2413b1e26d5283f0adbaaacebfa0ae85d45cb1158949b67be39b55a3388cc031030f0d695bd72de026c535f7c2510313a28e5d57a2b84d2ed72fdfc8d55c230a6c32bc38ed063af1552d31037e27b0713c208463ec2af2a39c5596aba8c0c1a3bf4e6f24d6c83080b8ee3633d13b743ded6c208dc928d56c75a1b79945f7b00c24edb9230bf4eebbbfdf92d4495292c4a6d9bb819ac3c81d91e14f250ce3002703c3980251d89bae631adcc8e6eca0e067f902813f9bc11eb93404da1140e0cfce4d6268920f79804023a30bf277adcf569f1373bdc9cd5b0407986fa499d4324851b7e4ffcae9ff0ef4c976dbcb49d689ab795342dd90bb104e4e442441c13f630d68eeceb940acbe5dfd82432c8dadec2d687f9835bfcc1ba9b3a71a2e377466bcc1a285192adb632a2d6427cf4e8b628e076a5a441f6552d169b63c436007679b7bfe6118dfeda99ab28cb204325925714ad2aeb6ae625aa935b5626624e280f9580f9038a1187a95de6d91cb7500e3ebb119084c012e764d79154649409c29f100e0bedd1be245be8400d0e751152604af11fac2614b97a737b61d0555c8271322e4d128d393492bfcdce2ee0bcd3fe9305f39644d06c224ee2b44bd378b2bddc1063862ada0c363945e37dde66a1ded508ea1e1a867fb754e6aaeb71488ccd288d39c867b2d3555f576118ff728b79d1c8e8fb846a7d4e96c212baba45eaf93c97911e9be4dfd5f37f795aec70028ec826495986e57c889d3fadd00338e207fc55d03df286f6beb510ed14dd0527a6a054f8962afc92e199b8d5442756f5c4ce018aaa01ea77b84b8704079ff632d8c59e91dc9a295f8057e8e278860b646e2fe87ac9c42bf0b36c6c790da75ae9b94bccefa5ce538ddba582131f7d0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d89de569a61a73a7f68bcd6ca851632b2d33dfa6d4dbbc4b801b71a3eb23be29","proof":"a4e41c3944272f23027d15e93035ab51340bd867e424cb69b24426e1a458b877ce2239e766f815f1434f14bade099244e47f569d182fe56f6760e627295f0e2aa2604dd7c1efbaa8ef596e3ff7b06a528e350f470898558d6e0199a628f76e61b039d3a003f8736429306e81977517c283adcba71ec72ca10a409833d0671779634705a092deaefb2d5645684c7a1d274939c6803fbf9127c23d7f59af7e2205ef47b9077c77ad7d71a15dee37fdce514a92a90c7802c435124bef074ff6b3078dc077b2187b537b9099793adfaf1a84037c5e9dd3508e45f9e85e391af6cc0ba42084c3b82814c5788cf8789c418e6a8c04b89b92c6d69d0c026ff72032f1346cfae1dcfa5426bae9fc25880d63d91fb4f52399b4622caa2c6a82399898f04e1077f256c6e03e76925db26b00747611392539e92f773ac52b88a05a325b94519689c8c5d4bd710faddf4446f2150a5d9fd86e3b478d0a95961539d95541604ed0dcf2f56ab8c0541e3fc577e320c78c3409f5d9a9e233ad91516b10433a5f68644bc7b6281089dc0111de734d8766f9005d7a5847d76f2ba84d42218751f454daa929749f80f5e6632f1905def5498b85a129f9457964f610e633ec5ca47920f223b7142d7dcb5f0c5a387d36b208026ef45d66665e89aae7831540ef78ba02c8d5b7c97a752d4183762246fe131081e2bbf4668e61423b279a6b1e714c7a4184ecb580f59b35aaf3bc9ed22e58139ed9b482350279a610e62259b5ae0fd676a2c801e3112fa31a984ee47e7ad2d821da98f0f29da0d808d63a3ba05c561d15c63dedb249f5dfba0302dbcb88e67a9f50b8f441c8c2d6e04cad72cf9e61062116d9f7a9cfb82e4128fda19f704b407e61419c748c254bfbf33451d49f45400f0d7050c685d17b20e638693045cdbbb283208daa4b109d9256151cab7c735e02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"32412330761646d50d19884607048162b75e4f2d918f8a84cfa5271bd91ae909","proof":"1e4618d9b7b4b6ddd057953803811e74bdc1c8a7feac35c528d6a7ece855743dba946d782f94c79515cd7a711ec31d4bb0f0bfbff0f6eb7fd575247f39358a5884c788afd305922386cdb0a3ac5e351e8a3905e0f9e7e7a6187710bedaf1914646f98f2b7b85ddec2b4736f12e26ec9b17a04f28db7cfdde61e0d400ce920e43bf020e5a709ec93aa7c9e0514335d7a33d4f807cd88628c260a7018f48dee50b35ad98bc4284cddb1b2e0241d99ed32e470acfe2d2a167b3c18856898d01af0b719f3277d3cb35178c3e2a6d31ee141b71ec8f7d4086d36837de309e77c0690256abe49b4ecdedee7aeb9a7bd23729887dfe261e741ddb042d2f10d3d4d5751100560ad0b724f593a08d453a815008907e9faff909ede16f441aac7417bdca444cef9b281354b90e511eef764656ba4785b67a7a8c4140481ee064ffee80de5954448f9a79a0267c52b04eea43c8da5c3ff700a113c8fb244450b52837b45017a0d6a393e42a80fc87a3c71a0af6a8afd3272d5abe844a3ac4883243f2a84906024adbe499dc12cdaf1743d19f9fd32d3f3dcd84b3d3fddafd840e06995cd04fced63ed60e90782685c7e37f306a21591b37e864d2b08c33a96082013f40903a4c7bf3f96307fa1e9f5db59b8fd989eea35fc0af3e2936b3a3008243efa8b77684d3b3b1d1dd35b148c765749af9d44a2675309944dabc59af7a2b0f9c7179640e3ebba0a492ebb600510a25568fd5d688276f180201844d01287c63e8f1665f0e1b501e34c9a8ede7eb29787a9a59facdc8b656b8fc687ce9f62013a914930f0e4f9c4946fd556369262caeda53d4baa8555b1d1d165757298a9e4aabb2753f28c0ccfbde79e8e82b9bca207ee4fcfd9a43e761f4a169d37afc012bf2e55b0410cb625072b776dc42f4e36eafcc13e58ea785bf7320f954a5d10800502cd00d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"607b14c30437a0cb60da8d36bedff3f86852762a24fd019fe9ba009e17f10139","proof":"04ee0ef8e9a2c42c8b6c05da5e4608074089b05d99d19152f7118a8b18047b04f67bc0d42faa5add7dd3461608fb00664905dcc627d1612c0cd6746f544ba3554e675a0d7cdd1086a666d3f43bfde6bb7cd963cecc2fe4b3bab66852f9ee6519fcc285caf820f0a3b0f07c99247401299c8b8ec43cafc7426fc7aea46d90f125a34c31976ce2b9dc7f160c6fee9eeb1d044c690698bdf8a2b4a2f8f5b9795d0bcc1c94e5b0550fcc1c1227b980ea5d5e270d969267c1d1a9bb7ce33e5a9f260bd0657b82cf4743908bf3320218a1b7140db4ad8b429ab4aabcbbabece61d0f0534488e33e4269833e58e463789f0a5e6b7b37bf11f342de845b57b5fb044c2005cc65395551607436f1ad45191e1edf21a00bec1ac778bf59c80784b8903482004f742283b10649045af8329530163af58cb1f7ac2e0e7673582190197f7e64a5227c034da0bd4bb7854c492acba9bf4608636ef1204c00adb164955c89ec73656029abace3952ea8f7477c0e14722c21e49c21dcd9b26e380f2c63d3821dc57548c6becdfdae1591bf781a1d3174054c544fe2094705c9ab5e464b51c3c0b4e30622c060cf4e622bb2400b2506bec686f58bd7b08e9c0b72d0ecb45f380305800d097f65b467e8773198a62131fe1f22101e8d9553ae6dc22688925218d4026c8eb9d15ae6e4b097ff8576466e3e23e7f980f319c274a5891321d4af3f6ce4af217f03ebbf2eb76527c7ea2b7b938a594e20be496293d30c421534e572f607978d5a4b93120bc45a9cd8a3b824b973cb85dc7f016b1abe90cb6fd457c70540472cd9ae12576f7ae93262477801a937b052d4f594c4dcdf5fa6e063da5a48d671a8c447ea969d1a2851c3ca55200dc3c365b182987a00a6d5dafb0498605630ac47fd8a68c1ac8beab98577e0968432444c32faedf672765934295e80215120f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1a6ca6c817d5cbe0c62496b986b6148d8bd97e5efdf16c43c65afdf31f4bdf28","proof":"9c58a5d4c8f76b833261cd28d8f722a9dff525313600261c905356dfa08458623c83ecc79188400485fede38fefac88731a065bc7656b9fa2c1bb1395291a842e0efdccb3c46750c2225cd3eaadcb7cc99e5036de3cad088d498ebff52ab032ce854c99e460a3864f2e17c18f1ec0926b96a191e291d3cb0b1fd4b2f1ca7b662b9537a2ae6732320b36876e37270be479c56a552611773e874f19c1ab8eb2f088ee84c29a98ecdacc91d9b1b822d710dfca165e08a08aa6f9610615cfb93d2072f550949ab647eb18256cb0a4fc02dd29a1871900bf6194c81e807bc8f8ad402803fe8f3a9ebd979b5cfdaf49e2397a2e7e9007842452518f5d8111c959cb22a940e20b23ef846f3be677f2f8499533584f8aae36af8d84d58ba35a6b0968326a2503b90f9e8098d91e60eecaa23709102e78a7718cea42622ae82464a03fc467021212da6bc64b3e93ccd8042ab3e979a9cf0c2dbabb8f23d500440f858ea06381026f91695a2b5ac4f027a41136a432fbedd7031acedbc0c2e90e78fbe4e66ae57bac58177414b5e02c794a9b292c416232d149bcec35c9eca8c93d225946952d9f5ec9ffc07b0a75ae14c602abe37ba15205494b4c96b0efbb7e962a7d726363cd747a6e5f730f6ff085c7399d3899a639713fa2dee2161a4b9a0d94237393a6c71c28f3d55b923c8c4d6f9e41aeaa6307c37de0bc1811ad37c417365da5d584a89a7e4544830b089307b4bb37315c3d16583fbd05c3326e58c3c6a06a33b82299a0965169b353a1bb9cecf50700f0f0b9b2f03b92c8268a3d7c372aa363daa0a82c41f6b8bb6510482621605699ebd9fbb52fe9bd7220398c69a3ca4e61fc0d57ea14aa97863209099cda65ee78b621b2220c374ad7ef8daa18589009803936999507165f77db395ff7222c7afb50fa21cfa21c3dcf00d1b3f904a21960c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fcd8370fb8d3622fdf575dd75e6090caba08d1e4c9c1fb69b2b2cf4178c89d41","proof":"f410cf302fc0723650c43d86f1144e4ecfdc19ec701df9e981ec07f12268c12bb21601070eeeaa5ef608f95ce1399715507071afd43dde6e935051c508bbd81990c0fc85db2e75725c510fadcfea56bbf0841248d64277c54b56ff66954fde43a8f70cbcec6ef733fc361df693953733c254bf9020cd20c6abaf61b36b5b6d19eb8d8e365ad31ca900f8be5b8df1af6fb69523d371983e39161273de3fc9d103c846ec1ad553d54eee3f5632aceb8d0e687a3666936c021998f657c042953e06626b261f491ed0c969f58592d93b3c08092883fbe9f4093944b80919e496f1024079b0706addf242e86d6d6f90312053b96c32893b137406965eed10bbe07a157cec954ac8bdb7fcece54a4ca636c9b28d041a77395d873abd4ec8c614b42470ea23d3d4caf119d44c50c04008e7336bc461100b15085c93f6788d1964a86522f853637e7a435692596a16cc265a2365fa837206db60de59bbb645954bd7e473861a5fb41223a628d88d7051d6a5c7cefea41e1195baa03f92b7cc2bf971575e3035fc584cd1705a8a79de586108e1cb3969ccea61d08e56e7c282577d270844bcb5feae8a2c2aadc470042e4ee26f954147d9f665f665f50a2251062d9ebe12260889973d1966db8896df54e9cb7762b0699dd4643d07ddda0ded30bbdf9145a6c8f066fe17046fe7119cfcbe57795cd88700409294dce8c2193557615c9b2b14183b3bfc64d6fe6f7a3681caacfe2b1f5b3072ca2adf706cfb1bcc2f210f755c7d81bbe24c186a32164a4eea0fe83d0d8306a684f750caf5c3f99c8777fb5360b9b4f4b0a99b66b4803f60decdb8714979caa32a72d815d27ef8e4d784e52fae9a4d753124855db7a40e125912dde76be50adf5014d76a1f5824d8301d1f06aa8323af1639ebe8fae1dfc733a439cb041997cab320c80ba690eca8d2da4d03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8240408f3cdb0273a444286651b10cc028a4049dd47e6a64780a153371cb983c","proof":"ac3afd8e67af2eca72292f0fe3bbd91cb1bb9d8b018d9674c4a764c6fbf1697ee80718e7394facad3f93fe11798eea28dc73a8070e08d302e374961bf772571c380b58e18e80b387ff55a160d4383368f03f8deec82951151bd11f51197175240622d73c6ea8d4b64bf7a6a28fdff96343f59e2a573563c83be885baaf81c808a1bc781cae831629e8c8e8f7b997aa43c0e946d03fdff4a357a4640ed18f6b019cdef41c3af52acbaa9964f017e2088f32534fb5e5d5e6a3ae4665ceeac74d07ec9562a86d7e3015e9f6871235b57a23570a9be77ea783fdad31fdd60750cd0632c089a07d96c67b2fa4ac0ef9193574d810160f667d8f0d53b88fe77a94f7006a1a3339f48682259f740f98e5c5fc357173ab50734bd11c6b96b03060077c61b88b36ce6417173f9a352325e42bc0982cd7fe9dad1f91231603e566f290ef0decdb8d0e5e5c7286d65b518ea7be43b5c8e867d57e80d03ebe220bb7ba9b5a3716a27d220833f19ce285335f40fe5f18e81a22e275500c82877c1bd2e119665fb070bb7ff63346126653d8942374d847f0d31294727afe971496ab5e9f6c363ab25e51e5fc2bbfee084737c1c89607e32e14ac872ab380aa1770703f3a498647f2177dfe8e27febc70d58d64202524cf4324c0e6cc9b0ac0169e2cf2e659dd26ac9f19427c7d02fc3a924155c2fad034cd76a02548d1629a0976b157ab3b8e0276b2bd22aee44c4d7e51168ceedceb914485009599665eb5ac6088554fa0141cf87fed2e88f0809690ced6445738887893b2a707453d6230d66925ccc35bc84d4eaff81fa1a1fcdfc2de36eedd80bd5131ed09cdd6e40305c622912fd30ca63560a7c5631db369753848e16735a12c0136b0cc9f4c3a38bf134c0eefc4c30900dc52a2db9d278bd88ee7e956631f594a58cac8484f26e92142d9e8757a6cb60c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a8cf992e701be4b49784aa32b235a30e25adc509e7997877f53a11ec40953c2c","proof":"a8ec0ddbdebfff350006ba0de0cfd96e98fa113cdb827411e8e28d02f656332ec69568dd9c942008793975c0097cbf6be125a0061f64f4237beabbcea3addf0e80938561f8c72f3df61690bc6f8d810aea94b0f8f1b2cbcc3a37ca07f6cc4e191205d197ea68d68219dac2d1616d6513253ed6bbd3cf62f0c6f2b6459f4ea76670d363b1fe39a8d941955d0052f0b12ffb5c36b8645f115d4a3bcfe1bf2622001533c648bae425c47cc75c2fc889f83227c7b29086f7b37ca49afa69b9c7830f40a0ec334f38d1622ed8249881794c48349afd97d7fd1248a6f0d9fbad3c8a0a62c2c2b26b88856915cfc80e1801f0b6a37d1537e80ca9e91f2380d9fbb9b5273c70e5a277f1919ff2bf995aa83fb38408abf63dfae441427c3d73bf31c0204d12ddef713ed445fdf33cf7d7024cbb8f73d83e20ad6f48aa03d9834f2f467f1768c69449095bd97e47aa1ced1a0a4ec3ea3f3970d7f321c6d53a255116c19e2784caf91e634aa8e0772703ad7df18b2d8f3a33b1ebc98d143cd4da412d689a34b6c7ad9636bf474768d01ea8a67be02fdbaa973f7abdcc56ecce520aadf5b4417093e9431ed20229f3a5003cc97533209951660155d5bdbeb64baaf4b4833c22f8dbdadd6cd4f7a3f8c9d1500e440f6731af0617ee96708affce8591485e18159cf6251df0796e666f0526814888ca1dfecef2c95e50bf8c69388b676866406ae456cadb1f6be8e3b6f073d6f9fda6e4d51f8184de3d6e0cf098e6732c0b2d1ab6016fe4733dd71f476e8db368a50f4a57a5262c0fa3d9bc77c29068a3e1c808c882b4fb8c7555a3cc6f693f6f459a914635fb2022fc7e74ed6460920aa78c442f1b8f636232a739114148f600779d0bdd34729ce013aa643ef30b887ba65f0c9eb758f567f1faa4836b40820c7b6df8e2d343244a53197127a7a34382348f05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d43481c558bd27965f5dba0a135e242966f5ccbb0b0c4aba440aa4c28ae16e2c","proof":"a22585855809b1fe7095fabfe63af63acff8d2d93c5a5e39dad43df49bd36e18ba8fce5df27947ca9124d2280eca3bb2eb9253a209d2bf9acea6ec5e8b2dff37b06575fa50342eb4e609810c532381f8089dd6f1265a31dc5d4e835146063e116a177e63862b2c8c41c009fa13eb1b6d0c84bd9de409a45fe4b8ed14308a2d04898c4f82a9c92c96ea5ee1f856b7ca364442399a4bc941959857f46770427b045aefb196bc470a089294ffe36dc62d0b7e64d3705507fdfef09dc314dc05fc05bb4aafd2b5b0812bea6b476dee2b495a4b891f9092dea2bca05f55c6e234a509a6adbf8de3f893a134d3a6c6ba2155f3e62371ed0ca4afdb96180029d905490be6da44a853247ef85398bc32d5d40a2f9d72387846081969213836c4ad44222454daeb46d9312dadf0bf9cf8d3941647f67d613a2c1321de0409f462a70a9c305cd7e4f52b26243e5b67ed378a02d384ee411091162b3ca5ede837f04e99cc7fc00732e9ae6985ccb1f3fa5bdcfd0c73da48fc1cc0ac842fe880660d9436ea6478e5d93751ee84b20f466016d35d9ef999193f12dc737287868af9a298113024aceb8ffee627a73e05894ad30ffd249148438277c4463791534274323eaa842906ac8074cbf11ef1977ac0b88e98be3a92e46aa09234ffb5ae3db459bb6a3418e602e476c148366b167bab73a982098f4deeb0fea7dd4de3bd513f143b10c65fc84eae39685b750a654d2d1d8289c83fd9ff169402e202784b79e14743dff60d90164a6d2d064514b8f1b049e397742b916eb505a4f0721f492c43a485d7e67b4647b4a27907119a4f8c73a9e62d845893c2035ec9c126a55a7ede37b6d53920569af487f7fcc23485593e69f7e879342e73eed481ee4ea227a96013086fcf0a09847124adf240581c8fed1e0985aa7c63c836030ddbd27a872afa56e18d0201"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d43a2284368cbac0978d2c98e820389138e901d4cf4ac8f70a9abc16385a9e6e","proof":"cacb84437a685eb13c4aff7aae035a603d4a8e5dce897281677c7a3d471322691038bc915c3bed395fff8564cb5c95f5127c64f520bb86cab27595367be8f03640f806deefd7f85486b8452360f40115800398526183943a3f1365db4035495586662c7f4453940e09415dded2d3e66ea5118df3975ba652d095ec45760b3e1f41bb1340089d4002791217c88f4e21cdf0bd221eee32741f8e891ea77ef9b50305d5ff21ed02d7c18a6e20e33030581e3651c27ee54be197630baa716c03600a02fde1b3fd20058d7e8125861088d5bd47e6b4540454ed0ee8fe733665da87069cd0803316f5464a661d505b096fa91173fd83ab988be5675568af381ece5e728663e269ea59755cc11a1f377d5aa64964f54b688ec5c65fab2daf123cf2262c7e5d6c056dbc480e8bf52ba1e7f178ab1fcf6c796d6b9f1170dac3077f256e03800d02efffa6de1ccc4884a308e650956654e3a6429e27e94dfeb32c26889c3f4a303e6ac4b9aa08768a023038f4b83e6e7a39fc798ae93f69afa046b0d1c25cc40699506eed988ad569b75e9208918c5565b91dc9ee88beb0949dbb5024fa53762467d21db6c0463dfec3c6f90cdd4ad97e2b9fd9c26353a6a7995c85fdde16be0cddb90c09e1279c12365ed1d2893dc68349819ccaf2ae8ef8535cfeaf920e4a75a1036a782de46fb1b81387fa777d86f1551240df664761bb7c484105df4d8073853e4b9d572614bc80a2943f3edb6b133b15769f4a6c7db609e18dd8d52e44a971cda4e1be96f367d599722c8ac474dc32442b5e090b0c1f8cfb1313e95266e86b4881d698e3da0a1607850e4d62c8505f9a81474fd3637a0f60c1ace57419008466485ca2db6d09573ae3019db8d66500131e799cb5b85b5eba4d20d40f6b7dc6cc8078f757917aa8387423fa77d7f855861c1a33d0dfb3cc922da97e0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1c1a0fe1afee95abf2f32941d709bfb3ccdbd867be246edb1fdb5b70fbb9120a","proof":"8e605387a74313a4736d97a026d83503530f3ab68597e57304ce7cc1b1f39b6370ad9ece28a2e12156fdeed4ab84c8dc9c88ae2ea64f21649d4e706b82e545463a7a5f39ecabea441bff0fbf8b4daa02d5fed50ed8af7ac31cc9dd966531b45368e9f79aef7ee99131636063dd2340283c641181dd2f03ee83139f691342886d28b594f54ab354c88af683a2ee3e6e0d28c2314730f9e6d35899db56f849f00e8fa8ee79e875cb6dd59fd7020b88d0b43da879b57eee087cdfa8ea386d663805eb460535bf3b9e99548c717bce6bff4f8cbca2d5107afb9d5dac8ed57ee4cd0220027b8759ac1624c2b005425f47c33c9a7581fbe22cd20f2e19ea7f59042472ce90819f1ea5028d5f6b0702b3b6fa0ec0575809f867c69ca29b600b73e62b6ddc5a0ad9846de769a85d9050c5f259485a2a4247e4568d2caebf843b6b564255e6832aa98c279b94893dfebfb35eed7da8544b985900affcb73583f86be0120f364141fb400cb7d068c969408ce20bf842b025ca397fc2253cfc0cfdf85b910ac0d3f2850ec227bc1d3dbe4a1516cdfa0496bf0a9f8f05a43f93971105558f4f36503e1196aecc9869a5903a01d3b85e416bd04100a989bfd8cf2780596ab07a1cd4a80480e2f00163153979ea3aafaf9660e9555ea7450d5c2a95ce7bf4a739aa1ec394befab87cbf8930f6daa3ed25e111046b07c58049d84574669fd0ec5b0875d3f8ddfd84aef32ff405b1a87c5a895f2505871f6d4323bc92f8518c4d66d682bbe1190d6fc753d35699f68f1b48baf39b3db21a956c0a03fddbf453486cbae5becf6478426863f43643c813617c9c61fb764f0016e7f103044e42b26d05384336358382f2c1d311a4ced8b62d34d6d4afea20d18a73bf976f8c8b21f108f4d7b86b8a0a91c81930affdf279254d8206fc2fe40345656d6b9789f17ada09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4cb2f24a31b615f955e0af121a1430b530b75c6787dca6d4101030cb3ebacb6a","proof":"98b9ffd76dd2779f1a8a1719a94313fbe42b0f553e47325fe94adf16f7b7ac3b881441c6b2d64805ed548655338e053b0e0af0405f8b7d8d66647d948674b00da22d516d665395db5dadfa92caf7ea898ecf47b98126879ef868e13f07a74956585c74c78a7c199c8afbddde53a3157e2ff2f07c25861c3020d49f318554ab2d9061a3c23e9effa8bdc8527ea749eaa7187ba60572ccc43f03f4530c2f890c06dde810e9bb0f3697772c4095018d537748e4168fd7e6ed41a890debefe801a0351ca8dd5093c425dbe1198be0430db808bb18999a399c7f6ca76d913cec62b095434cee49a3ffbf352a0016593cb1dc6a8155ee32565c75f2bdfddbb7d61bf7df2a7529359294368e900da1c53a6be76d6908a608b79fb09bedc17d7eb9a277ae298df8f6cda23048d2e671309d139572708114cc2d24f87ab3f243508ad6247d86b8b1aadae79e9a2212e85f72e78d89c8d5d642075ae45bdfab65f4c68dc187ab4a7eb1b8e9b61f195810d5e4c95d55e353267dec5c1981c6933a79267ab2422bea4bcc290c723da7e3d8b215960fa913daaa983579b34b973a9e7e2fcdf267eef7a61d685d8e51ea24d6052cbf2fa561b6ff6d14deaa6652d3b2f6e584b7d7c7dcd972b557ae3b7270162b325443877c987e45bc9f5fa7a71fabd32dbaf2fa66c3726c62301d41024202e898397b370157d718f5ac336baba140c03655c0708480a177e305c3ea6fddbca35bcfe35b11d202656f7f35e9e0f66403ebf7b7580cd4c3c49abb5eedf849ec1748dd639b6fb3780140a943835089659fe62372cced0974b9956725a6a66f755e5caf5fee6c539797fea808be9ce2780ca0b4656f7829e0ac7e3c20c4ba3d8c73fc786982fbeac5e4e7e5a0b8c8b8a3d69270f011efbb6a53f4c1f2ae9d23e34910e60972860bcc15bf0d86ab20fff4255caff0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4e2f1fe0f29e6fff9fb46718542cf63fba7af7845a3b149b7e056fab1c26a643","proof":"08879c7b9f16e9d52e0fc7a1d23825beb345a976eed090f22a093da94567594f88a5a07438b99c7c669110b4fb7dca85ee90506d462e88c449b028a325a3cf3140d968b7d9847963c2667ec299a55870469c9bbe06de3d1517e370dbbf67a56b767a85cef4ee14dab5cde1461553c02d4462cc5bf0dcb3bc5fac2ab7559b5358840340b7eb258e7b8928109d8b80d4944d806f21df544765db1fd7f7d3cdb3040c137417adb9b5319474dee6130a5e17f18dabb208065387ad57bbf1ea553e0289a845735661d53a94abc5b920c346f0ade8ca34a5bc8a624f1acbba3d1a18019e53d8f1079fdbc89048a13e9827fab3049add8d97ea9d9ace9a1b7147d9ae53b0160a5d0f8a8ae708ec76124e3ea2776b125a45ac6d4337f889a66843d5364ab6530b8dcd846a47d368bbcaa10a9d61e94bc2a8a2bf8fced8ea031291276b4632dce688abfa2a480eddcf28ef053525d009cfba0f94becbcb30eca8768528705a696eb3cdf4f45f7a04d345bcf2ab3acbb809dc2caa9418cd2622e03b366845a47966570578d6888d9597eddbe13a9cc4a93e32483f57f3fad07f6d96960d06982ddfdc68cb104e3b8308a9dde014fd7bd17dd0c56b7ba49c055d27c2dbd05d6a9f5390d77e3bf59e8794373ee86a9bd4aa51dd5937f69c5bc66b648543d32656f6fbfc193d79d57c64771feaf051f1b057a9235c3a173003314aaa7e9ccd6d2812cb4a24f7c72de7e284c5cc5500e72e1365c3e29da431302da58aa6280a1592e3e60a0f83084009abe912bd698bd290f016431dbd300b00f862ae17e73d152e5d181dbe4fe4bedc48821bac301347f26e2fb7124af48422cc8af1315fa23b0d5e9028ae25ff2b87d5822a060866211ca153518e0ed9ecbada76706e92cd050286e057aac4eddc7e86ea442701bded0c41fd4630c5b7866ee6646dd456f005"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8079ed6b425746ccce3f3a665c2e15d2e6ab8c74dc25ce101cd98f1c8ff9a70a","proof":"823ad061907bcc210456fa736c98469ece52e2f5627efe442afaa285095ea82f3af30ffd3c2d452459c0bc9c27f5a0127bf8a8524830c4701565bf815fefd73bf0874341b4475f01784e63b40cbe3e0c735ad58a489785129cf1db4b3f65ee39a6f571506a7925c809718f9a88f6044034b3ab1e77aa841f659a22965135766694f7291d79690206a41b42f6d54e4991ec482059cdfbf799da0828df6084260e648c5b6d8403d6b24eb6c6742ccacdf25f4bfc5065e1f938c0dd03a4faf09c087ed3cbaa81f6b76c0496876cb1570477ac35c7bec0636348269f271375e99c0464e32e54f298cecb2db89af250f156cdd11e91d1c85be412a72a4312bee3b6534ac9c8710524755f2b63a71c661064543e6e01d4ee94a339f02d7ee41e691e4e2a7d74c63b74a4925a8c6da3c54e6b015573e901e067e0d35c0e13e2cd155555167978efe3723b4a64aa6bd0db7cac7c54e35fd50c0bcc2cb6205b8563e47322b4d7ed59e0df2e4fa93d417e4105d464ce3462056cd716cc60731d3fcb3a8c42fe360609ab404e83fd0a9b5f1b9393851ce049ade374742a80b140564088f24962bac0c09d24c759d641641d0c9f4bcd00fa4bb9bea41727229bf43811fc157b18a84b8bba5abd3cf4589c5d8d104d3cc5c2a3dcfc9610aa8a12b60759230a1a34fc63d74b418468708a49c8f28b1848b69f0f839f886db778adc27510fe6f5ad831bc84cbb0c3d11b66242a08cca67d92a9e241cd22481c3044dc49daabc53204864aeba46bd574e08d18ec2afbe66dc6d01a1faeb1162c0863c573ad79d112325416d551ac2a1ae74e58d3c26ac54b12b8e6de1f65335d52ffad426934ac6d93bfabb73aa60dfc19202b81f07bb10aad5f9190077119e8fee851a98f5b2b07a1d29252168a33b6f714df38f50b75422a944e85d361f1d762812559643dbe03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6c06203a227853306c3acd6e55998115efc467e5b122351dd94a986e7a629003","proof":"e644f1ffe3542e705bf9067ce1de8681f22bf2fd924b352cfdb056dec0d98376e0990f4359c2a65bdc903c34af4bdd78901cabca0159952158a6f284fb843f67f63a8da40f4daf96f8a4c9630264047b3dfb43d6e9aefc749151251442692427caf71cb0883630f15a4efc726c8816d9391041c0e25053bc63f4eafdfd77f763172890187ee3122240a456b1ac0768e0f27f90135ccd21310dd6b410090f460493a20b16e756a2a24742d85b94296a185f5fa1888360bd9417ad06eed59325053eab09bf6e199f4101df342bc0bd581b0adf6c03ffb9205b0a0fb01b046a340982a3140ac3358a6412ed19f22c8119897f46db2598b6532d034fdd2839324772d4101db3e4dbf4773e614d1224e0a4ef8f93a29886e2cfdcaf31ad06b9a1085b78e94ab8522abf173752c450166c35bfc3ba65fa88c8d48bfb330e09486be067f87eee8be385ab9c30d0c49015efc5457e3fd7b9e89bbec8b23add41efc64532b4ec6b71f8dad88c2392a44239a3d16ae6fdaa1cb1552d112c290bfafb53d10148105062d43977917fb6117d84e47d1dcfbb0392985706753db4cb17e09752141010b16a81a2a9eb2b6ed67a4ecb495d839e0d2eb15278c3cf1ba6795ec2c8101e6dc1b0f05e429f6b839e9e6d0b051453594bf95be4d3cc7448bdce8706735664863bf37ade4be9c8940dc2017eadf8253f9d6ea3153f6552690ea1d9ec406ff8f41bf8d46eeac1c1d8e73e1487e8ca2f4a34ac8419428f105360cd3150d96b5ed27ff9419af02cca91896d8550a5770a91763166cd02022bde2f512c913f67925afe85708553d2b01aa4354202d468e659dc13f2911e044cd52256f4a7970d749bc17b3924900594c760b52b168a9938592925e817bbebd71413032699fa05e8751211fc77039b64e013066577618d4f1cdfc2b9e226e455148c522c654a04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"049c50276119ebd02c0f91e098739b79d5098966b4a9921f9a01c9c875b7702b","proof":"baccd1d24e43a3be918bf95ad8d35d3e88376935227c2c94a6f80db2fe22735aa0b3b9439c35ddb40e423cd6d4f0c281439036c96a69437c2406cb6befd1f440a8e2e0eb0257e97021956eaeac584fd187068829d47745efbea2addcec1bd7455cf8eecd92b6570f0ee9545ce957722436b1c659b2a81549e22af752f0a6ba5dccad1f89db5e2d2f0e3cfc2f117cd6a15c0876ae25dd7f7e294bb49a96fa0b09a535a2fb8fe73b5e9c037d175bb1cc51abc6429fe7091d60ad514a9c620d210c429ef8eb2eae3ad23420d078e8159f453a771ac669bbc24234251b0230898f02603aa7b14435e3dba02a3364ff04703e35b9131bdef78aec52de54d4d7f866645e9cfe5244d52672e55f77088e85f4efbe2934b0a42c11d24c77b278919cee5f22c0db5b7d193bf1a2ee94add193e14820e10e947a693fd6c2b6543a4bdbd10900a385ec4074759c24932c4cc6ca19246ddec16a77854c36bcd27f55a3d0e3336082fa13b3e40501a02c9531728fe9bb9d3efd99b9508fe688fb54c7fdd1ff2a62d7a82f1eadd0aea56af197e28534474d1ff56d555a211a48a4bef3196f7d72241c12d9af8ab3114499059696fc68e1380b5458040a9890fd8f1251692f107f2cd985bbca48d085b45c89c65ed4dac6a7e359ae11f38286ac295892c4d526606a9a4cd34a0bd9b1283ddc77320c737045b75d9dd63df48fd35ed5d1007e36749aae0c92a1ea68f116713b8c10d2ff596cce912e37e7a5d86fea065b78ac585a40f35abcfd86c9dbce22fd78dfc778e0be6f0e13326dc6216d1c9a2cae61e41e9cbda415a3d48287b70c29b3cc8b977ae63d7960a3d36a99873ed23924a8501d66743634175b7a8560b065cc7135fdbd210baf492c0a85269fb809d6257de207d4693ae2213cfdb60e024916d8169b3e87398286931b79fffa32588fa996e501"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"54457d7d22bdffae16e478f987d3af112ef51fe56234d9090dffb78840bbd16b","proof":"84593519809e62c8ed6338975d155295cff13cfa8295ea4a8ef4b0ab7916b45766edc945db2cc92cf04b48da70d8a89e5447c3de7058779464bc542345282128aaba067ecbfe5fa6f650cb88e0e96e220e1d1e3bdc99c2d583958d581a283e1456d885f60c78573027b4ad05f44299a0244f5f25796593a440978c92df4c9a7b450beb3e2952a938d8b9cc9ba151c4c27780825c7922dd18fc10dff8b274480c7fd0b56c919a57703972f8cffcfa20c9198451949dd73a01e78c4c4db758230144bc8c5c0cc56dbe3001e8fa40e3f5979c345ad279f313eff6b049fd52baea02b0315fcbb37b20267a82849f5ee692ce236d8d1ce9a4192959a50b64128f127d842cbd31c7e294e738ca5f9d2747cd6ce14d5fe87bbbc651d7b0a1ef8d6b82273e73be3acff95195bc32e5e20669fefe58202b5ea10337e216b3f0c3e2117f773065f2d08ad7df6d83db95a5bbd4b44c5279919e14e0e722f5200e9e533544402ca262a0ee0d76f2f9eed3b0fc38152241755846e631173411731fb8aa20c9169eb9fea42776b5894ff1de8c3e9b7b9aa7d8862a08437549f55ad016ec1a30786495b8706eb8382201559fb64b139a337ee1cdde24f8c083783f538f798c996edce49cea8f2a2bf0a1f646ea060dfccaeccf1345e2d8348971e07b7eed00e71fecfa14767f9f698e02035e61911f07a19eb969db9417dfce1d03502f6e4aa17d1c958f3d7f93e1ec8aedcbb14816ab29aa1437792e7ea77144ca62f1b4032546c8d1cedc71fcf21c7fd4136eb1c2e5b43efa553ff096d9d8e2750db4318c36011ca3b6dbcfecb3eab62a01c74fc1f27b4649787d6804ef57f8dfe08e3453a4241955bafe13258d1b2bd48e1be655601dfb3a2156006723bd177a78decac8380b8f621ef382918ec353c6f5d41938c434fdbe97464e038a29e3a6acdae22b3101"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8e33694015d10b7d22065a99ae53923f9b4a40fcd8aa61a26ab98e7dff608d7a","proof":"5e4d9cbef9e463a481de5fde8b07d3570cf6a84608ea4c895b8a42f52bd3493c4a088308cd0e9c644ab3afaa77a7a4a0d33d0c8dc7a6e18450d5026c2ec066634e214642d3135d6415ef9a183c46d0e04bc8caab51edcf1a7a2a4b4fa20c4b62c855c08b8870855f753e5894c1b1c4194afda7123c59a9733dd20b130c7e501726290ca84ad3952cc64b0875daad8f1e444db6a400945a1a45d6265dddd7e10a605cce0ffbc4908fa138b8cfdb31fb73543920f4b5be6b45e914015f5f20c90934505f99755d8d0f3b904a4693a598da20585040eb91617886fcdf91fd73580e7a55c922ad0b13f4f8eb10dd8b1d1eaaa31e52b29bb45a4de0c8995202a81330762ff1407a9a93d23f996146eac25933a9dd2257014f6d03c340131ce532874c8806076c518e9224b85cca81490bc070d7783d8e611938cd94921272eea45601462ca10761d25775fa80e8fdc8b6daf90a2966a733dff679a734d801f282ca393081c7a198902e415f60bdbe0171a84620bdd04b461a92f2c51679e035c6646f18f0803015c345ddf658f4093a2de83361da9b2c59e468aef372d49df896290cec1f4729ea6e91658493f8d6b41e91f4875fcbf416b6a8fc86da47e8f1e87036bea4d6ee6b4fd001bad210556b7296a926c47e8c45cfc470889cbab3bd21b3761c3bf22af7a9173a1ca0535d5f28c42e7dfcd7be19830ddefeaf6570393b9a4ac4528fa4e6016a98fe6c72e9babf1e466c3954057579aeebc28be92a52ddeb73f8b10729c6bbac3c8f89cd7c81425be6121fe9ad006179b17cbab9987e5f1c1e7e0b562433254be9deb294973dc4c0991c2fa015ec10c6b8f1aba55e6d1ddf276e75870806e5dc965bdde19167cb64e696556b7dab9460bcd46f4fece1f82607022abb16299b3b5b965c73a79c456437da89a6ae86ca2e0927f7e3c6a633f807"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"02852de628fcf82e3c965ae4d0d31024e9c9a36ff8a37f2c189d5728fb144252","proof":"f2c0b4eb715978a457c8397bec0c1c1739048913967b14d5c66c742309d90653e8e9c3cfb47999ffe7ecc8a4cafbdee1b6b838ed1d154f3e33c89a935437491742dfa02a93da2867b241e53e3b1268dee57c90c0da73503de791a872c2c16d355690f735e6d2ebe9a1e0cbf023efc28dd0679604e73c488cb5cbc8359704bd17c9a36aadce39a6bdd26918efad9a6338e745dd4df92b7f1b5fda49d8bc56200685dcf1ae66585ef4337ba706cf45d4b782d5fab22751bf431343f026854bcd0b20cfacb0b02cac49f35f99ce15464a149dff73ff222657d87f811ca1e3af7d0804ddbfc7f5f2b178e7810d2191ce101203d3d4fe8b51971cbb5e3488a96b0978f0f70fcfc4a084ec25ad7b36237f950a7ab62c6b30f0eff879b9df5d9f7d84269684ac9a888e680cbd834cf8b792b09f5e8cee6a3704c19f498d51257e9fc1127e27fa6fa2daffb7cd79daa999ccdb9a8d352c14faf71cc667be5628cad63e34440bc7a8730d895d4bd3732067cf1360571d57dcbae4290fa50622d1cd35363c865f83ac3673401c0dfe610811dff9ba30b77b9e527e27734d9635d608adbc0020465de7f3ceaf86cffa0236d05ef45710c7e7848bc534ad5fa0522b2f1f617f8cc071df9850b00f1c863702b8059667b56f9536e187d99a618f5c00c925421768305c521fc6922c5da5274743b39b7e8bb1964dd152e196d73b58700cd04a2fc453c2bb38024bdbf74e6cc5dbf5e40db22890d224d1f273cfa911c22ca8933f1adf255ab98c797c0ba29ed090fac37dad00d0a5f374ed935e5e0e26fcd6c8384219eb0528215e399ebe5de191b9771dce2e8db6a252883285c5c9b7ab18e44bd408d8a424f0a94765a9c01b07712d46d0e4987efe1419600e98e0fc565d8d028210efcc663d9a9f79805391dc51bb634dd52a19efa15f276c443a5ad5bef20b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"804e0c3136fa8f7b0213a959032a8d754b4f4c3d2933f5b1dab8d520499def47","proof":"8e51619a3c1365a6299b6036280285f3363f0a34f58f96ff3b1c408955e84b4254d1e6e9c0a194700ee081f072931c9a4d409315a36563cff14ccb0bf19f1d0422d72525bc547d7b7485eb7a0d3118d7a2dfaeb3c01dfdf5c3d8f5be3c27f55f2e0292fb1f9b853cb2058f510080b2e0e533e4d29563d0effb391f4e835bb035f85bdf3b6f24b97fde77a96be83cb02c572fd622fa1ffae11f75aee83dd7080711c70b8c425bb3a8313c3b556d492f5ec56cfbf883759ec0ae4f1a8c169bef0946542b999abccdd5ee7ff11c21744cfa5aa3e134fc8b92879347b97de05dd90b34fbf0564a479930cfc7b4d4eae1e7db0ca9f4bc7c3a404cafe905ecfab3e8614a87794e8f5d356dccb01725831fafe9c71ea0209169b68ef472cb850fee5f5b166aae4bbb0f3c76db0878f9fa8f1a767402aac27b80dd560e3b2e235ddf334dc0b314d66ddb64d700e051cd257e894c64e5ae90abaf3d995bf0d9887a4eac714e7752111edb853d2787501fe124faf1cc6d5e73bb80125c8a1958aea1cc103eb810e7459b2c484d0e0c106c737c68c31d50b8926040814dcefd206ed5a841690cce17b7dcff090bdb0b7b1591beb0f695ea85889fed2a104a8f972cc1b1447dc81837337f085c969577a0e06b1733af1623663aaf55477e9e5477f3f09a6e0ca617b831c5718ac0d3955012058354d923b3590a02e7c23afa783c71b8b55b195ebfd6be798a75b5847b97ad8549da62fd543b344293a7c76962ac506d6938553068e1e63a5d2a496f2e1ac65ef68d5b6374c0adb3f5b238b569d2ff6ab51378b4148b4ced1e4f62ae3291dfaea7be70db1da3b53b3b8442c053dc1e89408b2471e8de470a28e5bfbe64cae9cd22f25ec22e0527d793c06bd1a66e1cd1568f0aacbc99df12cfb518479d72c8b7f07b7db3eb94801207caae0edcccce31811a03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ae0b8db21011d8fc2092c06b65de34e618efe0a8148cacfde6c599c379250469","proof":"5e8c86e462f0d19bdd9afc16fcfc322087213450552bf7db0f8b01f414b38b2842ebb13c295ce4502610f03e32d3ba2fb823219ae26c095c1b22f7c2868c264b0a0909ac0f0322d23ccc48e539f80e67d1b239e607311aff2ee0151dd8ac8011dcc5e70e5ddaa70c129294f4cd5babf0348f1261a784318bc1595aa0a82f793178dc0e6ca586ab2fe74fd7d372e1396994d63e9a3719dcd0025246f27270750b96f3bc9dc2ba43efafc9d4b8599e299a0966438dbc78d24868aaf4b7c581e90c9d17c5345861fc66c9e718c0152eea9b3a3eccdd4d457c59cd6387041193d907c2dd625154431385c5f90a18eba817e4df43df1fec11aa949570ad568d9e3634902b43fcaecef959c427694377cbfc64e407cf3b80d29a419435a624eb434e1f8abd98a056f5f626b44dfe69c77bdf551b733b3fa770866f7cc8014fb6ae640a06bed90fa2d15d62d48f93f4656ece2a385874981f17d5f5a7b2b900b5c129373caf420db21e04e694e81cf29bbe472403504061d9b7bbe6029e699b07d0351bf0f4092f69b051eda2f80d8360a5c2b81a99ab8c4d7572f4568764f67f03684b02a93430ad055091c4c64ff46df69c34046c89f9f5aad8ef5b7d7b4b02ef71732c807e21fd5dde45e09a7d76a141d2cae0e3fce21f2a75cd58589b8cea985c6bfa5b4de92a09df86d313bace56541d8adb4d5f4ca26e30088773d8eebbf66f30344fe2ed492dcf94f9c1a5d52031457e76714114a319e640da36a00e63936c418ce22359080bf5a6344b10daf9a40172cef2f8138a7f41ddea91a57d4b57e15a402fb5e24837757311c93eb5cf03b8e880cd1b2e25ed623f614cb1ee4bb42562043edf6842641813e9d837be02e7e79037b0b4a2eabc4589d689abbe669c1806e13917251133bc48a36400ee67e93326e30af5fc746acc57adbae2d016237f0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ce794703bff9d005bddbfd509955da2be2bc7bfb76e75ff6773b201a10789e24","proof":"1eb2de5bdf36f582427391138b1b76bd47bbcdbeb2160f730e92b4cbecacf75de07507b399b0226ed8870cc55c46e72a17f1f744e0064c2a6493b4086661892d487bacdfa973b7af759fbc13a123cb7e294624b4f77b67c668cfdeda18f4ee7fec1963c7f74645fb997a983fa3aa4648af5a4792b9d3284eb5e00e7352f3f5227928afca942da3a04996d923bbdd1db429f6c060ee03df2fe7107c7d45bd340e3f8ff0b1f7cfb8e03276687b340168799f52d535a423b35e18b1fae6b9a67c0138344a8f4ec7d8c3eff6c2b1d8a617a0b9de4695bf7301ae59703f3a6c631e0d087c62269dbb5f11a580c1b4abd9fdb132023bbc6c1e620f0983e01944d6312620744d8fea463669a212b8d05e530e350d6ccb6fd3be1f0a005259d40cc60a22a8b9a5fcc0640032fd23f0ef49fd3af5cf8680e9d61ac8c1b2552aee774852145870c0e221a97875cfd6ad9dca1c0ed45025d90b2301f160760bfeab280d90183c340debe394277f0ab06f1c7dcb9e4dae3ed91724f0dd767e4c8c3ee50cc366d2ef2986da1581af7a9c39cca120359d4290d47ad4580717e5354c6329af174c9c577b6b868a3e05daee256e58afcac539fd7920c5d82393b828ba5678384f1b223a3f3e768037a4ca7130640359444feb1609460970afa3325f80fb5b3dc426ea372057200ebddb0f95ebf76f684eac1896c143c77fed5dd0e2a1aacde8a50b6264a9626cb1a979dcdfac25dfa05f5d0c7f2bf58d25fc1b0246508d4989813d060e2aaaf3e520bfde44a681e7f538b5bafbbda40eb55dd25fe80b354d75214fe6e3b821faa6982a31a341555cc5e5dc7c2c462bb8fa4c8b7c19ad3524d57a286059724453a544bdfb9586732d3821dcaa9fc0fa8179ff2109a018fbd996130b1a89678e0976f5abcf7bdc7ad40f819f8225dff00586283e5e5fb130e6b91f08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f2a6b05df4897eb43dd07210282630e4f500bfaf3c42e980e6b5f63c748f7c73","proof":"4093c2be64fe7a76f416570cb808d8a03d92d9dee7c9d9180333afc854f4142df217a0ffb6f34d971196635a46d6bd11d30ab3ffd1b0ca4e48d161a35347781da690b5403ab8161af331211727450a66f8ef75a311ecffefe82fbbbd5fbc4276963b7a38ea2fc8978f1b157f310341bb39c09aa2a72ddc376fac480432e13967861111cb01358976d2692c7cb325c326cd4daedf4c556ea8eac8bf31576b82014d40bc81d0b715688ee952c3e6cb4c2561bee3359c90c362b57a7f76d415040028dd90d40ac088d7ea352a575f9f14cafd40c8dc7ef792fa0ea8f52834610d072c411b3b9b2f552a15b79ec27b657ef8afd4e44a9603b0a4ca10b602aea5bf03e8f1dec3fa7f4bb569e9f26cb34ca9b19a04738151f0e5df6f4a723c4e6aff76c46d1d4302b1d830e761a9cb3012c7dedbe55754ff7ec5b4f598801899e44c7b0a2bccbe8d9effcb15834ba3eea915a3529ecc66a5c06a4fa88bec03f32d6a6dba6cef69662bfdd2fd374eaf30bffeab39666f4dd9943591d5dae19f582e1f417c02f13aa6910a4d14fb37e8f21fdd9c627ef07eb06815201af29e4739c34a0c2eed74d838f059d78c3d4724ffc70387b8555762eaf1eba38d578c11c554ec60d83ee662b503e2bff2dc0f638b20ad06e0649f9ad8951724d9e5276e96281e2d4860d8841ad81c6cafd70dcbf91efad4c8d24c60ed58f331ca027c1b7ad33f2d820d4b9c7fc382a3c21bb2546a112a15336a05ab824eb13780bd681d6a1690180c50f65b3a4eb95e1413088093d06b537171b71e82336800f252f31c633f6b0b9ab76b625642205ca62d079a0ca79ece108d0785646c35ee1275c4b176086707ea5260deb54d8d00e64e34415ab7215a4bb702547a05c49e3a8aa742d44a8f05e27f50649d17c2092578e409eaee881099da5889b05291b8c17fafed1cab0e0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"663a89a32dbc154464ee431966099688b93a15a538de175d1faf44bfc79bcf23","proof":"ac01c64b7c86904874897c22eded8b2be70b01df4e0901d34fffaf9bc10b8812b461821004f2cec6aceb54d28b83aca796f175f8d30087becbca85972766a7028c6d37d466e8d02ff5c0925a04e8eb0b184d9664bd14611c87d4bf1277ddd46680a9e629c8cb2f724d38bf66ef26f6c6285391736b682eb404a03b2b585a151458a93b739925d4804221adc4c7b46308378e3524aab35d994b087005544f1a0b8f53c694202c5bda04fdd4cfffb6c99913a621bdf967d918dd6bcb22629c0b0ebfa7785e4d864a2cdd795c8b332c0dbfffe8c13a6d76246ca7e07abc3f340e0f28eca9361b558ec8103042ac9129b45716290d1c396b190e28db57988563f67918065549439a9ea223d4430662c2076093b8d34a745c8d6b6cde095b7f6ee950acbf7a46f460bb21ee1f3cb469acbf1d56de7d1a11d55a98b9de870336aa73179270221fe32446547236558d8f016e3f669b8d5715f2b48e340ef6358e8804446a5cff2ea939dd7baa10e93ee9b8e688448babc050843dbbf004c145c9aa7430d85f00e6792f05224f4b94c7111de81a05f47b367f495b7a6180df2e877f844f803750c717eef039d9f5c9f7e43f917f5dd84c695bd3461b9f27de66d7a3e81380418009ab3b4d8f83f3cf127b095bc2de2f759e29cf115035e9764202364670ecc3a284529c1ff818f7bc7842a83fa487d546747770c0bb4aa0e1111ce015413e67555e76c137ed2762c2384b4969e214156a85bb3ef6e1f89b04a07c18950806f2561698f58818baeb313d4ef09adddd2d6389c19899480a6055f1ce677e0a269abcd210d81c64c0aa50204123bbebfbc3341a2134c7edae696dfab5228d5426430f2b47ba4f6394e14e2fddddbe3f18ce7b31ed2a51a474b80096e54f660491a70709bc43e18569203a78cdd420dbe5a64ab947577ff617edd2c591f0900d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"68b45d51aab3cfebe61c21683d7f2002d192a58e4c8c71abe2cd1b6f2df8ee24","proof":"166c8eefe37c781b4bbd62b21f38ff6b5b8f11a4d786057df009d3e083b4140d34a198a3e2bc74fadec36ff68d23755815fd0d727ccae6b6a1c7d1f2779bcf791c9ddcee69d26cf5accacea90e9c6fbafe29146e3c796310a99325fe1b7ee95e4c8f4aea52d275951f5af22eb68ed9dbfc84778f6d613d2fafd688399e37fb7f2be71c33533e16f4890a6d2b53806eb4b47cd63dad769b550993461bf5719d002a103da99acaa42f0149b965355de189f2db980e6b0f271c24848625ad810807a0d703b3f253a9acc7e152f2a749e701b91dc735fea8b3d106c80838175afb03e62d8d7d6680cb22f70b2cccf9d62cb179ed8b42f313446972f2c73514dc0b36a0e0d3928253a990b8f624e3610e42420305a53d4b91502ba53aa68d7b311a06c0327c64d729e0eedc1af9108f085d6f112aea85b3c7ab09f4f2ba4a7472ee5df8ca97bd2f05a0eea729bee32b7dfccc7d4f6dfcf809242c0d170ce9da015b269c34e0de37129fa05a5b3882425514c2dc3b82623aac6d4e02427cd5e9b583261ccd6616e3d470437760250208bceeec81b8e2607d4c932ad564849c5285127dc680afff3c2692a4bbc7a3af46a69f2aa93f985bbd357957bd7d5aaab8636c3008f968cbc9927f34be404dcc372458e3251bba2d18b2f62707d314d3ee0a077c408fe9c0a4a5f5de46789362639d1d90433418f7dadebb7f80959bc4686b9770fc6b931bda862465c7c0ac2988d74c4665d4bf3e0bb1771c610fc4ac4922dd1e5e0e1c1950b03b1e44c323abb80d51a3c1d95e13ea66f287aa894afc1bd05e4a2e838e7665cfa4d00bed05e078d50b8dc69be26a4737f404c2d4efdd4b4b1576ec2f40c585db04645880b826f26c8e34adeaa3b3e3393e1c29e3a3dcf1823d0c8c6dcf777d4bdcdecb02d67bb73f05f2cd3c9dfbadd9a10ef4e9dd1f7af6c901"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0ccb07ba1f0034a84aa214475c45ce58408335d32f79b474ea16992c2b0bf300","proof":"f8269a0900832a57d4e0bac15a7794e83d9186780df3cbfdb6ea4eab59f2e463826035c60804e299cf1071e97b8378e56b00c86c920fb2b6560bcc101c15e87e96c7a7baf1b924be13ace730d7e7b8a2b23848a1a402218e8feeff5469cd3b7c3c03858bc54e55cad958d98aa176038b1dfefb03b8ec2d5af3ceb509de27fa32bd9bf18ba8df75465d434a6038c85acf8982558f8d7a79013072b96758f57d03780b16e0b9a4922623ab285a0e36d370b88544f83045ed39fbde314c41a3aa093fc3c0e871c06adcdadd221cec9951479421d242113ead9d7b11de478b60920024ccd7fdc8865668169764bdbc16554ffce10c58111fd94416873bbaf9f1c20d42c9856576b4b28402578919ac1dc36f9c262282aa060049491470bb65531970aa424b429b57f5431b0b9891fc1a04d34cefd4d124c79602acf0c40c5e3cb96e9c988757187517ea2293ddc43d22dc594d01bae37c832c8fb0c412077ca8995936f161832da44b8d6402cf1236defa0624c49e0a9f64baa66fc8137cbf48985d645350f5cd23eb12ad444878fd129070bb7a44af07f7b76b60132af70937fc6f76862f7ed5a595c3bbb319792fd9812d72e014a0444cb8704732b6453f6dd26734ba1e68dee57e4e869c4ba1957a249e0d508705317634631cce00981846de3e848d381afd407ee83932092c368a785049e17ffc3256622da1f24d393b31e969fe86fbb197086f9d16c0e6971241d5895679300addc2cc6bd61f4170444bc267fafd89540e1c81cc01c488190943b78679b4837c8a7f4b6849e59ce9a4c1b268266efff1ac8ab9da68d8254b819ed9d4d300189ef3798603bdd2467bff3409434ab45ef792c5e3d654634e269d25e5a8620c5defee7b0fa9cb571a65c8d7580d4328bc54971c3e4bc605b6980549f5282c1c0da4eba848c2530a25cd72cbf200"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"be33c7cd40e8504cd8fa237093f11aa2740ccac8add6c39a8b1516a56653d767","proof":"dcdcb9954029839883666f40b2080850097645e5f62b142b3c20c25a1c0b4e5b1ce57a9fe77e75dc23df73cd8f67f57e649744ad45bfc6651da4fdfe218e1319b4318133b43516364dbf874d4b4ae493a57bfd29c37ce99352ab1402764d576826201f90e2fe89192d24d62df03f3def9b6504f0ab325ed8ac0291d144015049a7d13bb32e4be0d96a3e17a4e0c568220e8fa7d80f1fadb8a8dc9afc66d39d0fa76a6e959bb03360260791f1d2e51472cf5059f196a22d7f9ec425d899307807e874c708465cd76602bf5e822fab07c4a328fe2313f6b39131006707008234060c7c6d1958bd8af4e23b750f92a0e48b0a62688ad986031ca6d1611d4a762e30f0ad41561252db834fa9ddc7888621b7654b5d5f03b01fb38d30ad5cbde75d444049d01bd2d35bd4c026fe69b620599d35a33f294fce452789c9c713f912322afaca01506ff4e3243daeda0ae817f593af46011be8f2a4241f83747f9575ea08228d32cf275966f5e69ffc58c4f1e2d2eca4fd5ba6fe47214819cb61ec878e71941eca203527cac0794f9af7ba822817b8542f6ddb38cc4183df2d996368500c2cfe82791bf7ff1aae43ebab8548cea9662b8aad29d33b1eaf4a55a069f0e85bcc25c189987d8bc300f61e581402b7995ab27510fecb6c106aba92b4b9c65f252e887db4eda5cbf806e586144ffad5b4d701a4ba9e1d3b2d8ec956b228804477265bf40ddb986328e2efc5b22319b57174088665fcb928c472dd8f019c8d51243a29463ad0d57868c4964f5bf59112c32a5f33e5924bbcc93a758b7c81f6d328deebc56537066c01e4489db8e2d7392969da7308f1471549b574ff1b30f15e5cbe805751de69db4b69541100dc799d589ecca1c247d0d073301e236d8b2cd7053ac1a93b0d571be35cc9128e4c1719b3428dbe6d66bbfabf3b4445d8bbbe6402"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"766fbff8e0d5762a712394b17564b6f3a8ac47193f425e5cddec62f6b841d523","proof":"92bffb8d1f26b4ae12181439b05f0752629197f044d8d322a943dd8bfc12d6791690d1c0ccc11aea26cc85cdcf5d6ac1ecbc2731703ef0b61e8a076df448f440b04d91214fbda91562d3cc08f3c8498bf0023c4c506476abdb446e5937b9d774d8e5e6760652fb6d1c038855dd124f50d4f58c3e16937751f2839e3fda2c356aa20bf90617e4725ff68f3ed4c6369142e3ea981ca7f98b058813269285243e0e36ce6ec7b10e001a32acc40315de2177ff3535eaedd35f0c95f17c33a74af204f6b8e76bbbd6f01c1033daf4be6c6ebad7a6d3fb9c6f23eda7b53c4d63fd00060ea9239213bd7dde2bde629a955212a24470ba8dd44c8c2b57b9637628b0b77bc2c73c5cea9f71616bb3c026dd14d1d52d4c1a1410990d3aa156ec0a8669b22a66660faa893a31cb521609cbcff124c97f6159bf73ea5a390fce593dcfcb8a6480cc87a7dca88dfabfe703b329cd0b40ff5bce372795ebe297cd9e3fe2f8d3284ef146618cc856181b999e297b47c593ce6c2ed59ea9f2bb6eb1077705dbdd224a279f560bff48d9e6f448dd7110148b64b9049306aa66c1600a82a53a00ef111e5799cc0ff660c4cf0b3f8567a2079f4429a40acccc504f7cd9d45f15f582395211875b0d60d7865c10a15831d7405bdfab16814363876b45f8d988ed843f2fee9c2544e07c12b9e2431c225b837eae1bd8b0b97eaeb474e0e35cc7fff513330afebf9c29fc506fd1276acd8549d37e09a8033dec094ae21ec1437ad7170017468c8e29a96caa8f92e2ef32173d1143e5759c46b39d55ab07fa4cdba5aada4ecad05eeee968b93b64937e023681fddbdfc67bc7648af78c1a3b05657b32b1468780020bf669cd8ed7353719e363a5763191e02ab1b3143f92d1fd2172f6f90ae6c02c9e80f9f12840502607b35b8852e7e667fcbdd11509fdd6be318de4500a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aabe8346ff8f0678c90bf5ef20e81063091122e8d5a7231455ad00eb27ef6750","proof":"fa3f73920c36a814ecc19cd1dbf22a7586911b9ef234fe6df4c197cdb3a8435e4eb56fa334200c77b5974a4d5e80f5c52186f7f7f98a9740474dfcfb8767143e6af18407906e04ee55061f31b54e33d2d51127c31dbef0cd3c998eacdbe94d0b48b5acc79a754bb40c969c3a24fbe24f75bb838ea8992e9237677968be4abb744d622ea3f985e451a902b65b984b244a713fbfb2abcd8cec724714d5edb8cc00f218ed3482264d3b43d6304fbff9b530c212686cb2e7c93c86bced4c3fddb00b50616ee99ab5b2ed14f10618a592e9e4a957ed0c3ae3db02c02feeb872675107cc9076f08c08578062553acc62e5069027ed7e287afdd220e8bd515646880603eac078acf7acf68ad05b333e2fb34f18bfd65395935728df9ce7b2fd7071b30166acfad3439739bedba075396026420292713e9400dfba780c9c3f7f50ac8d4f464f4b30f5b07bdb0c8fb4667c56c4e559c5b05d9dd802abe7b3178d901016770667fad6f427575eda851f030fb28c87226109bbba2ebf875148fd4ee3b36022fa1fbcff65f5cc44fdc2b3bb67608536996093a870c07e32373e84a46f11a505446259e1de2465960a72748420b4c2c8aaf0db4af9a74a27f38528eeb1fc79191cef3d22f75890556572ebf2e2a4b7e8dd8db9f677aeed57efea80d44810c7519ea79aa5838e5c87b1f7210380b0a5a5734a202943a2add1ef68ed711326c128209c96a73ca1076566de754cd780cc160db5b07bcfcc40255ba06ecd8dbc9b08c2430576d06de610348b6f2b93bdbc9c5399fa474b0cbab964fe8e1f49c61965d8bc6a1c4841703fb910e1cdf3a6ba0aa1b900f9bea9ee8368e760009a1612097aecc6611f25e4341e67aa35851deb40993cf3680d08683d53192520aab25407d8a758abfd437072747ec80183b82bb4edda0744595a3adef8884771dca95f07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"18b749912057e741b10b4e992edb3ab18b6b2d9e414f78e9224a4c01a9ffd714","proof":"ace0eee4801176f4ff2db0a75254bdd4be68df1f12dad5da789a693c1c4ddf28fc733a1c78af5cd709cc461fcaaf96c5243448ac75dbac09caddda3240783065f2b9a590c741ad4e3e2e8c1c584592f80358ac61dd9ab0a0a9087e8fa3dc6a5b6c1c17a7f9e0d31f496cf97707cb11e62bd085265e362245ea10f4de4834b74a7a84bcc2387d67db7bd1f08064fa5b526d5804e2a96aa0ffdcf206d3d8714c053dcac08d85563fe34908b2059b1f4267f210541a340f3a88ff9fd535c01cc60166d4dfbbe69da0e5fc78ad2e67a35da0e2678bb4e883cad81dfb443ab6f15005e051b0f6c825167f278811a8a1749fd84915dfcadf1ebc6e2db94575130af15d5ec7431144723893ba38e7c7ae465629c8a94325f20fe3cdb00cbd35f8c85b3c92120cf1d6bfe145ad5086385918d89ea00934cf9a75e3753fb872afe7e6a4308891b9c47ed18c218c7ec43e64640790bb072c3e28d272fd274574f1aa480562fe2e30c20f1128b36e64b410c88816f5b7d7028d19a1f3739a1cf565e763030f12808c421c926072405046c6cefb155b512a5961b8ddd6ff543122d8fee1a0627a0684abd13a9fb753a9c49a98a0cdfa943f9197d25d48a655be741e9980fa39d2da47a7322a58713f5834fcbb648a132bbd459d12e98fc10037182f341c3c67d8fb063e41507d8951386684f5a66e01f373b02c3cf225246673bdd4c51c740cae7b5d53214028c92f650a247f1735a44e35725096b35e382d3e0b214e2ec77ffcd6e30ae951f278c4389644ea530e1a89900b53ce7ac9d7b8e8c443025800500a86761650a4a838978be8269da582df0682ff3d2b32d789d2f3d677c583ce3399af3c5cc0ec7ce573a8df69430828c75e7240c8e6c5e29e2105d50f2c46810ce2bc18d4a81d50c192d465516228e02c862899ed43a32ad2ce2ec1f4076af304"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"30c70e760a9a1dd18d6c68659ab676f33ce72d695ad22e02dfe4671a6926777a","proof":"cef923c1d0e47b76ededb582875c2fddcc243b1d8b0a8a9a02394b661cb9e40a24fb75e1496c8f54480de570a48ac37e61085ed0b17f867cfbea1e6b111539648e2f3203e0b57cb93978e56a6db3978521d71544f007092219435523c427e65bee97e8bc5f71d33e5ee6f59a7e64fa513ac2c4afbfd9b3b946849eb9417bd922139daaa83ca6c0e76ecde1682db8379919e649fdfc53a5eccb21b24869529e0401c050aba7ac23742e8f2fcabf737c844fb840f9856d1927f908b7d765322f0d03bd5eed6e9219d5d6bf1d39c2fede3a6317dbe6ef49c7d00f4a37e1b3ead2062013ec2d4d984e811f8c38c6f341fba9dd7aa6b0049973d79fdf13066f59102b8cd6739682015d149be885f45eed209ea4464a37c3e02cc7f790083bc0e58b7f72a7080ea0af6d0e4bb6c963ad22a024b700355dfa27418a0bfeb1965736e55f2c4467168530ce1a7b0afca963a17929e263efa019853e2a189822485e3bfc7d72955b1616f48b5f408659e249a9a296bd543f0a7e3404c9c0fa5955e0567b100e5ffdc32a374f572a3324605df771ae2500ffce9f9fc3c00f793ca3d30e227c94742ce73a6a58053c8c9a53dddb9ecef7c2b273bdff9b6ba4293db50ea57a3822b9c90f9e972c14ba0d0f2828ba62a9a51a984c67caffc2d920c648bfb9c01d12bb454264adb41c4358131706652b944071bcbc0c1558064c05ba15f240830e10b7795320cc94688503be645a74ead901f42b25c67cfde7d0a82a22da2c9c0c8ef2bb3162638314bced7c3ded9e5e27576048150b4bc95ed7281cd4e570dc2d1c1f5a544c3ecd1b004c3f9ae462795c1b86585ff407165e4af8c61bbe8f705b1040497b4e2a02bb2447ff4a0ab6eb9eed37cd54270ad8efcfc57aaea863d3093ec2c4238d3b15872c3eadbbcd213f84fc7160f7487064b5bae44911c3529105"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aafef0e67cd1bf55fd60bc866163b48db31a4be813da2ac6726a173c2fa2b707","proof":"8cdb5eb82d522849b8688c44feb523a33597cbcbdfba7b5f96b5650de159fc4a88fa8710d6fdd02510ec6a825e9e15492933bdf70fa31d0cd5b99677920551520cb9eb6ecdf2f84635126a252ba8e12d06fa7e00f67acb51969cba4d5e6f1202d29d7a87105fe5cffc86d5694bf6d0cd4a43d4c1e0d3f8b6921535bb78d0e644b7ca67020ae39b7706c28c05962058db498dacf0a1d08a40ea83401f5628d4084af5bd2ba29ad3f0a43005fe4d510130854c7df021d4cd00e9163a8718fa280eb9f6e08f5538ec02b16b6ec159704510dec21d30c6e5a179422535a8d4b47703943a8ea5f7a672f177f4597684ea4958e23b2d46193f174e47a89188a36f00038e1207fcd9e8efa3291029bb756842da21183b4eb79c04a1dff959821938696520b2ff37da22e757e8a9fedfc3c6f76ede5611571cb6b90a921e99c9599377109a51e5b3caa48edcfc507ca5bec9e78ac0486d9d8257939f2d3d2afd9aa40014a209f54022607871c772b5bfe02b6ccba24cb85e4d58cdaf0ab0f1766ed456592ae3eeb19f4eb1f0e3bbb5fc3d09f3c2f13cb547868bc4a9fc8287bbf3aec3389e6c6ecca6cc4f5df93b43730e704354649df0b4dab4786704a6f552ec70db52fc0ebae4c07f3aea629ec42f2dc552730bbbd502f46ec80bd67e3adbccc8d666bca432e47e12d0549e269f13c7ec9c5fa6148b021974c95f3d5a4f0d275b8b22ccf93c38d1c7275bcb04f7e994f122a89d82961ea2e4c3ab9fd7327b7c4f425f2a0eff4e7e77c50da0e0fdbbea358b1d7264cc3e5f0accd8d7aa1a34a3bdf85e20138d083fe0c4f7c557e53e030a4d1eda9f128f2a89deab5ffe04012acc59102d2e016d4c82913d2d0728c46adf2a78adc4f8bbcf8bfe00fe80475770befa08ee563689fc5bd547ec459351ac55b0f0d104a8e0ff462daf76494e5afa74580f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"14cf4052225b58b78daeb64db9fb59f21db8eb0d091060042b5f1839ce80b137","proof":"2c34aa0cb8e7dd4042238f20ee0e3feca8f83a96c6a33573135aa6c79daa92366a70d9869baa4ed71141227864a4ea22e493e2796e8df16684974ac09a8d860552759362ee961223d645337c18cd9bcb81e375ed78cd3eebc6b028b1f4776d31f6b7bcf1d235cac90b267b83bca84d262d6374036fe80ff635e89278ea785c02c433ce26b4e7a35990996e3ccfbf8ad9e772c5ce67a91247f2dad74af1b41c06e66c5588de3636e0d0e9d3ea5f9337acb3e18d98a49067e0fb6db01c993d060f64d9a97dc84f379a637884972f075ed90953763fc02c1e40786bc4eea8993b04be3601059a8c1084d740262c4fc81163c31d56a5f539f351e6acf4b492f75c69e421bb224ae909093d25fa974cae99659e26b136e9f344d1cc4818b8e3c76e1f1a4572141495aaad0c05d4e3b6e0759ae3f1fd734ff9ff55d5f47e83b6e74328fca880d4cbc5c97a7d41798bc9e02d8a4785402786a2d9d970b1973a78ed8227ee491fe067bdd8c1dd462e2e81d4b135bb6ac313c3ddd44558ae1e4f1863376f167fda7e086e52c0cbce8831d002cff9247b0461a6e8fcba16364ff5ad3d6c6d861c47e18ce3a79d5d3c2f1cc766acc6224cbac85ac454cccc90747193816059a043b068d4001cbd10a80bb760dbdbd7a3df0bbf4dd8a7bf459085e4ffe14866fe310be3d4526cb841484b9467584d9fee62651b26e2ad1f2e1718926943446bbcd83ff8be3bff1c0ecad1b29dda062249f5ec8106db8162842191ffd2c430461c58f86c1cdaf45088b409d929a62d631608883f5f3704eb4014df2e005631268608fdf44b0465476e0458cac93c07c0a7df0f2e1a6b95c93e22514dbaecff653f6dd773c5e49487ce6024f17f1fcccad260cd1d09472417bfe57c3ec091910a10a235255f196e10db251434302b09e351146b20932a14c7cc79d333103ca70a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"10578444dbe3419c2c06078dd106806d8abc81d624950e29f4c246b75635b328","proof":"28e8dbcf159fbf5b5b7164bff0546469f16096abdf343b8adf988c47bb8bee2e065e461a065389164a546cf3af306b8bf5a1a8504a848eaaee42c97eaec3e3160c4259b42158b902cb24d0ef2e9c0d2ba2d2dfe1e8eb5745a5a8cfe7b2e76331b429cde0488404edd5d4eb607276dc15f6132223c766a1efbfb359d5912c510361d2c9b2310de38dee2fc47e9245a71293516935847e4d6f07d2ff568324b50bf5a5c82a75ce080ae563274de5dcdafc322a8c65f6151eb0c41d6c6143b62702027e960d0a91717926603867de2c48d8fe2f383c0ecd080bd30fcac732cace0d3aea949fac0742ddc85a07245e6c07105164b6a3dc23fbff9507e39ca119a462180dc76ae819a1aa20d81b7a316cf454bfca6b1a82b7c2dcebc2123470b74e722aa740c3b5a90aa2c2a4cb0c9f1bcafb441fcc8fc4c287f1d336f3909e1aa23a2607ea9db4f5d3e857af068046671cc57c0b9e151cadc5aca7cffcaa4c1ba25970d302909cfb86a7b567a35300a3b5166b67430b78bd3ee0cab25e763c88de3ecc845345f14bca5652ef505e0e2d790f9e8fbdfa7fd294b723981bd23093983cf66d61f9e6af7e9f79dfc079ecf7930c7c3c410fe1d7ca807e74d58af0693910f8900d76c8c956945f90482b1654831d3d1e6da8a9627c46fae986c157dd981f8a136e36eee2a876475b6b69d7e0e7d5cd219f71b7e40a813b8a132c7d8cfb33aebf23b63721ed8f938ab093aaa11f774f1533c87ee4fbab02dd55460027e67d84f49e2e061845f981abd037040628be6e8ea9e29261d89dc5ef39b13d3a3a46ce380b1a20216f51da03d0a809e846b2d4a0f82095999e8eba4a522b3e60fb7e170af508cfbc04648c07fc39331bd204aa3aa1a901dc4146cf36ae63c6585a00b3f9ab9622c821f012aaa9fb9dc1c6e44724d77bf0a1daee413c5d00e5146f05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"78c6a8bba12267ee3f2af89196a5b523d4d771761629214ec50d500674e6cf50","proof":"302dba7e49ba143b17c816c828a0cb951def80748a967fbcd7d502ecc348fc47a41e60287688223881ecda0c49ed801325c434c6834e053b802a838d4406b900a889ca8ff1a80577fd4bb3d8398a70aad833c1ccfbd384f06b79ca7fb7281258c41730d029eba48cab9940bebfd54b19b4bdead79c236fbb6ed96e3ee993080a169b887d4bfa75fab03f67b058527adc859b86b31e1504b50b7258b0be357c02e1559836a4dc3319ba6a140d9e97bf534284bf82a211755a8e94bcde459d3f0c92c7dbf201983fd48c099a53ea7e228155c2e50989a23f6c6cbec5d300f44f0f4cb1ecfb4573467e1a84c846ae41dc0ebbfa10a635b1632e7941d31dd7349b432e8da5c575e79737eb3644651936a23875207eb4cc42ebc70aba18fa3545ae7820ac26160c2957bc49c9503f2f750f2a3263157063443c4c084c596dd00be126526bc87f38998f2bcc4378f6457c068020eb60502da072c6e549c0d2a6d1dd4a6e3326599115e4a994b8bfb369b1c94cbffba3bac4ce8178cc3d2e29f655e26150afbe2e73da9835f2758d4d3c208d85951f6c87379a43d755e0f1cb0478b579188b7ad79910c5a3e027925849a5dd71e1cd4872e5428cb097277402c5cbe310268736fa2bb0a4b3ff3478767568d8873645d4296f5278108a9f2b46bb3c500afe5c81635985979d7003532d1ba0519f2885d024de5fc1665610de99c2b5526a32914d7e38088a547c5baad5e1552ef35047605c3f8f445aa4fdbb8f55e2ea6f4658e331d33ad56ea1bb282190fa541292fc5df7e8789461d4a4e3b2295d8f02264f03865c25b5901b273c45ba8577aac65d50574839ff5cc4b6d1c8e2f9fb4c188a04f5e47cf1b559a5d5af5150ed31f26a2cbf9536887e909d66956df23a00036d23346938456fcfda4f900e9751f0161b922082d78febb7237a53f4afab06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"860873a5a618bf1a4f9f0c0a6af19cd42f82c9c8db705e40b7015ba1852c534d","proof":"cca1f106254e581aed4381703a5457bf737ca8f1445cc8b2a09cfd5db47bf76df4ff6a291fa163204785c7931741af25e66de06ded70e31b41bc5d9c933009318c8b2d74f554e2925f03c1f920ae9365a59465b1bc8b33e19c76a6e50c721b3c5c2688e13ff1a339b49bf67411a358ea730550b971cddbe7808c5093d1604c192c01c121c53ae3bd257edb29ae175ad1f1fd0aacafb4d38d640d7434065a460ce8f9968f409aa1a12a874415bd3073305db1f74e8ed451daad21e513316cad06edb6d8041c70ba035d0f4d3abd06559ee98796953600568601348cf40b5ad103d081cd8ddfa5686673c33bd3e6cbbb4d83d12923adce9c85988b8a0a588d6e098e44bbad2b25ca128a52001d6db0b02264e22406df491085978cdff83ca3977cf882117ef8793b35bc186726157122c1b24b80eca8de1cda238bc02a37ffe46a62a37493d634945cc1425687d4c53492e15028cfe1a3204e11822eb3555d2a3cfa19acbef50612c977046ef18a37a63298f1cda5e9a0911c21a60fd9ea787b1d225c6cdd0e4fa23abb4c567ef393dc4e18a15cbb97e568626b69eab6dd48d52f74e862dd0de4cdffb9d9a82668977fc33909da2ef77cfeb4447034b947998171fe9c407638499250aea274a78ac2f0c69827f6602ff18babbc42197e757ee127cad74034213fc5b4fc6d2d6f4ec04a42d5e58c12ad6f65eb8a017921cf1d9f7526d91977bc73de8a2b6891060caf8cadc486e2285f4ee00cd8e10496d12e4a529299a21babe28643aa33ea3bd2b70d9928ea0c8b3761e8704553dbf4942ae6704659254299055302238088af983be4f4fd6bbf2c957aa1428af2b816d389211b3cae86e89e0621bdd6883199bfca865da0a1870d954952091fe856a96834a005e00869bf2e6494ccd5c52547ac15776f19b7599d98877f85a93dccb247ac9605"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bc7515fbcc76c97fd2f670dfd22427b844c17765dfc4c227d778f32ad224970c","proof":"46bdd89e6fac8ba07f6bb45d4653f6125179fe3de83a37a05a42b15e8c1d7d2682d9dcb303780b23294637d79b1d9a1cc86065805fb8dcdcb820ced6f9fc3729361de5f24c36b97d6d00d61e6fae0f89657971dfc32d52fa49ad53e9410c2017fedfc484c9f2633baf9bdd7f55d191d9e294f545eca62475d55aa2eecc7b634c3edf87d129f4c79e9e3394d05dc5ad51cbd663597750195e54350b53e0648a0afd57512480a746f205cc1f69fe936ae35532849e1c1df725c3cc9fdedfe5f606e7bfd8ccf1d4993722d0325380b8591e863bb601ad6cf0aa2507d564fa78ca09b8646166afc817285bc807320b39b682dd51a8c297fd542fa8928e248da1322b4e59b6bc26fa47bd4923bfd9ae6ca725356fc5122da648bc734648e8dd4ce50f1e72a64ec9827f6a66da405f44038bfafd2597b649faeba688a1ba0c5828467b4a51b2885d8d8a2a68f2c971bb3002052c01c0fe4993d81cc71d3e7322fdb40bcee964d959c313b2d1f56841c7caaa12846737d432fe746d6f65b14472ea0572223a956b68f552741573aff0a9014b56e4d321fa90c2507ac7866f6dccc0346c94a3670bf1a1d7de2ac414b9fe02b60505440f881fe9a85e25c4773d99967f34c642552011a4796a797480a57b9d84f4d6919a48a002b04c77504d3aba474901683b25179ea2024fb6051c1cb2ff241df8c1ea3363555934c7c33ff6e91393487c11de9ba32a59820179d94048105f31f4a63bb1cce469f551e7ddfc8b8f4b4dc42337529106e15953911050c388d05c5df1a91cd1a7c315387a4803226633729ae9a40405afc15d593ab99eeaab6dac0d0d53d854c79aaab99ebf14cbb1496561a6648ca59a467344b959ec36fcac9ddc87d83f8ee5916686f5eeb7061c5d08da4d1039b9ad9c5759531b56ce5ec9a2518cf34abb0acdcc664d992f233e7f04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"96ec960ce5a399982f5739fb9fa51be027caccb86232e264047732f193198b75","proof":"7c5a69eadf5466b6d9f2b90e67136499dc0f5c0091659b35808493330bd9d552e4487a59715c6e0bc4c6a8207fa9c05d500923c8ab7cf9370677b5926a6cb31d42f2f64743c565a6d2704804428f70c87e611b5042b4cd80c47ec9237fd8e57e5c7a83c08431fd158a3131e0c818340c8be12cb10f206d33a8daa6a597ff9b113ac28f3e97666c9b15fc20b612c151b1882775c6c12346e962db9e39bde436038e3b3ab4a77c185f921f8dacad312351076911b896b7257395d828d36acad60cb583dcebc9fd0d14b796f160b70d9914e4a98ea2fd07cf66421f9c54fbea380a821867d6ddfcdc6ff6878604b1fef8d826a99ea54a399c43b0daeb05ee174f746209b4e8aaddc45e240e9da26d90b27be55112665eddcd06fd75fb86f36ab179703e4ee9517f46828b003f010e018a5b3c145642da59ec066b2310f0aab1bc601044c72a40f02e228d9188da86575805c3252d5fba66d243effeb37fdf0cf85aa01deb324ec2e3c20f144718aac613729173ff1a479306064f391ca9ce1ff95f68940884545989ac4357fec6e4feae2158f49ef3291ad21ded9af255065848748a4fbe83fb0a6848f6fbe74450075bee97ef0cc466920eadc26bfa1ba108206c623ed87c0a804097b373bb0269916995360974f9f1fa74069e46e3886fbe637f42503bcc79d87725f16dd918a096b225306edeaecf1980178c5d661944634974180947741d5e0df4868c8054c3e6a2093e18276274f4cc6199a5a3beccf816780e3b10729a4ef04260af1350d7bf93ac7cb7ec8cbdac6e71de5b39ac46ddcc687c360d50a9ea66f8a314fe6dca6af89358b658583ed167c22db71b1fb43c89378c8542e90e2ae9c63821b35490d0632ae374cd1e7d3465f510e535aca74ee10dc505a3f09dc305738b9a9c3dc2aa737ea98eb4d935874299da0edd99008e2e07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"44c28cdf69210a1d195204f49daab5375669ab2c26800ef85bc7669e0cbe0645","proof":"f2134b6dac79c772835541aaa17902990b4d4e5c0f6695214decd2e61c1cb4427cd42d988ffb68e857f303e03a4f4be8ee54abcc342e004638a976c12658d7225cbf2f2c284f695617a942a462298fe89e36f88dcb01067cc6d5b092965ec916c63faa1359dee46da780ff8afc68f19e8731bc92bcc97bb37d813ca5e88da349d298e3ad4ad07735b8b40169e0ab1ab8e73b9387914a148776a69be1e7799d015c4fc6fddcda13296af9488e4f56d1f95c946fdcb466b54cecd7b03e4eba2f06ee2e7afb34a239aa6b1261110408488bb20e43047d7d84fd035a50966d736b067e4145c3e450e1d9330b996355f08519e07d3f87df6083aa14bb620e21c1fc72046f2092b6082f021a97f1d3547dfa7cde2543aa4457878fa142d448b179692af8cdd58773d9e134966d2a4b89785c194f5d86bbdd381021b27f91835052d533ba27b747d8919bdd579246f0eb26eb8fc1345cae9e9735c1bf94321798b07107882e2d338a2be4bf01e25841ac821d74e73c29ee8b2a2d0c1b3b72da3480bf5838b49a2fb966212099aa93459b7ef4c85e1a7251964c2273b1130e7bdd47ab6b3e71e93e7925cc313e4fdd41cf005b47db66c7df479daad5f967a2d9dc019d5a94afc8af7272877b3d097b781aa284d2aa185c47671b30b445309292ac10b23bac9d9a5f9d2af15703caa19c27794c614658d4a43887a25f3d417f60397d8517605ee0bd08645f6542b1e3f12101bb03c45f94c1ff9c1a7c02e98ff6c9d4e01d9249c1fdfd5aadda66ceba2e70fddd3ee297d37b9166bc9f99688a5e068b4e3690b9ec142f54a03e75d4c9c9640d8d51be390107e67cb2df2dc0708708eabe687a15e68c7525188b98eee7b505a390293ef00c92f900efba049d0862a1f04700aa07c69c8780ff17695e14c8452f4b75385e36f6333df12e87a4e282f7d6090e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"84fde7c3eeacbe21fc1bdf80a8b801455cb82c35de9a0f38e30a669414434830","proof":"d20b89ce9d336c0e83a1f183d080a69ca4dfb4c78975841f2ba40541719e9d252427291d044f760c9fb46a85e76d1df769448486bab44d1f94e98792d0ff3e48e401cb7425c4c1618985d4356799349762f973790f6276e249e4f79cde3b872d4679aca85dd7a0adb9aaabef1065beed2f6fa9df986ed4fa2e20b84c91f034766c01ea869cdfbe1e38fe2f185375f8464e583c92340c2bcfdea06413e1001a06ed9645e7c39934dd5c8595dbb6b6f4157e6030d789ecc15ebe50749759e47b0b9e92c3c8bd97efdfa5b62cba5e7cad4835223837a110fcf00bea879f7cba9b087e52d9b345f792a56b4a59f8baecd0fac07c1b918205e55177662fc2ca73d25ea06a931354bbfaadc55aa3969db03784f7b590d732f75a8f991cf6b4da192f5ea2cab135af1d2695fa6ffcc96b7ffd7143cb618025e175c93bf7463c4b4dec657e81522b964bcd6c7420cbbd99a6189160f86fbb78a911092abc10b676f3fb4edaf63fc20fdd2aaa8f961946a9071c6da4997edcf27d4003c61045b40e8b01370c140e050e713de9b88ef2cb3c60e66e6711499b8ba6e241f8e38cb9f9b696482896a4d62270514d3ec0bb151a3b979f428b4c75edaf49f0b226df52fa0e445ec89219f3491103066e10b52b5ddfcbcefe29306c837852ae13a9c4b7d8c6464d66ed5007f1358de809ed7c5d6747a2d6a387afd924ecf02ce0dc6686b1f48e51da65e4ee893adbcb019baa0bc29979d037f7de0f9f2a1218850a25af77b8de22e01de9a6f2dd924a776ed12c582506d47a209a74b490fd95710236544b8e466c7e796e9702143667176f9acb71a72eef4cf2a8e5f266d5d02c1e344fcdc5d0083481ed2c6a5bb3d400380bbf1698e4cd68666d13e1f7d5b151ed7115ec222a04f99efb8b6105af01645881a987ba75a1d54d8a4df51b8d86f389594b02b0fa0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"144486e27788a03d97168cc8c54ae51e035e131e35ec5b7c1d96f1b5dd355b26","proof":"2877d330c795048fd16a84bf22397430e2530b68f6b669079bd590fed452fd7f1c0c20711b4a838e3ae6626d04668adf19b5904a9b801d24c190e1be4edc02415e88deff16735174e8a527ac676b324fcde6eed2a9124fa8972c5f73f9fca40270fcd1c95d4674dcb9045a099fee4255e6505092e530bbd04d66c575f0caef4d7707c5bdd4ba3d2843d257f7719bb929265375d4c4b6235a51252864bcc6b10d3e8b9b1b87d4640d7dc0886694a4c893395fe9e7dea7417fd51ac1ec4fb2b50a77edb61ebfd99e19236d107f7df2dadfc72930051159f0cf80913b355ef5c50222321c2f60844e5a1839b1b6b41d69ac595a7dca23596f2df909baa8defc023882bcd6af8539e4910df0cc61cb5fa445fcf8ef166ab6f4d6eb01ee2f9432017f22f576f9c4fdc0e6660a0fcebddae730661d04162ef24a742cef75856f98b12bbe02b7c9e85a6540a14f7acfdc00d9efac8afce2eb34f59a08a12374883e4d51542b12704178946b8b9916d8d19b044e184e2cdc6f6b3370865c65554d358e04724a8926017f86403454e18e29ec95809efc60fb222f8616b6e079bc3f735e3d9a72f16e867158544e050a122f46c87368cac9b0738348123ac3fbcb289778321c39f5fbba0885ccdf45225060a208685046cf73cd776c60d4cc91b1cb459c68049a8020ce5be0d75b25db07b4ad4ca385d2edb11e3bf8c06aa0aa7231b57c771e6bed213fd56fab5f0fe9d46f4d370e457625f49aa51ecbe6a2d0e9d2f11466c255980fc8a8aac4b72a0fe95dac98af92d6ef48f4a68bb5041641952d383319ba45a436040df10edc637bd5f0243a8bc63fb276ce41b20f6e58cf27a35b20405d58ef29401c1505bbae7cf65fb7e1366a8f270d3cb67387fde8fe26035cb405f85017a38dd41ef3d6a38227ab7864c20cf74ca5c799a859474c9fee2ebf5d02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a69c02ab7a4e3a4d5e49d9e5cb2e076bd293e08b0b776c3ef14266991ad7c040","proof":"e8559c155b5959ce953653ff0bb744a71429acf2606fae60730a6ee138ea6a5a0caa3370bcae87407208e9f6b8f27e73a764736341b483a4345314ec5b4a147764626366a8638db91435fbb844674e5935ad38aa4e09cdeda7129e5399613e2e9ab3296556a6d2987eca19f22a9309890de9d5f49053691076028f3b072cf47ef69c6b078ef514755394b6ddb1bc323b55ab48132f8289301915246aeb16d70551a8d1d9c5f1e1c0600d463ecf5ac69ef82614cf35e42d65a372bed1c65a290adf09117e83b9b573d874155026fc4dd9287e3f30cf2d007b70b4b8865fef380504cef14eca49afde7fdab038a5f799cec91793e1d31d8b9775813a8813507a2dacabde6fa56a37d7c0dabebff17ad283f7cef578602cedb04f2bf2002b745467528cfbb2d6ad50b2c744f87f540e78df1bef3ddf3d7105dd39c26327a6d2f246e0341672dc4c6887327ac189c4c4c62aad3a648e426390a8d26e478d229c2c681cc43f5e640de2a688e3081e3fc2667e8e605355fd73e09e21e1c0c41f81f4370e7e4424cb051f53c8f5e5cc584b793168d43ee42d727e11e29a1e723a439f43e0ba102c85fe61ecc08f039c1706a20f854e2833f4c294e8213528f5b8d19503ac260841fe9b6f42ff41e6036492f5849ad6ea46098f562f88ebd44560a4d34510106fd7a32356e6803fb59d94b0e43a58c073777e8f875dff76c9f3655c473b54e1f588f19af7ea96f98e9ef5bc30b3fe38bc34e68341508d8bce7774ff8a5794c4fd3be7d6c82f7fde8a1a97e2ebcd1de9c81fb4b512ee46b71959ff6c423a2668243bd33bae1c8c04bb97d0e698db32d7ef575bdbe730e9ed517209c51078cbc1559bd088a68482b6e89e8011b229acdf5307769279c2b0a2462bade6c206153dc61ac5e0083c8e9301fc2ca9e0d7f68ee5f4b35eef5348c4be669d81a603"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"38735ec55593dabcc20513cc3e18df1ee594ddd7ff5fedf13b71fe0ff788db4c","proof":"f234ae0b8dd3fb3c95895281dfb6636f5a844cd2d1e8509c7ac3ab9824229b2ed25c4bfd7831f23139a0532377c15b06c2358393b9fefea41b6a8cd45707c2223ea1d7fff8efdefc533000850dcdfd10d8e0b4874208c297ad10d362bad49b27641e58882ce6993d2d2150046a744cdf7ce93323b65aa6018875fef40fe8ea0a31d53509c48cf5feed82fb967a5fe461e6d988a0cd8e2c15f0c58bbec0b1db0abc70aa59a8b029d39e2f44315eb36b58a7a5078d7e063fc0aa63f99dd2c75a0a8b5830d65c510216ce49aec7c496b234f3d767b80d21dfd0c4d12c3038ed5e02749686d00a66b8d12ee0989ee2b4e1b1d1405b88e79d4069f47230ea7aef463c9249ac05b02e2540db7e3df1c08ecd295988a44092c6d24f98e560f5d847ff6118ea0b68b9b6df94aec2fee5270f708fc7247dd9d3ec909a77af8cf6ea747e293ab85ea3144841b3e0773c0a0a798b4a1989d3b5f6e1b9c5f4789b0271299c4860f92fbfcb144982d5d212a7239aa5ae6f3308026e353881b53c59b61c1c46797270a064969debdaa05f9809d9b53596761265f3afca0944227832baa1970c2dcccba1a038f2b8f13c575fd838b538cf7a320fe51f1d2fe8f1153397c4b89113e68e443ab4679960e4641a957d28b2dc1ac0ff82e572fc119c62c7d6f262ef2c90a6cb3510b11f67fcfdd4e8139df74d1b4b17cb48d704271bc34d9a0826ba1f3c742f8f422757f5538a2356b81a2300b71cce02d85be10e2935f69fb7a09205ecbd174bbdf9ca8ce5580d06dbdc74c9ddc4a61269e85f1e2fead0a2f06f2642361174b18074a50cbdcbeed24edc46669df14b6952ed997a92104dee85b40a3ef9aac23f003dc6a50cd0cd10e0f994d1eb0e702373dc906fb63acedad45bb3014edafc990bd80b09abb14519f98382ed5114186ddef8a320e5b206446ed0e501"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"00ac9440147581e77dada33155297c1b38b90d5ad7a6a8417d1f55b3f5acd410","proof":"640e7e038b81d120c6433cfaf61a40b05c1d375249044b5d7b729e025630593aaa6fa354fda038789091d84f2357e98050ec49ff8e6896c0f3dd1d372ae3ac180c19a58802bdfa48c58115a40f15e071f9dc8ddc07a983bd090e4e6420b29f311c0341dbe3d1a2e545e50a70a082c86ece0b64b04a7be1ba1376543a69d95a3c39fdef05f7b6a6830ddd11d6999fc0275ae9b187938ce8bceeef6c5cae9e93022efb307f9c86541e53162e51382f38ddead93d75db7272f35fc4daaeda58c409aa7dbd56e2e65ed433df13cf110ac8ab5a8aa06e269d4ab22da91a00bc76d8093a9462fdb4d1ce23680aa190e2510ea9c77dbd340b05051f5627c45ad61f124bd870579e1cd4c993cd48b9961c8e3b096f752e52ee7a858cd574cd131ae6423630cb542f772c8612c4ac07c8b9ab4cf777ed863bda9fefe6ec7d02f9cea6696a5eeaa420cdcd95537df3843b4958807523031b6c8029f7ce6e071a4266269f1280c05d29372aeaf3d496dce6ba8821b50b80b2d65c7ab303dce5a27909039029b41fa2a6535f5d43942ab890a37aae8097a6de9ccb6d5c945c47c18b860aba3ce4037d7c09b2fb4fa2ac141ca8fb4e41741dba95037148e3c33c590560152c2eacda26acf8310474dd10a96db3824b50f1a33f3765120a2916587a4be5480030c85d4d0e870e99d61a8a0549dccebdae234111aac7430dc2d544422f515fdf110ca8d37552cdd81ab078320958090ca5a066d779fc3b6e52ab5b6c8afbe8f573e481bc8d8ab3cb68656507465af9b1bb52b96ebf9d236f13ff241b6fd3e6a9585850b32f2701450ddb05375a165ecacec942aa4cec02ab23d9075787fbc7933662d2326f40e529a1630a9de3806de8d5ff17114ac2765e502348c6cceb2ca9047a6a9e83fb340e29061c0c959b79a4b8a32d36d98448153e7226e2af30448207"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5aa03563503901941afdb85ef0d0d2b1e7e47efb3789ff056252b5c33c5ae963","proof":"42cb6fac31564935acffedec6af1651bf19c4807fa8a83b0969c5dc3e1c24975ac9873cb25c0d4524bdd9e51e53669ca18506483ce50f7a02b2525a8db2e156e5c08ac69322ac3b68ec7519e770d887a748c768bc2709d783e2f99a8ab0d0d51e20259fa7372e50a4d6b76ae5de03ca5839788a3ac580d6f6eb5562529f097797fe80fc6d8e1353556ef8d662203ab82f9e912ce265eb8e179431eae3ca5260d94c448ca67821cb3d3aec78f410a953115053d5a4e94ab515db62b2919acf201162b71eed079e18d2e0129e081534a7e402a73ac4a46d7e00386bf4cbc2b0809a26124ff2522e185f44663a44a67fbaae8e5b17032a75c128d01320034b0a0177e1b25b0296881368d3bbff551d352307898539f7dbfa94f49e3d621db2e365d342fc06e93d2ceec650271dfcc79145757033ec2ee4028f85a4d8dd80fe949654487a5dccbaec75dfac803477699807a396ec5bbca48948885f715e477689c00729c5a52cadf73b48488deaa545adf0c83bbbe3ecbb0303548813fea3bca432dccdb9dc7e61550bbd201b1a1ddf8ac3333bce1e58d744afba9102633fc229f3df8d292fadbda03bbc0da1366c2c990b061f67db5ce4e2a789f04613aca604c489ae8c6449da322a785af10c45e17b0f7834b04ca757bb9c7cb9b917222d0973fc0feb4bab836c4d649da9ec786094546b34b1a028bd6a5d34859a869da24ee28825f7b1941b1679e6096ab78e2a1adf143ba1740dd7171c12d7ee00eb212e36be65ad4493a959de7070c14e76fe66e0dbcf3b9234057ee868a963dbb1782e73f26bcf81070c4b042cd4064e90ea870ce8a31dfc316547039948df85b36cd3410afdbaa33c0f81aa8d68f353245dacf3bfd612d57906866c7d1e2c93e377fd30b7151969bcb8f069f12fca029c475de70eef7ea4d9888e08451d9669d2ec8d702"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1424a789281350de52d929de89f16ca0c85207debda16b5a1ee0e066b8bce038","proof":"1a08307484a4698235d229550c7323588bc7c95c2c535b0435b13c2313d94e6eae5d048d18c314fd861c7507c325413c5192c413780f715860e6901438abab749e506fe2712e0832e9578568460847323eb04f240c2d9cd043670c6631e8c84774067fe6faf1ef036ee54de32c6d9f0a59d672985d22342a584958c24611221a0a6ef316588252274413ed9b623648adbb02a22eacd1404ca3fcc1731a163d0337adbca22c54d2ef941a170195dc7fca6c770210e02a7752cf66f6c882b9d0062d1e394e710fc7b083558b0f72c25bc5c04e75f7241b6c2a60a548639ffcd000b253374676a14356a178a3b74da10f092639fafa1a1c9aca891c18a8e7ac7a3d26f2cae4c29c2ed040346ca1e71d442468686bce03d754f4905a77badf8d815d5253076f0d33462524e5e2c59e1d34597bd69ceaf97c3303fbf6497dc092ee24622afc1d4f69007964f365e80e3b95d5f299bbdc86bf523a4a96b2cfd35d3a68fa211f12c0f9451aba1c235e1c3641ff086fd891124a2eceae8e07bcc1ba90094a1bcdcf34f9af32f71ae5be3537406c600b402ad784cf7a1fa916abf316ac22f8c696020d66986a4af7f992ffefbe3c7a317a0c4217d290235b261661b923739cefc431f3aa3872ac4b047342c47f69841d9c3ae094a1770aa9508227cb4863cae0ccacd2341cdeac9a4dee5035200ecece0523b994d19ee8386e71671c48050ee082a93104312e47b1598f9382c8a8f33043a06dced120fee50fd1bc4c9c6d1ea216eaee5f46f4ab159e1a6de62926b335bec1d219959473974317592d3d4c54ff7b77f6ed677e2a82cf64250aef47719497e88c902caad64517a32412dc6f1b934e121f07f292a4f3b95596b0a3fc768657975f59adabcc4f2e0200ae0d05ad9b24f6c09f2afc9732a4b6026d8743986235fe15e3a3f6defe7a8b08725b0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d480477d27b9e7ad533a1a9231317fa2113230ec1234ab93a879c76218e2e163","proof":"dc96c7eba83f827e9045210f774feed1f124c27b7a46b8add744e32d77a4e81458e68242d8a99cd16cb1b9c786d86163ae04442a3729ace5452b5006989e91404296997633906c9c3260a5dbc37625568a2fb091852d9b4668cdf20dfda8f13e72fba525c46814eb4adbd977f6376dfbcf92934221d913377282253550e0b1615f7c8936b339b0db09a37572d78928381ed37a9d1c936d50a813a90db57b710c2972875bf8c071796bbcd38f6a9c21664f484feef1451072f55256d85d6c970aea4d115c808482955f6f053319c350980ef38fff1b9ac62215381a42ba997600865402a957694d171c5654fd674141cf1e6c4e2de8834120ce172fca3429a20a441c4ade025e1e7987efbf7c162b0d84bcdd768c140c6d5922caeb90519b3b7880758269b53252bd9f14f7da449c4325313fbf36927166dca4aa616fc664061e1c1cebe4002a9b1c607baea1852ab9e17274845bee1fa164c2198f23331c543fe8ecd227fa8beadf1316b15727ff057f4e70f2e8feb353c41f263330990c760f104b4eb7ed032c28c72b244519a011ed439ccb7afd41e4c8e418b01ca29a0e42b615187ba5bd63b0284dc61aa8587230931e0af67f4eef28dc6734bd2415464b5043ceb6dc442c09b88c4a71adf8fe34f8f6cd37ac3e6fa58a86380ed3a7fe032e42efe08fa242ccbb34bdc94d801102fbed1fd4f265ea1a04ebe458248b540b54b3de94af25d03b3c6dd4d031691eae927d72770b3aef7c09ccd1fcd1adfe795a68421287c09cbbfe9f06c190db97c8a043e02a7842084c5fa227f7839e930a8e905ba24ec09b8762de9ffc98f9ae878795f814d7bd5275874a15bd5b5b94666fc29f0b0de78941b5ac44e3e4e095e9c054c654f910429f1a714d2167638c00d263168e5d3cf41e376a5fd6d49b176d34f2f8ff6b69dfc044467e1986ba1404"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"48c5ce84707bbfc86ea3b34cb987771236e5583cd325445aeb467061e6b88055","proof":"6420608692cb4cca750d3af36fe2d409a43ca8b837d3d1d29a08b1a4b86d932bda3c135f78de5614e34ca0de46815c4bc69961b0d08d0b3d5b83348cd080bd3e7a184b730345d0fee9cd07b6745d6e916fa525c5a13bcf30c33ca3c49ba4e559c2ef326464eca900494a2a6d1fbebf56004c9fd6f1da6dd658d97c16f854b717b3889c66259a268e2031ec57835bfb4e3c58c9340866e033abf40ddc098efb0e4ccfa73c29add8cf258e4365cd9a5a6b73b4834ba5705ca07fcedbfbf163f7061196dca3bc25bc372817f977d8db5b769f203ffdceb4883917cb265d6e2c260ceeb708864b8ec81231c46d4edef0a19961efe7337b23903ef39d831f5bbde2055ad94c8e219ef5b42a070a17091b0b85293efb3e0a56ad711c4e052dfe487f334a12329b0e2b0f1b273b6a73e2f7f9f87a5f726d3a62e0b0819e0097db726812fad7c1d0abb87ca1b3ca2a7891ab86d550cf4d117758b25d015743cf68af4e111aaf3116249ae266d9ebd908e79e27e473e7980d495bc5f20ba6cc257a3ff404d041633479bb9f8bbec6859294d081e1b3423ecd09602d428491d65c878e5009c857c1124f6ca961f3a1a80a3c3951ea489878439e58947ac377a0cb5bda6b30ae7b2fe1a437494b9acd74594ee82b6f6618ef0ac24f84a5b750e5a12010bd0f2abadaa7c5ce7322c8808d2be657b58c86a97ce5b576536ad29e1f3ab1c67002f05627de45f9e6be99dc1e763a0dd86e688c757f9a8e6e57a76117af245dab5632989d358d4181c3f883e9fd9ff2fbb2dd4a5df7c28e7a6db5efcc11a8d3b161c27b6453825061b78d1a05e4180ecb6dc46feef501cb058d5e5d720073d26f72cacf976d92c6ce0ddcab88ac19fd2fb7a44e4904452b4300371127587d1bb908fde421a1bdfb572749fdbaadd07ba8b74830e17c1e97f1f221f7e35f85020e09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8ceffe04bd4ac3de9d5f366efaaeaefaea109fdbed07584d95a9e4b949f53361","proof":"d655aeb8555d50367d6d71dac2567741c451c9c956b61be59194b57895ea0354661dbfc448d0838fc2d1e7bdd9e9361dd074df51aefb8c937985f0dad40a0406a24fb7bd33792d66e8e87bc72e1808c645dff1ca73bc10f74a3d16b6e0c16823345e05294434fdb0dee6d3e6985a05467901f9eb7e8eba5fdd33b2e4a5bcd35c397e8bafada7364108a3a6a02a00ea806195a7d395a1bc32fee53cf00be9020d7ae986e6e80f1d55f1f336be23c7eb7d7d530227f0f33d1903d9bd5a66345b008e0dd0fd3ff3c5d31a3c4cc780a13c47f4775900c5c2327d400e3bad030b66038cf8a113622f3566d0bbad51963394c0d246ff32e3bb2bd189bd4f21a268563094c0475102d39e6abd8ae8bfa2b458dd9bcd239ee2cc9a25917e884175356e5b643d7ef1e3fd2756e60015810df9c69b07d5098eefa7f3c8e80881a4c831c14d103a49491f690ad63588d23bd024ba5343f132ca84b5fa02df4bd1b9e4e4f741ae65a0bad0ac675d2b2f73d797ea610b4e324370c132817a232122997be817569cb5514cb9dd513fd0aa037ff8a54be0d62b34ba2600a1dcf6a10eecc0636975861ce5c1d375d3975d3de9651181da81ea0965e9e62979fd89f834ca682b6e1bfc8231336adce9c346da1679fd32a54f508f2ef6fde311a3933791dee95c7e6eaa7b7c66c75ddf73b9f008e4e023132a1dcf17757e763ed742ebade3a5efe1508ad5ca98a1b67d97571073280d1c9fbcbf6d893c687c671351d0a2324cbde971e4adace6d4a5069ab1e8c470b8899bd92e71b313e16f6fde258f0c640ad6b10ee6569d97662a5da785886705a8b9f1b4d4b0280362c5414e7caa3d6bd8a18720664536800591bab3687d3fc9c0503d946bb729fa3c9ef374bc44aa90deb2280512fc81c195efe3790f7c8aecab6925a0f1c61a39f26c3af08b1670867328550a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8cbf563cf72486cb13e17a1665240108bb800a721def728ee0f5477c438f933c","proof":"0efa567b2aded9717371d41a25f14d774b2bb7066dbe63b68fb374696b15ea31fcdafdd1ea0b56604dc24c4c9c2bfc6f3db9256411fc2049f3605d584a2853765a64bcc6687f78529615e4e1666f5b9139800adbc63261b9b2e79d38b48d835b9efc8c0b725bc97c2650388d5b1a3fbda7b29502bc6548f7432fdd18ca99f12dc991be4c2e97b2407d760fb1e62daa00676165d650f85b7341264a68ed92ab0e5b2fe745ef2e4c10a662ec1c8f8747345a50629a9f3c90b0d028fcdeaf8cc00aaa476920b6ab2cb7089bccd7ccb0d5e3768086f0825384cc1b316b4a6522f20a1e5d6ee40e221426e81c1c4556f2c8692ba02ad00ef5bf5e51468b8d1e9a071382303f298d83dd6f2988234bd95dded176fd1e4ac62bc51ac1251346476cb66246efb4ec2722c0b1623630236e03de28aade22f8c73803253616a2e04b4acf00e68df621f12e74cadd3494e61e4079501fc0dfedea0d71e3485411a2e1c8541354f1ecc57f280b5c6666e23320c27455d614443956938a79d3a09c387452da50ea94112fbe6d35e7ad0bb61c38fe1f9b5f87626c35f348d4484c7f1d8518aa45dce8dfc0221ae3013d33916721f6d6f654b8c48f36deb182c48fea6c430015241268892b664916e407676ec10de914168b81c8d61c6af3110b5741381deb3c7ad4e7591768b78eb15f159f895adec61c84b8f6c39fe0f6c955f7a0d8b15ba05d266f8aa6d4b53d8b9617d914d73503440dad0797ba8566df7335b3b3d74f3e27e8aac108230f0d15b435605aea8024e0b3db7b55a2ac5ffed6e411e0dc61b1416ae974a7608ef3a89d7e2c8c6969f1344afbfcd51120c7525b96a404291f537cbe7782989280e22222d17a36751d38691dbc33cefba1dee1b5b673b7edb5120b342871e9368971abb23ff16ce79abb1c8ffd350afe482cf25c8c9c9ccfef9f02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ac4533b60b8f026da44d4da8e8c2e80bcb027116dade6111845c758136773349","proof":"82498b0c1be9abca69358e28ceea09f5ac71e5f0810091e6118b523af55d5c3b66c67b58f08b3e8d85001834b7da8c07313579c45e7fb7d57b87adf21040a30cb853a01e9edc22224643abafa1f7c557608b3c066ba21ff3f3fb70e5d521971336b41c79cf02237d57a9165e34e417b231bb07ef6d90eebddc28be8a3ec089706ae7435c0f5d63b3d66c0cfb90cb6216058a8fa911afaab744074641c853e70e769026f387ea15ad10910395ce12df4ce9ea3f2941e02016d54105249576bc098ebff3cb4c59c628449a058ac6dd73dad24333d6636ab3be8444500b05539504caea9424144937c235d0b0e0a23350180c09cff0c5ccaf3e5dc7d516496b8456a804b9a9bf6b9cce266ccbff158e7410a6056f0871d138104167614fbbb93c2af6a92a42aa857bc650d21f9a96bc53e3685a81b267f05607ffc9e5bc52338e6cd80181c16ec7ce8ca46f98a07569a606872a93be0d88b8b809f0aea4d5b8db64ecfbb2573b87566e470e3425b950142d564430fa1a2aeecfd6aab418aeec9c5584d2a4717cf7079b4ada3e338b36e4f2b6622a6802cc14095e612548ec913650d2125afa2f14047c7e79fd620e27ddb6cf1c4ea9eebdfb3c6bd4ff9f2500ba61627818be200ac279874b6d2de30fadc5565ae6a0e69e9fd1fefe1a2e75a8b765582acfbad8633b8ecb29011e61f33e63c393819c218674d33d47958ed3d48a2e10c3e3c9962bc0146dad273ffb49b1bdf30f543ff811af27e3abe7986bb7822c0ae6974e0dcbc1da346c7f3cf4dac7c1c6cf032f099657ed99c70abab14b0c1f82859e3cf7d9288c4fe4f825d53fdcf836abba7dc02cbbeee1b3d1dac5f7db277f5df55ab574fcdeb2ae8d87d5603dbff2e53f0d126a5f56c8cdc84177e788088d3148688e15b772c4152b5cfc1434c68b78477d55be32b24c55e63e98252704"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b0fc3d83a47a7fc74ac6341272e8e9b504852d8d0a0e613440243963361d3757","proof":"763ece02828feab57cd8fc136f91d1e2447c4cc64d5000df1259272e2eb26739ac64f1ccc7eaba1c47c11ac304362875772d75990c7bed1a4539e1a2f121e5484c93654b6ce1132298f69de533bb26f5c978e48a8cde4422ea91ba9eb48a487d008101a59e6911a4a459ba056eea1003ce72f4e10b20753b60f6e9de52501846cfec8c81ab302f403fa77acecaf043b9fe8c97fd6d1ab94e01ee006998918e0e0d07ef0f851ec08538ba1adcd282912f6b9b0fc3cebe9d82fa6b8c6c14a8ae05ab8ecb6414e3a9d085e92c14c2ccbf28bf311059795b06c71536064abcbdde024e40ae592dec395cf01ae244bced1374efab297ab1683d3c3968388ec999777422e17afd2215d995d5489c3ada2faa76047514fbd72686fea7ca8e9c9eae300b3ee0c94074794277e6ff63fa858b1bf859d3ff127e5d47a2b8ffd0a49391b258dc320095aadc3433cec9769d6b11cacd6899f873a374a355c2d4b815cae5d800560a6c9d761c7b5befb3f202f8bb913ca2975430b642b7ff69cf31e994c2a81278e066e6015498239d790bde6659e04f0cd11a6a6dce8be36fe8df45e0e6123476cb5ade62647bea918eb7eae0f80a2a592fc042719c7ba0dbf2ae58c8c3e94c1a801b1c403bca4e84df869f9499d97a5578b2c27a9a43612abd9bc1d8e4f34230dadef5696bba3589b2fca9745ca3e2e48b718b6fea5c0365c0ef72fd81f858fc38b14b2c61af8dfe37dd6e7faf368a4e3f08870323a9bc8ad032b7f2435004fae91a6a7dc00f9c894e251d69ede218259464ae4c2ee822de07b01561474a07b04055e93ac3c7913c69bb363e792965c3da6e31d422dfbf5b64c0e50ddb531b58a42b6a920f9f2b50a42b9d66b57dd7aeb989f917e3e639d1cab43ab9d8630495acd56b40af22e58425ad84c94661f4937b9f4893c568e859406c1fdada3001"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"40f746057f98a3b44d38fbb5e9999fbe0ede254ba87af8c724c0ad3751246068","proof":"04baa11caea5defe0bf33cba6c078b8bce8183668980bfa575a04a7343ad2d0e040fc290eceae87be273c054783f67b37cefefe34ed39245563e4ca04033204972de755b35257d510e9a445a938019a43e83d042fd650f9faf09e0ad664cb12b226d5491ae87d2a85f63150d2c4d6353d4caba773a2c78e6e5df843da659cd172699b40307d40cd2877d281c406fbf9d31ecff30fa99ac57a27c1fdabda2f3052ac4967640e577eed98579e005dfc3790237a6d4a4cb9340a9b128e6fed85007d6bbe2dbc2ad1697222c779a8649eff481621fae8de8beef8e7ef3cac1cc07098c3502f933851768935bebfe061ddbe5ac87b2b4063c3a34d61c8c7cad744c207cecf56eb32f260a4a1c730e7d1a652c5a887e8c890551214cefc179173fec3c9a5226886e4ac37cf195cde8321b5a49c0b5e930bee65445a0231962a02366403c82bb4d601138e2aad275b59f7275fac24fc7dba107a253abbb7cff4ac33a68fc90efd745188eddacc0b21e907b0aafa7d0df9d0f68997f900d5fe10885417a4672d281fbafc9a6199af8b3f7509bc671628b894bebbf834199cfde052e0609ee139d6c8a512c2d1713c36733ba1135b9271e3b23246f24bab69e333e2bde0510429ee4b74dd8584b4d3ee106b85aa8570933bcbb949d8a30a7ab1f7ef2b21586c304b342a89e6e06cf8c217c0dec33dc4d5168f11d79d2feae73ab787c1c5ad27d31d80e4af8ee5e6c5f8d338c6bbbbf93fc4580b3bf345a3f6afac2ffb068ea5e9fcbdc6a10b7d3fa9b98133715fe917317b54c04dd896a1189fb6031141c5a7b0810cc8fc2c012742cd1584552916114eb89262b3676c8fbfd5ede2c4d419a675e896ef4d57c519579ada976e9bec64f0d38588ab6616090fda9a45af20c8c6e89e9316365118f4e6aae538ee4fb59ea609adc7e5572dcfdaa446d59a202"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"60e54d785535b52f80ba130f8fe0d17557a1dd0c423f9c2a43db702f4a14177b","proof":"1a635c73386cbd9713e170530a10bbfa8ede95a5f4bcd4c5804ec4ae40ed0f3a1ca1ffd9b07a84e5da10feac14b0f4612a6b7bd3e067853a66028a8827d6855928cf0c6f628e7a65d840666be5ea2da71ac004b3f148ed66997fc21fd48ae83c3ef28e1a0db0a6870d3127c01244c7ec96db63b8b2d9a8301a83a17da282315b14b62fd7f312da1cf3dba76223a915e9c1c78ea71106594ab95209305c26c00314b9c4d61557b174c47914e6e87cea75b040c881f81e69c0a713c148e37d820926a11db94dfa4f855b00c5b5692b400e82f96c6a31839858073ec61852caa00b6ccfbe88fef41dbd9b70dc28f39745972325100d89f39a9fb6f83dda955a3d48c4489e7431f25a3ee7b6b6621d4c23e7face0979a21770bcb11c66c1249eec7c9287a61aafba794a4946f0cf5fa0fa7f2a5a469833a8903d20956f9b9071b830c824971f4f94572cb56ef8b87ccab38b717891054d8a29381f3f9305946f105566f8890cd00a26c318e0077a0df32e72f651a20e51114accbb7d10372f99340f127f88772bdfe2f07b723f701b0fa7faa1d604cf3800db244d3eb893e1c83316ba71041c5052113c7d993247834dc90f25ede825f09ec1ef82fec3a72317e80f5861faccde4cc919c38fa4f62aaf7935403db13478090be925930c88e05ed154e419c2665cd8de8d1754b979c282d1ce6e41bfefa3c9878d23adff393dd2d45f9642f110944d7d1463839151d2c37994b41518cb488a92d8757b423a64ac9023a28587720b5b20a6c4a2520571bc8d33497868648f9291c9aa22d8da4b41e45982c76d185d9cab9cfc8f87322c98f0e6cda93832756e1aceb271871d8b2830521121cd3790d2fb61349178ad271e7e8dab56d97c75ad5b111ec8a281c5eb410b92635a0fbd5a02f6bf2e6945a65f0084d7e934e3261fe5a1fe012a4c359bcb09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ceee5695b67d2d81a0ab0a720ce1851fdbe69be96cdecb970c1e10693dd47104","proof":"2e535882aef6bcf4312ac1c38ef281c201660a9439fad474aacea1fd728ae7780c346f641e0c22cb986687667e2b908245940f9909fe4c453f2689f45eea8934e80165f737353c2371b0e20a29bd1ad5781610b2eb6130fa10bdd6ddbfd9ec1c7cecfdd4a5a26bcd7ae105d30a1f709576a37cc15ac8a73de14f1ff179110115742001474a7c45642793cb2aa11e2ea4853f3d106bf57a346e4b0176043a8b04e2a48049073d5d51e51cfd2e83685be6c1d4ba85b4fd908817d4246e947575062561915b1db4781b0918d08edca4617ed87cb108a8eeef9dfa34a126d282890d6c329f9d38c04e1e67ae577888d0da475781b9775faf1a96d1f5fd1091647f71ca840107fa82cf32432220dc1992bfe56d3421835283fc8bfac9ac628cad3e49b0d800d7b1fca0ebd18acf8d48ae48492917a56fa7f5a296c18ad66e22dacd42521d19584d771d26bb2ba54e0feeb1806cadd74eb4c894e53017b936c8668635f0ae468d351823a14b6c5ae7748186a7d3fdca49b72c42117475604af7925f6f9cc08748bfa6d23291d2bd2e12ec756c89e51e475d498c6f17257933e3fa0e679cc6115f0cedbfe2d328a2da7d8877341cd9b6c9225f87daa0e5becc323dba30bccee87a120a8c45a441de33b817163774fe2b9dbdd0e835349a93092e263d543ef0795c6bdb30f73b711bbe82ef19eb9b021b7ae1858635d405c5e0a339f12d7052eb41f9ea168704ca5108c95552130f553766acd846f0f62ad71baf543a5f825f3f757d664300e51cc3906737a9101f6b2212f5c0b9cd5bc56f245c1dbd23cc01e481d9f8432148914d15ca658b7e387c904f1819e0bd9693eeb1ed530c054a5beceff0ed1ffa4936a8f3cf4f7ac256b011b51aeb6ef9a480cdd017f4fa0ba7125a6046e9d5d820499f35a2b93879d73ba885205e50fdc62b6d1cc09ceb08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"38cf1fdb5dbd6c6bbb99d2ca946d7bd1fb1f91be21faeafedb39a786803ab418","proof":"8c1cd72ca244965ea9c1f1c029e3e7556d311f9705c0873322f1ba546323c3751274d0f858cd0a367228591b8c1c3bdc4cb7186a69ce50764ff46b3c5231d66afc8a536ac8d10120caa2cc1553886a8ea4ab7d268649bc5365ef01e0ee542237f8b93896a9608eed3b19a1a531475a7fbc5e04a218eec33b1fe9b9bb1d9b1a4480d31f196d81cb53f571ca29794472253220ac8d43b46aaf8d2a8287b92e200692aeb26c6da2688637499b197222f608930c1f923354767bc8a44114baa2770c2f95268ca54d3f206d21a405d2f87a8c01390983e272ff8524fd53fd0e70200f044ad80d552bbc15c04c0258939818a2043ff5e6b35501ad226be77323b0d026de982b38e7160978042ad0932acbe8130427d5a9c240be3f08393fddf2289913b8882571d5f8da3a7843a15f5e6b62657b26275e24429d7c3584f2fd3a3f49279e8cd4ecf47b309d17e1b87ae1e1edc0979ff8cd91bc3d61519b5c6359b63c6082d32d03d603acc04a81b138877faaa7255ee8454516fa860ead93ff3cb4d01de06ab3acdb9b2efec40e530aae54d58f0e7e6af4b956e67d3a1fe889beb5335fe640e6f46269f361714fe21d0f328ef3c1adb7fc5b5f144c513b3eb91778a624085a7bf07e3ab6e7fd88ec9a08294a85bcfcf61862e418b1e5c3ea7da7e20f10a65095c1ec6566f98bd424935eec78a73d37e606877a64a470f2e710f817de3ad27111bfddd0d8fde4eed31eef25d7b9a3853cb1734648a1f2cdc9ccf74832632a58a9279aba83f63054cbf94dcc542ffb0e78da1a61529426c4fac36b628e02da1d9f8c1b05c8fbc252186f10f2ea2d3c2d9b86959f1411e561de53862bc1279baec445c9558cd0677c4be88e5356bb5c10365dabb9cf1d2902a4c62941e6052360ea64754b985a044479f8cf3e487b5019a5cd3ead98a2f3d5ea1a3c224a05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"40a9a4cd3cca396b0e600d3e72cabc0fa87c1181c7d3f052d539d095a41a7920","proof":"c89d34544530a298856048d1ad9b35a3dd2c9d28ae12559931654cde53e1f301485c9922c5eaf50fdcd28d71f1f680cc471b8be6fe6c45c571079764309b2c189ce6b7efce2fbfb9b608a7610df4affb33c2420fb7a78f13684c5bdbf09b7d435a615b46892ebe0bc1d7bfec82f7ed5272383447aec1f10dd7d11127fe404e0ca7393d44c2f79749cf102141690f347833c9e27f72a79c1f95d64a1c482d690bd449294781421c4460d532a479a35169409487033668a811aab8463d23936903c2b40abcb89b2c98926966abfb2e06b90087400671d0b7729ce97197d883e50f64a95862b1333dc5d6ef012d2a08d590d6420531a79670afcf6d332a848ad84eacc93af7e775040dd5aed1b4400867c7853f976064a86383f87291bfe285fa4314971a67d4ee7415cccd893148ae7d875cc59f4bc52938df23fc29255ead3f621e70de641e5fb7d775d2156969df61b8909102fa694d94462f86f06e1a9f43454a334cf54005714398c43848e5a09d93e3cf50434f2f6e06aa6b25d9c82a7f2046d33961fc26651de00ccee269e0dfe29d1e578cdb77334c4a0d2e4adf9a7a50fc946c0f4dc8ce3a7b60085b540e9c3e7be4b3879e56f56949fee9db5f3fa921da2d611e707778fc90041fe21945fa2012149a857ba04e572805fcb949055f641ae57f96cf8f611d48d1e069ce4bac742d52a5b4942edb87eb29957eb539780024a553a4246598e85c225f48709918f3ccbb91a352f28e4107fce6cd9b6286495c7573a0326e7d495d48dce152d28cda599c6f0a4aa69365acb7cc6e96dfe354ae12c486121d9669e43d80738ebfbe2150832d5f77f8bd38950dd008fe73f4175b10860a14aa4f21a721948c99a88751ac4ab9697c233bccd7d55583188a880ec19db76fbe2f6cd7c07b19d8d95dcf379fe285677da31f193d540f621c02b107"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"806c0ee40c78d23674e1a73727b441397695c115d23ecfc6e154ea123a387160","proof":"36a81e66c3ca4cc337bd3cf75141328d3b035e501b1198e28acbdf19217dc905044c4e182b7a32a407fdbeeb764d64c0151beb0efd010cd98cc4699e666a085f6e0bf689fe6e5bc23efa2abef60912746453c4204a22330c906442ceab163551248285e5fea5b9efabcc5c704d81f642a7e3b229c9b1806c0073b6a2991bd36a16cc38b9de6126419911780b69aa57e81d8a245c7c6fc984ec528feba4eedd0bcc511ca2d29e92d1222224087ef8f226e3e75613fe62f34bcd37f4de9add5a0a3c9d4cce045a381d49a62bd6daf1cbeb5da0fe10c188623cea668588c57cc705caf47fb7ca12f1cad8f413f04f30cf20db5d329237aae79cffe53ca5d5191a3f6c8ab3b8968013386a7475853133b82db08807e1fad98affab6c6ca60fc7df631aadde7c0b858ba3bcf3714f776bb96f582d014bf13cd5c30f7a5e89f2763e59d0ba266ff7c9fe76e305f1e350f7f0f503d08c7b38e177f9e5e7764a1176a17b6ce8df5a5175a483fb4fb9a3d67f1a2446be8024ac11df5a0bd4dccdb1475f6d824afd580a22315c11129329558680132ed798fadf39f30377e42ff8073c31602e523b10298240c1e6217fdd8462fc4577f270c956f6c750146fe5d867d09d7a1280383a805741089f3ab0ba9ad8bd889c5d9ca77f854af9038baab77cda5065bcad496e06fe2f4b9a0a1d777ccf6d207f65d4e1bac896780bdf052da5c929579e502db5e321eaa4b979c511045397b80161ee8240c9a0d3b207705198cc7e518226b571f756e52947a49943f99e32a201894e6b9b69dd1f8c2aac63933efd7a92a43196d317716118b71902241f582663aa99aec663cb0d14e542d02d1b2d229b9b0b7ee84e110d869c3218a3e9c08fe128adaac2a545e762e045ee8fe23c022d7d3eda4394df390dacf4ca6d6034a7002222f900ee2f58b12bfebbdd2a5e0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c8d16f076113c4b57fc7068c90eddd4388792a355d9ea62e946caae930b0f16e","proof":"209c9e25d0df92ae255709a19059cf4e5d0b952479775678c4b4f358cbc5412a561fe2f8e4febb85a72a3a6ef7b9760ec17b35922075ba7e8acc09ba0803ff2d98089f45c71af1790df4107e22dc836a59c6f2bb1f3102fe150da7c6f116430508b4049b5651c5e402579d3f449f9311b414f17487afe37f3ace11e4e4fd3c324ed7c0e7a220634cfe77d11c17db718fd953ef02164d3acefb631d09c0715f0c580deddaeee97493a052ac399208c9e61eb0a074da9ed7b1124250267922e603d42e2357a78b2db96dc1029c138a08000eeef6359f71a671f8e5e91309d17e067c32c436250730a4244c252ed15714cd8501440ae1ca0a4fb88d3a848344666ea4fc4a6dffe8a37f31a057bfa81b0241464fc51e9508a3113c0850ab6dc0fc029e037721d1dd1fa096ebdead74affd6200ec62afffbb47b6976be68c45663642b84b083e7415db785b8aeba627e9cddd1a6ff351c5e061af24dba7dbf76e9c2da4cf96a2957f13d232cfeac67333b82ef3d849b319f8209082fd06a9b3049049228dfbe99942684fa8d8a159e56f43ac4998e86f237249c2de95a74cfd4b59068c2b6836b6e6db949a9035cb9e54568e2fce71508d8fce2d42f2f15c9840cb1dbcbcbe9e03da60eadecac79a242019f980daf5ab86b8a4adba86143a0af0b96f5ac6a59685bd1b38671b52e4ce593bfadeec33c33f201c86d6ed7afb1fae1e55a2cb0aec95062c01caed67ae7fc6b201df065d052fbeee3b7e957d0c4aa43c4f9255616bebc454b1833119fc37f2dd98e1781e6ff8d7ba603c6d73be735d374658ffec7e29e357d3c3eb5d375af0e91baab571da329ad7699dfb3fec77cac01f8ce6c7aafc0ed4e97116f7b7fd0031a266110d88094ca0aec1088a6798852b01b0236fa99661c40f90e4346396990c93fd84c24d0548c265a5b2c8875c476806"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4e42cdb3ad7db33723f77f4e1c529588f016349504089130a9fcbebc82d86902","proof":"face14132753591f17cbbd3494fe43ee0314aaaffb4fb149b4c9ac8e58ac1e63928bafe132df122581e76cfb51fb5d8c443c6fb986e747cddee1cb5559da71290c8cb90ffb1316b0d1aecb780115447b60c02740e50fc023060acaf374f0db25b8b28a5b6901ada3c0cf15a7e05c5c9f72a163506157247016ab28205e3ec9504025e8538996673f465484d6ac98d4aecb517f2babca64422f678cb45b3db50ead86f073ec367a34623aef884d03253c9003320ad69f003b316a772a4b204a0f16a35388478540d393343d5164c7119f35418c58299b98c2b7a2c3254664af0f9c68f67f79a7e796d779b79411981d159f306d170a7c958ad149349e22ae601cb86194f3f6902094fd2e791d9464f25103e72df48979290c392d0b784b8fa62540afbb462bb9aa11109cd052e65e19545d42bf21ae39cb4a102dc1c2b3e159310452ba930ff8410f1f3d2d530ca95cd0e9cf277dad32ecc6621fac5e3d06914f9e6d9025a474b3cf4d32fa1b82bbceb42bac2ca3fa95ad1544a0a5388b795b033cefbb2b50c172aed2f848eb476a85fc19cf9b202ec9f4969199a88106a86f5b2cc96e197f7538a962780c6ce07b78241785cb042b2e200cf3fe09cb9681fc4980247f00804cd851221a84af6e320f9b585004e369d6745ee02a0a10e5cacc5dc423b87b4cd4d861a1c541c55102799e330c7035df0b7aa71ab7deca171ac766f8262e0dc9a289c40a685ceb71f1b3ba5979d866ca33558d6ce22801465a495b580b7b5f194a86b1d83fe6974dafbef4ad48403e1cf1cb3f51be03a535802e3fd297d8f719a696f6ba77ccba53e8ed6dc8b3ee3f114c9bc32e53013032b91368b4233465218a7b70cb344fdeb7734aea10ea78c2bcea27296793dcf2d2357100d1d92f863a45120979e18ed81f5a53d8c7b6bfb98eb5ec7ee924ebad75f16d09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"989caef54d4846e63263f0a4c63c4d26918dc81bb8bb48948d161c1afceaa377","proof":"e20b43367fc2353d01861cd7f4a11e73d473707d03d5a0ff4dd6796558657c75fecc9748577741c6cb60d55bd11c84ad4b2b50d98f20409e9a367ef7e4f85b163aa5146ad207d9b9b91fdb0a072bb77a30c15e5b363fc0b4f08f46de345af62a88b063ea268655f08bbeaecae7ec6c4bd71cc7deaca7f5f6ba67caa4dca3387460085cc24f0a6e5f69a2371a67f6f67a5c09f390ff37a9d64f6a162ab6c17e088fe8a30f47dcbc330c98c03928a125afbd077272387f4c1edaa89970bc6d660b9edf937d96a1d4f168dc94de3657f1480b7a32ea4d0b6a5bee0aeb33bf7590053436a6c480004ed8c3aa37daa416fc86a3b319b433784e7476cabbd5bccdc75dd4c4e76f7e6633b9b2988c8b9646f9cc814a67962fa57b40d48f8672ddab9671848d6332ab8c28a79e7a4186eebc2d65d3a789aa3b237e3b839a6b7d98089956be01f91a0daab2f3679ac7e0b6e102e921da6aa4b8ca07918fd7f0dce87bf00cbad801d61bec0613fb574abe0598245ed72573da6887dbee79b91c13bf3ed160f09593455517a3593642011ca7d7e1ee4c6777928e7882bce1be40068069097010c91a79e1dee0739edee2919e34b63459e28e7144ea5c475794f1371e6c67267aa6f02a7ba841ef3269be7e1ca0f9005fe1214352114d4a57c18c057acd263f08946272e3cc073b9784b9c3041c7de3c0b341429b5d08a7fd19abc38b14a33862a7db83cd526b57540f6a65b3b08e3d667179e60e72ee7800966c024e99f00fd23e4395c7d84d8d823c817bbd36aecdfc94ae44699b79d4bc103ad91f06b257c83b093267642f206d5950ff7030bc0f182199999c80069026cafcceab73734f6c5c24dd2da813d7ad30f52d00523c79e508c724d075e434e48dab31def095092e41796848339c1108aec8bf6b31ccad9125a387707597642e2c830186b77409"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6e58298481fe5295f2cf268af590cb33a1f18674d040df4571b9837f12aedf7a","proof":"a68019a23a5f04b8539148570df78c944627d3edb06c4f4156c42746ad5a9c0fd8667feabb92234cda73c58d57849142c898898ad206762ab05aeec4c9397762105780baec82477bdc87d01863403cb2303781d1dfc708aefdff8e7df7e5814afcd9b46386d4bb5620b336cdf1839de581a1dc700bce356478d9494c0ccf8f40bef7295228f65f443f348f19c4708418b9a46046f708e5dfcb7f62ef033bdf09bdbd7f65d53096cf9a10d76f5bd114799295963fb518b48d7b917271e529db062aa92757607aca1b886b5d8750b893f8697a64af124384f839d440db18ee590fbe07c394fdc0588a27b50d6585c9f022542b2e62881d25c9db6af765c2129e049c7b9dfbb3090a590050c7de17b9b630b361ff14bcc3eea78f694b1550f91b0ea681fc363c7fc4c844818cbcac28ba35130c33d19e6c0a1a33d573c938721015da807ece979343502455db2408e31f87620c84f2d8dda2827f83ab6a44bf7265006504142806d0281e5173d5996556b76b5fd3ccb721f684686786b04310d57db64cf9c4d0a2d0ba2bee676d16a8eadcf4693e21529ad329af60cf33815fba6c72e167f67c427bd003bf1d716737213a3ec8800dd3116ee74b7a559051fcbf558229e3086a744307d854145ee7246eeefb92e40bdaa2badb73de61d281c84541768cc8b9a4644ed56abb52bafa43e0529a19c9e16e31cf87e3db132621a65068e2424f2dfe4cadee1bc6089591182902c514646076dbf3d68daa76261321fc64c81416a3c76f6dc221d90320324d4cad2ea6b4b5a88a1eb50052a73c5870852d7ebfcc0d7c223e8d0e4777f7f59358df5f8863384246669b7f351e505ac18d2528a3c654619d11a83d0fe7e55eb7201004662257bd246d709356d9b165c6c607a2f670dd56c445cc9a28f1fa68f70fb8664d294a4ed5af4d43f29c9e3831130b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"52e51ee6d7fc9f23923ffa342093b5180f6c4114d46ef80ce0b4ecc5068b6f5e","proof":"ac777126d21a03d2a488d5ff37a6ccd5f1df29bcbe228274d2cce970edaec105942bf34f686e01d3b7b051ac8f675017157263ab412a549d99c03f0a7ed2e56b045d10026ca3d220e96f2fe332a87909d9373f19f2a8e329e8cbfd7695d3b9252093f7aad6b4ded54212f67b1ea7c5ef7801e717a5dc1e7d3fc97a903883133cdf548e0ee6a5ad0053f4c6a2169c5b31172f7a09e715c17439374f72a50f07047e77c732d9814a7e4e00ccd01d8b87bd9784fed5c9bdff0c8a8dc899e3bc27026d3c1c0cf99d88137a73c510f476c33157297d0296f53f00f052c53e22ac46074ab1b798aa189edce2f6fb526622159c1b6a522a4f40011bc040efe1aa2c056158aacc3e39c6b4b11841585683de4fd0edcf3858cd447e69a46e7ba97ced3151b2501c2de806012efe3aabac9c39ce0d57f3e79402dcddcab0767a996c62a301e6bf9e3b4d13366fadc4af32047b1fae433cca36351094e5ab9f035a92c52b382260a55e7698ea3656f88c71eac964f8b42565b71b268db4736eef9417eb83000c716692e5d15519852b1799742d56e74353d56bf469de8214452014d039806f56cff5cac9155f49f78b11d09b43a465441824a78cf07037cb290092458cb73dc2f49f109621d52142e35a0bdbfdae10a5dd419cd8ee0e8f93df9b0627255e1d14092f60ffb494b79ff8cde7c92d397a251ccdbecc8def9e5ac37fac1d134634803e46cb12f5fa7195dd00c95e43fbc6c9e7e4d10abe2079fd0547d91924db26b49b663092279610b5cc61da8b0e92de18fb0490d9cffb3db2a7212224d21a25fa61b508833dfd9d97ac91078ca239c37c16f447210c006109ccfa836a8501661250d9b8cef5188d2d7848b88bb17404a82da2863b55c824a88b3a966360450eddebc43a3d2bcff602abc4c9bef7cac4b144bcefcf9690088ae65a33ec51b204"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c8fe95446075289bfee3288ee990aa6c18d87d85a45db91959e9d380310b5067","proof":"18cf8864676218843819b3e2e211366120ad2345d22aea76f8dcaa56f430b9026e08bc5b491e3526b4de01621cdc4c1c00885b9d9d95c8ddf1737be135da714f022081f3f9b02537d6ee205a7f4cecc406c9dd049d6522384fb4a57bd8580f2ef23a0c6916982bec00204d0e0f3e4ba681849869acc29f7179c4ee606818e671193cf56b5e0ddeb128ebbee880b3a6242e341e04ba0ea2d4296b3e69b8c4010152b013e4b84f47afa522ce824db91b002fe2f22311b8bb46209053dacb2e8204faeb99aaf401c9313b776b275e53cf2949c716d04c4b0d5b024f54505989f70fd6cb0e25f4d297b42471ace668fd192f5ce96ba6b21b9a29db8e83fe3719af516ea975a456790506e6272deb93af3acbde29af4e2e5e20269dd1814715bcbf2876c21101446c008f7c44716b0bcde4e2c8b3de0caefcfea00a4ef32e1381b22fc608b8cb43a1f9b76648acfff52ce9366f5b9966d26c82f3a3f539d17b371310727461704f620de47c73402df74021dd7794db13936579cd81d9aa207aaf04109cfd8cdc880d350c1bd4a8d5c88ae51635f78904745ea20bae6d1fbac7b8b22218edb65fac3f797624b9085fd55fe62dd6d1cbcace5f1a4a6a5bcfbd65395d51043d141aad25c029f210fc946ecbbea5a6dcb4aecd204c8b1244349fb9a3260704bbf42e2e15ef3842864639ab53547643b974bbe7c46c245e7da23ef7e72b6c66426fcc552de9cbc6ab46ebebf3b3470086f50aadaa3b86fe6dbc99a5a9d92a92456f5f912a8a2f8cd90ac9cee95377e56e0c5ebbabb23b23ec0c465c3f3045e64ec356d75558a8c4caa0103041e4e6f92aaf66b6bc0a4c43d1fb7e2430533ffc074de90f3ea0f74e5300bb4d20dd0af86a0964f173651540ada95a6a7855081d1f2dece64e7b1d1362e714b76fe13b956c99fbb2eda02c945fd1ec09ebf406"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"427beb3fe62152548e8b219cd5facb7b0041d174714473de8559dc8b76bac167","proof":"c613522bbe9056dc70e158a40d7f8800420085be54199075bb13ef9d931d8a5a104102d82674575fb9792dbaa8e2c971ba146d34b02d8dceffdddd0145326b30e29c4a6a40bf2758516c52b19a6066e48fda3e2bb921204d613fde70ab254a1336235eb5e8c1ebe29928a64e84342fd1a73de2ddf1a57218fb2f859d25a84037ad86aadd3e8ac592b1e0bba1b7d009e8f4fcc7e447e4a8642008b4bca365d9058c0fd6cbf826f083c3e4fe4ff14e1b15245587bcecfa14eca5b0ab041c99090256cbda3bed1de41cf7428b370bb25c19c95615e81f0f25a42b6a75179035fa0b06d4c4611e93dbcf0e5954324e5e3161c1b7c61bf68db3ce23cc39bef5127e0f86a88aa9e74634985b4b4ce8a366200da285c09fbf796968830b1f9422bf8a2172cf5ef8cbd1b39da8e973f2ed26375fb6a75407a0ac66c4820bfca9fac534526cb9788ff49de6c86866fceff601ef7d164708d581ba2ec7b4ad2f8c6fb3b63c1e4bdcc1828f792c0b458875339ae89cedcef1925a37fa9b1a44b8be274c0022cc85b2f62bd18657440ecf0b635f6b2d725b90be01506db9246cceca97b27260b4e1a9daf8f000b068e0791b7050a2bb0f46509f0ceb95e4e19bf49c4762d4641c5121125463801bdb7aae799c5739ee2fded04b0520c3b07a415e1a9acb2c3698c7e86de0a1f9e02f619a2e3896877cb81f72f7d88e3fe4848d0d4f928a55112aac535d590162cdc54cd1e871b85951f9c30857ea164e252ddb9e156e36586344c27545c53e7c550ba108432b624015d667e5899fb9c3e914e771b2f7539d762aadb728bbc99f10e0c988b349179746fd8428a8c90a242db62b8aaedc210f12595584e6085dea60a65ea313c6373ea504d25512cf94defca3f7a159ea57c809eb465183e7e46eb5551f70912c1e8289162f3b03640ddcb7dc47a5d2ce80d202"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6065a2b5d1fd5dd9f726817a5e9666e6f3a562480b8e0f35f793a062dc268b56","proof":"76ae962ddc3167a27a9bc2283632603de7ac3e1427457e3b38fd8b1f3fc7f57aaca97f7c872043708ed28f5a60c2f22e50bb873dfa81f223b899f09800daae4c2419dcdb77027ede0aee54e839d9c243b2076a8786c1763dceeff2b97fde1345bc2672d69feb2d1fb18462977963ec597f3e89c5eba0921fc79a944ffbed59241324a2a76fd3346fb6e328a2f158d1d68fe29d18991548a41cd3bedd55c7580f03736d3fd05466ab90a979a9a0d53ccd19203dc4e3a46ef388cac2823863070ac15159761e75af4a5ac965c0f071ae3160ebedf767c7d97960223dd62433a10e66ffb3d96d7e521e965677405f6916a62ab0bbcd54bd181390026cbfcece474ec48b7c6448a32103769d7a3c3fe066731c9ff2a97f432c3c03981d68bf7fdb5aa2665e4da58f2374b9bcd9fefb569d17d3a5b4d87aa73391faecd52550eede383664944e5adbf7c6025a50c08b0f507ab33ab4ff183d0b26633c62be49833179b21312e41b24e89c94aa2f9910d1cdc039d21846d6d466ef3fdf0906486ffa154ee7129dd2c1b4f8b31352dee46124d108f0a93fe3b104925d8405ec53e0e14b1cbe481e03478d18464c8236952bc510b2dd4330a6e21935293cede6f933af1468bf3a37907e90c39e014da227796f1d1eae310dc7197a1c05ded9222713e53506a7db43cc2e04141953d1b0ca106732d9c07455f6c275350802db0a0dceb97accfe829913fe685f105b1ee9d646d4fe5e0953cd6a7a163cdb3bd12e6379e5156e411a7c18ca0dad8796e712726b5f562fb13fa95beefc8826605b5936ab151338956cf31eda282d3f70017937ba212ba2dda042b2e9857bb5b1a3c4a6291a21a5d1914ea455bfe65eb17ba0d6a369149cad346d6cacf0498dad27ac2604de0b54f3505d8d144a0ba0f9616200d3049e143340569ff4a10744ca9fd78c756f06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"181bde37b58eeb3fc48ce4fe2898b165a5ba0ea09fd3f6908bb29494480faa0b","proof":"06e6c6184de7510e320a903b04974b723b56dd38ff5bbbcc61b10fc1edaa1522d890cccba44f99f1320d5cc623c17626daa701bd958470cd9f7c02fb70eba74e4e9c90197648cd92694d7a5b6ed96203534cce23c4bf7745f2207ed363230c389e9ef2fdf32a670a17dbde3f021bf75767b15fc06479f873141ad24a90c7db7d96ac00e05689d0bced0a9ef87d286cb20272eecdb0432ced36fc3cb99796c003dd19269e97b95f1299df645c7f8772a7b3e700115552be173ddc701666dfcb0463a7454ff4947baab7191901b0f47655ad06f5b03b28850145740da6ee68790cd635787e2963147c9e70ddd9e63cef56ba9e0ac48a7b9b4a317e72c309babf7cf2ecf413f50cdd37caf2bbaaaaaa3934dbad4377e6c9e579866c0c12c869e35314086908ab8d271f38c5050896f050b7a8720f48fd0c7414728f5da2abed6364b4f3ed72fdda6a3b3562eb47a99517bf1aba2d60397b79caab9201d5bbda3103544d6f82e903d5d8cf4a78a64cd8da1a86452f74abf4fdc0ee36203d70fd816bba5667dd132f190a2845a871607fab4faf92db431bc4342753bb31e8ff79ae3b2eb517d02b005faa372ee092322025fa4296ee9d0d6d58cbed11421bdf8a306e2cca3a30b155b6b515f3ecee6fda5c0b45ddeb37835ea95e4c1e34514426df73d4c1b44b1029c31999f1008b5db17c69f4d5100a337cc825f5599f00a9794711b0a1c2ef9090c90f9db48bc437e2f3fc836313714439d45b862cc3c63ff3bb74ae7dac8927e64f6aa7d76b1442d501dcace34ad98fa155e478cd674a0cd74d7d7eadc2f498b926480b878bf78963c86ef8c5ca387d9e757edd26a3a491f31c7913b8138e413f3f1e0d0ebe355355bdd51d89882c82d0ce601c6ce7e530a952086a916e31dd738a6436da0c11b9fd6c97a882c6b0cb260dcc955e3b3dca045900"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8e8aa6eabda020e452556c1695efb43d23056be0802a12c9a5041d8f7711cc3d","proof":"3854d8bdc9512341f8def36781c5f9de5be7f1d83aa47f245f1668dca066c92aacfbf3346a1e40d214b2c2d34f2ea724b9f8f92e7b1444febf2e2a4f51609163da715a455930d5f68410f4019b7515bcebecd3e13a8b03761bbfc3d962c4135768e03c153328268e5ec0280a52a2873fbf09adedb7dbb3e4d99ad1f9b9f0c30d084428385847cde6bb78c92a728d666e8ac1ee86c0b4dfa0faa6c3266d316d0670ee23a51f3b9ca66b1bb50f15fd8c870ea783b00243d8c8d73c9c8b194e3f039a3273a5f95f3b8915c36b9c9dcf06108c79345c8d47506c7ce5e87204441e05fc561e335f42a3be3190df123f02fb94f72fa33165cd45a4c748f2768c522b27485e39028a5e2cdb6c39449fa595cca42a380f23fcaf3962b4a3f2d6eb6f1228c2bfb2f8341a8ae42b3bbc49442bdd5954c62e5d132e1d3b33b3f60ed825404ef6b0e2dc25d069aa00d62653fa8954bcc97b2ea794b41719f77c4075ac69794c3abca203ebee06377941d241a8b7dd1535d8a689f66bba6ec37352731b75cb522458cda1104e71064ccc708c932d9b9afbd635f5961846292013ed93f26e0c4d169c4a0f5ddd1899f5f5f9ca3bbf5d764584551e5d1edc7203b34182ab1e5831ea36df9ef94a204142416964f185b43509611682cac710411aa4844bdca32d61a0ad531f931e55d58137dc4c2a7c46283f9307a498006dc63dd63cc062486063a40888bf9fa9b0bc9f7a2a2b9193c4edf0314b50186e25f5c970bddbc1fb4c4d1659120a065e0342220d8d94929d56237e3995aa57062c0ee69ceca6d3bc747a863ce1a9d3d63488fc5943efdf60a62c7708243dc132732c63595625fbcc01702dc4b08b4b968561059b5c1c83fa0d9f50eba5af7648b98ab7464f5293efcd02830a0be4e453b9ec06d25c62938a719b59356a84040418392134c29c71439a0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b09901376ae6d374cbae51f7b1dc943c88467857690cd6f41c6027bff521762e","proof":"8cea54362497ea1ba1efbc4350f3136a358aeab89f45dbf844ae95841f9ffa2b54c2c385368c7f519ccc4c766269b030dbb379b2c3a1099cf2974beb4fe5bd2a8eb0e049db9bca90dd4c8cef6f06e0bc6cceb41c9f45cdeaaf48bc44cedbd63120f431ede201c5ec900728ffb58d1df9132d446f138fa7f7f410890e91491d2f2586a8631c7ca65dfcd53cbdbe31658de49f06c8f6e82c22dcddfc4c45a70f091fcdaf313ec722b1c68342a74e20a35a0039f750842fed1a1d6dd99ccde9b40646208456aa38e21ae20a6329c8232045b3c02df23b47cf48a4be8c6f544bd809046e3d77c1c592c878c9c6f66222f9991af51c3ad51f49d0b637760f9865897c28fe14082a6e1f8ceff936eabad9bd2c617a086c73b0d445032be30cc6eb7f67fe23306a54b1e7d472a274d569e9b22019a310c562a9530bf0e3dadf95ede45aba478ee745567885a9699e02406ad1d5e182f1bca7a7c078c55dd7ff13a42c013caf079e64965490e4b8c39098c8d63ba1c64a586855127d46f9bde08fb1c76098a34816fb79862254d176d80f944d56c4bdc02de1aec46e046422eb2a575c22a6d1fd644738969b0567ee1c0912028be7e3ce463aa467073a534b3f1a17d32ccec78113b9b65f49e1c138cdef632e251129ffaf5a9f38346096980233e5c65b742b027f15e8aa7c61b72ef5cde9250f260ff0fcbc6669ee2a4e112be4e639790419e95d3e117bb09407678d9ad1aa1ae3deb41cca2a88db1e2909507ce5d607ac1f9824b02c9617e4b3881172bf41f9cac1df53c3816b35ed34e443d07ae21a3c7828409bb720e2c3af7c21f29be448af290c1857414fb8c5e3dca9cbf1b470c3073e95403b2ce90880428e6a880f3a7ba5d606d78c03a9a947c3bbeea16f0751db2b6bb609635ce28fc37445f19711e1040a79984c416e3c1cc8f6cfc61f03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fe17d2fec5dbf05566a68cc535d0b304d3f6a6ff6ab2b3616f0bb4733588bd7e","proof":"fad2fbc389117d3691714f7ee20d01035db3b4cd318673d71118efdbde7b3e4c1094fdafab82d9107fd0ed779f4122335dadb36dab1375679b43bcbe81a9c62bbc149a5cffcf3e39ece80b2d64eb319c046662a86c43ae7c384882a8dcc0f22ec08206ffe4cc23fbfa195ac51e25559e0de8f27904fa0bef3d31fd7a5d75274f9ed491bd2321f1aa569e60fc521d5e5528cc9636bab5dba9541e474677dd790106354eddefe9ddc81a7c517139666fb4cb99effb7ccc3b8c93090bca5298e30d1a97dc4c85d34bf649b0e110c9cbbb271d795fa148d63bf00ed560576b085d057ea1f386d8ba712a305d0d25d734074eeca5b9ceaa4de6e63629c4cde93d0c2aa2c159f4d1978c9d3d464b9da6175e7f93f9a47f833310eb347048c304bf9c6ecce99c4e6b8d04e38ca5fe99649fa325ce5f1d2f3bd221dcf44611e918572d221621f75ac87042b912c5acbc6315fb27bd5426e4c20e958635183a2a6a134d1e72fcfb300cfa5cd1bd2f769de656f7e8de6f7e16d534dea0b1305b029ca2b77c6c0de8ade4860365fbc3d3624e004035e17122bd8f942023499b0d33937a9060e493fa10bdd74b600c9bbddcaf09201f79386be04aeb79078526ba813ebb5e553062c2dd9e197b958cc25c9c6780801554ae9d766057e28201f4ce8113ce8b1d6cf452d263312631c0172af48d3771c3222437dbf06f3ad333002d49469fc4459413844028c0333f7aea6a606c0dac636ccec524174bebbe1343c4d0e5c49e5a60cc1d11349ed653a689e9873a83f7b82617a51e7800f8f7e29f10fd37d6e64f9a5ae6388dce1a251bf17755a6fd8d52dfcfe39ae34fee2ce02ba023c30a977fe529e027a1424e7ac88a3424dfbf056ed47b30c1bd7c6163bd796daeb8114003162ad7f0745baea8e8683ab78da1daf84744b378a9ac667926f9c530e69c650a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"023f313d109eac6e0e9107e79f331f3355463124f1e7e09497b97e777772695c","proof":"a29d7223e4ce11db308f94a565e231ce9242269634e5363899de07dc95216676b06edb00f8d5f2f69112d8994b5c6a2ae34896e392a2d7d79814ef6bbaca485118a80b1be83f4912f46b8659a7a342b76a664f3273d1ad1b3ae284a2b36235602c92dda165172d41e221f3ebab2f8e971a3216d2c610f088d1eca5b31aae167cd87a98d4111eea4998cd908ed07cb8d27f8ea6e0bee2343fb06023579bd41503d5b53cb4985f0249efb311487aea1db4d1128f8a5e69c3d81dc27a5b0455410fc81318b97440b8a7cb5065cc115931c29c151773f4c54d107a737741c0a5100a201f098d06ee5de82621ff823af9de20c8108507f641e440ab69bad169aed8277c07c2cf6429bd0a2f49bfc3587e4a477ad0d9b4c6eea4a6f223820317fc426d02b62ae34bae2ec701a3b1620f07338ef74681cd3cfcfb0965a3b19bc08ecb5a82af77e40428c9ac8caccf9b657e27d904c8410ac50fa50d3f57faa050d1fb09749326b74d4ed56f7ca330351237de5715d5cc04701d327c2beb73aed61b7f1b70778b0ca6b5f59fadd858dad54acfd9b83b7f7f3a699a73967885f09e714e423234d89cc87a0427668e38fa31496a22452899beae7359bd9dcbae55c3668a4392682a2068a04e7977f4fbeab5224a86a27fb44d46a878966be69b96cbb264485606edd438249795ffb877f31415e9bd2a52861fda5fb2c9171a143422e59a19244a6fc64901820ee27b7c39fd995dd8db400a2a6a9ede06220c3f45b659915ca4d30ba5ee844f8b3d2abff8f83efbfd51157ca72a5131732e944aac5325c5336821d2794d46ac3c43e0fa9eba72881de8aca10550bb94b51f96e9da8534787b91befe95f287c56d604e18fbd3cc15102b913bff722664995844a3f8fab27b0769e613e30a11ff93be8838d54ee48d8f9ea5c9c1bec4acf25afa49ab858f7c0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3e25c41342cefbffbe71dfedfe2bf8b6734932b5dc9d84f0a36cb240c73c7644","proof":"1e30dfcf54a2d88e18a3ab1463d495cce31ea8bb3c1ae3ef7118efe5432aff5d7288581ede41da15b736bbf0d5f41e59c03067b43c285629ca891bfad213c715b87d6bacb7325a9292bce0797c07d0526e58f309f0c0a5538c4f84d85521e1504c95f85214482babfeae0d51521dd6b1d58004d4473ac3e126f596fab5f99b319abdd7e93433fb9cffb3d48a91a07c8817393b0788ec83907831dc6e60bf3a0ba8ea75e0693434dcfd8aaf43239477fc6fc8f21688b03fec9c1b452c3c7ee4009b4856bc4216c72dfe9fec0c39be6d78513c5f6bb73243618e9889089fc2ce00281be38edbd0aeab044723a3a55fe43afa13dd89b783d7a06b1e4a9e054e41419a32685e6f6bf3b832c4c1439f2288972e8d3881966642169f14211288325f372c24bd2e9f0125adbc20d10284fc9382a547a2477a2043683892e9699e13385392ced5aeb66aaf49d9402556befe5d0b00c7fda5814278b27d2e26771974d246aa01661927fa3a428ab2b958c03b1debd7d50874ca5eea150b5e35189217772392deee9aaaf069a21f5cb3636644e7242eb4dcf4e481fae828c9d34d4bc5a85bba31aed8fee81a69c4d67c7b4d247a53d1dd651e9e2a2208a1d00f63a6bdf46512e0bd3938615db51cef5c7946479b90a4b15074c503502b8a604c3606d75616ae61a967c6d0e2bccf5b39fad78d7882f6dcb4264927902e5faaaeb460bb4c2bd05a5cd82bdcbfaf114c68b865197222779e15bac3816953e22673dd71450035aa33126a529a90e814ba63167041e4ffb1e07ff7426df284b9be9d13a6eeb1163673f6be37b7e6ea2b17052bb176ba21b0cd0b864d7795ed9a6ae8571d6d4a129dbf45b422566b670fcbc22b3c2a062ec002205efb7e13cef352367999d7aa038467217efd3668046c123b97d002129c93decccc300bfac5fa493967b33dcf0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4c6ff9a2743aca94d655ee564a52db21c80dd4ded51379b89106b4aed472b43c","proof":"60fda2cdaef7bb75c489629b3f8d2616cd8778eb56f6d4e2215f3206bc4042666083ee33490eb007fa6c369693decddc826fc5158754d4ca44d482d35b4bb64a82d079c11bcdf77f85091e250db04c17a3f470c1ab411303431532b3c9031641c42a31cf6a41ee439dc2ac605a506193f5dc9d9abbbdd5720da918802c283278ee7d5663cf17992782bc5a816e4d364ec9bc421d97cf3fce595c28b3808eaa0e520585cc587225e9b12b2a7d2ac4ba354f5b7e79d3b8f2d4345acab8ec0bcf029b3d45c98a2de9028d5d531f8d481c67f89b8648f5df00d56ca40c811affdb0896698d7f564a2c1032510272c8161f6295fbd8343a242eef41e0ec7c50afd86f6a31b48c0cb24dde7f171f59cc3890bb767525ff710915d2979e9396e9012d1cf879bd1169c0cedf41ab1d395a47c927cb4f7214367d936f364b47f2c6806b39ce0687f3d77c51cd55471ff1dc94ac53cd31a0e0ebf87aff593e6578fe357513e849e4780040ce2a190740fd988e66fb475c7b83d40401c342de15333b802119f6c3e59980956e8817af48ca593995942bb1f95e397372428d74560997915a06f85af8b0e9c63425fff3541791fcb5fb58d524354ce065a46b1b7d6fa6b97b377656d4df32dea441427f6f7fcad68e6682aac037fba4caa53ed5f051aeb02c141ed68c97ab3d0650158a0cfe4cba6dfb7a795fcb9d5dd56cb522971f4e183b3644c6989fe395112e6f997ed47a6718a92df18022e8df9c99b45d368aa0f15468d4ae3b95fcf83a69d08fee959b152cd5e602af4f1c74c74680ad53d38d97327c709d0c9d43ec991135df9bfd56cf429874aa348d68691c98ca011690ce13ae43c37d31399240599b92442d7f7e0a42d9391d4915d62785189588b43413a6b6052469e453d8e60cca76a61a0792a322f851317e72b66fec8e540c92aa8b71a201"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"148c76c0ae72a13572dc83e0f5037df3c779c63a4f3ea2eba5697875892b1b14","proof":"2c161aaff5b87b44dd77a258339501bcff4abfe9d0dc8536c36157448893417a1a8f4a51ece0cb7e22989def1df50b623ff30db036fc20b05733ef8f11170721780cf959fc046ee08799f0362c696b09a6d9cc843b1742730d5b4e9e4a4eda69f258858a95fe65d37ed4523a315ad0e8cd076c38983a04cf51f6642c304529440c40d6bf1f50b2456e2eb5090926f40892c52a3e8610c8c10c0cabf5c3e00f015a8161fb16a0d5126fb02001de76c66cfc90d68a1e203e946d1a43d05238f4010484dc6f8b2edc913bb81e9b2d050b8fd75cbfde45a2c3f9827bfe64a64a7804448af452c6d58d8bbf10a1a679393b32728a27573c8e5fb0127368e4cd7b9059fe0c898b67bf3e5a0b3f1bb383f7b960a45b46de38e2a3ec5123b72e63f7b3033a69dd3fd559930ca0b82152672c5e56f796fd14ccab48cac411ee5296ecd636a83b359bfccb13299b649fd926b9a5381d28ea7d544c6fc886c45c1238a592220088c34d8595b19c9385ed0931446ff0be18275f923ce9b8171ea11c8763e06a9c940354d09d051fe08b06f616a2434a9bb6078a3d9dbb0f1ad385413c5b6b7044baf16bb076abe9b141df377bcef935be7098fa2976c9c52c3760182930a52c90de0d7e7973a9016457837bbdc5c1eb73e94f548662f04db742b52000c7af203e9228398a9bc4f31d035dc532154881d20a26fb90b83025cf8e4506f95b56251ef73cdb490dcab2a02d73e3de7453d8389884fa45bc541c8b14237837cea82ef24cf7bdf8ff83325959923bdadffe1568aac5f86e359e4bf74cfe3e6a0d6c0d4c70048c6b530eccc76733d8d7ff71243386bd123c5ae2ec2371fe31cb2e8341e42440240e57f214fecd3b7c9865c6bd26e24c580ba15a159e2b28f03a1602012d744d72a127f2ea2e555b059b584f5fe3165051e9b566660a6a1cd8a50c7c03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e87022580a9368c13e57a1e20d18651737d97d5845e25dbe058745de42fc6625","proof":"48577a5d3da050851cdef059ba293f0f71d9999e2f46e9a36dadd37c8587ea381e47903a11c539ab081ce4121ecf642bf1fcbadf7c27008b23ea985c4a2f4c094a803632b9f17f83fa2e6e97d1b4112e945c32cc15ac13a7779ebbc11926f003884b89db02e1a1a182b7b90b855e54657cd160766b80312b10f14e6156eebe24d06bc494df370eeedfe370685371e41dc63b70153f4e3eeebf60b761f6ccb6066fa05695dfa75910a892f2c8da9270fe55c9efe03c1e8abee2a8c6885f67090177e244c63ff701e320217a5e562516fedb45ba9fd0b718126c0b3121cb380c0c3a509c79bacb998e4c232e3e306184db37abe567d4c1c963d6807d7d78e1e76ac874202149f47804bc28c036cb3b2b8f00d0e3a86518c09d36a5e1a45f16ee53b2fa9f4a20ccae93d23e6daa19e97344f987fe820685d523e5d8d2d4b336e7191c9a5bf94a6ec3600be91085eea11efbbfc9d2407a77aa9e013776b7d29187673043d7090baa1d88491c679ffdbf552b49c6c85255b1eb1ccb97b7c8df1b9b6c666e0faf6a002fecf9f526c9dcd260fb72a57b7ef64bf616d1315f872bed346ac0aee3892a1b632b47f95f6214e455323232077966b38bb84297ebd5ba20af30be3875abc6eb18191ad951c417130a1aafbc12ce52d1152a90417673a36faa45bafe3410eec5ffbb863a2aff2df9fce719598d0238648e1b0a536b1b5066bd39264d9fed396ef0610039c5f07b6efbc6445985a3445cd7168017ef40c1015d2f98521c81d1a8d45e58eb0b386d3f25623b98c2c4ea1ae7f9c68cb15131c6b11d085e67d28fac3d9e789c73ab4b74601a5a86ac150cc072402478b2eb43e7f707050a0a280c67ea728ac3cdeca4d25012c39295982b3e83f8b9bc777b23f3660c18e1577281b454427d0aa1893812aaebec79fceb0b1ab036e8cce5ab24e76002"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9eb95ea404a92466bd87f2e5ce5a813109f8311ae1fc5930205b4dd522974427","proof":"b883c38680e567e6f8b1d436cb196379b39d47121072bb33a2f35338fd1dd72802497dbfaadbf7be222d72d29887bc975ef303cc285d06fe81edf8ac56ba340c4a63246962ea79a4e933efdd9f855edd77b8eec39a408cb4b2d3710ea2be2b3994a57af109fed4ad3c9d7181ba8e8fa4eb542ef413377ec0abc9601917eb820def391fb6eace3313f4854057175857f036940914eb502a058224f96451988507cfc63d92a7b34cfd29bf6993a453978b97dd163d912cee344def8ea02962df069ec7473f28cf370847b4df6bf14374bee2140e0dbfec71f2c70c907420feda04bec237a5640a7fdcf7cc2357d255147994ca6ee028a08cccf2e07e6487f79036b6fbbd087c29deabf510368c95b2d743b6e9720c3653812829afc0b631b4960bfa656b6e331e78585843c862df67bc3a250dea652f0814da921a77a7ab1d590724ef4701dfb0716024b41a9cb33d50952dbe5b9ac370552ff95e827564654375d29cea4e32cb0fd2ea0f7cb51647b97ddb0503e507fc38ef85d4fa76f854f468d690b2b5c4f01be421176d4d3e47de2b809d42e9f7d2fd0221aeab624f9f9a719069efa31f742c2045b5dfc57295d3557bdea5d3312ae4494159b9b4bb70426676c95f0201cf886fea9b83d328bb9fbea38dd49e45d37cb1d8f272c0e055a913ea49cb3730efb36a90ee41d8a3c146aae4902f69ea17d7e11945eefcd13f007b944ae709deae2fcd4ac2918af53e471806b3e05670f73b57adf235ccae529e6f3c9375a7bc965fcce0ca1380e9cc6cfc7180725d8116f7413c04b86ddad86b2d4e2c27e07bbf3aa1733b66ceb692bbbac3bfb0c2065c37862e979f3d1cdaa9169c9ce4bc6768a848c464db2f1e77c6bee6dafda0e0ba47aa6a74a473c304450d6c7125a0406ffcd4172f064962f3785ab7c635ccb9c9e5eeb633b437f1d6c209"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2239f3f8812f7c9c74e210017a4c467d5a788423a7ae1eb0568b12bc7636cd0c","proof":"e2abf0b8496a34f8767312ef723d92b04038d9d57b73dff8bace1b372ff208330e8e59812a56bb063d962425daca49d1f2eee5fd8b4aeab547c2684d1ce10b1e7e1263a50372b8174af968ed5a478ea91d96dfe1dca392bf8312431de9950a5720f48e2619cac5d2b313003e931e5e43be212027d091dd30dfa177f326e7d5593b81504bcc4adc0e9603b1aad2c63d6a3a8fe9aa883fb64429ab222e6bd7f508cdb0df324aa4ef44f7680b13f6329d31e96d7fd0197a29e07546a7086d80ef015422fcb111ee5621585f61821a9a861f02327302243ca246c1813be5d5ac79041cf69c02b7ca81bc78c1ba2366147bf04a9455fafab84d9a0fd8b160b65a913a8a7374cb10e9f9919adf1e16435fb93b3a1ed89ff023b17352768f49f122c723a4c0de9ad84fa4930eaf13c3ab0224806e2c48a25cccb77a8cd83c3717c24d2f30ef1b3e7248875d716c167ba42b37c985ff814ad003b0475848982119f4332c6c0e3284f1018a9ccc7deb2896a3f8beab6ea3c3e3a7815ccacdb825efd3c26bea27b44eeaf6635e2bb4dfdeb47bdb225811b18c5c63f98339216d68d870d90ad8208112c79497ae531b8a295bc13458bd1eabdd9694fed1a6426a2e1328644a427236bea663f37699a462d987f1d83951e2a8eeb4c57d350aa5214234821f1a76972aeba9115121e2c9b4d5611cbc2846527266c9e74855f601ec0a13b7c16a5ef11b10b51efe4714b103d421114133cedcdd89ba20725c4d554a545dfb762306beaca25aeb0db1a761a87ae2ae46182f0ff3ece8f76dcfb10f224569b5a43314709d146b8849f8791193b622c9e9f832fdd2edb610104a5a5f82e395b6671899377c8a418b88d364d82eee9f7da4a671bc332b1ecf5d5f6ab4bae7ee039207c5cf579a9e77b14d4b7e242d58f73bc98f3623f0276f7851e09bdc32eba02800"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"da8f0a77a2538cb9f3c8d51eae864ecd702c7c37d6cdbb45d17d769e84ecdc51","proof":"08a7a6c54a56461660a659df2487d7927f5465ed6b6cd741a4467297c4711b6780d4d945b1ed0e06f65c4403aedf9e6381aeda16118ce0c07b601ec719ee1223d62e3e87ef6b5b400ea59a64b2d617b842880115e5c49739ab5837d3f909680a8a5ca2f82bb1e45d17803221120d1de8dda97bcc5c8101039c152f453b477b6a7d9726da5fea6c7582ce32956678942933b2b9358b72956df86b0239a378bd0a9e1f36f4d7479794736680a4ad4839c16dc06d8a7d9e50808c463f2f436a1200c0c2ce85ec4a0cac6831ce0f74a3199be6691c94d6bba18d9794c33b71186508deba89522512767b028b8d8adf95ce9affeb2526b1515dd790d4f980cf0ea9157e60ffc5e285e530ff6ae57def6559def2ede09fb4789b2c37468f872d460e4094300e33f6a64908e4e2d9767558f30143ca8e8c09c74c77f8a6c7cd72c58355c61977383231b6d35e520d06bc57f4d29620f3c3560248e7c5865451dd16aa19ec4610383e5a274dc42c6ccf8220e0ab49bf9eb7512cf3ffac6840df9de9170a2c32ed78bb48b37315a04fba773e450e9683f78ec72d18d991af461ea01d043204a57ed0e380e4690c9ffaee3cc788a9107690fd32720899ae5edbcd8308647c9a07d3f1a915011d1ba51ca0aa12e77dd43159d360279ee8428af29e95953553e201308fb30dc98fc307ecaafbd5443a6c61492c1a154c43c717c151a0e27e0e66a7127020cd123cec46c77323bf264447464442d06a10056bf34c88ec03787a7274358554c6a4585c24b56cd9e0f90b304313aa327211ca720e7941836d2f0f36fd28090eee1f999b678cb3059978df5ba3e7b7e21b5ad2eb24f31e31d8897a23e68e63e79afc26402ca6e9cc66e8659826b5e7161d222de1db564d57140e034c9bf8b47fa374f71f76e7da899195acea4887fe532c5a01cb0f7c89b4032e0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cecbc96def3fcff49d09be9683286fddeeb9bd9949a2553f05543f4a1ae88e30","proof":"808a10da0de60e8caf6a5141f7c9f41667a3ebb37524b52cb556a11d8c104a5282747f855ab04a205df182c476ad882daebe8366a65d9cf4c303f7e50ab3db792e087312bf186e8083c0143a9a79d290cd167ebaa9fd2b6df4d9bf2408843875b8dac2e6a042cc95341c8efafa29faa0e5858cbfbd50af99ff8404fd5d12022f77000936ba0a89dbcd7acc32165a6e619669f72e879595cbf13d0372c1c6880eb3963170076e54bdae22f1ff06534ad5409fb60d2d64e5d80147c0dd6e4fc8045543ca01d46c8e3b41e90a0db7f57c582ec2c171a568d2d70531798389f3190beeb97e175824a693648f2fe7f850a265bcd86679931e05d2677fb09557f61d7c764da637033421cb65d04e643d7cd8a475b2a5869b450bb15e028cbc66ec9a11b43295814bec27eab0c86d2bff10da7ae4c5cd72b4fdb6f9155b86984a83516dacee5c135967ae1330805686168aa206f182fd97bc3c4565278402ab2ab76e5448a4753ce748ffa006e545597e5c94bb9c824abe4315c17a0b7540bea457f37fe8b2aa817364717fba975fb936d5373b3f95892b862a827b4d9a26abbae47a45e25645abc8057048b631af782d69c4d5aa03078b483ae80348dd861a1158e51beca36b91825c087d99ff789716a55c68cececd8ae1350b6bbfc468539a349373c81a6ce2e488c0b20a0f25e14567e159ced2234473ab9c4dc2f46c29fff99f6cd099f0be6342a17551b2a1bd3ec6c637991f93d2ef1399c127e2dabb81a2be7948b96122adcfe982e1d79ec78588beca06b640e28471aca31bf24a90cf253727bae5a3b2d47bf3cacc4aff95aaf35a356c7d3ea5d39d2531aa23a7ece3339a7c55daf72610801e8fe288caefdbfccdaa128c9b6450daf91cd5bd9582aebd130957b1867af0e8c22a766d38e90eb1c2b2c9df23207d430367e01db9b5623c910c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0e533ffdb4c5e3ce2cb28fe7dec27b1f233d6828697d566755f5aab41910346d","proof":"3cb429953bd270d0ceec8ac78cd99dc8617da8639b591d7e745bc176f804e13c6a20bd60875d87d9d4f7068f16db00c0505732577aefa3c5f0920a3e260cb22d444724508a5adcb84f24f76732b4830db4e85a8fd401a2f0d2fc65b65ff5e81eee7bff4ce3883284d58747b66ebe26723a2a92cf296f82e86c5139353574757dadb6a6c0e0f0416da99e09aa5e522fd8b14736c92ef3fbc9401dad08a9123200dab5f8c4b486c95488b6fe4a2e65f49ad0ad25db506cbbfbff96a809df18a9092eeb218fe4bdda956c9c8630d946b41272fb31835d35e5ca8517f3834eab570b7493e527164a883477d094199aa210bf17c77c8784e9a4b5cd4c6c0d15035217065c0bc6bf6ed669f8f3d17cdab2329e3c120dd098c2258177b77c0cfd93733752e170c805d2b8199e19d5da17003d043550111bd82f8ad624b0ed3cfe5a4c5466a617c38aa3c31dc0d2ddd757bd379614868157095e5562b4968b3f2fef554c58ee9dc9800ddc7d951ee948378a7cabe232a53a43f76854c6415875f5bf5c0c1699112e946391e4e6cf058be867af408613dfb0b93cb759d88317cf33b3bc2b767b3b0664515aee54831175049a0b605baea592426cd53311141e2a8165b25c8ebc58fe78f0153dc8253d1d45481c486d0e01f910d3690918a2c7cf299886286035cb6d69cd02da9858edb80fb370f977c3d1b4449401c7efc3a1dc4419fe50fafc5aceb52ac6a26967e3a8905ee4282d1c3a8ab70b7e5a80dbb3f0829b6b72c2958352aff20b10f626c5bb1f8471fee045679f89c9a1707a38bbb3c1238771743dfad30b210692c091b2aebe37c7cbdf8bd8baf1b83fe0e5d47164c7767a72c1a05b85504119802147ed11fb6b3b82fb3137ff28ffe8ae9e2b9ad1406fdb0ae3f892219693180f8d5adcda88c298846f490f1c316edc400b59ad3e1ba53e05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5219ca7fc9345636dfee9690e06fee46b1739a00c858f76abbd9dac4a9c6f615","proof":"d018730b2dd748c895b590f4a98f3d354badb0f1f81513528e409f30a6177f6268bb72caf87b2af030a57fa4581035c1b1ad94523bf504bcf1ff6f60cc645a45d8d3ce4b838b2877374d898e0b26c07acd946b3cb172b79fe62c4bc86858b11ab4de82c5a1ca6355855ee5e9f2257a73257b21d40cec87492ed2a5de2d4c6a3c4d9da75c7aa0b366bdb9b95b1943becfdd719511100b1b8eb00eb72aa52b8c0dd858153135e52d3b2226f0a663924a3c25f784c09dd2d31b62c9f4e5b2243a05e1c7ae71cd13a50f9c0897507678280b298a0e974b9f54d21e473b6b5b0efc090eddfec51d979ee71f55e5965937f860d6d2b92f620b90e161c1878b6e12a7057aa2c68422f431374de6f37ef5589e83eca69031b3c0927809cddac77e15b9142c424f727149d391db6a9549c5b12db0a297e8d2ba5ac91c3f4f51adfd6882069673893b871a7e32981fa8db86cd8ad77c5697956a3c260973b5dbc3afa817304ac6160dd1e1839c4705122c33b34eeb3a6cd136a2877642fe6099ecbd511f738a12dd4b37049d50550651b30dff6b919bd3b6c83089badf9f813614bafc32173cf2925ce294ba533b099699bc2866c91090816c32deef08b476ee0e5f5f4a06103404c55dcc66cba02193e0317a91c21189cddaadbde77026fb0e996471ba1b90a83ac316fd732b42b247bf44356bba2dc259a31a6de636e60524308c11ec6d18c2e86e8fa4504f0fe055da8572ee0317b734fccc625c5c3e6250613e6ce45ab4df92c3ec2ddcd2b585b24139cc8ec28aca75819ff2cfee416a9b153d59d7064cfd35a8da6e26443b62ecd18c4ed84d93f7f321040f240a4f3194810f8a1b12fde0bfd3daf057bea5190e3d50e579d65ead1ff165a0a73ff89fef512c87d70cd592acbf0a21a66233f4a330e2aaaa56eb564bde625ec6243e144e18fefda10b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1a0d4534b058f3f9350e5fc1547e4e3716438326a8d56f9c4a88d10985cc542d","proof":"c4d7df932b2f5f709884ea49da3e5292fb6262d4f9934a11b2b59d081d5c285610d7a5d306306779746c0aaa2e06397566919b7ae53f27d794400d69a05d7c6d88edf73db94d2a2454549a66a7cce2ccd6347c72f2bac15c0e23e14a8d66b20b7ea6618c6f3bdbab6e5dd18bd8e87a9b421d5d358de4a7965a3609ecaaf1415c405b7214a7780556f0b688323d49b002d5010b912e4c194395138e8814d970006654a3fe91cf9b36c42a36fccc6dd93bc07a6928358bda310869f46d4552100e97f8594646d53a219287b14f268342d00e8e91fddf8868f544deafe724922d0cccbc77f3196fef9f9b81ea1f87191d0c468794d6cdbd18ba023a16516cf13c15ead3825db1916dc6a364c7329ea41a1373a6e8fab8b646440d495a3b61aa6c7a729c68339bd97f3530b255a41a749bc8e711e031955aba37b81501dd1e9fb536ce349f593fc8d837617cda94915f2e4913f15ca9d53032b40a095afa30717b3510d40c83ec82e66027b76453d0a679d74bc355c5810bd75003b7a1bfa0160514f05433c4bce24a286890a3c0df30656a80590c626416a9ee52408bde2aefbd57927b35fbf5fb4847309da88177ce6d173a335695dfcdd468a19e7d068cde823eee39895a33144887b5009ef611048ab096c35a625d734c589fc9dfdb3f0b0816ac537d7d38a08444559e530f6ba04aa59414bdb91b20d3433a0941096cf68d1c026421bc6753128116baf1678e26f48c684ef2d814046beb863af2b58423013f4e2441a8265fe3f444439135420d99ee1e3c2cf29b899b718bacba3692daab1e60dbaa18f0bb8e972ea807d0345f08fe26c39960c3d4e7e144470535282bbf23696199a5f6f8382b96ed561e9533b0e36fe256c78474f8ba504807fd807cda000ad051c3631237133cc11f81317082db2c8d38ba65205683d85bb9696b2e180f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c25d6c1894e4b0e3ba037d80b7bbca909d5d2c577b7c6c3c23b83edbe38eba6c","proof":"6efdd1aba916f69b342cc5ec6768205671d6bf9be2a18b23c8b12e4a0b507e288cb78b835e8ac24976da3dc3deb0dcda4209424b5d6ed8ac4a539f8893412d37a414d202fc316a15745e11e0dc6477adbd01199e6f5344a9253d64d37c46e6135c94f7b846649a60badb5fb9f5cb776d180adfa9800384587494a77d52e4de4cf28577c6a0b39801f8db3ac1eb6dbac7756a16dd01340b0ade4e1483e03e6f03a21c4903e2193996042e2ec02c69a2d684ac8a376c60e30f07c8272999abbe0698000f4ccef0c1cb9b76a4cbc77393a554c307dc22374b7171c581afbf3d1902f63e237995a40b1cd22dd64cb6815e440d9c3a31cacdc60b5fd6bab758b37a692e4ccc8a986f96b7b97f9aa0d1ab3b3e6fa334f2e52adb30441f7cc82347061238434e7119f4ec58b2c7622e4dbbd473e404acdd612d52b65ae788fc41889629fa6cd1f3500aa345fbb62afc2eddf3b0d16e5466fe61d74670376df62c59637a7295dd6fa5292d3e32e59f6e4015e2ee9e48472ab237e24452e92db3ecd92c6aee7ae269d8797ad15fcff1bd2c983c9222018c5cd4d85e490cabb8dbfd951e6c34e3b102cfca93b6cad0116c75bc81f72725c81d94a9dabeb572ae927a84eb3a4272a48e2cf88c90f83456f2958a24dc0cc1d6e6bf36d02bfcb9d5d991b6c345ca4349b57bd42045a72be3dba02cbeae6d07576377395cfbe539e4d55b3a715b661c4aa6080ff8be307924665e2feb722fbb3fc9e3b462c42411bf4c3e472e42be7421bb398d41c8372f98f32cd7907aa129d8447538362baae308d345a7d47d5cd33d5d43acaad8d4755cdf81b74402b6f69c41e6f55424dbe52c045a4c9733a5297ce19facad4709753226bcf904a61e5097c7645f17d8850c84320bb70b05a91d1d415462b7d476892394f109aec613ff8a774334193e5b36238f66c30607"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8cbd2c331fbdd8c84cc221189e67d5ccef43f1309e23009de8053b73a6572b0d","proof":"aa031970873e958b4746e8cd84119a1a08c1946e15c95ab3c63666b7e22320238656fa580984cbea577d4484653e2c3f2bb310c921f648bfc0e6dc2d18eb0f6746f3c716c3cf3b4a0ffcbad1468dc4356f971e88f7a50997b6d8611171c8dc4e7c5c3f0fb17a81f863e2693d7eeead7b8ee5dc942f33e8120405dc0324403e63a325c3d1742617dfe214fdee12f71f41942a2dec7df0a4f22da1e3d38fed3c0c4d2af87e6372aa437379588a6ee196e8e47b8977e301d6dafc1eaae0275b65058e89b4b8034c2448e7828ebe23ab82fb921c80972e6af6c51cf2817df0c212033cdbe2ca10c831c9f49e6e425eb49c3f216822447b0d369b90651ce9fd39350ec21e89b1f7535cd29a89c2965856780d554638f543802d04e8317bff6aaf235408a2e344d5af24167a4080d007aa64fca872dbbc382b4cc4361445b05b0edb312630f5b76ac539b9eb9ba3a77553e2666c29e8f358b7cefb718e9e4c5293021526bd30229be888cf8393c8f6109489b34e2cb7c7e70daf3f8cf6c38435462f5696a7bf66fee90b314db3c7e10f405182ad12ac8dfaa9b77c34b88a974659d328560e34cfad6842513917ccb88b0d0fb9864e7716afbe40df7169b133599ac47b70691019b23df904a7d3f60be5bea7b923d49b7fde74bb1ea6ca4556b346fc7ed202ed901a75bc710f0f2e9e327553abe9a043a815dde09dc758cd5e8cc264479cf7d493bdf87e38e7756ccc9aea5adf105ef8e998ef4f5c4a0db2d13a09891d501146e701c8b62972f84ab99135009c18fce78b1baaddd863e66df819da68197ccd8fa50b8d9f1e077988f830c0bcde931160f309bbfd70e3571dc7fc25f25679c9cdc1662c8303d0718ffe5bdab8b7a8ca7a61c8b865945a20f5ec04f1e20ce3eb2587edf341a28fe4deb1c6d63f0681447d848fdef1219351a4274ef8820b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2a2a9b68a288862715655eb391a3971086479f7fd3fd203050afc5ade829f933","proof":"308358a11ca2bc21b52b84feaf6b45183e4d3e12eb5383a62d3fc445aa3922030265df4b78f73e66493c18bf64b35e0f5c8bc4cbba26078340af0268b31cc401346c13680f22dd28ab7303374aa09398544e01188c940b12a16a1321b64dbf3c7cc47a416d9061aa4e71599dbb085b5715dfc94738e5e66df4fd002ab278281491fb9bad05402e5df6edbc6553c9ecab8b9c3d832e03c92f1a81717cb69092041a5c894416f11aee3d67c4e8e503b949afdbb63ed55fd951a86ecdd155504b014b8d95305109e71ddad93b590e5cd835bad1367e0921ff570591e8845f5ed70abc19f84dd782498c57ca0ca29acec2107a55f6d11c4e0e4101a22a8fb4a258205ac2e7895924915ca23a390e90a7b04a154cd4d75b19635fefe27c5b51b9015854fce7df6c8e7a9fb31552ba030eb50f03b4355aa900812ab22a9bc7582e153128493a15126d1a90aaa27d603c0ff74156cd2ccd688656bf3b86dbe468e69d6084171135685fe097f1c5cbe0e15df33f5ba9a9fca37e1ef1404fdec3c384f625d4af39b58b5ee63ba1e819b1e3f40541b90fc4f0335481056708329172429e77ac69e7a3973fcce11c262ae172b528cb92f82efdf1e3029607a7cd439ff24676466f2789aec34d9f22c1fd07fd9f9219f11bc86df6606c504a280f57ec79483d38442c518eafa3fba9c1aa83704ae8e405a88820a8ce46decfb26da803c03f1d1480c9b378cdf72651cd5479dfebf0f7f1a671cc47371aa884cce09d07ddeb005ad917cc372f6118565a301cb303175cf21f48e02750ca3656246ee7944f7f4268374da1390732c859705b414c9169fd4f5059b3ad41eca0c0f7508c5bc957145c27f45e9557891a2700effd1acd615c70357b1c9a626522f528a810b855b90b5a2ec999bfc6a17c52d33d19cefbfc04d70f41dd05c893795bca328f4f126c0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e6ad75124d02c9aeed782df45744ee6a3e35cf7a0175dabefa418605fcffc113","proof":"c25d17c0d6f4af64bfac9773d9b623fd42598a4c05872129a9892913a6ff4d0dfc13bdcd3131c0f2b2b4ce50fed5202a7e7fdde78d9ce163d2a0100ffec54c4e32ac785b2af7fdfc471146bd75283a183bf7f255bf59dc8a04d4202a85122f1ece13aa0b5852ec9d7ee63b168d7bad734dc63641fb9e34312db2f2fcde69d93fcbc298d5593b6ae60a9c147e0909cdf29eb619393f198832072180c14a35fa06de5a62448e54042638a3035eb466b3151384966ed1debe4e69a6e273eb8b9902212878ee2659de558dbfe7f24bed65a069c76414bd0d21f24a285c95b08c4e072826b093223577cda4799cec59121b059cda38727261c0ca61da195855b4d422c4aac74e205100aa3274686053eaa19bebff673a6c074803f04fd10dd70de564ceb184a1ef7c02e6bf5c563f773859747af7dc7e1ed87bb4ded9bb294532fa52d65c41cd79d7f6e39d09a658c4cb755e658f7df8fc8c4252cab27c859335651b0cda203556af73b015d0d4f2d20cd1420ed785d0923c184395f083463c427e066cc16c1727b3c11b1b9c35147bdc22266cc45a238f227c49fa760a35263ef254287c854d1477230e9de38f4b8ccb039909a2fafe173db6395fa7aaab69cb2507ee4901de87a6583f663578a0ffe8e38700909b157b13a53e8ec1f86e33dbce6948d8347665d46322752bbed23582dd28a054e37fa648167cf53669579f46ec58d845f6d7496910f993c29427bf58c11b46468eba39e8276ae18518ca06faf42068680089abf952a26d60d8b88f037d65d792830258e468f5cfbe4185d164c54cb8cbd7ab55f953c18711698398701ce2cc50d08301c5fd857b97937ebbef34740db58724e694956ad3b256b92ca2dc15056a7dde7a00be0647c2e0e72eff3102ba3d8cfdccc34b1c7ec7882a50c17199a1112d6b9984d4c3759b62261f951f07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"042356de4f60275f9767ced3be16a55a12ad94331e441a2eb99e396cc8533a4b","proof":"88505ff5f92da9c028d7747846342f10264a7f890b235683d74df865a774275e341483cacc688634bcef15eca335eb364b2fa921185ac92ce44fee2352692901b2e3ca03fb2317900acef771fcc1f4f3e1d281bcd91e44f7a7d20deb4f2a1e700caf02f788e9ae6fb7bdbcea46c32ebeef00e83c0e33e948db245c4c0aed341ee17e39820166d323d09f347cea1607fa7102f544ef7bc4bd60021f78f734fa047cb839c2a418f3f313ba2d2c0956305a4afbd2b6cd0be7f6f0d97a5508cb080ddadd09c4a89dda1d1f7cf37f4712bc73d7395126ec2918a330a0aacbb4653601488c25cbf2d3e188bc44e4c0a868ab5508feead3d0253dec38459c5099fc9f70fc4e295d73c8428816b595bef42e7139b4ac4a4b2b77bfeb1157f57a2436e436f846efd1070b48265b377f32158ad8ee9c384dbd77f2214808ac4ec03ac8ac52547637c09505d43cd2239e83b788fbcd2dedfc022749fcdc1a4656c632209e1da288911265e41ae65f84d77e1a80b27975cb050a45ec035cc843a5394cc10a3e30a3d8e2ad229aeac44e74af013fb47aea211a7b3637d74ebc6520a8fb4b414caad0aa7cf523152aa4dbef02641cb63dd29cfd86128a04536c6eb61580fc3763182bb3011702b7b67b1e62af333a35035a40ce614b9a3e5ca3b32633ed3ba47b5c272b3285482f19000642490d76d94f9eb01f0be597e6f7b6491a7c6c9cdd7fc0a4850fdebc39685baeadafac25fb10ed80b2bcd48c0314c581bd77804c2d3ba623c3a4b76276640f997722d80207a84a40e2d3fb940b4a6e7ff62ed029ee11f246d578caa2d5b2179dc7e608f0120c4afd8ef5041762f20775ee3bf88fbf17d8aa34e43c6684da316cf2715fefb3988312531551a7d6a40fd970808f2d6100fb3cea07a9adde3f0e2a778f2d66adfe720153c3a48f65a4168e89f0308cd402"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b0e5a1f34043e3433b4265fb5ce05b6771a28d71af4dd6f7fe16774e6f801120","proof":"8af4b5c7ad409f6c2d3feaf649e7e69032ec3028efc19c7a71bc9d2fa20f4a1156db52b138ea77cd703089a19b72bea267ef2ae04e9aa2e54e7b21b5cf729a7fa6a87440b4fa422e768226b8778b28c67809f88cbc2107ed815fb31670f3476fbcdfb86075fe4967f7e881d9098678b05698b25ff0cd4de5078a36b924646c53b2fd3a7fd6918a0a9d024a3cd164f4935bc8328e9ea3e7fdb29b69b3fe289907b47f64833b9c182220863d3fd03975563a53f20ce247ee6051d765a2e33cca0747714644c3155198061c7b85c15099affcf0a16de08a76a436a5b859775fc606c87c4d84ea21f1b1e2680398d50e9da8dfeea82898b25694951fe37c6c83662d186f301eb11e7bc3b77010b5c82e5201e877fb85be243365d09a528cde96692e7a14eeefc1436611879e430a430ec8bfc37cc25e9f55831f30dd0856bd53a05bfc8b2cce78ce5c8d005752ae6b8c4dad7d19ba2147755ff9e00f18b65834b27fda48e025c446f5c51d05cf9dabebe4973c1686037e7fcc5d18e34deba69f6d24e2a170716436e245b85ce68ae54ecb4436158b443033c42ff6b7fc0da60254655cfb14d22b19d3f800b40a2dead69b27b5068349890e04d01e4f1d4d8748130728aee5d642be613b6ec6fa7051cff00ddb6cc7ed7ee618b5c52801cdab88c156befe09b8a722a67be5b945fc1fd8abea5b0aa5025167bccfea58269be617772f123a991a5220a7d74fb459f2f9a2d80fdad437c3a79ca2df1ec77404f9c35a641a92fc577984e4a2c5f1a2ea7f47481314ba231608036e4fa811d1776030897564da393108249d58b1a4b76dec1b482d64686f9a32b20bec6336be01b456af6acd81b6b08c7fa3dd8f87a45bc05e166d87fdfd77546f9f9d6be761fbe07106019ac076dc75a49ae9f4b2106033f3a85399e3d553be69d08ba752347641ac750e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6c14e6310da4eb7fd7a9cfa50f331519c50fa35f8133d93245efe4dc26933655","proof":"70bd16542d90e61fe05a13bd87a14b342de7ef2054e4f7dac2a8a87b4ed81c0e8890689fdd14378525d76ee0e05a53798e4808a9a77858336ed611e2eaf8e75d8686e0bb6e1ce7af1174e89b07d9edfd465a36802c73c8244b3a8552b9a67057e0334a4676f6df0a51222a3c006be21315c88b24584dab28d5f899a62565675dcd7758d7d318be90654182f74a7091d72c15d2c9764562f56741e5ce8b7e190825f5416add8ce7683128df5530ece613c76210f36eab8176597ee9b91c150507a79212076f86f796e2443dbd077ea5d97a4ee194f82cf48023c8f8490e6bcb0716b5fb8ea3700977ec475712e2aa2f0e89eb0d69c82eb32c4e6b4203ee68b934e036ac041f5304df72e2b819509a3c96292d7291284999df3e02a2fe43271574344cbe37cfdbc420bd31d4bd42d63199f78438df8ff369730f4c27e189592637943bbf3b07e77ba759c919c24c05412b8b9773e8984cd961638e72bcf514de4466b979226de22514949884efd57532bdf9d335ec35f9279850c31864303a9c1fbcbb075374f127f0d322928ae0d349642222cbdb9888e2ffb57435c16c1db8618e9088629e8c0ce9094ab2ee0ddec25d222d78ace6067195611605d899017c4afc1bd5063273d49b2d44f8b948f395b70663f5fa47e9f8b64df5be4034bedb04fe6294722bb9c9775f6b9a3bb7385e259168d446a3f36e11b71503b7c8e0d52a0a930a0acd3ff061f6ab73e2f032ca1f83cf307a4a1a41115abb38342bfc5625b850e5a3b1402ad980f8660661bc4c57264c01485be4195dd14223d5f4660c3398b43c090fb1fae567e1300403bb6e24207af6f1b384812544f7868ee187be7a579b90e0906898851fb429f9302544ef7394f5db1c6c853354e11601c88cd1036ea08d6108a0acda3d698345d1d68852d50aa315d9b1c071ba5126707b93090a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e207c1090703a0edf8d846a299b4811e09d9a1c1d16c44f5f6c432c396ccd77a","proof":"9efe097c6f2020b6b3909975266e6d100fd733665b5f457cdeee4165ee701f5d4ef43975b6212317ae446735d63719ac1093deee15c79a2712cd69502f0e6824ec45048b0ccadc20cb4659c6dc3942d57fe3bb6f8dcc595fc703e40eba04300d6865af6886058234bd1b672cf4e98a2516d3bd5670a8dc2004db78322c327765c986af9fbe7dc696f941eab139a3c98a26c736353de8836865693e0352e63a077995d50e3d6a6c2ca89f4b2a1a84a9617542f185922e24c7d2bd1cb49edb340ec8f508377501d81a3953fcc760a6e8df805ae6f1800ce64d4fa4fc1f512cd707860eef554aa71bdb1cb7bfc587022d89603689a296093efe54ba4feb183e0f0c8eec8a3e486cf3a3cadee3ada09db7cb6ce3c3406201f293004e4583aa39df7cd6bac865e09cff74713d58c5ae15696be35a8a653ccd84a1c8545f6b2ace1c41066c394318850b056ee5a51a2bed994d28468a02404fa153859e4b77ab371b36e06c8b47b9ca7c67012b9a3c794c875c67597b4d636f0ed4348829bce690875bfa2f83dc072d2065709e9e835490cfd8aadae30d2ef5d40da35da807a020bf22a2c40b644f7980b285c0c9b2629db59542cd379a4ad5a850a4918c99f73b6f50f86b8d2e0d732b5fdcb3c56ee299d92fe783352b43359242aa7f76d19430196a0a3a9126aa7a61c0f3657261a11332a49ece0bcaa1850b38bc2168cb5250a011046c26f95c057ddc11f0e54a2f6417c24cb981359a167ad8c04b104c510d826ede73f378ebc0cdfcb04edc306e62c1bfab462eb1086ac8bf4e0a7a363796c82fc41aa47b108180d8b9edb6646071fa1bcff2aa2b2d048fe7dae712da15b21a008785e48b95071072a74ca253d985575f40e5fc1c140cd05629c5b6bd81a57f0998f76c0bc0d56129f6cc3d255bf001b1176ebaf06bff62fe5a438a01040ced0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"94791ba12d484ae0256e2afd609474ac238efc571f3276fd1bd855db079ce32b","proof":"aee9d9684a6f490f66f7c5dc0c8230a37299ddf90bceb5edac6d0a590cd3110604fe8f0c6ebaec38b8f8c263519079c47bc7bf739986f5779c0a1badd619f429326f7becbeb1af4b3290e74806000b8cdfd11815958b831078ae73ff66e5bc688c686045c944c7097edcd7c7061eab1d66ff147ec777ac9a12933f944924c92b94770dfd64f717caddc7ccebc8e972535c39483e7831ec76c23c93e48dfaff03b442d6a34106c578313b67a67644ae2f5f148eb8f18c88a284606b343acb490aba6770befcea143f87fd9ddc3725db84c9aa92932d38ec62ee195f485a77e00c26891eafe8b02c70f3ed58e842bdbd748a67d8d0747ea2376cf7a4a9cef3e52d9aba281c437aad5b55fbe39aa36f20802b3709be5c826b0aad3a1ca5e305fb1cc0b5bb8a29de89b8d9b68cc2db08638a9cac7b19f0de288c6f1fe65122b0bb5d1ef14cc7d5503a0789c094805c5288e85d3ebbf510b40cf9e8e90cadf20b9014ecc3f98e277d062ae6164a6eb7b87ba7a089ff6b41737bc8d069e02ac6fa844042097fcdbcbdb31a4f766286e3dea138d595c9ded3c07f9da97ab3640e4e820fd4836f80932ced3e18d4b9d3b2e2abb73016d0a8f33e57b1c0c6ff4114e31c5714070361f892269d023b6f81656a2502d0b202540a8ca6c903c40d279ff6ed0f7e1f0ae30d79fc81dba65757ec2cf851ff7c37c83fea2e6cad3f18eeac24854656633324e6d4d6b29a2993788aabbfc68c5ce7d688cddfe4cf7c8400b85b376a68af1785bd4536a950f152fd366ad5145fc29bb1e599aeb6473f4045a86c460c68f4e62d41abc39f2ad1cb278e7cbaee0c409038d098c62558e07f3878e5f22491e51113467b64178254c5ff99b8e2f0bc0f8337221d25b60bc1379f0c1d800e0e8e17722f9f7dc75d98ce9bc321e5275eebeed8a1617b622a543c928904640f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4e68c3cdc0a91dea59c755fff4a10bff31760d319f7d6ff67eee6b05122d4404","proof":"d84dd55b500811963322bbb697432453c5c5547521794c0d4c22003f3aadf9615057a528137376241a6b64e0972457720a430384dfa9105d3e03d77f2a432f420a13f78d27ea730edd22f2cfd3d169984d0c30c946bc70d42bbc486cba27b93c528b61f632402709829cc1556d3790fdacce9d916719362389abddae1f5c285a9b5737aafa3aa35f202338127a001203f92bd951dc64dce1f66aaca5b97b400844e1dedc629ca99545fc167f9444bbc910cf5158061bc5ff9ca9304b40a90901a1e24331f69162c56cc2ceee252434867ad6d91e45896576fbf30bcbb5c05c0218fd6d760dd3756404bf9cf2d0940ee5e847f2084b9789a1171fd40ec75dea3b64948effe9eb19fc04b30aee2979f66035527588b725fc53feff5675cbb6dc60a28c2e04df85e485f1f485fe0a83027de98a62191ed1e2b12960c78f9ca2f2496e4cd41f65d1f69ba36dcf348742a26203b43470d9538c2dc5a2cea5cab8005e5040d7b0591ea2ce7e6d26450af5d0acb295c52c5125610f49f4e3f13e6f5b0c92cad3016323daf2cf6533f40074a8700d65173da591549984ab09d3f5a4c662c0b305aa906d9eaf8e02703d84ad55c1b863adaa69a52a0663e46c73874e16119ceee7088904dd3f2c142131447d30762f6a974b0e5f38b148c098849f09732884cc9c766ebbfab7b50443a8734f6454f4d4047aa7103619b0f375ed88cf9e19506ebf0b74f39237e06283218ea0ce46042daa3ab1d9ed6e4a53e7f0d5611e3576bb879d24ed8fa7ad96096952ba7b4b37a2d257dfe1630a2cba39e1f104247b3279f5dfa61599aefd760db47637b0a3491b34e630d715b0040682c547f1a5455d49b64c096b2ff86c101ef4bb0b1839cb3a203692a3b818fc8ccc6728b8990e93812d92cca4462b7410e5eda7ea91447b5ed4f87a6fdaa51f16e3d02db92600"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e07c08d1d5736eb32b801de1b51dbdd8e0f7ded67bd43cca220d57b5ea73e33a","proof":"ce19e6be57086106e9e5e65a14996fe091aaa8ec5799c4052f9c78d3b245ed7a12f45e5ab5daddbb7097b4c54820f3c02c650e78c35b5270428f7c31153de13d142b2829aec0ed5a3998d51d37ee902148b4af96168490806e28eb340e72c875286a0cc05e5a3bb92c146d6cd0fece2aacf530f0aad9e901fe93160739601c7cd55781847936e23f9e29049b55aef51dda832655a160e28c2073736f9a2a4305bbc29c369267fe4b485738b28ef75ecb00b2696d42ff67c54f600d49ead7ee09387df4260c7d3f51caaf545200b1651126610dd09c5d9270b498d1cfa290520e7ef938d625ec534a27506b86b9399d3f024a28f8396ce09dce025ed237f1f7469ad0d931159f5b258b742683f5f1c6b01d7594da4375599b1235c56053762768246968e1172145b910c5ae625fd07111c2f1649ca153328efba15b022fae1e77a8c660c2389927c66a01afb1a95f944e627f91590de43c92194c5dd56cf18826a482310a4b91ac8bdbe410251b7d1f8024c70c2ab7c2ba04a26232be830dd0624ac07414757c69698df0dff881762fcb413b28ac2bd841cd1f4cc6720e58a525bc351b233aea26b6f6b92c2f4ff4d649da808d02f4545f047d72a4a75f79cd480cc28a04637a055041f896aa594fabbbdfbef6581498943920deabb6f7d26f10c6283fb90f1a9d068cadeeffd1c4e6c2c956aaedde408aca34c881cf8ecb9f03269f3dbb5ebd7b22d5a1e59de5f403bec752238b5a04fcc85fbf1a291ca13920561b604266baeeb9ade1e6dbd1673a16d9babbfb8ad47bbae1fc6061ecf2e57f5261a0890591ca9c4b5942c3f6e783761805b11ece271e84f4700d39b8fe5d22a3310cf714eac43af72bbe7ede620c62bede24b638330215c98a2f1a454aa00e0fac08dff225249c312146dc6c85a003c92ef7c540f514afcbbc86ab5756d00f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fae8ec41f826e9952d91abc5dcb103d3c44ddb8557c9bd358496a1939163622b","proof":"020cc8834703407ff21d5b39103923695c4a68915f884ddc92d225a00fba2649726e4a0ee40df191e17e7c652586ef1c8580aea0ec51c914ee1a31083b90f24338a8c76d345523a6bcfbccf63bd642532ddf840ddcc3f63b3f0d6adae556c26d6cc8661534bf82fbe0cf32d04c17b789f232b3dc40851bd3cd641d0e2024283336c7f268bdcddb59f7143a9658b10fb94dac3f67cc478d0bd3ad9dba42c4c107d77e92eb58f8715124828ab2031b0439860e67b4c63ca5bc68f60b34fa322e0de4ba33b545cc1a443bb24edb02d49804f6e94e778e3268a34d29775edb1c0f0ed2ff9518d8336d352eed4d8d70c5a2f24ca6971a845c764879a5879b8928b241541be26b289f1e29b95e475aeaa2f1299b1092891a34b2aa4e323b65066be130f6c9d92559581c2b6138a39a2f349e272f4e9b2ca3276cad0dd157437cf5b70c28753069b40576c83b5c66de14fad84a4df64ecffa306982af4139408e1eb219901199003a8b214eb3ebb6a191a64e87a56f44593357a3e60adc2adf4927c2744e2a9433ff8178ba3a34ef5bf870f1344bcf8197a230ab2f21ef51f5095d5278bc1ebd171367cfee86ddf43b8e8c60219d520c62bc54c702b8bcc07f0120bb295ee8f0bc1a302efc00704c6994faf6bf2aa360b882f57a76d19913c2fffaee273ef483fd4cad199b1c21941ee76ae4820aa46291fd8dfca577445ddf91334a2ea6d52330cbb36c7537638883be4646124700ee4267dbe5e17528d8908b140a4c88bce7259db13b3b9a4434fb4ac316dc88d7ebed777662f1b4c525eaca178b1fe6f401318f1cd9df9da5c48e43e108525bc06965a996fce7dffa0ee84569eb4cfd458dc348587151685cedd920016faaad6cd617c391d157038453a1eac0550ed4b20923f6ddd3d4c417f5bb045ef90045d6b9ece59e73e84958b933c1ffaa0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b462831ed1f2595fb95516778c7c39bf922b257080169548146265f8f98ea01f","proof":"1a4c3b1ee48a79a84bdc6a60703a91a68b4f26bebfef3af410ac09c6c9191a7fbe400bd4371707338bda8913d5a88c6668c94d2ab0042b29c56e32bab97dfc71f64d20a6b17b6612dc516255628c6e699caca41bd90b663a1c87d79b65eb2c0c329e61c976217772445dcdbf372ef5d4b9f81d72cc888bb67c1dc47234d4f06ba46be400549285ca665d00c163dbc552807fb15229d650ff7093eb6e57fc6c06f6d45deec677898d6d8eb96690fa3feee5721664b815e355739a1f33d8156201e71b664bf4bdf643ad036e31980f3275d30c1387e18cf7b6ef16fae00a2e94075ae86aeddaabce5633a6a33497fd4fcf8ac67f97a83db2d94d8d05fa7547ef216624dad5e4c16a544ec58b6cae1e9b5ef6169f1124807577ac5b92c2b3473e2cf2e1ab64f9bab08f5d75c96ceb5593f3f40308cd8d1a3dec06e3f5130567940c483831816183110229e75707861e5016098bb1fe1ffa9c4841a455241b071651282c2b61024b0939543e01e53f32ea8b295333e07a953df68d12b4059c19da5f1c684d81098c672d3ef4dc49f20d58c000747519685b63cb797119fa9c3f7e5efef0469c992b5d2ac706567b70b75178196114243ef1ec7d28f8073d7b7bf476ccc5f9cd83627b757c644f38829b241c1d1d5188015e7808f626c26497cad15944e077b31c804a9920a0e295ac950e1155c188294a2e6d1a614308e365e85869aece79f64e4d5a26b7eae40f10dbbc05289d40b6bc123d9dcdf13d56388461263aeecdb69c9708ab2f1e8187d9ffbeead3f18715782b44773ac0efc0790c3f7b18b3d088bef1d1778b3e260873339e0a2ec610a69edd1fb037616b62b0f9e6330f8dcd3d317b3f69b91c6f3d4e5720562e94edd46df43d4ab8fe6f4887ec6208c4cf5c774883db2c788819e0e5153429aaed58f70458b7ce1dbec025f705a200"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"527d7f3a4f84be55cf5394abaabb8b6a01ffff0343b17fbb1e7b810639b2396c","proof":"2634d1138807ba359c325049aad510d6847ded9f985189fc440bb71d6e7d51312883e0d379ca02cd504476f9ee2d925741c7f99be259bfbb606d27357ca9e76a6c502ca2bbc82bb5e61e9e31cca926561b83440bedc6fb50ed858bef6fca3052b003af0be133838d938e772bb49e0da5866e56b96db47f1cd34da997bb644c65554cf97f4b22cbe7577060ebcaccf9db59e2c6195babcca649da2051bab34804eaa67ed0145cb1dbd28d04c8f027aecc9e537b06b0c39f596809216691708f034653fab0a0c8ed4038aa5605114ec2566497c0e6e1c618dd6db47fbb79815907b4d20bc74701a73f892e310b71622d12227623e50f45dbdecafeafead46a4e5d0e9d22e34914aa4f3c39368cd91eb5e5b195ba9d6989a5461c5e5eca951a0a1d4012ffb52477128c4ca44ac1f0cd95826f1e8fc360f9944a6aa0fe7dbcb2386e685c7cfa780f6060f92b2c326493dd703e7b48b5fd5ab0e0b2b317a1f964b00114d8592962aad113c1a5a48f9ca8e58011be99746b8b6efd741a68ca51f9e20d9c2e1c246abfa8caa5576b85ee92740177f71643d7bbc3ee21eff1f8f91c80643e2c0a12afad4334879c038be983cd870409ea68f1619ec83cb99bce7f5d2d2624d777cef61e6f4c9f1cb7e01581440ac31b2eb82602b88f0a0bb9f30a2e767324585633b718c7e76b5217a1f9217385ad82d8fcdbf0caa7dc53d325a751935778f80ec7251dfe513cc9d37d3ac0b9ebeb3d927c9bb7caec46a77d5c5b4ed424a0136db79fc5a269484ea8bb23a6cc58692f66671f2b2301e337013e243a9963206d982d32cc210f45388f0e17c22756b98283573d769d67080dae82fe58ab60155e0b44a6cbbc30fedac0cdff1a18a8421a6e99b52983b07aabcf3d9089380d69ba14a386c551b84bf5d9a2aeb152aaec9fd6f95e22bd048004c4c8e34f3200"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"58a3830e587d62ba89cd6d102dd8227d0f3859bbe94930e05b40445f7aa54b4d","proof":"e05ff9c7e521d1e3d522ea10c53ffa6e810fc79e5ee415e7defbdd126c62e823748ae88fc58192081440daf08d1e178896aa385fc6e6921e337fb6485fb8090dea7a85786ac5390cf44f201982017a2fc1d4a8002ede47b1933960d6093b221edef448684e9276f2f627f27641f4437a687f8af6386e523fd0c25c883be9f01c89be698b3c3b63e527996def0a89fdadb296900c7fa5590326c3a57990266c05ac0e58acac0c2e7f280737ca804b9085a4459fa1fe24c4a2c12895eefd095a0d3caa32e7c998edeabe7e94dd2c42cf4fed43bbb043b25f15cc99fbc97548fe02321c787ad9baec870aa40d488e4f8c41f89bcd52deff043155f26f1f7bd42772947239a3a25e5cc8d0a3a5ba6714286bc6046e936c21e55bdae15b681bbd664b3cf0e0786b9e59f18e57da5eb5fbd0f9e984a7d5c57cb1eb4eca640d4964317dc8ce409cd360cb92dd819477acb76412fc83a896e9256efc586b1d5813c3292d92f6d245c386c8e839a27e12829d9ec66af7b5a2972e49079147f687c97538572cee037f3a039d482063ae549277341168eaa820dc63e163d9b3c5c6a256dc388c763bd03459f82fea395dd4a3f7499ef230c9948e26c3f8cdbdbfb5cb7f1242489cb287d7b41cb3c2c8dc88ad35e5f4521b9a2a6d7023a18ce4ae82452de232f45b07eba18639523f546706ae6805f39325fc591fd68c7a663185d244af832cbca92f9865f3bbf6c3fe9be38e7293eadea82fddee8980d9667a7c6782fee350b4c836c46e5df868873418d661767e198a5a7b4cfd757beffeadab416290777152b5c600e679b3afd533cf9168f787b6860438741f99b422fcd28caed49d5771ce741d37316eaf3c9c0159d55384264e1134428386a782441628fd847d4a0a0e98fce441a39157668afac30ed5630d8c3411a0ec0867ec9e05cc5b54f990a60f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"32ddf03360dc20ad688fcd9e1d2c43edbf7649ac65d07235688729dd9b4fd36c","proof":"a42212b2fb198730e07a3d4f95f715ed37110c9c8404c121ab09e9db81e0303a64bd9f11225ed80fcb6ec4e2fa3423f0a9f691df467f9e083cb6ad2e263e2223c45563d4bbcbdaef8c44f733b06b86d54747720cedd957d1da9a369365f0413d002565052152d341a54eaa6d5eed8b7321ba4fc526261995596df4c507c9cb4e0db56b2e154208aed70d25fcb7a5b36846b9084de42fb61b82043f02332ad008e4236bf8cbd8a38a422a06ee83ba56fe9f11bcabc2b7011904238c9954d7b207f27a6fc2c723a217e8e4784675ae8ede3c3016f2367b6d7ff97453a680c6b20d40747896627af58a39b932c09a6f9c1657e26d68ea8ab86f934b2c968b11a024ac488921f60ec3f5830ff4cce5e43585657aa14fd06c1801f1ea7f51a6cc79330e94a925d026f01ccec02520857bfe85b61898a9993fd6b7230e269f97e47b1f60d95cee9148220b21e08a2963c493b88ba1c9ceed38eb32b5e6a3722aa5307908ac5d273e6a5cadc08bfa79dba93515611cff3199cc0c0e00a5011d27d31220be5c48ec37efb9114404eaaf4f9a9f27905ea45da9136fb12343dbc3facac72302ee45382f9e2095cfafcdf150923e1d0b889dd5059b433064d1733ce288c639d60446bc49a14fe043da3ddf2f2a5f1ca01d61706d2a59245457414a7faf1c0d00b67222ea55b164f58d81a4548a25f1717f64f43406bb4935d075c22c15b92de49d3794c20aaea061e4ffd713c51b522f2bd35e95bad419659967eee3c5b3122c4d29271e4a9cd22b13d01e3ce9e3e147b2dab091b1dde0df2cd458d1133c3848be041091a6844df10dbf959339f6608412646498a13cc64caf8970b1e92804619e7bb55b662326efe335ccad1efee6ebdb9cd07e598c7c9a34043d628d1c0100d3be1b51df91184cd631944c6fb7ee8d4dfe356cb01b889981773cc434ce0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"42858f0a17605c22007e6a35495881b96ab5074d591a456c9a83efd79d80c725","proof":"1a7f408959411f040b6930cce33d9b5dfa07c2ad48c4cc6c48f76ccdbc29394650b196c1985caa7e9e1a52ea75c5e6ff635725b7d86939b53104c8d24cc65b0a402eeae18570f7bc81ccc55e58abce611a6193026a67de01461936993eb9f5404030d81641faae6d17bb33cdb6cc23ae98d269fd67c86bfb0af2e38c14163f6a119edb036cc284231ed363e439500be70e3fadc6c185a03f7c627ae0d5d73708045a66d585157bce010c6e0e6acc3161e39f11b312b6b29a40b172fe6f5f7f0b02c5943bdd5c550353830d80ab63b73cdc7c18b2b7365c42346fb9a3aca8f10ba0a227bdd460f50f0588e2c947aed62c37385223aa031227ef2ceb4a5247d11a10a872899f92c99ef75c7ac602e8008cdd563572a2a8cc8616e02c8f1410995a42c3ede50685773ec6504193414a1d068a6b192c9f04d29a623fe7c397307a1ac4054efdb73ea4499066b3fdd7afa426500e035cea53bf2964aabd475e84ba231aae305f4213da3127a78e23d5b75aaf038f9701b49759c55afc8666a0dabd7834d358a4f89b8021637958c5654107cc705143ce9b42ad451e4c3f44aedbd86c946012d2597b05089016eaa36f50141208e09470648ca7ceb857f175cc70126feaf27117294bfa41d36fab52b8ee41f74fa81568c17e985e3c7c5c2c84467502ce74c4ef529709f827391fc18d9d127e809cf14900d11c60290123b0c6db2b411a7647e681f53ece95886b07198aee90b6e70f9ccbd93d3ba19ab4ef289f530d7e0cc6515f0a81fb7b55bfb244570f29e13976b149d671dd8ba994df1c339416c60ab35c7e32bed9c0d18a471014abcaf49f91173a221c48330fb1866697b27eb4369c43a0c37e23e0ad6f66c78e08be57f692d0d7a89ebd7d1c87d95dd55d0f103e00c5702c0e2d6adf54f45ced49894152879ccff0cbd3fb35dabc9359f90c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b0a9b1ef89fd7e06f59ef9029f37f6e4f008fa7c8359f17b59f4fe4b16d7fa01","proof":"b27ecdb7c98241ddd68775332a8e2d82e46712d5e8fa4ad5079fc5575036c9737e98bc92111eaf60aa9fa5c0ddb1d00e3f5dbc06153fd77685e147ac7caa3b682adce882068d897aefdef207bbf3aa9e364200d1bdd9b9db84ed399342d9bf0160d089e240425b51021f6dd4c0df476d1c3040e76bab2d4740b04ae835408f192d01bd997cff9b29231a2808b51a3d0f99a65929bd8a58cd885b280f8dd4120c30435ef686a9b1475ba08d34d305106207355de0b3cc935b0361beafbe378c0a6a16afe720c3adc7082f88caa930fb74a254444839551f8fe68953def5d48b00fe3f3139448bcf19accf597153f7a50ed0e9a1999f286cb6503baee87730a90f80386fb759ba50950f89ea44f3d84458b86843022e7b68e54ae05dd0aabea0006a5e7992667d85834f5a683827d04357b9ddeed4ecdb401f1cedd26f8c52304d980fa6b7317f22d84e2ec4121bead1de0d54f6cac62b5fe783e8e956d3bfb51d4696871252d4bcd8a1a07e41d571fd1e0914b0ec490c574509a2117769cb97757c5643b6c9955556e5d0c362c64307e15dfb7a192aa76d39415e5f446f5ba61db6fdeb63d34272b6e6ed22269a7c920833b21b36ff2118085d1d2e62aee6e5000eb2dd8312ab93240b2f12ca225aa32a56828cbf72a9c7066c7c514ad2261e1bfebf58c48518e9ae5bc058c4a832b04c31f1c83d35e8ca2b65c0d3864a166a73427bac8e97758f6e6051d86c598c2012093439f752ad2dab2a51bb3aa5c7850adc61f8f3725c3cc5ae5e7a8276d9d18f3ac7d7bf9be4d9792b5f85248db69539344d09f18e493693f232832fc2b4db99344afd4dc58836dcf1d14fe54bfac02b1d78e8476270cbd42c1f703c687ccaa0b53d37e894d933fd84a7a455b4ca470b7eba5b93b788b13887474ef5745d8f44eba3bf734814b914a7e39324bd1e6901"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ca0346b959097d40f55f14a1adf8fbab40f22d76c91a9ac3117f455f4cf00e0f","proof":"fc4ffaafaff992776a51b4fb7280fb51eced43c492e90828218d528811022f1344cf8f1d9356973feda8ac583b64e51f4d714e21e802c6a9c66619e1a00cdd75081d1351f26c4c6fab574874d5daa5e17a6bdd258c3a89d6cdf96ecb7b10700ef8a47128120686a76e57f77d71e98b29b77cd2c070432e3eaef4b03c00ee7f4b56ce958c748e4fb4a7ef390092ed1453bdf17d27d996d61f16296a878dc8e80e31fa044308f91c7e47381801136ace211c14bf9a684bd3a4114934de463c6a06cceae443599888d76f550daf15179acb7175b8037cdf6bf0180a96dcf5f0120d60167faf5a7a57de633290ac99ef378be090839a8d310340d40689066e6cee034092d346e2849cf3684a20c6906ca77ef6c69c3ae2243a703e404347a0ec274aae7a8fdfeeb9309d836d9e308542b09d1c86e5f18b686d0560f74c0f5b3ed61d4c3eb92e0be6bcf4a2fc30bce0ec00cc293bcef36ec371789763822c1aa9bf1fd4567e3d8c8960755de7d61aa0ead21599be4c79310fc14b9307766663dda1447019d08c14241465cfded0604bc9cce23665ca162568ae706c83629c05c55c3260e7e765d40311bb78d602a71c85c2846a7bc8e039ea8463cb37a590524bc55c9093009cbb1409aed9fa715470e5294b4a3d4c8a142415d49a28089110ba327d84fef9e42adee6a779e309bc5b4ba4dba55208878f2bfe1a933eaf1b3760970d9495ed8349735b5e18aeca5fef2d57032bb7807ede0da940a2355afcc4695b784032fac79af007e4441e1c8c04fbfbd50d7b9d54a8597288114f3c344b6b7c39889528f4b068f6cc85ea20ccde4ad9c547868585a838128d1fd84f4dfcd1d32c6128f51db36148a31be55461a02431d3788e8165d4680f0a5195c01d6acc00000a72bca634577dfad820a4d6e3520280b8558e2cab35552994c3f879d17db203"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ecd057bcb700577c695d179a16521f90ca1572268e44c6a8666494f52fa0b253","proof":"ba5569073f88ee176612e34740f271788eca62b25e70f0e40a7534533ff7a82dde146eec2325a39bdfc7d517311e3a29c48c3f1a7eab4e54cf31fa67cbcb2202aaf0f91cbcfc3bf5924315c917053443f283d95b09cda3d1f187721ef1c2a75154a698b4328a8086b2b98c84ac795a656a0382c9f8c8fcea981ae4b144e40004c11ed8244b32464072c2922d54ce04f2f112212a92333468164832f64408d003783289a10dbb0e3b4aab62fa8aa3e15490e6b9423d05d0c6bfbd6b12bded290369d8e217b323e04f962d4bff2c51d45df069ef37c7ca5c4da420fdab84181709d842e061d8ba3d2cd6802ae1fd8731a0f26e1bc57a986f8a1c9464fd1fa1bc3fb431077330fc5280f312aba8e9c563c7a08c3d40aee8efeab46a72c682e39e73aeb3934852b3fd4962d25834bbf8710e1bebf2f293b1acaab99aab7c92ca1a36580568207334272e1c551ab26e7f7f96805a72516c54977e9af1dfe43d9d073692e8f0db8ef4fa286c7bd48c99f3206f9466ef0bb81180a4d2e5a2b1470c757d7e60e1ae2e1c7f45295d8487dd3f5ba3fa0bdcadc0c0cc5b077a75d55184ef23d671b72a4bf6218ceb260fae5cc586c80c629fe6b61cf873103b12d92ebd7d30a6c9f66b98015d57da061fc414ad92a3904f1cba7e73a13be0bbb0ef0875514d0a8021257bf0d8fca745a5c33a56a16b3a0c991505a4bf05bddc7f57f7c2c1183a98fb1979ed29facc5ce44ac4866e1d1446e2912d83582f11d3cc7d12c6473bf87ef8180c0f4c7d1ca69cd6239a31156e8c833636f85bfc04f0d8186e3ebc4116e1de00dcee36beec903c4a9b3e85eaceac727a0ef06ccc506c7d971819bc73ff153cc451e0143329599ddd74f295af795b92db53f01a51ba8757e50a979a05a20dcafa7be47f00db66901a3a52d8d0209d586d4692bcfeeb644756356dcf0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"441e53914e2e15a0c214ed10a760b6ede000c29fdc6c750e3b1a28b783db8044","proof":"bc21a5007d94cc146abd579233b59d469b73b42f7139051221e2e44a4dd9a6323c1781f10f0682869d11093b9cd6ccde1899c45a0f231bb4d130bc7dd612f148b4b51a3a0df951186159b1ef5aa325a80034f6d7d1ed7d7b5592c9e336762d1b5a00b14ac53d9673bda4fb5bd089ab72f3d0cac869b20d6660e8eda90f732f16466f3110009291cc595f67acfa6bab9f5c0021375bf96a713ae905dab2bf840f58ec1ac396b2cfecdc5c8fe9227f70b422910e2875950d0628abd7e8c0e0780b5b9f13a252468c8e8dcaf5e0ecc1b7bc7cca0766634c108f81cccb978c0fc80de00f5b698be56f69bb22f3234665a33fd9a42507cc7f353ea8adf5a60c4e0206b2043ca6f88a2226f3a3192e305ee5bd5b273eb273543df55a1f62f34d33855d0647f8d629e5dd1bdffd81fbd305f09317f8305fcfb5283ce7725a949e40db3fe81f344a9b979678d65d2adbbe25244fc84d44e7ff61135ffc5eef5cbdbc1a160aa5ce716596fd0939f5e912903b7413bb37d2c1c217b1522b41bacf022f6c7ba45dfff7eb15795eceaff6d0bfe65f80e977b18928eca48e5cd4f7c743887949f00d7d97fd7696e490449e104e27c7c8a2dd9d5fe4a6e69b0fa136997f754756666d225818e13befd38766405831bafc9b8d5623066efa7a2edf68c37c962c0b3a1e951b0b687f692a9753914e03835337b8308ba80631364d8a3fb2befa8f0bfca29f8f16dea999a126b574051ccb5daa03fe733c54aca5547548756349c22b3af972eb3d2ca705b864b320ae3960be0a1ddf2bbf6a2550b46cb0b442570c7f186768090f174f255be40e3eb239efe0d1677b9dceab36b9ca234c362e8a0b26fbc8dcf041692eace867635ceded3e56c8e285a3d9010dfaab8861b6528ffb0df5efc6feb64a280581603bd537e844aaa25a5474945500d8f41bfa06102ae10c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"168212f240954319376b4018f78f49c63905f10367b46b25cb2f9caf7659517e","proof":"aa04ee8615121ad512e63a02494b7850af58619bbcb65c68096c3b9a782c7d76bacfcf84006071cd69d0a28099f861ccf141c46bdd91c81048e03e11ba09cc6c8cf5de32d9037dfa06bffa62659997d645e42fbef8ab8175f9b79f3882fc9c0a187add26f48afdb309b1ee603343c0eec0820286a39a31c41b6d7a9755a9626c87d74369af48f337f77ee9e8f2304b1ad5a804a56a06cec0dbde37c926f9e60ef6311e2f39ec7d0bc89ab1a7fcc0d93087f4145854f231476d892168254d50081ac7d0cb2033b0edd0bab9cf2052397609b766b86592a165714f5e1884e066032a7655045ab41547d486c5e0015f4c30d22bbfecdcb370500c68f3d2b1d5741f7abeb6d8c146d6945f34c38101f9762c2e25606963d9179dbf19390155dcb4152cae52adedaeb4d362441e70f7ea30427b9fc3572fafd574e35f9cb254e46126aacb628711cd10350cc44ee02d9ce67d3a9c0972010517eb178f0b43e5309828e8efd288e64421891ea4fa8e6065593024853892bbefc60b5d32f3bd9372750e76b5178ea0b8b20463357dbdbbdc2614278eb61c8d1d631b5cd6d30c38d9d67368caf847e9345148de672f14e69573cc8f03c64e874e7f1eac4ff211547d6b0732cef4654248f7c44efbd8e212cce92ba1f1720167b0fb66735082209ae6c763dad69a7cc1720e75ee1cfabb4984d8f4342141b1f5ccaa1da7328123547c1f712cb021af4f3571a162a165fa1e73b5ff479bd0e3c04e9681cb25128d8cc0ff5a3ab3a26665b289e680ed9cca600bd9609b8dc26bbaa01a1c4469750d3f0a00465e877ca8d864ee14ed581e8513f3b3ed45ab4d255ca1e8f7aa7076980bf6df2e4eac3babd13d5cc3b570f169b59643afe2193cc8382c4953b4a332c226b07f0a3b4e823b51411eeea96971bda25ebd37fc079a116752fc4f98d738716f55c90a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"441e5de59d440e0fa209dc83e36f0fa0fe985d7b5c891011a07adc6108e94c30","proof":"3cc0af7e75ff8ecc8b67c72e48df8b7543bd4331f12faf4f21702e632298975808d3b377eb5753f95e95bb9993a0fcc0a9c276c07f80df2668aab403cb7d7525121a835fd569841bd9ec2208e48da2fec22b40b9420cc3a829d86f7af2cf0869586890892f6fa53aba58b5bf5dec8b9ea7b62f1f8a97796753551e0b195956116c441a88e2245c321fc1ae2a4de1ad6f9525d72ef6135b5d6387a598288ddd05b921c8eb5438a34eb7f60310ebdd20470e943e36a8e3bbab50f397dca8ec47056757e27903c43e45c09a7a292571e7fe53808f8bf02594eb91da9f99d20046021297acebeecadc8322272d0d54719ee87c245a5faa8b000293eead844ed40f15864a77a09df839f5e2cc65265edd2f52ec93f68092cdb7521a77043d29999649e2cf37b0cf98e47cfe31886bde8f617141b3081089b1cd04bf7f3164eb97656c84e11e7eab58ce3ce7afc053735604cb5e9dfb0e723b40ccac80d419a611be2304edbfa56fb5e77ab933420327b7096644cd4c7470560cdbc164a47c12474849ac497649d5e06f08b368175e36ee4753dcfb264117006a84e6d18fa35f7dde230854341173ac402af27b218dff5515871a0fe62d12ab130fcbfe3e0ec020690e9e3af380d493812c925c64a46b77bf30c5fde150bb6b9f907cb64a3cfef24a2ae8ffd53b7593b6985dfe612bd219b595bdfa221cb54401c2e7737d8104cebd4dbe68216e2030d39b9e65a5d9734a6fbc4c7b6b6a455e98678f9c54a36fe99b5fe28723d4acb44e0fef1a023e710468e51fea65b9faf2a66c15cab6587399855ece40d10acee652e031b3848bf201ed5496096f1f6a6fa5653f0dfad04207e02fa0810044937c5c2df14feeb30d851f1c4ad5e553f6f1b8946267904980abf40a09113afffcaaf450ec0f578cc60e2c4d0a4ddc045b423ec05e629eb1f2162209"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8e536e9fb70763aa438bd826a32094e1c0394212b84cc3760d7e032edd55bc1f","proof":"a2b2fc8dd1c7ed48947faf10e22f5f56685ba0788f8f78e7d248025d0d2e7e5456b405381dbc40ba8b2bad6892be63cf6b965f24602ad50be1f3df659492c86a48ee6bbd2ac62750407eca22422eb6367f35e48b245f9581efa8e00d139d2c442c066a6995dbf58186b006375b85b73d82814efc158115c2235f6c438be302209185e534e44564d000f8d70393d1da1fa84d10e56af3f67401bf78baeb046706c8018f6a986d1056c3eaee999a8fb23ef7be1cda5d7c4d0c717dd7fb90c2550d16211bb9e4eff3415d6753b7a39c4b7b4fe50da2e0f8ba8cd125fa815fe3b8029ac3719e038f4123248adb12aa4ce50e3c2c0205ecc9a8aab221a127f620621dac58528235f0086b4507c5918fd9b658a34950cbac1699c954a879fa1ccd1531f4ad7a25f900936ae1fb4b5f0cf9debd5f23cf37f930d04881853571dc71f523d880b42a6ee8af7417db4c9e86a5ddaca0e568eeb5aa3a3107acc8d43ca752232c62c89ccd438dba33e5f838e08969a087f733bc0bbc7ca3414e5b21cafea63cc2717608024df6aed52fc7dc29f48075ee99c63afddf770c161924c0541a73732ee0d182f6b53b1b67025906b791a1da87eee81ebc138323d23e023dde55e27f1c92458e38ff8de6a2f2e26d340fffeb1d6154a301188d09ca97492a686c64734cd2727d17d2f0e1a932bdcb8a46783a3ba3ce2b251dce58a426017bde205713ac8e7f192bed79f192181848fe967d95bf8e6aefe8e667bc758972b67b62461f7265efba4d87e90798ecf0e2db1fc6a223f75c88dcd8771c5ebc35ebda29e82f827a6296ffe56d4cea5cb407744421dc357f2e1c8fb6096176298845e6b0de48216673e1a6ce7904012a4a55c8e23488448433c8f25ee408524880d20a917603025a537e7f0ee51953c1d62657318d15f53e37524dd9fc9a7557be2de4898801"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a6b74c18b618bb9ce8842a2f18aa3aa1a5d94e275d143572bf62de151017fb1f","proof":"669757585821ecd98dd1b28c1f18a48d27ab651f46724ba63dd2077680520a1bb44801a9fc473bc5d1231ff87b201cfd128af9e87595c68c00cd336c3a9e6278f0bf5e3d97f5bb0568a62d9fe60790342533491a4247ed1cbc8df790f850d45496303bb1f27dc1b5f83cab5a90d4325705e31937ff656187c9edb029ac2b46742f503f93272095fd9f8232c6bcb9296d5df4f82cfc39b697f46eba5a2d48220a7d650bc6aab96d85f70cb195cf3da4bc47d09c945a02571300c564d9f4842d072acbdfa0cf9918f4a9abaa71bf20d2820582926224bfd1736ef1847883875608aa49fa35500c517ad1dfe5bbab064e68190cace6d354d1a36343933384297c3c3e79e360fc4a0b820fd7bd2ce9ec2c0ee69a84cb525321a8491f2dc76f401e05ba179f38cf9ed0456a45e42a8053d50326157a76c9d069cd49ace00230b36b32e0ad7428b1db218a11bf65690cb6d19456977fec665d5a17df009fcd9fd36b55d205a8bc8b874e300a8d586407deff6ce238acb24406c359edbd2d91720a420a984100d6c860837bbb01bec62f11aeb3e38ab6911b2f20ff87efdbbe128dcb3220a5c41ba53adb32b98a62b0fe15a662bb0b4e7900d11619ed5e48f97cc4a638f011bd8e84d6fce3ccaea5cb7edb797f2a708e526a0f9250fca0194db7912a33d49a1ef1cbfb3bc20382ef7ab4c3d25fac9b028e34a8b9b7159ccb9a4a54e13808a49e62f2ca79af98f077d8ed5aac345055ac8231faac7a5ab1b059431d7b337000c0e1916686cd0da4f217c05a25453d88a1ac1acb89fa1dc41d1dc4f7cc755ea4e5d632e2c88d76a9d422333bb7d7335718211e95f3486331252ac1276531080324a84f50294e9a28e5f82e18274ad93c26cd83b36f8ccd1fad770f1dcb058418d21cea71c6ac6196233b623f9522924c9260fb7c3e9c6b0f59508a786503"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"745d3b8ae29c1a4adbb030da7ff839d430c99bbafd44c05bbcb0c7c25f02ea56","proof":"78d3f1977b0d4a29c5e219df49fc8c87ec54c9af5e097742a9f534c0db260f46d2ba8588e3bc2a216e14943e5be64c9fa1eafa8b9e9e56766a12de501220e72cbc877a4b25d12b613d9c4218aa8fb1e58683be7ea6c60cd917143fd60418cd70561e4d5c9ddedf514308acc18dd29969bc019daff2b06504417ed9a054892b66188d3cbcb748f1ce70fcc31eadf283969ef9c1a2c50f87e03af742c73700f80b9acb3715c3cd476ddfaee639b59f7e5f8d4d91b92943b71234d28c8b03f8b901013cf2eede7cd15b765a0539557eed44a5dcf1e950d5a7d58620133250e6f803c4cf9aabbc3ce69bd2ae8815cd35872aacbe9e85cd7e1fa4d5407b1be5f4ed5cceb306576a1e7a63745c78a36d1ce1ddc9f7c1e63d5f8f986e2d1a8982da2c236c117ef74f4204c148c4d08a91af8a621cb8c11cf93aacfbf6a4ee4266f7a735e839e9957b00cb8d2608f721283376fbade866af74fab41db55832f1a9359b59a4aa2d09c59e5795ec5c4d97ef1532e0872604abce40c1c78c31f0e0c3fbb62c3001e982e8972369ee7e545f0ec157023d158195e10e6ba81318406bcd953f79b6b8655c9387368e4429712e16ba9b3876aea8a8d8c4d7c73edec6904a00073840fedb781aa8dd833a9a43e4aef397c0754873fd9771c38136dabd61d3ef333802ca8a4297b17ab7403041422a95b81834fc7048428be58ac231d83fc1c9946f9853f540b678d0d0698caded70593cf93828213718dab245099a1ab0f2a35073f06b29ece1d97e862ddcf1c01c1d364ce22d962a394d503fd98df94135b4997fa23b573f9cee40fdea9f23153a24657233fcfb4638a434f6553b1a4e40aaa968168249118e55be550a150f3db05980a74f34817cf78d30bae0f151744ddc960f9051b2d22721529eb6e5079ef0e182e7b1236650ac7fc94d9487efdaed333709"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"34373c02266dfa4b5d95c0e1b39c4a4e1130e6ace974f2963b10ad61f855ae00","proof":"86a6a7976378876e4db5712017e8030da3a779c311108e3eeacf42bdcafab64c302426084f750e6d71203d5d19e68dd929c11d18ccad17c486eaa174c33fe130d0308a29eab24f64e1db497a58bc3c29486e8df7b2f3836ae9e8b562e1febe4acef59bac91e1af46ce7e78ea833b8fcf7d3debe3c7186f685dff9f181f57dc4022706b017c7da2c822237fd67f5044bfa408d5a77ca46fcc54e2d92c433c520ea105066eb70a96a0bf61bf98c2bd1442f87a744ec320888c02bc869a37f2e300e91752597e1b4fcd1d77c554e38ad7a901001e400e27cc324390c79317f63c0e3c3381a932e93eb495478993fe3aebfdaa26e09b095bf984b15c4b8d9cdfd569a8169dac1be64e15117c9501cdcf385168c1592ca63c5b8f309c15a9d89f0f243c7e5117059d395b1f6c084b51ec6a672406e9f23d44464cdb668957138e9e32bc313416d8e6e5739547ce99bcc65372a1582b5b1a8d1660465e8fd79c9c8e1ff211aa462aaf4f3bb3263c2d223aa6ba6d505f81db9e3c6c1a518d6f3838a0053e08f3fe6ac163d36462aea20a50c3590d390561ba478149b5a07d57cf4c6f44ec06ff01c0b1b68a973688e640dd3e0f85d141427f062c03ca1e4c61ed024e3fea80675ec2dcdb70931785ce95fa01d026476b41b44ae92a2cced1f0a6f656236ed18b0b09e002f445e215502da093c263c099be20c077430119394b03b88c4a44aa33a01a3d95ad4cef63bca73091b797ca40e288bfafaa968d57633bc36975ca071c4a4c97d30c68b7e61851bc16d26341002467bc3824ee1c893a1264fb69344cecd8c8fe2dd917fe344f8c6dea2049990509fd7c296958276a836217285ef7e7bd5dc1fdb8a2f4830a1c931a1b198894cb62398026154e350989672d0f06dabe54dfc6c9bd7d31e9834380f3d4e390f3fd0adb4dc8223b468ee37d295001"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1453e13d0e31595fd9cdd3327578b0e801383f6a24fd19b56d07b3037eb7f159","proof":"34535b995bd8fafcad151533b97440f7eedc987ddb1a197a920e17f4aaf2832ebeb9be4624303d7113aaf9bae4e002e51b29562164695c52cf185e9b5dfeff2506d1b8b8228ed935cd55508da993e56fc4a333a857ae1d5c5811d20b7af50a67fed7d27cb6524ba7ecb43cbd7d98354029519193284f128a8f17660a6ebe844ea17a9ae60a94e8ac5313c0829eb387bf6db7c6eec74002ccb7b919bbe6d30f0ea42354f0c2de78e2143d4b82c84ba0ceda5102c7ddb10fe7368655dfc40df203a63120330577fadeda63f164778c40e9efa72b36912003dccb005c78c5b3d20920ee7ebf85e9c13ccba8965a177233a4b464a6f74ad70a48cc5c5fb747ccfc54bad18a5247fae20287de0f59b8ff9fb913abb579fefa3afd1c7887f8fe1b1a072c980826c8dbbeb0cc23dcfc46642b17aca321173f93152dccbf1fe8db52286e8a5b1b54bc056d84b2b9691f96a727d3fa8871371a7744a5d4714009c9909b421a33a8e355f763f7e9c95186dedad1a66bd67515f13bbe2d3411d5a1a79ad53c56b2a56a5c08a2d140136fba74a8a8f93092fbafbb15345223db1b04ec6b7b7da8b0b0db80fce624429152583efd52709dbd81270c67f525cb166f9d88b5523e3aab9ba3900096d04371cb0b359f32bde78c3cafd41d90f9d7596a3ca815fa79aebafdad879d8a094fcf78e0b46cfc6f7088389ebb8d2db1be4144a98ca3656384b6885004e58d0423391d193f2ae3a419759be9d243c11fba8547ee4f20736f3ae4dc8706d3729e8bf849e52785f4b37deaa113d31b6bb56c2d57fcf742b00a529374483c45af2813a4e9758a1ae8bedb5a245dc604bac723caa6fc8b64fa615a95920d5b9f9de88d33b63bd23ccbd5bb04432943570dc1418535cba435b10e3f6e3d4d71ed528b8f6daf81cb43b8d4e1999d59114cb358161a88248c9fe500"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1002b746a1e9393d3035f8b8e248b30c69e6e78c5195aafb3bcbb895f6b7ef61","proof":"44faf9f7d1f17fbce8dfbf31ca1a9413635c6639d2c9e08abb735fa91c46692a08b3f4cfa96c597c96c4404b1b4e667af3feb7df020af18e398ae0f5b04d6424b662364de82c7abdd30632b36600d669bd949330217b350f445f966912e9a40af21b0985cc9e08d8ff05bed4b2c2f37c486c4e2d38b56f0700d91a527939100721f382acce662129d462c0bbcda4871d9db61f5ff817ad59f7e44c1dc9d7c70c752e6467315dd4df97b3631d23147d113d2accd1599c30b2f804558338638f00bb8cd69080cfbd4a16c0188306a57208366c6cfb121f67d145400cefc29c1c0078361f60ba645ac27d1c11bdd583a3e04911506bb6e5bcadb8dbdbc45ffde5161c3162efbf3d95b959d5f11f1b4b9ee6e044cf438837faa53df1ee8d7028d35cbac98ac587c6a16e40e2c64a570e712ed000d52ed92ac7759a08c4d5f8d8f614449aac9a7f0313439ff6463ed97128b2c268f46963c8da05aaa1b57ccc105a2f1ae5deacdbc5372301795fdc7eb68dc687f98e264e273a2fc84f7d6a37295765d05482a160468e39974efc9356c0dd5a1ed53ccf609aa691ad19841dc794e729e41d6b4627c8785a67cf15606bf3849e42654cad372181e43a1a5fa0bcb1726ae0cc04d8d9ed9110807453d0a2b98f0490d269d217fe30641fe354fe9461815ab699e1cb54f39a92216a7d4a523188a24a67e66af720cf69f8eacbbd0f75bd076c299b26a88712b45cbfdb1ab3d485830c27d6b38127d63e1d30c5b4aebd0f2a627f3077293ae8334e277107ca84710098881ee0a238c227f4c743bd17d8900e703e9821c9c2825c6d153ad243a7771bef271e9c6956a66c4228ec6faf367d7025c7a664e36db255df7b49dd9af7fe692e7229e64075d72e363a6598b9c06a0894d786076949f4f0e262eaa55c256c8798a3fbd6d0f5129db4604ac7ec1b4c04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"72ffdd3d2034314559808b5e2fadcfae47ceaf329439ebc8852b1d8e31e1e760","proof":"ba3342d4c83f4c243956b75aced1aa2b973e10cd052c46f9a940a4ea8c24df03eca4d614426b8a1b04a3ba30623fbf09f32bce1161a18365e6a37f3ed852167f063bfa6c5959f7beff9301396cdbd281518ce63983ca92be280abf442cbf8f4028a836e14990fad929a456946306a519a92a0aa6b1978213b0db6b93b9de4b3a9de1a913e09f2995ceab65390373f48800c68d5f251093b3b6c2bf9815fb030f84dc5cc7e3a063b73328002dbc6e80653e4d211f5e5b5e82047dd778a817570b0ebbcec455038faefc5fad4964e865e44be7fc390beae77828d822848445980968469ad9f7d353a38355f2892227ebadc137dd7d440d388128275569fe38e268204f19011fc0c264944cc7cfe1550ceaa270965e8bf1eca5d1619d412b1cf35586726ffb0b22bec072481dea84c2e15995efa229f56d3cc1c9f81de7ab60550902e8a2fb678aaf53a448c697c27c370120f53a22ffdf24e6c8f2e8820bf820086cbc0fd533a686e483a85214a73394df1841f3cbf59e174d0db697e9820e197d2824f44ec20c25f5e01c7ef0407a1364c0db80382e36929b23c25851b2691f43285335d04628edd76489a28c592c755e295705d30d8a7823bbd11e7cea129058867c8fb4db6d82eeba174079e5f757593331f85d0ba00dec76798c5257ffc80a5c831ff77136da8c247d6bef7bced10da6f5eab5fd3e13d82a911cac192ff36ce03e13faf5680794ba91ae0059ab3dc10f00975e18415818cd93d1127baf1755ce9c1349e3884fe212b8e91a8a719f38dc2d515aa974c7b3f92075e3fc3bd13ab6b365c1e81bbfca9197dfef0ce83407c77fdfc483db106a98d0b07d9acec1764735316ff94a0b2e5f17c423efe0feb2d0c08bebd27f630665f8380e46e3fb05c5d630e52ae8b6794d5669fc57dc7b9ffb5d1f94d3375cf72cb0541e05e06d0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f6b0366b84c915a53cc4c2f747647faa5c850f59fb824e22848270c7d6c78848","proof":"8c7dad53eb2f7a0b93bf1cfb35c45ad333008b0f0d4f8b54f917bbc5e4570b3516f490cce9a6151a93fbdba17477a343db78c70124dce0587e44e7e8e5818c1b66c5b05a4f926bdf23b738812d8223f8eb4297628209bcd12d821398e878b17d74d76ff7724769e4e6f7920cbf69546f409ae64fa3c5d5167a923ad8aa08e87289fb31f739f98b991f530cb962640b44014c32a77ca6c35e2ebf37597fae3303fb457345df29bc5c357d4931d6babaef3b872a032dcf0b11a1fdfcccb9902a01eac3fc34bebb1569894b39e1ead16a903554913a85dde7e69ba550146ce66a0fcae6b630ac95b759d505df00d3541224d4ec962e43bc9d9048dc4aa79ed3ec4f6e0f41b0aa5d360e775ad4551cda6008c6876708c38b3e670df5da4bc418972b4a8830d6cdfbfd5cf98877cc79c51740b6cfb8e710dacf13d794899cddc0c91cb00bd9d458e05e6e3fd4fecea513d7ae5de637fc894a28dd7ee2fc220e21f11042df66ca946c772964f9acd4bb50377a7e1f5d64db3e4db47fe670055f12b7315622d98dd9eb4574b918c3e9a1a4e8db72248e2958510fac79b5e1f5c7f60a194e30fcf23385eb6105b6a853a01b837395956d524ac481450cc773a55296ab4a52cd61ae0a6932142c5be6150f4e98a7b27a6d12f857d773e51f103d656050590e6b14909c92558f0488dbd294e46212bdeb3d30c8896d97d87600c82589ae388e10253b36f31776e6a8244b4dfaf30f3d3eabc53c2cf3faf54c1828d327d81fcc2e66f34ffa258c91295cecab44fe827156389c08ea503adc93b5bf3810ae71a803f7d50fc663e6eddd999f88e11d776142893adba3239c88d7bf5bac9e971d779abf51746308b2afc0a76ccbec4d3a4d0715f3829ef811a73fac657da6a3081831e3d1bdb6e2f3d5703b3421c78cea7e51c8bf221639135d51e336c638dc08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"baf64ffc1c5f5eeffd9d19e1073042aa5b5963d622044e1b0ef679166d7e3a26","proof":"b84cfe3ce8b7c69e0a4db3a7f44797167edd63d4d965af94adcb1de6382bf3598eb8366b5781f546b46d6d70568136aa8d78290437e3344da5afb824715a4376f8b69c9295eb3a9b54345e7ea7d4f7a1c34ae70968fe8def8b9e6deeb2f4c377ae43a96f19b90d3dadb0d2ac071b6edc2da0a88d8bd8d370f98ec32d51c0560cbbda177376dcc72b0c5d358d70aac73104b7e0c0ba2a05242ce1c8a66a69bb099188f6542dc15dcafbc4f33619b155f8864e50ba5b4ac0e08a12eeb015369209f9b31d507506454faed0aa363967744856d3596897604c480798c7a2f215d101901a9d83ee0709a32a9d43cdfd02bdf85c180c9fdf3d2db71c42160d8d852a50122d0175ec887850480f5a6cd26faddd66bd866789fc2a3c6ea50b886ef214615c80a41bc433d748e0f9fdff7f8bc218a090a58ea89a08a1e62cfe5e97321a0abe8e51f74964415582a4ab9e795fe745a4ac0cec6c4608cbb1a229c706118c2b78389990afc98a1cd100f0a462159e53d88354983485cff530d95249f16aa462761b71506f9e908611378baf86d5fc2fc0c0b1ff3bb557f7c775081a8ea67e5d0093b2a7cf85a769d0d3618c672ce8be5f08e9575bb37755d4e8b3a9f4248f47846aeaf30a07861a82756144a8eccdeacee830e2949e2dc5f9432e034fcc5a17a492aef0a5b326c8bba7ee5e4c60f47c675c9edfa519f3f84ac625005c01a255dc930224365f9ffed2e6e87123180a09e40f93f864df31a94f584729e899005bf8c602fecaa1d0c45e8b6ed4cbabd0ebdc60f6c5cfae1ce39be1b0b444c1f27772bd15634f97d9e7d579f0dddd0fcb2d6fd895fe8fe00e9af390c2540de5f41a1e91ceca0332a11be4283dfb64f5f3e4daf9e22666e65315c972378312132e0012b05b4e72997f02366dea1913e6ed8f9e6cedf21783520244a575832bb8510b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5601856e2e6beebe84db7819660b6818983efeb70948d055122ee12c12b3165e","proof":"72ae7d1a75a7d86795fda29391625c27665efa34d6e4dcdb3e482135aea2c0286e95cd6f0ff88aa8fa80dd43d4c9161728b51e6a72b0ce21939030033f63275eba1591197ef6adc49664d6c1cdcab682ab03fea234df06d5e8ab9c88dc7f254c705884e8e70c899b4ff986a0c003a377849ff393f67dafdb6e36b56dc3443616ab564e93648739057de04f4b8d61c4458a6bf925dac755059d461389262b5602b07818f99381058f96e5c975463111c3a49224c375adb57a49e2fa43d6731d031bb377e5d28153fafc920ee362cd9d858dbe0d20b37196a73587067c541a760b74a30732befbd604f2e077afda6ba383ad06b3961922b0bac775fe9e9c9d2f7cae5616a7a61f8c43ee948db5104f076600a6cef9694dadd2046993b32a1731117654613f42dee4abd3adb9cd14b1550f3023743e1b57097d1df07aaaa537b343d8030d7b2cdb3a541688444d51f3ffcf52e3b2c37fa6ad0e83f33d8e5b7fcb2f9c1999f3a7f0d5a7e72511d8290c017e44ab6a4846db187da44ec02df637ff5adc5012fce1b590e9e9f2003f0b6d39b48a0585d0d3d9a65e3fca1c64cdf84113e26dd51e78f2720b271d486782e9a0ce217f00e6dab7413de5cbc7828a45f922ce1887577e8e2d94dcabdb4ff8f743b58c7438251e5456c26d8f6a2a73e98479901223ff1afef2241cf5ef134cd1e3b72fbdb39074e7b07e3665d1b8c6131e1d38d829398a008dce005eb897230d0c95816b7396337c45af066fda5ccd61651788cfbae359a5d36723550de74dbadf8ca035f83b18d503bc5a785eb04f667e281e4c6ed76361e93a036c9a7a7a50f181881730f41c1b7d4e2e795e1cde27ec3ef95093f49aa113beaa0957e139ce454f6355c2480dc95f59780956f73d741b0205ed80e5851c3defec779a479eef0fafdf896e0b43d42d44181ce7ec697d0408"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ae8bdbe3b50314eb0567e7c3100379c530fff9d0d7083a0b4ed95c57f9e3333b","proof":"60ae7b8f83e712a3213577a5a98d4b98c2cf5ae4ff39a0afde05ed82356b2d28e06642f331fd1bd3a6e9eb184b30c115a3e5fa62a7c587cd7d7554d75b0bcf47dcdd2ae578feea75de317b258716352113c78016b01f630c88effb79a0edbe1512b3fbbdddba247d2232be1190f5147d7a06072012c01d430e3ae08d69739275fd8c5e8b2ccf0c4b9b6539d61146d5606bc5be0c144899b0fd4b30703651a4007196b30c2e1d8444843e2191c59681245784ac4d1a1db096a8cd224eb736b20d123caa9aa78846808d207dd8016697de8aeb6b57ddea53634725da590f286f0f2c077590550eda7d3fb0cfc26cbe2db5c4d6c0526dec8970009b0d644e3ac318ccf266e21daf6240028fa27bef1014a1eada2458ded5a8fb19d70edde804302d848e95a1a240b6c69775a540ce6bec07d8816739121f13ad4b0bcdff1bcf3f66526e1cae06753bede38cf6044cf66cc4b3a3416d28e2888c9d87cde87ae1e57ccaeee2c0de03a73f26ea0f6974d8eb6b0d90b608720c96a647e5e7c473dc8405fe226b2c13bf0177869f6bf4fa842ccf72c99d2591df0eeceefdaba3beb41e0d1251f3e093a7723668ee1c9b0c5d1cf0e0c3240c7508a0e8e10544dcf8b4d66450e27e710afe91b9b45d60ab05e9fcb13c8d385e03b1e542061299ba93e1806c08ddfaf2dc50606ee8affbf3b4ebed9a6c989a938a4362125fea32a6575bb27d803615f0bc2aff857eaedd522375f165b1007ca38893b0724b72b59ae439aa72ba1c0ae2585210c8d98d03282c74956c22fc587b906ac298190b1376a49383794ac67833379a9147e359aabfd35ee90431f44f4bfd9cc0c44ce69e9ed4d09e2d2dab2f9b13663d158db4271077ceef1bb3a41bd63c3df838f5c5c21e80e68a02f67c87197648a5265bf799612d89d5ad10f92d594256ca626f797763a491d600"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a0f5099892a07f69ff8e6a231f6a71076f85de8593e789d43a112b5ab4a39379","proof":"2409d4de5648068106001d15833e6dda89b628403a6d932b6d574a16d273bc788a965704a38d12dbb8bd0721b0a80d70d3416393fbc7286f51526afe6b3eac46b011f43b6bdb60b73b9240ff9dfcf337fcbfad75cdab3608e26554c893320b0706500b8ed8094408ee7055a141a0673c39da8514cdc879c222516427579799529ef9a2bdecf72f6abd388c39356468bb3f534b11ea0a1ab430f1e5524901b60d8de77fa7cfb1f8c6b90f174fb70b33764360b0ebf7b3317128f6d9ad3ce2fb0dc611844320cdb6f902ea08d58562abcd6844b2ba02163dec848a5fec5e1f4c0742db5444ca8c1c1a9add07bdbc867f471ba758e0c89527cb006199f7bff3c6146273da52ff4e6367336c5e3db41c9cc9d94ace789f9eb3640b7452c49f6ec35cb23d47ad0997a23db20b05b8c537d883a89f38dd6856ea33cc4f0b59b1dea46b9415c3d826571084a349eebd805d8ae3dbae63504e8d26acbca50b9cabc04972446dca63c449d2cd027bb1539b389634bb4b866d72563e890905f8ffad83c2392a0a51d149c49ea3420be43445b952ace8186864def9f597bf4dab868aff4739361b7c1c3ddb90c4c75b02fddd498e2a53b4d7975e6cb5016db7533d2368a713fa9c7140042de7341de94582f6546ebf444042db20997541cc5fb57799a4117a20061bb05a30694caed3aaf9133c3ab5be58eba7d58ff9e0561f02b13b814124026f2b6269860830aaa684009dac18b4616511746eb84ca5938d48fa82df273736a722050d8851459a6ad4cda28e1580a4a3331e419d2d52ba76bd61eefa16399ea9cef3efa18c5ebfc632d5589b17c5deec2b0b308676df11f3e20a1f5fd064bd97af9ec8eb559cb5a9ce072a0daa6b3b5a86d444e7079a3f21b19dae13ad0b5657bb4cf33cfbddd0ee71326a4482152a706121bba3d18fc32581291cf39c0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6226a0e06ac8a5417b9026c460549510337956018c989ec2645e8f18f10f2a54","proof":"e2350af0a51a18c00ddfeea2afc1fb4a1879c8624cb23fd926ad469f8b9e92798e6c93bad5f115ca205ee0c984975df2a80b815d523fbb48491db9cb767f441918784a3cbef4db6cbd1cc64a73ad79aa2fe39b4048087ef6b26942ed4e2ebf77084afb44d5401c49d67bb86c4cd377fc0023d822595e66a316a030e835b75d7bb1e5ede991ddf07093357a54631bc0a8ebd2472efd5851e3d98f35f428a13200e05dd660aafa0c5f815ec3f9e474e5b72fb86dcdc304567fd34f88295d36e30ae8b8eba7caf2f07c9dde4fa49739910128f0ab77109fd5166046b2347848160bf8198a357bf3a99091ed08caa0500a3989ace9777ef679cfed80fbdc2b977b0aa01919096b0ebb9989a9a6cbd4f08ca44d7aec75bd16e08563f7a3ea28436145babf67f14ee1b6f8701bd27e0b3891aee5a55f2bccbb1b6f123d686562d4eb1e727950cedf0ca380a650100cb668ca1c5da44f482cc8babd4914e5edff1557108676fb9be54c546152e1ace2a47e591282fe7254dce1658191613fd98f095f39cafa4bc2a7db2b5ee6fdf7e288d8b53f3cc929fcef185222151bbc603b4ca27c7eb7b624868e49330ce8fbbce3608eaa6d955e34f5bd791d971b5722656894254821218b13579f11827f77bb4d59cf29f9f00db8f032ffcbfde76341cfab7f1ac0fed6f07bc5fe90c75ad45e2c916f9bc68c450481c8347bc2778c10934729480ad0104eca210d115eb4be093c82958aca579d166a0469d1a7fea0ba4b69d74036f4743b7e82b19856c19ca68864016a7c7859853cdf87c05d112208ef234a111011af22701168c49bbb40ee14800c8af79f2d3165ff727457bf13a9dc5d87416138cab30a9edc3329650f59fd956aa8326ebe68dfbcd5414d8f8179fc1c8d02676a6d799a12c0473fbcc5f510e29bdfac5ae8e1bca222bec806ed68e5d4520b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"985d0e4f2833bbcc93d61c542c0fda00dbd1f9fde34d7cda1de5ddda30e6b07c","proof":"c6ca066a9afe9cfdc35a02aa003e522aa5f3d3f90cac8895c57f48b95f1d450e4e1b3c4011b25e41700ab80e557f584eca2bb9fb008d93faef0e54b5b5cb13381c53c499e080a1954f2f1a0c8491049675460e2fda207960dc14bfd97f30e1781241e8b457896c8a05ce10d28ad5e7864a4022dc22fbf78757c4462dd24fce3198bebbcbdb992bd2fee031580dd0f237cceda2feb1b56872a125633134ea920774798f35011bb9106bcde3e9067fe5ba5ad868199772ae455b3e1c625ef81105942c147a0df0a3dd9a68e14ec03eb847665d1dca735793ff7273c46de9fca80df6503a589f5da647c18ce0ca3bcb886d7dcc2b9bfa1a5789bbeee1844dddf46eb2ed5ee53a28b4d20dd9e3e64ce849abec524ef32c2950aad68d6919406fd64148ba2b395d4538ed3e93b24418a4c674d801ad5fa40f79cf80eaa40929355a4726a48b82f6a7106a1cf8d319b59c8b7ae40ffaac574d465d0dae5a48b4ade724aef881112c2a134835a58fd979d8f48dc69d701733da585aab21c706676fe25d72f880b19795a98ec518b12e5d9c0b489f19d08e30b004505d015da04e185644fc74852456eba878a22891b94cf52bc7225e8e0c470fe33dd99a53115e874243b02421c0c3fef45db79fb7c646852636d85c0f198456e18bce7e1ea63fe72564e085f5d31e713ce4c2924709ae84765af7cb72af56e067752ece9dffd4dc6f1092638308580aef3f9f72274e1287857269e2fd45e934e0dfbc9f2735154a23735c305cd8094637cd376fcec9cccbdbd27e0822d7fd066c6a384500c001173e01dc4929b8b6b736ddc1e538a26fab359f81c49aaec0777df9ba86101d1f80ce3d2538e52d08edd13355a9dc9882a262b5d2aaa8c61b3a5eae601168790308aa006318eb3ffe2949564946fa6a79f242dc77b6c9d780a6214c294245b0dc1f8f0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"669d1ba92f54dcb92afce3d7a6d2a55e1d353fef0c30981d8d9cf67596d5cf1f","proof":"d47687fb50925cf3fb4c95983719eb27608030c5e3859e2ee12abd103464d01a782c3e6be81f969349729e0ac988464ed5ec56ccf087c8ecbb11d250e557bc50b4aed97f6410e47b8d70f684c9f855adb6767f597ca29b02d8c51fe4f0b4406bf63be530a7ae4379e441f8ab641d1ecb4b8908327d507977a18770b6e2fdd65f90857d5416035641ee7dc01d7a27ec8ca12176f967edb5de9b9d71e4ee203d0766f2ffac56c65430d429ccb321116288867c3a29611c4b43fcea9ce85d36620390a1683e3528fb6fceb3db764a3145c33838d61c4ac85bc3031596c6b007d80fa8ac3bb1a26e30e11cff4b3a2dc84cb3b21f13404f7e9acd82cb90e40a6369508a08288e93ffcca8849eca90270eaf418118e1e28423736d782423a94b0ea37d14f422ca7a70a51b85a0941789f7d0b858c6e297ce86adfb8779db94f8955c663ef4785921019c7a31096a0aa4dcb4354135feafd90b7036f541b5672acae978ec1e974505f3c1e9ce38a8c451c534f5d8b112dbb8dd62f69536ebe50e5cc547ac5100585d18e2cbec0e86b42e984f0cbe1a6ef41deacc55fbef3f477ebcf10216206168e809ba02642aeb5fd985f6097c75cf1f266c39760e748370999a9520905d7da37741cd5c2bc2777fa5c175edd3e8ec15e3047c1200ad6da24d733a31c8a844123fed9c1baeaa0a84a8eeebf81ffbc2869eddd06e2ab1f508a28da83b36b7764b07251f7772e2bfe99c1e5685f494462c80878fca53f6087db3cae07dbe9ec2552449832e79ef65ca8a6bfa4bfc4d7eba531443f5d60a57780ca50d32966a0cc9e3d73fd71df6c29ec9717e380eefc4b2e31bc860083de213f675fe24aefb7bc765951d756b1ca217b9307562073f31a5ace03c285608c21c643ee705a77a5f64da2a1b355f9662dcaf5a3fae4ca713c242c75708d38ddcb7d4347909"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2c4413cf2c03086af4c4c92f0995a66230ad5e5f6b9e84df4102d9234626fb32","proof":"bc5f76394e72869ca4d719ffbfe83551e327c286671c893892ecaea93b37a5729c7d74a2ed4941ae6e48df4784b2101e18fcb125b3dcf9e6047bb2f7d32825314687fde5e7bc76bf939d503256b35c235bb7dfeecc587affd0bbd2b32740c6011ef7405f77b0b8871de20bcb090a8bc18a2c2a656a310114a2aca96b794c7d0dfe8702b31d06b180982753768be1b5d054334620b69c27042aa8ba2b2c0e970cf2a74be73a602777e15e0f230baf0faf25938dd48c80176ef977b3a7dfb4080d746a77b22320623e176bfafc3bbd16c7f3a71975aa2229af0054a42ab372f00c96df11fbc27dd494819688b77a3988b057a0a1d725077f706acf8fe0c4b2cb7b3631bc48db9201d3d235b1fd29bf03956a2eb386e454e0675fb538a32bef103fb616dc7830f6584a586407fc181aab3a503a363a250452ff693372ca738a20004ca27aabe79d81e4b5d8eb91b21276d0e905e21f8555e46a48acad1ed45ed005766ba9e8d03833b1756b42656bb4e0f6022f637c4885c7d339afca1ad95db170f858dc95b33f6e8a8a53b6c3198df3d4355115a19b4716fabd4795151415224648d8f7fa695da3582776315dd0c9e654aad498062a00e0d14ff5e7aa1bbd7d0eb6419e2c3d09ad7db4da5a54680dc3d1692bbe9393b57a51e8a9e91d93253671a01e994b6218b1c978ad3630415f3e4a8dc7b7f2562096990c3d8abe1ec6206702c60166dcee286854900c58d6cfe0334a87748bbbfb31bda30d2ec18c793956dab39b77b7ae2f453e8a57ada2ab53d5dc3658d9acf0db879c237b3a378d274c3e13cc2325adc57f773b251507d9904b894fba78ad765cb163186022e9100c7b8c107d14191d5eb28b4f589f4be19867c0afba9c24a194da7afb08f95505a30afacb665e8898ef87edc55d4bf12f7ef08c067965c988c96b974d821666b5d50e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"04a3f50bc1a0aac3e31598373865a8cbe410e9a634772dff5ddac681f210f509","proof":"6c54f3cc15bf648b58ca2c8114b289330326948f01ce40f0daaf6d73a4df5d5fa6c675dd68a9e7635c92d286d6ca6eabb8b1ecf4970366cb8c8700f0ea390a30f0d14abb2dddc8e84fbcd0cf44aa3d355d96f65705994f1881644ca62011e166687f77598dd2041c3d3b8cb3bffe128635cdfaae9e0e932ced40207a289eac27a4cb838739821d174a59efc898557a11d29ecbc21fb41f5743f979b7be015e025121d6b8db27fd08b99bda6547b1afbda328b8854611e33d041f7f77aa045806736abdf15cbe67b6b8d6d2c81eed8adcc975d2d389adf160d0db3156a3da6305827f76acd6541281f668f24569b46df5d812c9e765db8a6ea5664d6163d1bc45c6561ae16b65957e7a0b8c5a0c06a492f488fc02ef65323a1599e66d83929f53424c7ac5626f249c6ab1ba2fd63806a7c084522a8c42871e26d284c2ed38da29927c753c5212e1088fcc97f1a9557cfa78dec3819e661cc28dc8a950f1ba3641dadbf6f30eeeb3b84cef652941bbeb4ad54e466c9cb1c4e6bd2b14e5e2d1154b40baeefc439c2574a32266f464e15f208c51c673863ffb4044edb87fe68ba75ee6d9ad73c3b0d63296d709964f9f8d60b15a6635355a7fd0f069f6e07f3fef72bc165a9fe822d33050f28bb000e0fa81822c8c24d961c55eb25cb2bb78d7326d14c690c74dc1998b874a0a65c5b6ff944950a79fd6796df471c251a1e697c65134fb6ea8caf2786232d7872c830c471b77c9b40c45d6ff4ad4c4ff0637a0413588a6caf4e6c900f09931f0feb19b28f682778794cac76b7a91511b465cda271b82b63a2df125a1052932f76100d43b71e134bb665578bb5ebf8f97d05f68823ade4ea4f8a83a34837ac8fa2da5cb184b8ad13e0749e527742703519325e76f0ba65fbe8ae9ca8944ae4354fe02ea1620541b3c75bbdb587e801b435b5af37f00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4ee1a00befa57e3a7368bc158249d53e8d2c574ebf38e9356f4422b74ddb1d42","proof":"12bc5dd394149df148410f20155e663ecbb848d143208ba99457d431d236ae32084e45885f4825d2b62f5e38fb6811439fc80c9fe986c56aec0de2049ed25e5768e7bba483b24d02fa59a08246b5a3e9b819cf9805fe962cc760d0c0749e3f12401aadbb19dabd152d3a7dad9468f504f83613f62a1356cb35630b2a9b75f7503b48506ed09595d6dee3c2283113b4d89c1b7f4a69bfd4f310f9d26d2c9c8f0bffbf3a63a9b5b5b454ea4db08839eec12ff3e36ce9d61a62ca69f0f0fc68ff0875c9037e135133d916dabd2aa75905c3c857d13b3b8cbb4771a9d4ad71051507ba74c21291ad8d98cccadac6ff9aea4de8cfa24499900ed14ad44bdc269eed6ada5cbaacf881bbb50c28e568dcf16557d23f2d3e8350502aa513751d077d474bceba223a0f0649efc87733d94c4c716482927c566c000c79db4c65e238d73e6fb4c848dc21156b02e542ef9b61d3d1f0612882577dd6d6b58cfd4e921cf3d524985cb3135efd9de24f220e8d22e6d47645ab6d690e3311eeb33b8aebf6c2eb4d4aeaaf5eacac6286a040b5b41661c9d9abf2fa889de1303082c86c03943b97545a939feba1b025d48ab018b0c0466556bf851702f09f5ad87cbe8de8323a9b40645abe72800be47ef72065230544032f876ff3702ba973b430c1ffa3df34216844117e915601f808b7a5046fb163c0b0c0b7284707bed11b326f58aa23bfdd1986044322a25768654b8833fc9213e96c24dc19d24e7c389e83c1d47505128440a60f4090707c58423f74e5d5549ef26fa49260010d50d1baa6f6b7b110116f266c67472ed868295ec12dfcf87de6f7fff51996094688f7c85e1985dee5679d4e61b35e12e5ba637e65efc8fcc26a9f846fdebe977821bce055fede4fbcb8940dda7880584d01efe2f915a0f1483373c01acb67093d2f73eb964afabedab7510e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6694323b56b600b85efc220c346e947c884dd8427e3e14ce26049310143abf53","proof":"da7a5249944b486d25b7dc14fbea7c4420ade7299d67b6104a0df3bffc6cbe17a252455288801eee4aad274315fd2fe2aa7ce3968df24bb35c070c91ad0a6e1f4cbac0b89139a8443d964ec83bffd38a27fbcba1f632ccaf1ca487ff85300d79181c73e5540793b48e6d04dc4b5d49f00460a9e814dc5593ea15a78aaa53643a1dee171bec8fb5685d0187a8a1992932a81f82c36603635746d0c86d9a849e047fb22b068659f20dde2d551002ab5c6f6696294a23337713da1f07562f003604187c7e6b2f1265c6e5531d792aab045f6d94d0a1da90466dc1090af39db7d00f20f2ba7a52dea4184c5de6dc901a1ffdede88cee308f451f69ad938126d6cf4d9cbf606dc0f5585300b292be4ac39b1f704d30798e671ec83b8ec01559c5c0500a52a57e7cd1b4c47af4971dced51a4f594557341e70e909876bac76394b3b3d3a1e6d780ffb97480d2ec7042ed683910bec8e7121e7df12afa2512261596e3950402dee93ffb4c275d6be477902ed7f88be417591bd4c99920f1af9be858646da6974310da5909b647944156ef6f577509f8b1d9f60ca93c17af0d3d84c4657aa0437faadf635491122b44fa3a72465c3688cdb2b4879f117c96f83d843ea27fc1c06956edbc921382d6bf7d599fd342f6e7b8d3f3b5b61f69548170c1f0744c0778688efd9af0eb8f907f5704e5892f9d2f898f383651152bfafe2f6d38842dcf335e084ee8cfa781e0420de7b406d9c76825161eefd3456e2678580a8bd0484d608c9c8c613bfeaf8c74edf1a2121b0d8de894fbc4be8465c3f6d0f13d7755efdf68782492898f15d10c2d9a8071cb74fb1c4ffc5a50d10479af6a62c5c60c5220e31f8d601ee70a07b90e640e6730e5b2f640c45d897a52117e903bbeb07537fcc982c75b938d01132cf4876e378aeb664a86cad0425c95f2a17b6e10e05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"36cd152498053d40eb05c255fc1c6c9476cbb33f2dc86bdf2cea984f74f6b370","proof":"e8ccff32794a659a11bff48b8e864d185fb6885ae2b0cb28e15337d186cd1d389a0c0cc45eb7dcd24067e32a7ebb8e7444733e20681e62eecb8e2a0a355826145a3444cb69a075145c826672b1f5e14a072289249114ebc6edfc9e2f8bdd7a5722f583952fde88f45f8ca955c36f82ee964bb07c89d725d2e9ea1f9be479945928755f7f7a7b8f4ee837c8b7ca8e8dda3cdb408589ea0d50d4d43f106506c70f400f186aa8519e3d3fe2a8f6322c2cb3c5bccdce4b6fceab5b545846e85a0302b1db0e1cbf0a2737f07fceb77134ffbfcb86a7998a46b89c3ea64d374c0bed09bcc7fac6efd81888e00b7c13258ff814a30b05c9b9391da30f73b1daad7183722cc8410e68c7e3fb9d576a90f92b417dbba143e70d042bc01b6fd2786b10ef0932887b53df6c11a171b9817e8e620eb0748987feae1f240fa93ec0976e3bc1355c9402637a644ed10fb94261a2e433ec866f7f76769de231cc5560b3ecdaaf06747bbd04dc05730818be350529bc22404791dd5ecf5a11dfecc532e40b66190a101af39a8dac8910234170e8ed0fa93d312198327bd3d5a2726c0e5f79d15c766ce2f332381a645d5902fd4535a6e3035169005e8529c932a4a0e55d749acb63c03400460bd2fbd0556dd34409e81b61a146fd313970bfcd732bce2785b65d48728bec64e3a0bf075165a875e57c322188e8ada2abb63941699b9f23ac96413d3656207ec46309347e142d2731e88979f08caf16a3fbab52bbd3d7fd9493d40beee93aecdd6adb3d7ed3ba127fd38d1d0548bbbbc63f97542b61177928c0977dbe14e1a9fe60724be48a7d365b481b92cfd498b1ff964a7b52f128da9846d030f57a440f1daa827aaa6d38a7b3857789389b8cb8c2fd30017b791bbebaebdc0d6e6566af236bc7ed7971c4b1e4951add5c7017680f40eb890ed46ca51c47db01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c050ba7f21bc0b90224dbc0cb47a7b367da0db84f9f925d154378f9ba54b0d3c","proof":"aa14d964aa72535b5e365d06f3d5983d5f6a743629d8cd9f9956ffc301f24a19aa4fd63459ed7dd0557ba66ed15aec2e77f0267d13b7f5e2f09ce97247a19e350e54f9f8378e89f260d2394eb9f7c3e1a128e9a004f279a6ea8532fe569c0560f61a6733db5afd3f6e1bc1082adb22d779faf538ded03e1953a7f3b797fadf589c945d23925d29a7a6e882228e709aa74252709d0ff0a7d5ff00a8e6e742e509a4776ea50a5c7177e8ae6c3e0ecf36b9704d83769a7c8a6b9970b5ecb0c78d0e94cc72110293c2c0c51dca1226259c20f02530f40910fa7cb4cd04a44ec8650a084f67a322212b4738fe63260f5bb4046576f9481d73214c2e87d8a104f4cc2850cc60eaa1519ca52cf030568ab2436f121adda749c516272ca91d4d7f3c3271f212815cb1dbffe815dd6d5866fd6d5d61e262be4e240b377e60432acb39c81f9a08f363822660368e224e35ab07eab4f718ecbeaee76864d2c608b6bee5dd55baecd325a64809f74ba8cf4b3a1434686b53d956024a209ddefe578876a9164dfebb28183c6d351099b162d245bd889a0e3445ef6af69fc0afe6b990d8e7b92d58305f72a17edaff4d6b5ab522fba165a2ed6cc3f78659b6bae9c0205ca590209ae95fb69de1e7c229f94a52d155d0ba94370a4d02c0068a84077fcfb5dc4420ee35835972936be6b0fba481be10c66db0c7a08132144fa186510786a0a484591e33857928d30c84c80f524738fe7e83f355fc9dfa7becc854da74c506a3132dfe1ff76a24ea9a8f7f9eaca876655c481021f977827807c70567ed17a62aad70f4e0c9db1288f35327f6f37c21df0c4c21bf9eed785ea7e42e8c95b70d8edf4ba2354186cae40e3a312ebbf92c45c686971bc2d51d00178a917279386b3a1d0c9d129234e58d00425289bd4d1d423a7f32d06d734c810b64b4b61f94405e610e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aa8460c8079b6567c27915d3c24c30c29f48429d68bc79796a2814d31d9bd858","proof":"6e22477a7b3ca1ad5c397fee9202c659cd857a87b078cec6a9a6b47d3cf00634ac4be42073fac172acc8feff4e2a3bf02db75261ba036db38d31383c78f7ab06c8bd01e960669f9670767d33cea179303a5be1a2a63af525c0d1699cd10f1b308a6a73ed434d9f7fe65d32d159d59cb99e0ce5ce64d9224718660e7aa268041f502ff5670bb968cf9aeae74803fec4ced1e1427761d9956ed95b8bd3980264042732ab710515db18e798fe0877a8cafa185cd0f420e6e555d08a3311b8a76e046d94902d41ed9d1fd7fbe01dab7f4fba09e883b4f6bedc9c1c0165dc2393a603c07c5b3d1555bed52d39ff521047c9348e48972ff82ae57034dee2a9f839e70e505f52b2e2bace41ecaf3620f0092cef5b1ec7abaffa0ad0808216b32effa153b82a95d3f6a35f35087ab58a9350f70d679a98e2066f25807ca65f30c4268c08d89d9e26703f72a2ed29b3105045655f37719a3b470feac7c5999a0f1f4044399ecbdb2b9985c85b982348d63ab957949c25b912586f5f7750f43269bb4a16507c5f2497c2a013b47788f5fd644247a9124f7d31ff8c873ead4bc173e783900ae81d555518ca2c641f797f57619da70d2e543506d2cfbb6d770b4134fdab3a46fe9a7e8bea52f02bfff41030e082965dca77d8fcd4a7b3d39bd9c19017c7ea751c844cafa5c92a9650393973d6a85d4693ed567897b3123660ecd152d573826e3a6607a6115898e5bb3971680058b533d4fd0e59ed409628121e68e5c1ec13784a0d7abe8aca053bf8ce13f91d191100bf7bb94d8699f169a5a4bbec88b41630e6ca4a67854b1f612347f695a2c8254970050446ebbcebde4acdec3e703c4858bd93b7eb5b3e56ebc63caf581441bc2e4ce998d921b205e3803ca0d3d7861103f82c51483b2116b8e58f65f746d7c3cdc0ff8fd789af58b3a989ed6a84ea080b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8c23038d02201856dc8a271b0d7baa1a32f32915b248624f0805595aa4f42631","proof":"600e47eadc1d937d508e4f89cf7f5b6b95e4443d6eda0f7c54a4a04fdd68b300945f3c451b55fd6ae1a9e536d4efaa440567ac59cc4aafc2b917fc43868cf51faabbba698c1ef038d2c97fcbb8438e2830a0a5d97402c118f1d9862be879565c8af8c25ca4aea93a8d5528a68838792a5770be6dfcb2f837d377f0b305d35f626a8759ac802315ded12a86ebac8c1c6d7d661b5154f011559f96be1f3009f803ce0d6d81ac64dd4db1a32dd8b3a588b491b7de3c36e3a068d1dceda1743eb2075dcbacde7eb89a9ce2302681d0dbf9f1ed02a190708fea85fafca53ebf1ec906da330454a9cda987d280537b1e47729fda60aa417100ef46ebba94519057356c7054be9c3884d4567f0eb3eded64243faf4f78ccdf04e995f24361c6e8f9986f08778ac23bedcbcda35b6dca8e8070d7de9dac50628ec56af71f68fad2885d6c44ed9fe1b54268b24ada4c868127e4f826456292728989542985a19a3312354132d9d12f99f25442f7f4554b4fe3566018ca42cfab386adfd6dc7a988d5b4402448d453f72e93997ea77d760ca45da7e4f1f882c95cd081ee5aab3a830e0ec656aa3e07f76df41440310c5f393214a8cd222fbe0230f429059ac4e0934cc7a34c67f11345b9b6af93c9e9b65265cdfd6dfa3b9073fbd7fee359eba0445a52d56149ffee2d1b70103fe4dec63bc102de8e93f93f41af6f2d283e930833d09be7260ff6f89395568082cc9463e08f6d33f965f4ff7584f891531a0a2a03405354f161ccdc3b72b706360edcb2a1a9368f0f2d33c3cdcfc53adc53d35caa47b2908ec1b4798968e639a4d9103325c5df18b1357d9ac3b9ae7cedcf660055eb32d4830e75508e16af8284158bcf9c304e462ca129dab0a70d48d492e7d94b2e95c0737e49ba89e9a756cfb959045c0762b0d4e974e19d3c432ba7b1c8f8102338802"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1e7622f4accc7e9f7f0c64e322f4e76968fdeef466dec7daa245403b0869f40a","proof":"c031f8513a387e4b7e4770fc483b1d271404f6162009b2a8a265c0b6c631180e78bec6074eefe8180cb67638b30970d2b4205e586de53f6d6a9d3404586d792934935e21062f96449cb712693897b0d6d3c045be6aada9e18bc8f523848a83125a06086d22db5b0f69b693f5eb04f125fe8f3f84be0065a6b6b567fe33781636f01d0dcaf128b37fb035be13eadc9dba857bc3322fbf3bc144d116f6ff7eac00a6cb34918f5f6f91aa163f81d976377f8545d70c3c714d137e352ed43c14f709488d1178e86c4fec07f16bcfd220b4a88386633d790c2ce2627fd417b577c00338d0e0f3733c7df3b664e6d8d74846a1dbda062167ea7cf437a9ad0a9e43b363d80759dcfec98e6e79638cd812e83679de89cd92cfb0f30045e6f101f19e2627d0ac8e1d517f3a70e3c773ff21cf9dbf688a36fa4c13fa2db74dff4951e80128147318bc86d2cdaadf1c2041f55f7ca59fcb1118c99c260a8bd2697a5daab21a4c4620841f871fe9e8e57dbd3828b0fdbcc0daeff323439b2aed6876ac168e7a706173f210ef82d9347027e5e9614ce078e8ac128eaae333443f46a4b5fcb10f087cf01c4f30d8bf5c4e648a678b4aef8cd1394793f604b9ea224f4dd60595553ee84375a993a1334bb8efab6c0bed6ff06629c525acaeedfab5e6a6e4abf56cbab5227a0ee994c34707e170e29a8e68f31388c353c2e28db98c3389559cbc2b488819c0db97e44558ea2e6dc7fea2cb5f86e8cbdf76bd7282eac0ac5a55435a0e28308181d6f48ca1c42a62536a3c4661c229755075aedc8d13fd43419f7a25a051d6959fa31e0bf174006cc3ce3da7e0b05ab6d8754816bb2e4be507530f022b0c50ab90f9edc53dab1ecde21916c9b756c5cdaecd98ea16a6d0ce9cfb520f20176a9e5a256dc8ee858276ec20bbf0edc27b7a27b92dff965e9343e72afa09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"24728a4fee64170a2175c3984d5b0d147edf0026f2f9d26c64e5617840461028","proof":"9c8eff416e2e6e47a92c1f8cbae02248372bc0c037753a3536198bda8876aa25fe989346e3d50308bdda92aae2e9d9b82b0ceaa077219f6028cd8cce9a0d8046c43bcb0834dbb9c5d556df93d9290777fd3d05f30103b739e07954e0df931549c6cf539ac9e28bd2a4a3ba1b31bf752bc335787b99a6312799abcfd6f2f41f0393f2984ad3f697f5e808ae20d5447de29c81f86af3033b34812547b0dd7f57008bdcd72acc7be80775921c056e111bdd3ede62d17323cf0b3217699fa610b20e5520657fb7992424918b721c71ebdbc3386925bdf215ca82d62240e3bc59b20be0a329aa7a4722d0af0c4101dc133a135bddd9a57502059a43e313d22955bc4192afcaf2047cbd7d57a7fd35e66a7893ca14bf114b9ddcd29dde6295e336c46bf8f6b06b83e592128449f65a8c65aa88d7dfa4dc7bfbae8d199ab0f34a43543a5059d2bbc3d533aea3e364fd2386d4732e51fb82a8478fffc594e8aea2674c32f686a56bf27802cff8effaff705ecbbcad3abe75f9644b7418f245b14cddc863c84fcb210c7ee7dcd3c70e66e8e34518015548cb19d5f7aabdb551d8978677549031e4946104b39aad1f219eeecf22559b872faa94bdab53d6006d32ece50d0ad614abda07890e1d960898e673b4b04c5e4246efc1efe2a05af7f15fe0dc9350e87f881e6f8f1eb23ad0e83721338a73814b513c50716a9d10ecb9d83276f90b200e3d2b8069474f239b332e580809b0f69883224210fcde17dce60814367f01c455b3bdb9a6b3c9d8b41c7883d15edc9a3e029e6bba2b5ce132efecca0c841a98c4d167a16aec2d873a584d4d68a2bd45aa80dc5c72cb27bf5c679f197c0417c81830077f1d2824d6eeed61980130f9e6bf2d8e9537335bea88407f6b5dc3083b9d33299339b0b1be7ee366fe645c35932bf8a89967fa35114360512b40bd06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b6734c4e708d71e27f0e85df4b506f2b738083096e46769f27e4a212c6cd1236","proof":"d8bd40afcdaffa1190c1ea2f19b80787bde10fd5e8c31a42fdb30ed5910b670b905c09e1c5008dcc5510d37f0b53bc90c28079a12c9ccff62798d1709cf9361818ed1e82d28be5748249754b661695a1df083428d25438cf7b310e0f363c82076295570b5c7212f844b2e22f1589962a963984181dfd3aceed96c6256dfc6d593385dafa416094f0db4d9fef56fa0d79aea11bf174f9d544f90a780128586c0495eb577ae8361136e6faa6dbb961cf99373db96dc7e321b62b601d1b1f66ca00b508dd1c3ca09bffa4c0f4dac6e39ac2b360f73961ca3ad2197ea944647579084248308744684e0cca890cf9dc9d07a2a20d41668a3f2c2a9c3bd654ae5f8b60126192c40a6a614760b2d7a6512ef449a970f1b68dbbb49cb09065d9ec27691c569989369ca3f62eca3a901b4edec42f5887f8d235310a036fd3c0bc1b5527649ca8977e69757473e0ff96003b9f8dda8988db76d1af5d36ed9351248cc0a61c9a9aa43e2cbfa399f1f3d4a6ce82c4a6df3efeea5f1bfb92ea524e5392a24433b67972e099d3115ca50dd802bb311bd330b41557c718a048a46aa6e766408548bceedee83a481830a6e6f8c18cb4b60177b52246055342ad519b0b231d87ba63d66a76e71b29a961c16f7a7fa9d9d340066a254b8c71bc7b08154fe7ef52bb6928dd8e0227d02a5ca98459b28a4b9fbc94910acb817cc7cb88affb037825f920a4ec20d6c8f7b7806bbb8ff4b69985b9e02f5d6a8a7a62e2b1083990efd8e714b0ad777eb2107db58164fd411fff8f1aa36a6f40daaf76728cd9d140e0d9591282391285bc96bb340886c5fd86f5bbf3e5e85682b8149104261aabf5b8d92c7848b86c1dec2632a9cce86be6d169805770920dae186fe1a2c6d35168c61e9b012bbd6a9710029f0f2cfd7e3c68921f73c1db3772e42b3a1e0a18f80d27529b06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9e6b779d169d9d0356b614df86a8f720b59e8c6c7ea45e98c6820da0f712535b","proof":"7cb9b19b453cc29fc601224ee9fb67c440ae7225e3254ac8ac359543fa4e6e0f5ab0744947f9dfe1e53c52d2ab8587d7d4f66c29c93c5768807f32101805584e48ba8496e5bffde981da36a2079b46c7b5584514e1ff5d6f2e2d4ad2a089ce2e843e579e71fa87457141a3619dcde2dcc03d59c827410163260e549512e261204d3107756dee8074b236bce256c30b98086b078a55d333460f0dd2312795ba0d826c2592113d0f18f6f812a71d54df537d488749f395a88ee04278505416d80112471f65feb5b19f92b842dde14cd876c1ba660f3f3c4e7c5eeb41b1b2ed47088e3c5ff1ae2cf40b166c6807673ab426c1a014c3037e31a37a35177fd9b8df6d289774b02e066f54e1c214111f0e4f6ffe49055b1c3ddbdb04f35005012c9a3deebec1fd8228316b577fb760157f67f82329db8d005f5103ddd029a24f7ff12178dbc4acb8d67ffb3e02ae41f6a19e4866165c91bd634c254055cddee9fee3536e4685988ed8ea48381c482a7e3a99fda270573a7ec5bdd9fd9b9343117f2e31829e48023d56a37db4b2963319554bc14e4567e0132a6b34dc538189b1163d5f360b1732b88ee8e692a15b1adf5598bde30ceb23c4083fb45c3712e7f4a4e619425345ea02427f9aadce9fae6764c3a67ed5387c58ab837aa34217104867ce4bead7d68f62e0f3d6aca34fb01ae0eb29332a09aaf37d1d955036a7aefae06a43d68733adc58971dcdcfdd49298685f15bdbc4da8ce8130fef49a86aa4059f83e0afded8590f8de10a273879dde8a61fa832aa6617373507cea79ecbfe08c28539a2ccdf55421bf72173e44af29032f99c91552f54686ec638793324cc955717e8959b7fed4d3f3f73affbc4a79c6e1ccbfa38274818ef715eb946cd08191dd01fe13eb897734b4245028ae22bb0090e10af29cdf68043c83dd2e64b8ba1d8905"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d6ac53808682b07008b7a5f839256308edb1edda7b5e7bd844c615954d82dd48","proof":"f4179fd4d3e9738f950558402eef998bb0c61b6b38272e9e32053dae1a30413ae87037dee92ab2411148ac464d6295c2991009a283bda7f2f2cbbe23619cc911c6fb807efd4a05eab746c0f21522e32f7516afb92d9c46775aaf4fc322c30f4b0cc5974caef9bdf16eac5dcc4d0906ff9a669eed5eff53fd9fa1256e35c68566535caa70013cdf87ba19d9dca31a00a23dbab7d8efb0af3839eb130660866409ab59317078d168a432607d2b6daeb1a8c0d4be18d2f197974b83012aeed84f08c8ba9167571880dc2426ad209e97d6b55e15b4e1157996b1421fcffdd0424c0baa965c7fd53f48972097da913618615b3aa9d1dbaa130a76e28c35fc94e66b46da7af7cedaa87b92e3d63d1a2ea37e35e69c2839253812ab4b6f1ad3d1e19b30369fe1d5c16d35dc969308d72398ad4bf33b7a9a6b16031c1a3973fff67043635ee7e67dae3c54ba3b00ced4bf7737ce105e804530ac0ff70b45417681366f02e62eddb7adaf5705a808f76e7efdc71c7443e5c311bf446dd67b81ccba11066afa050d1a6e3c911a97aa5543319c7e3a969735f6ae7853a54b49f4e05128232ffe648ef81b78d5be7c1f2184321a93c214afd3f453f80cc378b4cc6a56f8774054c68223afb00e0fadf7f0404b094dff579d6dc7a22a24767b34ca570dbb6c2f16374f7187a29387ebbea9f3207fb013319ffb84d11fdedaf3da7cf53503643a3c915a333dc9e35c184e524497f73f2b4121f6e5eb926e48e5364795ce99d373b0251ad96eae30040f68c1be4feb1c0e74f17cad3a29a8d1bd1e12b7f28d601b0a40c0adb6bb16df1912cbcd09d752e09b68035cd30b13c2e89083b6ffabc02a06511fe0990b4a840597570d89aa957fa214ff0c94fce012f338ade8fda61606f773759c40c76af0cd3da59ed9e16e42a7155f058779a217d468904af00e9b0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"32bf091406df94af4b8d422056569c1ab85b496cc5b08423f8886ceeb6aeaf67","proof":"36d49953f8a8b8fb5cfba52d10a81e95d911531e135728600e06044e6fe6d771ae120be5fdc3e09e8ae4bf310a552d73587cd51abba83265098a50dc3732b46f9a6b6eca5fe27338d023fb4f761a91a4b4b534f1b2aa14096c649622a8bea42f6a04b55ab5d8e7c7711a55e893fdaf975b451b1d7f99692d8b388cd05fdde86933e5d63352b26f1e06ba077a0844a9e307a897ccfb41c70d9b3d4a768185bf0ff5d0726f606b203ca35fb4ffd001703763663b2b2d9e2346dd3e72641a78df0ad1ca05e4042f1ed726c0603529f4723de2022f8a677240196355208e20a15f0772d0dbaf9cefd9aa70ff1b14bd44d85b5836c77720b71c75da80b8bd143ac615c0915becc3e96180f3947d94312e1f7f805d71f7a8fcaff675f9acf41ca2fc0458dabc87f3b20362fb5a2badb29f7db16f52ac800002db1a40d6a1d1df6e7c469e1ab425a97497800be57f1f39c5faa6906161900a6c532d3793932f4cbb5270e21138536c80c0376e45d26ff994835325bdccae5b3ae63569de03b5ddd22446662641769940684ecf75c10eeb495cee8277c33f11c7206b844d7d53b15f88160c7ffa64db247aed3a9fc28719ee71bbed4eb8ad89aa933c5aa47e8cd0a0632b807c3e689ff327a4ae00f8d2329c0445c1f8a37499c5af3609b282ffe1365c7e56d5ba091b77a245e9024c0dd99086d404099b49fadefc4d7a609ff254d540596cb8143a3d1637f168f448a9b7082e3a62adf061caac6d7842e2f05f5d0b6673e88812841fb96806087d239ab83a988c8f89283aa912057673aaf7cbc128b52e30d3f39a1cb631aade19c82585177ddcac4c4cf48ec729d482460850835bf57347d3af8715ffe366a2b1263ef491488dcd48756dd2dfd25016e9c66f9516530d2d149f94f5f781b143164c1fd2bfb6385f060ebc7e0c1aac0a3ce38cd4ff6608"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ba352ed1f3354e4fffbc5f6bc10ae305f1e2f6b934fbf927db13a2509206067a","proof":"48d9536e6484bb319282646f9b752a0e5a430a231881fea81fa089df161feb6eceaa07977bb59d0fe53693178dc34a6bc5f5b33dd5ec9fc53784e8c524518b4d2c87e0df94d0eda4678e5598e1dddaded8dfdd2e980bcd17ebb92706389882678a98f59168e1bd83a8cbfa9991fda1d7132173bfc085282d31e5bfbabc87d16d2949bfae1e9edfadfe0ad4e960a64a9025a2ff4e12c8df9373501b1d6075ba07f67206ef3998c19c7cbd62fbbd5f2346332e0e6e0995d7354a9e6f18918e58015826b454f69bfcc3e3ed58070e4fa6c8df3ed5982c1a44bee4122c394b01f9004e330ddd5380abd002fe88432b2d629b9000ca02dbef91e67d33e46b2f86235396aa3c8152b7791df8733f446e4bc4902e38ddcd514d15f412b36d1ee91bbd6b969d910cb2210747c2b92646f373345945980cddf57b149fa5b85bdebf30fd1fa6dbe2e5435129b6ee9a7f5c8f03c115079038f7c5289766379e60c5c3043c7d383b87128955ae5f5e986d5578444d0ab8c9feb64a2ef125ff8b09245fa5e80e2c7140d8d6573b6eb59f70f7e728954b3b68f1d81bf42326241c3a20132cdb69be35390fbb0ee8bb8b88d2c6c737eade930fd730700da6db944066224ea1ed4d2eb39390b5e3569d33e9b665ea5a467377ed8250d530aa5da1fb6b2d84d4d7059e8f73695a46afed20bc6cc61b68ccccdee581b900c27ddfb69621d6a1927b17682df8fb554149e291c71f0d97d335bd6bee8b458a5566723bc2627c995225671689da799adef94c076020462a9a3536d960b4dfd92d5c09cf16a1dff78d7f22aa4aaa0c772de29706d062b5d3148765602eb2d4e87e99754c6c0f4be76de173b99e5da298e16d3e2b87d0300b050fc0d3f1b52afb5ecdd5d4ffd6c4810f960334c8036ba9623bdf0e94b329fe28a1fea0bd8f080c6561e268d2e358f458ea02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"143c20abbde0daf98da018d44d2a1a875ff66ebe1eb15b4061271ae779dfe242","proof":"62bb0c0706b7c314e44d8ad5a38e1b127c380beac503dcf0c084d58a1c0417033051c60a7817e4191487043edd84253378eaabddef796660f0d58bb045c50e76fc7ba2452d156f8a35e8c078ae0bfbb3cc2db20eea2bb423fc65d4f649af425b3abefd918e38bc7b9ddd3eb34d70920fa246a9b19b0fa35871db73f6272b4a411c25f6067ddc6640a1b430ac8838d7432f7b7a5a42b8384a49da1a8656730806878ff9b75e2a9105deccf30b18fc8b5cd037667bc2371fa5512686ebeeacb109208869076fe893599f30a697e68aa33b6b5e09188ade62be9525958672404802e2ccb62d12192c4ac2e76ebf039bdf4b6f926948449287f84d94576ba0147235749c06b8b35eee09895ed574de1ccb549862612aebec69fe64db5aaffb7f936dc4ed6eef6874752947b3ad0f43957f95d56b5455ff9f8aedd102df4efdaa905dc4c451b34c00416a7a67616c7c9ed3d727d4297f7f2a215f42187000b236740c10d9778f3995951fdbf3fe967cfc83af8367727bd90e8ea211af8764644541101afc1d65a4413abec1e56f40a3c8d566e93cdc4157466f2f998283b3a7a6305e527f16dd3c210d6dad7133abf51fdff60e298b36a843fc2676cb3e16bceb581d9e5bcf7bf03456716ec59caa72b3c9b400bd6cdaac8517d603a7feebc41fd710d887c28234d772276203f033b3bd0408d90bed8c69230a73170cf190f284c228e011ab12c4474c955f32dae21dd8c74662f2f7b294da4cafb8fffc084b718b72768b0101e7887e725163f02fd4bc211e84cc7838b90fa6edc1da5c021d19df1e280057cbcafaca9d6edcb122217906fdcd4f85a629349005c2ab0b148b5acf2a0b56c80fa4b40863fab8de8da5b3a2e1ed53295ee08e995eda907154d650bc0199fbd28fe2d870b6ba11b6bea4302f904c600af95907f5747749a8ce42d7cb0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"142963fb884a3db47cffbb03f2c718c720fc7fea81ebcc63915ca068f0d5746a","proof":"12b8933248a8c1b805ee22951aff5564cfd278dd5c3516da9c388689a2cda56aecb96cae2e2b7d59b0c1628c5e536ed9f16b6c4f41b9d75168ebc218c0992908c03a6fc2fb384943eb3b03ada57852acfbcb75e67285f4b633f9fdca9ac6cb7964aeaee358f401d91b3f0ebff9df83040b37d1b7378b1cbb398cdf0d3f45336df87ed07e956dac6ab3575d9e57d10d5084894784023b0f767baa555b2b5f2a0e70c8ec9b7082c04f064b018e2e035b31ca6b43956a5fc8e74248b7914efd7a002c97b397e9ae15d3f00053e174f4d24c2efe65a598d4ed9a4caab21b038ac104ee31cb48475fa1b95276f4fde1be6acc9264ea8b90f3d6057aa41837be892a14f83d67e8daa3c65c3a30e4d71b0ade25b305bd196fc90023ed6093c0011fc7228c36f398aaa860fa3691272d5f396af87f4e101eb390f98d5cf1853e61464e69f633f97fef342cd963b1c60677f762462270a6f99578c2d5137b5a53a1a0a71a56b7c7007b31c4147c1e470997205585460843c4fc8282cc97fee6aa72d9c465186405a2168648a5bcf89d4ea2a0bf00f1ab20d4e82a98ab9770a51c86692905e236200d6e65987628454709479f82b16acd13ca99d3586d55230898f0f91624f87c4b780de7398d18af24ac4beebc3033089dfc53c58ca0df657516df559a5e5c5910375612098400bf3f4f462a1b6c6c70b368515c04e8f7a208d00468a432902c60a2c32a0ba1720f7f3ccc565f23c18a41883cb797792bbe3d016af01e1daa476d77dbd3f43e5f07864ccf559cc25b5e4f6fdae3fabe67c0fc7a61e79d1c623523a95c0d10308c5c3d85d9fa491bbb4f9cbc45a08140e3470d7d85f64a4104c84be88cad19d4354888230c4a829490639a4cbbd882a193d855d717bd5e0be39ead957b3f6ed69d1d78cafc9a33595254cd54427cc86323d1e3cb7b5f9f03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"92728a71a471d58460a68d9d03eb742424836045d8ad05ab1dff88d25cfe6967","proof":"324d68c288b95a01f6d1d23137c98b357d744f61359155f86d01a1c9d1805d0f009373f09f75ca4e5210991af5976b24262a0c1649c6a48a8fc3d3188a99b86b0c99efd6bfc534c6dc832b1863ddd93f6598f6ebac1ff028a63c74baaf613b7ed829ee24409219d534bc664dd3a77e4d4b47c920d54163ccd0aeb0822d9171413d068b16acd15349b217c8b95ac825be17b1e841972128deee4e1e428732b00b3f16788fb93248c2b76341291d549a41dc2572ff356934641a927e2e2ab28504bc46a1f91c7682c816df345d446b820af52a810d43de6d396fdc961ead55ab00d65b5fb247d02a5ec636b63132e2ad5b3038497904cf4caf2c9b75f2c09c8d09046529e0fbbc5f51369f0ba7fdca9eb906a7aacfbe964e2051cfa67fcb715e1fc228057bd803268d19196222965f19f3c840da3daf593cbc0c49cfa48caaa455f85f21ef5368419d22eaf7b63d8c59d98b788b032aa502412b5a214a788d53062ab952d6de448f128db57c75836c47ee8ef6ddea9b753d7d4ec824b53ccbee33c255297b3c6e10e8c5c9952517ebf5f268fc136d4c3e6dd8421ef1e16700e22b32a62042d26dcb931f6d9821008c286f7f15bd50c903e889fc0f3a7603e93e78c0caef72424ca612a76e53fcfbc372255f0036ec96d8587d3d2c1ce3c509f933e84cccd15d4f20d1aeb614324ee3fcdf148dd81715b7df14de1c199a7fe1856184cd27f3f46a4d491431193ea87f5986154d7a94812f1bbe6be2b2654d479f4972fe8c6efb14c4b48c020d5a3f2675dd5e179c1c0ed6528a635e42e3819c9d2c188ac3f0dc27bb729340b55cf82350d380805a418724ffcc2294760832ad304221132257ff94f1592f3ddef3bc8bf449f133bf2b19bf6de5b3bf6ba870796d0f052c5a5b8fc180cd24dba0253ea82843db13c4a9478667c88c165d72ce43f103"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"36902048ea32d1ad67f1b9b12688cc9c6dc80c7905f5ea7009e6338467c9f44b","proof":"6cb4d0518b2fcd58b0e48a42564b0b398a7ed803d73bc960c16e37cdc3223a2fb89b416bc763a43120e85e2644a068fe8b034d471e2bcaf521282c2b918f2f18d6f9e7e799993231987a982478c27f11990f6e06bdda89340aca8c425573937caaa3f32b50668d7554fc6f4e6e7c6aaf5b6145ec143161aaaa53d2d57862a35d837a9a3fc6468b6a7724f5f39952ac47b3ea2a2cc7715be2b5ad7af345d2dc0efb43e51bc4f0e445f650486e041dc5b492f8f7138c1a9ca4951521bdb120d002c71e2e18847b326530fca48fed12bd72e5c9231c1d1bf52455a9010037a0cf05e2f9146b692af9b399e73807f60f2021718d5ad4ddb42ef0a2b87628dcbca76b4eaf1c61718ec2430820894a2775fc30be5a5ba90c4b65cf1348f511fdd4074b68ef443a571629648a6924b22f4c355df531abf108d2a431cd6170e30c2fb35f6a5736c1c8525c99ec54eb5eb2290528e2a44e09090e74dfb0ab1c5f0642f52df63c340341421bcb8133c4fa20a98de621f726e3614f077762297576c9b5e51e2cf218d5295cf6246572b0ba6a4da42a84031892b5e72b7f8a4dad538e1a1b33b093223acca692c50c15308995d234abb50812ed9d0aa51eae01fc28c3129e6ff4c3cc571e03a1ae793f9d7c5de9962c17c53845e08307577654e15b0e3664737ee64b382332f6869878e1e9a829869d6ae0123cbd1b7f1f0fc25f855a3a4b073e4868fae915169550712dd3b842e3fe71d4b2a8485b8d897dac55d62446b759185be540c65af5d54e776a7626b0eadd112687f335e971ffbe182426b48c9d170ea0bb5effaf1a6d1a002a1e9f96d0a25d8b7e68768da558ba85c3031efd6e7dea1ee0be65eab490e5dab3b1789a04ca61394ee7c0ea9f047eb526f1c71bf80f0f3256cd6949fbceb2afc82ae9681f6ce59e4693500470db760ac39de33ebd0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8ecbe8d3006811e52393f8439f99a93a40b66c2cd8d25e5fe3382b82aa17506d","proof":"98c52a41538955f4336a8402a62ed58b95a6637a1c206e8419fdfe9cbcd0a1502c8571fde4c579850df8e8aa8c60c00490af60ded31b7cf53c90b2244d42fc16f6195de6be8bcd75e0a724215fd2e97978b6586e6b393d75f68b3063ec3f6020701171a43cb55b3b0c6532b67d23c47975337618dbbc2883fadb7bb6b35f0f0ddcb659eb48327f37612b079a0d7a44c13e8fcaf4e4b36e4bd9562e5fcb9bd00b75bcf6a1d93745980006120312aa7e7258998d63d03b4b7c4ec23bc8be5dc20619464a13138a733c476607696a00621a34d9a971534ae81dabd78bbcd1813908840b6a5a81fe27055e5c35c9553978e6805716d6e912ff5cd52961ae659d5c78f8cfd29dbef9a1ed9912563af7a2cec5409efff8859088e4b036d3af59beae3104ffce73df18c998bd376b1fb66ef6d2087f681097fdddb3c3d7aab47d2a03626e2342314117f9b29183ac3482ef718988f9607542fcf30fddb3526cf2772f684c16322c3f3ed683e0d8c4f2511b944edafcfbf10230fc5b8540248d6e283412a423df9cc12051db8d3ef5dff2c6ac6346d36a0ef87b9c8ed5504e0bdb92646b2c9c676abef8a1ac8cb70022be491fc228cf9fcd75b576b63a092f736569890904993bab177a75320f021b9f4bf725cd6dc4a0ba35a28f634bff8b3483ee89117a3e1b5e5d7a2032166bea5755b2e45e829e74fa660e32d3909b001a8a8b79513ae7d06790f5803c86513c1b5d9d944876448cdef9c4cba3197274141443435eb618129978322cdd2379a5943d968270bf867b3e6c812fafe8d4e345d2940a736a67fa498678261c56ac6fbf08a9b7e3d2b98387d4db230d9619cc5ee3b1ed71324aa80bfdbefd788b8fc506cfb24b91956d1b7c55eba7d46e18e9c3b856f3090cb473d3c014daa522453d4974ceef55f33da09436e0777b2a4ef69eafaf6804"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fe61301febae7391674072727a7c64e08a7e88a12368cd4fc289049e1e9af465","proof":"94ac69e2dc6b26bd0eb45e44a6ad99fba1520d2bf2b8f9a13d58af36ff14d80f0ac4683317ca2d0888b0109d66a0110dc93cdc79fd2ca6d2296959e42379296cf87c3137477ee98510a34a4630cbd1da3924d81e2ca4c2adf059ad0fedf0cc3cc8fd3cf510bd270c3499021de7cc84b5e73bb3ccd12844589cf4feb0c26c2f7a0d1b774a4754a3ee06caef2038acc8a2cf828afe0c364e3d13ef0a49cfd4ec055714ca466434eb1469080af56376d81ef666214378b02976ea350d0879d8a805903f572439e921bd9f1b1cf5efabc854a8c33b6e3cc4c8c7919cabeaa7809c0288fce0cd7279ab4458f43f88d7ecead3b6f089d93a3cafbde15ed16cab31f67deea9d64fc6a8027555988c486b02068d7e8dc493607d07b74567108fdc76a60cbeee6dedce46484535b1a4a9e47b5b707b1396ef06ab4821529f42b86a9cdb01fca6a54ea8d86593c12b03b84d9e95a930332443ba4495baa7620dbb79403e6a3238be8741358099b237fe24813f72960914019d2909b81a5023d6d0b7e6cc4156bd9322187b58f962265440313aae10f4ac72383cabb503745f45355b5d477fbed81062ee655f91f5506d3772b37c7a86bba60bc32331610982318a81fc724a98e99c5e5ab990d810823a199d926e8769064b1afeceb01f41e926d4865b11093400a4ce51d58d5c27fae0eda844735626ef45c74ee802d6389ec5180be3b577d6d62c55a0a6fca495d05ed3c1be86b32005e70e6b116a7f7635f8b3929480213010fcb984df407d096e0441dce047d517eff84109b452a2104ac06011a5a94b0cba8730fba84935fe6f26d613dca92dd0a38a2c5e6c75c55b67515aea3c750e3bc323cf4f4076d2d85ca9f023b8bbe77e1fe60fc183657242504be68e2d8c099c24e28804a6aecb78fcad23aea4b9e999e29d4b32b831edf57f7e83e5a5cb07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7cb43e536a5b5baf57b6154c39bbb63679cb69b52e458c0a023fb720bdc8e54e","proof":"86b1b9a269fe8c36079c44fcc9eacf2074d93cb37ed640f8e2a2a13446b967090635b603293325fc4d7a8b4b2975482070ad635527139062b31a6f505b3a400e729d02457cecca5237101a76fc6e0255628072e7cf2d8c47fb6e6b0c2f4c275ef2a9d80f2148f64212b94870e1cecae1821d5a2bef1bc7750f6d1bd76a091731d466967807f3eaa44443a6c3b048ca971187bd2aa732c36ba2eff8d481f4170673baad71035219ace5d3a0fbf21e78c2ea2ab247e6931875b216359bff11ef0f56d3d865274c6dfd14408fa1efe30acf47709e88f49b1d7224a277763fadc00636ceffbf34a9a0d13a04ddfc4d74590f72f6f2131c382241e43bda58f4b658114ea50fd8866b933e93f36867e11df43bc3b883306953ff48c5ae46fb0381ba784ed61d513149cd1fd60bc967d6b3532ae0fd70f38bb3d64279bc3826a32903714879d183dd23ef6eead1c1b560f4542af57a7c3aa3e60c904c63a4b3434fcf79f4fc896926e695067fabfd5dd8c089ac56bcf7bf158b0f0660d5aa10e25ba15d4651158d1a389abad93518bd5753f47db691d539d11dd774d2edc3d3e2740758da1e845f46d6b7eed4e9ab6939bfbe9e6d57fa00bfa4bdeb33a9b87d2f6229225420b42657c91719fb59f088b9ec1a966cb6c4201c6d2ccf40b5f452b521e07fa41dc8c5e386af580e458e2f849ad5e2f48bac4b9d81ae26a2cceb1606f4b162b053ea5f2f30cd765e6fd73788861b830454f6f5adf903b4c54b2ed231b43658e039ac5d22b612ef72704f061b35f6771574c568b238cb95c6f1e80805bf7063da21d72894559322ad68ac7af8ecb590d9a5e5b7f289e6bca9e9c5e251b79b620e7fbb77aca889dfc13b34747d209c68bdef7f0e11522fae1b1b6c9afa2aad05653fe1085fc4cbfc662934b73fc472875c7da8c3f659185db558172780a53304"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cad77fb98fdd56f449424ae1dcc8c48c617ede2a57cd69dd8849fdfef2d1a11f","proof":"16bb6b1e24c866c8086fdeb6fe9023f139dcc62d275d01e80d42e154af15e92d48464c4c2376e43dea3f175c77e88d5150e655b9bec8c9e8059a13156fe9016cb22496e14d9e9083240456c50fa3b1ecd95e1f8422c2b0a325a35775b1e33e1a5a27dc71af94d26c76d2e2277a252c52488fb7e4f3b9cd51b371fd2ddba4e2746cdd2c0f904bd42819e95fbdbfa9cf9afb2b114f08b2aecc0193ba81dec9a00ec944bfd23899d961fd0ddca2a089ef28cfe8713aafc96ec05a5ef1ab7838d208a756f8fe844d6e15c02baa126b943eda07e901f03e807ca5c30cea4eba2b4e0c4edf90615e01774f06aa495b9988083ebc3c8f547ac6302c8aadf4aca0349038e88092485756185207f1c7caac938780016e8a15662b39ec0a8fbf46f9b8d671c206eb2d3ea99f9806a18799ff13825c7c20d649bcdcca106565afd55dcf076418734e3760f04155b056eaed7211f390fa25be30d0b91127dc77a16e7adf89555093b3cba27ea05f5217e78627a5f4d7024c526f6a7a56be47ca6184d443a254a2c85155acc15e79270456994aacd8894d0faabf94b769f3166e90c093016918564e79c81c1ebc1057b314883b1b5f46aad2f0b5f094144a945be081aae83b6ace4746d5d60cecf3f4664a3dea9d96659cb2c6873254115b365ba9e2e653fb162a8a3618321ff77861ab1dd054ac2326290f1cd3964d379a34c37b620d28286882574240cca80c18ad2a1a74a0c0a5f8caf852e58d118856bb1b9d8fb19de60646c36c7b16a5416cb4cc1921ab75583d67b90763b4585981ccb156f52eee242cfcc6491e09db6ac5a3148df4dd782d95bbaa27554b9410281a282232f1581f5906dbdae9a36bb71cb88a6ae149e71038a04920b0cad1b70eeed8c529b825420fb5042be41dc22a479ed56aa562cf7d9b0dcf85167309be53519d72239f07cd01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1c5f932d1bfd29431e88f91f8aef906a45f481ec53e45fe2fa35d63d2a37542d","proof":"3a506e8374e352eaa5a1bf074154164ce9796bc0c8482a9d74f65ba0a51a965552eb2ccccbb51ea756847179a70df16693cd3b1451a16532cf162322464da713562ae815e3d8df430c828cd1a3523e198b98fab4101148dd431ec6738b436020a0839b434dd78fe1aeac16f433f889836e45b8b7dfdaf81909e695838a2e881834fcb21d32d2c749f1e031ee165b45c09d5b6cb54315da4fcd1991e4a2658803e919c7367faff105fa16e4f083cfa0e4673f3d11155c00fed3706ee0ad6ed60adc263cb32d379c12d62f9436ff3e96b27fe316db39be71bad9070875bc2931044836da8d472f642e06e2221cb9e164fcf31e8302077f8e59544fd9024adcfa4c68e8b419254c8295e5f8b7a87ae7c28eaa9bb3084512e90fb67ac1ae36848620d6b58763d8e0ac89d457d6e01e1be63d295ddb6381687e76706b31372ac4625c5a113c79b30a28daca58d9692f873c53a7a042777305dffbcffe579581feb578dc06dd92d3fdf0810108e4574a870fdf7e3a39264ec6e60b3cb930a763787f04025c79f7a06dafaf0c9dcad69c7fbc347165f0a2cff7519d4ba5591ee573445b2c40a68b471e214f43fe421665a12ad46e461b255d4a86482f845fb514d1dd5470ac3762cc449e43608079e06fc1fc20c8cfa3488b493705b52507b29530187eae184876c60f57a1aecde1d01cb53b390daa75e5682c2d1654eefa5b77c87e20441f5cb5fccbd5ff7f2707027138d74fee5bb9327a0445fcb64b1f6b0be4497ecc7fc7de4cd96876a0b836ee80c6cbded8e31a08ecad4c83d5908b9a208ca53502e386a5ddc018cbdd983fd6480d353c3a0772fecf736a5bdd680a8c5f1fc12c88cef558271bafdd3128bded03d6adfe98697ffbae64a9cc364a8798da4b24009661bf4b1ac3287cb8d12e4c7789aa331b2c32624d4290e0c9845ffb3e9c1e02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"107501024f71b37dab6332ac516f6b03debb45164e3417718f71b4d055373328","proof":"98a3561a97cafd75fd18170ea2f5bd8f1125f7adb4b128cf78e30f013a64f678c82ee9d374ba2a1a5d600b3e5556352bb3d27fe4e4039f8bca4fa52b219b463b943775d72d9c29e26af2c33bd50097f7cee093941b9d0cad74487eb58c4d8d0fcae6e1d3ec73b957002ea1f9c7f13d8af9ef9547a99ed2a18bbfb20dfa5bf4216546002654765c2c56c2398c7d7eaf0f08536e2b8b51b497830b0e2faf621d0d81535e291eb396768c43685c889b8390a35e6759d417ed7b5ac3f897a7c18b0d13a8504d3536c9fb9e89f6146c833dbbafeb11963b953369ecba1bba5cc4b901c09fb5b14ed3a3ae74e7d32aa92d2947c8985e01af0853d7b5dd0bfcd5cd2a0b609f5e9ce94da4946a371865831507f1e9e3c692797a8e39ba0f7f6e8047004000d21684cdf71768b3c917bbbc340b6026bb9ba0325fe2b1caaac83069b9f114080afd5e4a53da78ad29cc603bcce0978b8f8848a3733b41d9a3e4d0ea7e650be267e76ea44477f359e4727c8ee9c685c9247e1af600d8476a42b83e560ec92ee2870eee62a1a1faf625fd191b665f2ace4a9856bfed92386883df0b89a26e597a5bf52cb29b810af374f7ea330acdaeadd1962ff07288b791deb265a043b70d3c34cf6fb38bbe60cd64020c4f3d350790d14c6bfe5d9ee14ac6d11f572e9860568cf97f824bf746a779406ead7503ff5a37cbfa981f5038f19b9b4730bb8338548b78dfa0928dccc2a29d4f73275595584114bfe9e398892531ce73aa3b35547aaf2d0f5655a527340bf3244218b88651b999eccc154f6225f9eab72f9b045062f78a4a67f832c50ff4c93f7250a67fe9e65486ab116dc1f418863dfd19787e89ccdfdbe3e8160e769321063a5b4cbb5f086462bc268c32a70031f6ea0c560fc58c12130ee64d93e618a64ce4a78c47d3537df442efa559e4b727d7b3638a0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2c3db353f19b59c99328a113272517382a595be0d89563de966a1883d157577e","proof":"040c8ee1fb02af8430b76a23f4d6b16f22e98055e7dc176eca76bbf8e173c65f6cf91305c7544992006fe83125025d732021ea3fac71192a19a915de27aa65246cbea6b04566deeb47b4bb976f3eb78490b79aa67861a92efe770dd4134c012dbe518d1af3fbb9947a45f021938b4bcc013081e7726f749342ccbf31f55a6d4d55d5dcbd4cb6fba045f145a8377d1b7eefe853e2d8877ed8a693591a99a5460193a33dc90042eb1e524d80ba7c5da0e44401dae4bffd6cc240a4eb7bd8059b01b22f9010a1a267fad999feb611684b9ec1d61a0e04f4ad9807d969b2aef4220c9a97e99a0f3bf39f75405e219c01afdf35732e850c733454b8df7ee22e725a2272145e472c489f7b0a8a198cadbae890646fbb534c6522b49e2a3f33fdcbc877f45c06a7e6a34c62de5cbb606e48de6519fae6de01b856877e69c48e817e8f0702bee9c2493c50a9c36b0ae138a5a8add4c63b7f6d32f5e3c46987c81404ff6e406d79966ee491ff7f5ec342f02186aa74cd3ddfa3bdde3ee892c805ac217e193c2f3433d4741846fb16247e3625082c1e554a9c7b5fa2c426b7e4569792b0556810eb95892210549756311309a3ab8056f5a2a3552ca5f676dfbe3b420ab2708214ba9754658e614eb6af04c877c208ae82f1f2d960ba69d95cc88f050fc97c00b4a7dc97b766282265a7ae9a2456ba4f44a9c36b45d740d8472b8a7b061b5212cdc1d34e3d7b672572be075c2e9405d88532c047d00551288630d19547942c88efd2b7e69e437cab74ee322ed8d5bb55f2e9fe4adc167adbf4a0ebb32eb64e4c878d141c8d82952eccc4e987f78eb50c94450a02c00a6d63916c946d73e05da7d10f1d22540c5fb7a44960826d73d21db61415901777b5f15186c0b21542002bea989ed6e4725cf4a1106913890444fc7aa0c77b70fdd9e78fa55dce8dc109"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"962cb2e4609c99d03299df4baf9086981d6b2fca13d2a1819602dc464f403941","proof":"c2dee33c9266027f295daef3de450b6544cfc34f056c4f45b9f6b6b0200ad12af4ca8486442244af614c75a3f0e4c5aa9ce010312cde26dcc715b52750cd6c7d82208abc084b72a6e086961c0d17bb9744b1620f05de3920dcfdb58633bcb20d38d078891713533ecbd175bb447e3144458726b77e9ea7d45effbe2581e09d7a324e1cb763272c4e58cf2c3e48380b4c25582aef0ff6c37fbe3b601657bba605d5467c4cc34920c88020ab54e19ea2038d72dc5c51b5662bdfffe9a7a636bc0ba86c61d2bb67eaffc4023186630f1de5af0ae0b5166dbb3d5702a50c1553d60de49c09354e1d63c883416c2955ec9fff9a642bfb93049e4f7dc2f57be27de424e6f525dcece7a212f4fe43d6a670050fa595c0f50d41f1b6a618ff622229347e68634c01b2c7796b64b661b190e702c9167f6c610103753b38e8f753653e4a208abb4c74e2cb2385aae4d549ca909e1b5f199c5d7c05de1365ece02198f9e428f6bfcb6d21e0331dac9fb2fe3ee457a6f3af9a9036229e47fbe95929740dee43e20723cea2569477625e001197abe78e2fa9442b58d28d9f9a864f788a71d43cac2d4ed65d8db603d95b07d3ba734de21dfae594bbb1234e16c55da51ea18646be11abf0a101ce72f16628f1c4742f41071292cab45501fa43617f0e5908ce3420b2235fffbe50aab8e4024e5fe3170ad1376214d3a71e583752df5234b50d161e9c14994d64046d1b32c56826471c6bf25772be9a24eaed123ec7d045fbaa5404c09a7a8b6a2c90938094bbc88ab9afa127a881b3d6e1c258d0d9d7b8c3b9057c7b63780c156ef77f4a704c15165db3106b2534ab7f594545ac6ff292edab732cdb22c4b4aa39c3646ba9df3c52b5eae45b43d17ea14e29078af69eccb82a0d92e4a4be0e752c58f2ddc67186b071c78e6f34aa4910f7334fced15ec32fad0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0e0aa4c689872228740142c2ac54269bfe47623b274de87562efbb7afa417540","proof":"b0d8b46c79ba630da71898d4954a8f2ddc11ff3b25eb001a77dcccab08467b5e184a78d972920570647f593d545be6712399e161fd31c5e4a9d4426a90a71c2af06b36261258b5061d2b360e40429e71e83c6dd9bcf82d3348b0001c1b0bf0378e7f4a802a7a78824e4248532e5b7fa2455ea50be5b10ca908fa4e0882ab0c30c892cfd0c4336123794bd3816a26afc7f982378ef1828bc58fc9fb574ba3c10e85720bf3deccc71949cca5a2dfac520d54102b2ff6d9490dc7525b4c0b5730071ea1f2a4e86629d729a63e498f09466342375dfed07778fe32bc776e76395c0122c1cd1e9e926fa0f1a577692c0d0a3cd5668e4479ee76a109fe83591c1de936f6c0059ac376338d11c1324dcdc866afa380f20b7d4ee08498d841f15ba5101eae5b030ae0bee3f387ab2d540d51a4cb90dbe211bd89540ad1b61c2bdce9475f2a018a95f758a781e8d2281eebdacc9b4db33e0d2520fc08d87efcd006d1f130a01765fc0343c82e8f8fda29835aa6d9a4dfe4a97975fe6fdee480f6ef7cdf059a53452d913f9fe478a6dd4212890661783b173ef3b87a148cd152a8f40b4831aa00e32eb92ddc50ddf59f35b3127f07f60a624815e96006222b1cc8334f8a5142163306b0d6234399bf5cc2fda442c30a8fb6e2146f8ad6ed0aa467644a9b6eac83bf9811bd4025447e2ca3073d7363062b06d3cad745b9f13ae60bdf5afd2710f3c4c6e3042f6fdeac9f2ab7fd6677d9467e90b2f222c3a45afab1c65a752e586b371ceb7541fb91613bc4439f98aacea4ae6e9ad06bb01b0e2184a7d49a3d6a8a0976faaf47303df190efbfd662190e0713cd73d31f82387d3631b0daa10cd0ee7db02a24b08b5911a004e43c170daa81164cb3c64219df8ee841a8e3aa082d18ebc7deb82976765da775b2dd4e6fe1726f8a8f2414d024cb9194c58f9c01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"02eec9c55921b125e91487b62473c4f72f9f96b6bbf6052f5309a744d7db2b11","proof":"3230ad9426145cfc2ad770e1cc04c66d669e73ffc40ebb9cb32a47b8e0a265137655e05645beb1108dbe6e9d4c2a04bc1096e1f39549c0137fc5a192b8dbaa532cd95edc95422496d95c5c2934cb08fd0a6fc92434cfdf27d5530668d9cc0815b2a97a24d92bad71683751ed9959e9be3ae141871207c6a021fb8b5afaad1c2a288290235304c942895f047f95daade96e23b428e8deb4706806bd2bb28553001c78f0c6018e9946114bba558ec3dd0bd694290bca3ea78e3b6c839726279a0add8bf35706c1422fe19192ab3ee662b7219361d6bf5b4205dcf6a9290385300bc2a14b3894a4f0460e5faa27f594dd8e29ccccefe2282a71a46df6ec16aff4102441e5391a3e93496f421f0b8801dffd0e6cb637c84b7563d67060562d29f0548a40eb8122f5a9c4ad97e1f0ccc2a8218958085a93276b6e870177b76535e855c484fc12b7eb640c444d9410aa24db08ce341443751a4b30553dfe9041b28537e8379fb6655aa877829d65ee483e61f308f8aaeb5324248997a220eccbc3484e28899275d07ffeb7cb6d9179e073cda0437543140faa88d6f49da1e2437f477b7821787d3a0a6d1dcd8e2717958c99d0d9ab5df8bfbb9c82eaa2c0272640ba5a1ae43106da6ea29c184c5652784189999582b6c1803d692425296a54880708248a4126e2a0bcc4f61ae549a0fec446e5ff4d67b4fe075c3e200e583716c697062a3dadfc258eb63ffd115d27045a18289589fd35b62f46ddb52734676ce3ab4e628680db96417ec9d990fe50c9a225cc077f7a1dc2b3dee582482d41aa7fa0679452f2c71b57dcc0210c0ac4e12b5412053f975969a827b88f9d19babb56140fab59658a5ba080da7e2bbeebc6204a308fdee34a3d45b538c231570c339a750d55b32655ca30484259c3b2fc892e06be5371b095961d6704060c30f3c7c74b07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3812c02594ceeb0246874c65c9d904657f0af58b5d56ef4133adca7e4ed1102d","proof":"f6ff9f76771403d9a3be372d7413e063e58a122a690564d4592191628ca6e47c4c1a2dd2dd3d5ce4a03ae024bf07b67523e27986e568236623e84d03199e563ad2759e0f49b9da442012e055c703bed7bd5984f1d14e4df72f5880390233a850567b3f498375e31b5d5490bcc405e5d255e19878de28d1aa07bac19c45c5aa7bacc7483a1f118056387ded9edb15ce8f376648f4a7802377293c4115940ca708fad8f3149b7e3903fc4424ae3b468472d87ba4808c695088e0b7db2ade933a09829f51f79c04dfdf5eb97187866076c9093da3bd0f23b11af3c69293b0cc030b7a504b45dee466c2f6050c648a9e5b338c82173a2e48e7ff059ab8c54ffc8409102fdd9a3ea738c2ae67449dc611c48d2ea83d4cbd69c9c03f1135d7cb356a6b84b8a0a21323d081cbb6f98b90416b2da28b5c68b91cfb68ac7e0014ad6d076df0ee74bee0020abdeb5d1063bb1cbce3e364ebe0e9c3aad6dfd33af4a5375217aebb6c78f65f03d533838a17a530c8afdd02e88aa2c9f95a98651bcdaf627452167e90fa247366a762ea422ad3b3f633fb3b13d259a621ef63fcbe2209869e7cbadf97aa3c97070fef214a4cc527c7158e71aa0eb92de543e9e4500541ef66347acfad9c6c8d5e924858073410f365ce7875db9243fa4d8104b542fd3652956c0417e1791c3ce01a8b0448a3891d0f01e6cfa489038c69a95cfdd0eb8137b54664b8f26f8407dee822751e8fe0906e8e6b47045a03d8aeffcece7ff473eb276556076f239daaf4d60a5530c7388a735b1f491691e84ac3e018fa22a7e5379f3e8096b9d8f2ce33b0cc18f90cdd38815b9bcb4818487a7aa2fc59ca25a70b4d03fbc609b5e8ff435f0600d2aa16406669c70bf50d94cc6188c3e640316fd469056f6830e0065d643dd6e14a70c451f92a2edfb871f4441b148327ac6b180e4609"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"64d78b35fc0ba8f0a3f4f5c5772168276db01e9adb728a51ebc05bca309dbc58","proof":"985aa774943ed2b082a072958859534c13e50f2fb33163fc4bde31f094e5df09708d786275db4bc9338a231a6c528fbc9f2ea2968e37956f0f34fb48d389e44ebed4331ccb14d6ed4c690dd88b8dc75a1a7700c248a55dda924c673837f32e01f06ce1d603e828918b2cb84b511d501bd6b51fe398873e59b9bc15f92fe9031938bac1053d147a8c3fa249b4b7f42bb673b0773009745d9d161398cef67a100d9a7b6e3ad1ff53a2742315c198b36369b11f4ee5bfbac68118b917724cda5a06c4f2b25717809bd71d187587482e6cc06fb30ecd0037499036aa6188dde4720e246067306cd1a03c9c9961f49c11457f2b58f2669f2c4c4904599afadebdd407e2046da996c1e96d6cd4da914e391fbe830ab6600d1a430cd30c59b419c579598023921618c99a805eabead7b29c45451dba944dfaf46887535758a506350124026ab5a65290afc92ef0de9977b33f1ac11fdadacbc3172bb78043af1960d920289f9dab4839b3bb7d567bc67d51b522878699150597ff2f13bb199b406abb7fb83eedae3b83691c5c72a16981f82366d7f679681cc1940ce49219b31f02fa1ec82d8be13e708f0ac5a0466d00eb24d383e002d724592ce6dc7fd0e7791cc77932a2c48ba1743bca00549d4f00a21c49785983c5e24d9863cb38f6ce0bc3d64812155d009902eddd2711abf50314488fd8f8cdd6d705ce523a3ed94127695e1c5c3ee3fbb7a37e2b4d98c6e209d5e0e969009a2f3b06a69fe940bfb0e4403f5770373d79185ead4283ff1e61e5bef3b226163ea49afc04c1feb67942ce421f57cc70e6d6333ed8a5c335bb34ed85f618b0d3fd771037d1f27a251848e859d33aa4ab4080d78a538281050206ac2040379b48c6be55a55d4aa3662c9ee11c1e0866a218c3cc8166207ae8d936f4dc02a1cf7b7bc8b3bdb2d205352290b1da6400"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8cc9180db76f7f6dea7add1c7c19e498f11f8630f7e0acc466ecd7ba1e3f3233","proof":"709d09b739f3aa8bdf19994da889c9d7ef8015799582cb985ca11cc84af7d002b68f3098778ae66f508fdb080a7a217414a00b05987814fba2ff77e3ac7baf20b471aa0a5ac98e1f7ca73a8cef93ad83abdcaa604c182d7c710bf80b1b33c270e0679cd19fe0560f75ebf61c6bbe263e6eea5770f09d94019d366c704178d9235e437f4fcb0d71c885c4c66a4f708e284bdbf558f05d2de0454a429049cddd030c8fe7ea6834af9261613163181e48113a802c67aacdcde90bd52a850211260feccc44680d8437dfb1e62fce23a110adfd67a1194d052c011e4c5edba91b7d059607d7e0476d755c124cf229c4fc9372d79f487ef5de5d53de43e3e7c89cd063ee3a6f68a890a39040640bf1c29833de1b88f1bfdfc2fc8a8487e7064bb6323a70960a7033af45aff84b64bf9d3e02969bd292bf3ddc2b61ffcdd8113dd8330f70b7ff160b780e7b9c422e3823f3fceca32070dc7b9a1f16f62d5bcafe690a3d28635982d26ab80b351ebc32c98deae489acf9b505b8fe79e884e8272809285840d0a29e2b25e1a4edb288cebcce208fe4bea2be536c9860c9f35ada89e5f9320c3e9ab4df0271bbe2a516d0e9bf23e8ce418121333ffccb9bd03b8bd2266250be3ef0ed03c0b75dd8404fb5bf661fb949888d295cc0d9368de35f3c19fc8127b675b2a6413173eb24d9f83a991c3ba964b4df1db91e2753e9ee5f4798a97076241058fb07c04765e3f740e2594c80885a12dca837c1827be007a3be034fbe66326e3e8c54f4309953478573f84ac0e961d641fa9fd8c125790fdad91fbbc64f18efcf3350d226e6b52ad1c061bcb037ca715358e69a6eabe5af442881e7ac1cf27b2c7d1745d1030e71c658cb2437fe483fc723af722a664e042e894104810be7650c939831e998fa939c116f80dd80c335a62a1a4ea5806ee4b71ae231e105"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"148db441fd7c323c87de315c8e1a47b149e2ca60014735a92ae0ffe98fb04e43","proof":"2ade0f05b9f178b3a2457c229195326b6ed9b1e24ab2747262469476157fd14a56833a7b19553b00df7449066e09cb24144e89c17a1001e2116f16912f2a8f2772e94636472851e12a23186f24660db8b2d60898e125d2ab95d5a63aa1341e684462d71a1de7b49580a89e8fd68eb82e66c12575d1eb2ddf1d4b986612fe4d2c2e2c22f6f52d4b4a0bba54cbee695ffcf89295f60fd3cecece2003503ecb13082d388c5ac8eb978844be5f929f07b8bd7673fbae3a5e3bddeb4defba9068de0315db0b4cef3de88d304a753b32182b07545e3b4f57f9ffa1d68436466f6c7b061c1115dd73fc40a598e1ec26f73ef3322ec289b998000fcf650de4d3cc1080058e212708d3c9b18ce38513fd856b24ab687ded1cc8157a6d9c7ecc0cce16d87582865de6801fa633c001cbb43cbe0dc76c9c846946f6ea71a92653da5d77dc50ec37b17119eb7854419572198b8316c3a91a5aea7e04748d3767e3d70a97451038a9c71c75d1442dbcfbb2b5f90657cdcd0006db49dba5b3347762613069e03704c0f09cc8d7dfecab59f7c9e9231be613f53cebc2407e5b6bc94cdc47cfc671aad91fab2a9c53b21cd1cdf11fc94be498bb7560d063c8da39e44ec1fdec1a5fb013033da4f66d3438225f724fb3ac4edc6d6e12f07f0f909d032f4e7658702f1e9c7e150892975adcf26eeaeadc976524b79b0df981752788d5db52ed38690180eb804e1d6fc62f9b0928fb72f6243319397f66803e8ffd3435664538f16f53e27b21515115207edd54344fe3a26f1ddc0d77a27857f5f3094a5145bd30a501e6eae74f76dc3b1ef9e490daa3fd79f248a7650b55aa4d96a5f6fcb768c589775c3a08fca41f22d4e5a56cdaaf6f60aaa4e41587e6b823a7c44c6cad1c44a50169d9b1a8507d7664d557aa69bfd22dbae8d49512ed15843b47b265cf869bde0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3875ec8279d53c2e8776e5632d658ced11018ed732376b1bae6443133898f828","proof":"d6238bed3a4171e82f53b9126cda057d174e4416b018d48f7667270f3bdd7352b0b1b8825c5a8dc70c6baa4b43c5cda51e3346e08dd74bd05d0a903903f6a5473a11a561ebd40fb29ac35b376c195d6092826b8ea5d20e8df1c2f8cc9f38eb0c360108bc22968241cc013a34cb86cf609ff11f1abc7bd7c3384bbb54e782141412f72c17134e664a036b8892fdb2a68ea667965116f906bdef28bedd149dcd092c3025c2deb47e61450928ab3d499807d6884ce90e4ba4ea109fd760b6816f028f95b375d3bfd7b816fd68271043a23183b8ccb887db88afdb1cb8430dcadd0b68c7d53137bec7cf45c1444157358c23d3d4d4b6d43dba7497f755b8c8a373153e94e080cccbb4d9b799b248848de93fa91fe9c14d1ed4d7c0a09b7b671e6f423c1e030d7ebb461cc145f32710dd21aa1072bfa0bfef2ced8c26160a1489b55c965c4b01fe665dbee2d067d4b7bf291343606620f78e1a859abe9d5cb89bce65eec7b45f28853b78cc1b55f4ebe82502166b50f84627f2ad427bae5233b6cb20ea08c0aa3eb0a1bbff48ed1db7d95f170b16805d3c3b0a68df7eb508f564c24810d4465d251109f10ebdcf43d766712dbfcef0713a8651c29ec0fc4ad6c95f64cc25e3e38b70bc6b62125f220ffbcd36ec5cec07626ce5b2b91cd51ae347813d201537a6dc1fd0309b8d2c002c6304186478e911a38e94aa0d955f1fccd34c0468d5cd26699630340a4e381e50fe93abde50f7424c37f80e842db6a9c52c6c3d02f1bdfa17974ac575ea9edfb6d1266d2a31a80be87e293ec4ea611e7556846778a196e2dcf907070e913dacbb994252352229fa912491a600c66b4722d19a18fec9750b26d1eaac1789c85e6c242188bdbd81ab73609cc076e7a2c07ad3df0c87246361bbef1b2d4a9d4cbbab2cc0fe640c1423ee3c8b517a7f23cddad2a704"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8a03d8681a7df5b7ad25eed42a35f011b2da88ae473c155d5c128903ad7a0934","proof":"44eb36c9d0eb88e68dd31af0165075af305e5f351c6a178fee2613fe57b9cd7bacc0b369505791f403c7306bb3190780a68e394d9e14fef03c974acfe2a2711634105203534d40e51bc6d86d05583c19ea1560fffd04f94ebc22e551a36ba216305e800a12a4b121979cb1eaaa81a6e125647c6b1e2b6aad99c02192975d856f020e6a260f1795157ec419d161a6f72699105c535f3b9fe4d68783bfa3cf7d06f5bb3f30bc22774a7a260bd00192ce1a590129e927fe6ddee03325fc0679ac02f779e82cc51ab913a241c4a400bb1d131d448bc84254689fb6303ff1f166100ed69ac2a3877578360a80405b03fe0d9be749788a4ef042e9c30b31d882493b172ca3ecf79afd5b9dd590b88453db67d60fd63b09afb21b6c059ce66032b6a953e616d984758cc83e452498ffa4626e151f67617fe67bcd1722c58dc6448d9a281634a5e98e1519b78fe7b426e199bf114b6f94ffbfb418fc14c85338ddd29835b4eb57911838eb3c666f327e10b328653eaec4789a31e5e04f5133fcd7487d047436a1758903f8fa65ef78fa1a146d6c101c41a14632d875a24bcdfa35e2f3047abcabc50360be4344fa1971a657fed8c2e65eebc6806fc9a2d99ff32ab276367613600578d2d7527e2c61e2d0e387a82ea5d36cfc1dd4fb61c97a069f455d07049b5a4d04fd920a01adc34e163b48c5be9e48e2a28d8afc794b704bf144e1628a0275eed95c166b41c1400a8e4f54ab2f4b5560a82888e23249b4026334320b90d3887122863906192cbcdb1a270889a2739e196f38c1521427157621b2937de0b09bdc445502a546a72bd5fa0b916fa0986490baf112b13430f70dd24ee900bc64e0d0add28fd65e3676331f5d978c836a00dd5cafdfae643677cafc95640c821fffc5942a381d9669e0c41f04a71894afd64bbe1b0209e71c502e5b70a909"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9ade64064386c817f76a91c4f4a018d2e2ea3c61cd485ec5d9a10ddd9870b305","proof":"cea1d260c550bd17407032b63f4e7de93a5d01ac2a56eb91eeb527f5f6f6fc0f705e41c096f88e6556e9b04e72306a1ace7f9b0d1c6b8caaeb04d012b315f60f9846e02fc0f8cacbe0c2f14ca540f8aec9256e9f603c436d2cc698091f06bf7f36cf70c84f9c66364b57fb190c57d2f072701a584aa5ed9813dcbac9ea9d2011bd8606d8318f37f145420ac219861b41144eb4d741474de6436a6ec7ac5908057f71a580ba97f09d24c980fc22c6d3d9759282aa05dd3aa8b1b910a931f7c203131e14addacba9a49da82dcec8a6d8defa3f70f1d84f01c2824820c9eb72d30f8a40286758cfde54b0fafa262311d89a70b28b1c42434f60dac2fed15cf64b417c37ac83292dc897ef3cd8b7c2f01f35d574b8a7605ad47553c4db9ca7fe302326ed1e49696962760c4c9c93003538edff49e2a206f3e2e4e5daebf0ea85f906d42c0c5b503dd615949b0346cce532c4f41bceaaf6d85ca256b76b6129384038b22d0fb43425014b420236ee8ae6de9b758a8d3a0c155a1d17cff3abeaa9ae50f2bb5821c367695312aa1c64cb032b6cc2f80a3df9efba744383b23ee3018b62922edfda00af2ccd3f3fce4913b98fced7c453d37bffda6986e7c596ff1fc9128c4c9cb8a43305a6a76d05abfb12852bb5ffc66a41cfa47e502ac507f4a3bc6e54bbd5e8232ddebc1c27b341875c0d74f51cee25a3eccaddd1a974dc2e2e7555166b74c1559b5e5cffddd5367cc0eb8448e245a2f18283c5694f8bf92dde8745b2f683b4c761f83ab9bb8e8a8335cff00cccaa5ec6f9efa3bbdff65ca2e62471bc5ef0c803a341332751a5c57713b51f059605ed9992d78cae857e94a3b2833b5d3ed12f9dca4ee92b0250e44429f21abb16e43189b38495459dd9dd815d1a0c6150c8343c6f64585f1e9c3e12976f0678884802a7e076a929a796705e7c3809"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9a970945c1718f697ece7e34c188aaa05b68734ed11ca1ea7d75702afa5a9952","proof":"785442b507197f8fe170c27f737bee6828f50a61ed02574468a13384f685a047bc87843d175457d656561f4a476d1713008f07e6c431bb1aa8affc4b63cecf1eaa44437e460d92a9892e04ec6f99cf41becc1369d6e509cf59ffc24194aadd4d165a2d791413ebc2ba14d0d001cab456ddc8df79d2643b71dcd96f9dc60237061af4813e32898dd35eca2be20484911e9a12edc9209e3a661eaa974297271f0dfb357d13c9d18cb4e7d7f3d4f63cd953da6c2abb8968d623f29af03fd1dcba0ea157772aacc901b93e0a891be20be9b0aff88132a4df412943cc579a862fd203b693a81f5adfc302c2a00414fb63211a02a5325aeb5958626911833fcf3f3e4e64b863847b5c725189e6bde5282e02db057425ef36fd7bbb6f30b3835a907e59a6ca6e429475730d6ca44b4c0f72122b69d071bf5b0fa0e129365ee93e7f7958e4e8aa9856f9024ee893aefbef0bcdd42199115d8fe8975ed4f39b97383c550578762c5e2555bf4e662b83c9db7d33c4a66521725701d8f33b87fff6577a202e92902b190db4fa06000743a1e7eabafc116c574b36207fe938d3bf0dd728120dfa596de3d45e5721212b7d7ac3115ad11ea2327ab1d4ca753c33c800d3ba153e0c8a5eb9ec010a26bf8357586ed58329be2ae942714ce0a883ed284ffbcb753a0a797429141d07348787d9b4df154dca98af40ee75dfdde044b8a1fcfa9e2b2aaab28ce7574de604170925b7b00804adb2908afc10c108487f0f2416090d4e1c26ece1f6dfd1ca670266bdbebc19dfe8bd8b6d31c210d3fac1c9f31085406d7308e87ec983a66ed718c1116efef94bd59030fa600d11f128225ce6e39e06812d76b39c1c32e85c382fb3089539899517cf27a02a1e89eba6bc05cf2336bb550838b6017ef71a5f699fd27ff1818bb6291c0315f2084440fc9f5655d0fb3b830f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e859e2848607b749d71cd727bac738f1edbdf8e3bab7996f46d1922fa76bd061","proof":"d0e56ecab83105310c974235b5d41fe5a926097f2d1dde12bfc3616ecfc3a92c0edf3ac6ab852626420a12e87ffa9e35bcfc501b1f89d695ae3b6171a0ea133a507b3bb5086f7aa7b781742ebbfa486fa8bbeb99573692e19a51dfc952725816e698acfacbfd33802f58e7ab4db39da0cc37ada9b73f01c6338ecad5f230da750041d52d48370e35d80ea6be0cc51f907221b854dd4e6a692b5a1cf1c833960c07e42f77f154e9eb64ae9415e91b5496838e3eeb69c256512ca3923dc668a70729c6a76eb66b894914dfac7f7a107b83c578e436811d764e577aae2bd727e30cb40b32a1bddbd9c80ee638e2c5c21ff1ec4732ee5b2a1b271acc42386cd6167df88ee989d2a43a31c72c74b5a2196035a685e9160a452ea8d59c2f7a0c186d510ce49e5fb99d0e4fbdb2c549aca0c7e0fb7a34110585283503697cb024248931003cccfa117c1f738dfc998d4bfd33ed677fd0c46214ee11b11ae8cb0cebdb67ec46f35b9dcd0611df434cd531528509bc63149493098405f2334d5fac77ec504263039c06fea90b49d0c38d5d692e3537cfc1488d593980dfce99b54f3c5b1ff4ec86fa10c04ffbff2b3f11230186a0ae8538b5b30316c62426fa14b871d80dc28fb4a8fe3e2baf874e55a59ca5e9da91c352e334501e622ba78a569bfaf141329c7b9306d0966375e6430e7293935422e32c646f99f6a6b50031bf52b44c2f6840ef6e437129e19beec4b621e38f666a9597a9b00bbc453496b45743f373312a5179839293186ab4c5b167c8bf0e4d71832ff52caea0edf0ad62805b26d7048ed6d1e5ca7550ae8ef3ebed481a6d2833a14845794f43a626c20d3d4fef5d6f248c0a37b1d66836efe298bc5f80bcc2d728f3e73dd250915fad7bd0138c4e0744605aaf4d841c7c6033b9adee1ecad84e7d2c8191dc30e46d34f196e2c2dc08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cc0178e2460c7971f99510b8ce6a23872e200bc2a7ad23154f480e5450ca161c","proof":"7cbf686118e4f5e362b85103fddb1b209f7b3a8835368d7d80fa0a12811ba027aefe01d8cae53824da729db1e7c6c55541bf800fcc65f9a274fb22b3d6cfc42f5e2e7ed4277e0909e7cfc3f37604e210f098bcb3d9d236a863a9e10b1cb03d79f2bdc6600da460280ad860a7e16bf5c7552329004303826b7983517cc2b9663024e77718d7e010a0d27685b83c0a8aaddb0c023e86be27c0154deff73fd6de007b686cdb4779ca09a537e74a4e9cf0fd30aaa9010018c1d4cadd385b6da2d40baf121ccd16618805c0f513036d1949d3c7ceb11a015e7a11aaf99cec3605d70c42760ff0a5fc86195b0e3388026219822cb31b049ec19ef3d9c63321748ca47b5e7bed718806e10c95b3a621de518225ff4f9c10b7599f036ce86d3c19ce046ab6dd6d23be12026394fd0ea3f79f83f20002ef956763a3933b2f2da6e827884d78d6a78c48c174bc6d3befc7894d8f98410fab382ce517a6ced15cd91a6b4711403c00f74f4d574162dc60b883da066fafd375d0d63720ae5e42069cf8271674c2f9c3b98d751753309cb06ab7465043edb610f0c8637c441f172671bf25fb60e40b41752c649f63389e77f333d576fc8e98b31b7025a6ff24d78fc545f244235a5aae1e45e85ebb180a05a4822091f06ae8335e87df3f6f10ea344cfd64b307689c3715bb671a0c0cdc5428c9c614e4f61f5d5e89305e8731080fb169e3b305e264e9418873130770a386ee332ce50f7109d49e949891283173440597db78687eb62b8b8ec20eb4af22fc17b7305dc6e84c38e3966b072197b4f3706989a1185890c1ecf417bb1fb9e93460dcabe6285165fb3b995c04038291f0a046e9a27834389cf0c9d68b31c0fa1bd8c5ba5ae9f49190b48eb12208a00498b20809320a2cceb46e4fcf627ddb00a47c13adfeb32d865791d7f0623aeb721b377226120d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a0857eaa6cf0a7c61c6fa622f5d70c4c3ceb2fb1843fe7195ad86f5aace0e748","proof":"c2e8a467952c657989628073880d12674ccfde02e96565214bdd4c89704b5434b887cf1e1c54b69c9ebcdab359a94ab60fea45b5d22370becc5c01d28e9a9e701a0538ba21270b1124fd47116e231be4e11759c289270680a2e6f97c5317c763d605839cd86b5162410fe31df8a98487109ef3c4f7df1be2f482f4d188752e1d59d088988cd6a432b90d2db990bc48857ad9c7519f294cd1eac132f50a02fa09ab99aec3d97196ddf4b427644ec21d3ffc2156cfb476392df0ad1d8c999d0d0c0729c3295a38cf0cd1f785ea905af75e60c0d2e835d76be245a6ad00f1e33003d25a8c88e3823393177e4a4bd77065bbabb8666392d86605703114e95244c75d347e7465906f8744b1dc1db20ffa92573e5499b5809e46941515247206facf2a6064ddaa4891d10e9742560b3c5be35a55c0f9c229c1bc40f95701ac8cd7bc3db2325a7d73c6ff37794df0836117f24791d87da0ef43b7ac9e770565953c7563ec7eab7e93a288dd606e1bf0635bf83e4ea737553022b19249b6fe67756d02391e5f2faa2ae2d69f48a49d2d5de277d260ea1ddddc630eaa68fe2445192d7c1aaeaacd95485164b03fc0a7f491b82d525c64d80ed851fc630703aea53198895d7a4846801cf86a5dfd6c905b0702a21514595be4d697f6978a20edf086d99c3584871fcafaac024a49e727492931001f72f41227972f91685caa430e8f6b64390873bdbc03714f34d30a07d2b0e01e2cc6989de11603a2775f182a95444ae10b5a93f7f220cf7d350b9f60a9ee107abbd1533f4aa4c959ac5d528101ed0f946816b90405a1e64c2f89aa7978db66d943c7297c965dfba7441dad146591998c69b7e36df2682da09f0ec0b096aebbd3442af5a76ec5cc8598a7d48a91d9e6770d6729c467290cfff3df112aea35790ea29b4899d24fc574b4368a388da957e901"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"46f8b33d05a1e15a74cbb734f82f4f773402a846ae494b1197a2570f64141b65","proof":"60c2a2c603411bcd3a6ad011200735b34a78ac0ba9768004b6cc518b7b53d259dadbd34b74d03a2bdbe6a833e408ce4775ab07fe65f2e63e1076746dd3e2c65780330a4027bf7678d38942538e82be784b68f4ba3c4bd40b5b18f3092b13b74984f4f10a5acaab2f22e1db1634e5501fe8adb9f832db54d423433432a63d2d253d8d7c4df48e8f12250e0f9e4834262f0665956bde00e29e038246753af27e0afdecad09ddaf5b549575f8428930cdae723cd51dfb4900dd8dcbb58a7a96610c37e3b29a8652b09453cd596d5869b9599ecaf44f56274bd07040f990dae41804824237def0f5477fec3fccbef47d45909de40566fe4fad61bfc10a651496fc71fe3717120a8d23865e0aa6681fcf98b41669149800b693394a67ec76b0776a465ee7bf054a32a95fa12e844c720a42344ef5df4c7d66d5b512e925456ed8d0107e03ab1f13039de7045dfe7c83116f11b9667db89ab78c4b8207a65dcc7df27a2246005a61945be30f080cc4916fc3ba2ce407fc78fbcb8ee8187e0937a55334a41336bc3d5fedd94d8b823893a6ed4a578fb82a3caf2695b893982d87fad068c2a327f076d7d558c2e4c16e3013dc467c22aae4a206d0418ce7b3897b0923107461f1b24c8646418a329f93946e8678eb536390aa5df310d0b2a9b44a9c690ca21098051a4ecfea9b4923b0381e889e4d9c2410aa32d769abc0d88874153c2c4eb216a9a5912ce910d04637d81e1e9b65101aa0e713a504c349499ba309051c5c3c2f706c70bb56b832a5642207676ee431a0fa5a076b9af063b0645c83146a5c8ed3b77a2fce3e07950ab4a82a9ffaba98826a966f4b7a7956a1bfdfeb3c2015f9a384ca97305d951bbf97114e0cfee44b9a290385e1d3564a76838cdc7c00df44634adbda4f26275645b5cf695c37b69475bec16b9e50da3a68139d2d8c03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9c35679dc3ebc7e34ed148df142dd582636611c18f135640b9f942369666607c","proof":"3ad43b6cd47e92798e7648d6f1985adb73ddaf75b4410ddcf965d7dcb3b9e90f50e0d1dbe724c0822a37a368600f6aa1f09a77faaf1babaae356950d60edc27b1a903ae91dc8a50b30ab38992911781fea3ae21a066e2d038cadabb40dfeaa28a87ab7134ff167fd66bbef1e5c367a7b7937417a3feef656418fd8b365e29e2f07d18a1df40fb34953c7312f58bbf6954a3040cdeab0a39b8c3f2882b9904900226e17e081d784418f5f7eeb2c47fe9c663a9a8e44963476efe23926f76a6705090a574123d37e3f895f1414cb7583d2fae858f02be34695b440fc6b08a87f0f505e333b7f431e1ec882cf6c059f78dbfb91404e148229f883eaf24524e8f34c1233d521768aa5e365f404d7122af97b662e2f0a0228a9f6d9f69829a1a43d00b062b51d33c912fe0c76352f34f3ecea68663889da853e7d33f18b4c442cfb7b02afd15f53541f17d34921bb9c962da6e8358e32b3bc715d799493ed0387096d8c89073d5361ba5cc73029d5864ca88dd967fc6142a9406c761c409d44fa7a3c8087c7903badb61381a0b1ba7a90b06a3d564f36db3e7ada856d25790302ff4d268f54bc31a3bb945f207209d8bffd9f9a690078acef419c7c39cc6dc2f15d7dfa4a7cde6a29253ec65a3f35b8a3381b114f86eb5c6d7242e08e4dcf4ee9df53829a6592f72864a9cced2655d12a3da5adb151571bdc87b6dac697a1ba7b0e6b5438905cc0bac80dc25fdf24d0f5c52baf2997349218c646c5ddba3b65bc803b50b24e6433e3a856a77355ba49ca9877834ec2b7b1a8cc558a2bbe59c9c91425a65dd7becd2cabe489f52286205397d3947d686eff3d95d9c0b76afba4a9313d641d0af94f10a69e6b45f7cceb93b6c74fa10c094ac731f21ee3a02123e2500e3e582a80d4e7fe917f359c159f1e6148136c5fc40e52b9ba61c7498c5ce7b00b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8c58c35d2d59b987ee43121757999561c1cd1f0f50069b105e75692004c7e821","proof":"ccf9f16e98daceabfd082bbef679f85cdf692c16a50a5b1d3fc44689836b8929c28502e06e4620100c3d29ca2087e9a39b03029312fb1c16dca8ff11f47d77174813843eeefae4b3b41849ace99ff889b78439289dc688d0cfdc32232c88da7d0000a0e9f292c06345b6ddfe2f0443e1508137adff47995f955e8696d2fdd32281e32d0d43afa4fcc04def828a175245aa286b7381b5562a767d8e037f68c502d453aef3efd0a203437d6521bc4a609a277dfdf2f3f1696919b19163da18d70181a27477e3e7fe93dbda508e4c24ed37e508fb475ec73e43dd0187442846f001be55aa40cd4e45b84194ab50a3238b5e38246610193ecc5a21a65f17ae02172b1ab11124b6f9712f89635c2b3cf0d9ecfc090dcef5110ba3b62d53e993f93f1e067547694c90cca1de203d4956c9588a972d94db404e8c0b0c64d361f5c5ea477691a55d51fbf0363ad4d510682a3d551fc274f83ea43e8fd763bef56ee25e019a9e2b27e534ef8e52c7e2727d58234367b7c1a908d04f4789165fb75fa40f29deb3b5a4d014988c2bb58ec3b2c7ec1a75d03945358c76fc514ac3145c1cc3612c97eccd0310bd36625ec3e027607790860031f2567b088793db3f9f1e102e725a498b36357dc8ae73bbe9bcbee7372239c60c7f26d06079d139f42d1b4e2845b054f7d8b57bcffb49c982404649c8270c9f19fd6d7e841e7f5406712e7a602f48bdd8294f4c68e061fcf1cf68370099836f52facfa67a15d79f3d0c70786e249a2e50ec8af480266c5f1e009a74e0f219c412be0bccd88920137a73fd1d707612563df2a67c238bfe2d225c2e5b26fe536427bd83148d66aa2701d03942fc7de4ee9c0258a205175e1d267a7ce3639c6afe07e5b516f155523b8f42ebd38007fdfa970184404a07be27f5649b2d557c98dae504ed8ee0fbbda838cf09ec5e08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ee2fc30a1dad1e341b9d6c8d62dd2a6c429441805032e4946245e89debf63c55","proof":"8aabb087ff62227ab5fe2abcd09aad55680b6dc606e945577a7ca7b7ec273c38fcf1ad6309f580299b9aff3983a2e5aa1f0270683d12ce3174ec1417bb41bf385ebc688606b5c647ed6aa5cb9118e0c9dd9b5bfc445b6f486bac31ae95f73b53ae3b05a55f5932cb61820b1483ff2322df573e1fdceb9cca0007059ff1051e49161c54af983ab378a425ba897f49f207a752087a45c2f7d4fc747c12a2ffda0e71b7c4eee877ec05740c69e7bf54a4e3e36d49549aae62c4a7687c6d52a8b30dc31ca185c7eea94e31884fc013653003e42d542378303a8120d1247c3a22e40dc4dad1813a560ae1b7c388d55fe8b683414439aa51da65738b4b283a084008530e47b8f6c9fc0b16539e6f320fa14c7145e9b1b26fe6a242f98bc5f8b3301d4a5ab2e34c29f5a41ba907e62e2b62ed8fa6b59e7632301879525bdce65ea6b96382a7ac3aefb0942bcdf2c21643ac8ea2e0a60b6c081ac0d331cd7afaf207c06446b2fded05e811ab7dd68943335424ae435936564c795ac863782edbc552f62a60faa7316d4566bd9506a8f88c1d4689b856824020522182c0574a8722287946d69216b12c84b5d564837dc1d6722005825fc1c2c2c99214c50d382eea7c2227b4b42a55958a16712675bdcfcab7099059661302644535eef1a7792d885e8c3eb8461e574f980928b49f6f5ad15499dd89d2afddb09f6802ad08711e5850bc2e70594438f5674af7328feb34eebed9ff069714c59a9fd4a83c04b8389808ca77587cbb67a3b3b474ac59b2742f2ab2257d4f50221668030ce1513a6370f3d8043c9b8f6b94f8d67477d60b8a8dbbaeb33db7559c315b59621bafe7dc82c7584036929e81829fe97ea82198940439611e47282d8b3887d09af1b133f2c68ef005ab6c702f0fe430aab3e6a9191ff145351b17ec1db2367a4d190caa306d7c840d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5a7878e0a1b914fbf89ab1610edd3e376bd6df92a52b63dd7ab64e8509501b7e","proof":"9487399ba4a4ba8828db3e9cef667fc64a47a2aefc22473881d6ab5c34c0aa085819fd5cff7d25d244a828095245e36ef9d843c21e5f40ad36a6d3a3b143c44be2b8d16dbe282cf3cc0082df4a75361e7da67366aa0b96b03018b3d7a0165c3cc29bde06526b1e1f3373d44311dc4ac4069f953003a1e3040df136a00e82756d21264b3e325f55fe01ab636f077f58b1aa07a6e760e97e4b18b249a8ac7d6a0449d09c0a23a1cf7bd9f165f132e8d2b1e34af888795b659bd20b43095df3de0918898aa776059e56e394b686e19fc66e9357b4f024ad1090eda85c52e6f57808dc254b93f2eef55c964fc601e1227b7a499b2d5070fda0c41dfcb41841335f1a70be6d372aea3f9a256e756ca9dd5f5fd735307fbcf11cd69407100912fbdf19d297ee6355e4319af4388331eb5b8c63726b90a1b218bd7992efe89870ad6c309a71a40604b256bb9fce0a51b95cb8838ca616b6438db36f7e83a7cba2813168e86841b5a5ba10754434f24731cf1f209bcc90e67bc07500d24cdf1462ac3a1a625589ce9730b48899136d358d0874ddc7010c30122d63f9cb1efab04dc998076e8cdc03da4ffbd98a7b3374686ef7572047db778dc1bf8ba507e6e1b073f273b8b955a2d1898c5ff50810e7f84b40dd524d4f5b6b7325a17d743157d2b4c36ccc2ea285563d530c0c4d2925306d2905d84091f98c6007f6795aefd8fc80965e1a6cd6a6c4e06a2897b176879d2fe3280dd36e90f620d20ae597e3c199b9a0668af952f763d3409d82d8a4a4edc96d715e2fa6681988cb52cbfbb2d5dc177859ce46c0918dddd50a94940fa58ccfa550d51220db8e23509bd2a51e8e98154d786baf68add22a2e7fe399c1370ba1a37df5057ed6e491da9d7dc855a482e52f0d66317d6cd64dec70127c9025001ea91dc9fcd5092b391b8092e1602139e4b60d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"48e9dfbec546b4a1c01a6074cafa19f25c18cb4afa624b6a935176780e86343f","proof":"5281023473570fecafa46af0e36c4d345845a498950c46b19beb847574fe2e7dde1e5a5d4b0940dc82e6f1af9aa98ce9edb3b0ccd4a6aa30619fab8565d0fe319afd60067361ee1c9af120b628d9922d2da944d4a1c8e7b9c1afda9d8651ab2798d4b50dc49d473a4868d1829dc20383ee8e41f42e79813c390580b410529b35b4f64a8022c3d7a1b479ad6f02bd25909acc704b3e0689a7e98648fcb7bfd1007c31bbfb13fa72f78fb35a9bd7575cdcf53c382f8df6b918bc58a6c67cfa0707c4ebfe72c2e579376b637a5de46317164842c4ef4986b5e157cadb44691dd10b34db50e47be2af781c2196d70dae7f4b19aeb787e05753b4548e65803975db2c2ca85f947208627706ff738107b30c4f291bbcf74a918ab8bfba7aed64a482480af891f782a3844dc26d0e7db89ca8bbfb673229c9f236ac82c02b41d488ca5c8069b6d96889b08104318853e4b578e226e4e315f7f0996572be936bbbb0e77f786479858cc798178b9208adbdb432a76de1ffc196604714157dc98226c46635842a4569a0a51344e0c67d1a836447c3b75b64cf2b8d129e0937e5ccc7aa1f41fe61130e2e334a2cde588dd694dd2e123991f1c7a80cdd0829d37bc259c03046e841493cd6b09992327b8f0c9659163d9610c357e97a09273295470748c5c463ea12121709425c123d6ae1ebac327ad0c66adde614d358cfb8220aa949ab132344bcf21e4a8b863ae15fcdba21fd7fb8797a556396dab1511760c6f34d0b8119ec7c4c194856b8d54b1a351458e0f8112fe62fb1c2d82d283cbeaffb3484ca796a31fdf89d91b3ea5c893045102a19386395ab9fcf48dd6eff1bc2047cac054154cf9a33733c0a86025c56aee4fbb5bbd0bc971b6c97df6dcb7bdce379cc27065a469ee4780f31ba59c5ca71b01b3f98913c40ff06b95bed8eeedd11ebd60b03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ee723dd22658ed94a46aae5dd87f544d2635ee8227065cccc8c1802d4d115107","proof":"788ace6982f341310f6f8eca6d2129a05b859074208079307efa75d4fbdd6b689a4b6ccbaf465ae7936b2af4708afefb3f5c32cce805ea33d2147e806a542871c601422cdfc54d457b8a6217e8ff9ba7e95389ce38817240202101497cdc1f5a5250982e50ef555d69c09bd1aa8dad0c59ace0ae4dd1483b38b3a365d12f07751884022ee176c1caa1cc04837f4479de18e84001506a58ba7b47fdd3e86ec3075751d882c47ade9bde5f541222b5e457fe9b334f93162d78b5fa372e891bf806cd0cc39eeff37c0945d38eef933e060e46d94b2f4bbd828c98031a28382a000dbc173954e0e32b50cb725976f3af375dd5bdb110d4e2e5b87436db9996e91a2d04bde633bbb8cc18820fb6d0ff26a939498d70d0d8f1a2ae8329032a9e433a66c49ab2d918e62e91c935d6971b83c4423f9484b21cd2a204614a919a4538dc470815fc51e631c3c7118e92a44be85dafe6504a30f85b7fdc317d97e04d12de47fa880a9f5421d368144e7f0b5865e920cf958d3d1467f5e48b0af94a1016a256fe6c043be872da54530d5fdc0425f67daae0929b5bc9c8dfc8147f4ed0d9550484ffa6c42714ac80560be8183e9d069a3430f44102df2659626aea0981aa077e36301c85733c62fa5dac58437c852d3b2f6462179b1b3ead9e68ea59175ef61196930531d9cb270fae640dd47673209bf327bbf635d3d2cf3a651051bf12be3bfc5dfab45feec389e1e9f59c8e367180a8132bdfe79f643ee866d95d7e780b40265fb4f998276197257f7b2d7a4c6bab5a32dbe81ce0c3027a32a2b4ef52f676a0f54efbcbbfa5fdf0d40c7119beaf9a72614988587019b62a15ff1caa78260793d2fbff49fca88b6960552da57a4804c7aac040601e592840832a68eb0c7b083ca8031b9575e55f6d560192687226bad13fc33576676da4387ec0aa3f647802"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"28763f986c6edfaf37dea164e420fb7213e9486d8d71e3dacef312a77e160310","proof":"2219d518f42f3a04fbe474c0c08545328c50cf34518fb581da0755ddb7a66e7bc225823e99920ad74590fed33979b56cee13003c1f8787dcf74f0382fa5eb246f2d0beae87ca3c537c829f38f90fee0001635d283bd4c87d9922929440093236a223b0f146c5e42bbcb343701e619eb3b56788b2abc0671341fde6c283f5a01595e2849cf951e4c71d73a66196d4a2a646b8412562084ddba2c29f6e6bfe020f020d0b2ff2c5abb0df7d6b276095dbe24645d7cd5b1819bde686162d4c364b0295799222fcb512befe8fcd32c808aff51299d2ce27b96f31a888e68b33b88707e2f5a1dda73f2fc7924c93e2978b47e5557ed696f8f85ddd8f57a14dc890c63c56bb364580fc98421267fd54b671cd5a9fbabf4bf170dc37771c43861ed9c93334c585d0a2976edec544b3b78639c18658529dc8ea28b196aabcd076d85d4e5a127c478af590424c3d2e27497e40e1fb4c3d79cb80d9b00a8fc9cafa84838928f824fb914086888fc2dc0374d682fa9844f22474603d7170d81b1858e1272f32a497c4d643e1b928fbf24cc293d8e88cb48c1a35b47cea2e18507e80af13a247ae408c97415a1ec4ab8f25d87cec33349d31f0ce14fadabb303dfcace6fd253db0fe47a0601c5a800dfc7cd715a749aca2ecdacb10c592b5620671e19df8c674ac0c02dcd4823d387057ec7bb6b0645a26ec5ffcfc67867e7758d200cea10b2e9a4b5afea8363436446cd4aa670953182affbf2cc10f014df3a0d36ba0d48a075a3f9e473953237c512a1faf2f26ded3a1165d0d1958993869b4606ce57b5025fa39efba3e7436385e36c771144509b759de8865286cef2f620bf8068c102e46334babef0ca492f7d960fe0b3dd963f504c19768b505be498201c910f645c104b82aced61a9947c7e7bd5c7b5f2e343f9984989133330c45dc22118c7e499702"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b23a7e2d8e32d274e27880b04fac090536179c3d0f72d21572bbe7e678d0bc53","proof":"e2977e4e8c8af9d1d86a1de7b8326de6b0c2801c75a59803e267454b08a77f6e1679ef309a12066eb7f4d0c5591b90ff86e042cf35d2ed98450502fa2e53d025ecf9e0b65ee40568627ddab07de165f3cab2aff9f0b1789bc18343abf9b22925e0bb25faca992d8b3c680b35bd34df462975369527ed2cbc12d80e0cf3496d1db2ebcfd2b9f188c6973df6d6215ea4aa301235dd09e509c6f0cebe8862684600a56666a97fc629d1dfcc13e15a772a8a4089505f11a2124f4dca9e986d7ee90feee0822568639cae4d7cece44214c01be97d76e18f59e0894bf969180f28550dfa3e0b5d0627ddce723cc596d5cf43acff3c8a13dd5eb342c2d5e3c46eb7962200ffcd93b4591ae2c5bf9e748245504dc566b8f2ff7e5971ffe2a9ac8e00504a5ede4d3fe04d027b7d5acfeace73b5c189f21f7a015ccd6daea2d36e47d348159c09f860d8609409ae07d0fa122ffc565ccef13c52a179eff33e1e4a978a6f211c9e853627c892e9b5b23531c64aa2a00ed16c6f2b474f19ef5922067022d8089e4ad579aa67bc46b913530c59c7bfc298075abcb6c38b975c3a9fc7d7095972e6e2e2a57644f8d276fd548a4829e25189ea75f914bc95c09d97b769765cd43e549f9279bf214249511f86dffdff49d20e5b68eafff3fa646a2c0918e7ecd473024b1a63949114173e72e7c484fe33c42c9f7fad9fc2a9c0aaf8c44ca7c886676894b0e7caa2784e727d6d2f37fc4ffc4345986b45e0486868260e47ea0d414a9c809c0fbbec5420b6bd3c09dcd78d79b88c63c21615508b9e27f807fffa4f5a5636273b6e97dd13a98c2010226e91280a889835927cc2175d8cf788ce8abe0c79daae03722f9e246c12ed19c01fd5359b626d4dfb2cbbcb58d7f51567b60502ddfa3714e270ef8613b323497c65cadb46c1e40c5bf8279e45131cba60420107"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b286e998f9cc0a98b2eb6eadb13ece50789e5a8f5c3995243d6738f43dab956f","proof":"80726cd9bfc11ddc97c5b58e07fce0b20912d617b89b29df84ddfda717a9fb263c81feb660281d7629e073919218a651e7210751ebffa8bef19b914ab575fc485893e0523acdd733f29916626a7b4fc242f814036600e6af3f1ecb0317b0d8333c814ac4b9bbb1982dd3a5851409ed0dffe27f14044c96fc71cc4b492c012916e8981defe689b22655f0b02224bb697b5fe3036b9595847ceda55dbf2d51a807d10f1c032516c49e9c35f9e8193aaef2d8e633fbc4bca4d94414f4531e0d6f09e6439ef362d5826c8894650792f6825edf7205a199bf0af72753eac690da7f0f8a7f626bd58b6d660155b82a96e9979c9104b226aed9eca4f8e625824bf6914506211d4395a38d9876cb597ec44cc63e5f82764f32bd4823e9405c44b903de59a40931bd0e8d2547bd87c4b7ba0ad62f5b95f87a3b4b8f28e1806f6581d24b0e9cbc5806376c78a44f15f4ea4b46112c1dbe9885b19852f8d39cb68a32ca3753727a5c569e91fdaf8a15a63859473c55d9e3ef4a5d95abd78865eab7cfe2ad60ac1f04a2aa6991b73fadc26ef8ce9e770eff351415be6d4e8143d6bc09aa9a0e305c0bac30b8a87c86cd65f8d659f11df698eb51f800ed249e18ded338a8c821c4977f380090e6344133af7f81dd3100ef76e3e99b32fc2e96eecbdd1cc35f73dc0a0c5347866d98ad4aa771818626659cbb66f67ccbc9f4ecf136d05ceabb6ff070ac9f555a9b5541338e58113f4c734fca39a3ae2363b96bb743b6beba23632a6205b10e9107bce5492c1d2322a188b5f8c69ad1bcecdeae876affc32af5624e33733d3d54ab24673284c9fc0afd33cda79a73db9764b72c519665a77b520adcb5e8374e2d30d0fbbdc1181fbb8d87a6331c18c16502f62056b99f07d7b60d4fc171c69f98fe0526e1574276d2f6d2bcd372cfdc38ce0e213ffc89684e8f09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"044baafc821a8a0b2a072c2425b2589364f1207c1989ed681bd466e52bcc4c26","proof":"2e9c03c3a6882a60cba6e20b9f6f41eadb4d5d1816b673c2f3338ce1169c0e1fc61d366cdce3c8bd9acf06da692ab3df084c2358a2bcba222c9afaed0786393a08127843760e85c8cd2bc0d49728753eec9ebbff2db353ef7360e3962c7690749c3fc3bbff3ed4a0b73d2c58a27312e21b0031eca1602ed909df3fa7b3fa5c6c1b8144515f7ebe2f770e319f0a3af46b9398e4d72003b7e0494ce87cfda8c8023b152fd1dafd1fd923191f668481c097f384495fe0338d2407ffac9a9a88c20b48326f78f3b62f824e3b1112fd31deb96dcb586547e353db8de0595b06e89607e2f2c863a071eff1b7f4b11d40906251681219d21646feead857fa1ed139f52eb4dce9d63e17505e72c25c35c6582ba0e1faca539f50ebcea18c515b24974472f642d3ffda403c0f8d295597fb71e33c42183d56081386601c684421cd708354eaefa6b98559a0f528b6613048ee270a166f29987d4699ae94b3479209447004744f5a10545f104eb95df1763ad448ee16c466c86102afa8b5050f8a88b1696e340b21ec3ab6292df84ffac82647cd3c085bf1bbdc122e4c1d05f49eda5e8f26881bee747d86b1c3685770ddad2b6da0bc578b5b7fe93dd57ec1f8524ecb9e288e189e3879bc0968a59d5733a60f2c47fcbbb4696880ab857ded0574fc400a72f08390f97f6794bd8372ec60c08dcf5f0fd033f2abfa6c0e383ed690a5bf7306802d42ef60596fe88468919a316905a795d768f51465ea53a8b1727e463e126b301148e086a27bdcddb17589acc25d02df2397f7e011dac4b62133b071cdf57a94a8657238556c95865c1245d57eb155be81dcb87874a2ed3bfb4d1f28d14128b20cae60bc893eb8f5b78341f92a44079b449cdb02feaf9a0d2a14a38b3b6b03761174f29489f847872361301cfb38bd313439bbbc37061c1bc0f927211f3906"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"94cb4e84232cc871c498dcece4532ab01ebf1807fadfc3a335183500a873de4a","proof":"106293ef34c9fff6499b6c7eb76c25bacf6f831e72bacc21ed32a0092ef2d5599a62df2d30e2009b78d952ce04938ba77a2199aba16346e26e183ae8c38bdb3fc631092a001b9f912485e9217bb12fb2e79c6a5701d2266faacee4ec642a9a1d4c8548825f9ab30a6254a96148dd8f95d52e49ad68c2c19dbd11361ad22e405f178452c5812d0a11a8d1ff937d1ce6c239d57ec755119044c6ee7b85e72a8106efeba66476b70f9c17efcda63173c1d694d49840ebe7b21955a63971e5908001808f3a89d1bcb00a88c6145b7d03d83b6ce6ddc7c5d700ecbd43793d1c8ecf029050e87770dc24f779b9923a1062ebbb379fe36087a5a0ee2ce87554ed1ae204c4b4a0f5800e7bee2a06b0b3651d0e2eb95b068ceb668013c1213232e94fe41a3089c981677365be7c5b48ecc43a8e7e69cb291dcc4e0d9c83f8a29d53e71a6ffe6ff24c1d7d6dd394491daf408f66218469de3e2daf6300a738b595afaf1b02a2d87560141c2912f576a6a3cc412ab579c2275821fc7057f81d7a7ae4595e41109aba800903699ebfc33aff1fa7a6b2e0de8596953e12ffea8afe7e741d886d2cc276d6c328714b54911974c19b4f26c46c772fbc58ac6b7a16d1f208b15b0362b42667ad68af3cdddc09ec3af2864ece120b6fa573493ef677414a928b826c5ae9af4abb7ea3d47a8600cb70cccdc368c9105d3910ec7702666b275aec724d247b8108ef8900b7d47945145f1f97d7b0b6e0e5f8ad9b289823f79e22ef813204928e8b4125d85e832184876575447741bd7ccc3f5cd1a49f6a5e032b98f50456d2d8164225fe19377b90032d741ae2771fe631dade12c38b7a92814f4e2421beec650f0218ba3841d798eac09bf8db9ea1a88de7a4a56eecae26b45e017e0e1b92f515f6b0f0230408d7d3ce201148d3996e10fd596b0a802ad1f479f36102"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6c034c841e28d257ea9139c3743e6fcc5a2e5b1f934b7c75ce63b5b95cb05d4c","proof":"4c89cf98f1461fcea0d4649481ef728c3830cd63db47506e8425287979349d7884dc0fd104ab5fe5a8beedea83f26f8cf1ec24ea0201d9303a39bfc4d7f0c67d783920277f4ee259271b6350431526abfb0aeaf653495f2371e67c116e51de2362712ef7bc92f42e0f870cf4701376c56e120b505e6a7a8ecdcdfcd52657a6781bc59d946d6bcdc5c1cdc20e1715e8d8f582f23f5cd94acc2e26b1f396024a039865be6060aa7a0e53a4fe630adea76c7ece8a706c2585d39be7ef5997002f0edc2e6f5ecdd06b8db40e78aa96d6a964ec78d3de7fe624103af998276c9022054cdab2a2370b5b730b2377c59dd9c241372b523b902b547a8d57eeca8f9e421e44c40889f768b685ca2c134c0fbb0c7d5f49178d3f7e71bcd684bda7522e764de8869ee251d0927ea7b9b886e862ebe8047202af7b8ca11534ca3be1fea62b18deb67fa40640b9079179ae6778a2ea8681db5de0f15d18a5690b6c8c27c4bb25bc232411cb200e40c725c5d4a0f368e0120da78e055b968b3861d7e6719a6e59c08b3170a8aeed43c05372884aa8bfbed511e42e8bd61d6e11253e447b453e35307b825a7244878001af330ded420b3285acca056c520fa1f91c34740554ff0f9a125d016b179bf5248e00fcccaa5064f0d8f970b43494054cfcf18e0e095c7cb259fb08cac2da4103eec2a6a97b38cf623417d8ee417a8d4c8c89009ecfa23140bb70764b604a08fd584c3033b67e15c8e8d9958b67b8f2557b5c5a7befa959546ab26aede4c845c32d89e3fac00954771aa5969c91134778395af8c7180b0e282e18e2e412b5663cc260dedc16e484ee1c34985ccccedc53529938383f972fa9f822a25ac212c8bea6ed0e5acdf27dc3fbc9d4f594966d249e4f392f773706db15eb624d4ef2d8f2c77f1cfd7493f6f5d117a2800b3b4c020356361febfa03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"40dc6c2568588a255099ea741bede978c850f4e6f478aa2c9dc1e5386134e975","proof":"4649209098d066023a6734c59d056a1f0c5e6c3ec9d700a7867ff6b4134d3b44544b091c0e8f6b55858267b4b13276deda8d1109b4b7d41cbbc3c28d4b7e4e304226ea44831a8d8565a5e4d713f5675a03c46a82041645bd7eba98e7fa373c0c986be8ea840cf44b0f404b20a89867771010b64aa18e69a4e2ee937674ff7c4410f491b7848de7a87279a7883f4e13f203a7debce114ef99add0661d07b4bb0e19ed3612e76b445965695a728e02a07c77aa5cf8e46d119e94e44f6f9faca7050dda7b3dbb74e4a6bc0b4706dffdbf88ab4f781e8663bdabe6568c851960930c8ebcc1851e010965c98722b882408a891d6ac3c50864e3e3b13cf7856ceb267922de74047836c360d18dd824c4757175e775fe24162d5e2894364d8ca2ec7c3ee0dc5faa05b3bdcbdca072c27bf2139e49e535194692bd84016987b62adbe119d8d88da66c0449583e158cf4020746d4e222e44faf75d8859fcc26cad7d9ea1c4699e527903c364ffe1ce5019ce14a71a7582759512edf36cf23524f6ee03567389ced34d32bf1b94ad50d5fa8f7b6f265c9a850c71476bdc42e123e6f56c1430eb17682d577e809182e68baa8b5a91c743613db3f8438cbba2d74916dc40a3992e0163aaa7e9b2d46246f449bc5d91b668c4a98538489f3cf99809cf4065a7af21302d28679c4ed15876a2e263ebf700c9a41b60c4f849f1717bc41a2cbf56cf23c70ad030b82db859856ccbe2ef3a223a630018930bd525cbbc381ec36e300e8fbb4af1326a35599381b885c1968b56058679525b15059c4e598b5c791002d8a881d6e454a160f3955890296eb3137e07b59c02806719301a4367261cd3357e19f872836e861c560d60a6668ef1651143d8a8eb3614abb43151eea08af0907a751c03b4aa8ba8847c1cd8d9ec33da8e08497f95e7a6e74fb2822631c3f210e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1688f70c33bd34998621952980d19a520b4d9154615de5073b3eb53e01fd2831","proof":"b69ca9e347b6bf6f835582bbfda41f201bc6be20c2c070359d3770f1a4d8b755525c791200878db8e34b0c69525c7f79232ea9f87c86344ef8188077b5d14e767662044126253e2184c8342aed6943c31c8a01a05a6e2f6b11691772df15b32a567efcc8789e61420216096dfbaa7cfbb5e925deafd64a00f997bdc7665d9d1987834016652a81ed4c2426cdf51804d6c0e7cca24fefbe194a46b88463b0bf05969a3aba39c2770c52088c5fbdd793f1650c50c49f3b52f15a64a79740850907982020e5240cedf183f905ad5719b8c2cbe9751c62357c4233bd60044333710a3a72e22a4b04123a6ab78395ae9d9849c8f081a11d423714db59f3f79452bb1fd24ca8f8365ebdaeac05c4c00ecc817c3be9f7e97f305ee5186819f655a8c6292457860a28bf563bb17a982ac7eb06b7c6d097668b0c713442a49d12aae98527e608a30412ba748239a6bc1e5479e89f8567be5676949005afdfe316c9dd2f5620ad4edf3bc37f65fd971e3cdd5f81844b2c35dca0834841c85b78b78ca4fb56fce9ad8d66e72f1b8de7871d58d5913132604f668d1a5cdce76b2931ae13410eaad7aee508e85a242280e7c450fdf92a76de3e5444813a3298ad359fcd5ff4208c73a3791c178b9bacadf497dc405006a70480937dd839cdda8907e1a94806144eed9d1f92656a36adab7c94288507922157ed1f1627881982d1e7bc99219731ae1e6da3fd7722e4c519b4514f194c68f95c6c20e1e8a14e76d94726118f165af0714b58577de053630515f16e7a23a83924118aa2b3a2c744da2794e74a4c5886eaa6b7d011a82176bb6d8a865032f449f35ee29667e3365aa4cce0de6b917b6b8bfc390a68f70d5c58db0425a1e104298ce90a8c5e66f4a8b6af3abd52d7088cf3214a79388b53d973fe171bd2dbb9c936788e84d8c069cd1835fa95a11f02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"788555f89b337bef0f93b8a71b45dd09bf75cd332906aa0a704f3cb89fb9131b","proof":"eef26c080e43e167de79bd903fe4f2411e75b589c923af04ac0847513e93390c5c9eba426499b59b12a6dd08d02864f668eed2fe89904d3bf0a728cdd0e4657a941a253824a8783240fbc7df63dc94db99da064f82d42d726244889fc1c95965a250eadb9fac07d63fd5212938ad4c09517f0e76e9c20dead2d1d7b7e0d02e3d81f8a5440e86e02a5fa011f56d638da427e7696ebeca2b9b91cd70a8d9ef770dce00e093652fea2029d8bd48968e888617833b242c1d6b41b758aed38abd0705de81e859f6c4b9c8ed654a752b0ce3e512747d6a1e8d364efe96eb4b00c58507b223535e8a8cc82d715b00b1817959ad24167d14aa8e7a9361598cfb4ef207540e246f53380692216e15563f3e9f463ad673a99409d84afaeed3a9f2b5cfe442f6f0b54bf0c4189440ba0f827ca680400eeb13cc10d9a34a5fb97849698edc7f2c09e944ec55d0110924f9e8259da10ae167a0cccaab77ebea070225d6a2b974801fbf0d91a83776fe7e3a050d7b48942848e193c4c99e86afbbb4460f1ab0489218794602f028e65be9bb77cba872ae5a8d2945961feff6354a0d831961cf7d283bc06bbd762a59eb8f263882e7e184f0ed0c47734bf89e991ecdfb32e3fc7ef494a0fbe5ad47082742086228c0149ea810cc7f13bb2721e36b1a65ef93612c603b042e9fc7bc1e9911bfb1d6af07c6e70a55a606ff3f1f0c830d8c9a55703e02c805ce6da7491f1a99ec4990d98d543b138f06e5bcdf37136e459b17e2ab540cf0962c5f994297a85407708dce201fba849abb5c0bdc4262dd5c7c0b87051cf41239ba9b46dcb8b667f1dc3f206af583683c40947961cfb52eb781f4844d564a88a01efd55091c8d509ffe34fe83ec6409925e064ecd5ab598698040988e090700b22afa49b870c7106853e85baefd976cfcf03675fe857c059d0a385c300c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6aeda623bb7ab09b3f1796ef1dac8a8647faff9d1afd74579d5f1ba4d3505823","proof":"e40bfb1723e0d4f36f50b0035d00a80ff11c9590a3d98ade02d7369fa5c6070be89546fc27d1c2ddf5d71ae9bfdaac64750ddaa8567e97888a79c0a7b07ae063c05cb2c95027209e0efd35e1d82c9b6159d5a237399c00b5ff76ba10decff0294a24e832bebbd4526ab61c5c716f3e2384f292e2c3d7db7441183e12f52c0a099ad34e98b4f6aecf54252f21468f6df9d744bbe42a249d023e4650a87e926205dd6d711054e6a9eec37a232d667ff25c4c644995b53de2cfb3e5deb3e6ac290a7c14364717b2738af2bbf5c2ea52d53fb5a6481c7776f5399074ded233d5980a949c8b7e103d192abbc26a7db70720c50094b033df8d6e97f0d1ba45fe6eff761a6f2d3d759579dd43865cc9caad40f37b2018a47aa0a24a47a722028f31545a76755940555c6273eb3031b868f6d45fc5dd8fb69d37a406742dffe036c8091ff20b3ea94d6fca5f8a97278b9ed0c811d5bbe7b8b7aac0c754219663dc3be810b437f4462eeb2e27fdcf208ee10e613bd27dcd929f3bdc3b46987dc22299632264e09e0465d0f267d3b2398d40a7406c9470f064f59d8da948f8d33fa04e413e9458cf68ffb193707507081c08bebba254e1319b0e930ab52a12a53f0b34f05d1020112b4a8b1401968ae0d9e4d851a3be32f4026961bad754bdc1385526a92344c208fe78c57e66f2d156e39c1dd3679f38900d7e4cb089725be4a159e6ec4c30ae712f70b09ae4f3028fccd59ae29452b1e5ac0aee7434729d1218307e06303e848f1eec907aefd6ed30d938cf1eef243bb4fa9f8cb5c6b55875cc55ac242c7c052bcf5881e00d793aeb937f91eb634c76b0f060d00ad3c2366877f3834151f91fd8533683bae7ee091cd6b65c8b7691e3bef9b70a6daab80badef6aaa670c3f79cb0a3f726ed252d6acc59470dbdccadc82302309a555fce9234b15718502"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dc100ba66ede9aac7ea86981a69a14f12e8eab3baa90419fe819e8e96ddff02a","proof":"264ba3c4cb4a9ec097b51b975b544620b43cfc15bbed11c0fec4821e389c875168c4769a0f96c596949d8043465e5ddefb3e93a4dcd358c4803d5b1a910c134b9aff28b6b0088d7a3a23a15bfada8d86dd8e075f77fdd1727cf17760229acd5fd4de3ba7e86bc9456f1675a74992cd5bf9511655ac7eaf9cd69d83536bd8bf14a22873d40b2e52f85fa1475651f464683d584b6a8e53294e649c0cc6fe06e908139399469730f80edd1efdb662c1c4d0323afb7e651c039cfceebb23a0ac0c02eaea28c52327a62a8780bb88cbb8aa5a5f5416de5b575c1e8c04e7cb6c19e60d02b4ac5ceab57bb61db4f33f05d5d5b6b507a9c7ca81e21af84d09051e7cbe3e7c46fd995689c99ca68de637086355a4be7ee3f948f4015d91a055b71410950b580dd06e688ed413b47caeaf8e0d27cb4c8a2f7e8b8f419ea0bd53fb5c3fb44eec082a229e8c5a7e005f1131108e318c2770a9d7aa3b8cc7a089ad10ba0c9b59760a33b4d41d3bde9403657e63a88a9c9f2f52119f51878460753ddf942d3a343ae1f34f99a5fc0e7f607a5e16240fb34bfe6880e852cdd4885d29cd8f805810cab6b84bc53dda286a0faa920fb4695155c031a64fbfd699dd10eead2cc3b8408211324441d37604a4dbeef1b84469b8001d2d1beb3825049d0e1dbd76c29a31746c409a08200e7de94b4fbfe8fa46feb39e8e6771164bcb298f97c20ad2334c52a57a4eacac126de2c9f2aedd51b149c79b480b3a6c542ab7d4ff995faab65f0c573cf94ab446fbbd6a3e4775d0d5c793ac400a2907d4239bd07c55ea182b1fd603899cff74153c08c439ce4159ddf1fbd10b4bfef121a45662ec673cce645ae2f18dfe8a6b7930eeac4debcc95ff8fbab3ac8d439bfdfeb24244d0f017160f7050da81621c736dd73a0469bb41c3084885b539a4122c670d10ed7169f9e306"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d04a18a048624110f5da1c42731e5d2fb2fbb05ba22fbd95d1b0d29e23613a33","proof":"b840344486f3fb992d5f6feea7a2d782e5ce74d6256680a3a0604b44e3914a0476138e84f52bbf95bfe302fc9455c5f3cc2864a991f0f914b8cdd13ebb7d6422163e7ec01386e97fcc9c7794387d3ae72953ba595e9eeed87db6bebcada3795bbaade419936f0154ce09de9fb467b1a9a6075d5b93f962b2328f530967da867ed46d8dd0349bd18d707502b29afc4915e2189d5b069c95bd19b841d6136eff023e5a4b26baefcd0ef7cc69a2accf3ce85484d4092f658036cac971f8ddf6d705ab743e244850e8f6e3fed9477876f52b8dece8f32f87b9d573d291c7918a4c0bcaef109de5473a0debd75a0d55d8fbaa4b9c69d056b6399abf5593dbdc352a341852e1522001dd1074c8aca0b5a067642e1b33b07758b26b244713fc9a49322220b264ac285be5dc3f9f7d1f0c7465dab6cabfe4185faed5dadcf9fcd87dbf3f50c735fb19aff0fbe61746c879cb64b157c5d708ea3836e1543571ad7df281683c1979e10a4fe092d6aecf15e38701921625692d15a9b666e2035ed12364ad64c4afe027248cb27c16e165f9dfc725801093b2870e15efbd5a87027345e2136214efce23cf12c64ea141bbc162bdd887e7f82e50d5f3c6b6c70e418a9321e4283ab6e740bc4163553de273aaca86e0985f3e209d39614de9d5517651e893b279b6ad657441544b36a6e0cc6b2704bec907e9b8255c14d7e1ea17e4263f6946689afb0b595fa96f6e8912c685c2489bbbae32979f6f3d6ae9fb6a3cb25977c577b6b84479d71c72706f2307721c4d8f5e7df0e26ae248ee3b27840739f1fb7807fe7c1faa9720496b6fd0355b930d5f959c0395b5629c29b22bc6eb1baf75ce4f095425bb3abda35a0f382313fef8ff842232ee0ef0d1311fc50df897e78d660de8b7d41197903adc1438b82f8555ff7f7860506d1f81a79f80eae52d3bba3a0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f03d283bfa6d3284f5e0eb70bac4e855350c786a713494d681cebea72f8c2f3c","proof":"8e4cbc764e25eb9b041d693e0e85e40516f00082650888bbd78f766664f47403dc3f78ce2dd027dd342abb8c1b5293bf30b56a77ec74b79fed3d1d50c1dcf441e869f0bb913a8c62d070b6d27d73a37135ffea50682e365b905a3d790ca88f5e4067d7405ed5ef88b1057eaaf580621d483294b6c62949f66769634c5fd86e07dd5603d11ef5aaadd08976f7b546b71f16df0ae656cfdbd604e720bf5c5eac01b8811b3c02248103fe4b45f616d7478302c5ae1c297f53d5d672254d9850200d07240714a0802b9a7d9a8ab69cc86c4dce76a21f475031f42692bc2aa1e2820c7cc38999f701c487150e493e8a7e13deb62c55d73b5e508b1db75502ba1fad7768baad6521b7b93fbea3c419e68cac1c96c2ccb4d42181e58fff0f1f5ee9c66ed42386db77bcad29b96170df913a8e6c396b5a9e00e672614bfdea0ead4dff6f1c380aebe0564ce94e79db1b5f6de67d7d709b4806f0ea706a55dab91bb76d448284d77dee0fd45af955cf220848111c7256683fc25fae14110f1f851f2e2d74309d7eea9a14fed974d77ae164ee5f45f0f269d4a5a7cde52caf7b2e613c7d55542b26543b3bdd411e29984782da448b71c552e776b35266bd15027b2567421eaa5ca33fe6a1712be9d5816bf6c2b280b0434523d7634ab477a5b7eafac8162c72c11c6cef157fdc11336110d1f26483c736f57cab4082cabd50bf8d20e62b301863054d54050fceb98da21af66b47a2ab508baa9f63477b05a1e1423961ca2fcc3a7f42fbc374d88876b7d6a65b4270e167bdca3ed107b708e188d7b5383219047556661744bb85c3168241dcc926655fd76c7d2306376f79bc0476e79d2b2fd6dfeda2366cf533680eb99cdc975bad44b50abbb2eff97dcb8385e0255e2605b717d2c327d59e6f3ead7f2f31826da7019b9c7793a6f150017e85946c2d2108"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7c36b7963fba0604c5e7bd1123e08f655de54dc29bde818b4395150d6f6de817","proof":"9460b8addc8ab92c1ffe06c16eeaa1e918ae544e74963275b46f4c552cbea06740b7f835c70bdde06f2d996ebfaced18cc6ffd0ccfa3f039e47f88959e69b41486582087500e5c4214513ad444587877850948f9456fd37915979a1c7131ae1532f1e0f4d3abe61bbe5c49ef78bcb1e1317ac4b1cdc0279e4818cd4de5c0246177f03ac30623e755352a01f4c298a87ed4cf209c2c885b07d859d32a7fadb80818dd1118f1961b8cfb190e25f68fb6ee19d92c0eda25850abfc4f708f71d160987fd0ca5b3d946522dbfe5dd8a5a268d52af7c79f843df3f649c1bcad3c11e0f9ef587d60455f81eb17349ebaba243bc552198f8cfd9327a6bed4bcc2b929262785b4b94ef3df4ead33c3dd12fb81f4fa6b47cf16391b537a25e615bdce669560888ea848fea225bb61f4a43531108181071b1362171f20b5e70a84ce14e6c7d7e803fe07398d9c137cd771b71587a83bcbc913de8c7522e0c960dea1d92d57d10c9aa62ca7ad07286bead04177ffbe2f5cfc480a8cbcd157dadee0f382254233836301e6ddc554c6eda955071a752bca9264d0eb61f7aae8b68f299e88c2649ae33a0f280976f022d7d7af1913953dc7729d1a1d427399f5bba560d3a4bbd54b25ab69644ad89af62639b6ce9f8b71a5470762b5762be1604a6937d45316b2036f7537b7850e25d9104fe0c7db291d2e24863c233c99f27d87f93934341892ea6f0abd474c1d6075132b94f291cb71acdf311aae9d12c32e98ef25c92561777ea9b049668299436298e43070c34078ba3a5830db0847bc622875a3023f3217bc4aba1db32ec96d0b03f4ca1b62ab6d9602704c69c0f8bc219dfc9e74b86cb55171c9fdcd173f454c5c9e7862b9d9256d2138147aa9c00684ee48b5b3472380ddf6f7746f1b8823694926ceab29e670ae2574d1cba1ed33152c0264dbfa95606"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f0fda4db949b00aa198954444c7f2db1cee726a51dd458d2440dfb323d1a165e","proof":"2409da1f63d566e4013522e01e2ca5825f6d9e0c6fdbdb7f2c1ded86e16836140abe2c481c2329cb2419de9b0f7bd41de17ad1056ad37452f6652a429d2a6f3128dfebaa05cebe9d6d1894db20f4b13ed8f7d6a9c3e1c5c42fad6101486c1a5dbe48e0584539e47bd3692e824d111f47e94420c64e799a3bd02622d1765202276ceb55e5520adf8c34b9d1fca2eca68c6976bf6ac76ff61650c3a9c99c44440f253e3c31e43499c69a772b6abe861112b6a6068baa58e4754b483f90c2999e02d11911c801a80da39bcbf21c6d8e15d46d2df5898676ded38bb55d3fdc1ff000fe6be8f2b9c0299551b51ebe4a09bcde1dddc7baa7297c70ec55c950bd50022a222bd3acadc2019acd10f8f9dc827c74edde9ee55201e38410b6a4576521fd3ed00ec8a16beb46b2d5d6570b84291cdf4efd1c3ba02b6aac309804361085ee37946e21f73c6f510f2cb8eff5b4749233fc17aba486f89cd2c2fe7ac529edc0442ad9b50004e442936a6aee72978106e9499a5695721aabc96613d3329dafd6312cbedbe652699b6bec8b4800793b4609f100e59cdda906f64c7275c68bc4162ef6c777cdf3caa636e289386648c89128d9d0293ec38190af39555eef4361212cb866a6a83b703fe92d156f9be34b1630e7382775302e7c08bb8084bb13d7af119aeac3237c4feaf85583a59046092d5aeaac6be85d908aff16bd646440a03c600e7dc6ddd457e6eebe52b79232db89665065ba3977c9b5bfb9ba115d55b67d49aa904cbe9364bd2c4bfd9d591677c45d58e79ae117964ec23f14d95d75c39f2c5ccf62188c24660b5780ac60652f7e21251151188d2304a57a344cde2d97205d77c4a60d29017e9557a6f8b73cfd267e625cc4c763e95c584be6370934e6e40203f490bc2d5a9cc35c488011c3893bdfa872fdc3caee79e9dc6d14b6c685c408"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"82c5acd8472b291fe12dd3f889d3d171392bac4edc3c7c083c35ab18e870205b","proof":"e6e36b155eb075a0d5717b73f5733c2cc83d97883a8bd483b98767d1e118913a6400f457eaa2e1d2f416f063608e3f8b915dc0ed83431775f96648c141d81f170cd6fffe966fd9f4967efa478d7f00c30e443b0b36b0619b08eff2b7f4e50e12e4d7d536706e63aaca73f64b974be11b08ef31e8e0cf21b894c60e1f29f1bc172c4128a0a7f11ad91aa5d2e93ce40035a6a857430ddbf2664baf103e9329340cf1746a6e32763bdfdebbda4e1e89498f28a50b408892a62e3820942e6e258c05a8ab2dcae73b443f7398aea3822f80808f5083a19dad25a0fca238fa2c6b040e5acb2343a7427c56dbc1bd8cc0f6bb0a10236779764ffa0fa076a2ae8b089b7092c26189fd789cb73a3069ca4446d19d5a238837e534d059a4f01ada78daf2375ea775a90c28edd6c67dd69a9a47976716cc07f7031e90e67f0b2d4f8ec88e577efb1970fc4948557886ace42775f4fbaf5a9399c0ca82963a64994c416c11635e4841292efaadf24f8632fbf645b24a4ee1894aad075367fde73801104bb67870f23213096f619274c55490570ba85e8cd0c493a28a5665f468077ba585330a3cd3b186a64977f6355950657ce768a98441838c0b9472420872dd06665644707adce41158c964bf43eadcbef12e73a3ce0381408213d22034763143052d7e697645f3b23100040e6d669c17c8a62f9e94a0b8df03eba8fc5d9876394efabe4c60b59f24433aeda39e9ef4883e43e55ee39235f0658d622218ede217b462e5798200e5adf9a09074275e00bc2cb9e9df0f59dc667a51a6b6730645ca276a2e48989e752d247845ed051eb558f9b0b32b68b4bec41a1236f31ea33834b19dfb5efdea69beb4be50060175c009709d599fd36a311babeab9693975472cda1fbd08d243d24f174fdc628a0c6082fb6ef7bb2bd9b744f609034b458c3013db9eec0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d804d70c45dec348bb84cabbf07d9de5c51c19a8cf88ba8ace6d5db5f86b333c","proof":"badd3952846e4be06d9724e424a2978d468dcce55c075c558a2a9395b73539121cb62c5c2380f32d1c30875c263c1f5d753dccc1f8b83314471db09067e59428f45b2d75b437fa8fd97b265ee3730457f297cecefe8d388076b9984907eca16a7cd758358e417811082a67f9e363e28c4c16626146532a619f7567a9313eba169661cd4487e3a95848168ceab594dded2389d1b25df4ef3c1790407373bea703cefdd05a58d9e087fa8d69fd7888038d93caa2b9c595200660a2ca5e4e42dc0b373af33810b791b20ad0df541969e80cdb7b44fa1635b3072d2481febf36b00308a5d39f0b528c90a9d275da05173ca10b59e07f1ce080072487f7135106ea6cb28bdc2d030f90dc8b1e68e77e8e0159c6fa65147414eba3a191cf8d542d1d6d30bca1f17ec22e05f90054b328629f14ac99233ab581b9823bf17ab8268c637f3c9e07af784d7be365f4731c216ebb095986ebf5698984170fbe3486bafb512b14d79c8984f76aff55ba6693bddd4947870b0dd42f5684dc9f373660c19dc91178117d04a47811dccc42521925c15ec3b8ef4ae2db40da985bdd18221757904b023754e216fd3f85bbd852812d0b2ce30fc828fce1c20e73b53140c444c3af461421f87c739cc1ea5325f8f6b126365c67367f3e4652d63493672ff33cd90b1ed48d16f895a7829d85efb3eaa7298bf87e1d50d708e6b6c37511fb036d93437b407177be8f63d1b229ee0241216270ffcdf450eac026afaaf8b436c7d7f82e683c62af8e40619d599d0496b14f39f7483ad09287e1c37b3d903df3d8fa33cc2748ce9f57bb0c7a8b4b097624a63c2bce56ba0fb67411d2ef793527915cf56e3bb34a656556b1f45771becce3df21ed8f11a343b53f7fdde67cacabb30189260a3b571981456f50d8466d51190c6040ed7cb963afae845f538d58ea1582272d0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7a30b54f8d83f17ef94b42a27ddd904dd2c4a5debdbfa608bc422cc899b2b62b","proof":"14f8e6e897d0a91b3679ae11ea75d3bc937f14c0e604650d89b138fbd602952f8cf0dc54a26196cd94facde907edf1fd43449391c0d2d1867e935b407ea4f97de057a21633263bab3c05401704a368d332da22f038f75fed8f6f20d014db242df8dcc90059b49d8d0b267f6c72d7a16a3aec61c46da04edb6f370a6c09e9126b05bf2cc3d4f39bc433161d462b565c15af951a05b0051093cfb3fe650c071f0e7c10902abccec521d89b252ee51842f0dcfe60f1f5ab9905089008cf2089350a14a1b83ba28d6ef59e1597364efc7616e2e097c5c58f79d5a669f602a1fb3305cac97caeb09d4bfeb9357e0739a2a837e23b369355b6c2ae101bb01c30c024216695e1ded15c20d5d1eec0fd0dfa310bd74057dc0d7b907b5ef6d42f804d792efe18fcacf15ba0df2044c73db99b271ac77e96e838159b3250b44369c1556d7de6d90679d251ecc57112f57fdaf1d76de3bcc1e94c0ff560a683ab3945884c06b84221e0be4e3b55850ed5b7ac98c946ac0dced165ff1a8f8c20b6be0de67d3db2d10b96d21d60ee21413840b7c0bb6ef4318f594d0e93985f72a2fa36543642ac4ffe9dedb74002a6a85dc6460cfb12249489cbd2471235b06c3b2c2914cd68208527e47a8e8dc9c5793d4becc792c6017ea296094719e945f7b749204ff3021ccaf730bd2a0cff93ed4aee22ecdba9c4a0c95b4daf3cc6494307e22c4c0d1c0654f248b14ba415685d130bae8347a340041f7a30c39a5edbc5a797f66af27598ac8d4a8417ae7623fec987c82db4687d7e08de8b18ff8394a89a3a80adb24c4ee9d90e801d491968c8b2fdac0241e631b82fa16b4514a2276aa5fc51ee6c221aab1db820bae82a4b8d523ec6e5ac668da36fa4fcf7de50135e2463dfd1240eda3a807ec91eb0eae24723151a6ae91a3b287f396bd555eb006afde7cafd4403"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f4e6037418ad88c75eff7cf8f6839c3baa7a9466fb59022e68bd488310eaa132","proof":"96aa0f6ac920e1d46dae98a98c72d04837b3392433c163c894684da355b3430750766aebb2f211786ca0f5aeab93fa903585f3334b965c9eb5e09787faacc17f849fd4094f0ec0808a6a309665ec903458b626c8ec6e62ec7de51f46bdb8e80e386dfe4ab0f7315c57be222385d7728c71d11aa3523f35b91dbed513bf0a3146b19787f4009adee14563ad4b529bb8c1bf7c36c155620d5f648a58c38b183e038e142c75c0a371c4dbfb12a4cc91ba2da9ef8dc3c3f51085b8949c6d8930eb0f198539d3826190315cc01177752e9b24607ceefbb0254640332e48ffcf427d035295102e6df8d314b9de2bcf2da65348d79f925c466641e537e45416d52b67513084a77837a28652e1c8d775bcfc6048e12659f205a35dae7524bc7a68e31e0966a0785e5a4ce0ffedaffcadf83902c05f63fc213ab93f8e094d1bb0d653ae1022c58a6a75ee18ca5b8edaec92d84f6956e3954dfabd77c1246a3964a888890e24e9f657cbbe7c6ad8f2991cd865afdc8041b17dc8581cf7c32a872cc02d4b20cae647c6ea39f5d54b831408d5ede4d0f102060601b2624eba7e42fbc9b8ab5b1605bc5c490259f170dd03e5c1d3ccd93e4dd08dfa7ec70c38de49242193ec53260426fd62aa366369be85444d9a8b5277c406316bda616967dff82b7bdbf41c5a722727aa691fd391983d379f3c35052200e36045f01d42864d583eb9bceb5e9a05e52233e3c238e2b3a12fb267a8c811c7a4ff9c2fd87e55533830c0567906b470b609d6a194917e73bce71ada02baf319c70434d647a36d06156fd9c2f204929aed3ca0d04889fb276046d0c9a0d4e03e10f40a4e42a9c860aab08219b11c48f1ff03eec3ab436ff7d49ed05ac9a8b400d6e3302c5bbbdd539b302ea9250c7a4cc16225c2426ebbe4bcac5daf611cf30ad4970a41983c21dc5a070e3ab806"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cca1e6f0e5b2b5ddafceb13e1fca719cea9b72cff93f9919a0225854c288f868","proof":"a62e6f87d4de0c112679071246bc00a32a02cf565adaf381266df97bc950485c600adad50d708de76bad36421371daee2cb845bbe5bf471adbbecdbeb7e67711b4776b2e53acc3b5d1606085680b60d4dae71562fa63e22a41274284339a064004b16c7995099660287ef136a39dd8e8cea8b58295fb1d9388af5042dda3283f5e5f100341ca20f67c2abe07c80951a47b7316bd65104698785d9fdbd80284082eafca9ac8a353d758d616701fe44d2475fc4210f077849d0204059182c7a90dc81a3d64d2c4aa8fb4c55d5295f83606a723d915703cedd8c7c288a22c9a98009efbad9a54b900b32fd612d75be63b989d5c83a4e97cfe8e21fd9e8dc9094e3fc84d3f04ec8bff93ce572be76684dbbd881322991d91143c8a52a516655755587a4c149f3619aff3c0a0bbd83436d85b97628efdde4c8ba3fcb371703848aa6ee6ddde045b77f5c54a538dcfb90a02dc6efdb67b8e89851711f440d184886c39d432dd07ec4f32cce2517a1fb178aab9ff0909ac3fc3f5cecd744f9b59f0996ec8b75f6064352515d60cfff307a155424837219c7092e8ec16d202c6cba4ae6ce02d7d5476161da0277c69d209873de93af59878510de5495f908a015b55b51af48664564d36f0de54121ca850b3f06b5ea3c45b8e5565a9814cabdc065a523f1a98784da562948c96e8f0d734975d27f3792477901c6f62521c9ea9e00c8a1500c201d6a69455ef47bcb44df5cca7c05d5dd6ecebaf6ae055d43f2b9d3bc6571a1c3f6db30838f84b4a608e3ea59ea0b6d62f9b243f0322390faa2ddf5bf01e0409486336e18bf45459be77f786612821aaa984ebde4e90a8d63c38fddd3b0f35c22848c6da23715c8e0b5c7734063bbaafb51912bc02f2784e495ef7917a037dd3d62cb14d4c71c9882bef692f815d265676758e74136acb5ae089f9635108"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f04b5e129ffaf83c3a6583ff7d6d783e7bb12dbec05db518393cf747711ee41a","proof":"e0bfc90174ec788ab41133443839b45600a3aa1b0deb655128c91a4336b6cc61e401d50b2f2db484f8d6f09cf589be3d544ba91332b950ebcf397ea0bd31363d069ec7f680818dbf90407928a897355c0ac23aadf241570d164fc022fcc14e2e62d81883231159479d50d540abea73ce8f302677bbad4d257b8c77d22a83cd6d610c1b26c90f8b10ab65bbb1e3c44f17f3b43d8bf1fbf0e200662464c31f600cb03df8d0972dbc240be5225b572b7ac93bded083f002de43605a176320cf9303e3b290e6177c86c47505f789465a740368e171e35e1b425f54121c3c7434f2001c3d1c088d63cddd0f8a7f2caf8f53ef4aeb03f96fe9d1f0793473c442adb11336cc3533331a84284dbb913b435b871fecf945606408deb679121e4bb419a91da28e97cd11405ec6acfa82962d818d97f24315867ad8eeb85f4ccb520513dc3fd407106e982ec1fbb68e368a3ef15fd887ca1845ee16ba32e3f5fc4c68c26645beb07dffd0b0b97adab27769ba732ad2e623534abdfb1026fa0268436ab734387e0cf923647356f0bb9ebf4dc65c7c9d9fdb7ca7e950a2c6e818aeff7b3dd15aa62fa37899b75956fade66fe43a2366598f2d245eefe6aaf47c06464a37b3f5eb86b4857794066e2b1d47de58b255701104c8ab9f46e243addb49c8c1f0e0917a22a0d399925d3648aab73e7045abccda83dd8576863715e2aebddf9dc73b87074c050df8a5c22f5e6c3842bc7cd8c1527f21f54c3c421be46c515b0b4fe4c7d32551a39b1f83b8ba862c798c5fd511eb631ab523df58f9452ea1908aabc4f3684d15ebd6f4e9a81492481dda3a8e12bbd6d7c86dfb0c9b357bbe4d6306085145e5ca4cba4f419168394d47af3d00de2841ff5684dce39b7b4157394249a4500d5388730e542ddd5b26017057469bd91cde3ff24dace9a33672b5c79c444f00f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"eeae4d3f2488f890e4424b547e470bd33fd7902c0dfaedca7ab769b109698342","proof":"587aff54b91d47a82c1ce49cd04595f38cce6836bb5782bc87e894c7fe4e1203ec8a541e9843ac60214b01a6415bb36a7e919ba9632ecfaca73ad7681601f85316e19c2a52d96438b74924967ca30217cada915a4c5751cdaea1f743dad97f37b6f9dc4918aad97be24e6e65d9dd6ed6ab62a1be4ef684ddcf2ecd2c9709090201b1a927265d497fe4b5f01fc2ad534a3c6b53699e78a48a327fa46d22c3e008ab7f73ad49ba88e4527e340cf82f82671e7ec10042348ebfce170e4c917aa9004cb7681edf5d3046f4dca91fa8e062dbc9c6470fbf64dbe56d9a0a1022be9a05f84816afc07aaf0ad9e724484a47cbb52d4628700a75416214040a60cfba6c13709e5412b789d95b0768d815da7a6d1d4514b270eebb9891728d0f4866fb7b438018981af6292d5309a992fab825d03119c2eda53dcbe8890ff92ad44baba617349783acd07f8decc609cf20f069e2f030ec6ecde4f837de9543a278f0b7884c449744134bbf2419c82e449fbe171ffb55ba73c98376a7a57a8272c821c0df18da10ce4d7cc03f2b9efdc37f42349b50fe49fd61dce86bb8819acf336198a977a0c86d6c76deb3b294e877f8da729c6e15a14f2740c2cd08ad996773e3505465a6cea69fcbb054b18aa012cdef0f2c5154546c9cf7642fa11438f1ec1276cc522224d07c47250ddf9d7f86ca8ca2cec78ccc4df3a7432c42747eec860dc87b074e699a91b52d5d3775b26ee78b1cbc3c062dbbd740f46b4e67e1444bbbaa026f8a1c86e60acbdb6fdd6d6041f58b9929703fb4ed2ae2435d72f4a01afe7aa97e6a01df78826ebe949ada615c5c2f3f60b4a047e54cb79c4fc9fd190338b62a17430603bf5b763fedf23655b7daa66b117952a212b465506980dfcae8108a2008dab9b9d446263ceb202051234719dbc41784c32b7211a02af0c5a83b34f0df02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3298288e9044bae7494df3eeaafe7a7dd3caec3076e2b29cc83d6f4af1982d6c","proof":"28aee02e820c153cf813b4076e71d673474b373f4cc079a6ec4ceea9b65aad33767fd54ce3a7b7a89b5b7584fbec10b51657959073004c0a12a9b9418b0a8631d0993a6f6adf9dda350d79e1a6208410cfc2d80ddc84327c2c5897b5869c303a10f551a9729cabfd4424275ec10ea186d62e18e8cc8db94651d0abae97efa6324ec3217586479be9efeb0b02bdcf247327147750ff6310536efc9d365e242007e53eadc8e91502258cc7829eec88fd9218546fe1b1feeeaf0f23fb1609abce042f225cd0a65a89c435e824a9b4a902d69f964c8746e107641401671d80d00c06c430ec787e4fe8f29ccf36f85c1a5e75ac63be6645a0e0d0562f165ae5685c7752435e5ec2b7805284d0df8182eb1d55fa370f7a13db33f984b45a6d19129757fa50c19012f7892a542d11bf824df789657a08a7b31b961a9cdbf603ae301d3ff8ee6e475100df2238ebe167776ba5659e826ac91022279833365a9242ac6e4038c537d92c9a5035f013d97505240020bc3da9becb6318a5fc7227a6e1fdd4354e7f84a1b9e0336339c208adc5be36abcf1773b37b928ebbcdeab555ae3cbe75603875779cf2c3b84d80a91fb1e08d62b3ad93f143eb4eb6ec9f00efbbd8041d8e5324babc9180a19062bd56c4ec32b7e6d1c9b767e5ae18c3a7c03d307f60206e6e6b2f7a75ee3c2fd5502e3b47d96926347dfbdc32b10e613cf17a3ffda1298aa83ea6db2a54d0ab59ad64ff22e7ad22b18ddef22467c6ce388806441812202c18c1c5833ac931c6f7b25cf429f0d5776b2008ccfd83ae1614557707e2d826fac67979e7609c4c42091840f62591a46ee34f682b636488bf4447690850a80ed2c30d2443679ec404a62bc528dd205de9bf89650c14d8fb7e816f29aed7c20d1d9e32732a66f06ec9052855fda2ee1ba308a4d051456f7965cb2bfed2218807"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2234c34bbc36e649e96b324cb56306c9ced872592b05410d7c2bbbc0dd0bdf3a","proof":"d687d48950379e3db54f3fa75f910ed71f465a70fc5510748fec3cc2c12a7d76c09020b1e216bd8860fc7be850bd65e26adf99b5f38361fe3ae519ca08386931e471849a6390aa7cc2f8ce0cb594b011adfa6006fac5ae0740e0454fcd5ede60fa9743d4bf05fb1210c6787a427452a682851556ca61c3abae534d096a5bb35b7d01c3ccd8bd8da3ab630fb51fdd77bbc3536c9edf6affcf0973fafe5d6e410929aacb5eaaac35ddcad42c882ad88842fc45ad82fe9de39e10ad1af3dc4122024be865a2a0ff73389a8dbc456c62e56d2e423443868bb4770f033330ecda46027cc893d3578ec9e8f1bf6d072e43153bc1a1ab9510c654812c1676f76f1fa559409cacca2b453d8c1fbbbf239778539adfe9fe8bd2a2aa1e87b2429ce146d73cf6b0da6069cd3518ed5e0d135011630a51dad29ed90c4cfd4262fe21d8442835ea8dde18fff1769b8e9f4ab9091e6a10803d68b63a805fe297616207a108b1056c6dc7e08be051fb55a6de9edc26e4e469e3b74a578777ae9903b9831d27cf371ee9e0e7b12371d6fd00ad9aac0b60da69b78523a1198cf1ad25bd9660673f33348e5c41401449b8b60576f0f371c80ddc360b78668c9b809c603fd3499b844a5ec6702f36e74d9772853535202e538039ae6cd2d682a5ac033fefe0f99fe4444e9b6cfdfa6d9ed3a768c0a9216c9b6651f89922cd0aa52b27c0e40754fd442b82693df4842146301c7a4e82c4a9b0a99f276bf191dc6216d6b1fb203870a73094cdb0f1790de07711d39e502d02235cc7213fff83e689702b901bd3448a91037e73235fd1a1d6cbffb87b524a02212e4ab074c540490d2f9df226bdf3d5991ca0b14a1513eb6b72fcd8f83af50c56ded27645442717fdf78fc203ef7c3dbb033eb16019e119554adacaa001d75a21769173e42cbf90b029b0e00949298f9009"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"08a5696b27891ee1f2f9cc4355e297acec9eb2e21548c3a2b0ce51324d78da14","proof":"3aadfbdab3d3fadbcbcf69438c8d7146fd8670d9f7160770eb3fa92862b0436e866cc91ef40a079d419403103e033c0387b3b0b86313644b495b62896ee46e4df4e117949b597654854d8d40f1cef9d9d4431082cb7f33384b9d5ee15fa760113ad85f3cc6ccd25bdb7558be18d61466b939aef2cab48b1400d5daffb85838722c1a6465843aba95158734ec94d3353e8030c1222cfce91daf5648943e07b30b7362d859612dd673c38cccf3fe818582dafefaee6558c062fb3552b685a4ff0e901015f7e7bc2423d12467c203832719927b704fd6518ba9f91b4e0f780ceb0612aeb2f86fd80b99f8eb6126b8edd070cea91f99c182f108a479362b99b5184792190073e69cd7890e27a66324c10a0126bff8243e499436f7e40c0fbc6b4c099e54280e9287bad83f69ee420210009619c7285400ce9eba8995d5f8ad847b04f6a6df669d593519aba916d2d8d8805b85ae3a48788507994c39bf2e2fb6e26f72eb8c4f070eedf1d8793304bca74dd24dcb0decc2a1ab5c8304c96675cf4007b43272133b3bf3bde4e2456a32db1c44c65e204a66b89f84cba41fbd1cb62905a04a64316c653ad94f00138ef1e16b98503374034e69191ea1cccf06a5bcf805d25514bd1cde29dd3871092a7d8ba2c59ef3b38dd496608cc088d2c39bcac331c2645ec87b9a1c78209944573abdf3e225e506baa902af283e6ef2ca3f7439137eb3e3fc7d9c275ed02d44410fc50200e1aff9fadd6816e35ae35fc318542746185ca4a046ab812442950b11f1090d3f345cf6518b00a379ca202f68a5e6a05c68f60f2e075d41bed49c062f88d9c15b4c9be10617b3e2c2070dbed0dd94a5188c5827a33aab178a2314e64af3dbafc5c458477ccad4225d846a918df40193009911c8dde501b1e05202a3db96b67109c19730a34ce9701bd68e4313ff76eb09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0e5312e049ff740432016a3be8522dca0d1c18660d114c7c45d6c9f6b78bb05d","proof":"881d26869f911400d06be16c2496da71d8feb3385622168cc8e6f7db21b2c8010c0818f10ccacef34063f83f064cdd2bbbb8f845731cc1064a6b8535d1056b03b6c72ef4709186a3d29777b2a3d438f00eea6a89042eecfe9939ded6f63f2d4acacf85c0c6a4912c0d76e2f0acc8f1790a6449ba710f478f3bc5dc5a6e59147f7fb896a4882df6283e858cfd384dcdd4396a6bea1db8bd97c9ca6772794d8b0b56c62bc11b396f758a89ccf96d09d87f2c8e2327f1cd46766e8294178f9db2017b7b65dd32483ec2fc31a6c554e23ccf97cd929bb9fa45de7fbc515d4ba29e0c5027fca152cff181b31560df11628718b6092370ca2235ed85dcbd15fdefaa6ae2706dd851bafb27183e4643041b20af312b291b6ba5cfd53e5b033424ab6f22b42aff1cd0b794b4811c0ec2caff66d1a7a53ad0f02a49bdfe9b72b48db0cc38e0fc675614e66ccbd6b6fb38286c596d30f3cb44918adcb1f7a3df86b8e04f7aacd8bc23a3f2f738e3d5305ea5ab0c122f26b1c418f91b1f484a54815d8fcf4d90a11536f2cddaf31d2307e65fe7a6d031960b86e40bc1c969ea1b89a34b375da6de683d6a27e5f62b39533e4b47b99d89ec9f49acdf90ff68c0d1c8882f8e58c6825567f9d9e26d0942fe816cf7c07fbad249d29e4d133124bb14b2e1e4ef76267558e6f0e90592ddac180f90fec7f524feddc344faf79aae185f4c73491b171c0f7b1020c0b1ab27082e9f7161037d9e482feb0bdfc99043be1ff7975b8a3ac88eda5b7044668c1a09f2683e3a6ac5dbec22e953e91e8e3d4a5c2ff2db3217f69ca2b848a2efbf0aa09c942c6b6fc4b803294fbc849f635ea5ece2ffecd852d8f159e9aa2d6d1c403d01cd7651f1de40d86ad5450b0ce9ae515b31713d12039f15e69dc75cc7c11c804e61a6811f8ac1ca3f55838a68707e4667d1efac990b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9c50085ef83efa9e3f11845a569a604ac0711c4b1d0dd7bd4b794fae56c76363","proof":"4845e6b03de817e83638caddd7be589564fd480d5f1653b11031b6a296721c1d7ab02436f3fcf606210d13d18619cc7783cf0156fcfbbfd3fcad399a96fc9360a2e62376fcbee4c3001a3dda919eae50a590b5a71a4f1c9db4430894ce46223bb29521c64b3a2a4eef3394cd0864b349fa82872eb9bc93c504d0d56ee4cb260b86451887785d59251e2f5365295cb8b886c71fdd7246fbddb24f14b4ff1c1f09cb4b66f3d8a5d33c0073ba3541941606a59547a0cde7b6258f9db90ddc201d0ecc4a7d50b8261e8fdcd62d3fec8173d13109af5f208e7d9a8c20f61d47644b0542b2af50de5dab0f28255e8b0125dbe67dbd2e5487b7a4c1d75b74e64f016814406c3519419447a2581021286cb0d276e97989da0333a8cbfa8fb20316152c53bebde621bf6c2653052d0f94aa970c0bff2a96e7de779c0b7fac9e8a88c42a58287a481bd246447701917c1b3198f6f3fa115348f9a390c407e1eb96096d5a650cb08ffd43bcc5d7a4b8a58592eb0929de339ce0f770de62021b31d5e409061ca42c4ea60867a50f865b445138057addbc03113a372ab81f7f576ae48f5d675fec017c1b29b07c70233387af46388e1958d5a4d7e579f7ee6b4c8de50536d012c6a99adab3b7f6627525ef9b8cc923f92c29f646514086f615d92d0ccbe2212c00c977863a8066b134e8de3b3d9b865a5a57fb59d851b203efb88f057b471d7b58d0549700a250823f2203358959890d4f409c5060a0a1d5b7c3288c02f0f41cb4025abcb239c2850edb7012b1bad131cd74b83e5e44e7ecb283330fefd10665f4c59815b1d8c895f4c3d50f991abfffc144950d874bcb3457e6284b3f495a6f2f7186bb590d7736fd6268c552a1666abb461c293305e5b76fb3049a5f41a10c84177c2ca7b251020ab6d8b91632cc33684c4621b75e31968a2357726b8f8e06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6442558cea24f744260c2056b131a81351025603a88e76d1cba6a37559676107","proof":"506c141484d21566148828f628854de4218bb67af132f53b81a9f53d77d75a7866c5e4b847e68227287df46a34fd52c18c56df2e51b3885e19f00e071f890e54bca28fd8f682f1e143eab14396a82ad0ff7920ded1ce5c9f5a8d6ece3927a5723adbe6cccf0994cfa9bbc05ba9d504caa1adfad867050969f9782c092ce14a604765e2f2cd7b3bd7661c199236d436c1672378466ab1e8f7425e76aa0c6ebc020e09683cd5a1a62b2dccf2e09965be24c98b1f4be5b97d033a015b0f7da7960591d59985413357f013e357ac41afe509fdc80b46c976e848238741263bb93304ccf737484e1fba6fc18e6bd77fb43ddb1280268ba4aef0d7b30a04e554cd2b76848d2dafc23fa6000584850b6ef07e1cde9da50ff7db1df2b8da565edbc66f62bafcaaf3d08093b5c70958f4e2805105224e0b8876d2aeabfbb8e9a197f9aa504c1cc30c120c1aa61b7127712f7048dedd2935edb0e6072c5f18cac4b45ee92de49d5507a38322f2d43eb52ae20199b97827325537775ca0e69299e19376000362e9cd06ee9e82636f20998c358f40b249e61fbb8290da1e226fe1aeaffc6d2918641011533310ff6bc8a05b0b2f3a17ae39c87e050a22ef7d8f19f96bbd5a002e2cd0b3c802a283803a818c9de386cc06e5d424a91264f583cd2c2df2417f3a98bc565e316d7c5144140744668299deec3e7140fbbf03040f27e45cf0b4d31f2e7ae1612c366bf03365c0ad7982a8f3ad7a57876bcf3628223209cfacee9042086d523373a5c610b9a76d81423f96ef1b201c4dd2943716f400eee5c7a9041598a29122b1643341ebd964f93092756e0342c1d22aad478692a92d8920c3dd772b3abf2187d0bbd99f5ce75454663871237cb5a72a45b8f63755539248227d0fbbaabb73b77aedf5d5d73a486d7679840e8f176682b24a428f83145fe1d22504"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3a69423f33edbd3e611291774c0a806243cc36430abafc6c0e28140b3b66ea3b","proof":"9a48971fe9f980c8765135b51d5c4fe3242794eac8e3d7477877db20e411c00338029bd842b553458a1c5a96cf58291917e31017557356730aa24b3fa8e1e17d94b6525ae53ac15ab84f565faa1327657ec6946075ec7bacb981b76a5fc715475afd632fdb9c3341b1ae81c1762fe0226baaf8fad9c10e3ac71cb099ccbdb00ba55ae6c9b2f15d92cfdd9ad14fa1f5d35a80e2c5c8622e948e5a0ec58eee7206763e0a59d8a4be60db646220ee6236df736c48161f6011e482d855654ec0cf0b14b3f49b7e9d27f0b96ea57af375ed1ce4f765a71a79acdca74c9ea6b33d7906043f63f22e56ac21265130461940e79faa3c77cc84d8813c71125dcf7eff281e582d93a15448e21fb08c69f2bcdb13e8c0f75e1932e4d9962768f4108163c533ae77cbde4b1baa1458a4332568303e5d08f870845d4a2743be3cd8c341032d1570a5ad5323af1434f3c7108ec6812293a0bb4295132f13ec29d1ad570fa4ed2820d75997b4a71b8f20a986acce456bf519f20d6d025a1329041c5c4750d2325532f710b6093cec7cb6584db3e8296fd0b64c54889a593aeeb9008e2290269b6a4afc4b9d99ce92f9e0014096e6238c1956cd7ba9e0aa15aef0137fae7a2b8d1240821b123b0f3d3c07cb005ea56b58dfe7d2c7ea6c8f4f5e06b40031650b204b76e3be53ded250158d7a2cede79828c892be9fffd1ee36f9c3164ad29daf0d08fede6ff9ab72d444bf09fb46a6ae343eca88fb222ebf9392a26d9096678d595360865986b65fbd8d64ef684dcedabd719379e8ed2b7009306cd89f7d06696a6a38f872124a46e4a1279ff835c7b90cf2ddfbb9dfe4ce02d747bdee968691f03376f1476dd4a1cfd42ea940eacf0ad5d4d660c59a43a5e0f2bcf59d6186e8ff08cf81afbb3234ba15f043c2c0a0d26e46be7b202d8e3a2a6fb9cac3019df92e01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a263267f88763a1412d10026f1694e7960b505a07f6259e7258990d75ce41b2e","proof":"1a8642932cee7d57f46f95e2f51a66f1e871ad9756377fa9cbbdf8e81aea5f1ae221823c37463d77277ce0b9b89d6e306ddf9d05afe82ecb5b08f1663e894d58c85e23135289aaf66c244c85efb86616dda19827355d3dc21d5c88f6f5dc4b3a70cbffb9a177e2bed65f8add130ec3ffff200dda33d641661d31244445b6486f1aa0ac90b8a12cac76e2db5523dbe9f25e9602ab6de84d6ac2988284214bf00db446ebc75f7adb1ad0c58620b85a398842325417587bea34d18ebb67d148fc0c86ce195bed1dd106927d77b05a4f28cd207d4201ffb67e9caf9bce2f39befd0cb803f3d896aaa86c816866785406dcf8dd68bb4316c7a8e59ce5ccbd583c4769b863544f18b6fefd961ef46a38273fb0177c68b24b3732329612098cc54b4e55729eb49379137858c7b952fcf7819726e284353a2fb2b9c83cced5235062873830dff69a3e17e841e40f797c44b8f1772b2cda357276d13fc5275b45c97eac652e561fe1c05cb6469739018cf4beb40ae743052ebd0ae99492267ffac0b45f44485f341762ceb708fd21074d58760cd3969d360fa21f7c3c1fc61ab71df8bf5abc162e23f796ecd2a86f70edb64e0a7df98c59bbb489912bc27f961eef01174e76b0085bbbad032bfa2579c07c0e3c90d87ac5e4c5add76a407f111ead8cb7620c5010d9a8538b2d0ce2f3e5dafed720b6a7f9926fd14efc4ba43c4f78bd6a3d52bf769a12ec89bb765a97a25f2b25a789e92a5ee5b0c06718c2b4493953180c6cd39b51fdb47abdab282aec00cdf27d4c800b10bfdfdc5d03c527ffb8329535cc20bfd44484b9cd203271fd0a4bb961027b22f38b3351b4584bcee41338c006d8fc789ba2dbfe68ff8a7ca7a0eb57f2d26d1e6d152ff548af93ecdc119ff80e1e190e93e36ed89147179349982cfbe65ea68887b944ae684ed92a3910585c01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9a9c1274c86f55d72fecdb1b290a919c3e2225ea464a775c5fe370685cf0e544","proof":"d8142f2e6d2f120f9b4573bf39f87d48281fddbbcbb3a69b117842676872f2312c3c75bf0a36ca69580692607f0418729c07df333dc75e3b32416c4af6d0277e8ca7af6ec9a12ebdd7c312a108e12a7bbc6f09270544f8eee15173202eb3a37916798322b913f195ff3f34c0839bf0e03a1a8dfab1d6bda37794cd3e0110417b4dd102dcb938c80a15cb3db5570c2521a9a97f2850e9841b587ceb8571409d059331b60a43b91ba050f3d70dedc16355d7aa6c9051f92abf58548923e01fe60681b201dada49a825736e80ec3534080170859273365319bd8786aacba3fc4f0dc461ecfa36db6f55defdbae9cafda4ff8d19cd600b8dfff5a923fc7d5c74f41dae55a4bb41fae2549f803e846a4194ca8eeb8833322f734a7be97bf32b468d50d27f1f91e4c366d3f766e0c2a8663a930666bca040655d9cef0e22e994856023925352cce4c660ce3959e696b51094008934a46e4e031a0b32204cc776053066f8a0b2c8900ea3cdcb742b27efe22c3b7e48d53a9ed456220d40ea2f8943ff2ad2ae0751ad9daf845a376502a28b17cb1c1247db8deec3577eb8860ceed7aa6f8401773b9689101557f45016a2393286362000bbe56c9900d33ced5a4ae9ff6c881a6feb3073b71f4eb9735e6f194a693d50731cb62f0d71671a291f1c4aa85684ae6fa19960e625c15713277df13a4f342418e2df74ac175516ceb79f640320b27ac3ecbcdb239905de861f34101511acb8dc6f07502c234fe3a57cb2f46e715ec988a690565e9b37b7056a0df3079fd31970f24b6d8e07c25fec02aa4f67064099d81f92794709963ec5df645efcae0b390138aa5000108dacdc139cb17a0ef0f4f9f885b35a333a7e002a51686cdc65e308c26b4c8b04006404d974907c05934d13546c9b4d621c601c31b99d721e9ba8c1bbfd676501403a0b38c452520e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5058406350f1a4c7c821a86e5c0645f07f924d7c0cd039398f217634ad195761","proof":"b293ce98ebbb2da22e280670530c8e8792166fa3ab547d8b52d9d2148cbed12846c45f8ee38216952763aee052ede3d3224bc9816f236daa4b16e01aebf7357e88a46fdebde492dbcf65b3eaebf07eb7ca2f661a9c537e011c60e56f6bdf8a4d3a462d07eba8470bdd59819ba8685dedbd5bd6dae5fe7c031608c72224a9607c243568b7176a7e404141fe7936e534cb739d06e883a9228764f81555a260890cb408e5385f76b0b576017bcd6a69dd5a3077f01f26dde5fbbce35d92a68e260df1c872b31a4f7ecd96f70706f56d5f6c6a4f209ce06f63ec09bddca74b02ff097a1b7bba26d7cf844d35256ca565801f7fe163a402066eb31a4b69ca5491817568d51cf3e83e2964d29f347306a76a81c32e0aa7c8567ef4ebddd2101450d40ae2e764dc5de7a3e1c1e2c91ad783eb484288c71c46f4b37b974100929df1920c8c801a15549b7c7617130b75cfbf515bb3219098d197994b2461e3d4007f715850f94db8caaa049d5b70bba09954ae278d522288ca7c715a44510eb6e828c823b62bf83b18e292d923097c5dbd3a6b0aeb5ad86a93bbc04c0cdb43143a2e0b2ef4e7149cc6d2cb71aa993ac65b94e69ff429de36e2f7be295e6b57ddf2568373b0d30bd08ae3e4a190c1784de172e51ff450c82e221c9c46791d6d50f16c652d9a42facb7bcd024d02a2031fd5763a0361aa188f79c9702a919495cd69b5313bb6006885d1ed10c82022a98a269aa98b25890874c6ddbaf572485fbabac9962798939a9d2de289801c65840a05a03752600e67de116ffeeaae057e628dd3b82482da63c8096eef0e834fe89f7d36d20c841fb9b3c990456021f75c7dbb21f8574dd98090d99e850fd8968d1db0f52cb694029e07dc770cdd2c33d790130ff5006de9586778f9498ef2ad889cd4653561bb2d33bb6bd771b4f51f01e136f73f09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"569faeac2a68c35273ef5e18d66d890b29b0ecf5a6488a3b07842d6e40f79522","proof":"42987afdd83ea2f2765cf917da9acb75e38434f6f01235d5b0a4c2993c84e049c611311e0646015d670688b84f9216be9cbb7ded10bd7a3a294792b1a62d9b3068298096480fd3809fe6d691216b78c44d04cf9459974f919baf63c8db7d9756888ca213d11ccbe61c39b7a7373eb711ca03162ce995d2ee8632515a78bbfd229ace490913cf7f70abfd960a85b05b879056806473fd5b5a20c381241fe59104ee7f8577cc4a10a789b6c77569b63ef0e502d1963021b42747a9b988278d010ff586e74db21f247b7295e8c307062e4661ae808c4ea8ce64fba0b4a9b6a53308124634a8a0aecba0cbfb14f2e85e5bae298b0a5b56ad251226565ea81b90c513be7275255ba032b6dbeff32a77837b3d53436e3bcae6a842757bfa9808ca2d19cc999efa254c443728b7cba3c9b55d8263201e29334011c548997a9b780fcc4354cc24a8a7e96af1b7cdd06fb42726d9afdc6feaac9ae79a1ed3b13a26b91f220e77a7258eacb0dfa663dd9a0998b8acdfa63694de922faffff8654fe9fc1d61b22a7437020ad6a333514f0b52b43600625cd00d414e485c2c8b5e8ea581ee749aa7a3d13a44d92ae95b907a86d448fefd3289b203b7dae94f07c750c560236a58cfe92a8b1eaf879681e2d537efb4b6fd4fead4becf439eae654e674cae824500bc2230cb1fdd88e38884de366f5cdb973d73a3b7082c521d1391d1253ca72538747ef78a94b0e8150fe7baac160b7cb2b8cc7387c4de86b9c0b396d139cd176a80f0c7d41530c90d5746ffb6e9a3548126343114b0102b4972e84b5d0dcf0bd434e0429791cd3e6f350822e17f102bbefe37959c3f0ce12be60e44df89f57ec9f1358e8b2905f43c52854a857bbacc57562dbc1b9b8707663254664c1f690ee59db76e14004bbe5561af72b2849a65448eb21ead36847024d5722546dbaf09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d0b4d7c57257a7a131a172464c2e48fd0597f37013e1b5b7ddb4a1c55d08856d","proof":"2eefa1f9f87fd4fbd614633d0dbf48bd7b8629595a4f8b301a4b82186126aa77f60f6bd9cd8e3d625c9bf7dab6ddbf851e5d8cd4b10208bd610e3f5f8f64dd6ba8681ffa672ac096bfa14b92eeb4cbd956bd39fae6c4f9ce7681042076cac53cc4811e3148841d1f07d7b54a65636b018b9ea37cda3e5a69f2c96538b78439763c4822a5b80fa28dd7d3c6f1cc22d5f1567f72726c5caf358e4ead1868550d0d229d16d8cc9ad051f8da6c7da57e81022db3eea186b0f7feb46a8ea5071cf006904c844361568ebf0a5ee3acb33cad4cd64b9c04ac95dbe72f337e39e2799b07fc3693d0c54422f523a69190b4ac2a7cb3041c7a9592b04cd5ee08e1fa9e7f0ac0a877575c4ae2eff8ce41245e0a9e08a22bd35a61f7b7285f87572aa759d008fee73a67926b3b718a328c1f8341ae5ca92d857da19b1afd1008521f09a0b051906f80a40dad53a6a54d0f090ce938894f4daa60a905f56b866bc43f7572aa6ceab98736af883291a39611914ecbb2d70bce077f74e15ff5f5a6880eaf37aa37ae7fd4ceb67680ec3f510b6fad8e5a2004e089386b2f680fc9ffb608e147c64a1043c82f6a24fad7b40d14d6b05e9d502a1422919ac1c6bc4dbc1517f600a6472af3f6f632c8f2c077ed9cbbcbda53f64f2d8ceb3cedc33b6e4d925d96cf1d4f4a33457920205a2d50bcec8b765cd2bfbbb695c65e38ee077c30060d57bdbd5e88cbd7acc85bb601b8b372c54f446cbf6f7c0d320d4b83cce469c043adf6157e72b4d0ff6789a7580b44610b8c04f41ece9f92c660bded924a0a41f70667f34a1614d90603c8ca7b0b67c3db3b52021276782bf361e032f961ffaa8ac5d1361a57d5d41c8ef347fdceb6ce09ce929e9fc271f62537a196e234376f0b5f1dfa046b303dc71d55cb34d9c0532d9a020f99818410c8328394be084f9afa6be9d60d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ca5e93e2429fb78801c3e5557711cf8c980d81bc72e5e8f697523fd348674130","proof":"147bbbd3a6e6faf836a1772def4a9b5be254ad453247a7c47e4360bd2c078b13ae4b6a9ce91b28212da46692f99848a8eba0ca678aa9ce3cca5fccd679d64b5c42c88eec9af6742609ac976bcd2a49c65340385de5c527f7b5e9c37b605dfe1abc3bf3c7bdc6b412045ee848d8b0cd68ecc7dfc00d099cf0646a91976f43771540518e23a95207440d0e966291bf216b09db9e9f9cd0760769216b44dd0174062835cf1317658033b902b1c56ec9f1c90ce04425dac3a62f1361b18900f96e0cd2f1e2dc9886b523972cf1a852ecdd8df76fd8c387ea258c8bd514bc265aae05f6e3908946e581070585188238130a5557d18f89f51c2c4bf0a15c0e90c10a5730d7b0fdd99bbb7f74398b62e0f0ccfd77979e35727bef05d212d4c35675d85b9a7b93058111c0198e6b15ec633f2172631540a09d450a0f5e7319889b02ce19ec32ea33d9a2e5cd1e6e50db44d3497cd83bd390d7858db902b86cb652bba303521605d3db818e4b8f5b8ee82d4e40753e53964e4bad7dc68496243c9f2e63583ae75d66e4e4b66afc04bf16f974afe8231ce8d55f2a2d841dfb8d31e14e25531c20edb429a0f383958f5185249fa9a2432517cf7a7de53f3046a314f78afa6c98e8af3485f3069bb35de49ee05494423b2cb4c2c6e2a324da75883797ca7c1952ca6ba71e1e55940f9ac5ed287a1231ec50e01e59002ac0cee89d53d8c1f03de80418248ff7a51073d0c80ffe7696405c568ee3f5b630947d9e00dcaa9deb40de5197dcca0c5e274d70f2a0f596f9865957df0144baad1ace64ea928760a0784c2aceaf2850ae10b7d1f355d26e7408feb71be53dc96f1c76c40d17a28e0b5ac9047935baf210883ea02f9c018e960001e5c657b6976c3079a85adbe4fdb50e6d96eda3375bc1ba90308992a5fb77cbfdd19387479ae40e925f3c2e80d38b04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"64cc6f07d97be0251c2df32cf6838752e5eb62d8936ae33558e9e83f08849d6a","proof":"dce49144d0fdad63e28a40ffc5ef661fdaaaf3b3a60777d81ab659f69b4a8338a6b03f45943de83d5d0647ece0dca3881279e9b8ec7030cc573188d3102df743ac8ba4c340178205c89e98d165312b2acf31d2da4d2ec6872c167c8674aa7f651a5420d81aa54b67ea809ca29b1b9458d8986978edb29e306f6f2e2246d1266a2ca5bfb5036a8738a2d72d2703b86e95d4a22f0d973caf3a7f443f01bcafb20286800559f98755a5f99d7dfb05c3cff2b9769d4fda2c171627f5957b68819c050fd697a51d0198a7a94aaa5afd0f9689c9e072697540a3cb1ad33c963c48c90856025e36b821dbecf43ee75cc62cccd366d21717633cceeded269aad7094142a9e2965155b84cf4cf19ac5157ae32a3a9bb7362bbd3d1392ff8be35f2cbe6558286696515b21edba3f8a056f4e2883b6b9cfcef30100b6d261ad2adacee5761cda942983b6a5782ee07466062bf352101a204ebaa8d80830f64d627f30cabf4302cc316f07f0648a31e9356c8291dc342d0757da94774a05082ac3158089831d4a6d77d573bd4b9ddd24cb8df9de6654fa27cb3982835efd1db2291452441f481662762a78d06c96ecf0b984b51da0757e43a6d6461c0921f6e423a92543476c84d40124ff47cb3ae15d08dd95a4d029ac4bb33921570228de3949aa41c55148443151569ae309cbabfaaa57739835023bc58f6fc789a6f8a3e05a246e7c586a60c713b1fdd4d582997f7372a9ba8b83cc8ea512c0c74234b949919b4018812dd2a4144cb1b001917822b6b003862a04c6b3daa75491401fa2888133ad0a635d3aaba4350ba12c49efdcb97fd14cc4a57978c6fc03b567fceb2c9ad30486b11712f17bd272668dbcfbbccb82aef4e3253a17c362c31d870f8f12efe4d24fe609999036691848ad9f754094c56d00e3d7d054a0913f6d9b20792f969dcc5aab03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ba36bccab460e029e432074d5d24e63e6b85ed15244a0f959a6c407147dc142b","proof":"e852788f51ea44f3a9cd52f14ef2ccbc34f36a600a7bfee02b2673ff0e4bd16842d5c40c9375a55e5be0b73d1c602033b6694efa475d26505b1d55a06787c75c86a52079368b7e1b63a133d6e16c7c7c8c81c9d6056036df2b04a37ea2b0e96dcc352a11dd060cea4d808a45664fb5f48aabf30ebf07c0d3c92536ecf6934e12c9b5364699a26b11421114257fa141e3afd565761156e9c00be1cd9dfe32ad027fde2f7f123a32cfad48bf0d147bd82cb6d224b5a4b77f95f2fee77e9929800df1f2c379aaba479d0f6d88db0884ada5d711754346f544403e2b222e1687e705f2c2a797ba7af4ec77d85d6486e88297d099f481e45dca7c22bb1d6fc3e357067ea31f76d80b52c22d479b3237c6320009afe017c52d81afa7df782bbd1b7921c4dfad55ae5b5b24cae7757cadea6f35a25c49e184e19592ce9139ebae27e76e26513c598e591894fac8811d80d66b0294150dfe6074d4386f63e0d0eb803c6b469face373144e20e9dc4272475cd697c3ba8172b2aa75422572e3be0fac3a5d8a3dbf295d27daa2553a7f472aed03bc6a2a9b7f195b17d1f44eba3fb525d915ea87b22aae458b88b9b0e30942fecf4f533212434c3a61b9344c00c5823ee14e3c8b6847505f45515f1835775ceae0b8cedd4607394673d5561e4f8e3df79d6f58d6edd9f98969220bfa2ba9fe6e8618967262048d47fac1c8fb2066772a3e5f8ecb63d6e9bb8cb15797c308fd6c3a0edca15419f6b26b6a079cdfc8509d34158a99f024c6b95bb38219a8388265a8b22dd247ed7606f7cfca81b33b58f1c621229a12966ce59cdd926eb84af9d0370977fea4c8060c3f54e0b964e45ffb1e42d1c7f290de0f0d40a1b3ee160158b5ca625cfad4e5fdc7106bd500d64661ab01c433e30c2ff6d4f806f90ad1092037d5eab7fee4a7df2a81db5f2bc13a0a4607"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"18b2a986153db37dcf855f4540834ac7c25d0c6cc9d81eb9ee0e843a88d3e548","proof":"12c0231a2246585b2dc3d9fddb6b2c0ef41adf96820da898a997eca11ae4df5a7a5378c1fb5fcf05bbb92f25203e44122c5e34d52244b1cf1d01186193eda600343cdcbe95746837734a0966358b5a375ef07a746055cab633b919af77909535f8eca155c04ec9b772999e4b66976f0c3fcc3e4c88deb5a964a765e3d01ac4401b4eb98e7e1101aaa9de9517b577fd82a546f415df9b015b48682a15e3885a050704050f4de33ad41ed701ac8c23a38b2552e047f6d7f038b9737971ec55450f4ec13dae75ef50407450e99c6599819a7b9de27b1eabd0f210f7f63288ce5604da10e3fd95102a3dd21a6e3ff9b3549c52e460d5dcb75b1172f5e786787c1926b27ea4e2663f657780b17f407debcc0629c64d70120a7c15d1828136ccf2405c12d132f27219dfe457898dc0fa914eda95bd169c9f2b3f780ba6d92598f5b32d6077664e6e2f5dd359aed2e61f38efe0477d725033520f61a6642b0ecf221740be0edd41f6796cee505ab36ab9ff0d7fc7f182d2c05ef5509d9a7cbb2b91c03d50e3d9b56f1d0fce957afefc5c6a2c5ff6a04144a6b62ce54b782c371ab1d875283173bac0af901ab79c624d87b17673ba0348b20350cd5d1934aae002f75149129d36e47ca74697e5f46c360ab8b7733e210f13751927ae5c1ca59b46736e3d0644c2173a638ad74d8168b655a8e30f031f2b3216739689cd8ec9cdcee4e65d54c5d9f3fd1eaa5f5116aab7375338d634062b484ceb55e1903cc59a104e8c5526f2e66c57ea2972df7ecc09ad937938dcea56993e04e40728fa082095fa836548fbbc4737dde4e54c58ae9d8383e048d79e3af2e6cbb492398c8d9505881d3054b403c505b96d1f3ab2e59c4a39fd51a6327955484dde7dd4d50499be606d08855514e881c6b6df2303e9303c955cb948c3a68926cb1d0b4fc30cb5ecbf6100"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f66246e37eddc8d8281e820cb1e19cef25c752cbb4107e1938784df52ebc0871","proof":"a6a2d3a0b03ad8e72e593ce10d43599196013a6c61ad1b1981f1d86675821957fadbf2a9ebad41b74ed8892fa5e370f7c470963e3eb05127cf655ef7c19e706698b407fe198e03ec9ece09ce2fdbd41eb0ca88fcd6a398879e330a02bab1b54c5484811bef50387fe44c6ee0f0c26d868abe911b8c795fee57a8537b300b59236bcd21021507e40f18c9f860e5443b9e17289b166a25276594a4e7b0a9e7ef0f64166959bc2701ca30e60299d517faec92fd52c5b08a0b440ef7f9aac8146307fcc7f20771ccbb1b6853a9adda15bb094c853aa591d185d4039704f8493d790facd1f3985b6c13c9df932a085fc92ab4215305e15f75d0ec18e335f4d9dc5e6bf638f892f4016a6fdaadf8a00c1bc702cf1496d51ee155095f5d35fed1f95e279c4953f03823b8eda5e33294a8e4a5850535b50fc9a7170831fe1c1d63bc607da8098e8a609793819e15c9ab87947cf32be887ea555e2fe5c80e6b3beb9f0f3e22a6df88ad4e77878a38a3b365336816583e5f3f4d565a87481c75db7998323dfafc79474264ca6b2978332c83047e0a1b2967c0f51c3d0bf6e9c51d2cad6a50ce381e07d337fdfcf9d5fb94eb7562f134c514fc7564447558f1e1ae0e04d847c60188b8b3fee85d311c64ce898a72853fba55715b9eb252a6cbb5673b25c2078a880e89ea715263aaf084af525e4f9fe55e6529a4d9406a065c41fed29b6a6f080d58948e614a79f93635b7cb01367aecbd0f41afd953e1495882b9a14f5b3a664eabac4a7c283bbd469e2f30030e2c949b3a2a414033303359cf857189e731a08529a84eba28b7a3ca59a360d6b71559ed74f22f94e79fb604f8f2112e95277d9317c79ecf831b2ec8955de99747bb92019c0f5d8b5336be8fdfb692701403f520d163216b4d55165b286ed5ea62e455e0e6d5113e5dbd6b0bc660f3e05a07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3a37d692677f0fb0d12eb33d1d9ede8c65c75b39dc1100de41912bc82cb31306","proof":"94732e0f6580bc6f1cf0c13379248576dcf71b21ad1d7c0536776668ec02d03e86d2ff4cdd4ddf158d0ca05fc0e4ba7789462e74bca6a7726c36d62485e40d4eaa5eb62f96ff7276808ca6d8784cd3764ee1663da17a6644b23d3d3e2d19606d14de7e197adb1c1bae700aa052922fa2cb5e413e98464ee99e9a3f7544d26462a91b4191b1175bb5ba774319b94926f7f118e81fae79e0d9932b6af7cdb35a097323c1ca6c75a4297ab321693e2f1d4f325abf3091813e2acc508f5c351cfd0fa3d6cb83d46e40aa241f0fd711c327cecd24b6d3a59660727f41171a09c7a7094c67c4894a1e341806be13bf3d264e30b0307104e9c049b28319b3b3f113b4637c1ee4e3828d5a8e4c64d53e9b4b13b6db377f8d3085c0c3850647e8079f656b8e5fd14687f1ece0b6e7a894c2d9880f400538a72d9aed2f8cb2eab7bc000f230ce32efb3b86eb7d48b39a6999eb7e0441d908b40f4fb8faf591ebc07d38ed738288ae9a5a26f8db2990b01dcbb229cb55c29e9dd00330bad5b9bd48f011c24ab886afeffa480c490cfc677e1480d850d0e3dca949c26d8b110409c934cf50187cf0f38d621d673d4d1d8a4fda60e4f4aecfa3eed9df9c3ff12942a9324831401a750e03ec572f5b24a2c33ca85cadb473846054c41b3bd950e50b846990a174fa976d34db50b8dc3d9d546ce4ac0eb133e13d4ba6ac41a3ea7a19748808e362d4a8257a12f3d94882b1faabff1606f6ef1d4d4e1bceb16d96ee6b4a2c51ac5fa80e22f78ae1975affe73ae946fbb4a48f85814ab6734d51a4f400d5bff35d3ebcaa0dd44c867a626b3013cb65c6d6a620ffafc114a00adf3e579bfcb065195f5a7fee1e0018c7643c32278e2d34bd97f027ec35973241030bf3347a27094e0d66872201a70cbf9c9b7a56dfa884dbbde805c813396f0e72b36ccf064594da09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"148e2297b92194244d6ac7f4892e1335da12ff25c88524a866c8fb5e0de4f955","proof":"e6ed387e9a60764b0cfbfdec7f8f5583d8343654c1f55080f4a89c798bf0f1457440fb3a97f6ec59679610dd1d0ca8b91639efad75ea143340ea6143c29b5474424e999363911d6db25facffcf90ff3c8a5698e1377e61addf5da211114f4e5d98b890287331e8efaff0112b6795fcff3a14d568137adceaab12fd043d807c7f20db32d3672a187851c18f6d167bb1486612860cb19180b2fa71129c2063dc03f66a2b511ba33051ff73660527dbe544a5a1b656a6def29d3eb512a94a81d306d6b7ae14930eb28a5b6a91a1495ddd0d062d8d0f9d41f0d174f70fd1016bc50bb21e8dc14dd8ed0ecbcf738ec5af4a13658e71e228bf5df0c994531febb5cb57ea47716e0c2d3053d6d7c698bf21a939955f782639a3abc7a46a32909ca2c70b008927e728978d10745d52c06c901f93f8692367f813a34f644962cb7883927c4c9bbdb6f3f91757fdddeac730b1aa517e31e7d18b1c5aa961fab23588a25b47d6085fdef94fa1592f93859aa9033ee3821620c9e04a8b14c058516c4f29500c02189c5482c67a126a44d780d3a4e0409a9947ee54217be47d76f4de769edf5368ca874c099f30877a00a049648bb39a16db0573e7bb1bc7b5a1405e0ea9e20d942a6c774a4b662b4ece7b6c0b5628fe41e5d4bd53b3ed550847f0890cacce443ec98d54b6f0cc59b6b4827c0f768c6ec1c3ebff2fae03dd90eb74d2961ead08a29aab7f374673f8b3900ed46ef7bd71da4206fb17c9646175bd826bfc71ca3406a603bb1fb4d05add409240f76f7ead4b10a6432f92ed23d3b5606a7c674a67128b26a8a0ff425144e046d8d9e498ad7f2dbbce0b6076a4ce0826a5d377c74844609ebba9c800f86535c4314f1c15b681cbef1747210047897e65c9eb23730b99baec317d027090a0d045314c0e93a5877624b9579f65384cc0f5d1d25eed0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5461895ea382fc6bd8019b4fc34aff7aa11acdf548c53e25963af738c2ce0308","proof":"18922f9574442ecc10d9a05add56e595e1dd153592aff2fd8c553ff19b3b0f5a3eb9f8e2106b98c16b6080add7f9ecde1f80595ef1e830fe64b8bd7f732808231293f3207747d3fd91b24943c02cebbe6ae3a1031cd2bebe164f3a529ce7c812ee107497fc5dd67ac35fc225d8e287ff20e5e5d34ad1e908c4fc47ff1c12011015e373391b2497a19d410fb961d4685646699ef12349b99821a10bba747333048c85ceda4184d67921e24296b146752627db3945d8f93ac9e71e4124468bb40dbfb170f4d500047e4b4c63376865696e903cc5e2246e9a2fb0cf1c83ed7db700845b46a75ad91ca08345ba92d89b853129d9fbc303029d516e40d2da500c516404efb78063f03ab9bab96bf54e9185e92ff35b6f49966044b9826f645732e1649e457943ff8fae40d0eb898b92397b145b48f988a590c827c836e65a39c4527bfa7fae06f0a37f873d8af44c0bba55b7ccc22d011bff468a8c901df4cdaa8c6e9cfb066081204702491ff9a42a0ce9665efb0ceed49ad256423b699ab8ad987c9aaa02a6dd8b3cc8882262b8d2690100ed598cc95632a25f807be535a43d8e25f4ae49a3573293a7ef56e6916987a2919668f0c976eb64a825bec1552a77a81d16c6113bd679a08bdec71c802097f0df4d23ca36ed4a407165db27f2fa97c379f43374834e458ff1aa9cfc817bd52cd704bd67d83762898bd6e461f36d48b8725c2f5be745a07fdedf5fc90a6a51efb856c8a6881bae1a51a8d35ded1a799c0dd896607e10f32844b765f75cf2bea3fc00638c80a49aece007c5e86dba980d139696ce6d0ccc04471cec564c64e6f0850041233ed89abfe24133a8ef0d4e45303be5b566ad5fc6070d5297589fb90b8a5c50508298cd2b7c35b69604ffad0c0b22fc002b14f43e27034b00cb9c04b75befd8d9febd573fd48069ef348eeee807"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5eb94e89594c3608e731f7f64afa876421937cff871d6d8617d0bc560be07c56","proof":"5636f20ff429389f4ae4c5d1a0b99bde2bd8b2ab465b148e34fbb66af5405b2848927f54f964e124bbddc072dd55fb9a9b0501126bef547bff23a2816326937dd25b1aeb9ba18828ec4b154a9d226dce806a76684113486abb2dc52e15d5b75ff6d6097a710013fad3cf6bfd56b67cdf972252848e8a0a85eda35d1f860a36434de4dc540d6458b2d0736e21f844742519a9fc70327c7f3886296c1810d54c0fc0da2d8706200ddf5f6181aa6b5df260285cebc066af313539c774f7f4a31d02dc1ab3d95d356bf9b227ed6614b3b2c8f7b00f11f4e2f3e59e8ba1c057a76401acd38720a11aad0c4a434d04619577e64f41b5a6ef66e910690da31bcd64c4197a992a5831f5c2e4a34674d4a948b2208e0903e9362cfc6e6f80212ef58c6a5fe2f2000177493120e24aa96c6a75934249f2491f7ece158a58ce430e12d55b2dc432f1fedba06c437342514778fdccce3034a273ef67cd483b6e733eff3cb14ff49f598406077d8215c9673d9f770132a22c8798fb08e378acc2ea2475d760077e90a8cb1040c4f0962c2733c801c69e871a7b8bedb8c9b87d8dd4f16485737e64f8bb6af05c25d60771b58bf813be49611c0a8282bb0f45cec74591bf6446584ef3eb392f0d1d7e141fe0978d16d8f2f80668ee2138a30cbbfa8a65acfbdf793a266b366a2a485efe79eaf4ded01f3a8cf934e09e2e6c04068eb86b7828401568f7d4714c570ef3ef381ff8c7fdf77fad2136f0d53446b6ef72509246aa1119da008c3d6d4ebc108183d970e686a82aa41580c61685fe335c957ca47247b80d1841cfdb23e5a6ed2507e0b4462b051bf75672cb4d2bea1d60a4fa31be5e8166b5eff8eb913a0db62ea4ece84a9366f336182654d1a6df1ed6736019c94717049bb44108047a4824dde321ecbb521700f5bdb8bdbc09a56aeda5d32008b48c0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1259d6ad0dcb017d167f43e1209b95fdb6ab8f4df5ae558b69bbb70dda0af617","proof":"165c4057d1c0d980c28f5ed7b64324a544befd0325ef7cf03780bb891a72f96bf62767441866dc12bb46b1f4b0f6107adb8cb779a5355755a689525925b0825d36bfae7598b61c7f406e13691f4d89ec8d060d5f40c7702cbd7e0e756ac42862f630dad1cbd052ed33525868195e73be0e863bfcc67710645ee2a57d5801fd60a9d48f91812d064ea88e478971e082caa5e6c32e4d5808ffce36e8e2ee05d60f3857577a2678e01713af1b65033825031cb886c6f4f67c12663a40dd24ac9e04e5840136c3f744cee1182786665fdd993239a501e49e2e023e2af107fd6e6e04606c9709137338bbf2692bfe7b22a768e202aa3c71561183455552df777b1601469dac36c913552c3a9b42ebc5c6695ede29c16522b8d6633c22dfdd3d7a1f7468f5e0b84cb9b2ece196d200429c0e6d72c3b73c7668aef2083180f1c9cc50125c4c116a2f7a085503eb753d3c2dffdb03c60d9769aac84b73e08a8d4df2350384f7c2acb0560892f4f09943273ab6f45e90f99a1b758a7dfd070dc5632ec92b0ae265750f7990a657371e78f2a846ff6060b99b0b50c585eff81c8d2bc2520298f6eeecb39aa022e21d897b8e26d75a1fec8ad7d0f06620980524fa4434cb0e2abb31de8ef94715a2a40234b720786c3dfa652556e3d3dcc366ca23737eeb3c9a85aad14581477ec04dce68722ee4d59f02e73390649334bd846ba5fe1be00f383a21f5c3cedbab5d8cae1243fdbdadae6c1bc9526c6d83009143b3d3981e5f06f8c746257600dd0c85b0e8ecd6291f2cd77ec04397a88985e7553a40c93730de1ae14f4ffdafdf34a509668e05e706c07968c3b2925e0f872380a979139c2540ed33d24bd3b2e0bacdae4b7588d9075744d4a70a25d880dacc66ff3c87b200608a069cafd9792d11d8df3dc0099a05b590d4a9e9072b6dc3ce154440c3e703"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d233397e00b023ed2cd000990071bed4811e0e1d041699c002bc6ef5e5b9146d","proof":"04d8fffc6ebc7e9f638339749039223a769ce83a26bb719932c96e3460b955619eaf8fa5f01113a01184f81528c89aeac4f92ec27928bad7a439aad6f64f0051664d5c810d9814bee24b33312ce702f078384fa07451d246b5d49d186985e738d262f1943f1c4c58ce93c9d5679f262bf48820a816163707e2aad5f263f2b0095717b42de427de701e0282d054cbd8f7238bfb2299d08a0cca65b444b870e206fbd124fd25f8c6f012e5ac9898542dba2b14576858910ce550dd1e7a75097c0a856927f2951e81b0238a5e5d48d789441cc22da98897687be7272e2201c8110b323f5ac5a5da3b768e345b22c0eadb00c6a0bcf37ff6a5e8362bcc1bab9e5763da0989393fc4ac1640399d8782cfe5957cf4bd910206bd70cc56aadd929ae17b1c36cf377fbaa43341e532a09e72ad0e6cac8a1792beaf0457effb42b4805925c01b233635c769a4df6113151b4dc0389469a436ae00fa937f401d593833630992708e34c8cd238c11a908211d8d14b63dff5e64fd19cf54b6e3bad172091b11140a2f9aef20aaffa05ab80fc56905a6c28ef9da2b4e2ce58bbff605fdefda52ca2efdfc85b3511002779f080ecf221da144a1c8e366440a38a5f837c4eaf879b4cfdd9ac083d39ee1cad4c082e62dee2d5a6b34f9f63e94a4fc900cc0eccd72502fb3c5938a13d2f84fa570deb06c59406f74d26bd3c43608de131c2ffca43b84c293c2ff42939194b9e9f053607b3cfa35ff6112b9d93781a90a7c6d6ae4133cc5da9e489908993c07f42162235f1c31ee652e64b3615b20376d478166d47a3e9f262605f7abb06bf1ab07cb172f819d5406afc57133938e82fb9c5a7b7762aaad70791fd05a8bb7acce3677b41dd10a249532548c32276b889cc2b85fe90049879937d53c548d4d6b860a4c29d8d673336fe889c7b01fb624c2f518b8de00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b60f0b0cc0e170c0d755c203029ebd6fbceb9115961c75c2e76e3a60577b1871","proof":"6057a858763929dac3245cb942f55e841580e654fbeeb949899322badb704d354e168d4896a233c5cf4072877b61fd1668e8390e3ffcaa850105ad89171bf4047adc8212e097801c52e0ac17c585d63623ab26741ae5d815a0248a62e42dd033e61cafdea9ec97f48f3645fe4a724342499c2632859181fbaaeb3ee9324716581eac0a1d3bdbaf9161c05d00112ae23f29b7f7858417ad073363069aaffbce0e47cfb624b067d42a9017dfc7898730df44372297bfa14d3f9541dd064f2e3e009cde3dc29cb454a0f074cb0d170e6239705b1909fc3e7db6bc20feec4cc3730ee0a7d56a264cd658298f345dcd9c89c4eae7229f93cf79e5d84fe8e40929632380326fc369efb8af77eb4e90cdf46791ae5ab1b1080104fb3f385176ea527751022e77a8e80e7f0058d1fa12e4572015fbf27d00e345f186710319360026dd0e8ad67b147f04e4d91d6adba08a7d27cef186d1097044242c3b3c42f2b3597107deec8f12af77f14f0eefc61db3e608603593cbc5dbd87906ebd8130dd8158b4634245fc1d76e981b7a460b9d63ca625f4ac3886387c3bf558efdeab505676a1a0e5c10454b748de23cc6ed640682b68b254cee1e7a536aa4b2947d97ae23af7a8a84db4bdbf0be2d7db67f5ca5e7780d4da291f27fa43054445f688c24fa4c4880f490ffc91cba867e93e9b6470d7f194c77ff5c8fea1de8ce37995994c569467ab71f9bb1ee41d5c1c956826fe0ab47688410e864baa15e3b4e4e75364ef9119481470b4abb64e877f3b90f07eb9c1ebb6193c02fa034c4476c265157dfc24b64e54ee475ee967177f33ec5153d2d242325cda8e2dc541ecbbc43e5c44f0560b0350b5262873994c15af291694ab1759d8f3978f78fd0b3a10d792d0f81080abf505fc6c3e4d157bbf03f356dcda639c819b7540cf1bd2a3ed39e9463fca20d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e2f9c9156078990ed0cb436a1c263189dac082ef80f16394016db6e59f0b4605","proof":"fe2aa39ed527cf7c5fe162859bb5be0cdf3cb64c4f8c1bfa356fcb4eea8ea37b56e7b1e2f5b039e30d7d51bd350b36894dc3167d7d14a248c8bef9e170b8fd4c14294f5016ff7830c03ea6c3acc6588eec947b5149916e0244bf6285454a773c52a58db70bf4a96b32a96147e6b3cc0abfd0f0011ea7c92a88b2288185c45c696c33f3c948f715df0b9f3faf5b6e7b48cb26797f74447dae1d8f9d0eac31510ef810c3bafbcf5325c5998e9f3d5aa0e9d46b7c730956d52b1ee8f5d6ef37cb0b758827ed7b1d71dec9807c484b17e052a55ae8f1b58563db03fb275b6751b8098a92d689ae0fdacbff7c1f1270b573bc52e16945e205b6891eaea5941c440822228cce943cca02a0efc8a903045d8e1a87dba2aff2c79c229dcfdef3217118516494384fceb09bdb6a533a26c352ea4f8f1416e1534c67f0f1955033e449772a90af66785421a458833385054949a638eb39d773b7368df4d44c13620d6a112e6ebf0d49d5490e5f6461a5e9002630b4f9c7b1b0e0c06180f55733f22ca4710c72e2e54a3c81dd95ba3ca157ba518282407b18bd6be6413b0d0cd6d3ec24db31be5d50920ca345ffd69aa9d0b14b4226321f816c6083a5f867dcfa50abc3aa3e2c15cfed3bd6356a1ff3e3b99f423120a55c39e45bce28e38ad51d2c108ee942d0422466f89a5e054d02bdc5f3f06729b5994a8947b455160822634f0920d267e4b11f5be475c77dcb037d4b315b8871255234f3f4b469c9f12f6ce8e46c9a1622df243c63a0e52c6b5b4aaad668d8c9064bbf20672a4902da192e522d6e18643e0f163ade3ed93bc9739829057e2b451985ed542257924e5a0d81672bc86a377eff80fc3695850f38830deb927aba9b677899f138f55322c95eb7ae7343ca0b53456e5b4ed1436d7fc8b3bcab54e3fd54adcbdfa1d459bba6c477db021a040e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0ec671494ba7e8c0bfcb8c3029e97aa2db49467030a07a346c07343e0a20916b","proof":"d62a163acf78b014ec2f08e24890c1b8ade924ecdd4b38f41a964567a683b739a40c4d1ab493cd8e5de8f2c7c6f0ddab1a91eb34a201a87ce91da22b676a2d73f8c1e281ee5e05a3b749b6f0dd7857e8362429da55af6fd87fcd7a15acb4123b10c76c9d755ae780290e89217921ecdbae01e961a5e441c5f49323439d65ea3c234115e2d70f78d7208deea78d27af4bd1823b5c47201e8f3adea671925f2d0e3c922d8c974abd1bb91cfbc6239cf9abf572aeb9cf64558640455cf21d2a7e0a201dac31e8e83be736ac240a4efcf6ddd3c0c16f7161bbd5f0f86f6c5b2ad3016626b9ab3196e6f4bffb4c036240e1c94f16b4e37afddf899e2ef755c861df0ed47eaaaf25e2ba2a3619d24c427c7f892c1168326cdec7eba141002d5185e73016a812e775f4544750e531c324b0304a61fc5f48014e96f04be2ea4b3df5cd159eb4afe85a00d2e41a1beaed98c3fdc6775b489116db9f04b7a6dcf4eb5ac33e6e62f99ac4fd6ae3d71402dc769253a7efef8b32c8964e385fb4ceeec1fceb6448e62ed1623d0ff7a88edcf5e89707358bc5193d19c4d6882b1d1e0a40beb22b220a44b0bfec06b78c71c8357f3a915f2206a289a22f9562843f3e2e760eb66e7e577167ec6b3ee2f98c9edb524047ea4e7574ce58735f1bdd981a40b7d34f2c0689b4ce7897f36f23a7baaafb271efa662f37fb5ca5f8f32b8716ea900ef21510739166e7f671558773b56782deaf1c94c72c4eed2b6cbb3217f044c713b018ecbe21ead199130ad587119d50b48d5661e3c4a50a4f6052df573be2e7f8cf3170a77ced325bda6f8fc72ebb2dfa1979610ae07b0758f661feca5a4c310dbe7a8c3dfcb0692f16f4dff42ab4aaaf5b14360a3496f37971f188a3a267957c58078ade4dc578a0fbdbc69228673df3516b00b4a28c6f2821c7a5f4fb619b892507"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"303142e1dcdab6179cd247a0159c58f8c43b69993b606873120ab1964f225d3c","proof":"b2ded497460261cdcbafa8741c1b2eac6c9f753393e2596b00d950ce20f49050d0b6693e0c6faed983e2a618f89d7152662db96489504b7f6c7e008969a10f71fcc0ffcd2dd3ffedf1bb70c5c529e8a7609dfb38b74d51fcdd5bb0ac14a0b46aec2fa6fb3b3eab9a89ca40ed74a1d892e86496d17825de0fcb4ce3384fa3c872808d65af7f5a27d993b945dd9a25ce16617d8ffe4da5b637ecef5ec682c4820e80e8b0c8321d5b295235035922cae488d790723f9bdb41fbae7fe8a65887ce06123ea41dfdf06a73df2c966bc8c0739529f02ef1e9c46ec72044a79f95b5e0089631247deab6deb3050d6e2cb5eb08a84980bb7e4ca3cbbfd031ec37f696fa3df0cc7575885727ff8e135e44797ad19d13faa104f92c21168a62125b3a5a0230165454648cb39ab64835a4e3964c0ff5f984b6553434003aa1841e65fd371939a08f2e18f050bd929f41f931aad2e052363fc72b5e71646a03af8fd4f71b2b21b6e3403a47ed70adda6eac756b79dc9cf8b3b21e8e8848e2a58a2c1e68ffc025f278b429e9bfd8c7769f44ebe217504c22540b12708ecb7fd8c1c72058085462528d5f2eeb19f2967c62ee064057a324a6155d9c3ab3bbda3fe3b10dee37730494bf4ad8558c2fd33a84ede377038a5d1ccfe56265c175fc3e2b911fe392f172664dbb3e164448a8d01adc6b786985afc375b1ffc33dc825759099b834e888592c8835987fa974fc95fe41741cd0c5b33a0d1c18a0532a1b79769967dbf8ff025c7debf82c878158aa33931ac03b121ed93069b197c8967bcb16314c496ba05de86d7fd23ed346cf3b7d0ac40e6e44146057ae6e5ffe8c1a549941b094c72d7e625fcdf71c63a57192e4b1cc33b4ed27662b0261d44b79681879e8fae047210769ed66332994d287e55bde588ddb1767ded3831e38a37c89469f17c0b6a4790a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a845c9a5d6e835adab03dafe956d2c120de3be1f9651ac286dec3220e120c71a","proof":"fa7f31dbfb380bff058b62a04f3a3749205ba8e7b1ad6e6212ec68e8a1c47a65e097476eac86b05449e02cc59f96544dfa30eb5edcdc4203e4c5bfed8149e04976bd8a728fbd456e5930d6f8ad5094655ac3f2d8c27bbcf1046aacd5ecd79d0e9ae459b8e7629826d0be1d3d5430a6b7b2a325365bd534bc0b4c93bd67046241dad81ff96f3fcf811881d9976b4d04fff53046ca02a744c054da6d755d4ba20189927660ed9be9e03dfdb60a77fbf65b8031963014a6bc771e2ab6fcd993eb04deaca047a9e0965ade826367892953b4395925171edcd454e7214c4e72722109b2535ea18636c795cc2cd42172f8e76b5540fe17a870d2317a68f4510d400c1e0a2a48e127af87edf794f21556d10e4f9239017a0ec369e732990175fbd6627a1e64bb0feafca8a1085df6ca2b4a36a438459733bdefbba3691f5a0aebe49a48ec7bcdf49c7d22f77837e9077fd4bae7da609881308e490b806c802bbb7adf2c723d124387710ae82b95f7da1709e1089a8eb4b955a509af720ce001d02dd945b493c8196adf766ee163ec99f0064d98c4be5ad944c5f5e11933c9f973afce0bc08d5d8729347c0f6e1b105211cb8ec842035e1ba10175910955ba279d50d16c607cc4e8f3a7200c1ed54635a4d6d0df578e39d054ad29453e4d59f662eafe3502a844f4d1872add62116c43ce0ea0600a889b67a6c7c0593ceb10e2602554280ef143bb5b2357fe7ab1e08cf78a037d8fdb2adc97800c30310a1d431feda027a01ea7a909fe1523b248937c5f563b2e0ec018bbb87b14d701c763c33285b2779297e624d5dda338f994b6ff53f6895d1f6dd3fd654442b78fe628af39684f7be610724f004fbf1ac598dc281fa470cb25d43a2c13190dceb5ecf1541d0a690ccffe86a86398ad410988e611dab5e2923b671cbca9e40e01b03600245ad8d804"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5adf6c2c0179ed86631d4a59c631022f1d9dbfdc6e20985ec535796eff2b430a","proof":"aae6f9754b6c29c3273e5b92881f6f1f8ad7c5e8d253f4b0a57d9b81fd353202e8385060cfbe02869535ee7ce51a491f38c4989dfeec8f015af5ca68f736211c02f2e96fb7b21cc7a29b6551ea9308395bee6922862b7d568eb62ace98c3cb32eac7c773a82dacfe53c873914d7f3249f69ef865ca14328e322f46df8504a81207b931fd04be2f9f57d2bc0af783cbdc3b2724d5abebef702a489bb070f1ae06616ca324b03a634946b8d32b2dff7978f1297b5ae1e9dd8e25da25829a4778051560280a7a05cf80aa839c5793646ef54dcf9390e843569f2df8307a28a53f07b2ede3a07f0d78a1fb95f42997c36bca6add570b43682ad2c657fce220cdfb4bc4855543a32db05d708f3dd3a771df54516bb5849d736ca142e1a05d65c2885e64e57fbd46d236dee131f27042be6670875a8dbd9d47ef9ab497b92224e716424226145b8809bcb2e83f0a24af28ab2ff1a43ce1774eec581551c09865dc7909640b8a555b6a125a960679bd82b206af76399214ce549c95ec38f170e3cd652a82c5804ed64558779da590da5a04e15f446ed54951a7f191ccbce0e48bcbb550d85a79c70500262527c285c477d2c495f981dd915f029acbaec067260430aa509c5763f5d3db406e62f91be54ae92ea86a38791751814d45e269b4eb784af84042a6f30068beff83f61ef04893c2944c6d22cc132cf804b4d4334b6437506e14fe127f0ab862c212d95e32120989ee1a5d5c1b16a8c920ba8d4d7961570fbe7826cb036df762f973cee1cb595274f8db83278a108f8356e514fe0f9743a4ad5ee6e5702942d742f6870e514587fd7b77877a57d577baee5bffd48ec44091ab5447fa917c7068c6dfba5e177c0b58377848767588b9190b7cb4daeacb3248b2025120bda9dfa65dee4ba92b30d794b5bcc485a30305040f244c84aad98c4b330e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"06b7613131db2dd376470ca7d91bbbce0fc9d7ea944f19adfeb18c3c9298b315","proof":"3c50f3c8f9ff6ea22e2f8b52735ec095ec2a448eb5cac7c20e1b8b2e854d5858f6497acc90e6f64aec3332e262d38e86ad8ae4bcee6fdcb520c48b7102d3687bc0ff192c7a819765676a0b5189a44e00198cf9dc69b773ba2bc430b97d6c5e058ad120eeeb58ad02b0a3d44840c8c5d6e598f7553c8bb401957acb10d263ed6939ad31abcea7412649e7f6cd966525b4c202cf3db41e5c533794c49e4b40380be8da88678c108ffad66f5b2b6721e6de3148faa49cf7c5b199ac3064b5e3e10bfb7515af7b849a0f4ba96b17868dbd277bd4e5b29a5f89e54c448f32ac356207ba97529033726bcc25f8f994e0f324be6e686b32fceb64421fe07a59960395238a2e7964f281b7da9487b2d338ef6a71b5ade992a14d9c0380f16c97dd3f956612c876592854642b3a2c370fccb19fc365fda20c0be9c37e02aea72c7579c8346c112890d0da0db72ef53e9ce0ded0db64d737c6183b18a387418e937bb10078882e95ac7ab2e7e25f9be7cac70b262a5c3bc61b9b77ed25189e898c46007d48f2a52a14b2985b4e9981dd4e99a0d2f8d3becd32daee034b58c53090a98c55217837cc58837b5b58efd632ae0577d395b4dac6ddfc9398d5e72ef7f8bd0ec61df47546794d5e74a2e6e190e9bac672cd52ebb478306fd0ce3e3d452c27f58724d0659542efb975ce446b3cc4883eb3113a355d85b295f20d9a07519e1f3b6b49c837c6e74215ce9c92ca53beb3de149b6238396dabb5c8a8826bd5f867f88803c838ab51372c101b90a001f7471c98739e4f5bd0ec986df3a57fde6a6bcb7670aa8cd80aa080f15d3439aaeb6b54e992be7613f14214c447194f7129ba437c12802df5a76dadd37b23f648ca8c3e91f5c079e1909f45b03e1f57bc676e06cf08f9f1e603bfcf52064631241859dca524bd94fa55eb432685a9368dbfe9660d08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"44ae19869fd0fa04ef09242fcf0f025a914923358da81e092f0047177740211c","proof":"9ec008552000d0bdadec5dbfefcc96b661aec228292f8be7067636de4e29ae316473a197ec88317575f9e2cf64869005a60a606c3aa35b978560cadf5b287f039cb81e9eaaae9c8edcea85870965555b20952dd891b29338bd2d91560d6aed48282b50d25b0a220e22e60dac5321945206ad84ddcad5ff44a3c51248e500400622fc6627ddb33fc088b657ddf8668fe6ce296697d20dbdb972f86e79144c8100f6c158f2dad501989f7e0e1f11b59784147fb08f2db89e921ada97ee9a738902c3fec4d8de5613a7f05e5283d292ead3b8a8b1a87f85a6aa8b20cd7be87b19079c164dd59300167566cb519871c16311a5ddc0880de564fbbca36bc7fd174a779a2f319adea2faae52ae042cb45e709d10a70965c4435adf610c7785135f29521e49e753bdad6037d65711c81a4d59416cfe1a34904ccd209f6a469832b0334dfc83dc809ba38d99aaef75c88ca1721a794d600e408665ce6e4c4625c28e21351020753f29b012bbf8b2c34bdcd6e78d326af03cca5215be34c89c331436424b0a1668d57e9820c208fd0bee8df7cc7b37009fa90dfdeb3ac57895c7df016d2beaabb083cafdd0df78909d27d266e8ac9d9e0b1eeb33ae5f95a6a64179888b6a62a7367ccbbf00bd85fddff200a667e904febd00f6cf9b81858312be046c0d1e70c3b97deafb9535807581c9445828659bbabc4011deb64ef838ec7e87d6cd343e4d4e982854eca2fac7e613452d087456ae726da346341ccf2f05c3d95dd20970bca859102b485366c1fc8f6c59e78bfacb98012d883e4370c2f891bd38f53f782a50b83fd997c8f6c21e14f6bb1a8d5836a77c56f5d6e3341d307c5d597876ccc8628e8947683a65a2aeceb065fb4e7a23c5a6875bc6de966f0134e064d503f2f29b60997fda4cf76cec1199b9d05c491700ccc7bf2aa54e31e12c6e25db06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"64ee052cbea947e40c72d0ec3e4ade855a47cd20ca1f6d50449222be2ecc484f","proof":"fc604c8f59c5f5a9410ddd0b491194482efc9a575e4fa9253702fad80501404d30e1f1dbd209a8004d0118cadb81c48f8c40479d742baf0907b1d686eb7e89607092d5dd5adca500f3543c02e9516f61da5d15bd6f4fc564113147fdedff2c315485b3847ad3ddb1d23e66d09ac6826493ffb2a13521dee9e44b661ef3cf2008fda4a3c7fa2193a0f784968504a6259b586293086bb090f0b907eabe6a7f91035f91426e6f76d3f59178408ccd183fc1a2243b271433c6dc89665a73ae1e13013439bd628e7f412aa4ac069a550179ba1a54ed31fb43054d6f04aa7f08c163019022e29b306a7b4214dff4606246b4492e34af4ab1c446eea3229457553f930ddab9a70f17205d0f0b4d07ec67b4cf240358eed32af0b53605c9beae7ec2b809e217bbcce6b43e428a1df2e5fcce105d2feb2fee3590eeabe2091994f64f016cd8d04e5824b9c84209c99024c72782fb7adb84ff56017221ea6c1df35a18ed62ced0e1a09b968c0bd60d44d280041d60fbd09c46dd8090adde73c6ede3e222044a50b38514f11b448c247c84537e197f0862f75bf83ea67138181bb2bddd625ffa726ba81d0206679f0aab01ec369fab70c38ccd8c6213a183068d73aefbdd644acace31fe63cc3338c0992635c928e9aed02b12710d654671a26dd47882664560844b6ca4c89e6fd41225c58993e26673298b38dda4c0e3df7c2d067cb33e192a7debf776c0df486656fd1bb2f3c3a73692c455115ae1f228bc6fb0dc26af76a403d1d95c191c4bcf83f08e19a1e4c1e5753a5404795b4d53bf6d5082ba0734ba0b63ddea828186924e8bc87080d0def627e6c7513efab6373b87922aca4f7767eb657e7536fad5c356cb31ab7addae47f2dd4e0dcba0a0e0686e65b83dd907b04188f05c8c547779c404b9f32ac70af567b0d67c1bd1f79d68c1169a2dd708"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7cb525c7733fcdad1e1ba1fb71cc8ec89ed0bf38997b57bf650853c6754bd774","proof":"9846e669fa285f555233e967eeec150e3b1b1ee183f73a8e88f6887437cf4e39b0fcec91f8c901dd25d79ffd1c10202cc33bdd71293fee3bbba85c0d59d53d5b9475f741d3e7f61374ec6104a87810c6072f12932b6bc71db121d32c8ec45f5ff828b21fc18907dcb98fd660551233dc52bca49b69188f3357a3b4432951d24df729fd9e70d1b6618fbbb9ac08bb40273a77c74abe26b879d6a8666cf92434029a90d6e5556624fb06e00b47542d7457d9b040b6d4dbb75bafab774fb659a803796f55449ab8c2909d5ec12a3578226b575044545dba2956249e152e73170605e8396dab991964d930841aa589976c1187a7314af47879b1854e61c503d5ba22d423b84df4e4950d8d49027a85db60a94cf361d63258de3c173a6339fd3552167c836518fc51b5afa5d30f1a42bbc1b1258575c78dd061c57e5ee947bebf1e60c224c442fde15843af61e8b3d8c04fefdbf09e4673109b99d10898a5ba30984296b49963fd2132350e308459468ccb81cb6921eab5fedcde5408927889a4af74f6ab3821e4b8f06f70fe33519bd8a5d48b4646424ff10c62b12f4f7e28be8b5510090649bdf5d348ebb1d81a3731b6c16bccad8510aae2455b552fb4b3fa1b106ecbad6dffc2588c6c38fa9d7bca98589270df89ad5f3ef7d910657beff07307069e0256ea65fce3ea5725d532cf3b75718d741fd7e25705c454cbfcad1e826fcc060b9bed3972c05a31db3beab90e25f90098a1f50783c39aa4bae8949c222caebc9a90be91ecc0b8914e3d5fc4b2adc9138ab2b9da8a613937229c6e86d623a4714f42dbe5f0b83e8f7741c19425d2c0e66a66ef1ab614084fa69252292954d816a95b9c699f53b022166c25fd1a1a1a27225d8ad5cc3673193d5c18d7820636bc2b82ad0213cf178d5a9c580eeb1843192eb1e0cd2bb6597e460a66de2b01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"16250f0d44bd774497a86a15abbabdedba05b4e9a82a0ac1e8874c497085f118","proof":"0cd81220efca832474bc5e007b9331fab813c769be7ca5b653b63f0a47bcf12a1c064aa54de31959802c5d98eae94fa968afef6c94c785a646d78a355bf6ab7d822fabdaef6c2f1039e6b0f87cd1205a53a4fd4ee97dbf11a6c0173c6842d8306e6407c6bcbc05f5b1e50b825bbd8820cbbccd3a2009bb42d628f45df6fee07b80b5ef3a43183427bc68b3d4588b2655612b92a14cd2a19644258127a0ff2707c0c5443f7d0082f340be6859ee2194c67158a20a82b71c35cfe8411e2a9b6c0a0f54769479d603acf9c5280e03f7b7dd044b376d1778c8349c05158376df040f0e97cc62d6a347b0c994c8ddf1a62ad75baa959ddbe4949d525d0afd5eae0542126b490f4cb44c85b43eddb848e5e481228b409f8bd3f4dc65164a5d1bc7892b3cffc59d1215b67f05d50fcb092bee45306d28dfc851fddfc6c8a62ff70c296cd448c8aa26dae9bd0a2ae5e4ffcf80eec25fc299170e5057579f626fbbaa6c4d786af7347f9658d4813b15dd73c52860ae33dd146ff13012c2566aad04abb847141ea3c61c4ff47e7549b091a3c43c8f1d0967e5afa55a3b0c2e87872493805c56f7f1e47e046346bede4a062fe696ab758e2163c173b788e9b9e14cab3e300d804c950c6fa8875d79f9a8cd514737c0b012ea4999e68596dd9d1d6089d500203ae994d251c2db520796f19afd3a9d2263523383b691b9fb54a57fdb6ce5d32f20865664da5fc34789a919ea973dd70167ee98025252ce9980d513c733fa74732029ea14078f6cdbbb05a142490e16cf8e67253c6a0525787ec06c5b011d7c1bb60d3320ee55b991c7f4283d99018c8cba1ac21ddf5769d602e89342b898832e7ea734e159cc5bcf655d8ca09080c82dd7e4fb00fbb6dcabe4ed6cf395524609960c6b8252b24d2c375810ffefa44b4e1b2928d7a20468509c967db18f64880a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0c70c54dff81f332d323cae0359d42eed3b6350b7a869c0ad2f8a72b41b05720","proof":"4838c8fb4cb5f0ee1f7a6f89962d808b627b2bbee3db8e705b78114734157e0cba234f5e21440cae873fbaedf5a9df6ebc7e604b2cd4448ae222ff90af7f9409b6303139a869585124ad73a6b27d19c5de32869d20d7f388dd47e8b0cdcb883998d5c3c10d4af578429125de95e138ede0fe125e969f323d3c366dcaef12ae169c24e03b62eab3f36d5731a35231751b73533d416d8f8e0f76fc5e1541a66b031a809ca12a250f5caeb4812a62fd13e2272cc9a5bf8900636aaef393f44b820bd7b909012618b6c2637c20c1840be29ea97267d0e05a3bcf3292aea92b781d0028ad97fa9cfd29f9ffe834a0680dcd91333aa769830636ba7fc947c32aeb1630a6e6c15846d0ce5934ee4aaa7bb2ebecb17c14916627f95a3e9db184a416c10cf02c6e8927e23772b431fcae67e58a33127ceae506e6d9bfe3bd25ba01bbc21818ee5abe5175c5e25f29de4bde99a01b2dc9eef32911a91bcdd27e85ab18e96bc0841303bc878347a025bbabb56d09f2e6bf952c58b7c12fa4bbb30ff5a10e46a0e0c78e377f225e724a175017e933ef00eae3350894e07b6e59c1b0522bd4316ea8eb4bda92c58fbb1b28d70b918741695c835d81919149f7326c74cd383a4da2389ec71075a3a1e2dc6647e67b0984aec09619de56100ef69468be9dbc2c7c845f1d6ee13c0c374c0df092319ae1f5a105a06479e488206fb3b3b09be3d3233aa1b488112a79b92858ab5bc425049128230d58656599cb8b53980869caab7b06ba7c046792c911ff1a1c96d6b323fab7f1dbf63a5c776b23ba1d4f3c48ab558cc9bd6aa50a67929c8f66ce90e68dc78fb672250322c5a79901399a6272cb58111ff3ebaf5bbe3bc2693195040b6768fcccfdf82940ec46a712661828f49c0a7469168569d661055a9b49fbd4a70f6bdb7598822be1ebda6a07def787c02a01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0ebd836344a5534766c4d2456ffb4befe42f307aa60a32574ff1fac3c52de120","proof":"2e8f3577e12038215b78e6593c16caeab23674226b5dc3283290c802cccde963e84dd92024a23b4ead941f71ab46040e7e3645f87004e58196734a440354173c94437bbe417977cfba3bc0b5d2469923524a7135ec51c44b4b41b152ff6af42292a5f73fa85e08c0ed7ce85557f31a7a4c09090d3fea8c44ccdd90409e79e53175b1bf5e916737b65d6f6cb9f4bf8eb861b69e29ffe01eb52f0d2d1c24749406c5b164c3c9dcdc599eecf1e04ec521f339562f4ce67ded06e96b900a7161c10766eddecbc3537fa26595e968270f1162fd0998d112e49bfacf6b167673fabd010e15ce4ab39e1cdd9faef231566ae1baae6dd8b2e2b714a07c2bcff93821b97a14a9819c6eac2ef43349005989a6cc0d2e1833606c46a8abfc66d8acc6682968f2a2708e2e8b471ec7f02168006e72d69cc6e77f09c7f502977d0d0cd3dd1a7db23c4fe268ca782c6f2308fd689a7cfa12a3783699da1c2a16b60427c8ed6d6526bb67c67b75d8407b8019df84d8857ddda7236d1d57c0dea0f482007cf50b503cee67df62be18ce0aa849c53ab8c92e378f7de9387d0a7e61f938a326b31b48ce3170e46296bb24c04ecb3d1226aeee035249bc345c41b217b8b04e623cbd25566ac394574c7099a0d30d2dba21fbc54e54b5740b329b32a44606cd2fe24413347d013777f1c9229f9b64b7e2c5fd41a3b15d104b572d383b4d61d11815e92fd6cc292017c480a5b1901f5cf8505feda9891ad4d53013c6bc2b782e651dd4394a6adf4a3d23af36342411c6409a4f8fb192f557e28161c7098c1f7583920d05ba06e168d8072af50c714023800a27a573ce1e5f584accfe78b4d44a037a1a73750582801e5767a158a8acc05c2862f956c179993f382115ffd5f93e94ba13068f22cebf9b92398d61508f86cc4b24b217c4e145b1a86c752dc9d41563512b0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a6b9a36bf96c3ad749490467cb3bd8336dce68b04ca1255d7b5e941ee9e0322c","proof":"ae650f8fcb2aa1ca42d5bf7b90b8a21caf19d1c4be0e63c6d17323cf0740a0258e63e3af8f71b6c8674877906b437928e90c1853511b733e376d4e43630113483af1b6f3320a67e2f7ea4b126d24f6131ee56fd81780f8fdb46472d1563bb67c3c532cc506a278e49d94ade9e7cc952a26307742982661b60f3239a2d29eec73139e114022a59209718850a10cdd02783fe7f97e8118a01def9ee3da412b8d014d8792191b482c5d3664bdd75bb47c7dcef20e73cb5c7b8f526bd6bc509bde075d87debabb7652d570848c68893db586fa085a521cbf61576d66fbcc84480f055a8cae70b17db416d505c0a06d32152265be926834992a3e591a99194259fe6d365f2aa40391fa1f47217d310d163c33a4f5e47434a3edd970f8d4ed60f3632ba2747f03f5dab72a9c2ee668dfc40b89a002ca260af142a89f943a8941365f62de1296debacffc3d50208ad8228edfdfad39d8f2e2cf2b060f2c5b6dc396580346602e9d35bb59ad94d9ef717337da4c4e3dfe9bbdc74371adc02a59122edd2ee005e69eaa126fc1dab3b21b16a8a638dd20528e76e483abba36bf6cf56a335732308c77fdb5a2740cd8e87577b3af6281c96f050759c2892081a3fb3b4dc52478016acffa05d788aa2eaea395abe7f81c9eac72f6b3efd56209cf6b8062cf6386d35394640e26ef26cb8186d89d3fc115a7ec125dfff623381d3ac9df9c5f0abe6ed2dcb97d83ec1936c4ce695e6fc161c932ef4892f271a4fa1dfc80010830962c64fe418e278bbebe572b6630e45dcc52100030a8203ad9646de84b62f757c468b7d851e60261ad12729428457a50829e560bae58235db2abc1753c1b7f1abd8107bbcee602c0b44c7aa88f448413fba4c892f53060e2e6b2bb308c6a8d032753b85c21b810136876131bf46cd49d9e733e6d840ec33aa740d62721eb3506"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"341b777bb1e046d32207ba97747463780158e277a9ba6f3087291ef987633867","proof":"72b8dc6d34ce034486ea10d138ab0b8daef7cca4bf691ce354fb5e4d8447785c40723e407227087424cc2d044798b172cfe41139fff7dbe6842d6f906660032e98bf52e8ae39a8273c6c8069b3792f5157f3828704c7cbc104e205996a29aa0ef4a3b832107e5a71bb28369c9aa7e18644c5a3e76a397fa23e5e04831ebddd3cd909e4def408d034d6d2e41a5bcedf6b4f84500c8dd24693fc2ebdfcb79bf7024dbc2c404aed6d1014259753d94b06c10b6f65000c7f6b1eae35fdc8ea29c50c64a9cd9d29d0ab679beb811214c0ffe1827dda2816d1859a073ed6281be22b042ec228c848a3c63aa5716162653da1e33f587f9649f189d6949ed7eef426915f3a5662bf9fc73e3f3cbc2c9a289873bb2511ac6ce98607d938f48f3ed352f20dc02610bb69fa18ba810fbad7665e6b7fbe6d119c3eff3ae470013cd71a993936b036a4f52607336430254fbf9e9e6f24aac675fe0fed08e85a8d1d7dd71f6855aaad679a55a96fb2499fc5e2478d654228e6f504c96f57532eb0320851a40d71006a2df72bbf2d670a1cd078563705744916dbddbccb845bd150767730e86863aa066c2d57f57b8a8b4875db563984b08559c04bc3b58841d4e1035849a7d912c6790c03a7a0d322ffa6b8193c4527124190363f2638ad47b3212fbbcfffb23be898dcf689252a8b4dd4d9796a8b5c5c902e9188b87d1f9e441befb02e9c85107c46678ab45c168ef83da1cb0b59a1865654901b326e4687b7571dc2c88fb93c664d959919c59f1e10411f9060ff6984c70d28498476b2f79a3ec33bcbcc7e782ed00604441d9edb97e99b29d978c48e50c60674bdde56784a72fafff7d33159e4061d5a63fff1db590b18b85f252ef54d239ca7d5cb896f1465384297deaa05d5ed773b118dca12d0afd40aafd3948a19b2e028386a051e8832074e9ae71704"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ea661319cf74b0131e6024d3dc7555df0d34913dc8f510367cb6a9ef5acb4d55","proof":"d2bfcd0a4a907c6f228318c45275b6edc8c44e9ee51e3302a07d59e37db1364fd45cb17ffae377fb492428af24520b46a1cfc80dba2c8a63e9ff4aed8230467bbeb5e10e2cb3863d965c639d9b7cd6055ba1dfeabc236a4e661ff16b2878e960b013576e12896beafdd84333bc999ed73e4e8edff150bcd27fbba2f610d83a4974334b5833ab2b51da34d470539d6a405b0d4ddc2bc9a54ab01c5a0748718503501cefdd6dc67111fb9b40cda6086e4bc66a66e0b78a95daea3f5469856b8b02b0c6e82b34cd135542f036bb33bb331cbb9fdf6a7a5cfe57337227c117a20b0592c077086043022cbbc12dbfa57e9298702025c3c86106e16833ba9a249d3f2facd2d880b5543709ca8bd258a43d82cadf19db40d89702476e8f6ed598df265fbedd9109e4fe9f97fe08b6a9438de427fca285a13bc92a48c5ed9b83f5c8893c54af2426514cffbb181f95b1a9abbe8dcd3b7aebac7aad129d121659735eed11da2d565c7143db0b5bc79f150d6413703a5056f34c584b0c6b011c0dc0eb492ba6f99275814eb0f9cbe1e7df8c0a37391eae83760d6c7ce580d8a247d1d2030390f61531653c7da90e70a5e8f7dafb9c5777fe4af3d2586974369fb61845ae53ca49eaca296d65f098f17c07bd026de3efa005430a92943c32d58b25c24566516ef59a34f7528fa8774333bb0873ab7f279c078dc45d31ac963fbadeb24417093e16dbfdcf019e5f1c0e36f2e5c6c049fae945e44bac5f2cdea60ad1456b341cd494d70291bdab6af1e5a62265e0d3aedce4162a072ea75a121e808295fe457984481720683056b1943c905d25e6229b054d499d7db58e8deadac4cc6fcef948babd68fa1c9e1e26630e260310f0d369b64b3475e24bf5e4043e577b3598e901f7a94d134b45ae32cb8ca925bcfffa745b57196132a0af4702421b26e1fab407"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d46f85c899a3af500ecf1da19589a084a7fcae175cd5b13b05ffa9771f88306f","proof":"e89b9f3af8a1c79a5a030dcb0349d60e306099481e8d60599808cdfb0bfece63568c4586e93ad7b9373e59ffe757ce5ce30aa885ae08be2f900f24e560da852dbe4df99fcd6e956bdc4f84c48cd4987836a59d36b5e7cc4315ed54c126dd8d5c82974ec92c68705b85d48b55aed715fecb25a2dfe872ef35b948e88d2b74885cc4e4bfa2958c98344d7b1c4549ef70fc69f24f7ffe00ff0814aaedfcffedcf06936355e36643110741946911830416d80a977c5364d5f910d66d26e2926d8f067c1b8ac49c1cfba4337293f25e35a43399e5f7a7619b17da10ce9f940d3f2c01cce3ef92072c101581125c938a89a5b73deaeb97e0346a9803f3fe2c0ba9132026476cc5f08b1184ce82ce722a4f421e91e196c069d50c8a5f0ad7f8299f5033a468e3019d92db95377e22460ac6c426740860a3ecb17a35e15998444ba9423316e963afaf2efa39ba499edc6898cb0c0ff72cd6f399a7b39f0a37cd6354c33df6a22b72515cafd013f67d36619dc6a6da83c1cee458aa3c347a61325f766b3a7435277dc06cb673267229d152dc0be2987b04c7ed39b2131784c7411df61e1ee4e180af1d197230a854bde819081263bc76a9c16ff4b71153751f5e0acf955e9c905c460ccd9e6ba4733109376bb0c9a1bdd17f952bd78c4f04d905de739c260487a0289ebce6c2cc55568be691abf2704b219823ea03f323232171ff428b0c446d2e94d6f06ca3d8769e7596e867a214382f9787ef538ef63ec378eee8966a16bbe556e544798bddef32aa1bba2ff4e532e3dbf28c782683e698218c2fc60a92380327ea0f06a708d021bad96b176c8da6b2b79a2512418ee75f8e397cf969817d7bf1f66de64274d7ad94659475d6f7a0f97c1ad87a08f08aeb97657abe0fa696a8bfa71c95c6fe485a7ff7e204db1936c628d696642705b52a1c9318ae0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"32cb910c15b65d6246d5a8e08b294c15fa794a9aa6687d11872cb0ea6d41fb13","proof":"9259a8bd7c4ba4cbb0f78dff50468bb6d18b25341defd5f4c238de10ac945a1dc85247071c2e134940067e85b1fad4c0ffa6748206fb7d12336bfb933f70fb5fbe1d192f2366f8796d62a06a7a29eec7859c3cf652014b3d1f3291a1ead36a31f0b56bfb49f3e8e0aa058b6df3b47c85f250d0948e2b03594abe6b908367981a4f17bb4fc47167dc6dfdf0fed2e97f340267c9506e812b7f68e11207bcee5e074abe374f3311d4573f033c0d2470d7aef81a5b3b419cff882af00d8e2ba0ac0af6715bd91947409d201e6d4c3fe7286910036b6ebffcb00125250716a4fe7d06749caab6357bf8ae8841e6c8fc396fa027018b94b308ff9097c3c287cc81dc6f5a9a02a54d3b4b895d053ba0d4f795f919c1d6b43f95cb57df920fcce02ab166861c7dcd86ba44b4d7bf3756d55970fe060716159d26988dd19a5ed730558e58dae9784c099c8e51e56fe0d5b16b962f106038add7141e963695151bcdb60923006812bbc8a188e708569aa1636234ed914492794b24870978f9d2e24b295932e0c0ef2aac5af717b6df2c347ebd78a4a63dc31cb0c330c3e94a405e0a495911507a038075aa6d164e225f10e64f9ec1180a039bc3d8b0eebade9a2db5ee5a2a3424dfb9f8b517bc8f090556c85a051cb93d033c68c14603e437e2e5bfb6cc1b3a5b26d7d4ee699cb4cdb9be2283ad5ac2c67d0ec632bb8c31590b145654d624c2e4e23407bb8acab5448a37418f1dfa36a11718e7ef82f368d59d9660e44c52c032205dcdff239610d96b1bb923fffb08f983dd826f8cb5de0d96fb3a731c41de3bc88823dfcc5307248b08eec0f1d7ce4967f2075e4ac40d607740683a0a5fa14d26f406e56bc62d26ea16c5e0cc197821137bea6df68fb64bc8f00ee44203f2342f989ada126a88770614c77ae950fd9a654e1e03fbd34a321309c4dd7401"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d2fa802c9d49061d580e0fd4a34913ffbf9baa918ba46139e7a0b0d660d12b17","proof":"329b868dd114396889acda7e0eb615dab6f412c9117a7822646c35829626ee4dbe39ac3d0881c2dc2f22e144df98cfb3de69374fd1402b8eb12fb6ddcaa938139886aa22801a17b349d87074af88541044cdb0391cfc9e6973fde544de05331c06b88dbb97fd21f61ce5d6b1bd5fad26f1f6ba7f068734bc03cba2a5350e2d6b11120008a4f53ae0aa7b752a2914ac75c5cfb5c0ef74563071c04d718b729b0fefd0f1b76085bfa0aff7dba0f50ecfdfd9d4ac0c96dfccc38862d963594de403780de38213cbae05d2157ce5e132cc572e36c9836ef8f9ddb6adaeaac8cdd80168d826af4ddc992909ed6f1542eb1ffa6ac6d5b501b85f89a561bd8984ce012cb2b2fef226812874053db82c7ebe78aa48956b4307b3b2cae32193a60948042400a272e499283ece5a0f9d2e5cc8432d77b47b28a0964f665966fa937aca381d26567ec8cc1fe6f23eb2bed5f8e20d9b36cfe3ead08f17608d70680af9071a7bf2939339339e02d651cb405f773aca493ff48996c9087eadf42e8018665508097661f1747442ae93246af174a75236941772537639736401a890ca520fc4a3325c3647bbbdb17c86af04dd0cc3c7b8f7c25106a56ef72bca679df36216282d2a28333f0b66349a15f6a9ac33aae266690c66ab06feca833bcb0a465636099f06fa107f428173faa03beb374a71078358e60693ed2451ea39a3137c6d7d1f654256061d80e8aada6685e6e12dec4de173086f9921bc5f2e1388e6f3513cb41a77525e185b10a28a59a6ff423e9cf23ce004033c09eeee6efbddef1806f9d4ae4dc00dddc21b5f7ec018ba9b534b96e76cb373bc9354e6d486abd34147d48d931e99a82d109c90976019b98a6532214a643d18868b91110ab094636da7ace82d09190703f02eb08f04960002fa4fb4faca473859720c5e7e9da9ba1111b9a0040f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e4938db9f35c111174f0855e06c1faf70bc3d8c643f8d14856b4c731eeddfd1d","proof":"1c1b62aa6bea3e6901eb94d28410bd3b29c6fb036e6290ee88c34dfd61e75550d6dd397307696c7f178fb4934defef841ee04ea54efc964156086b631408bb20621221293b3df32841ad213df197a32435f5d3d394e437ed44d01daf33a52953fac3aa7c1d956f6096d744b7473e7df7f35eb5fe4556131f2ed2dbc1614b244af6449fc54d58d622e924cd64b1b40a816af90dd4bfa1280ffd0ca8d668c057004b8f0e91f1076aeaf53a59ba14080cc45ae1709d655658464c0e1ed58bace70e693f22153595e6889c8033dd56e85a32b69f1fc99301e9a2babb69ea7d57590400e655ee54f35d6f5efb6305978a658698275bd54ee896b50af4bea6082ea04320f5e7cc3375d841a7c5e97a3956aba577a46bb6a31073cea36c850683bb132afacb8d9b8a2d50a0777987cc434bfecd879a18c90d9e96ddebb2e7ad08cb25624e549c5d9e441d1fc7887c94795b7dd16762b33199f2e5ae57ce2d172ddbf761d4f95d698646fb0f76ebcc8824fcb5de5ea1eb23cb23af75a0335b5b1fab1f7b5c8fdb1a4c44ac05d3702a395024fab7a988a95234bd76eda7fd4df4a6c9ea793e9417c31d3c20f0f9b8130a1e6f2760e42da41167baee89569b30e105915d402ec075358bd1da03aa1d8a3b103e2418cbfd8c25f96dcb5743be7fca0e19323af694785f28fd01f872ff8c8663f336f0d48330e69ebd1d68d4faa57a80dc322c96bc323ff1eb654d6c38cde443bd2a393880f8ea3c9e420971bf1db75afef06d02206af9c02f9dc37aee6e5da220140f5d194388713d6559713bc006baf1b77cb46cc03e47acbc0f87ea1f22f615bc91c961d1fc239a0e2ac2e03e5887a19a4d1e52f558ca1b92763ff883cdf735ba7ba11ad2b354683ba69023ac4387f4e0030e050fdd2f57a5f1fb9995e1ec6dddbfe5238fd7abb6eefadcd627c3d1a8ea04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2a30908f9c93431faa1fc46542b92d91523429dd1e6e26f9bfccde639bd47842","proof":"461dfba3b3110d853e532fbcdd315726bdbd6721f0322432e0aec93b75f2cd5e5235c4166a9dea784d87eaef76ff80e9f4527ecef828e277c61c0a173736211fb29c35c3c2b8728eb5d75e2478cf90f93e2a9cbd8ebce487a902b25e38d4d6411217aa2c134e22e8e8403b5854e8357bbbb67c9ebd61789331d342d7c636f62f1c9cc11e323fad064f77eda2d24899f7aaa10d8b111a2f9db579fee1d907660b15e9da4eff2f58d077574679ae126c68f780db39f7ab7c936af9afc5f5e3dd0ba031187622c563fc7cab0d5e0fba5225b84fa64532b0a1f4a636c3343d394d07b0491032f2238ea583c033103d0870dac2e56951c0664385856b1049a8be2264a2d1a5523977fda3de178fc69a209a11f5cd8724a9128caad9f8767047608c7618fcd7812c7297b7fe04aaa49f4d2ea3b2f0d57e13238d6aeb2e2de87395717f705f063c9ad364114c1e69758c033367ca99139723d53244c8465861fa352225f078e3884482a0df2521a3bed945b5c19c133d638a5b409212be591f776f4762cef5f040e6be7b1fe14a9b61efffe22a82545c0a9661d9a111e9f09c11ea2f70fcc6d1d4880fbed9110ac8776e8348f64062d5e59ba61cb803755509bc7f1572348af7bb1f58f07523170061159ff365fe3f1cebce6b338dab99d05c1d282b4414866316865051d1122c2fe3d7e9ed3fae8acd26473c8a218cfbb75ffb77b95358d68d596c84130515637c522f9a216fb8fb320f8d029575ceb962b94aed5f544c069d47714dfe9110b41be7fab6c660f748710fe1cb3c6e5dc290aa64c4ae2d14005b613a43d37c0e818efe355fbcd65f99e5e77087f7f6a24b3f178350d74241828a829d042c7bbda59822f632df1b9420c6fcdd889a679c3550616f02570d7aad074dfa1813d3f1cd84dae884fe4b73a25a45bdf04fcaa667a1455b5a6c06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"184b60e66e6816947bcd10c3fb7487e16aeff26e3526338cdced86a1a7b0e050","proof":"16fe251733d82e6e56255122b42522a0f694c712cb318578d0df56d5d20eda18247b68f980ef3212f46e129ce14534e79ec269760579c832f5239e0c960f733f62a4191cd61d0c3a8bf2b1c3d5d2d126e32ab9899483c58237c4743918485f25083e24225a03f94c85dd99063c371499fcf8b321fed13a7ad1917670fb383b4202e0e119d2da6e9bf8475fdc33945adee93ae30923217449203b43fffa174206dfa3bde2c12f99c422dbf182bd432ee04a26d3c7c0c2f9f958b70122ef2c7909095f024d5a882b91e74228eadee52dd1de598808865e63e81baa27338957db08ba676387f7a7b204ffc5f943e301628283273ae2ceff8bf72ba53b9987f185401238b370871195374bb9195f2d748a908dbd0a581e3453445fbaba3f1b014258bc78413da26e7f4c98ad5fc047c5121ff52b0caf45f9a1fd8e56e48c22e8bc588005a4d508f0368e3ed16e229673d19370fafd9583f326aef35e42dbbb7ad94e6ad4fa778a071a64182792422dad9b8114e574c740569d1b665aaa56a6314b3ca0caaf117905f1598db5110585c8a71e54843626ac78d87142cfe281f4e18050d861a731c86d355e8f361ac6428d0172afb2061bdbce8f0729930a5451983b6a4c62050530d95f031079010848c3225360ee0df51a4cfb16afc2fab9247fee5f08674269dc693f42aa9010719c6da6b56ac056b10b336825474f6bae1bc33c566e10e762eee178cdc6c0243ba8cbd7928efb4a9cfca9e3324f3d159068cfc46912c350cb593e60fda04102af730ae10a81528a8c5b12797751c255e3270b1d4654df5a501f7b750297fd9664e96c80c3a24464cc5032916335d4e9efdb2d0755dfbfed73a5d88eae960c0ec56ac35d71e274ce764b996005d97e4cada59109016ed799d5e33e8734678fe4c9267f144775a051ebda3fa561e5559403cafe1603"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2a594c1ec2653d459fbb1427b494027707224b491d03f38a4ca8ee8a75508c41","proof":"b4d835acc3dc433b2536d47fec0f2803ac262eac2db55978c60e347b25265e75cad79bb14a038106d490c5ae148ddabb8b5f1ec40e032e3891a36e6306afa3301c0fd4aa5e1d02979741f684c53c8969edca3d4c1895f4d02a1188c9bfc76c7dd457745dfc8df0153bf92bcad0382c7d354007bbe191e1974c199b9b59ff0210132f2aaa1fa3276f155552c6500d2aa949178438da38a53d3272bad9cb89310276d173b67c06b917f3cc507a516d6a58e8ec91758cf3bb7c162ca854de79490fb2a125c98601c2b8834129366b93c1b9fd9a6450a29467f68157549175843e0f6ae2fdf775a8cf1a729ec0961437da0323288fac1f18c1d5b91b9bc80fc30633a65a3753fab6a20452b7cdacdb712ad8cccfe80681062b25f0dfca8812383945601da5aa141d04bee85ad563115b3ebaf59c1ecfe15c37c311f63ca561e8b503088602347580f6b778ba1a21891a48d5f08648d89bcdd7fd21d06e6c1f073647acaf2e43abfe52b585bfc3a2cb76db72cf4dfd1daf675ca942fb8674ce94ef1d709fe6e3c59cc065057b11d45fe4403de8cf9d283dc1d83b42f6ddf67fd8a5041258d4949cc4e6a686743d07cdd33f21243fb70675eba4a9ec79477e10ea1174343f1d946ec3a0f53c408a837e1a2369212c0bf853cf987b69a4055ef29cd62224584521b0cc9b5e51f0e4adcdcb5bd78472aeae2eef493d2d5acca9f953876bfed9f2c1a9d41eb09926e6927bf263dbd70ed53da8c61d9f1c19ed2937d18a5cc8d35f2f807312bc11a1b24f6139f7f9f2f02fa6a8fedb2d8e9383dbee5b8d7b5c9344dc725b379594ad4202e2752644e5d28021f4871cbbd5b51272e5000166b99a98cd3ee959abe22563d796717ee8063e535f36ccd9494d3d9dbc6469e80404c425bfd9163860fa8c5eaf9420b4418ed02dc96ac51f848cc164095f6ccd09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7c757ff4131708a1a1e1d53d558c2c7c19c6d94715a057584ebb1f4f3258fd2d","proof":"3c1d7287c7b9e607d868ab99d445b1e209527ddf94f3038e1f84d4672a3710569285d38ed3364f0eb326a8f6f18d12f150a9c1c504386c393f765144d2ccb019701bb6993054aefb99e00916f7505c668a53ec0d68a0dd312464afb79b7c105d1c290e572657960b428650425d99e250a01c6f05ada8f47ea75ec378a53c6b36e587a96825b077ec0b47d3e6d45f17bf58ebfd22cf39090150473e9a9fc3780a2ed81c1a2d94f468931017538bdac6ef9c353ecc9608a25649b7f56ef2c4eb08119a0922f8b303ba14887217e9a1b9e33bda043886fd305b79a80f0cbd931607ae7c013966220b77d01d07a8a80331634ab5a6678fb1d75d7621f80fa0265720584717b461e138a27bc9b0c2da333d4c5940e4823ffb52270c0d18b9a99ba612f8f086dd677f0ff3cb9d07019de30d08822ee1b070ea26ed57a7032e0242eb1886ab721efce9658437ce80570c9339220f47f8ef4782ebcaed6fa47545a34259046c7fdcfb3a8248276cf8777b7ae2e1bb1226cc4504851271f550f9fda8e80a74750fb61c0a5a41341d90e1ab6e9b82763a88821d053a8fe155d03f3db3ef08722ef595a5b329cb017c784e0c8421309949133df412e0aa3048e7d0ab757834023011494776e92a14c24dc6f80907a2adaa89bca92ea58f081eba8d77570c00e488438451df4606cb699fa1461daff7d401b6db2151824038a5c8c85f735c342092360ae85911e828932ca94949e8d8cd392354c0d096040ac94894c4e8f35942f4dd6f6a247761e97984dd5dc1858f265944f5ad402b42891c47ab324a02238c685581e8c065d1d6ea128f556d9ba1d91c943ccf07e2001f1bcb4c2da3cd6473eb7be60ac05b00f776b82749ecceb8889ae18a0faa085b258879622a26470c24f473ab140b3281f8f2a2dde525cc22d60c1c725544ece9b2db0fd06a0e6508"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1ece8fd53590e92362f8e4f5b69ac8f8ad6048983c31d605a60926223333206f","proof":"3c77fc604344d493d9ca8f94d256fbaff47f328b9141ab297a4634dbc3ee1332723aca22aea5aed5cb10bcae0363a81b567f90ec0195264a90fa4ee8bc1bfb18080f08ea08ed81dd0a927f1b8b94a226658fb828b9b2765705aa940aa726296ef6f1fbe4b62e1f1c5cf6d1bbbe6bbca56ee2d28bc543d71cf2c3b02cdbe1dd67c40701b72050e0f1b4b43d5a633aae3227fd0f1a9e17eae2298e83c4e958740ad471a54828a5028594134404461135702dbc4bd97d6fddeeab985eb6d170c50feabd51e46fce166b3cf79b8286e31d9c6b7b170cbae04b5c5c72648c1fa5140ad69512a698af36dbeb29f81adfc80f574af9195ba9e9ed0cbb131827138119456ee8408b03aa671a654198784ed97edc67dbefd08a691c249a37ac1fba016655663c268ded8b2e9fb8305935b33f3146778ddf0f4d9ccdefc8cd4e911718864980f0f09f2839877ce26483f2b60354f7f7cee5607d68f1796f628eb5e7618379f2e088ac3fd78773edea66a58d96719a9a413f7b6427e3b45be3ca4930a3e83354a3e77680f4cddafa1b23cbf389a754c05f023519ddbbb324abe7b2d6794707f2d2419e91fe2e48d0e96b037666f61248fd8ba121344d5c3b0a2ad41a4f860af41f9c27a3ff7559cab31f5ffabc3a0be1b07ec00d66cfa4888af2fa2d13de5f7c774880d704ef67851aa4689b4bfa13bbd38eb1fbe5a9b1b3d6f0caf584a35920efe4343c95c565fe93ccea8e3d276bb877305ad113d2c10d5c0a720ab8a94320036ab40f2b255852bff1f6aa8484d3a5134c61956b25f63ed4f7a806e42a253c0d65b76b9a16d616238bc89588fbb6ad1ecbe0376be22616a927c50e5cec177ef064ca6b7f8e98ee9a98e02d11267e699c06905095c00fb04cf1a80d54b20b5900d8c83fd605f0c23d789d9dee7a1caba9e40c1b3b8cb5e588fa82f4fc6101"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0c989b536808af7bebd7679433b2931eca3effd96e3af79ddc2131c42e6c107d","proof":"167366a67c808cf7f2158192b734011878770c3667ba46339112d9ffbb593009a84e50f26d657f94a2aa3119930f4103cd4fa64f45b05ee409118c9f95682f4b56457a5e7b8703ebaabf411976de7b937354ffb4f55548ac3980a489c5a6ac6c0e76bf658a50e375ae6ccf25e861aba3765888b82a1796f278b6caf037b653790f8950c99f08fc83be630b53acde03f1ebe852bdbd3f14a7c2158d6f08ece800925b9ed07e9e519b49618239b56d65928643da40a6342f07d767f6d689e71e0e86fbf5a508cc4b0d2aa8d191babb438a9b87aef0bd9de9ac1162633e93594601b6813d6a93af9db7babfbcc2eab477e53562101f0e88f11c22765c8d1b929577ee623398d145eec81154854690f7fa55ced72e7662fce552e0daa93d5017b7682219761622a872d68d016e8fb0857110e7598b08df02ced5426a05d0f4ae8b36fe6c504aaa5c1f9a7bd94a6df17461114f2ec5b2b7b3126661428c7639fa4a08e4b17bcdd7e09bfce76b2dc7f06ae5c2d8abb27502ce76ef22e1936963743602ea1e114e289a7428427e1b02a65d96e7a4da5be6a8bddff735eaeb160592140efa552814843471fb3a56529fce2676876bc90419182c77a0df9367753c57fd7e9c213f0c823fa24e170c43641bad8ae556a02580f0dbf24871b6f227b6ee476cb090befe7a1fff241bb108d1991ece6bf7b87fb664748fbf522e12d7c9d7535484d9e7919a4589962ad96748387d742ca4ed43fcaaf8dd5ce2ed6d3152185a1eb49ed64075af920fd90f9c78cb026069b491c86575c01ca41c8fa957d948da328c5ad7aba02692568d7761029983b598517ee325668a64e79ac98da7bcb9e912128ca3d193db1bb3e28041a584df977c42d324e91117561ccf10cfd6f06bdb07f47ef48c8bdedaa5e6f42f171b4662a22290c1d06a601999c134e851c6809a06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"542dabe8d6a7769d1f82627a7b8f31a7193d708d8f9bdf6dd2182457b5673838","proof":"665f73620a19b5f68a4b1d074080e067cf03bed74b66ced7db167f710c9f40266e559d069f0e077507cdb69c67e4645a4ea66e086169eb70795417ec9ef4c634c44b76e3dd8cb13e2df54bd9d1abe8271e452a714df7d685656575da2692c76d0498b99d08123545a97fceb89daeb605aa34b234b5941b3e1ce4e63cdb51b05571bdbb4e0c217cfabd565888e5bf781bc40171de1f176e6456edcb0f5b83740b2ae19f8432bd5fcb01f7a5439868c2f21d67684c196e3fb7f5d0f52c6be29b064a3b515edff316308b5e36f125e16babe63dc27e8773a502b9c883d976f6500b8297dad36a3879a67d05a8c08a4a4e011e7996e630623938014d8b051c84cd68ca6384292ec7178cdce688330ab5cbbd6c2a53df75b88b9f7fc6c1b981a5f86f16c6a8f4478c8ed3e639362b1f93067878560c1a04b19c43918fe0a444d4350fee7b2451e61fc97d412ed92da9aa9873a4cfa15e74cca77d8f29a7fb4b2cbe5472e0503c935a39c54ff000ff2d424100750af13d8da9336678995b954a6a7364aa202bc31bb19e452143e2de0ae1fd748bbd0b543d92a52ff360065bca825a3104d46a7859f2a8a6d838dc5755b6f75d8666db5bb276f38ac2a9bc880b95d72a9256a490f29655127283dcd0a1126ece0d631c1f948f8284ec90a1c53494b523b0f1f39a409af4cd37878a0f478ea92da6990dfe11281b1090312adba700bb289edae1e2288d8978f9ff570c98de997d7e7e144dea4ed56e0466bc8741c9ff20568b407caa47f747e1f9cac565b32d2113e4d640723cafd688af277ade3594654c0f2498db0f5b4ae1cfe3a2ff37b08b7156e73a30aab8942b5778a9e39cc77459f23b02f266b571969fb881a689ed6b6a7ff7f51cc375ccdfb680718e5814068a3d1ecc92fff412c43b6539ed4a8f0553b950aae9d282e09f2cdd554fde050c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3a83f1831e4e680c9fcf5d26af2735d0425f28c1d9abef6453aaf16c059b5307","proof":"c03ef1dc09b7b1aef0227166252447ed9b2d49290b30fc7f903a942cd92d17157a7715e1a96305105fcb55792e75b2851a04b0abf2315c80e3f06c3859d6480a3e722c0ef3030b61b61143e774494d4a9f5e97e727f4ec35bf489474a4c630330ea06050c95274a0b4473af169d56dbda26ae66d962749f0aca8cfd852436f36176cb471f483d9409052ebf448b4148b3f5307590caaa6762a95af49a8a7a70f7fa21b715783abcd9d57528cf19f5d745b83b8615dbfd9d44277cf07a029e907f483c9ecee55cf055f63a48b47f009770f83433c567a180bdb7aa24b6c3e690288365a7e9809dac9f2fbd152d4610675e2e8a69a2502a65f5ab449669123754ac0dcb497709d5f1836fecc0dd5a47d6befa8973fb451eb0a1adb069ed827766eb86fbfac62be64c3bfb8b164846213d1e879562bcba5160abff5e0846e775d693474a7d60f7a7cb653f2694a83657122ae560498227a257d65a92186e98c777c20c35aab473e8794fc8d7be7fd57a0a50ce9269525f30861c8de3daa9ac5392506fbab9acbdfab3849b0d0d00aa42ab20def7f13ea507baae574a313ccc2ad645ecb9e069037b2c5736d944956c2a1a7750ed080c14b91bd6752a6732ff5441a5067b06203c8a0a41ce952be6687b47a9cd5fa665a2599227c7452840f72aa2f1eebfcfd3443ee522dcba5e10541c11b5d81eaab6e4c533244a1390e867f7719f4385406011d2f4ab3455b69e990dc3024881dee85f1ac2266539da75fa00658866bc1464e8b8ef13441d06077b73f9fdd102c690e36613656282ad7cc43e22d2cb2ce273c32aab525a9e5ea4a830fede9aa76d0e5c15386e200e3cd41b8a67554831ef3d443167917aa52b05d4bafeb41b363e2d6e0126549389663ccd5ba09ae19aa4758390a7ce40a98781859df4fcade37bf43835ca5cfff1804e4fdc803"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e832303d5f5abf8631059c6d037952a664bc72f72831496214aeb68594383e5f","proof":"ce79e146a62e05f495b8704958c0ef0799f9499b8198ce5e7e81211467d2f84c684380faa0696f0a920137cc5f16cf78b0d66a286b3e01507b05c04eb0118f4e041559d18b2d801d97c1fedb8965c5cad6d1ab1a9dbd656630e7518bd4ca1323fc45838d8ed9c485c14a5a29e947a8b3987389e0b41e7a4c102995d74f52520e8a7bc0dd6ac9579b2ee10a6c2117d8eeed7c52ea09c22c78db66662eb6128a0b801a0f78310fe35d0ec22bdd5380d1dd722cc1440c0c31efe6adcae74019b60bae8391038642483e0fe1b34af5127836e7a72790b9b064a4f49ea8c6bbbc600932fa48e40a8369ef4a18bc8b9622c1fe927a13b4766ada705e78e3dbf85810147462b9acd19c7c0a5189b8517bf55b43662b873adffab957d93765d17d57ea41b0dc1c0cfbaf86f1897cca82fd70e8ad8b0567278a9d6f2bf239ff4f76188c0b4418aaaed58391b4168a147d4c26e288253b940ba327eee982aed319c36de040641cfa9312d56f26730dcc71e35abe5773bc7efb4633d59e6b937090323c8223645a2b7af046883caa42334ada8340178bce893c5e01e78edeb9128d049c255c6034220a16eb33621912fe96247da927f41f4c4dbd8f55fc449aca565157235a3c61d75234b92e2d7db40490c7f21514272add9fd01f7a391edf0f37fb35fa1fac61fd7fa59c63869a3e5d47e7deba1a64e6b23c44fd7c7aa0cfc9968e93f03da492786629cd55c3e67a30bebc302426744807766dc5d004ecb8062f1523e94610f8a229a9e4f14fd18903305a8ae1a579efcaa1b6b99c78f010c55526e26a7be42d7ddce65216634c95ee1d5436df36507eaf020663172f072988f369b56f10b84de3b4bf27dd01f5e3d8e17824ee625ebd8bebf601fe2f2df0035f771b520178581c40cd832d6a89425f62d6e2bd6df74325d69752530f71d1530cbe512807"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"365dc566c5e0418d81ccb07a436dbc360d146b4ffbb2813ccedb1dfa5e79033b","proof":"2c524fa46b7787d43d434a7d9b331bc431612f7c8c3b4ad6d12b43a6976b2615ee8a07aa9e3766f5c064d34d0fd2e1d9775d904209268d0f7655fd077f931e3d6cdbfc02c8efc69ab27f33f77979cca4a2dc57ce615fe8a4b868b0f7f514556e980ef57bf85c023f8d9009327e8924b2a4f4998d3a710c0f2c28599b70aefe3f3ba9efff6b195620c16d1f558d46615e94a8290c926c7495f5a11d728cd999078641814b4eaa1e2faed8fc025423ea040ca2bd92636b0de9bcbdbc75e744f40e8f7248b93f40eab06a762ee40bde2711f6cb0a7ac54b2ec05e1dcf48ae213202ba8f51be5a2bede8f14660e7509835cf11a3bd453c7041993b46d4d0ca3a050370681233e8f173eeebb94b56edd1f2dbae0e7f086854c0128aa4e119870a3c3888e3f51a706f17f9ebcd39e1bb30803a68e8247653d2a502aaec9b4d42134c372838b24a1a700094dae8d12ea31ff4bb01d05fbed8026163cfe41a226a223b208c6b7180587b670138e1fb383e7d0a98f6dba927da9db90737cc0b57e7503957fe83f1e058172a79ff06f47b203be96960d2b0210e1c5bacfb28074b3f284212c29cea3a361f7f52e7d81395d5fdb44af8bd39f66b9da4e1f047d4597e8a4876243bae4a1fcac592b0022b6300d6b841386c3b5cf7fa799eccf162c01749f50db61f7469e4f11c8e8f8ae5a16a40b3249de743d8cc4b4671fee82689d80c6e6d3e843f41b71f68e80d7023dc151b167758fe74fe9a77890c84021d6934eb292036d305ddfb026d2b420b672df6f8dad263a1adc5087806f946d3b4fcd11646673ecf005e21008e54a3eef79387664082ebb6a5a93db893e94d509691e424e614c1c3d8ece871db7734caa695edda38d8e251c5d13381db059dc6f0f5daf8970b873f37703904e96f5d9a8daec9c65fe7f22440d07d51aa14a0ff4821bdd5a301"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f2678a36efd92ee2e34ded737c16246e51090ba3603b82c50ac73efe3debbf47","proof":"be3caf49a7c4ab0516541d427cbbbf30d581d32f62d682e899d26a3895240536b0b5f5e6eea47923664ff9957dd37431b90c5d8f0092a86a7fc0caca115640519a5e04d34ca1d6b768af75e23a655919562cfce81b4fb7f18b91b4c52e7ffa574e6b1458ae2db94f8628dbd5b508687215482f11e9fcd6b10ccc1cc87ed2d11d576f79248af65c602a363e9f8577903e017ead72936bf078440cb7ca48f70f02fdfc4f639be6ff3bb11c5f4f7cce78b68996e1dd27cfa20c8cfccaf6dcf2fe05f4d248e1a0d16114bf3f731bbb57eea3bfca425797086ee3349fd5c53f6020011a7a4258c1c8fb4e3b9cf9249ebc42b3555040607e9b75e1eaa2d6a4efec9805ba1b0ac6f788296d729f406bd84bedc00dbca8f82553aa30f4762bdb4e56fc75766fd93bc320bc4385c5529b3cd724271f8f97ea39d488d34529e4861763ee746a1751994681bdc074edc899d56b09a3dfc7f08849419bd81551d8ef2a6fbd3412b8bb358e00f9da06208fe30924574978e154ea087864ebbd9d4f83f209135a3a845fb774e425beec83a70eae7e088386f25b76bd3b5ab127ccd1927febc82dfa35882ef7aaebe62cc017ca83c81bf9204ca186c47d2d26d676ab683b8e0575d66ecff79db49cfa90930a3664e44c9e712fdf193be9a522fbfe1cd58d4acd7a7ac9757fb03d8fb570c438b2e72287a698e348a2fec99f6ed37cf3cf33d78e0f2c5c98233cc981c1bf98027076acb5926260f115ba85a23440f8568331adea175c1de3e49313309844524f97fa334c7297887bdd3cb3799251b4c6566a402c49d0df50f76e82f23d063d2f677cb94072b28e4c7734c532170c6cc0e6a57ba32aa35695d4da47c2aa497ad7170033f353b481b3c68113196867d1c9ec2b1a3e0c1258dbb1a0b97bf39079716ebb4f3ec77fa59a50bb4850f567379c6c004b460f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d249c323b12f029a439d6664735fa274c891fad8a7f8d3cb393855a098c5202f","proof":"bada9906e6c75dff2a18a656d8c68958d07cfc8c2a5c1affe8e1b5a4be55786dbafb9654297147edb65df3478b8e60e2cc43f5745348bb7444229e42de194654dc3e9c97d74007c1b1a976f2a79f8a98546a49c9f89f33fe184d6c7b0631e12830fe4e3f69dadfb6d9411c181933952cc1e243f715689b12441c5ba3230396408ad78ce21df226ff8a206ce7c8fffe6a87772966f5a646d540d681b33f5e1a08119cc0d158111d17d9315e1a6a44e2ab13a1edf2cd70dde3ffb34cd9f4d67a0ddd80b8c3fb36969ea183d1d2bff81770aea409668501df7dc8792e0abd77c0012281b77c9d8b95a2a75cde783b826d7e641d92837e3660d5a58c61de7527ad7944c1e2f7191e0319dd8fd514d9d857d115f7473aed5a19bce676cb913ed7f21ce6ac38e0f6c5a39be697388c11233f9c003b7c0baadb955d34a33e5d6f81352a70094206df121fb5bd25e3268307af32426010b0e543f65e2e10ecd10a708e58d610d26031494ae3213afb64d872a48b515964071f0d87bfb138dcfdff0ae756d8617dab67324d7d258cdac14f81db60412e7a8602d55b2da4d6505ac717bf7b881be7064af5a3fcb447d9afbd1f8e8e9f8831a938c15abed62a2dc70d2b9b497a6d0564fb2bba60d6d596a3ded5b0cc8cad6af2c566dfe6ffdd66eeb3a93b535082929fc85ceed6b5c83dec24d0d56ea594cd831c2c5c120fb346f59cc5fc7e6ce2ae8d0a8954a8a6a805914fbb872ad91c9d21026bc6227a8bffbbbebef156fe4e0604a633612ea46aa0610924384e41ae0fbb80bf61ca6085ffba75c4b447ee46f34d946fb2ff7e25864e735154216e70512f0a4135be0ede793355d8a8108fe9c14f7406d5122f42b38d3716f26d0a8db71d30b536752ce5e4a312be2a052adfaa9783c14f34b3b82fff781032a6f9e60a692a172f5e4d47a5c056335506"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"86e6d5c5dfcff81f63a7852e592d9c90a46b0a9bd05abb876f00129d49379e0f","proof":"72b29b36628137d1f0d69c7d432d9ac04929ab26d4f81387f01348a87bdc550546fe4fec0d99db39d4fc3c3093cf8c122d16dc65cea7ec218a0f40cea6e16f7656d5abee2217e8f893151022af7a7cb25eb1db3c8fb615122e04a928895e3153c2e88e18dd553e83ec0e14df66ad639d0108b01960c4022f51f7848a77ecd77ed8984835cadfe1ded3d1cd9b9d58ad5ba3b53b1347c131818fdef961ce0c270233c8c85f26283aa40107ed93c1275fc60fcab8aefc11dee43b2c740bbff40b02d56b699ccfeee4641b21159188332c673a6f1fc75bdef1dae7e16f31fcdba3017846a26b23c5f43177186807693861efee16148a28c282adc7879017f99fba706851fc849aa9063df7079a411f4340d96f67ded0067992a1597284fab2510508be8698cc8480ec39031573159958b855dae5c0c0a972ca7e7a1ad80ecb57772b32cbaa07da3ee2d1cf7c8f98f6d42ca60439cba423ec976d899d38bc7b5a64312ae5d20e9efd7ba2d97f67ca5970c6928d1c4d6561b8ab2bbdfd2c6ce04a1b4b4adceec454889f85e501a410dc84941d42b638b3cb66ab1eca812bd1ad2b1a230e55e607115b003fe966e7d8f8744b44f8ad30fcc09614d2f5e6dee77faec95374a88949cf88f6a4788b67393bb168380ec3047b3685a66326cf7f14d84d0f40ca1d76a23ead97837a702a0d7e8d07c76462337a918baeb957f4cbdf23c3912d6cf0385a809d6a74eb59f3bd5e0fe25703a3c0946f2653a5fcd8bc9be8a44b5a42f54a9086d3a814ef3d1f08d504c9c7e3fad6d85c7767d07fe34b7a59378322f4820b8d43fd59e2235707478af795ee4ff554843dd002bb50cff74c9e19f6171438b688b0b99796d93b27df8d27acca5130f4588f03a401ef7f321fa921ff0dbf98847c037504f0640ef1b779cf38682c5799de964b0908cf076290801b8508"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f0689652131c005b8d949115d9f1697e3ea76336a0c386acb8e90147811e500a","proof":"9cad2bebe3d9a1a2ea5a33d1d754b2ae7b0eb3223c6eced3628c803d6327d27690a0e76197f986eb6fa1b7d173ed18b4632be18e5e57a7164c5e6db07fb4416b825eed4c3af5b34c35919c70057e625053186a2212a42677f6b7c029f43dd346a0915b036e23030c3b74176ac6ab11fc16f307b17cae1b40919628b859b4045d5afea5df6abc82b1f8fff577006e5918766ff01ecc5d3df5e30e3aebee293e00d7597fa6afb9e81a96c04c9fc8427204c15398a9ebb8f390b7344f5c53b125064166dbc938ef1f0fc312a53ccfd011adf3f583522bd59d90d8fa6d4e35cc520b9ee45fd8f0ba4788691f77553896da0b757661876345e169869e541fdb9699036cc319d7492e092ae15e8b73e7a85b9f91c4632aae8abff5c2c8a20deaa2543baafa1929af7807faeed144579495d937f99a0c81ef9708a4538854f1099ba64ef26d1095d99a2182895dcdf7a1cdc7fb297f6c19d85e16baec8cb1551efa8113c8bdb68aba54fbe067d332a944e1022d5421cd2fb7cf610fc138f78757144464f43f4c6823d3e59bed62d5103b4647e17614f53da8a633c68ebdba23355628473ec972dee683ca836e3fd035cf5155a1f38f3e22b0c0dbf25edfce6fd1d96813fcc86f5d53b2699f0aceec40a9c2a5b016e556f35f1f84a5a6a7f5ee0b763e375e2382a4d253bd7d919afd1c09e55f20c44064b5626969214e69cf9819e10b23ecafd0143d5b16b17791aeac88f441b71db120f3eaefe3c118e4d04ff644584806642f3924d6c80fd82a5cbc590899b5c6a2bf752e1842d5553f6b3a3420f3479ceb7f5d3c53c665874bba714bd989a83ed21578c341087d4310ee1d2492ec7330f50c7230f7aca231690eaf577ca89e08123c2cdf4eaf0617857da423e9f50e6ca3686ff0a9032c2783ea5dc10a16ec5b8e583167788ea90d38282cdb894104"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6485731fa5a60fc2e19e57d2bd12ff256539192cc516d7886d77750cf1fc4c25","proof":"d45a9099351b9d64c8c81ede5b1ca0fdbb9d0a8bbe2904a658d50955ac7b2a48d0371d7264e36ecc6ad1d8006ee730110fcc22cd952ef5cee26dc4f799054f6d184508cc4e3fb03c599a5167cdd78a5e3658815ab1a5bc73bd77f4985385757c6c30a8805bf420343ffceffdfadf00ceed562c8fd21bddac6cb90845b18bca7cf87deb21a50917463065783c71319cae1d49675b93b91fa662b8a85b27bbd1074e4ca88ef64ca13e01ccd962c58aeddc226a3a5e10c8da7e411ce9185838180fd44911822d86b1b474873a5fc34fa5733b383f5710f64f376da61c336b47660624059e6173c59b9ff4d16eebc2cfbb6f09b3b48d5c56884df38cba228474c15098283cda544abb766e3ff02ac9512f1c0339c2b6856e6630e23b5e46deab336b621741e6f5994d5e9d3a9b2cbefc781cee75d666d58d2669c32bd9367dc3215c2cb44b38b309e80098386348a13b198ca196f33d222935129dbf3c01c1ebdc3402e094a5f00616eedbc1e8fc8581549aba9384cfd07c658d50e1df6637e0c83f4cd621702b37318788a4ad3dfd6e0d2508f5ef48d7e3dd783cd6fbee83ff7443da13d2fd807d9bde81332e5d88a6429e40fe8c9b0cf2afc26e8d244b493d9644a666ae5dcaca15b1f5ef18dec055a73fdd06744d235282aac8f4949cbae1882eb22322efc5ef3695add323c18857d055508fd39a8426681a24f6a88351a8f12b80197341a6e3554bee396e4d51ad6a126839a3870fa637657c74105ddd07c425e8a6bcdfd2905498c160dcfa10afe7e13169e02014010ee320253655b60910541cf246acff2470af506dd07f3d6eeb2e2155d09ae5dabf5cf646a269351d1f0b32b9c767841ebeea0697e1981e7bfb2ec78444473e60bd16112f74b8da4849015132df932f6e30c22695a51df9c953e6a4ca21ccaf0da743a964ad58de1d8009"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8676bcdee0c8835d2ec347c85b720d5879a39fc54130c944465ccaa2ff442c07","proof":"30f34eebda119f6c588a37a71e4c44c8eb79531278f021f72e658290e1115160b83a42c80cc7c8588b813c8f445481202d90710640262e0233ab6f76da92bd4c36f1799544ad600f5fa3d1553407a31931b30c78a664d919e2bcd24d68e64332f848ce166a17b08f2c1eb0bfc222f248b2a7217a34a8c41903df3adf96db8a1f0a485bc5e1dd45e7c828d446e61d1aa27c6f2571f23ff4121aa8700c2ba6b7075c2d8324ec1d96e8a0e61157cfef380b92c3fffb6f638372c04497389d777e05cd004bc0717c72b7b7fcbaf08f3c2f82b3dd685b4e9f6523b8efead0f3cfe009120a51365ab6ddfe363ef4a4827a94e476a5016d0eeb15dd8dfc97a8668a8c601e7f62d441b71303d79c0de67d9f1c257763174bb8164fdf9d810785fe979f34d0bf1c382961b9033ed3f5ec5e202edddd2a4aeb90990e9edde37875edab083ea8f6904fcd6248cf2ae41519270c26782069906f0f7db485bdd44ac51d537e728e7346c94fcba9e1273bcc9f4ebdb68bd3f1e4a2e676330f56ddf6070a949a040a041f10812be4fa1a58de26f35391e6d837138d3a83927c98dff445e4a1a85ea6600283d560b337c3e507554975666ac16a543d73c85f84cd6aedef7230680bb0ee7ce406e74ef1116c6b8d7c0275f6636fe7a9cf9e4ab8cc7d2384ea9e6267c2ce4bdf8140f0bf0c9cd8a2a7bf7f986cffc1cf9bec94ea0206a2d751ee4424326d91dcda57dc9efae93603e31873925373548758da354eed7d789b63a5a914e6eb8f272231e53e1ad418bb2a041323720a8e3ae4fa1aad3ad6d02266cfa500fcd979d79b5cf8d657f66b30c80e9276d9feab4a74cf53c37ec501c009cc8450f742157166b663164ba3ed01694c708ba481e182a60ab2d629c8142896c3ca0c6955e04feddac21ca4b3c0034856375ab025cfb50e10ca2dadcc10b6446e3108"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"20e292a93a56b9c82c46f146b6dc8dd61493b6ccc73696e6953e3b07b825403e","proof":"ea46cca0d953395eb609628705397956c15858c3228d4a51d08fff5883991e5d525a1c98f9ca883cc1fe74a12772b0e29a61434490962efdef6613ed35117f0a88f574114c5169c22b3d5ff67c9b46830cb0b4c1999d0bab5150d80fc97ee669eac8d649277ed94820936810f61ac48ee825317980b63048cc96bed8976b0852b7e7efad90783aa8235a1eeb8ce98e8a4cc4af9578a8e198ad5ceb01fdd36e0cfd1bddee909865fd4eee6ffec5664a8dd032933a0d76710c06f92747dfb1920537b379912a0a8aebc345f7608dee2523bbdddfddbc329b8830f4a2b4805dc300f2f0ce61c1afdc40b1039baf19a1c401da971d4ff4eb4887eb9a5c018f98ee390c35c2e97e404bccebbb8c2c271f546bd34da9a410f29a1555a01ae34493d06b1ca0efc0e2a51025666c1c69aaab88623717f1be5f399fcb47a00aed6e9c52476e4ce173ab0914b804a9006bcae5e1a6b69a31d8e6fafb93eaaa81c5c23a6711d68904f8f4f0ec9072ac4731f0c36b93766a9001bea8a3fdf470ead38123943f34c8cd1baa80cf12a5c9ab657846ec37bb1a8f25bf15220557f66621fdc48e71680fb5d0f4cec9278be37a75b9364f3c9dd18e47487c8df901dd1b90cdfdab4548921ee33efae09a94b9c3c70c7dd520bdc6d910ccb6bf65e77f203a4a8c6c03c271293ae1c11ae58a12720e557102b938ca68e5febccd22889814f464a50e30608c2f235282f684ab9cf04959727e3b077eecc9bf7607ad253e5783980d5d0a107553d104b96936dc46290be284c93fdfe540d619cb7fd2b68c12a1f49660758042731b3eff33a80105ae2839f73eb9a5f7169cbde0254f25560225513d06130b8c17fc97fc297677d863a60ecea2c8ba73f5a9385130b9345b4b96aa8b98078d08129bc72f4a989364c014a62e6ff76a53c250340acd987d213a7335a27901"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fc651e585b66eae035839c53f553cbe8b35e52089a75a49f8352497e6b9d4479","proof":"d0dfcd845d51e6131bf04c0aa3ca0a1891b2b065a08a965f28ade8a913459e097ab1a446078c4ac8fa6a03bc82c88c51ea18c62320b1e2046dc88a25ed2c5f108c9b7bcc426a4412028fd31ea9b35f1bebda47385cad4e7d7c0afd56aa3df269eece06ca512bf87778fa5619c473030989699a05897ded977b84ab76b0ee88225f80fda8847128953187b4145b2cacc170fc36ad7bfdb6db607e1c5bf74dc309d3cfa5350f6c70f3978606fff0b6855f09c4534a407b25e3013697f656c0070b387bf544eacfd6abec6b574d9d8f774a4c42d0f8afa0eb53850a269ea01d4000124bad5e2e273b9fbf2ba6ed19fb9ba702352e29d7b32a1195b494591642e749d4b4358f55dae378fd21eaad5c6bed5947f71dbbc0e93d7a1a58ed7f7a67040baecbdb6256707af2330d3b71937ea873e28282a76360dfa4a867c85c50a9de4da0dbf26477b97f56f71b0f8b80798e2022ec7bb17d948a7bfbe98bf42ab35548107d9cc2ac042d8467d397d57265061150c74e5f471c3403b88b1964e5e8485680ceba1c254805657a77dd8c189daa8aae15549c599c31bb84420bd8a877c550de9f594183537ad23ae75eed82dac5f1ed5ab416267d728a1ce3b95bb2d4462f16038b4f523972f409ba0f8d1c23a05a116bd610d12d4092a4f3af8a2c0e6d2fe860483ec6ccddd1aa862972835e67e26be61f89bf17c4570c508335c2d342435636883b8c76cd90f3dc4fb2a9806dff065da4baf0948b59e08d7d173e824151dc9c412657abd69acf165f9d87c5e9a34ab1977d2e9fa5138c7fcc4a6ff6ad7294ee908ac5c104ce6304ca34e93ca05c04900793e638b5e046cc84e961ddcc29c521997d4d8790b02646639de7e9091a3081742be502503f49ae327f2f38ed0bba8d5981aee25fe83a09cf18fb5bf0b98316f97977d6a70b8f1a50ebbbf71e08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"645fc25d9aa0e4b99c06816a06b37f04a6d40c25be8959be6f4db8bec166ea69","proof":"7a14cbf081cf3bbe234d3a9249ded5f1dc004b0fdc241e1fb126c343af39b41566992d35bccdbc8e8a839cc567d2fba69ee785a91a6a04193738ff013f1bc501624735418e90e9668b4c7d1a3211b07493837ee4739d61d671df128553139c53f42df11227767e0843271f5be5d6e320f7b961006e9280e5a8d0723827ff975ea4ef81ea76b9b7ecbc0e8f9922df923b4cb1646ba05fc582cedc1e17ad2ae603cc86905e509029726b234ca47dd3a70790305337fcb7d8bd65f72d6ed19a750c1647d37430acfe1ac23e69de31cdd53c046ebf4e3f8726dad853e7bf8ba3500ff00f5bc541dd3e2462b24acf4acb7da3be5848a08d9e9f6585dc529e3c2f205e4a1e267c1417ebc49c6126abb82055e6e0a9569fe550b0e5e815d4d9d3cc671744073edb0d638b1988478abb5886ef2abad1119bb7c22a2ebaa2bbeed966085430162e0bbcf69a8397ba5b5dd3a1610308c8b27d3160c148877fbdd719970202ea210e4994eb91feba683ea9a43cffc4bb3fcf584c4334562d030250c3cb042a509b593a2fe5d6ef7b2926d89adfc389f1d74ea1e9330ebde8c9e29f131a59107e5b2b37df7ffc2f66089bba15ca94c40b91a4321ae44f9d82f47187a0920a45fea3458d2e6063be6dc810b3795d384e866d283020e2001ffb151f2562f8a252a0377b512659bad83f032e0ba236604e5b056443dd61cce699a0c8a4d1037367fc80ee1cd3de9aa2acd9256f92157d9db1b7ed4aba67b930b62159ea253c8b5e3a26329db748bcb38468c2199e584bff2bf83848e1ac3cde677c32291d3e002152a9573d33e9176b07aaec048411687646760f0e046bb0997f6fe37a3bb78928dcf5f0a7a4fdee94492c8f26c5a4fec552de46eb0a029d63fc35b4d7d4eec1008004a153121fa39891e51ffbf674284a8e333ad21ae7354f81c803127fac2404"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"24db2970dd423ae21babd3e6115f74af2e1296096c03166404b344a380fa026b","proof":"dea13c9588d548e3654246ca0f66f379e9230dbf1c15bc74130bbead6d0ca0476c1fbd914f768f6118a697dd162ef5ec58e92960b8a132fd5e01673d49b1a30f24d2073ec63b7e0ef5410a2e78945d18cc4ae8b32575b2051a1fd5e6519df253065d0038034b974707c027b5110a47c24169b257f85103d907315ed703ba4c122791b8369e09f8f78fafd315849627c54fd6bf604f2ec554638cd7f0879d250b1d47f5d74bb66df1be1524c1a249db2f836a1e50d2b4e065490c2538fc0fff0d9ddbe2a50b924e22f8875894445c0fd62a93e410c7cbdaa597713be3615a5a00b08d127561b757646d3cb4843b93834fe4c971f388b5a9ac519b60190f5c190470bb4468206cd165092701585797a4b4ab4feaf63aa17000b50105b7f7394d568af38efcc1408472c062622e2ea30570a9724bc8e984029ff494429daaa772186696b8bd8b08b4264be1723f74f39a9bb7ecb6c71d5d8828355b0f99d918c1592241b8cc12bce004dc77e91a0ed5f967f1a2cb0743b6269ede1ee125d4cdc730e81c860441173afb27a9039b4b27aa2c2ba63986f736ba49a94570ca36c117073213ecabe0d177fe9c27eadff8a29682c88c7e3d6725a76f4f7a994deef9a5586edd227d52eb7d484ccd00d072874151524f49256d7eb3c828322a1c9a1b41010657bbf354ad833ad551ef046b2971a708487201acbe88ba051d0c4f0e5b1b2162859e6bf821be79bf635965c8d00b530bcb7b3b5fa0808aea71b5e9537877102e66fa8878e774245659b04bc8f204eead6360bb4b2a9062308f6088ab59d176b075eaea31d977ccd46f30246f70edc9d228d49f78a9905528cdff091f5fbe7d6edd66f160797c18d09f0774d54dea53505a041733a0ce2e39825ae5bf9790049d3f5137cd526cb6af6c31a39a58b0cf7383a648c1e110b4e6ff768b14d1fa01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"220491f8d2bde7c599892c9231f6d66c9a8f0317a22363e087534f64cb5e442f","proof":"2a5c38b55c2d852556498265c9034a429b302c7eefbda5c2532f1c505e55c0480ae5d2e9a44d1fc5a813fc345677c842f8599a03a86f5a3541250a2e7ca99e1f4e44ce5df6b28756997865af88eae1de8ab03cc81db98f07f78162983dd0b459e478d4a5651efe6d6c3eae65ac2c237b67bbd4ea265d9006e55903732c26ac5e0d72315d6e4ff1c6e0c55c39402b06068bef1aa805c70c2a1ce65a187b0958041b5923f946a954e9dc3c7b594d11c00ed53d2b3ef90fac90842f944db6cbd0004e41cd8f6163d581e634ae4ecac89d3b0fb9b33b0d105a508e6b6da8248dcf037c88f98cbfaabee13c655ab1bb1a5a2a3fe7570bf969c1740eed9ad73849187c7863e0b786beadaf27973dc66e048cfae400c110d282eb1de5824c610654d2598870ef77a9a90aae2fb738ac064fa6bf2c2367ec5536ea68b7e0781b9ff5555626f868bd0708032cc00ab11df55e285e45743fe03d099d7b1237e7a7d080181c5a89003c7ab87999d9a43172625cd19334d3cbc611e3b3475c23cf4c31fb722334e7724f480503a9fff746e818ef05a7351f4b996e5d811353a29e9c46af6b29d4cbcb3b82529f5b0aec7ccd9f2193c27ad906431d037669357fbbd1f13d6433f020951994a71c01e7277a13ab4df384d52a3d3cd53465b53c0526f4bf33ad05d66e64b7d12151bdcdf905b8f3f62621cda9966c6a328bc751bda3e9f3f4004c9895a096f9ced5da55b0274f895b29f04f6d1684a1d9b84db1930c9dc4f33675c20406f076bfc2cf433ffbfde2f8fcc032ab2840477c24e3afc385900b1e6018968711655ca4fe1eec9a474ae4c4f8d8c28c257b56e825ccd6c7466f570e7962625e9386bc8d85b2c03decad40cb0c9ba095e5c1e1fd5c036604d85b3999670c45d3845047ce28f68a92e3dbfede8dee9b2b6d25b3aceaacea58a795a66b3908"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d0ec1e49823d3529175c9479ddc5c451bc60d313df1ee440882c8766553b5662","proof":"4e6bdab8a9622489653be28b09613b332aff17418d8604e4da38fad86c5b526004be76a97858c72c765bf72ecbd914c20579a3d38babafb68ab8b5a67050e20cf07b1113a7b5d7ebd29ce10433f1e76ca6785376b59b31a823cbe678c3ea7a6ef65633e56744be57ef5c85e14dc6388d3a875aafad2c6812b49a92e98a75296fa1e2bb1042ef653d91f4eca7f2f9a14705aa5274113905fd062201be7934570c4490ed73f29e5eed01ab6a948362f51cce24919f3998a2c400c33b03ab577609f8657ecb3a18a213e8d4ba57cf8da8286f4e8400641c5c07c2571eb104c4fa08c02a3d659bcfc1c87f039978cbceed7a5fb13594aef5ac03779106bfb36967714a5495b2c40d20286d51f0d89acb3449aa5f5f913cc9d60fc5ed339627d35763b085dd0f6e58c59659b6d4dd76f6f57354c6a1607c04f13db1087729584e453542133ede041b2946718aaab9714f1866e0619920ea32cd269979fe8c32ddbd2a2c195c20b6df8a92ef226034dc1c42bff1ee2e659da4dce2bdf42e1c6488e00d5cb038c0831e088506aaa9e56b621aec9ce4abc5d0d4e37340efa59ce39409676a8620b4549e1af4ccea904671e8d4278cd4cd33072205ab44925d26bfa6cd6f48259b03107cb2bd77c216ccbf141311bbe06c6a4ec74d6a88725fe29d54705dfeb8e59b92f94cc0a2b7c9ec55be428f387810c46e7770b3e4d62490d7569a252ab6987caf062911e9f32e8bc89481ef2184dc2a276c57e19a78aa7d83165978620e9fb13545780ddfba3ed2c1d408bd049018d57f0247edcee4e3eadbe0294b40ec6100d5938c11ab6aa0c715cfefb84087928af84656fc139dc6d24a29256ed03482e93b6f2eee3547a94586938b31196cce0284b2728589cfeae418f38b0b3a671fe63e96f8fa2105fbabe719aa5630b23c708119c06d91e5d0cb396ea60d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a0dd2d519541b2e603dd621fe6803cf0d735543b3a53d5464384b8187e962829","proof":"aca063027be6c35279f79994117f98199ab73ec9c695a4258d9cb6c1baa6e626c09afb252a9133b0b16f535cd09b5cec26e54af6e2c6f7d1a9927ae260122729dcfefbb2b6db736a9c74c17222353d0cfe0f3830ed833e3569d1c20db9b7b3396e0d6d2deb83eec7c13fcbe4b9b91ece31e41375090f9b1bb56851fbe6a71248dca82bf513fc3203a0ac836f313ca33da2b6595b449c0a0253f8bb1add5a8506e66e157a45bf29d7b9aa6c363f273956e19387725a3517661a0d42afb2440b0af32648c2a2f7c00dc862df6a11bc4f5dbda55c0561e698b09af7cd774021b20744b96f4639eae11b3043a909ef85fe6ba0ae8a83a96287f1057b7eddfc64182786e5af34d0e02ce216b7680747444505333622d3a2a278853e86de9d9923c85c2e45b8574af1009231fc47bbba773f2f693e0eb17547b74cacf90f3ba407db31ba83e87d275375d71434a24f2601530f15c8c4561d0dcb0d24fb7000c98de8546c697e7123e2e211674a05c893a76747921bfd2d2f949fc842aa1148d69a7a2b1651cb147142e7edeb76dbaf71702f4a66ab8deab00fa2a57f86d5d9bbaffa34784be9fdf74ed37881a32d85dfb024a9af99b25b0b02dcd571b2613a43c048183aee4568150d610808d97de3c71949c062c6307fa69ca4ac07d92f69d0be561620779dc269c375aaaead4dc8d9ab3aba209e5ffcdced1674e52604e728472f1dc6d6decbc0d73f752d9be176cbe620e1bcac331152aba27f8ac7bdbf06d12f5edea1c6552064e7e204de1a04acfa1db5de7d64bb722dec8da83b9440a84a633e10a54a8fa72706272f4842e5e7884685b5439b775a3ee20f2b2a1a1439a8e874534ea861626ad281fd81545d732087f8dfa06ee8634d9e23cd9b822bbac4e40f4ffb07291af9564fca6383356b2a93e9f44744f2cd1470c95d7c8c99a834a20e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"587ffedad150ebe777cafa26522d8b65c0f847cb4daa006a8aa6616a83188171","proof":"9a7c5d67fa10013ec91acb4e1b7d9ade192a5e442e6d91e1ef41ffc89d951f22b4b33c61d6bee9b42c7d1d60b1dbed12c826f4cddce3c2c3f6a87cb8c4283447627c903524d7c1befed477f7e24d28f2d2753a2991fe9f08c2836faae7435d5392f0b5a015aea28391f13bcc9e2d41039d777923a51f1ae5ee056c2370dcf948aa05170dac29c452cebd9cb268f6641797d941dc0df7de313b0dde442a6f110776dbdd8d99f3d9f20b07b886d19c2324a2c0d472862802ff48e3908c825e740d12a3ecb6b84bde965b317ef608c89a5489321d647deab7de3706211b10ab1502062dc9368f93af675c02f0c5fbccbb968fdbdce19b7c1c16d879e4420fbfba347841c422a0f38697ec66fd685cc5f9d73a07c3ac52b8c075401398da05c0bc635a8c8fd51bef0ff49776320307065332511d5a8bdd5ff7ccfd628bc75202211ec07be4f285d1b56f48f9dd071cca698014509d6ca9cf693b1ef939b68378f46084cdcca4a6a724d5b2313a340a3495735a0f219d5948bbc4ccb8f03e7a88aa175c18c4a172d145df2d18e82e4b3d5aad3a156efd92f7d907e44683eea3dabf47fed6687efe829ce59b872c9cfba3990706a99ae50b66d81d50d635db25dfee38da74a703a0ca8dd8e92079908ae4f97ea0875eac082423b3eaf216a18d69425298b204fb19c02dc7e9cdc4fe5384f7820f6fe9ddc1be470c08fead5b9ac58d6f4a0e14abb679ce579402fec828aa7aaa75a451f05b74c0a4967d2a619d3d415ea479792ec4afbee9f254edcb1c0562983da70dadaa57618babb720c7a94319012ef891d7f1a889eadc842b010142a6ee8edde13584685e5df4cf13f6dfbda26d449e4940c5cdb0095ccfc0e69e5942e0c5908c32378821909daaedd72ac41b03dd97ec0524689539559b07de072733cf26d8715f60ce6e2ce67dafaa556d030e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3a74c94a35f6331f81c8b5cd21fad1f870c8268e06e94581910f9c7e332d4f7a","proof":"8e91ef6c7cab4f74c2b8f7575e0634980394460c8c39c6e0d0c1400610e7200a444040824240b89392612bf5b33b16453f7872ee68d8ba5373825e29d1872c27ae95763103e09f80b3396cd4b2f2f3da9277e79ef098d8af0280128dc8128e3c02e7a4696f8970f9e2ccd91e9a0eb866fa3d3b0be54f60452eaf4067b12dd4581393c968666e510db4d2ddb212045509a5e0a31de61801fb7c0c69751ff82f06300ef6bd1029def82e0949e5d4ed4a3a24ce606662828951a41417e9a5831801a786cb80711b0a143f67b8baf4312b2b2f4c681cbbceda49e820071f8fc159023c4965a53594bf3b83f8dbc052275b90dd71b053746645472fb0b6d54e9ae448186e68d572eed849ba9268d8bfa79101f3a5b946ecc60828f13f20a5442f1736ba453f7bf40b7fa23e645e81f19855cbd0dad3c613def9f49e3f3a1c352ec471029746f3ea51904e1b35fca97346aff66a052ae052e1f4b0124666c2b6df1250e2749f3bcae30f7eb10efa70421cf8b4988c5d8f9a834b35caa84bb1e8833a3628128d567f5339d84526a83ebe8d8b070e767a25fe1d0c2bfea1cd3a625f9b74ccebbe9570963746608f6b5bc120c2e7430248b6f0ff95c9eb15de0b06a60c3fa06840727aace3b72a971ba4b8a53575e2311a52e721e946496c78cbf2bf1c648604cbc3ff1bfe7baaaae3eaabb22b5d59ca06c02f2e2f0e5e8b34f1d681750876e99d5bd7589e214de0b4959bb3faac17101464262f9319e0e37c55155d2a668c4a7bf603c2cd93b54ca854f40ed33bf15074f896d9fbf518a3fef2d6271370de95a02a97f00fbfcc17560f46ffc0e168319378b710865209e9d02591100b3be0abce91e976b9a1870b3fc47f73f7c23b33c6f7b620c822a90ea7ec8a0e3c063b93d13602ade1f740ce568ae109041cd93ac2430ac7c0d08fbd67c676fef304"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1887b5d6f0242db5a2d1a49af88a1b7feb365f2d48617987bdeeeffdef6dec19","proof":"e440cd65160ce9db6226adae2f56de23e752bf5fde0a8e448314a7f655c4f409bce908680d81c36b961c0b1c1dafa3b9145f1ce082097cbc4dfd088ffd5cf9503273a1d3f8ce74d485bda8718e64ade9c6ff2c3a702e4ca465f0e9ba89a1437bc03747e18c6f747db6c2073d35767df6a09e37dbed2fb25b3b088339e0b9b26778bfd22ce65ef7569609605297e84a2596d34a9b266b25e1c0f1a8a8a996d30faf902a57842a2f535bd9c76b340877051b5e1a30b95c24b2349fed9b36ee9609e13b51b94f1a3c1705e3d83d575b3779fccf00cac3bbd2aee1c3f21437a3ab06942da77a2e72c15a6ef7f1e451f7acccbda92347d98c8651ec81d9bd4a566701ac4d4d3688930712ecdf782a8419af1f4ceb6d8ebdeae8893943ee3ace80ee321a533d93ac5117b119f7f6b3635658c6ca2c1a2f60b0dbb0eacb518f7792d77e607f45646d301a5234f8aefa27bc870d96614874077c18b2191a8cea6ef8b3069020fb2dfcfa2880a797fbc57b2fcaa74fcf6858d85df25776fadddc51baf7374aa9d9f5795392206a72f585e962411e72d05db5635850f927b965a9a434b76c6a893049bedf2363abbfd291db64dbc157014e3bac41e4b9a9b60fc570fd1b78b4b5df580a0c710ec15bf7e6fa57a154a6ca8937b4e4bc1650b3ae8d8b144341bc502038d52e4b1e477de4967f40e42b929822c36c6469d1a1ed68181b21a4678452beadf35becd002f5bec3b5a9388d7dc5652c6629cf9e0f29d5a5167cc907686f09940993cffa4eafb92cc03fd21c3cb74aef84030667e0b561996598415058b43bdf5d8a3f26545278ff8660781327c2da97b0f5070547daad1f66907a0d3e26670e7ebae92eeb3b10736d551867424864e257c633a08f2689a5b5676f07335a5636b84bad5b5cac5cdb03a44796bd00a2b44b6f99c91f718cdab9978804"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ea3d76204cb51d2f70a8d7d52fb39ee92010b83eaad32285a03401c46ebd8e00","proof":"fe832b5f5b7de4c3c543b87cd2ab321787f93802f908d40ef3d44a74aa109e7212492e44013947125f7d8002476363f90ec10fb8941c2cb85701470ff8482d264ed6a482b4723eabd7c7b0a78d850eeefedd87d4fc49fe910d2e182df503580196fda07ea7b1e77da62506017ebfd5481f57b3103d1e5b728b31dd510c41100fd6f3b918c01cecb553cd5e80502d3a033cfd3d61ac39140856051f8415ab030d0b14a2cc4fd176d7df48fe6eeef79921b6e495f2d7affdc978fb0682ad40720e45add033eabf0f5cc489790532c490b843ed1992b99c649bb0d6960d9613de05129e10a47b3424e91a166cc612029b3b0cc8e1a5b4a8cc80f3e4ceab4a5e4456b094ae756999c41a48d30550547e8d672f82ea2129643326a691f8f1aa056f1384ab96af233659d48191f7bebc55e8f2b86c539c1ca7a444d7847c3c19c1ed768c08acdf49ca6dff0c18baeaf7dc892a871ab7535e2ef12dc337dd9acfdff35772d6bda9f5b3e8ce9cbfd64bd4993ea72b3efee62afc36bb8a89124bd11f29097625a4075b26c01b7efe7eff5c60161cf7e23bc553949db37cb6896cb8579a7c8af4cddc10d1528685a066628c02d63296437c449b0b22c362a5dc56ffdfda04845988199ac04b8b759ad6446c834ff3f761d6986b6494b2e3f0c54a674bd71258fce2836007f7dcc9ca01ef780eab90e5b7020eb3fe2cf189f2480570608d00642e02841d126aecb7a023874244a5c9f75076370fb34741e507c42a3adfa55886c0445d5783562dfbbfb1a0eea819e76b76c8f53210d535ee3b806e1dbeee3a984abc16ecee9a51b8552c9608aa98e6c2bcda0b31070e72236443c3a4dfd213101e8625ec80439c11cdad85ad842589957918afeb00a4c432f19905d2b6a10dbed408d35aa346952e11eff34120de6184641b6f07effd065acaefa2e10cdf0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"66ab8192413b958933b1981ecc44d926b8aa0f18a354cbddca1ad5df927a836b","proof":"fe8b84e466790d6bc1b3de2998eb297c5721427b66cc02ba4d81e5c738ab3a710aed3f650659183d7e2d6e9d1854518d3a511393756a4e1811ee7e053c15930ee60e0a3974397ad031a4cf43497f2f2bc856c122afdad0125eb647f78f1c3466d8e12ece869b99e64be815a1c82ca973845ee2c58b6feb1c15189b34ecc4c029f1b54e3fedd22165ca77c4de95db9cdabe309aeffd04d7e349ae200239dbca0f534674076eabb9218ae7827a170be9585f7e967256af6c39e81354455c86ea0fa21a1476a64317d96409c747e186a9e3615d3bc63ef5d4910930e8b06da10a091aec6ad6c010b8d9894347477dfad0f6593344750b8d1055f49b952745961670f84ea232978ba5e12e1d5febcc0266d698deb2b5199aa727c50e0e67237c122558049a6344a3f4a933737fa277c23fb102aec263a75f7983e0b57349de19ad4ed81b7430bba1c2a60d2b5a54a0d694e071ec2bc7df6e572006bae07dcfda7451a200e8be14fbc29f6be89b610d9572130f47490992dacd2729ac2d258199ca7c0cbb0f6464f30827a87a9d148c77cf7b17cfb42b70c5e32cdb052d748451634166d7d338fafd9b2d34b24627cda2f68d8a4b9ee19e3c47903e412a67725f221efe6df316c636d661c96b2f5b7a89df757453a07526d2f2cc558019a82c74271f0ad3c29d3c166f23b78c56cbb59d6b62c7965956702297e32b0ca3bb21d97c239ac17a6e2f6f50904367e4a4a83cdb31e2042ff29d1adedc80d3689c48d9cb6b90cba0f623adfbc5022ffe0998c43162ba600f32ff07b0f3d8083341e51f5a67cc96f70016b9673d128795b4b5e767b80fd7aefbe1f20c9c3eebf01f7d1d15666945ae2d143255b6588b862b7b8e9e0f0b6b91b9d854d8a536490ac15e4af20594f50cf96b85bde17a01631eb00f4afbd98d8fcf2eb5baa075f57f8d6ffe850a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4aecd56124e57fa8d3b5d98f331520aea17a813621fbbd95ec4d287877558774","proof":"d2f365bf7b3bd84ed72c86be7e22b4bccf4875c664f5f6bf234db93e968f765446672c1242e1765833a8ce2a1a5974f7a320c6d3f8f6618aef46e9c62fb77516ba789f1104adb0902ce891680523cddb09fa0f46cd1fe58e63c925db4b1b0b2746baef57e1a8cca3abb247a3e07066b12aa99ffb53ae9eb9a24b1080f67cec1e166c5cc2d292feca1bbf9c8fd23ebabaeaf5a67a214fc16f39135b4ff99caa05e448550851e375c330be6f5817573982447ee507ac66f9d9216b6ace0d87ce0a4d559a122d48a5259820a071c64e8e3039d33803a65ce2b8875f402db374ab057c70fdfe9666073f8ffc38e09d26125f0426ef747224593462fa17b7de979a23de47189132923243db31417abde5fdbbb7f13111736a609ee2cfd9c7e2c4d746b47f07b2666b009bf39737c2c00ae5346c2c6e261e1ee33147ed12d80710ae0f640add81d2f4464c776a0768abcfa5b7ad68e79c758dbd2519380a206a931416b4d84ff1bd748901ec9e9e05be05f8f230dd89deedafd2488d5714114196e023b0d61deb62fe142edfb951b4bbdda0bb7bb3042fcc8d4fab553266e65be812454c98b4732ad69f6b45b4fb24cb82dbb2b395eae9f7e5023fe0d225a1ef339d336e357edc3810069b524082cb5b639c972286efb84ca8bc83df03ff71ef3eba4e56e9d8a6c105ebc796a6c6a902c8a90e42b7f73e6456437345af160abe796e6cdab5f236929db7e7fc67d02327242299d905f052df2fdc78eb52d52b66c9fc0f6243f5caef35e1d154f54c4a50fed5cff7e529c28a4b71400bfbb4b44c59bb6a44aaaae710832756d68046982201ba7acd1b01f21ccd6375c82597eab7f5eb0ac062be49d2f3b83b07047a6299280aebc6de6d2457752a4fd67e04e604679409a60252d474fd65b6221ccb9b16c297936bab2bdc8a645016bdcbca14f92dd408"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7628e8c781a2a33dbc1a6abfe652054bd032435a50fd3781dfb8989d3ac15c41","proof":"84e8ca1f7f98901c79c501aec94bf48bb652631e0d1e1dfe5b0f226787e6dd1ee02dac2fb4390e5e11c61409ff58ba7d2979b4797925233d7c767b5a979d79290af342d260c2d69bea8afa0c36cded185bb664d5249d8b500bfadd643723a73fd0ae9b8b10d37a9ad3e41e121a356f7bbec7c3352bb84c4662b112d87de5365ae132b507582ec9bd6d79261bb7e1ef678008e356d8e986060490e8f213048b0ccf6fb6d9b18988cb5e4496274eebcb00eed8c5d1dfd0784665e211731049830d2b8ed5b84c9c79fff48c443d46b776df8b0dbd91568f3ccda63d3bfce96656090e1db19c82c03baf73b5fbf29683251438ff3160e7464cf301c8caba1f249c7c3a73b8276d7a1f59c99b5167ab78d1b568f023aba6c6e63d3aee38e6a0b7fd2eb2634ccc5c27e4135cbbf4cfa7571e1cb9b2a6f129b680765615d7b45b50e2265e04df1bf5048629a8bd46421f167a777ed694167006775ff8753ff22ba33c001c14e1fcd309220e87257e82a04c06305933ad56a09c538817421c6fabd6a07438bcb945b027ababcac04c1cc293f5d4a8784afe0339894b6bf43e8a6089ad5b86758766d2c112d04d5f25c8159483853abcd5adba41d6fe35402ce530683b474e6fc4bfcab8c44c401c16cb9ba1d6ce5d2feec46e65d0d503fb79cab23e1d32aa532399f19c429f34f4220f84a801acc6ac78b18fb42d3bd733f523533223579e6653704f8a48583adf6ee72815a69e321bc4c57780590168459cbce53f2023eada496f8caf806cc955e02875568624327167750ad6e5085f45194b28a6fb06300e7b9baa81ceee4e313e6608459867b4f9c55af03626aa2812f78218e50b2ed35b9e0b00e442276ede8641442ccd2d8f2e7025934ea776dba5acf1fb0e25034fb0ca00ef9bfed182ae5e112baf67b85b2446e81445aeb82c213b7de2fd2d00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0a0572948afb0e8cd398811b65da933b6b3ee6fe447338820570c15ff3fba923","proof":"86bab93b2ecd0d32246d6324761a027f0f5662327da858ef2a2eb470eedb7d2058a6d44bd3bb3df7db282ac9fd3076dc9c7ff6787c00de74b5ebd0f956ecc53b62b1d668b63e137e4a03870a3175455569bcf12d49777890c5d2c8ce278fdd4cd669fae051e503b7115e1c9d8ebfce5f41b0f51dd76970a04c63be691597734705fe2f5fd133477a78eb632a305c4396f4750503417f2fb0a252cda15efb0d0762cb2670c7663d854bf5661f7f0bdeced5561805b24d51c8f6919067bbeccb006adef08a9afcc26cf4c985c70477b7057b69d218d6234276fe3c56aef6d49a0c923a98c711cf58c47b595d449dfd06208742ced48772c7943e17209fc73533130cbf051753afb8060eb10f570681ff6d6026e86cf6fe06b7ae42f76d87d88449b477cfbc304f70cd61d0a981db1cde1c9ee5e520e5d33f94de5f5d42c69590138213acf02c7c3ae636e68d66b53fed576dc2269fafa10e10426cfdd59a562e2ca8b4c9172bb49d18965a8f2908b552ccabaa977466941ee16c374347c7991638beb6b95be3f30ceaa1840a0e3a32c4261fa2b859bb55316b0d51150a0f476d6c28b2798eeb3200ddb4a0a5f9acb0b09bef0f676e855f179f08e3f57e79ad556b3001b94065d09dc974b451cd76e7ae37a24a9862848ccd88659a667736701b3cc02b2d2ab69eb49caf58893260acbbbfdc459bf38363abc96829a41ab54dc2673417f615f62419c06f9624e4121078b4dde03305cb21d4f96daccaf05f0f922c30b885855b7264316bdc4faaca9df2eb589e2073ace6422e023ff155fbdc46230a8b4c6e76f25036adcb67f46578b02882188d43f643b504968cf5128906fd149775307c9983560dd27704ea25620e36cbaeb0ec172efdcf84b7882aaac4bc02599036f40bec8f1d1973b6a1cdc120980d859fed9b093e75b098dd32a2d14b07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8affc14ef63e3bb01b61e5b6bfa139396a44dbee56cc3d8611c5c9c2a4f1766e","proof":"5227cb6620ab8ff45e07be6c09b9f2b7594046abb44001f3528437971a893314fc6dc39056249c65935285ccdf336fb4654fa48e0b2420785f8e37f05a72d6131a3a977e810ef28832647d03b15fea3afc944797d31c70b6595b974d5d8b445b64bd647a787dd188c421a056a74c478118bffb2c6b9b256713b84f9b1816372d396ebff804be3ec5c4660be1aad1bc9ba5c4106a7693d1340e3362dd16d5200b654e062f26ab4fda0c95c9c612a25631d1bdb7ac5451492f16734021c2c2140a01725aa70f6fbad4419ef50721dba5fec467e07471139b570475f7dc807f45015ae4358aa978ebb45a72bf6b9fbdb3dd227335e06103ffb78289d1c5eb108a478257f9d9c50d92df3e4cedb3a32e6f06efdd95064585e745383d78ae3bf0473738d2cd6a6103a48316f2c6546342ec97d2a05857abd30a8b726531ebaaf85f3a4e56394c6929b4170ec3035348ae830fcc69b21b0cfca87bc7124da1bd1ca02a0ac77c3213617167477489c944b8097db1295e1d89e98aa087ea522cc5f12446a00188af75e7351e459da97c3343efae9866657db48a03fadfce0b961f08b8018aa0183f483235341bdc8ca7045db07e70f8e56cdcc6670d19c98c370aab41142e800175adccbe9419e3526ea2be7b30c911017f137ee9e01e1f84f576815f2584486693523ed3dcb5a45594fa62609e2a93178e6afdba936be383c08c8bdc1182df8f24f81ed57b0801cd875b7593250406a88a10533e77893329371482c7337cce76b1d7249d052d15c49adf959bf989b3935be4994ffd1ad9b8ef1d576f7bc46e1bfc43f8495ff0a95a3c109ff61d0efe2003e1910fd331a790e65977da0d63e35e465a446905a764a232c3b5133b2e4c35d4f2bfeb9e776ae22598f26e0a9e1ec6ecc121948fb3b44ffad605938955dbcf357eee4a634a7e9662dcf2b507"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"021d1695890b75456497f60657bb8be847f01e02f30ed82fd5b98a6a4139b54c","proof":"c69acf0dd5eae13c43e21509ed0051620107d6a8db6293123fab43bbaf8a150152fdc52cae191c0839e6307ae894c75d5fb8bfaff5ee660a005fc0f1a13df53ddcdb61c9f406436261e95d113ddf1114d4f585324dc9eebdf7f6002e93cfb83008c934dc6b3387993939ab6c60ec1637933adccbf87f32866826836fd6c23f40c60f581b14bbd16a028c27d2b3b3683313f7427eda34301a38b1d5d4db47710246d5c981832cea34f7af6bbbcc2f34f531af3010e2420ff81aa94a35f55cfb068ae27ebd3f7af448f6846c1bb1cf1fa7f0e64baa25091cd91e24d6fa3494aa018a3e819083b116d46515732e92670db1d34dc8999a3256e5a67cb7168f531230080caaca7f9cc1cb2cec4a57ac1a1a6100553218c2e28260981dfe121a29ac2f9ca2b71185c464038cbd3f99e6866b32bc7d7abd1c90e7554b44a8cd6c033e3ce65305e177d025a9732a297fb9eb77c187bf9645bb5ac13b34c7b5d618a6f050e208945fa82785e8c0ee3e1fc3dba170217e6b959a0058f15fb32c1eda86db21a27f48f51f4fba6448e163d71dde85b27833a2a8488cbeeb622567327c2f7c1b44bd1de7580a1ad4593fe17cda7ae4f45155cc8e53a48978a359225df12803291aa205244966079612a501656463d680f8357ea42b466e4cd65e6ce2b42e31579c8b2ebd2a95ee31690211d7e026c1f4b923c7db749b6160b075d103d1d8d7702a630678bf6116602d8b8f149f8685fdbed084c0836aa619c154d32130ba6766787f72e4a81fbcfbde2af2650dabb73157da2c699d9868a7e9a4083e72806b510a759d4fc1606509bd26f3bd33cd6f4b64d10f05349cac429b2f14946993af4c0113dc6c45640bc5a0ea2c1848ee3b668121a5daca21ca40032fb7e3c8eb000eaf18a8c4594d3fabb34a23bc3444a305a2c252f50fb751e42d7f17bba964e209"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"72dfab94adaf1e9ced35826f1357c4f7e4a91c75ad90296a6a55622a59bb5800","proof":"566bcc7f5306a1b39662f7e07b4634191d2b281827feb46659e9d94d2602e2306cb184c33f060c4272f2d2e469a7a0a2cb512fa14424842dae597ab350e8663fc0680bc9f18a320afad4bdb3381ffc635e91ad2413cb7f000d6d9c3cf3543106f8c23d8ba3d01defa432f70ca24710177c8a15ae5df0a49f1b7d2377bcfb7b1aca55d0910f9a59e1ac8864c9b7c1e5a72d7d5d3486778d16b38b83e5b404f80dcd93c88abcc6f062347e80a69699237fff8643ba968150bf0084b3c117f36b06453ce8d087a4f44ecd04c0bb3f2d1f92247b88d0838d8909cba56f26a2e1900d260148255893b2ca454220ad66553ad1c576c848151fa79216eaa98459c184087a3bc65124140edc5d0df5350e73cbf928b700677d4be74c13f4d3793945133a447ababc353f3f86652a8c673c50b4c8b95a96df58406906314550a254e4e4552e9de357d0cbb4c7bde95cbbd5cc32f446644e2527d2cff45984e414cdbed171948decdef24f6e862b9929f624118709d3c0ca9897b90d6e993c0b3715dbbd572a62f3aa4341c00a5eb449b967e2d9e7c5de823c70e28442741d5772ccfd9b4ac48db82ba57d397c59d1d3571c35578915cbbecf87e37d4b7315ee9f39f0d455ecb60abd1b07b859b48cfd05ce16ad7e67581e07e3be04b0d32564261281fb0e7c7bd4f9e5f077aede0202e6cee6eeccd1e5d56c2b19d051e9a45e047049141e645b2f8e2b82974c2fda9945449786d4d2998275bdc42b578d9305e21018017e06a19e98275c28c341e99d07135545298283a378ccd3e751c4ce1fc65be7804aa457618c4010bd1c72c80ca6ed3a218b5576d4b1ad61450de587eb867800f2661c7aa50598091f5f2e8dbefa203ff9b84d037d50558a57693a0923bc7a1ce9019ecde6c43dbb308fad52b8b4cb1d11702aa640dc9bc3727fda0d25d6c1ab600d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aa490fdf7efae5bdde02dd78f0318bc4a5ad54b0b4d769161e7eb4262ccd0f02","proof":"402503fe96079ba7fdcc23aa4281161246beede8d1bb32990b54aca8071bdc6f00d42d75a18445f783e73016d80a1d930c3429f35679d5908fd84c3144eb6612e6915c6a75476c78c5ce4b1c398466f3a831cbdb2f9a2d1b3b9c2d7a9c1e1a5904c9a6d5f3501a2320d4df3f25e72496d719c450179d395de4f330422756ea640c1c335acd136bdf1363f12a253ffd249da5e603359f459b20d9a206852c640afd7fe8721faec0b692917db891ed753aefdf7832291f8e027b79ea7d9dd5b9018c13c221d9978cf89f82e4b41396bb82f36a0126cebb252a11089202b5c520089a750729515a6870faf0b9efc32c4b0fe9a0e205087a6926e04ec78b822f43398aa4723073782bee944b6363ccbaada010d6d133eca52f91d3612c3498b4f90beefe9066db6b3f91e59486cb8288396023289a4f8381adcc210904a6d884f077ae9c52701382cc0300cb65d8ed2ae1721d489fde1eadfdc3debbb6314b8194790c6206d01690918046ea78e3e53e17d5998973f565e7f7d27a14eead32b8bd375a46b72f5bfacf05d2545dd103d463e619a1e9c69e502e5da056cbfd52dc9911ca2bd29c972532f763125d440d90c84676627c002c57a4006324ecccdf7ef07a52ed116536b7df0dba5f73c3caa9613f2361bf4398541b911cda6b92c6b3305b48a38375d9bebcd688539207725fa5decf17d67e259ddf2dbc87aa6d260cbc6700c0c7b63a9ba3f45019ab61fde311355eadd91da6414956fe374977beac1e385c1169d1d532adaeddd45e31c5813716b67a1827fbf7637fb1597dc352e2f37ee867e28fd41c8f9a92764027569bc0955d73efc649d8e19e3988d8726632094f6dfb3496695c9737db992edc218517bfdecfcd32b22223733b0c88b17a29b0072074e16b7efb4577dc72323c66fd151dc14aabd7617936fb4501f93dab277d02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9e3937464efa18653d05d5175b7db57acc8278bf57e5da395dac46e8222b6a2b","proof":"3e2ed6984682ff0242006affdc12ef80ccb156d8a80cd864a966e0f191bf8532461bc1905886ee699e5cde529575bd4f47df661db519e67cc80ac4246cb8d805ea164724f51ea35223621b752a7cde26ae3a32d74a94a4c956e36b6eb162bd6e4a95eae1ad5b673dcd965668e6677207a518ef9d759dbfaebe71bc171a17ef05ac79c2b93084bc7af67de6b779addd5704970b0bc7dcca7cbb73d94872a5280a473771e2ecc082bef1c1691bb642998120399b0ccb2f7b4c27d387381d0baa0da963901cc239f1a91717a82e985ea818433dbcb2515eab665e8bc153354e0c05c6382b7ce138e6bb8103dd3bda7d493e8b00d0333195bba729427994c4c60a0d84ba321c505b37f3120dd222f785c4b927a0abc09816c7b24181e9b42fc57737e26d8f492177c61d4068ce8771033cf10bccc85d4948267b319ce330ebda4a505271b3fb382093110b5c7c4d4b64994707decafb0e18bee782e26ccc901ec434b6c606980ab52556a8ba44d8b3d383589d5c92111c943eaaf7aff46129887f46a68d69b35d67fd975802632ddfaeeca7d2121d3299dd0a6e7a49461d56788002449c60cf989e4294e0d63df25fa19314ad4dea7589f0f02c9cdde3b5c848da5e1eb1605d71221537f954de1d2171603d63f1fb25d10042fa388912c5f1f9dd45429814ec67363dba5800844a9fea71547d0ee38c99ddb376b1094281ef5b1d6710cea309f2f9e213a577910c3aeb1cdaa7fda0f4c950fb53a782cc1f42c7da66be67cb90d183a6caa57cac28eeaa8118224eb3b47d5a7f8e76c6b5050ae601093e56f84a51e4e092497a8c05c22633cec2bb10e66de72f359a056f7198a49218b4870660d588bbe316cbd82e4e31aca9db441cd4a6d09748bcc2bc644e968701ed4b5fe0a9a24a42dc05d5603f6dcf888efe05bc6038d2f91c7dcbb4826efa00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cc2f880db51070d6a417822950ccf8164fb5a193d48410d63e2c9f851b9fea0b","proof":"96d623cdcaa3833af8e84b77314a2a3c04109e4fe84c5ecdea048ae1f8c5816f14576939c92147a3e67c54243e763d2a9a30e95074262c0de7da9c6ed246c26b525ca303a93e2f6171c7bc05c54fc0f9ab0e23394a7c4c3f9f3a847c3948a8592893aa90e3400103d2d67a148186d840652657e078ca758f90008d9e661ff347b637f36e332e71089ecdfa2d392475fdb97621a564338564885afba66cf3560272e342d22654bc19195050c25c741eac4afea4c436fb977d89759a61a5cd21079a6621deb9851b6c3d352be38f21a7b588b8d14ecf92ee27ffd196f351d962044c70a0a0372a4f8207e44f6b4ec6fbdbfaddae5852a39f2bc67465878fcd6b36f44460c5d867845df7bfa2de78fb62a21768dfcd5844d0800a0a0ff18b8d6e686e23af9b6c13b24510b63eb024c60fc9d8ec44323dc6c0cbd03329aee5517b2aa8ce0307b877e022fc761cfbafcbe2a3841565f9c7cea8041ac03cd6fc782f5cc49975aa7f3e2982601e4aad11973da279e9815fc260b193e9b8ceb628933a3d38a6616146fffe591ac64bdd1703b18441dfdeee6e93b3bd4c1cbf9631146d59342610c28fb74cd051178006613124dd589aaafe14aee9ac4e2b6c9f10395b4f1237ea54366846be244d8c1912eb98f1125be46b81d6a93ff7780ea331b39c77e6d542875966a756aa98f90e721764b42de114a86ba18cfac4fc2f1399f5b66f6acb37b39cd85686a33d1bf49da0d9b4c06141ad39796404936247112045090accacbc7f2e07fdebb9962cac6d61605901a14c614e7918e6dbf3f17e1d521616c8eb6501c3efe086510a168108299090d213f0e996bb0d407b869b612106c46cdebe4463e29743d1fcfad122d2795ab6884cba699216d3f044daf324df21370d027d48698df5d74fd832233b19215144f67a502787a7894a37e3cca98b0a4701"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a205991d3cb05dd55ab3c6d81ec2fad8f1e4eca69db672cbea7c1709c4b48e66","proof":"bc858494c46727cec07d29f7377df70148249fb8b8d6da27a2982b37327e5f2c369eae50256e28d467c86f0a226bae9e7666f8c52fea42333e8a355dcc8d0842aec6bd79e6e5618381a4721fa7167013b88e1bc670462602427abe27cb4011629ed01c3db5214786447111b9c0384fa6489ac6026e2ab7fc7215b30468c7bc180d7599cc51ea1b9715f23910b0f88c714d5ff386872e352be1098f992b8d210b9974109d832e92a3654c07bbddd978a9d108dd47c7561cb74b189e7cbedd580df3c39f5ed18fe1f8de3d16bb504d536940c60aedba8a89cb50c08c88ba351a0ba8ff1805b11147a98c71470bb1a20a4270d76e635d19e3c7957e7413dad2ca5d24657bc2632aa4fc31d0c421337d797d4ec1622e13ac2dc47831768905fee24d9c2d87cc9294c7e2734fd77fd50760b968b93b639bfa9089aa5c94442042822212017d35d8282cae66c9156f5945c9bd1036551516bd2708c41b4753bab9196e6a5da2b77d8f717ae075fc71d3f7aa3d7c38931cee47f7edd748646898de424b183de709cbb39cc02ee853fd25c01164f59757d8f6694cdb0aa5597540efc93cecf4a6a8edb8c033211f0e0542703da45e5b30acc7b2b9c1a3388f5949e6490a44189f8d826277119a2e4a5d95dd35c48e7254332bb4c68701c36afe1879bb130a1439eacfecd3b807a0c154edbaf83f7706167d6441725d250692ea7b980d7b0c25e11c4879019421c67a962f2b206b860a458511475cd73a327448b7f80470a02cdffaab557f75bd11ac83b0bacf32e0e75f3bf517a3c24fee3dbc6eefc57eec298aed71f996cb082d880629a78b2994eff0f08a5b32f33bafac3a85eff160f2dc22ab86dcecc8f1feb32632b405b8d6a8c1cdc1c0eec15505a9348d418302e11e37e8e7e15a8f643aa51c9ff9dd473b37ab25b45ad1c2ad328f4ea05b500c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ec38d5cf8efbd657d4ffe9bd7a62c311bbd584abb6004e2a34ebcd391afbb155","proof":"ac61e0c00629fe21d7cb4c389b5ccc5fbe924ef78adc12c722bea962776449342c60baeec8015bb2f0c5f82d5740337105509e50d4998dbd5ebb02b0f316a44b24c919c8872e54d032fb390870bef89d90b31f64961bac2aa44ff524cd370135d813bc66286616029d8b130c9391b2e7f5c5e1b5c5e3386ad3e6a9fea69edc4f7422507a9391f2a0c792dd6d0e6e90db6a6312d18bbfa4bdef9ffc838b2d6d0d8462188becd5d13c5575bef7bdc770763c21a228b97a3c4045a11ebbff98ff0bd3226da5f8be34203e7cc983760f7b07ae75530e8205178cc542b8c5080fcc0b343a2f5a5051cd180c9ed292870a895e7b9df2fad2ee4dc1f87d9813ec12c82712cb8db160a39e43404518c8c4b1c2673dbb96efb7d73a88ffff90a102c6c644d49f689b1003ee72e4732e3141f0fee071ca9b662018e7e59b49f6258c74ce645837768f118474e02ea1cfd110bf72489041a1d45e1d1ce4db19f023274864763cde4c7b0c4b6e2bf2938caa4ef6641e63ea2210973d44562c8fbca779ac0c59ea47eab080da1550c42e0f150161a04c037c8005012e5dd185a0d998b2e7a178e2bab6f4aa419d0f257671bf7fe17ad04fc1c074850791a0e0a20934a6a91f078a164125817e4dde31bd11539ebc4f447a2b2038fc2396e8e9f85b9d65aaab5828d0e5a2c0fdf83f770036c5296a26b14b105bdcddd133df3af54e4b62e84278d063ae98cf6de2cb4084bbc7ba140a20124d4133054f197b38488d3497fe182760706cc114834d70ff61dc98479cc61b632b57e461adaf848a7dad2db942b5161ad468bcc7a36bd956fa1cf35571cb99bd4c5a144ed576a45ca8798849bc9b12ddfd801dc400b37877ced7183dead079740b2f07c03893e3e2a6de37fb15580c59f8158d13fa18c8ccd120da93f6dbeed53ea268a77084ec7f25c34e33c2100e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e684959c5a87f942ef1d20feb1309d77649b3e238f01e1fcc61e6b22daecb074","proof":"1896e12b52ca9e715293a84bd0401efdafab7b61bd0eba85fa7b7b87b491722c1202b6305c5008d9f78d6c0ac0f573a71dc287a036e57ae00f23bc5c3c887111c0adbe1c781276e288e3bcc7840d274d85a0f4ab39991ab54892a841726eaa6ab88bdd2e58f4db870164a11f22ba235f833cd1cdf5a436352457e547ec464d57c9344a45f7c763145e70e5136cce5945c598964b45d815d21836cdb59016b60ad1f083df1cc93cd4a9e0bf5d2cdf7827976b77441ab0b367c7b00ec80c395505c6e891792731a4bc2449fcd2a5272ebb7d40e8b6258589c84dc6dbac35b0ec026a2be04c04975782c8073c1ef97a8e5d87c31da987fdf396865f0cb8c4ab4613f2de4e52745c75681c5081cca0c3f00460dbe624a3b0d8da7970b2a331a335195cbe39cdfc07ff18c92a35ea1a3e9927de57040edba9bd453290755cd9e9964a00a0dbfe578335dce4ac5aa0629b147d533dbfeef82d1e212f6362f42b1b5e147e6024c11adb9e7c3070a3115ddb9cbf2a575c06be07054a7c36aec0b6e0f203e28aa2cef71d617b72e8f70d139a4d380ac09ad8d4673debec66ddca4f751555aa0dd5f66aa8d78226758f474a60e554efff88e117526caad9e7edeb4340a77344e1d9ca6883abb63edd8246ffc1aeb40d92004efa17c9255f1679884abf9a5b449372f040953c385cb603f498afc4700ddc3c8e0aa5c0f83fc9eb939ed5f2561803a978e3eca8e601ca4d763c0878f825a98e32da65c86b7d475c66a3a90f4246cfb3b1ab98f74cf84261bdc2b6eea98b9347403790512f23e72850872a332e9a0f9f36cd994672277c7d073cabd5c9569b3549b2704d675dc45eea87d35f7cfd60ddf97dd74ebd856af1a3feab4b2bc7c91081f8068bc05ed8ed24f53cac0553391aa7f7a3edda08e3e21d105c6f70f554d9a0c2bc266837ac72fe6e2eb500"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"184c5c14a7bbdce3a298bd6637e06de47242dcebe64b1177c3e2589e7fe07640","proof":"3c149e43838c04d76445f38e86f279def691930f59520fbc91d21a205e171a1e1c927f4ee3df0ac3609507871da99f8a1ceb2715e3992fb0a01e9f54445c6b465ac302e0d61dc414368972980d4c5833c3bf75546768ae7f86fb3c1d141fda3c10f83d8a115f4d0d412d6f277997c6ef21408551289afb2f14305a70d2e0177a576c560d362b049a5f91c399f7e87458163c028960a0fd438993fdd9db65270ff62d582bc16a9142cf7fe69b0dc0f28632546d7ad2566c3ac1215e803cad84049398e878e220cfcf69941fd078ea1a07483504f9736533ccc4774933f7747902d83af89cbd86c46955c21af8bd2005a2720f906540f2667e01ef6f5703c6cf23f4189b95261b310d341302cc40b0247cf1485ffb1223bc9a5b159990cef122725e190c68f1dc369ba2ac89aa76fa09b270a28bd43ac74eff810c50faa7b5277350a40d882aecf1583c20690af174d9fdcf5f5cd75229dff165dc14f30df13d6a9c6427c3a9679cdf3ca74bf864913d6591acf8aedb04954c0299fb164aa7415e7ee029cb6b6a145a3d4d988984991441dac328ba238b6376d2c048c6fc2eaf3e5c3e76cee097c6263857089b32ca4e7a76ff9236f3dacefb3bfdaf9fc34124662c94aa887a70b215f8ba9c474f7a6afdb04e93c0772dd780a3a13db4a7769c03c80dc7852b78a5a35ca6511dc5f82b3b07f26a28fb59f9433d01346e9f83e74228964bee2bd12962484f0c44c4bad21b053adf06899b78ef81c164f5ac8b7528c0971fd5534879b190836fe794627dedb41c49e4cbcfff7358c19ac235be864a9e81cfa5be304a6862d5526b399b9e8bad087a0b2ee0ff68875453f44dd273360014f00da1dd260317cfb7a5cb036f22d3324b5ef716ffa3207a6e7cdd6a480720a8b444d2a4b83bd9cbc5c2cdf310747d0e2c242135619dc23d431da1f80c05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fc1e4b6648022ea7be239ac267fab9ac10f39f6964854bd132c590466007a83c","proof":"20d7f69ea54eb6caf00f38296fe8e69616a9f00f71813cd2bd94f985b02de17428ae7b22cb415dd643ed459736a9ec89bfca327d0229b12f8e2d9ce25b14561e62be280801caf4a6cbf55fca31fccb69f36e874ea6c34c29cc1360e5095c483200f82feee2ece839fd2d968011d6c395301d1827798f2d91b97d3314764d56239156e9e2b4d13196e5517db15d52b85069522500a4e10e7505941647a6b59102e577a62c59c70717f3c51d19034968ff886f0eaf88641a3e9a18a93794fa06074818bb1d100c2f4f56bb86f85c1500706b8b82d9a8c486454f70cf9bc91a3e03b4e7e7613057fbb686866a33e05a7ec7d17c5e1fb304673c32b82225f5040422f8bc3f33e552e65d7a8e526d89be2507d2a7fd031b7d7b459c0c75b59d3e4240fe286c0ab8ed54e5ffccd0467bcd4d4b32c36177052eb81a98bb20c07fb1ee3bb4f6499397ca07745a14f3563bc08bf6726caa3d6fd53ced9e8d0ab7ac93336362ba90b9dc9747c4d01db2fffc636de0277b0f0235fedf21dd89a15f15d92f1572d15b4a205df10f46c3173a97e65fecbe941db062d06f34911a48393d8e487d3a5d74d44b864c7bd16cb89ee9b1ea055167cfdb7d15b46e604166c93411246ff024a1ed4293a5c2d1910ebd9d1d60264d10b3c758ad61aa8a44874aa60b275d16e371b971ad83fe83b85e1fc3f4391ca3192667fbbb3e29fe4670a8382f351572cee0459d1df85bac5f82705366580f6cd71c5006c29d0c79b1c2d131a71658624d213b78853e50eb278ee51d50574cff249dce2c3528a1b455e0d536fff4611c1730c6a2463265d340de33ec626a998d822d17c29c8e2a985af64757ab660181047ecb901187a4b7b3b0056908dc35c9e84c268bdf6d348256ff763565220bb9a0baa2af500a9d338ea3710771c1334aa7649bd8086b62f3f21b012f3fa209"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"54870d360c76989b6ce92f1da31a2a95eccb4f31bd7ad068bc2230567d7f2f3a","proof":"346a5d02e6afd7f5e89d031506b5d2cdc17e1fe9b50f453e69ed038dfa0a7450e47bfffa683243592479e454dd5d4e85b7b68394ddb8fb79f9ad13abe2e93f1bc0fec0d02a406f7eae02e253c0ce633e7a794c80fce62ddd0dd606f546374e0706af88fc1dc06e00eef058364652e11d8af7ee4b4218e6d2259eb1b7f821b838c910103361f6ca3f929b094648bedc150b88f6a2521a18d0af95dc8e60c13d067b3f0ede05c9b603017340538e72704be989ed04fff7d590faa41071bebf8c0e65ec9e042c8c834da086a348869c448ed94f3db95b7507bdca354c317df05c01b8365c5b4819ce2a25021c772edf70d5bb58f0510d4d07991c1a4ca874df050ed2fdaf7bbf7cf87d5e0d3f35233638dda787ca9096db6d38f83c383763191c193ae0515c8e530058e6d37df1a1e99caf14f6bbc3e9ef832384bbd39469426d14aa9ae24e711de5f6ec192014d64ba6914a50ed02d42be751d030ad36a2799b7f06cc6f492d93e04fefd0b1f16a23a4de6593c141145448855f657c488cea3267c2fb1bc34bde5fbdc1f41e4d529c1e334c851e900d4f75747b07364d03adbc012a9fa43b3df52224f01e611d167122f068de0434e2149bfd180a33fa546e1979586c9e92bd8faa6e3043563c88ac25be2e0204eb53d3c7b5d5e2e98581678a530a87715d2729eb2468d7897934d730ce7e62373d3c59d525e6daed6c6c36781490b1d8a9789c54cd8a5525c97e695db43976c3cfaf4a02962721691f0edddc65be9d2215fb6a3e323d52836fd55d7ca166f6305eec7146e547f8e48d0a01ee736689bbae384bbc2ba85d6cf04e2cf64fd673ce6be671eb3298e1bd548b16245fe1baa7d481e87755f4b425a4e071057dd72e6b7265658722f88c74e820f5ac033ec6f3fc108980aa7cc7fe611f4c14108515fccc1f26a6ace236de23416c3c01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a054539cc8730e95d339711229f53999d43a9bbd1bc6f9a10c5d4cf96d610332","proof":"0a45830d391ebd592ae786c2e7151c099a85cfdc371019c68924fee37d209576a81b8a560978a1b4e55f301ad2170aa22b9117a55b9818f10a234362b4c87b20cef271a0ea60d0af8317f1f127ccb7ae989db25a1df5ef70d83ab366ef5de4294228fd56c2ddc28271ce73c72383d8778b932f386bfefd33283198f38835593c49ad7988c5189f9cfe2f64ef8a631907cd95c63ec4081f354de99dfb9feab80b486eb367583c86183271252243a6ce3f5aa47699c692aa477c9edc5bf3738b04060ded071144d748ffc5a94568422426f9315acb5cc36a25b00ab0aed2588e063af212744961d38968c9057333b958bb4ab16a9d3c364cb2fb9ffb840b509e54628d92dc9af9fbee00e64136e43a392aa29a9ec8879a9a038daa6ffbb8a18b082e889ccabb4cdf9de26c4a6f4023ceb68f55ecdac9e633d4aec6295cfc6e2b79ae62105791e1b85e78e3bd4eb3d02e8d77be6352dc6df399aa4532f1a2e79714aac12568e1f6f53c604f48a51b565c68a9c78565b191c308351476ba9972bc0efc3ae7a4b7a40b0629c1da0d97661b69f8aa1787d05835fdd64a616489d20413aa98bbc2db9204e19a6b45c8878ebe23844c9203fe3bdeb74a1c7dd4bf17ea3f567ab470e8aec4e63e60371fe235557233662ee899fb02a1d9c22b83f4cdea01e81f5e7fd339985d3c4fa0b6bb24451ff20bbc616f450ecf574eac318418a66336048a5f748a2a05e7d5c719294ecb257f2557a9652dcf8241560880154c1b16fc074e90fa54756477445cabb0a7b23c4fee2c17158b943cf957491ca4aada2da4387446a39cbf89027111b52d4686dcf63f22d0fa0aa21c8edb99558c159e33f386680710ea45f2e05224d71e736b8504098bbbadcf4daadd12cfec30313d04c18334d221343ba2460af6fb7e9d060d9e94013e26b5deb71281be7cfe1c5d09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"185a550ef472e357ff49c202d22bbc6e7b22fa00f231ce3cd4f3827b7442ef03","proof":"1095b7de4fcde339340d100d9f3088b4e1885c87bf6b5af24079c2c096f78901a8fa82451185878c169949e9014cf68b279b2043caac2a8e70602bab567d976968a37d4527f0be9d62b3aea39b11f1dfe41ff6e14272ea755704305d9e6e5b75bab0bfda0864d8499748ab93f4ce73d723ee9361618c2f569c4ecf9645630028f67e4f8379e976523b7715225dc9e377d1fde1b54069e49b44cdef70378e050a2324e087d72256649ce99ea9951990e6f068e9af2f32e57896bb7c3e61a0ac07e142df0398a29ffae72ccdfb6113d50ed09c4aef529ab04903c889ae4e2b8607329d5beadf5b5f9220c2e38b995c682687d0211e86398ee582df8b16f5c3e97f6addfa2ee75522d768cecaa2e2c9415b64fa5b30f53f7bcbc96683fa932f147f560c4ba21ca733293fc336ece6558407609d709395bcd7d614e46a7178dc7d72b4865686a8a2c774e8e457545df14af05b8d9b1e41fc6c7a27184d3c2e833962cccb23360a922ef804856edea15de27adb8648a90f3f16678e110bd5770f537cccdc5ec4113c87d17d6c3c9ea2a815b405bfd93ef65bec9fba190f6105e40139202d9c63ad6a7caf4f6491d038b72c55da7748b1e723bca21c95520c9aceb16214d3ef607cf1774e4dd2fcc301a61b222a9dc12b6640eb0d06316a766befac743027862a66e0b351b8f8947ee326cb4d934ec9f753eddef3b76de8f4c4d8ac652619f45541561c98bc8e3d0bb9c9d6163af97f8871a10eaa2aff535ce6a0467d06f456c51b83d4cc9f5df510fb5d354a2346f3012c3baacbcba1aeccff3e792178ac7613c50cfcd5cfc595da3faf7b6b7d90d88c5da4d4a3c37023af0146723a5e43216ef9e21a0ea6c2a0feaec0b2d7f634ce3ea571095451d9e0b3f177e002f7b7d280a117ba39b048499c83eb4c4cbb0c0b1f6d65d52e70f9d9c8d37f2b02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"be61a2e29950a3d9ed1c7996849cd1ad0eddc03be035b61dc8b43140af4f6674","proof":"c0c57004bcd3831f7c5f200ba1c25c3b2c96fb0d1f64f24b805204be1bef1a2d5e32a5c7bda298180bb23f49b870dd1f4ced8796ab1ad0210f3d36b201bb21578240ce6674e5b61eacfdce05f7332b592919b24234b9a3dd97f1242c6b87b67e52eb6a8aa738c4de6f11b07cb6d20120b379bc136075cc2733b3b546efb74c6874b3c6819d1b64dd3176ef4c74e2f344a14676d39af31dbb5d9ad6b7b58e4109463fa50a48237e08cfe7204f979e43b48e57211985bcb15d4d01805a1e23e70cd962b6a754b1cbf74936b07a942af096f53ff7d85fbfe228fdb481249ca0fc0dba4eff45f115ab5528bf770f2d58045568f5942d39553ca2bad6ead6507f5507a69f476aee8004815aaf4558dc77fc4d1e4dcb3fdf5ad7e07818f38e1520f328660e9d5e297720620712760d95aac07c391d604ca7544cef4af815104faa0c222468cf3f02134a9121163bd204a48459da414ac79e42f403dcb53f52d4ef996e58c901daddb4d21558e36219315ce500c0c93d7af56ed4f7fc941754fcb22d3fbacefc7f124eff0a66305f074840851dcb05c7791d8bcc3d7b047b52d4d4fa23f8d987d82b40c917183f12b2e20a88548785c4865eb0a582d5d476203c53a61c869bb0e0e1f6788b06861406f2ad1747fe81606a4d80c1ca6d9ba2de06ee6f186e9cc7cdfa4e1bb79ba6117cda25f8e29d2519e5ba1bd9e671e17b5b3cb41c59980057bfa66cb8bc88599db4a9742081afd29f965e38f63e80fcbe9a494158258a18149e70ac85382e74b3aa94fdde9e7d03e6734d547837b6ee406a62c5ca6dc2e205ac02bf2e59f9ab94793b00db0b2a6111ac2db235d8be922cd96a81f0693873a1721d45b56aef27e2a3c4cf25bba90f52f6c8072c0c3ed4a838bedaf10162b5a6b199c17ac185fbd05f291ed8ca07e7fb7e9913e3250346d56136621e00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0a820bcd3d625b30dc27bf353a4fbe6dcbe443a5302c541e56eca4a99093b876","proof":"66be3a9286695746a8692995b9b98c3c1eb2a2e879cacaf492c60d0786aec36b7efd6458b3ff9b84a512b50406ea2a2f3ba2a508a1d0011fc5edf01d58653c5926ff2359aebe1b1cfb9e760cca3675884d39d6cbe18f96696c5bf211dd1cf26f4260f58bbe3cb314e33b34d78dfda8128969ef0267498dd995028091b88c952f9a511e96282cda2633e742ebc3da8c8834de77ce8ef1ebcdaef5264b71b63b04555228bd5a8fa90aab5444b40ed3d3504af79501aa28f71dce63fed186b81b0ecdea049b47feeb4005a06e60654f4f69567734c3264a7931b99a508f61861e03f634f86bd334fe3c1517226920f936b0d73c9e0586e5a5dbd4b53be620e27867f8c9438c083e77758b3060a07da0601ac24891506d33895a042995eb5240016ede5f32a67d90a8dd0a228d17048f962d98bbee8cb526510514161c106f56741c9c735872430e0223ff7bbaaafbcf8b72bea7622270057fb89441e605fba0067c64d0812a239e6cd0b110412f22cf5ec1c787d4df9427371a9389dac1b6545c568a4f10fada228913ab5234b45f36854d80516097a6344a1b17f03d24bf843426868109dcd599052e740205791335377e3fd2cf65d2b2ae677f194fbd8d11a17ab4c9857d15afc752e6ea9877ae58ab733dd5cc8999331c634713702a2c3ebc76587d0ac176f190853411a380929d83bfee4106851d4766d8aa2a174b743ff7190a23b211b82b36ee3a004180d6c7959b776dd96832aee8a3f34bedb3d361543726c3714a1f44bfff982028ebb3ca0a058920e4211923e28b4d374435e35274336850824081caedc3c4b0d9178896573820564d8393b4e646c4ffe7998a657106993a44a6e4f3073f63099a761e3618b6c47704faa173f89b91e32508d7845603a83d90e1d78ee0671eb3cc99410056b3d54709d010596b6802fabc73d25df40f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"30d064c667680852fcfbe37a58765599aeae8d4e12bb666a63c446f4ae194110","proof":"a630e733dfd7372e9744d1699d5493c1b44a2024e33de4538222db80a8f2be7dbc271c663abc6b42e25b49eec372981695d1e9bd530261e2f7f96ab1ec461d0f4cea52c8c61db5d5b979dc53b96eaad760e67f7eb34b968478b9bbdbec3a0652da14a6d65d75203db225529041afb7b338f675eb1d4a6fbdd671ef2d2dbc3c47b98f8d153aaf2ff7f1293dbf99ef2ec34128a82c82b4a6b112f0d09c1948df031c10998dc8ed0839c8e5f4a353ab3a2ea98b1f3d92a01c55d133041ea04b8f057adf56c96a996bc7b2d1ac4878949b6fbb06e22d7b04184c1b8da66606e6f0012683113646ad73bbe148780c0b8b24585e9506eabdc52b44818b9e628ae28203908fc6bbf01b4bc640569d3719e86d3bf6609109f0301f4533fff1c7c16eea4a2af05f1cd7787f239638b92f22dff2ec1d590efb8e8efa79982a45dc4c7aee61c206cefc7ca8d5a5dc124a85b8ab6c884b934d2f58352a92066a47d2cf5fd52798f0ad0f387fd8d7791acfbd78e7a5b3c43d3daea2780e0d41d8f315957f5628d8e9b38055ce566e9f01c829a5b29bd7ec4e03dd4617f695fa9d6bdf40444d36a48564b8efbe1916014e78212af7c698e5a45df6fedb2e000dfc9ea7dd57eb1248acbcd70bbffad61fd5e4812c96b155cef9c3b7994d66ff821afe282b261976405cb31e7d30ea6815c9eaf1aad9f5677ebb0e5bbf2c376dfd6feafb6fb4b2637ce0f1b82fcaddfdd79b7cc9afc67f07a6121ab95bee982e63363326cead697aeab71f0f525c8739918088ffb439e6b6a2cb5a8c3294745d474235cc86be7d6376acdbaaea620738aac0781fbab538ee90161e16f338e18a20bf7a9b83700760deb91452945f7c74102293c1e3adbb5c0dfd6839d6753eceda2eca98b892480bad34ea2f72071c7cbd4fb2edc9074dbd72ebfed500cdba14d3bf12221d9d460d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5a1168ccfa245d2a6c3c159ebe31cdd594eb6017e909246da3d34dcec280523d","proof":"ca93324a5eecc1a626f226cf3685bc3429c9b9f9763bd76275f75d3cbf22b7785861d26240f9f58de082bcadffb321ec4ba04723cdcf3e475a54145826b810001207ebf964913cc6c4edecea783c62b74f301acc799741b935379d469e1d7f6ed4993f39acd78e57ccf244347c6312f2af19e680598c554b0294cb17814d1e4bbb6d7427a6861d26a334ba190a9a6b315abcc483fe22c8c7b74084aeeaf8860c0595177a1bd481e6d8e5acda1907802b1fbc37c0be22a7aa9a8f3ad503d6440cc24038c0929d44ef3994132ef75175bd5fe4dd0a4366565189522ccf20142103dc0c58e5973173c8896c8b90b0661e5b39ecf6b018149a535befbf44e8de8b0f863f1157130e7dd7434820ca56952d261fee697027fc8a5dc46ca1c738a2a0550c48b0585653f0923f37909ec70a4ac635d94768a20eefe27ec13e09d3f6ca6bf02fad02142fcede648ee00ebbcef7db35485f6997a8869a5cda651bbb92dd001a3e5ab468551c93b2215a1a0abf9dcac3ffb788ef3b73cb321e4b270f90d60f32eda0b0976a8560d91079b9519af103f2bdf341b7b616398f2b1318545495671621dc6121b7b730690bd9faa87d917e3cc2cd7317150859c07ce0eaf840cb351ea98c91710c350c7471a246ab8ceaee63d7108f1bf288ddc2736f6dda075e115a69344281b0aae0846ea5b4be5c6ffe86843713150fcdb37b269b6524398b08d2e48dddac6365469484b036d5871408ef7994b190862cca9a147dd9fe15f43bb007ba4e8216a645739713ba188ed688861c01202076e7aad082eaa6fc04907ba65acb9dac483eac65ba9e28a3d2b1651a6e1ca12728734025e37546abaf5c41b639ec81324d506d45d15ef991517b8cbfaf556b179e46a51329d5ffa8dc9a00bd25b69f753ee52b26047df14a0154438696c1b7ef5454aa25899d12c619af0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0c4fc7cb29aa04e8af70bee7b8d0631a37c0dfe1863104ac5a69022a562e8937","proof":"4c2f22c5d3db141f172b0b94a37d8824bd359624c66898e745fac5c9bb3b044e084f48b8b0e7de9ca08ae618f14da93850165c7e71ba67b5766dd17d5381a34c427fa62bf7b5b1dcca0d69b01892ed666fba50f536255e7738c4936e2fe8811d64d3a4ad6da2e8b325bd19415d375a6154032e4ee8ac3a4f565c33ae02622810da24acc3d33d13699ac0c566865ae72af181bab7b988af11c861f0cdd0e53b0f7deb56068a2bbe4b412525698604af06050cd64dd693598aa9ea476f08694f0043deec4bad486683e39d98e0f7564c5bcfb4dada1367e86ea49d74b00f2608090231ae55989b85832118c9dcacc8f44139d533fdcebdee36277d3751802ee023ba70b689f284a240bdd9ca3547b23950f8a589ba6c34be64bb0eeb543043a16d681ba87db8dd7ce0346fa8b47b8d71dd1098aa18bcf167e14f86a25ecdd71e7f58f6ddf506915423ba7a62a430280a9829121a0821609be9a455da0478ed1d24f62ef6561b3c7905c640ef70d55124d71565e2a2b26315578c3e84a450018c3816b245ebe8cceccd850ac77631614ae4c520fb76f520d8bb504c4c236dc5f7758ceca757733e23ae37bc8bfc0fb0aac8eb07f9d8109c39caaf3787e07618dd11ac9035ecd2a485bc2689fa781fa133ded55c2a0d43672b93d4203957a3ad001d9ab136bd1d3ed28fc6219a40f26611a04d3078092a2ee1fbaaca56403e6ae065aead504a3d9e08409e0bd2162f5b9119758239e4053b1ed84e861b2c3a97f55af8f05e0b98f78fb581ae8b9d50ac9091180fb8198fb8ff01ca2810d93878aa0044681b6f6ef2a5af4022223191667b4a6314eed7c5d815e9a406dac540cd5a1e2b9ebc5b3ad12946061afd8943455a51507f4bb72b2672811f3c91f7300f370024f97b418429e2cd3989342290df18ee8b55717cf5d2cae2efc1671ab54f2405"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1cbef5a7932986068c849e4ea7cd6a7d23b815d05b7dd7b30e40aec3c5a55872","proof":"021de023f714564e0510764c4372be86d9dccaa50093011304730c0eff658912ac9a7ade71ab1d6d3df9fc57c50544c3d19e6fb34787bfd611652386e6d1280e2a6f27d2489ab48fcb268a5e9b815956bc5cf6d893315b1f20087bf81387b420cefac3e1dc00cd94d8ff85cd087a84397fc0f58fc5bd0be1a407c2c46d009a694ce313c1af5127224a39e4958797c33d70e1a25be5ab00f862f1d0e416cac20b5733ddaafedfcce43982a16a6e2a38d80f5df2859a01998d9174371852f6ff043f89e7e5c22f59dba2cf53828860551c58fa8a93bd879f61deeca25916e4220bc2dc5dd2a79338739dd6b806a2f528f35fe44f8dc8d9532495901d574ce7ca4d0264f3f0c35c236d43f31e47951bec8b4e31e0ca43663c37b8c78c78b0b12b405cbd162b2348205e2cde8e4e545ded585a94f0a8d7666e0cc3ad93fc515276356ef94d79b6096006545f4ead644c713cd64169dbe019881c1b911269ffb794214e2ae8935054b3147f9349f4ffc56d4b36c643d9e4c16ed9e5b4677c2b4d0d297248308778efc80a640cbeaa179ceec76b3803c8c71764ba526f0bc60aeb2531c441cdc5bb7f001fd8e338f123166a6f356b3c3a3f8048c7d0bc234fef3cc41272df6582e094871ac19553657a6f03b826728c51f1f374c3bc9ae3c9943d1c3bf2556dbc64e9266856f70f3b3b9d36ae102481d776346a0db8e88cfd26364016d6a529b3d8ea8745581bbaddeb12ebcaf3d5e0bf449008f8378e88872dfda00ee601d33718cfaee0c31771fa35a533f27c20542b916a35856dd32e06b075cc6194ed2414a512d55958063f2780574556cbee77bb1532100f57f7f093e205b93a5c10c3fd69aea9d194bcebe4786ae750eb10f43539a8a2017ea84088e159650554ca4c8bd64c35ce39e72aaa3a6df25e00c4a5eaaa894987dced99f54b329205"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"66be71e4de7d9693ee4ca7e597649eff00d300ef5ebbccf046f8cac52c75de0f","proof":"1c6270c048aba0e2301ba827c34a1ab056078548e0ea5e9e951a85ae13d5d450aec5ee7f6f8ddeada28f2e5d3d29a2a493b6bd2d195709f32153f6afb6ee3f7e3af28908c6b5d2624e3e2201e20e9ef5d8fd6839ec69fba9feaad16dabbddd59cc5fa463645f5dc1e82821d87086177682f113e859456d55c8e9923e7cd9245b73754c2209535576db9a171628d66a05e4372017be9663be8f45b5e2eb25ee0209dde31676e2848244ea719d0d4d57f07c754b24a14f7a4ae527d5ab705c9e04ced0afda6eb6cb61f3fbeb5e5b356ed0d9a2a20b3809ea67d24e03e5c3749c0ed01afa84ae9a9dd563a687f1f0ae2786326c8e5d8c43d50384c6963d5b287a3dd0d96151f9412672e49738ab037ab8e347a294007bd220c5c0305c1ae186bd79face6da91853b95e68927825fa58ae4a48c6a7a1910a3bf92c7963f852d1b87672dab2f39f132276a5fcbeeb9e79a8db5583834aefd2618b4ef7020e7e1ce014923641b75ac59e78f936953f4f47dc1e4e7c86f8ba8db1e467e8e2cbdff48c5a7a8f97900f34179276940e7a68655aefc29ed01c759d3f66e26d6b6d9cc0547312efbcde7d06a032e72d8476e45aa6c75089321c1e837db9fec1fb543a09c03af40ec4f2672befc4b8913af531df4c1a169118046291beaf2229ae92c669f42ae6fe9d99ba0cf0af573c9c4e2f7d595851c9331a813c0940ae5c93bcffb6fe78344c24d06f1b2ca46b992daefc6ff5dc62a17adcab8dda6cdadae9272660d269f06ff939cb03197911f83c83714ea4accee4682c8bf24a334f6f26c5d975b874f4aa3fa2d943f9161e67e91a2b88d7f35dce30f75117a7d8005e6d66ec84c129a56b7afe66c356feea3b817a2eae739ff3e39b8130920d52c12fba534af3b501b4381347c961459b42d43d703bed00b82ff2b98fcbbec3cbb7a52ff7e2519308"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"20cab6464588259d627f6382f80113da0938a70db4ea7a80496c13d2f5632c3d","proof":"d419d3b5fe0cdf615e3c602a614aec26bd38c86900cda806ecc89b9c0acae758ae966ea09218e6b9698877f02b65b8e89fc907bb5717e6e645b012f9ded2cb0788ff28610d5f0be122dd16461f9d92fbd01bcdb9be60b1860a23c435bd64273a80985b0d78ee277ef0b9933c328766d3262b659c27c5848bb23c3b92dc995d751b9fb81896d158d0b54ab9c1f55f15892c26225d61da240641658e48f4a2a30c6b0d87bebc90935ee78fb344502636b5bc5b700d0ba5a4ce0864e6c5291ceb0fdd88d0963724960d14d114dba5e2a3aba69a9849a55e6ef69161a35d5d5bda0e14bc3f87d0bb15f46fc577356f1c153ae5dd99f6ab01aa9699c0fbf95c516455e4a43b245c1bb12d0bc6e4cb2161f72179c81b7f2b9b42305a37cedbcfcff83d0a27252a068b525bb8ef16a420bece4b09290b6bf0a573b1ac87a4dd9726e33310746c59ecb093672e14b4b6443ae6293aaf2aadac87b740b25380164fca9d71b491a5bad9d1fb37f03b9269977ae09dde154d56a35b43bb5aa51f6904058976988585f35ec733623a05505519871cf8e4ab537621995f2fa09099cbecf7d12cb2f07208d44a090917d3f511859dd548fe9a57ec7067b5f64b33177bd413ed00acc90880a185fa5fa44305564d65a14ac189a47e2a882f7f1f236b81d6f24d7974a21169055845db588f9fe76f9a54f8c67b44250a8e74fff14c71b61e3d723f642f9e48a48b705f30b81bff821a6d076845eb3a60a0edf8f25b58777d935b7f32e54594082b5f40d084542b9651538d19a36a38fa1acba23b0c6ecee6c029087e3d310a17845222152673feff91db2f68d1775b17a0b288bc28edc3d4d4513794d6d77747d6dec92c0865b0a6020210eb79600d867b8fefaacbd84e6132cc09b6f0ae39435bffc60a57227ab073f9b60d267e8943b512730a4972f65f516c02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aa7799994fe6e02a2f1290f4f47d6ba18d4194d7a1fa5a8d08ff7d922c3cbf43","proof":"f8f2c81c3fcbe5f8d8080552b267d6b1270bf2837c7fc8226c5340ba32ad666d2090975d8522ca04fbec218a8c7ee6ffab4eb3f30a70e39ab681161ed682ca74b646083306762e9b3853208d112be5fe9740db8b1da75211fd38c0fdc908154d0c25c47b3525f20c4496b7fcd4195d0168e1d1d8b16e4d5a5bccdf7424c6e040696bdb1bf671fa1027d6b63c5513e036892db503e0f4b4a458349a2ccc407001b5d4aa4e683ef0c73fda27ba40de0494eb62ec712cd01c05ee6d9baf31d4dc08bc9d8df4fdb2ad98cc5d8d2ae08030d295180f8f8cf56ad52fd1db251a6ed30cccf0b6c73bd9ab1b3475bf1162ee7d9cb71546a7ed4da987073d17002721313262421c8141d7889d137812c51cbcb85dd12ba33e799cf74e6d83fddd7d186f44bef3a4a4f063e9ee4045a2b2b1e637e4fc7282fa79b4a00daf2b8cc8e01f341a96200e25065ce1d8d5f8a7c9b43d18388571abd283b77b78e2618c8025b623596ec87f9e1af7629a58592bff27351f4872a42aa2dafd866b7e3a1909fe205514768b855ab717afb9b8808ac5f28210264f2dfc04e5b4528f480b456296f8656ee064a0ce8f30e1261c1f10421f52de121944c0a9531fb97e2345a369ab63b433dc1c0b068c49374171b2b7d6b1f2080d1fd9c43b9c25babc700b095f7eed3425987e94fa273294b7b38a96d6891dc2e97f8460a6e508f984988eb2f6fb930d7360792f94425038985132cdfab3a1f32d5a4943ae9aac8827cd632a8349cf0e4374e1b8e1f996dc6c335155e2af05061bf4bc699e5f90ee5f13b493aa08478343bc53d8b8293ce3082fb115c249dc2d124b413916addb80836a87758234bb1c5cd9b25fe1109403e4158002b6ad346781d180af66308d656027646dc39e008f0a66a0c60e4ea10b35ef8c5348549a1bbca52e0633e58f60a2aad2e6371914ae01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fcf027c2d6fd63a494e9b99e87407e086f3b6d32ed5263e1bd418dfb4c901f4d","proof":"863665aceb0f22dc20a675fef45f3fcb8f747a1e505a451d935b4e361caecc680099c6c9ea93e6b010e680b4a56ac362a58e56574856cd7e39a67c020a8e3e2efa33dcdc9c97c950f1b473d6fd90c66831bc0b04a18626dc05e9b1b6f6206f44ce07a99ce7aaf114164c65f59b3399cfc3461f31231360eca05948cc990b7d684c3a76650c6cf8b913d3226f527630574e12157db2750da77e640583afb735042772f56f3a4abf3a4639f07f061a8bc12ba64b05dd379b448a3407e56966b70a790bcbdb802efacdda60c03c739f766a1a14c3f5ee64f6d1541ba66f7ee27a0846aaca85e9234962ed953d903dcab1580083ba85891b2e68efb41f2ff3a8bc47a44a02e47ef994b58db0bd193ddcf3191d85417c130fc36560f90026e8741e6dc27136b09a3b97f4ccbe55f2454deaa040172761a02d4d4d739386dba6d63d2a4241b86d85e462309ac81cbe9d11e36aa57e342a82c78e1cc6c293db473e79570e3aa1136c1ac5669d4444f75bfa2819fca76cd56ed932861b29d086fcb35e1d64d17efd71045a8c99260d9f52a70b7cbecbd26c8284c88f03635667e84d3a7460bf6b227c9b54c1f06d93f6b4250a2e379c7fb3da68ff3abce1a11885562a7a404de098b51497b56b4b5986044666a926fe677f492778ae517d7bb707edb751fa9843abeff7e86285ccc5513e47300bb1d7fd4eb05f208a4be8a73ee294fa7392561aaf19eefba6fe1efc410191fee9cd361ab23ce742ffaf5508295f194073fa8c669db0094ff6811f417b4f5eedcf60f644e651f2e19defd9cb52e817af4a08ee2128d080fd3a8cf6e226329e12a82edc26caf34bae983a09edc58c58ff6a6d49f0fbb82cce8ee0ac76435f43e807cea6b5c5277f53120338f5b18c7ab20d4e6cbd04970b78d8a15a6e2ac5d6bb1d30701480a1c473cf0df77d62cb867e05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b243124a2dcc01187029c8d8fcc8a7bac9e7555e9301c74852b712217f675666","proof":"ce2b6798f7c71a2a4d94bd219282f31a3b7b9bbd5c1ee80826e0cfee59c11a3f0ec8aa446eeabcee9752e97e1622fd0124dfe15b6d6d552d1a078ebff0bfca1efe7bbde2c28ac5e63dd87b4d9e8c9dc53739748ad7fa340c2a8853a14f18b049f453d561b7f676dcd49e9372f6857baa7c9e8bed668806faf1691fd6f81f2e0186371d824338e6b56a22a9a26e4867bb5ac2a1dcdb2d4379d9dd74989330460031f2c6627d2bee4a19efe5f682231e5f0cdd85101e67f866994ac6fec1bfaf0946cfbcd279f7cbbc117137f7306d4ba676ece173ba392bef220ac5cffa6d6c036098f5357a3e98cf4dba80c676e10d615902d4b9455355e86c1496b1448b2666b8e5425399569d758773a0516fa7b5ac1ea7ea07dd21be88aa9b4e27d27384368645c23c9c2b6b00eb5241f31787bee4a1da4da43bcff7b92429599323949039285eb5f4b43ffd2559947860ade203a096faa5d5af919441cad46cbac888004c684ebf137c208ac3e3fdb0ed5117190a3e3f2a24d4e94cce737144dcefd2bc0f78e60c2a5ba04f244b6983106371577e61368cf6636957efd1a12e97948dfc77125e9f5ccd01528a0b80d84991b1e4605d5631b0038007bada1e12a39806c307c27df01e14359731a465b7c1b51c15e5cedb3d48531163917bfa406620f105690275daf6070527779b9bf8a22b1ccb4ac316a1d212f8cc3cef8733bc66f17c59f07993f79b99529cd7438cb1ba21cdb3c90ddcd00e8b4d990893f92fe98d5e489edb1690d82c08bf70e031f0a36cbda5cee675462212a3ffe985efd74dffbb043aa19bc867c1e273558c2d23a77eebaef25ca39c01f3c7e5a9be727901c86d5325f5349d999660abc8dff1397e366b32aeb341234fc638425a00a3ca800dd40e08ad55baeda456990c8a06adb2638e1c15e09c360111688841bd932943418b0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7098b15852d0d76e364f475d43c9e9ac8e7fc0292f12ea9d4c634f2b8268d206","proof":"b084684b39119a9924fcb72133ae5f40eb32f203acbac175c6a22e7849477f1eee7f74ba7dc4e3c46c2ec4c4c6af9af847b3e3daeb98e4cb5410e4697e61462ec8051d443242a377ff5d4b24656bd49e6a01d78ff203e9445161d8edd8e0e25cdc86d254b26bb2e92bbc6ebd5510015c0feb5e616a7f71ff26d4ea749a43c34ed71e31ffb198d437cba6412a7d0d3fea538f79d3716294ff30e806105a614603326bb9bbf421af3d542fb6cf4820696fa23eb9c9d6d68fab5f7dbe08fb70380b1a6b95cd757c5724e623792ba4f210f6acb9f51581c9a47b0bb914bfa855450682e3de114c10c9b51d64fb2915cd50e09b90209c0f6a20ad8ac8a76ef481ce0e1215ba2bd975e3a8b6d5e906559147388225241e476a4e76387718eca116750c7403aebf66081035a486e0e676da364a460fe186ea23b67b6ee475078ad0b3793445a00e6a0ffa716d9760d8121de5c36af2100de822cebe92ab4319e52d093c1ece5a5c1eb292df45125bb65cca3654a0ac9de36fc6c9a15c92f21672d7cb26827fc4c1cc36c57e68faaee08cd22d51659206db52da552896c2fffcf40ae410c4089272e877921b10372c4d79db591175b37d7100cb7a1d823e897f7b83b9109a00c65329fbfcaa31ade95b2db079c5f3798d14e917219cbe5ad7394d0c1a12c8dae30b9d8615c37c0db55ec148472539780631a2104e7866da546cacbc8106242f3a851fea19b3b1ee7974f504f29d071d9d14bea2bcb990b14c285eac7b07bc4952f6d4500117bf5bf7dc8ab37aa3da830c3bc0256b3db682c6f01469206068b36c605a894dfa6211567cca6daea60aa034654fe3337f8b88a52899a6d13640ee918b5f04814883ca8fa15f2aab30742c4ebda19a44e663e4da794540b402e63a3766d8a7873ab6cba36047661a053c4c9fd7915008ebd168252b44dd760a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a86aa32445ac3d9caa51cdca663225f095b801689b77450199643b5c37746774","proof":"863617201e919558f57528d197dcb5f10c51fe0b6240e94f5b986c774ac7477fd6784c8f2ebc6269d3defd1a596b48938bc32c60601e11ef13a479bf191af50954c08d4062a5dd980e8f0f291399162ff110c716ae38dcfa643404fd950c39487200830cf3a40b69dd5bed98739b386107c6eb49ec8d870bf6051f0aba927b1382be4871f654d05a2ca7a40917a56b542b6b1eca22861a26708d1c4ae257080183172c33e06c9b2ce0a2ac164c3970ec43fb7d6aafc9a104b1d9ebc35b52010138d290094b1fc14e9beb1c10c1f684f4b98b938f76dfd44853faf27cebf419067ef9ff8ad0f33a9cba152fee01970df503c63d68afa66858afa9575393daae6adc40c8cef489cefcef9ab7b21a1c5d6204c40a8df6d26a6b54d9c99b9f267773a2201f97e14b5d0c93a066a37fec18a1ba7d9937e3b4e949b8e43ce40ca87e2a5a8d9a6ecc45831f9f04afc712706e1fe8b2f555cb99c88a1cfd525e068a354fb284f8e59fc85102182047969788420ea2d1d935994855fc2c7836e1e0bc1415784b7e31ba691443574d83566fb8443f280f13546927c99add5877d41265f43562071f950882031ce0fa50ea7473f8a00ef6a4206cf51be796a5f49a8d65371f604827b7c8c6ac9a7e0ec4bce63d08ca15a1e23034d8b34ca078b6794932f85b6ca42f6bcc8e52611422a8f273e897f42122326e408b71be3b495f1b22d558363c36a374f41f27524d0754831408157f294cc0f64f699e4527af42045c92e916fc4d0d6bad8152955e9b17ac6a88c8d943d197a4fb3e74bca19a94f3897f0b4fb43155efd2e10b586e76884ef745f8004b4bb734a87408009482c0706f5f51755dbf56c225d607c17e3e607bd2e7cd4883c99ba548dc87b6d1ddf90c312e7e0c4d11115e7fc15383e551269305e25c5264f2b8e19783dd555fa59e136e455106"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1e20a986ca3c11ed7f923978a2e60522d69e3f0644bf85a886c4ca1f7add7e4d","proof":"de6ae7a83d259d2e9766eb39f1e9b41fd88ca6e1a2d8310778aa3579f061ab49c0937995716114b97889190ddbfc502c8623078620db1cc02c196b712bc2fd38ee9457d78109406bed82887b7c4b1d6cccefe4dbb060976c3b071f125353377e7ef6b214818491db9ed9392e6464bb1ac2f32e8c293ad73b70d57a576c39ef157b2027066b81297c58872a49c2750f3d506d8384328beea91a315e2d36d39e095e7db4eebaebba8348b6c82eb9cb34d260b74777885de6b25fd47f085b721e0c536e3615467a35018459e91f0732de5ca1eb2f3fa1c772890ec37de2f632c9068428ffe0ae637caa7d56f1abfb729d575361377851ede22bbdea230fe70f520d00630102c3246e923e0e0556084a614a5dd9ce6c43c17578472b6cbd187180714e07c30800d81dac5943c38c16276d15330cac682699a611bfe5df79fedf264d18a5b5677f09ee23d97576e57258835550017d113e98809c0f388a35e4bf901d660ded35c1d4965795abbca674d2125493fc1667f6fc7dc49572fcfa6895ab48588b4700ee809dee61dc05260a6f345ae0714541f3cfc0a9fcce2bb5f2ecd24b2a0e7f8a28009f0ab6804ae3e8536a73a77a434b69397dc1c759b2907fb3603230533390bb4effe899dffce580346b8ef201a04324b918127b177bc6d468476e0efacbb1461351076461038ece7b728929dd6856112a93f5cfc6ce9da227247714032badceb5f051cec6819a6f0549725c241ed74944b9e89fc18ada82f97e18ae3f264a10ef43c74d20bfb2c811eac92510c1c16ebff2bb2b17624645e5102ed4f0d89a1ce94b8b7b8fe267c0f61f074ae14bdca38810e17c550dd99e569f35942d6111d50ea405442e04cd1e54dc7abe1898f68857ac376da1cc17a8e2090e57266d6724be346596ffdd2dd4ea8b56bfc5b104c9a20531ba8aae23d2eb250b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c6cf5ffdf88281e99e3c33cdaba39882fcc701b7cf006a8742bf8cee8bd1de02","proof":"7cabd446e754ab8ce9d483eee3b4aa57be4338c24611657d4895078f7bbbcf573e06e49a3ed2159f9bd197b3f9fc1d2f0046b1e3ab2256443350a4def7435e1f401c6ce90dc7b3d4c6afd1ac3a0d20cbb484a444990d98311572737d0f8464595caeeca1de27b21dbe931f15aa946cb6987f0af911feb4408901337130addc5e14c70d3aebc94162aa86373fb37ac0fc4bb89a4a652303b0deda371a6bf46d00abe919782172dd5d79bc977edc919db7fbeace57805e0c1915f886b1a30e540eb31f951ef3d44223448aa2f06c31ff6f8148bc063d02f064d415003872e9760e74402bcae2972120bf917145b5e9e0cc6467a878fdb14e5846f65e46f8ccef54783fb64a8e728975bbd807fe561cc0e8dc8ca7e1b76b6f7858d42b3cba27846fe891686f4f08546607c38cd06086cce85496f67775d917eb061c94c2ad339139d0fc10b1eebc144064deba4d5a18fb16213cf6ea56ccc3dc89c2b87b429af42fe85f062945f43483230337e990a41ecfe5ffe86be1cea60692357350b5949c408e1e97cbcd9410f9a05e62c6164062f851104ef9844a7d7fd08f110d2feb5719629210a8c673923634c51c7d83665f558aa19d771e6ede49350a80bd897a726168eeeee75e082fa601b99423a9e2c8c3124d7d4875cccc6d86a9fd38dc96da0b7080e460fe3b121b71efb038331a54d047151689d3494010439871de4c48233f80502a81479dfce66f2d8e48e8a92dc1546ce85958db0c8546063b8be478dd793cbb8031955975b338dc37d75a5323b1df5e3a6e58f16fcc008c5bdc1d8aef692c3c0989d80b4c2bec04ca5bd2e2f30ec5be3dac8c4a639df102efdea318d02069887828e31677ffab5b680d044e53b88a0b1695ff4632cd65e1a8ea10ae010b2b2c624553ac962e4c7e5fe41bd2f74a947e8ae9e78e925e52378eec753a5c04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cecf63dcd45da8ec38a25d83552af3914d3e50186e6facdf4f72c427f8f4d860","proof":"7a4dcc38f4e2e172393adeaab6433dfb41ed7f3d58e7ac76d9471532b4ceb77910f4ef2e68b467ad094249b11a7b5d65ad4f581d24e7bd7bce1683b731b51270c283cbf728800d4c11e1ba12506925c80fd2bd0d843e531dedc5bd0b66c0221ad213110f9cdf67b743e4d45c3c9f31ba72aef31960a0380b80fd2a24ce5304397e92997b06e51db484d17428c999cc92dd8aee996e147aa14581ffe735380c0e73cf8793c3bcd73c4ebe2f359a7bad3cbfce21bfd699f11f96f67402d949c70c159bbb408d030ea2ef7137e42a104b43f3dcf8a32dc9f6d716300e00b37ff40022e13fcfc13ff7a795a170d9b97e3306458cca983b2918fac1256bf9c50adf5af28a33224ae5bac4c1e885dd892c096f29596a249dca274dcd903607b299776cd86ee6ddde2f14b30fb5a0dc8ba07a026c9a8be8ec9b1b01d118dd0c5ac8aa579a74c13c20374a6c4c65b5efb2df20fd5a2b913eaf734ad7ae8a2a444f70eb02ea8901f98c87374f59f4b15aa9778f56e0d5a3432c2be276b1a4017845f12d0c04d8a214fe47a152e6c5827d162724645d8da4b607dcff36b2db9ebbe2cb5349d81b6e845ae7da3beb2233891dfd405d6b5d980b8119c53f6e90d79e3a55c516ee74a716165e52f5b854c5581e02dbba09b3473ac14929226a2e4cb71bed092da42f6a70e88e0fdf8d1f3309c0dae62bd47dd3ff0dee20e0dad8af0e003bea35109a0cc1715867ea87b60c28f656ec2bdebd7f3f1505d0e822c68ea59ef7f475028c62154ef767612e8fff5f8e74d0d146f2658666615e47817fd4643f399e21082528032914e42c371cdfe6fcbd401cf2533dfa435a915567dfe57bb81c3d137bd481f149477312ba095e60eb0eaf6a479a4b2227215ef44490019c3b337a000c6ea890c17411a8d2c7fa51850040309244aa6eba62d443e944f9ce08ec3d0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5e1a34823493d993407e1f330fcac41370e3a4b6cfeb261eb1d9133a8ea8095b","proof":"f0b3641074923481cfedf926bf990076365c614a6d65f51665c4c0bd6fc2233b6ec054bab4ecf46a652de2c309923ee414a15e8e18e99ae847d532b74da4b1457ec8d5afb4938800e25b82716f5a3635821000e6600d68c31b255d124f4d1c579ec954c36cbd2b284af557f3c3714ba8ebc7e193d11e8aa40dd2666cdec63a5c9cf29c25c8610c4a6f1840056a3e0845aa76a56e5cd532788324f720a02e750b98db2c40d72a2850424b48eff12747a39e934e3ec1f500fae6df17132d39b900fd46b78d334017a61174129fad365a27b1f90a8bb04aff632a630954f36bf9024e9e1f9be544bbfab4aef61b8151a251862df71431bf11681513a8223b59fb4ac2fd844f9af7bf5c05d26ca895af00cee573a7b34824581cc8a55cd0665bc72fa45ea48ba46652f627b02f8c6e8f9acff851b7d3435a209ce69d5f0039964f3eae6c08f7e5dce86d4aab01310a36719b01af956c01cf59add18407e77491d656caf032af45e3b77bb6b3a7f37846763e391d13d5ed63d395989d0300255c9669682de435de077a82148248127560817e663de588342bd643189c4306edfd2729e27b8733cc1a41a66749a366ec211c67c70bdace68cfa854e9fbb6aea476f26298af9e31384134006aa6d379c9b2b949d9d2ca7ace05d44b0fb4817afad826699088d9d8ad3ab51d36075743bc686fa9ff2cb1c409f551ade3dba58a8b1cd314dc487c0af0653c2994c31fbc4431ef8556d6c8c3d9c6bbe6edc0173a7dbd102258a39c02ae01b264381e5734d1d508ea3bb8673947fc5d89edc5dd4ba8d3f761d4c8575e580d9f23ec2c3cd73bcef39be319a947bb611ee14a986cbab921e20c46c15e4c5c23ca13dd288ce73af3e6bddb455c22754fdec0562275e28aaaaa0b19a33ef946f95e3f72996cd128fd308fdb5f21121d05b2ebe75355244e875807"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1a7b2dbcd3b539b0ff7812f74805097dfb47e8018c36a9eb3d3f93852180bf74","proof":"76942a6afa84344109cac82c041fb0d2f1db33f09dd62fb62b659818d07c58345630c816eb7d0590ab68553668c39e2eab6e9beb0dda0f801319f92f7ce07c6a3cf98809f3d5336ffa7cc7196f31ae73580844cefcb2b838969757dcc681356f52b51c09692d0b81b49c59c6de81557f270f2b78ee1e00fe9687787403e5a5679fd4f6881795bcef986ceb54d708aa8288491aebb55e1400115952e3f3bf690f029cbd44fb053a1720097ee243529026318b8a5c4415153c9303e5a5802aaa0880b191d164b92b3dd0b6b14cce34a606c7b5748532a2ba0e7e5094cab072ee055415fd0a9ec33a4a8bf79d8903782c8a99b3010dd2d199faff9f5d93b4c5201dd2937a9550275fa2c5aafc0ea88fcb7350440cb543c8a633d05c1bd479e2533f8254d4aff7031d5797e357f55c9db247b70cf9b862272f1a8121980490e4a8109e28ae4fb7c430447fefda6c2f9770ca31fc106dd06370fb83d51625dd17b1236a7aa998e7c500ce496743484fc9c305d6140def54a6e1c445f29e6187c1036b94a8d68c134b3ec06866243ded40c29bde46bf307180ec5d2f276994e42e407d82767925ff8cf080b2d488971531c16abf848d967be7af8fd14e26749d3f1e1d9680ba75af3b4f496d001e765c59a4a36f544a1d914b606573c43c843698fd51ca3aeb4c18efd2fc46a7671fccaa4b6579406e9d8baed51e22018a4b86866b554a1f1d986d119dbcf3527e76d4f47ed3ead7da299d8c4a64c62ed712ed18704ca20c587ea9023a528ede0e5e0e1c4ca6e2cc5ce0506cf90f695847c3b7c3f059c2ec14586d22dd5117f3d5037ef585690175dd7a0654645bd56787323c710e7cdcc054e3112720ff84ca1064916a29577b08597e268993ebb130058dc26f3c094250faac69c15b0940de116792a5f61c1b1739d7ee4ce93b81d4e2b70f0cdb06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6ac6451782a71cfb0ab456a4f15f4bc3cfaea7a744287cb2a4b9afcc520bb000","proof":"bcc730756d699519d188883451ba80b498e91efec41f8d1fb35c0bfeb5740d6ae68d7cc7f8a5db409535386d9696fd138633d9d890229314ee7c3f23ecdc8b6bc2e0a85d92afbc05b42b4f8e79598e0a0211ac1f154936e812658a2c786a7d725079b76eacce010e485b8b06188bcd98db90305a54b717fdb576714c4a716e5f4b6c7255a5f40354a6eed569ea5022bb75a5db4898cfcd7e6b5a7794abd5c00761166a7198d3e94efe26c79e968ea92b9e975c2eee50021ea4845c2fe40a550e16e31c541940e2b7f00ec9c0b854e915c4bea0c29893e78c6315146c8025eb0210857023eab96a387ef40e5adfb56d8c8385713678bd516d9079001e54d7b75314c75d11a88a4b83dac7b5e6dcd6564e05dc268064f3c46872d1a5cd221b43209064ff1f44f7a8b452f0dcb54c40ef94d4f30560d1f0e0e21aae92147c18f46fa69bca16292a9b32c557eb2536f74148561346a41357aa878437a2f1de9cbb2dec5dc592165b811fa742d7876dd0ab7bda9d56e1f7e6421499eb45fd60c55f22d02aea8344a2743f7bb5046a0a0345c67cb273af7333c116d8f9a3006d497f2d84d1f28d233a4354afd0d284b7ee8647633b1a037e2bf9308d870f15d211b769d2f68c2ccc043705a3f850007af96a874af3841834d63fa3783b27de47a8b34334dcb27bd14d3a5da95c8ef768dffa00fe76120c76adb64d648701730b800f1a005d6139e5e7b1457054cce747eeccbb5f264722a6a0e6c250519c2461f7be7b5680a5894399297f2a1c915087f523a9a35318bced0adba8aedc69e0ebacac38703687e96cc78b6a5921157f128594ef362381cafca8539c5f8eea9d537d830611ca1153327a080e6c69cc87702ba1710b08337b6eceab07cac00799bd7a9f04e67ff68a63a542c4f257024ff52e2bce9f867b4506c140c8daf8da89e9fc8106"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a4445070344097464fdc360f137d1321e753483a5460161863011a527eaefb1b","proof":"e62813eddf19aff8dbaa7f1a85f7a0273d11a2842606c4e85a40a8c7d9e5361b62d7822c0633941ebec41eae3c6402ced7e37153c9774d54610b2168be55df31887411012a805013b65aed9a7807a9117907de8c3b7b8200637659528a68d2415ecaffdfadd283b4d4224cdd21dffaaed055b69af4ea6a9b4f46fc5e26611e6b8fa13d57c66978d1fe7b796741c98bbdbc70ae6f31a1c470d5d6fc45794af60f68b39393dcb466503fcc46b9a85c4330c81d24c59f526bedf6a3788e8b96c20bb752c1113bca06adb5a3692947569855b23a4ee3b82ec29d8a7d36b83c1d4000aa6839874ed49646086e930290dcd69b6c3a990cd2e26e7c2cac5f2ce599e07432f7df328971c99829ebf678f3a3402dd725499fc59c29373d0581c1c583b65f7227fe7f53e541c17f93d4231a3830b849f14d568d0a61db4d91195a33e6e601daf9b271093244357615aa871370de40d2dd5d9c17a45944e0dedee154744905ac20021ea2a90f16aa7b0a83eeb719627c3003487d79a22bba485d8d04288440c00e6031b3d13ec72b240c977f91fb24ecf749e0de76e8cd240e00b953277c4940cc496504332618a255e7f2bb0af15996380b3b10811434c690a74da84bf0096c512bead297421b37577c7bb45a1f61d7edc16c7eb58d29f83f10f6d8c6366ea40e6b1b68ac17a685b55154efbf3c393ddfd370efd351baaaf1a903e739e040ba75f0c9ae6c9127b09824d58d29e311be2a50e04615858e4cbce7f0f838fd3ed80e80419e288269c4120dcb4f36f2970e391be7c40284b836834c2cf4150c68268c8987d0e776ac2daaaa7e1a5eeefe2f95a4a14a39cdebeb95a6c6ce19223541d8591914c2350528d87efc052db97b03bdd8b45972d846a0b14bbd7ac1c40ed82a55902bd0bf6ea9d12b933a4cc52402cf97565f86c8cc2e156850e6c27b0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7ca45cf0154356ff0b8d8377ed1d33cb446e19052d243d2bbaffe3e4d570b818","proof":"f61d6bcb2453d2dcdb04fbf973797b61b9733f9c04bf81c5c921fb9d06cd3e00f03327e116a9a41246f49ea6fe2dd25364fae4fb508a4ce1e226281e84a5bb74b048396dd7e9af606e522280154743d0c8497fc17f2dd18d9d59e3b9bcad8d372261257bee56542be7d2133e6874940b0dbe989c995d1b7a9b80d7c2bd39c260b408844ecc685aea0e517a1108c60f892e3f9291b70d66f1833d3766c185980954c5c04c9c29fc9ab720fb5875d4f47711ce1f1650759a24d2ac2c41c119e800a1706b05b2a8b027f3e15e00cddc9cabd23cf8f72a8d2b78ca6826fec6589604862ae621e869bffa42c250990099081dc147ea2d62bc5823181bf90ef9d3692da0cf29a203e96625b07ae4ed1f86667224a96a6b390b3b688d9d3b88ab4f8b55420c5e5229d1f0f7dfe96f91f7899064c08f218cce1e16d20cd422d3d10cbd6d02dd6534e840cf6d8e5883c0d8621c0f1754c5ecd1f17f25239c69f307d8e76dc4e0d7d25eb6db1765e538625c580c76841d662ad86b007a4815bcdde3392a0248e715ce816ed180bcacc8406188fd090b76574e120a4a978161e3df3d08e01154cd74436a407a57a4cb682405484c0ca33d5fb5b4c7cf427d217f8eeced7e732ee9a56d1691b9223f383250450dccbfc8540280a371618b4bbe828c1205a810ee8b762308bc6c28995e8185d18928039ea2ebfb29ffce4558973a3732967b4ce8938d32214eacd874c5bdb6f3fd3b56fe6b716c5bf324f273054574fbbc332e14e86ccf675cf6b507be0362b96a74353cfbb5324ac1761e3b07d0822e107709e4fe20859fded167198b5728a8e75906af27720fefb5310ed930b6451010a4402956c7f1422d5f421d310c18132c73f1a66b687ab12c94087d291309cd68cd0778b42ecee5833ef1b8b93a9e70e937a9f45d9fc9f60de71eff38566db67a7d00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"90471e03483e76034031e90442cf528b0f7d7ff93aa17c968112f4ba6a443156","proof":"4c11d66fe7f8b5802bf55cf3452286b2c3f97be734eae1df62b69940e212c24c00db1aab0d435ed62e78121c3079af665d251e8df379c42a28c8f1519b4bdd262a70510e0288534b2a507ad3e7e1d1fd4bcfba19c2617fbf60b48b84de0aae4cc2215e12e3c04e58aeadc7d53d17069f33f91172a31fe18ef51cb690648be13fd3e24e7613e55136342ae794048560933e73f6e91952f85c8d4f3133405a100ec504a1070a50b60ebb04c7b8b455dc07b23ae7bb8fd64bff7a1ccde9fc4921005e701994b154d80ff7cafcced6fa8c0e1dc6a82a9c3d0816e41ed0254755880e1a2abb99d38fa6f0afb46440e2d3be755e67060608409fe92a68e588ae57772ff4b02c02227af539f36b0b42d2c137cbf8ca20567c3cb8a0cb49fbf7cd66c572f6bb7ace51808549507c6ac7e256ea680cb7bbc2b45d3710895eb1ea862d2840bcf382b61863f69b109d5a9748d4c6ec66f2c783e2b61255f53e16552315775dee2ca8c577920a6a351cf3a08da6a8308ab09be077663ebe2063b20e43db4c198a39a2785b8bbd83a697b0f067020595f5d266a938845a21e8d5772c2dd72c2e7670e42aeebdc10f7706603c69b4e2c7b24e1e92f92b0618efa7e255419b82279824fd884d3e9b09594018773ff6a5fcd516df5b6abefe8223f219e6ae0497744220301403556dc1a93a3372495b248a9c233132748ae1aa14cd440941246d51f05652c2beefcbc77bb2dd2b8ff53b99204cc4306334422359fe9f856c349435e888d04da355312728eb34b6176fda684cd8e6ba7ec6dff465792e4ea73c9714cccb81fb53cba5aefc56bdfbadb18d71531305b132b0e70e6db4dc19d55d156b7f23d34aaaa90dc75dd26369bed50112928f85223b98b83c5f9f1e3d2e011a0cd740e2688c7fb96204298a690592b53048eaa37efcfd780504643a16391c7b02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a258af3d3bea7461b22706a98fb96665ffa68445a245647cc1dfe694a1b1b25a","proof":"fa39801b4462bd1e8c26447a810137d02f1d15f05a4f59cdcb6c62b1029a1d5082f276e070ff8eddf50699a886e204897fa6c629f74c93da7e7b0d000360ab12b85f1f91bd8ca5adedb0bc55734b654821c58d97c58deab285bf995b4051f238a0fefcda99540d1aeec4caf216abb26017ddd9e0d003f5d17247bdc3d285bb059d9d248efb845b58099ee1608a192cc6938b29a7080af7e9810a494964cf190429aa098944624c2d8e48b466b32a1771ad119ec729606406f17ce27e4e420f0ad299653efe8f84f31a255f07f8bdeb028193d5b2caeb2ad0a87876c9c167b309ba50a38802edc9b14730dfc19cb3e853a94ba386321807803bb46a0b6fab1a4e029b4d7ec2b239b4eb11c32d36a56d2cbafd2c07cd57c0535a0f4cd602c1a96e4eeb97114767abb94f57a242ea0bc0fe1484433890b6cda1af857f376e7aa47ed63737ee4b1ace90067b3cfadd9b798a969476c487e454a52b46e31bcb79461fd2c05f57d4fbe36151ccb5df71c5f195867f9d1e71584bfdbae8515d5447386642430adb26db596f8dd12c7cf239fbb2ce3fc1b3cc49c9f9edaa264d6bc5a271d02fa5b093a5fea9c99f053907591a976ff1167f1db8dc13c802170efbdf4a2be077cb45a4b07b7a55da3c4c241b65e5cf0182fa00a8bf0cf66e8aa110dd955884cfabd5c5251fd2109eda1d337eca9972fe7001dda434c7510a6d423982211642f78cb7a35daff8662f4001a198c3163474aa5f2bdf66f9e8e3d573330c037d36814176ea20fe96a4593f3443a36961a439852ded278cd65bbdbee4c03f394a086d89c069f999581241f529042fb3a1e1c60aebd824b207583b5f5949eae4548677903e9cad9953d872547dcae040b66201a6b9fd098e0b33d0dcb8997402011ed603a8b23bffa56d4ed14a72431c480ea9f5457cb44df13abb105a7a8bff0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fab62417ce433435b74184681484d1698f66bae1a69a7ac50cca00b3985c0f67","proof":"768994e75a99b0c84378778c789ac03f0082931b9de44b6ce19f3a5406d2e06a3a1c78d7795c2ded4a21e7d4ce3e2c6419979b9b8d31123bb154f0b2871fbc1a701a3502e5bc32ba98e8d032a91d2982cef0be552558904233b2fd9d8d0fbf7b86a4cd3e4b4e411b26aafbc54aa89e6ebb84745df509aee5814a8c3c3b614b4c6c14e31f6dd260d3794862142bb073fee714c431babb37153c305997d723d202aafc0062d0d090a44e5c274f572a2d6f3d0c5f22ce1c36a8359e437715b7a10af81890f816d0810ae15744d66dba8d37ca0421d2f5ebe6c5aacc47224343850436b1a011af6a660157dd801e721507bde1228a1f7b14760fe7723440a8e6902264650a9927729e0b4f709c489e7d61f26f9f5e3aec67fce16b9c866d39dba17284d5397995fce836f68a0f323a3b7717a49c08fd74921551b2071fd1105e0642fec4462889343a1e6f7b6c2b35fd81d14cd4ab36509e679d19f9848d9c1e4f5b5205845e7f924796fa8dd8a93339d34dcbd9b54ef7e4590c2de1058438e63f4be05b71faa05888217abc2d39c0c4cbdeca6853ab9166d3916f71c430fc7b187ec0ec693346b18c4426de8848310f98e6b9d92881b388d50be6a55a2075c3046648af5dc24f93dbc88bf875bc41abfbcc2ddf383cc0fe2ed06d91de650ce0771262eec0c86477a03692cb713cf38129e65d9879079c94afc7c5c778384e0e7232469b4e71f3f9ec4c764fe0de4cd676552d82545f64077ecf4ddb4d5e8919cf70a815bd5d0e828ad02245881470e67f5b32d82c4fe9f6969fb4690ce63885ac6fa2d083e5bae0dee89c0e4adea58ec3075836752bb4f736c3f6d7443e02e24219705e0daa00e556994fd34e2d5255d8b4f1c9148c8531ce67e4066ba3415ea009c47fdeb0fe29057f5bb2539498ded84da418752d2623ca1140c4eea40cd39303"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"866867fbaae84879f6784c462a2138434e6c00fb4766d443d010c4dbbef5e305","proof":"e8f61ff7eb02375dab44c927f0a6a64f82fef14581832b063fc0f2818b483c6dbc2bff8688c8a90666d344f928850bc10dfb640d92501bffa191030030239e3726c44bc3314cecd60bb64b03a60564e463218f75bf3198bbfef07ecabcd5fa4f5e576a6b1305a44d6ba065554403e9d5e6a0ee1dc24dd8f694a59399ad086508ff9a7d05b330d9e1a74210b12aaa0c4e5ade67a65bf74d38f13245503d32020402a55c4b2d1b9bd468e7e201b2f5d036b09758a94e4150bfa383b7e61eab5309f6ad5fadd6120427f0b88e1399300fec25cac4d89b974c2e78e0ed5a9bf2100d724a10bc503198adbb65d6c4b30575ce80ffbbe128ce02114bef6b77c3418b76de9e2ec590443ef229e637090ddc88fc2f36384973969fca1286435ae24f082f4298f18d32d24ea10398593b0332ae71017dd61c978d9dabdd84425b34b8a2466ef409aa1d1e26acd1754de6cc0681994553f7c0623a9d2920e39698a73b642702301c56cb2aca2c4f759b7bbfd08ea9b84e237475a5daa3665453dab62e5d733a18a405ba264bb5ee154eb94ff3e79252d994e1fa76277582fdf9812d368349d4253c944393edf082ad8c566484dcbf735e9b495d4a477b3d942c5155cb237b184f5dd32a0bd727217f912aa85083b6450f16a2229fe192ec6036c118809375101a161c01906d62a93bfeecdcf72c828a8c5100ac017d4a9a5746e547634f1de097b63f58242c4efa1a8cdf61d194a8caa5bad61ca4537435a735c492d9e7098a8dcd151ed1d48b2e0efd9182a8aab82d704102e5cc381d0c1132a3ebeab871e8dda909ccb9d8b319d6532104dffc33b528133672893c0082714e91163c0103a2c65a04b7022f363521e1fbc0ab99d24b51d5cabf07b6857f3f0b3a43767b0ca1938366c5b2041a53b26e680b40c06a02a26988426f6da97efbcc92b5268505"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"662be3098eaa913f853c304a979ce3a730e7e8975efe637079fd8d46f64e8d08","proof":"6af1f5abc7ebecca9c6b4c07eb61abc24b358782531b57e01e70dc5b14b3550d66b9ee4bf008ade0ae39fb408b7eb0c1e152ad8a3ce72fd23a8b89e53e55806702ea8da280dc8cd77c3873219312a5246bffc34d24b6bdc2d2426d338660a72252d844aecfecca97a7bd70a77e5528081447c8b8b71b3b0dff38fb0508f7f234c15620c28a6adaed5b2ec2eb17bcf9a35f1f69f72c785c80475df6b588b24901f0813f1106d5e659e4f809313aadb36cb6e4eb7d9bdad3b3a2f5316fd5817107912166411580720bb7400221ac82de277bb8212dceb7bdb95bbf0ddabf68ad0b7c9792d7f10870463e13119a62fb3eabbe793579ed7c0e865306bdf83c648a1d2e6d2c7740506964c033d0e34200c58f3d6e8451ae7e960acafaf64c659a33346697184f2bb973d2d1447102fd6d1e6e1a9445d3ecf21feedf2aa5f313c6491b12bcabd0d55b352e05c129e8f231c8d1189820220512cfb2648d96ac13c42f59b8c44daa3d3ab597fc555e044984f9e23db5d8ad8cb9967cd1f3a665a5e74e60f0035ae7e9b1a81d6916482ca1a4cf4ce5604b6b57d68b2d7b9f61bab6323b45f27de9d31c829281562967ba8495ec8b98ba8fdc4f279a46b520941a8b33d563ee4f68b1dfbe2723962dc2e8cdc076f0275134bd74fb4991553c5ed5335fd106301ff9062299bd7494f6831373e20fc245be4d3a4d87d981fdcf4fabf67e1e4730a03c7d462fc906b944d9d464e34cb75ffd334dbe1884100b07ea56860842624c22c882ec1cfbfa287af0a936274afd279a99df519ca437e5f6009905a60e64985c272008201b88e1385b98836fa8bd0c3a35f54ef62f84d3c0ffa9115924344a201add64969489f0f3b61b7720544000f42c0101c0c13542e0d883aa952f091078cb855b0d16f8d331ce171baa6503798371901219215df499fa6bfc471509"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"803bafc9ca6a164930f40fcbcfe5d2d85839644dab8b7f443b8d430db314404e","proof":"409132ffc4868548884ee75416773d9386d6b26aaa45738e8dee51e50cc3000fb2db6f6627cb94d65ba31fb263a16db65c30140c03e1414c6552efe5ffbeb240a21d8b493d210d853b95df29a8488587e3efb2b76a96772a87138781b1638828a0c5a7eec0be7932b70d210eef6cf888686877e1b4d2e794d6a776a98fa6c224fa8ef0465bb074d361af87eacffde7fc9dc97e8ec849d4217b75fcc0efbe4009c16af505fcb324b7c27dcd3bc7d190a9d1d7abf3d5c065150a2cb04a0029b203a2b66c3ca6f018f073f3636d7d01e50e73cde9078e2a07a3a888e5826af2ca05c856fcd99524be1eb5d79202d2c4f83010f2dd9ae827e6bafa0172c0c2385c3af60b6e31a0461daf4012720a12a096e5cb7bdd96fb0f108bcc7c8e971e72e86130a5a5de498cb865ff131e72e92667166aa908663f6a887aaa1a398ee49301069ed90a00a9e97cab1178b76f5a46714c864633b7bba8893ecb9cfa7f1a864b60da3d1405db322d3c278ae0e24ce99c810593dc34d6d3854bd38fad7f986c5b4cccf61b9106927bb9849ea6a4922a627e2a9c3cf0e4e5eac92d3cf60dc900e857a4a6208faeee042c452d5d10ec924991a395e1fd96108fb62c4693aaed7c376bdab33328e50c2cc699b670d4e42b0bd0787246ea61df5bf64ea1a65fd3c22a41585431771119ea6673d11e1b1d7c23e5d9f9647a488c260a3e9d20a0b9f5ae574a35d5f886c05f615cf2fee2988b3512a3dd000ba26a9284785fe018d508236d287e60e6b0a02f72bba5f24c15113dbc9619a6e89a972e584b43e37519b28374e445d078d39a1b73c1d1e5767b493268ee950c66593475f37e2858dd900ca8484e7394e34364998758f1a62b6f2ca5d43ebe901e885f18e94206515fcaca600e2567f2d706789aa652f6852d047b256aa26df3614afdf77ebfff1a945594f301"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6a5d5e769f4bae98fe33a6f29c396a772c823468302927b5b0f7458779591a5a","proof":"18a8f6e8f5efc27417d8a308ff0e13c42115954e244caa3e4ee1daa3601193170ef135262c50b5f4d0169d17e3d256db2c3321b831b359516f03fb136eb0a70872b76328c384a6b656ff2697494dfa83a46e89ef851a85c9827e3f50c4df277502dc0ba3ad18797c3babb163259fc561978dd74b60031a992b5a537201dca613bcf0ab809884198add7682b4235badf62e149e8b0291839bde885d185d2b7500a9693fc333a20e0941112aeda44fb22476398533d6a4a12714e0f3bf0ea3a80204e21e250248ce6900c21bf00bc8bfef51682a68638d046093ec03e1fe848d05ec8405f6341f8a3bfad6e1540b4f5e6da3927128830415dbad926b1f20f3604da2836a645aa62ebe20773ac89989b12b1a1235a0a52998c80c4097146cb8720160e70adead3e4be4ccc8de8d6a317f713a99f4f5505eb0b3cb25d06ef144df0640d6ddd5c10baeafa209a93a955e15b32f2809fe8eeb402b8d4b3bd3ce71b97536b515399111d630b81afad9bd96868bf6614a91060936fdd4dd53a8c8c8a15728b28f88487dbd7f936630054b886569ff9e277fe7a7663f2d9adc6146a63c034a6dd4f46eaca99a9c00e0b43aa2223ecdcd98a09af8d5bf3bf90e1cc121ad2c806b3d1a7f4cad9dbd21d1c2218db16b562de7f7447396e428e771f867510d7330853772c1807364c0ecd0e122887f9fd6485f7f6ca5f426287543e8f3cfcf62fc519bced2bfdc31e6978b79683e5c7165876b35bc7438e5cdf09a79b9b8331678233abef2b48e5114ff76f0774f90793bd679d0e44f20c842c2d1a910d5775fb6e4a827cc6867ce878a1dbcaff897997e76c75178b8c5f10556cad9ae99dc697650534aef0b6b68c08e785d8e5c4cbcf2607186ff48a6e714eabe52c6a49d08e23f6b41c8d74a61f40174635147582655d3455e5d3673389278c00ece43fb02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"50eb022888a5d1c1597c345fe5e234d7d20411ff6af14746e0bab199d312376a","proof":"d4f17ed04426033dc514090a84721cae696d69c1d6b10c3565e047f0127195245c6abd31a1c0bd66d67f37d857159ded568b27f6b6333a7b0d3c6ee7dfc84a38b421b8831db67a8f51c26517abf4c716b78cd2664ad2191e52fa851f4202f22f4a1e131037b1277c7a00335f207c59106453c7f2642d36d3f4b595a865353e580585f0e58b6a168df0ef468adf2cbc686d929d2dc9b4b2d77a212f3cdd57860ac290f91619e5e5f9fe578c79e0a3c4588be3cb182d678b1ad3d8053fceae610938e62bfddf8a26a8dd302222422de723ac3fd44e976b53aa7e469afe067256041a4f2bec1d23754129f07afec51651c887d8891937351021d9a3937b9cdebc6e74f6ee999657d048b7e2a7b579e100f970f0613bee90cc2c3b72e3f4a22de158f6f2b62f6f6f346a598b6b5908f35a059db7d1e3a7debbbba4afe4c97bb4942c4eaf8d3a1f4351b141ac9a0f7c6982cae433c527ddc7225c5298b3a3393eb171b8b8bd0e5c4638dee0faa186cafaed452b701ba3ec7a01f58980762987fa631eeede0763641bb0efc62529b6a1e90e56a2f2235f310556d2e0b74e895bd1640138d10d2445108e3ba9757c695c18ba2a164571c8b86f5a3101a37902eca596567c74f9174f125c3646c2a862bad1ccc23e4458e3096049f10b89f6813f108257b0b39c4a81851bf407ba53c0862c56592e444ba5b2c52d58cd070a0e1b005b15dadeb831eb062b02e96f27d01e4137b29b62f13604c045e8d45580c1b0cdb03ebe7a9296ce93f7da7e9ee95d71c09eba4dcf58abea7a422a882a35aee69d4c72a46193869ff208555828ccd0aac5d9215348ed11066084601966cd1190a124185b126ee9d9f5b06b9f32412ef4b2b1edf17a90330441ea67a5d593a02afa4006225a41798d51e35f5e550dba62947278e4fe4a9ecebc555bf4e387343bd00000"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d88027306fa76c68517aadfac21fdfaf2db82a52e8e6ec46e54054aded760726","proof":"10b90ef8106cee70f8b501594c9edd6bad0d25ba17a0a528a0216fa5ac89a02a46c969ce159e1ae2442ad8b9274e5f9799db15aa3b16561ef14acefbb4907a7a10cdf0b7317ad553fca34f20ac84acff9743659b35092abd48fd881c35e14234229770075ca075832a18be0946dda2f500c3a1bf96925910a9a2abd5967339599ab6e46000648f6664d81500e58c9226b0c0fe920d04bd763e554ad20c2e3b00caba8d05aa0d36ba4f63d69c4d65d538f012f691863f984698bb7859b1285a0f887277cac1483350900cd440ab5b7f7bc7cb905da73956747c5802701c09d20a0c1e43948e5894e0b8ffbb5d532546a628db12c829b2ae4cdf2c46113baeb154b8204c5bae935970639175b9d8efe588bbf92de57b30b993e08ef5ef7b0d9b3392aa32c3ccae2a58047d479cbccc0a5d5b78e267201e6cba2e4bd5df2e07cd30e07ec0192528bcbe833a674e048213e9632c17910c9fba8a5bb2fba9583fa55bf83295b5f9f94de1fab0be9fa402e69c951c57289316e35243130d17aa8a98077e8826e654cfc1e955913cfbf23af7e4e13bd7612d63c616c71d5f0b02c1a16b1e9bc67f3f0279b0edd10aed0b673296b8a2228a05c2d058214f7400edf66304a8a511cd6226498b895b2eccebb8f77335713fde07febf04fb5b44e1a62252753247c43617296b20486aa108fb11c5ccbfdb3e8de2d044d8cf07cb07daec7630bcaed486479215feee4a8876a3bfa96c3864aaa105655693204ea62801f3b375d41adf1b6cb2717fba9c4980e23548304c12e1c7535c5f1b732a11b7116cc97a02d36cb65c90b2fcd107a38db2ddc6c65fe581ab6e272f7f459db7803f1a6a5f6c63e8487fac449d3d2cecb12ff35b420766d78094712ea893ba713eabdce20da609bf01dc36c13206482b39c373445184593c68a30a9864be56de34a821d000"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"708ccb3503c85aac4110c60801151764696bf029e24bdc7b9314ce0efe2c9b55","proof":"faebb07a2d4cb854dcf5411965526cac52d1b0219d7f84215b7606d4e110902de098a55487c7eed5256f7e59bcf8291e4928ee5cc23156015b0c7c435ee9e673eac73b454bdb471e37d942f4fcf360fa1fc84d407e6e702c2a75e0f5e624776280deb7348cbe631aecb6c81bc0de50a3cdeba7c95d6369dcb16a52481512d16052bb19a3d5ee4433e4215c86cfff9938dc0bfd8430f54339b4fc78ed214c850704f02484255c0468598f3014624e690a624aee95ad0b67a04f4acc2bb3af7f0d210afe98c3a196cb8724f98bf9344f7281a006afaa71c3b9d1711aba09f408092c54a821a6641c875f09694718c3c314795b374176df57eb91d28302928f69021e0bc5d5d342e4792001515b3819075333a7d2e5f1e0deba3259c5e91240d0130860c8bd6a849d14e8ea8885ccaa119a46ba76292f1e8872785f098921aad27bbef127ec0b6e2fabe31a42dc431e9eafaecd47043fafb738da2170099d3b530b7abe35343fdad0c05e2e75585eb7607636b76da60470d0374cee19fee6a90d7bf894e3d23ed0558e7e9a690fc781c97852ab36249cf7efdbe50fa72fbab3a24d080a8e3977a413fd3edc7abe41a1a8eab70bc6c06c0749d8cfa481bce6976b577ef771796cd5eaaa59ebe359394f0c152fffd474a937a09e0896edc2d63cc1052625d7d91f8b5d6ead07fdaad1fc6cc69486e7faa05e34e3eec3636a009ba457d804d293923d843dbe8a753019ccb74876c987cb6b259054fc95841454aed018781a3ac706f952447095a2df7312a9d5af1a761c414806a5bc6b8ececd606a2ab29afdca665a12fa1ea4593a813922f567e3a780897fe2f3f53f610d1d2d9e21287798d7907d51df2c3aacc289737792b4f50b5b2a9768eca8afff8d646fa800c3b8fdc032697d025e824b64e8c2056c5af6c07be6ef6c1f27f3c1653bdc8100"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1e1f3ac5e78a85a3021a0561b9db46241f59d304133968a323b9e9d3d4993e2a","proof":"96ce9592a5481cef70c15675e9461556f444aeffd7ccbfa046beebe7af73bf38a26180c71947eeb1d94ef7cc7e4c8fe78f113364bc6799c7ee91d0c1f360bd007e6897cdb762cd76c495e511e08e8956fee663345b8acff234e9ba0c48b4b3670a8c3c3a851ef1cf3ec9bba1f5ddef5973a5d648268bfd349fedb4af9c693d5d158ecf1d934cf4134ab0949eb58a63f164c442913a0d29dfab3e81b063f1a70efc468c14e7b7ab2864c344f00a45d3cb8605906ce964e568d35a83647448e80dbc94c6adee4800dd74ddd1cbe2232d861be2d7d356be0819fe61fceed4b5d305ecf0f36f50dc308bcf33b7c7d903444015a028bba8554f0d8851b7bf87ad786346e4020da8aad171800e61842151a85e1914db69b8d1ef5883402000dcaddc7e8444fe1f0c265991b53c7c1e793f830eba9de6f555c197da87b70ceadf893a008cf2ce3df317985aebe685dba8f52e9698d3723bd716bc0939cbad8458d8ae25e2f655043adcf1da2941c9aacded41d66acf89e9b276f65de650b1d0d869126b8607f770174bc616d2178179d936ece09cbb65e180cd9f02295d5f2e492c0f40e65c4157cacb1238dc9a1a8056c14394420d64b128f8ed13cb6f32e2ccd8106640eabbea3ae7ab934e85d09cdaaac4e851c4a744717e85e28e842c38a280403e1c8557fa8dfd5134341de2642750266ad2f26a770ebfc0dfc76212b5605dfc7128ed34b256d15293133aa0be309b5b92d756fd1a23af9286151b232402ec0226faec9076778940b5bb076109a4f9280f0dc68e503af0d11cc65094f4afd88b26e4b97c713afaabb0a1f0807ac2d2f6b333b5f46fa3819441b2cc7a77d3364f40cda6cae3a061077484e5cef768066b565e91b3024eb1be9812ec8047f0233104e6800245b9c7f4da96d4f8220beabb470e087e1926a7b2b17c4672b58583080c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1ca846b1ae0882559f5ddb3ea6984f7203601b5788d051a5dc0a579b41389f49","proof":"38e35065599eee8bc8aeea9607635a78502c830ecf885dc8174110a259ba5f6fb4e74eeb6340ab3c29c5a9e3f0542071a04f5b11b68c7d909c4130a38e77564412fc58fa5d908367d935a81845f1b613e1a03826ed10e8269e4a1c69579ba435fc9100b9aadf50fdafcf3ea58938feeb4d402288ec0e85b96d200d0072b4b6758e83537dafadd058199f02b86f88c8a0afa3d54c99662999c2c5d539ccde06022f7abc9c7e4539e258fa8ff06a784364e12f6f4cca9a0470a51c995fbfc4020613193982ce4514d37f057b17b86479a467153cf1edf81f306076a8f5e274130f16da7f1a3a326fdbef8906da3f12f0f6f97ff7d35259cd9bd284adee33dc1c0ec81b4e4f628fcde9d1279bc8cd55166ea842c2e12103ed6fffe85bdc7eeab5471e897636ca856a89cc1adaa7c981ecc34c9d8b955e2dd25dbcf473c2a1d13a3ba63a4c4b23df03233949b48e5a2ab9b635c169bcb2a52991fd8be7c2bd651950a85dc007735d66670b73833888b43a2339bf1e6b251d4977fe260a1c821d46622cc2d9026a178729a669829323628fd30b8ed4950a8869d6cb6118f6e5b986722c0b307a2d94249830fef1f7d25070bc2ddab03775e69cc1b648b3c1fd38e41ad02f566da190e6cd12fafb6f6d2d142e4f3c6510f9b015c94f113f01384e37363c95bd54306c4ba7c2022d4d5619d6b4796ae67b7a01dfa1006de68f9e148639906801793cb0a8cb3334c53b5bf9eb243c1d028fe4294f6dd8c23755493eb36beee7e828aa5e8ee317f81242b90e9f8b60469de085ae780b35300d5673011a30882b5fcb31c1be20570b003b08a54e1acb4a8620ff09e265f5b46b59def77b045f04d1f6cbd47f5a05167330e370a7cd1af407783492425d56528fb189c1ff04f8cd1bc7c8bfeb886ddba261688b344fe30fcea4c01c738f09c3a918a1a3280c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"96787b3bbc30cd6250aab7b07522ec58cc4a432a65f98fb6cf452755be169161","proof":"902c9e016bbc54e3a4964165a0739a105ad2ce8061ed0bf6b71e712da730d87274e9f46511665a6a9de5b57d76129ac5596b8ed560f32f50c0d94f48b4a3716d9a22bd15c2ad3d2609ff1e3fd864dc5db87562d851314ad6c1e564d82ee6f24ed0dfefa74d11ff1020ea5dafe0d098ee401b57fcc2feb4efdbd7cea6a6856e009dd4d95c51a9fb8070d1c779021ae6ba3024461a2e7a9d72487135d1dd51190478e767ee75adc5b0f647f100f5ac34d22f3b6102d21e4e276c08a8c6b913db078aef97cc7fb21bac06bdb9b3b2350632e3d92a0f754cf66dcacafdccf6a29b02f6700501d05b56248de3f5b90c5095446650755574ebba4a36a153d3594df4049280efee5f1aa61d805b72e011622577c9a0c20449a9d85f28e655e4ff446b0b4c93754dd577a3ad6b09cfc4615bd024727874a57f3565870ceee168b11cb36d664b2043ffb4edd763ce2d1c79c23a205e0ec228dc36a8644cd128a78d402047e406b9438b38e5b37708869d70d2e8653f7e4cd2ad135dbb3576e977cdbac1073adcd30da79a8bb05596d95ae3adf5ee792b5061299cb5aa7caf3a0fb1873d14ea39bc35fd509322568bdddccdbd422fa43a2c3002c37d92f79cf22d95179c727e1f642e191d64e6a7ba28f8d32e7a25d90b616e3161de640b6affe52c880b0af6a539d985dc86d73615db0feae2a0bd3614047a08ecfc7dbfdd860b027dd02d1c892d9f83cb5d892b10b934517ce5363c1f427b17c4b13ede9b6fbdd3558850789f6bc7939e7280baa369bfd80269ad140893cc55e0e7d31e81629a0d2ee241dc73e6dca8cdfd69d0477561ce725c5c4332592901f8f76db380be6b801cfb54aab92b730e2e0b2c49ad720ab0ab1823d4d17369f491be6761483e1c3a6d530336dce0caee70de553ebc166f017337f6cd692630a2d9b5b33b807e4322e6ab0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b450e1d27ae0d891291f44bde0cfaf33bc0eb40cbcf0a17be05ceec2a8dcfa23","proof":"78966ce4aeeaa96ae05bec6bf1ffaf08284becc4fb6a51abe293db05c5ff1030882b0df7c27b15aa6a0fa6579dcc4b03f435d4c85a5b5b5275e70f43a31583616282ffa1b60db705699319ff982ee240bd3dabfca75e84a190054f31ddf6bb11d84f88677f08da7d4dd43af0962400efbcc4a9d77fe97a29c7265432d052492a153f85ae121713465abe211adfbd68f8fb5507f8ef42dd4b8eef2581a4e0f705a40794c687f836041469eaf60084d11777be71b8c74cabbfc8491374b7092d055b3580785e12b9396f5af123a35de12065e645296aae0a7deedb225922c2e90e80b4cb5ab900b9a1f93dd61c4e4b9580043174dbc74d2be0fb662504e1772a6be0ea01a29ca8e4d7eaa0b77148c59f86f37748f6c43895c521b442df82ecf82c7a021b2225a948e6af556228495dae3672d6891a9097d4d4833d8d3fb0c71c065c398888f0f91ab8c839645a0f92b24031cbe70ea6e9c0bcf0e36d602c08bf0d22ba6793300ec784e05e70bab068577b86030e67ae42f06031b0184c2528142b7a78dd92bb97e437f26fb13521ade231b99fb6e6a7a505c49c48c5d7919d047c04c84d7244f95977a3d3d1ae8b9385e97b85cdb1b4e66e54d9b3f50574237e170601b3f1b731a2245078c5da33dd454b6373d15b3a8d426ce1c25c6e9538243e9e1b2764d4cdcc0693504f7cfce3e53a2f3ce751fd1c4bf23f65d2333f1d602060d99341fb4d20e2959a8bd97f6890efa88464162fb18e4d5fbc3529b82876160029eaab3a809edafdd22a8d05eee8b675f4ba18caae44b9b15f8419b7fee43e2c9a956ea6a064b9e26dcfa22c231eb36eab954c627a591dfee3854180842e38734f9d51bfa5fe81cfe2678c241dcef2dc26c1ee75f1d142d3ad2be8e529ed0c43b523b73a81d6aaf3c9aadc9dc52977a74668fc332c92235a5372b45c102105"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"90cd03a16b997300bc63276c5ea68e903c70972fe51cc3672e0dd65a893f7e31","proof":"025cab33178d4c876ddfa8ade61d73d414d42bf49e2b29359619d86e705e17056074f259174bca65dcd06ffc4ad14a0f75f2e67d16a26cd3620dcacaab8ce71b2296c6c9b48d06772aeedddd85c31d1ff422dcb73dbd4a2a5273c842642d3c098071aa8631565c09f2a9b3a67091611b4912b03c61e1083a5062585ba062c549dd58f29c587c25048168993a773fab74f233b64b2f6db59954c71fdea495310403d5219da4f6ab82b34a6b6c85a36c4bcfa0edfd03a171dbbb1a1c8b840c7109d1962ca55477c941618fec0b1032b58c5cadeab5ef16f2d7bd5f3e2db03a7c0564d60375cd82a49ba68183a85c9a8bee862b2622fbda1db738f67b68b939b702407f5d44019d79d309249bc41b846be494514bb081162501e67e96f52a03e30c1a8fae84fc5f88b73f6a3637a9c6bc00e960556e55e28916a5d08d3f8afbbd582e3af67718cca51d9314cdfb7d062557f52a4dc2a493c7916d140102ebae2136e2f9d47eab3e01591e4a8ad5256eb9287888865de8c5a02dcfe37a2cf01395591479cceeb3c8de73b301bd90471664483afc4ab2fcfac41426b857c5ecfc4e06fe994d2ec2957a9ecbf9bfc172aece4f35962c705a229b31d9bae5fe8b36fd1ea478eb5421da4ea62c0d661b852b988f9c03197df975b02a1c7069045f3ca62944b95de0ec491563cb16e8b5d072379f298909f5da52267f7aff82e26a4f6c6d1010f5a0d0e2532985e18e9fc0a142d826554d739ba691f6a0f77d9388f9932888fc2a5112ddb945b4a18023f4cb01ef4f29431a737124f6fb9beff998fe014ba657ea4abf2cb2a594b785e00713b1a45ca0d43f79175fa3b53859b7863a956bcfe3b8ce10dc94c8073730619c1ffce6cd5a4246751431f813e56612906a6200f8c4c08df02a9620f54669cb424eed7bf469842c8b80cf7f5b61935b8acadd00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"40d7e57241f9cd7a2397e7eabbcb62bbcb8720301f00af39deca369f784e8258","proof":"e47820f1fec8fc58e9ec3239a09da34a9cc4200a3e8899caaba3a0dd48517d1bc0a6ef34ca6557503a21df1ae57b7364f890c9f2b9412655b49a041e92f6310912a585654dc8230b2f4445091d4fb41396d3aaed36a9439300b616aeb7d56c3206eb5190d9bfd666198d2720bff4e5211b7a030662b4c08830b0b53aed3a5550ddb1cd9715b43a73c8d677cbc5a6badf8252c73052d4422386c084978604cd075f6ca953cfb6a534d5ec7a81f6432263e33d9da748885080227649fa8aef1f0dd064c4337db192676ebddc6ba04e06d67833febba0cdbf7698865b5512e0680b5a97d1cae91621006a033aa0a80a9cc275e5e07c47b684dbe3ca05fe21891b31f60a178ddf7d80361468cf36913bc43f77dded601a7ac989eff8df9d6d4fea636092f783251bbb96f183ac56a2f22b50d1ccd9010ae663894dabb32db55e1614f622de0a5f5b2e3f8e06298fc229fd709e137f78d7c93fbbac50059295c55f2c80943a4c0933ac6e99509f3edf00167a2d71853df36dda447c697961afb8fe4bb42db26ba08ef065f62a9e4365f12abd55642da13e62d3f0a36d6d7d617c7e7cec66d91d29a741398dc651e4b8c01ac1f6275971736eadd4458ce82f1fe39d18c8d420aa800e42c76e82bdf012ea6bf2c1561139505f5de716304f52f2262e2280ec5bd115346257c0253790cf58d947d2b54a1d58b2824c0d44a74b77a9e11c9c53e188632fe09236bfabee26f43f118011ce55a9b4d9588f9f6f532c82c66a3c65ddcf59452a0e470b75e3b20d0977fd86835d492478e72c2e61c0d6ea3f3b7e18f46616e24cb1879fcd3f6e667185ac6ec9384e75158cbff2119bf6a58979f4b0fa85d536d7c4e943d4997202b4a7386767fab2bf5b966813551e1b86e7031c222071267cb7265dca66da5de82c8fe3689578f3dc9c1d655a57bb7df92900"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5a138f0daec3a68ae974cdb117bac90d77332da51f62b8561f9403d84451df46","proof":"96935a96e50ed98495428a9df62c31dee93119028dda38594d91b83e719d7f4bece36c99f020edd0750264d4e0e6f5c2cbf37acd699bbfa6c2d19043be0ba0364297db1c37676499ed3a6036c99d67ebc6aab6e033f956dee7a8a70d17f2894c48d236e7b1387ce135534fc9f833b7f34b8006d53970dae1b631f4226a0d0777b1fcdca8ca4e6c68509bcbc2a80c0faed0af199d1cd7cdf96d07dc45de99540b61b575370d41a49a2087184ee7bdd1967e431f70243108705d29a91fd8fb960724bc0db432bf0c68fd21da642b04104a021bbc58612d29c132b40b3d867c280df2fe471977e22a05736a596344d2eca764d05b45d32c3889bb42eb3a18aa795ed878a81a9a538949ac8158f267d6bcd0ea503889a63edfea55b764971b52e74dceca5b99f70ebb07b90ce637a3371f037182868726fbce776ae9361b9de0d154fcc71e50e9ae0e4338278858aa3030a20478e7b1c3e1722159c566dec9b3886f70c1ef13be7e297a894fa472a876ce7039f0b6149056ae32ca4121e50db91b4f1e3cc89ff5875200cb5d3b83828d65b7a8f6a6507b26d220051a00a604d3d2035484092b0f166db2835a3773283c75188720fd1949a13eda8acbd3f817405c23a89d491572f10bdbf410b15cd3277f0308917c30850782a2634e7df8c457d8105e019d705c45d40719fc0a76633bd334dd20e233cab721436ffb412eb2607c3c4806f08dbe7f5c63e8fd78ddcff1b87d73fd610f6ef2d001f9b96e403b7e12367e74cb45d96d8de1fff92e08367033b6338868b5507f761d5966bc06546f45605ad388b0c2d8545078a011c232649fe200f9237619cf88528fcd21e3cdfd6f1aa9fa85218f2c04a4bd3b28602c164305d11424c039e747b029d1923a06e64c07fa53c2cbd1a9a0ae56daaaf97356c65ef9cc70a58c7a9ac8d91d3b60dac1ea0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"88076d8789699a714753d64152d8357390010dd9e3b25416911eb9f03878aa20","proof":"2c2046477b50116a6a1d7c19d29af0feaee13a51fcb47a38dfdfa89f85784830e2d52ee2305006156d47d73ba4e5d1fe5abd119c51881fc4c95eeb7db8861d0e7ca7d2e667558affbe515c610427ea8ee8e04cfb01ed36a5f505dfa4e386a5182857482736236c396ad261f24719d394662d6ea07504f3dd69f508809796975f029b2c2f6014c1ec64a1de705b4027eb00209e509c24b3317021d5ef94c6f604b4f2052aab2a90072df2aa4f633f096abb4e2a8021a24024a92628ee15ca6c0a2fe611e11e2aa3c0bbe05789628db58b1757000b21410104bb0760f6698e2e0a9e6a9cf27269998330d48431cff5c08ab1cc7ccae4a02516f651743ea1b2a83a60ff6283bd4ae05536a4ffa085a136d99ac2f49fba66fdeac11505e90c7d88623aa4a1e93df7ac2b2e9adac8cd60c0ed7a3020d58d3aa2f45296bb80d2a12535ce3ea80f46a6abd44af274b93bd1d4adc7db4ba09d8cbc64e00f77c62c0d07561c005295c59d49ce3068d42d710e2602c0816281d5ee42ab3e78e69ffe4cc825ba7496119deb0101f99f88756cacafa6857c20d7d138f06447b598f89ac9bc61f2d29bd77df003484afce357b5e767665221e57b4a48f0ceb8a444b92d44432caa80992c748174ca8b50d21279cc35ebc8669bf15991216706e051285a20b53278beb031e6911e30f69c9982c5344d4551410a9898508a2a7e0f31124edb430f0036a362aa495c82fbb605ab2da82b879968c9c0bd1dc693dbc85793b7d70957b26d144b76c0a2d23e008eaa159849e0876b9bc04173b7e5e852880717dd83329281ac3c6bb76c36b248c5361432d2c73a54bf34060f1dcfe56d9681d5c98c285cb07c3305cce329b064bab16fefeb4598c4b784faaf7d9e7f72189aa42efe01f218158ba9627c4206ba1a33e59c38905ebfded57c501f35818ee2786828700c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e8e6bfdb44ff19d8935ad4637c82ac0482f493fe7fa795eb5fdcbecef3021e56","proof":"46aa4318b1789debfcb80fae00f46cc73f1cd4b11d8b46bb4dd5e8ea01a413090c2a788a4508c310877659d289f43c69ae26960c3ce3fdb9f595f7ba695b6d14a8051d374b13a7fce6c4b72e048001f7a69b4d6549b9f445314664b3d7dcf164fe2a7ba02110c04fb4eec006ff9eb40902edd7e4afdedcb736ea308c98bc820b7ecce94c4fefd18afdab13e030037cd5f33ae85cb6eae27325668e5bbfad9b0cb0af5d681cf8be16056dceece2eebab9afa90c2ed88f6efe8034fe536bc6cd066ebf35c0831df56a2e772e755489a429f1ad5724604046e589a424e873715d052a4af10899a52fecf61d42946e34e41e773d6f489bb684556ea9a17c00031d735cfd5d1cf63ff9aa9d4f11261072eb2fb61593a32b1208d171bcfcf716eda51858c3f28484971b02ae04b1c4b4219c84bbcc538a5c026d6c844ce4cf3892704bde42028940149256e8257dbe1535a712fd0b9a3a7b7d16baea76c4dbf0cdf73b80baf302427c9ac37132bd4bf437872ee15018e6157c242046cd15fab8a9a529a6f439ebfb35344f9ff9f75eea78994de2593e33b9c04c7b3b96654530c814276c5fd932d79a9b26a6b79ca1408a1621f4e3193d197bf64447c00fc785df903f54eb7b226e17a7bdfca6e7ff978c5fc5a86837154419ff002839ca4d9af5644e5462619553559d2f8cb4dff3eb1c1ddadb79907a618d52406fa237a7f10ef068a8c5e8f2ba1c3d5dce7891a8c297e9f2c376adc9e5205a0c201e9cee00dbd114ce1d637a86635548e0fb755315c5198f845fdad9950b61c29e1fa70c20e0735ba46916912ed7d498eabebca40dad021f5168549f7c5d38bd5253fda59a1b7268b1c10f19b2359ad0cc6a4efc2264d2aa5b3e856e5e53ec912c56e00077e234071814b2ef5ebf74e58bf1b3672a431aab7dca7ad3718a5b2f1c56d535539ba701"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"021d77e4ca904532cd6998f3d15511dc7087b12e191ab90cf64f46118eea541b","proof":"84f740fecd93f820996fbabe8c04d2aee178b199ba7e1706934db05f50219313bab2eb9e9fcf048e0e2e4b4f05ca855df742e0e0c6e5cb7282451e0a66343a018abe0b15e9ae6a0a1a2aeea6d2077140f856513d723b1d8f79fbaaa531d410253efd028a87f6e23267ccc02921b9b693d01240fc3b9e3c4d736d59ea3ba1716319f41589492040ab00507796b3078ee54c5cca0856d88cbb28d19b69bdffa70a229ecfa4c5bd91377f5728128bd54427573b0e448d5cf44c38d471bb3e5c300b901a7966fb1d50119167d99200ee0654ffecc02fb292609b1da131c191a48a07843f559779108ea3a709370eb87dd8107f6e59beb57372b10b299bae4dd3236d94cfb62bf5cd22c23ca594ddd3a72a2f589eaa32c0f1959ff4d3f97ec11776489a61a2f7601f8aa8387a13a5e1ec3b4584e1a70e8abe51a38ba8a94e3cf336008ebc16049a027a311154368ef10df5b3e51d45c7b956498065f403a94f0fe30d48a4a11885a451a1cf38ecbc472347ead16b0f30a55cd4fb21f7cc6a78cd5937c6863d7c2b1248a53a1fe62654ab4d14fbcc5b8750c8b0b23ded14aa62aba92736a566b8cd1fa8fa07fc5dc65df5386e87458d7a251a2d7d2f5a410020b7ce0536e7ac193c2503f62d979b2aea768f0cb800a55ef58bdb7bda67270cb163f05502ebbdf36fbbcbf1c8c178cc666fd749c6667e24d1db18c6e4d4d45eae83d35f5e6a7004d57039727cca853820c0ee47e90400524a594d4a80365e7525b46f0f781048d7838ebdd035a9c1930acb3f9f8c323ea7819878c403148377549efc578635bcd873a3665167befcea77ce747c97de82f68ee4039d219f249625d13116b37acf01150cf4daabfa026d9c5b473daf9d7248721bfeea27eec8f92611af0278b79832f341336c468a90a6973963e03a32d70983d7a13951bf5dda1612d30e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2e33abe6d60ae640d3b6c58798b463dade10a3ffd5f6f5cd0d6b5094a5d66906","proof":"7884f68faee0488674451d26c254492fe3b62efed636ac90b100622e6aaad81fd0ca6479c53e2568a1bf38006285f65b19741604e82813b872e27d01c8e4f3120e358362216d83b1e8c99b86996cb860294d2735ca1a2d26ca64a145842e6e6aea06b88582eadcd4e8e5b27b8a1aa8569a0736b5efcb796a1097566bdd52aa61e3a8f5f83ba8b6c9e8036aff3e1dffb1878c6ad574c40e1093050f90ce45be0318797a9b446ef4dc73a7632ebb53d6c1a3b26dcc01447e7f74ffa24985aa0c0708f68b17359c7cd5dccf7fbe398c08ba0a46dbee99639b1482be34552333720ae408511b5ccf54a45e4bbf8c12ed565ba7d97cb0d02dd1285888fa59b895762052679d998dee6fa7f9ef15a9290a7692495c9b6f95a791f70d1e691fedb0a80f7c264ffa73b4ed50ce803604ed0013bcdfab8aa582d8548ed9cd2e697a23dd395e40adc5a3a944fa176e510457684129e7037907a13c4a769a62a732eda3dc288c595b63e85e8e0807b3ec5c3859410e6820ac032ae0aa44ffe37166ea37e31ce03b8eaac66930f65acfb0a383935e74b1f8e767d7e903ffd4db3ff71d9bc22982cf18fb1dfc67107cdce045a2628f52895f95af1f8729cd8c778435cd2b5e433a1fe4aa86c015afd55e3e8ec96782136eb2b877bd5109607a8862bb655466267eaea371365193a444f63820eabd7eb6a92d4dbd6a189e8f988ffc09c498ab049c3a20142f0c60890e02dd6b0f8010fcdefbde56ddb1259ee2d87365a86530263276ce4ac14be28b385cdad407019a7fc6f0b2e406bbcc52966758b463544741a88fa337a672b6a8fd9fefee0f0bda6f5503d5ca2be8913ea6ade211e471fc371ecd303418fae2861920f91a75a798299ec78a3fc4f1af8be6a50b9b7f78090779ef331d1e8dddfb88c4b76c8460b29076074fc205c008dee39384d59fddb205"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7488840f70dd83f6d2383f3383fb72812ab40eba771c8172b98a54965847dc20","proof":"aa41d85c059cb1102bd6b3d7d590e23f24f9a99a70fd145549a68c108cca1c74fc77b269478fdda372671c24af9787d1dcabccf03e3014947184ec0e543a171c80a2da2ccc6941306017023f6c0ef9998bc376cd53c0fee10113b533e774fd1716a7efd87538dc371b7a0166f384d6bc6ed214cf99d726e8e46b3eff0a62af4c72c2c27b1fb03ba174fbb70724c954436d5189e281a4fba53c1ca02d50053408d2ede88890aa215dd14c1e490030d13f15f34d6302de93b6cb5311771ddcea0cdc83f1f1289c875097a3abe6d87efa5d124826cb298c3417c252262b27f199088481b4b4399fb0bf2f5be17abdbb700b575e4368463ce23e878a2b528355760640911ce671c191d05d516c2ee98efe031394e1dbb32f551be7795545ce731669c01b264aaee2354653a994d7ea62f0a290b81805b7ff9022f900611f6751b21d1c56fec67e5289fc6382ca9ba626422b9b1c2dc9ae687b208a856abb01dc9a65a2d12a6b611e6e35efe0eb243f271e46034281161d07674346d6e647c287bc7b44535669a2ac429778f9722b36bd7922bc2affe530bb4404484ac3d15006a2297698279917126c498902e2b8f4959ff4329a50f2061eca743da4b86b05c49f6be8852b466ce2109b6337ad518d107ac6340b39f413ae5b1f9c9ed02f66a73c7a243151dd9c2f16e3f8650c4991da45e7081540060ea8923f07875db27213ff757268d0f06a1e08aae6122d016cd8eeed8132ea13ffbb22e23843b7b312f25063ec63d659a54ed589fa1dc832da4ad420bf36535638a82688d2f50deb82d5681ee41eca2eb69a366bcc9804cb50ee07d112bebe29e6ad4a120d98f07cf05be5361946a1ee1da1c5897660e02fbd42aaee3e641681f8f1d7321ebc0fec03cd58095923aefb7f55e2b99499058644b98bada3018ec5efc0a28cd8fed8f26c625b0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2eb9dcc4e6ee2c717f0e0b4027bf96f85fe47c3b9518cf6fa42b30e7a9899d48","proof":"4a93ea87282f98b2b61682aa7f30ac5bde24bfd440b31710a71ef461328f5228281161b08f88e4d3b965185829ebf726a1c821da648bdad701bb0da874af0c3d8c3bb2977682d4e2596cc3e54675022aefc2918bd134f98c979a35c965cb142ff82f2e749e61dfceaedb054e3542f21d9de1a46991f06419acb7053b2bb5312d025fba004e070277d867331cb6feb385ec101bc85095b668325d75f09737e80e3e0a4c1aedef6eca2be6cf21a31005554fd0aee7f94725020a10d8d3b301180913da12f4e781ee262915d06bfa7601d09ae7e8b4988cd87757c4dd77fd0b2908b4102cdd5eaebe24693867e9ca72d41a48bc7932fc8b6ee3fd21fd2634ac273cc269cd04d935a466c88a06c8d4ada0f0dbee9d9b9f96501dace01548319d9619960ca3b0004ca7282b05cad634be6546d3446c5ac4b82c69d3d0d57fc8a73e32589427c0609ab68c960806a5a33c4c2e9fa10cf1680de8cc79b0e0d9ddabae1a1e72296f20c0bc32726c2903c85c4d8f5add0f95dd5ef6ff81a48c1df7f8a91d788d02f7cbd28593d7e970101a6c8c44e0a60410fcaf4f0cf682a530da6bea2b94b8a983b0e9cd5405e3a4638abcfe3cec07838bd9b9bb8d44eb1a5313822f14087ae035e360c2e746e628790c41c4ba71201e6cb7f22d5017a99e8980179348309e4b770158c14fba13517dba814e0930e7f87681c071c455f1c3cae7faf952a854bda90606499444d1dd88f157496aadc4bc409845622a9dff64a3ee135630b2359141e792fd6ef96607a6fa838c76134d23d3b5a67aaf99f7060f6eb6af427e41b822df175414f5d1ed9941578aaee7bcf4c6482fb3dccb603ec660a69e127cbc25aebe6d8da663118ba8a9fadfd8b2a59e287788ae7c53f64bc1af44560762fed30d4a649d986620491996e8c6b9114714bdd1f5e539f232f6a72655a408"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"683e29ee7ae2e3f863d669d2cbb7f0f02b82ba5387ade23aae48af006b5fea2e","proof":"dc3efda92c9fe238ea15e2f51bbfe3fa173bd1790ef266590c1f8b8e992f14080e49ff558224ab9629e9ee6839dc9b9d6dd9dea1128434dc020bdff24372756920e7d5d01ac42f34b5f519b6415c4668118d1218729c2eb0345b8ca283fcf23436bfabb86ed3c344daf8f24d285671fe58c67a0b6c8a51043bfa6cb8cfa4b42d61ae19a0bb9522da9073acced686dd42a07b7e25baefd47483fccde6cfd65d056ea8c01eeec982897911f0da366620d2ad0efdceb3925cb3e9951b80f6c9be02825e25aee0da131d595ed4971d7a97b6cf10194d7f98c782552625efa5f91e0110befabccdc2105dc504a671d3287b9bb64e53a0cf3a836a3d66063e44cfe31ab8881e7fab46fc9ce6d557292827a00835ffa2daf7bfe5716290d97c0e9cff310c489d4483f9c91819026d3761688216f50166793738f0eb51362f638d0ed5061e5de01abb99892eba5c32f4bf4517361796dfa34d3bd2106a75ad1c2df23025107292094a6d1c82a8700163c343eca6358b53347802cd7c7ae14ef7ab6d057f981bc3df8d6890d20eb319c2a75224fefd4e32dbb39118db9dc556bcd9ddb8535281dcebc4e0d7b65a76d53683cc7ccbb4ef629e9c402763f470dd92216f6c7234fa145bd09cb04ae77527f0a1c70981e8e7b150394abdd927b743833ee03532c61bb02e21ed7bc9cd4f3f6e05a7e19549bd02f770f9c318b49383d2424cfb66a4049b6aae429213ee3793725d9a9e42605f3a8a72a25dd9094e76f916e38860a406bd363a0b54a94a40e06284bffd5aa59cecb0c2eb649b4136dd8e7e2dec45ecd435b3cc488e4d5a1c2caa9624c10330114dd131e635eb0831f3b42d49b7405f0646f27e87b5c1177b252cafd8e09a7ddc0325db75583e5c3ecbfd14b6860d9e8f2448b20ff46ec012cc030c43dcad84673ac5f52d18ba0ad43fb3eecd0908"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3896f5e44017426dc0ef04b41743fb3c7300e7c76b3a5f19bf32e2d6b520811c","proof":"8edd375422e0958c9a90377593268aca5c8ce7b9a82114680ca368b64e40f374dc3cdb46b13b3b4701bc4f6ac22698d50300e44b0c5f144f61059befbd157923eaad34874db60ae5d37952dcd260202dc7e2c184a53576eb30d88cffb328761a1ea76166be64ca20dc2dd0eca43d207d8c033963985c27b02d5b052667d8bc0a1c55a8033bc655c4df03389dc84774ab737c6808aa1cd9808d816a5a3ea630026485bec41e4b2fcc2daec39554e34f5c8c4d4ccb9eaf58ba2719e91edddc130a49a5bde327a4bdfe4e5dc518e7ec015babaacba03d5a9c465f6532fb8937d80a00cfb45d158ce1b14bec5dd64888c922adb0bf48b035b926ad7b10ccfa41517c7e707a4003bf373123a3463e70992a2f078507af25ccf41b0d7a08440f14b722dadc38ac838ffcd7dda1228c932d9e8682992c484f272a900f480665eb66b55a78e11c780796c7a412b805671df859b87ad55d30b2775e798292d724e9de4b436cc38a43b4db9f30211d1645d4d4a505cf4883ee20f7087c19b90edcfb07b6253cbd36fcc1327f133ce696896fb974eef3bb88258fc6828760b6bdba0ced074ae6f665a293466b7867b0e0c1605a614a9f12f5243bd3f03cfa654dbf8c511e4f7681c4eab8a37ba8fb875afe19547e7284a5ffeccbeef7a339570d3edcc6ba2ff274c985c490df5e21844eafdf22f7986d2bdc800644a65e161f77511319f76cca6dcc7ccffa0e40f8482005f56cb2b0c575cd40cc664ef6981ed3105686c10b88c49a5c05d65459fb006b73d1ae2049750878a0efdac99bec9a9516a05a1f2998e4262f7278ee75347416770489f4c941d959049b5293b35caf7f986922ac11d7e8c239d8d44be9e44d52bb7f548be7c90c9769e006f6e7059f7013f3acf60bfb1f1a621c573d83e4d81a486e324b89bec8f3de1993a0e187a9ee4ff2f8f003"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6afdc684eb09e0824e8fbab24d2c7b5cd7a8e77ae12238f5052840cc25950606","proof":"8a0959cdac8f727d57dd95d927ffcbe39922692bafac9f6c8492b7a773ed3318ec25a7f4038a9ed443c6a0b3718e8f4577d0fcc54db1f6c7432d4c1a25dda771d8941d204dc0d6283226e4449d8b2c763f4b14012aed8fdd42af4257df0c5d6c988dfad1727f7e750ec941ce35572677f6e5176f8b67fdf74742542ea1f51c14864e983c01bebfd1541078c67a0ef0c6c201a4f7719b2d32aee9ba0438425106705a0b4bc2f7fb4eae30d5254926b02382d609c2df6ec3e069971bf117a63e0014556d38415539791ddf3d94f0123012b6c3cbe26e53e70b517ae8cbc677a80ec4c510490c7e321670cea6538856d52772b3c591107594898058b96d6824032416acadc347162f0895871672a9a2d78a3d97c17ad32740539b6aa857fcedb812a0fabd1667387ce7780dddd1f4f050be52a57c0deef268642321ad12914c20164e7b7683e18a1f77019166a604f710cf0b3c8a02fd4e025a928c71244eacc742ccf046a47ab079384bb98ed6c772602a2e34d85d8a371fdc246c518cb9733a1ad8e4c7cf036e4dab1814bfe925af6824689c5d74790797a10815330d4098194a70222d032b790a78edfb7454c831354a4b8b59de6a475d10e0775530e452b918ec0d095c89921222842ef63baad0bd34414dd05cae664bf860d521b25661b039ac55b836bbdbb2e4a8616b24915e563f17584f1b321636337c2723c65bd7f1303e6e31be04265c0775304cd9f0841db51abbcf52b100f526c0840cd387b2a64b408110646f89ed630d760300500691c07849addb47aa7dd9cf964216c1d4ad2f5c0a4840f64059fddd18681a71ea455cc03cdb5c5721b62fddb149f80e2128127faf5b4db5990cf6da642ce3f86d80c7dc0c907dd5813fdbb85bfb831b1dc6075a875d432900bee32f09bb234ac387aa92163d2eb371e3aae43da758f830890c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f222c0c60c22a0a08a378154f1da27a5c43d72315f55d717633e57e191b60e7c","proof":"8e0e158101a2da4d7400891cda600c5012171fa24d6b8dbf7bb78621e37c456cd069cbeb100a8b70e32175082f1555a81bdb264bb774ad359eaff6d509e90074349bc781d036cf7016c0e12f0b29f1eebf230191a36aa3c09e2477065b1f6e6f5e67f8b6f8856da64a99dadcedd28bd0b5050edbee5cf0cee5ddd2e3b4459d3aa2cd9c7850a9dc0a0342e208c150be75cd9902ea4447a6a76222d72a9564620b6b96092f2be133d5a8185889eebbbf6bf0bc8ec9d7a7bd8d2c7d7efe4e962809dd7451a5e39548217b0ae4ad7c622d859eb8d5c6c81cfe556cda1b2badd82d0cb4a172ad38d7eb1a0d7161e743abd0e94ccca73d827ed55fea01974d805fac7a362b347f23330f58afedb6d0a26927ab0bf7fa5e3d794b4be9ac5e3bbc9dd845b4c7c9929e2cc041eb8981b5ca358cab21a7bc8258926e2b9de6c1c6d4cecb6d724a88567ff5dcc5e93caadee89258506ba252999837e1c1f09f98c9ab24212e70a9a6aea1cbac7f179938b5c81e21c387a426f86433ad07fc8ee4f8b267f4512c376687ba7119109c187f844ab3c10d10c20dc793030561c4c8ddcd2eae1d7416dcfe4684c4855c95f53fcd385c57733048338d852002276eaeebbe53b15f51b4eab9baed447342325be63a9bd4383409191c1613c92a849f2bd4002545da74ee633d2832dcc7e57019ad5970f4bc59439641e7d1a3733daf921e0c3819ae79dabf9ee7937e8e8b1da42c12265915b4a1d76b4cc5237bd3ace90ccbae4f8b5542c86aeaceee389e754e8bd8bc58136ae2828faf180c7c54f5d82c39ae3804452eeefcbbda79d56f1cbcabb637baae5459b66ece68cb1a182744ce03b4500f73d6780418223bffeca038c3e545e918c25754f9578693f8dcefc74d3d7beb7e09b84adb1fec55a77d5d5b68ebfbfbf2aa006e880acfe119f154a2d86d2286ee07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2aa50171b48fa47aa9b38cbd4811a8a029351988cb598e0e6cf7d2fd4608c36c","proof":"e81b385fec5c55cb251f17b9486240abc257f3bdfacc6afce32952baae7b467598d9d8f7f5ca377edaa7658740d62012cac2f4c8d6a27087d7a65bc848aaa35b603b22e402e6515b956d7252802d5a1259ad129dccd9167bc10c47185f90b2166079eb58d0de7b974a449746fcefb7eaa59312d1360bb74180691b161b3267604644bbe7117ff37abefc7ba2a8db380b7115960af12ed4931355c5fde69cef07b4f2e628af9a106ad6f6622f74ddfdf2d3dd3153424fa21653e79d24e878d705caa99f3cecd197085a500254d22b9af5bc36782c6b10144b28143420eb047401249432928aa9749c800c855542953b2fca9d2d71dc43916233fc6694801b242bbe05ae6c79fdc10e174bd9e8d18f34699ada3013f84047588a0be5bd9e67e471c6c226b55a52bf80cedf1baf73edfe0523caaf0b7089cac591344c446fc8e43a909b4257084bd44b0acde7107d191983bbe4ba667c81a0a09b8a6bab00a88b0b8c99d8cd276207d54cf882c4887d1bbebdf9ce46db251085a07d40d8616cba3af834331e5ace11b876dc1269e05543b6867c17e909d1e54e5241ee3c8bf0b110020b9cfdd95e0106b18f75320595aff091bf64d4475648948bf766b27fe61a4ba0d25ba93b82cf0508ba87e196b49ca6ca1d636e0f11b544fe6884c2aa0ba606aae0d58fa631f5ca20d3aba1aca7bc9308a56ae52d64178e855d18eb1b8c0835bc7985bfeaf539edfa8288ddffaf1f1751a709e976d4c4bfed6d2583caeb3a4f58297a0311ea791ad7014ab6d3788cc98cae45e892914ce468ef5ee6597ab8139a328601bcd7950d6b705b334eeb8fc1c141a900a9dbaff970619967f387237069beb850581cefdac1c80f931af75366bea3f6355b1fa19f3a4676287a4cef03db897b6246731257e1c7681cb903a4bc14a92560f6ec8fc59ca59287bf2cd107"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"427dfa9aab08db4c85d5ec470c5cb3200d821bbc8f77a7c87b16fc544d6f677f","proof":"4a28e45a4ce39387d789fa84a8524fe502f049225d9cd0c81a6330fea60a4011ec1612d59622c33928ee07f3fac0efc83ee074528ff57b587d3efd9999bb24036ec6bafb459f35bd5415c8642069cfe069360caac4808e723f8ca9bbf83cbc31be00585eb5e0a4fe1f8f44bf5584ff44ae36e35e8eed4e2699e5c95702d65d4d1593a58bcddc115341863032008373642f396b4e74193b881f595834ff3a1907c6868b908c0d00afa4ada30650ec4c4879bc4bdfb1c61d0f4343d2a2306201018cb2e0aecd97fe6a310b441f4298a89f47552771255db00add7c83d62da1c20c48702079c6f3b2007f5f9362d66539fdf3d25c3f559c95ce95c45eeb89fc943b6847b94f35432497a90b01a612859ff748980b52c42d47c251d0575a251cee234e650659a206602141bab1409b2cbba08e2fb70278030cf4929fcdff3f161263e89e05a81ffa9651dc2dd73249559cce5a4e5d734b2f83dc5c7e372e8b8c74316ced05d93c503ebb152ee65c1f5eca0aeecbefb6d2af07ded704643b24c0004f769fbef527e8f2960cceb6d8834ccc24b19e08f41fa315844d31c181f3da045c928e44937a0bc4133a0ba670edbac946d981f6c64e8ce5fa5e16134d53f846082644f333e2661365b56fbe1fc5044fffdabbb23a772bd9b6816144d833d5ec68a0da9654de22a7e3ecd04a4846b85dabc32cf4c82cecc0a1bb3f34323a3c920d3002fa898c4294c77efe267391dcc54dbd519528594ce70145e55a60d7f6c96b308ae932442bf858894987785da82c08121316d4d4203e0e69bc8fae00122f7cf20f1498a78e5f83f30f44f1a401b45f171bb12863ed1c76eb97a985eff03e72c42cf8f6f5e5861d7c2f1b0aad9e290796019bee24d959669da4bf6bb6c8ea08cb345a6064a5a058b170493233daad332a20adcda45d2d62f8dcf5196a52440d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e429e67ea81344da377cce36042a3e4c8005764182e966f6e35f0a67ece5ba2a","proof":"de0dc3c35769cbf087cb40b2c9901cb4eedebd1bef635c519859a7ab94894e1972a1cb7e25cdfd887d744d078141223fe99988ff0da6640d45cf3a48152010133c58ad32c4b3afd6bfc2eed7713c1064f8e5919f0f52d00f7d70388ee3d35641304299cc12f07b8f3acec666fde877624b26c57521d29a711398490e6acec809fc767cfdbd83418d53633c50cce04f5a96958e4226f1c7711ab12603238c2c01228e929c23ceb53929851d2bb1abfab1d9cba361c4ce9c4db161872c1bd18505a45cd2d926539084cf103e9951413076580b3598f54d256238e6da1ae05a4d0f142bcd010ae6e84f2c64d872e3d92247158378d26dccc5b9b61d94777bcf421576e87f7a2b983dc61fee66cb18f7b19bcdafe0451a6c9cc4b23d2659048fd44a004ddca3483a5082670431a385382b95cd1a49a020f425f4ad29647eb483e40d36cb95fcaf9f6c2675350ee8668cf6e4cabd578eb657b5c63d65ab6b1d03b67bba507eb9d69f3ccbd372242c0e7fee1855b1b5df6fe8d08091b749a7d2831f0d963ef4408ff6a7af4ffe11c83327acfdc9d063d24502006ab97e0a2bfcd8f5040e6571aaa40ec254de4b6eb25987c779075504e57cac9316ece5e025f3ba016f4e8a3715967391e66196471adae37e747ee433396f63b0656fed38dc6201da5676c761820748e9a4470d8710563e9662df273a83f6e3aa122ec6d5fd8ac7234314cda10e906cb3f91d2fb25faed4da94504f8d21be95f416b527edd4e138da04f62889f0ada2778122e6ca4773ff053f93314fa535a4f3e2d534e31c111e4e3cf88d0db0911fe0a82984cdd54d3be8862c4e2f99df138dcb4f802b54fae9cb67d4f2bbeb342b0e47624b976de9cf9da647732660970fa253e28adde4d374ed0774b54a8896329198ad6d2d302e0dba4c45bed6d4fa11fb342f68644c6ed46a06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2459b3172a54fea19bcce8b9ecacb97656b8297dd8c0d63361aaca8babec2d3a","proof":"6a050b8cee0dbf57208e203792a6ee8afa991cc29f387b5aaf75421dafa090605094208b7f2a32b6645cb5aaa7a6f2b5defae72eafed1b3b3becaaed9138a067a8aca0a8940ebfe20d9e6d687953fe068806f635a23af70f47d19fc02195e44a7ceaeb910b3e51d9405ada2f05382b49a35c08048d10208d6a2793a2f7e1ed65b638778b0eeca9d4d3f8b9f52c4408f3227d4a3235e4c078580b6991021e4407e2e846cbc6250ac3be8bc56b98755bd9743e7838fa3a9b2920271ec123f9440aac48ad5fccd712394ec6e14dad75d32b40a4d9138ac31087b344db730c248f0da2fb1b41e5b82dabc8d7c89e35aea23d74c817e40191b1178803897182cfbe535e48e62d698670776eef4870d5c7bcf46798238cd4521bab89c6d8a24a477c367648b5640dd9fcfd266253e88d2d5f2be09c4f5760d28c9ce7054d6b4dbe1e491853f78b03f27e0a54f381d9ffa5b4375c47bc87eca713a83a9ed3a338cbeb69aa370b2ef78145e431fbec03fae12fa04294c8407c3d26c9a356671531c6d4258074f711d6e28a703fd56a46d0c1bc0473ba532265fcbc9ab45c8d9f08ab0430a899c026ebf60ee0229ff849e25ca64160211cc05fff186c659cc5874dd19b2470fca9a0231cb70630facd83d244ee544a0bb41f911ddd3897f616e149717108b075192f38170f5cd815cdc6f488e47a944f671260d565400629228fa1312d015a05abdd1c06676f3d831aa38b8d2267dd0eaf688e8cb8b64b2e11cd5b30a746224f5ce5ff428297a2aef1a36e59bc58a843f09734d245f0ef2a242e1a962343fe2787f9c1e95e1ef78f7642c524a0b03fa9a9aebcf934ce6851704d2d542c7853b0603f3b2b727d874f453918e8114cd77efd14fdd748c82773f10b3406990cd98443124418b21e0a903ceb805473baec86444c50a130616ea9034313bd9f0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"125ef29b7f211ccf801eadb12557a9b320b3576e65b628117b20d8cd894cb342","proof":"de95adeed3c3eaa5a673f448086f0d8f4595f3aa6255e908a22770d303706a6918a66eba7b35aac10f12cce19af33ff952340ea9151ecd0c63b4791ec18f6d3dee5f3ef0ff9d932bb1a9f114c2c8873a6271af980f4d33fa440e5b77dd84692caeb59bbf7af07a8059f6652f2325a36fd9aad21f3e0001ff14ecd817cfc72b7a15fef071259486cefe72ad329ff7cbf57cad540db055d335ad36457c6e9c2c07d5e3e5ba6062db27bba1d6a90b29f2a87c6f653acec9c3f338c5031b618b1a04801a812e3baba95408dedc4d46f177841e7fdfab1931f96b9d585fd2fe4deb0aaa1087b401e01e2059c52bae3904cb2e8aca788edd457068b0bcf026bd08f74b82e3e0f75b0ad28671e975914f0225dd03d45532e9a321a3ebce4b7d1151d32280c9e84cc83ecc252be2bea35be3a5f4d1c566316b24db6fd3f03de363fdd66a08e55b7d85191b3c9b748aa560fb6607faead8c493eac6505e3565c668986a20eeb53afc3401998bf3d9077533e1a2de663a696559db991c564712858d3dd61ad8ea7e620394c0c7402f23f332b594b8f45c2424c7b9fd0afa0047488aaf2b74a6ddc97c86d8f6d122fc2a486f365d17a459c2e5a800f53d87aef641ba942721e213f58c502012f6002a4bb36ed0e151cebd9d5dac02dfb22a6fc04ac6b7217094c8c990be25b492c46d3a2ad04e3ed1665114040f5f2d844de50f66eeff2c11e0d1550a5c501c4e307b9bbe1966d370f970c711241e6c9450d10be464c4984528f2bfaf35af143949a4955489bb6ab4413fbb5af9f1c26e8a6850db0b3a1d526e12f3837a4e4de09cbc15e5837d024ed41349854d6c4381918dd0eb15b29a1b053f6420269e52875ae23c581624ec6a5bd2bfc0275b36c2ab4d4dc29897610e10a35c539169658f606d1d1f52ee8bd0831c5aba82f1d26f9f7ff15b462ea608"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f2d4c3b111168aadbffc83aee8cc15343403831e48d5f56d6799aecb84add432","proof":"52c59ed827acf487b113e5eebb086b54f2b04d3375e59f00629a47ea80970a39c685c52a55e3deb19a0e0ea6ff36125937d14eae2bcf5c2cd524b4a42f2e0c3aaeefb84703bc2005abd115f1e15be135ae3ab191dabc4a392e9d65ad1e891e4c4c4e731f956cf787ee00482671157cebc5f1ac3618501df6b200e3c77e61e508c136282161321bcf40a0665c9bfc505626c91c0ebc90c865e5c5a76f2804de014a7bb52dbc5e9c67c877157e652f24c5c6e5c50298c559be213b9bf8ab1deb0f585b5aa35b2d2b455ed26799f31e1056dc7d9e0d4e8607d827afc94eddb265016c0dc4e1f973c8009cc2b38143d407bb0b1239cf69e626eb1f879522875200066269c853280902fb77ae488bb8c4a728be8f3e6cdac356744862cd2a4087c64e063be6716cbb1481fdb77f5c8f189481f46061815ef4e09a3d6873e6a87b9658982516b2f4d0e5cd05eef0d374e5a0f9457512ed73b2dddc31e2559835fbcf2e00f5f0b4c8dd789ae885fe4c84b3b2c55db754ead24ca699c32732ae8738b5394e965e196ff9067d3b5caf630da918d627c7ac9c397d0f662ab8599647cdb333fabef6ef5f9d1d61bc2bf14c4bd5432935e2126a8043f08fdde1d08bea1d8b39a2496a9c7136b2f4bbb99c2a45b591febcfeb5e410614783de196f5fe2c0fb7816eec52401109ece2189a881defd50dfe2ecdd5187dd734d1e95cb061056205b3e64e4fd430e9e13045f252862fb3e589a75465c47173984493331735613787c6ad0dad86112b15fff32c1338cd06cb916f0c377395217325ce85d1750dd415b38b3648c8fccfd76d24372dff3f9a6dfc522ca2fc0b7d5ac2b75680bafcbf22f3f177cd9f8627d046fd0bf997f2f812413cb387ff96fcd337f812923f66d4f0787d055ff743f11cec50568023e9190750dbff295366b3e324bf959ac05d5050f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"62981f400baebd11ae09e1fb8417af01579b6281274bad2e29847f87db7bf02a","proof":"e0e252d5ee5bc964869db41114784c5b1297e9fb34e4e552750423b90c2ff938c49396689a05b6e4be66191c23d44562c92040a0945215e1efcccdfa0dbcd71940ef5848051a531834e30f83aa00eb4c32a210c823fecc672aa8eb5c1dc198360a82197c5e80efa63f46a2d5508963d2640e1b22e6eefe426328aa2c85c3676247badb58a2867b415e23139f0f672bd08bb92785534da692c074b7825f9d030673de4ed1f824bcd14a2d14d73d88d2075afff4229fc7bb2a5d4a730bdb57fe07c3664ca446c08055d290984b103e94081e83482ccadbe8fcbf37fa0569f8e3017a69ee1be4a81e9114e628d7877cc18c11aa8e96f4370fb9166b767e8f27407492dca3ba51f3542f2168f8cddab9637b7a709f2cfa7dfa1103fcd05437aec874f03d05bbace7adf1fb174cd56f8ea36129c045bb3261e81c9760bb390b4ec121b23662250836aec784a849ff02d117d5498cc91567c1a3756dab619a0ccd443ed4f3bddd2ec4045c138bf9824c9c65c95454301804aaae5605ffaadfed591c14aa7d07fd53333eb177373be02c0f1f5cac5985f6f7bdbb8b68e6b061c6bb604ba29b84926a132eaa08a73325ef90604176d622a38fa841f133fb0fe7153e6b5c96c37b2d4bb1477545c5ab0dd15ee736ab79a0514b596c05c526833a7a330419bebdfe188ff151c1aebddd06222d9dd1d4205aed36f9559a60c2216947cc7b5978723e83b1d11f953ac7f5e19b13fe8197845b0b4ffab55d4a5c2226fb98044e80ac81b4255817d81fa0c0da90af60023ea081942566bd105b49f577cb6a8e2d5cc448024471990a8f33de5f3f821dba8b1dbd6a6997af7d6ccc79afd918ca12cfd0d6f065ca57de7bfc3a7db03adac4a8dc97441fdf742f46e8c7e3c2713d089507075265ae08bd642ddb7c1c508497a7ff18ddf213d937cd3a08b8d32e5500"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2a2b9e3fc90851078e93f8baacb29e6776c221b88378f221cd8c4d6036d51b31","proof":"d2f574f6732daa076b7f03c97bbd80c64c2d4d75799496f0621175d86d38751aeca67aa0432f2e1d3c775045770be95d35bb19ad48ec2bc5944ec8c8103217532aabc29be23a7bb11f3b6a8f5e5b7fd718c1f4f5725ce32b3473456de0148d4c38098e152edc729250549d2860ef7ae5fd09ddf3797ee7b51b3403224d7acd38f6bd9c24d52f58e3a2f88b9611a7e5c76c6a458f4e8542549afff5a7f583a90e6b4ed9e4a0bb05b034ad16230576f1f5cdc8cb3dbf2163a575663960cb58700c22c069d330537ac79cc4750dd51a657f3da5e0b2238568d816c9ec5c3dde2200b6208428042c55a53c9523b887527674281e1e18bbf24c79c416ffaaa570c93a86cc231143bc04d525439d6b6edcbda70ea5f054470575917d0bd03561a0e9336a33c9715bd0d56c3c4e4791876a795a370f58427394155e512357e6eb88ea134268449fc4a40a24ce3e595200f56df7812a074b5b4c31f09a5cf9861b363c00d2cf239ba3f6c41b1d47a2600acb10df97190083ae0dbb2866f196f5a22c5b59827f854df7ac778e27f83cdfd4cd4e70a86af305afac515e0d02e69d5d3797442a8f19e69de5256635cef6c8b5d20860962a83fc8b4c7dbc1aa29c82837b53575a356d4fcd67998e04fb2dcf28fce9c433a8d725097c64b7dd6c7f81258c99009c1c7762ad9ff847fd8ad169674a09ce61363e0093b78acaf105b438092dda66a44af8eaecb03c412cbb97f236e66cb4e846c72a8b096932ca55b61b856ebf547e5f1ccff4f9bd14d6fbf03d67d6f77513f7e79fa1113e9592402d033460da0b3a3f17d6c73ddc8dc188f74a4bf93e627b7644ff5b67ebd120927c894c06e33afc2f60a75de7e8ef34b1145ace8a34a6109e8ab5f84fa1d77afce82c7602560ddddc8881daedde3d8aadfd6310e17a5740b6e6400fb62f2b212ae49b7501cc04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4e8352d82b3c28a7adf09fcd03dace4a62abe6b06fe6e9c531b8a6be5887505b","proof":"1a06040ff224a39077154c93d86236abcf600877532edce1b3066202f461d32ee4f851c7e80435fed71ba532f04f8d297591186b146dbe52d6f1f63c75dad50e68d90ddb4974761fe189a439eea93232cd49fd62b62a39914bb978868c199d66f0316da652af8ac2fa2be14fba9ddbbf05f6c46e2d97845ade817e997e23976edb5600a2f9e45525ba9a12610f82271abbf2097d61a3a4c594ed653e1f6c900c0941d0f0cf5ea6756dbb91eae2e49c79e20a95520614667d7ae833367251ba0843f7482bcfbf791eff95fb5a77ace4c456e92a76f20dc0f8f5a58920f6b8880956185ae574fb7e06b60cac726600d7df0513c415970185308d968a9ec29ec4443aaf6e7a05b5e819bdfe5d4649d48329eb94c3f76fe9327695b6c377ddacda4b5ab05e8d792637320856f881a6eec3ee8369b4095e6e673dc05c2fb7af50ff4c587b8209cedb1d3e8d349a2a16583a18a36b3657ffb3eedaf3222b73817f6826301f15f749ccc42267f8b9308d7f9b2f298d4761c58e61a75a18cf07625e2d194e81bc94ed090ded7f83a6e68a0b6e78b47d263408feb717c4c5de661252a71cd090fdeca25020921aa1e6959758e43cd3227bb756100151664b88b976368f41329bba8b942d2a16167f5a17c08722546962898cdc1d4aa2293be20a3afcb861fe7b6f12b884d4e1652d9b3168bb78294be7002532bf67e2bfb85699bef87313f8c76b9bd7e05f73b98a8d9541fa57ccbb2ff1a2214637317cb261d24cd8357dca84db56907934a96809d6a304839f9ddf156537e67443fccd6477518afb1a2e3ecde8d4f94f9af12a790b70048e15e1fb6d52a366174040c411fcaf346c4f235bde89752cbc80b91514ee88667e796720af8fa092ed10b994626d4709fdf1067c0f65b9165d7a4190c8bd65a3680005c6914649c20c56d2d3e31f61823bd10c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"94ba0a9f05a770b380fa9bf6a7acc40a82c8ff5f3f219d85dc0140ab3e4b1706","proof":"f63f514144e9a98a6a48ec28d0bb6fc059b396c49884b6e3dab8bdf96df94a0046f07b537e6795997d2310d09cb684403522401582a33bc38cc506b29fb290325ac393ea4b440cd427f08d622cfe66aa74acf5deb4f7e7659abd4c7832f9d861baec8ca026f81ee63350e326b7bebd27fb654a09f52279cac2d21e71eae2397caf553c9231eb668ca65cebcd608eef0d81a35fc5acc3fc2827873dd0a885a500a919cc589e923dd123ec011d6dd1ce180fa734573baa685763d651f0af724104ffff6d8298319d9431ff2932c74d86b9ad19a9965b07d5f2d74ee9a7c173fa00e8666d06b4f64147fc564415d65dd3604d398955d9bab7f78d834ae62c883c7aeccd717dbb9d5857ee18e1c85a8569f6daa10d3ab1f64b37441f1571c141bd6c7c5c29e043decd7c619d1c6d0e36d2f31e50c2feae8dec719e0dd8cdd64abd07940fecda11a19f7e4ec948052cb432887c6a1d9fe8925c3ba5375e712834903d6eacc71e7e903ce4fdc786c108238d0b8e99925d55f2160b7da8b5830d407923ec4592d75f1dd40fe73f7ee4eb6b9c904bdf3acfcc00f465a7c8e9867165f87b8a6524e970557b77910b3b849b1b49a13c1ceb64fb7dbf23062872f8b7c5144e38895079991ddfabdfee34e102e528a2af106076f11d6c78694c38cc278d5a533af4b399ba84b0d64ebb7d6dedb005ea0160f39af23850f91f17a7d99042a962ee40146b6d08e51795ad9521ccb839f9cb520ea4c7bd1470d2ac66af536fcd1bfe8425f678673f55e80d5483582d53f07e4a37953ba284c14a51d505050ed725fa65c5865b4dce194c28729739c8d438402fb616efa93c50c7e40bebdb4cf37846e3446a0489d999f53c0d44558dc64d2c59d104824587592eff2e56743002068e44e483de38c69d9b339c92aa42b8c8c17da3dc9f6a4db75432d1345e96340d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9ea262e131dbfa5fdc835ed8b281a602992adbb40dbdb14a538f3554b1931920","proof":"204006c8afaf9f4aeaff12e7678593f5bbf7635adf7192d54b2ac761f100da373c690aced9cf567c83461fc130b2a4a9eae0a22a164d06f02dc656909d21135aa49d9f7bbdb874e8c6431585cd409bf18f2b23eb139930ca24d0eea417276c3208c92941911e7f0c48a5737f5cad584a6bfcde15a1e7b545fb9625421de77971268b019363d406928e68574b03d653c848e6772fa7bc1e949a98c8452be7d00a5b9d0aca78d86234a385bbc6bcca20380207e0dd2ff594dd02bc9fbbfc96d30f149d5218192976345df0a124fd0f71cb2e60296d5bb78d0d46018b34543543038044b75b78399828efcbfe5bc4cdce223b0c52031476755d3c8652f99242055314141b03abda3e2ed6854dea0877fb140057c064f3a417527d233f98a1dc47329492559a36b0a5baaeb74189ed5fc6f2a0253ce5cda54f26bdbf9686fe42cd2e8616c8fe6317d889cd3ba890bcdf00c6ac0a4cdd8a32b275ce762434a7ece857f2e15084aef53a4295d812f48731eb43442bf7a51e3b3ffe2603b342e902292b54deffd31bfbc99cb6bfd8403afd77e4b5b7d20d47f2bd039f449d0948c9157f1ae92a4b0c8eec2dc90a2eeb596f0e7d208a420f5991e71a482ce9eb08b15f283a5618b18a4af5813181a2ef63c3b695f9842220b2a3d1bb270b6ec9b20fc7222cc5119c76330a694e9605dae5f51f6d7a9683099c4aea6733877028590f6a4a7663dbff07863f38bd570008d8a4c409cf83b337df6a88f8c433ae539b53e063501f0c52da3242b2210b2a8d3fda790d7307c0081a891030b2780ea9e47fd24e081c7f2effb7ab54a91f6586c658dbddae59b27524e71775aa00f9fabf9a275bf508206f2bd36cb371073cfc06669f71244c70bcc2be04bc1fad3c7a4ae7fb04f2ec14e949d46d2a99874cf5ccf817dd0b230d4ccb1709bb058e9294547dcb00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b2cb88fa75a7fab29c677f3c90dcd39149d0e6872b1a58d7f248cc466e6e2861","proof":"dcdd021a36c91e0412cb8bcf3a1748515af5c9af5840e4c879cb530ebda7ab03da95e045d72282b9a7e464e75f5f96081cc14fb4cea0bf7cb3adcc5ffbf2430ac6055f74cd00924494f27957e89a0f46c3bcf465b0f7ab63d5fcb6a93728a421006372fd572dfa4b4137c9885497b802cdf2016c377fd41562e94650cd54637368d4ed45f31e2a2317b016a4fcf6bd7cf04d9cbeea358a1849b7460138021a046a8ed60f02fd3c34a2c34f902dfa60a252f54420aa278d052409a4ce09652a05182131963a5653127035d52e62ebc816cac3f23c651b0dd4d27f1777c170d10d8410208dececadd543c6bb358899ba550f732254a70302a5fa9d2decf89ecc7a345fd7b87320f05df8d98a764dd7fd9c68c770d868cccd2b0f93a4ec4180712ea4868ac72a5eff0fb88e32cd32fb7f1bfd352d50f0f58858676fbc954bdc075a2a6b51bd80b211844a313a8114d9b6486c4d9b17273c7186cc735abb90ae4c2196c4319048d84a0467e3b1e0aaeba05d809844c78f6964629b1a8676d3c7cf3b6ad8c83e49d208ee2b900393e308d0f4de5f53d89066a69dff17d849c336fc3a4ef7eddb04935cb6acc7987f423f9a9972d942a7b61ac40af0326142df37b06fb4fe89e4dc59b5838f371a9df84759ea2886cd8a79d21d50d27fbbf3c05ef3159e8c94d15f616d7b75f4b1ea621a09e9bb29c4b46da475cb53ffdbfd6995a0103af0079075cb70f12a7a42fecf2ae33f3c91bd7cf9e87e3e849cd4280c6981685ef41e8a8e743a5df03c344d9774b98cea9681b33fa68c7560bba02868f27a315e73454a57b3afa2077c2d49fe839d8733d8e624eb3516001b0ffd3828cba8417bf9de86872c345da67b904589247a6d7dea1e970401bfc55bd76b66675af60253618f63a6954f43945d80dfa7222601b4a4f0277cfebb64f5338c57c9c51f0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ea4e9cd584b53515650d38ae8fa593f9255dc1cd50bbcf943742064a9efb7379","proof":"be2073f5476a8fc1958be0ab9a219050f22576028fb804e352846b20eb055269829bff5a491de5296f7a2a6a4dc0ff1900cee4270076687a0e31580625692f7a98d02d0c15f615571f8a30190fde5ccfc527a83af3bafbe15616a4a5a1c1374616256811ead97c3cba605050aab6370b96d97ea794d85dbbcb654fb2f027e96617f486033166cfdd0249a377b1fecefb041d9d4cbbbcd187f877f2f02546510ac1069ef62cc9917ac08447ff051ccc8292d73fb24c685e40b39e5f1d88ca370a2284f721eb755b40d3bda073e758bb5c6f3c776ddf43ccefba5af54e8e15d0060e51bf62e49236065df849d47720eb80a71c6648eba9b8468ca757086275b2485011624db8dc30dc0a530e1e87354de14ab6a3d8101aa39240f1552ad05b215a2edd7dde598d2d08f5b981308c9dbab865fe893fd6d52c966b7d7ce1f54a583d44d08db89e3146892c180f7823330719956554ee9581d91113121ba453b4d23f76874e7fc081cdb1751695167f0bb2a1f1c5f2a944c0dd4f7244cede5271e30a2e14f6e50e3643e1d2727d67d162a2bb68fc81a0cf15a0c618a6aac9590aeb582ceaed326b784bedba77d21dd52a2f7cf106b11cdb3bea655b085ea65fa0df48e24d04b8523d01a2c7e53d77555dfe96b995ee7e155f1941842f10991cbd7d4ef27bbfa62b5474622a0bcf54410849a123816c9098445f0eacb9b98ff605910200df1ae37bf984e13d0eb913f10c3ddac6b0489472019557b81b17c7ac59a058003c56609d5d937731326bc3deaa0d453c6c359d88e839b9161ef7a4ba92406f624858c0898cb9ac487a26511c406c49b1f333730791d3bf32a488bec6996346918d598ec36891aac2b1975071ec0f3b406592e4eced5dc642bd9b4015d8e0083825dc854cd1ffae42e3135e39cb5474d536793e9a0d17d6941473d43393bc08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aac3c741b7051d1681590c3ddceb2e2400e79d62c0f2f6143bfc29d71b37632f","proof":"be1972a512e07bc3142e2119adddeec3ff1cc4716b5b2cdfe998ac4c2bc11d1dbcc8e42bfaa6161cfe27d2a8298e8483281fad066b08712284079e9f458b0f115cdef95a303112c2e182bc4fd08bb5dcd328d747003fe7f1aa19c12246485e2048f3d4f82775cf57989a907f767fadde91a28a73a921958aa32b87f83d4c022173e9d6921156b9b1a8bb32359be69cbd95198df1a398b8e1544244e18431100bc23d21beffec316c17c059b27a5aeb1fe3427c8b7e16024b99b39941129b170fc081062cff16ef9ee931690b6c76d5f37ef0ca5027f980a6f0cef012f2a61202d27b3a2b8f1193fc16be89b7edf8e0f83c11e31d43f501fd1b04d323a699070de4c32f6a8386d05275bbce8ac77d3f94d8f7fd27e5b7f4593388ddfb405ee65a02193aebc8817d5a7cb05c24c1cf4865a5b1aed406513a9c86b772b7a256df7a48b9ae9287bb6f3272235feada3cf0c07f2f0963902914dac071793454ad2839164ba374167df5bd9357f64cca68dc8a084c050b1fd5f9b33b1b63e14e623708569ad5df778789aeecafc433e3a4f14ba21a0cdc65003064c402f051c7cc4a4bfed64679d260a8689e4e2d0b826e1aa59d4a2b4f53e0c2337bbd15beb6502739dabab6dba09a93fcb2ea0b171717f290eb825419aef1cec0633cf668a63973072e2c9c7a8157b39b556fec8d6475e45e937271dc0312698676c6f04226304a140e584d8a0789c4df2f459c052e4ea3075bbe6bad54f699a9d6c3aab17833604ef83e998150f6766ff3292e512c133a6dcf9717770079097713e275b941598a704232cef35b8e3fd41c6ae15c98eded7957d81f8bd63168fecaa9a489e61aa4779d936a57fcab117213fec5d87589e0b89235ea46ee85208fcb7057f040bad105e135f89c4ca5e335ab3c99c3fa32a0fd5fb610401d8a3f77c5c728b91899e20c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ee3372a0a8d16d845baf740569bec334087e51ec418d07d2d12678d45ff39f4b","proof":"b6e351b040b9760ec3ac86cd2e86d038b2016058218c291c66c02ee5fb67fe432a1bf0d7e910039c6b212708955599b76c9928e870a2a8eece9dfe2924e3a828be2cf03623215e94896454e96f857c7abdae11b5b722ef2c17bcc8ffe9830e3b507cefe40992312d4243b2623a13ba2626db5138c299eef0412904d445a2e55f42d66009797b00187b5f54951fb9fe4b38a5c79f7d60c8beb46ba94fd5fe3a03f0b97b305aba10fa47c4da78606a3db9a490d6dd9acfd6fcd226c3690489cc00fc2a7b29495b5e321e1af766bd51b361358c86f47e702eb0b602c9aa72749606206a3daf3842bb8a4dd08532aa6f5d5d9e965d9d63767506bc25b47fdefffe50946cde12ba92b42b84ff19d7f21ca246573082a758c0e925c1ceb7df262ea75adc3b71e2dda9d458e181328369ef03037f4f15a523da6bcc427c2e3e72b27d37184db1798d2b11d7a381594f2eba58515b7e95e1a5757de97d0230533cf3385ce225929f0e875631c0ff0744d74581d564c56152b221395795136ebe6f84675fb4f4678669a7bfdfb6b140daeeaad9960efade576b83c1133729dbdfcf20230c26d4259147172f291ead78151c871c88f04283854d29861c8e403424e421893596d95229c5899a952d98b0502050b006132a7c3b5828d325743ab14332226f266a0462feaf376ca390fe50e24afe2b1f3eb879aae7787f267ce84ca25109b628dcdc4f620cc995a19a9d31ea0a4e7db9856f4cfbaa2084130f5b843f15d79c4b9a223f6fd0e6e367daadd1b5b25dcf58bc51c92abf871932f2a421918180e44a8aec88aaf3d5023b5641004ced24a6555d334c87d2ff18e030703472ba71dd7177e768885e510fca1c6a9765c94b7839b9528b21e33eb37cacf6d66d98eec604e21c2a59b2a41696ef73b56c681a3fdc7fd1f17e8e7dfd6f495921c1ad19b50e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aed79183d71b479c46376e9b6a76b87d19c56fe92aced248bacdab7035640406","proof":"aa9835efb64b43f434de3fa7748e9581f2ae07029e99c7cb4e51d5ac1605f76a6c8965a1777241f90dbb7f13194abec14f9e52b2ca92514ffac2c6cf6ad37a62601b490e5e9f66c9294c5eefa26c4666cda32afed70af802505cb58e2e9775386ac3bd9cfce7f7e1499e6585660d927664f978565639397d035a1d410bb6b40b391bc18f5e4ed06d64da03f05676e8b457d6410e90e218715543026f72f4d4080945c0f2bb6aa7a3141009314086c36138ce2761d788a1c8b5e90f139cccfe0287bbbc76cfb48773406c5344ae2454de76edfc2429ef82649fa853c26ae4ae08889d6daaea229bd09ca5a2ae01af84311da7252b237e92019ec7063056ad66221a92cf6c360ef9c4266ae0ee6c7fc44d20097cffd0128e8893f347abcfdd9562648d5269591f0ca3b6c6bb728be45e7cbc129f404d3b6e60e6307d2fd5d6e267a28b9fbbc3b0a26d8e5edd5cea9cdcc882c9cfa90359f8ae7b222db9c0a1f401627af5c08c79bfc519c658dcc76fb1d41c5eaaec97992eb491e61b4317b64225ae9dbff8df9d5f3cdd19877b63f02035d77a071dcc8b84a201c2a68a82934d4b8859f9782191a0e62b59508b811535b8a1869de77c3bd0b3fe97314eaf23e61e8af69477f9d3e72a1092ee090200f6613365c36ce2d0cef6da9db49df57ec37e084c1a5bb1fed2fa85ce6579273dadba031213b8275005079a54395d3e82b87174ab5d8d022342a6bc7d396aa3f7de85989793c4eb2f8202f61d463be7bf0c6dde49c86a81e44283c1d15b0995ec3fc5d5d7f69921be56b6ebc2e928f235bd5dbef294454047b7dce1b1b787a5689349cf65865e327906aaec462f96369c4a12e00aacce3d5bb630746cbb434fa56fc82a75fd985ff488a66bb6bd5380e0fd03cf2aa270a151f2c9a5b77150e4d95172b2d9387e6474cfeb75601766ca121d01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ce8927b6f17af91521cd5c163789bb9349591d51408150d6873aeaf54da6e233","proof":"aa5fa5fdedc787eae2847277c0224ef9ec4b8ab6491c9498b79a7ba9adbaee65700fb65ff768e578cdc273743f9e10e9bea1f3b477469a011de5243cb9541e18a08f09249b2ac6c3a73aa8eaf2cab04d795611de033b3e10e4d255cea9f90163c63356e21439fc2a0268de2345f1672b0329c689cf79201ab8020ee6b9b67559732a8dac09db7315b26466afa860ae4b73b16c8d570de8599f9355969a0dd702fce2656c91722932749a0ffa4539b0e89c747a777a48f55626c54c05fb7b7c09fd4a408cf1683baac2e9d1f0abaaeb7c6091d7de46204cebf40c3b10d2533d0d22b4d0505468f1d1f59f1d35336b5538b864df2dadaa08c480b0daa8e92a337528b9d32c462f9067113442d66e8b62d4334b2a3d055d719eb0318ae534865a79886b1eb6aeec8807085e78a78e38ad5deaf0fea33869eb3d8e158f4f49cb3758cedfefb8d68d1c02689d326d1b33212651d49d115ddea3d42b298f75a664b22ed0df0e64bc29f084eeb27bb3affa11d7c69b1b9ea2cb5f8c13e177bb1df10018e261cbda991d403d1a690fe33ecdb4c22f4f6e3ef9d7c5b663f2752c7d6d8b3108e4ca6508d565a5e20a1dc9ef9421c40d130424722195436bbc3af91a5f0f744667569daa81242aff3aab3ec549a93623a26c0f150e5a7bdc800cd944c66663c6b367b58e397e26aa208888efb163b047ca2ee82c20d76b152ada9a1b44a36e0cffd85794502430211d73aa46846b19704764f1e1d31afd9ee3ce35bc2bfb579ce645661e1db3ce77504437b9f5e475586f124ac6bcb4442ca548a86ab452429cbdebba455c6624f763e05f4557fea14e13ae85713018d0cae5872fd8faf86a592ef0494e88a7bfc044e59894693e1e97644bc460d5f9b411dc87dc314b6b0aadc280cacdb70d2f5c824946763055e80edf4b1c76ca790dddc997be86104c04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c4d8c72db85f767c19e391de6dad15ebb6d004072f659ac903ca5b60e6476340","proof":"44039310346974c36f2c7ac3fb7db64dcc2d73eae6de8f2efce870253cce6d284653c6289ab01f9169d3a74392a601f041228fe39711b623b01ec6302b16bf26de2fc2796d77d00bc377b1b154897310675bc6736daf95237b3abae53130ce2e0817b9432174d5492639a24a7a4e260a98461d89b7e24214a681cfe3ac95b100fa1563aac8330d65bae2f4ff7c5f21b8c399d26d07d7b963ef4a8a825879500c41dcf55df720f313353c364ffafb0b33c0e2844b63cd6fce0b032e40adffa20aec3ca490f86e52897e617a5492f395eef956affcc04816958d50af6dc9405c05dca98377e4a39aa940aacb0357e250e097a9d8858f040824d0c460e0e5776520542a9fafe83ddc7979f6ae63c090f1cccf3d0922544ebbecb6b722d9ea580f5dce0efa4cefd9a347df324b52ed73c4a850b658c2be17a10e5718e2d73f609b2c8e421a4f4e77add5d5edd1ea52f0f9f4a10bbd75be4abe7e7ab1b50f40ba30704ad6ebdf4c600533607854e429ef999b9fe76b6cf3e85cc0723cd34eee24d531c46cee67902e409a359684176a28f8125e598b889ea7a82a4675c53d5f2ac72cc6a69d18be687cd30b34ce3639ac128ec09e530c945a27f8f9c40d559d415f6d8ab085027601324cf4d9b87d03f24d05b30317d522fe840835f93dd881a24b2ae6baa30b00208ac8dc7b3ee859858bd6288db00b007045dad31ab5ac135f0f25d47ff1dbb3984313c370ca9f19c72d26e3a49ee11b60f19daca55f4ae3176570b022a6ec744b4d83b02b45b19371a83735afe9afdac37fffa6ffd754b6c2e83e1439cbf3a615187f05032f27833588af9404ddadc62b1efdabd98cf8aa022374646ab2b22b77536d040a96005b9ac5a5db3d53e5aeeb848c37d4e7df1a0f570f3422df6d509caaa4501b9ca2aa3cb4f0480797fcbed2235dd8c96c393aafb50b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bc3b0faaf4d862527828168d547cb252c108a294eda3d79de5a97d0f13230210","proof":"1aa8257e8dbb7073f4ae46657732bcc3d9077616e7ace8b9b120ccf17d726243ecd8f7b3d8660b9aa11e2d6c81d9c39eb6c7a08b6535649de9a0052b10f70b7cf2eb1d6e2875ef0dc14aec38cea758ce7bd1aebbcb89ab07f6bde912fceb4a4860fd614c6812086ec0103f92ffa7c95424377a816d5d0501cdb39857d0d8f76df860fb7f4ce847a4021c93f98a257859cbd24a9584ad24caa733bd0ea55bad03c55a681a07a5831abcd94c7b021cdc7c8839e13f71ee97cb948c3e371a0fd20ced15d41bb565684582844944b686e2666baf97f9da8e7e63068043eb6c1dd605e057814acfd1ff317971e0e8c161c6e9c06b8fc6795e3a101da3ca0c0e0e9b7b12cd6a08a6d3218dee2d34b1552bfbb9d12951a178e3f2b72e66eee8aba3837508043d861b51a60b7e80a48f7a4e5ca615fa25648327ae0e30ac4b7fb0d38f3038c7185af8c62821f904b0501954379add158e320043a432c36c85cba234a41170af27158880ecb018978ff65946fab24c8478e5fa705e7c1ced2063facede4548598dd53fc42181c5e4563dd96fbad8a5e33411d79434647f3c287ea844a1590c0b1992743c44ad1ed20c875d33fd6ba5e08bbf89083c144cacf1014afe68669ae967ad74cef03c4dafcf47f81b6a0bc5330f4e3f5c9c4840212d2bb2b42252608d101ec784310f1ba3450a5c43238eb5bcee970d734e5340bc0718ce222416d2a22d0f9525c89b51d821a6d5fdbfd55da69aea31dff460f1a3f75f3736f622b49e6895b91151da7f5825772e3517e5f14c40c9c970b5f9dd1197f6e2eea67090301ff009c007bb5a3825b2faad9b401ea4f04da72caaf574cc974d1080b46ab09cced05a9d20151d7c2d064585fee1d7b706fa8b17dd9767256177f8cfba07bd73d57ab0dc4d93ab5b5173cd31e66e199ee40d08c1eabe0d9fed52024d0803"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"58e042bfd37f0ac1d3a785ef685e34c9e7f6357f3afe693b9d0f358dc43fbc17","proof":"7ef60740d4e4e3c5c4bbc7d2b5b049f29539f9f0e05de9ae8bbe6db41411de0dae5132ad692f3d2063382df4b9dc839c9a966e4fb1d38216007af6569c5731747677436d900ba6cfb9f0dbc12506549b98e61cce0876dba6abae49700daeb10d3aa0f2f301c51d971cb336a2da8bfeeba22911f8f469b740675c1d8a13e4df19855eedaf8ef6af8a999e04758e8e11a52618f81a3edcef38f384164f6eaa5c02ad29140cab0abc29adca639d4f96f872238e1614b94aa1c23064430553e9f90fb347c52a420e3ffbb08cf85200b7265dfea20e11d19a64473af1cb6d66f42e0782dd9adbbd40623b43296af623e6a497ace5a6b8ea0405db43d75d0d684011484e151f1da9d7ee909ec248c7f69958da733b21f0369d91c6b920b07b17011d37face5b8db5110962f8869510985a9c3e029b155a73db15c387b6356f8478e74f6eac55429c4a3391c0018cfe31c9740db9910a82715d6d834953af742b8ebb67ac5dcc10e48a033b51e040f06db723fdde41a075daeb1b5608968c53bf3ee24ba8a32e104ae71544a30683c10fdde826832e565a09da04571d43a691b3cbec1fb26c61fb1ce2296a08a5711c18c0ca487a69ce8fc5278bb35d8a368e4bb2da1d6c45d10d791219a2d72855b305a93b8d5c08744d4580aede96b029ccc026a51736247d74dfa7f416ddf9699ef5cb914c1173712fb224626ca75399e016de1501489d9250389a1377602e320b10e0603b56d748e05d8a839f3727453561ba9a619277863ab7b2799e24e4961fe1410e5ac1d9fc3e093ce1ea21c7e53d8fa628165439b5ed58d8f48ca3f54ec765b49a8fef1ca859567dcfb6e74be42f0761ce768d6f6f12561bf8a281bb024516ea7cf3be5002dd3205aaa30534f3579aabaf0f95ac63579b96ba42a4a610cf27547ea3dcaa269dbc868b8db3fe401dd8091b0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8e5ed89d2317bf415af482d2411ea542980e87ae026de4a818dafb34cb980e72","proof":"5e56fb74c8765ea955dd5198f2b55d53c213c4ba05156e7adce9ebace1d3ff2234f4017ac680268bbdcc238267321eabbc51d0613d57433e753b7b1035e9be28ca8db3f52ff036f9b460ec39a2d8bccd480c08ae353297cb6a02fa28007cd042525632f74cd40540c05b1c430232ceadec8ad4c73e34fb61fe8477e94a6be81f6f39b615e95330c15caabda7eeba2346ac8ffdb1011caa52bd82fcbe2de75305b1c3d8481737cad6ae9554026cae4a4e8f34bd8efaf6438a251882ac6aa5cc0b8e75ecac9fbc164c8068b485402fd4a5e6c5f5c75c549c9b330dc6567cf01607c4e44f50e52cbed7b260e8205a40eef6091bb9cb6b56c2d8a82928c307e4d627021aad761282425851c444811afb4dc7e2e9ab8c7279b97552c5530a8827c366e6bd7ae33b04c0c58d9291f7dad0d9deca36f16e473955ee8e9a8693ffd1476020f9da52989060f2c8d8e83bb4735716f0efddc00c304304722414b03555fb4818f1015922ed5cf3e0f6bf40f7807d89c5ea43a02f3765c6bf1b1a8a591b7864f0f7a8a1238fa38faab27a2b557e16d213eeb98f723ff46c7d3ce9f45670de66464ff64db53955fc575efd047ec7d7872f32e529112f99d7d3eac75c5acd5618785b333cca6ecd9b7514540ab595ceda0f13a515bf7be1ca72122a8d5d36880ac0b410cb2ff28814dbc2eeeb390cb005c36070ad096d4eca17f07aa11a566d222e837a74be4602001fb07bcbab91226ebf7d9db25dc4b5d7c8a0ca59f9382634f65c497c15c5a87fb93edfc0167d9111c8f0fc00ec3a769b30c5b3e5b57b6643a01d2d27b3755fa15d2d6dba9621460dd0effc99a5c744ca31317c123110351be24022c09b9acb445f47a3f98addd5997a4b4c7b4592716c9ae2628bdf35e904a6b73c40ead26e6762915e86bdd4498cd9a5e6df223f66a5a4265abd1d8f660e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6e41d42aedf7ab00079d1743fe40d140e5d781672ef5d9dee60eb98828ae6b3c","proof":"66536522f4ebfb4c245fa6fb671b3b5f429c245820b40f366d41e90ac558027c40ae548e2068ee86e2d27283da2da6b888cf8b9ddf17fd38830537b4a35dd33f04a150db8c57ec22908b2a018154858234007792edebe6803ffe97b62182d51db84bfa43f07e1f21a594305dab4eb15ceb43a783c6aeda4dff3d583e7a42811b0ec1b007b49dcf4058cce299d3a3ff8d8bd06a07ed9e7088f805b44ad9a3fe093c83f4ee037e0edbc1ebc2e6398e6bbda28b6b483ab0c806715dbe22fdb4210673398b7d12432192c46d83fa38b1a57ddbf04204a81b0a98a5457dc22db1a609ec108d64ffabc00489de6eecdf6848c477f4b0dbb12b1487d83980ff78ee210dcee8887f7e8d471589ffd0dfe6a50f1608d0b67debf430a9c5711e80b3d2ef624e7fe54515fed61eb3333b5d631b5ffcc151d271693d013b682d8ed0f3387258727065df52829623fd3c5c8f9c53bfbfb261679f83d8897f2c8185e4bf16ac7e606db551e287e6cdad433a1d218223b36ea3cafe92a2f23f47dd49113e19183272cc6009b3ed95d1b532f2414eff15c02dcbe3c0356fd14cd98bc587e8bb353c4821f8d4c2a39e6747b1341cff6f72892457bdf1645598c074b81ed3ff0893675c4669b25e7fe9c8e97d27e58cdee9eb8e99daf7935e91cce9f407c6aeb667179063bc017b6d58d650c6cfe34cb886fae99f94e30014e7cf29020b3a6e46e6442c980bfc38cd8cfea9380ea887471361332b9fe9b6be4ed350dcf0c0ed77de05d44d1d6213f5da9db42983836922ae82849a096f0631fc44e5a94d384f631e5dfa5c51165dbd46d3e11bbb9db39b5b8b816d30bb2a01fffda5eb6bda1efc2f68eb9b9ee2a93982200275d55242624f9bb0e37c509dc27b9149fdbfc7d127ec0065cf53e965863bd526b8c245654a50bf25be116abda4ce614843d2fa192d7b09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"161acc0109815a67022a79290b8783e7461ba9f0e3e0087e7b15967cc874f76d","proof":"2065d21dc671a6b9e7b4d6d9382a3f2565217d4e4a63748470b788c1a4e6d65068e5076f067d35206f3de49628ae7917c73124a884e90c1f8eb3f083f352fa4578aa7e0331ab5567762e3d3030ed5b3f99bee6b19064b2fe3882249fe36a6e1400a0a86c69a0f97b069c3bd54461b2e12b3513c0f8cf203e9d5b814abdb347363a8a74e69c9068bb52be49d47e703f0a978c40c16ddaa0e9454c1949d6131707020471682e375fc8b45ee0873d64da6920e175a4b3135fd4f09930e6cd0963078adaf3b748dabf7d40f5b08418a5443f673f51f655a80920ba90119433212509cc855b0ea1af654968b7e8738c9b37da685c4e599a6867f14496e740aafeab4ada868cddae56b0e8840679d57f305a5f2424b2129a3db36e0e6d8911c67d27601c9729820ce61c973daf05c71455ef526151d9fd7a53fbf7083efbafec154c514c60d06c985e63619ee104479537de9c2689d2c14c3e00ab4e246c2e9dde8e3d202cbd81b0474fe00768aafc442da51a49a21e0c1a9ae688d442b084fffe2637243c7a9c997a0d0a3acd4533cee12043e3a7b9b5cd5f3e7c903fd5b2bd527e13a2804769ba9d57b8d591d0d0c59341cca348ce6d4dc4769294ec90e23510096e9e544c8582b662a889b1bd4b877658578cf788441d764238d8f96fe7a054aa3ee48074aa3bfdb16eeb7899b485e66796a9d0eeb11abbcb6ba0f09237c3059e6d06732e278a61b1ed679bb152fa7acd537127742dd6f1289da3c9c9624db3c9073c04e831418563620cedd47d3c1c583286164e34e6b7fb6478532d79986edc3c40c62801dc9b16068034b58960d758d7de074d456a596bb2de564b03875abd0e7d5dd471cff3641f4f4786823747ea64e8e4fd05f495535cef381487d09ce10e766ed28bea23c4c601ac362c179015567c50c4834155addc213635132ad69509"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6ec15305bfdece5b834cfcdfae1192501ab6658288d7e9f2fab06eba10d3621c","proof":"0e9e8ce33194bd93bf5d9ac4bec30dad2b4a07176e10e0fb32dcba613ff64d6bfa6cf690fe12fa7773399bd374893b8b387da5c3ce241c020efced18d90bad7a169627fd19279cf6e398d7d04f84ba77d0cac85d2db01c650477cf0dcf069c0cac40cb946dc32e938e4c8a747102fbbf2ac897a34734c0e9ce4a898d57916464c02fd519948e615c878c6f8d536b757b352a84fbcde879773eb7fb14c73b35046052f25a3be5883bd78b07c5f8f979593b304cd35095b4237ff98734c2635c0f5afd5138ecd26e61aacebb8b5c2c81d4949ab7468a8544be9c9eea188810610276a02d2340a4c236b882dd3cac1a0302207c4708d47512e7e56a9114ab030e62700bf6ce7a26decf15dca6a6f08ab863795ef141ddd624726c2e12e65f08fe4e5c17983b1ccadeb3eaea42c0ac4933d1b2014ef26aec55ae83e7cba743840f18161aa1072a0a9be07258277a2f0ded532301dc1a75ac790e39ba8ca0d5945a737cfc5bee8006693e1d03bd309922dbdb59fd98c09dc377e568709fa68986e62f3641e75dc6828e7bfb15c52ae19342781a318b20d9fe67369547c04bb5395e4dacc7638e0cf3892349089b0f52e165664b913b95ae1ae55d880a09599209b6489e654cd09e4d0787fb24ab42f6ebc76d778560391ddefa0a8c98d90488ae282cfef902d37fd40550a4c5208191a8e382533d8aafcfa97e4d964048faafea99027e0dcdea2f76b7ee760a01578562a7d39ceecddcd9f6f483772c8c4ac483825ad81b82dfde581479f48240578024cbda3fb71934fa2b3108150cd03573753e2a1a787d43fcfff9c6cb972ff915f410c7ff54b77acb4c0c8be634e2173cbe904f77b6db7951d564abfb778ee5f393e13b2de274cef2006b77dbb0fccf5673f7046736bd89194fd1527e8792b7079a5b3ed9094c408bef4e6d814d800700041401"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4a934b611df039f3b8992a0839260c836a2228cd3a5fd1f34f8d5313fc1ace0a","proof":"b8d9ea7e4cc587744044c822a2b8757e805b2ed64465356a0310f04cdc6d9a7f4619cda802f123261bf653996b57cd937809c869e174072305ce24ee4d50677700066103c48093de88f60f8c56075e5cbcb5043ba92a470faa70d2a71ae7a74c78fb824402e0fd6eb143a908ec79f9f0d0d4ec1fc3fa649773ed3a6eca02a76742ed7572d6ccc6e372175333186e942ee6ab7ca4671ca58ac048f22d45265305ca9d26fb5a23db864ab34afe4d40beb9d590aae5afd66981e93aeff701129b0899593ce4287464361bdb09a8a1261e1f4ca1efb3d0053935629f27f5ae15430b544ac0381ffe8c37118fc8f8645aabb5b224d38399c02328b9cd040a3403925efe0dd6155d5352a51cecbce9034026628a83867150cab7e774a3b1e1594cb548aa82c0b6f879dada99a1982c6fd048d3af7018c4be88acf3325dba15f2375f466cd9012f12dcdbf3c07e34bcd9ff3fbc005982d5775eb5de0f2145c6129024662eca4c27973c66778f6846f83c74da98655ef55d67deb8f75427eb0a22e21e2b12d1541aacea475d4e29b722b68db8bf7e8c63ee39a378d36172cbc05e522e66da510b1994a5925a9be0afb3ab2587575f3c538aba29731d538a1b842ef5371b70374fe78fdc35768e47b245fcfd40a9b2da5ca08bad445791a9051ee5395d018476d2b06cc5726567b2627c08f8f8bd22f7c020b72674b190af65db17335717788acb4758fb408ecdbf442661e9e9e9a11a7ac906a5b785568926213e324c01deade8eeffdc130c1aaae2891ef901f67b73c1582a85746649535f13b044d450225618f77822a0c11f8d68c226ffa0a82446c24dbf10ded6101376bf3371b724ceb086c98eccf94bc5990dce2da07f0d5aa3872f24a745df1a04b697989d4905340bb81cbb0eb3088219f946ce92ee864dea7bcb178a706cf8f82f458a62e109"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5a9f7b901c57ea9968575320dcf4b4e427bf6501d9dea47997d0ae88f097c91a","proof":"f6e43161b2d90d3a522e6bdcda4f3ac900d6422119d0b492bd3da14e321ad556403630aa3d097d6fa22c9aa124931cbaf98efc158496b00aac52aa980c06d61460e6854f222015fb2b45b0b52c89462be66e11850a6847d7c70fbd826271d237da1c87525ab9a0e5c81751a2463f5b189340e7c014a590739d7f0097a8908508f34cffa2dc5bccff232f7d99e9efe80395d0479f0a82d6d0c3e6e558449a0c00bf9b6bea08a075df60c945e796ac852c313e7e7f83a77e3b612e9c4bb54fdf0b2df88c5922897e7998eda9c263666ff87bbad7a504aec3507ab831d1740db002f48098ceb932fadcd9442da6810ef15dd1b17def208befdd9909a7689dbd687e28125c09d07324f22b91cb2d6bbcfe3c90f12e927bc01e2c4da2a86d7405180f68d8699ae8f50fa929bdf100e776c324406e7238e9e1c8d1ba080ced5c831b399ab83c11dffd31e5fa2289fec55c5bfd353fbb835dc3834eaa5e001c3c1f7e34e6d42dac773ede4d31b24157452b496b4dad73410cca8fcb37cd27bcde8bc30ba229d70486b170236da8d1fb59028cadb639ea34d8c3e9efce637a1813b4af7b864c6fe85bfaee36358a4f7c4d7e89955ecd03bce24b6f5fc07c508deaa50509ba1f361c451f68e26b8bc7461e6901e1804812611a6e6c0ce6ed3a35fe0f3916a647a13394945d36d0592c499a545648c96076b29c349afcfb02222cea651f404072849c4d4d99e1161b7a0db708bb47e6575142a28c9e398ff8c3dc1d875d4578ea4854e5acc324544555ba59ad30455a947e307612dca896b1620e89d359624447365c33e397ac85332676a20c50d0a21436f92addb4ec0389005c3807d0571ef442af8999c859b745d189ffeb0bd7eefe78d05e2841dcc1dd19b6c802fa010f8c76035eb3965f6114fa547706738f4643b1dd33729acf5aa9db258cc1aa04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5cb24e119c8f253229d89a66079e4029c6bd73a5510727ef82a9a58ed7c7534e","proof":"1c8be4262438320e88825ed5693032427e0ab0be02c152e51134f8f172fa0e4ef66181522aac8263b7391519292bfdafcffde9e8cc470be24e8818d38d039743546dd4dd210901d94377a7081f4fe793c8cc49bcddca48af5183831eaf94c71f4880bfd520eab99c05b5aab813d757259469318e19b4c0344966e54b418fe86db68f41dc24130c50be8e570224c08a91222500842e5e6eca897a8ac907a7060c1fd3fc9f1e58613578ce39721640965269cdc1f60647e6649ca313d380910305774dc5bdc259f292a31346c5cc8df0e3dc8d1e99cfc7ffcb7ee01b0c3dceb30ebe3ba22d1eff7d298d29f5b132467d7ce2aae8038318a55feb38cea9604a4e2f1eba61f6d1dfe2eb4e775f470d606378ae2fa4e65dd6692a01f47070c836244eca1e48da0dd7bf879524ee40540a0dd6fe4758dee57dee7b09b293ea2bc3d6384c6383df962e5938a984b88bfaf4bcbecd62fc8a5b8ddf60a2d56508ae67376eec434b39aa2209ace73a356578c0c18277ea77748f706b71f00b62d612ece20e1eead45b5a97a65f1f4c0e91f5d30c09ecfe5079dffb80108506aa3b1cb8b002765ec7944014c61107f98a62ce3b289d379b6ee469fe9c67788024bd592bd56e38013d4c6323a87d9d7aa0032640b4867685d8d89d0527e218362d35bf479b549a39b9f476f2bfa119133f9aafe1f135cc654865fb3c765c38d35527a83a5d5aae3d529b00dfc49430ae801ca55b91e973990a00b60a79c37da5a5ec6ce00444aeba09f1d8676c75d3d68ee3a2718013b665914e54176a817dcfcca91a247a202c7556dd4f3f2ce0e9ab2338a06db839725830b7e7c0c476aed524a1414ff42c283abccbf46afb4099d6b3e0bc13515c283eba849b4224736b39240a057b6b0ded77215e6ae14b92b8316a49cdce2756e95ee97ce48b7b83f01451edbafc8f03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e2baec66f594d129ddc3840d7c04f8f44d45b73ede0b11897a3afd25096a8754","proof":"8257e797e44f6c139ab6664b7b4ac6a171cc17f6c8be88b99e9847612abc8806828d55578e9767dedc6e8c8e6ef836d052f5e4cad3f5d9df57e6f7177e93386f3c0a29362939b49c41c42a350ee1c40612f8a8212d48e829404cfc32e4203e6d6ed5b5a7298426fdee99e0b6186ba452a9394f94d8ee9f355d7ac9a48ec0407a491e582e3b371a90b6d7c498fdae65f0d24bcd6f9fb3d1e022c2182a3a315503441c9720e9ce76b5a1733ec406b288fd0403663ce7cf2249f1331e1ded9f160507ca149913e29214e088968bac3accfbbc6ed45b3c653dda90814a66e448ca013eacbc37bff10519f7c8a408bef40633b55dd84ca4c7a94e176b4db765cdd813f0e56c19166e1c4a856bf21173a16fb05621918d148c0e1a153ea95b4d1bba02cc46dffafc562491507113da37c46a0aef87d34e48351033f6189120fd34d535b240174daa8efb6ecd4bc60979b11f29a4bbd18c2a1054667708a5c7eece074216f5917fb2d94a0afc8cdcb403e22ef50d9094bfa15b529c10191b3832a3380c706a74173234d4f5ff92bdac70d6f8d9ea771ee925a4768779034c20668d983232beaea8b3bbc9928b09005d9ae2649131c666add744d6393a14cab70253d56fae29f09fc1bdc3e34e8071cc0e552dfe0bd1269e3aa000934cafb0acaef19f2854b7f07b0df73a1e2aa219c0aba77e8e2335334955899536b1d28ddc01354f387e82d3f4c756c98e39b416d7c1bc784578f2ef835c58ca4106228c0df3c0f91b2e5c21c7f7ab97c3bf6bd8345e486b89b765ec27c5c0e461242fb728ce714811d028ccde0ddecabd4738254be47d99e1c71817669d8b8109a2f13e4842ce0f594d1888447091a6898c72378528e4afc1b9aa04dc56a38534dd0d47ccd36f3005ca0b084d2c1fd196634db861c67305fa797d12909e36bae824c944be8b6d3b0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"022b1587ddb5ccdfcbf205118d2c1b01e3b5e2a51d74e29acdb0addc286f396e","proof":"1e80e91961886fe47c174252ae98c74321ab73d39c5dd328f55a7afc80d53767f440b16a5334f9bcd151955cd8806896b0ce0df8a686990f9da765e03c42dd0f763dd83a506466884e0d677cde7c0ebc347a14ec1c06210a53a5c234e81f867584a4a317409a853bb8b703a0c72e247dc5a97e59e07c788f9aee2065b5ab286ffe355579cb24bdfaf1425bd8790fc7701bd6c0be310571567f473000466b9f0344d5c8bea64a72c3e6383e33cc10680ae24f2d90123db15244fb789936df14078763612d4c8ac6cfce2bcbd8fac0dcf64aa5e36012694a6c007ab1bed6f10609960e4dd94187f5683faf9936cff63e11563e67da3c16cb452a85a631d2afd97f78582df0d1af23bb27d4872bdd0f07ccac837094fe92a82eb934d6cbf591523cfa58fccdfb8028dd4ee039860195f6aaef9b47a33293f2074d4d916003a30a4968336fb928b543da67f7f5d66445af96dd672346936995a5264a33dc978ed658ecd19985c27d94165b967700d0ee6c723661a2178db70a66e49820306fec0d493af0c23154f54328ec1263c1cb80ae99ad3906038d6b87d07d9aad0c2cef11436c0d81c4e55c73f224c4a7d53872b49188feabc8d0892456713a5a233751b564743d61b70dc734b9eb760d0120ef921261147fd3085c2eae899c435781c74d43fcaace0ee117af3b578aa3c71869ed9c34fda52fc35946a61edce47036a6aa5a3cd3a155b78a8f68938748492e54d501da893071b13b119c41d8f6117a9e644b4e58ed5c117406eeec2dcb5643d096dcb027da6900d725f5eaf394058ff51074c4641a9ab0c38f98a9ae9713cb688fc2e9e93f651d80da6ad03308c7be056d15ea5a0bf938d26301aec905e9ab1a23a035d91495f4c9bb95d08030759542ea0c0ee39c8f30bf08626a494b95b14701cb867d142ee4dd8292504fd9f93b71bd01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"544087d82725692016b64bfa6d0ebc98ba84f72987c1608332f50eec1d7eba48","proof":"bca7fe1b3c4a1ee0ce9306116c8c225b2072751ac210307aaf3e2ab9009f145cae5f1cc727a5272ab2bb97ddd9ec50b6aa83fafbc382bc6bc09e18cc5781874c4842767795b3f1eeed341234f8fec0829c0a7fb19cf61def024500145dd5675502dec56853fe84bd094aa0694ebb8102f0c549312947750aa1c20e80d89663222aa1cb03f8e0812e3c95f4c233fcd42055284511cd29766c5c33b072b392d308daac0071369ef388f31ef025c5819134135cbe2424614e910342de217d2a930a5e74284e28994dc50678c6c11b7ee1fc962cec644e772b75204a67e5b8d0120fc292a3f6ff549e947877c4798f3708abda6d8e2f2733342615fe5b2457479b2cba2f0e173c660ba0b7a17e45f39f06af244c8c992f7d0396419fefa7dfc77d4cf88a6363bf3f181699776ffa1e02391d3c8c41b3b4a40df1683bf90f8e48f04402ecbaf3832e9274faba9f53a66fa653b4fa819f34eb70b64362f6de730f5827f2772bb767a28aee84032cfe19b79cce6d18adce79d090b861c23f9312fa1d516eb107128bc8512f29b720d386e1d213a12bdbce8a1a5294534e30f0dc8efc0bdc7dd073f53d788a9e29a19cabf9719cf7d660a3d7ffd560dc108f3be3d60969de00dbc5d48f7ee05428523258fbdcc55b2838de24ccbbdefbc04981985c5f073c1fbf8538bb7a9c915665ce57cddee7522114430012bb78b0cc5279bacccd074aeb131621a15313255a3de3e2bbf493c88831aa151a2d876fb18812e354ff6270951045746ead05b1581d92599d53f15bce9a3acf0f5a77554223371c52126a324693859bc1a07b395c683f03cc5e2df191947baec193ec48916c80b77b78505d3ce71aa8c8a4b42fe443c545051b0378ae5c475f026c038e39b814b6f8fe044a8343c0fe6875a05009936ff0ff8c53f47c8148115352ede801af9e5d165908"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"acbc145366b60f24c82440754fe93ff68b05ac69d0ecbc1359588ab55a18fa37","proof":"26299e12ebc2aca0342b533ba09a6e7ac86704a66d702326a9867ed55ff66d1b9640fa9ec616d92837fc8b65a8db3eabab7623009066bbb22a435f697be1d337c43d8e981f49d54fea2ad2b302a46508ed734091a13f7a7d90f63eee89b4442ed20fdbb6a69ee3d9b6e98c262a740afbf6373e26c15c7405a60ee24e56613619b1d285d734e55e33a26eb643e250c1218b411bc9d881c2620c8d7af1ae182704630b8096080c17333e914dfaae8f09c776d2c33f6ddf82bb627145b2ca1709092a7b4c662dbdfaefb043f4a67acc8cf39fbd33aa92e3ad46977cb32cb278e10d8e21966c254c5361b83b42b34ebd3d021f5bc1f73852b353f7618af95938113b8eb28c2bd6cd64bf5b57d893deba22d84a93bb06e5d5803eac2be55b347df2554a4bfa9df68f9a8a86ae89902ac2d19ed0e3508e11b2fc133bf39968e20dd66b60062954d2887586faf8cf2e6316be7449af7c50e9023e3602ff818b7a79593444ea7150ec61a13695a563fddbb22f60d828881fbbeb1c717ba400622204e7207e4ec2f86c5951528132180aeea77b4622d5bda52a27a1c55684847e7cfa3c12d4f0c72b2f8e16ce97778a64f14b707b21d5ec4b626d3ad22e46752856c56a7b2e0724fd497106c6b002489e0f34ca64b410e1f1e3f9882bc51eb75bbdcdd060ca6744b081f5ffbd04a421cfdb48465223912608a6b2f633f7e82bbf93157c779482d44a0aae015fe3d671e844295ae505b87d24f81643245f2bff0df52799389cfecd85fadb815764b3412031c65b2ad3d24c30605f72d1a7233a5ff3f5fd1dfaeb4fe3b77528f9bb251042908b567b49d4b1646a4fa6c8bfe2ee39b6e54844f99442b15d449f57697a5f3540cc74ef3619c510effc8fb552548c860e827c0559e786b45c8a94340a1e62fa4ca867b8d073ca07e33f67bbb4bd0e9bb55b170b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"92baacc6822ca82268448f0dcb830dd066983f7f6bc6c8f47db7bb95c325b077","proof":"dced6acb3bbf176171e97592e85225d82b33c392b43ed7d232a339320977cf77fe4517a73ffa7cedd42668bf06f203f29e1ca9964d7a983beba4de33e702eb54a450b8be573590562c7b53d9b28be287d52e3bb6b498fe093790636439671a1348d552c8485c74f0d7aec71e72651494826736694affa297eb599214491b97050bc0c1db6f8299f0123db2bd8f4412cd1cde1dc1b1be2e69eb16e691f9fc120f0c053efd7281c6578753a6a4f1c0c845b6b4ec22740d22a0ff793ddedf7a6302edfa9c4e4eac9c6f11ecbea2dd697af379a4e633991686f8734cae349171e50ff00a1ede4b17f515305b6599ab6efbcc448467e1d679aa1b6f33269e50417462805901deee2a9d1401285861b8d76b01201e82d35576f0345f13cf4e803f915aa26effdfa49f9e104b3417c4b7a1c1de8b02531742dfbd8b9f70120eef39ac619cfd8fc7c272c57841b4bfef19ca75bf0de6476dfeaa5a69ea59b03051583538a82df101f14193c29b87db8038d3ead331b8456998bf9000b4ff445d0660784e4824fbff3d71ed304c1f358d890ea817786d4eb8bfff29ceafbee4ebd02ba54b4cd0221054d0303c2394d053df2304e5c960f9ca5b0c28f25a59dd543236fa568a133185e8b95f4d3b5ab27e5fcf7ea0d5760a198edc3e30e353be24ccd2d470f67ddb4cd7360c86d9ba30b4aa3f810c5ffe2aba855c4c0d47a215122813162278cdc3ecc9f9300275a8fd1917de8c0c31dd9629e6001fd41d889c61d0e8377b528777c897aa2a6b0710ee689216d4fd63aee1daecc9f2a70c0a33c263ec04288650390d104f628da446fbe9d4d010f5967312144de8b9bd925db23d431a694a3adf99faa34d1878649fcfa4d3462661cf61b344fa004db5545bfe86ec5de8014c0d67994b2ea42f57cc42232e7bea9a37c8f3aba1ae23d7f281023c09fe7108"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9683e7f2428a577e839b4a15093a1410e18f3ad0bef6e598c802277cf806e639","proof":"c02a746fa5e7dc581f30ed465141f2746423d99836d6897564444933503c224784c606539030e4a0f397b72464e93915ff51118bae8268f00e465860ce2aa628ee25b2bf85a97fd3a1085e4eb1b7f820571e1d04c4b6344af28a5c4ef6745c104e6aedc4c10343dfcd9bb762475c879c51770c9eb0fd0d8e03a3238be3dff828dc2684e70ddfc65912f9f1584cf060707402f0187ccf07903595d6b89e610c030267452aa9b65de691343f4502a25835ad55017e941d097370d487f722a30407c96c70e709f11424884a713a702f615aa3d454848c074885e8230a5b21f30b0372da4f7ad7cd3389d37d977808b939f8720b1a58af8d2478b0b85a8bf49048502246d942857fb57e2403d6c1d176eaa200e8d84464cc652089e00456bd126c56306bca6a7d41cb74470f9e3e4367e38278f4aac7520bcbc2eb3caca4a818bc3f02cc37b540faebe67c8f08b37b3b2c25f3c497ecef0a8da9a6558846d2c544299ceb9cc00b1c80fa566c07e33af52d457d02e428dc51f58432c3ea14b65bab50a453e41feed0c8a984d8e2696501e6e50a9235df4e47da2234e3ea07fbe2c96db4f83053014ae70b590c2eb39b9412615b6f17e7821291eff38ead10cc92575a700342844ac8edcf347ca880eca62bd92da65fb61e74cc52e1ffa5c2a11c700948527e174056f829a1eebce4707fb4acdf4ed1896e639c5d7fcf1d90bb59551af62da52b4c8cadac734f802c89eeff1bd8162a5ce4f0628df1b745d4e3e37e0b54c92993673985525a92f4abf550b40d0087ed6233ceb200031939d3b59222608e3b18409cbf44b09b78761574129e0193b85a190ce2122e129fb67db82847798a65901e9df8315203560cc4c9c1751de350fb79ec5ceec659fd4e4de4f2d50f3f7db5e68cb4f46bf581341154efbbd415b5da3b89cf5c916857d784d6956b00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"84be03c030c816abc49083aa2124018e5216c8bafb073663455035a0f265730e","proof":"2411b71f81dc5f133035923100c7312e80e7187ceb4654d2a19ea1f2af42906f14cbeec897f262f3966e34f726add92c2c667d9f0114cf9a8647d691eac5111ed8ee6e6a7b73710a5a153cd2481e09be7a023dcf0560064b5140be20dc692b4936f2ec68419ca949d312f18eb80054106356d6ce8dfe970bca64bd3e9da6b870bec27952b9ebb50f515148b2a485518b46f698acc830c789de18460c597b9e061e96b85385fbbf490df3a7f973aa6087fff165df7e7bf3b025735f9ef4f7e40c468cb81e28e6c51da20b776a028732d07561503aa9a0698397822686c6a53b09387be6b78f98d4de8d1a8963a70cddf089c4295d46fe0466c530008a2145291208bd55ef03a6eeb23d90580348a7e35665882a025f42c9a3336ac3729b08dc5d9491d8a2abd7a53b8722371222918ab4f10e079ce33bfc29bd1330aad075c0581c274262437e6fbca3f3561638fe8c78e13a232fb20a8e401cfee34f96757b157476906c1a722f339c5bb6f4f45e61b08e7c87535a43a266493aebb22d964574f20dc50df00f707405535502703fe694dbee7ab2c91eb36c309fd4d428c81d4f3a46374a06577b330de01e09a1db3c5300cd6165d09e033e0c0d3ff5d1665f5638ed125000790fc7985ea2d8c1890e61d3ffe7e90748c58e7100c232584a104d7c0bedd48efd145044ee68be3c63ce7abea9b0ed670e93725a455032b2ea70799639f16eaebe4d6a46a9ee78124268cd8d8bfecdc0eb45cb747d366e90c2744ddaafed898c2ccb0d1beccbf7315c0526d0942bcab110fd0ae031f4e8ce9a775ab8b9a4e1c65f35813ab48fe8ff5d9ce7a86f4cb113a4053e0d1fd9ff6e3baf40f554bb4b897b574e8b34a19a4dd311a6174f5af5f7a08ccd1029e69d542c770fa206ff8f3b5d4c7c03c75993010da9308655087da325deb21a64a75d46c46001"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f4a3f853afd33824fb48481740dd0e8a937e5c1102e5144dde339db3c9a36053","proof":"5c78aa1ad0b7ab84a8cc9e20837d2e313f7b7914e7f889e9e2bc1c608db7a618d6fc602ae5eaa578e625687f892346c20d700000336e22fb19b9678a0b849c71fec15bd64da6c27c60d5dfd13d0de7bd1247123a1ceda83b4e15e38e8b9e9e48b279bbcdb7d0d8bbbc77c434b7b505e451f653409ad946abde00ac2e8259ce626af559c46293eac149dc18577f397faa24d8b73f6e052206a69234299a34c00d4136bdf5c351561dfc322b43bbd35ab077249b8f039b129ee1d4c63ab816f90c128a3eef1b503f636d3c36af5358b941a0976db6615c6defc5038d40eeea1207fabfeb67b12d819a3cca3641a557e62f4e62ce70582329a3948d765eb6e50068c670f038ebd1b749e850d32eb4a17d5050b964a001715a4d615af7a8391ef56328f32ef4beafa02d8376be8130825120732fe1b91c913b1a60b53d3c440e672050e4886e1ff17f4dc390878f8fc910209a625d4b4c1eb1ef897a969451ba5c69563a3b17b23c28eac8db5a00e71642fd7f7602a44d4e58c18439fedb3d28253176366608b7712d5828e9b71ebe077e746f5a2556341de56d4e017ecf6c28e95c36b12386f855f68ea7aa555cc9da6f520da5bd660558d6e36a0ed145023f4b1a7aac300185dbff546d29fdebc7c300c7e921b7a1551d111e9747894a990b432fee6a4fb223902849c9805c186d3d13913014782a8745b353e6dca4503441cc3db089acf739fa0b41857f7778bd119ce8cc09e7419825825a72d5d4151db1ec3e1c49a834540ebc3ba0e9fceedbfc9ec76640c82f0b806cec1e1e456f9ebeea3c70dd2b01d921dc99822b1ccbbdbb5bfdd18a9cacaf4712492be2ca046b64e4367f9dd6c6262ca1fb525e88f2b1edbe268da93a02d2217b00ccad7fffe232500288338f35e1e85e6b7e59b56a9747cd72cde1d51a2e3bde161d411b3b95297209"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d658438aee87d257be77055df7a24af0248abc93c33bae37f4fff78f4f1a436c","proof":"e0932e5bd125a5fc50fa860e9a0a9afda7bcd301edae6d9f20116aa1766fb6207e663875cb05239091119a205f8470ed894978c76801c75433c091f4d639d02e02775e69faaac411248d3d3f5f452d81c20598e9ecfaa1071adcd6e5c7028434406302788693d7aa9e27c4f8eab3881b35047a101a2b1f97acc7042ede70923009626bcc4ec5950e8dd0debbf0a8fc832ca8becb2ca5a0d54eb114bb27058f085cc47838b22e5a831240eec1d9b8bcae53e1e702baaadaf8557233654bf576004a2a870e9a04d33d203a17f152c08b1c9192195a7916b10adf718a41c769f707f6e6c9960648d112c91fde750f30af657a777d52bb83a24495e0c4f4af3afc11520e19437851b0568051f1b8e593184b70e57ebc36baaf7b966be87ca7a4396dc8ebcbaa63da74e7e1777494e7ecde088e709d3f4b801f8019453411c6af184e023cae8f233d0ca36023b5fc25268174dbf16ff3f25a7d02adf6d08379edf1449ab0f9049edd42a9766233e998972581b6eb9b8fd05fee3de38774e039c0101600428adbabeac881db7747fbb73de1ca7fa4249ab764494c0338123ff25e1b200647223c0bd0e130d625e34922dca9243baabe51837a86f8d87bf37a86559c1e866f54b6668f4883b527575a2787f8a468ce87eb3e191413fdf734bdfca86823463e1bb311a4a0c9442617a374592ae3bc9ffcb9e8a34ca8a34e9cb243f1b32e509e033f9fc070bafe19cefe3064658de40f57cbd13538f4e4c00acab1a2407e2e16608b8148b56213f3d4188c5d571bee36a5c41948528719eb668657da8828f6b1adbc65604c7ee77a469b543655e9456e049cbbd3cbcb25daef61a553373dba7209930f332e7628d9a30448653748821dd3e0f82ed52c3a77cf765378ab09f0e24ccd2e082fb91cec2003e72a17b82716c09905a1a16786a6687d14817a08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e28a961a73f078be614937a3b4dbea7192c3636df793d3cfecee42bb80874300","proof":"34ea70d18c073ba55dc87a6eb433d27f5514a10d65c8ee096133677217703638f64a0ec79b5415cf0a806233b102882f88aee0112de1c39a3dc3e5e3e082fd764ac2652e86daa50742cf3d1d89e2f2772f0deb60a9e584c321e3a4a189e0876498f8e0081b39053414c617fdf18085156694cbc11c85321e051ee139d29b7f227d83fb8c383fe080cb816a9f8cd6651bfd7234d10fbbbba1f5cd6b225ba57f079ac24bccbbed3f29c97ce2d25666c028935e1f5eb809bef0d9cc699f61e0b5095a3a18a1dbac6ebc6d28790e399692e269c34474c9b009cc59c0506149da3005187c5c8ddde1a25eb6a08f057fc7357f560e54db99dded198d075d8a2628604c1caae223b775c2006503f5aaca7c5e550f695ddd609f29f8117e4a84aae1ce60205d1644fa4a9fde98265289b9c468fde0e2917e2755252836bd811f6ca70557ea1aa9c5cfb04b753500a08d53257ba123cd62dde576ce521d99118f5ef7c40114a2f238866f187a981c387159f3bdce215e4bc48ff4cc54af16ac46b2a954330ad44d14222818fe3a4daaa86f005d031be180a97b88a52b99a433a70a7aee6ea8e38e73008c8bc5548f7e25b9f8049e260b1e64d09954e20099fc56b89cbe165ae85d59f1efe9625dedef6f4fb2a526a0a14d179aa5fa19ff94b0c9d02f7a7a445406c29e9bdf7d3d1a3678acd4e911d6f29f7b80b3baaf5edb671b0f09e660889bd0086e67d8b6de3b3aef2d3cc3dcf0b9e8e9ce62fb963d0cfc6cbda7b019eaaa9621ae3c7799398df21e7284d3cfe696125fefb18339a22b75df382194379cf4f01b42ffa54d4e32e4840c3efeafb27eaac5070ebff55d5570277fefdf6d5a2c9bdebef586762ee35126a53c68b3564be2d430c0a6e016550f1503a2ff0d10e3e289e7c1a839e00456a27e9798af257d9ece9c2d5adc577590b0e5c43106"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0ab36ea213dcc68dbbeb30b997df1081b86df01fb5e87ed40ba18c7cf81fe50e","proof":"7451f8c2b72d7093a15a06723d2be947fcb6d9cbbc80d18208ed51f6091e584d8afa817656063296557c62010c736d78457ed4cc5955000dd9d1e75f3f395e7516cbf782c8f3ba9350cd1683f9696cafc803189cf7ac372b6878ad5d9849420c6ab153fb096a6b76a64bc0d43f4cf068604a8c3b89bcdbc8aa1c6b2f6a6311147561e33f3ebf78c5bf3b95d59c8a20ec16e5ece688998dc67ee68cda2ab85a0f2b2a6e58d67a56ecafb9981e457a48f608d274a58d8de3261fe7d20e220ed90a2da6bdb0e644fa27b32eb65c76e0233236fc982b7e739b8a45e4e1acb3778101b4c034c641674705c77b41f7d0ad5db099657f4d20ce1a6e767abe53417bb4699efeba74f4b69b2af588952d98d5c5404f6c3b7cbb02322b4a8f3cc72f24076adc27ce747096d4e10717d368d2c6df52e49ee54d9e5faa2a65df15eaf5712d0612fce35f609004f059e2beb36b81cbfd0664b162be4d75e3abb5958ff6989e486c0fce75f34fb2698df0ad3cfc8b6646573b4cd7b0b6733b4558866450f5601e4e5ac96b76afbb7ac6fa8cfcaef1de634ce2f544f88024e565cdba90e00e9a066e20e56b0ea4a4ff94298fa473d3f4f167172ddcf320befaa3be2ec62857020458601649d12025ee37612e8b49123cec95bb82247c68332d1e6ac4f72e2e755aa82fd376559703ef3802cff28ccfe9a5a4aae5a6cd72350dc9a0d6afc8f57c1506b1c91f94d961b146bb2152dea18cc8db29e678c1cb762f6b88f203b789fd65b69997e164083c4a07328dd9cf1e0b57c1da7553a7ef09227358ab74a8b0f7266ebfa7d436194bba269d439b2e17d09cd144215a1d0f79a904efa4e5d51cdc2a1d115a7fc85e33e56fbe7e49b5a38b51a468ed40a87a291824f3a4a722649b0dca2f2bfa11baf28dd027ad14a0c69181a4fed93c9cc86bc2b6ea509ea7e72a0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dc383493ae374de93322852828a68f758f0264c100abdb72cc2b0108b245f745","proof":"26d06dccff0c89c7ee5d5f1326650961691542b34a7482c3c45d2a32ed72b45518758e8bb5767996a1037bf459d063cb076910ec34d91233f66912facc72c95e889ca688b7c34aa9e2775cb589545b2b31f1f9337511949eb63ebaeef567332d5a066a31a15555a3a30de946943f88e5181033f9af6483a679c46d88f057337ab4325595b3a216a91b9bc128a20794036a979c51d678bf498dc3f760896ef102b3bb9280a5551c60e40d17aa645ccd52a3f7094a773467fde3f30ab95a18ef0ad837ab651b701d43e98e2c99e4c3d31ce094af8b8e207204d0bcb2629734a302f805685718d0b9e17ba7a28fc40e7d9a5f1bac696c694f6ea07e3f727c7c8b44960c6d0557b1c064e8d38832aeaaec7e7254f3a60dedb26fa799f08d34bfc347a6a1c5f989d1235e467ac31f4ec7befd248d1232287ffd5d92da38a7b6fcc13d346d4a72abb15168989bb6cf31278be0c840d360961bf476a5cc8d50fefa431014c437cf2a32b14651497f2f33b992f9fb0bbd9980516d3e2ab2af44d387d80074fe0b5d6f1122002d7c33e3ea786f7b808bd0cb523f23b45a23ce8091e2be27d21cd60103b28e2a1c9e85c5610402e3fc3af776b198aee9669cce517119d61df02133fa891e4e126eb1882e9c4b3497435900489bceec67dcba25b14fdc935976fa772c1f11a294dfd56b5dba8fe489ed6ed5f4a302f03ac489c8568d54bc5172709f69174bc8f8797c23d3c2966561f60ff79d69dd5453dd81a80a936afd7d2e4b6e2b606a15f45bfedf0965b18f7bc60aa3ee6e42e8bc10c6dec5104b1950005305a8110b8adb267255489f5cf892962094a513c4b79c6d7dc69c1746d72741f0f642ef6897a68c988687769ae30a93c29c4294cc6f37a42171b5fc194a0aa150857dc4ee259f5a422bee54966e9df6e1885ec9f3824cf92a2006d67f370c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2668fed699707613d65a2e86e238a5ae0fd30ab8672325de95b50bf3c27d1c5e","proof":"3e5f846cdb32c958b8202f178485254c3f3c188cff703645d34daa0033497340888459fbe2c204526c672ae2c15293b17c08df6600a855733ba4d85b01cfc7467054474f74b937e813fb8f088480a4fb582e2a5036d2f51a7f50b540da105e174cf002abc5b1a2085450f73e03e43056ef7f8f698349f95a11ec3eab115cd551a3d19e89a0c7d5f253b0748612667f3b7ac61b38e30efc0b74449d214656800e439d9f5713581f6cf7a9b6ed53465134a0492ffe8128d6d3ffb8cf364c05b80ef01a848ccc24d89148609fdfe107cd6dbf3e9751b2d5b5a1856b1eee8e1a5a081874ff7eb57e890c9affeb9dbee264fd220e3b4d42eeb49978fbcc51660b054202c560fa2bd564930086c17b888a7801cbfff365b4505f14a465c17441b1244a8e058ff1599a8a1888d03dacfd21272d9fbc42fe0e1cdd21cca3dc2b7be2094bc650f004656850297e84c4cc3ea8979a37fae09e7f4b7fa873bb281d06e3b14154b389775cb9213499cdd2bb4aeee474b996e378700af91557b49bdd95aaf37ad8a393612ab784d3b1b2c372df3a53b2ccfbd2b70e56c67cfb83c2410fd74138421a390132542972a1ee44da8441c223cc4101e1b68ea8fb0d0893f6d783e139a0d46626664254e117769719e285104cbb12ce177fa3a1557c66601efb2e817d7896ca3c9b3f1f028f417bcbb56b96f4ee4882c2106096c15729e8269621115fdcb79add46207862ef419fd32e70750c4b2a4361994261b95aad66cdc3acf4414828acd2d3bd34e4dbd6362ed756e59265f2046cb8e058a167544f06c5490d44de0c90fbba2ec4f31e1c3e56cbb4ca6d6e7813e860a5f62f280a627e732b415a1004e8794ac26d8c20cc800fbed9f65cfbb424be56e6bd6c70fb24c111c5240136c01aeca3149b1b333a103a544f0a0d28c5676f5af5b66751129f0130246c0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"98add1e9d4523a09256428a73ece0b28191e4e02e491ebc4cd4d97970cc09239","proof":"a08581e8d2a8f803a951367aed61bc9d3a322e3e46ab89c902a38551c78c236004393a934dfca63eba2476b6ada68710bf4c00f8970d2fd5a7909f5e0016e94a7a19cc4c4834a6d622005b296ba8a5e1f8aa42b0bf228666402864cfbe363c5de65a24f2fcd1d0ea857596561d0e74cc12a6dd7c5a4960891ff969cad8c7565c7184114465ba12c9a7597d4e6b311c107d81156a3e4abfe15f130503c91d410077e693ade3c5b9095c7c35e85e6206aedcf8f5c935fd1cc70fba4304cac6440996f86543ae4e5ea07ca9c0cefcf7787ed1b8ea3ec890b840c51586b8be13eb0fe4c2f28dd0c90cf947f15571817b1060a0fad28b75aba625e5b6ed50e66ffe4df05b69d55ae136aae64c58bbf3e9ad057af9ea7e9e08f385cb9c4047b1e06a7f3679ba50f65038726e76f6e11fc927bba6fadc7730826a426be688cafb1cc00aa8684b8a1071b16eb638e2d91e3d65aa0c303d0d40efcbfddfa485e4110e5f77e2e63f97eef7dc8c115a541550e3633ff98f363a00b65a2e8ef85006d52cdf75e65781c7792e6a0ec157ff05dbb275fd8120f549e5617eed75304ef6049fef3030b687ec4d544a646be4892fe7faf77c02a16617b767e689dc9a726e3ab9396102b03d790cc8e673b8d31ecf86da64f1ed2b9bb4d30717e7baf629e55b426c0262cc6045b3dd0ebcd1d7c2670916f65b00a93af5c552268d69d6b211d83623115c5f63e66f5cf9b344556296987427d9b333336f75f6a4a9fa9c89878b5cc073849482aa8915b68b261085336e4be4d0c4a08ecb411f95dee7c6e9a346e1020016d18281a2b6acd94b8959c5e409db79b7edeb31df0194374e79713889288c28801e9a72df84c63f4c56c469f058344262974de7cebf6c88a020115ec81b300083589987621118f0ada99f6500fec08267fdc8fb7e318f138a7f505ef17a3e0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0e4ede1b52427c2d7678ccb945f046adace23f3c31be98ae00166e0035362c21","proof":"1661927a0e2ab15c4a00b57d8628d0a249500a73e9c77ba94a93b28813fedf4d789ab2e55c906b372dfcdc9f9cf5d199fd281e2e12d76f6340ebbaa9c63bbd3cdcce243063b7ef059f35afc65b1bbb8e22f82657186425a0386908d7d85fe75a48dd616951f64fc6ae6d21c2f0c4bbdbb866bbfef1302d2cc1bc4b7c76cf102004db39d38c7d1b7b71015052b3614011c1fd4998faa5da182ede2a3b64df3e0901859094c91c8432dee2b682e4bc7afab24e5c11419b5b018f9b5a4f478338083664956e71aecefc1b07086dc2666b18f326cf2f1d8e53ebab78c516ff55de068484cf924568028a32a5230b4596c0afc3b63ed307bca7f93989bfe4d24b736d88ed9fc4c0bc799961a12f2679f2429bad307e6c179f495d1c3dd98e39ff4a5b6aad7c7bab79a3973414d39fbb04bfa0684103d56e2dc5de03e4caf4a405116f66db05014301e66d879d6ee30c989bdcef4bdbadb1b7723e02846e0c9405f12b9850af7261d62c8c6fb1d83fe3fdb70c0d94382d8691149834a410910e711e44f2cf9bdf6bf29fc496abbb7d541fd13afcc71d1bd681364868484b56c6101428b09b9fca8c207342e8cbd74145e644a5cd8cd081466fa2dbe5e6a85fb15c532aa44503a509c6af3c6f82970dda199581ebf264675a6330765c1cfa543f48d538d8fcc36b831413abbf24fef1245336cac1eb179d0e74d7d2aa7fec45a7ebb63c04bed5a807dce5255a1f9b3a9fcd32f3fd5d77babcc4e7ac9b037be313a53f49e613c2ab28e3465d05597bd21ecb93f6a6f6d02b2f7aff69ac0423da0d9628004c0997d043293f5d5b7910179395ddf64b07bf18ff9afed3997ffb211faedf581ff150259b722f00caaaa672a8bbc33ab16ca00c42b84aaf55dadb6a011f6a01ea29e79d0de326f67649aaffba5e6b73d2776e25519d64d79a15829cbeffde09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c8c97e60be67f5dbd38cd3963d14edb26bce26323dca5ae9e5d0f34e3c078945","proof":"96fab378f45381d9005866c8934381b9f58b5e264afe600b29284f407ee3966c5865684916649e6760a7fc26c217287a672937249a033c75946f71bd14f5ec38329c24ee25283d106a03a3a099cdc9aae2fa0c129339be19a78d37d0667b3e3ebc360924b77faefde78f5decd5e8ca05e4583a3dea708117dc2aee397b6fc738243c19e056471fe400841cd4c827b33ed4c1f092df89506a8df985b1eb3b600c63568e2efa24d8fa7f9a895e5515024961c4ce24c65e5d2bae218bea3f4bed03800df6740a0d5a2c92e61565dec15166e05adf7db7ce9c44fe30a428d2079009dc23b4334792d936243209db743c7b71425b714344373221d7f557dbe099a03e6028523e8956ff671dd5794cdf8edb77081ea6258c75cdbd5c1b295f8ea4a92d7203a34380f30a9e196eb7fe1b35b0c2a14d13fb4629b12f92a7b51345fbf1757689767cb9d3810f42e7cfd40c11431c8bfba9850972ffe61b738205fa82192ec6e30361113724bc852fe8975a44e605cd6dc00050814afe79908489969eda26628d1d1330111d2a1d08aa66ebd0c5bff76af9c48ff884dd961c90f9756fc34bc20e618cd723e9c5b23e077246e1a65de0ed930eafd6feb68e8de6749e4c4155ce44ad879fd8b82038143e0cee2361ed3dabf699d74edb578fa68a91fcc2b14e4413f7250a496bac927f57a085e99fb0b4d52d01288decca36e1dfd6bfdf6635aaf27a56bfa4765437c6ad91a07ccfad0a318821b453f6ec44c571402d2fa670ecade6438ecb2f5882f6ca11c511baf295eba2f2ae30f56620f636db9d6c3e56fa89cf278e34b02e7c67ef92af10f0270acf4e74e7b85b3c98e30bc1c151c0749d62120022d12283965c7508f00c9e8f9ef1e616bdda5636d72cd8507d8dc4061dae56654bd3ad4ae290c5aacbb3cc7b58a4524c49c933d30330f3b28ea3f707"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"801607da79b29797aa4abcd837a96393c5415c8e2ab6f5ff4b1139911f34d526","proof":"44beeccfdaf31c23da8d2c5d6154f06f2cc4e2e41c20b511ea2f54ad9b65143dec275a503ca4513e1f43e3741e2f0f9b8a32c02e3ba60f1b6b6dd144a7fa7b5f7c336f7e0de1655765d6aba617058764afc223544464b39bdcc15f5af61874065c17e76de8b15028b7b0e17b9ec3decd287d3804e7bf614d7ce760c24b7fa96df1a1f679865f6e676fd8f1086082a197b1c452749f355319c8df48d1088406007ff5d86ed691612ca726cd6f86d3907af8e60e70ac8100855a2835c11feaf30c65e738f634eda5bb3a108e083ea7803022bdcc3d9a0a3fa2650df5ca3379a203e0d33ad5036ea9d9060c6ac7b09787e8575a1800824a05bfe4feddebfd7efe20d430846cea84d6cf2b74525d9f6760ddf1c0343873bc67c5fa448cc944ddd416465c483ade50848a53607893d7dbef19b4ad75905569a38dcdc9fa8968f5b01ca063f237e9faffc3a2e5f46ecc0388035d2fe9e52889e8febd6be07f9c6ef03bf215bb6c7e519040eb193cd66aa9bdf9952d2057d4e24104e807b916e267c029b01e100a79db8514cbf37556df2e4270c7b772b9c218c7bf497fb883d6a8d124e29fda2f47809efd7f00eb2e6d233f1e7ba6f8b576d419604c6d65bbead1c34e64ab94af8b66827afc7c00ef098a54bd81ed5a2b1eb4584446213f0fe7023b0f6470180ba2dc7941055807dbb1da62bca2f2a2a0a3ac7cfea46d92d9264e0b524a9f9c868cfd57293fafabdeb7e35d2707b7c2fe226080b3e9a1e0184ffb331bee9b56621e35bec731af7234b7fca051d0e58d9f5dcebfb117b40df2d3a0977758b5a0299ecae68d0a63ed62b0b9e5eeabccd6992940136c24ad4dd7e01216238bae6c4e616178c05442e93b5fd4c9974186f61cd78344f4e5fe9b8e666a110e6d095ad1218ad380fc365f0b59ff4cf4da5b5c5142076f68713a7299ccc51708"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"623a73b9379741e61a8473af5fc453060626c5c5c8b71d172c8bdaad5cb6b041","proof":"c8a031cd9a879a5f1ae9319164185728e570df221255df05db61804ab706c307fce66baaef93cb05dbf49fe9cae64e4d457ee91ca61c2dfd178b356b3b97164b567c97c3f3d2e0c6494760876e63f74b9b89dce61bf70958f08b4482d9fba956ae2c53dd28e8bfd30d4d8792508137e88ff36bb7efea7a8e38125280c6dc4f40f70307446192345962434106835c44554225bc4dbfc7eaf558bf050d7fdc480b717d2b850d67d5f12e2e8c031fdee35cf796d9abd3e69de56410ba18da8a0d08155b7ed8696adf2d7ce0b6827d86bf9010eea7964f7ee18b0003c92363e04a0be0d1dc20f456a3308829b68834c551d899dcdb6a67743dc665ef9c8ca5b41d50b28754c6039c626153eab91eb284bd812c32c0673a820ea77c2b75e23b81b113ae3bee17578aaff300349fc27d9f496a0613288b4bc1037f3131575237c9f5724aafd24d551e246dec878ee75ee0604fcd2726487725386b8e9e1376f901aa6128121d724c2fc281c328af1b5047d39d2f6d166fd19c6c47e4aba98c5be55e012a421216a8f9825dea60bc206f062da8e1a51cfb42319938a0f628eededce765d06c6a04c82e2ad0ce04778ad1992e52e46219bce3133fe5887c89f5c2c06a0d6e4c1390d86e3f2971a5d1d91abebe6fba677fe125c3679e0dded3fc5d32765952b05caf24477ef9e71ef549bd9c75fc31d53ebc05ca4dda0486cbb1e2f667566e810dfd702f6894ecd525f6a21bad5a5d7f1675d140d8ddef5275d0afc71d686e2cea631036274cfdddea6152b5c36febf7a113fb490223acf4850ede84be33cc6131ac5701dcf2d2e2c2c4605084d5fb8b09e8cb6d4850d6656f15e853fd5232ee9d6d96224ebffd87dfffd98253fbae334ddc904d7c4e05e3de8a3b07c00e1ab9fc22161a4a09920279b38ad493df672d05415ee5196bad4169dfcbd4aa04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"965c12d31b3d7cf1b56e5255cbd1a3183e268c8decdb5b2d7f3d8a79e38db062","proof":"0044bf25a3236d8017cb573f593005843cc54ea36b20c47ef1bf3647a2a2404feee35b97aceab5da73dd5cacdadbda23ea125c4b0c6e5ba3208f5bfc0bc8584e1a0ea50c61607a5f8ce9f75656c67e607ccf8608a8663e3e48a46b421c9658267ee4e79637fa497c0aa607fbdf0a257b875eec34e50b859f31825009313ea24aed679a91613711837ef14c9d1366e85f739f032a2f81448adca2228ac212a30e2c060f9722aa1c1c67f7ef4304e6ef13bf728536dc98232bcf1f046f60912106c3a68e991123d25a48ca152b1465aa800aab1fda946e9c0a6d5ef06a0794750200b4130ddb6a7ec0e9451d8d3f514f42ef9de4359bb910a8813facb3df2fd861321d55e8d1121b93c796f28cd4a23c6493734231539e3b990e2e65b7e46f020d78ff590dae1b3fd345fe6ab65a347d9d69d99928614d59e496a769c2f8dddc645a2e2b7bdfbf03e976b7afe444380c49ee2ce46b1b776c454e5d2da10e659466021501e3092fa49152a4c7e8a20726a31416a6f02caecba1d5467fd320c87c4a065cee5d1940c8531efb03e384c9a39e7def36e99ad4aa5c6d820141f6e645229265ba3dc89e60ff9a0134e97c14192c6e3f54e851d03be6dbb9c1caea44b65c32011a4ec12040922ef0013db08935e172e0c5700ecad4d7e5b7618f5560bb5d9a835e95cd0da89b9180cc100254aaed598ba09da6fa31e8f5ff3aaeeb7d92146245a55ea953746d6742fea776cafa47fc1b7301c6ba34a8e133aa4a611a0c74c8cf3a26783a82d5aced88ab79b02bf2e429adb5c19f27516a31debf5aae892b9a8b0142ec4fc0157804bb6f711ac9f5d29bf471a54dde2470aab9cb92899a0288fcb3b62b03d9330005a8d7124f8bd7d3fbbb5202e01f6f168cecbd48dfce00ddc49d6d481e4ab46a8db65df62b7ebd9eee04f62350d14025bb324f75275504"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"24c54a3f77b5894199073c4eb760fc372b776b3e457f5075ffd2e9955fb33d32","proof":"20dcc19813126fd15ff39263b49934914a32738d8f1ada43d9c2ee98601ef07ed21ed0dd50898eddb5d7068a21d6153868de3f7afecd3bf72d2ce4f03c829a278eec66f075d313f2d1ab5a78849517a49c7d011f85ec720c6f0e4e56f786d402ee86754c4adc3b4bc7f24ad7df5817573466df697c622eadbd8e537127040201a8cdd25d118bbba2bc981a7c50455df411b80ead281ec65a4964de0b045c7206ab1c5c53b28ec433e96f0fdfe72d2b3b299e208bad4365c5cda00a5807a1f7052d6238235aef7a20a01695e6e759c6e061c240475cc2b046ec7017cefe90c80f2a3b04287df4ad45fe2e2b81732538016b954d9965b13b6fc6400d9cbbaf317fe22f620d4a6947f1e9ef5b0964903b19600fcb07f73b6a535bec271d8523941a66b14a266b6082cf6dfc596fdb26f193c2a08bfe7964b5f2df1559767bcd5e5014fd6a66bf2b25d4a1ff8592a71e357cb73c9da06fa73965c52a736cd4659f12c22786c47f84b3be399329748d8f4a0095d31adf8fc7d514ad4beae72a00c66d84d4ed633ea355ebdd131b0478bb60758b9c1dd17b8e8f988788bc3e7895901470cf34e66019002ba6e3713639d6442456b854a61e531b27547fdfc170d6f81d34a2e3a3739f585d3c1bb9280b47ee38a380ab95b254941438bc7a21a75148050a0caf893619a5fc01e582edc2e90df089850ac5caa00c92e72690508806070ea65e74786d63f6fb9cce811ccda466ce80330a5234662e720cccb8a26531ae653c6e84cbec043ea5dc4498d5aff03c67e661664454211cc216c9753944dfea2906263d797fcfa0184cb0bf4631ed54c33851a109dbcd5313dd53b5e9f64a3e56d2bf4a754fca7a221bd37029925316196e576783d22a00241d50d2675e19630611bac487e371671d35f248d9dcc2f20762da6ebe9b4d10dbce5b0d3a51956d07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"42c8460ac4a5375e70ccb8cc32b7517db3c0eab1ca8569a9545a4b13795c5a6c","proof":"36e790fb91739d71144473cafa8cef6eed923da91cddab71854275b25deea36c74511df53c1e1c19398b3d7475182cd3906406fb7f0d4b2206e71520fe63783032c021a3bf6b916454c547277b4d78b767708f0afc4d8c2bf43454b00b8d176348699138e759985eb79ab4b2b7e742fc26db6b2914cad8b68646e4ef4d29771587ea7b9d09b8dc58f5c920549b8f83b613b16527570955de1492e19e9974930e4e06ab7654437bbf2c2404d00452b38c077799541afe109cb909b5066201c00692b839887684b8e35c336cf8cf68faf8f73c358d54d5b494a3ccdc569ccb400c9e3aa925d009d7ba554ac5fc030f7ca4ad38959aecdc5c29752b2dbde745a74e10e8aee3dd283d14ffddefd253f2f59bacb9866737227b8b1e767d48cc163d5fe0326bbbf211c3a228f93a1656320194a5471a6889a14479abd761994f9857157e8a840c5d9fdbe28e6ec8c2d08b0ef61a95359adcbf9a4142cbe3c9fef3842362271795ad27daa4e4c01f7ea87aa59c2aa4dea1b84932525b7b2a8c6e68c53eb8e7c67b1f07df6d73df1b29b59f52a08b805a12444bffd273b882354224ec67048815ea26b7ace83f6b708b2d491ad2ced792d1dd1002f59759d505957dc26f7e4c2fede33bcd7b4aad0b9f8bef387f78855056b4d2fc50f99c009a4b3fa300726a32a38732264529483aceeb8d0de33b59b7db4058ec00c736296574237c59dcc21a600cd7e3ea308bb3c8c1b6e6d23db3543acba930e7d041e09b84638c339ef2c6e2043e411c9bd60efe203292e47fb6ecd9c35b176d25b7e8263635fe3532925f435e0d55e901b9d25d7546c7c33c43c32c51b6b7f2406784bdb78081754622213322a770d74f9df5deca39d6d73e4215e225ec6a0f5b7d6a75efc8f101e3d98e08a62c48d5db93ae33390b6fc5d806016bd7fca0edd132d7988629210a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"acb3ac2336a3464a3c94dcd9f9e5e2bdfea4d85ce22d10088c1d12a1cfc80d74","proof":"4e0fa1fe5f4ea62e90eb2383fe14af384f240c647e98b7e82f075d5355fdc517a40ca6c8d6ac9d24badbfe4de44b2b572f4c514784a8a3e1aca2f0d9c97b683d8e678b67533ff2c97b7ad3130cc5eede19eb29b2143c1cd22be82970cf6f67471e6874706da0f9a48dfed05b8a38f31e07a41ef56544b5089ea8d4c9050ff632a7387753fb7bf570b5dac6f61c6b65098412c4f293fb09a901a02cfd670b820f443231a4611ce76cadcd496c6cffb2499b11875773bdfd1ccd84344f073a540447dbd5f306c86aa5a9281c9e234d458111e4ff3a0bc69244fd4adf3e61238b0ca0ac90c3bbce84dac22c5e7de8c42157bd9fb0ccab4b1c48f0bfbe14cb020e5e969e585b3c84a1c9d8529f5be38bceab9bdadac27d9dcde2450659341f5c7716de9d7a12220db7d390655b31b820a6881c1736aee2d4ca824e05f78a669aca798ea956abf428a6b4a8b31662ce76d0ea161c35baa91568d856194814a9281a234a0ff48f94f13dcc6a6eb12e01c8af17dd64d8d4e961599e0097f96454c2832900449af847d9e0aa5de570aa3ae4af62e97c4105b26f820ef5c82c6a68ebfa7792bc1485aac1b58254d9019cd5f04e7901de709abadb48fc5c9672a43eaa9f49721016cc3fa9bec1a33be533e3bd0b8d050c4cce0129598b58d2df5c3873ff0da68eb63261aa9fd4dfb661b8b8c06948a9d1981152ff8932adff9dc224c1d71c6e4e3ca01e0cec7dbb9b270689be3506b6f08d18bd2590b4ac9658127b47a90376b7dc712683718090c59a82ad7b518e4128af05bd6af675cd961a2a5102c1656863e38c24fe905d8ab87ed35aecc822eb497ad00bc52851f28f81b44f3abe1d60a2f849806b284ec78e4cd123e24e8872201c882e740061f5a88b0a2fa37a08ee78e56d3ea438e4139734975539e967762e8a9d4b14592811962ead7a5b7e00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"72ebdd4bb92e1ceb458d4ba53443aabaadd3cf7c64e9d9f94e718ef2b569eb36","proof":"6c42946e9e2d3c5daae3b9a32fa01bd906aa26fec07bf217ae2e8961f1f4605ae6605d69e49b0ded21290df110c030c69d20580e239b6e022e7aaae5b0cd323e7865ad0489470bd106b2a7c9dbe9e8ae71048b52067e08ed2a4531c60da0871afe1a78da8b27cef564a47da331e63cee0a943ad26caf23b830994b8c4c1b623721dea62a4358af6096a2558708a50390cff53e119131bb7cb65b5bac36e2730ef3953e20c759eab46b799aa229d177534670c79f94b56366d5a867f632fa5d06e2514f3beea288acde801ef6419971a44a9e8a176ccfeb9dff4485face8d990f488c4879f272400270db1d4ae9fed4bb3e2ff48cec03868990f1ab4a7652b91ef23f18119bba690e5ec90b16e3f32834dd8c7ab25ae1cd2e789b8e2f8de63c1e78e1293977b54dc90c313a75e00eb490291100984f8260d810669d110dc3413cfea0a6a9ce8590fbb12201d1d24caff5bc34c045a928c48d7ef127197087c56ecacdb0f611bef8e43db302fd054fc20353f070eb4731a16d7ad0f5cc73fb093e086b44b237923cad6daabf1d3fc086aa4f7ccb6d80c478f48b0b97c25128e0104059642ace4dbf39c47cf9b092a66ad750ab2b8e68865d3072efa7033e7d9002e6253b693c5349517156fc449f42117befa12b1106be0a19998de8a989a45d6d941c60d788711d2f437d55368f54a07a51616c0da9530f1e716fe482d24429707e1a24ea23c15b66093641bc1f5d8a24d154ba43fbfe26c84d802b9f71776f075038c6bf4d14b082cfb3e07c02161afe74ab77a097bff677ece120a92480ea20baeab3ceeea3fc0b8ed3cf451952e4d0af958346bcbd8054ae46179148ab4e4ebca28e3db2cfd043659406a71cf1c954543b48db2a302e271720ee4f359cc40cb102aa4ebb5141cd14392280c7258b8aca9a9c1803203b49545fd59ff8b31107"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"741418437c04384e212246e82d05cad98c0c0b2dad9e1d546bed818e812a9527","proof":"3ee069e3a936f8ebab8bc88c2de43ae40b193283e3e5c394d6161151dd69dc16e016e05366a71e29e041a97d19b9b250bbc76a2fb00ee702c357c5394a8dab25ee0b3c09318d0a8645b7060c0d8dff62ceee6cd4a5ba6894ddb6bf690a4b3f709e128c725eba44e96e345ae936a285ee622fb0e2fdfe964198251baa70afaf7ebf46dab2313f159fd58ae79332ff0ea3c3aca358460c83f56c6830ad17d5d1027aeac9309f76b79da0518b40f08f6909ef2ef5abf43546b796b5b1333bb3d802e91ffeb298277d38a91bf31fa397df85d4bbb8668bf312bd7947bb88a9b366094e7fb35f41db0d930e76d019be1c6828f8723fc54e71431446198362d78f8013e2f509e2310e70bea07074ba2a92eec5b91b8bb10453707a52d962a42e4bc44d1c271b9dd9f6e7c4bf158a437e6d070bdc91a57c943ae5f526be0117ddd63f4cac9f17d7a8c3741f4e8d2780259a47789d96d453b864a801aea93f218d1dd22828b2a8cddc51ae45ccf2744931a111463e1b061361aa8d943d201a6a25c4ce76d60cb7370b4c27c9555acb58a68ee0d0aa22a0db6868cc7ddf208c6645271859ecde8f11b5982e612dbb151ebda15495a85c11d1437e1da20244ed57a1ff11617a053a39f5987d2d9ec66aa5436abdfa4ed042788c404eb0b29fedee82ef3308dae6dda9cc76f6c2162ef15bcf5c9b512887a4197331d11beeca34c704bc861780c7f5859ea2018b8863d2a93d57c2efc819add2c6652182cd200891c2d40b199cb378db5d3e7e1abafd39e3dd715131e9f56b5df5a0d50e7db782ee6fdbc47bb891444d6a24ae8de0a71b50712ccce38be479c1a7249112aa4deb3795febb68e56c4c9b46e76b04ee33bec7c324fa319feca8347eac385f0bf48e98ee69bb08282b10596bef65e627c2a0a251050867dbf2bf8265d01792addca2532bd1b404"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7a0a3f37aae167e3c8e831fe705fbf6ab1ae0098694e6ca32e4f04f74635c66e","proof":"50bd8a3518af505433dc3fcc558c88f09282a849fd9e005dd4319d30628cfc15f0f79c946388dbdf0b1dbf1c150b75a3947427688005a3cf62b362150100ca46ea7a62ba312945b9bb5bfa25c77006ba822bc7af60b581f59e19856517b10a0f5ee23578d37ebf4ddebd4e23bf0ccbb4b70c8dd53970ccc399f6442c86838e213387b2baae2d7e725d9643c42c4657a1ac430130fce7b1832d77fd6ca680be0a46115c6ae7ccade15aa2e68513906d804b072240eb3289b8048561ac2f32d203b9a541cc2dec769105fa53ba362df2edc9a3a7c4dceca0c97eadac3a048e440ab873689dae1f78f63b1e0248fe2320257194b32b7ad5ec55cb5b5db0b306df7f98ad461d5532c746e70eeb3ff21ee8323727082c11377f9c6dbc0046b5f6574536d5d51e53a14fe0e6c5fecaeae7a7e77a54584929c22c0aca650df844e44a70ded452daf4298dc3b4d8f9ccaeb87ea60d1d6278043b8acbaa422fe4f34d5418dab70939174199cb88df700118ef5a3414e6a3e2b6d5aa335921a34aedb82c79beba50f51781ac57cbde66d29913c3af684ccb3195a2fd8c1dd408deba437860d4bd153c46910b71b27f380e188798f4557878ce7f5d819d908e87ed2780962a44cf995bfff52c6a03f3a2f6c040cfacddd88701d798dacde65deba699f219300aeb1ba5dff93860cec0cf1928aa142fc37802cda7ab77cbce0553234693301814c33d58977b23bbcaccdf2c256bd767c72220d1fb3253a74d06aeb42064103cd484fa2213222bb5047a2946ff08ab5e633afbf212fd877c58d2befb2584452f1adba2077b2fe2d55fa478616f253c1726dd95d0206cb4061fc70f9913ee7e03418ecde22edf472d11a97c43f187a37e0302ca1c27a7e294518ec4450d2c290a6dea98f7bfa4ad97b31e0476bc5f50d9762d252fc6f424d5d71aed0e3e312907"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e6ed1626def9bddb31affc00475965065edd90f4c13c053306e36f2b7c42793a","proof":"7ed0bfe0a09d7a1f0835784dc6cfaf5509bd47f843b59c740cf9b475cdc7bb621e2f3542fbba89bb717f7c889cf3f2f2974246ba497cb62bef3307b043fa1b1be2a88a27c3343892200ba0cd563ed940539f3c3876a035a5b640d6c30c431e7c5c5f42efe677087259c06c4cdb56b91f7abd95b0b6cc8a0f544fadf19686840a2bfc9d24b8a755d7285db09670f1fb3222061f0128f2e5ce09225dcd4cd19e036ded994ad8510a47d5925ceb4b1a357105279bd2865d91f6c93e39be8b41ab03c8b0441db3e4449ea46ebf97cb486f871a9d6436edae2417dc3b538589c0460dde2ad7ae39ff4a4bce23aa939a058c210aa73576d3ac809da623de116494c6356687979897ae8e07aba0cad1c5998179e7defac781158a0dbd3b0854968a6415cc213f1087cd7c9af3daed26c99684e85e55aaccb68cfbaae139eec913894877c2f5c5c20ad5c2721d28cca7bbd9a18da7865ad5fcdfdb2d711be52cce9d576f6c8135cd6c9558ba0842936eb725a7d1faa440cbb7651a7211b144c55a4ed34124ac9a08422f6981efacdacf637ddf1e74a4baa5b4615156f45cce7b6c3a687bfaa661fb5163b136a1648d19737bd7d72cf920a78bc0e92ff310da6fc603d426926ecc62b6171193ca38e9387025bbfbdbdf646960e25ed8aca91098979a93347c721b8e8177c4e2144fc444fee0a766e3143cd01251dc382e5f3de234d4f94cfcfde9d2c5ffef281469b292ea63932dbcfc49b1d46dbc5f28c7b0d20820f57e1ab4952217f02ea2d647d110623600938fcdb8129e3d3b83a2770765cc807205bc9c622b369574443c8bd78e74efb2cb492d9cb1e5316d9e0e3fe560e365114a761af541867648784afd38a496923823b8e89a20310b905fa3a1286b28dcf20d4bd88bf061b4fbd44ff77041d3842cb2171be3e4ddb1ca0aeaf30215e6044700"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1e4b55df6967d63f5824a73a48de90dbddf056b442c496fee054fd3afe1b5c6d","proof":"1a3740efb9e520cc45916a19bb4f2d76926578df423c6c164213dd771ac919269ca0f373597adfb3a1959ebf6605108a799480c83775ad9cbef9dc7b5da2120ea2b4a753be7770f4bd85cdc800bf0adcbd037306c68981e3e135ebad8cfe6447560bc981c31284b262a195dbffc45fb454e3c411473697daea69f1fc7a9da4767d252856c046b0c3a023799ced5a2e32f10f2ec4c8b86eaab577c7e9ec7dda013f8cda1b6b87c23ff28d8ba212e002095efd90de48bc98a14eec32687075ce0523db1e8c8192e3fc2180c1cbcc0bd9fdf81989422793c2aaade28278cfb99c0320d4f47374535d86434425e0e7cb8d1ad6d99b7f2bd63068e39836a0d857987f3ac132bb1d342271607ac4900e7ed7512ee4323860d1460dd4c82bd8c2aa6812082c0bb3d6947fceb8ede9cc292f7073507e7c14bd6e37a9f067428b4f152c22d02385ea200a5d32619812997e5cba13ea7618a6d12338e1e9fc9f68ded8c4183c0ed9ff534d3307049fcff0f8dc9d19b3945684a810fbf391e2060de7e3302e7ea3434b80ed22783606b6ea0db91fa6b2a8e17dea188bf889fa7d73ff63ba5498ee9ef8ed70d166b2eaf6cb526e963764074e11c2366b2a242f5e8478f5c03d0c59683d5d26de3547ddeb0468623bd1f0f781f7b3eed0acdc13d3a7e2924a5c2a64c15a8f1886c34adb19c97b627783a9cdf57fce0a3f9e53dd93b32f235005a0d48b3b1d04a6b64d317de929d455d4beb852503d3c7b14b106f30632f44661f28e88430ea54d1896bf013c19cf780169be59391e58f8d94048983b1af570343c451fcbf996ea6ecab7acad2aa4a2e5863feb932f6071bf2ad861b6eb4fe26ddee352c880a979dc58a73d03b521fdc10f1da26883d0db121a6927d955bfd6051d051a7d2098ddb891ad5a9099e1da679c6ecec20b4ec8ec4b14f1d02a28e405"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"525543efa026d3e06721437b9ef026cd01cd10b187c4bcbda30a4289fa2bad30","proof":"d43dfdab00c791342872bc012ec9f1c72194822e3ff1838e09803567fa765717c0d54000475f9d40fc97c34cdad642f57a40f6abeef0d8b365285fa1f3c2d441eef4226ec6c87d8074e911ace6af31b723da7b436e142954bd831401fd3f807dc4fac31328b61764413118c4d652272a3c8281008d51f2db425bb49f20f32d19120d4d362a3e71876569f7176e4b8246ea01dbfdc028553f3b935a39c9b3420423ff365adc9db6ccdeb06193e8f93ac736bfd478c47e287855355837207a7e05ac9cf51b16c8d7386de3a63a895ffba292f10c7f603c035fa13960bef4a2a30b3003d535ce3366624fb369f4961bd75c7a69ffd933148552b30ff3d53e7b9460b866ef4e8faaa043bf8caf4b12d03795a30b782cb5c8257d6fb8f916cc9c140f94d748589829346e35007b735ecfdb08f563a567b0a05d99dd0145b0b2bbe13eb4fade70a131e5333a6fa69126437fe44dfc2698cf385e3024ac21c7e1754150ecf7774e1d62fd76aee4bea41fec67103d0edacbffa190bf806288cb78654a5d266bacc2b55014a3798f549b38723b3d5de8c32a5655e8342662cf685474db1c940b8b4b3873d7ae21ce4d853a72e22418f297ba8b0ed4531bbf1e2ca10ae8213cf30d017e59e59083ff146e4db853a744977dacb808ccb581c3a53264ef5f215c8a07d7494bb028641a427b1fabb9867e62a2fcefee8b40bbe65f149ff5e1423e3ad075d19c3297ca52453a41eb01c53590737e29086a378eae7c5e7e2ac35dce2b32887efedf2ca0dead58446d555ed6ddaa38099f0c2ff0a53ef6f4bb424baef2cc7792ca84246cb278a6c7f48d8023469d85553a0f0135d8bcaea71a9276764de181790b3915953bef9be082e8ba187947b718923a6aa341a9e58683d70a4ec879de4e9f611518d3cdae70df22cef9009c4417107b7570a1953cc6ed9400"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"76185c49fbdf054cb9badb87760b0b6abecbc4bf7aac4dc753d1491c3bc0313f","proof":"72f348f6adaea565ab50695133a3cc50f660a74517e3695c650ca259b2909e2090d3d68a2c77eae18ede7cad5daede5f476da20dbfbedc2c2e113d77e3367a0d8689fca8f6c2ef4a6bb8e5c770c19fbd995a23ecc360dacc423cef2a0621d064ba98abc34c20b35b317f7678da485f5c490839e2f229397487fc71776547a31092487a36ce0bc7e72c75aad33a1b8c23813bd34cfbd8b2e813dc39ff2e7c6e091440e248424bcb2bbeec462afefd34bc21ed66d8f0c7e398d9de006302911a09a3b41d93cf50ba154764a05e495e6fdb921b4c097d9f5bedbeeab2b796231401a69fb972e33967f6352cfc28c40df70eb1ac2356a08b4ce1650725f5d1b4fa69849ba2699571b596dfce08a422b440b3ede0e9ca222060bea04d501efaf63e397e9ce78eacd9a0db8937f339cce4c08862e8cf15b55f86f9ffe4151c4593bb0d7c440fd8536e9853ad11249c4a785dfe1a4cfe45f3788e27b1efd87a5dbccf3a84d0085fd980737a3640d0510f46e57da1a30b2f4bd196fe77610f1e19154e22fad55f747332ea804e42c1fc3977295bdf7cfef321088cb4ec221a3282c93d027e94e3d1f4587037613535f62ca82ba3b7de6681b655fec9af598bbc1d652911a6e274a59863dbe3e2cc3002f097863c2a70a4110234985dc36bca8b9251f4061a4801c9a09b676ec329e33cbbf0201b25104cff9c3e709e62c95fba62cfb87aee340ba1d74a721f8467418260acbba2b43acd9d02d217f298b12424aab50531de0ca601078b6cc7fe6c1e6721f749353f42b1cdfff6d463919f18acddbdd32f9a042268d421982fad208d4831e4ffb18b42224c72e7687f286404ef96ef904966ab3eef051959ec3998c179d7930e1e8d1740510045d824298bddad8fc2060a75a3dd7f0ac3bd4764b9964702a4edb689d4d416a27996d53873a827ce0aa300"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2ab077ab40546d892b13462c7211cfd8f3faece362962878485fbea54b4d4512","proof":"508cbe85dc819ad8fbcedb8f06a26c0ba17a87654b2695fbe6ed5e62f9617d7db44364c63f1678fda4b5bf160412cb68e152f969c90c808ed68a36bb9402e8540cdc4b3a440cad09d2de2cf8f172124711e5274a26064898ae3f8472342878510a80028eaa13b93ffc1becfc4b91a84d23b5389f16eaeba756e86d17ba833a7469cd47f996e87f334ab0cd38463af79220257f95f3ffc8ef25cb48451925670ba3da7f7d75ba47c1af0ffd28c6d021b45d11d00f3b81ebe22649bd587626b602958636d227b8d8248a744d8fe8ed48bb236687e449df31d78112c01ca7549c0582a204fde57a94789ea335fc86fcb72ab7db24b916bf98fae06a3f631f5bc56bb491dbcd723364b1ac322e43dc4ce086c4df16f8e9e5e90ab28acd5afd93b11a885f9dac499a87218c7e01fd101f43d30afd226585f386acf36f374d0b7b565ace262f9cfb5de6cd338fd5bf32c0c56c49d221df3596e537d04b4efc62135427fe00dd64e51d7c8f854ce682d05493a1c9b43e66bfa8fa7ee3d895b0fa07c9482e291f29854143992695a1a0c044d7cedc40f5d32cb0a7843b13e614c4d54b08de98ef58732ab2f6dc1a919ea868ffc74af12610678c0b79bc89c9fc4aff8338be5efb712c03f635095e88c7167de3b31c31e4b6d13ae2e050b646b6dedd037e009f77dedae4d662bd238dcccde759ad271c71e3db8fe389a6fdde098cdc854310219aa3d11e822b6829266d3e7c118771f55edd91ccaa5226defa8acc43841f36251af273ccb6bdfb74400201cfb89df3f1db4428b44ae6d7d6de02e1235f5062ec9df1a382223776ffcdc03a4757edb838f74b43f00ff6f0d6e9f8b68f6820228dd09b70d60aa846f6b774da70895cec6b8571ed5fbe441822dda0f33bec0139c404c4bf8271e837adb04e745eb040207beb63eb1f57cc60134ee290e31c08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"780bd3c2cab58194f858a2bb1439173c16b434dcd5b07496a19c2f7e9f4a2c43","proof":"323b6639605385cf3fc03b9b124d35d4cac8ee2c42c45542e4aa499cc91e760d4ca1b63bc63499046ec1d8ef4fb79cf37737da9165df31ebd0c256cf9647d7582876978a5fd31a1c90402270c51b3a6be7cb4d48fd67590ca8db54bdf0150706dc75b9ebd1a200c7cb4d8d2ef909a8baa56a1eaf4a0bcf9dca848f134af8506191615eb9075fd4d695e4c5e15a40c9f78ed1d054a1c58233662895880af487037d7313411e751a95bfd57af79e9beca0863cd5c7c56e24007f402c7d0b746d0a5fc2898a65e027372c143570b782eece9c212fd9d313a3453cfccb36b2056f074892d49f90e8fca5865b3f32c9e37e31ae3fe13e586ebcd33fb941c57d76e20ef0e2015b378b68dad3ee116a4430ff27ff926a42ece68e018cb68f69c2317907be513e455bc577ac53f744b97bd93ad1bef6a3db65fe27d3317f266e3caf8a7fd2a5a7c4ed6830fdafa6d35e5d8cd96f388028adff49bbdd82a56b37a24da8601e3f086cdc6553aa507ce1395f3f8dd50ffa9e6b1ca3d70be5eb30285254d51b4c6b4897c7a09edcaca364781d625da81bb7c9cfa16871c45a26590c4146e668e0575fa5087d19b81a140a6a05ba93e11fb329686b1de0538b500ce9ae6450603eecf39c4fc8985a2feccb73e4f419ca5ee43195e166e1139d175ab5fc25a7030ec181b3d0927561ef2ddf1b023cce5c84d3f63c808f160aa30a8b7712c7ae11f0b2710e4efccdd76847732ac238ca340427e7fcd647ed34b0fc107adaa62e6c56371a31774c6b879c866c4fca9801c524f0b9ad8a558011e4a2e725bf1a8064029c7276b429367f99543e82cb44c9b172aa302716b22bdf98ebcede96bb02061f69688332c3d50ff0cb1eefa345d75f1fd99724f4fe053e7c312595a025920fc08d934eeab95c764a769eff1ff110a7aa644bc34cd38ec7f16c8556cd0a2905"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"46fa8530d9ebdbdfa348f5b7ab850ab10663b05678bbb78a72c3f86d74d02428","proof":"a86a6980ce6db16b3f7712754256233bc6707717e777122c2dff5f7f46b4297c84318cfb4b2a68f4a8c161276d589c4a9d92fd5e08973a4999b4fea8db8fe56ade4b5c66a3bd41766a4552a808c81432ff28956df54a6865f950e73424e2ec6dda50b936be64b07ff3586d96813c8994500a578316c4682dc65a58dc4d659e11dd3d3da7de488da20f32d4306b649b9d94dfba7311308014f2d76a2e42d2f60e60c262b3eddf81ac7a9915456dff99d29c9511960678b4a9090efc4c4b43f10af5f16b7e7ec885e2220e721c0bf8b415b4a54b13d6f6b0d97284ae002ea82c011087ccae80e551fe04f27cd89a89ceffae90ed808be41610e5f5d04100427704b0e56d28bf6e1bfa53862aee529ffa0a80f0d7fb1da5ade313ce2fc76088ba7c608c5919639fdf046af495570c77b40317742b0ed2ec171300be24f3d142a86a1659759ab052f1d9830ca162f364e58ea4a76ff60f25b7a66df3a6e3301c480e36a6323a8aa5968f06bdabbf6ccdf696a0558d99d00a8fd97ee089ceee30be0350c78c6f6584a0ea8c335d7e1a102127a983a2b16227b081a44414b0fd3df031aa41735aacc768ff1b88550baea64ecc4b3974bb5082e291353691861db9e42788204de613cfe1319f3b1a37d540873484b95a63f92cb43f663b1f5af1db306ebcb1fe933c7549652835320fb32fdcfcb15f99a153d5606a4d140130048ef4618461f3e60d93958319430baee8f3e0c8f9d800208f6f79372c225a8c7a36576ffec7a6485812c6b26693c783fc29f08af277b2bc25579b20dd88c59ecb54d81fd4dd3533617cdf8d6cfbb5a214c57cfaa721a4e94b569d3716279fce8d5a2837c5ff33a0548a995a75eef941bbc6c3638d1612aa421042a4f71abfe7dc7db90685f0e47e442e901bceb9480f7fa62bdc5e74514f5035386ddaa01848995ff30c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3a2f78de51c7527b704f5ff175a74045e0acca9b63c73398e0f29a2391d8150b","proof":"088109add4cdf65df9dedde4c51f224f3f1baf7b69d8961fa555328ea37c8b76a69c640e0a98fb21dc7790bbaa737892e71acd1b7207b3ab1d991a3bb214fd7fda9d66d48f1cd94868604c5ee881e6a1f050c266ff01624152a7931b7a52ee287c992d9d3874e0c3a07e90ab25a0d3a076c676ea10424d1753158523f702f96256fe7ff1c0c697b2ddae1342a8a10a3e37df8987d6bda70c021cdbb6942f8300b61cbaea2fc34ef712c05b01acb8cbbcc6fa25787f1999c4cbce9126c56989082a39efdcf90d1bc33d63a835b061f7483434983b15b738990c8cd5e2d55163028ebe2c9c197d4384a7843087baae83e938e9ebe1e08c4a90010b0733ba130e1122552c46024565a50e73cde666316940f5a79a4e991e6106e2e13dac043ee50bc2092525ad9e4c0134ec24459e8ebddb16ce03b5105d51b8c0499a0303dcc3494a940d105bff9389fa613fe5358a9f92e93cf91901e8e79cde2270d1ab92304e38a1cfe267886b4efae46bb45bf00419ef516f14e9a89eb4fb75648ca40afa303ade11571520292162de1a52a6e533f5793d50e57aba19d9c2c3ff7cc69fa05d522608e3eb45b33044d698109a27dc0944f40ae60354f749169d806b085de42192d6e4e6ed3a16cb7b4888a5068da2ebc3f064a718c26e4d8aef2e0f1f640b278692af1952f4eedca8fbbfdab51a910b533a1103df67b9ab731f04d0f76c5507d86b32064db64129ee6e6d0571c4803e9e729e96385b5c51e10006841003703dc07baeb059875637065969aac6efa57132641699a1f8893735a702f9abce2d5e4630300b0ea757ea4c368ad2393debd7875b5b9a58fec9f379a8fc546508270aa017807a470794c056827df8839cd8fadc466f6b32a5b77dda88c54efdc46c03832d106a0c79a6365147cd6bad621d7f17e5fc3d93af0ea31c73f9dff90e6e04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"50a64cb90098d99279a3861254fc6c69668c98639d354f8d872d9121c2826b0e","proof":"40609a959778a60fc991168139f725540a44e27b604506e944cebfdcd25e5c2a28633bac5346e4f25d8273f4f490281635a892bce59ae9811e72efee0af926719a7c4122731d3886155b5e0d1344d39251a515b24ba10ca49de46fde68a7495534b9716b95c5d882eca09af0d79b2747c6fa936da7256a70663295be241dd1158c76bd6e96b36505e4e1ea524a5d14af83e35fb0ed4a45bc6e2a42da7d3aad05e863bd4f5b5fd5f8cae4b304cab69c642095afbbccd757eb96f4a0e93a5b8006b68bfedc6197115b513849c49a6e59f81ae04ac400e2a64b054e9b7e92b62604488564dfcc6fe8c51d97d5b66ed56ed0b912ef66e71f809e3cb7c2b47ddb374beeaa303fdc8a2275b1bc0970f366fb032283b37615cfd2cd37ca775fc327c34606aed4032d9eef6230c805457243a6bfac36ae221f965416d9c985a26725ac20de22785e09e288d88b4361a15ac5b2389b3990bd41f404652361c30bcec8841346eee7f9428d4a0b853152f40c5b1ca1395c126a6a07be941e060b1fec18e238dca6cdb93569d0a300520b059623a54ea5afd0a4d6544ba5946cb0f4365e2f12fce295386cfcce74521f84cc9779c378b464a7b5f8715945aaf175e8db0e593f9c829564f53ef7e31e668a8260b6231f67b6076d27bfe3fe5719a161e7bafe0496a65d143924e8cc538df4975b3b531eab23381935a1634560fe156df43e40793265b9d52a4e5e6dc2a51d7519ca9488abe936ef39448b10e2a9202629d2761e7a69669bb586409f83d5d578f3f3e18eacb47e409490be51ef96753ceafb1d5c5edb8d8cc2b3bc28e817cc1f707b70edb9d35d6ccbd6ca99c74c7ce3007eb008f98d4a8f0d3018b9ead5c5d2fd373a5c58679c68f14e8ff149e9661cab9dc307b5a06ec8212d6d649f872419440b07abfceb8d8fd53b5c16e9d17da5ce636200"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"969172ec2274675b905ab39c56366964a4f102b4cbbc0a68837d6075a302d864","proof":"ca631c087bd226324b4cb863a717a8e6d7023d6b7932c877f501fc7608827d24808792803081ea11855214869b0101ffc00f77ee4da9931d2bd5b397a140961bf4ce084138fa58035e19e39b55cbbcd8249a2ede30615858b9c298a3c5e69520241783e0990f8b39eb6abbc72410bbbdf4b034d1b7e257c864c65efb5a5917171b57359c87a062dcdb8df2ed275b6a9585f5c50b832246194458bca1f51f0405d7d26a09cdab0921260c3e38345d36bb00a45b49a7c5a6174338c6244a6a250f453ab430247ff50ce6ea87640adb9db3abc9603aed46c5dc48d108bb108e0c06c4c22b7821ec44ff94eb20bf31ae47d8019760ec06d12c815052685bfd618a16a4d4cbc7933de86bfc049c38f34879b62c07e08cc96eb532646aa3184cdf866bb205325796665fcdbcfef0d9284fd4c09dc32cfba7710f74c2a74deed7d7867c564b2db1d8f9f47b78a5932bb5ab40fbb4b7c06322f00abc250a177d1454f54f06d55e4f497c9d6cfd462a306d4aa6fee8a7762dcf071b3b9491f9e165b58409ba676ef90f08a87d7c806a33ad972c8f5a8235b8e552a31822842554ba76267672b419cd7e15f37ea77d4b761e0b325c864c96967c672e88f9176d874ceac122d4775d966998eaafb9896125616c26648b742c0db57c57b0b1e4ca7cd37a095f5e7846a8512b14f69806631e32c6bb4bd635e689b1181fc57644857b3225ee472631f6243b4fb78a95b9e7a0bc878c2694d9610f5ca9677ad8b17a5d4cc70674bc39bf1a6effb9bfdb18e9086a4129e40ab0ef953fdcd2b9d9710371374873031e2afb9668e267cc0890101d371014bb8ba16ff8efe177bbf566271534631c4e0289be9aabd38080900752061fb0477717c0918ac3ca88f2fe3a93a1e87da50fedf55701f4fcd8264917947f407796ceb6cb423f8e56f26a623feeb193f63d0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f0e6da35493f0cf3277b40964b82db44f86fbc7b260451ee3a264832f84b8102","proof":"0cedb8d0a37d55acecfce703cd4f4bf0a72abaae8c64cc73abc20c33afefa873faa2a0ecfb39c89ad148541e668fefa2795f030ad31e62d300f216b05949106a42691ea9c1d1c07a45ef0bdae702827a0a8b7248e9b6b8d58191c644ed83a94a90e224af6ebc5ce57bcb1c1df98387bd27c25d2e1645b54d4d64aa71af9c953ece117379f557dab672e25a8e6b70ab211bf6349d7d4b77f94f049447d282e204b1c9c34c46d0044aeeb860f5d5f42f65c2e826b5ff95a29b27cd1b6d88151903606b533667e5aba6ab462577932ede54289d91438d07e08021dec5e7a830e809a47f991fa4e3692dc612570dab5485981682051d13e4709d20fe4e42ebcdc150ea4898152b2554d6c0b71fb874f5180cbca6f57886e88d5754121db601e9716c105a1a64039c15f6b3051c02a67965c32d453ca37935d7646b79c6bf0058104112dbf2c8c539f8bad5e671f305effaf8ea1f44816b10b16d8a1d31a32b00b4273e36ccbe82e7256d609cc73213ae335731dfbe800765308605338b4938335b131612008776d7423140487901c20b079645dca733485431ac13ccb85b05c8b9670065519407e20f1c0b0815464e13bfbe59eb7d56ddcc8c1a8c421b7d6c8faf586ccfb2af4b05beb7113b8341799c0f0ef53500ff6337a448229edb3aa2a37852a84c77a91e47414273fca49590f286ebf5af024e83828246419794e2a327fe094ec50620523f01f8f5b7e380f34315e8cb5ce6667c5d83427e2a1993ddde5b6bb43aaadc3ccedd7178eab7cfe14f74871db6b05724da317e95d85cf81849ba78a6f66da8a7179014a443dd077cb2e34ed2fbc8379f40c7db616ef639a90b351c3ec0c3c01347dfa8dbdd58363893e3bd9a6da5f97055101cfbd2297fc1bbae06eacb370235dc13d5dd135a48e434d4f9548dbe2f6c57b8e68d298ed4f95bf500"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"668b2c8cf57a8e61526e47b2a6d16cae8fb311269304f82ade3daf38b86ae606","proof":"ec3cad8da70c674dbf4c4b6bde539ac76ebab74c8f5318234ba1bd1730519828d83a0ed6300ea6659b75851827b1ffdffa73c2759a17238da94bbe0c8ea2135f6e070ad1d83b6438c70e64b2825d507c559c79dd18518072b624d0113f567842280e0733c06307484372b3c236f361abfd34dc4b93ca741026237df7517bb34757fffb131fabf591ddfa2a47efca8f07069f2b56ab7819d459f306fae8d2580f4c62dee0b07485f8b2aa3d5b3dafa9ed95295e5282d2a9a4f1f9bc8a6112f70852f4c77f3f47f2d0ffb73324802f5d4455bf8d22b7c9093415a3199596760304a4bd4aca14eef96269488826187f645568b73c6efcd4886dedf4f5c30c529c64de4199be4098ce1ee811ca03b49752909b595aca8c3b4e10791e0ae64d79e64bb60063829847300ae99454d8ba0f52dccb3e934346c9284c5db8d91c8ab79c4dee1d5a90b27f1a3131cabd61b026b07bc2261047b44c7a3140d461a6a54b3254fec4c6dda364757bbb0a6a38a4a57049eff4b37c3ae81244f61f3992e8104775a4db649202b96d7369abc9d8ee6e88c44206017c4accd905b600ff5616c9fc7d86d6bc044dde50998464e05c733394ae27299eef2d14f896944d163dd69a3e3cbac9d4d690a81131f790f0d39493ab3b66a97f73b17bda8211e7158e70324a2fde0506781bd86214e038f627273c5157298eaea083148e7f0c54c5c20f57a407d445a473895109059b3198406953c33c7b6fc30cb252e0ee071a7a418911973ce49f705371702b093bde2abbe369d888c8a487b6d46d8a018984a92aa94e844ad8d378cbfefe094bd04f76395a559f64dee9c3776c2bf528954f1ef4658c66336d0407e02d33716205f084757e1d623680debb2cc0d6885aa7454ab0db082e08f0ba8ca3bfa502ffa82db82134662c6951eac5e54f0506d4905fee70ad79a507"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cc09de06f25abf7d1c0159dc64e0850b261cc8901359474756bcb22510b7101e","proof":"569b8faf3186aa7b141dae957dc1226708ac993c39563eb5d913467a70d59116262cd44bb675fdc21ebcee91c5312d805a98fa810624f63c8c5f7141a4ed46586a263dca847e6d37fa18941ef4e1991b8abf974ddcf6834f0723286c1cb8bb42c4cd21b370abb46cef4c0e8c86c42427bdb2c239725d63631b07bc500a67da657a4e04683007b8f77e968004cf508de8f9acd9ba8c01c3197a2a8c52cf6e910169febde0ac70b3e8e1ea6efceec68c90b0400c9684fab3bc40da7e338fc5900ce800bfc558aa97531a0138bdcd48dcce18bfc85356188bfc0c4ac973ae93f70ab07df4f835dcd171c5839bc9e00c54bc1537c8b8cab9b1aef33cb0e3d473d52e8e2ada2d4c24f08412573284cc94b2b2a03371d3b64ed868142028b1139d7949eaf90457249f60700aa565e28ad734de8bef55d717dbf176e8383dace03b412c40a3da848dd3b45a5ae8cd9fad86b04b092b1e4a31df8be7ab52f0436ca7f02bfa4776884c6352dbdb4b3437a8c7d23fb0c391332ffbf6f87ed0252d942a6d29808c2226e624cad9dacf6a224b92ee42ff4c1e3845bd241bc9bba1eae37374732e8cdf45f26d7b5c25869991a99856be54f31f90a1801c17e77bb5c05220ae733abf67854926ba671b1b776c8cfe7f0bee76b8f9175a9f1314a3183bde8dc12a8865a4942af7f596728610328d7c43b53b2e63c78c95ecbce2c7972ab3e14d48343c9b694ac3742d82f125c06192ed3c15ced42797ba05913aa929e26431057f5a3736fc1d8b3eae0824de6d777485d3e479561db21885b70e542b524ea0c1328ab322a2fb0fa20d9a625238d21209276f1c740c43f36e5cd005f88c7017ff4903be45863d8663a566b72b9d719a8ba380f9dbc225ab1ae8abfceab90134390856355b5c9184992bd00ee86ed1056ed21aeac63a8ba9e0bba102a6124e5d7d02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"30ec31e90eabde58c9a4eeaec368f2d0dacaed9a5cb5a87c0ec17d2212c4744c","proof":"b20a1a5fb88f45ff8d974ce21d9d2a47eedc8340e67c766d82ce480e58193c1a0441b9f4633790bfac5aee29d634951d80422279c80e82537156aad746537a1bce2a2ef829eb9a6f5a96451664f3934ae49eede960c12f3ed25ce016d4c0fa4ea86320604155229c7a3dcb265833601f0243c69a567199666d4c493490e74668cf5c314c82dd36ad60a694c20eb58bd08d65225db8f3f237f50bf0335a957f04a1cf997db86d25da1d8aa166b843feb09b3b4677bcd6a0864ba71509c9cf090786951c9cb6efdd40cb63d0e47ce2d07d0b3faaa6ab1effe0a9b50eda0a37b50a1c5a3858a7102f407fc68e128f13313c3565ebe36c077c11a560795cc7210a333c0e310f35c9bb5ea0d2bc716b7454db57af575c7449b9e2ecc707d0cd518b61be1e2377337a0602157983f8d2b450f5f2bda981ce7395bf34ce87c51b27fe61d6ae06cb886cd407f9111ee990633cefac641f9d67b7bafe538f53f66f3e465244484e6bff861771a9b35165711a11f74ad47850917c2e0f4bba5aa23c144b39fc90480d54ac62a34dd2f12e1c577877035ed61743b425519871d6db453ee12848faea6c20b0551b41b9be634fbfce42c77c7024d289d55148b4bcc92a44555fe0c59a625c66d5d481b1583bec3cf126fcc9d8e30499e99dccbcd4dc4d011d4494807bffcb5ee5561a49a8e495d9abd7a63774fc2893905f8336941dde1c8369844fb23c4587b6fdbf94466ddee7c6b5c17fdf001bdfb771516565c1e606d7679045e7269354ed58349c65c041a327020ab15ebcadcc4cb6a23773b44d82d67c0ac2cc76e5dfdf3508080e973c81eb81c959d2b8f8f532e6a74bbb666f02f47644ec04a1586dbbc4665954b2bd8b5e543623ea63070848bfae1a48d8a9fe4e01247b442851a3b26e30f2e4b88dd5b1999227cac11146c0de587c80994d0ed300"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"56a8580e963ba118f22d032e0c3ac42e64f95c077b3dd4e6a1551478135ed977","proof":"94ebb98d049d56635f41161bace8833f7e644c6ae5c95d6ce985393bdc9b852240c2f5d6f2a0313cfe7da313188bcf860f5e45e9503cd404376e9fcc9fbfd93f44551e2a349e9519d0e197ab3298d33c544e712c329d182f650b4bdc13450c537ca89f3f38737d38b4d3135a4fe0e135f31422e77d1c5edad022af136be1ce32fd31b5ea498f39ccddbf2f469f8b84bcc53c6178a80d1bf52c82ca9eafb4a508b6f10683f8fde5835be446f4d488b25267742c0d920992ef7dfcaebef31fa707013eeb300fa8b27d794c57693e4da9fccf93b7fc04d71eed59fa649aafeeae026806d2d332fd751b5b8de90093562c7387824746d26f917d5793b1d4102b0e2a2e676f4f651b8a93e0de57e17eb1d960d871708b29b9f5b58852a1e65eda481c7e0333797eafd4612c2d9fafbb3e2b5c8aa39ac6d7051178e1453b4eebec6962329276d7cd59362030e2237e0b4f06424ebb21825f500dd42a6826b3cd2fa6262a318d1c901049d6c68acc6636319369a0b0e4be3a436fd88dd7bb55b1db7205201f201aaf716b325cb2bac93e02e358b1f7bc1c81b7e00aeae907cfc875dd566641c340cf2faca832726481b518d9dc44f6fd4f5645efa0ca14a5fa3480537012bdb878c3aa9dc1fd08b8c411650a61390296d70e0588c0c6af44851d9d0e011c46225693d83aac18b4155560f40eb662e0ee4eab125f902d4003b68fd94c616e09312edad4839c940c76e8c4e8ba03df4f9c9c2eb39dc9d0042849a41a1d5b042392825201c435dcdff7b3c143c4fd04f7ab3cd3add363e8d1a3c14ddab10452e2c75499d58092fa45b92ca0d22a51a3233da68eb2142c24855a6600b0a86bb6b1dab89823031220c090261a838979935ec76b7e23486df564790652792d02eb880940329620eabc5bab554e26a9361fc30bbade935134d0c5cab382125101"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"401fa1a38b022ecf530fac705f328d0140300efae1fd4ca9ff6948cdf39a1f36","proof":"9092c82c5bb4f997f85ff6406bbd70d251a5bed016c478ebcd0eeb66b70a49154a99affc0504820607d9fa29f88dfc9542cd87ea7bb26e1bc9dffb7a99e6472fa65da66b8121ecf5aff62993bbabfc961bc71850464527e99f29fa3860484d50a4d82e878d5e8d550b3587957d91924f41db9dff8135a02356051653606b3a129a0867050d2db6cca0bebcaf401e9517210483d8f05b7e0e6fc8a3fea8b17a064493da72d47ef3f12a4ea02b7471253cee72ae3b4c71f7e9614f16e802687502795b76f05d52a7809b042a9e98fdfa68ac9537fe61ce3825978ffaf4db741d0024c925b041848de8a356d28fd1830091b22d6891d6db6512179bbaf4f22ba468eebafd7e32e962c5381643621cd28353f6c3f7cd5101c61bc830c7a8a1100a5784b275efa05375126a59f037d4f59a375c3361c1f7bc57edb5932793d5eb09784c79427e3775bdde6930f126c58726d0c2e7a1146acefee1c6b942cc02f64e1c06f765f58b989d3632e1133b82e40d5a6abfe8eeaab30d541fed2dc46dc70b5fb4bed43dfc2e1b2a3b5d9a7fc4f8935639421beec5e5bfe0ad0682a045ebf450a02b01be445fb8e0812f5df2a31787062e81c3997fa6757f54ad2acd5f963a4dfa503277ed34322f717fa974063ebce226d4b46adce1dd3db993611d456f4070ceff2c4a5e2a355f1f6aa67dfbb07e4f7e483857474cbe25a15b31dc447b8b34c879ed616eef29225ad2cf97a3967f575c263b8d0b50af48dbd8e36dfadb400828a298f7cabbb8ea31f76ab9274e7ecd80f9b75355a4b2a22cdea7d7fef9c6468a35ea30bc04b27c92163e9636c3e1357a67af1de7381d0cbdd0371748cd4d2464d4333ebe86e5f495543f559b3da7a3f7b6aa451bf873fff3d5009523581b02654eae66120ea5738e76a90648a41cb2d1f6f7cbeefbb900f01c206e7d40d503"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"54494ad5a597826d8b58a9b2399c6d717947fce4d196150a91d666782d14a953","proof":"60823d20736b3056db723945067bd50f152de9afdcb6192595a29f9a1343b42fd0d6b30859e9dffc9f7229b45095d21a02281fe0cd953058c4189b1414b6c615fe99f50543d4fbd34a52f3c812d3cd124606117d4f395564f90989cfc2ba2e179a96273dbdf84db037f1a9683f75553ef33e1e3dbbc501b527c99e9f10872413652586d8cbfdae808b32b944b5f9151f14f2634e4b40574ac297a6723e9c510fec930db309655c3059fe5a9909a447bd7955140b7fa6ee1351e6df2aa603ef066f871883444d39a0379e7020aee7ceae7d77949c5d5c8d4a3760ff6d607ebd0ec0a9f9b66c853e8771c79d74022fbb5fd965c0a9bf0e397f42fd067278e60c100a2f84f3571d95c176e8de3762f241e98606004ee2f73b8cedbfe7abdfac2c062e1ed991aaeccb99c1fcb3223db7c65a1f973de53eb37c0b490a40ac51096e34104c82e2a4dce3c44156937686663f75b493a063d21352effdb59138907070628a0ed404e7633c905e524c2db5366559b485176b19ac06d53d7669a41429e963429fc1f95554d3719312b72782a1f7f28dec0e2f93098fda177f5a0e70f4c9000e9e23b7da6b981101a6a7c303ea860a367facb5ff43c7b57004837f66857b3d1a489a924db62c8eae3b6d1e92f23633ecd9f103e50b605a40fa5e692fec46066e38efe2822812aecd18a30101cb006ee4ea635978328705a2a556d27efee6706ed2618a4dc656f558030761291d64e7fbb4a568157ce94ef50454988dfedd69dc8fac0971889479ab9e089546f801f60e2c0fd5daae01083a9519e4839132534071cb2cc7a1baadd1617080b5ca87d52af1c54b864d91c7c09825f04405dc268437ab66ebd128e41e94066e811106e1e7e5c0801e766d334d7f44b0d893fd07102baebd10caf34696e938aa6dc8c3701eb82810772a5dd8119cc3ce52a8790b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"22a4de4052db5e4477569184f239638817292e041eca3490adbce0cf41c6fe03","proof":"7494138eb0aaffba49c539ffabc777ef3b0ac72c141bb4ed850f7e08b6b119151e2517ece7b85b4a37539c47f9f7d159fc35e7d41237e8571cd605585052792470671303af144e07dd15f65be0ccc34fe70ce51f5e777aaaf3a7bf131a49d73eecd5b4d4453acf30fa63424240c2f015f6f8de9e951d56d5119bf20ddfdd116450b4e36291052170055bed160d81c5e64b620094581f44da0da6475cfc05f500ee3239e99b1fd56ae14abc8186c166d47fa9eafedeb3b0d1b83b3a48cc7e9709905e357c2320e29ddff367d71f341680dba5dd092622d647727dad3964b2a40892600ea4bfb23bc19d1dbf028226879fc173dac9bfd5edec996a001621de5f18da9d78791a7243e384643c25f5a93632e2f3862a0c25873de10a0ac2462184742c7f1c513da011097d4057075c706f2e2a0379ce5f16b0847d1e9c7cd3f53f277083bbda147fe9ee2121813f862072f12d7b800e57bfee33e75136315e072314162c7f9ea08513bffe935b2c176d4ef65296f24af970780b8f7ebb261f114a22744ded53a18f655ad44d0ed229adf1b1f4389efc5fd314bd4405ec96cb1c7d3500ac785c3e5e62d95e4afdb038a4e29d3f28b6272ed7cc9603c131401c7b0e15b28a1181cd61d9d13da2217dab68f346784f653c6c96033a24a246841e525d0ad8749ad345df4b843ae860fc2565e911aff4fea33f92612598b76620a6e5d534ec42b13324c99e3e732ceed4fdfcb93a56875eac3ab7bb752f4423f2efaa3d238eb397bd39d9d3159f21c0f51d0c2d594159c06b1d49a1b4da89e9f4186c3e55382a48c91c4811e9e290444204d44452a8da2a196995a4a20fe6432864fc672f2bb7ae859ebb237921d46ee35cc89ba47c911db1f563a60639dc260a56cba90c7edc17814293bdf6da6ecf9b4a7a751c0aefecdee80f8eb013d54ec36e9ca20f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"487829cfb51dcdd150ab453381763a12dd2271e4136013fa2a4ad59a4c569237","proof":"aa65f33fcef2c238f21c9bf8da1a24a4c9dec779026a915ef61163d93c0def70ec8e0b25542c1541875843a636a796377a9b62a7815f67a648f8f22ece03475398c9e15dca36041d43bddd90e14f8cfe22108044684b5c84f56b34487c9b985d0ed9359976366d76cb6cf26a4f37f79164929052e182a8c170422faa60dd1619e6230d68c445caa01615beeaf5d90ca214223a7402c155bf76e356d5840eab0b624d2f8865c40d3163da325c7d7ac2c2fe83269de90637eeeeebce064bd68c0d568f44c0e7bc49a148d879a237b38864dcec1b4196f02272387db550a1ae8002ac158a550e17eb6e0e4e36a043318c5da7127fae6915d6d7b863185a26eef62250674e38a96aa4f3556ffc7624157c2b212a6e454700946a0755770652350124faab88f5c21f219d3807f07f9058ffdd6a7b5b0e131683bb83d1c32c31ab932b3c208d8a5e9ceb14db59225e97e3532e8fbc45d4fd14c7803d52f8ae7f517e08504a84cbe92add0d275ad1697b76364c035484e66555abe3d51e00a529ad9e58be3e36e8e6f1735ddad59305586be82327fd8e505520d07068e3248131252a0848ddaecf505aaec122c255aaa9a3a27106d10847f53936c7304be1b04e0868773009be5f8d0c0c3149ca3d30536e20b783ab66ab7895b64cc6eeccb267e54f2472d5e329545a5898212b94efaba8402cf65a08abf9d7a99e361089fcddac964a6ada5846e08da4780ba0263c76cb653fd166f1c66da31ed4ff2d6efe0f9fc31730e14ead2f3f3ccf245a7ea5d5ef4ebc448a8fe430c6d9e66b9e428a02c85a7e5ebf6b95996661e8c5bbc8a9f46049f695eed8e49ab98c6a1e2f896ab29a7f471da87c7fcc831f82ca7c9d5acc2b33ef0c9e4607a0c0d578f581324dcbbbdd05b98338959198034adc4192ad6e2331778aace70e811702df127ca5e20445ec0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bcbf73bef11450d8f7e2df1a0ef85bd2293947ee006661b2226f6e4943499c7f","proof":"1eff311dbbe5b6478f5d7ba8e3a1beb43049db6c7cfbd594f224e1a5c1cbde32f85061d8090a9ec356ac4abb86cded6244567de04af277edbfa80528409fc21db8b4bfc989c9ef69df3c6115fb125cf1f2d36aa65651f8a3b922c20afdeb5b3136ec6a65c7390c06dcb6458bb632449ca8090c454318d0e134f56fee57f63e45a829aec239ecf5e4fa661475d65058c4993cd3e70ce91b0dc5d9daffeeecb70a2a52bd8bcb36301c3176c9007a3548c0ef0771ec080001823394ff535b1ddc0fe233e3ef5bbca37f1faaaa097875263828e5b90401b5fe6fda23c55200ab780c688007b12674bcaebea2054552a2e10e9106d57eb64bf3b3f1337b2f1d1aed770a4e0e0ebee4d6eb42209dacd67062dd30f3aca461f8505147028f09ab480a459a636c4a075487572f8ae96f71b92a4dc9060a1a280aac73bcc17773081ef049b80b8da92c6cdbc9b0c62e60cd0de248cac371a0a8899425f0fe37215e816707363cdcb4a98b4b9c686b99dea993f63f6d00c4b5fc30de8ca99dab8f2a4eca18a2e96f7b759e5fd8238ec1fa8da353c3306c4f096af5beb1eb539f6bf9af292cb655ec2efb82505a1b61b03c0aeceefa8c037edddba376319bf9a3ea5f24f94dd64c6ccdc53f53e06d34c9b5dc257ff76f551d6257c13b1c07dcb6e7ee066327b43380a3239e796277449ba3691c7a21760c978aa2510a5fbef243ab93b2d17bd8fdefc0fbb3492ffa8eeae2fcc7c4b0598ba9d9f357476134b28e02032bf71a0adb011bbbb356f878124bf2c213cb24f50dcc0e65109b772c70368f5eb7e460eceb572e2270bdf8a840413849de55ac3624b5dfd7017671fdbfbed00cfd866e12583fb9688a63340306a2fb76fb774316b685373a44f7a68654e6c56405810015e1c0f7536cfed15fff591a6e93c9e0af9d9a76251a50fd851a9099fff9ff01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"24c1fccf1d7f6f5b4b4838fbb82660b074a9ebb44a5876119b85ec373341af03","proof":"3ebf97585c269e269ac39422ade51185f37faf3cad698d5b91bfbe9b8dda7076c4f552591474ac4fb6cc6ab26aee95008bed23c504dfcb43be53ca11b27240055ae7e85698d14aaf907fcc0ba7df8680358023c7b06c67f8300c9ab34839fc70f02627afd4c5e15701d8f2c364bdcfc268aa39a7647cec57156e09a2ab5fbc1ca200165465fa3eeaadc9f7c7a32b014dd76b93bf872894469adcfcc502df9c0a8ede87769f92d074a93ddc2b66d3a386ce385f16581fe9d72cd535096fdeb60c52e939ef3301878ed323239c081757b0f0724fcc47394dd189a0418e0948550d0af8f3146287f20dba26f9b639a1ea5aa25d288aacd8207396b3d4f87cfbb26464dfb4b2f6b308f821b5c0f4849a32ec01583787b35e6f33e88e103ef201d72ac8ab2c97fbfd1c6b2d0ad4541a118491c0fa3bea1a30d4d577622a6318e7272baaf7ddf820b5bc73f5fd752cfcd6fe011619c1364d87c79860994f23652e5e1ddcd21ce98aa6d901fee722876c6fe607392be38b44ecb73675ec81e56c9ef33e06ebf65a29a4d7de0bee32c53b2d363764ac0d0ebf422caf96f0ff5ccc8b834068e05f6a5e083318ba61ee0d671cb03e50e315bccd7018492a0c23bcbab4e015020ce8532d9ca8a841aeeb073b649792711c9291ff80ef968403660bd9a5a759a873eb7ecdc188616cd9479da4c01def0931acc4ec0932002907bb3a662804410a1cd82c269ef0bb8aa4c6b9af3163a269f02d14a15f31c5654b4a98fb3941658481f0bc59f56920cc3be8f42e8904ff86261081b7bea6f0e9c0692c9d0bca5ddc615618f77d525e80aaf2f25f53125e98230230e9df451531cfbfc2db11b61dc8c02216b4b4d2b38198e5786231aeabe5dd076b44b60d2f4d15936af589ee0fe4fc2b4b3690b8ff2b8a5a47008ddf5181acc7857ddcb86e7176b402717bc303"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3a122d47f2e88d943152e28b388ecd81fe09ab899e2f8d3cb87db5c4a3c92f10","proof":"e06edcc1b8071db62932871eca32f35db6e695174143ed96cf54dda648589146a0fd244e7cd422c9ddf61d787a6a2dc0fb51dc22fcbf3641f8f810938d80010a002f2b88f3bf6fc955b9b0df18b95614ce28ae4c372de5a2d749ef9cc0bd804140a54ea3508bf44b6c2221377960765bd65b52a6970d13196f79a8999967bd38f4d1a60bb7b2d0ea3641a6ecb3b7b26edb2500e9b15cf369a8123a93bf13ed02878af03b247925e399522eecd2e0f5d08d875886c96ff5da60227cb062a34d0fa030c930e74dd88c78a375cd763266c7d3bb7f55ed48d42fee93a9f70725f8018ef348c1d27b820d5e12e921d4673a0b720ae9ebbb4f37fe97136a2c54e9f9038ae87e2ed5b8cb1743de312cce720511415734c300d0feef93ac4ac9b2bd0b41468bbca912244f732bc8e37474b99295efb4f2f5d63d35c7927ea462ae56d9585ae74814572b4ae0e06c492dc7b94ac476fe69265aa277c9be909ecc96d49c50e89ae758c6a3e1ea955cdb02c794949a4fc484bdcaae328ad857d1bd0fe0d90014e069748cb8e214250f91fae2c73795a8468f6d46f0e3854ce3fbabe962034948bf33c8b383840256bed3eba940eac3d20588d5a4069d8a6691e73e48e03a4a54ec6d9bbc5220987ba07dd3a614a703a26c9f7274193d0a4ae14fbd8a151556c2727d4b958e58353b3c7cb80c644556067d26bf066ae0ff0d8c665e0ddc466c0698a289d22be4e6d85dcce9384c4f9bf37272f3a90fce3ecde99f9ff9f2565fe44685f22d0d0328ead8b1b8174c0e44e39d6450ca5cd22cab554b4d5aa9a76dce55cb4076bc2f49a13a9dfbaea1988134b4161308eea14f5ed6939633bbca219e9607a2c610fd1b9f476d0b8a0bd99a971b1392dd0b323b37bcd2858acb940bcb20304852e72b612e328ef800572b564c3cbe8663e93d904961c475d0fbb005"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7e7337058a3b060ec593512cdccb581c87036bb0ce91248293f24bd4aca31215","proof":"f419303513e2bcb283518b871dc9f23c2f61b26f6edd861816dbb8f1a04c6e269217248ac7e61342dd3b5128a5b0bcc7739db6d960d7494c154277c15f256c4e2c8d91c52af9297f1255109a3ead480a2c32b6be5c719a06a4b8d6e19d87ba49cc1a373d28c845255c6382021650274c06dc3da17704030ccda0f991d929a005193b435c74f8c71f935de3e636cda55d559b075b72c65fdbebaea174472aba06d6413bd10175302a2c49326ea07b1dfcaba7a0b1bde212872a64f1c6e1394a002228ef87f09d833dda4abee003404db8b69c3739f40fbeae238c82459299f404f24a129b099061f96f4ec1a1ce0821d2c1102a5b4abf1bfab53ad6c73aba17727e100ee97e8e358e11f21f769e778b62a1f9770791d6cf8f12de96c176e4b96caa4b3c81097628de7b176723630dd31463e3e6db80640797d57b1e1ed1f3495072b4b4c1b4781be2ff7f364201abacb173d4830a0e3192aa4bc1c7917db6b707e2f8ee1b5ff9cb22950a438f44307b1c3d729a101dad4441b7933fcd0d2fde47104c188bbdb8d568ec5505369fd64b60552d7d58e02b050980bdeb35294c93466ce90a9d261db1628638ba820ec016bc3ae3f0d3ce82f5ab1a4faaa43610e174fe2c6551f1934cd64b84eb6680a6603a684b3aa42e4daab0159f9f8e6fe3737522c6102037207f634c581782938d53b6f3983b73ddd054bdfde4f6e184629a015a6000ff1a447e66f33b191eabc085bedcaa36eed7267d2da342004009ede7501676e3dc260c3da902b816022c72e03ab1031f428e33d93e8b91698691d31c06c08459ebe0d9e33df8fbcb60ccfeb1c07e1781c690490d72affbc1e1505f352dbd537f534524412276d6464ab0621fd2d87a3d0dbaae14ff37bd55a04403550654da67f55fd69744ec9c9476d2e358883ab09e6a827793c376cf2255a1129c0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ae6227210f46203e0948a1a1bbbad9dd42b65e661fae2accb3d45e7172554572","proof":"36b3400b677e6b6564ac200788b4ed9560e6509e2974088a981c73cdba75df5e560a7ced7b6502e3c262a73144336fd589c2b1cf77afc6bfc39b357adb99216dc0bd027524a5c571d4552778be4bb4c61a8a7630e8eaa619b97fc76f9cbd665d403b4c8239bbf7dd956492500e069cc8a9150bf1e814f0b309d01b2801708c752256d47e6b4882c384f63215ac0868ddb649302f643d24bccd2a77f406d20c0c7b5d8fce83163ae1cb222a3cc03441f54830bc84e77b532e927dda56205be706317571825e2e3a6cc2bae211e07c6bee7abfc3265f2b3ef678f929e17e705f0e7417d88318f36f5ec761948d2a98e5823b05e43593a180ee964503c23a8c651b488d5d113e43806f0da07ab4170a86b98c11c169bfc2287d983a5814c4ad745d246ae735fd4de1e16483a02a7da84dfabfd032a622c7cafdb9714dc4cde4d67f36fd2c8f888bfa61906caef327f04ce84507d74ba06aea34272ac649344faa0b806b49eb16c7b4b08c745b57e4c67ce9ca7400431010ba4690d2510aeda67239e46e98bc7c1999a405fff38ccc00e91d409f99d89fdd40053be194a5da87092a8a08e7e0823fadaff397166531188e8b5826e4b0dc9a55d1e69216ee3b6e8d3ef4d478dabcdd0ceb3ab4f59e1bc6369e3dd336a84ca278950f6f0814258af61b90436e9e9147e575d33ede78ab32479b7ae81d2bdbf88f8e8d8c2dc0b61bed470a3a5fbca2f10acd407420439048034aee0a5b9e2b2ecaeca703db0eab472809fcdaea8a0623e0d8e3eef5d7bfd3596dc3b02430d51d5ec33af761ad1264fe223e93a4b59601694de0229220dc16e39408749f4e4249b1f90e6150daf06e4126895499d323111b611ab124b5da19e482758e38f43cbded1ce9cf56b7f3871d09cc901a6578add10eae5ad34d079f1d9e4ad72a4650a75ff57d8e63927a09c603"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e04cc80214a9160cd684971a393cad949beb2eda223c5d863ca24bbf2f4dee70","proof":"02a0153b3d71f40f65eae82dc9b09ffce13b06947c76cb736d549e6fb351b6075c000b55f6f0fe8089caa864057c0de1d66b71f5e83a23e567eb913f421806525a9816719bf9a43fe360007219301e65feeff0f742f75feee972c205969e801e203101a51e3a11a77fda1a8cb4cf7cdf2fb1031cafc7abca0bf9618b672d1f6b0f48ee3bf2d05ded16866a3d7fbc01231654c16315d77ca139afbc7f09a8f90f6f58e46b513f2e705931f29e536e29d122acae4c78f4ed767f80a32d95eda2095e7dca4f5f9f70623490412dc653196d7f9085f8535bdf16d743b2f6a65667032457e46d753ddf96cdf09e0dfabec3f47e60aa590310ba2ccde055ef7a1d557f9c96c8c180940454f06b02dc49eb621773addedf62f7ecaadffd97fd99ce177738c30fd51073c205a1961d62084000cc27cbafdba3b70c1b707c4c89adc9f370d02deead105b133aafc80d45dbbe73b47aa408c34041a60f3f995edbe67f7f080c501b61939bb32ff978ef8da44531884b83ead500e0ae138738b716521e1c63a25fe100fb97220fe00f725f0089bd8e720070ed58ed1d658f1d78e1f9227a7e522ddf15ae3b45f42e577040888907bd4e92e3f3922ad44efdbbb34796ebe6578e6da12da6a8e5a97816a56f353ae2f95a20ebb9b6c06e158c034be6f77b2166fa0b7ec4df9fde85f5bee83bf8aeb47b133e91c75837f369ca37ae147f347d69001481741e9728cc30066060bc62c62b0fd2bcd0849a5620f3f537511e03c26eda8323e9a8dc00b19fe3121d0ce0c81624dd012eadc63df7b0d8eb692f3e2b22ea5d682ced0e2517559d54af1209250bbdbf788820961214db517ef525708312fe23fa96956702cb735a90b6a2e68f110ae6c1abc4d73de16ad0ffe7b9e83e0c835afe2e1d9d23d13b1c83b1711f97b9f34d199942cb4f7e2db14251971f1b0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"36e86613560690683ee8ba1e3fc6161657f6d27036fecd8c3f77babba1ecfb32","proof":"9e3a5dc16fc42eaa23db0954b95218826c03b61283557e25ca7562e043cf9158e8de69c5d3a8d781c4e6cfbf7287ce3cf6bbeaefaffef4b42179c793f935337d664fbbb63fa25c1ffe54826bb01a0bee731ed9683c85147770641b774d2a687d84c3d8733c68eb5f866c5d38abd82560b418083d9bb3dac071749b9693eb0c31c4e21f7c7440abbf887574ec7b63fbaa1da42eb0080d311b271fba13ed832a01f98ddd64177eb3a2ad3ba1a41fd37d3faa16b1aade2774c0cf5ff1b63ca02f079077b074e4a8e65b90341802ed2df28ef8333c0fd42802e128a427532997940a4a98efca6249519a1198fab35ec77db55c16ec8ef1abc8427e65e3e5a10ebc06d6ce7531db4ddf24735dc6257d7a9ebd945ac315a6e125e1c30c8bf7a7b056179004994c273bc02e7f5385f2202373f575bfa8702e60ae66c6adb42d6806e45f9cd6440021aa39631524ad734c6e396b4101f9f9d0bf4080faa108ebd25494396c89ea2ac0d419b75e2b96a1dd79d3419106140ab5edae535733b6424262b42922edd3f7ab050a9dd20addebef4312647ba400ed3cdf1900a966878f5169627d165443ff87eaf76f7646ec5e0040b66e1a484e2eeff7eaa52656f09e2578b4766a92dd477fc3fac8293716c7d5ba039c56520a46c1d9b02fc3e97ded55703c0a7e0d91124d410d804209bf7abf716cd61881db6d0f8fb9d7a10db3b25d889121de729dd7ab8af308017aa006d6e225c81d6d9d080662f9f0f994f18d0c4fce4fdcba46d92cb530f48b8d1114e7957ead7c389ba30f3adcbc3be11a0aa81b3825fcd59a4c1ee72c1596ddd5a07660f79874abdff46751f3120fa018cdac0b89247a6a7a6eb3cbee6745258bd08095df473c3410db2691d0a2c78d618dd9684d0c89e0f85426e4b26e790a2dbf6ae792ca125db5ee8ea745be012b109d7e15870a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"86d068120e736736fce8824f007b9f5bc1088d6f1e618e810bdcbf9f69bc3f44","proof":"f2e428500817d421244eea0856a13bf011e267c4d3981920d9e19c08332e6f67f06c585bb2ce0f4144002b3fe0308e36b7f02083c8d2a1e5f736ff85d24d3e6a9a9d9e811359736c602b34fb68f0e65538e24d8350b82fca040a8555ed0c491936b39b06fb54e1933551702c7e41f6f051e16859c9a5e09685e2b2d79c19603e9dceb0659efe29acd00a08279d4df99f17e5bb0c5fd33e30a71064095bffb207eec98241e5fb9c40426a92497eb87cb7f83686b047534dc3c242b784d1800f0849831b9a6fd6558999ee4c8f6d2422be761ff684a358877d43630904f9681f09e4044498ff4087cc50238e3f3ba993e3e9d43763127649bc4e3549d22f1bbb380ee6053ab04f782bb992e50e8ea975c6f71a59aff9f6a0a50ac3bbe150c4ea48088a3e3b3d6f2bf9703acb0af2150c54f454451830d66030c1e8425cb067c6656ecf40903f8697040b81508cae13fffb16da5a9823066ec365dcbd09f30ba64c4080dbc704c629deed7ea3129816f6ccca5b48c1e0ddfe47bff012afbb4baa74f8258bbe89ddce2062dbe313dd9e38c71dc3895f7c129eaf38620bb3ad73f4593a1e45e4cd867d9ac338cfb0f40630f7f909752fa3b60f5771c14caa3e8dcc70fe2df4baa8a813887cd0dfcad3310fd44925e440761a429d362e426e3427134dca0fd6b72bbcf8c6c667f531556671bf60c6da9c510ed827ad1109d7e1f17c14f8f6f1b957b7196d191b578b64368edddcab1a7b57f46036954cb967716b9f223287c0560d28d39ff773a56599e798c9302f91629b3fd7fc53345e5e8ee2140a3aeba2d12c3205fc22ccaefb6a3cd85e6e5b6517901ce2d91167b2f67ba7bd077bbe0cec1b1eac549f62ba2f18a79ca87f9e1e6b5b08a629f62d99b131af760c2315747e606032b373dc841334db810e01e9af0f9923c9d619a50eca4b320504"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ea884e7533062cac9b9b1b9ce31a285f8ad125c7f77bcd72c73b41bdbc5bce77","proof":"8c36e463aec495d4f2bdec98abbc3a737926b09a7d6dd25bd5b07cdcf22bdd735a1afe7179f43a3b83cb340843a143c95b4d5836f9de6820cf88e70468d8ff3e8e3b7e15660031625e92e12d0291cf7dff9d283a87ce6ebfe9e303c9e8a6a073e0dee2a1f4f4002bd8f1bad7322dceb48afdaf3e8171f8d8df3199ff3b68fe35dbab53926f78ad49ee9a4317c0f574d6bff5411e007d9c35d9b3d64b06239307ebe3591bab221b4e4fb09bbfc14bc20203b652156ecf590838dc0173b5e6e4027ee09c35715339dfea5ab8b406d2b121c5ec81b88e69ab90dda848d0859a360b60b61aca74c01b485fa87d9212e0ba86841da7efbccb6488ed96b3d91964b70cde2bb4ee2117ee630df98d72e2889e1e92acf0fb8b74b0950a6f8822bfcd675c628d6ab44a5390c51db28c31cf76ba3208ae5022bd8f7df8cfc4154109105d0a76cc9ba21d1f23dc6ecb1b1dbf23c1c15d135d38c657eb0adb2517c618e81f4626f2cd060e58869e5f688b48749af00b2bdd83b8b2879933c0f0733b9e834f0fa0edd163134a820bdfd8acb0a638b2f4b0d20dc5e7daf2b08f1729cfc59027216c942b57fda5c556f07c85215723ee173e93045d35bd75607039431540459c207eac9922dbc557afa8d667015ea7f6cc859c37886aead80b0198372f9968f451f0dbddc6e7eb18733e58fa1511e06ef83aec460eba717f9336b559352435ee4e16dea8e3cc3247dec8773b3b12e3609eb2ba0b6fc5d2257e607f88e62c52ae23489a320d19a2f4695313f29a7ed3b94ba3a119ce809fa49520bbc423fda1b51ea4ff1e4ec3aea7f2fbb261b97e200368ab9ede39a1c94ec8e0a78580d997e24f1b173efde605fc7ee5e6bf353144eee4a2a4f3ccf2dbc19a4f6925a24cd11c034ddbf5049ce51a76fc9baea6d056b4ff93a879c72053a1a433a8d9a7174b9e02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"92354a506c3a26f387782deb71db22ecacaea6385526fc64ea1fef45b103fc0a","proof":"a852dd40ed36e16040b255106a64185360e66404b9a360d2d75a245235a38e75540751cf7fd9765cb52b58c796d7c5b30ac9b7ccf3ad7d7706e7aa6e187468070e626f2f88e9eb3616cbf837cd387976ca7f423bda9a322c6ca8dd4c9cb632734684a57b195d7d8b3ba8f11ffd93f4d9290f311e3fcc4f2717dda60094649a0051e0c31080e31873d0f66dc9c2014fff08aa630c68bcde42b0bf0fd48d957c0c7b6c23823c6f503ad8011d886edffd5637678bb6a38b9d0dc1c081aad8fbe509334ca68986db9525f8a69cfecf7d992f2d99a3e44e414f41c871ba9d8d3f2904b0ac6958ed8baa70a2e6d5935de375deff522283f3a0b46b83f0a4f1dc42ee080299b0f1485ccb8975b02519f0a3e7e9b5401e1c8a5eb5b4248ef218d0613402c6d695563b07576746720aa5beabeca7b8b8b8674cda451e7f29e6cfb8eec161088dbf0fdb7aa91f4a6ff93057aff670216d30247103213272e7d88788c7db1e806c4587027f5ff4a4418de49650f9cf21b01827e5a6932f10e94dbe37504161d4a97b6feda7a44f75c31a42205e431bf71cc112d7597b2bd168d2134e63e347886082b93e128d090d32dcce7d00d17665f119a6614df266550581f4c8dd5e63c274c68e9cac6f8e32659d38dc7a331248ef9cd044e7aa275368693bc42e5c2e707c5447ee92c77a05e0493fe9c1b5b8513282ae7245cb8b8f68d8596df9fc29c4e406a95295dc5d7212e065d46246d951298e456dea36587dd363b8d8aea566444f33aa23d747b2ff6442b3bac5b356e2a61a734dff5a5391c2a7876e5ce67198302fc361d4b748d0aacb7f88efa8c618d6cb855cc4b4a69508831f48fd9563903eeb8398f96b1d6db7e539c9c7323c01bd1b6631ba11cc2e6997bbab70360bade98f9d47787d59029ef012078e34615bca6661024cf9689d738c838fd84000"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0e7a9efbd3934490a70874dd46d9f7a58a07bba9d158de638ae2cd7e1058e644","proof":"047fabde7bc3e9348fad8896271f0e4b0ea9ee5fc11d52a3fec26199c57b4870c0fa187296173e42b0ed985c9f4d66afa792040e30cd0fd43615d43e71463521a4e5b4b19188a37cc3a1e48531c4996d339a572a13ddf2766c8668ca9a76b46618391b2e161c3dc747c5b964b4d9a6e2b76b60524eb7a3769c70231bd36aaf0aec98d4d60558efc94c4e7e4c049ddca456c1ff42c2489c0fdb48fd08e0da1305c67da53bf000b917063c66a5c118263d0268fe123c07678101159015fe02570ed4f7ad7b3b4ca8b4359bccd722b0b611fa10d754934e3e1c1df973ea6a5d88004e7c823e355e6fe33ef03bc93450c86f81606f914edd69f82b95b9df8679b66bfc18904d09bfc56b1303cdddc3e6e67cdc2f9143e45ef84558c73af5034a416abc7c45c8c35806c1df01a21a6b69c0f110d41bac801c410d3176eeac7b77f31f76998ab27ded8b6eb0a8e2d0d4177b70936e8ac47733f54d34db2f551d3b1b39a4b6be4d5619c208aba87b5f3789a088f4058aa46935771f520be7f925c3a132e897b923b09d636b84dc9c291a08568895e233a710ae9cc10875f748f3d7690ce4b5cc85c9d61fd97b703429757ee766c4587c68285a55862e9283b07bc14d6fe622819dd36a1430d95568a5882c16a568202b39f90032611da786134dcf2e199c2beedf6d09863bbad803765c4c859cc05d8b3dc8cbe783b03ddfa092512f50a2c55692e2e66cfbdaad80933dc69da82376cee554b332d8b85a5f4941314f52e424d8881af754e42d0173dd2c8e78c520bed8e61c7d514611606ba53c9ed55fe26ba848d9a03be75693b7cf83d3c5e1893b72ea92c77056cd171738ba282b1f93ce51b2ead1ae5fdf3d313c6164aedc5e4ba2d7dc324535f542e8724047ca0b469fa3bab79409218448562bf33d9d490f4fe4c255d180bcbd266094590bf206"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9a14d7facc0966eebb8352f1772cf89350cf07351758613834833121e1e64232","proof":"08d5fd260df2c3c40ae26d30c0fa94a299598b9507dc89f690ef8d3e4327e84eb6b662a86805803128efc562f50f7a33d77f1b69a08bf187972eaed01f15480feaf33f632a81200103a2001b9a55039c85d05fb1d3e0a7a8d014842a6c7c7c205a6080331e4014fd383c733eefaee0c8911e5ea9ecb76d84606ff2c626468449c9436b30eb44cde722f2d8a23e79e0b6173868b4676358e362e3cf8162e3260780ff8c3d64692f0c7cb38ae86f9d6d10bf63dfcac5cea9fcbb9c082dc3064d0c40bb8523c87bb483dab2e1ad42cd98a1a252bf394dc25e17efcf9ec2be34de00802115403be4f4acdab51c90c12e5d44c425fe026e90aedec56b678de048bf63a0a7a2d4717b00106f3d0ffd6d9f1cccc5c3383fead7f3939614a7179e4a0f50bcdf4ce99331928d5610108c108d6c800dd383cdf5757d7b6dba581d151a1e29525f52eb1b5c342bead4def42b856ca2dcf0c408cbe0cff63eada218c846583a50cfb10dd5df6e9335db0550d924b4fc6f43cadd3848a2eba06b73978a05a945645c892d7514d4f2f8d82786d94479b6e821cb97045982162649d8d8cdf67a057025f0558f23efc6acb3f17fc3225506b510c38408615e66fc15d9f109f9470fc685a29243a49dfee3835da6ea4bb540a7d4f7015dcc7f7e0f20c14fd9c3200ef864ebb94ea14c70d31eeeb59027d8fbb8f5677b4789fca49bd5c12cd69dda5fca4ae655be4f6d6ff7a987aa4a6f0740db01eb131bb39a9f2b6b0e408b0eb50d4691b55fad0df932d6e338f722c2c1f33a2cfdc51222d234772c233ae7a75932d87a7af94bbc5ab8f83d09d811a64fb347ad67bc464b2ca431824161e0b3d97d54503b6b3f1dd11109201aefadf3d79ca0a1749d55c1b47eb0d5cfd1b8477e07de0eed646c08244d811ca1530927bc9105271cb4bef91880333fc7ed7f63ed09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3eb970d956b9021318a28b7a5790fa0599511f51206e7820054b5509d425f616","proof":"a64df12933794e74d5447d1ea49c3b5d7c229e524d75456b689c949f4761f15fc2b9b1816db54ff1c1c0d354554f100509924c2d0c43a1c479c7487db388df54221dbf2c3f45a1a70f1c0b026a3772b9f3967675f723195017a194fcc01a355c88f54c110b46a67c892b089dfc17aca9af0f3b0461f016d9837ddc3c4b9a106c6fae917d5ebaee9542e8ce68cb014e5bf607c61526eb5330ee080d594143280576f5bef82ec6af21f2d508f7a1a75bb2136b336e2fa206b73b83dc733c53fc04ebf14fab07ae1a887675622a449cd28207cba36d675087f98cf8993e731e370a06f69db9969154a820494ff49452a3ada84cde21de1f4bf938051474bdc0c830e6f01ea25e2e851056015183d19b97b2ff042d4423eb015bdd43f80637fbb501e8b8305d60c6aa3f6d9d8c5e848ece0876a48c670ebd5406d3fa8ca8abd16b632289c7ce31662a4cfdf41d86129a828b0e3c9d068187ef0b596020460ef64c13e4989c54fd3d82c7d7738d965d0c0600670a545afee756b2559d184043fabf4ae02de7252ed96aa69d12eddd3d391cc93546d8fa233ee7fea63dc92223995b57f48a633d0468a835229aed98009d45bca0bb9d73bfa5ea3521142646ec1e581b7ee7d185ca454309f7eb5684564190b4e272c894cf0160cdfc5918362dfd8973c0ed120c364a43284486534d4c637ac2a7874c8d86455208e61ba9065605564050534060a59cf88c6f5aae45c5da70397b67943fc41146084bf51871b0f6813082e9c84f56658381347e2a3c9d9e718da299bbc3e9c2bd0850eb61f864114c2fbc6286739d71554731c9117992f3b85f51e524918e043d40f44bfa3896a4804b2f8743fb9d4298270180f2d4312d4b7b620554d5d32e8cb658d4aced22589a0cd1a504e1f5917cf09f071b9971779a8336be39d5f96570d9d73284fdf950c10d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"263aa3d03b64e56b7ee2923f0067da8a6e9318235584b0ab3a2483b328ae7d20","proof":"9e299472ab724c0ac31c66ce4af97e7551bc0173a285c60518932fe7ca34720a9ab54a4e5be8097a10d322de635d582a5679f0f4e65ddcaaafe98a5d4c0bc87864cfa765493329eaadacbdb91db1a070d412ce4d03a69119eb2c016ebc6d8d4db6c0c77d6fcd7ae6d5f3b80a5be5d082a6e4a99cbdfb6657e15696e778df5478a209debca546a040e4c3233c39ad6b9bdbd1c96ecd8693e7243d5bf1f27a8407a734f9ee52a2daadecf31e64bdceed63968ad4ba9a88e2c84da66011bb4e270031296902be4302be908cbf82684440613a0ad763aa9c7cdee2ed1b670e854c0cb4004dd85077ce46042f51fd6cf464ae243e1af07a030f098a21fff303f76003d61ec47a980106a5ef2ecb9b5b9a5c89a58b010ff526a214f90fa0fadd6a0f4530edc12d6c655cca149c456d4e93aaa7a29eecd3681f23468cee505717bd921e7616cd38a8700ec14b3e249f64be70fb580171111b6171f88e901cb064ca7804cce1f7835fd68be5a7043263eae16097ff105e62e1470cbc6816b30930ce0e12aa1f8d4a0b43ae43242f4fe3fa526cf80ee6e232d2f5a55856a4dee802f7cd60221410871533311dbcb6ee277ec19b6b84607f20d844f9bee5d387269e291b6d90cd4249f142b66530d47a7a14ed76f9ede4d315ce7a1a3055230f178010645fa020c34300a3c44eba1e9c51c12001de10819a819649485c2e4f2f6bee32197be4a0b06a7d07cb14af87fc61488fb3ab4b48c863c08501ce94285d702e46ce60c0076e439a39d3cd62c3b8c909a79461ba3a2b8c62080308cf1c92671255176c628f2d8c1725cc91ea36fc4ae69c8c86a0fcda6fbdac000ad558664989ab6f0c220785888d138f1a3c37924e6ddc3fda3c965607d7c993b25146c778714219017b282904a7ca9b96cdeeb9c6b775f51c6b8afd004a3c03f1ddf4559c13506f01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8a914ecde46fd1dd966b21d7006c2861abcd88fa4a627dd3ec44d094b3f0ea05","proof":"e88d4d7e8b859f7dea3908ba130f6362fc9abdda51c65e89568647f5429f0f1f02fc09997a0150ed90ff8b241cc6a3e0b730d88f9045a46c6eb16d5070cbc10e825a07bc686653c47a68a5bb98f1a913ef681314c3a185b21040722877268f2f2471d4d64b3ea38155bcda80f49bdcc846f035eee9851359a42c536af7846c1a1446b94d0bd7c9523a94ac3472b9c903ca17b3efa15cca9d099df1d0169996014d3cb5516ad960d2577470942a723a5521fb0bbe232a24ed9184cd8017f6d00ebe6417ca36f63ccf35e24b6d7684f30a3b224f015952682856405cc5da89500d28be9fa53ddd1d5d1ac784ce4087797d6dc34c4e9fc1eb9b0228e70b3fc25a6b2e6871faa0a817fbaf90a0e596604a12b6ecb242d83c596f16f53abc9c62631850c7385c1a6a535f71fe341dc034facce1f1d7698896d80f59c778aa17f5e22cc0285bb236e75e9b846fcfd29ad049e282f7195b365b6d4711f184728c7214361addb788a622071b0cc75c909a01bef4f122d281c3703796063ab06ae83f1744dea036acb4053382fceaecd011c355ffb803b2f3543394db4b024a18390af333cee832f892ae3a8bb6718ee97784c0e44a6359db7d3355d6cd43cccf6571a70008365b9555f22578dfe543bb9e9a3beed59009af1b0731b518f9ce91d21ed406da9980c0f667195a99696e920d185e5663adcc7a7c2e10fabe148d7b3d8efb5accdfbd6ebf369265bac2a2b4a011e0d3a2b900f731bc088b4ec3d164813c0f2b2440c632412c0e67a3f7e5e503d8b2df793fa06fbb3410c75b81dcb2fb886118aed4e5d7b828ddb04d14e55276ec7cb12324b9315c92a10e85db66af675c8b25a1087aee7962a5aa743985a8661a7ec62f32e5b885f83d5ab1848e43fb49060726c4cea03dbef82d02796f14844c77e2d39adf0da14cd9bb4ced7fb4aac0620c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a2252634ca506d1d19f0c940146f7999539130fda14c3b4fd095b10110448c45","proof":"381cc69cb871c1efb4e8f28a60c0e8a502820f66062326dd6973ceb5c35e6f316c6d512682e2f0e10c5ff28090e9ef63e270d4ebff0787cbd1dcebf08dd8e21a74075c9c590fec607c4de8a222dc44f7ec4122321940c290c9b6ce3f4643d14f969c7d711bcf29996c1a28eca4b34df244dd3fa611a554b293c910144f9f6739e4691f27542d7154b13922feccc425a22d61dee86e1c6c4a87356f84e7522f0be12dedc8b1b6ec06501062cd791f3d35bc70863f11adcaee4e762a6e70aa6508d342c6d22e63763e6d9b15a1f6b45741a3cca2f0ccfc38966f87de6c2dd620093416131dbfb20ca6dad0f47b4aa89e43fcf9e2871ac87790733da11fd638991ebc202878bf84631da85f3362972f1e0ebaa101dc3f13aecc4774b236ecf0be321a1542805e4d3ec11712694a9f15916a4712328b0e9ce5dd87583dc852dc732980e7182b84bbf8b6bb79abc9e1f56de8d80502adb38febcba19422e27367d84ea0fa66cbf2d2a813824c20a1f9967ff0774015ad856fe8f78a3f60afd89e9a47821c2ab4a48d295063e4f9c9cc8556dcb426f7f31deacde29214013847235c25be5f9e825487360798aafb868f42de78f73f7617102ebfb6aec92596f5a3a11444f2cc25cba7d948e272098b0f237fbb2000ce5f1a59bc9ee38bb5872cd15a39f4392202b01cd735fce75e6573733da5dc8390b3674be90f28380b8f5253f84a04d5926fe857a992e8be66a3b4bd3031bb16a52d8b9e7e1b9d63a64e56707506ea16d4acffdf88be244f32722dc1cb356478e35f2c40c54f732e8a016017da69b8498d1414be875b5fe7be2955e0832b59626bb5570296a9f4926410a29d3051fd5998db364a95aed0bf1a5d4c7a59d2412afd4efe379b4953e1e89c7ee6a7013aeff169aff761fabf3ebf4d79c40a8028c32a575bc01c0ef1965d4b4c9a2800"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8c8d9dfc7862d7393fcc22cd81fe519d61af04a673260b61ec95457ace14f73b","proof":"8a8eed9196984187d6de165d8caf34651aba2db84965fe7d9729d204c8f1942cd80de142042ec20c843d8d60cd478ef8ee97b92c83ed8f137ed734960b06f371e23deca7e33c61dc40ad38c249cdf76c6a5faa6f72c57a91fb7b28d7f9ca7205eac373b39198c186d9c4cf0683cd1f2c074fc13b8e8c32732ba1033fde9936056f60f4c8fbdcf0cb946c799016e476f46d1864b614bf4af2c79ea8755284d9048da62166e6d217897d9512b3f25333c4eb37d9e5bf9007afbb26052aa08fac09569699fc003acd1ad82f0d3921390ae609d31704a82a0c7ec4e5f759f323a30f2a943a4afd557a1cd75e3a4323c9426402bbf30fdd705dcd068ca0f3d414247ae63875e1f261c22e5769c7a0e73e7cc5d710d92f16e48fb61176daeec6edd06928f8ff38900500a30b10a18cb497e0d1e1b29ea4f8cb51f1027de7ee7522540390cb7a9633d1445c2a3426f2ed1d9041574066cba45105692c81b09764dc122892735dcf44e8fd84b36c3b4dd98e2c8c86163ec74f6e0a21485f11008230e6020212f050e2ff4a0a24d9b244588b2a0146f10162b22c96569e35c8932068644cb26fd9143d02bd1c9dee763a90b584943cc76513bc0e0d83f014d4e4d09a470ce40546f6f46442e8214b9bba44a164ca21674381af7e9c39734d7aa54dae9374acb2ade646b490879bda5322c0273e2e44c9d96bec9ad606efed3e1905d2c46662e5016c2eb1704231e3257028eee31c8fb6a27e0fd85ffb73a89b4aa356f55c6c2aee29ba91043020c0163b045d378dd9b6da183b7cc5a64bce224718ea8c747a648dae020d7a4070a7731e03165e1b0bd4c4df09cdae16eb5f6cde5a567a7d2afdb96c076deb61f90b3d7882e729617e9f1ca979ec29f96a500b4c80ad4c0e681fac03a5ed4619a032691f39950ea5dbb054f2eaffc319e8fedb6276b7bf05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"52e15bc6df35fd45da4067145ecb7f107b8b9b0b56c12c812fd9a44c73444b64","proof":"48343ec7fcc8664340603edd2793171c9b5af65ee428081a033b49fb6972eb11080671fb4d1d31169aceae2585c14b128e44f97667d3cb20c91182e309c1d96b4aeeb87f186e9b342fa4d0f1ebf19aa2e3c39c4d0f16b533d8b3461693eca72784071d5ca07b03273b9375be0f0c32806f8a26c8a39b9dfe7e594fe288b0d255bf3017250efc27060eade00a850fe33bb6a00a4a7ce60779a940840a92dffd0cbb81237ee2dbce521c9ac06f9efa2acd1c63ede61f40bbdd2938e2ec2136390eb846cf47ff6b7dbe172bc17a7f0f678822670678fbdb856e67e4e9be95f8db0bea92338d93ab6aec7181ea98824084a29630ecdd523f31ae9b83afaf7ec2e155485a1c7ff1cd0b064752de40d6440a3b6b373e2f2b1662f868d9798eb75bcf51baddc7e4a0a455d902a4c44ea760369b6ac540e2c0b4988f9add38d3383b4b1d1a1ea11fb7ccdfb66871704c4311e27bda539d6f6db1844418727d09990c3805fe51ca1574ca8bb5f41e91d37e90dfad02c1f53098e735732e3c75a26926ef1d184145206918480332bd44872e89c0380a426dfebc6097ed1055cbaba3b4532e547fc12dfa242c46381a07e51b55a0a68afae6ba9c00ae668bee1ade22ee5b3176095f95aa3b4818daf9a1fa9062dcd55554aaf0c8265670e8a6c481505d6b4b3c0a6111c5416d0a3a7b5e21f9d76c3e01b31ead4fcc871095e5a9a65905fa6d50f1b521235ef9c15cd3cd0faeb88bba3e202abfc871c25b820f01c8bcf05965f2bd43d9e56cab4fecf72cded394c7bf2f436a0bcf4f19546d415deba0fcf1422e49cc8d3935387b3b27e0fa740168b20b3b68132c37ce8b476e4e26a574137b8d86773a19ed00a5db617990a08cab45d1815fbd2ca0d08ea7feebf2710bfe0aa937352a1b64c1bc756c270dc6d48f7eda5e6d123b2b5f26127ce0d7a7ed1e0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5822caf2b9aa9e3ea56e2aca1fdc137ab48d9179935de8efac93c3c10e3fb121","proof":"de70d3829437b39cd4ac6811b88fb77e9336c5283e505f43651d34c3c82bd947e020db2276647ae6464585be617181289ed28428cd31abc9192771b79d3d005bfcb9a0d7f9e8cbc438c464296e947a37cb024f94b528a2d0b703e411f1b95e726a7858a3adfb19f01b9b6ee2bb39116857176203be3cb636d25625c06f218f1279d1c305e514adca5d1e573b7b31326d2bc3ceae88ee1cd2e319bab4f7d1da06fa0642ce54c09bf63a637f26218dd36b18cad5efe484a05fe454ab06f0e2b30e992c0f71f89e8bf56e04c61c3f986398a70127e936bc045ea8bbc2a353b5d20df2e95261dfdaaef24ad986e88fc0be772a60778fe224d4d066906a8db2df20692ebc050e750904633c037a57f408bf0b2da2500f17e4fb7c0d1d8a020a042724666b71cbb20ce3f0b0b135327c46496d32166630db7c5dfb260038c70588425f025254a3eb69b2734b92860d4dccb5b11f4150e5acbb4645b7d96f329c3f5715dc2a0f9092fbbbd38fb175c29a4786195d2455fdc86e7f4857a6f1fe038aa94be0ac83ead99a9a102d91fe36fd086cd84e3d9db9b09ed954fc24ad58289f006058a24ab897478c889d2008e53696733270bc69c5a3e548f659334937d0b4595dbef1859175a8efcb31cea339bc8c5fe452542064ca0048c1fba885676eb9bd5ed23111b849f429ebfc1d62cc1fa8ff525ed30bb9ed097ae84f1250653e333e282487bf691473cdca272b6e2b3d1fadd99c383c21abbb472d9fd1ea52b456460ec4518d792c5014ffab8249c6c002cdc53388b41274805b6f9a0bf6933cc20111c20f23c4acfa58307f6e6909e75d0ff482677b7d7219dc2d9b191b6205225c76a2d83044e8653595d0fbdb8a21a94913b6bd95fa891e048cc60d941a9b99b303059ecff7dd3b89b6351f3975ddd8c19e7d553c3c2426a75bdecb1f0a715ee40a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a220d68a3cd4c7b0f6f641623bab3fc3a860d890baafab498c286b20a889a174","proof":"66947d5f99c64ebbfa28d37c5f51b9c0adf6111c8500b4d326f4a36a7cc243037c2e8faa71c4ca9f76aad3775f31737de81c50846d7dfdf02ba190be29fe9f3bb253840518d79ea5da506c96b0b9195d3b5f93c90380851a23e2a1a1f376dc6bdeefe27c1c34ea1ce6a9097d676058a3aabc525e3ec3652e9f7d1aef9ec80a597d9a321a627b836193a9e011d47807039d47842606873e780bf2b6a8d6136b02fff9e839193a9c8f39b838a5091432e7b640e6b4ebe5f548f79f150d17f222096d5b43314a3da0d451e4a6768f370c75ecc08ead69cea0a1e3f08f723c223d042ab7aaf50abc21004928064a3842880e52cb6ad3ad2e9be2296bbe9c13616a79b6e32cb7736c9f71fb7c5f986f74bddf6e6b256282ccb44ee68bdd1930e75332b0eaa203d9d4d82e22775e5090711aac089f87e0b8f66b57f062caa4998abd3f2ee2aa5dce258d343164a5303b7eca5cb0acd937fe3c320fd4b9b5c108e3ec48a0b1af8f37b1e54d4adea96b8da8b4d4b05ebe3f4e454df102108f5e658ca1023cc044f22f9903a433fcbfa8286ac7120fa31326cd23328c87f4a41964345611fe089911adf6b9b7b02f50a274e1f446e1fe44a2d570fb2aedb15b1cc7ce40043c8ba129200caef2da603e647cd4cd82e58275cab9a2256cb237de6be269181a64d3fa998dcc639379483f416dbc9fbd43fd9cc59a570c0c8a33cc53631b8957eafbba158db3c3eeeb58552fbfb418da2c90b8c2ea7196f3f456d4e2719baa0990d9e617c8eaaba22e47af368574f4f2dfef42c937041572c187503c4602903cce37c2a989651c6157184e043fa828f47bf322c9b40904ec9f703f732b6aac2c2d8113ace1b83d5a974bbc61dff43eae9cf76581006b150ca2f1ccc23ba52e0803032d841d195c1bfead8f2a8962cb0f2f152949a1728709d6ca73878b1ee303"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d42083bb4e664b75a8baa58bd7196c16f6084b6846250e0ed50df3d56c48227f","proof":"a6757906a1f7e352470d620d002a79c00860617cc0684454611894de7ffee12db8fd75f731a73b3c7a116f6705474e9b7d44c62872f740045d05d96aa51bf52fca8b65e0184c638e218b307ed675f831b5b3cb2d5cf75c9f5293ebea8f7a9634b6e7879acd0d2717ae9998c8140830d1a98be10e6523abeef85a8baf58119f0492947c8eef4683408162ff02aad81999458b427430092cad656a66289cd44b0dfa3128f6e0a05a23aa83f219e2f8a2f0e465b27635f176aa5e02f2b0313aa70202c6bfb7596cb98fa645f2ee621c181d5f1d05ad3b1ce070deebecfd3a22a9080a3f2f7017358aebc070ed56fcd573128d4a7dfe6521929850590263fb8b946c50dad79e6ed877065d15a019f24f39901864fc6bd4e7dd1538667bd7177e1c73fcb86964eab04d44d3365ad66839af86d727a556c2208dc91044a8f83c2c9677f0cdf8a5839c12fe5df7d4bb4bb02b7a3a6cb9ab323bee6ed2c283988ff8753fded6f241df8fc345c6c83d55e6a058c70beddd5e6d8bf4db68880b1ff6610c2e16f1835d6acfe3c6bbae65663e6839b6535dc83bada0a5495e454014dc3836540adabfcda48379b34c30e74fa98568a52e2dcc834334b498af88e4a3682261266ead3ee836b5e38c75784f868475cae96f80405e02dda3e512329d520503aa77227a297c4c4167989fe9015fbbb9a616be8a7dc468f1a7b4acd6f9781efd2d1286782a042df2f8031085d001cb24a6fdc515f6540c73d2b43678655bfcf0df6404bbf33ea2c14e4d01e88fffea98ab9801e5456ff8636f43d2838cc335610c38ea2cb93629ad558503426fef715c7e32321eb1f514f0f5d9671df5b01acfaf1dfe92d9cbc5c9490d915e45775e63e543a68fa97719deb48a6459c93933fa02073fcce8a95d6ef5067aca46c37bfd08973ec09c54956fbaa0df40478503dad605"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a0525806a5b3c10299afac023f507e2b15b38529f86a9aa8f01d4d258f430a26","proof":"6eb13b9a5a66e48978d6efa73dd33587eeacf0543c13d3bb98fcf42dea304840e02d6802a310998c40c8c4fc23310a43e16df9b6449d846647a5d780afeb610216af20fc4c41a9cd5cf1f370d227ed59feb46cec0eb71024f9550cf961127658861634c0262aa196f74a0d372d1aefbcfa4906e297da3d169afb36e32ba2ba1d86e48b8890c0f37d9696e9912b6f699f8d79b3f7fc86f8e2006b9d6e9fdc410667f038ed3fdb4c82a0d02b275fa82a711342be2267ea4128537907cecb2aac069c3de5a86aba1864df3eba9991894ca6060fb219e0c0a5781faaa66b2da69909e69cb0e6ecb6a3edf036459270c28dffb9bf3ea46e058196371c3609441e2d6066dab612ce61e5bf9443f59e2b100fc6913dd292891a2c3d9c6c48b60156e3095641d9e33d49963d3b9fa2710f836453656557a2cb228767459bed2db3a41d07d690b7ba5e3137a30097a218a37e6515e587e1c17247bc0b66609e7035aae26506106056c903daa9d124431fdd532b15f36babcf347d1bc4ad49338b187cf456b6b392e4c08b2add139c3a4124c981bbc4a78af223087ba527d299ac4ba63e7fe2dfc7393cfe2ab516ee3f48a394d3fe41ed105c1dbf21b43952fe6784f76f75c24b20344b70f57bac73ddbed4132b5ba0520cbad0b3b8db9790727a77b6dd5cec0f4a6876674388febb3df07a1cf60bd24817e27088a28df485d38fe357241aa65f14e7904a67c9828ae0ae5bf34d9f91298f977c1cb49d7ecd8ae9c38d4d0052c53657d37aa12362cbd2014dfd95ef0a51d5e443d96cea75e0c9666cf9f36d26867c83f6182cd7c71d9b348b8a406cc775dd994148188b63225ead9b58e5451ea97e6b8acefa6e3578998e6f94ce8014e7b6171fe1fc0bde8a07d3781eda030f074e7e55be2744f76e76f8ab393517794e1b159dd33d5c961965b5719ee701"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6425d182dc08bf576cac603d49ee90473de33ea690b9271dcb8f825992981332","proof":"468dcda6649e490c7a3ca0f4a53ad31e19f57f4dd2c5cff61f8eb68ecb381d3d782f6c3ad9de5a1efa7006bec1c8972914428408c45bb026ef3845f15e5f1a4ee2e4a1f9efcfd4a8e548b3b53766843e4d901c11424a30661fa12f727a77040720dfca61925117df980d105e94a7ee7b244b46d8c60d56a67b6eec3ab22a890a1fe4d7d47d6d501cfabb726a5a4236d6b045d3ac8cf52a757e34542e4ec6340faab15786899941cad2cb219057acde8900f9fd38a920c0b3e201e5d598548604478ab224a3c22d333d928c6e426ce18b7af4efebc9317592a6a150310a6c14066a0b37be9e77a13431aff0cda0ef9722ec5e7b6a8a22783035c90ce48495c9236024f6147856f2021b8ff38f1c6f944ca41059155020d9b01c594faeff816770600045a1e00105ea1bbaee005fdbd25b61cb70549d14990f375319dacb4e7c6714d351bfb36b13ad6cc4a2b27568aefe005d0aad18cb3608321996435543796eec029443f134205d070ca8054ec5f1548efa63872d754f96fd6f3a7d6709b37d34fb56fc71ca01a7e07e22b97b4aa376e16e184a9be185e251c851e8bf699e7f9e16450feef413d8f274eb89b99020051b06d695653ea73b5542532b712f6f1f30253d404efab8f49e80dbe40d732083fc7e45e670ec7d1106a50e6e969869201aba5530f937303f3e2f076a1cb1c2c45ed2e724be5a50b8f1f2bc40d75a5137860c7c1532f3c89f577481c8a63229da7ab5f18b3754dda822c31fcf73148c06bcb0f3af8f9932d8f1c23e17dcf5c676a4a08a420e743035ff6a0e1b31b7d83eec681e105c8011ee471f4e4a870420285bbe17814922217c5ddf84d03be98b64eb31ab3582a8f4b557aad16a81c87f4e64987e62f07e52dab3a4b97a22fde40bf23f7c2e98547a2e9547265f28e1c93db74d46e81dc3d68a8245d5159f4bcb0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"be10625bd8e0b0f3f20dff6ee38092db44e93962dad1843cd1172a9731476b7a","proof":"a2ac4209b0a55635c54a795abee9e5fe04f97494c4c221f8dcdb6793dd1d2a17ae8dd44a8e25a8959b02aed3a0c5e46c745734e5958d36c1e1b5440e33b8e91cf2ed7bc4cb496b9e26faf1c61222aa8613c230402dd919d4870116524d4e757d4630ed77f59d599d5de8f167a00bc0c5396dbc0ea80a3286d91aeece34aed82acd23f73b081bc811dbacbf941110642dfa2355308cd4a5d428e9b5a5d2fe8301a0b2c8bf227cd0f7edcab42ccf0606d4c5c2973aa8320eb5ef125a34f5cecc02299e40e2201588979a79de7b9e7b3f0d3022548f8bfe793846ef823446d72d08d603598974d7d11ed7d54450bf56c27b3ca46b4c382f6db46690822238c6f63f3a4c24981f292dc2fec02cfe50cd7e1c01679a5d434ba9127987b9749dfc8d6c40a1e43266f23fd4331165f88d3e740b6836de7d51701f2889b790f3655643483c0b67970cfd01d7d4d3220f58faaca1fb47988cecd2732ddf358073e5a6b46af414e215c31057c4891ba75bc142dfc08064d5af9cae2f4337dcb19c205c4240fafd95764e5f4624133019ce53c0d87e8cef34d56d280456c3988b9743707d5c36d026f0ff8f5afb8c187190c65714d4805b18f90b4defd84f7167aba65af57caa3174d347fa3718affe06dd90d0a44938a7ddc59bcbfa06f8c6ec4de0c1fc255819419805a37cc177e9f1c402b4bdf1e82d4dc2984ca950648c9aa62fd79350fc89fb71c37ecc6bfbf392ecec07eba8a9ee921f48769999a34ef6b10328f24dd8d0be4ee3ec645b08bfae5868aa9359fb4117c1ccd2968d1d61b7b5c7c61f44faf2cfc1943c01a5855af0f8e1a7d55dbd2a7af6566fcada0d38bef655c44957f45fe54db6bb8a2aeeaf7073f161766eb4b8335163ac89036227c4d9a9830300422c2f12f06f82cac6df1283aa44aed66887db643365378199e504460bdb9f05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d4386f19e72bef17e037c299e4f30b334e7c2702a359ea8eaf385a2f14318a70","proof":"28b9315d6738ddb691aff1090a4771623de9a932e72e4f6224f780b22db4d3504aeab7f30dedcf05288b57d0732de7e07846fc1c01a20930c2925f0324390c170a5f70e87c1b82fcb4340194640f5ebde4c801724df2fe37ace829e7d53b16637267134dc928f8697fd753c9f4764bc70b6180451ef1782c6a18b9b0173583507226485de2a0cf6bcd32b210bbeb4af3be80d8b489369324c5cb5b4185169d0a0e37e92df6ec7a7a02ccb890da386148307e34a35a646405a90a233adf1275011098973f7323090dfabadfa8d542d1c9bcee7ced0d643131847d7711b4a05008c6ff663aa992d1ac04c25413963fdd4a1b8114ddd1842089210ee73cf0bae56e8c0f35b2ea0341411120def6222a5d30926a08d435e21f987348b7f76155bf3f90a0996c939ac03c66ac50832afa415cfd00976890f279b10f9ad414f53fc003e2c19b6b9988215930b0ba0b531d1fb3007a5ffd5d960c33708b73ad96bd286ebc77e40ba21f579474b3bfa1707bb367c2bf357cf09fb29206fc65231d9d2a0bb66aed18d193b9f233f9f5ef8b3988edc1cf85ad0ca2a2e2abb8dc93c6de376f788885d2e04d079c6c25220f584c3e3847d097345cb4eef57ba385fc41355d6430ce3a237320d3dc388a2c0e0ce818426215a9bf6cd95a3096073dace2dc125edc2ae7405570abccedc534471f95f820565817a907e3cbdc3c582538fb2f941488219826597c2d7d6f68c4f6a871ce40085cf0a99fb71d58e63fe79a5c7ec91ac017cac62c2489f5f4bced4356afcf590c24dc9c1c6c81bfa1ab272303b5a87c1a855844a52e1d4abd5d00b9b5807e6bf0681e19d744ec915f73ce544241e57558fb41d5e7f6855e3ba7a52a5edf4d59338dcee4d97d333e98e2756feb83040ed2f4c2c74180867de0a39ae2c4536456688fc36da723563dccbd8850a97f6d00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d27a6a840e2667a11c0a6f03f0fe3a330dde472481572f79117d215f69f51049","proof":"2afca4a38e83b1b458f0d22b0aed6bac63511a334d9a26040e205aec09dcb96cdcb0a7acbe475830ec1ea0600802ca2d1834e6cbf0d86227d791b6b981386b0df0063a0549984de95842ce1ef764e60f838dd1373c0d210197f839ab157e314fbe89dfda3448d1cbf51b5bd4039991cb67edc916aee3ea711c28f0253218bb6c6c6449e887594739a8401d3867ecd7429bb81e0322286603da748ac10b717e085c3b60b00691a2970273561c54f86a459168c855581eab831933c2a515de260c96539b23fb07fa19d48028bc95fb3456369375eb8a272101b0b650e397177501bc0ae21ebd501cd9b2ee4a198c7e0f24f98674d3feb1a7d2e2f84e4b8b9d9c3ca0f9c78f0388c791c66ee576913eba1a02e7f756b684238554b0a259d103f21522d6dac5ee639b88df95680523aa8b0014507e740db45145b31ab01d4ff184602e69ce941a00621604ee323cdbed6bac62453ce2249aab12833f5f4bd4393510b63b1a09c93ecdad39051e10c776b2b41d1f478b3e5b8b8b1000c171f88684628c7273e6c8f8ea8890434438297cdc47e3d72de46a2ecfd8e4d14a7c94c8d0540eb5f7e651c55f1cf903cf0f9e6ceaa3880822dca0764abf28ae7f879f878200b67aab4373a83fa63d8e0b3e9357564c26377353bab283021994bed0532f301d26cd688cbadc718af200fae1365ae685c3f79d3a78306d5efaee719c5854b376e606014ab6b287325a067783f5a0b033fd2c4c46e4ce16be2ed3bd92fe81fa4d5498815ff5e51895517497c84f64743021b4edd11bf6229620097dff53ac5a3d24c7e1e3904ac5a424f0d75af6024ee9294380f065e5e6740f258d75bb5b2a3c10235e018984f2848a56205947931ae23353dcbf43830f61450f17c04947db052052f385206dc683f98c0a14633d558ffa2c66da3b5a216401b128b739ef9504"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3c40f325b135b5f35dac1f78852c534ab1ae4b06e6d489547c556235a5e24d5c","proof":"3a4dc46b41eb3737ec6c570e677890181112bf6bc0db984c162675d76f147e39bc46da459ff08dbf682d83533516aaffdabe85662046dd9f9aaed0f8c90268537cb73367c0ad2d29bc30308709aa5d88bdd8b92f8ed2247236ab7725228ad81f4478efff7b5afbd7e25da89d30fd488a1e4f54380a31c472eebf2a0d1cdb1c27ad9a8e29b3a5bc7828349b6dee3f01e3044940773cff7fa10eb5edff38039a07538bdb197d992c3e687c28ea0f5172a66be1ad695248376ebbacfa359307b80eb5cfd38be99727996c660c687d2929254cfb69a5fa3a8b142a6614f3b4125e0b5ae7e08e562b274dac03115588bda0ad42bec41056598bfdb22a37ab41d7704f109fa5a2688a79d72ba50c2f400a2be4aa6824a9b32e1b3702913fee64cb0a59ba63d5f150ad875d2d6860b5d5fc6f0f826aa8b170be45b6c94f8a99bdb338029cc14a4d78f6165755c31f65921c635baf47e4cfec8c5f30b81985f849449e008c943097b952d1fa01d5965831d13f7cec3e980ffa53510a334b72df07b941406aa4b02fdfa1dbde09aa76f0cc6eb380e68e415829bca384590761c5a65a091ca8ce8e83639a2012e8353749cedd363327f2cf6c952aecaa5ad452890176a47db28e72685fc3ee784eb55c018091674bca09320e54c42f8bbf30e69030431e162c598c98658fdf0271b1df076f5ef4462fa672d091cbc3fda2f46616bd7f947216603a90f46215487183a8873153ca2dbef9d923fa1a8bfd385b6f0c4faabf34d04168bed5bbe55bc1920c5354d2f1086a24c778e4697a44f3dac9dc8c2eeb5268afbe426a32359cc3988ff401fdd191f71d8104066a1628842e9d71c8780960e79c8b287d4c8c52870d227e392c43701a753720356126ea1cadf8698e58970596718ee1cd6da77c510604238ae1ce144d6a5858a3abb31ce07ceedcaa2d5c07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6e2c941caa14431f9e140c5b2b7ddfd96d5564884fabc142b9333b7b06120650","proof":"561ee6c1d87fdce4dcdbf580b0de1e9d79a39940aaeb1094e175f88d842e136ed23bc10e28aa2ab592006e8394f42237961e2adc15e08c39f36c14179020c579be3f7b5feca71398aacaa87a3e5b01ddcffa35e63b808fffdbc3a494d6c21f6b74748c53a5927bc94d4bd9e29541a8f30f25ad0ca85feb1fe60ebe937dc03c3fff32a2d69ed410a0fc2cd5f60e1a93808175f0a0dbad66572efcaa620490e40eb7178089bd9ae5ccccbbae9fb90b0082128f768531bd4ba8f9461397ced6ae0b2cb9d58c00a9fd635889819d7e66032c40f51eac70d69856d57f715fe8d031044e76b6755d188b2d848283cd306bd8348e5eb99265e5bbb0c36b3e9be22e353a30a5a4eaf050386da439c95d2925803ffdc16b6fcc1926668354ded9974dcc792aab1466077f3ed49cef50e4b8f6f3433de03ff926e32cd54fd3cd6e18e8517f0425c8fb9c243642b79c4cc7711f2f4332b45ea67f7ab79ec02541eda92e6327d022a62430eb950af9eedc1b6dbd21dc0784b20108cda10e6d65aa5c79537b1436d0583095ef48d1e69e1f4745bc73b82788480ed04e8823e6b471851e9aa1617e232c8344effa02ba14a819003d834f8fc0202cc4d2f74b4f62d4678db73d10ce79fb178c8a57b76b9fe3125b41a290b69204fd41d0b33746a9daff80f19878d2f3259b8348e4e8c305a455eab389cb308b1664e430ecdfb832160442080a490e58300517f36675ace9e7e3157216aeb9263e4344018a1bdb31eacc4504ab255e5637fe735413103680c17afa124a163ca65b0ec8ee9ba4d3ae67b57e99dc52a0fe5e100ac17b2ffcb80f6721c01fa502cc9343632bbdb0a9c88b11a84b29599543a0b5a272f26ffd4956e94f453536be7dbd3ae86590f4b1fd5d759bc18b0d6c0029817ac6cdd85e2e4979239b0e8f2a180963c140c06eea4907fed012a00a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f6d25ae826ec01cd38c5b097e6d54f6f6561f665e36d71c39d8f840c8421382f","proof":"6a1e7dcb4a38ba4d7cc8ae68e5fa0b89993203a3e82b90106d54ab4ce37ab5777e1d1b66323ba87dd901a81304ac2f283de0fe60d2348fa19c631cb85292f04ebccf6b8638d722704e31a65a70bad926edc121e98bc98e03f8cc701066ca431efa2e4b4601dfb127bdcb38af192e4f428145ec99baab64d3182a2afd7848bb7e7920a1a0e1cb211b973c4f3dce75d79df2aeceb55b5808275e792854d0fbdc036ae3b321a9b5702e1abfbed09adbfdc0561d4c8c6471b074ccc58596a590da097fe8ee2fa114427ff5c7247b7ea5f0af47b8748f874ca62bb385b2c84cfb1f03906c738da89078982faff27aa2d0dcbddae451930dfe099caceb545b2e2bc0050c05441b5f211ae9e4b4acc62e5096a0a0a5b2bd933d88b901ab8dc81e88c526a0bdb4077923bc4ffc1d499785d09d5aa0c9afd357f8730e3a522dd99f99ad2fd68f0f706acdad751b197ead63fa43b6bd3babd9e572f7befe49ca2d349e5061841af69d02e9a8adee2bf15f6049423dd1a4a1e9241386f53eab7e5eb196ed11d61aa6122296fd517c837d7c16fb3179a53c54927f99b3cbd8554d711f7b5b71f4484c6abb2d12e859dbb2b045832c21f47606f56e3e64ed71dad69bd77a05531ca1e450f6f4944e7821a318e49b505a62f07fe8269eb7dc0d3b30070d471e0c7e870963ec9a6296d748fb7e9d9a06122c9696bf7d98924d8a807bfae70172760cdffb29d6d03a89184513af203d37bdcbe94a9297ec59f088b447b9d0fbfe02c4eff288f2f57f34f7e2667c82044a422ca2de4770d95d7b2e80e055c10ce762c6a7009e4d869ceda4e2de808752f780f3690a43f0877587805ae824dfc55e4c548d97298dffa7c382c3221ab4707da9e2ce0675e476b0590bf457b28bed140eb25c7025305bf16bdddb26d9a6ee811988292b1dbf605cb63f6e8bf0874c4d0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2853a62857986816d06317ba09892ae5ceb18f04dbdd7223120890227c7fbe40","proof":"ac8e4e65cd2ff0b7cea1c3d38354243759349c0e29fddfc0d6a9594b54184d3800a47293a5a89c597c689de8e6d2d7119a596fb2a614b7e89d0465b3d90cf5791400b15dd98ad5c723ce9eda05664f319ec99c267171e9590362d7fd496f2c4044e1ab3cce2bc9c3040086d49aedb7212565503cbbdd33e30a70c7a2bf6c266b665f30b4cac5a4d36a79452af8f1493fbb4dc55f7fb3cd21c16a5b0d45113501cd92050c812a1ee3ce86432999f50d478da20f48b0cf472759f223ed81077308d3c5adc8341d5a897eda0d269bfc595f9744c425a78837dea7ef495d44ddc1044ec01501ff2852a767001ba21068b6e2719e69208d7c35058808b43d4a84d766a01175f8656419aea6b07fb216d774ab087d3d17033af5ef669b39a4584f4133160ec896c90244413e2d082519674c41a29f88b1a2e83903031fe4bb44f2c9758a93ce8a22112997978cb22e29cc6963394d7df3a5b3acc65c21ffcb382b26770ec7291bc27d385932dfc89a9e1189d8dfe75f417e291eae4569858b864aa72ea6f576a6c7ffc3c9ac09f57c4e4edcd3d282da6d5e1593c8aec45529f2e4592670f294ee7ba52c6b8697400330d348ddf77ca3ccc4cbf4ab8f389db6d69fec1520a402359439d1e77e3dde3f782b6f1124be748a2cf97d5f0f195c52ce12cb133c6c96f89c3bdeb1a836b5aa09f73ea6d12d3df6952b4926bf44d24ed13da3664087ab1061a86cd245f9b319109b13f6f916c2b8fe68541c1fbc22114dff3733e83d8011d71bcc319442762fea002e843ba499e7a09afb9f3a62b4d8c48b1557e688f957e17e231d9825a46ebba68dc4cfdad78da80e89aab2e7f1213895db1c3f18a1ec76bfb18ef19e14f165244c3360a2b6cf0b0e8501229d4b99a8c0ed0f4f3dc16e25229d7fd68dd494fb98269a89810b6ec496d1656128e9673a76fd02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5c779069c53827719f5a22f4b1b694a13491836b7e973f6e686d5aadfaf19c08","proof":"6ad1aa14f6527426a3b059b715c8bb38bfa88c1edcb667b4ddb8c65e1032d71f2e9b63d6e64c7d682c49f588245c20b8fcf6ac855968745f2c54e178a755ca7314ddc018c53f386ba625cae7e2f262e8ea5ebecac7a716c271156a849d074722fc06d7d5c236a6742213cc578ac336b3699fc088e49ce673f06f983d6ca311476b863fc19c2cdc5a50bc5c6332d2969c12f2cbda944ac7e33417c6bd4d036e0ecf19493883e99df0c61bb36b7a5dafd43dc178acb011498b7065a8ec816c88062ac312749cca40189ade6911bb2f740b1fe67b85880d1d72855611591aff9b087c8fa34f54c08157a046c55fabcd46b590d137528c140390a6fd77015835250450e6cbdb16a5475362ea03946250d74e051150258c5efa39a8d5e4d9105451765aefbd233f4383c7ab1cd1bf033ab59b35ba4a2eec2ef0032c22aeb20535772102fe0b692b16ae32cb0c53860e845e6f8174a61ee5d9054c65acae2a28782663e85604f05504b5bcadb9dc02bb2342bbd23bb3b55abaadcaff21b5424f8579499a547234936cb0553536979d4c8114a49344816c64320abf4e4012bd0db98c68d0c4d00666aad06e91967babefec3879a5a70ea37cb26caafb07961a2b03a975d8aeac589fc75a2c3a65d05b3fc53bbaeaee61e403a9cde48807ec38ebd93436aa6bf798524511a62caa99bbddeb153f5faa1f05669ecc5836f7de4f80bbb42fae67323ec4ca104e88397ecbe0e1dcd03efed9ad96095bf881ccf988c32acf5098fa0c6e68001e2d3bb6f184625f5b3ed12a3249997289de0517c096e5618e4650899f49d5e3171fd94c36cc722a383d2a7e32c79ed5818bed75278cedfa1853e9b9ecec8faaf92b48ce2e596c15e2808b93b3c2334f3eb0fd9d5b8413b7260362fd57683ab61158cb67f08217692200e4e54dfbc0c56b8b0d63729b15f0ca08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"948321e5433bc16187484b00e35c4ed2fed9cbcd72cd6d9b8f89006defef2f5a","proof":"28a6201775c6e1c3523fcd2a791c03e01263a05bfbbdaaefa38c0ce2276df17afc240bfdfb8dd40bbdd3b6c01618bf98d245d437548c770d0779da3069ca670300b4c0522dbc330efb22ec93d4135ab33418d73bac68cae932553fd2b22384245c77ebc914f7daf0684ddf7e3026cb6a9f59d2b0dec5848439221b6648560b5128a0cb4fe4e1004ca37d47a645bfb97ba0b1f414c85543515abd55637fe15c02031184a3faea41a1c10ab906e35ed7430b920f6bf4c9485cc506779fe6a9c8077e8229d2bd057b0914ecbcc5e2dfff29aee96175a896bb10432420f3427ca40858b3ddfc46e1105b00b33e0cc5c0b97ea2437348d53895f9aff0272f2b959b3f4a50fb80f601f2f48094858651cfa61b3c216079fafe502f47ac49fe412d4d31eef09f42f234e11ba2c9b2a5ed287ad29602bae56a126ed4d7bf2d014414e115363b0dfe63959600c6d2de9cc555108ca46d5573f23bf72cacebd5954fb6454b48f7ddc9628805a847dd78a6387c85ed191c7491704b6bf982b9d1e1435f4405c6cec1734ac51050852e52e7f89ae9b1482153d4c1bfa63092ad4f986e9a1131d6b5e0ee882f2b3cc669778641362b92438a731afeb7da9e97749b2bfad79639164a0876e833866a0d4f2abd7f5bbd13748e1d9f62a814aa265fec8007eda5087ef9d24e2b45b455b650c29866ba72b65e47ffc1330c5927238d3518a35c715ad0522c04d2651b7843439d2e4cefc3d1e777bb3b55bdf90d3666592db853865e12a57ae1c3afcaa22279863397190cf75d27ac5649c4f35b2c015f003024e645ac634ae315fa616ef3f7913e8923190de802cefaff231d80b4f8c65e5a8e10372cf89c001a8bc9f25fe0dd580fc50c576eab302dababd28ff47d7daf7c9b9e0b92c3a7406030422c790f1b574f4e665b0dab2a7bb732c653ae466ec51e2e220a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a8646e35d8f328540a37fb4169bfa01f721cb2a98ba5a9d548fbd7f8a9007f30","proof":"881329beb89abda9991eb5c4daabc508564e1d84fde5964c6313adb24743e30974c9f50e792c6b7a811a726b25bf87679d77332313ea8646a336133cbe438454c81b8d1be12d6394b8244e9f759a7012d0047678426459f2bb52387515279132508bd14f621f390003a11cfb7beb8d8b4868e7c77df7a4ff6ebbb87507a7c92e730f35308d5e9d279d774a6a345ee267bbf8eeb685b2d336e35fffadc8acec0ed9f4a76934c020638a48095423f8ec826ef0637155a033bc50e257dabb487f0ab6d3e0711667a4ff82b89cedbcfa157d75173751a12ce331ff78241e9310900688160c0b0d51598de7fbc0f4580682d3d24b8ed750921463e844fe62a5132252e65aeaf11aa3136cf0d40ab67c03c8c839668793d673c0b175fac22416940262361d2ceb6de2b9097daba5f8db30d34fb5b761726f5a5c739bffedacf5b2373caaa1ad5faaa24c24fa6b37499eaddf1fb82ae7258013d40125ebd2869f91013582df077dffbc52c81c27f7b03e9a203e1088b381d071a079468a9cfdfe867574b813561479bcc1832e0cef24dea94d53b2881ab6146cda7f28440a4ed99217742236a32fad0d6259dc0c8852a7035a282b65871137fd4ac4addb8bb4635f6101d6bfc1494e7f13fd15a70e97e1dfe49d0e0b8b833e572bbeee69e5eb0e77cf6db84dec80f45a3bd66dbe9ca48bb0e1b532ddaaf4ca43d26902283f126f629615de387565e48a08eeddef17d6aab82210a36a465cb4ad0109f2fa140aa1adce562a275b17b5095ac33379f1909f7578f7b8dbc524c3b375a90b116819ed154832cc3e7bdc08f96b86cdb2c0532213fc0d229bfba3af0b6ec1716cbe5c78d36b49bbfe2b9941bac2726be394e61fd114faca6b7e067e728537114d3dc98fe6e303152da7b3f131e80761a1db1b8f07839f61d9676f21f375389aef6adbcb54340c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d21b1b4522e82c25ad0d3cceb848d8b4f8d477272dc31cfc805b21605c887a0d","proof":"2ea1579b1153cc68225eaa4b91878a648c89aed7da6025edd9f990020c9d24796e4a2ed96e907c0bdb875a827cf6d7d867ee502daa908f753baab03576b9fb492e714ed6ebeee3c60911188b010377c0019d95b8166f721dbb27b6a89d74ab1766e36e5d18bc666a5868f76ead457b3b9c48ab124340193d0d0026c73b8b3b718f602a24fb8382a5ea16393b84cb466af5ee141991bde06ce0597fbd929ef704f97eb18cb2f88486171638143818726fe26d86ff9add4783f2d8072ddc5f56041e6fd4964a0672f5a645cb4167b20ed8fc6b5a4c04379ade7b75312b36967d0ca4045f5d1fae4f27617f4c3e6e5a271cc93a72ad94f7a67fa0380483b529a940f06784f7fd0f44aab5f15155488f6b335576694fc67e28cb2b3e1f794f25705246f8f042b7b3452be3df313eecde18cf8bea0fef0296ebec69129f557f4c7f2d3aafc180624d2a87216726e86cd0505b3c2c1f5a77230d77b10a774644aa2c5964ac87418f973b9714ee1c10ef511a51de835b835b063c77d5144c5b6503da3a7625282d13a6ffcfb1513ad5ffa2f16135492a4fe023d7aa6e45bfdc7fc1b3582ed3674320e42fb28b8390ba28a279fcef6c5406707edaf0111287ff4749552e106a040854efaa07d8f100f269548ebe7aec3182beae676f550c2e94d10388385074ca7b069c0c3ce6082985f639154ca7a5656309a5a3d522f3bb188650842922dd223af5e158fa10f166e8d5b403815f6c926ba8944723ea44400403a50a552c75ed614646d0d7f328a777d30e53aac184e19f512c9c1c703041c1e2a9c562129f733e306fd5eee5a9f2590ec4ee7feb44256fe21c4ec2db47b79b2b6b19100d316ed261f7f6d12ba5e81a91dc40b6e58447762fc5522d5969b68fedcac80cc3d7705cc3e135fecd1f488a5dea5adcb39b3be6aea90ae0e07644c30b1e800a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"742484e375d6a20cceba9e164bd1acad40b366f8e74f915b66491a7510884830","proof":"067ab88541d5cbbc0da441daa2fed8deefa0f922d8ea665da0559f398f79971b841bded00154994998573153c29899f569d493902cba14313fbdc9f4fc400b1872b0295958013bdf8617272eb6987d1c4234f686349dc5851f719c430d13976e7c57e0efc7b2849d0375c2351efff05469e02db84d276c9b6a1bce438177875926a9fab14b2a2bdbe8141f02101fde0e006f1b2b18c6a476b285c0324f9f5e020d057bd4cb20ef8a37313e6a9b306c94396bb0a857cb3b4c04534aac6830c405fd5c1bc8aba07719ce6a0aaa12c9c497f58b975ea26db9dca68349d624727a0548226b55c679dbcd2fc1a68a0955b0bfa73c528e1e9a3899ad72a92f8547dc4cecde62d363c944ada7e6d3df2c436bf5e335652b8b2dc10c540a11022008230a6ec64a61c2e1959965b3e70b03536d0ca98ddedbdb6be845de0497aaa4fd5253344dfe5b0e6b5ee18b1c5db7dbdad47860694874b5f8035194e4c486a244c8392843289d7b53bdbc0dc036859bb41bc01ff8f0c6ac48c532bfea1bc53038b9572480a812484b6f55504b4924d7545c88975e2a218bfe14c1559cc01e7da6186fd809f6fc497fb23326e4958dabdba05d2c1b22dc2d8e41f6c7e96a59dcfae067c672993b39dc4f8147e1345409ba8d4daf66a43bc35e18413dd98535a339e15bd0f742d87a9eab70a4e1a54abbba383859b314fd5a9d6fe340a56c20384cb9325e8f9949abe60f017ee4e87d9e66086c5cd6e2c02e7f07475031ee998c2958441adb33c4c221e92f9f8957c3c63146f6939d018ed9adc774da350d93b7d17b490645e5eb7009350e2a67054a68cf6c1872f1bdd92c84b956111c3d6e2090731b62fb677712fa016c140876925752e7479262a65514cb60046f882a915c76920b2c93bd434ae5b9e07fa9483d6fe6d01e960cb8b1ecee4ceb257ce6c52b0c9b0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"94d0015df6adb980d599d2f794d379c6ad1ca89f7bfd083d9dfc3484b580f032","proof":"d684a8fec21822be35c0d09a40c4f4b4b2f09eff5ec12e6a9f3a5cf68d167502ca72a316d00bb63c82c321fab60e7a9fdd78eafbe7a69f47bd488df1c17cb3235a76896812a5598082dc9d6a4b46b3552332b3d82dbd2225c0e75b4db69bba0236c008ad3291fb16612ac0cc34834b040dd660b804275a0a80f90101eba1cb5ef1d20cf7d33038dafec556caa1ba26e2a3be1b31292cf792901fd01061f64e08616d1438b1fd7f06abae4cd738f71223835fab083fee1a8110e7d1fe8c79d205da36ffe99a5d144430bdad9c022239df2f2254671e04122db88f740757eb0c00fcd80530b523379ce6608ddd2d7c59e0f8b14bf2b5af0a444c7ef7792e9b151c649c29480c5085c7f9d23a53db2ad60baaf383fd7912048fdb82f51c0b1bc15b30ee3deb9c6ed4953d5a6f8312f11b72ed5afa7c55dc2f2f4eeafbcce78dfa110e39b8c4caaf6c77e77be1c7fbad0e2fa49e3cfaf6e72f81822049ac440cc34c66f4badecd0eff177fa9e908acd8e37e676f42a913b05d2256ae002d857e9c0bea3aa64a84806f903ee8587966178af536b5767e83afbbf82b8bd52be6c6ac63026490564a24c31fb6b0f868ee193dd58e4e107047d0264ac15ce896694dd76926634bc50d8266b3914aaf4743906c87a840bacbd8b5224ce5ac6a8a19d7ac409c309f03da9e9b55624416dcefeefc4a9ace2d92c2c86ebaf9ad1a07b3039e04369d4542b2c4c32de54b209ed7ea1890e2d7cc74637c6614ba83cb1a6ccd6804989c2caa438de1e9d3bebf14115d7092b53da81fd78a77970c6618a873053c5372c1ac7b6d676b326c1c25d6ebc3cf3e1bcfe5662b83dcc2e8e3af20b21d7d2ad85b5c3697628e6982a615be590bd2382d8a4181d119519b84b00573741071029dfa1083aa6e7d01c46c88b363323f9cac75e817109f1adb19869e82a1c0e70e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1adc7fe0ea28dd2c62219cb7349350309e143efc278a8c45b036710edcae3c23","proof":"4e950af3f6b625b4f66ec69e0ca848f93bfbe9ce45715a9583eda7935c21544710e1ba09e8b932c1f77eb3a9b4a730eb2e60e1db45da954b0cc58f5b1ba7d23548601ae754f3cfb7f314331985c76b434f135657b5483a8550723cc246745a03b046ab6a022a184a44a85b74235f5c5b4743375cb3d96bf27d50c8ed29e49568b5fd6d43b281c7b641f5f4108f468cd74e16680dfe6efbbeb66b7db9825ef3036e032780ab253b89dc27993b9ba2495f0e396998ef06a8bee170b29ff2d58c0a531545c9d1e983b9ad7b781715fa61b8b17efb7db729d96044d6e7bbf1ce410bf4675680a6d6c48b3ea8817fdcd620232fce6487abf1735e73b6db06b0652a2d826b8072e0a1868203fb25f8a6dfdda36a4e4afb953078afdaac7a73893a80308ac99d6a1ea7ad996cd6182ced1e53602e9bdbfd12232439637f2c23b9b0f07d0e011d5449c516276eb2750d8f6a46f26304c0c1c1de77a5e38a0a65ec12af2bccec9ec9669db92d1196da3a19dc41f89dbfb614832bfc5699091653974184641ad3edc76347fdcbc0e44474d6f902f90751d068228d62feea07545728e075463282770399471b6878c2dd031ddca6566c3e6402549716e8c9f2ef145f1d0b7b488efc1707ac5dafb9126c2ebe961c921b6ec68c0f8dac9cbee449330074ba4582cc21e700d339eadd23e622361774a2c0c201c50855107701418af99eb0c9207ee586d4d6f6300114c69f75765564c403e6fe2970669dde35dcdb46cb630048cabc9362a5bfe9ce80f68761122c514b25a0f71d62bd8f966086ca960da8387be8b1f25376dbccb41e8563c73b3076d8d1a609e52a67f2b3433c2508af71f06a2a2ef2eae5140b4c8d36b3ef92c51db5e96b1fa40cf95f1cb59698b949bda0034af9f01581cb8e6d73d83419179789c2661d53738d085099652cb51951875b0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"986dc30165ed0b326544661d3126f7d2d21227990a61f6d5e089c69a6d078e68","proof":"8401ef6c6c4ce03b379349621cad97ac37c087e3bf2cf76b83e7d288ca00984ea22b7f9b6be4f74a17c79a1e75be4dd0817eb201cd72bd827ba3ee88cffe7a545e57a592b6ddd45ce79a825d7813bf26e55dc2091dd2e9d9c8410ea474088f29ec979f403c9e6a9d51caf9c605999f0d7d6998983992c268df05b2128035946f4ff251940c029e8df10411c820ef4eca57c0767088141427144e6827ee2df00dc0a8ed4fd3506ace049930a43610f8c0e4a32e74e1cde193cf9a6ac9daf50602f22fd593e7b0d68e41190ecbd6d14acb4b2d7b141605e3b800b565e9804f2106a236f10ceaa0a2355ba740ae86425ad4340a6d4750497443005ece746e523954249b7c73ba138b4eaabe11334e1463434cde40ecd50a195f719dbe164f41bb6e5c30edcb3b8b0334d4767a1c11ed1e79dc22157d6f9a9945d11bdf1acb2e8970ce60cea4bb93136f44eb7a5e1316ae1f1c431678205070e06aca7495e666c43514b46348c52be7a07a04a756102650a200ccbdf8cdd20da9cd97515912896435989963408ad8f3d7c163877c915139a83e40d33a99e5812337e1f50e46f33e14d6d2522213db3d235b204a97616484de2febcd3be47febfe2fc7142f32eb8c7a60e96c60117f512d7a5703e8a139ad0eb91ba5a68d101312b4eeb37833c25630d2e15521a38dbf1dd493125f6560f6d454f9b55baeadfadd5db25e29e991b416542bb22de671e26dd10428fac7a5ef2895f2c840d6f741ef4922882459436b127668d8fe378cb14cc1d9b8e19a4708ce787ca8abefd4d1eec997bbf9ef0fd970442f974b223da32290f275ec4bfa1dc4c7396ae67171035acb9fbaa8aa96203def6686f838675ddb0db197184593998a1cce70baced95313a4637a728daae40525db5fd0919f21caf820952cc5b20e59f89f420d8591b22bbfc0084729c57c0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"661829bbfc2c427ae27d70db35d0617610bda749875bb997d8d9042db16b4510","proof":"82f734cc7a31f2837f4c6a21365e6626f1d27f6ddf0a12b754700ba4efef9d0af4d8a24036a9d6c2286a9daad95a2a4361745dd2cf12bf5836fee2fb4ae0ec7bd499cbbd27954ba8300fb117cb0b9acd2efa5e44d456b3402214cebb8a8e1c0662b3a18703b7fbd02cdbcb25f3a5aeb36b66c35b4312540120cf5470920d945425f0e1dfdb8802c1ff812c9ed3ef2c7a97833fc64c96d5567dd40201a70d5b0f47184303c4cb76b027a248d9be7e7d85105710e6e796fd44eb3a97f28395d80182d5fd3f37513a2e807ad8c37d170204403662a0b8a98d66c0dca9602ab94c0ca4cb6e4c373fd86bfabd3761e32c67c0fe031a43a6f6509f6b78a3c16d08d76b0651e204098371b1a80acccebbfccb863bc6568a36ab1b7ba9ac5b737b9ef629e60ee177a841c3ec3ca6529d124896a90106d8587d40f2529c9aaebd623edf3bd862291c314c0179deb253a1bac5f1457ad965f1a15836264e86a826b35b5704302ab519b7d5af2825db2f1cf99beb936288d22e44e99fb9d0d7109819580e3b30124557b1ab0670fdcb809682e9f6f0632eb048068558ce0837749ac5e05b59406ee80136937de6a935e49ce63351d9b3bb63b2f141aa0bf5951b13ac766342021cdd85f2a2893d3fdbcf9dbb19c78e0eb1ad0fdf7e3772d638d72b03f6ab3bf0d283fa8d7b1ecef6024243e7d72d6b1613c1fff8370517f35b087e31f6016b9a8318acac388459d33b14eb6f7df513470dca83250de25be9b3451e99d8f03d70ccaceb938c85bde6909602b3c9e211f9349f8b9300e3f6b2f5ea4892c4910fe0a71f7a34ee15d927b6c9c756c08f92b6cd163ac0fa9d834324ea6d194cd810d765c996e7cab6742926243eb810c30834c43aa2f6de8cd574c2631b1b489805efaa77c03145cdc28b861decd80a2bd58cd76ed3e3bcd32f1f2cee8a41cb960c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"201fa8036e419287f4521ee591d6bdd992ef1101b3f7629668ba1eaef5878d48","proof":"0421b667ab6ec9a2ae4051c51f3cb2df39769bf2c8fdfffe5bf41bd29e72501e5cbbe2e6f8c231ce3832ec03459d7c41242e7484c7f60a00b22722dea168cd0dd2a27020ccc6a24b59d6cabffe16ffd5ada35a0f49a455fa7b87805c8b627657268268626b96ac8aa8c737d022802f2ed6586b1728cd1411fce42a56a5bb40179582b2997b89db8a1e7efe891ee857715b859d0089065f0b6b6e2feec7e0d70e8ee6c7f9289adf10b98dd82d193df5116c13e5d2d190a23f6ce89939604c6e0063eb8ea49e7de0e887b6ecd59dd17069ed98deb1f9907e996d08aa4f2f146e0c8e2198f1c5f8a9c51f8195c67667058f82ad99e29a0054b195b77056afd9ea709e7ba3520c5fba4947f0402b96aff32ef727d5c2348e185c67a6ac1c127de56bc285529b45c04fea9346f24b3ef74a9322ce4c0b20a04d9cce92142c9a1a4662de2c3010780112ee801c69136ead699d921b1388b291fcd6bb69dd7845a0866cf8be0b14de44726fe6d65d670f5221698eb8ef2982d76a9351d552a161be596cae61d5fc5a6dc4a849f194a9f2ef3cb451b785a19104cec0f85a575f67f07856e4413b63d5ddcd1be76202aeb9566d8bfeb16d84668c3c56c8d58e257fc8535e38af4709fefcf4cec33474c75eef74c190f6027b8393a02e297474b9d0fba440b03d27e8a9df3724b20b6b45271900511868d8adab7b396f47ab6e68fd3ac856d85456659cc2e884049926a955a5efd8a7231800544fe6a3250a509bda37ca38505c1f66057d49e11127b5bc31e9d528241e9dfb0062b0072ef8095999f10b0c8a01b9e73e1adfff867d51f262f776d207c128276774913c6a66f9cdec97d06e914c80b30e7b7071a0ad121e7e5470fc0c5a72c87e4ccb1f19fbd61c5e09e4043c83aeac07ca69a40ab625b27aa0d13da778cbe040572f0f99f30d1f67ae5b0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"52cd165c6a5b9fcb3623c8b6558f800b0f3e3307f8d7b61c8cf9e8cbdd7efe0a","proof":"364cc6f4cdf54f964c808eff728d72bfe54f4b244d5744d0e852fd19fb69a06c9eedf617167dedb50e1ca35d36db09dfc541a2543fb13cba38188172fce9ba286e2e4501af75e346213c92c3d67a0b0adaac4fb972350cf8424177d7061aa0112433f7caa6a17cac32eb8531009df6930485e4b25f294b500ebbb09053c8d64470d41f41e556685fff4af40329f75e8930a7ed1255f5c306a9c593d66f07440af71870c7acfe77d70f75d4bb0fc50ac161719b7ce86001eb879ec28a7d1c9807549765027ffcd8f56212c11126ab9eed197147f874f90584b0e47eed464ae002c04a8ebcf94547c82e376694f601c4cb38de8e7567257765e11c91070a81130c9a9cef9562edb34a71867d5020d05c6fe9ced9ec1870d82cd764eaed0d8f6d4d8a1ea9ac1ddae27eec7ca7f2b100b987034a27e194cc8efefa697dbf54138167b210e2848b060fe80fed383c6eef68675b641142ea1d0512a964d9dbcc8fe72a806ff4ef6f75f92d123d9522d4828440d8ed3d3b15cf41f8ba947a871f58854e747b9b8e0b1f9b5fd983f5937f6d25d3ce539b7f9925f40bda3ba3c53c2ce12e2e9f80261b65959e5d8362e116aad0a486a5873344e69f19c3ac4e429a3a7232fcc6c4e47f2017e094e665eef4350be85ce02cb7d386289c0b3574d40c103d52707c2f723737ced9a54f8edcbbb44a8e619adb29d79541b8ec6fa323ad04f0643e686f5de5609b7aaf86012d6c19eef56c1807abc6e7cfe50c7c6fac9ba5211ea086cb2d2e0308b98fe94c9d1278439a9d48c93efc5850bf42b14458addeac4090482b877da23993166b600b17101e04501d75c366e913f8d1e9862533652b0f7826bb3a6989063e62c13bcf63fe58711d01f7c0853df0db0eb8a52774447105ccec197d1d592efc46eb7c5819f090747af46a9afc0de2c0fd1fe73c6a9f1904"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"62e6fabd2f657df2a2e800c0b618ba873494fe845dfde88783f0e03fc887fe54","proof":"48c9d852b092e61dc548f12fca3073e203b3b2260ff5e9601a08dc796f4173618c24285bd8e93c48efdafd263be4c2645e0ac17538aacd70c638dfddaa27cd4e66cc73993b8b9c65129a6e16f4a4bbc369b4da6d42d1e56cbcc3cf14a030814596ae46cd4f28594ac3baebd194c646c43e20e1170077acec64c800d877e4451d0026dfa593da7afc83757d7b0a0eed9f77bf3c6bfdea2a948b08f11584404d05fce1827173253abbd3ac6de752d6c5236509b42acd4ab989a2f2851c2e33a308b688f068c3f278de368680b4422aaf5fffb250752e260499424de271a4aae20fc25f3c16860b6e87b1dfdba69983adfdfbef78ec6e1f34b504efea3ae6f50f5fe6565c84f7b028e35da22a2b24a9bde0ed45bab49743da2a8ba3d5c01ce98a35963d8f95c5a73818561eb035eff3b8560b8c5dda79af62dea923b0aca8550d1ba8682d78ba7917e697cadc25108e846468eb6f25d4ce5cbaaf4e78231289fc2e24431628612526bf3c47f8c9efb2f92cad62c451873e802d1120bb55c7171154667b9bbd3a3d8a0c9a5698fde2066adef8d6e21b6df6b6c6fdfd49171d1990180aac9ea97f74341fa2f3ed5dff4f3af70ce63f35245251c620afc4927e82be1e8a3c8326c1c8d9389be865c729e14120b672908e8d07e7c8598a1dcf2985872a223cb0a5a3e9c90731fbdb2e880ab271d7e8706ca14e22f695ee247fd7098f76be7b922155ce428c58c261a3de2735df2dc9977e3609b3ddb17578f63ff3ca2ba69bd8946a9bc19f75cd95a73a21f3f0b8e2f03af6114f140bcc8029e31d8341164c5e3090a5a56af84bb6419ea76f4d353bd720d31a71c58651a92e0b1c725f734e50968ab18a1b2fbf271ea71ad76146fb55668e229ca81be68e365dff8e0869dba119dfccf22950368b5eacd1737c033645dba0969254b28b39aa1c73e406"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5e46eabef254f12eca877dc7e488e2285886ed1bfe7ee4cc2ac0d5b6d088ef58","proof":"2e4ca5928686ccdee490b7034b5d0d9fffec7f8c516d24db46bd6f85bb176d144eda6d4b56d284c84edc78af0feda078a5963d8e289c419362c5878a0f39a063ae436cb21a29480928ce27ce68570a4dbcbfa059b24e8f8026eda4f0799d53129e755df76b1b6680abd620878474ce4209e893a4472848bba46bd4b1b9e4595f301fbb48b6af74d97b4710a9f85e0897270973ef80f586a0336a3c4d80d35c08668b78aadea7ba1dcda970b81ebc226d2a499a3acae7e88a2fe0752b16355806b6067ddead5ea8ac8fabd659dce55315949a9347ffece5c2dd8a7b238f6a4d0d88c42c12f363c8a4f68d255c658d52839844694b3b175de50d735e6f200c930c228cbea5a3523c8ada250d7b6b210c30505ff1f5028460ff2452b73dc40f955cc6eb1c6e5fbe5ebac51bac29923c13abbe57e1375b64a5e9bbbee6c356927b63c47d4c3bce2b0863d5182c6329458fe605780c3c7d778078b9195d5d6107eb3724a723b12504f0ac1fa72f0d7b890fd0280e87eb43f006dca165e5737b060500d41f2684dc922ec9ea8179db4153064895a2dec48fd6e210bc79f4f6a873455d745f75e25abdefabdd3c07cf39374e948ddc62c3577bf6993cc39386cf12c971a28094ced0c2cd6e7e34199aac6f73c8b3dadbc09756fc11dac4a89112032d1352153400f3e11e2107b52bba0803603914b8d3b5f3e08f5e6f2ab24495793d46ccdf29522b7be64d9fed8a86d6d8efc9803b01d6267983e95f4f64b6811bf94e86614b1c8312da60d96fa5b1fcd578203bc0269e3c0e80e4f10ccb5cb6b53b035c36df1a46281b468d8eb2ffa2313f76de5f4613de09b850784981fe43c3777ba1fa18bd3269c91bbee27dcb753288e69e495165e3583bfd6e14ef85aa589e04bb5a655781dc22daa62b8d00d6ff587512f14b26d1ccf22a60f1162e89ac6e05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"141b4a8b2fe56ba2d3c2eba07b4d6891890863911239976b63ec2a762679e91d","proof":"60d90715b745cb558bc8d2902b012f729030a3a5d157291392d02dae9f0e0832625bf78ac64b7efaea806b63ef2338a62b19e193dd31362b27da8f45ddeee9353c662b05350cf906d947c616864869c0c5806b4f17026564b14b15456558bc2a067dea8d85d23a7172a65dcee89bc04262e2c8d4cc1e5088a28262fd130a1219f517dfd3d3281ddf7529d7ccee4713887c11baa8170abfd05b450696b68452061945d4d68ca5d2e32bcfb6d7002f211fb40a503ab9f8595d6655d3f12895330296daf0469cccea4d783ee0ba2fad13b8d187eb9a57a64d57b93841f4e42cf90698afa7b6660dd243b6b95b14066c51f6d96c95775e2cb6aa7f8fce078d07f10708fd6797c26ecb52b4b958c6ff9967a9ce37db8df60df5af59e0dfd65d577b5f9e3b2a715619cfe70261bdf58e76ab1525b71996af20695a24289f139c885a297ec7d9bc2f036f5b2eae2eca5a967fc8c09fc53647834998cb3e8e91862f9d61baabc00c70c41e2a5797127d1f5bd226da73ba32314756ab9ed3da85287a363dbe95662000287fdd6d333fefbef928d9b5e86079e26ab4c391a025997fc785656ca2f0eff4a7d33de8a9b59aab56031f11d01a06bb984d6ccb1b7de35e70aa328a40b1c6ea4385cce7b78b1b4b9241dd4b5aca1af26ffc030b6962033c35de595e987eacbca20e3db44a06b9c2853d3d5c97473fd0bd9f0a6c503483664878523ab0f7612efe122aefddbef93d417951640c9638535ac0e901212365d967fd749211228f2d2d0646674c3bacd07caede84feeba4c4b3e9df0da6d1fecdc44271203ef57302190c967e3ccf3efcd40468fecdafec7ab88652c64cd62d5664ec0b6d150c3abc8d5e529bdb72a4b585b579c6076aff2957dda67da9a84c579851056dec77d955e35b4f77e972021d23f6cb8ab8e6e53fa65a7d7ffb76412462cf07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a6b57ba2c666b046c47eb4ec99a7519914f154c17151ddec07882bc8056ec936","proof":"4e60174484b0cf8238d08af3fd21d903e00061b0d750f30f4bc4750376ddf233b0a94d272d5f6342519843394c575d93c9390d6249f1a5fccfe04c3973b5731daa4045b076854299ff1e8d9f9bab39b1d0fad0655327144357a37caacc80cf6f9c91a72319a4486ac6be6d01edc5ce137e9e38f241cdd7d13aae9b11689b0e0b4c14e2a29cc33195ceeaef15637944dabda96795b521a854bf67016558931907cf9f0e6714e7a2e7164d9496f6f2a838987ec6c66348fdee5930108cb180310e6ef9fad1789b7ed4d85c0654b77fbe1e99e3a06d7133a75c864ad13cfd25460c441908acace9395175e44c58dab9646b31e9a9537b50b4a1b1d97430b8612f263486389efc7206a447970845ff27137669050772f88a423223c1f24e8219a430f87ca1217714ba3a553d59c2843efc83d1fc2ae18bcf2d0e9e2188a39696a7083cc464c6ba617e6a40772ac4d3076d9077e81748180a9b7c283af85804305835d4091f0da6b9373190937ef1cc432c44f53545c367be71c133bc7b99a0407e41682bb704535f0404ae074c1e7104ff67c5dec2b84270830bbeb01294635a0b044493ae0a26e90136db1d7f00f1a7d053a0aad5e6a1b2d8a9ef347f5da984526a1a960918640be83e73bb74850d1d4657701c4926260e6d17c97abec628cc063ae6bbafe48c091f1bc14cc29b2acdbabfe7438e69cb053ac1f9d5a45648d79a5f669247685a0ff05a2e41b04432a281f01929d2b55aabc332f5026c4fc7a7b2289c09f0cd448aed51527ab4242c36c47b7d4d3b86752dd74334550f6e4f15671b16793529e945616abd5253107a58ffc5af1ee372c6db10b6a5e631086a4f7e3dbfc9f5d22e025e1345312c8dc9f5f1d7293f83beda30339bb3cf6a236ca0a701e8a20e65559b31e7dfc0534b13f0375c9fcf41652ea7716f38e1c672912af60a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b0fb056e98c7f846b66b9cb143e859979a3abcf9d5549c2e843eb91f4f33a27a","proof":"6a7ba43d6a4661b9c41cef88821d4ce103e1f6cea637d304be0a129c366c4f0bbc84a03ad53a2d95bbc50790a2d500c12682a87c660d6de37fb1b38cfcf04d1a0a6ba2cd150d94fb002d77a520b132ff8f101e13bac66d6e830da3362e881e5412940f590fff8b81a4c00547dc39e9eb96db5880c2427cd909a9f8a5a6feed497c424db4cc8af4aafd5857228d5be4e17d9d422da8a0a7465260204936a0fd0a86f6b67f89ee6d3f51a36c78f9f6cd1724bde66f057ee7666256afc826a013043aef0f4bcd28dcc7adfe65b8d43c4d5b5cdd19326d350eec8c17ce69fb5a6906d056cb776457b38ab6f2bb877562d3b97248efeec0d7dccac791afd841d6b3092ac07180eaabb800102078f43055f2eaff0abb9e4fc95d1ee24f7d9c5ff5a1414c2088f0146bcdc124bc010227cf0f83e95de26472349f294bc49891a63c681f5017a8d2430443143cf467371c98b505e4c01fa9f7187f9f46b6e4548d6a8a436af21abeecea1546348e03c89ff1e43e19e137e720814f4159881c5d33df9d2a320a4d80b1c9439764102b8ded48295d439a6e4a0e668232f251e25d81599960fadb755b7190dba35c448412d0550e3028dcf10bbbc788550e782961ecfb6a1dfc51cb1862bdf981a8386e0e867d640c5fd1c7473bf9adc7dd8a8ce06d38582780ebc013143b370bb6b3e38bf031e283587419810c1eaf3e9db8cbdb3dfeba1f8800fd021a52fb96a5088c5fb85f20fbd0c91791e5a219b2bda4490105d962395298809c1021c6c87700b81f3804f5939ee4f77fc5f070ff63a0679b8b193e1a2c755abe6da0ca09730ac5228e66c4109e0510cc842aa6cfb432da0c8e191c7ea4d2bb7c92fa137a392c5091b0bc5eb154604d2fc98537821aeb5e5d7919da025a37dae638ab5cb75cd6a98de7da6ac83aa477d74aeaf16141617b1ecccb1607"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"805df3b1020c0174a78747cbf7c69b741a62f9d844bbec5c8b881b8b091dd255","proof":"fa9d12f757b5dea1bea52e59f08812bea5235f0a94ba5b052c390bbc4063db21aa4e2d4e988986e6fb1fb61be8f74f9d396917209ef5b64fa84187fb96e2e240b86969f9807a1da60e6bb79aee76e58fda2849eafd1b5d943302cb4d3212054e54f1d8d30cf67e79d20d346b07868d0080b3ecfbadc1652142d3ea9ff720fd2f9a4ef76e65db0761d1faced6b9c1da7d918f9fcba84b6ebc0cb489f2700be905ea113e89d7a21b96fc384631e770bea9497357db2b3206ad2f5eb2e955b0a80c2577f20c7d681a685eab69cc85a8b396d9c5a60cfb7ade79d1a8124ddda5020374832ac1a8e5545baf27b2f44e27d6f0409cbbe3114be4b73ca44edd57cb9924d68e95df1cacdec3d7c3a44138dfee07016af5cf2d59041f6762069e8972e20e2cfe5dfbef946ba49a9cc90169efbf123fc99aa418861150d843a816bc26591c782ecb0d9e8de56ca996f0152d0c6e2a3d99c945adac49f8f69c1798d514112ede5193ac60a87ccb5c79ddf06c0853b0146e4e1cd4ca6adfaa90f69ba833f86d68687322a5c57dafc1abb5b9d95c89515c9b3acb7abc28efed9d6732d48678408697dc86fff63987d1e0d1dd6e555c84ba8e10ef294153fb3fe925de3154e1419416bea83cffa4709963f0d69762d61d738b6f4db5ece564d4bdf09c08a2f3147a9a62ed074e128b9e9f0a6d028719564f178f6270d3ed125c42e4b7682ea300ac6eda78a985ae0fbc31508b232a306b5339640468579fc06d46f614e68e7d1960472a202b29bf4c48536941c2736e27d49e618c3970f6c3f501d3e3cfdaed77d2d3b4ce4ecaca3c61f98b274792b73401f7910990dd25f1db45ab339b5e207a676d99dd7f219151646ad454cc60a9ad3f897d1e405005854e7dc15c702c3d09fa9363e22be4a4c08632f228250083569b8508542b6f40c758dacae8338a9709"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e8f0b07df6695e6aaf2337ba2a7371e741cb431c2d57d4a81fae1d14caa1706d","proof":"b2086aefcc5e3a929a24362381e8301a34873bdc2ff33198b54ba0d83ea8ff1040a62e062237ce45cad82177201736a0805b97d73e98d28e6b0cea5d27845354607b6a0bf524155ec595a0ad25b272ec473d1bcdd44ce700dca183e5d7abbe154cbdbf87d839daf7ab53fb4164ea60f401e875ae775b54e358836c8b16f79950e13945fcc20c396485493d22250bbd1406f08a9a76a3b331d896c3ea55e1e800bd696977c2981a231411087ebcdd6f6ca6c90242335805830d498c09ebf444013d87d245501c985f89dcb535e0a7289326208226f9d27d6704dd119953fe740a7cfaa0d5c055a610baf15dd52056a990e5e28add7107e5a9c51bf8eefaa3e6065aab772330070c39c4bcd669d8292c9625b813370d3e2d1bd87e7f82a31c095934e09ff0a911f9c1c040e6f5d14f210752a384515d026ccded19d80ea609550ed0e9db397ac0afb2eaa71dabaa3769e81bf5293fee62c0d6d8b45022ec847676faba25d1719f7674d771b0cb588e628ff0f7c181a14ee4ea8183ca988d334431b68b176726be9e9cca344714ece0e1f44d7ad67f4766a9dadc5db92a72cd9d3bd8e95b4c39184d030cbe9ee1b7a2207af340a6008dbbe35697ab9a2e8192c961123cd9927101dd30600a9eb8a5561c6f8ed8970f0fec4a269472d5a071177a1f04bf874de39fc4f2681d6fd13fe871ad3096be1339ec83e5e8724d5c3760035956e17bca278febfd8903815e60320b63f160e68f696c6e3bf6325de74cf6397af246e2e7a02c42e706fbdde832345be1c354ac7270e7cf226a8e921cd846a53c7a0727a8cd41dcb9169c817030cd0af8c9bb1f3ecf3e55c6d1b1d4a63f6b427f659c8e2bac4845c646a431ce2704bb516352cda496c7354a10aa458312c86f03850b1a523061f57ade8d23a647b4befb91a261ea957bbbd257e7d7dedd322a0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f44210e8805a692a38da5ee79f493b4ae7f6ceadeb4611761296372abf8adc62","proof":"7cdb3694960a0b71ec49f3c7688c2ad6d1f851a9e12a57c4b16c880a91cb93015c385a0d05c275caa0af21e9e6661e0e2e990b4b246a3183477f6344a0782f2c443ba1982a9b785cf6c37c11062b19f6e4afd6348cb14b412486ed613cd728330c7e64e46536eb3fbfcd0ec19e4863e1a61754180cdd5f238878274752e28b0ba4387f2b57214c37d56139c15fd1fe41fbad162716b99f81c629fd0d0de03e0d565f864b86c545f56d122fa5f36aebc05f0f902745594097af70e1688e355e0dfaddde0d659f48630e81d72be42ec683ffcf2086c4183a257e54f50f53658102a872d2c6cd4278c1886986b7bbd8569e6f29ca452091157c05c2838071eb664e040484ca4c0656cc929115b12f24bb006088d36768e5d28acb7b875a48985970dce5cd58bdd97ed38104a28424173f59f9c23cda581b4d90e69cbc4a7218925bb4af0b5c802ed57db12ffbcfcc53b408d6fb3a56bab555042d2aeb77130c295a703aa7551808e849347fb9489b57c817f141329bffdd959a06367b98950c7831b0509a308a08fbde93065565e62dc3e68fbc11bfbf4054cbdd2b4bf2ce32de2c70ec120b2674f2d4d8a35b4a4bed5ca5d7a0fa1d7df1fdc9668729043f6a5b2c1c9545f7e9cc19e8a03537f2c8a12598729cd51957984ea8a7c0988e5d0cf157e694eb8316aecf62d41d4a3a41349b5f40083328dbe5ab877e8e74946a7963750cec17de5f5a3dffea707664b16059fa355c181f6471a803d84840f174cd3e4a9e26d346bb2a126d2267cf116f7de8e0335d61356f42723557bb7b531809c85b4cb1ba1e81003ea351cc55d6bc9a5c62c04105c43a9bb121f340778534fb110a813c89dc12d525d6ea4c24d2caeeab50355fd04982a6f765198a8ec6ae7ba906d16f64ecff1f3f8581537ef6cd30426bf8223c0c0602d14adff27c88f595b80c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e495f6211e0f277ad03c2f9f2d6b4e25ba0f2e20540dbe01c2c73c59299a944e","proof":"3e4b374a71064b2f3d17b50f52b503a844f5f8dbfb6c423c6166dfcd9fb6552ac6bd1ebb3191082578a8bc3297538ec750628e549893ebc957a6176a49efd86bae5146a847a9833c83a4053e303d2e9c73215a8b1df82d50e5803aa8046fc22e00164149e377633c156a52a9e87b4228e89bc8955c22c238f2322eaf12de6241c6a6a7a953091db58061903d4b436b77f922ff4824bff383377dcc6df6ee070867de3abd3592e0a68a974644e6799eaf8fd45b97d5bdc5821ee69491e596d60f2d19c3dbdbbed3b842fd8d6ef3dc378591b4e0b30bd0237bc6a3d7f98f576d05801e57a347bbd56363f58f38a340e76d031705aafc6d59bdaf083c18a5ca150aa00ce3b8d16066acb170bda4f55637911705f277beb6676ecb45bc63eed27e40d22e5d7425e0622accbb8953a48379fe4669e63f1631d1790628e2fe63094f2e8e4be35fa94624e77dbe9bf535888e636e70728cc349fc1f3f50fbfd7341656270b8d781d2da0be6947d276be2b1710b1b9edc9c667fc8f9d989354aaef7f72e1eeb8e6bda475c2ca56c48767119d4b5c5e682b24571fb65372acee10396ff0d2020f17b6e9ca6fdd1ff3da3a586d39919ff976f84a53642598c1e86b8a3775668d364c5121e4c71b5c81a43bf9cf16138a78ad3764a790fe0c69144a8e00466522278be25107db1cc1c6017af6d035fba8901de01f3336e14734fdba4e777256ece6ca04b28b13e11db7e0c98e4b1b5d40690266c47df4ec81fd9b5db423322507c83639695baf05e34f7705fcfd0f5e350a289c8a671984cd9064e767350152abc5871578f12ca26391c6633c70162892175cb26151f7d3037d13965c0ae4fcd60e46718c81295d7f3c5477f92b249dfd8f6e6e1a55e9dd9b499c95dd3130ebdf9e608a418b951d89c215046b60b0a54b1778d75a4081876cbd2f95aef7d0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"eeefdd8caa8423ba488e5a630d2c69b742b67d672bad0b9853971b5fb018a82e","proof":"5e3c3411998a5d22c4a6a47119f111b60fcdd80eadb23c0bea7c8a0bddd1464e2232a8f6e15d3ee4cdc4c9dbf651a6601bf3cf5ef4445e524e843bc0b643ca31caf034e7d7abac209a721e792373afd7986e64c5d144f23b3d1749a1affcb84f863b398bbd336f6a532689cfb7f5d207cbd86d6b19b98e9af9ec2e728ba8967614768700fde839d6f46a1965697d3f3df40450dbc127ce8bdf15b3ae835bfa0b600e6930100b0b25bacff0556c38864f38e6b1573becf4b8dcaf3bf8c537e00f80c1865aa3f40e290f4c7a228b75a7302efaa31397f70fe8a1bb10b9f4c0130b52642be3ca265f87a0476c11e34a7dff984afe2a9eb09c2bd650545c7f5990769e57e79f85ae3690af493aa482694799499c9d4038080e02b28e1dab6d37d345be29e391fe3b4f9bf84d68b965cceba483494033ac131a45c38c63b25d94f94080342135e53dff9eb4a624c378cd423faeea1832ac4e803390301e31cdae7f2a84b0049d39fa57aeb35f5c1edb331e24e6964c4fd7c56a91fe14e326a92b2232cc1280e6af61c1dfc103a8b4448de3c5cae726dc1949c03c0318306f8b29462020b02e2b9e6ffa25c967fde3cc6ec15684a2a78fd3212da5015b02ff3722995234979de55a5e5c0a7e202486e6f1a5e8d0eba6abcea5c8522a3ba4004829497c8af64e3fb5757e92cf14996ab8a707bd613bb3fd3686464323b81cd80d89ff38ce91e737a2b41bcc04409dc2154ffaf4411ef98407ace3ed2732a9843737631a26890140e10e3ec26371968c102075dedf5f325f8a39d214a71453445e97ea6c667d726d4b547250f34049acec25cdfd2eb15ade151865c1177e44d5998d5a7a4250d5311c18458ee0b09e9a18907c9bba24d231f2342dcd076355db6ae2390cb45f3655d825fc3cb3c088ef13a139706ffef0c7a11a8ead4b73c8d35569530b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3001aab1ada84aa3b60f53a52c83eea6ab1208c58598b51c43389ae319976906","proof":"10ea7a59113e74d1da7ed2080bd2d72a4a6c3a020f952f3ddb782364d10a8f5a90dd001adf9aabfb202d5ac6469886a967a6e3f92633894b4e5b8e783d74832e2c53a176d4f49c2c60a26174fe508f37737af26e4efdadc29658dafe01a26a3e02618e51523ca427e040a1f4d1a3df78521617c0bec48cfd9dfd02f409db2d48a75d0bf4b4327f58b6806677fbed5bcbcbea0d5c129507c0fb225d5b4fc2df01ed4bd5c916974cad77b1edbbea8d4b10a0d52d6ca59ebb5a6465c7836a38a300100c532df06562736ae7b9c133af51cdf1f0c161433edb03563b3851e46b260b54f4263e5783484cd968ea1f2418341e11fa70c20e9cae5954a27ae045c97a5a56d76069aada9be112b29633d03e16eb58dcfd63e02ebeeb656bc063e5b3e419aaee2c2558732e49d434cf32d6f89104f62bac443b54f6366aca9e49e85d03295ed6094c13b3c4217c228fde83915e858d6088385b269d17632ac9319c0fda5f64e12be901f67c72e2ff9a78420be47ddb977939b9c74dd51df3e9bbd4c42f2a145e2155c47b16428740463b33d24edae2da4ea92de3bd34e1df69d75381bb2ddea26e4a4a87a3f4f6cf90c01e8d30c02e45dfd2270e2d8de01a8104c9e7065dac360fce6d5d857c87f5d1b1922c1af8d2c993d40330ee6214a3eb62505a9a6cba8c8928da9efedb3ae179175c82b375704369c43334f6a2bdac338a493a530f2400c02afd4f376642da9178b53490478d19d1e433b6cb1724dd4dfe5922365a8a4819ebd18c72a010368f162352cfe7d4b4f7ccaa57fe38453f099c548a534a2e50145340d777c1e215e2e337c98cd3338a9dee3ba0cdcc9e719810717ad8218944829b5facf8bba30bd7aefe45e745930c084c08f7aead073f260767dad9063d739727fb4be6d37ea8d8fca533ec6b1b1a663aa78ff73665ae197f3fdc7805"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6e3daf3bf5952ba8f3b316a6e01824efce059ad8e3568d4b698eb6c4b3b5ef1b","proof":"66b729cb6387c9186e42fed077349d087a9ba4d15c75c40280060f2a12702d485ebc863394541f5f6fc82ab38c537e9908b5ea3f1b8c17f4d2dac70011a4246c22bef651c40c4f983328297704c78672db4d5f4c9d833d6994d350e311c6b3652abe4c49fd7305182068cb19bcbdeaa2c22e9ea52905db89036447ce6935f253eb40edb589e4f91bc3266aff6b767cfd6658a727435c4c24c0eb443fc6353607e0c2ac579b625c05e11f23007de56db91f95dc90faf32c7c4512195da90b6802118688449b52c30a4ed0e439780a2b0aa8aab534703639268c42447c725a3d08a0242219c8e202745d7257a791aebd3675a831405e07b7674ea0cb779be20931ac4e253ed6cbe6907adc871ceb21be58e70422c3647476e88cb05bde4a7c7d673ae536f824147d86d7de75179bf81c3fac13185e53cbed9c7fbb9085175cee2372a425ce3cfc09ce5ee320f4124a1d4954479fdaf3106e0c71d38429c553a222d43f54889ca364c2fb17b590da82eff82cd312b3f8311f5698bd397d09f2c52bb252ea94a04d7e0e1b4c19909914919eea157610d3d4855ee03fd2891a72fa0b52332fef1985f8311b6197416bbdd37de9fcaa315843eb896c144ba576c6627b0ec52c0aff107b1620eed744994014e3d4247dd41200757b86a3a3328436f1433acae9eccf9700c185127f42162de78f44c46bda4e36b8e6eb77b8e8d562e614d625d2ccfe230d4c29421835582b0076413229011f23e1cb6c62418295ba407a489e06e9676cad5317adbf038a70f0d12a54f2d68892cade7c6653de2d010809489c3497fb215940958fdc757d923b9f2ed5caccf0633f7a9d7179b280d5ae4cf70bf669fc2f35285ce3fcabd5f8d072e507630274cd683dabb71508b140de0c026d7210f56802b9efdd4a643d63d3a97fa84501ffb068938b52d54ad8996702"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7ea18f805edbe5a8fb383d6fad67c38777e580f323101308a666c6dd5d63c04d","proof":"e8378b895971496191de2cef933ca359be5aa9852c6d9b616d0441963a523254443d3ffd9693d6145b6825fe8cfdd2bc39d264116f0c531f75bc7329b46a6e0148acfc48cbe42f7793d1b9e408488a4987e42014d3406768d9ec976a4da3c91d76329c6a22a3faae5c0444d0dac7402d1deececea087d9108de54b696fd0743b717b4249edf2ac4be8943337bf87b34b9fdb56e7d2a74550a88793d28b8b0f024f7c039957ad7d309fd1684c495a4b2cf7c5ebc067b70eff7d352d1611bd8f05b364a857c3e1823b24db82f4d9fb5d5bd2f8fc8f28453e22cfb7cedbbdab390eee3053e6fe7dc4724e63e10c7f815100127ee8c6df3428e8fc4ce2836037fc731a4687ff33acdb5bd2b3a7ca1dc3044044ef09500589ee1045e9952b01c37566ea8f4b60ee06d41a53a90d10007adb1e1bdcb1d85b9a88bae205e9448ec76428f0e17e239fc09b8fe4267d544c7b6d7c9e5ded27eb178f9bc8d66e547747f52e74a4ba4c6f759effbbbd6a266aebb6eaf6419b8f6b7d6d0a145ee14a71054476727c3f41f942cd279df8335f6d0032ccf81bf158ddf4ff952ecf604de9ea2c61eef3b880f17a8819358dcce87d4d33ba2a0439690be4a646edb9144dc29b557a0a341fc34073d84df423582c3ac9bf03d50523b6a9e2d688d49c1ed9486c142fb68b943ded0b046fce0f1dd7f78b2a4b13eff35d98e9978740d9bcc7b8c5791c8ef06cfc3e270e12c896a53ab8450154f18f664385b7cbf35079cb47d401fc6368c9476906b520ebefba3506067585bb497507789f2e5c1371b9a008c758ee1e5451cd23123a05194ec5cc72798fe8ee8c7870df8a658cb7ee41fd8b012de84f2e4aa33f4a71ce215d518549af7c9ed97867eec0c2ef3ec59b4105e916ee45034a2bf5815672c8a0fa2f7876503b1aaeefcb045c6f953150f88e07de5ca1d006"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6ce3ab306f217754d5f5cefe60ca2740240d32eefd1da5760e6fe1237913721a","proof":"5acbd8f84d41d0bc3676b5d5b51ed9cf936f8038980fc9927832c4220c586259da4331499b20d401b6bdd1f3391f8b0ac35cde400b00993a63ecc86d8ecd633aea44e1c7c23f443f6f3de449a12a911fa203ee5e33e6bb22ea478c0f055022029c48cc9785bdaa3959137d3e9f6ec1f95f3ab4909669e9a0445de0e290a24b49aa3e826d48b21c47e9db4ff4c0c3937df76b9a38a7ba7b33c586e5f38621df07937cfc66c6680e69e5117953ad990ed7fb2fb562cae5fcfe889dec095603dd0b072076edd9422dca94a06271da3ac212268bfbcdbeb385e2334d3b245be4b8028471065678b7dfa6c5b47e86a6b095a5bfe23015a2d34c9b176a7c0ac38c8155d02a5b1691ee92ccdcd63cefc59e4397969880d62c0cd5f8bec220ac07921314642871aa4ea956dcfd7db9b92f93ec3d6d164d50feca1dccc7ef215ff51dbb6aa24886869cb36a2b19d58a663330861617741dfb1d0eac3eb4779959a8bd4c4090fbcb95601caf89c21508205635ae44d707cc1a2593d15137ef5a9ac199c43c4064b154a7a0728e6e7c707590a0870baf2173d6d86b6d472cdcf1c925bcb107000db61e2f7082fb0b462a68c6dd9589d472a44a9a34a2287fd57959c002a916ae3cbea3d62f7d57af9777a19954daff44c583c0019bef7e7d9ebff5198fd405ba26951f3b8a6f56c448fc8b4874272a6feb0480dc6787b94031df0ab97201793c23ef4531fba5096b15250c17036bb14dfa1172836cb881d0a304d77020112b260c0be35006cfc0bb7ad1a8c5632c18702c576e052d0a29bdfd3ab98c1f7d3588c220ef10a2e73abf921cd75a24b514decb24f6d8beb1e7d308b67dbc8e303688c5c9be70ba8299cfdd51d59312363381c93b5b1deb0faba20710848f01590bfb6abc01fd5fb9ca19f872e7b1a3015e9f9043bd67410fceda3d02b12267c70d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"92b51ce532327a0b175e574b9fdd53d92ac0a50fffa8fca7f465c6e74af2f913","proof":"ee783213e869c3b5a761a36bedeaeb5303418ac6d2e987829e01ab584a615f6bae80714167bb3e84975baee958ce3560109e015606658097bd0b11692a6647082cc5f09513bf7ef98eaa9cb5e09e2dead98627024cbe576a57c30c08d1c1a76ba2e04635679e1c6568e7b36d9f52c34ed69b48f8e33c76f6d4fe6188890954296dd3760b2fea86e8d936b3f0f41b41aabc6d066cfcdc48712a260871801b0f02a9e64327fe9997e5d0f1d39bb7124afd65fdb1212aea81ff1a16e8c0346c790d3427b5c8a7988174ed806bafd9731c6532c69f9f4df9883bced53c4b0f6e900886581d3b471212664be33c3a271026353dc9f6653bd35c378f0ec04da28d6c4a70c9d1e18f21c8780c12ce040e72ec7649785a6efab54014784224ed4dd2413b70d76ba302e97a752e8552feb88317da8b85be08e561d891b3501176acefaf2cd6040ba78df27e63b40f26a884455edd9901c1787d97f441c3b129271b22de6f3005edda117faeab7acc1f3f76be322503037dddf33861d4c65bb47237be811428bfbad2767dcff93a4c2555512765b35c9bf65dc12b9a8c84d48abeb04727431a3eebb308ec47f044d6c619392e936ea3a2a07ed8c3f0f5b2585b37bf8e4f43843da0b6bac92c66b1ccb2218e1fc66a7b3565bfd72b75fa1fa741aaeb349902a40ee18ad7ee36a0012b91918ec68cfd0532e3862be924f6ce7f28c86dfe5932cc0e20a9dd87afba451cc55b5e8bd810e08052c5863bc526ba74db3675e3a16dd48072df541549023b4697d4ce582677a5b273c620874f5132916519513298721c418ec3609123027743a877b8b9e2809aa2f89687d1b6abee55db4e2aadbb554bafad2e09d1c1ec44a479d14ca7a780623a4166d2de8fef661d91fe4bd5b500d5af1b4ac6364c51c3fccaf6e6eb72f1e74837f4aa37c94b2903c067a030610c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b6804131563ba3bf60d5bd8883043435f41a9496bdd19ac5a1d26d1ef6d6f256","proof":"f6bfcb4c6a10ee0efa781d05d1bd9e224c4a211f293e0d5fb642be6ea9d1a277e41c1e60a548c3f82d92262b70dd975154e41fcce99dcf6966e3c270f5d7d055d48041d17480a838f9888649d4d9a091c61270d04abdeac39b4e6727ee39d6476ad74f3cbc43b79057c8586cf328428a23ccd06523c8bb2b330f6b0f2ebb96237273d229e647cb7ef172d9f2f4c058314a3b33569bf0071b7cae6db1d5e34c0907bcc8f0a2ec6d40356b26ce8971117e81e348d9f99b7985a37f67cbd8cd5a0c10b7153eb5bed9759b659604d22ec60406d9ae1dc93e0913b6bd6868de975209e85864663f33fb6a19f99755a167c9b018bdaf5704367c053446e4d51ce5903600165c180cc178fd5f2a2c63769fba581ad90e9854ef98a9b438f96fad47de19425f7713bc16ffcbdefec40a6227c02e97a2f324478e6f8dacecf575b61e9b7202816efe64da2bf00862f61cdaec06dcf350fa58fab7ba43e7c63bbd2ec03966540fbd7a9ad9776f86153f60a935a60be0510dfc4db955be322a4b0df4f1a76502a8d350dfb129cf7308545ed54e7874a654fdebb4650c1f4a5d87fbad6eb8323a592145f965d36662946fa84233d982b712ef321788a3c691583b2fbf505d14803b4da2a904b87f017353767cf25d2cfcc918f3ac872bf6d61dffa826c4c43c5829a5424a593e5c295692d7c589c037431ba4349d09e848921f5564d3f7231e8454a1cd3e3f73d6661bcaf27a935a9502a98cdd09869bb1469f0cabbd719f71f263c38695a939cf26b919a1b0c720c77663481cc7d576157612ed7f2ab028000aa0795c2f86b7dce211b26791fe5111a1b9582c0874de24c7170e49c3b3170d096a1c2bfcf0bc427181c8078f7897efd66131f3fdd26c71dd798d587440820fbe879b1966e31ebaa774eac7106cc30d09f25b150c9c058d4ec6665631ad5e0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"626217ebed81c2e7123e446a2615b670996b7d9973236fce8fdbf147c6cb4256","proof":"60f558b91e21fb68b114acffc6d5a33e9505f55daa36d481795cad79e687ae7f1a36ac725f4d0896ff27faceb8b50b6d29acbd1e074cc933e1d687f7a4ead40116e31338096d0e78652ec8c547c1be81bdd952932b153d0d16e7a07d4baf8a3e0e28c9360d9236b4626c75a623a98931517fe66cab1012f84103d38ef076f93b8e9859dd9469aaaf80fbc8c2162d672673a86ecf2ce264e27c21382c9e392708ae71236e9a6da5fe16f253121e3046f54eecf930f78010e56c07ad5481630b0ed2fa3242627d64a7699ea06011b5973f4c26c1a586b7e6c892d669490d7e470e6cce1504715287fbb927e7fbbb59810203ad6e7d754058b1d4a46a3550d6606af8b71089640097e99e246d439e237e78c7c839557a9a7db64f2bde9001b3636088c702a8af26b21bce10204573693e1e491475f935d0d7c4e5b3290909d6c335b0bb01b189f6474b0c0c8f3d856488cdb455a18eacccba9a6d6fa9b3b38b94163a77d2a0a03141a40fe7d46745d21f39ee2eecbbd5073a1cc92d3226cfb737179c4eab4506ab3d4e8f2aa1129655586a58dd3a131911b91271023db67dba0147563a5e5bdd2f1ff872dbcb286ece0a4d5514e7f2d9b4e474cd7f1ebbcfb9fd5c48019a7ff148d952e6dd902ad58c549b2e663de20c581518fd0acafbea657c5f18ce2fe449812f8c4458cc198be4454826237995ba8f83c0d1c9c4bc6b189955bc385f6cfe63ead2d7e843e46d7217e195fef894b7052733c31fe865744d46271eaf1e0ed1caf0a8579c3e62c8e4b8857ee41eed5d841638d238f5212bc4521c800109b93f681db1582df85db01b6c820b6bb4e1fa341043500517c2ebb3ac2752d5e71583d34c881ba68fba92db2edcdc2ccf3f1bd40cc9cf081e6557cf35031d5887ead07236b34d0d26cdf294910b4386980bb5ead68f4f6fe086675f560b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"90de6e2a0ddc7000ce01dd33594472caabd87d35134011ea544081fd8b80661a","proof":"4480b63c4d98d61703866cb322bca99264b382f12167e5bcc230e1e2d17478319c2f57dbe69b74d716b289887a22764ac8e21b3b9cd31105a336423945e0342aecbe8a8aea9b906d0dc9c144b8bb44e6797754a72c04e8d8f4ba1f9082cf566794a46c887b83fff8402cb5fac5a02234e0b43f36958346a5c912bb7541421d0fba2eadea0da9f8954af5ac39ad27bce34857202fd3676d6c5adfe5892c65e401dc76c25bcb5be280c833b6a58ca6b5b2892c69414c155d76eaee58b918efa10a0d667691fdea1e7488ee870b4b0babb37f40ef3dc16deef91020187a1faa9b076028faa767debb89e7dcbf355791b4866d89ab10d1eb5f44a0585d5ac1869b0b0afeb66897f6b6df81195e6827304de626e3adacdd342ab64565f18816e1234d36df2a0f591ffa33c213a9843e6136f35b1d329df41871d8e485524ea664866eb25a3b5cb80408bcb3835f0dab0bdbd5729c81d6aa6e4b8f36fdb895c26bd579fed6aae995f35b00a18dec91b9380cca93ae3cf556dc6c244be192ebc27c105620a9dd41dc0e2244e8ac5958f000d536cf2edf0bbefe19050c437068103e02493226f443f424077b796f167d5d302a11a0fc3116a11c742bf4222c7a2f6c533764ce917e8640647de1a9ceaaeaa1f20b278be98cff15400f80bcfdc6df32077cde70622a7d7f710738b374435b379bb3949a8cffe8ceb74a0c3d5f989885d252d4d3d06cb157b59f2df4afee1ba3e6f5972eeea4ca1acdfdfbfd9bf676a14c310e6d4f65d6c78d353df05a60e755abd100644cfc18d770abacfc72fee9c9aa55708848a11a724d8e36c5a4b7b443118846c1c108a0b8d7da2b4ad52253c8e63448807132b538f7219954cf67bdf41adbe3408604c61b63ef4dedcf063a4c5f0687fb61302a50187a8570bba83a699cefa206c12aebcb583911db7afd81d22a02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"102b814ba78e81ebabd4991917a77310822e63889f8f9520ab9705adb777db66","proof":"30b63250cf6801db43c5444675e18665e6228f84dc78f60bc19c85e4c1ba760b7ea0434e4569cdd314da86c0434fe0c7aa420ceafd05454b61dd8f9db6edaa1e76905727ed98002ddd54d8215ab50d801cc2698123a2dc204e252c01de8617593ef46226c77ffb3b9a0e4e16655b33a39a8ece704c4ed8103840455a705209403e4009d97a5880655ba6b74d434c8390502033a81b787044b797c9653d4d5a0f1098d79cc04339dd4a0324ebfaafbf4d57f9051dd10e742fd6a86aaf937733033c8a9f953619fb647ede7aa73a2712a74f3f539efc87ade1d75257bb03c4260182ceacec1b4aa4bb02d4159b1ce9fb5db1480fa1327da59421015e1e5664f22a58954007faeb7bb16cee778d2bbba9c058b2e537a45d30d9941f12547b8b8439a2fb1aff4d318ac5b413bb95608506e771a171b086685dbc70b378bfc8f3557e86665521bde27341884dccac0c7a2248c39ed969c51e198f5f3acbf7db9d02540adfa13d2091861224f6f0411262a9471b4407a20fbcfb39376c68cd1102c879a034a9a5e66e0371216e88fb252e25074cbd35216e8207270d5b36cba024230816420a06053e75762b1394eaa1a60c7bfb16babdb96ce1629f6a44db20b6e570c05debeeab78de42b68f3a7547a222a6ae6cbe8444097e8cc8b636537c8f25595eac63effed73aee2f29cebf0cc3bb23a7c1812cb38faca251b58010517ac3352e974c5a64e9e330f9f13b8afe1b8af5247a15430107582751ef119cd2b59006326266f3d929c7fedb751572df173d132f09d3b7a492111d60334f8b2c0ebb5234610abf6e3e2e9b36f9bffa85089b1004be2ce440430a8df4f83d3a9d7e4b17a450a77b4cb4eb3c65924e3805a04860c0bd4bc4f7f612b38ad0ec86926fc506a0c76b3380b9f113945c54a37418f79c71d77ed6aaaeba117bcbcf01267f0a0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f26752cc705cc3a89817cf954877c4ef7bddc801cd66c9edef40ad8b4cd83e6b","proof":"ec6b3d173b4f0ffe13c1dec91cd7c1b2fb0e682a32c2e8d5ab1930891c2375228c64b51c21ab0e7d1208f7012c9500d18c87b5c09b09da054c630e0f3268975c8c66e5349a5885827f35aee478614a31e5afda6fb3ec11bbd58e137849360d44ec3ab36621925b3512fe2397fe3ecb7035fda360e98b30242e23973531438653cddca8a1f490647bc4c5bb85ee17c1677cf6a0fc4a424fce82a6f5212268d007bd19d9792500d476b4a27bfc9f30b6b76ecd728e03eafd5fe996f4b7330d3502ec2bd762176e5426199eb9dfe25ce0280801d81adad5171529af71dc93babb0d24bfb8d6b16bd96cf6e643377d6b22c00c1cd06334c78091a7e876b2dc423b104038e7c5111b37330ed80b42e24494dc2512ac0641e201beb2d705d0876ad13b6831131e1a49e3d4012d26347fbd264571e9df164c6275c98026fd11ad25ac23524a7b3f843711d3dd241e44a8fe548c81395c4a01dd069383dfa150ad3f986fdc99911bc1a9a79746f98a50a532937f095a32517bf50317b7d9d59e1970f46a7cc4bb885f94ee0fe9fb7ebe21c2126b124e0b122a1f19fa124f1ac07e56a714f68e311c76b7dfe88ea5ecc8a264a43a3a8d8f599bf5798258dbc749d0ccb2342af9085f68adc0e8507c02fdba6ea062390968b07732e8078b20a324ab2d5e7a94e7be23f921ba61cbc6706b8b9388693ab2cfdc4c2c8a67286fa8d063162817d4b0584d5299572575b6376ba3365c247e2023d3b591c766be6613ee3105d1721a93c347389e18024acbfb9eb55b38045afd32e113bbb113a81fc65ac5b9442192c44e4e2a31ff9cd39ded0945154237ca5725a8e187b258c9cc25cb870e8234b7eb89828fa55a8e90e5acdf01eea60abb5db4ee0fbe9b1d05c989f89355310897e2eb15ad7786df55dc10b3728344dd7889b87289ef8cbf4e3d692a2640fd0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"70231f0cc691832a33285543f2680cac293c98bd97cf2d5d34b177c93012fc31","proof":"4eeb1c0709b2299d4516e52cd1360cc27447015488a8eb6941ff0fb9f6625361a8a8d99ea4d50a9c8fb3e7de6118da01e923cebd0379678235064b4f2227e15f660da99e472bff8ae2ad59fbe74be676b13148dc4065f0724f5a55a84646c6425018f6faae6f67e39e270925717fec65417bd14e0d14e9adc18f8bf34bf78d7638e053168bc8ae3b0ad6491dac20e16e6cb46bb543196a573d6daf8495dc450f758f3d01976feeeee7cd5ae0b1cba85c884b8247f8d3e89a0456c844517a3900561f910d9b796d5df5cadf9e9e4164e3aba964cc133ffcfa90bd13eedc416a0b841094387dea940117d10e71ded63beb8d369c64fe213f48a05187b38891f97aacd1273ec424c8d45ef75768e34943799e69b413959a5b9cbb4763248703d3604ad34427a44daac541c39e73c6b196cf7ee38122f825d6322aad7d5df6ba67608c3a73785bfd3ae9078599b429d223036bdddc3bb2ebdd3454055f039e35026a44b73973f57713a5c8952add4f9d5757ef489ee71dd02f635d99a593f0ddf322bc5592ae2f28531042b549ea14770875a914aa461dee6d83cbf0eef1b5fcc172be6f07c56fc3c6adc49645d24709a9251b710809807b0c1433dc1ed3142a93063637858464bdc12d189c5ee88deed2e0dc8e4f07c5445b8d33cfb797ce086a208832603f660f4ff041a319522793cd598a71a403189074790dbe3ba2461e0a4f8e2353386602e6f27391a2f64bc9a72833b9c260bda459dd5b80578c36b6e02ba0fbfb4d363f70129e0edc904d64cc67c38844b93b8de13426b9cbeb4e512b6ddaa0e65502c69e3492cb1e700feaa6869a8df82fb9c5539194095a12b8b2f6196b0bed813ecf855ee3812796db50473fadb8abafe2cdf49d329dce193823a709e2f800b142564b18d48f2f81b96070f5a8c454a2544b18e78e6edda6042dbc09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"860521013f4889b2a1d759fb1587e99db1ff475592e16ba21b4ba98427ba3972","proof":"f6abe81e1d840a612d9ee74aaab80d54571929233869e7975fa934fb857dab71e0ee8adece42854be1e0ac2c8274c735084cf2051930014390e64653e110685350d2a22a058d737f586bebf347e835f0a9ba151c5630d218bcc3ebde5dc6a74ba4ed1e85a684bc2f6c82074b08cb55da9cf545e375fc20f08501c0d3a99d7a78fa955265ca0e8e7a28dcfc8bfae7a35f6649c2ea1addbc1e73b156e94757db05613bdfb6db46ebe258d86a2ae50ba9dbc0cfb82a26848d1fa477147abb7784098cc6b98e8a717f4c20cd4b70cb55deca4a7f2f7487f02df1b1e3cd6804fe830bf823b6d4f1cb0c55e54c33905fbbdfcb2e0ab085489a4199e722e58de4634b26fa9c4690a9ae2eec1efa55430789ea06ff099deb63d9d992f7137bf9d242d62d08dbb5bd8d0e26d2eee07688560e4d18edcc5e8591c855bed8c9a78a513fdd121062699fecc8302ba54aea8867c1cef09d268690b488627fdb7f665f45cb4e3a12248e96d1e8b2dd8184fd9d9ca18def7cb99d9fc28e6f627d4e2b1cb5f5b42cf297aed8d3d4401bc5bea4f62aec345e0773321fe67788b7fd5a39226c43383662683dfc649999f192c09842c6d7a4b21daa9e741eb6a610cb0e76a5aaea642dee18f66f407d7b60c202146fed4af57582168f9c48551ce6916f21034a44cf081ac53643ba5ab2a68dd337a3712a321bb7389ded9f0b0ab6e911768d51b29908bca122fc2e5208e6f126b919aa26c65e48721c8e6db56786a10da24ca28e0044ecfddf9104454c56493b2b043c65b35248b05b36c9a82e0cbab5aec57c353d591016cb7e2fbbcc7567dc56cc0d0997e3b4e1db11f3b848c025dde02e4406366208b7be7ff2b1db3e87987d3d6283c89d1af76596b58af9c7865a333553a79000b9668ca9941ebfab60df3174328746c7f2a0cfe3326c2f9380f5c262f3436f00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"94fe83c1aff5f186f3a8c14c110cbe4f645c4d8c7e003c06f44380cb50eb9c14","proof":"944a622f1e0ed93db11abef2f03c8154b92f2882968fe519a78f9f93fc11ec093017b349269f837389c11c6119d7de85705f1df99273a5aa8100175eb31d2a53c84d3eec01d7040e7607fbae22b27a9593aa52babca294ab65a9603757755c2b28a7dfcd762a5dbd4ad9dbd63de8a06485cceec562996f5483f89d035616e359b70b029d70b77dbee5130fea3c4a20e64f1986213072ceb1e8dda5e1980dfc0212a349f3b56d8538ae3ee5b818cb11bb636e1523210cc106b9ab19e16cdb670cb4f603a24e71f81cba6cc9a9fa07c4a311d31b68e0be4fe159338c2b6d091c0422d8488ea4ba489f020ed88544a98a836635f7b4ea8eb078f4dbca3d308b93023a89c270dfac2d3463e972fcf23651c4bd8b40f67ff165613b351ae0a3f959390e62795e693ef9cf2be6f23b4b210a107844b65432b633000930c86f4bb42127869363e6b6e33ee38d69e4b5a8d47f8ffc005077a0fe4848f3fb5945af71ad25d29d4eac512b381ab51fdbb25cfcab305b8f1f4d7a6989f2b693aeb8e51b937aa27f15a554da1140ea3589c24ff432892dc0b1f33ba7c3d83d77c205d0d7807ccc4f98d7512601106b72cd1882e60bba9e1e9f26248f262ef5bde17be8a7ee24e4d412aa7f44dc59839efc0d5759bafe629461236fb126fa64ac42f5a8cbb73fbab0fc7170a4cb155f15915ef9af02fdefe94d62fb18a02bf61608ccdb0410383a8a59f42236ef04eb4ca5ddee828c1af2d2c1d87ffb37dba27642b42a6f183842d886eb3f0c0bab3b93b30294447e18b661287dab54822e7a126c09fe41820ef2316e6db8cca42418b6f0863f38c5421cbc51639bb878f76604fd00f98ac712d5acad0053e84991eeef1a9cf049c28a59fb4cdfbffdefd34aa887ba5ef9520f7763c6b96ea364b2f2358b911515a4b471e5f09738924d27d7cf6067e6efa100"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d8e69476be31e773e9bb837b2a7d9391e273e3b417886fed9ee61dffaa8aec3f","proof":"34811ce1ef5a63a2feaf94c861f88e5790612520f06339734514fc8aa914235ca06530ce242a399fd3afd365837991dae9ddae6c28d41af00d04c10e180ff832286c6c0eb2efff1486c81d4b7cd5099cfc9c60d77b7dfa26598eef631a6d874de0cc6ea5831c48a3095772f18a51af9be7667b728ab56363718c12342508fa4290192c4412a3e071dc90f6098e6179b6c2f7de8fda8d92dd4b03f80358dfc50f4fe51b0486af33cd5ea04acd869ab45ffc84c4f6aff3c341ba1917f6bf48960cd971e4488cdce8ea27edafd25f5b844a16bd8beea6bb98ec80128f3302202f0e4627a6594b92f1be17672d3f36047c5f57c5631ae1c74783729308994240ab26504bbd717e11bb91b6436ff0102081b2e71d5ed13d324b3bb508769989273c552cbd8822a5de0418ab27e573dee21cd558be09ceb1ef2657693cb886e8f5b913a8c3dc86cedf1e857143bccc853db0193210788d6c3b8c1dc26fdc6a602a6915008621fec35474b2a3cac4f16e38ca258a003b5741ca78dc600da7a63ef23736e44400bcbe0bb714116661d5641ad0fa54ea8e0768c2b50f82209d32a6399617c4e10d6eb948e26ee2456cf91cd77b59bb6007aae75fc4a6f6f9227860a103099497abf70ccad07c63dc08ae5a72331b48a28cf17c0426d70dab6cd0526af65964b54ae0e9f755f4a3cf1322c3a0b6cbc8a9d3a01dd09507b157ba7fa22c103fc4d099579043cac673093959d7f51f187a2da05575d8d3e086397eb9fb9d9b57b0357200934661264de98dafa02a0231d05e95da15799e0e4d6d18c40ee2af45d078f537f4111a9a9a50b66f0c4f300879af048d2fc856d8a7243dc8fa1ef15262bcc49d0de693e4f58d3b4a007d31e6948e221d8f262f223bb7db488743a10fa54811e619070650e61ae41b6d390d901ca47019c2493fb1c3a7761adaa5b900"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d819fc56dea9af0cebbcb03a45b2a6756174d555e53a61178f755a1efd70f349","proof":"b40f9dc3eadf35113eecea8252ce47eb1a4675752371bd8a7335e06be7368e6db27a247579e83eec7b2fa5a8509bc13774d8d84c0d1038fd6863e4d3bb79cb011ea28c65ff1c4e11614107b49dc9a8317698e1fa04e683559af8b88631337d7aecf5334a2c1664524a1e416b7a90bb6c98eddc7661ea4cdadccac3c4a192947abdae7823e3130071e75f092f4fd92382704d111e5ffbe0407ded3b3832344b0cfb843df08ae5ca90176ccf229f92cbba30f5b52a0eedc0707674da05b6265007bc883f8b776bae76114e5763bef452a2397f3720ee6d95a887adb874c17da20f1868a61ddb01063da6744bfd614b7de9224ce61544683f3f95111bfd52ff6f6bcc15b3941956527169769bc7ad39209e9fe0776793466aa228f143a0ee770d5fa6323e2e99c05fb36bd3d9a688d9186e6037ebe541f4c95f940790330ad9784cca5a5f564072fcfcb49058962fcd16207c1267f215dcf69b297171be3c583377cac5c30091f99cbd4779b104263d2e1d53dddb8e9d20cac57541435acffa1333f0d5f09f9b6a4a30bea8f68652f8b840c218b7a888f81ef2e78b8da469abe61bc05720700d4b320cfe296dc05ea057eab960939406fcd8b20f7470158ef4dc696a8ea4fe49d16fbd6de904413d68c6fcb58993ee0fd03e7f7fa2a8cb719078258c134c47048abf9cadb8ba586edb1d55bdd52cdec2f64983de0eb832b8ae1c55302a17fd5d845008f1596d039d4f9c78ca19afce09109a92b0b00d1425acd03f6a452864cce3a2c5c8aca5807e84fd3b3de7ee1e9f39d4883c2ac7b261070a18a6208aa2ced977fffd8f7496cad3f0a1b0ec82b6a3c1d7b989449f32a65b251336b3d47fd11530191352675c78857b3f2bd3d51323ed10aa177c6900e69fcd073f56dff935538c0fae9adc0f670305a5f8065d6488ff127fe67dd135fcb85f0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1c11ed8db0352b3466ca1380f593925210efda9d6e2a97cf57a975b1eede8e66","proof":"380b36dc2a4c5ee072ff3a9d18ba6668dbcf568f20bb1924ef2ac625a29c756ec026c4f0ebfe160c8f9ff38b100f2b16e0131b2c37e81b47995ade757cd756544e1ee1edbfef751873aefcac8ddeb9b5f21edfb1e965726c96d731e23cf7e85b32189ddba2c8dbd520f4b16c75f5f0e4ac0660eff97fbf49abed17c7602c9f7a4adddce425fa896d867ebc1916b6c33417f73e70a419601a42f0c77b0e5bef077fcae52639191b2fdc5d35714fa3f1038a04efd69a23ac8f8157fa7382c4bc01f79b3b4032c16b7641ba3519d2c60012719358ff4dc66de22eeb93a363aeba0018fd6b1ba598ee978087974bdab7bfbfbafc4ffd23c3a873807cc83e54954a780ab81e5154a990ad31bd2fa08f4425a9c934d57184bb49fbac601a27714c4e43ec69f11274c350f2a63652d949f3a6434ebab114c952608b9fdf8b191dfe4f30bca28362965102e341f0808c54abb17b72a00ed9b5e38a2f992e1dd91ba39b0a6cfb14578128e98a0df2c9e1ea0d5c2f6fbf6205133294268522f152603a9613943097e1d0a2a19e7ba2c99bcdc3067966df3ac355e282bad21d28651bc5c81bc6d771df24a2e60a19bd51d3867d85519226d0ee689b852e4816ed30e099c533d4f9ef16501f169ad45f790f904cd105eabe406f896516e765733642be1c7569e8e4611769ba25f32610bb309da8e3a012e6c066a384bafb082e8917e497746c4e1952377a6750be34c290997aae4c1f34c9858581c49cb036686e888884d053e4dbbc54abb486d692568d41463a1cbf601926597fc40889c5974d5cfb7dfe4afecc5c5af7271689d3377d4fd4ffd182515c634d5c01e801830bc900e50b7000018900f9d6eb1d108f5d80b5d678fa984f9ce6402e667a13fd9a914b7a69f2026dd2b577b458a0106ab2625fbbaa500bdaf463484c25d2e614286e8c562d3600"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"be210f82c4b633b822422ec81ac093d8d4bac72c128f06964b60eb3cd7e3902d","proof":"6afaac1113729e7128530de0ce148aad7dcd58f10baa99ca9f99b295feee417ebe000d8fe0b688ab8f9754f655d90b40a325a43c571c24b24d5d90722eff3a34ae45c21e1debce124d6077dc6cbbb590d0844d56102a3c94c784371cb327097342f991fcbd68cb762293863b9d92c2b78bfe5582418addfecf05e31b6401a675799f6924e65d709d14bfce64162f48f98f9d95b3d4b335b4cc6213ecf0eb5e0224a83ad2c3acc8577db0ef85bed85c9424dafd6822dae750b7b116a8019d3b0becf8d5a57ae24983ac052d61823ed14ed97db62b686a80e2debe738eb9095f016641baed8ee833b1a25b74aba9eac785eef58b16266c9c4d8052091ce71e0727d07d6e3f33c7c7773ae109e11b29e451ccbdedc98bf149943afb7829212e483034c9ba6f0a4de0daa63f18f9dec59bd2e82d96acf85d995e71bc8f7ff69209177661e448a6491f87b718ca7ea3ad7214c3db2f85d2fcf5d8447e07fd76b3b6682aa00d6c295aa4ed7cca795728979ae9c0c61b11a19405a973329e979d65464bdce26e7d74028e727f7ff9c2d150097034bf45b615a930028c999f2e52a53c7aca64a01c01ca80ab692387e2a7a74b6520d89e1359626da030213bfb59682300f6340619394400fbdf76daabcae45250695a3c24cd4bfdb0b8195b49f9e6eb03d69aa4f33de7d625996a5cb49aecab3d81514a54b5b7cb04940ab670c5b6b51bb6a05bb92f6a6582e04543a826830a562ba111c63be63adacf7aed2daa784d3dcaa34b669b86fe7fa9eff43f54345298bdbf62b2d8ced638f1d9eac3d819a24956129b20670ce8f4dfa35c52fcc85dd44adf3cd69042a337549e785e7bb96d00cf6f698b4ba86b42bd25af65ee704982cebdb52c7b6c2d9b6f418135e04261033641ebc0f2680f9350b5a753c5d116eafa3a99321a5ded6714154b67a0beee0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0402d04598870a9a8c688028ed877048617997ccef0dad088badf39d6a0a0769","proof":"cc04e50e1498ae63d519dee1a6c18c315b939bef3694f7e4b12e54727d817d2482d43ac97a01c53ad12e7127043279fee7c7475934439747d785b4c4841c39512a933f27403758da74266688884a24d7c19c2ab693cd938b6053579a9812731a6c54302ebb8829077ea770eb34b92f91942f1e567440b4ffbc95b6bc843e057273fd6606f03bdb2eefda44cb0f5ee53fef34667720e2866f57febe10b5e7da03f20f17e2451c974345d16b9a40c92ca5ae1c39e281412ccb85a1c8b06c89be0c4aec5aee7bbbeee58ef046d9a76a4cdfa1df8af07e7004ece847c27462010c0c9093eec88a278cd6da3be543b42bdf5b674fc0cb021b576d3bfe5a60930e392d18802c3f08cf21f2021db88b7d831cb1f9012ebf4f4e2f6028fc340df3ecd31a028473970917cce7f693c1cc7d2b655eec54eb2004cb1a873d6e43a4b91a1d09306cea633f293dc70558e86b1dd66a8fbab820957d71fc16e9a22536fef61e6c5ac42be2e6d397e31a0d65e12e91b6a2b5fb3e20557291c0f5fc29ca3a31373e52a234d4c3b96bcf9a1337b34453676fcac7adc4782f5d6ff8e231538752cd303875434b9f5386ca8c26251f0749d4a9c85d2c3e7bbddc9051eb264bf9397b74dca7854479a5fa977a2076af88c6ecbe6d5db6b2adb5fc1eecbf743827c5c9514a96582e3c4616b74d6ca2fa2cd06f242efec17f64413446b6e9bc249286411caad4b554f158880c789cc9c310135cb342dc817992e269ca4012ce238e511b7cf2f72f6e4566c22857179f4e4324d3068f8b70e807186f34ad47da30c6f09f61b84131a2a28a7386292aff96859105e5317a3260a1ae01a48c22dc711b7d181490599d2035a86d1ea0de4df97161ba3f56073e7080c1f81c1b5197c00199450fa4e01007f3da951723f5e892ab6c417e84b2de6695f04d749f0df2cf256c3e03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0ec54efe2220e5c01ed926f12a06ca4bb672609be566aa8198f90305f8018d2b","proof":"84aba1665d1614ac75db170dd9d418493bef136195fd6703c0439da9629c964fd86295f45ec57307555f221e502cf84dc30863e336a2d2d23d32939c49517c53b80f8756835cb35f197f4870e61cfcdbf71f08c71b6816702e44616b0ef57d2bb60f563291204aa5a4fea9744408462f87ab2751b549e0778adc31d9992c967e24e6c1c1eeff7d638f92e73166c7f5b68993149ed9707bd004b9ffe7db7b0d0b5cdf587701ad75c5dbcc25dff3ea94d415b659edbf36896e91b536a0d070f50af33d7ea2d51df79050dbba4e88484e634e09d177ff1c49b0ba46f054b050220730bde0547c7edb81e5eb0d1759d2789e9e966c6ff01dfddb07abedf7a9fe0930240dac32019eb79ea6a319f64cb588544afda392910fdd5ea8250f8f465748717444487f867e81e78c2205852bf211e166640e30ef4a5b47493736c8c6a7e96096b50ab18400fcc95fa05f2edf3260fbf1e1427b98b11d6a96b1681eb5bf5349e647807e2499096c5ca2ca142f20700aae812143a134dae87f3accd071c4e4737a33504fb7fb292b7f3b92ee1473e2d028db992ce06b7c35b685c4f2ee2c882c589e71d518e410433450c1dc65b9d01cd7ae1619368880b74ccd204e027e4151087c38fce4b74a1a026e8f6d71556ef53deb2d721e9e37c22d7b6503b404c760e4f8d27e64bed96199006a131aaacb6546e6a0bfe4fc981a4c096868adc4544a002a5b14bb0dba4204754b5cff0712f7d8497dcdf16eb60c1e5d38a95cb78f571ca8f1cdb41b9c35e48463375ef1aed7b669a495b11cebaa978b5a0d142ec937986bf68b3fd05b037225bf8276cbead41cfbd5c2cbebb825b1ae2ad01c39f9146a4589853253d3d9762f451e2b365beb58aefdccb700ed5a919d74aa7a5bec0c110e797958a82306439774cbc9407d2be5e29335d5d556214ffcfba3134a9103"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9e546c88c7bae056e29c50e7d66478530f147f28f54a7e3afa77bcde1f78de44","proof":"e22e3304a521ce9fad48fcb5ddefaa06691415b952ff51e03f07eafc8c0c1e19dc798cb7d96106cc33491da0f1ace0ae8b651f932e143deff85dfacf96272a6ade308d985d163795349b185e342ba09923f64b23d9e0f05cb3f26308debc00648e4c8b850dbee682cc2100b8fa48594f292019a9f0f7d5fd35c7db527951387bb1503b03bfba7ff2d3e084e9c0ad886ea8bb517ba9398ba323018799954eea09751eb747714cde6b812b0e171a9525e01d343361bcb336c13a4bf425e7740f0868ac72565f9e025ba8752ef03fb7a2f84461b048106aca9122c95be7f26fd30e7c25270e5900ac21d7d7eadfd4cbcae276f3b1905bd2db6d8986c1659ee1b15a8ac35cee1dd31259c91d0d11d99a5c16eda59c8cc9a7d10019a8e5f280c0a502accad957e0cf0f206571402c7ac324d33e58c3ae68467d66ef99af8877f1135f70de86ae9e6f159dba5283988de440b3c7d97b3094f6552b5a7ed1d6b19a60419418a22eccc8ecfc7190bbd0864f47676254a9e2d58458e33394ec50a9aef72deed7f77f882e4ad0c77dd8c94c902b651b486c64acdbf8b62bc3afb9f1734c3d6601004c1e79161e2695f6bccba2975f39d33401f1a4ea74f8a4df7329fad637b215a8fca2c9fc3e639b467d5e60427b3637c38637fdf354ea68e8253593054e50890fff0307d226117bafcdfe2ede123535d2c313534c3bbedb1ec66adf72251e00c1918e877723b00c44eb1db36fa05d624754638f5929923ba674c213896b5046d2b7ccfcb3064fe805e872f9d6bf565e0d7eeed98de68c5a2a7e1bbae573f6017d5414b999a860f52bb50b9d7ffcd990488cf9fb7218c3fd4133d57d71162d07b122ea8f2860844ddbf9ca7f19ad6dc100124cf0aa7bcd4c88efe090b909b703d0821cb7b09b44c4dff05cd15044b581e9c70c9ae7c6e2e1d217f5a1e403"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9e3c5a748d6e8ebe43d9193253fd704aca0d242269fa91c062697de4c5c99448","proof":"cccf35cda0ad86348cbf7a83442e6a821aea40210ff2d3170927af188424d9021e8824b0e7e51d91ae345312ed64312ec6b975a1b69712b8f530116e9a5f58692e37bf09966d88618c1b62be4e9536387e542b0eecc5a741c334322db00f7c10c05e65736febf55716e1201904fe26ff9d305cbfab698cec5b8e381baf1bad159bc172d0a277b65c8448548c76c99c55d222a357a85cc08c6274b5c1947f9c075a1104ce771d337872c79451e7562d378bf59e4bb9ec7113ad7e6514c40f310391f580d3378263551f6ac25ff80fe9e6435ac013cca623a7e0fab448dd96c500ee93c94d58c30bd44170c28009fef22716ae68ee2f6ff35d009ca408aef75a7cdc82e85aa14bd8c1bcb8d1151215667e464abd10074ce8dd4d3775450013d3083699f1e69addb668610b5f140ea89e573bf906e587290ae6e3a6e2f97655101d7aeaf02c5f388830d1fadfdefb6bbd332d95be419e5fc1afb598847cc82b5a19d03675fa35be6b1cb0b776b08f35827ec7a6a5be61d7f647fae87ea38da9976edc19392d0d9fe023b9bab66a9299fe2e3d8c1f45c557e80f97f82b7c736c335f845b262b8cd25dd21b194c30d6021bb0a64bdb4c9f3e7802b324d4533a529b2a6437f5eee37c294bf7082e44b2386ace9754fb4474edc43205224e40a596cb559ef5b158fc77a9f3f2b363c75c22f2f612306b535cb72573254ddc80d0c0ab64ea93869f0e9336bb2e2266510fb30ad477d8c37e4d91a0a2f01194a8beb8e40a443951316b86189cf19f872d5d3c0c362a7db5416ed9c2c544f3069ec498eb3df6ad2da7a617903d2059677b1412d19688127b2498ce3e66c357b41d5fd46b01810db7a9fa033c368006fb3544d7363b697950c6379ffab9e98e54a196c2200e5801327150c527973cc0270d4dc2d42c6fd0a27624bd4f83cf9fe50165a93e0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1ae5d58f0b4fe79784938ba3c2b5999176f756a029b333a27203ec86b7e0761d","proof":"e485c2b88aa4f99022db219a7b93721471f747163bb896fbdcc5547bfa16997ae08d3af5f3c8cc0cc37784bf30b89086e1d5f06b57ff2c173a6b5091bef5c821d66b235d31f348e8f0daecd964d7ac06109637fb631550e7fb05cbfe5d6a0f0106283338f949110477b022b513982f77a5093a1f9e11109777bb6384f829df66de1a72115e13500699bca4b7c32d281670427867f34e3e74247e7c9b894ecc02afb22946095720e747f0a2bead1f7def881e0382a339abdd1940af6078be1a015a4299936256cd2ad0974db7aee87c780281c1c672a8d16682dd7b17c372bd0574db6bee817bfc7dde090b03b44b78db0dc6e1a671ad895edbb085fa10830c724af2cb7666a03c16671a44da1373d4be448d3210d815525dc7dfbfc11ebc1108503c0eb2d607371b38af42dd61e9b40de711e30b9bf35887e1b0ae376be972666ab172661212b5ada8dcc96f769f452090588f0ef3d9b3d03b5c6518aa6ae0100cfacb72387544465947d6dac40956b12f3d062eb38fc400fc60ca561529a14c88632f9533ed3011b15980aae68bc93dbe188b001e30bd05bd463215de40a0575ceae05576efbd0b44d30f9f8f320ebea7a2e065a4caab04d131222bfd783873a8c5f1839178a17c5a00322e049bdd4377f2ab2dcf59f4814f2c18b5f6d7e87934483cadf90c9d5f13825d808667f89b024fd8f8182b3299a8225d8a60d83411a817161eee9a0df5f279cb99b549ce16224dde9b0d0b9fa70dfa4bdcb9588e26fe43fe4d9d00424bc10527979c2ae78aae9c4e49291bc0cbe9758c52e22fe61d3c2785a140081db297e26bf9e2c2aa6003330a91a54c4e6c68c319f4f254f5452f13d82f788cee991111360f06dbc859eaa0e5881a9b8ececd501b03492ce50264b40f0330f62056d82169c8a1c2c8055a714fd9f03f1ca06733706bf416c700"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f67289ad3f4f4ab89e34f95559da4f90075591d0245be01be2242a86676d1f04","proof":"ae47d9851cce76e2bc3a16a92e4ffff58d7d38a35452280426ed1fdc0f522d6d486724d061207f8c7a90de745d09b77391968b82dfc7b0297041b932c62c093f00edbef48b4eacf885641675886016d0a88a3476018d35397fef8dea7d85a13194bf06082d2c734adae40be1de8b2ee78465ce8176ff3f2c4aaa5178c0fc484c7957320a6ac1398aa8d3880581666ce970a49ace370016810ce36289f2137f02c23c732814063037cde6f8266ae9546b47a3c6a5304497511568d91f680ed208b0929b057ab86dd92167742c4d376acea392c7be2eb0d814f2f837434b718609de8c17b5df9312d17a900076505f70ccc0424f189cb445f7a7eaab42c983e7364a3d03bdcf1e9023fae6e61258709fc68d8a11ae9600d3b43b4a5ef0b5e0ef2ccc3c013f5d48c7dd40bdd7928052a143240c844b30bc0d5b74de7ec6cbed626f10335bc4aeb04372c7d4ad7d6c9e2bd2daf64090518789aa773b0534b0c95a7efeca8d82641801c22e5d260d97a68b2c8df9b41dd96551bac6499dad7fee294f0a06aef773f2a6a2ec2615d3f1f5f7e60e43f4d06a8c5ccf70ff9f72336d5758e23ffeaa9c8562ae14a85a671c029867019f728e65959fdf399f66bc188acd6cbe8bd1ec5f05731bd2f73b78730161a1c779788dc491f6a8303ebfd19527f158002328ab6f1f4eb258bcc16c41ef8b584323306ce41ea9ed70127dd9c823004e5259b534d460c57b9cfcdeb25d4cba7fae2e955bbf5614964a8c5ef6bfa52f70903363c0c3e678bb551801a94ba51c60dd10c14679985dce02adbc5b4889ab17d86c5223de5e722c8ca1c99bc55b0015bb09c24bef97f6c22e305487603e6673f858dce6e8257d67105caf8412c369e6e04d8690c966bee391348387a64f340295458e168afee4545ef703dde0a118099f3a8dd046dcdf6424284522f1c5620b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1c8a2054a4bd36425b4fbfa8256367b360dda85f3c9df27ca3565d13e5c4166d","proof":"6a8cbe1c88bfc92b28e944d5b674b59980a94b6d0d6405564d663eba0ad7085d9ed9c76f889533ba6a2ee39cd23286cf1533a24f24c4a8f3de62cc25c22a432d609aa6c4d11c57e22f80e6d3236a4722afbf097fac40542856d0ba9904d70d2c662decf724a8c6636cdfa10173590c77b0cdd3a9f4f711ea4b3d851a1fd9e04cb50e9f4d211905011433880f6fb1639debf64b84f5b40aa218b39380690cc206525687c7c510af3e6f0c87a197d5f77a1f9feb1053b4c1c3c061299dbca32302a1a091fa93430535a80ff3b82bbaa3fc2e72ad52f1c1009bd6a9f322806ab10daa96def698cf4a1410f6c6c528b81033400f5d63d41b636e7feacf6817c8955d96fff4304b70b6c105aa047f37e664bb824a2b641eb51ee7ab12ae33d95a7e7f2ca05f06fd326aea53a564558e2bdc2155f4fa6aaddd280575cea54f133a0a100a8260e23df86135ab50488e2bf2b088deaea3f50488143124d57fcc5d1f4204be265cb9953e15875f3298c96e9229b020d71fe0158538d1e3bcc087c1b85c0de256a213930cc42c0c6e701b062c5c7070ca54e4c68a436123525cab662961305082fe069f69172085e10189dd15d07f2580d2d4f55c6dc8e9fd8d8e46db52147c1bd7ad47146972361474d78db520a7ba53bfe13456aa0fc5f330836c2638359af7c602d8f0ebedf968b8c86cdad011ea9f6b8d864f7049602e7b7bfd1ff319da8634fea250a403f43952f1779a39da2b30f22f9ca796edbf42b37271fbd860ce2e5d2d0000778e0c4c0e7a4bca4bcbc0c981d0e66d498de841fb743d46315d305f80d1cf86776a6dc51dfbf64cd6dfe7b53b828dcf6ae55ec13c07ecd8f459259a897868e6d352d54d11bb4134b3fc70f87b049d7d0544dac1ace65ca525053f9cac74520cf5eac81d65b32eea64e251fd8fdeef19eb4967bd704f7598ef0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"580aa6debde31b640bf18410e0d132ea42e3a1da1c2c3095427467de9fe2f237","proof":"a8ddb93c1d015c0b936b58981d7ae666c7f6ae61fbfec4f582fe9a5394370c4e849c9456cf4173ae631d1fa5735fa5047bd340efb59494084186ec74c49db232682d99299b3a43204e1bc2cca58cdfd35b46fbaa6c46bd0ab4b2fe3045bb9d2d500296d2290ef45563a9c9a4c2f5c944b7c1f0a1bb71b66cfde0e6f4bf5e02567e316089a1add2747b1261680721af3ea3a1a1235ec2ee00e2d5745cf7f69000a4adee2b61c4853e688b1e3bac8e2174271f14a0bc841f61f987e27de23937023fca375d9009b640790dd76881d7f8b9567c8b729f228555b1825d7deca01d06de2fa730a2ec69b28c438cb2ac3bb899e9c2a8f942afa8a843481ef650b7bc286ea9ac0736212a217ec464a1b5d06eff21be25506dba17ed309908052aec337542002c789ecf3b37a97084423feb60bcbe0abce51da627231ca1f2adc188a02b8e8bc5deef89008b38a78e02c03a36a550b4e6ac81832dc9ce649de21a20bb4ea6592f3dff17bb56bea0770e00cefa79efd4695b6fcea1b09c2df3a0e689bf12dc7ef42bb612897e6e68394ef0c47c963aecc30241806e778cc2f7ba5ff64704eabbe7e7eaf74a8d7a68fb4bc784b84856c0ea4818a3de7db28d4ae00c5221478c610a9c0a73d5def8b75c8fa8117f6371a65aa079fd7188cc1df9e008e9b36a4c66157b9121822fd744ec4f9641a9c555fcc8a2a91834c8ef6dd90ee9fcfd70ac575280bf62add98361605f8827fa0496d855a94b44efec2f26109027fb04248c19b3e24cd17f2297387f5c34290b6f7aa0b75c770515221f379a019742e6132cc22b43101bb2753e27d19f5748d6fdc1a1897ada97ede6ec0539e9883ea96f5c13a0d93f741b0b08074c13c9b6d9871ba52177c06e09756351d8ce96192807828acb8bb844e040b00f2825709f87f19d691818dfc95570c53f5fc2f83ba40c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"40996b72050e1b0cba1d1a39a2e325221ea020952f7c5fd7888ffcf97a16446b","proof":"7e99cc87604944bfb282a3867f1e517408d2227bf51460405cfd7c383df2b33ba6a298ae0d84fcd9ae306525ef66821e57b3f480056e197fd65739a1d793af3d56dc13180156413028825db04344d9770067852675df2620bfb10da1fcf89f68e80a3b1aabd51d302bc0231f38458ff8f0c5729acdecd01ba3041c431a540365c5450d1488a1dec4c3ba48406c4c24999f51bfa75eaff55be087ed41c0232e08ffa35a403899d29dc066be80a2518376d35a4a601a27e959dc439fef2c8a0f0c6a91cd503aa434504fa028f45432bd8ce76126584d6d389bf35114bfaf9fa80fb2f1d9557280638e92e654f64e3138d1d580a0bd1401abba6b9ecd798478c73bbe23ed812385744a635cb43baf7264b7633768e68a6600cab22ce915eb600e2b461fb5c2ce3e86d4b95b3d9b44b59113d76e252354dacba3fd0a1aa7f06d9360bc734731a40b18c06c71ed04de7f4b8a3966cda0236d1e4415163113d435175cd6798fab32184644c79fd2bccc5b3bb92e3e1ef3d934be2122f9c810f93289499e2f398c3d6af34eb9183b7de3b9dfa409376ead2c21311206061a08e01755511a803b4eb59befa851f270a12fce8bbfeb17c2573d55d0fad44a5bd88c9f67518054d18a00b0b61358082f6f38527f61fd5b5fea33ba5c7ccf22f2afe690330fced9b0f30b26999f5917560039b556b20abe746263f8a77258beaa4c44928810125b7727d39765b851b46f6d7d60c3eceefd9889d455635bf51caa49a99ca00d221537a20222e94443930da851555105e7786dfc8855a0730bc37e3a3b0e4057aae29bd2f814373ec15502a0c32c77e7729e846626ada2f6e11c4d259270575d6661832aad8644e0d9f955b714098cc693f9e0428ae95d60cc7a1df773d1640010bae80fc8952c260245dfb93b80c6dd77916ef61c8febe554fb1f9121a61903"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5a6e2083c1e0cd133863e8c85d6a8abad79773eddd7618bcbe8f7fc99f44be02","proof":"60120a19656c471db8b92c47f3bd6ea7da3f8c41ff8ffc1ee1537ffb61b72571d241bbc1571a8dc8fabdaa610d1ace5b695e3e8e4f23f4c387112dd0d7e2d454ae083d88501aa7a960c2c322440fc53ec934a93468150f510af38bc36de3c871b819421074ab9f85b0d98ff90d0f3d0eaf213d6f7ec44b7fed7c57f4ca245277aad486e40d1aeb2c101b41e7d810bb7106522f4f84aa6b807a8b8a5c931b7c0dd3e4190bc39ce7efdd4c8df1279919226362dd08a3eeab6cbdbdfda84d578f0bdbcad791f3285bc5f196829ce0bfc51cc09dd8eb3049a0bc603272485c32ba0d003a43a5d1c4e6e1fc53cb7e036e9eeb86114f8a84e91ec7daabad29f492031f7e747b3dc8490a17c244098851dd65ec48404228548fc8c688da76cec0fb834366a4ad3e070072ec2840a6603966747b917a0fab3f5ec7a0d783c27c982818627ce13edbea9a771c57c28edf1bb530c790fcc834d172bdcd12db599d8f12ef6f98e1603a29d1e703df1beb763003d03f4ee1c2c7c3a6d6ae29dff6badbad6c02d058fa5ba185a50ca63713c4d4706a5b092983e3265ca58a1d794b0d735e9d795aecfc32c14bdc450cb45392c94e9e76abda35c92b691193868a32d890f96f461c585cbbd18ba7282d645cd348d424f67736f7ae3ecdd75f64689aa42106ce7a1af0e0bd16cb0ea2ea64a015e307cec632eb510c1329dff6479615b051e07b1860ae387c3e9f53f63338a179b8c36bc037b0bdd926d05bac8b33f8ff00dd802f544f7d3f9e8cee2ffd5955e9bacd1a059904580677c50ab15b7d2ab5096b5e2f4414d53bd005fa0e4226a9fce3e0527a469c9ad9fabf2b149442620b7c2df03d872e8f308a79957351bb49cd903c41e14703ed1c083a44057ce0ba891a292306faa3be9a2ebf3f3c0ac1c255e98271ac92a366722ddb79f1fd6843a3a80b680b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"94fbb4d6bd82e232a6f6ce0639e0fc717b50011c347365e2b3c743555ef80b1f","proof":"b6986ecb8cc063c407aca6ce778cacb347ad21da68bf71684b5c19ba5a42cc4af408d381d0ea2ec8a6495b8907a07157fe6101e020a9f39002203e61d17bb0364ea9f6c154a7b1f8f2b91fa80bbc563b2a59af05cbf148294cffbd7c9ecc196feaf6e4c9178d34b70d974fde935e3cf2f9915e7403bd9d55833f0fef3c30c0755bf29f0cf7b0dbaa326a390367257f22e36d5d38a80201984420de7a5a5c170fa126ee5de3431881886e34bd56cfc8c6bd6a61bc1878e7d96dea3743578e5a0a2f868b67cb4f2da788bee4625dd90f08738189124acaf82b459fb338ebe35e0782e72df49a8e163f66b4b2be9b6d4a42bfb00216512b8ff1cb46fe5055a3107212d804566722ba5165858407361a8ee2962c5d98dc23e71b70a0d91bf9281b7be24283dffcdf5ac73c8adabf7b22edf6df5115a99d6ca99b37f50fe69182872710c31a314c8d198272ce9c13f35af97be1bf932d1f8a9c20fb40a5f2cfc0972d36e48fab50a25a87e8a6ce125e68969c94f30af8fe68e54e1f4804a1dbcbad43cea5bb548c59265d3cff5cb35349d19918fbaa3d1f9a0e30fd1d3db0d2eaa9266c693d336519c73e3aa9b00e78e34a63437cb22281c042b3bff43b15efb68933325ae028a1d78522bab389c2312eea34aa0c326528b832d6234d6507e5c6ab49363aa0bfc04c234c9a35e9f4032a8234ac14b7cd687ce26b7abe8fe1eb6f10570c18724de8e6a6b0e5188eae7c3573950751faf894e43665050616f99b623f12eebe347a15e8aa29a8d456723e0013b8c24c8bac91375924bca530bb748bf1381e5530e1eba6f6b1b3610f6c07db04f8600bce6166675422d4124b84b88ca2458accd706790b428d60414af2dffaaf890b99f720742a908515eda5065594540e078f2623b59184aceca1cbd1018a5c553bc8ce6f14348d2892ebf5b3ca71fb0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e47de12f2d3fd6ec7255dae52802610e09f373c38075af980a0b87c62b992268","proof":"ca0527882007d703a9fa4186f782c95f0bba258d17f0f536e407a3b4b393a31bf24df096373e1cdae3f0f4c7f18e928001881054d527898d8070ed975e0a2320584ee89e187522e4d1373df0ecf2ceef9e2871c9149651f214663df55154aa3a083fe073f35db6a4e1f0f94089341e9db61b1062eb3e306e896947fd04319a0fb7f19caaa42a8da47665b820096ca4f4812ad562f9e79135d3c20ffa782a4109f95d356e826c291c2745b37155da5c71485ae244746adae996b2d126afce3a0c857e20adec13773aeb4abe9064b4858193730585ea75051885087100ba0d9a01b03b0c0395a515b960c87bfa11d7aaea00730fb7c73cc73e5ad9a280c5dda968d26bf8814f9db60da9aa1d732f76e9b4434a2a4815ce6d246c2c24086752e6223019ec1f3e3c080be54a6b74145fcad0c9cd6d6319ab31f21586216de756bc5dae1ed45af3e1dd2f0ea99e283d095eeb7729b5fd8862d4edba2569d9db117e67b4b870054810798f438970ac3ba52a996d7a3b49aa94382a5647bbfcf23d5903046c56ba35db207672175e2333e957613b4a4e2a8d3e8edd7c0ce6f7cd3ad85e68238e9d5b69f1907a0be27a82e87ea9f3c2993dde2252427d61240f70e80b02e25293fb26f1e05b611e7c76cecbeefd04b0d73430d6604b88e72d9efb6cd051c4aeda69c9fc8a8f6a4ecd3c4a7b83e551a6a1e1cf8ae863fea18e910aaf1d0ac05658478d273acaa96736beb40204de19360562728e6d5507386ebd87168a15f8a639c53ec078d12630bac57c5bba30dccfc5ae6c8d59083be1bf180fafc536086570674bc17607aa8bafb2f7bbaeddd7f3fee45a5f8741f2c4ff906403221798099b2395fa3bf26b373d97ab2ba02ec5f1b596d2d0ce43f6c1169934939d0864833803021979768ec59f507a7877ca0798112f10568c05c7a952d15916470b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c8c96607343a256ec0f014ef9c508d17999fec2994a5909b548f29998da9dc6c","proof":"2e7d2485099bef8a5b3f5db10a489197211939f60d45ebe23740153b8b8ede3a421599cf5b21d2cb90ed2476266c177c6444da2950f740c2a72d5eeeaa21054592551721775ba19f2a76094da240e5d885eee042cbc2fe43a7066b71ba8a682690e5c6d45f5220d0abc432b97714239450b837c998f596c4d4b26ac5e1730e1105306d3696eb55ac06d2c91470bd86f6b1a0bd7f07164f510dc9cb68426f930405fa973dffd8bd1af8957327182e6a32c2c77fac9ad88fc4a5adb3cd5fa5540383465b4757d1389f4ba0af374edf6c390ae774419a2758dae55bd26477b1ed052a51c4a8172065d76cadaeb5b525ea7fc6f85b2c471486db081d08e95f0c503734019ba0a930c463316825669994adc5f9e661e8bffa2586fffd53fce4dbbb737282e3a7dc2e39eef679d3a876c0db8d4ee530293c114d9cd0c449e76a96740846e1500f445ba6b4ebbf808fc19ddf72f2a4b8c715b818b7aab81d48a6ef231c0492eef742d8fc8b5196fc4a32badd3c74e98513d97b2894f97e12050536a92b74ef6083311a0d7f7965aa25a47a120a4fdf11cf8ed14877cfa86b70542ce467e80dd847a7dc3a9fa03f61372226f5c760e776a32b22e1381c30c1fe4b4c624374b588941e759f3018c30e8c3c874e0b3eb372b63fb146bd4051e4677fc6430aca1372686f01af98387012acb67a608d4c27ed66db6f5db424de5f0dac45eb54aae85e37cced1d905d4fd4c328f65ef4bb15704b36501554aa127136b2fd9d771a5999350343ce13ddf1bd5fe86f98d553180eafce35fcdba910b8a5195ec41f88dfa1a05bccaf404ea50ddf2cf7f33bf9762412ed52fb395292dcecb042882d037e78a1a3acbd05580eee16e255144b74e4c96c12124be2183934ba6cc6ac0d6ffd7b1eebffeefca59e40787a53fadbf841daa519bbdaaa3a79873fb638a60e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d0caf7d58598d63bf209526c65d51d17beda8826204646c9d4c3d0572eff186e","proof":"2a2df11dbcc56f3f6f13a4d89f734790f415fee92aadc8f4493f98d613780e3c0a82518eb105b715a3c365ed40ea5d7d4377c2ca3f7a9c4c76d375e60fd9670462b299d9ef69351a6e7a6701ff40c11126a4a670010ef76e56a7077b1b207b63c2ad32212ff7963e8f64feeadce61bb12b40360d4085dac3ca62e3cc2d484b46f30936fb5c682c784f0eca6fc5dc2199319c69a962e1e32d015df60ddae738001a0c7174ca8d424580aab1f1ccc4d575aa949031aecfc05479521da1633e080bd83b3f137a48a594d3383c56a51d1bf3e95cab4e547a3f836a71f00db9d16f05b65436b4a0cb3f267795b4fa994c3ca83b692133499b7222c09f672ea3531f78e4362f29dc15019cf2ab0e9e7b9fb888f24be43d49fa097cabdd65e64c711f22f09c5765670f3734d0d92630a80a79361a2c13c225b26bf579019faf60278861c444a020e0f391093d1d2651606037712f0c446036d0fcead332a8037abdd8559ec2ff9898a4f0200d01444f5cb506127c0a02824a2757776903a6c62676f41dc0171ecdb8b65c37934db6e76766fe14ce14f1705734031bb2d3362cf03fb149fae03ffcef4dbd3f8f78bd93851f8aa3a0c996f40a5986bc9010cdfaa971e428faaaaede84db1994c076fd45dbfb080b7a039291220dcc4d5f1c93e22d5c644126ff1f00b323228df63f863a647cba15c2280f398744a29a7b3f0a25785fd64bb4f20cf48086efbe074a92341cb62b2674f0f1fd4d292e42505b7e76becf203d26064e0c8a8ca549bdb40867ab8298ebf6e56b12a571ff008e01d7a263e4650b66435e09f33e9c8d8e6c20db7dcabe1e7bdca52430b6b77cc48573aebad86405beeffc593a24f91fd622b929d5ccac79d05a1717928b5653b873b5959f38f2063a1c144afba4653c5439f8898834472102c11f15bed84b6988c9531f2b180306"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2a8b39779160f416152f8738cd2459d284d18a0dca5dcc0cabd769e18dd02a72","proof":"a0f44f7ac3abe0f05bbf93c2944279711f3d1c4454870ec4b1cc75c763eeee3580adf345d966ec0198335dadae09533736482b0d3b4b69550a79e18b2048416f00dadd2b56b265293ba76c4bd1c32a162b300178660f394504e846b6d052de5efc4b8cce38ae08e9736a19329083f084ff3aea904d89c965f213fb6aa8fdda510c553437a9c175d4b3536623a7a2ea3e9f45aaf07b57b02c6d5437d2a59e5501607003888952895d26e597300f49e69b44c500fdf7f066bc551da4639675620c6c34ef8a061b11900eec4f85027c8293e6b8902229311957e16d0031ddd528062c3f91f086300b0ab69657fe2351218943dd1f786abe7f050bda368dbcc7626e1eb03653723285fa144f4ea862b4d45fb67766a7278e42504634dcae7f66bf2bb263818b48590a79b90644a9103476b45a7bbfc1d21dfa5b10677d08979d6209c03d858b4977abc8a3ca3d011ea548b60bd8b5be762826223158a8345650076d6612dd5476c0d78998bcaefe8a61adac714562f72fa95110a3c887eefdd9f73d660a9740c54ca29b09f4f608fd6fdde2aedf90e5060670ddaa39bf2ac2e07d0650568ed6ddbd6beade7ef0c36f2226fed4fe2b136de4d0aa35fb8c509ddd296c58bbe9c00f7b9bc42e20ae58292c8722fd41ccb03a0df9ce4f6a4a6216727c79f67c0d06dc4892cd43120c31c046ec78bf5eb08d79e25ca4b0ef41ccfad3ef501676c09824345a2c3d0ebf227a2fdd7583d995698e9461c239b58ddb6a8fde0396d80119b6dfb5a478060a9244d6898591c679b1e8e3c8ec7d7e22d0130d942dd07231eb5408c2190aaa273cc7879e52f972506e64fc2697ecc32cc38c8f9e4e5187a07c43928f28915cf1650d5fd6626c2c09baa1affb3135f7f4a1f166b302efd8573d7766780152bcd6fc3c244d85f0f0d965daa97ec4e14292339dafe407"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4aceb71883130436342d0225cb9e24cca3b803e94ad3903df2842d6b8b8ee602","proof":"c01c1b95f12e2ab37cbafae497e59951916be050033a8554d368960d926a8646bce48f4a17c541a3843792173bef0407aaa7d67db34316fdd7c90277dbfef632dec196d8502e39625a46ead1610fb01509268c65df2bf7492cb14e0cf4dc857da238eea346029fa5a1e63e6e437e7dd15339190287d60d2c4b883681a77c7b0dd872fa3f3cc84c1c3619aae642589957f40355d96cd522949086a2b4dada6607fb9d573cabc0e470de8e566d75b52728ff692241f95e3ee36d7ffbf775f9bb08ba72f6645af3b52a2ee2df64084091b53f6186d3b837e18259c3ff9d3246f60516ba9e9fb7ad0a5117f2c20b320520e67e060bc072e2cbe53914bd902754cc5e6ac4636d523cbef8293b708d72bfb19cb40c11388cb2bcf2e4b39e5cc428944e4263b96491fcfa419621342410280f84b876319031f210b987992ae54b6289347629f0183a8a2d9ffe8d33c6c9948050c20dab801f7ac0fd4b012d6391a02f692e7e5349f2eae53a6d4286ea945a21d55212c182d9387048d04e1b6d8afbb773faa86a7919a7336cf7070eacf3c98705dc7fd485c30407ded3b6b2c3d8243404f0546682ffd7e5ad287f7da3d471bf1ba35eaea46186a7c5036c0a8e414d5f3a387857d4dbb87b5ad0c39cc0794fa716a0e208d453531e727916d54fb4fe396802e7b8c89196a0fec68801fc3249a0a36fe6d3718b70a653e0c21fbdc0c52d0af49bfc85a2795d32b761b5ded9a9a9d3ca1edb70b634e14a7164c8cd250712748a82f65a817ba855a1edbbbc675cae33051ce7bc1712256febf17134abd222458ed3f8357fb3fd8a59ab8416ce77cd8309a73c7065f6edec6cc1818d435c2c33117312f868fe0b7b3a508e8f4599d0a341c61fc3994a583b751eb58994592206b0ebed67175ecd0c3b920763ce0d0401c79710f05fa8ba88f3617d3e44e60109"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fc5fdaadc052c7e9266f17e3939dbb19467f97bab46983cca0ff013b00d99846","proof":"a03e4778989658424b7362a7571a6070162f06f9885763eb07d4849d964d2f6eb6837e4683a68dc375a0cf44c14afeb2abe4e07636da943152eba6d2c8ef237540ee9db29656a37c7fce4e2639dad3cbd4216d4371b0523bb5583a31828fb943b2dd620935a047684d4ddcea846460bba9a631d4a23c90370ffaaaa98f0dc819e2052ce0ed96be9acd417852f7bfc0f8110ff2b67801dfaf18219fe97e444a0240d85140c9831031b9f8ef4445892fa4e7aed3f2f68d776f33c6f769d8812305485200b6bd98191865f86843a902e111ba253f61aa73b14654dfd872744cc60edcb4e683c43fd06395c15c2ff3e3ec890cfb1c5bb50535586504156a69f4f239ee626022ecca6b2ffd3196b9d51c8822e37916e8ae80c1cf6b08512ee5af7a005aacaf448c97c95a5c4a3c02cd312030c0f8f751dcba0cad0bc6c1e6a100623dfce442fed20aaa5561516843a9d0d8ab2a84edbf774882e92661be68d1b0ab055aa5e6bb72958c8f82dc27b31bde6d894819c51ad0c2248a1b622c4d4fe41562180cd5b42e4ee78cbaa49b62225671a1cab68e2944d61387d76910ff5f8c65256a3b65af546f7ad7a8ba041b86fb020178c5970c2fb10ee6409cf07b0f583d2a0a833d934e36804bb3094198d3beac9a32cf522f3faa626933e8d16f9f5fd52442524fd6ba15189fa99daa67b93c3d63a0d9484351184a0e8110bbdd13a99b0b581d124162991347659f91f484ef3a395d78cdbba4ee6244c9030d6e436f7f4442235b850cd043994fbee88ec59f620212320f38d6a36b265f80eeea7966980dc4409154b57471712bb04f2a897b183e8be59b955d89030ce822bd47b49b765e5f9d87dad1a9a7db5e12c0b5f66ad213e140ce3cd85a107277de334ca35f91005dc311f9bdbbbaca0804029bcbd5abe827308c7f8c0c2c086c8b2e8ab1a5d302"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c6b0e87f10455d0b96b77c3b76a46f695d6ec097d9e40e66109d1f6a7dcb5416","proof":"a24545cfbc5f33414bbc09beec6ff415bfb4145ed80aa006e0e23d13947b9605e2c34033737b47287467dfd0a7428ccb212d33e4636af4324ab0f9e7707f6b10ba82436233553538280b3687523550256033b8385854450b25f8f7541bc5ce67a0028bc58f9bf585cb545a353c84187c16cf688df67942bf28a98a2131ea7908440b05b4cb71418fc688523ffe1ca39d4e5e0ffd2e6754f59be20b4ae810d3046cea94d39a2dc059021362fa1ae224dec579e586c582e4a1624b2e1ae6c5c10c7b5ffdcd01f260f77e96bfd25b1b7b0a93ce8a40c9c30d2f2cd5c054eb2b23050a1039a3ba48c642f468f31285d28cd8bc0ec0bf6e9d77e29c3a3d9abea7710afa07a402f8017e7691762c7c84caa354be77f8dcca86ae1c41c7c3d3bd8e192da6f450ec118e9f76e29a3298a917851683b151525cb324221b71b32cf5163e13fc50f3640f8b773468a61ffbd041da12d20dc4a4094db97a0b67a130cb4a8d1a5416adf4e0000bdbc5406350214f872d8022a8c73bffdddbc38bbb01bb75500c3a221e0249259b96d9a63b2d6aa89556b833d7189c836233d6367c55cad5c44f284db39349a089be8b1bf6f867f26ff9d931cbbca77adac1430288b75f892648485c963bc8a5bdf112c96b01dfa1515d9971c51efe43f716992bedc6f0788551de50ed7a73102d8efd6018749f9c7c60da40762c052e4b7f57b25db4d5152b600851ccf2e768ad35ecabec3d2f84ea3b637924867ada4df959243528a4cee81ffccf7cf8e489b206ae6f612b8a403d0f22d19fceba52d23561c3fc1649d43f0ee8424c2b9d97acec56f4237d6031debba9ef18b609bcc3a3b89377692e2cc5046ee123020f86ffdac2335d21682165544e1e739784736d6dbdba134f227dd80cd5ddf15888f7a75b76158bd4a127e23a94f8935e20590520618c0b40311ae50e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ee999ef10767fcfb7fff205ad83fe979abcd569f7c3a8ba2c9f6fdcd7a5b3b6c","proof":"e4440874517bb20f1ac8427166cff0a6079b951c3b204c1da2ecb9c8b89b702b0ef5c10a200a4bbe242a4317a845f234f509b57f422987c47692cb21e58b3a04c6eb3dc71cc32528fde9e713126ba3ce096c6c3ec36b0cc2338b53d8475c1e63141682c853253635e23751cd532f4464272e53820fad18eadda0088087d453563e593e9baedf3e29754579d067d886762fc22f61b6e7d11014dcec23de3bcd00f99d96892d04795908e1e9eba943ccfe335eb81937641cc180b18f909aaf5d0203d1b64fc67e33be30652f6e5af6503dba248ff2c6f32429e76c279118a2c8093066c311f02448733d43ae01b7290180ebacd076eee6c2d75852573e761ff630784d542a059eaf2618787257ea8ea89a7a957c5497288be62f49850978aca8608c335004016c28e2a81cc797957e631d08c148059239c665fae17e79b58a6b01ae622a8b5726f38174b2bf6a6297ee8f15a7c0347058219d3af4dbcbf3c69839f8f97abbb8572378f29ac4e05e0c544472d09e006722f8a28c389cb543edc65e102663b882a6511c4d3c71e945f2fbfc78113a9378d0e663c5d1177a683ab14160a2e6971bda897bde3b85fa551e4d0e25f8ca072b6ba95f3528b685bfd4e45038c7148b8966b68b9cf575553ef0324a62baf44326279405b5aaf1389bd4d101e20f5025c8dacde301599fe50cd06ffba16c1aca39fdf4083b538adcdee0656ea8783e9329d21fd1baf4f78f1ece191d32c0b6b60b555e2fdf9b6724e51e23491e4b8436387bb5d7c9e23f61f6bc2aa2fc7fdcda9e5639ac6d8d32ff8e89ac1bd01844852faeb0c45444484a727c8b764af8961de49cf6f2f142d0eec0f0ec16b1644a888171a74dbeba49f92e4627387357325ff9c30edf2e50eedd502ef40d1ebb7d48cdb702049f8c31b3e2537e3a0b4dc258637506c8d2a7e3adc1731d04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f47dc21927f66b549a2305942c2e9be887eb76569f22b325cf743ed5311b5e68","proof":"fc77e8b16675a84f223cb68f73ec68edf009b3846e9ea38cdbe588d4ed46912fa6d89b8f24c67ed9b11a68aa235a892162111e9a6278abad8d0b14f27d42c32da2c82fe36ae534ae1f1b57113c8e7f713c2f73298db7511c6cce044a445ada0c5a53fc3647612f6251650860b9c3e81db356413b07db525267087de50b17c4643d39196371d397fa9f6a9a477fe90e1e11532046eb13e1dc335c128925ffa9030f49dd13c3543c4a636291ef0213b79d0daf25af4a9551c84610ba50de36ac0f1a272d6658ba1e8ae883688a25a9c71737386b963c70e2af44457b8dde19900ec6939016a56e55559fa9fad5a8dfd93d6ae79f9b7ab5b9129c773a9eed798270b696ac919ccbb26cb5d9c1aff879bbf53a5ef3e3adfd08f811c9a517587d947cfa601988404f0a6ad7098e7eaebf47a526ff412bd7064f413cfc75880930665a4aa1beb28c1fcf307e525f69d149cbaf13b6f2f395ab31c9beaf03a7e09b227616a2306123cc897bd70c5e5295ba37c2ccade5f3a6712a6c2e2595c3623b173420e267979842486f38514e1b95552ddfb21be7969f2e3419e08133ae3bf18e7f28f9fb1279a5173d1ac0b1fa90f57c4cc3afe59762ab57b754718154353e80128001c02544da3acc0f1a5e6d880a6d3c7fc1a91e825210ce2d2ad240ca5a4c6cc0a7767a9a9b5441547b112b63195821920c208a7fac79927527408f6fb4d907d2e5da16352ac1955bbabc1cd98e512e90b7ea02826ddb2fd7955f428f5a1862cc7153767aced57631a4b131ff8e0bc003928ffa2bb350d435d3c6c20833a5049a7140bd8b19b436eeb12aad8064544c359fd759a871da0db26529595ef12e7db95cdcb0175063615b35e773c04450b439a63f76bc48d4b070b838747e412f03daadcb453d57e834c842ad08012aa80515bb51eab7f6b3828154b09d67431205"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"eaeb36b3cafe44652b1d1069a2a078be7cbc186ffe3db4fde91438e0d9abee02","proof":"d0e8ec42f76a9f0a4bf52cff27d46b9aff4bcded5bcaeb33799fab4e7f8987720e4e12ea664c13e1164d2adc1a67ab8b07dfdbf6ee1ad8140af23e38eb7e1f17ce0d99464f5f0752adda887b05abeaa66664bd1abfc78270775238591e2fde473409570638840babb1e30c7191d1666c3fa6c4d37110b7d6c2a05ec57989656d0a87f6bcd1e0903c46abbe7cef9ec1c526be5a2326af65e51014c8ee49395404dc8aba85840151c0a69f45a1d89fd3c1072f02742d586b5f3f09c5085032480d036c0dcd5a94fec1f2d6428ca1307b5653abce66c28e8938245f81c58d633308545ff3036e6a8fbb6211c5a42c1d96d780eb0d5249e09cbbc4807079fb65661f38e3edb5f0a475523eac57a5c47f06c953de09cbfd41fd2bfbdceaa22ac8b9016e035cdf4cc7e5e227762257969f50b8757e27630fe5509c3062e27ce2756036dc03980266c0bfaaad9da3afed29ae7b48731c5bd7521881d72190dc2df1e3374a40cbba8d755cdff44d2d563a2aba2b98d773c0f640244b62c517e2756a524cfe47e250cef31309e44db708708b22cd576817d19be4297d0b85c70ee9a42c60a8c4f2bec6c016234327893cd4154b94d2f64c28319682c51833cffbacfd7543ec1ebed7afb164af84c9e61d07bb11daecc106c0300f7bb446ab2eaa8b9e134432417addb58363e27b4812c5c5e869aee32225d31cf913a8ee98d718636e851114fe3ef877a35ed64184a6293dc15d160dd98ce000eb63dbf482bd63a82b867a52284685385035e17ae0d6023c6ce389bd1370f9c32410e7edeb25f0a837036df48b7c3f7a3fc0212cc76fb87815bb1c51f78009dfac4f5e92c271ad7001b63453f15a7c0758ba0b6f953ec28cd0dc243e592b850dfc1e997089994c6434b9086af7954058df597b34d171949a9a19d1fee9060380f2550b02c6b0de6e32100a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4eaaff6e0853d0ced40968d27b0d717b64047b685def90b0ac23a7397682b975","proof":"16a787b7d620fae0153ca2de59d067731e7a7b76f8764480dab3e9765c6eb97fe2b7d3d3fb310fcbf2925ef8890f9a43784f37a1cf1c8f11a9586c2ef0f3994e120ae4d9db6eb7fcb1a42e76a7498d29bbb1847cede892904f5acdf3e148b7327e69dce5ce9bd33ae6e4a1f89ea380d38090bde308ad5a7a4457bbda25019148d85727698828e7eb6da4f1860bf81d9be9bb2037f8969425f4ed3882a63d890404a4e2c061a85f394d36d11c392c051beebbcdf199b89b1062bcbb1c09122a0f7ad1001d5854570c40ac38f52ee38bb8741d36e3cbc3a2f399432369f54c8a0638e5dfe2b021de953616e43898241f590ba6766cdbde636f016abaae9bde4d250c4ab4080c7480b1190a676ce8d3c4cda51d5ac46e780d215a98444f56d7e565302ac50254193e6b1de1dc795a4056d4cfa0c3a02d29737b6097d74e6e0362183e31a7c2444a884c898b1209944fe6a5fc4d958619e693d688a00c57ab0600420e4e9b6ac182f2b090a828cee9c3182c74d14db6fff1129ba02c0f78db9d4f64c2cf29a81e4bc2ce9ea899ac1cfb295b0522d8d47e285b51e6d1d60ea37b3229f81eea5153fb76cb54af6f9cbb6b9d062d72e29ea12dcf6efc21296d30157a3a7eb04f0869a6c7815dd9b133dee2ef3c69d57cbc950c5a86bcd3b6ce96b71d4db64600c4048b1fb3331d2126f43399510af37d0dda4a1e51ecafff986a03e134080fb9e63b87ce086911e0854d3115917fc741590626ae28d1d9cf1a4e8a3d0b78ba4e17677f7f3830fb04e9ac5d8d5d7cb1ec6624393b7120461132b36dca3feab60b5dc4e5db291852fae0aed27f905f58374713e94028fc28d8ec0fd42a417b7ce60d1c6ed81f4500baa3eb5a5b0b13fba47a3bdc1a4cc37d20b44cc10c01b2d2f36a2fede1a312709e26ae8c43afe9622b5298e778f78120dd7036c18209"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"78f3432687c79987e88e98526ddaf8224c8c6ae0b73addd7c6a9c86730ccc804","proof":"d6ec467453942aa61e607dabd98c1eee3ab3c23a6775f79b127709393a0cd1767c4c7446da4f9e298057410eda9344350e6a7be3ae8a74e8d7cff3154b148019120234fe54eb629fb30f9563c4da9305b5a9f4145409d0869340607f9b0dc948e478ac845de2e70280afbfde7d9447eb56e1454af6841c73986c17b7a57983024ef2801902dd94fcf1e501f9f2eacc577f095a0ae78e4faae6eadb3260bb2c0daa4fa93e88eaf519cbf00ab0c86f6c74ac315e44296c5875650af3f6c0b56407a2c5ca649374ea2294c1ec1a8bc6c6aafec36b6796a50e368204dc0ee0c4c8032cfb74c2d4d7509c4dbf9a9ed626a9885a9ca4dea9524c3486bf3d725779ca5a58f70e8315991e053d3af7f68f272be06994dcfc17e1ec5370a39472f226f719109e7cf3bde48076d873b7ea47fe8be30bba9d7d3e0d6367ba88ebb3907fa359949755acc2aaf10ca070606d721b9c60aaed887cb0ddaea4b4ac4aea5821193288fedd7e36f10c7e603203ba458faac84d7da7793fdb7ed19404e9c31c63437a10a1ff8f7a9fc58312e6d7e205097668ecda2b1b09cd6cdd91798eae52b56d669ea1f9dc51d6e1b11b9b1e66c4789e5527aca9e9970755acff31d70aecc9d427287d1b58f7d730b7befc49ff36596c350dd7656339575a09066fc1ebfe64c21acc2de00f50101fa5e0237c5f8a939813f54631cd4e8f9c6703272eb2c5759e21409007c9289132854be92ef3fcc389787b975aa959b1a63689c03d020552822ba2e2b81c42dbfc7d0abc016688cdab007d5e260409eb1458caa7920215f193237096114e980eb0c5f865210f22353c3feebc4df812bfa44d51d87734a6f059588d4ac38cea2c9d7b115c62397eabb0b29528f1a2f29d5f34a43e8c7a2760d901739a4d69e809fc5e903b82dcc9406c0eb40189067083f628f00e513f875d8c0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9220a85b19cbf29f715d0becdc7c471cf375481e880bf4690cd66cef75a1ec0c","proof":"2ee4018eb933de049fdfc24f101ab03db58f94ad30baff89ad24658c944e6b56e60d5534d518874fa5dfd68bdfdae990264979b25a0db1f887c34a3b627e2d1a44510e32320d410672af731e00605da657bc049a6a41b5898429446eeee38c166c6292c9119626ccda7e6b07404a9bd0e51dd0179d3cf91a6bd379f1b1bb1f305e780864f2e37d5c15cc60602c377d762e9d8178888d0706dbc4f87128f003065c3d4f8bbf0f8369291720e3ca2ae36a8c16655a830405bb892e9708b2f85806cb3bfdbe75e8568e7d4103b86a4652816b821029edeec335239515fd65e5b00d38b737eee8684d818d0fbaacdfea8b6c66920a8dee65f1e88c073fcfd87e6e61e6e933f07de680bd54347db6f3342d1981ad1b794387c4dcca310ac2db9925327e360d236940ba924298e51f64ce8500f10fba7d88fea6c45afbc290bf177f7a9ebf851172b3b9d4f4ff5eead1a169b4a45832d1ec6d71a6c2abc00466aa4c7890d843c2ba3b5fac8ea6b16fdacf2e331df9942e2eed51cd55fe6553eead522ba291d3dfeea6536bb4adb0429f471d1e2b75e760caada29e73b29068e121451e2aabcf2af4d2267e4c6e065360194ea027310fcd1a0aade743e7688cc545bc6268ebd89447c233c2d6cb2dd2d5b4a3be0a59c1bc734c51a574dd65cd9974f755e018d5bc42f64f823d6d5f04876e73d93c1628f2d6b082c9567971cbc6372707f07421a0cbcb4f3aeff2ca901c83cfb3cc43c052958b4e0092c0d551a683307230457f287dd0d22f10963b7c6fc9c946b472e584ee7387cb58b067d5d3e14b463ed27b01d3ba40a20ca80905e2c2f2fe0818cb30c723019e5dcad47c692f9603b83b9b087be30ec3ff857de0e689002979549500a5b12b6f4c18c03ca398b1041796ee8e386edc5ebedf41301f783b98f12ab356e6ce2aaa614c5641dbc92a03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c07e77b10f83a3a23a8893a386233d55c5e119053243bd51a9739904db015723","proof":"7e1906ae6819a13a80e3354cd76beb5e861490aebf9a2a4008ff6ad7de76017aaaad308817ea86cbc0066991f3f8789a8b1d50b96b04423bd1e57347bd1c77489ed408bc45015e575a3a5cf5bda3fc4ca3c2165bd5adf1c0e679ea65db15834d70ff9f2f600cae3e0de327b37706f3dcce0ddb6effbe671a228d020daa103751c76dc7de5590e2141bfc9151da26559a64ff5d91fd9153cfc8043d227cc7080d303c1626eda89efcbf921d720f67d2529c01bd24f38ec56e7d9a9143d269400cf37cabd0b9435e5a1ec85f8136f7959d7e48a5571eaa3cf742624ac870c6c7036efb8172f72cad0ff95e4661ecc48a04a87c6fbedb93859388e2c8079580d537ce46b3312e7a2b12519adfc6c77990efa4389541b0a9b7651f8a77335be63b5c224e00dc96d227ed98a26b1708f0e58de3a8b88a68024ae9acce34c5a1ee8f56b6c49ffedd3c86dd1d321d4dff071511732d3509d90a951b72eafc620fb3300640e3fd3f70f7959e7ece99f9f2d3b42295a2bd3ce5c38e96a2de0d983e2541366c3feb750429a51a3a747f6dfeb5a0dbe26bca5350f36e58211fb3ddc44ca965f657a21b80f0e0ee46c187514b93ef1dea153d31b9213add37aec0f2d9d4fe47d053a0a24bdfb5360d176867d2a89739a5e3fc45196b23ef9361bd3d7823242e4e3c394caadaa9fa670e7da28cfd2684d2e936a519d79f1688aee3255f128c46d4ea14288c1cf58d9edebaa30e7750ee882a7ff4233758d3071cf22d28554e3e7027ecb01b555f4fe7a2a5b13bdb2d74c67f0761aacc3fe003f16a6b6cde231860e7020153fda255a6f83a01d3b14e540f4ee4eeb10d04559d63eb354f040f1ed38d1a98d4de93ae39b66db827ac7baf34718e1715b394d34fe28d3c5d132308f5b0c434cf97aef1272e83e63fa20e0c5fc35a540576d387e1296b97342d3006"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ba8abb9e28e6ec3ecacad276aea509255088b828c83b785525c1ba745bfbee4d","proof":"ccd7bfd8c2fc1d8c573869a76666508ac2ff913bb740d99f7dd90cf8031c00508847ef95ce110cb4cf8508ca7f6908c06b160efc56faadb3d5980faa6196263e62cf7fed0865e71439bb229b19009a29c1f48a638fbb67a0079e70d72028122d82af11c7af09f64ac4a90451eef624a0d5a528ffcda8584235fb773a72d7c13fce02d02101b5df0a6dc6f6afee05b5cb3986e2bf78915bb57c7a5859b78bcd0ab7ba27935545ed89138d70f781b2eb18d59fe691769cf0f4da3b0ce2f8117b0ba6d0d32f843e696cfc92e850b3fbde9f9a0406349d1eb131d3f821c67fda0c07cae823b4cc2c6edcdc0ff5a0b0f125ca17edd0a0aa49b8acfe6da32174195a324255eaa3753eff6293414f1b0cd38c13ad533496096bf8acdd4b32ab6264fa25ac921333b55a92b99d9c58096a05a9172281bcb14044e9293a19d1c28741541d4081ac9ea68cfb4b5080a86859f19f4dabc0a587710e13d7943f07f3147e426a00de20981580ddf6abee3aa2fe1275640892183a7e94227d4a810f1b4bd67e2afa5a8466a8cf1943a184e6d289c0a8218a2720bea181f3fbd518f70e02595d6dc23ba7b7fec71d6beaacc0e077d68601e6499c5e7191167bbd4e94d2b91fde780ceaca9afc5e8f0655ab86f555beeb19e8e360c050ebdd7e05ff41057e3f3a0f32eb85ef6448a0f653261db8a2f1cdf9962bf48b07cd47016a2d3fa82c3b8552bc723c8e5533a88039e742b9b6ab8f44accc2e3a4b7d9ec4c0125d84220b791c46fc8214027246a9eb7d231c290d6af193fd9e35bb01f6c81ab485876d46925b42fe29f71ea3ea265f7b69ef875f15fda08eacf37e63706a1d11f725247de47975af54d818439a50e5cb13d919189b0a01af6dbf96e7e7b409ddd2edd74c8907456e64a455c475b84cadd0b6b173b6451895f602ca9e0282bc79518dc5eba50f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2279efe00cd7141fc14d8b3dbfb3c69a0818edfd4aa2ab5cfc2780ad9f827635","proof":"8a97dd7af418bc064a82ddd3c8e236025d7f38fbac8ec2be29bb64a364e59908ea28504375fbe444a350d301aec2b8a3c1749f97ff1a78116eab8b6f661cc8518a823df63141c7a5178cfc1d66d9aa9e41807ccb657dd83a6b064e3eafd7ab48a0d0ac87ebae81f3388767f10e6674fa75b9c14d360544b54ad9a23bfd64081c9190d7e23319f8597419b5102a1bfbd8dd8dcc300817a017a6b93b389dbbfc01331a7b2ebec2cf085220aa21eae454d3428f22b72d0cb5fbd6d2d35582786009a0b0c836dc37e4ccd2b60b2e704b2a937929d5918393ad22b28b06828af82104c61c9155cd5af5f1b94884fefbe8aff852842e9ecc144051209de2ae4c912f49447823b535acff16115bc03491acb2f4401d63f4a5e443fdd990589e8b4e0e2964fef7b8e1d39d402e746746e6f4c5dfa9448b79d348ee19dafcf01a00b2c42c02d400ae8326ab5733a58ae757660530d70df2cfaf897878f8ff0a671077f17b0c990913a6fde0a094ced001556891b65ec0934b9b2fcdce6e3838bc73f7ec37de2f6d167ffff90f76f058fab462f3f6fee3d5dacef76f9332da53e65a3bc962841b9c46f327d97f3657e06c012dee4b30fbf89fad28abee197648e59ec4c842522b0f7d42c6c108229dfdbd67b245e182d11d66072dbba956782e06f448fc33c80c2233774c1b959bf78b04f93f5ac5c476cdce6462a07337de7639ae838014c8944da276df792a1a185902307b3538104d232693c9b2414f0b8ecd0ff1e501d26ee3fa9a775387c01e16ba5839a37334215c315b2719e78c1eeeb6e24b395c8ecc4a9c9b027ef628f04ac7f0275d4cbaf4b6711b04aa5bbb40ceedb22eb5783042b5a777cb69f43d4ee67202b6e258295cee2e2782feec354d2c063ffb110429a4cf2a2e3eead9e7d6f7bdf6b5a9bc5ae82d6b80951042ca61fff6d1232f0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"18ad04aa4843a0261591ed60d1722f64d26edac8636e22d1b8db04ef07fd0a71","proof":"f6ddfbdd4432fd38a0776c05da1674d3ebbbb9e769f72edef9d274c122af967400d5f56dd6a507d2a243a8dd7809b9f2a1a5893e7d83161974f096de9a0e767d40c2760111bed70b27a1db1c361bedb9abd05d7f3467212f6039b926120ad60384809d96bd7919ecbd04ce38536d83be65f0bbb9df43d59ae9d3a17857b6a13b9ae5da560b8b0d0091059c6e37916c76a0dcd0493d9f4bc6dab12458b637a004ea81e59ad78db71fcaa27e1004247fe1f98542ce1e3c4bbd3a6574e3e4584e01d6141603eca76c2e7165c1c0be46b2c3f30af68cb8233c69987ca91bc3e8a6000a8479e4f9bfa628e3a62a37c7dc2e91d4e865c64234b9021633d83bb887b927eee04f7cb14b29e28fa1bc9cb49669f25d74ef6ad54eac555d1b0311db039811aca310029744210f679ca8aa96c8a9c81323e4c615972cb51210b9e291e1cd6f54daa17b8b27383c31e4c7962f99f92127bf63add0e8593abbb70854565a380a98536673599f78a426811f4f46176f18dc6b89d0b4a46a789d50eae6e25dea0f90193c1a24459180020e8cc4869ae87469367ee1a5fb17d119c95b6094d29f425a15f33013eb55ba5e15f52adc469d6044b3845a90e62661045963402ddade576630739b19027d60359c6f43f3538bf1d87ffec20be66f579fd6364072482506d41f2d0b891037ea0c255aa1ae44683a82a3b11001827386b54440581f9b7f4cc629dc346bab688cbc967a8e18669cbabcc86261a550848b2240b459475bfe161ea83d224aa456604ed7ab54f2448870156487df8ba178bdb1bc44a4bf491e3052e793a2480d509b0c19a1d0bbfa9bc931d41d9f82cb5ffe5d2cce753bac2942a5f2ed72a1b929eeca9ce5abf143563877f0e2a59ffa5fc0c7b2bad91a05c70ae113e5123cff3db093a09ab092b08da212f2adfa88dd0ff9204b9ac5b7fa9004"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"68bb08edc4a70e67ca3bb4b72fae61aafb2f1f0b9d8dc32d8b20c0fbd60a8e1c","proof":"34a0fff036be093d6d0c0dd5b7e80be6aaded1174db479d36986bdba91a78e2f88a348f59a72271393c8a923df8aa398ba39b8753d3816ec928cccd6619fa24a60c6d20bea1b5953b686e15fb02c56a359fb1c04535dba316a319634efcf4a37f4156aa278725b4682028c915fb2fcf850a54c8881850a9a280c199aa01572619315d1cdeacb5e95becf71b15069225d21ec1d55b2e8d3dbb6b957f4ebe08703c8898540bd63c88d0c0029084c753493d13bf4db772849467deea6e030e271046c07e29e0bdf79fe1b7cae9396f75b689e3470cca24b47baeaf053cbd63a3e088073582eb205eccec15d695e7058910565f1a039870fe229166402a03997a055885858ce722de9a63e5b2407576ea386e00d5d671a8d935136177d2cf207393e4296fb7a75e497354a7de9b298b1ae3171a3ec11c37a75dd911765e34494b42996c2afce5b9c39001f2eff91c93f9f7366935fa90804b30c7cabeb8deb14774e2c2a563188d6b830b3f3df7f34f1f9e39c4d6a8935f76916c5ad0e2668a09075b29fe8f7e29ad1e8d2e0e93f5cd914a7e70deac3867e96c27d222c328b2873547419b8fc07ecd7fb39455d9646c78335eff0ab0cacec7bf8815bb8f0f0a42670c8308b0bcc12dec42ab55454935b956183d973441020cf8b3a28b3a3a6ef0e2cb260c0cf2c96be1a1d2b53f1f5f6dd32ca2e5e5313ad04a92260ebe17926db53984c0f46f8af2c657d3947d3c7e6cbb03cd9705fe616b5cf4e7fade22a296826f8f44e896ea9bb0e3cdc91636fa27b4c3795a58fa5a1ae0a4c0893ef8a541a18424a6767dc652da55a16c180eb9babe1a6935b28f47042297707418c44a167126466dbc44a9451bad6c2d21fec7bb13d8b2f8153f0052d2db17e83da4dd4470a894706f91b78f03249a413e031a0584a5935b7dcea07b42d9236341474556502"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c00a99739dd176538bdeccb47dda00a88d63bbcc4ff1a876976491f67f636271","proof":"40041322a8316b042d1d889f5b120a3e359746aec82a949b46907fc93fd177165628df4e22bb769765f196056defb721f3499f7c094378e59954f54ef11b8b2942b14ee63c0e6341ae54aa55954798b70bbe44a606d5225b28e87d5f7057f73988369cb2658a62f519e2d00c1566c1b0b5a838b521ead9d659fafa65131a2c4a0042f0849b1f05288a234a47b658021f13d1f99ccd98c27ba4ec772ee0709103d839f5849f50bb3e5e80888cd0dc6f856bcf2635352770dd32f3200a1925490ecdc4347e63de47b0f2bf9f9474ff58a6c54fd155a2439a6337b8309e4d9ad50bceba6a7bb603c831591cd536c756009482aa888671f9b8e5a710ae9e580fc2217aa366889d4194f9001ae114e8b78581ff6d655aabd05f901a0a52195691d37dfcd75dead13364a49105edf0535b7efbfefd9f49dafed7ccb31000a7be87df3b002b6c5f7029bd77ba81fa471da4aa64b2042784a3524fed07af9546f750150a0a8469f07b38f45ebcf0729447ed2e662ffb5b3841f5af543feb2cee8e63ab39b2e4a36586dad53871592ce2947217266b386bae9a80ed72ad3c6beb13ca864cc2628baac022a6edcad56fe43b4f85d5f6acc95b7c2f817e18978ef961d6f759fe56a0a734169cd8d1fef3c3b4d3aca35e7ac1711a11da98dae7f4d18881c932a8b3a46aaf639f6e4ac5f7766f42851d2c27522e568d3e0dde95126a1c90b043c6d6ae926186112c4cc16e94dd54f2c64988cdbe04590f16ec36b956f1e26109b4c655c2e2fbd3b4fc286532563bd396997198f67f3e582939fd780cdd534a49aa4096aebf9b70279da0aad95f03faa446fc51bf857a377c1144800056779b60b496cab7f95e06780331f98c7c9c431cf6412b45ce00f92bf97dedd6b5cdde0115063abccd0b9d48cca4e5aa79e8936f618e91cd6075c58e5dffca57f239b80d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"cc58ce90befecf6a077df210e54812ce36bb44769655d0db6f6e8d89f6ce9d0e","proof":"143f216cb7d437432b2c989e7203a31af2711d74a523fa0eed252f3bad292a37981c14593c1a4a0cbeeffcaf6a7911da1a6d3f1620581f5a9440c8ae527403118a8bf73f22fc3dc441660f1761f1fb930a75ab4ceaa1a41f6a8d346e12673e5e0e49fcd9176c878901cc2e470b059613c4f97be5987568a3a9bdabde4f2e23559b560d089d6bbb205644ca3bf0d5a6c9071152b38c84a746466fd474f2c12102a4c11088d4b5b59c0289276ab247d2c4be5ac8a92ab7ec548b6e741e9e40d2004696888f1ac4f557e80889f24c9d6ffac52813b0ca5eec4124c3e59ca924d70b38e63d6f7c79c50ccf02047adf439545faf317a2d3d7e6c9b4b84f7d51132605fe49c7f7159445a558452f3948aace9b8fa17ad29affc2eb4104b37a588225277a8d5d2272a3b9aba41ba0ab8c42225471e2cfc025dfbeb6113016c7caf76f51bc97edfce76c2f7e0de58c0196529e6fcc83498543d763a6758ceaac21e5224520c282ddfa2f352729c1655b8dd6c7623cba0ad22c2b12fd063a86b229d53743143d6b3a6966ce5a55e1979ae2ca73e9b9f15dfa8f7bd219813dd3876d3ac853e4a2d62ec2d4414b4f6464ce03ef2da83881c6b9c8ee393b7d06d8facc0ebe2336d50f7bb0850b6e12ecf01b9dd28573a22f02abdc33520ea34f62a1a27bee1ae49944cb3df29a1a4c2fedff1f716250f8866abb8166eba3132cc80afbb5634d4c63f75852861fa1df57ff383f4bfa0cf3fa3f8ad5a884ce3f8d9b0c1cc516506637147ff81d9371c87661d02e77833d7a4bbc32db55a6686da6f728a7ac9d299ab73261e3df6c17268499cbded682ad895376bd52473808f8212ac440b2db6b070e8f4100d9ba4dc8c65d9020cde62f2c274b4e508cfa28c132609f3fc26e042515ce961b44cff93d04653c933c68e891290fde3654a86935341e4322da9500"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4442473b4b8a1e77cf378ebd0626cdadf0e4b3d0c67f09a327e77050510e4d31","proof":"9ad1c982a8eba2117ccb80553eb95cfd1e6dc454c469683f1d5764526213926a709804a32a9818ecdbd7bb3a62f7b50488e5b623922afb0fe29c5e373947ea3d56d8933079ee08c135ba0916fb6384864a27f66080e8d50143e738d6d0fd536dd6c4564ae14248f76542e9f7406cb0aaa7e323d1695eb088dd68cc359344b43a9d69bf6b0f98478e5a9c23b4fa4ca40c9e3c8fb0d6473b4cb75100b2ed40b704027679e82bc6ed94acade08cd8f2a9cf78b4943662d8728c76e08d56c280bb0185ea4a79fda5590e139a3850b9bea36f766e92e91b754dc1fed761146666ce0c4e8c646c42ad1ed5d2414c18401fced6408ca464e9b2cad79dfcc1d34411d90902d43eba9552b32522f1e0d93538bb820057e6e659beaa99070fc4a8b5320f7550fb910d38acac9f4443ad5d86360ea899b4b1d15a314278d93becf212b5cc68122e5457ba53a08244cf71a40304e7ce76de1235c7bae375a39cddc0e840fd0612476fe97b59d9282e5893fa7051a19a1d002441ae7b3b0f7c5135121eff507f123eb393abad606976b56fdfa61e21de10d45f7adc39bebe6e0ee22416e0f53ce00cd0c2ebcf9abc0cbd16d05b7a4279d008d802281aa41e2cebf527e16a9d7aaa24e6fe52953e3005683a94816454090544ab4c38e2528aaafc93dce4a9106d64a9100270f4105f35ac0547bd1a6338a62c05148f07e00c6a5d518f10536b6dd01d0dec7987e74798e823a826470711efbbb6e3a1fa2d23a8b22eb54508cc6f32994d64a7d652291dd407bb9a69c67376f39ae217c4a3f0fd1e94b78c9dde3572621ddc68262f8b45ca9c5ad3e7a9d99e0106463b60a8951b38546ae017183736615b90404de47d811489115bd0215433cd6999c65556d5b43d16dd4724600459152c88f5b86ef321df76492df79edb5cfad3f063b0145a337acb6b93561e01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9ac1de45c4a6e17c9f765f9bf53e15c35f28d1a65d25528457949c5fb12d877c","proof":"2ac80a67019991e07ea15916b0814b0f50d781c8617c52c95acc78d00538a4232477d63a145d8f07c6b5d63c34d470d3a88f274a988dac1f59b26835d8744b2e4453966b3a402e8cf99a9bc9e10e857c0471e5f55a4582aa2dd532a9dc034534e04ab397b44d111ae31513e75c90370b716ff98e55a102f41e66a005d448355edfbedc012e09052d1785284c03b4328dae5c6caa8e84e8c84a77690395f34f02e01085d020cebbd5d043d432752d0966ec1ccc1f4818f19a7ee6b13e8752c109f35f3a8bada929be1e8ba62a4340b1ae4eec8a58f2dcaff89a17371fff4d9805f0e8a69fd0c1f32fc1de291533ee34f305a271a42da83c51b6ce82a42787f87c32995d4f448d5caf0e3356f5df347f09af044dd4faad3509fc64cbd417d46351e88b01bd4d83ade1de363d2b0372501f0a455f9fad06ff32c7704ce030cdea4ec24c77dfb50ec3bd667be8b2ddef8d8d7c67bd9a4bfb0a5f1f515aba3a9975235019106274100fbf42475080f5f10dfc9931fe726f0d291f40751d9ede126d642a85302bf9349fe75e9cc8a70532827cbf870b6a10c8501aa14ccb650b469e02402f94914d54d8a1de22d2c257a62fc13defce8bc527d5f0a86c958a9e1ec40b0470c94867e0114c58271f79005945303f9e027d79b84b24850cd8df7b5e091254efbcc06f303817f8b3284857ad721cb9a7c115bf5a9bee9b2928c1641dea219c4592a0fea6c7c5323ed6fc0a115303962efb6b101d47258008b068c382ab75cec38ad203b9149cfb905a0115eae52d230fac7873cc608334d0f6e38ecde276c27568954aa0db2d732311e9febd0da57c30afbe50546be28a139eab60836050def1f697840f139e1f3f18ed286e9d6cad087b6d9ae8989cc869fd1880ed7a0b07ade27a38960cd0aaf8a11bb2d906bb4e425156935d9467c19693addb16620d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a236dd78521a1e9ae6ff99aab0f1e27382474a6dd82e6598b82cfd06ad1fb53e","proof":"a22a7f78082dc18bed68773cc8fc05dce2657b38d5713215945e96874e16da60c2feefbd10a8cb2d803d4543a93975733197d17eb82ea42ac78b49460092ca1e6c94b2ee053869587c84b706549d9739adb4ec57a3c11c2e7b247d560ea6e57922f4facf2fb3e6145c27aa2c42121764a70923f316d50484ff634c482428c863d245aee50911a48d82f658d9e107ccd09555238de8e79857d0ce25e126eb450aea64d34ec7ec9fab80f4b9d6d6bf5006aadbfd32359d189513e1ef313c9ecd09da8a78e506ef9243040220e00350f0049f316940ea018e88e4eba6e90d5ca807cc9dfc9c349f538ec1023828baa9f459e7875fd2d2f8d6d3c33a206d84e02e42f08a5d4db33a3eabcf645f830633b2a9b1e27dc079d4a723c8a7faa72888cd283e7c211f83cb949ab6fb1de0e6e25c852cc4bf47369e830e17eb18de13f2da20d249fd58b7dcbf3ffd9a0372be720985e72c1fc0a43c50de14f35a4e0893f7138c01dc1bbae1b486fcb9183b21b930307cd0c228d80365c8a43e723bd89769238cd4edbb185817e46c1a115642604e71d2d89909bbf18ae27068fb217c95e2060c359a7b6b6cbf08aff4f8da7420a2cf24de04807dfe4cf64826dc93fa095766b4065e76b5c9112b3fc3cebb8806b5220bda55e3d174133eb5b4dff272b31970703812eb4fc1de81dc3c2c0178e140801d1d70114b1f161532653336a6bd5c0112028475dcbdc0b5f3116a09692af2aba5ee7a9e22e946fb27dd5cf703c0ae679e85a6bbca4d46b5a359be816f19960c9dd00d584f6124b8f72318ee5e92f9551a715606d12cd52f7b7424d31888fa4ebcb638d1234b3651fe333d064c6eba54b90db965cf75a0d8cb531d9434d49df38a490258dedf6f4987a86195ee6dc4080b5332787d0e0317ab3c51e50ed5757cbcf556b5ad7ce099fbb105faab43da06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f01e867b3380d32ed89f77979f707a99129f250ab06a898aaaecef29f3f9d431","proof":"8ec5782b6768a693ed2421abdc6c3968e3dfa3c7fe9cbded0922e433f312941170107256fa48b11a3dea5be9e6edf5aae60cde8c621ecc68ffcdaac657dd46541884fd51b6027c0ddc37533b4225da3d1314b430ec0cd31eb8b6d0b6b21a8c7d16d5311d4292208f6dc1a7a0335f8adb21787a2e18c154e1f48803573fe7f6780e8fa73ed9606e6da7d196bba6b31cac628134904f4786f72c5ff042fdd13a0b7cc07c3cc24b18abb85b6fc4c6db1461bec3f78f99fef0a0e29a8419011b1409ad0b6d1308851a0e4ba0db4f78f03f2c976d2bc9d0306ee2ea2d0cd5fba25d067e5be4edbd656048826a0ca223da7e6d83353ccf57988d00aefdd0d342448c3e04b617140ef7db03d69341ded0357687910b3c9029b69452b8d229bbf85839750e507dfca2b01b26df4cef3ddc8985620465a2e8e0c603e4036e8b13339085446a955dbfbe220c392a3bf0ef9334c522785a978f4a33590a2b32502dbae4c303eccbdb8f3e54d9d6257cef99c9c22dfa3de1201709e62fe67f06ae0baf673e73887ce84b62ce1f9477b9fd0107066706ed1104e38b55f926a89cc015263f1d14f29c3cf2766dda4a06ebc979d350c554e48c0d822e5ec57a1e103b75696d5b61984285f52086cb1cc5fe2503518ddf5499787b1b2edc44d8aabb310d4567ee627a1be203450504e32599850ac643f46d191269ceb03f8bf224111f296609636c98b8550fcc30f56349bc47694c52c11cc9bd2d89043f1a55a46acc3486df213c52ffc67f1e67a53b6f12bf683d208332f81e2046812736895ac82158acadf344203b9e3d04e2f1215ae0b791b18c0ba0368a4d46b8bcbd037625d8e02243a43c2c4e63e440df8ebeb51e366f43801a552b618f24b282ae85ce6952fa9d6e720e6556ed8a4a0ce6d7dd1283c88715041696a91d593fc308ff16c4eb44430ebb0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"38f9afe156d593af1f08492c9fef9595c79da38438266b843ac025ffcb513543","proof":"6c938ef74d9702f7f10c1903f2cf9333324a638b5f946740049a769ad29b4339d21dbf824720bf38e97d484ae0ab1f0b95cb865386222d78fcb26e63a3a4c54b424e42b15935446b26fc800e221b40b6e56adc8a3a32bf67c5461549979ae738be6cafea3fbf744a6b80b0681dc8c548994544cb01b5d77f852ee96da732375bbd22724236302194c79e06d1384bed4d0278754593e327fc701cc42a8cd01008cb44c55ed4ef744b5ef0ded9c575632131ca30b872e300f36dc82b8d094f6207c0b4680e9be9b5de301a5a82e91c7f485492bb0617676a566c615bda9c87420e566bf431f9c7934909995c2cbcab4b34b2be4260bf7c31512a609d9a67fab02fc412e0b9478948c779dfbd6188a09133106bfc44740210e5d234d3fbaf7d0f1e889c96d8f831d0b11a553efef3f309ef3b7844ff68bddd15c13255ceca113b1ca2b3f52811dbc21dae1632a2462401494ab27df24c10d8ff00e06a60d3a9e85254587d4e73bf5f18897c64d6c082a381267e7235bd1fae4fe8a75bc80a17732392fccf0f7203789c483954e4843069e31de1d8d37a2521b33c08cdee8192730424d6ec54b65e356184d9efe220518a2e92a6c3f1e9fcace806e87f0958d530544e7b2a9ebc54deeeb0ce501da443378d52534b79fd5ba57f322436295a65812652a5f14ad751f453b7e9b4d6128996c5a0461869d8d52a117431e52b72a5b365e84d925fb0bfe0687486537550b0d6d8c8ad12f36737719e48a93f7dbccd2639da300f0565aff0d6c6456994242e5d216a7c015ff3c8b3d39cb33cca4355583508ff02cafc916bde08e234942472d8b0b068ca2391417c67d2ccc12b5a3fcd64dc415f25c29d0aeee1b14efe5edbeedba8aed709a669b82308211494bc471a0a0bb9ec76bcec27c1e011785b734e02b0583197459f9c44992823cae657ab200c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e269c382b12b93583fc19b102ad445e41ca7eedf606413fb256d76b8ef54cb41","proof":"8c145b388f031be3081dafe8268cb75496848f11308018a5372f30deb7accb508241870219b487957755abcbe7be8b2fb16342c859cbf27295cfed6506c8a10aee5c387265fa97682b9778b7a325b0df1ecf2599bb194bb92e6aecd62e1e143052f1ac05409dbbe048e104373a6136a17d6c4ba35b1b61e1b153906b4805035285b8a654d05c40084285091df6127219bf4eb2634dcd01e4ccc04b7e74eb0f0e86fdbc549db47da9577ea35fb43d1dfc80e7d68ac96b313aefb95c22fd20420557846712f671fd670557456124986d74c4cb8fa4033d00b9aba2a87b373cfa06d67598497ed7c432c89ad7094cbf1235db559906108bb49b59b18b9ca1998d28b00b99905f9e13ec07a6e6b92bf4e1df1b645d7585633979d32f74c20573a3556eeeec0b11f516a2d39fd8d61672f3b8fe3759e6d981adc16660974519e7eb11288a4809f23013dd1340aba26b778c5433c099db7ba4082a33e2d1adeb7dbb68b81c6bd8cdda3ae54fa46015d3f50fa84b725e5302cb3947d6af223e638ae66418bd8ec89801d3a497c3773b126a04218212c609f0ec4943235663d8493f0b5410b2f11d1e8ef7d727c58cbd0bdb7fac3bd20478424d7474d99b90e5c8d7b5778a1a4f7e8fd01147db5037f442df0adcb2da191dafc520b17daa78dbb94bc22d8431baf43d29de9b0f5beaa6f4fdcd479c1a44368b1f336c94697e29c1154a24c8630e516cc2d69e5969447cf2ee936edcf1ed523eaf1001244779ab9fd9a215d88dfb40e2c8b6093e6372d8c2d76f22cd5cc332d18c36680905a9cd3985753bda079ebbbf5744b15c99178651211c5391de3a047b4f0a3e27a99b53ba1c7b36811dc19678720f62804233b47e098b0aa2714cabc8de1fdf598dbfe6933f0e05fa4b351f523b1091d28b1afd8c0a5d6b217e184c5ef6b7d4f8dd3593ec264c0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d4e1d42daba634bd66e860f09457d9d3fb196261143f9b68c2cd4abfc1fa4471","proof":"70597662f01de48e4a9d73a38c0581038f63b178a49e633267eb069c61472a3264a316d6daef6675ab850a637aeba0f07197c995fc3523519f5f2c15a48d762e966286eb73f5c2db4205747a139210682948a4b62e12d543ee4af71d5a8a833f2c7262baa186bfc2ed29b065b6a91f25cc830cad78beaa9f745d9bca957c521fd5bf33334878fd79b005a99afd38ca111e2ddfb277a9fddf3916983faf63f7086b483527a9f5556790bc3a25ce4a55eadbe17411be662a985fea5a55a90ff20aaa2231d5dd713f7405bc48096d59d43f450a6cc2475524956d42496d6c37f90ed49edfa4aaa98a22f93101062bc06d31e2704b0f47a34a1ce8b8be0f27f59a764cde39ee8727e46780bf999390892e0e45cf9ae1470e6e514e6e1c5bb265d924ba61b08d7b8524e2fdd8c10ee91972e75bece2de6d9bfa052732c58736e48051f84935edfb6d6160f30937b6b5b5f1d0da5057e5c59432fbe77ae91311a2b14616583fc0991dbbac905df960bd941c79bacb0cc512a9727bd8b1014a4347fd1e18d067790000b32164f3b1f0f0b20967f23d7be655484bc95da1067380f90538885894d9023d6b251cf132444a18a876c3bb84c5a848d3e50234923f66390c7f3c8a2b8fa75fce86b12842fce392da767d38caac1e1c109c7ca7a7cc4840e65b3044c41aa2b063575d3e33ccb3d9e7110aafc9d0bada11d0a947526e9654651036fc3a1406b0fc2ced909e752b30c8900fff44babd3cd16a8b72a27c3f61dc15146116f77226d26a4ff20cf5594cedd22c085af13f5b01603541746c34c5a97dea4069244fd3edb9b4c3909a6aef129062bb30a4b26d23ee9cbb0d16cd6fe65b4e0fa9f2c84c86bb180db876357bc9195de21b1703d44218e29744676bedc30c91bf7a99c6e2f1cb3b4fee745006785906a5d7463180774e566f66b9ef550807"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"401ed20123ae46d35370b7dd5cc740424d369d820df0ea916b1ceb2a756a0029","proof":"4c4ee99577bbe332508c70a0af64165f6d5a4a7e1e8a54e61560fc5d7c015f0302404b12af863db12b0135bfc274d8db37b39c32813f6d5aed0e20582b26f5680e1b6daf7ce8c43a6c575fa252ec511950fbbd6417996026249dcc750aa20110128dfc45dd93b5f575b5cd0e926b4acef9ed459250ecaef3577bb7cbceab4a4821e4a5e372ec932c6f95f0ef773620882129dc9744d1c097bf44d33325c4ae06214a2d3b33bf0f9686dd93f9a830e9586499db2230eec9d1e95fbe8c1c5c090501afb5eca8f7b5dfa61df5152b354ae06f17c97f7854a58b7f45a6b6fc444d050e5ec82b59c24fa980581c51e24a601129d57fbf21f81eb1bd1ec0266057dd5392bcf3878d4b9fe315a7971181c69e6b31132f98ee052f1f8e2203c4f191100c2c2d4f46ed35e35cb772ffe911b5bf6e4323923f3c4a9549b25bc2a2166a2c18e46a458624cec9e4043e0e2deff09f8a12d5afcececbd75ed16d98a2825b5925a22d2b272fc0e79fcc92ddf6d5a4e8d5bb261483a4c1055f5fde3e92fc09280130045d9e7bea951544b5302c1a5af55a433f2b8533851d424a4748fc3f337f4b429f1e5204454f1803ffd08c4ed39abe4331c0f83c53a4b9da683a1456b3ca1cb0897824ca7baa5620645a90a8dd43bed394237ae679432b9fdb1c5f8642a65432910727addfb79901f58f42a9f4b7b13690e598afa08d94ffb52104446f2929fa04e2bd334b9a21fa95e18f58411ba3d890e5fb0b1975361940184a0fa1547c28551d7701fd4b74165e9563103795dd295025f1aa903fa42455a83adf1f7e1b14d966d7e7f4e5f172199f5ee939c1cd91707dc4277ca5a0f871e313b70d324ce2f7a5298cd8f31ecdcb9326b3376fd671a501f89c1aff1ad9adc693ff99590f1eeb7448e6719a611ae24eef80f735aa08ea50fa754ad79bbda36c2c5f9acf0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"68a9887dc6bf5eed66245bb7d49f68eb03b66b32ef24915ca990d91c91e76d69","proof":"329ecfd31dad9399045a15fe88d0bfddc7ab142b93aff07dccbee71229c4b92afe33b989c1873adaa6619dc0a9048e41b1f1f2361c9a53ce2d36b4d112e9307380d158ab5a1d4b97f36b9fdb88d40cbbbdc18e1137794c500f49277a3043f92b744816fd4b2d08bf290ed202c4d579cfd1dd52e87418677629d0ae3e6faf872e05f2453df6997f8c2b80840a76ba1aef3db4ec7b6f3155a7ad1bfba26b85dd009f1593033456a0b843d0538f519191f69f2bfcf3e04f0fcb74e0f264b03a2704fc060ed7e13b1c331fc60f26bca7becf66ea3f93cf83a386f30ad7165c97d80b26f5cde8845dfadf29cfad09af372fa334c7065f3f2b9eea8149994cdfb4ca60e09a99b66516587a861c09992a70c7a8c84b538c145da78294389ff4023ee1751e27ecc4a8d70c2ef67fef4e166602a00cbf5fa93328653a7ac29149a58a7a142ebad7448b042147fd3a1a49049e12331a713653ec503e234b0f49b6f93a98337e96ebe116c82bfbffa295b54bbba5442654fc28757df0a8ca20c244b5ad945d30f8df76929187d6a2ff7fb17d8b3a5c279e5110d18c3995c9cf1fa20e0fc47a183b45796ce076ea0283913ae9b7d1eb9d23faaa845b02c5df199242fd55d0734a16df7e37159038ab6fc5a97bb9dc2602c42195a211ea8faec3af8dcdefe5118e798570e4460a8c2c9b727165f55fb084ad53eb4790f965ab998297b851442e2e670a4277fe57e42a062097b41daf8b7da41b7ce18174dc972c0bd22cd341464247b86dfc415e9e71c8ca63ea1a991dbd78b6c7275e9518137a868257a4974ddc21f1560b22d1988db1730af1605fad2425d8f8aa2b4d0e5c7dde0cd62a1609ced2c18c56a595a272c61858818818f58c472d9772bc6053b1b7a8fa47c6a10b39b7f743f2d0b09ae5ada057fbc57955044dfe3a23ab73f61f7ae2acd486d70c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5290fa577ed0b1b39b1f019a318de86f445c87548c930e7cb4e5ab7602ae5618","proof":"6a4fb55c58c71c87149ac5b0453d3bc197b82a437f5bda703d576d5242e3af020435137b88e960ced5ef531ed34311e4d8666343add6eb36f0e97ce044ca4350104776f5f5c76432a3251bc32f7044810bc1d61aeed99169ec3197d18fd8663f30fb7cfb7012af76a18b3bf1de3e6bfc5e1350d6fa6c7628c775e1f9a2b5af4cca8aab1a29dfb40ea9613ca70e8aae02e10a163c7c671cfc108ef6bea8212b01a515b875eaae5f36471cd47acaa306df54da9141e9a50ffdb915476550c50c07a17d858803690fd962eeb0a8fb39d7b8685450b341857dbf27dbeac7d949310302004be592c69184dfbcdd4f9da4bc63d4490ee9b63e644c7405d5091253e149444ef52037437448bee16e1a2250855187994d549c40977983c4825afdcb09678e554751fce723cc57873b79adefdebae255a5ea097f351aedfcfe0a7e426c09206058540444315fac86fc5eea2aac402e1b5e5b4135d7a38b70a222c0e38a07d0ec32ad14ac37217e2390715eab8c9524610235feb7e5bba081f1e5ebe62e506424dad3844dd74bea56c4c754e368e646bc6055ed36dada1d41b9e92d57274f9cddb02c95e601e97e475d84b6d8301da7bdfa927c914e5905aa0130e868b43874921572f072fa5ceabb75d4d134e6b73d0919f00417293fdd1d3889b6be062ceef764d994d70f11801866845b74e2425b37a4e6052461cfaf73eeaf14a4d47fdcc7c09a648f0a0711a92b7fa9e2ad935fc134a14715b5278a227bba60ea834deaf9738edf48529bb69b783d2261c511e30d8b5f36091ba9dc1654d8cb09362c96744b4ac84b704837916c66f3428066fc30a2605467caafe9aaf6f7bf4853377c606f98e2a4d148527c578e211545453918d9bc5ac4eb90883468211839e30a8f349115c7c5e45f7afdc123db35b199482d35d433800775408583f365d03303"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d683f112b6dd802616c41b724da64b09c8e425a5399d764bbeb929930e44cd61","proof":"8cd1b7283a0cdff5974a5060ec2f779f3e4ea7bb72c63b7920018c74cdab8b1812c28fb377ddfeafccb8c5ed967fae751936fe645cda8c04d4fcbc66165684422cf643fb381c5b882a9fa5cf340a7dc33dad8538f3c9b5667281ff65d2f3424e3232fe11ccde8bed4d9420d128eae64ad4ea035ebefd90c7b0e9f1a70a318d61295186b4c875273b5200fc6078a81b1d8efb26f615317a6ec90af26857d77002050885024bde4a179f6100e80cbe63ca590a1a0a5c7eb2946e32c02ee669a20cde58c0ffcee8d266945891f6cf869d20e466230b91db1ec3fdb382bbe1a9c903be81f1c7eb107aa7f14e55f6bf08b6ec89fed858adccaee18592cc09e3e927009890e156140199a3d354e6ae2243033265427d66dc9872aaf076febfa3ec6b219aff7818da6f768e1dd075d00571bc8bfa3347479a953ba1fc7c56b6d3bb9e3e348037ed66bf85b4b3b500a436f28ab0ee4571e663517a462def8d9fdda51f05689d5494ca678b0202207114aaf19cb5f840297c9c553c54751fb1ad4df5c566185c5fc71bede69a83fae396edc19ebef2d4ee76e1872b52ac8138e808bb7526a489cdfb10f960d6c2e5d30c90bffbf7c743ca7c80ab6b23d7f9f965da24c017da8a659bf77b92b4b62f8a8470d44d4a2f28bfcf347ac91bde7165afc420290b4c63630b9753b1c30fca261ddf12a3c7c657eaebc4e93b01ac5a13e78e0241155e4ce0972997387bff85ef5b1e9564ffd004d8a5ecb8343846c76164b18771421438d49f5acd594e83cbc667ec13510d5572d1faaf8b6b7caefbf4d8a7077b6a5e7e34b8261ff48a3671ccd182b87e72c98813791613bb901d709c5664bf0f6cab1363c59819f88306265832951c59392459dbfa61667674c5f2f0c3b8522406d4444560f703e84bd80a98c9f3690e570ad70cf7e9046c65f93db645ba9f8508"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"244cbfdafa3992a8395f1b5020040c80a5ab8f38b75955cc7aa6989d992c8e7c","proof":"127d7781e7905b347bf040eadc7f32bb20d73b97f18f81a282cfff2fde202a0464b047c309bb815122c3ecf9b0cadfe6cf84ef7a0465032d2bd377ec9c27a2132cd9ca745d9efa000a2aebb3996db4ba2bddd02597f6ba7338f73d1c5304bf19fa09ee153f08dcb19964c8829e7715a186ec2e7fc52cd81f7e3db3573db26465078bddd29288ab859c8f60507ce454e22864fb055c9335e02500bf4283dc3a0471ffe54e0356de854735090cf14c50acee452ac494df708e4a74a18614828b02ea3cb898fa9043249dc25d4c2262ec4aa8920fb9674c4fff4a5635a763f4530d9a8523d1a118cef18154f2240fd60e7fbb5aab2f9d3d1a876005ca2d973ac161e2c66142af7d4372517b6ee2cc0dc0419404ccd2b53e476fc42bffa56038d43f7a68f23e60e54a159d989ec4181d5c0eafb535bc00b09eb1b84d013a91732b71aad654bc5daa2fd8debc3ab97d9561a6ef718c427eeb7d274918da9861aa403858ea7ee2d0b83e59b8f2eb7f411cdf05eeae4cb4461d4d48e879c39e3882361c1604b221d2f428885880c2dda101dfcc662d9b5c14f6642993d4004f3a9ab05738bc56d134ebb6e3f733f1481f89a27ec63cdea4bcfa9bf78676a71bc6906401d80905ad0dbe89ced252f1c6f79fc2721a0f6d94ff657d88fd492714927d717c160d866a4105c76f6b45590ebee1d624957d64d1ddc775edd03881362ecd1050764a9f5994ed8776d2f22a508f161a50e2b8c2371493affc8829ed9062d5b94a52a111f08a9a652b7ca4faa8d6402d27aa024a918192f5113b3f8b16444d7e0fc02b694f6ee382bf3cac2e2f81a41e46b0ecbbbf20cd9a3fafcdee8302913f0fbbb909f4426a26470df4b1255fe4f50afd04c471c948cf669f580cf73de47a081519258e31ca6fd765b41b43152c389b71128129a4783e46130cf2b1cdd37306"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"de1fe3d06d50333e033f85404de9d20310397a0ccb8e740bc4cf121212aa8c58","proof":"10b8b4f1fb93a33decc4b80e491e50af0ce8808b8ee60addb16b96f6c8452328f858b49e81220e3a5ec0a3ec43c1930973c2cf3a9f042c57e79a9d239f593a01a21471818aa30e97c53fb558ee73b99bcac3b483c091e77497bc6bb93378bd281a8e7490c996b5c04136033862565ddbb982ba819eb00e5302156c0110c7604a30fab85a880a24e0aa71e51a334cb6c3951a607064ae0d452e1be04d5cb1db0887c8cd1899ddf80aa9908c86eb3ad162f02aa23696efcbaa8651fd3bb14eba03bbed5f8409f2812c5b42d0cc16d3401446d1be096ee24ce85dc8b3f1d4b64c0078d2f2975e67027fb897a5eae8dfb5e95ddd66d1a42669789fd55be3ea6c87737c54add78434f15142450286b4b66d87b68bb5f9ee8fc97a93a95736654f797db288edabefe67f8480998102d3b838e12265348b3182f4b49bf13732a34e1c7f406478095b6b61ec40f908bcb6230b0bf3ee0f85694a50adf1ebcca9ba382640b206363ed5b5b7b33fca52ccadf217bc66e9cfa872da438dc06867b8ad00ff315e1ec5f8ba2cdce7931004fdaa81c730de0985d2b53dda33fc5527534d73eb7ab6041c13d5a2daa75b2324da56f4bef142b84de096b82aec5999b0d1e93e60167640f6bbd5cb1d80bde4c97e95405ba6c8dd2d69df758b44276da6167e8ffd63bcefa9eb670af02ca80e81fed2ab32d5465ed7eba199e7319f46673d1ec9d66778e7117bd711aa25f4ea23b6b809c6964a6a94220da25a892219c46fa87cde31fab99856eb6c104ddb97668ef01e8396b5cd12be71cb18b4219cbe15f398a361cead96e6717b627947c6b167c2259bc36f5ed2218c0b3e0da4f6e4fd5f2a342acc370235adcbce7dc64bf04c088d86875662a3e0744043e14d410cb8082b190b1bc6d8b04d4545a8473a470c7a35eff37644f0940fae3ef0d5729b3e1d0a1404"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d8c17f6bd158a17dea730b0ebaa24ca4f579852cf6afbfcae82c7093d3956439","proof":"cc891b383c10060d3a1287a83e4d17cca5af36b5c128be1882785ddf4a4f316de64db110ce1dc1b673d7f2ecc78ba56f174cd883946dd8075e9c5086be10fc767cd01cb2489fe0d63280c39244ae5b656d569950006abb22833776e3c23f5a03244d546dcac9383f6a8754de58d23c555c77c5f4a076ea34359c5139ac4f5871c7c365ea0dc83a7f66865f96f27009ca1786c21a56a5bc8594fb2096a1fc3a0ddf1050e8b739585633fa9cf454c024d03486bc06443cf4a452c5a37ae9e9840b1a48e07dea8cbfca7db28f140d89e34a5378a755e8b99172c07e795eedb31f0f00b8492b182d1191c01d22bca00500a7a7034bafd244f4cc73448832663c0c4d3ca34cce891e388e57bd463de87642e40a6442c4b92776e3dc0456883f6e954e226adccc5074d1c1e0fa25b88c5986dc0329c502ba55718b20276ab27d230c00ba582b9c86bee18ea0bc99fe9eab8db881602a999185d8e56a49064db35ba86b88b5dde0553314e9816a5a4b82ef60701de456c5fdf3d6f3af7834c2975b9b516096eaf791596fc6b1282599340ab6018b9e4c169e0e6fdf19494824aac917531863d9f0e7cb5b47815f315946230dc2527b2112360cbbb9926b086c3bf74567e82683643bc6b15dbb2839d42cca2caf26969958623c0dda1fd84e4f4d818f7fc88dac0b17dd87f5d4413a93dcadf7b699c56630e1f8b46eff846a20d15781729460b3c1d87dd22b891a4805cfd38dea9225c763d6b76c1d819d49cdf7c5e4300aec4d1b997e77c9b734cba04499b96f66f8e18d15b2da6cf5ec08b92919a75c543fb9be7622077ecd7507cd39782b25dfe9ef56b0a15dfdc6f4c29629cc36737ffc4e1487ae717e581e9c71a433bbfbd035c2f5db23fcef331736dcceee7800261fe93e865ce0e25729682be6615e10f7f3caac0c4678ea1ff625676ef7b90c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4e81c80cbaf37e558178c72dbffe77fd0831e37ff86e39c1f88bcf16777e087f","proof":"6a36292963aa092f8ef3bfca8c6c1118d3a15582425f127b9ce7319e7257a239228e5d7d9bbb51f426ac39ab722886cc9c4dbfde876534573e3124d7fbb3504e0aded14a989a13c1a61c61a182a284a9546862c8b0cff2b9521660333bc36e6590a9766d8ee8594476ee6cd8788ac7352cef3c26a84c41830e483fe1ed9ac83476c5d1693635e563d90117f29a9e81fd8c7b2ed87ee5fb0ddb17280056e2600d30d17d23af6ef4e47a53eb86c1eb2a31491124680cf876a230da946f9c620e0561e910fdd8e4005179fe31d9dbc905806a59de4c3f680737a8eb80afad1cd60d76b28fdc20212322a35abd841a4cb0b6f280e52daa91976d67e14bdbfdc4c572c0622b126a3be33d361cfd566a852ead1410abda18559be5fe5db28f7bec92285c3b9c8cce60ed4176dcd145c4f8cadb96e5a4780b4e869b9e1b7353bf08d43ea2bec0d2cc49160e898da5a93407de29f623920bb76bae57be5fd7d08c99be669011b252012e7cb6a91271308a9a5552d58d5832ce3f7a9be210660c5b9aab5e7ad62b19799fc3502ef6d825123e9168f3476140c47e917d4295692ae208136eb4f8520a6df7a54a6087f8bc0a023b6b53e34e4cb3973c830b6bd00e5b815c348e7d16468ac0d88581ffade3a183acbce7a47e18fdcaa453ed87bde672d6b6688e1ce86e78f4876a2750062965eb0e3307e490010c0fbf28b3afcf6ab42884065c53116bd91e17ba50ae4625af227429102fd18a18bfffb486dae5607972947e3cbfeee440fb31220b07d0a37771235d734474d906b078193c70416702bb230a02c84ec25abaae7f1b59213a062ca176e4f207b5205afe4d642cd7197cdc1436f053c0be0732089f2a88333fdef8388c30fe53abfef85fb166b313677b73980da72819df2388e963408d8fe46f315a518a3785451d3f9e8b599cde98e4b57709"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1e64a58ed45beae55a359477f862c14102f32886c5eaaff90552e418dc405546","proof":"e4606c1eca6141b20871b6785156a0c354f67d0885f216970483173c13f3f80f141d48835ee4a9622d8b154c7269a3c78ba25417cdf5d5bff9ab8f5ad542aa76f2c9814d6724cef475eda8f07b3c5fbed0ca3ba85e0b39e1cf41f326d95c660fb28bf3c287bcb163edebdfc0ffe4bb0720703140b549570144342aadc275253a430a232721a3da2bf4ad85d59c950f7fad214108af1689c41b45e6fcad29cd0ab48f9b55f26c9beb5a2808beaec1225c5b1dce646429bc8208442832e2d2e606deebf39cdb1a9de0dd0d697b933a32dac4b4d633033788de97183571f9287b0dce15a2ccaf0c9d4d8097545982c3c273a95905d65c32ebd4cb9066c8833ec967cebc8c2d8e10fa73b9389f96bc6233cae65ba1d08a3e9b1e7dbfa50e39401f6f527c15ae81624e37d637207df1c917cc40d413fbf720953cfdee4c334c50ce31a02e2b628e45cd5d58de31596c6c250433aba1b826df1ced0d86493f363d737322b984d06d1e661763797985344d767e00ff6cf07aa74d33128cd8dde4995429ac9e0e1b0699cef6b6f3a956f252726e846ea543bf3243d3c05fe16dcf995116565a951d17d081dd8fb6a1ecc0b8e0fbec1966f68e31ce8052d92a34039ae31d2efcf6be484d2efd43a736afb3c4517a6d4d5d6a4a17dcaa73b973c764352f6f96b6f34a46729440ee1368473822327fd752fa6b628938570c4aaeb8f8c90b49a87e30a4580f84d8bcdebe7447b021fcb8227db3569afdc3006b2527defdee010eb7ddff70cf7925f93d9dc49c1a8ce6076c094df0e7689c4e3b6dda0f0214705226a0879857798fc1cbaa2c9862bd1a2308a99181851763387e18393bb0fb125095f550a16842b651442d8ef65dc5fd54a7bf2de6cd1769d6f3ab45e90a9d05c71486ee24d0ba59a404f991ced0ab9fee6c3138e15875ac7402f48679e10c0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"22e2550d3ed01b493d2dae046a89483caac955fe114fdb572146c8e916b64b19","proof":"dcdca36aaaf2f170dc06954ac0200e767973aab5d86a2db5766e66243a43914020d800ef073429aa09e84c3c4275921f8d037005c3b9d1821971077ab0c16106165f6b2e226d6528cc913d7610f5569457d634e57f02787d0cf3f5005103f64c2869ac27eb76850f8cd81d62e14eaf055c88a0a028f23505f2eb28ba5509596c34a952f2ef3b15de0c58b6e969a3b2c60cc6f797c61342d46538704cceb31706bc3202a598d4959acf1c2058a30d51ad3ef01df4c01070134c7ee3ad755427050b598739918870c870b0f6c7e85b6b6cdb6c5d44f2f6d6288d8d5cacf139fb05b23d3b3119bdd08c2ceaa80d49b94e6d52d9826a03267a2b790bcab7b454ea6fe81f1c5695a008ddf18ab1ce052129ac3e0449eac60969f461586239e22e8d6e582a2bc590d016a1a9231f8adcf768aad72efb56a8444c3eb82698117306b042cc92e3ebc8bcfbf410a816864ce0de30f03a8c1b4b10f2366deead763f39f273b43f3cb2c5bd9c572248f1abff7e19aa2e0df09a8dc13bd31db4c0be5705af73dc8c59e2d3f9753914e3e025e74fc19343c665d915a3cc7e1ca4c9f24dc8f5656ce9426ed734b12aa1933b18456b672aee7a9dd070b7736a85df44c777fcc971b6264548df357a67bdb2e0daea5d92af082b52919d8f22c9bbcf185158908c0a02da9ba588ac4a7282a7008a7e475e619e5395fd7070d2fc5f36141620c5ed1e6241944dec1be56fb8a08f74971e2948ebd93d987dc8010863139c835346df6576086ed538c9f7e181689c4b01764dd8cdfabfd1493d0915fa4749606403233d86168292c22e70b9296ae429289e0cb91bce876edf8d53510f88f692e9f1f55aac72821934fe81365404be051e61cb44db398f5f61f26af013a0f2bbe06e9d04a304995b3eb08dc8629007d1be524d392c9b8acb24c172927489653445473b09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5eecf4e1897f239cbd5d830376e96d608e45050fb169f70258362f3028dc712d","proof":"1eb88fdbcced6a7814b8f94d1f62521c3fce034637176104796ea02e94ce390e1085b1ffaa73cc08b4b24c445d83dc98ccc4b0866daa6452d1fc3863b5a464316e4f06df8a0fd829bf7408f7ff1a51e3354563e2e87694a12aebf72e9cdf155848e2e9bb0a9f50e14de3a94cfc278bcb428f32163287b3c24146139b94d53d2a7ba64896a6ad67066e0f7db50d47f2d36d99112ee4c735b2b9cce742b3a6090b93464aa308100a0a175370f7e192cf9822c1ea8ef5a235e98b0e0bc8f6847708d0049937cf88772adeb634f6502ae25bbfa616989470dab61d144c9bc1b19404d869e9876e109627cd273c178c1dbcbd0b8bc08b335fa06f464c8d314470ee6048170575022a2348be9f30b2793111c4294e844cdf67002ab177a56b8e008070bea5fdcdab7f14819e3f6e511add45278c0f3108dea62b5e9f7495f98ba87e7762ebc860ea02b4fdbf363cac1611e94eac22f9887fea4844b4a604da10032031a2a5b10531048f15524d13b10958b662abd460ddb7a428ec51e8382dfac9af2510589eb6bf0b6c3d662a455939f554f1e04c55c7b6be29865ed94ce182e36f360ae2e2255e23230de034191d445e24b3c19315f41f778ee8e20e25c51a1d39710437fdeb0e2c2022c3016147897e9150625c950f65e17261a770739ff811a93118578fe87cdc5c16dea2952e378a7be69e72648b3e0917ab3c28e7202b84fc7656f0e7f01f0574b83ab86110063da03b1c3f700be709b0f5131f377d115bfc25b2477fac0cd20c94ac56bd6a7487c72e8772fd01127623b707fa29fa5d9ab371da683f99f1ef24088a6e3859125f648ef9b1e8bf3dbdea3d2424c1e220514d6afe5ba45a3225498e8c5118748dcc8422c997f3b19d71b6ccbcb14a98673a310ba1629e632f756d5aa58a9d4301428e1a22a6da13cf360fb62d02a487f11b600c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"82a636d9fc3bd486f2597a51470ec653d4ad638df9e9a850fc2cb72eff776e7c","proof":"62d1d3154a4b7102ad0654813b9d9c85d9fa1e8c3c6eba46b608d8e4203f8b08ba93e5d6a3beb212ec7cc41eaa014812180a1e11719f62a436349bdf11ae16551a4c7580f17f980798887e116d7adc2fb34633a43323a37732341477d8d1cf6f181f55d6c25ffd9d7e6c14e5d78ecf4b7afcf9e0791723b7fcc2fbec767bae3d6e0630a43bb498e7b1a5d923f8127b049a73ba9c2b2fcb87df1b23f544b37e085f9e28a2ba58937d6926f67eedb35a71747298ca6a180dc3fd63076368603c0f87e5d0b57021b5bc4712bdb0f00743809e1bd9e1082b46dd8842beb582c3da084cc11faebf03e9f4511769e3f199d1b7c929dca20a016ea3472613715ebed16b40717ce973ec3e5acb81020bae9414f1ca51629f6f72c27ef668b69bf141fa17f8d450627f7f5419ca45b11235371f340f13b0f52b48e7b9ae99f176c84d722176f2819c0774b538c3d2f92b6525703ce85aef66efaf35c14f80f2527688a02ef8771493e1a6442ac92f2b144622b03f2106760af940fc2dbb48b7f71daf8e7896466eb7b0c6e4fae5b5c862d09bd46ab8692cdff03ddea7d3a24a5bcceb845ad85f9aff4bb47b894de4e1515b0339361a25af9659d4c04a6c39e0dcd2f16e12d44f9bf238fb75ffc2eea2b7fa450caebae4ce258e98ff0715e78697cc2b8e6006e5bb8e5116decb30016df954afd5e304a91b7359059f19046b636c86e5ac47dce3dc0592352e3ca01b39961843c20e572d108e8122e81424b788f6cb93f16062c55727e05ab11fc83eb3f848d7c903e2590cc0b517504391222b9db2ae9a5bfedfc44e61b3c2adf7c0f8b63e972adf31a6066aef4700b014531ed73af6564882f2cc8324584ba35e0c1d9ce5b9e80febf88c11e64b77f5c6893cdb7c622c085a9766b04f227d6ae2eb96dd1d30c05af246bde229012c05f8df472da7227d06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2ab88716c2315953d20505a812ac2231112dbf084c351a18aea04f7d6f2bf34a","proof":"6eefb4357d8244b159e2148886bf9d4ce4eb71d508b35b1ff18c99e6e104be0168889584edf427b1046f6ee171b59a1bec17d780ece55ad11b51f5c62e398340a89595702eeb631bdaeec98114ced305fb490890ed1c1901bf43c4263f294f08a835ce3b69590893f2932524c2453e436c590f4e01d5cb823acc8a5c5bdf6c66ba6b4e6ff0b4b397962865769ee55019cbf9dd5f3c3a96b95cca3bb4b775ca08b0bc5d74b15817569693f9bd4c93c459610c037595f17149f79b27dba0251207435f46a76a0323d91bf04db7cc440aec3dca2507560725259a123a97044fa80a28c240b2debc198f38952c3b53875eef349a2bb9aba9bf7e5784a7a7d63ae131f662b08ca0f4710f3b8f8daa3b6c7491d49c6e98d0b13e2005587d4a6aa7a219ac1d31ba3cd98743a12f57f823e4fd52d53147f5991cec87aa8b8f9bdc5a9e63546b393083706d631a339a2dc958876baae693579a2ffd9ff6f5f0ba9f02fc7ef8c2c536378e8432bd27a5397e7c1ccc1ef907b396a919e18f1334c5a205086ddc2a9ac1c795d5a9a4a0e8bcb1685aef8b1be67324a6c9b6040e1685a8cf0a3b568d594dc85e17de93bcf63759be3c609147d59ee3741b480c26425356f6565c3a6aa5f6e1dad8455defb396f6738ba8dd12baf075f12cc80164261cc29c96000ced5316829bb0e59be762c52591e8282863b9a63fb72aa36870f591c8895678866bd777911a08aec9097ba21b3c98fc3db29e9a0c2e99623c8aaf2ab28e6a10daed77d942bf409e399123b16b4cc47dd73dc1967bca78d1cca983fca0a83d43ccc0d060fd271be8be7833733804b8878f1f79158cdee5ebc066ed7a1cd26641d2781e920933883fa274fe12b129c38448aa5300915d906b91ca5162c4b380079aa43d0208b56d66e5fe9b4f04d437d167b2ea21844b8b58e69767ca1878ae00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9e45dbeca188aee049aeba13460f6e10c1bacec8385be31e42b02cd1b9312b32","proof":"7c04b9c73dd91aae7afc129faa5c2579f14871562108cf3ff04843468ba72b20a40784603a9e4f7a8838e6ab2d420759a260222863f6602402c22b6bb8436065309d030bf7a473210af531f497aafec0b82c5522ef859569d5a794b38d50bb3f6af96861b03d66a78934456a7aa706c418eafa71771f4f623cf72760eaaba5124ecd395d6a964cef2690be8bb177523c07d8eda5ad6629727ccfaca54294890156803ace32214ff40577958ab85f69e50a9a7e48729f342bde2f32abcaa57a04651e55d5f9a3c1771dbddc86c97d1f84f510460c30a2d97d6a7b63f1ba558e04eefa62105e56b56462395e90fb9eba81ab8b9e989137ab47a2cb51b5e5f8915ffe79fa67acc110137d6b7e4fc19e37817aea4c962965a7d775117f374499cd42b27cb9187d2ac4e136b977054fd4551efa96cbce901fe4996d660728d0847d28d0097bd8c4f230dcbc40bc270264af5aeaaf11f8bcc6ed250af3727b0d46c3103e3afdd10173062e6fbcf53f26e3b69228e6fa508cd87d09dd62f0357d35c60fea02d23f2ce3a0f2eea1ca8f0b8a0ca3e258f9096e4999a913d750e33d30e7599a9ad16ec6af7e67ab53403c11cbbdeac4ff2f34b5b39af8f33cc9e9183fb464cea822196d6c611d97aa126e6bbc9c0bc3dcfffb479c28b2eeb0c03a2d1bc609a21e6df7ef67ac95581b2007f8cc5d869da755961348075b313e0649c5f65e0158b365ddd4b0235994c9e4e921e2fb94a006c33173be2c2f96d50e960e709e512e15e12d91634b90981971502600532bde1de96a4ac5df648f4865f8b459376bba17297d984a28877ada8389c81f0b529cdeb40f72681d33ec101d4de00b267e045aa05ea1c9c74df63225f65d380391c6bf69e9e36d6705a5638f545cef270a2431812ae3f83f677646a6a7124571607fc1e7ca1e2e113cd2b7d32845ada80b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e821722553eaa3d7340598acdf6ca5d223044b60bc8288e6cd4cd66006ca360e","proof":"041b413ec8c0803ed50dfc40c90ad729fe2ff95beea527dfbf4816c0e02a037c7497eb088ee92c87527593a6bd83cc6a829f3d7f7b7a5ef0e11285b78728fc451aea445f4fb691bf1393028442d98f1d9d0a755c131845e03ae413220bd3911ef0c889bc5f18946c4aea4ce7fd0517b83ef2dd8cad7de8bf4d58d4e22c24b3688b9dee2360b03426ab261cea88c143168eb2229b61ca9fa3486db4d48c6ba204923eaa7422eea3a1ba06205e2e8625c2396a038d2f47e39112bb9f0a415b6a0adf19a958934cd0a56268892223436064fa55fd594e241f5a186b162af04703002a2d5bdf582d5fe3a5a8e257b1b81152642e0919c8fdb5e628e722cff7a77a056c27fd5ca91bd16c7fc182409450cd2a251444cce918fa2f5150ae9c89ee3a057a36ab441e08ef597d8d9b5bf3f8c706fa83a6bb183694aa1c3a7e9bd75ae97ad2ae8261ab11fac92d5d29fd75c7167cd14a139d906c89386b9d575fa4e8714050f6f0182e818baf3a509225c2242644c4de97dfacae0a680118cba3a5f83445b251c2e61400f08343e23d5539ef0164d30445cb20598893f506ceee62cccc1b58194ad678925f8d8f02d2fd83e8a0441b58cf85ee3d200a4f527bdbf072e7312096271c9af4b93e61fa5af28cc5fa0d597870fe229a72559dbf587f9085e57ad094837e4ed00f7dc5cc4ebdc01824919b4327f671fee2c8c4cf5bf752716f599affb4d0c11492bffbe6ad86ae3599ee1a0d62d66a34272d27b48c654019d70918b89ba69c47d1e14fbb28c44e4b58ef8fad5b365381c54e8b50238aafa11f3c4e555f90fc7d593a23d00c1af5a82e46e759381832604fb0b3e520753d856d0a0d41ccbea549c89a0a007e604343b18746f00c94c79030e7f72803cae350ab06d27ee91d37d714fc0736c153adb419fc8af14428a11ebf6de7b371861290a30a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3e05717ba5b96095477b38ef800461f637b4d6375d25811c1be9522857b17e1d","proof":"c4498eff1207f503396b07f70dfed8c1fc961cbaa5b82112d94d5c1b55b13642685f0efc06d4c1001296997f5f5ff78caf92e7007823dc41cbe1a738d395e76a783be9c35bd7678dc73a8507fca287bbe7b9e92581aa1f81b0acd6739c79e2005403627cbfbbdef92c8c4ea2ccedd1d9f638df89a0111e43762142f494ddbd724fe4e3079a80527218f27de54d1ae0588b45dcf5ac795d99464f206cb0c7a809f4a8cc7c222cfd53f1127f9fc4289e3c6f96d2e1a7e024784e7bc6db288f950f9ead71d320c594cede2f77970703452daa62c477033ad159ea6ff662a28872030c4f20f25ad12914c7188af682725d1fc7a7019be27a9c46e67dd16cf9f8ee09786b0facf64e27028d3bd3b568cec5a3cef1d082537d6186f9bee0704bcbea3a9eccb682aacdb08b356e446970fac2e0c33694d2e41267d624ac7d2589fe83559c1e9f6e547854f5f51ad879b2ac08b63c932fec2c847867bebc0a0e75fac870844186e3c80354424e0d9d9d7441e073710c4e3a1bfaa4a7bf38c9f14b7d7d1826de44d76f5ec0a77513025f867a25b534fb1e9ad154413d4b1287ca18207e3b4e28338535c7217f20565ed48d72e5a06c1da38eefe2eab0455a113bd1a6987d06ab9439bc281f258e11ba7546237f4a63c57f8d5168cb2ca9ba1809aa6a1d1938885fb94310344d08495831667ddb128ee11bf77d1c538fae3120d97189bc49442e369d7228fe60a9825a595fb808417b13de4ff638c42b11a9f6c905997a17a4c889d6c64cfeb83d36d626800c5b1e15cefd8d871445b30e6fc3558b54411d124460486bde25b2c5085a95bd8527b41d5aa2d771ead49cbe5628cd311b52184ce73cbe91329cc908b7579d73bf0c46e4ad5f6548fecb833a9bfa760a81550cf9ffbcbed3f14b306db3fda2a9ea7ebba51cef6cdbd5cd953fd576da8c703a08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"005dacfd65a775315a3080ca3f99ae2d385789eea6754014415cfbc3a56c4455","proof":"defa5fef5166f7b619e1503c6a877d925dbbb7dfebcf9efaba75e5ebba81207cc29c01983fc6d1012fc215e665bf12427184963016594ffe16fe35a503da586ba2b423678bb8e3eea7849d69b3be6799d3a8cfbde8e61633228cc4bbbefab4410c663fd1cd74e2234c861f3351022b35188ee0341d454c2a016ac35aa419d37d09dc51e344df38de52b7a053b50b8a3749071b40ffbded109f8ae76898b47e0f268b2521ff8cda5ef01ac4d3e1237a36c5dd11bf1f87dace4f8c95b7f2ec4007adff8d4459e401b1ba94a4e91a612a1d2847ba9294c0b2a29ec92d51927e6c069ad6c4b9da6c8ee28cd05a944ed3832e881197bd6cfbc60ebb6287cfb0c2eb4906e0c0f8533b309946cc8fea49f66354faedd7f97b4cadbc321ad0fd7bc15a6732340d79e0a827b9e2a77cc01cde6cc2f904914730c6ffbe6b37c9edf849a15244d06f5e96ded4bf9548564535bdb393b38b94da1e1d78edda70856275afec15d60f323b6979a7ffa89a7fe8a2df9b2dfb41fab07c6420d27c1891640fd8994b8000bc3bd710f93b098618014b213f8a5370f5a61149b275a048054bf71f650b7e51b325d419a3398f0e779b1f18f8ccb73490e3547f09d5a01b1d60c3932e4a541728527085ae697dc27afeeebbcbd4eaf418231e018fc19b5708b0653868313ec8e3993b9904364f3315bd388c2df68a04a357352e04560bed2f01d9a69e5cfe7abc329331b6b9f911b020158b5f822aba28cd31886c316ec89c2effde8c461414dc45a100e3f4680b29db9d8fe40de2f4270e6d2b971f575ed515289e80201237c3165b250d20b70988767e68c91f0fc4b508cca0279212b5bcbf77e19c26f874f8c6d91167c1199f272e85c76e8a9ac91dfad4de4c47a57159f48a459708c7a39138fd7d22544d6631122f6edb547d4256cc25732698a79b4800216bc703"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"449b1996acb311ad54383f189d046098705769e2cbb58a5f5e70fa783da6b157","proof":"8096f4a3c3129e1fff54fa9ecc5f599073e712c018aa18727d9a315cc7255217361afca9e8828141d75621066d251e9c02c3c8be265fe5ce79979b5f90012a5f687f9420070849dde704ffe7e9031d24640c3cd65d7e5a2271533ad8c62ad44a9c3295035123e0126265447acc0fb46f447893dee2be0c8a6679e91b59e60037892273ca99e236cfab4053de80b71f06fbc7dac52ec6ac31b70081979da7820f8c7f319ea9f6842453cd194916db7470981b7ccaac002be6a4d3e46b108b710db61b8352c46a68e198dc2c39633d085bfc79df22c40b4ca26773c304021f3b07dec620e56a92911480e5cc47e7c2e65f56e9d55534dd7739d0e99daa9b4a0b63eeb5ec3f4680e7d11a799503463ad7eee6696fdabd7208158e309dbb121a241404f03c372e5802d0805d606745bfaed74302cac66f9a159417c443710d3f682b92b532e26d5f29ee771eec183c4817634a08d14b762881f621ff17bc30145d11a686b416041899a89ba7d7349e47c80b06267837bf26b2429c3ba5e2a659d1167c8b62e68bc388f20c5ef5e79188065605ba5ae528525c9aadeffa4f29d4994850c0b438a011d2d2bdf35ae5333fa852011d63ef9dcdd6bedc0d5f9b2241ec3f18d717ebf7d3ec829ec347ffdda57c76a3ca69eb2ae596d7e57e45a838f78c16ccefa6d6a41ba17ac796b2114c2aa8343f1596f285a5dae7418f665b5f9360240ce4ba81f0f72a6fc144800256a0faeac8486f80d2c2114701b3ffeee8fce0354052be28384e1e487342b8da539abf67225e46f852c04b3df70763fb682ec86c4ae699676e3a9df61869bdc39c7c8d2d0c5a4fab5be9a8ee705bf2825d2f771645471021dc2d0c0254e4215586673374c58a635f1983c2370b09ac59add4450bf03af59a6361a8274dcece19cd6f7f715b32f0d40568afaff5aa1741f736d80d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3419306cafa1af2236e1fb1535669d2753e0e7e9d97f94308a6f4c344b2fde4d","proof":"1cd4d03048d80db1247b6baa84fc6fb780a724f9c8069a38b776ebb95823d658389f85fed464751eeac813dd8e5f56e2d62a7b167e2b44bb6068420f5307c52f98494bd83a5e3f62fe6128aaa635907791ae7dc3c3171eb0fa62452090fb30708447a46d8743451fae4b052f90dacb543e9452f8aab2061609a2e5d7da87af1ed72823f34fbf39264cc6e0022388b1874f9ae8843eeaf1fb172c148471723805db41e1cf8404996d94e57eff22bc246097257f9dfe397667c7db10c2132e400a466b4bbf841af1e6eea577262c05585d4b8231b1a1140b201411e68eefcc580180817631754838d934aa0c0460bd5e78a5f764414f12566d01d47dcb78d14502367afb187eaa0a0c07025608484ec72906a4d9c2c34472787f6390cb23f9444e6254d61cbfd00691f35b5d8d0891580dd0dd841fcb660d7df0d7d0f6cd44a025a466fe679f6c264eae416cff1cbc24ee5d905972fab34e21d810686745e3c426e803d14c0ee604e0b0a2f8e3ad47153f6f4030725c87ab312db8e5eead3196660230e2a44ac07da7c457b22e5a52aa6bde6c201f1bba496744543184e33573766c7dea1fb38459e2427b872b924fa65d53949e68f99f9d96850027e5609b4c40d2033209d94b3feb77cc6f917f87edd3b32a01363cd2826dd6b40e66f231b9552af7884a0ea9f37a0b1e02cbfe14a31e9d3590df703997a7275872c68b2143056e81acf9e82f75e7447b60b6253527406a3bf510cbb58588e802a122c3f8f805aa0e80984acde510e7885d1bdcaa1749a24ca957d38ba1149059942705416b4374d1e12c0b73a6fbc9cc3ed3cccc2e06a7e2afd2db89d377d80a149495fa365ddaea8d3d7712fd953dae2864e742c7a5a6a45848aeb552c2e369ed8146d461053c3490713e1e5de836c67356ce79822c451eb1348bcb68c257eba0b256f1f504"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"386ecfaff37bf6b69d73288e31e0f1cd6d1d892d05091025509bc5f69ed13a0f","proof":"8a4e31f7a2c9706a5aee9f1b6103be2dacf560e29edd0272db81f95f44fc3507a62d3ec6ac6ecfff9e3d0a4f82982a00f0701a2f67f318301df8d078ef59b0692215c65fb19325f335202f8387ccc3eb36ec394af1aba8ade8b9f940b748b95f588369144a96cd6e4174fd98e1a7494c57a1a0c35320c8e3b49f11c5ac6dce19680617698e6e5368a44c77888bc67fbe805ee35dc7a74b7ab741770c8cba77066a80e5d684feb2a9779a6c7f280d40e0fcb1f183ae1f39b2e545e0960302ab04ed9cff279b7ae18aab1587293fd309373071ad5cb5fa5cc0d1f04b2739e2a90fd025a3d1f609974461c4a8d6f393b14633c3569dd4518ce95fd8e2bb20f41f058c1a2e28000532e20713fbcb92bac5031e2331a038608772e9a76599f8d63930c8505222f3ac0c86e6442737ad677523a8b36efe6b1d17dc91acb015914737011a36da75b2105a8a9b104f2e062e46df41dda590dd317ec8e3b80dd747dee9143ae0620a29d0f049a5573486f7a647512c9ac50485fb287afc27f042282eb96daacd345dbcb826d0352009c8b9abb54b6815337a49f1ea66741e871608878c01c292d02da56af5d1cb0bc44b98589b8d5bb271ee092c5589955f80dd27aa360e62252ab7c1e41fefc9377139bbb5a92ee610eebe69d1587337540d8b7ac84e5c2824a6268bfa3ea18c002d5c2939c5842692bff026efb5fe17b132b0e974db2eac2fa65ad940d855db9221943a3fb037b5f0119b4be0e65d120455ed206d1e1f8c39503cd40a75964de80734222ccebfd70757ca6fc0202e89e70e51c5eaa2588ae5f81bb1b5f0e47d14161965ad521f1173b048a6e5c225ad25fe1204e1b761e3f2334dc656db009d137018f361741762bf3681b86db04ffe92730488f5d3032d838698b6a30a5edcd7000b3f641f430c68398ad2ce900d884697f0e2447d03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"46c96be96f7125301f56ec2431828f9d6b68d784f7e5232a4364d9f715c6bc6d","proof":"e8dccbeef4549490e6db39b956ce80b4b1a351c29c793cc0e2a898b194d43d148cbfe885f785a546afc0b5c3f430f0cbc7a826a2a981e1ed851ef3ce8d365754b6279ff733693029dbbc95a7b657adc2e2398d335579c764b11c5106fe65420f1451fb1928f2f288d480e651908b554441383ec2305b9773d9c3819ffd15072d9882e275848a714b6bfca145013970771c1cd161b55687a54680b21cd258d209bb8bf248502fc6ad7da1a831b9dccad5516887be0075c81309844b500789fc08899b323d0187cbbed895cb176493f871b6ddf0f4cfee892e560d59c055c41006de1f3e866c63a191566219ab513473e30e5d7079a39fe9c5f83189f56e968b3a983c633d1e606fffd15f8022dabd36c5c5bdca14777b0a4a1cea893432c3bf31a2c5d50665bcf8f9d56c4f6fbee6148c60bd6484cc3eee657d5dc46d96befd161e3143e7438004a97c942104c20dce34ea38bbbd695eedf3813a25808e16522b4aefc8c09162ff733c78c76bd259d40b9d95180dcc34aadfc9b524e348dc101d70de118f7818991b50e9f302003111d62a46923c72e4241fe5295c62d7c6f877b6383fb41a6f70c3a59b147882224dad76702893d48f1d29b023428564d65a79a22581b95494cdfd997bda65645338c14ba65e043ee6dcea82806ea1d575f46922a46c5cc6049234576038d12111ba605ab544227b78555ae640c0088ce1dc33b0078f5671e9356406b71d76424adf22c4f8aacba026afffd0c7f758d5e9737c243f5e8dec91c80e869a605230923bc5f6cd35b73b5a6bcaf7465a565e2d1e2d50206741afd0f9178bbbddbcc3f42dbc3d3d195856ef49bbe37435dcc1133a7eef585be8cee1a68d2bfe8237d338657aac0336750259115bafab8c4cd747fa0b5947abe9e3632650b3d6bc6575099717e381931ef34828c881764efbd3e9300c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1a25f3b3b571487d815fcd1c147de95dea66701b086f5b0109f90676be4e8c07","proof":"30c104a5b781dc030cbce0aa43c8e2e11a2ffb31cea46f1ace7213ea015c623660899da50635bcf89f04b08be95dfce44a5a0d97f2768dde626828aeaa707e08a05fc3211bd9390a91950ddb2f87fac2fc265bae5f662374d6ef0b9711a88a4a6a7874c63c278604f15d1b0568d1139231729a3ab16813baa30031cf8a77591fd867fef05aa39491f7045533f7aac346e81c85e767d1e57c8fe2acba9647af0ab438ab4dc2e17c9050004a88c6a87966b36890f93ab89e9e122eeb9e7f9218047d46b9410571d6c9819aead8089849aab46500f34ab12b1f8d0ba13e43e02a06f8997397a8607ef926ccbac02f90040ff64c21fae27ee98550430c470376a87188dceb6a18b738c5cd15c89b42dd25ba56d328d055e4a4fe312466794ea41f2d02d09180593132ce67603615b5cd85cc640b8cded47cec3c66d3b002b48cf917e477a338e1e6a7f0f477ec37eb6632ed6b33c10fc7d396efa931a98fb3bbbb19708ea36b93f8a299ffa8fd42cc9b384e96963acc7ef120601cb7bd86f2ab7302e8427b65a35f8980f831d18eee74aad3f6f897a0a8d27c73188b05aa4c046e6bfe42f3cc86e663b3a3065abd87d8ab0e3b849a770b674e7190af3d5ac03f65600a8a64000d0c7561d38f461458eefb57f801c6d35cfb6e5427545f1ca06f5c66a4f452f7ec9b9f6e6f12c136e35a0a122a849d6509e3ade39626161905a4791570733537aa0873a59a71e3ed7ccde973c1269d05b965bd1a5bbddb2e2429a96d40cbbec637f86f3e03ab69fd22007f1a4ee17c76620ab57e5b444d3ad33d553e5cbbf0bc43a01152874418ccd6757e86c0ec948e6e1a85e33e7ed0e0907dfc58347e030cd153852200c6a371725291572d5070f48fcdd4b9d0433f0a6c6aea0d4b825e5681b40e993d3a3f9c20b6aab622382190bd6106036b88e2d00475020e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a6f1decd53bbfefcb8393448e20d7b6896aa361b00aad243fe5fd5bbab6b370d","proof":"9a7847dd88fb93f7a8a36a6bf278b845e2215b3d1c4e90ed26c3c28f6472aa2618dfda62e0548dbd78be963b05fb49c2b9412f84ab6f8c496a348f6c3c8d55154e4b43aea2a167122275e54c14ed1f54bc37dfcbd9df212434d3b57ed8812b19b01b17ae392ab63611f43578cd15cf8ef0cf02705f10114884672c3b533f0b5c41ca15f142dd22523bea18472cb7dad0dcf085a20b224e5250af81a0491ba10e8ae15b1dc883d8129c6b86d1d9486c21308dddb1b0e14096216f9ea039b59604de9b6ae798f6932a62640954e0157086b9f34e0baddb6adf504530b66229900aec51348f8ff23d496022c066e1824cfddb7d5dc63aaa231f5e78b2baf520414a3c706a798ad831e9eb68becf98923e0cf0a2a496acc10e0669c0c75b1b958e2bae45a6de5fdd4065064fb2d181c21326e08a02e79736516e2a33f7af937f8b622ca379743ac02b70034321417fd99ced3b86f6988f113977163df20d42795062cc965f516e40466500b500a28fe18defe0d198d77df84663719c319f25ea7518d0c11c64a863920a54be08e6c14db0c05a1848556174b95b3b8ebb6482827077b492ca2ff5fadc5cd97fa841b907008f495126844bc099d5b9eb10485a96b3176cbc029cd422232778dcd02000928274f9442fda01c218b280b1559cb042653ffcae8936b2525548092bd84d9351f57ee394f8cd78591bef7e978e4f701a537a606c1d470a2313eb34b69227de982d0b4861816a45408e8b8f1e32507a393d52d467ee88b95b7ca9ada472f50ac6e8949c2b750eb7e82d8523ed76e35bcf8e24746fb8a21382175107bc50a3c84fae96f52c775f780bb43f32e37e9c4bd76935ec3c58396d4d361ffb299abfb50487d34eefd7e6d91e6b25d47ed9e00c58ad0df1f9452b6ddbb2c4500fbe57a9fb3c3ebc60fd0df261f7ea50e17901d7b7f20f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d4e37ad46868cf6370ef9b06ef81bba145cde3536752a56630a1c1727c74d20e","proof":"908af72e3cbe15f647940a403a7b7d17c606d62e6d836a8cc0d3b1c2d0293e04667b67564e29db10578b346ec935f6518372d942c96ddfef0df13a1435258d3b1ed8160819ad57435888561d58e56aa5f2f91ae26d039b21fbc8a2220044b155765485dbd11c3e67d571ac5bc4f89bce8ff0dcabff7d41efc89beadf9dcf07765cab4cda7b4e6411e29758716d463b740a8fb01dcb5b5cb61f09abbb534a6f0411b16b63f7aec80e57d46665ac9c3998087253963aaaef945c562edb37c48d0551ab125f2aa969515687a192b0b438f55282a9702e37f729dd7aee4fbf193e01dac9fc46cdd772cd4d7df2c647d0f12c952b73ac5ac22d95a2b1a7e8597db9048c92df13f1e60428086abce0176268fa9bf75e3e8cb219fc223d4f195938025184ad9833f5c2526e50ab6aeb2883ae79c4ffa5f79b282bafa4c4787309a91632b6e0cf04d1489332888f0876fa32a61f9184b876beb214c69d7371e2556d194da88029ba45a63c0ab8c1f339bd04848cb00a90964db9fc1dd6f8ee4e9cdc6d64566fb6231e40167978cbd7723ee7b2839e3381283fa592a63954e253c2068a41c8da7f653f68c10bb16dd7fa1c5483fffc4f23a0d9298cbb2a036124b6953456e2390d187bc0aa0babc9cba6f9a2b97dc793acb3093e8b6b4d5ee1b5bf3a9b58321a6d98f362eadcda2dca4448b955d9b3e26ec42ff957c058937c12ac7b176968cb648f552754c7b3d25037c000a5d9327a0ca6e5b3e103673be12622c1a03f9842a1019eb744a4c8d27a72c164d34fb90afdba598965e273bd18188a75f6557054c0313a3a2f07e4abf196b2970fa1b26030c1fc3c551e63f9be5669810c46ac383636356f986de02e62ed850d48f79248c308ce97807f229b495009091e063bce86f648d375fd241ecc73f5d2563141b3f97b4e3f8d34a299170f36261808"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e427a8f2a4cae39999c85b7194c97e15c7aebf88e0142f2c50a568a09feccd03","proof":"f8550df774e25f1856c3b0669d036302f427488886d9707d052a6503ca28b7759ae84019c3a7a29c78fe4ba2c5321434eeeb0651ae4f1a4b29dd49a7c4bd43116e339c3daf4dcbb4733f9b14840d09c71876dbeed3de335e61845f7bfb74f86c728bc188f2451dd2361d220137a49f7a23c530eadd4b5e9a6a09988610c1b7779858919fc76d46c32edd854abe28e4ebd54005f2f185dc2209a0601760db040cb505acdbcb4ac58ada5a8cc525fd172bc423818b5cdd570c29b025be64761c0c838f46261bf4607f981b2cbee823abd2955505a0608fee5ac00a5345f6ce1005cc5c66960b91190fbd523bb51c8c0ad3b70dd6e42720db58f0e50efa8617413b043ee6e2f658b1c42a3d040ea5d4cb6e452fbb8f91378acec8859ae22ff6555c4498f6d473d7e1091d382f7e8fde0e88ee0f2ffa6abeed3de6a3a2b676a784708e9aeb4dd43f3b7062e62340608401b8ebc11c8b2051cfb697ed98d6da2d7d68ce5f7266736ed3cdd1c083e621ddae4eee313d9f5e94b9c7aee3a9870582a62ada2dceef83528d43b0374200b7bf0f933552af3fab59dc946e35faa1a5859c45f0897c53d13d0d6f733e31bdaedef5732e11af5f118a291754ef1594e2d52522c21c95f482e9074b45b0c3db8746698011c222efe1c832f284745f9596a90e48ec18cc65cad4a63239098983b79476778330fe90cfc54fa2a420050143f66769e8a16ded33784cc1ba0f4234649704c26ccf91669aff315b202e1f88ba877d447ce4618de86bd9a9c1c347452663203289a4aae63f786bc9164029937276a95098dc69ea29745ef3172010eedc0c0465866eda310751caaf45a43b84e9683742d65510a245f103eb7019ab296e7dda4b4c6e4bcfc0a0348977ef65cb905cd70dc03663a44ada0df4b81a9451c2abd093cc48be55239daa933279e138dc91140d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a8095ecb15f3807f031c932de5d144bd8f2d0c7a7b9dc1d3e1d50f80756ec06d","proof":"c49dbb48ec5f772b08fb85f049c825a7dd73ccb511b311cc909948c832e04d5eaea87130cf1d72885bd5ee048699d53d6ce60fd79a6dfde163da20854097371d0e9330709f0e145f5fe38d36a57e2bcc304fe10b255978e54df9cd4df7b4324eb6d90dc95d9a25c5167a2fd495b4d8e3ae7d01151c636440952a03281081f26d6b02d6b65c627da752b34dc2d8f927e9587604bc4d03c9f3b240212d7d352b0f745a9d6a447d0c611059dbac10c2398fc9a406eeecf65259cb378ca45c94480bf8b0348f185b0e6f6d0ee8bb32d710f8e8b176b37acba5a7a44ed815fd87ad014cea54c4edfa5d75879a3931c06b87b8bf8c8ad8f393cde0a55ab3ddefb03003749c028466823b152ad8d2f67b0357f0489dba1f3d85d76c46899fff66a4cd483c815f530954ce842e5328a794c494a0e998621378ab4e3b9f6c191d41e42b42a866f61f92f67812c512001579131535be397a95c987e7369b013afeb1350a70607583b5b2a348c392f3f5393e5e1db48e455ad18e81fa03001ed4c5aac14e1da457d67dca066c87fa112b68b1f9e98396212ebef9e0a4af8ad35670b0fcf0708eabab1b4b7744dcf5758dbb90ced18646365ac05642ea0e335fcc14e744b63da43243d27120ddfa9abc5377ebd9651b71f189732eaf0eb07c899bb87c31976bc6c6b612dfb085984646182f5c877ff242711a58c502ffc92c33a9d780c6d07140b7219f3654093c52c13201d6faf014bff97caaf708b1cb1ac259e3a7615e0a5ef2b915e6df9a0a15eb3be40b43e798a1e46ae82b38c9b015e329cdce2de8207cee3e99e510e58f22da05e754f766c7cda4f684a20481baac5ab5e92b8b18748fffa149cf54b01f0935113c05928a296cb4dfb8ffde00a235334317d8763c043de26a233fbed28daff64aa61952999c40c78030c7de7a7a67eec13ea6f1f10b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aa29222013b1ba3e76156b6a319e04b392c33f1611c8f7a118f527cffeb8fc76","proof":"40e35ea1cad3cd3ce1cc769bdaa5b1dcb2fe47f8b5b280e9d19ddb7606e77b40384732a6d3aaaebb8a5e2a97294d3d9aa574db21efb82a4d6ec4e6fa0ed62c4f163b836475dc82cef6efa66441e09d2374dbf0d79771c7655e96703e4e6df42f7651ba8ce8ebaa859b506db48bb1739ea6e2ca4b9c4e573f08b89b0df9418d5dc6ff1eda6852f096b42135fd10f42b59eea51386bbc427f564ba4adf4f0913044877d4013af1d7859a3ee55db5cab1ed5528ceefe7dd437107a6805326fb11080af409bfa03c7d61504075f9cb5370bc9063ab5336ef559e5d2fddce1cf0ae0e52378684abb21916c7105700b2b55cf7990bf0e5bd758acf545c3da58dfa636884743c1defc554451f318c26f09b1f92d30f411716d843cdf127796805ce7141c4c08943f050d542747999fa3a8391e60223c930ceb3c1775226fb4a8fc09274de3fc9df11bcca46652f2687761a6c26f303393e273f77a97718b5f6900d4b2d082049d41b260276cb9684ca08db9ac9c3be6c3085708f2a38c9b1f706c97c4fbc9adbf0a9d9f200d00252dfe7004d555d06cd10f362b6f8d74cb5b5cbf82f620054d61d6d9f16da947bcfc72e0f73f529da725dfe9b177d07ac90ecb5a6bc4250a10475518589ab6ed4a6ea46619cc0cb4c5f28e24c82600ae53ed0e5a4993604583a73ef9cfb28e8119c547354e447e93d9eef21b913fe52855ccd984b4373063abb04fb207e56589e9b9d3839b889af017191eccc03e6132e776cddb5915a2e54ea0a7a7483895aae19703f25c30749a39bbdb094a01e9d7792810bb4666f944620f3ec49de787a87056c4affafc2d962661b8a27d16f65cbf15ed3113031ea5a8d232f53a3fbe23682f5aaa6be91e0d713b2a04616406191a7c90849af0bfd8770f28048357b9964ccad9782fbba1d89b1be4464c7e16da608ebe763f40e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4ee03468ca8d0084a1db59c29076e80557027a3d0f174a8ec420986711be2049","proof":"9c8e655041bfba7353afa1d5023039c89b943c85518236c2b6c1f9a51f3c8d3110dcc6f4ecb9c1d8612946ab64dc50810ef83d90c58d08df21ea83969908c272a6dc6ea6764d4d3789b8c71920dec42eb405b0c6df7b2a3cbc9e6d64c51bf54d8821b992a36eceaee2cb4b5ae9c8dd2c227abe81a13d3bef2c2c3c8474947c189929e892bf8bddc97467deed7b03af0394f5a683a911204a92f642998bce2807d16d75d858d94818899005830e3ae01c41f92a8458bf2a3a12573731c70694060e9a71072dd0eb328e7b47c0c80d50903c9687ba2baf50076c3ac7bc716a0e03ec595f9a8f0db59f6f496a458ff9ddfc00083b4baeb72e8412893163a142ac19e042ba8baa5832d5d089e1c38437b8105e83a5c771939d69697bd7e7d9c7da3782f2222016aac2e32916a806da16e012199ad999b04b97aaf122639022e67b7a5490f836fb86dff80e77d0751f43aeab59e594f0faa00a4db477b061887dc11edc38ea6d7f27fb491db2047159f8768c9bce50b1a514f3b49d4c898f3e3e8f548001909b2b707ed0cfe551a694dbe95025cec95171e6e1867ce8215bf4913f6c867cca848deee4a3c6b16573c360e482cf507ef977b787794fb1e51a903fee315afc9700bd8be8db256299268f43ca5412599d5334bc238b8c2eea011d9c422978420e1b116a046acf3d19d0a4e8101a9288c575f79e8226a063422df24c4d72d2ef6724caa594de6ded11094cba8632d946e60e8e4569f5f2289042a321f6261cf3a41928956c2ad29500d9c76cd72cd4db26e597a50b15d10e6e7dd58ade441848ee086327a22dca72cf75ed216de7f59afcf992fbd90dc106fc95ed0be57ed2847abdbfe42835acc9ae11fd80de9b1620057306056ac24667b09dddf8c602c72d1aab22f532b1791b7e5c31d419ce3996544455efaecfbaba989893360703"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1c3d2fcec65ff39e84bb445eebbbd96b7b7e05b028a49bf8835c5322f4523230","proof":"5467512d4f0e99887352fd97ae8545166c2e2533b0a982b88510f2e2635d6f5acada76fca1ca640d8df84190fced78f8a5ba5175d4f70dbac2e3d6533f64e514b6fba6b2d0176f7db81965a42b8ebfb8f64a5b1208c4329914349948da345d1acc9c5dccaeae65666e7704e77c3139bde4df493e43a4fd64ecfebd667059816686b70c7b147dfdd5f3142591a675877d37f1184e3a3bbb96368ae2e6ec7c640cc5dc19b55e77ed9c0132e78a124381c1d38fdcff4ba8f92cef30b6cb16b275009e87820947ec972c45b3e4ba41bc7b554bbeb28374e3d6cbb3fc2f94a476c4058e434a3f3d3a44936957bbd4bedd980bdfb05da309121953f7a0c5955fa6741490e392103435ba8125eb15c2791fc70630c108c3ae2b483940aff83bdc0eec1612f06e70b49c6f611a0e2eba8a14a7d68b8755b80b18a28d1da755460a0ee778acfea410b404b0cbd0789d3e00a33e01d3d860ac65daa266bd661bfe9deb6e4c2efe122fa609ef9a328e3cd0352b0348a863d660d5e901e9e83e0444524a2449d626137977e5adc0089d3f784aa92b48d45a9d07f63d97c89b332f513e32c358d2f04190b8a9822480117bf8ef3f2777f07d4023ea8eceb8a46cdd6e7811680b000cd748efbeab27701429e0463d58c365ce34f7127fb1b45fc81b985f37d34704adfb5a1b13c883bc1ba55c504342e04c4f35418ef1adb00eb6f611a3a997053e1a1655e4a6d8473b207b552909ad85692405125482628b8b1f6081c8e24303d0224b54eee5981ad01205cd194da01da5c91cf523510169cf976eb908d5d07d3e373f4b63c5a2d652e44866129fec8f0a063ed95ab5a7e20c5044f82ee2da1555b6f6bbc0ab0b9ed53e5b63bacedd1ee71a6ee52004bd432e7123e26201c900372a624e7eed75314575dfbccd51a37e6f0e999ab5cadb7e1dd97e65fa68d202"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"22b62ceac64a75b8376f04a00cf9e309db7996d10a8ae6509b505cf328e15569","proof":"c0c8506d12fd6853f59d6d52315af92b57cae1b8220dcbc73fd9e699e33134129873fbc826bfe388526202aba91dd89d1916a0f4874ff9310372f2ce2af88b7498a20258437648e9fcb25097a1690e9ff43e096172c73c9d962ced98a5982237fa71fbaf0a254e03ec958989d181f9404b95732cfb98fd2958cf4202cc234f7316df686f6672b2f244cb9602c54cc796ddf5eb1aaa19f6854d2660fd5039a809ff472899e9148c2081f598eba192eee39140fb90c8c409201da0f4e7faa383032ab45f922355c3ffba57639f95ac7a8fd856a41919623e1ea97d166b62bb850c3a6e91c6bd2447fdcbbf69d8775aefb9a289fd88246ed9e1190240171a72617ae0fe1c7d34c50964de2e6a3c6713075868ab4288292b0a9cb5d8d4743e82920f54fd25cfea16bccc4e14c2df9d9b73d22efbb79ed461ea66293946e0777d0b4f004534064c6bf3f50b60db8307018b4ef02a7b8a3049f1455c36b089a5ba107654a75017189b7a37f4bfcd0ebc6c039d7fc80ebe0fb90e5df7ce02714c7ff228463e87f70f036d11ba54f70e45fe3f605dc6ae6fbdb498e97c34e90c692ff444d09817a8eb4bf408a0d5003405c2990888e23200ea269a5e015ab5d84401eb10588e74901275aa367ba50321dc2060ec3989ae026f09c132546e7a18f846d077f2f28073b2b232b08964a38c8643d74a021985202adbbb5c7c533b0a71e77d1bfc2862cd08f67973ce5d4da2fe64db3ec096fe22a8e77858ea097220cf24c41ece9140f8c51d6c14a7ece0e8608dcef5d7b3632da573216984012d3e4b54bb3b900b4c1d378ac59fa7e0e5242db0887476cca85e11a734d2e94f4aa0f9f3bd2ade56881b8099efdfc4be6791c9279aa5e0ed08e1a216b53ce89740eda2e3db053699a295ca1713e670c5344695a7658be4cd7e78e7575a67771bb4286c24d90e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f2cec6354d367c7bb1002c38b8ee1e9f15433f4b05722ec2d7858d8bda37402c","proof":"a6e235e1c4a1548e8337d5326ba2b151584ae96fbbf0a8db176f5e0200ccc32cce88c3024b49c8f67472f6d3a328d747b9ca457b415ce4bec953d394ca440528d086831fe878c81f4be884ca8cfd848ec7a7590b1332a8b1733f9ec7e69a29750a129b719b8c4273d338103d2a9cc7578237865f33be96555cf5fe521d3c1912aa35fe044e2524057f5890ab58b4b739f297be5582514cb297a01b44994a220b316b5ba77a814228ebdc23f39eb7888cb34282bd7efb16338928310b44ce6e028a770ddf59e8be4b91398e2208cf65325080707b7a791881afc4caea792f690ce0d3e9a36da40ad6a30c399f6c06946105be4bdc60a45ac1d45dcdc47486df0dc0153f1726cc0dc82f4ce87504de8522c431eccdf976d76048a16b92154399172c66b8eb2a9f5f419ab93ce0ebc5ed3a02f729b8c99b9136678d163f2ac2e25d8c3b2f341946fa23cf2d0923ed0887ed8d17f94ca8f7b78fcbe9d131a75d5f6d8a2b5851ad7756e53990bf262b7e90582b2be4c52dc452881130a32efbd6fc0ad4c646b397242df27a7a74be7c4230a0f7a88f95b6151bd340e49eb337641902f8ff426ae029b44b4f03f1013cd51d51755efe70dbad51108c831275231c672fc84ab7559d07c3629e021bd0ebd7b7271a37769c70cf2f8f899210a2959ff621106815f0ea6d624432df913788dfa151aea5084aa034c51f9d3ff6fc60954806b86af44552d52eaf9b987852889d6cda31abddab96b7bcea0a7b12bcbb9bd6590a58175b6b11f8d5a4efd8e8d7735decead6d7bf87f2a083c0206564d89ed20dd2c0aeed45099b9689cdd6b70f5a80a92e00fb16be70a4dcc816a1725d2a3b7d604bd64a0e920ef1a2e65561626ee48e378bc72f35d0dc20c6426a200f3b2e0fefe9cefe5b62576ed65256981d0c1b64e55d4669d858d11fb2c15293e63bd50c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"006c4cf79ccee00c1fe15136ed2a1fe7833f0050cecaaca1a84342bab2aab725","proof":"7ec2ef351051aed1fd0d2ee7d5fba956a24a9894e43a1ccb34ff49eee498ea42e62468e9401b0f8614b92c1aec4d4b0bcdafa69a85aeddab238f19cfafa5bf15ca7bbf87c81c08cd0932190ae44fbd7703aca38e11407b3275b6552c54f33b1f5453c768aed3a4329818a6f36f8a052ed6bcf802f562794cd5c0f0c099e01d0daf6193a82e926f0da301472435bac69ee43e58ba7e2b96321d6a4cc696e4e40af5564f4f06c82fccf26c57c05b95b7d42ee73bf7f433d9c5f753d6ed9672ac06b35037b7155b5a06db6a521855c037c0df3c5736518a574e5973d787f30e5f09bc6213678d71aad26807504b47695afea41d6f52be7a96573e61fde94edfc44c78bb8a1a16299fe420efe425da6dd3da87d09f8e66e82c0e31f29a1fec5bb165a217601fc056f0d662d62ce8f71ecbacc268480874ba23fec297afc4594c42590c5439f13209cbb96a054fd4751207c67215170765656a69daa3594e4cbc9d00346df1ff85d24f57005db46c40d84f9e11f467c3de92d0aa16b6bcc54ede1e18c62fc2308fff93a4be78d1a2ae0f38b99ec25744fc03736acb12d87bcdc0cd0b5e8e31e830b67f46e8f4a4aace0f17b22f768535bee9aaf337f8a713c4fad86242b2597cff65639cc25506be32eaf03cf1d906c13ca09bda69c5d97662eef40a1243374c0adddad0cb3d3babc1e8db359c0f86cbe2992659a50ae5078e64cc738058c1d60d3b44b191d20737570f1fb32fcc50b16ca5e53554d39d4507041b3daee074c3ee8a3b601c8225b17a9167c4809d85b6a77d59393a115f840b7260283e0063683419550438c483d401c3a233783471a3bf898e5ce9b743272f9b5b2baa40aaaedf54dece8fce098d9f51b6e56a1644e5d27d19b786bd70b442d1d40cf8fcb0b193332e6b1fab0f4e0ee0327fe3ec738e6d5b6acebe263102b074e00f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ca09eb253b5c05e03c8eb2c8b28d398a90105980195da807e92989f758488a4c","proof":"54ebade2c6fb7c0cba256c02294c2ac36a1455983e2788ff84f3e8e702a61d3ce89f3500646c4968663ef651d527ca23e9b0b56425d820fa099580bca71c94003adbda5d009ec9836ee2fd764b45c80b167eef62b0eb8edfb1644a28ab8a4b5f7a576d1f1da8370329d9400fd77150129ad2f63b04194656a9fdfe917605712a0b6a3d87e165117c83c21dc7f735b33a1aa99ddd399e612188b0f5767226f9092cfc5a76eb95a47d3b2ac3faaa6b850ec263fb67c7eba919d659c774f59b520b8c342e146a77d1981bf16aca17a35bfeafd5c2627581d510b00bb6595afd2402ce1426eec1fbf54f02fa8cf4397932cc76906d1b80832902a3d69afbec62e17408cfa9eb6dfb1d198bbeb1f2b1e185042dcc430aaa4a946b3fc10c25309341482e0a5fa25492f505dc1322deaa9a24eb3218163be60e0f22b313976decadb94c08f8b8f8c9a6aaa987a3e50a7c454f759245946513f3bd7f987563ccb33ea6557a612b8d3bb6f202152242a5ebdfffff75d863216153e932941a6a190202f75b34d801ec871caf1366869bb7af1cdcb6fea04a3c93ba2eb04789899fd2e2d719da4d372c7a3fe8fe06d9f04718079b7083812de80f9c606253d69e16c71afb316865f865e1b0ede1c5eed3acd54a8d81691d1c988b867e2381f46c75c511fa71ca0f2ea92aa3e12332bcbc38f1d445e4668edbcba8d7fd5437e8c44c47d4b6416ae0b233c724cf9603dc3f6da30ad60ea59dd509e87304bb2bff4b4713d8e83d0c0b30b57ba1a7bb8f7fd8e95d0bab90e517a9e745973a010476b1e220f5a732ecd37d9fe18e0e65a185441de88717c1bd37ee8bf057a1c15968415dd14f906e3c4c65fc6eb09f8ffd51e0ed92cda6caa3caf18593dc8eb7d5c92b2f9815dd0c0580d979e47191be3e1817ee378fae2eacd9f70bda3097f45883164f7dc9d50c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2e17d9dcb7e9bc3b8df186cb829cffd3b9394eb2dd585e1af06075e94b78063d","proof":"903684d94ca89bc3ebc3b9903839c03c30bbf4abd659a773ba535ee24f2cec4724fdfeac6a06226984a19715f14703ea559629b13d982025665d25d56bfd940972e8ddb89b7e4057a8faa15597dd522f0b7f811f1f73aa2a89624a8b182ce234ee8345a7ee4905318ce653eb09e1d1b379aa58a8edbe658c66abaeb479b44740b109f0d0848c3dd3d38c77b80c1eecc45f4f8b7af5da504107f94305843c490ffd04d2f3adb6cdbc61889c81276de869789a58372d27eeefe4720196a30d9f03e266232010d1d50fdda545806313b37b3d42cfb3641043a3bf60f5678b6f910fb23f23bb08d8be44b7c17309e174d90987bb57ef62bb00fd6834d6fab20a1c6efa4ca683242d6d22421c3839e7feedd64c1f942c8289f2872144c1ba3a842c67ea5891a513d65f2d311807c60ed6e774ff02eeaa5dba41d7bb332e33265aa86a601b5743145b1716890ecfdab1f333da73145b645e4d369ab7472f1d60cac16dcc4f3b80fe9f7a1f24df82fba8819aa27cbcfa3ee7708060c6823c0d2c70e843c0fd11933d687f2f70440b1ba81e8f03d26aac008c0eb5f606d542ca27cea54f84ced5913dc8353b146d657677798e2eff45e72ceea9e52ca8c8237a1c3e9e589a9e1c0137809028dc7451b731ca14a971562ade9bd87305452a401b09b19061a29c0e6e2fa21dc4fd875219b45b2c1ebfe7719940d2ac590e87f501b168cd4ab02fdb622a4fd8e2a137697fa7f738d8e06832f8077ab84489fde48496ab4e552cfde13356990dea595f6e5bc0b72111d5e3eadc1e73431d71ca13c70263064e2623c950c559a354f1125988517c4f636ed9f493864ba0feb54db69a976a783f7559d525aa4069cda17cf6bde3a6c520be4787b0a1254cfa7cfb847b7137a1098cf2fea019b44aefce4d77b3c0659948dfdbbaaf1aa2adc9a2428f82d8599e0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ee54c31ab054157650a56f6a3a7c82a8692c25c19f5c3ec9dd0518d8c7fda244","proof":"0ad552866fa7ea45ae3dd3e680396c0edef001985476fcdaa3ae34aef75cb213accb155fed2eb27ac15416c5326cdf98bada7d8561e201447047ce69aab1b03410c35800165fb1b2fbad3b7e3fef34482430c032a38ab649fc9697179887962f4e354ffb69ab7645a67661da2629df7f7d85459d84e894ef62073635794bbb1fc5566b76af779ceb188df74ab33c2c1d386e132769ba4f5bb57c5c39ce49350492f1dc2a0e9a66793ad642ad0fc28b3139353a07a5a4bd3dc88eb925cc6ae206adcc2aa463bbc8e056da5d8af9e5ef3ec6ab77dba730ac4098f22aaca4bc300928d36d959f4b8378f54844c7a30ddd3d708bd447197feecb6c07aa7d6d694a0cb08e24f3e7d37f374df9d51dfeb14f6f8fcc0e3ee92c9f7841b9f3186748b56c2a82a0f9b49517e76916ed579818aedd2d83e6aa7c020c6ed4f28d7181dea70c38704575feeae41d4bfeebe2c2babd15dfdfcf0db5a24ef28544da562e93795f4a49636eafc9e8d5d8633396f4a7a33180fedbb52d3a875ab779ee15aee1ee744a5099fa192a39fc48bfc452b9d3e696d509a2dcf0463f8769ffc5b3a1b14c403e04f3b09eb34062cea94912deb47c5e0980eddd54d05175e19377dbbbdeb504445355e1d960796c1f8d65ae393d480b349ca78bbdece8d7f6ee6cc0cc17d54c5a59ef3e09dd0d732aa48826f13b6a5af1754d5a94befa74b242309aedecaa513271f3bab16862256e9afe3115b215cfe5a99504041eed5ce59fd90bb9d5424fa40a19cbfba69ae49a551503100202ef41f81717818929174051429ea46ead46e4366e9af7a092ce784e0f47780dd49e49c06cd8172fe62cef654e3fc957873e015c2c7ac641fe8e4785bc51ef1f7ac1c26c3d2dcac988f9ddb9bc87881ab70788398177531c83fa2396c82c38f929d1c3aab2f68758484010777b9160922502"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2c500067739c2c7e260561cc1b3d799849792b82f6ba0baae93f654fcaae6c14","proof":"56e532226ed273b01d00a597c271b763bcf789f5f176a94083f550c98cf1996d6219edf90e260795167c5f92488c1fe14cdbebf0ef8344b95898bb2c27c9995594982d1bdd005cb6ba41c5d528477c12fd73610af76ccf140a9d5fb6b50a0e53709c68c4376023aa5b9842c5435752699d19e91aed091ff4399b4ec6f6be9f388b8c824706ddfc4067851bcc95418de8f66025666e763ccec4e91f0a8dc3c80b2664dea31ffb02ebd95fa37466035f9cd2e14e8bbe307c360d63d19d768ccf026b0ad36349c32da0414f0c0011b5851ffe8e5affc9ad8894b10167c0ebda8309d695c69693742ce7a448d31c02077a342c6bca3f1fe949ee5a99ed2db9f87a477c7fdeace69218c550ec59c855abed13d15d428c27e71413df7d8b5d3062341592ded8b297a2a09b4329c9a99de4e88df806ae5263b20b9a7b01fcdad18b88025e1aa49e295fa2f53e663b1d09082415585faf666566edc50ebf5ee50385da45561669720f5664890bb62f37ff3318b079b9776b886ce1a6ef98f30477536c65f6a387674afc2bc1d7a6d443a89fa5fea0e1692af21c56d84e4e3dd40bfeb969400f38b47e6b3a9775670cb706dd97b28dfc3251fa00ee9144b36fc306351228d491a58740764eb165e2c84711df594a12f04354eee3674f474910ccc71e0b1eec255e1923e6ed8dba56d42c93ef8911082f5024270e7e5bb307f7b4c440e81fb23827e282519acc456616b71e9d6aa6f7ec483524bc794ba1a58334a644a95bce272f0d0a1eb6e06584d3a0723a3bc32844391a1f76c77097dc52adad4f441d8458394ff3301c94474227171512da9bcd405ccdf7b00d365dea9905d3e335346c2c1b79b0ecc46e6d1579274a16a360a866fddb0d7eadd0d6748244b641160692d1af4a65b7706c29fb8bf69cacabc530a402156d07c9ee817f7e9cf5e3e505"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b69789d5c342e7147ed68ce38fbbf9e7a67f5e75707b3a417cfd731af5af8249","proof":"cca4f42a98cd60b42d10041d47d0e5f41d682563529c5201ad6ff20b1f52cf65e09d3380f26d8ef7dad4a1752ac8680875e29b6fb543978b74b1017517d71736c05598e7c24f5803a607a26a7485ef4822eb92dd3ca76f0737979714abf0632cb8b313d12ceb6b1e0e9d6622b5d315430d93ada69380a7e6850e74a3b704ba50a63db1865e090bd194ecc32355c465e8417552a9d0231dbc83e4db37f16e9a02dd1f38c74e824a0b33048c6fbc3faca11d10cf0f850b16100d57ad2cf3f2a906c289e464538c3ef866616432c426f13446ef1f48601a1a107ac2501823428a0fdc81f15ffb87e085119f3b345600bef74ae852f7fc4ade69052c844b3c3e046796e1f281bd4b7a557564b9e9777841c46e16796e9012e1f8a6fe28e1e1fd9243cec777c09ef2156e2dcdf86a374a070195f73f834ba26705ecd79ee338264a07f0f755667f0755aec914a6fae81f6506f4392849db58f1cc0152341a67f19a5abceead9d9b6b447052397634412fbc846bcf25a24d24ca3506b5bfea4f6a8e1660dfe102d8d5fed304d03f5eaee78c48b894d714e12a803f0f8a7aaa39c4252410e1f84e23587d73fbd738af4137ce56df5d08f0118439c4b8921b4863c0f00e10046ae2a392f62d20776e7355cb6fb8cd196a133064fc41b8f37ef275d6467010bdf4003f51f623fce70bfd06c72ac3c9456ae063a6158270ee43fd2cd75454dc5c5d63934567d98303719b85803d6c42aac741ac0c608a967083522841967d2c7aaa55e7ab00ce5568530c38001c9859730ab6bb7915e3e7cd9d49812bc26d22f4150d65be84dfa0387f4eef343f0cc131529d0905742f14ddfffcff9297139f119e3afb0ca3a3d242d2164949d5922f62453491f16ca5c593b2042ef0ae0892f5fe34cd64d778a503001538b171f46eabba8c7d692063a49f054e15e7e202"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"24579e963bbe8e3709e85476c22f2f95190811a67335572a6a9794a18a14ed7c","proof":"0e2caf66581eabd6b766a66df26bba54dfa33c307ec509c04e6f346953942e2c2c2c167b6f0f5d457c3f11891819cb412f8129656973a3dac454b3bdccff582cec3de0a962349ba74bac640ab23dbbd0a2277392b61046776bde19e61249cb575a8e7434f56a286e731347f9c2a4e603f2ed1d972c0e9a7d0bbfa1e71c31f8654085a06b63882605bddf9868f842614e0116217a46f0e570b954822b20c3270141ca51bd06f94954c69f9a89a028a11c4b01ba2fa7eae0b2d45d852e86163402bcb93ff2df59dae9acb88b1ff13c6bafca81e6c2b63909d4c541b358f8e51505c89a85236714ecf67e8824d01eb7921da53bd74737a4b485a158fd45737d8d2522bb44c783a6935b66ecf2cf638f95b137a0540950e95ebd98ef55392747aa02c810a4da8415bf86ce887344ceb24006781771f385628bb1b1e838b36a85906354219c5f252d78113d05270cc228f34b977f8011c6a569b147b9c0bbb9b6ee09a2a4b04285e902fc400504a71b9fe2193db11cbac65b552b1ea3c1b8ab01d26926b9e1acff18e9617aa626f9b6a773fd7385d057e02f700de703dc75ae780e25a4790bab4b243768ace4b70f9561ebc7e61afd61671b8404610fa2c34087690642de64ce03c3d0fd3e3bcee2c4524484a9772a6379bd9f1c18c65f357a429b5dd8f650d61d7ead58d05c20a87cb9039dc74f22715d961ccdbf85e5090626e57a809d7b7498c47ccb0b9206279b34a33c8d299e0d0c5adaef79aafd623b983a7740f39469e1fa78a4fb1f9b3fded1e6b27e0fccc2c6a6e5b02bdb93e2bd190e163cf6c79e151a8681507cbbd7fe1b170abe0e7a20ae9fa4a7039ac4a66b48ad0541ba23d7f042df306c34c798ed66f570f340723b7053b845912dd26933aaf40789a36262c29c6a23774c5f61204961fa5904db4256a2080b815ce83f051e0c07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"76052f542a9702cc8cdaece74adc21ddc2b5bdafaafcd0b61e72429db0e8b25a","proof":"ac2fe9a96c9f31e7c4425f377dedfa3d8a4372e31d6372af3b029d49ab27737f7c9d1d411eb3529f1ba5c12eea400b1c2a9da9283d2c685152923e5cc4e3c650ba2ae709a3b3fa6bb6cd0bed5ea5e72505170b1689836dc73a5f965cd5e93c3d5c6a6e4cae13966f65116e81f405517c4aa6890f212e80c04b6e050844b03425dfd323df70a673f91284ff43fd24eb37bd082e8be6b5763ca1635ef2601d9d046eeb2902e3d23ef30f24b579e524f3ed3e3227a11a303a435016ad872e19fa0046055a97874c0cb03bfc3bac31de28cafa8f71394f0a4893725b79386fb82503940a5537f232359ec8a5e8548fb563591a626f05a17b0f66b81bf286451722465a2f099a63dd7e8684f3ad1fcd0a3cc1326ae41b450c046226d6e3a0766e3b55081254bf3c630806bf5ddd2380e3e3b8ccc06cfc78e541766bd620f911a5e97ff2307804978dba199ee9e5102169ba25572e9c12053ca40d7594c126dedfe02c38217d79e587e4bd2037b836a19ccfd4eabe51a0d4d7b900fcc66e04041e6c66b2a5d0c935c98b802e188a51fdef501e942779b3c9150fc78b88241585f32b647a472def506c595b48756e0b73cf81fcf452845017f913bfe1ebf6debe22f94cee4e2f59c824114f5c866072d2a563fea395491a74cd04f39f3338c16a4d083c52a1287d56bb4ddf5f21813c3b345bfb49f2ab3694738d45fb058bf391cf9c595e5b065d66c705417d2759642fb78a45087ec57906f19d15d367a2ac91f70009fe9b6eee30d5d6f0ad3ccc0ef758f4d6dd1a02addbe67b94b719c6c6c75ac21b5ca32d205f2b89349e27137f320c369fd3707b86162a573914745f1f45ea64335427dd9c6fd7e351fb5ae67827f62b8be9a92f4d6c92ae7cdb34579783cbe70a05724529cb9f778498c518a9c6201b4e5878aec55c420ade53f13ac64f51b901"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"60aef2e068d806a85064f221f463631be6adb63b29d3e81677833a3160294a47","proof":"a6cec18e4e9c41378b018e4475b880f15862e86288fb285b4363eb5e34202e18cee2d520cc22b627e8062ff92bbaaa71d4b3315c681587e467ff29524428df26f4467e02c912b4131486dd90bb09174e71f92e2586f1fd62e713a50045be63023eb26b65d616007efc877509b7ccb29bee7bdc3b3665b813282c4e25191ead61497fd6f682535f4185706fc26853076fa117611033335c4bb6955fe6028d1500a7922e995a26bfaed63d87c5fecc029719c3b45ee2bf6fcf8e5d31b00665c60c462ea3c04b5374f0dc9ce6e6adbd9f48862566a0313be8deea15b1eaf01a2e0868b5e409626848421c494857052009676e1af09846e3200ea6bc854bafb3926e701aaadb801836a927c88d8fb66fbb1ead82d34502a6e93c1d35604fa59b8d26bea1fca2f9b1261e5dc35ea5e18e5f7fe6fb56e884b700921282322545383e681c1157247bfc077bfd82cfff3244b72f8e32231542948e5d96d3782fec4c723af0260adbec9d344c3e1ee8a61b9b09f1d5547f5e1e18c7a060dc3b357131af0fd40dc109fee5b8aa52ce6e8bcf908b175ff58f3ace52052444ca2d52874c2d05eed70f5165fda4c3001334e3a00f6bbe0f92d64f6700a0978d5a6e1377b9846850508cba0170af6cc6a43f725a36e5d31add6e2593778f53de99472dbe4f266a88f26bb23f63b295aed6d0141af0b2666325d4feb62172dad62fc4e2c429e3700cf5e701b2aa770033e33dde64dc68c0496bb3bd080d41a67352db2128e4b546a8a1642eaa2129e0219a34366fa9944bfeb787cb3f5031d76147725d18617424944188103561f20f1443ac6320b37cf3e677ce1140f2537d41a36a121e90d92803026319954abf5c2288ad6ea9819f74fb84032a8951c3552baf9e2e825e58058050bcfdcc5c99e1d15c16939a2b787f26d452d315587c770433396d668d810c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2ec580ee7374d82cdebd8162e8190d11fa4c625046c3ceb85e577c43c93f9b0f","proof":"7cbcd27aeef5f157859a28c67df3dde1ae31d2cbb42820362ee8c737778462735409b349afeb145c61e78dde92ba0e0d02d504ec20acdee22fb1e7dba4060c29d87cc8ac16fd46fd6c37e0bf9803821fd5b3d19a788fb2afa77888d6a7fa79190e351ecfd28d84df30e861c3f5534f9bf5330636f22c0c3bbd25d65b0cd8a37eb6f13cf635de5f9fb54f3de4893d3351d2b38e63375cae85830bf9fa7da58500db8c3a47b3202d289ad8828ac0418de30ab819bb053791faf383a1d40b6c280ef66f5e7501012bea3e8ad284858126ee58a2a82bdefb1fb11615ad73d4a0f40f04d4522e2e1eac43bf8d9c59a2a0d743b7e6efb6b250758db1beb5156c1d116dac7597bd67a83c24a049bd34039bbaf79324b808fa2651cfe55d4397a120f039a0116bc18e101ce519cd13141c44b696a3b037a73fff8cf2febaf53309e01f254a11ab228bd3369573c01b7e6e0afd7fec8dfc3e5fd75babdf6e3a0fe05c330cd86c620caf4dde72f3964e97e6f426588953088b8680972feed951f588a6bd327e1fd531f042183a3056faa64726a175adea916f509c40228b2d6fca0757aa37acc781c78c259274d8bbe09b4bdbf2159e3515cbc6ee5bd1d6d3db08f720f51cf8903402ac737f6b16cbb8acbf7b1f98ad0968eb48bdd97a8783999d358ce50fee4e0feb067d37ff974af5499464dbf7902683bd7c568e16ff72bb4a96795c33bae64d96a988ba5027dd408f0dbc07a0f3769101b39c3c925928f70e4fb16a15e22efc495392cbe890c9d8aa4a21aa5aff28844a984e39a81f9c315904e0b704b055c7af17cb927a30de75c1dc6bdcf144c52b92eb180b177f0ba293dbc6a47cc655b155679ffdde7abc57f1460fba887aa736bc6a4573854a8663609bdc56084b109952b726b6fc65dccda28722bb6816bb92ab2ec4796c41c42e190f02410e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8406d28d3156037673b1803fa492b75686b42bda6a852829efa86d59040c5069","proof":"320638001453475c1bbf4675edadfe270a89988d2925e6600b0236841f432b23a8b67e43d8109581296d5a94c509a23787cce427b78e67910bcf24ef06908c798028b53193e905dc99ab752f3baa42b87376e8f832f2468343f1490e8c43410464b9088332ffca6e9a53f6bd4b75ec763f491c7e6fb8a3718e0de209cabe3648a3cb92f46701054dfbea835638bb1d69db015a24fad1603cd6d7b14e90ce4900981118d557c13164d638f365b9593cbb2af9f672f07a02f1e3265a903867860adb9cc8be596bc8d823e19957c89d944d13e7509e01d19262dacdae9f754c60052e0b6a0ad1278dc94f0d6a7ecd3d8e6cd0feb0d6081c27c74589b83053ce92776a55f1846af75d136704489803e4e8576859bcb0565e9e9a6e1f324161d9675a04370ef44722036d9d2e3d26f30d8867cbdc0f9d07da95331977cc823b966179742da32041395ff92c91fbce6281d1d86ccc0e562e422a023ad0782a4172ef12a09101ae9fa699157705145eb715fad87812f266634cbfd6ad369b833afb106c26506c930936e6f22b4946934bfb2cfb66feadcbfba63dcf25828faf036ce4408041c3d0f3befc91fcc98a205ba4cfdc2cede6513b349c8d0988e770189be21854ada592c0c1a389a236194c2c41ef6655fee756fe3b5a7e8d5a5eb11520294aa6d5ec189d08194303100a4a4cc79df3cefafbdaf31e05b03a40910b4355ed1d8c43a835e42fab9adb7518cca1207f0a7910d916b1e08f82a92f153c23e8306dce49e5f1cdc956d89d6ee384354960ed6a66085d500faff91a2b5e631c9f7613ee8522db6644d12016dbe0f7b683d9fcf2c268a90f1a3d7866265d5796d30d16ad328ac83ef3887d2d5cd5971ab5850636814a22921916d1674a2295b0138c0c18ac919236d786e7a6df8cd4623987d171f060ada293615af5fb347383bbea08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d61c2347e1ae24d884cf9df2da2f4f00e61e79db90e7802cb195c9ece51cdb1b","proof":"de5b84008e3672461cb1f54cb5e6e478bfcd8d3d56ff986a2dcf9b12ab1faf77000277c214d137b9b1993860c6cfe64e50694ab0611b18465e8cdf73296e360ebcef4c4ae6df0194f9cfe0944c5baaf25f20a628a71f126f0b07662382c6bb3de8d2d887ee594daf320e415d8653b606707e35ed5d19985e6ff2a3e3a9f9552868331021f459f59210821b449d45eaedee750a58484d3de55e4dd851cd9f6002e44058396f173533f1e67cd74e9794d213ac93824240dcdb7d1a61653817a807c7f5e05638cbaf97547f026d8657086b78641a94e54f4e496c9a7d775a651a0ae846673e351391f127ff6f4a5e0840a0dd410aeb9d91505074e03213c5f7375a5a8041d7be6f58a4f5f43993e3c5f4f5287d8dcc26e8d0d24985912d87d9023e3664a65f61a0e3538c665b03d70cbdbc8ec017aab2d7c13ff9f95ec2ff374344b8b82d041a400a920d5ab4550e85fbcc20eb4df2eb71267aac0f060758d1784b6242d6f4321f7c86a8bcab41554497bf3a26a7793ce374573f11149da5aa713286cb7ea98cbdf5a6ae456680c7f29ff88375c2c0cb261a47164bed0f00e23b43e6b4a67831a02cb890a0c8e3035d35076fc30b45ca94fb5265f8f226eef02c12a6ac35c5a388e1cae0b135d7022c78a85cc11c159a1a444fbd2191143d772c07d47632418621774e235ad18b305bb290478e580403d208fb507134edf0f35b7ffc91b22a4ee7605d0a2f81eebe2303c05f4929abf3464042c235e4725ec38d6b22a4feeeea6559d00932519e4446fb9cedcfbcf8e2d30f5b5dc27335adabc460825d5faec8054f1376afc0527a4999ba0fe53b83d4a50206245d48495ff0c57d2865e79f926a7ceb9f491c915115f33df182ff13d34881834172bf16bcf85d0b4c94873caaf0b846ae84a86a97e6c27bb1d18095b195ad83e263c42769cc1d0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2e62ef4295929a1e6843348140f0a121d64a1a420921d23bcdb28c22cbfdbf29","proof":"545e896be2d5c332011e8a644c1311116fd399997283854b199e40b7e798914cce5d87992ae6bcc28a66580769c9107e39677e62278e7877dc5717aa6a1fec0be8a3262ef95c0ff8bcb283e9fd0ad89fe9c049aed1b1ee6c547783d44b929b40a0edc435a7d80f76d5d4994d3ca755e3e89dac5c65d3288a53ead05876b16c7d5b7b39f178bc0ef0ff1217418cbfb1ad8961849399d6a0fe6d49e3a955f07202915124809efae69151559974d3b9f7128c06ed023a7c6fbfe6ea317fc8934e08d1ea9547adde6e2860f745e117d5c386d220a4704e875dd34e689c0a01a0190b70a72b35f39e2e9b5fff6965df6440746e39a0ffa1612fd3c9ed7fb2c26dbf4e1c05b8d53385b8dd1042ab57332bd8102f134ac844d239bc78c618933b52fc02f0f1358c87340da8fbcdba6a9d93ed36e99410ede45de840bcf476d9e007bb7caa600f9c6d5026ebe46f297f1ba8db8a079c596bc9d0b776e8f0b0413502c452d8b3fc83d104dd99c9aa840acd95a1bb16662475e2f2a228fae006a40aaa9b2e502f9915e77df86cc700b1775b6ba341a724609de6bd2a258aa9b6a5420d4e27a8199fb93c7e873755cec806845036dd4d7f3ce3062efd3008056b83aa28d35c60913cf09713adb439e6f1542bc0eac394bb546f378449bd80303fa781b2f358e68dc9697a1b2092a0aea1bbf45b06f8cb32c276d0018670d543024a6507406232b95a52830b2b059733a3ffc9da57eae303b738904e2758fae7a0afd3008311ce283391f42d160f955a7cdb3960109464bc35084518f8b58898c3f63acfe0297499aea4b9db0d7b970536bbf15bf471d467588a1d7c1d6d5578af804a04bf0237b23fd47d62b0297a0514d88b7160ffbade188e50e4de5aa367215daba03c0f938f742b7a3ae92860458c522d66fadabe155dc36669267d30de7c16bc200001"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"285866331972fa58ab808bd35c500079727d556a0114111550fa3135d784a111","proof":"aa7d6240b4e543f9dfee99a80aec4afd9631f8a18422e167ce84166bb563db3ecc1dbe1b3c73ea335a901fe75170a40784ed9a93a157807ae89ed3d633912d53ba174f76f873141b79c0fa867aa80d376998d3e5e9097fb75ce40fd4afe16a787248fd6149ef0b10f7a376adebc0a246877308ae81f35c9d529465130273c53c38a565bb82730472f9a5a147f73ac405b1b83392fbbb0153f3eaff5e6c29300c8e1ec84411370e1cc6fff9da92d6f3c0dbde8031fac29d1d284388065ced4301b2561fb7c4e569cb3ec6b3e2136ceeecf645b698b38f2babe6ce3def4b81c50f0eeeb8e75407a7992072d2ba89aec8db215124c4d722f51e25a7d94ca9a15134785d08f259c6d0e0109c6a9dbb72006fd60ba06f318c6b585523ed8446ad26554aef77f892a24eec3c019cff87d6d00c2c2bc862d0956acc171655878a6ff06ace0aab8980ab8f59aa04d0418ec4d6a8fe1a994993d5acbf5edb7118c77aae002c59f48ada63a34756ba4db8f9910c2c34fe28d77bac713bba99528e23fdd256b2f2e3a83bbe22e0460d942eb532f786a188d18900d1775cb20b7379f9065d4eb69c319a017f379c6bf5a141c54f183283bc6f76501c2bce4422b1ca3e80fa6d32a43c09f7771601c0545f5d0974450d9b75cf7e1278318e8f61969b51475476f0734d0d5a7fa7abcb0a8e331f82f5bef0589382676588b2202e70bf89370c00bc3427fca75ad00f54225836ddfb19e9d3b607c2a0d80977f6fd657dea4b1d38c283764a3cf0330f5e1a639da9b0c03c20c6a8093684c3994995d624eaf7704d66fe326842d35de3a588869b1032ab184be0364f41a7ec9edd8f58e8bb128c50568df77580f30630b711f0fa971af9d37e63e17cb8bdcb153cebd8899d1f1e0e938591f6b7c1f576f722eb5f8654027db754ed10ab4fac08f42026052a099b09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0091607943358b5cfeee2353eef4c699b0a6ff7ce5fe567fb8284b6194b8f60c","proof":"8095214d9a691c4d496901e710b1151f611ef2f7224aa42c46f7ecf1c940d43b34bfc812526b5c41716de37b10323ad23645dba59f01cb3a2c102d5b69523b452c70440b89161b9b4b0932b0cfa0c6c4d28c7e6069b0585d917d4427c2d44910bc20d6b3bfe17a4ce8bdb8e85040e0f993099bddabca99c09a3333d2416491161951637a5232b69ce0628fb796a01db4aec5a10a495831a0e0067c0a391d680a97fe4e8891aa70023e4d5bd18af234c5ad6105186d11fa02762e8d6115b4570914047277f8769db830fe3e9e3f1c1b574f1c6bf381cd712cc9ba96edde20870b2c87b9ba8da25a522d78fd80f8dc64b0f6dd100b77b7b826c233f4df9161a750f472520f75f783f25cae1dde19a8be4ad29dd61d9426c058a32d44fb6d8901767459d2d63bd31502574679619162a8da8931ccc8b26f1a4510add9bf113b027234c1ad93a75059c9218bf1b7d031983ced856cf7fe5fc393aa26cd1865292418e265d3b2e3efaff4aa6e446bf77aaafe2c2f0bfb41d9bd288d9d412022f6bf325ad2535b099f7d3b764ffb80afa01e4bfb8331da93e4067072a7860ec9278e379c5d69e0b35376d33eed337bb9b4f08c52f9068789d710c919938b4daecda83c96561cea0c0c94ab296d02609e21c3f70514d0467987902a7b05e69b9bc6d27d48c79f445a7a4935ecad869f53b0b9925b02816e568cc07c7eaf7a2d87848d2c74cc3422972def79d21b1defae53da7bc1a5085e28b2cd8839a10f3597853164982c4c4b2408222d7dd186112a7d99db9a5103748629d5defdfbbcfb8f8e47237614233e2b64f8d5bae198774ae733bc53305fb2e8bf89c9f266b5badd8b826303461329b6a6f5e515a69c4a157e15720c08aea7d10ca104848f0f9637f6be0174211bfd7ff76341a3073987a04a9545d847870dd4b93fe1e05f832da1b57804"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b0871c1f2855c5e04e64ec4fb424c7a8c5d2a5da1d454bfbde9769a30f15ab70","proof":"603b6e6678752a0facccafbd24d81137e9917dda00d2251ed2d21dffbc42f65f2e02c57706695c0a4f85f3af10002f2bc46121542a94eb40050ddaa6ab0f0c10ec4043355e5fc69f81d410627bed4f98b98a3a67447e049fd83cedc224811c41e8619338177b867bc03079544038e7471a372b022019e49391ab655cedd3f7663b56961566b22c77a58cda3ab1e441fa53dd1d3bb2c4f2dd4de8252b51c4ba0035c29fb8271e848c038676b996cddf628dcd73d66b730106e0227a5a9f44f007c0a667cde221bf4ce4d475fda507e9e4334ce81ff7fcce8d0268e18187d50d07ba4802fb9e5d406150e55052b942afa4998bdbcf72579ac881aecb6ff470f02610069e55861a06b608927f506b599d38be896a1e8822d9bc0ae0142fb20bf27cccb571418fb4c92c6aff237dc1bc0c5380f02ca48c273315c92ae2e616c68378609f5bbcd6cae6949666ee95364841203b8a3c279cc7db96522ef2d067ee665134487b473a70594be91955dd2163428e9bc43ed145268b535bf934f67ffc0350863f5ce788e9e2cbfba9d46155ea37b24f2913ee6a7400131cb3eb5353c2023458c3fddc5184e2ed550b23269fc9af45a06c15c932266d6842572fef189302756a1835fa810dcf4ca41e8ad9323b1a21ba018eae88e22106a516db1f3cb12c10f25b89104c441e392956e9805523208234605050be66f3502152d034a517e56db48ac727abbaad15743ab7ad9f2539a4bc1b29678dd42cb6e780531bcb6dd97a7a36fdad50b2dbff059e2fec427b7490f47f8ee1faf8193322b11f001b56b8581ce252d0adbe0cc798b4877c4c809a16e203c40b4be1c1bbd5b98d4afe640a221e6edc7fbd15e2ebee61a2d2ebd682058b78aff201309ef572b9dfa2a80dd80c98feaba17acc5885ac3ad35bdfee8ca578d3c33a1b0612db8abab7278e0e4209"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"08577db35787ffea8b04a8749c407b3d311b35ff8ff10759f5e6760ebeaa6c51","proof":"546b03dcc89a48f743f15b536e33d905733b3777e8909000805b8abba24dc239861075ced6d4ff31b9c35ccb264c1deca09604c4a3357025953264a0d2304443d647177cbdd3accc0704299dbfe845f5ac2d018ad8c7d5fbf6af46521aa0a340a4e152b808420dd7249ded6fee6c985455f45904e162e22fd0bc01d90a87e7221e6bd86ad8109f7eaac6d199da3cb41a9068d47658f42a6c57838948f1209c0adb0df13e20f523101ff58b1a926599612705812c9b22697a73d3e1239019f2040b9765a257ca5e3b071060dabb3eaa63ec3f12b1705611ef7c48cf80f3cba000e21dabf9e6158dc0a990e30736710b9e7e93de1b543e77d0c16036f6ba3f4c144e27ed8273efb697ba07a1fb265084caf170ef7441d038a02bbc06b7ec161b7e5ab94517da8f775463150c4931af49784be1999bda52c25549b46c20ffa89225ce2e6b33a0141ba24edc4c856747c30ebbebcf91b5fd653f2aaf29afc798c1289c33d5456132b8d85c807474a63f797afb54ab3dcbfbe68e2adaf9615fb0c2795621b897651ed6f3c03b7c9045af57a4685240259c555bf12f79521b0bd0ba7038130321df6547da1a5bf28825db404494dd10cab4098f5956ec8930cf6f6853c6cd76778c844ef95631c61f7488a6d6589b904f62a22410934e5e7556c94256d2d5cfbd226ba748335ec037cde7505e7311e0d903fa1b175d1b398035f606539ae1bff91889623bddc75528e2801b5ac97bd399815962082789429b1a756d7dd20f3b7c71de35c7d107f0def1c4c740bfd1e679ad0901bab36c332801cb4f6af892423d4227a65b8860237729f0098b8a9604c243ef2860932654439abddf6b18ec137a0c54365eb28bd3630d86358ce8aac9dd047377b157881b2f95055e02b6da33a85d7cb7b296757dbd15e01199ecc801025bc8edf44f0c12c4ba759806"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e4301ca91d90944cefd555e2d41fa094c068c44c00e6c26df0c115f1d0c46b20","proof":"644588df13df266472e463fb0f31efe4ddaefb802583986f5ca1e2f2eecbfb20a459d978278c5d221713973ff9903a6dde591a7c6abe2cf5715bc3172f1a976282c2ccaba1d6eb3f2d3735ca15cdf197e5fe62995294425c5a55685f78c53c42b810516031e9e20ef4205cdd72094218ec6f81fb3d085ce7cf4eddf5e754f075b1320215ba4e206e075b52f52f19e6977b06585216d467f9e02403c312346201b6c287bba975b8963dde641c152906e9fe1a7c2eafbc8447fe4c824bfefb1b02db30085280aa0010dab9caf75f9681227410a53291ff2aed4789d395d62d280662547b21c5b7821ba62063ab90103b672f7155a9bff2b64313d09bf2008a636618cb4d7b4c223697cf077edf60a4bb260ad36c87694aef3233fb97c1cef10f42a862d41d8bf258502d247a1bbb7f75de5acc0adeb33b444ec331d133b6bc3446a291e27e01ac1e4eca22d7bfd061b2e732d5654498c19142429af8a400aa89574071eff1c514310595bbd51eacf77111987dec756f79847889ea2a0d28a2d740bc8bcb710d4bc44735fbf751f0057f1deec6ad90993b6b827c5eb985d30e3a7c70af5865dc4825034a33fc197d40c86d55f31d68d90ddf7eb7ea20fa6685f66f1c3d1062f0540b3c5a1424802507985029066e02b268b66ae9c5574fb22c707be61dadd9d86e966b4fcc010576ae291c555db92cf8db0d5cb0746230ae700f2f76ec4add5bf4982f554b0de0c5649f4f1bb3be626790d6f6cad4521b3a7864164a72d3ae04fa89ecef57918cb7392ac3a557f3bd402e54c5a8c085dc943545163eeb61ee997dfc20f17ce91ce20865906a76b0a49b4303af1e225c20a2a0a83871cab4f45081aaba8ff7d26cd40a09b440161d795872a9527aecb54dd6e3c20913c72393d807a79495215125a144ba9ec180188e7d1144fd5acaf8166374020c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f86c74ddf82a324b250c61197e455bc08121da33863aafb2dc934fc43df93129","proof":"1e3abcefb455037861f106136703e7b82facf639134a73c48580ec729bb4e61ce493f8eabbfca9a54e59a84d1ba5c6ca477f2c4d3392f01d0d76ffe1597555028cf9550f031c23ff5afea0f6a1d6b8f24fadcfcfe451b05df2e55a9396faec2a2c97b3c7fad31c8248f71baffc3f77da7eab5cb2f2edd8e53f0a223fc1d68e04b4cdca7d5a8d433d8fe934b97af44eaaf342fdbbd5bce20e328f6bd170b7740f1cf0890b17bdfd3dd5f918074edb74dde8b98d100234dac7c9cae3f57ac452002d1299f8f22231fdf08e6b7112a2d013680bb722eedaf7351d925ecf078bce02f475e46f3b5d79fa081779961bfb9ca458d95a21b1ceb7a7bbd2e62997b1d718d89c613f1c7b658245d2b3cfc4b6fad7ad8c9888f85aec816ccdd1872476890e8288731ae26ae1a6e54cc091b47b9149f235324b4663e26f26538783760f0678ecd74629e569a4090e66e603a66e193d61c117dea97ecc16509272a634883715204d8968b7dd6f0f3b63def8acf3d43ffe428e6428aa928f7e377192d467b9280269107e09f977e7ef14eeae901f5cfe3392b26db4d3cb1926938aedf92f2662d48e11438c6f0279a607f89cb6970876600a920838b6dbfd33807b311e32612a5ed4ae4f957e28358d03990eb8cc662664f08424fdc35ddaae5a9619904c860d3cb58b8cbf2d12ee8cee45363210c44c0e64898b63d28074acca7e5a79b76064ba683696937da3dba5766dc6a57c590b00cdc6e4e1030505bdd5af647648e137e46c8c5f31971da7237710392362decf0cc2a90feecee843dcca0ea1f81ecd447284310c2a9d1a6b738b87edabaadbd35237c6cc1bfda3ebf0a5b3fa5a8a10159eee236e6273e3f85f315324165fcba0336c273c2fc399b383ecc00c832297092dfc5efd9196d5d4416a9e13264889bfc7019c0b4d03072c11c42b1a95ee050f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0a703b2abdb620359de9c128a00e49b8fcdf4eb2b6465ca9b5328614ca381b08","proof":"f8d1d7de8610500aa686314634f7765b3e7cfb19915c9e1d58407722aa599605eee6725cdb854ffc8ec053cb626111104c8a5497bee5006f17e2df008e5a0f470c2d7e3abdc09faad7185bb43f2c26a93c4f15f1dac0b8351f9dca8c8cdcec3b1a164e10b5a527d9527a75cf6d1efa3236c7b3e69d784e85d9f53d369ad311308870c499a90ce6a646bc1b27570f8c7473fc7a92c62e5e403162e5875e5cec0b3f514c5987994bd7216f1c7b654b03feb02974a2f6ac00f688fb3f09b1c8ea04db914ca6cc3d2128b655cb7dae0e97d46e274bbe6802a51c434a95ea5368d90c1825b8288ff8a5b2ca6357aad86dc68c69a59dd92710fb67e65ea14a24dfaa4022f2392060596f0b8d7e36e87724a2f44fb955538598a17554b31c7ae4276f0c3adf3f9ef3c6d5dbaadadd0545d3dbf3740d2d3ce700e4ec2fd08063c67c600a5e3f1f9c59d07e5abcddf6cdbcba35ce089f15f48ebac9faf21962214b11cd0c1a8c3241f868710c8abf394405455e553fe44e1d72da1ffc64fc5f92c31b8c33c631e5a2ca7d5eef28ee9c9402dcad97db821648ba4d52a5431d22909ddad567fc39f59951511c9d77a12c6bc703725713737bd33951f21dedc6a28ad7fb662b3ac2674ae6332c5fd27aa0c46aa37f08f53481243526b7d6369805f02f96810478dea0aaf3b153f1b532d16f48ad7fb2007b4c5ecb44cddc1786775007265c0dc41c9898e22b4bfe036179b2ca66e2aaf4f73dbf652c89c103e295f4f0787a3578d784dfa7a366621f98c1f079471c870746bd394084d763a52c6495f648832f4055fec4e38377ada3693de711c37150bc82446fb9eef060206e7cacb3b5ca62b7beff81dd2bf255b616838ffeeddc0703fd34c347467bcaf5a28ac40219e401fe8e96d18ba9c5416810ff59826c1e384dd6d8ae0029bff15939f8b4e292040b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4029c7c7acc587c0be245f4195529afdef6a5bdd79cbb9464b192eb43366b441","proof":"30e3e7259bd7030d1359526a35a6664b0e80f4e9e9d510b56274f78263e68054a61810e27533c8486917194468b4ebe466dd66c8d587465c66cd969feb85fb3220e0d87cbb9e2e284592f5692c598df99efcf432019fdb67b8e4cba6f645d07240d84ac4c7d4487b733bd2aff03dede322a3bce1fe9e094f97e5af4123a1f536f12e57d05a42607a006ca89638fbc340115d1f2b2d78806472cf349da2d6db0c925919f4cc39619497612d3cb0744df27ac1b690f759bb048eaa49a246066708ab7903dfff013a439a4f6e7f12a3ce72d2226c81596b98f4d8d6725c5d024d03d822b41f864dd97e6f40cb0e299a827a006bbc232e21d6e4dccd91f6335b1655003be23c3292217d3a219ae093795d8020c1e522f9e9376faae0d4fbd1d9b4620e2843e8afa9266eaa7a2c5e7fe0548343c01b96b49a6099bf01cf13e3fe792f18b216b0c94cb859a3d5251a6ed9f100edebd33ed1a0b99311003034bceda7119850d6f9efcc55bc53d8e88f2b5ac66d1d068425a5f1023815dce01b12818d1b0e291fe26aa2a98c3a8acaaa75e6bb0866575fafc51beac804087d60fccf9b6408980462a2ac24c63bcdbb0d7cbbc9022ccacc0447503184644234f41c1c9c2ea6d44922b37c2072ec839b257bd396a9217cc61c6dcad4b679b5d0a89867cf23948b7bc1369cf595be1a2ba6e49853a2cab883bf628a1b702c10d4554dfc2b28b8d154b50522be18bc7dae9cd5b92ffa32cb12f44c74b875803f3f5a5059157ea475d9f18ddbe6d7477652c97fc776041203413d9408690a04e1f5a5f8d11203f22c37d71f9c6aa2eeefc130125cc6a213baca9e29874fa36c928660a59f92715919d4d7c2311a8b375d342a135634938976f414ad464428431455c2eee2ca0f921d08d18cd56d8809629990b95fd88cd1b5c46696050c4cb2eee518f9e89509"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9c062260bc671ffa6f7e943e6c48aefbe0d8821fdb3c78a54b9947e1ff64fb5f","proof":"4281b5825e544e4138f166b7fb99061f5c943cb595378be5a10f5dd04ceb5b136022692070fdfa07dc97a7397f1c5b8048ad5d15373039502e0aa0e821b8693ba49de0de5cd76a1deed9db724e9a8de6d969fbd2285344e4910f7b5763099d4460a0625f9977fd8a54b5a3a24cb97432ff5ea376d824aac9dad7663d5ec7fb6a92f128b2304172f64db4c007d039a57c5f0b77acefca6f64a9d5a59a51270f017683ebac3eedd135a425e848b777a27546763a7feef1fbd8136fe72b66f2540e8b4b95865e4fbb063d55f27ef3344d4357254c8f518d376cf2758ae486b583034c29caf0c8193c69620d4344e27df11d65db976215cb0d5de0ebe72b88358d58927b92c8d45291b619abee6a39239805f617af50c4a4ca6f7a2eee7d7ba23d105c3994d466e5d1220aa89ef813f00e00316351675d066ffd8853c435a87b3b6440a3abd4398c10a33ef0cf93f9f930c25f36a86b617f3b86a203696db63de60248a4ff4f9c0127252b76d9445176c02e2f44b75ac53e3e581ff0994ac6182f03be1e6e060780bf0fd4c773b2db5d6b3bd0928eb96ba2b1c8ded74b51d164b2645cab4935fa86d0e80fe714cfe2e5c36c36be65f8c0bbda5fe8eb6cbf5a8d5c6f08f180575b05958cf658e73601d92ea7b978ae52979e3da0745d35e092889e133618339f78160cf16d99c3c3f753e246afbfd4562e48ff8f492554d8887c0e1d2a65535044d626e38551a464dd6ca11763d8cf314126fa578e443dd5d0d07274c66a6246b6922bc6da5a9d05b498c0297f11af41ce16002624d0d6fc922cb74f50195ae733a4e7b91b70c0d7e26a96a352db187cad5ca89f17ce8e6779c6d10dc641dbf2fbb15b8b00eb6b081e7f57fed5b064491ec0a25bf2412783e3164d0af97eeb437f40b76ef958c700870d827752fd2edc1c883d9b9e8b4339fc962c0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"94f50beae3f3cf8ed0062f41ff1f3ace92d6bfac2687d8de51f2650aae930015","proof":"860960b4bd72a8e92455fe45a6c4bda3f8e18d49c87b33793d209600c45df30456032f1d468b24e0f26a3faabe0e710a8b9d9c85a847b68baaadadf8b93c95038e62696ff7877ca456f03cb2c556e5a6dfc726cc5a11ab89a9e4b32275fc2b22b8b3055940d1b97628bab95f189ec2d91ca3e6fc7be1d7f69ec5002416af9316434c0b11c5733abed5c5241f6598e7f45fddf66ba816db4f992e34e96b55990b6e7e1c4fd1559dbbb1564f8ecf34ac8f6e29444999abebdd980b57038ec9770e92b8fc479fa44743e984db011a6afef60a517639dfeb7cc5f353f84b7dd30a028c2363b37e9f340dc23dae3f02f197f7d4a6236de2f6d66e8a13e60de3c70b764ce2773cb45c81e81a0bba4103685e90337d9c19b0c1c60890690ce56cad7360ae73206389f8a43ac730f05e12d19a226216d8837c42c237940c2205090e2427ea94004060500ece0101df8e368f4a0fe2f0ce5ccd4785ceb0db893e32fa494a6215da20a606c54360eec06adcbe235f82f6f1cf4cba1775e5b82a91b9577e79b8955db6f17951ec9a707a67c9cb04e5479dbbd2c2d7d0d8e613b516ac61685438ff55bc8ba1eb836e560a494bda3aaddcb7931148f5d9e8a655e8218e77c868bafee90baa18ba4fe7e8dd167cc6c6079ae63a7137a7485d0b0659938fd7ee01fe2608627c3ad0aa6dc99a1439f4829572c4ba29ab09cb16cdd811ff60c8954d7c42754c61845b033a3416b3f36048fe2aad4339b21195b95a24e8c6ff5fab6e506f620056616d3d16fe8df3ed8c63a97be95c1224f94045ab1dc59d047c082f2c4029800dd7995ec74e4a50ac53bc532fcd042f7c2ac654e25746cc7c4be9033f5bcafeef4c91e90f1409b3e5219f5f11ac9ff78a0c3adcfcafe27b901cb70e4ba3bb341f1c6442bfa6b13b947406400e01765f728366abef19c46f07775f04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f065039219f7e6362c251defc82c0bd21ecdff137fd204f3380ecb710c266d7c","proof":"869246f67944051ad5d81aa1e81eb9b05710eb028e59d7dbf05f89e8015b55428a4d2112264396418d868e699678416b04d675f79f42f6b32ebd56c346c06c74c695529b881ab3807bd938a6d4854d4c0e3d2946c4c7cf5b00ea48d11715164700dcf5dbe5f622c8f4276e944cc04dd88ea21fffc53d77a778c90a769fc5396f5bcc50d03594b55cc6f2fbb8cd4868177852b80974def002531762fe13a8a907cf29fbd1f0adec5c1bf57bf7b12e0c118072b35edd21963c1e99982fef4b7b0e096b7c855fdd85029a264f35a3297d484ce9e7e32ccfad84b782e0292d9e620b18de8734e81a50d90940abdfd0b5f67bdc20f69863413f22c47126d44aa6dc01547014261c0996db1cd58f32cbaae308826db09226d874de396f4f32020f5d0f76e0a905943c36093de58f6524f9915ac107a1a14e598dfba36965b8fdd1e43edc4ed748083c7aefde57a436aa6f05d2c82231de52b40e9d9b67f60195b43a186c7b4f4c186e9248794dee79bc9dc2073581b23f15294b0e6760af050cd7406590370b847c4af5ad283dbef7bb8c428dfd86ef8275bd583d8a19c23acd8c6215482a1ed7412a4030b69c35d11cea39477884ca426c1654ca400e9f8c09763d29acde019d131d57e7ad78b1dd03556856cfe834b291189eaf9618dd7f5470045f2a44fe84f0ea455309e5908289932974491e057133ce76769f75c32355a4423b3016a4b0509664236fb34eb746dff8f570a0ec9dfc4c9a954572fbad2303122e70d486decdd4c5f13e746cc0fa37ff92213fe3b0c97aa1dadcad8c17aa76511c580bcf55e1374300a9086255a2dedd7c44f47f98569137220e42f23d4c11992a380535f65c79e9ac7d64f5e2396fbb44c7ed50db8f7b5abed7482f91cfeb3802950731a05eb1bf3983eacbede3d219a1e7db3d71795f31f195804a0f4c5dee07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0043656415f83887729de9ca9956f0e1f402c51ee8a104c5e21af45d2b572e70","proof":"62b4a9bb767fd74f008e066164d15ea25cc6774dd596fea2ebedc29eedaaea64320b95b3a444799491d569226f7c56bc169374f737706009c6a7c6ebd9a09954a622f43904e8a04524d64668cf588ee9c71fb5606e8916b6154f166fe1edc05296a35f2dae5482091bdc7c540d0f44c5c4cea308612f798ba1e219831e1a594bba8a3d73c6938dbc4e97432830e77eb1758501b62d51cb3e3e377ee127b31c0135182f7474d73070fb1b2a1970c7ecbad174b2dba99a5ed1c81df1988dd1b30428a75fad0d4df575d1139fddb78b03ccfa8fe6d0ddb9624279baf87ea3d6670ef6dd6aea52279a5277e82b20c651fdb47050c04d76e7c8ad27f4433248cd0832b2b4665ac222e9d80b195fd303267193b88b2d5a74ad9a59461496b5249c1d64903e9ba488f77300d26bf808b3715c7b04060976c3fc1e0285fc228e8a43ae058c78e485d0be1442c2f8ab1a6012010382bfb43615ebf699af3abe91179b8b30ce62f514b7b3c8762f4e7df7a9e88f0f014ab58d0cefa439fa42ef1fb6bfbc192210ef2541e2544828e281351e3a76533843d236ee01230d9d558a2fbf041640503038e9cef7cc32ab598d96d7b1c27be5cd8ee536af62bc60bcb83f0943b33cb023d75b768093c98856016f13383bcd00c1fe8ce4e1e79c1eac46210abf941f463eb597d532afcf4d881eb66c1ccaca42293f85b268e0e6ec27224b7843122918b15b620429d2fa82e9bf66b7e4b747bf14ba63cd1d485b28f5a6bde18b112adad1ccdb351cb457cc51d51c5f7c3f3c172c633c579a50025a8e60244b1cca6fd8a8d866423ef7c8ce4fb7529732e7f06f39eb6c219ae71bb5b59afd97a5977802ec508a60062ce8f4601e8b8277f1d4de004b5f9f7a641565c44933f7882b033d339af0cb61e1d5fa6c9121d9404fd4278ee3d51fcedbfe3eeb96cef9687a0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7038e30384425cc320318a5eaa32ef22eb2977f719a6a0a73a2303cc685a7f79","proof":"84a3a655b88a59c1d104bdc037dd311fa008fd80aefa80dc040521efda2b855eca3dc419514f8afef666120ca048e7866e33a22c78dfd5d6997a15a30a3ba45cb4e96442fe3c604530ee57b4dcecae2ce866700959510596e1f2f8cb52890653284610e6dacdd0f54f3a770405813d14ab3454969256737199e54af4a602c465e990001c1b2ec1b80acfa54576ce2a24da8d54291a6741f9f2decedbf0e3d00a8aae78608e383d55cd539219be18cdf0eca5672f5dbe8a4e89b3313033b88703e86cacab988a0bebe871ddee8e51a8a3da9c14b19eec673f7efae27202517a0874af3b1c53ebfdbad1151908def40f4ddfe1cda1cf1f725b4e7b587bb24dc00e92a35ec65b4ce451239783e67423de22f771e4051ee1f619384c56546bf1a429a2db424f127e09b133a1e02fed0e32c0b18264d7b0dbfab41a1854ef8a36142d064c07f95c845af6be0d8135ae017384f1853dc7f8bd113695956e56a8fb1a3e4cde088898eb205bd757e1760cf94efe71a210ae44febbfcbdde1cbd1a34f86cd21cc05b23092eddab26ff627f36634249166875cd37839e45b294e9d2800610763f7861945f783e64730d0fef01ee10e88d0f34492bb4e94c8eb0d84b9ca547de62d0090e872f09c2babc2fc5249f32fd695c17aeb1a299ec04bcb870813a60a4d177f1b723782ce71b51728bca3b19787db25f4930f37c209727e84fb5b367e44833bec0ab45a0685950c8ec6097feb1f300e47e792996c075ca61fdd71d21d28900d18407e5fb8abc9692416767236615de4f009c214e5ecbff2993400f4f5e6a24f88c61543b28b304b225e7db57db916b55a030b7664a4297859bcf9b2a33862b7327bc390fccf68f733dea3249a32fedad36846f9eb48af1b4ceb2db0951504a03686cde0da3ea264b17a49dc9df1e47462c4bac1dc7acba1431c56d0f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5498f3e976db241a5cda17bb31eb0e39747d372b10ab6a585844b1334fb83f24","proof":"28c6c105b0314cd4fadd35dfb36e0fd77e77b6b45d22d7d63316758024c0ba31cce6e34041d48ac07b124342c24a47f53b56e7e51695cf0c0b3b04c6f404dc25ec5ac0797642775bb5ae08575f65da71f12b19d02a3d60e0abb450accfcd1317ba25fe7223678f5e2da28155f6cbd6e029cd996493ddf5bf92a92eddd533801ac9a54e228c98291cb6522c0c46e9bea5c71b76bd6fd09f8d16ea42e1bfdae804302ea26be3cdcaafe952e4f8587c1ec9db0550ad746d763a80b8739c201f94004c6711621f7c37db32cda6ff7b44e20400aee7a79b6a6d5eb61783aa67f3c30e72c6426e8d1560b1d0110e7c2ac21f27b19d640093998087dfac5cbce54bec6e0c987fa2387a6c9ff988e4fff24e87ad174188051b6afed2eb363c9ecf8aac0c24f3941b78514f232e61ca38e199fcb53b23aaa4d9baf653a348638bca77d530a2132766c3399ebe3a2d8cc947208ce5c564a696bb3ed7ba4727420b0172de44acf579f9cc2df08ed54f56c7f0229b61dc0246c03984a5c21c6cc4f540caf620c8a0ae2e12b9448934f047bf0cc6355bb3622d749bd908475482ec094ab29674a46d5bca42df7dd7ace37dc5bbf3cdc86a55234f0c9d44bff46b1baee594730b502bde1768923a5836f7ccfbc5ea599d8d9dd918f2a05c80fde1ae8ec3caab31a29ad963e808ee53b55f02cbf18b1a317f55fe5d27eafb5c005b6e191d03bc06dcb0b86ad5c05f70a1ca390066edfb667d7c785d6f84fe1648b5eadd72719500924210c23a8c23e7a573359d6f543e71c6e38fe9db01c1fe42974429c0d96060eaecbf9713ce079409c61077790dc299e30b0fc1889e9dd0547b2bce93b1a638be75cf1b254768b15ca678be81b48b7af22a5c9eaa7732b0ce8f2d6d2177340d91a6323afac4b0c7d0605a447cc40f33f28ee71ada32236603bc0eb0611a270f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2e35938775a545408d900e50009397461c91b05d29eff917e1d5b7c1256efe3b","proof":"b2204d4ca866bac2b31b103334e5e28bfdcb19f13fe063bf2bd75d393d999c49f6e5fa4700fbb876b646a8d5777f34701fa152c58ea6af7c99d5ce4074e8a02ef464c54b872a097613a9dd8679702a61638390b0aef2e2bac3f6aa7ce28dae00f0a4f78dd6f5c9f7abaee9ad267600c19e593235ef9bc5d29ab8e95713dd0f19f9fc6252f301a42389e786cbc0d974ec25d62db86ed829e0512a8a12b0fa8d01826af054e291a82d5cb58e88d7a13abbf3c794673e23afbc2740d7ce892d9601df3a421d02e661536436c505251d5f407e6c903cbaffd57474edcd133db825047ca5fe8a5e6022607158703735d29d1fd6742da1739a66a854919628c1d87572aa700baf576208bdeb362ac9cca924b1eca8edf115367dc60463dbdbed11312110ba7c8e5430d2557e606f6f7858521a3315454090cc5a21541ff1ac6a586e5eb0be3758bd0863a27301d390e35d1e13145b4369895649d45f583407f477d107128781ebb750713bb257d279574a5d598e0d00e1301fe374c8b9bb19f5c8a413fc849b80eb3e0c8ad1a8d2dad52dc0a9645f0101ea739afb94e5563f71a394363a606087f65b4f9c4643f574ce6032b7d158651430a9dc5dc174858ecac5661fac0f3e5ee676b07664dfe51dde76eb79e8b07fd043459e364ae2f181d1aab435ea64e4c278e1dca2735d6c2418b648e4b64838fd458ac065ba5579044ea7a664aee3498658f631893ee6153b5f8b5bfdf3ec813b78a207637ffde1f723b7b76206256c7038bbc3db5297df360c5571277350434dd4ea6cf6e757fd525f32463c0013bb972b68cf77a66097c250ba8e3adade9339b3501db0370ff67e1d1ed03d14bb5b0dc79157e34d187d968470a93acaf1f3445ea5577b3cdf79b56b7bdb0c49e4f87aa8a2c988b632cb8e16b8b39e2b65c53f25d86efe9d8a8ff2ac464401"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"48209dc74ed9a1ee4a911d8c196da41770c4ffd0a3493185b18e298b76073551","proof":"9ecea1953f30521fed73a888a866b48101e1891fe80232dfac5875f08f453e0c2640a6449e9a3f3071d49f47dac4d68c84bc3ae9643f82ad6eadd501a9b5e1614013c4ee72c6daf5df2980ddd876495927e47330f1282b3d7cd1f0ba7a53f8055edd6f4f38a6931716d125a6bca536fc9b97c1bbb949459f9cf3d31ce1c04079ec34394b17ff56d9e0eecc3b147c2cd2376a7f8d8ebc176a1a873ad75b475e0731f2a911f4715fd7bc6b1e86093a8c58fc943559800afeca2233d227ee0188071d649ba82fd2439c7fab072e2898425b3897810284fac989bc247d1fd009c506740cf1f7551c538bd2d1b5661fdcbb4f2b1022d958ff9561ccee202366c8535ede833c25c07835b09492d0de7eed4a52d07676f3f066659a7666a8484dedd952b60f8ed8d03538d6f9b35edf6c94ea269065953b1be8d9de013fa179c280f04ec44cbc0a51107b0624483cc426f7541d1355dfa3a855f98fb7ab5b2e5982230cc2ef7e5389603345f61550b7b8bd45c9b4e198d19ca77d47d423e8182d36ff7bd8f9030adcd1820809a24ae24463ca721b3a59634f3515f6b79b4ea65df072301040ee89955c0085cc1c5cee814f01facca9df30b53f72963753f5f6a7eb2a2646b9d8d6a0920289c1e613986c5113007d2245a9fa8c7abd7037a6da7850d202b6a82e5165b3c0030169bf77fb2e8e43dd95ad8dd0b19bdd390adf7addb7d32c18f72d300c057dae232ef46a3776d36045a9698aeca2bae5db4a27c4e64c0213e4c6459ac6741b777ad2536c5e96e5e04c0ff6ed91ccfc674e469a866463165c46d4d5a936718f1b25946b7e6663f52daf9fb6f09e6b95d2be6e1a6a75694e1a096e3e49ee511dc189c11eb9a16b60b2db00d9b2ffbe5950c053b0617c89fd0033b70ded1253f78235fdc57db9298d622278af9c031abdbb194e4f54cd3b6b07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"923a6c956566f83406fd618c0c26538128708887dd17a02670cd76f5bddd3870","proof":"4ee939884c3041e171bbafaae30e009cb31957aae8ce915232971bd7c590fb785eb2da6ed2be669a7e538170402d319d94ed87294fcaaaff62ac2cb80ff6225be449efeb229f884a8403141796baf0b8030519e7af5e30e663aedd7dc5dfd824a4825a16da27c5cbee32dec682a6d2e8b0508e8f85b64e4f44d2259582cebf68c149015f3f20c13f0ec14ead3e2a2e79145c23f45d1e21018e6db881160a5405a8b86ff0f7a3434f334a43d4372373df856a692cb923327be13da7e2ee81670c61491738349be4c4df40911c481f5127c7b63e2c5c9c32f28d3102a2b80d6807181219cd71a8ca6eafd9f608f692f9e37c2ac3ad7f3bf374e64f54a358f1d20624316c98d3aedd0b1c40542214bfd5a98cc836846343f4267f5c578a742be021b0ecb74a7d454860c41374826519edc03cbed3e7d024ef739b4555d54527b85c78e45ea84416908e8968a4b61d8ff398f0318f8dd3c040efd5acfb41388b5749269660ff1086ca762fb935dd4f4ab26ddaf003ecc817477aa369400f7f335055329bea77dba9cd3e07209e78ceebb8c5607b8233538862cb41a9aef942433d5ef0b396093da5d4ac913ab2df52064ca3647673564363d62ee1430755a797a91902285c3feecbcdd2f4b8da7a3c9769ab41ea782db6158394d151a25cd802c2582ae721e3b5f8d19332aba858e4412916ab98954490094650df5e1fce87f5bd596475b78035fe0fcd8694add0ed4ea82632b69a68bada6df453c711f6dc2625794c6fabf056ea9b753d56890ee55efd69d77df6273dc6541a20d3111ceb12020ae80f747ac39dcf55975d8adf266d58f83ec1794333059bd9ce11621534639230a66a2a908254c02b92282e5b6a1e5a014cb5d2a62c6229f200c0e3d419ed9f058611fdf47c23afaa74e8d3d1ce7a0135fe7852b29b0f768ae4f7a3237c422508"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f86538aef7b59b81fcb308df7a83b8d186dc061487acbe73607e4459b6083f38","proof":"ac9ff4871d1c0719e69d046f20a5abfd7043bf06c74f5b72c8af6b16a5c509302c71c7d244c662ef4764919bb7674529892a7742076954ae3da372727cbc9a3deed29addc61fcb9d7ceafd359c018c7a95b283e8656ae464726408d5f118c25054036c2d07d9872af06e7a50f909446063a82deeb47bfaf2e797195b2e54ba7250e8c8e7bdc2541109afe0f550b17d57613adadaaef90fcae857101db1363108956bfeefc7ed25d4bf86bda09f294596d8299d3a5ed6f0cff7c1ef93fbb7890680333f0a21332d17facfdec489793283242d0d83cf69fbe01b19f97db7f06e03c41d884f69be28e3925f33caf126e31cc116b493262bd790fca01617425b2b7730b36723a379319343a89b7deef69b87c21a372619648a4b90b658af9dbe446b1e5669fd14230e0e601b164928d82cb52930ab4a975ac7fac85d840e8ce293532c6cda08f58f355749a4e8644c331bcdcea6657133c8318108fea29ea94b8e470c829299f3fda304306817429a9d2f0f18be92d5a4dcf96aad5243d61fbdc27d487d4c2b9ba03c7c064273c223cdffce41008d74b1b0a8f56ff97c976564d1422a202f7cb36863365da21a7ccd3e594043d7e26fbf8edb8f372f02c17d20e848fe360373dc3c5bc32ebacef6c5f29f21c0755225e102a5d5805b0656286acf2250ff8e013ecb2861d1e49da780a0fe1f2c6b78a7267fc677d4ed1d942f6a387cfcba0e8ee9757d6f5a8ddfcf70dd6bafdebb6d8dd16444a13d21bb939fcfe20f248085f5c7a9de42bbc4d656841015923eaa7b2683ed27c1fdd69d04e868ba549c1ea47d53155d096f101d192f364beb2967ea47d3070335c8d1b8d2aefe552c04568580ac33b6447273fc83cdaec99997612761ef7c1950fd8ab5dd86cfaf04310e7ddf947e01c149fd5b05fbc1b7ac3e9410a8f05efc02b412e789020b3c08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"144b4f1ac75acdc6c346e09e9cad5371eccc8e1c8d42af32cc34bb6409bb214e","proof":"0af1a0ccf3f0d29b5d6934156a015c900758c7f5f6f2bff540b0365aa453854b001863b5f732a382117db51c1faca7e0ea0f1025fc4d949909c023f9df2e6c12386f588d53dab65490eabade1e8bca1bca22cfb5a583b30a2b340edb16df046714aa4e4bdaf64f936fde22b1e12ce2c168af1aeba19f200e40296a4730f06d18409fd3bd3f1e4d324c5b647239d536d6ff1674545e85b1db2d3b47d33b132800897232abd9ac67373ed8af6afee124dcf09ca33d679a2829a2fc6a6feae4b60fb34b694981a3b0a64ca838a782865554cd375be846d5fc1e4d232915c5d5040700d9c48129c6391afd3039437e4778725834235653c8705f0fda05ae892f2333580aefcccb2094e37bf1a6684d4377ed278386ab0d287341622098ae3ca4914e80ce806df2b2fa1ba467ba3342ffbbf9cb1b2dc436773e81bdcf5b277236a04378351ea2c474de33e141c8f8c1dfa28dc7a44f200a380c8d2973657778fdd54a3e6f16e08b3c784efcbe5d4d86ea88a42618c01846b0a0217fdcb3450c18664352093616a69e210958846d809c5d70dd43da2e5ac0e0f16c2b31e87377a43e681271a5d2165593716dff638004d869f87b6940d340c307a8a7653611336795721aa0e37a54d36fc0a53bb2ad7a5a215483c3f19f03725b07d71bc9fd377cb91e3639c1ab58d9786f5aa80b588c474ae0e856676fbe8f0acbf2ce5339bc089e1aec57489d755e570af937b6f48fc6b57136e3812f3ec1658224caa425ba4bb64cf0519daf97bb2f40a3b861810b17a229792bf107559dbecf8a077fb3f7a7315c9cbb5a7b208334946444bc41d693e21c7404c0a496d4bbedf23b708b4b5de927aa26d3c52fcf6624c943ead4e8054c01e68d829e5cb6181a790a518734745205bcf361d27f36d2c43e58d7aa5f48478ec51f763932bbcc4a8087945ec0f05d01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"10ced3c2df6006463fca023dbadbbf52c80408dea3cb379da85a53874fa8e03b","proof":"2236fa9133aa39fda63b5377cceef47974d22e1bef8a6bfdf0adc11278b246050c4bef7f0769456c3c17f10c75aed11469d86b7db798da56c770a9e338893b202627cb4fc11b7e52c79114bb2f10d982b9f555cea97f0cec49c6e5befc70d15f362c9a7d682175b68790c3eba8c8c1b28826fd8743d5198ac0445c168a928d176cba13cd4c7d91d17b2077acdd3a479b8b636e18693dd4308cf311192fbefb0819df5397d752ff4bf31649225c115801dbaf350efc2a1b87509825ec1cc6bb00ccbc197f8526e9330d7e3567c6fed5199a64d17a03acb3685bc0f5e282e41d03e81eadc8cf0385f60cb40e20509e284c806bb20962849256f91a92bc1e8b9627449403488368c6f28303cbabac5693b04cd087cb44bd07e8583efc7c2d831857480ea39f2e528f4a4738473d8a5c3e42ee7953be3552748601a5f99b0b694a4864154bc83da1a57d025bfc5e7e4f154168837bda4d575b99b7863db05c666921e82f27e5070f1ed988cd1a408765f04dc992bbb04ac7fe42506735b84500577630176925f1520ab51374fab08b3a17af2da602568cbb11eca0d833839323c33a5c38be8202db7e8641769074aa6d4c756b16c8341c8c512bc94ec48ddc7cef7ff46c7100bb2c1188fb8c068dd2d8f7518bccf9a4b223f5dadb310b8512902113aa303ed76d40faf9508ef1be58e18371b5f1ff9867885e91c3307f4c1d53db340818d7f709438e11e58644450301ae3e9c215c601d99df611e1c75664752336018d2b46d78ceefee4bf5fa68862457ec4715dbfc9bd6d28100aee5f233cd6e05ecf0aaad57cbda00a4f65133fc8f01d671b5542e6d59812b01e4224a81f75f75d525e53d261e2e0853b7a83ea42f0c049d0119aa66183ff1758905be0a462b0034c8ea0595ada3753615296a4652bae46b12fc80e3e324fd70c283c1b54eb70a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"eedc0c1272a3e0db2aecc9630143b35202acf2ae547aa02f93b3df7d17b9d104","proof":"8ee0a05823ae7a90c84d345d01f5ed85a8de1d5aa825dfb890037bfa937cca42884497dbd594a5f36ee7df78c6aea76d2af6aa7009115d3f572febf60cb7991e6e29a37b79d159533d4d19ada4a97a773c316b4a2cab4f4baa7e50445eb3b6282cd6392adfabd02a5597139eaca1ccc21acd60156cc15c3b17581f3ba11a4e3c614c09aa085a02a91c6d13ab3b5ede02c94156294aca350024b304d2f9774d0dc979534b521c6bd29940b8c6d1b1bb8e79f27e739d30fdd4332ea7c3fd80f80aa8d1a668c800be577a620eafb2202418415724a25cabc7565804cd26bbfa8e025a47ed2784120e9be0dd1273b46e5a7a5effa7d36028da02f30df51800f65e50aeeefe9fcc56341f9fbf16898e345fc5a93174403685c5382f8a5df620b070103aecd0ded840122ac2c6b56732ec1fdf0961a3e8079ddfbec9eaa26de5912e2672bca105993688d88442363f4c06f53c6596e996b4cb6eed2577df376609c15ed2e4259b3d640b2f847259c98f30ebb4bf2c08fefb2b1d89c87dfeaddcc96952180064b28b4cf42b4655e259181df19f60bd97f9b2c7e2a25543aa224e0ad074b4b810f108e8d1f09146dc284b86fd8fc1d1671205165b7f0a56dfd0b71fa000f41026858e89a115fffc34d68c0bf259ff68fd71d44c8fa5040686724cf01436ec8fed3f68ae38be8042e78655e3422dbf45f40410a5da21417273bd43e72274b09d37df875dae83ce0e54cff26b54634bdd0191abe659e9a3181ca30f8e781326943b3dd6f5eed45d435ed510378e505848a7cf8f6e19134df6e834f2093542f0964a68cedb44e459bd9f7296384270e5fad3f0375b80b83894dd21b6cccf4d06d926bf945a0f298b4d58559927f87ebf9c3f3b2ae0ca2b3e002f239ab02e0203b172d59b64b8b6bd5466f22de339f1d7ff61d1a302f9bad9ae8404428eb70d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"eaa88ecfd4243d83198818a3e7c8483b8dae40d3bf26c8e537004d94a5a4756d","proof":"b0db34c31e6897ec93c6e7cb594a8f0fd4f4b5204aced26073bbaa3470c5095736a0b619511685146b21badc76112c4e5f2a02c2bf9a3b995841f30e5087741ff261569b2c389404533f46999b46074716a668b11ede87b1a8c8746531a39440ba987fa1344e2cfcb3f4bd39be93cc6f522804be937d06454c1acc9f39b4821c48c1fad4f1dd7c4b4f2c4ee218cf30b4efb88a85b7fe28a0574f8182cfb1560eef53c7495e0a0429e00a12f3961e99baaa8ac842d156780f1bf768f2909e720e7b3c80cd8292ae74a9e7e8447574afd33fb7acc8edfc41d6e23c357b871d92089463eaccb614084dae539ff01e93ce531312774cf3f23d830b1c5c1c897e1b76dc12957bbcb6132df71d0418777ac76fcb80af87a045b73468fbaf0eeb693869bec41b2389d981457c4e18fe1d1408f38ddb40808b372d5bb894039dbbb86906b60e87e88b8fbcc726fd009ccd1b11064ef83c9efafb8278eee95d47d8a16a6446336034c5fc6eb7251fb5c61c74b3c9d072ac0aba1f7689a2c5ba2f186e4d5ba6687b09627185fd8f0e8987fae275d9be207a92f920e195a0a9fa8bb5c589268e8658bb434428520c65b80a1b4a5d36b860ec130141a70bd8c9339f0bdcb04ba2af5a9352b955828629dfefcf28e1cff89117ad240e53880cce09030c75126346673ac3c8b9ff705668ed5aad7584d3dc947e918ccbbb0547aa04787a055d515c4d1b3fda8150db2ae2df8090ff837c871b722b2a40b41ac8ba6eedf856a33df27349c0fa27f69ff6ad416f9ad1315a7e52fccbfbddca95fd044562f972e25bee26b824173688916e862df27143df1ddbaa19ac738337f936c7bb9aa61bfa75a9d4bee3e64f4620898c71fd86492ddc722d543b8304731a0e610cf0716a4f0c4df2078b9e39aad6ca6e5cfef2d9f96559a6c34a19eb602571e9d419b2031501"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fe9ce43addf910c7903c346a9482760b45da3c919aa6d0d4d4179e6e9f1ee03e","proof":"a8fac4eb9f114277431a38221bd6f5ce443bc6962ecb645646b8df253829aa02ba0b8586afce35d966b21e9ad3e70872c0e50681ad18b6fca04ba910a8b27d1e9214e3a30950f62ee4ce57af1d9374e3c24cac09160c66267d3f520096e6361c9a8737cabb90f477529868f2e0f85710dc20c1895f909fe3d04b577a9b5cc46a5b15ea3b63cc1608070df4887ecff18b67366e2fe6a43339111bdad8b1cfe50e5341b79ad04b801559f60c452c2283762601db281cd02697ea6df1cb2ab2ee0d355dd8221a5f3a5417ce5ba3d961920ac3ad505d6d2f0c9201458c869f843804162278d851a2675e29835b468eac499b0c62fc9a018266c80ea0584d61a8af27bc4ce5657162a2f5390d2634822e4ab954dc605c25158c5de8e403baf91751379a38dde3ab97656277772052b0c2d53c6c49ff5e9b934448b429890883e46f609e9d2de55906487f87545478be8a25b3734ecf0907301ec6780a27db1a17e43ac4627a216aa0ac4b4e15ce16526e3dca7c88ecae6187b0fa691278d86c88b00e24a41671c6292e3dc21f37ef32cec743dd4ecc775280a6f7f54f6225cc338100b895242aa4f04b85bba9da07b7e490c4b432205923d667c347a5a9e2078093374ac93ce3d6b90d0ac8d3d8ec517b3307715478de5b65da853d23da89b4b2b37a98b2849c84923b45cf1e134ef109353c7d6be91693bd2b39acdc3b99d426f8214c9715672c67d82e0e84fb3e5a44f967b361ad96c12467dd08dd11132925134fdcaad53244ab789085d316e4b955558ac437969e5fd4562ecfae2d1fb68ff166ae6c26cfaf0a49af8a8f3fd700c9cf6c9a1c0c7c03a42f2a720e964594b2b616d1f43d95c924950396af22263e170bd772097024de5201c059223b0093ce900240351b8103795b24b0ea446eee7836da9a5383338cb2491d53069a085f76850d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4e5bf84eb6231c184158843f51e6b39771d0111fb8ff758c1d937bbf0f304524","proof":"186cf65ff429a9186f6081ac49bd56b2f361b64fe9db1aeb8eac488a6eb1331338c214356283d80a682d2a15e3eaa1b19fb5518f2c9f1f9257b2a732799d8850663a45784e7cc1fbead3dbe80f284dff516e9fe79d3240f43e0f801d0a165a4048fe5a37603ad48ee6e3bba547059522d212dacaf33664f914d0acd2dbbc93005fb7547a52d2bb73ff92df76d0391e9a846ba5e67e80f3a4efa6353de99b2f096951254bed38f0f0bc28d1b9940040febf5de3776fd54ae7dd99e2240ebb1505d170125a935f597660fef83be9b50d2bda5e61f6872b21460666b142c16a410f34e07442949df8e9088419f5665f41dc1bfa86d6d65b983211e869b9b1b72d4620abd8aa8d520fe303ebbe173a317f8217d62dd292e8f60e15c0e69d63d3c83312f24a8be122284e26944f40709a157f12ff1f8ce0953ad26cac558d0fc3272eacada1d64a1371197a3c19da58a6e6b5200da7eca5a0a447d3e1e06b053b380ece06f170f1dc47c5055f3b692b156510acaea4a4ef4a7fe1ecdc563c76e888528016c83e835d220cefef15486a331533b0d186f29f99d820cc1e949dda173e289447326b010e0bdf25a2682cb59c1c66ce2703c694ae1f3d66e0386778cc3c28a618441ce0a31d1f423f7982f954ac7030f15361fb32a59594b2c2e9eabc7853bad1b000e691f4c2fa79a70bce6fc4707b1bd1211d2648829f4eb8ae3c377a2d40b2f656dba739a0b446e17173e8f4ac7623b981d804e6f24f5e6249aa283b4342e402756422ee574042db599862202be3ff0b6e82f2df241b92749a845a63751c8e8d02556424977231f80395ea3858512e89c07195012978316825aa2cbc7d2afe7ef218b34fb77875a2619f3bea0d579427fa0d40b82b6c26ce858d0e30048243cf15256b6e4d36cee0e7a25d165db6ed1f60720fa94dd1319c17bf6b8c09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9c50974e10fc66e672be0465123d44582c1023543fe35efa40649a45afdb1623","proof":"54118631943b52b1628fd057dcda8a447d57d622e2ea13ce5f2a0749a4ec2a1d88686ca852202895d2822a830020813cfa664a90aed94fab997982285bdff901c4b4618773c60ca723c7a80b64170cb43fa745037f6becb7c0a334174009cb41a206a388400d73727bfe456bd309352180a9dd0a1fdb01af22ee1e10e153a1669679e67abcd67dd8aa7c0b743f91c9f185a2b444f1dcc91405dc5e1626b5b509fc0e2acc36175ba6de9b9e574ed6a61c1e9d23b42c37edfe3c5ec2d9d788480c462c1ecf194f7489f4839a0d574333aaa7f194b41e68fc5e5b37de0e0e21cf0f1a83049ca695206c874be7e890b885640e9c118b2b716266f74aa92267117e07d47bf864ff371fc803b4afed0da752f46ff0072e611714e30a7d88dd80a7ad60826d6ae224bcf6bc09f20a11fdaccdba212cff1aba039e376e7dae0514670a48d04652db5a7c84f009120490aa414bc1f9663551986ab923a71a20806377b63552979702082765de1cb6fe0c9f9f938beced2b964d9043fde7bc85c9176d272cccb6b9ce063d50617687363e2a7711e1effdae6cdac8515250bab68919ed9e521689a02efa0b3b227b2f63e9e39f78eb67263166dd7a85788c9e423c3686f92738a13ccd3b3be7d1d604c18ce02ec76b1e64c3e91293df1ef79faa19c5e84c525446bfb6485dc05c67fca778421bb310364f77b0db2b150651924af3aa4c2d31fa50818f1094f51ad24a25ee99ba86384f5e74b3c9d279ed2eedb85627a69d5160a7be1c0b5cde80c3b162c27e96655d5ec95da22a9e1d8d5aa0e5fcc7294e037639a40f3d0c962680eceb0820b2ebb25add3105fc017931cf12fa8e213a2c0d13ef11044766fddd0c5066e806889f877f177d90d63396468fb1672abd884407c1bc07dc0813b103e0a1209c024a4e8d3e5760e30f91230c74e7e9d8ecb87702"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"38beecb0e681b07a9da01a7bc0b0c2b004be52d07802b2bc1c2b1ce101fc885d","proof":"803761e2deca6ca3ce9ebc9d4d6729986aa0ea50fce67756b8bbcb531d7712663a392577ba984463b214fee4fe96a49dc415d4d96210507fb0d86e91f135094c7a96d2633201b0f79e922e25bbb732bc517930ef5f6c09b26c662ede3124383e4050a861711a6423c9a256bca49c83793d89ae43c7b592c11b40cf77ff162c69d3daa23bb5f26b5c8231ea961bd5b34cd9840ac26028e935f95082ad91406203a8adc0e4809e8e6fece770d0b70a71bf8cb4a15efeff8b1785bf042d1e200905eb097576e19f96a93c7ac18e0af457c54eb1a7afe1e50d1b5e7720088f387b078c530a4cfebaa324c0b32f6cef23ce50758b6e45348853c848ff8d4c2c8df153d853b047f7196e1cdf4933d60eeaa24e0fdd6723308310f0056d428902937a7fb2f151feb2131f4b612b78e0e3d343c2c5b0f9978ac5d7c2239d91bbf1ddd73a103d40c838b2fc9897573c3a3914e7e99dc5755e5a46fe96fdd6437cc2819b27e2a3e95aeea5e4a045f4bbc5794d57be35eb1a71eb65544faae96f9f989e521994f8580bb2949165d536b995bda94aa818f77d538063d3fb9c0abc85f920185bf0eb487078fcd87a91a2a8c022a50fc7910c64ee8bc29f98e8ac262ecca2cb323e12ea0724eaa5fc3bf87140d38ff8adb9bcba202c6ce895efe844fc45399444be8ec4970fe25ae52ac4cd4368e010deb49aa86a9142da9da9c8e03aed4a9806286d25ab16ac3a68c3bac498f54d5d06a96d3acb43f2a2e973b0802d84400e13a829eeb027c0367729c4a3b3e6c704134b8d366fbd0d851561c36075be442638ceebbb0dc9de568684123503a8962073d81fe937113159ab62b1096f5b0bba18668b7cfb8667c73febc493f627e945819c9cb7faf2804df82b0ebd3d9c0a740cb33c7792078833937b273eb8b4876ffe3e85764144045ca473ee6cb6624e010a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"64066ba7395e8b8c9c9a758b9f5f8dfc3a238c5dbc0da0afd1c64b296db0d523","proof":"c04a36a18c4382c0e28f53ab2c41cf6ed00268df1bcd7110b10712e6e3f92e11f2ee6cde75677c4d896e0c8c73b6e86b9f7e1593bf6f131a561f80805cbf3c41c0506dbd680af629c3c55c8511a8c6ead4980bb4eae1a676280ce9cc749ae74fca537fa81f9661b9becc082410dd8dfdc34d9c0cfd6621b41573af4533dcb4516bfa040cd952bd6a31bb1ef3458830e5b91df9b24eeb397d5f122d495d4fb501cfb3cd3bb851e81aff00c675e6dfcd1baebdad1e4d697dc045966503f72abc0784cc708a5e006aae65ee5cde90da0ff8907904301f51c6ded2c51b8109652504b4c92467464e89b8c2f17b5c5674e2f9eb23d5ecdfeb7a20d7175a1accc9c6080252787a639e20262423bc4532653ee8a0618282e00b4e43b79a335cee84aa073c52e4514f99bcb6e5f388d8b4e2bed46c72607fe79ee68578fda1eb11358d64488d36d7cca5b4e7ef8267246b491ab8738a904479558c02c6661d6948e4291482991201f34a553a329da3b79773c4af9cce1a87624015f52d09304d759fcd7ed8ee2cd08a998d1613f4cb24f39f3d9ec81873eaf4f4d355c8d6a426cd1a771fbe0e1cbbf7e2ed538c52079cd95b49aaac28128004a8b7b7c5a7fa1536b732136eda0841ddae5b11afce04ed6e94cc5ffb415b1e1f268e0728ec1eb740d03c0f743e6de2e20e9c523dd1f246667899b19381df8b21cadce18b6209a9ca34bc62d64fcdf3aed4ec03b3565352a7ff1c09df593a5410a20a0ce0410f1de574a86f420ea3d01c467ca11ec819c130ed19a16599b711a446c4bae5596ce218eae013a2162c1ff9e56df783b00c9a1ebb29197751607cacb9132b2cbb5b3ff02d4e47aefae80105ac36324d50131d31d7c09cbb95cb7d20a8a48af77227f521e4800fa710b73e1fb0a77ecd1e7bccc0fbd3e0619ba940833a0e26fb36b46fa75bce09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f40f9b9ad1adf373ec9386ae4a458bf995fc44f724eaea4bc294175748ac7755","proof":"b2bfd89636756d6f331d06f1f23a95f5c7f42fb187a26f9e92fb4474915ff8334a66e2bf560039792b7b7642cf193a1b2fb373b8cfd368059628c8bd883b0463a4a5bd3feda297be31fecaff8a840e44de7215c402479b1a5c07d340a2bea607eeae1248b1e512d42d3d1627c73974502375ccac015fb4a3fbf45eeb5de5c8201b194a13725a8260d6235ff84df8c11974b0bb3a10408fbc163e4651621ede01a3f4280484867b18d135614ac9e9987b6fee6e197fe717e78f73dd3984638c0e873a6ea16f3854d49021660f9f4b29c443f40cfce0c08b04ebf01bf5e2fa2e0ef2777c69a05f5f980495788aa0f4fb858e7cf0c3c12a6ddcab4ea44a6d4d7401f03281191a8ea9aad906573152bb20658287bd0641d87079e878e12a2b0aa028e87bad9ae9ad7d5518d988690f26086446d5c3c677b545f572a28b65a2ba1c038a47cd8f76b9fa649fcd49ab71d05643a6d1833e1f4facc41a20885d2c6b7b00fe5c9a3ec257b06d10c85a3517bc6fca33ade194658343dbed50899470c1c974eedc36656c71d6488e92d0bd01883abb051ff0de5e6089d0a9ad728d01e30e256abb2febfdf182b8e5be10fbc926ec82e653cbc6df5c78f6ceefffad50e11a33e4158d5e92b97810157afc4a978b1a1f2488557d41cd59478c6d1e2c95e84549fcbc4c2740ac66b3b06b6886896cd2bff1c5a018b9f84c690f78b03cc2ed8457fc1bfabb0c9ec2d3fdf27f9278591758a8a813dc222db7b1d08ce0c5f452223dbc5509c8cbbe0bba76f34970d8eb512d786274c8b69e445f711ffa61a1583f3db2dd2152dee1e2a5615dcd576d762b107a67e90e53f786a2be72997a71c7623ed245959d02ebed41551622717db1bacc58c521244571e84a77645acf2c8354010bb4f1d2e08d31bedbb99e58bd3b5a0cb937bd34bf74b122189b6e5ee320d30e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"509e7837fc9af96b3fdaa4360ba56bcd65c7788ef1dc1aabcc749b1fb5c20f35","proof":"6e37e64f79b41b43bddce446816090feec45349562520a0572435c4f67cbde0f4e1a28000d79696eb7330017a95fb1d7094f968a3bec4b1cc092fa2dad314f525a7f3b19d391285297d1c3dc6c6bb8f63dd9eae44878a0cb27757f303c23eb27ee42962485c8a96c60152f11a7c76c1632f2b520a95a6bd67483d59cbd47c82dbf84220708b9e9347529a10acca9f2625eb1787d4c752ae7707db41ceb6c0401aa58bd86a0cb4e44fcd897201f1d374cb08ffc4dfa2be3a37f5e1327a706d2006290ebab514f7dc749ac51869e74b47771e52bb243ccff786a13b686a3aa290706510f9d98ba9cbc33c36862180aea4e5ddc8a5872c9192be2be66a49e5a82679e9012e561e42ff46a04229654644c1fc2246ace3a797c4e5ba7a07120dbf84a28255571315463af6df853a43db7c724132c63b090e43eee118905df016d7d5e74f9a0ca47c7214a1d7a9e8ccccccc0c604e878314b8580eb357df43708a3833d8f3201972cce711b08456dc8e18348a7c267e333efe0b3ac5b7d8ab937519672247eacba144388df9b81ad91b7826d6ee8b38ac66f71e42e20446672de31f3246eda0eb887693ac490049754e96c94f3904a81bb47369f19241b6e3f5f6b02c426d431a2a600448f83db665c4c7ea44c2505594b7e184fe329b6ca85610594fd8041c9e665de47902bc9bfaaa733ca183b92403affd24c511a6f3701d4b115e7cab56e5c4b3821a94dd133da871a559e7640ba372b58c7b095e5cf74682cb5882194f3fb1424eb3f6df4fc18ff083655cf881cc38b1e9b8ae7d0dc49ea5e4725a0718441aee0bc20b7f4454c349bb32d931ed91dcb8c0cf5b26a539310d563bd5c854358a0af3323145cd45b08475efaf2b32c27e41e1149512e94d21ab6e01a92eb2824f0b28305fcdb68cd2a6ca3db6a84445499b1b149a5f99be452bd708"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a275ba0dd53c88ecb604a9a070225cd695d1dad9e032f7434c45df6e1422b215","proof":"ca7aaa1107128555dca67d4799775172e6fdd019fe81e96313536c6e4626d52f5e81b5cde79359b7ca443bf226fd666d242bcc96b33f16e0e6f7ede2b1b1e56f5052061ef9888a1e959ebabd8a7df01f988590d74aaf9c5e80b5d84aed7f011bd24a411522f1938a9bcbc9ee6a03aec87ee58853671c8d2270608e542e324c50720a32d59658e11dce54bcf7639bc0ea12c1911f67f2ef3a848ba62489510001098d104f0a6d4ceb101cad536f13b58c370e4e06398f84a9ac6ff0c9aaad7b028b7b55a352b8842b38526493cc2ac6ffb6e353428fdf44b2b72f9298cbcdc70100ce6a0c6a6ec161393bee5ba9ee61dd99a2fcaa1b867db2ce6836b2e2b36e5c9c1e7708ce91be86147d0b562ed5faab352c05a8a140ceac4c3f75c4b82dd974ecb9873e003a8476afd1c932adb2ff253f282db276af60a510ebc28c5f9e7077368a81c97803ae9f9aa5ac9cd5aa8ab810daff426139592b482b4be67aaa021088be54e30bac4752798064a5a933b8fdbc51044f98ba5477496acef85385407f3ed450678f1954a3e83ca7c1f3c9d6f7c14647c5cb793339cf4ca421f03b1154400bfc997b32553694865cd8b592be6a3ffc221cd8aa630d0e3695b633b7f521dcedd5978ccf9c10a5a99c74fd116014539150338d2bf4d9b730943ba6fd4d749e1abfa48cda1f6db54385cb5c1ce94ab292bcc512536e5bcf4a26aa0dfa0546308ee70afec5fc7a95cf7163027cad197f01ae73fd882f59c70ac1e2ebfa471aea11780d3d0bd3ee5b78cfb90160ce2485f720fcfec4c3d21e08cc83267a2b1116a29dc1e08bb8fa882628b4652487aad923ee842e1b3958784ec0d79a9be32f3df09a5f25ee3a7861057babb6597e6c468d783b553bd6e74057257d4c312e07eb11979af66b76d024f979bbd1de9319cdc6695f39bdd908df4a935ff1957102"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"24197514b2af47426691efda48aa7edd098d592eb41cc4d75d06e5581ced8474","proof":"a0ba18cf8fe06d0d843f1cbfe8e531bfe2b2cd9d0d162453ceeabd1122ca520ffeb6ec4cb3c647500aa9062f1376f7fe766c1b9a1aefbb2ceb2af9689b834937004fbd5d51c4125137d1c446dc6b4c63f940926ad86225c369dff1d6d06a7d1d9298e93ce405d60a47a704be08ffbf75845713a4ef38ebada7679bcc63e38529d1616a17c86b8d804bbf7e844da42edb9ce9872164f34959ab49a97d45d17b09786c11360ce5dfa31b1839e275ef12b99e7dd81bfbd6f1473006b4fd42590902ebc125aca20ef71651fbe8a628774bfeb2f295a5cd22e7efec6a4608a2b52807265b4632008cadc47c58474c4331b2be36f835c5413fd2fe855a54fd7a1ee646ecf7cbc4aa6fd2d10e80d2151355c5bcbae713653216e46e04f2019ed65aed3a38b31076fe151fbad66b7648f9b7364d9534cbedc9dcd3117d20c7f09a9d4e2e9e68304e47f0f21a5a3695e3ec9e00b0f3297297f6955164fcd00f87cdaf5e4624b3395d03373e3c78e005e1ee1739a7c312b2484213108dd75db59f72151a0adab34935b3e4c3cab04522ebe00e91936bb4971b3b09ca3473a61256cc4e975f4c9e84b93add7a996b5f012567e7fa952b8622343ef378b13d5527e5b5be522720c30deb63506e538d958a8cb0bd7a76bfb3735b1a47f5a2b59947a9a9601869fc5469b9c5883cb1cfded9397ebfaa5af25149e72c927e467984b90c5309b73432142cc71e8b092090c558eb69b918637fe5036f5350b425d1d1e5d0e75d170796ba64bc911519d4fabbe12887cf96f67db656fe9bc18b759888efcc03a0030b46b86cc4a2ae9c42a9d7f01ce70caf53d83145e315f7a332a9cb08e398f34e3b817a896d64d5e93fd137c9b61d39ebdd8f1f6c952c6c4b1a64d20e7fe9c5dd025fa2e9b31a285bbbbbc297dafab79e97d6991c3f0c634510b63deefb2264e00a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e21e229b69b039982faa8efe2ff33455afb631816d4981a764c69f65b9a0f016","proof":"aef95a435fa393d1d003018dfe1eb4544744c40ac6ec7db15ae4f3b31ed0900e0a8209643e3b7bd8d944a4b16b06b8887ffe531ceeb012245d76db47199f4e224ae3f21746dc2ef7e037718c2f8333c88df9772c6c74d8faa88f9453066bdf7d48a989941d584112063c66840e2b5d4973e9f82b7f0d898f685de1ecef598c672afb4fa6da6fc427372e64580161ba46b450b304674c7bcdadcf6cc9af8a300578411d654cda8629230a8ebced072a53d9f49688df46b9a7f39100698138b005ef05ffd372499147d1ee2ff0ad7fb585d0aa0770d8dcf8a7ae8e93928513df0c9480cf834f11e42cd931c7d9c202a2c878cfa6ee3b1f7a704cd6c618df89ce14d0e4da711448d1a20c09b8373396d6a70e4f9c18bc0a66a474d56bfb9cfe641ef8b6eda8f537f24585059ca0d293f55b5ff4d747e9d80a3703d81eaaf5201b323a096f74f65986920ccbdb7bfa109f13bdbe98764d0564013e5e43875596987080d5148e7ab648a4a41d14e07258bd83e9d1dfce72b7550f12fb41389a88e03676897a9a93799d8c04c5cfac6578a191984d5a308117ef49821d08850a6b1a6422889f4f4be48482a1111d8abe80a7646375114ad3d194fd8763453b223a5545d8ab63de93373ad93c0c0b7c2f048c342719ff32fb6e481f5ca4f247304a8106c8787fc21bd57a229efab22e0d382007e6a985c6e7e19fa461deb794127be558fc21c44dfc87ace2c1070ced147dcf4df5682f654ab641063938887effd36434221ca9a0f3955815b02acbf1215d82d811ad161b0e67228cd639d95e849a580a5e7d336c05209ba8da1e2708a27815155cc51bd3c0eb5f7e44274de15388717c8e55d7907f300a35e6641fad4a741941fbfbbbf54eeaa3cbf87be6e5b1bfa3011245dcfda4ce7f4188e84b9871cd23af0e3aa9c6c515622260a3dba429d17607"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"40b801f62138f38ffbe431d1193ee5c6a88a51d25ec6eadb40a50cfa9476d777","proof":"32412ec35ae150f39be1c75933c6074005d59ef7e6acbb6668676a8e2928880ad40bc51df8b6be931eab9b5eaf3207ab4ed360170da0992076ef56747d3eb525c40e7495fa4669dd49e23fc7f5ebde613a1b8718cd6f0628dff63bbc59e1b70a5a5ebc53b34dacb518f2ae8a9ce2a6139d2cc4ed97fd653e9ceec28ab457b032b568fda9904bd6cb984c6bc506e4ed15d05db01045e76a83220a1273b3a58d0776b096fc6e6767c075627ec4074001132312877936592db5c19985baa4195d08b097591ffd5cca630be900afe4932f606c90d31f742e0dd991e616d90fee5e08505a9af849e76627f54ad18444d7fa0753e811bbf8fb5cb28047fa498c06242bf867bd1503a56851c6cd16125ad3b13b676f17be70afeb2eb037e33e3fc3090fa23aa2e5ba88095275974d8150af406fae6427cf006764df58d796b0f59b200ae0a97030bcc98b71b94b6ed012125627af5e1a56cfb1f53ac4a6f5e5e09b3277740d42fa4e6d227a43cb5ad4da90055acb501316e7b945953844ac6234f6a562caac58c99f67c59ed89362cfa413709f6ffa8d940d92b0089df3c4443982d779425b9df39d17ee046255f2aecd237bdb5fdd85afad65b9abd5e28fb447861f7e2608e57d6dd0365ba39041c533f14df0baa070c01a6996523f49795412ad5e1c62d7ae76b47f9ffa86a3a28070a12a87383ab0e4e227a482af63acfb91ff10183c473d106758283d4830a7a61ededc44ba8d13447542f489bf64385b0901b74afa270b1009da2980be25778ebe6d989274120fd6cb66500d8671fa49157d14574233cb9183e1a615861a82e00353b3f8e935da7d5ddbdc112370e9cfb89ac507f167af131efde9e1926ce1bcb738f3ebaef95a8c2545c16cbf1f77cd29c7f60a32d19bb6d59fd674ee0510765f31a473a93da21549f897f211675a8d412f520f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"68c4e95f7826015451320e76b13fee15adf525bee13c0c62062b82dc806aa51d","proof":"20aef1daa4eb41e6117c5372e5853c2938c21fec5ebe55a7d925b80cf0914541c8e75308619de3a0279cb2733e31871297a5851f64ca6604e44c585bde76c74a88577746e7f0c1514f664592fc9018a0211cc8a6495493e2d4f79c2cb1e2b770ccb54bdf51e42ac878f505a64c91bb291ed64877bc191cb9380d59668cef2970424f2975bb1bca0dac7071b40a1321ee1cd4b857f1588ccb66eb7b86065e41041e42e389792ced73ca85c41a927a10396f9265840f2863ca25272e61ef399401fcc7322c6118db744c2fc7064e46097f0c81f4e4c82ad74f935e375e97a7200f869070bf43eff6a715e6fceda8a3e08dbbd93d646ab6f20906b280c02572f8328ac637ac19bb5fe71a514f9ad547306ae46dc358a0d9e52319a43f47ef1d164270fd8ff4ea168a9a1337ea51675aad5d9ad2ef5d072020d9ff8cee548b02a80ed07e44ddfb496b4e0132f5eec43864e63043dc4b1e0ce3df1cea39c76966793a64ae2c48c47b5e2372da880f57fdda858c0571adbb279244d1c0acc782e06f2c6808fd78fea83df07d730d30336c9552da77c88e93f00bff55d7419308875711fc70ad6f2bc39cd9df2deb32e701e6e267785c4b1ea2c56e0ff5347798622b0b0c68af12405fcf176b05baa208066f765dcc8c42ef196e0ad0cbeb3a522f4020a03ef1b102937038f81ca21c7f6af1bfbe2a564d667f0a9f388b65957808dc0906d5bedcfb8acee21fdb065fa438b7125eb52e88328d993b0fc7fee396c6f37e5e7d01400510185b37c6b9fb6e11f50fb5687546a20c0a1ba615e22f57516a3f1295ee5a0fc93583870acf0ca6295b498af03af91677abc386e3851abfbf71030d62f4382d7cdf46e49bc8a98d625425501f1048e631dbab1fb68c8a1900e00e1f643ca29cd96f9b339160b5cfcfb39640b9004f070da568399f272d383ba40e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"feac0a3936e57f8ed12735d85f8cc45267a7287d5467ee9703e2bbd5ad751a55","proof":"4a7475e9dfc34552dd2a42c4100c6972cd838e7d44e8739c692e2533b71ef70a7ef33d96d0fd54fa327600a55880f35d0a927bd25b042e59980453761105535b9015bd3c6dda0de8c3c0b3298177947a81ed84b5e0b654db83d30376274fd85084baf1dd42ba7c1d4608242301087b3f02d6423693b41ad24bac061046f24d5d6fef35f95f85d63c35645eb05c2b6e90da0956101b7f310b26913259c02a2f0f30db290ed2461e81f534741856142ba2537249ed12e3f3cd160144ed9dd41a08085fb4f036b92d698eb21e570d393483934fe6cd21732ee860d2f703373f4b00ecb1b4081c491bcbaabd0bfdd366a94955545d82efe6acefe876243f2ca96d4a98acf6375c856befd16f0c5e0491e015d4811e935297f76239e671548be18802eebe63aeedf08b79f65b00ca261176673e86d5a42e3a2756b49a6fa60e143c5c7edfed4f0f1def18ecd18824fe1603d42061b02765e45be462754c4a8e0571648a813e68522793f4cab45cbc98dd8416b6824d5a465ca02c2ad6b514ab3af84792cf9f91fc57fbf33a8313e17deb36eac47c6483276ecbaa9baf7decdb36d3240647ec8bf120c94c8c1ced8052639263f6d47f8780febe12f019187ef67947167e3b90f1272e387fb5bba179b9ff58944193600c3526c04f5e16e996bff83f7f1ecbcee851219ef7d9070374a9afac1e055716188083fb5676e0b0722a01b72cc035a0daf6253a45212db039e5e866f255f91e73e3a284d0f6fe60af4757a758e8fccecfb623884df9b70c6bb27acfad5a23b3e086820fd7eddd8276cf01ed034a72c6838316e365ccbef18a68bcdd43b38ec5173e4f79b6704027f07cd9a95b7756c816ac82e55058b69082195bdb2236a15371f41ea92b4d9a25818bf8cf0dc7aab258467e8f96a7759e9f41d036fd8cdd663f38f538c073d059fe91f9490d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1c4c8bbe9397f1b166c44c415641700439735a39c9f25179dc41b091a5fc8a71","proof":"deff93f94db89789c16bceae570ed8f9dbc19cc51b3fc448d860a6e47ac34665ba2c5ed2e8b24b0fc30fc4befa3317ae7e9e4e7f7756b5d2c0edee8bd82d4b04b66d1de85079712a222904c9b8ec3caa8c13257cba08ca997caad835cd1d39251c4ddc5a5c7de033af1ac19ba1da8993a0084225de7cd6908c64913e2f2f2a5fdf5021575def5238842122b8bbb2249057095e99d3ad76c1780de292059a01076a3946f204aa2dcea878d427c0007d36a86cb99d41dee8f908e990a4e669320089827eac2ed540f39c429e9ee7f091fa45b0a2383e245a37deb3bdadfcdee90a56ca9bc475193442073ff18255269a2587756ea31ee02577fb6846c35528f45fc6e554d44d8dc7922c125edd0d290a3d267823f6c5d902a65f8564d8ae288c61c640f8ea12acd9d1f76de287d2e92a14d749c29b063ad122b443eb1c2b7eef1c14be714c257b4d961109d961d934c9bffc16c6f5997adf3c2654c09dedbffd2cb6158767d54426e84b64a104b687ee6e1359f999838a0e5425ee6a9bed964258640fc3f7157560184d2e07ebdc3bc6ed1527e28dfa754ea3e42359f258d8db3ff0b77942adfeb6a92738babcd276322339611df4d5b9c4a6cb5f3d179ca85107ca1baa64df06ea01fb1dc8665439604a0d676a4d03043081cad3edffc945c14090976adfa820bf182175cb47bc2227c19fdac36f5f8405d338ef755b53482b04caeb489697b8341964e669fc1a9ae7ab15433e6121f2e10e693d8b29043b3b08e2a3512a5e918c4512dc17d8210ac383246f87ae402a239ea28d9888216a4e43e8a0059331d20c96f1297dc1138b0546aed68eabdd3d435cdf8d6a23e369745535a72a2adf0f36daee1e02a1e2263a1fdaa43e658ee37cf13b99316b5ac0ab0ab90f93f04983cd62b790a875c3cc583b083211aabb005d13a784590e339c2b03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a27a8b8d577fac7ef005ad1ad4e0ea9f6aba4165c39ea766ce11f9dae33c533e","proof":"64afdf7d6eaec37335a1257443172b71ad995d606f39ad2a388a3e4dee38340356f8f5d0b4ab79a84229b772fa97e105a280a8330cc591f13843103f682ec15f4e1189e5668e8cec210811633b20039f91c7b61a9054173ac7ab02668865067fd81a95c17b8ed923f693f8d7b97920c9377b546b06006f5a14a2204265f41543cd1b00e46bec219b7385d42653612b48746393c03cb81333722b0bcfd31bcd0a1076b3458bc172129e7f708d71e68fe68b7961b2d3d3d8dfe42e042f389e440f701816ba8857ec4fd9dab546249bffafb680a687e47f0d794e43131a59233e0e884bdb0e6543e8f1ba93ad0ca5d0141a2943b03c0e892caaaa14ec92f9586b36c42d236f466c929671e6e012fedabf8d1a3ddbdacbc40b7fb856782e895483002ceae58c81cf8d62041a45d8dd49c902dd06ff8addaedaca9e65ed06d716c5188ef2d4790dc66f8e245e2f0bb67b2ab0e7cf99bfb4e755c0dde65f8a590a5f2e922afde2ffff1dc4213de99c8d4b9431cb855c7fadec91f8d53f8f49a49b5904b45a936df75367ae32480e0d2d70352d43b0dbc84b07e825f59acbb6d7ebda44fcb2982c6aaebdd4ee824ed7ece0bd9c5bb66cc1005402dcbd9b27a3b7f0743a5e065c69a3a8c8dfe419d1c26c1d3b28997c6ab6ca188cc1e88033ab8930f9174c5317cd767fd37c4991ac18626494b03cf5815c09f0b694bb980ec356d9f038c6f570171b5e54ce4d05be0955448533ebcd90b170b6ed156f88d9639134dc6684a4067ab61e2756a9fffc7c3373a6697cf5178349dc0e213ba425a32ad04a14704cefc0e086c2f0ead5be052f7eb4621e8126128d944fae8ce1f50e3a08156704e2a5a557f3624e783c8f376b967b24d94e2a415a5f65031b24dc5aa63ca3095c19d51018dd8dbb8e66f0ab5bee0a652c16361ade9df6eab3ac6122b29ab108"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"501c99a92b84aaaa4ff70d1b348b99b4707ba2e3e8450b10cf07ccf5c59fdc23","proof":"42269d60303f9025045a1e6817df1fe4507e40b24eea54299e0e9d1517c09f4870ad8bf1ad8922637a21db93645f5dc1990197a2d7552a661c239a18b1e1ab273a4163de9a801375101bd48290c99df518a8d8eac269bdb2668cb5068e395e17aafff7a95fef4463e631f8c726e7951fde1ad7afba2679fe6fab2827fc9d01330b37fd85acbcfaf1e5280d1d2b3694780510b0d15b29593f05c7aec705fa300c6b00d907d99f6882bc7d99e23f0129440163f2aaf6cb3dd668a5f8f9fd5cf60abb9ea4b22abe2cc5728a510ab97ae71229fcd079687ec78db5e530b8f5595b01ce539f318d0f578b678bbc3c00f32d1a9d4262858e4e51a6d1cd7c311bbe214f6232308baa0080869cff2c83d2166c07fd020a5d09cf6386a53973a9bee92a4e94e3da49236cc4ee6fdd9f496b2a79b00c98126ba2f3361013b52a0759b0f579568ef53b57d0336794749c14d03bf548ec05173bb7a0d92218d74aa78aed0b15ee6a28e1cdcaee227a398e19e18e048071692cc49fae69ebca108836b5527c724ec6eb5885d35897d788236a1e49b34b3fdcf645ee0b48fba3266a9a21f5547a8096f12488cae81bb03a68a7f5b511e1e64d97ce69beace7348292a09841f16f02f7ee7aed6d4f3f18f25b3f444eef095a8b825dba44b79b795b36e11ef8160b46b961af52d619f8b0b1f9cde3d4702cadead1957acd7db5cbae1bf89e04e017464ddeae076a7cf8533b600db1f336528636fdfec8192ce76889c504ff165c1072f8f9782982b73a1029c60bdfc2222565e9a3723bde05b1e9c1ee8796454a69d8f8d163ae0b7f87c398c8218371201ac3f18c4d37ed7704ca0f3bdddc7f981f2e1db15e9cce9cff1f639cb8652189c19d026936553cf57982de9f92de2eef054178b163574feba1a68ded59727165843d666500d25de3d90ba6510e35a73809"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9c6e22df2b5fb3561240b8d7be240ec0cc69e57d10386833307d3031a37a642c","proof":"7a6eb7e2a8b6c71cb108b41d1104c0e8246a4c27ba9973517fa134138589c354acab02198d0b975ff910bb3f8a7c874c0b3dc414b7551165b43be0615cf177173015b649ca2ca56fadb4090103c57baafd028d3011939e7b900fd92607085f77e271276c9f9b29f41ceb4b3aeb971e2e74747071c87e53ac009c7bda4947737d6b1cee40be2df0cc3791f80848a834d11392695268303b2dfe443aa1ce83ec0cda4de9e6423557820d3001e6697f4c4cdc71a51a9fa1fe158e2909a3e92afa07b2025a4cb57a03d340500ac1fc25b53ffcb02932b4df6cef49de2bc370bfcb096aba0e9a1fdb304abba69acfa6edff1e1de871682b390a9e36ae295a7b8c423f94377549382ffdec641d059b1fb411300a168caf74ba8fe3b962ccac9385592f603adde0892bf43a47c12489f440d03447a1a1e42a9cc4f13d165b58be094d76e4428b491f35e24df7416de91a304e6e8647ad486c64c8785dd24c268ad2ba17824b1e04c9564df05a18fdd524d4662f7815befa482d37eec7c1b890658a8421c67c8e3a627ad77e622e0b707c7ab1460a4585713a66483c1966d5c9974d564cd009cf12f8e272c2f2a42dac6a69a6f980c6c993463879c872cdd428d77aca70d622966991bf89f4ab1802c4d928a66b04165a50fe626c9bb81c9d7181189668a0b35d1e919381ecbde35e670c33f3a39302d57863cdd66c46d1c32f8aae8d05504935356c43bf899a6fe8cf31dd7d4e391f56fb3b1e683ffd192944dc93f73c5a44c26b06aaa2caaf7d61bbeef53b5f354315c31f6785c0ceccbcd7292e6c525caea3d598cfd57509062ce22355ab68e50ad905f0f0e214fe8e30596a26173564973770988f27fff756aed66161d592660341e23e0c894639745974b688ff0e492cd808996d98966add353d407e84d1f4c17ebc4873b6d0a5e8fe5a6f397406"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a450b1d1d603f74ed367889059015757cdc7956f74c34485bf89b79341877745","proof":"d2b6bb605ce00d79f95f5b3612ecb9a70de2ae4cb78bcaf6c5d25c60b8c3aa5c907d68b77cd87c1522f4c9212c8873b4c07a05b7c359a988d975ebaec6b7657c5a67296cd22a0aee8121b5951f3dd6764cd6227ffc3f96c924dca6b3684e3e6a1c454941595810c47e9c9af130569ad3150691e4487c99c44ad4e8a5a0f094441427c2c950a0efaafb083cccd2e08ea9e7d0e9ffda41e69bf069b9723e48d9001337892f9eebef64fc83ec3850dbcabffecd768bc09fda43dffc3533c066ce0cc94eb244742c6cd208bf068b363b68f751a30aaa2a182c1294a6b2a8288d330904ecab67042341e592c2690d2d8f469474a9f424b2a9ec6cdd869cdc731f7a73d49d4e7da710d1ec71c22ed6ca763ec73ee4cc59f49662d9cfe18f4e8661b61066e43457581319e4733cb36d8945a42a2c1909139643e2e2914af5f2e4f09d3cdc0b6e9ca60651f01a9c5fcdbc6bfb55590235aa0aa333088cbb95667ed1253cc2da9c7190b99c8eed5a3cf19290774c78d9740f683f2cebd6a0f88040c3716e40bbc30d1df6b3ca973048ebfd2681812fdf9ec6eac7ae25e6447407b2e7dd046a438d37a82456ee06ca2acf7299370158228b38822cab795cd5174778b01c4d3ecf3f2d6d097f9649826edfab0fc1feab46f964f41b683297e1e63e77d5572ec01efc4342201eda854614127610ee6a643f32a9d5ff54c1e31c1a6eb9af890838e5d6ac1d4d798255efb9ff85c55f4bb5947fba6f5107d26ca08931fcf35806726dcc91ed5849d199ef285abfbe1e04b5f2f8aa5fd0f8216331bab3bbf3fd43443e9ce540f07686a6c9372f90ee0fe034e27bd12774710894e605e7284a2b7d39faf5f2e48412080adf4ec952d1e5740554e91774690eae3557c0e31d792b036119ea8e9cae0fdb470151811423fbbd58a1fc26bd41fe1d5c33a5182f47270c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8ec747545b9e8853c04279987a36abc444c1273c0ac5c10bc0ab53619d263600","proof":"403bc1522ed953102b2e274a9a703325bc526c78b430b2dcf39247c481928e79ae138dc1c2f983986e8ea0025d2ab147b3b464233474e085e7ef3b1f34a31642508f3ecf461412dd84ff755dc356ae138fe1737cb65c13d4df3095134b177814ac42e030bfe03853d8ecb58bd411b5060716f852fb4cb5475ec7a9f5c8fafa4eec35af843dd9125e19380b7654d84c4a282f1648efe4eaa8802b353716bfee0bca5639e37eb1aae23eda3cf2c923402c4b8da2f0e1f0200b5ee45f1026f6150e8ba708208d136c9578bc150048854fff3a0579e8590840f18597937f9c7c8800223c3923fe1b8588571d4164a64ce17eea4df64db5f37bf0ddb42661a16b2a6deecc2d83c668baa2253ab12f768212972cba9280916f6ecb62e192d2e0261369d277fbbafae35654dba2af3f091d10f452b82899bbca77eca2469750317c0408c63e53f459b2741b983ac328968bf9f524951ce4337ef61c42cc526a622fab4a1c4841dd34520773fb72937bc935d2b5983ded84dd0a1c707d2c7a6c744e8741d8884f43ace61cc6b365e4b36e14411cd18a86b436b0355a229f593f0583261e90be1c7d34e0b56d13e86f8f24d7bfbd0f043046a9484a845b1673c0939eb17414b4ee44bd6b68fee003899af1e488739de2ffaed5d389b9d9d387f329baa924b00065b4d1a3489cd282c8962a6eb5af79af563d84d71b4505a481da1dd3e458ae82dc9c69843d28700a1e35fbcfd28dafca70402890bdec149ee754d10d9246603f30cec3867f3087e087fcd6c784853357d16cb393fbc3bff8c6f56ab3c24740932592cd8e3c8324682692d7aaf64def5d720b50ccb82b930f27503fe4855bf0c119f9b8f87f60888bfbae48748f0363280470e3aaab20415c8de120ae100a8709dd5b0db331667f274ae2b43bf04f1ac19a07dd111f931e66c59aa02e6308"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"82c37bcf92f7ab058b37ae4e33c30bedf92471d62f264478b2bf09b4f875b05a","proof":"160a8b947dbcfa4581283ff87fae5b58def3d7c448eaa8d14e729e656bdfea471058678d987b6e54c3681ebfa1e226a6e808cae009a169b4efbe0e678655c5541e9c66faa36ff922c2130d6adf79219791c62097028160d59723afe86aaca1047201a1ade9349f24ce5888085c701c7b4791a5483af157d6404550b1288a9024d3e52ca9d9603c51e5204feea9fd8631c77445057c230bfe7252ac2041add80e4ae38e5c53e87778e31cb351a91db91fd554ad284fb015b50f737085d0ff8f0204af9bf8ec1c8eff7d72ff6110a70f2b8cb118ccc0152604813efd7ed13ddb098a7811fd6c333878e8c5db181c15d4acc21d12fd5a5417b99e6473c5dc71f1501c750ad2aa8beec2f9a0883a20177dc66784104cfb069bac1641a42125906810ee7d29eb58f69f2326fcf3dc383cb4bab1c0d0dcb512c99582bef71c363c9b3cb660dffb6f6b7047ba0e3d55eec39370e2d7217e226e861b4ade81a33c8e3e2c467243ec7855c935f0863010e45ea2f16f639d472748f7237efa318073c2f5177299bcf2eae508bb09d678fb7c51a890b9594bfc5ec8fd4c1fb7e0d0c9ab4e294698e9e5f96c0bc2cb1648c4c6e1917ce414228f5e19901baaaf7cd0a1067a684e410e8b444b83d92caf36acb2f92dc58530d17c05c321ff7e234fa3bf31a96eca25c751ad1b8818fa0312186aad982a37bb20f1f1310211c3279058cc01e537c8e779c04532f403eb3d2640bb30f74cb0c557239eac9b571a7e8532175e7372504cdd68dde5133f9c63c577b191bd865b522b100fc278152c292bbf5dbd1c73384fb8cc56bcc70980ef297acbb3f617749f88e872b5711d5de762b3d033295cc165b5d8e4c7fc8ab1b70346dfc698583be4e10b1b0ea89ac22e7cefeae7c70c30702530f760441e4bb1c06d0aa925376a7da66da4c327a605389bedea91fe05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"86809faa7d0063b2895bf9f4ebf2ec6e3b9240438f084f9f217096d2e24e637c","proof":"a6c68012e3de3211d233fc4055d681c6bd2211fb70255ac385c876838ddcca1340266bb7b82f42139cf3e899dc92e94a4c811042154fb9120d5d3369ae2bbc3c4883ff03ed4fdbd0d2f04da79eb075125d9e7a58a8958ad42ac47c673399e547e4802d2dff23ff9a9fb162991b553c8caa9cdc7a50e6834f433345153252e5019f3720718d45872d9ee56bfc45a4895c23675c778ce6079290e4e1262b15c40807a19b2cc01b5c721bb640742fe87a61efdfd96f64244aef2b385f17a2ba3108570006c8b3fe2f92f2ad85014d0d65f975dc0bde0d05b8641ad21acbdbf23705c4536b38de723216252eb9f5c185e46fbe7b8f0547d21b26e27f09b45730413dc0bcbaa5a7786dc314aac0592154668191497a82cdf96200da740618f116861a682c3e450d352a470d4da62ae2141001e488b6b369b48d5d9e56cd24846aaf6da81fdb9e3b9d8de0b355ebdd5f349cc9eb66714026867b04fb6d2365f707e572bc562683441253f402136899007c050a7d019544128ba468cdc2c56130fa2f43f608be5878fb40e112f9fb9130a52612e32f4f13b7b529fe3e31ccaefc3a7c6684ed2cca2aae602ceb394cb76b9ab4dde53a216e2483fa02fcce9203b900bc5ebcfddafb899de57217f7648a705b537d712c9aa0ad27f3f4d90b7f17cfdddc5d66dea60799e61781fe5ff5f29feb41ede4f5867cb9dfbef350fabec70ac7de084a140eac45db593f32cb952b7e24776f20747a95c7a40827300932160ecdef5b2c9c8f4c6e0928f621b7a079b1990eb49cf2d1600c5e0984f65d7a8153c59714a8e3bf9b4fe1035d3738cf4d9cd1c8e6325f33ca2e5c574c66f17c589047c617954717fde7a67e7a3896e170e48c22bffc968c3cd41cab44d3d126ebd3aac20977fa1865b3eefeaab4d0c3ce9d8a8fce8053aee46957e068233e619afab2b402"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"92eb34d65c66dbfbc41d15b59159107db0d6c43dd01444fa979bc2b3a09eb726","proof":"c0aee5a0a8e7a5bdcc8b5cab6500cee0b9b6df97e4886713a0cad6534275c40390427377983f91e80b401db1c56721c9f36ab7e2d7ff08909b522717243408662af03e092b09547f84f85097005fda0bb90689bc0209284f2b471223b3bde72024dd991c32f7478a7deaf957d281476343fa0d9355ce14fb820128897ab5201b11f614d716b741663c297a6e09b3c1c82231c0f9cd7c9dedc22c5351bceb710883045f996488f1633b3103fafde88ff2b8f97f934286fd75341a13622c17d80772fc00e68805385a22e4e1bc60c3d7e3621a0b2b54873b17b80b1dee9824960344402a047e351a8cdec8037fae98fbe4808f6ac964e53023903fd3585e66440056febeb36953be4c1975a2659458778725cf06abb3966dce3410b5f46b3a2e07aa471fb1ff819f968819e29cb9427f72a0cbc008f47602770a24ab5ce0eebc352870dd427a8ca8470294559308bc8a396323d17b411a9e50d851ec88fcff614e3e660448888740e0f0ae3786a1578aa1e38da3f45fa9a19bf2d503878b8f1630029aac399b43c79e421a43baa5714f4104dae90d557b44940f7c98d397b6ec2e6ec17347d221a7e8f95d4c55a6e34e81761ef6f3982443951c7039309bd50258f67c6691dac0606186518fdc45a30d522aac49812bd400e0583ca97bacc74d1aeebaa675318acdd14859b088748749b06fbfd6a8050688f7e637fc7706f346273801302d37dd7b4716d63a7e9713ee3c4b0364f3a7529d9559ebc12d8c91365f1e9936f77bcdf0f2cc01ea64e137ea7a5717f79e964d7d00838759731fef7706ae63b91ab740a3950d463a4bb1ab9ba8037e5b936d5202a189243644946f3d0746c58b636025ab1f4eea620c9c80d1793af71b672fa9c4004b9fcf3fed337e0ad04c338280f5e126dc23b31a593509327d723bc93a7ed5e711d750bbe6df6b02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d2ac3b4b13954f5a1f3a85606e4234e478fc23a29af951d305daab5feb600017","proof":"b04b41f239171493a9280e24474bce4b68b8e720c07e2f5d887c8701145fcc5514a23d2697f8bf21c5a65321f822e6d41692bfe61e42cea1b99db3bd889113769ac384493aed38e5f1df78fa5fdaa3ce0cf9cc01d93d9f5e3f7341c3a719323fd0bc21163f251f2433ff2c8b11f132c5dd6b6e149b0e06f44ce882a5e80312764da91935927102111264e9b416b204a16bae8b10946041770294842d952e9c07eea556de54609c688c062b20e24ae07ed18dadfcb4884452e359da7c98819e010d8c38ced3f181298d9faa7494099ef3edb26045eb9964ee41d93a6a5d71650498cd78e8edd7e89af6a68b8ff687ef23572a731865879c1cc19760baffb71f46129a1e18c3ee3084c8eacaecd167c8f101c91879228daa373e207b0cdea9365d48e1f443b3279e3be204c4445be2746863e089e0b4b941364ac82dfeece4143fd23f4991c7526098b0445af76479258705bfe06d1c4393b2750bd123c6d4a312f2ab5b221e4c22f99b6f0d2d86713e23e7c8dc13d4eec2748d120c32d6550630f6c5a8fb50bf3887b33d281ad75f2f47cd6238bb8b3df94d2e759ea083258e4a9e0692fa3a8748696bc4a6e13c2e4094765fad82545766c7df060dfe34279535ea390221d9d68e5afb9d01852bc1d8a65488c8f7c820463c6f5ad0d14503b7575c25041db1c35fc26ee1bc86fc1a2efaab04f8395be68b0de74dd0e7730d7718c8a5e315ac9d581f16a4fd989250f480845774a7a2b120db5df9b8f86b9d457cbe230e7963fa5ab542f63a110bfb6097c6f9250d5cd1e2b84f55aad1bb817235c693c3d986e68d23eb358c94d601f874ca659e2264d7fca3d0fd06cadc665e138d5fbadea7c650149f0bf762703508551213be0a89ef3daed1d42b93cc9b350ba621df37e55598ad4ba76992803ba92749db22db71367c2e63dfbcd9ca47f70a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5e9bd3265fdfeefe0eedc7ade095ee30fad7a047043725e4c29df59fec63261d","proof":"40fcc306181d320b87d61afc1dcdd0dc74a65b594112308279f88811bde9d26dce99fa684154e0421769f62621b03ecdebf3d4afd36d2f66c091689764641f38f058b52c6ea8cab3a59636bd2c325108bde4c08f6759e33a314109a232cd3c0d6070f10116dd851393caaf3d52e4fe97fdbbb6637789471b4eeb8ba812e76a2d49c9f806ae8e3cc8593d4d975dabc84ca3973fd5c84926a315c6ce218462840411959cbf4672037185d218399eecf675cd47a7ad72bbe7c76fce3f921bd2690572c7edd52951fd9f29897958017b2ca013a02c1b47de9493dce1a8e341c24b0dd4e210ad82dda8c9e6b55d27919b601e167422ba75ccf906582d76836bf96571101338938f48b498f16ed5b9eaeee782e5c156370f3d329c36e23e73a28adc5428a001292c8e047e2d24f371d2874982f7d3e3b1e28d8f5e14ba28592afe5a17fa45cd2b462696fbd49cb6a1276997ef05cf5b892f6f8dc8ecd84ff57b25933e340f5d823b35e7ea1262d4cd6a3396936a48708029b4a0539912678b36e5e1267094583020cbab9ad9a3b63dbeb28ad649a5b721a3129e12affc7590b907393038463528344540939a56897627747e624f2afbf72021c75af1bb4a3b513bdb378a72e4823051d7abf9af70099ece6464c903363ef005dff2bc915607a919232796f1f73381cbde7fa85efb654f717bd4eb555647f37773fc5090590d7a0b2f4e4a9b643aa0d9d00ffac9f0aa68892d597a83e67fa6dc48a4ef04a5865d96a6646a27bfbc940575da25ed129a10f9c0d15bb8d9490a4ffea703f547a994704b4abca4a9c9ccd4f067e38ad7f58b4549dbacc8d034bff3190e65d6b7ea8ff6345742a2db1e6aa0b4d9b5dec17e8e2567b376ce8b8c7b80232872b3cf1375af360c6b918aead6db85f0a2eae931113dc6cf687477e75b2b8a9b1633d6d2c079580e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"201c64c879593ce1c834bcdcc77d97d78bc39ff85ef20e4e46386fb7ef61732f","proof":"dc54d6dcefc5e0abc6aed3d743654f3441154e72f54118b41f811bfc4e2f6f39900a20d6476e9979de96cf02d374a7259efb9994e372d9a70024924c38d5f0319aa4217107868e4bf397fc62e5b1bfdc9ba2024014fee03c4bdd74f2b47333075ca37382d82f2a709ab1ce9296f8b7e551315851020b4605ec7f30b39346332bba22631a3ea55307c6509806818613ce3a865b0f68450e556420e39c239e0809ac02fddf06aff41864f12953f68f3c3acf72759559b32b0d01f8ca4c5311a7067dd11e3030bb44169d87600d836a474881dc9ab2649355d7a7e09a51d8714d0fd0266f0c9351250b90b69dadd1ba7be3b723ece821cf6a37e695d452e98d647fc2e63ea64857051979573711909b32a1adde861527720ee5c7c2d814f00d2e454695fab603d506fbdfb2e86308d8105ac6b9395f282b4de37e53a8465287b46392c052fc64cbafc9f94bc1ef3c67be6d9d3cecef78b8dd01fd3a71cd95f70a133085601246db7a4c18542dd7222b203d182f31f90d761ea341d82f1d7ff6b90a12318ca19afec6cb0b00c57738d441fbe30ce0b484cd54af29b7a3a6639ab9450c12e346cfa3277484eba28dbe26ea70d99b162da0cfb164c8a8e89ceb212275c0968479d3d950e36e585d068a68a3c9a39bb773875cb708eff598dac14cb22f7812ece4ead3c6810a774019b5df0d5806a54e76bf1d7f5e5575bc8dc9a7fe697c3586c4cf96560e4270dcddd33ad970c0a598b8f9629a65d9f619b46a195525b21791a7d661e91899801a2df9ea800651ec59bdfe56b0083a3667e262887504f84c34bba362841f27ebad80d921487e768ae20e0aa5d0364099e3b0cbd410386ec082f14123d4777ad89b68436818a3c8c6c617ab02b4925d70396948cd780ac7d98a596d51d6a8dc8853246bf0c642effe60bea5f6002d45b961d9b729d903"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9cfbf1ae7f7b7bb1ac00f5f07ee74e57f54f45aa177b99a17d9306978597d500","proof":"78465ff652db3c474069cd7c0f541570e573507b017fa8bf0df2a7bc2c0a587fec5cd78e10f89f5d512f966d964dccb309b580c0fa9ebfdb20fd1f96a1fe581e44c4b07ea767a526d2255dc55bbe17f7580652edcc287a189af81afcc0e11d45f6551dc83c48a03d8bd2cf6f22becc8b4199e5e6bef2636d7b2fded98c89c9485874778903421681867b07c70c75d35e17591cf9eea9a10ca2d0f0a6ada1fe0da7c503dd097ba4b3059e6cd460c4fc78c517ab506165380a493f4082b3e5f00af3c7584338686a3a4931c6d72f16c6aa320a88b6b78835b591ffb34322433007a2fcdbdce56684f3f2826c0f847e0d08209e3b8f30ea26ecb9e4c1b4d9370d470a0bf24510253d1201977f1d7ba087c38e17fc154e002117b06069145ce43d3284159a5dc57eb19999babcd36d5bf7000ac947fc79f6ab85f30243031010a5573e04bfde74dcb00cf4c2912035198ac46125c58a913c7f9a0a595c5ede939671c47c2364ff1f769fc2ef495eea31d923e713f915992602b273a9b5d75b92463eb0ca168b126efe9959f90970f22b5ab1682162c832b746b779ad9cdb343889660240692c8c50c70ee25ac3592d1c03b8629125a3bf3692b75511b26f322fc02b784955e0cea8999067c6f7f213b5a5783da0ff53e55177d6ead2774c23d96425f21d1265b5678e5acd465782d68166348972ee002b06433a79742f2ca319813314ee52d7cfe83aeb826a4eaed96941952a9d007b16b139551fef0c5e42d219210cd2966dc07cba1bbc8735da9ec770daa73de80cc628d1c6638182c258edab51d098e80a850cdcb10c7f35c0abd3fb0610b865c244d4d7971e7e9844a40f267980d74426178f5efa5017732066353b6e69a30433d42d791c83340863b5369505baf1bc42a82c32a06121ec46809b080944ca108efc903058d210e25042d41808"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"560b043d0c1b40b465d38ac540f9467bdc03b8587f6434030b55f3f49c2c5e52","proof":"8ef06d276f9a2ca4ecc5502e7e9e5c6c0e6a189a9bd7b8ce5e3aa1cb61d4fe6540ca66697fa01b0cea666e2584a46a7e5687ba62ca8c49f77bf21fea0c8b2a758cfe81ab29247382c6afed02b3f6646ca88737a50558ef9f618f03cb3fd9ba432091778ada6f3e7a2ccef5ed0d1f5ab8fc2f95dbe70f005dd9b1e542eeaae57a8a8e156cf2b793ba01ef132177d438e567ba226565e9240bc327bf910c22d70e48eff1e1eff0594180ed8c5875931e79de6363938fdf40e85fae74c950d32c06eecc410ff68129b63eed79a672563c03ddce5e3cc9a9710d825ccd64f99305033a807170f326f6f5033aef519b10bb937d79118cd78db2235dc98b34d35a531fd853a56b34f5e8ff863c23e9e94959a88b7e7c1697498f1a33a246ab82a2382110af8b51133aa5ba931847fe6386a03686ae80dabc29cace5b578e2e9286653904db964325abef3447ae6fbbb9dec883e8a5093757895817131b723b52c0a367f05bdba367f9d5f6168d04b71d3325aa8a89818f4241d52e6f79910a727a037ac693ec5dae88a69725ba9dbde41cd084a6ac7e3bf7b55abfb47fd9c843bf1f0d4c41ba2d2b4862f3b9a85a2da206244fb7872df2ae3565355fb1bc6e85acae0d6c48a2e10b0ef15794e7a370191bdf0563a83a0631e121671ea702477853cc4d2ea09b3d20e8fc488cbf4b29215f0888196f32d12826d29d0b0a7a3cc2cffe5560b41ae1b2c38a125daa96fbf00d8fa8f722c8871e8e78e8c3dd9dd9bfdf71026a654f2ef0a905ae01d495b595a567969a9fc39522ad3a42d6be658c5172b267a60620687d611324b89e195384590a7c697213cf21218497dced57117466476f61226f62a2a904e7761e0fa953bbea19335a5ee0be739dc7c5620224916dc501a14683c9b4f1a2b416e322a1e229cd0f1b65d35f11c2a1b0fa3d4f91d40bb407"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"6c2451bffc4ce395e0d27d10461693c983b409a192389c748f320364b969c857","proof":"5e6a68754fa1067e790678148a44c73e2da24c8abb0a7400f133fd89288fa04f0884229ee5a7dc4c6bca426aeb90197ede5ccf0590405660d6f9c9e3355c7658be2dfd59aa6cd66700e96a416c3c9c13a7380abca86a3fa95177d7ba418d91553e107556031d528a62bbe47efc56726c49e14a011696c72c855684a6d0d74852a92769405d0d87b71929855d0f75ce92a849df814afa50047c025fbe12d2b00b77e2000c1a858ba84bf63f294072e5132949b643fd8357179e6de45623170202c1ded667ac6f08282da8eb7bd1ada869f168193a35f04015ca3a27b70a2048091c27b1f9b1e642ce6fa624c7ef643ad39aa26620aba11aa380c8546ea720484cb806313312bcfd46b75e570bf7627aede90d9f51ae805be908a02d5ba7178d28c0e577c69abeb27cf9d8cfc64a6f02d11200a4b1f8d847fa371edd2cfd8d8b3b2a9152c5f6405e0a96d5c17253b292dce511abfa848d104eae8dfcb4713dac4f703bdbcd5ce539ae8273fd750a3456b965faf8b6820ba8e720079f5e06e37a16da0082249fe8373378a0ec7f173697c7d92d7bab8e677c3caa3ebc3580b8e426b405c2a4ee4d81ddab352abdcce1e4e5fbcaf8a46c6050c2cd18be3cbdc1234eb4f731c5ca10f71f25893eaf2cf8ac95279c3dd2add9ed4472e42f939174b0178e85a238c6ad1ef02464d77f2580c190a611dd342dab67ee1aa2ce2274eb320bbe9b4c74a0ad197bc0ac5b1fde4e2aea21c375234d279bcee18a4b205f21b17e06159048a03f898a0c88c34dfce9dc62fa7086218a419bf79b11c604a37a4e1ee805d1b93d8a305b6568dc09e2ed512f1a3670885873559fb4c7ee888305b92af165c82f3cc18c0d765d507bc074edf0254e4feebcf16c090a303d227af5ea08b5829fb089933ab8d67e1f2f5b18eca9d4cd4691c2e2a76f367616ddb3b31306"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5a7e09623a7790b297d1ef1331289b69952e24460d6d114c0448e5b9c649163d","proof":"80658d26ed518b5d1435ffa38bc36e2e29033c251a1c0c9040bc716d5228e51908e4beb222f5fe6da92f81af261f7f01f12e33e622b064fe742da35f8ad42e049c8b0a63977d4addc5e517629380cb778bae900aebade6b54f99379a1d4e7635bce7339899c1b51f0520ff887e55e5c98f46123be30026a03ef4c07551f43e0ef0bafc893943bf1d968fd26ba8d4fd66b034a734d7857e7448feea2426e6d50f9994c5bcddeba19ab608925ca5fcb5093e2d71feed90e6031ea5f576f9f97205bd6f3535376a7638249274a5d51635f230e58d64849412d2c888a3588d27430d90916005b70853cf4e55c93bfb8572903cf3c21c5b14db987bbc24a86ef8ee1ab6e147be4c8f0bbbc7082210706047a54e471ae8f6b602285b8e221929f9584cb82e666b5c9b760e327fb3c90b94dcf5d4bf716ce75a02fbba30c60d8dd4ea2732fbbae299abd20f38d03d3e793b5a528ec19450ac77248935deb5cedfdc8e2802586ab4887aae53e765a602b2b209d99fec13068cf9e2a88079ede01b2b280f5c8c8cab764e4edb779e7347a5400bd7b5d81f75d17d6909d5c81e91340bcd7b88bab26a50636ded0c7f401943ec4728620b50d5ec865f89a495d989ce43ff6290ae4d03724f9cdaeea7e157304f9676f6802db1516c22588970b544cb126b66eabea28cdcc5ae01f3a1ac86cd080dd2f09f98164afd977bc9369ad743abfb5bd290b304195403776908884af7331a1368a97733d54bb26c61426b32dcbb1169da525f65fe6847cedf9425954338887362ed0481f94a1a24ff6fa18015d33a00c0369fda06cea624388e6ce671015019b60be6e9e8529d0c08da640c4dca3058f93641e3c35d27b8e401dbb1d22bb83106cf2cdc071fa1231cb210f05797d601752c48693fca4328c96b492134f82818c19e0ff785bed635e39550cc22a85b08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"da0266008d80b1d6afa3a71d27b6e72a0d5a9d4e14fcf3b19bc092a4daafe101","proof":"daabec7c86d7bc4fcc284fe84e5f97b16519370d86c7c209fd160c7f18540b0cd2ed4a2241fd41206cc6a407b269ecfc66d48f7217e9354fc0e28f08b083dc7c1c5209a5b1c7e73b992d18251038f1230891e0a16565eb36643d627221d308092c01a7c046bc05de01638d9a17934d6b7c6eff4df398fef0b8c5805d16af282df28b44687acab2bca16a921b46c7cdb9100509589ec4073988db241854310f0acf9ddabb90744a99e86ed2b166454a5fb1b000e2ef4176e06e278b20bab91809d51371fec79f023240543f9ed0cf7ab6d5a6e5a7bcce2df75bd8a7c0a77cc30ab08300180963f293ec7ddef3b25f87dd08afd811bb577aa5a7b5e326c50c3f40040f7678a4e55e52ad06231bb541330ac3f9caa58f4cd3a325096fc7452e9069da9f9b693835c6aadfbdaeb5cb69bd64ec6e3aa030235ba7d2ad8c68cdc6b9325a848de89581aed30a2df400da50e1650a93a2bb811374a112846bbeb8501a312cc7e3a199af9221cec0875e027c2e93c1ed4fbc5ed5841d89f69c30606df777d08154a5a89c3d34b1e5f216ff5594c6ef20a3e7cf4e1c951626d09546bc915e7a66e7dc9f9442bead05f253f6b48c75baf4b293e51598149c9d055a4b5caa3e7ec8a8ac7609c80aa3bff5e08addc958c7eed9c5234068f961e97c5392e8540060816912627ac8a30116eb252ecb2659b7a09258316aaae6dff538a8aa1d3f7e1480f846c06f48d948e14e8bffe6b81dc7c91521b01e91d90eeb73a5d1386034e43267175e9285c82a209cbf417fa8a987170ab2d737885231559246508d443fc030294c04804827616cc0f1ae8d7a7b89d45deac53e571e04ffea11fdbe850e9d754260b41a837b766c4956052d28dc2f75e768df6831ec940b85a5db61150c4ede847010dfc0db92897c78ba9d8a17229ffa815510517940f70e11eff7a70c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3e17a91b758a92813ed2070121e5d1cba4ccf578b2f4bd259334ae3ba38f8a6b","proof":"46568020a933fa96bb16a478eda445306f44dc2f5f6d0d5972d2dd98cd7f194aeaa66fbec2f9ea55d7e63ca98944db8881ba734761e92cea11b98aac12bfb1270e6f463c509fd24ca7e2faf38af1931d3af3ff4b74888a4dd69874639f6e12503cfc5d416cb80de755dfa0ee579f8805833195db1d35f427e80df99baa78482827e30f5dc77a17b6d3826f290a7ef4887e8d3308c50ed30a5f9a543fc8413a0ffd0211c13c1b08083fdd9328773fada5e28ea6293ccc35ecef95306f242bb30278dfafb8e12a9b11492dff07dd2bf2322367bdf39b0ab613168b868a58974203ea1e06e15904f430347620800aa58b6d26858b4f684ebfd86cb2eed346159170a41a1c907e8b74216f799d5daccbd68b30eb1287782ba7017fdbf84d2e0b6b3c5e0c8371a86a211944774bfd9b7a93b94bc7048b859b6ed4d7f12feccd633e0f807c6de11c73ed1e1958d293cd25a5242942485e540dbe4a348809fe312c4947ba133406ba65fe07702152872447746ced2e17a1060c254875bbe258573e911e70ea93e5796de883416c7831e35221d995ad8131d21e15e24b9275518800886e5a3c0d4a34239968e35a3b612d0728f2964168c01246d8102263a97e90c8fa3372545196fc12858b1a49644e49661ab64fff47ac47aca6d84833c86f7d216173a20f7aedd9863a548abc761e6e064abe1f063f8f2443416cfc93b2301cae515c20d9b4f1cd7b6143839119df93957eed6400bb677b2ea1d6467b9b24d828c15eaedbe0896de3776df0ce0c751ab6b66587eac5cf675f6e9c462da5918e7705553a30cacfa22f292268a9610af5cffc35231135b43c8a5f3a01e1b62a7cadb177ed7f4305697d1681d8c03d551fe7939f17dc7efed66c8b6af1a5fa0aebfe15004f89c89306832a4c234d08cf17b7344f856dfaef728e34f8790a1ccdffa4bd0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f887cbfcb76ae215b83007635431de8c4f5e438719c49baf0397d8f2bb6f7f0d","proof":"e4b8bde497444c8b6324716dc4c2acd00dca1996fd9b425b3cf7cfe68358bf2e940d805017f5a2f62d1f9c57a6803f08e42b78e7d373e38c669b2537acb5d30490dc9a88094ec0b1d417252e46e5f8d814a9f4a1a07f31c6f4f41430ef4d556d9e4fcf6fcfff04554aba3f65fc98e4f24c160f08231ccfb04af1a6cc9cf5185ceba4019c9d2508ca4656f833a60562a1fe7f8a9c84e2f349cc1aaea83b8c2e07b280071751975e091b52891ae41c0702a6516af83cd86d1871838691817e850a564c24051ad6ae535173876c0363fecb034cb5fae1a77e5b9b8da5ba13b026079c558f90addd5500a696a86509bc32bd0a5675c4f8a89511ab6e91b23637763956b28c3f4d66a1de685ba959ef4a471ceff87d4cfacd6c837d91332b49e1ec5bce80df815a45ef592d4de42692a7227a0c8fb53b486419b1dd26fc5e790ee0653485698128ad3d2b91e99cb6bf3531a7bb44a7734a3dfebfc19fa5c2d559fb3e54901f269d14c102f0e20a3d870fbac4c628cc53891421edc8ddd39aa0542e66305104740c7aaa77661415dcfe78759d3cb07ff27bb24f216491ccce4f12c17968c3e56bb3fc4e784690d17bb3ab90fab1d54db43438f73a0e132b46e42b837c18f7ec58f2e9d6a2aa79250a09d7c3c90b6f3c9c872e26e3f5b8cfe785951178703d3631c9d180b7158de07fd59c733902104f567851dae569152dceeea1be0e8ef911300a7e3471fe37a26663ec4e559c42f1c93b632c13a647f6e95c9aab06908ffef9f7fe4f6e1b9e59cbf94d46107e48929302f3869146c1a828170f276e280fc39a42465c72490987801d51709d326bd2b1bc1bf1ba441f2d58f8b45674657c740bf7b4137cea44536c21f8442de20c7d5de1e9fc05a1fa6597aff3b9049853859629af5800429c52cdbc5e198a2e40a387ccf818980239e41668bc2604"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"eaec688a563711992a3218e0cb2e12631fb41dc5c330a04bfb742b312a3cbe3b","proof":"a841ec9aad2aeeab225b5e58199f1032744b3c674dde52890fafbb3cc8f8fc777605ecc6f877cf349f04c567d59200478544699b031c730ef8255b999369ac4b02b48bae5486687b9532dae3166d8bb18c4090d5bff08efe684bb6d70ed12770607970ca3509505ec98478b424ac187f947f7fd70629e5c366f3e6b55220c738f474e2f9835c8423b32c6990c4bdbc8b67f354c57c9f1318cf98c8f09e063e001c6a42976fc5a9234fe151cc6a31a77dcf66283231c7d4f4804cc7e3f600aa05c2aadad0ce10ec415da7c3bc98d08e363cf1fe62a86035e9d4cf8ddf32c3f00a0ada5cc0f69fbb38408c6892eb9a579da4dae2552a3fafb2512f3a2576d4221744515c6c11dd545729f30af5f711661306a1504c802b3faade4b9a8105ad7d0b764fc52027f02db859587203493ff6694376992138bbd70c19ec8078ee4d904a32a0d61d4b147f6c83dc75f3f03fa8ed5e14ba3299eb5c63630b22dfbed36b1424ddf68acddf353641cf0bb49ea9b3ec5b8979a0ec46b974edbdabeb430edd2ab66b9c41790bceeedc0e7a92475e760510132c15eecdda233128e2e151ea0515a076b9c9e59150baa6d8307b2920cfd3b37c74eea7c2eb1275afa23e16efe85b9471fbbbd4917154ba876b89cd3353887507490739b9085f6a79355a4587ff48f8a7f179cb83ffb8057e56da2ef0e487d8e958be6f22dd436c53e76b515615549e2be6968373dafad6ca2c63a5fbd3b6b26f7cd88a419fd1d28d16117047f7181c81445cf51f13524b72a23f715b210bb16be47e92ffde3b44611dd940fe625748e19482bf5503c075f85a4287940e67eb11fe31b88e0c6164ab94a3067501586ebf5d01854933674d1290fb39ec23a1ad23889ad193b11606995442af9f12084ecb1fb10a81d6169950b0db3aaf0eea1cf24189adefea61a7af51f2d1010509"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3e5c07eedbb5ca77de5c7e80d8a1aa27c7286189a44bfadcbdece6845a20f62a","proof":"c06aff854e9d07518200eb7e253898debeb7c725ad29bd766b0864c7c6efb70dfca439848b897fd680d6c7d146574719989e4013074fed0e76749388ef0cc75ee84dcf6541c70510c4c10357b230677e180b57a1b6f47d5520eefcbb109fbc760efda88ea14bf381c707a049fbe64604a52df16155de1ff5e2c690aa5458e5081e358296067c88aa4ef75634e11c046f7cdb16a7847acc08cdb362b5c2e25d05864df295ce2ae3de0bc90ce32383d815a671f9b9d77a32cfb64504106ecec90a1e86425ace50370f1123e0db2a93afdc9e6667eecc0ce593f02213df0837e70080bf4003d4e694e4e145c884ff920e79c7e86b2e44e5d8af7bafe4ab554cef46f4fc4437836a3852f88589dced43a2324cefc04e4baa98d94c42dd6f844ecd03c034b4cfd7fde08013efe255ecd281bc7719a57c052f4cc9ef142ca33eca595c3e7ff9864853a198d2777f406a43c747c987a4227b2c12f1e459aefa055bcb325ac6775cdbff000f6134bd662ac7e78169ce593fdb2318bc90af74685c3659673e5c1f6c80537c30607b7e322592f4669203cde682ae7e019f3cb5cea686a25e30e81d9b59ead93a3c148518435482e7af4851f53ad88d4ec52864b2081b374c228b3eba8d1b3595ffc7a670f796f217ca27dfe091f51a71935c97bc9729732d3613fe9964201e6c7d6343aeebd1128a8ae2a06de322716c875aa409bf3ace38c82366ab68ff81d4ca23cddf37dbf1c39a59494d86cf6882687e1a5de029d03826a7bf078f36e2629318f7528cf564c1490cc8fcf827c0e7aa3b119787c49613909737ddc4f2d1a976249706e6337f3abebff7a4bca6e43a9b09b1550be9f56a2139c5121f2b879abe4f95ff88372592f7819ae79fb5d9b1fcf32f64d1ab160ed737e8f7f6ec8b6e0151b52c715e270d43640a229baf96303fdf1416a9a4c705"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ec9f15057f2e7e3884b5e3ccb80cbf5af199e1c08c6835ff3ce6af60cf046b79","proof":"aac43333aec4836b05dc8027376a05f611a80aab2e1c1f6ad7c825c7f11cd27a426923757016bf5197d744f3a38cd369c8b17e4a285bd025159c8d615f2a8350983504217b19aff955a29894d6f5200d149fd9c6e3e2aa8a20ff81371989ed446a6ebe3e315ff4fd613a7182acc6ebdf4642e8da147a92100fa725d70501cd0c51b1c9fe723db67078e558026cacd3ee3cd81129d4a0fc17f9106784c812da0eaefe63131c0c6895cef7470037d2c4d3f5fad1e229fc8af6210c6e7d9f544208617db64ccd39eb1a237581e4037c0b928285b11c1ef0f720f720b6524240bd0e607470346fe9bf914295c761e5d8b36afdaf60d01a823e7ed3a4497d2a29fb5ede82230015ef8d04d5745c7b67a1cb9cc7e5a99588b6f4f94546cbee91c8a660d8d2605367f15a1167b7c3eaf1b020731b731b8717014adb6f79463977873a0be8b66de1bfdea534456dad47d7daea2fde6a272f3a18b229fd3af627a76d787f7c924a5de9f239b84448f2e96b45e2977a1698b4c678d487a054a1b5d2d5f47064c08b25d9ff60492a67068c692021d517662cddecd2eae8c483cf823fa59301547ee3b60dc50566dbfbc104465339bf2aefe7c1d6f4d952f3900160c392c51186dd8ec5ab1eeec008c226cb3ddfee0c2b46e19ca7085c29987f0d984cf49611449fa5a8b18f595e099fbc14abd26d07b985cd7b720d9cb04bf9edca07065b51dc2a67ada7a668da0a0ce6b0a11be86863785f32b631a9c3808fb03b0d3b3e31fa5c909e7bd9001f33a4a460cdbf9d928e52525b83039103098a01d0af725d4be666cc9e672bc93d68b5cb2d2e2a8e04e94c02279c2437054f10a0b36d44f3409c6ac8d01a06a99f26ae2e380fcc0411fdf8a7df5ccaca55689fa47aa54e5f05615cdffbc32c47432f3e1b8180755f2e49aa569de53e669bd48add27c4c5bc01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1ae4b302a7b2897b30126c0a81a78b033a48c8853919953489035b48d84aed43","proof":"34d03734c6e427a6823bafa467237e8424e5a73f96579aaefbb1eec40fab9f336666384f322c30a026bf1e336d36e071e89bf1c3793142f9ab90cb9396d85d78ac147ef8eed3089cb954e09d4a664a37a1194e994550f0d83978c809f1625127e8799187d936770fd0a95726a32f848bfc0ef09e16e777c1412985a3c2b9f405791c2432eb9ea85b72e9248d0eab2da52d1ee8daed492d680275954be2d8d903c32ff08af10261e735c7405ad9f22b9193c043999c6934dcdff559d0baf18a0d1c08ef865950a919605c672f0d82b8d37d9127a01993398d178234b96c53840a8e9cd01c8386ae06c145179fb2f972430b65ffb42d683264333f79ca2df8dc6e2ca7926a5585987d515feeca2588240847e2e4594d5d52dd2a56dd1a10e31611fe0509fdbc68152329e7936da86324c8e7945fa771afaa5590ae0f4c2ad7de3a0218e86b608b69679965cf21596dc5973f980a7683be1574e57e8c1bda104e2318848b98f04336913020012749c076e519eb30a68aa4f4f0a6daf9b598d79411225678a326888cd3dfc22119f584eb2cac7c9e217eb1c6dba6271a8f27004e75ec656cd903448c074e2a4f037b8affa1c81245d321cb2affb0e77463cb862b6af47fcf3114f236d29c946486931262adbd7337536ed701aa4717cb4c2e60400c4689f7a3b110ee6b5b2836ac0b03ae47c29e7ac98ce57e6b2d05239489d8cd22f4f707629a97518dcd216f1fe88a1e8ae11ea3a3e302fac71b0d7843c3d844466e75afdc9dc1375a3fb9d6cbf57c6b6f5c979ff8ac683ac0c77b34b99691492a4afbeaf4cd165aeac7db86f72bb87f320371d3065112f1a4d40a37d3e5b9ee760406f244665ab37fda740836b88c0b833b60eb3dbe3f2344cdd2a70dec147f09ec21217fa987b1ec2830406b4e69bc4dda1658b0421387b48ed29d4d06990b06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9cab7b358ff87fbc26f50d0e621d9edadd02c3f9e778a7f453e119db0611197f","proof":"b4366cedcc740a0eb1b400941f4dc63d4308f674569b32730e3c7e27ced2d633b8a3fe9a509bd6e03a2c6e188631ae52db12795a08730abce8d7c5dda6a78822242df4af5d1162b410bd7607d2749bb642547a1e66cd5380e30ce44aac7540223e514130b0734f11907d43dfb3bd6ddfc14fa055e79de3bba1b25c277307443e68a4869e678ca95b30a7a2b94b577d309fa6873299226df9c2a5c1e87634a400e7c92691dc658f3176f042eeeccf1739ce9972cf6a2d6a16c818426da39a78099fc201e4fb215768505344d891104245b92a56c9f00e1f19b71277ae809a4c0566622c5489b771a1079c392cd4e3007d2dd25af39a86255b1e3d6dc3db91e759fc24c2cd090ac79a7b39e04c689ee17b04ba51d1a5a499486fb7206789579745682c078d3de7a49e754ba4d998c59d9b02615637f6228e250188f6024a025e27ac6c91f49a9dd355d7dcc046ebdfb718bc564f13223bd043c725fd5f62ef7a4d0c2b2d5ae24208525caf3aa0200f35d5e67ed4b243740621bff2fc1293c32f33042968f7a4312a42cafa5d0a6b69f9de9371b9968a0d9f9463511f92c9b8894272499ba529bc164e8d8d3e9754da5bf1e597cc9b459bccee56234f166e60c30f7acd84b9b91aa22e21e9491a90b307cc9fbcab08bbd7af96af16ac94f4d0434852a0c08f220b1b31c6594a74617ddb50d0b7996c653fe9ff23976ce378c9cc47b2e3e48fd950f414265cb54fea221683c6f689fc81be2aa1f77d91ee36469964f254b33c5bcd684e2f603c77049a0341332cd844e0585d1d2094d1081828b6208ce23f4832e855df417f02a128c2b2f6a2e7cc3e4cdfe69815a9088f6751bb298368c3e370fb17b753f5b561b4dfa7259a1be67129f8136a14471b073947ac08190b3db35a2a4f4bc3057703e468ae79d4cd445cb6e2d085cfe2b266fb69a70b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d472dccff34aed7686dc6c92205358fc2b4389a87974338d01be7cd73d9bf369","proof":"3e0537c866fa9ef147d502c3eb389f310743a81ee41178030c8deb65054d555c8e99b3bdbe1a23eead1b6a731fd39412369518671c92f5d1c6fe66853b1aa035500fe361c04156cd0d2701e3bb2850f498a934d30f45a11f0c903b794de6a3691a96e818d53b10fb9b27c1011fb5991fdfc6e1aa70900ffd93926e671c492e0fb00a52651bfb50430b42299c641365670b16e6f7265bc44a3abbcb652d53c002f5719dd8909fb2e09639c17f96ecd49b356d879c778c82f5c86e1a61a04bd500aa9b8929b42038d8b24436f9ce27cf3b10b6c5dbfa6d0aef08417765641d740ade8fff30b18ff3bff38801386d1fb24ee92e6f47f75b09e60914b71ad2c10e45a0a4479ee47e6461139fa18c34ed3cb541ed4dff9e31a95dc822d35bad6b8838c82c6c6b61c055db5b29cd88542e50f34ef7ba214d61b6e31ee9a590ceceb905d606eb58e6d9662dd2994dba2fd4b7d351691784fb087923fd28379993b94573ac98f1d9bb49a49b9a50ac5e532d6d71352c42fb7e64fda6ba6a3adbba031055f0420f4eb4c49dcccd6ab6865b9add62f0cf5c8a53e097f74c69e8e3bd1fd1059c5dc07f68cf19ce7f85024329afe16f6c101b91697a29adc1bfd01ef3b80c697e3940d5afab035b115089f8c9a7047d110c4097b4df83160fec81d0712da324541bfccd40534854c5b8cbf0632acfa50bdede02781e76ea2b0d80bfc5f6533a542e1051d09593c773287c41677f16170e1e5b1bedea583a9b2a528b43e7307eae5ce3ff69305b3d49f69ae9477866287138d72b5ef4ecadcb0a802f5e88541cf2c2dee48e9fe70e5b1ea39850c0c420315ad73f81f171db797a7ed9cf51ca0fd6aa6730b7fb171d37b34a1b8f117e92a8570e7117619dcce2bdcaa239789b01bc13361c072b4a92f556586ea0f72885c524e710f4c3fe9551177db66a83ec02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ca639c1d7109f84da0781c114b9927a867502b2cbd6fc4fda2da310d383b8c47","proof":"243b339a3f831a24240a884d2aa4331dc79de62838857b035121d4f0569c611baa9a41112ed23f9a52d0c3ca42a95fa1f50471a8024cd90321db96e911a34848ceba08de55e2af13f1ab5e4a8611c893514141dc9856ad7e5ef146fc33e2e272e6c5008dbdea72805ecfdbc5aded2d5e4f611d58aa0ecd84e231c752dee31c2f08f4b84fb24c99d92da018c9689623b4ba6c93f18934dafd0954b8353dbe8a0d01c2acb755ea40e5e6451ebe5eafe830e754a38f922c313333fd438294ab350b866110636e6423f351cd03bdf1c254e780be50402b5e587a33d378581ab305049a5de3be11f22e5ad957c34b3a6261c2c25d53728605d86401cbd9f0fcdacc28284e78a2035632a91e5485dd1c396c80d56038e117ead4465be693251958f45e4000537df72627a4636be616ba1330a906ae613f530bf30d5ccacf1216eb602ef8fceea33c349963c7eb7a292e9272b2bf7fa368542477bb82d6cc6a25746a10b8f7218464f9668455ee808d4e61823cc95fd54f0a37c6b540806c001387fd0e5291e0233282b17f52bce596b059ec4f8eb30559ec6285bc2ce13ef48969803e429ca2f4283ddf5d28f50699e6a80fcf99374cb3679cb2bee74bf3a90b88bf46c4c1cc6f5e607c2d5911dcfc3aaf4ea5db7b4eb445e5d17dbefc4d2225d43973ea23e42ec32cf3e72dab01de85cea44ad4b6d8264e33a41555ae8a12b41557302231678323b57bf20dc2856fe48838e00948b69dd7d33252e6e0b4dfb0d3f3460a5c4718605d42deeb9498913d767f36e41ed3d3d6bc0e9e69008f7c441f3401aad30a41884f49b112ee92dc03418e297b9d1687b5e733f2bfba29f7c0529c2820051a475d9667074fc58a3101c6846c659e48b38ef5b2af5b1193c7adf6590ae13518ab4f4f6d5d93f5f8cfe5123b859ea6f2fc9794533160c2eeec63ea0d05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5034d6d4e2e2a5f3461afc7ae39e346eb82f56b6e625455efd1316e50b34801a","proof":"380887d33a3bb403c5a4e15e1d99f5180dca71b3001a4d8064f486e7d1346812e68a25f7cdab2e094240ba9569df86fe6f5070ab3bbc01cc3a7153d78eec0f0934cd6eafad605272aa7388c7a04b970a00b35ff4898faa0d92b8f74f8f79327ece6c2cffd85d7968207f5adafa47a717e749eec9ed058d77cb73730751624327600503921c2e9f3f8d18a0ecd3df269bae64e5e48754f89484d6ccecd50a190e1096f5feac3b6729f845addd454236b3b21aceb5b284ecb4198f2c6c0464b40e2fdf040a8e36fe0f7cb9443ef371731b095805e19843847f3fe4c8f220988c0160d4973e94acbac17f2d56bda15dbe5fe13ed1e20202f90867f74898da22a96f546f1bf6c631c9aee0980f733672c7317936b72e13ae0ce66062db0b33efe4113a90023a71b1a11493a784a7a0aa757c7021beffefa0075ae0a2180de7b7060162f30a4950e6345bdd42c1d1f26afecf502dbb64c85eaff52355281f6319b64694299daf9937df95baafe19244d2dfcb288329d61ff374f61afe5f7cbb56a433e4e7042e0c89ada114900e8488eb66c43a57676e6407447e9c0c0f0a711c500a6c06495ff695664cd5dc8df62e8a8d8a9730c2a1343301e5e8a1b990bd851f35b45eeb8d05282668c98deef5b880906cd5bffc3c63694ecbc318b5f882e248791c7780b163f9d7c5f1c35d9f1c3c97c047b342b78a96d36b618d0b179348601440a9aefc43662a18c61dba131518be2323c750069218009b9190745a2c60c96a86d728a6d61de234b645d5a9116066b0d490fc73f7d8104bb18b49cd7b6eaf27a012f819464d80ceb8a0e2f786ef34da06fcf078631b3c8d69315a9f88044c1336906927b991f4a4d1665653527fe15b0dac3933babb699279a27b6a3cb386005e4da5c890a3d6200fd097f8b4a5dd32c2a9c98977dd1ab3410eff89da89de01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5a9e456779fec76ef2f8ccad0eabd5d841c3c8e1813746921975ff60fc8ad376","proof":"3e5e34a322c235ba63bbfb7676b2f72efc4d53b4ecc4e0fd40f89be02814a20c68a0abaff669f5448bfbb3f1d52dd792af96dae27281e172f136ef2ecdc634253289c4c9c53e3cdeeec7428b3d76823a79c479796534209babd6cbccd70c2f54b87983997f9e3c92d554e2db664723cfa1892d72c393662867ef5bbbbfde402656a7e18038f6eac3329447a3dc9093a46bacfc1ce388639d0ff8dda8cd10c100af5ea2dd6dc821344ff97f5d67ec56b474bfbeada6356fd9f145e45c9a02a60b6e70b99dc5262a92207db52a4926ad320c6efdb2a4fbf7298a9e199b2d36d1031605303b6644e3527abed10a7bd2c2dc1596ed6e7e066c3c9f0101a13b7728424493be2f5b8fac536ff4100fda1edecdf0a5f07b6e6485011d893ba27c1376580a15b97fd806317ce18a8e4df09b66b87d83c9ca98e58c6637a2ec912e49f70e94468839dd2b0f6ed12dbb35179eb571b9547b85238e6632aa7b8eee37c3bc78348822b565538d69d83002d035dfbe7dcfe4bb86d056ba3244bbb16d423ab747aade9416ebc75b9e8bdd9fe0138266a9c7d50449aa15665cc71f0ee4162a9b3e58f1836f053c1f448d3c63ff9ad203e8a7365cc33f07e65c207825d11cc5ed0cda61f6e4c6c8aeac43ff43e85d2019c66d6a2e24bf6837a58be6796f573d1867d05a9b4c66bb9f561e350beca489ad3bf7e898ede993f65c786a09f7729d9c7e2e8a9e2df40b42e12bb678e95f9e5e0a699bde3c9c42e1ffca6b9aa497dd1f3c94bfd84dfbf2699a6ae879c7a5c1ccc616cc32f77d06b4b9cf8539d69386f2047ca01d54c3f64fa11d33e39c66dc1d3828b589fe3f83d1d1f40efc3fcd178f1746079b144db3a333705eb158b5b7e554f5fab756b33612f3c19e9306d8f0f40327d6d044a98750d2a4ee3b0ab8a6ffb476387ce660f76daf55dc7e644c561a01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a055e15980dfec6166ae79b4e58cd7220071e30608eb72315f3db3a48ab17468","proof":"34d01f3fede98737e00e23d543c75a46b8340ab83e19f6aef5f000bab50c033f409cc386178e2dba8c31b5fa645dfdb1556425f31442864428b0ef96abc613002e33fded95a75101f6080a0edbf4a0a350dde17846d6cbda01cd61139f17577d48055709f5c0dec2aad374758c910a040a5dab886b577590f056b58f95ac40470ecc8cc02bb6026056a525ca465c18567efce854dfcb8db0bd5b740818447605d482c611caadf6809754f0a8eef5d5b06c448897586a7493446aa197b59a4000b7b7b7d8dda33b3deccd37671a715e7ce7e0211ee357be04eb418b499cc6ce0e1ade3ff80fc9c887f8f3f32dfaaebf67c297ee313cb973223c60348155f398752cb2106ac0c1759250f2383234c13af62daec946c6a3ef7098d189a555447a1b0829c9fe68670bed5817d507ab45f492e1a70bfa924e54f8e4d9b7cec8488c041e9408e4872586a9ab28975c72d95feae7ceff68b80aa2fd3bbced178d774c60666ff0727694773940f4ce74da583939e8426ee91db2998a20c9799fcf90281ea0942ce68ab57786fbfb4fed2a97f77b6d4408a63425e7471078e7f5ef905923b825db4fb9ddf2b464380ef2c5a8d2364666e37da486628813d2b4f77e97ae7ca415cc7915ff614279136583f9af6fd9329312b8b77a21f8e5e52bc2e5c80879aafab55b5d5bc26196effe30444b0eb59317367a133da8cbc2384171a46b8b3d7e5b70e83a7a8a73d929ca1f4e22e273b9aed3320ec042b8260930486718f5172672745bec6ab745f668147b59fb2a7c2d4832f29482d898c107067b3228ed15460be2af22ca84d8ad21a32a9321c05e68f98a0bbec551628ab7d93b09d7000da2c6c058adc16c6f0dadfc957af16fcdd52e0c62a473c63688858e8d3714bc09ac9d10b1cefbfc49813f4348a6961d19ae12945c61f1bd87b7dbdd232401b103"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"90dd4f02d7851f927f90f155eef707483c8a40a831637514b4ba37b6c7ca1b1c","proof":"f01f5a372811379bf40cfb374a97a8d4953d52de68fecb2dac3c9cea72f0a6675e9727d70592333f9dfdcf73e94ebd79551a49ee2fce1293c34743e14b65d04d28b689ecf51d7f0a7bac081cdb120ab2c8ad0b6578ad549c9744ad9a71a0a4465048b7fa066eda7e9ba08d092f381ab30bb5c15bc819edfc83de3678283032023e7f34df420fa5b63199d92a9e60df3b933463b40edb33f7d23133160fdc140a221b0291e6fbd6d4764fb80500fcd3fc4649d49a15aaac19dffd75de6d64d50deb6d846d691c22223fe6083a44b75bf6ecf1d271cdaf7eb3363c39b4ef51350328498b769d3e91e2b893dbd0d0f180a931e95b41a3f42043e9ea0560d969460310d57212b39f8d376f76bed6ea36a8e455cb3bc1d82fe2a34de513f2d8b65721aed731d4bcc972e881d61a14a1ee08ee591653bff96a9fdb34fbf72a4b60fc57b4407e87f17cd470d0c9b6f6a29a93d40ca1dedc6a64d9299684ba9805ee2e26b048be494d464c2326191d2f6d9e9bcc3411026ddc63f5d1ed05605533c24b087edbacce94aa81bee98d6e1c01049be4b26cea81511ef336c6cbe964ecd7167ca48c4019331c4968edddc155a6242bc2aee555f50f957f979eb6f11e2e61ec1d1c1a75d8daa118d51cf891403c6e19b31da602a63b98e9a3cd37e4464b3c621254e25d59249d2cb276950715405817eeb2442456eae73608c429f404cd89700d1651b0c1b798cef1eb241a143a79c1be1c4daf63a9b3c2926246bf614828f9737a02ce03cf05e1dd598c976cc5a4673ea645fd7782603f805e32edbbb930463dd4749d68d2ba4e26a8ce621eb1e81d45ff0019c1fd871ceea5485cc93ef154586c5c4fac8e10e3b95054f03c026a84a107b26a97f14fdd8cf0ab65fa34ebe80f44b5ede8c6eb1ded6a9c3a0d61212aa8605b22fe4bdaca8ffeb844d7bdcc9200"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bcbf95b5e3de10590d19c26c0da65f6f60f6b20e443997643e94781294477712","proof":"c8acca31d2ca07fadd5392c12659633832c78599d5c32456baafafcd0a159e4d4aa2ed4396c89b1a7a4890987460eb837ba4b0c65d5ea9fc2c74c7f50456623262065e2e5854fd43f6a829f69c27d131b5ef73ed6ae849d9dac420fe73fedd4168946ec5d2e82fddf7b7fcde3ad45818038ba35f8a01f190868ca14ea09d604ccb8d8d8ed2dbc639358ff5b2227d8e13923c52837309b3bf75a5c29bbf64970aa763be8ea410ac8168f2456c733f95b1f8ddd5f4b7bfc9a96348544eea082405e1512b2bae4bfafe6c58a49513cb657c86c599bcb679d91a6ac429561f26a300f42d89abb178426a2bb09c874cab59253ac3e4c1015d735ab88ca870cb966d7eba5289b964406020c6c1ff38e9a3f9d54b798a5a753707f74d9b7d3c317f20271a173e64749fc534f092608e12be13f52d0daa3d5d50f3a7c24832989762b576ae1b027be9eddfe5dc290645bebd7471d3698f0e1727ec66fe96ba2cfd7cfe0136aad68a63ebf561688b41bd64e076b82997d2e1dad67d38719af50dc543ff20f88f19dbc8b3e01c9203613094d362478c98f8a0f36b534817d136168982e33b7a46067db9d5671618e34018e7c4cd7d4419c9fc0a954fc226fb34c85d2d424b5089ecaeedef2c8f5e0d92fe24f0802884250ece6cd4108c38f4979d68f82c27ca2cf685b1cd6e5ff03ab9be7897a6be1dab8b19395e4be0db384e89d255245844e49a7b3d5f613d2dbfcae1270df5e938760551b1b1b9f6ffdac74cc1b1c2263c1b9f17e681193571d73f1703e3d74afa55be5adefc9a167050f61bbf93d5226ee46fd2eb84d8ac1dab400de0d0438788227a7076996fbf3c932435b4621d103ced2b642d2b901390eccfcfada3019aba12031ed8df8532b2b21a125e7c57089a130c832f8c8c4371bba4f5aee14c2fb4e49caac6047e73371b5e8ef537300a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"50012e766af177cc0fccd50d911edca3215b9d86cf6b726d2ac0246b807db779","proof":"f828b989121345c90d75066c49409609ca7627462315346da0175ff3abc0502cb44e19bca1bf8c2ff0b780aabc1d63dfb6ba3010d532f5df79588d030434310394aa0723924c1a1732da70105c5938f29aad63781d201d0b82b04814de9f1c5b5a8b876346db069b446f879bdb54154a981e484c81faafe0c0861f5425b92936b4727a65013bb6884f0dcaefa661e019087b6ff24e39c11943b095ea1c49c90041c0b42c82345cb14b5ed7f254d72e2373dce68594ded07a7b699efa62963e0a4af7e264b71135b13168671ad5b1b4b56279215aba67e4337e78323413c3ee02082ab040132e3a75f2cf4291bacf49d29428407ade7e09342d7a2a4eb2d11d438a4c4345529239b8a3e33eecbfd8064881d47173f14928a4c21dec6812a81569a858958262a6cfb7940e6fa083404ab5644d9c38f5c73b879630c279555fde23584dbdbc5e2d3e04d44dde387dde81e15c75616cce8f98d639c3c3e974beb60df66ba8d689ecbde76292defcb26cfee2b05ca20ae546855afc8c9228731ef309a0ff88398312da99ff3ad53199c2a6bb6d453753a35b98b14c1642a2f62ded11a616d201355ace67411a9085f50856b65e895e14a8e3347c020ed99d6fcab346e8de71daaf98aac9e063a648661094b92cb15530170cd512e8028bdab2e10d7356e26cdedaf517fcae2e7869cf2eb649bef9b76038310a87021ebd1050abf16964a40a174e186b3212b452d68545e01f4bdeed164a700fd744f3f5b66825c643c2ed7f0985f84cdc8864a7288f9b229f3744c9059ab846d3c9716e5ea5de3b3d2a541d108c2aae1ef17626b54718b2b9469d2a3cbb5f774e69a587dbb17f7169fc0577694112e767e339e7a559169de11b755aa76bc866262d4c65cd5422f50ed4a22e3ba9a305712ab0fa7fc6da30705ab8eb36da68b57f417a0f5c94e8e60f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a47b104aa943c27091ccc743806804d0853ec5c4b26865fe81fae699a474092d","proof":"f8f16aea92f77199fddc4d3681f4d98f13b6a9f215b2d1e18f6c78cd6a6a824ed2077e6617c2834d694e28f3a40117b0e3eb02cd58eb9636b2f3e140bd60337d9069bce9919e4ca2a698475d9b9986e433095ae0daada421679f9c2752ec8f73fad885320f7f89c22197d76214cc13b2480b73944f3f48dab1d70887a203f30efb17876e2846abd436c7688ddd63b815caf9eda592af0b737dff5fb10d83120b73f9fc8756c38dbeba5d9987ff599cdaf5dd18f0e748217c241794bf30158501a4ee88794bb30c42fee0e814d00821f17ff4a5e75e7b23ea573dc8423e3c0e002003845973cbb6fce0247b88ff537d97eed11c30d140a529faae66c21184094024651dadcbc5000609f8bc2efdcf680f4e39ef3e2ab33b269d3b74ebe670ea16b2b74be995159157df74c8c013102a66bcbccd6f69364acced09632ff622762be650a02693b034c203fb2e849e49dcc123e003ebdbd3e5655332c8ce94e3656688ad6eb77a1daeffec6b0f4354704ad3058e56045f0ec4ca14f00d34eb2aac4e420b9ae841705a46746320dc26ad85da5bc52758ef48074f61d14acb653af5347ed676073ca27c6b96fdd2c6f12697cdd5bec658f79e60207717c74b4c0bc41f30ad03b0360f1b05d1660bda088ed250b9bd09541e4c0760eb3ebd03125c61194c8ebb27593010844408bb5fd4da1726395a74a51c383f0d1c63a6459564cc38ee8a5e7f1e5ac489ee4ba2acc2eeb6038df3d26b4100f2b26855ba5f56eb6272da47b4cb9d586afbbfc97602720f2f757832653706f3824d589406b1ade8786734d37c6e1ea4295f6d98984760741b71160faad3da1b61abf9bb46a23026232ecd2944034dbd214499b4316ced5bdd2635f32da8f5d4432538e970fb2c62560f9beb451aeeeb6c708394c09ffee3a902161264077388e916ccf3f33fee345f09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"961fa140897ae2990dedd5e80c89b63d73db245f49a931f7823e99e4ddaf1321","proof":"547ac3394a5c0cbe618e93547c2a49bb04b75a5ed4aeab9ddb3f07e0db91de6a621235ac5dbf059b71be024e100642f5fbb05ba23f1415077cd500315112a658424889880a3272169d12b2f1c8ab9723aac44916edf1f357db669f40229e981094b6da4672edbaa5f37fdfc73207e01a2d9d3c6cb757d9d33ad3cf091dd9d647c29667c1aa20307b1a42d0dc2ea0e96deff985ce39176f7b73f6f020b7c7b5050220340e4078ba3de4bac7b6de70dcbf00fc1ce6e29f27a5518f9252fc2cb60d80f829d5f6a54cfc15a34a3208f6de18304ed1521a6764400cc227ca9d2e1a06f069ebdf6ea1fa57d9a1abe9ec168ad7ce4d92dc6265780995e0a1711fe2ec190ac3da44e16626e9a41ccfbc3853620e979a4d9afad0a1ddbedd9b74e270b90b8602dfe4121d5f224ccf83717418cc3dbc1eef7ba3991f876cfe78afcdf92645a682aabfe819daf6f9544c618cfba60488a29bc71c6b6f8bdbc9b88f852beb29e6c13dc1367880f95c6d4d41e1c7002b0fad74fbe6a9a909e324d9d18c5dd16ba8b2948b49a2177b2fbdae5a677dfdee7ceaf6cf60e58585756c57756f59c05002f8adffdfe2f12c2f7c6c096674d2f682e572820ac886d2987d15bd65f4c04f948ebb54f6ab0cc7ed5de6bf7eba49c344aedee7323143823c4a969d21b8d4627ed6321c3bc1065de625d857878bf51e95d1f9cc6d7b0fed70a238cfe00b2620466c51dd5ce4b696cd916231780cbd052b85967f95f859a726e3d9c772f5fb158a8a179d514197f9f21a72d85e37c29773245a65edf04110d78360e6b002255dfe92d7fc5356237bed494d7bac02ff6cdc23bb60bf42d1d3b8721ff260fd996094b4aa2d181bbc3791aa768931bf643fd88a96ec411b3a9a5f63437d9b6f7a036be9d524ad3652172db40adc07b9f95864dcadd1f6edd043a6ac57ce7a9fa30e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bee540329e29a16bd1cf43c1ff7adaa05df6f7c1410371431dcc104cdcebe80e","proof":"de5521a3f597b10163a98a7df25481b9d6427aa3a45f222e7c06b8147398603fc4d92070dbe5c5d2da0c8cbc12d6d037f5bf9b6cf63ec8fd000574741fa2065608fd94ad77d3d7ad284a6663902b863aec4810a9a03192eb416c0bf6fe29a93ba050546ad12f69455e16d0c45df8a6f8ece00f535ea8ef175d0fce68478b885860ae7ae90ddf5b96db649c8245fb0618731111848e35024ac79ea5ce5735240482542c8b3563ca3a4b1275f2714c0b8374d538cc5278fb7d435c0b81f2ed21026e088e77846a765bcc08191f7d92f781a2bc0facb73c70120dc593dbe2fccd05c8aeeb53ad57e618780c37447581e9a084c20a8215dec654b43f84f68863c7496872ced1365d5173f519750aa69245c85efddfbf51ede967eae0d18a5e66b83ebaa9137ac6f5e2464ea807336d53d279068e80d1b88ba001e5ee6645abac66145a834ae72fadd11ee80826e1563820da5c13db39be1a09df30102b2a4317cd5ad4fde80708029a5c01b6355c4a75f5d4b38917a95bbe8556ae8f34a4afa3c57c281e11796187351061afb38c1b282d9a59470e0bd60d5c3c93c5aeb66b146b2b9022197249235a8bd12967cb902c2f8bd6abd6ceb069fbffd985ff7a2535a5246a5be4674047cd9a9b3bbab5f83f285db0f194ffc134df48419cedad022eee484674080a05b51dc3e50c7d720a82336a26f61a7d4307078440d9dc2bb99a3672ecf038aecaba349fdc1945d507f69aa7f25fee0d138633a0f14ad4e241f814719e86054da00c349cf403af9cce4c143d0935617e545db8aaccfa806217dbd076ae23f9d7847eb3935e6ef4865eb991a83ac032eb0b375ab069d1c8c25632ab422090e5541f048cf6b57fe746c6ae8b272c7d92f6d40232a8f0f6bc4d97dfdd013c2d39e903500de17218dafa929f50eac075ef5f807827b7f107ac6913b8620c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7a682371c608df5ab8ea5e0485e7766f3bc5714b8961f8e64456fddeb374d55c","proof":"5250343078e028ff853be9b6797ca0c2e3747173b5f60e7d7a0cef85126a3d027c2a4ce43492d209c01f4199e0ae086ac89cd3b13bb5519a54a3636b01d8897d746b4e789fcd99e9eba26994beac70cb8a2b53ec9a48d892d308e4a770b69d280001178b86af3328b69769f38d6a7b4dfe999e24a7d2825c43b7d566228d7e30631086635f27d7df32b53b9f0de01829b5b9d229ed8e555dbf2a86a7956be8037c18f14c181e6e485d0cc0b1774aa2bf8c48642a44b250fb81453cfb229a8d04d8d81aeee966dcc02dc425efdda9c752505369b7fd1aeec5e8aaa8ada8f53d09e05993b7888c752e67d7b2c062d056636f382c97d2fc0798c600c2956026df79e6ab4f415378f607e46989ea187c682ac88790a63e76a860519d012fa077496b209835f11acf1c861d214323cbc282ea72ee297353ad6cab5ec199ec354acd52a0a1a90379f8c7cef8665010072d8a487946ba85185d393a11e78c1a64ad6842deecc26ca4851b730cc52a78b7eb2cec0feb2a8b67aa48559523ea32eb86050e540f0d2dc30adc4ca605780c3f6e150e6c463879f6ca254b422d3e1d16e38136bc41717b28a292de2bd27c77b6f5286c02660c18c2d9c6d242315865d845b626f2d33cf16c4361f61456c5b4710af5c7a702b90d6cd2f7a0e9d19b56e2e9af1ed8d2e404c7ffecd857dc8f1464d51c0886fd2ff47749d87145ca612a3636537d3e0cd3f845f436749c5f4a926a56afab99b815bc4b42d76c4dcd28fa1e24da5cf0739c2c0dbdab30d616bbc61e4d0849c8588bfdf72b57189c0b9eb95c87425388745ac771860c20873384759fa612c7b53502efc0da5e87120293179a030705860160a8369e97d2aa134b474edc839271cbf4c7250bb4c506a63b6af039500a3294cf83aeafa0dbbd0c7d839f26a380e66fb8edccc96f19552accfd89005f0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a0e9d74448bb55125de7d76027f77b49e60e74aa2b080089e7299fedca62c007","proof":"7edbbff41450b7a6619304c374c95b1ce3356c8ff777d86e3c720ff1cc371251d414c03a672b34f649cd5ea37a37f4851484e77bf9c05af5cb15f1ac6fceb23a6818ada1f720598fe6703844d539c8a6159d09ccbb51155c0bd05eda06cab70aa28ffe488a36b33bc84bb02e590dd79ac4f3ee7ff8a58c610d80e98a790cf4443ba021c118f54a1f104278af612f13602791136d921d0680f573de7d6cf0f30d30bd95e54fe29124e30010b6c2b550cd1aab3bebf8799b2ef9a85fb49920b30a6c483657c9bdc93fb423e5115d81d7874c4f60c23837843d458da85c9e37ff08b47416b1f06f5565bee1d97e4e744ccbf264910b9e0bfefa53c1c84fdab2540c06ef927c881bd50484358497042a20a2edbb9ec3bf4519494f03e1ff91e6ba423873fada5c94a40065ca84af47d740c8fbfe4599d9abe8c422123733d3421814025dd4dfb2cdc5d42f7aad9dfe3f09d05cf822c9dedecaec98f8a7b94aaa086290e38a1eb6c5122c11fac03ab135c4c24cf1853f2415ae10aa288273ba58824b2e3434c00c09089af649f2d1c8c3d6e7f202fb8302a3866e552c56ebc4cdd9401017ec3c22577ae8bb426a61b2c1068b179de764115a8aed255f8cab8bf6423bf6be63d4bd26dea677050656fc791051312859e5d30a43e1f9e6f8f123f42a65d241fd6bb263ec00e83fee0458021e5ad90925b92a95e848f3d841c22e892d6ec6f3e2a352eee116c42f18ff489a5e5ef2442a9170a1d5bc6059c24945fad77492b0295ecc956e186f257a98f98c0546117f0ac56bea92d64150c6b8fdb20c05d6ca3dccf3a98fc74253da6421ba60b4e147fcf962f4492fe731a0d887569036c1a06805db1c54b1743bd2fb4ca71993190f7ebc0ecb7b41412c8484c30cbf04f3bbfbca0113d67a25a88db4b18f7457107cfd75a224c735f1a60122e330550d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1c9337e8d569501d90b4682d43d1361319f6271fab1751cea06cf4a26e44ed54","proof":"32f07fd481f3dd6fbda07c00d38e1baf1bb729786e6e4374bd6f6b795c659f709cf1e2bfcced78652c2c647ac84f88ceaa820b850c059051f90006195479c940e8e730b99b3162003e9e85192ad3061e91d16d99a0c3ca7d9251ecbb9e71097c1e75dd335e94ffc0673983c9cc633d922d39f424a48c59897ce4a07c56848347d20aeac0d6a4a16953b31c90a1fc2d3eb9f09710bc2bc38324b15469531f0608d65c01772a8eaeb19da1385de094c24cb6d652e725794efffe7186943dc6fc00edb3499d2d863d9f78afb4bfc32e7be6058fc36c7755fd352470862e31ba8b0958cd912442003016e424ac46cee3447181b4e433954b994dcab1d0c989c890315215eb3ee4469fc75d1a125f844906d7d4b9969654ae55ddcf9001e0956f7b708e08c86fd046fd7fcfc90b80209046c91b0fc11a5b7fd980714d37fb3821826f965069557417d68178abc6ea4030f5694991a4e68f668dbc2130339980597405607a1bc3acac056bfa71cbbd5add365babc2d26bd6d52ef9d5c3f9aad457ff4300dc5c202fe9ea3887cc258c0a3a98bf015234a4afe985e74d291dca260b1b39824be846db19977afe0ee2f275e98550728871aa45d6a4a62e2f67a2c915a70d267ccded8bcdfe8b8b8df3414b6ddfbb0fdf991d73e31fc31f5b9514a062c2064e5da85369c912eb4940fc9a9f1808dc926238495140025cfed921730597e322ca16cded72fc9c055eaeebead82fe5e52d8978957595c304e81a3328e2f6a62dfef66d9739aefba0db8b4f7735bebd3cfa65c03230a8bd8f4765647b2e012d4fe286b998af6bcc7e6593f87622bdfdcbf500b8c330f57dbd27241d0eedc92d24b973bc3976e488b8697fc905e3445f3ec591e5253105606a4d9d3658f0d99705c827902c931a30fd53bd6c4fb2877a0b2b4b682e8f215bd1b49eae11c6691d00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e6e5337bf013104c222965d914b99bd7fe9ca03ef6ca8fdfa7950d540c096d0d","proof":"26aa0f09569e224b46cb50c151b26f762b0622ea990a008ef209d05c3f13a3217c9d67382796290c9fa5ff63ce1c058cbe0231acb29597120555f26a59b30910f040ba4cc8b0bd14348264d524326aa388fa45f78810aa13e77a65760a78620492b039d0440c438d0f3bf33b85e1ae8235947e98025f7a1b2d09c6036fc07d624a8b09991ab623ced4888706e00cf86bd0090dbf70c1d6cff09c7aeee504290768f73059f45c3050eefa86ea10a21c73bbdb28a6acddd5ba84b1b1c0145fd00d8621f0c2ce19b9f69cc8a05164e3eb89d98671ed3e53b1f497bf113a7d402a0f10d5c25a88789e5fe1ace76c58cf172e67aee36701bf8b4d2933b1c035cba072d888585d966d1335a59537a0b71420715095b8912f89517a2f818fb06a622b30f49518c90863176aecdd9914614ca45156b183156aed94f660d74e533445200dd831c3590d91c5bc3374a5fe25c6fbba45b3f112b1f07160f8545c772d649105c89cb2a979c424045f667f4321e8ac686bfe25b89d7d0f39d5c7019c4f341b6302d33afce126b8bcb4f78542ade3df5c38459187b8556251a0c02c6010cb4a1938e374b744741abfe650132057107a05dae49e1095055da27db1a830bbb7c830364772af99f4b70edbbf8c44c0bc5291e1a4ba6c53e0d3fff4ad92ea4c02af7eb44e5a66edcbbf2be9702f12c2c565d1e687f144a6e71d291a4428336c522376863753815d21b75be1aeb6b399974f6f25e9b0fbf574942b384c896f8566c05684752596ed1aaad9f4a4e122d94f1378e5a1ffa97f84bcd6852ed673d277280ca2c7d8f60fc59213819ed526d5efc7a348868b2fed794594c0f9b1211bec0b11322d4e9fc77aa1ca8aae5f6b644f90677f90f92998c4f92992fb9f31b0a48d0e1c22cfdd9ff36c9fd7af9e9fcc1a83ffc45b6b42db68f4a2b9b89cfcf5850505"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"884ee7e177b9fe9132338113700b54f83b20fa2d55a4185d53aced51df0ba368","proof":"063c4a33fb49820e9d105b0e003544237c7bf7b15a0c119b403b653c628acc13720e712d207a0cae600292dae145a8fc9263dd48197c7433ca535cdbed74680fdccbf5923ea4e6a612526d3b2d23a99e2c36566d5218b4b34e6b161f8720fd60ac49e1299493238f859f87499987f295d419344d2f8650b08e3bc292bb546e6c6bae2acd8082e7be77a54f230993e2db40860fd46915c379ebb1eeb698cf3a03b82949ac7babdee7bfe023b0d54042b945b4b397ea5888ceab758a57c48b060050f3f6b0f7468479a1c3d6a96e7bc051b7d9eb1838eda858a1d5c31b0c179c060cbe04fbebc205b068c4dcbd65f10f037f6cc0c6c59e865f692a09ca5c8c8a085435b119bdbb0602c3bb6f6f29587a9322bbfd68050d21962b1816a2caf11e4f222bc07abd39854321ce118af81c66d2addad9af0d725b47312b8e9176f0d1704e47371e216812ccd0e469bd546921bce90e4efdbb1ffa5f8c70441ebcee4639e6b0ade607d6e3fb2347737c196a58d66a37fc66e26b7e2095c60e841f2f1b23e648aeddb954615f82c49b724de671ae0b7f9dffcd5629206dc69da265fb71130ea3e69a24ef5b1d8341da5ea8dd29a3f351a013361c69fed7b37e469c74ac56ac4a2f27e6bbb6a8e54eca512234c65add142c1abb4d8d8c301d9785e03c3a6e6e2ebb88c2c591ca63c42e241f89662e8ece0a248a0af855a14477644064d13fc4910abf39318cee6f247a7d28c3bc8460ea1c146afb30f79ae42badbcba6919ccf685ac65e1a12c05619978ca8fda840847d07b60fb09c7c85492616c40aa62d026209d475e6272586372c08b50f82ad4089455f48831056c41150ae5bc56485bac9878b3790c519059a258bfcbc60ac19204e369099b4cb3ba547c902c6d0ceb5f4324914f07ffc93bffd20d88a288c276b49bba8aeff212b484bad972e906"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f43a5f04970b7697a794fda7bc38818f1021ef38c44f731b9367914ec549f80c","proof":"2aace10894223b9014edadfcdf8948cc27c8ab4d968f222105a1b4f98b9ea338f202a1c4169c7f8852f2438cb11ff91981b83101c5056272a43c288e7e685d75a0c144a7bbe6c1b9de104e20f9ebe92977b42502c0e92a24d453523ed7b194798ee5756c411d5813cc0c60fd32ea026a6856ede211fb5c8cad4a8f66d0827a7e7258d36525cb3558118637e0f9c781610a4cd4e075d9d5a35ab0ce0c7584bd05647e29a640e465b71b8ac170cd41effc28974df879ae4f7af6946d14db886b0a79036c62e2653eb0856f608fcb997ba2c9f1ceedc66e6de1eb9874fb59ce3003f49ecbf809e76d4dbd17ef85a7cebce4638a87c37d2205ac702bcd6e02211e216025248df143ea94063efefb3e132eed939cb89960d742157b062b1b7cd04c367ec763a3966c8c7f0ec2f650d7bf1bb66bd9a8f1fedad858c56f3d4ea1e92e01209400c6a7f60f5514f7e949b5b125c0d4291fbbe1b42d4eebcb8bdc9442ed49cafa3df1fc959b1d795008c18c2862164659d9e4a873343f40f490bd2750a56754c86849c757bde15e73fd51133d4d52470b6694610e712965a819ce4a6a0d543a848728f73df286cd65f39e8b3fa8422735de3680a226b01f4e9e4af398e0394c390dcd31e4caf1da9f8077cbb35c9ebe810f822455ff491bcddd230de45d5ec483e11a13b5bc09624ae472937580dff1fbdf813382654afa6698fa1900a118ae4b994f07990aa8864f48e9f609e0e990a716d2f50e4b7a4641d40cd718c1295406b13e41f2493c68a86adc05cbe27b8bec246f21d68016f62460626fe23827f42b2fa1e9ceb43c6e4db799625ca1617a52d62e3488f7b123e49b95f9ba1d306e7a134fc11be5eb2f258f1c3ba1fcfe2425069c6a2b5c978b7be6cffef7d10ca233118b3ae37d7d0d891908004ec1e00528aae3556f2e2bfcfa6a4fc9abf400"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"de7e21f5782cf970b87ab7f680edd802a04479c7fb827682613d7144ce5a364a","proof":"c4f0c3a13a2a0e0bba5c7617b1f5b16c70272b71d50fb6fb03196b7998907066be619623946f3bc0d951ec26b38858b7288ace91cb9329363902c8773f0e8934c49c0adb22c94a0d75a13d60038314fcc89de231a6377be8564b75060833464c340460b34459abe2ee76e2a3e2717ab98d7f494023365040b4fe27ac6f8376761895265182c8bbe581af2df98633a68389516c2beba9f9c14161509e4075de087c9b31d44de6fcb6a7487b58e18364252b7105b9f407a52fd69d99edd9d1820f5de9b69d9d6b8caca16763d7646b71b1490befbd93337d69b4b6f5bfe052d109c8536156d4fe5c417e71d8d35736028b4e3fbadaba552805eff40ca44b83e063d8cab9add092c874cddc8cb0ce3cfb6328b843d463b9a0a808dda0b14ab6d252726a9facae31fac7f1f21c29cf4d7346f0514ff7d96fdb8e6c33b2c62a346e329ee188fec8e426825ca3ae1863576d3ae4b196c36ee6c89d97086939d1c4c97e1e0faa7143a83f49fdafc493faf06ababc1f373f4d94cc4aaeb30485e8bc4172aa2a8d875d30670d0448b0cd0e117e8b9664c1d19446db7cd6718db4e69f557be2c81965e942f32a89a7b730837b7ddd75dc9d262fea422f1f28bc0d9d02715b88db542950583534d7047182f94bad1ca405d67cd271b5588a01d6f26ba09851444663c2a1e9ca63725c35f9f8030a472d06cd3bf7a640c9b7e828d8236f707424dfd16e67a7dca50b0ae13c1881838d998bf6120e99f427ff619c37adebd53838532adf6e6fd69b770d585f7ed7834780dc859e2b63044ecc299c67ff26fe3e22f577e7da851f0df39095c9fcd82f7b58f5a39d4bc26357dbb845e2c9bffa121ed7689d9cd173d115d99abced654633213f10f2cae208c8e0aba4e0973a9f07831e6b3d95964def45c2ec8592006c27a1c7dde649df97f0b52d0c10d65c740f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7e766a625161fb8deb2f069904d72c7510d6a5e1c015fbde268f99a099381b47","proof":"0a873071d8824ee1c41575303d2a64f6a628a84b020d8134e8a60d6c071d9a7a0a773d15f3d7d142bd45b41b7c395cb8c9d28a2788038ebe707336871549b5208e1f6745211f9837b084cb58b29f59e2aebdf2b2c0421db59cdcc89aa216fc58181fee0e780ccd21f4758d7569595068fb7c131761d746d11699d6452860010b140a09a7e8a7edfb1cc7379166c3774e73875345f7b8289dd47f576aa7cc350f7803cead23e71089d8f0a09f9fda7e241cabb85f8c2c6a70c93609aeee3ef605c10424d0392aa302116aabe640ffd82edcfc060144c1163ce82285f3d5a8560cde988d1bc765f5dec8eac90dfab3f91ba3ed3a2ccf098a5a2caeefd490461d5b66ad24053714ceb1d0720f3fa17b64e9a62317b78f52d0371e0493848bf6382006d5002cd5e854ad07c27f60acbaa41040c2a330f214a45ef8d1285020d9ba6c0649cd7f8311d26f5d2d0fc98600ac4918e779459d7e5c52bbf7106c6e92ed682cb8c0cdcafa43affd4ee19558265462e7d6d1f92d21f7acef4feeb61f99db1b62dc1daf7fbff4efa5cb2ac01bc6b6bd3e52d543d7b28b70bf0271a042ec5077d045b49836e68211559664788e79a9343b6ecfafc4b4d46f2a9a5efdbe79612b90b1eb5685cc5d1bba8b7486206184b3585587f1e083472889c78fbf3d11cc7af458fda15b9db7cb14965b764ca6a09ce55a4522fcf467df4bbe7b98eade3513bc21ec9c23bac2fcafaad5a0405bfd01e21a4b971d9c795b127703088d93a1381ed90f593b00f71f9d1f801c7b6f32f065aa9155eaecfc28929ac82fb2a20f7dd2e0e909f5dd3bbb958d962ff169c64224bc62dc9c21c70a468bfd6f875bfb6d44a86ee17247cd58d3fd2c2de3c3c1d2b62094b87ad401c3448c779274d73f0ca012418b4e3b90a504b9e85b99e894956576441272d413cc9941cec56e207d04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"18fd2cd6eaacd53d72df170331f79f54c8bb48c919c3781493a32531f2cff50f","proof":"805a2bd16f07a777212cba2b24c6b870b9658d054c4cf895ed8af0d6063f342022dadfd3a1513edc8496d5f2ce04c59282f6a60095aab31db7fbf6e06c76d25e12e3bc58b6bccc8dd74977a8a38a9ef2164771420862e6b41edd54115ff33512e2926fb537e35e9b9869c24e240b7db24303cf462a8478962950f4dd3e49be2024a701c55103e4821d8dc6dd88af980b0e040c78207cbdb177695ae0d455e803a9448dccc75c7ad0339a71e6b6d18abe7f61c14526cacb1e983265e2dd6e8e06ee3e7d71a76c96dedeec22238bdb8b82b435ef110965d71a427aea9fd8d3c10c8e2f9bb1bf816289888f69b10b2a2d33bc144752f939b61dba8600ffb92db938ce4fb102f29fb6335097d9119494133d884041593e521e0df5247e574b37774996494d5011e096d29ae1ef9e04799f8e1d51eaba16b0f0e69da117ff5aba41146c1cc7959f7691cc3a96a64a2e1d9ce276c20003408381c9cb7455b1c69dec75986e95f4358221b1b0025d27e3293cd21d86052647483afcc0b19067d799e03d9694a0699ba35e1c22530e6a7a7e82e4a5de9e556c6a12f0c47e29296e922d14bc1b2253af01856a888eede250a86ac62dd61d464a25d9aaa9848aa8dfe4f0077cf90a25bd7bba28de561626552c731bfbdfd62319d1e1a4f6777af3c373983eeef4314d17d9cc0622d53ae48df530cb4bfcf48dc7e8d3c5f6297863eef64720f053c17a55f09e41e0dfdd6edd2277fe3e486997effdff9557445184d436dc1b7cedc16b3fcf007942dcf19effe594b8bdf7b472cda65e453afc90ff7be7346cb0b3e7cfcf062abcd690e303f6e2dc83ec00803d95824cc721dd00c822acd0106cabccaa0e5cea6097fc5fc4321bb9d591424d13e90c715b4c82902a6183a60ac2d68bade3e51f2465215b6708fc2053eec8bc80dac870393909915a660c9a08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2aee2cb310aef9ae8cc3734df28267e1af34db80802b5b4a8aaf915e7ace8b2f","proof":"c8e0b7484de5693516f76e7dde848d400a308f194139174572e5bfed5867af48f83e105855bdd5d75e1b5e75ac83b39bfe4e2d50eeb9b9aa7acbe5fd41f57900b0dde7a30b378fe5be6dbcaffd2d9c4c99475118823fd5ac8ac06e54976dc669cc8ee1fe748c1c63181ba950e2628ca9cf5855aa01b8b75893df5da3c5965770c27e518d34f6c224ebc8da714ec18c35f483f611673b1d8beb283ce76d9a9e0312118fdbbbafaded4eb9a58cc3ae72b8914c0ba72c62bd6b08b6d655c432fc002fde332a914f3ae9fdd726d78e4aee6ecde4def42078dfbed0f7f7a7946b7b0a26d19d637543d6e617eb2a4fdb09faab3b499a496c00b741966fd02fa9300b7352c7ac9d818aa05c1d6a09eda22edaa4b2431a4a0b7e51a35cde00ab02ae2a38424f39eb9213c4c7c18b4a2f1929d8e77b77b9b2ea7c38efab5db8b9b9e42f37ec0ad839af8b6c801d700c69e25018746da04efb34b2a97d721c720464d2d83862e207bbac71460f31a0b97efb4417c063eac74f204cf33a91abb01f2f918b3dcadfd4729e00f9eb8d4d0b8df088b47324b50b4ab1970f207baf8c6b3fd70104800d22dcc56dd481fd1b5507ac9cc9dccd1d57ccdf8b98f77abe06be5a8a66553ef8674e1817e43f831b21b9e3721ae0d61a863eb4b79983733cdf48753b423ed23cca85dbef495a303dbf3900175096bf54939e134907ea651705bc03381b679403a301c0874aea888ecda533668fe9be44a9c06737b7db81b61ff9fbd00242d2a58b9b08fff2ea8373b85f4de74865dffef288f726087e41658a7982508f178cca43da4bbd4ff813adf5b97dab47b02efe7ac5af1beec002f707bc93a178381c8105ec9548a24f84030c1408ed4e05e0c8862374d05a15e2735f4275a25d07ecf0f25640db157d8906f2dbac08736cd2a1907aed3476508913a1c962e41d0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8af983ea056d65a2a9bae89fdcd12aa3514f57a713206fdab986e562ce9d4b65","proof":"d6327d66fe9b9c4f501e91f2e33188401e874e8cd6fe1ba60503053d31eaae4cb2a75e7da8506875490e1c255a47a282cd5230e71bc3943f76fde9e566969370aa642a04b4a0664fad65bf05aa9688e8931bdbb59bc78f661930d5d1246f4928fcaa51d39f7ff477304183dd5e5a57cc267ad92f984fbb21c5a84ff0e2d91475e643a3d1b4dc8178080eec6d0b0fb01866c7b49e91aeafa8e251a0a917678d0e39f60c2b027ab493b84cf56cb30b8a32c24a33e8f0d21081bb54cb37c6d1560c8f1ff226b3dbfbff98c4dda84771add090ecdc469d115275f3cafe375b91e505ecb3521859c7fe19c862ff48898b859d5a2a16b5e94526083033b08f1ce72711aa713b688228897b33969c3d5b5949a4b1f241616956aa8cfd017dd21173162346579fbdf91c3df10a58133158f70b6a1e305ad2abc8a9c1849c5d07b0cdd856b271f808e9a396e1fe66688a949fa61d950bf8ce29df361ce5774bd457000d71d68cf756f89b92249d02e289077a0c8d13b004113c36e43834103f0e9276835b785e9fb8e8578683ee4c37c5e6ea8cd2cc83a817512d862d6aed3ca5ac367f53585e844522dbbff05e0838c7c8d3a7a728847e07a0902b6e68a2c4e6c3cc474548632bedab7dbbc6a5322b43445296c4a4c565ec13e319bdcd1089617bd3bf2bf2a03e6bce46207b7cb54e42dedf151a1a4ca7585a8b370df703203fb88ac978f8c79fdd17966aaa1493f43c71fa347362fa8a8ef5f96ac2af0a63aa7a50112412d32af705f130331c37d08358db1f3a3d01975ea3fc86e51155c87ff468e9065c071d85f5b69040c379e8605bf1504516128d9aff17d459f5c9b76542abe10c5a57798c97efc057f49ddead0962bfa81ca5aee666294ea5386110e3a8915100dce8507310977da97ef2b5d462b4e461657c59041e723889cf63ab9689d16e05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1cd663ed0063f34afd88ac2d211e427ae303f1423ca1b88715b25a39a78a6968","proof":"d654787bc7a158bfe9e4e7177a255bf93efcea7be8739334472d19560d9a434ea010e8338a4469719fe5a8b552a2797e3b07e8c9026c6874a52d73e262f13c00369804f674716edc5c3cf6b826d3e1ac1fcf22f282cbd186a4fa31bad73c205b7275d2b70d62edda636b36ab8b535a591ec6d0d3201cbe06240d5192ba11741bc54fa8651d697271fc46e60b10bd1aef0016227570c7e5ad4fd3cd0231518d035e5088da154e4b261b006b5682b7d2ed63ba384b75356f2054bb89254b45200a077d55114a3fafbcd3f379ad4e5d84a89c38358fbe1a102e7fb2e1800317f705400a5ef86674ea670f69e63e2a4d58c25d974663ec6fea08e7cff9659dcba943de0ff3b0644a1ee3b65a99809714fc9837038e7614d4d45e9e66f5035e94e8781c2e8e0c46c8ea2f382c0c23b8e9aad2f03a313470f512fae6f139fe1015c86aceb980ac297a1c2e5c2372e25c2ce13f0714d47c4e1b5700ff7af5419812ee147469337c2f8d267580f8afc3a4ba066653af2795bd0a4b15b44143d8c5e46316ac77bb94d079843fc189fb0dd9d6dc6793b3b903d0ee9caf3a7ee1d1ca11507a2667f745952fc11f470ce782144c6c00cb2c34c362bc18bf38fa9e383388ff298aba1428f858e95be2ea8d869e69df3e643d2e3ed2a86b4c8c5ae6b5b42688242ee3641944b39e0b4e1e0549b9b578e20f6233885d7aed52c7141ab742320a66d6fac3820d8cff454ca226db75680f96c1d68771b1e1a9e69b0cdb4f6a9cdb1ddcc019e4f3b84df699278b92b449b7dbe88b55f143518742e7660f308af9984d342943d5cc03947b753fa4bae6663d38824800f82469143b22c3cf761669092ed29d223d7e3f56800ff54aeb04b5f85dae040550445b8f92ca3dc0c10f484e0aef84c1a1487417ae8cdd4e65ea0263af7a97c98062ca409562c6fba648a23a0a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"628dd5e746809a4ce0555dedafd58672b2ac722d0c59dbf681045e0ee560250a","proof":"de8dd8a400cd879174b12fa219fa68e670a8571c533cad7bf4776612baa14a0558f1e6df75440118adda9fadce5959393b2bea4a215455efa2bf38ba21b8b45eca2fbc7cbcd20e96e17717c909e47fb78dfb13d3869d1f8acc295a6aa10e697a306cbe6677ec6731e4508dbb3e5004cc81a7bb95d1a868dd0a9280c33f47de043238cd4a677a2e97f24d69c7267072171130b950380751634563f324e4e72e01249916a09c6a82c89d6bbc0b4d1d7b27fd5093fbe848bb221909cb659ef179093d35e7595a4ec38d73aeee3880ffbb234f71afeda2f7a8ec847893fd6661c705dad60d7624b29d539acfbdbe63e9f4ebe57f6198ec558977aa522ef71fde9e0ef2232911970be6668557fc1bcf46dfedb23570ed1de42a08184c0d9087516b540c9f68b397f84d14e711c67a524e224072613ba82ff1bf27fd6b43af8730236e725f54fd8d6e1c1d7f37cd21450e5b8922b841855e7f93123bfcb1d90b5cee40b023bf391fe953937fe9a81b2be5f57af4085833ab8265e53db995cce92887416e2a24dbff9a28695bfeb69f159a175c83454bddcb76b22e719c8abc45d4a0289a9182540f219cbc555f3348ed4358013a713e7680fd7d5b77b1cd83b113ef1c2c26ec867f8645f894a41f5cf4fa818fd8678dff59ed7ba0c6a1b6ad704e272c82276e41af157484900d255e73e5e78dc8c14431b51051e75f07679a75cc2441d21843b62ac761ee55d615491e6c680352fdd16d11684d31fc962c8da3c4b15fc6e0a6912131d5999384dbae7f165b3ff798d947cc3136e6f0c24dd89ea7677baad8faf150703208e4990e8ad1ee417d7b9af395dd515aba27f6f2aa550dfa093a8c1c71e794a6033a7e155b96671905297f7cca6881891c050b6023e997e8080169a37dbf11074070d33cc2e37a8e3d60d202923033681e7241ce9647f4fb01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f2c24133d40313a06f43cdbff13f03efa39885120fe762c46c90543b40fe987a","proof":"ccb3e984004ea6032e04db8fbdb04ac1c580f5c1ecea57a0517dc9fd7cad6c241a10777bb3b65e9035915fe6ffa3cd8c83ea82962f87f208ae9a1526bd9e9d1ed06f209c1202623fc99da2dad69be514c726317b8944f61e8f863fb938be127fca143a4cd7599c673db6df373c3cf6b3bd1e1abcde2e30bfd67167bcdcb0950a1289345a500226b5e8507852ca251edb37e7941f94ecff289001671d42de0f0397f7419bf9ae9471ee3c9dcafe3482601a9dc71a08dd3802906a754cdcd86b01eb70e895356395ba150cdf02c9691309171e72e462421c350aaf7845fdae100500742d88f894d5e58c731999107efabe2c40b5ea9e7aee184f1d08115bf1ef5120da44fea6947da37af10ad37540686fe5748d3b6018d36ac84b4ec09cae741d60a0b7bc89a8591931e8dce59d5d37bd3e6bb0256d7df151948ee0ce1e55fb275ade2035d1e026c1319f6200cd4c092a40b0cf7ab0857e6b4f8201475932c77adab93a5c8de365290582ca6235714a526bedf1e8bc234ffeb22dc422774de868e8db75b5bc093d17f134a67b43f877e6a9eba2f1ebc50aff5a64e50aad75174362243d0a484ea0c2d50a0a4845b8982448bb7c279a2c6a1f2b8b0c8d8fda0f3f98722ece1d5e81d66f0eb828eb8f7497c3b9d825af5d7f4a97bdb53e72ac946c9c49df03e4680ac8e59d933ee5b6088271ba5601f0521e1ff9f0164b5703ff54d404975e9707c6b78dbd368c2e5fb884624cecafadae9d1b006a97a37d962547e886909ee70a989d35e02ab400d561cbcc8cce50817a9db3fd70592dfab09348acc37b67fcaf050888bc2b8dc11fca3f6907cd8e725742e9b7673f50b6c6f8088cc510796b5500192b56bbc6593878e93e0f8cadf87cdce14495e7c5ca7a8c0a7d33706d3fbc1d3792275a0784005709ca725984a9f6c889f7bac178707e3600"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d8b04ecee8658205a1c1a50fa788a38e2545eb2270e7f7bc64a86132d9705c41","proof":"4224d393787d87afe6a3325613d63d8c50f2ad96f967de15f65daac213da5e297cc8eb2bf4e1a85f5f239adfa08bd0b98acef935fd45a31d6e91ee8ad7ecb733e2a25cee2c1046607d0f5e072047840c75aa6f7a91a73acd2495c331242cc36eaec6d10bdbd97fe5a94894c2cdab92db8c7ed763d07bf6aff29ec786a8853667270ed7929a7b939f752e6d889827b814e5f3d56632d59c30753d6128794e700eaf3399edc72e70eb342200666999067bc9c4ccb6b57c749f168c42f4d4667d0461f341bc69cf28edcea03aa42acc0996bcbf35fcbf62410ed72c247212fef80e0a2b283c1f4e21edffe8b4dc68d6159031184f1083e2899cd0f503d1cfd35b13867245db255eda020f48a01831c061f6b6dd21dad9c2c1e067a1d4a96a896813dcca1e19a3b57dcad86b8075d7fe43a644fbaf299ed100e026ac9b8436cecf2a86d3e790a07863a0109311cdbcc4f949d94ae22d90626194873f10d38e7d1425d2f95abd91e8605f4a7225c3dc9825f2f225f653706d651afa198f56babf3a563473cdac20977373161664696bf33baa614ebe14cbcdc15e23bcded18dbaff580e01c5bcf34adb221ecc0c14b529076c1d65a3ab1382b02981d7bafa547e687cfa43317bf99e38ad3ab025eae7849591bcf317c12e218d219c9030f3079e6419acc5bb00ed1ace3751d46beb0da89cd8435914744ea6d096e22e2ab783426e22dc75027135ffbf1efefa1aff3c862872b7c902ad92fe962921d18f5aa9063d342021264e410131068f2965bd6be4da0c903db1f280b76dfb9a014514833b9f0e10076e9310736080c4d5f9223f62946b483c1cb6490a9ed513a00b1928e0a23a6363ffc69ff38cd71d07d615f318091770180e343d9edd951221024ab3e9e5028e178af1105f565ca2f0bb96b2f3488ed618a018c6326861392260b6bfdba80e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d267de805fc0799215f9056f157313306f604718690bf06e9f6179f69f057877","proof":"02dcca776b926e967a83b65d01d9906b3a2506ccab7f664babfb4762ac75fa4d34f57e3100c221ab3af2201ff6112ece1c2d1ef7fc397168fb742f27dc59c802204eb0265a49989de3227874fe8b152cdd1f15677c1fc8a81911e7fe2c47022d4c448557c3b6c5e14e94907d8cfd88ba80c913cb5208e7e158730b330d5e5e73658b6082d807a9b3815b7efbe0c44568044d2e1d497c26edf1142e2606a7fa050aa62b51d4c8e9bcda8b896f62cbbc8d4cb2884da583ef3ce83c64103b7ab40792618f9be5a76a3963dd7ca47eeda2868b458f7896a03d5ffb122d7fa291090a7eefa0310103d61c90eb76415f28ec74a0ef6e3aec2f7d90685eeabf667c7469dc8484229aa94b6533c4da92c2de3176af9509a24ca1b50d8b0634155788c9696697e72c160d9fe993561cb85f0b099946a85849b19768a709530b1f45c9b93fe628f4cac8492f9176e7bf62044423e1e506e849d178eca4f7fd8bd49d9b0353d0339d085a97ba58b3ac40c830b76ebf5adb92a9f3d6501872743f593f8f5e7b6ad51f4986bdd2c2c1d14998df76df9dfb89406b122c31d6026f1ec5963c2d06fc0aabf8b40d6f540a84fe0427538435e560c3b4f75e18345fc893d6df364b49f2bcefc22588bc29ca4b61966b383bb1c2ea782b8c4eb609c8cca25beadbd357328683aaab30fa695b4bf5e0d74e2e808bee88298426759e84bfd8413f8e2a14225929eb2bd8c2a1ec74ec307159aa756a1926f7b460b38f736c3e2bbd531013769c52e3cf666ad4f74f0954b1d6f9e482590ad8cc35223aa03c304010e8316692386c84a326c36ef9abed87b37a05f3302470fc3c9ccb2fa3d01fb84eebfb157ea4b48430a23479fac23480d9b9de1d10760723e5046693888aec995cff5a0dfeeb9b5ccca5f90181c1590fef11ebcce79ed71bbd596e974b03f6d6f15e3502"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d60b19cc5a6a0345ad99c1326f1e3378d3a474071b8a082dda8d43975a1f332a","proof":"2a21b6a17fd367b66421d31fca7a633cb14cd3e99c6d709c7b0e0ecbe36a201ba29ccdf938a20b1583565c95c61c9687215942afa128d8fa82326512c8d3326e660233e017c7ce7933b5d961c5725503c725f3a982bb8d0617acd809b8d1933a7ac933dbc2110e574a488273670348291101431f9118e67bc075906d9df16907ab7b7e58a20bcbc60376bf3b10bf93100268adc47b84bccddd48457e7d3a350f5d95d5aa4b625b4e9d817ac1cf7051a0b57326285b7b016422b70dc58beb8b0edbe624f697e323cd53a0050f01aff1ef940672344c733dc3d5647cea60861d0056757332d1daa7a97d2451bfdc2e06f5d7678c1d34b7d8a67bb64edd9b65f75d0043ba3c0ca2e033e091e3ab01bc67807164b02d840f76872afe253a75f8d530b038b029facb705506e15873c2448e680c5a33399bc25b204d60cea89432d038aee20783a2da080ca676c687ad1bf8c234d0c5d160f0ac5337904cb1b20b5c773a92343ddedfb3a25129c7c0dcdd42d0cceac9fbcc52585adda1b00d79bd9938c0e5cffdba679c771f599405100a538094f71aece01a13ac89cbb6772f403750ba1d59a475feaf34f103fdb660665b13e0cf69d8f85f89bcd430c3e6de7cdb120c594002fe0edeec5d555e1beb5f2d57dc07b398d5382a8393bed2fc9e3b7f1b04b07f83c375fe57dc813cb1db7239dcb523a59e5557dbed9c251b99193b1b29bab1ff754bffde3754c9f14abdf3c772f8750133ae9c77090c7ecb39d239197fd4736ff6c0128482da3ea65d751156fd9b1399689e9a180309b650a8481ebb086adc6f51febd41cec5425270081cd77bbf3821de10bc8c0f5d47787250141403fff34a88aff46eba2e3ee0bdd20a198b7d00cda39a9602c903ea3e116b01fb0ac6ee431e21183510ab6a08375826a5d979a712fe025512c8c18c5651755e7007"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f4446602d675e625f2816df04227c8d45b5af12521da935e6e62abf6932c666c","proof":"c0e68e3e3d9a35406becbd5279ffc0cb29ea4393b07082138183a2a3604f263cc04d8344f050f96df8d448cb960cfc29115f6a73fb6f70c522f48abf896e6328d8b4e192b064ab089290aeb9ae10dc56487694d05b9ecc9bb74dfca9c7158567daa7a446fe987bdd63e5e18ac8b16ea2dba3c75b675caff177ffbb869eb0d1770e5bc8c1a4a0d2063755f58dfbc660a782137c2ae5e2005ca953b7464681f10e2c94de5b38d28d1f71a139a586f08f513418a1bfe9e8a346e473728c4e94f50c4bf9a8a92f2d8d64ae5979c7b166d09c0bc0bf3e642ab55b224666504e18b10d88e20c6d1dbe7e78dd6dcb7fcc9d22524f310ba00d85e0ac1131bd967d44b86f16195ac74eae425cbb14fe4528d71861c87426a35d95935d4ded3ca8fabae200463d0ecbfa0ffb42f7741367b27327077342e63c900c6d2b8778151b238eda49da9b719f3c20870c6861da6c025734b8dc1135065d691ed4f9010dbdde92be5f42d868bd455274da354f0bd90ad7fd8865c660a97422361fdeccf53350cf3f403c46e41462b0684fb647c7199364d1ee5f4efec0550347dba13440ee1bed8a09d8cd6af23ba4fef58c2bd35f9723fdf7e734f30d6359e08d216bcd252409de38aa00318dc378f804bc893b13884669f2e63ca6e9572b3c42f5a76883565fb00eecd419e33c8200380e1dc0c8f326b917242d5865758c6073bf9b2245d88e8e6aeea41e2dbb76cfbda88d118470c16529caaaf42eeb12754db79113e657219f3d24985b4143142c5ed1961069814918f7a221a8639c7ec544b7a202ef791e242afe9046d3c5fc417641fe3f0452117e12be637da7b70244728fe0b904a940fa6ae381919b3c54d5670972003790c8560fb9013d59692460202c07b1e36081430d41bf3103e542eddc21477e38ef3110707376b1969f932b72b81afe6bbe081c06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4e851a8b005746ac657f054f5bb78c7f7867019ee3beb87264d2cca60522be4e","proof":"3a880903f5719dbcdd5df200120c7b0c827a20385d779128619509339fda096a9a10b8dc4aee7e59a78da6269ad2af7edb4d1c21917a96ab1c155282517768259096f7210eebd8e175043fde24ad3e8ba72661438cca2f2a097cbb7d962ae27d2aeb16a1a17477cedb727d8dc24385fc2e60ab2417b2ff5c818e8ae5848f8e39297f01a9b7d80a0227a33aeed613c25f2a305601e36400fc26771b845444b60ad286259dc869f8b232b092e633bdbb6ed434b15e77216355208af1ec72c91304a2039a4768c4c63d4c639cbbc8462566a7ddbaf1658c8f90434dc4dcbb980b00b2296636375c9cee945cd487e2c9e5ad135f61fc0b8e05683596f290b5d4ab3d4c49e03cef1acc4f99ed7b7cf5e77430baf94460d56e8b0039f5bd9bb5bf305f78e98ce94784b7a7b543ec08d38a1a933c771703c2b80d7c9c9e9fb0fe302b3924a43b70bfe7a94904289ed7bacc0c5dfe5a439d832e505ad87f1e245bb5e756668016c99081f8313fa771fd9289a0715186ded6c75b8258b43b61b032eb634d3ea788959bf71cc7c714964d4d7bca3329b6f6f1513f2359166017e9febfee74567dc4ad69b40411b433641ea73a5544d92b51fdc525282e012038815458eb4ef06857cd38e19afb4512f08c605920f2cf1affdd4f60576eeda27fc9e4d8a25cf82e196f7a7e114105d70474477e5b157494484b99c62e6f17aa519640122f1582a8618822fc4a3eac6a0efd838ffe501888995b00710e4a05455d3d24bb8e6c20f3724a1238224e3a20131602236ef05d1b3f197dc3a004c93bb410bcb5d853e4e13fe9f40d9286493699186a60d679a015af2477b5fe1fe7a1637e84b33f04bdf25e07174b8f6971f452400703a39b2f830ead755b119b9d21e2146c3b0000c14805e0a5b0a2039d451610b93821bf4eda1772f9dc240595930f3002d28101"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b86adbe84a56bc3aa09ab9ae8f9d0356bb0219e12a0faa0f4b9844153f483f34","proof":"102ba867475e95b8b097c0a6ef28661fe9486493246fb35c03009acce40f542d62fe02f836577aad808dce5cf7bf41e51a8fb82a11ae610a4c55d2ebcb46942fece04347bc7aebe764631f9cad69c5eff90e7a8be2b30692e6b1157b7cdd98235e44fdbe1bb2abda0757bdc64fa33b9497be029a7c570bff40d19d2e4431806e47a70d16a8687b0d0ee0ae49e29dd70d11b3856d49b792247d6f2974a68c8709f1f9ccb87b06150772eaab9d63841ceac9358849f2f1611afc904b8dae715c03c58e34f94f12daa55f6761b322b7bafd6ace87d9e0fb3a65ce1ced0df118bc0a6e7dffa38798c350d1874ff847df319c24c1ecf7c340151df66aeb61d8e91c1f18a79e91d948ee8f1bd3f4ec14ca69957f6bdfd6ff03f7bdb12d1457bbba4b26ea98835236cd477b1e50cc34c14c9d1447e375c44486eec109e86247272e060522018b20185409ec335fee340f9eca7948afeb35ede40da8f034a68c0e828b44f6f6fd955249bb8306992dfe29fce8a4dfc840687694387cc94cdabf454d17166066e4fe54938c7d0b92329abdaff92f82962911533edf71c8050f4bfac0b63b900e26c1861ca1a5fbef65c27f7f4afe7bfc873d9994546be2c9f1f47016c013c674d778d330f3b311da0def324c795d201c254194e1bc875a549cf4186df218683e4197a27719de03b3b3d7263d746c4e633cd530dee03fd0f36e929dc0663f5addf80b9b710412ffb8d92489d77e6f12661d62e839a3f4e97e8780cec96c4d14441be9230978dd8ae92f6fe27b5af40a831cf669e775a884a2ef009b0a797b80522b2bb8c8a371774038af53f9cf74dc48332884533dc686997f6dd1e17c45152651f9e08ee6ea77b83916cdcdb2561420b02b6091ee426642a743703e79034511191dade48be58755e235f5aed4a2e84693ea47c8d4fc1de8e3a42a02bc0e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ae1df3ba1c7ab6c70f68f23dcce8a144d572e52d9388d54d7fb9f112041e3408","proof":"7ecb881c45000cd3c7a8f5446ba309da753a87c7c696c4192575feae2e164f45b21eacf438d0a16c6b658ffc6d883cd5169a95cdf43910efca4321e16babb135b089425b27a7757c05ba3e0f7901146fdc73b4b55475fed460a1f6b6884fd63a18675c6b972cf527ff7030c723b0ea3b52cca0bd77f85411c52eca0fa7a8d0187245ff9a4304e3cc7e089094c0652289a25e33190292bb451ab358a8fc2400050fe574a92903fd1e9c4b8858c074cd82e3aec587308db77257573df1033abc0360f6cda6b7c7bbc4cb43f6e483e4e3e83a90d9fd8b4125dd2053eb3a0ec993070047fc8dcfa226fd8339e33ac9f332ba5f2793cc4d4e0a92a5db02d78e547f3002b72b12c8670a3a655ab2537474d078db0bccecd45fd7dc968695bd09205f025431947c64def863159591ec42578b5c0070ad83ea98cf15420a2ac54dc32b657479027c0cd45181f0f6092bd3ce4501de1e9067469f213d09429215d5ef5c6d06359f9ca06396c7e8502ed83db354032d78e0ebec975be2187a41e082eb4f74eae1a8f136f56f3f3656cd436e46652a7b7e5eb7cb93de349db31b3ff657302620b1a2a72b4724c64d54b230cb38d1263e76acb378259cb29a48d1fb97c7b11708f0662174b1211b05dcd8944a8ef2a32d0a3502fb67f79bdb4a83094f8a437e7cd476f59d8e06f4ad488725b93362dfc21ab032fef80f321198e0ef48a780256e01e5f2bee2aee9eaa187df138980c60ac5c91654fc56e975b7e1867e29097c6ebc9def0c8e965696bfc4d2beb2e44a51da7316441e3f4013c5d08d87cc8f5e76da4860850050576f55575c995ce237f174267aee628260c460715ed78efb1077f066fc37bfebf2d7d3e6e4d6b5c64cf2f716cfa4a44147b4e098c1a1b74c084b701a8d7694df33c2e966c03327dfd08366d100b69248e8066f8f0d93e5b401"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a224ac5315c13827ccd500891b2ba06067d1b8ef233bb048a8fdda028eeca528","proof":"aafea1d7c915adf2cbb669633d8bfb62c2cf2897f528b59e874c1396283529190403c5e67b0ecff5a2984ff91d5aee8040f739b3b48f849f12d6f1beb99420656028e22e676e3597efb3f571ec4b4c93d7f05064ab2152220cd78c8b6105c7391ab74c367ec9c2266433a03e6b102da02e43fc7ede09e28f4160c1e2b1cb8f42dc9b78ca4bc2b6697daa39ff0ffd2c2a9893fe8957e09c044ffaf943c442a108cd1f656af843b8e9746d45b16a0edae33a9344a4c86ed8f6aab6818ae88a9e02282ae610899ca22adb7987df09d1b9aaaec9689c5d65887a07666878fe03a60848706cf4c112dc38fed9e0fe3e4e348cb73cac77074ff00c70fdeb939625891b4a9af8a34955a01815e284ff33b824e9190a7358abc258ec9c744c844a7986688ead8e520aac362cc661050ab6d0c51e62d5c319dcb4d72ff4c54cc1969ae22f0078958fd92257051c29673e819ba7c87320ca4ea06dd1fcd9f0c80cc3be9f424cdf99ef8989998e66adfa14587a2508a9de660c1f955a4d60b6eee933c27223eae3b15aabfa3069e32a3d7146a6f721897e6f0ddf11b43b44e86bde6c49693a1670eb2a8bf0b7c3100b10484e9e4ed045f14da7c327341c244fc1729331457c34ba748a476668abd84eb4f82751fa39d00dbec39985b65fe5c8b03956abe05b08845048ed2b1d8c409f888bf5c6ee26fd882cd93786afbf01a51491e74a48735a87d4e048c599d2a6d5551f94243126c3aa7c416a63b53f7ac93b85b13f7859fa0016d1f9aac32f73441f650ba3805f845746c7580a28f426fdf16988902a5480e7c1198fa237366799f87c686d738c1c46188286b2402c8939bad1c6f8673d2d8c0331023ceb59c0fb0da6ad15c21ac8d6f4c2547acb8c25ddb96724305a01682e8ef25b34f7906c2e0425c9e6bf62f9ec16fd60d3c50b77a5b223c2af9d02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fc4e53053a00c37cdc55e06ef48dc216aac939fa1ba55b8825d10f2f2a25602e","proof":"c22fdbec5acd81d5731b09d2cecb2896fbafabd2edc949e7c8e961b071c59f03589e8507d395a66d01c44438aba45eae3704bcab1680275438d020cdd2a93425f02c38132d72b48b526a3e071f132c3a14ca65744cdf9a0661c63ba84b68af72388221deaa3aadcaff032359ad40cb3ab5cbb49f17ac6c1f88a58e57c3b86158e2013ce883a547e33cd7f61a786650036ee65a44804e3bfbb9dba6346200c90a9e4602e6f8bcbab1098bb578e254e4057ce44db27784283e4a631ae40eda9408ab6f6b25861da0b51b983ecfe63e826094a9bfecaed0e9ff65bdff21af985a0206b6c7e5c9a7dad64f109ce36efc3c96ac3fbe62c156cfee52b32976bacd6b0d62feef9808c52ec8429d8d8b9cbaee5005b08fd67ce3ea35319f9aa491fde04ad621825eec1ae431543d0c8828b0ab06d302c16a34e985d8335554ff2f90727466fdc898d8247dd292a0100e251645c8e3eef1d8b2f8ee63a3bee14b7d57866142a2300bcc4b83baff03a840d561decdad61374c9afa2960cbf6acedf1361c00e0315f9bb98732c3b45295bc7bdc6f594ad7df8a9109c4219c59705fdb10d05466907bc0ba1f362e41a1c4e82d476b871827d6ed1d9f45799e630226d9e9f01080d40a58a85d733e753dd7ebe759849e7c2beabfda15cf3b8a4555bbced1ac32d655751184150d455185f6650543eb519d028152fe2240d9639d8b545ae0bd7f06a8fa645b3c5486843671892fcb13cfb319b3282bf4acb572e19b0534b9fb158468dd3e5f05be3b7e0557e0c7b9ce481c6effff19841920251fe223b4b16b30e2e5b1539b05687eefdd9685367e1a6ac4130f72990bdb20b7dd23f6e223206fd42a72829506782f31ef86480102bc23a8aa8c24559d0e3cd446adc86e959a0d8bc64f639459c92ab3ba0c6828cd3be1e9fa54ca1e35896543eedc060504eb04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"02c0aa48478516c147888c13c5f25b51ebc78c24a64731c70be299582dc1753f","proof":"5ac97b8671a7ce6b963b73552dba5ba31ae85674e132a5cbb55faf4ac3de0c53c83bcd85956b7b6b63d7d6dc565e0820f3afedb683f048373557f611f5cfbb525ee9c79f3da793a058caec04941a19d9c83e53a5aa701d2b857753727d7c583e4cd76ec3f4bb35a80ec1265ed2cd14c436b2b7a6708a6edf1abdae5d56d651611e57e6ed6f02bf1ff5952ab52d5e0130449a013d2ce44df2130b2275a7947b0fabaf4603df9201f629354a40cfcbee0ae9502f47042441799966da9ce5ebe30cbeca7dbb7cde93e2548caa2cb4d42fcc5fba0f375c495087107b652fe9e3d4046a2899e214189314097618025bcd1456ac60111fbe49e46141237f2e9ab88b72b43632213abd96a441183be66cc08f022260c977d6dc286eb3cbd49fdb41d54cbe2d253cd06a08ac39f9d7f6125437a0f5d032dfde29cdb1c11241e63de7e051f0a6371ae89701c4d576ff43b1f34dac5aa4608502e137415655f18f322a8a089cb291793fc200e7a346c7d26feed6ef40e09985db2b065ea6a38798aa6bad74b6ab201fdce9663f68e4fac0b7a203c360d094ae01fa5cc7145210715fb152507ab558fd075739296c745eb3f6f1e0b78fce4d5a6849242cead3598b0a3cbd0eda5dfa7724977486d1e9d46ad39c2245c66726e2adb05cfaaeb2b5942b6d202aeaae2b814c507208bf3755f88ed6899dff0cdbdeb1406d7a9139af60a7c9366b800bea649e1c2e091515e514c7588be4900314c02cb6c3fc1d43d2231fdb9f2b86bfc4f613733a492c7b580b66900dd1f6ff2f7aead28d90a385b328161ac671a8cee8eb776a92244bfcb01d5cb00d2e647c56aaad0928de4f0f10106db525107d46372cceaa5353214b9cc91a8971ec801248e63a3a5bab06980861b1fd700124db3fce4dfae634f815eae132a0701a74ff7f386726b542bbcd5c79a7430804"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c06d6dc0f7ca0a00f2daaa52235c105ee1257438ca9a79b76aea8b492f95142b","proof":"8a88145c08ef5c7479f18509b218cd61eff35ce6e8094f614a46910eb5eae81e769b88fa36969376dfe4b9fa11348719fe94c996a3510f4f3e0e65852ae9d520ae4484e5bf5bfcb9b34c00c33ea8475a4b5d4b18370cbe7cd3706cc843a691420cfe7d7d8fa6f5a42168b5dee564238253d934f017eafa5fc82fb8c210ac247b7dd033000bdc59500941352382400e6b9b93bc3bedcb51c8ac65def9a7d8d30e462fc1dd52ccb064dcfa77f8002ec6bba5fd3c96ad0dee472d5c4120012c5e08c1263dbcd17420e6c8b278a2b40899bc8be91855da43e92301003273dbe3bc05d45a6016dd88bb5ede918f883b48fe8e883078e9a74450f8cf9e52c4ebffc92f989cdd24b4ca9f8eb9fe9228bca0f5042c91782ed6bef34971d03f6c0b6ddc0a3655700b1a7028f2bcebee195ae7128932faea90e17a4fde3f7019e11c42f52816aa39aa035a13d79f459c1e4bf295a74360018efe99820bd97b220985a9d64bbcd5dda7827ccad26f0f62445f801336a97e9c4e25d2d08aad8822c187a13d661007062a6f92a2f96d551a1533c1fa4d7148ab489bd06b466c21ef9480faef37fe1d84d04606dfafc56e3c99554509544b99c7d7ce36d15fecfbf399537cd30578cfb22166532914b3f31c438d094e49f365a5011426beaca4e76418fbc13a73847df620678415e640dc68d6a3064187e5623bbef515363768e47d8c3f80e121025a6d501b929a5cccf4c2b9e67ab1cf2096fff85314f7a4bfc3301d7fd0cf2638c3c448b333ce1aab02357c160deb7735b57a0e062d9e83eba3cca54e1e316754b6b094a8d5f039e92d5c41dd49ac6abe4b1342de60942b1dc811750b928c5798db1af9c31d78a5bc5e13234544d4c1cac4363cda2173904cb961f079004b05aaa703e9b54cc3472b289f2a0f975598038c1b95a6eda9a526a4a3802a539c01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ae350f9a16f9a556b9b9efd82faadb022796a2349c271c2637d071875ca06d53","proof":"62350a3c8d25c164ff4c4393d79a4595805ca8d8aa536b8efbe77d97e5d3fe757216e861f19678d8757e0d234d4217a48dbcdd1d2376b54ab6d8ab9da4a712484c484bb7ad2054600d36b6005ebbdae2df5e3a38bc0ddf29a85a5c1fcd76b73f7c05e44a3fbb2098fffe5d58c5071ecca08b05df40c3b6d294f8b1e46279ee78a955f33f1365df7f5c37a5b1836af3c18bff0106b9fb677240c6ceee71856d06dbc3b663189841fa465f479381665de52ab0097293271fed58173c9b6236140b7b6f2f1ec7917b2bea612f41134a94c7296ad5399a8d18c9161bffb68dc0f20f02e1378e4f78ce6ea00e7e52b0dff3291e83c3a5397530b4047fcc2a9c37e974f263bd614bc63b0147ba605a9fcd812c9a075fa6991d5047205d5d1a20702e4bb4176e980f3631f21dbdbc86368dcf0a77e50b6a6b5516b0529d65f3a5d3395578a90283acfac7a164ce8916d7e8992c9a33f665bde2bc94d70fc0d39effcd1b20597b3452934813151fb7bfd972f7a5151f626dff742274c0879c6da1d6be4da8db3d7ceffa4ed4617b24bf05fc036e8791745b18a5fb3f5ea991bd19a38b31f494f5e5120b1868d31e7e5b80f16e4fdc50f08c3ad1f9744649dad9d3784470c4d5932ba6784a783d20a51316ec81e025aa2cec4b257a7490b99e8ce0ffdd66b2efa0649f927fe278e6be20ab2977f069c040abec012195eb394df69b1e3c72206192ed3d00209860fe5888933596fee16b7bd421d79e1f2e9ac5b6ad678d2e1e8c4815d64b6c38d2da220599f33f8f9b695a86b98c6ea0b8cc6c7970740f2ae05ae5778dc44e70a2e0ef3069bf61e4ef2058a2c2d916c6db247789cecf0609a7f6ca73583bf914c364edb544c6f29f0322bc1ce628ca83c83052b050a891085b29eb0318553d8f1b4ec0c27c390128e62ec9ca8c739e49107aabedb188b503"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2471a0d8c0fa0b976c4f04d03306f758a3c0d1fdf489eb4280ec5f51300af110","proof":"96a4ff244364ab3e8f6391ef2bc5cdb2d7e0961d55a46335b2a700b0eb2894349cbfd9ae12559ca12b3ceb242411a4a1e57c154564739c0566a589fc62ab426082c71659732741e816d46fbe75fc1c7ea7aa400aa8f042956b02a088adea4313c0331c655fe0b481e4c90bb2b2006ad7edd05729dc9ada290bb4f76398b81e71b45228c45d8d3022a0ddcaba7525e38c78c33ad439639dc902986daa906d4c0a362099f507cd3572bde5399f4c8dfc3823e54192efa266340dab870c9cf85c0e518dfcdf9bf00e0e20ba29baa2ec0127f9ee0a92c3e4fe2cc9b6b2f62306850f345081009f540f5bf29badc810833bf69e4eed86a2c7635d4a11dded8ff1e27a2015d7f5e48706608350195620fb4f4687c82abad8243e4707a2db089070f0672abd1cc1680a24796f7c94a32663ea5017e3512c4921bc0bce2c807eb30d97538467ac5a54b61ad4576f8de3428a74fdb338b5ee1a345d6622219ab35c57a369ce5bbbd2551d5454bccea5aac0b007b8ada013053c2d36dac8b91aa6efa1350b4ad2de346347e00a28077f53d920b296dc308aec3e38a4ae7a331ce7c8908c302e8fe3245b99e2d69452662ac8da34633b43813cfe680701d75fcc3d3263961d5c5dcf3837da83164804caa67d6ada0e85a65b578dc01c2aa3385b0255cfe734ee0ec6618630cc30c15e39f657299450a6f28998ff8267fb6f6f5ba2a381203b9e9900b35170a06618b38517185b5ed36f34af4f2b30bdfc41e48a753866066fc610a358b8f6db5cd1732814171e635263470b5ac8b70e6857b85e9db3ac3d0bf6e482559ea39225b39e554eaa902c9b25c7b8a97e6b833c83a8288989c2ae2d4ea99f3118932804cff7456d9f4820e4789d3b3ec7c8112e005630d54b44a307e0e259f1cac6b1c121cd14c3438863d27a88b46ef9b56b05d9af9f294ec87702"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bae174c04f144b4de3f687196f338e4bb0fc53b8dbdd4ed4f95b2e8a19de4f15","proof":"aca9bcd291a4da13ce0e06d888bcc7d2eea7c40fba154b2c0f80ebfd9899fb3e4259cfd7139814267ccf4a2f744d630fd0e57e5d495b7973481da699b1557b37b800beb029c2fedd5d35db73905d7591e9dae3c3c8797745154d070d2a16422cf85b0f2d3bf77f4fd67f49a036507a908de6c89fe92af33862044c42590f84137f7190e0b2ec81d9aa31143f8242ae62177af657c1ae8c1febf19d90e01c1200b401e2145fc92491cc049d8ac3ee3eb76788ead07370018a8cbeae5838dce1080357ae7f8facbc9ccf5cae5bc881a355760200611bce3427f36358849e5e1d0156d376bc2c6d52879f00ba9eedd1f368970b26cb989f3926393753624cf1ea67923779c8e0e5fbb80a88b98ba673f333e6038be2bfefec9bd618aa5600e05b134a639ae709f2dd9ae1e5d1019df96cc442448cfaeba607b6abc898c9c251cb4a7a0261d3d82aebc77fef77642c9a6b6874c6a526737e25944248f8b1bd11cd4704d5f08aabef9125cef96b007e3144f61d1537bf8bb9f6bb208705fc52b0e70d7aa54d50e3711f5fc024749c5ac0e5ccb3404218bbf853ba4fdb6c7f5115824cd405b31675080807a5f165729e548e9190a9a16ea788164c4baa2e43335808726a5013a68669c5599f6e6846f27449ad0391c794feb234b37fc8bed43dd33442aaba70b5f3b38970a88ca1a4a47c8d0aaeb0967918141304d0aeed859bda887d00ae715c5e161ef80c94ed04a8bc69b3cdcd05d3b5b199bbae4d2ab35754f026e6785346602faca840d0f065f4720887eac26d45058a5d2ea4820bbc3b28461af41acb029e4fcb7be3cad198088e68876eedf7d07bcde75061bbb988ed0d866b44806ea76add1c5e794505cc0401f55bb1b4273853c07b158abce43ffdf1a0060812b7729572b8845ad4e2e609c07c741b3541d22a01b7b04adf384d5823ce05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"9e08b951a4a056b1676f11c2226d93a1e99792ac96ad759338d4f400d67d146b","proof":"26014eedccc4309127d633244d1323c353350be70a5fee44f9f77d05a1d57149929214686f5fef9901f9a729278436561f98a0b93ba1ff9a446c7f7c664ffb6ff62bbafdcdbd4d2cfc67cfa76bde132a40613779bddf752fbb90c68ab5feeb3af2a36d3534d6836e4481d204d3aa7f6237f8667881c6ec2f407762042b6a7d0c74e309d6ea46ad81d07d5469c6d944a614cc9cf8d7aa6f5846eb425a1e32d80ee3b9b99e8a840d7d951c59549e680f7156131deb37cf0838425c0bc1550b5b0213e461b9d9c2e63258a6dccf06eb7dc16ecde2e7e5694a4f0fa6b84b815d7f0d8e977c89265577547ccec34db5977270942854420919a447ab8b0b196b3e2902683d76b85e92a79246279b1d8e4a9415e5717817b0226e02fb380fbbe867f1743090714b2292a0798d8d503f773efdf38b63f5e1b0faac1e38a829ceb1d1de1eba55033aa02d10b594ec95844b5c9a846bedbaf4d80e1833c64fea271694ab3cfe9581f3e12607a53661c7a1e1e11d1de004120e3c45b33170a8cfd8f7dc881770d100b4d0327f69e161232d5f9f6685de6460abd15541713e15f99a02fcdc497a758f15e04b012346b2aef902a0d2f9205eb6b81bbaf00c63e0916bf23ba44c94e29178555f6c0dbee4e6fa0d8eb12c885bfcafe2794bf4f494f51610388163a6c71aeef52b3373444426a69a31501c8972163a4ace5b2193c243074974fc4dae6b71476bc0fc03e6ec6deb7540d70b24c50dfb9e7608139c9318886f86e12a5ce378a1a3f61e1b7f6666b69818028b6f549c8f7b03e9b35d0a9b8c4711fe45ee81a589e4d1193f16a9d63b38461a0cff00ae7cc7968346f4264d2f8ef5483c6b8dcb5ec0f86b8a1625873ff92a10d7b4a0438270562edaeb75ddcdae06570bba320396edfe179570fbd2a7f37844af4ef1276a346aee87f6c85996d790f804"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dcd5af399e1e1ac28e3956e93caa30e8ca174f23ea2cba5044ab325c46c11d4b","proof":"241191b2c4b63038fc5cae1a9819349d6634e3f5e2c12bf337803a9a1515f601da226220588b525b84a034094578b010a04b10c7e9ccd0d0b48f8deb375b2d05d862f536714caafab22fa67837d4d9e3c094ff9d326ea716e3b067ae784a161f5476e274721f6737579734543325abfbfb1177603dd96963955075976ad2e167b69e027353fef94165450316fb64bede870ee5c84efe5967a0c2622704289b035e0eee4705093cc0e7e56cb3141e7a288120a0e621c6772be26f9f924295d5000f0dd3c0ad19411b6f0e945ed94166510883e3324b31ed01839fcd3283d16a0a50672ffb3a4ced2531016356971e0d3c176aa4995f37689a1bc17a0388a25609324f7c31bb1f46ff5b32f57d0d5415ecd18630273c84abeaff6f022c212c20362ed40381f47503739dbc3b72ee807ae1cfb86eb41bc05a864ed7061bc29a474852f34fd9bc9be7c8fa39b7f2c3efa71c7152ae12b3349fb007915b09c7b9061e2e076ec4aabb18682f1361a28f962eedaef1e93891fd1273e469c99d237b2c62a22ea637fc98f5c8d6b922ffa03078bfe868284d2b4f904317a45eb822c041440a9c57c019602f63018dfeaa9eaf200455f4280593e71b9ed86e0de4e0536e72d81a7098663be6e2a66be7ac7770dfc7faafac6cefb0a882b2606e3ba7cdf45bb4c65eaa7828c48a94204d1d37a8a51313b3ceb27e1e07d16aed3c936899184b58c90bdbc9f26ec2f397e0b8ec03460b0abaac1c96e1a23d46183a0e2788ac26183988501f6e96b5778becae46616c20ec1481ec55311791cb577de4a4b264325c817c630655ed165696385d0de607d4c63665374991124a23c6c6094307137220ed934b3ce1c3eb8645deb2c1ec56f638d811aea27c86a7083bece35e65d20ccbc23eee4a01df43ba8acde5f6971ae520a432fb41689783d72575e20ccef703"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b668a90c3ff112ff5086cf3f6e20f3eebeef738ec5a84e6b24a5726c33e3da3d","proof":"3e0686c236488d1648b9752ae034028627b7e1950832b92166f97412364ff66f1461050a4b7f220a3f8dee8b31c58285db106235064441d5079685bcdbdcbb06f677c0f93efbde8c28acce9af7ba38ab0dd51d9a14e19d4bbc8ae64fe154817fee2b08dd8c4034a0109162f1b419e36e398a6caf4b21cabe4dc8ce8e4b490d2aa75653d6c229326ff6e939266dbcd4c44fa36532361e608a501a37cfaf44c80662799b4955e1929810789a0ef7751392021a0350ef165fa795d32cc01d06020fbfd707f9b41818e14740cd70754157dd961c34f0aa68f273e0990e923be1550b12fe17c2e0f16ebaeb2f44f2555dc51bb3ebc696c044cd07fc3b888b1c927d45f43c315167f540c6c9039e163f84bba25d409438e5fa89620b7237d5600dad28109f00f93df93aff544ba7bc629a8e6b6742f54f998eceb3c8294fc991e8784b407853124daa74cb0ca7a4446798e44dbb2f78e0a3025b50bd605048d440e23d60b1dc8b7d86861ee35f0506507ed02caa42b152d635eee2c04bd94fd7bf2362b2c36072268c8a48c3ae8d24a132f1364c1fc02617f99b4580680720947b4530fc833a3bc7aa7c58cac615f9df1472705d2f7337ea876ca71bd7fad3a3235410aa5f5d51ccf50612e6994f22d5bacd077ad5d03bac99fe0877bcd1ec8de4201bf458e0471f2051116f1b3f87ff65e74d3a014195b99719e698c822e1c89a297926a1b32c76ff7934b25c6e6bc41ca0701595bfaa31172a7b84e526052f65d259a2dfa45aa4b368eb76eea6c45bc9919bd12b2de71a05f29223cde39ff65b3f78b22830a47e2e1c4cc664575ae97c3870cdd5649ea169328310ea65c976fdfd75512fedc0f0533f2eb0445e917cde82c70bc4b84e876501ab9dba2e0ed8e14e01cdb2685377cc8a9846e60fcc5fbf97fa6efffa3c648692283f346ff9f483dd06"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"484f2452f7c524f71d65b5e4241d6ed2f126cb98784ce3f4436b1db4dbef5978","proof":"444c7430a67b970f30ba28e827a07482bc30cf1ca84b4ea7e531e1d49f8ccc157a5b45f0a44843d718d6cd37cdc5f5216276f323f7d0c56027093ee455ddc3452a11c42e17657775fb01a3d0ed18ab5128be19e8dacd7c3a51258348dfcfb1369aa7b3ea6facf3516a1fa6ebfa8fcf2045434ff8a604c2a3b6b0bb4229a0e30a97ae19275e22975ae084f43d6cef718df94e11bea176076f297a6573f65e2b0e85943cd33bbbc7b0dd20ef64d1e18c092f2a11a00869c6774fdead2d2e81d60f0fdbe90e601ead96ca7e2ffd456474409d084192512cb2951c2225299cf9860848d8b55c89e4bceca174bebc98e4b98dedcb2ae8afc0ceb0f5714b5e4a918d1b36c3cd911e77c6b0457cd2fac2abf56ac993db442a4fc9c61bae512b0f07345d6cd8ef7c1eb2013372b7b1f3c6bf1760708f729d0f7ef04122f4d74d53ca2835eef2bd1afab9025bc36ee5aa8e6809caf516ab503a04d61a5c931452a6e8f202b48157a35ee58984d8f3e01eba2376fac76b273fd39bf6136adacee5708d2b2b26426b6541c2a3862460258e0e37067cdee73cce0ccfb914db80fd4631157745c80cfb929c449c811694e25765f2e901379389222aa20fc26cbd17233a302b4e3065a0db957607f58cf2cf453933489e9969f459ecfc4dd75b13dc79cbd3046b18b81620868054935c6140b3f4e53f44d3226a96f9366678943a293eafd752316cc53e1857e3ef460dcc35e5b8b9b3b8588e798ac8aa97ca1093fedbe00c38767221e364899f30dcf195618c069345e22ac0d5d507e7bf5a04d31a24a0d11f7cfe7e2ac96238d451f41c9a758fb9134a0913add0904a2cb58dd8e1090eea400f3664fd3ee44551ce37672d7ee6c28f7dba5de14408804b681dfb6ba887dd68035a562e75c6b383a87a3bd7f4d7025681d5b4cc229acfd0b1a182752267b3ce05"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"bae51fb1954d2a6f020f421db0950bb639de2bcd22cdbfddaa6100ab1cbf9654","proof":"baa2dd1e1156d51505e87e5d7576e9b996dcaab204cc220bf545a041576795307600d2e080c7dfd617cf8822484131a3523f99b642ca240926b1c727b0ec6b73de5154d26bbb16b588c1a371d2394cb9996e458e524b115df740f44a58f8f41954e082e9aee70b126942ab66f3eab294286678eece8606fbe66c3ea4b57a5c08e1108f496d28eaf73c8cbf4c8e996aed76811036b020e8c956becbc455e6b10df083aaf3b49af71adc1c5950e7ba3e2faeb32ed8ac286893847a4e8d640c9c045dc7cd8c38757c362e7c504c3e588bfe15280c3ae29e2433d73e720b492eaf016a5de9b52e4f9a8dc532883b5ad8ae21b25d94ead69256465c1d0fd8cd3e714a18064dce2b038bc3eca495cb99e94055e457dc7d0c16f565bdd4bebf2c5f962426bcc7a8f8aa57f85eb0929656b81339a0e7b13de47a617b24c8050678b2e4294cefd19de90ae28a37a6acc16cd9059a297e39ac20dbc95c6d7c3cbce0773c37ac2b050bbfba0338bc2ef333b70b46dd525b94c75ff5a2d318faa0809bc31502bcf8aa42c3318983e2b41e9adee4c7ec98dacaa5e2847a67b3342e3cb4789e0fe4a372b417a0b09b5f35e96353aaf616f092c6d4ac64a025f29842cfa146e77d6ada568547d603ee0df005b7fc85981e0f3d51362dca541a86f610dfe334b42318eaead874f101c3a98a2a9bd274a03c1cda7bafe2e656579ee5a1d3f08843006a9130061b71811d88ebbb5d2bd28fb46486155003a6d726553283cf8803716a4a1a9f2e4345132e38243eabe8263c625cded3ba886f4984d29353d86086341d22d5af6c9bd28253332453036b53d03075d7b8190c8ce8d2572b1852494df62381ef55a4fc2496fae3cd6b3e48551707364c88c29c81d2f63bb5fc5b133c9f07178dd10cbf75a31f07915f8f37e9ff469a7d8af6cd6e63e14536beaf1b957202"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3cc4ceef9bce7bafd494deef7a9ecba9fc4cd61deba5c4bc8c47b14e1d97e833","proof":"760e9118c07ecb091611815f906d07337d87d29c4f98031f3a055b6305243c70d62cdbef1c200d867075e8511da2442931e8a409fc9ab20ba68f1804422e201eec42f252b8b1e8578b568e9b1d9acc569c059729fa3f7be69af33e7f321dca31ac9e679d98a19a6a301c6a6a7083fdb71c71de89a9d8f09ecaa762f6e2e3557f8920fdce36260a05aef9383afb4979a1bc59f2da5baae8f5e03ded077bf17d019fe5ee228f11c8d3f20f1c384e9a11c148cf5aef98f25ee0904df806f7989209101bea9c4e37275bd9e832745753a1e6bf8f33a0da92a2faea7b2d422482500f2ae58b83535f33ee4607cff6112c7c8b669a1783308dee70ebd146eecb2c60474e9139b5ab8d22087092b5777637e3016dd38609b3bf52f9f80277440722f2083c0d6489656c298a2805867cdd9723cd6443222430f1dc9ccf30e0303f9fa8494c76b8d77187c8f77908304e737a6efeca490ccac85c71ddd77a756c3a050a5660d05983fa45af4c308c5f1235cb4a2bc88a97803020dc018a99359af71329740017fc4cbe9613b7662b476b11e697a7eaa5a958f901b2a31165aa091170cd41908a8bf98401e7a3f2229200294a4248a318e89baebf42ffe346716699c018440c92abd8bd3718365241a5c697142571875745d5fc2e4c4eea2f3b0f3eb0293da073d76fadfeaf09e1b9354af2924a6f761c21e99003349f437e792af87567768a90999ac363e988f02bfc713045d0ce3bb9b240f1228faaf6b1d77d15907f4e54a59839f961787fe5a3a2fe1f35e639dd84156953a392c639c95ed7299fb5501c83fde260891dbbbff08feffe7e3a09cf84499eae2e273fecb4dd532efa005f3beaadf0326cc61eaca3528a3f309ea868214d7279ee1c6ac63af920a416380fc58e72be5e8b060fdc26c50ac682685bb5bbad65122a59e4c41515f302a39b00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8696f7f0beef1182d881d575eccaca5fbb6b90b1682bc1fab998998763fc825c","proof":"d2336a9723348920a4bddbe960c50599ea62f8c3796ad5dd784388f7158ba86b46809a08bf1083f66bdcf8ac47f8b43ddc478cf6fd222c3db53ad4ed37b7ce7510c6bd71a068a0226825268212b54442b324797d89492ad4494cebbb673b996686440047172f5d8aff5e30345d98106545fac8026b9f18e768d9e6f43207606dffc69a458e54882484a7a870f370732049fff4867b16a184d9da4072b45d0f03b16951d828f4b5c416b6a41bb4bb61d4a66869c05d9b9878d3671718bd28a003a6853aac00eed176473076c3bb1a88d91c758bc42d6502ea3f8b031b87c501053ee94e2ea50f29b79eb58b3edd51423460323ec19f5d37519cf22df469d06f7ecaffa1fd60a909eb9f9c44cdfc142fe3ecfa1509e61a854f8d6dd688e08337049ed76c2e93cabaf8e14db63ba663c175796f68c84bb04248cafb2c40c02c16374cc17bafa87bfe4a49420b745620b238115623a6bd87d4919583b1277e91cc05fac38c1bec6a998bda57aa577747b86022639209fdf298050edb98ab06fc2430043379637db59f0250d42d3e23f03eebb83b208c262465c67e241100bc435d4582825853bd3e6a28f43b9fcb377ef3ba7f70355e48c1b9992fb51fb7b046f72e46bb40bf0a632a9bfd6872a5f4312629fa23cd1088e0a8065c169f786e2f1e73e46d44e0f04394edfedebe75710da54d0f4e39a56574a898ce9b6a708f0edd47c2012570ccb6120ed4753437a1f082348cfacb76cab858bed8c324166d03d9493e10d8eb3532bd832a9bf7e1c89ce96cd70177561fb9dcb186e5c077837f3966309353edf3acab84c4c14c99fb9c7fc67479541f88bda597f5c1fa233fad6459e517b7d930516fac9113368f1551c25adbc8ccbd10a98df04223cfec7614a0041655a4cc8e72f31fb83c9ad20ff80e1e94b5afa4a4cd081b5e157300eaf9960c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"82f06d6061461a66b03ac0b5943fbad552849abd65845632495ab837688b625f","proof":"e825cc4ebf47c3d627aebb9c9cad27b4ed5f744b7331b6cefdc8bbf786760b2456d816c57a787912b2a795ec0298b20ccb468112e581b576ad68028080bcf91faaa2c075f365eecdb1c5080542d1048e48754bb0c19f80b27f6dc1541102927e0c39de6cdd50cb8e8b8f951285d6744da71279a57e23e68f75ef2d754399367c2852ab3901b9c30d16cffa7a7f01101b00924d0071508ae6cb7f05c1569ed807e491275b88156899cfd5b139ec7040b90863e5c1b26bf8b1d71b7ce7214c13092e67ebe51abbf845b8d2eb3d9dc9b1f94d746cd10eb589d4c6b66179178b9f0d1e0f5673b90eb1b20f892c7c7a75d905e70155a9eb5ea389d49e958f24db212cf8471cea7a644ef35cc87e6219bdede8836b6fa421afc27c2403cadb338e207bfeb6b8d6aab1a4f8b568663213c6eee3350b83be9f3f05a04eb06fa554d7af404836a67117099ad9c0859c4195a57f4c5df003625d3ff3ce3e56aabc4415dc106c9a2facc0b7fe4342974508d880fd4498511ecbea6ce102af8c35d983e745622a7add421faf840be0ff07d1bd58e53a1522c1f42aa80146168191358b6b160a04252192b3976bc190b0f6444205a0626b565632270501befa670d93fe88701942a3055ed613989ba91de2c7c154d7295fe82904ac22b2aaf1b59cc645e8e23de0abd54d62c5b12cc7f54b4d26757763dbfe064a39b969b6d434a51fe8564506a637c856783939f476bb23249c2efdc0c430f6c088f5d3a788086c6c5e29897c94c40e4b653c41dd1d6c9bf24046ee1d9d7aaf745b98cd0a010381e896d46735b0159ae79b4567409e27b66626c8180265765913fd21f90d4564fc88653edc05c49275dc34cab0135c369742bf269d7209a8f5342f2a21c75f9a4217937b8602fe1011a16f53ecee61a67c6270cf90f9388629bf5ebf2be2103da62576b5f701"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"54e44e39c1111cfb5f4cbef37f21d37d3467b56f77c0222f3e27ad5e458b5172","proof":"7ec0bf5410bec2e34888615d1ced92f902a34bb847629bedbd93786f1ad936597429923a68c10f55ac8378cc520369106de8b0fe89f8e3e400540d5043be4d09f60def24a01ad12f3f4ee666a57195f13bded6c76a1a6342ef1b9b49c25ebe2e781eda1766c767b5f4cee7b3e8aab402dd144f108c452fdde6f72156b08199488d26fc90f15fe90cb192e34edea0dd36d3fcb4d0036173c89c9683e71b0a1a0c1a8e887fb3e854a39eb2e4b24f0f59f923a6c7e858465fa93529fcf335d08109978a0d9dec41074ce16d7a7b4218a3de6bf8e7a3fe16df69e2d60080311de10664e8f80cf5e1f6c62e809353d02f88ff69e6d613f466e2dabaa76eb6f12b961db4a964893da481b076508c11821d8bf07407c9bf1bbb9afcfe99d16bb7cf5c5f708e6816b3b9d940224b8b014b8c04c0387e8a7ceca73fe4afee42dcdc23d113da75e3896a78080ed27d7b5dfcda6c91c64c6aea784aa1f87e9fdd5134fd3d291455999d9710f190aa1bf50789b506524680d79a04c5772d47788bd5c7ce861bf20388ab04c40b5479ee1fb3f4e66f6fec3a35dc829db88494c7bc6e9d96866698e7f5950912ad27e60b826bab535c27620981dbe95103137d1f8106e8f537142639fd441eeb6696d6adcc4a3ee86d2f1ad2998f1e3a5ca9547392dcf146764972ee4436ae394619e13328741ad10ff69924276ec45bd477d21aa7c04f892c64c619dd82c5d24bc0d67fc6ac222d24844a7439e8581dbcdb5b0867c7683a6a13004bd78acf8cb9a70054c2b5405cf25977600f87884a731a48564244fa1b1141dab4278cc13cb28c5eb46a9bd55c16c0827d13d62825a514058eabb7fd4e2e516caa84070826fe7a686d265dabbcab3f0f8faecc0e6f52f6c766fe8c59c4080e3733a7e779c08983828181904c359d6c4562c0052d210d570fdee2e03ad3da07"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5a25f7beb956be5eb70fe91a3a2c11d6bd0c99e4ba95bfe9b71b8916af88f95d","proof":"66e57e02c3ae34c9dfd2363643d5a03624e2f54688ef4c4a9c9559826dd7f826c097d51565db9e1365f999b7d9f63e403815e420839043e975cf3fbcc0e29075ea888cf2d927c84dcf952a19b9a20df786a31784137cbc02a419f8603eec505e8e4f33cc22ab5166592d583dcc7b8a76ced38d818c0b0b0910a9320c324bd276991719ff454119ec625fd33982de22836d90ab50208c803b9c11b074b8d0e4010543ffd279fd0d32466b4b13945cbc8bfd97b1ea295a2d5e4bedbfa4eda8ec0e11c81be46e5d8723ae64be630c76027f0fc92fb4d841cd306689aad91234510cfe3f0da398c29b0b4b7d3d1158d11a4f6fa3b98397a412508dd6cc3d23ad2f07065db670f2aa77502829795a2c1b29d27ba273a41f5a91389a595e9d0f74bc607298835eb80e29674bf4326f544ac799d8321c14ee7476555b9b8afe3051cf770c770b808d6c3a68d7efbdec312865895a5991bc7b5b2f462e9b41c9cd06fc74fc2f3e577265cbc3b6219277eee8f5885c0bb415cc6e6741a19aa485febf870728ad24f1d0ca4c32ed332238675be2ef0061a0a069155963efc38bcbd2afe60c86a108358febd6aa5105bc51d4c17be2efc3a88187aa24b296099ae451b35904daae7f4e17cf4de08e7a3e2c87e1ed61c9741e8cb924f1d3394c293313a44f1714c2727bb2ab2c9bed83e1fe94d0af86a8cb923fef9ed73d706e6c7ae4c6093e4aa6381f485d7c8669d6ee65d6964ec582f9630c07db2be38b528bcfbb48ff21b8d2d650921d39ec9532563928b30c9557c51fafcfc19424dd635f5e3aedbd3580b89f2d13652a9dfc95417c734695cefc7496d43f079a18b957e27e2bf16a05a2f76630139d32def86f13bf8d9273e97437ac93e68120527d7355e6a6341e08202a8d08eb6379b07fab2da584b4a9f324a24557dcdf2f1d6ece5e9b8fd6c60f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fef53919b30f4673a0b5d35e02653da33b08998b5496881e4c7937e82b1fc841","proof":"ce855de42c3d633cdafea76ca4d937e9ae168d066983351b58a1b26364830e77cad4f9d5265b4c05cc6262f062dc35550cc139f7c8c5c8f7a0ddad2ff40bbc2854aadc4b721fb563ea536906291f4aa4a61c5aaee8c452783ccc26137116e724085fb6cbcbebe192878096f80e04f5cdf55ff378bd8710f83d2142fc9593e145858ede7f1ddf35373a406eaac309d0a46699f8d7fe8439648342a97361e9da046522b89c43a275650bb3aea0d1e2405ffbeb876b3f9157fc60de2fb889d259082b18c5cf90b751d5ce90a8fb8f6b0e2cbf43d51fffd805ac68fc9fc42770d005d26946025d7df5ed74cd7598be5453d50ced97df14ed5faafac6bb3007f4da3f0028848145d8ab88ff6ae2b5774fdaa1f043b9e86891dda87bcefc2fc4653c55eab92d8fe3c42c4af9975b360a4aafc2cf07eff9a761735382a4fa2ddb4f2e1b3418961e5f2d31e2cbd3c1e56e6e0b7b34c3161f2ca1d7c27746b32e0701a60bf0aa0bbb25081fb45054e0fc5433400a33c75e0b5aacbfe444bb6b6d5d849258cc3d0480d3aac425b7eb43bf060a021d32882b70210a3a51245aef674d486309663dc5c3de4bc8e12bf4abe4d75dbb12009a53383772fece673ae6daecec132678ce62cd0f812a788d2ab531768a63bbd679a9c4534832a39efdd86e9c0488405434e1cf32f80f9c0fe97324902cea8f60e22ccf351797bccc502358c34f873b74760ace432f7263fb3ddc3ae3d791cd96f02b4f64169473387686fdbf0b3e07dac53a4acc63f347c1f72fef9ced27e19bce480122870ac2b2e785cd53a8960d282524a780ded2d559f2a2c505912f36ef28d6d6572ee7cd8f28f080580bbd245f09f46a4c9a5b441f465528f25a170cfd657f87af6c1fb72619abbedf38e10aab0e5e22b5c6ff0ebc5403b785b01b231ca8e85c740e2bdfb289ec02ff902407"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"92eecd27f91f9f81956b0c38ccad31e28b43a403cc77283deefee28cd69fb37c","proof":"140a4012b9bb065d7da355b8a34a3b3a453f740a10aec1ee1b88cef8e8cacd5cc49c444b0b370c3d7d23ac066eeaf3f41c9da39e15f5cf4a1a5cfc4716ce2b0bdcb92f80381246f83f3155581076780966c146105e3653c67460db145feb9360368360cdb64c9240341381e7a77e81d3c5b6ffa3303f48feb0114a7a3f84273a73c13e09a25f8cccebc2a663dd32082b48266cd2beceda1dcae79cb87ed99900a23d47165dfa9b1d61a08819cf565e6c9d5892688fc201d543c1853d81105b03a0d6070bda2cac2da7c3cc9e65c6487a9010ba81856047c5af9316deb644700c3cfaa4051d635b8771a6e9101ea8a778331e0d69f454eca1ff58fea418f7e6655e41a6f1fb83fc8dc57acd2a6340acefd97e01c2cd21a39dc0dbcd46adef906a38083bfa4c6976ec1d90e35bd6b876663404ab72b0b2d17bbcd01034b260d57566c96752de10e11d243ae929197b73a8454f89d6ec399bb5895aca0e74b4cf0978ad40301a69b09ecdf4b43f23c8d950d06e737ddfdf3a2e0e27c53fa5c23e3f401aa84b64023fbb327ca30edfc3dd20a9f5dd4c82dc49ce1da382dda0768c218e8744e34c4fcfdf93fd308292262afabea71f6f25b20f94be2c45f6ee95385f42d3176a347c75f0786c4183590ec89db0b6fe95aa616f9535d2cafdbcd90a7006862b7b4ad3326c3ec169e68d407bd234c810191c002328e1eb6ce74b9db26dac43159e3d5dd087e5116625825e2fec8049761441dd75860750c778777b583e42e99f6eacba26c273744f62dcc3c3fe471beabd83c13228246c0840d6a75971b8180cbd22e790c7b6558cd2c7c02a265336fcd6766c76057cd9d3848a756e33c7c1b3610826a93ee677d9c8b96f4ded14973a831f02124aa40c422ddac632078ac36b27aeccdc571b948a1565af874cee625afb9b2dde10357d1bbf2f5ab404"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"32a8c29880b743d2784c7bcba197c3bb0caadce2da10d2cc219d2f664142885c","proof":"3ce706f021199792b9edb082c75246e883156be6bf5b0c7543198e6e6c0cdd11fe2247a390838a3013f3b89acceee5e1ec1abd2dfe13b56e335ac5779db37c02caef1aef092d16e91933ad1767a7a2fb2afada20e411bcf4afc6d88f76f7d320b2fb4f2234e16d8dd8f74037ff85f78038f4e1438928105ecc47c919a74a1a7a15b3c27354985dfe699a52491ec747c5168d6e60ce885b4ff52544957dd8c30907d74514153dcdc16e0acf29b6fda9840abf8c2ce623ad6226f9e67dde353908f78d1ee2d1d4d876a5cd4b7926518be25a03c344eecebd50612140d515b31f022efd7f0a141e8156948dfde0120837996ec128d2c14a5545fed660f40e3649632e454eebb2c087bb9a5895c65f45df562f22a1566816812054f918e62094f70a8632c451f5de00f07ff04e136ff49edc24a0c67286042fa904a8cb89bcc8fb0a16f1a890a5f653b3222fd428c1081c6e3342a306c0a70d1ca18ef6ab89e963678acdbe78543d3038d9b61f0ffa1401c1a2f56408293a06752e9882e092c8165ec4fb1a026e4ef6867ccf781537ad64f441c8e42b0e43ef9d0148506145d0954b20f2af4514a7a5e8ae1dbe0a8109ffedc4608477bd5644bd2bcc3b3558cabf1a72e68f87c3991aa1a7d2a358b3996c0efe16f103a9a309b794596c9f6c3bee3ba8bcf287c9f4903d13c190901e68941521d2f2ac0d5b6e60090097c2b3001e3dd6d5f6620e377a21fdb9eaaa6040de07672e025e205e7a3daceae5bc59472046e6fdefdcb316aba33266295c4850127529c94f443938a8e4c7afb1a11b86994c34f107e68c1b07d41e35a32aaa84670a804f2d10786989921102d66f295c605476dd30c9d5f70d1b226ed41063ec415fe846ea69f41420348c864911ccbcab0c516eef3325cd5ef3f1fc9f286dafa9e98bef5c7151dd5baa50f79936d2b7e902"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8c603103fc6c3abdb7106689d56afa3fc4952f9442fe1c8b2feae4dbea43535b","proof":"64deb612164efa3db298cfc3b7896481f7b3af524a7a6e336f32c5eaa1c2cc35d8a0598ad50940cdb8875b79490b2637bb3919ce26271894ff256db730eba823fcfc73ed09916e33b30d65529efa6d6bdf524686131015f4e8114c8d4b54fd5e6a3d72bdef1e2ab93e8911b4cb8b78314c1dbe3cd1dd9326b97b3a4ee272db7a0879c25f7832530975b01b4c3deac8c00e775c755bc79c47cf7081a49ca4ce0ab33572f314f725b08f67a7e51505ac3b9963574e7e64cddfdda261313fcc9d09f7c8bd07c9e422c3690be94a1cb26bf6892104dc9d3636bed5cfcaffbac7b70b44d16a25b59d48d295a452970958cc502fd1f8c37011e6e46f79d7b47095f95520394ff07d59a71c66c4596ae5ad727bc7d1244640d6df8fcbdd78852246a373e866c2bf21a0ae7a9da59dc88c70bfb3aa20fce2cb1115f35aff345c4f228f765ac2433a551f488634b787dc5e995dedacbd32e3e1d52d580c6f9756373cee1d887dcc56b8732a4f4084d3accc0e04bcaa1fc14ee888838043963d7491fd240b58132e8895fe9e2a75729913b001c4279abbbbeba6c601f6e545a56e90e1011a5855e1b71227480a5dc46c7e010995078e7bc213647a4100fa4125adb253d22d8cf647ae53ccf969392260b4cec462ca0d7564789825e2484a064f7d776cf41ba0598c1660a1cb3e07134ca49a4b071e97247ba2c1811b2d526c76f84f6132571c2de80afb9a134d3f1506ef8b8738bf30c16af09b800f49c28f6c413177e73dc05e61286855a4052cf71bcfb6fb98eb2d96176bdab6090a3af33b3eedc93a3e4a1f2fb6ec6303cf648eeb812e14549cefa5369807dc48af8c5ae1bd5cfd100a293900df616f187c806d21f75d07213a720e9bd6a3b603695f3cf4352d49650a1cb1d9322c1060904ca2f0330754903fd4f37d5a272acd210c760da50a1e9703"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fad3bd26bbb56796e8895dbd9571eaabde124445204422ee94ad807bb89a8f2b","proof":"b47539eb4b14b876ea39c704fabc929c91752bb46ce00d2a77108d8b2e5d821448407249ed06b4e3834136c658a9124dee3ebc49f5bd0da21f4fc926db272f44ac7adc4b2018a026e0f9f60a7dab8402ff71f6656350246d6683679b4cec9f1cb49964e1e8d0b47838bb6635980e31b33062d66eec6592d746f7eb17a9cd7845402846f2d12b8f3db3d3c891e917a74223d48b65ec0230d329ae9ae037234d05b760dfcbc1257bba8ffa745ccec0a1857cfe8e652af6bbd1dfd653922a6bf1068784033ca09d4bc6d2cdc72c56430eb508399e4a76f41a23fff4740eeb634905c6e5cdaad8354d7d25c8c720920ce07bb5c84b4f9140d3082e4fe2efa444413636d84155dd340cc075c3d50b08af5018c0faac6404da2463dceb6aeb8da7ec5576197e2781cfb9c68fa35aa5cf9bb92b0e823a49b1e87c668d144db0b11c882a289778801832f6fe50cafc143f80fae25ca152f31f11aa358adc52a659feb65e60b1f2038c1808cc58d076557053d150fb09af7faa722c22541de9198b6c46163ec2b8cdd20ad07257f655e1b8018353241262e1bf1fc2883e4cca2a2d894733847f11eac04222d0f15ddade252d55be197456f83ba50c25c0fdcc5f75a7f94bfa4ac0d3a649113db0e6a909def493d97359cab6600607e72a2dc6aecc5a714e4a2ee464107b0a1fea19d59c622e2e497f60c01f531ee52acf400e90abf89f28ea3a97b959b59abdcf02193141c8577d60e32105cf65b3d694b9ee229469297d64b74fc2f896858c0d062de3c96cfc8ef64100b8a2246e74c828ec5d95d3210fd4b2f0df9a587552811e5e066ed148a1ae105eeea1e8ff097cdf8258cce76135824db23ca7bc801b6518eddecb3b7ecd5f8770c3563fd2c2db5b070df1a3aa075ca201dfb404ae876ed5d5ab67d688ce432b0fd9519a97dbcc35cb6e6a12d40a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"98aee7d60451e44457b2f9604e0eeb663dac02a46e1750c93a4ef877f8029728","proof":"f27d356814df10e7fa7e0b3c03e9ad5620b87ff33a600fe4c25047af338bf14e56709ca8583bd55e7545f0cf0e560d46522be7f77dfa869bc8600753bb32831d14ea8de9106b8acc03452c25d8fe1c0ab37e416e431dfbc512147135d3975658ba4586e8704ce54cd1ce2d82ee38dcbb16795597d5821c7bd6fad1c5ad984043d160899d310c3c5657d277c785b72c47f7a159777f5996528caa6325d00f5604cd5c1523e781abbab63613da59b9cf7381ac1a53d20e2df149acc99afab3c902d3c9e76a3b79a68ca03fbc317b5849c77cdce59ce02f4e6ab96a1d77e3cd830098f95bb3434577e1ad814d25a43f7fee6cec38cfd647def76d2ecf45ce8a1a1524adb9aa4a04ea61c634358a087eae41c5057fccca855381bfe48074de9ef538be05cb9d19a9bcf88957bd7cd8c4dffd7e31564996fb977a75d344dd0f12153156ca2d5bd9347801091456546ec2a5e1e081c6b6707c27ecc4cde55029c2a57f3662398f782a38ed995c6cf6fa4763354dfc67a76f1aa8c2b08d30deb71e733608b86873bcd39d7c0a7f76e927bc71332f54443bef64f2fa008ab3f5cca3a63e56c5cde9d924b6ecd02a433d8918019be6cd62c67897641c54367b94e4af8255a470a8182156d0658eebe5bf15eee7d87293203f6308b642e4839483b7b23f5ba20c8808d558bc05792827a552dd769bb19eed81a402b85628593e9bc46faf292616a898628b719ccce01d9edfaeafdc8bf5f52d94a27c08792336df768c6e15ece053e27c58b132243211de55a13d64829471f7946cca074ac0dc1dd6e3e5182e407ce164cf97975cae507097a4303f15fa0a704f4327f8c3b1bdf18c60bb6a12409b97276535fbc03ffbecb285eeaf80925a9b1d010324512949f1bb98050511cc997ad1a8bdf7a179ebc340fac359edf14ad66292449b502fa27a7c838d0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"76803ec500a3d9be03bb990b90a780dedf406178845fb392454187ec22c1af6d","proof":"842b82017af94e47892be451cfafee5e867899a26da4dbb6a67ee36732ae38786cac219642b0727114d330af0f612db7dcfefac1fec484ce6a2b2bfd6bdfc22cc40763a7fcbc22d1c6d6b6febd2af0188d8c5937b10732266bc7daedb4b0b34886955526c6a613cfda1d5547dc91bf6ccc3dbee4817d46936e4ca8cf495a621c28ae678c1faf718dc2958b9ce09d9e775d43ec71ae4787a2a219ef242dd52d0e65235eff9b4a918bf7859ae029ac62efffa63246f3893fc7a020a368bcd00c07d2ae0055b6f15baaad234ae06f45e1c968fec1ca33d79f03e736fd11a0267a02c6a0483b56e8550275719ef9745c62a1fae144ee8558a4667dbd8b1f5a112c0b74674e5483271830cf028805454609cb6227494468d48f0561a563eb2303a770ccf25fc04a1f5f05a996c0e38ff80fe9a51c72d686e5485521085cafa94a5c3a665ce05bb6e690c0d6b02ecff3521278f2e3468f23ec80ad9b0699f89b8402314625bb15be49bd6fcbc07014fc7f963a8f4856e4fb4beb353882f43dc277d133f0471c89f162ece3b5b96cfbf6c4882861ebaea83fa39eb006f8e1221ad5243780387f0281d2dbfacf6a189a4328d44d5c7a3e67cf8b76a039cf92df7eff254c98daad8f3149eb6bf726c8d5e7dd01f93d042e9ef36a104520c853c8f00f4f7356411a2d19defe59b201f8b6cfbebf9d540cc1c1468310c13b6acad604fd3a75860ff21d8e9346d95490bfd75de16daa775e5132634d74ffa3bea75be5273b3dd6f7f94b6252d3ffe9e4e93cc087ce1ff12ba383b3dba9393a555cec9dfb2d5524760a9ddb1f2ffb375507c9b76026a83492d92ab4b6707c7527071df8797944037e288581968efe0b875c7f773642d86a42227b93e6a5e2e489aff0537ec409cb2c48d0821b0da84efe183bf1c373a1e849120cd53b34aee7bfcc8235e9160a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"66ea9bb2ecbace3262358451499636dbbfce1d66cbf94bed71251af6646c864a","proof":"08b4a32f78fea4ed985f7b3ccda4723828d47ca616675bbf9a20b48f0a660c79b46d1496c3740266a4012fe4df078ecc6bafd3e5b45268590126eb910b84e850a8e64cd29bb683756ae2471eb5272beace07723cc8a7a30ca9b062a8d58c6a2ace106d86c03f9abd236ee3b792825822b44af2b44c8daa2706b812ae8070de492db5d559eec4139562f94769d808f675d806ebabec1a844eaf96912da143a10c3e7351f1204747aa3eed8df88a1d558bbedf7e440933406e1896aa5df1052504fd77f87eab4a027d5f545b885f2d1149c87973e0b0c5d4c2fba5769371b9a105268a64efd3affd7ee4bd1415e4a373c407d78e674ff5d9c0cc7a2c2f871abe298ae7839201565b82c00b9ec6e39ff2478e009c14c7f879bc86a758d513e2436b5e4a3798ed91e0a15c9d35d939468a4c3f20a83ddbbb69f12a725213037a8b25c6430e29da5d970001d53ffee274c382ff2881293f5ff7b32ffb67b93d4cd33ea0e30d80c6c85c38ec51db0e5009fff9c204149a19ce00079e63b78cd0d68c392cc3f188d5e8b20010a6c2f41b82fc25df4a8d1a7b114488ae7a0ae45c0fbe3ede84664e93ee22bccd7a7f930cf6e6c84879e10f1e7ffbc7e21d2ca9635fba252abc1541412cff113641582942b8e8dbf6ed0b669979cbdd0b17850e3ac1bd62ce11e7a981dba25e671336afeb420c956a89f3a0552e14ef94e460987556da780a5c81a60cb2f221ed00307fa6548e412e040c62ed7f02c6f42964c275dbfe5cee390ceced322594e934b1115414802e0e11697b5452ecc572fe429903ae131cda8e8dc9eb5cde391a9a22b8bbafc96f0f6ae2924c0670000060ac2e083fa64f0c6ae90b3135000f5f20840f5b5f90f0ac45321542124c9e022b483f33a1830e3ebd8f1b1cb485e25f821de14d739acae8349c6d82e9bef6cc381e3e4cf7f702"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d450f141a183025040732f9bc7acbc8b1b387b7199a59a956c215766bfba9a38","proof":"80d33d384f177f4d9f3937946e4a73e16e5c18313523ba11455d8e8a9997544646a84fc1b8b5262956ebc9555a494391cd7d91eb951586a047b045064ba9b3196cb6312370902614e5c6dabb4d904429d25f1cac128c328228cd9199677851300c19ce9c779e30e339b598c8b521e44500feb15313afc38d06484333f3d0da51baa1db7d55f1924b267f099a22ad770b7cfdfe4b17b2bf8ac53da3d8ab4a5b0dad95053b0c0080989c769756b9187bb2745a026e36df80ff8354e8586a768805694c3cb421a38451fd44abb1d7b17a0752603cfce75a57bebb07adaf3fd09904e481147faad1b848f9f4b372241ea842fbdad4ddf4c8723f2104c17757d2ec3b384b67e9799217462d20e20c5321fec158fa702d92e4f3b326bfc05b212e896cb0f841aa8954c1a85d8c0404bf319242c946927c11305a6b20ce4050a5adb814861350eddf3254bdc95d1580b18b4fed4797de7479a6cacfd5d7f55dbd90ce13faa4fd61783c3370ba70d4f2d06f2500e14602614cf41d661e9a890c2b10464cba4e93131a97124ae0e49e16077c86f518f6b4cc46797346ed9fdc2940c54b0cbe525f920ed5187b2f3fdb1bd1c1464b59ef8d042804c69cd333bd86998efb5a38a5765c977e09d4d870703c1cbad487058fc370fa6f5427f46f8a08d9b85a0cce0d96bd2ddc13961bb5bf3aaef7d10cd98757cc890fb16551d7bef63c0d95260e5716447e0ac1e54f714631e1f5a8ceb88432d8e2ebd0e2acbdf877c934c9565a465f73896d4682b293b6c8b1b0c3e131ba778863d0c0b88eb35fe320395e4e049be7ab2a9ebbb81ce756d412f78612c64b271612f725c3fba07edc8b517f4a4beef27e2ed1ad8e163c75a3a5ef8f1c219c8a08f5203b3e04355ef60bfe260303ba43ffceb98f9db8c2ae81d8b7c04b8dcfb3c1934069a8440112a05539910b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"dcb13e5bcd60c983784947db4d709b2299ee973b058cc33cf6b8c0a3ed5dca79","proof":"a205eef66c36a3b0fb0efb5888a0d5e0a9296765cdebc6d88166ad19054efb51f831a3bdc9dbeb1e0b3b6dfc2f8e53777557cb6726ba673e81928cfb4f418d614ef74b95ebb3886349df443e6afab0562ba69ab15e27b4d397aa9e302827b83fc0e4cf5c647cbf519bd96db5f282d67ae170ae977188a8848d1fab55b7ae03391b66242f509f4879c8d88269215104000a68d8c29e3c103a7f58f29b7040c105f5b78837b28848e349334163feb686995939f30e851c2707ad9113eca871bb08d03ddf439c4c566e7d99ead4e769dd28d9bf7470d78401d03582b3933df80e03d63ac4f1399963d2cbce7988c5550954a72680738ed4b1499596837f878f994450d1e71c4ce16be729fe4bb289a4617971df9955625660f2b2bfcb61b6b9e753522a1702990c36406ff89eb126fbad75fd14013a24f4772ac6f496b53235293c8cc46d606d96120751e9ab37a7c5b8249e9e9847646db4927e87533e5beef571c23b276245b6ca67bee94b684a31de54056ca88fa03a1d8c0d8a90fd6f5472125aac9f23b4cf6261c9ae66892ef94d369deb00951e96cc8c82a51de62ad9514e908010fbc1c73422ad2c1eca4e5a75ca23ae1c9cb8ab3e3d77492e002198f312562b5b73f3633233373456c18b1dce553936e42b608080755e4ec7f3a55d4e1a721f54eeedc17a2b62829be5f09ad90dcfd80a9b74993e945c0d035413dc18137471d8f78320505e8d97e0c9c8bd099b880327817ec8e502af20a75d811c8b66304f914a6564ad43ab338afe37865eb377bd750286bc0c01fa507f1e20f4fa276c3429c364f30631af1a13f603a0771ca4d710a364fdc320ef4905749107f9232e86449ab95d0a9a3a483a443e4c9d29fdfb368f4d139f871a13889ffdf8c50466f1ddb04edc07c81d2e2556a8ca8edd5eca392489bd2988da83439c40309902"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"e2545fee00cf1ef7e5dc11c8e95eb55e4d53871dc240bfa184efaf41a3967928","proof":"d8d3de71a93a45bd707f5998b2410f28fe9266b00068457f8cd754f15c75be0e640310c49a2cc33d7eae03a36ab4a3f8b8322e4e1b94fc1b7e79baf2b7670a045a6494eee985ab95d56289d324a3da5382cbe5af1b6d80091ff3b7d0baf49b63aeae162a20c20ef2148ec1e44bfbf97ab321ecc54e477e2caff270a765cd810a82cf63550d66db686128809aca1514af73888f70a6dc1e68ef1184e7c3262e07aea28cd61ba84cc3362795ac11e471e8b7b071df73775a476fe9fcb1a075dd02c2cd4df7905b181f0768f249dedd123a7c3becb6c0fab66a8732bfdafc31150a2cfa4645f466b5f0eff75137226d55e2b4fa2a4cfb100669157bf8ad6525d740d069fd454e05dfb5abda732b5bbefa81a6834cb53ce2419a70239a0c15354c1f4e13036d5f77b105af46f0f1fe590824e4a1409df861e5e150c17078976cd529321e6778fdafe01100133d8e9049a3fffe068d459c29e5e84719511b98322a6056c3b9002dc3d3b02ff657777e363e376d4d015550ad57522a4d2224d38bad4bce4aa4267f84c6628fd6b973c8d88abaa86e1ccd724406e98fc59fd12593b04ed4f7fec2d92a0a7df9354044d59f67cd1dceaa60a57de5c50e10e577dee164083c0744cfeb7ef96337555305e605d9589a6096d7dcc7e5148955b9442acc495da40920a2cccdc239739079272e819c581b92757950a9b6fbe66adf9b774ae97ba034c686a683a2e705953486912e65a4137fe27006c7896fdb6d66b88addbd7d24207510dc08b01cba3b469392770f8ced3679c4a8dd3f3bfab81d3e45fca04d562a960e0c6e9c2895b9dc14c41880a1d29900c395a9f2737433acc3112c6b4edb2e05f634ed0982c35d5eb5f8dda834df631b1365849e8e8c1e7555a8592d00151ed81bebe8657f5239f9f7ba258c53edfe65702d96e363a42ef0431cab0e08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4421cf21b5452e95e8292839b30eb1d54374dda70e8152074f4efc8d84972479","proof":"8ab842f35baf4c200633a76e7bfac905073f19fbe9398572961f2b7aa86e46507c921f502e34f624749855a20f9d2487028b9b4d4cc46b1e94fbb9dc1b6f905a3aecc8defc49c5d3fbd8840f2adae586b8d0f41809111e6a8e5936d43284194c52a449182560d3bfdde24b533e91bf98c3860732eca882f1cd12749441e1b22efb7f06f5f1aae4e16783f9ed97f92edaac917545e9c5d679961dd150ba2d5408d11b53f647baedbf4bbf8974a9cd33dc4444583151611a2fe05e12a46bb4790ebdb0f68eafea8a980ddcd0665bd3ea4ba2f34e424082d7b4759cea62461a8706ca80e037101e405cb79b0008bf5b590ef0d54e45fcd82dca204c25198d7a475a1aa8bab572eb6137ccf49860bee9a85e263e3dd0a2611a0659260c34ac11033696dd9247cabf1389b33d671263fee369a1815697da20dd992c58c973a1423925da4327816060ca62503e46e0f70b397bfb2768762d45bfa8f760641ae2eed0721a6745da1f4cd8d3cf9e6879f8610e7b499963ddb5aa05165432c3ab4a00d32466c51993f4aaa128b8ae58ca968546984ac8910ee21c970d8ea07cbd46a97d33ee20f0b561053709f83ce72392d6335e5a473bcb8c0293d35b1675ca179780150642819da49b0c220fc2288c1383479cdc15cb0df299fedba8549b003cb57b5e06a87e37016b9bbbfbe5af77103437892208d70b6c548aa00d37b0910ca0c60bf0a419a4d3b0ea1568a7eaca33a5d84ee9063aae1c214051a034aab5393149059ab7321afb2efea46d7958767f2f2c2c3821f016872a5a5de75dd0302cb0de59d8dcedb93b42999746b645679177067a64458460644b2cc85ac4f5a6cfe68a1b869877b30ea31db3e0949b351e75bcc29ff42cc9487e2df447eef8d2c8e9250a1b8b305f20df467d51e6c7343999dd83df82ee620cf3b31e982bedf2259c2c0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2a67bdc9afbe741e188fb881e6995edc8f1053563e13609dc4509821a169a37b","proof":"44782a3122d522f91997ea141e606af4b590b93b2c34f8a2c32a28eb11e4862f7e035ea78f58716b854b5458d1f88ac98344009b08d2f69d30c524e322316e33eace63cb355a42ef573cb31ab57a27306cc94c23250e8ecfcb2fe9ca6326da3e5c3af9bd8fbf0c346e7a66a5bb9ecd9ea488b9094df2fc336702c9243083be76c7e308809df6aad2557e319a56bc2f4fd9c41b5d3471a6756c178d55b2862e0839bde2516cb297963f0976442c7ebf961db28ba5425c045db741c38d98a597089289668bf9e84408daa74e7383c49c9a017dea06b55cad15f485ccdc46bad606b2198f16a4442b20158eb48799e57ca84d110eab407025e56df54a2f0c62bd7ce0f8ed6f91eadc260dcff1ab505f6817d4addd207e6ddcd1aed28598b494fd6b44abea8182b124f1498f431c63248bf6591d26afd9db8ab53ba5661f0377d7689c59367ff1f6fc4cfc9a185852ab0fe963d922ace99a34ec063cf1b29dc0a0760a2767bdfe4cd1b8bc41990c0d15e5130bbd9e8ef22dcfd40e1cc72b358ef4609aea91447e6e42b8e97810921ed5e66f9f6e6faa7f6a784a8705f66d8cc0866b764c8c91305451526243436c0312166f562b9cc7701d90db5e85b7b844c2b65c86425963c5c5066b2b34dd03b83cf3d290af7d113ca2a045ff76c6373d1297371a6a6043c62c4a547a0bc475902993b841e1d1a28d55b9fc9dd9b642d47552346a0f540005695e2061db817b3fe5cec6d6399f366744e46b400ecd63b2b57d1cd6b20f76907771e68825a7d1ab9d2727baffc2559237102598b153375dbfc80056b7bbe84d84e78c07232c90157269ecd8efee500ceb82b5502af6bfc01b3c1120a67a61776478cac1ec99c519198e1862600bdec58a4d054276744a49aca206ee3d8fbf11c4f53eead08115a0192975739ee2941da073c2c06249ae1744d305"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f248449a5657a24325a53901f5989207dceee70c25b2f6298c2b9315d7b56e77","proof":"2c5f2dafaff9b95c918faf941580335cd540ac0968cbbc7bca67fc56484b891516c3d17a54ed6cb0a72fd8ded8221ec7b612e5b834b597c4bafe3fecdde45e52ea48f7caefc88ace4d4f05bdacaf71fa677a17bf02d897d7da8759c02bf81376f480618cab4231a7d01e4a6fcdce73f505a05bd019f02a11eeb0420a21156c437e99f2c17791156846ae762c68b60e68ab7635f4fff61457b4ce6abf65b6a70201a0b21e57da6e898c341061ca5b90a510bd272bc681e0c6b47e88d3bba4ee0db8a0ca4c7a3e23b3674d84892343fd2133f3da83c92a998119860b28e070e10e38e38de9c40fc6435e7aa74ccba663da3f5fb096262412897e9bf577207a3011e6e440dca4257f6799a9e2551afdb2750d35c53f06037688317742b6319de5685c697fd5596b675fef4aa0e2ca649c08adffee157cfbb3124beb8f9ba4d3e539d81c595b9f825ccfadacf44a910488602cf1a8ca053d46949b828568d4e48a765eb947001fccf60ea6b1fcd4f639cd9422fb617dff4ee701d5dff8f4578cef74a4444f486ba165a1da25590f50ee7fd3044bcfc86205d09819cea42b2de08d3c00290a6bf56cc3ab008515bdda3bd91f1e0d6be40db8bbbb7edb6238871cf238c417576ac93350712514a1630ab899cbd7280397475b68e3cab464db915dbe00046e5ef55193c1e5a28e31d8045b5714528ae9281cb1c2e174bc02dd4b43ef7b185ea5926af8630d1321b85a057c99c5f479798e1d6f42c0314d3d2b5ff3bc6eae270cbe48d73052e86374adac45de7d9e506c90ad324d68d8a00e4a571f9a450a0b8969fb1b9ac76970a515eaacb38aaeedb2ced7269c56fd339580d9cbff20aff1748352e7a930e47cc2ad5eb6a00253fdac39e4d75b8328d651b31fb38d00dc4579d58724849e112f9b36b9c74c1f997b6b43d9e18118145bba9d6d1bde0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c07a775f39f15bca81e16f376eb2a526502979ccb4a1638669052ace0eaa7526","proof":"300fd60b3a3bb05449415c43e1bb5842ee2b848d6e64b76b26c7cf5919579f151017cb1e82476b3e3214d33de2ba567c5b350628fdc0061d235ba54af892c357e8b8445b03ad3f2c696df31e3ed9c2674fd1fcff158a02a4d4c613f0dce7a262a665d448cb21e01eb555216c3c571f57017a67ccab2538a1847b75d298323b14f974fb2148442ebc8f1d69d75b6be46760d79b7fc577d2c9826d429ab7d23002776bfc03a93c3c4353d02fe4e53e78ad8d9dba806b536c3a2806811b0fbd28062fcee66992c9e1508a564c7a02b82390dd80550e80a307a2fba24e867b61000e9295a0812231ad5239fb7a09f3de0bd4f7398fe8e54994859239410419cc9650f64da83516f04fcea402a6d71f39c9dfca43cbb43454b1a11ce5568ba8fced7ad45602227827d6cd8b8b3a8bf263cfc4494dc9323b4c6f8bfde25e61ca4c9d2202c29eb569901661f4b0bc90aacf48c95875e6975623b11836e2dd27b76c74600af67a6b99ecad054e883f4342ebfd66002a3ca736a3bbcbe3900f48f585f5546a87868f789e74d8ced290ff8415aa55fc90880428f14801432a126184aac37f4ad48300faed3caaa0232edb912a890452cf3a48926d86e177fdedcaa2d85b0710f98c2edaea49257d324f7ba3ecd0e62ecefaccedda326e2603d04d33c6900bf68e3b47fa496bdf17e21ce825724df7d7e18234d112b0d30093a74eb3705e52ec42321b2e5f733699f9b2c6184663dc787bed4e621b955bc704776f5dffff7d2406393a99ad1c1fac3bf073f16aa1fe1dd5c84e6d53de8f242ab4a89c2ab11a58225bcc3d634bf3777e498b590081d891505157bd4c3b5cd1e54da5d385302ae65506514146b37ad08b35efea133e7e8602e496b2ed426126a376688b5dcc02245f54c7e946d68f93f676aad4b34e09609207fd22a1cd6cd60fa3aeaf92b903"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"102f324ee83c7b23b8f83ceb95ec0dc10dc6d89f19f6843b5f44d4fdc5f00e16","proof":"3ea2b370b09e68d6be8cd1f3cd9453bf57d86214817f82a45d2fb082c70be91c5a1e12f326feef5f24a081c557395100c237f5729a305bd3bd875ed8b298cb6de8163a1c0b08f400d8a920c072814402681fddbbb628840392e0dc8ee4ef77497a2c263bfa0327fbcca271ed9a3671f1dab53eb8f3322321b465cd59e63d187c4b76ccb0e759b043a725d245f1e395af2e1650b0bd470819c25722d2c9e5110fb6d02fb437733b1ebf990098eecc6d4629864f08ed72d3a19c71825a6cd6c502362c2479ae8e0e8df93e4619019a87d27ca228e8af69be128826568865d1950aa204d300b529440980277e0063092bffd1cfff476f5c01d1ca0537a88cecc074ae0ce9680ec3ea455605ec309fe3a67254fccc516956b6ff30853258c266f37a86fc2216339ce386a2fa95c6f4ccdc1e5b1f3657990eefac7f48daf3cfff682afede06b7b697107b9a14350d394b4797e046b14ea65b0cfc5b4474b520de220ab40dba077cec287f79ca1d49319986322a358016421b283d5f346a1ef9c34b1f3401ffbf60713de459cf79fd0574661e615b5d5a527e58e782f771d5eb2259552a10bcb52f397b31fc73948e0e8ae8822c3c60969498eb468699457af5e0b174c25c59d8e78dd062491c08add8d59f635c9caa61b94463b58033a276f23f4b0e6abf2ab972bd4879d645c451b925311f44c0e3bb65f8f2a620b3724fe7a7af1146b3eafc8c91f6e050358241bc29b9bdc0caa4491f916cc7fbd5e4b0c151c16a522f9dfdc09d4d77c95bfea507fe62f4c4ba9dfb82b1da3abb33bf9c0311de011c29773376b3794167bd9e801a1ae046d32f4b618a73c0a5695ae7697fc2525dd688eaa606cbd0890933c9323d1ab8c951a97a4946c16338b8de82eb10a4620c74eabf181ae273ee2b593b64ea8176f6447b3878f64594c083f6713d753a3505"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4af9e458b0cab2154d2b34d16ff6aa68f415fa313eb79846e9f84a0fb978a64b","proof":"ac8007f8cf5f55aec02c58bed868daa01c78f6dfd4979f0a21e84e67d7acf20c5a27d66041b2708ab7ae89167e0c1dad93142d971484ff9fc69b11a22e0b69565cda38a9e972914f258e0c7640c76b2a50f9c417cdedce187bb603011a17325a2a91857e4b90f71e46313a908538498049fb84dfd1c263fe0c9467085e9c017de3d8cc41d9942491cc738e841c697ad68eabbe2925d6d4fe6246e7f9b605a40e4847f85578bd7e749132c5425f47a4f4dc3fec662038f17265b79f649947a203bd65213e42b1746d34608efcf63e98d239d8985086be8f99c90784fd9daf4b0e245beef1c6e09bc94e096ec813f52ec1f983b9ed83c01310205fbeba549c9c123e33c2225842f57bf3c42b6847730b58c4a906e2effb0f6f3ee7444a75e8f94a4e77884aa901656d4407d2571f8afe320b6c98033ad8dba57d6a5ffba0c9ff38822c46d90ed020e5d7caf777b9de43068e02080547058d3a10f2749997e1b91b3c2b4cfed6940ebc9d237ab6032548db9f8c935fae76a3d0d538378eb2a33f621e3c0d56190cc6ab20cc16ca8dca35778fb693f1460bad489d1ec8f86f23231642473c11456541966ccc59d39cf91e37927ef343bf191e58f395ef299ec19f019e72a1af5f11206c42618f207d19d90a359d5df145aa02474b5a7d078e98bf7948e2aea62f20ab198b31207d5a402f9100e35ca512ee217f89e7dd8e6562636572295c911dfba503cd41a02310446004bfa514cef7aaa9c095a1ee2b76ca65530a7d2743e88b5dbf161bd361bb6b12e7f0312739f4d407464f3f0dc0ceffb96222a23b385155cf61de17a0c603357ef711e39e7c48feb2a43c24eab616d8a45ac44b460a2e417f3fbee1d6394d0d7a1ac81553e105ddfa405d36a52b6fc0290dabd2f50ca582cf6ecaa3f6b64d86d89fa47973efd798b79aabdb43894afb5100"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c2616b5d0738817a641436acbba29c64a7354632693ffe908c20d3a99df5b95e","proof":"8e916683bd4325d24a697a6b546bc894caa838cb95649f8fcea2f1b5ca55ba6a9668dff26e93aa34d97fe2f672a724da6d1e78176d2b9d8ae10915c759a7380b22b9f92023d57053e42bbca92e0a43a0bb6d9b2a30e5b7bb7753c71752f8414a7ada3285dcc089f0f63bea3edf0b3a1d088ff9ac8411d19e249541f02a6f52342f2d594c4e85cb7a0369ca7e153f50c652dc3e55ea894e949942ef18d0ba89037a6e6e9d7291a5c12f155a62ea2c59d5bc96343413f946497d93eb4715a9ff0032368ffb4e73d868092ad81a82b895f6e0e4fef6d4ddc46f3acdef94d5ef7f0d0e311a9401140b94e88bc0b2840873476b2e2fe74fa5cc247df1e8999c51055fdc89d4eb7c2a3d739d13d6d18c1b167f1b5882a86157d4dab326cdf72a636376eeb41506b7f0b64a27d5c70dc9110c8e6014cf7f09bd92824fbbb44c2441764cea065e325312be02088989fb22137f75df855ed9a175ce188918483f2f7abf6c14d1b128b633e2cd397de78ee9ed295b0db368f4e59eabd0a563d11aad8fa54ef429a360ae610fc34f723d4ccb99111c569fc951df9d0cd73c987c3a80a5b606c478c2b91b9f5c71e831ca6448af4c79e12e4f27e0a5a6415e57950e2cee5137eac4e5410a7e6a3d176495a4926682fde59fdf1b2f1d19e050a58e62f984ec11c481cb60900caac3dc5bb682af78f9feffde73aafa0791549b92e1aff4dd964b629964e996a548be426d1fec2c8394a5ebd181e2168d69117ef29d0b0a2d2e0ec267868a79e0747de6befb430a925d08a43a982dffec3736424070e7944dc81a4edf1617ceb43ca33f2debe55891b02411984f7f882b0c070d38e7f30f0ca125189d2868dbd452a638445da6476878731e6e44563d3a0dc8742a6ba66b61d30260aa68428d99c752eb6f73123a7089508d2a08308137436a1a66996a7561bd03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"38b8352bf402093f096c6e60dcba3cd8ee5eb874d6380eb314e4120503bd4e6d","proof":"fe3dd9c9cc1bb6ea67fc4ede4fe397db5760e038227624ff7441aedd893f692ad2da324a5296f961d5c4579474aae7ea849ae7dc5a7183177a1347ded2708a21f6455688c444c48d9ffe65df5ee9a62ca61417f6986da54e91e29e06aec1b322a82e859e66b55ae0674787f324898d6319b40c5ccc4f6383d59b9448ebde6569de8b76b3d516d8522d5c6198d286cf718e0afe714361c875938208ba7f44260b0b90fb14d14bcc21de90ac1796410848fa03d9d9f8fb9a44ea0b9cc8804917041287f5aecb80523376862a8dd02df550b6d31b0c277784fe4077c80205b5f300c8fdc78b96bcbb897b84fddf131c48b12e592a4f677a70fa9d710ac4bf16993562e61d57f20512a633f9537baf95e541b70c54611ee88b170969b13ea9c55c6eea56e248843567f682eeac856a2a495dac65859ef15344e5bbbb0f6498cfad25aedcee7615e2ec0092fbee98cae50bc388f20853a7f9820578d21eeb53304057f03a7bee325fc1d47eed47f0af1fd68d429d77af97737a569109dd9d72bfb208c429af754ff4d5ce7fff48c33a41ed27373091123e6beff5935aa9fa97c33b4dd03a6eed0066af89566e0a967b7e63630ab7daf7a55091ee3c1020477c099623d6e3758df020392fa94489208a6641ab328f7a416bcdc999d9968ef7987d44743eea8d141b9b0fa237029b24f3175be14e3f5946f4ba6b437bd4a04003e3d01790257e98801405f8a0a190e1b3a03d9c44408883efb3a4efc06b0c61fcc83977aeb0675003f9b4bee7b6ec0e1069ae011517f3898ad9dd08c5f4008dee54de6406c445049995cb8e40017d7f8e0d6aae1cb3989e2326e79c6e4f948abe0b0c0dcb2093e990e67f0114b0e7a104c3c50817d400d3df19f754616b5f5666fade0ac69eb6f7deeabc722c47d4f57fec04bd4ecb4cd642cc7fb9386e6dbbd8f0140e"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"94c7169b8efb2f65a1a7c238a782a5288a22945b6474422fb098ca3bfe3ed760","proof":"1aaf8d53a043365de4100681d4ed1fa06ba1d4c41445dc13fd6cdc4276dd2b379a7511c0f5c86ebce0bca1e2f767ef9825a42a8fcbf8f2f17a33a534c7b5a241202062696c316ca0a27b766b2d47ba5c837f2f53e7412ae0af4e8cc2440544212e5d78a90b22786384d93fbcd54463217561dc0dd8f9d8022de61c8b9c0b965eebd803df2f49bead926b1c39c54bbacaf5307276beb0526a94e28e9ca4fbb005eb53664a87ad255464e5b9494fb289a95aeeb919a491bef0873e51a92f6dad0302acdf68e47345d8720bfc7a5650af61965dd5ce082b5974a8c5c5dca5a24b0a00b6d632867fcaf3ab78831172b7075349d19400105a36e8a6dd0312baa19d6c30a8af06c5e751a4a06471a7446667a93c8387ffbc5218fa80c5d4139d034d55b880a2b75d6e1ea381f79d912434b6ab7946a8e1b5172c7f4da92cd7684bae229abac20c7e532ced4a8a72e3063c23d86b25d4bae6e1562870d6ff49f6a7450f6c0248436fd661b59c532f889181f542d07f2cbeeeb50717f4f352118b85bf602a3dd171c6013aa43b05f4b52a91ccd9296d5e48f411353e54908fab5b196c7ab28a67a696c4fa64e3790bec7cad99b04acecbac4ee7dc5344d0ec99a6684b06f60f986607cb0d48f9bfbe055e577adfa5f42495b4e50d29e64d5405297d956ee0d1f68f8b35554d5cd16941621afad18b9aabbd09f1bc7638b61a6f26f4cd212cf21d59f85f32e8ac7ca080461dc0471ef7c8782fe6690a887ef97e41ae822328f9b646065a55e9fcb7e360f5401365f3d5a3381672d3bfc0229083ad265e498a862d22bf453b2154d0876b71862e9daa61237269f1f068cf03142aab552334289a6e167ea0e1d4fc052bc8c0cfae6aea34e1c2963c118205fa2d192202530e90ea285fd62d400da9c37575c61eca7e514308d54c0244988bc01ef487907701"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f077180842c387c2cdea6e0c4503d32f7927f4d7bbf8f869f13a434484350b44","proof":"5cbe46965b98ed214996ae5295725351707662cd286b2d3100ce60ffbd892a1dca4a1ad19574f0e2c7c7aafd087f35c31b8690a553ca9875c1c8aa76e28f2e52908b739b7809b37d81aa62f7c2926c7858acae4a38fb201dbd625fa37398d61ff2644db34d645c66d806a9fdfc02428410aef414d0281866a0b88af795fa5e4cdda6905568184816f4394eedd3942e84f0e3d4608ee4018f4781d2ed6bec6405786fdae862278125ff17cb1f789594d98d572bd7c895bd6cb3290e92af6ad906389cf781848079cfb133f93b4e8f5b82fd08cadf69da39813ed186f8b7735a08f23d91d3ab6f9ebbe86e653bca58a655ea4d3677389ad64916404785c83cad266eb06a48158242bdfdfc2ae53be8cc1ba76e3b30841b7f1d525b71d438ed61568c9521c4d65bd0e8d36b09480f296ee7fe28469e0078ec85689657247686b729f2376e7d51cd0c24e1348c33630fa9402e268edd180e3e0e0cedcd812700536c9ef22543dc51289fab7f9727b37211bdffbbf6790f0a80b6f15391f0c088f150602e838e62ba9c38077ef6182db798b80771815b340179ed6d74e2e58951244e5a4953b7d3ee99b041a72ec14e798583abf30e00bb998feb0ddc056b0d60fd283c7f7384caf526e28766e82a399b114bfbb3d4d19f81e04aa2ee9505c0c8b969107e985c455e477dacfb66d66736b88405d1fb7470eedbfdb305834ed1e6b8419ac5bdfd2cc3708fef43b6f3c986a510db7285b0abfc7b1e23ddbc8cee559065b82580ac91eae81389be180f6dedbdc4105bef5364c09a621122b50a1992c33c30b97d14bab1361133690644fb013c05973d047fa57e1dd287132819599424714d9d83ab6d4a738c46ca3249603b8781aeb8b7840409a81ad0604b0c32ffb00aed7eb7a58b6d70d8564cf12c4a3ff0fc62b60ea6d4be13c51b6c4b324fc28609"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"942a754859ca82c9b7d1c4a715f10911fe13364b552413d2ee578cf24eb14901","proof":"98274971fdb26233ee6abe232013a0ad2ec9a6aac6e72ccb8a9ec7e87a97e359e2cf4096466b464e85c6d035265d911b5d1484ecb0f1f5a28cde36db19090b497e813c235843b0351b3ff949b7b4f1ba1f886bc8aeebe3af6f0b518ae1fddc5ff04dbacf22163ecef4f4ac3c2daa81272f7ed81c60992d2114933ea2b3857e5d11ab301c97a4a8bfbdb57d8887bd3df73a7e656ab426ea4b08fcc3ab1259710960d80cffb2d1c87f2efd79848ae6fd5da50e89abe7b39c1dc77c2932b9dd6907f75441b62645251354da80c57a0dcb78f35e4b65dda6d4f8b55b0e131cdab70468185a4a23d559ff35e363436dd61d51c2e269d750c1304bfabc452f9e47574c44afc203e3cfe9e6294a98086ab236aa4a47d6b1a7e6b6da0ca74a9cf802763c4cd1d709000e001eed9a9d251d293fe53a6689a51312b792f151eb39edbf1621a4f6b7ddb428a2db7e63561fc9d5c9e97ef801cab7e409b60af3ae6df901924fe0e63d9e647ffc9211a1fb3ec44b3bd2e5fa804e6aa6e9b1cbaefcc9c5b78b4be6003b0ba07dedec27efbb8da84ef36bbf5440567d4486cb061b75fd2596c5794a547a3d6386d5266e88cf57919a3a1ad62ac7c8877c4452781af42e546bdc1ba44f274f347275e29b5d5408ca5c119b3914ecb2337f55ed177929313cd1483f580116cb3ff48a680c2f735006a8fba7571a98d63caa845198057029f00eee5d6eb32b7e092f90208e5273932908da4c596f329e4040ea4c30f7c551a0487b259c74f7dcc9dca41547fb8e122f9f3d07ee8c8ceaa64cf0a7d4ac656fb37ce36e2aac85b603eacb35fab3d732d3efd8b1ab8168c071b086bb424f7b7cd2fdc95e3b8ed1a491a79b3b00ee575566b37369b8a28d14e44ea013d9f73b67449d8e09fcd6dbeb2024574ef15afce3367bf337b84b2d7cabbec717933af4ed35f5d60b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"403ad3cdef15366d666eb089e24ffe0b56261d094f657dcf9f713fc36c665247","proof":"b2d8c5bffdfba6dca6fc8b29762cb026df066fd1c8dff4d9e807d88cb62e2561369d1e115cd55957cb71db5a7e52576571be6b5c91e4d4fdd4fa4dade7c4fb7e36cc6bfab48a8f771e42d624489fc5a05a7095be85a1586ebb9c045187caee67b4f162df7bebdd90f81a815c288ec6f023306afada8fdb502ac3fb3abac1011e5128c410dd3be0b0560c131fdcbfe49fb3bc0474439e40c6d463a0e406b60000b66279cde160c76c3dcf0ec3a4ff123592fdc355f54a78eba97413faa5042b062673fa0ee54ab19d2144c7bf87ae7b41cd3c7ea09fbb12395f4d80b08c73120ab8c679dc3dffa9b2c7702e1545e8aec93033d82dab57a404e3a9fa1963d333480a2f774c5605bf54593020c0841a890a510f35d5e63cc719ea06440009601c55749821b08f2b29a5650d492b67f6370d88e54353db244900a18698a73d5f385cde3d5bf2f37ae6ae7d57178deb98e9f9e815aafe664d915d6bf77794a5604b60b466037003724a593cec8160e13665bfd4290020e4fa92d5a13a86cd48ed8b00909c073e30162669c301fd202308236d1b95fdcc6dd896e8e3169cb0e885b64df0e99e709bec5dcfcff49cb33d8fbd59ea2f716a89f01aeb418a1c9383669c572caf3a18b7786be701c118d84101d7701249a00edc8abbe7ca82e9fa5bdb3e5736427dbfffae9f5ce03b0e472c14cb0daac79c080cf35bd79bfbcd69d5b1e606164b3aa8ca893dc65fbb51517cdec42d55e78ea4a6e148c83874ed47f090247e06f29446945b7f190a136255caab77353a1c5422978112c03f9bcb05dc5adc373687d2dc51fb04e7c944b00d9e6dcd3dce0d7191a19bea284b36fd4ffc574c4e6bce4298177d22067b73f6c3e9d1d6e691953dc263425f8f8e1ed52a17c74604a520b2f92092a1a889df147736b9a6613c03724d02801b8275c7f8e36ec36303"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8aaa47d8a534c386f18db329baefd8abcb3667641b638b513e096b19757efa72","proof":"14ff194c7d65d9942601b6106c4e5d6a35020ab6ea521dd09f3ac0a7fbff766da056798e57e2c053344336193b49a3c128cf7aefac1a5931c75bc5e06e7c59640ca9b6220c15a3bee4bedb08372934730f6a010897651e1f4aae29e5c2b7e41be0f53c1868f311d001651a27e83199de134e413f3bdbd5b0ac3dd5b691383c4adbec6e92451d28fbb41e470203c88ea551ec0ebc3979b51617158075b7aea601e44eaa9de4506724f7619343c9e65d7ba9a2f12a55c20f6c5b72f3cc5181c90fa8425c109b82c86575a1dd7d5b691f5eee24c7a162eda5a48c1385b7508e370d869c8c9c1dfac080973e567594b839488dfaec8cae831fc4924aaf347c465408cceaa53a0561dabb737e90e97a7ace466ca20da645428d6795e3e2415bd8bc359295be192ab7ef7806e8cdbc1aae4c348a364b3ddd6146162237dfaa7954567270ff10b880b2af50594bb2123986e7aa68c51d11b74ade8853a17fc884bd4d73f0938cb29bffb1af1a98d723e8bdcd755234b1d3c7a80915afe5cb81499f3f4e5c4c215fcaab8ac7ab3ec3fff360f2c20cd150095ff503e47a71d85395f5ad2a7ee54d41b8e6ec0f659afa47ee832e64fae28728bf15cb996f6ea06ccd3c545c78dfc78c84caeb4ceb2a3dcb2028d01e76770a69c9c90d4e0afdb01ffe9018205889943a2abc342f8d1f8ccf02d8dc371d46cbc461ea7801f2468154a2343c252098cac7bb2dd292d58eeaccbcf66b888f954ed601473ffc4f053e4f4173da6cd8cec7d8e4f01dc34b5132ffc7931b9781db0e7649b21f3176c560d009d817236663afdd314de837c7738142a33b0087c93c186b0a43df794f9949570aaa7558ff2fd31e45172de8054c462e60281a58ef7b3d3e6ef5b910325268f425827b0802e7534bdaecdb780c9430a8df7d8c17ae68c016ad6852cb0f24b942b7348305"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3878d9071a3a9f6fdd3b0545f1f8d7e78646d7b1174e185dd99884cd44357e12","proof":"8258f3c442f609b4d443268f81c034e0f9695ef2bd36545c2a5c705fa4a43e06e2812ee46b415c72a3c9f0a8712bc215f82869898611b8772b173d1da3b69c041a7de261f97b398a5caecbeaa6595d57b80daf3250569741f30a664190d05e6b80ff5e5e05462a5eb9f061d85bd1c7c7d38884c2a25063da2f4786b7af4d324d3a3c860fab785c7055e5d10b6d92b9837193bedf46fc93e0c42734ccc6f7980b9d7f1cfb07d7f28513354569a55b6f94e79b7e6f20c7735d0f75d9340ad1b4069d79d9b06891efa9f75c9bbcce31e2eefdfdabc3ec207471842e3437c05abc030c742fa73fb39f40a7612405716b9bb4600aedb6dfca8fa54784e864a731a23da47d3efeecb46326e5c352468fef0d0e672ec3265abeb6c815fe117bdc5eee68a09e28e4af16e3ecdcc2a4cd77dc8bcdab655801e4ab57881b9a0b5f57c94930604c2a79adbe5c739818507da69348982971c214feeee51463bbc6a0d734a63e3422aee2cd038014fc1ffcc4d5f8809bdb7b58ad698e3b6f85deab9fb4de404952601a780489d212c9bc9e7da889e1d7a4557c6b726dfff889ca531f4072942bb81efb81d4d7ddf62dba9a606749039224626125dc2e03d18df8e0625b5eb40be241e63ea3c9adaee7800e272e61c4f7a36cb733c98cfe4d0108ebc5a0c3040f14b27ba3e1634cdac473c5eb3a26daac0dc1e1b0aaed2e778ffbca534435cd4220695a3d97b05b2c2badd05e655838978fb9b32bfb28e3e055f5a4a31b36a10b90e1c86d4794d912eba799eddd047b15b27fe1603d85de3b5ee2d588a964ab2a2ca3e0be9766461284432a786ad87af7b08399d1a29918b68da8b24bc66a847fe9c704a594f693937d7183367dc4c4eae161e6fde2b338995329e235ec53d604a84560ffab2402895cf3da2efc86213bc760adfef5d1680272ff0d6e35e41e09"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"d612708454182859f859af5852264adccb7621ff70a47659230e47d2c37c3645","proof":"30ce01bd7403d69bf9f450596392c62155365aa25c9422ebaabfea2989af8a07dc38eeea84471ada7336845206b1d77da083ad8e58b3f81d16b585c6cf06035ba6b2023914adc93c2dae94c89f703e875ac74f9e5f6808ef1d2f219f2b10ce5b5c9ab7ce49ce90b7742ffd9e0f5d7ea4f6f93d286846549793b5a6cabaeb1634c25a46d8fc6ef4d504979d5b6ae47ce798899b2f0cc9737c6ca7639cef868a0ebcf44365b5e3787f35ea0a3b1b0e0044e67f7a53cf0ee9d83b63ca0b064d1f08bbb820bcd436827ac52c11f795c69a2659ba28e78b0da65c1a83ae2d6640ce0828605ed550090a301d74f9532ad7e7bcc574bfc97fcf2a67fba95f12db4eaa6844757107de807d473e5a0d1c54251cb61435400b2d7526eef4e94c35e091f063e2fcd4f91897d40e3acedcc372c5e3ce117c3b4715d6e0447b638c03b5d8684ba8b281991df27ffcc905c705213ca0462365bda646ec396379ddd9b63de0865a3838c80a5664bb625a03b164f429a551b7d27a6ff014543191da7c3c4bc1396ef4aacb96ad53f91bdd24ac2e8cc388e338f9ef2185ba5f52abc84ee2aed7a0285e5477d11299cfce6f0baf5e5db6060d1174fdf212d65c830caae026430a6a55d4ebb9429a53f7993a0f7eb01c46265cba6d0796c57f983c4219f2a65e68ce26a09e8c90cc5f55700ce654929d9fc6b7e936ee3f9c39ce6c04dd65b97e2759431abc829dfca9d7aae03337230b4a6d8bf0915591b11ce9c59eb7c3c6497cc3791429196c21ef9e3c5888f1b5c10c5963edfe816379f72e12f8896598dede123f0caea8c44ac085723ea6718fbbfbfc4ef5a0f2b34d3465529afd1cd3ce8b612536bb8b1d452dad81f13723d5b468809b1e59d77c3d5c4af250b5990fe682d00a157bab2c1962d11f88eaf2d4b68b359d02772e293e2f00b1d725a8aa69a8c00a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"c0f487ae8e2961aacdf12c61fee3483f6c1f3dbb172e1d2881c4113e7b3aac01","proof":"9ec9c111f96dd1fbae8326f6527c130f288bb69cc9c7a427c5bb7e63a3bc063310a06d4b6f4c75dcacd0d1ebb937ec2f5d0f2b1218652f30b4a1d16d93c74662d48c83c06c61e51a72648aef19d304fd8ed287c1c526da029680f1cb85e1277f7e10fe6b2e6c89589d0ea83e140f1c507729510a3526d9ca63e384aab9aa33605ac0e10175ecd48dad6d3750cd7de479137290909c7dd380ac0966c71d31820ba0d74a1a476dfbf3eae4e1f6dd4b36cda7fe55a1afa2cf10f15479fe14c21202d59a72288e430a4f747dd49b28154ef39819bff876c01e5c9f2d05927a6b43007408ede93a9ca2b93b5058c15f79148d2a55f988cb80777f5d4605262e95a77fbab761dbf5388c3f90b7520951bd2ee91f318fd6c5a114b58bb63e5436629e60240da1546655f29447245839290a7403b1ccf3263b9561bab8f4ca57ef84dd0f2816ee79bdf9df048d2db3a347b62ead5c5077163b80cd5c9f5d3d1029521a36d6a9f91f96532e9231f73c8d746d5d19941e9883b0c43089114be807cf95e426ba3b422ee9fd67b4fc633d7458bcd270e8d6613793b3f6efa0d8aa1796b9087c52047f41ee4187130bcac33536441031acab7f646825f7f9d244c85bc907e20b749a2f4760245a3d938a6af0d5f029966ca30b48386a3c5245509cc8c0bd4209f6a9a93e349735a9388fe68e9f5c26a2ea956b8adeca5691c31e00a64d4a80607688c968ead000d46a20271658295b45e0f039d217323bb0c8f5dc40807f2a2572f82e7b3617c551735caccc0c00fd3b26341f428841a8859f075cd2f161f10b16f0eb6934e797cd3513421c5a244d08c724400e693c84edfb47afe2913c877d303dcf8c0dbeeea9fb234d8f566ebfb53e84f2bebd78d1be3685141b044a1f05cc88907a5eaa8648c2d23e6e2563757833cd102b28e0e80114a93d8fc2f04903"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"fab0f6e3776d1b5542287784e8be5ab18ab4aa7662de2f07130263432e05702e","proof":"4e694725b472e6e801cb75fcd5fffcb71085c8ea41eb39aa4d385e9631873d03c053a2b7597d07fd2dd882f2a3456f88650bc20d1de88abd19194f5b429fd6226c825264f8f306ed60c5b921603c5a7a279d4ec56659a91b8991349661e5711c32ae0fa0113467c8da65a0e7d34560981da2a5405e6bb0ab01442a9c4424441613e5afedbb574df63be06e7ef0c6ccb0adb47e9379601a59ea457b70f482ba0cd440d2f2d4d5841c04895efb16d7b38988e6a80c5050029c977da2fcc62fd20aa373be3532e1ba855fe82513c63bb3d2afc0b07e954acbc9a797d18860e647081cf31c20d33c0c798e369bec02f97fc57bca758acde1ac7377ee61936f01e5077485a8088ed7bb48b9f67b0f34196db41f3d022b4fc1332d941891b0b67ce006e082037fd27fcad596bcee7155d8a87d236b90d4827b57624c54ebff8a3f591654232752be7c99f7661d2cb9b8b77b5d104e9b1f34b15a456eea227e9807367a96cc9938c536689665d4d3fa1bbd83eed54754fdbd11f15b09af926057178f67d89085c73ece0dc5e11e6268f4e94600aca647554b5a1c846110ea8b6f4d883304a8664a9dc93d41dda74c21d5857859fd500f716f3fd5ecd854b08110b2b052aeb2313921739891343f4613992f27540731dbbbcd1dfd31a6797cdad907f8408ebf2fca0c852dd2a782baabf5cfaf297c907c0c084b3ac5c301646106e9393578e8647c360dc9f24533b7c96a145aeb253457342e7570037f4aaabf0c26e64a40b36c4bdbbc19c7b2661735257d52eaf4d8326d549e6fc1fec0d091cbd3c0025643171c6eecefe1e1f4afd1911d5f5f0482dd8c180dda2b2599ff2b6e3a1b59b9400a282d75e5bc8eb796bfa07e3a6c392a3d87726458fb83b6a4116c076c0e2fd2733fc981f956e458f54fa5e77c6241a0e8687b3e339b6ac99ab1e803380d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"88c4dd6160bc93241c7284c68f908a2671b6f7d2f564896d125d3cc1fdbc2543","proof":"7cbe590bfab562b78d94a8f987f2207a0bbe8f5465978a572e3b233166dc2a7e7aeb2bce46df9f02b06e6dc3bdd6ca543f73324b7ac99618b13cad2b82fddb5aa43552bb1f53c869a793a5922bd215dbe93c76b3507ceddcb0c77b8937261772f220023d118727ee5b9d50bb90d853d3181ebff47cf26a56519863473933964adb1d4a66a0744fdfaeb41f958dcdd4ff4831cf415764f68898cc0722363471094906f23eb1fade1174083ad4e6ed6476fce741ea9df33c0b0430dd4f72dfbb0f9f7219060498a296cc3f6d8eaa46f016957d7ae2d91e0c5e81c99559936bda0596e5c1d14abe069e08dd6487832097b9ab1ecce683ad7db9bdad8740364ccf627ea428da410d16f32a03f94af0b3004f2538ede952fe816958a52faac5ff3e74b654c44415e69c2233bd963b29579360039a1221f815d62eac1a01fe2f10705340aee197f974f798d6ac7305f1dddff5a69c4c27211aecd34c29e635bd61bb3560275ec5ceb67f2ad6ad47c3d43ae04971dcb83e35bb1b1bd76c7440d66a5137122b98134c77d354692fb6389aa74d342b311904a30cbbf62f14bda976942218f485799c4b0dcf7cdc5f0be90ae80d71047097bddb413b481ffc166df25c90797e16c9bdd8ff668f2b187697e2f743e575d04669d685a1643f42b4a45659da719cddaa8f7f76dcf1a2fc6a3e0ecd63638feeb8cbb72ea56d3b495c34d7c16071fc6ff4df8e0ecc838ae19c25447e8dc02ec004470b552075fe7c4e21d0fd7c07466e302773ee569952fab7323b250a42322599efdada57696a1580a204f5ea4d08c3c8e9885937db925b1d1897842d151ff101bf45de26d4fd30753ca9c8486c3ba511895bdcb58cd8359f47cc2a3d6f1079c73b6aed184ff845852a952c2209c62eb85592398bd70e954738d254d80f2d48bdfc7f2b9553f1f9d29c04b9f305"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"be087ac0a3e754ae5b6f6884387f7fb355f1cbecac339f7d97f385f6a1195f7e","proof":"7c10271cc17e22bf1dcacb204d3e4b23cb0e93431686a95359f3b518c6e6eb046a0190c9f210e11d2f96b086749ed129ec1e24f4d45c89e37f5097a106f17d3ebc888d4c9444b3ad1ca576f396a6911fa0f5cbd28969bc089e50b3f52b22830a8c2e470e478415d04376f647fcf5f6247aee02da8e36e103908cf790a6722d2078f23e2489340144690545bcc77196de9abc3ad8ceb957f9078d65c639d7a303c6636bf8a6a04f5bd970e731a7a7774e18ddcea3afb0d2a7583b0e7edf23ec0c4bea5035880fbf44c142ee15ee58450f52a4736dffd4ee3c7e0c4792c77b2400582aaa18acf558d850c64dfc9642b108b5427b76530f856c18964e96f4834578364d8ef875b6887de605179a520707f877343acf76a5463d882cd1befa872e6eaef3a186049117740a1002b102281274d31cdcba5ab5bb61d9a6de732e836d503cba5b6319effd86d4285f652375803153b79f049714240c5fddc0d86f625e7268d6e7d5377df0f99af605406246a0f082539354e8f296e2da047182660248287896fd217093cda3fcfa78a92715aaaee14cd2a31ba2375f866bd6ea1351835e84e0cbb2d249b33e3e017819542cb900fa9e70ce16caf9fb7274f8b28274623c0c8bab5ea89fa3c50ba6663bdf669b4e444ae747281b60379dfb8a852a5cc74a6a792e6c93f59dde0b00495bfaa85ed9692efe0192492efffbc27d2a35e3294a7cdb79d08488cb9347361c41bf5229716d70c09b8eef8d5dfa7f4b021b935c008c93fd8618b69c3c36f17eb25c061ed6cd6cdea99ba6eff2afe17c2f6ddf1c7ec037646251639e5436b85c98b53cbe59a6b3d0e885e488a29814eb4250178a22b40d275095b46fbaacd6b3c97b3c0a9ffbc27e9fe3efa36640db0e2185e3ae06180d3e8ae02284d6e5c1c28ef5a5bcb11eacc5c1ed332e501b741246fa20e50a"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a482af73e6aeed2eb14f3befaf200688c59566c305da795706f5ba4dba1bca56","proof":"0ce4c86da857fb06c406c61592e149e0a074f9b6ec14cc8f0711219d372987699e8ddcf93fba23867861ff3aeb96fbb22d1298f9537b06df7e96858ba4ea983aee9823d4a7890dbe4f659496a2be2d739beb6546dcfe2d2d8444f6c9fd709d1e545b75a86219d68dd91208bb887f2d8418bae8ee335619c980ebca361dfc675f2a2b3ed47fea7bc2be9d1dfefd0f3c38dbdfc48389c77fc25936ce93ead0d90e350a12d45ee76eb89a7ce6415b65c7972a47a76d5eb74f89cdecf55b0ad438037bae082108820eda32366f1afb5ce3397820760d5278c0fa507fd6dcc861810c2cf6f1b0e05538693df2e60f30f02029ad79ae5949d9267f1d67473dd0d58d7cc06b10fc4533f7113a15000ca1912b61e3601a058e35e8299a37ba735eae863912cbc1aae5e48b9a6a2399a01fcc22cd182106ca404d1dfc4db50bf48d15c51b4a7699ddf3ef0c55ca16f7639c31a8f1ae090d339d7b7607c167f3c9c669b42b9a8962b5ebde0626f56e1cb22790beb0793663c9ff90ad49eb6622a7932c7c2dce01162bf643016d036475d79f517ffa631c530a9ab5583783e8dac1cf56015fda07b83ee27fc8f2486361dbcfddfbcf8e4fa20d2f14937f6cef1fec25e53d4bd0df3f327b45ffbccdfb40726f8ed49fa40abaccaf9351d9010a18bf01260703a27c2cd096bb33143c22a14b26489df558d79dae43aac0d9b4b7bc09a325527a78dc36af176c12be096b88ddc43352a37664d4b7938143093987307621b5257ec06f1dd937bfc4b95e137cecc41820f241797a200d85b61eb07ed20897b3800138dcba23d81f52c074d96f0a0e9262bc587317f2804713994f6233aee7ba8a4c4bd87e4ea56a88e061f0dd47ebdff2f3a37dc765ddc45ce1a1efe5aea334710139e244d65d5ee66aae8c172ae1fb124ee8828fc9601806b61622f110f91da306"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f89f7ec3cfc7f17629ca7b16572483dec0266c25ba5af04fb1acb193d24f3551","proof":"caf4209ea87255f1bd34602dedb08e85da4aec7e520408c8be22dfbbe842a81c36e3982043678b0959794929903aed35439325fe1495cb661a4525222419b5536a6f2bb3ab76c4e873efcfcc5e18bca0500db7c73d773c89ba8e93266f93b020643d95abd3d76b39fae48b3812468591ee7889e33cf58b95d3213986b55ef1401af39edc81dbed27216fb4dc4cccab65f3fdbbbb7338d03c62e5c0fc06d8670be2eeea3e3cd5329feb9514e8abc8ec41f5054a0823e4e73e5c83311410d8ca054740a1237ba82cea1420b54b0681f2d1643f7397560c05c4089708ed1a2e1e0c4493b7e36ab50d9ad1159ea6cfd60dbc9da8bbd7089ce98525c64e87f71f9a29164030a72d7d2b89cb89884b64cfbd917014f1f0c037087fc2b64d324785456688094f94280cc6387eec60545e756a4c4d6076d73d6e1cad092cc8e24b6e71015030411dfef529aecfe81887b4b62b5c3ca3548575fcd61db6bf41c027b80f2168234e9422ce8a2dd734a428665e3e2bc95e3e1ef714d7eb698fb468f83dcc22c0e580730d23275f94cb7c4cad561d88deb9b0b5994076ebbcfe567950431a3f08342b3e438fdd8fbfc7700cbdd31d2be668ede516da70188f664461f5ac5627a0dd6dae5fc74bfe8addd8290537afa836223fb6ac2f0878cb1773ef32156479da7e62d76e6b42466729b37332f88be27044d5b92c4bfbab6c1bdf3a61ff5f3b38123912093f713bf728e971347084cef61912543bec133a97cf4a9ab818a067a86c3a6373f76428f82493c70fb3e6e42c1e9f436d1a327c1e878afe33236c338aa635cb4c658363a00c6b2fe868dab0a00dba723dd07c63f441f47a587e984cbaab1bf47eac53e7044576b6a08fbe94810ba0f0e6034bbbee192dd983d7480329cb865316f3dd622dd45d72afdf8f4ce9ad75c3c48493cf44bc47302aa3c109"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"84029a06c4c2826a534b208baac97ba1f393935608ba3ba97a59e810be153b50","proof":"8ece7eaafdb37ccebf492db2c16dfa923166b68983e5d1756a0ba50def97193ec868d1c6e4cff885332de1e93184bae3ce56831ba41b7671ea19afccb659ac4fe4782396fb817350e876647c2199edddd4b97b4937b68d96c2ae4d434a11c96ea0368ed51339f6ba5a950f40179d2f60d57bfb721e18f045f3a15e4ea7f8982fe199c931c215dcd78e67259e03c7408d2365cc38b3d9f3795920c6258be2ae0cf6562b97d2032ebfd6f2852dc43d7767e9aa5d0fadcbac6f06bcdf1b4ced2e06eacef5d3a6dbaca95a870799cd6d373a5b05620dd99a95a4d6016d6af7327f00426d5d72da60659cfe26c9b87ca37d76386f4d2112db72ab8010f92c468353240c8b12496236914318ac0f5e8bd5fed3d9986b75fee3b30f682fb1a5d77d5e5b5a3191388ae37e0eec7019ec184d3094da37d1de4e46eca5e39d89a4f3196f10128868e7f76d6df2b45f51b1c783e43c166969f8c5d6cfa0f1ccc9c591a4657b9012518dafa9aac3f2a539efc46cd1bb2235a1fae9b9ecd96420d070472ec45b5c1e59aaf81f1c531f349f8c7411b07f7a63f2d3f469afda7828c7924b404848ae086fc524d2ada6efc5d6a06ba55752d2a299b9b500a9855e21460394696c715269904113314832c8ca3a0e245c74cd6c313d7489afa54b3ff209027d134e760c844238fa76852ab58995558adf570de2f212ad4f225e9aaa10a8e789f12e34bad22124d4a61139fcdbed87224f319a3c2411fcab01b8e90dcf1fe4f250d84f12f9175b65997e839a8661c52039d149a04d55193b36431fb51e1024f1b98b64445ebeb9d7e33623cc4b61e0408ccd1072c81e117e27860c8d1cc47baab38a0c960adeb0c9bb9c8b66f1185ea28e254de7773bf7cd35184689b0d6ca5c3d7a0540e2ad07a51c1eedccf26d238b73aa556fb81174dcb54e72e5d66fa7b7dbe40f"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a0b62ecf9963e8cea28bd6594e24fdd2d3b9dc5c7ea68ee121aa7e819231f84e","proof":"4ac6b27e58686440fbd2a4af1fb0fdb0081a0e335c18397ddf409a4f8caf4d2a7ac8c08c7543f169d378fe73aee71b32d8773dfdb11877cfc2fae6ac3435a6378ce1643c29b5eac4156d9b9b91a4004183e3f434f891b88754750115c38fde01ea0bd46bd6d17a89eb97fe915df180b064af2d2614e95269f74c1659cda99f5884624d00fa0d4cc6332d1fe0c2984885acbb88235d8805f0d5061a238d8d5801b4356cecd9ee0aeda9c3b66fed3f05db55563ee03091cdc7b82555854b8a0c0958c4eaa5d6aadf66199332f0219647804e36a83e4fe52c333bde0e6a2c17470e22db45a8b60d5f03b22aff513028e292f43084c0a01e0ea88a7ad87d4226c521026a2811a116b80b0f3dd15c90db1d535151e3ae1a46eb8c7bcce0c7f5beac34baf389fa537ace4e86c161d94cbc1eca99a8e6bbc4dd6bc6fd2898ce49bbc84e94be243848e6369da459c76b74db4e9ce6f3885a2f42968c50bea132ec5ff32e5681b80112dd5325318bcd67be62536b80abd8f14797bfcaa38ba71fa5238a456e81b74b4552779ba4f7317b49f6464945dd2f15b08b89791edf055d8499f051c4a5373a4468abae00c002563415a8f7b70330650e3b6b636ee52da9847f901ebeaf60a2a79d37e6a31a1018c34808fea6657b03048bd7a6ca735b066b9a3219f2f58a8a2333aa73208ac09586b66a9f79e226e155e7bd5b56c7451b21d4cd2824da4965aca2863ec9a06d3634df845bff93c764e88775a55e57a4e337fece6ff475b2abaa212031dc258c2e6d6a9285a1834a1b6842e4a9361bc98fcbf5786f2cb0887ef36053298bd348b9cc7c4a0a277f7bf4d57eab692cee1d36baaf765df14b9423b944c1d95ad132010006a3f1802db2e87bdab130a18bcb8bd1e10d06d27309d7e813721ae17bfb50a371037a3326ea68dff0df6ddbb64050eaf60e02"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"86ce200873974ccfcec4c9300d8dbf9cd62a8624ff47cbffd3e059b9156ef20f","proof":"8cd555eb221aa1e98587858de148851f5f786cc211f27e48316b4aec0ba50d0f364a71a95fa7e0ae2c605a659e6c92e72b27c36b508830e9ccb6bbd828a3c118542a45e9d6da2c9740a32531066686146e8be5445e08c5697f2a15715ea49d7d184f593d1680544e8b3323270c80fb73d332ec8fc741ed1cf21bfa45646f5012106444ca4bc4a633cb308c7990570b2379c611e016972e1ba1e2544d5242c905899545bc3ec2497654ad913a62fee21347bc5d465c012755fff8e57aab81da084fd92a88f1d762f0785732c9567062d7db4838548dc401e0863fbf02a9c5770146d1a16a4ad5f684c66a696aef8ccd83a0bab7204a942041d684b8e5ca3a4a0394d2f7cd67b14344c06e1e30f4db1c9c5a3e6fb0cdc18b4fa0b305b617b06718c0a6758f70f2a0fd36fc420a656e2f09655c0a21ed3b31a4a2e7d1d66ce8484ca83faaa45995f55f986dd2ec5f01fd31c2dfc2a7e400659323d3f880358d2c5bc886dd95ddc3d3bd0aa31c67866772ca7d4efbe5190aaa248c3e806a7c36c00260342cfe4ae980ff19fddb313c438aa1a1e6266ec3514cc31504e61e5d859819c8a8a480287d39e0887730fdce2566c682b7633e23bda4d2b6b5448b93f1315be443875d9a032b7f9a308e529e7530308735f2886cd8a73944b3279a92d4cf7e9a31ce59d7a5adeb77b8d7e79193ff57966e5106606b37e2c1496be8d626e645b8a402e2c455345a623c3e243f96f5a93cb49dfb49b5428df2ddad071cb0e441f4e2e6fc49efa91af09befa9b39665a4e03ea0fee54a372245269cf2ca5dc62c12c6f6c2019b169f5c985d8908519842ceb3a9166614602750e206977ce29a6bc46bde03ed25e810335a9aaa456da401dc1be402af3a9ce3a24654660876e40a8ffa0c46378f5cb8f93a718ce06f20d2f271d1bde1575a6444f794d258750d08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ae666eff96a146a116135165ca3eb965fc362ced1e04b36b400a8c736ae85d3a","proof":"3a3045c6abc2da88b421650ccf1f38ec1dd351fc1f525e57a6703479a6073f2be6e45ce9a0fe1ed186598df7d1cc130ade3d5d07f1ecba4d76b06547cb830647c449ac0ababc62811b2576a509efc108317fb881286fb087ba99b00c527c8872021a09b6e49e71be34d7d586ca5926348ac2cf69b30a1b530b47592a7d2610272bec8876fffa11588adcbefb69886ce19ee282c4f7d4f6c498e63ea5e100c605311aaeb15fa15097591035733c4ffba67a22d99a33d8b69ec993fa6db4387507db588bd315e3c992c8bad0ff70de9eeef04fbd135524fc73856aa7bde213ee0614802304af2f59f8a86bdc5ae70aead890ac09afd86edc994990767efdb66c2562f48a42ff0cba2d5fc446744a09271e5d61eb0a9c287fd9c1c13334e45cf24f646745af70e1a05888a8e681c876aded201d81b211185c64fa2c4a185ec6f454568c69b7d6bd0ffe3385455bf16960b401c7255905e7d7043d48dd267ed5ec45c0f8cd5a4dcdf75d1c75c80f17665399c7bfb50b9febc8a339150a0b24b7827e0013ca3bc98deb6d63482b86e86b76626e49d489f13c070871894bddc685ce6a92adeecf9e9f9858f165b1755b76b8098f3d6b3c093e87ed894969e6c141600646966667cf8d6ff07c306c64deae4f20d2ffc3e17191a6bc52dbea94b1742d4cf0f9b9d12ec2ccb8446b68d9e99bfde9a655b538bf4c2c6da0284418af9f4e411290496e06116856cc2d1fb3b079e58687c50d5dbe539c261a6e488e209f246c66d0e9d862afe9fff9110dca49829ffc410cd98eaabe8ab852cc9b6c26d4ff623497e80b8f9595b9db13e24149576cbc648bb5bcafe9a7de716ac451eee5ec16c76bc94698c26eed47a162309798716b11cca32d0cf08de8036d50693671d208594ca7901aec4f437ecd8120aa1f054d4dc8d541a059f90df254bc83bb7d9d00"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"0801a715e174455ee568f6f18a9437c58d968360788ae2bb45b9d3f608d6dd22","proof":"d04fbb12d29644350ee9fe79bce7466d4e1a31df6b47cfbd2c366b1cd36e293534807863ecfd7670e68f0b58fec0400ae236506d8231d5fbacffcda35946e0045a18d3b35b43297ecd582496328aef237de779732d140715284345df8b8011586832544e1578a85a3d2881e69ebebcc35fe80dd88c616ebdaaffbb92c25a1845d9fbc28d86ab912e7f6a2590ef21a9a7fe692e85def2b5c7751958157f20d606243109c617b917d0760bcfa429f826b769f15d6ca88894a1160d8e59a3c97a0df961f6ab82b462ef56339acbfc92ed6addcac9b39f3e8909880d2d088ec3ae03245a96d09dfb0ed788d729dc3875f562c44c1ba822065314712381b943964b22fa3548e0536f0fe89519a50c18a1f69a461e89ad799fc1c42b7d554a25a2aa693ea0608fa887152cff629476dec8957a5ad1c7d60183e764d573912378278a40b8fca7c3e1aa152866328c77553eb391686729a782b62a51a4e881f854cf1201f43cfa4720ecc7f7202f29e92519391f5c8f466c96e600ce6242f9c08b463d2f544d33df0341934839f08848e2e06f1a6abc6acdacac3e476ed574635ee34476e262c4f578164f3e6542f7bdd3654c2dcc5c5296f777b7aecc1df01ab3a0cf067a4dae8eb8cafc66db1e43f1f740bdfab504b8616945c4b5bfc6ea5aab22e56ab6053ff7a65a7557e256727d9c82ea099b69c197555e9bbe3a28736843673a7edcaec219bac31787fd4187aeb68abe550c0a7e09ccf14dbe167b8e659c08026c3e3d67f9651ee9e36651f594967d83d74c0925081294b8fd7b36ab14cfad31553cd76bd07dce302a9c1fb8a1fc7a26042753b5a40cbb1c7a2f2aee44fe336362a546fd548a6320074259f916460648af6a5a8910eea7810ae2ada3deeb8cc20b98f62b2f77ec13cadc52f21fb116fed59978ee723d04fdabc4dcf43daeca6e08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4c4f1d867d95c9b245f0d4cc8d2126ee1ccc8b3feb8d8f9b42fb94751591505b","proof":"2e4ec80b1f60b8b884a89c6a174db2a5d5ffdf457c2b8c23761108f5611edf4e464a2d86841a91fb948008c5659a1e8c7961c9a799d1add8ca074d641eb4c52c34175ffc17f92bf00dea68a6607fd37ed0c0c07b0d52ff85a5cc9e66c70215639aea1a566e078b43178f08f94b15494c23b139e8d3c662d002bb5a59c614dd36cf64c194855d9561edfd2f355b3b76dd88c2fcd4b319a3280b80b95940fe940016eafbe11086283c40144602a84e35f758b1e4cd6b6922b338adb3ed29e24d07279ae212979b193b59ca5a9d441a1db441530965adca82567e0cf59d185fdd03a6c35d53b1e1fb89e73640509223977e930fbd71f6d7dcaec068ea66007cfb305060787dde730b498675f93947edc416f5e2d2a090126e52f33f0d7b5c32c572aa2d443464f2b5b865c51a6f53403af31fbad0e10f4b13ba7dc533093e4fa61a62730d34309079c6b73678a178ee47348d4efb63972f1d42b5d02a6ee93cc23730add0081552c6bf0a7bef686d4398d24a71b67c5c83fcb3f7878cb821f3e95eb8ac653496cebd3a489e9528a69a35f6f3243f1c7169dc138706d8a50c03f12404dc46da9116fcce10406632f333c6520902f8f56c3c8721a25bfd4432bd80124c94480223e6ad9fcdb76c8ec574c891dabcfe39d73f1ac7ad455e969d21782c1e2d42bad42f4c3b0cdfc3c1826f3f3faecb522df64a3be950e5c928a2878e0c6c55b6526916e7bc414cb5c7af2c6134725b0a43c1baef54ce1e972fb627ba7736a8935a75280e008f843c140598cf40eef2b92e93b7c5150c214af76f355f799a610ebcb5f566facc11848364407672f477da32301093b6b16145fddd3a0e4b3069cc161d3e721acf1631729fc26b57547a0787ab38b58546c4c87d96fcc3096ecd42eef1123555e970decf0487e738b08b258786ad14ed0c8b7aaebfd4d405"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aa76dc3333f8c43977cfb69a17d77387ba843ade818547b4683571429eeef40e","proof":"966e06133a271fdcb8235fd609f4db062050e4b4d705b676f000014be63e4a74089e9ef019da5aa67787b06af425d527399fe3cf48825859e459b1eafd828e516418a4cd8e79b75b13bf9bb752573040d8f6440abd755f0fbae0ead4a4c8aa04e068efd02dbc0957c7f5d9b076d05b198d792a40899197f9f4f6c0b0c8f7393b4053299640447a6716aa61dbcfa8aadd0f8093db293f86939d5bc94182c5850bf561a9d088704444c615c3414277be82980b818dba4be12ca64845c33d9dc301d5178ac26115eb5f54e61a936594108760912048fabc40494239db37afc4e908362baadeec800d11847c867815a84795b57f93e4f3b6ef21dbd3c3b11cc17935c2b31cfd7f86379324cc2b0404c3c9b8d4ac768bfacf66c4cc6f37b20214a305b6b447ad86405870a2cfda5395216351ad5de0a2221cf8934e46811a6b94f15328ae084f38461a91a20bc1dd0647ee3f73a1c540aae0b44d4d24317c7a8bac6e9c6c21d12b303c166f0269a58831e207dcdc35344adffb940f4108be8b3ce5584e93f1cd87b464a94c2213672ce5d13ee7a43a5dd6679b460925018767c3983b38540f78c0b8e2a942338285e1797e32190f1419f57baf919dfddb54a17c9b6e0e908076e14c9371f3651edda9cc23f6f2a8ed970be15ffaa40811873a23154c003d91e497b588a9ef41525d68c551d21808e237e88c686110d7dedf0b862113dc7d2a55c9e260b668657fa67c0356ac17e0d0df3fe9e0a43b2a557228c4fa29e014e09c238903a40560e54e7dff7c85864feb9585cbce5d8c6a8f8366edf41faa2368f052b96bf8086bf059cee8c8e4264be4ac56882ae57992cce74d1f930196508695a7beaaba7364c15ae132b496546bd550b6189db6d145f81daab4ba0a1ad449e69ee0637e3c5173a57b87cced9f00019a34082add985efc65729c910b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4edf0fccd151e53147bd3e50907afe30705d7f43c129e350be42075282405e6b","proof":"169de622b9e1e8da0fb62ca28bab1f80e03e379bbb1b55d8b7bc8758a26e5515ac2e25e36d95acfe23f6fe249f4f118cf394018dc36b9339572f29feddf5314c6840218930e8b5dc79b8332139e4f4e5023d79fca789d3edf26e86f5709c2b353854bf4524ced3aaa450646f03e548ea54c8276dd79b8f4076cda3dacb33b206d7d7fb100a5616f1187c2793fd510cf06606a95039fe04c131b6acb494df8603843c7e4cf448b0106070fc9a9d8a9238a3a71c7098f4dc9bddaa1600571edd03aaac801c6dd46da79a50858a814e4aa55b5c7239a46a29e1dd9ba90410ec38006a32add8032d75c9a311a212bc15b6c5e9489b1480305bec72eceb99d1fd473c1e2dfd4a6ef06cdd66ac908de83044a6a917cbe53a6600b1cc8e4e1e785b0f10b0f273ad8f12beda13244047a1f736d9ed6f697eb1cb58f712f5b9ccad06ad748cb0937f6c014e9fc6b2a69533b032066104847a2dbe9c77df96868fd31c9542a0d15b394ab163e26e595bfd94a61eacc1159346c565b1e4fe46aaa42d51674a68998f4491fcbc32e01c6a37cd463e1681eb27a59dcc878c254a9118e5d6bc14ae371d6f83dcb3d2c3ba0dbd005ca97288add8985d92245ac74425ee2ae8f9548497706d46df7ab040cfb43b5f715679361b6270f23093ae133739cbdd4c820ff294460e32c2e12ac71309062ffefb465a2ef1ce4c372335924e90c2a181e50e72247e94beafefbc16fbbb1f3cc85336832d052c9a6c5a064a162f9feb026c77e46acb9ecdef698f1fe57e8a18e784b9034a3bf58e2e7599820ca38472c55747bec7bdeb8c7454954875b51a870c06a14f5c0f60e43c2b6293714d41ae00f34f46b45d078eb33e0763f841439ebfb0b7655297d15b459e355bc2b56b90f1f803376ceb15fa6e1034692942e7a9ffc0d4533c49807ebedc5051c9dac88307d104"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7c72aa836c2abc6ef90a18be3b2d5e337d8260320571d7808052add2d98cca41","proof":"58f54c66a649431948cf186b6d32ff76257a2b6e2fc0869b0cb8aa0a283c951a9448d967a8d649d9af5393c7da99c1a737e31616865de679d830de9205935929b8c4b1328e77dad24dba327e81ed821cccda69bd9c5899c6e3c9b5b8ba841b68d07c5973c573559e02115fdcbb3d3ea04899b7cc0778780b59892163f40c97490e6f89a5780464dfcec249ee44a0f33643424b72a810bd1c4b1b4de07b5d000c739349688042fe0f02fffb87645113cefdd41edc71e1da22e51c98cff5327f0c677a6533c6f0217c4f38466aaf006d8759af1d8918092ee69e45b3079cee4f05443a60a0a940f2c6e74bfd15d920a873e4e3dbd8183d5eccfad921e02217af5054830c8b50b473fa543597078988dd59644add20bc0ac09993bcc6de7ecadd50208f7f3181f1903bb141de8a40774a61a4c341046e8306b019b1750bfd57667b6ed03f97ec7fe1bf71797fdfe2b52ee4b621ad0b0a4fdbea16848773d8b4124afe616782438d625891127cb9c3caa7a30137329cb01a54d9a8fb9d290be66a130ebbc8c9fdbe2af7e35a124347fe143617a202379b081f9f65c9607dac02cb2b384cb1f9487ab697119c952d3fc853c76c3e5e125e1c387e741c76317983701b783b491f88d602e8ed000c0f52ba8095cb78d47b0ac5d0fa355b28cf0091d934b6f2131204ddd96cc8cb6148134bf4a7b4dca0e80c22dbb5d89b763060fc737e44aa1353fbbe4054bcab2ba7ce95683ab78b7a698070c83970ccb454fd4b695f422f20c605e16b57cb89e4927d9206e06f165e1067a04ffdeae3d3fcfde0f23562ba7ab2e3d2599b4421f5290b98ce7f162198912897c5389509fc07a968ba3512cf509c48e83d87a90e7a70854a08b8496403d5fc621e3008e52585434f180633e28368f706a13a462ae3d130259b0c3308b4dc319ff67c56c14d2937ad6c03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"aca274c5c8d62c3ccff4cd352b30b03c71bceff82fb4824dae9131f06c978512","proof":"a25c3fd3064b8714bd3f797029b93afd7196c534fd0c3f009f200ba7698fce454623d7884f66170f9af4d69d7dc7bfcd6b95f6adeafee12440514bf7700bb9312027412596d97bebdcfce0d73688d09262867b4254b2762e809cb5c6a4a365337a17a6df8680e18c3c736582f93a1f842dbe35bb10cf05c714e22d73e7e9081c020d5dd762447ba19d10ba34da3cf58ba4fda6fc0e8f73c69ea3c05b65b0f803a663b0e735b016a46a57616d12af6ce03c5714bfbc1896859808f9d9988ad6064d1669b77144002de84cae44ff4fc491f8fe84f86d6c9488f049639aa0ca660458deae7313518414238ac5f8507b1b0d224978f030e1aa734aebc7a69f4ce403a45bd3b5d8b27fa1ed543184c439e5855e45ec3eaaa3a53f7ca9b1eeb94b611e8a1e47c11aca8941c3c4417906506716bca89842a201b79b30a3452148333f30d64a019ceafa9d60aceef816f5430955d6c32a5b2d83bbd13445f76ac0bc3c5cfe1532765a2c64b33e35338fb85369b944843193383e15c3e521855ffd932a2d006cbd2baa04eb8d4403fe096b34e3407786d479c019d94856c7a60c6d300352d2bdb49f6d3c58fe61ef1ce980bb75b9b80c69e120a70d947fe9eedf4c65f835d6bc25973e5ea8838268de1a30ff825fd0dde93f30b81daa3c8f0b2f162f5f295492018cd290e03362950074868937422c792caed03fc6555d673e94cbf7f663c67762a0ea329524072783a9fdb7c6deb3765e8188906ea16eeff8f39c899f7694a50103302156ce18dfad59f4a4976be94687624c039cd59838d538e572c43af62ee5311c318c2c72e815d7882e2be5b06a7e2327ef8443285951749720987258f478e10c0425471d4a833cc22bfebb6c99c3c99ff048f50cea6b2f25ccb601af3ddbc62cac713cd2a1af1cc4869b3153206258d532afd65b91fdb19344010b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"16a2a4586c6201f402661c42765a41540e2d77be85e87f24f7bd2253fa8f942f","proof":"cc2f0ebf9ef316db9abc9c39bcf08a0e12f051df074d9c4b633f75e7dbfc534dd8084261274d0bf1fb85058504535ca0b105958cda0166b658b15e07502dea576e3cd5cb65e672ece34acbbb68060a90940656cf1aafe1932906d906e64deb18823863816390c823ca3cebf82b63e620441acee9a751cc1012e5af1f6c415a1222b577795d6da1ac04ae98bbbce2d665f6dde265513265e88453578b5543290e5058bc329c8b57139e447cef1ceb3e58d67c1be45a62d9cbc8da762893c5bd0ac6a4b0dd391608a8d2e6f5902b26f77ea4f55edbfe746a03a620a893f8de1107064caff0714db322f093e39c46cac9a5a02efa460066a8d4c9a2e0d09d069b49ca16ccd2c405ed55677bf495fb2200e0e147f210517934241611ef551cfd3f341ca941956f39d3f42c2d5bdef453f372bf86163ec91ed5c5e37a13fda0f5fd2816657659f45bc574cce6034cd143f35bd2bee012b3c950b54e110fbb9e87af7d5c2db93921ddfbbc7011ca874126408d8fc701c3108d521a3895ad0184df1a7d0cf73f1b2da36104312f106f84d7a0c8b4dea9df226db28fe2b291942931fc177af2bdff45e6cefe7abc774d53c2e2af2b347c9b21561611133bc90c293d4a60103914df6727dfd499ca703e7496764c20e86931e3037e85f101fef52ae9bd5598401d3ca3c94b73f054acdb599d968102ade0fde85fc38d817055d79efe610d0a7e87236ec4b50afaed5df08c77424a3576933b5b559f31f7b09e792f791175a8084cf4dd9864a22a0dddb09d9eaab078866842963ad6a754d88a6a77d302487affc5c9c6ddc572bf29db70d64b8a6e6c716ead6866809d2b18fe13a7b6ff24d0a3a9fb2bcc1b440796c5bf63a977615da135f59d1cd68338ca9c8873fc2a0a94a369da295cbdf336412ee29e04494a7ccd37a14eaf6140f9770181ec26fb0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"8674af4f4b3e04a62b67e3d6db814c708b57efdb162e6c79475e95db5970f25e","proof":"14a62cb3c1fa47dee0ca04f4d18e3878b8be50e274701c32f90f64c3a71af734a69f7b9b56246169a1795f29058a8f5559036de28c27ebe05573ef84343f231ea699af84c2ac4e811759fd52d4a75baa55ac83caa1ea1f84ceb80206e12dc128d27190d3bec9b0875dbc19d2455120cb16c4ef8910ab979c380d8fab934ee62b7a11a91fd044fe19400f39e3fab5a6759487a248e04f5bf90b796efbdf76850ab5d4865d0449b82c2056068e07e4e23a08f0e1bfa3b3863e4e8899595a31aa05fe607ef2ad87b806d1ee64712392a2b837b84174fe3705d3f5f691898e9a0800f69feac8e4fe502cd625c9bc24be3c015d43f5dac0f7458a123e15a050da43190080025e0b4155427cec02b08250531f71ebc77934cc6d6088dbb630365e636ffed585f44e008d4bee3c495f57c15cbb2a55271694174a2d40e1133ca9128577a69b873e0e174cc754fe22e1a6bf7454e7f9f36c24ea657aa5d840e2c05a7b0edc2bcea4a84d2e85d76bda298dbed36dabb2c95860424e3d4175cdc20330fa790cf1f646b2832a35302ac39877c073ce0696016a73fd0ce866f6c4907988850788f2266a7ccabe2a37f6bc55aa3099c0c8790624344e7a64ca6c67317ae85e4a7422c451aa0ca4acca70cfab067cac9e0ba664f0b65a9a9402f5974b971ef0012461c32d5bbcc1929791e08a754cef2cd2cd5c15c2ce6df1f0b925f5794c4d68be095c89de34a85d9cae0509546546902b0dea37fb6cb81b561253de2cdf631114a844a9009162336502bf6e84c98923677fe6fe832386276592742eaf7a173eee3d19c8c7683b2042ad86d28ab5369c55e7ae043aceebc66775f70d3aea5101ef9a7a2209e4af5037d0dcf564626ab17638995c36e574e4e4c8570518c949011092a0a26e829edc2e80260bc02beacb90695945be9ae3599435ad7ad4fa2903"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f03cab28318b0d7794825afee36b18b20ba1b41a6dc98a792faafd46c2ca5168","proof":"de374f179a292324655a739cb8904387e943edbf056ebd28644f723c1d3a4f08dc96296002f4d56f8496795147b7b90c766c0c0cc3347a20586f5fa2dd5b281660047c762aad9047cd3ba437d8ad8629db0c0ec04dff67acadb893502cb6e254246eef0213723ba146676b103ea59b5cca2a7075172fc5ae5e14a653468b2741524442df3c62eebcc69206af34ddc101854f5c3fc23f5eb50b9918c4d45c890bdde734ef410bbc31a5714a760bb97fdfd40976827ff7fd6b785d3a62ab7d96030453f5d4317da8f947143d04c20f77561dca0e24c6eb39e11626af737d43c108b0283f0081bbb43a11bb4e2f4949ce4425a549e46a6800cc993093b43f2a4d4e804e2a4f74a9dd91c7d713382919830a16fcffb2227cce44e794215b84085563c0edb41014380470afdae74eb9dabd5dcc25cc77d81fa44e281d826fa50245609c656c730e086e82d53528c63f06efb92561799594c3ab527d8802f9f615c45f188e822dda1cd389f02095a33517be7fd80c6cfb7cef6894ab13e6b6f750fc7e7c4ddfe86b0d3f69c8a3a1035bc5e569c3742d7ed8586ee07db1d6945259aa2a40d352df046d0c830e46b9ddfac12fd6fbd2f98f31a4955627842fd19d0e2766405f976fdfecf7d3525bff519305e20c50f6acb1dc0dbf27da41785ed819ba17ec21d5e1720c6fea37bbf54823cc159d85b13a97ebfa4eb7031c0ba966869a41c682f02e9a703c6e67c141fb85c450458a68c0852169f4e7035ae5a5d19bec3f1cdd045d62ad0e8b1a671d4a7a6b7498a2ddc15e493586ad0f4294422d7b212ede1b73ed5107580d63d2ade2d35840243cf0421523b9dd2601eacecfd0c6992ff0bd05a325bba350ba07fd1fd9d1141557c2d5dada8c243e413e6895bed89802de962cbe16a1ad5640563777add8b8cd938fd640444d255946e35a1552100509"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"b4b2d838a73ccc5562b53c3a45bafb7755c5ee4f2fa36a3fb38278e6e34d5f1e","proof":"72ba769de9fa61965e6e6314dd888e1b6ec02f90556c38f56a8f29997b4ee23516b30ab63b9d11c1319185b3812415fb490e43a79908e2ef3d2720d73b08b57a4e2e99f36d25bd8a901d28e9560d7d48e0ea76ac1d096c00cce6bbb1dd0e270aeeea73d9c9a678d418f4662958ce185915608d22f5dd37bf1c7ad0135aa154563e3c93b573e413ef3859b540a5669cc30fbe8053864a17cd3cf457d3d53fa005be956cad229d4de9c12a1fcf92bed64308b1cb8e16faf099d7df6dbbc0170d0dbbe46cbb0f064a11e32a1243df08386e7bade91137a3a281220b06ec05c6de0a687f71bf21a23accb6bb744444688b6e56103c63bc9e940470081baa1377ea783c9000892740c72528acd3367c32a09d62b363ecfd676431aef5d7132b97c464aafcd2c57693d30303862e6965223681a4d2d3aa5b302f71931de82d3f926605e2a96a409725f2db6ba705c78b03513b3a7ed2fd1b7819b1211c700a4427294be2c6d976cb3611813e6d27e8689a21a7a4b52ad43c43913b6a2cc51c23caa314b80003c2256420f09cd66980ed6a3eb12fb4b329decaaf8d691b9eb63d755334e8666dcd6e0935c0daf065c1049e5b6cec73eac10167049a3380a95cf768150d4243d3733edad270cb87a835240f3765a423da668445429c6eb3f998e993672f6a2bb72359bff94a9efda1d5f5c962fcdf9acba85ea43b00344d0b28b690e521dcba69f0e9b5068b377f47ff5c887590534382c274c1b7597786a910b41e610bd016a709ef1757c641ea9db62afd6685a33a2f7887bca33b3423a47134cc3d14aace1241ceaca6c72b6d4f2f3ec895e441c9c569f181214dd0496de601bbf81b4715325105e2b846d7c24974b18e5a2f6f53996002a1945d5fcc501395c1130babf71b45e57dd2a9c4ae1616fd085dfbf179a5945f613b3ce9035fa5eb619406"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"de673054b8dfbd22eead53564b33f90aa5a617288ebed255db5435064870db51","proof":"ccbee453f185c57bd30e5634c35a28cffcba72356bd0881c0dfa63fae27399479ca312621db7f1b6ab75caa2a5d229d5db65276677cf527171b6502c7fbe687d26d82b1bae3e0c28cf2eacc73dd9c6604f9f349af9afa4ac9f067fe79bfb5712b4aa53fcb5453a9e7378d8fe14e1440ead7aeef0f52ca507765e4061b30d4051c2dccc7771c0b36fb3e45d3e942e79619eb4a36e1d6ad0b450a25065900e9505781c7410efdb1a67e0cbb0c9b814ab437b5892ff4cfd3c321642e9b1b1843800f7b3aab4924455cc2e624fe5b5ea2804ee63a4256b381246670cbbc0e278b301dcd8f3898d725409648b9862101b83206ae90420bf868faa097d80eece61640bc02d59ffe883659df156d43e67126b1cd40cd849cb5cb26b00f360a25cbd5e524c5aaa8fafa8173b46a6badbebd6e517e98ebe7139f2de822c3a4269f0a0501d2eedbc01a4983b1ab88a027984cb3a4a549d25cfb8329bf82bf4f72a0447714988ecebbc97742d3c0e307b591c24e1ac1d80c4f39427e662b226e3616ef93d5a14e45ba8d52844671be3b6fae78004545b4088d657ffc36278ae1736d635c549a0632d6040eaa1876c810e81faaf8c81c46d9d3282bd2b910f2bc7aac2dd3b77de7a1386ee774a86c0c32f5806d3507a3b10a9115fdf4cdd882a7f2a3870680f74c2a18e5efd94eb536fa523a86bcbd6f50908be77a3acb5e7f36eec44b75e69be579cd78568a112d6d765233c9cb52685dc12f92f261557b69e9afa5a3aca2e1241fbb38bd6ed3db3e892a97c91022d91fd9b8632c4de930fa9ba3c6c942378aaa98ab762e07e6a09eeca4c13f523256beafae52b870d76a12a8db78957ae16d671defd291fca728a14aefc638d9b7e992404279e293515ee1d43a7e8faea03f353d808000c48e1f03c3b89754500b6d753b66992ca0faffcf0a754de43480c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5c3a1232bfdcac3e4774ad63ed2248e91ab7333f7dcda5d48a90654f76c9fc3e","proof":"7097d0d1f129548e5514211ebfe796c5149cdf06667e9a4730196f13c3ef1a6b905d852661b90823891b17cfa2390ca2d65f447515334c9af217d726f729960d84e63dc5a0a5745ec6505dd6c041bb6538212a9a9f06f5e9442a1a09b1c8f5608af2b88609495356cf80c01ab221f9010429bc0ff6cd10f38ace57dcea432226d71bce6301564d3b0c27757ac47a1c6ac0c5a5be5dc2bf939f34e0a17b65ba066028e5d5c504c9049a390e9cf304bfdfcb609abdb40b5b6f308cfb50d767470b0e93823961298a6ac5df1aa7a7b65258133a37e18918541c5f4090d0fc929c0ee09b2de5b020261262d6b8b7f66d063faab4652b08b747ade60c00791ec0e849f495f81ec851ca5a25ba2279d04fcaf0da6ba021fdbcbe08c5d00be8526fc34d86e9fd0b8819887d8f77e0589da76edb1dc05532d77ff8afa0798e18236340366a528234dea8ed4ccc2d2a4634ac667542f6bc5ab143548fee706c1d7887b95c9a1e608a0729ca6af9f8e4139f1915c1597a0cea0243f5c1a0e5853ef888653170c37f38ee88a202f3b6013d9e8372eda631a79b2efe2c6e22e2dec20bf1e4726a9f7667860ad0c53fb385c04f076c5064f7520a49f7845b0a7ec6c1afb90b66ecebe9a0f35d3883b79c5a175919bda730a35300ffc1957baf5da8f563ea6275c4e3bab2fefed654e16cec1594bf414b15df404b7273b4eaa5c0f761127b590bde60c4cec1d1b7aa40ea608b78df07208484b95c29a185324ebed2cfbebe7f53c8f1e2b0a307000b4839bc1d0be483c5862e8fd90d658aff0c6fcf2e9949185ea6af6a25d3891c0373644f217bc323662b60b1997c0917200a5b23c6cafe6d597370a9363476f1d6587ab5a82646da5056baad2006b46720eb4bb9511fac75097835aac0dfe50ecb6cb3b7160bd6bc4ce0666a6a4cff5ad9cb0aa46f592dc005"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"3c84b087701ac840ae552583bd33833539c1d88edbee1d80e7ade993dbba152a","proof":"e20cca9e0c2a6012f58a7b13b4b41186fe7055ccdf39c3f92c240aa5b729c509f6be1693823fef9d124141e739f65d5d03e6ad87dfb45a94faeb9b05cecc3c6830864758d2e542d724f210460e22c0ce61a0683634066e11335d71010d8d8c31daca8a416d9e17c033595ed96a1164bf7388081529368c20cd639d18901b6d0ba02e35ce4349d82a29e1d9d57dbe2196c542aec0afb3a64acc22e2901082c8048286291a13efd52dee0d8d910135e127621d6c5fd8aa1cb20123be17059a930be6317472d7782d8724a886091ccf548defbb39a3ce2a38bdf3a0e99501fb540c72083e89fcfd0067636d13c2bcbe9073c2e3e5eb5db42ad2e7d0d1f68653632ae6d47c5cfae582e57be4944c53a21683e038623dfb2d315c5ba799840094af28c89095136f93de99dfa73c243790bad9157c4858f2bed9c87f3426bec6b4a64ee6d1065c3d806be84284021ca0f9d1022f0db92e28ca4e8f6230395eb77aee4fc4ce1ba094d6706bf985fc94aa21971abe08620cf03a717b6d25f795dcc281727aa9c29e767bbde785c93d3e55462994872834a6454d82df1e1885f7dbc16c385e3be4e631ddf643d46fd9cc027d3549c9667b0acfe5a7dd12b80047d22844115a5d0378c6a6a2dae515a6a0a038a0efd3475de64871c0e40d1a844510cf4f16c8676434dbb5c28006624930c7b6f079946d257ceda4d5e8986dd4e172b8a02e980dee7a0e7f7e530a6fee48cad9663a81ebe2a5c9cf29a7859aa6b5dd459f4c568947171d409525f7dcfd7c9f54cdccc083ef7465b5bda688c249da7475b653a42b41aa3b06a5b417ea6333c3f078e1fcef8f993ca9ca3824b1a97d25278a693ddd1d8dd74633113f49165be9c6617571c04060d9d0d8b31ad56c9e7a54c401d155591dd5ea28b99139ac0fa01ad19cc90bdf16e8d75006c1516dc783e1c70d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"704d0f7bcb3ce04bd5992739ca2d9659f79781180ae0b2051e2e1c3f8089541d","proof":"2e4b38f20cd631501f6119f3f7a2a83b821dc4afcdcc098aa3ed9a3b0c9a4446a41c2d1e4fbfeae83919634fd84d03c47630a36e3542bc6672f238b90716ac1d7cdded892ea3ea0346dbc322b9935aede58b8693c67c0e71420b1f1bea69d55d0e7b9c37f4071cbaf50820971990467d64437569fd50255c4630475f7cb84007a7bccb64019f6bb41190558578e1a2eb2f33a301bacc5669e8baf567710ebf003963d0af5dcde81cc824313906f8409bd0bd4c30ee86dcc363e6aac37b91d901945ab8371ac40c255c5a6752eef0ec6d6ac20a4891385aab56c3193f26a6f208fe68d14996f87080e7186b15082959be40e3d8062236b408da4e1d5ae4c2807e7037ce729c2e139f78c021470c75388084f6cb775b789b696b36d07ef6b4f36ee86845bf40a17be28771a640272de9dabc3caac08550b6e80da4e62250253f1d7c8a489c39eef913cc0a61e1834e165c0e100591d900d6a289d3ffb8aeaa93134e4b156839b86fd981d0a00550f273c31039360be0fe3da82c4535ffb7e01b297c32287c692d673146ad27a568cd1ca6e798344dd839b3c2ca447f27cd7c6703f455f0d381170c46228f11fef8d9202c50a5dda2d8722fe976c68e75125b4035b80358f865bd52da4d4d8efa2b3121727aef4fb179479b701bbab17910a4dd514c28d9cb5c4d2ecc87f682c8f9a5da6eb794513c2416c36c06940d1eedf4f50dae6fb650ceb6bf62f411f5d106aa83b3ef2a610de1e8b4cd52f10b16a06ed859ba951fd221c3d76698bcbb7160c575e6336d886672fdac48aa5c563581762b17fab120f89059268a5fe5a0e9107eed20d0edcd94f02a37c2aa160fa5410221490cf5af393eac27647ae0ae1f285b269d088ae41756ac3cce66b959c5f0e584086bd2a742e2302b665352e7e02b1ebf63b04b968959d81f0dc46768397ede830d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4c160c301b7efb18c70bb2362f55c43da22890b2f54805ab5793257182c68802","proof":"2c004978914ce05e6b796e581c2113eb0f7e67857e84d6c151e5c6cb3fcb6d51fe92bc60103dd070697b2c8d46e5f7eddf0474bda60475d45859da7e9dc4b311ca877e434b8a809546cb471cbc1ec3fd68347f81538a3b37253e6893e3265a09e2b65ec30ae2236611af570861594334daf40d7bb550ff09d6f63404d840b17c1ff89d4a7b6bcb2824bc19325fc81cebb194aed906ae075a36d49dca5a8c220698478829f1550fd7d82e5cf5c1402ad449c3e5639ba8aafaf79335c3b6a898058767ab38e98c80f0874115a7067caa1ec75c4b7db167790215e703927179960148cb15cf59f93f787204c9c684260e8a851f5e5a74d64377dc4a34e8fc6e8c7a969a3b1687ad5129ab31a6c8e0f4eed33d1a0ddfa17e7aea288b1f8b738aea4f041e5897d97931480d7db3fb0250370b6fa680714608f1762d17612fbaf46f428091c2406e5853bcf023f0e756f7fd9a019af1af2ced246cf8d80ca026e5ec6662ef4d7d41d15986509801771ed22710ada6ad9f286b61da0cfb16609ae0ad17dcf7e9c0eb5b39d6852e03531c092d202271fccb03092b71065d0f0f801dac1b1e7e475eeb119b52fe55d2135ceab9928094bb7f85eb95be9b32b98fba392674682cf07a8be42cc06b04978608bdb752714d61078c136e9a70efb0ed80f0b7238e9ccc71e850865045b8a2fa9d0db91b0f4aa2fd8ed325ae5687ac99a415ed307a7e48d5b110011b794e881859a518b9a52a2d30ff481dffcc81e4e37d9e934d3ad478ae3fc65c4fb3b16876cfe8c50261ae4acc1da60e30ac59c003e7c73068e69c48d5682da1fb9ca6eeca4f2dccd99271da7de2e14f46828dffdca8d825214706210d3762c1a4475a61ad3c525abe571f5dcbcc7229794f2957e8e17949092b68ef62f249c8737525809e5af2818f0f7b271c54d9a7eac387ff8fe77a2f0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"387bd0f7994451e3839081c919732a1849d578c894da163f85c243b305ee6b2b","proof":"80074a493a3456a279b1f829bb8323236254a9307935316528b40cd309de3e21a69925893447df7244864230dcaeff3ec5c2e8e5c071db4818d9c11a3e16c9346aa928e559454280d38a58050b38a71cb19407f36101906cfd8f00204aace32b76699db5505d2113d125d7bfccdce6e54bc96100c9240f496ddf6cd8d3add738387c16c79f55c63fc095090e7d73e53b15b79ff59232c63b15b0d1eddd3a4f03aafb3d0b0ca35de522b90e30165fcc4acc1c3b434c43371dccccd24676f2110fdc3bad4f96cedcd86980d4a2f02c744256d74a433bd92f9b3f1f5a8761c56105a23a919950b8ba692223727fc2558a52af71545f0f9db860f38184b98948c667da5d422fc12ab5abd5033fee0775b7d3b6793315f11bb8b5763bf66e5fc7860fa039a71b91b2b5d116aba1cfccff4dbca668a727e76c024bae013c49275612685ad41a8fe04728efea008f09797052ec021701b43fa27d280d8659d8ab2bf76e04f77e096f5b6284d8c285871bf2f9179811acbb10b3c3dfdabc0e7fbb8dac54420ef76e77471d930a0665f4d87d7f6e190697bbcbe963e7db21ea9a20889a6ca286df6e2ebe781a6956b2c791739d7b119bcfc34af757f19de563159ce1447b5ca8674108865afd8aae690e963d5a9c7b738699dd3eac9ef07c194bdc68430bc231b57dde673a93700657a50b61c9e655fcfaf292201701d1aa397c7a7c9125c2f063d35825db064f984f23b56067ea1883032d35a0f68d44c90c0c15789f06226c11ed822ab85521410c29e06fcc3ab6aa701fa02f8a45d00242f0aaeb9f3c064d08e5449cc0d2bee1c579dd0e3899f74d2b4565fd9ca3cda510221a1eed5ea21910ea581371196655294cb49e7176a41c9e69c818b57fd9b1a91888153001e398182029529c681565a52fd5388527375c9d04f1759dc3b769ab15b9644201"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2a51b0d028ed07e4fa23bd4dfdeaf5924d7fb6ff3ff288f8280d72722a528b31","proof":"a62694e5c7d5bb25243277d01e230c6f8fe19163ada74432d1aac6e61f1f697046661b4fbfe53757a11ec8f5bd47c100c35eb5c0c935ca43762f20b722a4344aa8d3ae67924b23e4596a20247c28fee14a67010958b40e6ea77410709ac5c457aa5a0c7a205118548d1f85038e0f34f4edf093e7296ec25e41756846d1d0c618fc72c753d2db89c250ad780e0275590e89af7914ef8d71d0630b434d0bab9709346e4d2b5faf1cb04294237882c007814454fc090cf4d7ed24bb81add884dc034d17ea269f906297c4e77aceb90833e7193cf84fd1cfb8a79a6026c90a23390916e3577fd7456e2705ae7c26e1776f2d0caac2da8d33dccb4ab1b091156b923232ddefb469f0c40a7266f3b317f17806647f355e0f915eca7e9ef7521ee19667461ea4175060a1d72fc8919775bf3613b963b3014424cb3a13cbd5b65495590dfc8c64ffde2096ca3b62af43f4e1acb88765e0a9b10cb83a3dac218ade64a97674858d4dfd5ce96d9dafcc4279a35f839f664521c744219528719a3775bab6101260b95cd027b5ab5579eabb8eb48150b7925c8af236d84fe2364bb3e8b1411bcabc7bbce3abbb45480fb357e8560ca69649c5f3d7cf7af539317ac6cc530a3a5249a2b2452668f9afdb7168d0a2057fb69af2dae33f72a0f9ac60f5cec01e0d0042eee00c747a2d12aa0fd0011f8a5437917fc16fdbd086cc6632d40034f179f63b21503f1b2bf0375391460ce343aabfac996dff48007c226d93fca53205781a546e3a6f54d7899ccda510dde0315f884ff81109b7aed9df7bf9b329ed6b5a78beed4ddb5caa4410caabab2ab2d43e230d506daa300e4518c2b77c0b38c87189a3bce7c4d85fcca0e25125ff6bc7b46ce906e9f922a23f77a596758993cd06607e13b1abed029e38fc9268e863fc8e117fe3a519af3d8849cafa72c1233a0d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"7c7fb217a1b35bcf7179c652f10dd71826942fef56987e9f6282d9f3dc178b7f","proof":"e64b5f30a9f1d86e69bed8283a07c41ab4a273af458631239fb4fe83f909876ec0a03c7064ffdfe46911db4f9240e88e6be618dd5de3c44ca6174acadac40e13ee070ae7ebfd1f4be00c45ef69d9d0d06d1bd4d6636d37d38a7d0a47dff06c4d8a6d4787cf451437b88a0ddb698404a2657c057a7ce3c85ce6ce7130dd8e115822eaecda30b928ed59ca57128fbad680c705eb4d724ec146bef2fc3e8ea3ff0769938e62ec7e667711952d07a8be6fb4e1b52d6768f508358e5492775bb0630301dc323ab80f592bcd94b750ff2ae7ba441513289e90500a0fcb391cb71bc80220353aca81ddd56f454c00ee54bf31f817e0bbca621e0218bf122b800af0766f38f14423f0f367a8303317ccae506f260ac2989331d7b07990c3910096b5c91a1c68574a5ba08987b48ed247b10d6e8018f530478e328c7cf937b3dfbf5f2c083071084fbaade874246799543972c575a9c30ad2f799cd8e4d727eea547fd741762ec1934fc91195bf4bf26fdc05a279a72f87f3d65d10334896cce79fdfd376760eb235f6fcb639bfb7545bde0c4d435bfe6d62b4edaa62993cc5ff03ec592000a6c228f0d364b5e8e2a597954bf3348820f644f6389dc63258f31caa30832faa8aa0dc40bf1de52e2fcb65094d692a3ff041204de8db67713a72516cd9707eaed9b3cc13de9d0beca6a8f91d94570a092ea5e7222d2254d8233970a0665110d2d36e2cf58ecc6c72f444e552ef7cd5c7c84fc8b279df650f07022775b7422c5c9313249221675b5edf0ed3723edb19dc0aade7ebb36486ac4496205dd5e36376a5a3c4d156153336470dd6042c79c2a3726f02e930e3b9db4d55598cae2201919e0efe20253e4e3f9355e04738d2b009666b675d6b5df37e22b9bd0374a00455e8b937ce118e022a24b3d2009aee830346c0ceb266d2640f29707bd7f92702"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"26b3d7fe84a8ce782a3b0b142ce2b3ccad0345316280704c549341af10809a1e","proof":"e45dc9d17536463cabacad524b337d82fbfc9ff45927d4ded636ce4e3c36bf54e6df56ef8606c7e580dd560cc37295f8552c7ab390c0b3cf21ec3d1b2abe750d88c64343e3257f1edbf6c2d07b26bc6eb27806ebf6e10256014f3cfbae23782410010a3f2395156769a19a522246bd645bf32dcdd8fb53ca286032e74bf1170ef3b4860dc65a3fdd7c4b44da01aff45997cb607fb28f876461b4584625a1ec0132e5b5b3a8b78405f48ffef8f5c5dbe86583a57523691b75ec88a80cecea4100bafc7a71a4bdad94b654f134223257f74d0319ab4fafc780b4fae392241cf308d8c19d6fe63296876be57116ec13a7c43c164bfdb942c3ff54d8e0a6c85a7c2b64ac188abcbe27c0183147c5fb7f9f047a639d0cc02f668899388a113153095eb09fd2a225c048234cd7bbdcf033e081c58c4e39c3aa7d3c1bb1ef3a9f1c534fee23d804edb296620da0b8b2cb3b5642eff1196b73e8ff35e76f762273ea7e1168a191d7c9bd0395573bd00065d62d865b9d480a4b942d2f938960231e6386641869e1c9e048eae00d70be5d0df8cc33c9f0b77ddb616ef2d70cefec56f0bd68d6fbe3117604cd2f6ca2ff749ac68728d2ea3aeadb34af54b08fdc55f5a28f514cb5a945636d9946de117e01d6f33910241b0742c2fc2218a36fd3a406383a56f63be55d22bacaf3a9d47609d856fc61dd0f9ffa337f95a691877a428133b61f68f719b0f7cc9faa1a6bf0cccadf6b1b497bdbabb49a500054ab2079a4acdc5fd4f328484067713c504057768d37e40a6327e61f306f18c7056f6675ad960011644450ee2d02fab17ff1931a62f66f9c7c7a6b2852f23fea5458e80d09b78937939f042e7dfefabb618c5fed2fab32d2e798168e38bdda06733d7f8cb4dafa07317a78b0853c51c863f4e30e64ea7a359fedbeb581801ec5168ac0919ac19f08"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5eceb89bcc294523461bc92a9e3dfb6e7bbfa2c90c480f4b166a50029e24b547","proof":"82a5a828a3b4f18e3340913c62fe116a329b0ed1f0f319d875e12a7a3c6f050c58955900b9ab5ce70bf4d9c6b8014483c6236dcdbb226658f8fb438ccb567657a8b21ea6fa63a8899e8d0a50a93d248c3f81d0345f7d06875f16436b3273447b16063e6e32ad7a993155942e129dbb5ad238c372c2a6a29626d42147d4a34b4ae0febcf0bfc006fddeee2f5d74ea8cf8b97f707fae7a30575fe4dd0d9ec2c7003565abdd8fc5c5be1ab57e956fedb996c10fa131a06ef6b10c5bd44a05412d036c6ed2db34bf1ee23e642f7b7ee3401c71536745f750e9eb146fa51768f5d6000c185e79528a7b6d2e2945d80c32e18a194a70f84d6bac528028e6c8dc19e40ef4485ce65d1bfd88449c706412365c526d58931d65d547fc5074a9cd5e4bd05d2401948edb64a3fa91fcac9d803ccaebde79c22414da83474336a8dc86bbcd59d4525c2b4d0cb928edf6c476c77cada712c38308507c7b2c6efb6f355a0e061b3205483dbe05ddc35b062677c99c21d3b3aaa7088c27716173d8d4d511c96f1c26657a0ea53a8ec6c080bb73e3603c729feb6285eb777f4ef137816aaf96ec06f424b64b4d77498cb4b308b3c5119407468d4d23ce84b0799f59fda97fcaee2e88f82e4c6eddff7632f0d8d075b7ba6bfab72011d13c3e4cb4907cb68d65d967d094ecbca33ea33214095505dbd2561612fa2773412828b6ac2e5101da2a7164d098e458b0f34820365195c5cc6997aa77bfa3b054005deb4503cb97dc35fc222049491f71c8a7886a5fe96ede5ce18a74b4b10f9a3fa5d82487d238a822d60260d1f0a73bdc7446bd53662cf2942115d77ff39fefac0b624487d0f8f87d5a27f14557652e469b3a24e1ba4db65925485046a07fc7fbbc66a3f845dc8e002d0708ac51b6db712a2b1e15d48ce84f5cac0e9b58c2a1ec0333e363ba6d25564302"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"4aababc5e88dd4b2a609e4f7035f7e728f01bc2556ac1b7ab7fcd410dc406a68","proof":"92831ba12638e7a08f1f6c62700c8dff146387338291fefb13757730fc8f3735ec6b85ed736e7f673fef363a186df1b2ba6feaf091d1b8edc5141b7aa71eac5d22d39c4c549378644fa8642ef1be681d4f94bae07c610c57a767344e755b56099eec3301fd6a7aa1937ef2783359900013d72b839f01a31286ab0e21c1095c301db0cfcfef94fb4b9292fa816826d74ef780203bb6d95cdbf5cb4cc832e4d6000756ac880b98b3a1e8b5904e882b9277264e2e74883b5189574823811137f603738f6db2e45d9ee76dafd597301ad35f8667b6160f2e5605e7900ade196d580d8427fb98ebf2b4824cafc8a1bb27f3e655868c03195ea7dbfa51b649ab095a4020db4ef67dbe64aaffdd48fc682a9da620dffa776e23bea9ad6c6dfc138c970cb477c7db0245fec43dca5d5f516961e3cca69587c458e234e0eef1352907e62580ad2d3ff63e52a30b108b9dbc58110996d87d308b49c397ddc683c0c994280cc44f69681a3556aad84b29c020baa7ec839903fd8e65464adf8fcf42ff6e316a9aea8c0b6e0b21fe8541ddc4ab32a4afef706963fc1c03b11cfa0f81351acf0404bcdcef24620ed30a5d42c5e168439883654e0203bd57191a485db4ce0b253986223a408929dadeaef3169eb9cc1f0e84a693d2c9a874703b992aa78a54d7537215c575d5c07e7984696a80c960d921edb020b2ad61239704221a62104a642df6cc7972017d6e4c1aeb28f1ce88d995a4bc0496b5aee24471df1021e51bac527621d4f6aad1f20711b718ca6bab31d647b9d5a125a9b60010ce5478d2f04a3da29b6a5ff7f310545037a09b6811e89f2ee54fe9bfd0b096c414b0b323cdbc39c47ed001c953eef22b52a750b38e2580150052433a7bb66ee6a8a9d9ab96080b0788310b465d6421669e5ca6fffd5255ae008a81796f1e26d447a461ec80c10c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1c8e57e6a4be6469068d112f18539a240610e204547e286eb15877e46ed36432","proof":"fe5055842820b95de8f4d9fb195c13c075a0b01478b5863d8b8a1ff0ed325246b09a332c48ac5e094d1b2ed844bb84ffd656eed82bad048d2a000418530e9a0cf80cd6df901c1dce52731fb9c6b0a62b69661d79c8f1fff74574f44d7199656614733f283f387504c4b28ddadf5f504aac885ca2e81bfcc66dbaa3b0d837b64bb1f1eaa8fc628d4e66bfac36af179953f49495745465aba0889b135c3b2fee037a33297692ebf988ed8657ccd8ccc1fe25df57564b50d8b45de897d148f7810c9261691b83a455b8ab4a75efbc6a2b6112f6cda7101c29389833a8aa340fa60a500440e2bf9975bb66859d50e4d8ba29c1a5630880ed63d9fe583b7cb782a6764ef91405c813fc272e897b63bb5a74fe051805fdb399baba8e3f23d50ad2e05d6ea2de57523e553656d4e964d64a876b69624e50a192703cb41e6770cdf55f455ef9a84c4bfa027d3c1fd23bbaa2520ca2ec7c97b7fe55fdb954e64e0263047beae190fe3fac0ee783fcead9df3a7891655b57636729cb4e79f0d2027201bf11120df5aa9b91ee02bd167f2b23e8cc8618a3f830c3349f3ee788f5d59289174bb8661d9e2aa67ccdf21b8e8d5b450c850fd3ad60a0e1881e996fad5c968ee924346a75f2a8e4bee04b5dc609eef92c4b13c17122110d5168bc3c3fa17513dd37d28ba513f23e2b53ac1f3136139428dc6399976cfde5797ebc95e1c76c613655902fc90d8bffd73a5be2ad77dbcef2ebf313608f51dcc74651f639aa867dff6f60c24a016094c704ecd07d84dc8a98d382f24093a85f8f7f3f0a6bdfea199a015c7987b587b53becefcba770db44f1161b35a339f9350907c855ac6d14ac5355a6bd999abacce1b415e36138bc44435d7f588a80dcc356b3c59b6ea74af8b007427a4997ba354e89c1f604bdb4a0dd2bd9fb952057ccbffe6bd7efb1e9091508"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a43be4128db7cdac1a8196cb7e35aff4b889605e362177d7db5e0f0808863a79","proof":"946cf4cc2bf76a88d4d71aa1fb1ae8c83a8d0651d9553959c70524c676f1e04db41662fb4b9acf5eba626c2e4f1f19fdc81a48b74f88ef33d3b62a90e6d9f511d27db3b8c6e9f3d103d74597030e1cdae490d3a606d0aae52a17c90e8476e715da208bcf82649383cf433f9157c6e8bec54f5df25b343f36ab1a6adfb66ad72d3fde5bdf100ac0f4a73a40366125e1beb08160abc3df9d94885638c6c5012e00bb8d56d2757be8c4972b464e217a8b1cf77fa28fdf26c44ff1ce45e8eac08e0f160aa3ce1c46a1e514e1df2f52a07b65a0d4a537a02bba7139549e02962f2b0e26b7904bece7ce2284bafadde9f8728dd196134aef101a328c3ef48a6357863fb232fa9f8fe8c0a3fe8ddccd33e3d8cf4b4ef0fdfb435efe1298bb6dbd2f0102ea48974e386fad63654530b1fc794704480ae68719fa96ac138927ecb58aef506c54b6bbe7377e45c8360fa8b2195674eaa8a8bbdfcf04dc07c7a64e3a93bd3e141ba5e8c3343b0afa8135495368b8c9c527abe33a6775e4b573ef42e9dd6f0ec898fd5aa8982f4a7c889de326bc517a03c3b00948ffda9a87f2fa3439cd090fc6ea5ab1dd5542ce02ebb390a9665ca77351b93aac30bbfba9e0f8ef8aa1cb4aae18f813423dc51579ce62b0a31810f654b587b458788fc697689783f0d3631136f7c9350f85626474a9cf37278f9769c9f7d7c12006b5be88ea08798f88673a8a27ab39049da20dbda6b30ea8d8209f646f31b1c50981de931aa4923077ce3140174b7d28d156d6fe9d144525bdecaf37a384d9e35f70f4c845a12e4323cc56303731e658da442050187b0cc4ad6eae986808805eb26302a0f61f0c0d6c4550de33a9db2c0297ba50f74f5e8dbd722adfb4f69ec4867db09f8d8283adaaed086e9d14f6f437006a2f16e8fb8bbbf0faf10f80b802750c2f4da2d362f1509d01"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"522a1c3339e3c6b4e0ca16c2d6db05a1a308d63499a4ad0882c2e673447c1755","proof":"40f8fca42101220cd528bd7e6c119d2304b06cbd722f0ac24fda0c5d052162185c7a4a222826dce0829211197c451ee6d5a4bd785b43c1d12f68427a78150e21bc13e59db6de8c8fde69f261c1edcd318f99daa0342084c43f97caf9a0d4fd72542706c3ebcd7edec3f27b017fddc95de90004d5fe1caddf967f9fd16db86435b4b4b38c2ddb2ad58c4c19ce0f4faf344591005b27849b8c3405ed738b1a180e54bde85b71f3e059d36ee3b5a0d5565a6cc7581dd6d2dccf0780434cfb6aee0ff996d8d1026085f76b438a07b3a22a1ed6c2e489c9ab36699973b134e00c410dfad03e759f57a7da2c4355b2d9602bb8e55c044806a5012240318693426fc6630c763f5b02dee168dc427e1182b12e2bd525c7439bcdcb62b1b61704d28ad30a4a15b65e7284b250c05b13d735531dd4129d182e2d5fc8d0788fa3da25bebe3a8293232af7e4d72f0ac102731e5459c4e8a9fad704dab4262d9fd427a8a7ea12460044e5f9583613d4ad9735a45d5d78b2accd2331fb510c56a0c4826ee5624de2df34edaa71c0b91cbe26da4dadd29e2afa3977dc005387a53be488bbe7455b369e50bdfeeef2950f78fe072852a0ca0c36ec4bd4921c2b885b2483cdc90f34f0d88da1a67be6f23313df2acb9e68411dc2b5209c6d02a794b94e84fa08b913880f6abf887819bccddb1cc475f8b0bf94d20eac867122dfb5944611869364178627713632d974822e3e94fa4e49d9c8e06aa37ca820596b4aa49af6f6a2ef5f8820519a622ebf8d62b12e28e1f84e3b074693af8b2f52c167fc1fd1fe24bb5e1c8f48a0f98e4fd93028ddae15469ac9aace2b82b7faaf31df2ae8499427dd237204019ab04c2bba95ab0d76ea6775cf850e62e86d867e3bf5ab01b23737a8063b6875741d0f7c445cac37a44799b39f30e095677a114eb16b358a4f20187800"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"debf0fae44b6ddc231335f768c6070583550b08123f659ba65309565d3889d38","proof":"7022a1d22e2b1131265f244ffa8072b5da38e5b86f8c8355a519d70ea5e4556892d5c1a18d160770b371ccc03fff467ffeb52c879f014c23b23431bb3079fd2a166a82a5d375a37274f349a814567898ac928373b5410723eea7886d1d933010642f7632f98770e635d9f16dab39a2efc5f5d43b8db660a41fa7b0f0bcab25778376b2b61f8f851faacf5ed6f23fdc960f61a512dd2405e0be07be0ee528fa0a0fef879042c59392c485f41da2677e8752a387a5a1f0d90118c3830f4b10f107622a300ea77a74b4fa022da0ebc8f2d607da311564ae1ae4ce65cdf7416d290606284b7a5eedb0fe6057a1fed7627803cc37bec809b7752fda616cde2525ed569e5b73c507607ce78689400b8968dcae54992fe213305175a08e92cce83a89774438d7555f7c4b183aa9ab711e818c87771e4e40c172a5a0de27f9a60c3dbd77127172b9f1bfcfb27204bb6d4c8c7833c1e146a5659fd0ab0bd7511cc8452e25fe71281a020a3a7ad03f5787e4d0a01dd11bbb98a1d8f00b3204c3cce835aa7fce3274f489d88217e5f0d659910cacef06dba82a5f9dc105a221ecc7b0c7187e562fdd18aec6191eb62b56a71a9754e7c79dfcba5a44b319826478a3dd07b57f8c7b1647fec08fd7849b8ab68b06bbdb13695d62bba31fd6ecf8a1e4ccb24a4d300e5e60eb2460ba665a157dbcff5031017c7a1b1f21d72b9565b36e3c727d4be68dbca6531793b01ffc4f6750f34f47afd14af9fc51448c235fb4dc396a647f24b43f2dd43c3f5a802ec9fc106619b81423cc009922829dccca7093596bf575ceafbe703699bcf609944db2a5fec3c366d688a740efbbd04b7695151ed16806175f2d7c850a06d64cf193e008398e8790c5ecc1d67ce6e7dd73f5122f3e6603d9fb12767565d04b9c73b7f27eb422af81f8be73787ad2e096e11e0bd197ab03"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"2c3be8872e4b5c5e7f9f0d7eb6f0d2cd21a7f88a2445a3cad1ff32a85f40c70c","proof":"603c0120142f5f8aa25019138c518acb899ba6199ebb14dfe0b80864045b2324b8e2b52cc6ad05add5cf7cba7b4a77a96f71b8db948f7e324f426ae0aa3f241f48bb5f9af250b215832701801cdf5664d37e71968b1766097534645795da5c3b946622a7920dd6fc1b5c8f598465e0c323ab90d9fb77ec2f0e596be8b584e95ab003f2de198fea2815c998b05f57a47c407a2722a0a8f3b7dd21cb9fa899e80e215807285152d320a961b20f1b357ae6d95d4da025932aa3e077b568177f300adab7b1da52c499f977dccecd87efef8715bd58ad719a62c08f620730ce8d410ec282e9f9f0e8aa00ba54b49de8a53679fe340bbd167788f455d764b9a960122800c33eb852c461999ace80036222ad0945b371bcf1306f3c64db8613cbac9218f67eb5fb7269ee33701d787a4842688a2c9d51d4227be99a8c276474662bc169ce4df0f8f97f36555926299774a575ce8d8d4bba0fa467ca8eb5c49383bbf67a180a25fb4a3718d6286b9b7088b68e824efb9e238c870ac675e6d7082308bf2e2cfafe038d1800ad9ae583b783861fb6c6d07a736c91f9e06afa6d9b836caf4ce40900950346a2e7c22afbc79bba4fb33078fac52676ed000468d5c5686223453cad18c2bc3d8a662838c7806c52778c7986cea8d40fea984c0aef369bb221264a93de7624e8edad50ca1ef052ba8c861f07b45ae3c08114beedc50a7f1d307b8e149c9bb0136c2ed2e94834875513e47ac985ce3a89ac11543d3fdc0c5eea3414a94bf33dcb40a5262accf517845131e3e588dfa557d8d2fc06cf13701b8e365aab1c91b9c4df86b0ebd7f48abe23e1f53fdb1f491aa5427ac9f6739330de3b775cb5a26ecb88e08b850868c06c4af15b89e78554467feec00fef6a4eaf080124e5c920985f29a136d00b04460f01c6bbe979d4c2701949f3227d9866e8cf0c"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"f28dcf1f6aeed716c893b502760532e0636efebfdfbf9c9970587128d7373752","proof":"66bdf0969398948934cfcd9dcc656e896c16f123f4b3e0195788832f4950ca0ffea167ad8936f4caf1ea034103d548be41445ecfc25cbfd06964b6a0df1ebe25f6537a4f8b998e467855b73268887e4454c4c86a92d3956b459558adf7562066b8c4c0e629d71d4dda5193048d0cf6933991802d20d6e39029eb79df1d955e6f2a42e8c0b7d581fa667d213c3b9825d1bdfc3a75fe9f655afa8d7435119889022e1f9342f01fe958af92931d790513d997070bc4e1ff45fef401cf67b7ba2e037d060c117b18df77329fb0c6014a95e14c4a28d9970aaabeaa67ae77831eb00bfa2cce33c49cfa598aa8f27a210c21a09540da4a549359d98a6164a21954c06c1c165e7c0da765c2a8cfaa68229a9f876565af86f1531bb568897a1c09ee542cce7b233ffe50b8aa1fd53a0e68fe061f807ef1638a1022482549ee3b9e70697fec377f6faaa8cfd0a1d1de6255ea0bb2b2af4c505e5ed320991421763a30b367c68c7f024c0d8d2e180bbba93cc1b469a1e82030ab318d6ef779ead258c5bb0662cb1dfa637d2f28fb440a04badd707081f17c0d0f7ce5061df05970d9fb352de464f3313a00ed8a99958cf3f9187b6c000e6d88751e58dab64d009245d3d372f6bbfd2f77cc38b12b6775416c91b45e0f63bc1e0ffa2e0d1604fe37ae2653723ed9b998549c72603829e74a85dfab02673e7f51eb13322f3a369fa7c55ded4d98e5191cca9a1c1c3f48e06a95b5c88447b6379cf9f3ceba27ee81d2ebcd4c61d80a55820194b62fbdb2a1ab1f752bc6f37efba05d2e36ed8efa245e9bd41540e62196fe82841e0b90d7ce0930d607a18a66ef62646723d829abc43be018cb1367ac54b1a06b9e244e6cc331f5c5f1f1d71c318e68e664b46090b2a1d6059f0995f1c0e7db0066524da047a4190c36f21eac65cd25156b34ee8a43e4085b3d0b"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"a44a4714c39a17d3050bf2d5f81baa31ea591e0a4d51ba0a00d2652d0bc02446","proof":"280cf6297c7923e996ac6a9f2c2cb40ebf111631715b316c7ed3b203f93a8f2cc6b0f839acd607136f5b10416fdd54638c83ae00899ed286a71850d61f4d707f1c992ee95c2d8d29268ce0d1f88d7ab5a8698406badaf789fa39df5a707af26d1676af1a955bddd21540da67a4c95081e53811fa7911b07dccada34dfc930745156c34d193a660a5985b656affa303e9744babe24a6d546c5e933e084682e1001939a598217213a1c8417a7bd1ad68a8cc81226b2846cc58020be0c9a9a8ee0e8f3d6b2894fcd16095d4c69c61752e838db7443b5a3314e8e0001b7187ce2b049ed6fe5146522e4d21c232c6cba25217606a7b3701bd8ebdda68c5cd97e91c79fc5a7aca0ffaa290a833711bc126168d8bcfec249aa3d153565285bfdc84ee63ba98a90f57ee53b734e8609cbc59ba9c1828c66f8c6235c7e91878d207480f5e6c7217fa55412fabb5ec68fca8729953c909c01e77203bec9034fbc8caff082784c7aa554aed0879de1bd9a52b3bbeb49bf0969a15dd9fdb9427873a5f323d063602dc5cdb761abe2bef2ffd670b982e305d9f78a6d5bfba65e171d7344b5f03464ed93f5386d528e834baeddb059060ce5f27ac6fbd59446cf8ed879c80995452dcea6d02fbc8f0d2540d2549e6f7b3a91f84e3488df0633b8c96d62358bb276a3f48b433209d0b5d263c49f559dd5573af535aad1cd4bf5a8a7914dacfde4cf093af7db7a5f867451b3d26001e0f06809023be6fde20c66a540fbf320e056814931669d48a25343addf017a0f899a8c370846466b6dd0f29acc7822c360c7eb8f46095347379a1f69a4cfbf29ecb779c39442bff71abca83a10cec3e0ac86c3982085a1dd7874a7b2876168dc09aea08d03cc7af106369b57b17de25e01a025c0a1561f62603cdc5bfc43659e1acfd799f5571031e81f4cbff54f0b393da04"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"ae1fef9448199fe60788d165a2900c36699d93b5abffa28c9b812aeea3a2bf44","proof":"78305e1d7f4324c68c54238e8db859124d6e54655d0871092da9f26e272d1a34302c8c234997ffc3c2934e90fdb031ae15bd7973cab800b1ac9402f1e2eeec3bec8998348821d24f65f1b43625caaa97cd07fdc4aa23cac1ddb4ea8244ef1a31d85f12dd584c0994cc614e4609ad3407dcb5f369a4c21f92da97207936128071564b82a4d663d71de7f2f50838ea38551606a860de13d9527ebed251e68b3f06580d934f5a386e5975070c232735cd93aef6bd45e94b522f1f3eb3c11dde390642c5c0a9457dd2abf25d9c780408d7607121918483b3e554aa715b1472f2d101eebe75a5f28494f19ca3683e487ec74cf057fbfeb8f759ab5f8469e713eadc46f25f9f63109e6dd0f5807f1701575319c39e26e46e0f4f02fe0167d41afa412b980a925571618875f5bafe60cc0bd8bb556b7b8203eb86e8bda0b849697bef7afea2ef68dc7567deaa60ccaf9d8b2313667db57e634e36148d3d66617c96e060be87ab1519d8d7f621e787b4e085f4eec47e55d69edcace9c27f59a40dd4e436685c0403d9648ba1256d74a2f8ed599f36ffdd11af2157c0bc995954501694038e42b6edc62acce7623a97aedeb3ec9d0d96d8cc0dff0a8d8dcc20da185bf13c9a13d47c1a50b08aaf68cd9a28468c4861bac9e44258d8c989ff419e1dcc8c5d0ce2bac62d37c16c23233ba5c9bbedc93c9769e7d77b21d055b0b1f120470909be7eb878a454e38cfe71e2ab46cdf7a44f84318c209025d30327caa669cc77307e2d897c50a395502a5336870d7638ecb91aaf1bb9410deffc17bcd1301e4c2f44d26d68c6310c547693b3e429847bad45a2002dd3eef041480da48f56927f4a3910d3831d9cd35df3a65bd12288d8fedfc5953346e76a7c29d9fae6bf85ef0db9030debd88929ce45052185ab082dee8fb688b7572d20259b43b9adf061430d"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"1263bd51c0c05e6f2f80865e00e2138cb3f0d76ff4d4ca05b179b3ed953a9639","proof":"489a54836edeaadc4f0ba426ba634fb422b2658eddd000931f5bffb17126f46974774ec6067a47d5da65cd8978a4ca936c894d0c76718d0fdb1230d68b22065648eb0ac569c5b29df77bac2e66a785402f5f497c159bc9074f1e596799503b2d625778b717dc0a94bc87adf047267624cfd6987e97c2bb29349c33fec14b852d7570feceeaa9d6cf1cdd52fb0818c2255b16d0b558390a3fa53af39e2d617e00a9717597daada145604215473e45b027976a46bb4853ace893f8550c20b1a20cb9a6b2d20a76dd3ebb367dcccfefeac5bd3b73867969d0623c135b23d759b409c298813b7f2425a36a021ecfdb1ac7c9ea0e2df156b86617b5fcaf280182f86b6418787f6c87649b05937acb8f89fb6591103c7f99a33f4a901dff01f6e302343a54c53e647e5dfe0e4e2d6cab7e32ec68a9b3af0b969536ecadf0b6c6faeb0f14f2c132342d82d28bc1cc9917a0da6e5301cf28713ac4c383670943ca402542a08db041f936ed5e92a03a5bc7ceef5b5479afce3f1ed07ae7ed45d6a686d83042b7962d0b2747168e0ec94eef442e0c729882a88193fd83d6685eac3c6e96427a7eb4846f5f5ba32924b0b6a4b8114fe6081ba31d74576415288fbe5281193be4d779da10454dd826e67dd20a49a8a943c98e7e1dc4ac0cd58da6968c3e8d6048158f22d967395d29205927cfabe226ed13821f06313b246addc0469c749650429d052a6e1f184ac237a4c48194effe67a794fd3da18fd2c31d3291531ac52a56268c397fac293eca118b4616a85a75b83ed8bd0d50063e4185687438b5c904887a9f6e765e2ee73a2560d6934a2bc32827a523d77c785bb72e1a24967f9575aae73d9ba35a12c9250dabdeceb5c5b8b7da8f74b58b163beecaaa1cd848b80b3bb911c96b00387c4145fd0f3f844aab408fd567daf63001c8dce194a147a903"} +{"features":{"flags":{"bits":0},"maturity":0},"commitment":"5a6f9e54a95dc33d1bc3f3c88b49a58bf19696761a9fc283ce4680fc0eaef928","proof":"8609f3be96035ff86d6fb197dc2c0ccf21baa1185e5fcb7a4723bad4008cd2529e10ab0e77a3ca1274452b54c31c619749d50123827a2eeeb3cdef293e4dea0ea24113f92d0e79c0215aaa71cb03a027de6111f96b042d786360d3292cb8ac5b462ba251d107c3784444231e5c6e5957c4e591127f2cafee3e2948380a5a9439865d469075200f6977a703765fd4b083c4c54f7d20a9ff9a999d29e51a078902e39273dc90156d34a33f446037099c333f116f913a4dfbf77f16df6cd556f90a22d04c539712da96631cb27d72e7e4e9ea661c5ba7ac614ad1b76c44f2dbb808f2ea3ca7fd964bf5ae05b4b5edadf2d4f931ac42253dfa615952ca6f7431f179b8e4229719f8aae9198eaa390ed78be3d4228d8077fe1c107dded5e1deef9300803f9d9d11a4e121528a9a307b5ea7aa9eb6e86951998f70b011d4d247dc64069897e8416f528904cd61d749ed2aff1147a58d634f297326d45b0d0b89f3c83716ec5b4aa859ab4cb9eea88be87375db0cbfcec03f98e37434b622d69b449d326c8348ba56114430bdfa00faf68a5faaf90f845fa7b54c572a70342844366455ace113a5c0cf669db71e4a96a273b4530275f85b81088b10bc75dfe60ea9036522f66fd2b69643ef982c4db474b8356d181208711884272a98e6b5b92155171c1cf5d68b55328f7fb2832f7e7e192c158fdcf8c4ed911d7de7670c238d903a6f02a88f416f328c1df958ff92e4935759328a138e1c76a3d0a04609e164fb6959ded57f5a66f0e08338ec440c4dd8969c902ae3f581b07d788bf18efca839cb25881d153913e8606beda22c6f7a6fdc04d80b1a9a23b188e3500fc0d4db6df37e2050874424805ff23734de96f944f7cb9e3f3f49e9f246ede8e33086717edf083e512547065192fd086cc4a5ac33c27e7121d7a5e84461103798d06ed0900209"} diff --git a/base_layer/core/src/blocks/genesis_block.rs b/base_layer/core/src/blocks/genesis_block.rs index 6fe3d1c8e1..13c4184c05 100644 --- a/base_layer/core/src/blocks/genesis_block.rs +++ b/base_layer/core/src/blocks/genesis_block.rs @@ -19,44 +19,143 @@ // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Portions of this file were originally copyrighted (c) 2018 The Grin Developers, issued under the Apache License, -// Version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0. // This file is used to store the genesis block -use crate::blocks::{aggregated_body::AggregateBody, block::Block, BlockBuilder}; - -use crate::{blocks::BlockHeader, types::TariProofOfWork}; -use chrono::{DateTime, NaiveDate, Utc}; -use tari_crypto::ristretto::*; - -pub fn get_genesis_block() -> Block { - let header = get_gen_header(); - BlockBuilder::new().with_header(header).build() -} - -pub fn get_gen_header() -> BlockHeader { - BlockHeader { - version: 0, - /// Height of this block since the genesis block (height 0) - height: 0, - /// Hash of the block previous to this in the chain. - prev_hash: vec![0; 32], - /// Timestamp at which the block was built. - timestamp: DateTime::::from_utc(NaiveDate::from_ymd(2020, 1, 1).and_hms(1, 1, 1), Utc), - /// This is the MMR root of the outputs - output_mr: vec![0; 32], - /// This is the MMR root of the range proofs - range_proof_mr: vec![0; 32], - /// This is the MMR root of the kernels - kernel_mr: vec![0; 32], - /// Total accumulated sum of kernel offsets since genesis block. We can derive the kernel offset sum for *this* - /// block from the total kernel offset of the previous block header. - total_kernel_offset: RistrettoSecretKey::from(0), - /// Proof of work summary - total_difficulty: Default::default(), - /// Nonce used - nonce: 0, - pow: TariProofOfWork::default(), +use crate::{ + blocks::{block::Block, BlockHeader}, + proof_of_work::{Difficulty, PowAlgorithm, ProofOfWork}, +}; + +use crate::transactions::{ + aggregated_body::AggregateBody, + bullet_rangeproofs::BulletRangeProof, + tari_amount::MicroTari, + transaction::{KernelFeatures, OutputFeatures, OutputFlags, TransactionKernel, TransactionOutput}, + types::{Commitment, PrivateKey, PublicKey, Signature}, +}; +use tari_crypto::tari_utilities::{hash::Hashable, hex::*}; + +// TODO: see issue #1145 +// The values contain in these blocks are temporary. They should be replaced by the actual values before test net. + +pub fn get_mainnet_genesis_block() -> Block { + unimplemented!() +} + +pub fn get_mainnet_block_hash() -> Vec { + get_mainnet_genesis_block().hash() +} + +pub fn get_mainnet_gen_header() -> BlockHeader { + get_mainnet_genesis_block().header +} + +/// This will get the rincewind gen block +pub fn get_rincewind_genesis_block() -> Block { + // lets get the block + let mut block = get_rincewind_genesis_block_raw(); + // Lets load in the rincewind faucet tx's + let mut utxos = Vec::new(); + let file = include_str!("faucets/alphanet_faucet.json"); + for line in file.lines() { + let utxo: TransactionOutput = serde_json::from_str(line).unwrap(); + utxos.push(utxo); + } + // fix headers to new mmr roots after adding utxos + block.header.output_mr = from_hex("f9bfcc0bfae8f90991ea7cc9a625a411dd757cce088cbf740848570daa43daff").unwrap(); + block.header.range_proof_mr = from_hex("fbadbae2bf8c7289d77af52edc80490cb476a917abd0afeab8821913791b678f").unwrap(); + block.header.kernel_mr = from_hex("a40db2278709c3fb0e03044ca0f5090ffca616b708850d1437af4d584e17b97a").unwrap(); + block.body.add_outputs(&mut utxos); + block +} + +pub fn get_rincewind_genesis_block_raw() -> Block { + let sig = Signature::new( + PublicKey::from_hex("82f5e603783cfe8b7d50ec1fefb7841398bffcadcb6102dae1f83b533f0aec41").unwrap(), + PrivateKey::from_hex("05af349cb5618e636021ca66a3fd21067b6f9b159b75b7783985a534726fe509").unwrap(), + ); + let mut body = AggregateBody::new( + vec![], + vec![TransactionOutput { + features: OutputFeatures { + flags: OutputFlags::COINBASE_OUTPUT, + maturity: 1, + }, + commitment: Commitment::from_hex( + "feba9eeee21bb01aea86cfa52ea3c905647e3785040581dd9c1f6c89510e6548", + ) + .unwrap(), + proof: BulletRangeProof::from_hex("9aadf23887b4bf69f2743c773aabfa0c70a270971d2fc9ad123340a1b6a1d015783012da766307c628a84bc2453f28f0157d64943e95a59ea3e4d892ef66bd70d85026432df39294a3000ed243c63e3e041c234498149962b447e3c8d234631bf036cd4f0649347957795b28a683e3b6190f1a42b51e5debbef7d2cee4941416ad15d8080ab498d7cfdee64d62a8a3701986aecede76894602689862d4465c0160fd23d97cc4379e857725905d757154f45b01803d8562bf03c25217e02ba600628c90e81f43cf94dd10b4ebd9acd3ee388ec99e659a06162af7ca3f34c84d0ef23dccbabc307af93fadad74c7c9df9e896f1c61b4340b55cf9a270218420c40ce8fec166f51e187fb9e53083e514047ff9970627b3d29dadcd8c4555e61ec2e8c09b99a9de4ac15c8583522a647960a1dd476992787f69cc9a2bf3a302fb0426ad6a121be5beb98a621e9171718901a1e6ff81e73dde7d5bbe32251ddf363423262293b81d2cb40354c41ed317b3c06f2fbddfdc1cf07547a854a50416b8d5f4adc39f021a189a5f032d1d445a19bf57c921e34d3f7d6ff8227490d50391765ccf24b84649c88f5a2aece1936c638c0b44eb21a2d9e1c159b96061d674600139e9e09d7e07ed7cc0f2c172da4104568e58ae3ef47b0ab5e7aafac7fdb05ef3a229812c4013d8fb19334f0b3488ce9bf0fc280c565ebde6197c9060740ae5d1a808de889dbb123e26fcf1dd99501f99ba6b6057aa0b0ffd07cfdf0690a2a9c79ecff61da6318c81e9066d3fb5fbc3b102b3e1d586a8933c653887c37f24c29257a7d123fa43f962f073c62e6cb0b318743b9bf9fc9043c0f56a9164eb0666174bf76e5e4b4264a35ab25edeae69af5388d7bec4690ce67304812a44df7af9909033a8234c2a777cf66b48c326de09fb2df8b23477a05d33cb59c0c4aa43ca60c").unwrap(), + }], + vec![TransactionKernel { + features: KernelFeatures::COINBASE_KERNEL, + fee: MicroTari(0), + lock_height: 0, + meta_info: None, + linked_kernel: None, + excess: Commitment::from_hex( + "d811169b90cf749d056416121ba34bf8b435e1c1549c446433c233289fc1372c", + ) + .unwrap(), + excess_sig: sig, + }], + ); + body.sort(); + Block { + header: BlockHeader { + version: 0, + height: 0, + prev_hash: vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + ], + timestamp: 1_578_296_727.into(), + output_mr: from_hex("fab84d9d797c272b33011caa78718f93c3d5fc44c7d35bbf138613440fca2c79").unwrap(), + range_proof_mr: from_hex("63a36ba139a884434702dffccec348b02ba886d3851a19732d8d111a54e17d56").unwrap(), + kernel_mr: from_hex("b097af173dc852862f48af67aa57f48c47d20bc608d77b46a3018999bffba911").unwrap(), + total_kernel_offset: PrivateKey::from_hex( + "0000000000000000000000000000000000000000000000000000000000000000", + ) + .unwrap(), + nonce: 0, + pow: ProofOfWork { + accumulated_monero_difficulty: Difficulty::min(), + accumulated_blake_difficulty: Difficulty::min(), + pow_algo: PowAlgorithm::Blake, + pow_data: vec![], + }, + }, + body, + } +} + +pub fn get_rincewind_block_hash() -> Vec { + get_rincewind_genesis_block().hash() +} + +pub fn get_rincewind_gen_header() -> BlockHeader { + get_rincewind_genesis_block().header +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn load_rincewind() { + let block = get_rincewind_genesis_block(); + + assert_eq!(block.body.outputs().len(), 4001); + } + + #[test] + fn load_rincewind_mmrs() { + let block = get_rincewind_genesis_block(); + let output_mr = from_hex("f9bfcc0bfae8f90991ea7cc9a625a411dd757cce088cbf740848570daa43daff").unwrap(); + let range_proof_mr = from_hex("fbadbae2bf8c7289d77af52edc80490cb476a917abd0afeab8821913791b678f").unwrap(); + let kernel_mr = from_hex("a40db2278709c3fb0e03044ca0f5090ffca616b708850d1437af4d584e17b97a").unwrap(); + + assert_eq!(output_mr, block.header.output_mr); + assert_eq!(range_proof_mr, block.header.range_proof_mr); + assert_eq!(kernel_mr, block.header.kernel_mr); } } diff --git a/base_layer/core/src/blocks/mod.rs b/base_layer/core/src/blocks/mod.rs index 347494b809..1fc82f74b2 100644 --- a/base_layer/core/src/blocks/mod.rs +++ b/base_layer/core/src/blocks/mod.rs @@ -20,11 +20,14 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -pub(crate) mod aggregated_body; mod block; pub(crate) mod blockheader; +mod new_block_template; +mod new_blockheader_template; pub mod genesis_block; pub use block::{Block, BlockBuilder, BlockValidationError}; -pub use blockheader::BlockHeader; +pub use blockheader::{BlockHash, BlockHeader, BlockHeaderValidationError}; +pub use new_block_template::NewBlockTemplate; +pub use new_blockheader_template::NewBlockHeaderTemplate; diff --git a/base_layer/core/src/consensus.rs b/base_layer/core/src/blocks/new_block_template.rs similarity index 58% rename from base_layer/core/src/consensus.rs rename to base_layer/core/src/blocks/new_block_template.rs index 60db3893d9..b34d653885 100644 --- a/base_layer/core/src/consensus.rs +++ b/base_layer/core/src/blocks/new_block_template.rs @@ -20,40 +20,37 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::emission::EmissionSchedule; +use crate::{ + blocks::{new_blockheader_template::NewBlockHeaderTemplate, Block}, + transactions::aggregated_body::AggregateBody, +}; +use serde::{Deserialize, Serialize}; +use std::fmt::{Display, Formatter}; -/// This is used to control all consensus values. -pub struct ConsensusRules { - /// The min height maturity a coinbase utxo must have - coinbase_lock_height: u64, - /// The emission schedule to use for coinbase rewards - emission_schedule: EmissionSchedule, - /// Current version of the blockchain - blockchain_version: u16, +/// The new block template is used constructing a new partial block, allowing a miner to added the coinbase utxo and as +/// a final step the Base node to add the MMR roots to the header. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct NewBlockTemplate { + pub header: NewBlockHeaderTemplate, + pub body: AggregateBody, } -impl ConsensusRules { - pub fn current() -> Self { - // CONSENSUS_RULES - ConsensusRules { - coinbase_lock_height: 1, - emission_schedule: EmissionSchedule::new(10_000_000.into(), 0.999, 100.into()), - blockchain_version: 1, +impl From for NewBlockTemplate { + fn from(block: Block) -> Self { + let Block { header, body } = block; + Self { + header: header.into(), + body, } } +} - /// The min height maturity a coinbase utxo must have - pub fn coinbase_lock_height(&self) -> u64 { - self.coinbase_lock_height - } - - /// Current version of the blockchain - pub fn blockchain_version(&self) -> u16 { - self.blockchain_version - } - - /// The emission schedule to use for coinbase rewards - pub fn emission_schedule(&self) -> &EmissionSchedule { - &self.emission_schedule +impl Display for NewBlockTemplate { + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { + fmt.write_str("----------------- Block template-----------------\n")?; + fmt.write_str("--- Header ---\n")?; + fmt.write_str(&format!("{}\n", self.header))?; + fmt.write_str("--- Body ---\n")?; + fmt.write_str(&format!("{}\n", self.body)) } } diff --git a/base_layer/core/src/blocks/new_blockheader_template.rs b/base_layer/core/src/blocks/new_blockheader_template.rs new file mode 100644 index 0000000000..057bc496e1 --- /dev/null +++ b/base_layer/core/src/blocks/new_blockheader_template.rs @@ -0,0 +1,80 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + blocks::{ + blockheader::{hash_serializer, BlockHeader}, + BlockHash, + }, + proof_of_work::ProofOfWork, + transactions::types::BlindingFactor, +}; +use serde::{Deserialize, Serialize}; +use std::fmt::{Display, Error, Formatter}; +use tari_crypto::tari_utilities::hex::Hex; + +/// The NewBlockHeaderTemplate is used for the construction of a new mineable block. It contains all the metadata for +/// the block that the Base Node is able to complete on behalf of a Miner. +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct NewBlockHeaderTemplate { + /// Version of the block + pub version: u16, + /// Height of this block since the genesis block (height 0) + pub height: u64, + /// Hash of the block previous to this in the chain. + #[serde(with = "hash_serializer")] + pub prev_hash: BlockHash, + /// Total accumulated sum of kernel offsets since genesis block. We can derive the kernel offset sum for *this* + /// block from the total kernel offset of the previous block header. + pub total_kernel_offset: BlindingFactor, + /// Proof of work summary + pub pow: ProofOfWork, +} + +impl From for NewBlockHeaderTemplate { + fn from(header: BlockHeader) -> Self { + Self { + version: header.version, + height: header.height, + prev_hash: header.prev_hash, + total_kernel_offset: header.total_kernel_offset, + pow: header.pow, + } + } +} + +impl Display for NewBlockHeaderTemplate { + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), Error> { + let msg = format!( + "Version: {}\nBlock height: {}\nPrevious block hash: {}\n", + self.version, + self.height, + self.prev_hash.to_hex(), + ); + fmt.write_str(&msg)?; + fmt.write_str(&format!( + "Total offset: {}\nProof of work: {}", + self.total_kernel_offset.to_hex(), + self.pow + )) + } +} diff --git a/base_layer/core/src/chain_storage/async_db.rs b/base_layer/core/src/chain_storage/async_db.rs new file mode 100644 index 0000000000..2b02316135 --- /dev/null +++ b/base_layer/core/src/chain_storage/async_db.rs @@ -0,0 +1,83 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + blocks::{Block, BlockHeader, NewBlockTemplate}, + chain_storage::{ + blockchain_database::BlockAddResult, + metadata::ChainMetadata, + BlockchainBackend, + BlockchainDatabase, + ChainStorageError, + HistoricalBlock, + MmrTree, + }, + transactions::{ + transaction::{TransactionKernel, TransactionOutput}, + types::HashOutput, + }, +}; +use tari_mmr::MerkleProof; + +macro_rules! make_async { + ($fn:ident() -> $rtype:ty) => { + pub async fn $fn(db: BlockchainDatabase) -> Result<$rtype, ChainStorageError> + where T: BlockchainBackend + 'static { + tokio::task::spawn_blocking(move || { + db.$fn() + }) + .await + .or_else(|err| Err(ChainStorageError::BlockingTaskSpawnError(err.to_string()))) + .and_then(|inner_result| inner_result) + } + }; + + ($fn:ident($($param:ident:$ptype:ty),+) -> $rtype:ty) => { + pub async fn $fn(db: BlockchainDatabase, $($param: $ptype),+) -> Result<$rtype, ChainStorageError> + where T: BlockchainBackend + 'static { + tokio::task::spawn_blocking(move || db.$fn($($param),+)) + .await + .or_else(|err| Err(ChainStorageError::BlockingTaskSpawnError(err.to_string()))) + .and_then(|inner_result| inner_result) + } + }; +} + +make_async!(get_metadata() -> ChainMetadata); +make_async!(fetch_kernel(hash: HashOutput) -> TransactionKernel); +make_async!(fetch_header_with_block_hash(hash: HashOutput) -> BlockHeader); +make_async!(fetch_header(block_num: u64) -> BlockHeader); +make_async!(fetch_utxo(hash: HashOutput) -> TransactionOutput); +make_async!(fetch_stxo(hash: HashOutput) -> TransactionOutput); +make_async!(fetch_orphan(hash: HashOutput) -> Block); +make_async!(is_utxo(hash: HashOutput) -> bool); +make_async!(fetch_mmr_root(tree: MmrTree) -> HashOutput); +make_async!(fetch_mmr_only_root(tree: MmrTree) -> HashOutput); +make_async!(calculate_mmr_root(tree: MmrTree,additions: Vec,deletions: Vec) -> HashOutput); +make_async!(add_block(block: Block) -> BlockAddResult); +make_async!(calculate_mmr_roots(template: NewBlockTemplate) -> Block); + +// make_async!(is_new_best_block(block: &Block) -> bool); +make_async!(fetch_block(height: u64) -> HistoricalBlock); +make_async!(fetch_block_with_hash(hash: HashOutput) -> Option); +make_async!(rewind_to_height(height: u64) -> Vec); +make_async!(fetch_mmr_proof(tree: MmrTree, pos: usize) -> MerkleProof); diff --git a/base_layer/core/src/chain_storage/blockchain_database.rs b/base_layer/core/src/chain_storage/blockchain_database.rs index a477aa5d6a..c09376aa45 100644 --- a/base_layer/core/src/chain_storage/blockchain_database.rs +++ b/base_layer/core/src/chain_storage/blockchain_database.rs @@ -19,34 +19,80 @@ // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// use crate::{ - blocks::{Block, BlockBuilder, BlockHeader}, + blocks::{blockheader::BlockHash, Block, BlockBuilder, BlockHeader, NewBlockTemplate}, chain_storage::{ - db_transaction::{DbKey, DbTransaction, DbValue, MetadataKey, MetadataValue, MmrTree}, + db_transaction::{DbKey, DbKeyValuePair, DbTransaction, DbValue, MetadataKey, MetadataValue, MmrTree}, error::ChainStorageError, ChainMetadata, HistoricalBlock, }, + consensus::ConsensusManager, proof_of_work::Difficulty, - transaction::{TransactionInput, TransactionKernel, TransactionOutput}, - types::{Commitment, HashOutput}, + transactions::{ + transaction::{TransactionInput, TransactionKernel, TransactionOutput}, + types::{BlindingFactor, Commitment, CommitmentFactory, HashOutput}, + }, + validation::{Validation, Validator}, }; use croaring::Bitmap; use log::*; -use std::sync::{Arc, RwLock, RwLockReadGuard}; -use tari_mmr::{Hash, MerkleCheckPoint, MerkleProof}; -use tari_utilities::Hashable; +use serde::{Deserialize, Serialize}; +use std::{ + collections::VecDeque, + sync::{Arc, RwLock, RwLockReadGuard}, +}; +use tari_crypto::{ + commitment::HomomorphicCommitmentFactory, + tari_utilities::{hex::Hex, Hashable}, +}; +use tari_mmr::{Hash, MerkleCheckPoint, MerkleProof, MutableMmrLeafNodes}; -const LOG_TARGET: &str = "core::chain_storage::database"; +const LOG_TARGET: &str = "c::cs::database"; #[derive(Clone, Debug, PartialEq)] pub enum BlockAddResult { Ok, BlockExists, OrphanBlock, - ChainReorg, + ChainReorg((Box>, Box>)), // Set of removed blocks and set of added blocks +} + +/// MutableMmrState provides the total number of leaf nodes in the base MMR and the requested leaf nodes. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct MutableMmrState { + pub total_leaf_count: usize, + pub leaf_nodes: MutableMmrLeafNodes, +} + +/// A placeholder struct that contains the two validators that the database uses to decide whether or not a block is +/// eligible to be added to the database. The `block` validator should perform a full consensus check. The `orphan` +/// validator needs to check that the block is internally consistent, but can't know whether the PoW is sufficient, +/// for example. +/// The `GenesisBlockValidator` is used to check that the chain builds on the correct genesis block. +/// The `ChainTipValidator` is used to check that the accounting balance and MMR states of the chain state is valid. +pub struct Validators { + block: Arc>, + orphan: Arc>, +} + +impl Validators { + pub fn new(block: impl Validation + 'static, orphan: impl Validation + 'static) -> Self { + Self { + block: Arc::new(Box::new(block)), + orphan: Arc::new(Box::new(orphan)), + } + } +} + +impl Clone for Validators { + fn clone(&self) -> Self { + Validators { + block: Arc::clone(&self.block), + orphan: Arc::clone(&self.orphan), + } + } } /// Identify behaviour for Blockchain database back ends. Implementations must support `Send` and `Sync` so that @@ -71,13 +117,45 @@ pub trait BlockchainBackend: Send + Sync { /// Fetches the merklish root for the MMR tree identified by the key. This function should only fail if there is an /// access or integrity issue with the back end. fn fetch_mmr_root(&self, tree: MmrTree) -> Result; + /// Returns only the MMR merkle root without the state of the roaring bitmap. + fn fetch_mmr_only_root(&self, tree: MmrTree) -> Result; + /// Fetches the merklish root for the MMR tree identified by the key after the current additions and deletions have + /// temporarily been applied. Deletions of hashes from the MMR can only be applied for UTXOs. + fn calculate_mmr_root( + &self, + tree: MmrTree, + additions: Vec, + deletions: Vec, + ) -> Result; /// Constructs a merkle proof for the specified merkle mountain range and the given leaf position. - fn fetch_mmr_proof(&self, tree: MmrTree, pos: u64) -> Result; - /// The nth MMR checkpoint (the list of nodes added & deleted) for the given Merkle tree. The index is the n-th - /// checkpoint (block) from the pruning horizon block. - fn fetch_mmr_checkpoint(&self, tree: MmrTree, index: u64) -> Result; + fn fetch_mmr_proof(&self, tree: MmrTree, pos: usize) -> Result; + /// Fetches the checkpoint corresponding to the provided height, the checkpoint consist of the list of nodes + /// added & deleted for the given Merkle tree. + fn fetch_checkpoint(&self, tree: MmrTree, height: u64) -> Result; /// Fetches the leaf node hash and its deletion status for the nth leaf node in the given MMR tree. fn fetch_mmr_node(&self, tree: MmrTree, pos: u32) -> Result<(Hash, bool), ChainStorageError>; + /// Performs the function F for each orphan block in the orphan pool. + fn for_each_orphan(&self, f: F) -> Result<(), ChainStorageError> + where + Self: Sized, + F: FnMut(Result<(HashOutput, Block), ChainStorageError>); + /// Performs the function F for each transaction kernel. + fn for_each_kernel(&self, f: F) -> Result<(), ChainStorageError> + where + Self: Sized, + F: FnMut(Result<(HashOutput, TransactionKernel), ChainStorageError>); + /// Performs the function F for each block header. + fn for_each_header(&self, f: F) -> Result<(), ChainStorageError> + where + Self: Sized, + F: FnMut(Result<(u64, BlockHeader), ChainStorageError>); + /// Performs the function F for each UTXO. + fn for_each_utxo(&self, f: F) -> Result<(), ChainStorageError> + where + Self: Sized, + F: FnMut(Result<(HashOutput, TransactionOutput), ChainStorageError>); + /// Returns the stored header with the highest corresponding height. + fn fetch_last_header(&self) -> Result, ChainStorageError>; } // Private macro that pulls out all the boiler plate of extracting a DB query result from its variants @@ -120,11 +198,18 @@ macro_rules! fetch { /// /// ``` /// use tari_core::{ -/// chain_storage::{BlockchainDatabase, MemoryDatabase}, -/// types::HashDigest, +/// chain_storage::{BlockchainDatabase, MemoryDatabase, Validators}, +/// consensus::{ConsensusManagerBuilder, Network}, +/// transactions::types::HashDigest, +/// validation::{mocks::MockValidator, Validation}, /// }; /// let db_backend = MemoryDatabase::::default(); -/// let mut db = BlockchainDatabase::new(db_backend).unwrap(); +/// let validators = Validators::new(MockValidator::new(true), MockValidator::new(true)); +/// let db = MemoryDatabase::::default(); +/// let network = Network::LocalNet; +/// let rules = ConsensusManagerBuilder::new(network).build(); +/// let mut db = BlockchainDatabase::new(db_backend, rules.clone()).unwrap(); +/// db.set_validators(validators); /// // Do stuff with db /// ``` pub struct BlockchainDatabase @@ -132,18 +217,33 @@ where T: BlockchainBackend { metadata: Arc>, db: Arc, + validators: Option>, + consensus_manager: ConsensusManager, } impl BlockchainDatabase where T: BlockchainBackend { /// Creates a new `BlockchainDatabase` using the provided backend. - pub fn new(db: T) -> Result { + pub fn new(db: T, consensus_manager: ConsensusManager) -> Result { let metadata = Self::read_metadata(&db)?; - Ok(BlockchainDatabase { + let blockchain_db = BlockchainDatabase { metadata: Arc::new(RwLock::new(metadata)), db: Arc::new(db), - }) + validators: None, + consensus_manager, + }; + if let None = blockchain_db.get_height()? { + let genesis_block = blockchain_db.consensus_manager.get_genesis_block(); + let genesis_block_hash = genesis_block.hash(); + blockchain_db.store_new_block(genesis_block)?; + blockchain_db.update_metadata(0, genesis_block_hash)?; + } + Ok(blockchain_db) + } + + pub fn set_validators(&mut self, validators: Validators) { + self.validators = Some(validators); } /// Reads the blockchain metadata (block height etc) from the underlying backend and returns it. @@ -152,13 +252,12 @@ where T: BlockchainBackend fn read_metadata(db: &T) -> Result { let height = fetch!(meta db, ChainHeight, None); let hash = fetch!(meta db, BestBlock, None); - let work = fetch!(meta db, AccumulatedWork, 0); + let _work = fetch!(meta db, AccumulatedWork, 0); // Set a default of 2880 blocks (2 days with 1min blocks) let horizon = fetch!(meta db, PruningHorizon, 2880); Ok(ChainMetadata { height_of_longest_chain: height, best_block: hash, - total_accumulated_difficulty: work, pruning_horizon: horizon, }) } @@ -186,9 +285,9 @@ where T: BlockchainBackend Err(e) => { error!( target: LOG_TARGET, - "Could not read metadata from database. {}. We're going to panic here. Perhaps restarting will \ + "Could not read metadata from database. {:?}. We're going to panic here. Perhaps restarting will \ fix things", - e.to_string() + e ); Err(ChainStorageError::CriticalError) }, @@ -199,22 +298,35 @@ where T: BlockchainBackend self.metadata.read().map_err(|e| { error!( target: LOG_TARGET, - "An attempt to get sa read lock on the blockchain metadata failed. {}", - e.to_string() + "An attempt to get a read lock on the blockchain metadata failed. {:?}", e ); ChainStorageError::AccessError("Read lock on blockchain metadata failed".into()) }) } fn update_metadata(&self, new_height: u64, new_hash: Vec) -> Result<(), ChainStorageError> { - let mut db = self.metadata.write().map_err(|_| { + let mut db = self.metadata.write().map_err(|e| { + error!( + target: LOG_TARGET, + "Could not obtain write access to blockchain metadata after storing block. {:?}", e + ); ChainStorageError::AccessError( "Could not obtain write access to blockchain metadata after storing block".into(), ) })?; db.height_of_longest_chain = Some(new_height); db.best_block = Some(new_hash); - Ok(()) + + let mut txn = DbTransaction::new(); + txn.insert(DbKeyValuePair::Metadata( + MetadataKey::ChainHeight, + MetadataValue::ChainHeight(db.height_of_longest_chain), + )); + txn.insert(DbKeyValuePair::Metadata( + MetadataKey::BestBlock, + MetadataValue::BestBlock(db.best_block.clone()), + )); + self.commit(txn) } /// Returns the height of the current longest chain. This method will only fail if there's a fairly serious @@ -239,8 +351,7 @@ where T: BlockchainBackend /// calling [BlockchainDatabase::try_recover_metadata] in that case to re-sync the metadata; or else /// just exit the program. pub fn get_total_work(&self) -> Result { - let metadata = self.access_metadata()?; - Ok(metadata.total_accumulated_difficulty.into()) + unimplemented!() } /// Returns the transaction kernel with the given hash. @@ -253,6 +364,23 @@ where T: BlockchainBackend fetch!(self, block_num, BlockHeader) } + /// Returns the block header corresponding` to the provided BlockHash + pub fn fetch_header_with_block_hash(&self, hash: HashOutput) -> Result { + fetch!(self, hash, BlockHash) + } + + pub fn fetch_tip_header(&self) -> Result { + self.db + .fetch_last_header() + .or_else(|e| { + error!(target: LOG_TARGET, "Could not fetch the tip header of the db. {:?}", e); + Err(e) + })? + .ok_or(ChainStorageError::InvalidQuery( + "Cannot retrieve header. Blockchain DB is empty".into(), + )) + } + /// Returns the UTXO with the given hash. pub fn fetch_utxo(&self, hash: HashOutput) -> Result { fetch!(self, hash, UnspentOutput) @@ -279,45 +407,103 @@ where T: BlockchainBackend self.db.fetch_mmr_root(tree) } + /// Returns only the MMR merkle root without the state of the roaring bitmap. + pub fn fetch_mmr_only_root(&self, tree: MmrTree) -> Result { + self.db.fetch_mmr_only_root(tree) + } + + /// Apply the current change set to a pruned copy of the merkle mountain range and calculate the resulting Merklish + /// root of the specified merkle mountain range. Deletions of hashes from the MMR can only be applied for UTXOs. + pub fn calculate_mmr_root( + &self, + tree: MmrTree, + additions: Vec, + deletions: Vec, + ) -> Result + { + self.db.calculate_mmr_root(tree, additions, deletions) + } + + /// `calculate_mmr_roots` takes a block template and calculates the MMR roots for a hypothetical new block that + /// would be built onto the chain tip. Note that _no checks_ are made to determine whether the template would + /// actually be a valid extension to the chain; only the new MMR roots are calculated + pub fn calculate_mmr_roots(&self, template: NewBlockTemplate) -> Result { + let NewBlockTemplate { header, mut body } = template; + // Make sure the body components are sorted. If they already are, this is a very cheap call. + body.sort(); + let kernel_hashes: Vec = body.kernels().iter().map(|k| k.hash()).collect(); + let out_hashes: Vec = body.outputs().iter().map(|out| out.hash()).collect(); + let rp_hashes: Vec = body.outputs().iter().map(|out| out.proof().hash()).collect(); + let inp_hashes: Vec = body.inputs().iter().map(|inp| inp.hash()).collect(); + + let mut header = BlockHeader::from(header); + header.kernel_mr = self.calculate_mmr_root(MmrTree::Kernel, kernel_hashes, vec![])?; + header.output_mr = self.calculate_mmr_root(MmrTree::Utxo, out_hashes, inp_hashes)?; + header.range_proof_mr = self.calculate_mmr_root(MmrTree::RangeProof, rp_hashes, vec![])?; + Ok(Block { header, body }) + } + /// Fetch a Merklish proof for the given hash, tree and position in the MMR - pub fn fetch_mmr_proof(&self, tree: MmrTree, pos: u64) -> Result { + pub fn fetch_mmr_proof(&self, tree: MmrTree, pos: usize) -> Result { self.db.fetch_mmr_proof(tree, pos) } - /// Add a block to the longest chain. This function does some basic checks to maintain the chain integrity, but - /// does not perform a full block validation (this should have been done by this point). + /// Tries to add a block to the longest chain. /// - /// On completion, this function will have - /// * Checked that the previous block builds on the longest chain. - /// * If not - add orphan block and possibly re-org - /// * That the total accumulated work has increased. - /// * Mark all inputs in the block as spent. - /// * Updated the database metadata + /// The block is added to the longest chain if and only if + /// * Block block is not already in the database, AND + /// * The block is next in the chain, AND + /// * The Validator passes + /// * There are no problems with the database backend (e.g. disk full) /// - /// An error is returned if: - /// * the block has already been added - /// * any of the inputs were not in the UTXO set or were marked as spent already + /// If the block is _not_ next in the chain, the block will be added to the orphan pool if the orphan validator + /// passes, and then the database is checked for whether there has been a chain re-organisation. + /// + /// # Returns + /// + /// An error is returned if + /// * there was a problem accessing the database, + /// * the validation fails + /// + /// Otherwise the function returns successfully. + /// A successful return value can be one of + /// * `BlockExists`: the block has already been added; No action was taken. + /// * `Ok`: The block was added and all validation checks passed + /// * `OrphanBlock`: The block did not form part of the main chain and was added as an orphan. + /// * `ChainReorg`: The block was added, which resulted in a chain-reorg. /// /// If an error does occur while writing the new block parts, all changes are reverted before returning. - pub fn add_block(&mut self, block: Block) -> Result { - if !self.is_new_best_block(&block)? { - return self.handle_possible_reorg(block); - } + pub fn add_block(&self, block: Block) -> Result { let block_hash = block.hash(); - let block_height = block.header.height; - if self.db.contains(&DbKey::BlockHash(block_hash.clone()))? { + if self.db.contains(&DbKey::BlockHash(block_hash.clone()))? || + self.db.contains(&DbKey::OrphanBlock(block_hash.clone()))? + { return Ok(BlockAddResult::BlockExists); } - let mut txn = DbTransaction::new(); + + self.handle_possible_reorg(block) + } + + fn validate_and_store_new_block(&self, block: Block) -> Result<(), ChainStorageError> { + self.validators + .as_ref() + .expect("No validators added") + .block + .validate(&block) + .map_err(ChainStorageError::ValidationError)?; + self.store_new_block(block) + } + + fn store_new_block(&self, block: Block) -> Result<(), ChainStorageError> { let (header, inputs, outputs, kernels) = block.dissolve(); + // Build all the DB queries needed to add the block and the add it atomically + let mut txn = DbTransaction::new(); txn.insert_header(header); txn.spend_inputs(&inputs); - outputs.into_iter().for_each(|utxo| txn.insert_utxo(utxo)); - kernels.into_iter().for_each(|k| txn.insert_kernel(k)); + outputs.iter().for_each(|utxo| txn.insert_utxo(utxo.clone(), true)); + kernels.iter().for_each(|k| txn.insert_kernel(k.clone(), true)); txn.commit_block(); - self.commit(txn)?; - self.update_metadata(block_height, block_hash)?; - Ok(BlockAddResult::Ok) + self.commit(txn) } /// Returns true if the given block -- assuming everything else is valid -- would be added to the tip of the @@ -326,23 +512,20 @@ where T: BlockchainBackend /// * or ALL of: /// * the block's parent hash is the hash of the block at the current chain tip, /// * the block height is one greater than the parent block - /// * the total accumulated work has increased - pub fn is_new_best_block(&self, block: &Block) -> Result { + pub fn is_at_chain_tip(&self, block: &Block) -> Result { let (height, parent_hash) = { let db = self.access_metadata()?; + // If the database is empty, the best block must be the genesis block if db.height_of_longest_chain.is_none() { - return Ok(true); + return Ok(block.header.height == 0); } ( db.height_of_longest_chain.clone().unwrap(), db.best_block.clone().unwrap(), ) }; - let best_block = self.fetch_header(height - 1)?; - let result = block.header.prev_hash == parent_hash && - block.header.height == best_block.height + 1 && - block.header.total_difficulty > best_block.total_difficulty; - Ok(result) + let best_block = self.fetch_header(height)?; + Ok(block.header.prev_hash == parent_hash && block.header.height == best_block.height + 1) } /// Fetch a block from the blockchain database. @@ -360,14 +543,14 @@ where T: BlockchainBackend pub fn fetch_block(&self, height: u64) -> Result { let metadata = self.check_for_valid_height(height)?; let header = self.fetch_header(height)?; - let kernel_cp = self.fetch_mmr_checkpoint(MmrTree::Kernel, height)?; + let kernel_cp = self.fetch_checkpoint(MmrTree::Kernel, height)?; let (kernel_hashes, _) = kernel_cp.into_parts(); let kernels = self.fetch_kernels(kernel_hashes)?; - let utxo_cp = self.db.fetch_mmr_checkpoint(MmrTree::Utxo, height)?; + let utxo_cp = self.fetch_checkpoint(MmrTree::Utxo, height)?; let (utxo_hashes, deleted_nodes) = utxo_cp.into_parts(); let inputs = self.fetch_inputs(deleted_nodes)?; let (outputs, spent) = self.fetch_outputs(utxo_hashes)?; - let block = BlockBuilder::new() + let block = BlockBuilder::new(&self.consensus_manager.consensus_constants()) .with_header(header) .add_inputs(inputs) .add_outputs(outputs) @@ -380,14 +563,24 @@ where T: BlockchainBackend )) } + /// Attempt to fetch the block corresponding to the provided hash from the main chain, if it cannot be found then + /// the block will be searched in the orphan block pool. + pub fn fetch_block_with_hash(&self, hash: HashOutput) -> Result, ChainStorageError> { + if let Ok(header) = self.fetch_header_with_block_hash(hash.clone()) { + return Ok(Some(self.fetch_block(header.height)?)); + } + if let Ok(block) = self.fetch_orphan(hash) { + return Ok(Some(HistoricalBlock::new(block, 0, vec![]))); + } + Ok(None) + } + fn check_for_valid_height(&self, height: u64) -> Result { let metadata = self.get_metadata()?; - if metadata.height_of_longest_chain.is_none() { - return Err(ChainStorageError::InvalidQuery( - "Cannot retrieve block. Blockchain DB is empty".into(), - )); - } - if height > metadata.height_of_longest_chain.unwrap() { + let db_height = metadata + .height_of_longest_chain + .ok_or_else(|| ChainStorageError::InvalidQuery("Cannot retrieve block. Blockchain DB is empty".into()))?; + if height > db_height { return Err(ChainStorageError::InvalidQuery(format!( "Cannot get block at height {}. Chain tip is at {}", height, @@ -395,7 +588,7 @@ where T: BlockchainBackend ))); } // We can't actually provide full block beyond the pruning horizon - if height < metadata.horizon_block().unwrap() { + if height < metadata.horizon_block(db_height) { return Err(ChainStorageError::BeyondPruningHorizon); } Ok(metadata) @@ -406,7 +599,7 @@ where T: BlockchainBackend } fn fetch_inputs(&self, deleted_nodes: Bitmap) -> Result, ChainStorageError> { - // The inputs must all the in the current STXO set + // The inputs must all be in the current STXO set let inputs: Result, ChainStorageError> = deleted_nodes .iter() .map(|pos| { @@ -443,33 +636,322 @@ where T: BlockchainBackend Ok((outputs, spent)) } - fn fetch_mmr_checkpoint(&self, tree: MmrTree, height: u64) -> Result { - let metadata = self.get_metadata()?; - let horizon_block = match metadata.horizon_block() { - None => return Err(ChainStorageError::InvalidQuery("Blockchain database is empty".into())), - Some(i) => i, - }; - let index = height - .checked_sub(horizon_block) - .ok_or(ChainStorageError::BeyondPruningHorizon)? as u64; - self.db.fetch_mmr_checkpoint(tree, index) + fn fetch_checkpoint(&self, tree: MmrTree, height: u64) -> Result { + let _ = self.check_for_valid_height(height)?; + self.db.fetch_checkpoint(tree, height) } /// Atomically commit the provided transaction to the database backend. This function does not update the metadata. - pub(crate) fn commit(&mut self, txn: DbTransaction) -> Result<(), ChainStorageError> { + pub fn commit(&self, txn: DbTransaction) -> Result<(), ChainStorageError> { self.db.write(txn) } + /// Rewind the blockchain state to the block height given and return the blocks that were removed and orphaned. + /// + /// The operation will fail if + /// * The block height is in the future + /// * The block height is before pruning horizon + pub fn rewind_to_height(&self, height: u64) -> Result, ChainStorageError> { + self.check_for_valid_height(height)?; + let chain_height = self + .get_height()? + .ok_or_else(|| ChainStorageError::InvalidQuery("Blockchain database is empty".into()))?; + let mut removed_blocks = Vec::::new(); + if height == chain_height { + return Ok(removed_blocks); // Rewind unnecessary, already on correct height + } + + let steps_back = (chain_height - height) as usize; + let mut txn = DbTransaction::new(); + for rewind_height in (height + 1)..=chain_height { + // Reconstruct block at height and add to orphan block pool + let orphaned_block = self.fetch_block(rewind_height)?.block().clone(); + removed_blocks.push(orphaned_block.clone()); + txn.insert_orphan(orphaned_block); + + // Remove Header and block hash + txn.delete(DbKey::BlockHeader(rewind_height)); // Will also delete the blockhash + + // Remove Kernels + self.fetch_checkpoint(MmrTree::Kernel, rewind_height)? + .nodes_added() + .iter() + .for_each(|hash_output| { + txn.delete(DbKey::TransactionKernel(hash_output.clone())); + }); + + // Remove UTXOs and move STXOs back to UTXO set + let (nodes_added, nodes_deleted) = self.fetch_checkpoint(MmrTree::Utxo, rewind_height)?.into_parts(); + nodes_added.iter().for_each(|hash_output| { + txn.delete(DbKey::UnspentOutput(hash_output.clone())); + }); + for pos in nodes_deleted.iter() { + self.db + .fetch_mmr_node(MmrTree::Utxo, pos) + .and_then(|(stxo_hash, deleted)| { + assert!(deleted); + txn.unspend_stxo(stxo_hash); + Ok(()) + })?; + } + } + // Rewind MMRs + txn.rewind_kernel_mmr(steps_back); + txn.rewind_utxo_mmr(steps_back); + txn.rewind_rp_mmr(steps_back); + self.commit(txn)?; + + let last_block = self.fetch_block(height)?.block().clone(); + self.update_metadata(height, last_block.hash())?; + + Ok(removed_blocks) + } + /// Checks whether we should add the block as an orphan. If it is the case, the orphan block is added and the chain - /// is reorganised if necessary - fn handle_possible_reorg(&mut self, _block: Block) -> Result { - // TODO - check if block height > pruning horizon - // TODO - check if proof of work is valid and above some spam minimum?? - unimplemented!() + /// is reorganised if necessary. + fn handle_possible_reorg(&self, block: Block) -> Result { + let metadata = self.get_metadata()?; + let db_height = metadata + .height_of_longest_chain + .ok_or_else(|| ChainStorageError::InvalidQuery("Cannot retrieve block. Blockchain DB is empty".into())) + .or_else(|e| { + error!( + target: LOG_TARGET, + "Could not retrieve block, block chain is empty {:?}", e + ); + Err(e) + })?; + // Validate the orphan + self.validators + .as_ref() + .expect("No validators added") + .orphan + .validate(&block) + .map_err(ChainStorageError::ValidationError)?; + self.insert_orphan(block.clone())?; + info!( + target: LOG_TARGET, + "Added new orphan block to the database. Current best height is {}. Orphan block height is {}", + db_height, + block.header.height + ); + trace!(target: LOG_TARGET, "{}", block); + // Trigger a reorg check for all blocks in the orphan block pool + debug!(target: LOG_TARGET, "Checking for chain re-org."); + self.handle_reorg(block) } - fn handle_reorg(&mut self, _orphan_hash: Hash) -> Result<(), ChainStorageError> { - unimplemented!() + /// The handle_reorg function is triggered by the adding of orphaned blocks. Reorg chains are constructed by + /// finding the orphan chain tip with the highest accumulated difficulty that can be linked to the newly added + /// orphan block and then building a chain from the strongest orphan tip back to the main chain. The newly added + /// orphan block is considered to be a orphan tip if no better tips can be found that link to it. When a valid + /// reorg chain is constructed with a higher accumulated difficulty, then the main chain is rewound and updated + /// with the newly un-orphaned blocks from the reorg chain. + fn handle_reorg(&self, new_block: Block) -> Result { + // We can assume that the new block is part of the re-org chain if it exists, otherwise the re-org would have + // happened on the previous call to this function. + // Try and construct a path from `new_block` to the main chain: + let reorg_chain = self.try_construct_fork(new_block.clone())?; + if reorg_chain.is_empty() { + return Ok(BlockAddResult::OrphanBlock); + } + // Try and find all orphaned chain tips that can be linked to the new orphan block, if no better orphan chain + // tips can be found then the new_block is a tip. + let new_block_hash = new_block.hash(); + let orphan_chain_tips = self.find_orphan_chain_tips(new_block.header.height, new_block_hash.clone()); + // Check the accumulated difficulty of the best fork chain compared to the main chain. + let (fork_accum_difficulty, fork_tip_hash) = self.find_strongest_orphan_tip(orphan_chain_tips)?; + let tip_header = self + .db + .fetch_last_header()? + .ok_or_else(|| ChainStorageError::InvalidQuery("Cannot retrieve header. Blockchain DB is empty".into()))?; + trace!( + target: LOG_TARGET, + "Comparing fork diff: ({}) with hash ({}) to main chain diff: ({}) with hash ({}) for possible reorg", + fork_accum_difficulty, + fork_tip_hash.to_hex(), + tip_header.total_accumulated_difficulty_inclusive(), + tip_header.hash().to_hex() + ); + if fork_accum_difficulty >= tip_header.total_accumulated_difficulty_inclusive() { + // TODO: this should be > and not >=, this breaks some of the tests that assume that they can be the same. + // We've built the strongest orphan chain we can by going backwards and forwards from the new orphan block + // that is linked with the main chain. + let fork_tip_block = self.fetch_orphan(fork_tip_hash.clone())?; + let fork_tip_header = fork_tip_block.header.clone(); + let reorg_chain = self.try_construct_fork(fork_tip_block)?; + let added_blocks: Vec = reorg_chain.iter().map(Clone::clone).collect(); + + let fork_height = reorg_chain + .front() + .expect("The new orphan block should be in the queue") + .header + .height - + 1; + let removed_blocks = self.reorganize_chain(fork_height, reorg_chain)?; + self.update_metadata(fork_tip_header.height, fork_tip_hash.clone())?; + if removed_blocks.len() == 0 { + return Ok(BlockAddResult::Ok); + } else { + warn!( + target: LOG_TARGET, + "Chain reorg happened from difficulty: ({}) to difficulty: ({})", + tip_header.pow, + fork_tip_header.pow + ); + debug!( + target: LOG_TARGET, + "Reorg from ({}) to ({})", tip_header, fork_tip_header + ); + return Ok(BlockAddResult::ChainReorg(( + Box::new(removed_blocks), + Box::new(added_blocks), + ))); + } + } + debug!(target: LOG_TARGET, "Orphan block received: {}", new_block); + Ok(BlockAddResult::OrphanBlock) + } + + /// We try and build a chain from this block to the main chain. If we can't do that we can stop. + /// We start with the current, newly received block, and look for a blockchain sequence (via `prev_hash`). + /// Each successful link is pushed to the front of the queue. An empty queue is returned if the fork chain did not + /// link to the main chain. + fn try_construct_fork(&self, new_block: Block) -> Result, ChainStorageError> { + let mut fork_chain = VecDeque::new(); + let mut hash = new_block.header.prev_hash.clone(); + let mut height = new_block.header.height; + fork_chain.push_front(new_block); + while let Ok(b) = self.fetch_orphan(hash.clone()) { + if b.header.height + 1 != height { + // Well now. The block heights don't form a sequence, which means that we should not only stop now, + // but remove one or both of these orphans from the pool because the blockchain is broken at this point. + info!( + target: LOG_TARGET, + "A broken blockchain sequence was detected in the database. Cleaning up and removing block with \ + hash {}", + hash.to_hex() + ); + self.remove_orphan(hash)?; + return Err(ChainStorageError::InvalidBlock); + } + hash = b.header.prev_hash.clone(); + height -= 1; + fork_chain.push_front(b); + } + // Check if the constructed fork chain is connected to the main chain. + let fork_start_header = fork_chain + .front() + .expect("The new orphan block should be in the queue") + .header + .clone(); + if let Ok(header) = self.fetch_header_with_block_hash(fork_start_header.prev_hash) { + if header.height + 1 == fork_start_header.height { + return Ok(fork_chain); + } + } + Ok(VecDeque::new()) + } + + /// Try to find all orphan chain tips that originate from the current orphan parent block. + fn find_orphan_chain_tips(&self, parent_height: u64, parent_hash: BlockHash) -> Vec { + let mut tip_hashes = Vec::::new(); + self.db + .for_each_orphan(|pair| { + let (_, block) = pair.unwrap(); + if (block.header.prev_hash == parent_hash) && (block.header.height == parent_height + 1) { + let next_parent_hash = block.hash(); + let mut orphan_chain_tips = + self.find_orphan_chain_tips(block.header.height, next_parent_hash.clone()); + if !orphan_chain_tips.is_empty() { + tip_hashes.append(&mut orphan_chain_tips); + } else { + tip_hashes.push(next_parent_hash); + } + } + }) + .expect("Unexpected result for database query"); + if tip_hashes.is_empty() { + // No chain tips found, then parent must be the tip. + tip_hashes.push(parent_hash); + } + tip_hashes + } + + /// Find and return the orphan chain tip with the highest accumulated difficulty. + fn find_strongest_orphan_tip( + &self, + orphan_chain_tips: Vec, + ) -> Result<(Difficulty, BlockHash), ChainStorageError> + { + let mut best_accum_difficulty = Difficulty::min(); + let mut best_tip_hash: Vec = vec![0; 32]; + for tip_hash in orphan_chain_tips { + let header = self.fetch_orphan(tip_hash.clone())?.header; + let accum_difficulty = header.total_accumulated_difficulty_inclusive(); + if accum_difficulty >= best_accum_difficulty { + best_tip_hash = tip_hash; + best_accum_difficulty = accum_difficulty; + } + } + Ok((best_accum_difficulty, best_tip_hash)) + } + + /// Reorganize the main chain with the provided fork chain, starting at the specified height. + fn reorganize_chain(&self, height: u64, chain: VecDeque) -> Result, ChainStorageError> { + let removed_blocks = self.rewind_to_height(height)?; + let mut txn = DbTransaction::new(); + for block in chain.into_iter() { + let orphan_hash = block.hash(); + txn.delete(DbKey::OrphanBlock(orphan_hash)); + self.validate_and_store_new_block(block)?; + } + self.commit(txn)?; + Ok(removed_blocks) + } + + /// Insert the provided block into the orphan pool. + fn insert_orphan(&self, block: Block) -> Result<(), ChainStorageError> { + let mut txn = DbTransaction::new(); + txn.insert_orphan(block); + self.commit(txn) + } + + /// Discard the the orphan block from the orphan pool that corresponds to the provided block hash. + fn remove_orphan(&self, hash: HashOutput) -> Result<(), ChainStorageError> { + let mut txn = DbTransaction::new(); + txn.delete(DbKey::OrphanBlock(hash)); + self.commit(txn) + } + + /// Calculate the total kernel excess for all kernels in the chain. + pub fn total_kernel_excess(&self) -> Result { + let mut excess = CommitmentFactory::default().zero(); + self.db.for_each_kernel(|pair| { + let (_, kernel) = pair.unwrap(); + excess = &excess + &kernel.excess; + })?; + Ok(excess) + } + + /// Calculate the total kernel offset for all the kernel offsets recorded in the headers of the chain. + pub fn total_kernel_offset(&self) -> Result { + let mut offset = BlindingFactor::default(); + self.db.for_each_header(|pair| { + let (_, header) = pair.unwrap(); + offset = &offset + &header.total_kernel_offset; + })?; + Ok(offset) + } + + /// Calculate the total sum of all the UTXO commitments in the chain. + pub fn total_utxo_commitment(&self) -> Result { + let mut total_commitment = CommitmentFactory::default().zero(); + self.db.for_each_utxo(|pair| { + let (_, utxo) = pair.unwrap(); + total_commitment = &total_commitment + &utxo.commitment; + })?; + Ok(total_commitment) } } @@ -496,6 +978,8 @@ where T: BlockchainBackend BlockchainDatabase { metadata: self.metadata.clone(), db: self.db.clone(), + validators: self.validators.clone(), + consensus_manager: self.consensus_manager.clone(), } } } diff --git a/base_layer/core/src/chain_storage/db_transaction.rs b/base_layer/core/src/chain_storage/db_transaction.rs index 6c5bacf713..813a0cd999 100644 --- a/base_layer/core/src/chain_storage/db_transaction.rs +++ b/base_layer/core/src/chain_storage/db_transaction.rs @@ -19,15 +19,17 @@ // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// use crate::{ blocks::{blockheader::BlockHash, Block, BlockHeader}, - transaction::{TransactionInput, TransactionKernel, TransactionOutput}, - types::HashOutput, + transactions::{ + transaction::{TransactionInput, TransactionKernel, TransactionOutput}, + types::HashOutput, + }, }; +use serde::{Deserialize, Serialize}; use std::fmt::{Display, Error, Formatter}; -use tari_utilities::{hex::to_hex, Hashable}; +use tari_crypto::tari_utilities::{hex::to_hex, Hashable}; #[derive(Debug)] pub struct DbTransaction { @@ -60,9 +62,9 @@ impl DbTransaction { } /// Inserts a transaction kernel into the current transaction. - pub fn insert_kernel(&mut self, kernel: TransactionKernel) { + pub fn insert_kernel(&mut self, kernel: TransactionKernel, update_mmr: bool) { let hash = kernel.hash(); - self.insert(DbKeyValuePair::TransactionKernel(hash, Box::new(kernel))); + self.insert(DbKeyValuePair::TransactionKernel(hash, Box::new(kernel), update_mmr)); } /// Inserts a block header into the current transaction. @@ -71,10 +73,16 @@ impl DbTransaction { self.insert(DbKeyValuePair::BlockHeader(height, Box::new(header))); } - /// Adds a UTXO into the current transaction. - pub fn insert_utxo(&mut self, utxo: TransactionOutput) { + /// Adds a UTXO into the current transaction and update the TXO MMR. + pub fn insert_utxo(&mut self, utxo: TransactionOutput, update_mmr: bool) { let hash = utxo.hash(); - self.insert(DbKeyValuePair::UnspentOutput(hash, Box::new(utxo))); + self.insert(DbKeyValuePair::UnspentOutput(hash, Box::new(utxo), update_mmr)); + } + + /// Adds a UTXO into the current transaction and update the TXO MMR. This is a test only function used to ensure we + /// block duplicate entries. This function does not calculate the hash function but accepts one as a variable. + pub fn insert_utxo_with_hash(&mut self, hash: Vec, utxo: TransactionOutput, update_mmr: bool) { + self.insert(DbKeyValuePair::UnspentOutput(hash, Box::new(utxo), update_mmr)); } /// Stores an orphan block. No checks are made as to whether this is actually an orphan. That responsibility lies @@ -84,18 +92,27 @@ impl DbTransaction { self.insert(DbKeyValuePair::OrphanBlock(hash, Box::new(orphan))); } - /// Moves a UTXO. If the UTXO is not in the UTXO set, the transaction will fail with an `UnspendableOutput` error. - pub fn move_utxo(&mut self, utxo_hash: HashOutput) { + /// Moves a UTXO to the STXO set and mark it as spent on the MRR. If the UTXO is not in the UTXO set, the + /// transaction will fail with an `UnspendableOutput` error. + pub fn spend_utxo(&mut self, utxo_hash: HashOutput) { self.operations .push(WriteOperation::Spend(DbKey::UnspentOutput(utxo_hash))); } + /// Moves a STXO to the UTXO set. If the STXO is not in the STXO set, the transaction will fail with an + /// `UnspendError`. + // TODO: unspend_utxo in memory_db doesn't unmark the node in the roaring bitmap.0 + pub fn unspend_stxo(&mut self, stxo_hash: HashOutput) { + self.operations + .push(WriteOperation::UnSpend(DbKey::SpentOutput(stxo_hash))); + } + /// Moves the given set of transaction inputs from the UTXO set to the STXO set. All the inputs *must* currently /// exist in the UTXO set, or the transaction will error with `ChainStorageError::UnspendableOutput` pub fn spend_inputs(&mut self, inputs: &[TransactionInput]) { for input in inputs { let input_hash = input.hash(); - self.move_utxo(input_hash); + self.spend_utxo(input_hash); } } @@ -103,7 +120,10 @@ impl DbTransaction { /// the database. pub fn commit_block(&mut self) { self.operations - .push(WriteOperation::Insert(DbKeyValuePair::CommitBlock)); + .push(WriteOperation::CreateMmrCheckpoint(MmrTree::Kernel)); + self.operations.push(WriteOperation::CreateMmrCheckpoint(MmrTree::Utxo)); + self.operations + .push(WriteOperation::CreateMmrCheckpoint(MmrTree::RangeProof)); } /// Set the horizon beyond which we cannot be guaranteed provide detailed blockchain information anymore. @@ -118,13 +138,22 @@ impl DbTransaction { ))); } - /// Rewind the blockchain state to the block height given. - /// - /// The operation will fail if - /// * The block height is in the future - /// * The block height is before pruning horizon - pub fn rewind_to_height(&mut self, _height: u64) { - unimplemented!() + /// Rewinds the Kernel MMR state by the given number of Checkpoints. + pub fn rewind_kernel_mmr(&mut self, steps_back: usize) { + self.operations + .push(WriteOperation::RewindMmr(MmrTree::Kernel, steps_back)); + } + + /// Rewinds the UTXO MMR state by the given number of Checkpoints. + pub fn rewind_utxo_mmr(&mut self, steps_back: usize) { + self.operations + .push(WriteOperation::RewindMmr(MmrTree::Utxo, steps_back)); + } + + /// Rewinds the RangeProof MMR state by the given number of Checkpoints. + pub fn rewind_rp_mmr(&mut self, steps_back: usize) { + self.operations + .push(WriteOperation::RewindMmr(MmrTree::RangeProof, steps_back)); } } @@ -135,28 +164,27 @@ pub enum WriteOperation { Spend(DbKey), UnSpend(DbKey), CreateMmrCheckpoint(MmrTree), + RewindMmr(MmrTree, usize), } +/// A list of key-value pairs that are required for each insert operation #[derive(Debug)] pub enum DbKeyValuePair { Metadata(MetadataKey, MetadataValue), BlockHeader(u64, Box), - UnspentOutput(HashOutput, Box), - SpentOutput(HashOutput, Box), - TransactionKernel(HashOutput, Box), + UnspentOutput(HashOutput, Box, bool), + TransactionKernel(HashOutput, Box, bool), OrphanBlock(HashOutput, Box), - CommitBlock, } -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum MmrTree { Utxo, Kernel, RangeProof, - Header, } -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub enum MetadataKey { ChainHeight, BestBlock, @@ -164,7 +192,7 @@ pub enum MetadataKey { PruningHorizon, } -#[derive(Debug)] +#[derive(Debug, Clone, Deserialize, Serialize)] pub enum MetadataValue { ChainHeight(Option), BestBlock(Option), @@ -172,7 +200,7 @@ pub enum MetadataValue { PruningHorizon(u64), } -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub enum DbKey { Metadata(MetadataKey), BlockHeader(u64), @@ -234,7 +262,6 @@ impl Display for MmrTree { MmrTree::RangeProof => f.write_str("Range Proof"), MmrTree::Utxo => f.write_str("UTXO"), MmrTree::Kernel => f.write_str("Kernel"), - MmrTree::Header => f.write_str("Block header"), } } } diff --git a/base_layer/core/src/chain_storage/error.rs b/base_layer/core/src/chain_storage/error.rs index 087cb88dd2..d44b6fae01 100644 --- a/base_layer/core/src/chain_storage/error.rs +++ b/base_layer/core/src/chain_storage/error.rs @@ -20,10 +20,14 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::chain_storage::db_transaction::DbKey; +use crate::{ + chain_storage::{db_transaction::DbKey, MmrTree}, + validation::ValidationError, +}; use derive_error::Error; +use tari_mmr::{error::MerkleMountainRangeError, MerkleProofError}; -#[derive(Debug, Error, PartialEq)] +#[derive(Debug, Clone, Error, PartialEq)] pub enum ChainStorageError { // Access to the underlying storage mechanism failed #[error(non_std, no_from)] @@ -54,4 +58,16 @@ pub enum ChainStorageError { // The requested value was not found in the database #[error(non_std, no_from)] ValueNotFound(DbKey), + MerkleMountainRangeError(MerkleMountainRangeError), + MerkleProofError(MerkleProofError), + ValidationError(ValidationError), + // An MMR root in the provided block header did not match the MMR root in the database + #[error(non_std, no_from)] + MismatchedMmrRoot(MmrTree), + // An invalid block was submitted to the database + InvalidBlock, + #[error(msg_embedded, non_std, no_from)] + BlockingTaskSpawnError(String), + // A request was out of range + OutOfRange, } diff --git a/base_layer/core/src/chain_storage/historical_block.rs b/base_layer/core/src/chain_storage/historical_block.rs index d06b4f3824..606e4e9329 100644 --- a/base_layer/core/src/chain_storage/historical_block.rs +++ b/base_layer/core/src/chain_storage/historical_block.rs @@ -20,18 +20,23 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{blocks::Block, transaction::TransactionOutput, types::Commitment}; +use crate::{ + blocks::Block, + transactions::{transaction::TransactionOutput, types::Commitment}, +}; +use serde::{Deserialize, Serialize}; /// The representation of a historical block in the blockchain. It is essentially identical to a protocol-defined /// block but contains some extra metadata that clients such as Block Explorers will find interesting. +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub struct HistoricalBlock { /// The number of blocks that have been mined since this block, including this one. The current tip will have one /// confirmation. - confirmations: u64, + pub confirmations: u64, /// An array of commitments of the outputs from this block that have subsequently been spent. - spent_commitments: Vec, + pub spent_commitments: Vec, /// The underlying block - block: Block, + pub block: Block, } impl HistoricalBlock { @@ -51,6 +56,11 @@ impl HistoricalBlock { pub fn is_spent(&self, output: &TransactionOutput) -> bool { self.spent_commitments.contains(&output.commitment) } + + /// Returns a reference to the block of the HistoricalBlock + pub fn block(&self) -> &Block { + &self.block + } } impl From for Block { diff --git a/base_layer/core/src/chain_storage/lmdb_db/lmdb.rs b/base_layer/core/src/chain_storage/lmdb_db/lmdb.rs new file mode 100644 index 0000000000..3bfc94ff6e --- /dev/null +++ b/base_layer/core/src/chain_storage/lmdb_db/lmdb.rs @@ -0,0 +1,178 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::chain_storage::error::ChainStorageError; +use lmdb_zero::{ + error::{self, LmdbResultExt}, + put, + ConstAccessor, + Cursor, + CursorIter, + Database, + Environment, + Ignore, + MaybeOwned, + ReadTransaction, + WriteTransaction, +}; +use log::*; +use serde::{de::DeserializeOwned, Serialize}; +pub const LOG_TARGET: &str = "c::cs::lmdb_db::lmdb"; + +// TODO: Calling `access` for every lmdb operation has some overhead (an atomic read and set). Check if is possible to +// pass an Accessor instead of the WriteTransaction? + +pub fn serialize(data: &T) -> Result, ChainStorageError> +where T: Serialize { + let mut buf = Vec::with_capacity(512); + bincode::serialize_into(&mut buf, data) + .or_else(|e| { + error!(target: LOG_TARGET, "Could not serialize lmdb: {:?}", e); + return Err(e); + }) + .map_err(|e| ChainStorageError::AccessError(e.to_string()))?; + Ok(buf) +} + +pub fn deserialize(buf_bytes: &[u8]) -> Result +where T: DeserializeOwned { + bincode::deserialize(buf_bytes) + .or_else(|e| { + error!(target: LOG_TARGET, "Could not deserialize lmdb: {:?}", e); + return Err(e); + }) + .map_err(|e| error::Error::ValRejected(e.to_string())) +} + +pub fn lmdb_insert(txn: &WriteTransaction, db: &Database, key: &K, val: &V) -> Result<(), ChainStorageError> +where + K: Serialize, + V: Serialize, +{ + let key_buf = serialize(key)?; + let val_buf = serialize(val)?; + txn.access() + .put(&db, &key_buf, &val_buf, put::NOOVERWRITE) + .map_err(|e| ChainStorageError::AccessError(e.to_string())) +} + +pub fn lmdb_replace(txn: &WriteTransaction, db: &Database, key: &K, val: &V) -> Result<(), ChainStorageError> +where + K: Serialize, + V: Serialize, +{ + let key_buf = serialize(key)?; + let val_buf = serialize(val)?; + txn.access() + .put(&db, &key_buf, &val_buf, put::Flags::empty()) + .map_err(|e| ChainStorageError::AccessError(e.to_string())) +} + +pub fn lmdb_delete(txn: &WriteTransaction, db: &Database, key: &K) -> Result<(), ChainStorageError> +where K: Serialize { + let key_buf = serialize(key)?; + txn.access() + .del_key(&db, &key_buf) + .map_err(|e| ChainStorageError::AccessError(e.to_string())) +} + +pub fn lmdb_get(env: &Environment, db: &Database, key: &K) -> Result, ChainStorageError> +where + K: Serialize, + V: DeserializeOwned, +{ + let txn = ReadTransaction::new(env).map_err(|e| ChainStorageError::AccessError(e.to_string()))?; + let access = txn.access(); + let key_buf = serialize(key)?; + match access.get(&db, &key_buf).to_opt() { + Ok(None) => Ok(None), + Err(e) => Err(ChainStorageError::AccessError(e.to_string())), + Ok(Some(v)) => match deserialize(v) { + Ok(val) => Ok(Some(val)), + Err(e) => Err(ChainStorageError::AccessError(e.to_string())), + }, + } +} + +pub fn lmdb_exists(env: &Environment, db: &Database, key: &K) -> Result +where K: Serialize { + let txn = ReadTransaction::new(env).map_err(|e| ChainStorageError::AccessError(e.to_string()))?; + let access = txn.access(); + let key_buf = serialize(key)?; + let res: error::Result<&Ignore> = access.get(&db, &key_buf); + let res = res + .to_opt() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .is_some(); + Ok(res) +} + +pub fn lmdb_len(env: &Environment, db: &Database) -> Result { + let txn = ReadTransaction::new(env).map_err(|e| ChainStorageError::AccessError(e.to_string()))?; + let stats = txn + .db_stat(&db) + .map_err(|e| ChainStorageError::AccessError(e.to_string()))?; + Ok(stats.entries) +} + +pub fn lmdb_iter_next(c: &mut Cursor, access: &ConstAccessor) -> Result<(K, V), error::Error> +where + K: DeserializeOwned, + V: DeserializeOwned, +{ + let (key_bytes, val_bytes) = c.next(access)?; + let key = deserialize::(key_bytes)?; + let val = deserialize::(val_bytes)?; + Ok((key, val)) +} + +pub fn lmdb_for_each(env: &Environment, db: &Database, mut f: F) -> Result<(), ChainStorageError> +where + F: FnMut(Result<(K, V), ChainStorageError>), + K: DeserializeOwned, + V: DeserializeOwned, +{ + let txn = ReadTransaction::new(env).map_err(|e| ChainStorageError::AccessError(e.to_string()))?; + let access = txn.access(); + let cursor = txn + .cursor(db) + .map_err(|e| ChainStorageError::AccessError(e.to_string()))?; + let head = |c: &mut Cursor, a: &ConstAccessor| { + let (key_bytes, val_bytes) = c.first(a)?; + let key = deserialize::(key_bytes)?; + let val = deserialize::(val_bytes)?; + Ok((key, val)) + }; + let cursor = MaybeOwned::Owned(cursor); + let iter = CursorIter::new(cursor, &access, head, lmdb_iter_next) + .map_err(|e| ChainStorageError::AccessError(e.to_string()))?; + for p in iter { + f(p.map_err(|e| ChainStorageError::AccessError(e.to_string()))); + } + Ok(()) +} + +pub fn lmdb_clear_db(txn: &WriteTransaction, db: &Database) -> Result<(), ChainStorageError> { + txn.access() + .clear_db(&db) + .map_err(|e| ChainStorageError::AccessError(e.to_string())) +} diff --git a/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs b/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs new file mode 100644 index 0000000000..8ca4150137 --- /dev/null +++ b/base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs @@ -0,0 +1,883 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + blocks::{blockheader::BlockHeader, Block}, + chain_storage::{ + blockchain_database::BlockchainBackend, + db_transaction::{DbKey, DbKeyValuePair, DbTransaction, DbValue, MetadataValue, MmrTree, WriteOperation}, + error::ChainStorageError, + lmdb_db::{ + lmdb::{lmdb_delete, lmdb_exists, lmdb_for_each, lmdb_get, lmdb_insert, lmdb_len, lmdb_replace}, + LMDBVec, + LMDB_DB_BLOCK_HASHES, + LMDB_DB_HEADERS, + LMDB_DB_KERNELS, + LMDB_DB_KERNEL_MMR_CP_BACKEND, + LMDB_DB_METADATA, + LMDB_DB_ORPHANS, + LMDB_DB_RANGE_PROOF_MMR_CP_BACKEND, + LMDB_DB_STXOS, + LMDB_DB_TXOS_HASH_TO_INDEX, + LMDB_DB_UTXOS, + LMDB_DB_UTXO_MMR_CP_BACKEND, + }, + memory_db::MemDbVec, + }, + transactions::{ + transaction::{TransactionKernel, TransactionOutput}, + types::{HashDigest, HashOutput}, + }, +}; +use croaring::Bitmap; +use digest::Digest; +use lmdb_zero::{Database, Environment, WriteTransaction}; +use log::*; +use std::{ + path::Path, + sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}, +}; +use tari_crypto::tari_utilities::hash::Hashable; +use tari_mmr::{ + functions::{prune_mutable_mmr, PrunedMutableMmr}, + ArrayLike, + ArrayLikeExt, + Hash as MmrHash, + MerkleCheckPoint, + MerkleProof, + MmrCache, + MmrCacheConfig, +}; +use tari_storage::lmdb_store::{db, LMDBBuilder, LMDBStore}; + +type DatabaseRef = Arc>; + +pub const LOG_TARGET: &str = "c::cs::lmdb_db::lmdb_db"; + +/// This is a lmdb-based blockchain database for persistent storage of the chain state. +pub struct LMDBDatabase +where D: Digest +{ + env: Arc, + // Lock used to ensure there aren't two threads altering + // or reading data at the same time + transaction_write_lock: RwLock, + metadata_db: DatabaseRef, + headers_db: DatabaseRef, + block_hashes_db: DatabaseRef, + utxos_db: DatabaseRef, + stxos_db: DatabaseRef, + txos_hash_to_index_db: DatabaseRef, + kernels_db: DatabaseRef, + orphans_db: DatabaseRef, + utxo_mmr: RwLock, LMDBVec>>, + utxo_checkpoints: RwLock>, + curr_utxo_checkpoint: RwLock, + kernel_mmr: RwLock, LMDBVec>>, + kernel_checkpoints: RwLock>, + curr_kernel_checkpoint: RwLock, + range_proof_mmr: RwLock, LMDBVec>>, + range_proof_checkpoints: RwLock>, + curr_range_proof_checkpoint: RwLock, +} + +impl LMDBDatabase +where D: Digest + Send + Sync +{ + pub fn new(store: LMDBStore, mmr_cache_config: MmrCacheConfig) -> Result { + let utxo_checkpoints = LMDBVec::new( + store.env(), + store + .get_handle(LMDB_DB_UTXO_MMR_CP_BACKEND) + .ok_or_else(|| ChainStorageError::CriticalError)? + .db() + .clone(), + ); + let kernel_checkpoints = LMDBVec::new( + store.env(), + store + .get_handle(LMDB_DB_KERNEL_MMR_CP_BACKEND) + .ok_or_else(|| ChainStorageError::CriticalError)? + .db() + .clone(), + ); + let range_proof_checkpoints = LMDBVec::new( + store.env(), + store + .get_handle(LMDB_DB_RANGE_PROOF_MMR_CP_BACKEND) + .ok_or_else(|| ChainStorageError::CriticalError)? + .db() + .clone(), + ); + Ok(Self { + metadata_db: store + .get_handle(LMDB_DB_METADATA) + .ok_or_else(|| ChainStorageError::CriticalError)? + .db() + .clone(), + headers_db: store + .get_handle(LMDB_DB_HEADERS) + .ok_or_else(|| ChainStorageError::CriticalError)? + .db() + .clone(), + block_hashes_db: store + .get_handle(LMDB_DB_BLOCK_HASHES) + .ok_or_else(|| ChainStorageError::CriticalError)? + .db() + .clone(), + utxos_db: store + .get_handle(LMDB_DB_UTXOS) + .ok_or_else(|| ChainStorageError::CriticalError)? + .db() + .clone(), + stxos_db: store + .get_handle(LMDB_DB_STXOS) + .ok_or_else(|| ChainStorageError::CriticalError)? + .db() + .clone(), + txos_hash_to_index_db: store + .get_handle(LMDB_DB_TXOS_HASH_TO_INDEX) + .ok_or_else(|| ChainStorageError::CriticalError)? + .db() + .clone(), + kernels_db: store + .get_handle(LMDB_DB_KERNELS) + .ok_or_else(|| ChainStorageError::CriticalError)? + .db() + .clone(), + orphans_db: store + .get_handle(LMDB_DB_ORPHANS) + .ok_or_else(|| ChainStorageError::CriticalError)? + .db() + .clone(), + utxo_mmr: RwLock::new(MmrCache::new( + MemDbVec::new(), + utxo_checkpoints.clone(), + mmr_cache_config, + )?), + utxo_checkpoints: RwLock::new(utxo_checkpoints), + curr_utxo_checkpoint: RwLock::new(MerkleCheckPoint::new(Vec::new(), Bitmap::create())), + kernel_mmr: RwLock::new(MmrCache::new( + MemDbVec::new(), + kernel_checkpoints.clone(), + mmr_cache_config, + )?), + kernel_checkpoints: RwLock::new(kernel_checkpoints), + curr_kernel_checkpoint: RwLock::new(MerkleCheckPoint::new(Vec::new(), Bitmap::create())), + range_proof_mmr: RwLock::new(MmrCache::new( + MemDbVec::new(), + range_proof_checkpoints.clone(), + mmr_cache_config, + )?), + range_proof_checkpoints: RwLock::new(range_proof_checkpoints), + curr_range_proof_checkpoint: RwLock::new(MerkleCheckPoint::new(Vec::new(), Bitmap::create())), + env: store.env(), + transaction_write_lock: Default::default(), + }) + } + + // Applies all MMR transactions excluding CreateMmrCheckpoint and RewindMmr on the header_mmr, utxo_mmr, + // range_proof_mmr and kernel_mmr. CreateMmrCheckpoint and RewindMmr txns will be performed after the the storage + // txns have been successfully applied. + // NOTE: Do not call this without having a lock on self.transaction_write_lock + fn apply_mmr_txs(&self, tx: &DbTransaction) -> Result<(), ChainStorageError> { + trace!(target: LOG_TARGET, "DB apply mmr instruction received: {:?}", tx); + for op in tx.operations.iter() { + match op { + WriteOperation::Insert(insert) => match insert { + DbKeyValuePair::BlockHeader(_, _) => {}, + DbKeyValuePair::UnspentOutput(k, v, update_mmr) => { + if *update_mmr { + self.curr_utxo_checkpoint + .write() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .push_addition(k.clone()); + let proof_hash = v.proof().hash(); + self.curr_range_proof_checkpoint + .write() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .push_addition(proof_hash.clone()); + } + }, + DbKeyValuePair::TransactionKernel(k, _, update_mmr) => { + if *update_mmr { + self.curr_kernel_checkpoint + .write() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .push_addition(k.clone()); + } + }, + _ => {}, + }, + WriteOperation::Spend(key) => match key { + DbKey::UnspentOutput(hash) => { + let index_result: Option = lmdb_get(&self.env, &self.txos_hash_to_index_db, &hash)?; + match index_result { + Some(index) => { + self.curr_utxo_checkpoint + .write() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .push_deletion(index as u32); + }, + None => return Err(ChainStorageError::UnspendableInput), + } + }, + _ => return Err(ChainStorageError::InvalidOperation("Only UTXOs can be spent".into())), + }, + _ => {}, + } + } + Ok(()) + } + + // Perform the RewindMmr and CreateMmrCheckpoint operations after MMR txns and storage txns have been applied. + // NOTE: Make sure you have a write lock on transaction_write_lock + fn commit_mmrs(&self, tx: DbTransaction) -> Result<(), ChainStorageError> { + trace!(target: LOG_TARGET, "DB commit instruction received: {:?}", tx); + for op in tx.operations.into_iter() { + match op { + WriteOperation::RewindMmr(tree, steps_back) => match tree { + MmrTree::Kernel => { + self.curr_kernel_checkpoint + .write() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .clear(); + let cp_count = self + .kernel_checkpoints + .read() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .len() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))?; + self.kernel_checkpoints + .write() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .truncate(rewind_checkpoint_index(cp_count, steps_back)) + .map_err(|e| ChainStorageError::AccessError(e.to_string()))?; + self.kernel_mmr + .write() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .update() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))?; + }, + MmrTree::Utxo => { + self.curr_utxo_checkpoint + .write() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .clear(); + let cp_count = self + .utxo_checkpoints + .read() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .len() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))?; + self.utxo_checkpoints + .write() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .truncate(rewind_checkpoint_index(cp_count, steps_back)) + .map_err(|e| ChainStorageError::AccessError(e.to_string()))?; + self.utxo_mmr + .write() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .update() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))?; + }, + MmrTree::RangeProof => { + self.curr_range_proof_checkpoint + .write() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .clear(); + let cp_count = self + .range_proof_checkpoints + .read() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .len() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))?; + self.range_proof_checkpoints + .write() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .truncate(rewind_checkpoint_index(cp_count, steps_back)) + .map_err(|e| ChainStorageError::AccessError(e.to_string()))?; + self.range_proof_mmr + .write() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .update() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))?; + }, + }, + WriteOperation::CreateMmrCheckpoint(tree) => match tree { + MmrTree::Kernel => { + let curr_checkpoint = self + .curr_kernel_checkpoint + .read() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .clone(); + self.kernel_checkpoints + .write() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .push(curr_checkpoint) + .map_err(|e| ChainStorageError::AccessError(e.to_string()))?; + + self.curr_kernel_checkpoint + .write() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .clear(); + self.kernel_mmr + .write() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .update() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))?; + }, + MmrTree::Utxo => { + let curr_checkpoint = self + .curr_utxo_checkpoint + .read() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .clone(); + self.utxo_checkpoints + .write() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .push(curr_checkpoint) + .map_err(|e| ChainStorageError::AccessError(e.to_string()))?; + + self.curr_utxo_checkpoint + .write() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .clear(); + self.utxo_mmr + .write() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .update() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))?; + }, + MmrTree::RangeProof => { + let curr_checkpoint = self + .curr_range_proof_checkpoint + .read() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .clone(); + self.range_proof_checkpoints + .write() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .push(curr_checkpoint) + .map_err(|e| ChainStorageError::AccessError(e.to_string()))?; + + self.curr_range_proof_checkpoint + .write() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .clear(); + self.range_proof_mmr + .write() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .update() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))?; + }, + }, + _ => {}, + } + } + Ok(()) + } + + // Reset any mmr txns that have been applied. + // Note: Make sure you have a write lock on transaction_write_lock + fn reset_mmrs(&self) -> Result<(), ChainStorageError> { + debug!(target: LOG_TARGET, "Reset mmrs called"); + self.kernel_mmr + .write() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .reset()?; + self.utxo_mmr + .write() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .reset()?; + self.range_proof_mmr + .write() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .reset()?; + Ok(()) + } + + // Perform all the storage txns, excluding any MMR operations. Only when all the txns can successfully be applied is + // the changes committed to the backend databases. + fn apply_storage_txs(&self, tx: &DbTransaction) -> Result<(), ChainStorageError> { + debug!(target: LOG_TARGET, "DB apply storage instruction received: {:?}", tx); + let txn = WriteTransaction::new(self.env.clone()).map_err(|e| ChainStorageError::AccessError(e.to_string()))?; + { + for op in tx.operations.iter() { + match op { + WriteOperation::Insert(insert) => match insert { + DbKeyValuePair::Metadata(k, v) => { + lmdb_replace(&txn, &self.metadata_db, &(k.clone() as u32), &v)?; + }, + DbKeyValuePair::BlockHeader(k, v) => { + let hash = v.hash(); + lmdb_insert(&txn, &self.block_hashes_db, &hash, &k)?; + lmdb_insert(&txn, &self.headers_db, &k, &v)?; + }, + DbKeyValuePair::UnspentOutput(k, v, _) => { + let proof_hash = v.proof().hash(); + if let Some(index) = self.find_range_proof_leaf_index(proof_hash)? { + lmdb_insert(&txn, &self.utxos_db, &k, &v)?; + lmdb_insert(&txn, &self.txos_hash_to_index_db, &k, &index)?; + } + }, + DbKeyValuePair::TransactionKernel(k, v, _) => { + lmdb_insert(&txn, &self.kernels_db, &k, &v)?; + }, + DbKeyValuePair::OrphanBlock(k, v) => { + lmdb_insert(&txn, &self.orphans_db, &k, &v)?; + }, + }, + WriteOperation::Delete(delete) => match delete { + DbKey::Metadata(_) => {}, // no-op + DbKey::BlockHeader(k) => { + let val: Option = lmdb_get(&self.env, &self.headers_db, &k)?; + if let Some(v) = val { + let hash = v.hash(); + lmdb_delete(&txn, &self.block_hashes_db, &hash)?; + lmdb_delete(&txn, &self.headers_db, &k)?; + } + }, + DbKey::BlockHash(hash) => { + let result: Option = lmdb_get(&self.env, &self.block_hashes_db, &hash)?; + if let Some(k) = result { + lmdb_delete(&txn, &self.block_hashes_db, &hash)?; + lmdb_delete(&txn, &self.headers_db, &k)?; + } + }, + DbKey::UnspentOutput(k) => { + lmdb_delete(&txn, &self.utxos_db, &k)?; + lmdb_delete(&txn, &self.txos_hash_to_index_db, &k)?; + }, + DbKey::SpentOutput(k) => { + lmdb_delete(&txn, &self.stxos_db, &k)?; + lmdb_delete(&txn, &self.txos_hash_to_index_db, &k)?; + }, + DbKey::TransactionKernel(k) => { + lmdb_delete(&txn, &self.kernels_db, &k)?; + }, + DbKey::OrphanBlock(k) => { + lmdb_delete(&txn, &self.orphans_db, &k)?; + }, + }, + WriteOperation::Spend(key) => match key { + DbKey::UnspentOutput(hash) => { + let utxo_result: Option = lmdb_get(&self.env, &self.utxos_db, &hash)?; + match utxo_result { + Some(utxo) => { + lmdb_delete(&txn, &self.utxos_db, &hash)?; + lmdb_insert(&txn, &self.stxos_db, &hash, &utxo)?; + }, + None => return Err(ChainStorageError::UnspendableInput), + } + }, + _ => return Err(ChainStorageError::InvalidOperation("Only UTXOs can be spent".into())), + }, + WriteOperation::UnSpend(key) => match key { + DbKey::SpentOutput(hash) => { + let stxo_result: Option = lmdb_get(&self.env, &self.stxos_db, &hash)?; + match stxo_result { + Some(stxo) => { + lmdb_delete(&txn, &self.stxos_db, &hash)?; + lmdb_insert(&txn, &self.utxos_db, &hash, &stxo)?; + }, + None => return Err(ChainStorageError::UnspendError), + } + }, + _ => return Err(ChainStorageError::InvalidOperation("Only STXOs can be unspent".into())), + }, + _ => {}, + } + } + } + txn.commit().map_err(|e| ChainStorageError::AccessError(e.to_string())) + } + + // Returns the leaf index of the hash. If the hash is in the newly added hashes it returns the future MMR index for + // that hash, this index is only valid if the change history is Committed. + fn find_range_proof_leaf_index(&self, hash: HashOutput) -> Result, ChainStorageError> { + let mut accum_leaf_index = 0; + for cp_index in 0..self + .range_proof_checkpoints + .read() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .len() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + { + if let Some(cp) = self + .range_proof_checkpoints + .read() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .get(cp_index) + .map_err(|e| ChainStorageError::AccessError(format!("Checkpoint error: {}", e.to_string())))? + { + if let Some(leaf_index) = cp.nodes_added().iter().position(|h| *h == hash) { + return Ok(Some(accum_leaf_index + leaf_index)); + } + accum_leaf_index += cp.nodes_added().len(); + } + } + if let Some(leaf_index) = self + .curr_range_proof_checkpoint + .read() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .nodes_added() + .iter() + .position(|h| *h == hash) + { + return Ok(Some(accum_leaf_index + leaf_index)); + } + Ok(None) + } + + // Construct a pruned mmr for the specified MMR tree based on the checkpoint state and new additions and deletions. + fn get_pruned_mmr(&self, tree: &MmrTree) -> Result, ChainStorageError> { + let _lock = self.lock_for_read()?; + Ok(match tree { + MmrTree::Utxo => { + let mut pruned_mmr = prune_mutable_mmr( + &*self + .utxo_mmr + .read() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))?, + )?; + for hash in self + .curr_utxo_checkpoint + .read() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .nodes_added() + { + pruned_mmr.push(&hash)?; + } + for index in self + .curr_utxo_checkpoint + .read() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .nodes_deleted() + .to_vec() + { + pruned_mmr.delete_and_compress(index, false); + } + pruned_mmr.compress(); + pruned_mmr + }, + MmrTree::Kernel => { + let mut pruned_mmr = prune_mutable_mmr( + &*self + .kernel_mmr + .read() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))?, + )?; + for hash in self + .curr_kernel_checkpoint + .read() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .nodes_added() + { + pruned_mmr.push(&hash)?; + } + pruned_mmr + }, + MmrTree::RangeProof => { + let mut pruned_mmr = prune_mutable_mmr( + &*self + .range_proof_mmr + .read() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))?, + )?; + for hash in self + .curr_range_proof_checkpoint + .read() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .nodes_added() + { + pruned_mmr.push(&hash)?; + } + pruned_mmr + }, + }) + } + + fn lock_for_write(&self) -> Result, ChainStorageError> { + Ok(self.transaction_write_lock.write().map_err(|e| { + ChainStorageError::AccessError(format!( + "Could not exclusively gain write access to DB: {}", + e.to_string() + )) + })?) + } + + fn lock_for_read(&self) -> Result, ChainStorageError> { + Ok(self.transaction_write_lock.read().map_err(|e| { + ChainStorageError::AccessError(format!( + "Could not exclusively gain write access to DB: {}", + e.to_string() + )) + })?) + } +} + +pub fn create_lmdb_database( + path: &Path, + mmr_cache_config: MmrCacheConfig, +) -> Result, ChainStorageError> +{ + let flags = db::CREATE; + std::fs::create_dir_all(&path).unwrap_or_default(); + let lmdb_store = LMDBBuilder::new() + .set_path(path.to_str().unwrap()) + .set_environment_size(15) + .set_max_number_of_databases(15) + .add_database(LMDB_DB_METADATA, flags) + .add_database(LMDB_DB_HEADERS, flags) + .add_database(LMDB_DB_BLOCK_HASHES, flags) + .add_database(LMDB_DB_UTXOS, flags) + .add_database(LMDB_DB_STXOS, flags) + .add_database(LMDB_DB_TXOS_HASH_TO_INDEX, flags) + .add_database(LMDB_DB_KERNELS, flags) + .add_database(LMDB_DB_ORPHANS, flags) + .add_database(LMDB_DB_UTXO_MMR_CP_BACKEND, flags) + .add_database(LMDB_DB_KERNEL_MMR_CP_BACKEND, flags) + .add_database(LMDB_DB_RANGE_PROOF_MMR_CP_BACKEND, flags) + .build() + .map_err(|_| ChainStorageError::CriticalError)?; + LMDBDatabase::::new(lmdb_store, mmr_cache_config) +} + +impl BlockchainBackend for LMDBDatabase +where D: Digest + Send + Sync +{ + fn write(&self, tx: DbTransaction) -> Result<(), ChainStorageError> { + // Prevent other threads from running a transaction at the same time + let _lock = self.lock_for_write()?; + match self.apply_mmr_txs(&tx) { + Ok(_) => match self.apply_storage_txs(&tx) { + Ok(_) => self.commit_mmrs(tx), + Err(e) => { + self.reset_mmrs()?; + Err(e) + }, + }, + Err(e) => { + self.reset_mmrs()?; + Err(e) + }, + } + } + + fn fetch(&self, key: &DbKey) -> Result, ChainStorageError> { + let _lock = self.lock_for_read()?; + Ok(match key { + DbKey::Metadata(k) => { + let val: Option = lmdb_get(&self.env, &self.metadata_db, &(k.clone() as u32))?; + val.map(DbValue::Metadata) + }, + DbKey::BlockHeader(k) => { + let val: Option = lmdb_get(&self.env, &self.headers_db, k)?; + val.map(|val| DbValue::BlockHeader(Box::new(val))) + }, + DbKey::BlockHash(hash) => { + let k: Option = lmdb_get(&self.env, &self.block_hashes_db, hash)?; + match k { + Some(k) => { + let val: Option = lmdb_get(&self.env, &self.headers_db, &k)?; + val.map(|val| DbValue::BlockHash(Box::new(val))) + }, + None => None, + } + }, + DbKey::UnspentOutput(k) => { + let val: Option = lmdb_get(&self.env, &self.utxos_db, k)?; + val.map(|val| DbValue::UnspentOutput(Box::new(val))) + }, + DbKey::SpentOutput(k) => { + let val: Option = lmdb_get(&self.env, &self.stxos_db, k)?; + val.map(|val| DbValue::SpentOutput(Box::new(val))) + }, + DbKey::TransactionKernel(k) => { + let val: Option = lmdb_get(&self.env, &self.kernels_db, k)?; + val.map(|val| DbValue::TransactionKernel(Box::new(val))) + }, + DbKey::OrphanBlock(k) => { + let val: Option = lmdb_get(&self.env, &self.orphans_db, k)?; + val.map(|val| DbValue::OrphanBlock(Box::new(val))) + }, + }) + } + + fn contains(&self, key: &DbKey) -> Result { + let _lock = self.lock_for_read()?; + Ok(match key { + DbKey::Metadata(k) => lmdb_exists(&self.env, &self.metadata_db, &(k.clone() as u32))?, + DbKey::BlockHeader(k) => lmdb_exists(&self.env, &self.headers_db, k)?, + DbKey::BlockHash(h) => lmdb_exists(&self.env, &self.block_hashes_db, h)?, + DbKey::UnspentOutput(k) => lmdb_exists(&self.env, &self.utxos_db, k)?, + DbKey::SpentOutput(k) => lmdb_exists(&self.env, &self.stxos_db, k)?, + DbKey::TransactionKernel(k) => lmdb_exists(&self.env, &self.kernels_db, k)?, + DbKey::OrphanBlock(k) => lmdb_exists(&self.env, &self.orphans_db, k)?, + }) + } + + fn fetch_mmr_root(&self, tree: MmrTree) -> Result, ChainStorageError> { + let _lock = self.lock_for_read()?; + let pruned_mmr = self.get_pruned_mmr(&tree)?; + Ok(pruned_mmr.get_merkle_root()?) + } + + fn fetch_mmr_only_root(&self, tree: MmrTree) -> Result, ChainStorageError> { + let _lock = self.lock_for_read()?; + let pruned_mmr = self.get_pruned_mmr(&tree)?; + Ok(pruned_mmr.get_mmr_only_root()?) + } + + fn calculate_mmr_root( + &self, + tree: MmrTree, + additions: Vec, + deletions: Vec, + ) -> Result, ChainStorageError> + { + let _lock = self.lock_for_read()?; + let mut pruned_mmr = self.get_pruned_mmr(&tree)?; + for hash in additions { + pruned_mmr.push(&hash)?; + } + if tree == MmrTree::Utxo { + for hash in deletions { + if let Some(index) = lmdb_get(&self.env, &self.txos_hash_to_index_db, &hash)? { + pruned_mmr.delete_and_compress(index, false); + } + } + pruned_mmr.compress(); + } + Ok(pruned_mmr.get_merkle_root()?) + } + + /// Returns an MMR proof extracted from the full Merkle mountain range without trimming the MMR using the roaring + /// bitmap + fn fetch_mmr_proof(&self, tree: MmrTree, leaf_pos: usize) -> Result { + let _lock = self.lock_for_read()?; + let pruned_mmr = self.get_pruned_mmr(&tree)?; + Ok(match tree { + MmrTree::Utxo => MerkleProof::for_leaf_node(&pruned_mmr.mmr(), leaf_pos)?, + MmrTree::Kernel => MerkleProof::for_leaf_node(&pruned_mmr.mmr(), leaf_pos)?, + MmrTree::RangeProof => MerkleProof::for_leaf_node(&pruned_mmr.mmr(), leaf_pos)?, + }) + } + + fn fetch_checkpoint(&self, tree: MmrTree, height: u64) -> Result { + let _lock = self.lock_for_read()?; + match tree { + MmrTree::Kernel => self + .kernel_checkpoints + .read() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .get(height as usize), + MmrTree::Utxo => self + .utxo_checkpoints + .read() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .get(height as usize), + MmrTree::RangeProof => self + .range_proof_checkpoints + .read() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .get(height as usize), + } + .map_err(|e| ChainStorageError::AccessError(format!("Checkpoint error: {}", e.to_string())))? + .ok_or_else(|| ChainStorageError::OutOfRange) + } + + fn fetch_mmr_node(&self, tree: MmrTree, pos: u32) -> Result<(Vec, bool), ChainStorageError> { + let _lock = self.lock_for_read(); + let (hash, deleted) = match tree { + MmrTree::Kernel => self + .kernel_mmr + .read() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .fetch_mmr_node(pos)?, + MmrTree::Utxo => self + .utxo_mmr + .read() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .fetch_mmr_node(pos)?, + MmrTree::RangeProof => self + .range_proof_mmr + .read() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .fetch_mmr_node(pos)?, + }; + let hash = hash.ok_or_else(|| { + ChainStorageError::UnexpectedResult(format!("A leaf node hash in the {} MMR tree was not found", tree)) + })?; + Ok((hash, deleted)) + } + + /// Iterate over all the stored orphan blocks and execute the function `f` for each block. + fn for_each_orphan(&self, f: F) -> Result<(), ChainStorageError> + where F: FnMut(Result<(HashOutput, Block), ChainStorageError>) { + let _lock = self.lock_for_read()?; + lmdb_for_each::(&self.env, &self.orphans_db, f) + } + + /// Iterate over all the stored transaction kernels and execute the function `f` for each kernel. + fn for_each_kernel(&self, f: F) -> Result<(), ChainStorageError> + where F: FnMut(Result<(HashOutput, TransactionKernel), ChainStorageError>) { + let _lock = self.lock_for_read()?; + lmdb_for_each::(&self.env, &self.kernels_db, f) + } + + /// Iterate over all the stored block headers and execute the function `f` for each header. + fn for_each_header(&self, f: F) -> Result<(), ChainStorageError> + where F: FnMut(Result<(u64, BlockHeader), ChainStorageError>) { + let _lock = self.lock_for_read()?; + lmdb_for_each::(&self.env, &self.headers_db, f) + } + + /// Iterate over all the stored unspent transaction outputs and execute the function `f` for each kernel. + fn for_each_utxo(&self, f: F) -> Result<(), ChainStorageError> + where F: FnMut(Result<(HashOutput, TransactionOutput), ChainStorageError>) { + let _lock = self.lock_for_read()?; + lmdb_for_each::(&self.env, &self.utxos_db, f) + } + + /// Finds and returns the last stored header. + fn fetch_last_header(&self) -> Result, ChainStorageError> { + let _lock = self.lock_for_read()?; + let header_count = lmdb_len(&self.env, &self.headers_db)?; + if header_count >= 1 { + let k = header_count - 1; + lmdb_get(&self.env, &self.headers_db, &k) + } else { + Ok(None) + } + } +} + +// Calculated the new checkpoint count after rewinding a set number of steps back. +fn rewind_checkpoint_index(cp_count: usize, steps_back: usize) -> usize { + if cp_count > steps_back { + cp_count - steps_back + } else { + 1 + } +} diff --git a/base_layer/core/src/chain_storage/lmdb_db/lmdb_vec.rs b/base_layer/core/src/chain_storage/lmdb_db/lmdb_vec.rs new file mode 100644 index 0000000000..7eff298b69 --- /dev/null +++ b/base_layer/core/src/chain_storage/lmdb_db/lmdb_vec.rs @@ -0,0 +1,239 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::chain_storage::{ + error::ChainStorageError, + lmdb_db::lmdb::{lmdb_clear_db, lmdb_delete, lmdb_get, lmdb_insert, lmdb_len}, +}; +use derive_error::Error; +use lmdb_zero::{Database, Environment, WriteTransaction}; +use std::{cmp::min, marker::PhantomData, sync::Arc}; +use tari_crypto::tari_utilities::message_format::MessageFormatError; +use tari_mmr::{error::MerkleMountainRangeError, ArrayLike, ArrayLikeExt}; +use tari_storage::lmdb_store::LMDBError; + +#[derive(Debug, Error)] +pub enum LMDBVecError { + MessageFormatError(MessageFormatError), + LMDBError(LMDBError), + ChainStorageError(ChainStorageError), +} + +pub struct LMDBVec { + env: Arc, + db: Arc>, + _t: PhantomData, +} + +impl LMDBVec { + pub fn new(env: Arc, db: Arc>) -> Self { + Self { + env, + db, + _t: PhantomData, + } + } +} + +impl ArrayLike for LMDBVec +where + T: serde::Serialize, + for<'t> T: serde::de::DeserializeOwned, +{ + type Error = LMDBVecError; + type Value = T; + + fn len(&self) -> Result { + Ok(lmdb_len(&self.env, &self.db)?) + } + + fn is_empty(&self) -> Result { + Ok(lmdb_len(&self.env, &self.db)? == 0) + } + + fn push(&mut self, item: Self::Value) -> Result { + let index = self.len()?; + let txn = WriteTransaction::new(self.env.clone()).map_err(|e| ChainStorageError::AccessError(e.to_string()))?; + { + lmdb_insert::(&txn, &self.db, &index, &item)?; + } + txn.commit() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))?; + Ok(index) + } + + fn get(&self, index: usize) -> Result, Self::Error> { + Ok(lmdb_get::(&self.env, &self.db, &index)?) + } + + fn get_or_panic(&self, index: usize) -> Self::Value { + self.get(index).unwrap().unwrap() + } + + fn clear(&mut self) -> Result<(), Self::Error> { + let txn = WriteTransaction::new(self.env.clone()).map_err(|e| ChainStorageError::AccessError(e.to_string()))?; + { + lmdb_clear_db(&txn, &self.db)?; + } + txn.commit() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))?; + Ok(()) + } +} + +impl ArrayLikeExt for LMDBVec +where + T: serde::Serialize, + for<'t> T: serde::de::DeserializeOwned, +{ + type Value = T; + + fn truncate(&mut self, len: usize) -> Result<(), MerkleMountainRangeError> { + let n_elements = + lmdb_len(&self.env, &self.db).map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))?; + if n_elements > len { + let txn = WriteTransaction::new(self.env.clone()) + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))?; + { + for index in len..n_elements { + lmdb_delete(&txn, &self.db, &index) + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))?; + } + } + txn.commit() + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))?; + } + Ok(()) + } + + fn shift(&mut self, n: usize) -> Result<(), MerkleMountainRangeError> { + let n_elements = + lmdb_len(&self.env, &self.db).map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))?; + // Remove the first n elements + let drain_n = min(n, n_elements); + let txn = WriteTransaction::new(self.env.clone()) + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))?; + { + for index in 0..drain_n { + lmdb_delete(&txn, &self.db, &index) + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))?; + } + } + txn.commit() + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))?; + // Update the indices of the remaining elements + // TODO: this function is very inefficient and can be improved by keeping track of a starting index offset, + // allowing the keys of the remaining items to remain the same but work as if they were updated. There might + // also be a more efficient way to update the keys using lmdb zero. + let mut shift_index = 0usize; + let txn = WriteTransaction::new(self.env.clone()) + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))?; + { + for index in drain_n..n_elements { + let item = lmdb_get::(&self.env, &self.db, &index) + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))? + .ok_or_else(|| MerkleMountainRangeError::BackendError("Unexpected error".into()))?; + lmdb_delete(&txn, &self.db, &index) + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))?; + lmdb_insert(&txn, &self.db, &shift_index, &item) + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))?; + shift_index += 1; + } + } + txn.commit() + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))?; + Ok(()) + } + + fn for_each(&self, mut f: F) -> Result<(), MerkleMountainRangeError> + where F: FnMut(Result) { + let n_elements = + lmdb_len(&self.env, &self.db).map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))?; + for index in 0..n_elements { + let val = lmdb_get::(&self.env, &self.db, &index) + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))? + .ok_or_else(|| MerkleMountainRangeError::BackendError("Unexpected error".into()))?; + f(Ok(val)) + } + Ok(()) + } +} + +impl Clone for LMDBVec +where + T: serde::Serialize, + for<'t> T: serde::de::DeserializeOwned, +{ + fn clone(&self) -> Self { + LMDBVec::new(self.env.clone(), self.db.clone()) + } +} + +#[cfg(test)] +mod test { + use super::*; + use tari_storage::lmdb_store::{db, LMDBBuilder}; + use tari_test_utils::paths::create_temporary_data_path; + + #[test] + fn len_push_get_truncate_for_each_shift_clear() { + let path = create_temporary_data_path().to_str().unwrap().to_string(); + let _ = std::fs::create_dir(&path).unwrap_or_default(); + let lmdb_store = LMDBBuilder::new() + .set_path(&path) + .set_environment_size(1) + .set_max_number_of_databases(1) + .add_database("db", db::CREATE) + .build() + .unwrap(); + let mut lmdb_vec = LMDBVec::::new(lmdb_store.env(), lmdb_store.get_handle("db").unwrap().db().clone()); + let mut mem_vec = vec![100, 200, 300, 400, 500, 600]; + assert_eq!(lmdb_vec.len().unwrap(), 0); + + mem_vec + .iter() + .for_each(|val| assert!(lmdb_vec.push(val.clone()).is_ok())); + assert_eq!(lmdb_vec.len().unwrap(), mem_vec.len()); + + mem_vec + .iter() + .enumerate() + .for_each(|(i, val)| assert_eq!(lmdb_vec.get(i).unwrap(), Some(val.clone()))); + assert_eq!(lmdb_vec.get(mem_vec.len()).unwrap(), None); + + mem_vec.truncate(4); + assert!(lmdb_vec.truncate(4).is_ok()); + assert_eq!(lmdb_vec.len().unwrap(), mem_vec.len()); + lmdb_vec + .for_each(|val| assert!(mem_vec.contains(&val.unwrap()))) + .unwrap(); + + assert!(mem_vec.shift(2).is_ok()); + assert!(lmdb_vec.shift(2).is_ok()); + assert_eq!(lmdb_vec.len().unwrap(), 2); + assert_eq!(lmdb_vec.get(0).unwrap(), Some(300)); + assert_eq!(lmdb_vec.get(1).unwrap(), Some(400)); + + assert!(lmdb_vec.clear().is_ok()); + assert_eq!(lmdb_vec.len().unwrap(), 0); + } +} diff --git a/base_layer/core/src/chain_storage/lmdb_db/mod.rs b/base_layer/core/src/chain_storage/lmdb_db/mod.rs new file mode 100644 index 0000000000..3915216817 --- /dev/null +++ b/base_layer/core/src/chain_storage/lmdb_db/mod.rs @@ -0,0 +1,41 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod lmdb; +mod lmdb_db; +mod lmdb_vec; + +// Public API exports +pub use lmdb_db::{create_lmdb_database, LMDBDatabase}; +pub use lmdb_vec::LMDBVec; + +pub const LMDB_DB_METADATA: &str = "metadata"; +pub const LMDB_DB_HEADERS: &str = "headers"; +pub const LMDB_DB_BLOCK_HASHES: &str = "block_hashes"; +pub const LMDB_DB_UTXOS: &str = "utxos"; +pub const LMDB_DB_TXOS_HASH_TO_INDEX: &str = "txos_hash_to_index"; +pub const LMDB_DB_STXOS: &str = "stxos"; +pub const LMDB_DB_KERNELS: &str = "kernels"; +pub const LMDB_DB_ORPHANS: &str = "orphans"; +pub const LMDB_DB_UTXO_MMR_CP_BACKEND: &str = "utxo_mmr_cp_backend"; +pub const LMDB_DB_KERNEL_MMR_CP_BACKEND: &str = "kernel_mmr_cp_backend"; +pub const LMDB_DB_RANGE_PROOF_MMR_CP_BACKEND: &str = "range_proof_mmr_cp_backend"; diff --git a/base_layer/core/src/chain_storage/memory_db.rs b/base_layer/core/src/chain_storage/memory_db.rs deleted file mode 100644 index e09f7dfac6..0000000000 --- a/base_layer/core/src/chain_storage/memory_db.rs +++ /dev/null @@ -1,330 +0,0 @@ -// Copyright 2019. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -//! This is a memory-based blockchain database, generally only useful for testing purposes - -use crate::{ - blocks::{Block, BlockHeader}, - chain_storage::{ - blockchain_database::BlockchainBackend, - db_transaction::{ - DbKey, - DbKeyValuePair, - DbTransaction, - DbValue, - MetadataKey, - MetadataValue, - MmrTree, - WriteOperation, - }, - error::ChainStorageError, - }, - transaction::{TransactionKernel, TransactionOutput}, - types::HashOutput, -}; -use digest::Digest; -use std::{ - collections::HashMap, - sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}, -}; -use tari_mmr::{Hash as MmrHash, MerkleChangeTracker, MerkleCheckPoint, MerkleProof, MutableMmr}; -use tari_utilities::hash::Hashable; - -struct InnerDatabase -where D: Digest -{ - headers: HashMap, - block_hashes: HashMap, - utxos: HashMap, - stxos: HashMap, - kernels: HashMap, - orphans: HashMap, - // Define MMRs to use both a memory-backed base and a memory-backed pruned MMR - utxo_mmr: MerkleChangeTracker, Vec>, - header_mmr: MerkleChangeTracker, Vec>, - kernel_mmr: MerkleChangeTracker, Vec>, - range_proof_mmr: MerkleChangeTracker, Vec>, -} - -/// A memory-backed blockchain database. The data is stored in RAM; and so all data will be lost when the program -/// terminates. Thus this DB is intended for testing purposes. It's also not very efficient since a single Mutex -/// protects the entire database. Again: testing. -#[derive(Default)] -pub struct MemoryDatabase -where D: Digest -{ - db: Arc>>, -} - -impl MemoryDatabase -where D: Digest -{ - pub(self) fn db_access(&self) -> Result>, ChainStorageError> { - self.db - .read() - .map_err(|e| ChainStorageError::AccessError(e.to_string())) - } -} - -impl BlockchainBackend for MemoryDatabase -where D: Digest + Send + Sync -{ - fn write(&self, tx: DbTransaction) -> Result<(), ChainStorageError> { - let mut db = self - .db - .write() - .map_err(|e| ChainStorageError::AccessError(e.to_string()))?; - // Not **really** atomic, but.. - // Hashmap insertions don't typically fail and b) MemoryDB should not be used for production anyway. - for op in tx.operations.into_iter() { - match op { - WriteOperation::Insert(insert) => match insert { - DbKeyValuePair::Metadata(_, _) => {}, // no-op. Memory-based DB, so we don't store metadata - DbKeyValuePair::BlockHeader(k, v) => { - let hash = v.hash(); - db.header_mmr.push(&hash).unwrap(); - db.headers.insert(k, *v); - }, - DbKeyValuePair::UnspentOutput(k, v) => { - db.utxo_mmr.push(&k).unwrap(); - let proof_hash = v.proof().hash(); - let _ = db.range_proof_mmr.push(&proof_hash); - db.utxos.insert(k, *v); - }, - DbKeyValuePair::SpentOutput(k, v) => { - db.stxos.insert(k, *v); - }, - DbKeyValuePair::TransactionKernel(k, v) => { - db.kernel_mmr.push(&k).unwrap(); - db.kernels.insert(k, *v); - }, - DbKeyValuePair::OrphanBlock(k, v) => { - db.orphans.insert(k, *v); - }, - DbKeyValuePair::CommitBlock => db - .kernel_mmr - .commit() - .and(db.range_proof_mmr.commit()) - .and(db.utxo_mmr.commit()) - .and(db.header_mmr.commit()) - .map_err(|e| ChainStorageError::AccessError(e.to_string()))?, - }, - WriteOperation::Delete(delete) => match delete { - DbKey::Metadata(_) => {}, // no-op - DbKey::BlockHeader(k) => { - db.headers.remove(&k); - }, - DbKey::BlockHash(hash) => match db.block_hashes.remove(&hash) { - Some(i) => { - db.headers.remove(&i); - }, - None => {}, - }, - DbKey::UnspentOutput(k) => { - db.utxos.remove(&k); - }, - DbKey::SpentOutput(k) => { - db.stxos.remove(&k); - }, - DbKey::TransactionKernel(k) => { - db.kernels.remove(&k); - }, - DbKey::OrphanBlock(k) => { - db.orphans.remove(&k); - }, - }, - WriteOperation::Spend(key) => match key { - DbKey::UnspentOutput(hash) => { - let moved = spend_utxo(&mut db, hash); - if !moved { - return Err(ChainStorageError::UnspendableInput); - } - }, - _ => return Err(ChainStorageError::InvalidOperation("Only UTXOs can be spent".into())), - }, - WriteOperation::UnSpend(key) => match key { - DbKey::SpentOutput(hash) => { - let moved = unspend_stxo(&mut db, hash); - if !moved { - return Err(ChainStorageError::UnspendError); - } - }, - _ => return Err(ChainStorageError::InvalidOperation("Only STXOs can be unspent".into())), - }, - WriteOperation::CreateMmrCheckpoint(tree) => match tree { - MmrTree::Header => db - .header_mmr - .commit() - .map_err(|e| ChainStorageError::AccessError(e.to_string()))?, - MmrTree::Kernel => db - .kernel_mmr - .commit() - .map_err(|e| ChainStorageError::AccessError(e.to_string()))?, - MmrTree::Utxo => db - .utxo_mmr - .commit() - .map_err(|e| ChainStorageError::AccessError(e.to_string()))?, - MmrTree::RangeProof => db - .range_proof_mmr - .commit() - .map_err(|e| ChainStorageError::AccessError(e.to_string()))?, - }, - } - } - Ok(()) - } - - fn fetch(&self, key: &DbKey) -> Result, ChainStorageError> { - let db = self.db_access()?; - let result = match key { - DbKey::Metadata(MetadataKey::ChainHeight) => Some(DbValue::Metadata(MetadataValue::ChainHeight(None))), - DbKey::Metadata(MetadataKey::AccumulatedWork) => Some(DbValue::Metadata(MetadataValue::AccumulatedWork(0))), - DbKey::Metadata(MetadataKey::PruningHorizon) => Some(DbValue::Metadata(MetadataValue::PruningHorizon(0))), - DbKey::Metadata(MetadataKey::BestBlock) => Some(DbValue::Metadata(MetadataValue::BestBlock(None))), - DbKey::BlockHeader(k) => db.headers.get(k).map(|v| DbValue::BlockHeader(Box::new(v.clone()))), - DbKey::BlockHash(hash) => db - .block_hashes - .get(hash) - .and_then(|i| db.headers.get(i)) - .map(|v| DbValue::BlockHeader(Box::new(v.clone()))), - DbKey::UnspentOutput(k) => db.utxos.get(k).map(|v| DbValue::UnspentOutput(Box::new(v.clone()))), - DbKey::SpentOutput(k) => db.stxos.get(k).map(|v| DbValue::SpentOutput(Box::new(v.clone()))), - DbKey::TransactionKernel(k) => db - .kernels - .get(k) - .map(|v| DbValue::TransactionKernel(Box::new(v.clone()))), - DbKey::OrphanBlock(k) => db.orphans.get(k).map(|v| DbValue::OrphanBlock(Box::new(v.clone()))), - }; - Ok(result) - } - - fn contains(&self, key: &DbKey) -> Result { - let db = self.db_access()?; - let result = match key { - DbKey::Metadata(_) => true, - DbKey::BlockHeader(k) => db.headers.contains_key(k), - DbKey::BlockHash(h) => db.block_hashes.contains_key(h), - DbKey::UnspentOutput(k) => db.utxos.contains_key(k), - DbKey::SpentOutput(k) => db.stxos.contains_key(k), - DbKey::TransactionKernel(k) => db.kernels.contains_key(k), - DbKey::OrphanBlock(k) => db.orphans.contains_key(k), - }; - Ok(result) - } - - fn fetch_mmr_root(&self, tree: MmrTree) -> Result, ChainStorageError> { - let db = self.db_access()?; - let root = match tree { - MmrTree::Utxo => db.utxo_mmr.get_merkle_root(), - MmrTree::Kernel => db.kernel_mmr.get_merkle_root(), - MmrTree::RangeProof => db.range_proof_mmr.get_merkle_root(), - MmrTree::Header => db.header_mmr.get_merkle_root(), - }; - Ok(root) - } - - fn fetch_mmr_proof(&self, _tree: MmrTree, _pos: u64) -> Result { - unimplemented!() - } - - fn fetch_mmr_checkpoint(&self, tree: MmrTree, index: u64) -> Result { - let db = self.db_access()?; - let index = index as usize; - let cp = match tree { - MmrTree::Kernel => db.kernel_mmr.get_checkpoint(index), - MmrTree::Utxo => db.utxo_mmr.get_checkpoint(index), - MmrTree::RangeProof => db.range_proof_mmr.get_checkpoint(index), - MmrTree::Header => db.header_mmr.get_checkpoint(index), - }; - cp.map_err(|e| ChainStorageError::AccessError(format!("MMR Checkpoint error: {}", e.to_string()))) - } - - fn fetch_mmr_node(&self, tree: MmrTree, pos: u32) -> Result<(Vec, bool), ChainStorageError> { - let db = self.db_access()?; - let (hash, deleted) = match tree { - MmrTree::Kernel => db.kernel_mmr.get_leaf_status(pos), - MmrTree::Header => db.kernel_mmr.get_leaf_status(pos), - MmrTree::Utxo => db.kernel_mmr.get_leaf_status(pos), - MmrTree::RangeProof => db.kernel_mmr.get_leaf_status(pos), - }; - let hash = hash - .ok_or(ChainStorageError::UnexpectedResult(format!( - "A leaf node hash in the {} MMR tree was not found", - tree - )))? - .clone(); - Ok((hash, deleted)) - } -} - -impl Clone for MemoryDatabase -where D: Digest -{ - fn clone(&self) -> Self { - MemoryDatabase { db: self.db.clone() } - } -} - -impl Default for InnerDatabase -where D: Digest -{ - fn default() -> Self { - let utxo_mmr = MerkleChangeTracker::::new(MutableMmr::new(Vec::new()), Vec::new()).unwrap(); - let header_mmr = MerkleChangeTracker::::new(MutableMmr::new(Vec::new()), Vec::new()).unwrap(); - let kernel_mmr = MerkleChangeTracker::::new(MutableMmr::new(Vec::new()), Vec::new()).unwrap(); - let range_proof_mmr = MerkleChangeTracker::::new(MutableMmr::new(Vec::new()), Vec::new()).unwrap(); - InnerDatabase { - headers: HashMap::default(), - block_hashes: HashMap::default(), - utxos: HashMap::default(), - stxos: HashMap::default(), - kernels: HashMap::default(), - orphans: HashMap::default(), - utxo_mmr, - header_mmr, - kernel_mmr, - range_proof_mmr, - } - } -} - -// This is a private helper function. When it is called, we are guaranteed to have a write lock on self.db -fn spend_utxo(db: &mut RwLockWriteGuard>, hash: HashOutput) -> bool { - match db.utxos.remove(&hash) { - None => false, - Some(utxo) => { - db.stxos.insert(hash, utxo); - true - }, - } -} - -// This is a private helper function. When it is called, we are guaranteed to have a write lock on self.db -fn unspend_stxo(db: &mut RwLockWriteGuard>, hash: HashOutput) -> bool { - match db.stxos.remove(&hash) { - None => false, - Some(stxo) => { - db.utxos.insert(hash, stxo); - true - }, - } -} diff --git a/base_layer/core/src/chain_storage/memory_db/mem_db_vec.rs b/base_layer/core/src/chain_storage/memory_db/mem_db_vec.rs new file mode 100644 index 0000000000..9cad5eb0cc --- /dev/null +++ b/base_layer/core/src/chain_storage/memory_db/mem_db_vec.rs @@ -0,0 +1,162 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::chain_storage::error::ChainStorageError; +use std::{ + cmp::min, + sync::{Arc, RwLock}, +}; +use tari_mmr::{error::MerkleMountainRangeError, ArrayLike, ArrayLikeExt}; + +#[derive(Debug, Clone)] +pub struct MemDbVec { + db: Arc>>, +} + +impl MemDbVec { + pub fn new() -> Self { + Self { + db: Arc::new(RwLock::new(Vec::::new())), + } + } +} + +impl ArrayLike for MemDbVec { + type Error = ChainStorageError; + type Value = T; + + fn len(&self) -> Result { + Ok(self + .db + .read() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .len()) + } + + fn is_empty(&self) -> Result { + Ok(self + .db + .read() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .is_empty()) + } + + fn push(&mut self, item: Self::Value) -> Result { + self.db + .write() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .push(item); + Ok(self.len()? - 1) + } + + fn get(&self, index: usize) -> Result, Self::Error> { + Ok(self + .db + .read() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .get(index) + .map_err(|e| ChainStorageError::AccessError(e.to_string()))?) + } + + fn get_or_panic(&self, index: usize) -> Self::Value { + self.db.read().unwrap()[index].clone() + } + + fn clear(&mut self) -> Result<(), Self::Error> { + self.db + .write() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + .clear(); + Ok(()) + } +} + +impl ArrayLikeExt for MemDbVec { + type Value = T; + + fn truncate(&mut self, len: usize) -> Result<(), MerkleMountainRangeError> { + self.db + .write() + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))? + .truncate(len); + Ok(()) + } + + fn shift(&mut self, n: usize) -> Result<(), MerkleMountainRangeError> { + let drain_n = min( + n, + self.len() + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))?, + ); + self.db + .write() + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))? + .drain(0..drain_n); + Ok(()) + } + + fn for_each(&self, f: F) -> Result<(), MerkleMountainRangeError> + where F: FnMut(Result) { + self.db + .read() + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))? + .iter() + .map(|v| Ok(v.clone())) + .for_each(f); + Ok(()) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn len_push_get_truncate_for_each_shift_clear() { + let mut db_vec = MemDbVec::::new(); + let mut mem_vec = vec![100, 200, 300, 400, 500, 600]; + assert_eq!(db_vec.len().unwrap(), 0); + + mem_vec.iter().for_each(|val| assert!(db_vec.push(val.clone()).is_ok())); + assert_eq!(db_vec.len().unwrap(), mem_vec.len()); + + mem_vec + .iter() + .enumerate() + .for_each(|(i, val)| assert_eq!(db_vec.get(i).unwrap(), Some(val.clone()))); + assert_eq!(db_vec.get(mem_vec.len()).unwrap(), None); + + mem_vec.truncate(4); + assert!(db_vec.truncate(4).is_ok()); + assert_eq!(db_vec.len().unwrap(), mem_vec.len()); + db_vec.for_each(|val| assert!(mem_vec.contains(&val.unwrap()))).unwrap(); + + assert!(mem_vec.shift(2).is_ok()); + assert!(db_vec.shift(2).is_ok()); + assert_eq!(db_vec.len().unwrap(), 2); + assert_eq!(db_vec.get(0).unwrap(), Some(300)); + assert_eq!(db_vec.get(1).unwrap(), Some(400)); + + assert!(db_vec.clear().is_ok()); + assert_eq!(db_vec.len().unwrap(), 0); + } +} diff --git a/base_layer/core/src/chain_storage/memory_db/memory_db.rs b/base_layer/core/src/chain_storage/memory_db/memory_db.rs new file mode 100644 index 0000000000..c6c9f7fa1e --- /dev/null +++ b/base_layer/core/src/chain_storage/memory_db/memory_db.rs @@ -0,0 +1,603 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +//! This is a memory-based blockchain database, generally only useful for testing purposes + +use crate::{ + blocks::{Block, BlockHeader}, + chain_storage::{ + blockchain_database::BlockchainBackend, + db_transaction::{DbKey, DbKeyValuePair, DbTransaction, DbValue, MetadataValue, MmrTree, WriteOperation}, + error::ChainStorageError, + memory_db::MemDbVec, + }, + transactions::{ + transaction::{TransactionKernel, TransactionOutput}, + types::HashOutput, + }, +}; +use croaring::Bitmap; +use digest::Digest; +use std::{ + collections::HashMap, + sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}, +}; +use tari_crypto::tari_utilities::hash::Hashable; +use tari_mmr::{ + functions::{prune_mutable_mmr, PrunedMutableMmr}, + ArrayLike, + ArrayLikeExt, + Hash as MmrHash, + MerkleCheckPoint, + MerkleProof, + MmrCache, + MmrCacheConfig, +}; + +/// A generic struct for storing node objects in the BlockchainDB that also form part of an MMR. The index field makes +/// reverse lookups (find by hash) possible. +#[derive(Debug)] +struct MerkleNode { + index: usize, + value: T, +} + +#[derive(Debug)] +struct InnerDatabase +where D: Digest +{ + metadata: HashMap, + headers: HashMap, + block_hashes: HashMap, + utxos: HashMap>, + stxos: HashMap>, + kernels: HashMap, + orphans: HashMap, + // Define MMRs to use both a memory-backed base and a memory-backed pruned MMR + utxo_mmr: MmrCache, MemDbVec>, + utxo_checkpoints: MemDbVec, + curr_utxo_checkpoint: MerkleCheckPoint, + kernel_mmr: MmrCache, MemDbVec>, + kernel_checkpoints: MemDbVec, + curr_kernel_checkpoint: MerkleCheckPoint, + range_proof_mmr: MmrCache, MemDbVec>, + range_proof_checkpoints: MemDbVec, + curr_range_proof_checkpoint: MerkleCheckPoint, +} + +/// A memory-backed blockchain database. The data is stored in RAM; and so all data will be lost when the program +/// terminates. Thus this DB is intended for testing purposes. It's also not very efficient since a single Mutex +/// protects the entire database. Again: testing. +#[derive(Default, Debug)] +pub struct MemoryDatabase +where D: Digest +{ + db: Arc>>, +} + +impl MemoryDatabase +where D: Digest +{ + pub fn new(mmr_cache_config: MmrCacheConfig) -> Self { + let utxo_checkpoints = MemDbVec::::new(); + let utxo_mmr = MmrCache::::new(MemDbVec::new(), utxo_checkpoints.clone(), mmr_cache_config).unwrap(); + let kernel_checkpoints = MemDbVec::::new(); + let kernel_mmr = + MmrCache::::new(MemDbVec::new(), kernel_checkpoints.clone(), mmr_cache_config).unwrap(); + let range_proof_checkpoints = MemDbVec::::new(); + let range_proof_mmr = + MmrCache::::new(MemDbVec::new(), range_proof_checkpoints.clone(), mmr_cache_config).unwrap(); + Self { + db: Arc::new(RwLock::new(InnerDatabase { + metadata: HashMap::default(), + headers: HashMap::default(), + block_hashes: HashMap::default(), + utxos: HashMap::default(), + stxos: HashMap::default(), + kernels: HashMap::default(), + orphans: HashMap::default(), + utxo_mmr, + utxo_checkpoints, + curr_utxo_checkpoint: MerkleCheckPoint::new(Vec::new(), Bitmap::create()), + kernel_mmr, + kernel_checkpoints, + curr_kernel_checkpoint: MerkleCheckPoint::new(Vec::new(), Bitmap::create()), + range_proof_mmr, + range_proof_checkpoints, + curr_range_proof_checkpoint: MerkleCheckPoint::new(Vec::new(), Bitmap::create()), + })), + } + } + + pub(self) fn db_access(&self) -> Result>, ChainStorageError> { + self.db + .read() + .map_err(|e| ChainStorageError::AccessError(e.to_string())) + } +} + +impl BlockchainBackend for MemoryDatabase +where D: Digest + Send + Sync +{ + fn write(&self, tx: DbTransaction) -> Result<(), ChainStorageError> { + let mut db = self + .db + .write() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))?; + // Not **really** atomic, but.. + // Hashmap insertions don't typically fail and b) MemoryDB should not be used for production anyway. + for op in tx.operations.into_iter() { + match op { + WriteOperation::Insert(insert) => match insert { + DbKeyValuePair::Metadata(k, v) => { + let key = k as u32; + db.metadata.insert(key, v); + }, + DbKeyValuePair::BlockHeader(k, v) => { + if db.headers.contains_key(&k) { + return Err(ChainStorageError::InvalidOperation("Duplicate key".to_string())); + } + db.block_hashes.insert(v.hash(), k); + db.headers.insert(k, *v); + }, + DbKeyValuePair::UnspentOutput(k, v, update_mmr) => { + if db.utxos.contains_key(&k) { + return Err(ChainStorageError::InvalidOperation("Duplicate key".to_string())); + } + let proof_hash = v.proof().hash(); + if update_mmr { + db.curr_utxo_checkpoint.push_addition(k.clone()); + db.curr_range_proof_checkpoint.push_addition(proof_hash.clone()); + } + if let Some(index) = find_range_proof_leaf_index(&mut db, proof_hash)? { + let v = MerkleNode { index, value: *v }; + db.utxos.insert(k, v); + } + }, + DbKeyValuePair::TransactionKernel(k, v, update_mmr) => { + if db.kernels.contains_key(&k) { + return Err(ChainStorageError::InvalidOperation("Duplicate key".to_string())); + } + if update_mmr { + db.curr_kernel_checkpoint.push_addition(k.clone()); + } + db.kernels.insert(k, *v); + }, + DbKeyValuePair::OrphanBlock(k, v) => { + if db.orphans.contains_key(&k) { + return Err(ChainStorageError::InvalidOperation("Duplicate key".to_string())); + } + db.orphans.insert(k, *v); + }, + }, + WriteOperation::Delete(delete) => match delete { + DbKey::Metadata(_) => {}, // no-op + DbKey::BlockHeader(k) => { + db.headers.remove(&k).and_then(|v| db.block_hashes.remove(&v.hash())); + }, + DbKey::BlockHash(hash) => { + db.block_hashes.remove(&hash).and_then(|i| db.headers.remove(&i)); + }, + DbKey::UnspentOutput(k) => { + db.utxos.remove(&k); + }, + DbKey::SpentOutput(k) => { + db.stxos.remove(&k); + }, + DbKey::TransactionKernel(k) => { + db.kernels.remove(&k); + }, + DbKey::OrphanBlock(k) => { + db.orphans.remove(&k); + }, + }, + WriteOperation::Spend(key) => match key { + DbKey::UnspentOutput(hash) => { + let moved = spend_utxo(&mut db, hash); + if !moved { + return Err(ChainStorageError::UnspendableInput); + } + }, + _ => return Err(ChainStorageError::InvalidOperation("Only UTXOs can be spent".into())), + }, + WriteOperation::UnSpend(key) => match key { + DbKey::SpentOutput(hash) => { + let moved = unspend_stxo(&mut db, hash); + if !moved { + return Err(ChainStorageError::UnspendError); + } + }, + _ => return Err(ChainStorageError::InvalidOperation("Only STXOs can be unspent".into())), + }, + WriteOperation::CreateMmrCheckpoint(tree) => match tree { + MmrTree::Kernel => { + let curr_checkpoint = db.curr_kernel_checkpoint.clone(); + db.kernel_checkpoints.push(curr_checkpoint)?; + db.curr_kernel_checkpoint.clear(); + + db.kernel_mmr + .update() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + }, + MmrTree::Utxo => { + let curr_checkpoint = db.curr_utxo_checkpoint.clone(); + db.utxo_checkpoints.push(curr_checkpoint)?; + db.curr_utxo_checkpoint.clear(); + + db.utxo_mmr + .update() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + }, + MmrTree::RangeProof => { + let curr_checkpoint = db.curr_range_proof_checkpoint.clone(); + db.range_proof_checkpoints.push(curr_checkpoint)?; + db.curr_range_proof_checkpoint.clear(); + + db.range_proof_mmr + .update() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))? + }, + }, + WriteOperation::RewindMmr(tree, steps_back) => match tree { + MmrTree::Kernel => { + db.curr_kernel_checkpoint.clear(); + let cp_count = db.kernel_checkpoints.len()?; + db.kernel_checkpoints + .truncate(rewind_checkpoint_index(cp_count, steps_back))?; + db.kernel_mmr + .update() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))?; + }, + MmrTree::Utxo => { + db.curr_utxo_checkpoint.clear(); + let cp_count = db.utxo_checkpoints.len()?; + db.utxo_checkpoints + .truncate(rewind_checkpoint_index(cp_count, steps_back))?; + db.utxo_mmr + .update() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))?; + }, + MmrTree::RangeProof => { + db.curr_range_proof_checkpoint.clear(); + let cp_count = db.range_proof_checkpoints.len()?; + db.range_proof_checkpoints + .truncate(rewind_checkpoint_index(cp_count, steps_back))?; + db.range_proof_mmr + .update() + .map_err(|e| ChainStorageError::AccessError(e.to_string()))?; + }, + }, + } + } + Ok(()) + } + + fn fetch(&self, key: &DbKey) -> Result, ChainStorageError> { + let db = self.db_access()?; + let result = match key { + DbKey::Metadata(k) => db + .metadata + .get(&(k.clone() as u32)) + .map(|v| DbValue::Metadata(v.clone())), + DbKey::BlockHeader(k) => db.headers.get(k).map(|v| DbValue::BlockHeader(Box::new(v.clone()))), + DbKey::BlockHash(hash) => db + .block_hashes + .get(hash) + .and_then(|i| db.headers.get(i)) + .map(|v| DbValue::BlockHash(Box::new(v.clone()))), + DbKey::UnspentOutput(k) => db + .utxos + .get(k) + .map(|v| DbValue::UnspentOutput(Box::new(v.value.clone()))), + DbKey::SpentOutput(k) => db.stxos.get(k).map(|v| DbValue::SpentOutput(Box::new(v.value.clone()))), + DbKey::TransactionKernel(k) => db + .kernels + .get(k) + .map(|v| DbValue::TransactionKernel(Box::new(v.clone()))), + DbKey::OrphanBlock(k) => db.orphans.get(k).map(|v| DbValue::OrphanBlock(Box::new(v.clone()))), + }; + Ok(result) + } + + fn contains(&self, key: &DbKey) -> Result { + let db = self.db_access()?; + let result = match key { + DbKey::Metadata(_) => true, + DbKey::BlockHeader(k) => db.headers.contains_key(k), + DbKey::BlockHash(h) => db.block_hashes.contains_key(h), + DbKey::UnspentOutput(k) => db.utxos.contains_key(k), + DbKey::SpentOutput(k) => db.stxos.contains_key(k), + DbKey::TransactionKernel(k) => db.kernels.contains_key(k), + DbKey::OrphanBlock(k) => db.orphans.contains_key(k), + }; + Ok(result) + } + + fn fetch_mmr_root(&self, tree: MmrTree) -> Result, ChainStorageError> { + let db = self.db_access()?; + let pruned_mmr = get_pruned_mmr(&db, &tree)?; + Ok(pruned_mmr.get_merkle_root()?) + } + + fn fetch_mmr_only_root(&self, tree: MmrTree) -> Result, ChainStorageError> { + let db = self.db_access()?; + let pruned_mmr = get_pruned_mmr(&db, &tree)?; + Ok(pruned_mmr.get_mmr_only_root()?) + } + + fn calculate_mmr_root( + &self, + tree: MmrTree, + additions: Vec, + deletions: Vec, + ) -> Result, ChainStorageError> + { + let db = self.db_access()?; + let mut pruned_mmr = get_pruned_mmr(&db, &tree)?; + for hash in additions { + pruned_mmr.push(&hash)?; + } + if tree == MmrTree::Utxo { + deletions.iter().for_each(|hash| { + if let Some(node) = db.utxos.get(hash) { + pruned_mmr.delete_and_compress(node.index as u32, false); + } + }); + pruned_mmr.compress(); + } + Ok(pruned_mmr.get_merkle_root()?) + } + + /// Returns an MMR proof extracted from the full Merkle mountain range without trimming the MMR using the roaring + /// bitmap + fn fetch_mmr_proof(&self, tree: MmrTree, leaf_pos: usize) -> Result { + let db = self.db_access()?; + let pruned_mmr = get_pruned_mmr(&db, &tree)?; + let proof = match tree { + MmrTree::Utxo => MerkleProof::for_leaf_node(&pruned_mmr.mmr(), leaf_pos)?, + MmrTree::Kernel => MerkleProof::for_leaf_node(&pruned_mmr.mmr(), leaf_pos)?, + MmrTree::RangeProof => MerkleProof::for_leaf_node(&pruned_mmr.mmr(), leaf_pos)?, + }; + Ok(proof) + } + + fn fetch_checkpoint(&self, tree: MmrTree, height: u64) -> Result { + let db = self.db_access()?; + match tree { + MmrTree::Kernel => db.kernel_checkpoints.get(height as usize), + MmrTree::Utxo => db.utxo_checkpoints.get(height as usize), + MmrTree::RangeProof => db.range_proof_checkpoints.get(height as usize), + }? + .ok_or_else(|| ChainStorageError::OutOfRange) + } + + fn fetch_mmr_node(&self, tree: MmrTree, pos: u32) -> Result<(Vec, bool), ChainStorageError> { + let db = self.db_access()?; + let (hash, deleted) = match tree { + MmrTree::Kernel => db.kernel_mmr.fetch_mmr_node(pos)?, + MmrTree::Utxo => db.utxo_mmr.fetch_mmr_node(pos)?, + MmrTree::RangeProof => db.range_proof_mmr.fetch_mmr_node(pos)?, + }; + let hash = hash.ok_or_else(|| { + ChainStorageError::UnexpectedResult(format!("A leaf node hash in the {} MMR tree was not found", tree)) + })?; + Ok((hash, deleted)) + } + + /// Iterate over all the stored orphan blocks and execute the function `f` for each block. + fn for_each_orphan(&self, mut f: F) -> Result<(), ChainStorageError> + where F: FnMut(Result<(HashOutput, Block), ChainStorageError>) { + let db = self.db_access()?; + for (key, val) in db.orphans.iter() { + f(Ok((key.clone(), val.clone()))); + } + Ok(()) + } + + /// Iterate over all the stored transaction kernels and execute the function `f` for each kernel. + fn for_each_kernel(&self, mut f: F) -> Result<(), ChainStorageError> + where F: FnMut(Result<(HashOutput, TransactionKernel), ChainStorageError>) { + let db = self.db_access()?; + for (key, val) in db.kernels.iter() { + f(Ok((key.clone(), val.clone()))); + } + Ok(()) + } + + /// Iterate over all the stored block headers and execute the function `f` for each header. + fn for_each_header(&self, mut f: F) -> Result<(), ChainStorageError> + where F: FnMut(Result<(u64, BlockHeader), ChainStorageError>) { + let db = self.db_access()?; + for (key, val) in db.headers.iter() { + f(Ok((*key, val.clone()))); + } + Ok(()) + } + + /// Iterate over all the stored unspent transaction outputs and execute the function `f` for each UTXO. + fn for_each_utxo(&self, mut f: F) -> Result<(), ChainStorageError> + where F: FnMut(Result<(HashOutput, TransactionOutput), ChainStorageError>) { + let db = self.db_access()?; + for (key, val) in db.utxos.iter() { + f(Ok((key.clone(), val.value.clone()))); + } + Ok(()) + } + + /// Finds and returns the last stored header. + fn fetch_last_header(&self) -> Result, ChainStorageError> { + let db = self.db_access()?; + let header_count = db.headers.len() as u64; + if header_count >= 1 { + let k = header_count - 1; + Ok(db.headers.get(&k).cloned()) + } else { + Ok(None) + } + } +} + +impl Clone for MemoryDatabase +where D: Digest +{ + fn clone(&self) -> Self { + MemoryDatabase { db: self.db.clone() } + } +} + +impl Default for InnerDatabase +where D: Digest +{ + fn default() -> Self { + let mmr_cache_config = MmrCacheConfig { rewind_hist_len: 100 }; + let utxo_checkpoints = MemDbVec::new(); + let utxo_mmr = MmrCache::::new(MemDbVec::new(), utxo_checkpoints.clone(), mmr_cache_config).unwrap(); + let kernel_checkpoints = MemDbVec::new(); + let kernel_mmr = + MmrCache::::new(MemDbVec::new(), kernel_checkpoints.clone(), mmr_cache_config).unwrap(); + let range_proof_checkpoints = MemDbVec::new(); + let range_proof_mmr = + MmrCache::::new(MemDbVec::new(), range_proof_checkpoints.clone(), mmr_cache_config).unwrap(); + Self { + metadata: HashMap::default(), + headers: HashMap::default(), + block_hashes: HashMap::default(), + utxos: HashMap::default(), + stxos: HashMap::default(), + kernels: HashMap::default(), + orphans: HashMap::default(), + utxo_mmr, + utxo_checkpoints, + curr_utxo_checkpoint: MerkleCheckPoint::new(Vec::new(), Bitmap::create()), + kernel_mmr, + kernel_checkpoints, + curr_kernel_checkpoint: MerkleCheckPoint::new(Vec::new(), Bitmap::create()), + range_proof_mmr, + range_proof_checkpoints, + curr_range_proof_checkpoint: MerkleCheckPoint::new(Vec::new(), Bitmap::create()), + } + } +} + +// This is a private helper function. When it is called, we are guaranteed to have a write lock on self.db +fn spend_utxo(db: &mut RwLockWriteGuard>, hash: HashOutput) -> bool { + match db.utxos.remove(&hash) { + None => false, + Some(utxo) => { + db.curr_utxo_checkpoint.push_deletion(utxo.index as u32); + db.stxos.insert(hash, utxo); + true + }, + } +} + +// This is a private helper function. When it is called, we are guaranteed to have a write lock on self.db. Unspend_stxo +// is only called for rewind operations and doesn't have to re-insert the utxo entry into the utxo_mmr as the MMR will +// be rolled back. +fn unspend_stxo(db: &mut RwLockWriteGuard>, hash: HashOutput) -> bool { + match db.stxos.remove(&hash) { + None => false, + Some(stxo) => { + db.utxos.insert(hash, stxo); + true + }, + } +} + +// Returns the leaf index of the hash. If the hash is in the newly added hashes it returns the future MMR index for that +// hash, this index is only valid if the change history is Committed. +fn find_range_proof_leaf_index( + db: &mut RwLockWriteGuard>, + hash: HashOutput, +) -> Result, ChainStorageError> +{ + let mut accum_leaf_index = 0; + for cp_index in 0..db.range_proof_checkpoints.len()? { + if let Some(cp) = db + .range_proof_checkpoints + .get(cp_index) + .map_err(|e| ChainStorageError::AccessError(format!("Checkpoint error: {}", e.to_string())))? + { + if let Some(leaf_index) = cp.nodes_added().iter().position(|h| *h == hash) { + return Ok(Some(accum_leaf_index + leaf_index)); + } + accum_leaf_index += cp.nodes_added().len(); + } + } + if let Some(leaf_index) = db + .curr_range_proof_checkpoint + .nodes_added() + .iter() + .position(|h| *h == hash) + { + return Ok(Some(accum_leaf_index + leaf_index)); + } + Ok(None) +} + +// Construct a pruned mmr for the specified MMR tree based on the checkpoint state and new additions and deletions. +fn get_pruned_mmr( + db: &RwLockReadGuard>, + tree: &MmrTree, +) -> Result, ChainStorageError> +{ + Ok(match tree { + MmrTree::Utxo => { + let mut pruned_mmr = prune_mutable_mmr(&db.utxo_mmr)?; + for hash in db.curr_utxo_checkpoint.nodes_added() { + pruned_mmr.push(&hash)?; + } + db.curr_utxo_checkpoint + .nodes_deleted() + .to_vec() + .iter() + .for_each(|index| { + pruned_mmr.delete_and_compress(*index, false); + }); + pruned_mmr.compress(); + pruned_mmr + }, + MmrTree::Kernel => { + let mut pruned_mmr = prune_mutable_mmr(&db.kernel_mmr)?; + for hash in db.curr_kernel_checkpoint.nodes_added() { + pruned_mmr.push(&hash)?; + } + pruned_mmr + }, + MmrTree::RangeProof => { + let mut pruned_mmr = prune_mutable_mmr(&db.range_proof_mmr)?; + for hash in db.curr_range_proof_checkpoint.nodes_added() { + pruned_mmr.push(&hash)?; + } + pruned_mmr + }, + }) +} + +// Calculated the new checkpoint count after rewinding a set number of steps back. +fn rewind_checkpoint_index(cp_count: usize, steps_back: usize) -> usize { + if cp_count > steps_back { + cp_count - steps_back + } else { + 1 + } +} diff --git a/base_layer/core/src/chain_storage/memory_db/mod.rs b/base_layer/core/src/chain_storage/memory_db/mod.rs new file mode 100644 index 0000000000..650a886f3a --- /dev/null +++ b/base_layer/core/src/chain_storage/memory_db/mod.rs @@ -0,0 +1,28 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod mem_db_vec; +mod memory_db; + +// Public API exports +pub use mem_db_vec::MemDbVec; +pub use memory_db::MemoryDatabase; diff --git a/base_layer/core/src/chain_storage/metadata.rs b/base_layer/core/src/chain_storage/metadata.rs index b84f57cbcf..f0f3708217 100644 --- a/base_layer/core/src/chain_storage/metadata.rs +++ b/base_layer/core/src/chain_storage/metadata.rs @@ -21,50 +21,48 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::blocks::blockheader::BlockHash; +use serde::{Deserialize, Serialize}; +use std::fmt::{Display, Error, Formatter}; +use tari_crypto::tari_utilities::hex::Hex; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ChainMetadata { /// The current chain height, or the block number of the longest valid chain, or `None` if there is no chain pub height_of_longest_chain: Option, /// The block hash of the current tip of the longest valid chain, or `None` for an empty chain pub best_block: Option, - /// The total accumulated difficulty, or work, on the longest valid chain since the genesis block. - pub total_accumulated_difficulty: u64, /// The number of blocks back from the tip that this database tracks. A value of 0 indicates that all blocks are /// tracked (i.e. the database is in full archival mode). pub pruning_horizon: u64, } impl ChainMetadata { - pub fn new(height: u64, hash: BlockHash, work: u64, horizon: u64) -> ChainMetadata { + pub fn new(height: u64, hash: BlockHash, horizon: u64) -> ChainMetadata { ChainMetadata { height_of_longest_chain: Some(height), best_block: Some(hash), - total_accumulated_difficulty: work, pruning_horizon: horizon, } } - /// The block height at the pruning horizon. Typically database backends cannot provide any block data earlier - /// than this point. - /// - /// #Returns - /// - /// * `None`, if the chain is still empty - /// * `h`, the block number of the first block stored in the chain + /// The block height at the pruning horizon, given the chain height of the network. Typically database backends + /// cannot provide any block data earlier than this point. + /// Zero is returned if the blockchain still hasn't reached the pruning horizon. #[inline(always)] - pub fn horizon_block(&self) -> Option { - if self.height_of_longest_chain.is_none() { - return None; - } + pub fn horizon_block(&self, chain_tip: u64) -> u64 { match self.pruning_horizon { - 0 => Some(0u64), - horizon => match self.height_of_longest_chain.unwrap().checked_sub(horizon) { - None => Some(0u64), - Some(v) => Some(v as u64), + 0 => 0, + horizon => match chain_tip.checked_sub(horizon) { + None => 0, + Some(h) => h, }, } } + + /// Set the pruning horizon to indicate that the chain is in archival mode (i.e. a pruning horizon of zero) + pub fn archival_mode(&mut self) { + self.pruning_horizon = 0; + } } impl Default for ChainMetadata { @@ -72,8 +70,53 @@ impl Default for ChainMetadata { ChainMetadata { height_of_longest_chain: None, best_block: None, - total_accumulated_difficulty: 0, pruning_horizon: 2880, } } } + +impl Display for ChainMetadata { + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), Error> { + let height = self.height_of_longest_chain.unwrap_or(0); + let best_block = self + .best_block + .clone() + .map(|b| b.to_hex()) + .unwrap_or_else(|| "Empty Database".into()); + fmt.write_str(&format!("Height of longest chain : {}\n", height))?; + fmt.write_str(&format!("Best_block : {}\n", best_block))?; + fmt.write_str(&format!("Pruning horizon : {}\n", self.pruning_horizon)) + } +} + +#[cfg(test)] +mod test { + use super::ChainMetadata; + + #[test] + fn horizon_block_on_default() { + let metadata = ChainMetadata::default(); + assert_eq!(metadata.horizon_block(0), 0); + } + + #[test] + fn horizon_block() { + let metadata = ChainMetadata::default(); + assert_eq!(metadata.horizon_block(0), 0); + assert_eq!(metadata.horizon_block(100), 0); + assert_eq!(metadata.horizon_block(2880), 0); + assert_eq!(metadata.horizon_block(2881), 1); + } + + #[test] + fn archival_node() { + let mut metadata = ChainMetadata::default(); + metadata.archival_mode(); + // Chain is still empty + assert_eq!(metadata.horizon_block(0), 0); + // When pruning horizon is zero, the horizon block is always 0, the genesis block + assert_eq!(metadata.horizon_block(0), 0); + assert_eq!(metadata.horizon_block(100), 0); + assert_eq!(metadata.horizon_block(2881), 0); + } +} diff --git a/base_layer/core/src/chain_storage/mod.rs b/base_layer/core/src/chain_storage/mod.rs index 5685fe2a7c..6e93442b54 100644 --- a/base_layer/core/src/chain_storage/mod.rs +++ b/base_layer/core/src/chain_storage/mod.rs @@ -30,14 +30,31 @@ mod blockchain_database; mod db_transaction; mod error; mod historical_block; +mod lmdb_db; mod memory_db; mod metadata; -#[cfg(test)] -mod test; + +// public modules +pub mod async_db; // Public API exports -pub use blockchain_database::BlockchainDatabase; -pub use db_transaction::{DbTransaction, MmrTree}; +pub use blockchain_database::{BlockAddResult, BlockchainBackend, BlockchainDatabase, MutableMmrState, Validators}; +pub use db_transaction::{DbKey, DbKeyValuePair, DbTransaction, DbValue, MetadataKey, MetadataValue, MmrTree}; +pub use error::ChainStorageError; pub use historical_block::HistoricalBlock; +pub use lmdb_db::{ + create_lmdb_database, + LMDBDatabase, + LMDB_DB_BLOCK_HASHES, + LMDB_DB_HEADERS, + LMDB_DB_KERNELS, + LMDB_DB_KERNEL_MMR_CP_BACKEND, + LMDB_DB_METADATA, + LMDB_DB_ORPHANS, + LMDB_DB_RANGE_PROOF_MMR_CP_BACKEND, + LMDB_DB_STXOS, + LMDB_DB_UTXOS, + LMDB_DB_UTXO_MMR_CP_BACKEND, +}; pub use memory_db::MemoryDatabase; pub use metadata::ChainMetadata; diff --git a/base_layer/core/src/chain_storage/test/chain_storage.rs b/base_layer/core/src/chain_storage/test/chain_storage.rs deleted file mode 100644 index b1576f0c11..0000000000 --- a/base_layer/core/src/chain_storage/test/chain_storage.rs +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright 2019. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// - -use crate::{ - blocks::{genesis_block::get_genesis_block, Block, BlockHeader}, - chain_storage::{ - blockchain_database::BlockAddResult, - db_transaction::DbKey, - error::ChainStorageError, - BlockchainDatabase, - DbTransaction, - MemoryDatabase, - MmrTree, - }, - tari_amount::MicroTari, - test_utils::builders::{create_test_block, create_test_kernel, create_test_tx, create_utxo}, - types::HashDigest, -}; -use std::thread; -use tari_mmr::MutableMmr; -use tari_utilities::{hex::Hex, Hashable}; - -#[test] -fn fetch_nonexistent_kernel() { - let store = BlockchainDatabase::new(MemoryDatabase::::default()).unwrap(); - let h = vec![0u8; 32]; - assert_eq!( - store.fetch_kernel(h.clone()), - Err(ChainStorageError::ValueNotFound(DbKey::TransactionKernel(h))) - ); -} - -#[test] -fn insert_and_fetch_kernel() { - let mut store = BlockchainDatabase::new(MemoryDatabase::::default()).unwrap(); - let kernel = create_test_kernel(5.into(), 0); - let hash = kernel.hash(); - - let mut txn = DbTransaction::new(); - txn.insert_kernel(kernel.clone()); - assert!(store.commit(txn).is_ok()); - assert_eq!(store.fetch_kernel(hash), Ok(kernel)); -} - -#[test] -fn fetch_nonexistent_header() { - let store = BlockchainDatabase::new(MemoryDatabase::::default()).unwrap(); - assert_eq!( - store.fetch_header(0), - Err(ChainStorageError::ValueNotFound(DbKey::BlockHeader(0))) - ); -} -#[test] -fn insert_and_fetch_header() { - let mut store = BlockchainDatabase::new(MemoryDatabase::::default()).unwrap(); - let mut header = BlockHeader::new(0); - header.height = 42; - - let mut txn = DbTransaction::new(); - txn.insert_header(header.clone()); - assert!(store.commit(txn).is_ok()); - assert_eq!( - store.fetch_header(0), - Err(ChainStorageError::ValueNotFound(DbKey::BlockHeader(0))) - ); - assert_eq!(store.fetch_header(42), Ok(header)); -} - -#[test] -fn insert_and_fetch_utxo() { - let mut store = BlockchainDatabase::new(MemoryDatabase::::default()).unwrap(); - let (utxo, _) = create_utxo(MicroTari(10_000)); - let hash = utxo.hash(); - assert_eq!(store.is_utxo(hash.clone()).unwrap(), false); - let mut txn = DbTransaction::new(); - txn.insert_utxo(utxo.clone()); - assert!(store.commit(txn).is_ok()); - assert_eq!(store.is_utxo(hash.clone()).unwrap(), true); - assert_eq!(store.fetch_utxo(hash), Ok(utxo)); -} - -#[test] -fn insert_and_fetch_orphan() { - let mut store = BlockchainDatabase::new(MemoryDatabase::::default()).unwrap(); - let txs = vec![ - create_test_tx(1000.into(), 10.into(), 0, 2, 1), - create_test_tx(2000.into(), 20.into(), 0, 1, 1), - ]; - let orphan = create_test_block(10, txs); - let orphan_hash = orphan.hash(); - let mut txn = DbTransaction::new(); - txn.insert_orphan(orphan.clone()); - assert!(store.commit(txn).is_ok()); - assert_eq!(store.fetch_orphan(orphan_hash), Ok(orphan)); -} - -#[test] -fn multiple_threads() { - let store = BlockchainDatabase::new(MemoryDatabase::::default()).unwrap(); - // Save a kernel in thread A - let mut store_a = store.clone(); - let a = thread::spawn(move || { - let kernel = create_test_kernel(5.into(), 0); - let hash = kernel.hash(); - let mut txn = DbTransaction::new(); - txn.insert_kernel(kernel.clone()); - assert!(store_a.commit(txn).is_ok()); - hash - }); - // Save a kernel in thread B - let mut store_b = store.clone(); - let b = thread::spawn(move || { - let kernel = create_test_kernel(10.into(), 0); - let hash = kernel.hash(); - let mut txn = DbTransaction::new(); - txn.insert_kernel(kernel.clone()); - assert!(store_b.commit(txn).is_ok()); - hash - }); - let hash_a = a.join().unwrap(); - let hash_b = b.join().unwrap(); - // Get the kernels back - let kernel_a = store.fetch_kernel(hash_a).unwrap(); - assert_eq!(kernel_a.fee, 5.into()); - let kernel_b = store.fetch_kernel(hash_b).unwrap(); - assert_eq!(kernel_b.fee, 10.into()); -} - -#[test] -fn utxo_and_rp_merkle_root() { - let mut store = BlockchainDatabase::new(MemoryDatabase::::default()).unwrap(); - let root = store.fetch_mmr_root(MmrTree::Utxo).unwrap(); - // This is the zero-length MMR of a mutable MMR with Blake256 as hasher - assert_eq!( - &root.to_hex(), - "26146a5435ef15e8cf7dc3354cb7268137e8be211794e93d04551576c6561565" - ); - let (utxo1, _) = create_utxo(MicroTari(10_000)); - let (utxo2, _) = create_utxo(MicroTari(10_000)); - let hash1 = utxo1.hash(); - let hash2 = utxo2.hash(); - // Calculate the Range proof MMR root as a check - let mut rp_mmr_check = MutableMmr::::new(Vec::new()); - assert_eq!(rp_mmr_check.push(&utxo1.proof.hash()).unwrap(), 1); - assert_eq!(rp_mmr_check.push(&utxo2.proof.hash()).unwrap(), 2); - // Store the UTXOs - let mut txn = DbTransaction::new(); - txn.insert_utxo(utxo1); - txn.insert_utxo(utxo2); - assert!(store.commit(txn).is_ok()); - let root = store.fetch_mmr_root(MmrTree::Utxo).unwrap(); - let rp_root = store.fetch_mmr_root(MmrTree::RangeProof).unwrap(); - let mut mmr_check = MutableMmr::::new(Vec::new()); - assert!(mmr_check.push(&hash1).is_ok()); - assert!(mmr_check.push(&hash2).is_ok()); - assert_eq!(root.to_hex(), mmr_check.get_merkle_root().to_hex()); - assert_eq!(rp_root.to_hex(), rp_mmr_check.get_merkle_root().to_hex()); -} - -#[test] -fn header_merkle_root() { - let mut store = BlockchainDatabase::new(MemoryDatabase::::default()).unwrap(); - let root = store.fetch_mmr_root(MmrTree::Header).unwrap(); - // This is the zero-length MMR of a mutable MMR with Blake256 as hasher - assert_eq!( - &root.to_hex(), - "26146a5435ef15e8cf7dc3354cb7268137e8be211794e93d04551576c6561565" - ); - let header1 = BlockHeader::new(0); - let mut header2 = BlockHeader::new(0); - header2.height = 1; - let hash1 = header1.hash(); - let hash2 = header2.hash(); - let mut txn = DbTransaction::new(); - txn.insert_header(header1); - txn.insert_header(header2); - assert!(store.commit(txn).is_ok()); - let root = store.fetch_mmr_root(MmrTree::Header).unwrap(); - let mut mmr_check = MutableMmr::::new(Vec::new()); - assert!(mmr_check.push(&hash1).is_ok()); - assert!(mmr_check.push(&hash2).is_ok()); - assert_eq!(root.to_hex(), mmr_check.get_merkle_root().to_hex()); -} - -#[test] -fn kernel_merkle_root() { - let mut store = BlockchainDatabase::new(MemoryDatabase::::default()).unwrap(); - let root = store.fetch_mmr_root(MmrTree::Kernel).unwrap(); - // This is the zero-length MMR of a mutable MMR with Blake256 as hasher - assert_eq!( - &root.to_hex(), - "26146a5435ef15e8cf7dc3354cb7268137e8be211794e93d04551576c6561565" - ); - let kernel1 = create_test_kernel(100.into(), 0); - let kernel2 = create_test_kernel(200.into(), 0); - let kernel3 = create_test_kernel(300.into(), 0); - let hash1 = kernel1.hash(); - let hash2 = kernel2.hash(); - let hash3 = kernel3.hash(); - let mut txn = DbTransaction::new(); - txn.insert_kernel(kernel1); - txn.insert_kernel(kernel2); - txn.insert_kernel(kernel3); - assert!(store.commit(txn).is_ok()); - let root = store.fetch_mmr_root(MmrTree::Kernel).unwrap(); - let mut mmr_check = MutableMmr::::new(Vec::new()); - assert!(mmr_check.push(&hash1).is_ok()); - assert!(mmr_check.push(&hash2).is_ok()); - assert!(mmr_check.push(&hash3).is_ok()); - assert_eq!(root.to_hex(), mmr_check.get_merkle_root().to_hex()); -} - -#[test] -fn store_and_retrieve_block() { - // Create new database - let mut store = BlockchainDatabase::new(MemoryDatabase::::default()).unwrap(); - let metadata = store.get_metadata().unwrap(); - assert_eq!(metadata.height_of_longest_chain, None); - assert_eq!(metadata.best_block, None); - // Add the Genesis block - let block = get_genesis_block(); - let hash = block.hash(); - assert_eq!(store.add_block(block.clone()), Ok(BlockAddResult::Ok)); - println!("Added genesis block"); - // Check the metadata - let metadata = store.get_metadata().unwrap(); - assert_eq!(metadata.height_of_longest_chain, Some(0)); - assert_eq!(metadata.best_block, Some(hash)); - assert_eq!(metadata.horizon_block(), Some(0)); - // Fetch the block back - println!("Fetching genesis block"); - let block2 = store.fetch_block(0).unwrap(); - println!("Fetched genesis block"); - assert_eq!(block2.confirmations(), 1); - // Compare the blocks - let block2 = Block::from(block2); - assert_eq!(block, block2); -} diff --git a/base_layer/core/src/consensus/consensus_constants.rs b/base_layer/core/src/consensus/consensus_constants.rs new file mode 100644 index 0000000000..1dfe205d63 --- /dev/null +++ b/base_layer/core/src/consensus/consensus_constants.rs @@ -0,0 +1,212 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + consensus::network::Network, + transactions::tari_amount::{uT, MicroTari, T}, +}; +use chrono::{DateTime, Duration, Utc}; +use std::ops::Add; +use tari_crypto::tari_utilities::epoch_time::EpochTime; + +/// This is the inner struct used to control all consensus values. +#[derive(Clone)] +pub struct ConsensusConstants { + /// The min height maturity a coinbase utxo must have + coinbase_lock_height: u64, + /// Current version of the blockchain + blockchain_version: u16, + /// The Future Time Limit (FTL) of the blockchain in seconds. This is the max allowable timestamp that is excepted. + /// We use TxN/20 where T = target time = 60 seconds, and N = block_window = 150 + future_time_limit: u64, + /// This is the our target time in seconds between blocks + target_block_interval: u64, + /// When doing difficulty adjustments and FTL calculations this is the amount of blocks we look at + difficulty_block_window: u64, + /// Maximum transaction weight used for the construction of new blocks. + max_block_transaction_weight: u64, + /// The amount of PoW algorithms used by the Tari chain. + pow_algo_count: u64, + /// This is how many blocks we use to count towards the median timestamp to ensure the block chain moves forward + median_timestamp_count: usize, + /// This is the initial emission curve amount + pub(in crate::consensus) emission_initial: MicroTari, + /// This is the emission curve delay + pub(in crate::consensus) emission_decay: f64, + /// This is the emission curve tail amount + pub(in crate::consensus) emission_tail: MicroTari, +} +// The target time used by the difficulty adjustment algorithms, their target time is the target block interval * PoW +// algorithm count +impl ConsensusConstants { + /// This gets the emission curve values as (initial, decay, tail) + pub fn emission_amounts(&self) -> (MicroTari, f64, MicroTari) { + (self.emission_initial, self.emission_decay, self.emission_tail) + } + + /// The min height maturity a coinbase utxo must have. + pub fn coinbase_lock_height(&self) -> u64 { + self.coinbase_lock_height + } + + /// Current version of the blockchain. + pub fn blockchain_version(&self) -> u16 { + self.blockchain_version + } + + /// This returns the FTL(Future Time Limit) for blocks + /// Any block with a timestamp greater than this is rejected. + pub fn ftl(&self) -> EpochTime { + (Utc::now() + .add(Duration::seconds(self.future_time_limit as i64)) + .timestamp() as u64) + .into() + } + + /// This returns the FTL(Future Time Limit) for blocks + /// Any block with a timestamp greater than this is rejected. + /// This function returns the FTL as a UTC datetime + pub fn ftl_as_time(&self) -> DateTime { + Utc::now().add(Duration::seconds(self.future_time_limit as i64)) + } + + /// This is the our target time in seconds between blocks. + pub fn get_target_block_interval(&self) -> u64 { + self.target_block_interval + } + + /// When doing difficulty adjustments and FTL calculations this is the amount of blocks we look at. + pub fn get_difficulty_block_window(&self) -> u64 { + self.difficulty_block_window + } + + /// Maximum transaction weight used for the construction of new blocks. + pub fn get_max_block_transaction_weight(&self) -> u64 { + self.max_block_transaction_weight + } + + /// The amount of PoW algorithms used by the Tari chain. + pub fn get_pow_algo_count(&self) -> u64 { + self.pow_algo_count + } + + /// The target time used by the difficulty adjustment algorithms, their target time is the target block interval * + /// PoW algorithm count. + pub fn get_diff_target_block_interval(&self) -> u64 { + self.pow_algo_count * self.target_block_interval + } + + /// This is how many blocks we use to count towards the median timestamp to ensure the block chain moves forward. + pub fn get_median_timestamp_count(&self) -> usize { + self.median_timestamp_count + } + + pub fn rincewind() -> Self { + let target_block_interval = 60; + let difficulty_block_window = 150; + ConsensusConstants { + coinbase_lock_height: 1, + blockchain_version: 1, + future_time_limit: target_block_interval * difficulty_block_window / 20, + target_block_interval, + difficulty_block_window, + max_block_transaction_weight: 10000, // TODO: a better weight estimate should be selected + pow_algo_count: 2, + median_timestamp_count: 11, + emission_initial: 5_538_846_115 * uT, + emission_decay: 0.999_999_560_409_038_5, + emission_tail: 1 * T, + } + } + + pub fn localnet() -> Self { + let target_block_interval = 120; + let difficulty_block_window = 90; + ConsensusConstants { + coinbase_lock_height: 1, + blockchain_version: 1, + future_time_limit: target_block_interval * difficulty_block_window / 20, + target_block_interval, + difficulty_block_window, + max_block_transaction_weight: 10000, // TODO: a better weight estimate should be selected + pow_algo_count: 2, + median_timestamp_count: 11, + emission_initial: 10_000_000.into(), + emission_decay: 0.999, + emission_tail: 100.into(), + } + } + + pub fn mainnet() -> Self { + // Note these values are all placeholders for final values + let target_block_interval = 120; + let difficulty_block_window = 90; + ConsensusConstants { + coinbase_lock_height: 1, + blockchain_version: 1, + future_time_limit: target_block_interval * difficulty_block_window / 20, + target_block_interval, + difficulty_block_window, + max_block_transaction_weight: 10000, + pow_algo_count: 2, + median_timestamp_count: 11, + emission_initial: 10_000_000.into(), + emission_decay: 0.999, + emission_tail: 100.into(), + } + } +} + +/// Class to create custom consensus constants +pub struct ConsensusConstantsBuilder { + consensus: ConsensusConstants, +} + +impl ConsensusConstantsBuilder { + pub fn new(network: Network) -> ConsensusConstantsBuilder { + ConsensusConstantsBuilder { + consensus: network.create_consensus_constants(), + } + } + + pub fn with_coinbase_lockheight(mut self, height: u64) -> ConsensusConstantsBuilder { + self.consensus.coinbase_lock_height = height; + self + } + + pub fn with_emission_amounts( + mut self, + intial_amount: MicroTari, + decay: f64, + tail_amount: MicroTari, + ) -> ConsensusConstantsBuilder + { + self.consensus.emission_initial = intial_amount; + self.consensus.emission_decay = decay; + self.consensus.emission_tail = tail_amount; + self + } + + pub fn build(self) -> ConsensusConstants { + self.consensus + } +} diff --git a/base_layer/core/src/consensus/consensus_manager.rs b/base_layer/core/src/consensus/consensus_manager.rs new file mode 100644 index 0000000000..b0e2e849ac --- /dev/null +++ b/base_layer/core/src/consensus/consensus_manager.rs @@ -0,0 +1,275 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + blocks::{ + genesis_block::{ + get_mainnet_block_hash, + get_mainnet_genesis_block, + get_rincewind_block_hash, + get_rincewind_genesis_block, + }, + Block, + }, + chain_storage::{BlockchainBackend, ChainStorageError}, + consensus::{emission::EmissionSchedule, network::Network, ConsensusConstants}, + proof_of_work::{DiffAdjManager, DiffAdjManagerError, Difficulty, DifficultyAdjustmentError, PowAlgorithm}, + transactions::tari_amount::MicroTari, +}; +use derive_error::Error; +use std::sync::{Arc, RwLock, RwLockReadGuard}; +use tari_crypto::tari_utilities::{epoch_time::EpochTime, hash::Hashable}; + +#[derive(Debug, Error, Clone, PartialEq)] +pub enum ConsensusManagerError { + /// Difficulty adjustment encountered an error + DifficultyAdjustmentError(DifficultyAdjustmentError), + /// Difficulty adjustment manager encountered an error + DifficultyAdjustmentManagerError(DiffAdjManagerError), + /// Problem with the DB backend storage + ChainStorageError(ChainStorageError), + /// There is no blockchain to query + EmptyBlockchain, + /// RwLock access broken. + #[error(non_std, no_from)] + PoisonedAccess(String), + /// No Difficulty adjustment manager present + MissingDifficultyAdjustmentManager, +} + +/// This is the consensus manager struct. This manages all state-full consensus code. +/// The inside is wrapped inside of an ARC so that it can safely and cheaply be cloned. +/// The code is multi-thread safe and so only one instance is required. Inner objects are wrapped inside of RwLocks. +pub struct ConsensusManager +where B: BlockchainBackend +{ + inner: Arc>, +} + +impl ConsensusManager +where B: BlockchainBackend +{ + /// Returns the genesis block for the selected network. + pub fn get_genesis_block(&self) -> Block { + match self.inner.network { + Network::MainNet => get_mainnet_genesis_block(), + Network::Rincewind => get_rincewind_genesis_block(), + Network::LocalNet => (self.inner.gen_block.clone().unwrap_or(get_rincewind_genesis_block())), + } + } + + /// Returns the genesis block hash for the selected network. + pub fn get_genesis_block_hash(&self) -> Vec { + match self.inner.network { + Network::MainNet => get_mainnet_block_hash(), + Network::Rincewind => get_rincewind_block_hash(), + Network::LocalNet => (self.inner.gen_block.as_ref().unwrap_or(&get_rincewind_genesis_block())).hash(), + } + } + + /// Get a pointer to the emission schedule + pub fn emission_schedule(&self) -> &EmissionSchedule { + &self.inner.emission + } + + /// Get a pointer to the consensus constants + pub fn consensus_constants(&self) -> &ConsensusConstants { + &self.inner.consensus_constants + } + + /// This moves over a difficulty adjustment manager to the ConsensusManager to control. + pub fn set_diff_manager(&self, diff_manager: DiffAdjManager) -> Result<(), ConsensusManagerError> { + let mut lock = self + .inner + .diff_adj_manager + .write() + .map_err(|e| ConsensusManagerError::PoisonedAccess(e.to_string()))?; + *lock = Some(diff_manager); + Ok(()) + } + + /// This returns the difficulty adjustment manager back. This can safely be cloned as the Difficulty adjustment + /// manager wraps an ARC in side of it. + pub fn get_diff_manager(&self) -> Result, ConsensusManagerError> { + match self.access_diff_adj()?.as_ref() { + Some(v) => Ok(v.clone()), + None => Err(ConsensusManagerError::MissingDifficultyAdjustmentManager), + } + } + + /// Returns the estimated target difficulty for the specified PoW algorithm at the chain tip. + pub fn get_target_difficulty(&self, pow_algo: PowAlgorithm) -> Result { + match self.access_diff_adj()?.as_ref() { + Some(v) => v + .get_target_difficulty(pow_algo) + .map_err(ConsensusManagerError::DifficultyAdjustmentManagerError), + None => Err(ConsensusManagerError::MissingDifficultyAdjustmentManager), + } + } + + /// Returns the estimated target difficulty for the specified PoW algorithm and provided height. + pub fn get_target_difficulty_with_height( + &self, + pow_algo: PowAlgorithm, + height: u64, + ) -> Result + { + match self.access_diff_adj()?.as_ref() { + Some(v) => v + .get_target_difficulty_at_height(pow_algo, height) + .map_err(ConsensusManagerError::DifficultyAdjustmentManagerError), + None => Err(ConsensusManagerError::MissingDifficultyAdjustmentManager), + } + } + + /// Returns the median timestamp of the past 11 blocks at the chain tip. + pub fn get_median_timestamp(&self) -> Result { + match self.access_diff_adj()?.as_ref() { + Some(v) => v + .get_median_timestamp() + .map_err(ConsensusManagerError::DifficultyAdjustmentManagerError), + None => Err(ConsensusManagerError::MissingDifficultyAdjustmentManager), + } + } + + /// Returns the median timestamp of the past 11 blocks at the provided height. + pub fn get_median_timestamp_at_height(&self, height: u64) -> Result { + match self.access_diff_adj()?.as_ref() { + Some(v) => v + .get_median_timestamp_at_height(height) + .map_err(ConsensusManagerError::DifficultyAdjustmentManagerError), + None => Err(ConsensusManagerError::MissingDifficultyAdjustmentManager), + } + } + + /// Creates a total_coinbase offset containing all fees for the validation from block + pub fn calculate_coinbase_and_fees(&self, block: &Block) -> MicroTari { + let coinbase = self.emission_schedule().block_reward(block.header.height); + coinbase + block.calculate_fees() + } + + // Inner helper function to access to the difficulty adjustment manager + fn access_diff_adj(&self) -> Result>>, ConsensusManagerError> { + self.inner + .diff_adj_manager + .read() + .map_err(|e| ConsensusManagerError::PoisonedAccess(e.to_string())) + } + + /// This is the currently configured chain network. + pub fn network(&self) -> Network { + self.inner.network + } +} + +impl Clone for ConsensusManager +where B: BlockchainBackend +{ + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + } + } +} + +/// This is the used to control all consensus values. +struct ConsensusManagerInner +where B: BlockchainBackend +{ + /// Difficulty adjustment manager for the blockchain + pub diff_adj_manager: RwLock>>, + /// This is the inner struct used to control all consensus values. + pub consensus_constants: ConsensusConstants, + + /// The configured chain network. + pub network: Network, + /// The configuration for the emission schedule. + pub emission: EmissionSchedule, + /// This allows the user to set a custom Genesis block + pub gen_block: Option, +} + +/// Constructor for the consensus manager struct +pub struct ConsensusManagerBuilder +where B: BlockchainBackend +{ + /// Difficulty adjustment manager for the blockchain + pub diff_adj_manager: Option>, + /// This is the inner struct used to control all consensus values. + pub consensus_constants: Option, + /// The configured chain network. + pub network: Network, + /// This allows the user to set a custom Genesis block + pub gen_block: Option, +} + +impl ConsensusManagerBuilder +where B: BlockchainBackend +{ + /// Creates a new ConsensusManagerBuilder with the specified network + pub fn new(network: Network) -> Self { + ConsensusManagerBuilder { + diff_adj_manager: None, + consensus_constants: None, + network, + gen_block: None, + } + } + + /// Adds in a custom consensus constants to be used + pub fn with_consensus_constants(mut self, consensus_constants: ConsensusConstants) -> Self { + self.consensus_constants = Some(consensus_constants); + self + } + + /// Adds in a difficulty adjustment manager to be used to be used + pub fn with_difficulty_adjustment_manager(mut self, difficulty_adj: DiffAdjManager) -> Self { + self.diff_adj_manager = Some(difficulty_adj); + self + } + + /// Adds in a custom block to be used. This will be overwritten if the network is anything else than localnet + pub fn with_block(mut self, block: Block) -> Self { + self.gen_block = Some(block); + self + } + + /// Builds a consensus manager + pub fn build(self) -> ConsensusManager { + let consensus_constants = self + .consensus_constants + .unwrap_or(self.network.create_consensus_constants()); + let emission = EmissionSchedule::new( + consensus_constants.emission_initial, + consensus_constants.emission_decay, + consensus_constants.emission_tail, + ); + let inner = ConsensusManagerInner { + diff_adj_manager: RwLock::new(self.diff_adj_manager), + consensus_constants, + network: self.network, + emission, + gen_block: self.gen_block, + }; + ConsensusManager { inner: Arc::new(inner) } + } +} diff --git a/base_layer/core/src/emission.rs b/base_layer/core/src/consensus/emission.rs similarity index 96% rename from base_layer/core/src/emission.rs rename to base_layer/core/src/consensus/emission.rs index 570053f6f0..90bd1a5000 100644 --- a/base_layer/core/src/emission.rs +++ b/base_layer/core/src/consensus/emission.rs @@ -20,7 +20,7 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::tari_amount::MicroTari; +use crate::transactions::tari_amount::MicroTari; /// The Tari emission schedule. The emission schedule determines how much Tari is mined as a block reward at every /// block. @@ -79,8 +79,8 @@ impl EmissionSchedule { /// This is an infinite iterator, and each value returned is a tuple of (block number, reward, and total supply) /// /// ```edition2018 - /// use tari_core::emission::EmissionSchedule; - /// use tari_core::tari_amount::MicroTari; + /// use tari_core::consensus::emission::EmissionSchedule; + /// use tari_core::transactions::tari_amount::MicroTari; /// // Print the reward and supply for first 100 blocks /// let schedule = EmissionSchedule::new(10.into(), 0.9, 1.into()); /// for (n, reward, supply) in schedule.iter().take(100) { @@ -124,8 +124,7 @@ impl<'a> Iterator for EmissionValues<'a> { #[cfg(test)] mod test { - use crate::{emission::EmissionSchedule, tari_amount::MicroTari}; - + use crate::{consensus::emission::EmissionSchedule, transactions::tari_amount::MicroTari}; #[test] fn schedule() { let schedule = EmissionSchedule::new(MicroTari::from(10_000_000), 0.999, MicroTari::from(100)); diff --git a/base_layer/core/src/consensus/mod.rs b/base_layer/core/src/consensus/mod.rs new file mode 100644 index 0000000000..56b752ddf9 --- /dev/null +++ b/base_layer/core/src/consensus/mod.rs @@ -0,0 +1,31 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod consensus_constants; +mod consensus_manager; +mod network; + +pub mod emission; + +pub use consensus_constants::{ConsensusConstants, ConsensusConstantsBuilder}; +pub use consensus_manager::{ConsensusManager, ConsensusManagerBuilder, ConsensusManagerError}; +pub use network::Network; diff --git a/base_layer/core/src/consensus/network.rs b/base_layer/core/src/consensus/network.rs new file mode 100644 index 0000000000..479f3ed6d4 --- /dev/null +++ b/base_layer/core/src/consensus/network.rs @@ -0,0 +1,45 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::consensus_constants::ConsensusConstants; + +/// Specifies the configured chain network. +#[derive(Copy, Clone)] +pub enum Network { + /// Mainnet of Tari, currently should panic if network is set to this. + MainNet, + /// Alpha net version + Rincewind, + /// Local network constants used inside of unit and integration tests. Contains the genesis block to be used for + /// that chain. + LocalNet, +} + +impl Network { + pub fn create_consensus_constants(&self) -> ConsensusConstants { + match self { + Network::MainNet => ConsensusConstants::mainnet(), + Network::Rincewind => ConsensusConstants::rincewind(), + Network::LocalNet => ConsensusConstants::localnet(), + } + } +} diff --git a/base_layer/core/src/consts.rs b/base_layer/core/src/consts.rs deleted file mode 100644 index 2872b123f7..0000000000 --- a/base_layer/core/src/consts.rs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use std::time::Duration; - -/// The maximum number of transactions that can be stored in the Unconfirmed Transaction pool -pub const MEMPOOL_UNCONFIRMED_POOL_STORAGE_CAPACITY: usize = 1000; -/// The maximum number of transactions that can be skipped when compiling a set of highest priority transactions, -/// skipping over large transactions are performed in an attempt to fit more transactions into the remaining space. -pub const MEMPOOL_UNCONFIRMED_POOL_WEIGHT_TRANSACTION_SKIP_COUNT: usize = 20; - -/// The maximum number of transactions that can be stored in the Orphan pool -pub const MEMPOOL_ORPHAN_POOL_STORAGE_CAPACITY: usize = 1000; -/// The time-to-live duration used for transactions stored in the OrphanPool -pub const MEMPOOL_ORPHAN_POOL_CACHE_TTL: Duration = Duration::from_secs(300); - -/// The maximum number of transactions that can be stored in the Pending pool -pub const MEMPOOL_PENDING_POOL_STORAGE_CAPACITY: usize = 1000; - -/// The maximum number of transactions that can be stored in the Reorg pool -pub const MEMPOOL_REORG_POOL_STORAGE_CAPACITY: usize = 1000; -/// The time-to-live duration used for transactions stored in the ReorgPool -pub const MEMPOOL_REORG_POOL_CACHE_TTL: Duration = Duration::from_secs(300); diff --git a/base_layer/core/src/helpers/mock_backend.rs b/base_layer/core/src/helpers/mock_backend.rs new file mode 100644 index 0000000000..4bd90e4fcf --- /dev/null +++ b/base_layer/core/src/helpers/mock_backend.rs @@ -0,0 +1,116 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +use crate::{ + blocks::{Block, BlockHeader}, + chain_storage::{BlockchainBackend, ChainStorageError, DbKey, DbTransaction, DbValue, MmrTree}, + transactions::{ + transaction::{TransactionKernel, TransactionOutput}, + types::HashOutput, + }, +}; +use tari_mmr::{Hash, MerkleCheckPoint, MerkleProof}; + +// This is a test backend. This is used so that the ConsensusManager can be called without actually having a backend. +// Calling this backend will result in a panic. +pub struct MockBackend; + +impl BlockchainBackend for MockBackend { + fn write(&self, _tx: DbTransaction) -> Result<(), ChainStorageError> { + unimplemented!() + } + + fn fetch(&self, _key: &DbKey) -> Result, ChainStorageError> { + unimplemented!() + } + + fn contains(&self, _key: &DbKey) -> Result { + unimplemented!() + } + + fn fetch_mmr_root(&self, _tree: MmrTree) -> Result { + unimplemented!() + } + + fn fetch_mmr_only_root(&self, _tree: MmrTree) -> Result { + unimplemented!() + } + + fn calculate_mmr_root( + &self, + _tree: MmrTree, + _additions: Vec, + _deletions: Vec, + ) -> Result + { + unimplemented!() + } + + fn fetch_mmr_proof(&self, _tree: MmrTree, _pos: usize) -> Result { + unimplemented!() + } + + fn fetch_checkpoint(&self, _tree: MmrTree, _index: u64) -> Result { + unimplemented!() + } + + fn fetch_mmr_node(&self, _tree: MmrTree, _pos: u32) -> Result<(Hash, bool), ChainStorageError> { + unimplemented!() + } + + fn for_each_orphan(&self, _f: F) -> Result<(), ChainStorageError> + where + Self: Sized, + F: FnMut(Result<(HashOutput, Block), ChainStorageError>), + { + unimplemented!() + } + + fn for_each_kernel(&self, _f: F) -> Result<(), ChainStorageError> + where + Self: Sized, + F: FnMut(Result<(HashOutput, TransactionKernel), ChainStorageError>), + { + unimplemented!() + } + + fn for_each_header(&self, _f: F) -> Result<(), ChainStorageError> + where + Self: Sized, + F: FnMut(Result<(u64, BlockHeader), ChainStorageError>), + { + unimplemented!() + } + + fn for_each_utxo(&self, _f: F) -> Result<(), ChainStorageError> + where + Self: Sized, + F: FnMut(Result<(HashOutput, TransactionOutput), ChainStorageError>), + { + unimplemented!() + } + + fn fetch_last_header(&self) -> Result, ChainStorageError> { + unimplemented!() + } +} diff --git a/infrastructure/crypto/src/range_proof.rs b/base_layer/core/src/helpers/mod.rs similarity index 51% rename from infrastructure/crypto/src/range_proof.rs rename to base_layer/core/src/helpers/mod.rs index f6326975c4..75ad729b7d 100644 --- a/infrastructure/crypto/src/range_proof.rs +++ b/base_layer/core/src/helpers/mod.rs @@ -19,38 +19,44 @@ // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Portions of this file were originally copyrighted (c) 2018 The Grin Developers, issued under the Apache License, -// Version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0. + +//! Common test helper functions that are small and useful enough to be included in the main crate, rather than the +//! integration test folder. + +mod mock_backend; use crate::{ - commitment::HomomorphicCommitment, - keys::{PublicKey, SecretKey}, + blocks::{Block, BlockBuilder, BlockHeader}, + chain_storage::{BlockchainDatabase, MemoryDatabase, Validators}, + consensus::{ConsensusConstants, ConsensusManager}, + transactions::{transaction::Transaction, types::HashDigest}, + validation::mocks::MockValidator, }; -use derive_error::Error; -use serde::{Deserialize, Serialize}; -#[derive(Debug, Clone, Error, PartialEq, Deserialize, Serialize)] -pub enum RangeProofError { - /// Could not construct range proof - ProofConstructionError, - /// The deserialization of the range proof failed - InvalidProof, - /// Invalid input was provided to the RangeProofService constructor - InitializationError, -} - -pub trait RangeProofService { - type P: Sized; - type K: SecretKey; - type PK: PublicKey; +pub use mock_backend::MockBackend; - /// Construct a new range proof for the given secret key and value. The resulting proof will be sufficient - /// evidence that the prover knows the secret key and value, and that the value lies in the range determined by - /// the service. - fn construct_proof(&self, key: &Self::K, value: u64) -> Result; +/// Create a partially constructed block using the provided set of transactions +/// is chain_block, or rename it to `create_orphan_block` and drop the prev_block argument +pub fn create_orphan_block( + block_height: u64, + transactions: Vec, + consensus_constants: &ConsensusConstants, +) -> Block +{ + let mut header = BlockHeader::new(0); + header.height = block_height; + BlockBuilder::new(consensus_constants) + .with_header(header) + .with_transactions(transactions) + .build() +} - /// Verify the range proof against the given commitment. If this function returns true, it attests to the - /// commitment having a value in the range [0; 2^64-1] and that the prover knew both the value and private key. - fn verify(&self, proof: &Self::P, commitment: &HomomorphicCommitment) -> bool; +pub fn create_mem_db( + consensus_manager: ConsensusManager>, +) -> BlockchainDatabase> { + let validators = Validators::new(MockValidator::new(true), MockValidator::new(true)); + let db = MemoryDatabase::::default(); + let mut db = BlockchainDatabase::new(db, consensus_manager).unwrap(); + db.set_validators(validators); + db } diff --git a/base_layer/core/src/lib.rs b/base_layer/core/src/lib.rs index 219ec6cda4..ead4c3d11d 100644 --- a/base_layer/core/src/lib.rs +++ b/base_layer/core/src/lib.rs @@ -20,33 +20,44 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// Needed to make futures::select! work +#![recursion_limit = "1024"] +// Used to eliminate the need for boxing futures in many cases. +// Tracking issue: https://github.com/rust-lang/rust/issues/63063 +#![feature(type_alias_impl_trait)] +// Enable usage of Vec::shrink_to +#![feature(shrink_to)] + #[macro_use] extern crate bitflags; #[macro_use] -extern crate lazy_static; - -#[cfg(test)] -pub mod test_utils; +extern crate cfg_if; -pub mod blocks; -pub mod bullet_rangeproofs; -pub mod consts; -pub mod fee; -pub mod mempool; -pub mod proof_of_work; -#[allow(clippy::op_ref)] -pub mod transaction; -pub mod transaction_protocol; -pub mod types; +cfg_if! { + if #[cfg(feature = "base_node")] { + pub mod blocks; + pub mod chain_storage; + pub mod consensus; + pub mod helpers; + pub mod mining; + pub mod proof_of_work; + pub mod validation; + } +} -pub mod consensus; -pub mod emission; -pub mod tari_amount; +cfg_if! { + if #[cfg(any(feature = "base_node", feature = "base_node_proto"))] { + pub mod base_node; + pub mod proto; + } +} -mod base_node; -// mod blockchain; TODO refactoring +#[cfg(any(feature = "base_node", feature = "mempool_proto"))] +pub mod mempool; -pub mod chain_storage; +#[cfg(feature = "transactions")] +pub mod transactions; -// Re-export commonly used structs -pub use transaction_protocol::{recipient::ReceiverTransactionProtocol, sender::SenderTransactionProtocol}; +// Re-export the crypto crate to make exposing traits etc easier for clients of this crate +pub use crypto::tari_utilities; +pub use tari_crypto as crypto; diff --git a/base_layer/core/src/mempool/config.rs b/base_layer/core/src/mempool/config.rs new file mode 100644 index 0000000000..98846a9cf1 --- /dev/null +++ b/base_layer/core/src/mempool/config.rs @@ -0,0 +1,177 @@ +// Copyright 2020. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::mempool::{ + consts, + orphan_pool::OrphanPoolConfig, + pending_pool::PendingPoolConfig, + reorg_pool::ReorgPoolConfig, + unconfirmed_pool::UnconfirmedPoolConfig, +}; +use bitflags::_core::time::Duration; +use config::Config; +use tari_common::{ConfigExtractor, ConfigurationError, Network}; + +/// Configuration for the Mempool. +#[derive(Clone, Copy)] +pub struct MempoolConfig { + pub unconfirmed_pool_config: UnconfirmedPoolConfig, + pub orphan_pool_config: OrphanPoolConfig, + pub pending_pool_config: PendingPoolConfig, + pub reorg_pool_config: ReorgPoolConfig, +} + +impl Default for MempoolConfig { + fn default() -> Self { + Self { + unconfirmed_pool_config: UnconfirmedPoolConfig::default(), + orphan_pool_config: OrphanPoolConfig::default(), + pending_pool_config: PendingPoolConfig::default(), + reorg_pool_config: ReorgPoolConfig::default(), + } + } +} + +impl ConfigExtractor for MempoolConfig { + fn set_default(cfg: &mut Config) { + let default = MempoolConfig::default(); + for network in &["testnet", "mainnet"] { + cfg.set_default( + &format!("mempool.{}.unconfirmed_pool_storage_capacity", network), + default.unconfirmed_pool_config.storage_capacity as i64, + ) + .unwrap(); + cfg.set_default( + &format!("mempool.{}.weight_tx_skip_count", network), + default.unconfirmed_pool_config.weight_tx_skip_count as i64, + ) + .unwrap(); + cfg.set_default( + &format!("mempool.{}.orphan_pool_storage_capacity", network), + default.orphan_pool_config.storage_capacity as i64, + ) + .unwrap(); + cfg.set_default( + &format!("mempool.{}.orphan_tx_ttl", network), + default.orphan_pool_config.tx_ttl.as_secs() as i64, + ) + .unwrap(); + cfg.set_default( + &format!("mempool.{}.pending_pool_storage_capacity", network), + default.pending_pool_config.storage_capacity as i64, + ) + .unwrap(); + cfg.set_default( + &format!("mempool.{}.reorg_pool_storage_capacity", network), + default.reorg_pool_config.storage_capacity as i64, + ) + .unwrap(); + cfg.set_default( + &format!("mempool.{}.reorg_tx_ttl", network), + default.reorg_pool_config.tx_ttl.as_secs() as i64, + ) + .unwrap(); + } + } + + fn extract_configuration(cfg: &Config, network: Network) -> Result + where Self: Sized { + let mut config = MempoolConfig::default(); + let key = format!("mempool.{}.unconfirmed_pool_storage_capacity", network); + let val = cfg + .get_int(&key) + .map_err(|e| ConfigurationError::new(&key, &e.to_string()))? as usize; + config.unconfirmed_pool_config.storage_capacity = val; + let key = format!("mempool.{}.weight_tx_skip_count", network); + let val = cfg + .get_int(&key) + .map_err(|e| ConfigurationError::new(&key, &e.to_string()))? as usize; + config.unconfirmed_pool_config.weight_tx_skip_count = val; + let key = format!("mempool.{}.orphan_pool_storage_capacity", network); + let val = cfg + .get_int(&key) + .map_err(|e| ConfigurationError::new(&key, &e.to_string()))? as usize; + config.orphan_pool_config.storage_capacity = val; + let key = format!("mempool.{}.orphan_tx_ttl", network); + let val = cfg + .get_int(&key) + .map_err(|e| ConfigurationError::new(&key, &e.to_string()))? as u64; + config.orphan_pool_config.tx_ttl = Duration::from_secs(val); + let key = format!("mempool.{}.pending_pool_storage_capacity", network); + let val = cfg + .get_int(&key) + .map_err(|e| ConfigurationError::new(&key, &e.to_string()))? as usize; + config.pending_pool_config.storage_capacity = val; + let key = format!("mempool.{}.reorg_pool_storage_capacity", network); + let val = cfg + .get_int(&key) + .map_err(|e| ConfigurationError::new(&key, &e.to_string()))? as usize; + config.reorg_pool_config.storage_capacity = val; + let key = format!("mempool.{}.reorg_tx_ttl", network); + let val = cfg + .get_int(&key) + .map_err(|e| ConfigurationError::new(&key, &e.to_string()))? as u64; + config.reorg_pool_config.tx_ttl = Duration::from_secs(val); + Ok(config) + } +} + +/// Configuration for the MempoolService. +#[derive(Clone, Copy)] +pub struct MempoolServiceConfig { + /// The allocated waiting time for a request waiting for service responses from the Mempools of remote Base nodes. + pub request_timeout: Duration, +} + +impl Default for MempoolServiceConfig { + fn default() -> Self { + Self { + request_timeout: consts::MEMPOOL_SERVICE_REQUEST_TIMEOUT, + } + } +} + +impl ConfigExtractor for MempoolServiceConfig { + fn set_default(cfg: &mut Config) { + let service_default = MempoolServiceConfig::default(); + for network in &["testnet", "mainnet"] { + let key = format!("mempool.{}.request_timeout", network); + cfg.set_default(&key, service_default.request_timeout.as_secs() as i64) + .unwrap(); + } + } + + fn extract_configuration(cfg: &Config, network: Network) -> Result + where Self: Sized { + let mut config = MempoolServiceConfig::default(); + let key = config_string(network, "request_timeout"); + let val = cfg + .get_int(&key) + .map_err(|e| ConfigurationError::new(&key, &e.to_string()))?; + config.request_timeout = Duration::from_secs(val as u64); + Ok(config) + } +} + +fn config_string(network: Network, key: &str) -> String { + format!("mempool.{}.{}", network, key) +} diff --git a/base_layer/core/src/mempool/consts.rs b/base_layer/core/src/mempool/consts.rs new file mode 100644 index 0000000000..ea8c2b7125 --- /dev/null +++ b/base_layer/core/src/mempool/consts.rs @@ -0,0 +1,46 @@ +// Copyright 2020. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +use std::time::Duration; + +/// The maximum number of transactions that can be stored in the Unconfirmed Transaction pool +pub const MEMPOOL_UNCONFIRMED_POOL_STORAGE_CAPACITY: usize = 40_000; +/// The maximum number of transactions that can be skipped when compiling a set of highest priority transactions, +/// skipping over large transactions are performed in an attempt to fit more transactions into the remaining space. +pub const MEMPOOL_UNCONFIRMED_POOL_WEIGHT_TRANSACTION_SKIP_COUNT: usize = 20; + +/// The maximum number of transactions that can be stored in the Orphan pool +pub const MEMPOOL_ORPHAN_POOL_STORAGE_CAPACITY: usize = 250; +/// The time-to-live duration used for transactions stored in the OrphanPool +pub const MEMPOOL_ORPHAN_POOL_CACHE_TTL: Duration = Duration::from_secs(300); + +/// The maximum number of transactions that can be stored in the Pending pool +pub const MEMPOOL_PENDING_POOL_STORAGE_CAPACITY: usize = 5_000; + +/// The maximum number of transactions that can be stored in the Reorg pool +pub const MEMPOOL_REORG_POOL_STORAGE_CAPACITY: usize = 5_000; +/// The time-to-live duration used for transactions stored in the ReorgPool +pub const MEMPOOL_REORG_POOL_CACHE_TTL: Duration = Duration::from_secs(300); + +/// The allocated waiting time for a request waiting for service responses from the mempools of remote base nodes. +pub const MEMPOOL_SERVICE_REQUEST_TIMEOUT: Duration = Duration::from_secs(60); diff --git a/base_layer/core/src/mempool/error.rs b/base_layer/core/src/mempool/error.rs index bfd2b71081..506b3d7a9a 100644 --- a/base_layer/core/src/mempool/error.rs +++ b/base_layer/core/src/mempool/error.rs @@ -20,10 +20,27 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::mempool::unconfirmed_pool::UnconfirmedPoolError; +use crate::{ + chain_storage::ChainStorageError, + mempool::{ + orphan_pool::OrphanPoolError, + pending_pool::PendingPoolError, + reorg_pool::ReorgPoolError, + unconfirmed_pool::UnconfirmedPoolError, + }, + transactions::transaction::TransactionError, +}; use derive_error::Error; #[derive(Debug, Error)] pub enum MempoolError { UnconfirmedPoolError(UnconfirmedPoolError), + OrphanPoolError(OrphanPoolError), + PendingPoolError(PendingPoolError), + ReorgPoolError(ReorgPoolError), + TransactionError(TransactionError), + ChainStorageError(ChainStorageError), + /// The Blockchain height is undefined + ChainHeightUndefined, + ValidationError, } diff --git a/base_layer/core/src/mempool/mempool.rs b/base_layer/core/src/mempool/mempool.rs index a7c0ba03bf..ae4b25900f 100644 --- a/base_layer/core/src/mempool/mempool.rs +++ b/base_layer/core/src/mempool/mempool.rs @@ -22,107 +22,126 @@ use crate::{ blocks::Block, + chain_storage::{BlockchainBackend, BlockchainDatabase}, mempool::{ error::MempoolError, - orphan_pool::{OrphanPool, OrphanPoolConfig}, - pending_pool::{PendingPool, PendingPoolConfig}, - reorg_pool::{ReorgPool, ReorgPoolConfig}, - unconfirmed_pool::{UnconfirmedPool, UnconfirmedPoolConfig}, + orphan_pool::OrphanPool, + pending_pool::PendingPool, + reorg_pool::ReorgPool, + unconfirmed_pool::UnconfirmedPool, + MempoolConfig, + StatsResponse, + TxStorageResponse, }, - transaction::Transaction, - types::Signature, + transactions::{transaction::Transaction, types::Signature}, + validation::{Validation, ValidationError, Validator}, }; +use log::*; use std::sync::Arc; -/// Configuration for the Mempool -#[derive(Clone, Copy)] -pub struct MempoolConfig { - pub unconfirmed_pool_config: UnconfirmedPoolConfig, - pub orphan_pool_config: OrphanPoolConfig, - pub pending_pool_config: PendingPoolConfig, - pub reorg_pool_config: ReorgPoolConfig, +pub const LOG_TARGET: &str = "c::mp::mempool"; + +/// Struct containing the validators the mempool needs to run, It forces the correct amount of validators are given +pub struct MempoolValidators { + mempool: Box>, + orphan: Box>, } -impl Default for MempoolConfig { - fn default() -> Self { +impl MempoolValidators { + pub fn new( + mempool: impl Validation + 'static, + orphan: impl Validation + 'static, + ) -> Self + { Self { - unconfirmed_pool_config: UnconfirmedPoolConfig::default(), - orphan_pool_config: OrphanPoolConfig::default(), - pending_pool_config: PendingPoolConfig::default(), - reorg_pool_config: ReorgPoolConfig::default(), + mempool: Box::new(mempool), + orphan: Box::new(orphan), } } + + pub fn into_validators(self) -> (Box>, Box>) { + (self.mempool, self.orphan) + } } /// The Mempool consists of an Unconfirmed Transaction Pool, Pending Pool, Orphan Pool and Reorg Pool and is responsible /// for managing and maintaining all unconfirmed transactions have not yet been included in a block, and transactions /// that have recently been included in a block. -pub struct Mempool { +pub struct Mempool +where T: BlockchainBackend +{ + blockchain_db: BlockchainDatabase, unconfirmed_pool: UnconfirmedPool, - orphan_pool: OrphanPool, + orphan_pool: OrphanPool, pending_pool: PendingPool, reorg_pool: ReorgPool, + validator: Arc>, } -impl Mempool { - /// Create a new Mempool with a UnconfirmedPool, OrphanPool, PendingPool and ReOrgPool - pub fn new(config: MempoolConfig) -> Self { +impl Mempool +where T: BlockchainBackend +{ + /// Create a new Mempool with an UnconfirmedPool, OrphanPool, PendingPool and ReOrgPool. + pub fn new(blockchain_db: BlockchainDatabase, config: MempoolConfig, validators: MempoolValidators) -> Self { + let (mempool_validator, orphan_validator) = validators.into_validators(); Self { unconfirmed_pool: UnconfirmedPool::new(config.unconfirmed_pool_config), - orphan_pool: OrphanPool::new(config.orphan_pool_config), + orphan_pool: OrphanPool::new(config.orphan_pool_config, orphan_validator), pending_pool: PendingPool::new(config.pending_pool_config), reorg_pool: ReorgPool::new(config.reorg_pool_config), + blockchain_db, + validator: Arc::new(mempool_validator), } } - /// Insert an unconfirmed transaction into the Mempool - pub fn insert(&mut self, _utx: Transaction) -> Result<(), MempoolError> { - // TODO: Verify incoming txs and check for timelocks and that valid UTXOs are spent - - // TODO: UTxs that have passed all the verification steps and checks, except they attempt to spend UTXOs that - // don't exist should be added to Orphan Pool. - - // TODO: UTxs constrained by timelocks and attempt to spend nonexistent UTXOs should be added to orphan pool. - - // TODO: UTxs that have passed all the verification steps and checks, Time-locked utxs should be added to - // Pending Pool - - // TODO: Utxs that have been received, verified and have passed all checks, don't have time-locks and only spend - // valid UTXOs should be added to UTxPool. + /// Insert an unconfirmed transaction into the Mempool. The transaction *MUST* have passed through the validation + /// pipeline already and will thus always be internally consistent by this stage + pub fn insert(&self, tx: Arc) -> Result<(), MempoolError> { + // The transaction is already internally consistent + match self.validator.validate(&tx) { + Ok(()) => self.unconfirmed_pool.insert(tx)?, + Err(ValidationError::UnknownInputs) => self.orphan_pool.insert(tx)?, + Err(ValidationError::MaturityError) => self.pending_pool.insert(tx)?, + _ => return Err(MempoolError::ValidationError), + }; Ok(()) } - /// Insert a set of new transactions into the UTxPool - fn insert_txs(&mut self, txs: Vec) -> Result<(), MempoolError> { + /// Insert a set of new transactions into the UTxPool. + fn insert_txs(&self, txs: Vec>) -> Result<(), MempoolError> { for tx in txs { self.insert(tx)?; } - Ok(()) } - /// Update the Mempool based on the received published block - pub fn process_published_block(&mut self, _published_block: &Block) -> Result<(), MempoolError> { + /// Update the Mempool based on the received published block. + pub fn process_published_block(&self, published_block: &Block) -> Result<(), MempoolError> { + trace!(target: LOG_TARGET, "Mempool processing new block: {}", published_block); // Move published txs to ReOrgPool and discard double spends - // self.reorg_pool.insert_txs(self.unconfirmed_pool.remove_published_and_discard_double_spends(published_block)? - // )?; + self.reorg_pool.insert_txs( + self.unconfirmed_pool + .remove_published_and_discard_double_spends(published_block)?, + )?; // Move txs with valid input UTXOs and expired time-locks to UnconfirmedPool and discard double spends - // unconfirmed_pool.insert_txs(pending_pool.remove_unlocked_and_discard_double_spends()?)?; - - // Move Time-locked txs that have input UTXOs that have recently become valid to PendingPool. Move txs with no - // or recently expired time-locks that have input UTXOs that have recently become valid to the UnconfirmedPool - // let (txs,time_locked_txs)=orphan_pool.remove_valid(published_block.header.height,utxos)?; - // pending_pool.insert_txs(time_locked_txs)?; - // unconfirmed_pool.insert_txs(txs)?; - - // Txs stored in the OrphanPool and ReOrgPool will be removed when their TTLs have been reached. + self.unconfirmed_pool.insert_txs( + self.pending_pool + .remove_unlocked_and_discard_double_spends(published_block)?, + )?; + + // Move txs with recently expired time-locks that have input UTXOs that have recently become valid to the + // UnconfirmedPool + let (txs, time_locked_txs) = self.orphan_pool.scan_for_and_remove_unorphaned_txs()?; + self.unconfirmed_pool.insert_txs(txs)?; + // Move Time-locked txs that have input UTXOs that have recently become valid to PendingPool. + self.pending_pool.insert_txs(time_locked_txs)?; Ok(()) } - /// Update the Mempool based on the received set of published blocks - pub fn process_published_blocks(&mut self, published_blocks: &Vec) -> Result<(), MempoolError> { + /// Update the Mempool based on the received set of published blocks. + pub fn process_published_blocks(&self, published_blocks: &[Block]) -> Result<(), MempoolError> { for published_block in published_blocks { self.process_published_block(published_block)?; } @@ -130,40 +149,99 @@ impl Mempool { } /// In the event of a ReOrg, resubmit all ReOrged transactions into the Mempool and process each newly introduced - /// block from the latest longest chain - pub fn process_reorg(&mut self, _removed_blocks: Vec, _new_blocks: Vec) -> Result<(), MempoolError> { - // let reorg_txs=self.reorg_pool.scan_for_and_remove_reorged_txs(removed_blocks); - // self.insert_txs(reorg_txs)?; - // self.process_published_blocks(&new_blocks)?; + /// block from the latest longest chain. + pub fn process_reorg(&self, removed_blocks: Vec, new_blocks: Vec) -> Result<(), MempoolError> { + debug!(target: LOG_TARGET, "Mempool processing reorg"); + for block in &removed_blocks { + trace!(target: LOG_TARGET, "Mempool processing reorg removed block: {}", block); + } + for block in &new_blocks { + trace!( + target: LOG_TARGET, + "Mempool processing reorg added new block: {}", + block + ); + } + + self.insert_txs( + self.reorg_pool + .remove_reorged_txs_and_discard_double_spends(removed_blocks, &new_blocks)?, + )?; + self.process_published_blocks(&new_blocks)?; Ok(()) } /// Returns all unconfirmed transaction stored in the Mempool, except the transactions stored in the ReOrgPool. // TODO: Investigate returning an iterator rather than a large vector of transactions pub fn snapshot(&self) -> Result>, MempoolError> { - // return content of UnconfirmedPool, OrphanPool and PendingPool - - Ok(Vec::new()) + let mut txs = self.unconfirmed_pool.snapshot()?; + txs.append(&mut self.orphan_pool.snapshot()?); + txs.append(&mut self.pending_pool.snapshot()?); + Ok(txs) } /// Returns a list of transaction ranked by transaction priority up to a given weight. - pub fn retrieve(&self, _total_weight: usize) -> Result>, MempoolError> { - Ok(Vec::new()) + pub fn retrieve(&self, total_weight: u64) -> Result>, MempoolError> { + Ok(self.unconfirmed_pool.highest_priority_txs(total_weight)?) } /// Check if the specified transaction is stored in the Mempool. - pub fn has_tx_with_excess_sig(&self, _excess_sig: &Signature) -> Result<(), MempoolError> { - // Return Some(Sub-pool enum) or None when it is not stored in the Mempool + pub fn has_tx_with_excess_sig(&self, excess_sig: &Signature) -> Result { + if self.unconfirmed_pool.has_tx_with_excess_sig(excess_sig)? { + Ok(TxStorageResponse::UnconfirmedPool) + } else if self.orphan_pool.has_tx_with_excess_sig(excess_sig)? { + Ok(TxStorageResponse::OrphanPool) + } else if self.pending_pool.has_tx_with_excess_sig(excess_sig)? { + Ok(TxStorageResponse::PendingPool) + } else if self.reorg_pool.has_tx_with_excess_sig(excess_sig)? { + Ok(TxStorageResponse::ReorgPool) + } else { + Ok(TxStorageResponse::NotStored) + } + } - Ok(()) + // Returns the total number of transactions in the Mempool. + fn len(&self) -> Result { + Ok( + self.unconfirmed_pool.len()? + + self.orphan_pool.len()? + + self.pending_pool.len()? + + self.reorg_pool.len()?, + ) } - /// Returns the Mempool stats for the Mempool - pub fn stats(&self) -> Result<(), MempoolError> { - // Return the stats of the Mempool, including subpools (OrphanPool, PendingPool and ReOrgPool). The number of - // unconfirmed transactions. The number of orphaned transactions. The current size of the mempool (in - // transaction weight). + // Returns the total weight of all transactions stored in the Mempool. + fn calculate_weight(&self) -> Result { + Ok(self.unconfirmed_pool.calculate_weight()? + + self.orphan_pool.calculate_weight()? + + self.pending_pool.calculate_weight()? + + self.reorg_pool.calculate_weight()?) + } - Ok(()) + /// Gathers and returns the stats of the Mempool. + pub fn stats(&self) -> Result { + Ok(StatsResponse { + total_txs: self.len()?, + unconfirmed_txs: self.unconfirmed_pool.len()?, + orphan_txs: self.orphan_pool.len()?, + timelocked_txs: self.pending_pool.len()?, + published_txs: self.reorg_pool.len()?, + total_weight: self.calculate_weight()?, + }) + } +} + +impl Clone for Mempool +where T: BlockchainBackend +{ + fn clone(&self) -> Self { + Mempool { + blockchain_db: self.blockchain_db.clone(), + unconfirmed_pool: self.unconfirmed_pool.clone(), + orphan_pool: self.orphan_pool.clone(), + pending_pool: self.pending_pool.clone(), + reorg_pool: self.reorg_pool.clone(), + validator: self.validator.clone(), + } } } diff --git a/base_layer/core/src/mempool/mod.rs b/base_layer/core/src/mempool/mod.rs index 3c5a2e58ea..64dc6a302b 100644 --- a/base_layer/core/src/mempool/mod.rs +++ b/base_layer/core/src/mempool/mod.rs @@ -20,14 +20,48 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -mod error; -mod mempool; -mod orphan_pool; -mod pending_pool; -mod priority; -mod reorg_pool; -mod unconfirmed_pool; +cfg_if! { + if #[cfg(feature = "base_node")] { + mod config; + mod consts; + mod error; + mod mempool; + mod orphan_pool; + mod pending_pool; + mod priority; + mod reorg_pool; + mod unconfirmed_pool; + // Public re-exports + pub use self::config::{MempoolConfig, MempoolServiceConfig}; + pub use error::MempoolError; + pub use mempool::{Mempool, MempoolValidators}; + pub use service::{MempoolServiceError, MempoolServiceInitializer, OutboundMempoolServiceInterface}; + } +} -// Public re-exports -pub use error::MempoolError; -pub use mempool::Mempool; +cfg_if! { + if #[cfg(any(feature = "base_node", feature = "mempool_proto"))] { + pub mod proto; + pub mod service; + } +} +use serde::{Deserialize, Serialize}; + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub struct StatsResponse { + pub total_txs: usize, + pub unconfirmed_txs: usize, + pub orphan_txs: usize, + pub timelocked_txs: usize, + pub published_txs: usize, + pub total_weight: u64, +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +pub enum TxStorageResponse { + UnconfirmedPool, + OrphanPool, + PendingPool, + ReorgPool, + NotStored, +} diff --git a/infrastructure/tari_util/src/thread_join/error.rs b/base_layer/core/src/mempool/orphan_pool/error.rs similarity index 82% rename from infrastructure/tari_util/src/thread_join/error.rs rename to base_layer/core/src/mempool/orphan_pool/error.rs index c34ca7178e..5a8f73bb5e 100644 --- a/infrastructure/tari_util/src/thread_join/error.rs +++ b/base_layer/core/src/mempool/orphan_pool/error.rs @@ -20,14 +20,15 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::chain_storage::ChainStorageError; use derive_error::Error; -#[derive(Debug, Error, PartialEq, Clone)] -pub enum ThreadError { - /// An error occurred attempting to join the thread - JoinError, - /// The timeout period allocated to the thread joining process has been exceeded - TimeoutReached, - /// The channel has disconnected between the host and the join thread - ChannelDisconnected, +#[derive(Debug, Error)] +pub enum OrphanPoolError { + /// A problem has been encountered with the storage backend. + #[error(non_std, no_from)] + BackendError(String), + ChainStorageError(ChainStorageError), + /// The Blockchain height is undefined + ChainHeightUndefined, } diff --git a/base_layer/core/src/mempool/orphan_pool/mod.rs b/base_layer/core/src/mempool/orphan_pool/mod.rs index 1304d842de..a693315aac 100644 --- a/base_layer/core/src/mempool/orphan_pool/mod.rs +++ b/base_layer/core/src/mempool/orphan_pool/mod.rs @@ -20,7 +20,11 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +mod error; mod orphan_pool; +mod orphan_pool_storage; // Public re-exports +pub use error::OrphanPoolError; pub use orphan_pool::{OrphanPool, OrphanPoolConfig}; +pub use orphan_pool_storage::OrphanPoolStorage; diff --git a/base_layer/core/src/mempool/orphan_pool/orphan_pool.rs b/base_layer/core/src/mempool/orphan_pool/orphan_pool.rs index 127261a188..121117db1a 100644 --- a/base_layer/core/src/mempool/orphan_pool/orphan_pool.rs +++ b/base_layer/core/src/mempool/orphan_pool/orphan_pool.rs @@ -21,12 +21,18 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::{ - consts::{MEMPOOL_ORPHAN_POOL_CACHE_TTL, MEMPOOL_ORPHAN_POOL_STORAGE_CAPACITY}, - transaction::{Transaction, TransactionInput}, - types::Signature, + chain_storage::BlockchainBackend, + mempool::{ + consts::{MEMPOOL_ORPHAN_POOL_CACHE_TTL, MEMPOOL_ORPHAN_POOL_STORAGE_CAPACITY}, + orphan_pool::{error::OrphanPoolError, orphan_pool_storage::OrphanPoolStorage}, + }, + transactions::{transaction::Transaction, types::Signature}, + validation::Validator, +}; +use std::{ + sync::{Arc, RwLock}, + time::Duration, }; -use std::{sync::Arc, time::Duration}; -use ttl_cache::TtlCache; /// Configuration for the OrphanPool #[derive(Clone, Copy)] @@ -49,192 +55,205 @@ impl Default for OrphanPoolConfig { /// The Orphan Pool contains all the received transactions that attempt to spend UTXOs that don't exist. These UTXOs /// might exist in the future if these transactions are from a series or set of transactions that need to be processed /// in a specific order. Some of these transactions might still be constrained by pending time-locks. -pub struct OrphanPool { - config: OrphanPoolConfig, - txs_by_signature: TtlCache>, +pub struct OrphanPool +where T: BlockchainBackend +{ + pool_storage: Arc>>, } -impl OrphanPool { +impl OrphanPool +where T: BlockchainBackend +{ /// Create a new OrphanPool with the specified configuration - pub fn new(config: OrphanPoolConfig) -> Self { + pub fn new(config: OrphanPoolConfig, validator: Validator) -> Self { Self { - config, - txs_by_signature: TtlCache::new(config.storage_capacity), + pool_storage: Arc::new(RwLock::new(OrphanPoolStorage::new(config, validator))), } } /// Insert a new transaction into the OrphanPool. Orphaned transactions will have a limited Time-to-live and will be /// discarded if the UTXOs they require are not created before the Time-to-live threshold is reached. - pub fn insert(&mut self, tx: Transaction) { - let tx_key = tx.body.kernels[0].excess_sig.clone(); - let _ = self.txs_by_signature.insert(tx_key, Arc::new(tx), self.config.tx_ttl); + pub fn insert(&self, transaction: Arc) -> Result<(), OrphanPoolError> { + self.pool_storage + .write() + .map_err(|e| OrphanPoolError::BackendError(e.to_string()))? + .insert(transaction); + Ok(()) } + #[cfg(test)] /// Insert a set of new transactions into the OrphanPool - pub fn insert_txs(&mut self, txs: Vec) { - for tx in txs.into_iter() { - self.insert(tx); - } + pub fn insert_txs(&self, transactions: Vec>) -> Result<(), OrphanPoolError> { + self.pool_storage + .write() + .map_err(|e| OrphanPoolError::BackendError(e.to_string()))? + .insert_txs(transactions); + Ok(()) } /// Check if a transaction is stored in the OrphanPool - pub fn has_tx_with_excess_sig(&self, excess_sig: &Signature) -> bool { - self.txs_by_signature.contains_key(excess_sig) + pub fn has_tx_with_excess_sig(&self, excess_sig: &Signature) -> Result { + Ok(self + .pool_storage + .read() + .map_err(|e| OrphanPoolError::BackendError(e.to_string()))? + .has_tx_with_excess_sig(excess_sig)) } /// Check if the required UTXOs have been created and if the status of any of the transactions in the OrphanPool has /// changed. Remove valid transactions and valid transactions with time-locks from the OrphanPool. - // TODO: A reference to the UTXO set should not be passed in like this, but rather a handle or stream to the Chain - // (BlockchainDatabase or BlockchainService) should be provided to the OrphanPool during creation allowing a - // Chain call to be used to query the current block_height and UTXO set pub fn scan_for_and_remove_unorphaned_txs( - &mut self, - block_height: u64, - utxos: &[TransactionInput], - ) -> (Vec>, Vec>) - { - let mut removed_tx_keys: Vec = Vec::new(); - let mut removed_timelocked_tx_keys: Vec = Vec::new(); - for (tx_key, tx) in self.txs_by_signature.iter() { - if tx.body.inputs.iter().all(|input| utxos.contains(input)) { - if tx.body.kernels[0].lock_height <= block_height { - removed_tx_keys.push(tx_key.clone()); - } else { - removed_timelocked_tx_keys.push(tx_key.clone()); - } - } - } - - let mut removed_txs: Vec> = Vec::with_capacity(removed_tx_keys.len()); - removed_tx_keys.iter().for_each(|tx_key| { - if let Some(tx) = self.txs_by_signature.remove(&tx_key) { - removed_txs.push(tx); - } - }); - - let mut removed_timelocked_txs: Vec> = Vec::with_capacity(removed_timelocked_tx_keys.len()); - removed_timelocked_tx_keys.iter().for_each(|tx_key| { - if let Some(tx) = self.txs_by_signature.remove(&tx_key) { - removed_timelocked_txs.push(tx); - } - }); - - (removed_txs, removed_timelocked_txs) + &self, + ) -> Result<(Vec>, Vec>), OrphanPoolError> { + self.pool_storage + .write() + .map_err(|e| OrphanPoolError::BackendError(e.to_string()))? + .scan_for_and_remove_unorphaned_txs() } /// Returns the total number of orphaned transactions stored in the OrphanPool - pub fn len(&mut self) -> usize { - let mut count = 0; - self.txs_by_signature.iter().for_each(|_| count += 1); - (count) + pub fn len(&self) -> Result { + Ok(self + .pool_storage + .write() + .map_err(|e| OrphanPoolError::BackendError(e.to_string()))? + .len()) + } + + /// Returns all transaction stored in the OrphanPool. + pub fn snapshot(&self) -> Result>, OrphanPoolError> { + Ok(self + .pool_storage + .write() + .map_err(|e| OrphanPoolError::BackendError(e.to_string()))? + .snapshot()) + } + + /// Returns the total weight of all transactions stored in the pool. + pub fn calculate_weight(&self) -> Result { + Ok(self + .pool_storage + .write() + .map_err(|e| OrphanPoolError::BackendError(e.to_string()))? + .calculate_weight()) + } +} + +impl Clone for OrphanPool +where T: BlockchainBackend +{ + fn clone(&self) -> Self { + OrphanPool { + pool_storage: self.pool_storage.clone(), + } } } #[cfg(test)] mod test { - use super::*; use crate::{ - tari_amount::MicroTari, - test_utils::builders::{create_test_block, create_test_tx, extract_outputs_as_inputs}, - transaction::TransactionInput, + consensus::{ConsensusManagerBuilder, Network}, + helpers::create_mem_db, + mempool::orphan_pool::{OrphanPool, OrphanPoolConfig}, + transactions::tari_amount::MicroTari, + tx, + validation::transaction_validators::TxInputAndMaturityValidator, }; - use std::{thread, time::Duration}; + use std::{sync::Arc, thread, time::Duration}; #[test] fn test_insert_rlu_and_ttl() { - let tx1 = create_test_tx(MicroTari(10_000), MicroTari(500), 4000, 2, 1); - let tx2 = create_test_tx(MicroTari(10_000), MicroTari(300), 3000, 2, 1); - let tx3 = create_test_tx(MicroTari(10_000), MicroTari(100), 2500, 2, 1); - let tx4 = create_test_tx(MicroTari(10_000), MicroTari(200), 1000, 2, 1); - let tx5 = create_test_tx(MicroTari(10_000), MicroTari(500), 2000, 2, 1); - let tx6 = create_test_tx(MicroTari(10_000), MicroTari(600), 5500, 2, 1); - - let mut orphan_pool = OrphanPool::new(OrphanPoolConfig { - storage_capacity: 3, - tx_ttl: Duration::from_millis(50), - }); - orphan_pool.insert_txs(vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone()]); + let tx1 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(500), lock: 4000, inputs: 2, outputs: 1).0); + let tx2 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(300), lock: 3000, inputs: 2, outputs: 1).0); + let tx3 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(100), lock: 2500, inputs: 2, outputs: 1).0); + let tx4 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(200), lock: 1000, inputs: 2, outputs: 1).0); + let tx5 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(500), lock: 2000, inputs: 2, outputs: 1).0); + let tx6 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(600), lock: 5500, inputs: 2, outputs: 1).0); + let network = Network::LocalNet; + let consensus_manager = ConsensusManagerBuilder::new(network).build(); + let store = create_mem_db(consensus_manager); + let mempool_validator = Box::new(TxInputAndMaturityValidator::new(store.clone())); + let orphan_pool = OrphanPool::new( + OrphanPoolConfig { + storage_capacity: 3, + tx_ttl: Duration::from_millis(50), + }, + mempool_validator, + ); + orphan_pool + .insert_txs(vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone()]) + .unwrap(); // Check that oldest utx was removed to make room for new incoming transaction - assert!(!orphan_pool.has_tx_with_excess_sig(&tx1.body.kernels[0].excess_sig)); - assert!(orphan_pool.has_tx_with_excess_sig(&tx2.body.kernels[0].excess_sig)); - assert!(orphan_pool.has_tx_with_excess_sig(&tx3.body.kernels[0].excess_sig)); - assert!(orphan_pool.has_tx_with_excess_sig(&tx4.body.kernels[0].excess_sig)); + assert_eq!( + orphan_pool + .has_tx_with_excess_sig(&tx1.body.kernels()[0].excess_sig) + .unwrap(), + false + ); + assert_eq!( + orphan_pool + .has_tx_with_excess_sig(&tx2.body.kernels()[0].excess_sig) + .unwrap(), + true + ); + assert_eq!( + orphan_pool + .has_tx_with_excess_sig(&tx3.body.kernels()[0].excess_sig) + .unwrap(), + true + ); + assert_eq!( + orphan_pool + .has_tx_with_excess_sig(&tx4.body.kernels()[0].excess_sig) + .unwrap(), + true + ); + + let snapshot_txs = orphan_pool.snapshot().unwrap(); + assert_eq!(snapshot_txs.len(), 3); + assert!(snapshot_txs.contains(&tx2)); + assert!(snapshot_txs.contains(&tx3)); + assert!(snapshot_txs.contains(&tx4)); // Check that transactions that have been in the pool for longer than their Time-to-live have been removed thread::sleep(Duration::from_millis(51)); - orphan_pool.insert_txs(vec![tx5.clone(), tx6.clone()]); + orphan_pool.insert_txs(vec![tx5.clone(), tx6.clone()]).unwrap(); + assert_eq!(orphan_pool.len().unwrap(), 2); assert_eq!( - orphan_pool.has_tx_with_excess_sig(&tx1.body.kernels[0].excess_sig), + orphan_pool + .has_tx_with_excess_sig(&tx1.body.kernels()[0].excess_sig) + .unwrap(), false ); assert_eq!( - orphan_pool.has_tx_with_excess_sig(&tx2.body.kernels[0].excess_sig), + orphan_pool + .has_tx_with_excess_sig(&tx2.body.kernels()[0].excess_sig) + .unwrap(), false ); assert_eq!( - orphan_pool.has_tx_with_excess_sig(&tx3.body.kernels[0].excess_sig), + orphan_pool + .has_tx_with_excess_sig(&tx3.body.kernels()[0].excess_sig) + .unwrap(), false ); assert_eq!( - orphan_pool.has_tx_with_excess_sig(&tx4.body.kernels[0].excess_sig), + orphan_pool + .has_tx_with_excess_sig(&tx4.body.kernels()[0].excess_sig) + .unwrap(), false ); assert_eq!( - orphan_pool.has_tx_with_excess_sig(&tx5.body.kernels[0].excess_sig), + orphan_pool + .has_tx_with_excess_sig(&tx5.body.kernels()[0].excess_sig) + .unwrap(), true ); assert_eq!( - orphan_pool.has_tx_with_excess_sig(&tx6.body.kernels[0].excess_sig), + orphan_pool + .has_tx_with_excess_sig(&tx6.body.kernels()[0].excess_sig) + .unwrap(), true ); - assert_eq!(orphan_pool.len(), 2); - } - - #[test] - fn remove_remove_valid() { - let tx1 = create_test_tx(MicroTari(10_000), MicroTari(500), 1100, 2, 1); - let tx2 = create_test_tx(MicroTari(10_000), MicroTari(300), 1700, 2, 1); - let tx3 = create_test_tx(MicroTari(10_000), MicroTari(100), 2500, 1, 1); - let mut tx4 = create_test_tx(MicroTari(10_000), MicroTari(200), 3100, 2, 1); - let mut tx5 = create_test_tx(MicroTari(10_000), MicroTari(500), 4500, 2, 1); - let tx6 = create_test_tx(MicroTari(10_000), MicroTari(600), 5200, 2, 1); - // Publishing of tx1 and tx2 will create the UTXOs required by tx4 and tx5 - tx4.body.inputs.clear(); - tx1.body - .outputs - .iter() - .for_each(|output| tx4.body.inputs.push(TransactionInput::from(output.clone()))); - - tx5.body.inputs.clear(); - tx2.body - .outputs - .iter() - .for_each(|output| tx5.body.inputs.push(TransactionInput::from(output.clone()))); - - let mut orphan_pool = OrphanPool::new(OrphanPoolConfig::default()); - orphan_pool.insert_txs(vec![tx3.clone(), tx4.clone(), tx5.clone()]); - - let published_block = create_test_block(3000, vec![tx6.clone()]); - let mut utxos = Vec::new(); - extract_outputs_as_inputs(&mut utxos, &published_block); - let (txs, timelocked_txs) = - orphan_pool.scan_for_and_remove_unorphaned_txs(published_block.header.height, &utxos); - assert_eq!(orphan_pool.len(), 3); - assert_eq!(txs.len(), 0); - assert_eq!(timelocked_txs.len(), 0); - assert!(orphan_pool.has_tx_with_excess_sig(&tx3.body.kernels[0].excess_sig)); - assert!(orphan_pool.has_tx_with_excess_sig(&tx4.body.kernels[0].excess_sig)); - assert!(orphan_pool.has_tx_with_excess_sig(&tx5.body.kernels[0].excess_sig)); - - let published_block = create_test_block(3500, vec![tx1.clone(), tx2.clone()]); - extract_outputs_as_inputs(&mut utxos, &published_block); - let (txs, timelocked_txs) = - orphan_pool.scan_for_and_remove_unorphaned_txs(published_block.header.height, &utxos); - assert_eq!(orphan_pool.len(), 1); - assert_eq!(txs.len(), 1); - assert_eq!(timelocked_txs.len(), 1); - assert!(orphan_pool.has_tx_with_excess_sig(&tx3.body.kernels[0].excess_sig)); - assert!(txs.iter().any(|tx| **tx == tx4)); - assert!(timelocked_txs.iter().any(|tx| **tx == tx5)); } } diff --git a/base_layer/core/src/mempool/orphan_pool/orphan_pool_storage.rs b/base_layer/core/src/mempool/orphan_pool/orphan_pool_storage.rs new file mode 100644 index 0000000000..a386e20787 --- /dev/null +++ b/base_layer/core/src/mempool/orphan_pool/orphan_pool_storage.rs @@ -0,0 +1,146 @@ +// Copyright 2019 The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + chain_storage::BlockchainBackend, + mempool::orphan_pool::{error::OrphanPoolError, orphan_pool::OrphanPoolConfig}, + transactions::{transaction::Transaction, types::Signature}, + validation::{ValidationError, Validator}, +}; +use log::*; +use std::sync::Arc; +use ttl_cache::TtlCache; + +pub const LOG_TARGET: &str = "c::mp::orphan_pool::orphan_pool_storage"; + +/// OrphanPool makes use of OrphanPoolStorage to provide thread save access to its TtlCache. +/// The Orphan Pool contains all the received transactions that attempt to spend UTXOs that don't exist. These UTXOs +/// might exist in the future if these transactions are from a series or set of transactions that need to be processed +/// in a specific order. Some of these transactions might still be constrained by pending time-locks. +pub struct OrphanPoolStorage +where T: BlockchainBackend +{ + config: OrphanPoolConfig, + txs_by_signature: TtlCache>, + validator: Validator, +} + +impl OrphanPoolStorage +where T: BlockchainBackend +{ + /// Create a new OrphanPoolStorage with the specified configuration + pub fn new(config: OrphanPoolConfig, validator: Validator) -> Self { + Self { + config, + txs_by_signature: TtlCache::new(config.storage_capacity), + validator, + } + } + + /// Insert a new transaction into the OrphanPoolStorage. Orphaned transactions will have a limited Time-to-live and + /// will be discarded if the UTXOs they require are not created before the Time-to-live threshold is reached. + pub fn insert(&mut self, tx: Arc) { + trace!(target: LOG_TARGET, "Adding tx to orphan pool: {:?}", tx.clone()); + let tx_key = tx.body.kernels()[0].excess_sig.clone(); + let _ = self.txs_by_signature.insert(tx_key, tx, self.config.tx_ttl); + } + + /// Insert a set of new transactions into the OrphanPoolStorage + #[allow(dead_code)] + pub fn insert_txs(&mut self, txs: Vec>) { + for tx in txs.into_iter() { + self.insert(tx); + } + } + + /// Check if a transaction is stored in the OrphanPoolStorage + pub fn has_tx_with_excess_sig(&self, excess_sig: &Signature) -> bool { + self.txs_by_signature.contains_key(excess_sig) + } + + /// Check if the required UTXOs have been created and if the status of any of the transactions in the + /// OrphanPoolStorage has changed. Remove valid transactions and valid transactions with time-locks from the + /// OrphanPoolStorage. + pub fn scan_for_and_remove_unorphaned_txs( + &mut self, + ) -> Result<(Vec>, Vec>), OrphanPoolError> { + let mut removed_tx_keys: Vec = Vec::new(); + let mut removed_timelocked_tx_keys: Vec = Vec::new(); + + // We dont care about tx's that appeared in valid blocks. Those tx's will time out in orphan pool and remove + // them selves. + for (tx_key, tx) in self.txs_by_signature.iter() { + match self.validator.validate(&tx) { + Ok(()) => { + trace!( + target: LOG_TARGET, + "Removing key from orphan pool: {:?}", + tx_key.clone() + ); + removed_tx_keys.push(tx_key.clone()); + }, + Err(ValidationError::MaturityError) => { + trace!( + target: LOG_TARGET, + "Removing timelocked key from orphan pool: {:?}", + tx_key.clone() + ); + removed_timelocked_tx_keys.push(tx_key.clone()); + }, + _ => {}, + }; + } + + let mut removed_txs: Vec> = Vec::with_capacity(removed_tx_keys.len()); + removed_tx_keys.iter().for_each(|tx_key| { + if let Some(tx) = self.txs_by_signature.remove(&tx_key) { + removed_txs.push(tx); + } + }); + + let mut removed_timelocked_txs: Vec> = Vec::with_capacity(removed_timelocked_tx_keys.len()); + removed_timelocked_tx_keys.iter().for_each(|tx_key| { + if let Some(tx) = self.txs_by_signature.remove(&tx_key) { + removed_timelocked_txs.push(tx); + } + }); + + Ok((removed_txs, removed_timelocked_txs)) + } + + /// Returns the total number of orphaned transactions stored in the OrphanPoolStorage + pub fn len(&mut self) -> usize { + self.txs_by_signature.iter().count() + } + + /// Returns all transaction stored in the OrphanPoolStorage. + pub fn snapshot(&mut self) -> Vec> { + self.txs_by_signature.iter().map(|(_, tx)| tx).cloned().collect() + } + + /// Returns the total weight of all transactions stored in the pool. + pub fn calculate_weight(&mut self) -> u64 { + self.txs_by_signature + .iter() + .fold(0, |weight, (_, tx)| weight + tx.calculate_weight()) + } +} diff --git a/base_layer/core/src/mempool/pending_pool/error.rs b/base_layer/core/src/mempool/pending_pool/error.rs index c94d472e0c..af72ec24ec 100644 --- a/base_layer/core/src/mempool/pending_pool/error.rs +++ b/base_layer/core/src/mempool/pending_pool/error.rs @@ -27,7 +27,8 @@ use derive_error::Error; pub enum PendingPoolError { /// The HashMap and BTreeMap are out of sync StorageOutofSync, - /// The Thread Safety has been breached and the data access has become poisoned - PoisonedAccess, + /// A problem has been encountered with the storage backend. + #[error(non_std, no_from)] + BackendError(String), PriorityError(PriorityError), } diff --git a/base_layer/core/src/mempool/pending_pool/pending_pool.rs b/base_layer/core/src/mempool/pending_pool/pending_pool.rs index 39313ffa85..4b08c717ee 100644 --- a/base_layer/core/src/mempool/pending_pool/pending_pool.rs +++ b/base_layer/core/src/mempool/pending_pool/pending_pool.rs @@ -22,10 +22,11 @@ use crate::{ blocks::Block, - consts::MEMPOOL_PENDING_POOL_STORAGE_CAPACITY, - mempool::pending_pool::{PendingPoolError, PendingPoolStorage}, - transaction::Transaction, - types::Signature, + mempool::{ + consts::MEMPOOL_PENDING_POOL_STORAGE_CAPACITY, + pending_pool::{PendingPoolError, PendingPoolStorage}, + }, + transactions::{transaction::Transaction, types::Signature}, }; use std::sync::{Arc, RwLock}; @@ -47,32 +48,32 @@ impl Default for PendingPoolConfig { /// The Pending Pool contains all transactions that are restricted by time-locks. Once the time-locks have expired then /// the transactions can be moved to the Unconfirmed Transaction Pool for inclusion in future blocks. pub struct PendingPool { - pool_storage: RwLock, + pool_storage: Arc>, } impl PendingPool { /// Create a new PendingPool with the specified configuration. pub fn new(config: PendingPoolConfig) -> Self { Self { - pool_storage: RwLock::new(PendingPoolStorage::new(config)), + pool_storage: Arc::new(RwLock::new(PendingPoolStorage::new(config))), } } /// Insert a new transaction into the PendingPool. Low priority transactions will be removed to make space for /// higher priority transactions. The lowest priority transactions will be removed when the maximum capacity is /// reached and the new transaction has a higher priority than the currently stored lowest priority transaction. - pub fn insert(&mut self, transaction: Transaction) -> Result<(), PendingPoolError> { + pub fn insert(&self, transaction: Arc) -> Result<(), PendingPoolError> { self.pool_storage .write() - .map_err(|_| PendingPoolError::PoisonedAccess)? + .map_err(|e| PendingPoolError::BackendError(e.to_string()))? .insert(transaction) } /// Insert a set of new transactions into the PendingPool. - pub fn insert_txs(&mut self, transactions: Vec) -> Result<(), PendingPoolError> { + pub fn insert_txs(&self, transactions: Vec>) -> Result<(), PendingPoolError> { self.pool_storage .write() - .map_err(|_| PendingPoolError::PoisonedAccess)? + .map_err(|e| PendingPoolError::BackendError(e.to_string()))? .insert_txs(transactions) } @@ -81,20 +82,20 @@ impl PendingPool { Ok(self .pool_storage .read() - .map_err(|_| PendingPoolError::PoisonedAccess)? + .map_err(|e| PendingPoolError::BackendError(e.to_string()))? .has_tx_with_excess_sig(excess_sig)) } /// Remove transactions with expired time-locks so that they can be move to the UnconfirmedPool. Double spend /// transactions are also removed. pub fn remove_unlocked_and_discard_double_spends( - &mut self, + &self, published_block: &Block, ) -> Result>, PendingPoolError> { self.pool_storage .write() - .map_err(|_| PendingPoolError::PoisonedAccess)? + .map_err(|e| PendingPoolError::BackendError(e.to_string()))? .remove_unlocked_and_discard_double_spends(published_block) } @@ -103,39 +104,68 @@ impl PendingPool { Ok(self .pool_storage .read() - .map_err(|_| PendingPoolError::PoisonedAccess)? + .map_err(|e| PendingPoolError::BackendError(e.to_string()))? .len()) } + /// Returns all transaction stored in the PendingPool. + pub fn snapshot(&self) -> Result>, PendingPoolError> { + Ok(self + .pool_storage + .read() + .map_err(|e| PendingPoolError::BackendError(e.to_string()))? + .snapshot()) + } + + /// Returns the total weight of all transactions stored in the pool. + pub fn calculate_weight(&self) -> Result { + Ok(self + .pool_storage + .read() + .map_err(|e| PendingPoolError::BackendError(e.to_string()))? + .calculate_weight()) + } + #[cfg(test)] /// Checks the consistency status of the Hashmap and BtreeMaps. pub fn check_status(&self) -> Result { Ok(self .pool_storage .read() - .map_err(|_| PendingPoolError::PoisonedAccess)? + .map_err(|e| PendingPoolError::BackendError(e.to_string()))? .check_status()) } } +impl Clone for PendingPool { + fn clone(&self) -> Self { + PendingPool { + pool_storage: self.pool_storage.clone(), + } + } +} + #[cfg(test)] mod test { - use super::*; use crate::{ - tari_amount::MicroTari, - test_utils::builders::{create_test_block, create_test_tx}, + consensus::Network, + helpers::create_orphan_block, + mempool::pending_pool::{PendingPool, PendingPoolConfig}, + transactions::tari_amount::MicroTari, + tx, }; + use std::sync::Arc; #[test] fn test_insert_and_lru() { - let tx1 = create_test_tx(MicroTari(10_000), MicroTari(500), 500, 2, 1); - let tx2 = create_test_tx(MicroTari(10_000), MicroTari(100), 2150, 1, 2); - let tx3 = create_test_tx(MicroTari(10_000), MicroTari(1000), 1000, 2, 1); - let tx4 = create_test_tx(MicroTari(10_000), MicroTari(200), 2450, 2, 2); - let tx5 = create_test_tx(MicroTari(10_000), MicroTari(500), 1000, 3, 3); - let tx6 = create_test_tx(MicroTari(10_000), MicroTari(750), 1850, 2, 2); - - let mut pending_pool = PendingPool::new(PendingPoolConfig { storage_capacity: 3 }); + let tx1 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(50), lock: 500, inputs: 2, outputs: 1).0); + let tx2 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(20), lock: 2150, inputs: 1, outputs: 2).0); + let tx3 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(100), lock: 1000, inputs: 2, outputs: 1).0); + let tx4 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(30), lock: 2450, inputs: 2, outputs: 2).0); + let tx5 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(50), lock: 1000, inputs: 3, outputs: 3).0); + let tx6 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(75), lock: 1850, inputs: 2, outputs: 2).0); + + let pending_pool = PendingPool::new(PendingPoolConfig { storage_capacity: 3 }); pending_pool .insert_txs(vec![ tx1.clone(), @@ -150,37 +180,37 @@ mod test { assert_eq!(pending_pool.len().unwrap(), 3); assert_eq!( pending_pool - .has_tx_with_excess_sig(&tx1.body.kernels[0].excess_sig) + .has_tx_with_excess_sig(&tx1.body.kernels()[0].excess_sig) .unwrap(), true ); assert_eq!( pending_pool - .has_tx_with_excess_sig(&tx2.body.kernels[0].excess_sig) + .has_tx_with_excess_sig(&tx2.body.kernels()[0].excess_sig) .unwrap(), false ); assert_eq!( pending_pool - .has_tx_with_excess_sig(&tx3.body.kernels[0].excess_sig) + .has_tx_with_excess_sig(&tx3.body.kernels()[0].excess_sig) .unwrap(), true ); assert_eq!( pending_pool - .has_tx_with_excess_sig(&tx4.body.kernels[0].excess_sig) + .has_tx_with_excess_sig(&tx4.body.kernels()[0].excess_sig) .unwrap(), false ); assert_eq!( pending_pool - .has_tx_with_excess_sig(&tx5.body.kernels[0].excess_sig) + .has_tx_with_excess_sig(&tx5.body.kernels()[0].excess_sig) .unwrap(), false ); assert_eq!( pending_pool - .has_tx_with_excess_sig(&tx6.body.kernels[0].excess_sig) + .has_tx_with_excess_sig(&tx6.body.kernels()[0].excess_sig) .unwrap(), true ); @@ -190,14 +220,22 @@ mod test { #[test] fn test_remove_unlocked_and_discard_double_spends() { - let tx1 = create_test_tx(MicroTari(10_000), MicroTari(500), 500, 2, 1); - let tx2 = create_test_tx(MicroTari(10_000), MicroTari(100), 2150, 1, 2); - let tx3 = create_test_tx(MicroTari(10_000), MicroTari(1000), 1000, 2, 1); - let tx4 = create_test_tx(MicroTari(10_000), MicroTari(200), 2450, 2, 2); - let tx5 = create_test_tx(MicroTari(10_000), MicroTari(500), 1000, 3, 3); - let tx6 = create_test_tx(MicroTari(10_000), MicroTari(750), 1450, 2, 2); - - let mut pending_pool = PendingPool::new(PendingPoolConfig { storage_capacity: 10 }); + let network = Network::LocalNet; + let consensus_constants = network.create_consensus_constants(); + let tx1 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(50), lock: 500, inputs: 2, outputs: 1).0); + let tx2 = + Arc::new(tx!(MicroTari(10_000), fee: MicroTari(20), lock: 0, inputs: 1, maturity: 2150, outputs: 2).0); + let tx3 = Arc::new( + tx!(MicroTari(10_000), fee: MicroTari(100), lock: 0, inputs: 2, maturity: 1000, outputs: + 1) + .0, + ); + let tx4 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(30), lock: 2450, inputs: 2, outputs: 2).0); + let tx5 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(50), lock: 1000, inputs: 3, outputs: 3).0); + let tx6 = + Arc::new(tx!(MicroTari(10_000), fee: MicroTari(75), lock: 1450, inputs: 2, maturity: 1400, outputs: 2).0); + + let pending_pool = PendingPool::new(PendingPoolConfig { storage_capacity: 10 }); pending_pool .insert_txs(vec![ tx1.clone(), @@ -210,7 +248,16 @@ mod test { .unwrap(); assert_eq!(pending_pool.len().unwrap(), 6); - let published_block = create_test_block(1500, vec![tx6.clone()]); + let snapshot_txs = pending_pool.snapshot().unwrap(); + assert_eq!(snapshot_txs.len(), 6); + assert!(snapshot_txs.contains(&tx1)); + assert!(snapshot_txs.contains(&tx2)); + assert!(snapshot_txs.contains(&tx3)); + assert!(snapshot_txs.contains(&tx4)); + assert!(snapshot_txs.contains(&tx5)); + assert!(snapshot_txs.contains(&tx6)); + + let published_block = create_orphan_block(1500, vec![(*tx6).clone()], &consensus_constants); let unlocked_txs = pending_pool .remove_unlocked_and_discard_double_spends(&published_block) .unwrap(); @@ -218,21 +265,21 @@ mod test { assert_eq!(pending_pool.len().unwrap(), 2); assert_eq!( pending_pool - .has_tx_with_excess_sig(&tx2.body.kernels[0].excess_sig) + .has_tx_with_excess_sig(&tx2.body.kernels()[0].excess_sig) .unwrap(), true ); assert_eq!( pending_pool - .has_tx_with_excess_sig(&tx4.body.kernels[0].excess_sig) + .has_tx_with_excess_sig(&tx4.body.kernels()[0].excess_sig) .unwrap(), true ); assert_eq!(unlocked_txs.len(), 3); - assert!(unlocked_txs.iter().any(|tx| **tx == tx1)); - assert!(unlocked_txs.iter().any(|tx| **tx == tx3)); - assert!(unlocked_txs.iter().any(|tx| **tx == tx5)); + assert!(unlocked_txs.contains(&tx1)); + assert!(unlocked_txs.contains(&tx3)); + assert!(unlocked_txs.contains(&tx5)); assert!(pending_pool.check_status().unwrap()); } diff --git a/base_layer/core/src/mempool/pending_pool/pending_pool_storage.rs b/base_layer/core/src/mempool/pending_pool/pending_pool_storage.rs index fd169f5eda..40ae2bc7c5 100644 --- a/base_layer/core/src/mempool/pending_pool/pending_pool_storage.rs +++ b/base_layer/core/src/mempool/pending_pool/pending_pool_storage.rs @@ -26,15 +26,17 @@ use crate::{ pending_pool::{PendingPoolConfig, PendingPoolError}, priority::{FeePriority, TimelockPriority, TimelockedTransaction}, }, - transaction::Transaction, - types::Signature, + transactions::{transaction::Transaction, types::Signature}, }; +use log::*; use std::{ collections::{BTreeMap, HashMap}, convert::TryFrom, sync::Arc, }; +pub const LOG_TARGET: &str = "c::mp::pending_pool::pending_pool_storage"; + /// PendingPool makes use of PendingPoolStorage to provide thread safe access to its Hashmap and BTreeMaps. /// The txs_by_signature HashMap is used to find a transaction using its excess_sig, this functionality is used to match /// transactions included in blocks with transactions stored in the pool. @@ -72,6 +74,12 @@ impl PendingPoolStorage { .map(|(p, s)| (p.clone(), s.clone())) { if let Some(removed_tx) = self.txs_by_signature.remove(&tx_key) { + trace!( + target: LOG_TARGET, + "Removing tx from pending pool: {:?}, {:?}", + removed_tx.fee_priority, + removed_tx.timelock_priority + ); self.txs_by_fee_priority.remove(&removed_tx.fee_priority); self.txs_by_timelock_priority.remove(&removed_tx.timelock_priority); } @@ -80,10 +88,11 @@ impl PendingPoolStorage { /// Insert a new transaction into the PendingPoolStorage. Low priority transactions will be removed to make space /// for higher priority transactions. - pub fn insert(&mut self, tx: Transaction) -> Result<(), PendingPoolError> { - let tx_key = tx.body.kernels[0].excess_sig.clone(); + pub fn insert(&mut self, tx: Arc) -> Result<(), PendingPoolError> { + let tx_key = tx.body.kernels()[0].excess_sig.clone(); if !self.txs_by_signature.contains_key(&tx_key) { - let prioritized_tx = TimelockedTransaction::try_from(tx)?; + trace!(target: LOG_TARGET, "Inserting tx into pending pool: {:?}", tx_key,); + let prioritized_tx = TimelockedTransaction::try_from((*tx).clone())?; if self.txs_by_signature.len() >= self.config.storage_capacity { if prioritized_tx.fee_priority < *self.lowest_fee_priority() { return Ok(()); @@ -101,7 +110,7 @@ impl PendingPoolStorage { } /// Insert a set of new transactions into the PendingPoolStorage - pub fn insert_txs(&mut self, txs: Vec) -> Result<(), PendingPoolError> { + pub fn insert_txs(&mut self, txs: Vec>) -> Result<(), PendingPoolError> { for tx in txs.into_iter() { self.insert(tx)?; } @@ -119,8 +128,8 @@ impl PendingPoolStorage { fn discard_double_spends(&mut self, published_block: &Block) { let mut removed_tx_keys: Vec = Vec::new(); for (tx_key, ptx) in self.txs_by_signature.iter() { - for input in &ptx.transaction.body.inputs { - if published_block.body.inputs.contains(input) { + for input in ptx.transaction.body.inputs() { + if published_block.body.inputs().contains(input) { self.txs_by_fee_priority.remove(&ptx.fee_priority); self.txs_by_timelock_priority.remove(&ptx.timelock_priority); removed_tx_keys.push(tx_key.clone()); @@ -129,6 +138,7 @@ impl PendingPoolStorage { } for tx_key in &removed_tx_keys { + trace!(target: LOG_TARGET, "Removed double spends: {:?}", tx_key); self.txs_by_signature.remove(&tx_key); } } @@ -148,10 +158,7 @@ impl PendingPoolStorage { .txs_by_signature .get(tx_key) .ok_or(PendingPoolError::StorageOutofSync)? - .transaction - .body - .kernels[0] - .lock_height > + .max_timelock_height > published_block.header.height { break; @@ -165,6 +172,7 @@ impl PendingPoolStorage { } for tx_key in &removed_tx_keys { + trace!(target: LOG_TARGET, "Removed unlocked and double spends: {:?}", tx_key); self.txs_by_timelock_priority.remove(&tx_key); } @@ -176,6 +184,21 @@ impl PendingPoolStorage { self.txs_by_signature.len() } + /// Returns all transaction stored in the PendingPoolStorage. + pub fn snapshot(&self) -> Vec> { + self.txs_by_signature + .iter() + .map(|(_, ptx)| ptx.transaction.clone()) + .collect() + } + + /// Returns the total weight of all transactions stored in the pool. + pub fn calculate_weight(&self) -> u64 { + self.txs_by_signature + .iter() + .fold(0, |weight, (_, ptx)| weight + ptx.transaction.calculate_weight()) + } + #[cfg(test)] /// Checks the consistency status of the Hashmap and the BtreeMaps pub fn check_status(&self) -> bool { diff --git a/base_layer/core/src/mempool/priority/error.rs b/base_layer/core/src/mempool/priority/error.rs index 912a4a40fd..8c1709fa11 100644 --- a/base_layer/core/src/mempool/priority/error.rs +++ b/base_layer/core/src/mempool/priority/error.rs @@ -21,7 +21,7 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use derive_error::Error; -use tari_utilities::message_format::MessageFormatError; +use tari_crypto::tari_utilities::message_format::MessageFormatError; #[derive(Debug, Error)] pub enum PriorityError { diff --git a/base_layer/core/src/mempool/priority/prioritized_transaction.rs b/base_layer/core/src/mempool/priority/prioritized_transaction.rs index 585540fb02..78c28381d4 100644 --- a/base_layer/core/src/mempool/priority/prioritized_transaction.rs +++ b/base_layer/core/src/mempool/priority/prioritized_transaction.rs @@ -20,11 +20,11 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{mempool::priority::PriorityError, transaction::Transaction}; +use crate::{mempool::priority::PriorityError, transactions::transaction::Transaction}; use std::{convert::TryFrom, sync::Arc}; -use tari_utilities::message_format::MessageFormat; +use tari_crypto::tari_utilities::message_format::MessageFormat; -/// Create a unique unspent transaction priority based on the transaction fee, age of the oldest input UTXO and the +/// Create a unique unspent transaction priority based on the transaction fee, maturity of the oldest input UTXO and the /// excess_sig. The excess_sig is included to ensure the the priority key unique so it can be used with a BTreeMap. /// Normally, duplicate keys will be overwritten in a BTreeMap. #[derive(PartialEq, Eq, PartialOrd, Ord, Debug)] @@ -33,10 +33,15 @@ pub struct FeePriority(Vec); impl FeePriority { pub fn try_from(transaction: &Transaction) -> Result { let fee_per_byte = (transaction.calculate_ave_fee_per_gram() * 1000.0) as usize; // Include 3 decimal places before flooring - let mut priority = fee_per_byte.to_binary()?; - priority.reverse(); // Fee needs to be in Big-endian for sorting with BtreeMap to work correctly - // TODO: Add oldest input UTXO age - priority.append(&mut transaction.body.kernels[0].to_binary()?); + let mut fee_priority = fee_per_byte.to_binary()?; + fee_priority.reverse(); // Requires Big-endian for BtreeMap sorting + + let mut maturity_priority = (std::u64::MAX - transaction.min_input_maturity()).to_binary()?; + maturity_priority.reverse(); // Requires Big-endian for BtreeMap sorting + + let mut priority = fee_priority; + priority.append(&mut maturity_priority); + priority.append(&mut transaction.body.kernels()[0].excess_sig.to_binary()?); Ok(Self(priority)) } } diff --git a/base_layer/core/src/mempool/priority/timelocked_transaction.rs b/base_layer/core/src/mempool/priority/timelocked_transaction.rs index 067b470bf7..db01c4904d 100644 --- a/base_layer/core/src/mempool/priority/timelocked_transaction.rs +++ b/base_layer/core/src/mempool/priority/timelocked_transaction.rs @@ -22,22 +22,22 @@ use crate::{ mempool::priority::{FeePriority, PriorityError}, - transaction::Transaction, + transactions::transaction::Transaction, }; use std::{convert::TryFrom, sync::Arc}; -use tari_utilities::message_format::MessageFormat; +use tari_crypto::tari_utilities::message_format::MessageFormat; -/// Create a unique transaction priority based on the lock_height and the excess_sig, allowing transactions to be sorted -/// according to their time-lock expiry. The excess_sig is included to ensure the priority key is unique so it can be -/// used with a BTreeMap. +/// Create a unique transaction priority based on the maximum time-lock (lock_height or input UTXO maturity) and the +/// excess_sig, allowing transactions to be sorted according to their time-lock expiry. The excess_sig is included to +/// ensure the priority key is unique so it can be used with a BTreeMap. #[derive(PartialEq, Eq, PartialOrd, Ord, Debug)] pub struct TimelockPriority(Vec); impl TimelockPriority { pub fn try_from(transaction: &Transaction) -> Result { - let mut priority = transaction.body.kernels[0].lock_height.to_binary()?; - priority.reverse(); // Timelock needs to be in Big-endian for sorting with BtreeMap to work correctly - priority.append(&mut transaction.body.kernels[0].to_binary()?); + let mut priority = transaction.min_spendable_height().to_binary()?; + priority.reverse(); // Requires Big-endian for BtreeMap sorting + priority.append(&mut transaction.body.kernels()[0].excess_sig.to_binary()?); Ok(Self(priority)) } } @@ -54,6 +54,7 @@ pub struct TimelockedTransaction { pub transaction: Arc, pub fee_priority: FeePriority, pub timelock_priority: TimelockPriority, + pub max_timelock_height: u64, } impl TryFrom for TimelockedTransaction { @@ -63,6 +64,10 @@ impl TryFrom for TimelockedTransaction { Ok(Self { fee_priority: FeePriority::try_from(&transaction)?, timelock_priority: TimelockPriority::try_from(&transaction)?, + max_timelock_height: match transaction.min_spendable_height() { + 0 => 0, + v => v - 1, + }, transaction: Arc::new(transaction), }) } diff --git a/base_layer/core/src/mempool/proto/mempool_request.rs b/base_layer/core/src/mempool/proto/mempool_request.rs new file mode 100644 index 0000000000..0342bf26e5 --- /dev/null +++ b/base_layer/core/src/mempool/proto/mempool_request.rs @@ -0,0 +1,69 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::mempool::{ + mempool_service_request::Request as ProtoMempoolRequest, + MempoolServiceRequest as ProtoMempoolServiceRequest, +}; +use crate::mempool::service::{MempoolRequest, MempoolServiceRequest}; +use std::convert::{TryFrom, TryInto}; +use tari_crypto::tari_utilities::ByteArrayError; + +impl TryInto for ProtoMempoolRequest { + type Error = String; + + fn try_into(self) -> Result { + use ProtoMempoolRequest::*; + let request = match self { + // Field was not specified + GetStats(_) => MempoolRequest::GetStats, + GetTxStateWithExcessSig(excess_sig) => MempoolRequest::GetTxStateWithExcessSig( + excess_sig.try_into().map_err(|err: ByteArrayError| err.to_string())?, + ), + }; + Ok(request) + } +} + +impl From for ProtoMempoolRequest { + fn from(request: MempoolRequest) -> Self { + use MempoolRequest::*; + match request { + GetStats => ProtoMempoolRequest::GetStats(true), + GetTxStateWithExcessSig(excess_sig) => ProtoMempoolRequest::GetTxStateWithExcessSig(excess_sig.into()), + } + } +} + +impl TryFrom for MempoolServiceRequest { + type Error = String; + + fn try_from(request: ProtoMempoolServiceRequest) -> Result { + Ok(Self { + request_key: request.request_key, + request: request + .request + .ok_or_else(|| "Response field not present".to_string())? + .try_into()?, + }) + } +} diff --git a/base_layer/core/src/mempool/proto/mempool_response.rs b/base_layer/core/src/mempool/proto/mempool_response.rs new file mode 100644 index 0000000000..324a54aafe --- /dev/null +++ b/base_layer/core/src/mempool/proto/mempool_response.rs @@ -0,0 +1,75 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::mempool::mempool_service_response::Response as ProtoMempoolResponse; +use crate::mempool::{ + proto::mempool::{ + MempoolServiceResponse as ProtoMempoolServiceResponse, + TxStorageResponse as ProtoTxStorageResponse, + }, + service::{MempoolResponse, MempoolServiceResponse}, +}; +use std::convert::{TryFrom, TryInto}; + +impl TryInto for ProtoMempoolResponse { + type Error = String; + + fn try_into(self) -> Result { + use ProtoMempoolResponse::*; + let response = match self { + Stats(stats_response) => MempoolResponse::Stats(stats_response.try_into()?), + TxStorage(tx_storage_response) => { + let tx_storage_response = ProtoTxStorageResponse::from_i32(tx_storage_response) + .ok_or_else(|| "Invalid or unrecognised `TxStorageResponse` enum".to_string())?; + MempoolResponse::TxStorage(tx_storage_response.try_into()?) + }, + }; + Ok(response) + } +} + +impl TryFrom for MempoolServiceResponse { + type Error = String; + + fn try_from(response: ProtoMempoolServiceResponse) -> Result { + Ok(Self { + request_key: response.request_key, + response: response + .response + .ok_or_else(|| "Response field not present to convert".to_string())? + .try_into()?, + }) + } +} + +impl From for ProtoMempoolResponse { + fn from(response: MempoolResponse) -> Self { + use MempoolResponse::*; + match response { + Stats(stats_response) => ProtoMempoolResponse::Stats(stats_response.into()), + TxStorage(tx_storage_response) => { + let tx_storage_response: ProtoTxStorageResponse = tx_storage_response.into(); + ProtoMempoolResponse::TxStorage(tx_storage_response.into()) + }, + } + } +} diff --git a/base_layer/core/src/mempool/proto/mod.rs b/base_layer/core/src/mempool/proto/mod.rs new file mode 100644 index 0000000000..6b9ab69722 --- /dev/null +++ b/base_layer/core/src/mempool/proto/mod.rs @@ -0,0 +1,34 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Required for `super::types` used in generated files +use crate::transactions::proto::types; + +pub mod mempool { + include!(concat!(env!("OUT_DIR"), "/", "tari.mempool.rs")); +} + +pub mod mempool_request; +pub mod mempool_response; +pub mod stats_response; +pub mod tx_storage_response; +pub use mempool::{MempoolServiceRequest, MempoolServiceResponse}; diff --git a/base_layer/core/src/mempool/proto/service_request.proto b/base_layer/core/src/mempool/proto/service_request.proto new file mode 100644 index 0000000000..993a6cfb1f --- /dev/null +++ b/base_layer/core/src/mempool/proto/service_request.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +import "types.proto"; + +package tari.mempool; + +// Request type for a received MempoolService request. +message MempoolServiceRequest { + uint64 request_key = 1; + oneof request { + // Indicates a GetStats request. The value of the bool should be ignored. + bool get_stats = 2; + // Indicates a GetTxStateWithExcessSig request. + tari.types.Signature get_tx_state_with_excess_sig = 3; + } +} diff --git a/base_layer/core/src/mempool/proto/service_response.proto b/base_layer/core/src/mempool/proto/service_response.proto new file mode 100644 index 0000000000..9d4809d965 --- /dev/null +++ b/base_layer/core/src/mempool/proto/service_response.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +import "stats_response.proto"; +import "tx_storage_response.proto"; + +package tari.mempool; + +// Response type for a received MempoolService requests +message MempoolServiceResponse { + uint64 request_key = 1; + oneof response { + StatsResponse stats = 2; + TxStorageResponse tx_storage = 3; + } +} + diff --git a/base_layer/core/src/mempool/proto/stats_response.proto b/base_layer/core/src/mempool/proto/stats_response.proto new file mode 100644 index 0000000000..afc6ecb8d6 --- /dev/null +++ b/base_layer/core/src/mempool/proto/stats_response.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package tari.mempool; + +message StatsResponse { + uint64 total_txs = 1; + uint64 unconfirmed_txs = 2; + uint64 orphan_txs = 3; + uint64 timelocked_txs = 4; + uint64 published_txs = 5; + uint64 total_weight = 6; +} \ No newline at end of file diff --git a/base_layer/core/src/mempool/proto/stats_response.rs b/base_layer/core/src/mempool/proto/stats_response.rs new file mode 100644 index 0000000000..6bfffc37e7 --- /dev/null +++ b/base_layer/core/src/mempool/proto/stats_response.rs @@ -0,0 +1,52 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::mempool::{proto::mempool::StatsResponse as ProtoStatsResponse, StatsResponse}; +use std::convert::TryFrom; + +impl TryFrom for StatsResponse { + type Error = String; + + fn try_from(stats: ProtoStatsResponse) -> Result { + Ok(Self { + total_txs: stats.total_txs as usize, + unconfirmed_txs: stats.unconfirmed_txs as usize, + orphan_txs: stats.orphan_txs as usize, + timelocked_txs: stats.timelocked_txs as usize, + published_txs: stats.published_txs as usize, + total_weight: stats.total_weight, + }) + } +} + +impl From for ProtoStatsResponse { + fn from(stats: StatsResponse) -> Self { + Self { + total_txs: stats.total_txs as u64, + unconfirmed_txs: stats.unconfirmed_txs as u64, + orphan_txs: stats.orphan_txs as u64, + timelocked_txs: stats.timelocked_txs as u64, + published_txs: stats.published_txs as u64, + total_weight: stats.total_weight, + } + } +} diff --git a/base_layer/core/src/mempool/proto/tx_storage_response.proto b/base_layer/core/src/mempool/proto/tx_storage_response.proto new file mode 100644 index 0000000000..dcf5a91389 --- /dev/null +++ b/base_layer/core/src/mempool/proto/tx_storage_response.proto @@ -0,0 +1,14 @@ +syntax = "proto3"; + +import "google/protobuf/wrappers.proto"; + +package tari.mempool; + +enum TxStorageResponse { + TxStorageResponseNone = 0; + TxStorageResponseUnconfirmedPool = 1; + TxStorageResponseOrphanPool = 2; + TxStorageResponsePendingPool = 3; + TxStorageResponseReorgPool = 4; + TxStorageResponseNotStored = 5; +} \ No newline at end of file diff --git a/infrastructure/tari_util/src/locks.rs b/base_layer/core/src/mempool/proto/tx_storage_response.rs similarity index 54% rename from infrastructure/tari_util/src/locks.rs rename to base_layer/core/src/mempool/proto/tx_storage_response.rs index f3bbfbaf87..90def21c71 100644 --- a/infrastructure/tari_util/src/locks.rs +++ b/base_layer/core/src/mempool/proto/tx_storage_response.rs @@ -20,46 +20,34 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -/// Recovers a poisoned lock by returning the value before the lock was poisoned -#[macro_export] -macro_rules! recover_lock { - ($e:expr) => { - match $e { - Ok(lock) => lock, - Err(poisoned) => { - log::warn!(target: "tari_util", "Lock has been POISONED and will be silently recovered"); - poisoned.into_inner() - }, - } - }; -} +use crate::mempool::{proto::mempool::TxStorageResponse as ProtoTxStorageResponse, TxStorageResponse}; +use std::convert::TryFrom; -/// This macro unlocks a Mutex or RwLock. If the lock is poisoned (i.e. a panic before a MutexGuard / RwLockGuard is -/// dropped) the last value before the panic occurred is used. -/// -/// This macro should not be used if the implementation should fail a if the lock was poisoned. -#[macro_export] -macro_rules! acquire_lock { - ($e:expr, $m:ident) => { - $crate::recover_lock!($e.$m()) - }; - ($e:expr) => { - $crate::acquire_lock!($e, lock) - }; -} +impl TryFrom for TxStorageResponse { + type Error = String; -/// Acquire a write lock on a RwLock, silently recovering the lock if it is poisoned -#[macro_export] -macro_rules! acquire_write_lock { - ($e:expr) => { - $crate::acquire_lock!($e, write) - }; + fn try_from(tx_storage: ProtoTxStorageResponse) -> Result { + use ProtoTxStorageResponse::*; + Ok(match tx_storage { + None => return Err("TxStorageResponse not provided".to_string()), + UnconfirmedPool => TxStorageResponse::UnconfirmedPool, + OrphanPool => TxStorageResponse::OrphanPool, + PendingPool => TxStorageResponse::PendingPool, + ReorgPool => TxStorageResponse::ReorgPool, + NotStored => TxStorageResponse::NotStored, + }) + } } -/// Acquire a read lock on a RwLock, silently recovering the lock if it is poisoned -#[macro_export] -macro_rules! acquire_read_lock { - ($e:expr) => { - $crate::acquire_lock!($e, read) - }; +impl From for ProtoTxStorageResponse { + fn from(tree: TxStorageResponse) -> Self { + use TxStorageResponse::*; + match tree { + UnconfirmedPool => ProtoTxStorageResponse::UnconfirmedPool, + OrphanPool => ProtoTxStorageResponse::OrphanPool, + PendingPool => ProtoTxStorageResponse::PendingPool, + ReorgPool => ProtoTxStorageResponse::ReorgPool, + NotStored => ProtoTxStorageResponse::NotStored, + } + } } diff --git a/comms/src/connection/zmq/error.rs b/base_layer/core/src/mempool/reorg_pool/error.rs similarity index 89% rename from comms/src/connection/zmq/error.rs rename to base_layer/core/src/mempool/reorg_pool/error.rs index 7fd34fcad2..df993fce6b 100644 --- a/comms/src/connection/zmq/error.rs +++ b/base_layer/core/src/mempool/reorg_pool/error.rs @@ -22,10 +22,9 @@ use derive_error::Error; -#[derive(Debug, Error, Eq, PartialEq)] -pub enum ZmqError { - /// Inproc address is malformed - MalformedInprocAddress, - #[error(msg_embedded, no_from, non_std)] - SocketError(String), +#[derive(Debug, Error)] +pub enum ReorgPoolError { + /// A problem has been encountered with the storage backend. + #[error(non_std, no_from)] + BackendError(String), } diff --git a/base_layer/core/src/mempool/reorg_pool/mod.rs b/base_layer/core/src/mempool/reorg_pool/mod.rs index 7ab7b6ce50..e5221f0efe 100644 --- a/base_layer/core/src/mempool/reorg_pool/mod.rs +++ b/base_layer/core/src/mempool/reorg_pool/mod.rs @@ -20,7 +20,11 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +mod error; mod reorg_pool; +mod reorg_pool_storage; // Public re-exports +pub use error::ReorgPoolError; pub use reorg_pool::{ReorgPool, ReorgPoolConfig}; +pub use reorg_pool_storage::ReorgPoolStorage; diff --git a/base_layer/core/src/mempool/reorg_pool/reorg_pool.rs b/base_layer/core/src/mempool/reorg_pool/reorg_pool.rs index 2ff79790f2..c952cedee2 100644 --- a/base_layer/core/src/mempool/reorg_pool/reorg_pool.rs +++ b/base_layer/core/src/mempool/reorg_pool/reorg_pool.rs @@ -22,12 +22,16 @@ use crate::{ blocks::Block, - consts::{MEMPOOL_REORG_POOL_CACHE_TTL, MEMPOOL_REORG_POOL_STORAGE_CAPACITY}, - transaction::Transaction, - types::Signature, + mempool::{ + consts::{MEMPOOL_REORG_POOL_CACHE_TTL, MEMPOOL_REORG_POOL_STORAGE_CAPACITY}, + reorg_pool::{ReorgPoolError, ReorgPoolStorage}, + }, + transactions::{transaction::Transaction, types::Signature}, +}; +use std::{ + sync::{Arc, RwLock}, + time::Duration, }; -use std::{sync::Arc, time::Duration}; -use ttl_cache::TtlCache; /// Configuration for the ReorgPool #[derive(Clone, Copy)] @@ -53,179 +57,281 @@ impl Default for ReorgPoolConfig { /// from the pool when the Time-to-live thresholds is reached. Also, when the capacity of the pool has been reached, the /// oldest transactions will be removed to make space for incoming transactions. pub struct ReorgPool { - config: ReorgPoolConfig, - txs_by_signature: TtlCache>, + pool_storage: Arc>, } impl ReorgPool { /// Create a new ReorgPool with the specified configuration pub fn new(config: ReorgPoolConfig) -> Self { Self { - config, - txs_by_signature: TtlCache::new(config.storage_capacity), + pool_storage: Arc::new(RwLock::new(ReorgPoolStorage::new(config))), } } - /// Insert a new transaction into the ReorgPool. Published transactions will have a limited Time-to-live in the - /// ReorgPool and will be discarded once the Time-to-live threshold has been reached. - pub fn insert(&mut self, tx: Transaction) { - let tx_key = tx.body.kernels[0].excess_sig.clone(); - let _ = self.txs_by_signature.insert(tx_key, Arc::new(tx), self.config.tx_ttl); - } - - /// Insert a set of new transactions into the ReorgPool - pub fn insert_txs(&mut self, txs: Vec) { - for tx in txs.into_iter() { - self.insert(tx); - } + /// Insert a set of new transactions into the ReorgPool. Published transactions will have a limited Time-to-live in + /// the ReorgPool and will be discarded once the Time-to-live threshold has been reached. + pub fn insert_txs(&self, transactions: Vec>) -> Result<(), ReorgPoolError> { + self.pool_storage + .write() + .map_err(|e| ReorgPoolError::BackendError(e.to_string()))? + .insert_txs(transactions); + Ok(()) } /// Check if a transaction is stored in the ReorgPool - pub fn has_tx_with_excess_sig(&self, excess_sig: &Signature) -> bool { - self.txs_by_signature.contains_key(excess_sig) + pub fn has_tx_with_excess_sig(&self, excess_sig: &Signature) -> Result { + Ok(self + .pool_storage + .read() + .map_err(|e| ReorgPoolError::BackendError(e.to_string()))? + .has_tx_with_excess_sig(excess_sig)) } /// Remove the transactions from the ReorgPool that were used in provided removed blocks. The transactions can be /// resubmitted to the Unconfirmed Pool. - pub fn scan_for_and_remove_reorged_txs(&mut self, removed_blocks: Vec) -> Vec> { - let mut removed_txs: Vec> = Vec::new(); - for block in &removed_blocks { - for kernel in &block.body.kernels { - if let Some(removed_tx) = self.txs_by_signature.remove(&kernel.excess_sig) { - removed_txs.push(removed_tx); - } - } - } - removed_txs + pub fn remove_reorged_txs_and_discard_double_spends( + &self, + removed_blocks: Vec, + new_blocks: &Vec, + ) -> Result>, ReorgPoolError> + { + Ok(self + .pool_storage + .write() + .map_err(|e| ReorgPoolError::BackendError(e.to_string()))? + .remove_reorged_txs_and_discard_double_spends(removed_blocks, new_blocks)) } /// Returns the total number of published transactions stored in the ReorgPool - pub fn len(&mut self) -> usize { - let mut count = 0; - self.txs_by_signature.iter().for_each(|_| count += 1); - (count) + pub fn len(&self) -> Result { + Ok(self + .pool_storage + .write() + .map_err(|e| ReorgPoolError::BackendError(e.to_string()))? + .len()) + } + + /// Returns the total weight of all transactions stored in the pool. + pub fn calculate_weight(&self) -> Result { + Ok(self + .pool_storage + .write() + .map_err(|e| ReorgPoolError::BackendError(e.to_string()))? + .calculate_weight()) + } +} + +impl Clone for ReorgPool { + fn clone(&self) -> Self { + ReorgPool { + pool_storage: self.pool_storage.clone(), + } } } #[cfg(test)] mod test { use super::*; - use crate::{ - tari_amount::MicroTari, - test_utils::builders::{create_test_block, create_test_tx}, - transaction::TransactionInput, - }; + use crate::{consensus::Network, helpers::create_orphan_block, transactions::tari_amount::MicroTari, tx}; use std::{thread, time::Duration}; #[test] fn test_insert_rlu_and_ttl() { - let tx1 = create_test_tx(MicroTari(10_000), MicroTari(500), 4000, 2, 1); - let tx2 = create_test_tx(MicroTari(10_000), MicroTari(300), 3000, 2, 1); - let tx3 = create_test_tx(MicroTari(10_000), MicroTari(100), 2500, 2, 1); - let tx4 = create_test_tx(MicroTari(10_000), MicroTari(200), 1000, 2, 1); - let tx5 = create_test_tx(MicroTari(10_000), MicroTari(500), 2000, 2, 1); - let tx6 = create_test_tx(MicroTari(10_000), MicroTari(600), 5500, 2, 1); - - let mut reorg_pool = ReorgPool::new(ReorgPoolConfig { + let tx1 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(500), lock: 4000, inputs: 2, outputs: 1).0); + let tx2 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(300), lock: 3000, inputs: 2, outputs: 1).0); + let tx3 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(100), lock: 2500, inputs: 2, outputs: 1).0); + let tx4 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(200), lock: 1000, inputs: 2, outputs: 1).0); + let tx5 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(500), lock: 2000, inputs: 2, outputs: 1).0); + let tx6 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(600), lock: 5500, inputs: 2, outputs: 1).0); + + let reorg_pool = ReorgPool::new(ReorgPoolConfig { storage_capacity: 3, tx_ttl: Duration::from_millis(50), }); - reorg_pool.insert_txs(vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone()]); + reorg_pool + .insert_txs(vec![tx1.clone(), tx2.clone(), tx3.clone(), tx4.clone()]) + .unwrap(); // Check that oldest utx was removed to make room for new incoming transactions assert_eq!( - reorg_pool.has_tx_with_excess_sig(&tx1.body.kernels[0].excess_sig), + reorg_pool + .has_tx_with_excess_sig(&tx1.body.kernels()[0].excess_sig) + .unwrap(), false ); - assert_eq!(reorg_pool.has_tx_with_excess_sig(&tx2.body.kernels[0].excess_sig), true); - assert_eq!(reorg_pool.has_tx_with_excess_sig(&tx3.body.kernels[0].excess_sig), true); - assert_eq!(reorg_pool.has_tx_with_excess_sig(&tx4.body.kernels[0].excess_sig), true); + assert_eq!( + reorg_pool + .has_tx_with_excess_sig(&tx2.body.kernels()[0].excess_sig) + .unwrap(), + true + ); + assert_eq!( + reorg_pool + .has_tx_with_excess_sig(&tx3.body.kernels()[0].excess_sig) + .unwrap(), + true + ); + assert_eq!( + reorg_pool + .has_tx_with_excess_sig(&tx4.body.kernels()[0].excess_sig) + .unwrap(), + true + ); // Check that transactions that have been in the pool for longer than their Time-to-live have been removed thread::sleep(Duration::from_millis(51)); - reorg_pool.insert_txs(vec![tx5.clone(), tx6.clone()]); + reorg_pool.insert_txs(vec![tx5.clone(), tx6.clone()]).unwrap(); + assert_eq!(reorg_pool.len().unwrap(), 2); assert_eq!( - reorg_pool.has_tx_with_excess_sig(&tx1.body.kernels[0].excess_sig), + reorg_pool + .has_tx_with_excess_sig(&tx1.body.kernels()[0].excess_sig) + .unwrap(), false ); assert_eq!( - reorg_pool.has_tx_with_excess_sig(&tx2.body.kernels[0].excess_sig), + reorg_pool + .has_tx_with_excess_sig(&tx2.body.kernels()[0].excess_sig) + .unwrap(), false ); assert_eq!( - reorg_pool.has_tx_with_excess_sig(&tx3.body.kernels[0].excess_sig), + reorg_pool + .has_tx_with_excess_sig(&tx3.body.kernels()[0].excess_sig) + .unwrap(), false ); assert_eq!( - reorg_pool.has_tx_with_excess_sig(&tx4.body.kernels[0].excess_sig), + reorg_pool + .has_tx_with_excess_sig(&tx4.body.kernels()[0].excess_sig) + .unwrap(), false ); - assert_eq!(reorg_pool.has_tx_with_excess_sig(&tx5.body.kernels[0].excess_sig), true); - assert_eq!(reorg_pool.has_tx_with_excess_sig(&tx6.body.kernels[0].excess_sig), true); - assert_eq!(reorg_pool.len(), 2); + assert_eq!( + reorg_pool + .has_tx_with_excess_sig(&tx5.body.kernels()[0].excess_sig) + .unwrap(), + true + ); + assert_eq!( + reorg_pool + .has_tx_with_excess_sig(&tx6.body.kernels()[0].excess_sig) + .unwrap(), + true + ); } #[test] fn remove_scan_for_and_remove_reorged_txs() { - let tx1 = create_test_tx(MicroTari(10_000), MicroTari(500), 4000, 2, 1); - let tx2 = create_test_tx(MicroTari(10_000), MicroTari(300), 3000, 2, 1); - let tx3 = create_test_tx(MicroTari(10_000), MicroTari(100), 2500, 2, 1); - let tx4 = create_test_tx(MicroTari(10_000), MicroTari(200), 1000, 2, 1); - let tx5 = create_test_tx(MicroTari(10_000), MicroTari(500), 2000, 2, 1); - let tx6 = create_test_tx(MicroTari(10_000), MicroTari(600), 5500, 2, 1); - - let mut reorg_pool = ReorgPool::new(ReorgPoolConfig { + let network = Network::LocalNet; + let consensus_constants = network.create_consensus_constants(); + let tx1 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(50), lock: 4000, inputs: 2, outputs: 1).0); + let tx2 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(30), lock: 3000, inputs: 2, outputs: 1).0); + let tx3 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(20), lock: 2500, inputs: 2, outputs: 1).0); + let tx4 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(20), lock: 1000, inputs: 2, outputs: 1).0); + let tx5 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(50), lock: 2000, inputs: 2, outputs: 1).0); + let tx6 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(60), lock: 5500, inputs: 2, outputs: 1).0); + + let reorg_pool = ReorgPool::new(ReorgPoolConfig { storage_capacity: 5, tx_ttl: Duration::from_millis(50), }); - reorg_pool.insert_txs(vec![ - tx1.clone(), - tx2.clone(), - tx3.clone(), - tx4.clone(), - tx5.clone(), - tx6.clone(), - ]); + reorg_pool + .insert_txs(vec![ + tx1.clone(), + tx2.clone(), + tx3.clone(), + tx4.clone(), + tx5.clone(), + tx6.clone(), + ]) + .unwrap(); // Oldest transaction tx1 is removed to make space for new incoming transactions - assert_eq!(reorg_pool.len(), 5); + assert_eq!(reorg_pool.len().unwrap(), 5); assert_eq!( - reorg_pool.has_tx_with_excess_sig(&tx1.body.kernels[0].excess_sig), + reorg_pool + .has_tx_with_excess_sig(&tx1.body.kernels()[0].excess_sig) + .unwrap(), false ); - assert_eq!(reorg_pool.has_tx_with_excess_sig(&tx2.body.kernels[0].excess_sig), true); - assert_eq!(reorg_pool.has_tx_with_excess_sig(&tx3.body.kernels[0].excess_sig), true); - assert_eq!(reorg_pool.has_tx_with_excess_sig(&tx4.body.kernels[0].excess_sig), true); - assert_eq!(reorg_pool.has_tx_with_excess_sig(&tx5.body.kernels[0].excess_sig), true); - assert_eq!(reorg_pool.has_tx_with_excess_sig(&tx6.body.kernels[0].excess_sig), true); + assert_eq!( + reorg_pool + .has_tx_with_excess_sig(&tx2.body.kernels()[0].excess_sig) + .unwrap(), + true + ); + assert_eq!( + reorg_pool + .has_tx_with_excess_sig(&tx3.body.kernels()[0].excess_sig) + .unwrap(), + true + ); + assert_eq!( + reorg_pool + .has_tx_with_excess_sig(&tx4.body.kernels()[0].excess_sig) + .unwrap(), + true + ); + assert_eq!( + reorg_pool + .has_tx_with_excess_sig(&tx5.body.kernels()[0].excess_sig) + .unwrap(), + true + ); + assert_eq!( + reorg_pool + .has_tx_with_excess_sig(&tx6.body.kernels()[0].excess_sig) + .unwrap(), + true + ); let reorg_blocks = vec![ - create_test_block(3000, vec![tx3.clone(), tx4.clone()]), - create_test_block(4000, vec![tx1.clone(), tx2.clone()]), + create_orphan_block(3000, vec![(*tx3).clone(), (*tx4).clone()], &consensus_constants), + create_orphan_block(4000, vec![(*tx1).clone(), (*tx2).clone()], &consensus_constants), ]; - let removed_txs = reorg_pool.scan_for_and_remove_reorged_txs(reorg_blocks); + let removed_txs = reorg_pool + .remove_reorged_txs_and_discard_double_spends(reorg_blocks, &vec![]) + .unwrap(); assert_eq!(removed_txs.len(), 3); - assert!(removed_txs.iter().any(|tx| **tx == tx2)); - assert!(removed_txs.iter().any(|tx| **tx == tx3)); - assert!(removed_txs.iter().any(|tx| **tx == tx4)); + assert!(removed_txs.contains(&tx2)); + assert!(removed_txs.contains(&tx3)); + assert!(removed_txs.contains(&tx4)); - assert_eq!(reorg_pool.len(), 2); + assert_eq!(reorg_pool.len().unwrap(), 2); assert_eq!( - reorg_pool.has_tx_with_excess_sig(&tx1.body.kernels[0].excess_sig), + reorg_pool + .has_tx_with_excess_sig(&tx1.body.kernels()[0].excess_sig) + .unwrap(), false ); assert_eq!( - reorg_pool.has_tx_with_excess_sig(&tx2.body.kernels[0].excess_sig), + reorg_pool + .has_tx_with_excess_sig(&tx2.body.kernels()[0].excess_sig) + .unwrap(), false ); assert_eq!( - reorg_pool.has_tx_with_excess_sig(&tx3.body.kernels[0].excess_sig), + reorg_pool + .has_tx_with_excess_sig(&tx3.body.kernels()[0].excess_sig) + .unwrap(), false ); assert_eq!( - reorg_pool.has_tx_with_excess_sig(&tx4.body.kernels[0].excess_sig), + reorg_pool + .has_tx_with_excess_sig(&tx4.body.kernels()[0].excess_sig) + .unwrap(), false ); - assert_eq!(reorg_pool.has_tx_with_excess_sig(&tx5.body.kernels[0].excess_sig), true); - assert_eq!(reorg_pool.has_tx_with_excess_sig(&tx6.body.kernels[0].excess_sig), true); + assert_eq!( + reorg_pool + .has_tx_with_excess_sig(&tx5.body.kernels()[0].excess_sig) + .unwrap(), + true + ); + assert_eq!( + reorg_pool + .has_tx_with_excess_sig(&tx6.body.kernels()[0].excess_sig) + .unwrap(), + true + ); } } diff --git a/base_layer/core/src/mempool/reorg_pool/reorg_pool_storage.rs b/base_layer/core/src/mempool/reorg_pool/reorg_pool_storage.rs new file mode 100644 index 0000000000..e2827a832c --- /dev/null +++ b/base_layer/core/src/mempool/reorg_pool/reorg_pool_storage.rs @@ -0,0 +1,127 @@ +// Copyright 2019 The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + blocks::Block, + mempool::reorg_pool::reorg_pool::ReorgPoolConfig, + transactions::{transaction::Transaction, types::Signature}, +}; +use log::*; +use std::sync::Arc; +use ttl_cache::TtlCache; +pub const LOG_TARGET: &str = "c::mp::reorg_pool::reorg_pool_storage"; + +/// Reorg makes use of ReorgPoolStorage to provide thread save access to its TtlCache. +/// The ReorgPoolStorage consists of all transactions that have recently been added to blocks. +/// When a potential blockchain reorganization occurs the transactions can be recovered from the ReorgPool and can be +/// added back into the UnconfirmedPool. Transactions in the ReOrg pool have a limited Time-to-live and will be removed +/// from the pool when the Time-to-live thresholds is reached. Also, when the capacity of the pool has been reached, the +/// oldest transactions will be removed to make space for incoming transactions. +pub struct ReorgPoolStorage { + config: ReorgPoolConfig, + txs_by_signature: TtlCache>, +} + +impl ReorgPoolStorage { + /// Create a new ReorgPoolStorage with the specified configuration + pub fn new(config: ReorgPoolConfig) -> Self { + Self { + config, + txs_by_signature: TtlCache::new(config.storage_capacity), + } + } + + /// Insert a new transaction into the ReorgPoolStorage. Published transactions will have a limited Time-to-live in + /// the ReorgPoolStorage and will be discarded once the Time-to-live threshold has been reached. + pub fn insert(&mut self, tx: Arc) { + let tx_key = tx.body.kernels()[0].excess_sig.clone(); + trace!(target: LOG_TARGET, "Inserting tx into reorg pool: {:?}", tx_key,); + let _ = self.txs_by_signature.insert(tx_key, tx, self.config.tx_ttl); + } + + /// Insert a set of new transactions into the ReorgPoolStorage + pub fn insert_txs(&mut self, txs: Vec>) { + for tx in txs.into_iter() { + self.insert(tx); + } + } + + /// Check if a transaction is stored in the ReorgPoolStorage + pub fn has_tx_with_excess_sig(&self, excess_sig: &Signature) -> bool { + self.txs_by_signature.contains_key(excess_sig) + } + + /// Remove double-spends from the ReorgPool. These transactions were orphaned by the provided published + /// block. Check if any of the transactions in the ReorgPool has inputs that was spent by the provided + /// published block. + fn discard_double_spends(&mut self, published_block: &Block) { + let mut removed_tx_keys: Vec = Vec::new(); + for (tx_key, ptx) in self.txs_by_signature.iter() { + for input in ptx.body.inputs() { + if published_block.body.inputs().contains(input) { + removed_tx_keys.push(tx_key.clone()); + } + } + } + + for tx_key in &removed_tx_keys { + trace!(target: LOG_TARGET, "Removed double spends: {:?}", tx_key); + self.txs_by_signature.remove(&tx_key); + } + } + + /// Remove the transactions from the ReorgPoolStorage that were used in provided removed blocks. The transactions + /// can be resubmitted to the Unconfirmed Pool. + pub fn remove_reorged_txs_and_discard_double_spends( + &mut self, + removed_blocks: Vec, + new_blocks: &Vec, + ) -> Vec> + { + for block in new_blocks { + self.discard_double_spends(block); + } + + let mut removed_txs: Vec> = Vec::new(); + for block in &removed_blocks { + for kernel in block.body.kernels() { + if let Some(removed_tx) = self.txs_by_signature.remove(&kernel.excess_sig) { + trace!(target: LOG_TARGET, "Removing tx from reorg pool: {:?}", removed_tx); + removed_txs.push(removed_tx); + } + } + } + removed_txs + } + + /// Returns the total number of published transactions stored in the ReorgPoolStorage + pub fn len(&mut self) -> usize { + self.txs_by_signature.iter().count() + } + + /// Returns the total weight of all transactions stored in the pool. + pub fn calculate_weight(&mut self) -> u64 { + self.txs_by_signature + .iter() + .fold(0, |weight, (_, tx)| weight + tx.calculate_weight()) + } +} diff --git a/base_layer/p2p/src/services/comms_outbound/messages.rs b/base_layer/core/src/mempool/service/error.rs similarity index 68% rename from base_layer/p2p/src/services/comms_outbound/messages.rs rename to base_layer/core/src/mempool/service/error.rs index 087758ae9c..c5509d3954 100644 --- a/base_layer/p2p/src/services/comms_outbound/messages.rs +++ b/base_layer/core/src/mempool/service/error.rs @@ -20,26 +20,25 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use tari_comms::{ - message::{Frame, MessageEnvelope, MessageFlags}, - outbound_message_service::BroadcastStrategy, -}; +use crate::mempool::MempoolError; +use derive_error::Error; +use tari_comms_dht::outbound::DhtOutboundError; +use tari_service_framework::reply_channel::TransportChannelError; -/// Represents requests to the CommsOutboundService -pub enum CommsOutboundRequest { - /// Send a message using the given broadcast strategy - SendMsg { - broadcast_strategy: BroadcastStrategy, - flags: MessageFlags, - body: Box, - }, - /// Forward a message envelope - Forward { - broadcast_strategy: BroadcastStrategy, - message_envelope: Box, - }, +#[derive(Debug, Error)] +pub enum MempoolServiceError { + DhtOutboundError(DhtOutboundError), + #[error(msg_embedded, no_from, non_std)] + InvalidRequest(String), + #[error(msg_embedded, no_from, non_std)] + InvalidResponse(String), + RequestTimedOut, + NoBootstrapNodesConfigured, + #[error(non_std, no_from)] + OutboundMessageService(String), + MempoolError(MempoolError), + UnexpectedApiResponse, + TransportChannelError(TransportChannelError), + /// Failed to send broadcast message + BroadcastFailed, } - -/// Represents a response from the CommsOutboundService. Currently, there are no requests -/// which result in a value. -pub type CommsOutboundResponse = (); diff --git a/base_layer/core/src/mempool/service/inbound_handlers.rs b/base_layer/core/src/mempool/service/inbound_handlers.rs new file mode 100644 index 0000000000..ad1686591c --- /dev/null +++ b/base_layer/core/src/mempool/service/inbound_handlers.rs @@ -0,0 +1,100 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + base_node::comms_interface::BlockEvent, + chain_storage::{BlockAddResult, BlockchainBackend}, + mempool::{ + service::{MempoolRequest, MempoolResponse, MempoolServiceError, OutboundMempoolServiceInterface}, + Mempool, + }, + transactions::transaction::Transaction, +}; +use log::*; +use std::sync::Arc; +use tari_comms::types::CommsPublicKey; + +pub const LOG_TARGET: &str = "c::mp::service::inbound_handlers"; + +/// The MempoolInboundHandlers is used to handle all received inbound mempool requests and transactions from remote +/// nodes. +pub struct MempoolInboundHandlers +where T: BlockchainBackend +{ + mempool: Mempool, + outbound_nmi: OutboundMempoolServiceInterface, +} + +impl MempoolInboundHandlers +where T: BlockchainBackend +{ + /// Construct the MempoolInboundHandlers. + pub fn new(mempool: Mempool, outbound_nmi: OutboundMempoolServiceInterface) -> Self { + Self { mempool, outbound_nmi } + } + + /// Handle inbound Mempool service requests from remote nodes and local services. + pub async fn handle_request(&self, request: &MempoolRequest) -> Result { + // TODO: make mempool calls async + debug!(target: LOG_TARGET, "request received for mempool: {:?}", request); + match request { + MempoolRequest::GetStats => Ok(MempoolResponse::Stats(self.mempool.stats()?)), + MempoolRequest::GetTxStateWithExcessSig(excess_sig) => Ok(MempoolResponse::TxStorage( + self.mempool.has_tx_with_excess_sig(excess_sig)?, + )), + } + } + + /// Handle inbound transactions from remote wallets and local services. + pub async fn handle_transaction( + &mut self, + tx: &Transaction, + source_peer: Option, + ) -> Result<(), MempoolServiceError> + { + self.mempool.insert(Arc::new(tx.clone()))?; + let exclude_list = if let Some(peer) = source_peer { + vec![peer] + } else { + Vec::new() + }; + self.outbound_nmi.propagate_tx(tx.clone(), exclude_list).await?; + + Ok(()) + } + + /// Handle inbound block events from the local base node service. + pub async fn handle_block_event(&mut self, block_event: &BlockEvent) -> Result<(), MempoolServiceError> { + match block_event { + BlockEvent::Verified((block, BlockAddResult::Ok)) => { + self.mempool.process_published_block(block)?; + }, + BlockEvent::Verified((_, BlockAddResult::ChainReorg((removed_blocks, added_blocks)))) => { + self.mempool + .process_reorg(removed_blocks.to_vec(), added_blocks.to_vec())?; + }, + BlockEvent::Verified(_) | BlockEvent::Invalid(_) => {}, + } + + Ok(()) + } +} diff --git a/base_layer/core/src/mempool/service/initializer.rs b/base_layer/core/src/mempool/service/initializer.rs new file mode 100644 index 0000000000..f5cb5d8602 --- /dev/null +++ b/base_layer/core/src/mempool/service/initializer.rs @@ -0,0 +1,197 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + base_node::comms_interface::LocalNodeCommsInterface, + chain_storage::BlockchainBackend, + mempool::{ + mempool::Mempool, + proto, + service::{ + inbound_handlers::MempoolInboundHandlers, + outbound_interface::OutboundMempoolServiceInterface, + service::{MempoolService, MempoolStreams}, + }, + MempoolServiceConfig, + }, + transactions::{proto::types::Transaction as ProtoTransaction, transaction::Transaction}, +}; +use futures::{channel::mpsc::unbounded as futures_mpsc_channel_unbounded, future, Future, Stream, StreamExt}; +use log::*; +use std::{convert::TryFrom, sync::Arc}; +use tari_comms_dht::outbound::OutboundMessageRequester; +use tari_p2p::{ + comms_connector::PeerMessage, + domain_message::DomainMessage, + services::utils::{map_decode, ok_or_skip_result}, + tari_message::TariMessageType, +}; +use tari_pubsub::TopicSubscriptionFactory; +use tari_service_framework::{ + handles::ServiceHandlesFuture, + reply_channel, + ServiceInitializationError, + ServiceInitializer, +}; +use tari_shutdown::ShutdownSignal; +use tokio::runtime; + +const LOG_TARGET: &str = "base_node::mempool_service::initializer"; + +/// Initializer for the Mempool service and service future. +pub struct MempoolServiceInitializer +where T: BlockchainBackend +{ + inbound_message_subscription_factory: Arc>>, + mempool: Mempool, + config: MempoolServiceConfig, +} + +impl MempoolServiceInitializer +where T: BlockchainBackend +{ + /// Create a new MempoolServiceInitializer from the inbound message subscriber. + pub fn new( + inbound_message_subscription_factory: Arc>>, + mempool: Mempool, + config: MempoolServiceConfig, + ) -> Self + { + Self { + inbound_message_subscription_factory, + mempool, + config, + } + } + + /// Get a stream for inbound Mempool service request messages + fn inbound_request_stream(&self) -> impl Stream> { + self.inbound_message_subscription_factory + .get_subscription(TariMessageType::MempoolRequest) + .map(map_decode::) + .filter_map(ok_or_skip_result) + } + + /// Get a stream for inbound Mempool service response messages + fn inbound_response_stream(&self) -> impl Stream> { + self.inbound_message_subscription_factory + .get_subscription(TariMessageType::MempoolResponse) + .map(map_decode::) + .filter_map(ok_or_skip_result) + } + + /// Create a stream of 'New Transaction` messages + fn inbound_transaction_stream(&self) -> impl Stream> { + self.inbound_message_subscription_factory + .get_subscription(TariMessageType::NewTransaction) + .filter_map(extract_transaction) + } +} + +async fn extract_transaction(msg: Arc) -> Option> { + match msg.decode_message::() { + Err(e) => { + warn!( + target: LOG_TARGET, + "Could not decode inbound transaction message. {}", + e.to_string() + ); + None + }, + Ok(tx) => { + let tx = match Transaction::try_from(tx) { + Err(e) => { + let origin = msg.origin_public_key(); + warn!( + target: LOG_TARGET, + "Inbound transaction message from {} was ill-formed. {}", origin, e + ); + return None; + }, + Ok(b) => b, + }; + Some(DomainMessage { + source_peer: msg.source_peer.clone(), + dht_header: msg.dht_header.clone(), + inner: tx, + }) + }, + } +} + +impl ServiceInitializer for MempoolServiceInitializer +where T: BlockchainBackend + 'static +{ + type Future = impl Future>; + + fn initialize( + &mut self, + executor: runtime::Handle, + handles_fut: ServiceHandlesFuture, + shutdown: ShutdownSignal, + ) -> Self::Future + { + // Create streams for receiving Mempool service requests and response messages from comms + let inbound_request_stream = self.inbound_request_stream(); + let inbound_response_stream = self.inbound_response_stream(); + let inbound_transaction_stream = self.inbound_transaction_stream(); + // Connect MempoolOutboundServiceHandle to MempoolService + let (outbound_tx_sender_service, outbound_tx_stream) = futures_mpsc_channel_unbounded(); + let (outbound_request_sender_service, outbound_request_stream) = reply_channel::unbounded(); + let outbound_mp_interface = + OutboundMempoolServiceInterface::new(outbound_request_sender_service, outbound_tx_sender_service); + let executer_clone = executor.clone(); // Give MempoolService access to the executor + let config = self.config; + let mempool = self.mempool.clone(); + let inbound_handlers = MempoolInboundHandlers::new(mempool, outbound_mp_interface.clone()); + // Register handle to OutboundMempoolServiceInterface before waiting for handles to be ready + handles_fut.register(outbound_mp_interface); + + executor.spawn(async move { + let handles = handles_fut.await; + + let outbound_message_service = handles + .get_handle::() + .expect("OutboundMessageRequester handle required for MempoolService"); + + let base_node = handles + .get_handle::() + .expect("LocalNodeCommsInterface required to initialize ChainStateSyncService"); + + let streams = MempoolStreams::new( + outbound_request_stream, + outbound_tx_stream, + inbound_request_stream, + inbound_response_stream, + inbound_transaction_stream, + base_node.get_block_event_stream(), + ); + let service = + MempoolService::new(executer_clone, outbound_message_service, inbound_handlers, config).start(streams); + futures::pin_mut!(service); + future::select(service, shutdown).await; + info!(target: LOG_TARGET, "Mempool Service shutdown"); + }); + + future::ready(Ok(())) + } +} diff --git a/base_layer/core/src/mempool/service/mod.rs b/base_layer/core/src/mempool/service/mod.rs new file mode 100644 index 0000000000..ddbb4df685 --- /dev/null +++ b/base_layer/core/src/mempool/service/mod.rs @@ -0,0 +1,42 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +cfg_if! { + if #[cfg(feature = "base_node")] { +mod error; +mod inbound_handlers; +mod initializer; +mod outbound_interface; +mod service; +// Public re-exports +pub use error::MempoolServiceError; +pub use initializer::MempoolServiceInitializer; +pub use outbound_interface::OutboundMempoolServiceInterface; +pub use service::MempoolService; + } +} + +mod request; +mod response; + +pub use request::{MempoolRequest, MempoolServiceRequest, RequestKey}; +pub use response::{MempoolResponse, MempoolServiceResponse}; diff --git a/base_layer/core/src/mempool/service/outbound_interface.rs b/base_layer/core/src/mempool/service/outbound_interface.rs new file mode 100644 index 0000000000..47e2c5dc77 --- /dev/null +++ b/base_layer/core/src/mempool/service/outbound_interface.rs @@ -0,0 +1,103 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + mempool::{ + service::{MempoolRequest, MempoolResponse, MempoolServiceError}, + StatsResponse, + TxStorageResponse, + }, + transactions::{transaction::Transaction, types::Signature}, +}; +use futures::channel::mpsc::UnboundedSender; +use log::*; +use tari_comms::types::CommsPublicKey; +use tari_service_framework::reply_channel::SenderService; +use tower_service::Service; +pub const LOG_TARGET: &str = "c::mp::service::outbound_interface"; + +/// The OutboundMempoolServiceInterface provides an interface to request information from the Mempools of remote Base +/// nodes. +#[derive(Clone)] +pub struct OutboundMempoolServiceInterface { + request_sender: SenderService>, + tx_sender: UnboundedSender<(Transaction, Vec)>, +} + +impl OutboundMempoolServiceInterface { + /// Construct a new OutboundMempoolServiceInterface with the specified SenderService. + pub fn new( + request_sender: SenderService>, + tx_sender: UnboundedSender<(Transaction, Vec)>, + ) -> Self + { + Self { + request_sender, + tx_sender, + } + } + + /// Request the stats from the mempool of a remote base node. + pub async fn get_stats(&mut self) -> Result { + if let MempoolResponse::Stats(stats) = self.request_sender.call(MempoolRequest::GetStats).await?? { + trace!(target: LOG_TARGET, "Mempool stats requested: {:?}", stats,); + Ok(stats) + } else { + Err(MempoolServiceError::UnexpectedApiResponse) + } + } + + /// Transmit a transaction to remote base nodes, excluding the provided peers. + pub async fn propagate_tx( + &mut self, + transaction: Transaction, + exclude_peers: Vec, + ) -> Result<(), MempoolServiceError> + { + self.tx_sender + .unbounded_send((transaction, exclude_peers)) + .or_else(|e| { + { + error!(target: LOG_TARGET, "Could not broadcast transaction. {:?}", e); + Err(e) + } + .map_err(|_| MempoolServiceError::BroadcastFailed) + }) + } + + /// Check if the specified transaction is stored in the mempool of a remote base node. + pub async fn get_tx_state_with_excess_sig( + &mut self, + excess_sig: Signature, + ) -> Result + { + if let MempoolResponse::TxStorage(tx_storage_response) = self + .request_sender + .call(MempoolRequest::GetTxStateWithExcessSig(excess_sig)) + .await?? + { + Ok(tx_storage_response) + } else { + Err(MempoolServiceError::UnexpectedApiResponse) + } + } +} diff --git a/comms/tests/support/macros.rs b/base_layer/core/src/mempool/service/request.rs similarity index 66% rename from comms/tests/support/macros.rs rename to base_layer/core/src/mempool/service/request.rs index f87b7e601c..aeaae1ae22 100644 --- a/comms/tests/support/macros.rs +++ b/base_layer/core/src/mempool/service/request.rs @@ -20,23 +20,29 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -macro_rules! acquire_lock { - ($e:expr, $m:ident) => { - match $e.$m() { - Ok(lock) => lock, - Err(poisoned) => poisoned.into_inner(), - } - }; +use crate::transactions::types::Signature; +use rand::RngCore; +use serde::{Deserialize, Serialize}; + +pub type RequestKey = u64; // TODO: BaseNodeService and MempoolService uses RequestKey + +/// Generate a new random request key to uniquely identify a request and its corresponding responses. +#[cfg(feature = "mempool_proto")] +pub fn generate_request_key(rng: &mut R) -> RequestKey +where R: RngCore { + rng.next_u64() } -macro_rules! acquire_write_lock { - ($e:expr) => { - acquire_lock!($e, write) - }; +/// API Request enum for Mempool requests. +#[derive(Debug, Serialize, Deserialize)] +pub enum MempoolRequest { + GetStats, + GetTxStateWithExcessSig(Signature), } -macro_rules! acquire_read_lock { - ($e:expr) => { - acquire_lock!($e, read) - }; +/// Request type for a received MempoolService request. +#[derive(Debug, Serialize, Deserialize)] +pub struct MempoolServiceRequest { + pub request_key: RequestKey, + pub request: MempoolRequest, } diff --git a/base_layer/p2p/src/services/liveness/messages.rs b/base_layer/core/src/mempool/service/response.rs similarity index 76% rename from base_layer/p2p/src/services/liveness/messages.rs rename to base_layer/core/src/mempool/service/response.rs index a4332d7c9b..fdb2e6e98d 100644 --- a/base_layer/p2p/src/services/liveness/messages.rs +++ b/base_layer/core/src/mempool/service/response.rs @@ -20,30 +20,19 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::mempool::{service::RequestKey, StatsResponse, TxStorageResponse}; use serde::{Deserialize, Serialize}; -use tari_comms::types::CommsPublicKey; -/// API Request enum -#[derive(Debug)] -pub enum LivenessRequest { - /// Send a ping to the given public key - SendPing(CommsPublicKey), - /// Retrieve the total number of pings received - GetPingCount, - /// Retrieve the total number of pongs received - GetPongCount, -} - -/// API Response enum -#[derive(Debug)] -pub enum LivenessResponse { - PingSent, - Count(usize), +/// API Response enum for Mempool responses. +#[derive(Debug, Serialize, Deserialize)] +pub enum MempoolResponse { + Stats(StatsResponse), + TxStorage(TxStorageResponse), } -/// The PingPong message +/// Response type for a received MempoolService requests #[derive(Debug, Serialize, Deserialize)] -pub enum PingPong { - Ping, - Pong, +pub struct MempoolServiceResponse { + pub request_key: RequestKey, + pub response: MempoolResponse, } diff --git a/base_layer/core/src/mempool/service/service.rs b/base_layer/core/src/mempool/service/service.rs new file mode 100644 index 0000000000..645d3739e9 --- /dev/null +++ b/base_layer/core/src/mempool/service/service.rs @@ -0,0 +1,422 @@ +// Copyright 2019 The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + base_node::comms_interface::BlockEvent, + chain_storage::BlockchainBackend, + mempool::{ + proto, + service::{ + error::MempoolServiceError, + inbound_handlers::MempoolInboundHandlers, + request::{generate_request_key, RequestKey}, + MempoolRequest, + MempoolResponse, + }, + MempoolServiceConfig, + }, + transactions::{proto::types::Transaction as ProtoTransaction, transaction::Transaction}, +}; +use futures::{ + channel::{ + mpsc::{channel, Receiver, Sender, UnboundedReceiver}, + oneshot::Sender as OneshotSender, + }, + pin_mut, + stream::StreamExt, + SinkExt, + Stream, +}; +use log::*; +use rand::rngs::OsRng; +use std::{collections::HashMap, convert::TryInto, time::Duration}; +use tari_broadcast_channel::Subscriber; +use tari_comms::types::CommsPublicKey; +use tari_comms_dht::{ + domain_message::OutboundDomainMessage, + envelope::NodeDestination, + outbound::{OutboundEncryption, OutboundMessageRequester}, +}; +use tari_p2p::{domain_message::DomainMessage, tari_message::TariMessageType}; +use tari_service_framework::RequestContext; +use tokio::runtime; + +const LOG_TARGET: &str = "c::mempool::service::service"; + +/// A convenience struct to hold all the Mempool service streams +pub struct MempoolStreams { + outbound_request_stream: SOutReq, + outbound_tx_stream: UnboundedReceiver<(Transaction, Vec)>, + inbound_request_stream: SInReq, + inbound_response_stream: SInRes, + inbound_transaction_stream: STxIn, + block_event_stream: Subscriber, +} + +impl MempoolStreams +where + SOutReq: Stream>>, + SInReq: Stream>, + SInRes: Stream>, + STxIn: Stream>, +{ + pub fn new( + outbound_request_stream: SOutReq, + outbound_tx_stream: UnboundedReceiver<(Transaction, Vec)>, + inbound_request_stream: SInReq, + inbound_response_stream: SInRes, + inbound_transaction_stream: STxIn, + block_event_stream: Subscriber, + ) -> Self + { + Self { + outbound_request_stream, + outbound_tx_stream, + inbound_request_stream, + inbound_response_stream, + inbound_transaction_stream, + block_event_stream, + } + } +} + +/// The Mempool Service is responsible for handling inbound requests and responses and for sending new requests to the +/// Mempools of remote Base nodes. +pub struct MempoolService { + executor: runtime::Handle, + outbound_message_service: OutboundMessageRequester, + inbound_handlers: MempoolInboundHandlers, + waiting_requests: HashMap>>>, + timeout_sender: Sender, + timeout_receiver_stream: Option>, + config: MempoolServiceConfig, +} + +impl MempoolService +where B: BlockchainBackend +{ + pub fn new( + executor: runtime::Handle, + outbound_message_service: OutboundMessageRequester, + inbound_handlers: MempoolInboundHandlers, + config: MempoolServiceConfig, + ) -> Self + { + let (timeout_sender, timeout_receiver) = channel(100); + Self { + executor, + outbound_message_service, + inbound_handlers, + waiting_requests: HashMap::new(), + timeout_sender, + timeout_receiver_stream: Some(timeout_receiver), + config, + } + } + + pub async fn start( + mut self, + streams: MempoolStreams, + ) -> Result<(), MempoolServiceError> + where + SOutReq: Stream>>, + SInReq: Stream>, + SInRes: Stream>, + STxIn: Stream>, + { + let outbound_request_stream = streams.outbound_request_stream.fuse(); + pin_mut!(outbound_request_stream); + let outbound_tx_stream = streams.outbound_tx_stream.fuse(); + pin_mut!(outbound_tx_stream); + let inbound_request_stream = streams.inbound_request_stream.fuse(); + pin_mut!(inbound_request_stream); + let inbound_response_stream = streams.inbound_response_stream.fuse(); + pin_mut!(inbound_response_stream); + let inbound_transaction_stream = streams.inbound_transaction_stream.fuse(); + pin_mut!(inbound_transaction_stream); + let block_event_stream = streams.block_event_stream.fuse(); + pin_mut!(block_event_stream); + let timeout_receiver_stream = self + .timeout_receiver_stream + .take() + .expect("Mempool Service initialized without timeout_receiver_stream") + .fuse(); + pin_mut!(timeout_receiver_stream); + loop { + futures::select! { + // Outbound request messages from the OutboundMempoolServiceInterface + outbound_request_context = outbound_request_stream.select_next_some() => { + let (request, reply_tx) = outbound_request_context.split(); + let _ = self.handle_outbound_request(reply_tx,request).await.or_else(|err| { + error!(target: LOG_TARGET, "Failed to handle outbound request message: {:?}", err); + Err(err) + }); + }, + + // Outbound tx messages from the OutboundMempoolServiceInterface + outbound_tx_context = outbound_tx_stream.select_next_some() => { + let (tx, excluded_peers) = outbound_tx_context; + let _ = self.handle_outbound_tx(tx,excluded_peers).await.or_else(|err| { + error!(target: LOG_TARGET, "Failed to handle outbound tx message {:?}",err); + Err(err) + }); + }, + + // Incoming request messages from the Comms layer + domain_msg = inbound_request_stream.select_next_some() => { + let _ = self.handle_incoming_request(domain_msg).await.or_else(|err| { + error!(target: LOG_TARGET, "Failed to handle incoming request message: {:?}", err); + Err(err) + }); + }, + + // Incoming response messages from the Comms layer + domain_msg = inbound_response_stream.select_next_some() => { + let _ = self.handle_incoming_response(domain_msg.into_inner()).await.or_else(|err| { + error!(target: LOG_TARGET, "Failed to handle incoming response message: {:?}", err); + Err(err) + }); + }, + + // Incoming transaction messages from the Comms layer + transaction_msg = inbound_transaction_stream.select_next_some() => { + let _ = self.handle_incoming_transaction(transaction_msg).await.or_else(|err| { + error!(target: LOG_TARGET, "Failed to handle incoming transaction message: {:?}", err); + Err(err) + }); + } + + // Block events from local Base Node. + block_event = block_event_stream.select_next_some() => { + let _ = self.handle_block_event(&block_event).await.or_else(|err| { + error!(target: LOG_TARGET, "Failed to handle base node block event: {:?}", err); + Err(err) + }); + }, + + // Timeout events for waiting requests + timeout_request_key = timeout_receiver_stream.select_next_some() => { + let _ =self.handle_request_timeout(timeout_request_key).await.or_else(|err| { + error!(target: LOG_TARGET, "Failed to handle request timeout event: {:?}", err); + Err(err) + }); + }, + + complete => { + info!(target: LOG_TARGET, "Mempool service shutting down"); + break; + } + } + } + Ok(()) + } + + async fn handle_incoming_request( + &mut self, + domain_request_msg: DomainMessage, + ) -> Result<(), MempoolServiceError> + { + let (origin_public_key, inner_msg) = domain_request_msg.into_origin_and_inner(); + + // Convert proto::MempoolServiceRequest to a MempoolServiceRequest + let request = inner_msg.request.ok_or_else(|| { + MempoolServiceError::InvalidRequest("Received invalid mempool service request".to_string()) + })?; + + let response = self + .inbound_handlers + .handle_request(&request.try_into().map_err(MempoolServiceError::InvalidRequest)?) + .await?; + + let message = proto::MempoolServiceResponse { + request_key: inner_msg.request_key, + response: Some(response.into()), + }; + + self.outbound_message_service + .send_direct( + origin_public_key, + OutboundEncryption::EncryptForPeer, + OutboundDomainMessage::new(TariMessageType::MempoolResponse, message), + ) + .await?; + + Ok(()) + } + + async fn handle_incoming_response( + &mut self, + incoming_response: proto::MempoolServiceResponse, + ) -> Result<(), MempoolServiceError> + { + let proto::MempoolServiceResponse { request_key, response } = incoming_response; + + match self.waiting_requests.remove(&request_key) { + Some(mut reply_tx) => { + if let Some(reply_tx) = reply_tx.take() { + let response = response.and_then(|r| r.try_into().ok()).ok_or_else(|| { + MempoolServiceError::InvalidResponse("Received an invalid Mempool response".to_string()) + })?; + let _ = reply_tx.send(Ok(response).or_else(|resp| { + error!( + target: LOG_TARGET, + "Failed to send outbound request from Mempool service" + ); + Err(resp) + })); + } + }, + None => { + info!(target: LOG_TARGET, "Discard incoming unmatched response"); + }, + } + + Ok(()) + } + + async fn handle_outbound_request( + &mut self, + reply_tx: OneshotSender>, + request: MempoolRequest, + ) -> Result<(), MempoolServiceError> + { + let request_key = generate_request_key(&mut OsRng); + let service_request = proto::MempoolServiceRequest { + request_key, + request: Some(request.into()), + }; + + let send_result = self + .outbound_message_service + .send_random( + 1, + NodeDestination::Unknown, + OutboundEncryption::EncryptForPeer, + OutboundDomainMessage::new(TariMessageType::MempoolRequest, service_request), + ) + .await + .or_else(|e| { + error!(target: LOG_TARGET, "mempool outbound request failure. {:?}", e); + Err(e) + }) + .map_err(|e| MempoolServiceError::OutboundMessageService(e.to_string()))?; + + match send_result.resolve_ok().await { + Some(tags) if !tags.is_empty() => { + // Spawn timeout and wait for matching response to arrive + self.waiting_requests.insert(request_key, Some(reply_tx)); + self.spawn_request_timeout(request_key, self.config.request_timeout) + .await; + }, + Some(_) => { + let _ = reply_tx.send(Err(MempoolServiceError::NoBootstrapNodesConfigured).or_else(|resp| { + error!( + target: LOG_TARGET, + "Failed to send outbound request from Mempool service as no bootstrap nodes were configured" + ); + Err(resp) + })); + }, + None => { + let _ = reply_tx + .send(Err(MempoolServiceError::BroadcastFailed)) + .or_else(|resp| { + error!( + target: LOG_TARGET, + "Failed to send outbound request from Mempool service because of a failure in DHT \ + broadcast" + ); + Err(resp) + }); + }, + } + + Ok(()) + } + + async fn handle_incoming_transaction( + &mut self, + domain_transaction_msg: DomainMessage, + ) -> Result<(), MempoolServiceError> + { + let DomainMessage::<_> { source_peer, inner, .. } = domain_transaction_msg; + + self.inbound_handlers + .handle_transaction(&inner, Some(source_peer.public_key)) + .await?; + + Ok(()) + } + + async fn handle_request_timeout(&mut self, request_key: RequestKey) -> Result<(), MempoolServiceError> { + if let Some(mut waiting_request) = self.waiting_requests.remove(&request_key) { + if let Some(reply_tx) = waiting_request.take() { + let reply_msg = Err(MempoolServiceError::RequestTimedOut); + let _ = reply_tx.send(reply_msg.or_else(|resp| { + error!( + target: LOG_TARGET, + "Failed to send outbound request from Mempool service" + ); + Err(resp) + })); + } + } + Ok(()) + } + + async fn handle_outbound_tx( + &mut self, + tx: Transaction, + exclude_peers: Vec, + ) -> Result<(), MempoolServiceError> + { + self.outbound_message_service + .propagate( + NodeDestination::Unknown, + OutboundEncryption::EncryptForPeer, + exclude_peers, + OutboundDomainMessage::new(TariMessageType::NewTransaction, ProtoTransaction::from(tx)), + ) + .await + .or_else(|e| { + error!(target: LOG_TARGET, "Handle outbound tx failure. {:?}", e); + Err(e) + }) + .map_err(|e| MempoolServiceError::OutboundMessageService(e.to_string())) + .map(|_| ()) + } + + /// Handle block events from local base node service. + async fn handle_block_event(&mut self, block_event: &BlockEvent) -> Result<(), MempoolServiceError> { + self.inbound_handlers.handle_block_event(block_event).await?; + + Ok(()) + } + + async fn spawn_request_timeout(&self, request_key: RequestKey, timeout: Duration) { + let mut timeout_sender = self.timeout_sender.clone(); + self.executor.spawn(async move { + tokio::time::delay_for(timeout).await; + let _ = timeout_sender.send(request_key).await; + }); + } +} diff --git a/base_layer/core/src/mempool/unconfirmed_pool/error.rs b/base_layer/core/src/mempool/unconfirmed_pool/error.rs index 3c62d15a26..b611005ad5 100644 --- a/base_layer/core/src/mempool/unconfirmed_pool/error.rs +++ b/base_layer/core/src/mempool/unconfirmed_pool/error.rs @@ -27,7 +27,8 @@ use derive_error::Error; pub enum UnconfirmedPoolError { /// The HashMap and BTreeMap are out of sync StorageOutofSync, - /// The Thread Safety has been breached and the data access has become poisoned - PoisonedAccess, + /// A problem has been encountered with the storage backend. + #[error(non_std, no_from)] + BackendError(String), PriorityError(PriorityError), } diff --git a/base_layer/core/src/mempool/unconfirmed_pool/unconfirmed_pool.rs b/base_layer/core/src/mempool/unconfirmed_pool/unconfirmed_pool.rs index 8fb8afce7a..7797900d72 100644 --- a/base_layer/core/src/mempool/unconfirmed_pool/unconfirmed_pool.rs +++ b/base_layer/core/src/mempool/unconfirmed_pool/unconfirmed_pool.rs @@ -22,10 +22,11 @@ use crate::{ blocks::Block, - consts::{MEMPOOL_UNCONFIRMED_POOL_STORAGE_CAPACITY, MEMPOOL_UNCONFIRMED_POOL_WEIGHT_TRANSACTION_SKIP_COUNT}, - mempool::unconfirmed_pool::{UnconfirmedPoolError, UnconfirmedPoolStorage}, - transaction::Transaction, - types::Signature, + mempool::{ + consts::{MEMPOOL_UNCONFIRMED_POOL_STORAGE_CAPACITY, MEMPOOL_UNCONFIRMED_POOL_WEIGHT_TRANSACTION_SKIP_COUNT}, + unconfirmed_pool::{UnconfirmedPoolError, UnconfirmedPoolStorage}, + }, + transactions::{transaction::Transaction, types::Signature}, }; use std::sync::{Arc, RwLock}; @@ -51,32 +52,32 @@ impl Default for UnconfirmedPoolConfig { /// The Unconfirmed Transaction Pool consists of all unconfirmed transactions that are ready to be included in a block /// and they are prioritised according to the priority metric. pub struct UnconfirmedPool { - pool_storage: RwLock, + pool_storage: Arc>, } impl UnconfirmedPool { /// Create a new UnconfirmedPool with the specified configuration pub fn new(config: UnconfirmedPoolConfig) -> Self { Self { - pool_storage: RwLock::new(UnconfirmedPoolStorage::new(config)), + pool_storage: Arc::new(RwLock::new(UnconfirmedPoolStorage::new(config))), } } /// Insert a new transaction into the UnconfirmedPool. Low priority transactions will be removed to make space for /// higher priority transactions. The lowest priority transactions will be removed when the maximum capacity is /// reached and the new transaction has a higher priority than the currently stored lowest priority transaction. - pub fn insert(&mut self, transaction: Transaction) -> Result<(), UnconfirmedPoolError> { + pub fn insert(&self, transaction: Arc) -> Result<(), UnconfirmedPoolError> { self.pool_storage .write() - .map_err(|_| UnconfirmedPoolError::PoisonedAccess)? + .map_err(|e| UnconfirmedPoolError::BackendError(e.to_string()))? .insert(transaction) } /// Insert a set of new transactions into the UnconfirmedPool - pub fn insert_txs(&mut self, transactions: Vec) -> Result<(), UnconfirmedPoolError> { + pub fn insert_txs(&self, transactions: Vec>) -> Result<(), UnconfirmedPoolError> { self.pool_storage .write() - .map_err(|_| UnconfirmedPoolError::PoisonedAccess)? + .map_err(|e| UnconfirmedPoolError::BackendError(e.to_string()))? .insert_txs(transactions) } @@ -85,7 +86,7 @@ impl UnconfirmedPool { Ok(self .pool_storage .read() - .map_err(|_| UnconfirmedPoolError::PoisonedAccess)? + .map_err(|e| UnconfirmedPoolError::BackendError(e.to_string()))? .has_tx_with_excess_sig(excess_sig)) } @@ -93,20 +94,21 @@ impl UnconfirmedPool { pub fn highest_priority_txs(&self, total_weight: u64) -> Result>, UnconfirmedPoolError> { self.pool_storage .read() - .map_err(|_| UnconfirmedPoolError::PoisonedAccess)? + .map_err(|e| UnconfirmedPoolError::BackendError(e.to_string()))? .highest_priority_txs(total_weight) } - /// Remove all published transactions from the UnconfirmedPool and discard all double spend transactions + /// Remove all published transactions from the UnconfirmedPool and discard all double spend transactions. + /// Returns a list of all transactions that were removed the unconfirmed pool as a result of appearing in the block. pub fn remove_published_and_discard_double_spends( - &mut self, + &self, published_block: &Block, ) -> Result>, UnconfirmedPoolError> { Ok(self .pool_storage .write() - .map_err(|_| UnconfirmedPoolError::PoisonedAccess)? + .map_err(|e| UnconfirmedPoolError::BackendError(e.to_string()))? .remove_published_and_discard_double_spends(published_block)) } @@ -115,38 +117,61 @@ impl UnconfirmedPool { Ok(self .pool_storage .read() - .map_err(|_| UnconfirmedPoolError::PoisonedAccess)? + .map_err(|e| UnconfirmedPoolError::BackendError(e.to_string()))? .len()) } + /// Returns all transaction stored in the UnconfirmedPool. + pub fn snapshot(&self) -> Result>, UnconfirmedPoolError> { + Ok(self + .pool_storage + .read() + .map_err(|e| UnconfirmedPoolError::BackendError(e.to_string()))? + .snapshot()) + } + + /// Returns the total weight of all transactions stored in the pool. + pub fn calculate_weight(&self) -> Result { + Ok(self + .pool_storage + .read() + .map_err(|e| UnconfirmedPoolError::BackendError(e.to_string()))? + .calculate_weight()) + } + #[cfg(test)] /// Checks the consistency status of the Hashmap and BtreeMap pub fn check_status(&self) -> Result { Ok(self .pool_storage .read() - .map_err(|_| UnconfirmedPoolError::PoisonedAccess)? + .map_err(|e| UnconfirmedPoolError::BackendError(e.to_string()))? .check_status()) } } +impl Clone for UnconfirmedPool { + fn clone(&self) -> Self { + UnconfirmedPool { + pool_storage: self.pool_storage.clone(), + } + } +} + #[cfg(test)] mod test { use super::*; - use crate::{ - tari_amount::MicroTari, - test_utils::builders::{create_test_block, create_test_tx}, - }; + use crate::{consensus::Network, helpers::create_orphan_block, transactions::tari_amount::MicroTari, tx}; #[test] fn test_insert_and_retrieve_highest_priority_txs() { - let tx1 = create_test_tx(MicroTari(5_000), MicroTari(500), 0, 2, 1); - let tx2 = create_test_tx(MicroTari(5_000), MicroTari(100), 0, 4, 1); - let tx3 = create_test_tx(MicroTari(5_000), MicroTari(1000), 0, 5, 1); - let tx4 = create_test_tx(MicroTari(5_000), MicroTari(200), 0, 3, 1); - let tx5 = create_test_tx(MicroTari(5_000), MicroTari(500), 0, 5, 1); + let tx1 = Arc::new(tx!(MicroTari(5_000), fee: MicroTari(50), inputs: 2, outputs: 1).0); + let tx2 = Arc::new(tx!(MicroTari(5_000), fee: MicroTari(20), inputs: 4, outputs: 1).0); + let tx3 = Arc::new(tx!(MicroTari(5_000), fee: MicroTari(100), inputs: 5, outputs: 1).0); + let tx4 = Arc::new(tx!(MicroTari(5_000), fee: MicroTari(30), inputs: 3, outputs: 1).0); + let tx5 = Arc::new(tx!(MicroTari(5_000), fee: MicroTari(50), inputs: 5, outputs: 1).0); - let mut unconfirmed_pool = UnconfirmedPool::new(UnconfirmedPoolConfig { + let unconfirmed_pool = UnconfirmedPool::new(UnconfirmedPoolConfig { storage_capacity: 4, weight_tx_skip_count: 3, }); @@ -156,31 +181,31 @@ mod test { // Check that lowest priority tx was removed to make room for new incoming transactions assert_eq!( unconfirmed_pool - .has_tx_with_excess_sig(&tx1.body.kernels[0].excess_sig) + .has_tx_with_excess_sig(&tx1.body.kernels()[0].excess_sig) .unwrap(), true ); assert_eq!( unconfirmed_pool - .has_tx_with_excess_sig(&tx2.body.kernels[0].excess_sig) + .has_tx_with_excess_sig(&tx2.body.kernels()[0].excess_sig) .unwrap(), false ); assert_eq!( unconfirmed_pool - .has_tx_with_excess_sig(&tx3.body.kernels[0].excess_sig) + .has_tx_with_excess_sig(&tx3.body.kernels()[0].excess_sig) .unwrap(), true ); assert_eq!( unconfirmed_pool - .has_tx_with_excess_sig(&tx4.body.kernels[0].excess_sig) + .has_tx_with_excess_sig(&tx4.body.kernels()[0].excess_sig) .unwrap(), true ); assert_eq!( unconfirmed_pool - .has_tx_with_excess_sig(&tx5.body.kernels[0].excess_sig) + .has_tx_with_excess_sig(&tx5.body.kernels()[0].excess_sig) .unwrap(), true ); @@ -188,9 +213,9 @@ mod test { let desired_weight = tx1.calculate_weight() + tx3.calculate_weight() + tx4.calculate_weight(); let selected_txs = unconfirmed_pool.highest_priority_txs(desired_weight).unwrap(); assert_eq!(selected_txs.len(), 3); - assert_eq!(selected_txs[0].body.kernels[0].fee, MicroTari(1000)); - assert_eq!(selected_txs[1].body.kernels[0].fee, MicroTari(500)); - assert_eq!(selected_txs[2].body.kernels[0].fee, MicroTari(200)); + assert!(selected_txs.contains(&tx1)); + assert!(selected_txs.contains(&tx3)); + assert!(selected_txs.contains(&tx4)); // Note that transaction tx5 could not be included as its weight was to big to fit into the remaining allocated // space, the second best transaction was then included @@ -199,14 +224,16 @@ mod test { #[test] fn test_remove_published_txs() { - let tx1 = create_test_tx(MicroTari(10_000), MicroTari(500), 0, 2, 1); - let tx2 = create_test_tx(MicroTari(10_000), MicroTari(100), 0, 3, 1); - let tx3 = create_test_tx(MicroTari(10_000), MicroTari(1000), 0, 2, 1); - let tx4 = create_test_tx(MicroTari(10_000), MicroTari(200), 0, 4, 1); - let tx5 = create_test_tx(MicroTari(10_000), MicroTari(500), 0, 3, 1); - let tx6 = create_test_tx(MicroTari(10_000), MicroTari(750), 0, 2, 1); + let network = Network::LocalNet; + let consensus_constants = network.create_consensus_constants(); + let tx1 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(50), inputs:2, outputs: 1).0); + let tx2 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(20), inputs:3, outputs: 1).0); + let tx3 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(100), inputs:2, outputs: 1).0); + let tx4 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(30), inputs:4, outputs: 1).0); + let tx5 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(50), inputs:3, outputs: 1).0); + let tx6 = Arc::new(tx!(MicroTari(10_000), fee: MicroTari(75), inputs:2, outputs: 1).0); - let mut unconfirmed_pool = UnconfirmedPool::new(UnconfirmedPoolConfig { + let unconfirmed_pool = UnconfirmedPool::new(UnconfirmedPoolConfig { storage_capacity: 10, weight_tx_skip_count: 3, }); @@ -216,42 +243,54 @@ mod test { // utx6 should not be added to unconfirmed_pool as it is an unknown transactions that was included in the block // by another node - let published_block = create_test_block(0, vec![tx1.clone(), tx3.clone(), tx5.clone()]); + let snapshot_txs = unconfirmed_pool.snapshot().unwrap(); + assert_eq!(snapshot_txs.len(), 5); + assert!(snapshot_txs.contains(&tx1)); + assert!(snapshot_txs.contains(&tx2)); + assert!(snapshot_txs.contains(&tx3)); + assert!(snapshot_txs.contains(&tx4)); + assert!(snapshot_txs.contains(&tx5)); + + let published_block = create_orphan_block( + 0, + vec![(*tx1).clone(), (*tx3).clone(), (*tx5).clone()], + &consensus_constants, + ); let _ = unconfirmed_pool.remove_published_and_discard_double_spends(&published_block); assert_eq!( unconfirmed_pool - .has_tx_with_excess_sig(&tx1.body.kernels[0].excess_sig) + .has_tx_with_excess_sig(&tx1.body.kernels()[0].excess_sig) .unwrap(), false ); assert_eq!( unconfirmed_pool - .has_tx_with_excess_sig(&tx2.body.kernels[0].excess_sig) + .has_tx_with_excess_sig(&tx2.body.kernels()[0].excess_sig) .unwrap(), true ); assert_eq!( unconfirmed_pool - .has_tx_with_excess_sig(&tx3.body.kernels[0].excess_sig) + .has_tx_with_excess_sig(&tx3.body.kernels()[0].excess_sig) .unwrap(), false ); assert_eq!( unconfirmed_pool - .has_tx_with_excess_sig(&tx4.body.kernels[0].excess_sig) + .has_tx_with_excess_sig(&tx4.body.kernels()[0].excess_sig) .unwrap(), true ); assert_eq!( unconfirmed_pool - .has_tx_with_excess_sig(&tx5.body.kernels[0].excess_sig) + .has_tx_with_excess_sig(&tx5.body.kernels()[0].excess_sig) .unwrap(), false ); assert_eq!( unconfirmed_pool - .has_tx_with_excess_sig(&tx6.body.kernels[0].excess_sig) + .has_tx_with_excess_sig(&tx6.body.kernels()[0].excess_sig) .unwrap(), false ); @@ -261,17 +300,21 @@ mod test { #[test] fn test_discard_double_spend_txs() { - let tx1 = create_test_tx(MicroTari(5_000), MicroTari(500), 0, 2, 1); - let tx2 = create_test_tx(MicroTari(5_000), MicroTari(100), 0, 3, 1); - let tx3 = create_test_tx(MicroTari(5_000), MicroTari(1000), 0, 2, 1); - let tx4 = create_test_tx(MicroTari(5_000), MicroTari(200), 0, 2, 1); - let mut tx5 = create_test_tx(MicroTari(5_000), MicroTari(500), 0, 3, 1); - let mut tx6 = create_test_tx(MicroTari(5_000), MicroTari(750), 0, 2, 1); + let network = Network::LocalNet; + let consensus_constants = network.create_consensus_constants(); + let tx1 = Arc::new(tx!(MicroTari(5_000), fee: MicroTari(50), inputs:2, outputs:1).0); + let tx2 = Arc::new(tx!(MicroTari(5_000), fee: MicroTari(20), inputs:3, outputs:1).0); + let tx3 = Arc::new(tx!(MicroTari(5_000), fee: MicroTari(100), inputs:2, outputs:1).0); + let tx4 = Arc::new(tx!(MicroTari(5_000), fee: MicroTari(30), inputs:2, outputs:1).0); + let mut tx5 = tx!(MicroTari(5_000), fee:MicroTari(50), inputs:3, outputs:1).0; + let mut tx6 = tx!(MicroTari(5_000), fee:MicroTari(75), inputs: 2, outputs: 1).0; // tx1 and tx5 have a shared input. Also, tx3 and tx6 have a shared input - tx5.body.inputs[0] = tx1.body.inputs[0].clone(); - tx6.body.inputs[1] = tx3.body.inputs[1].clone(); + tx5.body.inputs_mut()[0] = tx1.body.inputs()[0].clone(); + tx6.body.inputs_mut()[1] = tx3.body.inputs()[1].clone(); + let tx5 = Arc::new(tx5); + let tx6 = Arc::new(tx6); - let mut unconfirmed_pool = UnconfirmedPool::new(UnconfirmedPoolConfig { + let unconfirmed_pool = UnconfirmedPool::new(UnconfirmedPoolConfig { storage_capacity: 10, weight_tx_skip_count: 3, }); @@ -287,7 +330,11 @@ mod test { .unwrap(); // The publishing of tx1 and tx3 will be double-spends and orphan tx5 and tx6 - let published_block = create_test_block(0, vec![tx1.clone(), tx2.clone(), tx3.clone()]); + let published_block = create_orphan_block( + 0, + vec![(*tx1).clone(), (*tx2).clone(), (*tx3).clone()], + &consensus_constants, + ); let _ = unconfirmed_pool .remove_published_and_discard_double_spends(&published_block) @@ -295,37 +342,37 @@ mod test { assert_eq!( unconfirmed_pool - .has_tx_with_excess_sig(&tx1.body.kernels[0].excess_sig) + .has_tx_with_excess_sig(&tx1.body.kernels()[0].excess_sig) .unwrap(), false ); assert_eq!( unconfirmed_pool - .has_tx_with_excess_sig(&tx2.body.kernels[0].excess_sig) + .has_tx_with_excess_sig(&tx2.body.kernels()[0].excess_sig) .unwrap(), false ); assert_eq!( unconfirmed_pool - .has_tx_with_excess_sig(&tx3.body.kernels[0].excess_sig) + .has_tx_with_excess_sig(&tx3.body.kernels()[0].excess_sig) .unwrap(), false ); assert_eq!( unconfirmed_pool - .has_tx_with_excess_sig(&tx4.body.kernels[0].excess_sig) + .has_tx_with_excess_sig(&tx4.body.kernels()[0].excess_sig) .unwrap(), true ); assert_eq!( unconfirmed_pool - .has_tx_with_excess_sig(&tx5.body.kernels[0].excess_sig) + .has_tx_with_excess_sig(&tx5.body.kernels()[0].excess_sig) .unwrap(), false ); assert_eq!( unconfirmed_pool - .has_tx_with_excess_sig(&tx6.body.kernels[0].excess_sig) + .has_tx_with_excess_sig(&tx6.body.kernels()[0].excess_sig) .unwrap(), false ); diff --git a/base_layer/core/src/mempool/unconfirmed_pool/unconfirmed_pool_storage.rs b/base_layer/core/src/mempool/unconfirmed_pool/unconfirmed_pool_storage.rs index 27485f5f9e..6abcdb0969 100644 --- a/base_layer/core/src/mempool/unconfirmed_pool/unconfirmed_pool_storage.rs +++ b/base_layer/core/src/mempool/unconfirmed_pool/unconfirmed_pool_storage.rs @@ -26,14 +26,15 @@ use crate::{ priority::{FeePriority, PrioritizedTransaction}, unconfirmed_pool::{UnconfirmedPoolConfig, UnconfirmedPoolError}, }, - transaction::Transaction, - types::Signature, + transactions::{transaction::Transaction, types::Signature}, }; +use log::*; use std::{ collections::{BTreeMap, HashMap}, convert::TryFrom, sync::Arc, }; +pub const LOG_TARGET: &str = "c::mp::unconfirmed_pool::unconfirmed_pool_storage"; /// UnconfirmedPool makes use of UnconfirmedPoolStorage to provide thread save access to its Hashmap and BTreeMap. /// The txs_by_signature HashMap is used to find a transaction using its excess_sig, this functionality is used to match @@ -73,10 +74,11 @@ impl UnconfirmedPoolStorage { /// space for higher priority transactions. The lowest priority transactions will be removed when the maximum /// capacity is reached and the new transaction has a higher priority than the currently stored lowest priority /// transaction. - pub fn insert(&mut self, tx: Transaction) -> Result<(), UnconfirmedPoolError> { - let tx_key = tx.body.kernels[0].excess_sig.clone(); + pub fn insert(&mut self, tx: Arc) -> Result<(), UnconfirmedPoolError> { + let tx_key = tx.body.kernels()[0].excess_sig.clone(); if !self.txs_by_signature.contains_key(&tx_key) { - let prioritized_tx = PrioritizedTransaction::try_from(tx)?; + trace!(target: LOG_TARGET, "Inserting tx into unconfirmed pool: {:?}", tx_key,); + let prioritized_tx = PrioritizedTransaction::try_from((*tx).clone())?; if self.txs_by_signature.len() >= self.config.storage_capacity { if prioritized_tx.priority < *self.lowest_priority() { return Ok(()); @@ -92,7 +94,7 @@ impl UnconfirmedPoolStorage { } /// Insert a set of new transactions into the UnconfirmedPoolStorage - pub fn insert_txs(&mut self, txs: Vec) -> Result<(), UnconfirmedPoolError> { + pub fn insert_txs(&mut self, txs: Vec>) -> Result<(), UnconfirmedPoolError> { for tx in txs.into_iter() { self.insert(tx)?; } @@ -113,7 +115,7 @@ impl UnconfirmedPoolStorage { let ptx = self .txs_by_signature .get(tx_key) - .ok_or(UnconfirmedPoolError::StorageOutofSync)?; + .ok_or_else(|| UnconfirmedPoolError::StorageOutofSync)?; if curr_weight + ptx.weight <= total_weight { curr_weight += ptx.weight; @@ -135,8 +137,8 @@ impl UnconfirmedPoolStorage { fn discard_double_spends(&mut self, published_block: &Block) { let mut removed_tx_keys: Vec = Vec::new(); for (tx_key, ptx) in self.txs_by_signature.iter() { - for input in &ptx.transaction.body.inputs { - if published_block.body.inputs.contains(input) { + for input in ptx.transaction.body.inputs() { + if published_block.body.inputs().contains(input) { self.txs_by_priority.remove(&ptx.priority); removed_tx_keys.push(tx_key.clone()); } @@ -144,21 +146,27 @@ impl UnconfirmedPoolStorage { } for tx_key in &removed_tx_keys { + trace!( + target: LOG_TARGET, + "Removing double spends from unconfirmed pool: {:?}", + tx_key + ); self.txs_by_signature.remove(&tx_key); } } /// Remove all published transactions from the UnconfirmedPoolStorage and discard double spends pub fn remove_published_and_discard_double_spends(&mut self, published_block: &Block) -> Vec> { - self.discard_double_spends(published_block); - let mut removed_txs: Vec> = Vec::new(); - published_block.body.kernels.iter().for_each(|kernel| { + published_block.body.kernels().iter().for_each(|kernel| { if let Some(ptx) = self.txs_by_signature.get(&kernel.excess_sig) { self.txs_by_priority.remove(&ptx.priority); removed_txs.push(self.txs_by_signature.remove(&kernel.excess_sig).unwrap().transaction); } }); + // First remove published transactions before discarding double spends + self.discard_double_spends(published_block); + removed_txs } @@ -167,6 +175,21 @@ impl UnconfirmedPoolStorage { self.txs_by_signature.len() } + /// Returns all transaction stored in the UnconfirmedPoolStorage. + pub fn snapshot(&self) -> Vec> { + self.txs_by_signature + .iter() + .map(|(_, ptx)| ptx.transaction.clone()) + .collect() + } + + /// Returns the total weight of all transactions stored in the pool. + pub fn calculate_weight(&self) -> u64 { + self.txs_by_signature + .iter() + .fold(0, |weight, (_, ptx)| weight + ptx.transaction.calculate_weight()) + } + #[cfg(test)] /// Checks the consistency status of the Hashmap and BtreeMap pub fn check_status(&self) -> bool { diff --git a/base_layer/core/src/mining/blake_miner.rs b/base_layer/core/src/mining/blake_miner.rs new file mode 100644 index 0000000000..8053a47f4a --- /dev/null +++ b/base_layer/core/src/mining/blake_miner.rs @@ -0,0 +1,98 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + blocks::BlockHeader, + proof_of_work::{Difficulty, ProofOfWork}, +}; +use log::*; +use rand::{rngs::OsRng, RngCore}; +use serde::{Deserialize, Serialize}; +use std::{ + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + time::{Duration, Instant}, +}; +use tari_crypto::tari_utilities::epoch_time::EpochTime; +pub const LOG_TARGET: &str = "c::m::blake_miner"; + +/// A simple Blake2b-based proof of work. This is currently intended to be used for testing and perhaps Testnet until +/// Monero merge-mining is active. +/// +/// The proof of work difficulty is given by `H256(H512(header || nonce))` where Hnnn is the Blake2b digest of length +/// `nnn` bits. +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub struct CpuBlakePow; + +impl CpuBlakePow { + /// A simple miner. It starts with a random nonce and iterates until it finds a header hash that meets the desired + /// target + pub fn mine( + target_difficulty: Difficulty, + mut header: BlockHeader, + stop_flag: Arc, + kill_flag: Arc, + ) -> Option + { + let mut start = Instant::now(); + let mut nonce: u64 = OsRng.next_u64(); + let start_nonce = nonce; + let mut last_measured_nonce = nonce; + // We're mining over here! + let mut difficulty = ProofOfWork::achieved_difficulty(&header); + info!(target: LOG_TARGET, "Mining started."); + debug!(target: LOG_TARGET, "Mining for difficulty: {:?}", target_difficulty); + while difficulty < target_difficulty { + if start.elapsed() >= Duration::from_secs(60) { + // nonce might have wrapped around + let hashes = if nonce >= last_measured_nonce { + nonce - last_measured_nonce + } else { + std::u64::MAX - last_measured_nonce + nonce + }; + let hash_rate = hashes as f64 / start.elapsed().as_micros() as f64; + info!(target: LOG_TARGET, "Mining hash rate: {:.6} MH/s", hash_rate); + last_measured_nonce = nonce; + start = Instant::now(); + } + if stop_flag.load(Ordering::Relaxed) || kill_flag.load(Ordering::Relaxed) { + info!(target: LOG_TARGET, "Mining stopped via flag"); + return None; + } + if nonce == std::u64::MAX { + nonce = 0; + } else { + nonce += 1; + } + if nonce == start_nonce { + header.timestamp = EpochTime::now(); + } + header.nonce = nonce; + difficulty = ProofOfWork::achieved_difficulty(&header); + } + + debug!(target: LOG_TARGET, "Miner found nonce: {}", nonce); + Some(header) + } +} diff --git a/base_layer/core/src/mining/coinbase_builder.rs b/base_layer/core/src/mining/coinbase_builder.rs new file mode 100644 index 0000000000..4e82d4a671 --- /dev/null +++ b/base_layer/core/src/mining/coinbase_builder.rs @@ -0,0 +1,227 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +use crate::{ + chain_storage::BlockchainBackend, + consensus::ConsensusManager, + transactions::{ + tari_amount::{uT, MicroTari}, + transaction::{ + KernelBuilder, + KernelFeatures, + OutputFeatures, + Transaction, + TransactionBuilder, + UnblindedOutput, + }, + transaction_protocol::{build_challenge, TransactionMetadata}, + types::{BlindingFactor, CryptoFactories, PrivateKey, PublicKey, Signature}, + }, +}; +use derive_error::Error; +use tari_crypto::{commitment::HomomorphicCommitmentFactory, keys::PublicKey as PK}; + +#[derive(Debug, Clone, Error, PartialEq)] +pub enum CoinbaseBuildError { + /// The block height for this coinbase transaction wasn't provided + MissingBlockHeight, + /// The value for the coinbase transaction is missing + MissingFees, + /// The private nonce for this coinbase transaction wasn't provided + MissingNonce, + /// The spend key for this coinbase transaction wasn't provided + MissingSpendKey, + /// An error occurred building the final transaction + #[error(msg_embedded, no_from, non_std)] + BuildError(String), + /// Some inconsistent data was given to the builder. This transaction is not valid + InvalidTransaction, +} + +pub struct CoinbaseBuilder { + factories: CryptoFactories, + block_height: Option, + fees: Option, + spend_key: Option, + private_nonce: Option, +} + +impl CoinbaseBuilder { + /// Start building a new Coinbase transaction. From here you can build the transaction piecemeal with the builder + /// methods, or pass in a block to `using_block` to determine most of the coinbase parameters automatically. + pub fn new(factories: CryptoFactories) -> Self { + CoinbaseBuilder { + factories, + block_height: None, + fees: None, + spend_key: None, + private_nonce: None, + } + } + + /// Assign the block height. This is used to determine the lock height of the transaction. + pub fn with_block_height(mut self, height: u64) -> Self { + self.block_height = Some(height); + self + } + + /// Indicates the sum total of all fees that the coinbase transaction earns, over and above the block reward + pub fn with_fees(mut self, value: MicroTari) -> Self { + self.fees = Some(value); + self + } + + /// Provides the private spend key for this transaction. This will usually be provided by a miner's wallet instance. + pub fn with_spend_key(mut self, key: PrivateKey) -> Self { + self.spend_key = Some(key); + self + } + + /// The nonce to be used for this transaction. This will usually be provided by a miner's wallet instance. + pub fn with_nonce(mut self, nonce: PrivateKey) -> Self { + self.private_nonce = Some(nonce); + self + } + + /// Try and construct a Coinbase Transaction. The block reward is taken from the emission curve for the current + /// block height. The other parameters (keys, nonces etc.) are provided by the caller. Other data is + /// automatically set: Coinbase transactions have an offset of zero, no fees, the `COINBASE_OUTPUT` flags are set + /// on the output and kernel, and the maturity schedule is set from the consensus rules. + /// + /// After `build` is called, the struct is destroyed and the private keys stored are dropped and the memory zeroed + /// out (by virtue of the zero_on_drop crate). + #[allow(clippy::erasing_op)] // This is for 0 * uT + pub fn build( + self, + rules: ConsensusManager, + ) -> Result<(Transaction, UnblindedOutput), CoinbaseBuildError> + { + let height = self + .block_height + .ok_or_else(|| CoinbaseBuildError::MissingBlockHeight)?; + let reward = rules.emission_schedule().block_reward(height) + + self.fees.ok_or_else(|| CoinbaseBuildError::MissingFees)?; + let nonce = self.private_nonce.ok_or_else(|| CoinbaseBuildError::MissingNonce)?; + let public_nonce = PublicKey::from_secret_key(&nonce); + let key = self.spend_key.ok_or_else(|| CoinbaseBuildError::MissingSpendKey)?; + let output_features = + OutputFeatures::create_coinbase(height + rules.consensus_constants().coinbase_lock_height()); + let excess = self.factories.commitment.commit_value(&key, 0); + let kernel_features = KernelFeatures::create_coinbase(); + let metadata = TransactionMetadata::default(); + let challenge = build_challenge(&public_nonce, &metadata); + let sig = Signature::sign(key.clone(), nonce, &challenge) + .map_err(|_| CoinbaseBuildError::BuildError("Challenge could not be represented as a scalar".into()))?; + let unblinded_output = UnblindedOutput::new(reward, key, Some(output_features)); + let output = unblinded_output + .as_transaction_output(&self.factories) + .map_err(|e| CoinbaseBuildError::BuildError(e.to_string()))?; + let kernel = KernelBuilder::new() + .with_fee(0 * uT) + .with_features(kernel_features) + .with_lock_height(0) + .with_excess(&excess) + .with_signature(&sig) + .build() + .map_err(|e| CoinbaseBuildError::BuildError(e.to_string()))?; + + let mut builder = TransactionBuilder::new(); + builder + .add_output(output) + .add_offset(BlindingFactor::default()) + .with_reward(reward) + .with_kernel(kernel); + let tx = builder + .build(&self.factories) + .map_err(|e| CoinbaseBuildError::BuildError(e.to_string()))?; + Ok((tx, unblinded_output)) + } +} + +#[cfg(test)] +mod test { + use crate::{ + consensus::{ConsensusManager, ConsensusManagerBuilder, Network}, + helpers::MockBackend, + mining::{coinbase_builder::CoinbaseBuildError, CoinbaseBuilder}, + transactions::{ + helpers::TestParams, + tari_amount::uT, + transaction::{OutputFlags, UnblindedOutput}, + types::CryptoFactories, + }, + }; + use tari_crypto::commitment::HomomorphicCommitmentFactory; + + fn get_builder() -> (CoinbaseBuilder, ConsensusManager, CryptoFactories) { + let network = Network::LocalNet; + let rules = ConsensusManagerBuilder::new(network).build(); + let factories = CryptoFactories::default(); + (CoinbaseBuilder::new(factories.clone()), rules, factories) + } + + #[test] + fn missing_height() { + let (builder, rules, _) = get_builder(); + assert_eq!( + builder.build(rules).unwrap_err(), + CoinbaseBuildError::MissingBlockHeight + ); + } + + #[test] + fn missing_fees() { + let (builder, rules, _) = get_builder(); + let builder = builder.with_block_height(42); + assert_eq!(builder.build(rules).unwrap_err(), CoinbaseBuildError::MissingFees); + } + + #[test] + fn missing_spend_key() { + let p = TestParams::new(); + let (builder, rules, _) = get_builder(); + let builder = builder.with_block_height(42).with_fees(0 * uT).with_nonce(p.nonce); + assert_eq!(builder.build(rules).unwrap_err(), CoinbaseBuildError::MissingSpendKey); + } + + #[test] + fn valid_coinbase() { + let p = TestParams::new(); + let (builder, rules, factories) = get_builder(); + let builder = builder + .with_block_height(42) + .with_fees(145 * uT) + .with_nonce(p.nonce.clone()) + .with_spend_key(p.spend_key.clone()); + let (tx, unblinded_output) = builder.build(rules.clone()).unwrap(); + let utxo = &tx.body.outputs()[0]; + let block_reward = rules.emission_schedule().block_reward(42) + 145 * uT; + let unblinded_test = UnblindedOutput::new(block_reward, p.spend_key.clone(), Some(utxo.features.clone())); + assert_eq!(unblinded_output, unblinded_test); + assert!(factories + .commitment + .open_value(&p.spend_key, block_reward.into(), utxo.commitment())); + assert!(utxo.verify_range_proof(&factories.range_proof).unwrap()); + assert!(utxo.features.flags.contains(OutputFlags::COINBASE_OUTPUT)); + } +} diff --git a/base_layer/core/src/mining/error.rs b/base_layer/core/src/mining/error.rs new file mode 100644 index 0000000000..11e9c2e55d --- /dev/null +++ b/base_layer/core/src/mining/error.rs @@ -0,0 +1,36 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use derive_error::Error; + +#[derive(Clone, Debug, PartialEq, Error)] +pub enum MinerError { + // Could not construct the coinbase utxo and kernel for the block + CoinbaseError, + // No block provided to mine + MissingBlock, + // Config issue + ConfigError, + // Error communicating to base node or wallet + #[error(msg_embedded, non_std, no_from)] + CommunicationError(String), +} diff --git a/base_layer/core/src/mining/miner.rs b/base_layer/core/src/mining/miner.rs new file mode 100644 index 0000000000..c1ad55efd7 --- /dev/null +++ b/base_layer/core/src/mining/miner.rs @@ -0,0 +1,296 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + base_node::{ + comms_interface::{BlockEvent, LocalNodeCommsInterface}, + states::BaseNodeState, + }, + blocks::{Block, NewBlockTemplate}, + chain_storage::{BlockAddResult, BlockchainBackend}, + consensus::ConsensusManager, + mining::{blake_miner::CpuBlakePow, error::MinerError, CoinbaseBuilder}, + proof_of_work::{Difficulty, PowAlgorithm}, + transactions::{ + transaction::UnblindedOutput, + types::{CryptoFactories, PrivateKey}, + }, +}; +use core::sync::atomic::AtomicBool; +use futures::{ + channel::{ + mpsc, + mpsc::{Receiver, Sender}, + }, + future::FutureExt, + pin_mut, + StreamExt, +}; +use log::*; +use rand::rngs::OsRng; +use std::{ + sync::{atomic::Ordering, Arc}, + time::Duration, +}; +use tari_broadcast_channel::Subscriber; +use tari_crypto::keys::SecretKey; +use tokio::task::spawn_blocking; + +pub const LOG_TARGET: &str = "c::m::miner"; + +pub struct Miner { + kill_flag: Arc, + received_new_block_flag: Arc, + consensus: ConsensusManager, + node_interface: LocalNodeCommsInterface, + utxo_sender: Sender, + state_change_event_rx: Option>, +} + +impl Miner { + /// Constructs a new miner + pub fn new( + stop_flag: Arc, + consensus: ConsensusManager, + node_interface: &LocalNodeCommsInterface, + ) -> Miner + { + let (utxo_sender, _): (Sender, Receiver) = mpsc::channel(1); + Miner { + kill_flag: stop_flag, + consensus, + received_new_block_flag: Arc::new(AtomicBool::new(false)), + node_interface: node_interface.clone(), + utxo_sender, + state_change_event_rx: None, + } + } + + /// This function instantiates a new channel and returns the receiver so that the miner can send out a unblinded + /// output. This output is only sent if the miner successfully mines a block + pub fn get_utxo_receiver_channel(&mut self) -> Receiver { + let (sender, receiver): (Sender, Receiver) = mpsc::channel(1); + self.utxo_sender = sender; + receiver + } + + /// This provides a tari_broadcast_channel to the miner so that it can subscribe to the state machine. + /// The state machine will publish state changes here. The miner is only interested to know when the state machine + /// transitions to listing state. This means that the miner has moved from some disconnected state to up to date + /// and the miner can ask for a new block to mine upon. + pub fn subscribe_to_state_change(&mut self, state_change_event_rx: Subscriber) { + self.state_change_event_rx = Some(state_change_event_rx); + } + + /// Mine blocks asynchronously. + /// + /// On the first iteration, the thread will loop around until `received_new_block_flag` is true. This flag is set + /// to true when either a new block is received from the node, or when the node reaches the `Listening` state + /// (see [Miner::mine]). + /// + /// Then, if the miner hasn't been stopped, it starts the main mining loop: + /// 1. We request a new template block from the base node + /// 2. We add our Coinbase UTXO to the block + /// 3. We send thi sback to the node to calculate the MMR roots + /// 4. We iterate on the header nonce until + /// * the target difficulty is reached + /// * or the loop is interrupted because a new block was found in the interim, or the miner is stopped + async fn mining(&mut self) -> Result<(), MinerError> { + // Lets make sure its set to mine + debug!(target: LOG_TARGET, "Start mining thread"); + while !self.kill_flag.load(Ordering::Relaxed) { + while !self.received_new_block_flag.load(Ordering::Relaxed) { + tokio::time::delay_for(Duration::from_millis(100)).await; // wait for new block event + if self.kill_flag.load(Ordering::Relaxed) { + debug!(target: LOG_TARGET, "Mining stopped with kill flag."); + return Ok(()); + } + } + let flag = self.received_new_block_flag.clone(); + flag.store(false, Ordering::Relaxed); + debug!(target: LOG_TARGET, "Miner asking for new candidate block to mine."); + let mut block_template = self.get_block_template().await?; + let output = self.add_coinbase(&mut block_template)?; + let mut block = self.get_block(block_template).await?; + debug!(target: LOG_TARGET, "Miner got new block to mine."); + let difficulty = self.get_req_difficulty().await?; + let new_block_event_flag = self.received_new_block_flag.clone(); + let kill = self.kill_flag.clone(); + let header = block.header.clone(); + let mining_handle = + spawn_blocking(move || CpuBlakePow::mine(difficulty, header, new_block_event_flag, kill)); + let result = mining_handle.await.unwrap_or(None); + if let Some(r) = result { + block.header = r; + self.send_block(block).await.or_else(|e| { + error!(target: LOG_TARGET, "Could not send block to base node. {:?}.", e); + Err(e) + })?; + self.utxo_sender + .try_send(output) + .or_else(|e| { + error!(target: LOG_TARGET, "Could not send utxo to wallet. {:?}.", e); + Err(e) + }) + .map_err(|e| MinerError::CommunicationError(e.to_string()))?; + } + } + debug!(target: LOG_TARGET, "Mining thread stopped."); + Ok(()) + } + + /// function, this function gets called when a new block event is triggered. It will ensure that the miner + /// restarts/starts to mine. + pub async fn mine(mut self) { + let flag = self.received_new_block_flag.clone(); + let block_event = self.node_interface.clone().get_block_event_stream_fused(); + let state_event = self + .state_change_event_rx + .take() + .expect("Miner does not have access to state event stream") + .fuse(); + let t_miner = self.mining().fuse(); + + pin_mut!(block_event); + pin_mut!(state_event); + pin_mut!(t_miner); + loop { + futures::select! { + msg = block_event.select_next_some() => { + match (*msg).clone() { + BlockEvent::Verified((_, result)) => { + if result == BlockAddResult::Ok{ + flag.store(true, Ordering::Relaxed); + }; + }, + _ => (), + } + }, + msg = state_event.select_next_some() => { + match (*msg).clone() { + BaseNodeState::Listening(_) => { + flag.store(true, Ordering::Relaxed); + }, + _ => (), + } + }, + (_) = t_miner => break, + }; + } + } + + /// function, temp use genesis block as template + pub async fn get_block_template(&mut self) -> Result { + trace!(target: LOG_TARGET, "Requesting new block template from node."); + Ok(self + .node_interface + .get_new_block_template() + .await + .or_else(|e| { + error!( + target: LOG_TARGET, + "Could not get a new block template from the base node. {:?}.", e + ); + Err(e) + }) + .map_err(|e| MinerError::CommunicationError(e.to_string()))?) + } + + /// function, temp use genesis block as template + pub async fn get_block(&mut self, block: NewBlockTemplate) -> Result { + trace!( + target: LOG_TARGET, + "Asking node to fill in MMR roots for new block candidate" + ); + Ok(self + .node_interface + .get_new_block(block) + .await + .or_else(|e| { + error!( + target: LOG_TARGET, + "Could not get a new block from the base node. {:?}.", e + ); + Err(e) + }) + .map_err(|e| MinerError::CommunicationError(e.to_string()))?) + } + + /// function to get the required difficulty + pub async fn get_req_difficulty(&mut self) -> Result { + trace!(target: LOG_TARGET, "Requesting target difficulty from node"); + Ok(self + .node_interface + .get_target_difficulty(PowAlgorithm::Blake) + .await + .or_else(|e| { + error!( + target: LOG_TARGET, + "Could not get the required difficulty from the base node. {:?}.", e + ); + + Err(e) + }) + .map_err(|e| MinerError::CommunicationError(e.to_string()))?) + } + + // add the coinbase to the NewBlockTemplate + fn add_coinbase(&self, block: &mut NewBlockTemplate) -> Result { + let fees = block.body.get_total_fee(); + let (key, r) = self.get_spending_key()?; + let factories = CryptoFactories::default(); + let builder = CoinbaseBuilder::new(factories); + let builder = builder + .with_block_height(block.header.height) + .with_fees(fees) + .with_nonce(r) + .with_spend_key(key); + let (tx, unblinded_output) = builder + .build(self.consensus.clone()) + .expect("invalid constructed coinbase"); + block.body.add_output(tx.body.outputs()[0].clone()); + block.body.add_kernel(tx.body.kernels()[0].clone()); + Ok(unblinded_output) + } + + /// function to create private key and nonce for coinbase + pub fn get_spending_key(&self) -> Result<(PrivateKey, PrivateKey), MinerError> { + let r = PrivateKey::random(&mut OsRng); + let key = PrivateKey::random(&mut OsRng); + Ok((key, r)) + } + + /// function to send a block + async fn send_block(&mut self, block: Block) -> Result<(), MinerError> { + info!(target: LOG_TARGET, "Mined a block: {}", block); + self.node_interface + .submit_block(block) + .await + .or_else(|e| { + error!(target: LOG_TARGET, "Could not send block to base node. {:?}.", e); + Err(e) + }) + .map_err(|e| MinerError::CommunicationError(e.to_string()))?; + Ok(()) + } +} diff --git a/base_layer/core/src/mining/mod.rs b/base_layer/core/src/mining/mod.rs new file mode 100644 index 0000000000..18cffa13a4 --- /dev/null +++ b/base_layer/core/src/mining/mod.rs @@ -0,0 +1,29 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod blake_miner; +mod coinbase_builder; +mod error; +mod miner; + +pub use coinbase_builder::CoinbaseBuilder; +pub use miner::Miner; diff --git a/base_layer/core/src/proof_of_work/blake_pow.rs b/base_layer/core/src/proof_of_work/blake_pow.rs index 5d88f76e9d..bc987bfd08 100644 --- a/base_layer/core/src/proof_of_work/blake_pow.rs +++ b/base_layer/core/src/proof_of_work/blake_pow.rs @@ -20,16 +20,11 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{ - blocks::BlockHeader, - proof_of_work::{Difficulty, PowError, ProofOfWork}, -}; +use crate::{blocks::BlockHeader, proof_of_work::Difficulty}; use bigint::uint::U256; use blake2::Blake2b; use digest::Digest; -use serde::{Deserialize, Serialize}; -use tari_crypto::common::Blake256; -use tari_utilities::{ByteArray, ByteArrayError, Hashable}; +use tari_crypto::{common::Blake256, tari_utilities::Hashable}; const MAX_TARGET: U256 = U256::MAX; @@ -38,81 +33,78 @@ const MAX_TARGET: U256 = U256::MAX; /// /// The proof of work difficulty is given by `H256(H512(header || nonce))` where Hnnn is the Blake2b digest of length /// `nnn` bits. -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -pub struct BlakePow; - -impl BlakePow { - /// A simple miner. It starts at nonce = 0 and iterates until it finds a header hash that meets the desired target - pub fn mine(target_difficulty: Difficulty, header: &BlockHeader) -> u64 { - let mut nonce = 0u64; - // We're mining over here! - while let Ok(d) = header.pow.calculate_difficulty(nonce, &header) { - if d >= target_difficulty { - break; - } - nonce += 1; - } - nonce - } +pub fn blake_difficulty(header: &BlockHeader) -> Difficulty { + blake_difficulty_with_hash(header).0 } -impl ProofOfWork for BlakePow { - fn calculate_difficulty(&self, nonce: u64, header: &BlockHeader) -> Result { - let bytes = header.hash(); - let hash = Blake2b::new() - .chain(&bytes) - .chain(nonce.to_le_bytes()) - .result() - .to_vec(); - let hash = Blake256::digest(&hash).to_vec(); - let scalar = U256::from_little_endian(&hash); - let result = MAX_TARGET / scalar; - let difficulty = u64::from(result).into(); - Ok(difficulty) - } -} - -impl Default for BlakePow { - fn default() -> Self { - BlakePow - } +pub fn blake_difficulty_with_hash(header: &BlockHeader) -> (Difficulty, Vec) { + let bytes = header.hash(); + let hash = Blake2b::digest(&bytes).to_vec(); + let hash = Blake256::digest(&hash).to_vec(); + let scalar = U256::from_big_endian(&hash); // Big endian so the hash has leading zeroes + let result = MAX_TARGET / scalar; + let difficulty = u64::from(result).into(); + (difficulty, hash) } -impl ByteArray for BlakePow { - fn from_bytes(_bytes: &[u8]) -> Result { - Ok(BlakePow) - } +#[cfg(test)] +pub mod test { + use crate::{ + blocks::BlockHeader, + proof_of_work::{ + blake_pow::{blake_difficulty, blake_difficulty_with_hash}, + Difficulty, + }, + }; + use chrono::{DateTime, NaiveDate, Utc}; + use tari_crypto::tari_utilities::hex::Hex; - fn as_bytes(&self) -> &[u8] { - &[] + /// A simple example miner. It starts at nonce = 0 and iterates until it finds a header hash that meets the desired + /// target block + #[allow(dead_code)] + fn mine_blake(target_difficulty: Difficulty, header: &mut BlockHeader) -> u64 { + header.nonce = 0; + // We're mining over here! + while blake_difficulty(&header) < target_difficulty { + header.nonce += 1; + } + header.nonce } -} -impl Hashable for BlakePow { - fn hash(&self) -> Vec { - vec![] + pub fn get_header() -> BlockHeader { + let mut header = BlockHeader::new(0); + header.timestamp = DateTime::::from_utc(NaiveDate::from_ymd(2000, 1, 1).and_hms(1, 1, 1), Utc).into(); + header } -} - -#[cfg(test)] -mod test { - use crate::{blocks::BlockHeader, proof_of_work::ProofOfWork}; #[test] fn validate_max_target() { - let header = BlockHeader::new(0); - assert_eq!(header.pow.calculate_difficulty(2, &header), Ok(1.into())); + let mut header = get_header(); + header.nonce = 1; + assert_eq!(blake_difficulty(&header), Difficulty::from(5)); } #[test] fn difficulty_1000() { - let header = BlockHeader::new(0); - assert_eq!(header.pow.calculate_difficulty(108, &header), Ok(1273.into())); + let mut header = get_header(); + header.nonce = 2606; + let (diff, hash) = blake_difficulty_with_hash(&header); + assert_eq!(diff, Difficulty::from(1_385)); + assert_eq!( + hash.to_hex(), + "002f4dc46d5ac0f0207629095a479d6b0fa7d3db08a1ae1790e4d2078376948d" + ); } #[test] fn difficulty_1mil() { - let header = BlockHeader::new(0); - assert_eq!(header.pow.calculate_difficulty(134_390, &header), Ok(3_250_351.into())); + let mut header = get_header(); + header.nonce = 7_945_536; + let (diff, hash) = blake_difficulty_with_hash(&header); + assert_eq!(diff, Difficulty::from(2_459_030)); + assert_eq!( + hash.to_hex(), + "000006d29c3fce2f73e2a96daa9071f3c5c65f0b9334513bca6a39d279c5faaf" + ); } } diff --git a/base_layer/core/src/proof_of_work/diff_adj_manager/diff_adj_manager.rs b/base_layer/core/src/proof_of_work/diff_adj_manager/diff_adj_manager.rs new file mode 100644 index 0000000000..318e71f62c --- /dev/null +++ b/base_layer/core/src/proof_of_work/diff_adj_manager/diff_adj_manager.rs @@ -0,0 +1,103 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + chain_storage::{BlockchainBackend, BlockchainDatabase}, + consensus::ConsensusConstants, + proof_of_work::{ + diff_adj_manager::{diff_adj_storage::DiffAdjStorage, error::DiffAdjManagerError}, + Difficulty, + PowAlgorithm, + }, +}; +use std::sync::{Arc, RwLock}; +use tari_crypto::tari_utilities::epoch_time::EpochTime; + +/// The DiffAdjManager is used to calculate the current target difficulty based on PoW recorded in the latest blocks of +/// the current best chain. +pub struct DiffAdjManager +where T: BlockchainBackend +{ + diff_adj_storage: Arc>>, +} + +impl DiffAdjManager +where T: BlockchainBackend +{ + /// Constructs a new DiffAdjManager with access to the blockchain db. + pub fn new( + blockchain_db: BlockchainDatabase, + consensus_constants: &ConsensusConstants, + ) -> Result + { + Ok(Self { + diff_adj_storage: Arc::new(RwLock::new(DiffAdjStorage::new(blockchain_db, consensus_constants))), + }) + } + + /// Returns the estimated target difficulty for the specified PoW algorithm at the chain tip. + pub fn get_target_difficulty(&self, pow_algo: PowAlgorithm) -> Result { + self.diff_adj_storage + .write() + .map_err(|_| DiffAdjManagerError::PoisonedAccess)? + .get_target_difficulty(pow_algo) + } + + /// Returns the estimated target difficulty for the specified PoW algorithm and provided height. + pub fn get_target_difficulty_at_height( + &self, + pow_algo: PowAlgorithm, + height: u64, + ) -> Result + { + self.diff_adj_storage + .write() + .map_err(|_| DiffAdjManagerError::PoisonedAccess)? + .get_target_difficulty_at_height(pow_algo, height) + } + + /// Returns the median timestamp of the past 11 blocks at the chain tip. + pub fn get_median_timestamp(&self) -> Result { + self.diff_adj_storage + .write() + .map_err(|_| DiffAdjManagerError::PoisonedAccess)? + .get_median_timestamp() + } + + /// Returns the median timestamp of the past 11 blocks at the provided height. + pub fn get_median_timestamp_at_height(&self, height: u64) -> Result { + self.diff_adj_storage + .write() + .map_err(|_| DiffAdjManagerError::PoisonedAccess)? + .get_median_timestamp_at_height(height) + } +} + +impl Clone for DiffAdjManager +where T: BlockchainBackend +{ + fn clone(&self) -> Self { + Self { + diff_adj_storage: self.diff_adj_storage.clone(), + } + } +} diff --git a/base_layer/core/src/proof_of_work/diff_adj_manager/diff_adj_storage.rs b/base_layer/core/src/proof_of_work/diff_adj_manager/diff_adj_storage.rs new file mode 100644 index 0000000000..1a615d0249 --- /dev/null +++ b/base_layer/core/src/proof_of_work/diff_adj_manager/diff_adj_storage.rs @@ -0,0 +1,274 @@ +// Copyright 2019 The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + blocks::blockheader::BlockHash, + chain_storage::{BlockchainBackend, BlockchainDatabase}, + consensus::ConsensusConstants, + proof_of_work::{ + diff_adj_manager::error::DiffAdjManagerError, + difficulty::DifficultyAdjustment, + lwma_diff::LinearWeightedMovingAverage, + Difficulty, + PowAlgorithm, + ProofOfWork, + }, +}; +use log::*; +use std::collections::VecDeque; +use tari_crypto::tari_utilities::{epoch_time::EpochTime, hash::Hashable}; +pub const LOG_TARGET: &str = "c::pow::diff_adj_manager::diff_adj_storage"; + +/// The UpdateState enum is used to specify what update operation should be performed to keep the difficulty adjustment +/// system upto date with the blockchain db. +enum UpdateState { + FullSync, + SyncToTip, + Synced, +} + +/// DiffAdjManager makes use of DiffAdjStorage to provide thread save access to its LinearWeightedMovingAverages for +/// each PoW algorithm. +pub struct DiffAdjStorage +where T: BlockchainBackend +{ + blockchain_db: BlockchainDatabase, + monero_lwma: LinearWeightedMovingAverage, + blake_lwma: LinearWeightedMovingAverage, + sync_data: Option<(u64, BlockHash)>, + timestamps: VecDeque, + difficulty_block_window: u64, + diff_target_block_interval: u64, + median_timestamp_count: usize, +} + +impl DiffAdjStorage +where T: BlockchainBackend +{ + /// Constructs a new DiffAdjStorage with access to the blockchain db. + pub fn new(blockchain_db: BlockchainDatabase, consensus_constants: &ConsensusConstants) -> Self { + Self { + blockchain_db, + monero_lwma: LinearWeightedMovingAverage::new( + consensus_constants.get_difficulty_block_window() as usize, + consensus_constants.get_diff_target_block_interval(), + ), + blake_lwma: LinearWeightedMovingAverage::new( + consensus_constants.get_difficulty_block_window() as usize, + consensus_constants.get_diff_target_block_interval(), + ), + sync_data: None, + timestamps: VecDeque::new(), + difficulty_block_window: consensus_constants.get_difficulty_block_window(), + median_timestamp_count: consensus_constants.get_median_timestamp_count(), + diff_target_block_interval: consensus_constants.get_diff_target_block_interval(), + } + } + + // Check if the difficulty adjustment manager is in sync with specified height. It will also check if a full sync + // or update sync needs to be performed. + fn check_sync_state(&self, block_hash: &BlockHash, height: u64) -> Result { + Ok(match &self.sync_data { + Some((sync_height, sync_block_hash)) => { + if *sync_block_hash != *block_hash { + if height < *sync_height { + UpdateState::FullSync + } else { + let header = self.blockchain_db.fetch_header(*sync_height)?; + if *sync_block_hash == header.hash() { + UpdateState::SyncToTip + } else { + UpdateState::FullSync + } + } + } else { + UpdateState::Synced + } + }, + None => UpdateState::FullSync, + }) + } + + // Performs an update on the difficulty adjustment manager based on the detected sync state. + fn update(&mut self, height: u64) -> Result<(), DiffAdjManagerError> { + let block_hash = self.blockchain_db.fetch_header(height)?.hash(); + match self.check_sync_state(&block_hash, height)? { + UpdateState::FullSync => self.sync_full_history(block_hash, height)?, + UpdateState::SyncToTip => self.sync_to_chain_tip(block_hash, height)?, + UpdateState::Synced => {}, + }; + Ok(()) + } + + // Retrieves the height of the longest chain from the blockchain db + fn get_height_of_longest_chain(&mut self) -> Result { + self.blockchain_db + .get_metadata()? + .height_of_longest_chain + .ok_or_else(|| DiffAdjManagerError::EmptyBlockchain) + } + + /// Returns the estimated target difficulty for the specified PoW algorithm at the chain tip. + pub fn get_target_difficulty(&mut self, pow_algo: PowAlgorithm) -> Result { + let height = self.get_height_of_longest_chain()?; + self.get_target_difficulty_at_height(pow_algo, height) + } + + /// Returns the estimated target difficulty for the specified PoW algorithm and provided height. + pub fn get_target_difficulty_at_height( + &mut self, + pow_algo: PowAlgorithm, + height: u64, + ) -> Result + { + self.update(height)?; + Ok(match pow_algo { + PowAlgorithm::Monero => self.monero_lwma.get_difficulty(), + PowAlgorithm::Blake => self.blake_lwma.get_difficulty(), + }) + } + + /// Returns the median timestamp of the past 11 blocks at the chain tip. + pub fn get_median_timestamp(&mut self) -> Result { + let height = self.get_height_of_longest_chain()?; + self.get_median_timestamp_at_height(height) + } + + /// Returns the median timestamp of the past 11 blocks at the provided height. + pub fn get_median_timestamp_at_height(&mut self, height: u64) -> Result { + self.update(height)?; + let mut length = self.timestamps.len(); + if length == 0 { + return Err(DiffAdjManagerError::EmptyBlockchain); + } + let mut sorted_timestamps: Vec = self.timestamps.clone().into(); + sorted_timestamps.sort(); + trace!(target: LOG_TARGET, "sorted median timestamps: {:?}", sorted_timestamps); + length /= 2; // we want the median, should be index (MEDIAN_TIMESTAMP_COUNT/2) + Ok(sorted_timestamps[length]) + } + + // Resets the DiffAdjStorage. + fn reset(&mut self) { + self.monero_lwma = + LinearWeightedMovingAverage::new(self.difficulty_block_window as usize, self.diff_target_block_interval); + self.blake_lwma = + LinearWeightedMovingAverage::new(self.difficulty_block_window as usize, self.diff_target_block_interval); + self.sync_data = None; + self.timestamps = VecDeque::new(); + } + + // Adds the new PoW sample to the specific LinearWeightedMovingAverage specified by the PoW algorithm. + fn add(&mut self, timestamp: EpochTime, pow: ProofOfWork) -> Result<(), DiffAdjManagerError> { + match pow.pow_algo { + PowAlgorithm::Monero => self.monero_lwma.add(timestamp, pow.accumulated_monero_difficulty)?, + PowAlgorithm::Blake => self.blake_lwma.add(timestamp, pow.accumulated_blake_difficulty)?, + } + Ok(()) + } + + // Resets the DiffAdjStorage and perform a full sync using the blockchain db. + fn sync_full_history( + &mut self, + best_block: BlockHash, + height_of_longest_chain: u64, + ) -> Result<(), DiffAdjManagerError> + { + self.reset(); + let mut monero_diff_list = Vec::<(EpochTime, Difficulty)>::with_capacity(self.difficulty_block_window as usize); + let mut blake_diff_list = Vec::<(EpochTime, Difficulty)>::with_capacity(self.difficulty_block_window as usize); + for height in (0..=height_of_longest_chain).rev() { + let header = self.blockchain_db.fetch_header(height)?; + // keep MEDIAN_TIMESTAMP_COUNT blocks for median timestamp + if self.timestamps.len() < self.median_timestamp_count { + self.timestamps.push_front(header.timestamp); + } + match header.pow.pow_algo { + PowAlgorithm::Monero => { + if (monero_diff_list.len() as u64) < self.difficulty_block_window { + monero_diff_list.push(( + header.timestamp, + header.pow.accumulated_monero_difficulty + header.achieved_difficulty(), + )); + } + }, + PowAlgorithm::Blake => { + if (blake_diff_list.len() as u64) < self.difficulty_block_window { + blake_diff_list.push(( + header.timestamp, + header.pow.accumulated_blake_difficulty + header.achieved_difficulty(), + )); + } + }, + } + if ((monero_diff_list.len() as u64) >= self.difficulty_block_window) && + ((blake_diff_list.len() as u64) >= self.difficulty_block_window) + { + break; + } + } + for (timestamp, accumulated_difficulty) in monero_diff_list.into_iter().rev() { + self.monero_lwma.add(timestamp, accumulated_difficulty)? + } + for (timestamp, accumulated_difficulty) in blake_diff_list.into_iter().rev() { + self.blake_lwma.add(timestamp, accumulated_difficulty)? + } + self.sync_data = Some((height_of_longest_chain, best_block)); + + Ok(()) + } + + // The difficulty adjustment manager has fallen behind, perform an update to the chain tip. + fn sync_to_chain_tip( + &mut self, + best_block: BlockHash, + height_of_longest_chain: u64, + ) -> Result<(), DiffAdjManagerError> + { + if let Some((sync_height, _)) = self.sync_data { + for height in (sync_height + 1)..=height_of_longest_chain { + let header = self.blockchain_db.fetch_header(height)?; + // add new timestamps + self.timestamps.push_back(header.timestamp); + if self.timestamps.len() > self.median_timestamp_count { + self.timestamps.remove(0); // remove oldest + } + match header.pow.pow_algo { + PowAlgorithm::Monero => { + self.monero_lwma.add( + header.timestamp, + header.pow.accumulated_monero_difficulty + header.achieved_difficulty(), + )?; + }, + PowAlgorithm::Blake => { + self.blake_lwma.add( + header.timestamp, + header.pow.accumulated_blake_difficulty + header.achieved_difficulty(), + )?; + }, + } + } + self.sync_data = Some((height_of_longest_chain, best_block)); + } + Ok(()) + } +} diff --git a/base_layer/core/src/proof_of_work/diff_adj_manager/error.rs b/base_layer/core/src/proof_of_work/diff_adj_manager/error.rs new file mode 100644 index 0000000000..8e2e7f5830 --- /dev/null +++ b/base_layer/core/src/proof_of_work/diff_adj_manager/error.rs @@ -0,0 +1,32 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{chain_storage::ChainStorageError, proof_of_work::error::DifficultyAdjustmentError}; +use derive_error::Error; + +#[derive(Debug, Error, Clone, PartialEq)] +pub enum DiffAdjManagerError { + DifficultyAdjustmentError(DifficultyAdjustmentError), + ChainStorageError(ChainStorageError), + EmptyBlockchain, + PoisonedAccess, +} diff --git a/base_layer/core/src/proof_of_work/diff_adj_manager/mod.rs b/base_layer/core/src/proof_of_work/diff_adj_manager/mod.rs new file mode 100644 index 0000000000..30b2fac4a3 --- /dev/null +++ b/base_layer/core/src/proof_of_work/diff_adj_manager/mod.rs @@ -0,0 +1,28 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod diff_adj_manager; +mod diff_adj_storage; +mod error; + +pub use diff_adj_manager::DiffAdjManager; +pub use error::DiffAdjManagerError; diff --git a/base_layer/core/src/proof_of_work/difficulty.rs b/base_layer/core/src/proof_of_work/difficulty.rs index b09e7e8105..0a092f12fc 100644 --- a/base_layer/core/src/proof_of_work/difficulty.rs +++ b/base_layer/core/src/proof_of_work/difficulty.rs @@ -20,10 +20,12 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::proof_of_work::error::DifficultyAdjustmentError; use bitflags::_core::ops::Div; use newtype_ops::newtype_ops; use serde::{Deserialize, Serialize}; use std::fmt; +use tari_crypto::tari_utilities::epoch_time::EpochTime; /// Minimum difficulty, enforced in diff retargetting /// avoids getting stuck when trying to increase difficulty subject to dampening @@ -35,14 +37,26 @@ pub struct Difficulty(u64); impl Difficulty { /// Difficulty of MIN_DIFFICULTY - pub fn min() -> Difficulty { + pub const fn min() -> Difficulty { Difficulty(MIN_DIFFICULTY) } + + /// Return the difficulty as a u64 + pub fn as_u64(self) -> u64 { + self.0 + } + + pub fn checked_sub(self, other: Difficulty) -> Option { + match self.0.checked_sub(other.0) { + None => None, + Some(v) => Some(Difficulty(v)), + } + } } impl Default for Difficulty { fn default() -> Self { - Difficulty(0) + Difficulty::min() } } @@ -75,6 +89,21 @@ impl From for Difficulty { } } +/// General difficulty adjustment algorithm trait. The key method is `get_difficulty`, which returns the target +/// difficulty given a set of historical achieved difficulties; supplied through the `add` method. +pub trait DifficultyAdjustment { + /// Adds the latest block timestamp (in seconds) and total accumulated difficulty. If the new data point violates + /// some difficulty criteria, then `add` returns an error with the type of failure indicated + fn add( + &mut self, + timestamp: EpochTime, + accumulated_difficulty: Difficulty, + ) -> Result<(), DifficultyAdjustmentError>; + + /// Return the calculated target difficulty for the next block. + fn get_difficulty(&self) -> Difficulty; +} + #[cfg(test)] mod test { use crate::proof_of_work::difficulty::Difficulty; @@ -85,7 +114,7 @@ mod test { Difficulty::from(1_000) + Difficulty::from(8_000), Difficulty::from(9_000) ); - assert_eq!(Difficulty::default() + Difficulty::from(42), Difficulty::from(42)); + assert_eq!(Difficulty::default() + Difficulty::from(42), Difficulty::from(43)); assert_eq!(&Difficulty::from(15) + &Difficulty::from(5), Difficulty::from(20)); } } diff --git a/base_layer/core/src/proof_of_work/error.rs b/base_layer/core/src/proof_of_work/error.rs index 22a7e84cb3..4fa477bd38 100644 --- a/base_layer/core/src/proof_of_work/error.rs +++ b/base_layer/core/src/proof_of_work/error.rs @@ -26,4 +26,14 @@ use derive_error::Error; pub enum PowError { // ProofOfWorkFailed InvalidProofOfWork, + // Target difficulty not achieved + AchievedDifficultyTooLow, +} + +#[derive(Debug, Error, Clone, PartialEq)] +pub enum DifficultyAdjustmentError { + // Accumulated difficulty values can only strictly increase + DecreasingAccumulatedDifficulty, + // Other difficulty algorithm errors + Other, } diff --git a/base_layer/core/src/proof_of_work/lwma_diff.rs b/base_layer/core/src/proof_of_work/lwma_diff.rs new file mode 100644 index 0000000000..ef9fe3d744 --- /dev/null +++ b/base_layer/core/src/proof_of_work/lwma_diff.rs @@ -0,0 +1,236 @@ +// LWMA-1 for BTC & Zcash clones +// Copyright (c) 2017-2019 The Bitcoin Gold developers, Zawy, iamstenman (Microbitcoin) +// MIT License +// Algorithm by Zawy, a modification of WT-144 by Tom Harding +// References: +// https://github.com/zawy12/difficulty-algorithms/issues/3#issuecomment-442129791 +// https://github.com/zcash/zcash/issues/4021 + +use crate::proof_of_work::{ + difficulty::{Difficulty, DifficultyAdjustment}, + error::DifficultyAdjustmentError, +}; +use log::*; +use std::{cmp, collections::VecDeque}; +use tari_crypto::tari_utilities::epoch_time::EpochTime; +pub const LOG_TARGET: &str = "c::pow::lwma_diff"; + +const INITIAL_DIFFICULTY: Difficulty = Difficulty::min(); + +pub struct LinearWeightedMovingAverage { + timestamps: VecDeque, + accumulated_difficulties: VecDeque, + block_window: usize, + target_time: u64, +} + +impl LinearWeightedMovingAverage { + pub fn new(block_window: usize, target_time: u64) -> LinearWeightedMovingAverage { + LinearWeightedMovingAverage { + timestamps: VecDeque::with_capacity(block_window + 1), + accumulated_difficulties: VecDeque::with_capacity(block_window + 1), + block_window, + target_time, + } + } + + fn calculate(&self) -> Difficulty { + let timestamps = &self.timestamps; + if timestamps.len() <= 1 { + return INITIAL_DIFFICULTY; + } + + // Use the array length rather than block_window to include early cases where the no. of pts < block_window + let n = (timestamps.len() - 1) as u64; + + let mut weighted_times: u64 = 0; + + let difficulty: u64 = self.accumulated_difficulties[n as usize] + .checked_sub(self.accumulated_difficulties[0]) + .expect("Accumulated difficulties cannot decrease in proof of work") + .as_u64(); + let ave_difficulty = difficulty as f64 / n as f64; + + let mut previous_timestamp = timestamps[0]; + let mut this_timestamp; + // Loop through N most recent blocks. + for i in 1..(n + 1) as usize { + // 6*T limit prevents large drops in diff from long solve times which would cause oscillations. + // We cannot have if solve_time < 1 then solve_time = 1, this will greatly increase the next timestamp + // difficulty which will lower the difficulty + if timestamps[i] > previous_timestamp { + this_timestamp = timestamps[i]; + } else { + this_timestamp = previous_timestamp.increase(1); + } + let solve_time = cmp::min((this_timestamp - previous_timestamp).as_u64(), 6 * self.target_time); + previous_timestamp = this_timestamp; + + // Give linearly higher weight to more recent solve times. + // Note: This will not overflow for practical values of block_window and solve time. + weighted_times += solve_time * i as u64; + } + // k is the sum of weights (1+2+..+n) * target_time + let k = n * (n + 1) * self.target_time / 2; + let target = ave_difficulty * k as f64 / weighted_times as f64; + trace!( + target: LOG_TARGET, + "DiffCalc; t={}; bw={}; n={}; ts[0]={}; ts[n]={}; weighted_ts={}; k={}; diff[0]={}; diff[n]={}; \ + ave_difficulty={}; target={}", + self.target_time, + self.block_window, + n, + timestamps[0], + timestamps[n as usize], + weighted_times, + k, + self.accumulated_difficulties[0], + self.accumulated_difficulties[n as usize], + ave_difficulty, + target + ); + if target > std::u64::MAX as f64 { + error!( + target: LOG_TARGET, + "Difficulty has overflowed, current is: {:?}", target + ); + panic!("Difficulty target has overflowed"); + } + let target = target.ceil() as u64; // difficulty difference of 1 should not matter much, but difficulty should never be below 1, ceil(0.9) = 1 + debug!(target: LOG_TARGET, "New target difficulty: {}", target); + target.into() + } +} + +impl DifficultyAdjustment for LinearWeightedMovingAverage { + fn add( + &mut self, + timestamp: EpochTime, + accumulated_difficulty: Difficulty, + ) -> Result<(), DifficultyAdjustmentError> + { + trace!( + target: LOG_TARGET, + "Adding new timestamp and difficulty requested: {:?}, {:?}", + timestamp, + accumulated_difficulty + ); + match self.accumulated_difficulties.back() { + None => {}, + Some(v) => { + if accumulated_difficulty <= *v { + return Err(DifficultyAdjustmentError::DecreasingAccumulatedDifficulty); + } + }, + }; + self.timestamps.push_back(timestamp); + self.accumulated_difficulties.push_back(accumulated_difficulty); + while self.timestamps.len() > self.block_window + 1 { + self.timestamps.pop_front(); + self.accumulated_difficulties.pop_front(); + } + Ok(()) + } + + fn get_difficulty(&self) -> Difficulty { + self.calculate() + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn lwma_zero_len() { + let dif = LinearWeightedMovingAverage::new(90, 120); + assert_eq!(dif.get_difficulty(), Difficulty::min()); + } + + #[test] + fn lwma_add_non_increasing_diff() { + let mut dif = LinearWeightedMovingAverage::new(90, 120); + assert!(dif.add(100.into(), 100.into()).is_ok()); + assert!(dif.add(100.into(), 100.into()).is_err()); + assert!(dif.add(100.into(), 50.into()).is_err()); + } + + #[test] + fn lwma_negative_solve_times() { + let mut dif = LinearWeightedMovingAverage::new(90, 120); + let mut timestamp = 60.into(); + let mut cum_diff = Difficulty::from(100); + let _ = dif.add(timestamp, cum_diff); + timestamp = timestamp.increase(60); + cum_diff += Difficulty::from(100); + let _ = dif.add(timestamp, cum_diff); + // Lets create a history and populate the vecs + for _i in 0..150 { + cum_diff += Difficulty::from(100); + timestamp = timestamp.increase(60); + let _ = dif.add(timestamp, cum_diff); + } + // lets create chaos by having 60 blocks as negative solve times. This should never be allowed in practice by + // having checks on the block times. + for _i in 0..60 { + cum_diff += Difficulty::from(100); + timestamp = (timestamp.as_u64() - 1).into(); // Only choosing -1 here since we are testing negative solve times and we cannot have 0 time + let diff_before = dif.get_difficulty(); + let _ = dif.add(timestamp, cum_diff); + let diff_after = dif.get_difficulty(); + // Algo should handle this as 1sec solve time thus increase the difficulty constantly + assert!(diff_after > diff_before); + } + } + + #[test] + fn lwma_limit_difficulty_change() { + let mut dif = LinearWeightedMovingAverage::new(5, 60); + let _ = dif.add(60.into(), 100.into()); + let _ = dif.add(10_000_000.into(), 200.into()); + assert_eq!(dif.get_difficulty(), 17.into()); + let _ = dif.add(20_000_000.into(), 216.into()); + assert_eq!(dif.get_difficulty(), 10.into()); + } + + #[test] + // Data for 5-period moving average + // Timestamp: 60, 120, 180, 240, 300, 350, 380, 445, 515, 615, 975, 976, 977, 978, 979 + // Intervals: 60, 60, 60, 60, 60, 50, 30, 65, 70, 100, 360, 1, 1, 1, 1 + // Diff: 100, 100, 100, 100, 100, 105, 128, 123, 116, 94, 39, 46, 55, 75, 148 + // Acum dif: 100, 200, 300, 400, 500, 605, 733, 856, 972,1066,1105,1151,1206,1281,1429 + // Target: 1, 100, 100, 100, 100, 107, 136, 130, 120, 94, 36, 39, 47, 67, 175 + fn lwma_calculate() { + let mut dif = LinearWeightedMovingAverage::new(5, 60); + let _ = dif.add(60.into(), 100.into()); + assert_eq!(dif.get_difficulty(), 1.into()); + let _ = dif.add(120.into(), 200.into()); + assert_eq!(dif.get_difficulty(), 100.into()); + let _ = dif.add(180.into(), 300.into()); + assert_eq!(dif.get_difficulty(), 100.into()); + let _ = dif.add(240.into(), 400.into()); + assert_eq!(dif.get_difficulty(), 100.into()); + let _ = dif.add(300.into(), 500.into()); + assert_eq!(dif.get_difficulty(), 100.into()); + let _ = dif.add(350.into(), 605.into()); + assert_eq!(dif.get_difficulty(), 107.into()); + let _ = dif.add(380.into(), 733.into()); + assert_eq!(dif.get_difficulty(), 136.into()); + let _ = dif.add(445.into(), 856.into()); + assert_eq!(dif.get_difficulty(), 130.into()); + let _ = dif.add(515.into(), 972.into()); + assert_eq!(dif.get_difficulty(), 120.into()); + let _ = dif.add(615.into(), 1066.into()); + assert_eq!(dif.get_difficulty(), 94.into()); + let _ = dif.add(975.into(), 1105.into()); + assert_eq!(dif.get_difficulty(), 36.into()); + let _ = dif.add(976.into(), 1151.into()); + assert_eq!(dif.get_difficulty(), 39.into()); + let _ = dif.add(977.into(), 1206.into()); + assert_eq!(dif.get_difficulty(), 47.into()); + let _ = dif.add(978.into(), 1281.into()); + assert_eq!(dif.get_difficulty(), 67.into()); + let _ = dif.add(979.into(), 1429.into()); + assert_eq!(dif.get_difficulty(), 175.into()); + } +} diff --git a/base_layer/core/src/proof_of_work/mod.rs b/base_layer/core/src/proof_of_work/mod.rs index 32bf162ff8..feec825e57 100644 --- a/base_layer/core/src/proof_of_work/mod.rs +++ b/base_layer/core/src/proof_of_work/mod.rs @@ -21,11 +21,20 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. mod blake_pow; +mod diff_adj_manager; mod difficulty; mod error; -mod pow; +mod monero_rx; +mod proof_of_work; -pub use blake_pow::BlakePow; -pub use difficulty::Difficulty; -pub use error::PowError; -pub use pow::ProofOfWork; +#[cfg(test)] +pub use blake_pow::test as blake_test; + +pub mod lwma_diff; + +pub use blake_pow::{blake_difficulty, blake_difficulty_with_hash}; +pub use diff_adj_manager::{DiffAdjManager, DiffAdjManagerError}; +pub use difficulty::{Difficulty, DifficultyAdjustment}; +pub use error::{DifficultyAdjustmentError, PowError}; +pub use monero_rx::monero_difficulty; +pub use proof_of_work::{PowAlgorithm, ProofOfWork}; diff --git a/base_layer/core/src/proof_of_work/monero_rx.rs b/base_layer/core/src/proof_of_work/monero_rx.rs new file mode 100644 index 0000000000..9a6bda110f --- /dev/null +++ b/base_layer/core/src/proof_of_work/monero_rx.rs @@ -0,0 +1,106 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{blocks::BlockHeader, proof_of_work::Difficulty}; +use bigint::uint::U256; +use derive_error::Error; +use monero::blockdata::{block::BlockHeader as MoneroBlockHeader, Transaction as MoneroTransaction}; +use randomx_rs::{RandomXCache, RandomXError, RandomXFlag, RandomXVM}; +use serde::{Deserialize, Serialize}; +use tari_mmr::MerkleProof; + +const MAX_TARGET: U256 = U256::MAX; + +#[derive(Debug, Error, Clone)] +enum MergeMineError { + // Error deserializing Monero data + DeserializeError, + // Hashing of Monero data failed + HashingError, + // RandomX Failure + RandomXError(RandomXError), +} + +/// This is a struct to deserialize the data from he pow field into data required for the randomX Monero merged mine +/// pow. +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +pub struct MoneroData { + // Monero header fields + // #[serde(with = "HashMoneroHeader")] + header: MoneroBlockHeader, + // randomX vm key + key: String, + // transaction count + count: u16, + // transaction root + transaction_root: [u8; 32], + // Transaction proof of work. + merkle_proof: MerkleProof, + // Coinbase tx from Monero + coinbase_tx: MoneroTransaction, +} + +impl MoneroData { + fn new(tari_header: &BlockHeader) -> Result { + bincode::deserialize(&tari_header.pow.pow_data).map_err(|_| MergeMineError::DeserializeError) + } +} + +/// Calculate the difficulty attained for the given block deserialized the Monero header from the provided header +pub fn monero_difficulty(header: &BlockHeader) -> Difficulty { + match monero_difficulty_calculation(header) { + Ok(v) => v, + Err(_) => 1.into(), // todo this needs to change to 0 when merge mine is implemented + } +} + +/// Internal function to calculate the difficulty attained for the given block Deserialized the Monero header from the +/// provided header +fn monero_difficulty_calculation(header: &BlockHeader) -> Result { + let monero = MoneroData::new(header)?; + verify_header(&header, &monero)?; + let flags = RandomXFlag::FLAG_DEFAULT; + let key = monero.key.clone(); + let input = create_input_blob(&monero)?; + let cache = RandomXCache::new(flags, &key)?; + let vm = RandomXVM::new(flags, &cache, None)?; + let hash = vm.calculate_hash(&input)?; + + let scalar = U256::from_big_endian(&hash); // Big endian so the hash has leading zeroes + let result = MAX_TARGET / scalar; + let difficulty = u64::from(result).into(); + Ok(difficulty) +} + +fn create_input_blob(_data: &MoneroData) -> Result { + // Todo deserialize monero data to create string for randomX vm + // Returning an error here so that difficulty can return 0 as this is not yet implemented. + Err(MergeMineError::HashingError) +} + +fn verify_header(_header: &BlockHeader, _monero_data: &MoneroData) -> Result<(), MergeMineError> { + // todo + // verify that our header is in coinbase + // todo + // verify that coinbase is in root. + Ok(()) +} diff --git a/base_layer/core/src/proof_of_work/proof_of_work.rs b/base_layer/core/src/proof_of_work/proof_of_work.rs new file mode 100644 index 0000000000..c9e891ac37 --- /dev/null +++ b/base_layer/core/src/proof_of_work/proof_of_work.rs @@ -0,0 +1,287 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + blocks::BlockHeader, + proof_of_work::{blake_pow::blake_difficulty, monero_rx::monero_difficulty, Difficulty}, +}; +use bytes::{self, BufMut}; +use serde::{Deserialize, Serialize}; +use std::{ + convert::TryFrom, + fmt::{Display, Error, Formatter}, +}; +use tari_crypto::tari_utilities::hex::Hex; + +pub trait AchievedDifficulty {} + +#[repr(u8)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)] +pub enum PowAlgorithm { + Monero = 0, + Blake = 1, +} + +/// Used to compare proof of work difficulties without scaling factors +#[derive(Debug, Clone, PartialEq)] +pub enum Ordering { + GreaterThan, + LessThan, + Equal, + Indeterminate, +} + +impl TryFrom for PowAlgorithm { + type Error = String; + + fn try_from(v: u64) -> Result { + match v { + 0 => Ok(PowAlgorithm::Monero), + 1 => Ok(PowAlgorithm::Blake), + _ => Err("Invalid PoWAlgorithm".into()), + } + } +} + +/// The proof of work data structure that is included in the block header. There's some non-Rustlike redundancy here +/// to make serialization more straightforward +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct ProofOfWork { + /// The total accumulated difficulty for each proof of work algorithms for all blocks since Genesis, + /// but not including this block, tracked separately. + pub accumulated_monero_difficulty: Difficulty, + pub accumulated_blake_difficulty: Difficulty, + /// The algorithm used to mine this block + pub pow_algo: PowAlgorithm, + /// Supplemental proof of work data. For example for Blake, this would be empty (only the block header is + /// required), but for Monero merge mining we need the Monero block header and RandomX seed hash. + pub pow_data: Vec, +} + +impl Default for ProofOfWork { + fn default() -> Self { + Self { + accumulated_monero_difficulty: Difficulty::default(), + accumulated_blake_difficulty: Difficulty::default(), + pow_algo: PowAlgorithm::Blake, + pow_data: vec![], + } + } +} + +impl ProofOfWork { + /// Create a new `ProofOfWork` instance. Except for the algorithm used, the fields are uninitialized. + /// [achieved_difficulty] and [add_difficulty] can be used subsequently to properly populate the struct's fields. + pub fn new(pow_algo: PowAlgorithm) -> Self { + Self { + pow_algo, + accumulated_monero_difficulty: Difficulty::default(), + accumulated_blake_difficulty: Difficulty::default(), + pow_data: vec![], + } + } + + /// This function will calculate the achieved difficulty for the proof of work + /// given the block header. + /// This function is used to validate proofs of work generated by miners. + /// + /// Generally speaking, the difficulty is roughly how many mining attempts a miner will make, _on average_ before + /// finding a nonce that meets the difficulty target. + /// + /// In actuality, the difficulty is _defined_ as the maximum target value (u265) divided by the block header hash + /// (as a u256) + /// + /// If there are any problems with calculating a difficulty (e.g. an invalid header), then the function returns a + /// difficulty of one. + pub fn achieved_difficulty(header: &BlockHeader) -> Difficulty { + match header.pow.pow_algo { + PowAlgorithm::Monero => monero_difficulty(header), + PowAlgorithm::Blake => blake_difficulty(header), + } + } + + /// Calculates the total _ accumulated difficulty for the blockchain from the genesis block up until, + /// but _not including_ this block. + /// + /// This uses a geometric mean to compare the two difficulties. See Issue #1075 (https://github.com/tari-project/tari/issues/1075) as to why this was done + /// + /// The total accumulated difficulty is most often used to decide on which of two forks is the longest chain. + pub fn total_accumulated_difficulty(&self) -> Difficulty { + let d = (self.accumulated_monero_difficulty.as_u64() as f64 * + self.accumulated_blake_difficulty.as_u64() as f64) + .sqrt(); + + Difficulty::from(d.ceil() as u64) + } + + /// Replaces the `next` proof of work's difficulty with the sum of this proof of work's total cumulative + /// difficulty and the provided `added_difficulty`. + pub fn add_difficulty(&mut self, prev: &ProofOfWork, added_difficulty: Difficulty) { + let (m, b) = match prev.pow_algo { + PowAlgorithm::Monero => ( + prev.accumulated_monero_difficulty + added_difficulty, + prev.accumulated_blake_difficulty, + ), + PowAlgorithm::Blake => ( + prev.accumulated_monero_difficulty, + prev.accumulated_blake_difficulty + added_difficulty, + ), + }; + self.accumulated_blake_difficulty = b; + self.accumulated_monero_difficulty = m; + } + + /// Compare the difficulties of this and another proof of work, without knowing anything about scaling factors. + /// Even without scaling factors, it is often possible to definitively order difficulties. + pub fn partial_cmp(&self, other: &ProofOfWork) -> Ordering { + if self.accumulated_blake_difficulty == other.accumulated_blake_difficulty && + self.accumulated_monero_difficulty == other.accumulated_monero_difficulty + { + Ordering::Equal + } else if self.accumulated_blake_difficulty <= other.accumulated_blake_difficulty && + self.accumulated_monero_difficulty <= other.accumulated_monero_difficulty + { + Ordering::LessThan + } else if self.accumulated_blake_difficulty >= other.accumulated_blake_difficulty && + self.accumulated_monero_difficulty >= other.accumulated_monero_difficulty + { + Ordering::GreaterThan + } else { + Ordering::Indeterminate + } + } + + /// Serialises the ProofOfWork instance into a byte string. Useful for feeding the PoW into a hash function. + pub fn to_bytes(&self) -> Vec { + let mut buf: Vec = Vec::with_capacity(256); + buf.put_u8(self.pow_algo as u8); + buf.put_u64_le(self.accumulated_monero_difficulty.as_u64()); + buf.put_u64_le(self.accumulated_blake_difficulty.as_u64()); + buf.put_slice(&self.pow_data); + buf + } +} + +impl Display for PowAlgorithm { + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), Error> { + let algo = match self { + PowAlgorithm::Monero => "Monero", + PowAlgorithm::Blake => "Blake", + }; + fmt.write_str(&algo.to_string()) + } +} + +impl Display for ProofOfWork { + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), Error> { + fmt.write_str(&format!( + "Mining algorithm: {}, \nTotal accumulated difficulty: \nMonero={}, Blake={}\nPow data: {}", + self.pow_algo, + self.accumulated_monero_difficulty, + self.accumulated_blake_difficulty, + self.pow_data.to_hex(), + )) + } +} + +#[cfg(test)] +mod test { + use crate::proof_of_work::{ + proof_of_work::{Ordering, PowAlgorithm, ProofOfWork}, + Difficulty, + }; + + #[test] + fn display() { + let pow = ProofOfWork::default(); + assert_eq!( + &format!("{}", pow), + "Mining algorithm: Blake, \nTotal accumulated difficulty: \nMonero=1, Blake=1\nPow data: " + ); + } + + #[test] + fn to_bytes() { + let mut pow = ProofOfWork::default(); + pow.accumulated_monero_difficulty = Difficulty::from(65); + pow.accumulated_blake_difficulty = Difficulty::from(257); + pow.pow_algo = PowAlgorithm::Blake; + assert_eq!(pow.to_bytes(), vec![1, 65, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0]); + } + + #[test] + fn total_difficulty() { + let mut pow = ProofOfWork::default(); + // Simple cases + pow.accumulated_monero_difficulty = 500.into(); + pow.accumulated_blake_difficulty = 100.into(); + assert_eq!(pow.total_accumulated_difficulty(), 224.into(), "Case 1"); + pow.accumulated_monero_difficulty = 50.into(); + pow.accumulated_blake_difficulty = 1000.into(); + assert_eq!(pow.total_accumulated_difficulty(), 224.into(), "Case 2"); + // Edge cases - Very large OOM difficulty differences + pow.accumulated_monero_difficulty = 444.into(); + pow.accumulated_blake_difficulty = 1_555_222_888_555_555.into(); + assert_eq!(pow.total_accumulated_difficulty(), 830_974_707.into(), "Case 3"); + pow.accumulated_monero_difficulty = 1.into(); + pow.accumulated_blake_difficulty = 15_222_333_444_555_666_777.into(); + assert_eq!(pow.total_accumulated_difficulty(), 3_901_580_891.into(), "Case 4"); + } + + #[test] + fn add_difficulty() { + let mut pow = ProofOfWork::new(PowAlgorithm::Monero); + pow.accumulated_blake_difficulty = Difficulty::from(42); + pow.accumulated_monero_difficulty = Difficulty::from(420); + let mut pow2 = ProofOfWork::default(); + pow2.add_difficulty(&pow, Difficulty::from(80)); + assert_eq!(pow2.accumulated_blake_difficulty, Difficulty::from(42)); + assert_eq!(pow2.accumulated_monero_difficulty, Difficulty::from(500)); + } + + #[test] + fn partial_cmp() { + let mut pow1 = ProofOfWork::default(); + let mut pow2 = ProofOfWork::default(); + // (0,0) vs (0,0) + assert_eq!(pow1.partial_cmp(&pow2), Ordering::Equal); + pow1.accumulated_monero_difficulty = 100.into(); + // (100,0) vs (0,0) + assert_eq!(pow1.partial_cmp(&pow2), Ordering::GreaterThan); + pow2.accumulated_blake_difficulty = 50.into(); + // (100,0) vs (0,50) + assert_eq!(pow1.partial_cmp(&pow2), Ordering::Indeterminate); + pow2.accumulated_monero_difficulty = 110.into(); + // (100,0) vs (110, 50) + assert_eq!(pow1.partial_cmp(&pow2), Ordering::LessThan); + pow1.accumulated_blake_difficulty = 50.into(); + // (100,50) vs (110, 50) + assert_eq!(pow1.partial_cmp(&pow2), Ordering::LessThan); + pow1.accumulated_monero_difficulty = 110.into(); + // (110,50) vs (110, 50) + assert_eq!(pow1.partial_cmp(&pow2), Ordering::Equal); + pow1.accumulated_monero_difficulty = 200.into(); + pow1.accumulated_blake_difficulty = 80.into(); + // (200,80) vs (110, 50) + assert_eq!(pow1.partial_cmp(&pow2), Ordering::GreaterThan); + } +} diff --git a/base_layer/core/src/proto/block.proto b/base_layer/core/src/proto/block.proto new file mode 100644 index 0000000000..82fab7046b --- /dev/null +++ b/base_layer/core/src/proto/block.proto @@ -0,0 +1,85 @@ +syntax = "proto3"; + +import "google/protobuf/wrappers.proto"; +import "google/protobuf/timestamp.proto"; +import "transaction.proto"; +import "types.proto"; + +package tari.core; + +// Metadata required for validating the Proof of Work calculation +message ProofOfWork { + // 0 = Monero + // 1 = Blake + uint64 pow_algo = 1; + uint64 accumulated_monero_difficulty = 2; + uint64 accumulated_blake_difficulty = 3; + bytes pow_data = 4; +} + +// The BlockHeader contains all the metadata for the block, including proof of work, a link to the previous block +// and the transaction kernels. +message BlockHeader { + // Version of the block + uint32 version = 1; + // Height of this block since the genesis block (height 0) + uint64 height = 2; + // Hash of the block previous to this in the chain. + bytes prev_hash = 4; + // Timestamp at which the block was built. + google.protobuf.Timestamp timestamp = 5; + // This is the UTXO merkle root of the outputs + // This is calculated as Hash (txo MMR root || roaring bitmap hash of UTXO indices) + bytes output_mr = 6; + // This is the MMR root of the range proofs + bytes range_proof_mr = 7; + // This is the MMR root of the kernels + bytes kernel_mr = 8; + // Total accumulated sum of kernel offsets since genesis block. We can derive the kernel offset sum for *this* + // block from the total kernel offset of the previous block header. + bytes total_kernel_offset = 9; + // Nonce increment used to mine this block. + uint64 nonce = 10; + // Proof of work metadata + ProofOfWork pow = 11; +} + +// A Tari block. Blocks are linked together into a blockchain. +message Block { + BlockHeader header = 1; + tari.types.AggregateBody body = 2; +} + +// The representation of a historical block in the blockchain. It is essentially identical to a protocol-defined +// block but contains some extra metadata that clients such as Block Explorers will find interesting. +message HistoricalBlock { + // The number of blocks that have been mined since this block, including this one. The current tip will have one + // confirmation. + uint64 confirmations = 1; + // An array of commitments of the outputs from this block that have subsequently been spent. + repeated tari.types.Commitment spent_commitments = 2; + // The underlying block + Block block = 3; +} + + +// The NewBlockHeaderTemplate is used for the construction of a new mineable block. It contains all the metadata for the block that the Base Node is able to complete on behalf of a Miner. +message NewBlockHeaderTemplate { + // Version of the block + uint32 version = 1; + // Height of this block since the genesis block (height 0) + uint64 height = 2; + // Hash of the block previous to this in the chain. + bytes prev_hash = 3; + // Total accumulated sum of kernel offsets since genesis block. We can derive the kernel offset sum for *this* + // block from the total kernel offset of the previous block header. + bytes total_kernel_offset = 4; + // Proof of work metadata + ProofOfWork pow = 5; +} + +// The new block template is used constructing a new partial block, allowing a miner to added the coinbase utxo and as a final step the Base node to add the MMR roots to the header. +message NewBlockTemplate { + NewBlockHeaderTemplate header = 1; + tari.types.AggregateBody body = 2; +} \ No newline at end of file diff --git a/base_layer/core/src/proto/block.rs b/base_layer/core/src/proto/block.rs new file mode 100644 index 0000000000..0de01d6140 --- /dev/null +++ b/base_layer/core/src/proto/block.rs @@ -0,0 +1,246 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::core as proto; +use crate::{ + blocks::{Block, BlockHeader, NewBlockHeaderTemplate, NewBlockTemplate}, + chain_storage::HistoricalBlock, + proof_of_work::{Difficulty, PowAlgorithm, ProofOfWork}, + proto::utils::try_convert_all, + transactions::types::BlindingFactor, +}; +use prost_types::Timestamp; +use std::convert::{TryFrom, TryInto}; +use tari_crypto::tari_utilities::{epoch_time::EpochTime, ByteArray, ByteArrayError}; + +/// Utility function that converts a `prost::Timestamp` to a `chrono::DateTime` +pub(crate) fn timestamp_to_datetime(timestamp: Timestamp) -> EpochTime { + (timestamp.seconds as u64).into() +} + +/// Utility function that converts a `chrono::DateTime` to a `prost::Timestamp` +pub(crate) fn datetime_to_timestamp(datetime: EpochTime) -> Timestamp { + Timestamp { + seconds: datetime.as_u64() as i64, + nanos: 0, + } +} + +//---------------------------------- Block --------------------------------------------// + +impl TryFrom for Block { + type Error = String; + + fn try_from(block: proto::Block) -> Result { + let header = block + .header + .map(TryInto::try_into) + .ok_or_else(|| "Block header not provided".to_string())??; + + let body = block + .body + .map(TryInto::try_into) + .ok_or_else(|| "Block body not provided".to_string())??; + + Ok(Self { header, body }) + } +} + +impl From for proto::Block { + fn from(block: Block) -> Self { + Self { + header: Some(block.header.into()), + body: Some(block.body.into()), + } + } +} + +//---------------------------------- BlockHeader --------------------------------------------// + +impl TryFrom for BlockHeader { + type Error = String; + + fn try_from(header: proto::BlockHeader) -> Result { + let total_kernel_offset = + BlindingFactor::from_bytes(&header.total_kernel_offset).map_err(|err| err.to_string())?; + + let timestamp = header + .timestamp + .map(timestamp_to_datetime) + .ok_or_else(|| "timestamp not provided".to_string())?; + + let pow = match header.pow { + Some(p) => ProofOfWork::try_from(p)?, + None => return Err("No proof of work provided".into()), + }; + Ok(Self { + version: header.version as u16, + height: header.height, + prev_hash: header.prev_hash, + timestamp, + output_mr: header.output_mr, + range_proof_mr: header.range_proof_mr, + kernel_mr: header.kernel_mr, + total_kernel_offset, + nonce: header.nonce, + pow, + }) + } +} + +impl From for proto::BlockHeader { + fn from(header: BlockHeader) -> Self { + Self { + version: header.version as u32, + height: header.height, + prev_hash: header.prev_hash, + timestamp: Some(datetime_to_timestamp(header.timestamp)), + output_mr: header.output_mr, + range_proof_mr: header.range_proof_mr, + kernel_mr: header.kernel_mr, + total_kernel_offset: header.total_kernel_offset.to_vec(), + nonce: header.nonce, + pow: Some(proto::ProofOfWork::from(header.pow)), + } + } +} + +//---------------------------------- ProofOfWork --------------------------------------------// + +impl TryFrom for ProofOfWork { + type Error = String; + + fn try_from(pow: proto::ProofOfWork) -> Result { + Ok(Self { + pow_algo: PowAlgorithm::try_from(pow.pow_algo)?, + accumulated_monero_difficulty: Difficulty::from(pow.accumulated_monero_difficulty), + accumulated_blake_difficulty: Difficulty::from(pow.accumulated_blake_difficulty), + pow_data: pow.pow_data, + }) + } +} + +impl From for proto::ProofOfWork { + fn from(pow: ProofOfWork) -> Self { + Self { + pow_algo: pow.pow_algo as u64, + accumulated_monero_difficulty: pow.accumulated_monero_difficulty.as_u64(), + accumulated_blake_difficulty: pow.accumulated_blake_difficulty.as_u64(), + pow_data: pow.pow_data, + } + } +} + +//---------------------------------- HistoricalBlock --------------------------------------------// + +impl TryFrom for HistoricalBlock { + type Error = String; + + fn try_from(historical_block: proto::HistoricalBlock) -> Result { + let spent_commitments = + try_convert_all(historical_block.spent_commitments).map_err(|err: ByteArrayError| err.to_string())?; + + let block = historical_block + .block + .map(TryInto::try_into) + .ok_or_else(|| "block in historical block not provided".to_string())??; + + Ok(Self { + confirmations: historical_block.confirmations, + spent_commitments, + block, + }) + } +} + +impl From for proto::HistoricalBlock { + fn from(block: HistoricalBlock) -> Self { + Self { + confirmations: block.confirmations, + spent_commitments: block.spent_commitments.into_iter().map(Into::into).collect(), + block: Some(block.block.into()), + } + } +} + +//--------------------------------- NewBlockTemplate -------------------------------------------// + +impl TryFrom for NewBlockTemplate { + type Error = String; + + fn try_from(block_template: proto::NewBlockTemplate) -> Result { + let header = block_template + .header + .map(TryInto::try_into) + .ok_or_else(|| "Block header template not provided".to_string())??; + + let body = block_template + .body + .map(TryInto::try_into) + .ok_or_else(|| "Block body not provided".to_string())??; + + Ok(Self { header, body }) + } +} + +impl From for proto::NewBlockTemplate { + fn from(block_template: NewBlockTemplate) -> Self { + Self { + header: Some(block_template.header.into()), + body: Some(block_template.body.into()), + } + } +} + +//------------------------------ NewBlockHeaderTemplate ----------------------------------------// + +impl TryFrom for NewBlockHeaderTemplate { + type Error = String; + + fn try_from(header: proto::NewBlockHeaderTemplate) -> Result { + let total_kernel_offset = + BlindingFactor::from_bytes(&header.total_kernel_offset).map_err(|err| err.to_string())?; + let pow = match header.pow { + Some(p) => ProofOfWork::try_from(p)?, + None => return Err("No proof of work provided".into()), + }; + Ok(Self { + version: header.version as u16, + height: header.height, + prev_hash: header.prev_hash, + total_kernel_offset, + pow, + }) + } +} + +impl From for proto::NewBlockHeaderTemplate { + fn from(header: NewBlockHeaderTemplate) -> Self { + Self { + version: header.version as u32, + height: header.height, + prev_hash: header.prev_hash, + total_kernel_offset: header.total_kernel_offset.to_vec(), + pow: Some(proto::ProofOfWork::from(header.pow)), + } + } +} diff --git a/base_layer/core/src/proto/mod.rs b/base_layer/core/src/proto/mod.rs new file mode 100644 index 0000000000..5e192895a9 --- /dev/null +++ b/base_layer/core/src/proto/mod.rs @@ -0,0 +1,35 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Required for `super::types` used in generated files +use crate::transactions::proto::types; + +pub mod core { + include!(concat!(env!("OUT_DIR"), "/", "tari.core.rs")); +} + +cfg_if! { + if #[cfg(feature = "base_node")] { + mod block; + pub mod utils; + } +} diff --git a/base_layer/core/src/proto/utils.rs b/base_layer/core/src/proto/utils.rs new file mode 100644 index 0000000000..b99e334815 --- /dev/null +++ b/base_layer/core/src/proto/utils.rs @@ -0,0 +1,37 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::convert::TryInto; + +/// Tries to convert a series of `T`s to `U`s, returning an error at the first failure +pub fn try_convert_all(into_iter: I) -> Result, T::Error> +where + I: IntoIterator, + T: TryInto, +{ + let iter = into_iter.into_iter(); + let mut result = Vec::with_capacity(iter.size_hint().0); + for item in iter { + result.push(item.try_into()?); + } + Ok(result) +} diff --git a/base_layer/core/src/test_utils/builders.rs b/base_layer/core/src/test_utils/builders.rs deleted file mode 100644 index e799bcc2c1..0000000000 --- a/base_layer/core/src/test_utils/builders.rs +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2019. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// - -use crate::{ - blocks::{aggregated_body::AggregateBody, Block, BlockHeader}, - tari_amount::MicroTari, - transaction::{KernelBuilder, OutputFeatures, Transaction, TransactionInput, TransactionKernel, TransactionOutput}, - transaction_protocol::{build_challenge, TransactionMetadata}, - types::{Commitment, PrivateKey, PublicKey, RangeProof, Signature, COMMITMENT_FACTORY, PROVER}, -}; -use tari_crypto::{ - commitment::HomomorphicCommitmentFactory, - keys::{PublicKey as PK, SecretKey}, - range_proof::RangeProofService, -}; - -/// Create an unconfirmed transaction for testing with a valid fee, unique access_sig, random inputs and outputs, the -/// transaction is only partially constructed -pub fn create_test_tx( - amount: MicroTari, - fee: MicroTari, - lock_height: u64, - input_count: usize, - output_count: usize, -) -> Transaction -{ - let mut rng = rand::OsRng::new().unwrap(); - let kernel = create_test_kernel(fee, lock_height); - let mut body = AggregateBody::empty(); - body.kernels.push(kernel); - - for _ in 0..input_count { - let input = TransactionInput::new( - OutputFeatures::default(), - COMMITMENT_FACTORY.commit(&PrivateKey::random(&mut rng), &amount.into()), - ); - body.inputs.push(input); - } - - for _ in 0..output_count { - let output = TransactionOutput::new( - OutputFeatures::default(), - COMMITMENT_FACTORY.commit(&PrivateKey::random(&mut rng), &MicroTari(10).into()), - RangeProof::default(), - ); - body.outputs.push(output); - } - - Transaction { - offset: PrivateKey::random(&mut rng), - body, - } -} - -/// Create a transaction kernel with the given fee, using random keys to generate the signature -pub fn create_test_kernel(fee: MicroTari, lock_height: u64) -> TransactionKernel { - let (excess, s) = create_random_signature(fee); - KernelBuilder::new() - .with_fee(fee) - .with_lock_height(lock_height) - .with_excess(&Commitment::from_public_key(&excess)) - .with_signature(&s) - .build() - .unwrap() -} - -/// Create a partially constructed block using the provided set of transactions -pub fn create_test_block(block_height: u64, transactions: Vec) -> Block { - let mut header = BlockHeader::new(0); - header.height = block_height; - let mut body = AggregateBody::empty(); - transactions.iter().for_each(|tx| { - body.kernels.push(tx.body.kernels[0].clone()); - body.inputs.append(&mut tx.body.inputs.clone()); - body.outputs.append(&mut tx.body.outputs.clone()); - }); - - Block { header, body } -} - -/// Create a partially constructed utxo set using the outputs of a test block -pub fn extract_outputs_as_inputs(utxos: &mut Vec, published_block: &Block) { - for output in &published_block.body.outputs { - let input = TransactionInput::from(output.clone()); - if !utxos.contains(&input) { - utxos.push(input); - } - } -} - -/// Generate a random signature, returning the public key (excess) and the signature. -pub fn create_random_signature(fee: MicroTari) -> (PublicKey, Signature) { - let mut rng = rand::OsRng::new().unwrap(); - let r = SecretKey::random(&mut rng); - let (k, p) = PublicKey::random_keypair(&mut rng); - let tx_meta = TransactionMetadata { fee, lock_height: 0 }; - let e = build_challenge(&PublicKey::from_secret_key(&r), &tx_meta); - (p, Signature::sign(k, r, &e).unwrap()) -} - -/// A convenience struct for a set of public-private keys and a public-private nonce -pub struct TestKeySet { - k: PrivateKey, - pk: PublicKey, - r: PrivateKey, - pr: PublicKey, -} - -pub fn generate_keys() -> TestKeySet { - let mut rng = rand::OsRng::new().unwrap(); - let (k, pk) = PublicKey::random_keypair(&mut rng); - let (r, pr) = PublicKey::random_keypair(&mut rng); - TestKeySet { k, pk, r, pr } -} - -/// Create a new UTXO for the specified value and return the output and spending key -pub fn create_utxo(value: MicroTari) -> (TransactionOutput, PrivateKey) { - let keys = generate_keys(); - let commitment = COMMITMENT_FACTORY.commit_value(&keys.k, value.into()); - let proof = PROVER.construct_proof(&keys.k, value.into()).unwrap(); - let utxo = TransactionOutput::new(OutputFeatures::default(), commitment, proof.into()); - (utxo, keys.k) -} diff --git a/base_layer/core/src/blocks/aggregated_body.rs b/base_layer/core/src/transactions/aggregated_body.rs similarity index 66% rename from base_layer/core/src/blocks/aggregated_body.rs rename to base_layer/core/src/transactions/aggregated_body.rs index df51440de8..5b0dddb214 100644 --- a/base_layer/core/src/blocks/aggregated_body.rs +++ b/base_layer/core/src/transactions/aggregated_body.rs @@ -1,4 +1,4 @@ -// Copyright 2019. The Tari Project +// Copyright 2019, The Tari Project // // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the // following conditions are met: @@ -20,14 +20,16 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{ - blocks::block::KernelSum, +use crate::transactions::{ tari_amount::*, transaction::*, - types::{BlindingFactor, Commitment, CommitmentFactory, PrivateKey, RangeProofService, COMMITMENT_FACTORY}, + types::{BlindingFactor, Commitment, CommitmentFactory, CryptoFactories, PrivateKey, RangeProofService}, }; +use log::*; use serde::{Deserialize, Serialize}; +use std::fmt::{Display, Error, Formatter}; use tari_crypto::{commitment::HomomorphicCommitmentFactory, ristretto::pedersen::PedersenCommitment}; +pub const LOG_TARGET: &str = "c::tx::aggregated_body"; /// The components of the block or transaction. The same struct can be used for either, since in Mimblewimble, /// cut-through means that blocks and transactions have the same structure. @@ -35,11 +37,11 @@ use tari_crypto::{commitment::HomomorphicCommitmentFactory, ristretto::pedersen: pub struct AggregateBody { sorted: bool, /// List of inputs spent by the transaction. - pub inputs: Vec, + inputs: Vec, /// List of outputs the transaction produces. - pub outputs: Vec, + outputs: Vec, /// Kernels contain the excesses and their signatures for transaction - pub kernels: Vec, + kernels: Vec, } impl AggregateBody { @@ -68,6 +70,26 @@ impl AggregateBody { } } + /// Provide read-only access to the input list + pub fn inputs(&self) -> &Vec { + &self.inputs + } + + /// Provide read-only access to the output list + pub fn outputs(&self) -> &Vec { + &self.outputs + } + + /// Provide read-only access to the kernel list + pub fn kernels(&self) -> &Vec { + &self.kernels + } + + /// Should be used for tests only. Get a mutable reference to the inputs + pub fn inputs_mut(&mut self) -> &mut Vec { + &mut self.inputs + } + /// Add an input to the existing aggregate body pub fn add_input(&mut self, input: TransactionInput) { self.inputs.push(input); @@ -97,11 +119,47 @@ impl AggregateBody { self.kernels.push(kernel); } + /// Add a kernels to the existing aggregate body + pub fn add_kernels(&mut self, new_kernels: &mut Vec) { + self.kernels.append(new_kernels); + self.sorted = false; + } + /// Set the kernel of the aggregate body, replacing any previous kernels pub fn set_kernel(&mut self, kernel: TransactionKernel) { self.kernels = vec![kernel]; } + /// This will perform cut-through on the aggregate body. It will remove all outputs (and inputs) that are being + /// spent as inputs. + pub fn do_cut_through(&mut self) { + let double_inputs: Vec = self + .inputs + .iter() + .filter(|input| self.outputs.iter().any(|o| o.is_equal_to(input))) + .cloned() + .collect(); + + for input in double_inputs { + trace!( + target: LOG_TARGET, + "removing following utxo for cut-through: {:?}", + input + ); + self.outputs.retain(|x| !input.is_equal_to(x)); + self.inputs.retain(|x| *x != input); + } + } + + /// This will perform a check that cut-through was performed on the aggregate body. It will return true if there are + /// no outputs that are being spent as inputs. + pub fn cut_through_check(&self) -> bool { + !self + .inputs + .iter() + .any(|input| self.outputs.iter().any(|o| o.is_equal_to(input))) + } + /// Sort the component lists of the aggregate body pub fn sort(&mut self) { if self.sorted { @@ -117,7 +175,10 @@ impl AggregateBody { /// will be added to the public key used in the signature verification. pub fn verify_kernel_signatures(&self) -> Result<(), TransactionError> { for kernel in self.kernels.iter() { - kernel.verify_signature()?; + kernel.verify_signature().or_else(|e| { + warn!(target: LOG_TARGET, "Kernel ({}) signature failed {:?}.", kernel, e); + Err(e) + })?; } Ok(()) } @@ -141,14 +202,17 @@ impl AggregateBody { &self, offset: &BlindingFactor, reward: MicroTari, - prover: &RangeProofService, - factory: &CommitmentFactory, + factories: &CryptoFactories, ) -> Result<(), TransactionError> { - let total_offset = COMMITMENT_FACTORY.commit_value(&offset, reward.0); + let total_offset = factories.commitment.commit_value(&offset, reward.0); self.verify_kernel_signatures()?; - self.validate_kernel_sum(total_offset, factory)?; - self.validate_range_proofs(prover) + self.validate_kernel_sum(total_offset, &factories.commitment)?; + self.validate_range_proofs(&factories.range_proof) + } + + pub fn dissolve(self) -> (Vec, Vec, Vec) { + (self.inputs, self.outputs, self.kernels) } /// Calculate the sum of the inputs and outputs including fees @@ -168,7 +232,7 @@ impl AggregateBody { sum: offset, }, |acc, val| KernelSum { - fees: &acc.fees + &val.fee, + fees: acc.fees + val.fee, sum: &acc.sum + &val.excess, }, ) @@ -206,3 +270,25 @@ impl From for AggregateBody { transaction.body } } + +impl Display for AggregateBody { + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), Error> { + if !self.sorted { + fmt.write_str("WARNING: Block body is not sorted.\n")?; + } + fmt.write_str("--- Transaction Kernels ---\n")?; + for (i, kernel) in self.kernels.iter().enumerate() { + fmt.write_str(&format!("Kernel {}:\n", i))?; + fmt.write_str(&format!("{}\n", kernel))?; + } + fmt.write_str(&format!("--- Inputs ({}) ---\n", self.inputs.len()))?; + for input in self.inputs.iter() { + fmt.write_str(&format!("{}", input))?; + } + fmt.write_str(&format!("--- Outputs ({}) ---\n", self.outputs.len()))?; + for output in self.outputs.iter() { + fmt.write_str(&format!("{}", output))?; + } + Ok(()) + } +} diff --git a/base_layer/core/src/bullet_rangeproofs.rs b/base_layer/core/src/transactions/bullet_rangeproofs.rs similarity index 97% rename from base_layer/core/src/bullet_rangeproofs.rs rename to base_layer/core/src/transactions/bullet_rangeproofs.rs index f1ec3481e9..fa97d0759d 100644 --- a/base_layer/core/src/bullet_rangeproofs.rs +++ b/base_layer/core/src/transactions/bullet_rangeproofs.rs @@ -20,7 +20,7 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::types::*; +use crate::transactions::types::HashDigest; use digest::Digest; use serde::{ de::{self, Visitor}, @@ -30,7 +30,7 @@ use serde::{ Serializer, }; use std::fmt; -use tari_utilities::{byte_array::*, hash::*, hex::*}; +use tari_crypto::tari_utilities::{byte_array::*, hash::*, hex::*}; #[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct BulletRangeProof(pub Vec); diff --git a/base_layer/core/src/fee.rs b/base_layer/core/src/transactions/fee.rs similarity index 97% rename from base_layer/core/src/fee.rs rename to base_layer/core/src/transactions/fee.rs index df9962b4fe..2d642ecc61 100644 --- a/base_layer/core/src/fee.rs +++ b/base_layer/core/src/transactions/fee.rs @@ -20,7 +20,7 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{tari_amount::*, transaction::MINIMUM_TRANSACTION_FEE}; +use crate::transactions::{tari_amount::*, transaction::MINIMUM_TRANSACTION_FEE}; pub struct Fee {} diff --git a/base_layer/core/src/transactions/helpers.rs b/base_layer/core/src/transactions/helpers.rs new file mode 100644 index 0000000000..c0756f5083 --- /dev/null +++ b/base_layer/core/src/transactions/helpers.rs @@ -0,0 +1,374 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::transactions::{ + fee::Fee, + tari_amount::MicroTari, + transaction::{ + KernelBuilder, + KernelFeatures, + OutputFeatures, + Transaction, + TransactionInput, + TransactionKernel, + TransactionOutput, + UnblindedOutput, + }, + transaction_protocol::{ + build_challenge, + transaction_initializer::SenderTransactionInitializer, + TransactionMetadata, + }, + types::{Commitment, CommitmentFactory, CryptoFactories, PrivateKey, PublicKey, Signature}, + SenderTransactionProtocol, +}; +use rand::{rngs::OsRng, CryptoRng, Rng}; +use std::sync::Arc; +use tari_crypto::{ + commitment::HomomorphicCommitmentFactory, + common::Blake256, + keys::{PublicKey as PK, SecretKey}, + range_proof::RangeProofService, +}; + +pub fn make_input( + rng: &mut R, + val: MicroTari, + factory: &CommitmentFactory, +) -> (TransactionInput, UnblindedOutput) +{ + let key = PrivateKey::random(rng); + let v = PrivateKey::from(val); + let commitment = factory.commit(&key, &v); + let input = TransactionInput::new(OutputFeatures::default(), commitment); + (input, UnblindedOutput::new(val, key, None)) +} + +#[derive(Default)] +pub struct TestParams { + pub spend_key: PrivateKey, + pub change_key: PrivateKey, + pub offset: PrivateKey, + pub nonce: PrivateKey, + pub public_nonce: PublicKey, +} + +impl TestParams { + pub fn new() -> TestParams { + let r = PrivateKey::random(&mut OsRng); + TestParams { + spend_key: PrivateKey::random(&mut OsRng), + change_key: PrivateKey::random(&mut OsRng), + offset: PrivateKey::random(&mut OsRng), + public_nonce: PublicKey::from_secret_key(&r), + nonce: r, + } + } +} + +/// A convenience struct for a set of public-private keys and a public-private nonce +pub struct TestKeySet { + pub k: PrivateKey, + pub pk: PublicKey, + pub r: PrivateKey, + pub pr: PublicKey, +} + +/// Generate a new random key set. The key set includes +/// * a public-private keypair (k, pk) +/// * a public-private nonce keypair (r, pr) +pub fn generate_keys() -> TestKeySet { + let _rng = rand::thread_rng(); + let (k, pk) = PublicKey::random_keypair(&mut OsRng); + let (r, pr) = PublicKey::random_keypair(&mut OsRng); + TestKeySet { k, pk, r, pr } +} + +/// Generate a random transaction signature, returning the public key (excess) and the signature. +pub fn create_random_signature(fee: MicroTari, lock_height: u64) -> (PublicKey, Signature) { + let _rng = rand::thread_rng(); + let r = PrivateKey::random(&mut OsRng); + let (k, p) = PublicKey::random_keypair(&mut OsRng); + let tx_meta = TransactionMetadata { + fee, + lock_height, + meta_info: None, + linked_kernel: None, + }; + let e = build_challenge(&PublicKey::from_secret_key(&r), &tx_meta); + (p, Signature::sign(k, r, &e).unwrap()) +} + +/// Generate a random transaction signature given a key, returning the public key (excess) and the signature. +pub fn create_random_signature_from_s_key( + s_key: PrivateKey, + fee: MicroTari, + lock_height: u64, +) -> (PublicKey, Signature) +{ + let _rng = rand::thread_rng(); + let r = PrivateKey::random(&mut OsRng); + let p = PK::from_secret_key(&s_key); + let tx_meta = TransactionMetadata { + fee, + lock_height, + meta_info: None, + linked_kernel: None, + }; + let e = build_challenge(&PublicKey::from_secret_key(&r), &tx_meta); + (p, Signature::sign(s_key, r, &e).unwrap()) +} + +/// The tx macro is a convenience wrapper around the [create_tx] function, making the arguments optional and explicit +/// via keywords. +#[macro_export] +macro_rules! tx { + ($amount:expr, fee: $fee:expr, lock: $lock:expr, inputs: $n_in:expr, maturity: $mat:expr, outputs: $n_out:expr) => {{ + use $crate::transactions::helpers::create_tx; + create_tx($amount, $fee, $lock, $n_in, $mat, $n_out) + }}; + + ($amount:expr, fee: $fee:expr, lock: $lock:expr, inputs: $n_in:expr, outputs: $n_out:expr) => { + tx!($amount, fee: $fee, lock: $lock, inputs: $n_in, maturity: 0, outputs: $n_out) + }; + + ($amount:expr, fee: $fee:expr, inputs: $n_in:expr, outputs: $n_out:expr) => { + tx!($amount, fee: $fee, lock: 0, inputs: $n_in, maturity: 0, outputs: $n_out) + }; + + ($amount:expr, fee: $fee:expr) => { + tx!($amount, fee: $fee, lock: 0, inputs: 1, maturity: 0, outputs: 2) + } +} + +/// A utility macro to help make it easy to build transactions. +/// +/// The full syntax allows maximum flexibility, but most arguments are optional with sane defaults +/// ```ignore +/// txn_schema!(from: inputs, to: outputs, fee: 50*uT, lock: 1250, OutputFeatures::with_maturity(1320)); +/// txn_schema!(from: inputs, to: outputs, fee: 50*uT); // Uses default features and zero lock height +/// txn_schema!(from: inputs, to: outputs); // min fee of 25µT, zero lock height and default features +/// // as above, and transaction splits the first input in roughly half, returning remainder as change +/// txn_schema!(from: inputs); +/// ``` +/// The output of this macro is intended to be used in [spend_utxos]. +#[macro_export] +macro_rules! txn_schema { + (from: $input:expr, to: $outputs:expr, fee: $fee:expr, lock: $lock:expr, $features:expr) => {{ + $crate::transactions::helpers::TransactionSchema { + from: $input.clone(), + to: $outputs.clone(), + fee: $fee, + lock_height: $lock, + features: $features + } + }}; + + (from: $input:expr, to: $outputs:expr, fee: $fee:expr) => { + txn_schema!( + from: $input, + to:$outputs, + fee:$fee, + lock:0, + $crate::transactions::transaction::OutputFeatures::default() + ) + }; + + (from: $input:expr, to: $outputs:expr) => { + txn_schema!(from: $input, to:$outputs, fee: 25.into()) + }; + + // Spend inputs to ± half the first input value, with default fee and lock height + (from: $input:expr) => {{ + let out_val = $input[0].value / 2u64; + txn_schema!(from: $input, to: vec![out_val]) + }}; +} + +/// A convenience struct that holds plaintext versions of transactions +#[derive(Clone, Debug)] +pub struct TransactionSchema { + pub from: Vec, + pub to: Vec, + pub fee: MicroTari, + pub lock_height: u64, + pub features: OutputFeatures, +} + +/// Create a random transaction input for the given amount and maturity period. The input and its unblinded +/// parameters are returned. +pub fn create_test_input( + amount: MicroTari, + maturity: u64, + factory: &CommitmentFactory, +) -> (TransactionInput, UnblindedOutput) +{ + let spending_key = PrivateKey::random(&mut OsRng); + let commitment = factory.commit(&spending_key, &PrivateKey::from(amount)); + let features = OutputFeatures::with_maturity(maturity); + let input = TransactionInput::new(features.clone(), commitment); + let unblinded_output = UnblindedOutput::new(amount, spending_key, Some(features)); + (input, unblinded_output) +} + +/// Create an unconfirmed transaction for testing with a valid fee, unique access_sig, random inputs and outputs, the +/// transaction is only partially constructed +pub fn create_tx( + amount: MicroTari, + fee_per_gram: MicroTari, + lock_height: u64, + input_count: u64, + input_maturity: u64, + output_count: u64, +) -> (Transaction, Vec, Vec) +{ + let factories = CryptoFactories::default(); + let test_params = TestParams::new(); + let mut stx_builder: SenderTransactionInitializer = SenderTransactionProtocol::builder(0); + stx_builder + .with_lock_height(lock_height) + .with_fee_per_gram(fee_per_gram) + .with_offset(test_params.offset.clone()) + .with_private_nonce(test_params.nonce.clone()) + .with_change_secret(test_params.change_key.clone()); + + let mut unblinded_inputs = Vec::with_capacity(input_count as usize); + let mut unblinded_outputs = Vec::with_capacity(output_count as usize); + let amount_per_input = amount / input_count; + for _ in 0..input_count - 1 { + let (utxo, input) = create_test_input(amount_per_input, input_maturity, &factories.commitment); + unblinded_inputs.push(input.clone()); + stx_builder.with_input(utxo, input); + } + let amount_for_last_input = amount - amount_per_input * (input_count - 1); + let (utxo, input) = create_test_input(amount_for_last_input, input_maturity, &factories.commitment); + unblinded_inputs.push(input.clone()); + stx_builder.with_input(utxo, input); + + let estimated_fee = Fee::calculate(fee_per_gram, input_count as usize, output_count as usize); + let amount_per_output = (amount - estimated_fee) / output_count; + let amount_for_last_output = (amount - estimated_fee) - amount_per_output * (output_count - 1); + for i in 0..output_count { + let output_amount = if i < output_count - 1 { + amount_per_output + } else { + amount_for_last_output + }; + let utxo = UnblindedOutput::new(output_amount, test_params.spend_key.clone(), None); + unblinded_outputs.push(utxo.clone()); + stx_builder.with_output(utxo); + } + + let mut stx_protocol = stx_builder.build::(&factories).unwrap(); + match stx_protocol.finalize(KernelFeatures::empty(), &factories) { + Ok(true) => (), + Ok(false) => panic!("{:?}", stx_protocol.failure_reason()), + Err(e) => panic!("{:?}", e), + } + ( + stx_protocol.get_transaction().unwrap().clone(), + unblinded_inputs, + unblinded_outputs, + ) +} + +/// Spend the provided UTXOs by to the given amounts. Change will be created with any outstanding amount. +/// You only need to provide the unblinded outputs to spend. This function will calculate the commitment for you. +/// This is obviously less efficient, but is offered as a convenience. +/// The output features will be applied to every output +pub fn spend_utxos(schema: TransactionSchema) -> (Transaction, Vec, TestParams) { + let factories = CryptoFactories::default(); + let test_params = TestParams::new(); + let mut stx_builder = SenderTransactionProtocol::builder(0); + stx_builder + .with_lock_height(schema.lock_height) + .with_fee_per_gram(schema.fee) + .with_offset(test_params.offset.clone()) + .with_private_nonce(test_params.nonce.clone()) + .with_change_secret(test_params.change_key.clone()); + + for input in &schema.from { + let utxo = input.as_transaction_input(&factories.commitment, input.features.clone()); + stx_builder.with_input(utxo, input.clone()); + } + let mut outputs = Vec::with_capacity(schema.to.len()); + for val in schema.to { + let k = PrivateKey::random(&mut OsRng); + let utxo = UnblindedOutput::new(val, k, Some(schema.features.clone())); + outputs.push(utxo.clone()); + stx_builder.with_output(utxo); + } + + let mut stx_protocol = stx_builder.build::(&factories).unwrap(); + let change = stx_protocol.get_change_amount().unwrap(); + let change_output = UnblindedOutput { + value: change, + spending_key: test_params.change_key.clone(), + features: schema.features.clone(), + }; + outputs.push(change_output); + match stx_protocol.finalize(KernelFeatures::empty(), &factories) { + Ok(true) => (), + Ok(false) => panic!("{:?}", stx_protocol.failure_reason()), + Err(e) => panic!("{:?}", e), + } + let txn = stx_protocol.get_transaction().unwrap().clone(); + (txn, outputs, test_params) +} + +/// Create a transaction kernel with the given fee, using random keys to generate the signature +pub fn create_test_kernel(fee: MicroTari, lock_height: u64) -> TransactionKernel { + let (excess, s) = create_random_signature(fee, lock_height); + KernelBuilder::new() + .with_fee(fee) + .with_lock_height(lock_height) + .with_excess(&Commitment::from_public_key(&excess)) + .with_signature(&s) + .build() + .unwrap() +} + +/// Create a new UTXO for the specified value and return the output and spending key +pub fn create_utxo( + value: MicroTari, + factories: &CryptoFactories, + features: Option, +) -> (TransactionOutput, PrivateKey) +{ + let keys = generate_keys(); + let features = features.unwrap_or_default(); + let commitment = factories.commitment.commit_value(&keys.k, value.into()); + let proof = factories.range_proof.construct_proof(&keys.k, value.into()).unwrap(); + let utxo = TransactionOutput::new(features, commitment, proof.into()); + (utxo, keys.k) +} + +pub fn schema_to_transaction(txns: &[TransactionSchema]) -> (Vec>, Vec) { + let mut tx = Vec::new(); + let mut utxos = Vec::new(); + txns.iter().for_each(|schema| { + let (txn, mut output, _) = spend_utxos(schema.clone()); + tx.push(Arc::new(txn)); + utxos.append(&mut output); + }); + (tx, utxos) +} diff --git a/base_layer/core/src/transactions/mod.rs b/base_layer/core/src/transactions/mod.rs new file mode 100644 index 0000000000..75f9d8d6cc --- /dev/null +++ b/base_layer/core/src/transactions/mod.rs @@ -0,0 +1,15 @@ +pub mod aggregated_body; +pub mod bullet_rangeproofs; +pub mod fee; +pub mod proto; +pub mod tari_amount; +pub mod transaction; +#[allow(clippy::op_ref)] +pub mod transaction_protocol; +pub mod types; +// Re-export commonly used structs +pub use transaction_protocol::{recipient::ReceiverTransactionProtocol, sender::SenderTransactionProtocol}; +// Re-export the crypto crate to make exposing traits etc easier for clients of this crate +pub use tari_crypto as crypto; +#[macro_use] +pub mod helpers; diff --git a/infrastructure/tari_util/src/hash.rs b/base_layer/core/src/transactions/proto/mod.rs similarity index 90% rename from infrastructure/tari_util/src/hash.rs rename to base_layer/core/src/transactions/proto/mod.rs index cae3ea397e..9d55a40b38 100644 --- a/infrastructure/tari_util/src/hash.rs +++ b/base_layer/core/src/transactions/proto/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019 The Tari Project +// Copyright 2019, The Tari Project // // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the // following conditions are met: @@ -20,7 +20,11 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -/// This trait is used to describe how an object should be hashed -pub trait Hashable { - fn hash(&self) -> Vec; +pub mod types { + include!(concat!(env!("OUT_DIR"), "/", "tari.types.rs")); } + +mod transaction; +mod types_impls; + +pub mod utils; diff --git a/base_layer/core/src/transactions/proto/transaction.proto b/base_layer/core/src/transactions/proto/transaction.proto new file mode 100644 index 0000000000..c788e5bf49 --- /dev/null +++ b/base_layer/core/src/transactions/proto/transaction.proto @@ -0,0 +1,82 @@ +syntax = "proto3"; + +import "types.proto"; + +package tari.types; + +// The transaction kernel tracks the excess for a given transaction. For an explanation of what the excess is, and +// why it is necessary, refer to the +// [Mimblewimble TLU post](https://tlu.tarilabs.com/protocols/mimblewimble-1/sources/PITCHME.link.html?highlight=mimblewimble#mimblewimble). +// The kernel also tracks other transaction metadata, such as the lock height for the transaction (i.e. the earliest +// this transaction can be mined) and the transaction fee, in cleartext. +message TransactionKernel { + // Options for a kernel's structure or use + uint32 features = 1; + /// Fee originally included in the transaction this proof is for (in MicroTari) + uint64 fee = 2; + // This kernel is not valid earlier than lock_height blocks + // The max lock_height of all *inputs* to this transaction + uint64 lock_height = 3; + // This is an optional field used by committing to additional tx meta data between the two parties + HashOutput meta_info = 4; + // This is an optional field and is the hash of the kernel this kernel is linked to. + // This field is for example for relative time-locked transactions + HashOutput linked_kernel = 5; + // Remainder of the sum of all transaction commitments. If the transaction + // is well formed, amounts components should sum to zero and the excess + // is hence a valid public key. + Commitment excess = 6; + // The signature proving the excess is a valid public key, which signs + // the transaction fee. + Signature excess_sig = 7; +} + +// A transaction input. +// +// Primarily a reference to an output being spent by the transaction. +message TransactionInput { + // The features of the output being spent. We will check maturity for all outputs. + OutputFeatures features = 1; + // The commitment referencing the output being spent. + Commitment commitment = 2; +} + +// Output for a transaction, defining the new ownership of coins that are being transferred. The commitment is a +// blinded value for the output while the range proof guarantees the commitment includes a positive value without +// overflow and the ownership of the private key. +message TransactionOutput { + // Options for an output's structure or use + OutputFeatures features = 1; + // The homomorphic commitment representing the output amount + Commitment commitment = 2; + // A proof that the commitment is in the right range + bytes range_proof = 3; +} + +// Options for UTXO's +message OutputFeatures { + // Flags are the feature flags that differentiate between outputs, eg Coinbase all of which has different rules + uint32 flags = 1; + // The maturity of the specific UTXO. This is the min lock height at which an UTXO can be spend. Coinbase UTXO + // require a min maturity of the Coinbase_lock_height, this should be checked on receiving new blocks. + uint64 maturity = 2; +} + +// The components of the block or transaction. The same struct can be used for either, since in Mimblewimble, +// cut-through means that blocks and transactions have the same structure. The inputs, outputs and kernels should +// be sorted by their Blake2b-256bit digest hash +message AggregateBody { + // List of inputs spent by the transaction. + repeated TransactionInput inputs = 1; + // List of outputs the transaction produces. + repeated TransactionOutput outputs = 2; + // Kernels contain the excesses and their signatures for transaction + repeated TransactionKernel kernels = 3; +} + +// A transaction which consists of a kernel offset and an aggregate body made up of inputs, outputs and kernels. +// This struct is used to describe single transactions only. +message Transaction { + BlindingFactor offset = 1; + AggregateBody body = 2; +} diff --git a/base_layer/core/src/transactions/proto/transaction.rs b/base_layer/core/src/transactions/proto/transaction.rs new file mode 100644 index 0000000000..1d4a1e45c5 --- /dev/null +++ b/base_layer/core/src/transactions/proto/transaction.rs @@ -0,0 +1,233 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +//! Impls for transaction proto + +use super::types as proto; +use crate::transactions::{ + aggregated_body::AggregateBody, + bullet_rangeproofs::BulletRangeProof, + proto::utils::try_convert_all, + tari_amount::MicroTari, + transaction::{ + KernelFeatures, + OutputFeatures, + OutputFlags, + Transaction, + TransactionInput, + TransactionKernel, + TransactionOutput, + }, + types::{BlindingFactor, Commitment}, +}; +use std::convert::{TryFrom, TryInto}; +use tari_crypto::tari_utilities::{ByteArray, ByteArrayError}; + +//---------------------------------- TransactionKernel --------------------------------------------// + +impl TryFrom for TransactionKernel { + type Error = String; + + fn try_from(kernel: proto::TransactionKernel) -> Result { + let excess = Commitment::from_bytes( + &kernel + .excess + .ok_or_else(|| "Excess not provided in kernel".to_string())? + .data, + ) + .map_err(|err| err.to_string())?; + + let excess_sig = kernel + .excess_sig + .ok_or_else(|| "excess_sig not provided".to_string())? + .try_into() + .map_err(|err: ByteArrayError| err.to_string())?; + + Ok(Self { + features: KernelFeatures::from_bits(kernel.features as u8) + .ok_or_else(|| "Invalid or unrecognised kernel feature flag".to_string())?, + excess, + excess_sig, + fee: MicroTari::from(kernel.fee), + linked_kernel: kernel.linked_kernel.map(Into::into), + lock_height: kernel.lock_height, + meta_info: kernel.meta_info.map(Into::into), + }) + } +} + +impl From for proto::TransactionKernel { + fn from(kernel: TransactionKernel) -> Self { + Self { + features: kernel.features.bits() as u32, + excess: Some(kernel.excess.into()), + excess_sig: Some(kernel.excess_sig.into()), + fee: kernel.fee.into(), + linked_kernel: kernel.linked_kernel.map(Into::into), + lock_height: kernel.lock_height, + meta_info: kernel.meta_info.map(Into::into), + } + } +} + +//---------------------------------- TransactionInput --------------------------------------------// + +impl TryFrom for TransactionInput { + type Error = String; + + fn try_from(input: proto::TransactionInput) -> Result { + let features = input + .features + .map(TryInto::try_into) + .ok_or_else(|| "transaction output features not provided".to_string())??; + + let commitment = input + .commitment + .map(|commit| Commitment::from_bytes(&commit.data)) + .ok_or_else(|| "Transaction output commitment not provided".to_string())? + .map_err(|err| err.to_string())?; + + Ok(Self { features, commitment }) + } +} + +impl From for proto::TransactionInput { + fn from(output: TransactionInput) -> Self { + Self { + features: Some(output.features.into()), + commitment: Some(output.commitment.into()), + } + } +} + +//---------------------------------- TransactionOutput --------------------------------------------// + +impl TryFrom for TransactionOutput { + type Error = String; + + fn try_from(output: proto::TransactionOutput) -> Result { + let features = output + .features + .map(TryInto::try_into) + .ok_or_else(|| "transaction output features not provided".to_string())??; + + let commitment = output + .commitment + .map(|commit| Commitment::from_bytes(&commit.data)) + .ok_or_else(|| "Transaction output commitment not provided".to_string())? + .map_err(|err| err.to_string())?; + + Ok(Self { + features, + commitment, + proof: BulletRangeProof(output.range_proof), + }) + } +} + +impl From for proto::TransactionOutput { + fn from(output: TransactionOutput) -> Self { + Self { + features: Some(output.features.into()), + commitment: Some(output.commitment.into()), + range_proof: output.proof.to_vec(), + } + } +} + +//---------------------------------- OutputFeatures --------------------------------------------// + +impl TryFrom for OutputFeatures { + type Error = String; + + fn try_from(features: proto::OutputFeatures) -> Result { + Ok(Self { + flags: OutputFlags::from_bits(features.flags as u8) + .ok_or_else(|| "Invalid or unrecognised output flags".to_string())?, + maturity: features.maturity, + }) + } +} + +impl From for proto::OutputFeatures { + fn from(features: OutputFeatures) -> Self { + Self { + flags: features.flags.bits() as u32, + maturity: features.maturity, + } + } +} + +//---------------------------------- AggregateBody --------------------------------------------// + +impl TryFrom for AggregateBody { + type Error = String; + + fn try_from(body: proto::AggregateBody) -> Result { + let inputs = try_convert_all(body.inputs)?; + let outputs = try_convert_all(body.outputs)?; + let kernels = try_convert_all(body.kernels)?; + let mut body = AggregateBody::new(inputs, outputs, kernels); + body.sort(); + Ok(body) + } +} + +impl From for proto::AggregateBody { + fn from(body: AggregateBody) -> Self { + let (i, o, k) = body.dissolve(); + Self { + inputs: i.into_iter().map(Into::into).collect(), + outputs: o.into_iter().map(Into::into).collect(), + kernels: k.into_iter().map(Into::into).collect(), + } + } +} + +//----------------------------------- Transaction ---------------------------------------------// + +impl TryFrom for Transaction { + type Error = String; + + fn try_from(tx: proto::Transaction) -> Result { + let offset = tx + .offset + .map(|offset| BlindingFactor::from_bytes(&offset.data)) + .ok_or_else(|| "Blinding factor offset not provided".to_string())? + .map_err(|err| err.to_string())?; + let body = tx + .body + .map(TryInto::try_into) + .ok_or_else(|| "Body not provided".to_string())??; + + Ok(Self { offset, body }) + } +} + +impl From for proto::Transaction { + fn from(tx: Transaction) -> Self { + Self { + offset: Some(tx.offset.into()), + body: Some(tx.body.into()), + } + } +} diff --git a/base_layer/core/src/transactions/proto/types.proto b/base_layer/core/src/transactions/proto/types.proto new file mode 100644 index 0000000000..608aa518db --- /dev/null +++ b/base_layer/core/src/transactions/proto/types.proto @@ -0,0 +1,25 @@ +syntax = "proto3"; + +package tari.types; + +// Define the data type that is used to store results of `HashDigest` +message HashOutput { + bytes data = 1; +} + +// Commitment wrapper +message Commitment { + bytes data = 1; +} + +// Define the explicit Signature implementation for the Tari base layer. A different signature scheme can be +// employed by redefining this type. +message Signature { + bytes public_nonce = 1; + bytes signature = 2; +} + +// BlindingFactor wrapper +message BlindingFactor { + bytes data = 1; +} diff --git a/base_layer/core/src/transactions/proto/types_impls.rs b/base_layer/core/src/transactions/proto/types_impls.rs new file mode 100644 index 0000000000..888e1c4434 --- /dev/null +++ b/base_layer/core/src/transactions/proto/types_impls.rs @@ -0,0 +1,96 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::types as proto; +use crate::transactions::types::{BlindingFactor, Commitment, HashOutput, PrivateKey, PublicKey, Signature}; +use std::convert::TryFrom; +use tari_crypto::tari_utilities::{ByteArray, ByteArrayError}; + +//---------------------------------- Commitment --------------------------------------------// + +impl TryFrom for Commitment { + type Error = ByteArrayError; + + fn try_from(commitment: proto::Commitment) -> Result { + Commitment::from_bytes(&commitment.data) + } +} + +impl From for proto::Commitment { + fn from(commitment: Commitment) -> Self { + Self { + data: commitment.to_vec(), + } + } +} + +//---------------------------------- Signature --------------------------------------------// + +impl TryFrom for Signature { + type Error = ByteArrayError; + + fn try_from(sig: proto::Signature) -> Result { + let public_nonce = PublicKey::from_bytes(&sig.public_nonce)?; + let signature = PrivateKey::from_bytes(&sig.signature)?; + + Ok(Self::new(public_nonce, signature)) + } +} + +impl From for proto::Signature { + fn from(sig: Signature) -> Self { + Self { + public_nonce: sig.get_public_nonce().to_vec(), + signature: sig.get_signature().to_vec(), + } + } +} + +//---------------------------------- HashOutput --------------------------------------------// + +impl From for HashOutput { + fn from(output: proto::HashOutput) -> Self { + output.data + } +} + +impl From for proto::HashOutput { + fn from(output: HashOutput) -> Self { + Self { data: output } + } +} + +//--------------------------------- BlindingFactor -----------------------------------------// + +impl TryFrom for BlindingFactor { + type Error = ByteArrayError; + + fn try_from(offset: proto::BlindingFactor) -> Result { + Ok(BlindingFactor::from_bytes(&offset.data)?) + } +} + +impl From for proto::BlindingFactor { + fn from(offset: BlindingFactor) -> Self { + Self { data: offset.to_vec() } + } +} diff --git a/base_layer/core/src/transactions/proto/utils.rs b/base_layer/core/src/transactions/proto/utils.rs new file mode 100644 index 0000000000..dccd366b3e --- /dev/null +++ b/base_layer/core/src/transactions/proto/utils.rs @@ -0,0 +1,36 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::convert::TryInto; + +/// Tries to convert a series of `T`s to `U`s, returning an error at the first failure +#[inline] +pub fn try_convert_all(into_iter: I) -> Result, T::Error> +where + I: IntoIterator, + T: TryInto, +{ + into_iter + .into_iter() + .map(TryInto::try_into) + .collect::, _>>() +} diff --git a/base_layer/core/src/tari_amount.rs b/base_layer/core/src/transactions/tari_amount.rs similarity index 88% rename from base_layer/core/src/tari_amount.rs rename to base_layer/core/src/transactions/tari_amount.rs index 94359c9036..af8bd4b029 100644 --- a/base_layer/core/src/tari_amount.rs +++ b/base_layer/core/src/transactions/tari_amount.rs @@ -24,14 +24,17 @@ use newtype_ops::newtype_ops; use serde::{Deserialize, Serialize}; use std::fmt::{Display, Error, Formatter}; -use std::{iter::Sum, ops::Add}; +use std::{ + iter::Sum, + ops::{Add, Mul}, +}; use tari_crypto::ristretto::RistrettoSecretKey; /// All calculations using Tari amounts should use these newtypes to prevent bugs related to rounding errors, unit /// conversion errors etc. /// /// ```edition2018 -/// use tari_core::tari_amount::MicroTari; +/// use tari_core::transactions::tari_amount::MicroTari; /// /// let a = MicroTari::from(500); /// let b = MicroTari::from(50); @@ -40,6 +43,17 @@ use tari_crypto::ristretto::RistrettoSecretKey; #[derive(Copy, Default, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub struct MicroTari(pub u64); +/// A convenience constant that makes it easier to define Tari amounts. +/// ```edition2018 +/// use tari_core::transactions::tari_amount::{MicroTari, uT, T}; +/// assert_eq!(MicroTari::from(42), 42 * uT); +/// assert_eq!(1 * T, 1_000_000.into()); +/// assert_eq!(3_000_000 * uT, 3 * T); +/// ``` +#[allow(non_upper_case_globals)] +pub const uT: MicroTari = MicroTari(1); +pub const T: MicroTari = MicroTari(1_000_000); + // You can only add or subtract µT from µT newtype_ops! { [MicroTari] {add sub} {:=} Self Self } newtype_ops! { [MicroTari] {add sub} {:=} &Self &Self } @@ -48,6 +62,14 @@ newtype_ops! { [MicroTari] {add sub} {:=} Self &Self } // Multiplication and division only makes sense when µT is multiplied/divided by a scalar newtype_ops! { [MicroTari] {mul div rem} {:=} Self u64 } +impl Mul for u64 { + type Output = MicroTari; + + fn mul(self, rhs: MicroTari) -> Self::Output { + MicroTari(self * rhs.0) + } +} + impl MicroTari { pub fn checked_sub(self, v: MicroTari) -> Option { if self.0 >= v.0 { @@ -133,7 +155,7 @@ impl From for Tari { #[cfg(test)] mod test { - use crate::tari_amount::{MicroTari, Tari}; + use crate::transactions::tari_amount::{MicroTari, Tari}; #[test] fn micro_tari_arithmetic() { diff --git a/base_layer/core/src/transaction.rs b/base_layer/core/src/transactions/transaction.rs similarity index 59% rename from base_layer/core/src/transaction.rs rename to base_layer/core/src/transactions/transaction.rs index 0ebe20c985..9e34a2591c 100644 --- a/base_layer/core/src/transaction.rs +++ b/base_layer/core/src/transactions/transaction.rs @@ -23,27 +23,38 @@ // Portions of this file were originally copyrighted (c) 2018 The Grin Developers, issued under the Apache License, // Version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0. -use crate::{ - blocks::aggregated_body::AggregateBody, - tari_amount::MicroTari, - types::{BlindingFactor, Commitment, CommitmentFactory, Signature}, -}; - -use crate::{ - consensus::ConsensusRules, +use crate::transactions::{ + aggregated_body::AggregateBody, fee::Fee, + tari_amount::{uT, MicroTari}, transaction_protocol::{build_challenge, TransactionMetadata}, - types::{HashDigest, RangeProof, RangeProofService}, + types::{ + BlindingFactor, + Commitment, + CommitmentFactory, + CryptoFactories, + HashDigest, + HashOutput, + MessageHash, + RangeProof, + RangeProofService, + Signature, + }, }; use derive_error::Error; use digest::Input; use serde::{Deserialize, Serialize}; -use std::cmp::Ordering; +use std::{ + cmp::{max, min, Ordering}, + fmt::{Display, Formatter}, + hash::{Hash, Hasher}, + ops::Add, +}; use tari_crypto::{ commitment::HomomorphicCommitmentFactory, range_proof::{RangeProofError, RangeProofService as RangeProofServiceTrait}, + tari_utilities::{hex::Hex, message_format::MessageFormat, ByteArray, Hashable}, }; -use tari_utilities::{ByteArray, Hashable}; // These are set fairly arbitrarily at the moment. We'll need to do some modelling / testing to tune these values. pub const MAX_TRANSACTION_INPUTS: usize = 500; @@ -63,6 +74,12 @@ bitflags! { } } +impl KernelFeatures { + pub fn create_coinbase() -> KernelFeatures { + KernelFeatures::COINBASE_KERNEL + } +} + /// Options for UTXO's #[derive(Debug, Clone, Hash, PartialEq, Deserialize, Serialize, Eq)] pub struct OutputFeatures { @@ -80,10 +97,18 @@ impl OutputFeatures { buf } - pub fn create_coinbase(current_block_height: u64, consensus_rules: &ConsensusRules) -> OutputFeatures { + pub fn create_coinbase(maturity_height: u64) -> OutputFeatures { OutputFeatures { flags: OutputFlags::COINBASE_OUTPUT, - maturity: consensus_rules.coinbase_lock_height() + current_block_height, + maturity: maturity_height, + } + } + + /// Create an `OutputFeatures` with the given maturity and all other values at their default setting + pub fn with_maturity(maturity: u64) -> OutputFeatures { + OutputFeatures { + maturity, + ..OutputFeatures::default() } } } @@ -119,7 +144,7 @@ bitflags! { //---------------------------------------- TransactionError ----------------------------------------------------// -#[derive(Clone, Debug, PartialEq, Error)] +#[derive(Clone, Debug, PartialEq, Error, Deserialize, Serialize)] pub enum TransactionError { // Error validating the transaction #[error(msg_embedded, no_from, non_std)] @@ -136,7 +161,7 @@ pub enum TransactionError { /// An unblinded output is one where the value and spending key (blinding factor) are known. This can be used to /// build both inputs and outputs (every input comes from an output) -#[derive(Debug, Clone, Hash)] +#[derive(Debug, Clone)] pub struct UnblindedOutput { pub value: MicroTari, pub spending_key: BlindingFactor, @@ -149,7 +174,7 @@ impl UnblindedOutput { UnblindedOutput { value, spending_key, - features: features.unwrap_or_else(|| OutputFeatures::default()), + features: features.unwrap_or_default(), } } @@ -159,22 +184,20 @@ impl UnblindedOutput { TransactionInput { commitment, features } } - pub fn as_transaction_output( - &self, - prover: &RangeProofService, - factory: &CommitmentFactory, - features: OutputFeatures, - ) -> Result - { - let commitment = factory.commit(&self.spending_key, &self.value.into()); + pub fn as_transaction_output(&self, factories: &CryptoFactories) -> Result { + let commitment = factories.commitment.commit(&self.spending_key, &self.value.into()); let output = TransactionOutput { - features, + features: self.features.clone(), commitment, - proof: RangeProof::from_bytes(&prover.construct_proof(&self.spending_key, self.value.into())?) - .map_err(|_| TransactionError::RangeProofError(RangeProofError::ProofConstructionError))?, + proof: RangeProof::from_bytes( + &factories + .range_proof + .construct_proof(&self.spending_key, self.value.into())?, + ) + .map_err(|_| TransactionError::RangeProofError(RangeProofError::ProofConstructionError))?, }; // A range proof can be constructed for an invalid value so we should confirm that the proof can be verified. - if !output.verify_range_proof(&prover)? { + if !output.verify_range_proof(&factories.range_proof)? { return Err(TransactionError::ValidationError( "Range proof could not be verified".into(), )); @@ -192,6 +215,12 @@ impl PartialEq for UnblindedOutput { } } +impl Hash for UnblindedOutput { + fn hash(&self, state: &mut H) { + self.value.hash(state); + } +} + impl PartialOrd for UnblindedOutput { fn partial_cmp(&self, other: &Self) -> Option { self.value.partial_cmp(&other.value) @@ -233,6 +262,12 @@ impl TransactionInput { pub fn opened_by(&self, input: &UnblindedOutput, factory: &CommitmentFactory) -> bool { factory.open(&input.spending_key, &input.value.into(), &self.commitment) } + + /// This will check if the input and the output is the same commitment by looking at the commitment and features. + /// This will ignore the output rangeproof + pub fn is_equal_to(&self, output: &TransactionOutput) -> bool { + self.commitment == output.commitment && self.features == output.features + } } impl From for TransactionInput { @@ -255,6 +290,12 @@ impl Hashable for TransactionInput { } } +impl Display for TransactionInput { + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { + fmt.write_str(&format!("{} [{:?}]\n", self.commitment.to_hex(), self.features)) + } +} + //---------------------------------------- TransactionOutput ----------------------------------------------------// /// Output for a transaction, defining the new ownership of coins that are being transferred. The commitment is a @@ -295,6 +336,12 @@ impl TransactionOutput { pub fn verify_range_proof(&self, prover: &RangeProofService) -> Result { Ok(prover.verify(&self.proof.to_vec(), &self.commitment)) } + + /// This will check if the input and the output is the same commitment by looking at the commitment and features. + /// This will ignore the output rangeproof + pub fn is_equal_to(&self, output: &TransactionInput) -> bool { + self.commitment == output.commitment && self.features == output.features + } } /// Implement the canonical hashing function for TransactionOutput for use in ordering. @@ -324,6 +371,18 @@ impl Default for TransactionOutput { } } +impl Display for TransactionOutput { + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { + let proof = self.proof.to_hex(); + fmt.write_str(&format!( + "{} [{:?}] Proof: {}..{}\n", + self.commitment.to_hex(), + self.features, + proof[0..16].to_string(), + proof[proof.len() - 16..proof.len()].to_string() + )) + } +} //---------------------------------------- Transaction Kernel ----------------------------------------------------// /// The transaction kernel tracks the excess for a given transaction. For an explanation of what the excess is, and @@ -340,6 +399,11 @@ pub struct TransactionKernel { /// This kernel is not valid earlier than lock_height blocks /// The max lock_height of all *inputs* to this transaction pub lock_height: u64, + /// This is an optional field used by committing to additional tx meta data between the two parties + pub meta_info: Option, + /// This is an optional field and is the hash of the kernel this kernel is linked to. + /// This field is for example for relative time-locked transactions + pub linked_kernel: Option, /// Remainder of the sum of all transaction commitments. If the transaction /// is well formed, amounts components should sum to zero and the excess /// is hence a valid public key. @@ -354,6 +418,8 @@ pub struct KernelBuilder { features: KernelFeatures, fee: MicroTari, lock_height: u64, + meta_info: Option, + linked_kernel: Option, excess: Option, excess_sig: Option, } @@ -395,6 +461,16 @@ impl KernelBuilder { self } + pub fn with_linked_kernel(mut self, linked_kernel_hash: MessageHash) -> KernelBuilder { + self.linked_kernel = Some(linked_kernel_hash); + self + } + + pub fn with_meta_info(mut self, meta_info: MessageHash) -> KernelBuilder { + self.meta_info = Some(meta_info); + self + } + pub fn build(self) -> Result { if self.excess.is_none() || self.excess_sig.is_none() { return Err(TransactionError::NoSignatureError); @@ -403,6 +479,8 @@ impl KernelBuilder { features: self.features, fee: self.fee, lock_height: self.lock_height, + linked_kernel: self.linked_kernel, + meta_info: self.meta_info, excess: self.excess.unwrap(), excess_sig: self.excess_sig.unwrap(), }) @@ -415,6 +493,8 @@ impl Default for KernelBuilder { features: KernelFeatures::empty(), fee: MicroTari::from(0), lock_height: 0, + linked_kernel: None, + meta_info: None, excess: None, excess_sig: None, } @@ -428,6 +508,8 @@ impl TransactionKernel { let m = TransactionMetadata { lock_height: self.lock_height, fee: self.fee, + meta_info: None, + linked_kernel: None, }; let c = build_challenge(r, &m); if self.excess_sig.verify_challenge(excess, &c) { @@ -449,11 +531,45 @@ impl Hashable for TransactionKernel { .chain(self.excess.as_bytes()) .chain(self.excess_sig.get_public_nonce().as_bytes()) .chain(self.excess_sig.get_signature().as_bytes()) + .chain(self.meta_info.as_ref().unwrap_or(&vec![0])) + .chain(self.linked_kernel.as_ref().unwrap_or(&vec![0])) .result() .to_vec() } } +impl Display for TransactionKernel { + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { + let msg = format!( + "Fee: {}\nLock height: {}\nFeatures: {:?}\nExcess: {}\nExcess signature: {}\nMeta_info: \ + {}\nLinked_kernel: {}\n", + self.fee, + self.lock_height, + self.features, + self.excess.to_hex(), + self.excess_sig + .to_json() + .unwrap_or_else(|_| "Failed to serialize signature".into()), + match &self.meta_info { + None => "None".to_string(), + Some(v) => v.to_hex(), + }, + match &self.linked_kernel { + None => "None".to_string(), + Some(v) => v.to_hex(), + }, + ); + fmt.write_str(&msg) + } +} + +/// This struct holds the result of calculating the sum of the kernels in a Transaction +/// and returns the summed commitments and the total fees +pub struct KernelSum { + pub sum: Commitment, + pub fees: MicroTari, +} + //---------------------------------------- Transaction ----------------------------------------------------// /// A transaction which consists of a kernel offset and an aggregate body made up of inputs, outputs and kernels. @@ -479,10 +595,9 @@ impl Transaction { offset: BlindingFactor, ) -> Transaction { - Transaction { - offset, - body: AggregateBody::new(inputs, outputs, kernels), - } + let mut body = AggregateBody::new(inputs, outputs, kernels); + body.sort(); + Transaction { offset, body } } /// Validate this transaction by checking the following: @@ -491,14 +606,15 @@ impl Transaction { /// 1. Range proofs of the outputs are valid /// /// This function does NOT check that inputs come from the UTXO set + #[allow(clippy::erasing_op)] // This is for 0 * uT pub fn validate_internal_consistency( - &mut self, - prover: &RangeProofService, - factory: &CommitmentFactory, + &self, + factories: &CryptoFactories, + reward: Option, ) -> Result<(), TransactionError> { - self.body - .validate_internal_consistency(&self.offset, MicroTari::from(0), prover, factory) + let reward = reward.unwrap_or_else(|| 0 * uT); + self.body.validate_internal_consistency(&self.offset, reward, factories) } pub fn get_body(&self) -> &AggregateBody { @@ -507,19 +623,71 @@ impl Transaction { /// Returns the byte size or weight of a transaction pub fn calculate_weight(&self) -> u64 { - Fee::calculate_weight(self.body.inputs.len(), self.body.outputs.len()) + Fee::calculate_weight(self.body.inputs().len(), self.body.outputs().len()) } /// Returns the total fee allocated to each byte of the transaction pub fn calculate_ave_fee_per_gram(&self) -> f64 { (self.body.get_total_fee().0 as f64) / self.calculate_weight() as f64 } + + /// Returns the minimum maturity of the input UTXOs + pub fn min_input_maturity(&self) -> u64 { + self.body.inputs().iter().fold(std::u64::MAX, |min_maturity, input| { + min(min_maturity, input.features.maturity) + }) + } + + /// Returns the maximum maturity of the input UTXOs + pub fn max_input_maturity(&self) -> u64 { + self.body + .inputs() + .iter() + .fold(0, |max_maturity, input| max(max_maturity, input.features.maturity)) + } + + /// Returns the maximum timelock of the kernels inside of the transaction + pub fn max_kernel_timelock(&self) -> u64 { + self.body + .kernels() + .iter() + .fold(0, |max_timelock, kernel| max(max_timelock, kernel.lock_height)) + } + + /// Returns the height of the minimum height where the transaction is spendable. This is calculated from the + /// transaction kernel lock_heights and the maturity of the input UTXOs. + pub fn min_spendable_height(&self) -> u64 { + max(self.max_kernel_timelock(), self.max_input_maturity()) + } + + /// This function adds two transactions together. It does not do cut-through. Calling Tx1 + Tx2 will result in + /// vut-through being applied. + pub fn add_no_cut_through(mut self, other: Self) -> Self { + self.offset = self.offset + other.offset; + let (mut inputs, mut outputs, mut kernels) = other.body.dissolve(); + self.body.add_inputs(&mut inputs); + self.body.add_outputs(&mut outputs); + self.body.add_kernels(&mut kernels); + self + } +} + +impl Add for Transaction { + type Output = Self; + + // Note this will also do cut-through + fn add(mut self, other: Self) -> Self { + self = self.add_no_cut_through(other); + self.body.do_cut_through(); + self + } } //---------------------------------------- Transaction Builder ----------------------------------------------------// pub struct TransactionBuilder { body: AggregateBody, offset: Option, + reward: Option, } impl TransactionBuilder { @@ -564,20 +732,22 @@ impl TransactionBuilder { self } - pub fn build( - self, - prover: &RangeProofService, - factory: &CommitmentFactory, - ) -> Result - { + pub fn with_reward(&mut self, reward: MicroTari) -> &mut Self { + self.reward = Some(reward); + self + } + + /// Build the transaction. + pub fn build(self, factories: &CryptoFactories) -> Result { if let Some(offset) = self.offset { - let mut tx = Transaction::new(self.body.inputs, self.body.outputs, self.body.kernels, offset); - tx.validate_internal_consistency(prover, factory)?; + let (i, o, k) = self.body.dissolve(); + let tx = Transaction::new(i, o, k, offset); + tx.validate_internal_consistency(factories, self.reward)?; Ok(tx) } else { - return Err(TransactionError::ValidationError( + Err(TransactionError::ValidationError( "Transaction validation failed".into(), - )); + )) } } } @@ -587,6 +757,7 @@ impl Default for TransactionBuilder { Self { offset: None, body: AggregateBody::empty(), + reward: None, } } } @@ -597,19 +768,20 @@ impl Default for TransactionBuilder { mod test { use super::*; use crate::{ - transaction::OutputFeatures, - types::{BlindingFactor, PrivateKey, RangeProof}, - }; - use rand; - use tari_crypto::{ - keys::SecretKey as SecretKeyTrait, - ristretto::{dalek_range_proof::DalekRangeProofService, pedersen::PedersenCommitmentFactory}, + transactions::{ + helpers::{create_test_kernel, create_tx, spend_utxos}, + tari_amount::T, + transaction::OutputFeatures, + types::{BlindingFactor, PrivateKey, PublicKey, RangeProof}, + }, + txn_schema, }; + use rand::{self, rngs::OsRng}; + use tari_crypto::{keys::SecretKey as SecretKeyTrait, ristretto::pedersen::PedersenCommitmentFactory}; #[test] fn unblinded_input() { - let mut rng = rand::OsRng::new().unwrap(); - let k = BlindingFactor::random(&mut rng); + let k = BlindingFactor::random(&mut OsRng); let factory = PedersenCommitmentFactory::default(); let i = UnblindedOutput::new(10.into(), k, None); let input = i.as_transaction_input(&factory, OutputFeatures::default()); @@ -617,24 +789,27 @@ mod test { assert!(input.opened_by(&i, &factory)); } + #[test] + fn with_maturity() { + let features = OutputFeatures::with_maturity(42); + assert_eq!(features.maturity, 42); + assert_eq!(features.flags, OutputFlags::empty()); + } + #[test] fn range_proof_verification() { - let mut rng = rand::OsRng::new().unwrap(); - let factory = PedersenCommitmentFactory::default(); - let prover = DalekRangeProofService::new(32, &factory).unwrap(); + let factories = CryptoFactories::new(32); // Directly test the tx_output verification - let k1 = BlindingFactor::random(&mut rng); - let k2 = BlindingFactor::random(&mut rng); + let k1 = BlindingFactor::random(&mut OsRng); + let k2 = BlindingFactor::random(&mut OsRng); // For testing the max range has been limited to 2^32 so this value is too large. let unblinded_output1 = UnblindedOutput::new((2u64.pow(32) - 1u64).into(), k1, None); - let tx_output1 = unblinded_output1 - .as_transaction_output(&prover, &factory, OutputFeatures::default()) - .unwrap(); - assert!(tx_output1.verify_range_proof(&prover).unwrap()); + let tx_output1 = unblinded_output1.as_transaction_output(&factories).unwrap(); + assert!(tx_output1.verify_range_proof(&factories.range_proof).unwrap()); let unblinded_output2 = UnblindedOutput::new((2u64.pow(32) + 1u64).into(), k2.clone(), None); - let tx_output2 = unblinded_output2.as_transaction_output(&prover, &factory, OutputFeatures::default()); + let tx_output2 = unblinded_output2.as_transaction_output(&factories); match tx_output2 { Ok(_) => panic!("Range proof should have failed to verify"), @@ -644,9 +819,143 @@ mod test { ), } let v = PrivateKey::from(2u64.pow(32) + 1); - let c = factory.commit(&k2, &v); - let proof = prover.construct_proof(&k2, 2u64.pow(32) + 1).unwrap(); + let c = factories.commitment.commit(&k2, &v); + let proof = factories.range_proof.construct_proof(&k2, 2u64.pow(32) + 1).unwrap(); let tx_output3 = TransactionOutput::new(OutputFeatures::default(), c, RangeProof::from_bytes(&proof).unwrap()); - assert_eq!(tx_output3.verify_range_proof(&prover).unwrap(), false); + assert_eq!(tx_output3.verify_range_proof(&factories.range_proof).unwrap(), false); + } + + #[test] + fn kernel_hash() { + let s = PrivateKey::from_hex("6c6eebc5a9c02e1f3c16a69ba4331f9f63d0718401dea10adc4f9d3b879a2c09").unwrap(); + let r = PublicKey::from_hex("28e8efe4e5576aac931d358d0f6ace43c55fa9d4186d1d259d1436caa876d43b").unwrap(); + let sig = Signature::new(r, s); + let excess = Commitment::from_hex("9017be5092b85856ce71061cadeb20c2d1fabdf664c4b3f082bf44cf5065e650").unwrap(); + let k = KernelBuilder::new() + .with_signature(&sig) + .with_fee(100.into()) + .with_excess(&excess) + .with_lock_height(500) + .build() + .unwrap(); + assert_eq!( + &k.hash().to_hex(), + "4471024385680c8bfa36979c588a0e06d3c3af3dd9ecff57540e01c18445f4e7" + ); + } + + #[test] + fn kernel_metadata() { + let s = PrivateKey::from_hex("df9a004360b1cf6488d8ff7fb625bc5877f4b013f9b2b20d84932172e605b207").unwrap(); + let r = PublicKey::from_hex("5c6bfaceaa1c83fa4482a816b5f82ca3975cb9b61b6e8be4ee8f01c5f1bee561").unwrap(); + let sig = Signature::new(r, s); + let excess = Commitment::from_hex("e0bd3f743b566272277c357075b0584fc840d79efac49e9b3b6dbaa8a351bc0c").unwrap(); + let linked_kernel = Vec::from_hex("e605e109a5723053181e22e9a14cb9a9981dc8a2368d5aa3d09d9261e340e928").unwrap(); + let meta = Vec::from_hex("c45d3f7903471c55e0fe77f644c1ed9b87151b50c0394f806187138eb36a4200").unwrap(); + let k = KernelBuilder::new() + .with_signature(&sig) + .with_fee(100.into()) + .with_excess(&excess) + .with_linked_kernel(linked_kernel) + .with_meta_info(meta) + .with_lock_height(500) + .build() + .unwrap(); + assert_eq!( + &k.hash().to_hex(), + "988ed705e6509684eb78ba81cd49525692002ab4dc79025bfd3fc051e45eb0b2" + ) + } + + #[test] + fn check_timelocks() { + let factories = CryptoFactories::new(32); + let k = BlindingFactor::random(&mut OsRng); + let v = PrivateKey::from(2u64.pow(32) + 1); + let c = factories.commitment.commit(&k, &v); + + let mut input = TransactionInput::new(OutputFeatures::default(), c); + let mut kernel = create_test_kernel(0.into(), 0); + let mut tx = Transaction::new(Vec::new(), Vec::new(), Vec::new(), 0.into()); + + // lets add timelocks + input.features.maturity = 5; + kernel.lock_height = 2; + tx.body.add_input(input.clone()); + tx.body.add_kernel(kernel.clone()); + + assert_eq!(tx.max_input_maturity(), 5); + assert_eq!(tx.max_kernel_timelock(), 2); + assert_eq!(tx.min_spendable_height(), 5); + + input.features.maturity = 4; + kernel.lock_height = 3; + tx.body.add_input(input.clone()); + tx.body.add_kernel(kernel.clone()); + + assert_eq!(tx.max_input_maturity(), 5); + assert_eq!(tx.max_kernel_timelock(), 3); + assert_eq!(tx.min_spendable_height(), 5); + + input.features.maturity = 2; + kernel.lock_height = 10; + tx.body.add_input(input.clone()); + tx.body.add_kernel(kernel.clone()); + + assert_eq!(tx.max_input_maturity(), 5); + assert_eq!(tx.max_kernel_timelock(), 10); + assert_eq!(tx.min_spendable_height(), 10); + } + + #[test] + fn test_validate_internal_consistency() { + let (tx, _, _) = create_tx(5000.into(), 15.into(), 1, 2, 1, 4); + + let factories = CryptoFactories::default(); + assert!(tx.validate_internal_consistency(&factories, None).is_ok()); + } + + #[test] + fn check_cut_through_() { + let (tx, _, outputs) = create_tx(50000000.into(), 15.into(), 1, 2, 1, 2); + + assert_eq!(tx.body.inputs().len(), 2); + assert_eq!(tx.body.outputs().len(), 2); + assert_eq!(tx.body.kernels().len(), 1); + + let factories = CryptoFactories::default(); + assert!(tx.validate_internal_consistency(&factories, None).is_ok()); + + let schema = txn_schema!(from: vec![outputs[1].clone()], to: vec![1 * T, 2 * T]); + let (tx2, _outputs, _) = spend_utxos(schema); + + assert_eq!(tx2.body.inputs().len(), 1); + assert_eq!(tx2.body.outputs().len(), 3); + assert_eq!(tx2.body.kernels().len(), 1); + + let mut tx3 = tx.clone().add_no_cut_through(tx2.clone()); + let tx = tx + tx2; + // check that all inputs are as we expect them to be + assert_eq!(tx3.body.inputs().len(), 3); + assert_eq!(tx3.body.outputs().len(), 5); + assert_eq!(tx3.body.kernels().len(), 2); + // check that cut-though has not been applied + assert!(!tx3.body.cut_through_check()); + + // apply cut-through + tx3.body.do_cut_through(); + + // check that cut-through has been applied. + assert!(tx.body.cut_through_check()); + assert!(tx.validate_internal_consistency(&factories, None).is_ok()); + assert_eq!(tx.body.inputs().len(), 2); + assert_eq!(tx.body.outputs().len(), 4); + assert_eq!(tx.body.kernels().len(), 2); + + assert!(tx3.body.cut_through_check()); + assert!(tx3.validate_internal_consistency(&factories, None).is_ok()); + assert_eq!(tx3.body.inputs().len(), 2); + assert_eq!(tx3.body.outputs().len(), 4); + assert_eq!(tx3.body.kernels().len(), 2); } } diff --git a/base_layer/core/src/transaction_protocol/mod.rs b/base_layer/core/src/transactions/transaction_protocol/mod.rs similarity index 73% rename from base_layer/core/src/transaction_protocol/mod.rs rename to base_layer/core/src/transactions/transaction_protocol/mod.rs index 4d96f6d61f..af31eab3b2 100644 --- a/base_layer/core/src/transaction_protocol/mod.rs +++ b/base_layer/core/src/transactions/transaction_protocol/mod.rs @@ -49,49 +49,52 @@ //! end //! -#[cfg(test)] -pub mod test_common; - +pub mod proto; pub mod recipient; pub mod sender; pub mod single_receiver; pub mod transaction_initializer; -use crate::{ +use crate::transactions::{ tari_amount::*, transaction::TransactionError, - types::{Challenge, MessageHash, PublicKey}, + types::{Challenge, HashOutput, MessageHash, PublicKey}, }; use derive_error::Error; use digest::Digest; use serde::{Deserialize, Serialize}; -use tari_crypto::{range_proof::RangeProofError, signatures::SchnorrSignatureError}; -use tari_utilities::byte_array::ByteArray; +use tari_crypto::{ + range_proof::RangeProofError, + signatures::SchnorrSignatureError, + tari_utilities::byte_array::ByteArray, +}; -#[derive(Clone, Debug, PartialEq, Error)] +#[derive(Clone, Debug, PartialEq, Error, Deserialize, Serialize)] pub enum TransactionProtocolError { // The current state is not yet completed, cannot transition to next state #[error(msg_embedded, no_from, non_std)] IncompleteStateError(String), #[error(msg_embedded, no_from, non_std)] ValidationError(String), - // Invalid state transition + /// Invalid state transition InvalidTransitionError, - // Invalid state + /// Invalid state InvalidStateError, - // An error occurred while performing a signature + /// An error occurred while performing a signature SigningError(SchnorrSignatureError), - // A signature verification failed + /// A signature verification failed InvalidSignatureError, - // An error occurred while building the final transaction + /// An error occurred while building the final transaction TransactionBuildError(TransactionError), - // The transaction construction broke down due to communication failure + /// The transaction construction broke down due to communication failure TimeoutError, - // An error was produced while constructing a rangeproof + /// An error was produced while constructing a rangeproof RangeProofError(RangeProofError), - // This set of parameters is currently not supported + /// This set of parameters is currently not supported #[error(msg_embedded, no_from, non_std)] UnsupportedError(String), + /// There has been an error serializing or deserializing this structure + SerializationError, } /// Transaction metadata, including the fee and lock height @@ -101,6 +104,11 @@ pub struct TransactionMetadata { pub fee: MicroTari, /// The earliest block this transaction can be mined pub lock_height: u64, + /// This is an optional field used by committing to additional tx meta data between the two parties + pub meta_info: Option, + /// This is an optional field and is the hash of the kernel this kernel is linked to. + /// This field is for example for relative time-locked transactions + pub linked_kernel: Option, } /// Convenience function that calculates the challenge for the Schnorr signatures @@ -109,6 +117,8 @@ pub fn build_challenge(sum_public_nonces: &PublicKey, metadata: &TransactionMeta .chain(sum_public_nonces.as_bytes()) .chain(&u64::from(metadata.fee).to_le_bytes()) .chain(&metadata.lock_height.to_le_bytes()) + .chain(metadata.meta_info.as_ref().unwrap_or(&vec![0])) + .chain(metadata.linked_kernel.as_ref().unwrap_or(&vec![0])) .result() .to_vec() } diff --git a/base_layer/core/src/transactions/transaction_protocol/proto/mod.rs b/base_layer/core/src/transactions/transaction_protocol/proto/mod.rs new file mode 100644 index 0000000000..628741dc50 --- /dev/null +++ b/base_layer/core/src/transactions/transaction_protocol/proto/mod.rs @@ -0,0 +1,34 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::transactions::proto::types; + +pub mod protocol { + include!(concat!(env!("OUT_DIR"), "/", "tari.transaction_protocol.rs")); +} + +pub mod recipient_signed_message; +pub mod transaction_metadata; +pub mod transaction_sender; + +// Re-export message types +pub use protocol::*; diff --git a/base_layer/core/src/transactions/transaction_protocol/proto/recipient_signed_message.proto b/base_layer/core/src/transactions/transaction_protocol/proto/recipient_signed_message.proto new file mode 100644 index 0000000000..8923847745 --- /dev/null +++ b/base_layer/core/src/transactions/transaction_protocol/proto/recipient_signed_message.proto @@ -0,0 +1,14 @@ +syntax = "proto3"; + +import "types.proto"; +import "transaction.proto"; + +package tari.transaction_protocol; + +// This is the message containing the public data that the Receiver will send back to the Sender +message RecipientSignedMessage { + uint64 tx_id = 1; + tari.types.TransactionOutput output = 2; + bytes public_spend_key = 3; + tari.types.Signature partial_signature = 4; +} diff --git a/base_layer/core/src/transactions/transaction_protocol/proto/recipient_signed_message.rs b/base_layer/core/src/transactions/transaction_protocol/proto/recipient_signed_message.rs new file mode 100644 index 0000000000..699cf3145a --- /dev/null +++ b/base_layer/core/src/transactions/transaction_protocol/proto/recipient_signed_message.rs @@ -0,0 +1,64 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::protocol as proto; + +use crate::transactions::{transaction_protocol::recipient::RecipientSignedMessage, types::PublicKey}; +use std::convert::{TryFrom, TryInto}; +use tari_crypto::tari_utilities::ByteArray; + +impl TryFrom for RecipientSignedMessage { + type Error = String; + + fn try_from(message: proto::RecipientSignedMessage) -> Result { + let output = message + .output + .map(TryInto::try_into) + .ok_or_else(|| "Transaction output not provided".to_string())??; + + let public_spend_key = PublicKey::from_bytes(&message.public_spend_key).map_err(|err| format!("{}", err))?; + + let partial_signature = message + .partial_signature + .map(TryInto::try_into) + .ok_or_else(|| "Transaction partial signature not provided".to_string())? + .map_err(|err| format!("{}", err))?; + + Ok(Self { + tx_id: message.tx_id, + output, + public_spend_key, + partial_signature, + }) + } +} + +impl From for proto::RecipientSignedMessage { + fn from(message: RecipientSignedMessage) -> Self { + Self { + tx_id: message.tx_id, + output: Some(message.output.into()), + public_spend_key: message.public_spend_key.to_vec(), + partial_signature: Some(message.partial_signature.into()), + } + } +} diff --git a/base_layer/core/src/transactions/transaction_protocol/proto/transaction_finalized.proto b/base_layer/core/src/transactions/transaction_protocol/proto/transaction_finalized.proto new file mode 100644 index 0000000000..70673afd5b --- /dev/null +++ b/base_layer/core/src/transactions/transaction_protocol/proto/transaction_finalized.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; + +import "transaction.proto"; + +package tari.transaction_protocol; + +message TransactionFinalizedMessage { + // The transaction id for the recipient + uint64 tx_id = 1; + // The actual transaction; + tari.types.Transaction transaction = 2; +} + diff --git a/base_layer/core/src/transactions/transaction_protocol/proto/transaction_metadata.proto b/base_layer/core/src/transactions/transaction_protocol/proto/transaction_metadata.proto new file mode 100644 index 0000000000..93f1c4a448 --- /dev/null +++ b/base_layer/core/src/transactions/transaction_protocol/proto/transaction_metadata.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; + +import "types.proto"; + +package tari.transaction_protocol; + +message TransactionMetadata { + // The absolute fee for the transaction + uint64 fee = 1; + // The earliest block this transaction can be mined + uint64 lock_height = 2; + // This is an optional field used by committing to additional tx meta data between the two parties + tari.types.HashOutput meta_info = 3; + // This is an optional field and is the hash of the kernel this kernel is linked to. + // This field is for example for relative time-locked transactions + tari.types.HashOutput linked_kernel = 4; +} \ No newline at end of file diff --git a/base_layer/core/src/transactions/transaction_protocol/proto/transaction_metadata.rs b/base_layer/core/src/transactions/transaction_protocol/proto/transaction_metadata.rs new file mode 100644 index 0000000000..72512ad94e --- /dev/null +++ b/base_layer/core/src/transactions/transaction_protocol/proto/transaction_metadata.rs @@ -0,0 +1,52 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::protocol as proto; + +use crate::transactions::transaction_protocol::TransactionMetadata; + +impl From for TransactionMetadata { + fn from(metadata: proto::TransactionMetadata) -> Self { + Self { + fee: metadata.fee.into(), + lock_height: metadata.lock_height, + meta_info: metadata.meta_info.map(Into::into), + linked_kernel: metadata.linked_kernel.map(Into::into), + } + } +} + +impl From for proto::TransactionMetadata { + fn from(metadata: TransactionMetadata) -> Self { + Self { + // The absolute fee for the transaction + fee: metadata.fee.into(), + // The earliest block this transaction can be mined + lock_height: metadata.lock_height, + // This is an optional field used by committing to additional tx meta data between the two parties + meta_info: metadata.meta_info.map(Into::into), + // This is an optional field and is the hash of the kernel this kernel is linked to. + // This field is for example for relative time-locked transactions + linked_kernel: metadata.linked_kernel.map(Into::into), + } + } +} diff --git a/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.proto b/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.proto new file mode 100644 index 0000000000..d780c89988 --- /dev/null +++ b/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.proto @@ -0,0 +1,31 @@ +syntax = "proto3"; + +import "transaction_metadata.proto"; + +package tari.transaction_protocol; + +message SingleRoundSenderData { + // The transaction id for the recipient + uint64 tx_id = 1; + // The amount, in µT, being sent to the recipient + uint64 amount = 2; + // The offset public excess for this transaction + bytes public_excess = 3; + // The sender's public nonce + bytes public_nonce = 4; + // The transaction metadata + TransactionMetadata metadata = 5; + // Plain text message to receiver + string message = 6; +} + +message TransactionSenderMessage { + oneof message { + bool None = 1; + SingleRoundSenderData single = 2; + + // TODO: Three round types + + bool Multiple = 3; + } +} diff --git a/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.rs b/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.rs new file mode 100644 index 0000000000..2663ec2bac --- /dev/null +++ b/base_layer/core/src/transactions/transaction_protocol/proto/transaction_sender.rs @@ -0,0 +1,124 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::protocol as proto; +use crate::transactions::transaction_protocol::sender::{SingleRoundSenderData, TransactionSenderMessage}; + +use super::protocol::transaction_sender_message::Message as ProtoTransactionSenderMessage; +use std::convert::{TryFrom, TryInto}; +use tari_crypto::tari_utilities::ByteArray; + +// The generated _oneof_ enum +use crate::transactions::types::PublicKey; +use proto::transaction_sender_message::Message as ProtoTxnSenderMessage; + +impl proto::TransactionSenderMessage { + pub fn none() -> Self { + proto::TransactionSenderMessage { + message: Some(ProtoTxnSenderMessage::None(true)), + } + } + + pub fn single(data: proto::SingleRoundSenderData) -> Self { + proto::TransactionSenderMessage { + message: Some(ProtoTxnSenderMessage::Single(data)), + } + } + + pub fn multiple() -> Self { + proto::TransactionSenderMessage { + message: Some(ProtoTxnSenderMessage::Multiple(true)), + } + } +} + +impl TryFrom for TransactionSenderMessage { + type Error = String; + + fn try_from(message: proto::TransactionSenderMessage) -> Result { + let inner_message = message + .message + .ok_or_else(|| "TransactionSenderMessage.message not provided".to_string())?; + + let sender_message = match inner_message { + ProtoTxnSenderMessage::None(_) => TransactionSenderMessage::None, + ProtoTxnSenderMessage::Single(data) => TransactionSenderMessage::Single(Box::new(data.try_into()?)), + ProtoTxnSenderMessage::Multiple(_) => TransactionSenderMessage::Multiple, + }; + + Ok(sender_message) + } +} + +impl From for proto::TransactionSenderMessage { + fn from(message: TransactionSenderMessage) -> Self { + let message = match message { + TransactionSenderMessage::None => ProtoTransactionSenderMessage::None(true), + TransactionSenderMessage::Single(sender_data) => { + ProtoTransactionSenderMessage::Single((*sender_data).into()) + }, + TransactionSenderMessage::Multiple => ProtoTransactionSenderMessage::Multiple(true), + }; + + Self { message: Some(message) } + } +} + +//---------------------------------- SingleRoundSenderData --------------------------------------------// + +impl TryFrom for SingleRoundSenderData { + type Error = String; + + fn try_from(data: proto::SingleRoundSenderData) -> Result { + let public_excess = PublicKey::from_bytes(&data.public_excess).map_err(|err| err.to_string())?; + let public_nonce = PublicKey::from_bytes(&data.public_nonce).map_err(|err| err.to_string())?; + let metadata = data + .metadata + .map(Into::into) + .ok_or_else(|| "Transaction metadata not provided".to_string())?; + let message = data.message; + + Ok(Self { + tx_id: data.tx_id, + amount: data.amount.into(), + public_excess, + public_nonce, + metadata, + message, + }) + } +} + +impl From for proto::SingleRoundSenderData { + fn from(sender_data: SingleRoundSenderData) -> Self { + Self { + tx_id: sender_data.tx_id, + // The amount, in µT, being sent to the recipient + amount: sender_data.amount.into(), + // The offset public excess for this transaction + public_excess: sender_data.public_excess.to_vec(), + public_nonce: sender_data.public_nonce.to_vec(), + metadata: Some(sender_data.metadata.into()), + message: sender_data.message, + } + } +} diff --git a/base_layer/core/src/transaction_protocol/recipient.rs b/base_layer/core/src/transactions/transaction_protocol/recipient.rs similarity index 84% rename from base_layer/core/src/transaction_protocol/recipient.rs rename to base_layer/core/src/transactions/transaction_protocol/recipient.rs index 9ae1031bdd..4efd6fe0d0 100644 --- a/base_layer/core/src/transaction_protocol/recipient.rs +++ b/base_layer/core/src/transactions/transaction_protocol/recipient.rs @@ -20,35 +20,39 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{ +use crate::transactions::{ transaction::{OutputFeatures, TransactionOutput}, transaction_protocol::{ sender::{SingleRoundSenderData as SD, TransactionSenderMessage}, single_receiver::SingleReceiverTransactionProtocol, TransactionProtocolError, }, - types::{CommitmentFactory, MessageHash, PrivateKey, PublicKey, RangeProofService, Signature}, + types::{CryptoFactories, MessageHash, PrivateKey, PublicKey, Signature}, }; use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, convert::TryInto}; -use tari_comms::message::{Message, MessageError}; -use tari_p2p::tari_message::{BlockchainMessage, TariMessageType}; +use std::collections::HashMap; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub enum RecipientState { Finalized(Box), Failed(TransactionProtocolError), } /// An enum describing the types of information that a recipient can send back to the receiver -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub(super) enum RecipientInfo { None, Single(Option>), Multiple(HashMap), } -#[derive(Debug, Clone)] +impl Default for RecipientInfo { + fn default() -> Self { + RecipientInfo::Single(None) + } +} + +#[derive(Debug, Clone, PartialEq)] pub(super) struct MultiRecipientInfo { pub commitment: MessageHash, pub data: RecipientSignedMessage, @@ -63,18 +67,9 @@ pub struct RecipientSignedMessage { pub partial_signature: Signature, } -/// Convert `RecipientSignedMessage` into a Tari Message that can be sent via the tari comms stack -impl TryInto for RecipientSignedMessage { - type Error = MessageError; - - fn try_into(self) -> Result { - Ok((TariMessageType::new(BlockchainMessage::TransactionReply), self).try_into()?) - } -} - /// The generalised transaction recipient protocol. A different state transition network is followed depending on /// whether this is a single recipient or one of many. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct ReceiverTransactionProtocol { pub state: RecipientState, } @@ -93,14 +88,13 @@ impl ReceiverTransactionProtocol { nonce: PrivateKey, spending_key: PrivateKey, features: OutputFeatures, - prover: &RangeProofService, - factory: &CommitmentFactory, + factories: &CryptoFactories, ) -> ReceiverTransactionProtocol { let state = match info { TransactionSenderMessage::None => RecipientState::Failed(TransactionProtocolError::InvalidStateError), TransactionSenderMessage::Single(v) => { - ReceiverTransactionProtocol::single_round(nonce, spending_key, features, &v, prover, factory) + ReceiverTransactionProtocol::single_round(nonce, spending_key, features, &v, factories) }, TransactionSenderMessage::Multiple => Self::multi_round(), }; @@ -145,11 +139,10 @@ impl ReceiverTransactionProtocol { key: PrivateKey, features: OutputFeatures, data: &SD, - prover: &RangeProofService, - factory: &CommitmentFactory, + factories: &CryptoFactories, ) -> RecipientState { - let signer = SingleReceiverTransactionProtocol::create(data, nonce, key, features, prover, factory); + let signer = SingleReceiverTransactionProtocol::create(data, nonce, key, features, factories); match signer { Ok(signed_data) => RecipientState::Finalized(Box::new(signed_data)), Err(e) => RecipientState::Failed(e), @@ -165,28 +158,29 @@ impl ReceiverTransactionProtocol { #[cfg(test)] mod test { - use crate::{ + use crate::transactions::{ + helpers::TestParams, tari_amount::*, transaction::OutputFeatures, transaction_protocol::{ build_challenge, sender::{SingleRoundSenderData, TransactionSenderMessage}, - test_common::TestParams, TransactionMetadata, }, - types::{PublicKey, Signature, COMMITMENT_FACTORY, PROVER}, + types::{CryptoFactories, PublicKey, Signature}, ReceiverTransactionProtocol, }; - use rand::OsRng; use tari_crypto::{commitment::HomomorphicCommitmentFactory, keys::PublicKey as PK}; #[test] fn single_round_recipient() { - let mut rng = OsRng::new().unwrap(); - let p = TestParams::new(&mut rng); + let factories = CryptoFactories::default(); + let p = TestParams::new(); let m = TransactionMetadata { fee: MicroTari(125), lock_height: 0, + meta_info: None, + linked_kernel: None, }; let msg = SingleRoundSenderData { tx_id: 15, @@ -194,6 +188,7 @@ mod test { public_excess: PublicKey::from_secret_key(&p.spend_key), // any random key will do public_nonce: PublicKey::from_secret_key(&p.change_key), // any random key will do metadata: m.clone(), + message: "".to_string(), }; let sender_info = TransactionSenderMessage::Single(Box::new(msg.clone())); let pubkey = PublicKey::from_secret_key(&p.spend_key); @@ -202,15 +197,16 @@ mod test { p.nonce.clone(), p.spend_key.clone(), OutputFeatures::default(), - &PROVER, - &COMMITMENT_FACTORY, + &factories, ); assert!(receiver.is_finalized()); let data = receiver.get_signed_data().unwrap(); assert_eq!(data.tx_id, 15); assert_eq!(data.public_spend_key, pubkey); - assert!(COMMITMENT_FACTORY.open_value(&p.spend_key, 500, &data.output.commitment)); - assert!(data.output.verify_range_proof(&PROVER).unwrap()); + assert!(factories + .commitment + .open_value(&p.spend_key, 500, &data.output.commitment)); + assert!(data.output.verify_range_proof(&factories.range_proof).unwrap()); let r_sum = &msg.public_nonce + &p.public_nonce; let e = build_challenge(&r_sum, &m); let s = Signature::sign(p.spend_key.clone(), p.nonce.clone(), &e).unwrap(); diff --git a/base_layer/core/src/transaction_protocol/sender.rs b/base_layer/core/src/transactions/transaction_protocol/sender.rs similarity index 76% rename from base_layer/core/src/transaction_protocol/sender.rs rename to base_layer/core/src/transactions/transaction_protocol/sender.rs index 13148e50bd..cae2c74589 100644 --- a/base_layer/core/src/transaction_protocol/sender.rs +++ b/base_layer/core/src/transactions/transaction_protocol/sender.rs @@ -20,14 +20,19 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{ +use crate::transactions::{ tari_amount::*, - transaction::{KernelFeatures, Transaction, TransactionBuilder, TransactionInput, TransactionOutput}, - types::{BlindingFactor, CommitmentFactory, PrivateKey, PublicKey, RangeProofService, Signature}, -}; - -use crate::{ - transaction::{KernelBuilder, MAX_TRANSACTION_INPUTS, MAX_TRANSACTION_OUTPUTS, MINIMUM_TRANSACTION_FEE}, + transaction::{ + KernelBuilder, + KernelFeatures, + Transaction, + TransactionBuilder, + TransactionInput, + TransactionOutput, + MAX_TRANSACTION_INPUTS, + MAX_TRANSACTION_OUTPUTS, + MINIMUM_TRANSACTION_FEE, + }, transaction_protocol::{ build_challenge, recipient::{RecipientInfo, RecipientSignedMessage}, @@ -35,26 +40,24 @@ use crate::{ TransactionMetadata, TransactionProtocolError as TPE, }, + types::{BlindingFactor, CryptoFactories, PrivateKey, PublicKey, RangeProofService, Signature}, }; use digest::Digest; use serde::{Deserialize, Serialize}; -use std::convert::TryInto; -use tari_comms::message::{Message, MessageError}; -use tari_crypto::ristretto::pedersen::PedersenCommitment; -use tari_p2p::tari_message::{BlockchainMessage, TariMessageType}; -use tari_utilities::ByteArray; +use tari_crypto::{ristretto::pedersen::PedersenCommitment, tari_utilities::ByteArray}; //---------------------------------------- Local Data types ----------------------------------------------------// /// This struct contains all the information that a transaction initiator (the sender) will manage throughout the /// Transaction construction process. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub(super) struct RawTransactionInfo { pub num_recipients: usize, // The sum of self-created outputs plus change pub amount_to_self: MicroTari, pub ids: Vec, pub amounts: Vec, + pub change: MicroTari, pub metadata: TransactionMetadata, pub inputs: Vec, pub outputs: Vec, @@ -68,8 +71,10 @@ pub(super) struct RawTransactionInfo { pub public_nonce: PublicKey, // The sum of all public nonces pub public_nonce_sum: PublicKey, + #[serde(skip)] pub recipient_info: RecipientInfo, pub signatures: Vec, + pub message: String, } impl RawTransactionInfo { @@ -91,9 +96,11 @@ pub struct SingleRoundSenderData { pub public_nonce: PublicKey, /// The transaction metadata pub metadata: TransactionMetadata, + /// Plain text message to receiver + pub message: String, } -#[derive(Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub enum TransactionSenderMessage { None, Single(Box), @@ -101,17 +108,8 @@ pub enum TransactionSenderMessage { Multiple, } -/// Convert `SenderMessage` into a Tari Message that can be sent via the tari comms stack -impl TryInto for TransactionSenderMessage { - type Error = MessageError; - - fn try_into(self) -> Result { - Ok((TariMessageType::new(BlockchainMessage::Transaction), self).try_into()?) - } -} - //---------------------------------------- Sender State Protocol ----------------------------------------------------// -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct SenderTransactionProtocol { pub(super) state: SenderState, } @@ -162,6 +160,16 @@ impl SenderTransactionProtocol { } } + /// Returns the finalized transaction if the protocol is in the Finalised state and consumes the protocol object. + /// Otherwise it returns an `InvalidStateError`. To keep the object and return a reference to the transaction, see + /// [get_transaction]. + pub fn take_transaction(self) -> Result { + match self.state { + SenderState::FinalizedTransaction(tx) => Ok(tx), + _ => Err(TPE::InvalidStateError), + } + } + /// Method to determine if the transaction protocol has failed pub fn is_failed(&self) -> bool { match &self.state { @@ -220,6 +228,30 @@ impl SenderTransactionProtocol { } } + /// This function will return the value of the change transaction + pub fn get_change_amount(&self) -> Result { + match &self.state { + SenderState::Initializing(info) | + SenderState::Finalizing(info) | + SenderState::SingleRoundMessageReady(info) | + SenderState::CollectingSingleSignature(info) => Ok(info.change), + SenderState::FinalizedTransaction(_) => Err(TPE::InvalidStateError), + SenderState::Failed(_) => Err(TPE::InvalidStateError), + } + } + + /// This function will return the value of the fee of this transaction + pub fn get_fee_amount(&self) -> Result { + match &self.state { + SenderState::Initializing(info) | + SenderState::Finalizing(info) | + SenderState::SingleRoundMessageReady(info) | + SenderState::CollectingSingleSignature(info) => Ok(info.metadata.fee), + SenderState::FinalizedTransaction(_) => Err(TPE::InvalidStateError), + SenderState::Failed(_) => Err(TPE::InvalidStateError), + } + } + /// Build the sender's message for the single-round protocol (one recipient) and move to next State pub fn build_single_round_message(&mut self) -> Result { match &self.state { @@ -230,6 +262,7 @@ impl SenderTransactionProtocol { public_nonce: info.public_nonce.clone(), public_excess: info.public_excess.clone(), metadata: info.metadata.clone(), + message: info.message.clone(), }; self.state = SenderState::CollectingSingleSignature(info.clone()); Ok(result) @@ -269,8 +302,7 @@ impl SenderTransactionProtocol { fn build_transaction( info: &RawTransactionInfo, features: KernelFeatures, - prover: &RangeProofService, - factory: &CommitmentFactory, + factories: &CryptoFactories, ) -> Result { let mut tx_builder = TransactionBuilder::new(); @@ -293,7 +325,7 @@ impl SenderTransactionProtocol { .with_signature(&s_agg) .build()?; tx_builder.with_kernel(kernel); - tx_builder.build(prover, factory).map_err(TPE::from) + tx_builder.build(factories).map_err(TPE::from) } /// Performs sanitary checks on the collected transaction pieces prior to building the final Transaction instance @@ -356,13 +388,7 @@ impl SenderTransactionProtocol { /// formally validate the transaction terms (no inflation, signature matches etc). If any step fails, /// the transaction protocol moves to Failed state and we are done; you can't rescue the situation. The function /// returns `Ok(false)` in this instance. - pub fn finalize( - &mut self, - features: KernelFeatures, - prover: &RangeProofService, - factory: &CommitmentFactory, - ) -> Result - { + pub fn finalize(&mut self, features: KernelFeatures, factories: &CryptoFactories) -> Result { // Create the final aggregated signature, moving to the Failed state if anything goes wrong match &mut self.state { SenderState::Finalizing(_) => { @@ -378,14 +404,14 @@ impl SenderTransactionProtocol { SenderState::Finalizing(info) => { let result = self .validate() - .and_then(|_| Self::build_transaction(info, features, prover, factory)); + .and_then(|_| Self::build_transaction(info, features, factories)); if let Err(e) = result { self.state = SenderState::Failed(e); return Ok(false); } - let mut transaction = result.unwrap(); + let transaction = result.unwrap(); let result = transaction - .validate_internal_consistency(prover, factory) + .validate_internal_consistency(factories, None) .map_err(TPE::TransactionBuildError); if let Err(e) = result { self.state = SenderState::Failed(e); @@ -397,6 +423,31 @@ impl SenderTransactionProtocol { _ => Err(TPE::InvalidStateError), } } + + /// This method is used to store a pending transaction to be sent which should be in the CollectionSingleSignature + /// state, This state will be serialized and returned as a string. + pub fn save_pending_transaction_to_be_sent(&self) -> Result { + match &self.state { + SenderState::Initializing(_) => Err(TPE::InvalidStateError), + SenderState::SingleRoundMessageReady(_) => Err(TPE::InvalidStateError), + SenderState::CollectingSingleSignature(s) => { + let data = serde_json::to_string(s).map_err(|_| TPE::SerializationError)?; + Ok(data) + }, + SenderState::Finalizing(_) => Err(TPE::InvalidStateError), + SenderState::FinalizedTransaction(_) => Err(TPE::InvalidStateError), + SenderState::Failed(_) => Err(TPE::InvalidStateError), + } + } + + /// This method takes the serialized data from the previous method, deserializes it and recreates the pending Sender + /// Transaction from it. + pub fn load_pending_transaction_to_be_sent(data: String) -> Result { + let raw_data: RawTransactionInfo = serde_json::from_str(data.as_str()).map_err(|_| TPE::SerializationError)?; + Ok(Self { + state: SenderState::CollectingSingleSignature(Box::new(raw_data)), + }) + } } pub fn calculate_tx_id(pub_nonce: &PublicKey, index: usize) -> u64 { @@ -409,7 +460,7 @@ pub fn calculate_tx_id(pub_nonce: &PublicKey, index: usize) -> u64 { //---------------------------------------- Sender State ----------------------------------------------------// /// This enum contains all the states of the Sender state machine -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub(super) enum SenderState { /// Transitional state that kicks of the relevant transaction protocol Initializing(Box), @@ -447,27 +498,26 @@ impl SenderState { #[cfg(test)] mod test { - use crate::{ + use crate::transactions::{ fee::Fee, + helpers::{make_input, TestParams}, tari_amount::*, transaction::{KernelFeatures, OutputFeatures, UnblindedOutput}, transaction_protocol::{ sender::SenderTransactionProtocol, single_receiver::SingleReceiverTransactionProtocol, - test_common::{make_input, TestParams}, TransactionProtocolError, }, - types::{COMMITMENT_FACTORY, PROVER}, + types::CryptoFactories, }; - use rand::OsRng; - use tari_crypto::common::Blake256; - use tari_utilities::hex::Hex; + use rand::rngs::OsRng; + use tari_crypto::{common::Blake256, tari_utilities::hex::Hex}; #[test] fn zero_recipients() { - let mut rng = OsRng::new().unwrap(); - let p = TestParams::new(&mut rng); - let (utxo, input) = make_input(&mut rng, MicroTari(1200)); + let factories = CryptoFactories::default(); + let p = TestParams::new(); + let (utxo, input) = make_input(&mut OsRng, MicroTari(1200), &factories.commitment); let mut builder = SenderTransactionProtocol::builder(0); builder .with_lock_height(0) @@ -478,10 +528,10 @@ mod test { .with_input(utxo, input) .with_output(UnblindedOutput::new(MicroTari(500), p.spend_key.clone(), None)) .with_output(UnblindedOutput::new(MicroTari(400), p.spend_key.clone(), None)); - let mut sender = builder.build::(&PROVER, &COMMITMENT_FACTORY).unwrap(); + let mut sender = builder.build::(&factories).unwrap(); assert_eq!(sender.is_failed(), false); assert!(sender.is_finalizing()); - match sender.finalize(KernelFeatures::empty(), &PROVER, &COMMITMENT_FACTORY) { + match sender.finalize(KernelFeatures::empty(), &factories) { Ok(true) => (), Ok(false) => panic!("{:?}", sender.failure_reason()), Err(e) => panic!("{:?}", e), @@ -492,12 +542,12 @@ mod test { #[test] fn single_recipient_no_change() { - let mut rng = OsRng::new().unwrap(); + let factories = CryptoFactories::default(); // Alice's parameters - let a = TestParams::new(&mut rng); + let a = TestParams::new(); // Bob's parameters - let b = TestParams::new(&mut rng); - let (utxo, input) = make_input(&mut rng, MicroTari(1200)); + let b = TestParams::new(); + let (utxo, input) = make_input(&mut OsRng, MicroTari(1200), &factories.commitment); let mut builder = SenderTransactionProtocol::builder(1); let fee = Fee::calculate(MicroTari(20), 1, 1); builder @@ -508,26 +558,32 @@ mod test { .with_input(utxo.clone(), input) // A little twist: Check the case where the change is less than the cost of another output .with_amount(0, MicroTari(1200) - fee - MicroTari(10)); - let mut alice = builder.build::(&PROVER, &COMMITMENT_FACTORY).unwrap(); + let mut alice = builder.build::(&factories).unwrap(); assert!(alice.is_single_round_message_ready()); let msg = alice.build_single_round_message().unwrap(); // Send message down the wire....and wait for response assert!(alice.is_collecting_single_signature()); + + // Test serializing the current state to be sent and resuming from that serialized data + let ser = alice.save_pending_transaction_to_be_sent().unwrap(); + let mut alice = SenderTransactionProtocol::load_pending_transaction_to_be_sent(ser).unwrap(); + // Receiver gets message, deserializes it etc, and creates his response let bob_info = SingleReceiverTransactionProtocol::create( &msg, b.nonce, b.spend_key, OutputFeatures::default(), - &PROVER, - &COMMITMENT_FACTORY, + &factories, ) .unwrap(); // Alice gets message back, deserializes it, etc - alice.add_single_recipient_info(bob_info.clone(), &PROVER).unwrap(); + alice + .add_single_recipient_info(bob_info.clone(), &factories.range_proof) + .unwrap(); // Transaction should be complete assert!(alice.is_finalizing()); - match alice.finalize(KernelFeatures::empty(), &PROVER, &COMMITMENT_FACTORY) { + match alice.finalize(KernelFeatures::empty(), &factories) { Ok(true) => (), Ok(false) => panic!("{:?}", alice.failure_reason()), Err(e) => panic!("{:?}", e), @@ -535,21 +591,21 @@ mod test { assert!(alice.is_finalized()); let tx = alice.get_transaction().unwrap(); assert_eq!(tx.offset, a.offset); - assert_eq!(tx.body.kernels[0].fee, fee + MicroTari(10)); // Check the twist above - assert_eq!(tx.body.inputs.len(), 1); - assert_eq!(tx.body.inputs[0], utxo); - assert_eq!(tx.body.outputs.len(), 1); - assert_eq!(tx.body.outputs[0], bob_info.output); + assert_eq!(tx.body.kernels()[0].fee, fee + MicroTari(10)); // Check the twist above + assert_eq!(tx.body.inputs().len(), 1); + assert_eq!(tx.body.inputs()[0], utxo); + assert_eq!(tx.body.outputs().len(), 1); + assert_eq!(tx.body.outputs()[0], bob_info.output); } #[test] fn single_recipient_with_change() { - let mut rng = OsRng::new().unwrap(); + let factories = CryptoFactories::default(); // Alice's parameters - let a = TestParams::new(&mut rng); + let a = TestParams::new(); // Bob's parameters - let b = TestParams::new(&mut rng); - let (utxo, input) = make_input(&mut rng, MicroTari(2500)); + let b = TestParams::new(); + let (utxo, input) = make_input(&mut OsRng, MicroTari(2500), &factories.commitment); let mut builder = SenderTransactionProtocol::builder(1); let fee = Fee::calculate(MicroTari(20), 1, 2); builder @@ -560,7 +616,7 @@ mod test { .with_change_secret(a.change_key.clone()) .with_input(utxo.clone(), input) .with_amount(0, MicroTari(500)); - let mut alice = builder.build::(&PROVER, &COMMITMENT_FACTORY).unwrap(); + let mut alice = builder.build::(&factories).unwrap(); assert!(alice.is_single_round_message_ready()); let msg = alice.build_single_round_message().unwrap(); println!( @@ -570,16 +626,21 @@ mod test { msg.public_excess.to_hex(), msg.public_nonce.to_hex() ); + // Send message down the wire....and wait for response assert!(alice.is_collecting_single_signature()); + + // Test serializing the current state to be sent and resuming from that serialized data + let ser = alice.save_pending_transaction_to_be_sent().unwrap(); + let mut alice = SenderTransactionProtocol::load_pending_transaction_to_be_sent(ser).unwrap(); + // Receiver gets message, deserializes it etc, and creates his response let bob_info = SingleReceiverTransactionProtocol::create( &msg, b.nonce, b.spend_key, OutputFeatures::default(), - &PROVER, - &COMMITMENT_FACTORY, + &factories, ) .unwrap(); println!( @@ -590,10 +651,12 @@ mod test { bob_info.output.commitment.as_public_key().to_hex() ); // Alice gets message back, deserializes it, etc - alice.add_single_recipient_info(bob_info.clone(), &PROVER).unwrap(); + alice + .add_single_recipient_info(bob_info.clone(), &factories.range_proof) + .unwrap(); // Transaction should be complete assert!(alice.is_finalizing()); - match alice.finalize(KernelFeatures::empty(), &PROVER, &COMMITMENT_FACTORY) { + match alice.finalize(KernelFeatures::empty(), &factories) { Ok(true) => (), Ok(false) => panic!("{:?}", alice.failure_reason()), Err(e) => panic!("{:?}", e), @@ -602,24 +665,21 @@ mod test { assert!(alice.is_finalized()); let tx = alice.get_transaction().unwrap(); assert_eq!(tx.offset, a.offset); - assert_eq!(tx.body.kernels[0].fee, fee); - assert_eq!(tx.body.inputs.len(), 1); - assert_eq!(tx.body.inputs[0], utxo); - assert_eq!(tx.body.outputs.len(), 2); - assert!(tx - .clone() - .validate_internal_consistency(&PROVER, &COMMITMENT_FACTORY) - .is_ok()); + assert_eq!(tx.body.kernels()[0].fee, fee); + assert_eq!(tx.body.inputs().len(), 1); + assert_eq!(tx.body.inputs()[0], utxo); + assert_eq!(tx.body.outputs().len(), 2); + assert!(tx.clone().validate_internal_consistency(&factories, None).is_ok()); } #[test] fn single_recipient_range_proof_fail() { - let mut rng = OsRng::new().unwrap(); + let factories = CryptoFactories::new(32); // Alice's parameters - let a = TestParams::new(&mut rng); + let a = TestParams::new(); // Bob's parameters - let b = TestParams::new(&mut rng); - let (utxo, input) = make_input(&mut rng, (2u64.pow(32) + 2001).into()); + let b = TestParams::new(); + let (utxo, input) = make_input(&mut OsRng, (2u64.pow(32) + 2001).into(), &factories.commitment); let mut builder = SenderTransactionProtocol::builder(1); builder @@ -630,7 +690,7 @@ mod test { .with_change_secret(a.change_key.clone()) .with_input(utxo.clone(), input) .with_amount(0, (2u64.pow(32) + 1).into()); - let mut alice = builder.build::(&PROVER, &COMMITMENT_FACTORY).unwrap(); + let mut alice = builder.build::(&factories).unwrap(); assert!(alice.is_single_round_message_ready()); let msg = alice.build_single_round_message().unwrap(); // Send message down the wire....and wait for response @@ -641,12 +701,11 @@ mod test { b.nonce, b.spend_key, OutputFeatures::default(), - &PROVER, - &COMMITMENT_FACTORY, + &factories, ) .unwrap(); // Alice gets message back, deserializes it, etc - match alice.add_single_recipient_info(bob_info.clone(), &PROVER) { + match alice.add_single_recipient_info(bob_info.clone(), &factories.range_proof) { Ok(_) => panic!("Range proof should have failed to verify"), Err(e) => assert_eq!( e, diff --git a/base_layer/core/src/transaction_protocol/single_receiver.rs b/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs similarity index 80% rename from base_layer/core/src/transaction_protocol/single_receiver.rs rename to base_layer/core/src/transactions/transaction_protocol/single_receiver.rs index f8866cfe1d..5c612b6a97 100644 --- a/base_layer/core/src/transaction_protocol/single_receiver.rs +++ b/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs @@ -20,7 +20,7 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{ +use crate::transactions::{ transaction::{OutputFeatures, TransactionOutput}, transaction_protocol::{ build_challenge, @@ -28,14 +28,14 @@ use crate::{ sender::SingleRoundSenderData as SD, TransactionProtocolError as TPE, }, - types::{CommitmentFactory, PrivateKey as SK, PublicKey, RangeProof, RangeProofService, Signature}, + types::{CryptoFactories, PrivateKey as SK, PublicKey, RangeProof, Signature}, }; use tari_crypto::{ commitment::HomomorphicCommitmentFactory, keys::PublicKey as PK, range_proof::{RangeProofError, RangeProofService as RPS}, + tari_utilities::byte_array::ByteArray, }; -use tari_utilities::byte_array::ByteArray; /// SingleReceiverTransactionProtocol represents the actions taken by the single receiver in the one-round Tari /// transaction protocol. The procedure is straightforward. Upon receiving the sender's information, the receiver: @@ -51,13 +51,11 @@ impl SingleReceiverTransactionProtocol { nonce: SK, spending_key: SK, features: OutputFeatures, - prover: &RangeProofService, - factory: &CommitmentFactory, + factories: &CryptoFactories, ) -> Result { SingleReceiverTransactionProtocol::validate_sender_data(sender_info)?; - let output = - SingleReceiverTransactionProtocol::build_output(sender_info, &spending_key, features, prover, factory)?; + let output = SingleReceiverTransactionProtocol::build_output(sender_info, &spending_key, features, factories)?; let public_nonce = PublicKey::from_secret_key(&nonce); let public_spending_key = PublicKey::from_secret_key(&spending_key); let e = build_challenge(&(&sender_info.public_nonce + &public_nonce), &sender_info.metadata); @@ -83,12 +81,15 @@ impl SingleReceiverTransactionProtocol { sender_info: &SD, spending_key: &SK, features: OutputFeatures, - prover: &RangeProofService, - factory: &CommitmentFactory, + factories: &CryptoFactories, ) -> Result { - let commitment = factory.commit_value(&spending_key, sender_info.amount.into()); - let proof = prover.construct_proof(&spending_key, sender_info.amount.into())?; + let commitment = factories + .commitment + .commit_value(&spending_key, sender_info.amount.into()); + let proof = factories + .range_proof + .construct_proof(&spending_key, sender_info.amount.into())?; Ok(TransactionOutput::new( features, commitment, @@ -100,7 +101,7 @@ impl SingleReceiverTransactionProtocol { #[cfg(test)] mod test { - use crate::{ + use crate::transactions::{ tari_amount::*, transaction::OutputFeatures, transaction_protocol::{ @@ -110,27 +111,27 @@ mod test { TransactionMetadata, TransactionProtocolError, }, - types::{PrivateKey, PublicKey, COMMITMENT_FACTORY, PROVER}, + types::{CryptoFactories, PrivateKey, PublicKey}, }; - use rand::OsRng; + use rand::rngs::OsRng; use tari_crypto::{ commitment::HomomorphicCommitmentFactory, keys::{PublicKey as PK, SecretKey as SK}, }; fn generate_output_parms() -> (PrivateKey, PrivateKey, OutputFeatures) { - let mut rng = OsRng::new().unwrap(); - let r = PrivateKey::random(&mut rng); - let k = PrivateKey::random(&mut rng); + let r = PrivateKey::random(&mut OsRng); + let k = PrivateKey::random(&mut OsRng); let of = OutputFeatures::default(); (r, k, of) } #[test] fn zero_amount_fails() { + let factories = CryptoFactories::default(); let info = SingleRoundSenderData::default(); let (r, k, of) = generate_output_parms(); - match SingleReceiverTransactionProtocol::create(&info, r, k, of, &PROVER, &COMMITMENT_FACTORY) { + match SingleReceiverTransactionProtocol::create(&info, r, k, of, &factories) { Ok(_) => panic!("Zero amounts should fail"), Err(TransactionProtocolError::ValidationError(s)) => assert_eq!(s, "Cannot send zero microTari"), Err(_) => panic!("Protocol fails for the wrong reason"), @@ -139,15 +140,17 @@ mod test { #[test] fn valid_request() { - let mut rng = OsRng::new().unwrap(); - let (_xs, pub_xs) = PublicKey::random_keypair(&mut rng); - let (_rs, pub_rs) = PublicKey::random_keypair(&mut rng); + let factories = CryptoFactories::default(); + let (_xs, pub_xs) = PublicKey::random_keypair(&mut OsRng); + let (_rs, pub_rs) = PublicKey::random_keypair(&mut OsRng); let (r, k, of) = generate_output_parms(); let pubkey = PublicKey::from_secret_key(&k); let pubnonce = PublicKey::from_secret_key(&r); let m = TransactionMetadata { fee: MicroTari(100), lock_height: 0, + meta_info: None, + linked_kernel: None, }; let info = SingleRoundSenderData { tx_id: 500, @@ -155,9 +158,9 @@ mod test { public_excess: pub_xs.clone(), public_nonce: pub_rs.clone(), metadata: m.clone(), + message: "".to_string(), }; - let prot = - SingleReceiverTransactionProtocol::create(&info, r, k.clone(), of, &PROVER, &COMMITMENT_FACTORY).unwrap(); + let prot = SingleReceiverTransactionProtocol::create(&info, r, k.clone(), of, &factories).unwrap(); assert_eq!(prot.tx_id, 500, "tx_id is incorrect"); // Check the signature assert_eq!(prot.public_spend_key, pubkey, "Public key is incorrect"); @@ -169,10 +172,13 @@ mod test { let out = &prot.output; // Check the output that was constructed assert!( - COMMITMENT_FACTORY.open_value(&k, info.amount.into(), &out.commitment), + factories.commitment.open_value(&k, info.amount.into(), &out.commitment), "Output commitment is invalid" ); - assert!(out.verify_range_proof(&PROVER).unwrap(), "Range proof is invalid"); + assert!( + out.verify_range_proof(&factories.range_proof).unwrap(), + "Range proof is invalid" + ); assert!(out.features.flags.is_empty(), "Output features flags have changed"); } } diff --git a/base_layer/core/src/transaction_protocol/transaction_initializer.rs b/base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs similarity index 83% rename from base_layer/core/src/transaction_protocol/transaction_initializer.rs rename to base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs index 56ed48e76c..bdc87ecfaf 100644 --- a/base_layer/core/src/transaction_protocol/transaction_initializer.rs +++ b/base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs @@ -20,11 +20,10 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{ +use crate::transactions::{ fee::Fee, tari_amount::*, transaction::{ - OutputFeatures, TransactionInput, TransactionOutput, UnblindedOutput, @@ -36,21 +35,19 @@ use crate::{ sender::{calculate_tx_id, RawTransactionInfo, SenderState, SenderTransactionProtocol}, TransactionMetadata, }, - types::{BlindingFactor, CommitmentFactory, PrivateKey, PublicKey, RangeProofService}, + types::{BlindingFactor, CryptoFactories, PrivateKey, PublicKey}, }; use digest::Digest; use std::{ collections::HashMap, - error::Error as ErrorTrait, fmt::{Debug, Error, Formatter}, }; -use tari_crypto::keys::PublicKey as PublicKeyTrait; -use tari_utilities::fixed_set::FixedSet; +use tari_crypto::{keys::PublicKey as PublicKeyTrait, tari_utilities::fixed_set::FixedSet}; /// The SenderTransactionInitializer is a Builder that helps set up the initial state for the Sender party of a new /// transaction Typically you don't instantiate this object directly. Rather use /// ```ignore -/// # use tari_core::SenderTransactionProtocol; +/// # use crate::SenderTransactionProtocol; /// SenderTransactionProtocol::new(1); /// ``` /// which returns an instance of this builder. Once all the sender's information has been added via the builder @@ -68,6 +65,7 @@ pub struct SenderTransactionInitializer { offset: Option, excess_blinding_factor: BlindingFactor, private_nonce: Option, + message: Option, } pub struct BuildError { @@ -95,6 +93,7 @@ impl SenderTransactionInitializer { offset: None, private_nonce: None, excess_blinding_factor: BlindingFactor::default(), + message: None, } } @@ -152,17 +151,24 @@ impl SenderTransactionInitializer { self } + /// Provide a text message for receiver + pub fn with_message(&mut self, message: String) -> &mut Self { + self.message = Some(message); + self + } + /// Tries to make a change output with the given transaction parameters and add it to the set of outputs. The total - /// fee, including the additional change output (if any) is returned - fn add_change_if_required(&mut self) -> Result { + /// fee, including the additional change output (if any) is returned along with the amount of change. + /// The change output **always has default output features**. + fn add_change_if_required(&mut self) -> Result<(MicroTari, MicroTari), String> { // The number of outputs excluding a possible residual change output let num_outputs = self.outputs.len() + self.num_recipients; let num_inputs = self.inputs.len(); let total_being_spent = self.unblinded_inputs.iter().map(|i| i.value).sum::(); let total_to_self = self.outputs.iter().map(|o| o.value).sum::(); - let total_amount = self.amounts.sum().ok_or("Not all amounts have been provided")?; - let fee_per_gram = self.fee_per_gram.ok_or("Fee per gram was not provided")?; + let total_amount = self.amounts.sum().ok_or_else(|| "Not all amounts have been provided")?; + let fee_per_gram = self.fee_per_gram.ok_or_else(|| "Fee per gram was not provided")?; let fee_without_change = Fee::calculate(fee_per_gram, num_inputs, num_outputs); let fee_with_change = Fee::calculate(fee_per_gram, num_inputs, num_outputs + 1); let extra_fee = fee_with_change - fee_without_change; @@ -170,22 +176,22 @@ impl SenderTransactionInitializer { let change_amount = total_being_spent.checked_sub(total_to_self + total_amount + fee_without_change); match change_amount { None => Err("You are spending more than you're providing".into()), - Some(MicroTari(0)) => Ok(fee_without_change), + Some(MicroTari(0)) => Ok((fee_without_change, MicroTari(0))), Some(v) => { let change_amount = v.checked_sub(extra_fee); match change_amount { // You can't win. Just add the change to the fee (which is less than the cost of adding another // output and go without a change output - None => Ok(fee_without_change + v), - Some(MicroTari(0)) => Ok(fee_without_change + v), + None => Ok((fee_without_change + v, MicroTari(0))), + Some(MicroTari(0)) => Ok((fee_without_change + v, MicroTari(0))), Some(v) => { let change_key = self .change_secret .as_ref() - .ok_or("Change spending key was not provided")?; + .ok_or_else(|| "Change spending key was not provided")?; let change_key = change_key.clone(); self.with_output(UnblindedOutput::new(v, change_key, None)); - Ok(fee_with_change) + Ok((fee_with_change, v)) }, } }, @@ -210,12 +216,7 @@ impl SenderTransactionInitializer { /// error (so that you can continue building) along with a string listing the missing fields. /// If all the input data is present, but one or more fields are invalid, the function will return a /// `SenderTransactionProtocol` instance in the Failed state. - pub fn build( - mut self, - prover: &RangeProofService, - factory: &CommitmentFactory, - ) -> Result - { + pub fn build(mut self, factories: &CryptoFactories) -> Result { // Compile a list of all data that is missing let mut message = Vec::new(); Self::check_value("Missing Lock Height", &self.lock_height, &mut message); @@ -237,8 +238,8 @@ impl SenderTransactionInitializer { } // Everything is here. Let's send some Tari! // Calculate the fee based on whether we need to add a residual change output or not - let total_fee = match self.add_change_if_required() { - Ok(fee) => fee, + let (total_fee, change) = match self.add_change_if_required() { + Ok((fee, change)) => (fee, change), Err(e) => return self.build_err(&e), }; // Some checks on the fee @@ -249,12 +250,12 @@ impl SenderTransactionInitializer { let outputs = match self .outputs .iter() - .map(|o| o.as_transaction_output(prover, factory, OutputFeatures::default())) + .map(|o| o.as_transaction_output(factories)) .collect::, _>>() { Ok(o) => o, Err(e) => { - return self.build_err(e.description()); + return self.build_err(&e.to_string()); }, }; @@ -280,9 +281,12 @@ impl SenderTransactionInitializer { amount_to_self, ids, amounts: self.amounts.into_vec(), + change, metadata: TransactionMetadata { fee: total_fee, lock_height: self.lock_height.unwrap(), + meta_info: None, + linked_kernel: None, }, inputs: self.inputs, outputs, @@ -294,6 +298,7 @@ impl SenderTransactionInitializer { public_nonce_sum: public_nonce, recipient_info, signatures: Vec::new(), + message: self.message.unwrap_or_else(|| "".to_string()), }; let state = SenderState::Initializing(Box::new(sender_info)); let state = state @@ -307,30 +312,30 @@ impl SenderTransactionInitializer { #[cfg(test)] mod test { - use crate::{ + use crate::transactions::{ fee::{Fee, BASE_COST, WEIGHT_PER_INPUT, WEIGHT_PER_OUTPUT}, + helpers::{make_input, TestParams}, tari_amount::*, transaction::{UnblindedOutput, MAX_TRANSACTION_INPUTS}, transaction_protocol::{ sender::SenderState, - test_common::{make_input, TestParams}, transaction_initializer::SenderTransactionInitializer, TransactionProtocolError, }, - types::{COMMITMENT_FACTORY, PROVER}, + types::CryptoFactories, }; - use rand::OsRng; + use rand::rngs::OsRng; use tari_crypto::common::Blake256; /// One input, 2 outputs #[test] fn no_receivers() { // Create some inputs - let mut rng = OsRng::new().unwrap(); - let p = TestParams::new(&mut rng); + let factories = CryptoFactories::default(); + let p = TestParams::new(); // Start the builder let builder = SenderTransactionInitializer::new(0); - let err = builder.build::(&PROVER, &COMMITMENT_FACTORY).unwrap_err(); + let err = builder.build::(&factories).unwrap_err(); // We should have a bunch of fields missing still, but we can recover and continue assert_eq!( err.message, @@ -342,17 +347,17 @@ mod test { .with_offset(p.offset) .with_private_nonce(p.nonce); builder.with_output(UnblindedOutput::new(MicroTari(100), p.spend_key, None)); - let (utxo, input) = make_input(&mut rng, MicroTari(500)); + let (utxo, input) = make_input(&mut OsRng, MicroTari(500), &factories.commitment); builder.with_input(utxo, input); builder.with_fee_per_gram(MicroTari(20)); let expected_fee = Fee::calculate(MicroTari(20), 1, 2); // We needed a change input, so this should fail - let err = builder.build::(&PROVER, &COMMITMENT_FACTORY).unwrap_err(); + let err = builder.build::(&factories).unwrap_err(); assert_eq!(err.message, "Change spending key was not provided"); // Ok, give them a change output let mut builder = err.builder; builder.with_change_secret(p.change_key.clone()); - let result = builder.build::(&PROVER, &COMMITMENT_FACTORY).unwrap(); + let result = builder.build::(&factories).unwrap(); // Peek inside and check the results if let SenderState::Finalizing(info) = result.state { assert_eq!(info.num_recipients, 0, "Number of receivers"); @@ -372,9 +377,9 @@ mod test { #[test] fn no_change_or_receivers() { // Create some inputs - let mut rng = OsRng::new().unwrap(); - let p = TestParams::new(&mut rng); - let (utxo, input) = make_input(&mut rng, MicroTari(500)); + let factories = CryptoFactories::default(); + let p = TestParams::new(); + let (utxo, input) = make_input(&mut OsRng, MicroTari(500), &factories.commitment); let expected_fee = Fee::calculate(MicroTari(20), 1, 1); let output = UnblindedOutput::new(MicroTari(500) - expected_fee, p.spend_key, None); // Start the builder @@ -386,7 +391,7 @@ mod test { .with_output(output) .with_input(utxo, input) .with_fee_per_gram(MicroTari(20)); - let result = builder.build::(&PROVER, &COMMITMENT_FACTORY).unwrap(); + let result = builder.build::(&factories).unwrap(); // Peek inside and check the results if let SenderState::Finalizing(info) = result.state { assert_eq!(info.num_recipients, 0, "Number of receivers"); @@ -406,9 +411,9 @@ mod test { #[test] fn change_edge_case() { // Create some inputs - let mut rng = OsRng::new().unwrap(); - let p = TestParams::new(&mut rng); - let (utxo, input) = make_input(&mut rng, MicroTari(500)); + let factories = CryptoFactories::default(); + let p = TestParams::new(); + let (utxo, input) = make_input(&mut OsRng, MicroTari(500), &factories.commitment); let expected_fee = MicroTari::from(BASE_COST + (WEIGHT_PER_INPUT + 1 * WEIGHT_PER_OUTPUT) * 20); // 101, output = 80 // Pay out so that I should get change, but not enough to pay for the output let output = UnblindedOutput::new(MicroTari(500) - expected_fee - MicroTari(50), p.spend_key, None); @@ -421,7 +426,7 @@ mod test { .with_output(output) .with_input(utxo, input) .with_fee_per_gram(MicroTari(20)); - let result = builder.build::(&PROVER, &COMMITMENT_FACTORY).unwrap(); + let result = builder.build::(&factories).unwrap(); // Peek inside and check the results if let SenderState::Finalizing(info) = result.state { assert_eq!(info.num_recipients, 0, "Number of receivers"); @@ -440,8 +445,8 @@ mod test { #[test] fn too_many_inputs() { // Create some inputs - let mut rng = OsRng::new().unwrap(); - let p = TestParams::new(&mut rng); + let factories = CryptoFactories::default(); + let p = TestParams::new(); let output = UnblindedOutput::new(MicroTari(500), p.spend_key, None); // Start the builder let mut builder = SenderTransactionInitializer::new(0); @@ -452,19 +457,19 @@ mod test { .with_output(output) .with_fee_per_gram(MicroTari(2)); for _ in 0..MAX_TRANSACTION_INPUTS + 1 { - let (utxo, input) = make_input(&mut rng, MicroTari(50)); + let (utxo, input) = make_input(&mut OsRng, MicroTari(50), &factories.commitment); builder.with_input(utxo, input); } - let err = builder.build::(&PROVER, &COMMITMENT_FACTORY).unwrap_err(); + let err = builder.build::(&factories).unwrap_err(); assert_eq!(err.message, "Too many inputs"); } #[test] fn fee_too_low() { // Create some inputs - let mut rng = OsRng::new().unwrap(); - let p = TestParams::new(&mut rng); - let (utxo, input) = make_input(&mut rng, MicroTari(500)); + let factories = CryptoFactories::default(); + let p = TestParams::new(); + let (utxo, input) = make_input(&mut OsRng, MicroTari(500), &factories.commitment); let output = UnblindedOutput::new(MicroTari(400), p.spend_key, None); // Start the builder let mut builder = SenderTransactionInitializer::new(0); @@ -476,16 +481,16 @@ mod test { .with_output(output) .with_change_secret(p.change_key) .with_fee_per_gram(MicroTari(1)); - let err = builder.build::(&PROVER, &COMMITMENT_FACTORY).unwrap_err(); + let err = builder.build::(&factories).unwrap_err(); assert_eq!(err.message, "Fee is less than the minimum"); } #[test] fn not_enough_funds() { // Create some inputs - let mut rng = OsRng::new().unwrap(); - let p = TestParams::new(&mut rng); - let (utxo, input) = make_input(&mut rng, MicroTari(400)); + let factories = CryptoFactories::default(); + let p = TestParams::new(); + let (utxo, input) = make_input(&mut OsRng, MicroTari(400), &factories.commitment); let output = UnblindedOutput::new(MicroTari(400), p.spend_key, None); // Start the builder let mut builder = SenderTransactionInitializer::new(0); @@ -497,16 +502,16 @@ mod test { .with_output(output) .with_change_secret(p.change_key) .with_fee_per_gram(MicroTari(1)); - let err = builder.build::(&PROVER, &COMMITMENT_FACTORY).unwrap_err(); + let err = builder.build::(&factories).unwrap_err(); assert_eq!(err.message, "You are spending more than you're providing"); } #[test] fn multi_recipients() { // Create some inputs - let mut rng = OsRng::new().unwrap(); - let p = TestParams::new(&mut rng); - let (utxo, input) = make_input(&mut rng, MicroTari(1000)); + let factories = CryptoFactories::default(); + let p = TestParams::new(); + let (utxo, input) = make_input(&mut OsRng, MicroTari(1000), &factories.commitment); let output = UnblindedOutput::new(MicroTari(150), p.spend_key, None); // Start the builder let mut builder = SenderTransactionInitializer::new(2); @@ -520,7 +525,7 @@ mod test { .with_output(output) .with_change_secret(p.change_key) .with_fee_per_gram(MicroTari(20)); - let result = builder.build::(&PROVER, &COMMITMENT_FACTORY).unwrap(); + let result = builder.build::(&factories).unwrap(); // Peek inside and check the results if let SenderState::Failed(TransactionProtocolError::UnsupportedError(s)) = result.state { assert_eq!(s, "Multiple recipients are not supported yet") @@ -532,10 +537,10 @@ mod test { #[test] fn single_recipient() { // Create some inputs - let mut rng = OsRng::new().unwrap(); - let p = TestParams::new(&mut rng); - let (utxo1, input1) = make_input(&mut rng, MicroTari(2000)); - let (utxo2, input2) = make_input(&mut rng, MicroTari(3000)); + let factories = CryptoFactories::default(); + let p = TestParams::new(); + let (utxo1, input1) = make_input(&mut OsRng, MicroTari(2000), &factories.commitment); + let (utxo2, input2) = make_input(&mut OsRng, MicroTari(3000), &factories.commitment); let weight = MicroTari(30); let expected_fee = Fee::calculate(weight, 2, 3); let output = UnblindedOutput::new(MicroTari(1500) - expected_fee, p.spend_key, None); @@ -551,7 +556,7 @@ mod test { .with_amount(0, MicroTari(2500)) .with_change_secret(p.change_key) .with_fee_per_gram(weight); - let result = builder.build::(&PROVER, &COMMITMENT_FACTORY).unwrap(); + let result = builder.build::(&factories).unwrap(); // Peek inside and check the results if let SenderState::SingleRoundMessageReady(info) = result.state { assert_eq!(info.num_recipients, 1, "Number of receivers"); @@ -570,9 +575,9 @@ mod test { #[test] fn fail_range_proof() { // Create some inputs - let mut rng = OsRng::new().unwrap(); - let p = TestParams::new(&mut rng); - let (utxo1, input1) = make_input(&mut rng, (2u64.pow(32) + 10000u64).into()); + let factories = CryptoFactories::new(32); + let p = TestParams::new(); + let (utxo1, input1) = make_input(&mut OsRng, (2u64.pow(32) + 10000u64).into(), &factories.commitment); let weight = MicroTari(30); let output = UnblindedOutput::new((1u64.pow(32) + 1u64).into(), p.spend_key, None); // Start the builder @@ -586,7 +591,7 @@ mod test { .with_amount(0, MicroTari(100)) .with_change_secret(p.change_key) .with_fee_per_gram(weight); - let result = builder.build::(&PROVER, &COMMITMENT_FACTORY); + let result = builder.build::(&factories); match result { Ok(_) => panic!("Range proof should have failed to verify"), diff --git a/base_layer/core/src/types.rs b/base_layer/core/src/transactions/types.rs similarity index 65% rename from base_layer/core/src/types.rs rename to base_layer/core/src/transactions/types.rs index 4b99c419a1..3154c81203 100644 --- a/base_layer/core/src/types.rs +++ b/base_layer/core/src/transactions/types.rs @@ -1,4 +1,4 @@ -// Copyright 2018 The Tari Project +// Copyright 2019. The Tari Project // // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the // following conditions are met: @@ -19,11 +19,9 @@ // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Portions of this file were originally copyrighted (c) 2018 The Grin Developers, issued under the Apache License, -// Version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0. -use crate::{bullet_rangeproofs::BulletRangeProof, proof_of_work::BlakePow}; +use crate::transactions::bullet_rangeproofs::BulletRangeProof; +use std::sync::Arc; use tari_crypto::{ common::Blake256, ristretto::{ @@ -47,18 +45,15 @@ pub type CommitmentFactory = PedersenCommitmentFactory; pub type PrivateKey = RistrettoSecretKey; pub type BlindingFactor = RistrettoSecretKey; -/// Define the explicit Public key implementation for the Tari base layer -pub type PublicKey = RistrettoPublicKey; - /// Define the hash function that will be used to produce a signature challenge pub type SignatureHasher = Blake256; +/// Define the explicit Public key implementation for the Tari base layer +pub type PublicKey = RistrettoPublicKey; + /// Specify the Hash function for general hashing pub type HashDigest = Blake256; -/// Define the data type that is used to store results of `HashDigest` -pub type HashOutput = Vec; - /// Specify the digest type for signature challenges pub type Challenge = Blake256; @@ -71,16 +66,49 @@ pub type RangeProofService = DalekRangeProofService; /// Specify the range proof pub type RangeProof = BulletRangeProof; -/// Select the Proof of work algorithm used -pub type TariProofOfWork = BlakePow; +/// Define the data type that is used to store results of `HashDigest` +pub type HashOutput = Vec; -#[cfg(test)] -pub const MAX_RANGE_PROOF_RANGE: usize = 32; // 2^32 This is the only way to produce failing range proofs for the tests -#[cfg(not(test))] pub const MAX_RANGE_PROOF_RANGE: usize = 64; // 2^64 -lazy_static! { - pub static ref COMMITMENT_FACTORY: CommitmentFactory = CommitmentFactory::default(); - pub static ref PROVER: RangeProofService = - RangeProofService::new(MAX_RANGE_PROOF_RANGE, &COMMITMENT_FACTORY).unwrap(); +/// A convenience struct wrapping cryptographic factories that are used through-out the rest of the code base +/// Uses Arcs internally so calling clone on this is cheap, no need to wrap this in an Arc +pub struct CryptoFactories { + pub commitment: Arc, + pub range_proof: Arc, +} + +impl Default for CryptoFactories { + /// Return a default set of crypto factories based on Pedersen commitments with G and H defined in + /// [pedersen.rs](/infrastructure/crypto/src/ristretto/pedersen.rs), and an associated range proof factory with a + /// range of `[0; 2^64)`. + fn default() -> Self { + CryptoFactories::new(MAX_RANGE_PROOF_RANGE) + } +} + +impl CryptoFactories { + /// Create a new set of crypto factories. + /// + /// ## Parameters + /// + /// * `max_proof_range`: Sets the the maximum value in range proofs, where `max = 2^max_proof_range` + pub fn new(max_proof_range: usize) -> Self { + let commitment = Arc::new(CommitmentFactory::default()); + let range_proof = Arc::new(RangeProofService::new(max_proof_range, &commitment).unwrap()); + Self { + commitment, + range_proof, + } + } +} + +/// Uses Arc's internally so calling clone on this is cheap, no need to wrap this in an Arc +impl Clone for CryptoFactories { + fn clone(&self) -> Self { + Self { + commitment: self.commitment.clone(), + range_proof: self.range_proof.clone(), + } + } } diff --git a/base_layer/core/src/validation/block_validators.rs b/base_layer/core/src/validation/block_validators.rs new file mode 100644 index 0000000000..01ab80673e --- /dev/null +++ b/base_layer/core/src/validation/block_validators.rs @@ -0,0 +1,189 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + blocks::{ + blockheader::{BlockHeader, BlockHeaderValidationError}, + Block, + BlockValidationError, + NewBlockTemplate, + }, + chain_storage::{BlockchainBackend, BlockchainDatabase}, + consensus::{ConsensusConstants, ConsensusManager}, + transactions::{transaction::OutputFlags, types::CryptoFactories}, + validation::{ + helpers::{check_achieved_difficulty_at_chain_tip, check_median_timestamp_at_chain_tip}, + Validation, + ValidationError, + }, +}; +use log::*; +use tari_crypto::tari_utilities::hash::Hashable; +pub const LOG_TARGET: &str = "c::val::block_validators"; + +/// This validator tests whether a candidate block is internally consistent +#[derive(Clone)] +pub struct StatelessValidator { + consensus_constants: ConsensusConstants, +} + +impl StatelessValidator { + pub fn new(consensus_constants: &ConsensusConstants) -> Self { + Self { + consensus_constants: consensus_constants.clone(), + } + } +} + +impl Validation for StatelessValidator { + /// The consensus checks that are done (in order of cheapest to verify to most expensive): + /// 1. Is there precisely one Coinbase output and is it correctly defined? + /// 1. Is the accounting correct? + /// 1. Are all inputs allowed to be spent (Are the feature flags satisfied) + fn validate(&self, block: &Block) -> Result<(), ValidationError> { + check_coinbase_output(block, &self.consensus_constants)?; + // Check that the inputs are are allowed to be spent + block.check_stxo_rules().map_err(BlockValidationError::from)?; + check_cut_through(block)?; + Ok(()) + } +} + +/// This block checks whether a block satisfies *all* consensus rules. If a block passes this validator, it is the +/// next block on the blockchain. +pub struct FullConsensusValidator { + rules: ConsensusManager, + factories: CryptoFactories, + db: BlockchainDatabase, +} + +impl FullConsensusValidator +where B: BlockchainBackend +{ + pub fn new(rules: ConsensusManager, factories: CryptoFactories, db: BlockchainDatabase) -> Self { + Self { rules, factories, db } + } + + fn db(&self) -> Result, ValidationError> { + Ok(self.db.clone()) + } +} + +impl Validation for FullConsensusValidator { + /// The consensus checks that are done (in order of cheapest to verify to most expensive): + /// 1. Does the block satisfy the stateless checks? + /// 1. Are all inputs currently in the UTXO set? + /// 1. Is the block header timestamp less than the ftl? + /// 1. Is the block header timestamp greater than the median timestamp? + /// 1. Is the Proof of Work valid? + /// 1. Is the achieved difficulty of this block >= the target difficulty for this block? + fn validate(&self, block: &Block) -> Result<(), ValidationError> { + check_coinbase_output(block, &self.rules.consensus_constants())?; + check_cut_through(block)?; + block.check_stxo_rules().map_err(BlockValidationError::from)?; + check_accounting_balance(block, self.rules.clone(), &self.factories)?; + check_inputs_are_utxos(block, self.db()?)?; + check_timestamp_ftl(&block.header, &self.rules)?; + check_median_timestamp_at_chain_tip(&block.header, self.db()?, self.rules.clone())?; + check_achieved_difficulty_at_chain_tip(&block.header, self.db()?, self.rules.clone())?; // Update function signature once diff adjuster is complete + Ok(()) + } +} + +//------------------------------------- Block validator helper functions -------------------------------------// +fn check_accounting_balance( + block: &Block, + rules: ConsensusManager, + factories: &CryptoFactories, +) -> Result<(), ValidationError> +{ + let offset = &block.header.total_kernel_offset; + let total_coinbase = rules.calculate_coinbase_and_fees(block); + block + .body + .validate_internal_consistency(&offset, total_coinbase, factories) + .map_err(ValidationError::TransactionError) +} + +fn check_coinbase_output(block: &Block, consensus_constants: &ConsensusConstants) -> Result<(), ValidationError> { + block + .check_coinbase_output(consensus_constants) + .map_err(ValidationError::from) +} + +/// This function checks that all inputs in the blocks are valid UTXO's to be spend +fn check_inputs_are_utxos( + block: &Block, + db: BlockchainDatabase, +) -> Result<(), ValidationError> +{ + for utxo in block.body.inputs() { + if !(utxo.features.flags.contains(OutputFlags::COINBASE_OUTPUT)) && + !(db.is_utxo(utxo.hash())).map_err(|e| ValidationError::CustomError(e.to_string()))? + { + warn!( + target: LOG_TARGET, + "Block validation failed because the block has invalid input: {}", utxo + ); + return Err(ValidationError::BlockError(BlockValidationError::InvalidInput)); + } + } + Ok(()) +} + +/// This function tests that the block timestamp is less than the ftl. +fn check_timestamp_ftl( + block_header: &BlockHeader, + consensus_manager: &ConsensusManager, +) -> Result<(), ValidationError> +{ + if block_header.timestamp > consensus_manager.consensus_constants().ftl() { + return Err(ValidationError::BlockHeaderError( + BlockHeaderValidationError::InvalidTimestampFutureTimeLimit, + )); + } + Ok(()) +} + +fn check_mmr_roots(block: &Block, db: BlockchainDatabase) -> Result<(), ValidationError> { + let template = NewBlockTemplate::from(block.clone()); + let tmp_block = db + .calculate_mmr_roots(template) + .map_err(|e| ValidationError::CustomError(e.to_string()))?; + let tmp_header = &tmp_block.header; + let header = &block.header; + if header.kernel_mr != tmp_header.kernel_mr || + header.output_mr != tmp_header.output_mr || + header.range_proof_mr != tmp_header.range_proof_mr + { + Err(ValidationError::BlockError(BlockValidationError::MismatchedMmrRoots)) + } else { + Ok(()) + } +} + +fn check_cut_through(block: &Block) -> Result<(), ValidationError> { + if !block.body.cut_through_check() { + return Err(ValidationError::BlockError(BlockValidationError::NoCutThrough)); + } + Ok(()) +} diff --git a/base_layer/core/src/proof_of_work/pow.rs b/base_layer/core/src/validation/error.rs similarity index 63% rename from base_layer/core/src/proof_of_work/pow.rs rename to base_layer/core/src/validation/error.rs index 7a329525fa..d04a5af1cd 100644 --- a/base_layer/core/src/proof_of_work/pow.rs +++ b/base_layer/core/src/validation/error.rs @@ -19,23 +19,29 @@ // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// use crate::{ - blocks::BlockHeader, - proof_of_work::{difficulty::Difficulty, error::PowError}, + blocks::{blockheader::BlockHeaderValidationError, BlockValidationError}, + transactions::transaction::TransactionError, }; -use tari_utilities::{ByteArray, Hashable}; +use derive_error::Error; -/// `WorkProof` is a trait that captures common functionality for different proof of work algorithms. -pub trait ProofOfWork: ByteArray + Hashable { - /// This function will calculate the difficulty for the proof of work given the nonce and block header. This - /// function is used to validate proofs of work generated by miners. - /// - /// Generally speaking, the difficulty is roughly how many mining attempts a miner will make, _on average_ before - /// finding a nonce that meets the difficulty target. - /// - /// In actuality, the difficulty is _defined_ as the maximum target value (u265) divided by the block header hash - /// (as a u256) - fn calculate_difficulty(&self, nonce: u64, header: &BlockHeader) -> Result; +#[derive(Clone, Debug, PartialEq, Error)] +pub enum ValidationError { + BlockHeaderError(BlockHeaderValidationError), + BlockError(BlockValidationError), + // Contains kernels or inputs that are not yet spendable + MaturityError, + // Contains unknown inputs + UnknownInputs, + // The transaction has some transaction error + TransactionError(TransactionError), + /// Custom error with string message + #[error(no_from, non_std, msg_embedded)] + CustomError(String), + /// A database instance must be set for this validator + NoDatabaseConfigured, + // The total expected supply plus the total accumulated (offset) excess does not equal the sum of all UTXO + // commitments. + InvalidAccountingBalance, } diff --git a/base_layer/core/src/validation/helpers.rs b/base_layer/core/src/validation/helpers.rs new file mode 100644 index 0000000000..284b198de6 --- /dev/null +++ b/base_layer/core/src/validation/helpers.rs @@ -0,0 +1,126 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + blocks::blockheader::{BlockHeader, BlockHeaderValidationError}, + chain_storage::{BlockchainBackend, BlockchainDatabase}, + consensus::ConsensusManager, + proof_of_work::PowError, + validation::ValidationError, +}; +use log::*; +use tari_crypto::tari_utilities::hash::Hashable; +pub const LOG_TARGET: &str = "c::val::helpers"; + +/// This function tests that the block timestamp is greater than the median timestamp at the chain tip. +pub fn check_median_timestamp_at_chain_tip( + block_header: &BlockHeader, + db: BlockchainDatabase, + rules: ConsensusManager, +) -> Result<(), ValidationError> +{ + let tip_height = db + .get_metadata() + .or_else(|e| { + error!(target: LOG_TARGET, "validation failed to get metadata {:?}.", e); + Err(e) + }) + .map_err(|e| ValidationError::CustomError(e.to_string()))? + .height_of_longest_chain + .unwrap_or(0); + check_median_timestamp(&block_header, tip_height, rules) +} + +/// This function tests that the block timestamp is greater than the median timestamp at the specified height. +pub fn check_median_timestamp( + block_header: &BlockHeader, + height: u64, + rules: ConsensusManager, +) -> Result<(), ValidationError> +{ + if block_header.height == 0 || rules.get_genesis_block_hash() == block_header.hash() { + return Ok(()); // Its the genesis block, so we dont have to check median + } + let median_timestamp = rules + .get_median_timestamp_at_height(height) + .or_else(|e| { + error!(target: LOG_TARGET, "Validation could not get median timestamp"); + + Err(e) + }) + .map_err(|_| ValidationError::BlockHeaderError(BlockHeaderValidationError::InvalidTimestamp))?; + if block_header.timestamp < median_timestamp { + return Err(ValidationError::BlockHeaderError( + BlockHeaderValidationError::InvalidTimestamp, + )); + } + Ok(()) +} + +/// Calculates the achieved and target difficulties at the chain tip and compares them. +pub fn check_achieved_difficulty_at_chain_tip( + block_header: &BlockHeader, + db: BlockchainDatabase, + rules: ConsensusManager, +) -> Result<(), ValidationError> +{ + let tip_height = db + .get_metadata() + .or_else(|e| { + error!(target: LOG_TARGET, "Validation could not get achieved difficultly"); + Err(e) + }) + .map_err(|e| ValidationError::CustomError(e.to_string()))? + .height_of_longest_chain + .unwrap_or(0); + check_achieved_difficulty(&block_header, tip_height, rules) +} + +/// Calculates the achieved and target difficulties at the specified height and compares them. +pub fn check_achieved_difficulty( + block_header: &BlockHeader, + height: u64, + rules: ConsensusManager, +) -> Result<(), ValidationError> +{ + let achieved = block_header.achieved_difficulty(); + let mut target = 1.into(); + if block_header.height > 0 || rules.get_genesis_block_hash() != block_header.hash() { + target = rules + .get_target_difficulty_with_height(block_header.pow.pow_algo, height) + .or_else(|e| { + error!(target: LOG_TARGET, "Validation could not get achieved difficulty"); + Err(e) + }) + .map_err(|_| { + ValidationError::BlockHeaderError(BlockHeaderValidationError::ProofOfWorkError( + PowError::InvalidProofOfWork, + )) + })?; + } + if achieved < target { + return Err(ValidationError::BlockHeaderError( + BlockHeaderValidationError::ProofOfWorkError(PowError::AchievedDifficultyTooLow), + )); + } + Ok(()) +} diff --git a/base_layer/core/src/validation/mocks.rs b/base_layer/core/src/validation/mocks.rs new file mode 100644 index 0000000000..c7015ec065 --- /dev/null +++ b/base_layer/core/src/validation/mocks.rs @@ -0,0 +1,67 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::Validation; +use crate::{chain_storage::BlockchainBackend, validation::error::ValidationError}; + +pub struct MockValidator { + result: bool, +} + +impl MockValidator { + pub fn new(is_valid: bool) -> MockValidator { + MockValidator { result: is_valid } + } +} + +impl Validation for MockValidator { + fn validate(&self, _item: &T) -> Result<(), ValidationError> { + if self.result { + Ok(()) + } else { + Err(ValidationError::CustomError( + "This mock validator always returns an error".into(), + )) + } + } +} + +#[cfg(test)] +mod test { + use crate::{ + chain_storage::MemoryDatabase, + transactions::types::HashDigest, + validation::{mocks::MockValidator, Validation}, + }; + + #[test] + fn mock_is_valid() { + let validator = MockValidator::new(true); + assert!(>>::validate(&validator, &()).is_ok()); + } + + #[test] + fn mock_is_invalid() { + let validator = MockValidator::new(false); + assert!(>>::validate(&validator, &()).is_err()); + } +} diff --git a/base_layer/core/src/validation/mod.rs b/base_layer/core/src/validation/mod.rs new file mode 100644 index 0000000000..54f1945ea4 --- /dev/null +++ b/base_layer/core/src/validation/mod.rs @@ -0,0 +1,38 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +//! The validation module defines the [Validation] trait which describes all code that can perform block, +//! transaction, or other validation tasks. Validators implement the [Validation] trait and can be chained together +//! in a [ValidationPipeline] object to carry out complex validation routines. +//! +//! This module also defines a mock [MockValidator] that is useful for testing components that require validation +//! without having to bring in all sorts of blockchain and communications paraphernalia. + +mod error; +mod helpers; +mod traits; + +pub mod block_validators; +pub mod mocks; +pub use error::ValidationError; +pub use traits::{Validation, Validator}; +pub mod transaction_validators; diff --git a/base_layer/core/src/validation/traits.rs b/base_layer/core/src/validation/traits.rs new file mode 100644 index 0000000000..ccf58bed72 --- /dev/null +++ b/base_layer/core/src/validation/traits.rs @@ -0,0 +1,35 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{chain_storage::BlockchainBackend, validation::error::ValidationError}; + +pub type Validator = Box>; + +/// The core validation trait. Multiple `Validation` implementors can be chained together in a [ValidatorPipeline] to +/// provide consensus validation for blocks, transactions, or DAN instructions. Implementors only need to implement +/// the methods that are relevant for the pipeline, since the default implementation always passes. +pub trait Validation: Send + Sync +where B: BlockchainBackend +{ + /// General validation code that can run independent of external state + fn validate(&self, item: &T) -> Result<(), ValidationError>; +} diff --git a/base_layer/core/src/validation/transaction_validators.rs b/base_layer/core/src/validation/transaction_validators.rs new file mode 100644 index 0000000000..7f0318838c --- /dev/null +++ b/base_layer/core/src/validation/transaction_validators.rs @@ -0,0 +1,194 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + chain_storage::{BlockchainBackend, BlockchainDatabase}, + transactions::{transaction::Transaction, types::CryptoFactories}, + validation::{Validation, ValidationError}, +}; +use log::*; +use tari_crypto::tari_utilities::hash::Hashable; +pub const LOG_TARGET: &str = "c::val::transaction_validators"; + +/// This validator will only check that a transaction is internally consistent. It requires no state information. +pub struct StatelessTxValidator { + factories: CryptoFactories, +} + +impl StatelessTxValidator { + pub fn new(factories: CryptoFactories) -> Self { + Self { factories } + } +} + +impl Validation for StatelessTxValidator { + fn validate(&self, tx: &Transaction) -> Result<(), ValidationError> { + verify_tx(tx, &self.factories)?; + Ok(()) + } +} + +/// This validator will perform a full verification of the transaction. In order the following will be checked: +/// Transaction integrity, All inputs exist in the backend, All timelocks (kernel lock heights and output maturities) +/// have passed +pub struct FullTxValidator { + factories: CryptoFactories, + db: BlockchainDatabase, +} + +impl FullTxValidator +where B: BlockchainBackend +{ + pub fn new(factories: CryptoFactories, db: BlockchainDatabase) -> Self { + Self { factories, db } + } +} + +impl Validation for FullTxValidator { + fn validate(&self, tx: &Transaction) -> Result<(), ValidationError> { + verify_tx(tx, &self.factories)?; + verify_inputs(tx, self.db.clone())?; + let height = self + .db + .get_height() + .map_err(|e| ValidationError::CustomError(e.to_string()))? + .unwrap_or(0); + verify_timelocks(tx, height)?; + Ok(()) + } +} + +/// This validator assumes that the transaction was already validated and it will skip this step. It will only check, in +/// order,: All inputs exist in the backend, All timelocks (kernel lock heights and output maturities) have passed +pub struct TxInputAndMaturityValidator { + db: BlockchainDatabase, +} + +impl TxInputAndMaturityValidator +where B: BlockchainBackend +{ + pub fn new(db: BlockchainDatabase) -> Self { + Self { db } + } +} + +impl Validation for TxInputAndMaturityValidator { + fn validate(&self, tx: &Transaction) -> Result<(), ValidationError> { + verify_inputs(tx, self.db.clone())?; + let height = self + .db + .get_height() + .or_else(|e| { + error!( + target: LOG_TARGET, + "Transaction validation could not get height {:?}.", e + ); + Err(e) + }) + .map_err(|e| ValidationError::CustomError(e.to_string()))? + .unwrap_or(0); + verify_timelocks(tx, height)?; + Ok(()) + } +} + +/// This validator will only check that inputs exists in the backend. +pub struct InputTxValidator { + db: BlockchainDatabase, +} + +impl InputTxValidator +where B: BlockchainBackend +{ + pub fn new(db: BlockchainDatabase) -> Self { + Self { db } + } +} + +impl Validation for InputTxValidator { + fn validate(&self, tx: &Transaction) -> Result<(), ValidationError> { + verify_inputs(tx, self.db.clone())?; + Ok(()) + } +} + +/// This validator will only check timelocks, it will check that kernel lock heights and output maturities have passed. +pub struct TimeLockTxValidator { + db: BlockchainDatabase, +} + +impl TimeLockTxValidator +where B: BlockchainBackend +{ + pub fn new(db: BlockchainDatabase) -> Self { + Self { db } + } +} + +impl Validation for TimeLockTxValidator { + fn validate(&self, tx: &Transaction) -> Result<(), ValidationError> { + let height = self + .db + .get_height() + .or_else(|e| { + error!( + target: LOG_TARGET, + "Transaction validation could not get height {:?}.", e + ); + Err(e) + }) + .map_err(|e| ValidationError::CustomError(e.to_string()))? + .unwrap_or(0); + verify_timelocks(tx, height)?; + Ok(()) + } +} + +// This function verifies that the provided transaction is internally sound and that no funds were created in the +// transaction. +fn verify_tx(tx: &Transaction, factories: &CryptoFactories) -> Result<(), ValidationError> { + tx.validate_internal_consistency(factories, None) + .map_err(ValidationError::TransactionError) +} + +// This function checks that all the timelocks in the provided transaction pass. It checks kernel lock heights and +// input maturities +fn verify_timelocks(tx: &Transaction, current_height: u64) -> Result<(), ValidationError> { + if tx.min_spendable_height() > current_height + 1 { + return Err(ValidationError::MaturityError); + } + Ok(()) +} + +// This function checks that all inputs exist in the provided database backend +fn verify_inputs(tx: &Transaction, db: BlockchainDatabase) -> Result<(), ValidationError> { + for input in tx.body.inputs() { + if !(db.is_utxo(input.hash())).map_err(|e| ValidationError::CustomError(e.to_string()))? { + warn!( + target: LOG_TARGET, + "Transaction validation failed due to unknown input: {}", input + ); + return Err(ValidationError::UnknownInputs); + } + } + Ok(()) +} diff --git a/base_layer/core/tests/async_db.rs b/base_layer/core/tests/async_db.rs new file mode 100644 index 0000000000..36133fdbcf --- /dev/null +++ b/base_layer/core/tests/async_db.rs @@ -0,0 +1,239 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#[allow(dead_code)] +mod helpers; + +use helpers::{ + block_builders::chain_block, + sample_blockchains::{create_blockchain_db_no_cut_through, create_new_blockchain}, +}; +use std::ops::Deref; +use tari_core::{ + blocks::Block, + chain_storage::{async_db, BlockAddResult, MmrTree}, + consensus::{ConsensusManager, ConsensusManagerBuilder, Network}, + helpers::{create_orphan_block, MockBackend}, + transactions::{ + helpers::schema_to_transaction, + tari_amount::T, + transaction::{TransactionOutput, UnblindedOutput}, + types::CommitmentFactory, + }, + txn_schema, +}; +use tari_crypto::{ + commitment::HomomorphicCommitmentFactory, + tari_utilities::{hex::Hex, Hashable}, +}; +use tari_test_utils::runtime::test_async; + +/// Finds the UTXO in a block corresponding to the unblinded output. We have to search for outputs because UTXOs get +/// sorted in blocks, and so the order they were inserted in can change. +fn find_utxo(output: &UnblindedOutput, block: &Block, factory: &CommitmentFactory) -> Option { + for utxo in block.body.outputs().iter() { + if factory.open_value(&output.spending_key, output.value.into(), &utxo.commitment) { + return Some(utxo.clone()); + } + } + return None; +} + +#[test] +fn fetch_async_kernel() { + let (db, blocks, _, _) = create_blockchain_db_no_cut_through(); + test_async(|rt| { + for block in blocks.into_iter() { + block.body.kernels().into_iter().for_each(|k| { + let db = db.clone(); + let k = k.clone(); + let hash = k.hash(); + rt.spawn(async move { + let kern_db = async_db::fetch_kernel(db, hash).await; + assert_eq!(k, kern_db.unwrap()); + }); + }); + } + }); +} + +#[test] +fn fetch_async_headers() { + let (db, blocks, _, _) = create_blockchain_db_no_cut_through(); + test_async(move |rt| { + for block in blocks.into_iter() { + let height = block.header.height; + let hash = block.hash(); + let db = db.clone(); + rt.spawn(async move { + let header_height = async_db::fetch_header(db.clone(), height).await.unwrap(); + let header_hash = async_db::fetch_header_with_block_hash(db.clone(), hash).await.unwrap(); + assert_eq!(block.header, header_height); + assert_eq!(block.header, header_hash); + }); + } + }); +} + +#[test] +fn async_rewind_to_height() { + let (db, blocks, _, _) = create_blockchain_db_no_cut_through(); + test_async(move |rt| { + let dbc = db.clone(); + rt.spawn(async move { + async_db::rewind_to_height(dbc.clone(), 2).await.unwrap(); + let result = async_db::fetch_block(dbc.clone(), 3).await; + assert!(result.is_err()); + let block = async_db::fetch_block(dbc.clone(), 2).await.unwrap(); + assert_eq!(block.confirmations(), 1); + assert_eq!(blocks[2], Block::from(block)); + }); + }); +} + +#[test] +fn fetch_async_utxo() { + let (db, blocks, outputs, _) = create_blockchain_db_no_cut_through(); + let factory = CommitmentFactory::default(); + // Retrieve a UTXO and an STXO + let utxo = find_utxo(&outputs[4][0], &blocks[4], &factory).unwrap(); + let stxo = find_utxo(&outputs[1][0], &blocks[1], &factory).unwrap(); + test_async(move |rt| { + let db = db.clone(); + let db2 = db.clone(); + let _blocks2 = blocks.clone(); + rt.spawn(async move { + let utxo_check = async_db::fetch_utxo(db.clone(), utxo.hash()).await; + assert_eq!(utxo_check, Ok(utxo)); + }); + rt.spawn(async move { + let stxo_check = async_db::fetch_stxo(db2.clone(), stxo.hash()).await; + assert_eq!(stxo_check, Ok(stxo)); + }); + }); +} + +#[test] +fn async_is_utxo() { + let (db, blocks, outputs, _) = create_blockchain_db_no_cut_through(); + let factory = CommitmentFactory::default(); + blocks.iter().for_each(|b| println!("{}", b)); + // Retrieve a UTXO and an STXO + let utxo = find_utxo(&outputs[4][0], &blocks[4], &factory).unwrap(); + let stxo = find_utxo(&outputs[1][0], &blocks[1], &factory).unwrap(); + // Check using sync functions + assert_eq!(db.is_utxo(utxo.hash()), Ok(true)); + assert_eq!(db.is_utxo(stxo.hash()), Ok(false)); + test_async(move |rt| { + let db = db.clone(); + let db2 = db.clone(); + let _blocks2 = blocks.clone(); + rt.spawn(async move { + let is_utxo = async_db::is_utxo(db.clone(), utxo.hash()).await; + assert_eq!(is_utxo, Ok(true)); + }); + rt.spawn(async move { + let is_utxo = async_db::is_utxo(db2.clone(), stxo.hash()).await; + assert_eq!(is_utxo, Ok(false)); + }); + }); +} + +#[test] +fn fetch_async_block() { + let (db, blocks, _, _) = create_blockchain_db_no_cut_through(); + test_async(move |rt| { + for block in blocks.into_iter() { + let height = block.header.height; + let db = db.clone(); + rt.spawn(async move { + let block_check = async_db::fetch_block(db.clone(), height).await.unwrap(); + assert_eq!(&block, block_check.block()); + }); + } + }); +} + +#[test] +fn async_add_new_block() { + let network = Network::LocalNet; + let (db, blocks, outputs, consensus_manager) = create_new_blockchain(network); + let schema = vec![txn_schema!(from: vec![outputs[0][0].clone()], to: vec![20 * T, 20 * T])]; + let txns = schema_to_transaction(&schema) + .0 + .iter() + .map(|t| t.deref().clone()) + .collect(); + let new_block = chain_block(&blocks.last().unwrap(), txns, &consensus_manager.consensus_constants()); + let new_block = db.calculate_mmr_roots(new_block).unwrap(); + test_async(|rt| { + let dbc = db.clone(); + rt.spawn(async move { + let result = async_db::add_block(dbc.clone(), new_block.clone()).await.unwrap(); + let block = async_db::fetch_block(dbc.clone(), 1).await.unwrap(); + match result { + BlockAddResult::Ok => assert_eq!(Block::from(block).hash(), new_block.hash()), + _ => panic!("Unexpected result"), + } + }); + }); +} + +#[test] +fn fetch_async_mmr_roots() { + let (db, _blocks, _, _) = create_blockchain_db_no_cut_through(); + let metadata = db.get_metadata().unwrap(); + test_async(move |rt| { + let dbc = db.clone(); + rt.spawn(async move { + let root = futures::join!( + async_db::fetch_mmr_root(dbc.clone(), MmrTree::Utxo), + async_db::fetch_mmr_root(dbc.clone(), MmrTree::Kernel), + ); + let block_height = metadata.height_of_longest_chain.unwrap(); + let header = async_db::fetch_header(dbc.clone(), block_height).await.unwrap(); + let utxo_mmr = root.0.unwrap().to_hex(); + let kernel_mmr = root.1.unwrap().to_hex(); + assert_eq!(utxo_mmr, header.output_mr.to_hex()); + assert_eq!(kernel_mmr, header.kernel_mr.to_hex(), "Kernel MMR roots don't match"); + }); + }); +} + +#[test] +fn async_add_block_fetch_orphan() { + env_logger::init(); + let network = Network::LocalNet; + let consensus: ConsensusManager = ConsensusManagerBuilder::new(network).build(); + let (db, _, _, _) = create_blockchain_db_no_cut_through(); + let orphan = create_orphan_block(7, vec![], &consensus.consensus_constants()); + let block_hash = orphan.hash(); + test_async(move |rt| { + let dbc = db.clone(); + rt.spawn(async move { + async_db::add_block(dbc.clone(), orphan.clone()).await.unwrap(); + let block = async_db::fetch_orphan(dbc.clone(), block_hash).await.unwrap(); + assert_eq!(orphan, block); + }); + }); +} diff --git a/base_layer/core/tests/block_validation.rs b/base_layer/core/tests/block_validation.rs new file mode 100644 index 0000000000..36b5ebdb83 --- /dev/null +++ b/base_layer/core/tests/block_validation.rs @@ -0,0 +1,48 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use tari_core::{ + chain_storage::{BlockchainDatabase, MemoryDatabase, Validators}, + consensus::{ConsensusManagerBuilder, Network}, + proof_of_work::DiffAdjManager, + transactions::types::{CryptoFactories, HashDigest}, + validation::block_validators::{FullConsensusValidator, StatelessValidator}, +}; + +#[test] +fn test_genesis_block() { + let factories = CryptoFactories::default(); + let network = Network::LocalNet; + let rules = ConsensusManagerBuilder::new(network).build(); + let backend = MemoryDatabase::::default(); + let mut db = BlockchainDatabase::new(backend, rules.clone()).unwrap(); + let validators = Validators::new( + FullConsensusValidator::new(rules.clone(), factories, db.clone()), + StatelessValidator::new(&rules.consensus_constants()), + ); + db.set_validators(validators); + let diff_adj_manager = DiffAdjManager::new(db.clone(), &rules.consensus_constants()).unwrap(); + rules.set_diff_manager(diff_adj_manager).unwrap(); + let block = rules.get_genesis_block(); + let result = db.add_block(block); + assert!(result.is_ok()); +} diff --git a/base_layer/core/tests/chain/chain.json b/base_layer/core/tests/chain/chain.json deleted file mode 100644 index 0322df1213..0000000000 --- a/base_layer/core/tests/chain/chain.json +++ /dev/null @@ -1,17497 +0,0 @@ -{ - "blocks": [ - { - "header": { - "version": 0, - "height": 0, - "prev_hash": "0000000000000000000000000000000000000000000000000000000000000000", - "timestamp": "2000-01-01T01:01:01Z", - "output_mr": "0000000000000000000000000000000000000000000000000000000000000000", - "range_proof_mr": "0000000000000000000000000000000000000000000000000000000000000000", - "kernel_mr": "0000000000000000000000000000000000000000000000000000000000000000", - "total_kernel_offset": "0000000000000000000000000000000000000000000000000000000000000000", - "pow": { - "work": 0 - } - }, - "body": { - "sorted": true, - "inputs": [], - "outputs": [ - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 1 - }, - "commitment": "723ac7fbd0fc86d252334e018bdbdcfab51f60c095d2ecbba2abcc7bded63b32", - "proof": "d4e57c90a9f0d7c3bba2a786b6a4238553c35a1bc071972e6c33386a27146f204c9cfb8ebae8c80f9d84f22e7c37a4c811b6fe0aa4f8b0cc40cd36a2aba1254b0238fa7d1602442aacb5de316eea13c17bba8bf7cb337a28e3c9b782e5bf9218fc2efd00eed4b4edef9786c118b002f994465faf6e41414701d96de95a1549557dfb3f40b99cba30dfc61dfb4fb6920c2a9ec5bab2313ccf1fca73a8a563bb09ed29be18f9d392f83dd59c9763cc06a6a7b4e88a67b8ace47d984c72b78279094e6f5f5e89fe88754ff9668f87a4bc30c91d92e46a9316bef8201eb15f35770f5a5ac11be848646b42b17b9eb6e744b87573cd7778c9a8ee1999f898e18e786cb47cc1b0fa0b6cde3d22a7c41ff560de13598ad7712c248ce2f380cfcf03361114203857166bc3c8015e1af9a5971a34091a6a79221805a9175c1f9c06a36a5fc2746dab1b8682e8e7173eeaea3833bdc50f57883b493c30d1af5bfc8219145c24c12e887dd92c403270fc0ed40ef6ead313f2197f6b2da6b6c2e1abed0bfb1308adb8efdd20c9a4ffd784bb01a9d1010ca050532c0d955479aabfd9fa5afc054429d7835e658c1c600d28d1d790a8f29ca861ce3fe9464d6801496440427f346884b190b1ffa9d6cf037648f186044349791c842c01d48b2fc54ce55ece31161c39639e2d78a88827b07746a867a5b875859012924e2f695f8c331cfabdce123498fc114472a90d5a944a706988cf553f5139b7337ae5bc135b12730f78e15a6a8d93697e8a0688f55d21f4dd40e78d3702c0601aa6240fddb520ee9eda19098e2d18b9d47ab028a970085ed2cb63a18f6a44d9cb0edceec5d5ecd7b934bf139a4d27aa042b4a3383e5747c77b1ca658d8345559f41dd8ddd29be73dd34090a6e9f03cd43cf346bf6b9385f239859965cbcb7323c6ea00c70e9babbe933b800" - } - ], - "kernels": [ - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "a8aef7122e83a82a6b8ffd6729860ad594d278a5651f77be007af054fb2c687a", - "excess_sig": { - "public_nonce": "fab511490f09850da21561405e7160ed0fdde6bf04fffb587808387f02ece23a", - "signature": "0576f44c8011c3b6372bd3dbbf6f215987c193429084106f6f25485196ee8b07" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 1, - "prev_hash": "83458471965451ae7eae37109928771512a363cc01c539b7c0e6313c0de4656e", - "timestamp": "2000-01-01T01:02:01Z", - "output_mr": "b877b3f82b4db018e3304b39db70a1e794578c90a2fe3224d52f87d3cfe8894a", - "range_proof_mr": "e28c669a06bf169d76dc414fac67690495962c8c5ba879e863d085c2f9219aa4", - "kernel_mr": "ef58a77c3d2b2d605165e3cc6af2d5deae6ee04f695ebe878bde23a31ff4cd4e", - "total_kernel_offset": "dfa599695fe03954619aeaf241fdce02b7940da4d93298b3031f07b7c51cfc0c", - "pow": { - "work": 1 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 1 - }, - "commitment": "723ac7fbd0fc86d252334e018bdbdcfab51f60c095d2ecbba2abcc7bded63b32" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "a24493361860e1e4f07e0594855cafad65374ee8d1732b4bb7f18817930ea615", - "proof": "c8d64fd97a11e469be6e963271ea8bfd09fec70952ec8413653c384f7b1b354a6ae5be4070c8911b0614708adec652373b1291a0a62fcfca9187ac36fcfb69249cbd4991a3dd8aa02f408e5c294406eedac4e917e83f47f2be18a7a7039f4245901edcadf48042b12bea6651c78dbfb22e089f26c83a8ee12f80bec55e19507172a5a917ba107fbfc7fb29dac4d824c721fb60b7e28d171ca57e15606dd25e0c0c6743d27f3cb64f6d3be4ec29d83e2d04da507c437673ffeac583c773829e0b5d3d51b91f1665e7ba71b0ce3c9f77b34e978c45aacc210499e12922f38d13052a96fd60641ecf133fd2d070dd3009e37656342d2ba65dbb6b9df1fe44e1ab261c0c44628bb4f9f1d51edbd52210d54537df750f20110c7fb4c11c7d79213201564f6b1f0337f8842a4dddb5ba0f7e8962a892827316e3e111d196a5328c371a681fc48743b4a7ead1e26ab0e2c7aa0592b515467f26d4117ea8b6c572f30405dcf13351c0db8bf29673f78ba518a9e4a46f90a7ae2660edeb2df82acbd9e9492802ca3a9c2a5e2e23306783111d247d939d78223a01d01c7abe81c5b29c7f609ee583a4e212c69390fa57f696f7e57b536f0a4cc7db71ca79228437ae424e24fcec70ca40b95e896ffdf9533822bf3c3e2609e3a2ad77aa96b7838541edce394c21c4288c447abd51e3187fe39aac3dac944c9cef38821a3741bfd1305d6322d645c735775d96c661c293281126ee03a40086ba3950860f2a36a689b519ae2c18c31139a5ae3e56c1063cc24b03c43fa4c46eecde8f274a79a02e16b4b6da51c2dd89fdcc27f30613247324e2e89e1f0594e7266245124586c06d59012f0f35ee4f4b633719ca725bae3c95d74d4d751f775ade16fc46ea3b688fd45be2520ea4bee7c6d2e71328eabe5cfaa082eacad775bedfa6ab5014f9544918e7aa8f07" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "bec1f1a8e990ef62d1f3d4d6ec62a9767c850b116807147d7c51d131bb83b439", - "proof": "fc4d500a3b5eed2870160bd444824ffa903acb7a170faa4b51937f33e4afd153ba2ec2291e009d0a8e62a118597b7f63aa301084c2d316a20738765829c0970b5a029664802d9fb5791e0bbf270249161f2afec6dc4c816fc354bf7c9ebfe71d5ae24ac181322236e37314ef98c9294cc1bfe9cc3e78259656af9eaaa1cc043c16add88ec6d56c2957ad5d28f9994c41e507a4910fbab36a3f303d4e89a23f083c64d72fb27d238918e0a8de90716eeee12f45f2390f39f3bcc5ba3786f38f024ddfbf10b36229b046c8e70d671c7a9c366ebfca195aa4f4b87e7459fd362f004897985e3e9db0653ef4f560b6f6fa8637e1ea5464a28db2ba4744b3b780035420acbe451a1edd651d56a22bd2fdaeff5037a9b74155ddd07986edfedc97ca7382a9516d8dcc5615607d46bd39b1f8867004d614b0bb1cbfba77ec4ffa81254cd0f1c792a4446125662b6d9281284b36ccd28fbde09a01fac85496a053a9b95352797e9245f4fde122678be84313c098c64b554fbfe786ff173e04704892424b72039e5a2e8d1ee91e879f335fddda5e552311e1e0dccdc4854691594a23524cbc3ec43306bea9d4d88a99a6b4478fa175aff1bd182dfca1415574327cfc8830c2893ac9d320972f92056c78a0e43a5b163acba22e31d180ad11ad41c8f15059e41714cbf567735f399cb62b03fec97de115ad9654e2e72acf299b0aeb8c0464564e6dfe522f0e5aa77c9822a6ede43f8ca983ea55833672f77d1eceebe73e4214735b3a9fb6e86513f0f547f00fe6c247409438b5e8a9e934d11db36a55e054b67ee6bc2a216c3d63c329a30565c7ee60b45e59be9d20e16f1d112ed016416eb7114b0563748564ed7afdf9bdf2ec90d27d5795adf4938c214428a1e314be09ce09a4cf0a421fc3a1d1fb31b76d62fc2eac9483c4abbc4a6fea0b3896a92601" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 2 - }, - "commitment": "fe96e82c83a455693c4af5a30947d56f9acc505c14c3a4e3cf7b48cfaca1f81d", - "proof": "b668d52ae93f59f0bebd3388b80860638d15f8d191e8ab79d644f0bee6d5574a5046fd7b262e471d49fae0e86d50144dca2087922426e8ebe4bc9f600480003bc6f2020a297d53ea6b66ba177a17635e7e23b6d6817ea25da27e1addffc47c3caa6654b5e3f6dd069e799898fa3b5508bc81153a659176dcadd0921e7e2d3374664cbffeae4c876470126eba538671fc889b973bfc360f04db615a85fbcb4a06f299cee86fc19ad400fb26379fc98eb837dbb3f015e68f6046e0999419c1aa019f065cbe68d72a2fd27c16f9b19bd53cf8f5a8aff90674d063c6b914b0c4b70482fc83d7b5023cbbdb8b19bbe1efcad8ec67005df2c4cf6f5e64d687716b855a24980fef56078280774ead921bb7be03227ea70f058223b0e104817e0e32e82196b245e39ec26f98fd3708622e011ed3f1f5d34aa197ecf615a148014c92023186693432c4799c756eabcdc6e6eec678fb9af80aa10a79d6e8e52641c4a57433ccda01adc75cfcf27a428218de2bf5dedd6136f69bf0f8d0d00963d9302962438ede9ee6979527a186dddcd88ead0fd1243ba7b7d6bbfca0db8eaaf2cdad3f60323fbbbcb754b564d9c60fb950a7761e55955fd88f145675e694ce0b3a78f653aa14e63926f83d91d6d4774abbb2dfd086a530df7eb94ed2ef6728f51bb8247ae8ca41249163dfcd80103c1828a1688d4c7516e844550a42482fc3e3fae90124a65bee616797a5efeed3443dc556c045a5eb6b9472c388061b01304283062d69a06c4f87596a09e3f85e440da0d8f823b47d77120f3f698bdfc7331a5f960b37465171edec813d757748f03ab4133cf5b30be6bd0edef73c3236feba4a9c777c94598d031d23682adc0786fd2b44075af3ec40e4d73c3ec9a323bd0a3550f107fe4f9782748efd953934eacb8394fb86beaadaffe0f0db7d8ab518fd57d9d504" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "842749b4190a5ecf9a9ebe24a4dac81e85c1288f25aed5cdf77d289b12da5340", - "excess_sig": { - "public_nonce": "088344c69f41af851528394fa1cdef493f43a50807aabfc08b9015b8bd9f2f56", - "signature": "2411f6298890b4a0056252ff27787a080b5137a0d556efaacf83abd20920e500" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "52bbafbb6ef01004cf708cadedc20253fa4b4f163c6381734b316fb90aa2bf72", - "excess_sig": { - "public_nonce": "54375fcd1234ff21b1292a40918957d33756542b4a45787570747a5141726849", - "signature": "c04373d3bc9c623bce422faea880a93aa9d781227473dd144447b61b1df6220c" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 2, - "prev_hash": "8ebfefc40560cc1fe6c282c1458ec3482efd19c448fab8519d28736ff4f26496", - "timestamp": "2000-01-01T01:03:01Z", - "output_mr": "35cefa2f5dd074f8cabdb1953b39fbaa8c6c5014f0ae3503835e0aed0d68c857", - "range_proof_mr": "df17a4112249cdb1f6ed3c21c6639e5bf5b1e02edb7f07210bf53a27192931d4", - "kernel_mr": "5010f4f3b3f7b14429ee1a3aef826ab82ceb80e145e3cd544ab805747d4ab909", - "total_kernel_offset": "23ecd2fb0c82a06e354d139ae340d05eda27fec662f2fd0917fe36c3c61a0602", - "pow": { - "work": 2 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "a24493361860e1e4f07e0594855cafad65374ee8d1732b4bb7f18817930ea615" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "bec1f1a8e990ef62d1f3d4d6ec62a9767c850b116807147d7c51d131bb83b439" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "1852bc4169774957861763da779805844eee814d5f47f6a44470b7097bf4db50", - "proof": "e2c62f31c65ebf6f31c5b6eac2e45072d2daf3fe8fdd06cb745ad2434f4d770f8a07c0ffb803624319e843f235ec4b81a644097ad2d650123ec021b772235705f01629fe9e8cc53ea8fbb81385c1bf4e9c015f878e82587e20b4b92858b8725efc3b99cdf2b8c2c8b607360af5b43a668dae2f8eae378f235114c68f3e981d734d47077dce78bbcff4f3661b0dd84f28edc963785b0962852dd421a95c5f7b0f396b609f68be35fc75b6699f64e69122bae0fe9ac79f00760e69aa115d2d7d01f86414683b5a5e123d8be531b0b45c5ee7406b04cbb406cc570c7070eb0c9c0ef2fa78fe178f3124f7b8b1ac76322ccb11a188540c7e0b1c3d7e6e79bfce0a0bda2cd53a8b8f1655c37c59d7c9d98d9da2335ebe5d72b957d672a1b1429f0371703ede82f24e38fa9d273afa45e51d5bd8aee7dc0d3ce6b6fb8fd9771230560c3ad1bec9ceae1a0b0633df0629322b2e4bdb63da902ca952d37cf594f8199f403023fd73cebc5d95b93648a15d31e33e3aae40f5492d08d277c1d115e6687d099e0a1e7ae886047bb9c3f841d5d4ee35de8ca74b36b9e5f07c89dd24bf82560900600a0b35421c5e2bd35b05fabe09ab29da2a65f7560d51c4e9ad1f5c2df7373a274c7a9bf0a0b45f05e60aa855a7ff97c638b8adc0948efdcc1296f4bb666b14834acc254f4c0fd956a7c8ded5dfc492584cfa9607f332322ad33486b7970b124b7a9868c13c8648f4ecfccfd2f7fe89193260f0af06c41ec60d9f6c88cd635ad574b6bf4075b5763b5f8ecbaa1a4de24a974537af2acb0fe39b33da410a0c4252c8dc28cbf9edbbedbfd1bd12f31f0d6dfdb0ef4602b504bf9d1e2fdd99086c21b272af99ce9b5e139e0997465fa78c6e4c29da913816c108c0e879e3d30049b60b4657a54a717f1961ba136adf499abd8951f40cf379265e7a9ae23bcb07" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "68bb408d0457f3fa265424b9bc415deae64aef235b055666aa9608dc21d40869", - "proof": "e2b38a9533c9578b788d0d57d09601d34021d26ac54960c912f6b1ddb4f38611281ccf3da59c551e6e766d904d7268c42d9118dc4f3d6f93377bda8020b5bf71ea422e30b6d923aba3e45716fee5bb1aad0bda6317606fe15c4f3563adb1754a86558eee8738ed2418ce889bf3592d54ccf5cbee0183f42c52c8cad45eeffb656eeeba3931759b563bbe1e0b6a8ad289a2a2109b2680723e9848c0f498882505d9a0430c612105f95cac87a26b24c92811cd8659b310d9224fea49ff912fae0865694d927aa0cf384bb020068fb531637816ce6e32080124ae910019553aa103d89775dd14775338314b1cdcbf40129f29c94bcbfb5a87e8e50b08757f5dd450bca4416281b3d46141c10e4397001b87318d2ea63230aa6e8062278cd865981aa4ff6110ed165288565e2ff6f29f0be37dd387be61a2ea79b95e5de81be3b40d10e3f7b581adc23e9fffd9c172bda2aa30b008a0eb4125562e75f065c60f102cb65ef396dbef125b298d48ab283cddf1287becb80db137f6381e35f7b10cc123ae8f976a8c5de2df7c6b27219829c9b31b4f157b755ebd8de3eea1f55ce37b338a29466294a2da9abdf9c6f162227cf6ee70ab147c43a75fefd21112105cde06cac5b0bd2746fa8bca62ed77844ce2bdee4fa3bcc55a30c72b01fb45205fe606b673bd7dd236747a27d4f8bc22886badc7a62c072e43105de31c2471fe4e0c0ad86a80c211f58aca053bbef8e5f92bd1c8504cd31d4735dff6b4dcf66c2ca562d04b7fe7f0e3afff383d0d73e524ae9f981ba0f9cff5e7b7e9d06d91445231703c15b2a8b1f1e266b2edeef1a023ffce26b0f95777f34a99d5685930fbba790ca4dcc0f9b5c8614ad8bbdbaeef426ebe5bdd79e632855a25ce89c05f9e891b01350ad58dcb6e800853f7e49440a972cfccd9dcf1ce8735cdc0a1d6a2fd843103" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "848d2a82e660c82b7895793d1da94daac70243adc49bad561bb394c76c063322", - "proof": "18894b869c1cd159e646eb1176cc8fc442ea27af10b98d3c7320d89915bf071e4ed9f1d39f2242e86e0ff21e250348ebbb14b5cdad7d1d45a19281a32778483f8e538ebe643ce1390f58a777f99f07085e0f8a6ac09e7964327802454d20b36e482b325acf07edbce6acd8f0fa33838abff8120c16f8a796ed4937242e6e283c1297d24e80315d1d69331d9853558632761f4d69c438d3c4811a5bb2e9a9840b6bcffa9ac93fd901d3b0be6059f6961a5b4d33300d721834468dfbee9df12d0f4ec4d1136f023a55972e69ed49344912056711ee88650e0348c6b6decd54470f1e83f3627adec75e7a3c3137298fbb51b6305caa1f55b47e393a65e6da63de6656c2d780b9ee6c0863be989a6a7cffe9a6197d1098848ee2e546ec5337a77c0ec670c0a4ba61c76926f780dc0a641228b90c3d4323b0a45a6e7ca78d87463e5c2eabb50ef807fbdbf16355a071dccfa82daed9b7fb8ffb2bfe1bf79eab48250e1ec655411bbc67d2629dd38448d41c22bb5d587d9bb6b9d035a5fb8bda549435c8001378d5c78233616d0874a25507aaeec2094679d4cc18d582126a21bcaa1a86ea0c7fbd9b0bbbfdf234b561f56be0fe91374ecf1de5589e3e9f4fc7df6e2ae04abed24dd8cde860b4e855aebf0f1512c087da95cb24e6e530ffb8fc675c77883e53d14297803def70b31734c56fbd691281f8899862203a95f4de54f0836f5cdd5e5c63fedcf5a325202121d0a0b1bb24e0f7f81764166a3734ebc77d0721e6491b2e19e1d70e02368b62df5f38a32105bea2ae9ae6f36f821683e13a9230b0b272be8cf088c369915bd2d67ebe8f218a6a85c455edc273f1f689a89b4d5185cadc636cc6bd3096390cfcdb5a8d30a02cbebe3ea7c7b7e8102d2453361305b697b2b735549865d2d28dfab2b1d37d7d873c149877060740dd351fabcfa806" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "8e1d2f1cd95031dc33b3b9e53faf0d54dd548250c5e48a8ae2c0ff9a113f0a4e", - "proof": "ca67a8f2bf1871f6f4816aedceae63ed7912aa42323ec39f790fc4b0fc5b9d29b8696ebe37b1f397823dc4fafd819b098a1f0694bdb489e6b72231abe1f73642665b111f115f6a752f2ba0d8f87173f516c93c1cd449ec9b734884228580ea3f860fb5b5a500a2df85134c0b649531f8822893498841d2947f853f754b324e7640a4dae71178e8b4b61062da233f9b4eeacc33f73bc909cc2fb34273ba736002b907e16d5c2b313a8fc79ca2162bba4eee0e890fe4aaa1506749be346ad457088d79d3faa1c6e7a3f4923d1351e5a9f8af3b80342da90b3e4fd0f85bfaef430b32e65caceebd64f4f0a26fde345191aba308b2bc72d0978c1db405a64d2401397ee7bdf1f75fda09d631ab430df30612b202d571c8a15b1d59aaf326ade3c164284470d0a61acd841709e4368d599bb34de9666cd817650981aba89b8236d65f7ecb911e7bb152b5bc250466fdee76d23070576290a12d6f8df7e1ac89a6ba4548a3b7b94e4d5cec68060e464a5c95533ff46da147e32c15d2adef4d8a98d62410793fa8de683067cbe1d14773ee896f6140f0ebff84f5e24ecf65279438e30a7457713fa3e3d748f68318e53f07441812d71453e7468f68e0e8c68721f546686ab193449c9f9f10dfdc64b1b3dd6189b3e4e8f077061685ddfb6657760e12552219b55643c3c62174d9eeb5ec82e8508cee8b1fb472dce09f3f1dd8e51eff518e04c212abc678888ea8ba4825e671bde1f1fc70d7ffd8948b28d94bc4ba34201cc5e8cd442eb43f9599f9db06b0ba242a3dcc2d7c6f779679a79b365603f91e201027a5fd4e9f5acce220eff31e39e943845dd70f36a6f93730fcfc6c8a217b6bbe7b4486a65f91973b2fdb552485e7c944eb31268c19376710d4b7f6f31906fea371d171c543ec6b46a9ea27f6c6e2ddec5eb4c6ac88e0530e4346d7b60803" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 3 - }, - "commitment": "1c74853689e34efe8ac094362c70ced414bf0ff072eaafb9cc8985aeba5e2809", - "proof": "0a5fff810276295981435e941957636b2481ff977df351344b0c5f6e024b123136480b74f636d7639bd035d17480e8fd74ed5a3e93478d2037679c6f7911c41fd42747e98a03be622e429b8bee865b63ff8f6aab5ad2353dd33cdb006d193170d6de20795d1d69cb989ea34c496386f3f245dd029b05d7676ab099fff30678372dadc1165b7d23118c900027acd643387f673b9974f3f1f8ce436cd8982dfc03542fdbf5bdb38257a0e4a0d19648e784bdd47f8e4e22a6b22cc0acc7b00f200ec5a44c14e6f5698dd66e01e75b50ae88fc9f9357ed5af7a18387848f328ca703c8e48f3915fc098a0eb466c1caba70b055d440883b10dbb3bf42c5dd7caaac46762bb38b67ed919fc03920de97beaa653f4cb7ff667a5c318055371c74bc27475cbd8c9939ee45e9f9db6b2f8819d00a9d644e417b64c3a41810b3dfa075d50820916f6b2acd2c8c23171dcc6b23ebba243c0d889dd19cbd2e7e20bd64e7e87a30620f46227d0df518d89d712d53c3996cd211c89e43abf0280359f3db7efb27fc5180d4b8db640ea8faf1287eabfe3f554f177c87616d6d700a75c47bf9215758ab1bd04c2e722585abec408992d7feffb0ae4a0be5c13432b74b53f06cf27792daec720230b4adf942e580a3f11b0182dea83081c2c67d9dd7399fe612613e1209a374e438741f010c19c85dd34ae36bbfccde4e1e9e755b8c8328f896a721ded79c2c55e96dc5bf68b73e6bdd17b82831e3d9c0674eba65f8fd0b5e43c97c64399ca46a69f7e5cc3d719ea5433847acdf0e03cfa224ce522456e60a0fc92dda15ebc8e88a27e41aa1927099add5d1a7cf0c5527d4d931404d2171b31fd019914f02558d9921e080f890aeacc24ee9fb11f48a6124f3beaad5724621b83d03e9592df104d4cacf4f837926f0bd404e31afb158a51e290938d481fba50c2704" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "5640acebb0d5891f97453a3df0ade330a4f824d34cb48627797760c9bcd5c41d", - "excess_sig": { - "public_nonce": "eaeb0b0919e335051525ed3e6d13aab4a4a0b3e3a5e5e2d510f68c239575fb0e", - "signature": "e3e31714c0f924e906ad26c8fcc576df5c82105b2eb1dd496a38ead2e2922d08" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "ec0f4fff46817d280a24cc8ba554801452c5a887b90a1c5ae20c27f2a6ef1c4a", - "excess_sig": { - "public_nonce": "309e80eeebe26d001f840bb10d79d2f32f7d36d9fc1e578a94f96cc592f4564d", - "signature": "de5a0ac920ef22080c97322c46f5b0cc5df666afe45f6d872b69f46ddc2f7b08" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "36486999f9aea343778505a15e99267c1766792f63fa5673e57bd7e195db4b6d", - "excess_sig": { - "public_nonce": "641eb569feab8b55a85ad9d402c45c134ba149b70c0974147108143bbc90bd6d", - "signature": "9f8f376614865708bfdfa6a64b0282f4260e0ef2c2ec6e7e91bb003d09ac8802" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 3, - "prev_hash": "035747f1ea39518796f2275a7b759f524c66e61734550c0b933ff7f171e8bc08", - "timestamp": "2000-01-01T01:04:01Z", - "output_mr": "93f1c24445f0746c2d2177fecacb080200f72ecfe309bf0d79f8e308e52b4a2c", - "range_proof_mr": "bce3b6a9778ea8e5ff6a8cd705ea7107534b00ff516a04ead40f7e29f6a5fd12", - "kernel_mr": "f0bef3e3683983ff41f885dd59c2a08c3a66033e013c03b42f7999e3567eebd1", - "total_kernel_offset": "e427904642d1c911e5d3b699bf883ecc58585fbdc4d5b2e3c403284665c6270f", - "pow": { - "work": 3 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "1852bc4169774957861763da779805844eee814d5f47f6a44470b7097bf4db50" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "68bb408d0457f3fa265424b9bc415deae64aef235b055666aa9608dc21d40869" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "848d2a82e660c82b7895793d1da94daac70243adc49bad561bb394c76c063322" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "8e1d2f1cd95031dc33b3b9e53faf0d54dd548250c5e48a8ae2c0ff9a113f0a4e" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 2 - }, - "commitment": "fe96e82c83a455693c4af5a30947d56f9acc505c14c3a4e3cf7b48cfaca1f81d" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "10819ab258636ef2c9d4a8792998de985ea5370b8f818a817be9dd4387251b12", - "proof": "c4d088d622b16c97e84c5122ab885c30b444b5aedd8111da61e5f871c3a22c0244bf3a2d9b8a8337acdbe85af4a95355407801ca8926bc72d6bd6225d6866b166c8aadc47273c38b4c9852d87c69eb046969684d9639f62f110b730bb1d5517f1c6394c1132de005bcb1c528e23da8bc121a3f38eeec030a44acce4b3a5fd665ccde779ccbac83cf1038be90070d7a5d8e158efdcc91a061b36141dfbab9db0a68d82c8067f2e23f8840a0bebba5c90b2c545fb0187e2d95bc5cd4aa5c65940c72117caf389b5c48cab4dcc60734ee37ebd5335c8e0ea2f83aab8e9e7d16850a1e5c8e04b0585372008eb418295bb2d8852672c7fb620d00f91f8c793ca48d57347fd9c7f256d9b5f1b0844612c1aaa25229fadae217217fc4b4a77942c89e636254a7841fc58a559c8656ff7283814ededb725d8e0805b402d79612e5b9e3204cd7ebecc586eb21b70d4e7a7dca8c5f856b05bcb68088f22a6fe6eef01f23752c8e1bbd3a7b0cc47814df62c36b0bff2c42f82f6f5fe221b21af7ad4770175ddce20eca0c1d101a4e3fd947ca3a418ba79a3c3f7047aa52562b123d05103b7470daac8364736f477664e645c959482f4e13c1353245ce79b86bb8c7cf703a5f5058322d27779463cb0cdd3ae92e53f1dc7d1ac199a0be068fd3e2bedc8570006e7141cb768678bfda806f8156815669ccd212ae2ccd67c28f4d6b047aaf38729c7e1af639fbe0bf211e1dacbafa8eb0749625b508e2002c246886baf9213e25ead342275aa8224f0ffb5ee8a502788d50062457c960792aa08ca2900ac20f6494158371c988dd4f9bce48f2931a6dfa388744a9b99b3b62a80c8f212d36724b632332636662e965297a1bc0576db4411f55eb81c9de3939825e8a64484f3c023cff07b45152f341e40e4585febb5ce1d0c1b9093cd92c4674ecd3fa9989ba04" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "2a5cc9885cab5f1998534534f18e2717be1fff00da99e1a770cc04d995268c0d", - "proof": "dcfd86c5fd9fe9b1f116c902bb59191ad2e874088ddae52327d2cdd10020967ce67a0e15f0d5898f560897957a96f66a7a3ad14cac174ada35d067cbc5675b204c63c4c9363d05092f7bb76e0974226f2327d74146e602bcfad46a92bb94c26ca611e3ffa10029d41af5e3f3ce41f7ae562ace2c54a1aea8923f6ffe809f852f2b552bb90f020366dd4937e4e42a4108acc2e86b99d794cd20c77089a40aa701e4dcbaaa06efe44b55dfdec7eeab82ded11a7698eff513428bfc75ca5fa1400a5394790a9e29674dd222e328dcd6ddc2d1e6c5dc7c34812a820152a2d185b8023a568d77f90db84fac29484df7cb29c0178e16c842a78b4fc5ef4f0bdc030c27840c0fa11c2d8ab8b5b0956c8b523100f7d77c7168416fc2cc52218f6cbc6c184c7f0537dc8597d541dbd4af655d12382df6b53c0a4432f7cfb1aa00abfe0e3800e8cc50900efde0fe81628c135622c4bff40a08beed49f1014e6fac1028dc0714c7d9e4faf6ea5e4e04dfbc2e542aa78511272a63c3cac4f6d3f3abdafc9a5d745091a61b961363a9f44175ae871583d7f707c050cdcf3cdd8d46452d8b260fd2d9a89d7def97750762857fc944323a8e66cf8331606401b446551e37b38e3b207bd57e5849641738bc803879070b93874e129361928bd70e8825a1f7fe3c517eb84623e00715b7a37abc3d3cf7092b0c3361246fa823cbc7b00e4af9f3b10d901c6a7e25fb3382ea7a14979ff7517862e5bce4c4240e3852aecda6e8e2995f2c843fe7e70b55e88decc79748a4ad19380428045b36fd0dab2152d3c67a25462670244703d8e093b6dd35f6bc07597f1740d44ab552611cfaaede536f8f2c69756e0186d78652fed9725abf67864931438ec3a2a1d236651b0124496b89740d952a2a5c1556d70f04af7b28bbabf07ccfdb52ecb746a68f97b3b8dd388aa706" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "2e3f9e7bae6fd6ff9c3a749d0d78904028d66d075acf11099213dbfc151f8b73", - "proof": "901e376287269f7b9419c020e683d7cbfadd02e7eec2626864112bc018d8d278a0f392844c3970402dad603d2e2bde4cc6b5876bb43cd4990ff6b65bc7fbfa4e1adf8743e8365412401b86798da2723ebda43b37c26a13dc76b9cac3a1bb036496d185e87e77b34f8ab843f3354a6396d6ba204a523875c33f98f8eab91b516b7b9fd63b94a47743bc487d70576f9489d2243a1aee33e3f2ddaf5d0fca4ef70929cc8501c5decf33fd4b5b33feb9230f09484d2bf79efe549950e3f4e36a0e0680c00dd1eedb5a62fe94e9eba904ebb97d84cd46c2263a932e857a24cceb070f1e23470e67f1ec822134e2ac81b569a33e0dadf749329d21085b60c0ea057e26ec9b61091fc71688f41d928bc7aef13856a4e0561c0d41db2642567852b3ba0c32b1518df8f3b838dcc0f68f25cbbc70c17ee67affdcacf3388d8621c8fd4348acf1299dcf48649fb46aed585d1fd7e6c1c0d460fc1830610df40590dcee482a26405592070f7432a7582a0489f51da645351bf54bb6038e3a09d8b1d1b5e33ad8e45e97aee51bc6ba059b223024fa7a905ce2adda47875dd4bbe0e6ee8b8d19c60389e0ac29f0f630b9bccb2fbda7ca2b1fb300df7b26cafc795103d91c744eb897d665cca2884cfbddcc2f791dbaaf776b3e9ebb4bc948c914984ffffb25519299dc93a0a17b571f07e36a73257d72c988b233f1cc15fbf15ac97d007a3f778c1622014a867d75a0381d83c36131f7f6ca7a1002f277b72ada688a556bff7c1c823220f52d759a7cb8affc93499710fd565685da0961fbc4d7e005e19d407a500abad82f055b15c7bb94511a3627a892dcf924c3e0d77b525d1adaea8a8972072099ba01c6c1f92e38573ca2fd1c84c15772a368fd670c75b23c90e8a17f06f921a3ac1e4fdb41bf117e5654ffb59a2710c506d38fa01b268dbff46a46850e" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "4c181345492db49f5ddd592099793694cc8e9e7783c18295e2e5ba726488e95e", - "proof": "bee98659c7f35d913312258dae5573ec9f7436bd3d5c81d7b382b6b225458c70a61e75d24c5f091632c272dc7454f953ad822536b8c0a3aa0a40e8e552a16a1956b17c2b5203852a7c6cbc0c93eece51d7b47cf7913f6cea179b76734a6aaa6cda988b0a75e15cb3eeacf2641fb0e6bc29b6b3ac506429ba6f5d8564e19c4e3a3d70b91045281f09cb254dc8e9e567253e86b36dfe8c2a01ce7f53d0ed6c9a0e99fa543a282b1ec7691467c956641086fe59fbb5605efcca7b050fa6f631fe0a729dc09a30aa48eb355eead205c91005cd920c9ccc2dc195d9b4e1b8fde99d01b6f9f6471ad975832317aef8209b588720da65ccdce5bf49b9a32fc7252d705bd259a1f9202df15086d961bde390161b7937e1ff8e819c84454989e3bec18c10d8f8d9994ec8fe0f0db71826cdb6ad38b7cb2a4580d268caa7956ad2016ddb09b00c7c2cc13f0f2fbd359b8aa97582f29349e85b53bd1956b152a8c4eb14e17c564140f57e50c5473fe3dcb27a170f4f249e20fa6a4e641add85ff2713274167fc15126bc568ff12e0c94dbeae8902de726ae362db7ffa4eaf70cfb3158da669d60ff8379c0185a5ff823022e0353907010edc8818328b1ebff6a5213d08c5406eed8929f81ce6518071052cf56684cae8542ac1ed060e16339440349291cd26c84c5c9611b841ddfd312710ae3837c7e45a02c87988a94af2039a8ad5d1435db422dab27761d0c4e8aeac75eac7736d93042615169aa714f92421dfd3d4216d72128c8ee8cc89fca36489cd830a7fb6a74c2cac050f05c3e3f48a6ccccb245f24534df345227d123269ea1930c317b8e955e0d7ee8013ca1f05eec3028984166e6eb6848aa0e11c1e7f62785f8467f4d6d99c5aa9bb79283abff3ba6c47070b1feea3f6326bc71fb12d2f5fea44ef1040bb2c65899cea5dd8380059a8efba0e" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "92eb113d298ae9adc51a4e1ab84c08aef2328a471a1235d437838faf86393157", - "proof": "9e9dc4a9f233a2dfd9d5b73196907b8a377d1971b5eeb6797d53b787ea2636416c6994099993488b01c1a25c6deb43279ba506e478314ee3af4f42662138fa4ee2f51462694cd4337aa5912576b3536585ee2418ea34d3daf217e14c81cebb089e8405737001b29fb091c8b976888788fe5f8846926596edfaab3ba88366cf47bc5eac9a08cd8990502d5fe6fd3f1d3e26c5298b88cf3bb22ae8bbd578b0350e26a66eeacdbfd754e96b474d0582b9037955cf12464664a4fcc8da4ef7831d0116521e31432ed0caebb0dc8203398e96182875b966bde751346803117f62d30b3062c8ec3d4ac6365e03cb81859ab7bc982c83340d6e80582774ef65a0840816f05b4d605784712c1d267f15cfa77e822005b9ab4d747bca5c7306a74d5a6a5a62b1a3f22ed162427c0d0e2c11afad20c4ad509d2373d8b4b27740780930bc391296270b264b660352569b4a66c4bd6218ebaffb92958edf6ae8bba8aa319a1b14cd1e3694238e2c45429b662b4bcaa6ac328850b4682e8f8143b9e5378e3f7584d4a9519f4152d9f88596c0b8608eaa15b72e8aad8dcd9cf4af45452ea17b2e4aa440379300d95e1eb8d9bfdd017126e10eacf30a5df9f8f6aa028695c2ee6cce1b8e063795de3968f933d565b7d578ce7026b7e9a333b3bbf1b171b1695a426e1be7b5685470c72496c605a0f402b3c6047b3b6a436d806e2070dbdee3690bf000500f78cae28e31385305dbcdf24febd687d01d0b70c2b1e62f742cb32623a62c0f147102a7b41f11a428b206daadbb7a2af5645c8e0f95a013a59f32807c905f7bb73d3a3f8518055102492fa7d8881b9ab8c92032bf1f68438e00975240641b918d03faf61da36fb4b2531024d09647999c88eec83835f4c6af0767480a7b4eabbefa105d90e09a2e1917e8a6ddef6e382d73e0e3fddf78220d89fb340c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "9ef08ee542586ddb8fd9788d261d9db4e32c13de733a33a53b04d730a0897939", - "proof": "1c19702939a86be9c122d7059f5dfbf36f0f4a5f56ef25ab96f4ec8902014e4f8a8b24af4f84ecd5a7203159aba8bb96236b85094ae0de3beb046a5c5c1acd30b608d7964d76a97eb69e6d9a3fccc33358258a2cd329d9c8dc5b651df1be426ce2986a26bf5572d7d9cec7e263a067bd0e385b4d2e40fd7423c08ff17d3082356c8a48790ff8647388b4340ad86f951f2bd65e823100f1e06f2f9641fde3540fe7da65280f46d381ab79062ad004ffeeedccfa3bca5f59134badaa5d9171680a4f31d0c470196970677fccd38f5630f4716cc5a47ee392210bd88355bae4ad0b422689e7eb75ecde3895bded9c6df8d63a69d5c28c286b66cf1b00db1b46496622af6f4637de035db9cc4bbf368f55872460c648e4c13a4330691acd5220c4442a171f1f5e07e4e4c9f0a81186184bc43eeeec31121155fd096ea30a85d4da57c8585aa9cb488c8559c6bc638f9e9e4c0a4e123d37688b2f91d350e80e46863990e5df4133a19bb9b11115990c7474e2f8d50876459dd8c889cf2328bd87925118a4898b19a4d1e12e68967da12e1bbacaa835e2e724b4d060adb6cfe7970226462b3bcf5da84592be51406dd59fec35dc2993875e3365d8586038ce5aa8e96d8a5809a6c9900d3d70a3001cda60aaed3f477b8acf5a76746409286ef349b13946babf263a328768a0e07c96c15416a01d103b98031d7f0c590585b31860f3421282bb4dcd7cb794b6ab086f7de63e2c019fa3e9adea019913883db79c40ec7ce6166a45400c5fc464dc0e47116a8e99370bfa7543a80761ac31703cbded897a666a53386a08bcc526febb692906dcd461ac8f9b383db6336caa37026a7d78194969c073b09289d10ee27db7c9f41eb2012fa954495611b7b52847f51a9e3007593491fa247158d26c663f50bbb707860215d40e141f6c11d8e92af9b2ad7104" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "aed9066358d21b658689b6c751f3be49e1aab81cd7f410962aa6ce17e493082f", - "proof": "74477895d572d13abdc8c37f3e4f066c5e61d1b713521496499b3208ab57dc12328b4293e04c0d3db01070629baf0967c484562ab9b5dcea7d48a980386f924b224ff9297dc9ddc86b90dadb0c843c8d19befa061e2fabc667dc5f04b852cc2ab69b020608b94bf0a103d58df433741efd61c1b1311bbfcd476f9fa9fa176641fcccd055e9a9aae5a13cd19695afc64f1e1ccd2302fe6abb6a0ed167dabd330d2c392794193a851fd17110fcc4d14b0dc8fb117fcfd449d90df5f04202d0b1050524bbfdcd35dee4c5b1ec8debe01490be3c894746b456ce6dd300c9e88d9e0b12c44cac1803b8f0ba64cf99d6dc7e47849ba93eccbc0a25e5a8a742f8b12c368e80f80aa27463c8d1a73e41e9c1d3e3723e88afb01c29587d7c667c1d560f36a8bad080953f51d4e9f770852982f0c49ce54479015775ebaff26188f6aae35492d952f70456b27d19cef4b4824ef804ee96add42b6543cb1fcb9c9833588221d8e3582b1d15958c33dcd2e877af8871529a48e89bc0af888629a3ae6bdcf129420d5dac4dc293c5d289c319680e7a6babb01847f762fcdaa6ad4be34e60cf18f291f2c39d6c17181c9bb0afac3890f506113577d14993398dcec12a4def970978daa45a78627408e1b503c76ec40a313490db53ba3de301086cbe4252729d62de63c082cf3f3dbe8e3fbd3575452b113204588141f535c2c54fc4f4ae8f1c1a668d9440d54d1ef2cef792d8263b708e5694f7992669c5d975c22258685ed01fec73237b06d94fd8c1ccd59f3ea3a33009a2670cae7abd16d59ca135ad9b0f702a59a3c9ee8686aac51e23cfbfbd7fd56b10f0d5fc7525fe11d1cd578e00c444782f5fe9dd581920f992c62ffdd6f4aa2e23a7bfaad734000c108ba77f205d0e8125759c91d4e7aed9b7b38b8b54ece0140b4e174a5e2d7ed7d19a4b63a2d601" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "c4fb6e488d26b799aa118ee7255e7281279ba2facaba1d46c24fe1b9f377de16", - "proof": "cc1367ddd246d66e74dc22b933191c171925b4e32df45c6b816350ab8377bf46cc165cce310ff301bf8447a816d6b4005a51d839c0648849ba62945f1389b63a8ea61cd5bf4445c5d797b733fb87810a7414a5792d2bf150e78178cf8f94b015d8ef9591c4410c0e1133e09c2cca359b72662663ae29cc69159912d11abd053ecadefe35da79207662542c88cd8889a7cebf9420b69c6263268d8ad49f0d2509d6086fc2662401f19716719e14af456329c30b537db780a490255bab1b212208c88b290b687aba78db1cda89c19b148bf7fc81d303838f9d36241860053fe906feb6f09bc29a6dffb63c6c216c3d9f09e7b51eb7f6e066bc805558ec4cd5da3ed2bfa251d51c6cd06fdc3ef890da8cbe6e9faef52292f21d6aa72438ce3e7801a866cacec2279c71afd8f526334abddb3b99a1fb2b46876a30ed049aabcdc3355045436e0eac7a28e9bb33a63fd1429132db593e4a08daacce286611b4e62f401261ee9e045c8b0b3f06c22a02792db540c744f679b3826afd3d8f427e5d795e8415dbd3a74df2d44dfea005ba31ebf9f6bda8d78010cdf8613ced94b6d11b69c8ca015041777f5c5611e68e6024eb81b071798da5fc6a136627b9eb1cbf8a4018ba684d35d84099824f2c25bfce1e4d735d2ffe4b576afe00f63616bb6571710a95f29b0def498dbb193a6f82ae3f02708cf39335657db6f995af03f390f34d6ed612fc17976168d3e56b0f317d40c2cbf8e99b921c0afa928a67f72b169c27f227ad000bc177d55dd415bf998c21dea37997ec17795eb9e45c70167a5c4965941651d63824ad289c445f4385ccb32be2a3dc731a9402b91d42eed1053d0d27bda46fde5167135f262ac52a1bf0eb3af2d541e6c098476058f84440d342090e004749a42ee3190c194d98ae7dcc6e90e11a090ef15d822fcc0acf01e85ee80f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "e6bb327c4d62bb172780a6ce3c557afd8fe6b4159fac302ebd237f4204458647", - "proof": "8e6096103abc762ea9b13720b973b279dfc7ea4700c67c8ce96208793ca89f6c4248c74790e187f1109d0191e38b7a9ef61429b165f21d7603f1ac2857ab77724aaf2059b3d8318c0c5fd00be303d16786907f395f8233cbcee9ab299969aa08d88ddf73243c207053c1a759cb62036461e947ac12a20d8aa9012b1be983da2093e5bbdb521743084bd9a8414a0c0569e181343f0e7a0a546912c3f5cf25510eea2b40d641bc4efcef366c87f9260f2e25c3e9119463711820f944cc12f36e0b841750ca873d588db1274c63d31c944fee6acad01dd4cb972c0f9288798d3d02aa627a499095606f9342fa33f053f19b6f40976daf19d87b9ebe47db444ebd0ae44b277aa9a7f33efafb8f0f50df9c279503831de9db15b5a3d30466c2101f35f62492163f6cf8fa36e0cf6bc6c4c18df21a7bd8b026e184aea019aee07f7c46a85a0d2fb8a13b95eddb12544e9aa705497b372749adbf12ad41f6437823c742586e8d705866471201425fa06edf213b15b796a675f910039a4729b66d9fc13192f263174521dbf5e333e1dd256524495f4fcc9c31be198e8b04b29388beb630b41adc9aa9c518a2c1e10a780c638044e981145ae28b84bc69ae898e7398ec3f705f2cb0ab4a267f0bfbe4cf0967386a98ce4ea26d2cfa3fd6615bce6ef81860f2999272fbc7cc662fae017d4e1197f9933bb15e001873cfd44d529d07754f26500a76945161ab55618da17e3757bdb18e570a5a0c45bf53da51067cc0625754a0394759b4182eeb2b8348242ea3a5bdbad8b542fbfad21193db5d127838117db294f699086d36fb27d2961600b2ca23c1c17a0b8423eb7db363b6ce1f2c09355237be2de3fbf814d5e39341e6ec652aa7943cc182581e9ee6a2fe478844d708929b2e5a050a2db653c283e7a75649d77121e86533d82446b979ed976e83810e" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "fecb2af9b231a5d15fa7b4c05fa6aa2aa1636770c623af3c80a9a803d1ae5c63", - "proof": "9c63d41b4aef053781fb8a5b1af7148b11a368a39935b65ae2e1d87652868f1db0127feab6766119237e36f1fb8b05bbd19eced44d173b998383da19b183747850b49891b25a234cfda494c9901ab872ae3878727793a0c90ca9346671fbfa5fd40972d78407c92e01e2871333bab642816f191ac2f4fbacb1c82c3bce01ad3e26d1d1c6afd441e5bf6b7fada081ed1e283f8c1a4b2987f5e9883afe0ee71d0d68efa7715ea08e0e84bd6a6d91ee3adcfde6289c33d932b4d93abb1faefdad0365457ab4fa4276c969eefa055eaecad213b82505bbdbaf323b28bc29651a710dc4a09e515439a5bdce40eb870076dfb3b8edfbb9ea06000ddba69d3c7c00b42e94ba904edff29c85c7bdad70177fec40518c53b1df7d376e22131239e1b9d556589342b8a958236a829c814760222b0630811335b0ad4ae2671081bc7a350843aac2f5bbfa8f8cd4fe71e3cf9ea12cf1550f2a919d0f60dbb87db9f757424c448235afb226011a0460bb3e78dda99f2921e7e4b04e11761ea4ad0980cae5422bf0a260e1de95faa3226386094e3e2121adb89e1c5f8193b980a9840a94b3f35720ce1ba29ec57f882b0cd3a71db9fb58d2952a37a71da98aead27f4e96ea56282493683e53eaefe1ec0c8976a0e25a76603e2e4d0c89f2472eaf417f7ac2b16c2c145f673aa8db7f8cbca52df63588d4d94044ce4abcfd716a0a65a698cb3b13b248918f8debbc7cc54da6b66b1ab9a4851038529ca9b6596757a5a18f2c5c78646396070b9420d8ae6413db8aa13756324b486a1f3ebc802137c7220b63fc666073deb8f68de01fc86389c083b1d8718414d24bd6401b7156217dec57bf2d5135f69433ff4c39ad48ae2c58d187eb21e1f7aaf7aa6dc37e7ffe2f6ca73cc304b740e021de78ccdf4800c5f0205d4c9c6ed56713b752087d95fcf1514b4ba20c" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 4 - }, - "commitment": "70ea1ed62cad4bba017ac73b24959403753e264567cc5e26c0c4618c7172d347", - "proof": "4aa2b0a73f96095b8ff4517f099f3ee494af86051aa684b3ae4c61fca9cef80ae48716d8133b66ccb1db39dec79edf64c4d1d0af17f1b34207c1401a578d20732ceac2a3892717affe659f25e53a34d32cc04fac9f1884f9c95567d956d747040e198df8144f933307e737ce85eccd944a4d3c4260d766459ab2b3f9f7f4e5701dcc5083771726bacfbf8a427d2e25560e60b57abd04042d642d5a7b023d9808ee9798a3aa1eeedb16703fa6a4144057a9caf5f8b29114d2a4ead835a3400f01fd46e26b67e4fcca6be88f098cf97ea5756b2573d2e441fef069337b0388970c48ec0d5345f2f9bda5f1b4d8a17860ac804163348f81f3791b5c6e684da593262a7155c5f028bcacecbc3f029702166139b0b3ee5cee01ef2b42a2d584adc06e5448423b90d2765b445c5e880fd0c5b6da9a9228fc0c261f2dbf710d1bfbc57a8a1857b968d3e816bfa849f13ded569b90f6b71187e8245e5509f900ff9fc7587cd5beebb9beda7a0e4773a2e38b6f3b2d7c94d2ce6c5af0c68c78df063020352608920243e506e7e53afd7fee32acd5f0c3bacf3ced55aed78230e5953ecf43e8798ba23719f374a06da2957430fbe04612f08bf7f08607af94da8625e05729583b7ec99d5ca86eec7a24775a0ecb8448d67f80f4285cd572ba8ee80717f41a7a44fe8532a45491f23a89cccfd53626e088462a6297b6edc2565ae7f99d902588dac198da6a9529044ef8892d23b3422f5e851f5974b5b54b93b7c203964b76f6644fa92a7f3017ed7d50bd6dd736f39bb48b99535f30ec86b079fec22f195ea079e6b72157896e709bb53ca034b9880acab6f5df7a200424f8ee02d81e425a7e9db1bc94b91ebe78c30e6b1f909816acda9d21e6348327501d1d49cbe43d093704bfbad4b5d9da5390b691450de3c77377a4081219bb89f3e1cfea4da65708" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "02b6900c6220bcf8613873abdf06907294d7827ccfb043aedd90662f55dd8f19", - "excess_sig": { - "public_nonce": "140bbdb75c9d8cd16cab3aecbd7e43b3d6165f3a7bd21091824f9ea83514c70e", - "signature": "94ee117fac82a122a7a6fe405e5e5c07dca3d903bacc00f5a9cc11e51bd3b202" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "14e012cef80006995c15953ffc92b8055fa923d0f3b50acb31b19947763a542e", - "excess_sig": { - "public_nonce": "18dc89bd101d3f599dc770125d941c7f35f2a71dfbf59f16deaef6bcbec7a378", - "signature": "01153b18840ef8bc0badae8618fc68d1651cbc6d6b30587a6de74bfd27015d05" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "2cbfdaf1766df51c47f5f29c5a5edb919cc927be01c30f099f2ca7ad28c8445b", - "excess_sig": { - "public_nonce": "20571a435300060a52a3bfa3896516b780aeaa9d28cc557024a1d69273c9a173", - "signature": "15fc45f056190eb243338903f433fcc05d944fa48634b553900cb6e4673f9c0d" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "e08875d2fbeb6a297cc33dc156d81915100af4130e9b60a85bc4fe54cb55ea6d", - "excess_sig": { - "public_nonce": "466f9251a1abbb639a6190653a59d3420e3c0009f9ff65370539927fdf9ad22c", - "signature": "31c223de87c5eeb54fa7ad098c58e8c74f7b68367bdf5ec0b78296de4b73650e" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "ea04a888f203bda1fda8729989c942ad9abfe37b31daa6ac806be23fd910dd24", - "excess_sig": { - "public_nonce": "1ab9e8051eaca6c89720ece12deec0d71d98789ad57f03d6fb5883bdaf31a267", - "signature": "e8bc2713ad02b7bf2ec7b568143e6578fb1ab5172ca53188c80dbcf27b8b670f" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "f8cd0e18073cc43a01ab42e3691bfb0de26d9fbc6d274c353d7d378016ec9115", - "excess_sig": { - "public_nonce": "26cf90956ea8b9be25c7cad32d57f42b0f5400f1c8354ca30ff94dc5f2499969", - "signature": "8741969f65443715145452dd3d81ae254f294f2fcf1c1a734017e1202afb5603" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 4, - "prev_hash": "a5d795d8f8027b63995ef6b5fb1ed787ea6454c3f1bec7fad93a52660f1a5434", - "timestamp": "2000-01-01T01:05:01Z", - "output_mr": "f47eec3c7234a9f953bc12dd31522cd95060ee0f2d0a0faff55d79015d58fa8b", - "range_proof_mr": "3b0b18cdfdc6fd38afcfc0b68a8cd076afb0a95c3dd9c0e33f5f326c1051bf88", - "kernel_mr": "2c1d0afbe37017b60a92eec22c6f60843156d47832e5fdf0d548ee7afd1090fa", - "total_kernel_offset": "b96a29945df6c0035addaa071f08112aeb3f1309007f9ce3705eed03c5b29e09", - "pow": { - "work": 4 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "10819ab258636ef2c9d4a8792998de985ea5370b8f818a817be9dd4387251b12" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "2a5cc9885cab5f1998534534f18e2717be1fff00da99e1a770cc04d995268c0d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "9ef08ee542586ddb8fd9788d261d9db4e32c13de733a33a53b04d730a0897939" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "aed9066358d21b658689b6c751f3be49e1aab81cd7f410962aa6ce17e493082f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "c4fb6e488d26b799aa118ee7255e7281279ba2facaba1d46c24fe1b9f377de16" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "06c9c1f6845de8c029c26a819f12fc5439a68529eeda35fd48a990627f1dc553", - "proof": "b470b2787ddc9e75f4f56f3f6366cfc53e9a5f0d14f324a82e54f8866718cc2fe84b1ca0855a3ad2f521e13de29c43367aa62ed150a173397df30fdc3de86168246d5bb121ce39912e634d27c91bc8ca24689bea063d71535eff3c51385012613e0a5cbb261e4a4dfdedece908ee1b915a4cebb3e7586c1e55ac0429762f81763e8fbd1bf6a1c375719abd6ba612ab904a5e320021e25183110fc50e5347f90e33250658439818104e307397a4cb89b6892d620cbd21b44e0f31b65fdc7f9008e978e154b989e92dcbb37ba727e065988035ce35ee192b2951ad69798ba2330e20a882f0ca0b255ffd33e18db9e0f72e54dd2f92e9c3d6201ad119cf5c7e447764fc9ddea4edc5272228e3a62daa6c32deea7b70e10c8d1b3706b0fe3ded3a726eb136caa1935ce52c0cb806fd0950843e405ea385ef6e26a037743f3a69a72828adf67e6809168ae9d64fc1eadc13a23f7dc33a095435790ba16f6322180e07aa6ec8f35fe3a609dab324cee90b5c44c44945cdf1aa0332d2348257dc6869599e6eda7cc05206505065fc59f7ef549193a3e232ae5d9f9972ac9b0bdd60137b707c0e01d23a16194c922c3295a24c4958d4afbdf933fe964ed9b5580865f8336e9fde2dd52da929f75b0b6caa59b3f387a735e276c913fd002e04d47c09192006ce856ef7ef5e01edfb70a0142a7af3503cfdca49bf58e225a0d99cd7c7a64df053e3ca8f1ae341111bd71429500b982d7717d0bc8d210660e04363c535ef6c226101f115762d576ac97f128acaaecfa758054af226f462570e7771a07ecf625a9ff1c7e3629867476594e80869bcc088e06bdb88f223643e70ac096da3d15bf849c05b53d3916c4d4f577ce41b161d0bac06972c8e3f8c20f1c83adf441d02e3ae1305cc43c46db18b8c81ac46a01dc663204a99c69461b52880065bc6d10e" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0c468efb77a4ce8a4d1d9fbd1f01dd42227504fac1197449123038a2d9bf6903", - "proof": "16e0de0bc4fac2b10a05b0698492c64a39dead1daed5c411fa7ac4f462a70c31d69676753b5d8acb0f59adfbf4cf4310bec79175598a06f83930b258209eb615e23d7926b284402e4600ee938a65e4cfff3ed11fae975f474a28a321aabee331720d09bce8acea2970e8f17aa6230bfa7d2c7fe401b73d5726ee9b50ef16c854ad1f0245c26bc2a66fa8a828e3ec37c3af6120dba75c2f9fa6467be977008401ce36d00d33e7d305ae372f7c8514b6858f62b56509eec84f0d04fe82888b8e0bc14012c22f09ec139b93ac5a107c1c36ff7a257962fc4d3b8f36e6ee952d2b06acb3b6de5fa121de0132f524fe057007ed0d8546af66820c26f2251c8dd4d03388e92d983a96bc5c146e223e3ada875bc9745ffabaac62fd6bb8045c6274870608597d12984a1f0a6592c141f5acad8fee18907c6a63292fbe516eb97702721156120031f356190d05982f216fe59f87ddf474bba16e0d545ace3098b0df104810cb4292f3cbe672858b4888487eba90da81a135e6f6fa9bfb2fc52c506bc65f30891d831eff69441b50e84b7f1d6df82f75627dd71b75aab1d173f32790f81ec6dc1d5c27a4261dcb7188aa0fffb84f3877478df49fea32bbd2f43286bd94515a359f4ff0193a5a2de69be9d23a7184fcef2a511b94dfde205be86c3e9fe22366c5c104de58b00f8c2650be690f9e00af52c8e4f354f3a0b7893f6748702b06eeeab5d270e28c088c515abc650b3b95bdcacdb3c759b25357b7daf4878a587f44b797502d16d79a74c07a257f95a5f4d233ff515889293817c488ae9ab0831bf8594aa58bdbd62b7766e1732a1d4d5d681964e6939c800bbe42a46662de4d476b0df389322b485c47c4f4c3e07912a93ac31f3e851f7cf766ec1fff2af5f80d02d9b4dfcad320690082f4c5bdef4012e6b69028ddf8c684b14543ff5c6bb70a" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "1e96e35cccece3cf198cbaa9f1c9091e12b4da708ac675def85684cafda81554", - "proof": "d878f435aeec1b183fdb2870bb007985e5aaea4a3bfd6e17edf4203f4c5a856dfcefda708041cd190b2b40a8e554934e0748a61a8adbdecf7b94b50abcfec93de2544513c3020435346e9284b8adb7ada64be910efe136a7636c14284e8bff6e9436f3d9ba448de9d95f76b275d190eaf505a829230a20133f43d7efca9b043c97ca1bbddeb3a797870f5e977f91e576bbc75523e3e0920c0fb542a64338270b2f4df1669ae8d551a40b3999a1db2f0f4a9df897dc38cce1d082d864694e0101239e76ae76b461473362ef1f747dbf9e823e2d93d711fd193f701899cf46ed028aca5aa21e2173aada7172a567924972941356733bcca3450fea95aace57231600498ef9c2a8bb47016573c0f2a55af0d026348eb81f6608d979aef2c54a05567282e44a0acc2682373a774b87ddd9f18007f012fce502b5ebe105a49f197f271c246edad85a3419a0bbbabeac17e69c2454f117039f95b47a3da3d6330411094a60b21f95a1c40705792704f6acf6612f7609483d1195dbb9fc3ac09ae30d4f908ad6b2263adfad77b10da69a8f4a914ced1682615b8c476b93c295d78adf29e4ca2ad886665db2a5cd3da217fc278821f62f5d7ca18df0fe261b150ba5d515443031c3014f7e97bb794548abd3f72497e45a29ac0c9c66445df85643f28052a207e5c3e8807ed9d3bee7eec2ddbdb8fc05e8d1b3c7dfd1b0c1ef84f13a731778ed0e629194a7b8cb16a963dd5bcbe965b6333c2297129bf23da84a7745990ca002487855a2f770e8b8ecf2ef8f1644992920b0e36f956899ff426ebaee9f734645fef217bade32cdb0ca8ccdaebac63baaed89d071e7288ac8b82eb842bf31484eaeecb15437913732927a8899a1b75e18c01f4d80072d8cdfa85c8f4aed0a3a6d9df55fa8b4d462e957c9f19f715a16f751d2c9ad7c5e00aa0cb73f63e106" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "201492b78ecfe229dc938ebb0b09d1fc3a8852add23e4748e3dd768f16f86b6f", - "proof": "42c2122dace0b7ac4e8f3200ca63cc7656ba3ed8501d891e5def1adee64aa32c4845e9387ad8903bd36f4525063e23fabd62aff9a2129cd7869191d912a576648456b6b62bb80f6f6e976fcbe40e25f39512a50181068c8fde619df355ae1d58528b1de1f61cbd3c5f0f3de89117dda0c3d895d87bc7ba4ef7d15c6d9a12b140a99cb200a536b3e58450b51075e34bae5da53d2e91f1238443e0396a9f46f20462ccc67907f6e4011db1112cbc6eab5fb8d462fb4c21d38d698b2c3d0e69190fdce75b402ce82ac1facb589b66863d3bbdfbd83f62c3a368bb38fdac26964e02bc37a24d668c0e5dfeff55c4e83db71ac1c6684b2f0361bfffb365e4fcba404eeae8b19290c375aa0adcb30d15547aef2a829c005456ab884a32287e978cb875e860db5711326c429e513780e7522546fb5e73d8960a9dc146519a44f0d6817e3ca0f4e18ceabf4bef6fad0eeab5f3099e2df7ead3d436fc5f5effd562fee3092e4f88449ca1545751401a91c4d81954324ba3d53aba1384b9b0df37951cf85778d2f1e910a0472758d93688a07cf403ed41b93204880374416caebe650f4d6136f677907c1bee5a9142c8596d0cb8768ac33a5cdcb2a6c6e428238a430d961d2086d8c11ad30b6f941f863f966045b5bfd77e8dfaae206b18c3f6df92a02b782493c588589e098b036143253d040802b58f05cd89ee5cdc5a840fa948b8af031627f00145ca90521ea1a81f6caff89371bf54e26f72640051578ebe58a088345294a69d7e1e6b0e15fa92f6c8273d2c753c99502459f9a327fd1a5e34ab792f9e1ab2ca88ef4204fdedbfcdb9311fd2bf7847268970ac5ce55f6fd68e09257ff0f833139819fc809c65cdd96a2bd959674706bf60a1056f9c4b15331460bd033b32d8a5d7169ed1087b68abe8da8e1eddda49884f1c54399c0028fff5159c01" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "62e9e96b7be33240625a8325a39149dbdf5504c2446bcf8b7cdf7d6be0f45359", - "proof": "b626d82273ec22a42440b313f9d403563079339819bcbc47bb843cdb8d15b968d0a5f9cbea58d9914b84102f097009984e36dd26e4b36b2525ee69f581a0e41a5410563ec9951dfceb3fbebefc8411106815cbd614a561096ca480ca15c8ed07a457667956c1b3bfafab1f2427351c654a9791d9c48b92375861c4a461c82b3428ac1c540f4a943be894a9d590e76202602597c3cfde5615ea6571e6d6f42601ad2713ddb696499fe03566f347ce4f6a42c6d325c15c095e17d55b093b05b10030bcd613322738437a7ed8853881540d276f034b3865233e7d5d7f4de37991023e92d8ace29d69c6ada7bb1e6c54ba477342df6e819be6c6b3c2302df97d416a3c94532b281dfb27804e152e548fecac0ab3e43967c188bc687c683f6a1077184ee108458d392d874b540c9c3543f07ddb16674ab5f529c1923560d5f4016f0ff2ed9ff3890c49b5279bb1b63f6e6ef17573a8d3cd7ca7be9b5cd353b95e216ad2d218f13aa5a5be316de474c4292cbeeec8bb3b71fc992b17e43f8325c8e771baf3ed803033a03f8563f16aa53d852da352b4b8b0982cb9751fbae93b6af86dfa11f445a23b147f7e3fc7393c15a0dbdc8488779cea7f700b1edd5d54381a145adff81c5168b538da42dc24622f226a0266779c36edb108ae9e57dc49169608ee7594111a88db1f5651582940a0dc458fe9c8c8f3faa040fd55b6cacd8eda3fca5cbd7f57b66e5c4c6fe6bad212ceff6ece8cc1362a6bebd3861208c6a0015ed2cf44a161d044ec2a1a9201245bd84695a7ff88ef607520f13a2a101099cd0a36d9235be2ccba9ac0e03c4eb931987dca155a4af87fd83ba9b99d3f36dff76015e15d19c48ebee868d9a67caeedc7c025bb5af66904096c6f671c8b09c8e004242c45829ef4cddc80ffe275eb4eca0e5a9c1cf9b3a9e3b130ad445981647003" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "6cb2cab44b25d2e3d12a2c729131457f8d63ad17080367f7127562fad5c34866", - "proof": "2a831739c3b2ff0938e00b10be7dda346fab0c3d1dd27a23af7b377e73cd251b98db9726e4c40f5b6f110a311d0a6472af52820c36937c0f1f9e8ee8e3e9b344b6c4cba4f967a1f7b509271baef14fc5110829c132f9db4eae8f2fa7dae7601ed87cfd971ec1e301baa09bdab559de438a58fa1d4e74e65da92d7e6f7ec3790ebc54934d26232794dcdfdf5c80ff28ac4e831624157c60d9582078caeebd890f217336b990acd5474d8d44d6ea6112fd5f0464ed8763e1b47444df242d7c5606dc0f850a0c17565b344edbd8da2f8b210eecfd44baba491e5624058b1a08b903d8fbd7d01d2d0ec9b97fc00e59793c5fb26f735a1d1568ff54c426a6fa7782001266b305d685e326bfa9628f978ce2e6c111bbf769b5d5747ade1424b8305e7a6ac0e198b8998059504f31ac4cfd22999e66f3dbc97864189dea187c2e8dcb1950a48ac0628d1515ea5ee848739444c4d341e7a60547424338ac0b4789c5d266124980a234b7db02ecd7ad1d668ab2a69d01f7215897114fd312f0b64d037d037cc0473fc89947d64bdcf0a55371a9bad97cf20ded273c1f81b9cbbdd962dc27a2258338d25311559948953c09b5797b5016c5593d8d7f3875c950c654e68e4356ba5f957c16231d107285698c11e06e76391654080619eda2560f43833ac14f763a3da562eaef875fcb91c85eaa0c068a02498b24ac60484c34467684e1bf40441a5a8ee30963f08d4c78f5ccd1a29064faeea1b67ec533e6b4c136742cdb0a5aba466c4eef54fe5d365ac9c899e337ae1a2a5003081c6a1f15cc3ce1908d4f3e5a4a60edcd4d832660f6d35fa985fcfefcfedbf3f98dae10c36df58d41394eddf9f04d819de3f12c0599401c71eb98b320fe80b73587a1c4577c597325860ae44598c5aee9d418a7fcdb1403e4a3e37a0578cf1d398c68fe701a62d0073909" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "9a9c64263382a8213f750ecf4642606f1ff52de9d23082de24b79aa6cd96e92b", - "proof": "fab5592b9bd8c18de48e141e68fb130a06bf55658e311c67a376b36b6cc8a54b168dcdb4c05f3539d8c1856eef54270df76fff5c0ed95613bc0efaef134ab2176ebe069f7eb77d8924dda5cadaf1711cc0b861c22750e319e77ede95ab090f5f2e1c7c82f73e47886669055244a1b8007e9c35d4f09653ef72daf1e07cb2f3209ee161b16cffac195d6533a796b6660e72a34b28c53c798a85c408186df5c506a47b5db3f59b82336f6192d87540f473d9c097c711ab4a8202d375e76d3c770e3461a7fd46c688c34391f5881d73467148045f4b1b747e7d7f6cf552028b8c0b18bfd15eb96a7f6bd0e1947b3f58eb368967c99bb77e30fb093c6b13082e324a4651ba3f7b916b5dfa4db34a918c976e26b1c20198ddcce7f6f7c71db3a72417cad55b7e6f40853e03821591a4a40e9d59d3f652f6a57a33533d1db57fe3a14b268f82c5a13e79dcdc3c799de7cbf752b1efd31710dc0f1fcce2be187f43fb0446582b937b503fd147c008c7148b575a69aa2d0733f4056d55aeca1e7a458d1d404eefd4329241df8a82ac4a277b579835c8bb28cf578625e677127f2d0a54231c674c94a29a2d4668f41cd973e9173ee1f262f17b41c3cd21164ac999e49b22220a1109a926d7240641b9e8a818248e58f4dda30b4ae62a4dd5faae346c995cba80f506273170001a418c25e58a53f8c39376a7b7e1911b892e7ad459358306ac974e6acc00f11373120c1315ead607e6f82c332fcccb2120d9642e1e2a604d204ced44bf61e0130e60a20ab5c43260f85364fc77a3dd724219bab32b536572be01ccff3206916ae4e01141c3664effbcd7756d609fea417e9fd2ebb447584fbc0bf6c990fbc0b35bd3f2b602a607f71793d002887688b4416b43090ff4e70d120b4beec16587c88d681235332bef1d5341695fa90d735016f8d9f90f5bbc0a" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "cc591c27d565e22d405c05d8242a902a8bc3d67792f9ed18911594525c748118", - "proof": "5294d1bbcce3d099e2e37bea0380b34c7f99dc17630552f67c8bf5c5a2b61e319255e585e687098952ee6398fd339e7d4d48b4851c90a3460d47c0557b7f7a6948c4acfeed4e079b5e014ed66cd75fe9a21f750b44c44fe2300fe0966807f87a66cd9e725a3675f5289859698a2df458038a6c9f1e1bb7693bb127dfc8e8377fb8a0ec06404fbfa181a887d5c88acc7fd0c46c080ea92fd142f1ab4b3340680d7b81b7951e7a3ca1ddc9b448b9646750f81642391a0480a48dca023abae9ca04e7912b5c8eae8dc02e421448454421b0a76824b15f0ebd91f46a0c88cd50c90a8e37485d6fdb10c78a08832db7cce1027d0fa129da90634ed3454cffc4e0d51ccef91a23bb444fcd9c77a355714928b8c5f39cd94310bc1557bde803d7d3111abcc95b2819ee45e5f4c67c60b6b13129dc2c0f1d4e653bdf4ff71b43100a06120229d33d033ae2b33b305c805e7fee197d1b48760848a6121c4afa0674e16b71682220f8a3c93a62f798849b76a4a87754919cc4e26cffad355b25d40ad2a1290e17b7bef746c09f4e4d08f2c7c38ef266ff052f531632e6e5bdf5ec87d60866304485b825c89e25fecb42d27643f50a2ff9b1d64f2fba3f6426e0587c76d54bea42fcef7b9577578c1a2f2a37a5b176bae75a1837b208cc3f2565e12c6b090bf23c1ea0fb0c871993d33870e05e78d2d34ee4afb2e7173415c16084ddcdd63ceeb48cefa156493544eb8265360402b5138b5bd5410bd9f61d28528423e9546ffeabb87b78c45460fab452ca60d373c3a745ac64ce55ae03c6400daa9c72261b0aad27711599fc5a84647c457ef310b0a3ee61f415829c86cfda396d18fc5744893ff7e5f4abe419c4ed82d46e129028d7a4deaaa1e6f00f38cd707faef9770d67b1dabcd9d3d934736c95d87421d8daab3ec41fe07b0daae342780e5d763a07" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "fa7e9702199863ac347ce758e3f7be4efb624545a28aff7552f6eede76ccfb55", - "proof": "b2dad299f8c9a58153b2c995c12d822ebeb50e11e6db503abf82be0422d88522deb83382b88ad6eaa3fbcc634359b910de0a17f127595b637442fe1d2908275bd6c61836d2451d8878b4da6c34ab45f1c61655e78031289c0c34c4f6f12b9944cc912b9f9fcce5340066357667ae468677c9568e603684afb24d0778e6994249a00878607f12abbf8365a181fc061caa0b1cbaf4d03902007df7be3f27ae960f2dc5b2c95f8084595c9a51f59465c1b129f5a4241eae207c3eb3950839157808c93494a43f368090084241f0698f1488768109ecbc7abbf3605b08afdeca38086a0ff3c31db685b1f3f2ecc795f8297db94ef930791ac1669768aaf9cc1e4b586c3e2a96905fac68e9c3ae2c32a732edc4fd539fa9cae7f8f58087548a3adb055ef18db6e26f1f7efb939310f5fe22d61144364c7aafc408b7149d02165ff11044f740358bb1939e99697d986382d03af4db07455f6ae2cd3ff9343c10c87859a2a71ce55207ae97384f8d80822fd392f8d3abca3fd0ed58dd1081c7c5e5e30524fae05c85ad7bc9373a75aa45751f636e538b8383d80629b3baf6a8212a8a50b0b29c60ded985225e3c5a5d55f5f720b115bb868a06f62e5a41aef148d8e338b2566d9206151983ff7af1275983d66e6ee608dc39f46399f2e4e189a29abf4d7885b766e5550823afc7833aedb6a19b030392fd3e56ef2eb01274111c2d487982acc60a6853047bad5e86831efaf2bb4cdbbc61623fbf06e7865610b016082bb8c861b6111947d8d1fe3a8fd694c4d67f85dcfcaca9071d450aebb887739167a2abb6c4adcf295677cea696146c3990bb5348d8ec20ea7fedd23f7ab54bd861559308cacde4817aced42a1708d7bd9ee5411d880a38d668730dc2e2bc589100ef945a4bc2c96fa73dbad409b3c0fe5337b348a4ce6e4bcce3620f83a859a40b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "fa9bdc4045add5a66548ee1d22ff46f2b584d7a508f85325f68a4ae576a97629", - "proof": "8c186d07072bac15a9e5b66fdbf09aafb30f82c9c8f862e382c51f72808bb76584d0f039fb4bc631def629d318eeebf9026afeae6055491f7e077f38a2a8d64714da13e8662a4b1da2a4e8c456a457a27750303c8074e605b57e79cd72ae1a3c50ba755a03348bf64f2b3e576496c8132bb79c986d70733182e25fcba2d8576d69cc3154723e4983f0848c9a8b49b05050ec2905a0282e4c281fc453be7ee30c582329671748fc02ac11e8f41a9117164bbbb07d3464056587836395680f3305c0c7f0edc55b02709b0de8fabe65c5968f059f1fd87660baff4316b37542b908e6d4913ef8c1b7c5a7f5231724406abe05277088a3e0e59ec3d0f672f1617f5cc0b08a67729c904c00a73040baebd8ab6c23bafda160fe08975cd0b393869e3792f20b6a69a9caeb148a85c91d974d720929145cae4dcf9302a2ea0cfc4aa32088a62ac1d57e5ae6a2e6eeedbb74aca7bdf77714807ffe4d15881b2830531870d0c63e3c8457bf7e43c99bc14922e491adedabcb60e3935c74095de377b48d77fa364966b94740390304c8236bb3aeae737c8abb05bd678e1bb1343d1412d92588baf807c682c8105e6a9103483ee9b8fad05349da9547b6607a666feb94be09406c182e97d5a38e7e8eb5b7670110e68916a0eabd238c229d94eec3d75cd9462c88ae4c0dffdd8aad11a43f1b32ab0d39f75071b60cda9e1816020f59fd876448ac9d3bc7ddcda466c345c53277fc1d7c3108c3839f60e8fad15e1a95c9704070c61376710f4e41c91a1bfd2afaa60e9aad9cdf8ca1935bf6c91a5696457775dec46bdfb373d21ca5871a2b1aa7c609375f668273213afa44d4d633cf638e6696d331a9555d4e03dca1e24420549b7bf5ded392a75f105539ee0bad6cc68107025fa6fcc06eaabb2721d6ad51a804211c635aa9b3081bdde4ffb80442eb2700" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 5 - }, - "commitment": "9a0b7f372fdb755963e5fce6f0f0bfe78c39594e7c5dc8d05b41bc42d5467946", - "proof": "fa8d034e910870479aec1427bb7069e072acfa326ff3820d7357c39d7ec9b32ff436a2f7e4ef7e8dde4ba228d4734dc61456372834b427239e788e3e0ffff96d0a8f55e7e8122fa4f83ed446ed2b67a779854916f34bfd40b32975b20bf91d67d0959c921f3e507352f84b109e707c27b1ac0fb7e0d7413632bbfe82a1d6b91d9b2b70d7b58e77b120b0cfdbbe33397c6fecc8f9561b6ce7ce77ccbb5dcfa00fa5e9860dddbb7516713a3f18d380051d38850d9da0124990bb06f0fda63f7206bcbf04b1c9c319fb493dc1e4b0184cdde0de04c03e13e464bbe06572c5213f00421fc0d1994539159e03c756ea7c6ef6b6e92719cde3e35c4c701efad8ce4b44f4ecc3c9f2f9f83c9b5bc34d166ab3ace4806d522ffae98b84e23b5a2113d8484473a8c0db7d824b7914c3eb3044d06f66587ee0bdb6a62b0119070a06947c7b1c4f08a521d5d4f0547eb01c8484640c00670074fa492e9266999299752328299e0e03c333476d946ad3e94e1155e0d7a2b3c76910a00d6eb61870c4d0459b3636d2e10a3688fb143adfd3068b1095c2470ec0c10acee91dfc44e225087365000ec447b986317f990d47ba2eaba9883d67b3637f565f62c12116b91d3e80b56af89ea95181c3486c357c4ef49efadc44f51b0332caab13cde9d66b1cf10ed21bca359bb73f5aec8697a2a774a1c0f217dcf86ca14979c0ea4400c8015c2a2c633c558a3f0ca58935cd6a31f014e216d2a4bdb04303fd3b742971bbaf0ca67e3068b1f367e25e16cd5c15a6b6ce048d1a417e6c6ea67893e162bbb2a76b1378220495ab9c7b58217142bcd77df893dd8b65f8ec18063e133c76833ab02344511d5544c4b11db3c13d24aa8ff611b5e5f6481bb2b7f09c3a67f1a0895247b2f70fe73861750a9d48e346edfbed7dca2beff386e2998b35b6b4e10f936add326202" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "389ccdb505867cb92ac983bd3662ed232664e62fd190a18f8132800268e1e036", - "excess_sig": { - "public_nonce": "782a5d3adb0f8174adfe1fd6bb16b66efafac608a1256f6a4c515511e8d3ce14", - "signature": "7f82cc87052eed9da15616ad2138eb6c37639fdc77499cfb1f582d6eabf40104" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "3a0609d592ee305dd76d2d4d4e4311c1759139a2fd730fb0ec1caebcf260c55c", - "excess_sig": { - "public_nonce": "56cbee9c749ac41d612bdc05cd4ac2d1eb68e7b0a9f4267934a4e2e2aa8e2b7e", - "signature": "6a4a7f8dc198bf850cb3b779ff386b03066a19ba52a1de3c10173fc544911a04" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "ac9235aa95d94256fe9349be4d3443cc8c673a5f9f1abc78296477e073bf3946", - "excess_sig": { - "public_nonce": "86b538e2e327f65908d9508a9f6ad684a1d1ed742d819d07119cb0df7f314b01", - "signature": "1220795723fbacd3f8e72db0a09bf3c9adebb828078a026f7a0f750a0431b203" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "d6a12b95a7e54b565118e89323be3f984eb1304cd9f1dd463691b25a848e090d", - "excess_sig": { - "public_nonce": "64e7e4adccf02421bbbf37b100362706a59b1a6bcac69d426363b4e37b372a02", - "signature": "0ca5c0c2d737b58d36db805b0b177a34ec07a062c7aa6b26d3e058e925ed250e" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "e696cc953cdc18fde0c2ab43deb54602851e69ce311a63acfa1d3ac1eb20576a", - "excess_sig": { - "public_nonce": "3000e10aeb74e1fcbd1dfcb04bb18ae4d63796990d44ee5954c51b2e35b5a403", - "signature": "9c898c5e6db9e1f73d429f8e9499c0450a54e2e639677017cbfd45f084484506" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "6c5d377b331798484f88a2f6178ff681ea1eb28de40ae238ba850a8c1f3c8242", - "excess_sig": { - "public_nonce": "6ae560076e14d2a285cbf859ac1944e3305a793c41f51c8a019752a2e530f35a", - "signature": "880602d922fe825bb512889d2d124e0ae7053cd5dc553341bdb9636fc74cfb0f" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 5, - "prev_hash": "be01fc8608a54cf3347397b55aca70477344c8b680f8eb9e55070aa23ef80911", - "timestamp": "2000-01-01T01:06:01Z", - "output_mr": "5c9d18924b94936d4bd6889cea114cf146b83eebad701962752d55242e0ea261", - "range_proof_mr": "a9820f3447f176123dff292848143f9911354a619e14018104af70cd201f2520", - "kernel_mr": "349c94308d2365506d598f82a37a7ef7b661171dc2fa36d3f113b842d75690e4", - "total_kernel_offset": "601e22fc46088cf84e48eac9f3c25e7e1191304fdbb9054dd5fe1080e8235c0f", - "pow": { - "work": 5 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0c468efb77a4ce8a4d1d9fbd1f01dd42227504fac1197449123038a2d9bf6903" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "1e96e35cccece3cf198cbaa9f1c9091e12b4da708ac675def85684cafda81554" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "62e9e96b7be33240625a8325a39149dbdf5504c2446bcf8b7cdf7d6be0f45359" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "fa9bdc4045add5a66548ee1d22ff46f2b584d7a508f85325f68a4ae576a97629" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 3 - }, - "commitment": "1c74853689e34efe8ac094362c70ced414bf0ff072eaafb9cc8985aeba5e2809" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "06b695a6bf4d2bd1ace456cec231e402e9b2ae329280a3e5da780b555ef72200", - "proof": "f0585237db69d6dac9482aed7ccc457779bf27c4684bbe33e61d8da9b3ab6d2552dadc76ffbca4ffea32cd607477469ad0825f4ef029740f5897cd023327a72bea5f525aa81688801e8d18ca059341b4cad34d3ad9f8f69a5d163a7cecf5ca509e4134c110f92d53959560b6e1ae3ca01290dd757af9a989e3f0f7cb40b4e04a6bcabbe27cf52a89491522b107bc491468e8f2dfe552e6c71025f7e9fcb50d0fd3896ed96fcc802d60bf247dcf72c7acd4b520f2b83c2927269a2a01d8d2400d0c5fd762b507739f2ca81b579fd7f5fa301dbc72ad9d6cc529935225536d080c60ff7ec17c9d0b909cb6b188e5e1878968c5849fd358323a8e832dcc6f7a2c715864ebefd9bd628b74a3790f17714c1e843d3777ac9d61655e2134383a5098079a80e3922b37dd7dc5f3f6c033358debc3b932de3e927456f319d1f067ce7a19980e2e68994bb4166ef625e26e05ea1ee5847a2c0d64767552084e77ba77f43e042bb7f43cd33045e826e2ced26ba84719e14a9692de0743b5b5fa3a051d375514119cba667bf687097402bc313487e0bfb2c04ccb300bdb96aee84fb81900270ea9e9850603c42a325df9de80b299bfe2d0cb21a4e66c8c1eee81ace74682271c651e8e6f60d5e9edc51b52781bb5f4818e5aa6161c191d2cd040260af80439a82b747935162882d0dcd8cca0b66cbe22b3f3df75fc28b601abb845593da81d747b742d3112c48818d721bac3509ea950233974db2d4b5c3991f34f95075e4594c08b6c50af0188dc68600ed345626d1e56f3d15de1faf756094084c002246378f827090a49cc53b096385009b38a10a297dd06775e9b4bcc5c8c5fbe434347f1be0cd86a3a48e14515d02949eb91d5ac6c05a56e93818733fafcdbdb29a800e64997620855cc1ad8aea840e23321585e5e0d66e2e4b537d84af03c181f890b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "12dacf3364c226f29a70649f734dca048d6da94cf76cd081b67b41d9a1a4e53f", - "proof": "1e3fc57b1f109b4f8d16481aba443524f237a342a498f3a2165cebc30eaba7197a756cf7c5e8966b52e93a2efb004c643214e04d437af6c3ed1d5db68e9a3b17c08297f8ea65cd2446399bc328cd448fd8c7c6b7e28db84cb5b29f7bc68f2e4e6c188c291aa5bf028c9ae46311f56d33042699b1655fc2eac143493508e1fc738b4a264400e15c4abf2769a607322c65073b221dfc4b418d21e1fa2ebe329601e623b75e619807b2df7eb53994966e13aa5e9f942b60c281624541c7d099840911016b5f9a0bd47991483af83d776773a3c3d830438bc7036dac608301474c02b2a18141965472349f686b7b03deb63afc9ce176c92e67189bd4a682dfff840c909a5ef628b3004e2f78d525b66dc34c800d2d809558f39d552f5ea3f90f0d7c720d49d34729282884d0b49478b4c799d71e9365af392b77d568e5c172c301225ecc4ad2becea315ccb37c53f14d6df3a8553b1aab030a43ad885b7cbf8aa507d659ee6987c4b713b8b8129dfda06ce8e19ed406b21ceeb292c73958cccee656fe4f378bc29be22532ed50f5bf160a1a612bebdbdc65aa9c2afa6bc16b5a5437485d4eae4aa319724a14c2e408487e3c57cf42f5b1865f1330c66a057588860eb075d2ce3b49a1aa68d231fa00ab129f768b19d33b8f2cea8cf9cfa1ea47406f8e8aa973ae243f2ded670c81209656600e76042cb91a80a7b278a346f8b6a1492ebc8bccace0ab8fb6e0c482099decc6e31a102a72d1f1de4d03a36b9a1a7d27d2f633afa41c3d3e4f99ce8fac30c51d1db7e5a47d66d9840a379074c017c02fcca8d465281a362770b61df5fb1a294e557f46c4866cfa5ce09b67c5ae46d67d3b5b13f73638f57e68c89b8d41ce9db2c971f97db44a27b955c5ebed30c0890554284bf3bca2cadb44d05b468b4e164e9f768270cd55778729e791f9d9d99a0e" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "2483324eb3cf5cf7385a50bbf408855a65132e108b65fdee7dde51144479fe51", - "proof": "30bcac4fffe48e711b24d834b247f542b84e6b0c30341e7870919fe6ed536f760618855a8b0cfe95d4ff331c1e1f6b5291cea3a7265246712b6ff678ae49c472e2eba6097bfb4db91f18b72d5362876916db826cc1735e47f36dbc22d4f9ac468c17f153e190b134043d54c7b4aa349f5bbc1c7a84cee954cea47d42392b1770d5e46810d348d0141feae4125eb355588b048967fb7692c721f061fd4fb0700b24567da99b7b24150beced378bf287b66e37769dcf6c2e69dc26989734a0ae02d8834ee492641ab02e5121cfbe6c80a5c8aac84450a0298827e40745a489440850734d7fa6942744015b49a54c423c7a6bd548d44ef82e97a5c5a43a8e16226e007c2c72e875d25b10f86f8534f8f119b0869596885a33811e1b9d10c099054ca09f86d1ee4da0b4a436a0221ca4b44a60e3cb8377735808fddf94cc4cbc3a4dacdddb7865f7d465b1d1ec24b68928d7134ddd2108f56682cf553851eb7b8a7394e15f87b9dd170063f770b425f9e199934f8dcf29d2634db1d7f2b857f15f412cc6e44ab2b26dced969396b293121c405d3ee33bc3b62c8d7aafb0e103c7775c05f55e06faaf90a60df90cc967ffa5563fe7331469410d9effedfe13c17aa4420cf61c14864c10518d2200185d6698b153f1ef2a922efcf43be90ac40b3ba6d96d3a46f978ccf70d55fd41a530ef7e80c4569a96c65adbaf78d39ee63c2c5359cf627c050da30fe9dce10537438c39504ffef74e84070d582fd66b400ebe822b0d8c6604a4987a71d2b3174c57e196480c4a8d81f6e7228a6710d40ea19e06fe24e83ce669a8f44f823b365285969634de8230bb79299dd659338b5cd881c5c23491cf317dd4815c454e628a6e68b84a07e774d56b510d14d20baecb40e7907975beba3e4fffd72aab4a866c55eeede1f8075288322d558e2f86dac5078f90c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "3054f4c914cd7bbb8da287fed8d5ae1f8ca4c0e0221e0ea6bc1ebbfef61f972e", - "proof": "5accba0f890373339ca829c0f30230460838e73130e5e5bbfc72af9d80932e539a4a41ab939ee4e210589f8a1fd4f893376086bd5f4c9678b016e11bd55ed07c869c05ea5e3a1fcfa4c8398c6b1e219e2fb3c155d918bc6c2c3971ab6d924b210a02fc6621305a1bd8d16bbbef17d63d44e7ff0094aa12e12c9495be7102f029669053dd1a89444aea1953af67b5ddfcbbad5d9c4a045ef5f3eba375a21ddc0750938e446670b055fe323829abd2fe855cc65798f3ab4a9e566f0bf886576f0dcd6bc881a047190928b35b92b961b656c94d6a7928da20323bf9705b1845380752733404f7006cfaa57f8c3b8703b524069170c7606965e813ccd0926c57e64206b08742f96cf9869ca66a74e431b524c37abdb0d996a2516ac28a8540cefa2e4e07b51f3141a99ce5a016f746450cf7a8987fd547670c63f2466cb6d43e87028480d85fada19a527af6146e45d4783c4a9035b112faa8aa7a26b290540d1a6d0a5adf8856039f44671fe972adc972d65cc646512d0001eea9c77136432cf700ca545925ca9d97b6ae946ffa1729a249f95487b07a812d41c68ef79a02cfc60786abad9d87e416aee9d29f9686ee44941deb46e74e506a7ca63a527b05e4271530a62e280ca2f16d156feac10b08db0b11a0f599e3cdb7aaf60b9c7359fe065e28a4173df0e2058787c8d37cb93cc6e9e25fcecaf91ad1d5247488889384206a7c35796608f8c5335d7f359c1787c398e147d555f0166b332027d6cb6e2fd603b669dfd715ea422aad8646b16cfc2c62afcfda27be6a7571412a68060820da07c2f13d1860e96e02ec683ba5df89b5475e8382a2bce9a3146c6015183e70ba6c4c53f07d93d5c5cb30d76b69ee4a07fc5ea698f7cbe5bcaa30896e8e97cb450bdd2ec70f79ea91e637a44f0552f4a2fb8a286b8f74cba0ca56d794e80a0bd700" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "306ba473485f68c11702bd3d52446141c38382c7359962cb0bae9e3c1c351227", - "proof": "e0538ed8e5d38746f129569b3d2b6f2e8a83d108ff5ae91200f20174f15a314ff60006c985fc15dd34bbc04c88583131d7657deddf38525e57af3e14c09e34354eb6dc7a17b9a9275da83871f7cc0c89aad770d6c8a7815711600f1d1aba3f18d0ab0fcef9b2908cf7b425828054debb956aa13152123df23f9562475bda9756bd37483ad08ccaec9596f42724c4f32e2e5d7cb487d20026822c83dd3156a105a437d65ef692e7e9f0536d9622d982a6f9a26901136469de6e48daa15454550272ee15a2e6247dec2bc273aee5675e42c57867b6774d6dc8e7ecc63f9357b90500ad64f77ee1f58f529e7dac7ce420d65a74aa22e70ea2ec85ce24d37ed2014f4c251454b1ef4ffe45bb2cb5c3d0f427560535af371508cf4832bc28171edf5f1e87e1101107af65c0cedfe6ef698d3223a69a8d3185ad5413d297bc1f17524dd21811e32bb3cbcd974c947b5dc3fca590c652ddd0062b50e6b7226460dd3769be954f05587962008d619ad95c516faaa08aff2de6ceb26bb1ef30d1a26eee1e7e67ef46f726815483764842849bc5f821cd2ce9607dc9e4921f785327cfbc0f7c5e952c3f81709ec195121e53e8fbef7115811f47506da6bddcd58b99e3bb41c414d0034f2fd6c1c0793c8c2630710feb02c46c87a97916271751d979ce957cb0ef991708267e252404175fa6988dc7901c54d3d73b03b196a18db806a96d37d2b63885eb2686e0c583393eacea3f159d21340c9e4165f32860851c6d2be803048ef243393c348f094834031bd7b40e5ac56e9c54ea7dff844d503c070f78737090a8ed96afc08d003ddd66261c07b80f83109ca327285a18c055bab4722505916a1fcdc59117073cfd5b61a01671d9a15a267f9efdd29eec88ee5af8182d09a39042a279f1133f13ffacc598a2c46e45cfbbdc066b822c6ad1bc28774edc09" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "529bacf7db2eb62e198e3e0e5bf2f827171ae13a4146015a6c1b588318b66c66", - "proof": "60d37c5e4adcab9e7a9d8ae4e1c9c22bd0d1f7c279da9d1059907a89e1c262306ca75460152afd462db5fa1fe029ae0797e72a2b82f82414b1532031352ee003b2367893293e91e8c9701cbe28de568285da1d47737c094f17e34a42c1506140c8175af84b3817d93fd1a67b16eb3e87eb283d84b148d697fe501f45031c672140d236de171d2a20bb8b869fbc16f6e93a3d8c9d79829438daecbcef01d40d0c6dad31df948d7aff0d002d582fd1080d22df68154f3114d0c11c6029a21ca60ab44313f1b43c007d8534398ea8dc53cbe2087c9d3c1f445fdbed00a28e696e0b94913a37e521d06c1f8f47f3d0a9b52b2a37ed063f7bd6ed9fa130dc0bd429062e95b1688988b2ad318432bd49830aba543440b21a784ba334a76448bb08617fec6ffd5fcf4134be0646dca4641d2f2c3ca467a4012c6f3508377cc01ad0a64190d79e45a2006952d4a8af8bbf77d01c99178e7bba3baef71e637a563d3ab710bace3578aaf38ed25f28e689214923c9bb86f3b5f649afa993db26e2f1803d2a669553de3e9df2602b72174bee1aedb729c08fcd4480a642dd68f87eab19d0386e8cc02b10d93d03464d97033560d43dd7c987fc4729fd85f81d890148e8bd29c65c8a6c8097a13978f4784dc297d5baf49b5d4f76dac12c84288cce5727867854e831eb67be11f2ab5e61a45b4fb41518fb8d0e14112c39403d64bf17c06b167a59574b2806efa814c4960b0875873e76bc03bb3b48e8122062ca62fe2bc43732eb31fc9dcb19f8840c1db86005a359845fa06b7bb528d5ff1a6235a1165747e680f6832e52f1997f37c90aeb961c6646d5659623aea3f5315eca15e72c9474ee64b1f3875a66730b552f846aae963d454a6fd3216638c1270b61e67602590a5ba33bc207135b96ddb0509a00a3cbfd4dfada534ba7c5f410555b7c1fd8f50d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "a6937e0068adba8bc4c9b8a8619a07f3a82ed681aaa496de70c3ef966741611f", - "proof": "4204d7fd411c7cc324b09169d9b92f77a4a10c4b3f02c1cb6d452a71675301792c26d3b51f767b557f4c8c6c8f9c14fa027ac4f7d2ceb8c7c4e9374aa76059286ad08aa57c04a226e3977bc8cd383f5f32ed56f099eb444a54a26282056ccf28be86b70ea952a1af441dfccb24b133e1e7decb5ba4b1ed00fbe493f3df21e8493a90eb79b78806b9fc5c820687a0b38a79539ec1c906be90d7cd7bddd64316056bbb2dcbbaf3ca12e12936e4e5d0a1941767c91fbc0e1702d93726db3e4c83076017d5545b01e4d3c1def826adb876808a8ce398028cda240ad86162eae8b6019e90af3dac3a32b399746f9535f27ba9d70ac12a6fd7141d9de1e99ec3f788148eb75d2ade67035da84beee7882f564fb9c7a9624ed978e7eb94f2ce7375822c926052c70f75dd229fe3a96c5cb7c96160160fe9b5bc936f38ae64d27401f25742022b17b6112367b473fc97931c8c33027294b66e1412c6960ed03d55f50a2a606ee475776f94790564cd5973dbadb0763be5301bab6d92a74ba0d1c098b062f8780c5730d48b085342526237f97e5d43e4a13efa6661ab23fb3229f82001680038a1e61949f2dc758ad270900a1ee14390cd5cdd6b159fedd5fb84daf4133722d4356c24ed789f7be4e1641f4c8e97a52772c6ff0f4f8e379dc5b2bb83fc765e6955d12fa35eefae4f081e5dd64704eeadbb8b1bd54d0f71200fe894b2423830d31618918220cf6ff8f46371042cb70cce38b15aac3e108c79abbbf1fa12348218252f00d3f36f5733490234cf3300de2386cfe86aba495feca28c1e36ba2fc08b78f317327da527bd7658c2ac889cebd6da6348acd0f8c0b7c22bb716db2c4d1bef02d2e3654ce466843277cec46807636027c2f2fb9099a9fc1379ae0503df9917be6aacdeab1376d14d1f66e9d08ff0a322349bbdaa273228a26238f70d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "e6d1c904ba1802f778900edcb9fec176afa5628acce6a953ad2feb430bb38115", - "proof": "ba5811c7b4288e758667334f9fdac4317fd7f97d95d8027a6ce3d7d9f765c25138230316518ac3fa8ff23c5270ba590e4380701069d1f928441f983525b9791bf6af53f617f2233703b6f77b5ee0c4e65ca3bc21cb767bed779efb9eb5037216a82ca2b592fb54ac3839fe301fcd819aea35c656c740e2a2962a05953d5973707d7fa52ece6c9f23379cdcfb8ffc369a82bda1157e00c3967269ba0d5004eb06a2ac8447b96030a94c86f9dd5c27641aeeb83e99280af053bb8b0a6ce48f1c0787e5879b280cccd08f7445eee13dab446a5c24a399ba720e7b22ac405b10b208626a6b5961c86b2077645a0b640838e04c96620daaea8d913e374fd1a9965824924b1f527bd78c76cf2e852b7883b016be72af9e94289abb485b73f0253f0d47dc803ecd9e396ca3ba0d06087c0b50836ced184bd304325e5495e1e821105630dcee3603e33eda30dd9873d5778cf9d061689f8719a189e8b24a962146b5dc6dd6cbd9423b55e6b6c92efad91343e2cc1ef426127d5c3976aeabec9b0e685d375cb70e3f075d6056cd768d675eb689d5ba5c66931aa7f11dee193293838dc633a4c8a8b6c976f4ffc822b2da9a65ac24fd8c1cb36d8e63ee75ff83bbe337a4363ecb3bc59431d3356bf7f3bb0df82f1a81a3b0e373c34dd6f5b73dd2981f7c3fa0ae43811cbc6bfb2b9e148093bf56cd4d278f15beaa3e4ebcb6bc9117a54e4f3cf1244d0ed511d88dd7cfb1569c9f4895fa569cf25fe46ce87d84b714f74f5eba6dbf31408d70ac171d1b2f5ff3b16d9abc2031b10c926b7a059687c2e7ea462a48b7bfab4198db06bb3d178e7b631cd73d709a33958d6b1f11c3e84bb8c56e1b4e5aee9e38a4333583f35e191ed2b9649e2057a3152ddad449d610b0553e0d5af5a13bc5314f519c38780dc9b0142378a58887d820571d94640eb3e8915d0a" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "eccf2eb87dc8ac5eedbde0db5609a6dfe673df56f906436c33899f878f63747e", - "proof": "2c351f6b85a80ec7583935ee392f540ddd9ad9f16df6d19cb2ae1a758eb1e4732e0e8a5282fff0b02237a7bf88ba55021a451a1b8aa7c0d8401e82f925e9de47d4d332ec7c9cd5820b90eb2ebe8a44e0fa773b6be223126add01e985f4b724293a8a63cc853e83844dccbccfab0cbba0f4042540c2249853ffb4670d1f107a3197564630ff7aae05a6db1c1eeeeb00a1f6f840ccaf9347d5fcf70a1aaaa7b0051cf90a8847efd5914b8d927786042f579080f4ff0c1fc841ebdb04ae772441007486ca49d4ded63ea4bb0c29cca3e302ae5187323609d76ecec4e04da31fd506aed1debd0eab9dea785fda27e367d167bd5ac69c03591e57ff87f8d6bd93156fe01f2c42baef1b840772fe913c09f4d66aaf5e606148ef3cca0771b5724d091ede76f3a6fd65f8bcd0eb7a8623a63cb9cb3d195e4f43d366843ef2c222b9dd695ca205f21e7f5d4ced9e9bd579291b74d4e474c3ec9bb453c30dc460cc1784326c08d119ea5eed17507e42f09644d931ec45b0fdfc9dc32c15989aba4c13a11f74a8fe2718d9aa2d081608136db7ee7bcfbc9b4e7a3cf992bb268b2911209468407f6af94bb609bfe557e86f822cd36a901f62a70d15032edf24102280099b766a839b393c118bec3925c8fea4ddd19ff4a85deac7f1e36771756e8d9e6cf46caa997273565dfd55efb98aa7f0f221a740a7ecdcef808e869e911ea22e643a22ae90f6ff5ce32c9c0c8d1e7a8c446795ccdc51f9486b0dece7cda47e9526cb031ca3f59fbd4d7a71b8c2672b60344bc6d074042af8bfcd722a9f5067dbbd7733b433c07808baef27ceddf48f466bba0f5330e69ba157f3c6af8f8fc97dbe597d0f7d027a4c2093d418ae7b696346abc196cb5c9d8c5d130ca6cff56f4b7e9f071ea063986e64afd47cf9a74efae555869e4391cd4554e3c830747479d0b48602" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ecd0228403c39840b710608770d893115655d514dd2a1bba0e78243f8dd3290f", - "proof": "f45b379878cb2e4db41f212c5fa80c2095ee117c15ac84bd34e6601cb8683750e692def868c5ad3bbece825d85f11dca62704938ba585beb316edd1720513d228c75f67c30464643024b41ca918792bff8493fb86de82e69bcf248253bbf8c338a2d08c59626ffd9ebf7af56adb5dd5d309b1d0feac7c766e2a9c92101cdfa7ad610febcda8504f4ffbd33e46a53fccbb9894681004cdd5a9f5ee9a5113ebf095bd430b37db878d31ca55570b26da3d75bd0b09a026942141c44e78774ffd9085c0bdb68c8634fe8b65a92d991e4cfb81cc0891ef1b01b10170301b2a3514b0200fc79ffce5204f5eaa7d8f26ca360c8d1a88f1c034a0bb34fa919c1a6f0a82972aa21969a84bc6fd0eaa846799ec63ace17c0c9923e26d4cc58b3b38aa73374325876a9e1685acb1e8e67138179fef368056c20571bba073a14453e841e2d6160a762907249c960b3875b892750eaeaef6a85deacc635f2c98ee68a41186629ea75b30bee3f14ad8793874cbd3ea4d8899422eb887e907737ef3f0b8d23df77f2c2bf8ca75ba233fb5a84c49c4bdf8067e6923b569219c7d10848e99839ee2212b2e1cef4b83939b559c71fc75ce289af7739016e2c56cd37593d0509fa7f58660740de8f69a4bdb9e43ea2b768d50933336a2eddac3aeb306ddd016880e634f465402a8c544b20c1f764d4770f15e35d1c27692307ba49bc9fa45d9ec9540ce6823e98c063272d95a34c5fd569131607053f1d56731498a73ff954117fc167f6fc109f8e0925a301aad1e14e15736d7f941a8ab2d9b70c45a0b3c089e1f26b787bfe3c4143562c0331e0433b9bc36952d83e37c9725c850b43584acab5c207b3beddf1e690a123f8512a17ade1e17b441d746736f65418efda51f76e7e83014f241103cd201c8f5d3a13c18ef9d9c34c64488c8d1d635bd38883d8b2a49109" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 6 - }, - "commitment": "a4822003e6a42970092bbd7f714b7b670c0b1039e403e2bd8d38b57d8ab2e546", - "proof": "1c9fa41b61cc56dbb965c7563e11e11a53a9ca00ee30f1b66bf29565a4605e694ccf883389fad575f3a3d320554bd89defa383b2312d459dad41c541e7dca071889343d2e9901c8319ceadf8a6f282aeeca3cb68b51092e94d158b1e0acf83366c4ac64b973fcdbcd1c4cde50710cdafd943ced450c1805f2b2627864105530aa3faab8141070c6e08aec1dc61176529d4ba336c483b3f5ae0eda1c49c605c07129a2426b2953de8f965e761aea4c8c042454f949f2db817b19354fe28b40f0f21b15016e907b9aa4f4d1735cf4c00f7638b7f732d448c2987e7d300bf445f07a409886dbc239be027cd7a81e3dde68f6f583d0ba3befbec1528165af0683e7fec2242daa1c69dc482dc0d74ec6cae80df7d9d7758f4f55d6afe1e7fe63f445d86d03a6022845e608ec0dfae934c0d0b492da59941982044b278c9447f602162d4564a4f7dbf0a84649c116df3f38743ab525895ddbe00a5799256e3514ef414b09e4cf1069157632466af75a95a7bff3df2487221ce378602631ca8ec48f22d46e3c6a7009ee83f05dfddac9b029a4db3c60cda0bdc04890f5f40477f57a92c34b4f77001295cffa94852f9c1f3b3a14ff70c10da605de3d818513e40132d6a5418ee71fb1d40233644c306b8669f82aacc9071b452ad28523f2b672317a05e98361fd7ea5a7189faa2ba7b623b00c35a7a40fb100a26b9d599f7c7e79e0f55fceaaebc6c22dd7a4fc90e94661e88c729734499d132b87949bc2a9eadc8b445886ed6dba5d87148533f7fc9ccad17ea348f1d4ffda431e9897b762e65294c3ab0808226400594568d775b1cc2d2f95970165fea9600021940741c19ee73407fa6170a6189f5836b9bdb71e452c218b2326198ee10fa174dca7bcae8deee7e0b2058813722346da23cd05ff00d5feaa2e4a68df6ab963cbd4401c0b07211a40b" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "0468c3f5dafa2317b5db3b149c107efb987af0bc078455fc3ce81a1982eae139", - "excess_sig": { - "public_nonce": "2a37de7a6de3fbac5c9c12bab13895e07159b69fa0eba012cec6773a8d921909", - "signature": "3c7a559049c5c51ba26c0639599e5d835fe290360f4649252fc6436909daaa0d" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "40a096dc3ac0b5d92e238e2f01105503f2a7c511bbd1f5646180dd1fe024600c", - "excess_sig": { - "public_nonce": "b4103a61035b823255d4b6bc3b91ead6a02f539ba501d88713e49d402ea40f4e", - "signature": "07ff2ef85081de5c04a930552b9b4798ecfbc106c400c177b9686b14bcd86309" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "7e0de9dbd1abfd850c62c685db9e9634862a369ff3430ecab39471c3fca03d16", - "excess_sig": { - "public_nonce": "16f5435b59ad3a2ff09f865140bc8cded1bd449d25489e9dc5e8efee768bcf67", - "signature": "152ae52c14a435a22b8bf931c30b4efe47880530621f8a1171fad0ba0906130e" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "926e8283313f3f0a11ae48d65b95e7573879df86335fbac052c4b2a4e68c5f78", - "excess_sig": { - "public_nonce": "324103fea0463cc9a0c18906b0111c09a8fa54674361f46ddce1fad801d43308", - "signature": "1930a3596a587b4f1dd2941daf90a51888613c5e83e7972fb669259f646a230f" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "bc8c288d179dde69a29a16da46fdc0657915b73267f65375e72ea31b2cc7cd75", - "excess_sig": { - "public_nonce": "f272c8fe752092c2e10d7c7f9cb5502f7b546569dd82a8a6d8ff2f3211afad65", - "signature": "374a9c1921348fc8fd0583a0818867a46175f35c70a90cc144a24807235a2702" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "42edf6b866ce22c4d1c1a18987fa588fc297f0246ac3aef4dd6f3016a0fefb6a", - "excess_sig": { - "public_nonce": "887fca749e4c667752e8cc8f9d9856494e6054dc07836c6530dac8bb650edd6c", - "signature": "3de844788db04480ce1af67f10f05310b76616a1aa39e40e353b7c5c4e3a4e09" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 6, - "prev_hash": "15bf761e8b0ff4a722e0b3b339ba6cc28022bfd832e6ffd9a4803caee6ba57c9", - "timestamp": "2000-01-01T01:07:01Z", - "output_mr": "93ff3a6db3e65e82da1809d4fe80c6cc1b52c2e0c458447a378e906d0ef0a757", - "range_proof_mr": "12dd3e33396c00fa6a4465ab300bcb39864d043d3db5a6914f501436289d9f9b", - "kernel_mr": "28d5f23077af5b34ee225e073672be445c95903b23e818fa4c58120674924963", - "total_kernel_offset": "35f238fa0daabe9d38ab806602d7d58fc2b06e7411d00bb627fd273528d2a202", - "pow": { - "work": 6 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "06b695a6bf4d2bd1ace456cec231e402e9b2ae329280a3e5da780b555ef72200" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "12dacf3364c226f29a70649f734dca048d6da94cf76cd081b67b41d9a1a4e53f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "2483324eb3cf5cf7385a50bbf408855a65132e108b65fdee7dde51144479fe51" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "3054f4c914cd7bbb8da287fed8d5ae1f8ca4c0e0221e0ea6bc1ebbfef61f972e" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "529bacf7db2eb62e198e3e0e5bf2f827171ae13a4146015a6c1b588318b66c66" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0a281b7f74df13fb093c19cd4c32bc6b21936739ac95a973dc480e3539cca272", - "proof": "387cb54d9411a72b261899a4ad48f8c7ff10b98a7be0c8a08117c768be20087f92b9bda55841562ea5c7efe04d8362dcdd8cf35d2e011d773e43904faf21d620fcdcf637251eacb0ad5f22af307082d6aeabfbcace57bf10d32fdb74c7178f7834047dc34c1b48d52afdf77c391ee0cbb07aa3d5f24db9ee1b555efe1471fa69ba814fca08809f2d0ddc2cfbcdbab945f1034d12b88dedf1ecf47ea955a8ec0dbf08382fe0174012c4d29b7709e8c2e485659f3257844c0642a6b4c1b2a29c02ae93c5938c0adae6edfe29efc5bc3bacb748447eeda6f4981d94edba6728060d5cb50a503f7a8953ee2b664371e1d8abd1ebd8b212c75910fdafdce53459c221721a76df02bfd67356c95519f5c9e18ec5a800df05769634b8c7708f9d280c37aec08291d1146173100661f430b11de560582ff8e96dafff64bd98977415795306fcabfb0c1f1ca4a1decd0adfe4eaf9b8adde1ee616b5b717ba25278ece6901767e99319b493bff0a1f89aec59d3450e4face1c39844d9289cb4af2d4a15a36040906ae0de9cc951415e5e5bf18e8776ee40ccf1e42e717e25bad387ca3fa26221e36b1f6e5550a1e80e3b3b0f398777f56e4ac7161ee8e9a877451c0facb724e82f13e7ba2e5affc5e3cc6f148cad5ae55066273fead9888d503226384523f52964cafb56188c42bcea05cffac0c8cdc1b31b799d7f4542d7df325db5c993a2e282a712b9443478e8c181e6adc6463ddd735b42634e0c8a2179b2c4bf36209c8803d5ac4b10c1c7f90eae435ab55192562a2cef03eabe720f0f4679554d00afcccbc44f0967e2fe20092b154504fa7d464065cec563c78f792bdf3dd86c83cc383c58f27154c062e738174d8071aed77da3772de9d28d1a4bf82f8289ef2010bf2f47cdc4ab5a3165ebde75be87398e7a8adabf193317274fa276490aad30d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "2c9c84231d10cb8d09effec1efa12e807b7b5ec59cb96caab7a1b6c157322a4b", - "proof": "e0b3005c68a829d1ae649a117e48018a2d65fa0b06ac854579feb31d2afc9d1d3eb47b4a4abcce5a00b94c787cbf91a05f3661ce17980e08fd443dab4d6f746e7402bf3574471616b151c5ed83e1008631567d8a78a1b8435f90a25a1ba11927baafe4111eb49f805809f68f521cc076060f6a90fc1a7e42b3cb0be29cec155be5e37d150dce51a38264966a81a9941295a6f3a41199604f1b6b855641d9df0c878effe729688487f017f59f8e308f61124da872a5622522a9ddb6091a8378075382b898ebbcf884c6428c5c5ed59346187c1658b1465b634ea7fbab0ed8c20d82b51ff19c1960bfe8dafea8b441c540d434f11ef5e7f7dcfdfe35c30542813e7c3b1db8f7d60c7c06b43d7d4d780195cb1065b9a271b8f749e753e756a95b091c3798f3e1694bf7a804455568ed79877af1dd485f608f9f088f868f7745f431ce395e7778f46ce786b236c3579772a17b6e7fbb85e2b7035cd5b479ed5d335ff4affe1ed86183dd530467e6797adbe80a3a14e95f2238d23463776c2e95ad7c3e4dda44798e63bbaa8052d1315347ce2ba048e8b7c746716db56c28c4f9ab25e4c7844865767e5c77db5a271058d44efe1d0569e5161e3de02ecd38332497513281d3e3aca31b7bee5e3f63ca9569b86c728945815982066d4cc41f9ff5742340b0c9b5d3bd9fd214a6c4f4ab3cc482e3110a637e6c4ca1a988fdf07c20fa6384cc4cc3ed3da12d8faa3b66a0d77c3343905761c953517c1e7e1167cf3fbf7c523726f64c01123d38f391f9a476e844623cda151f0b30f4ce103c1ebfddf024b0b280c760cd86fb24a4c3744196956c41992153f24ea508b19ca2f030dc24413ce278783f7ebe876bb47586583779721c81a7102c1b9b232a62d3e715e08702fb9cb0dd001edec109eb45bfbc1c884d05b26d9347d4dd04ba3b6d5c0814b50a" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "4e97f1f76130e298c9ef877c250dcc2673b2ca6ccae84aeff3c5cc34f3018076", - "proof": "fae20f4831636c20b02d7549af36e71a5d060a627b3c62022f24a266e028fd511a2ee919ce9c5dfa7d07451efe568ae46131e2c7850cf4500e3fedc62e9fe22d9a0c754b08e258aaad2efb5d78fed280fffa6e875e6e42c595c8cdae16c3a56ea8d570eef3f50305b427d80a79c8370de29e31257d75a697d0e78c0fa6486160a1e6199b2f0c24fe766818b8e10c7ec892c86e1f8847c8187c3a8019460c6d052efe68bce5cd30f63f205a6b537e1156c60e0f6ec8fadd91009b42ae2d261f05f6f34af5783be00a469d3bf198ddf90c3535982428cd9dbfd633c28ad581010a346803529ee07a18df6c5adc4e002e8ae276f027e6b03ba40219ec6c7fd9167a9a7aa4ac0b52d0aa83548a464e60ab2085d26222bb41275c267d17afccaa026ad059253eae0da824fa29f5503509da92f179c59d9a14848f9909df66489c640c701870611aaf3f05efc6c3430bd001f5acf80a72b5c79d031214cbb2f51b3b34fe7e3a5ac1a3322c51dce6110ddeec32efcd898417782dd603614b15e11854465c1d94dcf922580ac6ea434283b4be402425be29e5c29dff94caa743214f67495e8893bcdea6c3237a2242a87fc974328c5bc536e5eed65f0ba8ce0e6638150b8817a844ba5b97a98c9ef0eceb4529a35dbfa62d9adb3814eed57fb3208f906030c5b58826b5dfba89abf596256f99a075280155398fe2414de644cb593f0217682fdf10fc65f3d05ce3488919bbd26a3f6e4d40e7dab92136e678edca284d405a4a98bcfb042c5b8f19a6c2a69f2142ad0d4c98b3cec4c24af1216afc6fb844a68f4b6617a41a455252cbb1f89cf69adf4fed2bddabef483c283662fecb0225f5aaeeff8798e8619b1031432062cc5ccb9d22d0ff4d747df1e3b8d7a643bb062cc3854e527ada578b7748ba67abacc5bc926449b2cb1c61ee31d58bcf9feb04" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "529b927f4f1801dbb81c762b36b35bfb24f32aab338e727b4c973dbff6311964", - "proof": "1e23e515ee1c176f96d273259263061db9355510cee58bf9a9ac310ab13d16688c40afeb3ec7612a1b2cc1154de27ef25352759e438aeaa189c35bc1cbbb5a724cf1061737c17e8a694dba359ff39c0d188e65fd83057bd44f0fcf84e83c2561305e74f4385e8cf14bfa565a6ef7a0177d479e5772c493b2447ae1673996ea6d2edde0c6beea2e7ce6cbcbe2258654642a084c80ce4f193982903e3f6f114000dce4939871c218c7a2cf1e0046bd7e451d962669dd7bc586e4c5f4cc471fba02911d1bdbadf9ff0e5fad312ca55647495466ac03743cbe2bb638b0b1cb99e90d4ad1d8efbaffdd3a7a8984475701c472c2b4571c1a27e345fab232897f169878c056586e0bab3b05ca98bd4ccef09c4139daab068cdbbd811a3d5f01d52f6f6502e3078fda5079b9d54851a0206381ae1a5a0b94ad2936b47bc2a6244563906bd678cfc7376ca8cb71d5f0953e85542db0ef6cfd0fa3b6709a7a60e28fa95b4afec64b76ee09b3a840e8c85d00632c2ead6a0d08f630ecc854f267cab7d93257366ace083b32ae946126e5934fc68562691d12f90ca586dd3d6d749a40ca0b06de94cb42d874729de531890f78006df109cc4519488ee457f3f1b92e9e3316266ab7dbd0fe20122718ea19195a5707492b760dca447a1875aaec00c5c429ec36365c17464c10fadb15078f2a64a3fb71795b896a81acc618f161c3feaf271d41ac44cf535690976bfbb5fdc89302c24147c063b9fc9e752e4605b2f5905ed4780e2a432181825016de5dcd5c558d0a4b39bd2a17fb4a9a0d0d41e79d97978f73ee9f4740b3ab0ea4b917ff50c89168669b5a8969a8b66f42ac320e2a90ed340f2ce102f851d43a471701c65da95ae92da057a9976deba506ae2a6ec332a9860bc70ac42d91207e5a1cffd94a6a9bf36f4ec8857f8aafcf34195b6339ef207a01" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "866ff9fffae8304018f65082409bbe533dae9af7d6f800d6a1fa68ca83ba7f23", - "proof": "74347a2c088613b3aa7fedc52bbd5c3602ce6a6c98bf418114563cd3e1f146055c772a0524a64859840227ff7d32e8b9dac87e1eaf398d984cab5d7dd9f97d5a7c42591f8e7b634ef7152525b91189af93503c7f3ea67959459973968db19b7e8cf6b18897b5031c5a68db15db0f1d8609a2f54523935bebc09ab205c38ce228617c0c804e59691e9bb072f31923ed25620ee49aad3d202d934ab23fbe1e5a02529884bde151a7f401dfecf7442006db8130bce5d8d9b17ccb23c016c544f3066d87f6ea6c6892e6f5b527662f12b5cadf5514d804cb799929ea99eb4acb860942f6e7ba36662aea4df0089b2ed3c7648ba0fe2cc8eb2e236c47730ff067697d0ec8ccb9aa80ba759f87a5118847663fc2e2df03259e35065b3f38d3ec25102fa8bf3de03f20a9b57c31de6223ce00925981274aa1aad647d67b90450a45a81baae0ad6d47bca1be106d6334a51183476736a68bc159297c00df044eb737a504de7aade67d06f0f8ef49e6e7e9414ea7d72013b6b6339fad94fc789b56dfd941ac2b06af6d8b9b7070ee816a12a1100079833f785466720114b5d3cb7f62116c429ece857299b0aacf221a9df202b88c6486baa4f11acdda388fdfd7fe7e7d00d04861390bb6310a8e58e953a4fd48f8809b8f3d1276ef3ac35d27c3caca162e788d20d95e227f7ee986fb2dbb722b7961736881a74f79a921df4f8514676c2a1a2b08bf75e60597714673ddc0388e646f6124a31dacdb1ad88dd5ba2041a970d2188d290c9567a29687701bbf16bf13ed5d5ecf57c19be5ee9235d8450f4329a4f042b772050b55f6048f6fc463292a6917973c4721ad773040896ca5cbbb2ffb96ce9d3324e41a03e8d71e3891f6ed613c26796dbf42286677f5aea921960587b447683e39a3dc3f24ba537f37c02f8601c1fc5c2c5db86a7ad9595ba74508" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "964fbf5fc20fde2ad4ba3f8629aa259fbeb1130f82b35324bb1225c6a67fa374", - "proof": "06ffde2169c9e8319282e229bb8a81ef2a72f61f92225688abc792d07c62cd03241dd1ed121b36a27ea3ba76a5690e2a6e37d00687de0eb1a825909252d79932c83164919c81c65d3dc6ff9256157f6ed8bc55f47f9cdeb8f6b5a67557b08b1f0497f60f64099d42fa84774d9fddfad35c80271026dbe7e5882e1c6ec7620542c97023385c8d114b5f5abf0ac45fe387864224faddfb211306005d4980b8f90f840c67253b7d6677ff23790f603594d75858b9255acff7ae3debc07815d3f708b333c0700de1d5b5e7e7ef2f797f810e4d95666d95f55af62ed70a64850c1f0076c85ef2955980e1ba3d70ef58af513e91bf1a05a8d23b7b2cdca9f1558cde3cec6ae88a0e9bb534d864cbd3934427d8808be901a5ccb1444d61e5362a8bd7560a9090cd059f490dda2f2ed6e0bb758637971eb4aa130d46c3502ef4a75fe02d82103b1e3301cb3b74e965154f3137b0e44cf9a754923354e4af425704d8f1724054c177805512f1981d86670c96b603bef077c634a22dc1e9e23a2f77d0430a3c5f4b2adcbff76089eca7ef110ea97cd89ad59a7862bac384a8ea35b57a27011c78d2a635d6b7377ee22f55482e1c8de793da88e72473b235249578e931be752c8b884813c018601f2b0e220af9e4c7db9200745eb07ef3b92e17c0cd061b4a708109b9fc8a98d6577320ad36cffa4826f93b774410099d0ced420ba00927756a33a0b9d6e124f8fadaccf15bc5306c6d9ef81a1e85b9f8f1694a25be7b0d652c2870703f9eca53571664fc687fb3f890625fd4727ef4e909a62924b7c49e1a08904d093115256203100357e730d2bb6067f69cd4b2608294f5c20aafa92315db98a12fb7c8b3ca16fa7bb328d688c837178a45461e93dc54ccd1095e5a5108bb43f7b5fb8357d75f08ed4989fc1275da77c26b709ad31da98de626cb73bc01" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ba90a6a42b25105d3690ca49756294da8a389f54c0a0e0593e7fce4b9a2ec231", - "proof": "6a48bca303608d1b6159927424cfb1792b08b6896c99c09cdb15f1669043583c04d1a45ed66fe63894ff61798cc941cf3286accdfd025f2dd656e04f9ef73d7e52f2f8c568b2be446bcb725a493ba65096e826944ee3883fb2064dba02c12665e2a47b89188759efeaadfc9e36bc33d7cd9a30889e988dcab3658ed96885935bbb2ba4507257e22623b65c8f014ae16df0dfb958fc8a91af69a73926e1a3f3069ed65c14cd6e4745d4a3ef1c23ec02042ecaaf78575cc88310022a5ed32fe00580f82b94c704d021f9e8520fdb3cf9fe7ee15396d1bcb8e12eae2eafa36e750b8c57efdd3fe7ecbe84804dce9a4117a4cb1c9807ecf5bf60db2b8b15e09a80049cbea08fe8897d803693423da239787e7e77912e2e3dc625b8c996ca9afe6060207e40677105752e6ce6aa9829097d8bd3134df78d42109a0b1dfd72c436d3684088497a9332701090235c2ae0c6a1ed3f2ac53c0b5d87c4aa0c6172c071e523f6b87c0eea62e07d2932a5c4352bc93a162d6a1697956c0f290e505e1b373519a2346cf3021e9c91ee1bd8635aaf5277bc7e6aba4718aafeb2ba7491f56735674ce7119d633d7c40af162dbf4e767a4b6ceb2f88d17e089553c3fa37eaf60d168894d37b26f8cd076f6408bf94aca1f03882a5ff38ab30e7363c573dfbc12112f607816782dd800b05847a6f63b3f93d2befed0fb740930fdf8fe85379c6675068e58e2a1aa7af1c75442e0db974ac586d8f8086a0523fdb2c495b23dde34b1092905e7406dad2c4e6ae54d8b2b08cf6ac17963a7ecab39842d88fc15355fc14c05f4030bdd37788fb419036277adc8c946219600c33707f6aa07764a685dc1622b8c2611df4b21e7d5151b989b3ec344bb5bab22f4d94bdc6bc4f81e5fde40a3b5750e67eca6dddbb4af5344a630994ae8a8f78781504153577690646c0bd0f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "bedd01d88083d065de1596d3b99282983ffe1e6d4dd5ab813c9d71f8fd30b562", - "proof": "a0fc3902ddae8f5ab88a637bf603189f5f456692268e255f79049d28bb07a77608d1ca536efb05b355517c8852c4228b24d58feb66f049ff7139f04097e3f65a3c9d153b007d6bfdfad682cf3684b8bf1b8df52ae60d65efa98bb4d745ae587596639100487bbc58b992dfd39ef22f6164eb7cab7f3e190ea1c01918a0d4035634055ac249a3e687535f9fa308c1d14991e98624b21ae092158281f3942107012787b7b377c3871655b2ab25d29ae421732c2f685e0caf2d61be97c0e145df0012c535c0b98f87c7f6ab9b6b0f4ed40954fa97a0d94b3c379674e4b33cae8a062c5240101b913e543cdddadf6ba5de90d2fb472eb89e327b36fc2de62e2a987c5a6e9412a9234674056b5c77c47fbe6b82f6c7b07a42d3fb91a71f31e705565338ee174c2991cfecfc97c0ce598c8ea908f081c9835065185b5dc4dfda2e2e4c3e6a4474874047ad706aa791df47c7e0ee108f88aa8df91b786f97ec161be90ee21adee48564504371b95001dfa0048543d3be6572ebeb680d87353e4649c5096a3199dcc6dc45eb4f94df1d17372b14fbb2b8a19e5b3db3c5b7454d42d968016a0e8a421bfddbc0eca7f15fda26ef188460fc72fe787a202b1a461c7209315bb4ba869893f6cacb1f6a76d6a0a1a8754494ba53e52e49dd56cf96d0ffd60d6e8858e183f2ba7ce93def92e70c7b7547a900a5df136aa812d30724039bc01e0948ae961311e53d3cde1a08dd267646ef1526cd40fa65f677242505dfe6420b4b0cfcc74e32646525100cdac8d6b04b9d7dfba0c98a708c508618c0d56c37737e204e26011ced824ed5554aaf1b03d18167570f17bd069602251bed83eed68c798543f021262858c4ea1c18509ddf75eb5899bd8d7eb8485c624abe78e848710bafa55b06b44001288b03b669319ce44bb7cb2da340e5ff4d2030e1d7a9ba0006" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "c2f82a7acc5f4b16b8f8a04479c4a2a1833df726c09b9e8ebcdf2c9f29f6ce67", - "proof": "705afde4b6158df771ba781831a5e98ea568ed12c682868fcb6d5368a833ce48bee6f820296a6f953e2bfe7a85b39e26a8ca54a6a8d5aac53f983b26887fd67cb488e52c6c054bc185bc320e466ef9725a6e842484076c8fac297acf2583736e7a6d0caaeee687b8c633cfcbdc827a3a5407b2dbd3c518a548043ebc9ba12775a78c186c28bcc747a111d5af9e364d4c9fec151fa29755cdad42fbb05764510290281a17074d94470c7cc45a8d7ab3da94fc2e47c5014f89d027736ebf26ef0854ee8fb75b5f388976dd7753f98feb1e023256e2035d2bc2824e350dc8864a03d8a8620b8355f5f4e6b226bf631fa4f5ddca5857e1c40cae17472266e250870d3849f6989579cf8a320d5e6214d504ed42e2bea8b9e4bc6388ea35056a7929756a1fede16ef1e1228274b7e0999f402cf25b316a89c156d6c055b7eb0e953d12e22ce5065807c28b332c9ed44736080afab71bb3536a15f68b5d89a0d84b2c6c780ac6c01d12c6381b1f9e30e1b7c7a62c8669d323695bf79eb85d0cb26b1c662ce827476d5195100857c1389c81ae33edda453e2b2fce6c448021a7126b641c0657b503b894dcf5b8261c1221c0d82a6a10a8ac53faf27c7c1cc03b45fdd52de2c163ddd7e6fb00abfa0c10202603b4f3c9029ab51a800e332f766d5da3d44a7626e26e4e31c9f96b6ed6040def72c48f0e5f672b2e8301bd6d3bdc4bfe6511fe16b32fba037e4c8e756c2a19bd44acc82513098f690200b3523aeba3a40c0724fb75a60e804116f374e95ae4f747f9cc931a03da40480d8264e13a04c4d42780e9d3a0839695b0d00dbced5de4ac264dad562e8ee295d856da2e52404d7c5167c3e0f717e9478921ab57c2f92c50d19d3acc933757c63b79eb1c16d4578a0a57f422731f176a632764d30e7f3aa04ad58b8bc9f90e3c8de9136dba2b88f201" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ce03f25709841b22b91008b19abf8e17d664fb7a5637e4ec563c6a4c6e7f7418", - "proof": "08d3ffd9a2bbc990e15d13840b0bc2e8a1f828349b23fb2fac458a996674c500ba8b8783efdbf449c73a3c7cd2edfe5cda178199cacb8addc129832f5683910d4e5f328fd88c89ac83ffe464fc9d3db051b75a1d6b82b799d30adea1a70ad34f4acce67faf370167bf2597f77de17599f7dd2b9bdd71391d88adb1040de4c847709f6449298a73fc1dfd5eee960da8e1a94bba4692373745e3e2b79396eb230e1fa540ffcf96a51d0c0f3d6a71404375f56f879afb909b96d4d551bfb72e390182db9765774152c27fa2cc2f6ec124f59b382f53bc05c752817ac828af960108480de7d82584ac407ddcf5c93e3ad4ceacb302236520dd0f4505d9ce1467fb6dfe1e0faaa1ace2a6284019f9836687177d4baa5daef4d3400af37ad84aa6f535e28506825aa46645dba1fc3b9403fe7af3ac06bff637b605819f3a88100b937d8659c7c247acd656b71564ebf5d10ecda61fc4fdb8d2a65e9c9e5afee930985674fdd8d59cc7cb4eb48f7d379c1f4a5bcb71d83c83ae8774757e70e0898abd6ee295e82cfd429ea5f240b65523056b43c316d9f664d78e30323ad0c0077775296241de7f15abfc090d0b02a387148b98dec14a484844aa5497771f8ed1d6fc6eb43e6dc2122052eef7292e5eb87aaab0a91aff8f6a048302427f36e3099d9821ec1e62723e9587bfce651eb4dcdce5017eb15c5138b25dcb668064fda923d95a706c61a5e534eaf428a549ec5b98bde925db48c32311a8c4fde4ae549734e56d9cc4360fde67cae215e82d752e543b05b19d62f953f5b295b4a3e232c8d20a24e09970d90a9744c862ac5cf8ee944cf6bc2d67db681e070a50582f8a17d334320b897a0884a4e0a449083449e04421694d2bc1cf67ab7d50514ef23d0be5dd0b6ced75574f736f6ce09b40a8ab102d684e7c45a83c712c21472e73628f25ca04" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 7 - }, - "commitment": "145e74b7e5d80ddb4a280a6abe7f295480ff093f1ca91c327ea05ec22402af66", - "proof": "ea90541010059cc344ed1245cf972ec9a31d65b05816b05d0e1b30fda2e53c6288b0e763d8a469f556c61dba5c342d169d976b1868e2c12a379451b76d6fe319f2cb19b53112ea7c192382f2222e1733cd10065627fe012922cd988ba2f37504dcb6e1d7ee6b86968ee0b66d5068de2189888ba323deaf99bf8bd6b438e9e95c490e7ae06702149a3aff018d0eaec7e61e4a4021161d889bc565477f90eb2608cf6253ff89ea5e5deb360e8abf013b9f90e20d6fc0f1144575725935c7127c0a8e9a2f4d5637f32b04f2a16d74960a4c35bff97132b3f80b09eed9ee06da170aae971f7894c4f6ced7be466c3af674771c555dbbebb3fbebc50690ca9932170536b1eeca2ef90a3f53278024aae469534833a3213b64eb8bb9e6fe8b4d5abc05148482121141eee76f7821f5f13ec9074433010ee1416e5d77ee4b0750f8824830f41f5db9f983c48002f0238e5988f6e8c3b62fee0f0642705f341c9d23416e745f0f5e586fd13f92a09acf41e7b17bbb1c3c23cd9b98801db0e1f621e9e358b86b116719cbd4eb0c29304ed26914e2eaed2073403301ddc9e271eda7a8aa4fbaea0218a6a824c6d572036c96b6a86df6ecbe628ed13c0b081493fbec848915766c6d137c0a1e53c311d1575c0341dfa315aa0cdf982ea49932ae7c45795705da5b6c1d417aa578e8caee94288ea3a91eca0ff747286402c72e01458c7fa1094a2c303256fb4b50037f2ac4986e5d85a98eef7be8e8a3801e73884f6ec3c37a1a1f82c23c521abe85beb5d33a08a1cb488ac6d41b4629018282545c0a9d9177dcf06b2c4b5b275b87f32a458657ccab4b32b719ad44ac58b750846e453fd64280a126adf545855ab21bb5f764ac6923eb0e23679c55d3101d80deaf29aa9800dd2a0d0dd481ba5e153ca9f74c7f163b6b7e035f40fe589ae2abf687e40cc00c" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "1e84397529383e9bde69a26c211d29bf4873f26a7fbc1ef2cb8410cf9b44b251", - "excess_sig": { - "public_nonce": "64da7a5e482218fc73bd92987e5d1b545727a1a9e128745c175349ea0dece305", - "signature": "a05a10cd121812dfc1db4bfba16f1b3cd9f0e8bb8e958e887e6efa74cb5fc40f" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "6ef2ba154b42b31412b3cea46bdee43c04b5230e18b1412d52149987dbef2959", - "excess_sig": { - "public_nonce": "647ac318f522839cdfadf12e9623adefe0c218b69d057e0fff615ee665dc433e", - "signature": "90d4bfed5aea4584905969a14cc1decbddf749bd81edb6f60c475a8eecd8d90e" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "98d69dc72fa9f3b87a490132aeba47e5955768ac963e9be8010ea914ce8a1a4f", - "excess_sig": { - "public_nonce": "08e7cd5b19b0336af1fb414fcb8ca1e761021f5c6f908cffa0cfe73462db0352", - "signature": "1c84b838572bb9182f91ce725d1a984d3eb4001f0af3535e727a36a818f25708" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "a0cf8cf03c61f0a58c2d900dad2dd35fe7973526970eb6d26f72acd93553f06d", - "excess_sig": { - "public_nonce": "facbbbb9cb0044e6176855acf4cf5becfcc1db47962086c7d3f5f83c04dc0f7a", - "signature": "2b758f2facb59556ea691cfc4b489effb14eb4a4b9281f45c3cd157bccca9904" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "ea9e46c0f90fa90de2bcdbdeecdf3c631b73b392763720db2632ecf331706a48", - "excess_sig": { - "public_nonce": "9c54316bf573504811540e3eede13d9bb6394c640a2006abe3920360711c3e59", - "signature": "fec03280d30fc6ccb051e51acfdfb81ee08fbd91a8be0aa0ee15fe01bfd3cb05" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "6ec264a88546ad4566f11964f30952b69863341938318e7a1f8fb1297f0ac35b", - "excess_sig": { - "public_nonce": "ead4f25de1969d012716a1185e9ae83297e85f1be8732e702b3ac127f8aaab0a", - "signature": "d266c18b220a7ec4af31fa575e7356da3a0b807ad16a4cdd2d5af00d6fb6d601" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 7, - "prev_hash": "a14e1ab668c846704300fd7739e3be6456d9121e3fd7260dc6e1ef2aa66b7a9b", - "timestamp": "2000-01-01T01:08:01Z", - "output_mr": "dcab7b4d15338769f6a5e2f65599d694b2c19bbdf2a7f49b7f767150a3f07c02", - "range_proof_mr": "86ec0060f1862b8f13fc4fe514b3434e4489efaab2961d0e77f375144d8b90bf", - "kernel_mr": "6c05a8749813d46a7e0723adbbc1b4d66c02c4bb140c1457838a6cd6fc2257ec", - "total_kernel_offset": "e36b41967b512750e90495e733bd3a2b197fe9ee8bc09c56b7cbff98bcac5e0e", - "pow": { - "work": 7 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "529b927f4f1801dbb81c762b36b35bfb24f32aab338e727b4c973dbff6311964" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "866ff9fffae8304018f65082409bbe533dae9af7d6f800d6a1fa68ca83ba7f23" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "c2f82a7acc5f4b16b8f8a04479c4a2a1833df726c09b9e8ebcdf2c9f29f6ce67" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ce03f25709841b22b91008b19abf8e17d664fb7a5637e4ec563c6a4c6e7f7418" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 4 - }, - "commitment": "70ea1ed62cad4bba017ac73b24959403753e264567cc5e26c0c4618c7172d347" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0e5e7f3346a2fe8fa3485a1db7e708348a20b154d6dc1277ec7cc8271a63142a", - "proof": "027c0a0687b2c1a410b4719301cdda8d4bb33b8de2552003f814cdafeaff703b28d603505f5094b9aad09568a06b05f035f197d1c1e1efde64df455bbef58a7e7695538d28950ff3a5c7b3280eca82a2facc91474c29f06a69dd382e0987f950c683c229357c7a00e8521984492693706b52713303455250e08ab685d1253d77b9e0cf0e022a18c8621a169be74c9edda1222bb10efb549238c4186b9b25790dca1e1b36f9fd102b68ab0dc2d53ba62de6f29fe4043e90034082c63290f63a06892b73504305d908af3b61c56ac27259c860247b518b4e70a140d764cc7ddc03e638dee639303986b631ea5d216cab616ab702930a82e0ec8ab33de09c6c393e4801a7bf143678c5650c85347adf0bae7e98394665c2671b52113808b54d74518844af0066d97fbbeea54d5c93e85b7b6bc51119d2041c615f882d4b18dd3666a6f66fa68bead9b0fb867937b14ebc546a1191f526f251b06d6d15f54c53e7153c841eafda4ec2d8bee5d2f4c9fc8ef955e22d1f9702b4bbb5df6db29c801d24c6310bffc237e81d90c33caa588ecb4f3ea76b4b029665d62c70bf916496ea138040702cb66f94990fe4d33ff5d59b01310c3974b75df48550164daf5831a244304298383abffdcaed2551030c94117cb6e3920d2f78e9aecbdae91513b88f33de435dfdbd9b2e75b9a477bd10e8d464afe0b00e286285a06366ed341ef90c464eb7f5a1b002526a63348578158dbca1e84b57222a1210ffaccaba6b47106e71da0ccffcfdc1279875b436c85911530ffc04e55924e6aded4c8f680dc8b30f01c04be5be9ed705629905813a766bde14b40e2d4a7007a5180194d4d68497d43694a11d3ff1fefda3e074530894a1dd689621b772598fac9454b1e3b7526f780d0bbcb92832ad3672fa5c152b24ca0fa400b0153d4f03a03b28dd57e61f7aef07" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "1095ca85b2c74077bf4b6500c4d7bfeda2ce6e0fbc546e5e6caf0385d3b5fd41", - "proof": "1c6dbb834a679db1b7f6dd99594dc0e3fa4a31595d4ec755b53103fe6165bf6664fa5e01f6ef07a05fffaecc235d3e51fc81b0a06db96124d7d49e07a632c34c98bd471c942052e9143aa132c1c28b2b481c4a2aa1baf6d2d2c49ca7e7f3ce2050a8b99e7eab363bb65894d20386096d1e8512d2276d0bb941494522b2b4a83103bf01102ca69215523cccbb9513fb7be05dcf595c0378cf7efe5edb5221780e94d801de0bd6d5a298391866e22dbaa59878acf8bdc494f5b328ac24e45b6a0b505b824da93ebb674d9107c7a27dda0c378075aa8dcfcb16e23b7ae76e4d010bf42ee66cf2422520b3a0fdbde98563d483d013710e71aaddb3f8634c04f13937c42950df459a7ab74f826d33140399811f373c0d4d48cce9b31e990703a70e2c18cc52fd399daa9f4491fc9b31ea8df008eb190b1a9fa8d8373806106379ca1a68b5d2838a9d11e81251453f5b97aeb55a76d848c1f0f311159b49b9dc935668f2358d1b2fdf975de8b8612f2683defc6f7fd746527110d9a923a8fbe3a75674e8cae4e934c47db03b6d544ce7344079a241d0c4de40a50bfc9a2e0a51178f6daae499efde2938e8f0f36b0b0f595b86b385e2fcb9edde9d2fb8d977bb19a243d2016195884381816bbda677a0a807c9b64ab76a1661b613d5adc226fdec161f32ad529bb76c5bef9ea7e40bda0ee88484885f3665a0249f7ce1ea197130855978d2234fcd9c9da78865ee32b1ed90154d280df6b4e3f2979fba0df5738ec61162d40f6b4e6b9d801b5b48a656244073bf3fdc5f2dbee8aa55a8c94c1a7afa212244799105bb3cfd5e0321e7d0741f0b8a02ca9e3a739441c95ceb5c9ae71c4f991f46f3514fe4d85f72d56ccd02d9788c683ef4220789595188170cdf38b80acfd7db133deb0429df70d749c81fc64bc40c8b02f36a22d3f8ac5e3ee25a6600" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "3c1019d2b214cf264dcff93b059d75e3fc0f41a7742fe206bd638f10fc0ee821", - "proof": "7690b4e9c16c199c0c821f051dbad7dd4c6b5a7827ffb0389421a3e3ed5bb35d9228eccdd4e832c258450fcd9297fcdee9bb50a7173ec5f5683ff47276fe3d6298a02291ceea84b684a98a8b08404db1b3a097c6a5c63d4e2aa1e09bebe08f15f64e9b0498e7a97d89a4ea459dd3f34137da5cfb2cd0a7ee25f54da4a140ee61dddaced0f9eae473f07d13318acc7d351edc471f596052867741d4748d0309028994763cdf7fcfe7bfb1ec2f219592a60175c4c6cedc302c84435af381af590f3cac58c14406f93c4458f1326e04a07beba0018d3c922184eb5b3628020a7a030ae3d2ded627412476226229f45524bcd9a69058ed64633744ced4ad7d15e20d3ccf318f92ed8053d979c009b66b1d706b91f55c27db68710bd84fb2ad29a40af8324f156ec29236a31304a5b2a69b790b23fb45f446d6dd52c87f5522944542ca0fcc1949b6eea8ec09959ce4e3a9c7e2e84f470d8d00cb04cb0cafb9a8f9121001469f8d71459462dbfbcd90aca39f694b25c7e5060667d20f30f2baecec7fc4f7b8cb3b09d0ac694d9d287fc2d4e9cc1eef7dc833790860c440553bdff05baceb387aedf16f518f89ef4417c6d3537db9584bb6d4f7ebfc280fa37bf648603a30283479ab411370a44260d3a2e56693f6375339aafce1d65d0cbdf72883009637b5f7253aa3b209eb750446d8a7755c2d171309776a48f7e428180d12e3081c631f6794c78ac92a2f0f5a06a5c452fde961831a6a1aa0625d3ff73991862f1ef260fcc8c3cd756e8236b87713bb31abb469ef731f01284004673a65aaeb212e55690d445ba24a3e6f828a1a669246327e2cc067e7b904de91e8bc2833884a9a89a191ee614c91ff53f30d4a15898c0764380b5b21858989f89cdaf629d00e5f806bde14440f51cf042e5771f0d6dc1b272f88702eb5ab6aec211214098205" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "6e4727eabf292f829cb402d101eb137db211b5c5af8f50fcb256a712ca5ff47c", - "proof": "969c57670840a629d1003563d667db7604fa2f3d1a7d3a60d5043d915827de36acd7a926f621268c71f2091f1e7afaa61339a70261cdbfa7e7e3d1b01a49323f1a8c17c8f428250275b8ffb16cc8425afa968e36024a0cf0e7dbf1d62a75070e70d73b0508f0af9e01f3ee87498bf982b46f49380c6b77b84751443ebeb6355289d5548257c4d763ea28d543061cab60788e4c4094a41fc1ac84a9bb1732fe09078aa5a0ef4b093e0c5982680dfc82c3db834b9c4344689d03632877479f2200f9c5af9b921320cc8ac4d7a4f6b23f8990c8e717d46a10cfe37b7384c487b80630eab5cabad6ec11c1a766bb22691394b55bcb4f920df4683c17e382e4c02a6460bdc243636df7ec59df659473ea095f6c47232d666521efa0cdd25c28174e03aab5883a13e4e96aa9a9a8402f76e74ae1d6963a8423b47d94b5d66b3388a45f5ed47469cdee2b6005160762e29b52e9667ea8ee7deff4bad919d1314b718d409ee60ee10d6b48924466d0b7e5cdfa06a1e009afd742955d4619a2f782579022b0cc08b5b7953965b7d699a4586e534908c46ac5023161088045c036a8292910ac7b0466505be5662ec3d18b383d721b538a27e7a20433d7e1cfd3ee8d50cf700a3304e32695700543cae9ba1dbcd13edb5acc41db3966ad7244d065ea6b6768b2cb0ff8145cf0a6987476d516dcd3fbbe718043a4253cdcd6e30c971969c81ed04e8c9e8f0c69a8715e402ef1bccff6efc8a46f0a8313867cd990807b1e6b7a34b58c655ca4f1b4bed2e33d25a55ed3738ffd6b915ef2be99017f1a0cb181506aa38bb51f40c7ba50eabe38dffa2f19bf9014e2e02b746d0364013056eb48676d9cc969348fd51b6d7e6c2c1da7006951815e902eb9d8466ceac58fa95a390c5552f3f897e5c5df719e30983d647064f31ef636f1f17a8aab8d0d1b83ecb108" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "6ee9ebb7bbc9a0a1036084b20bc31e3bf42993d885d07da8e8a2d151a14fe332", - "proof": "40a1d6f0f24d73822342501f332ecd51814a473f3940b4afb0a5b5d5e7582a729ebd04319c614cb41a524ca57783ea01e81eb7868380c72fd64463e50189af20004d8b5741718d948e2c4479c21060844dfd22fd4dbb13d5ee56238d9aa9551c24d8b77364e6efc7f0e83134b18b71ba03bc6fec5096dfbabe99635ae54cbf46066ea3ac563de6a13e06b27a42a46f4dbe9bb35b40c771d09da7fa3d8d59e1089c20455fd6e0c6883f1c9c66224b6f2344eeb299e4d801691e82846f4c4bb30a70982d2afe18c764ec3e8601033765feb68411d98681202d0908690e7a06f10b725c63b606978d0b3a0e14d236694fec5581c2fcbbde5f59062475167bd32c3b04fcf44f7e6fe71a7f94d24794d43daff5efb181dd11da4d72f9add4d48bf22aba48dfaa162e8f31914838f0e2745874b022242734c2091bf9aaae8fa9d8d6166227611339810b6f60f61bd9740649aa5e865752e7c2d0100a8229455535d423b086d4bce457556d886a7a8d6218bc31799ee4b3bceaa79e8361ee4628e97142da39bc0f0558fbc02287d03c3a8cacc3855c934c78585b8ee94ba42eac43815e067fe0f0c239091ac76d84c067c5beb1d48f09810682ee840c6256d3ebb7fc756004fe6a784495aafb9e74e8c2082e0d9f9eca0aa7caa64e85d6c27b9fcc892a6cfd315a322032f20f0a98bb841ab17e4d119b2c2fae7083691ac5ebed32044f8efab0be3bcd1644d47d4fcbb6ff6ba64eec71555f60482d99d09d0dca90ee3dda119e182a4aa04605cc09ea784b0d0d701f5c8c87a45baa8775172a1988c759ec36c84013de5a1e5530800482931afd601c193a1e2b1a23c01e47291b384520cd3aa6db7a52c37a91c0ab8a8c2d0e5042899e3c7bec7671570e92f9681c84011f2bf7b0cf9220b25b57c2649e9e26777ec612d6a2b326f6fcbbf34184092306" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "7210bffc0723fe763dc31674087a37335da4ddb5fca8f1e83a8860ddb394d473", - "proof": "744d340e0a3e24abd3b30e8264ec919a4ed1800b8a0fcc4ed4a2c71e449eba095c791bd91814165bcc5ec97493d7f7c9ce74446066c9eaac2fabdb7bf560a87552bd7ef8f4f7b4838c4e8a0775f93593b80c4c175ca3dfd7e969a9f3f8417f41f635c06d4e3191532278a01d5b2d159d2fc7af2011b810c25a88edd7ef0a491a4687237cba182808ad0bc9e1c006324eaa8bc07f9c3c1fcf7a9d656452708505d1f450b3e49f9423eabe3c1f7133d99ce041247aea234274f236ce6fca660007345d9dd086aded92d325c03f911337a7fb5ede76600b6afb4970765a9d9854026c6f60cf97675c740e3bd65db6e1da7f855b4db760a91a0963759104d3037a648e611e0d666af80cb82467682e30f9de5e0ae1188ba0894d26d0b3ef3684da45247173564def6286a3506268d7daf0f41e800d11b0fe6d2de6b5dca6dc63cb6d8825acfdcb98cb396da9767dd39256f751c22fd723bd328331fe3005ff1a9f379ac86656c54beaa7a2fbe283d3474895a6f3690aaaf01828f3a26440175a065902b6a0b14be1ecc9c7ca21d89ea1dcbdedab23d648bbfb3cbc403850d2775529be8964ca73c43b4d6ae5ed295422f3310fbe21aa4850e92946ff10a0a0237e40a6b4a5d4bb0222cb221c6733b603248394a849e76298be0c696b591e7b7160757626ef2239e26fefa86840eb45070eb09ce9e285a4e7493952cfdec07fc77325ba37bea5e7aa7e91d7f7e526ae81216d0a2f494ee97fa6fc33e0024fc9cf2e670ca671b64861fd3b52d0a3e0ed8890b7b63879c40d0052bfd125f93c3258725ba42708f0ca6b390fb811457d2bd5c8d53a8a6a075379c3308c8329d23f77a42054e23c02ff60f06fd1b4011893c113d5d53c1392130ac5caf1029bca78761a023a885e22a8971eb34b5a9e9a91d96706c1b7b954473dcb13fd551437fac60708" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "a4d276e021abc54452596406de7c5ea281e68285981bf66923371c0e1537d251", - "proof": "b09c5e4e165cba00cbd52766c07b0ef624104cb8946ef63d1b57b5590672ec281c83d2d39f7c9902dec05d0c404f60224ec1629d555dbda065a2883589a4d64a94e8091023caf09d678ef71db87f7dc05de2bcd8519dc0cd93729557d3ae551d70f46c35a2d968820ed0d54bd2aec94e40eaba05bc98a1c4b52243bf633709091f244030bfc1b7b372c0a5cc18bc8cc920cb1d0e767031fd6dd612547a9cec07d7622741b4fcd4b6046ef16701c6b1d3e1640e73082398773fb2f4a67ae3fb0bc5257f0a09394b3a99a96dbb0100e9b27378fe631f963dc1e57796a1efe04309e012b768d08bfe1e03419744aa7bbb0c0298b14ecc76c3d24ce569a6e9c1b478ae3b7b56121c0a035fcb4583a25cd3ea5054e3808b92c148da1b1b31e8eb0455c2ca10f024bf225bee9ee693e2035abe87bfb421cb483e18a0fc9e687bb9816f8a01160111c96ed1f84603ddcc319bf283fad52765b1465beee3701c472d986a7afc78bab526fbe57174bc00317880bf1349a399e765ad403cb103333b68196f8efa84b2c4c6b4f47ecba4307133903fedb7462aade66ce47218293aa3c81109c857cbe0ea62cc4c8188aae9356e7d0962faefe5eb2df00f78b5f9634f37787c6e5ff095d25a67e32046141085666c991968eb1cf37f9339140630188820e21d7cc229edfcdeb706fd19686890414f95fd5eaf31f6a508b3df8c33e7c924215ecc9e710c3b3847cad19ba2508cf02422e5a532dc59b9743ecaa80192082ea84104f7c5e91cf757419218f439678488e9b23770e0197ba9cf275553feb1eb5f4a7eae39e7b802edf3e9e1f2ad9303e06bdc6d422583e40c131cd3ecedeca6b753f538ddc64e44eeabed6307e204ec3ed9086833d2d5c1c6600afce3b46cfeb40b1ec0158ec185c1f13e1d681be5de52ca495419aa95c14786c3a9a7dac5be9909" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ec2e42ddb39a55fc4b099b8cc95298d75d4b1d7a3e85a33f5fe3ffde8ad9fa5b", - "proof": "f0566aa9a9891b765b8e55e6fbca02e57de0d1479c3b9b46f6ea6db80096131fe426ca6f84ec9ec043dd867e1fcd287a6ce7ad541f1ab8885de622bf512a3f7508f3f2ac2dcde3c884e1034ff9f328c078780cfe63fbe1da7030c67fad48ea1b36553d8ae4636ad8084b7dc9ec745da330ce775509d13e09c9c1d7d2ec3f72778371881f8d24ff71bba906163f3eaaf0e916836f209d18418d36c13394f8c408abb705f766c49b28781c0da21298297220bed98b27706008136e2bfe80752604c82ab47d2700d8f2cc2f876433b89a09a028b1a6199ecffc41ba7b15b8a77b073ca2013947561784e15d3d1324c86af4663225fa182b13a8110a66fa77e08b27b82d3d43ba46323a87bba8bb2c173cdf16741f4b12644a2b97a6fe20de41cf60862cbbb1ecf6b62b4b49e56e6be6c54b79ef00b9bea44478be47937493a8bb3408441ad0652d4f5f2258bf2ce7f128f5e2e9e0643eb20b526c9898c7bf852b2de236c31a3d91d56a170be15ce2a26cd944714c476648588ffa2826848dc280420c54501bff845171ae06d18e6b709f5a977f07b6019de39d9b3212a0292fe145d475f8675bf46462ae2bbfbdb9f29addee566658d1a77a3ab2fb36f336564a22fa80065512c9e71b619d427ca0c915f4536523349819a5e4ca0da37f7f78ab7efe677ca1cd2d4cd4b3cc1d1b58d633b2e30c9fa19d4fa988865a3adc3fc7f26fa061558de09f734f1670b8004dea5ba06ea7b91e657b150582d766d7fba56000246a18f2e9ef45b88cde6c151203eedee934f24986e49852a11e638a5329a077568a82644f8a99799694661e419cd629382c88377a96a8d5f4495b225b3ad30dd238d07288ba9dc61527538bcd0e5d712a33039983a56604b6ed563ad69286000ce9fac9910056800b12c5b0f50011d5f88ecb96de275cba64e91273e8346e04" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ee6c277567c54e5088c5e36ff5b742ad295df891ba7bebdd8efc1ffa19d9236c", - "proof": "a2eea31b16701b111112e61930b770fe357786ad8b1520de3062205994b88d2e7ed2eb697dd17f3300b0b8d345b4edc04c61a503abd22544aa6a314de5eb4c26c01f7b259bc8a6d0e92bbeea9eeda0142f07d139a7e8cb3fe699bf6c4c54cd0f8a976fc74236b74a7516f78b354b814400c6405c14d7ecd09013e5e92794d81f55c7222bb09888d00dc7a35302ca5385913b484a3b26dbc0ce053bdc106628094a375d9dd5f7a344d0fff5d8a75859d786d5fbc626bdccc1ad94bfc6bec1c50ebb14b6bdf63efa7d4e616166be5d377b07cf42693ca17b557238668f3b9eb40f5624002f39ccdb50f6e790cfcb0ac87b9ec69ab1eefc8c6d9afa8f3c59afe3676c4850c5d57985d42798d7152e5184efbcd22bbadab13e85e60c145787af0159dc8cc9c853158e836dffec8d11b774dddcc0298fe95ec2d2fa88e6b8feb8c23778aadbbb1e4ef7932642abb7c0cbf3594968a7c0fc08837b9e10688fbafc325824c9a2a25556b4f3e10066877e8d2dc6628dbeb7483bddc133ef43995c262900a25094d09e5ebedc8120f29d15e247846ad50ce64d46c2750a231354ba3bc72e9c2aa5aa59f643153f3746a151f86847688ed9fda5c46bfd279fcf2c0eb0304a9460f9ff86a27d35cc33ecfb2a9a8795537571d81dd26ad9e5ed5be151542b6aa0a4bbea8cecc97b2700826bedc6e873a8bde4b9a65e9a81cd8cde9af38f220d6617165180522013534bd9900f4e11cf55a518c2aec7756d11b73d64316ce41e5a889930d7d65ee76ce00f0c6086e00cab77bd460a8d48dba53005d081b5933eb249d87b61bfe5529ad2fabf2c8d5420f5bdeebafc07bf1bf6add8826479ed43a603151012093ac3997a5624439e74ef4fbc8d2f3f8e927139ce3e88081b090d40beda2f6f1ce851df194f9d2bccd829b0d4b8a011b52243f12632e074fddf09" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ee94275b1f0824f593ef0cfd8622479d15102028f186c8dffc76ad5f9d739858", - "proof": "7a1aa3e344fb0ae794d1f3cd7ae263d0e421893d9a6c0dfd48f8fd424efa914a3e2455c9c6e89729d2f310b0fa3d759bb19e2b8606626218d930e80f415b0e67a6b0aa8240100c08474a2115754bd6a4de9e23507a0761a29cdee595de194d7416b6a74d39ffa05e1af80febd99fc80d2cab3640fd76b2a445978e508b991e039f6ad3f760c1ee22a1ee950d599470b5494bdf06be5bf5be965f1109e3cf670781a8d53abb46b57dfeed14b08d34f9359241cdefd3a92fce04b803bdcdef5c094b3f04662aedb0e2db08bd090cf8fcb2c2a8c2b7528106c0708b558d0d6d330512e748913a6ee869f34074f623fc4b5980beb4184458ef3ba868d9115a09883afe63f18d2886844bd00cff6f043b2c8135a101bb8f5db4966b415d50c66fbf7028c1e1bd5d6e2337d169ac57ae39c94f92335f3eb23c03e77cd71a4b8d963558a876e44571353f52e19e7d807ee4fa605e89b6deaedeb886ae995c2d8cfb5620f249ed55c4377db4bc3536f864215659bc01d2e4ffa615f7fd3466be6805aa1efe2c43a4e0a62ea82ff99f51172ed71c45a557cae879cc58c64267a82635a85ea892db4aa505effa1ecf4a0ccd64414a6e138ec4b67a60a2f2cbb9e065e0e9525e0a698fc35385838fac714fc0f9bb9ac3d5f0e351ad499152294ab9c7899801f63952f9300ef26ecc76cbdabaf367d8b4b567940c9ee5071723f6ce9a8ede55f42628c797596b70924d40f657416656eeedd8b65e93be46a58a1b2e88023d1768a339178933b2b6c3870c089899b9abc17afd4ddfa43ad82111b1c84b95c64ca892a4a3fcd44b05b1e2bebf91a1e7210149b7bd3a82dff5962386fd3cbedd755da493c02490c9ae7a26b5c4a89ca82ed74673a46d4042ead0e0f072e328650db23d4897ecbed0a79d3320745d0f2812c438c0064b93173931f9e3e01cd57801" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 8 - }, - "commitment": "5001a29c380652d4c263ceebd4f1fd4329b34fe80a2fdeca378c695205f61535", - "proof": "122bc4368c8b2bbfd41be5e340a3f833c115c597d8fe90342d94e052b02baf531cb71f1ca0547efc06aee726a983ba9826ac0f774a7af672b729395d6eb66872309a7c4145538b180dc36f45cba166745cf27debb365999dde9c706c3e31a01e36bfc78556b9b9bca3b6ab6a67a98faad68acf3371b2d801d902ba632da2842de95daa04c173d0b885a60d6b3bf6e71b1c3acad0a33eeb9cdf12a7f862508a0c4d42166dee21f00ced7c51e74a82a6e0342fe52ea76634dd0034e351b15d0f0444ac568e847795f993688ae5cbc79ebb2a5cd5e60c2ad2cff9230560f0a241037e06b03fc602ebf26575d7da0f15b744977ea64fafbed6a42b5a288cf6b9b4339436ba21b370804e9066b444ca27df8d473f37ce307bbfb1f41b7e2a31448b5184bb065594c9b38bc94d03f1d939bdbf08dc6222c472bb5ea4826c0bbcbdec49dc76d84435124eecbba4bafbc41da45f3ea54f63d94fbf11a60f8a9e2e5aba2e02e096350112dec3b0195a9ed7820991d23b7f48deb3c87f14b58ff086940071b29cef709216f18a5b787c45bc7c60f50c03abc5c3fa8455f06f671420b194643062e87c3930abaa851052bf168b2e7aaad2992f834291555b9eef53bdb46b1c50d71fdf70b3d876b8a873ee8f9706e94e1b797c92214ad8d7cdf4ffbb16552530be3c6bd32a098bd63ffab40e39f1ec5f3b4af0eadfcf70086e4d5874cf8e76aa4f13c7a2487ff750ca886387a69acb910badb017168a4f0a6bc5985c1f2d64e4d6b528374f49def2fd3514f92b6a133106de37dcf740487ba46865aa5c0415c838aa09bc8ef6e82477b0a0c3af3e5c8b36c453716a431d3fef031d0eb3ea6b09b2cfbecd9717d584a410af67df66bceec9a22b879d75301bb868aaab5d0901b6aff5bbdf8f5e73fae3fbc5ec829dd361716c1ea45d04a9302cd0a34deab401" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "1c5aecf30f36f1f7a3b42f084480f4a64343bce458b588aa0939b40edac9e324", - "excess_sig": { - "public_nonce": "82c6ab1c59bf1ff651154c90dcf01f2480d94afbb032c8a7eaa3f99d3a9db324", - "signature": "5a89a5ddb731247248a2652fc35aedbdce47e3b3d68574fd1e3d82a930797e01" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "72fe4702878d6f13333d61f2d05aafa88e436df6206780d8a9ae2ccbe0042e00", - "excess_sig": { - "public_nonce": "becbe21739c78ec32b71663abfc3a3f5808a350e30f376c6432687aff4e4bc21", - "signature": "11f69e03eec45d129759c3e6fe2f80089dd9a5cf153ceff7421a3221ed8e0c04" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "a64a82991efc2ee27bacbda0120d7d6888310f32b5927d5b316d327227d8cd07", - "excess_sig": { - "public_nonce": "88c5a59c363ad636a073caaa1db228ed1e0f2334b0df4390b4085d40d7d19e5b", - "signature": "7f4a3a4a9dd22cce78ee98067a442d8b69dc8680f92e013ba8ebe29ec44acd02" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "e0b742d4b6b16e50583dea1a1f7adc8bb4edf1d1cf0dffb4ca1e30a9f16f1208", - "excess_sig": { - "public_nonce": "347b68c7d402b28d60bc4581e29d50471f11c1bbf16989ae5e940ba0f6db3e5b", - "signature": "cabd6e9455124da5fed81b69dd44fdbeb83d9eed7360932a786161ac120f0100" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "f69e24bb3df5880fcc4a384fe0683062f2a893263eecdb71263b0940baddf80e", - "excess_sig": { - "public_nonce": "02acf7b515c6c54f63f65877d3eb41941f477231a5426e56c5eb26d010397b4d", - "signature": "466a808cd09e4b82460ef6d3dacd2e4e0a28a2d7d2a1176f4f382708f17e1205" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "dc793e208b477a903ccb7e959fee200ed299abc5041be86590e0596a5253a273", - "excess_sig": { - "public_nonce": "aa34f14061ae508121ce47798b4741ad11764fa6ef1c6377beee227d81aba70b", - "signature": "13739f132aa3cafba84812216b974071123589d5f6d323a1a068cfa69ebe3d00" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 8, - "prev_hash": "94a8a59508af27d48608b47c8abbf16ef197a053cdc996517502ddf2c042b3f0", - "timestamp": "2000-01-01T01:09:01Z", - "output_mr": "fe39a4cd22c451e0027d09e63c25e4d7cd7511a038e3c4af2699e00b402c7314", - "range_proof_mr": "e0e2fe305cd22aedb7dd8454c72e054ac235108f837f655168faafacaaf7dc67", - "kernel_mr": "3782bb29774bbdfab90d90ccb269c9163e74df880c44a236878434370a0fc20e", - "total_kernel_offset": "3aca1715ad121f154981ce6fcf05d126fc1305ecfd168ae474c990fa921c8307", - "pow": { - "work": 8 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "1095ca85b2c74077bf4b6500c4d7bfeda2ce6e0fbc546e5e6caf0385d3b5fd41" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "a4d276e021abc54452596406de7c5ea281e68285981bf66923371c0e1537d251" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ec2e42ddb39a55fc4b099b8cc95298d75d4b1d7a3e85a33f5fe3ffde8ad9fa5b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ee6c277567c54e5088c5e36ff5b742ad295df891ba7bebdd8efc1ffa19d9236c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ee94275b1f0824f593ef0cfd8622479d15102028f186c8dffc76ad5f9d739858" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0c9df8687c74e9b89a2255fe4ee4966054619d1a022dac53db65cc3056a7402c", - "proof": "7035f75f98a467c27155353af4cf2f3e32e3b94789c49fc113f9674a7698451726e028f04c2c6b8e4e5e2b15c30d7f45a95ae78a6c1dbdd4cba713f653efea4dfea6aab187cb35f3c7c606fc3aa00150cd5812943a9e575f210d1377a8baba03c016ba6131b9618bc57a64b2266fd904fe0d4335d72556d0fee8270725d814617e95111a5f8a389bb4a20a85700304790c55d0215e47f060ffd5b88e0b0bd5057628292ba81ec6e03ffba6fb19decded44dc6f02132bed904228db07712b8b02034f586fcbf27341508c7a43d7e7695d4b79ccdfaac1e43a91133a36e32ec50608ebb4776f2863aa4afab01daa416dc09939dfb867a0d5e6ae49897f0a98b50fbcc9d540a79026eb127894be060427b336bccac05bbd4ae270f05e2ddd6afb5fb8a9e4065e846480fdf30ff16da0c69bf67353d2cf4051b6347a7fc0e0041a1208abb63bdbe71acafa4c16c019828c26af8c6034215e82a7934fcb98140ea77ffe65af71bdd2a855642b3ff0ac2829e3732bedee5da5ed6f93c19036ac4cc94bb406e6ef6bf68422eb46e72b22008d5e91257a156715d1226729cf5ab8db5e20645f5679f03d213431c92e3ddfd58109e2ae810e942ac30452860575cbafc9105244abb4db23891f81e3a012d2d0a512d306d6ce3999ac7ded43001a9eef9e77e2af84c391b8e1f4fd8a3e7a209694bd521e2d6eb4f082bfd679725bebc3ea31726e2dd1eb6db20da6d9b673f4f30f68c6e01f283974b414d0462c2b788f137d4890610a8f9fdbf8aa8b9d4933ac230d45309357a988f053c3720bbdf913f432a0e44c25ee723d74c557ce9b6c05d2674be2d9dac99b0fe41c4d4ec8b0451f0e819bdd8493b38268f07075c627c5cd61e3d64ed6db886f00a5d7fa81dd06310602cde8d0a3b3932f5d8febb1f14faa96b82697195a11c76dc05669f0b618c209" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "128b997d31ede16377c855ae74de018de23f9564c0b3f878bf4966356a7c7b18", - "proof": "58035a166b601987cfeca41e77cc950bc0fde413d6d1ae6673dbfa7eae156965161eec749e38489133b46177382b5cbca49929dddb6829a3788d4347ea9ca7698ca096089d99f7f8818288380c37f63f61a82c0e1d818dc98a5ec1bb410f9818d406f309fb0ee04df0cdab0e04edf1b9cf14d585b3fc8310bcbd7f24e069883031570c44f9621cfdee6e08ab724f979ce406d58ed037ee92ae54cb4e43a1dc0db22e505d535fde4be459a6b7469e785de87109fa079321870dcb01ab67acde04edb412a89db2e38dffe1037f14c9132f4c2994ff1bb2f4a2de172fa285b4820cbc407ab596332cf91c62d040e6500b971864a8a5264677b1e5a1120b61177326389cb63f786e6c133147b4417fc1ba94b9de893bf09514d91325f01cd32f1e043819cb704bcbc4ac54f2afdc18936eac13b7130b61224ef1343314c42c0abc77e8473250afa3554e58c47b26da47fd165581841f1635e335371f7432c7417b69fc93a9995ef3bd94e932fe31188945aa52109d1b1a41033cb201c3c50a3fc83c9a3071d18a13e010e0cff5d0d7ee02ba27c9ff3853260621e221d6944a1344021c1922129ee0dec57aef9424fd6b01eff8700dea97110033e77cbc730147fc1ad08507641bd1fc97fe838a0305779c5c5074dfc88deb55bb030e7bcd022e9a476e5b564183de8cd6adaf6b084231bbbe51f5304ebe8220343f2306700628bb61f4e4b51afe43ec669bc3a3d042703fcc18d5cc75133e8147a2189505114fd426e6e1a495072920e159a370974aa3cbdac3fb8d2ee63a874740da76d5b67091619e06372c681c256a2137a2742faa876c1f2bf35611b25614c615112bf46add4d31c3fc98f0df7f18f75485420ed4a25118933b7fd7389450f2c836c6cbd5b904d5cf61e28b73b6e0271a0f44cd086cdaeefc7e7071a9ee948ba6f3be5fef100c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "682b7b9317b92f722f1c28330bdb4c5afa19911697807979468dd3c2c10b9d2e", - "proof": "d073ff69925ef5c906bcfb630d0b477b25d91238603e85e6d6e2b723f8477f22dc39a75ddce1cf56b491ea85d5b19143e3c6a7d47a7544eede44bdb5e0f5f22d864c0eef027bf37d4580868a458b3a45087c968061476ba5a246092f6f02524e4491241cbca39fb33c5354f47033e78f3a4fe963f573f69d1391fcbafdcf8825206ba2772ad753a9591c252fdc0202f3dcb798066005cc25a80ebb3374acc209d6d718b42f84f1757ef97b82714e30516c59a5e4b3302c2d011fd5e06c172e077cad58966ddebb99748e8d072887d8369e240337a8a93f55965beb45304c2d05f244d88fa892c13d1c2024d31df71b7df5a40d309936cdd154fcb44b4740f91b7c0a392a1be7577d843528c91d056e40cd35a5212e6d25a8651dcc4802300f573aa6e0abce06d2328e90850f70c198b656aa0c199037dedf5117791f725e5664982ff2f77abca47b5cf3316a0b32eecb1b5c9107a98ea96fcfac13338c1c2c09ccde68dba2b80bd068e81018e9e4836538cd0c78dc9450447d90a6e8304d8951e64214e62a83f3c078ef2e0c8b824fe9903d70e34397bc9d1f2896e323475711766160d3b1bbac08e8dc21ef7963dd6350dcc51cc72ee85d9ae81a4af74d8a5646cc2388d189a95a1bbe0441e212a70dc37618399cd0b267626adf04e2e1cf3520e0a86d49b87a38a01643e88f3c2c4e4843f5a017cba1b7538f727c842acb10d81aa9aa663d6106b696f7b2041c101ecaabd4b8182f764aa46bad6c35837474f68fb90686fcfd97123c1d1684afe9caf7db1073f89541473bc10bb59933d17dc2c08b151d15947820c419cf0d44b6f24927130163acfbb755d4a62ed69e8d64372188b334d448a307e1460425b37cc594663e7819bc63214c1b424e63d1e30f2395a3ca45d46c2729f3c238043210d5160ede328a6b2042d3d3fd994f656d05" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "70f44685c6a164573af3af966cfae1ef0e9a722ea7f512861d34cb6e9ca3a94a", - "proof": "a86396c9fa425a51968d83ac0c98e6f82293b6035ab52d981903b2865f31b77ef45c9c9f99d0ed1b71617a496a4db8ac7cc694eafbf513f8e71ea5f1ff34dc714e07b10b079352821cd815931049ae847c6851feb601fd2a4cf76cf288c4035670f69641ae17d1188b2f80004cb339db4d1f26e0dc134553d5cce18fe8f13055e1eb52d05655840953fee9a6b85943b9b77e7d58df00e79979bfb3aa88cd2003309e386ad8c3f077cbb884e8a41b7c194c20f6e1a4571806bf2489826731ab02619d7b0d48c59e18f2e991d653941dcf7c261293e500bae9f1c6b7846cef980b245a1870a6a300c9cc3f04cb371924f763ad189fdbf5925c5bd314dd9722e714fec4227627adbd67272cdc438e18bac26dd98a5e8492c20e403b84115e0a6011e48de410b30135ebe8365f7e112e6a4642535da03800ef7ac19957e7fc40dd39802d3d041bfb056c3d431257ee86dd8f79d08cf8ef4245dc9ae9c0c40559105e9886b72f3f9e24608c4d5bccf767f8371fe5298f04173d051a8cf32e8368c4773cfa61e40e63133724e12cd3a16cef54c9010a2586a91d1c2735a05ce2d9a206b8c9b84d8361123c4adc96b6024a420669ad94d5d0285bfb9ec0ac6ca6724a61eacf7bc8f86b5fdca67ccc3eac938911080ae063a10d30b47ff1ceb617db2f3dac85ecd55fd4b229be304d5173dd3ea86238d80f7e0bb1cc0a831f618bf8ec33b637c44b586c9fc250da31bce88e3824c81af13e71e64091ad585f02a4866e181a518758d4d0d5bdf2b94643107de2e0b01971b3281266e5e605a9bd298171094aefec26447d01f1f26f625b58beb39af966dfdc4d55bb1e28bf6e299dbac82501401f6822cc9b343c12e444bbd3f5e4eeeb8abad8c1188ee1c015bfbb41b0048878041f1012539524cfbf8726baddeb4393fdb813ff9b1aba8412c265c4440f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "7ab0ea6ecf565fd05e1385c7babd93be8e31392429ec0bcfa28222e577d40f0b", - "proof": "78b7701eb9548c999e2926ef3b7af163e8ad8445444158954e7f5871ef093d206c0f80e939f8842f4e51280dbea9367f9ba49095f468a41cb0385a14632cfa6fde387fdf83f383267e7363546a0dc295993869e2a2a1ee61523f2c517d57d241ac0edd3ed0ea7e0b5d446c5b7a0d9512da45d36e96a84d6ea1df6b2ded83926d25129eb11b10cd21bf1fbb9c7d1c69730a29d10bbb46f0647b0308a4e7afd209cddf4202cce30af1a1b2a7f8e6dcf3b65c62eba81694ecb69d3aa4ac86f6e30ef5e6b9c0d3fa04419c2297fe2b54909784fbe62dc0b6da0cc2a9b92cd640aa095295a09d9ceee8daf7e3740b85623189f5657fa396769a71e51892247dd52d34e691becb07c03275bea1340a6403e0da26c9b073dca0c6f11e69a6aafcf6916f0cb217cb70af8af980a2f2eb8f9a48132dfcb0732ee78cc963d2672e225f49511edd1c1d20161e181388f2c2c7b4684751d2704fcbbe7a458ba5598d439c9e6ec45e58b7da7022f4e0ddf89e502de4ddb78f0747c124013d4b32f69017c147394074e3cc56ffaf72b137490c37c320a4630dafe65152dc17752bbb763ddc845c245690d65963799f3805e2d0c3b51068a921003b33223b496c42f297613b792282edafbc06eb9df5d359c6dbb6c2e3e2511f0fc44257f3dc7560bdcd87cf373d52dc9e23fd6065b5c9111eaa4b46d5c726cfdbaafc6029ce7523c63e67b06d0ceef09d342dd4135b1bca5d5bdec3fd7443a0739854905d00d2b606dd86b29214d447d240a6b76375187a1db2de12e65d46e6f89f7189e756adbcbcba31c5856aa2aefc34b2267886b5c407fe4b9248fee38c0939807788f794ee2ea14a54977deebcddb1f07765b2fb74b07a5702b831aa07191954bfa5d8498cd0e21be9850768a71365797b2b9f475fdde673676a512123ce82e7505ab631cc0ce495812e00" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "a42cf71b29fede878d1ac17e4bc86ac502874c6471bde74cb74c1ed9878d647c", - "proof": "7af97390f1f217b798866cd9751b4e99115945cf039225a6599d9ed9bcdfba0058481e75b6d061e732a323cd7c1eb0d0b5eaf1e116ec96c71c808210d01a653c3e77e0fb90c40e97d600401f666e3926a802b05ba9ee3ec9e476672fef048d4ef6a3d12bc0494f2c0eb772aca1db065ca559630a66c8d680a00fd4c3b8eb4b7c470ba2047d090218f83227f58e8fb2b7f08846c8b62dc5693bb3f1aa2105ca0983b8426a36ccc688b1b47ae07f1662c38f4acff038419b441b35fbaee1c08e06cf1c56e0d5744eecd80e3b8f6d99d83a85696ab87aa0d8a835cc0f82ee7a770e2e8419eedc82d734783b0a2c091e2b51aaf262c8d9dd1d391eaf96d9ce55d73148f633ff0efe15c28d0e98996bceb8d46b7c7ef2ed2bc179f6634ccccb7a8050306be78683373ba90867e2e5e9e9d5effda4253c9106268274c79b83aa151f37e899beab673b8f61634da0010c78eb877f11c9f7ff7fbb2e752b7e87cb6faa211e46365cf37690dc0b8d1527ec5e38b15d0de931391a8aaf842efa1d415fed032eee4f75043f37341fde4c523404ba3ba87088b5c264fc787b3d02e13138db0702af3a569fae9ca257cbcfdb107f24011abc2991cff7b3c5a16b0f428467a77958c88b129d39bf6304b55d2794b55c88305926986f92cd53e34b9ffe5b5e556fdca5487a26aece8cbbed349619a075a394d17deefa70a23d4343abae2572032f006f948e16ce36ab11d4852be29e97e29706bbbd85cb7f7b473f48c95f2f944c96076d4117f6b17ffb4c268949c5c4ae063c3c6a8639d3b2be1dfd86433049069e27600340cb54d5d654f13eab8ab21e35e31b2140c335bc1e63ee1b4700fa5224c79188bba9a65779b83c249b0fead0f19cd9f126a965f3da9c150ada503107a77db9563b5393b26074be3fe07745e52de1e1c198dfe0227113e2ce6d4fc306" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "befd853c5ecc99e5b382d4d8188ed6ea0f5b7b74c430b07db9295b8cbe015035", - "proof": "c4b49559588a543d50b44714c131013cfd013f41f3fed8c8acbe485725095918748ef6c54c8023e9da97db94781b981311596ab63378c3c8a59088d07aa1410468526bc18b7bbc02be7bf2addc203e20389e54e429e8f4ace09179ebc366b8318468f51edfebd427544423b1afbebfddcd91ccafae00a0c9a6a2ae8ada482063cc59375c4e7c4f544784ed79ef931301c00b0e36e7985522223c28e77986fe09df5dba915bfb54f4faf58f68ad9d5c810de56fe23a10bc8d0de6f52800c3b60d49f623a20aed32e0d49b431e815c4ef22a208b1ba28b83803c9860aff406d50d36fda3f02063e515d4a928207b3a919290b1134d7442bc1e4495530475ddcf14584dc96f7ce6d24713ec498f005a01cb26933518c2e13394d997f70bfd00920496df4cbeec3a840f8d24213b01646810964db81e11fea90eaea1ebc99aa30a11d24db19deef9994589e688e34e1770ea57afc3041c4f9f7bd160f7163cb58d35f04c01a496c9a32ee7507fb8ac34642370228f89fd853093ce702399e6c773057e0e0ba0c89da9b006421e951e8b0b85bf524fc5c8a58a0a7e1adbb60fab692d7036830d6470bce605d695c4b339ec7a74a06d62c55fb9c6315437b60efb6c458658694027e55a4455855a911a39f58872fdffcf4e06f070936c6f79e9b1642ed241df635b62274c61d35cf3715f28405ba7b24fe0f9a0519fe6aac1c7c5d02d42c53ee381fb8428ed73f7db1ed01266644d2c596d9862e9af909df7087c454832b03ebb44ef3e3caad9d80ad41424db667816d6065d997a49ba856540c9456f70987ac8e4541725fe0fdc081d1691530305abb0296bbb33ffe0f2db228dea29b64cbe7275573159d552d3b0117b10aba9f74d8298dee4ac8f382ab57c75070312b31de0a80a3d0af165708b366439105e854b3b92de377e2d37429a041ab702" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "c40c894c49d6435f854bdd871e33f835cfd01b0059816f276d730b61f8a61903", - "proof": "08b53e1ab34a4f661fa099dc84169e6c28c92799d842a36e37096c7016f44826a865cbd50a62ab988a146d3a7fee0289f51be34c12b3b6a28c3145c27b365611823b0151f305db2a0cb73ac626e8880a2abf5386a2d7d08478ef6e442faeea29ea58fd8e74134bbeda9de049776884f6795a79df4255b8f6bcc1824ead0cf776ee1188a9b29ef27c5eb85539fc860d51e355b0834e87dd664edb87843c49140ae1bf4a575a7174b21f5364269e5e498d399e45e3c9789feab14f19f2085895068eea9dbb33c0705647b1373f5a9e932a6c10125de97d01c62cf9479e21cfb80ed60fb43e1fa4bda55035da6638b6c81ba9bad7904803264ef917e37d8f3c0a2e620a5fec5d70c35990ad0870415576b77abd5cc66e50b4595feb1833ee990812dea5a52a9f71ba5bf345865e5081856eb00d2d55f29bbd02369d27f101fdb75fa869dfa5b380ea8f216e7e74aaec8e9b8d85128d97ae2d5b75254e8078104a1f9629f0c3e747a323320ae4d828d77c7e508bb31ec9095cf03eb0d314d8b83101b231c8908858d40a49f2c315c7280b2bb1389b373e5dce6adeffb1034f282040c23d47a9170ded33a0b5df730a8fe5fedb8cba0969b522a8ab3feef2565a4f495608bb1b552ac88502caaf56c1642b78885ad149e1b68f68c68a8ea01aab0f421472fb118a8f417fee0cdebe864bbb6b18f9f10d1cbaaa6945b676ee2fd5e35b50e506effd24065e86c75cd9b9660bbfa3804ff4396cd4939a51c20bee1a73149c36ab6b34a5ba90e4122a155aecf7f1a6a4386ea046dcfa7d569f79c3f03d77aef32f2cdcc031f0162059756efbb758735fdf15b2857ec5ff9d224240aaf039ae62d99605b92c331266ba765236d82416aede9b9191dc72ad7f52b5200ed3084185efbff8fd7eba680e46e14e050d0fb6ff160872039145f83cdb1b7b72970e" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "d0d1fa5a4013ad30700b610a41c97e8c54f3990194ed52eef2010fa64787af07", - "proof": "f693375cee8ca25d859b2dd531d76bdbe7a730cde520f3e79334d44267e97913b6f77a4bf7d6ee7f547ef0c3c68a701afa0fef675415b56d66570d1c0ea9d05e407be5b5616a8fc670894c72da92c87ce2c5350b0f02c5ff4d1eec30491c38024cfb615cc64e7c5b65e8459321331ce1e3db3375bbc2445f83ab3bb2421c9a02b75c70b3e47d0c7887226e7c68a87f1429678f1f992bdc8c7280c2bc4a210109540be4b0eba348322b77125660b277483f732050fe1b2981410ba5de4ff34c0e3a8b31b14d5bee44c1a9c3445ca80ab63974585b15814bbde665622bf5a6010e420ccc88be72dc0559174930c5b8f87c36aea8974fcf9c5fe0ced9855a6f22730464f2af8e4d2c53d09885d640efaf301baaf1e890fa4852050b901754749760342a0ad757a10b37c9728600d866955936b6e3afc0990f43ac413693d13b4840945751f88a859c4bf0c703eede7bc05dc124f429422cbcb453c9b73ae048101cf63b395ad19e928264f56e0e22484e106f6ab0b3d83acbf4f1cb2fece1f5577db83aa6c11522be2db0d192dd5bb181d101f4e4260bed178870df55ecd4f259669897b1f74ee6c49999889565d06050d303204c2e6152745c4438877b580b016c64c26402e00f9337bc6633ff168e6a506edabc22afd8bda5ba94d411dd144a722c3fbd8fcef67f40d38b31594791a33fd2b2fb6084a55e298e43b9c25e4a9658f0343693dfd4a5920c19c06d6f5f738a28abe640d75b2ec334e5f3f7659a9847ea81be31668999dca8f60cb3a0754b51190a44f8b0d1429b92a83e6626321d146ce41b212d65f17f68c85837be43a1f35ba5abcbdd2845422dae1275d06bde28cddc3b7eb995b0b164922eee3bc9f40a1b7d1eae6cffc6bd10525b10cfde1f0c198e5da259ff8a1e4a4bcb9858de0a0b58c51ba6c1f1fd7c4c4972a405e9c905" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "fa8209d1998f1f8e6a0949cc10548c8b32c0235815a9a3f988325eb6e0473b74", - "proof": "8a69189e826e3fc8e78479aaa0370d855310b3a1f7c400d2955a395c7b41b05fb672bb8dfc34cda88a0cc2e9b9e6a9ffb9c0cd42e059b3b0904e576a7d08ec6196a0a2d3d34785f743bf262b55a8b89979344b7ff2f8268a0d1053055c4d095f5cb2ad4d648f908d9ada729486a982e3edca92f758c78d06bb2c7478748c922443edaebfa653127045c20c80cb7c30300c3b594bee8157603a2ec2b610833c07df5d41dee3a94a01c142669ac469378cdd0c9c1196f9c75414d5e3e61da61708c5fad5db2de0e6ae92614a29e089397e83ec4ba6a4bd445940d7b40f768e3c0bb29a03254d77032718120a973b2ca0acabdd42e1b9fb22bf39a82b55e20c24176e9af50ac3c0d95634fdf91a0143ef4ccdab70ca9ee98270166f71dd0a46673602d4fec5817530c4144bf61c6803a24dfe466281e165ceb08f9824218b439f11cc4a943e5da54503c702e7faeba697457671fedc54f16136fae321f3a46884530acebdabb42f1fd2bbbba31a67312f8b48b9eb5838bfbda06b166a797f7ca63b722235e79bbc59397ee23ca787eec8e21ce97fa95dec90236ce6290e886d0875b4a2729def79071f4a48fd2cad3e1bc24ec23043e08a4f6060928ae093565f4b7aa06822c7e941d1b2c9240607ee5872c9ef122cf22caac7a94ff1695545fe243caa608a35efd28384d7332cad421860b7442e2ebc1fa49a2e2722b799eb346592ab06c14fca2137daf589fd1aed123066a008132be05f39006b5e9cde6a97228ee71ffbd335f2ce1120156b1cca2ce8a86ee69572b3bdb390b676bdd7c6c60b6e5c301e4d22aae2c7feca514b6bc05cd65314d12e4834ddc331689d60b5e95752bffa8ffa2aebc1eb83ac6918d0733abae18f973ef46ceeab40308fc53a0900b807433444bbf90df5af2509e522f05b38445f35cce80d3dafd58cfb8443ee02" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 9 - }, - "commitment": "2601c6458faf644e71550b28a6d3fbf262c650fb145d4b9864abb9c59e3f497b", - "proof": "9429ae452d8476be2442abe977ecae5e61feb9dbef5defd8b1cc358908715d5dccee07ed9f414505262039701e4455762ce72d71f5be7685032ae21af15dfe057a51af9dec9a1afe926d56646b66748b94bcbca0d2a095aabfb2de2f65c5b5568cfba174c0e98e8f880332790b4aa6f6c52de2f97947c259546ae8c9b6d7cc73509b7852ee9adf5dae29e2a215e1a158d38f331cfca111076ea10b9a812f340aab6bc4af3c6383f167016fb5fe743dac89d1bfdde5aebf7e3b7e7e51fcc63606256f5cbe7f66ebd1f9e3847b83d39018a42190a2656c7a8aace1789e26a4e501a237abcf076efdf888606511f148afc2a7fca66526403deea4fccbfef3b79200e89f2a5c2855e3f197c81021351f29c44fa6cb3435d9378f7798014c9c68445d309d5b05b4df299e2e94a4cca98789463bb1d2465fda68bd83e661f4287c0152768d6149a4ab746e94a0f079776387d3fe3a3cc0ffe52603058c1c5aec367a3864c1605321b6560e942309f035a42648bc47ce9114cf6aad06045d378933a116b8735b44eb8b033a860dfa21f95168bc1bcb46d4f6ef734a71edb3db1397a269a0098278c51bc74747c4de51fdfa5872667fd6f4f8145477647131668595616ed6cbc162c79dd105aec1db8a41b339474911e656f7a1c360ff58e9088e6ac160fee427a0d76ce1ff39ac0458a2e23ac2241c247ddb741cb5ee74aacd344f142202a01752f630744e892b8b17a7fba0df9a9f5ef5290cda38570b38110ccf3476a8a62b90b4e30ecec6d47b6e1f574fd8e63ce812cf5eabfe7fce0eea15c88757e8f584250047c1e1d2d8ee2dd538c1e66bba6485fba6de6c0a6459c177d87838bf8b0ae392db05f01427c38926298c5e0730cfa407fe7993106e50e4db86980d559dcd5a4ddb6837dbc88111ffa1358ebe9d77052398835576c5968801206108" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "46eccd5572aff754a6bfc4a3510a081c7e6a3c5e817fecdde157810f357d3464", - "excess_sig": { - "public_nonce": "d6815e3cb1d45ed0ba7e236df455436cc5e7f034d4bc29310412f7a1ac6dc945", - "signature": "9adf45f66ea67607585bf90a6f8424e68fe514d4fab63527b51093f91836a30d" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "6c146de5d8ef668d483c4d7ade17cb215bc8f9e8a47b3cfca3262e0e9c6d9f50", - "excess_sig": { - "public_nonce": "221408367671d5c85026e16c8f3a205d64aeaf5131564c62f56b6528a27b1742", - "signature": "a48cba1e4adcacfc00e9e773ef1d064725fcd3277496f6d27dfb8c470073ab00" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "7eb6cafa0e81e65bba764136eaa7635737da0b98bd9a0ac7fea7c561b76afc0e", - "excess_sig": { - "public_nonce": "0c7c4bb4ce705ec1191be1c144bfdeb20d287fc5873849b280b97be7b1ff3109", - "signature": "2e13c58804f7c40682d7335a1e8496d934db2a92640a4e055dde0bfd24689708" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "c238f9c216f122c362c763892edc0889cfacd09d22b14a279577635d6079c135", - "excess_sig": { - "public_nonce": "8e9805e621060ddb3da35bed4ac7a32f2bae3bb376024dd99d28071262e7762a", - "signature": "9d6b57ae1aea9e1cf0569bac3eccc912e55ff4c95ecee0e785280ce54db20c0b" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "f8caf0d35fec5cb1e2f03c21ac6db306684982d85b85fb693bad289dafb2af71", - "excess_sig": { - "public_nonce": "163b5eca5709ad348bc6e4f59485d641cf2418cd5481f0eb24d11c94dd63c34c", - "signature": "9800f44388946a6d84b3a96b48b0eff1b913872e1b8fbbc0c7014326894f6404" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "2ac9b07534022494acba41908bf63df7abfff9a25cd1358236655ca9becf262d", - "excess_sig": { - "public_nonce": "4c0d87fbf7d057de34bfbe72c3e34d11c578491c491a9308689417fddfd4d560", - "signature": "8ffa17a9986aa27e38303decadb7a0f47cd64f617d0aaad1c9e4fb3212834e06" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 9, - "prev_hash": "b69023e28a66f9689d4123890e6cfb47e1f35a0027ed12eb62b04f2c184d2552", - "timestamp": "2000-01-01T01:10:01Z", - "output_mr": "9bb9c90f1200eae82d171a157250fc87957419a1dbcf7677c176a053db59b34b", - "range_proof_mr": "bfa30589ff51953d1cc66535a43aa27846d9b3534ecd8d819f9308f48be91cd8", - "kernel_mr": "faf63a08fc3788a67e53dc49fc43ad9c2111bf76a874fa4773582c58fc8b4755", - "total_kernel_offset": "35402b422d5f54996265f21d34f4ed7dc8a479daf9bbdcb296e540972f0f6906", - "pow": { - "work": 9 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "70f44685c6a164573af3af966cfae1ef0e9a722ea7f512861d34cb6e9ca3a94a" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "7ab0ea6ecf565fd05e1385c7babd93be8e31392429ec0bcfa28222e577d40f0b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "befd853c5ecc99e5b382d4d8188ed6ea0f5b7b74c430b07db9295b8cbe015035" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "d0d1fa5a4013ad30700b610a41c97e8c54f3990194ed52eef2010fa64787af07" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 5 - }, - "commitment": "9a0b7f372fdb755963e5fce6f0f0bfe78c39594e7c5dc8d05b41bc42d5467946" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "008e1489381ab5ad8e5a6e597f5315b8e2bc9dee3429be1567b39dcff3f84853", - "proof": "06ecedb924f86ff3f99d3f9dcf737fe0c401722d0888bb68d1da9b2b87067252ac5a13a661a7d3881d949857952705a2e60c5775904e3b60b21eb21c0129d9049ecca9c1185d508824322561628b88172e53466164a1d3290cad5a8dd551326ea481581814ab5918a66cb87538da4488ee0cb7c519a86ee8e520c476b309874432f20d550b0c9154bf7d8082524005ae4847dd836d9825b9674b3f978964990366b981bc6bca594d6f65559450091a5e3e0e0e05c975a8fc40c5409ba8dbb601cecd9faf084e47a7b2475fa3153ae700950cbb47e1a75071b65f93e61319d20d82758f3f9978b60405ddbdd90502e7b5f4f8daa065f0d6db0189b470c09c06495cc9cef5431ac8d64bca8d1f7b1b6a80c06faf626791301589665f49d3f8cb7650ebce35a02853517f16141848d570633e201935c01bc92fdfae76991fa24b5174901c18ea885d349b0a6618ba7bae836c8d19f56f68a8b9643c9945efb4577d44ea4d338edebb671bfa6120d9abd01819c47826024995e73089f8320112ea2962c9388da695feffb7807ae4bdfeb216a83071f980ff6910de6acbba1c1f4169f60320fc100d25fdb89e2ed93cf8cdf0ec4be4514a95af6c811c6f382cde673b30d1e7f6293c5c580d22b28e360e7b48d1c98c25746e9fb29da407854acf44609c97dfca7dcd6bc9a253c332b3fbd7eff2bae5fc4bd2afc99bdab8dd56d8284ae2a93ce36c27bfc9b11912bc2dee3c522cfe8229e56d3fb872e5d15a0f70091cc8044031d29ee4fd9f49319bd41045237093bcac48437fab6ded3c873f09352bfcab98a3f0aba5e410e753a97f0725d1feb03eb531248d39fd89635588c6257b5301a64bf6586cb2bbb67947226e50a4ffd6c65bd5db552f28f04d00470f9c0a2c546518c337a6adcbd4c82c6254855fd98bfe809b8bf4465bf023346f5e6d0f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "064b9659eee7aa620b2b28f3d6544a04af32fc60f018c22732ba115b63289e5f", - "proof": "0a34c9eed0a7896b26416b58b2a837bcfbf1eb47d4f7d8a75010c6738ef6070da6b94b4cfe7727d491ed35cb4c7767fee36f35d7d57dd09a2c697ce2f1be350f70c8d720cf9c9e46f4ae8933e49c67cd4b7fdb0143af43fc5ba05a4710a0ef07feaab0431c6d0c3a6a54ff8a7a0ce296d1654e68b33767049e08adfb68b52841ca46c3f9b90c93a55b412d67476f387107de5725bea02bf7cd9c9b8392933b0963d41b018a0f2ac8484a96b786153442f96871f0e5ecdd3dcf916ba2a198e70ccf2a4a1f5595fb13085ec9a3d377aa55f2ca7fe4bad61405540fd21237344f0ca68e82af93cd5a7fb903b67d98c8bfde49c6ffafff830f55e77cfd407f02760ca8dd4a4c894f7797f4e913931faba6002360601be170e2bfc8a4cf93b80dac603a6e629082005b55b071efb669922bf13532ababc7bd82d36c03c5e61316dc6cc08b59d53dc6f3f65180618ab4dfcfa62db26421f924b03e1ea3c7e6c616ee5a1c656718209fdae0d82b82833fdb3d50289a3678fb216a955ea201f1b4d7fe73eeccf5ae5f42438fe7b033f6598bc1bf029c4739e6c88e09a7afd21f932f436738cf4f8eacb1ad10a1bb8bd3aea31d1e970587ca85b1d44f0bf633e8b7273210584e38f5dcdc8e45f9291a0680cc52a6061cc2dd309ab8849214f972941b5427e017377527e6c0e5a73c1637c5824c714472868d309035b9a5ad4b206a8303123c1f7c0857a1ad2998342be9cbdf499354dd9c0e32b5337f05d6eec781621a57b278a58cd206f409f65938688bce001585e6dbbd849b4ed4b779b43bf9df99722e433ae7fed22edab918554b1f1445bc9db5550c09808b554b9997958ef95c585b5cae91eda5ec2a05576ad829c953371a843e16b4cba55d988e5bf7bc761306b04c79058e426397182a38db0926c0167c2a60e9b5249a0effe91d870c91700b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0cb189ed7cff6a5fd188ff02acd757a40d21f1c4ca51ae2c2dfa57640dfc2061", - "proof": "7ab458d8038cddf9080973692f17bc36e2cf1357a2c073742a017086afb9784afe628064f3c5c8868088e47c10199dd8b1643c02fc6adf3b66aadbf26785674bc4fa95003d78972df160c61871dc8e007d2ee0c231f5e037e9468832b3a30d78c4bdd82d20c6444f5a9c38781b3c4528cfc33436ae1be9c6f8b4b41b944df123fde8c28f06e923b44bc47f971077f2184c8ddad3213285b3c4461e7d4041450d7980e3510e50a84606b56e035d44000737f6a0ba2aecda04125db2eb97f6910fce815d83a7e6da01fbde2007e7ac15d0fe62d5bd11928bcf501ff1583f4d0c086899bc6d6494909861a1b2f95466dbed3f841cbdc4f390d95e4a675ac3246301bc3602bee86cda463595322a03e7cc75968f89482d073cde92c24af099bfcb276c1e9b5d8ec46fe937ea5c41a5c57f3eb92aac08246805ac72c85d3196a1f34ab45cf8b5c7731a9aef40ac31c588f62e63a4246b4c1f58815bc235bce01dab74e8341a9cca4f5eff7ea6c76fb3ef31e114b311979b91e8b00f7e43783d1c3d74cc229e6b9a99bcdcbe16f479307d529fdf344f4ef9ead220711472d890abcf64c8dd85a740b6cb0e68bbc446623aa7bf754533e49d36cd4c92abdf632fc066165ea7ae5e9781a45d11b8b8989e7427f5e80c5560ce1ec1141c289f91cc828241528cf0d03407945fd83c0d9bf36bdf8ab8490d7552cd7bfd1c1877688982154ef6a2bed4acf4ecb6cbe52de77bb5f628c1b3ba91bcc45c5847fb96414eedb01866ad78843c42100efc2e16cd3b61bd9b5d8ac2c36eb81efc086c26ed23a5c8258ef6542ee3decceafb19ed0de466033f27a661200c1748e2242e08e8b6109d429ec14511c03b179bf8bfb8e05e71bae3f9655976312e3295a4ccfe03ff1c9a0c28c8076f8ff09ef9bb159133f099a1795048b4378f803a01ffd54e6efc90db07" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "42b65274d2b594f6c75bf436f2d346efdc0ef92be9e89edcef70fe16318ced55", - "proof": "7cc2e800871badc56ecbd762cdc4173d47234e96f9ff5a07ba8bd016de427f34fa2e40b6d36d869e3460e5bbeab2790c7bbda96c9ecbb71fd7c2154cdbde5133ce1e01c39ded35d31fe77d03df4e90b0f3987eb5c9b9d1fb403e06ae76aca168ea07dcd7545cab61e4993b1096254b74b656e279af01157d48e34afe70823e14eb5d60b413087a5c34881a4d344fe4cdde73d86f57a4a6fb2c22eae46ccb96052e33722a5366029965eca94dc9c036169afb020a9b073a0d10fc7c4570e2180d457b42054667e72ab6851d41c665c2ab876b34853f67d877b2eded51e05f4e03d4fc0131cb756bc9a5d14116c5f412a5f06d092dac526e98f68896b800068b7c90969a96437bd196f1c5e3c412ead4ff9088bcae40d7bd337c342eeb31080e6718edf367de128c5749664ca43753aa3a0ecef5c997a78e4be19cdaab4cb4b81a306b34a79d00291d7fbb090ca8041f4695fe9ce4539227b50e2de1a87bf22f2940b5b52222d1ff541d0feaf8ac66581bbb46b7339434c8fe7d0896a67384027e0e9875f7a3c6dbea6734920a5f157b227e1f2c2b3838c009452407d6e2e7ac7b525af9126fcf1b0cf3a48a9a6b4f433ad8769eb2b02defaa6d1a3ba22ab9fd5e50ae67ddd699f87e4b4aa6a8f82ec6f3f5242772674d26849c899e0cd3c15b5642861030b80a996294a187e93884e4726dd29c52c8658a5a4b359e19cf2b845a1afbd95dd643294d45bd9244bdc993cc1997f82963b53f2fd465254a52048a3b26cccdaf061a1b8ebba09c36280dd4862fb2daed0c7d76002a35971face0e31196cabafd78158c8c0c3d0aadf9a46660cf78ce36400b15de2032d6cb3f29a15f4ea666ce8aa253190f51ac218a7411f3682b6c5595eb68b1bd054d8b1111840803295819d5dacce0cd314ca4ddc1be6e590dc2732e9bf40d05c80aeb1ee94508" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "50b2cbee304d237a4fe98c37b2d9079257fd48e0301b603378bb563616acb00b", - "proof": "809fa2247df277b4a69a7d87d584b5a205ac16abaadacfeac0e6a5010201245bee1214c49a2d76d3dd4a29e43bbdfc9bcc57a2067931df46219fe1e83a748d364066a1d8a37d1e2b0fdcbed72cfe874da547c85c88ce1b14fae7fa76fac4e67ac4e5b200a582a48e2c12ef0d24eae2b5edd0568ce56cf5bc11e38d906442c339e0842e20371213809dfe08fac291bf6698cbab3cacb548bf947ce5d768c9be0215073c0461eec675e5896a1386dff58c88bf5a9f5c9bbe1ae6220c6031135002c3772789b0ad02eaad7e9acd3ef888d365dccc206ffc8597ef1feafcfdc9df04486a353560d2dc6bffe9abc69c0644e268b7cfd2dd410df10596140a4028047844188738f480731e6f08fa43ed58155a56811a82d62c50d9c9388369e85eb97fe8547d2a9ecee863f864faf1c6a93aeb5f38f42822a4712b9060f328428d9577b4e76e59fafb700d3913fe13d0c8b72232150a9d83defb648eb35dfed0e71138c2273b9cb612b500c67127acfe8de07bc39bdeaa037b1de81c57426a28ec1d7430a50f4257c70bdca6a52fc8cff6599d76245b0a65f398c37d60584d8038867c9ef83ba136f3f9b8d2876c99c0711f553012ad1892b551c230bc94c5b1a5907e1cfe6177c1d9c11d1cd07eeeaa3fe285be8c386495eea56675cf3237cfd4fc0038f3020ab9327b2c58ce3c6b67da64a154a91aee985ec5694a92916cd27ee918407fe8a898cb6554b929f2846a57d47487d14192a770e0ddc2c9a9508bbec4314820b8e071e8861661b2f5614068857ce4f15373ba19fcf27d4c3d4cb400c523dcd0c07f070166c656a8f7aef371619a6fa78d22d211acdb6d2a03347237593646fb8742d8e579eafee1fe5680b2e1df2f56102f1aee8ab0d6dff00eb11d3702bea8cc3de6dada43fbe4ea5dcb4612982a02ad4473933d2ed884833ce645be08" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "90ab152f8f0aa42e40c4b8c14444dfdb4adbeecab5afa6c8e569942dc9c7e97c", - "proof": "44ee26e106d5dbe985092e2b49469c04cdf17e31897b262259c9558561509f018acd19432de57a1d367ca19c2a7c7465bbded4c6e6ad1ad288c7d73a99536e55eccdb75b87fe397eadef4c0d772d7b92d00154d3079e2971fe211c159290d638903d8c9f6690ef84f5ce3383a0e9d2fb2258017d89eefb5aca3f5dac49511d029d28fc361a8649b58e39927d73765337fa8a9e7552132b47de2f5c42111e5500cd0e382c71df1f8ddb84793ff864ce39df17bbaebaec0033b11d6ae90574e708884ea8f294d8697655569474de83235b58c2d6e387487249e98c7659ba026f0f90e145e7c9afae7e91ba53ebde84bcf16283205aad83fc29ccb5c268bf854001bef8e4a34d18c6b126313fe008b5d29ad7ea2ea9bf8bbea4eb225635b1b6550d26812d392d1aed784da202ebd9656826962a49a38a57198de9e70f540474df7fbe51c3f0009fdd329e71f6d99e1ad4e36350a131eab610521a682e4ffef6506d1ef88c567e9abc6ad3fb1013111c54ab7c0fdfd8058c44094e5f71697513126baecc133ae032701bbd42b4875b3f4da2bd969fd7006d66c4898e7bdb88763e2c8032a9800f7583c02aa60b1a715f0b369da761d55c05451cd42fd19fdd36d359d29fabfdd85df1914d94e58ba016e42243c3ae4565e8ea39a8a265d35289a10928630dc9019230de3815f0d0f8ce3dcea62f8833a49926a306e01b91a1aa40789c8e61781c1015846c67cb649b44d5bc37e8ea4a4b893d51541b54b05b5d4a3dc84b390da37acf9abca30fcad1cccd2809aee6199f216f2f8782170e35db2062de1a217432876f5c271c0bcb0a54bbe5cb1cd77c50749f87340d69b21a128c6d7e97d6307243802c84bb131eaa429de386bc7aaedd425faac8cd10977922df003fe0cc00b7cdbd998dd7e1eb9ca8ee6cb1a76763653f0c33b4dc1acf8df6750a" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "9ab7224c1f2deb0ea32f3d99334b95a6e69f7f3a0532d408c9d63d5ccb424f1f", - "proof": "d453784f8a96391ee287d953c344f2388c5935026e04ea9c672ab99dff47c907ea0ba82fa07b09a671bb1b141569ccdb598f79f9ab331672471b79093c77fc77a2c24a4f6057bd379b882b011a02ac0335a6cff3af0acdee139e0e3e080bc63d78cca53b87c0797668fd0d92fe216bba13212239e731b008936fae2e55c80373e2ad9d5317b0b768136632b706c50c412279bb32f0a28c9e30c991a232367f0abec381753a3abe42f4d24bffac5c843250ad4639a0d8ebfc0a89fce64dd4d00b423c202350e73ede6e3b3ad68dd1eacc9597f61b09251e123900e1538adf490d84d431950cacecee718b15d3c5dcf935796c46d434efeebff02ece651aaa706ef8f833bf0b121aae4efb861d12a75db865acda9e2fa09157f2ac6fb0edc21d4b76fe301d933f57ae6176b5820acfe8f75c2b40852484f6a78a4fdc219e2d2a4124c0c997fa9fb51a9f923986ed7e48d84d72bdf09344fceb4311c417ca03fb7c087b540412ad8253d4dcdde46eaef95c09c1b894122b20347bcac1afbfb87c1cb4ffbe9d8fcbe59224a510e76c4fc48d829ec2c0fa46ad778021451a21902607a25902f64636785bf9f189dfd61607933d72d915bbe1719b1dbd761aac046b0fde595424314e2fe41aa7ac7969b50f332f51238221d5c0e6d2029e63df625b1f52aa6883be6a45f3d7d5c4a5d2a11d257c71c07ae12de2b768481fe0a0a5a945044555fc92dbf514250ef36013755a86083b84e744ce6ea94e8eb32387f4bb483e3e372230db8c69a645cca68696c69fafaac507b2c23b0c1ddddf3fda0a857434810db367bb51ceecf453ac3e2961ddd9b7b3a9ac0fa0946e7ae882eaf505149f793cc4667eac7814873a693a98bb09f4735d2f494fb369d47f2842e37e4b0c50d0f9d51f1bb5a336f84517e8c2889b8d8946888dbdc84d2272acccad376a08" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "dec93694870d0bbd61208f37ca76bc951c7d38fe6117de9b697b8d6bbd82f25e", - "proof": "7e22e89a6047d7f15bead79a1da3d6715aac5c2736cdfc861c90667f2daf155d5411897774bbebf3e8b2cebb542a65940270dcc43071ab73e73e6cd572576941c6234e80c9475e12d9b0b81f115968a53ccc7d007e683911518129d9e7f21554647a352c319c7da6702ebd26bfbd2d8a36271872b5c457ce0b893fd13bca167587c73f1764186bed4d7e4d571de394d541b4b6533dd627259145c874e490710f219b8921640b80c2ce51b4baadb14274d714dde13fb63fbf1d8d32565d0fa10444e243690349247f59d2180e833c0eb1f2812de469cd085947f8ce0be0783f051a02f52288859e9866ef1ade7a2eeeb41d3e9f3d61ad376086fd6c718a2c54301e1a46088f5ec368600205cb651472544e41bbd07758022a52dc684010d2c452faeb0e1bf6bb754abf025d4da2d6b0cd1c6bd97f9d4d69be9a45c5825efec60f8e9c41ec7944a4c0e997db749eb71d2c8ce3c6812344d16ce2d6cc6a02f66a284a55f6e35cc968e9c62c89644d62f776e4a6ceb0a442d2330346a4b15ae9f5607891627e5d7f205dda1bc267974169e26aecbe32724ab1912070b801953bad29e6b5c16dc3c6242febd215cbe804f33ebf5c427fd16d06fb0f5fd55403ca1e7748972b2f81a8f705d3616ebb20611524970343b85fc3fae63dd9705a4c10a75af208702e035344ae8fbab8e1bba91735859ebe1158d64679570dce3863e93b29c201e637fafa411e16d7799110bb2c643e17164e466f6158e080e1503f4b8d77fad7c707b49445e5d7c7415e9b1880eef16dbfec2976d6bfc621b798d5acd7293cde650490fcc64bd62b3cc10b71f1c3593a6e82630a4add2ee4d2d0dfbb2d4fa7eb11a460abdaff0de8e667ec0a429afa719aa36618e20ce660426c182f900663924d2d05f324fca4a68fc8c19e37016391703c71e8259c48989c9a54362306" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "eed51fa3bc418224616e3048472d1530a33a3aab028e49c12329bc70ccf42f3f", - "proof": "06418023ff6a1522efa22a18b03a76bab97a2e6507894baf85a344d10f5ba87fc4880536cbbbdf502bcb285c9ee37d382d8ac71058a2846b78b4505d4c527429223f0d45ccc6f63ec8c106226f7bc71d7e2946b6123f2e004423dfc75fb1141d561481028596e2b5380083386b6fe236611f968385be0e186c5439affd4b5a45e66d4f77ee00d6ac84e5a7070e44e22f0b5ac8a6139d69ffef0db05c3f95d00d732b07f1f589c254f4c5586e118dc835216e0da665140af8f35501ca1831f90b55326b6fc16c5e1304915d2259be4da59cfa6ab97743a96d80f6d8b85378fa0e364162564fc7b17d846f6908e308e5b43aaffdf646880e93e8f8bbf86a7cb813e214b50bef86315bea53637fac87482878845c23012ffcfdad6884b6fbdd6c11fa742ab5d295c9307afa0aed5e25b52b4904edecc6551c86e03b50365fb1b4441e66c57596c911c552250d5f0a08f9c86cbc9c9669b4e9aef91af44be7bd536f56205d78b47d49cf967a6fd4d2d8fb9146c4f54b347e65ca9c598f53fa5d3a127e672ff84b24b04e9f0f0666d45d3b9eeab5ab249ac49f7a2b9a009d94a4ec07980b7d32375903bdfc0e85b8637ad9972eded9076b77e32b5222f55d1ebeab0e905f257acac037fc327347cca20cb8b6bad2b9c013244597303e7a739dc4052b48ff1e31f77866283e5aa1edce4f4849ac0eea74a84fcacbe1eed313725d516ae4ab6a901c21bc361514f3ba31b854aaac5498939e81c6a93c32fb05d428d0286ed30c87cd0d4068ec97ac08462987930008216af6e706794754ea016463436590e0135f2eaa6f7a0f9187d2370ffa45e99d237b89bf82b19261cb853d806404d843f643f7a1cf907919bbdd90abf39acf8dcdd6f5e182224d06fc09ef62b70ded90da48d46e5f8f75ed5fe2fb3b79b642325e4b95e107001c550deadf953807" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "f67239af0d5c5048bdeef0dbe41811ddc2cb9d450ae5b8aa07fdc949990ba501", - "proof": "0e57afcf8722082f2872660bb1fe0ff62a26371b945496bd2c6528c753c74517ba50de08317748458f36fe62831c2cb03fbc39ba0dce3525821cf63de98835451ceb7c0465a334b9d7e020c046eef2675dfe9ccbb10ef741b7b72e7c39dfec4bf0c8ac8ccd335ef21d72bd2b9862aacb10fd4339431870d85d2620b3916d5e3af82898bc77504c4593f2faa3014f18afa5373a14b7101a2e9dad6676a956aa001713bc165a196d729f99dfb1e4a3c6d20bf364f9fcfb308ed7cb75fde7e82608230a8de9916dbc85bf0aeaa666def95d970b6794dd4516402849b53c8424e003c66a9bb70385e9962d22df6ccc9cd90e5f0e281557c1e3b2dde0c49d350a2b09e4ee632d83af97252bc4e869fd96de3b944d3cc6719a645e074ec9e2a95c8f04c28e31ced05a3ccdf4d01fc59d311b58fb076325839947e88e9c2c4636ec2d3ad8451b1bebf30770b670e1a2d6d7927081d996c5eae2e6ac35caf42c23717d26bcc678416daceed14e0b7d3591f99ed7db13b0cba0aa876b64e5ca0336b2ff71ce2bf7dc89f8b0980a397a6714a44768671718089d7f12ded8f7e10084d671678abbe11415885f8aa4f65b18e12447ecfa80e1a0d86c38c543095ea7e2d588771c3a84143f385b49ad3d4bfce3cd2a0c250a16fc712af227c3781f85d6f0fb5ec2f42baf5422b1ac6f844aa6599b08923620eeec66e61d5fc66f78b1f4e23a55de06a5cd198a1c1bb62870106619fea3709631e823c05d1a84694c8b556b2e597897e97bdc6e5d3c2af6803499afc3c1ed7f2dcb6a47f7706ee97cbae5091024365836910ce117b9456ecb3f504acdb182b92dd58e505dfe59c9b4654abb4610a1b4dab2b05746efb393532a3288dea5d2b154e81f4ec64ebdccbab02fdda10902c9c3fd415421634bc8230375ae64d5a3dc9f0fdea6e16f17fd98774a0ccf0b" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 10 - }, - "commitment": "9ef8501388da66549aff0bb1cae35fe8a4ecfc3c79595726fe96dc21017e6a75", - "proof": "da075d3df5bb0b807ffea87e32796f336d2a617efc1dcfd655030a8281cdd66d7c91d2486d2db9c05ed1a8d602553b047617b6b91fb3426bbb4dc41dd4aade6ffe768e85f79c7ce7af6486c55d25c1124e3355bb17b250df5193985d3d59e3544e1ddf68d721d6360336a237469dd14d7d65d48644de40370a52cb7f2a17194b21ca0fb127e0788827edf04d001e90c8b3241c1baabc322af670caf95fe0d50fedf100c7ccf7128457e37b44d755151b17fcae5281d6bbde1ec31c709d629708d7e8a731265be4c6e753e04ed3bf2f747b3d8e2b2b660ba6c1fd4fe8044dbb0b96b834875d21d4eb6dcd76100f1225e08f831d70934385276bd74477b034177f20ad0c301b8dcb3de3f96f1181fd26ff3a5153772d4c768d64917866558a253996656ec85d6bb59e9ac35a6115ebec86a2b431823b7edd3970b72a073f273b2a7ac109665bde6340f2b75b5f8bd0ab24cd710af4941c834ebc9accbe4e12e968b6042986cf3e13bd063ee5f48c43e1965bbc88b4a01af889d2b8dbaa2f9c43175cd89daf16e7980d5bc3d5f65eaf23dcbaf477f1cac616bac92cb5829a33ac697ed679f4ee4516442207554953ecf6f33452ff25bed5f8adbec28ee3aaa5a568fc3059dbc76011984a6c5509c4d90f2faee8e7558b6522189f25d28e9680624ba8a8227bf3acd30b29ff759f505f003c9de25a8468e96b68d51cdca271a9714566771552095683d46052dc3b8da6e62fc01fda6007ece5d06bb0ae7055821f519c014b40fabd3aafc2198c2b6ecefbb84be7ecf62adee9d18ac41f101aba225ef65a22e3459663b6a7538776a43cab6c9597303f78481024c518ec859d6c005e45850cbd8cc34e7f463a42b1639ef967f9dfb0130fcb07f4dd2619c78caa200cf2bfdc7dafa7c59c725c46c4792b1c70eefe8aac5b41cb273c93664ec3ee1e06" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "34397f307e22077eeb3bd4413b76acedeb4c1b471b3ddf8a703b07febec9927c", - "excess_sig": { - "public_nonce": "58b1fbc61e8a2175126427614423c60abcd6231ce29180336b25ae39ff9e6924", - "signature": "c709a900fd4e1856171f8b90aa7a964c17e847b0a3cb83e97a148ba933878005" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "369f9e7f597dfc380ab5b5c84a38683ccfdfc91370069e319450701736db2a79", - "excess_sig": { - "public_nonce": "96514e3f65cc7ac785dc3458db1c72c5a8542b78919f9bdd85d253c7e2f9d877", - "signature": "5d659d904f9bcf41193619c128b9863507b7ea6d00d287e3ab9344ab6831f803" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "543cad0c4a3e4288cd08e4a8f29702dffedcac9201ccfd234610d5d44072d33b", - "excess_sig": { - "public_nonce": "9c3ed035c24b35037d642e14ac12f0505d70f8c28f88fd894853029fbfe55c4b", - "signature": "931bec9ef73776c9158ad7d53e72f226573650736c2b20a8df1250c3f2abd900" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "96437dd99010c1e2f97d0df9e4c396dcd21e4fa6909cb3d6aef010d77ac23a23", - "excess_sig": { - "public_nonce": "ae52b575f0fc2c5622fe248deec9147d770d3a4f6fbef805bf2645c79af8881c", - "signature": "7f2509aafd27539c0ea584ea366d584542f8fdd052b3b32afb906374e45ed902" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "bce8073fe2b71121066470520cd650597b27125e9290197d4ac224217afdaf23", - "excess_sig": { - "public_nonce": "1c84dcdca48041933807088fd41992324f3ef2ff7681a80c72917c04ac720d35", - "signature": "84162d9621c76604b097a855a6c0078e0aacabc501fbdccd6f6bcbac6e992103" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "5adc945d4ea17fd65a2e2ee054db2c4c748daab0088bba9996cbcc416a5c9403", - "excess_sig": { - "public_nonce": "2abaa8c5347ae70d0fc571235e3b3303b8991896a3d4e44b491be38bf13c2c45", - "signature": "16746063d96079f50492607f679300f0db95e21544c3302ce326229ca167cb0f" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 10, - "prev_hash": "38b477f43a8f5f35b3fb78adbb1f452c55dea4c681db144b83581f0d925d8ad9", - "timestamp": "2000-01-01T01:11:01Z", - "output_mr": "cbfe7866d438fe47485842d8632a05f3a37da78fcebef3a8f784bef1e94155b6", - "range_proof_mr": "b6db67d3055db2d0d5bdc77d8d003fbd89fc6373f1ae2e097e5e33d99764299e", - "kernel_mr": "21d776a206c3c9579942bd31d761fd511a3ddb1bdce0129e131e4fd98f76df45", - "total_kernel_offset": "341293acb3b4d785c773b6d8a94b451ebae3d480e79ce32d0418870da7341a05", - "pow": { - "work": 10 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "008e1489381ab5ad8e5a6e597f5315b8e2bc9dee3429be1567b39dcff3f84853" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0cb189ed7cff6a5fd188ff02acd757a40d21f1c4ca51ae2c2dfa57640dfc2061" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "42b65274d2b594f6c75bf436f2d346efdc0ef92be9e89edcef70fe16318ced55" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "eed51fa3bc418224616e3048472d1530a33a3aab028e49c12329bc70ccf42f3f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "f67239af0d5c5048bdeef0dbe41811ddc2cb9d450ae5b8aa07fdc949990ba501" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "02523e8fd2e00ea3de0cb19079f0c19642c5a2bf1973e68261d88b0ed80cac21", - "proof": "0caff1aec0948f27f9db2b7811df501deb8193dd364594f7485400687d54f46b98a72d3b9cbc140c5c65602333063328aa8cf5ca033a32776c35945de79b9f0ea46138a313e77e7ffff61b0ade564f578ec5d01691a792f50a21aa20a1648b300aa433c347ddc60635e0a13d6b3f11a9f93ad9e300123c500a216c0c3ef1a96900c63ec7247955319550ef75c0bbdb4053cacdc82a61b7df0bc188e40a83b20996f86a172397388427355b0b77d0f9b8b473234cfe70dcc5251f1feb351f3a0ded59b9cccd440a1e346f5a100a099afbace2e5b66c04266688440087bd13320998f777e9c338d9a9ef6c2cf1e791c9486e9b9c73b57b4f81212689d4c4e7753084b5772ff181d7dd9be3a1d3be24e5b0562a9bd634b742d02604c64b76cd00143878ac13770538551038343e1835eafa6caeda946f3615556f77c98fac90e4308a819a5236642d0b0517c0eeba5fd0019d369083a4dd9002ac8d896306da583516fefcda8dc81a8f23822fac92c8f0884ce2004be88c58c217eb14bbe419d8347a35461bb965704b09810e96b42ebf4858b62c1543c97483d410ee10a854e61efa27ccdcf3f19ab92e203f5c81663156cf54da96153de6ff9c3b5fe0765be65146f37ced781865954757c62699ecfe77b01a0107b5152d14a7502fb58053011f40a6759f0e955a89b1d83788f6940c6cca3374a69bad58b27cc0a9a96e28be22a69554f623fd4d2826952bb6fd6f5357995033dfc028f8c674976132e32ebb45066a1cd22998b21de9fb570437d6eba4da99a67beeabdc6318c6249f8f252c46ba3961d3b89352c3aafa112e257cb5abee5f2e2d53391b56e1cb32a534218c1be13cbebf202f284379fda92ec34d9de4d8506bf262c7bd6b6498ab386fd63d0337442aaa8b0fb05eddf0869562fe21c8da9e44fe3409760329669859353aad02" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "10473358350647dd31e3db78e776fa110f2193214f07076eea7120ed74b4770f", - "proof": "2c1eec14d532fe0758a66c3d5e3e677ad6f1a3e992ad683c973192c4e8dfbd5c2c4d2a6a4a842aa5e8edbcd1048706aff7d01491510e2c9410e2cdffac913f2e820746d99a6da951a51296a084c77c29e8c87f9ef2b79935db48126566d7e22408f7db8c90b871e30f383bbc388b6fa0855ab1afbaa3fa0d2f791579b404de5e43b37471b8bdbffbd392bc29e665159a7b56e89d3e21f92eebde21500f8b7e0c1e0b814c98150113e162da6361a49813d83dcc39c1fb92cfbac6041192a8f103d80d5130bdb671eb9c884ca054696ad04be5009de9756752c705339c56cc08009a90cde6c4b39f76cbc4e12ddafd6dfb0f3e3d0d2101e48a2197782cc120160fec72771d5ead0041ef15d34f7b4d67243b231b0d12d73cd4528873c9b893601800623ea0cbcb6db71faa71bea005f56b0c4ac83401ec283a6e56163aedcd6b77160ec76ac9f6eb2cff5e36c9b3846258529e99cfe8a16f353f6d8dc786018402b43fcabdddc276c3d639e1935eb4576e7eabbb67efae3373c062ceeee8eda45dd4eb7bca4e544c857e10648f3cfce25b5c7432aae31b0339e961c404359cd9381e11787f5481942d82e7b0235db17bf7c441e35ac08a4337279bf6a481e41b5a0211585e423734547735af3fc1242ba317fb1da86e84d21a47ca170a1f213d6fa26835d91f1c2ac01ad220b143a81a5f65268d466f71783434d5b7fe32cfed7786b99cf8b1decb87ee5dabe2dbc2a3aa2fc719630f772f905930e4343a92813a8e9e6c4c25e00664ab19cf1a37addee908f6e0089e66899d722bd0b4202bee060e15da3d6edaf730843b8acce9075f3c04b3004e04c1cbe9fecc6dd201ebd66e65a5461ac416ba4e71d2e21c6dfcdf8e0e839e1a7afd053de0d745ea546775085b0c0ca0da4757489626790435c9cb36fcfae2a8295c5b179a4287108c08af01" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "28b2e790ce8ee396b7cfe76bbaa0ae47620193aba2084842f94ffd99e605810b", - "proof": "36504b8e4c745a717094e8f89fcf754d536b5ab9083f3b0d389967d4af002a16402d271333683165fd2c652ef35062d82ae0a1c0a76aa4df1f31be9ba2bcbe63caf12e072c0f02c05e098473a1070426add836a2f5547b35d61d2cbe56e3d421ee62e4b3212333ee88f92d64cd35917f7e00230c1756c9f915745cf7e922f2320489f7ad8455f91eeab1f0f535051cc1a9162b73e8ecfb99bba8aebbdd10030c01eca61f2aa9a074520b2cc37e8235e3f259b202d0cd9d9af98bb4e92037f309b1a7e2f3241c11ccc9d871f025e6b719208a87c637ab3c75417e5f24e741b20a202daa1a23f998188566cc8e8a07a15fb5ed408a7b54abc28475a446f7fbc33096542fda2e8a6cd32c1a4b811d17c089308cbf22e82216b049d2a5d185f63f250ab34914cefa674829481c8ebff98a069a943b333f64d1fac0dc8f75848978091c7fa2952541e93d396eb7e43e0d9996dc290ae3ba0beacdf29c89474858390f4436ecbf2718609c07578c92da3eb5f49423f739b97db40a4bd2254c6c69827b384221e47057ee82f107d9d6ff9d1bf2d4851b4db43b1819d15644e114dfb55f809cf6799143d48f3c89ea6821f45f46745b35302816035d93f583c861bd4a7bce3f66550b411a3f74a6bb6738aff22bcea8fa305049aa3a98adbb696ebbe23f98d9597cf07c0223f440339c7bb98e2b1c8896a54e46c67b6896075288c0722cac740b94c9bd608c45cba8ab834e35db40c19595a889be20dd5cabf22a5308375efb0588ef4f5e38082b1e09e368afa0af1b6dc6cb612ca72d7578e2c6067666967cd43b6f50d445db88fdc241406dc7ecac8ea605affc9675bb2cff86e48b49c6d82684e63ba0664de997eb23409b9d01a0753395066fccba93a3bc7448110fc379a6be999d7cc60ab964393564f3cf162ee3373f3dde8588bb8db816ca6301" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "4ae5363513456e99e2cc9dd90813b94a6df23790262c290a4e878e42cee36a4c", - "proof": "0460a20d5ee6149f72edd64ad2ca8d927f68752c78e3bccc0958d6b537d34248e8b2861d5337c2c19392441af36e24600ac1519732ad9214dee8ca685d67c9791231f3a9406138849b2e28a659a0276f720b22836b86051f75855fb3e4c82e0b309760c34b94e72ed269f3505ab678f3cfbc1c0d891f93b9b831f784782a190763108eb6a0e3fd2e9f7ce9475f1787790836486afde2bd2ed67a79a8227c6707e8e941d418b79c5bc45f12afafccef5d9fd85186e9f1026728e9ce6bb65ba8055b52a343dcd5f118acd0cae03fed029eee5b5b1396c04018597f721aebee0e0f3a692c097a11f8acb14b97db87a79c8b7934c453c7d22c786567b92e653ead691812a48d30311506e19a4eb6772eb7c553c809928d5417af960098aa953d7355a6c4c09247e5faba6988c57d76b62d55c61b5860ec84f3a3435774e1d1c7597118f6ef2a40ffb0d3a013acdeec326856007e03b3b3873fdb801dece878bbee42205e56a355e1ede5798eb720b092d98c56d637b72bce539664aa35b7e8019a094caf9b0a5c3403bca880c82c339bf0cc34397f1e5b3e326aefa3a27cd92eec13f8ac314ef08204e80bae3940f2a8e4b72c158820136b48cc0f91bbcddd66b523c020534c32fe2d799582d2ca0ce697894c15a9a589540bfd0711a928c5230969bc2c543addcbe9bc595f2b6e9d29bda1fc1903cc8697ebcee48629fbe2060a7cde40d33467616ff1ba9fc45cd4449524d3c1dc02c46fa3def8653ffee17a3210e03261b9eafd3a2da1276a37041429ad220c2b8d83808d7555f211b247a5fa46326ad145639720b33e51eea4cf5b57aa9e2df3c605d6172ee0572fc726d06c04183b4e1134626d457e3b9017a686b94bd8e5ebacd16495bd85e8323c5f04b800c6aea617534e8ab3f109414ae023bde5145d09d897622a9e44e54e16ef48c60d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "6c90af20dff9239de6a9f433d535602a85d8aa1b8d6e44486811c0dc88b1fc7c", - "proof": "a4f176edaa24ac387bb997a157aefb4fea99b3ebcad6eadd9917e31f8f372b004c2848d45316d1f53922f1a44a7d94385e26799d060dd214ca274b9033da227f1a6b3090a7295ff5e124041d6984b284709376c5c797c4860b178097a7169d0c88599eb76a1b30f081a12b600b514a46e050d67463072307d799c9763233aa175b15feea9f61059ea4b2db34a6ffcef5c2bc87bffd6adc819838685800766a02a2055ea381b546e515be50889b87bf66e5f64a5f1b82c0402fa13d26e142960bfc7bcad13c54a3ecbbfd3d1e9bd70fd68ab666486e3ddc84ef4655523499d1077c248b7f95a7f0130045397b524c6da41f5aed41be4d0869d8f80915c3657150bea4df7129dc47e3af496dfdb2fc991d7f0baebc3b3a78406c500ea8b96066551efcb5a7c19576f14c2be94853f4976f1fb9d500f2dfa45e6a16dff444d7df34a2774c974bc6c81f05171fa392076e64b199357635bd93ea4b6ffc40ae58cc3dfa3c69685363f142e908f3e1868e6453d7ab4ca7cdc3e5e5a4089c85e59d0f2fc811e1fb4925076f1d674733e751e8100a66c6c5865a8f2e6c4094965e150646e4d04b87c04a73d30e8c27a4515c90789d478bde96aa2634d3b4ff18247c22511cb51de8a60380d1f3a536e1b9326a1f77641765c9fa3f6e29256c9a07a67a4fa653e0dc6e8c8ff640fcf5ba0ff26fba8118ad0fa9ae57e0cdf1a7f5390adf17f0d25ea1cc53173a1dc9f97ad7d647dd9c0667750553a6ce834f18a68247f90490a0abb1de336cb0045d16702c4a34da94d2dd82977772040511396ebe64116f605a0c54d9df11a48405b07cf90433ee22285734e9cb23b6e393a56f4fd4da798d098f888354991fa6465e29d17f846ed2e23cf26e0756a4dd0ab3f70cb4bf0dc7ac93e16d7bbe13d9823253195655fcca100edb08e7d72aa570202d7cd8430e" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "8a93de3da2089b25c698ed9ebb2ba64fe3a22e3fa1efbde6f968bde886295b28", - "proof": "f4d525e92c77f2bddd26715e8f6a48eca763b5d2a3d193e9eefa8768caf7c600ba7c6f0acae79c76cc3baa3381b69e885c8e0c0ca05b581fcfc37908607bbc31c4cd855a73adaeb2190b422f9c85d8ad27f97ac1e79c4199b600c9b609697c46de831b79ba5875d6bdbc7ec2ef68bcdb72203c078941bb829f6db235f056fb0831d6b0e3edee4dafc9cca4b0513f1c6baf9476c8b4d1e441eb72414d9fd38900e2ca9563ec80c635160ee1a104c748d660472746371c4a6cff81a93d52d7fe04b363a8feb09d100dfe209b721ecc02a77e383f4d867411cd206d04c84eb99200cccb9dd642dd95346745e29fa6ac5f92c4c1814a9494f5649e559bffe52aca61d0e4847d3e6a74936a30b7a3d840501acf88988788872ff9af89669eb637665a148d3542bd850723a661b53930a2a2ff8abd92b2813f58b66a9d340af499122f2826b0ecd84d7d981162a5b0611ba37f026c2e91a765f73b4c949e63943f6637d0a41cd3d29b80d0175110e2a1336621ded5e8883ece84ea638357d16fae9055a411e562a305b12b39376f4e662ca71c0454837c828c3cd5027d1d5ff492cc199cda525a98b61ed8e73a54eb43783648e37fe6351ad8deadc9800349fe18c17f40a8ee13b41ebd3bc4f52a859dce9a871bb2ba870f3a1a6c18bc11670cd5294d0ab8cd0f52f53566bf362a9baf39a9f856a8663a5ad91f147b78d67f4a447f4314e62ef78e9d45568eeefcd0ff7ed996981233785a390a488e0b40c370f1d96464caf42edcd07647d8b6a8018c47b484a618e47bf6b36ab681381cfb2749250be873fb5ce2d2d5b78383890b6c8c4c1951f92476b8d9a35fa2d369ae2633b90ab9c8de497f821dc2bf6e6dba0153655fb7d7cc027e7b2bfdbcccc890351f3a03f679ca0c1e1c1b14182489b23de00484a700be0764a02588cc08e7d6eec5ed01" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "c8ea27bef6fd92c05d57d0e523d2d283b3073861e749c152f2cec97b8f68703b", - "proof": "ace6517a26731f0679cda8cf25775f823b2699c622a7f4c2498395e8c10226505288e44f474ab4166ddfeada2db62e4b38d5a7aca718a602d094ef72db08b00e98d52d2fc7ac413eec7b25b60c061e1984bdd2e09e5dfada05ae626eb7757d44966a6297fef4fc48c292021d60762c9c1294410574fe3fc53d7746e65677725e79299e2217813848e4fdb85f6948960da2f381db383003b479a2bb48a624120e54ef7f603ed63348afa5c10b4caf3f7db5437f526e329d1c3b3f4ca86751c2064ac4008abc94e9874fe5318b0b11eff47089ebbce203eff5e5c7dd9e93956d0ac413a5315ef630a76b6f149f636a1f7cb38be8feb01db34ed2f2f60b7131ea04c41f5775ef7db20bbfd5ba061f33a818af43d4c74850822376877b3580d5506a8e7e5c620c1eb6ad45f4ba16a05b311198ee5e56448ed8f8262d62ce437c682b1ae3d40671597085b1b3563ef2c0e54121bc3e22cd53ce0e20b0ca8f8a2fee78f2d1743bbd4ca057edf1c5e37e7cebe9f79fb8489b3229c907e25587c0a8761536427c92c60aed1811f429308988857c267986b9492103672b612a3a63907453081dcf22b08fa72d719908ce5c849e173aba06c395307be5aa8d1b534cebf51a6ca8c25b03f8122fd10ec9a7c1e4fa578f841f6e04cd57fe86d5875f0cb8f54d78ea39f37454b35f5ffbf9b18e66a5a1ebc93777eb8bee0cfbfce14dd7ee1f03da76269869f4d09541f91aba54e93ac52a956c10736f06317ca1996fec1d1323063c5265c15bad8a46a8186448b80cdacf63ae632b0a1d540dd6c685d779de64d0e6482b576a91f16f52bf8075cabdc65e0e63261f4890e981393477c67e9f0fd515b1ce26e7e00bf709851a2e98ec835ca381dd7a6ffac15220517780e45702c1f75b9c84f0f2f276ade61d56f7d1c3c87b26103083f846a35a5d622213e404" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "cc5ef773cbd09dcc50a1253750f9dbc0bf78777b9e3aedf956e7f03d31fcc801", - "proof": "b4c226f0c6ac691a38f166f4c7004d02b0cc2fbde2389250983d66d49765e85572c38d3e2604e4a5e579e582819f948238ca27bab724a736b10bba70e8972a40c64c7f2c76f176bbff2fa544052882778bab650d2bb56fab4c2be3ea2cf4cb5c3a4dfeddfaab817f2683f662d8b6c4084dd6abc6cef9b9054b16feedde03bf73ebd2f13c51a71f04472a02b447f9a9ba3d710f03a94fc61ec4e572779571750c75024c96b10762b97eb269e360420b7c9eaa4f2fcb585b0f67a901381b69600d84ca5b3db7ce81ccddb066afa975aca1cc7e8a8d817433fe2f14958446311603f62bf4dcec08e62bfb2d3f5e173127a563659a4e65ed82b766d209673f5be537b4004abbdfcdf5dab5684f50ada1dbbf50ad20c377354c42c0078568e8bfda473c9b31f7b41d9c21d8be375b5e76b984b239d037d1118594afcd787bdea3a933dce83fe81a71d32143752f31559293e7097763bc21b3a55098623f37dbfd8f622ea7544e508b070605e7a0730f539334b31af5baf5d49274ea920ef9d4c2d4043e9ea0859999d08e64b181397ac28686e5dbbf3fa4942210f4c0ba9346e48d08e288448de7ae1f3494ba7ccaff475756fe8db8d168338cb62ff985674c7e4301109ce38999262c58a156e44b13779e053bca20da8b6031081ac595c6cf8eab49d8f0f22b163d4dc88c81775ad201ad21da41fa74ece113e63afd4f6b4f19ca587497e9c68b377aed942ff57c5aabe89c53762be73dcada4f1350e85c58508a671458943485e7cc42474e1534ba52c28abeed78d3b04048bcc43eca69073c0d6bda10b99e5d0aa77fed59cc6ed158e7d5516eed46dcef2c0e05ec61c2c7ad743b4999a5f764202b6cea75d51da66e61946dd91d3f079f1372f3fa5dd345d0220809ed7cc1ba8a368c19cb5818045495e21d0c094d1d06d286944af59913420008" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "cca7c18d51acc09d53be5da57ea36053ea85c843e25e25b48cca5027e5ce311d", - "proof": "4a90c10acf5db49004ae550710cb78272854d3da6632ecfd769b5458a87a156de23168702db17a63714244cdbc5f16c16017eac0a4a25eb7ded3617b37c33878661bfa156f2d12e9b80c175d1a36f256451308359ab856cdcac8b23424a0bd2d0602ae08a4fbcd901755ed9c08aba466b77e16447e27678d0dcc9bdbf235191974e91beee8afa967c23c98775e573efb7ab69c291fc80b44416d42edc6bc5104b7488d717e0e4fb801009f6b6ed9ae2edbf3b1af6530f1afbff4b132b957260b50625355519be642ff3ad6c1fe1f32d7e629a0af42779f70760a2b3680515a005486c2e949d61e799a49d585ef6e6136314c2243305a03342cb0abc93178a252e24dd0ef14112fea4f3b64960c34278b994000f08e9b666feae5eb2e3640b87a4ec3a51a0f260e4a1d4e2ed84f1cdd53720096f69b855e468ee7c0403d659d0bd69f1fbdede625ed3ffdbddfcf3e5c675eadaa98b221a4fe124fe2534725b90be673deb335f6f696157c7a8651852e27b8489ac3a849b2e87e2c53520b25ff57d8e25165081d6c5c915a038ff542de41ddd11be9581d5619a6f23d1a800c5b42444781ef5bae55b19955e89a059091e721967711807c3eecfcb0ff3fe1cbf9548c85c1d80c70e072c8b61ca1b414a3fd2604151780a7a1fb3ca7176b75e4732c6e2646a21c3c647f5f5c31b9191b25197fb05f37ba918ca88ba6277219e86c067414bbeef09c75521db892b47c5aa30a17dc60da48bc108a4087fecf0ac56f7caa6e72b0781fbba0bee4583a79c2e48709c55dbd16fe51d151cef5b3fd937227546cf5d211ffd18d724b87cb156b13a3b50e38fb80291d80750b7ecd7480d77770d6642c2221c6f578736a42a217a53c28d9848297e3d6fe1b61e00aee6e7e06b8a1740fe9b1492f558295bbd46b54fe3410858496e196e9b954e02a53d7e300" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "d468b48cc6f0bcc2ab130da3808cfb5facfbe4a8b179080717ee2d5c29468f53", - "proof": "3869a812fa9222782b1e79a7b0d052ab422b84ced6793f4f5b1fb8364bfab863bea2a9608b9e08f2351d54b6c1faa14294c1273e9a112a4138f5520b4e798a47e2e540ad2ecdae06ac9ea0bb8a407dce8048be114d4634f5a4d243b4265f1f4c181811cc958d06eb01da2dee76cd536c0cc68e122f5ee2fd19ccba0c820b444e6ef8fda27c4f2cf646ad7d1d39d2551bdd931785dbfdac34d687f955fe96cb0844bf0b17bf934aaca12c9cbad1fe2ee3e5ba3f1591f7a40a9fcc10ea57e2fb024437f6f84a47cb6e8278ea1bdf8a362089ef2a6b9e57646461d1db40e67fda04024cad85f2991cc0795a77bd84e45dad28636fbc1a2c59baf3d29ab294665842fc0e5cda630c26d3e5b16738c3604e247851c6e164a83b0fa0c70bf88f4eff26602bb51f4d9c029390ee3ec78bdf9e62063c757df28c1ed7ef5d94f05d2918499ca984f252c0e574d0e48db4b4e2588bd5699a0672e73f11af472451bc7e9c0e4c7e0d6e5cb9b924adc15f785f04e9855799c58cfea490ab739686b09d34a07b7c110dcc23488d005071ce1986d3ce2fc064bce05d6c1f4f358c74705370060fb61c672706f168fb511cad5b6925a8c6b99afae91c31f8d8655080ef16222604fcbe2fc7c9b01ef50d835fd5b75dc3eaaf3ece4c7ef48805fee0cb92117e143e2e52cd0f64d848f55827a05020803241fe43be52cd2142854ed248c0acdcae09885ba212866447dadf9e49b9417e2fb12c3f470ac4c165662589c0f573c08666dccc5a1fd4dba970a844dd6c195e68d72089e76435f6627f8f3680f5b1197f52e01ca8d55de6be4dadfbc6390a1c90fec3c7c8e1a600fc7f06272b52a7dcbe1b994b89e236387b22d780e397e5702bed072ebabc901ea759be1308bccf13af03337b707f7386f2f81073e8bdc7703953a844005762fba8418579aba0f6252f0f" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 11 - }, - "commitment": "0675a8c4532ec8188903301f2b79de8db2db4275192f6c78d93fc282a2cadb59", - "proof": "f424da7c1f6f153da35c138adf27a776e786e14263b6d598a0b4ef5c00bc0666343e07f55dd6ea455d8ee40a68cdc73f9b23e8c190133e457de35ae8222f704fa45ff344330a0175beea7c9a762e0ea739d0333dc3a614ed5f0ea41a665ab120ea59e31d888f77d3e983781dd063499feb98f7579436853320d7d3ef75c70e760d4210febc0e30699781b4aa4687fce8eca44d70cf09501ce1a44f410056f80a7d6b82d95bb83fc470146ecaf7caf563327bf4302413f66cf00da7d2c41a75026c8a01aaa7ec381a848c45bc24e47fcc1311ce29d921092e828ae46b431deb03baf1b5f7e4e47a1b369ebaf347b48337f7f9e1b54945abf0ab25fe493f459d7d6446eb1c930577ec2b6e5c45da2a718c6248c67be37df2d3457e3cfba657737ba8e1ab79e3192c336f47333159da084aee1127926308fc4243e1d261eb38f71bd2f9ccde98e01c384537842d6b49737e8996f9c1db3510fce0db299b0ffc371df8a2c384087a3186ada06e168c9b2ce65417b62c673c39c4e16485e2d91ea04854798e62475c23029d498ed210fe8732d8f842d53dde839c8047574604c3f731506130d3bf171d8b759a3a5933845c7d0b849293fc03f28c578a01bf433aaa20fc0a643e5f1c0cf70a286affef9785c3edba5718f8dc9c06f69bfcf5403ef87b4e990d0f9e4be28e41c18868719d2887bacb9d3c81edde53d9a6d5cbcafd9d69204187625c1fc4d7e978e60700b6ab51697bb758c8bc9c8da82b8817d375390520b48b98d3ec3ebac14b245f8c2fc42679ea4bb20d43f85422c219a58d5ad6307891aa90ffda035199c5184002cd5f7387dceb8ef89a5787b0cd5aa989bb4a2e48040593e4a387e5ccd0936014b46ada863adc78762b6d0189e6f1d24b34b608fe674a36f943cb122bbfa31a5b8c109a9d40870af9c4656083ab5a263690f007" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "34bbce60ffa7558a6c828f01082095becd3e3c8bb519f2494cb5a9ce18c32846", - "excess_sig": { - "public_nonce": "de5de95c5886ac17bc0beb7ef2a32f014988bff06ef6a9a4205784d9cd40e40b", - "signature": "cab6bde07239a5398e7ccab54c46c8a00dff6591c89dc5f6b34facdf21341607" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "360dfcef69f0af24b9b1d2b5d7b0bc9987dfc44203be52ec22f2d3114d20d606", - "excess_sig": { - "public_nonce": "a2a080fa00438c963114217e2fd9f6213e3e673a7bed04ae3ff5d243e1f19312", - "signature": "3dd3d60f5b0b679c434a4c504c60f506d1738b9cc461c20267aed7ff657fed0a" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "942227c38615c164020119a90f0149817f0d74f718b48c729ceea93489921421", - "excess_sig": { - "public_nonce": "94a49355b443003a3b24564db5c70f65eec10604d56f22151742e74395830a63", - "signature": "79f04ecdc3d04ac3e67796149c5d1ac51383593f1fe97c559f5fa4e18abde408" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "ac17910447e2c177d4d203f34fd7fa3e26bacac55e91306b5100571bbf2a3c7b", - "excess_sig": { - "public_nonce": "184ebb631a775c7c16b82f2deb688cfc9b9ae110646096a15f45fc465fdc3f3b", - "signature": "31f1d0776f8e7de9bea7fb1147e377fce02c3a6c5abaafed42f91907e22fd605" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "b65657cd4f4fc9761fe91d00b7017cec1b97c484d21223020e4ae5d8e96d0d0d", - "excess_sig": { - "public_nonce": "c47048a917ca86a3f677244fc0fd92fc52cb8b02ab846e3e3f285ff855598b65", - "signature": "b347b38a4df9fba7b6032afe56bb131cd167d72b194380f69bba9a775db5dd07" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "4cc9913322d127e55daf21aac48739c8c894e74033d7ea170c4cb2fcc55a5424", - "excess_sig": { - "public_nonce": "8c527a3f62fb7a5685c110b18383f39083a90a91413c559ec42e06b731f58621", - "signature": "ebfd738bc93868339fd352523e7f11322c325e4e97cae05832f9a8392e15f004" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 11, - "prev_hash": "333e58b6d09d389d2c9c69b1211a06ce0f0eae9ca96e23059c4c6b700877f6d7", - "timestamp": "2000-01-01T01:12:01Z", - "output_mr": "984898efeec13314333e4fc1676848103080aa450211f3eace0bce0d88ae047a", - "range_proof_mr": "2f0e9da14b12094d13b1961b97a0f7c324a0414f1a69ae52a9ccc6a8ce466998", - "kernel_mr": "d7867972a46e6b464c4a355deeae3318ab36d04069b40dffd667fb92cdadd6e4", - "total_kernel_offset": "d9250dd6b9446c8f76d3c02cae9001fd6320edd8466edac901cd30b616985b05", - "pow": { - "work": 11 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "4ae5363513456e99e2cc9dd90813b94a6df23790262c290a4e878e42cee36a4c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "8a93de3da2089b25c698ed9ebb2ba64fe3a22e3fa1efbde6f968bde886295b28" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "cca7c18d51acc09d53be5da57ea36053ea85c843e25e25b48cca5027e5ce311d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "d468b48cc6f0bcc2ab130da3808cfb5facfbe4a8b179080717ee2d5c29468f53" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 6 - }, - "commitment": "a4822003e6a42970092bbd7f714b7b670c0b1039e403e2bd8d38b57d8ab2e546" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0419150dfa12d3272e9abfe281bd5ce59fbdc6fcb4f86534f0925a2f0936c832", - "proof": "8a7f20d0a0477818ebeffbcad60e7fd54516f39e6c8334b16c4d656695b4b43c56584dde8913352110dbc2bdfc4cd6db1783f6f30aa792142cf43cc900fd4326e89a458514d7656b08c0695b4133501958cc497db5179326af0b9b220cb6dd66f40bc93cde579f0c56561b6b87fc938b877199bea45c9a33ce05cc0d319bc90e6b4942336adb88fb0789a438eeb4cc9dbf61e262602fad0366a9a977ecb78d01966085f3c8f6b14ba4f4f794a01777f8f54b799379b146199cd659c1fa369401520e6217aa65beecff55afecbc23049820f51efc804f304f2940d682e8299c04e8fad879b982eb64a358526c1217471d8c19b8c8ee7712ea737e790d0994e81f4e1ddf15d0f8b50ea863ba45c5ab203d9205a3234f30a94312b354c7f3875f1dc8f01a0579ed8cda610f021dedc8546a6bf3006bfe275be3524f48dfeadd6f2bb6a7451a88dfc40293dd699b5c08ae25729882412ef01eff6a4b2259235cff4d20b7418abc714a952487de3217c71edabd47fc4c80c18b300bb19f3ef9957900407fd50b59b4846480485756ac3e2e36993a0bb42afb1de221e6eb59a468d172f09d142cae94db07ff88a3cba5c1a98e7b24847673eef5ace96fc690e9fe5e49d0e4ccfc89e3515db2ec4adcb4805cec3087008efd9653dd0bce3a4f1862ad23623583cbb3be5f178fc9c2a6438b516e1a9b13426c5ba22259d4ce17aad65f0c22fe9b0a75322b5024248ecf288b10d8bf9bde999f884cd8578b3c74e2a1990f6a72f752d19d27ced6024e79fdde63d9856acb117be45310ac8020524bde0a601ea6b7e7f0dd89ca01b28e0cdc238cf362b7350ab488a1c38a56f4aa9980ad262846abde89b3ec4062fd6bf0cc0b9ff39ae4b409444a66a7722669a3684f8401a57f85f56555fa46012bbe6b3e6b80368d1da1a87f4bd42bd8c622ee260df80d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "329998200f82cbe432b5d226194a3d855c03913cc03c87443985f0940558ed77", - "proof": "5e12a6a32160adde1c6a5d4e4bdc78abdff29fba0ddf99b365483e62c0cb7a1320ab11f688816592243ce29969bdb9759d56c0709b41cee03a418bb9b7389a180e590cf0843a0bfc7d1b055834852fc9644a5f2b42d89b3e81d82a12ad8ce946a41f1f9fc72975e5a7c7525b2d4b0d4d14979ad40f1c9a0f7b90999904af7b7178347197050cfa176d86249c32296433be37a3aa35bd236e827f15b4af7ab40a92b01159ceb74d9cd3ad5f0db6fd7225bec293322918f0bdc9a18712e8909f0d07ecf9cf4e51a8f978da2b99c15548f14b5f40682392dd2a3ba582289babfc0d1c82cc9a916866ceeceb7c8552038c04623aba42522156a1c9020360ce7b695ccc895c6a991c0480436164f8a0e6b34dfb1213cba2043025f65db7ace26eb417b61aa243276897a04dde329249b7a9ca349261bb47a4c571f4a963d56b06543c42b305951cfa6312a270645eaafc5a86d0b246259667ec390dc720c071c463555872d1d596fc8aecbb574d6ecda6a507eb712531d9cbb2ed185ed5196d766b188431333fc5447afe7ea07b33fbc2c8e9e40cdfbb3149081acc7a8b281aefeb2dbe04e8320fb24dd341388fdf90e68366aca5f608673c29249b078cf04a7cd15228467b658bee980da9ec9ae27b68bda77f19a90d45c12d589aa4303a079c187276de50414cbf1d08b74b2aeabd1e11409f24d950dba381a7df45a2b1abfad11e8493b257b9878d1603bc4c374c55a539db877801339bee30f74ae2eff209d24664825a323069c8de7e7233b8ad3ed17db0c0485ae0b70242bb2c18afcb06ca080855dffeddc45b7684392bd56a4f92203753d0c591fe92588f549e9272ec1406c26f93a1eec592fe454b3a1dde48f5779badd74146d8c17727ced29a30304805764479395bfe4650359d368f325a7d2024c6c1dde13fe422fdb3975f19cee208" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "4077749bb79c431b96cc0431fb37e409568062c453323f70b8307c0214bc5114", - "proof": "b89209bd88fef374f537d3ed23a60099ce0e8e912ccf6605eb72b2ceee93a17820e38f1fef9951319d9e6b0958d7d4701e78b804a6a4ea1e70ba6e26f08fb6484eaee797be0d824e5e7fe975208a3eebd14a65a6e78d4ee3e2aa8ce64e962c7bd2aaf1291f2552b1e5dfa704ceefd35080c6b1367307750018227d92418d1c54a34c8fa4a46d551171a627ce0c265e2965628c268a8816237d22aa4eb0db2c07adf1c730b2b1db1b9c08a75661ce184227850510c259b766b40ff06e443ee9072b3942b1f5e8b90873baa069b688a4711b334c41e3b5384a205bad32724e760fe01b974a0e39fec12fcd68e8786353fc28a829aaad0c286fa94d13b6dedab1279a6d90499b129a3e6fc7eb859a1b7d937b140b41aa92fde2fd59fec532699b1728abaf75e73ee781cdd01dabae1621924e27b0af37be3a2208074c7ec18a5561b2a48d669ffca55097c208106ba28eaa6a2ea9b2f4d6cff1f2f77fafefd5f739dee6c49e3e965fcde07e9181f5d8393d1770489a34ceab6c72c036718c63f56136cb34954319343692dcba6a20618b43d0cf78101d3725e1be096e6b2dd0e033ecadece2127eb070033c463c15af6e504846d0d123f5d4267d3fd78d23e55f2ef41602964ce255111c7c7f37c7a77073cc298beed20a6e9462fb56fc42f7805914bfe3c44a29235a7f01c92f202ccf7ccb02d9b3e55dfba3c6d38c096457414110a19818a817e051938cfba04054a487c03a3f7e2804e336598b6ddc15ed2305bc8338e0c23d6ca5f2a298078b921732b5a9a4d66b95212c37dc416af11442724490c8b4111cea4433e239451fbe0ff202cfbee310fad93e1eae492ad155cd11bc6567f1ba7d4f9c01f3bde393617aaff76f96db8ce33df503954b52f41dd70cb690a535ff025aca12173ee3507a896dc23990c9340ddbdbf6062b04184e1b09" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "96ab1eec731d5d9e0259c03974fe8f8e23a5a1d5a0e44e9d57ae7239de756868", - "proof": "ce9b04b5d19dd709f95b4153be57bca946890835403e672f58e9c87a941e5438fa1adced06ef7b9ad1524b947a67142b696e4332f34e022d35d0eaa1929e175ae46805ed51239328816b2ca94f673b24d6245a31e088b20acaa62804fd9237412041a609e3ac12164e6e600a3f7612d4afa2f3b4b196e2b3be5964c02128791f79880d2a592caa26389724bf0cfde399e0e32b488dcaf39f8bdc374c551c220f0a11202e1cd074f262dcbf2e575cc4592f33510628b9f6d88c9582b6143aec06f02c07430647a205df3e5b7f1ac2adad275c116bb9e5e49f22555e8e0293dd055c0578d24869d1593391a17c42c2334b03d9e495cc3123287288df77436cf249e24fd3dd526e74b1113dbd79b0d279ffb80b7f8c0b60335182452634c094c70cbcd439562b8fe9a4d86088172faa7f583d5603c3153e7b7d71497bfe7238de5e9cb082bfc8d194a4ab79c5278b5a7f61db41e8959eaadb2cb851bb93731670425a4eb6d18d5442578236b5aea623d8cae00e5c78526f397b19814c977ac564215e6ae39602bd9d912d6b97bee4cdf247071238495ffe900295827f573740b328e693f9cb422148833768f5c4f9e0873dd5f6f3c5946d9d9bbe3b55faf1c7aa44e2b3a1f7ac74be80e6885112aa7210add30f50c1450efc018d30b6c61e07a422ea1214ba5159a33a405a5c0483cb2212f11098e2739b390bb919923a82577c2fac973798940f21b21ef00e429f1712a7d3fd4c19b1f808bac99f0c05131a2704c0e54fa84b75a33e13b5792166f75862a6c4f7818cab1b942894e73f63430408a40c92bef122b136c8429336b01bf08112c68d24160ed7c9f09fd923e7813662aa01714ae1f1561166c3e6b25ee3b6e99d57e88202f421990bf0255cd312ab0024a9a7e3b97ce9825f24d33b1a5a476a54cc96cfc000a22fdcf88fd060c6c00b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "98f466e0a79c051fa5e89010ef229780cd36eb652ab57df590ed4439f2e06b7d", - "proof": "789f5a0a7284aa1ecf8049134fb669f865b60b6c71341f0a93205a1aebc9ad7438b41b770510ca531787ff64dff258f85a55797e2de2abb3108ba2bc2568a11c643a22464a1fb0f7306301b484fbb20efb652591d959ff7c019162b8eeca273c8c4901dfdf802ef1f734253fa53dd367b0bf7b9e40ba786872eee8189fe56c5b667e62e67cffab053595f2b4e786ad328fdd993cb4f5bdd6dc6c54e7dde6a00f1657c71bdd4fdfeec989f44a6b46f6191b2e8e5e8db0bcd68d4b6904fd9359096cb64fa64821854f14f62057b91fe80d9b3093cdf8fdb968c05aaf8ea1bf5f0bc6dc06a08f2e318a474ffe7c034d0f002cf3efb877c66868f2a4e4aed1ec21170ee08af6e9bc9b7b5e6a8c14d953ed4e644ed87211aaf244cfd357d4f2e54d68b811c21644233d8d0f0ad5a711f30377748356c9a8c7fe4ce41ca8f184c8bc505085637f2ddff81e4d0c3952a3c90ec214f0b1c6929b7a4acb36f1ab39c10c58eca0d21156178dd67886d8ac4c32d5086c6a6d5f90de697fec14c144c730a27558815a304f9dfead40a2cf5ae20aed7bfb5b6578e53c54ba70590c96ec461d1328f1ff6237e28bf7212408f9fb3a4c8966915880a27e91a388affafeb1f429295423ba1e8ba31b63cb9f26091822ed0893381f8cce78e811b093c599317e682cf0da7444815a775bd689e9a569d389f76c72be8d672ba11de43fc3bcc2137a0d4252aa0d1bf10d13cad0958babe37ffb11b95a9373ae4fe9159fd255b5debe6354576d2d05aaa857185d1ddffbfcd72ceb60ffb1d8bcea9012c1222e11945604f8f28d09d7b40b7a336d45956da759ec13170a54a5311f2c6f474701ca05dc0f965a0fa599d341a83a87cdee82253a036512ee7223d3aba20c39db7bbb56e000cf9f6a8860050def852503c34c0904abcdf6270bfd3a19a8e2ab2d9d933e150a" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "9ad1319fe72852fb3dc4020c2919022a3cf515bd8b8fae51d593695c72cb2661", - "proof": "b6e80329f645becd05fc680269e47bac980b21fd5243a2f341b097bc9f563c5c08fe1fe547926b39c50869798206349316985254a3b3427861965c8d3e0ec1533a4a2a19451cfe68d22843663360dfb33a79b615e2fd43f76b6a5a9ddfc8586d3e55754f3675c0a4990c3c6f4ddd5c9f8102d2265a07f76c43822075150a051de099ca06c7aca8822fee5a57b69fc31c0ae8193df91eda813be6e656ee661c053afc9a4ac6fa30438841abcd335db86be9c304436c74c79dd97ac77b43596f0b94043fdc6b45ee44e155415329e9be46c9bade1ccb4b5d42d99b775a9aeeb4010c023fb4249e5beed476f282fa10ebf0d2354ff53c97f4d1d5c67d59e3c6d55c8aea0d0d0a30b74efbf4405addc80fcde2cdc41a944648c1c863ff1b68c3157032d42be8923077561f70e6359354cd9841abe22056bb16da6dad5e0ff5eb5753f8f5b032b5a82a3c408064e2c3f3c2dc224f9cf1cc13770d2ad0972304ea32751cb394ba65cb6f0a8a67bd4078fd4b5f549cc0dd7f0f1b415ffc112c2e4ef0651610d569f2963ad43203a07ab6b3a690791a9bd93d8ddf45fee7f80fe40a2b288a23ffda47043bb96ea8bacb034beeb9b61495741de45820fc03c6051afcd4231a4b5f7d3e73c020528c0d787c34adc4c5e5379652d31d08cb8df64a754000361ad599aa179001225106977a4938d78f9a3c3e4b5bc82d7f2b2e0aa6754def42da34b6cda827ea616f9ba73649130e470f61524485b028823ac9770b65f2f1345a25a4d86cd12006861e1c67cbdb35e8aeb0e7ae626eaf4134b582bc47349a524ac609b7f0edd04747a1140a513f730a31fb004a6cccd74fdf1fb34236b1f8311fcb7a0864855e25d5b30003905ec1dd4876b2a2fb44f2db4f6b54edde4cc90c90045790428f5839abafe3649ee310854376ea7ef1161942bb813e1b60356d06" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "a6eaf372fdc47d5d3d9335f438e02d88539e9e3a235c6dec615f3d3668a44662", - "proof": "b8250881f3d7cd55a7859faefd81300b32a6f3944c17960c6b406a5df1201076f4315edb902c9aceef7cb58195b66bfe1025d224c3d8adf1f46c126069009d3e00b2b50e9a3ef33efb4bc9b7e1077e05a08ac153b57ebe9fad08c6ec56688f43d62aad1222735b3420d53d2ec4f0697bf8cb32d4b71af0be2331338c17e85558daac0cf43092cec0b1f579025bdf40db97a2802b153cbd89127dc71a7430ec0b454b63ced2a5583b4727410fd1e4a4549bf5c28c4fcd54543ab25a0cad22820e2b4e262ad02d48b5d39995da97b55fc306721109907c3541a0f7cda9de21560aec96b3abc16d659d1222b337829660792986bd2bac1f753c6c1662f5a0781f4cdadaf90ad0dbaedf34f4c8b8ce9c5309f618cb29b6618a179631522a14807822dcdffda69335c34f07a99d2632e3d502b6443a3a33046e8699867e3a8ea86f18d0225ff4bb35ce4201ba95e2816ef240d9a79c208f8bda6590e0185aa5584a1a3e0e31d8c95ca6adf162eb47ec9f715b8b47743b92f4686064dfec605d9c8b2d02206d6fdf9f69710f0949e32fb47f4d173d39fd5c862ee27e6ecce1cc799529004e943591c5a62565bca9d86c118e03288e0d6f573de1a00c4bab605d9ed740ac9b1292b4f8530c63b76cb8aeb23c083e2bb0edf2f8e602e9ef0c58c202ea2fd44be52fe6aecb593c580fb8319efb08c25e3d15223541036e6cad8692de72511aedb61fda92330cdb3578f81a0227c23337f297762141ec8028fe0ec96941406e6529a2ea29e44f048ae1c0c03c5d651f4d17f3d10f65dd154cc139541ea2149eae99a4dd02d52bbc7873e42cdd9877626d9ffeac27924ab3fc5c8efc4f22638bae27390f1d3e1f93f34ed61a4d818dca9aab1eaa7873fb9abc7c8caca8e80204b1cdbc5325d6740ca1d2676205df1312e134e10d3bc6e2213f03201f9c4805" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "d28ab7e106e6709d734a8c4806ff8a1efeb041c606964a5e42bb6f2a394a673d", - "proof": "fa544b51333203e714b67b126615a57fc423799b5a24c3de2e35957859e8783e5c0609dd354fbe46872113323ffda81889480f1e25df7639beac86239f34fc13c64a6e798326fca027bdc7cbd2cf3ca2b932c04eb7b3e4b0fc82d6b7c8739b17d86a8588d9f4aa8caa2003efe678140f77ae0b98cfbf91163ea7bf251385583dfe1b0a8c79c98c42d70b4f54d7e313ff64cfabdcbbcd7337c1cef70708113d06b3e4c6a422b64f5eaaf6b375a9a59d161ff722fef25e7298504f0237859c3904f2d38485843e062f0b9400c1451b3311f2fa59f1c93dc0d6e71cc8e810a24b03209c0e2ada886f3e33b08c52706e32f36d23c8a32659747247235b95104784095640262d792124f004a950ec8b354ab067df9fc4c6840be318a0d5073e594c0156a52f444e1c6ae31a3a1d19afcf7cb8c075c5fdb2ef3719ac192bfb6fda3c1d72ed076f91932b4802766a78bf1c0181d3b6fd4e61b5a8b58d914915e8064d32b47b3142c5ef05f6aeab8225a43dde3c5f8edc29e37eddfff010365d194060464edacdc3f55073727c742013ff7b5dca74722d29cb62c3eb43bdcf5ff073d80f9e08dd3015a60aa0f82fa85b83ac9c430012570f949d50f7bd016f7a964dc5416a7ae2ac3875cfe1ab253a2e10eaf5db3dbb390a169c4bfff073c25e0789c42cc2ae5e24493d03dd8a3a81f8f028e8dab9e623cc11e463a3741bd3f71662ab13ce91463baefca9c44d7885ac1cda2e578e4dc03da3e025086ba777b61c5aaa2f5cb5d87f9afd8a33ff13e694f7f1ad2bc151207f17e8beb6c8164ce6357a6a0608a9fc6a69a09f2ea866ccbbf544a7823440dc3391071742399f9faeec73533e2e71255d2f3d3867bf6c6ffa38be3f723491d09fee1f1443ef1f57bc79e94d0d6b9ddbc3ec2ba710be08b22caca4433917636ebcbb04789ccc346acc5da37709" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "e821ce0f0646ba6433181f5b6650f7f3e93ec5b24154fa0c06abb5e05e21fd28", - "proof": "9e6ac4ee9ab226b37d81e190c6fd5a8d77c6592f8e839ac3a11b3dd5fdd35913a212638eeece71088dd0ca284a09c775f60b18c387a21288db114647ee6570574241eb4bc36754cf44ff2309ebe79377e6daf82284865c3bad91c5729a1b4d445829ea5a3fd0fc13e7f931787a8ba660ec75d4f7879efa715e3e67e7e0e08f0453248b801f781b8848cafbe5362f517a915d6afb38af8c3bf59a1ce356b4600a236259933e37d4fd0dac2d97ce797a93dc9bdef9544dd814b8e96b5fa2409a01446b7db98bb469eb9df60e99104e6e8b17e399ba041d8083c83ebfee3e17d001941515a06ffcf3225ace106f431b75013b37388b80986097bfec0de345cd4b23e481cdf65450966a42090709f188788a4864e2791ccc3103accf10a34243610fd821e13e1f47d99a6d397f4a11f9df56ad85f53d3a0ccafeb33f4c21b99a201872e7e7a4dafc849ecf372c20f806feee61984b8476028780da0f728a54d36d677ab6f4a2639e9b6eceac4c43a83dfdb3248837f72eda08650e5f634ba197953e167addb8bb128452c03b33b9fc6604fae38c8851b7c756c9fa77fc5b297b0e43fae9d45ec8500967302c8e39047394b8385fb5dbf6743c19c491398dab9cf449c4a47f003d5a4d509892e0106609f141a4f25570c0c67e5b00073ef5f85b6936d837ce90e4d62521c1898833fe2807d615377545ac559eeda5f030287007844edee73ef8ca8a3f91efb10a1fd6f8011f44fd8927f213785c907b33b50687242e44ca745f1b5101191c64dd741c6f9664bbcfdb57d72d1a66f006f291704b3c5eec86a44e9ab4adf64b7f5e74d78c82a1c9eb58222a342be0a73de19fc43bce458a803b27cc3e4da706f8ea23b6ce80e0c4c8b7ee20afb8c8f4ab4193b98aff0f48bcd6d64e8eb1864bcb79a82ac744eb628137248a743d95d0b84b622c98db0f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "f29a321e90a53353b92699c9140c957cc6b6885c088ccac59787eaf6860c5552", - "proof": "4ac573e8336efb2216f9cf853fe527b8bbae4381048a9e12623dd78db3805f079c204cfc4d11661f844153fb16a52cf46d5a4f2bc7381408c1e447be70961373248072d05f5cd853a651c8a19dbc94f76425e989737b3b782fe556ffe22f3d076a2e2b883f0bba40830b387804d3ac3334314d390701e2534173c27793153f2bb283e3a407c1a70ad918f8a96c8a87fae14b0c32afd38a3402bbe203223ead056f301128ab6ec0ebf403cddc7674b447d11de775a1f8c8ee45647e4590d20203f06342a7839f96169f669c63e2bdbde4ccfc3b013fb04762c8da43dad6f0d003b0599161b3e307e5b03192062e3447016ad522c7e0ce106dd7c568445688365d123779d2e3d1d1c550a5dd7cd04ac6f1b7cd1fe240092e52377382dd769d2a0fbcd8356f775f49d21fdbc9f6777e55b93585164138d182eeef80822c0bb2a87eaa04d568a773d3ba064e7be222c5f28bd202f5b47ba3ade8fb7600ebb125ef47a4b5a984e685fd68dd40b4f5b6b528d5572d11256e15b197d11e646aeeb5987f589a29a32330d48f79e9121811c094c95a864413a36afa3388126d1568441632ce94805736bf261628349dc12d70ac64a5e5b3f43846a2fd6209ab8a1c49dd306044d5d9be23a6bc3e03cfd530697ef4480f32f6a33bb132327c934d83ca4c2e96ac025a5037746d1c998bbf806a233c40ca78dcc6149ac41b25a83d7351192c68e8e70f810f67e15a207801ba949748d6619974b1fd714d91c44f50940fcf4d22eecc01a79c2ee9d2122c808290a83a00188075bc4c05de16994f7299f05b2706e30c4be285affe1017c23fa18c7667117ea1892febf039502871ce2a8a8571c663bd7f03cd9d625d3a5da9d07f41d1bef878409c1fe4d8dec278583d4801043339dcd93b46e2c80681d54dcc79a7ec8966f436b530c3054c7cef8220623903" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 12 - }, - "commitment": "1e44f23761abe164ee449bd913f95d42e61a55c151e283483ab6e18c3c60f17d", - "proof": "b819982c4ac8be5ec37269ec351dd1dbd53ae19839c922e2a9552c79b8a5971dee5393f47cbaf7b2ffc09599bcf617a57f31a83a3fe5cad4e942fabf95d80a6bf874195d742a7ba79e7541066f6912bf64a97b02d0f788064b6a2e64299e036e9a7154325326d178f2e3f22d95c4e3ae23894c8b3e5f17fbce9ba815322ce36256515a19b738a7b205cf336f18fde151300bd70303c7f2c3d1a2bc13ef77e805ca246d3f3794cc8e799aa15f52236b85286bee0c754fb6763d34779dcdea2306d4cb910ebfbd40a7ef9bd30069d6bf087e0706a3dd6ac13bbeeb52626d9b2c0c88ea335a755532f8adf747df416704d4f0a3ad658ed2b82c7e7f0a1c76b49c1f1ad841a5f40bfd57479588a99fc0aedd2c3708fe1244b2080f97828a7caa5879d0a1539f4b61a4f6ce8a5357b2853521981107972009f00e50cafce2c9d4577c6665f3d5b176cafef4ff749ea465cbb54f9af9eedf744d40d2a2061f956a5c1700d12344725f654fb98b813a3d6911e3f217855d53be40262e024214705f6f4028fd3fad54d5fc5d84c06cfc9aa7b37059b4a8aa2748b923bd975defb27df42a004c4b8a0e72b53597f3a342e10a37aa2dd767b28afa42f279895dd4408c4b71368f6f78c1f7a5038bf56faae614a5c7aa2f573ecb65ccef252ddd5a9e91ee7140821c90178c5c01372bb85260d5c690a0b0f195fecb6db9ea79d59265cd3a1c4ad647f98eef647670586217b6bda635c50795123dbc5f47154cad52cccdb4309cc64f657ea46036298cbb665c12e3db214d7902bce275b71af1b9fe40b0dc3bbeb041d008b20947b600fc470fec6c4e1f16b9ba3bc31727f73312a74870657577fe81013a55baca5df5b4c8ecd7bb5b9c7d43336e854c8962acb5a1c0c8680c332ac3d81a880132a807802e6b133dae69bfe62d9d012c175ff24439497e2206" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "4015827743e9a3db90aaea801f5fbe0ae8914ca5d72e56c2922fe59df9526879", - "excess_sig": { - "public_nonce": "bc50384eba03b6d17c833e6ca696e6440b52517122863e559323ca20ed943778", - "signature": "0506bb4e9e906d710f53cb594ea8bdd8a25b83c1e792472d9067b5889c987b02" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "7ae2a0fd58620b227a82444dfa34b3d70f1285befc1fbe9e38c0b94cd6dc5923", - "excess_sig": { - "public_nonce": "e2c507f1e0dc6fb6ae3c118efada1e5e33059959531696fc6d08665e90d4da78", - "signature": "136393c763fd414eda34e653d619cf6091689ce6d8f7a30b4114a13e1018f30d" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "98f4237a611fcab33c69af2718b7a6277309a307279f88b8c4d10ef3d923f67b", - "excess_sig": { - "public_nonce": "b83deb981fff89eb427eb963f916c7539b3e7e46e4a7961a2d2df415da32d546", - "signature": "5a4751c3d17471037498aa2c72fff9ed39ba54bb62e9d9c0c14d016254f5ea09" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "acb74932a17ca0a112273d056dfdfea1543b917d07c6be9bedee73e37449922e", - "excess_sig": { - "public_nonce": "406f7901b1443f884fb3cd8ee496c20b63c868a2d87060a40586fbcb4fe54f20", - "signature": "117ae33000ddda06d70edee08c543e510455c4f08173175951197cf8d71fca0e" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "ea9aca988b960a786d4af4613cbf664fa6cf22f27faba805821f258cca93266b", - "excess_sig": { - "public_nonce": "f29c2a240080bc831c4e28195f07d937c9fb1c4f8be81aea81e1bc425bbd3412", - "signature": "e99487558b6208ea0ee26f6555bf2154736a49d6956dce282e6f66a57c13d80b" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "08015466cc7ed27d4732b4158ed86344a90f7fbd7f603561d6eb9d36e9de1d32", - "excess_sig": { - "public_nonce": "a0979c70cb8ce74d0c669469889290d6fccae698e562af4783be905d6b5a6f28", - "signature": "427af18d3c9bdb1603ab847c75199f79713a19970f4e00734941eb2306da3b0e" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 12, - "prev_hash": "3c6a002c5146ea882939eb901e3c394d0ce752fb72c8533ab4e388991c1b1460", - "timestamp": "2000-01-01T01:13:01Z", - "output_mr": "dda9c7b20358a6613256242ae3c58681c3d21095eda13bf80afc318ab868fb99", - "range_proof_mr": "507c39df50db23e80f40befa37a941187bec96fa2d7af89ae6209cfc4f9e70b1", - "kernel_mr": "1fa9bbefc69950bcc878aabd8020624e77ef3639aa7297e721275a53a55602bc", - "total_kernel_offset": "73c0912dcf08da2adb11f725252cf56542963c58a4dba221ea29bacc684de30e", - "pow": { - "work": 12 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "329998200f82cbe432b5d226194a3d855c03913cc03c87443985f0940558ed77" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "96ab1eec731d5d9e0259c03974fe8f8e23a5a1d5a0e44e9d57ae7239de756868" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "9ad1319fe72852fb3dc4020c2919022a3cf515bd8b8fae51d593695c72cb2661" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "a6eaf372fdc47d5d3d9335f438e02d88539e9e3a235c6dec615f3d3668a44662" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "d28ab7e106e6709d734a8c4806ff8a1efeb041c606964a5e42bb6f2a394a673d" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0a862896b7f27f820e3e08ab61eb865645f066ca65d16d5cf4b3928266298729", - "proof": "984330e8aae26b0eabac75f32735cc131921b29ac2c0ae403c06c66f7d51d214cc57c3d82810feda868231e683140d5b7402bf2c981d69b3a6ff0fa7f79926517e24a2be9f26ebd72487e60d8e519679816273b54e208b19661c84c6b86c092ce48c965e787c09e024243963a0f4ada5644aed50e975f53b33f53d18b263a2633c47ea423e5ae40ede1607fc13f836f0e75074a5a0dc43127695760f6d550206014ec1d8440189478cb76995729d224592cfa8d81b2c826e6141cbf879164c0c944df492937a1824103d56e271ba8429a3f1ee17cd2762391af3bbc2e4f7950de43ac137cb3451d3bf8871a85d7bc2c2576253798a6d92129c7decc296ab38189062f87b48bf6329e3225c82ef3329ce4aa8494b0a63d78df04fb59b665fd241948bc7d864f39d7c7ce03c7866a2aafee8919b51b35c252929d5186a98a2ad5e7460760439d0d84ec97a7a76f231ff406ea9a3bc43569f6ea8a135856a8c19116089d3cb33c26a111dd268edcc8fd8bbc6037e3d5c41d5baa15c5b273ca5cc032c462799bffd86aca0724a4fceb45fddd79b46af260cbb198261ee9a19796b3928f2054a07162eba52b968e5438b05c7fd1c92357c945ea89f6225e1164ff6695621188db238684090193276681d19afdcc589fce9821549783637ea71155552ae6616f8d35bd2943fbc23db1a00d488b89ebbe368d8c064ea564dc47a15f82492806084016d65be4aef242cf2e32d92d562f0601b27b7ea06785cca443031762080ffd5d852e56d4f8d1925851c368fa30d3aae6f78c0aab7904543c1a66907d4bcd997b56fd19e5c2426054e4238f76fc559af701d1e2649bc3247f58e2c29d9a374693209be414cfc09ad568c5ea41f0cbb7fa8bcb34b886d2d6ab3f9f8064fe606c93a9d1ce964ee336d8d8bd361227cb1e183cdf091f879b33b5d660f03" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0a9798eea7971caf77fc9e6d61a23c60da8a2139d69f542a4eeaa0b6756e3404", - "proof": "7027f1c89f831961718cedab475e5c3dd2f8083464a2947ccbad320be8d84b5a98bccf0dc7464e683793a99ff9828cb11d9b4f8762413c8da51d3c5d9df2796b42cc30b5157b07aa1991b7a62a5ecb7c15eacaf8ebc90ef934cd21abc8bbdf6fa4e7316df0ad5145f4ba95578d3fc29a5fba936df4a8fb5731c132d9a10a210d79d6c9194f24ecd021f64d83034f53aefb4c4df000544eaebaba766072420e0fce7e8c25bcada9f5a8a1b879d4d22023bbf6be43dd87df5b4b3715766d5ef30cc163b50d32016f4ae648110e8e5db38e3dc40e2e35ca4dcde21e70097b744908089389ecf9be66ef6d097866f2fd5f171c6257079b1261fa3968fbcd5b3a6608fc4a7d91364011795bdb0e4c043f897fdf24c82ef8943f9a77e870116f45d138baeda018402cbe252381a351870c9baf239f9a64fc7de0f73fdb9cdc0f0124719ef181af2e70fb94a3af016b541e8b49adede7dc5909cadf2c54c10660cfea6684dc480ab164d0f9545b2f25b360561138baaab0be738f99354d876b9bfd706414a06a5c7989a2364b48d00bb096faaa402e025012933b510bcfb1936f651a2c0e62acea866ddcf66f0d547ef1441a153d69e025ea81cd063871f2404f8454629e7a6e499146330c11d134598be357a2e8771bb96dc446b8c6cc774ed2f50e72b670b49409ed48a8497f0e6d0d33fb2e0546fdbad0cba639f41fa60e23ad5e63d4efa92549e4ea95426935df4d1662ec9103ddec3f2c38b07b3590a8dcfd6c5982abcfb015948ded3089517c850b2bfa2becebdee845961fb11f9f58ec3f630d4c45470ebaf6432b08c475630e9106b1d3c181f5bb9117316973864ae7b50c3ed2281f6ec2fe04f066cda2ac29d9247bba3138b1b2107c1e2ddba9fca834a40fe7b72771caf0b598d8a86f773004c1d7fe90a34283033456af77e57bc4aedd08" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "12e648a1a9ea10887877c8a071d2f0e385ccb2c378b1d2d222a1f76dd42d7943", - "proof": "68b6b1d3364b9b13917ed1b22a1710e7d4dce8a5d7338e87513383b82e9a764d5826c8b337f7844dccebbc98e4301452433ecf068b2b22ccaa67d703f8c34762220a401ce88fa93f2d0b186c109372d8c26fa3f46971e879bd5129b00cf4c61d36531a6500c3278c9d8fd11d3a1e3937da71fdd93e746414a959ffe86974a42c1325d9c87288195277bbf3caa7dad2bdf9fe9adf732752b130d4334ea36058041a81841d98962391bf205466c40285566950e46d488605deaaf48fc17356ea02ab01c3484b5a6f3b672fe5f13a3c079c08e4f18bbf56edda4aad57b5b2b6710d60ed9cad3ed1b409e01f676fcbf0d1144d0230b5dd0f67166f04d7a2a5e6344640f007a97f52b7d510f2c0e05a20441b099b72a5950e96c7662dcc415d69a051e6281b32c323ff0326a77d17222c47997687feec5b9edf079c52726bb342ed7772f436bd187ec1ad8fd7ce1998b2fb45eb1d1528290dd505b34426886bd7c5438a6f1209cc0e3ac43ac874512fbecd66df5cbcccb3d4bbfa0318b693e836103a52e7bbb5b92e8998230e742a6401fa904a7b0e9aacd609d700c81d95c8091870b2ee9047481ebeb5c79b3044855590b74a48258d1b31cacdc8ebb45d2bb4a332ca671ad9273982e72c2a2b4fef45d3e32e4bb5dc2fc676f5f27be59278a5702b143eb12e011b97a442898e192821e7bd4230ca60543a2ae2d3eebadc636f6a112edd25bd5bc8bac9582712f95e02c9584736427d5bf3c81097404e0ea62ea803a415ba0bb4afcc040884e634abd9ddfb9c0c3e6c85e917d33e4082089f8cf00ce802cb8a8ca07bbee893fb327e52ba96196116ed9d7bdd6c3e9f2fbad5eaa652e3b4b315d80bddc0967c043bec18ea3f5381cd8c64a6dcfa8abfd0456ee90b001ea5b16d88f6a3c132e116cac6abf1b85b2d14ad7686233cff35fa61b450d90c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "16cc2c76c3e872205cbead3e52b7a6a91ea3a2593289753e7bdac955fb330760", - "proof": "64ebdb0cb93eabd633797f2840356b0ff9f00af21082d199510b12d8d728e74044658c311e760109bf23c692227520b713aa28c16f0f224adfc095b164c6c747a07ec21005c34bc2170ccd5a1259f38c247a8a7a62ee6a6f38e687fa0dd11a4da855a5840322ea7e0fd19bbd40ed36f75fed09e431460732e72cb6874b26c12e79f31818dff76ce24542c594bb4d47a43628ec15de77ddc8859a6c743b2af001180eda87461d45568ca5efbfd0df2dfd1839a791e3bcc138e66596f72b54250d1040e89cbe3498ec1365befd3d9223b8382e3712fe7d293d0172c3bff9fb7001e6342dbd88817d3537a58d03019d0dce57c9b98a3d8b0752b323e87c1edc622224ac41a94bbeadfd2cd2ddcb003fa519d0e020f1590a63980f85b3356381927a0899ebfae5903c50789c7b918c8ae916b54d156184f32f7fd5d47837e9e1ab340cfb426ac8b2e8a949493300d1f3901c824ac129b5b71dc95509455bb871b40b5082d2ad8420f5caf7cc2fe43f29ff281444691abc1b05e35beae3f5f0a8b0219ad3f1a33d0abde074fe32b46bbbabc7057d337003f0a48e5ad5e46661e9dc08fa714a356239c8d968b392f81bcbf4a897f87dc06c857293359ae82754cea86b2c4f6ceb5769203e6c41d9e020b6b31935ff6560a272f10b1ff79fd8c161a6126e8355aeae5d241ffb96466988ec3e5caa1cf4432ff17bc2889d29de4cde0639963d19f9aebb8d0d20ba1c16a52e59c5ad70c0210a32112d3a439546f4af1441c26ed0696d48467565b8c56889f4a5abccb71affc3bb9f97039983982ddfd43e1a3ea695a5324ef5d866acf7abae8dd06cb20aabf3c4ca00a0d983c039de9b17ecf41c540d0defbc4da3e306d648a93b1df916f4ffa0f87216edc8f6a5cd4c0eb08b757496a58389f21a5a89bddd21438a3163da1066df4341f9e1fb8c563602" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "16ff835f8300b04a44f59832d847e970dfc39e0a9a3f4290235cc2e56ed5d336", - "proof": "74633d7f9cccfee65aa0bb9c55ee673a03581e450671a75adc7ddc7413ed6016f8a84dec7c8dd7a8b399e6f048dd8ec15903f10f9f44192aa81f51c51aee6e5b3c5c92a6cf9b029943178e29cb0a71814e39c35669a31872b8188498e103ac022a701360150ee7a01d7579428cf15a15910176ea7672e938c2102bdba089f14e6c99ba6afc2a03b23ad5c53b9bd36de3c799bebd2fa6085c3890e6a7efba06015a9a115d8a6059926743641a0f42cde827e6a73c3627d75af507573b04c3d606274326e461344e40c4de11fddcd69387a56844b16f6302bf9a7c91088ceb790ce0a627281d9050517ab83959f0a7da50a6aba40f3d4529e8b4ad7d621346f95f9ab4210448bf1392b78097fba351f4a535cd53a6bccd0435bc5e46116d203e01e4a77fe7650c9e365a05f8503222c3cb2e890ab1b0e2c4244743e61b0d465b547e72bf6d40009b01655ae982440c05de902e6b65dfba2a17f68a51a5fabd6a07786ef332b3336a7dfd120f8707742d4346dc0a86d1a2a206e9f809a56066771432ac73bddce04d57775b3e56830d2a56d9ad3449e4a584ba726e051147547772b6500f91a58357fd7cc7ddde44eb099df77accd43f05ec8ee45aba3b3a404d60e64c32250d71a7e253a92e8635045d28158812b56b87c318ef1acffe6777656666121465791248c233f360083ad382ae6faad656cfbe37b3f9dd63d529e96d5668b63fd4fa93662d5c976fc13bf6e6eea1d259b50c3af4b5b6989105ebc7e44a30474958fc962d25ae823921a24aef94c78feb23b83dfdb193e92d1f1198a835d8198457b049ca6d419c2014d16fc4743f9d2caaaeb4e131a411efb7c4ab7c7828f1434b5e4a3dd754e9010a1b822bca905df15734186dbabc4129133e57610cfe46bd9760f320aa8fead6e1537c887b6d68c0208d286c0f2f2c67ffa3d04705" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "209346e8961376c3f5b6cfb5cc2973dd081cea4451d453277a0e7c465b066825", - "proof": "78833f282ac6797e02619d8d575b2910dc5ac5215c00c60a27a549a03ebb0956acdfe9fca4780fa18d4bf0f9c0f8577ab4b836fda8d6d228ed11d28e3de1221bcc349073278ba2a2f7d37d3f2705d415d4363d00b9782b2854fed782d23f715d743a26a57f605ef9ec671472f294fefc9603fee042fa31fbc4eda478e7ad3b2fbc7896671ebf7d5112277713111521eeb1a0d3405224967912b8fa54d537560ab18b46dea826e551c552bbc64e37233cc2079db3bfdbd9a1f4d2dfb66482dc00e2be32c1ca15f6503d33d16b347133d75946d51de8765d16163aa00072b5f70bf0a358e03e84c45e6d32a28551a2e8ed682b21bff5109e768ad945f1543a164142dfdd5b95701cdd577ffb9d438f4e0be861b040133ac61e3d6dbff4f022e7604c79ec952bf5909775937d98df0d9b6b6af86605ee93fd2a1e2a1c49c1daa825327f58ca771bde279bb08be9614c17d8d18d5db3c893e1c0181575624e34ff48a09ab35380e39246bdf649e48e26767e5686280261845da1d2f12b6cc31778371a843f3e8dafa62516ed8e9c8ee28bd5d0b3fb3ca889f86cd1738caa5eebdb568683a5458450bdbc26cd125a7b82c5fb1d338c36609297b0661764fbec6ed3723ed832f108548a213053fb4ee34b09102b64639d89ec9ccc8c823c07b2c3fa0bfa2b859659469481fd9ee26ea2f9f51d534d828cd4d2b5716c0f29a84a8cb1344a32446fa91758b4aeb2d606b771d050058ce706c9b75d9b071dbccaa4501a07ec203e89589d73b1f87f006c05f60a65442616fbae1ac87ba3f0812d686b0e2784752b0bd0ccb301f6c595cd3324b8c1869520406d973a0fd078df0c80b9a5250bea532f6d40a840d35be56b5634758da8c6812e8a7778a7ca6d3bb45674cc0d6e1e70a18d4b9545ea1c8cf3337688a6e1d50f82bb672cce873e269b30a62504" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "3048d4381ef70e816c3ae9a240173c9a8f059cb2023d4d0d25d461f8d0860f61", - "proof": "d631ec13f387405f690b65ceb633d2a25b67cf90f4e24486e600fe060f99fe6c5e84fb2894643e72c5444815d7dbe26996981cf66cb0748ececec18db127d93620ed91f9cc07dd3ae201df7abc1ea470f039e361ce41c5feb72f24493ee47e16487e7066f6023ecb0779032b4951d162c51e102b10f5c7a61d98de16b259192bee15faead8692dbe03f782060b8a17d27637eb2f30be3a6cfb38e13dbd3a79038a4baa965aad1ffcee2897304dba9c565bdf824c39d25f906f2f73b5deb6510df250f1ebbae5fc7c5920587342605f93ba324f02e4e130332d6253a546569e06d6021585ed6c16790c2bd684d7d6f84d1b37855a64b1ddd3f1e2df79e9a5641078e231c30e7e53ab1944201fec3dcf5154c0444e64ab44e9689f4de68ea5d91ec0d3a24013cfb514392b1d55d9d0f42b76e3a26d5b6538fdca358b23865c407ee2b1079c39b7344541b545f7831ccb9ccc0f714736b6d20ff0a0d00fb9d07415dc97dd753b387c9d3c7f0faa41879ec3f5d8558d292eeae6c2eb51f9972bc930b67bc8f18751953dbb34f30d8253d08a4478909238e5b89a77288524867b321eb0910851e2d75f3bbf80cf3d3f9f4ed544b2a1b371a3d4ccee445f81bd69be0226096b247e52889d74145eced54fbe904ec6457de6e6b4c21f0e4f8fdd8f256f2cb134c64244c1d8cd0b94daf5af57ed7865c052c64d20423808b207693c6a4fb674e3e1af80a3a9fb94d100d94965b41f75bde683d7b0c7a9bb04d9e0360c04cef55b30eec7340394f10962454857fbf83ad5f8cb72d35e539b86177a950744be4a43109b09379c191946cff270fed09d9f0b05d8e47fe641e3af84a6a20534b955c3bafc4a6341f048a1fd9953ef592dbf5c513a7be2744bedcdb3096dfe09a3001fab0dc79dac1207bf1cccae5093522d483975b932374d1653f5a5c60801" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "84a8be25d259aa3078449c31358504e39ff8eab1a0a566abd1672e9e9619ae35", - "proof": "9c20d6b70b4070cb03a6f18bb36e17dc613abd8aef7d909c603e1ef224c1fc361431bcca892ce1e2d20f30152347d6ee17ae1d6139ba9e60c52fba8f2e12ee65d8c58db781c82fbe44139e0ef4f6cf2a4eeea0d63b1b62215dd061fc7438995d6402a2e20c482862978d909378cd6d056b28eeb775b013859325fb53a666416ec10f246cf13408e27bf5fae87fa647f1aefa818ea6ae9c1d3e72eef8b9a8b605a41a2aab922d1eb7f8d11d36bad682f345b6493fe7f5ff989ccfca843fc850066438f77e97282d8a1e98a3c12d2f6d3021d6de2f37505eea72dfc5bb69924f0ed83c00cd860fc25a99e375f450672701168baf135014e4d268b0ec1c6b97e72daa64be6db632a7a5e687b296bbefe715b1cbb8695a5ef563ee19de5f8476e1080c23d6066223b31c20f0c9271de308e1df6731be3c2453ba45e7b7d5ad0c8851caa21b37e8984abaab6c5157d3cc9a0ddae03b4a687224620920b8613139c97430590ad1937f052254803817c12f7c2a46552122e744a15b63669d9ed90530495492cd3b816d3c88dbf409795d839f1a0a3f83c750672f78a02493f37e56157cd835a4186cc9e6bf0470f578314a8f245b4806f5d0b4918e6d9bc0f853f5cd0c6e0f4e6eb42f63877de7555fd9160f514d887f19adb99656fbad82b0ef2f83575cd0904c3e83295840004ece23544495c6c3034552165f2529e64c5d65503f6bdaa983397ff6781a0e34b8791a147e6312f91d788b59a61cf032c6cad754567716879f280b67fee111158c398b7d566e4c8d4be48c0dc5f58cf52d3059cf1430bea5d2388824a5ef84853ca40afeb398887b59bdc78665699c1c20d0b97ab8641e3bb69fead8d337bb23f17683db07c1bade5f879466e594e37e623d26b810048d7d7dba47d5bb21a5060a6bbaf8144a71406935a9d3a1188610930491cb0304" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "e43d9e6ba07a566a9dfa2f0cedeb91dd3ec34058b4b7f8808cac74f63c318d04", - "proof": "e67cdafe04fe2a38a64c9a640edda27daa26d09890b8cc856003befdec9109551e94cf512c56fe28883fc53b5394e0330acb1895471a6be46cff9bb8e1877f256a3485b01f3b06e1e0313bdf3565482dcc0472f30d02816d89d63ef9105b2d55a4102e2743d966aa54ac60cbbd89fe69b4f24081c27cc345e295f0dc9716b90ca8d5226d6b94a1f7bc3a28b89601fb3a19341e20690ae1027874783122839c0d22f81d34268302c8c5c942a35ec63f96cb473b02faaf8fa7220c6e6a99e41a0abdbdccee193ce7f99ef280f3d23053860dbe39f13d8f03b9112a6461aa59a70492a2dbdf7542669a984a20aa7e105443b888254857c29e145c921507f11c8d28408e17e4e8fddb51adf10222774d92a2081b3a52cf0e44758c4b026fa7c58416be98cdd59db4a49c5f4f71298b1b68e69fab2ba3e6f9b6f4217645333b4f833694533c13734719b17a106fa46b0b3d6b218a360595d20f0abd354715e6014b1dfc6ba45f61162edb29bb1ffe4305eee1b18e9f2fee0563192b74fb4b4b565f31c2b360ae31eb444c5a8595cc5857f15270bfa4895ca8df20225606de086bc55bda9d7bc033fea91b665e9a2746db67bca47a22b2c207b55f4d4c8088f7c1d168a0c106d84ef0ffa77a03270f646e28e57f8288089d10f34e1b555ff8a7014e050826336db526b7e0da29d797966731add268cebd00cd7f367020602675174f08304bd14e62a77c7dd149f1b5095278e0d6deb789792db2184ffe36ee239414706c8dd24965df8b1931a49351f64697a6d024d49f282ea5078d19111226d8a001b0239c86c4a7eef01ad12e9733cda6cd97be5ff1aefb58168c78f35ce039b41fb99c6faa73912d2bfa2f67b54bea2fdad87e099efbc91829b8d254971f1fe501f1b4632b3dda93931617d9cd0ac9e67d1bdd8e53805576b4b9f738eef10d600d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "fef2fef99d3ae40055bf4dbb654ea0d258262e6e71e7d972e054f7d58f2b120b", - "proof": "b82f18fbe34b1f978455f737b7626795218eaf2646f07099ecd04b96fadf4e41e4459e99071fc25cbde8039510106e918c5384d48de3f8c30f4d7d6c5de0dd244621d38c6b1c982640f1ddf999fdd548563804f10844500f4c77be7090e3e524c004aa1c71c26aaf900e686a8ac6e17f4ff46c307cd83aaf673b9aff0583876ffe6b7ec651b16e57ef344d4a612e4dd51b6b2eb4ac4263122e12b27873f1b7029031712b8943c382f27811712d8be71f691db9109f8332c107421603718cff02e6d40433b35493f63737e091b5460eef2df6ab0d22c284a9f50649d373c8ec08a6af3b6a8d95e3483d5f21551c1c215ba1f1dd8a611ca54a0df13172d0878330a2914b32b33724144b022c8c0d8901d98c9544a62e103f835fd609b8723a84399478d8aa2bd33dcbeddaec1085eca7eb5b186184eee76fd0ce5a6e0546d715372ce23d34ac59b568abc6122130721de16877ebe8ed944e5ebe9ccc7a9ff7602dfa960dbb2738611ed1fbc4f7b40c7c5db1fe17a9061ebea2bac0c525a305406dfa4eeefdc49c473ecb30d4230870c2c8d37ac37236a6e09231a070953303e74eb457448512dd6537f5072956b4126ebdd2ec925ba1339daff20936cd814f650d8cc6040012527199e7470cce51708bbbfd51cf171889daee284da8ae8bcb781282f7ff66416b8468811e0963221e5d8889ea8d07d081b807f6fdf02128f6e45f8247452f21e65decffca4bfd1ff379f89cbe300ca8ff5615ceefa0f4fa50646f1463fc93d8be94c31070eebed444177c5f25eb1ecd03f62a468b810d8414ae08a4b39494126bf2ad145d371518ef5633b4367db3c249354d290158d075aee9775c28db928cc8976c396c5b89981385945b0fce35611cd693a48c9f7a56121c08ce3a26bba64dfb8ace9730082cc3902cd65d65de18a96658c3544ced0b78cc08" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 13 - }, - "commitment": "303680219244c897d9e9cf5e8a0ec570fe8515e11d1b89ff7c4f3a3afcfad156", - "proof": "420b434b54d9c180fde9f7c718129e886c089a1981793171e1c8dd6d3b614c62dc542f32b5fdd3eac9480a7e790c7bbc7ce320062b1297b715b4aabad6d3bb0a4239bb9e2b1ee97616ece93138a06c04da637d2128a7b44eb2b7c3338c6d86492003c19965ca8ba22f46f548963c0eb6e776ccb044a89be2ca8cd6bba3bb6e5ba8cf44fa65ef6bcfa31d446521840710ba11f116267f21fc7a4e78c9da9f2506e3daee621cf9ca9f1068ce9df8afd4ea4939f77261c4c18c8b4f23bfbb78b209a52fdaf9ce54f94d977554a08b6f93d96d4f149087843b42ba3182332db31c02509d62edec9ed3add7fceaaa37ff279faa1cc66fa6aa3570e559f0b4e29abf25aeff14a9a13753d08f3b191f3afb17229b46261f4efad211afa7533a19581665486ba0083a91abc9765fbedefb7ebfdc45a7494ea8266d4bb0dbd6040f6b3c04e02dc3f04bfe62d674f8cb6ea319076919135cd0eccc1b541b77a4985c6dc229aee56ad22c1d6531e096ff8f60efd9bd8c37dc4e46e49077bb1ba4c43e9bfa7a38895df3e38894ac7f8b7aed93663bc3dad52e76a462cdf1c49ac5d362718b76d2c916d52d5ca6850f5f141bc51e2f18c00e4633a0eeae3e7ece18005015ab5b54207477d28b3a52b5cbdbacc47ec37ad2a1239dbd691d7c6c867dd079b0036cd0337e641a350ab95a4c4d393de542dc993409b4ff914fa7dc48cecb069dfc67cadf45b29a173a218951f90ac76a989680bb195873d38459f203007417bbeb4072b65fb759c1d1e1567ed74e581395ac8d5df6a8bd4ce62105961e2cad57675138acc3faf03183728b9ec3556ca9014ab3d07082603ef19e64b563b0f2d4b958474387fe6808e5ac2371d086d39493d0879da3eb60ea9095c9aba1e872480707138b8beea354ddf5c7dd629e5d9d091c845d473bcaa5f87b69a8212ecdcd100e" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "04a1e3ff0e6573798504eae5efe67fcedf6f7fed99adc343e3626ff86c1dfc47", - "excess_sig": { - "public_nonce": "9cf2a1572b89119e2caaea61791cf084b127e8b2007bcf1d17042e9d03fc0902", - "signature": "05e94a4f6de8381c837ef5e7db8d03c2f79935dd0e7d992cf4c65aab06865f01" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "249d81768bf69a3d670feaab3090f6b541080a99115d319dadf5b72eb953590c", - "excess_sig": { - "public_nonce": "20a597f663ca3e7a217e27635dd20a060955c4bafb910ddbe3e34e58df30256e", - "signature": "c78240229a3d62b22752bc00696c522540f0d067a1204fbee0c4dcd97ae1fd04" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "82f131a8b2f9d277e219a03425c9af8e0d97caef49c660b2a5faf7e9bb634d49", - "excess_sig": { - "public_nonce": "22e9b6472405369c3b8eb36492632fed7f51af9388ba2ecf07347aa001decc36", - "signature": "8211addd532e426474b9a9fbc04b84c5c334615328a9e706a6db6b17b2d4100e" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "92f58e13b2383f68d43a218c1256b58e75eacf187685fd095006921d30a34e75", - "excess_sig": { - "public_nonce": "1e5ab42a18f531dffa5d1a7bf441f68ad43d4c3485fc444c480349b70ea54143", - "signature": "74077b45fc3f945922c2fabb54d2379bd96acbabd78c7f48c3ecda9a87f29709" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "c2c54d1fe300dfefbe1755ca2d5118636028a7e7adf436ba83c71788facdd608", - "excess_sig": { - "public_nonce": "eaf0703069b1aa4035d2e6409c81b03ad4d81a43211c6b93f73cd3c79e322c73", - "signature": "5c8c49498bd3dc7b63f576635dc3eea8724aa74e3b98cf9231b04c68f3fafd0e" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "e609b813dd47df5bc8498fb328c85e6911b7a9d3c7da960c6e04ec2d595c221f", - "excess_sig": { - "public_nonce": "4c64a333c19d8ad71852dbcfd5652cfae20e37c9d26f63f9edd550b125bab209", - "signature": "afa84a779dec1ef9445a5ef28b91e69f16ebb84eb62a2ef565399241c79f840f" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 13, - "prev_hash": "30383c492d8b8cbf32b3b9c2b5fc67886de33360d80275ad9f5f308941c28a2a", - "timestamp": "2000-01-01T01:14:01Z", - "output_mr": "6d3acbee8dda81bd624dfa2ac271703fda0dba2b5198ea80c6dd1a56659bfd6d", - "range_proof_mr": "12171a7512bd0df639acbe9ca1d22224aee4a8d38281ac7cb3b3e93f4e0e1101", - "kernel_mr": "1faec4ba0722d7660e8a3ac2d0907a7eae8ac357eda1b73add9bb83193cb21b6", - "total_kernel_offset": "13b223a53b2148d2d6f8c2c97a9166e613de94b9c3f0186370b4dfcb1e72f90e", - "pow": { - "work": 13 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0a862896b7f27f820e3e08ab61eb865645f066ca65d16d5cf4b3928266298729" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "12e648a1a9ea10887877c8a071d2f0e385ccb2c378b1d2d222a1f76dd42d7943" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "16cc2c76c3e872205cbead3e52b7a6a91ea3a2593289753e7bdac955fb330760" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "fef2fef99d3ae40055bf4dbb654ea0d258262e6e71e7d972e054f7d58f2b120b" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 7 - }, - "commitment": "145e74b7e5d80ddb4a280a6abe7f295480ff093f1ca91c327ea05ec22402af66" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "12ad4db8f1ee9c21ab624d17afb33fccb63491ccebf993e296b1aee6859bff1f", - "proof": "322d4d27f62b0ed985fdc07d7576f8a7bd077fc37d3cc9c69f57e33d68eed7401cbf3e5ce81886b12781a344d6e3293afcd931d5e4920220b6f580239ec5e7643ebcb1e4eddd8b785c5542ae15120e8ddf1882d2abb0d5c6378baf737e83d026ce9859689e0f0b84dac560f950f6d6fd52ece11c91025c94a40066b31c18ad6acc43bdb6e0f46a153ecf25583a8ca99837e14a45c987c45e4a3fe8d5a2cda30eedb57772fbd9b9e1f51d86cdf6c9ee39f9b0eaf9a3989b2526a113a1409c1203850fa25546fc69cbe31ec1d3b8a7721dbcc0f05ed195bd6dff77029bfdda0709a083a85cc879dc552ea85ad22405e771714461e8fff910534e3597c5efff3878f6a58df32d020c53065c82c2cb100c29009597e984029484087f99391752b11c7a0c828b5bd03c5524969ce4a8a42fee9fcae7c0c335c34f78fb7b00b75b9863380babe3283fbf26750535f7e9e1ca3f860af2328f6865b8b82549d07105303a84eb86022ec22d5a2db18b29ddba478965fbb5683fa84018ac5b9e4c347b1a34160ea3e740a09f49a7770c11347553952222115e1c5e21dae912516dac3be02d4a985d15b62ba04cffe247d0cfe5551c4d2f463ddb640f33f93339839ea3026d76f3caa42d6f0865332af40df397e75cb7ead9b9a7c70dc4e455bb6cf2438e7fdca3a9aec3d57031c5ce13875590f301b439b988c62c5492662b975ff423ab3546de4bed6d717f5b0dbd0ef0a721ef2552c5b22a93c1e0187a586dfe53fc7731c27f7fbc20b08e5b03ddfde02541d0f3334c0bb950267f1c62ffdbf8b05af45246ae56ebf039117519ce7ccc078e7b5197dbc338ec60328fafa06793e5e8a56d59e67c38dfd7134868059c67bff75af85738315488005d4d7937589fe2855906d8496c70082524fb6a98becc461ace3ca547cedaa06d5681d7e6396d094c5a0c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "26f0822eaa9e43a5817fd1bfde541b6a1f49c0840a43183f72cb0be61288cd23", - "proof": "40d7da7c32b3ac2fbd717b5c308c49b64b48743c533cfcf131dc7704c591f13eb230b7ec7432671e139050009d5b87d57fe1b406b5039172decfd06ffe38ea6146ac317b10204abfcdd5f2427ab57cf7d857be3bea0a22676a8b781f8b8d507a70571aa30c5db17b60c5ae9654911b4688dc1673e99b925811ab75380321973483f869871f2a0e3975e85876d1321183bbf55a617c3dcb220c179f8efaacef07034142acc2bfd851f4d7d7d0e277f1d31ff716cc87c711b572a66114e59287026158ca4bbeba2d047792c63465a9adde0cc947ced110fd58195b58b32f7e490fe0f335f3c6692c2b4f2bf0cb91ac622d70957590f7f70d3cf9dd55fde24f255c4a10553ac93ee9f914c7cde54ce38466dfc8ff53e3f7b0888f45f74eed8ee75944f7fb6e254a0eb2e73d2c53b96f2d379e61e0bd7f0a3474cb228734cd524d1a28ecf8ea5a68567896f4b305083c047ad8a78da0d91aa0011eaaa0b9e37e3c77b8284005ac75fdde43323b05360eccdda854883388bedcd58e63431812d5937a3e1259aace5be01badafd2e411e5d93a1ece21a8abe0ccd4521a5ad64aa8e8372c7cf38b31c49f157efb2a7e2b4d2fd3fcd8c9b464358c30ba3347296f323731a02a7db7106e4ed901ba97ae06c4aee599063e0a76946603c3f0a038f5a2cc6f588f1f4b790be69891783fcdee5810d28f5a1852c1afd1fe394a315902dc6f18f661ef5ee135b1d3f52ce1f2f1de9ba23b36badbced2a2e8acd936eaf447f11e524ae35b5d47d8c6df856b336c1af8a06bb14ccd7deb899b3c10e00e936c4045342706480b75326e3adfcba6c1139c66790e19c5bb1f20d1a9a4418875299d765fa737c0be27c1c3cb85132c05dfab499d7db796109b401817f62029fae4130660cca44b0f00795fc79e374c0a89f6332dea95f772dff8fd7ae2ae91a0de440d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "58528ae562e6d94fe42c1caf7407ca251fff119ca873a97fb35bd8bd34c35002", - "proof": "00dc8c8dd26270f98ace961f7f20157503a057ea93e218cd7169053ddb515017028efd795aead97b04c5f8621259e8baa09760b54d2f333c23a0793b030c5b475a1073af55d9082a893af33ef4de771a7991fcb4819255ed590bccca4be0204f18554511a2474e55966fae759490b63f9f6182733b086bb78eaa6176d49b374e51f34358b8f1bd81957a278e0bb2467aff651397c6e8b23d7a4e79102fb50907c86639aea34dba42831365f7f6e8e8145be53f3a1a6c468f023b8f6ef1bf9901ae1813166fc3b893f99533779905eb6954d5c44b30306bbba8e4e1be6cde0004fc53bf52e192adac4f97ba9598814714fa94cfdb25ea8fae7da78783f320513f7cf4c3de8c75efac4cc512a851e8e7ed196c63e3d00bd402c17d7d3b7f64132dbc510abfde56b7b1042578d9a6db90b00c1da69c10209c256967fc9bb39c6e1bb438e0d6a15be8a8cfa38703810824f525cb6f71b1e13870d6860ed040b339649cf30b850a0bb8a4ccb98051332e00b691975ed3f97e5d75badea06c8d64cb751427afe6a0c3b5be653b7644a9e4cd34f3f2ba0e3c74cbff36144155be24d90116095be036bce646ef21934099cbfa67174a7048ce6279da9754aa1efa366104ae8615bf506bdc816c238de21d109d70d22f181f6f57d7feacfd56289f385a58a83844b6a558db9763045d6a58d86e4bd93c83355351647a2c861496367d7945c0bf8373183d97689e5be7cb1f30ed5b4d6e4f31db1f83e49649490549be0d36d8ad42caacdaf76923153890e899549cc4c292a599b44311a6d19f2edc417d1efe0df990b8fdf06fa910767b45dfabf7dd9225b4f60050500a1ce2848a910d02297c8e339e66f0891e1eaeb973943ed0d7fa0dddcb33dd25b767c7d7af38d202ff50643e2390bdf978af1956a758dc33360b25ab2df83acb9e6929768814f80a" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "845188c596b0ebce5b5044439c3d174598361c8aa0d87d24b4d17359ec969d48", - "proof": "26e2266646aa581e64ec9b1dea14bc78d5a59df90e821bba79c50614fcdd454d189ce9c862d1efad02b2b1dcf4b4a1f184e1d1a877f5b6bf20be8d928be7eb3a1437f5189e896a898a4524f669489f79f29e1acb97421cb7cb985e5becbd575d246735f6969841a5ea03071401b1a7552832866d00f9d01cd121a3b34b696c03b62de024f1ac740d1dd6c465fdc6e217e5e225692a66ada0aad7659a0877200c0879f07b0e00d7b9371ef1e487143de912e00877736fe997ba3ff1966aa7ea01255ffc94859b79776de2ea376ae33deefc5e0c3ba09231c5b55ba28cbdf4780c24634a92f1d7920d4266884ea64f9612aa218ddd57ffaf0fbc4dcaf7732189780ac8058de81550b21b9dc5728c8bf9bcf78f550c88b4c7172b31150a8f7cc65f1a0b89504cf7df188902cf4da050146e93228272591d31fc6862b122b2fe9e0d10d8349552c72752a5f0b77b3a268391f56d8acd6901b071f3b88a3d65c7d22954e2597fba71e1bebc346d2debef152514e8aa3ad539f3a2384c22bad8d84400744688e571b16e29be8b81853c572d96d0a4a39290839ff849ae0d9f66db00102a187c78b52d3958859aaa5b2fdb5701343d4beb7cca7d3682e2d3ee73a24341b4ee51b9171d2ae1eb51185f8fea8a88645a9fac086504b7cd03aecfa576747c3865ddf88e1ee10f45f6605f356d0a3859d25de4089c12245187030e11b5be35d41d2ed76a14ba6c46fb387d520733122041b8490f9c3ef2215a1bc86b10cd3366ac0b8f89f3661494943074b4cc11ac2daa1b248f3c377c4326fc296b99bb392cc1b79adfe9048891e1a60ead2b45f158dc65e36ab26643afa28ad2a5856b2008710ec28d2810f26de2fdcb1f1c614aa52c3e5ab50d9d7ee509295e84287b0da3a10a3cfe5a9d286d554c5dab721dff6cbf5c975a9a43b5ad8d036a0a5f6305" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "8e5c5e7993047ddd1de118aa006a9154964ed173146d5799d8b7b22fd459ba79", - "proof": "10dbf7bbab5d0880aee5a7ed977ddc2925aeda2a6d7ef5a46333458db81264528a77711afff27a7b14314d9938a66f7b68a6eb484456ef6a20a5b8ad93dd55638018f283239aafcef3bed22e2b71f966aed793d5d582ba51c1fab031d369155a3abd7ff2268a929664c85cf4f18ecd0285089d515dd168253242bda384a0eb0162b9155eff3a8e2c1d7398de6037cd4d066adb8450fda01fa4e89d1dd1b42106099175d7908a717424aa82fd5efd3b90b155c7293bf97c536cde45053807eb08a9e9ba76191d1c86fdbe288948e219442a542443c094a6393700cdebc1d19406ca0a72ba9921596adebc0e9de60c3c0b7c13b6391c254edbeec97838e96150666ca3d604846274abba965c751de5845a7f2aba4352de75f04e82e6880d87d3024ca4a5cc27a9a87fe802d5330d59c11d44b94121aba85bca3e98766434d18748847fe0e1b01e73f63dba7756ab2be59439fa77c74ca6feaa57209560c6762f21b2cca9ab1b3f4a959d5a7e6c6f04557d24ece4b4fa162e2d35e062d55c12f1251024f20b46e2aa3db553e32a5036b3dec38d9c4d7b8f1ae1c49399f143fafc65106e57ebf8dbb37d3a73c1e16bb8cbda523f1941b676b0f0f37fdef449de5d1e2ebe20fcc940616594f96d0a4f428ae77c7a4b5a15058193aa37a91e30038343785234ff1c80e9668624786ca724d7b6e4b84987db53efd32edcfde36f7492769c23e5100ab9c2f264a37099c93546e72f3b01d008e091e1a1187b723be1830c7ce3d97030a7daacc70a14ed547e1d31ef67867b9d39fccfde8d923fbb8d04583e4b40c870fbab0b1f5068bfac8bd387abfd8fa7a62217a1476e2979bb854956d0890b90032393814400591740dca721a99dc920e169a1da20fbc0bf8fca8407b9e4ff39a68064bfdacf27079e96983a4fa6c7421fdd43a00a030032bf7f3f0f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "90af391bb151b71e2a2716054f29e6a0b436360364c20dc44bb5a4ce7f8c2d5c", - "proof": "f8ac2a122837a32fe3436b2274e8d45e6863a4755d4c53a388eb03fc3fa76f699c636cfc65933bc13a7aece8e4f8346781587a99cef842741b35db49f53bda52d2c7c2a26abf2077b57b12af016cec533afca30a6c264f44f3b5089919c79a271cd0ee250bf7aac166ba66d430fe3281b0e43ba4eefd7fa0f4afefa7f109f73f7ea60f0bd7ddd35459b2e76fbd88043f7c787a1fa9437b15c7a22db71a38e708166ccac966721f7b89b7b3c41cb6a2804bc50a24b48f6280cdedde814cf7b609bc2b0fe9022b5109c5b82978b7b41fda51db3a470a239fe51601fa35bdd544068671e74a8fecef0fba3808488b6c22890c814672b66a2ca06b8b1259e812583502a5bc4e7a488db6b938d0957c32e5ecee016f06c7b7be6cac7ed1b71fc2ea136a62124d09116ac7014f20b5d8bfd3e105ddb07b24ad2256f245a9016f66fd40eac8197982ebde1df0978edcacc7c9ff709a8534de60ff2e2f90bb812e059a77028a6dc9cc04736747b283667008abe7b6b0451f12b3d65eee9c8e71525f902c82458b3b02c1ee4214d4ee503be8b34d65dcaefd51dd587345348019bdba240d883882ad7e710f405eb246ad0401e76d84ef85d6dec83742aec53cbb238dbf0ff0463dd99db5687bc97cf2b35455af74ffb7067029cc5969beba2ed3196a1404e02f2e6390bf085891f501909d7dd06b796701d5b13c0a431cfa7eb8f29b892a0c2e61ec1fb790f9654690397199423e7ae7c1058d2fceabb4424cfc40bf2c65d8de9ef43edfa57526d687d5e15573532fcbdc714fbc908868621e78ad945d40f636bbadccef9ac7d5bfebc9ffdbb1b2e9342e7ac1263d470052eddc6f9f626a5571cd50d6de0f25f9287fff86634edc5b226c7d194f17210fb5259e52013d03163129294783f876aac415fe8d391ae4288c9e1f91dca39bcdc6c7beb3d3e007" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "a456ecf3b718edefd05278f9c68dc17c91ed649538a732f257bef0d65f995a01", - "proof": "c83ce6c4a10845b6971e42275cb5d4b0bec310885632bd9652e99c392427346cbad861ca22c7475f1c7cb1310facefba5018832c2d85851eaeb6e0343ebcee14da047c94fa82914794d7343755f9bc57947cc85aab361a437e717ad4eaa5e942be420ce86c5ac6f4fc69d9d3b2c9063c5a0282a3e4ecd22089694721b1d6b5178694b0eae0d6e5d460fb20a2269d45913a1b75f35512431dde7e4eb435b9df0b47247fe03277474dd2227915022e5e338fd02eb194f345a1418a6d0f8aea0c0a69ca181e374c45656ef9989762b831145e391173a415c9a5e065d8640b0a68060217fd828f3647c1bfa660b3cfbe5806abd2f40511a78fed55fd0701b1e9c37c7cfbe122f61c2c40f7b85787da9e32be4681f9ba990f4024cb7836d34601291ebeff650cae1f5e23b75d9ce022fcd64c19cb2bbb28e9d408171b7bef6ea937705c77cf84e348af486db2daeb7f3f02eeaf8229b0563fc5769d7460c5854c44764ad747c8a1066acd1d47a26042284e416b4a463c93064036b1c5c1d64418640de6df08ee1222a7d3ff028cf0264d198f65650c62def007a3369e91d4df285a08f08ea68f42ad9746276ab85bf0c913ddd7f6a1aa56c66bdefb96a41d634b750d982129b5cbd7ee1c1fadea3afe908c2b8383695c90e612263fc51451bc7bf04686eaea2fe16c86736034ef73388bc30034ad29b7de06a22b09caafc505fc7f74dadbf7b353a40b227fee987bc8459a9ad1759722d16b346be3cd8b94ce142d12286a42de7008167ae3b98ce0354c433c9b314bd1cb542fbf14fadf053387453d62c65ed512b8895fb188eb14dcf037babebe8d76e087b3b1d6da5316843e36381195667e00929540ca0bb68c0d84ae0cd0dfd7052d1d64dc560d41be0f8a69065587c081bb76d10a7a2ef8ce573c1074f6003411356adbe730da0186243b1e03" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "b000abd87d78211387fa2e41b2d14038ada3f869e5839358a74e09e16bfa8f70", - "proof": "42c5aa030fd20b3e0bb1baa4610586c46a93731b418b6e7bce0b69905290040b92d5ac2e48d89d14bf01fe055f6affde1aae0e6f9c8aaa309d61255ad2144504d4660ed6c50134de22fa1fdc58f4fba0ee6b99728b07a058907fa14c02bb322ed6c32f4a6f95c5576b2b27c5e0cc2aa9e157a8ec59bfe5cadd32dbee5441e646457b0efbca0591a4aff7e94608bc825284a1ffa02f8fdb6c7065595de67842039c8b2ead186370e266e953fe593110549e5d6a678ce034a7919ced06b9e5a20ceb36e2c2930a33e6e664a4fa8922c4a7a1d6376e2af48546823a1d93f35d3e05e453ae6f49a412ce79e3cb85c090dbfd71fa2366bcf2fed933263e5da260920adc1b873f9a278e2b773add937cfaffe236fbc07d8319bf9f60a7825fb6e2656cc2267cd3026a2bb79f282f2ca838ac3883f85de5faf6ff281921b9a3e285583ffe77d8fef91cc8590df7c9ddc97064e78d35aeec62c91d2951afe0700391fd02ecf076b8cfa4b8e130c7233403cbf135d98a6c7bf7c901f0d9b065d451cc9b70261197ad44ae826062228e1c45dc4505a7b20c2ba8d58b9980b07fc393ee6303bea6ba7e81337e609964c08b6fff6bac2b9a02de9032be52e64c61cf3935380e12c47292d883d30fe02d596643b7c56503086864287747b88f9eb9770a5d3d1b681a185f305ff745a886e8427414810dae155d37d38bab072bdacb96c489456a9aa53ac5f154d5cf1799edb919dda918d4e1a2d9a15de6f908bbd415086c0848320fbd9a47feb0ca3a2f20949d2abae1fe4fcba51f2a61979f5113846660a5682c273d01f1f7b3c9bc6f7714023dfbdcb5e3db51ae708e1f401773840d03784957c39c02a7c60bd35a7b8a513585bc837884415c9a59795d198862f19089a80d77ecb214f6cc58afdc512f7b1278f8a2fd43c28a275adeb5e2956263dc12af0c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "c2b5a07541c11a7700030c79e2608fc2c10a4750c4f08938cf51d6cda8238944", - "proof": "10e043256c0e1a5beaa1fff6d50fdb4266ac96a643b7a782967635cded21465ff8d103a9d786ff15ceb0c83f190c6902ae646e1ebdeec6d0f47b53be062b5b7db6e37341904db504ff419b3880757ed4bdff4ef36da0b7384bd07b013324b40aa2de938079cacdbb2ea84bbae28de1aa19a77543cad5272989afc533e78af1556777f0fc399947592c6259ae64b565d418cda36ef6dcf410e25bb88464980709350fd2d2c86b1dc6f10f6f417e6c8116016bc507db8fd54dbc597ad4848c780f055cda2b13eab4efbe1d7cea675b5af177ed122549206cc154fbf6e4e537950c70a5c2fa02a9b73ac8aa99034abf65929d9cd2d7dea7b3ceaa1cf6ede2b8d96f3253a3aa2b961ac297c49b30b054d90a8f6e160caf7d9ba263d04ae3b5427219e23fcf164d0df6c6dbb796a33e6bf99aa260add3679139bf7f688ee7c9cabb78d235af6b84cb94b26644d66018d008da800528bc2c5a615591474bc6733937716c9fed88362303812b2378b11eee2e3fec373409203195708e970813cc95fd5f24fc6f6bc799d5c20375a2cc2ac6676063a56f8d0f475b7f7586c1eeaf838c002e6b4920e72587b5af309daa3d0202c4972c0ca61967eda61c0bac6b8104d00db0f30f85f3351ede39052d208b55f193dd11e5d4a807fb50ba7b330288b2966fde880233d4b652e900e26722974924157343078ade1491e58a47dff2a1deb75a18176b676134816631556b7ee4f1f4d71a5a13b8938308c60a214ca8fb6bce0620a94f2d9173e889c949d22f5381fea18f6a5418f198118da91b7b8598e7cc3c8260c1e60ce499524c82bf78e7c6d94f2453613868f53cf9e7e992271596b834f3470583e359c6a46c297f0af0d370d6c80a6065aae37705d1e9d0ca24c9d00a229837f3ea9d64a83766bf50091b4cffd4e982b4444137a5039b50f18d876800" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "d83c2995621a6c2f35b462983e72395fb18655d02bd82e4259996cb23323b129", - "proof": "ceb98940d2a4442111c590fb46c37ebc0ffcd5cb1cdf50e0900f46ce3c671267dadf2b907237d8ac38c19f573e936bbe9b8b531ce9884634a0cf7e021874aa73eed34d305bee2426051c83d7d854d3cb2d1ff2fcd1b99f763d4ab956eabc1063dca67748d50560cc9f0ec8dc7971bb617c595a8210de7e98029f99ed0cae3842b216286258388929a488fa5a2b91bb54657169821b8c8bbf53344c3a7cd378013fcaddf95ad544bf02b07569da1dabf7910776f6d1eeb131d1cb512fc3fcaa0df47efc5a21914a20d8c5ae00a0b06b24d2c47d3cd7d83254aa9989b690ba170416a50c4c2aefc397992f8fbb68fe2ebf9f06712a34d8b07cafc3c65451150d03ec7803e53c394ddaa1819010035efaebf25ad2a34955a2873f2fa049ca6b6471048f5de14e0dcd21769f58e50764bef341808167788b04b8b96431030333ac0ca04b0a9aebd2bf28084532444ec4d7345fdb8a855f52c61be2d4660859252928aaefe669179f666b7e51540d1c75f95fe8d7d08ff63c74caecdc23b8b8dbab3924cd86e78e99b3e40b42de79bc88b76428b850f1f9767f74aab9cc80e3abb42512b254736a5d0468337689f42641a089782e18de3217f0b858334d5df2e03b2ab6bbcc302f6638977d74596e6cfcd1ffa0c346d12fefcff1c15a3597e4877d3b38ef2445fa34c98b492940107430bbfa11a0573226ed16fe01f424c2a6f43d0bd4726af0295ffcc629c5783e22360fc0382d889ae3ad1b7819d177306ed737022ed3c99256b211af293ab8c596196a3d402158e2739f0d50f954fdac734d000a5e7c665645b1439b7e41537999e7a647170655c138a84cf0da2eae21e107ca42f06e9ff92b349f51f9853730185c7e95bd89e51214342dfed662b3c2a6764b0a1896147f4cea84ebc426e900987deebb2120979cd4316e90633722c4d2f6990b" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 14 - }, - "commitment": "405bf2c2f984f24bfac6b242f9b51560b1605c2a110ec907799a3882aa670e39", - "proof": "b26ff0694ce0f9c1810d078bafc67aa21494cf61f067333ee00bb785c27e2f19407112e976d95584d2e09c64a0e10abeae8baa35dcfcf882e3d124232c103669627a3a4d42276f300c983bd52dfe78ad3f04ac71fc067f6347d12f711f08bd0ad26df64690405f41704328e7a8299663c6805a222d0a6098aefe7d507f6db84f6fa7b022533103de6154ce9d7b8b9fe30b505542e2e4fbaec1ee5771499db802126993e508d6377e38f027f6789d43f8adc587cd9ed2c1e67eeae06a86ab8c0fa771dde26c6e66e64bda1eb8dbc12252360863474b84b23292e0b5179be25c0ace778f20b2a416c817a2b0886f6843560eceb22fc75dcb1ff32a96b15160bb1388bca60c6ba990e4e7192ac86b46d66d82c29a13482de1936589d65c31101a46600927f4b6560715d7864f2b638288e92e7ff20e74f475d1026a093c242b2d75825db0761ef495ffeb540fffb79a18fb55c14e6373598bfcd25e3c80fbfde11e5e24cebc9b5247d2d9522e05ce9dbbc30fb8fa4d988828f8db7b0e185a27ee6976d416e86623f27bebcffaad4f98867059b8c876eaf3bf563973bb4920af9d77b29fe62475a529e890edba27a0697283a1abcffac312f1e5dc4d4e594e2c182ee08e0eb53c28558688e0dd8bff8509b113df01628cb49529b47f3c2cb470a8508ea5e6390793dddfb8f5649d4571a017d872644b3be510855aaab5f58a21d710e202b4ca8f71b75c4829a7bd180cedce1e4c60033f99e84dd3d4a04bdf6fb242283dfc92bc0f51c660f94970a74780a048a1d5327ae755b287731653e120c526041abee79ddce478c0fe93a1de1639cf96334c53a9c2da2ab07a303d7c096473d96e0196c312e334b71cfb6547bd3bf70807d0f9b769439dec0d14b0754ab504e2e1a50d4f5087f269678199e051fd78685d4602ef3a57e8a8ce43d98689f305" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "10a62f6e2c9e1802cdc80557c5e7ee2f6aa2369a121715fa17cec2798d197f50", - "excess_sig": { - "public_nonce": "54bbaee97b9f55ec23983a3316772468595b268d177248f59563e2eca4a08349", - "signature": "8293db0c43d72654a55ac9b4a4dc2f8a2c7d722d91caeacf60f2ad1a059eb609" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "1404ddbf161abaa6439113e10399a1e8c86a59b67df25d26a5ddd53581e0ca52", - "excess_sig": { - "public_nonce": "3034d5a3d0e617b693e270eb4bceb1d7d9091eac4c27809fb4275968b0d8c740", - "signature": "3116b51607ec65afe5ce361d6f9523ef6e08dca8c29d114c6cf00e087d1f2c06" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "826c1a48acedbf07dde15e67fc5d89a6c24a251bd08f734231e8c92d449cbe2e", - "excess_sig": { - "public_nonce": "b212829d3bf6c2a1a6fa48b0a3a21c835b751857ed72dea3e1e72cc1ecd0b03a", - "signature": "fad4c1cb1c2563beeb918dbb12db960bfc2ea89040de99ab5921ec664569640d" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "8c6141e86f9a2ca1d0f019edbc98aee99acb927ec374aaa3fd10995b0b86b667", - "excess_sig": { - "public_nonce": "b6ca0bfa2fc6eeedbe57d0582095497b42587d2bf0368625e03a6949b99f4941", - "signature": "3236fd13e31483e7465067779e283dc7bc3a5ce4b63c6cda7ee585620764530e" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "9e68a01f3d36c58a2f7e2e9d9bd1531d3aa978f011b5f697f111c08ce92eb203", - "excess_sig": { - "public_nonce": "e0bd4d2206889f537633ef3e91e377dd7fc4b9ed921a351247857ebdacd05d69", - "signature": "e7e3a1f6e825bab3e671475104af918f833c5701a3d702ee522454c530777209" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "ba3d3155f2a30b97a6eb970aa2f61332e204a08be302eae3a0ee8edf274e7e30", - "excess_sig": { - "public_nonce": "005da323f50f80dc1c2fcfbd5729163c52fa40f878e5a66b2f67ccfea5bc5a7a", - "signature": "5ea9361587f853a3ec4bb59e5af92ea71b2f2a8841964e0baf03777262848d05" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 14, - "prev_hash": "ba66a8079cf2f6f537ce09a6801b3f31074ddf0bdd0d7fe9559118a39a270611", - "timestamp": "2000-01-01T01:15:01Z", - "output_mr": "550254cb374d0591dffc9b2cf46427055095aa20233f86a0c9df0228fabd2c2e", - "range_proof_mr": "a5efdf52f28030363ec10365cf637221ffcdcfcf06a419bacb4f8cf63d720e28", - "kernel_mr": "5eeac9d60c8257ab410494d2a7657fe4c4bd061f0c769107044099b0e0f4f032", - "total_kernel_offset": "99d5cfd0364fa240c306cf88e539f94e5f4a31c1b0cb5151c40147e6d1f78309", - "pow": { - "work": 14 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "26f0822eaa9e43a5817fd1bfde541b6a1f49c0840a43183f72cb0be61288cd23" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "8e5c5e7993047ddd1de118aa006a9154964ed173146d5799d8b7b22fd459ba79" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "90af391bb151b71e2a2716054f29e6a0b436360364c20dc44bb5a4ce7f8c2d5c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "a456ecf3b718edefd05278f9c68dc17c91ed649538a732f257bef0d65f995a01" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "b000abd87d78211387fa2e41b2d14038ada3f869e5839358a74e09e16bfa8f70" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "08871a8ec9634adcc2193bb575e3727ca7f3d25483aba1da04711a2a338ee543", - "proof": "4a397b16ba407adc853cf42a5c1f31ba430021d9968dd0417aa190c812d51404a8f539c07ce08db0882f889412149a4ecdf74af34c5181ff0da815cd1a505c03f62f9f33d2e0321c9f64a31817566eb5cd02597e73ed5ed58098720cb6ae0f5390e11f57b802aa54844bda0df592160f9f16c7a5ce3517913bd948935341700a54f425c03a83a0bed87afc3f3bd6d81a4dca3de29368c2f9006d125f1d36b100e3f96be76b01f6c7ff13930e48d1fd4293e008e71cefdcaeeb431817ea724b083f0ba3e63ce3f80b35b9a3b77c2223acce0cbfb18aa8303966a83fc74070cc005889d2b778d461ef3461f519b5678b7b49b4e65218d590fdca7dedd68f6d39363488ba89f99dc6b53215428703e19ad5532b1e6ab9b2e5b95593306396b8427a6ec9e07e1cbfa5c8912a4933842f741a8515f816353a95a88f69bcce51b8791ac873dda783b85cb1cc422ec22281ddc44bf1a699e9ddb0431d9a4203e05d3e6540c03505afcc0954b95adf85174dc47499f059da8c64cfa0746e20458d3cfe4110bd54e9f98b68330bfebd70c6aa30fca5eae1c91da4343f2ed8b3414782e52b42dc9c1c86c9aebe9b6b87774c871b5fd2dd0371224328f70e5b6306da0ab86c085dedefbccd0a1cd24475452ca14772d6e60d7cc60e9f33ca8a73428819382b40bd60c4dc1116013fc180592447764921ec36d18c62b528e614c5ed4dab123fa69164b525130b3662273cc9c789f6a795da9cb4946041305a6fdb074fbf7553aedfa71e0973e25865b86aefeca0c2389a712ae62c27c883a66c1c0c41aee21dd88041fe8a9ec4270578bbfffcce7a3e1e326c23898c15c812aab35181137e1f259cc7d51d5fb83434fc86ec6c4f2ae23b54701b4ecb51a693cf3575f87ec20952f8d1daed6c3c5901b31ccbaebbe53dce0e9dcf3279c2ca69068bf9baf60d0a" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "207c3cb882ffa34150c6ce4aea0050891751898a5be647d8b23ae3c3414a1c0e", - "proof": "403de5ed22f4701047345ff2dd27d68faa41b77e4cc4bf2793b21993f90b635fd66d778f0f9451e2c13f8ea175553d786fd270adb0b5b796db7ad2ccfd904c42bc281a7409a3846e5ed88f3bd640017af7ab89f9fcc91fe0a353a97e4ec9e4362409f3ffc537cd1c41b1b417e34dbc5510c0d8e854e4fc6a72d1b75c03ea796de6c300622300ec286907a8d11096013bbce616db7d9383b927369aa1fe06c205a96e95c7d95523b1322a8b953376089ff0dff12bf561f66f3710d0356c23f90d542d847340b6c7fe42e7718270128426f3621f5b8f9da4182a14444d74f25c01427d6840a05568edd9099eed629d4d1ba7586e61101ca8c532c05bbc97580e3f5262c6a8ad7928080964d5d90d7b8871f7b4a0849946ebbade84c0b156656e3470e86a7083d98fddf342d9cb2ad472c0da04bc30a362794ae77e5e7c611dfa4626a62275db320989d72d5a1b4b82371914a1aa0be30f24a35cf6081b2e08196a5e32327bd8062146407c9b8079ab58beba47dc88832d400637a74390e1e759070247caf516b192c29d1851c6e67481821e9b77b02f32d4b22241691a9447b54638fbe5696c05edd11b18ad4ed3ffc9a0131a91bc912f315689f86b2bcfc2dc51e0c139f8bfaefcc5b4ae055e0519c4b42c877bc1f3a24b5d7196d50d9dd3480f8eeddaba08e710e86127441d941f9db943178b764d6739cacb5480946f614724a65f446da3aa5c3ea7cf0ef82e32fab4848dc415034c2f3cf90ca138245aec327c6250566a5ecd156231bbabdc32167ada0081bab9e7de01deb2cf6a88799a044ad4cd011d960e6ff989ec88d312a5e19c740c71e076b1e863c5b3c1b3668417860b535eabd7c2ae5fbe09a0864fbee493187807694b2685e9bc609ed7f66f07c6ff36ca68d9bebb7ca72f651f81ed368c9fd506b6fa02d86473bf06a4528701" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "70b8a8473a00ccc41c9e40364fc724be6c35a0c4ebaa9862a50f6d99fd2ed64f", - "proof": "8096025784f6cb5cc61629db616487e406419ed5aeab7578937cc84d6fcc95251a53df0b56e5f1779f83a52c002280e0908aa93ac6eaf65cae9f5e91e84e3c3eca4719293278f2e112df39eef33be74137766c5f6e662066d34bcca0e4a9d50aa6acffcc0316f3d3e5eaebbf9e6759b3578b1e3023135dfc801834b9b3ee81092583161b54ac2ee0b52f35fb2d481d7a4e01e44c3bf84290ece1b3fde56efe09513ea78dd346fea8fab1178ba1ac4e3e7f5831cea00661c3d8e184877780050394fc0b7e81569688c4558aa59cae107d9bdb7e8e4df62908ffd5936276c13a0b5226994cd76b8acda86e699d74dbc5dad284c24a310bc9590ec8f1fd15635b37807e5dfb68019e7b8066c18c4a91c8968c47b724330541f9f2a4acf43c1d196e74c8488766cfc17d22cad24dcbf84176a54834e0a44827f7736ffc9c2da53a42b6549a59fd4603e1bf8f86a687ff713a532495cfab49ab1a231c484d8facc44ee8d211399fe5efc2b9b8ad565cb0cdc0b80682a86ed8859cd129bce74ef7e443849d0fe3e0ee3d76c2139a320f450ca5e8ec684187621d2aa464c6b6a021d92510348e049024c156cfeef89503b6ce7255ecb84a46f345e610438308b266d750e0cc1cc35766a6fac51d876fce23bdeda81b727ace79c868f3fd2578c59c9c29e6eef4a9e107c21e8cd3e667476c6cb4b8c8bf732115b6bbcadf63ab884bb375345e707e712a4d93a88710e7dbfa86b07ad8f645a1b510fb721dc0e5f42b4e5dea4722b6fa7e1a393ebb00e79bc4e5ee77dc0aa51a4e822048f87da3ef739b39d08258f25f94076ef3f6165037a5abb7545f6b5713928bba4bb99b2d28766e2e8237990d9a4ce9de3ed9384d20394ca17091b765e7d8bef20a066ae1ef44c6095421df410e8782712e622d1947f6f6c60f37fb180e1ed3441e3a50e95b06ba00" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "74304d9eb9e04a1d1a4104b1aafc6cad51ea627d948561e1852113f74b83d374", - "proof": "7ec5a262ac0f387ce7c26e53938172c7defa1ab8cc5def9d539698f3f7f0cd7b3222af9109fd79f944e644a9313b72229763af3bdfc710f6996fa54cea556d781c53631f5481a2209e25715db3a6b8235c9f93ce34d93daea7b1d8656e8bb408de65ccca261ff3c06e2dd8b8d9d8cd242c65e385309a8c1e3e7de6f617c9331a3e3740183440d66c0b4187af9716c36ff23af4a31da8179fa9602490399eeb08e3e50a5a078f29ff78d62ae6cbeb72f119f305e4a94e47888aac0bfbe62ff90a90acc6bd63f5c8151dab416a088b19adf2e04b06e558cb7ccc6bbb9ebadf5a05a002ee69e107adc77f6b9e009b34b0839883931f4369c3681b6f797b3773367c463d4a2d1284fb43daa64ad20db210f3994b1941a803b3f7e1746a8666ed6a432e3bfdd410a7f0621b9615e30847318eaf5572f641bb46599bdc6a428ddd741260edf2f36eea6d5ca8001b9158cf45266271a42d7188c83fa110f85ec3427d32a243505cfd24470b5e9ea03650bda54bfb60c5949b64bbd43a337cd3c3b176324ac3dc62a4ea912685f9dae28517e04702ae27458dfdd0015b6bf1bc45608d24be2a1333815efdbe721e851844971d066db0d943f34cb7610987337ca973374aac8ef71ff128155e1bd5063d00dd77aef430b2a70e6f1f65bf9cbd7aa2aa692ce6649f9d1f6af0b32c2eede230f2cc4bf7c217e9de347d6c4f758074092b612262af0cd7ab0fecc3a78274c3a9793aec58bf013ef88d514ddb757a332b9736657cb523918ee35de3635634c64a273f6fce04e8e5f604aca097aa6b041052f11c20274e76fc3aaf9adbd622ec7cd93220051381a7ca3c9d53b5fdae88ef785755fa226b208fc1bb40ef52d3320a4ff0125aa57ec1f52032859e152606684ccd0c1847ad7d5cbcff0aafbd50aacf78fda58c8242d65cf0eaf9115474e5f4a58700" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "98a492c0e7a54a6b86c72ee5dbdb7803fc7ab035e82f8d37f161367dc62ffb2c", - "proof": "ca608c30e84ff44c78e74547a456f46f4b88b4e867b13d0ac7377af658e3b03c5a36f05a52a32bbd8ca45c64f1474bebb0bd62d4e6d7150059cff66af0f0790c20be9efa590161216c697efab33e3aa6f91b24e6fbc6f4983c7eda6cd07b3874ae6bcd49b71c158d7621c12c2fe2bf0b4e15d74124f1132dc209fd25d673f91c0d9501ec86edbfc73f1363269658c3870d6537730ce7745385b433c02f1efc025c7b67c8328744b17d3582e42e65b71e288a110e9c7280fe1794388f475c120098864db5b81bbdd63deb938bed09d978792e3a90309fed67557f460fcf281f0ba02c210efa6d8a3625f7fdb417f9d6d40ab54e3b323a128a3362dac0d2ee9d652edb16fac5f86357cf952aed79c7af35c6b95dbd9dd2c5921a61bb19d5bbbe0e3248d9081300510d3de669d15943a00ccd6a3ee3397b40b77329fc8e9fa808335a56640102d377e42be43dc06f82fed3df1c7ea944052c18ee9fa581601c79389296c722e799c774e67df97ce7bd1a78c4c8c9195c6aeaef0ea787a184472c0dba80b663b2d46f83cab707a7b56ad9b7bbde9d18539db40d80b171a97980194654e20c83b9a6ae126a6767e0559da9869ba11489bd565067b755bc10f1e5860c2ebcfbdf1ec0410e9ba5997a215f1dca59da8862446117156178502cd063d740f6a8cb4da63de395883d541e59d45f2ea71a9fcdadacd4e4936429530e120664f65479b09d994c2852d0f72ad695ddbda5b4e5daa31018387530b88b9590e9490c6f9fab742968331652905a532ebe457cd433dcb119cade97cfabc1bc15053b4e2b4d652cce8807d93135be00229a770b294f60f6cbdbceea38262ec7e29b11760f920654a37f6ee338dfb31b9b31cc652b6284b77e2869fa81e63dc6248706d42706ccddcc95c9fe11861a265ba61f28edfaca9f0ab46de8ac530edca3640f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "a20a1dda16413388c715b669f4e4a9049ab3b7c62be2a981e833f0fe6365bd4d", - "proof": "ac91d4891ae3c52bc788e760285a06bdf5b98c743a38243ac95cbcb7af8720770e25589e51a953502a38bc1ee5f9eb120c541ae687487bf531ab0cec4f1cb32d2e08e2f29a5852a5e640ccb01e810be5dc1ab79eaa64febd331394ccdbab854f4cc6f52f84610625ffd6bd2cb58edcd0d16bc8effcfd4930195ec2b3832e67647c1f128223a20cffe6d6a70089dc6447595bddc44e444e3b87a6894efb836e0d85668f48a6a65fe7f5e75adf638622b61427ca3d54e3bca9bcf42c1f2bd8b90eb5b7386cb77c43505eba927c3756e767df442244c04b9eda790a5feb084ca00020e85a5cd25e0cf6094b90a8d871e49012bdad19fefa3d0d7808f216b00a6114166d6cce72011303b6d9339851748cb4a457ce1e6ac0092d0d8f9feb64f4cc0c005fa029e7ea77075881e430a40c4e719fcbe69cf7e1b4661bbf57f7e42ce550a09dc0bd739a9903f53cec0fd260cbfd6a07d9e162bb2541e09465ee3fad461e52dc739015fd0da145b000e8a4dda2794dae4e2ea9890aa86e32ac0188c75d503ec9b47f34a4a5876d57e32e1106d39f37055a16aa7026996d8132b03534c241884ea51851d097fd62f30d7d45b087d7b6ad1bb81a8fe089b0622c59981b706f064ebcaa5cb795629d4a032e21a574b05e306b9046d124fca811e4124e5e854f525e529554c72fce9dfa32d1959758992ebabe12b6692a99a15e6626f14d01254ce6056237129f98a9b2a620e6bf34c54f6062e66c35fc682e00037161c22679164cca5f0caf93be15d2853ad3bb3f7de5a34a5dff1234318943277acdee147c86eb2cafa4b0dab2f879f981e15ed9be50fef3882043e126e3430a99895fef18344ef7ca047374e271413ef6472f8dc46916ee972c3d1277ae8d38b634a6f60d5db7a720672da27466cd5bfbd1d2d4dfb992cb18cfc610eb91a8adbaca62cd03" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "a48fb8d187f8b9dc60efc67e1c975d50a31afabe88307705a4f16f513f714a6d", - "proof": "acf3fde9d61490f4b9aedbf3313a30c0754822bc45b0266418ceaf3c73e7d51be831d367e6e9da212b281be552d81d8506aa76217bf9dc869ec51283e5396536f688097521f801a06c9e1c172bb5ec3f1085ea1e594cc9980f186d14c7df9870e631c31fdf274f69c85dad0dff7d5b24acb9f804d5a0ca997e3ea0f77c683b3a5cf8468f9342e2076a876505be48243f0750fbcb2e2b44f319d764e2ca789a0ac0dae7fadbda95da1e650becdf3e04a7c9436593d7b5060587948d41de69f60b59d69bc41e4d0205d9dd7c1b9552d89d487e10c79315bb1995719e15988ff5006001b2aca52a01b853b01b7c8077a2e8203fa251ab3b5a85e57cc9128dd23c04142cec4ed73d7a09baff66147cdedaa158230a2f8be1e4b1ba4b554dacf67c17c09d4afcf982ad16375be1cd84912ff19d6b2e8425594ab0a4f4fcaaf78f877502aca5566b7733ba515bf26a418ed61d3bd6155e7c4eb5175b6f464e2a4a004dcc06cc5d0b8ac4481fbc572a0f2d3e1e2a1299498a8b6399cf8ee1be09206d2b968dfdf2d3295827806dd999add1056a6c2d699c60fae76f1f022916a6b1445f70c79fc8f33c53707254409de5c4cc3122dbcb799920957da8ea651b055e26442cce111c9492cc51805e3ebde41db262b88433bc2fb049aa738c4ca93411af19bae0f1da2ba3c07a3a86a8f72af13a37831db7f4484a09a75203768900ad884c90769208673847e64c1ef4ceeb643f8a2ebd2a955370309d5fe235732cc2c02b003ff3e811f7f09154901f7063dea31fc21ba2fdcf87ea3af5c93cb28bb20e2d440a61ff5b7447ef2938f5d1a0bbd1e64a0115a6f598f03a0ecec1d105bbfa59c0f762018045d1a365de9c07a4469afa2643369dff04b50366fc7deec5f30b0331a454c771baec0405b80585b1ed26f939e5814ab80afa503d9dc5c303ce7b0b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "a8d9a0a92b51f5b7c026c14f09a9fed536e67a63a4bce868f1388aa4ea5ecb14", - "proof": "8ec81d0cfa943921d19d73336467e2bb404d823c82f58df419801e0055fb583714ba55d250a8e9d7e7830a9c50ec0a1d7910c0b207154fd6694733742e3ae71c705c6ff14ab37aaeb172deff1c33b3a550b4008e000262cfcc61c006d3fda90cdac4919d8a8f12b667566f1129ff53b8caeab382ab9a3fe3c2121c79ffc8107f2886cf35ee2f0590a1e7fa9a07f35252202d6974baf494df32e0fd9936d71405b92c7c281293e1d2d5c2f607d542edd706919f6d9bcefe7436b8b83aeb207f0fa2a38b0526f1274d84716af2023974f57972f6cfc0bba0c119a2e33fd71a4106925557e321d7ee4b1fbede64d5226f2f279c2de0ece72324e3dadd54042f8e3c78aeb1f2c27e561a94d395fce46d81e896698bdd8d0ddbe59b6c6f3f24480e66c84d9921e795b23a94907cae72dc69d342034a2446ea649d389dead384cf2e07c65095f37c64c551b764b4596553d7bb369cf55de944d66026a9884480199b7d143d25ff0645a57850487e2a6845de363ef0245865c0f320e663daff1f933568ce0c0bc794283d53d0f13deeadbcccff9f12952a7fd62bbba49e0d1dab1bd901b4fa785a126e949cb843a55138b1b2892ce4c083a63a0c00a6475559803c8754ec8ad72ffae043684934b7e3e9ea0301139ec2a959b36c1612c64a93ba3a9b610408bb8d554b4ffd4162153a411a1c405cc96a84223a70f965e0ca8be7ebdb02dc27bf46c425162fa1c6dfb7e0d9bfca4f17f35be1ac4b5a4680649446749435626e009318acdb176277a85d6582d0d2e1fc348a6b426166aeb1790b0d4f0c21fc0a2464e7f4bdb6796425c221d9e6f925381efdf1cce5a13c565f58d4f8493aca5edbfe864f94384ba5c8e8dca03ff1511e513de1067dfd70704fb29ec949079c7d8297217fe04208e2e7a8d3c0fa22bf0f552d81734c42697aff5a68e7500a" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "c26d4d6b27ad817ccb7dc3beecc0c20a572613b6f8ddb321c337c5d30a22a479", - "proof": "80ccde303157eef3e4e9eb9ff04c3464dad029e7cb1d2d5ebb6145f894906f679c4dae5f728c2e708ca196caa3eed6f45cb24cbabcee613c3195162ab981680df8aad420ae8180cad5d9009f4c95e8c83ddc745db1cb54374318c2c9f0a6d23e1c9add00f29b9f8e557cb691b448760fad7c412b61bab79fac47f667de1ea87036cf5bfe1de240de363da8794ba422f4a51d4621aa6d07fc9c086d9ad4daa10738049c737ed18d09ddf37274d6ddd3517bd80d74eaef2d2a8f0510630b556c0a2d0fff3de23608ab5438e43074119fc3125f608a6915999fadb0d9f1f5fefe0624c4254b2cf512845113fca9b8720a2b508c8592bc5329ee98f4125d0d7fdc0baa8a0080b4dc0d6779a6111a0bcbc78b6881f72ffb841910419ff5cb78074c7b7090b5a4dbf014561ef02767f4e4bd78c4243440419a9ba0d2104c50aa81e653ce7fee78521a537baeb7c49d30f49148aa241c644a4bfc8be6ef330fc1020d2f6e2c0d65251ccb8f67e467bb89ba01c706e0831ff8a48d637cbaff197f7652576809e0b311a143f22df1fae38a75ccaf343c51f4aa2767f615f94c70bdf58a138064f297d3c26666e138b42fffc323e4e9e52002c8a550efc3c98851148e04578007966150bd4ed000f0b98525ccd2dd2553586fb458c56a5705fb03869b321e7a1bc8f1b40a990726e5eeedd804234aece4a18215f819deeee9b30c0593776296f324ffa5cf9c2efe136fef175e79e4a538202e5df6358cf47fe6f415016241d6783365905d252a620e64a26324460d4bfad32d4d77cf07cf659501548d953268f5710c15e853c1ea621d71ece7688a13f946caaddc6e20ac3617bae148820f667b63844e9b15c5424c863933b84d2643f7abaf91ff934804d504f8ee53d80de176ecadb3960b7e405efca8789d3be2246da4a0770654ba4670512c81f41709" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "dcc72d4c6a12328805a9ac1475cb2a9d42839ea104ac3c8489ccbf866fb1ca2d", - "proof": "eaa0c212105e79507f42d0540f405a8085492893bf319d1a7447b4087dedcb32e418db387a3970a986bb89ce2bf69d9d054ccfb97ae8cc0c10f9aede8a86aa6f2c007274032afd14e37bfba5a862a75d8a6269019fe14903759e995764056e73a43b27abde5657c0f913799d57c9159ca2ca20ca95d46c076acf90cbd4149a5115af4a1b37f62854b0f80a92905d906e11694ccea373024f07df112f9ab9e900ae34733b6319f697ffd952138d3bb5a67af8caef4de14c84a9e50b3101ef30049fc0b1c1e7d1d4ba84ea98332ba0a7ce254c04a6b67e72777a1196231166e60afa14e632489819c454199ce2c2a21dd7e6d7baee380f195a760f00af8a114224b44c8cd2cd4bfb5b535a5619321afa3745a42f5621e7a6c2d16d0c322407a84e9a57815996eab3c68049b005225145a4d1cdb93633653e38212501fdedbb9e0b20b7e4324edf873342c60f6103211ea9a48b024238cccf18a9528bd8621ed741be72d852f335698a8b247f0a63a52c5ccd4b2091a309efadc2ed1e8b7ed9f56060cb4a6cf89168df6be44074d538045a0f5159c0e4e8fa16ff622b861f7ab713c29a46a2a0e199f758f6836bf6c76d20e8315912bb314d86aea183217c731b0b74219735620314fd5ab9e1783cf9dc9d2a8dbcb4dde21ca0536c14e85c28d1130617afac61aab50aa67b33ec1d4727ad42c88bbc99838d53491ee312c4e3a225066ee3096a94b7a67c91ac19bf4b7626067868d6eb265c9f4599ece3d7630c51302a2263282bcb74148bb3f6baa8fd1c07b2d6a621302618054ebb2b0aa3774d2cafcbc2be1a92749c298422019b86b04c7fc6f13d358742bdb6608c833a94209dc50a62d9e76a26aabeecf470e4919705d42de277ef5436930834485427910494c2372fcbc9a198bfe2e8805e75344b9338efa0560db8ea193e34bdf4bd790c" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 15 - }, - "commitment": "ee48d8caf578688003c654fdd2cb466ff90322923333a5d73c39411c7da1221a", - "proof": "207bd3ab886c5a04d54ab9fb97a2f6730f43e735252570402c588dcfd864be310051402374a18fdb78f4dd3a72405a378082a20147e3f8e9521d8caf74a3b5421a35588f1ae8ec18f2ab63700d72eafcfabcf71fa1a887b69e2a6b1c2f556c26b8a90107160cfbf02ff50ba91f347e5eb19e9a9aeb7b4c43228c2cc052daf46db1457af92750b5220917f54d08466076bd43b5db9689d2464c8253867c08d8026c1b9f65c980761184ba139bca1d40286aa5f99444aa792379a2e024f4b0660aea44f9b409123856129038d5c88d053133869dc3a9fad4ab7091b951c20b17086a3d9aa6cc1983952d4e84c75e6735000ac49064a0708066844156970f7ff83fb6ef54523df194325adc52c11eb09dc4f6ad67bc0fd0985c36a8f4706509717c82375e99baeb1d48665559a4b1bb2836d1bdb21dd941d2dd41a479393caf1636a22f269e4d62188f4e73017c7e5fa9f3374ac2edd0eaa35c82f93e6bbacf1d51d41f6daaab96253633bf72875abdd1034003de4d92e80684dfd6cb8d2373e26dac76a20fa00e3eac83398bf61815882c789bd8ac1af9d3f05183fd115622425c90eb38cfa9ca18b28f4c281c41d6fb4bc9eecb5f07d6a086f4c0ba8c2b6d0821d84b3870cf3b27ef583f294c3ef324fbde4d931ff8fba54676fcf3eb01b4ae207ac2773c088581b16ff5a8af87803560e2f41748915cbe668593fe32f0d5dc623a27173a5371dfa565c981db1ad35e8a6280bc8dc4a3de71993fbfcce738bd28e23054b8b1714e87700e669f2d362afbf055e93024bc64d05b76835b540b0a49ea382f1b12cf38f53237e313f85beb157de25d89ed83e0560c98b71219e6ab408bc1e391f8ed83fc71f637025a45149009c3bc07af25580484de55835b56220a975f240e596d043a9331e932e29b791dbd30e5a87e2a8a9c7565705277bbbd04" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "365d6c1e9b82e217f2653702f60c394e05ca369027246a2363cf6ce6cda8aa3b", - "excess_sig": { - "public_nonce": "365db2608dbe39da96408aacb3f2ff967ba746f7135df3ad3b8e2b9e8852fc44", - "signature": "5fce28a721ab80b7f3aedb57c75e72eb0687cf04d48a6f38f9a111b861518407" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "84ad73516da1dba2109fc78b992c79068f9a427f4c7c73b1a2fde157103d0346", - "excess_sig": { - "public_nonce": "e0bf78ad97ee49eb3cf303abda7235592d12dd80ca3cf75db4521d26c0aa4706", - "signature": "6447d5c08d19930a7aeb7ac665aa810921e90bf071fa287bfd8411747c3e5706" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "8cc6d666abfbb9c3125a1521ce8b3dc9f8bfc5d530bba788e2c69f63d0ada45a", - "excess_sig": { - "public_nonce": "828cb6072ea9f44741d46a7bbf1578e4e58bc48fa1ee81557a5c4228dcf0023a", - "signature": "558d66e1aed2868467d4e9c076b018f8ff60fa32ae6263a805e9b46c9e69ee02" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "ae0f9ed6982880033119f34751958a3a68e35310bb88a6f0560df9f0cf798342", - "excess_sig": { - "public_nonce": "287c7ec0f8d26093361aa92bc488accedb11f7e14bb34a9278ab6839db1fb349", - "signature": "c6aa65a9dd91ebe7d21c0a691f1d1556c3259cd5a5db7be26a0c0981aef72e03" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "b2cf6ce07eea8ddfcba6c14e24b2999d504cdf2de75cffe0d89dfc42b65e0b62", - "excess_sig": { - "public_nonce": "2821497599e2d99bbb6fb706da3a7ff6f3fc6788514a55ad953d5141be42a178", - "signature": "8fc76cafc296576c4d651cc466d86977a90a5f7c50d9fc7b1556b7884adf3a05" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "fccb2f75f2fa67d4c7dd10c3c246120bcfaf5a8440e3ab236d499cbafc84b15a", - "excess_sig": { - "public_nonce": "b8ccf749f3743782df7bc3647b5df46812ee33c03c8855afd6383582747f934d", - "signature": "3d0783c22b2fc22bce34f4de8bae25406aba188f8f2707eb148038a93c63010d" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 15, - "prev_hash": "2c346e910ace6195884d0c4c40705e6eb7616fd646cb697337aa463807f487b7", - "timestamp": "2000-01-01T01:16:01Z", - "output_mr": "f4790f3965273ab0203cb8e84945349c1fdb4046e7bf8e40c80730f9d20a72b9", - "range_proof_mr": "156add0be4289c7c052ff773eb0b2a3da80cbcb7e964167b029f21401640e341", - "kernel_mr": "3a85a2479eb8722d3c06c341824ee6e6b033482967ee7bb880c4e1b396f25f32", - "total_kernel_offset": "d1e313d2626bc25653f07a6f428298f199dc9e332d5fd8532904452a14a7b50e", - "pow": { - "work": 15 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "08871a8ec9634adcc2193bb575e3727ca7f3d25483aba1da04711a2a338ee543" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "207c3cb882ffa34150c6ce4aea0050891751898a5be647d8b23ae3c3414a1c0e" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "74304d9eb9e04a1d1a4104b1aafc6cad51ea627d948561e1852113f74b83d374" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "a8d9a0a92b51f5b7c026c14f09a9fed536e67a63a4bce868f1388aa4ea5ecb14" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 8 - }, - "commitment": "5001a29c380652d4c263ceebd4f1fd4329b34fe80a2fdeca378c695205f61535" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "028c063f7964a5fdf59988c52f8aefdb03376dea6359e390534fea4009659e71", - "proof": "aa1f29b52ef7c843695b6e780f59a5eeb7f0077d37c924f79fa71761908a34755484365590e5038c7852a121c375130113f04cc5d7949cc4a2118f48a8a8eb43b6f5edbf104b0898781b94d13eb150f192eb2884fe4b243abf3ea1f24610b14974518089f7bd7e41ac387fe694c66d7b869222062f3f53157cbd894e9982f351e09ff119359f325e22126fb6a0a9c88b90ba48317be43727833f9e367f17490dc570bc0f8db0f77aa809c4e7afd6eec977f3e27bf3a8c79fe8e52eabfee6980d321b59c7212e4f703300fc5460203d4a546c3a3aa4b46b9114f566514dd2630e6c78680b63731f5ea1ccd1e8968e627865b6f2cefeee52c20c48b03a015a2c49fef2e9b3a502a4e8ffa53742a567981c35ffb93b611fd09733efd9b9d068333e5ca031b308d9b8dd2b5d9ae1692d09d9cf4f00d61a37694d315a98950c3a01596eb0ede5cec6d722d7c9bba16a5923296a20a78e78c03f590057e51cebc97014f0c72c380ad66283b49fbde062acb2eaafcafdd7848918825faba6c93a1e9b7d8062e66814f7c02b979fef2e07a5a5f634252e3dce05d0f14196e6986cb3025c7256e02cdae484766a9d8bd04d57c9c2ea65bb4609ba6a418ea4fa7d1adafb338a5fe5f095f1ac5ce080c0701eb266be1d972733a4104ab2323618367f6326036e09047f0493d3a5ae28a01fe4878952d2e94db41cfc51b09dabe236c89af3460415a92d94c0362e8ccdef2e6835b74a13165be73a9b5ea7f6ee5c77cb74405a4245d6acb4b65906638cbe05216732eb20eed68721e9b0a2da8b6984b053b3055a1197a7787ad8027cf4cfdeffe035b063c2624073757ff604c691ef87547b2d4d5a99a58bd4a7730c820c3daf9ca4da1e9cd4c6674408684c0fae643b25c10b242a83618973dd536eb1614023f274b814ac20d943ce26534188e853209daf0c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "088ccd635ca5109f6268aabd79a2db5b4df3a57954789a85cf5b1c6872d63a4b", - "proof": "6a260a8c1c5874e5e21f14b87acdaa57288ff2d6a930505f127a1d7e7ed8b31a3adc05888c63dbef9199290c08803518ad84ecac1389e3710a01c70742abc8666a9b293d03380cbfa8ab5f4feac1e43d86e14475af57202a83c4fc978549c544ac7576c8ebcc951ccd37e4f9bbcda36864450123142c1bb431693b02131435450e59cf29deec54f9436becbd3e9eda45ef8f9fb6aef71a6611af3664d4174102169aa18200bd556548c4a36fc6794d008186c6a7471ddb02237a3a605bb5ef06ddbfaa6976f1254f61ea3ad08048bd4d2f888520d2d672a01819b98d237ae40bf8fdae7e10c290d0ee90c162dc3fc4684ad37c23fe30faaddc38f359aae3b31d50882599a6bfd60a9b4e444549c20e25295efb3c5ec8fbbc08eae95be9da5e7a3a14e48ac379a4c53229f627ff5287a9dfccc3cb80da6ca85ea3a1eb251daa1f788cef6e53b2535ce05f3d75d15d190e506eaabbdae8b749d1636489003a451b5809bbb9e7180ef0ec3ab92df840eb4596e1c0df8cbcff4d29736a48fd4cff1c78099fd0b5ae245570ec9519f0c2888b59db1acd0b8704fc78484ad90a7d345b060aa554cac700cc248851b191163e7234978c09f92719b47360545362b3e359721bde622a58e62d5cc533dfccbba4b3fc1155b4469e13c417d415eb52a4c243508cb0b77bf6076237c5a400c4d20b0160a511c1f896a23639d48718bbf986163aa3aeb5b5f4fe873992265f329065c20c2a2562a413e9f19033b6094296f41056eab67002ab715482f23d31fe1fe907d6fde5f74a482f3563e03024d88e81732c7301fed5c750bde336a56bf221bcdb43309a88c9d2e13a670d9ff8906f0f1f55ef72d9a65acb715c8b970b4d5c9c620e15f958c8184461440ae0747c7dbf0d326971093deee668a673f552c50fa24ccc843e3977e1141cc5002321bd40ff0c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "286cf3d668ed1670b12def6033b333518ed596d5818ee92d2933bfa9d5dcde6c", - "proof": "e0a06139535b7596f90c6f02005121bc33df9d7069b409549ec39eb84d128e4c58ffe24ff27643b96d0dce3614861e90a254e56b30dbf71a028e18a57fae9c4c54893176139c7d85cea3c12121e1cfd6868ee75b755dca63a0653b693634f872de869daf4d1d5d6338532489e1139d5715ee150af5135160a340001710e74934e6f92213ff2fc568d04f064d7e0fde87dc071e6a9aa0c22c1f449669d49028086b26aea4814fb738490bd4c452e17f932a08b6d34b28ebf3d7d087d3ad7fa703ca4e6d492cce8026f19fad303cec6aca00aee3b07f85a0193b9fff91efd0cf0084e339ae639402ef12973400631ca9ac781a5932952d5a0fbb0f5877324a183252f0ddbe5e38fc6ba1d217a53f2050fea35431b56db725bc4e52315e37c488543c7cd1fcbe8227c20a6ab7a56da84ce13c21bc818c68984a7cb5a0e7cd23406f9864c459b825eb99a5468b9b9559f44a8765f2314812f7c706a729a0b9dd396696fcff0303c9ae0471e78051455b90ecdccc29e2762b294ca224805ff5beac355c5c8a1ee226df1bbe5112c2b69e8b97a74a6b2abba526eefd7be42d3ec18922000e56011faa03da4dc38598b5ee996923e8f93eb1bd756cf131cb8b56d66c61da1b98c98250efe13fb5701ce3439cd2c8f9101fd027e2d7a3ca9321712dde27c8254578a28cecf8137ac1e8d96863de743317d1e10534ac23d5c02bc70a5d1aac31871ccbfd39555c85a8d11b03c445739c776425bb88751bee1a5c0034120572bbb140ef1d4e3aa97ac1a824bb6e07c7781af880649e37be12b68e774ea04614c0fe0c413b5639e8b934b66465145677eb31e8655683a345fa79c5a5ba674ad2ad082fc224bb3205c7af6d21c7eba676d3d592a6e50fef64d0978bd0302e0eb45b8bd3dc771efaf70df82b4cf91cd94f25f582a6caf485eb7c8635303f020d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "4ee2fe3e51bcb325b049121d737643fff4be408e86f1e88886ad63f2f1d63f77", - "proof": "1a04c9a1ae2816f037042173c9cf9e8de4a4ca14332ecd4577bbe812d934fb453e210bf139143d1c59f6353f26838ad18fc0665d79d817ca89e144a850fe741738b7433b66196f63219d26c4f2a069a19df55630f2b3eb10104f9a27be746237640e0eea1cc44a9fdf147295d091a7011c0ef545eda769e544f3c0a55b759c22049ec0d853eda62d7bcbf7ff6944cb74c99e8625264bfd2a4d14ad07b7cd3809c4ef653265620c312d5f206e49fa0d6ec774e5fff781eaa60c526c8d5e68db0837c8aa7607e238a52fe6c24bceb3e084e54f0cace8afbab7a1e9f383cf269f0f08253870d052dbb9a852171f15df0ff25dee57e90ce1084f7373ac73ec21a8149c80736bf0b4ffcb1e521ddbea20de813c28a10db57de11f8c67f78f51f0143c768061b9652c4b38f7d67e49a3f1d710b805aaf8d9fd75c19549ec3c412aef21f0b28abb6c2ac055b59333bcca418bbcd0f0cea80bb4b8df168cb869f1a82b43ba2cfdbb022b6ed058c940249e966c515acc9ab1ed6db2fa4edcfad300ab705e62f0ef4760b389576eae350b97f2e9991f8f09b161811a450d593e3f5bf39f38fa0e43b79f092a9e0069f8d2dfe0c595c1a2689fc6402c1f7fa6ba2dd2d4a67fccbb903dc08e078df1d23265034e319cf09dd3e0935220e78a4df40659cceb2a9291eb9526bb0f8b8add3d6af0da118a8b0a485166fd91152b17c2f8ccedfe59f0af2ae80e70dc1f73565af1a8eeb5bce7b23826e8ff4b08a78b4ef8a3bdcb698c4697970cb89dd4421461f1057a8ed83fa7aaa22721325ec0156de228e0c30fbaf0e06b295ff0569a880a04c3c01e95e9f90aa9c9c3fc534e20a690eb412f51ae005bd3948455313bec85c5134475b50d1a4f248b390c2ae6a6517bade2400535e47b4f25afcadca9dd17a4a59666333adb2c8389b1b6a75f7070518f576509" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "58a10833d01ee9c64cb7fb55efd2b9535f3594f9f611f57ad8bea55c8c020671", - "proof": "d8ce651dd7e4adc0b0609f67b5151b081ffbd4679f795b76ad1c203eddf5cf3d764a4b98477044dd77fe4774d6537d55be8ed7e305589aba5a8510fd43a5b4155434fbb82024a5a0a371ddc46b4a081644ca2b3b7ff966d5f054cdb68327b624c85ec1dd0ebf2bad8e9813a54000219a6a19c18bd52e4131d1e61873fa6eda7235262b319bc61b58726934416eea729ff02dc514a0dd744a5f52fdd1a60bea0f70045bbc3f5860b1fd18ff133bdd706848c1c75c7b43f9ac96045efaf77de405bbb50ba781735456d194a2e33875cae5bc7cbc985bfc4a08adaa418e5374540a3a5b4637d142073c1e67891e3516a7ed294ab02211fff5997872a2f143055822b0853a9ad8d2c70beb46f4ccc7e2f26f5742879713a1293ad7ce2cf1b99448732e70c8f37be5e7cfebbc0ba6941af9ab3d2b3a0f753f3280da3b7a0eaf758372048c9389ec2daba6940142d25fb7bc0c792ebaeecb6edb5b18f51562845cf0637c4565803a2503cfdca9da279cdd8b3483c93af56b17af5d198e85d806a7f75cb024182f93e0d41c3ccd473430f1c6e049a2370c837cadc6df4c182b50449851ca7259806698df9b1e4cefb481edefa5ac78bd28760db1c557c6ce490b346118647176671ea94763589b425b039ffbfacbddac38d254d6b594390e565e5c5f49a88d5527aab9cb492e195fe23d6708c17e95ffae2f9202347778f93b9a06701df62f1105cb959256b8767143b4532d0f066daf15216a13a72524af848a740e46fa6ed0b875dcb663403e1f92c39593ce7758e0640e70e6e70a09693b28f09b1638b11371e44c45d1ee58e238841f5a7fee3fbbeb3790eb55ef5634d73a35d63b52844a8463c47ae091ee7847ab36edffc8f2e191c387d11ac301e9399a040a0e821c1bbe75e6e6133bca43e620480d6b677277ef3e705e79b51c1ab80b87070b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "5e4d69e1cffc6785def7a7ac1d7476207321a776096c0efd685b2d66682f0739", - "proof": "90ae2614971ce68c2c0615d4eddb8d5661c7cda5d0dd416722e9b602dfbdb53288420f97b86126ea1259ad046ea1f087c2125e54c2befdbff53b9df46f8d6f300cfdc5df3fa22d7c5632d98c972e277c953a282f3cc41e5a3ad47e9332622770ec4eee2b4b15c23491992578d437a4801d89639f275acc41f75e354472c1cc1b7ca48bb236d57ffc878795bcb58069d9b71d62e3e642511968179ebb02a9cd00a6df070241fcdc85ee9876f53ee44b8b58074ad36b4c25542dbe95d917fbbb0d199927005b04c15fd1c64882ee680a2edf6abc91b651bd7b22d20b54fb15ea01c4e273e971a948b47e8ce7b2d7746d30c2c67524c2739b5b311241503d74921766da63bd6d2a1709cfcc9a0b30ff8247a01e7165346e857aadda23c92272915c68efeafc71c22df4364537dfb25ffec956c570b6387ab4ddad20a82412d96b7a76fa8525c7bf654d2bf0b0d6f5e07c0267ac48c2060e9a23563ba26771f0e34cf4c5aef9b9b81bb8623e6fdc48e5e1a25d01b55d4516b356c19a9942f7b15611da2df03a94118f5e6d0645db5e5ed2accfc10a41f7121f60632c8cb543b74f26d83e935fb99a1b1e7b51cb76d56829395e7d5ca646885bdfda2471ea663b176ed64761c5864d8fee51af264f5e1bf88d356f7b725da77f17f59eb7f756eb6408b4e89783273e972706f650e4712b017a9227567e461693d2190b88d68586873200f3abb545d6bfeba12b04ec137edb47f3ca3432db17281e3109874376dbaa597e14843d6dbcf7e1c96ed01613d2a2f0e2c1dc7ed20eda0117fdd0fe439e506a4c61be3d60335bdbb99ef24b547d1a67c4ee163605a600f39e48784f2cbde61fcabdaca685b0b8b3ff0368fb934bb1fbe3e5d27d85c70e7a30a5ea7627069e07191cc6c1141f80e8176fb7872054c4676cc8e806064626dc2758ffb1ab509d01" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "621da901f6a0e819f7fbc2f29d60ae403f3a557fb6c99438b01b028f9180ca48", - "proof": "2c6b09f5a043e364673331ab346a05fcb0bb9fc01afdc80057c0e0ecb448624aac511c368e6be38c6bbfc49c6bd32d7943fafb15b19fd5313f6bfc237d3b2050f49c7fe47dbc46ff53b59b8cc3b8ed02b577dc72d04c22910b8fe216cfcb0f5b12dd51bd698a19d9ed1261641fe58129132dc80d5ca125ddb63ba1091d124840b4304b7102dcc5081ff50561b859e8be72cae6639247b844cd77c3316dfbf2066ec5267f0ccb82431d745d2ca29f5411bebde863072b24fc2e938de7e1672b092b9966de759ae51be2c725d66eb4bf3656a6d25d5e040da1dc846b12ba8b3d0a9c72ac84f805d6e8eb02baddfad2417a6e12b44d9bd3e6d5ec1e4006c713ec6d469e917afeb4f90f34d41e37b72967d1578671931eb1a0647149d88f9225ff105e96801347c2715a65c51febb036abc523fd0b82f7703849e2e8b4a883f85c22c45c99b282c6bc8acf2c33ff6e7feadba65cde30391d700236c1ea13b354d82294e7db28ca76f9410cd830f227532d475b7a7e97469bb7ad2bc1df8f5228d17398d6a680be6c482c26d999129e928f7bea518f690601ce588afee19566a133554c3eed25c65fe0470c7778587f241e6ed372fa3cd98aaf180f48f70e6bafa4381a4ab8b3e21047083706588c237ea0afe7386b9991e0259c4c966a5b2fbf8b6aa89e08c9ff64a1ed25498ee6556fb97842e5b69b5bd863b322676c075778b07504324f22bc94e87596bf279268cc702599244798d0af102710c2a6bf3dd6cc7beab8082064b1602fb8ebb6fc8061048ebe2e353f2b161dcf5e25414e7886f85f72e93bfbd4cd3bc2ed60e0419d4b3baf02b832280a49b95a064aa6ca7997cb3e881a3e5b131036227016c034e3d660167c6ca4e8903c9f94f557d9b622d2220b345f253bc5d330a632e1d8cfbce89da87567e8cadb195df1c13010d378a1090b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "7428a2b68b73c767503b91d5e0ad69e40a1e509baafe2c72a903d9e1fd2bbd44", - "proof": "1c6018af99e53086e7648fd6ae626c7f366aecc3a045a89b660919dcc6fdf368f291ffcaca97099592fd9fb01fc525aaf0f5f74131e895af04d360c0d2a25c0f4261270ecf2e7f6f2ac891972d36e0d1c8ee167f7506c5a4b719b2010e10321558925d2b3865409452173e241b4cc25b6c9dae36a615c4b1c9a308413b2a2f46e39947e24a8673fa04104c44a3cd8f966ef84ef179bfcbf5d7bdacc9b696c90c5ce2a71605da34dd6829fa841af7963400401dff43a127b7d053e8dc2790bd0a52ad91537781cba4b592d07344ee191599ea03a49484d018d3f2c231025f9402ded1ea93229e9d8069c22e70e1bbee4290344593cf06f5c3e383bf9c7f60ab69988ced5c817cd158960eda3ed8038aee2756c9233171fc62bedd3d1829d0de0320e8e99172478b76fa770e7cd854f09f1e470dda9042590c6a02d255f1d6527f6aedc0792fc3c7b12a28622c7db2c7a84a54ecf5f5fa9441dc672f034c0aab1f4a2ab757f54d1c0db3a5025f31c5b25b371fe575c5eadbd1d48119167df17a06bc3d4fcd459079b93e2a2986d628c9c200684b5d29f4553290d523d0e5e5903ac03423ef2a116dea1c4d84d113e61022391c0f1e03f4e8d67309789e9c8336484a80c0321a2e739b2f3b2815faefa0aab2e157bca2d2aa980810f662090acd27cc8d6ee417099ee3552e9f2f57866ba17ef162b56f323e9508f2cb2bd3d3981d0c6d4fac879502aa5a5bbcc75265d968a80be08e19b7bb59ea73adfb14171e3bf20b90b72be8c3623333924f1f81d74f2c665e5c833b2027f71d827104672c611651e182093243a59263df42daa0d0daedef97c170214f33c3d35081a4f19c169c4cbb9a959e2667a93dceb06831641a4efa15f3b1a45c8264cd8e13b327c708afbc37376a2b20806df1bb4616f4035014fbde1251e5d7b054d8b8610a325c02" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "d2a3ebe1ab8c968aee412d7535edbb16fc8c104e122b932e408cbbbea945c606", - "proof": "a8e271541dd795cc417dc522bc6b9ba07f9b86b93eefb8b84f5d3b5c61c69460b05bd0f08890b05327553b0a39c48cea012910d3c79782aa34d33a9cda48ba438a8a0285ec20b0dbce2f2a7a15740a14d645628c4f1f9019d0faa32637b99b70cecac752ad0b3a94622ac7c98b0f098fcee487c17972d201bbf44c024fd969113f9a5588812f2fdd7699750f2863c57c04695632e08d2c1af2951fc437245a08749b6b82e2fccb333cf5c7769459d5d0bac81836de994bf058624c378430d5040e3112c38de069c9f4e55470cdadcc32fdce7fcb81882bd6ddcd12ea29346f0dfc31aee962f72544db81276f5415a59fc2fc59cff9e3ed51b7990ab605861d191ae6631783a47d2928241e40cf2a723bb714d8f3497cd4ce8d5a637119291644583259f6c2c034da87c1dd4de3c309dc289e5fdd48f89712d4b762ac0a41b5156c012ceb3834b9d72ba42125e6804da9edb704c9b10d194f5121c2a60551e85c142337916be90049e90be3a997f7391410a87c6d0ef429679e1e7e5b5c89515dac0956844da72f73635b6b033fe33a6226dfc7b15461e23e22de33f0d80e136022d1624cddc99f440ff723b061e8a92cb02ab33d618d6962a2e8c02a966cb64608aabf4775cc7d603772aa759354cedf3326f1d1a1536623264e08bb1a295128fefaf968ff73bb7bd6e8820c286874bee23f4503c7496344e11baa903ab3013d88667ff298364bfa3a6a5afead1bf810380656c3bfbb69a89839f53db9244d4dd03cc97fdd5be7c51e3e1d71ab0947e5c617511872d1a7ec94253e6f9baabb784ea050ca1f836a28cf77fca6ee900f1d3f8d64394186072706470d7fa6b9136aecadec615a992dd263cd91435a277d2b0429f9b367c950c19e09329636a15100a6524561ea26529230da6466824f0739d9b426bb49bb9452d9410820406d1801" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "f2516ae3f213121e6ca722bd5b73b3096bca555bb3b08bcb81c88e9691f11345", - "proof": "44917c9060c7414975f2a691dceed4e51b2c1b93236405725ada0278f8d97a017ccbaa077e1ea054675e507bdacf7d9a13f0f85285d0bbead18e8858ce423c64fed20e6df6c2b5ba8d74241c231c0cd901358cfe7534ea09550dd8d0f456d935a820cea74086213a8b4498745556781903b7cfc70f0f3a635c5ed9ce9f32220aea81548c172f232d9c3518d639403e47f1b1ea94b7a340329e3ef0b5718ba70e401a3d4a2cb292effd7cd3617e2733cb39224b18dcfea9fbf12e50e30ac3a1010f6259872994030fedc56914470e2ef38f5d3b75d84ce1398bdec3def1e5fe06fc4589216c09cd1c841eae1299d1f780476d6bc6569b4e048a7dd16ccdddc03d80ee23c6dfa330ce88ecc393f0d0d99e29ff7502734f0588670c884f8302d74142b564d5d30855f958c3cfd3a98af9bb26c82650e55ef16708e4b426f826e2044059f16032c4b3f6f474db6ea54d7c971aa6548f4e802780dba341009aece53038a0a0eb9d12f346b5680600aa4ccbec875c2ac29cb7f4527a07afc306860e60f0768cadd16ebec9c0d9c4d5b8fac35e4b7c6cf77a0e856649eed0d4c8932414964b5d0baaca209df0dbacb89218001c424362baa3325f4a2dfbb657609da100b28c66f2e2d78d7952b9c5fae870c97658d0cd9ad2b23a455a231cb51dcbbb0dd87d4bfbbe8083aca97ee30fd43889dfb781cc6bd661bf6c5940340309d05079f48eae9d3da6a422fd94e9629bc2f534e7004a2a433cfacac35d6b863ab3ad53f0d7c67749ebf4a88c8d6bea97d4a98105e810f66a34b209cc6cb8f5d9a5a638960be9bcb897ab51acf198ca4c3ca37b9ac3bbafaa635910ae6cd6eb9581c866b10385104710e76bb073b9c81b0ca1622f3d5c56b9fa5de8c473f934ed40c601dbbf705d6a194b6d65fdd1d8850f88d7929809c7207713481ceff5ce2219350a" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 16 - }, - "commitment": "a016f012574e9afca414948e17435a0821a1bb87a5bd5d48a809109efe388d6b", - "proof": "ccc66718f2bf12ad9b1f6a52091feacf243c17c720926974700859896cede6638edd30b2a64c5ff6fe0cb0b088a61682dc0ac7129cf995c594e706c3d878c74a7e362344ca921547ed1a7b9c2fbd76eaa4363ca9863c417e84deefd16d2ed34504895b6c1beb4c4bc5033dd94e394b417b6707c5c6f2c9881b53ab424264ba5f4e31d6aff98ad609c0901a099a8d732e2fa847bcf01e8664317fd1145ef8c70c06ec48ffe284c4a1646ec3388247a2c96f1af2299334a2508749d9f2a0c41a049cdac0a94848044ea79c3f9925988550ca66800708ae5ea53c4361818c31f4020607850d85279e54199b05149a4b11b17b47d9bdd598df7211fa25d56fe7f12c542c6df2e1249461305cc17aae446d41c37604bf76ec703e8acdf2c09cc6d83d108c511db89a000370bac16cb90f437fac51570128676fa39e4f40ce25dcbb53b619f2fc94b0aa9fe3db62555f3318c86ac9342d3c110a694ddd7120e765c81ada0885a09a6e0ae91b0aff774641bc966decad0402b145b9caa476a9f9c1d0727c2a11a080e1398eeafaddd5947dfca40e5b884d7c2f0f3f1217c497baae104e34902d0c4e939a49fe9ddc1df3f1dc9bdc5b315b7a243ed12153a0a07dd24a7018c88183f47bf565290e07f5702d8b0da0491702ed6dbb4514aebd47bbe97d720ee5aedf43201d94f93e471c7e3fffb516f5e00622362d2f4d9b7da19eb6bb6cfeb98e4c675578ed79c4c237a49c8e6e7cdbf6e8a04caa44428d637b6d6b132a42c14f83434873dab18229ad6c8c62022ac479e0eca852cbb6273fd74331771b781ab52b1fad4d93329ee97e6ca81823a5be9908941aa2e5fa13371f9f79eb75cc1403f68bc0d92930889214663a03c52c3f4e1f0a38fb4794507316e7ea160a85523cccbdba3b2bb5d48785e8641550a25de0f9622c80ad77eaddaac665fd0b" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "36773f65ce799f6434a4276e5391613b7da0814ae2362ac2cf7041283cf3fe17", - "excess_sig": { - "public_nonce": "962ba59029fbaf1b1e6bbb3e6ff5d1aa88de5f1eee7715d19a52b134e3e97b51", - "signature": "932b98eb40e9fa369873f50cf75f2f419dad71115bd8ef203698eb43e8e92401" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "84ec8122fa15d1f67e34c601af1bfef8f80f07f3ebcd8203929fe9eb1f94f449", - "excess_sig": { - "public_nonce": "d482a00478353cc203a0b16c7df5a12d8ef058638a6af135fd068ecc25f85847", - "signature": "c61bca5ce5298ee46726881852650cbd615def72ff2509d893883f96781d2800" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "9e05555d79c4ac5e64150deecfe126b4dd6bf4059a908780173796b47509c20c", - "excess_sig": { - "public_nonce": "f817d63a3afc99a74d91358effc710bb36df3cfa5ee84dd3b13e1ffe2a115243", - "signature": "12e4d7be7974c6bd42ce962707ff485098733cffe69159195b363e7efc85f40a" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "aac4c81f0ee5f9952e064c393d0cd16dff2ce1adff80fdaf6534da72daa19a25", - "excess_sig": { - "public_nonce": "f8c483e2bcb19df66acdf70787895d4935c65db62048b90a29db922bbc6dca34", - "signature": "c531343597ebd0503ac9e6e3f77f5b3d51ba8238dfcbf781c8c40580f1d8fb07" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "d20085aa933d82962f2306b1a1a1ac09e27b510b88c3c78a7e2c7eb72aebc44e", - "excess_sig": { - "public_nonce": "4631e542767ab30760b5c81877c843717752021517a83a19a0d4325c3b15e24c", - "signature": "0b9102513d3bc7169dc2ee45910ae61a1297aba69e58a36a470c39a145aa460e" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "fa8ef5b9ff13eacd450602388bf305d4e520d4bb9c21d77654234800e2e3e768", - "excess_sig": { - "public_nonce": "ca857db392dff20dabe0aeaa2f4f6b33eae6d64dd0da8a5c35edce81e7112953", - "signature": "bbb77ec93445da2fa3c25ee4fb9d14bae65dce40e592822c26e039cd96b5f306" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 16, - "prev_hash": "a05ff754ebc783d2ae9852ef97f566923c3cec1a9ca879cfd2f6e514d088db83", - "timestamp": "2000-01-01T01:17:01Z", - "output_mr": "a3b49b84d794db0e71abdc618416f50b48dccba7f1de62ae94cbc1b7e1cd2b71", - "range_proof_mr": "8928eb74439cee7aa06fe7e367319810ec306392e4b89f1a561927e3d4541ad4", - "kernel_mr": "76ccd027390485f5c3a2b617a38beb2fa6698bbf304362a55ca271e29c336efc", - "total_kernel_offset": "c9621a1814c7e2680ceed136d92313e3df09559dd34317660d56d4581008d506", - "pow": { - "work": 16 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "286cf3d668ed1670b12def6033b333518ed596d5818ee92d2933bfa9d5dcde6c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "4ee2fe3e51bcb325b049121d737643fff4be408e86f1e88886ad63f2f1d63f77" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "58a10833d01ee9c64cb7fb55efd2b9535f3594f9f611f57ad8bea55c8c020671" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "d2a3ebe1ab8c968aee412d7535edbb16fc8c104e122b932e408cbbbea945c606" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "f2516ae3f213121e6ca722bd5b73b3096bca555bb3b08bcb81c88e9691f11345" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "2cd2d525604a8fc904cbd0ba6470c7755e00c2e22a6c18e722abc1bd48a28f01", - "proof": "6430920c560a9575b61641e54203097f9c4e2b1107949d64f5a0ec11c39b2b05fa8a95742ccc9eb67d2fbb7e1a7f45ef84dcdbb40be3238ff5f39163f2d60d3476c69fd46ab51fe8794d12fc8cf9395ec43daeb7229102c830259f55182ce24844bd9dc344fdda7854b0adf867f38f771e7b2c232ef0082cb3d7d26a1265bf6937e4f7eef34d649cc7dcec7c9c7daed7c23237e51e717acea4a4edcb8a31dc0bb475977f9e7a51094c62ee332b3ec5a7c486a388cd244c5ee8ceb9bdefdbc306e44ad2724f96c84670133adcbb629f9b384208f425b1cd83d96cc5815a6d480ca89287a892371812aa61f37b50bec1c9fb9db3b78a26832b9d8a5f3364aae9298eff2c4e36b482873079eefc8e12b86c39dd31bd7ace293f9686b4c4942cac368e57ceca30b3d0854cc619fb3ad0d28119521bc2370a646663ae76bdde618f13d6c9e631bf1089eef3d9c619fc2edd27e81eabd71d759344dc91f6c17e97d979663f88bdec97f2db352eabc5c9f9609443e4190138c876428c01f0317ed8fe628a1194ff58f5ba6002dcaab4e2824d1686c402dd7225d8f8a8db0100e400ca4778de4d742e79c66a91dbbeb3aadc08ae4e2edfbf928f955bdb9ae5c28243c4523265cdbab54e1f9070bfe9743e2be66420a9e7651469d36d429e84d309fd273388bf1e08ba64433fe72edeab9dd4e643c164f655315f10c365b5b8717a7af32036cc19cc8ce45fa8a3efc40a8bb924c258696ab4bc69ef216ed87c167268515b66eddb35942bf479212327039fd5ac027301bddc75300cf41586f16e5cd33610d00501117eff8150053a84a5993771cb1364f4eb3dfc711440589a4c95850e281f806b1bce1d2606428897078d8ad8deb301feae6b0b1e656969caf930cabd01e688ccd18baa6ec43a28c94f74cb1d805adacf0262966f600a06f5f0e58c3f00" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "2cef17f0a21f931c2e27a19ae5c09d950ba9f82bb6402b45c7a410836615494f", - "proof": "faa3e07f5b33e31f25346a2060c7cf09bd1662654baa1928835ed6c9218bf41d82482705bd701d52fcd9157470725b8be714887be2199e1933954bf3ad35d018d278188522e8eaac03f0879c51fbc24515851a8237ef292251bb75c250811a106caf4a578aadc5a797d4b2ac1d85e63c29ed22e068e6af86a7b0489e088fde6a2443326a06a80274cfa86c3adcd6bf21a80def059fe6f5a325d430f4c1c8910dabb735f3badc9e162cc661e9a9d41048777cb33f7890e7d2e7534489c9c39f049223cb998e6c8cfc951701b357a5696444a9269a28b502de77cd0bae2c661c0b8c631fab9526c2eb67ea227acc623676c4281795cc71aa9dd556a15e52107e1f469de17752691bfb889d62a46032cd769696f5458d9b12abbf133ebf918ff47e2a7101353fac8bb06341dce589c31c569d382aa7f3742bcf581b45909ef49264de55f7901d250bf377e37ed6e2964e15d96167c276ee89cfa8f9d4e26984b67a52aede96014f0c493416a7e450d20468dd8572c1403317d85469f4eb1fbc3113aa03bad0e98309f3be9db54dd613c55a5524a144ecef0c07572f99a7364eda24dc778323dd32d707dfc7449e3f5b3f3317f14390e810061633b0d731680b9c4594f471cf563f738f5495140d5601da7591c907d1204b2146ca80f1459443e82bec40efeedae7f6555c9f5a90566685de135d8926799e6830f617ba0eea1891339c7a2cd77ba435efa20b29addc4df2190bd0e5b195a5446e5bc9c4eeb1aa880fd81a806cfdda98ca3874631de859d429dbb8e55ca023c79b21748b318e97f861fa182ee3de3c917da979404b178a603b4f2ead6b6ae788bd678779bb625de035cef6cfc42a45fbdc966bc048c90081241193dd5645b93ee6738ceb315aa77b0d0ac6b740c3d024a05c68ff5b401371cbf1fd9e92b64b7eebb2d34827d412cc0f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "54703b0a114c3a1f8a20d33450c5bbae01d5ece2340ffb731b4f486d23f26a1c", - "proof": "507a67d05546f7eab4177b850003a8424b243339abecad4514a44a14627c8602644e56a664eefb76fe658198f9dd8bb8793e75c9859ff87ac6bc10767b66f2403ed822d94454abd1da8906e523e2b024f717e95fdea0c45681829d263c89304b2447f44b81cfcff71786397c5ac39423a2eb2d18216b7afe87b8aa677df6c06ae1b5e885ba35a87bb877e5296ad006a7836c1ff6bd97c5d95c968497f1602906b8c562c8bd4264d7468ae60ce0541e0a889b2d2b455a93263fd1398f8fb48b0caac27817bd422ce182a1b1a11021f56dc1ac36cab960e9cb0ce6292cccadbe07fe0d318cdf2eb6e1cd6c8bc98a3d9ce1b5d3340dbe750423852fa414000e4973d0f8686562c7b05b7b92af8739150a674210500659a0e861dede9824ecd8c51662c1cb1308e08eac02a9b8eaf5af4767c39e1612ac25a58f4130880cf69c83267c7de713f04efbdf7aacc584b15e75f1f957e1aefdca8e4bac8281493cb1e5087a2b137fba007f71eafb6ad91f5202285ee550176568b03791b70f4a43ac2248b0f67cdcd8e612ae628f52770a03cbde6981d8e5c59fc2c96ca5db9f82add108e07908e5d06e0aebe5e48c4725aae0eb16ede4684e4d0477af7d5342a15817418ccddc146183b6c9f79de10e36ac4700c2e5a98ce9df9c4d931b1d2533739e5f4497e1a9ef2cd9db2d317fb2867d1bddd9e10ee45bbada3233e89d5bb9a8c33116a506ac14fa7e79708e24248db436469fc6fcffbc9a09a38b99086cd0a3ff3b666d48ea751df834eea79ee3ae5b293fff6805428c1ff928244e215be4e00b7866494f04129f42099ef4c679cb4960f6251d178758d895baea01f8563f8dfd52842b0098292ccf39703299b68aa0326fffab7bbea04f6277c0a5e92c6a2a4509651a320af1cc8cde20916b6c46d78f7d97edf3ac66196f5ef3320273a012610b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "548d395123185e9418b9b789cdcf49381e338fa00988ccdac76ec497895a4c09", - "proof": "d8568c0272dc9d4710fa29bbe1303df1c99f26bf081f7ed748ac85777b2cf5789c2df3d2d298a5f1222d2bf6a20e01f6342ff4584f71f7208eb29ac79bf16713d69cf65b343e574e66294f734bd438969468b6fe6cc39f8c6ebe1d327d9d5e25e43e14a52b1562e451f208efc6ea9fc6a7be710d7201351086113edf3529b51249b4b2e81b88504a68e0eaf0fbcf1e8db66f5b5af9cf60f26e53a734c0ddde02b8cba46734b99bfda9cdb1a5eff809052efa4a6f2a9f22022fc40e21eb75cd0fdbec19dcd504b573095eec1a9bee120a3acb403f859b7e4ba8aa26b8056a5b0e1636288d0c5b1241338dc27626bfb991b7c2186479bb39bf7e97a6e2514c5e4b7cad9383e5023582520e96d1a8725e3f4efdbf1f6bc1e4fb6a9b98017fbf9f443e31e7cba8400c9cdf107ea15edf7c7bcf2a8581d72645b5cb5799242a99895436196acaa395491a05b6d0441bc06f13b37f8145d245087d645af078997c2a603694657e484806c0db338aff2386cea994b19df0a80d5ed22426c137cbeeb160ac79dd877066ee82d3098d9dcadc2db2cae205c496f974c431351817a63c6f4e1a2e4792ff59a0dc37a74ea7a921830baeae818059735b5e507442689668e756fe3a49eedd924c50441c681af22e98f397a81b34c6f92be3c03798ca12fb177ee8fcf746736aaa172fcebdc93c2522a7b983a764ac4cf85c0514db5942828200046180f214e0051df7a65b010f5422e6bcbbb7d69cb8b91bbe33dd47f3c6ad0c58882b7c75682a1afd1405cc2367a4cf5036dab04b7767d46ec9aa81c6588148584ad546253e1f6faf634b603f8d0ebaa27b77c81319c231046ccda474c4f5218b558685f10ccd92c0c41cbfbc3c635fc4096973a6f4cb16795aaf910392f00070399a1276e392ed4fb678de0b0e6e3f3530edbbd6c797610ef368230cc1f108" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "5c2211aad963956a5dc418bfa73cac3a14ddfeaf5ce07d796d438cf15e65d00f", - "proof": "3200516aa6b8816453cd757b70f6b3c57df03ba9257f77845095ecadb1e7576f6435eb2d266939385dba6f629e494f6abc5e9ee4029067a837edffa99dd4690bf66c1056de4d712c974ff9b29c530c1bc9a0426f2485720abfe3eed6d2e3cc54da3da5e37edd10df6aaf9456da15579f92dfdd0ae41c7103a11dcae32ffed243e2507f0f56a4252827612b5cfcec0381f21d7f605c7aefc90c1fdc72ef257c051293d1b68efff97cdb44e75b1ad961fdf09794ed0438fe10c9d71f9a5db6d106af0a8c2773ce7a86d7519ee666ff9a882b27e3dcb1546fa1a591a3ea74b3ea0fee9db7d100944fe5a60b36e683defc4761f559ddf610e785d96509b073ba381056643cf0aab9843d1fe6158461696b1599dd9cfc4d836090d8dad198d5a87e1026883b054448e4ac18d60bfccd18f7ad9fd4360e36578c641c1936592ed9eb777cdabbae80a80a92e1ddead13d2430c753d27fa23588c5e2c09d9893c443084aa4545f9c20a81431783821273a7d06d30a6c6ccd5adf11541b6ace9b0319fd379027b2a3bc928695c184cbd2bc375869d84e2b7a5cc0114e81755322167ca50698b834e20d0693c270aca7f555d6df6284fa47820539d4590588ed96135e4a6c186b9357c05553454d7d58c87bfb0f46d3acd46c2fb2a5d0a940a9cbedaf5a291ae8fbab8da88bfbc368bcaa6b39b5f72638618b6597ac7c2c27833a5a2c7337c48e0745708f67442410a1cb005f5bc3ddcfcc3b365654703abdefc2f808503e4098a561d78bf1800dc564f11ec116942ffc9e006dd4799e921a7a6cecfb6458a26d84d868f973301c51dd6a6deec6e32e9051945aa893a6e1ac55a3d260bd5d3e3f858e47e960be6f62507d35cda0208bae80c65043fc4dd40822a9f171410c026667f5eaa863b85b8b39a84671ca52910f38afffc6ec6322a8ca2ad5edb009" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "5cc33a12b9cee033fa33833a2eccd2972eaa56a8da4db022642955026d707627", - "proof": "24bb36bbed58325027f9ec9ca9a9fcd5b199be296b90decbfa702e215fa4cb243635b74aabc6acd9d81208584831eb04828bd46ed15c3b07a5562852d8183d13f6c054c78c2ac3927f6a7a4d6828c9049b6783b1b90fbc2b33d32d400d034a39322e7cd82d9503ffe094ca2d701c25522639aa28c99d5efaf41bb830dc090a0d1e8686024f80f12b806cffad8d58a9303e505f71141ac5341b706c4ef019ff0261a8973b2333bd4d9c9a9d668a87672069f57352a1d2390a466708301acd4a090da95ac6a6b1e686061dcad5f0c7003641d19061ef2ad5a7ce69f6c06b3b1702461552a7a88177900c2a2fa81e4f6fac2d15f3bff704551468d62902a1b53d53e852a5b84313a7a07ca2cee8270f1fd9d413425c782120738b2a57f39e30673e8e22772ce6fd35b80af7058f45fb464d4d3f09e95b4cf8a7526f9fd211799e2b5a777c6780b89de070701c67cf18faf7926bef89b7a9af65aee22a6414c5bf2fd87cf7e4fc3e7975ba7b108b6a109e1c2afefb3b57f400e1eb5509e21747c90202849fea0f2c417995d6e6039951a830a27cc10bbf6f7d10c78317dc348e44540a19df73efd6a93caec759651d0dd17ccf4e7f9dd7e9479b4bdf2f6217cec740b60c1b143400780ec99ab4d782a6c50bd720456b662c77c9e9c92f7f38f70547ca2fee00df13b24032b65075c14dc92b05324352d433f17acf274366a25914502c371b29bf6fbd8fa9da74846571afad6e3173efde1b1f4864af2d2acf160b4294bca06879f1e7fac44edcad8b68b39ffc16872a17a2f18cf45aaa3b7fa9aa55fae1ae1f8b534087ab019864925d412728341facafdaa68631271c0063859536f5dfc4ee292e96d1510092e9ecdb1e5baa03e874e5124a22522b6dc35e7ce80328ac33175f996227cda731d302d110125f18980d6f664a5106e42535bde7de0e" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "8015cfb69f90eaa2cabbb9895cd7df600dd709ce672ccf9241cbb7290b562778", - "proof": "7ebc5653b4c69ff86169660d655366f84c2c5be38a01058dc01177646e6539530e88d25e4d679134256b68af0bc00d79b6376022e7becd5d2ca703198da5690e9e31435334ac20d14c922e9cf03db3f612ed1f8e6afcbd5f85040f29bd62ab0fda97797b08f6d71cdddde2ea29e0bf1d5d912fb800b6d190333083fa56a51318e2d7ba6bb48fdc8ed06d043dc219471daf93080a824b445353d8be50681b06019189a28b0e443d47e292a485d489435eae209682eb37fcec0896443aa5754709f4b09031b206e9e8e7402dba91ab71098ac7eb7651e4d2d0ec0321b64dcfde0ad0d7de4321c82d51b25331e28a986a189abd11774b67bb362ba734a6a48f882d3e25662a692df8a4027f8e6ccda623f8a1fba252c9d119d4fbf4261f18eb1b6d6cfdf17b8be654841173a7397c5cefab9e6b770d10d4f3eac94f3514b43bba1148a1f7a0e7b92d32a83c718f351ba550289273a2f4bffc0cc36cf2fd161c320422f7b26d7233a4c45a9e70ebbd70ecf3674d7939e4da012d074583ed14da164ce4f069e8ea5c592b5f4859eec092c21a458d0cfa36821d904015964895606a1744c5bdeaedfb45d89bf063b114350c139dc5093922597a63a8afb221dc07b91dd03644f9589dc91b1a3965c02f4bdf64d7740b65ff362726cffa5442aa9279141c2262b7db0e8a88de302effa32662e2526b320afbaf554fc38ca200d4387052e68841a461fe34d2f750d28bf3935882658c9d552e0a02a7b2aeeb2d6ab6610b2cd08ee7b6ec157d5990996f4b9b693cf5bc2309b146102a45e0771d17a8b6490a09c27c15af1d14a63ad31936bd8e039f2ebd223d8b8ac45595df716ba5db0b2bc908c9edb1a00380a084495cb405f435614d3d1468988ae928d02c801a6406f94a73d3a8b0ba3d7addbb1f6b4c6185e4ea2554d9ce4e4e387de6a1097a1f0c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "9c677d2512cf478ed4ec3603b4abe96db7a08f7a0c77d5000300bd684d385e24", - "proof": "f02f85dea3090f54ef19e9a78f21f8306b0a5cff2e473665bd1e5e1c6334cd1fe40977724c35baf7e9be7a6bd6cbfe9c8a3ba2400aedded3d1c611ad306e176eba0582ac4fa112711dd571ee7f029bcfbc77f7a9f8d25c4cb89cd4f6d7709c2ec27b48a2cee4b660bfc2309fbfb728bc98f1e132f087efa9b0ec43da852aa9270df2d584ba47f6051e31486043e422cc78f6448ca6e6b618415d0850b8d53c0196cb8b20e2da51c4ac74a5e99eb775a338f2c84f68c0e2f8459c09696513880eb2024ac51755ea9198f0dcc9f817af405ba10203d69c7b15e96f3154d2fef80040c51f7e11a6ff2d9879f0cdda09a75c805bbf3e978f6e270b130aeb1996f15040d632c4215f162d064b466adf955f281ce9d23642f5215422aa12a53b52c34d0e3e1bbd58d9e3ebe11f049364d7a65d89502adbb0db3f3daa8da5b3b603c73fe4241287f9c4d7b3c49f8efa6dd4edac740178d4fd1e1c931ca4d1333298571d0e6192d7a3ed7cb70e975d6f8049a342ae34b1c80cab60e8da949651ebf686223ebc23d49c54fd8d8f2edd77251021d26a66124450239a9ee85233b10f7a8a675ad4849a3ed9f20e655229e57d694f574250149b994376d3f6c895c258341f1e14c7f5025504ffe113249fda08a08bc12cb6ea0cfc3674e49468a874fa4c6e619458ea74109ab03a5420699cbcd17ec172f6e048944f4277ca54f4d95444ae759ef48840d404fcb9d2ee709f1ae8242fa51d7026b74818a6a36459cc237fcd6a4c40b622bc5f92f3ecf0a7036bdb16cd5feb28c447533e059edfb9dbf3a2c5069aaf887e6ee0b57fb92226caa4232319f955516f78d116881ae5466dfceb88267574ec35d461f78c6d2b2d02316cd810ee3a040edb775e2d1a51ae106ab3a303cf5b280f12a0bebfb46b663f898ab593fd70a452040edff37fbf04d5b04e0806" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "a2d502025270ac4059401dd1ad492c978f6329ea5b30dcc905853b6bbf3f7a31", - "proof": "4e2e872f2ae61c2c7892ef9c934cd3401a5889be43819800dadfc65da9694229f0cf090c09db4d149ccff7e578761962e4f806c6f2ef8c428d7dd5345adffa3c34b106dd18e67badb13d3c7d6b46f9f7fa46a28c68c90b6d27ab36152fe63d3b7402b5f155f6fe28b051792cfa624e41ca08c86aa20cd7e660ed5ee53f37f03deb91c33b07062e522b371c1840debd01701115218c3ba8b75f42ec3c0bcbf908c76b472ce2519ced554105bdae28bea9f2e10d4b43e105ff3b2eda4cc6dafc0e231084b021b72e29b054d364addf91afa1544db0fcd458579df171acfaaaed02f86944762da1480e41b0023b75cb81c66154c19a8a06b50140305e06fa1e1955964dc0dc973d4607cb39da1c27467529c0d819cd1791931999a62a459c7c1442c0ed8455d9413f96a44cefdf0e6a1e0d37b4ca33ed5b2e03947fe6bdd6383f43f86104ba896ae1588a815f24d32010717d777b1d6510c5000491207985c0ef44ea07bb3b091587dc0d1d721fcf4e8b30e6d7ff8c6e6d43207d5bee8be85d2347f6d6e57091e871eedea2231175742ec688f829ba57b14caf79f6a55228495d54905af96df89dc8d3c925f40e6a1ff70a7d0e2f4847bc2e1f93aaba59b209f9702432f34041e982903c5479fc9d5e1e1d4c22013a233311f790369cf2298d801e804e870357a0ad71209ff6a9607b18e0b9cab2763cfa775ef2be958a243d7741468995f282a00fdf09c8d32b00aede28f27e433456f5e13ff538c862bd22e1256c88851c79d275ad3e5197d4c444f27555727c2977f42f16a085e06606430231045783416dade184124a0604ce580ff8b2a49283c5478afdbf88c1b7446d6e72c24decd987374aff734245e247618861a1b5265abc01b42d1808d4f22643b5015c57759de1d11c4bfe0f012c34f2112026a8a0a10392396c4ef9efd24fc3f40a" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ce0990eeb775f239d837bfcbb9db0a963fc528fc09665b9b8d27952f3f1def63", - "proof": "26ebb4606de701d153d0d9dcfbbc71850788db0dad6886d192090a79090ddc677a2d180339188c79e32e9ae91afa10439c1e289b895f0e329ce47243d561cc07a26a484c4e1475bcadf752482753eef66e21c840851857975b6981aa0d0fc2226ad14b4b169eecd49643d65663e0d156b4fc321a58125e17728193b792f6ad2c04730bbb3148a6a0ced346e03f83e12e1ed7e078b4114a0e833501eb98bfcb08ea76e93b7b959f91203c36bff5b75dab114027a03121dea22907574fb15abf0238ccbfc8e86a9059e856f4654711ce5de2782d66c8970a08dc1e6a32338f0c0a24f97d68e4bc242061bacb1a7e0600c1d8d8cb6c0a9580166b0dc21e451c0128926311c4720c695e9b84715133c61e85b76961f50d84e24ab1d4df87306c7359c6c32d113c349fff9f44ba1bac8873021454065d058a768216432656d725bd6e1c1b3cc1ccb7215d8135c1ca5453bbda27de07fa8809747860f7fb7b17a36e0bb4476f5a11a2cc3f2844ec6aa34f9817017e7286065f21a2f2cceee033b2fa27668f4c580b7ca8c75d318ae1bbfaf0fbc5b4e88ad7132066e6eef4f1d8e6c66a68c3df13ec198ce334f6b50346512b9560dac02a946271c19054c1c355eaa3090c476beee09c82a2c2e0a8ed8e417c1917d0f6b0fc064f4e3fbc807285848d2c72881a60f3b3061e7f1be6d83b90281e08a6bfc5ba100ef4c3766a97c25c676b1cbe9655066a4700be8d6c21577b02d784fbfef965a591e55e369594e6778a576af946629271b700ed5b54f3207a5ddb113d7b44bbd94f1e5be276485dd9613d9059f837251c85e64e333adeb472b273140071194bec17ac5191c70125cd0b79d764cfa88ebc5561c8c9925ebc218f86ad4c2cc67fef7855dc0c8044257ad9099b4ff9fae4580da245f87ca5f13e0cf298c7df063ece0ab8cf66aabb6aa5e609" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 17 - }, - "commitment": "62fd4d95c4bed3b7c245e4bd25d243fba909551253245886172fc2ada34d6f38", - "proof": "28b655b9ffd4c2c2ed9f8af864d0bc109606850e9d45d895c9a8d6ac4b783650ac7acc0ac50bb7f08d2b585fbd5749d0c29874ec5ce167a12797f14340c73a7af806046d9de3f23ce4aaf923c951639e53fad8e2dd52c125dd6905a18dc39b0476e32d819e9c92b01a05f6fbdf2d4596cfeea2e6125722e9232525aacaf70c74a49abfc9a53b65c6cbfd8fae275e58564346bad39c48b19ec1019ad9ceb4e50a56dd69fad7891abffe0e8a7a336b4637d3a62b1ff09a11597de210cea5d18902237f02afe5559a59bd4709319ae0e48b1e41589f94f5cc8c319c9bd0700470073e877118680c9dba8325c6e37f8d9eda786618eed8762401a47d5f1bbc9d943bf87dfedd1c5c0c9f4f22e26e3dc1a5e0f4e1ebee7fcbb3c0ee36d49d923c0865e45a0d77238015471292248cb73aa7aa97567423036cf2976ccd17e0b8a6f144d2baabd00b6990768940b747fc3522026df7d9dca0aae6cd662748c14b089f1a9e952c297aab9f528414c483cc626219fb20d810abb61e75c90c01c0bba7d44eea9b8bd9b4993f8276ab0eadfb4cae2de54847b42dfdd0d31aee7a627ce46111824bcce1488f5f25bea0bce46a1f76d89dd2c1e3fb18943774f8ee3da61b3c1530d1d15615a81b372c1e4440ece37f6519c2fbdea21b8b8355bf24fc28372a0c2826824e67352d7143073d3767fdd5a752bd7c1efd0ed594750560da963e05020cc31911f925267777c32854ec4962542f7d0f4a58fd48b9a23e53187cbb7179d8764d75366d6612657c54e677490c686e7ed6cba70793c823f38d36d912873f882f64de6870c26b1b4c9aac318896cdc4db53ea88f69e799907230519e34e4d7f46e6deba61ba5c863ef6f6256c39febbaf6fb3f104dd0064e4dd4a17ebc0091ddbcb878628ae7a4d672b8e4dc050846bff81a6ebd72d5cf9a189d315ea3c01" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "ae7bf62d03441fb8e3d4cb6f5096bdbd74983a903d96876e87f9a98126ddc441", - "excess_sig": { - "public_nonce": "524f5224e615441da679722d4cf08e9f163c617decf52cfb5bba2abe5f892905", - "signature": "00703dd145ddb03d628f4c760889a4183c518532200271497985e63168bf7b04" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "c035058275b96b11a2052d07f21975e551970b50488e2475ef7be663c99c026f", - "excess_sig": { - "public_nonce": "bece6cf9d7fe54f717f98a3f3fd39d5e85d214de544fe708a2759cd91363c862", - "signature": "e2f73425fc950ef45b86d120f5d0d1ab73b77781b0898f79ddcb3e394cee5b02" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "ce300631ca6d66e1752889e8a47de28b65665e0f61d5950be7a262bd73ad6307", - "excess_sig": { - "public_nonce": "3ab10c91c976b9a6ec8135b97431cc5bdee6cabf544039afabe9d28e0010982b", - "signature": "0c577af78e616bb9631385a7630f06ab7f0d5649d7b01e6ff5a6d907265aa306" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "dce15b442106399a029f4f4f28aa12b2da342e2342baee4faa576ddbe9168530", - "excess_sig": { - "public_nonce": "1c4d4b5eb3ad747c76b72d20fac33e7596d68d32bc0ca2d7128f6ffb6f9c410a", - "signature": "dca42b1449f5d825745a3c3427698ec8d0b07006809b5b4bf8bd3d18c0b59f09" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "e6c485fcad1813e8c4b414a0a471d37d3883538f7ff47f1e08d2fc02b5f6f66a", - "excess_sig": { - "public_nonce": "8895c48113ed643b5432071718c9e5581ada6dcfd99549c77eb7adb8074a6b32", - "signature": "9ee1eaea2344c63cc96d7242939aa2a7ceb1e9a91b2349489e7ec635fd68c401" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "76c10e4ad0580aa22a83232223169b84e431637ea688b333de7790ade5e13d16", - "excess_sig": { - "public_nonce": "fe145d465e6b21f4e3bbfb4d8791d2de45e47b33faf72ef7255dc5c46339c758", - "signature": "419c527a53ef6d968cae2ab89f80eb0af6178e9826424acbef8d247066f3c200" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 17, - "prev_hash": "19c746d67cb8887633d575b4d27cde761180581db290b1abc552c31d8d04e12f", - "timestamp": "2000-01-01T01:18:01Z", - "output_mr": "d2516e31037120df976bd17078a15e89759fd0c95acf345260f6a6f5ca92bcb5", - "range_proof_mr": "6ee56645bb35e6fbaf4ff7ca2a249c589d31a4bf22f41a34bcc251d34db9adf5", - "kernel_mr": "0ab71ed8f743de718bd52915a9877b5b26bc145e9f7fd3f32d4b669ce1070b5f", - "total_kernel_offset": "c8b01d6ac625c3a1b97093743fbfd414312115a76c8c4d50004dbba555284e04", - "pow": { - "work": 17 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "2cd2d525604a8fc904cbd0ba6470c7755e00c2e22a6c18e722abc1bd48a28f01" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "2cef17f0a21f931c2e27a19ae5c09d950ba9f82bb6402b45c7a410836615494f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "5c2211aad963956a5dc418bfa73cac3a14ddfeaf5ce07d796d438cf15e65d00f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "8015cfb69f90eaa2cabbb9895cd7df600dd709ce672ccf9241cbb7290b562778" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 9 - }, - "commitment": "2601c6458faf644e71550b28a6d3fbf262c650fb145d4b9864abb9c59e3f497b" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0a18c7c98fdadcccbffcbfd919f619e71c8a344f0b4b237d032433da47a4e86c", - "proof": "541e7c45cb7d1e45cd37b454a13acf440cf4d0fff6ab9abf2693396e723eff2e26f2460985d30a134ed5afad51b098cfb6017b3da9b75885029184e814757d675a4c5e8d4a0903149ee8a60630495229646ca11a7318c0d769b9f7119dce7f717c2c54d392f32e5136c999e3514a9692e04a9db3957e56fbc5a01f2b2853ef7cd08e4bd959ace1c4382413b23254b90d31fd73add00d96b876319f9f2e98770fcb6b0dfb730e49906d1818d0a2a246e22bb6f4aa93ab1c84e814292f62d45f0beab6208540591555f981e9b47471f43aa8b12ebf9b1acdd82f8e4a523e1aa208e61de8bba19bd3b63d9c149ce187ecac3ad3896ddaa18f4b7fc81e0cb0020c7bf6975cad1bf3784e30bc8b7dd2724c4b04c5fd03506ed68f1cabc885acf07c2acc17ae80a96859674f0ad467403987b5bd8696739eb7aaa851d5d9c759806a400efe3d0faefceee8e4c8c82830ef804b086d47cb1f84a5f5e3fd439709b9b375bc980d27b8d2eeb07aebe09380ace845f9ab5f6276f28622213190171c93937eea81928ea3a08c1995e68a2c34b93340c7d43f8703014bb7a39a7caadfc370615e60e6a39c5f25e6f17d994576304b7a1ca28a59463471fc9450636abcae01099c0a3f304ba5035286d546f9c3c33cdb10f3c05900040889c6b5964eaab8c538f4afb53785b2c0bd568e05db2fddd5426cf286751f146ed2de18a83169b51543029e5853f6a7dd4c7f78160b82d0b1f7c5017656af44e8c99fa8139f5ccb685db4d9b1f1f41d8c6e86668c6767c48ff7bd3477416ae30aea7662ca1ad9eb1a72fc500ed2d97af2a9dbb96e3d6ed15850642a5e0f8c07a5958a8f145e0e4251117e7a9bbe1548440582d001a4a4baac20d9044205bb1fa808953263b80b7d310e3fedfd05b7393c8eeb043c8c38fe6c433288fd2301734e08a8d61a0cd19bfc0f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "1035affde7b5dcc6023e23a0633e706b861be19573b836b940b2329d93e06d66", - "proof": "16cc7903e9b817593cab3cfcb66bdb050a91b3d4f88f3ecebea9f3fbf68b637da4c0dd14841620be190ce22ce12d656573703161ad7724efc50365e340d0db01049e10969822f811c1ceb59d9cac4cca78df19656aa1c40ae5ab44753afe5d351e71975b716986891495ac866fe185f29df9be63212418c63dae8ecc5446761401c1e70acf46c2d5339bbd80584eccb73f40e7b97b2313fe6eb289f73e960e00e03f021b0f6b711fbd6dade3ad1338b2d44d12379ac150e5780e37ea4b887e0ba453316575ad75ef04a5929368499a25dabceb385d2ceff17a9439715f13410b0c953013be848c167325c111c3da2431fff9047d52e0dc252223c285d189ef7f4cf83258ca112dc3be902c603030cfef4b56acb368d3e4111daa973ad143ea50fecacb554d374c972d43a10ebb799afba056400303aa14d4cf6d9c14c2ea114f804d4b067a66d52d51224ee883e33ef3e129a38fb3c250d3459e252a2f81d21016e776413fd7cb07f5f1f7a83a57c9c53b1ff7fc3ce8f3060320767cdabf3c7ec0850f385962c1c965d0435f8885de606bd9b456f35af571dd9fd39b0ce8f104c288c02fefd82d69cc28f192c153d4c1c95a122b99da62d074c400b23313e57f0c86f4098a99796becaefee833e72f99984d2c1fbb73a358eddd8748e2b0b972defe20fff4e5c4cb25e2eb5568437bf6b519682a96d58f542aa5aee5bb919525147610a4d4578dd84880fdecc4db7593c8a3ebc6eb79b8aaa7caa0cdd99b2f21c04a72d4b139ce0769742b0a83eac9a397932e9e4fc073404fb35b12578c331d48483678f129e7a9bd1733e27d3cbdea45e380e7714e789ebc6d6ddce4b2f2783794c1e38697aeec6b7145a721e68884244d515d5adc7d13b7d6cad518e9bf0d9ca92262ce07e2090b35b2ee7c0b98cb9d67c324f532100d6d3651708aa34406" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "1043fbb90156152c8f56513ca230ca06150a19d6e7bf696855acca7ac8337248", - "proof": "dcb39b2f5544222fee9be2136968e8daa0f3a88095b295c222655f479a9316016e3f35fc3a65330e955a187d85c51a8676377796ff972ab7dc017ea5871e3c3a9ec2a80c5017ec5f146e8e4d3c451a3f51525b2d82773e8beba2cec287e21f74085be747cfce224c084144532cdb2f883085fd2dca6b83e1d72ea5a6a6ce7f05ffb38d02bb751bd219d880e193ad0eca1ac597d77987efae72ed2ece03a7160feb4ae978c7ea38392f0e41f32a35b10d28682613e1ed57c7edc2645cb309890c3508a253658e028aab7dcd536f2b977d610cd9bc88177431c62b4860394c410892e324803b4a73a7168765fd767f1058dc25df4acf542e813144afb177a4dc370219f96371d7da1dfa0a9c56e0451aa1b827dd86f2134cc463422a8f7b963d5360d0388718d537f3fc9c96770edf91ded0e4bcb762dc075b19b62b3dafd5460d5ae62fb45b3bb80eb1c87c2531063eff426712cce89595c4ad77d80d9850810d16656b463d0b585cd8c06ad489583dced03a04796798178e3446b02140492c5ae01c76fd3b049b377676368b7d5e131f9ae363f611cee833716ba1408e36d02f1eb3614efb0a08016408bee634966ca39830d736c98aed6307e7ea4720de4c019420b6ded30e0b065680e923acc8aa7f7b520c4b72836bfb0e1112369ae7f057ec6159066d111b9ccc35fcdc9315200f116a2c2dde0c9c85a86c789a287f087d7e6c7809eb621e33f7eb52a4bbfebff3d39940401b1b54397d8d16435f8e88511224bb4c3d267a8fadc447824a3e7b0d0f9a49a13a1e79a765868f1e10fa30175ac1c0737d54883629921a5b194e78047a998ebbf1e562a7ee9acd03bceb69760976ba0c8f52a3fd1985816087ca92f0a9d04d26420045498bc3a1dbbc8ea7032cf8090964476640b2140aba5f0bda3352ad9f87ae0ac20190641c500d5ad207" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "12377053a059a00c08de8000654addc8aec8d880805350dd4af1c5a898d2a262", - "proof": "365ccb49a1a91a32b02e3e63be814b433f28d3b2dde875e1c6471db3b6f036579e906939f03baec81d08490d35c95de451d8022885cbbb299c70199fabd03c663287f953c5837ccd492d68fcc710929d52d20fb97bdecf5a9e3bd9cb56c46527fee55dd0a2fcb46b295b90c475f108f50bc09de36dacfc1bc27a59d164911f49c1641d61882ccf6bca4ea8d044fcd7aaba567703c8588d8ce340fee7edb4d607b389a11c29a0e49869de780c1b2cf5d29a6b84a3de83a2de32357f611038c20ad2386ce9625530d8ca0812c4dea36fc32f65f4b1ee0a8505874624df45aaed0bbc450b847ffcfe6f7edcb903c4b84b791537f4d95d19c8562aa451281a7910507adec3dd1cc0f73263c9cb506780e18def5b464d2a9cb4ebe8d676731f3cba455c091dc841b0232e397547c4b0f5197b4d7dae4109505ccd18ab6007df47a431fc8e24d251ce3cffa9a3cf4a04a99bbe8805d20511066560648d4712dc6911124638e64538beec90046c16c5022e9d4edf9129915d67bc37e76f187ff06dc555fc74d0cf3b6aa08a4163018d10d4c5e4c74aa7373a37b7906c328538f118f800daa69456a0e0b2b9d6e5d66c505571992f74692bf367e65c718418aacaddca466a3ac04ea78e0b9154c84f6c6fa0d1b4effaa8762603299c3c30e51f171ab0484a535d2d6ef9c9a71aeed49683a426c4a1aa657fdef56e00dd99011c7ff05b3eaa4d05e9190edfea509aaf0127350ca0756f4cb8dd752c17d275a525aeb3e90e765e4f67c1f8fc16b228c01f54217dd52fe9ba52f0c58f1b4f3b05abb0a1193cc8dca645d6f1fadb9fa7837a0fb351a060aeb45d8ec5469aa842428291eecf5c1a15a952a895268ec0b4aa8523367d659d2082a5e31ae832ebb0525eae1a130d99f12ab28a52b0091c2bdfe11b00e1ffa02c5a88ef1a2a9e1b6915894b9d1904" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "4a48629ea400ba9e2116fc183525f8496f06d8cfdc018ba54d2ee82e77a59402", - "proof": "d65672c1e42d44803bc6c743186a5b66f10df5bff6feb80b6ee3c4a326fcae19909c8bfdd8b6e41b3fbe1487fc7cc38f63aaabe59e6d39edb5eccaabea6e214f5c88eb30738879e28875aaa4f969e8ed6f4b3bb94c0ba3d3006dc144d92b0735428b6b0fc42ddf9f647f98b385de7f7f43b9c1eaaebb66b0fa882efe6e11897ff9c1e7800e54f016b1c33b3516db3f5b7ffd4564287ae9a9dc340308e2a9f00a498eca860d377bb71776f34cc336f623edb5b4754df7122b76c4045252d33705b0298c0fb149ac62f89d829bf9dceb36a8444939a9bf6c45fa9c2174e98a060adc35612e28109428ded6556ee18e636794389a344dedd9aa8647cef3e6a9e56f185d2e1e72d04a33788a5f3995542199506e69ca962f025b1d955c4d6213f623a6d211f6c1b30db5b9b0298a0c77f14b5a045e539a951c6eb440a67e4ebaae42163899582624683937876d318b6ce7211e09d93b4db24f4a3d344c244ad1104c52b2f10ea12761c487fc51b5724c6f3dabf441216d48e309c8063c4b11d41f1f60bfc80113dc9d764c9374da3549d4087bed2f36b40df7890bf76ebf73837e1a4826989e75c03545971ac4227f21c4f6a16a6a212c98a3c7a1056fedaec34e3bb02988f0a4e38cb197a9ff6811d47e3ae7b36498a47338f5e867ad1606e1e05cd09cd56b446dea8d06f936cf2073223a4ca44806c267f7ce4c1e6cf54e1631413e573bdd8d2e3044f779b1582b13d706aadd88bc2e3e49d48da520e2dfa3e61adedfc333b0209f26dd69b77a0e44551d3b15a1e6ac98540e052bc5eb98435d175e8f2e94dd12fb72dab78af01abc26e54c6a3a1c5f613f462eb11f096b86a16661628a8a134c69ebea31397bd28ad4e59e38423203b56ceb433ccfcb36b231010155bc17318c9b4f05211ecc6db0fb454e43868080f9cedb7223bf26bf5b390a" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "4e98bff77dffabc8ffbe6347fa75ad37f17637c56ad6bc5f1c2b2ad678ab585f", - "proof": "628c8b33785661d8e97ae52e96d322966d68e0cd34acc3c39fa2626bc76b87149e6f895cec069245febf76078375141de65c54b52f3ca53c7660d8021589386726ed1fb0ce32c5172948002cc92152b15e0a072b99887bec0409bff6873e7b48f29ebdfebdfca66af7dc923828a0855285bfb258a05cbd85caa5955998530a463c73f28ec3fb3724a44496679ac8a8a177ee146557f79df4756012fc66dd450f71c91c6a762d9848ec902f3355e17ececced20dfd5aef00d88b0928d7ff6950b1c36e19ec5423dc78d62816bc0afc32608496f22fd7015bbe07722eebb8a7d04ec014f8fa509ee7a657bb29741f868835fe1fba63bf97bf01d2963782c85067aa6660b58d854f40a73873618d0ceb6a0744af6d565f8fe2a75d857f99458691c8cf069eecc5150570fd099754e78d8baddad105d51058c92780aaf6efa4793323836858bf1aa94e50b7d2963b293a2acec4d6a03081428a2016df8b9ce5c8378165c3af19b193d4a942d23b033fc1a7757865f315177f86ff357653e7bde3126d804c4805315192e8269bf0467be89a98579d998945c2bf93f5d9d047163c9030c888d8ca90f60041d6930cb9a37cd6158f27a18f5d046397cf02cd8518c151f5896c516143d3ecd7cf41606468f909d16c950c2254b7f5018259b3d8b5703454c6a10b60b8d4c3b04ed73a312f19010ec65da861980a8388ac1b7d76a73b213b8da2de67a4ef2f5fe4b4d368b4f4155a2c181d1178538aaa68faf78beada22f2ce9e9ab70ea683be7a3ff3d737c5b44f3879baf2737d9ebafa7b54f80e4be4c2e3dcd6fe31b73244aa0609aee93bef773abf2a9fe1f17eb420edfcc8a930c2b8f4cdb317730493a7eb966f4f7c491b7292de4ebf8c97f68a39e5dca21fa750da3f3bc71f8412369446fcb0d927cceac7490fec227e0100eceb05ac78e991e0d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "663756f12444261636c700b3c00e7ee95e35884f3cfac1a6ae1fd6032a8add58", - "proof": "548219c332319e9c0704a373e5cf7d0128b3fcffd6bcad943b9fa0cf248043275cd2df49d6a572197688787526a4a93d6fbc2d20cd95c77c0da3ad252d06de0a1c555a7d4e7e522eaccdfc5356dc6b4d89c61923584076be8fa78dd751b33767d897434903c75fbce2d121168193abce446107390e2f7a6a53395ded8d86fa5dff59e72361338916b45f401c19f3e671f8c7835d55a9229dc6bcabef33a7e80a06ab47656b4372a84cabc4f27779a5a1b482dbb26e85ffd854ed31264133ea0a73e8c1b4f6d0653ceccff3b7895b3484d6364233d44416a638b6ba135a5b4502327a3c0779524144d0501431a5a184348820db89a543b1ae1e015d61a9c6cc57380fb2b4dcec9b2e791056ac62e0a2e8494edaed77b8f6cf548c3b0ba7e0d428e438f5a75d9e4668c9d09b5a440a84e76b5a01b93b30d318ddb92a2d5c05e152466ba7b78176973b5b6130e1943def15e0b1813497b26a6338bc33d8857b461c4ce79940eecc86f9aac49d5de88b33afa88eff93a6592ae23f01b401ed6d9133b2094a168ebb5169b061fdd26b149598242eb2128e89476e9a80a257935a60305486ebf3f11499a234b4095db910888993c5ab41e8bf85c87d81cdffe177d516c8e372538dc561828c429e1b89fc792c4e235fec73b5ef3fc98c16189ead4a6ce681a0487ed9a252c909b6d4ba9ca9bbadedc143e725a45691761350dddc7d46d0fa4ccedea07efd4bfa2920d631bed510da6360ea51def2dce7610ed660fa22ce95ffa0c6a528416b24b883119703c4ac8806425fa8a70c28259427788da33a8a6a0af4874e5c13d29d054bd8f96a850afa612e573625007b107b77a74dd42ef4c70473abeb24a108f3ece242f934f6c8fdfa3c594525d40b9fcb515dfb9d0063ebfbe3ee683ec79afe1227b7ae363325c9a91f7f8fc0170f14d1f20f435200" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "741a7cd6263d15d1e95c430699c2234fb53c3b3771b7ae4752d7e09b17b4b235", - "proof": "e6d3ad65f57dbaa41629bbb44bf01b9383fc6898f41f74da80d7d66c69bd64029c52e1028d6565d954245999ff6cf5dfc76a24569fce0d8e2dbdd9938ec74108624d3298f0f61b7eaec9b600fed0196f709d9eedc273bddd406f40a93bd56b563ebddfed5331c2ef76c556f50e01b167b5e618810ff0d70c9c2276591a2ee83865c6cb7909df7b303922e8a2ceadc22cd491aa0adf0a2c0768890d8bacba1d0f9a2af6cff98048298f0c61d19844a46cf0a2436950b1f7d05714f0f7e013c7005758c3716103678ecb2a20d106612e1ee6e69b490e6350847aeb6cb280ac780d9a07d4ad6782912b59b0d7b8ee165f41a166c607b31187a583f0482851ebe75bb022b480c2eb35657a7e462ef16bc4b33f54c829ae855e2c460f0bcca7bb97559e0b08ac8ea5953372d034100c39fb3ce98019a0175f4cd8a74fed3c7edf521d3acfe2b360feda7e04ce65bf159330a449cad13fc1e593fcedda54a9a0ea0001ca862a2848b7a37fe86c581023734c088930b79d9731e884d4145728a682914474b76e2510dd6c94beb5955f607fe66334a4c3b696c1513e1876655d75b64d2af05711554f86639940f15669e721cba8aecfec3c5fed7f05218550459fe87a002e3a19a4ed2bdf8cdef764df66f3094c136718041301aae9c51f803a3115754e8a42442a9812d44971addc4993912879d50a92e1c625c4f34b07cc3e887ff46d2428dfc294cd174400b17a9cf591eb372738ff0d867aec040ecb5606aba6d4746ce5c88b407b14c36f6c5c46c583923454d655468365610121dcb76476bb3f305e4a4a47a52419de3ab4912552f4c9910d47ed2a5411dab6ea3fabd85e36bb59e3a7fab4ba91455173e34bae69040ea7526856decd8b2c4b928006a23f11f10855ac0846bc7b0392c8500130a7f1c1c99b180aa73b8a24a207a4c2e4d927e70b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "eea33f7f66f2d5d228184c7865f2072671a2a8388e10a30357435c24c4894b1d", - "proof": "288cf448930e3258eb42084b68964997520559f0ac46661c07eaba4a72720776b461f80027e06aaa8ef69ad4b1573769659caea81cb45f9c4d1f93293c994d336094cbefc7b5b44d101f192a0fe3768e3dab9ea9d74181238e59980a054f6e0e7e005e9fdc4ca3737d52410b23cdbc172498320fe90c0925a0437de790f70119d82ebc76d9cf4e3dfa147a836ec51f86a7bc9578f81445148a1d8225387cb60e72caeae2028a90c6880c17a6f568dddb89654ee7fb3b62eb0a3c4d2c268107097c1f17c6ced045bec68e7c0a0647f9837e17b0b0f95fba609d8229061dad3a0c363b3a5d1effab0b790f63a690edd8553c5e378a70a5962707579683b44ff42cdec8e85947592cbb41d4331f0aa646636387feb9532c7870c3e7b00f24a2c97d8ef6cd3105e4439a3eb286578a36cc94fb13552b30502cb18a65e410b7d1677fbe81b633549395a4d6b7cd6a8f25104392dff17cfb14d016e963c71e3712a23c722a76f93b75b840b0ca0228303e4166751f271453e2a87d5941350dfc48854a16d7a5614e8d382dce86edebe9bb59565c81f7cf05f48d1cbda8d76e90cc290ebe3da2fc7c40dcd1e3b406b0629ebb59430418b258545ca0f4b5dcbb6db1bd08c43c72283a4b4f134c154db056f562d86c8c484c6ca2445d3e9e4245d5b1c5023ed6353cf97fcb05df99df2e725b324dacda9e2ca8545a8c4e9fa5b008dcce11f04c306b422d68b3d0f0c6e89c4bb48636279d7399c692f9dd609b33e60f6626f693bb1c0f59d6bcff9cc4c83790efe3d83b9889883982f3b04b10512cecdf6aca31e48e84b1e4624a94b0dc36315cdd3ac867b15401226d3dea55c4ac96cd713b7703dd6780389dec2181f22ebc12070a401f32242a415de297fea61f845b00d748e2d63c755ec7ccec58eeb74a78f8891ee64e60956b88d0fe6aad39ea8704" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "f07589866df759234e481ae8c64d7428bfcfdfd34f3416b3345198bc52e5e70b", - "proof": "7636e0553d84b29e8f0705c843ab9a75e4b0fedbe1f97242bcdb9aa9acdff1124a2642ef47dc3490ca7187994b27806b328bcec7121619d5bd7d07c533ab96161e7c2692f59b79aed3a1c0af90649a4a711e7e4b1ffeb8da4801dc23d060f76180a3389927835a9c4aa44e338d4383d7cc30a9b49543e5b91bd85023403db80d1f22fef81155a48861e79911f6da9c5eca958532661a69a2e2d2b2165a5bae0575cdd4cf404a3a395b1db3c8be87c355a23cc164f2d2d6dcc5468530f5a0df0b90c0651bbd04882459ef41612d09fdf05267343f2c3d1b05ccc3e2c7e7e8610ce6fc5e4fca81e8f19a66362600d72ec9494ab7fa7da1c317494992407b155d5d3443f35ad91c9e1f16cb10d063be1bdaa71a9de8d9dceee62c15050211ef574224341c3834923993802581115f8a7851f5758c272870237f5f928897e802c25b34b500cd2487ea06934c42195a8e650f3291e8c6b945e5baa663fd618e0d463c4c0add4eb547a6f04b1ead2384aed0dbf52c4dc398dd83c1ec4c94572d680e02024715200855c36ff58b9f30abb1fb255077c68e3be1e6da7ffadecedf95c46482fd121c44ba0979eaaeca5178d6cfbeab590bc17840dd9b09b640d11a27a930ba9ca3902e398a90ec0b60db754a5ec8fa3e6af73384abfc3fd885b1ca98f130b069b65af744f0d093e218a0e73512558ca9d18d32f928cda31ab4eee2fb1417d4a91eb8beb995fa2b5480761b1cdaa54fd738a851f482928b34f93f624abd6f3e95ccbf439e28d9dedd177c952058e859987eb83bb9e0f2af6a891b8bb596595a318eee1e01927e83298a3517c0a07174631c510b2ca836e397120ed852a13692c55b5403ef1223e70ae7b19f2c5cbeb35e47791094134a1c0b0ecbc78a270c49bdb57d909a2ccfbe4bb450cc24883656423b47384fcffe9775539333e6510c" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 18 - }, - "commitment": "3480e58d181643d932004bf4722f4f8ecca015b2ab1239b521655a49b225344a", - "proof": "b445e205c6c494f15d3c7edd7e832f3c4144c77ecacb43ba81313a4c002b35185ecd92cb4715386851308516b57f7d0d17661673627156c8da669f7ba9dc9f1d8414d5ac30561cabcfc92bad137a9b297e160486b0e982fd5cfab300c81417515ecd1099cb70c1ee8ae7397805695ccd218f590392f146e2f6d63633a39e472e56486b16261c97da7dd775a49b6e0940e122cbb9dce66abf45fce9925648220aedbfc5fc23f331cec986c599b151a14897602f0c4c4a8ab01fab51d72384850cdfc9221f08228c40fbf42c577b02325561ee5a8c3ead9361471e5db1b389b00d40b02b784621809da67ac101e6bd529cd9dc94ed65c1d5b3e3ee128c16b673184ccf3f161bdc348fc85e3f463f5b48fd3be2d79e49445b48f6c20fcf25e80e32001e3f2152563a9114e10254ae0ab1ae1bd96a53c6a850d615e0908d9b4c321b16e95cd4d834787876fa6763d08f183421318cef1fe47f3f7022196d2674da50a69feaad4caf28d6d7039c3ad0f81f2fbde1e9d93b219afa9482a8fa07ca79311a6d3254642ce326c1cf8b80cc7155152c9df33b130b15c433a397dcee04a53bf46064533c22acff505eb4e9f1e7cf88af94c76c13565342613ec54aab3bf23dde4069bc7d10a141dc4ca4048d4d5e59999767ecba4f256bcec993c3df10453e08eb8a2eb25fa81fb7bcecdfc12f6b8e8b7ddf757685649bfed7a628fd30aa33f600f6709c967d3d3a381dacb0a583f1111aa5231a4d2e9030b53d3d8a033e6c5659a54fbb7f25ebdeb7b1f71e278263403e59615e8a76a9239dcf445aba6467ce9813c94b8907123b17daf0286097d5e9a1a4b1c8303f4b1651b78b909ed36752af4b696078b2c2b1619002d7038790aa213ca3c38d496a9b96b5d549dfe3017c2c625f8013d1dec28b8198ad645c83f48602c7ca5f1bea05d55b2da6314606" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "7830f933909bf94137416a6d7d9adf08dbaa52a76bb3a83b94a8517a13e1cf59", - "excess_sig": { - "public_nonce": "88fac490335e448d27b4d4f8f09e6a97dc9a53c38d5d371fc3b36a52292f8d6c", - "signature": "0d9a8776f5b479f22b76e6ba78d3ea5222103b31a4dac2e6d91498dded72e907" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "7aac7ff8d0eb039c2827c48a1e054d95880be25e1a714c3daa197140070cce03", - "excess_sig": { - "public_nonce": "202e72faca784ce8cf16a284c8e9795ce482e5772ede7de53e6ebb0ef26ec71f", - "signature": "3fff83fc8995d7ba276d1f3e9d1b83c2c847de64b4edb844b334298af0904f0e" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "9af1419d1150a419fbc9185fe6d8b67e3ed7aca375ead9edccf8b0d18b9cba74", - "excess_sig": { - "public_nonce": "f66b133f8eed065f382d495089c9c015b7c1f7ccc3db083d114af1b7e7f3417c", - "signature": "a8d737e8eaef8435a92c8cce98f7889733c2b2e2845e4fbe248ad3a7d30c1f04" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "d68edf40a23f299feecae75defb5666cefa7316a6270da453b3a328119364850", - "excess_sig": { - "public_nonce": "285675614ea6dd211f65396e29f04f12db6ae3e1a2ac985bf979cd6c39a14771", - "signature": "c46893e152180b37e48489a3556dc59b37d6ef2eb009274cb7120c472355e709" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "de62e93506f42417c0df9a92ded57d6f9d0e98af42b2b72d35cbb4cd4a8e9a50", - "excess_sig": { - "public_nonce": "d831d2a530c0156cc75ef58412cd12fb1877eee23f25ab408572f233275b4159", - "signature": "8e96f840eba6b544092a61b2f819cac6d8366005214f05cb238e80f3f4e05904" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "ccc964ce5d9635c0046262e9d5639f7402f64469a07d5165139085e8c5089673", - "excess_sig": { - "public_nonce": "08070a9ec7cebaf9d1645141a55055215454f8779eabba754ed706bd2a417512", - "signature": "31a2d27d4570fd67c3144aa8ae9f80f1f84ae456c2970cdec1a1682ae5270c0d" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 18, - "prev_hash": "b4750871c973ffd34556da68cbc8c905f304b307f3d193fa862ac44de861b126", - "timestamp": "2000-01-01T01:19:01Z", - "output_mr": "09fc8894d15c239322913a45e0765d366c3627d797388543c601f6be5c6620bf", - "range_proof_mr": "791fbde81c4d77ea6b8d4ac34c0915c00f0c03a45081a01b38e40af0d1c65241", - "kernel_mr": "30c8ddb59391c11e5937ac7b07e95c5ba4d054e071c0c7baf6650d48902d6975", - "total_kernel_offset": "b58028119de5c0b6189453482204586947f564a9679083c03d484883b79f0807", - "pow": { - "work": 18 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0a18c7c98fdadcccbffcbfd919f619e71c8a344f0b4b237d032433da47a4e86c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "1043fbb90156152c8f56513ca230ca06150a19d6e7bf696855acca7ac8337248" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "12377053a059a00c08de8000654addc8aec8d880805350dd4af1c5a898d2a262" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "4e98bff77dffabc8ffbe6347fa75ad37f17637c56ad6bc5f1c2b2ad678ab585f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "f07589866df759234e481ae8c64d7428bfcfdfd34f3416b3345198bc52e5e70b" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0e35b288a15dcc18f2b3026ac1e922dcab3976c8f91e1b7096fb0107fe32b972", - "proof": "f25f1ca81a4594684ea07333c347cf250c466f6e3c6a8132e6a875faa65f416226d3544bbb4f5e967b9457824b1f2e01194914fb9669f9b82db90dadfda66657686e8e7f361b39f3e35f2df0491cbef396da6fb0426a0d7d2b613ed5309e7a00fa22a3efde273a58db6eb899d98a2018094d88e49e988e2df7fb8e59aa13687ecade3b436e0d05721786fa1460d5ed89aea718a7c57aff09f0a6ea7529ff5001b0cd7c0fcdda91588308baa27af1377b6c679d769098083728b8c92536e1a401ff657004a2c261f70d5ed91d4f57f498df1f75d3857b39654976b3d06e2d690902e4b0a7f7a4570ce9175793fe27048aba7e0bf979370e4a43b03bf4e877cc21400a7172419984056f2f781bf5c74d209788769b0bb267694da38929f1285a4b746074ccd840d1599c5eb8fc0d46c3a2df0ae6de66b18b3dde2ba5d3de1b4f27c41c8503fe7d6f524f254fcb44cf8f950cf7ceb7ba5f90b9277eee22d49c7703e6a68990134ba64c3b040473b426c982189592697ab125c77dba56643852ce474a035cec2f79ba6177b629768688ea34b338b733a8a150edd5228b6c8b6e5e0f1e63df37977772b393dfeec19a17f773381ec6a8f4bb1eb41a78c3bc309833695cff129cf413b17e54a48a1721afd5f9fc0929f9c4fdf34620f4d4dc65a7ec4f48757d52591c92f291503c5c2f1e4fa148c6fc3dd13074d08b1ab4eddc064e0ceabc7a89ea1b38bf75fda137be8504970944c4804fc25cbcb6030e1a00818011ae7b238e1a3a37e388bbea09a6b0f9cec641974d365a2063c69a375c7218944a1013e680463c364fe542f5cfc1d650553246b1ff4822c81975098cfa39b33051ba415823b13c29fb7da1a2c805075262018c995f912a03226403df0580129f0e03bb0da6529f927b05602cf188c42f7b965b0d20d5090c589382bcb21d0c0400" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "385acf45d71770f8bb73c097ee81cbf9798501fd119d71e09359b149473b823f", - "proof": "b4d72abc4526a7501a9805f1923a3655b8b3b3773b67ec153503b2097a98fb7ef87d820c1c6729b0a643f03bea0eccb55f8d24e1fab3e6507c6411967aa85c46982fce771cda9bdba14080a235a45f03982b52646bb1679d734c905b8a5ea67df84f716de1c78a1d8152446f2d36071a350aff67053647d05a041d7dc9985e39adf77e205ff0949e25f146d0b84ee8086cfa72659af37a09f769a80b61f9e10e74a782161fc7eb015513894147b9c7c441701e03c260508146cc9d4cf26ac901492963b56f67e237715c109093330f789abf87fdd0d692e31ad3820a8b2775028ae4fc099aaa0034fd0c3ef27bcfca2af87fe850b47a8a4c33c8d9fcb902e00d528f94089f56a406d3a6c7048beb1eb0e88cd45be1417c2864570687ad82d74bf280f88498f130498ed5d348c39d360abc62a69b88aa6129617e27ca0cae5e6f8c86a35992441701315f5b2486201fd81ae35c9a85c06fbaf4f24f28cb11fa7988c5b96d1bf52671571c3b202450986b4c5702912b295c12570c5a12985d5d315e5a79eb5365cbc4933e852d26cc691d01f8158aec88f0e07da81627f577a823f60fd95de93b0efd84f9f0603d483bf70657fb9cb0d19940bafbe8abf52dc12612bdc819ad5222966f0564e0e6092ed88c690ed156dbd3e1cd905f40a83a6e35cc93ecf89bc6deea2041cdd737c543a4805a2341f42ae3a6bc0d54042927bd5b480974a03263cd1a810347586d5ac1364564e99aff45024c5507fdbf1c05dc3dd40a316b7840357114d4e683505b7a37f6115bbb7cf396df325e2ecc80c9a96f8859cc07c9dbb6ae5a06e883fa52fa03941e824261fac461ee55ba5ad953ed17f7aad2855257e3519f567b79cf7bcac652cf20060682c25f0890e3404a120707e3ab445952f6b80baef3e80170918444c783941bdfa91d04fa00a00972fdb300" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "3e0fda1c1f5b3cd254f3c48d414c57ae42362ffc902a79a32f6eebfdb0882256", - "proof": "68bae9fa512be5bb4991c5e1ee2afd8e9e23dd0714142e1e9226feae6acb6f5e62cfc5a91ec9cde05cf916ee9a8d68f6ef129680cb0f2737e1e5ffb2ed49386d767532654a994c597c03cc0607e4420a33cfd41b317fe96bb47a644acb19162aeab01d576e20cb2c0547469b9eb0402303fc2f6fd7b7fe6e522b0c56b164823f99ea6939ba2dc11825f6b68e40111c8c79345727747763ab6da52915c20de204521b129d5c94bb3115d0aa52b9d050abb3a0ba902b589103a2c133a24c64c50e55c4f5e8e0efce2e2caa0544757d84e74b26324623761fe908c416980d73810880a629fa0435f6a5f532e0fa8abd92dce7619dae3edc907fc9753204f49e5d7aa263319b68e05e655bc11dd89fdaed1acc49c385ae9e4ffbd6ea4a28d5ac2f5cb8493bda3fc959da74f637e05df27fdc3d1ca1a32b979eacba14a47a8b1de238de59ea83a805132c514c9059e9bd56c3ff3f79d524f766c45d6d270fbe5188035ea63fc87b0ba1b2da56efd41f777331d481275d4d3701e5da86712528881a087e00851686766095a1368f32da6708ff6c11fd54f1f92887678749790da22c0cfc09bdd7bdb5a8e56da9e8ee32c31e9f633d4ce0883d58ad8731b66440c09e4d0e3eb871ac66cb91977a5f1ca788b609322a925a9aae53de2c418fd688a8ef7f6cadeb26a1a4f476e0d0544b3d766febcf78a956469a6a0a13b3ebce39442359669f143c316f09535a1bda5ce2938d6cfffd92f33fcdb663e78ac55b98816204e86d4a3629924f7252a59c24c729c8d0f6899a2dd0e037e7035fde66f2bbcd7d7c087e3272c8da1a78d0586d943eca3ed5228ca695e772447ab01aa48e2c177f9be3ba0209cb47762e353e4b3bd0376feb58ac88dace0ffa1c1c7459fe3e4b04ed3d33191b44a41d2caeef39471d110173ccb8a16ca85a8e3f1d3a79a4436002" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "824df6a2ee378c11677fc31b92c2e25d195b1dfda9fb0cedc15bddbcb6358d5b", - "proof": "8efb770f2384620fc62dc0f92aeac2ef47740f1c80cc263b188d65d3207b772bc000b3017f0519210d16e8fb7590e90f740f36b8db4592e429c953936c5c2c34c440370cfdc1ee7fbf45d1dc7d376e0e565d97e5a850abd83573a0cafdba1d77b2b055889c316e0efce554eea0c9ca52e5395192de44d4d787c7239a020e9c2f6d65345f1ccb32e9ab62d5fee88f23edf9e47b9bd057488447b68fe67875e6020878192369f5a32f9c582022af7e5e891f6912ab870d61effad6094f55a5d003aba753cb31cff775224c625158edfe287b03ea8300037e7015c46fcc49b23308e2b63f888a9ce8ad292801650083b330e6106546620f4897d7c5de127ef24660843c1f69482b25993a68fd9533eb60759e0dacce9c162a71b048063e7d1a5754d6f8900452bb5e8b009a3ded130e1dfe4abf7711d4933ee50daba91d120ad534e8be320d950e309ba7a961a1f89f6ec3be315947bfcde88b00b88549f994001e0aff4a1bfc446f56c2a18b0c7a9d64fd6d5d1c1c81b194e8a2110bc46bfc1a4a4292a7c713de704796c113df936958b56e26b51099907b9861b36e09474124518035e8493ccc16e36d67e823deacaa10228c1e7a8ebe2fe2aff2be266456582b66f8f3a94d73c827b2102c33f781e6f9125858f56d5ab29b4275a6500c5606375c0b27594ae08316bf143b077f5093a98da162571f54046de1171c74f8cfd462ae9534ba9d0001b52e56c3f35c7d74ad3c55ef1463564ee684ddfc4a51b3b224ec56b28eda15ea2f07eefcb884b6562cbf0c204de39b345b2cc8347032bc456cde62e275cd5d645294b11eb2d61f0aeb94c37e405d234b39acb2f441841dad351706c8ecb895cf4a6c99ba3269146d6ea423e9475627cd528e80df42b75d42035f7ce29b4d2e9471fa66c37ad91c1c3f8b1a347885d615a8596b51b03a8bc600" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "8ab9aac580fd2a26ec0642328b2e83ad943f9a9baebf146639edf7b66ea06c54", - "proof": "069d3eafa6413a7660304b157b5b45704f70ed90593546bba228985bb626bf132edec0d63c76bb37a8acfcbbb88fb6d8d3f08bea3c1393613bb6dc49e555bb656efe3dc395b88a0ea8dd61819a3892eaae64a2d51901d41ef12680af302bec484616c078965f5592f312aa95ef3d8281c5cc7835386e4155fddcf32a168c634b80ce973874d910af35305d7ace11f65bcdaf6d951e7b8ed1fde8ecfd8cbf000c0544836d1aa791570a0f5df21ced0999322a3038f44095aca1dab6bf6e71430f186ccb882bd79911aba59e37503d0990ae32c00889e2abab8e1dae181989bc082a2d82ededf3f624c367fc12ffe0371cf1212c0155690df4317ad289822ff133ee15da555f58b677072157d0e638963e5e3624c68fb2deaceec63f8490db3d1c34b8ea52a445e7a9409068e869d66e44eed627b52f4a69a713fec0c70cfe5c221808437dba52f09b173476f79b0896d4f58e80b22ba848d1b2ecfb1f86a52c2980f8abab1c708684692f05cfbd0a3c69bc34bcd895ffa9d3c6bf63dfe590a678f8bebc5295adcc70d405f8f082b53de73add7de61f79fa767aadd683cb6fc6472e616eaf77cd7a949b6965f43cf4c20c4ad730fb567a578c787952f7dc4b4e03ec301518e5f2294b780ce6082de36026bdec7c9c709cd1ddfba7eafcd36f7737d6d7ae36eae20c0f6ebedf741893cc6a20af374b0cf34281b463fb1b8c6b4423ec461e5b1b6e14bd9f1c44d716cec56933d6f684099f7056fa24db7d08c8085f32004602f09e38444f8d4ba0c670c9dfbf1a8165011b96de289af8670b690e3ac455ca0458b270f2e2842b55f3e783fa5c377ebc2e47974edf69bb0e5993dd6869d295d89497c32dfe060c9a66a20f3a1f374cf8a6e84ad518466c71f158bb0a4656cc8e793722875f439856f7ce459512989b6ca435bfe5b8fba39c2fa82b00" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "9440f3f8cecfd82703d08ec39c95b9503797d07465233e156815c5707592681b", - "proof": "0e68d91c4cd5b9ceda79e5e71baf85d2d9d7c7ec491ec8f5b84f21d8392f1f6900f17f3bae44d11b1dfcef6f4a2afa5cf02e99ead7539bfd5931b5f1522d5c5272c7acdc707a026e82edecf3ca32b7a101ab3f372b8997a1ced918e75614cf4f042aea1520aae0b48ce6f045708df364b19779f1afdc20b71be033060b49d3210bd802632876021b367ecefd52dc69f911308c0c1176451a482d5d4fe79576069af442e6354e57f143cb65da5207213fb060101d65f8cf84667723158f3ed0028e05ea9a1ec7f66ea949582fa2b2e21c280e7b59ea2bb2b25d2f995b63a41d0604d6af164e655e295d36710c8ea7a30eca6727b84725c1dc58c8abb781ec1c7a00c47f12827d826930ee8dba414d474c6fb65d992657b2a610cd074be3d97f158c2fbd064fede2103cce6255bf3cee7eb05208908f633bfd9dd8f5fc204c987cc29dc94068fd3974f170b5d44977839bceb73bfd1d48219feb7c0b61817e797ee274b143edf77b0acb1f363b9dc1ee1ab01ca5d986509a91a121574e8b594b7e463075f65f346c26112531a8cf58967316a4143e9a70f1910ae7a6c1e6f0ac2e0e29eba9a6fa2393376f94e382fdaa192ccac0edee0d861f028007b4c08d2026760273020d6991d93faea06f02c358de4dbd29bb201707d474ce9c9f28250c0ad8f1d09e9ef9dc0248fadd8bf5364794b20973aeeb286f01cc8f225f783b4e50c89f6b72927da908593b8e5da3529fbda7bbb5c35308306f3fbc453627a70a337293b19809c2e1352843847115db5ce8a73240335dd0112d829408392c41b64028aca93e84c3fe87e787daa0bd16dcf79ba15e537e3f2b88fbc03f388668ca4b2e0ed667315514a6e48ed1b1a9eb4571163340945ba5f4edd435391f3248a90076705d9e34c2f601bbd3bda2d54ae30f0c9afdf207b8d21884d7bcd1fc599f0a" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "bccc2c14e510159a5ff8f58f9e33fb35cbe6e3501ad69abc5aef7386334c5054", - "proof": "64b7b78c4ef4029a81ce43e62e8da1ffbbd8947990b36cb2b375cc1982179e76404c748708f2bfe54b1f1d164087e26426496cea9703fb0c847e2fdc9f22fc75b25fdb99791ec706487d3b6801d3ccd6649f17aa88285f50be91be290bd98832b6f47a720676aba156656b709d4b472c7fbd593858a5f178e228dd7aae6c1b35a0692d20fab62330c58b02d625d93280c596a366bdb5e03814f34cea0cb62f0a7be0683ad179ef732eda74c64956875eed9d20f10839d5790cb2038614cd2108abc0a91347111b47a30b88b689f89d4ed5c254d449eb0ee7b90b59ac9929780cf840349d1d181f6f716c7a8549032361db66c7e93c28528dcf89275cf88fca455494c5dd5b253bd8af74dbe3a022bd420764bb3054852042e2bb2b4fefcccf383e4c9a159a4660b3e1affd5f142b1ad6a65857c8753325d737ab4af20ce8884240903238527284f3e7460cdacc6c4f923237c49646c92b5228ddf1f862331203f04d6d8c99a2179c5caa35aa164f19d035d56e5c82e88a14ae6f100640ad6702c6bd281079069e853eba45ca836dde3bf733130bcb448f6b522fb9a22052f85e36daedd9cc4d1ce1622596923c305cab0d0e9ba29f924221dd500698e11872236659053f9b28306737b573ab6f0a278b97dab639ea730f4226cff74524f08a2d6800eecf2cea6f137d4fdd1892aec5835313f4c12a29a34e42791dcd3a030579621e1a9121d46529e05f4bc0c28c322d67b619af445f345695dcc47cb2c59a191424d73e08efaf8a07ba5d1256ab26b3c2b630b4cae8bb1d191b04d355a6433b5e4c205d8a1bd79605f8dfecec19d54961db8562310eb9ee48388fd357884b327d15e4b43c84e32a97196fbdb4645694d3b0643eda49b6c7787e1c3384a20903bc6108d272b1e3f10f07cb9dccca5a0987627640bc9eb5c8079aad288c3b0c03" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "e28243f2f7f203098d6bf480ebf1fee4bfa6fb5a5e7d7bdecd0618a0a48ef21f", - "proof": "9a67f8c37d3a09b6f371ad53c57232d72ef5d086405751b0e84194e31cb34d4b18db4c8276bb526115404399e8bee1b0f8258ecedd63dab2838fc04b329aa935685e3929b1e9ef4e9c3915f41ff135992b083ce0f1ec9053506d105b156aef7e6adb70119100ec388e46527017f45637fcf376a11cce228daf765e8c28a2612f481fa6978e080d466bd84b4a32bea8232bd53fa9a1944cb4bfb6b5c774e6c10df4cd94f0b283c529522ec15c783f03d3035616a88188532a8034eda7475eb80b75222714c714f45f2eaff6a368b8d4bb0b11f0dea7bb0bf9cdb37e6acd04a3049470f8a25d839d1105c5a402c8fd9db47f6ed204878580f7aaff06249f2da64390985d1bc3fa8066966f46bc2457ac4d694d9def8b7ece48305bae192fb8696280bb7e781e5a7e1779c0fb987a16171573cbbdbbd1747bfa131e842a7eb588370ed62daff6a5548b75d1647cdca3b38747e8b59a94c160afa68744203cc85b0f0a3ae6abc7e8ee3fd4519ad2187837186ccf05c4ac9c6b08237e2c98badb8a7f5616fbb738b63a1965bb68e937544387abb7fdd7c2eabfad1f79b817d29e85433258e45f6823545ffe6f3d61e09f7621366d0459e9e808fd4346b029239f816f40c995f2ae77a86d954d41a1b75982f06d41179afa93cc2fac2b9939d418464f2e800d252e23b0e839a21f7c98792cda6ad579d746e9ae35453904796637305c7cebf70fc762f15f1fe3d7691cb3ba2f024e541dd446e0a007450fc0238f9835ccbe2874489fcb7c282cd0a13b22f2fc92498439a40fa014376d2f70fa08883396899149653c425181fa60e3f6b6835b993499e348c2197d9f68ecaf8cd622765e5ae379ad78b48010772de5906c516ea68779d888834c0fbbc829323f537808c02493a689a2313ed6b8750697c6fc98a9947ff9c55cce3744e424b109e1050f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "f22797b6b285f479fb620499d87265d4ef80283eb3ae719eebfca59bf697ac3a", - "proof": "da8110198f98ee80943f7176335ad4fafe3824f321f0053bf56a3598a92a6b468033ea766f44b32ff938c51039c5fd47610c9b927c51b4377e60a54321b55d13d6754cfde6a541aa25becfbb09c9910cebad3d23d55eb49e953592a5b72682455483f952511393a100d1190a99269f51011178ed3488228ebcf408646e88114e6bef3d44bdbb07c97ad1b12948e2834d109b85cfc56647706fac9b4d758a27035b719f139519d5c7a6bf57548ebd53170b5a55822af6f8492ef8c4d6aa3659018d701ed7159999cb955985543c9e4565ff90bc8fa0b02410d65ea449a9883200ac7f26904aaa498cb94ae217ef30887013916a1d4fb5cecfddfce48cc42a047d8ae77705cf7ed20bf03e498b41b94cfc26a8f12932de58eaac3ace5c0c67084c1a13c2eb3f5c9960d3d029c7b63db580a9e76d7b936e514f62aa02d0f889560f282d7f836849921a5fa595b7e013aaa344677c2b5a975e859ff31586a658d468b84014af950299076b762f63064b3090fdf2d1576130b1c5978671062cb8487f6214af303161b91ebb5cca309d6d1183be3afbfec759f9afdd81eb55b7d5e05fa2049edc055c5fabdc0edd358df56bb1cc8614b82635472d347fad5aa48af919885f9c4784e814f42687a317c3af3c959778b7e76d4092cff6856f40f725fa41f4f0bcee7ffef82c2314da258b37841a2c98ba331f225ee3cc022e8c95573c65aa5db471892f7e3e3494deb9b7b92a4acbc1254275b21fc2055df99f2c532d3498917b52507396097d860d7ac7b714610fcaa1b337b92418b52b74ede43c7a2d34495b20eb21bbea3ec5a7d3660da88d907ca56a9cba69942fb01762b1c58c2180e06d414806b7b9b86215ff0ec47bdb482966c76f1cc829f3432fb233e15a06010be55afbae24d4b464596ea96b68b03cc260b45d7d92441a46aa1854444703" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "fa4335acdab316e258302b3ad5e63edf24db6d365b7e02a9f1422e6f1b474328", - "proof": "fe83ea62b687a70876198b901ca294c6910b33c355b8605a040432832503d60ece0089b505c3afe7c452a399131e092616cce68bcf901c34b6d4969b9f28cb193c0c096130b98dc37fcca328b820a0a014cb2ae6f0eac9d068939fa52228243cd06ce31597410300e787481bd23ad2003b7013893a4c51127a0011c86aa31a59992988d65dedf63cddfe0238d666634b1c44385029f6fa16c79e1a1653d511049e89d67f4e12065f6e446c5b0cdeb7d9dbd5275327e252cfd39b310e15b25a070e832e15e36e485a8cd96cd5b83c561224ec29e71bd619660b3921cde6f68f08206133ffb8b8d31ca8b9ebcb5c1980dff4f82a9eb859a7c67b20b75b5e152c2f3e5b92c19fc601bc66887b5ce136c8a5155d0b65a8ef76234240f4bfbecc77370606040020475a0275b3391912c247540633f22a31a08696e3bcfa9962c3fb4820f320a065f384a8267e716ff622dbdbf165a9c170618a924f18192b51dd5434e8159609c74b6f21aea24925381c280ad8049841cd097ccaa1fcaf45ab27ad1e02efee8a810a725e81758bba78f07e356ea0d8100aed5b1f770f2d6e2b423c62c218ad7a0710d5295aece365ccdcb9da23115c193ef8607accf84f23f9df4577a8ab7cab06dca657c3c12f437bb62de967d6e015e564b73a7107462cdcab2d1dfecff231c3793a8a47690ccf1b7ec2acd37884273b144c857619a87599f4a912101153a96222e92a3969a49b5d379fdcc8a6a83e0b5aa034bf961cd00034060f8a2603031b23732f8766b4eb53a4eb5f57cdb5ce23464e81241086307404e40db47d07a7be182680a4596053bbe5f362d6e45be402ed7b5d0704759fb0ef3707875d02c46b7241ac4279a3882be22c25ed1ed1d758772374f0856907754b7d05d01ad979cd87d29964f69c046733aac1c4c901dac2506dcd8c54766b17424e02" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 19 - }, - "commitment": "68b067c55997156efef9d3f223669d5a4e922511f261b41abf4e3f99d9a0322c", - "proof": "840da093c0ce2dd0e02a54aa71759da9addf3f8b8fa8ced1b8eaa6f2ff5c192784cfc7738a6c3a8bd826b4d16fec1ac410eb50da06c743dbee3d37f79b614108bc6abe8b7d5890d8447faea05af2247f34ce1fd2144e28cba8952afb9beb120a36fed527e8dab00cfdf18031e1a7fa4d48f0ac2891e65459d6be29a36eb3e77a0257362cdfcd704e7e188611d7fb0bdf97ae4858240334cf57e43a7700a08f0c7b7f1f41af22849071e9f91a422e3e06010fa0db9343ddf712fd3185a9e1c6019142eacec33e01ba9669f794996c743f36e76ce19d9235ca719817d1845541053cd01c8acf57f888eb562959239a11949ed406d3323341da0bc4383b61735829d2ec14d66f73ffd2785b68681615db7c469b66576cde298cc46ff05b0f68744c9a5e26cf9df7c9ef5d0c6d6d7692fedfd7ef1b5e3b2126d8eb8e076a095a9a6556a629f736f3cd1565d7c4b2faf3dfab89c9ef2c66fc8a83101137d88166373f381b664324df0c69c5de4ff8e66de92a6cb7abf10e2fb60fe8d186ecd900352a2ab1bc4f7d27fcbe14937926dc9c859379fbdba63601385a234d10ff8ec60e3e2c6c205d8e83bc06f33877f4811611fdf5210e79b37e3d97b4518be44c4866058a765c9809b66fc1d42bf710a8d380612c71757faf40e8e778e6170cb00b6e56263729feb0d6fd711b026f078f4f181e8b82ad39dbc878a91e27abff0d5f8d48ca3522d3d49f0e9dec9e1c208a83759df7f4baf3c6f974c364f3b45332ce55358413b7e05cdbed4e24e7c916cf378a9add3aeb7486251d56ef1d3798798248071e3518f8b3fde81eb0fa88559e2e7c7c15162e7f32fa342079e8fae642f8911a9d4c9090d132fefe48174b89c667cd5182f65f2960413c094e9a2c2cfec0f701d6b14f162542676dc2b59a8d0658751f7cbe8f1a635e2abf1ecb745d5263a006" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "52835c37239a22b3edf6d46dccffe993d1fe2946e04494edc0014786346beb7a", - "excess_sig": { - "public_nonce": "76b238a1b0068c74c0181e1e6efbdf3c9a45f0db00934d01df28671864a66122", - "signature": "6fd44de6e9e87ec9ed8fcb4652d6d416059272a6d44dd42b5a1caabf6e7daa0d" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "5a85574c538df9890fa1a6985bed07ccefce6b5a7adf37124dd95fd393277931", - "excess_sig": { - "public_nonce": "18b6513421f28a45438dab33362c227a2418670cb3b0af87eabc8deed2c6df07", - "signature": "d86f3e18f265e943879fcd03525b3d63c134a8eb286ac3d421a8c772a713b608" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "681b20d0920f8876e11f20a9cbe0099b057ad30db30c4ccf2c241ba727525477", - "excess_sig": { - "public_nonce": "ce7ad9ec8afb23d3a31e373cb2c398c5910b7cc3e61ff5c1f33eda92dc138a6e", - "signature": "2769db482fa087c2e8c38e92af57b4ae0844d1dc5cd922a61acaf33b3c56ce06" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "e2acba5bc573b519ff3b614651780b4ba66b2e0a82f9e311afc09143866d0f05", - "excess_sig": { - "public_nonce": "58321214cfce151e24354557ff974e8a30785c998012c4aa45212cd748b1d159", - "signature": "b3c357ffd73a43a958bf38882a73b25876658425e4f505fe36c505c7a50f9202" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "e65c1e284a72d8372614bfd5e49061d587aa44c025f2fdb3424e321c8c81ce48", - "excess_sig": { - "public_nonce": "aa88d37fa65e879682a7ef36ec9128313cc5ed629aa8db8685382cc96289c501", - "signature": "177f18779a90973fee6021f2386180b1f95514f6998c28e126268ada86bac703" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "d85d5fac0f6ee005126b081c57046a084da8213b4055c89a9da591ec40424306", - "excess_sig": { - "public_nonce": "18eaa70ca04a9d60ba1c91776800feaed6125e3abc912d14138ab01db3fb4b49", - "signature": "39cdf6d71f6e19092cdac22283ba43a31c6ab3eb8db56a5689d3dae7987cfb03" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 19, - "prev_hash": "fd793451fbd14b11aca5d3745519018d086a3ca7913fed12368212b4ba8f5f4e", - "timestamp": "2000-01-01T01:20:01Z", - "output_mr": "7b82d02a76b54f06a6f45fda0045d34c5962ded69c68510f039ec21cafb98bfd", - "range_proof_mr": "767a32d7e7dc14d2fdf66208fe13d103d3137e436f1f4726cb60cd16880e2aa6", - "kernel_mr": "9c32fe4819f62108370bb80e343897ff640b3686628601e357600115c9ce5f5f", - "total_kernel_offset": "b0e761038061ec9ae5e5483890c76133fbf7caba361213f0223fb4932c0c5801", - "pow": { - "work": 19 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "3e0fda1c1f5b3cd254f3c48d414c57ae42362ffc902a79a32f6eebfdb0882256" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "bccc2c14e510159a5ff8f58f9e33fb35cbe6e3501ad69abc5aef7386334c5054" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "e28243f2f7f203098d6bf480ebf1fee4bfa6fb5a5e7d7bdecd0618a0a48ef21f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "f22797b6b285f479fb620499d87265d4ef80283eb3ae719eebfca59bf697ac3a" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 10 - }, - "commitment": "9ef8501388da66549aff0bb1cae35fe8a4ecfc3c79595726fe96dc21017e6a75" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "06d1f7c3717b89e4f1492afe682aa5363d2d65bd566b467a99822312dcf13a5b", - "proof": "72cd4f546cb5f06b8b3073d22e4c985e666e099df3478bcbe64f06b46ae098158c782aaecb6c90c4dc58fcd7931678529528fea9b9230bb26f7b45f8ea7e580d5430336cada4b67e79b39c6b6caad9d31e8394cf0f68912cb4be2753abd88c631aa8e7e36c533e7a49b838955d2218c5681b809fa96f4439c85e132c8ac62167c204a684f8ab70d0e26600663fd3cb8948cac967ad5117b10df6cb46213d04069898317f6f468bae9a2befc836e64afbec8d1232c6b44ffd740998d7dbfb4e0923906b979ca567c10d03ce80274138a200d18d098fdf654d9b168bf638c1c9005ec2000cb9966e2c756a9323aef77f034c7b775a76064c3c6651c19499c7490acad0bafe9d37741af7f3703aca63d3d05d5ae18a289d096c3f3f28308d2ae646c29891e7527e05223f9ed0ffdb340f18843eef066db5a79590ebf9eda114a70c7eb23965b2006474d15164267e34cf45b092b0fceaa026f9baa2bbae65285250e0ad407005aeb7a3077548aa15387e927e187fce172d740867f83c192b5f3e102ce9d58e1ee76a00a302db7eb3fcc36a84ffc7069114405c575f9e3ae96f7415f4d7ee2a0af518c639a1873469c14af6474d70372ae7e16debcd24e2fad5e45638fb1165128e0c28223372a73bf7515f1a5f938c495f70034515e6b0a9da4c6c5a337a541f5779d7bf9dd6f087ece8f99d4c1e8f1a1c56269f366a922b621922da4815c30f3e4cfd455efe4d87cfd47d63170d7da9206bb296ef95e54f9e802dfc9ca65acb37a65ac41ca75cc5b407515cf23c3fc1dbcd462f6ed2e73883df67047180ede3d43f2417679b8b588aaf5f2948f81202f33b2bbf5ce9e02259f16d6d2b249f579b37896eb18bc5a545d19e5879f85acfc8b672a6365af7828383087fb1f8d7d0ea1436b367f59633dd30dfa08b6e9f1496797ddd95ed030d37a103" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "08c699e1369b582e67b00208131fd8d557cef52561dd6de858f076334722ad14", - "proof": "023e002d813ae728d8b85554e0c0062ccda490b349c732e3aaa354670c84fb2ffe886534f85f75de1da5ee99a1c8c02729eb04a7faf9ac3a7b5fdc75b744ad3eb4ebe8b28eb217bff450ec0d02e9ae9430a981fd1cc6435dc40e28d0c12c81554a6d491d03c3f980ad5a4064860e1be1f61a92e047045317cfc6c5b5a372a800e3cdb630d0c031713b8b0bc70f1c40ec477d75a1038d33893d17312393541308e47c09b8d14b74ec280106c7e7bd68533354ab72752c163b2952a7d02da05909d3ab1b2f5c0a485b3e6829f12c69d294d0bb9b85d7d568110985c0dbb891d60bd4fe44a82b6d4b5f4d0566771fd5529eec654bd401e43a7a4cb8817cbb690a32eac3d292c542e30803bed44d9c51f6a24fe1fb2431dbd2af3622a11e48be367b14e6d826e2e2eaf115595f4dbaf0dd33483ec004a6c189fc490ffbb012b40d6d18c24a5c715720baf943621f5e779eac29f31fa02bd364b17d6af40c3d48561936aa36f6acd0a7feb65737ccfe26bd4b116cd29a59fef7511113366b7ed6e63c508449650c7854c61283b9dca2a30c79ae3868d1741ff208cc7c77a10e07073ff8f483ceddbedf175657f17eb9f5ba9cd3db7e8079bfa50823d5f005760b4a49e2d6029d052c6ad35a0c1571a7f2945fa73a37edd609595c65467738b48cc7065a75245e3e77a885f8d40c57ee278ad8c015efcf3fb0ecbf06798c4c0adef437f631af667cc8e277bd405af4a05b761c189050ece726e52aa809560960ea405b963695a051e95cc2f6ca99b2574e1a17d40ae52bfb37b3bc62bfcbe5f05c762594c1a33542f266b28715efe9b6ea3f712cc5aebcf99cb48fc5b528ee6d24a22aab36140b7fcdfb5a0090256253d6780dd36a11d129dca2aa3c4b4a1d7c5e9701cbb12b044d64d3b7a006729a2f2e5357fb2472fcd77c065e765973ab4427070f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0cefa777eed5c35973bcf0ad5aa7bb66c72e8bc324389b6f930c22bcc061ac0a", - "proof": "2e9eb026d5410c1fe52b357f12dcd7783d8f08c5647ab6c8a8ceaa730c948823e20da4714117acaa9b3dc400b0eb84671b006d61ee8a2277d3e76b11f9772935b828607c1bab80645ce5d626f01854191e475a80973ec667d2bb89ff29751905366fd11f686b9657ebbf3a122928a12f46f75d33eb8f384873f5358b34e7940267544e425216722cc3d566533d91c8b14d2abcaf3d1913c5fd2507e91e1c8a0db5baf32001a0ac02b205dc4e6a8325267eaa6049545cfd3b9aab4c92af1ab502c5a8cbce0694b119df58addb1299704f02850370845c044d91ce09a762de9307a6fc821dfd973e989b3e8d61902855344fb3102a9f13e879d3d4795f6c82655fc04c298d20d6de0c0c3a849dacbaca32f7bb366888dc493af7dfaa0f1c81eb6efc46c283345f2dfe3fe7f98ad5d660dd087bb6ff18068dffdc691bb2b70fab17129d787869e2d46d3ab6d073adf38e6fd7c61a62de75b59fb645d5d95ff5796a865d4456e5b6f8333cfd3e0c040f43d1fc11cf0599f70f4fa079cdfc59e0935ab049299cad0aa1993f4066e4af9868d012d0ce6b0d17d9af697d44696834395ee0d9015ab0c28f8dd5cea988296e1ae48f472164819c481951b8d8592609e655962fdb01b0fbe6123d2218c18917dbbf60ba564d665bcbf00036ae2c72a1465c0ad9026bd145a4f592c61a4fe46686a9deb405f1bfb35bba472a4c8b6b66142038b12930cbc53106b0ad424cba4b73e09edcfdf45c8dfcbc2521e3918104572a46a449ff400111eca4f0f4584d387862a045bd7a4baed2009042574c1d89ab78f86d1ad55e0a24321624eaa5f8ade987d9d8adfd768570b1d4160a873c74406b91788188e6c238d43c37974fd1a47a7313168c49b0ec29059bdddd89a6529209e7435ffd6cc2ceaec9c67927100f6df0cb9983005618af414e5f57120ff86208" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "1a923c45c02f1d27784da4683bd476574ac6ef77110e8cfdb508fed9f69bef55", - "proof": "16eb14ff7b163d52fefedace9fa727610c9cee6261be65c3cfc5a2e13c09e5467af8aae91f04efa5c728b2a96c22070f5b155574a20a0761c47f7061f5c1fd687850a3e34755cae5cd3a0d8dae1107abbe17f03bbdcbe622d67ad2af24be542cdc3a76dc04c1e9aff9aaf09f2028c835e8f43828b3f0493ed311f5a26c5d05690d7c34d7c51eeb35062d26890d54eb3aa607c90bdaae5acf86e7b7fb67268d075501eca05001796c910230ac0cd917a6ee8a3b0280d8aaa83a7f133fec464704caa44b29a75437e6c9a393a14845187febc5e4920c5e14924b168f800bf5950c926c27ae1a8e51ef485d06267caf69747e0e99abb479f6214736753618b31146405a0735e5943c2df940d428e54610a6002f00512ce193cd0026c0055bc4322318865a3845d76f38c3eb724f6015de48b76b3aa00fd57b2b5aab9c06a03edb7b0e637eb1ceed6b64b95943a46bb0f3a12bed33108f2658845548651a54aac4088a657160e655704f1067927d218b67336de22a1970fee8098f248a1c6db2c51fe08e249142c6cfd4e478b289aedf73ae4cf5b7dc65a000cb776dfcc145b0ce644e0c1893515512c30ab5deca3eb9d335abbfd9fb3fa45438184fe457c1279c2ea6627a9c0b111f9d6cced100f881561d2cb315d99fe83de0cd4b7286b3b26776b8220077f3580bc17208483eaf5e6bde9a3c5508030b21b278cb867da9859d57301147e6ffdda6a5a61fb2077a13a55fa2e3efc32a46c33a14378dca40101c1adc28bdede3bc56d370ba178da0b3c9b6693af9acebb70d03fb82be938f2e803aa23cf46981391ac9fb738a597e29d861249a7b81adcb4b736955abc25c0ffb2df5902c22cf5ff3e9bada909a1bbc33470e480c38a20b27661b2ab4cd35265e0fc3f970a878f3572c83a007eddb3cba43d3c6ddd24c907aba0d1e4ca6649d3205" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "385e7f90a3b257d2d6718aa4d8da5be7d25ec0422884ddcc7c1f2fb8110d8166", - "proof": "1cf508629a8a765e39e66353eaaec1ae9aa4accdaf5288706f75589f9279eb26709fd09f497cb3f29b6ffd8aa7d6ffee17fb3a37f83f4950375511dc7a38fa5eaaebe8a09acd6b7ee74ec22666bfabe781d1cdedadc943d35ed2a5d17d96bc1a28624e00a30b0614f12063c7efd44d89e1eb28d45113f82d02ce25125f1100744dba89823da2a75f05758514fdfc6334f72ce38463e4e04ba85f412f858f3d087f09750611abae94ac9ddcfee2d5180a9135e7b64bf3003de836c3328beff706efb00389a90e2e311799924277df88160448345f9cea9a1b2d53816a18ec5607fce9e9ddcf6b32b44e73a1cc9ed5e6e436e1036af235d2de1269f38986e8d50bdce019cf81e67aa2e091872dd3898e0f0320d598eaf8d35038530784071c392f3ed7f13e62edc09f14fb0b679f2f4d1c4caf144506970d9f6796467fc77419489a58d14e5dd959b7e3ffc365996ace53e23186bc0b1f7c414115b84570e4573bfaae078264452b5be902d51a26df994ce8b70f09a74ee54758404311edd9db7daeb7fd9d944ce1f3a07b6cb310170e5b2fca88f8620e766d2227fcf13fc38f39f4ceedb47f65bdaebb26eed0a07af712622436503b1fd66b7e8a3ca1a81abf7ae48475effc83396189ec636e6d2c5fb7aef30d9b986062e07f7bd8a68063a66a36adc1f713fd43dc82bc8b9394904f1793787d03b2f543f99c78b391496cab563c968ff78d8058441db3001563da8e716a1228de511a029cc2cf241580860f70e8e7f60cb23f502c95497c820b076f2474e17238a9bc9528ad418baebc5c705024c16a5fde1e649ae0ff8e072ec39f4f2a3e71679a4a2986afa65b305ddc4f1f99282404acd94f32052ff124b16783aefb287af1e43bc53c61e89b139d53b90cb15f6c48d64cb345f1f6a86e549c56ae4ee84ddf226f60a47258d2561944c906" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "8aa4ceac310a89becae665c96fcebb77cc8648abc013e1aef0e1483b4a37d342", - "proof": "94635dca1729ff6b9cf6fe7706458daaf8e09aff9516f9604e17cb2f9054cc46347fb8b22a3cbb0f214c627abaa07d7a9433a66668478c3b79bd7b4c4e53001dee06004059dfc2a8c210e3893f5ea5960bdcd0450bc2e5251a0c5187b33afe2648b5acc5c36d37c58dac9fd7df2ce94127f0b5af4498e15adfb37053d5dace7336a122813f2bf9b1e92c3245aac573fb1814a562ae95bb2f526ebf9f2270fd08e60547aa7416e4d63c7959ad0e29e301c01d7816049fe7e9119c3adcad99e10e68cb3a95f1d674d888f24ca6594779a456ee0dd5f65ed835106d9e583f0d8902a84cd8845a5d94f8fa5dc0c0d337db74a7d03bcdf966fb2bacb846c9035b776a2806f07670f4bba56b5331721bb038e142b46280c75d45dc040ebafcb8a27a3d508291d9c65198cb96a115f6a27332b27c04767cabaefbc5dfeb6364d5fe4321eac35ff11f8acb07b96fcd750f5a51bd69879f22134f36d9f76f4557a4045642cc51cdf2ced309ac5578da5c735df8d63bf103032468ce23212f89f5a3de5563920df877afd1aee1e1bd83cb14a5c03f503e8be10515f86dfedc3705cbff79688050d366e55389f25f154969d5edb1aca885c220ce47c67c05ea983cd7aa08590063cdfe1c47c37dde11db7ddcf5298333704619e6164df8336dfdeed861687a401d52e77f98d5c4965effbe715cf4fb55cda7472ae06b6badaf496787bc601fe4a701a8e0a6c710cda47d090facfc1001d8486d8134b2130491eb6562e9e6320a9b152b3d65490fd0bab951e48e4f2cba96f8278fe02a69d4fd5e500406e9227a24d727c2b5fc9049c15b9df518a77e09e40be63693327700286ae981fbf25fc6677a44f6511d37daa7ab716c51f07134919aa8ffd2bf38438149abefdd5d074f2df86fc85de80f6dbccf8d20f3c44642a90d07d303b92e9a9603f22d8d750f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "9465161c3bd0283710fb2c109343d5cca3ae02391f4d7a7dcce9b7838148ce7d", - "proof": "3aec88e6396cff6408ed5d7a265b7658dfcaafb0cb40f3e08d7de03e983a6d0a2eaf719377fe651f21ed5e0b14646c3f945aa09a863b2dab8178f97136067d774a87214bb085c392548ecd57327dfebbcec774c8a4f2222aa1a9a1db9ab0e834e0e8e896d56dfc2ace2f264f7db62ded821def3ae08a90e6f60c3435f4e3c338e562c343b8ea40e369a2ead70b8621f51be55b2a4439154a6c248612f46f760f4866833aacfbfa5e3179528c2acb0e57a1e7da1b52a80e0b6561a444aa77a2003538cd83db362007c50d4de7c23252abbab5d5db8e90cca3d215201f1f85200af01a81be9bfe0afef57275db31bad66bb6f65763e461f08d8f8ccccb82db720c0e52135dac82b66ba490a34fba3ff14d2c3bf156d53819231d68227ee671e35f80990b6cacb50524d0d83dc8d2dbfd7b353591b3e0135b51ae05723e300e88653c7acd9a80c7703d56d0e7b300fd269f1c6227ce52d91241ebc173fe149a630b0ca316b0e8188e73de9fe22230951d22c639a769b3f7249339c2d8a1d06fd164e63f14803b916c87a1cd4e7019111028c1538b953abab92b267e1b621db52525268b8d3f1e35f3e9904e164fb05eea2b1a0c93287ae85617c1e026baac8c373b6486de59140f4af383de97e35856e4462cb58af33b1fa6ca0c290d3f1b67541f9e1de823f277617e7c4d8fed510bbc3f781b1923ca18358a80be6af9650f9b176ec6de3c3a9980748b71616f75a652088eef81d651abaf0cd7efedc86cce81161ed4db220b4cf1314ef7d8eb0c7a784ef085fa763c3c0113d19a14c8b640501d624bbbe6df6ed59ecde873748cbd1b98021dcb4e3869afc365c7c325134cb41709975ee6cfe0eb7e792c017a6856c5ea40940d3fc22fdd6a9c3f3646e854eb0c12df8f10a3dbb413a87528af5811e4a1ab75971c9cc9ea1ac4b44e026355730e" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "be6becb9eaa4b043eacaf47495bf52f49e427292d49148d4c329c62bcfffbc22", - "proof": "50cf37c75248a4e8c6e6a481ac93f0daa6fcc9b22f1276ba796b3692cdf3d2250834a264fce0f655e320ff7bb45412e010d0890a5522231e24c7671164deda5e64c2e9d72aa7092a8f5bc0ad792a22f7173d1066399216fa069c69617cf0b7087e863305ee3c3df48999d8e581b564098d16789a8a9d2ff18447a96ce460353cc2b9e9901a0b6d943ef935202d328a54c79aeaa85b606b71088aaa0bf9f2850d78e248beb8df504834bc06d9bbd9afd287ec7255f368d405db48faf2e84ba904b41db967985aceffa822848b3079430f26125bd47308756b2d7c068eaf0a1d0750a9e9fa8becb6f503d4bcb92962870967aa6dc342d277338a32fc57a655382fc00e0e19a2f195464851ef7e6372fe476505c662ea1d52bfec3b433ebdd2b4549e1218d60d4ce95af940bf132e616571f06fa7e3ed4531a8636ca653c0543e5660b564e1b04e05a7488635d51078b7527220ef03b10e36506c8504e3192655617a01f457cdb54551d262026d8031f286598425d713d06a5546388b5badeef95166f8cb3ea47e6dda62bbbe58f1d1c38342eaccea3e6c2dcc172b380c939a29215ca23d835d18350b31e188d43347fbe4828b788685fc36db9340946ed6dc8a15f0796854c31eaac5f1c153c92b3baee00389dfa71028e8b2ff49b05299c87219c46da580911f705e66887f11d4b8f1f4da93e08469d1a292c4f204b95888ce77461bca05124475348ff6113a49730ed9c42dfa7594e43e587a0bd20a73ebd375ec91bc84ee1b3e75528873c4ce9a91003808f74a784e8cacbf13dceb567a3c096e688c7d6f5864cc78afab5ddb2ac936b1d75d12471f4d11406a9b103306bc3dfe98582d5c3d46088ffc765dc0926bdab4d336b48d7f4968ec370d43afeef108783ddfb74167589f29347417f088242c4a91fe7f2e3d5cd44d132a8ba116d103" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "cadd560f2c9f8afb7234814d7a91f022458a198dd407bf2a3f6b74a1c70f8415", - "proof": "c4f0363a06aacc3faba0c2369c2183958b0e42a131803f3bafab541bbd7aa27a8ecc90339ed6171290c60bfefc4613bf1ce9b308eae672db47c4e122cfda0010e2593a9327309c6eef3c932b7595148e48f3abbef21fa47010f6b49153f0252d6632178b0e9769286e5a3c3edd5276fc7eadbda97106bfdae9e3041424ec9149c4ac9911e4a01de859988fc3fa9c9c41c2fdde334a4b5a95392e2060380a220b241d5c6425f23cf56de8cff1ac3d6e6a754cca38969f6fe2fce62250ef5e480cfe153f07fce85e8733dbfa04295b4c50b201a62fe51d1c5650632e941f4391043c8d645018b2fe59c1ec33c2c9954dfac4b0c64a303ecfcd770575194773596430dd1879d5ce888f8ae6850a2604daa74094a8caa51dd5798fa04ef5643eda23640ca99aaaffbbc355eb3153a067ce014ab2a11127400f32ca611651814d705d847ce8dcf5b373562ff08494edcf61e2a989e99ef595eeb42824b36447fee421e276398b39e7f8d2f56df44cde5e99eca1a19a0ec7e79f2005f67312ca1d1e52ea054e78da2d83f98a3665e11fb39ec6297143490c6bafdedbfa5a95a1eddd717e5122db77eaa387c43ab56da7d90ca93788ca8bb46c8f0a4828d84981d0e777202074fa1dc42e0f7ae4a467e4f4999728d97bd69f2548f6b3ff7d1ff8e7ae4fc6be00f727e1fc31609523164766ac0560e112f7726fee2e974c33711acb021034ca262aba2187b8226f13f1ecaa3f866fecc79e82dc7988f834a031d915727608a69d324b359a4e76b5ec8e0ba8b20ca37e919be9b0eb098fd0c962491a7b52c6180b90dcbf6eac9bba9bbbdcaacef093dbd20f39a46fed6b182b4d043bc339b35d1a91df836c3454a3fb01c14a43cf4adaa78b7caf8e9241f8da1fabe8af06df09b1df76c0f5cd8e6b5b0037ed07d05ce92dd7e541743709f6baed72141a0b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "d0c6397d4e9d1f2ff4ea2aecd7539c7f469fe582b762cef067df3ff321dd0810", - "proof": "50c64b0989e9ec9443573967cfad76cf7ab3c1cbe508a6d666cb73af79d6e74714d675139869d46ee83deb1fc31167398f851dbe0ba94c5a345c8f2ee2b35a6d5016b9914f83dddf9a2a838fc01423f702f4c5e824e204859ac405909783b12fd6332bcf63e5fb8d5ee5b2cc61af3776683e27a60a1f8bfc1dcf66d47dde8571d0cd1b5aa38ba177713918cad8c28eab7b5d3af3de8fff69054be6aec44ac004b85f481936cbc642a57ada22f41db399464a4bf7d17ecaa04f9ea10ea42f590e742226839a8199fa8275950a5357955ecfa8752ab4cfd26315d0bcbccff73c0ad039c54926b5600e6b3a7a3079208224c96c9be8c242d115221aa0abfb0b412230905c744d53432e05bd97942859c5d240e86010ea8442a2ff51ae256423d42c5046141fe48dbbc4d89cd8587057f145257d6c65d2fcf4e8a93e006b08a58d54c47df62f1a04200785496ca3ad6a43326f665d4dee14da11a38deea04016316d16335f0fed41c25104b99395d75df4a221a746d6f8496619c6c8d0cd97581c3050dd09d3712601008a557de1c23eba2ed969b7016427a6b409ef25cc89311a7b1e4e20f087b50c3496ba1135d1bfd8d98a22e878077913eb541d1a329bccfd7c8c0d4484bb027df60a5cf331924e1fcf020824cf360b75b5be2ad657b4d51f2de4d2b450459617755da17f325d995f54efa11750c8f102f216fe813a86c69605f831b991ff3abbf0dcde6426410467d4ef624aecbec8058695a329e602b2cd194c1fe73b5972b7c8f9bee00dfff8759d1272547bce3fba54823d1c5db187b23d0624c0c303b64115ee534784e43283b47bf41e47e666b204ba67a07dd71f1d584d92512401ba97f0e452df9380318c26ee383c61c5f0fed3686f234c00bc7503d97b04500a38eed2b01fb6b1d8bd9b23166d380477dfa3d16b2a9bca48f6250f" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 20 - }, - "commitment": "eac6bbdc2c2be436e1df778ba20342b4366c81b5f1e450d199698bdc108aae1f", - "proof": "c23569dd65b3541e8681951f712c9e04df48e8985537ea8a4996817ec5e6c519d681cfadeb76625a31f496d0798ac523923e13515493716094fe6db19b327c6b720ffeb696d0f29cab44c7e0da176eae1e39dde709947b15197eba99dc3ad120ce21dc6f082a63552ba19e7a45974f2b14083319ddc56c348fdbfe3a8ab1841a8a251f25f601aeaa120700436bf22fd6e9f5538878a54f6a7d5fb12690c12d0584b707adbc6867afb752e7ea2d9e62cead9d9f54e31abe4329865c72cdeabe0ba16a7aafe2feb86e78d1d187989c88471829dd6ff3b85558668f003722154e03320ed2c799e10c7de59435213062aa6805caa1b7a94dc25709b4ba16275b01099ac8caf836f54eb205f370f7e88bf9536e2f9cae986ce2b2b6222cf7440e4a16b22f8040956e29aed7a3d8d98eb47e51850a2ba790e23db733c4a360cbe9710432bfe73d374163412b7b1bb6061735c7f845b0f5b61f7f8b7a73ac69e5472c06c6c07a7049e08e72c1d9e7f66c62e88b19242e872a77d0008105ba2c336b617186cca673b79d83764c931529c3dd201c4d32785efe7e50b6756059a784c992458e8ad5eb920ea99e8bad69c199eacc4c39d6a06da56af554136849251055a16dbaa0ecdff140caa30de851d2c36f56123a227e898dd80ef93187bde1bd9cd2258e4ea016568f7b354c2407d7a5a37f95034bda8a7db366e59fa04940e91d5d70f695983b45bcdd54f391522b4d4690fb572a419e3408088e25761d866560c92fb877d5aa3d447db24f62c5375ad32ee8cd988d0a6036612182e744a74b27973b548d9ad79dbedb45ac2deadd6d0426a9e5f23703a03ef6be6692cbe3bc4cb43f96cd1c9b46be37d0653c94aac724bf4340c7a97cea6b2eda286429f2119d000dd59cb35af32126d3555a2109c7b645c9ad6caa51bc563e024d9be4ada913db0c" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "141b9e531363b27cb30a98e32fd4ecb50b37ee309d98aa3f02329cf4de6eea46", - "excess_sig": { - "public_nonce": "26aba7fe9eefaa7af285922e5ab5c0f5472c068fd974e92f2df97a3022c1735a", - "signature": "fa7dec596b7e42c58d511265b3431854e2d630cac84ac135d4678c2c30809f0a" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "6218f7170779d7924bec75978bc53798ab2b4acc7b674964ce184cb32a4d3643", - "excess_sig": { - "public_nonce": "2872a2df6d8e8b716983829cd7511a6a772b0eb007c0128a6f552dc16680bc4f", - "signature": "2097c3b3efb35bd5747b465bc8c7afa8f140921c8b6cc2f73eb17610c588df07" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "a22fcbca03f1fb4dca2ccedb089c3eb9a87438c99081fe98c3930881bac7c550", - "excess_sig": { - "public_nonce": "6062c7e94e14c1f2f669107c6faf8570472859f24a24b22ec93ecca6a7cfba03", - "signature": "b1d84e0d26be69e0e801c8b92277f334067498d638d7669fd7581c4ba5ada508" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "b02b819b9930764efe54f2020e46fc74f5127b6a4e278cec4307512c38e4554c", - "excess_sig": { - "public_nonce": "bed8ffe7aea700c42a5d5f178b114d7b1897aace2bf6d3430e532f997160e61f", - "signature": "18a2ac053fa5514d681f8a2b5e4221f812d1497781e2549dc298cdc19a90810e" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "f0b52e5075360a76a5acd7cdc6e32e1ae9ae6b3d1db9087b0196612a1863da4d", - "excess_sig": { - "public_nonce": "c222faa81843e9fe67086d918e7e0a057b7a3b990a5a0150c6682c5fdaba8e3b", - "signature": "439bf822435a5865bcdff41a865da4d80a7178abce2aab346de4395628941e07" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "d81d28579003859a44726822f31aa39ffb18a8857ac51c963e81f69d7b944c11", - "excess_sig": { - "public_nonce": "56942abfb96f4186d7fd79eab93243a9b3cb374c8e8bbbf8c5a2d3dfe24c1b09", - "signature": "9e3f31f4dc5be7b61f61ab8927128fc748571134c742a92c3af1284c00ff9f0e" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 20, - "prev_hash": "06584a1bbea582d2c5763fc0c5040341d5f14569c3dfbc929802f61a83101925", - "timestamp": "2000-01-01T01:21:01Z", - "output_mr": "8c32b58c247f436b6bc617ccd9e3b78e9741a02043814850f03dd0e87e31c0f2", - "range_proof_mr": "5b9564c52fb43c0d833d94bb2c2f615f7d826745ab3f9d4b92cb037b124895e4", - "kernel_mr": "c9f32bd184f1842c6c27b6b4efd6eb0358b99d89b46b2fe8af5e062a5581e7e6", - "total_kernel_offset": "ef27870fe8ff8c83d2ba73248a0b3b9abcdbb6be4e878298c79fafad389ae90c", - "pow": { - "work": 20 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "08c699e1369b582e67b00208131fd8d557cef52561dd6de858f076334722ad14" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "1a923c45c02f1d27784da4683bd476574ac6ef77110e8cfdb508fed9f69bef55" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "9465161c3bd0283710fb2c109343d5cca3ae02391f4d7a7dcce9b7838148ce7d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "be6becb9eaa4b043eacaf47495bf52f49e427292d49148d4c329c62bcfffbc22" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "cadd560f2c9f8afb7234814d7a91f022458a198dd407bf2a3f6b74a1c70f8415" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "06f73c3a41c5b84cd2030aa6d88818346c937ab47aa978d5dba60263eac03f0f", - "proof": "96c7f496a4f4d58c249ca8b6363238272d46670ea2130106c869df2ea090da3a028c2062ef1259431fddc5ed3bd22e27337304b7ccc0b9320c7ae136c82ee412b087f5ac20da692745d8b9e6c2c52a69b887eb3592c3361459e8aee36215506b90fa93791b536e366be1c34a163de6cb44d5bf6a7fcfd95479dcc8f7bd7ca30fa881bb22fd758c629253437def48a49982091163a174c8b2dd66b3f654cae0004f39f295414192addb887cbbfe2116fec4a3b9e68954bc3d4cba38ecebf87702d3504c5b7d486e4f8dcb34e61e472e09ef8543ff1907d78e6fd6541a3970ed0162fe2711dfea51b6f00207d36c1cff1d28890c3a63a05934b53b6fc1dcc70e397463d13cf87b947f97f9bec522ec770596de3dbe492c5edff7d609d35af69e0b047271de13f99831bfe0ee9825d719b9d94a83a3d4d4ad086567b77fe8e15b23745e067869b52b35e7f2de37f6501fdaaacbe452b168a06d28857e490810f754988b64d29a6da9e4c9f41b2f3b4c1b677d322c7f10c302bb564b77a78300fb5b72f883ca7326130bafbae1b9b4c76024069308adfc7f55d57f075804169c7033446ed57ff2fe348c51164dac2081189ba6b97f031439a5f4981779d445b80667a8c22e88c1ff94ae6b20b0b8d2947931e7defdf2b495a1b0aef70a3b9629ff5dded35361317a01cc786d3e0445080244a60c56fd06a7a9997f740dac4fa83470587e1d1dd35e27fc6ac4e6e798c612315fc13877c15cddafb3aff95d7e2ed805524e61f2887fd733182bcfbfc3f8b32be7076596fbf384d1332dc86f554c5d405000906e6497ee5f15aec3e95e46fe419f47de00d15a9dfd25c0dc07054b025260361574dbae11f0b9e647eee35cd7bf9d1b8a8ed67196b379f1a0a64a47f6018b7dd3ada28c740122bcfa775a6f75efeca773c427bcb11831e9b08fe6f34d05" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0ac1766df93eb4da1a5fa9486f3c53f14fd0cb26fd853b55b0e9234228745f1e", - "proof": "5a0b16d6fc0cf11f8572bb3d0e8bb0869da208d75226cd2de00b045311885c1750a1655c7fa9f74bcf2041d238d8efbb23c90bf027499e0e27409be953a18d109a188d6a65c673fa29f09b5d62cc7c8a3c9c6087ed1876aebf5c09ba1edd583726c9f775142daf2110907023d9a415113c9ffbb67f6d574e7fd7c478d1c5765e24d21a7412522c81e1ce0e8e442df906de0bfcd0c1cc69fdd7fff55987e26d0997b85518ff069fcd7e331102fa3f1da3705e97a045743dee5ea2b9d12761bb086388bdb4e90e5d0ba3b56080736204d26d112ea88343a9c97296e3a5fb8d6b0bce978a31451d47508559fc4f66d6458db9a93ed4385de5df61c0ddb64501ca730cfa6fa15023a20546cb78abc4fd8e9b087fa087785f3df900459f0d276d5a7f5091d4becfd3c4f890740bc8227305b13ac087a845e6bc30c609c15023aa427c963dbe075911d837661d9b64007c3fcf8995b895ee514dd6e6398afa3fdba16e646bcd40c2e9a79128df76b3215261a158276a2cdc41a3f714a2e3fd5e438d14dea3e2b44076a975639995d03d0d9664f947ccc28f8313a2c3c4bdde1ba4f67034d0b43c27f89c3f231d22a2e36d4a90a04a268731b8403a32fa1b0723f0b4026461d99548cae265f143bc01083a8ebe2aa824bb77a99cda34ea61e024750d60b4579140440a0ce628d8d50ea35919dafdd0725db0f0df508e59b690b4300b05366e57792a226e938b4af6034ee10063cd981f9ec27dbe999d26e0691618031636e198d5f7579128b936d49a84bca6e02391891de5e382ea7f2a43a81cd7c009e43fe5c9ce2c5cf13f32c5d4fe3047d762e5dfd6d3d26e1dc6ae21602529c9776242bf5503c5006cf67b35b5f3c74b30fe7de5d93f0708f6e6bb04818d97540a9e01d91be2455820e3d899014bb71a7398e9ec01202b2e9b5f10de4fdd89b000" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0c660194fe16dce895abf8415a3b02dfd21bd332c93ee75c13f3e06f3e6c133a", - "proof": "c232254d71b77ddee9a3e2c0ec929cdc37ac50bcc61e708ed3551c14456037212e2bc9c6ae2d4a2e5f5c22e158b2d2b1efb9d2b5db339fae1abcaea2b127c56720b0d84f9a8986e9313c03cf1318933a2a59cf71bac0606d6276cc4f1870e26aa6ba592a7bfa3aa2f3a0958a585f181f3b2108168fa225e46371636dfcfd3c72826541d60f811d01b79c6006b994d8365767379930a9f6cbc0b79d7f008a960f5c522ed75511625dea71ea53550173627984e156d00c52b793cd0e08634726022bced2160e3fba9360f4ad507d4d7bbb9ae839251d0de29db7298e9380f8e203066c7309dcc7c87c90265090e98ee85551ad5b2658965b67bbda19e9a2440660de291fb95c948ea951e22ad58307c40e36bb33fb1ec40f702f181606c44a1e44b6a2ad39c0018454d5ad8fd3dc12916a7a9870039e4c0cb8a9b5ef200768406576e9c63e4b374be3a57ea21780f7bd8d0922c2a74b9ce8110887e7e01cc5f5720a555b1c8cf8981c8252a042e7e15109dd393ea6f5c75d4084ba0ff48e4f7a76f811a214cb21018dd8b2ded7309cb4dfccfef3090260ec52640321577b59b836ba5bb3bacbb353ad737faa9ab23cbbd2f6d58057c874ee58f86ffd16a8629e272e664ff17bf95f8fb49f8aaec897315085eee0ad6f46a62b1b7292988147d61252b19a36f66518e363c75a158a4cb09425651418756bfc272edd2117cc21da6a00a219e8b22508a68bc09fad5fdffbd7a4300bb41227d9702fd90f8d09d06b0d085bad6a6b10d7f85c903ad31a10dd37656fabca6f8a23d7fddc32cd5f372348666e74a48d07bf79edddac7b6ffa6f320daa3c9da60e6e0920e8fe17e2cae9673670fc1a5af0c79d3261159c6066a674496a0b0252ee69c4b856d6e3e6fbe709733a964c5050421c58ef634e8a724ee05e7f499ba0ca8d8c9f742bff0e5ff202" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "221b749d8ce8892c935d81fb9703964577914cfd1df14377a2b09a39afe4d458", - "proof": "ccc8196ed3cea3c3af7a4b6b4472639f5ec7cdc318c2bb5b816dd2391d1ac27d981aba51030716c68e3342c6f8b0969e263ad4b826b9d5636384042ff276f61d2ac9a72afb9f425efdb78c0433460ce8bf9752d4e82d0049aeecbc9c16c95a7c34b69bed299441c1042c79b8ef64e0855aad498f20e27c2d2bfbad7b088fd70efd360f8b648103da1006227974e969afa41fbfd131f6238fb310eb7382192609e4407444fb3c045c9700de3dee8e0f5b72f7502d43e50709692b0d9ea47bf802ba4f514017cebf036d438cd46566e2f4385cc422210feffd8d0d80f45e57850e643bf8c2267e6225dc9f343104b498e351bc1b6930b21e30aea675bc42c2e15d0eff6c2924f2f256d5081bd3a0f920d44f0e068925ab169a74d01f4d2abe592d00402c8be3c6f5bac6e18b68bd14758b281d2c09aa26add636e858c4b5f7dc0c0ab3ab4795a73f5a777600aaae0e99a2c3fcdea543b0352ef33572fa870bda0aec6c1919732cef7562d2af084074df77a2b9033df4e195f15dcf58956258e62b748dcc24602b20e8ede41506a5c9978a63b5efb6230173733f1d2beb8c5309753412c25b4372ce1d7acc919ef23d0434cf0f371f21f760cd8179b35887a0ef27c0e0341a8ae68162adfcc22850bf1ae6c045c11ef53c208150d5c886fc9d331946b2cb1c4ce9665729e63c53b3fdd1a1769cb8742c5857fad3f287efdbe28f4322ca916e9bc3a1d0a30804952dd3fdc0fbc16c1fcb75435267ebc3cdc1a3f8227c0598c838ce4558bc0a9b02acddf118c1fdbf84e4cdd69bda2c92c590876d2f32be1584b27291c658fd6f8b2b58afb9cc2c621b58b93e8670be81d3eb63d177d98a6f4d5b9d6e38d63136dc759f4b4aab239b205545c5e8b83a0bccdda5fa0499002d35b46d1f7e5ca152efbfdc2b053aadc64cc44b46e6981969e82e419a02" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "22a3fcccb5f07c0bb20b804414baa0263638dd748c333c1696742903073bd433", - "proof": "049f33a59fa12b4cff319ac0989102ac4a0343cfe2f1ec2e34b88b4509d7aa2bd06af00768d84c4bc02cad6e445e06411052c208e843ce7060d36a491330880ffe6cb4e4135a040a738975913de17a940efaee82dc15f2bffec4f02e7feb9577da72a33f15238ccf6e4b34f630057226d8951ba42020f01f13220c974935ce48331cbe4802bddc918e12587a462ae66fe285a6a5afe347cd76bc021cec17ed0d0e17d1e195342d49988765b97a7ff8618bf76e5a0c79a6757da8ed4c4bb2a00a83bf2eec17b006a8fb6d57dc699ff6ff76fd72a2d366b9e11d44402e1bbb9d03981a5ba4d20a8c765eff620bcaf2c3f6b7cbbd4f8b8dcd998e5f7c5afc431317a2b8e51a3617cea469a8a485979ed2dfc192c1e966056f6e0a61759322d11475d8598e643446afefdad7563a25dda807481fcae19b47f5908d2301c20a13304df4b6c45d3cfd118182302d8b4e93dedd0a628f65cabff628c8de61f144b62e5b161a0744d34465ded47379ae4d02eb41f7fd80bd28bb1e60fe05d94b1cc52e55eaea69e0d8b48734825a938e1874035d6ed17e68df1dc1e70f58295ac07f6770841492a4281952310fc76e40276fb6940630728c5cfd9334ce597516e9f40f7d9621ae3399e638cf754d8b1407c9ecd6968b0c89d4eceaf0e897a82df7ae8c328ef56c4b53c4b10b1c37cbb23e3890e9ace27c8a1063061d396cdf14a5cd381f901b22866c52051a0781dbc90113cb8d76f7178c30512c512ca869284ce39266da48520caebffa614d193a1c1bb5a9abe8d153ea920b858ce08b61ea92abe2452a3f96ce3871864f2a1391a3d02105ba898b060acfaa0c3a141c14aab3d86f49ec0fc13fb6d1053ba466119c6f71c313a7f5b001acaf6c840dad52d48378aa0685173c25d506104178c47170c2ed587ed0996d4c9c68d7e3324757e37c8cf404" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "30b81cd728cb90fde4ec57be8c84f418175cb447736fe531a4d0d83bbd945c6f", - "proof": "729bc61729bc64ae5e9ec86a4df40db6b96ddd8a2cb55226e77f4b5ff57e5b7284946bae13957d63f040a50a24b3a2624436240a71b617c0060b5febf91fe96f92b2d42337fddaee5b53e9878143df563d387daba2669c9cdb09f91f63e3af45cc23cf5d55c5104bbaafcf4919a2adabbf6f399bb637a4a9432a00d05fda4e57c4354ce5c2c44f939058041aab5b31cf387f5b170fd74854af7f1190ebc565035964ae9ce6feeb2aed3fc99f35b050634308f34cae5b7ea3f13ff6af8917ce08e9fc1a38ff3afbfddc5f063811464578cf73569b14738fb5d76ca98b07ad720cb80e141af41369cf2b9247d5805030eacb1e7730fb480b52e3f61603d1cb94571613c204459934936c9929ec906ca5790bdfc3681461441d0611c67e81ff6e6d702d9b85129101bf87d347b1af31ab8c4a17f65e27f35b59b8b26f6c45e91d2d52c85b9f41c291f07ee2ba97e102678de03b9380ae857020e404353f1fec2e0786994ea749c3cc5feb99ec374650bfc83ea8f31279e30775150446e93de98d0b22aef4331c9e69744b36850034fbbc175ab6dba154b4bcc4fd42a76710f1bd75ce17e5da672fc68addc32638a7f9ae862f17cc9fd62f9b653908368ac99dd325e808c2e284d07c84849233ab5d49b345aaaaf306584972fff9d3889786a3764b4835bc12d7b7fcb78d23b7e5a97006f4f9b15d9b625231b4ece1d2b47550b558004deeeda71899d67f4e36c60a74090247ca002293e710c0eae7b6b6cf62175cac71026a7cb2ddbe5cc468483ab3271ad19cce200da7ae5dc2d4474088a4b25802307de25e48a04e1b8c57594f2b991c3952e48f9fb1a2a1c43e2f824a6657101bdbe6ad977239c632a3fbbd1cc742484aa94ed7cf9c74a1eb14d0bc6e58140911c6bab60a672878129abc0f2dfc7411d4ed8542d17e13707bb7b9e145d7b605" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "54f5fc6ad9d827799c59d8dd4e4e56b7a1bb6506e69574ec6363c694dd67077f", - "proof": "b61d69ab3a50fa5dc97d8b6555c9401d8c32b9686e10272414c523e2a4ad23686c1580ab9598a49865a9f125ab0b667200f6633431e2c7a98de943a38a6e7f6bc410822b70f93b5c655f312a2cf19fb3eebc459e0cd5db713fe9953f3e5b20567026a17e11700f7f7880bf88271e98c3828e0a46af88c6ce5f2b2130bd5015395981dbffac59a3500497241bf4bd1a7a3cba75cd5f80ae49b9caa28f8279530299cc86707b86e0cfe8c80077e2a0e0cff5c8bbecdaf75616e62ad10a4d20a10be7f8b0b70949af86707f92eb52e0437d3b5519908ac0b8eb2a69f2554cea1706ca44be791c423733fd536daef293a39a2235fb9e85d7753d7f283115aea8064444c8dcdee889b28263d7617ae5706fbe6a4e5ba0b395baf1d0aaa96769d14b2538f38c0e133b084076e064d1e42cb6af482ae6ab9fb6877de83d3c5059770e2f34a43857d86cb5e5f49bd602fe9e105b90c6d9f13559835582d1c06fda508853f4dcd36a011fdf534b595609dd564761c8d59462820b5d5614a7e628e85a0a3402e084faa83de70e8489cb0104e0e81745281cfef205fd9e8fd3e73e853f8f19dcaab5035b061431948a81c98640b727847a176032a82e1012725c5683924b31904e58f39508e13be0f758927c454e93212b4293ebb22a03caf212c6ea828931d237f22246e41a061e6328fa5cee11d565b604d379edebc6819be14938d8d42ada5407c92fac15de5bdea796e58b6351764d9e20142d9c2df5b72bd26ad78146fc01b7467a9498d357d3987630b6ce2b718adf45216a1f7ebbbc2a3b54cff313f056b6fc897e8babb3e19ad4555295be1836c2197457123657884367c5aaf979979ac4de9bcfc04f22f875791b646caa85a39f99b23edd123816823f917a9c04ab7dcb8d33e720d6c0080cc8e311e2b43926cd9ee29d54e4ac93502bd729c70e" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "6a3a5a405321566ffc5e1a0ea628b7fd5e0baf7c483713b19456c5513619b417", - "proof": "2023ba9e6774a16d6099c5c7ca9fe3efc7d544bdcfa9e717d3b27843d7951221c09bf67db15e0feb1e8599bf24f2d6a159e0ee8c5df5148472a08fec02aea1148cc39db0c72cf9aa9ce8de9cafae04a4f864ecaf4f6a5e302f6106ceee4b434c9ced9d0eeab42405da378107cb787bbf39c4dd253caed014a5605d12b9afa233c528f8cfa422c5d68f4f9fa490de23fe4cc6a3d4d748fb910b3868ab67d0dc00ecf54e757ebf7b0212776aad7656160997208274118ce2d13e3ff550a3ab350b769eec7edfccf668099644caf5d841638d2f64d505269bc0e99748db042ca70ae2c7880d3a2e706349a610480cc6019e1f11d1dae0b87434598a0df79f3e974564c86ef06d8ead38395221220baf0c00855e89d3882ef7d66d4f356944ce37123cc198016cef916291da7b6a3bfd6ca70a8cf0e054345cfb44a67bedef5fe21ab6e76fb77ef2e8afb7d234c54852795ecdd602fffa612000ee282a8128bfd13d82716ef3034363ed04f83355976f3bad91d9e1d567ca6b3ca5fe185a70bb8d681e03d92cdbaacf9309dfb82a7b621cd072689ae8b3283688d5ed5165777de070beced1e2afb3a96c4d644121e97ffa391d5227b2649cdef9cb1c2195f18f805874e5fb51422aabd88d97e87249fce68c14e1cc559386b568d5f867e330dc157976012c519d688063b4c87c8eeed6ef7652da6fc82af7754fe698434c4399184f220ef365cd0366e247b7c358c8ac30d288fd82b55b5a99be4728a6fb9419174df8075c6eaa7dd5cbe4e7e4da5c40db85fdb1c431aaf346303b3329232bd95f4bb6b437e50a06fd930f17ec03f56ea1306a2f0b4b38fe53a214163272f4bd0a6dc3e53ccde885ac6d7973566d3e84682f7e73d9948f8c3480d46c0b5db176db0ee3adb622bd6ad482236894a74b9efd3962079f50fc3461015366ecfb3580cf08" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "94cc3a87ae21b9fb99e2668bae97d6d51c3f6b4d7d32b97d12108fafc3523320", - "proof": "64cc5914f5431ca1af12d554f0c0e4021038660a45256c56d16cb6a15d34607dba89df0f8241dd0da298d952eff0e0924f13b54e5e3a983f80804c18ee6bb61e587bfafe10d47d2cd8889876b719a157c707ddd0852d91fd719fa5d39154435110f7db0ebf11509c5e8960c84f30faa59fafddb1c2f91393e1f3fb952249882600f50871dc28645457125497678b17ccb18f633eb882d79d4d873765c49b67057fe6390f5728d06cd9ea3965ae6d2703e564ef8e133b8e0a5e4009ecd0d8f800ad33997b238cb33caf7c249290bd795ff4a9ad84cff55928ba4d9530b965e2035839a494f03c9d42e5fe6bdc2f73eeae0c556d75f49d404c0898ca1656e0e54ec6a240444b7e7393d86459ba5caa50e98eecf626f95610897aa3ca9bcab6176508838e3526d894c57bfc2d3ca01b1825186f469b0224763f8c5912558a7a0d1c4cbadfbf400693b799f2762c5cc9c3caf872e18cdcd2363e5d7c3b1b7d79993068e612438494210a9d22cf5f4565c82a8b5f1686c62cb8d48883d2ad789a4d0982b6d3a21e84a5a45f4f341ce80cd7ae8d68b92e218cd66201653cba1cccfb50eee7dcbf3df81269bb7af7a96306980657af1e7e8ff72e6c3dd3224162f0ae1d560049e1f8bb36028737e1faf4244137e195f9f4f371aa428c4add95f801801a4e0a95b8a4fd0784d6136fd8c62877704e785c508bda7ae7f62f5253afb34e66aa638fc90f4b663c5eb706cbe30de8a5ef45e2d9559b8e55d161c029056c3e36ee25d7155871e4d60087e1ff86936d39f0df2bcb350f4869c84b1424b3d130584e3cd81a4314e24c06877c644b6beab95882938b87882f78507616809ca8503a0abfa890a44d7a82d36872e7ccd4aa1fb7faa46b9f3c6c40181e061f105f6b08b002c2bb7071f3b32835a3117abbf2ea4c5170635983bb034cce3c0b9ff38000" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "fad50006bb82747fc1815dedf704670b4ffc38c8ab1c7c534bdbf979dfa3392f", - "proof": "7e50f3d0ecb4630bb7104c82feee401c87e567597184da30819190dd9aa8cc759ee6f6fb5bc87aadd7d274a471e8ef6ee6cadc27f1f117af0652fad1ee41633a7e42fdfde135f2e65e26600760f9d3abeaa6b38cb467ad2e86b1a119660a6a3bf475dea9e08dffb6357e476a6256376247f676f067349c6cae552fb06cca4b110544bdfb87de44cf865584a9cff7a235f84147a01b5b2611329d363a2e19dc05fe0d4cff235cbbe1cc131343529081eb988adf3f6e58f6ef2eb7a760f4b3da080b6f1b4541a9748c5f494cc5ea89c4f607b6e2febf43a18334fca3a19885390258fa7b5bf55fe29094a3840c8c1dbcd3c41b8367e359db16db5221f8f0ad2c4970a5cc60bf0102561fbb8bf384b158e163de5b797aef920f2207703b5d5d677050a3e68bbf58b03664188c9f8bdd132228e6c1f40eb9da515210501bd11ec82b2415f24ceed84816fe07a172d374d93bc735108f6f679c44d106a3a9a713066fdc17fb057e8958b4481c0c5ae7dd75448546d8884c10c6bfdef79c6ff27c49584c13a9f0cb4f5084dcf22b635691ef0ee76678ce702902fa26135a89db5a29599025c96c8eb06e2b05f2ff18b8f4a6095e63e3cf0bef894f2d2b25d4063f7f213e55fdc1e7712f870fbc446957560fadd33caa70b58d0e6259785d2519512b100407f56da1d458e86b4864a271c172ac772728d794c0e1ea15bb5785e59ecd523a2aec2176a99eab636f6eeccab6a815f2197af1e870a26342161f0704f47578309c2b0491a797523d863b334e370f9d9efe94b93b1c9ea0c9f678c30ecc5e5b4071dd83f31bf496c9e071d20b2c702bbdb555bbe5426c76caff80b66a8f8743e01b6480e80e6f676ec3305467fab27cd9134b5ff381aed6c4fd8638b30fd50cc4bcb605dee42c1c9d3fbb713b86c3f1fcef8ab43b1c4453a1e53b91316c9b0c" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 21 - }, - "commitment": "c0e32e53349cb25555485dc71b24fe3d6b5f146001ae7126c14e942944ed5b58", - "proof": "3c555a1abe15529d270bd9decdb513dbdbd6311a078f71aac5b228e19d7d0938c07579f843ecab384069e48739e8e8085c7a1a1ec5d5698534f787342004010a30ca77ebba2555abe9ba6d3cfe91c9463acfd9e84a924b30e68a25f1594f1657406816ccd21f6d6455a7602967ad547d1280b03971f73c0d954e82caae0b9f54c2b34a3c37005f9ece9c7edf8726a8c1a59ec2b5eba40e662851d3354cc1240e6a39ed124bad30fa315544bf31444b0366478a2430758fc7e380ae1490375e07fcb7bd26e9f3280ccae59395c4c30bacb7c853ef0f9ff30c4cb83cb025766107d6f29c9dbdb92c46f942a222c320334ab0a41fd7ddea24c19c328c430a0c90530ca0237b49f8952b5a0165397f5352affb54ce164196c33667006ab01a3e5a2b0e387613a9f2137f21e4e1a3b2f6ce16bc8af24c344267151e7435c9963dfc376a1b6e027a1550ad7460987d0f8cfc4aa5057f14a4480d9acec9f6ffb18cf3163ea9d540c4bd6e065187db9e2540b403adcf8a35968eef69676f2503bc295f04f2d66f2ac311ccd2e78f3592b650691b714a9c73f2c025efac22d6be2d37990a56cc6e53becf7384097680bf1972ebbdf550bbcc030a4478ff5e97a8ef2bce0942406526f3fceaf65e1bf0730ac9c23d0fc8d95d7ac5e47e94fe5f09332bc16136929395aff3afdb521b7bb3c1b304265345667f86b8f257a0b7ffa3c2cae344b6c52f3ecf8bb771a8403cacf73ba04fb27893720e807bcbf44386da94d64a532625ee5d9d8ef4008e3faddecbead4ba4e186adc036250833fb8e2c522419b6534e630806126a54a61dc2d617c45a8055f386d6191cfc9d3ffe518a0b522826bebdce518c3d2a95523f013e323dc3a6f5181680f1d5e41d3d67539203210370472b3ff29a45b5b2f17c2280bceba90b9d82ba6e877e5aff396c995a0f9427b09" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "32ad3912a8cc065976d112f7aae3ad118f04c0dba51fd1e057e862755ea3d35a", - "excess_sig": { - "public_nonce": "1a8da3d8acce88fc92d8ba1385a0ad3ce2953c32e8c28d9ab7545c54ccd81f36", - "signature": "1464f55ec25ed3ee521875b23d9d91ea1a309fb4d8617c1d70b81bf72404eb02" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "34e2b4b5009b0bef12731f18daa0002cf3d7743f962f03b2a8dd6d4e4915b74e", - "excess_sig": { - "public_nonce": "70488272e827c1f1a0b79ac83c066cfa67756d1f0ca809ecbe8bda6292fbbe05", - "signature": "5d23f041eb673bd7bdfec3722aa582bf0d4da8900461b61c3353244b8411c90e" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "484fce7ba38f9d13d19fbf49227fbbc1a63d7d12226684c0ad2bed1efac38112", - "excess_sig": { - "public_nonce": "e8790765e8fe942456fbede9ffb173f9183a7a7c67a37dd2c60158d155b00f31", - "signature": "39dd69ad9400a9fdfd60b5db9e4b9005db39bf39f8f77bb4f509aa4afaa59c04" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "86de422f7f8bc2a2b23464a45430f7582d66354e62f0a0a1658d21b7dd69273f", - "excess_sig": { - "public_nonce": "6cc0dddf451f88f1bbaf827bc6af73e35e67fd50dcd202c2c6a82259e316386b", - "signature": "63d899fcd9c163416aec1761a81279f1620463a8f2a4dacb2750a39b164a8008" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "bc83c127f2468558d52a8c6b72176a668b8fdf21eb26abbe1b92c21e6a42c835", - "excess_sig": { - "public_nonce": "d2dfec63c645c89d30b6c67b621f6de41636c01c57d0447d70714917265b5762", - "signature": "b030333caf98c776d86dda4c5f49b59f611653a78fdbdef26119e8f4a0290301" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "d0ef71578040683fde5e4efbc0da11371cf278de6fdf99af2f7b318ec892c45c", - "excess_sig": { - "public_nonce": "8a8a0dd8dcea8628de78a758b750484523a2662adbe3bc6a2e3957c86c611b08", - "signature": "98f1506502763af2af81aef51e2e8a5cbb047115a188df979543729d381f5700" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 21, - "prev_hash": "9fc3d08973ac34155c28a03ff5bd6ecefa76c9862095325df63b66fd28ddcee1", - "timestamp": "2000-01-01T01:22:01Z", - "output_mr": "cfaa5b8e26a3021a78a6051950934ae4324d3e24c403b1902b160bb2fac4d57d", - "range_proof_mr": "bcd483a4e93235f8d03d44a0c568f3422170cc18878375ddc0c388a2990fc0c4", - "kernel_mr": "af3f6a334a6aaa334edc560a02af32f9e377df9fb053f81e20f5d37c37ba444e", - "total_kernel_offset": "b0e2a08799a6c31888f60d37ab2081d6e357b60711e623c01004a0b8e86ff00a", - "pow": { - "work": 21 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "06f73c3a41c5b84cd2030aa6d88818346c937ab47aa978d5dba60263eac03f0f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "30b81cd728cb90fde4ec57be8c84f418175cb447736fe531a4d0d83bbd945c6f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "54f5fc6ad9d827799c59d8dd4e4e56b7a1bb6506e69574ec6363c694dd67077f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "fad50006bb82747fc1815dedf704670b4ffc38c8ab1c7c534bdbf979dfa3392f" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 11 - }, - "commitment": "0675a8c4532ec8188903301f2b79de8db2db4275192f6c78d93fc282a2cadb59" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "3855fca9cf1b9a0a5ead45cbf5e5bbe67f3e8cf5dccf98623cf1c2ef1bf31110", - "proof": "8c68331793623447e6c2718730f56c8912d0d64baacdb229a79ea99acd80b20bf2cf026ee3ce4e42de1da64f8918e4c9e6396ad13905c33458624c8c5b70475558da31f8913acf653f5c6e8e9e7192c75131178f88f112ea56c8636338f70b549496ce389507b23388870c4586ba281dc1c55a70ec3c28714ffd4832fa682b700984edd27670cc3bac9a08122241e84f26bff1ae0b55e0be1d7407078ef81c030dcb6e1d9a235945728ec3597381b135666284f36d9531c03f92fa70b9d6160a8f44305565f2a580ad3557eae5201537de4f77fa07153c0a4ec56f94998d420298ff4952ae7b8f3a2c65ccbfb21b0c488b8b50c30b0a5875ef841acbb37a70262883e328695373562442cbba7ddeab27d8ebcf150af4ae601215b296e3b0f60b96f038a6a409c47667ebbcc9b6e8ff0c3ba00832c9099a5a0f1d7812bab61e0256ad45a2f15a8ddceaaab497cdf456bee2bc822e3fd4d5c67c573fee51fe7850a6b2659976282e5c09a83be4ee6a7a74db216ad19f89bfddad0ee50fd30c1a4a1a7d9bcc94b78527ad1c8f2e3be55cee269a05e5846bbd068ac6eb67d54830405a51e4ed53ffee2df60f25ecd7fda71fc079c800c4589991f35ac5a33d602c2cfab26dcd803ae8d968957244cdf303c837652125f3d52d6fa278c6d1b110a5592a6292c32b8e71bd645a18844687eaf16f596010477947196ef76022bbda7b6302ff91177410d70531df9f2ab05bc9ef00e82ab974d7325c6b40409b1830507f5257c0678c5b8ebe811efff03af0b1ce5632aee6736930fc218b1fa0b2d1843d346a364ba408a715774e80d0a0488c63733a68931247e3b1939cd4eca75b5e6a016800c53dc465f31d44537f3a43982a56b465f6a466887a97da31145ea2a10c4a895a59ec6caf8db8a34d8ebc3c43110ba39b2229e4e43a8f186dbcf5171708" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "40e8a102cd20dc2e604dbe92a6762ce0b2ebad138747c8feb1d5577e34ba421b", - "proof": "c2e42a4ccf85410f4d22c35405ab3dac017127d2bf3f0aa6dac57c02d2d9a9042e901c939796f56160ad4855d29226fd01a825f76a35a2dd416b2d95b10b077cfe107f932e6ba64f642123152112c4f175699937fda062d16f7f0c7192af9b556cdc9ef3f9fd4ece1ed5b3c623d2868d461b0dec476f1075e868211dc61018144fc26af428a41e952404cef882a4e9199265603fd2db86aa54d1e1debdcd9407e115f090f60a2b16714e387a971c8b2601431a1f4aaf473b61365cf6e83f1f08c2f531875b049acc17e710641d7ba4f01f8220d081037d26514620e789494901aad27c6d7626e8f105a0377b901fae47f714cbdea62708fa0cec337a2b49f77eb028e34db38831414e0fbc9f1b7db0fcd1b01e1ce584244c01077118cc44ab187ee25561226ad426f7bc1eb7cf650c4db48dbb256c73f9748fd08614e9cdd462809a3470c5fd39e4b4beaaa46a48afd2307d935f981490f9de1837f7cb14374e825dfea04f343215c7b096793d8ae055f1ceb676199684aa4a90cfa2cfb9c650fc1cdecd75c41874f2969f0c921e3b7ced6bb967882abb3f426abd69a4138f20da01506d8e51846c5492a67d86449060ca7f30af2c6996f34f805e8a14f808796aea829792c255fb45900d3397672956135e1a3a874a9ed6955c43e22a04f875b0c4f883d69bc5373e22d160c94a8493bc1afe992600cd70d3296afb40dbda3fec64bff451aedfe78a8aed170bf57a2cb8509270738c57595a7d33af3531ab3df8d4cb112ece4891cc747b3b97b8c73dfb931c87b9c7467a088922d578e03a7f90a0f278382fbc16442ee659e75bf87d5e8a07307573c51119e28954cb40ac584d4e8ccac00fb021f04fb5cf0c0583925e3e6cc6deae66d22dc37a13d2c3a4039eef5a1a589c26143c012d82e272ba0d97e234a3062817f2f7ab964bca9b9808" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "481a5c636cee5fca261a0de0c76ccfbcad298c2366ceda4b229aada8efc7b659", - "proof": "3a77aad63b5a1eceb55ef6c98bd9ae9f6b7610a2b1765f5c0756d01476f06476d6b7e0c342c3a187564061c211722017d2355f7072f52c3e8d5d0e3d5b62451f8c7a77907539f846a0cb008008a8440470fa891d8f107fa5ae9bb97e92a41e54dcc21d76b78be2f0888a0b74ad598269d477eb7f370a08e04a19f2af7a97dc6867996fd96692b572b356ab247a61969eabecf7daf695c75315828547f0b7ba01e03a5350286e2875e41353fd4eb09a3e26b4393f899b105ca2747af243cf6205ddca7ca19cc4133de6e3a0d8c35b42a96093439012234ca63baa0cf4be0fea0c4807994b1e7f3eee631b4e13e8960c61b8a1722415a35457ae7a5afbd8f9926b7a464922bd2ca93e6dbc36791160d7588da963cdafdd1bae6d3c2418021dd4301664a907b2a648044e5012f0c0922868410684dad51e62abcc7d8d87d3a562675ec075792d0703d51f505d868295c36976483a03af424ba0341564a05bf38a0b5a69d15c632f90c9ace6b4bd4d88ca6a7bfbe3ac40a5c360312be5e6ec50a665d0ac53ad2f562a6ab4645be911fc0abf275300b43cec787f7cff8689705fd9408ea1aab149069dbe6d0304e9d56d3f8fb7c1c9e7684fcb74fc7484989c1be367aa5d7e75a8053bdc92583e9feedc2c661b6907fb94b4f0c91bcbdc24ab21d33f6c7957d0bf2bf6afc1f0733dd3a5d358a9a5866f315cb0a24a0c2fb48fdec4165a07b7d4a60ad06ca9458b407ab35a41994eec83539ead5b0934f3c6d0e65461ec808b9094a8368cb6ae297c9bc68c53161e97702d6de218ac882cb6aeea3c0c3473c53033e7902c9ea520ac72cf9f1a315f771a2fae3e91a384cf32dd21c2650bd6bf9bc1893c0fab8f70b5112fd9a748272787ac47b6d73468e1cf4074f30e0fea424e7d8e35c4242f703234ba3c781cd374f39c3e77c3a54f25f80ea0d303" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "80b96dd31892bffa86ae769314dca4cf5ab5c8897fbc96bcb8864cac55db0d7d", - "proof": "e46f466a6ad2385ec7ad4b197675000788b3181da4c5d6f137c4b28c6469ca09e21df5722f7e02c6fa65624813c465a5998f451e99c3acffab7062013dbf832df2ad67fa3e46f9e2f8e4d72d48f78c5efdaafa3d4e0d6810cd77f5b3ede4ad2678d2ecad03a669732b580ce06f03b7793d44df91cde46e2b852651e9454bc544e3b4fea4a9515e4ff8bca5d7c51b550ae4cfdf443618907c49f5e37d99e1560b1a9f55c0a737dd0349fc1a17a1195b32f005177154e523a1ed67129d35b2a4064918f5b18c7418f93ed178852938354fbb46d71cacf6eb929bb52fd95523970dbab27d25884dddd7631f330cd04e99ec7d7a3add5fbd5d356fdba810029a7772a81d9cba38ed2308fe6929a671822907a94b52c46cf8f10be364f065488ace5e2c8b87de91a10e3422434651a1f47af569e7c47dadf88f6617bcdc2a21e964282efaf9d166701abab6abed23a6294c38223d44bc7e7f7bf73266e14de32f1e78088537e9b50d754bbe074a93efd81d71af372feb2d8c237591b2ff5a3f8bcf207ae4a74d575c68ed44ace684799564314f3778db28e5d0882ec7d5e40c72306236d9587f9ecc6a1069bd119d86643b4d5f86275d502ffe19b8c00834d2501905d06ebd0fc2b159f871da590ed9f97addb3e2e4836b8ddc79d30a964095760d4984e56563415c58a402ab7796660514e2ce2435d9397e690236d72461199174546e2d87f4d0ee7baf0831cb316a526fdba3ed479d7b8e71b2ffb4eaaaa782a765fa1ad85fb2a39c1b0dee26e3796ce189df0e99d9f81ca30b9531ed4e3eedf866343799d0a0a2a6a57474356dd0250621e187c033d6af7ec1052bb25403302316cf0fd5fa7cd27d5a2f28f55cf1d7b3021e8c41da5a1b3a2b3a61234aeb36da0838a3d2087c545e9c77f710d602223d5412fe694373f384b5d3b4d99e18220a07" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "8a8f6d9e8bdde9dff72301596512e33f1668e887f3e7f27e26727e4bac29b00f", - "proof": "fe0c8739fbe82b8392206f89db782cb1ade94bcbc4ff209859157be01fda9c6b7825d769713470fa8264dfb0f484a2b52ba64b8525a16931081d50ad52d6f211e2b0f09b4c24436f2c0b67a7ea4ff1b4c391ee5ed95ef90992d1cf335b097b0d6cc4a3ccbfa88dcb73a0b54b9b6bad6f65cb18268c0847f6c45302b5379cde6d9ee689a3b8800b15a1f6e8e00836358001559c29241570011ac0744e03a18d02d3ff67d9e2333e54a68745adb909e45247cd5202c2d61e5adc9e274534fa1b004d84cb5e09eab4c2ae86654aa97c772be977ee4959aa5c5810708763fddff9059615567478745a415448f6b056b86f75fc51a7c48a6228c0e2052e68ee91466b50961c2584ffac717a179516c89e8b44eaa27241cdece9ebc8d6f85fb089f873263e92a7fd6dbf737047465bc8f1adc7d3c5bc75a02f823ea70560087d48f969307a32039d072e3ba1e568f69c04f8a21469f0b33c9b320b4ba3216cf457066aa8f2542049c336907c030d46e136c85dcf7bea779f37f30b8ec348ddb4b56442b235b1bd0ae79c645b6aaebd1975d5533f6efb0d8903eb07b9010acf306798459e589b3d10ceac8530e9b182f80ba10fc0f2aecabfc70cf59e27c437d6058b604ce7a8a434beb26c141cf92056f954f8066e5fa53aa030902e346d068b8f53394cb410963b224b80ee663e74a589b525de91205dccd0806fc0a29ab3fdcd55251878900c309c5be244c2fa470f20537eb71fe713baffc9af95b5cb0293b7711d8eebb60d827471da174c71aa2538f32d86affabeaa0be177425f4a3d636308472677a149ba24cb854852e88a5547d58af75fd49da30cca9737ffa2040fb24651afdfdaeab696a7df2d3dbdf935d607421ac183fa3267459e6e660048af52650ebf12ccbc21bc52edc556317971cb19782af870b5a653193effc805f56c24cf07" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ce589dbfe477ed8638fc2a834b1bf578cc4fab0361d95faa0696027f3936d07e", - "proof": "e4cf69a5f0fd98e098daa55c989aa713f80c715a72fb398f1aaa56a621f82a564221e036ab9c00de8b3bee8515728df481949625ace49e4d944557d462024b051ef924fc7b9f53fa3e71c54e8babcafc38e7d1fc7d9b88efa1d1fae02fa38a79949bb4262e5bed0fa8d1b51e329c3f45ba83dbcf9ccb32916ce33d96ca489c10a0a603ba35f1b618256759fc80ff261899356bdec2a12694c29ec28167963b0c04b97ce2a5b8354aa4f00fe7ee6051160c2346bde4a3eee71d0fcd1c21f7ec0f5b7c81a76ecf0e02a9af741fcc17be9a51e8ed7d38f26f68fdfe03819dbf04095c174bcc52e58651061e8f192f18c8358aa465fecc7473b6103785bb4542482d80026f3c9f452732754aad782c72635b41e8cd3f39bacc3a284bd6cbf3919772a200e7f4f4ee0034fc75bdba715bae97b6bdf764b3b26896ba20fe4137ce4039a8e3d96eb389a36ffbd04ca8061fb6042486be0afb976aacd751a6be57ef1908c2af8760ec9104aa0978f8b473ea3c00e4705ee57c54a8bb60acf4ec262dee47b8690fdfd38406bfa76865cfa8826fc93a712ed862a8fd3b4120a6ff23d9bd7374533528a178d977a597452679b79de3441a4739eddebdcc1e417840800a5046928fd83772dce77abe89d249f201fd4ffd0b56784fcae1c58c6b226a66d6ca5f2ee2952caab21d349e36d05937a4253994cd7774d4ad0742469152080a49a22c864eddbd958a97b5adb3b2a6aaf9f3161f197c589fa60d02fda8a1541d8e80637ee91baec41807e03c2821db60c79761f0961ab23091801a8391fed87de8af47c49a7616e809035c7dfb03adc85c75b0c1cba4beb270ae4b4dfb9971850aa13583b5660d7821c02854cf3a264c9354db07ca147a39c1a00f8d5b5639a7ad090edd08434883dfd991a9a3c26ccace6f22c9c67845a8ec67f3e8135b491d494a05" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "e0b4183013a45b312480e4d8f7ffc89481e19cbeea8b30f898fcae0e8c9ab357", - "proof": "04e4aa97262bec1d35be6465773d15f53bd8e06257670c9e688ad94a73e52947c47929d55c7300a6ddf8ba8c34d5093fea212aff6b0271efb0cb8713ac57480c70020b6774586e4a037471fad5e4a526098381b080ea71bd41ec79042f21d71174d5824d4f731c6dbec9c25b771299f734096d12c7fa31372637e00209c460252a00b746a11ecc593a53007fcff84c7f308f990bd66babf1651d437d21d75d07e2d5ba8591cc3f6aceb3e32876e5df073f8dbc338604c64174387588af2d3b00ec751500952a934cf5ce34028c2e281cf54f6aa7bec0e2a2c6126ec737bb570e4262d01aa1e3bd6aba2e3bfa1ca6807e026b37e082e3ccdf83debb2415730431049163ad1e282941c692e7b023f02a02706e360005aeeb7f8caf0c8ccfd1064030e78bded6c92b095d1a5f6c2a502f1bde26dcd42b87e0a8c024fb67f9b9084222d0879cc4177c9c75441049519a3378908173008b9fc5d778367d2bc083df787c6c7b48dedfc1b5f1d6f21c6fa4e970ad7a4234f607ab949ecd3f9ecbae8a208e4e4c67ce1feae8e178ea2d8f078abefa3971fd73b14200db03ee98f8178d7f8229f0813af0046a28b9b4b3f061c5a846dab0c9cbae7f743af5d3e405ae3c3918b7b52db4cd2b3675923e0e67ded6cae2023a800eb3fc7aa18bef46f24af1053eb211fb071b7972024b3e0d2b42492eddd0ce0b9a4cc921952f809a81d70d2a64069bf322b96d9631bc48a32b6d357e018a342d0282e486c5169b89478ac002ea73b85d9bb76097bcd4f3f2f693ae6fcaf4804edb6b6657a3bbf8c676b86d6f167901e027a386de78ce29d0184b8f8494a3e9c3b0dc52ae68c44ff03de2302342e8560ca352dd3069f9d41227e16f8a9c9b9914b4d8f0ff9dd8a4bedaf9e9049ef5b03b1a8eb4d9ec1cb9ef97ca29a7236624e71aeec53aa0c221346b254a0e" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ecf1371dda337b34f6b9158378d0eb9ca65fdb713f699fff7c6810999e94be26", - "proof": "d809c603693681bc17cdb45b9042c596fc02fa0157ec16153ed92acb801c69460e3cd18b225a3fbeeaed70fca803a66a8219e30845220f68994f71f671e0b9591c753b694d46da7f131fcefa867b07846208033ff608b891b8c2a94c888f12601e31c9c83e2bbb8d864db95248f682baa41be882b62f7a264881b76621d4e37ec5f52e88048c274c3851923f51e718355e4c6758249c506bbef6dc847359c20960bd5a7a4b5996bf6f32fa63ce3a4f41428a5a7091b8be9bf23b9c62229d5d0b57a2c78a0c8d17da716ca2321cc19dae7768dc3786f003254d2663001fa18f0278a52d2b1c2cc6d452f9636eb85bdf3ab1d776b4c3caa794505b34f5b90d36198a2e7b81cf42c6ed6f01bfbdb3b1f5aab2443556abab1ac18461efe18c6c3d5b026bfa3e4ef8a28ad6023decf5a9bcb5eb243175d75d5141f0c0be496ad6c8197838b52f0fa1122d4e6675e265e82144d96d127cf5db456368998a5ed220a629ac91da2390b9d7a353afc74e7a42733793026768248e1383bcb366978f0fde738c24536e9468540eeb814fbcb2a4a20dc60164bc502defe119e506ebb5bc1b6bc268b0b58a32cba60dfb670d28a53dc209326f26e1b5c57948cfc24b6b8374589e8113580ff9a5982b63e05892c3773dd2b9946ab5607fcd74822f4d7ca5543f6e041d95fe59106926d336d881d8385bee408db14fe8556a14e3921fcb50274440c3df79dc7e50663bfdf92cbc9d004fdedc08c9d2eb8d562c51a0f247747156c628e374e2e28726d4d67893ba45be6d6494bfd22883061c36df19a97433182e66918ca94569c3e1245a961a6d2fffe586e97bd8774d9bba22ed4b897481d565ea7f24108743461bdd9716fb11560b0fc584b0038dfab58127ac124bf5566504724b48b99ac7ca2ca72e11638a74803e856a9192729b28eb5d0787fe44e1f40d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "f09394d73c25334a8157057e127e347c450f01e199e1422226e03d4f7bb69d0f", - "proof": "e0c47ff1d43cf60f95f0153b5b09bf58e4335137573c17b63554e7bb31ef521e7ec939c0452bc2f6c818f91eae447b2fad37c1bb7c39336ba2020e859962c63cfa696b54f788dd0eefdf8beb5e718ee22a1f164e118ad989e83f2bbbfc7881755eca49d32bd8badf2d616bb57bf6313e1cb5964250592cf7bb5e3341c4dee9188acd8e848dbce925ac34967aac5cdae5d9cfdcf739acdb389f6a885ec552e30dc57a54300cf9aec9db988678838165387d42462bc6bbba5948403598d42319005a85c6782ea86337cc8a5c1d495d4fbf01729f77c9759db7687d0cf8033b140ed255597b68e31cb3ecbe20ae4a67cfb94eb5795d1b7873fc5322b72751025760ec457333a04321a4547e14c8723b5a014f65494c23447800c1838c18dcd60063b8baa6e32507b0af03a43f76f772b84df8397ebf502a5c889691cf29ff9a622b8800280c322ff6c2e041094fda02833b046bafc16dc7314fca2a669302f3282850252d588fc81ef54cfa0ed8791730dca5707435d53785ac5a6863840561092f5e611346f9812d24730bafde7c9283f61c3253053cccb85f682bf512b534a105d8a77d1a815267ab01575b2e125049fcdeb5cfd586afd8e1bd19d474879ba46ab2a1c1cd8ce6e4ea22a3c11ffa1156f63c9f9f987f900897bd51db2444287378e429765956a99135ccadd86b87284659aa8ca120dfeea0db3dd6bca664c1517a50b89ae44052e4751c4dacff4d6c4dd7e72b0100d85ddb0b0b101fa80a708228a2e616c33cf8aa0649b976434b8c3aadd53ea97c8bf35adb449cb464b446d454aaf827d849a29fa8aec207655c478335962fd3d8bd2270d543ffe6fd9cd98c4a8344990395742ccee9726b3891da9aa6391b1a66c246e601a355d8bf2684320a2633694ce11d97e02baf64292437ed3a2bd93c5d292e557c828057a59969ca04" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "f601d4bd649b6ea2d58f6743eefdfc17685570553325d76b440a9d761fad1031", - "proof": "38945597a16a11934fca15007cfaf7bbb7d8bc679f6c108682c17f1888ba6b2e0a27cc81abc0fa25b535c0261e456fca397b5e2a90d5d124c523966cc021504c001bc0cf9509021e1034bd7debe7c5ffb5b7a1e5c2e3f0c582d4b6d4a6e3de12f6da695b4999e0d8d79aa376974d0e832fda59cd2ac10e783b6f059abbda6e70d1e37a12ab4d46dcb6f926d7c43815b0ba89e140d0f7333e00b312fdd70a2f0e4ddd48df595246c532686683562808b0b4a0ff9bf217bbd3ae31fe13a8ee930fbd87261e8fbc6242b01bda2fa7e01834419674c949776609a13f45418cb35004cc2a21dae5cf8d47acd685a32458e08a2d03d58ba6def5dc403f74a9428543665030ad2eaa7446ba6e6f99aaecfa5084423cb35b0f133bba28706b8ea254cc568c5c5d617254e6aceaafe223cef16285c50919ea1ca928d34f595eb276567b43fa30e02b2b5db024a3cfec558f4a58a749acd11622af800ab6e9591ea3d20662b01f0c2be816af4f31696807bef41a8b6e99b50f332271bd7113ef3394ed980260adab15ab3b8a18696077ac5560adb6ba578fbac58cabd756c45da15130af487ec130b88d9f3088f200824e1bc4e637dbcb207d30c020ffc4ac315b58e6d20058a132e33a8d4f17d78a994c9e25690d5d1243c8fbf863b49167b878b10216468e79d1544a2e2c0bc3db37f36f7469580dc6692acd61161704fc0b0c694c0760f48b071b95ed56e9cb4ef8d0f4530d943dd39c7b39b58e8b9342740e308146452c8adb4a5e6269bc752fcb7402bfbb19924759793e46081b8f245e5dda6ece2dd86dbbcb9a0f4c59022ed3baa03d1e452bceb6395a00ed98aac3d445f14748238638d88988f5f7afecad8502c1317df67a4cb0a94338138d11bb6db156a3240090917f63cf9c7eea90d69e270d476be2759b6b05ee89486cfd60dccc50d77b0e" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 22 - }, - "commitment": "044d0c6ac5247a4bbaf89906e13a97dc142c1e59c2f67f9c972697f004506f10", - "proof": "4ee575f0d910cbc9f799a917b7d29b9090685759db7b7abbcf6ddc4c8724e033ce2cd6aa563032cf6f5e9e6a124250642e5b2a03b2999717f8b920dbab07d25a94b357c3967bf25f860b4db4cdffa020367d1dda11d19521a7ce3806c1683734aae5383662f19fe613a0a2492c48ef4fadcf8d4d68838f4865b8140fdf268e68826a117f296b3acb4492613e18f1a853f78b4d42ac4f65d372071ede0e1364024f33ae68a2205c0ac90d664728c492230da8fb46e83a99dd2590d34b05931e08a9033b4e850d6f81cac38c9e69cbefc87dc86fc613b1fe135e74d4155b383300846bdc66ae79de60745a143f98e76e1d308b39a71fb512e7df2347c26e55025708388790cbd958fd7550960005ae167aabc3448ee2d0e0b971d4286101fed60c921b39c5b9f31ede73ae5f9e13a98311406ce3b4c69897195dcdee04d602b23684d5d481252025d1ba264c8fb7d2fb5b5ac1c57df30e628e124b5115b3e32c28cee4cc6dbfee183bd57b03ef700a5178e9db69b3fe1e37e3734e588c9ed6ad48aa7806074d246a0fa1ae4a48f31d20664ba5b0114f2b929e3129fd744260d6146c220cdd3941a71389fa03002ba209e9b45bbd1a5702136cab80fc4d42dc902b90e9f105637e282d86e4fd702182e8d32ef29a71960744d486b249597d512c6560f25505749c6dea94cc39236476bf41ed03155da55249296b10d912a0b442122221baa552c6e51ce3ca25d76267d898bbff0694d70cf3a4c26728860b0b0a2d08cf7d779dca252248c9558d6dc42a041cc42fe2c56645e890b64018b9b6b73ee858637b2ea4eb03a4d096bb72b69f933f22eab5535acc48b17ac375ada03933d522befd875bf7c3ef18999c2cee49fabbaff65cf1e5f302ec6fa2fcab37d409db244dadd86036d1fbd39245fda05d765671652399f3dca460a5efdf81342701" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "00020f1ca8e1d412b6606449dcb4b29dee9b66135995dcb0773cd0fa1a70241f", - "excess_sig": { - "public_nonce": "103b4738fd4bd355acb5f4fd124298d9e2d330a2f64af8b93c5e36c83ee6871c", - "signature": "fe5e1c03383a38cc50361ae4a32e8d71a4fdcef1c46de6eccf1cdaaa5c1dac06" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "0845fe01b39ce76a35ec673745d20eeca06889064c687e1959b4fdcf63e02b71", - "excess_sig": { - "public_nonce": "a4d31da1b36cb57b29e40892e96d3cbd1ddaabc1dd6aab6c1edf78db30401925", - "signature": "493652af98377f5ec2932e08d515c716af4a6ffc14a33f25d78eaf7e9285ac01" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "1c7a6f845e6d1e66628ed2cf458d68dd6e87bb5b0dcc324ee9eeb2d504cde10f", - "excess_sig": { - "public_nonce": "382c55d92198003c9010a7721384bd467e0d6c778a97210054a1bc25dfefd571", - "signature": "bd6232a9ccef87b1a992aecf38b6e4ae668ee9f595eb7e290a998692e14b3d01" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "3a5269fee840d150fd73f74fef805ed4333c5fcec2980fb10804a72fc191c413", - "excess_sig": { - "public_nonce": "a4c55a50bbaa353cb6e082f1e629264b3b3b041782761bb2d2922560c76acb2b", - "signature": "5db6073032337ca8ed2dca90ed8e89d713f88587ad14c83941896eae37b33909" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "ec0246bbca4b27dfa26d4c9621684bc64ec37d5b479154049171ab28222f864b", - "excess_sig": { - "public_nonce": "78495542ce08ab701b51ad34ceb7e671fbcc812a163738cbaa77632443f77d37", - "signature": "9ec3357a4776c136767f3a803ae9a96d2d8c6f69f5d877bcdfe0572aac21da06" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "12a7d0503112a59818f973ab6dd943e965adc82e4de7f793b191615a93fd8420", - "excess_sig": { - "public_nonce": "76fd4a5b1a909cbdf4c866ee3109ad232b0aaa4c5ede1e92d6c8a92badfc5f54", - "signature": "02c8b8231a8b1eff61691e55a4d03679407352be63f9bd94422c9fa32aa99e09" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 22, - "prev_hash": "9ae3f8d009dd07509c4b55dc2fde6331a7e9a6ecd5e15e9a9355e968320be265", - "timestamp": "2000-01-01T01:23:01Z", - "output_mr": "1075856ed3fc3c4364ea9fc584d884293b1df09e054e582f2ce4003235f17c4b", - "range_proof_mr": "71bcd9d72922bcc933a5181240f81921be6e8bb80932cfabef0c3e1c2c9d5ce1", - "kernel_mr": "1cc44fcfbb4cb3783945119bbd8e8ff5565cedd3f6ba389189615a2489c0b0c5", - "total_kernel_offset": "79caf87ee4899e04266b8c508281945f1d37fcc3bd67ae26acead2e8913d4903", - "pow": { - "work": 22 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "3855fca9cf1b9a0a5ead45cbf5e5bbe67f3e8cf5dccf98623cf1c2ef1bf31110" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "80b96dd31892bffa86ae769314dca4cf5ab5c8897fbc96bcb8864cac55db0d7d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ce589dbfe477ed8638fc2a834b1bf578cc4fab0361d95faa0696027f3936d07e" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "e0b4183013a45b312480e4d8f7ffc89481e19cbeea8b30f898fcae0e8c9ab357" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ecf1371dda337b34f6b9158378d0eb9ca65fdb713f699fff7c6810999e94be26" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "2ae0a1a972816d63294f2e8e43bb543825e4c0415c14bd8612e5c4b0c400fc3c", - "proof": "044097e774e571c5fc80cd274683a6a1eae3058f9bfa7ba81911c46a0e116f1a06dc992ee8b5f18be360f644cfcc022cd54b804b3d25cc3dbf860d18a877730938da7bdb8131c59bd0284519203870d187aa1db476d705cd6224f6f9c755df65c4800032ee9cc9e4b9f71315bb50bae942bc80fdd70b4630303a84b70ad95d12d3f0b01f62baa17a66af220b2390b796a3af232799e6a2869e9368485a6f3706d588c72037f904acdcdbfbadba2bec07a0a7b20e5592e14eb2619dc5950421052fc4052d19d81b687d1b81af7580e62e11e692befd9585f8dc31f2f52cb502070e873d5785a4d436e3cf3c25546218566cc5764817cb1433566990f0c40fe40110bbf097eb32df6a277804ca2b12d557b00b9005b7da6e3807d22e86daeb326f42288f797d4118c877f0c139ed82cd4a4a2ef5996b2037d7f8b0ec8548be935c169f2205e55279573d57c394dc331f146a4f30069f1034b8d1feeb8faf3e70207e3ec1e0b144bdb946dedcd29acd0ffeeb5fa07cbadefc885c34a1744a5c6e159c810eafa695ef352cb2a9dfe20de13928f89da55908f133625bfb2bc708c33676cc11f87ee53b7a203bebf64dfa4d5557fc37cf33e527d53386fdf59e8c8758d6b4c9dee1513afc9827e2d498d27d74806c48f87d396d9d1943cca50737c10a944acf6a3451fe5c5799d75e806ff1a536f664af9cd98f1e98b5ee9df4670918e62a8933952ebce8117239f3a7661bdd8a04a1929a8d33bab6e537aca276833afea8e9859593f68b1aef957954406f784e50e3f91bb35467c7f9a4aba653bb2564177c0ff3fffa7c91f280650d016cd1f6148a800fb5f9a579f6cd659c908801fcb188408f012cfbf560a7e09ba014c921b80d2d23eef8e0f18d917c73bbc8017eefceff736aba65d4e7310410aff3e35f5803172e156ed08efdc2801b83d600" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "3e7c624140eb82be900b7bdbc267a415ae65f437e648d43832c5fa8411ad2479", - "proof": "147556fcc25ef141d2dc4002ad504675385ed7365cbdf5db61f78f89d4d8d551b6a000be1ae969c1d1e5e0c30fc06232e791fd272d29078508026c21602f422bb07a2c05afc8d78b17f227bbe19b761b9d62d5f08186af14bf8b6907d876bd245e0f72211e8a0b1bdb3be38191283acf138bfae44ee694a53e41d0b0ac53093fd3f0ab866feea9aef030969289e3fa1b6a0dab3658b97797743b81768227b50b891010db324828cebc20e43c5b0c33cc2d42142d460384fb7f708a4020f4c00487f5ddedb42cb386fa17bd681e3582496ff6c51429fdea22b82f12cddf37840beef8cd5e961737f29c52ccaa3f180c74ba3c093d1cf3a8f7e11f59cb687b0b77e2669198bd530e2b98ce21fac3ffabe7e975c60d2c21fe9002f04d71b606bc715ab6e8202ba2bd83694e4c8e6e7465c0f00d58f7d9700abe4571bfe89afb087a106db2fedfa372869611bf913b82ff3feb2d825c22fa51392289379c6887f41c243d9d72e325a6622cc217356950eff7ceb360be563ead35d798235d62628a73b254208779ad269feafaf3332566a21065aab6c371dabcf5ce3acd37bcd9a1010c45a973fd15f7aa35b5075d3d1ea11a1ec704330980b13b83009f0cace0281894f624eb87b8b20f740683d2607c3af6d31ec51b3b29b39a20fe9632a6da594a62164c572432c86e7d151974c3593d254681d3ad572b1f4011b16dc8342705036ab244f40721cc6d13265ec5cf4687d76ae8d54623652e7add09477f2e63434cdc62bf4bc7e4df3100c4da7b6b9a460ab996e9e97688c5b4337bbbb00836071aca88c96984108fa9fb85baebd97daaa745f5b22a1bc9449b67dcf85fd591bd67061f451c0b4cadf86b06018c4ed84dda43e54f2ce1af8bb15d5c5f4ecf004b09bd06afdd89dd33a55bafd2405c84aa4058e2aefe937f2aa25bb95aa963df700e" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "4a6d0a4acd9f1cc0fec9234aabad11dc82d9a5cddb8fbc3ec10f9c75619d8a3e", - "proof": "7ee71eba62c49eceb9c02a7841ec69e2bdffa5f687e79d38f069f16dd15ff73180f27bd73293a9afd99e15ef1efb31a00dd75bd84aca37b59c0ba7f1e49cdf316c5dcf05bfa91f8389b2f5a34e0c7552e66e1946018cf0635bf6e82b12f45846200c8b8df784d8714764c4d81d40c8300a162254e8503e54f8bab65c4d6fe731d2fe3f4780a7e2d075e421e01c433628bd47dd5f04824e58654dae21d934640cebfc4aa7a31d3e5cddfa7eb49e518ead4a5ca183ed24fc39e9cfe17783b99e061d5615b4c18ad1aeeb226e592415e7368e2e434a21ceb130d3c6f8d3b7888004a6254afdb2cc78d2e51e04891cab5a20a4148ca793e0b0da4632886f792e32786e5d183416201e72621edeb4a6b0c50e19ede27f1d0b5e953c91cb75e5579a682e89772ebe3ffcc51b65f0f26f7d7ae531cdc0b703103b45b56167b5b7a0174832ef3dda644ee5f12a11e194fcbd8b5ee0379cc45c46d820d2f4078e2628395854bf72676d952fd7891ab207a7f97302629b2c66a9c79bc50632f576a0f938050c7d4009db4d12a57e131284d7a55bae87ec1fa44887bc0d963cad900acf811fc83fa60bde0721794ff8aedab5c7882f5dbd540dadbf23e507fe4a8329acf407a6a25b68970a09d1b2e7e830f1f65d31710d4865dc28aba37d5725040ddfed575aff719006d1538fbfdaba7279d8d1fbae4aee2cf1f60d49a06e917d0c0c294edc94db76ea0482e97850994dd395324373968845ba1cc921c9e351e40dcfd73728fa73a1c779af135abb0dde8481c5e49829b5ad0c58d5c04d84578a77ab961e74d22aba43565d7fbc6293f334f2f2f82fc34ca9b5229e7976d4c86f90979b01ac93a80c862def0ffdbfc8919a20fa6ca44911228d1804bc6be01a8f482808016dd8b73b6a3f8743e4b5b37b49cbd6d64ea0619b187a0401b3af93cd97dc8805" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "4c30ffce6b5194060c2a48a73fd540944a64a1046c9cc048b915327effb06244", - "proof": "ee25e242fe666fc0ce8896789e2e8cb73ae08a5a6835724ca1378d05b0d29b58c6e59edafe9b595c4f689a638793c4845c41a818f7dbe324bcd8b98f95fc455ee00fa218954f73e331fb80918f56ce80c563d1165867056491fdd2a64126a84a4cf10af0a0d31830a5f602e91dff580214003dcb501ab63988375849d5afe80f2845bb55e5f1ca17213b53e21e223cd4128a14ae2c25c74163d5c697fae0c8084d931a5c0ada0fa6663fda103bd340bf10c658fb60fd8beac9317205693e9403d5935e410f6b4d00e23b674a91c3940db735f4b78f172c66754cf8569803550e8ee8f031fa2e098651cfefc2a693051fd44812adb4f56a894a2313d2871a185bb457d88df37085af0bd1c83916ea1c96a533c080299e3d0bda116e0cd33ece1aacdefbd0e68e0d7fe93338b392d936a431bf75eaa5bc8ac57c6bd8f69c7f130a94f4d19f711d14d36dc78d75ae6a06ad92e54ea9f7ca407e8b2317eff083d000ca3a96c6e01d3f5f3713ed2accff3cd45cee74faa7ced9b6c8c50944aa78622656ad90e630d7287df21cd1a2f0201ce1d91f60794a4c4905008f59a29bd7dc22b84e2bd7557903d9646ba5e603fd511701cd95be5e750cadac251db63b165b51d2f2251c956233fa0c60151581d7c5d94b4fa2e407b8755b745843a60ec4b15934ece8e8a8f3f10d1227ed77c7adfa0de4cbbb884e75a81fca6552bcae320b710ec909ca3f22b8a8fc2915212d5d472c739f3ed574f7f0b01853560070f692305ea24bc11a64fc348f1e1266d1b195c1fe4d66f2b0458f5207e3c4e7d71da433aac64961cdd4f7a04977a17e2900099c9fdd1dff77c58b5e753a9db70fd40b77e65fb95939d4effbf62ca67a3d9137cc4aeb8e24ded767f3e8220d932849d20fed268345ddd36a6372d65e06a8b220ef2404fc4ea4ae7c107d947aaf16f47609" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "68c9bcc0a60e9af1d6ef9175b4f26ad65cac56d4ba36ba1ad55447bfb1abb259", - "proof": "94a7d05edf52dbfa7a0924fcaec01c56379ebbdfbb551565679a6d8ecc9aa90d84392c2c04756cabace1281a4d8f040125e53d559cfbdb5728fff321e347eb5752bfee4f4b199cc070c250d0d7e9ddf54176fa4be51a8ef8fb013cff5b87b36cac8a0df853877d4f26f3efd743f78d01100a741bc191688bd7056580b7a12e30b90da5a1296f6f9826aef2235043b776d9f8639a5fd2cecb155ccf46f11bd50dabb1f8b315d43be20b2c6f25c1428dc702105e6a9b18fa9d6c6b56485f47ab06559b961e84a4ad38a23e58d8781f683680191ab795f75be90daa2c90a6e2e501fc71ab94396041860bab33a88d3cce3392627b1998dce477820ecd91286b9237c89450e4c45d48e9db3b0338a7668e61149d27bee5f40b8e24ae328877500816305f06836e1b28806912aa01147d09b39e1db7fe7a43019f239ddcb237d81a3a1c2a8f0a574eb63df50bc6f47b3cdd7b9fc03aa471ea284fdba10eb296b9dc17e6b83c8ac0da3b117c93dc7dd13ad12fc2d1f8410a8dc682bd6af28f179c881ea257a0b8d560298709e61be5e4cb1d5d5500509deb424dd8b928b1835899a35d00e6831306aeedd9c935967459db4c2bdfa44cec1b2c006abc5570de0e8eb01c74de5dee33e161394ff2e51d64b230db79d4d5f75aaf579860034880670bb23f62dbf3f394bb23458f3a79e71ee3b3e81952cf2a05d985dd6419cd083f37aa74dc42fd5cfbd02de76e6071b28a1c4b2679ab82fa8728d08778635a5779498c7618f80983753f21a333c276afccebefa067ee338875540f98c8710dde7d59020ffa58dd89cc7922e15f598bfafc970c342fe4c84d4ede469470793bdcc5b94835576a1f10396e2d6f5063d3505b47c2c16a13c1c6fa9f0d6d5c0bfb583f79b404ccbd4aeb4b4740ca4f23c21f2830e9d5970ea66e08d40a374f54627c5f627c05" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "7a362127382a1ad9ac323c02414779abe3b8cb98f7ad8a2e4665e90e8103d833", - "proof": "5430554a66ce1f61be9ce44c7c26cb324427b306a833e9db89b806058aea71280c9725e93e0c563bc0118d4031e150285c2fb90bd7f21811bc60c26408c38c106652b06dbf2104d742dee2377f10347018ea906979c55ae7811026e3275d7147387f1a141edb322b503dde1f983564e875ddbf63928487e96ceecf81f665811da51cbbc1e4761349193b16341302da04214c35e24abbb8b951d3c742bb309e06e7a63ab9d7dfa02d6e2dd3582b9bd59df5ad1f0ec86240ea79bdefffc67e410f485822461427a0f78d363b3ec8e7783ae2f8c4a16012e9c0ebd87de214b17406a852e14e7e048209b7dabe324c0a83c419437a36ba988e24eb49177ef2fc2407b4342ca880acbbcd23a886f41aa164313a241c867cdac945544a2aa2a207746d90d36a21123bdedddde659509bbed966d04c75aa9bbb03ef894071333d27d43d5678ac36ed9db47e093d1f2c1a2909f1ee3e9db6e866c561e9ab71ea03806b59bedba50ce391b8940027f3c2d572e57316239e0691c1870fcc7325168a5cfc2ecaaabaddc2332d5210d02cd46832a6bd496f9d61125643cdb95435ac07e2360eee601d57577f909a1afafe542925c9fb64e2ae1cd6e582aebc3bd37fe9b6a607c05d06b27840ef82fa1b6b97f9e78761406d23a503724147f797eb0d3ea9165eac7276656792b4f2d9e8061e97fdcd8d62d576d1dbdeee158c324465e1688b478acbd1dd8090eeee16ea9c2ea4b6a144ef311e007dcf1944cec1c73850a57922a4999054128f2d4582b8f94089be622605893110807ee249a848a98fa1516c0d5e93d5c58252d9aff2404f626b390d547508b056fa9f064c20a78f59f9a6d72c595f9bffa5ebb5a359751b0d13f0a5b68add1239f64a6f88bed688a6e1c14c09498aca058a7caaeaa974c038660088eb254b23030af3b21168a39d3f8722600d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "84d1a2e9016d13a92df5cc494b5303bfca19d4ea8fa7ef8b94f218c4f5473755", - "proof": "76a35493a282fe8cf64286ad4fda79485bbf699ccea0e41b8462355cd62f693bd6735de8d9f995bfdbd644b7898718bb19aed627ac9a813cb8649e911300431578192c9e14f5b43cc562c67d6374d4db459a4f3debb70c19534ade5a6da1d02b1c3e58710fd49b676f5220b1247c0ac9512d6446d34735cd4fa41bd92717fd10a208a0af41c98c2171493bceec5924a3125ff6bf21ca1057001618491675680e7113dfda18ca35d2e78b12c03615b9c6a3f66cc7f5876b657c8722e9bdba56082db2c863074995ff4d5f7c60e638b0d425a5ed192d1b11fe8ca8182ebfc3d30aaa35c4f2aefbde090df61d94136344676138d8c8bc2d6b72af580edd9cb67f5efc89d2b52d0ee49fbfee8824f203e3fe72536ce2feda6ffe86d333b104b47743b4a86fdcdc6f7d22b65cfbc162cf7728907f2715cc355a875ad209028cabe3694e04ab1258d65121f2751d37baccbdcf2f075dc071a6986b62400f0286e1ee76ae876ee2e8a816250bdb9415f0f3155eba3629f71fa16e171dcf3f5ac1c8ba0eaad045a5ef810dc34cafb9c748cffc2956f697b7e2dc0c093b5bd7f89ce5b36318a9b70634afee080375917e7d588d7828d1e964ce2aa25c06f0654b16c4634442bcc944cb3182df41b63cf5cd5d40d9c63685502e0b3d5e09de0403726c25640864293784ba66da45ab5d7df6d991644c89840c1ce4bad81b03ad58cfb6967862ea467e767a6c2b68f33df5858214adc4977a4617d515e5f18f8fd997be3f6c36c15dbc774b15a57c375ee63bc78e89f5db05d21607df97409967d924e1626878a810faf7b7c4d02db00bb3da5ad7a63158a2c9c2ba8dd89c20a58c4e2e9a6d692097acc10e040750c03420b43576ffcd3e0ab8ee08cc13a78bfec26ab5910d5ec444095e753cada6f49abff6364c793a7d3c56937fc167f2ef102dcef2830e" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "c67c51ec987c36d65de1649720ed8d01d242233c33fa4c6c7db3efdd90ac6674", - "proof": "74d0ed49c565aabe67943b00e58b717646fa4b5b1d36dd74b1c731c4312ff16962af886bc2166be4a5201f0f190d8486be671e801fd837cb42a846ac5546167b38b24088174dbe951d7605f2c3309bc8ee8b5a8134e7cfbfc3ae4864cd66734ae293786d5f597537624180fd48473a3eaf91373b40456307f63165d6566496157f464468d921eda8f826c736a45632e3756fe0c4c9eb2717c5d192548d2d190a64d4d4b52f0a154e135aa1d467613e48375dca41ab952047425d73b2ec6fb70abe1acc50292cb6f7db9ff87425d0ef8e7547e0f3a4a8f920b9f59e7a1f0c3405e89b0a6ed60a65d2cae5bfe07153835ea76e148fb2302687ef4e61babdda3218089d4f84654336bb5f728e946ce3f8a86a8e24eb9fdd9fb97b6f31b407e8f34c806591de4f603ba8b0f996b938450af2a3920269a17c5f482cc8e1711e764953980f5d2a576574004cedfc950d0e028ca5f5e0799041876923c5cff79282bd7e34157d7f1705e7b47b0a147d1789fc32255ed250dd56364b43c0cd91fb627615d410dbe8e5189baa5595a7fd0b22d989862d8bbc8d5eb6d384b27e5f8a3a9b49988510287dc9b8e01ceda2ab253b9fabfd5d1e64e4e96d9b2dae975b4d297c41f677f6b5fc1c2b9a43bbb312471f0221e75a3b56f4cb42d4868833d6b4fe3a7f56fec2f921fcc1f766cb67cb9a37184b4b90ee2603c5a4244f8ea98e16b500312adb95d58abfef2c8ebc85afd2869ff8d3220b06f97bf38b5a79bc2a86d109183e8f46a39b99884f36fccfca85893ebcd454454272727d6fafcc77ac0c8d875bbaf3c0475af63f12d52e6a81a33ce45f5f13b320ae2052fed4fe34acfb3e737b58dd9f726ab86343c28c5a935e4e3beabb104b652d401dc9ed8e4f455ab64f0dc08fe401db18818c94c0667cd375bf66c87ccc6741dfe97bf724ad942ef13e0f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "e2e8ae766e68d79a86f3f7dfec8d2b18623e402227a6b674a710c28fbf6b6e03", - "proof": "e4e2921473da18ec1e7bebd632df1b2516dbaf28fdab6fd2464600189e737b68eaafbc2e8d58d4a3e6d2b1c13ece2bbac717b936bbfd2ae172c5300da79aa25ab6a069d1baf3158df010c45dc6539cf23fe49589fe10aa2413c14ac72771c14aac39fa1eae551dcba3232da39862a0d0a6db90d7e67e19901876ccb42b795e131e7142f9ff992ea8f09ab84e5168d67cbd87ec5e761cb981ad72aee34b026405511879a8c691e3b76715285f18206553f19d857363357415590153f7fce61104d9961aec4465a0d314b6fb506e59c9d5507f27bfa4bedab2f1948117d0fc4900384336649e4be336143b6be9721d10a403ded013b0fe6c3c047fa8f82d4e9a63cc1708df51fa79b16b14e1266576d0715161d7802302589507370e67180cb220cae99e18ca7fe17bad6b14202bcc967e71d9dd88bfe723b274f46be299db175a4ee70b28beec70436261c396798200b6b9ed0395cf96614d1a689413d6e10b063a961eb1b13d842d3ec49a6e79250c7ecd83963f688916849e204b9f3781236af492d8f933a9377ee0bd68cec47fba7a80d01131f0f3b41b8e1318f7c2902a54da20f199601e36940550fbb1384cd1692f288867ba609cb70b5c72d73ac5735df8aa06638d996809f6f3deb6b860964c2aea493bafe61da9812b86575afc9f4d62b70786035ad63254631551faa3e39ad0988456f3c4c8f483321c17171ce420128249e119e2a7346e220c5a06e8713088f9deb5924d840577d705276e7f133cc61be4a52920ecff25753ec30b7eab51e985a13e735520b9d20f0945c5202b4eec793f7ad56418e7293a7f63c2f8523fd7ec8d2907c57978155b0441d2d67114386f5b904d0239a3f2cb8c434103d8bafabac404137d24e4b511f0235a1fc004bd7884f52787d09a7dce360c6a0eb13a3a9c150d3f5304031c983eafe5243309" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ee76423d87964f6fcbea0a74708e736360be916179fcbb0e0665f56e1808de27", - "proof": "9a68f947d8f1400a486e5ba13e834a0abe446c4efb7fbcbd73f068e4c2cfa53204612efe2edad8dcf2a389b9b9ade1458f8ed0ccfcae4ec62b78f1544eec6f60bc6056d4427f6f0adaad0f4977f4c1b1cec0f827e19b54c52159bfcb40e50340d2b053449ac08090536d4e08aee62a5731519a2f4e3d42373f8b641765460f4ce9bf1c0adbc902dcf0b0c0ad7733d490e6c6d526834419ed15973c1b37b80f0a161ec80794bf0515dfbd39de30ef5fe63be820a50fd4c18a473e7f4e4de63d034a60711015c592aa1207d1024f9cf0187cdc897b85f9be6c7b11cb5120b8150e6e4725befeab1a0b6100772e34c16578ed6dcb582c0eee59ab0d996581d7dc2224feae6a6b8b29c1061eff1109b2d01863518f570e435d112c159e78c7ed033910f1b1a8176cbe3113918e16b2c579545fcaedcf82723051c7c35a51d5fa6b755477392470d03783194e504522362407dff66d291d3668770751655509a7d07804e0b5514a9de26124365592912920d337e79212709fc8e578230d8e2257166f32416f12934f279944e73e1a27c452e3b4829308f6cc9334e3a812c5a374210674e54092593cdaa0e1bf4c6abb7fa963a5de749617c344dd2c21bcaaeddcb73ddc0785fb68016a6078752729748852ea4d237b56bfb00eb94a9bcf4c4acd2d1b5aa3d22c3aad7ecba42cbeb770c11ea48d3dd3df7d7df5707fbc32312e8a780a8c0e821e0e089ddb69a40fe4a038015fb733c202de73d4fa3ac145fd429fef36ca09ddc58ef694a3f8f2a7a060309b6c5808aeee6217852ab1e8045478e8b5791e17e589aebb1431147e5626ca8e1caca48be6d29c2d94ba92f72c92ec06a4443a6f52e59699e5a430d12b24f3276cc8dac022fb70fca68242e3c2b40557d702ec1365422f447fcce89fbdf61495361fb9afcf90ed0f6d90aca18e3e65488207" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 23 - }, - "commitment": "5081312e47ef838bd52100e61a9307ba1c7272e3ab4c4905b9d5bb9b4d8e5674", - "proof": "988d78bd0fd4dd71258715bcde3f39452beb7a7f4d53147ba70480f8c881ed192c076f13397e89fa3b333862f1b44b7a7a469bae1b2b8cd8c4e236977ef8912b44860a65ef079a7806cdb39f245a05950dd1ea9ebb18d986a9e97c1ee6233923c01914c549fa12b80272dbb26fe9e7a1dc8d75dbd9fe2117524fb48248a14831e9725d5e1ce9ce5313dd8f9547c45365799d64dca870cd22ccb3701acb11e40d5e69833905e34282b92d61bd805bbb557a331107eb938baafb7e419b53138e02e4a36347e0174a3e1ab3be966e304b3b9044013a8726d391ad1feed66f0ee003e2eb77c3e529f1274a667f0375d78d21dbe9072ee08f5c9b20d08f49f4856b2fe8c42612094318a3ca6053027b26e3e1ffc8646696ecfe59e8820b04ed23f429ca6615b485fe77a0783d46fede579c807c7447fa6e66b40628da2508839db42c68b13376bd868ef18fabe6ea27d70ef68fe7e5103b8b9ce9592a3de8e940245ea6df8c165f87663f8617b17da07109b9d58156a6c9a0e432ff9114eaeee4885762d24eb96dc84af61b7764a43d8306081da10660ff93195947df7e7e14725f3e4ea999ce5a72a394cd9108ada102ba2e745aa6cd89019d71a141cc047195447b526db6c725672002302e92d332406fc9c4a687e7daf9ea0f79bbe61a88b8dc7314cec1f544179a52ecd3b186b3e43fd8bedc5f401a34da52d2efb4d7489d6750763e7afc11e8ef30a7d402a64119ee853c3cae2bc4826d27b85f59a63888e52890e73d21bbb618b171d9df58f6131a4d1ea8388c58ddd41383d1db31ca8b246fbcb3fc1b32b53e18058abab391bf35bdecfc92f46b087cd970ead3e2f2901019224ed513a2a10fd42af05e4007d80ac5d3f496d6ab24517f441d7369482e0c0973497762fdcb6a1b609f97668195b3decc05f41d034ad4dd3c764d45d903b50b" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "14881292781b577fc59621f83b6dd068e98dd5de5a24a6c8b371158c7735cb59", - "excess_sig": { - "public_nonce": "8c9d3eccf677c4a8fa690022511405215f390e59d90b86fc95adf657f7106b13", - "signature": "9337a75243ff7e715ad52b1055743ecf4e23c25cdbef96055ad1907c6ed7da04" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "3c0cb447feaaf5f53d736a2898b64f5c2ba25c7a781ab2b840ce303701f3842f", - "excess_sig": { - "public_nonce": "aa3dea4d761ee6afb5071a026754c4c5b70e7d0dce9c7e4238fc9e2dccd3b123", - "signature": "518f8570704ce128001e22c709e71ca0fca3c3e85f73729b61f8e572f5407103" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "a2ae43f59a61f52cbc4ff427452c1220537d10b1310c5e16082d1bf8e8a13f4a", - "excess_sig": { - "public_nonce": "e2d1f04c9e8828b9b8e4918a0169f15f09982a8627073f7db9640f258ab0531b", - "signature": "d0ce2661712ce171a12186aab359ab48b767d84c09396ded03efff8931816b01" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "def3281281e295511c24ff55df44407add13ad334e48f5a91e532f02eefe0f32", - "excess_sig": { - "public_nonce": "be15ff4e5104d35ec88f39f5ce5755ee0c92e2b192fda6c0adbf140d9a25f323", - "signature": "b7d47c7a89fbbab6288074d9803a95b10ad1b317160c338e6024910ced180305" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "e6437fc378dc6562fe14266003cff9ac153a6774aaf89eb47842dec6884b696f", - "excess_sig": { - "public_nonce": "bec096bafb4845d2d4611c9c7b95464925c48e54e788bbf343ee1afce9c6bc0f", - "signature": "238d981a8df2c87b0d093f2217cd3806b50247782d245ba4f88b9aff9c04940c" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "e2452887318e659c4bbe629b03cd98850594485374f9cd7b0b984920fb409d69", - "excess_sig": { - "public_nonce": "ec99523410aeeffe414878c320c3db1806954c140896fa76ae7e1085d065aa5a", - "signature": "543c16da1c07e2234bf4fb74688b85e0153b963c9ff8463786afd0f495f85702" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 23, - "prev_hash": "ec9f92b796fdc570c9cf5afb91e291de2e1754fe04606fcc27dad82cc680b178", - "timestamp": "2000-01-01T01:24:01Z", - "output_mr": "5079dd63dc30a7d32eb8a4e75538a091eeadad8a07986d419ffac374ed86eb45", - "range_proof_mr": "aff89af8f10272faae5f3a7bdadcc970cb5e9a12f64608aff4f25a1797b06a7a", - "kernel_mr": "cdfa027ccf1eb2a0373d76f0058c87aea56c91e5165faac258d8392a5876dcf1", - "total_kernel_offset": "532b3a69a5ded22312f441cbdfd2cc2acd926b9c599bdab75621dafef0918005", - "pow": { - "work": 23 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "2ae0a1a972816d63294f2e8e43bb543825e4c0415c14bd8612e5c4b0c400fc3c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "68c9bcc0a60e9af1d6ef9175b4f26ad65cac56d4ba36ba1ad55447bfb1abb259" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "7a362127382a1ad9ac323c02414779abe3b8cb98f7ad8a2e4665e90e8103d833" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "c67c51ec987c36d65de1649720ed8d01d242233c33fa4c6c7db3efdd90ac6674" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 12 - }, - "commitment": "1e44f23761abe164ee449bd913f95d42e61a55c151e283483ab6e18c3c60f17d" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0eba3f3363d3dfe9209ba52e386a925c1d070d2350fefc17273d04b46faef579", - "proof": "ba49d99314bcba71b0666af80db62caa7a006c458b574cfb5bcb92e19c028a5f7ee8e5f02d7dc238ead6a4960f8cbcaa05eaf7d200386a71836061e9379b4b4bf845010c6cc6bf98d87463bad580bd51044a1c4fc39f80bddde8faf459789d36d058097a6ab9a7948984de7c07bc557c57c553d8a3a91127e5ef8e86b3c6dc088e28a1a7135eb46f37d953c8d5ad46c838a869d494f4f87ef8df1e337eaab9047297526ddc16d265ee1370543c965a3e62743cdf6cfa2fc7052c12a531e4ac08f4f178f8b819a515946ab1f7e43430b6168ad644c2166eb7f004ad96d6e9ae06ca1ffbc5d550dd5656c3bc0daed5810adc4caa8386953eafebfe75204874d554528350bfc34f9f45a667ae714fc01be9d8490370eb1ae0b1d411c708b41fda303ace58db4c0a0fd9a7e1142f3a0f56184d40aacdbdb83ff9dfc2c74cec0ed07196dd448350613da1b1c601d91713f9e589ba094a0161181d59c069ac37a0be541618cee1ce5e32dcaaeea21a8d86a47cadd3c804bff21c5c71e3aef60c95af1ea23d601c841b32d4e86e03fbe2ec44fd29771ecd07d5a2be8be788a3889bcb318cc0c222318bb89ff7cf5f5108c68a1fc276fa48bd56e34ed66a70347e7fac48869023c649ea00339eca8b1a8a3ae8d2a48cb0339d53cf7f5ace5a5e3b1fad5d1265b43149d97a80647a1f9ea3c0840be2f32bfe4513b3bbc8c4a74416c400543ed5b706c1b6c85e3cd0750354f27425ae99a00a021bc4a5009277ec3c03a9512827bee697b182007325d0821f8041028de115c601c1bc43fddb86e94d8a206aa668a66b7ed625f1a5515b9bed25aa344a56f4245489e150561601c777a9535e79f497881ea26a27b3e37fd4900c539b485dc44b0523c63a763c3f3e9b50d302ab78365e791beec94bccf0e3b446fb04f750a678e7e8c10c7fab27b8a4204e0f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "4ae02a537b8432c98ce8d32a5abf2eee7f4ae8f8067523c9bb88aecc0f61d634", - "proof": "403e454fc86f2d423633fa08509d40e38b3ed30642bccba740bf65a861d34653e4f17bbe571e960f82b3d03c02655d375246482a5302ed9f77ab06888cbda75c7e46aad8bff61df266516e9e800ca91171cfbbde8662f9c4d6199ff464b56457a6c365f4d873382db1cd5f9cb50c08a02825e9ead7fda8a74ea6add92215db11330410360c8d44c09a5bc9ef1a08d1c2e43f6db787b0432e1db4bda7118d180f28b1d526ccaac3dcb6d46225c6a5dda436a7c6a25776bf8b99bd199d153a0b0d0daa72635dc3799304c5bcd6a28422b30ea927f4e53bb8200e2f00036b20bf0a5a0619aa70b496b82d88ec4b3407398768c078224b224bf527189f5a23c6a707e8a57800a4eee9227821ede4efdeb385712ca440ef87db06b16ae01915885b386eb716d78a782b241157722435ed7d4ff7ad11d2e243db903565b4ec8d94c537e83fe72c7cf0c1d1ef8e9c9cbbe438e196c786e620b4ff5ebc341f58be01be128a27ae4462cd69134b65f261b4ee01d050eb33d75c7ababb1408950305086e40066339c142db94ef9419ef0fa6e2fb1a06569ba630aeacae50723c90e8f2c0199a41b2017eb9793f48b9e95cd70ffdf0fe3a635ac0dc742e87c38dc20894d97e9a0411396dd864814c7e53b3d1fe4962a1e80f9f02f152c92bd91f25db4ab760622b3701ecc68e357c012eaaaa917004cc78ef385bcd525d2600bd6cac7ebc0fe84ee4abf26f80dafd9cd1bf3a3cf94e61557e64d8fff08035b21749e2976c682cb1347c7538caba4f9526aca3a46bc47f790012b3a504aa7bcd557eca114b6540d6b1ce84d4160f15b3b429a37d3d534a28f91662cfe49fdd2440e4d47cc746a9cccf7c9e96c410cdd6966a101c18eadb80df67cd5a1121cb7453dc9c4376073a3e3dcf9f25e9eedcbbd9922cfb980bbe2bb56165ee3e9326c92d82d6ccff07" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "5cc6b3b89d121cf06d45ad4353800b5afd25e7551415a1eb4887ddd180108e3f", - "proof": "925accd6a97c3e351fef9234673cd27fb32772994ad82da87c3b54ca3ec2f0664a542d6417d35c33953b5ed781de59f1709eab7a1db509d544ae4013c3a4966d9cb588411d1f5ef45a24996c727267ec79e3e05b9f6e3390ce4e95b71d535d7b26c1c0ef713987531abcb8cadae4c261928f831d4015d00e07b78c85e020023cab36e1e18ef0e75b2a43a1688288b38228b202e8a851781c194737a076c75c0535d908a200f9fdcce87648300fe46a7a24764a902ae98790eeb2278b2cb0ce0c33ab752047cf64e700dc758cff53d8ad175d798389a20c6c07068a715f75ff0ddc653b17cbda682435cdedb7df27a37f81354f9b8e59026aff64a84af423b1238420edf2c2096b2ed66d655d53b1deb3e5e8b69eca8616b076bfad76ba7cbf67982b7d8989f6780be7e44c1ed3b132a79fc9e552cf6fddd0ff2cc181aa8f8b0cc686e8f5479ade11b432f945a59c45decc58a4e21eab6af0cff6d57f2a96911310501cd7e348997b2d8c86a3ed3eb54744f7ba18327c19d2d6136901d7fa4d503c9098dced9ccdc4b7092654d521f474165e91e95b7098bbb4be624ca696a85d302b3487418ca0fcaca7f6cad1d5501655971549425b1a9bbd6918d96db877365e9fbfa34087aa50d3cc701d89b2f4f228bd263f27e7ac1081ba35095d65d61a4a5e86939e4903b233eafce36560bffcb983e6c04e591ffd1881bac5bee83d797426f95a50ca31fa4ef60fbeb3ae955c2759edab34ed69319086f6c2720ea0505445d66549fba0e04eeccbec646a8429946144fd57a5fdea44ec44d92ad651139a8e6c20cfae7917a55ac3342b38349483db60bd143edfa29d7a079845f8410830649356889fa7405b653b474844646efa55af2cd498d2ef12165b2af1a3d1040d4c18f0f0af7eae5d9f4ea275676d94c8369e37d46d997b0a0f0a2e260a9f02" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "76ecdd92a79d14763c3a96b5d17b8db110af8dc5fdf967bbef024975f9525309", - "proof": "4a0d6798f95c108ca65d9b4b05e866dae26568f469c75adbebbe02ecf3f2117966080751e5c8289dbf9c570db08e079f5fd59f986c7adee572a9b06c0ebd8473ccf2ecbf318b1744989ae5fd4eda686c3856789e2c88e3c540b98fe2953963576edec5a2d81514120dcec0116f5374606d47730e86205d1e20362d2b53e312520de93bcf359613d104a1a84ed8964a0f34b618c343d5164dba06d9856cd5ad02a5ff4a06f0d9764fd0c6ed278b72ac7f8328e7dec120a85a43341a6da65d4b022e2519ccf0cae5d4a57d9dd029533a936d82f5a643b9e1f98e5eec4bf1eea105d893cb45e778b2f6aa386f00deddb50d718748a239826de8525ff167b45ffe08505feb5d295f309edf0cca8af293b2a8af5d683303be9a23ce6fa179c67ad9688acfb462b67389305e74b47b2a8f1ddf662375c729a23d3ab56c1f21dbd01b04142c0e1d876ca4bc3a57a97bc2152870b88ea164b97a2147197e162c94df627496952628592d8ea64903323aa5d25350002150db6baedab9f97e10bd46dfaa506c9889d4c2045055d1f112d83254f2d357b10be428842dcc1fee9eb8b7b38f6408bdce1c5d592ee09750416febafecaac38c030417bcc05ff72718f6088d5d7e34b0919d26dcddac7a6ae5862c94b6ea699525f76244ed663ea2447b29f3790744af66f60f68bec5a11300b005cd8f5a5688a12ec4589d2c91d417815d4ccf4dde6ef8151c6236ab4a2cd3f5ebae01a09642c4214f8670931e80a68154b8262de4f8c2275b213a4aca5edfe433a02e1ba636751b523d4025bb1604af1185d250b84283bc064aed616fc5b12ee424a59d23710164060f299fe4cdaedadd5c8e52d62ed9fba26d403968dc70fe259ab51dd115e93cec6e71b22f5383354d711a0705ca783f96690d05a8afde6c6774764777606b704977249f7d84f27ca84f4105" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "aa7b597176e9fb487c85d9fddcfaee16f6e3bf126fc6f4bd9a763e6103722e12", - "proof": "3455e7de23b494e87b48b9592a0235c700b4797cabaa45906321080edee338644a050aaf41b9ebcdbba127300e86ef910c51bc03e1652b78a3d2d5c2fd195e25f81df97d4693a12b11637fd88edcbccd21538e5856350e1e83ec941c69667a34823b19a21c6042494ef710dce2e777f0635af8697770c30c286ecaea2292bd2b9ffbf9a2ee6ae8ea30f00acc18ad4cf488ae1714fa01358118f919fa52382f0a64542a6bc21b506ee2e54cb5d930689964d1ee7c0b1d701dac62dac6fd561d0f022967c6c7386043f969cfba74081977cde1860437f3079f11925e8dec1906023e6e3b4d70a4e0764de3c05c5de394d7fe0c48a4f6a766c2ac9398de34824503e04abf454918b2a37c21d194beb880052011b5357cb3e3f3da38cc53d150950840620746030d291158b6f00e6bfc4d350bd321a6d0cc5bfb3481e8bb5e71542610785465b599b9b10eed78a1833955e7ed42144236360fa337a26e91486f7b4bce92d4365d6fb3f84ea6d64daec5b587d831020c80879619e8c56d1569cc43361c1fe9bd4c6d271ed34d240ce37b1bccc152b8bf3c71332143f2e3e9b405654eba967f2dbaa0fed975632039a830498a66ca2782bd13fb210ef163b3d835db6b6ef76321fa2fef78045d18ab9aa63fa1864cb133020356148ac154223723077870d36934a46fb87f825e87618a8d74092b9feaedeaade00b0faf9c450f2bbd408c20d7767ae665a0e2c6605e77b6fcf86042305028a14cb7c11996dd62b6346a3202a2ebc1c7ef1376ead02c1098798222ab278b0b2000b700f2563255dee333b83c6c571c48180be278e4cd3a38f3f0938fdcf2c5ce9cc85497268f601f9402bf50ee647b25695483d235594bab1e16d2c3649cd287bcb12201c1f92c7120024106bd9654c1af137f412f83055a4138d01b4f3db2f9d1076c73b82f1eb5a100" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "b03a9a9a00161c58824dfa531bb00d174a961d5d0e501da994e97243026e6c46", - "proof": "2c31fd9695d57e4bcb05f8bd11ca8e14952824050b17c56edc5bfb2e7e200574e432a76b05f075e26b69dd3a0bbf3c2e5f4ba29a239ace373e6803a5444a5856526104fa8498b9093a9716a84213e18852358afd229dcd574bd5350a2eda9a0d9ef9be41c69c0f15f23273df5f2cc34e13e421e30c69ef775a01f6267951364cbabf11dff7c46f69dc974c805933d0e48dd6e1e9dccb3fa5bee2246b326afc092cd21498e4133f5881b66b77dca7699fdb0fff2785dab1f476b613412e7a4e0f0d9b2ebabc7fa039557a96e3a9272944309600d4ace316fddc3290f6ebbe690e284ba8de720f4edf78efefdfb1e43a3cf4e03f2342fb3f10d57f76dde4f298297e0ab9835f0d0932dac817f46e28a2f528eb6930b29ce3c84591c74f0ace5f046a9668ef99eb4a234b55de8a707b5aee08f929db465078e983d2d041dd1d181ac2961089d67a1b4c30644ed64db61d5f00d02a24f3d591046acbaf0e872a88002af69c0f3968396e68e8c0d281af58aeea862839df200d425ef536825403476d92982483824d9bf373b086f75ca20ac1e3b1dbecc88c1abf155897d8d22b7f66c4a04a415d99b750835bd20f6ced47f3fac4403b8bbd260988b9c172d2703e790807109a5b477e6bc2d1abcee8d9e86737ba81c2eba060390236e2ff6ec7e404ccd6998ce59176a805f2e768de17c3a80a257f7cbe39177b02b514398a0fd65fd65bf0ad7aa1970e1dc68a1ab4c880b5ae3eaa7dbaade3fcb902c1189834d81b987f2fab2c090616b7ee41ed064e3ff4466604439dfe082a46dc95e2d3b22f2c6ea6779c9f2014efedad151676989d52a2f74164797246fe9b61036bc974e214b491e13f65fc6bb823ae8745379954fbba38afe8348414754dac72c47e426209c7f19c27020d6554a3102abe6098814fff6f33f77da4553ba45b19abecb40308" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "b499ae6836f019bad720450ac1d2c16b5a15dadc23e64116ce550b851f55da50", - "proof": "9489ac08ab358a36015296cc8738fc1df0b537db27df54385c94741ece5ed210965c59b2e0861417ce9f33f99dd89cb871c7936bd17bf2bccf4e377da1096f62cc39a2e454f650e36774ed029b5ce493676c2c335c9dc3779e7d3d9742f7950e12dc09eec6932435fa91e6a790ec1bcc5ee045551a96467ac372061c07ee6b2f65885b21ded17ddda885261370b07081f0a254a0dcc19263cc080a42a20ae7027fd7cc9e4b9d85f6a9bf9c1ba624b23cf25809dfb9bbab8b320c26b468fae103495884399587baf37592d43c6fd16a30847955c3ef8957e3d6e5558d0045590416c92b53dbb8d12a42a93f7dd612e24faac8b046f15408223fb61c07ff1b4c73941418ef22a52aaf1de0929b00b9ec23e1b1e417aed0028610b6b512a030250b28f677f15a97216dae5994e2d07d57d62aaa44537955b59dd052707a3000e469b671eca64684a494c03713765982948da0f6758b0d4abf31faaf6178491a2e0426227247ebc89636a67b27bb659c2e6cde6cbe721c1f6d402ddc8707e48bf1421c13aa1a5433cddc5ef903b51fdf08436a49df850cb717c705552aa9257cb6346a76bae216ac49273f336e7d74d9166df70c3ef4460f947971c83171f3e29213380c7d6142fd6b5cee201afc6b63ae297b15552c63f2e55e2eea4570c980333afc946ea5312497978c0bc85c86d3e325553ae34aa3a866ef0a07741dcfbc5566742c2a84382c8a46f4ced33250a677264d87bed244c4142206f9e4f6c671b336a087f7e988fecedf37612458f32effe31ef5cf131edb0950e98f9355859162381683d6cf46acbe189a0e387dc8da9a04c38736a851949bf8e06a96b51d81b35b785042e847e93645209fa018fa925848aaadd301b4ed18e83cbf44f30067ae01c503b702a1e74dfc4383450c56cb573efbe28b3e2c9496928c0a77d032d74c04" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "c6a45033997beb664a90d59ae9e0f97db85741f67f677b932a45a55c9099b220", - "proof": "b6224c74330aa97929b1928917650e99a859ee98142a2d0f6d8fa9034cbad429304330b033e6a0b4b02f56a4a5a171498509131f3f8fcf3ea634212e5cd5ed6542c127576ba054509598cf329dba9015240d57c14fc632b5c8682936d66659627a3cf84886f2d522d3a1953db72cbd6183fa4c39d748cc27f0e34b221da6c62e75bafa766db2d6945866258dfc9b7d2f45c840d1a35fd71ddbb57b6267f3760c15a5f5fb756f0f062e64271559272d2a7a06662148cdce2feda6fbaa019b5a00af197d404d350a11038e6992bfe2cb25fbc225d913c25417f4036f1d86097c0b0478cc5f86ac06220d79168f07c224f53272615f21d3c9e68e67c947eb1f83461048b09c24f6433f018074910f3e796ebe5ccd3553aadbd011fb2bf98cd4d54618501d060031c92ec16a72ff7a2fedf183bc9864547cd862e2e3bd7104b8f328e43c0dba5d99cd175be4d36a7ca2dfc40435840216c3c0e2b9cab8befcaaa76ac638521ee8eb0e27056e3634e4abe6b2562c851ca19d0085ca347d2a8c234122243082dd3a303c9e5483b44243479f61f26656f3fb74cec8b37803c70a9f0108284283cebd98d2b344a8376d92f07492698aef8d3579d1c1e71e93820d4491492acd1cdb2c5acf1e7e3db43529fab87d1bd95bdff19e67d25f154602b66cf242367fc4f408a88193bc33e6915161e0574039b4bd5588e77ad38407ad14c3a6029cf2e0988b904b28bc199fc57f2f2ba55467b2cd4099d33506983b4fc99e4e6f3426ab5d424f92cafdb7bbeb3d35f325d9df3bb37a50aa4c1c8e56363a682614ea626d562b02be5fc5dff1961bfccd5e0e4de8cbba7382d63e6fde83b086717d9409d7d48ceab2ebaa776cc709dec97021a913d0ca7c810bb1d2e29000cf5602587a8254c37bd058ab3db451298476ed9db8266297e09f36174b67d16a0f6b00" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "d249f6f3b42c676658c03372834707dd4f8317c3591ee8072bcb1d3dd5f7112d", - "proof": "7031734b1c7663de05e9fc1be940d065bcf883a47dc72e5ff1e807de2fab035ff4656436cb85fb5aa023d89c1f66dc7870bb358384299841e7db9c1766a7372cdcf26158688e0ce430709729a663e3420ba0d13a84c16b305820ae8349e571649a3d17f31c1d9418c060441d4b959da3fa17649650ad61a8a4dd5b26fcee354c9b662b1fb60137bb9953d389a31ca220119db443e3d8e52612821e0a20c0da0d1f5309e2dd7bbab01c7bd84d957d980606c4eec5f209549cc4538e9387f2960e42d8f63313140a8605a8ffd738e3ae4e6e71b727d7c965156b38a717773ea2059ec2276c385e279b2b2e2419c8cdcc6bd24a958cf3b3761442f6ac0a4cd916520aca05158e884b1cc33f2cc4735f2670c9b9c71dc979e732b642f7c64093b5221a878202361836dabbb0d2c68461501c4a4228c275cae5cc5a2cd832e89aee38826fe79a167385f5cd6014b464020ec572834a5722735c50933af3b47f59f76a68f30e38079938007366165588e603c31b7d95b30a1e5515dadcbf0a1597ba5704b79b19db4a206bf8e06f4313e0bbe0c3e88d3e91bdc7fbe134b0deec82f13d2a2c5c5a8435a6e603e3d3b83dd0a3e985c7aaf4fa16128f7f6357f1d321e363888b6626fac2f07ed5e05d6f7db35060ccfe5acc1a94ecc7d32cc0ce7e18f94aac4a9171e462b8a34d0fa60dde00ac44a03b447a2176916196e36dbbd2b15377fa1a3f5c1758e476b5483ee76bdd9731f5b66b703ce21e3b573f6369324f036f04dd8dcb7ad43ade720593aef8bf57efb832a318317e26aee3367346ea2d035736e797671c51d7c2f6ff856588405386d773eb9c51a9ad13e9f2c54139d4b144ba31e424a4519ff1ebd8807e46acd9b04520198f74ca52f2e79045eb78f505061b77093df2dc9f84525fba8ba77acfdd779e4d0983c93e89d89772c17001db0d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "dcc3acf86b0562a3faa6f6436bb3d7237ce5797b3c84b0f8ab4b7642bea15e1a", - "proof": "44f1a7b18b8846323f4d03ecd136062a52b57052786f4222f3fffc265b5a5b2acc0005405f50f8ed7da188cbb96835ffe5cc4e7345f352cf9d7446547479e6609a25f970f9e5cd32211991dccf952a673ed818cf84f816e4bebc1a40d68c0c03a033d8d8533050b2c12f60762b8f4adb808b91226c04747cd67a1301a6705e224269db0789350983eec23d1394f13b00564cdfec0e2cd90909a9d79006bd4a01e2d760684703560b5a6f8339768b1769d877231ae7ef50b5760b8933aae3310869a52f239700bfaafe287d135dd4ed71c05c53b781f10e646c205e0ccb585c08b63e7fbf6070b15df3329253f07ca84ce9af8ebe3ff1dd13eb7486a55fc07d3458fc3ea8d6472ec88b5bda592a72937e355115683dc7a08fbf364b3b623a6554e07f90f2d3826b78a6ef3244b634faec73eac1d51a6e231603a91f07e6e5ff75401a3d11aa8fcb48158ef31aeafeaa7fcc43d48ac01d490045bb355ef68c193d24944ae324c178b0b9f9f94f9981d078ea1de04471b364ef0e4a1928baca2a2f484731f761b48d32da6e9692142426014c8233e80aedf85db7c35d09920bcf6a3c8ac480575a95897fdc95be9168ac9d5e01f7b06c10e17ed73efa2631b55079d0c5c45da45c9e4e0ce1a55d7457fa76876aadbc57ec387b95d3b4b901b6ec74daa28f6f8a7dfd1689b6491e01e250eaf417292ac4cf95a480427e63e1d71f70c0aae8745bc377dfd168184937675874bbaf1efa3f897f9894cc12bb6ad4c70ec69267f84159edb993a830d672e7f50e1f70e90c1f4e615cd71b9f2b6735d447382fa44d7ea13b78ae1c64265ceae13182d27fc8ce329459c46ef6ce563e90554b26538b1e2e664a5fdaab14093a0225cfa9a0c2bf6655a69c4a40239f91ed0852f3083d0d3e3e83611f0fe024cf882123d5196fce020a34e3e487f7d6dcc902" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 24 - }, - "commitment": "f481bf13c5263ddb2b2fd61fa0f884a3c5d646790791ef886d4aacae3d5e7f42", - "proof": "de9209488064a20c0b48ce6334b8e04c523397bafacdac27f2ce8f620d4d96419046330949be26d1469c38b83cbc6c1988b3462902c694f137c08eafdd7d5e621e800c7ff154190fd7fdc1f7476773d3617078aa6ff06a35e14572aa5086776efc7bd172fefc1638127c2fefd37756df662433f2cd64f50d70ae0d8d2ed45b07e0ebf980252e45edb4c39da40c2608d5400ab71698950e35b95a0cd791310f0e28189c6520dcbd76db1973d5cab95219c0c5efefdebc4aa5ccf61f887c2645031386fc56c6053c537f6a88c4b92526a616a409bc7e835157b2d8b292b2e15501c6fcb80d7c452bd4da23fd7a05c5ef8ef7695bbfbea888466db5e3e2dfaf3c637e9f3160ea6fffa8f40650b4c83586d7b24b148c6e5622d2e69836a8695f6d3530db64585745b3e0e2dd692166f39e9e2a0eef07e75ef2245e8e293d437f3e47bea752d59e09530a0649f3411795342d5cffa4772276b819fe78539a2e979873b6c29459fff1e9d6dad079d588482da087c5986985dcb601f872b52deecf7177c23f033d04180182ca944e1434957028365348cafa011339628374e48c4623375c010719f00db3d1a68afd75e8ea54d35064300c2a974eeeb20cda1fe14ee958f0e7375dadf8251da8aa2aad234c10170b358bda9025de22e83c32f7db1192046eb6c5a24a82133a086c03a977ee7fd121011ac5aaea8077e7c2f77392e9d829905d9b55cbe472468de6bdded3a9fe1df13789717d3b73b2b9d9c093096d577ef41b7decc28f9f464ae7790d0d2b8c2f811003155fbb4103404d76860d80a537600724cc145ef8a3fc6a6253c15bf2c8009abac4f962d9f05bf2fdb5fddc011b73e7c92816234fa9d4fb83436459864017935f17e74802825cc05be1d2d35503d5c9d1c7ebe733683b6dcaa908a9a37e0fdf96750c052733f03fccdebd36eb03" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "0c4792fa6f4563aa62c9d602228b7e2eac89f6484f7ec9036dd1835e78fe0a28", - "excess_sig": { - "public_nonce": "d2141aaf009dc911fb93944d445a7f46e4f3d1aa09d8050ab6419e97f4ddbd2b", - "signature": "0875b3aec06b1c3b4a2c88336627d0d2b73b5ab4fb36944524b4b703d3d73c06" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "209252dca08cb170f0491a3bb7799526b7596f67bf26beeb93839993d14d6e5d", - "excess_sig": { - "public_nonce": "0ac8234b819a918b6d558fe8c80cbed62542dfe38e15c3b552f74be0516f0b55", - "signature": "68d9c836225dc0c03efe32c4c83ab829da8e944c89c7cc31cfb3bed08048860f" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "9e9885f1522e3929fe3b854991d6b4b4b261d4944c830e7cd90359f05c9e6703", - "excess_sig": { - "public_nonce": "d0ac6f7567ecb57d2f4b667b55a8da52d2f5183613d368ad986013ebc862ea48", - "signature": "7717f34e406aa7bb6afce20fed1cd5e4738c02bb74b8bb53b1aa9b1fb30b9c01" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "b0239f3fa1f7816509484f555ed04cddffad93338ff63ff6cff2455bf7e17212", - "excess_sig": { - "public_nonce": "9492b0c2532a6bc3fe0e1fe121d05e6f44a0e5d72d2c373ff33ec85aa2d92066", - "signature": "9d5bf0c68dcceda04cfa910fd54da4cb8dadb4a12d71c45e03299dddbdd79c06" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "ecc8a071fd55e1126f26245aacfd9bdf0fa765ae5ca98c3d47ea0bdd093f1220", - "excess_sig": { - "public_nonce": "429f852caf15b8c74480ec87bfc9391acdfbfd7cef3f0f8df306f4bd05d42f3c", - "signature": "d23a8c69ec757e1c3d6020f4a664f47991c2db2f7940469e210620d6774c2203" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "6877a625ae6f7efcd51d79d79ca60d7a7ca35f577fc5dc326dbb77b5d259cb22", - "excess_sig": { - "public_nonce": "30f4bb84850926e82ad89a94d45e467a4d0a1896c0b457ebb62562738da8772e", - "signature": "79e1a12c2af7b5162edb6f92c3bcc930a21102524a23151f1408187a48f6bf06" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 24, - "prev_hash": "118ef404918ea0bd0155809c278db269494e1a83d591a7c83cd032e385d2a252", - "timestamp": "2000-01-01T01:25:01Z", - "output_mr": "996f4471b16ebed21201af6c629062570273786464831a3f184b6741b9f05627", - "range_proof_mr": "f412cf1969f1971b496216f1f20a39ade09f342d8f1790a759befca197a9f41b", - "kernel_mr": "5d5dc2134f367bafc8261220a83035a2b059c1fcf14c7c46d84b02591193329e", - "total_kernel_offset": "54a727160653c36df0e1226eec02885dc7f3c6cadc63ecb4880c9774b4a16101", - "pow": { - "work": 24 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0eba3f3363d3dfe9209ba52e386a925c1d070d2350fefc17273d04b46faef579" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "4ae02a537b8432c98ce8d32a5abf2eee7f4ae8f8067523c9bb88aecc0f61d634" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "c6a45033997beb664a90d59ae9e0f97db85741f67f677b932a45a55c9099b220" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "d249f6f3b42c676658c03372834707dd4f8317c3591ee8072bcb1d3dd5f7112d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "dcc3acf86b0562a3faa6f6436bb3d7237ce5797b3c84b0f8ab4b7642bea15e1a" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "34330fe2dc78cf6ed8f2e6493e63fab638ed1de7b422c1c19f7ffd29747edd08", - "proof": "1af8bdc39c2b9ed4523330c24b75c708831d09c7d29a667d2b2fca5405fbf552965013730a813e4543a8a2c35657e9b93ed7c241d86d5518faa05b4598dc310aa80dec836e0ef3947446a5f635ba68443a58c6ac5fe2fa558c5ac0fb02c4a051743ab4803438ef15efab3640669aac40049d8943fe481ea27cebf95e8bcbe56acb962cfa296e9ec392b4313c3cb5b4a1438dc4d79739698d3e23b5f57533570f2b2c6c1aa134b5a72079b59d3b1bd6a6aa541988c19ea681786e19627516f50b477f0700c4255e2589443b647fff85483903051137c1e9e4d2e620243ce45c029095ff8a29c6ebd26657bd3897dd0d2c298af610927564b68a79f5d21b609314947d798aa502234f3756285567d8a9acf2aa177304394227a494f11544304b378c9800d3e402e05109fbec94e0dd2aafd7bfdf47d65dd85b6bc3f46128eb87767668e98c460c596ece8eb8e998113015b86f3c33a60d683628b89c5d11e7db11b25071356c648cfba3c10a3f0f25f7b74bb2b15b3aa7424c32341b5e64345565c07a32ebb3b5e8f1101223ee4ca02f7d12456233fff1df839c1931bdff6e4652a8e3a035808f0422b206000287598c705b8fb449aacaad4570dbb484faa4f733826c2668d379fcce0026ef47f19cd5233c0b468e1c0b74ebfdb770872e9272508cdf8b0d7b200d0a40fc64f22fa9b9b16a8f7831850af8c10466343cb566d573665803e0c51eebe40e5ffe39ae8fe08923fa0cbdfad6a2a5bb27ea7d8c31ed1b18e7a4c58e97064adfb152bf35e8240a47c78b4c2f05d317bc265b54a4d51307787f4f06735b7515dc4d97e250456b86e9358fdc865e9d5a69312b0f71343c1a127c365ef89eca572601b19207e347cbbe38e54355b3263e71d994f1f36d200dfee163af675211c5e0e6dba81c4083c9d57c2a0fe5ca4e6d2493818ca142a105" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "40a116b2230cee973b9f2589dbfb0c9da4ad75a13227c7ae7bdd82e029b9ac1c", - "proof": "0adc87753306e755f43bf85ba50d0777adcb15ab9d007f0ab9f9386e3a910d343c4b33a41a878b93934eef7c13eb796e41988a884134cc092b932e9605adb7077ef9d45b1c5b0700ab73acf201b79761a3cbfc46559ab83c050b12321a1f7545e6208e09dba8fef0fbd9e9caf6ddf3ba0f8a5bff48364b9175d24892938efa65640040a2c3ce1cd42a0f077a2b97959cc903e981c6085d614eac43658e76a804f24521f40b5d92a87d2d1543ddf20c0eb59e87ef02c170c28fb3758f562d9b0c0f3f12ba18adcd6643e3bf5701148a512d5aaf045115f8cd84e6684046cf510f50114170e6cc23a45ddbe6f5ad8bf8a7c917732d5931d10d30e124706911d948d4041927b028f54c5bada3728da9367cb8542275cb07b64d9e885c97f80d376596b5152dea91387ce21b4553f756842069cb550e5e7874e953a6e45fd9755f76709b48e2ee639d77006564cf90e9742ed3bcbd8eb8e015bfc1f8268d73603d647891e668b48a6c7e620bb3fe1c7ed4a79938541b328e075eb140de4df7326d2f56938988a11fff72e36442d848fc193a967273b205b0402223d9422ccb510948303aabad29c9bd8baf195135eb76ff641be39bac00b65e6b70ebfd3ffa2f47081a0f0917538199d5d29000e2edbd461c9f233e5d0aa60919fcbbf210f14106117c303ea53e0c7f1363507f0b3e8eed7ea13b3475bfcad59dda0ad2edd0ec044c9e862ccda487ba02f2cfe83faab1f0d690c4938b6628e9fd8a527c2edf8fe6761ac8e79b7c0872d83a2d8f9afd58279a3c4cb55f22ca800b380be58e3cdb0c3384f9fef0b68f1abbdaf235133bea6a6838d474f64bc9fdbf77e66f0b695d057cc245b89b9b7612c761ee52697eeeb53e143f51668f893ebc2351b604b69905094e3390bc17b4f451a79062022ab4d3f6d7c798ccd2fa032cdfed7f94d9643a0d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "46d8f6bb78e87da8f8020d1beb49bbcc4ca938e50c9d67c5a682a76d3995df6d", - "proof": "76d6d7f752d4883fada85f6cd246d5dc91e54b160a85cfd143a6ce132037920f5c90de197c4a8f41e0175cce932695f541b60470ca2b6b58c0ec078de052120254f48bf62f1c0031bdb59e842d96391ab7704c8f45e3db9e31a345700eff4e49505afbdf6e03244d6229432a960c5135fd959f6e2e5074f22588dcdc2c317d1a9a83e3fb4193fa325b0b98db0209beb7c4431a47d9756e4054347f597e94b40bc00fac9dbb32714a47531dbb619dd315d78bf30d86bb85e63fbc7409e445b5080a9b96f4b36ae6a6c17468e05cec629a1aec495ff04c286c4d5c822a262bf006a28030b8f04798625c7e5752a77e2bc07b0ac80cc1fdba3aee44acc13bbd25641208ae925d5250900336f8d3158341f1d96d50fcc205e8f5616792f5690fdd585017d1540ade865e0d1b439abdcb424881162725fab6c2705307e4d36cb98d4bc08299887c746938d7aa4edc400e4fa0c6786c9ad80dc537c86754280070491210e7618f2246272010e11b134adff0e7dd5d4c6ba2ffe6799282193b40bd3b2896e97ef7a6292d4a1ae06e51998c58957474b5a118f9b302dae3a05d275dd32b7aa2a847726678187364b9934909df629e2d0b88b92ab2ed21e5d60e72c7830cc8fe71dc76cc9918d1118aa01f866d3466929ea0b4f51e66cc163bfc5a0b62411e8c33fd2f4c21e32b65da55d470973e0b0ea7f9148a00a1c5fca7f0fd89b66e46ed486bad37237f82c70a24677efe8d8e06f4c1a8b3d594f07545f03de57459c6d348808dca280c14287dbae23a1b7590f25bb654ebe1fd4c2ddb62e0a2cb3fc8d6a1461e542c92ccfd58abfb31ed3b50db31fe71cfa3a3222038ac63485d5cc8ee5a77c90870a836820fe167638d08656f022dce6d6be96b64779f0c6fd90d66b35419aa9f2eea891e6d9a65399a6e74d614374ecaef3529ca75362094d50a" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "6aef0a80b34f1e75a1019eabb83f872156a61931504abd6608ce468b37d1083f", - "proof": "3243e2a1fe7d53ea7239290f786f65d1cb59965c177bf8f87bc505fb004dd67666c2c8e958a0c77a685f74d5567d48ba6a6732a903889994364465ee6818ee2964ad86202eb5528e7b375fabb1903d11f5991dceb4e5c95604d42de2f2120d1ab2be1c53a4a8d83c8f528806a2472d87447aa4304e79006c89bc926b490f0f47e287036bcd55ed4679d2b60cdee7cbd3b7e4dc1aff97d60dcd7a364ec400a1052f7a805e621da374dd0438669b8550ca02387d7ee02eb26c65f2bd67cb98240c67185163f5f5560c635e4ad9bbb77fe501bc90588205b8ff90c355d50c4fa30518a3627acd2cdb09aa6c71ea19c59b9506941918b29fdc9a63fb650af9c1604b9c4fd70e2f65684c5b656c6a0be6602ccfaf0439bd322e0e64da1b773b55067e66d87c71c1197c73e902936f49b03ca70b1a49a3863099d611afdd2938eee940e8ae0b87f8271dbbe9da54f131988c74ca147af9f1272164102ee2e11fe38a4a563aad510cdf7130e4df36e2e2a817a308e6f1e388077ad87af2f19be11892054a1d0e7e3a7d696d15127c19dca9223d97b74471be5922625eec2f172cb2940cb699cce925533e69d30bf412885c117c3399eb9fc1858561c2ed8dcc22a59625524d9b8e57fec6bac644f145d6298e7f280f52695c2ad99c2bb677df784f8614b211875e136def5213f228f5f0adbf180a027748f1d0c811a60edd910a987170b6a3079d9d79b55d32b05bcf17b25eedf12f209479fac6d480a24600358a3a40a027f76984c2ed1356aa9229537da5e5b284ed085f73a57d157aabcc5f7a3f4586315a28a453d70cabd9c1f447f821b60e8b4c7c32a35e8db9db31203f19477e04c5f8f221eb67748bc81cf92da0b1f49e917b433137aebb2224b8a1f4c7e10f688d3278e541519395eca443d66b1678d91a861133b2f0fd3f793301f0beeb09" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "702ee43603efe7fd55ee9876c50a9015743b481840e8eb36db4891476139f132", - "proof": "747a16ce671073c307f880297ec052d2cc9f27d8ecef420eb00bfa7057c4314bb48a126f288e140da390f166ed4b689a8856b14d4a5c451065f29fe4cadbab744a746ab91468ed04d7a1cac666b55eb28ba1232073c10e8d81c30a0c3edbdc6f6cc0ef0801908985d9dd083cddb0b40e1485a03ee0b6dac4ae8bffa7cff37e7252694ea98c1fc80a1d25755011ae53c1632abf48163e08bff33bd57040c1120abeeb3c88682307e673f58634b7bf6b6d21e88cfde4cc11e8eec57320eeafea052d7023afb16ebd987f9f7cf6317690783c37a3ccf4cb99ced4866d62f5f4950fe690653da0bf31d68cf5ada41a196e134773fc30e6ce8395bbbec47e62a41242845b816d18f68c1db3edfa6b79730426980d767ab0e4d429b8dc056999648c41d66e515a1edb4ca53394fe64202dfa274955f3d9b37d77b02aaccf43d7f20c73ee513c6eb865ded8eb6ffc1402b3a44b39b755688bf2aa803fa4364a0eb0c115dc949d97124ecd86760401579c81b1f0cb4338c8cbf5bc196a1f6485dd0bb003b0d62a81f158c3cbc7e48219e9cb7a187835b9ad122dd26de48cd9a8584c195d9a2c3e73baf864068ab94cd4ae6064f750994b316a083b088b41f9e4d9f2a25bb2ae06aee4a703195e1b99730a618867ff95c13e514bda50520e2dd1cfccf74a2a1b10945f83bfa4e321d6868b9c3d0f111fddaf0c7c8a07bcc23da55b2ae7172438f9e8154677f81e726aa66ecc5e086b664deb6ceabe97c1774c6d22794415d62d03c1075d034a903214d5830684f40f60abc7ea4464d68172e34493e9a53e4cbfd2bb09a64b9d3c259cb5e6e48a05a78bd5848b372d44e40083e72446c56ebbce97d8f8890f617f09762440145192dacf6b9cff802a7bd5f600b0afc4590bdb13d2af340dd761f7d79bc10c100fa50a2d7228afaf41b24ceb2b9e4534720f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "9c5e7c7e6c679a971bb64c0566c9dae7e374256d8d8aa89c2d70c0262032600d", - "proof": "68db33c1745f8bbd55f6ca0d5c9a4b3bc3fd9dc772b01993b3d2ed1f771cd83098fd6faa6e52fdf06abc884f4ef53b78461e79b131383ce5463ae5bf69f98d55b693db321bed2581efec57493a5995b7c8f9f0d4279e3c5050051d448abdbf3e4c3d0e51106acb6ee6133324afc6212c81e8f2eb35a81cd632da15481f0dd46463def2ce8b50d0e4c307676b4b21c3145e60217568362d0689f1ea1972b5040b44f30cb654b7472d0ff59393bbc00b8ec9019b46912708c1e6d9b174b1c69d035c3bb163bcf5cd920179467f8e54a6882d7a6f797427adb58337ac7cf202e80e4ec51602c02f4261b63b84ce9bcd620bbcf9a14abb366680847e3509c2e0651120150ceb5ec8df1ce6a087f1a2b4738b262505d4c8346e2d01b35533def71a72f08b172e62039d8c81a4cc531d6835c3f5eb77dd9858fb11c931efb24584ca49167769ae53ad99f1b6bf7b5c2f3b700096ea5128a487d227eba42f431181423be2030034f6ae74875ba86d933a19f84ff83e323653dd00bb900ff8fa29e53d25da8176262e7aa464eef5cefddb5e464d5967398dc3e25d8ff8e1ecd0ef26747b908f358fe5f46a62e0b08cbfb9e00c72178e7b026e88b9d87532c1b7fb3f0863341090c41398d1c121ae3054ed787e8ef379c1a59d652c7cd9db36035eae4626d8d4a4ceb76d888b354997b2733536af8dd83540b8baf6f472ffc3b866b6616bcec09d352c11d975cd8a8e8426191b25fcbfcddf0f24c0273b48af7a3743d02a94f33c0dac02e5a77ae5da615c8621b27b9007ad2f01b61f9e2b457fbe0a761200fcd63cfc1855af7af1358e62514f79e86510f2bbd4366a6e798cc626b89156fa82ac7a57645c1f1633c9855754542d0349aa291a499fa8e484f519a19193047ec32417ef6cee435f40607286edb066e54d644053ad51a81ee25a687c99fb03" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ca8ea0b4faeb15115e9050214561467cf0e2686e03c2a77ade71992785344d7a", - "proof": "ae6d888f703d728a6201891c6ca75cba684903665f67b9c57951c053cd02e4342aa62e88c80041b0e9288405493ec03836b47dd6c1ecfd11e4913439b5074f3c1618109b2a5371b80838df9fc0fa8f9109cf9aebb22e4c1157d82e81b6243c401a456c0e95b5f6dfa46c2bded17868b3e12aafecb1524037d9ae8b4eba880d24f59de6fd60bfff6879d5ff34e9027e2950c505c570531b93fef0f78963d63a0d100fca15a4010b81cfa6aeca25fec30178830af153d99cbd9b295073e5dedd06d8d046f77b3926c71cc96964970737b36a605a52d16efe44f71d5d9c0c05070fa2dcd939eec952fd1fb53682ec19774a8cea192c1c5190045c458fd54e888d1236f4e1af0af38da87bcc0bca69019f8f4d186fe007874ab755d88386aa73dd2a54b0a3d45b90f4f90654a840d6dee32c633f87e9e03ed532765d5875085e1520fa603b9f46e27b320a1e2787ac6051b961fe94b641107137fb637d3dc0fe153bbe8fc53f075ae97e5d7f608a4104e440bf848850ac8e71883ad7cb205a14041e0a68f5e7b1393fd864ce0b4164c6f5ddf34138d0491c064bd846330d31e3d940deabc6cbf434d1c09b9b9789462f3918047575acada942a407dff0d1b3e9ca1c7834df6441f649082f252c9d8fb480b3dab552d7a513a5ddad308af38690e905fa83e2e163960ba995ab90c3db991ccf08995021a0f1711369e428ed125c57353abd0d54bb4c69da0a0951cd322788d8c136ea12e262585eeda8714a6d35f8424ce40b2c4f06f75e5b6066aecbf609f0baa553000db9e9e3b807ba6c9708fc719efd482820330069ebc4c6f69aa760d6c7c8118b6aa3ea02069c1a788d85c84317419c93aaaad30d57df6c400220c0d514789a160266c589c1d401e8821d0e0c6d8d52ab6c07910d4a4c89307c8b5203b4cf1b681a68b7ded717e9cfc565bb05" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "d0fd066a250c35914541e8fe97a0e4d49fef9052f579b032b64333d66d47ca27", - "proof": "4cc8d39069b401aa8136ed3fc3b13b196a76a062a1424d4431f1306ee0d465149c709a7605cfec77fe872c751193011c0cb696b65db151f836fbd48fccb5d20ca4d02e383c6c15400cf740add1661690578373afb7e1f08f5828503d5cb27364e27225cef313a433f46479e7d62b9f93b4bc6eb08347b51e3f40dfdc0e6b6650b52405ca5a08430c63538b9282ad9bdfe5016e94454eca009bec68709d83b8032ec07370dd6afd7f8ad0454d871a5d0a1e8756ed05490d24b6b828b5ddeb5907ae3392c34d053c8dc763cb2b2dcae08e386df426c81bc695cce24499c8f63c0ee64e4971ee4c027bc67e322a8f795d82f410fae3d3a9fbedb3f54462b7e6ad0f5aee327651154997343604d666c625efc8508c62d80f30308269f7a7ee8467125834da2a00919a5f82517f42b131ce02c9165f7e26a0815e1dd0d2a77fcf8257a0b4e102f4a80525bd4ecaa302ee478e143862ea000ae4df96a6e1ed390cc721665f9eb1e5386aa5dceb00a2d2a69ff84b381bf31c2061d4ca593af39acee80c365d3c2e1056a5dc866bfae878eef14add413b6f1bff8920f4bc91472f7f6c4afc6aa89ac03fafe2ec51f9c86ba3c841ad29ce2b4e5fe7eac1cac400e0f51f46daa9a37741f3db10dc3bcf93d4476582fc022c033adb9f34267c96b5f60ea660dcdc5277b0d3fe59b38afd57e24c3c89b6dec2238ae5e5d8122ba6dc63da977b1e40f2fdbe45e252e42ea04d4800593e8b0c309af6a8076813bcca079ffcf1078aee7487538ee2bbf4e3951d00d26024faa0211a454883e28f2b4a7be0b9f75ae88d4fd70c08590e61c996af26550b3556d60c49498f1662883529d663481d1602fdba12903cc32f9b94313cbe4507232d7b861542ed0c2b682cb4f16959a9008b7ed6c2307581f7bedfc9a825585ed915c9bacbc886fae3481726ffb517c505" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "da6ebea43c01a7a64befc4108fff7ea2cc45478e1341638b387e48f97e07e521", - "proof": "16baccfa95eb1afe41861e6d915fe5def50ad80b1f621cfa6590fd28e9504b791e1446d8c920737f285b952fec6fdd91b1d78cf4208ae1323b0039707d21eb3a8e70c0fd244a839b8306a229dbf2fa29fd5d73be3985c093870334674c2dea135698420effb08ebbfb8a7ab30b32c6a3f0726fbaed51184f526f3af2e3f9ad3ac35ddc720aa101632a9d9371b42d6abda0f6700436468c874c080b1dbd6b1002ae17e3e32727bcee8a7c084eb4bdaa628cab7a581fffca0df8eacb32878d9b0c1c6cb4ba93feb732e4a86db79226bb9c581249848cdf29d09f9a081bce792600ba9b00f6e6d3a5a37ff45a548b7271338a916c2d0823067c026c23415ed499333a17907471e46b1daa8264a4954453e11e02afa65a081574df97d6f3cd41dc5c9c8f1333f1607be0e5e333c55d11a4565d2d838499ce9a0d28dfb5ae1611d536e2e153508012505a989a6a2893d3867efef3f8ff2586fa17346579b4ce03a67da09dcef1e15ab99679de530a6ef29f5ed8c7f95333afbeb7be490384fc543f3cdc3a1a87f761c34cd21f378bc4e75c62d0bb65e0051365177f0f5e299e43f763305cdfae02e126a0d218b31c9deb63c418873067dbac10c09e1acaf4220be7373847a36a391bde5b4e3792fd28a0767b33018afb85b6f7496f528019938f493282c98548efe03d6e7968278a8bf0c88d151d60950e5c0d2c11e571b260772c594282eb1ad944e5bb55eec2fbfd54da2b64b239df42db9db7bac071031dfe356554c56d4d9cdff5d1b8a6560bcdff3008bf0b30dec9c719a081279ae7398f520dd405c1d78eaa03cd7d2ef1f00f05813b085b0d8a8c290e3c1277e4047cd4725ade06464a345f5e614557e0e257c77c79d62f38afbe0dad5b2dd6656bfc193506b4e5020232ad7761cc414a8b1c425b46458b84a2ee6faba600abe0d977b99302" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "de2152e3a4d20651e478538fdb8ca2cd12ea70f665a5968d9b3addd84a087265", - "proof": "2e14683c834b1a1f7a3107588620e1096133218103c68f97dba61a0e62f05f45f4852e539add5779a72c0daacdd5ba65cd17e4cea4a28c8ff8bbc9ab1eda792c9e46da192ba2287150ad7af8f58d9d917d1d73baa974a673474e440a6206931cced4fe5f414e607c0685a48126cf58b0204d8b269bb5eb808c4d003de041ee252db0f5d831f8f0810a8fe20164d1ef3fc0394ddd1a65f0bcc1336ed4981b280244ac1076c0788be790e6e38e820675e8e7eebf90653e2b37645bc3b76e0efb0ce83df210e702c23fea7b1cbff7a6c694a28bdc4a52c36284ed960033569bac077c5092b252aab02a0dfb451bcb4ff8af1f7fa2e4498b8412efc2a5322b434915fec7292f1eecbaff2806bcbb37596eb8cf487c7093fea913ffc8fd0a2e1e9740ecf40a75d2cc2f47644d24f1824f04dcc3db83cba64ae8f7d6acaad7e03e7b0290369bb7fb6d29dc08294b1223630a8f59a3069cdf609ac249f1a08554e6e527349f739fb5301acfc1fd9e5901593da895f6c796224906ce6df6cb2ac3e0ab19fc8e912b81b89b457608c2a055b0c7e99221c26f2eda6c2c008513c1bc76ea1b6c2a5b9933732e64380edaec3214c424f9278b3b3ebd0175e9043f52dada7049e611a6bba855de391c3bda511901761afeaf55a77421847f211aef1b21724672d0ae7572bdcc2a346b0269564c75a2932ee31e4b939622bc193f2f3c1e91a033a08ee37f422088a4c16abfc1ca2783e1410d64755701b6fc7ab49c0701c9fd1a3abaa8ec85918b97387f3371aea9d3f257f6f2e5e0961923245542be39eca306a6fe0f491a793615487ef2258a10da09a81adead250672f325143ff90cd3ee4869a7c6510898f9fc6218c38ede3145cb0d1b106df46c5d8bc18955a2cd2ed30cb2340bae81e62d63fcaeeec671d57b07af8c25bf24447c108d6c75c5fed85406" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 25 - }, - "commitment": "0678728e31adc97eedf2840ba93bb4d7fb9a17c55a60eccf5048dc8dddbd5148", - "proof": "62a3b9e20fe1fbeed9845e61b40bdfd4a86de41e95d059223d1d25820d616916c2084ce677f7756e619fbe74b0e68af5996cfd8d209646471854ec42e03fd730947f1bd892f425b8e69bcdccd2d0a0ca727a9dab12e03d2495d9608ef0408a7d4e51ccdbc99ed4b275f43a2972ce06230f1fb5059f084739e6904977cc22a169e625c578c72a68e1dbe0edf5b04b753e524629c5b1589c671ebe6b9f2211af0ef0992b4675b33ebd4812b1b5a5b02414f621a860c5064913ea3ac005599ec70f7d896a89d5b5d81e0980ee29a7cf53d580332596373c0589c5ff32746df3470174a5e100ef3075ac58e3f4c0d4837dcac06acb2226e015292e2d329cf023d668da864fb2ffbfc2d8c722624596d003a4b2af4271001826a82c56078fc5ef697f96d1b331a52241506a1a38942afd889b5e6e43ad2d7237265206fcde8bfe3014128ce1e89b7e38a074891cd5b09d332cd827693c646e527a530d40fe9dbbad59fac0164e98978f75b01f64b0d89d10d4b1ce2f43ce64dc73258b051c20b7395d1274508b9ca75a30f0c4e7c40f63e45399a42c5a1d5619b679c985939d69d97ab05c89684248b67e35f064b1e6add4b0343b9c50b8509ced06ba6752fafd1361a66714decef766d13134030219fe1cda3268101372599adf497369f0dab77a3c9e9852078f0fcf8b328b437baf5302fa3dacf954cfb82053b63ef786eb162353308f5c4334bc31fd8fa13baa592f2c1d5c7fa71b63ab7191651b5b5ef3d85261f8e96680662bf7d86791cdc9168b0f50b501b5545c5bc767f7509dd8ce40bd6116c1e052ad2cf598dca55db24611eda820cab0a9fec76446a2c41ac753fa835081feec977aedb4349c31de2709702dc4f7881cebcb1ec7e7797e3331466b990a360aaa9383d791faff2b0f03cbc189945b0446669f6e900b70aa592ac7e7500b" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "0e97a4388f23981093b255cc46e37145810d9b10f8973e30cfc7402265853c5b", - "excess_sig": { - "public_nonce": "bece804c74119fa198f23ad0f1cc32589b7c993814e0b4a61a5f7d0bfeeb661f", - "signature": "2dd69ef40fbc4e09723af0238f99cadf1fd19eb366702bb992ba39b886a2600f" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "1ac69244dddb8e724575f2a0b6c351163070a3f2266be953c3599db0343c3b38", - "excess_sig": { - "public_nonce": "6ea1273e475284c687f323610514a282c72742b67eb334ee53874eb324047c24", - "signature": "7aec5447b0d14390f6a902fc746df5d5798f28ce65ccd3ac1284fe0876916d0c" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "309e860f956b36ffde7105c65f507136cdffa7fb8822c9a31c68e032698ef473", - "excess_sig": { - "public_nonce": "4cf706c00cbc04421fc2ccc636a0aa29db7d238d82a9bb3496d45cffac84f07c", - "signature": "acd3cb6bc9645cb48b46ed7cd844c3fb15eea143f38a025042176fb7a218a902" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "8e71ea1ab8218ff385d41aeddf8dad024682338d31ff4a2f84a73dbec945615d", - "excess_sig": { - "public_nonce": "cc5d4c4e1c2e3d6b10afb973d617f977b76d1c7d14cd2635c53f1a3114c96779", - "signature": "7cbbf94a24c023c7993d5b821bd9691b25ba685c6599258b3bef205c88318c00" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "9c1440f7210feeb934a04fca729d6c51d6f436ca4573ead15d7c1c4841723304", - "excess_sig": { - "public_nonce": "184c968037fbb2b5b89ae790ea9ee17ae230edd6f7f51c12f27a0a1eb80f3f32", - "signature": "f8b7464dd1342e289287ec3b0560937f337d57f4ca900ade820bdba28f39ae03" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "261df0813d3fb7108ed8307d56c6ff5956b6a8871937f70fdffc6a48ae1e2067", - "excess_sig": { - "public_nonce": "6c6420dc5a64ee2698a9fa48fd1c9ab504a2d24fc1e5290faf3b78ffae183327", - "signature": "4b823e92dfecef0e6972bfc6dd175ad899685bde9f290c2ecc792b387241a004" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 25, - "prev_hash": "4bcda91b381989f2dd2900d3659b2bcd6808e0b3c0a3e5f003dccf6188cb9034", - "timestamp": "2000-01-01T01:26:01Z", - "output_mr": "7483d42b4d867b27e3f22486697ba70b0c102a1d8d64cbd0cd6df6aebd7fd9f7", - "range_proof_mr": "7dc8592a36e653ec5444b38f570b4361777616c1746cc3c74d2fea90491d554a", - "kernel_mr": "71b0c2a86bbae9d365d2d68476f2314ecc7678e792bcf704cf3e0537277f6e77", - "total_kernel_offset": "bf5718985e9684bfbca0763c9e9ac9c0feb7115bfbc475bb196dcd4b7f341b06", - "pow": { - "work": 25 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "34330fe2dc78cf6ed8f2e6493e63fab638ed1de7b422c1c19f7ffd29747edd08" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "46d8f6bb78e87da8f8020d1beb49bbcc4ca938e50c9d67c5a682a76d3995df6d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "9c5e7c7e6c679a971bb64c0566c9dae7e374256d8d8aa89c2d70c0262032600d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "da6ebea43c01a7a64befc4108fff7ea2cc45478e1341638b387e48f97e07e521" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 13 - }, - "commitment": "303680219244c897d9e9cf5e8a0ec570fe8515e11d1b89ff7c4f3a3afcfad156" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "504495cef8377c7eb5e386f9cfd515eb97ca3cefdd1fd4e2b2e1ec4fd609d448", - "proof": "441e9381d59a8fc64c3dedc77819ece573b3f9fefc4b8fe6ece2884e5b31482c4a667a141e450865c2fd54be55cff2459f23194c135045674cc2dfcd5ae3d02d42b13d3549226dff54ea4249788bd7f65a39979fcb93e981502d463c2d271043cc8185bb6e49940e532cbc0d3769983ca04a975ba1ccc6b1772d76e883952817d2f67d4b2131969637aec9dd327edadf3c8bb94a14d4de8fb5dcb52ac9cc370b4592742f92f488df295d8578409ce0ca66bb7910b3446a3aa5d8e1d6aca15d09f6ce4a2a3418d2667c29926f331990ac341c493b71ee3924db4d78eb847a1f085409953a0eb3ecdf2d36392dd33452b3975fde8a87a39cc7070f0be561f3c24272f25fa31709c5d92e3ee44526cc7b0d10367c85f567a77c5b4863fd96f39d096284409c16d4df22ab12e576f601c41e59be018188e47d9384bdcd685865f871fcc2648966a9d9608d701e05bc5600b5a69f0c740475c41d38a664bb878e1875180fb2b8b6476c11419bf0fa203e678f1b08bc7763d06a0125c817766acb7b6ad6dbfd47a43577b9cdbfefde1ed57f8e1c6e18bd250f54472f6b8d36b1763d1bac10d69b913d104d03477ef57c3d757da4630043c8146da97e7f7298c3503d7dde9dc3ab331451f6c7c39c2dfdd2541cb68becafb49ff100140ccc3a5eda2814dc74500935278f212bde194163b1716db6415858c67f26faa5ca0c498100e02ce058e8ac9b2790d170a349678ae07ee3a445e066638103c0854e70a24525e34e4e2020381113b3249eeabe312bb03358b4aa0392c1e5a9c72f18feb63cd556192c279bed2fb4e10a51ee5559011b9133d05cbff933ef342e05495c966a9f5a743fb92d84eb0081b9e2ea89111da50d42c587653e6b1ffb1422d62f5e6b6a6201c80714ceccd8e2c98606321ed4679bde59db2f62f5ab042e9ca0453cba698d06" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "58645de692f2f6f02e8bc16d74ff1dbd63080d0ab8c98c978797dcac7b50ea4e", - "proof": "787cfe8858fb71b7ba600964dc6433b72e87bad851f7c2302c5587b7ba38083f2c98ac5b40d378525e20ab76082e26f1fe469371eb52d7bb6138e806129aa040fcf0c135d16d0f9198ae722e43fd45fd16186397d942ecb0f654203239b955629872c1bcc73e3b397374e4e6abca7fb144af74db82b29a701bdc925133c21b03fecd75702f6717524e6e9fe8ceeb840be07e3d4add192caa6c947eb15ff94e0c4cf29e2db619dd213e8bc94988cd89757f4f45fa449a7132fdd5575f27f19e0025079898da9282156ad5d6026b3399cfc04ab61f794eba902280f6e8c736f900e04bc694263b1fe02c77b071474fdd7ca70205070e00ea575353ceb41c234810ac7370e46d521d8031cb6e7c18010c806a903bc8660b34a7019eb6452b3e2d0b1cbb955a8b209ed1715303c7b8d5e70b32a663923421f501b108f91c817b35072cd3c5e4c402a3fe419d0ffd77e9932da8ea01a0213d97026aead8a16f24ae182cab7aaaaf82c4caa0589044ead9cc0093efa19770f329f5f8ab93ab1db50503745ce8283d000d7dc6eb4a0b84ab07f992bd16a4c44cd1812598bf922169fe28d464197c5e2aacc2de09961976f10f247601ef8ee232e40800501d26153dc212c45ed29c8c12234bf707016fe43698731fd359cc189bc525ea7b562e56ea701168a4b7162dad2a1eed055d2bd51ad5eeae3f402624d754f215170dc789673c4b96caa466d310ff170d58bbfd9b1ae0c55bc07cb0c6385899cba57cd741ed087e5ad6e1aaf7fde2caf1783613135b3600f5dd9988b0f23e146bc6669dc3ce8034d621368bc29373fa867a784bc86b4a814697f11c88162fae24e0ed967d8b901c9c0ca98ced50a50473d4b1e18f5a0ee95fdf2c0d6e19c9a67bbd0c6e16e3980c43bdf8c10a1479223f7d1183888630b6d43426629435192b15c45685334d0003" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "640bede13d498818cee74cc7e08f476fa0b9a7177d04e282f40f143952f88635", - "proof": "dc88d269d42adf2d732bc7994890f195109bde0f2632fb285da51e797805a73b701a5b1ceb7f03579cc553041c3b8d597f6182e56b394942f977fbd0c7d67027824857c797c7dbaa4241a352f2a0e1dbf180833986bd5b6428ec118c7da10b1e1c5d8293d22c9b4dce2f628da4a399e8ca59492db894f09d2569f2f035508059b115fdcf3f905bca48910068f2bd1b4e1c1f8443f8745fdbf1d1c92064ed8c0fb1eae32b09cedfcc898b5fe38ed759dc435e79e196a4684588aed3bbddad0f048c3fcda88f964fc74edd1eb902f5b03bdf0db536088d137fd1e713086c31220874c0bc811eb031e24c6dbd02be348fbadc9f8733fea813593a52704476160133b6907cc63e0641067c6f6a0942e9676f3e138780d1320b566621d149c9092e50228b7c2ea1075e53a4cfbc62f9eeb676033a69fdfb708a1fe14797c3bfadde4406d62864fc2babb6d4cfdf695bfb7ede28dac7e1f3f246de0200fa59a0713057a07c1ec1a0d674b73d908989d136b5292dc0560a1e44dfb7acb73574a1fc5b2b6eb0eb8e6263c456f1a18ddf7fa43ec0fd80994b9cfe3fcb02aa877185db226dd4ac5d8f3cdad4471d0ea3f81869cdebb431edc98e023f212d24e08825feda3c2e68b849e2d65e568f915e1efb2240b9ed36fc2e1df902b243d77e1697bef02a245d2391a5cd57773d32cb59f613848daa9ea5946fcb0f79a47434844b1ffc3b4ebe0cd7a8cf8e08c20fc6d002320c135569676a6bc2a718e00762e2875674240c743bb2860000d11e7f26ccc04ad38f9d3798dad5c1cbbc644cb710e479f733c645c5a0f170fdc7848cdf1ac8dfff7c0477a1f7fd68366d1b09757a77a9b32d93e519912f41944048b0b66265469381a15ba61696b67a4700e746e015b9270db63dd510192c5e361e3be6914a3491dcc0806c75f78981fd462cc53fe99ca804" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "86857d6460217c1356a21bfdd78dbd6d95934531478992dfcafc547f88c82d15", - "proof": "c8d36400248f431935a91e3e56aee2c4bff3ecaafd4554854d9d72a47eb0ec4c4835da85e86d212be4feb9a125761ab114b9a235ebe9dda504e3d2c52254aa1f70db9fc0acada9f83ffd05b8faaba2df9f4fcdb61f4c007690cfc21550692c5cdaa022717ee05f102845d691bfea8ec24294dd5b93549fc30ab1146a0ac3b57e70c5b6ab8bb89893927aef5ed90e89f1b4b4493ca890cb75807418452af5c20241066093b244c2c8dcf4ea3f3877d631eb63b284bd0d994159fa219606aa0d0d2a2cc634709276cb9df00b4667320951f2ff288599fbf628af9868621576fc0a06cc437de916b42b178c20c535417625d6f361de99ce0155d1af9774a6e1b6218c6b0d18dd1a1a88a5ca313bbfc0e22ada34759b888db246f81e87d790cb03426e1aecaada9e4f77be3eac683f0972cd13fe79c5152461953cbb536a58b6767f6a52d84de6833ab8bfa30aa4def51b33c1419357749fadc9d4d671d07c270f7562ad896fcab75c676b0894900fabdfe9b5a003109b5ea4df53f72ad93d4a3b71846554c1c6c4c2e0c82cceeb94c986732446309f85d698bdd804c07309e14466d48e02f8f4b76fdabc3e29476bc33cb47039d86f2e7e5a900322af0373656229663a6f73c78699f8eb5a732d94ff10ebdcdeaac0236f26dbfe91cf34dc85433642205d935b68939901ceb65471f7dd8437d2867fc12e666bbc9ca4f4a8feb84450718221440ea2e08b58275a768397aa554203c0130d6730938c073c958afe5b6ae2e78ddecfcfd0b4afffa1857596b2de9e641cee691d671cdcec1612d7336068ee026a931c9a9f32bd36c5a57f8c342a1b46afa63ef0b4efa4cb807d9989415d2ea7fb2131b2ba2bc46099081229fc489505fb107690541f19584d3b888a0e6b2e557afccf00667037e49ee64b7f5689875ac71e37520be72c7a6cc397dc0a" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "c0f5267e2aff682fb9efe7bc6bffeff47ec4555cbb1fd195f8e15efe72f5d65f", - "proof": "dc5e64bd8e8542f9d2cb01221ae141897e0056cffc137dffc30b64348ef1cb62e8a073174ca9bc86c20e766ce160938f051fb8e00524dcc0185723150be722296ab9cd967258b268ad0d42615bfbc800594fdbf1ec93a3b6b07011669a42ac5b4eae8e70ce9b242085b5088281f149a1240df1336eebfee5a652be8029bc132a092bedc35e03ac82702340ca3a0988bbc52b0c8c92ef7edc2431de8a1128a706d389259e081ee05d9ac5af50009fd827a48528eb74baa7b1ea1582eb7caebd0537535384cbf025801eb8dff68837b5646d5a4d9487277a754da556fc7e9d28017c2454d45dea3625a3063fed2c5449b0de7369cdf928a6b958195a1851731e4a3aecca2600c9b0db69c2482a071f5b011428d1aa0802fa904d4f7a21aa8cfe35182af47cfc62c441a1a258f4fcbf36bcea20d00c90d89865b6f3b679beb3155e5cfa0431b96b7edf72c3c335a54abfd6884d491db5da7a323f56217d53c981774839df426fc07d3424410a022e9b981d951baf6fa6b68d6397f693269041e6316264bb01d0751512d628574cfc379ba82b06ed36cd0a4bfe3e7dc1ea196403276efd5f2a6934291328c213c63ff5b5d9aa9e9be5518825f9afc5c3610b505d5094482768a08f433740f0a0f1cda148d5698ff1fbd8f1147a89a0b415ccb8bc5782275a96026cf520ae1bee1ebc2e43b5a27af9e8dfc164a1d0feb1127176694fe01c94445a739b61deb16d767e215c5ef17e8626e1b6fffc6ac98e5d7e34053358d60b8f25fc9c5192d478d3006c0fdc84edbd2c931b19d8dcb1d16fdc7bd266d87c70ccb13f3290c1059711739d743add2aa88e99dcd65a01032c0771a516096a15eda537bbd7934da734e5310a1a26927c7360098545cf3b5f540af95cdb0ed7981ecf2d1f80061255d87f9780de97d2f5eaf8c1a1b79310965a6e8e12ce0a" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "d2e1726b88425d6ad36bbf6f8cfe382b6651616c0d90e8428bb8c75043f4d067", - "proof": "086bfb7a44e18d88cffed5186e57759703be66fcdd644d36ab5e4511aff11805e0d73527c5e9550900cd6f568cab343a3bbc21c70d484f9608ab20ab6da6360e780575d33a681e29624a58e7303af478f5b308faad45788fb1df4985c8e7a078921c6f71c6ddd923127f08e67695ca93b2afb183ad01360f44a5587ca175766f675ff54a90dcbeef37cfccf07b6e361c5a1bdac0274ec344272a882a4db3db06e07dd9f8a8808a0dec6b724e1f987110a2d537b71343f2d5519aded61e29c40ff27e87e71b95342449a6e3273ee4b5335b7d35a0e45b1fd1e4905dae716f01050a425374ecdd0fd550b2b562a160ae6d3c988752d401f2785f94d621af2cb15d2eacabaa4d1d6da31f1f2bd9c98dac404e289f28c7259f441676ff542c12a1063019b9f4f44a097793210eeb0721cdd1f19bd8c057a422b8a483e3354e43957b08429630a0c95cd84b6983ffa17013fae9227c774ed0faf7db27f1411be445180ac6bbdf5272822e12b7f161c097f7bec3d46f7ef71dfd2d1b278be4844322491e4bd57edc1e79cb88130d03c9c71b80edf702cb32acd687ecb6b840d74c1226d890a0831b7e02ae055a766eceba59d74ceb0838c5029b70820d81c0b36765012e23c846b433152c18b7d96a313b123fe9d4c3def87d099bad73d0f4d6dd6b6634a3aab7133dd1a9747cf6704b62b9f309224d1d72fced3c34df6ab565a19e07e269629ee02ec5660d2d8657e47e92c8ab9000c86a287e0f1428a9302760087d0a7634e79ac9e9dc698b14aa992c251b47ba876fbd13eadab13e0bb1b0dac409de3f4c3142b715315877cf0fdac68a4893acf360817ead940349886b0547be6d7d19cd1a87e1f2cbc5334831f9ac8a4e153b6afa038252ea8c679b70291c94025426e9d895540059d1ade207880e70c497f5a4dae70a6ec35efbd3904f930b06" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "e4c86e76b5b52912250c8063c51bc53280ee5b24a9901036699c6376180b2138", - "proof": "361d27734746007b19f8e3540b8e675493ee5f4bdca9fcc73085be7b5f966f12ca27955bfab433868b4566abfa07e104e59e86f78a5917cc44844126a954fa3ca498816a2bf56101e414976aef415a957e0a00ff85cee8bec0d157bb0f98c85e6cbb34ac57a61f703e7d789469fd903bb602c77ba9c474afbbba09730190b31187cbeee27f3f0e74fc391172bbb815ce7244894f67170067682a1406bd0b160342f2aca072ae93323ee284e0d6a72fa31ee6b11611d64983180add74c3ff920d50896c76ea79e5d237943675f600608ec7cf476eb868faa679b86cac6ce1500cbefd23f24f116df719625d7e9f874f461ff06f01a68b35b6235613cd06c4203b7ac1ed2a0580da34d0ddfe50d707408b8dc3f48f323676fb9cb78189d88cc33f9adf63635fe28984aea8e1478daf626630c7424bf525e91c02be8f143bdc84652aaa745799384ad9b38388c8c11753e63d68a4abe09e07b6ad85dfb42a81a8397c9c5dac4ea9763a98d48ab6f7f5b15ecc9e1736fd0ddc483c2766ec273f4b7b92b272aac53788e8638626c985c146d72faf05a5f095297ec9df70dcb84ba741acc8c2ce347a63a1c2989705a8280e38f10097b43f619277855757ddff34eb168c37b9d1541a1df22ec496ffe31dcbfc9c32c58479301b942e05557db05a1730c05064cb68866db1fac9a4773307b9f68ccea66eedb32939c0715a8ff29e0f034c30878055cc6325f92b8dc623b12ab6eec5036b287b9596d3e3a235e31cf01236e1f75789b7b01ae7836e929185b7b4688bb2002da66311bf63a5cc43ef732fc8007ad4278b62d224c5cabdc56a75f453fde8507c2a9a3f222ab92921a63d047595e5f0fe322c18faf9a59adf6bd71a819d9e3f6597d33df519eafec05e6b067a29b4d5dea0d4202447cd38cdfe8d83c8a13c5dd6c1bba7c0bc4db9e19f6c09" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ec10726fa7bf2f35ebe687d81f19ff7c4c07306654b67aea6136bc5d8818f977", - "proof": "8ee61750b9aeb88b8b503c09706efb79dd177f3eac49841fa5bcbbcbbcfd526d3e6014313af2b29466e4b3ac1db6422afd1ddaaabe28e4000399971c478a034cf8dee34fb74ae4dbc79d6eb95f95b30b8e93d45fe9316d275b05332c6dea582fbee6e49975ebb7b9509f34ce01aa8adfe1799b2581a455767b81c550ca6d0c26da23a7b29407f0ff92f3403b20ffe159f3063cdafac55e4c61ae7c08ada5480402a1fa2cd8dad611d7ca8231935dc3c93cc1fd73ce3a7405d2da4818acc91d0252328a3da1a4ce9a9f1e054f9a0f0d44e2abb369cb97b0c0215bfff576e9400120e8a193c4603912142b8ba2e0e1f71ce3fec54b477a14b257eecc4d29c9796558f6f8ebfbc065954dd80a2a7a1a4b17631ac4555ab6dc24b940fe029874ea3ae4ca5e453613e3e445987bfce2f8005b70a2a0067f559b487f0876aa4db6470b1cdfb552153bb86a037481c9bf3145460a3dde5a6359ed9b78a82c727186d008126b4d5968dc250728f1dd7e0dfb2e501c4c7c70225e0a75cc7f91e3e7a7403c3843716a75fe745993937772103a2766f36fb4ebc95bc5038a45869bd5c03b3920efe7b0f13842a256369e39c9b8ee97d33cf41fb61bb24a81e4972d2de7b93cd4f5cac591b6cc0dc1c4dad1ea275b1b85e37432f1243ae1c66cf5e2a7e73745fcaaf61e2935a0a5ca958e6e8cce4380e3eefd9c7e9a38b13b970c9f5220270fb4415b13df4b9275ce24466dc179240de0123e6650dc23193a33070a7b49b763ae7df24a42bd9b3977dfc8d4f5707ef98d3408e1dbf172b6ef9396477da2154a1a6766ba06bbede6df50642a7d0fed32b399058bb3b355184d1da06b6e5bd93f5d7a99fa56c8464770e4c09861fa2cef66f77e840fa857f851b3966d11939a05f600d8947a658f76b10dc972be0069bed1dc9aedabd44a53186163202d089b01" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ee2719c518d34a159f5d4e835431854b7ef9a929a29372b8d75349f8e23f480f", - "proof": "d2e60154ff093bac69b9645693810ccef57382bedaa45d4b4b2c2a5075a3f72ce815896dd98df7a4c67f8505dd3f080ecbe3b76ddf479158eb26379241c1ea1e187907eee8bf6d80da4c1e7c003133e49001b9759e3f6a37e52477362ab6ac0b645ba954e45afaffbfd27d361fba632074aa578bbae18aea55132d4884e2ca7a4f453b519267dd0a164ce0976ddc6564a00bdcace360f1d7b5f8d697df1de50213a291ff42c24bb64c73e0f4bee9c9b35655a6d3e5095701a7eb328dca597006986258ce45c9bc48b063c69dfb2c255efc9c340c9135ca1983249ba165c83703ec63030255d84fe53ec38679b9acd8cf032fd582febdb26192c63738a8dd0b12767b998f5e6765f715137cdf661e92bbe564db18984b8090620e80c8dfcbce05ce6b084f2991e2c58f473a170625d56c2d1b0f8213fa5ce13323e7cd5eb8ef1d00cbd2afb01d9a1a5ab2a2b0f18dd3cd40d24fdc5911668a33ad9a7e2045325cf2fbcf47e9924cfbd8fcf23ddeb4be9c3591ef73785f02c14cc0a9fd97a112387434b805b31e7cdeab8d6d94a33d68293f8df56ba831686e45c5b8cf72b6e2665291122087bbdc9c75be9dbd8611485527de256250f29e14910e95296055573668609605e497b36c37eac9a97f017f8c5b166c10dad482490babb11db3808f15fa2ab54b6b3b60c331e6b5306fff7e47388acf187a700c600f96e16ed28fb9330a69b90e21a94412cc1682495dc0b50bb0ad96d4c890272f7278afc666247a28ba2175a6395d4ee50716aaf1ffd19f9a21adc7a9fe900faa3782a07dcd6b1b48b6f188530901de9a771600a588e822cd73d1dff8b17b7aeb3d431e5183ea06105be086d0110df536501499d39165d83427046d3e3bbabe13ce1844593bab7f0dec4b073ceaa9b7fddabf843e9488c12b8090e531ee9dc36e328f98a59b0d640e" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "f64f5be6c9ba6d0fdcd1549c38d08635b1b240022d387fe3e31e2970486db40d", - "proof": "76cea243362166048411a3c94cc8bff5bd99d986921dc45709945c16e0410e0b66e84701063bdda914bd4d61c7b32a2856e18e6ac30422b1c2fcfb13107435107ac34deab15bb5560395dacb42e202b3d944cff362203c7429d7e18b27b4713ef018af7b5cdad09a4d802c79717e87e2245c4e446126bba67c17f16c0d74585c89c82eb7907e744cd870556f33a70933fd0628518561a088299d506659ccab00697c27422ada04201b0e597355ceaaaea8a480896ddff44aaa608631426e6504fdaaae2eda88b5e4e1fc3ffddd37b0b2984b482217b7b338f84e8d71482e2006a0ac36f159e65826d3cbbff3be79d18af0e28830cf525863f0924a60b0f6cf1c063f8210ade3b62f95391bef6835d9679996d994d2f8e5281fff7e17b7e79e6bf4236a62e07c80c9573f5ed2e946ef0d923344281917745c20f7461e4173440d26a4a253b7f889a7b48eea3126799a5082f2438af80f97f6e8aebb04a0962f3e6c2d445349a1002da65687a9561d1f84cdece844e4ba71fd4f0c53b8e9815a0ee860174695f1ae8f55faaa4eaef185cd8227338a0aa2a6463dbe5a3833e36522be25ff44421d65a37a0d68c870097daa85da21a19146d4471e985ebf216d473dc0982fc758f7c50bde6d57a1199af485f5ae9b4a50d38a278caac512e0d4b132c6d7d799a4d08355ce02128365696ee930accabd513783ec1c33764b107b433fca5f5a7c202512ab90ca1e4515ec239023fef237ea0701e1f7dd73e6aa2db42b2a37f637ebb49d17cfcd8e076b6c183470a60f1177f76a34b2983b5fc7352c0f0852ef0e8d7ec370d1706ecfe3010b14b86591adcfcac7506be7ed63a3c5580d0c7c40e96e6631fcfa7edf545ccbabfdd779af454b216a4194b185d95d672301a9dcafb100d10d35dcaffc6ea10ea03a24011b220cefff952cfcadae4757360e" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 26 - }, - "commitment": "b23fe569b3a3e62db7cb733bc4875288618cec1a169743008353a9f93dd15545", - "proof": "3617de119a31326ecd8c7c2ca5f0f4206cf5fb44e5b3694bb35919591395df77decb3d5ef40d627892118933190b9e206687c161b26104717a3bfed0464267173abf3aae56648c090af3d9cc69912c4395c4919c68d2209409b358eaceaa1618b6a228fcde91695763bdde16acf1fae8e5cb945d0dd1026dd39e3873614cde31783d14f1bf2426777a67b0fbfbbeffa5e1b09eb37b50103d30e3ceec9982070550b6eae2515b690a70e0109e03262cd236e4a013f577acb156adee2068ced8092a1c58f8e8021acf3d8fa180c47b00ca02f1a38a5c5bb403189d850f0c3f020c98a40a1bdced2a3890af59bbcd438b67f5c4587dd63d613672301adab57eef7398742d2678dda309a02b2072a6d041056f41f8da79eb75bb108239c112da555db8a7b895012b749195606baf951e146a3e0a37dd6d591883faef51f1776d94285c554760297f92888ef8d9bb8185873385e3d81fe55ffb6270f1bb38f4a4a128fe0e533fbe904fdd1759eb3eb0db8290b7fc548d215951fb7dbb9f6b40ceda0ac07a08d5e6b264cde9ff88bd85c8b78947597355bedac515c21c4515ce6984050af4ed5f97453d7e4c6e567690c84b43a2b65208fe7959239e0a572f29a09b2a5234bf6077d94cf580b0c101feb3c1fd6d09479e424b5df7c08f35abd06b2a631e03f361aee42aa1e541944fe449ec5dab51ba2db20635b4a6046e85f194f751aa351ef7838a0f3ceca02d763f7e0929b3ae34a5c2b06781bb29dbd823786666cc92e3c028e522d6d6a382d83eb688f2ea3c2082dd5246bd40925b2668ff3e4878d0e179abeda45fc1c21397084b0182277137a2f93956cc82ab9f3582e6b940387bbaf2155b82bd11742a2affc22fb1a1841074d616cd87976c45ba289ea40ad52d3e68d92b08f43562b35ddef5749ef2b13656d1d2eee3cbe678d8b8409e05" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "04b942cb7ac9ad300cb118d402478a417b0be85d0d59ad68678bb5c7a182df15", - "excess_sig": { - "public_nonce": "d2fe8827fe8ed2f6bdec08b8c38f983b3c9d85c0e4c950775574413af3a86857", - "signature": "f07527f129811ba2089b9a1cc4da4253c2d02b8202bcdc7c5842772601432405" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "4ab5a7f5973acb74a76cd341baf386f30fe45d8fc2b1a45078bd210c99cce752", - "excess_sig": { - "public_nonce": "e8aba5b7c33e5d4f2a453943e69b953f6ec41b656ac767bd37cb624e7d676c41", - "signature": "d2efe59fbc5508f5908a29f1788df1a303851570677708b38e210e6e837a5e05" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "6c28ae769d5285f83c40f85978f032bdf6df0ccdb32f9975ee7513582784f14a", - "excess_sig": { - "public_nonce": "825ace444b7cf28b269b5bb7f3a1db98d1c0fca6c8f5e2ac71903ed23f51b67e", - "signature": "6d116f59c76b0a64b11fc84445d6230b0843c968f3623bcfa73c52810af57302" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "948309ee8fb4dee3d10d32f6c3ecc3500e9c4d26065aea27ca79cf2e1891f40c", - "excess_sig": { - "public_nonce": "5cc2e16f2b9727f4bbc694b430cca436b3bf6c5574bf25825ecd4d571372387c", - "signature": "8c1e9ab3d550785be5d4f279220451b86f2d0c71b70ad9e597895f0f2334df0f" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "ee0617929a432c12715ad2c5e738101831bc4afeb8e43ffe2b8d2395ce18d84e", - "excess_sig": { - "public_nonce": "c8ea368f8e1984d1d72a4a2db750914992db7f882fcb2d438a2fa44c5abf6149", - "signature": "868b06618e6629725eec2a942a7bd68e7f5977d1a2567690419abd7d99283c0e" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "746a5ef37e323b6efe8f43fb3c0ada9d06d5cc9c34b24112cb441ab56d3b9e0c", - "excess_sig": { - "public_nonce": "9692e55b526c85287ef0d4ff75be10b91de888a9efe597631cd8463f7158bc04", - "signature": "997badb65bd40d6978580157d2ac725ff9c6cb5d1bff3e4d615db978c25eb404" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 26, - "prev_hash": "80a9514d8aa67d5bfe18bfa847e68bd707fcd2075d6537eadf2bbba545654d0e", - "timestamp": "2000-01-01T01:27:01Z", - "output_mr": "0eb071a321fa73ff29d3647e0ff9012e65a4e5a61e5825da8041522dcda38cab", - "range_proof_mr": "0bb7a19aae8d0e41bb407c692026a7b483992ff3399638a11ec28ecf82cc9a6f", - "kernel_mr": "4b5412be78ce603df4199bffe7e48e5e8f5aae1220f2c4402c506c1d296ef655", - "total_kernel_offset": "715b3e604725cae15af47313dc05fdfa800e623a726d7b9421bf3f42b490d70d", - "pow": { - "work": 26 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "640bede13d498818cee74cc7e08f476fa0b9a7177d04e282f40f143952f88635" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "86857d6460217c1356a21bfdd78dbd6d95934531478992dfcafc547f88c82d15" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "c0f5267e2aff682fb9efe7bc6bffeff47ec4555cbb1fd195f8e15efe72f5d65f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "d2e1726b88425d6ad36bbf6f8cfe382b6651616c0d90e8428bb8c75043f4d067" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ee2719c518d34a159f5d4e835431854b7ef9a929a29372b8d75349f8e23f480f" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "4a3635272ede6fbae6330d877a5bffb3afef32f2e21b262b8de85cc52134814e", - "proof": "0036aba4b861f1675f7c424a1628031e1e578f007a172909ec5f964c79387c4c0ac4d2590cf949649414a487337202faf8aca1d7e67a5a5e76d86b96462c751ef2f6130f235d79753ac20cca025f80b54462dd20524f1ab2c490cd9afeffa05bdae89ef268370a728676eb8c5d404bcf19f41cb6cb40b58d5ad1d4e10d971337bbffb5ac6f9fa84e1c00e7d52d5c3dc65cb1664aca066fea93f702ba9abbbc088075b3886e3357a031486baf8633c5281196e93bde8155e1d7435108441d5603ac3925dc356c937c946ea00da15721f706f67fd5c3b765d14af2b256d0e2230746f937a6059a44669f49631df59896c864dd8131a7f575dd70f66ff90835f8285e420d7632b20b7d165406e2f70a88b898910a87178c6d2673fe188ad2663f288a673da864f29440885457556d9deec15bbbe9f7b054667af8c6059ac62d0473fce71e926303be43149bdbf556307ab85b47acee181c623217815e9c2bf20e03982d2b9ef2029b27158a3691ee6d3154be25e6433b060f17043305986c2aea560855f34f49ac34ff9a891ab4b8a14dc0b8a42754f12cc2d8d4c5551e2fab523842c41f01cc8caee74eef55ead656090e47e6d47bc7c83461daab8f86d8d274543e7f34ab17085015f6658dc7e721abaed5091c647677403fcd3538d7e5ce35042c54e5b609a39aecc18d99d97950aab7695868e8b8e710c552fad40441f8e353da3035badaad8cecb3b7b4da938cdabbe6dc12fed7ac5dc5e07ba584a2464f22a2eec6ac1cde73fc839c8f76874c98b81f3f7fd66eb93d2d82ee05a5a225bd407e6cdc16920f3e6293213210e3393d4248d6e09aa9b80812e56dd19741a5567febaec0fe6aa7aac7d7b068687505a52b1812df0c1769cfdf42deec6e170119018bde1baa798ef94fc1226b1d111a07e9a321455608add785ebbf0309f323460b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "68402edd17e210bad42d074496fd49facf33b35abfbf8991e760d3ecfb11b425", - "proof": "7c5b76559f7dda8421e2de1c04c89bc55067a2692470901cc73ccf434e7aed6736e0f335d1af5fe8e4a703d3b103c7eaba332b215aa1432e021cc180bd8a602dfe2be2d5f8e0cc6bfa5c53a152cf794eecf0bb8489e919ea8f3ad1c7a574531b1ea227343f5b20a233b84c804fe6d6e73359660cdba86d6f2056089343be9d6ab38f0f7e237679f05424d75865edd3bee2aadd71c772e0ce5958b90950b0b50c3d6cf617a7260379a4efd5779d24759c57338f594d05e5a650d889119c37470c0dd864025ef674904bb630745c4282b124abe0088b0e2079384da93cc9ef080966dfe8814d41d248afa5e88b6b99c38e15af7616d4d20b31da2f815aaf68283e2439845237895d7b0b483bcc7e00bae9f2a698777df0ed97727b023c2fa34a6a8e1b96accb7f97246690c8273a753f524bce5ab183309853404f78d718f58a31ce47a89323698e9281b64f924355199b9107d49e3d0283ee1d06adc8b2877d6b9e073f01bc86f7e576fd7d6a7723ac1bd7f2ef0ee19d3e33aca80293f11cd71792000daeecbe1a79977b021ba9a1d169040f46962bd491c023567fc99429be5a045db1080ad37376a30d2ed28acb2632c361754ddb0855cbd26f91d4f6bc6762d6e8525dc3f1d240aae82bed45c4e6131dd4ec0c8da513417cb4f6470cd3571d6a9f191ee19326ec04093be9e7f1b8e40c46c201fd4812429400a00a4da61317e4741aae43a3db2f0677ed8f92f8bed1f8c91b5c6268a18c23ce4a849987ed6e1063fe2ff5eb3a64f3e2dc713c03e912e9bb332349a5911a88090ba08a2d0c244ad293d90d3f864361e8b65504ba1078e4de30b82d4f90c9ce02a3e3a3a69809c9ac4b3074e2fac742605aa9284b82bd80bd1f85d8c002fc1de3ac138afc040f20b928a7a487cf9b024c8589c9f4d76476dc0f233cf0990cf894c5ca24940e07" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "8e741213b2451177b649812f8b42e9974d4dcd8f71326f604283c1e63124e750", - "proof": "04b3d07fa90ca1c4d8806e2119ff68db228b64df9e60ce4211869c0a5098db6ba29df51ec846a3e4d6b587d836c961f5d573a813194344d183c1dccba162f13a74cd4cf8f4e3e70409087c043c3aa9f7299ae708e403031ca0f55d97f2e40f4e680d63f95911de26273a2cba2d3a47054ff8098b63a8f65252813c4a21429f6d853a3ee4eab5e821fb08da5c58fb3db4cf2116b9f354865109f18cf83fb7d106ce31bb1805925b74957a87d8c6d7a675d65f8495f27927784f6713a135d95e0fcf283748afd85b70b338aaddbd0e00836929b2757f107f08faae4dc95e980e072293bae2f8a59dd8ce5e32051f4b09aa9c90c113cc08a646f287aa27cd0f7725ce8a2fc36673608ce9edf330e00e200b2c6118347d60abb50c77b58abb1c9142d45306e7246e99fc2112e57a2aab901614cf8c9ab33f9648b5e613200feb406234b81a8825bf4712a18467dd025c175957505092141b655de91d3676ca37cd60b059d59e2b2bf5d1ba0cf87168a729102b6fb19d72a353b43dabed3e0bd70526a4cbd1bc13d85c77a7bc67307a6f9ea0ad25ccd58faf5974f700f63473588034b4a5c1d5cb5fd0188fc1e99d18b7cec66e1a9bf2f461fc218b30088f6291dd130cc01d470dac44be303b62c7957677d71337c72ceb50aa93c70775817909841eb2cd519200b7231a01f5e0a110f5774824ac736e1cc4d3bd2aca56618ab251643e3a300bab9cbcd20b2b3d5d144f7de9213ae9b40c2c9c52b10e0b6cad9d714b642d137ef2c7585a6e7e518402a5f79f3d1bfca9f7ef5aa4f161069a0f5aa5725633608e56887ffeac7bfd60235f6de2c3357b073a800f54102b9c1850baf83d149e24d64cdad30e694ea08f8cdda5ef2edbe4fc3bf5bf17f4a376d72eddca08174dd461f160dc0eddcae51c09c57c50cce4328dd9f3b9a59f0133acc3ff490a" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "9c80afc1e5f262195472878e6ef4807dafe15803343a653985340c46bea9bb0b", - "proof": "3a072954376c2e843e7dc3a20f9b25e226d6c09eabc67e93536e37938f7156096e0d1e0d611df20412828f89ffef5fe6611d333516c5b4531c394b54f1c915557633c29d34445da337ca99bdca4475a92b8b95cc2c0c8aceccd17e9b0de00e24fc8ce5227fcdee72feb15cd53c1a8e9e9e04f7deab58edc69da3d7f06ee2ca4bb53954fbb9f9c883d5eaec4173a33a50373714d4547d1bfee8b7ab539ba2c6061c145cd8367f96ea6d016d7d3a5739f427503a66e1c9590728ff3fe7fb76da07dcbecb079651c5e92e0dcc1d9475412d17dd195e26ef82d517c8f5297ef58409dae61cdbaa2f860645bbfc08ed4f5cf9ede7226d33069b6be2d6b9c9aba95d57bc61c5deb7077d36e6867b08ea8e86eaf4003a7a7a7abd4237f2a67031d3e85fce5c451243faa8a175705f34e2cbbc6454432f66a1bb1ed3b1dbad8bfe9dab579ef8b1659c9be0558b04814d83911912af08afe3da3d249a3a34dd4173db3560a0467ee317e14a8a0851cfc54abfbf239a01ec2d24c3644f1243456dea59e73a741e031f25ef6f9d0bed50e87709bb8c55617e560517a7992021ddab78fc6345f6585106f0f2cb4a7a2ae3a4e5ad8ae816cb3c42d0c42f2589e26dc16f0436385ef5c7234b08fed06f25d2fa8d2d50218ef7ffa7b46b2ad5d84719e1bee8c6143cdc5901b2e63cb867f8f95d7e4fcb3f213ef7f09a6a5d6094a2366e9b4d587dec1e4fea20e41cf4ad80f1f1f14bb10657e621c3f825807d182e5ee0074c270a5ce218de76c18f11e3c0f095cbab04c48d2a832af3cc086f85fbb80a2b15a65d18649714f231bc3df5439c7a3c035ff6e3f0ccf283f287877ddf90153af10e07bff677740396df2bc839631d18eabf67f9e0b5ecc0c0a88b4506eab34def420b06cda1e90c0604c3fc80ea7fd485156989e6c42fec38f62dddb1b8dc46cfa201" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "aaeb52e8cef20a2ef7e1ced7280fbb545f8b96eb0a7384d80940f0833fdb403e", - "proof": "206b6c342d7bfeb0665b3e5171a9f062b828e12a93fed1495a3d061cc26ef84728f75078460e2bf63ef97ad98fa2b966ac68b3d16b3056e9b1cf169c49f956713e8d448feb393ca416a8d00f113ca97aee20471cf48fc7b088045a3279ba416c647af02323ff1705b0aa24dfa118ba9e38bd3d5bf1dc73f7236dab6aa3cff317574a6b91b8766a2211cfa27c84f97014c10d151024c965c6307c538b2819570a5463ff35e33b9e02d58e3255754c97d71f8a192fd209c85b717866c9b1209509d1d17d59c9b1806628cc3c71785f9b46804a24f86b84fc1552a1c416024ff606a27f731b7532f57935ae123d902e170746ee83715056f2e3e50cdc0e4912e767441d04f016458a0d2456f2cd41e662391f98739993281c84f7d9bfec35c6131d36c96dc4de73433081baa916d25d94360aac0e022dd09bf7bff9430926087e381c49ceab802b85950c92205df6ed4b24c9b3dd26bf6b490c143a9d9fd59704210cc6e213485a32beb01120818016a9f38be6f9f56bbaa7b6a2613fb65020a13fcef47e697bba70b36b757caa5d1fc61dc55e763db138a54289c335d3657ad04342d5b6ab259d3562eb7d6d7cf33b1769f1e4a526c2f57eb3fa9b5ef361280631b8bc14c76c7612ca65d66d3dbb4cf4138d88c7eda5164ee2a75a1c072b5c02136420d941229a7d51fcaa6ee65256893049206649b088d079d3394ee85f25be598edd7d45c3cfaab67fd0c7ad4b4c60139d0f288b8518c2ca3c14fc08143af71e484db68c229bc19c9793731f7222c541a5e56dc09cb08c2fef430ac445c5db1fec9bcb8f4718901c304a8a3b48eccb2db6f35df40d0f268ed4700c3103563d6bcbdf10eb2ebcbf3560ee64821a40b1faf022b70c3b0e5df641e41fd343007607d15654d91150361c7679377b1c4266175cdeae16bf34d2d4f7296a066d8db400" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "c66dc9c09ae371c6dc0277a4cb02eb70987293c42c0bf399fbbe998f9ed0b963", - "proof": "3ae6f417403d5ad7709ad957ce3fa1123bd21bf7d9ed8d9ae6f03d1da4f3657884ea2f97b919dd5b38da2315694b1ec9cb2e2f4fc55c2ac963219aba3216f8194ed534a20eed4958c62e618226b9943c29f1946309d373a6fae2dd0f61caa4738e77027875307cf2c7f1673aadd9c7cac59e3f76681b82bac4c8b04a6f25667514b9044809f1a2c988d7985a159dd6c033f22137abb291949badb02327734c0a8855c99f7b45f4f98cd0113647773bd2941a62ecfc0357b4243fd65e8d448c013e83b8bcb2055dbc274023fdbc4202ed3ed7286e16a9a585819fb6876fc8bd003aae8b359997bfb0db4b7262f7fe20c127e92b1c653a382364546253e4cb1920faa36a5bb5245f5ee285fca15fd43b08ecb7f046f51c4e55d56153997d4e387980e5ef363277e795fa4c6d77b730af300e9b1270f8ba185dd1f01bbbe4be2f00b866b564dc7f6fb4c36db02991e5f6b58561eb5a2b2a52fbaceb36da05f4020680b8610c6b58e668388a78181a91dd2fb2aa4a2acd7aa9d8393a69994ad5171ec837be6ae7ffb0f250eb57aaf3726c3b93b0f49958699bc273d971f85d3fe91a46020a4fac15c727f9522aa565b62b528546ab26f9fbf3e736f6e43e807bc764f29f8bd78b0b3c73453f995c13156dd33521a3e5dc50676ec553b7214fbdaa470e71e604fbeb320315b83a54db84c0f38c168092c6050aa7561b64d3c8d990412211ff80969e88a9fda9f6d23102a45c2fbb206dd1fda12fe78095b48b6a215dd2c265e144058d915d3e72eb0038ed26d5032b4fce35f20c798c3f625414a137d8673ab6d5cfb7be575bb5d0c0f7dd9c1ca16bbf316b21c2b87d0f69cb919f10d5dfbfb1ed157cd35cae66e8432587a08c8e0e21cfc813fec896421d08d81c05bb0d012b3668070ef4d04c3d7933dab803b935aa7430a1f7449a1baed1364601" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "cc08d9421073875d06e8d2d0f7085ff86daafc5f57b145cdea4d6dd8239dcf7c", - "proof": "424c9d0f60496aed67b3cd1b9e2f8473400685378e1458d99c80e94ce16d5e68bec4405d706071de6daa4440ee67c1f0ba84fd6edc1d4cf681154f709b2b11420ebd0f1d3bfc0f01f1640dbd01580e520c701f86ef83eb0b57b86a574bc81d12cc40d844b97e64f2a936587e5a94284b835d8b6f46878d63ea76b1ef78f64160767b9e38a14bcb45a7f29c47be6ca0f588aa84d0edb49cb68f196163af225b0575f5c8427bc11057e0b233a2035cf56ed4535d3fe8915faaf6a9f7a7566d6b0f2eb895949655a907507e420eafb2a956c409396960d1b620fb33b0ce74e120051c917c4f655ab58fcbe575be4317815adb79876e510521abcd5c399352ba6405ca8aa70d55a71202d7be663fad4b8591adbe547206f9b7b2d031bcec2899eb265a89fc70d5b67be954400148b6a640e76e18419ccc29918aa3d93a4dd81bb055b0f0c33742040b142e050a84cc13272780fd2e85b09d42ae51c9e9a8c63cf50b40137b5038d1d9325d983d985e878ea3a64f24dcb437b9409fd120dc672a9040406c713408216ba8583d656fba142f1a5077d61c0ac99b98c6570165e697931610530c6896a278ef2bf8556a57c499c32c3304cbfa36da9cafa3958dc5bcf331b4123ab9e86d6c93ebe059dd7fcd6a1b2aa2793fb9b603f1dbf01cb7f3b0c115187131f2dc42fdcefe19210b73cc9e63fa43ed71be4ee29feb334435eb73d43ab25e48fa049059f0f751a159da9b4e6fd0e0d3dae3ade7afbfa8432495a2034e5203d3894fc803a4106f1a1736d2805ae5486fa78cc0725f84447ee22dd9331e6a2b563b8c022397d9f2b242edab08631f5c5bcd5ed5058d6ea40f0a2fcf503dded0ca02f2c5eb2dd70826f6dd460bb6d831613df1a6838887e6e6fb5854710205a97f3c5ec0abdff284df18db52e1f21dc1a04d5dfd9b8623795398dbcce80b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "d0ae446d3d3fb224bfa4fa2830516bc11c954870e0c8095114cc696df0e4b707", - "proof": "1ec0aa78a7cc66977916a2865db5e5584efc68d5db8a05700d0954a1d45a2058ee49df2d2fa38e092b3063a3b47d0e88d70bd29c72583d5ff5fa5ba6503404727ccbd8a70687ac1fdba4993e76d08c054cf6472094b2f58442ef1399cb4f6f17103dbf0f6ec44074a8003498565f5e15382d368088adbbc319948fef6cc9d02f4fc0dd4ba4a8d4bbf3c3a86e01837eecd3771e20b5494fb5a61aaac8faee370f49ba456cc82b4aafad252936a2799c02b6ac94f3559585fe32dc1b77b1655c0dcf7d6f0057ea81071d66cdf321d4f155c6681734af083ecefcb328f5c1b6be00ac3ae4cd3388da9ddd3c1c96cb6fe66ab4d8d84a7125326c1a5587699f201d1da6dce1586030d51564406f1c02ffab287f6ac174a67f900c8aee47543e7a673b923bc513ea4ec2c6bd08cbaf99edc7ea56cdd9a1c873f80e19ef5c4b5a9bb15986b0a33aa8c17b3024974bc351de3563fd5b8b74e999229cac59147f821e9206a47948ec84d1e7ce39ac871d408fdfbf2af2b6a010514fad9a6dc72fe05f3943f8c171b560147bca3148c3bfc17db808416dcaefbb4073518ced706a81033a3922bd52ab8ef49f5d5028ab110b884cfcbb9e25fb33613f83822df436df244e704aa2c10b1769e306a327fd7aed30de427155a11b6e7d7f5f294eb200ec91a7727a415af35cb91ddac7251a02f58bc3718221244273a55aa6e2fe06f9d12f54536ee5eae76c6576d6cfd076d40a9cce235a79d173b023ad771563fb7dc9168a6a50eb7873d2af93eb6c634982c9a5e6087d7799fd2d4c1a19c1355fcb25d77a568cff4c6a567aa84e7a6a1f138c3f64868c5e61025484e7478c6f1e8811c86903a1c799df3e38fb6d4d9233cf65a74787444983c1b882bed17c7fda588d5bb708e8e3f5834409c56d73924e74fd4f4d1f87ca9c99f8c0f2b064e05453042cf206" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "e4029ae41271eefd499acf4c958e83387f36b89a2084e39fe48bb53e94ce5f4d", - "proof": "cc419f6f22c63b19a4d77e7df51ed9ce785092fde046be43e96fe87690fc645000578b11d3dbac5b36fb84aa9cafcc8a78ffcb0ad2ce5aa9a996f636fadd2f5a825049c57a173a409a33501b88ef8ea8d886c83b6a6251067152dd1040d8f54b26a983bc9e0372e62332c8eb353c07afcaacf0eef965d49405d37816ca73bd7979f4bd13cad09e29a54334daaa95587d6d1f550cd9a605272e9c11c145015803b947ca0bb852236d149381b0bf6341c4cf8aa91f2710d2c899b7f60a515341085e9b35e61fb662a006f35c5487bafcebe276d220cc363463bd61e2fcba41380ff04a050e3f258079f9aca326baedb1a5652ce064393bc7531dae4f3aeea3812ec6e48407a83333de0fbc3a12aae1a25eb84704200769b6bf15ca82bc6cecff424e5b1791118230ac3a2dea7330af8758ea4625580779533be645f7408c03c4745aec2cde9f856578a40fa95004363c87c240611eefeaa49086872b08c0e740640a0bfbe48409ddc77e15330145773bfdec580f5fe18788cf723d98404d18bd00285c7854f6cff903bb256707cbb26f12c806cd6fbe84b5810214677edace0b52826af8440d6d630e4f9c40391d7b4f08573a34777e752a2ee40cfcc5610b0777aa9ae7524763efb68afa00a3a422b91809a1e7fdacb4949437be65f56520d8128c35ffbc3c239c36df334d2842df48f404d8b592bbc4a0d7ad8ad8701f41e816b0182e572772fdf938fb6e7a64885a9f8b082ce609a96599030525f2944b41798cf10e7de90fdfdcabe1c90e83cf7a2fb29276e88af16b317515c6512570525de493dd5e9d9eaeb60fb9dba667642f1ee727ef58fac9db9399c3e5bce054187a6ae04b47231c01ae2ea51166bbda85e4e32cae5ac5cf20a4190631a3132c4b0d5b4515df554451ad590b082ceebf81e494a7289cb6d987455b99561eb1ab0006" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "f466add556a9a3cccd308abebb7d703207d794595036d5888ee2aaecb3e2f06b", - "proof": "6a8734d54dbaedf0c283e3672df249194690a47296fb5f5f15286e7ce6a7fd5e84e59c9c244f9141fe8c907d0513078b7d611061c3e856862973f62645161b2e561ff2bdd6892676ae84b64b9bd9f287dfd1bb3032942c0108d2c81a708e895ab8d3f478911d1f3c8356d4abd9a3a209915f6df73a10e9b09ac474bcec553148c9acbe8343c2c201e5e86f1680690b5f5417240d0a4e9e3890b23e4271972406414ede9f3bd69752c7c0da7bf4def4fff9aa1bda6d8587fd90b19787e016150ca1427a6ab6e163b350677ce54a0242f62a73af1c4e4775c872239d83cd9e36092a871ea1fe7f5a363fac09d114be9dd10577984d788497799884fcb37a8fdd24900e3fe1573f37f7c315bf4422a6e56a5d147d735cd2537e82b0180444418f7b4234ecfd042bc170b39986417b2d4f58c99de9b2ba7482980922b4600f16677e2e1c4768560adca5d036e09d7bcf762f78b1a6f8f5b77f47fa2ba15aa0cdcb7ec647e75defa06009acc2554c21e4ed13b305ce1b50e5c427e7d66e61d89993671a55aed43b88cae633f486d34a9edc3c9e04effe153832e1e97c28a1fc24ef703cb333b3b0c3898bfbf38fbab89462880f60d6a0cca08af956c7cf588b2a6746bcbf77684bc9a8b17110347f940724a8284c086a0b66684e346f05be9a407f4816e98a2d1b6060226ecbb10755297ed8950039815807f0bcc4ccfe5c3524cb240a60d92bf84746b93342bb38e2c9bb4db19b627f648b0b65741d97a23d8bb8456ec051baa851c77a8534fff291726e4424195c8ae81b0eb840ca23903571a60c5c6dc61c5e9237f5a63f49e6067c01114a822c8ba36a70d091e0280ce4dd864a16a3dc38ba0f73829d1f0b2d9b54ca8efa6be909373f8e23a26fdca58b250d078c2dd691356382ba138fc5d79d7706c52dc970a8ffedbf09d1196da08658b00f" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 27 - }, - "commitment": "4ed296117b89981ccc07f8ebe1df9e18053d5e9e91b898c0629eeaa1435d3776", - "proof": "46fa3bea6f58fb943bd2e67457176bdfa808b1df1fab39ae3dbb6d8100b431773e9463afa8bdfc333f11395916215db930e71755ea3d47656c6cdc8d6279144de244855c8c7fafb4dce7431541550c797ba0d8574783063f6921cfb0719e8f4d5c8b4a960e19989936b698fdc31f8a9ddfb80cd792ca8f20cc83dd43bc5efc47472b69680ca0d890db2210194e5825ab2754fe43ab375e6fb0589e4c4812650f259e12bb6389923a48bcf762a458ed46cdae503b37222fd1d1af5905d1661b08765703cd20f9ffa07197e327999ad7ae62d156d13053f8a72d975a4d1b26530bfa5c0266f18772eecf5381c29190cf0446726c9e76f34b88c96214677ca56e116aa8f7cf7e0baa4e6d02ac4c56d58a3f53e29c75dd0feb23dd0b643051e7c532acf69be045c9f9097b80d9e2e4b5bb159addc70d696f48a1bbbc33999fbd115a4c2f56289649573afa09082539e7e2378374d9c9db65ceafa3ac7f9230633e74c251422592203cf990e47597a9b69716794075dbbbf3229d046650b453cd1e2df2af0dbbf1ee4b8122c22dc3ca0ce02899309d0d859a9df9aeaa2fc9af42df70ce82e4334e9f81d65a31c742eb24c0d17efc96306ac909760dc34ee48bc39123befacbbae51dfae86907a1b99cd2256103f46cea3c3bf135afa069e677fede6f68d5e5ea16a85c7e278abfc2bba31eaa050302d0cfa1f102426b7a900410a569dce2bd63de424e6c47997ca878659f54f65e59277805a30849f9af335ce2bf733a96c9d5b38bf325a2bfdf550f1ebe62306afe38e5c3e4b11dc0210fa34b1706ec9f2b4e52021dd4424d29d3daa1c6ad8e4c5983e7ffc5ed7b75ccb2f33a0f6f431e4b42592df6b9d5971302c3c11e72b87e9b41080ee12aeb8cbc1714405c0d1afe75f23edcff0b601911803b24cf152649d978a11fe1855e20761a91c4c80c" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "268f579e654f6ce699dcea50958d1df035c8f88dc8c4fb50463cdbea637f0278", - "excess_sig": { - "public_nonce": "ce1a141e79d803bd6699389c54c4916c5249778d7199949da6de83ef56919c39", - "signature": "a044c6785588a916ab65a6ac019d9b14f9b351a6600b2b5838471df34dfee907" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "2e26211d880227ae3be09c290e399b1ae1fdafd09830a2d70f44bca2beaeb802", - "excess_sig": { - "public_nonce": "18d9319defb97a21f82860582f27a36539fd00805d834ad9164a847984188075", - "signature": "232449dc618f095a8653c7fc6be3a97d227967b346c6129fb45bb5198f54ff05" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "96d87822a57dc125c9adeefbc287a150007fccdb805094ca5f19dfbbe7543f4c", - "excess_sig": { - "public_nonce": "8610ae78fa07fe3a55eb0f94f7b251b1943e7cb201b36dfde9c5343648a6fe7c", - "signature": "101c79dd4eae4649ace53cb923d3a6adc800c490db045827bb8febadf09bff02" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "ac1097c15d7edf13c34949701f1315068368ab4d25852992e084f201a6b1c86f", - "excess_sig": { - "public_nonce": "509640a13850601d1c31681b50c3082aa5e12973f6ae2bc7d27f50895fc6654a", - "signature": "2fab39d1f81860acc5d744985c9f6a99f1a49b702a4e3d0c3959ff6cc301b503" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "e6b2e2e50efe4a3757b7d08b296a2c9cad2c56cf15155690dc777cd23e1b8601", - "excess_sig": { - "public_nonce": "841c1725d8213da80ec218e63f5a0c97205274ee4743075f515260531e455e29", - "signature": "484601febc8b06f80e831692b546702ef7958d09e57a27c75855bc940e4abc0f" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "6641ad31a0678c7f857a89142c0234aef9e18c847b3624b735fba9022d387926", - "excess_sig": { - "public_nonce": "381a775d781090f5d755497b4fbbbc33cd76b0ac700cd46be26d4f43d1c67605", - "signature": "f3870af86409476daab623d110fe6702b94673e84d86ee08e926a61b25925201" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 27, - "prev_hash": "0d5098ae22eb41dfd53ef690b64226d04d49b92ee9feb1d38d9b0b920522f78a", - "timestamp": "2000-01-01T01:28:01Z", - "output_mr": "a237956159e33111143936713b92b0e4dc74202baa618c3323b796ef676da8cc", - "range_proof_mr": "938b6733f624611a39d468115445ed02feb37efe4ebc5ed49ff81a294c3cad32", - "kernel_mr": "7665800cebd898ad984423ace5933758b18c49bd2846deee382c117e5c498c48", - "total_kernel_offset": "17a62b64204423caeb1c0efebb74b8f808f3c3d26b042d4c0c79be4f90383500", - "pow": { - "work": 27 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "68402edd17e210bad42d074496fd49facf33b35abfbf8991e760d3ecfb11b425" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "8e741213b2451177b649812f8b42e9974d4dcd8f71326f604283c1e63124e750" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "cc08d9421073875d06e8d2d0f7085ff86daafc5f57b145cdea4d6dd8239dcf7c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "d0ae446d3d3fb224bfa4fa2830516bc11c954870e0c8095114cc696df0e4b707" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 14 - }, - "commitment": "405bf2c2f984f24bfac6b242f9b51560b1605c2a110ec907799a3882aa670e39" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0417509fa1648e60c22124e78191c277652d4c98ce15406c064dac871140d877", - "proof": "e25d31a661d71b7a354c9e0d65ab6b772065bf7ad8234888d4bd57cafb42f4186e2e36bebb8c993487f1399417781b807becca8d7c0b36e37c7c504371f39345080e3cb70a50df82e7ab3fbd42e6768e455171191d04fc37583179e05a9c6430b8fa800eb6daeb7a51b6179c77410a847eefdcd5763eac97e54f7d223131060c6ca4f6fde27c916263ac285183fe0fa4a70085c4561cbe931ba4e25a8cf1fc08d4225c7eb00a4c756216cd23289ce1d5513ad617746c016c533538cdceab02053f76fe9d5ab9a4fac44e68a624354c86e617ae89d7a43bed579899efc3352202de5d8cd8bcfe61dc451971903a2d2835208f0697700dc9001e560a4c81c20d34ac4df1f26e56f7896e601f2059829953e2988918c75800b22eeb3f53d1bcf02aa07bc6b797b8f678da909827b78ca6a97de80dc9417dc6fb348dde9d1d66c3289028eff726a91b3c5a51d410ca7c8f52011f1ffdf0927132a9db555120e6fc01fc619a3672f7b1e6d28e985212c15becd68eb9c0841c387f4b028efe885699014880c11721a1b5a6237458ff2449598c2f6ce44bfc4309292670563f43e7975d52aec2dc32adca801724e17614ecb774abb0c738fa07032860ed15727eca5f352468a328ec3962cd6bb6e35eeb86561d4731ce9c1e91ed6008f245730f5c845fd2f1caf2166c01a76112727f97479dcb35630415c5ebffc046f8bca74a47e312862ecc4f4b0727006d73fc1c1d9cc961256395bd6acaeca43edba2894bf5a95eae07d62ee3cef6ec5c982ead7bd1ef29b9f8a9eaf1f62c6aec9155b124f91f4d32312e0a9e52f1074cccef34152634f4d7a6690287f210005b78c82c9289815ba3ec523b7764a0ac99346bcd762b0c7e521f4dd1b7c96c42ef24dc66d60cc608a552790bb21ce501277e3a2e3a4682cc6729f1db4aa58ae309a4b3cb207e7c00" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "2c73196bd7bda160056edd8e7ec7d5ec5ef3312340ae4489c41ec3285b8b266a", - "proof": "b859016335b5a1ad6d25cc6cc7377f445fd09b2bd43ecc9cf1cc1798eb50323f30ef1c64eef98211c5375fc1626a299ccac5f0393dfac261d512265e26c0427a3aa8c058a6676ec523b38a23d9345e7e994c2eb2e0cc32b933454348e4aec32cfcfe55fb2eeb105af1a94014e0503d68d88cd890b43e9166de86ebf876c40565d2816efc029f186a7694495ac4b8ea9596fa865ab047cdae0c31992675f9d404a8c4917f8fd394f373651f75ecfd4cc4ce3e350591bbb2c9a02234c6e01dfc08c4db64d36211431018ca9efa2e2ac91ef66a74eb3c40e6398dd764ad2bc4eb02804044a7baab1529aa241662be7d91196af6a9ff88b37ce2a2040ba03edeb71cd200e17fd450dd4497df9f237b6df58efa838b7093419651fe2ff8ea1de5081fd86516e2e270ab58df816a09a185620b65cc0c609b1c15996375cd70bcd86f595c04d5eb84ac80cb08afc53a12b19722272046cee9682c09d8a1a08796377233c0050502ac4b084b889f59ae3688da80131359cb7c7d023c3054a9496def83153c94c962078218ef8919f893b203e8c8dd49d90506f817379ca57272b000173ceac056553335845d302d78258627e82e8e44938fe6d82b5a5e67589272dfcd6bc8d70f8533ccc3e8a1d9c9d534b4266fad8ad09cbf37aa9fb0143cd2d16efb50a87336fb9197c864bb57f3242fc48446dce163e212fe92a3a748f548a265035e1aecc4f301b7a4fe6f4aff26feb337e7360ee32488eebbf4545c020d4c936547a2b758fd16e907201d622af7b3daf08bef65ded413b84cdc4ab13dee5dbcee570855a5bbb168d2c8a2377ef4e2d90961db4d848c2106fb5c5a739e6fcb9b610f3add9cb2caff241bab81fbf81b076212feaa0c6ea95ca72c248a6ae54c7d7408da742a3f1a6317543f7597d5a3dfae090bfa33be23766b3b18a05ed5cd0c9502" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "34852042f345a3a72145e17f8e26369d1f7cfab55f9124e21a3ca8cb53604909", - "proof": "8cce68fd9e54ac46a40d42a66e227223a4152c978cc8e9ec340b1b211f7b307d2804be2c5b305283673d32cdffe0c1d052031a34858001b21b44f5b4f3b8aa737011eee78e3cde807ab5c5af089556a117dcd59da9cb16887826beaa0965eb59387e9be82de38269c2ba8227e2b6af9087fc397ace34d88d37532ff0d512f842c99d4ebfec699a067c39f78ac73d56ee5dccc4bf9cfab53c1815be3b3a208b0cd4f66d08a736771a159b702a0fe16988adc56b3a53f27f9cc06741c91129460ff2c82f7d711b1dff5a34c5251f1e17145f13f32c54a02ed773453df67fd7310814ed7de952bab42a5d0bb705aabcaf06b5faaf5ec9886f2dc267f4cfdf3252238ce02651232962256bb523cd9a3a160631966a08e61d64cae914288e69c46533b2db1ef2937fb8b6f5ed6220dd4686964214ba60436dc868ab8d1718e1e86670d840afba20e82aaf71121e94307c0eb0555c7d1551e1a8509a2956dec245eb4c9cf3aa8337e9cb87f6cc6a9a9560dc045eaa4bd6e756072b5bb4b016f7c67412aab8f83bda14089b75b4996a0d2c5229d3700a6b4651863baa98d706040c22348097cf87213d59a3680d9b9f5082cd185f19b639602555a5298df0190d137401100f5e4c213e674bdcd622b2514b3e2029cf3b3a6dbdd5844c9ede2892a139460a19d9e5572993a51d89a5265351d55e80a0f4bd446216612199976dfa603952621cca9a6378222b8c6a3e7ae59af0e538cf783a80aea0b1fbd36518e26ea84a02567665a5401de8017c45a5184c82a2a433a12f3430b40ca4a1ae2d364baf61d666a13b985101d10791338bcf7ce7544ae521ab0a90e3365aafc7123450310ed284da201abe68049d07bd3075e31a4682b3f50c6ca11ac898472faafc33790d39dbc859876f0ddf43d6b114d0603e05dded692e9e43bd8aae8cf591894a3d01" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "5a6453c63caf6f74ae7e64ca406bf1bba813b323c0eaf8256bd238da2908536c", - "proof": "160adec245e1f837dcfa1fb3b566e0aabed40c9421b7dce357ed2ba86e67c84e488ad62465bd12d97d8e163b337bb5b5ae846a3044a8461fe4be2e16391646708ab5209057169c2973db9beb8b2df580215c796c6e8daab818f271ccd47ad528e6bc9c64036daa5d2a7d84af5f041047e0dea1a7b163b1083dfa822413a5ad5fc144a3f19d0b8da8f0358ed6b72f9e540398bc5568c0cc1316ff85c846d3a10b8144c0702c81b6f801b37f7425445a166727f7deec42d4143bea42aa7e126000a0fe83c47b8a4b249e866db59e22974b1b7949e4fafc4fdf53b9ed5b4698460a441106ddab55b3dd2fbefe891a5c3361ccdb0e3592351eba571c4a9ee1ce8874aefa33bfc2a601db6c4897c4df793aa5d1c470b4cad48f2b872d4a35cc04b05072e47a3d330ce0ac753718e8a71e93e17feee80ab2666956906b0f948b59712616d9902f4d7f28381a72fa58bc6deff111d428590267ff8e75648c1f743ab2398879847cb388094ebd7438112666262bf4ee8391399fae6d1544fd0a30f88b6d4cc0ed32f1776caba67eedbdd748de02dde40ec340e955574386327fdf5cab76b47e9b4fb80f7a2fe1a17b7f2427c60a845932099315877d6a0c5fe2587fbf03166bc54f7d3a9f8776b3ee02e801df0f2f8d9456dc20c67d1539e4435e6f3215b687a0d6b380bdbe3ac228d840bccd98217e9dffea80e6b7d5ec18b89d946949644a6b4076db855ab0c6757d7479a58eb4618feb846b38a8bcd7801b688c1a37da3c5b36130563413cc36353ac0001e4813f7f49928e4e9b753ad7d2e7573256203a8e0fac1c509e3127bafb13cec40c5c1dc93909eafaff63892288af19f26b5cb25658d65ec8e6b40b8527c841607cfbff00ede11f38ef805bbce5551c1a0ca7e2bb16ea91445329d3b984d10637ac3659d638d6ffb323fd2e2c663c11bc0d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "b0e05b46ec08cb25dbb532d5048f077ebd814a83126765e775f0cbff04baef33", - "proof": "daaf686b04ba49de56fea5af459f2d72f4836947a023f07a19b5113ae141fc5c22ee7e378df51458852f3d584a9f2fbdbbc61a3dbdd9818d628b96ad119a384f5ed0564fbb415401158c9ec1900bed04d4e1d28d533b47a8a607a7980b958f434eed0052799d5f7b289e702df3b8009928b9b8a8e3e16853025642ecc1740247862ea210f34cfd0fa6bb52065b338c4d260c724fed37531a7080047d269f79065367083d21cf3155df4db7b3dd6aa8cd4e3d98ddfe5fcfa602609c3528deb10636a15a1a8bbe0f87bd9220542956e3389f974374db962a98809417d493b5f303dc9227221cf89912ba5e3a91f3690038e759edf81303693795fca477ea2e6862bcb5c3869d3cc71a14816e81884827459c37591cf7bf641a34f543ab3a010404e25852cbe2e1d67301e9c83bf481fe2b873c2e4c573732fe0d995ab588e6166d046a1bd298d84508fadcc0bd71a4240e3372063473f31cb207b62655813e5541dabfa2676f7217b0f9ee85a3a2626dc4bf65d10b993bc533df0e953f0fb30830a6978a139c592625c097e5b0507f025f46210ff070e2f88f8f13b382195f5945606bd731066566accb98ac6304c5447c727c39487e2b64a2aef385dcd26ae80fe82947282b079e889aec337b3d23a98bd244ef337dba1c255ffd6d254ec59d520e0568e76274669366b40a3eab83132515ec1cbe531b087861a63b345aeb0302107c808cdf7607e696dd8d1c31c52018078d07e4fb334f931557fb14a811802276b2f0b08f6f0e3218401ea13a1f4ddef69575d4b8322e6661c51d28d1036207bc957dcee194eb7ad948a6a67ef329ba7367938ff86f0d1e1e397138af73131395747285a8660821962c4a1c0bc0e5dc7230593c3d9b3a6aebed3db1bd26fd0d368aa6710a80a4ed8934819ead49b1d7b89e88a123e5dfe0e2abcbd56cb3800b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "be21cb182a38bc8c5d6ea15a0bc130179cb4792e70f0c35d194c9b4de7e24c37", - "proof": "d2562320a02c165540624e7e7253222b6ba8dc2b2720d4434031a27a4676f96d5c64751ae4b5912a8d52394578885f4a183a1e27b369e17d13c8dfc357e21979fe3b1b255f93a1d9da5a6f8271f1e319ac623a64b95da1c08c7abbf013ef4d20d8a428b580c06a3efa8c6a2f5a5850f7ffd13e0020907933112eef1c3b42a93cc11b36db3f0a0866b32a9b993a5e6eed8e0bd141381c6ce102cf377c5e7f050967767f9243c4ba140130904fb513b11e17f36183cc20ba720682a585565ae904d9cfc5dd970ac774724c735f3f40df1648e72ff113f2af1386c6e0312dfe3a03f282a38c10351ec6b37b1753604a570eefa0fa1befe4e16c1d46026b537c480e90d98d4d30310c5270175e58255af09a99c15c0730c709434650c34137290106b045ded0c084305c7d2790da54d5ee63d6759a7252bc442f14386703b28bb00f006f2e5a3ff95855b67c315ddf1b1e125ff125417cd1249d02348f5e257e0a54ca794bbb088e56da6cd833464b67f22aa26229501c5ee41f42c351d9c528194c362cffa8aee062dc57d4b32f8b69403af908c6583e9ef26f3cb96f027ee58763827652f1a8e8eef95a38f7543ebf38864f7b1721d3bd618a3bb977cc535b93195413960fbe0f38c98fc057723f80e6a284d6203ab98329dc2d6717dd846c45153ab73ceb8c29cc84a97d2d1907a4e903603fcc155e426e5bd3c4e9275988a6699e85eac5236bd65afe233382a1fac3f567078c9d6dbd3a756eb5095ad455a90504c7f985d5d8a45fb24ee954dc8a7199e33a751a2cc259bc1938fcbeba7a0b2218c14ef39f3e8d6c45bda855356cf099e8e73d91d4d461eba56fb8ea1a2c821a20fa8cfdce34c35a1d17af697f063ff3a576080a3bcd418b37973b7dbd087507ae0224323561f15faf774ab817bb22d4a2e4d3acbc8c193e65e4b78f1e1a9a0b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "c494b14600be1cd074d77d837faceb466619c573efc76ebb5de3a95faaa9ed3f", - "proof": "d277739e92a6ee6d30cc89316926383797d9de025351ed1749ae31582ab7a219740ec7dc8ee92060e3e2c1e3e68b7bfe97c82209cd72e2551d8c92baed29721b34d2addb83e06b8f8aa0f7ccbd9508fa6ea0a8b14341dc57dcbe6091c260e3193664fb7bc8ac4a105d5df93a893215ec2e8c7ffba0bfb9c0dfbb959b900fc23c1a0687fbc5d96a48cd94cce70a436732e0f4b290698d67bb406db4fe2114dd0199b45c854d427c1a9f753ea0a29fe7823e05ab670c5613b8413cf447e2f4730523febfda8efe9d09bd75a83a65e192f73558ae913d0429091c03919d20af110dae8c47f087f57bcc6b3016c1db440ef7bbffce6b5f25ef89a147649f995b524c5cc6424e4a21e5794340d6aada8f9580126c8361cab408142aa88732fdccf5467ed42a113e829458d040e290f5e643397b3decf24656bdf2f3d93efb5ba3576746f10fdf63dabf60615e0650a4d5e1d883a6a73f09ab418a232e93f8e82bb359248ba0a9ba7b2f67b6733c45b9977c25e53e150309b4da296c92e861e3140123a47ab0750c1075103bbbff7beb83aa0bbf5a1f5878a570d34e98205550a1e92d1ec4dcbbdcdf355e32f5a48c5a48ca4b44c49e14e741d28e2f7d9c656b3958181abb259600ea86515482ae791cdb7f5dd40440d0b0395b14a63d55dfc0ec51203aabf803d98597d7b741370cf742ad24027cb9228589af630f31abafc897f004a08ebe74f0e4fe516f8396a949de952fed7e91bb25523ae173108e833d2be01290d7c3f90f05dbb405ddac4d5b6ededca4d904610a304d39be87906eeccc7058c29678986bf9b29f5e9e262351a1d7667ac45f332ceaf04f054b56ad222d327aecb5d1f337cfd283893ab6aed51a3bf7a42815782faf540eb09e503bf1352e074f619dde074583d1d4b37d47ec6c9e6bec3a9340bf05da026e652f2351fa840d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "e8ae31ee949785cde2c8a0a2f1c3a52d0716c397217a17d354d10384278a6741", - "proof": "52b4630d0e70ea078d256067d73e21fb0ae6f8bcd1fd41b4c8ec5d284e029e6a3cc2dffa7d7c296e0fffcf1e355b07f23c5f090c571500bcef2e4e6ccca1d94c64fee11fbba24f77120016b1c7229d4646fa4dead4485ce5288d61d856db175c6c602c33a338fd07603e8dbcc253cb60b2f6ee5d65e84d459609369ecc48d03e3d9e40d60609738f2da6e0fb2089aa6a82d62ce76dccc7d686f7dd8ac5fbe20914bcd29aeaea7172b5086406591d29919dd83c4783cb2adb7527e11db3f1cb04aee1aadb713939e8b1b1d8eb101b5713ddc96574286d3ea2241ddd262974c207a80e07c44737828a414be1845046580b7cc4b8e244f2a211f5ba115e939b614c6aec14deba54199aa2e80672970e3b60c5d1b206b26e544ea6a1445a03ab3b3ddeafe2ddf022be47bf617103b7820089d60f9b15b4a9acf0dbf43127bc482f31da07c54da6a149ab413f58c1c5dfac5cbc3bcfc2c76bfd9810931609df502f0c7052e01823248cf781af82da377011955efb707e54f5a3f82d1562340eea1f7e3661b8eaa74ffdae365886292dd81fa13138d73d5cee5f12a11fe1a18d50580000f662489f13dc1886837839abf970e7c4d3015da7c2b5f8e78662c66a809a659a0fa7c24df2825aa4a1ea4f46b397c4b5962c70ac3b2bdf945631babb1a385dccca0cccdae3cb248f9922480dd235276d42d8ac3954632aba504dc5364342696ee969a64b6293be980500a247e11a119986bf535b955d82972688156a18cb400077c348e70c8d45b02270f9587fdca11bc21134c77bcdfe2c187d5dae616a6368fc53d70bcae12064c902c69f837deed6c7a9839955418caf05fb2daaf1592446417f765848eb7b0e82fde23be90f30829c720ee8730673221df79fa43cb5067ef20c1aca0dbf506fc2ce3b1f63ff49bdea25bc604d8a78c662a99722596b06" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "f67466c12f7de48f30f7e0523f57d32f75356fe40132b288e37032b22f1ea640", - "proof": "dc40571afcddff6e7a423d681ce7c89c0d172508e73a4e250e6673066dc5063c76d53fa556a605ab0b42142f90428fa4847da05988619f5d5d98f94477f79947d6c3e6d35463977ffeeb0ff06490598c8eb405b951e828dbbdbfe754c7c6826390cc93f0785c1e071025eab7ee2b99ec3c11556c030cd3df97b4f3d91ab9657d5e5062975545d23426d84ddf7a0261eb428c2c32b776e182610f70fe62b8d905d2a48ab9c1e137cfffabdb48de96fb4273d5560a29b52ec58fa63da21708190fe03aca2a68de1e4b4d71358b3a3b4cfbb777628bb65a8ea018d9c8bcd4c5fa0372b22a54a656364e68c0ebd424d9569c057b250d615dcb0991cdfa3473e8604e706ae645f4b547a9ef9af55144b37969362a94be05c02789532a34b392b3ee1ff61c61df3670346b5170bc52426b0686227cf9d54355a80e1236a0703adc146fe41d6ab9a70d55b438b4d411f436d0b195542df4d9bd6049b9f22714d727425f20810e792574afd1ac9ed90697184d5a6ab9c65cc2a6ecdcfef5f12ea8a2a971dccbf8e5597eb01f41dcda964fd2b48490a1f453152761d511d8076ef63a554c069d56dba25c23589697e6e7330b3ec7f1013981d82bf7a1b8bdcae0aeb8a05236bedd2a976719cd0bc91b933dd23020ec2b560f7c7568ce9f527c98dfbfde5170ccd77d6976f0b9e4a01fff6f7f19a8e424cb7e3f44eea298b094ce64b76e12009d690e95650fd841ebae5383f5dd7ed74bcf635b6973efe827cead12ce8a35c8727410498206489709a41d7ad68984c5bcbe4f4235aa011fcfd50cf79e68261a0c73d20eb319f733f27fc680bc56ad3711abfd6b46e49a01c4e4859b7102565ad85e1948aff6c18bea8b0247a0d871916387e3eb5e6baa72f0593466ca590df0508e4b83e3bd66c3a289af8e116a31baa8e35606d63c6a8ebab379d8a3830d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "fcc39fef062cc728481e13872d68aa9ad6a8cb3e1a2d0d175fbb1acf0361c776", - "proof": "a04a037b08340789ae986d28e9ff397234646bbb43f6f50084226df89f2ecf024a6660d3c3407a80c0aaa36f8b23b8788e78eb55f61d7481f0b5e7deb445280748a9f8788b8f9ee17575674414e41917bc524a0fc15e0d2f13c2a3f35c27fa4cb62c12661fa1245623c6fe23d8a7b22f000a1c1c987ddc597e2b46f9d086f618bf5ad45c3326b5f4f24f61f57ae922e366777bb3ceeeb606cb87b55a2811460c790e062c2e7620daeea0372c177e832cdee8cc6076301a2e6c1da78499d09c018125a127a9a91d9ea8cdad05eb01954b8567c9fdf6199aa04ac3e96ec99a630e3a149eb31f26760b6b9ba5f5c14ba9f607bdc4c237086adc7c0baef60b0b5c4bb207e91654f797df81748a0367c64db0859832cff6ffb141ae217b72c9d89914141a7bd0d94c8962d4b87c204af1cd7b038d88bdcf9de0eaa5174124ed22f874d29cc7d9d80aba5cdf57125b8f6361eb8e24197ccdfe223a9b7f89c563e8c706a04e256fbfaa8b9a5f60b3b98192d3e094d33cbaa803669cb53d390d6098fc74ea1e3985ebc26b2738ad41a82736f097df603c2152738840da8e37e4e9bb994b1cd4cf0bd3afb42229ef29c03bfc3d6d613a95bdaf1e97ca2e36b35c1b71e15c6833af507422c4ae7059fdeffefb29910d86d50a6dea74e3754ad6400a546b6a68318dc5fb19c04cf7f8494ad8d37ea8a3c93a4e11bb4774f0f117fb2f84681a28a1f491c27893be68ddcee314d835eaf23de6085e1b9d58bdb5b1e8c250267012b9716f5ebd84d8fa8c4ad18e3f78da6c0eb33859e4c74df7b5b1cf17bd1b5e2e908d270f46affe17a08819c498c4464ebec61f24eef0d5a72c6d528fd53657f079274299d8ebf5dbfec796637bd66a3df262dbcf54b78ce07f8eb175ddc701faf03962bca9f142e0be632f088fc5dbad6d3c4e27bc477442fe131b6630c504" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 28 - }, - "commitment": "74565a884e6e38e2e727318fc6ca629f4e929715648264d262a3233e3c4da97d", - "proof": "baf2a41eeb8b61bf4493f9f75f447bbef4c9b9eb9678a01c3a44d251073eae74fc5120ea88bf1403cf0bf271b76e08d0a00585d14dea34878105c1c89d59384c58b70116b538ff3157972a77a790dc324117a1d7c64708e4cf7b12c32f39054b84a64f7fd38136cb31c6221b46101d0b6660f9f1e3522bcc05f7b48ca32d5028f04882e52a28330e513099500e7d0c424195c63fcba6cbcb4900092343d6550e733b904bbf2d8ceaa76df4398e695bd137b02deda4c1381c38ee9e6f91522f04de5ad2ed3ca1f1872ef5a74342e769ee239701440a01d944c08cee49a44405057afb3cd64cb0892f2f27a97680f1c4d66f359f7721e6f7538e72331b39230970d6251d7992e666e0b96fcf885618707d1f3b8f21722957adbcb8725833026802183a0ffa0afc1b58abe6658f877e3a367e95f217ad1e725e5960aeadd4201c7d64c46465de293c10e5b2e14ae1ad259f4df0cd56a161d9e91c6f59030115987a68c57e40a2de132c40a0ce592b35ff0453bbf8715567f424ab7b4556027ea05d14bf577815991ec0570151e42557af5826ecd32d1b1a0a6fc31477738f090b197889663bc1c2b2c0cd7ad450d4b57721a4c0266bc979beba4c01388dabab633f5c954bb31e2d6cc53335b43dfcb8b0c62e4f8b95ca8c2f87183d5abd85a5a40ae6b83140c593ca9d30282ba6c0026ccb3896afec4e15451428b51d61576129427a16782f09cab71fedc939af4e53fe66801327e07f7ea75fd395147aeb16d76666403544c92b1b1840e6c34a85ad72d4171da05bd94384eb677216eaa7649b072e0e0dcf268dbf19ee6da7cb457b984be554b67ce86dd5aee4c49eb169d5c82f2d8d1887393ac11c6cbb27f431dae964a078368443feceee644423bc429403096fcd3a834fe7da4cffbec916fe23c8e6a58badb323ef969019597148051c7400" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "0610a39666e400d8cb2693d38174130de496fa1a6afaadd20aa64e9ccbe26a3e", - "excess_sig": { - "public_nonce": "b6c570f8480fac8707b8280d9e941aba568766ffd35b63c5f4b1dc320c8c5948", - "signature": "4c781494e4374a34d5ae38d9afb779f74f1e8df83a2727733b2224c0c4eae807" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "5c9c3f889cac9086d7167bb473f608f8ab8e89d900d4d8b40f164340d31af64e", - "excess_sig": { - "public_nonce": "4220b3e5bee01642ec640e62b285904362fbd6e55bb24bf25f7836fbdab7b119", - "signature": "6780af90b89f8dc953683236bebff0751099b2689daa1eeef02bba747b60fe0e" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "7c63b301ca24b405348d315bbc70d666883d0d58503ab2d679fa5b389ee3507e", - "excess_sig": { - "public_nonce": "f4a38d631b07372e6dd111de4e0393f6b6bd470bfa055c39b6fa898ebc393f44", - "signature": "29920eb5d5e6283c02bad4013e23862680f08834f67fde86e65fdba8fe033301" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "c28d3897c6414761febede32ff8641b8f36984aedfdc44bf85734c2b28e9a47f", - "excess_sig": { - "public_nonce": "5c854997e5efdc396f5c82006304e91d2b0e91804dbc8689ed19fae43a8d6937", - "signature": "8307c924ebd89dce46cb45be3d1c0e9b52b34716825e24c085584df550286707" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "f45ae8c9456af4c1760ded4c88c8f1ecf39d98c2cadbaeb173a53b9b0801c335", - "excess_sig": { - "public_nonce": "0872f9a9b933300db71a1d940923b86c967a178a1af9f0df56689a28ea953457", - "signature": "ca30e0dc281d9da82b215a54f98eb8967c991e98c027b8b3f6fba663ae621d02" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "16424c8924a2ed5e5b3fc688fb1792609cd948409bd8bc53b546906e7920584c", - "excess_sig": { - "public_nonce": "b691f2f22dc2ed9fb7b54950602c4bbc6b5433455289fe6172bedf053c9aad22", - "signature": "8f837c7c5a6f93498a873f9589c2e11fa2e1713b2c392b7043f727371a6a020f" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 28, - "prev_hash": "fa18c7ade1c7d0811ac711006b3d2571c38d7bcdbad62e48a4ae71acfae9b697", - "timestamp": "2000-01-01T01:29:01Z", - "output_mr": "17f07484e818f1d59c788c59d61cb06a451219fece925145c91e77680e9c66f0", - "range_proof_mr": "2409ea017485cf043381af40c80239ca6f28624d555a958ab17878c5cc3137de", - "kernel_mr": "be136eefe6176abaead60ef6eaa250162e3352d46e43611b674ba9ec1a073349", - "total_kernel_offset": "c7eedaa70c09244b6379619db013bb0e76c120b670cb18b4a3e3ee58be428c0d", - "pow": { - "work": 28 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0417509fa1648e60c22124e78191c277652d4c98ce15406c064dac871140d877" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "2c73196bd7bda160056edd8e7ec7d5ec5ef3312340ae4489c41ec3285b8b266a" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "5a6453c63caf6f74ae7e64ca406bf1bba813b323c0eaf8256bd238da2908536c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "c494b14600be1cd074d77d837faceb466619c573efc76ebb5de3a95faaa9ed3f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "e8ae31ee949785cde2c8a0a2f1c3a52d0716c397217a17d354d10384278a6741" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "10c0f7e11cac4186ff35211e604a132c75bccb33910bb4d3e75246ee3f80a520", - "proof": "b2b7abcb2c5f4e03545e6c472608ccb3eca57fc5b8323306eec471ee2b353057885b9b7781855508eda632c78de5e64d9cfdf9f116531a9fbcfe3cf3917f52431499dffcd06cce72752eabcabf3e6baf3b21961003a06fbeaff225e5b2f5116f3e5b7d0644f729059eeecf111a545f6e1988df02703621734d89a22e7ec71a59c3088cda979ac22ef9fb3918ba886bd5c745aedbcd52735432419ab0c6574606e8df586c286206f6f1edce068a91144bdb92edb3910baa5a9d3f4ebf1a0403023d7cb23178ae4275e6ee66849e0a098d957c0627fcb1dd2164363403bfa261042a61facf03f80d122ef905bcc8fe04395be74672995430972178d37619fded3fcc604369be99e6c9e8b6a2f212ebe44638811ceda4800b87dd2140fa26d8496d6aca4db5ff484abc9c8e8ad6991092bc9616a6021698b2ba52879b92620af55c48876e08f69a8e26b8387a2e1b378e68c085528c3f2c323e3e2d6df62339b643f8179d48ec89943dd5aa42d8cf04439b40cf46b14fe5dcff173b5e1d29733b53a8db4899ec5f4e7ffabd5453af19342256a08dade8c6e976d2d2088a2e40c3149ab6514cce2abfa2db555c624767b84cd7548046f104c0afc062fced6eaccb1b224ddc77d975a534a3958995dde546ebdf5882d89ad3debf8081df0e0089b727a820807fc6946488739be6f3e7e50fab7868bca5c826633edfada8647087cd6a5a8e8d268f1918665c0ca8b9e3a37141ea1212e0393881b71901a0ae76c16c715ac9ca3a678df5f81371c6d5430aabd21698c4346bf4d2b294a3a91b60252d46b098d59b346f2d86583ac5ae7a4647ae825eba4b1b59aa623b5c6d68a6fb18063204ff92ce4d2d163fe4ea9faf448be9115359f1f7b7f4137d8fb830712845004a26f9fabd0f7dacb07718655a8e37ad44275aa1b8f4323d1b271a719d9e1109" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "4840f13e4a0550294bc52a3239a57bf646eded62702d039d19589e60df658956", - "proof": "7c735ad33a0e59dc1bef93c416eb1e8c0ad586e3459b09b9390c62e80d9e0c5f76ab367939b83e6a70fb3c78d503c1469d6941613dfb7ca0a35c6a310065017bfccd694e6068ba1de92a8c342d34be3377f0c0b99e33cd8a4d42331326240b2e88dae8bf22268fd6d742f9db1382a0424cd562624d2497ce9b2449a35ac169066fe7e9cbf794f65c4f3adaabbead2320cadf8103321de421efa5fcf428819b079711da232f9d8aa1d7fe62a60068a6e1d889f887446ec6d4792bca2bf29b0d02b729f2f3cb192dced7bd1ff132be4f46468a27574202b17f5c4ea5485b6aad0a1a06aac5d6d52a2df2deded00e83fbe19aa242dd0f8439145c2a00818ec4045dd4fcaa29f5b346ce6e9d87174c16fafd0094005c1ebfdbd169301a80b1074675ac03bb0fac2c7f2f16e0787fa35618a99aa5993463e3cf9fda36efaed89e8a6e1a1352cf928eabb59eb4107fe20172fb7da54cec9e1fc4cd4ad7182bc11a4b23344f2c1e76358779403fe8d3294071ef811ee33a0b2876de1fbcac45d1d091226c7f6fb2496c55a0b71133b0dd852daa30c02694944b2aed8b1613bbe54ed3379efaccb994b46bf7df4a8e0f72ba4fcccd5f4112b74502575dc26b3e4edfc815b0b622f86fb6d737c9f28e91301f4e1866511e9e2e99c2220f4b883aa6ed383c8ab2351d3016e3607420587321755e887c0b62bfe03c67e8fee40ac8e76a0b1f285a8363a8d857ac9f6019e471bbae319df72435db602cfc60e0b2e01d891625c42781dbe821c04a89bec8576ee3a9c311b70f6edfa8a7b0956e552550134521807f87ff53bc386345b31787d2843f6d03e23582cf8f47f4d01bf8eb51423d5fb2db29b52b475c7e25e9e5f0a8445cfd1749f3a3449591ee84761e2489afa70439f143a953cc9bf33b2f36f1b8d307d27138f2df4bc192e83be47ec77e03e00c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "74f8c2f68515a731fae35a9daabd217f26fdc2f027dfde2d270c48e9d8686a61", - "proof": "26d80707d050763b2e7bcdc0e19070807f820a9284b5f629c99eca1ff796dd48443fa22b577a327b66aa2037b6557d18971055735984b93c60d8a22f2ae4374cae8b5c2ed3ccedaadb4afb2efdd1f5acfb1e24a0262fdfe3f37b7c35c4e1fb27f0c0d04e98b64338a4e37dc0857381baa925c61f7c989d281aad4ed4f498e929651e0333a1d1d9ff0ee97f0744a6d2e2e2a1a5b0a94b49ea69d8d2b769ed1504d84232317f82b2f4a6d359e281cd963bd1a4e51ad15b4195f307e4e371403a0c416a6289e7fc39caac5886c0c8633cc2034ac8e502574803976d8ec82697f10f58aa89292c8ed3bf1b903e8b07733c67fe54ecaa605f349990b3343cfb1d2c0e727511a7aba72e82614583699821162df418346df5f7f737f9dee0d722cfe715f411d91b8295295ec588d0481662e61e9700fc86ed68d9a9d1cd625f66560b107a994a1401fca09eab6cd28f29d4661ecd17c8b26574faddd3e1042f94ad7724ca0c547ca96a26ad6029b548c4d7b098690656b61a39af0371b71a1cfd03d72fba12017789ad07e8086af0de47ae36a118f99540b574dbd2dfb2249fbf157145561a87f6c5e40976363124903ef72b68b4872578a87b5022d5e64a38fed3c477d604d09595da1f9b94f771797b48c709854fc7711dde9cee6623a05fe4745e36b6c4bd168ccc0057f356456689808bd4a064e56a303948061fa92829910d84752462db44836d5ba863dc7412d86b37bc4a8355ba340fe48fff6e5af52970ce0164452e236b6415f763053ef5c7c94e61f8088e860459e9a33218c7c9dc043b54f098b741e8b12d454cee2f70bc7e09d5b2f7aa415bfde80cc64c85d6e6048f47e4655b898e14d44e702e614827131f7aea6ac358874151448cf0d1abdbfb380ca9b4500d20564e70b2ddafd0f0e77362b9cc7379987d1de302c5ed1366ab2303" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "82e4dde26d40cb5ac9a90406d6f5bbbd7c1e78cfa5d40ac28d73a7e116ae914e", - "proof": "94118fc83d361d243bf9fb8095a59ee55294965eaa7bf925c1be7ec8d13f6771aa501faf08de0fd815f403e8bddd0262a8cbf69a7ba0a8c0c4d234a2c01b8b5a405f4b0c1705c49cd70b80670fb700c8680e886c3238da27ee952855ee655218182fb871b4ac81cb48a12cce96cc29fb9ac5aee6f27e49f018bf814c4967a547bd10ad41bade5ab70c319aa58162eab3c48741ca31d4d676454bd68413253e0164874fb46855627ff3a2dbfddd3fdeb9f91580ea0b58e3dd8c4772d73cf8760a4366ea2afcc139cc82b436ea7b29ce2ad36fe6c01dae4b4b9232eeb4cb9f030db4cb7b4dff06605083f81323053acc3dd7222732c18786e4d7e442102623693b805c85f15e9b95151f3b04e39f8153e9c7c64b31494d5651d8f1a2773bd34f55ecdc281e526f882dc862bf46ad005b70c7485af24ce88287d5ca9ac903059125805308cf75a7dc4b9437dabeac9c8c26c2ff85ebd13ffc43276048654c704f2e6466c0b6f2103d5fbd816988831692fc89880839eb30d8cce17b1865ba14ff3a6477a9d5f9f0836a36c9bdc9ba6be0b6b029ee3d6a17a2bf8e1d49d8bb74e02ba65651e49720bf4fe3dc4d29e84844a8c7ee613a50f3d5b0e8ee8ccbc4e4556cf691ffdf9138e61bf4954cd99439b561c5fe639d0c5bbc49ecb24a804093d0130453552012f014ec4eb3cc3be2aa3e61b1191d80eb3cc9cc9fa70638e1f6027c4ea79076bdffca908703cc27c6bfd6094cc8f980253a705b6d6511db9e9d9566bc90a46dfd1769a473f0431791f8aadf26b5cc7373e734ead2e0559befcc8c6794e66f6f81be3d61562b71d232f013097723736ddcac3a8106937577c360fa09d41c96497a341f8c5bc55d6e76ad2b37baa56a9ac68a84a5cff13a0dfe52fd07928fee96483d0a3af87499c4fa8cd8e1ba55a42a939b41e19e184eb6a63b4204" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "a6a5de66dd16b8d1371a8b729872defc14174da05fe2633b9b0856f4c4f91d78", - "proof": "c8b696fc2a75bdd08c0240968239da915fef7d623f559eedaf9b7498a727577bd4f6354d42899c7db940151b5bb196b7aad3ab74475972c195493537985c06456ad0371cbd6b773b0c325044972575473376304ed97802accd675fb56098876d363dc58f5d9afb1e3cdd22586d91c7b3656cfd05874a632996d15142692a86242f1bd138643e72a4708399208a17f76198359f73733af049917728593e33af0f35c248d68a1b6e2823a05faca3fc5fc4bed45bdf66f1dd1127e06365501ce207475015e3892f0a6038804eabc923b862a7b9d49fc5b5a8bd016e97d8f28fe50c80fccac44ee03b70527bada1e8d520952ac44f8f9c67cb288bd50e6f67016f1546664b89f02120deb54ec0710c3aa6e77605fd560e350cd3916e1144aae1f36e16bf951ae2780a1dd8c9ca9d82d16ddf898bca0f0fe8b8035321cfc1ca4339319297c553a20050e94d717ce219835bbbcb154031b7068d97a5002438c7d4bc197eea1bf6dbf5810f4bd2ae252e83b71e23624e425ff9aa432b2e436e1b6e527868d2e2cc0b239e8785b62f20eedc0c1176c5429fb9a8f72e05bca6b13f2fde49c4d523e9e5f66b503f17fd72b72f70fb08d72bccab73829d51e3755953bffe2c428d441baa111d855e2812c42a72589bbe22db56b7ae28eafe49907e2945c163dac4db6130d56a2f6cb8771f2e2649af33424e7e36dc8d76a576cc6be82d79778ce046a097e52df446d80af41ac9a0e2cdb0b49089756e58c18efc6fc805be76baf346cea89400cedad66ab88b4b3f79de388d1998065035b5943f1a9f512d284277ca093fad907143bbb379a521a04fb5e970f7bf024f0e46e30bb2391aed0375ef8cce644ea3de70ffc1ad9d7e6acaab771bc9f63e82516a1bde8d42947d0aa37071353b1ec709a86378a4ef415a579fe85c1a8224bc14c16ddf4b0ce37406" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "b2f9dd565ce048fcd282aee75c68bfd1328a5abcd26f71c9ddcc5b967d927a17", - "proof": "aa3a8571dbe156c3aa131e6d90011b11dabc11951b0d92e3b9a32e5e68e3920b1073549bcfebc4006b0585fdddbd0a57025ee73c74a3101c684ea0a76b7d2a641c5159e799cf1aa62b4dc46436eda3d32e8b7586595c0cf3fe930b1060e97b5668bfa86b2ab51379df613469cc2f5180d2e4e8963e51a669d68d5280aba04f783093020f82ccbdbb34e6d694a3e956c2fb723a6c8042064618f0115b9955820026c2146d4a35dbf24c0ba391100e105a30e77ad8f27a66cee6c5d5593f0889053d64505d028659647befde40658af49ecac663e0380cd608b35d95c8ff71a30112ad6eb9702344a0427f3175532373f507eada891a0d69cc1f97fd34cea24055d8a12d47007a055480ab7dd3a3a35e6c348fc4492635312d0b692b64c407e259d819e8bee8f825fe6aad3c6663d26c82a09b6238d7adc584a2e3e69f452c874820bb656d8d451499fdf4468db6482036071d4ed5b62dccfeec2907b7d9b4ca3d9a20bdf10937ef424f661f92f107573f3daeb38b9672add04e89abc5ecf21b1592eb3392d79ad6ab9bcae2d986096eae4556131acb096c9fe42c710dd0f17379b63a052a09ed3afc7292b6385c8e2691956549ea44340c1227867ec59552043dd232e7a008a732dc0a26603785471d83ac17cbe9eaf45a6dbec3595f01a30508b6ba5e9ecbb72c6b747ddcbeeaffa211eaa4de67bde8f5b2579d91644cddbb4bd81ecb8a1c1c2c4f2e6713ab19bdeea049dcda51a8eb266c9d87684f535a367ec46db451858c3853aeb07942d054c4251d3f359ab58f35057f5166742249f67746cde3a505e5896aab923086d24ed255398ec3b06c77377ec3aaee05f6a8c227c5c6373bc00b5681f30a78d953252e43a1528312eff0ae962d2e36ca7ffa1700f2d17d33cf138495e05e1b4104a56dcdcc43049d8b5644728ca0a164bb9fb709" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "cae65fbc3036bee2dc041556167f80fd9e95c7464449fd5c582d4020986ce92d", - "proof": "70f9c5bec5eddaaa278ffd22ff200cd74c09ea458c88fb891ebcbd0c0567e9617ed3a9aa9e13db4b8737aa273ba3a87f0b83a3526031cd3d8bf6fba2a0208e58003140fc6da7069a54bb52fd41d29a7d2dda975b6c6e0b4d3a219cc0fdc12010704e0473c338a5a0f158d59afb1d60f4f38809aa2a4e9f4bf83a28316b474a6afd8d4b88965e60f82dbfd6bf9e41c4e4f1b195e1728008d4548d75b9a09cf60a538e088b9ea3c4715295df37a3d1668f798f90037f99da7d47f094acde297c097d4b90290e3896ebde1630a52e03544287da0d86ea9c887f89ceee7064cfd40ce43e36bfaa1ad8044f26d19f6049d481b0d076ada2f8fdf0c671046ed1920829fa05d6a4737c2cdf00f30b884bcebb3bc351a65f2bf2115e1d9cb2639d25f3373e71dec1b02caf74e5a5618ece0e973b7bcabaf357fe6a1ac4cb12a31b51bc5d047e8b87df8358afc2cc4320038a0a9b0d57102ad961cd6666a58e669cabb76fdcb5e90309ee37bae234e21e4a0307e48f74b9af79dd83bb9d8828a0818f5a7ff2240251afa4c2895ffa0419ff4b4935e63f49a844ab5964665b148d21548330966f831ba8b5c6a6374c78afe40683d04a853be96706d73c6f04b89b3c14f432006a1d548b80ef7d0925b8e3531b305f8f3161198cf40095c0f66c1ff622805198a1e4cd554cb9cdbadd9b709bd33aed722265f3993a1b29452c1ed5984ff617725f671cf1db7ed369bc4372188b5779c0417173369a56175d347a08e7b0f161f06eaba04989fcc8f51f3dff6f0d47048030d758e017b0c98f7d9547b1c439782c5adcbfd234f7da63b8a54ca1833b480d09a5705d77afcd49063b28a4d17b1e11866f44fad390fa84085a6f1eabf5b02fbd4c72b8d36cf2e35e89065d4da205cfe9cfb72f7c18346d2e271b72692506d410c789cfa8fa354c9cddf1e4030f03" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "dee23439653f81e94cfe4bcedd82577693b44ba38224cb4343fc40b5a7b82103", - "proof": "04df0f26b5927bcb1d37da37d396e0454b765a5b078f0ed1313146f6af80820464c313dfe938896614fb283ebf58930aed353e7e433909abd9691acbf2c0de483a7cabe7ad22f8788cf5d0b4ee272eae03813f24c795a7c95367f8eb533a1c336efbb5e2d28e7872bff68536dd0fd9dcdc361ebae012e14e08a496fce2a96b07e6146e2e47e930cd3a5cfcf90608b7ba7590e560250146cee5ba546ed313e2095eda8e96b4fcad01275441dbe4d0493f2ce7cbb015f5250ded870bb175ee3d0a08fedd9b27bd2d2ae1d6c9608d93f753494e26bc01cee3df3c511216143625012ae5f8694493d78bc5634de0c8e28c902f3be54b57a14f7d085c8883035653771ea2b8572a00e9629e8b4a39accc6fd27843c3a50d06ce561fd3c1d47315387232ae7ff1e0bc471049a848b9b5aa8d920538e507194c28694e82f2519af6571454669132b7dc4bac5e4d0c54471f26c1d66e9d567f9410ddd7e835da4b3fc916c0aa9b4ba46c457da23159811b9671cd95fc6c2ce7b45e98efa9b04c00c809636c8cefc1df35476f022a253f54e12f061720ad8b66c030286a31f61a1fd09e4cceb119cc96d262f61713b1ebf62eb0a7fa8eadc53a49706806c6a8b8ffb4b569de431924bd8ed308b570927bbc44db840053e6d73b3b38309f1c39d88ee764030e1b388ab465d920655ae94f3912e593b42f4420118d5d0af1ae1b0c1dab1c74662e66d5a220fa59b4dc93668883135bf065a4c23e80dad30565c77bfc83343bf8a60188e9f3c108a63b7597993247bdbd07bc00e985e7ae00e4991e9c044931b8c08830661970b4867e5bcb8f38dbe5790a3f5c689d1e2983dc162b5b04c94b9dad198bfa36ce430611f50de56e818a23ac1cd8b57c0982ab6fa8486b464e067ab4314e95d74b3fed0184f7baa76cf2e8e259d08ffaf4288f4a1d7cb791000a" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "f4b5af29a6ebd5d394b174f5283baa0fd288ef2670c53afaf0904bb9ce358708", - "proof": "2a30307a9934bd2db36465f66050f0c3542fbb978b23a438691ddbb581ca80015491cb6829b40719ce0a9f5dc7b53189fc4fd0bd4c397b82531a5d8d57bf9f421298bd811d3c0d49602158152ce65ae0ca9490e48822a17cb013ad80b4e07f6904dfe7e4eb1e3ee82ebeab916e8e1b26dbe9aa8d538735e7917e5b4bfb19153c04e39804548dc91f648db8975ce8a01f14edf1a4804115e530a0264bba15a00096f21cd24acbe9aac31f02108e6810e08082a4074fce9d36cea78b18f4f6ea02ec0de8d8102026274a35bc4a81a580db3dfd4fbc847bd89006b06cdb04aa25084259c72aff48de41fb229568fca5ae9d5006678bdd2864f6cc9c878ace1eed33de840403dea376848464c6e14fb9251e6761454e9586b557fc0bb4e527987e44f0cd04d3aa5074eb0969f44aa5d1ce89f3bf882bbc288befbb827bb9f57efd2888769c13b342156776bd791c4f6cdd3ef11477e2536109ee914a31de64d8762596675f4d609f617452a03d77533abdec688561d037f5d1012628b8fe0bd9ee459806d35cacc5e113ca5518611c0b49e60189e3e6fb54607ee6cc20cfc19ed20370b96a5368e79a70dfb2b31ec2b61d019ca2296dee29c6db91c76017363f32355a2fd725083d44e602c0bd65812287759520b2164ecd8b13ad52a5b6287556054aa8011702cc9d0402f3b9bad4f53bb03d4ae1a0b99e412a5884692eb7c76f15e4697fe312018be72af1d2aebc721aca6da6a883fddbc49334929f9cd0ecd12bf6c671aac5e8fca4b9ec2d3bad654e8d88823be1547cadb4eebc307364eb1e4c00999549314cbe9915bc8520663d7dabdfa28926d681578c4836b2583cd74c11f6d8296469a2675826878734e0b3ef4b4b9aa991deaede6993c8f92ee41bfa06118df428ecfb771183aab65ed502766140d4860a1d579e1f6d000a106a25b308" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "fe40dce6e473aa68ed4adb76674917ae43bd9d1681c965ef57ea02d27fa43821", - "proof": "22f1e475bc63062451abf2877db297eb1deacea658fa0a40467c2459a42b2073661d5098c555cfd7ec0c23f42c2850259b34edfb0042a2546b6e87cc5589202d262063ae5ac70bb4fe214750ac6c80e3775a248ff293cf0c71c35215e9c60a042ac0c1f6fc7403190284638b9ddf959ca5c6b85e6a3c9705eb13656fb34e693da76b7318a794f304312eaa6a75d01281042f8467298765064b3b28ac42c8e4042697abeeab3dcd856b7ded0197b312a5f5b3cc16c66e36ae7ad25c14e2ace800e81d33576ded1227a080c5674888dc34f81e49732eafc068b9dc7de08d800c0612230d224bdf44c14da68c8f55afc17b33fb8556b5435c27a0d846cfd6fa5f4282dd29f5f48e23a72c1e942081859cc4b25d35cc59e50cf87ecec19568aac0299cd832759ee3a33a126ef25df3b429531d4a38cd624b040d8d6f649c4a619c4032283de7f31b889611ca7c367a468953089fb854c527613a588525f042d25809d84bbe322800373b4b21ef35efd739780b0025e84d2b4987299cbde99f1edf299610caca40ce1f354479bee9988f376da8c59bc217675d7eb4c16fc896e2c00ec6f2a3e1a90e26e9c784ae1244780ce7311ec8f7c0990776d7905eba979657210a24eab0236de21b957398299501190543e531a108047868383df9752c58d21e0880341824d9f557f9e1ea0d21ec901d1d50ff4164a2651c40bf71b6f7a2ef08daf2fa43c7cd84d061689bbf2a373d9f620a8399dbdc35026241bbe4a2fa0e152c6d03bba5ef8ce6a3441fe14a68eff03c48f1cb7032fb5d73c0cab7cc9c46108ab1bcdfdc26bc6dec2d73027180263530533c21155f9932b611f08323213b5b65382caa1b51f1145afe94ae51a1983a68e81ea51fc77ebc188d8b1f9ff39001743040eba9eee06ae9cbf7a3892f7464ee110876269dc458f676eb89ecf84804" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 29 - }, - "commitment": "665d598ab0b48051c3f006d4103e87c8b65ce2875ef09c698e54913962cccf04", - "proof": "509ea796c51e7fb6eaab3e4e08de115c274d82a031772a805256e4e273b0377dba2e5809c576158c57e3389339edea6cc5ed39c9153bde70aeff938b3cef1b386c641772d905f473c0aa2ead4045e47cfe81bf0f256c5526d8664e789ecd3107dad34f21975b81951913924073603380c547c568d1cc8a1a8528ffc43940364fcf34c1206142e850b18cbcef2e46435429a83cc2a34482e7190be34e140bac08be4e4207603f30e1b127dbe71c0708333c61ad3062960fd297b37506a1daae0b609bfc20a25816a1edfeda95e71f1b8a1ca4a8eb31d12618a6007eefb28efe0302e7c803bb50a8e22aa55f1ddc2ca83deb79862a03af928245f380103b466a5ad2b38e9f20d51b0932684afebf74e419b16da538354fcd0e33ba084791a9ac79ca12e35b1e0886180d28acce8fd1c1342b9269ec39ee8db95e9c07c0a05dbd4a16040f3285d115317ffe2a9b24bfb16b0c9136f44269d705a3f6a5f4c5b579505c9e857e665fa62251724fdc92313385413fa6d797be7d418d73e8536bfdfb32500d5abccde6f6fc227c0b6423c2f122833b1704f2fc214f6c340b8170b7904b36c089d9ce3cd4788c7acb627567b82af275313023692c8df52633c4d8577b594ad7aab48ba45dcd9a7cccc4e3735ec6907b5456b881d9b5d080f23fdd4f2c7bcc55d18b3bb67dd5adcb228887faa5fdfeab6cf58552072de6908e2a3663444e3c56a73097f2fab95d43caaa973d20eaf907c1a9c928fc06a81a4c8e2b2aab097cb24a5766bf21aee0aed519b6bc35ffd99bb9853da7ae2ff579b1943134ec6fda65b71f5de987b9c185d6f0de63c1402db37eec73811c1dc1d0481762e3571ef6f3642c7e4259ca933aa354e866cf8971f14698b63fc03b4ff07222ec001d0ac465e1eb3e8c5995a235e74f1b43e4b866588af24b06ff9f86eb893c145df30e" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "0c1a4699a50f7fa0709233855734565590d8ae17294268f567d4cdb34bf9a349", - "excess_sig": { - "public_nonce": "be1ba0121bec92a3a36b8303aaa0956b41057d538fc1b7c30494d9b8ba067c54", - "signature": "7a6a66d49f4d305a027c734c4a8e898cea2be3e3cde45f4b3c0528a82d6cf101" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "4c9c98398bed774253b6225e0b24c667a713e0f79b7d1bcca06660594a809015", - "excess_sig": { - "public_nonce": "62bca37f129f9d571934b05f35088c1da4869831036d37d0641572e44b34a628", - "signature": "42eb8324a791d62c85276ea4d02d1c44b6ef6cfe08b3bdf01fd9926145e14307" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "6ca350d07aa0ac852a94ba7abe1ad92a4cd083713aa173ea29406412e3cdc956", - "excess_sig": { - "public_nonce": "8691e46aea7ce3b79d4c997449c59cb4c244c4ea2168a2235fb680b9eea9ba37", - "signature": "e515dd2ea1c04a995e4219e2d401304edc404a4015f2bbebff1413893354d209" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "8e9ef454d70fe46fc4f533f894726fb46b6a4c85d0cc12cebbf425b1066daf64", - "excess_sig": { - "public_nonce": "36772b4c4fad2c76e5146beff2207a2a0cc3aaeb29f156c6ba216eb093b17e40", - "signature": "1836691078de037f863dc07f921a1a5c35f19b9b088c67bcd1f66854bb28760c" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "ccae99124923198b75b57027fd344dfe327b977a4db1ff8e83b0c924559e4013", - "excess_sig": { - "public_nonce": "96740dbf6c4504fec05fc74eed11e986907c0a0fddaa62e417e7ec8a2299bf7d", - "signature": "701abed35c405f6cd12d9b40c4ac111b834a560a855ad2d2903669e777470209" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "7e64f752326d20abdd6fbf483e0f735f282316e16dd7f4a38ac5b2345b620349", - "excess_sig": { - "public_nonce": "e29121aba9289b5a61858368de8655be47478645c3669fb1fff7809daf61e339", - "signature": "2f5109942723b07f8efc7ed9ba228956f7e8d1856d38feba0a9cdf0d94636708" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 29, - "prev_hash": "157a823282648bcb340444d9d488ad09e1d7e206cfec76b437a224c3fb39e263", - "timestamp": "2000-01-01T01:30:01Z", - "output_mr": "91824240b92ed89c53d24c086915263727814b7bd99c0d66406561719f16da41", - "range_proof_mr": "d5a6b2efc3195054620c704156aa7d2ef487748f61b28ec546cb579ee6943e47", - "kernel_mr": "c7a6dbb0b0a88f1da9b823cdf0106166c3077532bf4348c62fb576155780c233", - "total_kernel_offset": "f58f4e6a0e2b816bd04d4a39eaf328fccde522c529d46d44004c3a45673c3e08", - "pow": { - "work": 29 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "10c0f7e11cac4186ff35211e604a132c75bccb33910bb4d3e75246ee3f80a520" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "74f8c2f68515a731fae35a9daabd217f26fdc2f027dfde2d270c48e9d8686a61" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "f4b5af29a6ebd5d394b174f5283baa0fd288ef2670c53afaf0904bb9ce358708" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "fe40dce6e473aa68ed4adb76674917ae43bd9d1681c965ef57ea02d27fa43821" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 15 - }, - "commitment": "ee48d8caf578688003c654fdd2cb466ff90322923333a5d73c39411c7da1221a" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "1666edafbef140d890ee6adc74925be5ee69e0b96c7d828701305e2dfa1e7545", - "proof": "aa8050ffa18284164324e193f34e3a14d38a590df3066d722e562ffe5534ea571e12456b86b981791a887af757b2f27da5cbaed424a6946d0581ff1147e9c65a346d47837b5d722126060bd361de1e135a73ba8fb24f01235cc1525dbcc32704764507e1e265ef393f6121201e0894e65905fd71ab7c47b7a8d8e4ebb8e38a0783ee669ecaafa2f9aa5de1cb0f82c7908be86ac68761a92d4c9f25bada626e0ae1123d5fe4a9f749c11709ee36ed3d3f508be90cef31557ff3cc536808b2490438ef9a15399882512b75722f6c3d4c84b82877f620e44bba02102beb09f89b0cba4d24352d74a23ddb8b756f5497f377dd06ba9e7b21d808e3c6be7d701bd77902a122816ae262b60e0017fb412314899319cfbb160fac78f211864ba76cd9352e45204cf0f122f12f0d842c1d1d45a1bd82fdf675b41887e9fed257f832e87cc01b135c065ed4caf95ab9ffadb4bc75665641c75079fe4504b022f616a1354d08b61b242ea91152f58e04f1d34a7a002f28c78e26939bd9662f2e35e645347ca2a372ec12effff22a92cf2a9af06b063ea266935530732465fe4adc71c7f70ad8676eaf34945a2d6554a10439ad51db040aaf9d70205e9ed77138b44f230e4550f16fdbfea361d7934335591747cf6d3f529a7ac12a4137fa828629c041a63c7a08a6aaced75a831ed1fbb6a470ba1c7ae1d9c568633d802ddb8706859fdd624cd837491a65c7143e42ed9685947f1129e48b2eb8d071aa26807b75131fd95c0e78bbf21471167d9c093117d5b47b78df5b9d1522a87d122cdc83db58cc4b0fb47f848c5d6a7f56ecd36cb4e8d34c14445518efac5cbc51ab42b35c45580649381370148ec59dbe8698b1ae14def9ecf46a966a15d87e0a0f0c5c33a97f360a48e6e89e8b26466ab826f3fb1e4493ae79a6232cb337cff030512aa3cdab5a00" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "289663eca6e58000c1d7a4f7e761225d9da21bf7e7ddc2a621c58b680987c06a", - "proof": "a6b91b74fea2c0ed6dc54de5c12a8df2994aed29dc0958b09756d8395185b50e4e942fd059c4415855541f7c0c0e17ea9b765d882864a75acba56b66ea81da1df0b06ffd4fc775862218708b7b093bb3e4322405e86d4c5ae029fe1fc1d08078e26904cbfeac01e7ca72910374cee82f0725a4dfab8d196166a1945904f9be48abeaef7d28a08752e5c7336b678bd2eac560c7a7696aaf5b06b9c8ea6ce50004f132b061c5840d603266edbac121976c3b6e0e7d0415b34f6c3574b4c61b2901eabe0f1f10ffe16fc91d582515b4b5e140fff3a47a9196cd74d2638d0117510422cad2e22490ccba451c2632f769582253d51dad7e4e1e1d3dc03cea87f5937404dc7f04ce478f6038ec41af298623bbcd03cadaca5ccc06947cead4f9cc0a79b4bc35a818721127ef4bf19c44ab16e5e06e8d9b15f3861efd0512727f5088440c32551e8193ccf640a32e9e77dfade53f475943e654ce8155299f5505635411bcc3c9c55907d70e946c905dbe33117a61d9060d9b390a777e9961af0ffdd677565d13a949ad4091d2aaf679b6291a6f25e249ef5141d82c38c7ab23c335b2262064aba0bc0e2cc52fcdb6229fe56f9987a373ef1bda5794fe45a795b367f5416ec3f80cd90000984320ff474c489d93b1e1ece3d46f3751ee41808968826a137cd448c5a3e3e1f22800bad68e75127731e4a069e10200785cd6220da75e7e169adc2a20c7a71a482e82578ad9c1429d01889cde1b523cc87a7e0db9633cb8304a7da321b3fd677b27967177c3fe2566707fb4e22410a31f16901cc1d15f0c18e20c53ba47b4494b34f0da7e9b27c8b890854d3538500cb0e1dbb0805669d07fde35656f17150a01851ddb0ca3653c404240c60832499f95422a6136d8a17b0ced00e70decbf8fb2dc2ba1ab2769288de7bd1087410027cca027726b88b53608" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "2a33ce97d3dd6a727f30e36e40a6069137b442c941607c08f5b9648e05c52456", - "proof": "448e55aaa4ce17d496963384f594a3279cdc803a9e5279dbce2ba6141718ca4d4ce68fc8b263fc78a32ccd5439afb493652d52483a754e73e2992bd446fcb92bf26818676e79c989b03d3bacdef765b82493482ca1c11e67a0b78d07066f267bb62c832bd7be91325d8035f5c9f41c6cdbf9c194692f8976b48dacf8eafe7128f112b25cfc9c1e5575d271ba68ccb499334b48d72ead245ff37ac104a7ab2b0cfa427d672c2411a65cf1f0e8ec90ddd4213ab6818856525b63c154cc8b3321017e54d1f90a95eb1a3373cfc2f4063c7ed48232f2cdd34a74e2f0648fc4ca230ef6f494330509739a27161caf492b04952340273fa94c268daa1362759c244d0db6798d363fe4d72003e03575d8852eec4b3468543336e66e77bd2ac1c4e38e617c28584850349d643b041bf82c0d236049a77f7a280dabbfa292261948e4535d3a40b8ca8d9fd169cbe3244ed256bbde0b5319316f97023f656dcbfa9dab4044263eb04d129e92a658d5ce9eb079fdd41ae2c94acbdbe69bb664f4c09fb41c1686cad12c4430c84f361aee2e13f9f9764b9a30c34451a13ec23f4252fbe7fd5872045d963f56325d4e817b5aeee11a83b49545e3d2790eaa2596ca13722ffa50d85f02b12c7aa3fbcc1980bca0a28b62779bfc7586beee951d982b91ceed1e6114a61738fa6e9d3aa67698d81b617ea040d15130c0d2c13777dde3b5bd22796366a22e3a081ca6de6d2f17b4fe90657ea82a10edd274fdc78eb45a95d2f99801e8117e3e67813faddf22bfc34eaaee16dc993aa236ce72c8c9c665fb15c38727d04c9388a34feb7aa4df9b68fe51ea92c87660165c7926821daa838d2f126238c53e4967c11a54f3a755c42c2f8331a7d02598a27fc5c708c257ec4180c1440ca841588436cd04e866e0ec008bf19f6ed773bc5626136091675438b1959c060d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "2c20deab3a22e1c49c88b6eb3fa102abe4ce4c45f0463318fa54ae0d8b2edb72", - "proof": "68363cc5f6f5e1eebacbb32b2d4808364cefcd4b6229afeea1104ad1fb32e47188eca8e1fd950e10eeaa8bb9d3df2b4c0e0308e411c823ebc8927e02058a3a02baeb69fd3255cdc185150175914e036cec520b5d988f03867d68ec2b609ab34e7ee80f496f87b5d101e0c0fb6bc21e7fbee5dfa0269f41ac2653b52d10fdc7019f1a5ab825eb6c266f4023e16e273188d2eeb6d57d837d5f9f5f7173edae8f03dbbf4551f1dd8ba82196fc797f88593ee3160715ade81770a5cfc13bab76e40ce3756a60be702eb5c9c5bbf574eaade727bb36fe363c062fd4525f8d93d8670c7208011cc2c0cebd9700793ad67037bc05b7598c72c7ac323d4539ba7e99650932351f5472826d1f92d7669a7d91d2dfec873a089056a88af975fc07c0c3663c3e44dbb5b0fadc6024b251caaf0bb124e7956158b46e93388178a081df286a33c88074c025ea5c48318982e397cbbe4177a6058c81dbc736d6a7a33a56d3be28b0a4fb54b4ec335583c73a290e66db6304626af81a22d1e4399b478a2411d717de563371ff675b3b74377b660cef707e5c51fcd56fe30086547589d1930b1526c2aefbc134ca82a22f12d20f1ce4d4aecdf049bdd0ec1479c313fb96a99c9028fa4c1e09b2679bbf14b85b9220681037f3a5e70b55cb09aee719e0d3739fd5426887d9eef6f4509701c1627dbdac254e48fab072848b03ccad94b284b59557331cf6c7df1bfc6d0ad03209f415ec4ec7e3e51e8096bb153fb06b0120789a833e1c4eecc81bc797a55b39ef94b8822dadfd5d19a3fb07c94664edf62435e29f1fe8d9b5cd9d76cf5a183255f8a9adffe21a390028da2a9f7c3f1869075efa081737c98a0579ca0d018ddf867685cd4a93eaa21e63ca5ba84ba0475715ad1fe50afa7746228aa6f481269d7002aec9723ccf7487390bd0eab36e86065c9a47210b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "402e9788d94cf81b05956eff6dfb6ff63f85dbcaf0db4970840fc5b584778559", - "proof": "1e162c27780bb60cc4624d1bef8487c7d3e6b3a59676aaff03ec75f519d3a74b44263971aa58f05b8c4430aadf7134cb5d2a8590ad6ee50ea970cb734d3647270ea8c1b85b7aed66c1d545b37236674e6d478b1d6c15df1afbde79731d7e7e19fe7e5b2279212d86449017316845009bc9a529d55789a8a74d6bb3872e6481540c6194e8e660be728ae6cf649783c6f2f21cab321e135093e95ca4244c185c0001805f809fbbd6664d4f3c2b296056bc9520062c2571e146d0d50ddfe9cbcb01cb359bd9d14dfe457be3c5e6c7f13a1f8b6017316ca91485133ec21a200e5109868a40b70bd699cbd65a247fc4dfece228bcd2c92ed754bde2327c271319555fecd72ad30f394d16c8f99fbb234bbac946cc47c4ba22d5880e8fc3fd048ae27514ae96fce338c42f68087633fe998a10ea8cfbedc6958cce80cde3fd0c85cb645acc420c58fc3fe8f12e639dc4449c5d4c9cf53d7aea9dd982267fc6f7ab2168faf74de1c30418110500a61bdd56db91a7cd5942b3eda5465af6ca8683be734d8805ba38272411998899702fc9bbf3d86c38282d8aec0d522df87b2765e0ea339cb94e24c9105fad95a2af6789fe784d6569266750b1c6637b4d7987c1ca171f9241532c39e1d73aa9d305a6c00a76de63fc7e8b25535375ecd3005bcd19e24d3a60f756a87dd8c73d68f04f759d0aab5391f042abc79096332bab09f07c4011dc0071a451fab534d207087d2372deaede3ee866447d5984ca936043755738726c49fd829c1536f8b5c16ca20b8c93818fe342760295373890648e09eeaa4a701eced2cafe1378e38c22fe9bbbb103adb0c224ed4822b19010ffa38f9c4da759de34aff74604d91d6e94f07c839e3d03699f16fa88f437efca5a76938829e50ae2a1f6964026480b3f63a681beff1632c2a8222c6d39934a6ce5dcfc75ee1f06" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "7686096287e68bc232aab95543eac1c2c471752a181b7d8bca23484bd6a90b76", - "proof": "eeb50bb898ed17b4fb4b7b0b455419d9b1ad9b93b3a9b8f1653393418e40f257b64a3fba2389d476094febf462b6b926dc5cc42aab2238d1ef83a699cb088a5fc0062a0327f587f6eb75b245da149e0c6f89dfd1286785b0317efecf124af52d88e2c54fb3d6da04e5dd35f6aba41c20695c7d9975671de86b23da80fac9c67313dd980d75f546d446a05dbfefbc8f783bf380e541c773e2c056135b11266d0b35d1b0a7fd9d5c078368315db9459be123d4d1615f35b212924d120069160002457b9b34614d89ae57f3e6bf70e8004e543d1ae094380d4aa6503928c535f70e1a62d278816fa34e45fcab4eeedef6ccb285d097ffd673279e521fdeccfb8d4026730024f80e367177943cb50fab8f3a775e6824f9a5fe8b53b8208de257df5336b440e58e7b432c378ab66b01ca7acdf14445cd7837b24306519457c9f3200ae6aa5a85b029bf49a10e00f9ffc1dbe55f4e030da60e22c9a9476029efea6a4f2ca20057b2ba8c12541e755db97ad841054a9b9655d85ad3ca0be7f0654f415ec4d4f9f3e8523fdcddf49fc12ff3d384095cc40432903ff7eb384c8366960e7de86a6f7d32d608e124df994c7d4b4931c54e31947852e828ff6f61c432cea26a4226f57051e94bf860796f63ff7ce44c92bb3a08504f010a90a77969b3136c56481c3bb79ba5d3151b67e3bc6fcc2fd3329512248b62a8f770f4f23fb1871955d2519e9db9dc9df68b677224d1b75711f147b748a28cce286241710e1dfb4f34cc95fbf784e5425c583b390336539c72e56506efa1dc42df3a7aeda31388997a8c9f5f718f318bf1768629886feaf8568823e8945aa4fcf3b3d9fb5a5a7f376b16ec44b32b1eb4b2d9ced4df34a370f80580063d8c7db5488999d238779b1307f996176a7a759b325c5046297b1e9e168b7f6eec6b493ef33c05dc44b713260f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "8abf4378c320982c4194b743d075828c884caddc89be3f41c78c518fd8fbd55b", - "proof": "763c3dc2b3676fcda0b7e9fbe5b5375e54cc282cbbe3197298e1f058ec828a498e5bc9640910b38fdfc84e6ff163e54fc9f50b6c728bd0621ada95d196ddb371cab9b88d229223323d6facba13d11071a8da47377504d659b9b1cf24f333f91062f34bf8da0fb2e64d76331d9e5d2dcf9afb4042dec492ac9e3bde72ba977f1079f9170607c718175c01da5d75437a513ff0e24c5cc9c8b05eae497d3ec09105eec20b82436805aca4d0c76481ab1047c4b4cbc1b6d88da01ca23287b32ac7063bcc053dc2baed5302e98669d868aa7b4af83ecb6549df6381e1df1ca3d1d209a4e1dfc1cdba16049dadc277dcbba64cf727bf6f2a6325d73f702d0aa902c066788d4f763c83ef764f5c0e3177304cdf10e05108da94b836fde6128199bd0d2b9af5b0b3cbe7e0615fe28beb43d51cdd97983e2c9f61ef58349832ca8006692814f0c167972313006d8d0e43af8891c52d858aacfdd06e6277b9cf5f7f906246464c872a2715f2a9d95158fd6bc5134e7f243c7f15c354122d69f26ff2492a399a40bb65d367181431bdd5e55a45a0e5d8e44fd31b92545b3516fd843c28373336f7918d0eb05d6408a59f89bfa7803d4f9353515eb96b2e334e7b9ca2e0902d60f115a8a870d6b443a28c79abdb5af0aa14595ac00380539fbe95e9fce2356adafd8c4b6c437d377a0f0cb6eb4ce5ed98c4a2d08e6c72e6bde1930252f6500982a77cfdd22e25d9cb223dbfe2eeee140e5cc8c2e7f77bc7993f1adbfe0f510b4e2d7aebd07e293e71e32b903d81a03deeb068c6270294d1aff4ca44d82ec305cea8bb5dffc32e671ef06e5239fed12b34de3f051043b8bc354c060da786652f0a7450da0554f7a994623c488c24f258c7de192969317ca7e5e482297d3e5203a30cacb2714ea425589de6e6b8113ddcd668bc8944a3f8dd782181c2c7fa3f0e" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "8eb2425fb608f9f0b05236a1754d5d84369a59e4c2ceb8fe5ca0fb3fb3b6d54e", - "proof": "7415c0355ba75fafe98f535f8c33bb32d3dc6393333667499dd60443d43c612fae4c158ce5f360e0b0e0972876684d9954d8006822b24272ac3bbe74f8913777040a6a6b1204f98017cd9c2ace7c3ae739d817126ff043a629276086d78ec0775405025953f1e0d157b65b1fc569a42885e037e6b4b8c7005ad5d2ea29dcb3364d2b50db980f6318a6f2a12a3615a575b2d2bb7a394b8c53813b49d7b31c170b482eaa19c6eb16afd91600d37d9ccaad157ec51af9658de41abb6e195c66510fec519da80f301513840f11b25a6fe0d38b6a84fa253e3e3e4586efcf533a5d02d0a5b4f88b44d73f211d181f788e48b7b7ab381b4f1f35c1e3aecaf14eddb12042abae8a4167c0b2a67c08d6ef156f874d3ed16d8a344dfd6bfa9c41e209f6025c0c9613d88739b04b67d9e7b5fbdd4f38e536d0a56ba6d0e5be3b184265b473e2f7267777e4aad3b71c58859b8ea5309f6490e67862641cd94aee159ef4c851ccf843cc988622db40edf9942ec104c881f365aa08e511438854a804c898695058f6c9edf767e029ae71e21aaf4acdeaa3410cece73c37b53cd3c63bdc0c3944f263b87cabc587e430f8d27597e377f97bb064a44c1656315bc7c55966a25a0d2203e02432cbeedb4cec26af5e370683c561983587f530779530401ea9a04465d45a917d437e95a22d1a01eb73c27f96e2e9bda3f392ee2f07da32018342b7656894b38541f68d3366a23ced1085ede8c384faf827899df2f11f2303630cca3dea81a12f8304410d19e585b1c641e15dc44b0fce6d2a1c3dfaab9bd8dc33b42622fa906d8abd1e48e513eea4d246ed4e3944e40ac56441273acd83eb3550e123ea4e70d2760bab7cba5d8feb5cf5dc1a8cac2db15a2c2187bae0bc9c7ef0890cfd00a4e52269958469555f17323a4aa86924735a0793d9004c08b7e8a162d90d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "bc5b6812c196b217106490087ca9d11add7c617708f8e15892b5410ae51fab6e", - "proof": "6eb8aad811254df2409ad3a8d15ec0f49560d5eaf23977d86a96b27ab1980f1b38b7b2d780d83e49094a416a41107be86171709d16110301167096250b16b747806848081723356c986322b5b8f064fccddf171697e4a31e547dfac5418a0109b2d1606c2effee59161d72204b49aa730769a1ca8e360a47b58aef37975ff15cf471774a9a145280cb735ec43659aa5b711499e28dd5949185f08cf6c48b090d8abb4804a52db4afd3b3beb3f0504cd233e1cb75e164aab77251454d9c25a907e9f1b946a7543cc8ea20bd0ec668e27131958a8f7d0a464e316379fe2ac9130ffeac5239996aaa1e43f1d5629394aab11cb64d0295db74464f129f222a1a711d80a04ce4619d98b2f4ff2c1b5b981d4a1048e612d9e6e056129cb2ffa356c60d4610aec3770d8cb0b3d61a6b37af7e3bada7b86fa5686c578e0df576097cdb43ca6d3cbb023fb5faddb9aee56d35b9f2b83860b98f4af1f1ad42e336c4f989573cc131fb1928debe837c565321d9fc78c96157742d69507214fcfe4c5129ce66f41c32211734de066d903184b5124a02c52a8bf8239f4f6be23cbf5108893642fe6f5335d18fa7c39c465330c07b203118c103879849882e0ca12d0e0cce8f2c064b46f7cb890e44c13ac3c319fa7e7fa60655578f1555a980aae70b90cff13bb2e923ab5ecdbdba38ae036ec4fa8100ce0ddb81b2bbce69443d2e759c184f6f56406e7fc55b964789a1017bbf42f66ac680cc47a97ae520b770997fd432c4230c741b74a57280893a4490567edcdc8f3c8bf8ead89d4f17992cac281f7c064d8435b8131f18d7032e0b8a8c8bddeffd3047b8120ceb1822a63908430ea52a66be66330936d8e8bfa31be36546bff37af7da3edc1a8c4ac76f7e0b0fb5e8660719c6aa4de12829346d7e9731bff740d612ce2958cae8151662db52bbae9e4b0d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "f8fbd1941af8cfaa53a085cba1c975a02edc0fc52cbd49ee9ead30f07d9cb371", - "proof": "6854e2d55b37657749412d2d13a1f6cf24ee17dae7a34e44c97447a44c977e3a16ea6037093fd1becc80f33408c1316272e8b7933ac967977073ad8453d75b4f2a8aadae17447a41935eb22f805b611ff107a041591bca17ce72e59d7cb7bf3252c05eea9f9f456db09db28449b623e11116140bae4b44243f87588f047184795a08491b18859c2cb9b083fdecd056ceac9fd550b124d24e236a2a0d93f8ae07d17713da0f757cf7b55f696e5a4d664c01179f64f5a72f7055b2525915245c0435c4ca79fcf26335dd4394e4cc6ed3eac6da163ba2f0f389fc76482026a7f40bf2696131a013ed1069ae835f4d40c73b374f8d2c628d5de5109a03df1930a92a60ffa986c8136cd3c899dfc5d4bef211269404a7ea8f175d15f30d65d5defe5edad1bb29cc22386f3255818ea8d496ed6d2e094b29a6a5fa97f8a71527462c591ccea6683aa6fffcb6f5af7b93fc16b7969386202077f6e6e99f454aa9e9c22cfccb33b3190cfa096b05bc4494a433d409198bd015b63832f2c95a1b0381bb1372ba295ce2333eddabfd55b28bd3b82df4b408d2037c4040d76eb48cd1f19a31a2caa3ff18989edcff9a01c4872037165ad8582e8f044a2a8c6cd75fbe1fb46454b8fb0d38a0f080441c2fd41ac23fe85c0d725dc646afaeff15f7c0b506635a789cc5eca1e1a74b49fdc8c31b1aa31123774a213ee84829d498022a6eac9c52e235eb3a47e17568186a8c021387e61dc8fe34a67452d2165519d46f6e7c32267ad38b5f993eba21dddd401a9c66ffac7ab67aa21423e9804285d8fdc0746330d09c132768b12e6aace51f0e83a87266873f323655bfffce6b01ddbbe95fe100ed505bdbb41cc08be49083d5f34da5667eb53cf0c4a102ba0ea70c337edf9b00299ae4a49570373a620322f9b6d15b8d64e9890d4c60eb3eae1aa3bfb57ab905" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 30 - }, - "commitment": "082dff11fb3beba2462e363044ac403cafd7e467e201a68ea00032027ded4902", - "proof": "12576d14a01fe3049a0ffb18575c6d33eba4af620ce7c2f86f76e051f22891693c922673b1825223cf90ec18939cc6cba94168b0e8b6d30b09bff4846c83653c22574779e2755697ff6e8add51e153987f02e379471bd5c48da3fe97ca60ec38eada8c72dccf2a21ae6825c860f171bdfd0207548bab2d3135ff1cd36a1294553ea834e403d241902f14ae5032212119fa1ac9d2823df74169b53a566f4c2e034c0f7793ecbf36e12663bf5d2a0a2f4d65806db9748e5b7abbbd29b44af0540bd1efaaac18ee90c7d257f1ae1d599877583559c61a8452cec5759f05a8884c061ef0e5230ee28e7df7a93f811c62cf9e52332f0e774eed678f7f7d168e13f75f30e267ff67e47d0aac7b1d0d284ce022bce57313c152be58f8c44b7080160f78b83a43a08d02f4b56df92ff9d8dfe02ae9e73f45e33514a5b27b1a731ab1d910a21768ef46a2d9b574d844bcad0a42c4af34b126a9125cb1455d709e899e0c23f6dbc086e3087f4f99d4dc608c808ba69cddff12f35780106fab71e959c099262c3232e994543d429131460fa0be05efc8c3e51a6b605ad6a44d57ba376a546e0291e141dd6a00a111571f4268781aeaf33efbfe937a2415bd756a62169e0642981dfa225bc9b4c5f64fecba2a8842ab9dd5ab0e25f89416530e1ddcb7bcb00d6cdd6c9ad4f58145e24f24dadbf72c9aa49db2a3800397cb4ce758321c245d14842d56331439eabad2dd53a41966f91d33986bbd63ad648eb51886e99fe9a26e32836f51ccc74480a443b2eb567481d5729dbdb4edd54a80f22a507f71998d4938b9423faa9a5c5bbf37981dff618b459a1e78a040662ccdc89ffcd85a91063211de4233a2546bf588e68a9ad776d02ce8b24e9a5d94c57bbb32182c70ee25079e8f86c8951550d2355572cfb061a7b724097f3affa9bd18cb1a0afc169a6404" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "00d55ce3390ceea88a122d92ec945c454b3d1f0525355bd6619ec32fc05f3117", - "excess_sig": { - "public_nonce": "42c00b05810bdbc1a20992dfb2cf287780c95309d96285fce2728c3a9414887d", - "signature": "40c11890a840ce5ff3f4e75042030ba4e3ae2823d7f729d8cdae654d6d7a0a08" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "048d7e6460605c6f18f261c56e9cf721616ec8c9412ef1562cefc8e882324672", - "excess_sig": { - "public_nonce": "802f0503032eace815be4f02fc623418eb3951425791b64bc49a745e2d4c1219", - "signature": "2bb4a645dcb4825e438ac0620423803a806d4f51b441fd92747a94bc8582da05" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "5268db1140b743cf8866bc21407b47b74cfcb99bd2967264c1062b7b35b9ee53", - "excess_sig": { - "public_nonce": "ac213a45de69d2d3cb37524e9a42284fc0417e1eaa19b48b972dd440861d664d", - "signature": "20584a8d7e886afe496dec8458d89c7d32e236fa3124d214827a06b92b7ef40f" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "6eb4ad3facea88ab7162f81d686bfb8816a11088e4c55622a65d4ec448b0d52f", - "excess_sig": { - "public_nonce": "089a3bdf1d665b5392036209236a9b71545fc8bbeba33b929062cfafca49871c", - "signature": "912970689431517e5378636fcb158fae27b34eae2701456b465fb9d9160c3e0f" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "c04123e2e88ae91bf32492890fb888ad536a8e1305f41e29dfab08e6dddd805b", - "excess_sig": { - "public_nonce": "3c877b33124600ae1989f3f29be9d15c45f10e1e56e39643187743dc15dbbf61", - "signature": "225bbe658945bdf155c6035e4c782937a168e923650b5a13e12e2e2d7376d60b" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "c01f3363bf5b11bd372b50d6845c5e654f27a1b1d42bd6c71f443d00adf29e63", - "excess_sig": { - "public_nonce": "08026ef71299187f956cd352cc0c0b80bcc125183209e3eb2b8f9f9dcf39af62", - "signature": "02cc458977e7a7cd2934fe2c3a42dc71989ab63f91bc0d2ea01074d18becbc02" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 30, - "prev_hash": "fe502ae957b5bafa8e7518cacb5b4075bb866a83de3e9951fdacef36a33d4698", - "timestamp": "2000-01-01T01:31:01Z", - "output_mr": "12737e06cb25b2c325c04ba50a58f61c9fd9ec9ff5c9a84546242a11b30be498", - "range_proof_mr": "af395012f83970853c73cbbc1f0bdee578f19bc4a1138b100f69e43d90dc3e81", - "kernel_mr": "12b0c4f5a68fe2cd871fbc42a7ef03a8aeee9bcdac43690e450b06bc32d95494", - "total_kernel_offset": "97eec939608f3ae44023375b2bed19bfa656105fa484213f19d04326d756bd04", - "pow": { - "work": 30 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "2a33ce97d3dd6a727f30e36e40a6069137b442c941607c08f5b9648e05c52456" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "2c20deab3a22e1c49c88b6eb3fa102abe4ce4c45f0463318fa54ae0d8b2edb72" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "402e9788d94cf81b05956eff6dfb6ff63f85dbcaf0db4970840fc5b584778559" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "7686096287e68bc232aab95543eac1c2c471752a181b7d8bca23484bd6a90b76" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "bc5b6812c196b217106490087ca9d11add7c617708f8e15892b5410ae51fab6e" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "102aa79268c694aed029565a776f594586bda7483a5437a07214815912654136", - "proof": "5e4641c9c173a0269c7beb5457e7282a60eb760cfbf66f86dd1f116ee46e1c3a006cb9f71dcdf52ab52e004e4d72eccf03729e2091f653ad496e1ff2fc93b6262a508b4d74cecd2be9e5554d5a60a2e20c37b3752ec0edbaf85568c570722f73003536567c690b33256dc7c4cf0909c7790248944765de82831171bf69b8bd7d738ef669937f17f41c7a78742ea76aa70110304294237994ce2b845145dae50d42ec1cd0e0addfc4f5c04e08108494f93c81c37a3d623f3768b974a9e8501301ea15333023bedabb840e868906e19039af7dd34d9ade8c1b7c32ee9c791aaa024854eb974f10f266523e9b9a0ac039ed248b7a8a499500e53080d9d4d51820141ac2d0b76d3befd6115e72c3b5562017f6a2aac88003e27144d0eda40022fc1050be9e7c4aac027715146db7f602509d07ddce32052bba0389894a2ecc3a230bbe010d5d8a0773a0fc31ac1df82f001567293ab3c02cac4f08b0d6d64383e36c80958b20d01c208109aca99c013381b8048d77f38e6dd4cde764996bcdc983233c51e89c7348a8d8a94016e97b740f2675a3f2f9e0f37aeee37c24ec54112159ae90d5c71c84c17df18d4d5c57e0acc819f3dd605ba471013205bd0e03e3877fb240b2310f7bf5ed2a60977840eb8143f6d45dd92ca82dd0957e2d676c27f263525608e9b0447493e31f7c4b423a8ad8414149afc9b54102d15a8cf4a2da36064a515d25381fc470088eb78c53f39cf56fe95e5cc55a27c040d7084879d4a81702cdf113bf19f123cd829bf2c8c9e03f24d76e16076d287eff7ec7509d7621030262ef6dba3eae2ec4cb73c814793eb2d23e6b9235097d2fd91ad05770366a2ab053399600aa344f7fd22b45afd2d712e5a9668e789b9517726b94aa5998df0f629ba16583c4eed742d6d472f886c1d9aa174e7a2830473308836d5e8f07390f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "10f14b3034bee8b0ba2c1d38d317823dc4cad470773f5478886d681c6ec05340", - "proof": "98f1221948aafb6e297d76cc390080284433f2ea946e5bf635be9f79a924a364c821029c08ab791645d7bc280295118a40e6d54a2413b721d9bd744df138b8619a881c99d46511a774249be508cc42c3efe5f416dd3efdb3fc8d9e0779da13155663ae61a8b524e0bc16c48b2805c116fef9666490cd4d3df547415710b0ea2faf8dfd4426a7c93c80eaf023b03a02649f2de74d9b2bb84ce015399b4149cf0744dd485f33572d939d3edf8fcac3360d592e75f60e8a82531984c8619367150f319b1cfea10450e9a2d6063da689571ff20f9b26ef05baf76301548afd927f0df613386bc8eedd0227e799216186a2489de036595039d28725c1ea0b93f0d746c26dd364567667d6f70ecc80e23f2fb8b5f5557c83a8a5d20bd46c01ed49563046b93223358951c544031813f073feea85516a492f8d2c217c8559f5aa9a15290481d8621961b76adb2db3edd4255a13679c0aaa465792ef17fce7e1ab940d034c2898bcf3a5cdaf3b22c1190a6fc48527ddee6457091bf0c094cafd597a4b35c649079640a261bb8eeced0a3c6c5ac5fa3386761a5089fb7c7e5097d5ff0806c64a3bb463f2f3d999e30ee84b42486aa5fc301a482a8f5fc273ea2fe39cc0141ae9e77966fb088e7bc804f4ff8130967dc10a93f23ba6ad45fd62968080c0145cae6e68dbececf3a88bf84e79849ecd0cb2d4f551cce04310534a0e5d10ef404aa3b24764e240655d33637ca0ffa3abfb2b98dae265850b6d0ac56f974e1a6e5a6566bd9fa7b0ab4ca0b2ab82dba4eb6baea11bab62c5d2e74d3abf9f778e303e2ccffeef6ee61aa0024634032c870b543d685b5ce1b1ebfc7f4321a7116e4571d4c60acb1fc480e3ec98dabcd456d5af84a2639016b8238e0f604b375d7807fada219b4f57e745ccf0dbabfff61eff9db3eb78d8d011842f2f4e923c12470d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "367a70f6186eb5b126b70bd6702a16e182fe4a07fa92e5a6edd4ae4b33ebf023", - "proof": "b00cbaf544f8f6bcdb70641e2cac3cc9ad9e5f8e8d6b29affcf8909bd244bf28842da190d3fd1d62e0b83b1436b36ce09ea8757c808781dfac49de183d33ed0a22d5b31c39bcccae19da25ba1c6ce8271cea9f7acc112a84e2818daed0ed42390a2146937e96fb6ac6fda6b6616d72eb7cbe21960125b3c8aaedefa1dad0e3328e0df2a4d18fc5ea6d909d9c54004b079cc8dc290d9a0acb6b3959cf77fcae0b0bb65a0c704ce1794e5b00e1dc3031f3bd2e2bf39b78b64d011d3a4f6b435d0c4b8a1d8a80571151620a4603cc95631c305d936bc3f1c5bd1e7f2927795000092e43f8e90b2d7c8e5d2f7b28e48905ee45dedb92b41003a82d247a246d04c56068010c301990c4fb7ae145b6537b39996dbc126321f339f587b294c389f2231308ba72c69de0655589d756403212bef50505496994efd0137815ea11996a5125c89560918d6d617f3d627e2f4cd09a60c70574c9a5fa28fe0a0e6bb6de154a1378869b935b6da34712fc1f397833c1aaea763a5bb0c7f196de215b479fc33864ea34391e83108dd82242ba3c52bcb44e581816e039164a6a5fae91e685991933c4a98beaa3066698ff4c3e8da1b73eff333d0119aab6924515160f946572881e862b1583a5c697bbcaa5c8ee2bdfe43ae628ce6b0b21b21994b927d4a1b9e8545cd38c0db46e8377219a1d32ef0e3a151c34d82caa1be44f8884942a552acc40b04826eb7aec5356fbe60f7d23f73107f1fdb0f5249fd363d1849f6686a44c43d4030cfd3b6b5d78fe5344c942daafffdcba3a937aa0310595251fad4d9d9f4a747089c73945852bf7a03620ea38558d0fe4cfe5bada84e0b0ed263eb581387f940fc75319c133992e8ce5906686fb811eeb69b1081e639ed7ae72c924227a014439989294ec9b83c6c29bf571acefaf799c4bb4e1989547c56fc42c8cb5bf0d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "68a1bf93f6e685e900c7d70d8671557e7ebebd5fe34c8efba0e3aa5331d1fd0a", - "proof": "9cd2f3ee58926b5c89df3f09e33e3026f7e1e1172aa45d507f720aff51fb64619ade118b88310d0bf0450345cb77f6d243c1b626e5adcb299512e6603e4c1d2148b1dc3c3f14cee8e540729fa7cf38a1767fa72bd57f2a8b2f29380f467ee1794843d82de184ccfd3c2cbf46a125789c2bd46ab36788edb4ed291cb70c15df2d041fc1442991d6c4d8c10576ab01bf713d2f6e47166e5aaaa1d3653ffc8eb807a8b320ff143aaeb9d96b954ffc4541e6db0fce0ab214f5562f6709989e1d4909d92f99d4c367b8877d4a5f9776be3f4577c7d99260757cac5b535592cd9d0b027878a8f82d4d46250ee91917b47199a16fbb401ebeab0641ca5afe4a9212120baaad69329b8672a262ec6e8234189915773211fd3a73adcecb74de81fe0cbf2d2ed1f435154772af936d95adf50d775eed73ac8a517bf97c583d3863e109a37652d1af32b1023d2fd7eabf03c8c86729dfa834317460b24ec013d09e16469a0a580b0bd5791aede266bdaea8eebe327ef7b0b1a5c4411d35ae48482a688f1a04a832915ad67e572d291c50ca680714c636a9336c52a6a351993d1f2609457c5b80ed49b4902b01d7111b6ae50ca073a2de56fd3e688f403b05370f733d551a53c68a25aea718593b22bacb4754a16fa530a993679f02bc6769841df4a1e5860a4e66271e6494f428439203070b497f035b3568ef5ed5dcc5c90ef74ae032d0217a64154543f2e6c1585819cf529e696a6121cdc8cce57b3c4153e0f09c50377cf209cd0c2601f816c6c72c860048eb25775674c5f9882f4aaef2c2a5e1f9c96bf2e0b217d483b25c8116169f0ea502b2a432222821a2e21c2006a1ba4417f85581d8c7bf3b277ac4dbc9e02e9814b0b19ec82136ae0b7f8b358c569e9b059c03d2b8b4a35411f28d206d1dd1665182a024c1d121482b1f445ba2daaf20d2570b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "9c8aff9bab99cf476288da41a7603951d59e62622ec156bba8f9aa4a55e5871e", - "proof": "02a62022ceb3cd9b0b0243cbf81a55dd03e560ec47c9e021e5b46a4530b6ab7e58c61bd1b10ed24f183d1260412c63429847f16a91d2ad18ea74943190065746e2fba789a2f4eaf5ba08e937dcd877ebc14b25e014eb02b5584bafaf8d4bb577eed8e01e48df17638ab68f10afe8598c2247a6a100787f3ecf2eb21035fa963f13f640597a7c9bc6d5aa4e8f9d57583aa09e92ee9616ce023d3e836110fd710bbdaecc30ef36ce37000fae864a5498e4fd66b908b4cf771c0fb9eba1e0683d00f25e8afb90d56f3bc030a049ac4d1a4afaaba5b22e7dc9df89bdaa52c52c730dbcdf155ed53ab62af621d152b8aa9f12839e8dd631ed84d86beb97bde3b8f2301eda98b9d2ab995b15cd9c99d2ba15989c427ee2a42b3cb7214f68ac7d99b525e24a0817c04e784d39d74677d5ec968a6211dfdb515f25896721e551ecb28f332ae729a8b6f25b64b21388747333b732254594ae0e69d9e3a225e431c8ebff161422c75bbd36f2e1ee4714eec681e22ca94efcf6f16c362d7c69774d5f7a34048811a764cb10576dbab365c2de64274977cce0d7c29109b1a7522da869c16d0192107ec7a20ada7897fe5f830f93e96bf143bf5a8be24cbae78c538db4cb512c7a076bc0658f44849161a2c66cf3772c5ba74c2b1c9fde078ef72fca12999674ac08cbccfd64e47066fd4d31fd50168b931089b98b57d781ff45b8238fdd0c7fa6df91b5048443b71ec264684c096aaa2ca3d5d04b7b3186796c92c9f179a71c3e92cd3b01e7d7b0517a3de00f5651151dd6e5cd96b49c8b7c6e58ef0d0ba17de681bd2a3622f96e83d3136ad5c2e4941d9db496825fbd6f84225e14f2e6d751c3304cc3bc2a09ec2771be5ec04b08159450f1fcbd3158aacc3c5182b51ce00a660525ea39c5d37d45dd9d84a1fe6108c76dba75707348a4681d3761b722dd07" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ae9eaabf841a731830a809d3bc30304fa556fe8941dd1c1b0a4ea8ae927cf921", - "proof": "54bdc0f7934c336b14224589b2b91b6f30fb05e4f2c201deafa871603365273c80d08d0b52ffd09d341e6d7389b6c1b73fbe2506a32d3ac31ec768bec703993e124be048a3b986fcb4aef32b9546f937c1c2d8e57a83ddd2a9131e2d9a2e4c148c2f6d3ba175d30e9195146a9deb9e25c078ab55619b0d8558a5ac393042ef6ef704f9d0d2de5541f8b853c7c919603426c40fe0352292f7531065d5c42bde043fc6c287fc5505e618a821bc15b4d99ada105ad8db382d8787d0dafd513eba06c9a64ec36af7dcb89d3523fd1e7819a63df5302a8eef337ccf62231aa274080cd46308ea5fd50b3ade5746159f4105c30f26a425b410b7cc0d6da43e6b2acf4b0a46689f7aa172b677c6363f1a9cb260c0a33b62f869b2ff77f20bef48d8d359c08814429c61b98bba6a895e6040f12740e4aa3e31a01a102e8f699f8723ec4fcaf2861be0d7fcad0e7e502bf22204620ad79545f93fde1f1e61974bd76e4217ca4566d3e2fb1d025212ec32a744f5639dde7284b5f2a28d725f3fbe42f7852cd6555fc567a25e7aa1fc18dff312af84f5c329df2e19804dc1cc7b08e1a2bc64ce7e61af92b0c1cbb779c3071f12c211bb77774363f6931e26316a274fde5776287aee08b4b7d54deb292068a6242be03d335a996a589c06279a5c7aa4147d1db8f492690c8c1d94fa53b293ee9a8b8582fbc7a0ce1f9192511346db624e9d06e46f7cdedbc0e050314b61ebceac75227f21b7d8528c2cc455164530820a24258a73c5015451ba3ee34449901f16553915e060a1c123abe700469eda2455e30e6c9f172e8f79ad8424ed2b0e5b8206626e22a2e3a96961f6baf2e9f6a62249371cab0f851a5ccc3895653b756691d18913ebce5ae7044f4d1c55c24265d9fc0dd375951d442a463c67323cbefc49773e133ea744c76b71791d9e89e85b61ea03" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ba355f405c246db6261e20f0cab0512a5f7fafa85072dc47798363e7e7c92d3d", - "proof": "6833f5e14ff9d6d1edf34f99c8c5a7545d88943fca1097e11849d4b89d0a3b6e64e79a8e7edf7ae103831cf59ad597a6c758b78b6d1feae4a6598d8546b0201054f8b57eac9d854b17be5092bf523eaab0a325f007308ce7a4ee029376a92e3994bc5f7eba448f794116f3816b6e636322ab07469fe85263a35f0d896e386f4b656c64de0fb1466a90a999acb6170f55f3c7b78c0af791597c3ebc7114fdae0ec571388e21b3df42094b22990528abad19c6f531f7fb03797ae555d4d8f16c0b92697a32daa58a0c25e124fcc5aa551660c17bbcd9d5d15e292a9c413eaa77032ce81d278471ea41b1e0631510744f6e5d1d948da035ace29ac63d63476a48666a625cbfea6c28e4a50db8dc0d2bd0eaadf848c7edd5e773d6acf31f751d8167d66163ecba1f63d248b65d52e3df8343abf3758750bd5b5d287f0b5f89aa803c4468c8a0811873e107c46778d6acd840a7306c22c4f4403c7304d6b4e11022768e69a358e3b8e0a95264b7acf43e498752f5469299bb112151fc8261c77f377d08f199038f4f5787b80a8a2d345983298f6989245cf7aeaae32be0cbc225f25934c1e58d9b62bc4378dee693e8ea2e19ea43bec695b9be4651c4db01cd180a0406c0dbf475721e8fd04aa7fe8bf1dc7340443e981c92ae7f1b09072db3f6fa40f4232d7964f55b67ab3544bfc5afb291aaf6799597ec8ca6615465d085085d27cc626c510bf46de3d3b13dac1989bcc9327d7717aa11093e32d5a2e178ca3151cc53638ac944464c6c3cdecff62d8b85192e1f79587bf9ef7a5034a875716c21aa908697cbbc181d9aacc6fcc72c2228c91e931d4c2e6f11b25417b52eb56b30ddd81490fb01dc73b7fe1696fc6af514e4ed0dec68cb5b3e9ca95a9e78ef5500955c9bb9a991dd27643999c55f7ffaa50c96070585d0d034fc9706ea9fe80f0b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "c0bfea0b38f5417c3d02b96e7d238089e7757a07e5915e5d250e256e4cec4d64", - "proof": "32fb9391641bd2c10d0e160536c95eab9c7db0ba1deb75d8e6e8308a9a87127fe0ee3b0c1d775d3302941ca1b1980854b3717a172fa7b99ab3eadd79f1a5d70932b3dab3e0853fd8fea6f260e888e0b9213c8b2e23daaadfc5cfe3ee525c5e073c395b8eb31890f49c9022d91ccbcd90c0d0ba04e26a3744a62c26eb99fe486d59b1e745578e6250bb8a08689788af7873219323e7647e0fdb36fc146362ed0c05059b546d8c21b5f75eac9bb3233546f618563ec0810fe70f42417f2281c00f402ad70ef92ccfa437320e25b315343b56ebf2b16950c2bd9ffbdfa7f518810cf29ebdf1cd3ad11ba6f1369f35e64947c3baa85f32de462c95ca334c74cf7512707ae3fa2bca465f4b4d98f12a9a2b1db9e2e79a76c53e163afdd06278909a43583dc4d032d97cbe75174dd537acc7ad99e7bc2db63ff10bdf0c777296213351ae425ea35805de133ebacf260c2025255fd46a5344bdc725d32c1ed07ad58c40dc521d81e06d7033edd4c15ebd19f4b88fd5c691c0f99fef1bffc96da109db663a2295b639ab4adc6ba13c85e519cb581bc99eede275599b277092a2a069757ae2a1b7f161c5ffbeeda91fcf43abe4831232670de7ebd777a58904066bf2ed44548e3072666dbcb7329a812bf3584b3cc0bf4d3b7b2bd0117b233f58c447fa75dc6e8bdda4d49ecc5d73aad7d034cdb276cb08a3938524d612a96589e91e785546608c9e7a472cdd783c2a445a373d3a99b5d5331e2e3f2bccd784ddd521e423b8696274178ad0f59c38d0941268809cce3e311741daead5dfffbbf5ca1f8f0fca0da809b9853232d95fa8fb3460b0ec9e7db90848ced3f8da877cecfecf141db10a63acedc272073b3bbf1b861d9d3ada410ccb65b41c2eeca3ade316f7e20da697a8a8be671b9ca7d3214acd36284f3bf1c3c2c9695d94d19786eb6c479204" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ce2e977e1c2406f0e43d2c72a2b8f654e6720322ef3922bb3311a7e3b3ad0575", - "proof": "b6349d78d4433f3e49a67c2f7c7a5d3f1cfe786f902232305b4d70ff23e3f64422a520b0fbf7d41640af3bfe62ca33764606e4c13bd171bbe79279e328b3137362b6aa326cad538f8019de3dfc35ef40f89eb4424ca623b043ef54ed75715f235c89eee548f51e47a4da3add44c1c9be24092ceeb0b5177089a9e548df38686d0852758dca8b5d2295dd7a558d0e87168b3b4d8c6dd2903e7f4d2926f3e899009f3c76208d43435edfc9bbc6f901fe8cefc7d640f40615cc45652d24b440f806d6f7a9606fd854f4e13aa28332e53a105db72b11292019090ca1150c3ec4fd0cc677f6e93c994313ca96e6d7a1a97a4262ccd811c1a92dd30cfb29bf8cff0a36b68b4888242c4b38dd73e5779f89e1f8ed043741d016d1c4471b90f83f8f3a54eae83ebc830dd7a9c05dc183db07bbff4e5cc74b05476030bc25452a69390429aa2b899097b39fa3b4d0868d5740206f792182c3151fd9e8b6ce497cba296b0e906855cd7698c5c1b818c30da17ffc5877f2528e024ce28fdf559e63412bfd31de1f3ed54c0f7b411ecae929aa7def4bbbff6459ece70ec59dad3f4ebc5a2f7be2ffa16620095cb7223ba1a24ab50dacf608f289d5c82ca4cc738bbbcdc90e5d12189c0bf591b9c90165e33b268693a4237edb9c52dc61988b5b3c883fbf08321651b968c4521152bb61cdec0662fbfcc6d657c68b0708f65a28008bf5692e00fe981cdd2f053cf9919273c1e4eaba33996343f1d50233087b927860aa3cca630c78080d22da1cf7eafd83ec258060bc394b85d69907b6694f737ee5feadda45ee8dbddb1543dba812826389c9c5325a0e00f239377a72b8799e35dacabed54b08ed3c082a814983ca23d4b901430fb3f96c1bd075b5c7e941e120b9a1c9360ce16fdb82dfc2f7da485f73e894aee1cdc6cb26731a8f2d9569d5cf2149f20206" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "e2c297eeb325e043a85a26e07ef6da3c8c8b2d4ef718ad03c61ba258c9c2c649", - "proof": "f8f311b208cf8b6f233dfc7d69aeaa5bdb1084cd3ce266b6deca4515dfe27f327ab589e3f6ab829a9be1d3c88d3d3c7390690fc594740427c6a05729fc927916584dd4c78322562937a6d4a0310203e45fc395b101a33b4e1a7738e560a27a6a3812f30a6628c7806496b465f8e668f0cf37b7fc5987f5f3b91a2192338a142d4760ed4ef77c1a7b99f4efe5a1d37c9647afc37ccb940bad4798b13bfab8560ec5ccd2f56862d975bfac26f58eb665165ab32f4f7e3def33a4fa45a575d2b00f213a2acba43928241b219c99c51decc8926095ecd63cb849427214f33ed08508a0281ea0581a95122d506a472525d2b9403a40908329c4888150f833f040242e5a6f3032330ed55f5a010e3c74d86ed71f580ce9cfbd6d3d3f96d79615966c13f211eaf2352aa149892fd32257aa371d69befbc942cd9cbe1a8b89a0e0b1532b22fef80c1837589982b27d3bc27cecfa48c6652bd53f92dea4d4d0dfc3a979047ae49f9ee83e3536a4f71f05a9cc1ce452cf93e84f7eb9f64e67674a9bb3232abc39fa97d8183f5d882249a6dc1bc0a490bdba525461b9c814f0471e68dbcc03dc8209da2d5a3641d35775720793ea1805a473306183359eb73a2b8dee67c06958048b991bbb4dab98786499e2748d551a7758dea7ad02da541aa7464778c558e297d1086b3de7efc60b90e08db6e2c90578a9aaf2704ffa20f557635dd4af2c42e5c76385ddb2efd293f388b73fa4f9c3ac55e8a250a6d6875f118feed0b8176c00ead7e7d195ff98f83a0b606ef545082a582cd12e70f2b092b6c8b0603350ea7e91f4e6611105ee96f5a57ae287390266704fdb0bdbb83a2b9262bd63145172fd10b99362691c53eedd3cb0c3bfd4e8351f498a1715dfa22a3922c5d4260a53b95d3c54361359b151c68c8b7ee56732ac6c4a33d3c7819e8192b6d0049d08" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 31 - }, - "commitment": "aa2f6505920b1daa39b2c084d0029580b926fb3f8061f592f257107074cab814", - "proof": "6a632a308c2819d65dc99dff30fa006278b6ca6525190d2c3280647a7068574142e71b45fdee7c9e20a829a5ef4cf8e319273afebb41bebf622bcf83c7c545238e9a2bef77945eca6faf93eb4a2d655a56f1c5cdf248209c78e19aa2cff3c46c9af7bf58155e9ef1b1f38fdb8743be723490f1c6c3c9c454e577f422354e92669b497ca92180533ec5088741e8f6d0ba915174a57d28c828cd3b7e99adcd350129d598f3ffaaf59a2a9a9761d0cd722e6b85ead6350a384e7b667999ba8dfa08f829be9b2ff98c0b7067690157ed10ecc546edbd4bb816ff0cb9f67ea1a33807b0e0643b702a70f0e9530fc5c088b7d4cdcdc58174293eea15aed3b30a8a082c6e1b37464e5f25ffeea4ee65ac829e0e3a0c436bc059b2f14675089880233f6f6033d4155836d73eeb14ca0dd70b075b565596c1d3ed1c686d709aafca2575462a97940fd946a87635612131b26e006da4d3c28ff78657dc65094e73aeaf7d3cec18ba9ac2f4928344f4daaca5fdc07e4fafcedd83555a95fc5aa4e2dfe52d459a1c36bdcacbc9a43468292e5c7127a28349a5946d266e44cfe45884baba5d0da4f7b7e4a024326e42b3412b53beaf6ec6a087d5bb79a9456183645ec6d1350e54bfefe59b01967a653bed0c0a626a18f3da704c55e22bf71519bae1a923401cce8afa4a60971ed9561bb087e7e63f356f295743ae80721e56169856719abd1e18428fe144e9423ca25e3069168f0b65544dcbfe2775c48a96b88afe44ccf6190e7268b677da867c6460dc0a67408de4af9b5c790a083aee396638ea904e477e32c15d4cb6798ba3c77c0ffac87b75d51737dadf4326df198660545693eca60e087b2fd7827feafb7fa83fa4670a7c8d0827f56412efa2ecc7b0462a2ae9150a849e4f826bf40ecbde2535e6b3280e91c8a6dbea0fafdbf5fbcd0d6c22ffec06" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "40c6d6d42e4105a3f1303994532d4c70c31ae9513d0f585f5c8be29e9301ac6d", - "excess_sig": { - "public_nonce": "18481536be894dafb924c0d84dd99c956a6f0879b7811a789566bf5358b73f7f", - "signature": "c06aa732110ea0f61e3e6090024233f957741e9adf094e3b31e7284cc077d701" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "567eabfdce0901a22bf0a5c12083776eadfa82a82e7c8848bd3d9abaf8c4e203", - "excess_sig": { - "public_nonce": "9ad1104c021519ab7d7bac75fb69f6ed8894b368bbc85499769914b3c4cea206", - "signature": "958df517dabb414c648e65b2271361c76ff601e1d46cfa1f9822611f1177d30f" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "a03790172f644280c56f4ec29e9ff035248124c475588ed57eda90411759786a", - "excess_sig": { - "public_nonce": "5cc33fb436d94860297a679bc093a4136aa4ef7587f0d877ca0474a61499f857", - "signature": "8f30dcd51c7d917750dbcb86219bda89e9c084b09c8d61a76ec7d0b0601d8907" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "a2ab7e54774ca87c3d71cf0bba6b6c0300c0d1a3fbb888230529b94d3545f675", - "excess_sig": { - "public_nonce": "3a021d9bd4655df1cb81f94a37bf754c49a8a95b6dfb143d4dd9aac2e2456e09", - "signature": "433628c35edef44c048052debd49a65978b1abe1b15ef63915d1fde2a596540a" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "ca0c6329d2628bda5a887c0b0f064995ff0f1b12c7a9df49e9109b750f2dd57d", - "excess_sig": { - "public_nonce": "f030802522eb65c944bdb51d5ead88d8af26f2504ef1136724f282fce15a0c31", - "signature": "aa633c88930a28d550cf1621ffc1ea8afc1f3f60da66b205ee29d40dd721700b" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "7e6113be34552e853eb240d308320a481a3f40f52fb8063b014851fdb70f4878", - "excess_sig": { - "public_nonce": "60853c048a37859fe50853a5008ee4344a6a31d36d3d8a84cd5f963846c24235", - "signature": "cf806a45263416874a138007a9139735638f61d86d745bc3156e8f5db3717302" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 31, - "prev_hash": "15f9f34846bcdece04de6f4f18057eb0484cf424ddc801ab2bc97332f2f23aa6", - "timestamp": "2000-01-01T01:32:01Z", - "output_mr": "580398d806d5c57eb67c21656b8d94de45c61175ce6270fcfdb7b8388ecae2c8", - "range_proof_mr": "51459349b15b2281a55e9a20f0876ee187f2dfd64c086122a738a9da6a89d4c9", - "kernel_mr": "138cf88492ab81237dd1027fa36a912cf0acda2419af21c6796d010588ce126b", - "total_kernel_offset": "1d438416d36bca5a85b474a9a04478bb783c5f881bfe4cefa898e94883f43108", - "pow": { - "work": 31 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "102aa79268c694aed029565a776f594586bda7483a5437a07214815912654136" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "10f14b3034bee8b0ba2c1d38d317823dc4cad470773f5478886d681c6ec05340" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "367a70f6186eb5b126b70bd6702a16e182fe4a07fa92e5a6edd4ae4b33ebf023" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ae9eaabf841a731830a809d3bc30304fa556fe8941dd1c1b0a4ea8ae927cf921" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 16 - }, - "commitment": "a016f012574e9afca414948e17435a0821a1bb87a5bd5d48a809109efe388d6b" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "040560f505f3d8d6c94ebe688b58bab7735aeda0db88fa101d11eca6d17c535e", - "proof": "a02848950fb8d40b0c711a35f86cd58417862439fda3efa1ef34185ebc98c57ab2bb7112bf0bbc55deec7a38b345da864c885c2b2943b01fc4c0959434ff0357dc1db9d4e954ae1b21dc6962e44abdd452b3140bb9f7d16a4f30e5d6c804237252e68f86f8b72d58ee3bcbbdfd35781730fc8e11aaf8905024432fd22589a678d0cafa17ee8d3c74e6f2931d400ca4219de02c28f6b34bf3ce3db3f1e4840d05ca5993e528f5a20d2af9503f68a74b63bbb42adf3f317d349be56cc22a3b840d78f117908c00c9faec4d45c8b82da4421df2614c3921b24fc06db13d267c6c0da4c42d67bfa4e39c2949fc382dbc8d9ec9cdb68670fa135dc55d0a7d53aa9d6f225040c4c62673af307f72e254980aab231c5502bc4751d7b551fa332fa81b040c6e933e91ab73c32f440f3b561511664d5a4e9c9454caa1084f2eb024031973867e684230124d7f4d6ad9be57c06f8c1c828e5ee0b5251c213d5a4306be864f880957390f62b1550db7124c23e999c786c3323b92577be8686ee24a95c27255fcd657aef46d5ed05dd24e4f7139f4734e6e4e5913b1724d58e13721cf85dd1f585013161e72a2f9707e9ba8c4337e56b41c210f229664e82585b0848bb3386d22f32c5dd7064e03cd1fd59787e28aa19716f72780a31632fa4ce976a890373ff64625c24324250673c65c190ca54eb995c1442af6e5e4c28290ca146304ca1d1875f2c5612ab8ea9f9910fb9a208252553c3103b3cf5b837bf7ad63a87e886cf0dbb78c5cfbfe98a4b022e590a323e22456d9a7c649247174f54a2d9cba3d468ae49728622368ab166e8a69c27f3496de201fb25571a41307ede6896f4ce328011561885d7e8368796faa575ae7afd8e99c166b2d0c8a5388db195608a8bd0adb1cb51688df0c2c0d5324482029bd8ca4251a86cb6431b3c1c3d02e7ac9190e" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "086ca2947a85fd6f94fa289a0a5f3782aac55e7633479c3b5c8c6114339bc213", - "proof": "eef85932660ea0c568fde0f73fe468ef40443b735acc3eeaaeef3da6ec0a2f20b222f13e887be571e82290d567ea64f140b35d65f164dfea7785937f88f0e235b858ea3f66af35e3c5f16e0bd4ede6e938d9560470d57d8268ac54f8d8bbbd1136d067fd0c22e486a12d72b24f300b3da5f157113c85f0302605c2e83825d17811a5ff6fcc163e6a8af79397d3e330b2ccc9546c2498c9743f174c8b1245c90c3292b31b288ba70102f897b6154492824da9bab444f79a68e2b7579289d042008e6d04059881abc317bdbb78d5a4122c3a23be4ada2036004247f6ba42aca908f2b740bd7321bf61b919f7bec48294002be16f306d8baa9e92804213b2750a248a3345cf244f814caf05c574d1c4391a348379cfe6620207c1f3a6f59757b77a38141e2c1146608d4ac35ca62e090af8671b6fc31f85caa861a414d46ee8467f6ee2bebe92df47ca87a53942e2254f07be62d7715fda1bfaf026cee7014a7e79de9d3823a8ab56873b2f5691f70bf159cf854343069bc39fe9562589feefa318248b6832e7ab030f99e7c4604208ddcd73cfb4ef5c9d137820d444b609d77e21c0320a38baa6974029999cc3182a8d17a93d2b72dc894e6671a8663828693b66e2afbf4c98488cccc650c93c7713a3916ca5971855b86ea12a8168540fc5f4187cc394083f5de2db3e3c799718e20991271c3c3de29c0df80c2387f6e6ca0f6f94051e20b8d1405b2c9a1a459ea56c974dce62cf9d38322ecc388c941b5e9872e47def3cefb964a217d7a2f2d27c8dcd6781543e8ccd287021080fe253bcd8102097993d4b9b3258c2f97cf9464b3b11adf86f8801c47b38da6bb19b553c660f396312558aa5292e57b9c574b48b8fc85b996ccb656e0b34b58def8c0151ea0164d8551018fba5a20a64a947fdc50bd2482dd88635e909ac0cb6d41652084a01" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "12a226b93d01f3e780c0c6346ea906b8adba94b37e6875f60b8c32b46561e64d", - "proof": "f6b77857eff13d761d51669d5b6e93f8682cddaa493699a967faac89cb982316205155447f58d9ec244c0b373122bb668d9c182ed5ae17af43867b0557415f143c93e63ec00b950e65b2ca09b0307b92459fafedbf012c376a5e60a5b352e06828bc4594c437278f535542521548d2da3ba413bfd4d0361e76710870b47e0960d77518c3c95bc1dfe2c08df9b3ca699e6e6b57e4e0cfa9ac9ca036f712cdd703221068b2f44e3fe3fca8379fd1e02d286817dfe8a62f12a5f3b4776204f0220037b0e3669d205fd02afc9c70b587eec187bb408995ac5952832825e8f5468f029252717075c5edcdc923a864fca47509912bef97f97f50ef88bfd4ef200aef3196d349a697adda338460698846e252fcef8a0051e95639cf5d52a0b7f5c0835712d6d99668ff7a29c106e7fb82b5d1a87ad28fa64598942585b6d10403a1287c3ecdb004891c2a90b1bc28a3f5811d0fdb686c11997c9d5fb3a0afcd2f6bbd12ded3342e264be28814d85d06a742862726af2fb7a98ff8d97bccfa645b7e721836548bbec9fa6fe12f9597db183b1605c5c84c35895fb053c572f12a0eb75c66e6938b88d5a4bfc9bf5b4aee0ab8b831312d219150b70e0a7b6383d2c81dc473a22995999da800060243ac0cc4dda6e590a6b8533bba67caadc696f0f9d51d0a68408ac36631307ae11bc47031cb2523aae90499e11102a0d1565e773c71dc31a8dcf52e1d158b7067de91ae15258006225d6e9880ceb097981da3adc497893fc27b2b81f1dcceefa908ea52f8f1a9644a39d1fdc94fed7d660366f34c1ab1432cb3193f50f7a0b89f6b609c91500dc634730a921400a38ea3d62f2ba06a953f82a7f6e712da137bdcd49e6b1022bf7e80d10cecae4648732ad7462fef87230a999183e728e12a6c45b936474816c88e04d106b23b4ec54bbb5a10cbc878960e" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "882010b75b9f2023560ad3638fd3e1180ffe13476e2e37f0cb9bdb7a6a4bc408", - "proof": "8e73d452fc012faf83b647a9b99b1d236b3edac20000561dc095f3b0f28e045ba0345a19cfad1929d3628e365dc9a51efda0c2514067ca3ab929dc037e00bc49823446f9f9a70e71d062172682054730009da699e841fa3ba2db09083eeb0b6224ea0529af942581d480caa88c92da3bfdf82e09b9ff8ddc6c78fc239cc34a10e15ff81c0a3bb79cce428dadf750d93dc9c0362e9d7261241497fa0145b2a90dac9ce4ab092dfacf24743c093e8a88fdaef29c391d710a7e8fc11776f6d8d50a50910ed6b85d7f7535b107651057b435aeff003a888150500c8b1509d0222b0bf8bd2b1f943a7e69d4ad2dd2c545501362078e1d28462d98e2a8d41323c9d65b26f2a0dd5ded5dc090aa73586760f7193d29c571d6cfc432ab676d9ef2d8e3283255df77b6accc73d1136de5e00bd4c6006e092c28fec89536f487dad31526141a3be54ed861f417c013a38830cc49dfaaea621e739c6eb778c70a18cd12162e74bc0884bce111c514805e99171490929c9e05893c1a4f90bcb57f3de47127678c4e05e9cc0dbe4937b2d422318aa04cad0162ccf34d2b56fb73e2d34284511fa8090ae04145ae15132ed893bcf1fa556a103d14516ef1c8c7ba17bca9eeca2da4236943ba3acae57697c9086ef797f90f590c6cfd769741e4fd6f35c73e465f8c190952a321ae8feb851d6dc9824b2d0e8dfc033ac822adddecdaafa3504c0e2a1c281c4294aa9acc882875203ed6c933f0a6008fe79ea7a1099acede4f974bb8f9fb437c974986761c1d2ad969470015708ead02d9949d4cfb593339d8487bd468af84d9b0ad0893180dabc1acf4ddf6f63e68e8e1dd84ad82645d2482ae0f164d4e1e1d0251bbbf49a6ca66133c793e71d241f9eaf52383d88e2ef057eb0e16a4b205992f540b9636bb31a72dcd9d9e2f33e8aa8fa07b8905b1006584ff02" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ba20b0baf53f5bef2fb9c78c59a797efdd67535771a9f4fde723e5b1c4924513", - "proof": "d87cc68622ef5558e720b9727f5d16e7c016d41acd1a2130ba3946df9efdf85dea4867f0fa08582996a63c0e3b45c21d8798140137d4f2c616fee44194ca3a5dacc2698300dfc2b72897e0668f6a8b9ab6d5cbc0ab751383d19e584cba7abf07b09ad38260079b1b630778251699e425a70f0fb2ddd37695066c1430ae2ce11e46795e47c938e7592843f81f9bd81848b85b891e2f2cf8912aee9602c95dc9004963bb8e98e457168ca82830fea1de407b83ec7c91bec75388fe653f6532dd0c3afee384477785c14f6a5949e67d66b778366b6df75d640684d5154b8a6d4b0540704d6f4ddc2e57a74ed21ceebfae27ce9d8e852e412a7a738b42cc16d1053ec0f2dc54b4eeec96200d41aff0a245dbb07a66b807047e67fbfbb0c933461729749cb51b57428bbe3f97c3d9ec47f36aa1b2688fd8cad507d759212abde18771e4d35583dcb6294b02b1248ba14844193fb3ec1319d8ad1827ea62d8b355dd5374f9c84dc7fe70161083d8546227414a08f5bf5982d959375b35c583215df239e085005a9d8b3e6059cff59a73321ba10e68973508274de40e95c1abdb7e4e6704acf6195460be5c12601d98ed64ef4c1ec18d49aecaf7915f194e0cbbd5eb5f126c2a732dcde1d61393516d122790cc761273e04c61e1d8a9fcb4c29f98ca0ba6e6e753c253f7c887c0e877c1ea4ac5949d0706d3eace64e8e351a8aa5d311db0089898a4b0b7dde7f8af4a6b14f0d370df87c54b82e788e64a2fe39adb3f76ac289dfa9b834ddd9274782348fcc4dc34d5a1264f434eaa10e0b2e247b32e17e497511f593469150c5d34f64300e4218ba11c0d8aeaf9d163a8b8474bb1a006dedd2034facfc1978aac8628332567691327b5ce37b3e42e06590e3dcd586200c4d11950991ebe1aad04b977b1504d90f4cb1433cb85ea7150bb7b0ead142905" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "eadcec559f12a212cf7dc70e5678e88112413327fbcbbd93c48fbd9f90443e13", - "proof": "7c53803a2bebdcbdd5a1456c8b0fc4defbeac5dd2885733e7def5fdf87fa0e7532d77b83bd3d44641c156bca4e2542a7372ecae1134decf559f5ed1889fa932ec0bdf665fa3db5db6907d5082e5f2e7f8c7405b935d78d07cfcedc4ea675007f1a0e0d504646932bcbbdd253e224d8c8ada7bf9c2dfb399d025a026128774060b766dc1645ab528cd8440256435efaecaa1f68e1e5e274d621660146339f94003ce97f29fbfcd253c282b456e8a0aa3a3ddff47e9af0e77ee89e7b7a08eaed064631931adeff35de2e830404ba72200afede0d5084636cd7e2e2d35d84c65707bc058666928aaa8d4bc85406d09ffbf19bf815c764ede3a39a21599543496f7ea8ab4a9656c4c81304ceaa6ea8745b9106bdba7dc3bae6219d37c533bb32593be67cff602d9e5d6fb5b7f1c127abe6000d5fa39be9360506ced4a5f012142a2e5e7a46798fb50beb06a68c5b3dc653087a3d81746a57260abc6b89eab0a2290e0cd0f7a7dd9059a4e167fdb64ddfa0753ae94ce4cec5a5b0e5e3fa7ff06f3a5ba4f18bf9b0804792464dc332ecce29cf998306e36d00b87b3f02dbbf4436596a2203c2f27d199ca1b95a76ef3b743a58741d45c406ee0c39a06b28e226044e2a88c58c3fc836a062e412791d59b334b35ab55adcc64683dd7549001957d4a63184e85667b5d0da9e7732a55a184ff4a8febbccc9663f3bc8a9ae73447394250008d75ad81f0bca6f2014b6b95dd1bc9600fef9d510f2a6179b32ace49a503f0150a83201072395bade51a66102d90686ca41f119cb83497a121723ad6167c54610ee9eeec2671da99635f5180ed3e66ad6b93be4c67545ccffaa56328c881a2e5f676078ad3722c494af39d0e7db688bb684daf1ce7c77032d5561752e65bf08534e3a1f59a7d1dae607111047ee93c0808a767459555a7aa3c38a724566bd0a" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "f07b67d62fb7e21a94f9ef7ea7f0aa09c076bf918c0d9419f7519af61a963959", - "proof": "4a42059db68f09a85b22d777abce5829e9231576713a2f399c4b9bdfea28025552fc10c3c105f8fe8d0b0166d4d1152bf85e1390a3825606df8c9c5d9562a713f8c723670072c4235c0a3aaadf67594f561270155817620ef07f731c6e353b781c959d43aa054f97dce7d0bf38f467cea4dbae411fa944c0f309d8c395d5b5431572e66a7cc0500ba8bebb434247eaf27bdfa404a70a2d5b09621844918e7e02d5546dcf381ff7a08b370ad84d849f45f73c300fd3407c1230cc14164024c806e6be557ff4a4cc71b2e9775176667b87abb9a254719ef8dc2146e948208582027ae872f3de5a195e2c6c568787dc78d24f73d4b0abf5a0e50759612a1cf2953e76bf0ae81eb590f82dac5a612d2eed1c26231798ac798de71ecb89eb4289be1e1efed87f15ee5f9b9bcb15eb5b2e24e6309e1571685658468869d014ef3242589289e5177abbdb0e8c7816344117cba22b122527315f284a063214430295b2179078625ca6bdad60105998bd97ded3c9dce39c616c76d48b35179a09f7048a07bc3298281ae6fc1306a93c7bdc1b5e670bd29576b38b7874e8c6a4d1da2f444cc21fbaba3a6a01de66d4e7f04b4e0c0c0aa000b80e7c3601d48809a4f2ff9c74faff1e7c4e86c54060ee520bb1dcf3340bd9fcaf66e728c9bde4fc61f80030627a19f3ad8754fac6a56322ddbbf958f7319824c67376235ee3b856d056b2d11e1035c5f0538eb9fdb4b75359f4a39d19982fba6afc2bd9c4ff17fcee48ca410aa2a9626dceeaf1e1706b7c81df4ff0e6b687f6b51472d6b7c604c6e1a819ba04e26aea23e863a0042eab61e1808948fecdb91084d273b8364f781bd1da86eb7447e53f6659f7c20f11f6ea8a20decb7f1b686cb5cc75c1e4700d819725780a0f83843623932c6e8280bacc52123d9c45e4220cc2c1da8b41fcb7c16c2b59390a" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "f225f8352ab7cce8c3224ae987560ff0806b493249dfad04998954fefd38ee59", - "proof": "0edae264a6a52bc7efb6c3995c283f02f7f2750ed63dff22f70a0461ec53131fe4eb49605a9457058d78239b6324b18175f47d9ebf66e639710a5992512f1955d23e272d85a0ca80616312b5e25a275dfd06ac524c9f9262d3f6d95d316e5474781bd8e468fb137fba3786e5de9f2d7e938ce896c9689b1b044a0e60e39d16154d3efcbe64ee74aa81826a9c1260ab5f0a0831fdbcd6dba00eedd24eb632f100282889e106067aca7b2c140ea2a7fbf00488593df8afcd0bc16b313b9b26a20931dbe3377e9e8076126725af3971d2c30de630d3337159eba897650ee83bc50c760dd92a01c8f145783a32a8e36a37687180ceb736c2048ef913dd844232ae36d4b3252b8fa8a6b4878278e7a83bd7ffa321cd086300ba4c50aa1b132849393682a928bfafc3547909e0fe6e7ffabf3b3d5b145e33db74f6ed05dacd015f7803fa20b33f9de2d828152f4e3b04c31d4ea5ef7e906b1695f4c85acb98c00db95dd0a8551ff927e6089ef471c5d73577f4dd5fe083e176823fa6a830499b41925b968637d79f20cb67fb04cf5cafa1e6a1f9f2108a2d908f1bed86887a8493ce46546c9ca01aca9b749695a0ee02dbed90229910016a3f59d7a056cc8268ace95f669e3dfbc7a97e1cbaf97beaa938d6678719147c5247092839526bdc315a1c5c480a67cb69f17b5910e46611915fc5ef395b60deceb42391f905af1b5d25eb6362f7bb3a9323b9106950ea29e3256545cf414088257ac6793c211b96280f9564c284d2512c4981e5172e173a9685299ea21830e186e7313f4f628e47b09c8122fcb8da072b64193d30112791b7e957dc8241affd7e82f3c8321ac1e8f0c3b9691e1285fc991ffec9b963be627b1d19c17bdf9bc1533e26435c8e47650d7e350090f8efbe32aa2b9d15ba97d3527b7198397373c289681016a91fa20c3c81be0d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "f6e77a241edd8a9baf8d14da1df8a13cdefa8369b9fe52d50b6ac120535dee50", - "proof": "24dbd29b7f5732a2287f8d8227555348f4736422a73219ae848c9cd6c55c7c273424bb5bea4488ea413abc7fb4648b3c79c7ce3d53062c223593f9ec2d75652b6c746a61ff3879827d79f17c103165223db4c2f3332d65392e05100fdf9d58594e90e665849a023aa6c535f493d0af4684f91938aa3328ddc06cd37ca948c93816814919d96844ab8637c833be0000be587e1a78e5e6d72ba222e60307d7170bba04e555da7171f30c69fd50fcd07ef4bb1020a9faaeaa00328b0a5ffcf76101c96952f3d98df412efc515d02507c95ee1410c0c3ea8aba4af3287615fd9b007622b02739bc567f8d6f2cb4525e2436ecfc14735dc8e737877b33ab7012e544ede5338f0f14372720f4a3822f77547412a5966632c5a17e5e7378f78ba14db7304a2fc8b7bd663696ba935c21fbf599a5edc7b8b9dfa2d7996bef970ece4d56c5ee780421faa83f523e0758a1e9a1e6853c6b739943e56190fb5a7c65870f23b8839b96bf1bda2836d8664f112bef47c284c9aef2a0051ffe731250f53d9572acedf590b45046655e923ccea07700d26e556b68e42d3c5b1bf2eb0b9123a7f5a76281c248fc4dc78b0cf97b7c607966e0c28fcde612d89e2ddcc8da017feb0045a9035e2fae3011ccb4c4b69d4792d9d4f7732329e0b6281e349c814d6755b485a6e5647fd415e7eda1fda057f35a6a0e17eed3fb891f29bab8f38b72719100c4eb97d8a777648d66855be8804fc61d177a0c4cab46e0c3e7b51887ed097ad0c9e40cebe04ecabe72d1cf4bb7e2cc54a9721bf8044d31dd0ecf77ff72d073f0190e64ce3808141f03dacaf11fcdc2eeeb3f077a4eddb5154d9319912d104e40978c092b322e47cafdb334b33cd580079531d1c14cfb8cf7ae3735ed483320a0f36ffc33c44fc29f5d78793ac516c29f0903d94c62d134a4a17dfffc3fba2aa0c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "f87197d2ec15073651ce9b12e9f712c3349d496c8e226a4c277b6461a515860c", - "proof": "764025dc05c26615fbb1c6bd6a98c31dc6b0c201e8f3b0da79658316507061795a24cceded7b1aaa7cceddc6b814c0687140e7ff0833fbcd5e9311e26d5b52346a801ab188544332365354fcabfb57016c16588714b3166432091f2a053b00705a871219687690ea0c064a970b4261bbe07ed564afdbc7b061cf4748c8592666332ff04840936097da17a088595522ae6861c0efa6d3f9c6c6dd22ee75394d0c21291d789952e91c073488e4c0ac1ccc56fc48c5d0dc65165280399dc8ea8209f5c3de0797422b3e9305904833940fc81c96b679b34128efa3dae2a44f137f02385edc7f0377335dd784206eb51684cf0eb25ecfc5e83b7e1ff9b08d61f9571664972497d9cfd8a1a002223d2782a0712543b6a6009299c5a6d898c75eadd51fc6a6ae25cb65913e99cf2b7c25888bd74a2d8acfc11cf2c0f964ff2435eb506db8cda8bc07112b24ed8cad573f7511cb5f211227e138996f44330e1b5fee5658ba9844ad013b8ae73b52e2fb54b43c6668310d4242ddf37d74ca13d2b6414663de5871bbd0ebc6cf132316b44ba00b0c029403a87156b23918a098980d28e83e587bce889ffeaf8bec277d0747288770012e55b6222c7c09635022387297a66c586f5e03036eb3821082493c2eb54e6b7a98f4c6e09ef5eb96d1d89664610c3f10b2d15dcfb5617c17ec83d83994aacb477ef42ab4d659f7eb99be30ec01613d30d011ffce737e8cad44978f3e305eced0e5266f5448dc1ea2caa30738bc0a098c3d18a8d2e58c74b30420fcd35cd434c8d3b68062031217177e12c44411df5e92798c8bc8605f7754230e1f74119f1e19f1ae3a639eaf9893c3ce8746bb2a7055ca1e2e930e3b1b1c77653cb1f2d01630006d3fb7eadebd69f6767d9038860fe36ec4be889d41d2dae0c4cafe6a33352d7518dbc683f39bbcdace4bb4efdd0a" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 32 - }, - "commitment": "b2cb08198634a4744ce9cb0dc037ef875404ef9a24c233b659b444b2396ff823", - "proof": "4a8c83721283ae2d8d1edcc25818a0f192a0b3ccc84229810e39f1e455b19a5f1cede439f558ea86720e7c2d3ddb1b0521b0dbf75e44016adf540cd85fe4f825ee87ee590ffc923cc33ac515cb332d6bfa9d3c0e3ed97bcef188c3ffe648595dd6e25772dc34bf8ae6a3a9d0a0896ae486ec6b8c8b1e05c53d606a245b9b2978ce7539293d853eb464170bbfdca03794c66678f22df16b8c60b9bb52787a740364c49a2c20765831e189742461c4ed45010e126f5059e7f2485cd0dc23ea6f04c546e43cda58e6e0b126343127c058f224b04c9d00bcd56d55be9621bc8b3c0f94bdae9a69ff0dbb1eed1e8fe6c680c0eacc48321db7c6c29a60167d4c69280b42402604e9845fd5d70a57dd13f467e6b82ce5116ba0507f66ad85e0179cc96dc8bbd744649c1ac1828738f04c291dbfcbb9c7504e509e6d84213928ad46c2799212e6ddf93f7ba9e9b31e8bdb6c616d3d9ced20c99e29b6bf2e078ecc067943741e22cc6d054b18fd972127561b198ea3b1ad03af09bcf2dd8943738ccb8221248603885861487b0fc8fe80d78bb8c384d84c7fdd6adc043b30eed9dc1a5e73da08e9e3415b9698a0d93887a7780bc0f93d08f1c279b21c4e0bdef2fce4b71dda79cbc09a2e865c166b16a07e932edb2968546cbe25a87b7169c532471eb400fa44043f19524e38e8ff0602f9fe7db54bd24b63e077cf96c37b26f5395760295247e0731966f5ae405c790371d804c346b6ea2d875d72801bc3a15458b1c257121ed0ef44fdffa9a96369ee5cef2e0553e2551f188db67ccc07872b2e4f5368c804a0fe2e2ee727f1c1ead0925a3d5d85c8f4528035894257c314bbf6bee030c1e73f3dc06918558a0901215a349a0d4d31ddede244680a30f6fe59639abd0acb172b5093d5710e7e71f796922d1c96eeb3e6f2049ed39115ec2a07c0633a0d" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "44e35af8ba3b47d9c5b666a6f59cb27c88fb5779593ac8ef37e50703a8a0c26e", - "excess_sig": { - "public_nonce": "8a9686354273c89ee089e73430bbca43b5c30d8fdd8f495655066d8c018f2123", - "signature": "f1b5aac8115d764df0e735123f8aa4449c7d222fc73b8e8e51837f52b2e4a508" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "662802018690622daa9335005b47a5102cb3cbc2a146d0f920234c19f79f3c7d", - "excess_sig": { - "public_nonce": "f055131b559616027a35f28226fcbc031e0d6958c793590a82c16eacd172eb64", - "signature": "7dcd9525d9f61be39420dd649cb4e3124e2b9fd643671357c3adddae1f6c000a" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "84036cf57f8a72ddebb1443024bee2771be5ef859f1bc2e3e32eb6bf9fd24278", - "excess_sig": { - "public_nonce": "c297a8219af754f9f2cb9deda7b387bd35c7a0dfe991b472ae8be7cd9156141c", - "signature": "e731abca7c24785f6927456e97fca2433168fb15d150451584293c2797879b03" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "86a588bbfe5c46f16810c4d1c22746565686e14f3495685ec9273a7abd65bc34", - "excess_sig": { - "public_nonce": "c8885b8ce882c9f1783a282d48db3d7ae8aaf706255e7f0f4c2b2dc79eadf703", - "signature": "feff0442b54105f7da86975960e39e33896fc984057bcd70c9fe8d2a286ead02" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "b0e191ebc4da95cd81e7a898b4f20589a6e70073d0149eebcc63a6511ab9f116", - "excess_sig": { - "public_nonce": "f89f3c97ef0a5b30f7ff66e603870479822365aa1be2b42cd73f0abbb8a54e73", - "signature": "1fab967335a556413c0ac3c1798f5210eb209ed1b5493bd4027b03bd9ae0680e" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "64fce4334c8b7946607ea93139e8fc183bb47a404922d907994f6aafacd04872", - "excess_sig": { - "public_nonce": "8654003ea3b90c341534fe2c0b23e3e9152295d2fbcc365f6cf8961387a53137", - "signature": "9111177c5860f7acc8a0cf584b9706c977e12412566ee2bdaa367d4a448d0a0e" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 32, - "prev_hash": "b983754e36d047b190dbab13e118f076855045706b92f88b445b62bf6cc880ab", - "timestamp": "2000-01-01T01:33:01Z", - "output_mr": "311bdb33db30c7be82b072dd47ad202d7aa557d6d20082a20dc652559c435bb8", - "range_proof_mr": "7bba8130bb9ce332c0839aec78f90995658c896fdec3664a83f1aef61de95722", - "kernel_mr": "135582b31aac2e8ecca5aff301f4c4b26033ab0a6427dfb18f571eaf258a5b91", - "total_kernel_offset": "a99237bc773d5670f917619a2e1a3547a20c08857f192a8d1e078b819c9e930c", - "pow": { - "work": 32 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "882010b75b9f2023560ad3638fd3e1180ffe13476e2e37f0cb9bdb7a6a4bc408" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ba20b0baf53f5bef2fb9c78c59a797efdd67535771a9f4fde723e5b1c4924513" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "eadcec559f12a212cf7dc70e5678e88112413327fbcbbd93c48fbd9f90443e13" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "f225f8352ab7cce8c3224ae987560ff0806b493249dfad04998954fefd38ee59" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "f6e77a241edd8a9baf8d14da1df8a13cdefa8369b9fe52d50b6ac120535dee50" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0a70e70adc7c5f6df3cbae97c341c6f45b2fbd3dca45c1b2947faf73d21f5b11", - "proof": "b2dc15442d285decd0aca497ba2231158a7fa181861cd87930e35de0e8990627fe0ee4e8cd8fa35c315129f1a5a7b01e3fc6dcf9127a0a16cbed9f579d162f01a265e0ff03c1a160c3d1832a51804ff9a31f5377e99025cd9e91cea64059fd19f47b0e51fff533aa9dff0baf1af9b74fb2a988322cd7bf750e11a53d08ea520bbb06e4ea2bf7105e96bab35ffd017295cba7958f309da1940100d4f5429f750a7530b2b7abb79a09d5a262d2e5cc19170c35e52c2cf0f4974e1551f34ba90a0c12ca5eff86938c22699914a2312319f939d9a41f490d8e89c8d99411899a0e0e5a3a15ce343f3a0c2207558fc0eec6330fc5f16698b81bbe7675d08147650674d2cd14786d3fc5cce34147831ad620298b84c3a4503f3f07003efd466a9aea5e7c1dbf02eb8cb3239bbc01c7c58705a52046afb9e9523997f9839167b9ac600380b6e7f9b74896d0212607830a4fc2c8a0126d4ffbc5fec7d9ddc471926762794afd909d52eb6f7bcddcc7b5673a9655e6fd2cac497bdab61ea8ae248c021e03d0423b68cbfbe0833e7c81c61327905d20d9729b008c6e5ddd90b4a7faae67569e078d1b6626834cd13ec29c1d8dc1d6ff0ae0bb8b3188c50573442b4e63166506f07f1a369cc42df6e5a40c2eb58d50fffbb944ea8c370e7cda533395caaa4876cca7644bef490b013d47b74e7d934c35a08b85ec8438f7452f73e319b53e75fcd0317d7d52ef30c957249e12fccbb074f4143c3c7e7c4764d8193f2972d730b489af7ea6f4bcb93f21ef1f51f5c9a6c7fb8fd66051af0b0dede19e980ba33748b8fafd2831de5c70197b8a4756712e7c75d3aa3371e72aa8a95bb5329b1233c32b0ff2257f0e6254d056e23cdc1fc2942c440350567c7ae47bd0dd4d70fb021518d9e0e0f71c22519e339e0f3f2607cadfa0f017db16669309d76207717909" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0e0632ee32bf50c2f915eeff20e98b085ab259066f83068cb9f0285a0582aa33", - "proof": "1cd80c784bc4b54a978131752f03b2f512b79ad0400b4fb498309ca679dc677d8ac99251018daa31ebca8936605f138b9f314c568a557c7141fc54aed6e8c850fc70916cba2c66850d3747e7c299c06901da3488f8895279701d828b8e281d6ff4119f8ec661640002cdfdcc29212f78dd4a34d10837ce3c7b8ce1a3af361d2e75845f254154ed374c953303a03414549df367ce303c23f6902877ee1d149c06a274ae2be7c4c489f2f47967a300ed732415de98ae047bf3139dad6c70b98c03719edbca11a04bba7705a3119ace451f8ab6f76a639e9845496c7cd6c9ce560c5227ba49e9463e5152501091391dfd741ec428f839dd5e3d448b8ba280264e6aba0ef8a30a7803ed51f63a3bf3c9649a0fa6148a115d9fe17a1a627f0127627d086f0d5bdef898b907dcc3bb70e6687faa1acc8175c414f83ab5cacf99e2096e0833cfbcda5f3ca2f46eb8c7075113ea1c5235ec42a2cbc73937c89d53c20825b8c07234f5d24b767b9b29dbc08e7a3a4416cb3420ba124709cade31c4119d0218178ba38a16728567ad69f9ce9e41feca217c8f35e59efeb111ad64d7a3e56f18fc8cac7be32c19e231344b4435e016d23a3a93a35a672720b35380d0769d7e42ee8c06cde707fb595968ed43050f4ab559378649145ab19a8c412a5fc6663ebad629fc3f9f94eaf764e80d25d6c1fcbe3226b8ea0091166fe5cc8e4d08d04010272cc4eac5960d69248d38e28c38cf141647804c8878508b15523662315a04d0bc5eface7a04486280508ecc07003cf691a0983476bdc6a718d26b32e2291c0a181ad7431c13a40aa7832ea92c0d8de2c3bdc1bb0d3a2403c8c73f87334275c950a4ab23e199feb1bc3195217aba316970534290b617548270c51eb874980b8a79b64d17348b302d94fd1c05e63d2386a17f33cc7607602d701a9731d4a707" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "128118f50dcd590c058a1f5d12bb504ebd0ffe206b3d244221b1c959aec42a66", - "proof": "8a6005ca26621f28ba7165932aaa469496a7042c79e650d178971e09f00932174a4b0d1ccd08e545bf1a39916e781bd97471d0415d3049b89a04ec4a3a35674d4e639ec018b2f878218360f2f561874a67e273797fa31505ec50bbfdc02ec16f6207704024ac62a39c6c1909c3eb0b514cb8078cc1d3224dbca8458ce6cdfe433c5ca240b836af409e48161d8a9d2864b11277488b3197f04f1933be03dbd70a6ccce47b65f18b47c4db4047fb28614640635d2d34a08d2dbecbb01282817d0d1a58bf83aa42f874d06ec36c1a7bcd5a223061a4f515c18de1c5e76023ccd30d3e4ab2492cda5155327b34fa30e90d3b2a7be5ed3bc6bd95d07b2a0ce5f12175d6fe7148c4341979c514fe374aa5a5f0bd36eb66fba76b480751c69329c43f27cc4cfadd9e6842cecd8ec6f2da616b18a67261f4c8d0e4bc2edb9c89a704924eda87d9ed47873b7ade6553f7becd2dedd9d02e648b01ed6d1fdcd78bb8a0ae0a18a513c3df501a732619b2f5dbeb569146668dd0c94a29679d49efdc23b7b95facad8042ecc7b6fd38f2002363a79aa69d578b4a87278e1022f2c7b82e85316ac4a639b1404e2c72f3b3496b464fbdf3f9717a604ece3c045c07e04c332ea11814bed77aa20d16282ee37e24336b650ac67b2ae8640c6836aca6e90a30d78e0cee971d90ff041e52be8d0b4904d124530f40b7594d736ad3b1184522b2140b394ccfed22628019605dd2f9ded3e80d6775230c51ef8848586b8060ac620cbc6720eb47e263340c1af1a91292e294cd2fe1c231813503977135a5b8e802e7f83cecce24371a45673ff714f3a52bee4244862526a4391d8a5c5737e27f6a17322dc87bcd222e13b34b122c466a3bf4c0e8b0bc0f08aa33b4dc9da1cba0dc09d40b8e9d005c5261c654edccc33602de2f1c7e8406057142aa6796e129f8f08a7506" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "1abae0bcfc365d9548aaa1ec96e8f7daf2ff59450b3e8e369e64a4e32bcba741", - "proof": "c06944147ab08aeeefbfbb1372b0c692acd77796fb6aed82010c81a40177dd7faa00db76f7526d5428f0134fbaae8118c51357359e495e5a04af1d5b9314db384c356f97d0a4f15058ca87f1b4d101632205c69eebab09cd46a62f3a482bd96ed0639e86daf0d0b2f6ae640dc47d3d0324143d085c01e83b0adf48e9f07dc82f6783cc7c41d9ddc46abaff4395a2647da6e545ed0f9554d8e37207369e42ca090713acf5d02f05b5580717649d187f20154d51e7e7f70e2c7d32985617711b0f1ec5bb0f4c22f4e90f560e8b724d631870000782749e25b0ce3abdfd332d280b96f75ada7438850bf4840e5508ca9f41f88248dda13296859061bc70cbc7a7500414b169b70e12e9a65056307b750328ed3562f02352feadd54ace602791813334250baaece3057ffa06e771fdecb648dfbd15654fc37a4b5c0539a7798cec299aa147676bc9da4395f418a32e58dfc6bbfd053ee115a7b0c62141891f9df76818c8e452f3bdc601c141807c6f901ae28a546a86bf80d149352fe15874f9c25c12413bc0ec764deac36bcc5394133e4f5fe0fd644531b8a5dc9182d29a3b832cac8c25ebb427088e4ab06ce3bf7f4c4be06bdca38a6cb16ec60fd198ff3844242eaae7124f9f881d0944e9e62b1c17c817169368398c4a6bedcaaf9f2270935d6adcd992c5dbd401f1a5a0ca10cc2c3e98d7f37a1be83f63c63cbdbeda41bf1d8a2b9afc820383163cd741c7b94968ccdee669e0da19ad07c83f6dc046e2002f3eb598ec04692183e7c214578fe6b65b4430b542f0fd820884953f31e98e933b744f0705d6da9af361562be1354889d813f43d99554496acfbdaa764aac2ab64ec37fa27c095efd8bf8cbce326c9fdd481c14fbd853509bc0b405fa86bcac3061836fb2508c3043e78d828c587d7d18d804907dd399edfe25c9bca0259e33f02" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "1c391011ba99d136c9109d57860a924d2d6b6b027fc8ce27b1f4fab7054be137", - "proof": "4e5077af03df9782ba779b27b02545f545b31cd9e4cc20615905d776f4eb756e6a131c3bcd278d2f805c18303a43b22e19d129c2d6a3bf5859b72ec6137e3f39ee6880e1c97b0332c7d6073fb155ed1a82e9831dce3352b8cd502c18e3570a5ca44ecb4c149917b59abb459ad4fc3115910c5fdec40c681a97590994b1b6ce6ae730698bdfeec5eb7c7dbe33a3e711e9ea6e0dd8de17c2e8276c415ec9bae10b705d9e4f892f3a2625a801b81c269546299e03b746f7f2bf8a82afdb5e1038059bdf9f6072bfd57242980f9be3ff187bb60168538c83bcefec0f62139e57430e5ea7f3cf910ad5b38b70901d7da06727813a71b3f183ccccb6a773da247008387e9e39a98062f3dd9ab366dc8b3d2ad23fee55ad924420c053104c74ff386b3222c07f0827c7fef36b3e4004f1e3778f538ad35b87550df2bac60378dc6f9501401ec506ef5b30947ece8c91c3c45c99e7ec2b0629468862d303408fed0fb240b0a68a0754ff76639d2b616e78f8970cd9525ef6ed3d519e66371f69ab179b00e42a41108e18ce7bc6685dcf98261a8b65461fd8f264ffc17848b220bff22c3e0053c2e72ae7841d650b0e29a64d33ab8efe9fa1a6f7aa57c41309b2eea74a15e076ba0d1796d7f013427905f99699ef3425000255716c48cc8bbf5590391d1b72ac27dfa1d8d78c38604031f428d755b0dc6c0c27142b71c4a29986a9f0633e1e266fada644461ae0e4b0894501cf1862ab8246e0c7d1f0f56da9fb342c4b55f89a7603493e9285ecaafc3142201c3b5fd9c73f82a2314c0806630801c6e00cf0dd7075ea7ce719f6cd317cb32743e49589b9bf2ebdb59c3c0aeba3eb7658526ae833cd3bd779e393e2b3de9de2d2c5683b3590f091dc794180182be36c70062e5ab244c9b1e637c22a3c682aad4534c8af17d0b08f37cbf09b500f4d8ecc06" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "3e4c223c98dcd10b4785dd20519950a6d4f104c38f6d310ef13bec98183c5941", - "proof": "ee6b1a14907fd5ed9f8aef80a8f50e011d5615de066c9f32b03344092865fc22febd4581a67f1a4d164227c765f2cb68fd36e19d1ef1cf8ab783d403915bc00b64ad1d3b6323cd36113abccea96eb733205aa81a678fa71b862e9ed440bc615c646100ef46e59870d8f9950328bdaddb055e72aa9875cc3d6b92597912d51104536b4c7816032bf0115587f3d552464874e9b6dfb3890cf8bcdc916aba5636062f455f89014f414fcc85bed472d3f6885cb438ca09d6a141fabc861f570d540e93e44895a34a4d453fe7348a9f1f590147ec5f614770b6c15fa4a50aa0a2ec0b3e7c437a6edb9711a7da2342ed26a4b805165a0935921a43163359edf429637b624fc48dae55b59ccdf9f53784753a7d87e6cd497e8079fdd8e227d8ac09d42c8cede88c0ab8a2bf16cdea79f983b0d085b4efd02cd612fb6c5b56fb7953ac2c4206b79393b9440b70d8dcc28c12a4e7a23e41f47776724a0538be68c8cd4635f64a8b28d61dabc065a4a54803fe12752aa42c3be650f5874916adb04457745d94aec4e2758f0aba287ff39dd206730ae82fcae64044469df757e869f54a2f171c75f88861631f286fe72e774072cc1a9c7d81990e7758246b47ad67c24c276b40a334437fab3acdf73bd1ff1cd3e969fd386a26fc89a50d98ad4bdce6d4466d00a3715ab1213dfccd7c94830f6e70bd74eef3ea72d084597878c10e627cad63beffba7d72de0f6d6840e5c48df1cf22b271e275e8ad2fb0edd096dbe5439b74dc2250f1c81fd316903d8090ee6ae4af8cb9655589090aa5c668989b0789330f46f8e3409cc9c546eeab366dccb2acd4e4ca2c0fc4ad690d167e9c5c0c1ce90af504803b081a317c5b1af3fb9369926f33e00e1c08d1b73b6f1d513ad0c8570df34992ec3e0bb29ca35c4b68290fbbbd7b9faafca826d62e382114c6a96f2d0f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "42f82f7ce2e251b6715407502602fa45277ef256340170c85f1103db23d35013", - "proof": "d46ae895adadf6f7b0fd5c40c6ceb76ea5ac7d59d73a433ec60b4a5cddecf0329eedd4ab57a08b515a3613d3a0ecec1cabee9744092377dcd815f3cafb14162380b1f385fe594cbd5e2cb90ee514f5ff00bac7b54aeca316cc6392868ce32614244b6f69be90e0738cd2c1d05a0b5086b81f6b46a98e249f2032c2afa0c2b958bb15eb605f7983ef67ef94e3b08aedb88c4180764f7f5bd4d359886bba1dd60541066e6e10f10015b9e4011d75c4a625a1ecfa8b21bcc6e3c1f72a4c83d379095fd3428786c3b75bff8af224e99bb72de46bf39ff77c935efaeeb426f0405707f4bd8a593b86ca640163934d307d2182aecea861826f2eda54d2fb0eeec50c18be6ea02e93d5976d21b9367a9a36dd7130f2dff800de774cc3fe800fecb81775a01a497222b44029dae63c9ccdf62e181278c95b05c78b3f113506144ddee87e6c04ac6cecf4444deb3933cc1be22f03426fcf806bc33d7f685cb21cb174fe12f6527c77f42c558ba741fac68a62ffaa58d62202973267360d850b836da2380584a981b119ffa695f91090b7103668e277ab622b3ef7dc2bc1d424dc5b91f46ffc902bd7109c233dca5da4113bc94ef5e44d5348714322afdb93decb15daef49f0e9a550e0af6d4190747bd2173bf94a0b5916cbf561ef3aafac4c6b5354ea04c05c1a87973f4a082653d3e1d9192720b6be24bc3eb59ef6cccac1084c37415e9ac71ab054799734c78d1dd361fdb601858f9001ce08bc6f0508ff372016824d982feb2efbc0adbf4607eb7486b0c3a2f8256163f68c9ad6973f31d54a9a425b72b9d0bb6bbd98cb9311d5efb910aeacdde66bd56336dc8e36d3a4c39071f557e0208d36609e028dbf4ce0d0bd5d6cc81e4c52487fa76f821d6bc42d7ec1cd0f02f3957a2e13bdf2360d7f2f20219f031b5ef8d4abb4db5b121da9977193820b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "7232d05f5b32dea00d78c178b9f930aea5c0ffb9c71b3f3552f87ea4d0615a75", - "proof": "9005a08eead3327d7c5e03492b23b2b18e7c528b6fe7a762099b6fa053bb971416afc43aa96170c2469be1ef308863a1d66f229f59ba9b7ff29f1500a133766e5eaa183a9fb63c698d5a2851e37462b50ab49bb130c65622c16be462e6e39168da4a41c951ccfb8778fedd342a0c0130c264d3e7d7c7b7d7fbbeab1e0537c02e65b0a8447c367f884586d6e3f0779fe8b566eb6329833ac874957d5d4310cc0c91831d6a04d11b900dab88750edccfc4c72053a55b2ff6c76cb1144bdcf6720160ba4831485bc36932cc5472afa35bba2107fee5b180e1ac2b8b54a4468fa40a5a86f59f4d12ac8614e9490d55ab6f58b2733115fe84960ddc8dca13f4e9475e2a4bb613744a9ddebedea00dfe39792965d33b79496fe89d6f159d8ec54d591382836d14d4816204097e53f19accc1325d39086a8cc14ce62121ae924e8364795ae2e63534e6c50a666cb1f1405ffcbdd1b24d5000feac3aa5df3cabcd18aa45cab0e1a9a9092990e0a797307545a00eeda0073d4f990ec74933c6111defcb39f64677c3bc1b793201a3729ab457cc76f02da35fb9d893229f3ff4f634792443da08a91404786ccf33537ccfb713facd977a1eed293874e3da50250abbfed174e8d4f47cea786611fd47319190af397fe9f5d028a7e129e0d186708ab7bff51bd667f36d8628c9df1e0929e7f46da13973888a8cbe41e5c68e3dd526148d71541ecaabeca39c666c3660294579557bbdd2e1407d51ae5d286bcff610243f0e784a65afaa6cdbcaa313c08f5fb8946796e8878eb298ddfdcdfc3ec3965fcbd724963be78ac3db6cf19873fabeab044256837769d5189257e599e0cff239416932f109decc6d5b6f26e88f57928d462e242dcb5eabd537a405d92e4a22fbbaea0f011379082f79ee27d8ec5a8f893548fd3e7a1a517864cda3af69cd580ca02605" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "9487e077acf794740373dc82cbd7ead91f9ece275c1dc8b4f014de712852af46", - "proof": "24d69f37c129edbf8cae38ad50a7d2219022e6b6660b062783d2896a3b8d077bf8344130f52d3f8f99dce9655de077cba726d653451990325c2a648e559920519eacba9884defc1afbc75a93bc0d474ab57f66be4242770e582ed5c9bb0f1f41088dafd14b59cff868ff52ed5560c5ec46b68bb2f218d94c8fa84c191c312c2915cf04dc122b14b69700f48468cd78b6039ff0161ebba949c4c5c0f328b8210107a321e4a2c31f57f9b3cd3df0eebb0b444421b8fa7d8eb21f15a0606b6ba60cd002e71f93d870c6416159e21a888247ccc90f8ac50e96268c962e1c65b5ad055e6f612f82e99595d2e23f5bdee726734b204d8de647fa3fada13aded8c50a04107bdd486c96d03d1782634db4bd2251e62e4282fa9db084fd49037306f8a1425e119f454c618c96ffb07847a87e4b3cdded27bdb3bd55a769d34a3a5a786c5228960e5d9a5cf0487677d59896ca2e81b3bd209c2300d65923ad37f2e34f0808da86f0b6bbf1951a4c13f9a355e06352067ab8b82af6d35330054b98e80ab320fc12e74a90f7ed9acdd569ad24086039054849d32deb57626e7656c703aabe29c8c7a9636058117431ae4fabdd4e352acefd5dee5bc7c4ffd9059381a200166f10fd2f4b341c81bace9bb016e2ebc1b0a44b421947d28de943b77a8c678a9c46221a0d117f13d4778424adf38779400d18020f90ef76f866b9e2b0349e47bc47fecc9a87e4207124d7ac19d9f5e1af566b3e443600d046a546f5ee40102fc444e8d59e917fb114b4e1a65f15ab3cccd1c9d09e91253f7bbf549376c5c2fab51046467faa6d9e8d61cc179a78646677437ac57295e10a935de18877802184490e308acfbab062b995ad6960a4714adc638344b1ec3f34ab5c3d6499601c384c09d927c4c9be93434546c3a9f57afafdfddc9eda5de2fc7a13b16f9caaf2dbff0d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ca161d04ccb54cbedd7fe32f7371b7f625ef1617809a7597dc39371e999ff20d", - "proof": "b209b48a3b69367a9a2342fca2c5874a9bfc4fc3815a99ad861dae7605350c007850e7730f323764063013a6a71da0e32c244fa016f482994a0da3f003f9d9151e28a202c9422bd0799f02efae159e9e1879588ddf9fc16ded11a3675d53613796e9fb0199fc05c5bd04fa126690fbfabdd8484b62b6a0c6a24f4d923a9cb77f63304f483d052fef615e4619e585bb8578378c2055081132254f522f4261b0053f2ad9894944cc805b747d815b0bdb63839b3b49909da4ec1f9047ec90ebee07d54824080e4826e7741638517760530f74b79d7b8eb7c1d4f0687dc64d0d9d0b9860ac41728045928bd03fb61819e58a337825cc41e051e46f0e079a8daf6a790ae7241008475cda83d6c5daad3f6f2537bea912b535b099c1924262338bb23f0e9ade6ea5343a7fe80aafe3899bf187c818b5afd81c0761024dac4f9a01384f1e9016e0af1314552712c473ec606a74f07cab2b978de51d333e76e05b2781087cebcc871ceb1f2b56e74fdae5abf0016db7096a9d6006f59d6494f80b9e3f4abc651e9c70a708275e6cce88d5493e374757bc4c5dd977efde3bfb1a2cb6957f6c6a3255d2e50544adfe257ff72fa52573c3c788497fc601c4732191e3f29a2ef0aac95bcd374aebdf8610a234c3948d31802fa556fb7bea010104cef5faa35c6e10f8e94a516e8e827ff3fe9d8a9ce28a0cca4e5838b7ff8a40ffd619ecbd547416e32c104850efd0d87928ff51b72c7c47c1be755498119c324c3fa27b824b12ba419a960db35694ebcf6dbecced7132572a92372933f3fdfe38bef060ed7488a520fe5bd584b37765b671742d43028016c378389751626b34b3cfce57e15782b5310d5c474a7c20401c125456bb8d75b8c48f5bfb6418182fee49e5cff9042b44191ae2b5a4b8770c9ccf4eacea6159635331e2d1262b2cc88e1577717e0c" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 33 - }, - "commitment": "e015cfe0349bd99aa245cc523001ea943665aeffdb90743b3cf52d83866a8243", - "proof": "b4aa65320f1bb25d0333c0219538bd0b21f6abd8932ce75602977d115a53dd26ca4bea79f3a62cdd1614ae77832539897fdb8658056a1657e96908b6054a0e66e23a2f4583bbbc40bb7cd25f9468f38f8e9ec70bce0758d663a9b50d5ef8b4182cfe3935466209354f99063ea406140f49cf2692e17352667efbc4674ffbd237fffe3530e4e517b112097afa620e6be13da538fb173e69d921fdd55f506eef0c24383f3b94171dd8958a1bd5a55452f069ede471edf6cae9311af3ebdb12b305c6fca239341a6c0a5268196a1c5bbddea7cc2e3fd98d13f4f6ff10b6272c3807b4b76e7bc7e3ceadea12f000409e146a00a51f4a2133879fa92f6ce752544739947a41b05b197dc783bfa3cd2905c175b0a818bb31dabedfb6506a91df020b7826ff71788fdaf347254ebf2d98c51d9854560beb25072b84c4b28870e937cc3fe21039af925a482ca7d7b9e8f41a20b835a18dbfca9117838d6ed595edf37225ae8c6ec9ea10d4d4004787fe8782ad7fd8baa31c1da3a9becaea4e5b02061753125a52c395bac6736d04539278c50fc4980118c8385954420e28ec60f4fb6967dc2108b250ed48abff045d71b481a3f24a219d510d687bc88fbad22cb4bb181ba0d76dd5cd26bc5ac8aae8365be31ccc5a6a2d70c5eb673892eef13df43e0201ca3267daed9c1df277304d567b9ce521cdb57904616383d39567c1a74df8c34990a33fa830c97eab08b50eca7c59e6086e2a9b318bbf9d3b66c638e6dced6a6e364a091c24286d39f8081028d1b2778f02f0189d8cd96c23c83437ac98fcb43c06cabae73a3601ec9e718dd90485963ffba9436058951e9d548aacf69effdb11baa8210c80b24e1bed18827be7b5cc27e32897a10c9413d5682050554abbe90a2f6967b3f0747e5d4127ae17ef1cdb55ca14a75621392124f20d8030dd155204" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "14b8639e8ba33445c4c5e93aa5be0acd04cfcdc5b64b260a73a1a6a9d1f0ae3c", - "excess_sig": { - "public_nonce": "f4ab1d3173b2cea62bc69aceab9f7ba9920e8464ef5f2f05c6148fa0df66766e", - "signature": "0dc12046a94e6ccb7197beee021f168027e1a89de7c81b760dc830aab4c93e00" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "207dc51d9f547aa92b4af2667127a6215409476b879b5298a8d5db229a043153", - "excess_sig": { - "public_nonce": "28e447c1fe4ae64d97e2a8a7c97d6503a1fc3ac5e75afc14c01309d0c0c4416c", - "signature": "f134dde4b21b92c841ae2af060a590417051b6fd1d920eea34e2f4d7a546a00a" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "7a7f5793d9d69e95fefdfa7c34ba603adb6a5c1681fe1a49ebf97ac9909ba92d", - "excess_sig": { - "public_nonce": "0e6070c7a204a69e2438b0345faffa915da979c03f64ca5205d6c1c5867a0e38", - "signature": "cfed813609b317afd3ba00fa1c1b8bcbcee9ed5199f150899cad3d22b1ed310a" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "d07892052dafd91ed779a0474153af8354a8c003a7ec0010e86f75b3bb61e749", - "excess_sig": { - "public_nonce": "e48eb09c661d70a7ab452f8bf4472249d1a72eccfb213a48cb333a54f906e955", - "signature": "000ca638f2832ce9a909db19f2c9058525e859d89b486576ee47f322a4ff0504" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "deea8764437caa680c5ecae3b81556e8a28b3180cd98b40049ca14f82410fc6e", - "excess_sig": { - "public_nonce": "b094b03bd488abbd28bdeace9faed0fe83e873b73de17de6b59c1d1e6dddba74", - "signature": "e3e69924614dab9d65b7cddf412a98e93b70aeb59a30e05e037132b1db701903" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "888c1b240a7cadc41e0b8e299694a9f7a0ea006bea49a8fbe89eea983b2cc117", - "excess_sig": { - "public_nonce": "14085d4d3eae72c4fbf42b772de2830ea6f5019a4d02c98be90cd7081f6a2f03", - "signature": "419a4e9c4d30a58d33d7ecb26d7f00d2d85e13547a77736bea53e2485b895905" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 33, - "prev_hash": "24c045c50aab1300f641f0e6ddc5a0d07c875984d1276c58e4c044c512c69571", - "timestamp": "2000-01-01T01:34:01Z", - "output_mr": "b6afd1752c2650ea30876857c8b8a55ec0fe6e47d01b40c009cafa095fbdcac6", - "range_proof_mr": "9f73c18955561baf2a32425d686663dbbc9dd15a04ef90c462a86ceb177efcdd", - "kernel_mr": "bc2afa8a85ace087e5de28d21012fe5f65081f783840de1c6b409d9cff4e2ef4", - "total_kernel_offset": "a851b715a8bdd7ffa707638eef854cbf57d55b27367e7fb9c076bff3681ec008", - "pow": { - "work": 33 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "1c391011ba99d136c9109d57860a924d2d6b6b027fc8ce27b1f4fab7054be137" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "3e4c223c98dcd10b4785dd20519950a6d4f104c38f6d310ef13bec98183c5941" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "42f82f7ce2e251b6715407502602fa45277ef256340170c85f1103db23d35013" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ca161d04ccb54cbedd7fe32f7371b7f625ef1617809a7597dc39371e999ff20d" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 17 - }, - "commitment": "62fd4d95c4bed3b7c245e4bd25d243fba909551253245886172fc2ada34d6f38" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "04519ead0863cd7d80639b5da9a94f0b18ab841a39aef176834f5a01e160fa4b", - "proof": "2ef103227bdd6c7675a8256beef2f434e428ec3ec69265eb55cd9758fd55313220454378fb305fb18edef8ed250fa4bffb84b06edeca2f85bdd34d24d136cf7110b55606c7a216369b85080d2fa0a5727d167ae8277ad9711dd47ac5bdcd195702b4fbab2c8234cad9d75e35d007f382a540359d2e4f18bbc5eb13045b183d56a22dd1c7d3038907581b96e71f9bba602a9531636cf494cce1227d884254590e9377756964b87b2426f86c398dca46cbc95d0b3a0e5a6e7a58b2765afae21d0aebae49785743b98640ccb706d33cfae85ec3d90a1912424e3ae4f76bc954cf08924e510e35ddc5433de2f5ef0d6727766e5b14c990b75c62ad6e2cafad0b0744a6b8ce59dfd820ff525ffe7b2f059e2e204b20f6f8f72ca875fc5f47b4b5fd0db68f4a85fe3b542c8d18c8decdfabb08f12887e7b7cef46ad666699525dc8b07f86931b0a4b430ece52ae00a5e69b8b810a4e6283eda67defb2102bd7afc7631a4b6fb2afdfb9d5ac6cb2e76a831da548225067b4c70d62f3a864bb239e33e590c3e531000854afb8ad2fb86c5058cfc90b4cf36aae77a34d342f6a2da64cb7724421f23d5f199c068e8edf78657e3c2d0c9dbc3d6f4d624356163f85a9f2776be762642d948f89c93510005b3b217b875e07c98f5ee554b39f112fd732b106b5ab7b02217100cf67d11e41b3f813b3792f20381c1684fae9032c6dba85ca2452829305f9a5d7c2ffb95f95afe4faa91a1dd941dde30c217035f5518cc65dc4a4eeeb07023033ead9efd31dddd9a92298992e8629a3f7159dc041c9d0051a65408cceefd69312b45ac238ce5fdc60a5a9985c8fae7db2e186ce44d1e0b3ff52e2549705ca6d56134d592e8232b19c6a61948a72821dbc758a8b893a2c188c70e1431d10f8008e66be9eaa960136d54ab63d07bfafc584b31e0dfce37b4f4e10b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "32e0deca835dac1faf0b9543f82706dcd2beee1d322208a0d36d3cf434aadd2d", - "proof": "4e95a08268d8ffaedf76d483f7beaeb91162b5d36b15bddcf8363e4bf2fcfa0de2507d1c751531654e02db57439ee228691f59ac98a325a0dedadbabb34ab706bcefa454edffd8948836c99c594c33d4214db7958a4be8743abaf342c503ab3b4ed2157ba7b0ffc255516f2c8f5eec8932288e46428879ec0b51faa65b7b9b125f0b6f2acaa7477d32b3275c9bd594a611ab2054d93e15627c29d8f307c36b0fa4d56cfd92435c3a2f5df137245417ab8f1d67cec242a310950050f893dacf06452f7f6a18c7412c06c6a560d6a4c5eab06ffd6a14089cfeac4927491862190dba44d4fb830c46d7b3d3613a85875e17490b47b22ff26f52f60f40ce835cb75d4ab382841df3839461eb657e360061f366217b1301d0026438a46229772e8b7f6044fc2ced080fca039d02214e725419f6cfe8c7a87694e3fb02abacdfaf7f5ed268f4c386ba8279d206f3a4e142e4898d07dbc382b5895881616a80cc904264d84bc3f88e33549c8558bcc45c5673c97297f4236f994c2305ac022ff825246de6a5eed00d22031aea9f9a96308fd3284cd70fc90d0459e7fc529f67acd1bc085812f99e75ae1fde47bb2a4facf062de6e400cc31feb66c484529915bde2a606be11717c333b2d86bd874837d79acdc797561f32acab5d003e6508701d5d21150460e53ab57ab577d563f3359f86259b9b4abf6c26e52499dc9ef115b2fba22a3616b0df01f63577915c7ca3acbda83f32e3c11df8e1b9cfb7d658635d4cec508ae9e29e6ba3847353fd71b7b1204584edd6fde75a2b0a629a48d5ccf9dedd28de045eed6b3d1dae7b533cf294a74c1073bf00a3b4fda8c93c6961e00ca747654cab6cdb31009436cff4db91b04122cff1c3355fb95df014fa2d5c270d3c430bf61d4fc881cefb791c8481401b68dc4fa25c232fbed01fb11d3a123089f54809" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "3c9de4b403e4c6414217fb0469cb19bca14d81e76828e0e85448827f8f3d5952", - "proof": "2077eb9168dd09950aec2a2b70f2ff85d4b3b4099c58359c716a32341814fc0fba755a53542106150a09f5e0ceaf3aa1c77776c0504c875a8936e671d0a14176d05fea36a131ab0f1c1c976a347ebbc5d40c3b568838f90cc18f7d95f7ca3140da3eb82120dd5b3694183788cd854ace947da472e92a42d104a3930420871e41fc535737130c67b9d5dc983417931e71b39475b31d20f1172c68d4e029e709088ff783dda0600260867e7551dd1bba487e1c4c9dffd28b5e8c79f27ac3b50500fcde9849114b1b0b906ff3d147ab71ba850190dbc6aaf1bd909ac6c3b6d7980050e8ec06c91f0f31f260650443ec7ccb9b7fb5a4b096140fa76d55fd21f804786099aa734411e71a017ae1c925cd047bc4c5df945c40b9f876d6d042010c8c47261ddcf1a79da69feb9a0d83f000c25383b33e1e38d3a4a9c4c2e5dd8230207b5e581dfb6502995d0c077c018ff7119ed15a299508bd6d89bc66d5358996f852987b1490a94fc725a13a9a0bd1899657b02f59e876ec9d74cbd6db1309e96b3446ec2920a02eb27b26849c20d08cec42d786c77ce47eb747d6e4b01d187cbd46b8ee98bbb83b19c85f8a293e4e3f51bfb28b58dde9db7c7ed23c9e36ac35721f569e64ac5bbf60a12b59ced6f1fbe5ddee84ff689b3349e8ff3026b118fc3049d24899120a2e329d297042cbfbd99b0c45a55d99a490b6c726d5ac4eb539082bd8e4a253069112613e219a70036653bec5fbcc477b3652d840f1ed95dd26256ba4a68f7c88992b615bd64eb033f8dec264038ec18f0ea0afbb97179cd61ac04eaa8c9482ef7012b95c291464646799ba82ce5c6877361a1263cca052073d6829e2a4004b1d3b0b4cb818fcb300b41cb656e35826649a6fd46aee2d68d8c862025fde2c9401360db095b2616e5682469cd845305103c9125ab065c2e8c2f39e03" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "9230e94b4f58ebcc691a46f043bc5f2d292b2647a927f2fdccdd550ac744a16e", - "proof": "269aad69e43aace9ab98f84cacad86b3924237c070e5b214169255d42373492428adea1651653e0f1024962fd313a9ad618fc2bb2bdfdd2c5b080fabba053d3dae842787d8d52411e7f4fb837703fe2c99bf92d3a363f140aee9694cedee1122e2fa263838e68284c4c1d1275b7780cdc56916b4071d0ca312ba1c3ffc90c11b5c3a92cc9721cc5f5eecb2e021f987f56472952670baecf2f2bd6e9681d5420219ed92c59c6b7f2b88e305fcbb653efdf7a2a6ba061d3741be795cef1ed9e70e8c68fa97c6e8aca663eedfc0bae995852f73dfeb22182188ac7193955a9d8a0896bb67533c328166b6e355874fd10ceae3dc1055a18be8e28f063bb358e97a5726aeb89faf88690734c9ee69bfbfeacbdd3e1e38d38a420a71e2ea07a977731f7ecf7a6f03fd63113ab55b6a59a0316cbed662384f5d9236cec07304379e9e602ae1787f3b095cd1ebd80e04845f1797f1365496716ab98b77db663855a3d06b7c6089c3d65622f517c45073833ace94ed547560e594f8d6ecf9bcc0de8faf5b34fff4f57ecf6c1481abe4a422563618f1da744f9a32e11f8644631b8f85ec3c98bdaf87fe0557f1220cac8f42cc14bd0e21b446cf9c1a087e3cb6c24b167f002ef0746c168eaf9793b90a270da1ead58491f19b1ed36a0847b8a1f56b820e225c49d541172fc94e63f03a097ddd6b0cf8eb43683cb213d073bd1e3f4c3f0a581645dbd85d3afdb32c7d91c0789b05437ce866699d15267d6461326068e00c15b6c2aa17303cbacd2d35d69c859b2d5295ac1d712ea2a720fb771ae25a4e7329969760d45dbd3e632bd0f8a11549dc5188fd21ce42046a60adc50bf5f8007627703e9bcfda31a47b96e5f95d1e008fb63e102115661d13326cdc24b846fee904531db89fe1b6178b7a0255bf2982c7f32a066182aed4e9bb2d9ca6fb51e51807" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "a0ba43e4d84dddca0844f77363f7ce80f13959c4c77251133e221b5b91d49f28", - "proof": "56991c6b6e0e4bdefaf299a595e136ba4f825b8f5ffa608896b99c939ac7887fd467a522df8c8d2ae4b24c9140b678e5304a52ee7ef9eb008a6c75ad4671413e0eb1f79a4baf3b4005028d06c473b80e97abf660efa078cc21c7dc6a7745ab00980be6033f15e8214b1235f179c77b4f694f1d7acee8c24eb34408c31faf2343d7a7401128e08dfdddd65bf537c90424ae09ca096121137851673bb842a2850c6ea91d74764c4419041f37a0b633312ebee780c519ba13706a57774de2b7dd06653ea6da4cae2c35670a3a5848a9e90f3f45dd01d9746990f166c6881977a803706b46b2e602b3c26ab50ab38893f8c526ab5aabab27f5505c5bf6e56ce080068294b2c0ffa567101385142273aee2fdeb45edddabd72fc137cefd1a1df39f10b2c334b6bfbcf39921d38d29afce92cf5c0d548f7f629acf7c1b3ec1dad47652d6678df51a72fe2d28d85b5ba2fa3b5792c1326b8a36946488bae957d8bc5430e60ad83a9af059dc0a0a11e6b5f56efe32fa2a8cee8c2848524de59eee672110daef50a3a721424f6e8f315b1ff81ca85bb56e1aba51aaddf77d608b3e3a3e6a78648d7e6119824a207254546c2093c8e9c716bd713eb09162bd4fbda56188632cf534f25b7715ed6e2f749df234afbc9640ce0e772d811af4c1d908dc133b28d86c004c088784c34e72df84208de38261c3cc63b5169f91ef62accb46be9c555c146466c714273c7921e307c2c0ff89c6bc2a6fbf39a4b57adefba0fcdcc3325a093f306c38df965b41893c8b9d04b6b46f06d440bccf1a6f239b440e405e663a7929e749cd031d542b04074b24940afad5ffa411045b781ecfaa5766e55f4b9b8ff5db644d782bc3a0afb538cf2c8480379690cd1bd0ccc1fe6f27e40cd10b78e5a88dce2f807fe7fc8492994b4728b410a9d4526714950d6d86a59657250b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "a63c748ea34c75817e5ec603f2a3e200557256b4bf91b0c3cc977956c2fefc48", - "proof": "b431699e8ba4d93f03e119fd996a138a6c9e9b16e7ea370b9c4f95976094244d0090b30f16e121dd3a24cfc8213ecc55d85618b6d29f7870c5c5a461325b5b58ba3de58b6b6a3056d773f32f8802c6e5efab33a91998f993cee3b5bde28000229aa1fd3db245bdcc756f8eca715bc35253c431496544a4dedba821cfd48ab10d3ec8d49ec6519e9a4416f7453a3e1bcb6be847ceb901d82b97751365b6105f00b1e964731632606f67d646e2bf6458ff9082f23712ed09012b042413fc920b0ce9c93759c8933e4423e6b956fbcb6848a021ad9cfe9176c32652bd5372cb65007cc0fcd94fde55eaa5fcd8a10a807bd073ef6a06cc4a0143bfff834b8b8adc32587b75f509a09fde9aa36964ef04eb6367125d6c5c82e88f909847fccf501849e4252a9685224b81dbdf3abe0acc64a3bc78925b7356ba5b71dc381b9191673cfcdc985dcc0a10da6141fbad29f09a478c99a65f4796942a6e0b7786c4787b3292ddc83f1cb79c96d8608974a792bb0c072168e322ea94685fd32491416bc877e895d345f70f0e1bebd83667db3b55707b0b47ee2a11dcb72babc1ec7c158374ce39f969c402a8356af81744a39bc60628a32584d16fec22fcf029869ac49e5e56e791e1dc5e69dec5e115b22afdbf94c6275a15bbd8a8932306e9680f934f16ca735454c4de539f83c81a15a88e15e81d52186f2d235f0c4802b4f90ff0047cbe811057d30ff85a2c710de9d2fbee6a632436b4713b519d6e7ed5d2253e432e60bf2abb707137dde6e6852ee4f0a7940f3cdaead2eafaee4498d8c73e060f562294f036e00865ad856c6a692298040095222b9aa713fd1c95328f60720a33373cdf5c5c146311c66f2a847e3e2250320198e3d4765dfb6d7cdbde0d1d402c09b3571e0c4936ab1c064d44e7d0a9627eda3f0d3829678777c97643ed7cf3200c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "aa45ab2aef06070431a184b586b9f74e56924ecc926694c8296c0b74fc30ec4e", - "proof": "70a4c735e156beff316a68a28742e5ccf03b3d9769998bc6519c91ec226e4a50a2031707e67a260a8d3c155691ad97e383b48bb954b074d16378a1829cf483608876e8e20ba8f5054542aba940a8ae7bb411a84ed647a7881b90fe1fb451da54e8451ef1642bb4e38672dbca544990befbed9b6863eade58d07cf1081af6526e6d542181602334d54e975b867d0decd9c8b27104cbc822231ed52c1995b79b0785dfcae78b85793036410d3dfe110cc0c468c3299746ef090582bd1329a1c306aa3be165794e6b90cbc427c085b2ed10e30c08ceb8b5938399337e29418a4509400395c59ea1c09648d0b49a852b64c47801e09bb334eaade5707be897759c30d0414f1fb87f21bee98ab3c793e3d283073b69f8bdad1607e52bf060a0a5d257b0536969d7771995890e22cd08d72eef446417ef0292264db01e454f2ddd20280c329cf0148a9e2ccf4dd50747229b6ef06ab736b61aec7f2933f9ff6c79f52aca99a2c8ee9ccccfb950aec732eca76389fe4d300a4364ddda951cd18e55e8057ee1953fabf47746140db8076fd05c681d403792ee3a668e7d964fcd2a185f42e08cb778f954c1ee4531a68a0758d37fe73c09a0410898a64cf0abcb2fa41a206a8988201836ad63bb0b243c0de82e35b0f86477b5a9ef5d49afa44505cdf43d20b34dac8fad69efa23bd2953d9d1299a4371e08e31bb2f2258864cf78d09f6ff2349ee861d2a340ae5a13b3efbd7ee984960b7b79cf480631a71f6d9bd6eb33328313423c49413f4d1cb4aab670416e94d088b22486314ab90f2ab39f390170e0bbcf5aa8664b8a41f7e570174102c8434ae6539bf678a366f0fd420167f8072a1d62005d07761f1530a908d807d51fe6c84ef8c5cf61d9c2befb715514d60b3577f61bd76b7e07df7958fc8aadd5220abf8816109dc59cc83cc660ce7b5601" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "b2bcda3bcc48ac180c7afc06ac0aa1da03602d4188a4a3abf2c95252a0e5b061", - "proof": "065cba967f33c44b43c5386026bf1a37531bc4af1872975f32fb1d34639f8c37c803bf5787ef2605ec6e972a441827772bdb45f78c18e302e5402c6225c0b836c4dc4faea848e5d00096cd9e908de2ae6a8c84d4fad0c2d4ef4be4a390f6090b6ca79ff6c7cf8029c4ef10765c11b84d8a5f7d039057dbc618f53c2da9d7321d884442f1f7fc88adf4c7736aaff18c83b49018ddde682fb5b8cb2df745f70e0d550dbf9f311d5add90443aa3d70cc72443fa2dfec37560eef033ff62796e1b0cc030dfdc39495ab6a70982bd6ac4baf197a2607fbfe38b6c643d4243c718080ce6d65cd22e531c0bf097650baca93f7c3ddc723680ef80a17604b1953b75f65ad4ea9bfc725e6b9b93d9cfb03c164afea7b34129b94fcc92f104a557babcb37f9c4c8bca37dec517b21f9ea789dd4528f43362b285634d9ec38a03c3fe1372288805a64b8f3f06c185f9382e6591be4d602656b1b1ad720820791d5f0d43fc483268096b323931790ec5b2d382cbdc61f51bd444912a8f1f1abbdf3d3670aa67c8ed9e337ad9ec1ed1fcf4faefffd8b77b6770c4dc002a285d915bac55e75724d6943ece9718f79049adfa2dc6da2dc7dc1c0250c9680aa5c8bb007f769eda26064d9402dc4b739e5b26158f8f69e8b2c4a59d2442dbe1abb3f698cf006c4c78daa193bb57129e5a3a6ce21e55a337bc71b2b93d372220cc3faf07262c85fd79e455cdfad2d9f1bc4f290c43b1d7d9a531df7be9ef2022679d58823eb0e83d23b2e8e3eaec0d706cc06df5c3bcf0440317962a4c5e7f6f5185e86b830b83e41bf6dc3d9851b52c643668ca4aaf753a94350549deba39af3fa3cee94d41b1d91a9726e7482910a12d4921b9352ba289535e9a287b9824373c3f1d0b953a62ae06d10383d04af0358875dbc420c026e5642d717997166c8d4bb22cd232fc5c360a" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "c22b22f48ddd207c07774ae9d90971c5288183420a22eaa06687e7abb3346650", - "proof": "8656094d3a04450c5d5bb3df596f54a7a089105dea0624338c35f1ca0dc3f6653c3654bff6dd051fe9d2f7bc26f2390561589d0d7eecc46861495a2f39805a39acce78aca984d07396550d53d3a189f89d2767bb4708c7974a7a2a25190c6b6918f09a22cf0c5954ac4e1848fed281ea5689dc66dca6153a8483e2caa944fd238230540cc4d99a2ce3791c4176b4024191fbd78eb470e88201fa17418a4219088d0a50c48352f597754b910903ea76765fee491f1d4d4303da16207cec15230431214a4773f64490e013ca277813141acccb57ba0cc6411e70eafadaa096870fa0b34b1c868642c3ad70b04eab964cf8ec7a51aab1d88380d655e3f15b6af7304045f1375970f3bdf3319479a5d3977bcbe17315020f4ec6ed045f144f37117d86fe179f7562efbc878053c5f375b906202f379b6d049fd3ec8b53eae2db4832e820a3f6779440e5a49c067c724caa3cb00b0a3f441f7db573014a5fe680413c98546a0e8a4bee7b0db2503617e281c5bad0ebacc2c58343618c845349f74768a09dcb026044c4cc80d4dabcdc9dd4e29a6bcc42fe3f8a41abad00bc69d0983994fd953f0f4ab769ba10a70edb2f00a4f11e867aa5fee1755eeb921cd9111c08523462ab4655d0f2ff951518d3f525efc45a661495a763d288d81d5e04df8d43b2f8a907820540f8419c836beebf679acf27e457977b42966a750ff030ee601118f4fb3671fddf0b5816b7d0ab487b3b2fb1bc04f8ddad89f77f8490423add759c1e7c6a7b2535a0914e4d80303e4a094f7f18fd8511a8faff1403fc2eb1d578427b516bfeef96341c79464b2f206c1be4da884045ae464b6cc224c99824c7341f8459ff8ac0ae6448de0a82c8db2b6698d67310828d3823f370c4a7efbeb3016d7060806ccd72624196f4e13b3dd005aabd6e4791cf2feb17b8832882daef03" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ea1985ba1f5d64ea8c85390ca5e4a19a956a5e3a419b6d8e7c5e7ddc769d892f", - "proof": "0e00de1b86e00113f57fbf7f03b55317ccea68ff1b522a29b0eed0c83de3710fba24cf70cf687b7e66c9c55f3ceb55771f328e8d18a754c6de19f01bad5f0e20240fb1e4edea076e6f1af61a3c3bc0b1c136deeff7b8bd70988ec5288bbbdb378a1020fdd4db343b4a971a7222e916a0d7a6b2aeda8e87a52d9750216119e214362f6e4b6f38e817c362cbe21d8d1b0aeec23f75cc5aad0b1702021fbcd4f30142aa684c1fbbc57ba360c32c1c8752926d37f81d0a712326d9d4b29fc95b570081de1771489298d2516055efb65ac47a6679f20057dfe88b3db56bf460088908e8113114483bda02fb95ac15f88f7426f028a64f88fd0275cf03b3145319dc29f8bce124bec2216e4df840b9d69d2cb68d3834bf160d115c1a5950941d38382d3841757326069387dbac6e07dbfd3b9a7020ca0fbb594f64c43af43c2e3f5529f002171650ab011f7786e06593b992873f4fd715804c98c027fb4005b0d69b70caa58f3475c32d23548f54f7052681e3a77e598d1b5755b038c93fec5c3e967d680c67ad4cbac19954cdae957de2a69bdbeee570aeae2f720f7c2d91d3b2b92e30bbbd64032531786862befcd9bea79122362afdeba62c4ebcad5328ffbd2f3b80e0d9dadc3eec919094326d1f05cc1e50ef31904cc8ddb78dcdee0738d53266889e97066840e27d4ac641511db99420fa249a4ba22143afb5b5745677d4fa609c84e40540a9ebecff0cc63842c0c4187b2f598e816216d368cc2ea0b11cd737ee16f7b2886537b2e6f898de7ad9d1ac781f996a9eafedbbcf5336baf6e3424b3249222f2a199992435196ce7df57b0d3b33e4afdc01487e460bc1ce36b1427cb69be0672bef0b3e9b6964233ea9f29170bb66228f0a521d74d477fc8dc5850b5ade603ed1062003e7f179b1e3cee985c4d9b261c2f224a09133392b973b1d0b" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 34 - }, - "commitment": "18ae2f0e1f043e520ff4f8ae0751f0d51581da850026041d08b1c6d26aee295c", - "proof": "32f1856da39b7746f240aa5642dea715a31b45ad510c3638948c9269a521d332f69a4148d82d815daaeca8cb6b9acd0ac6b1e19de41ffc3c6db69e25c3025533cc070e1e223b0181acdf7eedbf98db31649141ad4c9b7e4b0746d6ee7601db38a6eface90fb24d5cb510aa2e0c1530e577bdeef619b429ee86ca277cece9df3f4af1b4313b93833d13d2bd67985c470ce9d3463ff1731b928344b8009988010f8f834f390329badf844f45110f8137ca867deb607c8e02017f88ab32008cea0495100cc363907c34131f33b519878577e8cdeee93f86472094a0c6746ed361069a0e54653bb9a79c17878ee02fe00bf2c254bab8bd5503db4a8d2be3eab336629493156deced7b2a80872d38249d7e654c06b45853273c14695d79191cfe7b158c2454bd75ea8a846815858c0bb8394cd94c314835506ad76b439c512183091552beab6edec3d5420e36cb113dad595576c025c36eb8f0ed46526029108c6f351e751e45b4c8d72ae6798d3a7b31ca624cfa20310721080427708fa01335b73d8c9749e01a03a96f0177de8b9077bb4dc92b32f877406bc5a441f526ac6ef93d0cb537c13e7fea2456bae87cc0eac43a23c1ccecba483417b973d3027d00a71d3e93406859670045de1af463449eafa49f1b9cf8979a5caadb9e94fd77f2275bfe0ed0ffe40a2e6486ebbb5880976086f2d73a1ead2da9a61b5684810b60137f423fe1640be9277422e73267a298c515f13df0b20633276aa6b3f3171cbef457581ae07e41d240c6bfe257b5be0b137a13c0a7aa6ec4b79c50ada5cf486c2e5db02a3974ee5e177aa8f743d7d06e5a6b3231d72dac070d59d14a7dd6c7401074993d96008d15a29881279d4a15aa916d23edb9a00da941f82898df665a92590ca6fc28e2b6ab28d2ce6f90c8d01b64a25f1008bb53bb000b73237b4c31147007" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "1a43bdb23db1149ac8048164b21e6bf7866332cdead1aa5f83870a0824ea3224", - "excess_sig": { - "public_nonce": "5ef892f1056abc164a42d980cf784586dfad9c414a627f096af3f142530c3e4b", - "signature": "3e7e3d9188f5dd91227d75a32eb14c28ee23c8559ec53c93ec71dd70b39dd500" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "2e1a0761c886ca336c025873cbb306eca31fe47d951654446bf2e60003f3a966", - "excess_sig": { - "public_nonce": "b2c33ca38766dddc88062ad72c595b0b8224492910c7d4371861337fd269b22d", - "signature": "67281685cc23ca33efe95e94714fdd06e03139a3bb14dd1f3ac1a2200645cb0f" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "785bf0771858fcaf5a88f7210e67262dcdfb4a29478b0294dbad3ef30ecd0b44", - "excess_sig": { - "public_nonce": "7cb3bb3e1e3f0367a37b60907e1b60e5dbba0bd6e6843a13a714548a21d2f704", - "signature": "700c092357a4e2205eeecfd15a98a235fc8f79bbe8eee94f55f6dc3138feb10f" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "8c98ae77c8fd76d1a921ff22e2ba4a9f32f5ef01a97ac248da1aa20fbf731b77", - "excess_sig": { - "public_nonce": "f0c26f27df0fbe623177cf01872025e6df026c677f438c2bb1b3167614a0740c", - "signature": "9d55ab1c55b03ecc95722596d1d425d0afe4ffb0137f3780173b89fcd1384104" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "924ac6d6302b3dc910335537c79cae5e0ab63e4a952b29311620f9e0822e205c", - "excess_sig": { - "public_nonce": "a64c21125c48cee118df4997c4b532b901b3232b6d30b12f0a6bca002650215e", - "signature": "e04bcad9d0abf1a60268547a6fcad3a2e68c0c0a3a1114e9b0f70eaefaaad305" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "1404c952fc0fd14218834de6a790cbe90203983e6878d19e70ba0d522eda9f32", - "excess_sig": { - "public_nonce": "6ea4f5db3235252c1abf9d245da8d7f35dbf41b0927b18cc9317e3ac64c7fe0a", - "signature": "1782330a829e7c1eea2617782f42aa291fe70d5b5543ce8eacae85d359679a03" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 34, - "prev_hash": "a04d058d22d1da6c1e8b043d11aba56624e0634d4d15274c35e0d51715cb95ad", - "timestamp": "2000-01-01T01:35:01Z", - "output_mr": "0bae13d04860734a71bc6ddb7f3cbe59b13df2f188f6ad6d37a8075cf73a0d5d", - "range_proof_mr": "7752d80d4408d7899dbf6d387150c41f2d71584774fc4dbaba6ccdbd447984d6", - "kernel_mr": "68bd066688709f889c14cb921f0c2ac5c5c60b8ee6a62d64d6e68804fe910300", - "total_kernel_offset": "d7f5ddf7441c6737f89a90e76450da841fb8cd964eee160fb12d9174cc0b540d", - "pow": { - "work": 34 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "32e0deca835dac1faf0b9543f82706dcd2beee1d322208a0d36d3cf434aadd2d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "3c9de4b403e4c6414217fb0469cb19bca14d81e76828e0e85448827f8f3d5952" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "9230e94b4f58ebcc691a46f043bc5f2d292b2647a927f2fdccdd550ac744a16e" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "a63c748ea34c75817e5ec603f2a3e200557256b4bf91b0c3cc977956c2fefc48" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "aa45ab2aef06070431a184b586b9f74e56924ecc926694c8296c0b74fc30ec4e" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "006f0bbeac62afe9285ce4a84db94be22fb45a2d0aa4365568692bcf68d3ef38", - "proof": "561f1b0fda7d090a9f5f89d215202e438e031cd7e0d0070d9e6d67d718392c2c1a6706f38672f2adc9db61e8274b128334345f7fb72f4cbaa0fa7abaafd6eb4470756f363f5f9a4fb7ce4b4387640ad82cee407620f9ae62720bcf4f5b24565732ff4fc4832e567d32e805ae40581fa26f0f7f7c1e564dd32343ace486ab8062b9d6f21b7ffdfa816fd1a3e69e24ae182d3f727714d0b8fd4bcbf415f7c995066d14254ca7c1cafc753a00e057cb6e2b345abe3371325b366f466da300d0cb0de5da9cee9965bb460e049167ed4d5bcf443f6b174163f50175fe25afe05863079e307d6d0f97bef8a78527cedb63bd2288a339a8be43e98bc3c1c71c3114fa5dc8677f9b82abab87ce87359473c9dd9cd2508fd7162ba71cf1c067551aa0805530dc4fd29eebe98a9b626eeaa8d018ed19de9b581ff075843fc327d89ed7e43a24f327480e674753dd9111c3a700c2cc6ad6a886a774afa8d8911abc6410b94bfc33aa8da8197e1bfe1468f405db754cca5eb83a69be6d990b89df0c99e83f744a327143bbb3b5ba5cff5dba4110b07caedf6a935ab1b33989aa45d62bc57744de4692a78d428c2f59875c7507adc040ab95176ff5e6080735aa864c5145c474e66da8b1a4c9fa9917b0c59376039713c6cdd4418beafb7451943f3032421752529bdba76d219295793222cac0b83bb993a6de891ae4647cb6942aef96b2bd2d00e510449ce547377c7cb5ac2204b0922251d784826917c410a0d27403d8373e9efafd1861124f73789e42b3026d59d217a5c7a0ce6e3688f3c6fa86218afd532c29662f27295fd924746c69270ba2267fd9f148d7b9db380469b77639694e44f237d3cbd436cbcbd35b3c11cae6acab8ef3388def08f23225e1fc6781f47807ccf8c47b1ed1b66b6d1ee5cb075be60a4b3fc4ab11da5a0a4ca9ed850377cb09" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "1cc31c71623b5cf69aaa4a811fcb596b32459010f183df11df146989ccf70308", - "proof": "5a64941049fd91e33ab22f64eecc67404bd12d1e90adeebc915146cc197c885566a593aa85c393a6873aae9a68b00651a45de9172e500f51839229f716d6fc4304b2bc025dbbf5841340e8bee3c0126cf53a39d40ccbc7dac8e91a481eeb3b2988390d7135bce72512171bc2ed44eab7472972d50c0cd13503992af115661b124e4e9ae2f07c296bc0ff6f2ab914d4c9ada837e4d5cf159c5c86cc95a6dfc2042efa2d8f5a54038f0350b1a86958197533ec00d9d231d370aa3c84f98b316e0eea47330736c5019516400c25e594cf9a6f489fd84cc1392e24c1c7cce55222064e361f3c63a92600b59028fdc16af6f8908d7e4cafbe73de7893e113b0f61e777c65f13bee3202c443c04c3f4083dd819eeb3d7baffe2e730830982660bc2309b8aa1788772515c4444b5f7d58be5c139caf3564439fd1a042cc0df2f089930af2857586fb0a113acea58c7965fdb0ad16a4d8069c46db8e2cecc5147edb6300887c84bd3985c094a6c28774bc09b76fd6746a2f3f96cb25f7ed2e635756cf3eb83fda5f756ec1208328ed938442df8fc6bc448eda301541f3297eec1bd07a08249620fcccc556c9a0c270e3af178f855b03ada9ebdbb4af8eab74dbc0422451720f04b915754917c3a4175510c4331d1204f5c8871179040a755b0e28d3a3166a8533665563f1ce0042d931f8e01a17df2aa7e51abd48662335b159e8407a09a8eadcd114c6577c3613f4e8d07162bade9850f846be2271e200eaf1914df27be636debe6b640de8f41381887170ad97bb29844b31a718ba6348de9f105aee19e87a5af467a6d5fc30db8d56dde56d75c0e19d9c20bff670e7c63d99a99c721467bd67cf1556ca6ddc4726d59e05093ae006a92c88d79feaabe066b7dbefeb0bce75f74b86a7c8a1d61f182b7a91853cb94e5f523ede43818160f9350db63208" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "362e60c4d9b80f9717a268e7aba08386efb4752167314b871b307d41e4730001", - "proof": "20cd28c30461edb1d31b8f5b1d473c463e3ee9443a1c008bc4827f7dd176ca3f2ea501d048f917432de1c35f91bf1f9e1ccd40824771c19cd35b3aebedf5736e54673d89a8f128b642188bd567c6dacabfcbe860d02c3ebac9a6f3cc73e4774540647e64e098d04f01f4250fec17bc8ff1ea8cb4fe0e28c8862f4b78e1c148661c3d66f9f855e03bd46226c13807fc2604def8658995a441e8f01fc08e4a1a07360bddc367c1a137838b3a6f01932fcb1ec09c821e4d5c51d7a5344036e3010b77ed88e1bdabeeab1571604bf88a3d54c6f04d707401548df54a88b3689b9f01aab44641eb066032ccc3ad1d12a88b4aeb9f8245b2fdf3d2363ce184d7b04f40bace0b560ca7754ce013532ec42ab1c93470a405983c0cfd7ab226e0f42872130c59f73654ac6c8eb763e489f1d2873eb4ff154893207b31f274eadfeae7bf60c4f2b90c8e71c01883e61b06b2021bc3eea5a218b6f830c5953b91c9fa248b0b62ace681e8cec55934008973418aab04f863d676e08f547e7a81ac4f3e62fb33f09e38467e68f4ef208d1e85b58c02d6fc7225b3ec4922c2a6c113deaa84a9193e86838e8368808cc51737da24a738a512c513f7c5f554377dbaa97da343b84b5ccc76546653d2e4cc8023cf8b2a61317884753b440e6b8291fe097180080156705f649879adf960e9e454900af0353de9891ecf5ad7a809d68a2ba2cf5fe149e015747d00427af79b6e95e41456f3e4e0bb9449e0916c191b3302b9a434485fc2db22367f2dbb3b17e7f5adaf9e93b609a9a0896a5809ee13479e30854af33a14338328b5ab1c946a07b8daba1d72cd6d5051f7907c981854b3f8fb535e7a46a369b115fe3539f8ea8ecdf901403f2de0dbf346d3da48543b277faa2debe40a232ec30fa4de4392853183eca70d4ffecc169c25c3ec724bedd3a9313e2bff0e" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "3a9b126682ed9b9924dfbc178eba66db5c0c6c51a0d6f7176a358a0ddf8b5d67", - "proof": "6cf64f384c4b0f77ac8c1c04e3a61ca1583451e6a427ba1e2fb07eff0f023253f84e6f8bbb396404979e18a8bfb9eebf238e494b8f878217fe19a5acf18d42261a33589d7715af6955d8b999ef0991a8acb2e112e37897ffe8d8cc2ca74f4c3f1c16d4fe5d1fcf63c652388293de00d7fb152fd97fd6a15f2e5163a545cbe42537204e46c8f8c5f1a48aa416ac675640d0942359cc93816b3425ddc67ed0f50334a610243680776f44d6939f50a4955d1453ea7c01874e1197156941d5a1060c450317ae3070adb17e03df2dfdadcdda344caac5bf3c1db829f66cb717602b0a1c2ac608248c64102d435357f92b3e73f0fd0e0fe079e620bca459eed6afc2542644c0b12d9089750f6817513bc0d3c64df7c565741fae99ef10cabcb59d592300fd89da8dd9168640bd366bfe32f287d90eb1728e77f5d579ae0f57e9b3c91c4c34154d44ece99d6a0c835cac5cbe85bc5e5dbbc40646860047894459df4411e6dcbfa343f5717d68ddd5d6b04bfed8de7cc01ce4e7ac7084426b3e677ae97ed88b86a9a7c2e696f0c38a04cd63be6ff69aa700861ebf78272e9478daa8721792ac5185b8141a55b6b4e81ddaaeb3b430b47522eb4c365e5e933949b3e8a5277c0e74cd47fd6b7e052b155ffe9dffbb9dcf6eec2f14fc25be9dc595883cca202aa2a4ea2c96f8fca8032f093ddd58cbe86c440885f346a8cd69851193cfd81a86a579d1fd0874047f424c2321074be9c81be59718ed311bbb0d440c24f32f4cf0157c597730c594d49b628952d3e7f56dacb2608615804c968358e4f283c2223848ec7a932aa6c574e603d61a27064146ebc8f2215ba1b8ede068d1c4822c3b3de1c4620154a366197f75ccef8ac56ac79b5048e9c093be06259677ec3a5d0b615ea2874af60ed05369ee3529b48bc406db03900384c308f92b8700b3513e0d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "46b84daa6a3223b1ab01073e448865212fb297fbf670815e1c93c10e0f41071c", - "proof": "7e5462018588fc6ca58691e771451d7acf7e5547c355546f09e7872ac41fd927b0104ccb6ca80ea6b6b7233d9bd43e5a5206ecd86617751908b500bd8ca9ae06d2767950ecfb15ff5d73ef406b683517163559de9c8ca3a61e8d137c640ce02b60ea86e2c854f148704c57040eea151de2b0c92f56f1d5f9540a9b8992b86b6320401f7ea9ff7787f0c245bd1ad98c7dbd89ee6364d6ae5c260d3d1bbc40e70ca8058abc869b441eb43dc86dd21e862e788ca542769c87e4441f2eefcb30b30ad493e4b14496b98a0229c5e56285c7cca9ff2853b6bd43f39d45fdc617ef490e421873a01e94a620b873901e525488fd862531c13f4f39bcbdc0728400d94711e62fff52ac40a20850cf4337ed4c9bba15ebc971f34e3781eb6a3606ef0b5c2e48f1837f4b761ac10013d3289aef526af1a379efaa37ba3078a24bccfc3d14616a43872d533158f4bb09239785211e33bccbdea6d73d54ffdf56d6a652be921cda2780fe8192eab0aa73101e23f7f4e42b37ad5f7a619d02c27090f23c32366f52a402f4898d81b693c5e574f800312af99d2557cb3e0bf238287d91f4821d3034007b2c69892610f8c6cbe07ade0a9688fdf7dafcb85457d1db516b016333136e8f90f584bd95f256dd0b92bd36eadedbe1fe1a7f942a801a1f504ed0423865365c5cc060a6867a623a644fc758f0d1cbd30d5472e946cc8927d8e1045460018e8e9fc16c0e2b669f7280b9c07fd3e0fe6914bae221b3a37b609d2e118e0c42bc92052178b6514ec8455344733ca0884d9425164d69107676da6209f3a66f45f4664c259f37deec93d1aad2fd4face86a4372cff2877f64a9f8d998a46fa36061d69676144f2776a224d4bc75cc0bd06a33e98be0415737b62f63b0dee9340472e15224aad08054b5e80e0395b72c0dc1cf57d6125c7247c1cd563293018a01" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "928fba870bfe76a3cbd3e28a2a8de3f9ddce82a8c90b341aef17ff3cc032c422", - "proof": "ca2ed0ff006040ff9f4b1ca064ea9b76bdd0f1efd9093e7cd9af6e0efa38ca7a5a18f5c050860bb81eaa9d13bb6f751634f792f9f00b5bae05464cf1a662fc13b64c1e72f04f752b1357a47ed2c46080d19855a2771a252b3254dc75a85f777ddc8cc0e8ea1d3b0925918bde4d09c746a83836c729e0e54b5274aff2968c4a6a2c28eb002bb234d239b38304e72f22f9ea7781386c49e9603d113ff2d2bce8078ee01581c489c44cc5c36aec5cf1e54ca094ad50c18ca3d74fe960f02cf92500436bdfc60afe42259fac6df402306684bef9c5c43586d03fd717b331e0393a08e84d1242a99582b451a01abe6e93bd8437aa5f1357a9deb6b46780861b7bfa3e6e4c94bbc068726c8dff583650e49774cac027043fdcac6ecb5007237fc14b21aa8d8e335c02b6831228995cc79429ae0242c532553e37a316f449ed9c54e260c2ee54395605a9e1c4889cc79893a8d14e55571b2cfbaff664d4a659df84fa71504245b122958491fdc1a2ab98d4aa657071f7cfdb0df72289b90f36574b84752a5e02c5dc61cf47c1701a95a30d4259a1b1e06675ae0115d864b36573ba14512e10dc7af10757aced433ab598cff25dc82934ecc732ef2ed9c40137919c35319ea96405c10956a4ff274053102c511a0e9becd0c8b61f817566d345cd7e2436ac0ee37e5623c8818c71ce42c3054c60ef0de3dd208f390d867dc2b21ec3a73fc6262cd82c75a7ee0a9d03e3b40e23e2a6d93070d56532c042b0b8b9ac05952484be83a2a0c715391cc1d38bdc6190c1f06a02ebbe96bb8e863c75c1f047ba10aede5757fd077373ece50ed9e912521fae6282bc8eae7863f003b738d9f0ef430d46927928af3bf4dd3c726ddba7ab769823202f6341b2b7b53ffe7be098730a3dc4d39f584e480887679907d283ea8de2a5dfb894c1afa31eef5ec9d270f908" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "a02aa44698855ebef7c8f19307630aead59b78bf9344eeafbf43501b56547b7f", - "proof": "3238f0fef24a8897ab1357e983d1d082c4b86fd9d2bcf0e3e36b3acac1bd6a719e4b65b5d44c180b8a3c4a2f381c4e0a0be838c2d3f1a649932ea60f7751245db275e9a427e3b16d11f5d19438bff6df290274ebc3bc0f9763ace82c557ede63becf82c2dcec6ba9b87f1eebbe6ff48fe19655b22ea5e9aca86d468d91da772c36d4138f2219e02e04a0beebd5c8601d2a5c5a5861fde235898eb2746b148d0607f4abffc0f5ec3edc23e44606d73afe910e8573866b1e06a33dd15037c849097bf29dea651f3491a86bdd51cde0b90d094ae440d5382faa0d91daa7b355fb082e781a192e07deb05371ec0b21d6e6268d1002904d8ae692caed5deda35b697dda946b227d3d0caddff2786683a6f7303c57cc5105da4e35a1754cd9e41c0c1c12e94775ac2a6430f22fdd081e97eebab138eca6ac7c0e2ee3bd61ea3dc4fd3d7c594a13011949d324831156c16c124688785d8ef0e6ab51166e0fb39062253c9493e14bd2d12d764a3c4a7932c12ef3dab47933d14fd15ac69c4a2c9d81aa7512ec0e6a759ba69715a46d3dcf3ab4d9fe456df013c6ef62eeed3207f621420b1cbaccc872cd384495bac66eadf2587749030adcb0a3ffa96baaadc6f2289c426a2ed6d0c286b8d964c43e871b51a81aa5efb097248c030c9cd6cb45d104e915e6f5c45e6aa3022e2cc6083dd03d9b063b95f99ea3fb4747c0b34e82564740288453173141fc322af0c27a3a9b6eca58a69b4532654417c986bd7925e4dffe67421a6ed01cc59312d7b2017109e2ac4687c8f1e0642e7de33adb99155219596f9c9460d36825c29ef56bd37d35a74aa595fcdd55a58a364627acfde0ca6e666fee0f223ecc3e9d95c5442fae61fbbf9df1a6c0d3178a2d2b65adc7178208e9030c4fe77aa967eb3a1a2c5cd3225e9346a963c87eec2549f3afed11f18c04150f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "be97bde87868cad426711a321925932fcf18cd11f1c37299c8a54613969b2d40", - "proof": "7a5af881ad59dfe26587120f4021ad41f53f87d8d2488dc869610a96c597a97828cfaf35fbb37323d88764774eb6d7600910ec9f9867ff7c34c44f14fe27b774d02ad1096869a0f4ff94c65137345dfacc18e0686044f5c5570b9af43ef486375c3506f09c55be520bae467bb02e72fdbb5081f567d64b21c30672290fddb8214f33323093b7734e8ea9ce834e1ae092520ce522656adab9de5518c821eb6d0171290ef09dfe385f5d78acaab133ae0fc82c2fc4c06548c318518f634f6a0008d069c9a79a3ada20681931af40ac8c9b14ee84c45141157afe56db22620fdb075e1bef0f48ff6b0fa2c8fb9db2933ee65eaac25608e03afce9764568bdb95c7cd41509c8b46719b308ebb3ec67e4da2350cf17fe490814feca44bbb46c6dc825f847c5d1f9a74f13b4c9c578780a0e901c7b2a4ecbdc921c1c0d5f9eb1d68b23445fcba33db4edcb36dcf27a126370cd2c63c062d2ab256901d27f4ca7301101f4aa45dd1e027de4bce766254553361d402e70eb6c363d13fe8d9a5959fabf709cbc9c57796aff6f9c0a2ccaa8d4280a929937229cf0cf094ddd25e1a45ab060e8a661c32f2be389a91c18858feefc29e47bb291d634f015aaee86b8128cf5281c89f03f5af4f0785ba4e0124f993536b3e26dc76dd7230324a983b64cacee0d88e126f35073e22b53af9a86891764682fe1ebee5d0747697e07bd08b3f7104402c2c6fe8b1cd492b990080496a980ba6f1fd04169352dbf7c01c858caa4683ae451585014d7c7076b4ebece35592a5f2d1968438f9959727ffe619dd65acf47446d566307443b0bdcfdd212492933b2097ce5be0ef18008ceaa8108ec08fc5158d609b4bb0d500a1cc4bead8e95d4cff8ae0c9caf1bee005563ac6a2417870f09b7fc843a71095ad9ea44deb022802f7a10d093344a0da62a7138821aa16906" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "cc826908f9108f02f83955a5c08ff75567a1b3cfed6e8cc0e62c04925807a87b", - "proof": "46d7f1d8c8da8149a788844acccf3af510c9d2277458d95834c309b9ca5304069434b94f721e009b7a56fff470488db1c88dd8f0e411a20ef5536b44e8e0ec3b36ab8dcd79b9c8c209d53891587ffabcbda4f91ec1a1b0f429bbbce036235151dc5927c920e9a4fdd961819607863d0083825a4a69b6714b84bb71805f005a1b5210e482b0d948d4f2c7374d821ab6f008319dcd690779cfdfc3061c600cc807cac4151ffad013baffdf204861a75100f34cd6cea3b061fce4fa0de1fc85fd00e031bf318699bc6f2f440f9bf4067c04fb6dd68d2db4d231bb80daceb6da6b06c0667011c779588df95ea865151524864b9537b278cfb4fe871aea4bc67df706b432c61e31a0974dd0a8f539558e3c213041d81c42a1c072225744f523b24368f42eda95398c8b67ac94326932c7b772c9e78ccaa6fcdf77083f16e07b4a4a1bb6f86f5051bdd55c84253d49cab9b42869880050c88d513bb2fd9a08f7c57a209c6c0b64d12c9f0b34de02cda9b592cec4a3bd3366e6f750eb6655806c0f363bd0a1ef62fa0484093ef17eed136302bbb538f53167f0ef8fc034e0dc16974f6f80182e4b047060233dd7788734cbd73198ba5db7e45a8e7b28d9bceb2bf4603ca6d323901b1faa79d72872ebe54acadcb55f3691d9f64b94f530d2370b282179e0430de05f9ef291f8e78322696599c9b8dcd4470272439970da69a2c4cee129d426c2274106379c681d4afe997b3dfa9eba3f37fb9b29b7625a85fd4c65790fcc2860bc45dcdd9b44206c5230ef58e61cd5c5a22b2d7cc375154faf4611dc052caa142e3667356496375baf060c545a646961d766a8dbaa0f9dad9d179afc41f286711efa836ae2371177a241cb55e4723a53ed7fa988b2dc29a3955f7eeb0b735aa2f362ae53e660f2ae8decc22f938d3b6c4141720df5e32549f537831703" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "fadf1889108c9c69d4687169ea9cc9a61f8f7bc9931ff37ec1456b97e82d7a09", - "proof": "045abdd4b72b10a689366c7acecd969d1ce068613065605c097a1acd07afe13a92c08f0da0cea570f3411fa0106bd2225a79ee5a34d4f4a2e71cb43b81cbcb18faed8009e930325b9d184c0c186d68a0585d937ca403a36bcc53d76dbfe46c215a6c98c7102753866cc1b20f1f809e6ce98aeb93c2aa2b30558f70833d5391108244551d32306854c169ada0ed49094db8c457d6f131b66298d6b7b23e702203b7a99f3b6c75464f0c04a3450c4c52b548d9a173d6154ef47ea2ced168dd14007229beb5581c50b3fc006b017ec36771722686972a07f5ee4433d4823b36ef016aa90e56375a2afaf9a4c8dc73ce275066abbda1d8b2088bdcc3169bf828543042b774e29eec3d6f005f7b7f7d816913c155c01d7d80bbd45a0ccbad2ed7832fb4fc5750cebf2eb2fefb339a11f373fff9dd62a6214601deac5ad1622d6d0c444068f4c23b219063ac0f7b5812656a81685d6518a4f9bc7e6d6deb4b839ecf22d42e72eeded37a27dd27156da725db0eefc963f9852bbbc531b05c04c8688d05d40f12d3c092f808f4cb99e79cc338b1a6340f13c4beeb10ad25db98bac241376e17b848542389fd38d91833b80d32df058d1cbd9c45a21bc7963231a4a389103211a6e79a64878d4c4b5e45f0a988ffd8c551a98d44753bbbf36dd84d968f741ae9e77d2e3f558e7081ba4422caa8aba55a2f6132c53c68d95108e8a8e0ca30be85ffb5753f4758bd32277d9ac99814cdf63f231df38ebfddb59910420e9e035cb5dddfee31e4350e27858f2b29b34b248c6f05d1d40174f568efa21c63b23566bbe895569c6c4cc1465cee52275904a4dfe0aed4893f243e02d7346f96cd2b71c9d18f7172c128208b0d8eb54784cf945497856113c365f1daabef2075be0ef4a52dc219a9032b7bfa8365434301cb28f2bde51152b2b216341e7ab0be6409" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 35 - }, - "commitment": "587b7b847f6edddb3fbeaa3cdd7b1d3227af62da6b3f3f0194692dca3a62db09", - "proof": "08636744e6a57b016b0180873133d388d8c23186dad1ab32f0d21e9c94861b46da9d3456fdfa6f455592dda953d260df822251e223042c1147e9b4ac4dca947c06c5eb9bfcc7f6db5926fe939d9ccb25b93ec8cbcd79909cf465f6fa12d12145aebd869369a7c25628fedb0bdfce621ba84f27edf35a55797cdf1cde84a3305cb11527cbdaddb390a9c6fe0fb88afa2ca746dca860b5eaf66701a5c4d7f8e10459881507dcaf670e473feb993a0f41a2fa5f26f1467472dd1fbece15325e7302a48c00040b5896386017d7aae8ed06d992bd66a19008576d9c168e8cced41204eccea61742b4b204c365042e0a0ad9e688b7e3c1d9ea60da01d67e10cdc489412857fb46b3ca7991b2c8916f304b2ec911f401c87608822ec2a6e05e6a24043b5a61914d79b82b99888aa904fcb74add669c27cefedfd540df4ab8a97512bf0f4a17a2059499c4b9bde9c02849f65612b69eafe89e30c90eec26f9bdb01d972f946d88633e92e2ea3a4e1e92db437723ac03234cf020fe5198baa31f0a70b600b8982e3830b02435ca860c9e3e995a07f850e9ad5346b6e4b509c2c7778b7378bcbca41958778b3cbb8d51c6341641d43a8e834bb3c7eacbdd2cb3af450eb056f29e7dd5ab2d8c366bd1e12eb7f9055ad356ba5354e6484dcb6940e8b265d2381cc988235d071a046c67b5ea18eb447f39485fd0fa9c9e30f8d6e210eb2d2403a4fb1bac0c4762c27e7157254c0ac7bb50750cfa41909d8105def305869d8a61605576c70564278c374508505f008eda0fe7d4dda072ff76e19c145e89b45660048427762ef4740c7e8aae31e4e85ed7f070553b92fa9bf0cf545a12302fb64e7360352d9121257480b4c0e0d292a4c7b19b2f67b1885d163586907614550a0b4744003d9d5fbcec9d126c969ecd18baefe750fac3d8aa2806f5870f970c830d" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "04820d1b9d0f5494bf79f0c52f422dae6ba164eaea328a79d61a6ee6040c7433", - "excess_sig": { - "public_nonce": "58d790b8bd61320b1ad2604aa1ffa609462e5617f843b627bd9569aa7d51bb07", - "signature": "60cd9dcfdea4a6e61be7174d84153aa7cd269f8f14ab3b33619b7781bf822c08" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "68bb2b9c1e4ecf7826cbc239486149138afed618c0270741bfbda7a682f94258", - "excess_sig": { - "public_nonce": "aa1d2fcee5a11b790e88af056f91effa5e0ddf14e5b2ebda0852fce048e8d258", - "signature": "db07b6f072649fa06e50c62bb3409b07f7dce2033efa51a2bf0d9ccdb0297701" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "a20de0e9900ec6e35e700986f2bddec82f9406357073275d6d46426dd5abdb78", - "excess_sig": { - "public_nonce": "98995a96ac0c1d81915eb28212b198a50f686bbd6bae8112d4277f0a2cfe265e", - "signature": "10fa3d6440da355b7a7a13f7a0c7753bf3a5814d67d672e3e75b0db29d311901" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "f4d8a66eb783346332a0c6aac938a4ba81421da2cfa200de36f202c29274cd3e", - "excess_sig": { - "public_nonce": "ba40720f4265dcd5951a12aa974516906dc90bc850f37a092f4df71b0200a356", - "signature": "7acfa4bfac2b7905ff6ad4d364eae6da726c6c35d2604e6d3fc007ce0500d30b" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "fa2ab7544a8cb68dcd6fbb7360153a28532ec650de0816559a1e712aadf3865d", - "excess_sig": { - "public_nonce": "58f70f289a02cfd0781c97c677110f69583e9b4adb5ad60a2281df8c411b1e69", - "signature": "9e817a732365bf1e41c557a9e6e1eea803be8177f58fc5c5d39dc66aa31c9e03" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "c4f88bc7ca7eb8fcd7b766847081532be06e933eadd572866fe5a98955faeb42", - "excess_sig": { - "public_nonce": "c0fe4cdca6288677f05eef2be04964b280a99a2b8870ded36a1789090186494c", - "signature": "44c1f298ecabac5c4b41c805e4f741f8d57c8784df0fc8355a460549b225aa02" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 35, - "prev_hash": "eb03fc6dc2c32622ea92dd43519e7ab001740bbf307baff1db54684b8ef3fa10", - "timestamp": "2000-01-01T01:36:01Z", - "output_mr": "3e2e755d4c683b06262d3aa456a4bdcfe1864391da56bf69cb851902cec875cc", - "range_proof_mr": "29ed938166e36a189aa074fcf6c58173109b0d0371b0d1a8381db8896407002d", - "kernel_mr": "f75b15d3ad371552958309f70cbbcfd2fb5d294ffde1ac5ce9d73ea14ce93725", - "total_kernel_offset": "ba6d4d14d0e3698db36c13d017a70b6ff6c43316f28534962df12ec6f4336102", - "pow": { - "work": 35 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "1cc31c71623b5cf69aaa4a811fcb596b32459010f183df11df146989ccf70308" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "362e60c4d9b80f9717a268e7aba08386efb4752167314b871b307d41e4730001" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "46b84daa6a3223b1ab01073e448865212fb297fbf670815e1c93c10e0f41071c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "cc826908f9108f02f83955a5c08ff75567a1b3cfed6e8cc0e62c04925807a87b" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 18 - }, - "commitment": "3480e58d181643d932004bf4722f4f8ecca015b2ab1239b521655a49b225344a" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "183f65f245fd66fcbcabeb47fef8f4395700a1d482ab816ebdf2f1265adc3803", - "proof": "128226467e8c8b94656153c0ce368a50580330c131e13b106d5140a3a6fba0505459ec68ad4887084006cf35e5c86ef6693f0aa007deb2af734f6b841600cd628c091d43c4f1259791e02cd7fbd573844c35b453ddb5f0b7e0000c967556dc49fe2017d17af88481a4a3a2d4bf9c88c308e5f2376b512d19439e38fc7ec81019f195aaf12477b52e4bb524522bd81ad7c3440497d58e0aaa075de3026c9876087b2fdd17b34058ffec01b2d0d8f069deffbe701943a3e769fb89dcc9885bde0eb84b140981603509656eeab50866468edfe4e340ab03f99456dcb556d1c95e060ec651abca51554f88358efd378d8aee262d076b988394f8cf10b794342b9545845d6c788fce6e37ca3d9e58cdb9c55231d2abb11d3b9a7813c38eeb7d2f103bca27ef0fd68efab90c51f065a06dc541767b55be3dc8e2d3dac50c090c4ca25150ec7f6ac2487ad696101336b5b0be5cda183a4945f62feab6e81af3c615d567e44e3537609277058cbf506a6afe3788632e54ea6b7e532012825c420465a31b064465401d803756c59c9cc08c92f39a329fed6c0096524c5c1ff01988b8c801ec98c31ec00dcd8918097e022b779ee612a312ed843066e04aa25df7298ccb3f48e6f9530c477f00c1e5f10b9e47544b1d90366ede65cdd7c448cd4c6e1d703a5a9b1bff64241a054607da43316aa1b98c5ff95f6a5555e770bcaca2803374317a65b5abc4e417cc3a411dc914d4918b8d36e4de72125ada43b302579190f437c0eb807b7855800d8d755e10e28a723068cea0344392d9eb598ecf34c246064eecdfaa5142d0564531bf9d89cca29ac9c4a6da4640730546163f7a0bb1a8312022928c9a6dcbe879e46867530d1703919f0174ce0016975ccb5126649913a7086f9f8c9b54db121a98444ed93b9572d2f4b5f44df3788e4400ed16fcd26c0e0c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "1cce846a6fccc2a5fa42ec3aca40f8591571857d8cb7f683c0f7f14b7df1b733", - "proof": "4add85952e66cad0f31274e352e4e477d446c2be58ad7063d5af1081210e9a0a507dfe39f84a6ff9c4a6f30ba5f56778a88d63b9fcf0974ed43aabfdc29fc61e46c3da7984bb406c88654ad1639f611f6d12b75556ca45978a07fc27812b9916d4f83f19a7fd027c201fe46b191d07357dcf654652cbe3bd370fd4cae7d54e3b862895d36aa90fc551af3bff20a0c1eb131e50f60ce9e0cc18f42a37451f720aabba3676518b4817f21c514db563a7eb5028c8d0e55ae5825457997a27f8110bfbcd433fb3700f058391060f424aed06782ebbdc73e124bd727649cd18200c0584f8bb6b84a173970028e4d7606dcc7d09f753e081188dcfd84e9b8317dfcc63c8d6c4c1148904132c30842d29123289fa29149a87e43d7438dcfa8cab58915a8ac165f46df8c1f3beccadaac71b736fb3e96d5b4c4a58da977f9cd5199ed7232e4888e6eb949523555668fa613be327148a2e124dcde224dd508178bdbfc033b6548bb265baaa72866e275b9a36ea64ddcaa68bf5f19f1d154ef13c932963216628e5ef5eb0d352c927f228a4100ea953ebe590debc27dd8767426a151c7611145284f7f516059692292ab009affb902e9dae3d06837533b50db9f7c2a59813080859cd216f5baf6261ca6c08b64732c7f49fa72b1cfad3aa88b11a8bbe99021a25f3953317e09fd0dfdba8ac236a3e525465d785ddb3388df4eac98a6c84746e801d2ef380904c86ff0a0f3f595a8468d6bce46f40063754f0366725c3794b661d4f7a4f66084eaceef3b67f382c2542fd3f35dd2c0f06bb54b0277c5c9d66128c10a0e5cee9c0868328d70351072b5a4053caec778035d0b0ead2c4eb903a19ddd21942d5c0c36e38a26bf3a8435d709bb5bdc83b6841b6f745528f9be10a7754992311b32f8781fd245d14c2831c15d5d19afe546926ad1d3d1896792606" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "766570c960f413042a287cefd3bb24615c26aff743832198d7e800ad7a48b64d", - "proof": "a0d6d569198b66803444939f99e567447d4cf8a6d8c87e278a17226c1356805126bf0b29589534b18655b5c2c2d0dae18a625d0af27fdbf716b5e450f05fb21266fb6ced71e23ad33b98dc592879edad9b29a4ccf10653e804d11b946cd2ab303c751fb0d388a1ac92555009b859c4b4afc70e7a9ae97febfe41594f7dcfc901c6dd11e051a4182dc81f2478319e3e9f57d61971fd931e84e95ea2be11a23d0f38d0ffca0f4516dec83eece819b493f3c97ea1437e75e32cda31028031cc39098703b7452eac7bf608ef9c8bb2c232f75abe4a76e5466a2f999d9bdf5b194d0b7ca471d8ac0d670138a7d0d2a20846ea3e9dda9ca8606505416e493b0fdde309986fc2aba171e4635f06d74423ee4224c7ac8993f4c7c5aaccbbfe25a0056630301ff5b9e3f714c137bff35b1d0a17ae4af7a56b67088f2a0decd3ab6a0f1712be12fb980c06ca5d7f93a7ccd5b44c6e494fb5d059ceb6d427ed99a58645be1856967f9caafe5fd573c3d5cedb1e88a677ce5bae079ff46ff0dbf2149dfa6f3102ac0ef89ad76a811fb170fa6f2d72f57456d595a17e5734df5709d43df3a24d04ea8aac6dfbd654bc749bc995e5da4aa09f85164b55714bed6f20122ec4f6611a67c6085f2a9aa18852e95602cdfee71815cdb2b90f17a5c3bb25a8acca6412ae274581f83b5870bbcddf0f8babc2f8a9acd6cc6fb5a5e078f8e87ca64b20610a05c3ca31dc10f4446f845ae5c4fa5153975c1e1defb0811c0f748a3a8acd6abae861591340845e291b24f41286e9d8fd26dfe7ee6c0b6e00028adf728de3586c99c3ba9920e88bf4520137f56bdb3561da15c7a08a8f82965d6672fe773119d5918189bbdfd28bcb1bfb972ec7bdf2ce72c8ca6e3a9990aa0dae6e9f77590e3a8eed25209cf14bc91b0d2c6b66d200fa4ad0171131400c07f56915305db60c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "82318deb45e5f9191764b6ebfcdecab186dc7a9846bf6917e3817db22b317e02", - "proof": "2aa1d4e2c442ac8d60ac73d948b66d15f60b091bd995c81d2f8424409d58c745fa964ac989c89ed095c1d3a5b6ffd58cd111221fe2d9b780ebd746ee777dd718249775d0565a79e1142b970570daba11baf9c682d529753da30bff5118ca1e79462fc4655fdc12a6796af9f62f8a24dac98051c91ed0629da944706ff3213b41b621c2a21532e10548fddab262f8c72305fef896b552fdb29ad71196b135900c2f6714742f26867983ac288d231a7c7795d3733af940e2c3e8c8b99eab02c00a89f39301aa708151adbfbd974a8119a0cc318183c455d1d9c6d875a10f5da40ea81456570f6732f76b7b62fc7b984b710726166667f432782d95efcb32ee1668bc39f92456658c3e196e77ab187760349aff2de645e2c02ffe0adb1baa8ecd36de4f64977adca4bf5406135c2ccf74de89d09a4a32ca695c67c260f3e92f866e4c1f1701be88a2f0cc6c18af2f245e225f427c8951089279cfd230850dd583009ed9950b68df5df3b2e3d7b82bac6dc3533acae90ff59b698b3db088b72d8f1feaa204aa5f99c8a0e24c8976188aeac6008cc462d4fa214cdc37d57a8c34b0201ad58cda4cfd3adc1559a4dee2e5a2ffde5b0f6d2545184804d52819e6d610008e49aa218678b27599ae34fe65d751d5f332b68da382753b9e8449c6e0e0d02046301dbec38e94adeb3eeb0a8d37887a60e6561cd7f63bf8f27f5f1cd428ed50b640d58568cb47c8a8c445557c6d6857f102afde63c44926684e1ae53233f74b52b136e7ae0dc31d94d2f1452bb73bf62756bf0728630d12c3d7d553997cc1345a77d18c740133569e859b08f362db555cd64a0c50784f1f037400fe04d66d082c2c928632332bc642790dab77560d2e8c7b5ffddccfe0e8c16a7bb371aab803354f0e2f2f6454e9e44767f32caa7cb439aec5fb5e2adac8a24467147502d004" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "8cba281eb36b0679fc43837b6506b8a767a24ca15b8d182b0a43173f3e0e3a37", - "proof": "8231285d6b2d5290cfaa2e09df5136289067a0b72db916068e24221900b0fe6faedcfb1fb1c2a95e04729f4af7f026627a2c01349f93e143253d259198d4a82ca8836ce6b7f66a4417ec97e971649fc487b219fc14d21266a1846f7ab0749d723842c0c16b1099f269f8569db69e3f4556a36536da07c6a2a6a49cf2c581e0100f92e349210fa43067b63a246a20f6da1f462ae2f00548d7e18de8d90fd5df08d2f028cbb1259f628fe3bfc1ae1965be73d51b65e0a8e854ce0bf6704d57e80fd8cee7144010787f7cea73e8d96b5c8ec47ee5f43c95df4a8860a0b586173101125623b8a7debaf89725b8db570aaa57087a75b3a5c0452328282fe5a5b53d49c012ed46384920bfd54506ed0dd2e60ff0c7933cd73d2db921b5d0698a3a107faa507898d2e0a392a4dd828b47a6747b9ae4fbc6a01c4c1f33f48304c5b4101386926c1b1d13223531c922b23008d9a26c65c9ec8a22f20980b4faecc573d81d1427f43bcd139e0783c62015b01097260d54b62e6ab9f07d4ce78c2b6c8c942ffaea43a9c6e03d106d96c730f753a06a9b36499a9f4b5c67b927f7b80fbb9c43c63383f23c80c2fd31bffddca4f667f7da800c866af6639f9fb29d49357f9258725fe27fbdd08281056c4f8607f42c2071632a7a895d0f4bd78ee6bddc48534a4a3da2c55e30e04494adb3ea44d3498b6671945f1d31a57eb2f399305093bf397eaf7e83655489e8665c0dc8033c9318eb1fcb1f625ed847b33f9e66f5129f335069c5c6d168054813c44dc29054596ccebd2a1258b12c4564e3947ce5426521943509cbf472fe763857a76f9403ff0dee0d5e7e7fa2f4c86051de06e9d14316ee38fb23be9610369b0715f6e1574b0050975b2c9f504ab27057ad77e9235f042d52b26e95d4836f06e23b7a2681604cd491710bbaa3a953cbf7d0e2ef15560c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "a00e3b5c42840d6988c82cf8b2a24fb29b3584a86256619eb1b904fe31feed48", - "proof": "58404d9620e1fdb63bd85c1f291963c6aaaccd4cdb763756a21126c632bf1173f0fe5306909de479b776aab91e909d36f408a0e5877f4a637c0708f408c3c94c6272f2989ca026ed1af67f7ddc1b9bd84930a58aa339b9530c2954bff0ab823c48ad73a669c94dff4ab13d3e8887beb0dd668e7ee7b9fb6944deb71764690633f1e7a21d3332824a771ef7bee0f4990c06c6e621dd791af2f0020e4c5837510eec813691616b079e2d531ceccab052be4cd7193bfb612259c3c9704f42840e05c5a57204f8cba87dbd6bcf6d05adaa7e18ce8309c49e8246a44f92918cfd1808c0158dff6c4791d37bf8dfb0ac31c0bd2912af5ae299d6ce46f02dd5bf1ade151a4908f96f3ce468e0d39215476062b08bbc6fe2d1664ce6ac243b4f9d10aa47fc3634a5e14d94e55627bbc0af8e0f14dbf8c309c53cfa355ffaff4cba9ce96bd676ce210f2b2a777e31cee00eae87f9b98f8dc7cee5bbf5f54d667b28804319e6406983915fd96023d0910e777cdec4f883a34313df50af0ef4e7d85d63803258dc96654fe25132505d33273bef4a571232c32e30eca00035f1bdb1b154c2406e9a54a12ea6dc20d693b47d42d9b23465f53ea1e672228a87587b0d789e465212eb75a2ee0a346311ad0794b3c70a8a19885c7df8bda75918ac49a3cfcd4244aabe0921df11ed89a974b08400845a49b37981bfca417457b120c0128b2b0e475ad3c77c26d86c4ec851804b7a5f8b2869f62854db1580af796583007843891764ba1986c5f410b45a825eaf67cbb8a39189e2572eec05e29de45ea2add4dd42a043d7d071fa493f86f29f7f109ecf7ce34fc38d449d650fbf787c126541a50732d20e6755e8a40c609a663a245ee865165fee0659019a463e8e0ff699b1840ef3ed1f194087e2a61a0ffeb972f488e7dfa45da399382e6c527a23c5279eaf0c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "d20e0d4253183d11d0dbf014456070c4d09d5079bfbd9260edd83237a7a9e04d", - "proof": "fc59d99977538f0f04341326af23eb34c64a09e0e31df518860acb2bcfa94b578239a8eee6203a4db41f645ee25991441335fab51216147cfabb23e0fbe5e3409e141d3ca24799e0c19d54dc496d389895212520b7b3cebba92869f953bf217afc1d98a85835aaa8ac644acccfa19e1a680a80d429202aeef4fcb26438e4375ba6cbc5243a3735d3123fb5765491b96d7ccde4488006bcb56defed1c7bc06b034ec5d69d00eaa8547d6a0b2f29931161d5a4c1fcdc598819cdf63213a7d09f0c25cf731f282edb3246be2620706a730c85a583a1a66fae872365ecb4b9ac6a057e002baedd19cd6b1b744a44ad38ef41a93f32d111d7cde10d9b706ae73a9038f200b2d6893d5da6bb2f229599df2205ec30cc03c399a75378af5b426802211628961ed3a9bf77d6230b225b5b9645276c938d52f85303fe70ecaa7d97571b58dc7090bb3cbd3c596171b10cf14e6078b19575f8c300e36f0f5137d22f5ed019b088a69bf24464515233b24c091f3021f28de44a4c97e1b56a808781cc064a28402e17b7cc249a07b8e6a73058caf3f04f025c1100b1eeca6ce02bd9de68265850e387545f597fa1f4b2496eb4e84435fd15cc09a1955bc337d2bf8dabb0e826a01fca836338a8eeeb2e672d3c2eed9f8a88e9c8b313c00fcd2d51f3bf1c60787ae953a66def53d0cd33e8851c0e1905774f5bd633ddd0f71c1a2b5aceeb1a548e6034824986b19c75924cc52987463ee7f94ffa14574a411c43d5aaea5b403bc6476bb69ea1a3c537405d16fc5825745a7153f7c8562cb1b145b0f8a5fb2025ead83f6ef1cc526bbdc7d63a30368abccb12f7951bbb602bd67ecd59b468af2ddb0cff3748e8b2f3fc45a22eae0f6fba821d0cb180ed20672953227c07f0c40fe935740ae4e18da1054585b6e1a55676f265c1355e2b2a8d2b5b5deebbd96004" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "e69f74ecb9b2dd2b22599b1b5af49b2b1bb88ce5e0b931933a94d3502632043f", - "proof": "2a3f574b0c3b8cdd89baff4cc93976b2e42f7d889f25941bd720fe25acd27c403801483e5b9ee2df54f15e75ef613aa2ce16076d9cd8830d339b77b520ad54319e4301b57cf06be249fc76778ebc6a16c423e672a57f27150878a8fcd56d98353e26e29f71d01186a9ab8d2b1cf258aa311739429cd0ba21c9e9dc684327c34be673efb8c2c082bdaf2d86a387b4200a91fbdc1b2719a0512df6f0c30b7e460f3fb19dd6fda95d5270d6ee6155631173b99c0ca65019838643b75bbdc53d5009cbd4d5119da755799d97d916c19cafd65146f73eb28f55f9b5eea222f0d55507e6ebaa7f7e911d41a929556d5202b68f5554f57f29885ef36505cf82fd4fbc23de6b05274db2fb999c02cc4ee124aebc099514b3aaaa69b83191f1a942a0927af02a53e4164567a832656c440964c33b4c0a64d01ddd51b92409c9f6c5b63f73a80992d730e8a26aa8ffc407eb7920a779c88526cfc949ae66a883afcf72b37ff6cfc85b5c58f7deddfea07108ed7391f75654cc59c583b470268dae7cc5796df214207739bb2961f1e50fb78578338b1589f3b7cbde807552a38225d56c5a43ceb3a5401452b7d64c6a29b26b08392d5dca73bf05749e8271d7967e267e57475a5d5f7e78137a00420141031386629f433ba2f1b69cbb4911aef66d67f2eb565edcbf7172fb6445db985bf9b86299cf739638d6a23d2338e4f7552694591d54a29258a0678a79e0ff2877d310be8e52902cd38b4e50299ea9bd6207bb3033653e5bce0e959caaeb53eac189d21c1a934b755999602686d08bfec0edcde4fd1168e051c907bad77dc6e6f68189bae260f52927ece558e85dfa735cb998539844f717c42398c31c9221a38e7082bc3f65c276e5388ee1376677eefe73a184a20ded468f9a22646006ca33e5f5efdb7ef13581413b09343095b755567c8c7ab503" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ecc722aa1897f5ec4f64cd4f691cda8008f483147b25c650fc266fa4e0dbde4d", - "proof": "7a6a4806b8f7d0175cb7ab34f660fd108cbc38fb612efd19645a2865be6bba56d248a37135fa7364025fed0ee70795d1362103db2c65b56f2be39f6b911b7148b2ad403af720256c8b00a5f49e7ce1458d439972d751742ba17de771cc6c991216ebb60a58115fdded3831507a5bdaddbcbb9ce38ef2168641d815d5cc64007ad1f2f396eec2e49afd4f4f50041941b236892127ae42c01f4fa8531838717f059b6e2f479313737e6a0cf185f4bcf51b6626499b66702aade3bc8085645399030ca34fbaf314ccad26820bd996018b65c0719d814a900bbb7957fc4c3222af0786d0a852a5f72a70ce9cea06c96a06835af92b11c467ee37cbc5912b5b93487e7c1fd582777ec85b27f53c557cfb41bf1a5a1e7800807a33f3ce0e019ad43a709c6908cc02c0162e38644d8a9cd55af441c97dcd993f9a6d43bfe1e4c581735906955d92477d47137cfa13860c005070646174adcd981367263cccd703a8427ffc5dc508b273ba148d04d6a50a8fab7e2ea43511abf010e5452176336463f6063a3ea2e8b117c1d87f9d56d0753ac0428c3ec248a98edd16c1906ae9a0830d435e79461bbbabd8d866e84cc9adf03a51c12f80d4a5d23165ca9a6c6b0e52310d289309e642ee90eddb2adfda3594820ce476695705ae6f40a691eddb0394764f9878cc891c5af139f0ebc03de071a3036e5c40df1655c15c5c100f2a48085976828eb9e4580350e41838c96914a541cc843ba74bb5e1b058cce298766ce2a301deac3c976a050e8b0f3d882fe7984fa4ef329d433d93299fcb2897d2d9be080bb0976312c4b6936620a9f5163ed8c4490c2ed7430bbb58cb2efd9384e9b7d673f5ed1552bdd836583ae2526a6169c97ef6c450581647402431cfc3197fd6570e7177d6b6240115f58b846037dd9349fe55cd29900bccdc614964187054425903" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "f2fbcef22353e5c7505d1da1bdce4725e5b7eefbfbdaa16579d92d56a679d408", - "proof": "3e5475daa7afefd308491b69eaef85c6b00e73c616944b381a254f61aadade0010542697760e6c25adfc2338676856dd3192204639a2b9aa42c9a83dc1aae83866a49ac3d279540413a7ff8ac2ce498daec8e20b0898fb0ec8e5bac1a21d90713207cba239d41a41edd034dec187e3826fd6d551b57aca44962b2edc3324795e5de91b6d52e67278a7f963f062dacd1aee31893e98b169c3081cbfc42088b804e2ce91a20e726bb912775510f25b2911f69e10b90a862c77460aa6d0c9ac0b019e8b21356a0d7939f8560b550957643c9353ad70631febcb1f95931d82dd1707a08cb0564fe21faa1fdbd9f5da4659a369e06032fbae4e95598cbbdd9a73bc20748ac5ffee86afb9533a698c5e550f6713acb5f0763fe5a7da36649c40ae141f3a4b54feca8436712639541623e3f52aa7d31d5ecbf5109fca0c1e74b0fd84383cb875c59906f4ba31e25e7e898874315b2c85015df1dc13b1915bf99a795501b87081ecd188989dd46841b35721bec5abdf6a09c711a58c052079047576406246f6354c67aa81d726e607cfded6491064a7a9dac2d66b7344a2afbc2c391b6f5a20d3f703f41947940754d98f4d8ed6e2bb5c71865bd63aebf5c7aef503944ef6881abbf79ece57e269672d5077e81c0de6044b9328c6546f78b353fb104531eec2d0fd7859ae7c649cc975c46597ce2a7c68e1f404784c0ec519f3c5555c68f2df32901e82e1ec0286d2da9adfb11ec8520ef640893ffa32287dad4f8598086ca3f1646173a6dde34b2a65c059824af588ecfa542b7ba3d39ac278e8ac36046a61e069dd4a21cb13675eb4c959e6b737971f1f6d591d00af695bc0cf2e5322149e05e89ff6e17bdb9d059f15658f8a83f986a7a9fadd2370e664b18aa55900ab6a4907720c2a646e8bcfc1561268591a2fa4880d6417cc07c4346176a4ee01" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 36 - }, - "commitment": "0abbeb511b0ec05b4b9d10600b12e73cfa9bd9af08e5a79ede4e25f6e8e88a00", - "proof": "9cf1cdcca266709f23e1a1e552b4da45d19f143d7a397e310f635c584aa1187a5478e525239de5d298dd54f09846f086d722ba6fe8c1a97c98602957da31f3217ce460760c0387d24cd64fb7a96bd2df47be5edf7f06beed260112c3eda46c74526ef033409c911d12831ba55a5a0588f8692ad5711b17794aab7e7745588c06529493af2f0f6785f0fabe50f38b42a41def0066d3068cddf4a4c2d624d5d10246515d9a1c8b0c7b74341c7811e7ce1105298dd8efbec6dbc528a9f808a64b0521ea29598be95d89beb9e105c5b47a548d7cbba94e0899ed029b67f14e462704cac56f83f64a71f0db094febfe4aef51e0620b4003660a261f420e2358cfbc5d109ca3e6efe08259de50335b70c5360a84b1efe25ce98b90a6ee02e3cc0f5866f6b168e6df5758fef558d24cf7014ce5f6a7d0d086f377bb010e237e5412fb2a42d61825de892b1472c6dd01f12e931a0249ad1aeac31a7f1557e6fc9fee8005c22e05f5bebac01e645aa52d0f786b47e90c16ead629941425a149e6cfa354452e85d9e1133e7ddc80d3521822004ed9b2dd0f07fd6b2b5d91dbd866ed5b677158f07527c251b71d11e02cf6551256710ed3fcda543e0db8a6071ceaea43ef0e905979632c1f82c63a12372836fc54a3b534e8feca619beb2ac409023b644d4a6250b01217a434e2f01ee71f589c7576e25dbdb00b50cae6d20fe49d94e9fb4d483588b15a4e540ad76e2b5ca9c7929a7a17874713175d0f9fa7e93bea05b3126400c18272619d2ef74243330309199e408140decaf447e870cecfafc171b7499aed78ee1934b78428aa8b405fa5496b2237acd29e667550691b665ce94d6c3cefbe87052f6b25ac3409a3b482c5c482cfb9960daf6eabcd9c01c398e320ca018a86e5d8d6f45c52d0fd24299af5e095c7d88e0d4b9dfc2806d9d16b1eaf160e" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "32abe842909ba9920f1e892e29f5d03c6ac87feb5f958131c26a9419b7a5c214", - "excess_sig": { - "public_nonce": "ecc363f8101f615700e2c41ae5551e6788d58354df1f407e38d5c9fff4982262", - "signature": "8953da24c0cca70150ea31c20ae99a9c327076a8b6b4c67e2bbb67965583b506" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "86a9403f0eb67afa45f387dc0a5ed0d8967b792896b0cf71ff6ba4aba9be585f", - "excess_sig": { - "public_nonce": "a6217671d14749f3711cde27de6ba9a71ea7433ac69ebab22587bd1c72764021", - "signature": "1df971a86773b9156ef409bbe878b7a85e62f538bf0b3daa7763437ed8acd40c" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "98009a3e30ad03cda2e09e90e4e808bb7f157d7af3a0d6dbca08ec57e0ec453a", - "excess_sig": { - "public_nonce": "aeabc011a4d451db9cd22f6a753e1bcd4b9d0a0f858452d28dae087ef6296f0b", - "signature": "1217dfb8f802a8de2012a4eeb3892326bea0e8dbb5c998b212ca214890ba7f00" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "a465068f799e49b45a26bd57aa8506349bb554a0158dc532ce0d7910ff65867f", - "excess_sig": { - "public_nonce": "867864cf0440ab3a06e3bfaae1514ede27967a9496966ad7e49d4515f0fbe654", - "signature": "033ed689f57615a70f02e434f31c661c91c9a30db2ba5ff88a0ea21ef95bc50d" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "b23f6ddabd58e1d7c0db8dcb1357ee8a16caf2a7f6900c0802c5245a2f24c268", - "excess_sig": { - "public_nonce": "30b0f8583221aedf8030ad77bff7995cef103eb7308c18bb08e797908c609a65", - "signature": "aca066eed1f294e82742b083c88a0c84589cd050d1ee7e8d8f96c28e2010d908" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "2852e2907c9a619b34cf34422fd26e7ae8a5da6a331c0fe7b87d3f7d42a84351", - "excess_sig": { - "public_nonce": "7485cccfaadb37e82e201b127c0a81cce46d5369c6d0283501ea0d671140544e", - "signature": "a57cf24d4795d805826b00744c7d5f797eb0da307fa7c669048906e61cc22b0b" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 36, - "prev_hash": "be2aaf650771cf52eff4d818a7b129baad837e589f2a4235cad55b955d765f29", - "timestamp": "2000-01-01T01:37:01Z", - "output_mr": "99b1897a8bd8aed1616e6d7f636a146fe3a190c809764d6f1a236b622c7d802a", - "range_proof_mr": "de267cdd313cbc43b2208d91626767ae55a76db9681c0d8b4596b9c5c61578e4", - "kernel_mr": "5ec3afd1f7d047e900b04de62083555b8121ee8fa4f68cab6eadd836bc69b763", - "total_kernel_offset": "d92efc204513c38ccdff1226b6d7bb3829d9253b09ba0641efaa98b9362fd905", - "pow": { - "work": 36 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "183f65f245fd66fcbcabeb47fef8f4395700a1d482ab816ebdf2f1265adc3803" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "1cce846a6fccc2a5fa42ec3aca40f8591571857d8cb7f683c0f7f14b7df1b733" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "82318deb45e5f9191764b6ebfcdecab186dc7a9846bf6917e3817db22b317e02" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ecc722aa1897f5ec4f64cd4f691cda8008f483147b25c650fc266fa4e0dbde4d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "f2fbcef22353e5c7505d1da1bdce4725e5b7eefbfbdaa16579d92d56a679d408" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0ae457336d33c0ed8a3549c81b3de67f21213379c3cbb86dcb754ea6686b505d", - "proof": "44a1e99da7d501e496a31aeb91e76259d378d19b331743640b1380fbce09fc6d968a5fcbe4383a6d6982f770bd2a55a913bae258f9749727e07b2ee387899229f8cc0f6d73313163a832ad8f9e2690e7d5456c0b6238bf95ef2f14359fe0df6fe6d401826084e390c893ac83dacc734151db39988f868855165362f86272ed703e07b5eb236426758b77ad5e328edcf3bb0c9b369ac0b3429191a0b5dd299707e4e4a999b6b21523da549188e95f62fe24c62930893b4cf7d6cfc4c12cd0470f777a7d6e6ee5d9d54b32f788cc0d542496cce65ffec9ab9cd371b7561135f20f6e295a644b66737912e6d0660e5f14d045b3c998ef98f8e27992ec0130b24611008ff1e1054e2c772d50576f7d66b10be42b8b3715e8e76a0e39463334e90576a4f70a955c56af799fb98d3f060f52aa936235f81acbefd41d4e03b0743ec4384ef4382949425eba401c9598c49343c541f113eac9780df484402aac56151f4e6e234562e2fc636cebca7b4986326347e276fb11b1eec264e85751be01d0cc3b92f0e75fff353357c18f23177dcae928d3381d25dd75f5dc18d1bb940d055a03b8ad03574184fe29108214fb9050d251cde4b024ee563a8f2f8082573b4073530015f220b9766c0b380e058510414e3d1254086795832468316733ac3016b652969ecaa8ad616b6c55827076b61ccd08f38338ca845e390873525cee98c82a1d06d43f6c3e469d2fd36d690e961fc638cea3f1f060b5651aff56a309d7d8ad2c02800b173884d1f7f211041ab8350a0588e758fa1285e50857505961bb54701b509301348bd85ea835ed32edf9081853d024d052844a9d5e193130324d3981484a155f283576cfb6438a2157133376471ff0612a7c6afccda13f1831e9d9240977374c69fb35fde4e3dc2edf1bff2b5893890f68a03244209dcdc4b044c1f00f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "185b1feaaaa66cd3c65417dfb88c0f7ebf2d5a1a3ca095bd9f5f2066c59c145c", - "proof": "ba64c946aa336508170489f490ef17030e55af8ac2737e1947c7524d97279d34f83eeb1889ff2a6ec7535e4489d41720c325a0e73a28d559417f403ff8a5a3029a2e8295d2aa87a383929dd03a3ef40b102a9d3638b132424b9f1a33b5cb630eb0e4447236cd91104da1ba712af36c4bdad0760caad2d2a7b282cbbbc7a1b00788d96d0e1deb9cd8cb42f45fad4895b84e12d04949522127eb4ca3474bbf090e2ed86fa774841fd2b1a43efbfeb1b4a004fba98f30fa1a17b406eadcb186f303c61b677ace96ce8b107a43a5c62f6f29dc5bf2c0c307fffff0110c051a9ed00ae4f731fe0ec2fd574439bcaa8f235d2069f7290c4f78f696f8e42014acb60d18fe32c2f6084b50fc04fc1991445bd453dbb46474bfa7c14c1bc2d3020af32c15fe0b12a9632d7c3e8a7c3ed075863dab1176a7183ff9ff4ba83a053d05957b0cb8205ba69d4ba7f0417839112a6828f7d2f55040283fe087287406e042bfd07718938daaa8766ed79a9e8d840a3098098a1b8b25ea45d93db5f796b1f38fd47054cc0c6aaf247710ec01eef55409eb7f830c13d31256481ae2ca779727a9f6799215f37b0283bdf7965050818f367ef3938fad4efc99da9cd8a1a3caa8247620ce80700c47d2cc674a51634e2b08fd17382e7b8dcfd3db40e2c8a85811e7161410f88899294972fee149d3fe8081da83b60a2ce9ed87c576c62f8a4a9251be4696a9430a4e333c57cf3e6f2dc4a931e716fdaad20a1cd0aff54c8d2ed9efda38fcea8877b746523b4ba63b6a2613bd0537ae4904b4bdf35835dce1893365222cc00712e9836e742dad6c4a413b7ec793bb038a7badf66784970edb9bd8651154b1ed4fc83c4f529accb3952e9ea18eede00a2800f236c7567431e2f9070e600f12a2c80a2d7372e85b77fcaac28ae2ad5d7f49b6ac5bfe980d8ffee587090a0c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "1a9f95849765becf792cd1ee44498add853ce125315e7c3cffd9a244b4669b03", - "proof": "04acb9801f6bd53686dc2117df34f4d39a8abba8a9b8a74219e39b83049e8a419cea358c7fc9926beeecdc581a1871ebb4e847255c66c50f31c23382a6442528c0f4954f1f0502f6fcaf434e8200e204e95be40f87f77f609d0b28b9a55de01c185f63a7f58312c20acf2eeb44e25e61c006530853411207e3b791bdb72f392c1f01bbc461dbcc46e974b65f40a3e0e17fad4d69061bd75379455888ec6c560bbaae26315a93a400fc9cada6231ecf99f317d2c61560a7ae52258b1c0318ca0f8c81940f78098ce92c1316a20071be2d3e30caca4ac6a438c79f57ffe0365f08e4d1468e8ff8ef9ac3057056cb6071f9223adb5ac5b44af76eb792cf8899ef1bbab9b1de942776d67cd94b8d9751d8d65e89d867e93d9dbc37f9f5438bdce96586263968831086f06d91cef3401ec3918c27dfb6d7621f8da06fe3e076d52968c48e05d9069a0265b5146042c4cedd1cdddec25af5c62fb85d84be08fe950534362e1eb56ffbbbbac4b9874518bdce6b82e193a48559cd63fce108cdc4638e3feadfc4bfeaae3463ba8d934fcf730677f6c1b03d888663540f090d92f4d1027b8274b64ae0c923a0f1806118a1163925a60518d916460e8bad9f300885990c417cd378d03996caa6cfeaaf90de5a0b78592c7d8cfec79dff92ba6cebe48a3e3d302b8b6003da02c4b9a2063fa19afd20cc3724832a6447ae92f1a6b35db1ee79dcbfd71edf2ed1c215faa46e3538ee399142ea27a74f87b9780e5a08f918d836bef8788171715979bac6596b2647e0765e62ee905c8726a04d44e6d491f6f073b40941590d1f70043e275d4752edb984d538228f48f0a652d2e510478276602dd253c1a39f442ddc2c686454c10d871d0fd4a9e92d7cd5e63655b01f2c27ab096f662193f9bae537233ef887554cbb00ea876e52b02b37494176d3f0e39e1c05" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "28697eaae0490f7c58c6ae24ad8cf20f3fe3fc77458c34bc97a5d50df0190f5c", - "proof": "b0941e902c4ddca6daa0bc6ae4e4d8375f911c2d4de81434040da2a1cc1d9b5b3c69a95862f8c249d808d9aea3c6497de45a2873bda1ca40fae469e12604ba1ec89307d2c4fbeb5bdeb3bd01419dfb63cfe977a0cae7d341da7c44d8a9b9057236f598c7294b00c1267bfabcfcadd508542a47e768559adcf53cdf3ca373903358db8e91a8c8ee767364cb6d05cd90eabfb86efc3a0d215a49aa2145f7d4180d6ea5f8d3fbd32cc155c9679b55e17d57031fe7fbaaf3fd3438bfe32bf6238108aa03d48533334009dfdae496946eeb7f76ffb9d217e851642a920ee8d576ee04ca94845da942aa446874cdab9c44469e745c7a88b119622a60a7e5d46833495f3eb8eb9fe904371c421aa1b1cdf6774fe62abcedea2c56afe29d42d190e8a271fe153c092259b49ecb7f3a3b1f131ac0429ce1146bb0e99a8a91f0f2702b6d3a0c8af91cc60e0aace8877fac2cd2d5a66ae2886df2fc47d6e53e72ddfd74da38d629d98ceaf2edc8939e4d8834b5489ee900754a94b8d60529c821dafe24437cb8637456886fd7e03e3dc2eb836adc83a1bea92ac713f10b9f9728707a7eaf7ca21008de8d99d377986b7bf8b93934fb63ff31f66ad837d8411e28475f790d3bea700eb3316d63be7c5ef5037fbeee3332a368be902419acd72a13cc200cba6a94391def16aa3cb7e6ec8c151806ee6adea62deefbd008e0c1b34119f04b6967547c61ac76b10c86593d963e17bf358e07e6898cab35519e1c5155e9b66eb5709a5a7652122f271f61c2e590622e0bd5cc787e27d561a6f38e9b581ecf3bdf19066d9492fca80a9a009b5f750ef469ad6ebc4cfcea6dbc70902e8d3039966e12982c67b4dbaab630950e0411c34b3f682cfd896c706a3006894c130c7ce06e0a8259443d5e155ae509fbe42cdc2ab899c9784e4b0ceee7d0571c589dea959808" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "4636403e07bb0239324a104ee11e95bfbace92654c14b1d947da9ea736ac7710", - "proof": "b2101939cd268cde81b61c1c8e7fffff96b9fd9fb94a82c7858879438cec7564ea30774d40b07d1f07d6841cc426d21ce39b156526596a1a80dfb7ce35f4f94dc4e39977234290253577b12a5dc36c5ccc4fa70d42a60a8230e289aa1371f63b4880c936e910f88b602b4dfef750e451c38a34f19bef1bb3319d47e82ffc8e207b3be3e42bf4d3a45a8f3b23d4113b39c0c7a25d686b71ed1d869410b2537b04ad5876dc5350da6867de9e007cf2e57098d3f970a2c02a4f93d574d6f1f7e50c39a952c809435b7708918b54b8cfc932ffd87395510eb40ee4b3b99e59e9bb01c8185a217b962a337e9bd3f1e10c63cb2bc5b4f960d2862c503ab2b5dd04340588dc33c69bfc0cd4b90aea5b08e4cad5f9c150580ee458b88e1930968b820f7ada59272be38ce30be1450708da0b0e99ffc19302dd0b84ba14be5efe4cd403416cc477396214d86e0f623c93d08acc3e8a1e9c99c3fabbde71165bece39b191fc8095b8346fe657211d80b66073f0a9aa560f92dd99b70a1e6e3e34f0c85a3152e82de0224f2d0ffe6fe86174cfda1a71817662453c9cc017bf3081963b69e2820759eae733223532b422c818931143223b6643c3db5f53257772cb0f6551e47d4068354ae55f34dd4726686c247df52c78ee8706fb59b165ff7651f0caea31792f07656f175da996a8296803005b33d330fa7fb8be80472ebce12848cb0062da022181d96ea0fd5c264ec57eef969a8bbcb06bbf4a37637803691558db9803734db242caea8b130cbb04adf9632c82a0b10158ec2b11c1c522c3f4cbccd452d981f04dbc4362982890b22b43c9d274b5982bcf078825281e4057b9e96fad86f626e4ce11e70876c90a2d982b80dd6719b692d3b4a98679a45f355dadd1cfc0bdc52c06ddc3a9d5ea088a05d5869f9e69686e63fd44c1b4d5853a907ff73d702" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "4a70a1e7b566213908ad238364250970ea3222cb3f59ed3ca1f15fd160595378", - "proof": "ea0682fc51e803e569d6f82378996b908940812e2abffcdf5832d34d367a276d7086e2917540240d492dd37987df6d115cb426264461e471a56a844b49c59518fc3309ab0c29d632731ca987baeda4129fb7567684e8a9dcb03355c81b73544984e5b8501f1784aa621e07e53c570eb1b671f412715e266c84d76ff5edd7fc538a15499386c8bc981091ef0fe82bc79174d76de00f8f6b7fc2c622a00fe28009c5a9d7c33523dccb17e2e6d463f0c9edadca61c28898865429a0ac5201384808040445842ea859a78fb414b26fb023bce75aeee039128cb61b5f4a013e163f04f80047d5e4302c7f41fe28ef107705ab2532ae57397f4418e64adb35cbb05d5bec0fc411563c47d1688a10ae798dcb3d3356ec1e5da9cd12c86b468db37b98602ac17edf554f5706253a6b84bf05571a725220e21bbd2a7be8849389df71773e2236f22c943c6c1b2398a04b993016c329dba4945807f68a06c915926a43d60296efaffd792af721de38f0b6f1149d72cd0a541c3a569a115fb244da6b09c8381233f1b7e8c6f99e2b91d4f60bfcaab7d127870b7185f2d63d9daf84eda3eb6efeaace20d97bade0404f5de024f0a0d09b88a62d2ac3f81002bc8de8cfb54e163cc0704a7df12a01e6ebb61584a7a56df7c8b0618bb74618d70e8171b3d61a20be1f3fc789251c64d45a8144a91e1a2ac7fefade3e357f9bdc7bbd3852d7b95c5cc4717763574278cdb32c92cb5da80948343756c3efc6d74bac662c04e1fe1bf62f76c9828daa4f34b0c36502e9e6d66eef0d2635f911cb2e19ea14d23d6e3da2657908096e12feeb2cd11c379b0356b6db00d834358b392831fee41eaa5809f4b2dad60b99507e22ec0c60fa6001c2edd5044d77bf9bcc165e4087563c7e0ccbf04a08b2173a313996c9f7944500660054b56d357379e2c6f7e3d61d41d10d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "5015a7389aa4a84f240c4652f2388851cab92ae292a2e9fdda5a1eed97dbc02c", - "proof": "b8d71faf2f99ea2f64880d5419b38070a615652ec58400b73eadce3255a1d56958696e6429aae73b5b82ee62dfdcd48184be8f4fb2c7d21ce3a6a8ba43185e14c4fa1aa3e78626f7c55f9dc047b3ab6b82339bedd2f02e1ea1a6246cace5d624f0e43d7e845b8b4a03fba20a9c5fd221095970ae6b59c5d1cbb860c8c7cc65781a74bd7ec45c9e2160b73a9e21eec68b1adb3adf29b18ed7f639e5bca38d95092031a0247524dd7f2d76e8424d2453519e61d52efea7be25a6c0a2ce94f0900c7cde170d471f44616ebcfe71bc420f7aa7d6a1aa189a0efa045d9e5620dc9d0402b36343f745b5b6af8e05d1a15af6dcb000a6321cf474f498f494d5538a77258a96eba2a5e32bdb803a323270232568786d56c0f4758605c7dfe13bca14d91c80353051ea0082efa7b6b47ee54cb31ae47fd4a1dcb69775a9675537ad16083bd61086496d71246c6fe701b7cd2070dc7ad793d5a457abff93174c08d618873722976962eecee2e14d3164b14cd44974f56fc1d20a767541f6254203c3609508a6188499745c006f356f04b813e40ab231c5a39fed5b8242c179f4324d5ac14090c0a911bd676bedf4231282a16681aa610fdbcdfe845ed0036a8b8abb08d71a1a34f1157d1a226a01ec53162ed777f7b9aa8451505ef02df317470f7e51b9534c06513925ccfb1f1a96c27ef2b600442e94ce311f2643439b827ed978654064e0589c8a21f912ab5bf1556f2b314a02ffa737abcd60c50f4e3c147646e643355ae735cb8ebab24e03ce342096e3491de980e1d455e4b4f5142300ac6f874302129d345881badf169a5bc8260527fc85ece9fd18ac9aa71cc61b1ad338c2af21a6f1a421077d12da27764717e7df02f13403449f80d0ef2b924cca2bd107700f52326f6527f129c40d8a1e6346afa1584ea01425be7601e0204287cff12ad50d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "5cc69f2c639d4e75570f1577bcf5bd058a7dbacdef1405efd44a3196bc34045e", - "proof": "eaf1c9a4405b0bb22b649e71489ee20fa3efe4e9348e6076144a48232da0064bc2639a14d0d18f15fb9750679856d1e6447ba4df69dc4b64bbce797cfd17796adc2e1465e9017d58779ae58c6bec58d520e20eae424f87f8bc6363c0436ad96494b7ea7842136d8352810a8e3784d3b1695067d8d5c323a663af3456bdb1747d5e9b7cb9d7759f91ae7ae0bb49d0ea7bb4b2f885a5240ddc8a6a355c14de370a5b985039e7b7e57f8df6bad4d0ad3c42a4f490644b40c9e6f008a099ef1d9e0018c78b3a562904ba7028f78f55c1b40eb21386392d202a004560faf4ba275206fa7a95a994c211d10ec953e9cdbd8e9c28747536a82110381c2ac78322b2d6788c1653fda9463cb45f8b1e11054db5278ed00c84558c3714f553aa491209e56208eeff91fa4bb528a083a85c3862e5ab8692fb6c505b3e1af6911781ee188a41a8679ec52eeac02c71ef66e8ed8db3f307c3445bbad4e3d508fddeeb56448649c26a6b88b51c90e6370b2e9f88fe8859dabf8d6e1b49f881d66f1f1590edab26a0e1c7727dee06603c5faff617b8b1636676dad7fe36c4baf9a96de3c6b5dd7f46dfbba969f1eb3089f9487534ecee0ebf019a76e95cedaaf789babb41296d62b2ff65d8debb3f890f216707773454c5b7bb6df877646fd84ffb5fcaba43e62bc4bb2c01432e9a19fdb141ad7336fff08b69be729d17818a93ca852ae4ddb63df8d44971646fd683dfb46953adb36ba6a1277926d1bd92f1fa223c44187fa512b6edfbf58f4ccf6ce0a25b2de3a0f22f9822dfd531251f0938f6298dca9b547916a8c4ee3f1206bb891fa80f5a2561339709834a6e9c6673358566adcd98606498fd64fbde504867a78f7823c3a0f594c7e8bebebd07d86c3995c20db6be150cecf44f145e22fbf92361d99ddfb20db47188b98be7cea89c2d88f74e9a4ab10b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "9ef1ba9a7f67dc32dc6a757cc084a8d8cc2d8e96b70162272656f1d17b877769", - "proof": "0c75c53e852385bfe5453254a7ee5f802a717d5ca5876dce5b0a2598cb5d4d1eba5083f6597bae2fd8f1347f73e41fc36abc1afe38d59d127c9b3427e1a9be0b16fc32407bd6cd61dbe34dfd81fa4714ed4b7aa90e422559f4821b8f7f727b6f0ac60853d9e5bc8666250890b1509f7ad30234860b61d8cba64a468656888040fa5f53382ea280f11099eb2f731256cc67313b856f6e629ad94a3ee39bbe63063b3d3961212e66d3c2ede27a52bf1fd091b63e1edcbc08d22d7ce86f56959005e5c042583102f9c17d94791498db0abb463b5ab1ca5a26e72089e9302339be05e0145ae01dc059fa511df54b04b6b4927875663898e3070702e1072e81638a71dcb62cceb64ac87ba0b954e89e6ff267397b48c456b97859c9b7b58af89d0476d8eacc70c6e35d2b81a1083353c7a6c8519b1d438e7828a11b8ac1c4ddb3cd273e3e76274980ce51cc321c151c262d0f6ea6ac5959ba7dd142843cafdd87c238fa56fcf46ba9f08954a9cb20691c4e62e358b03ba349a57be2b5770fbb30922542a110976b6e703499decc8b7bf399b5f643c15423888ca04c97267b5ea5b4351e218038a6e4b94f8f445331690c35e9a2a77c36f29fcdb09c8a000ed7c45d57fea2022afc0f91946773583c94b895bbe9b6b7b3847fc9c92d0ab384947fff12aec91966bf50f097c93d48120166f27913f80087b75f7af10e97620809e1337a220648972ad5b2310d50a6000640ca87f7237ce8cf17e4021714fb306ea3700354b1e428fa1a8c32a5ee77a952b6a65311c5c815bd7da9df29e88eb7d30b3e5dc0f33dc310b18f60383b0a4b52ccb0b23b2f2089edfbf49265f4b819c902ab0177b81b231ffcb22e74635044fe962b382678e1bce5107bf89c663bf779b89609f8d8e80c6b7f62eefc8d3f405d016f521d8eb6e71db7e1d4a0d1432b4579a90e" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "be760657ec40f2073702f676f04f1715a3ba61e38db599b8955ab9e843626242", - "proof": "ee81dbe5288d6acf405aeb42d4e0cda426bf3aa508485bdbfdf6d575c2a2c1166e0b2187f4bb8759aeec3be79d2107dcd117c1c0c435ee7fd74f64814c029602c6a87f3cfc52ce83eec070636095e83552cdfb75d565e837a4dd5b854ab84249088a53abb41830f8cc4621bcc942cc2241e45aa01a138c28093f26d0608e752c733d187dee05fd2e695083cef9b1e0f0b6ab5ec3a91d96b308dd947cc1492101948344f008350a614ec351ff1b1533358b1d31ed9979d2bd27dfadb60c401c0f7f333090cadd7047b9831372fd3e1b02a6dfad64e2574f8edb5852503b020b0d28dd9e8e87be4581700227cb74c7035fca7e85945f03ee8e4b7e1642acbf20207c824618af88b3fb13884c049b65ea79a7be65c71473e7a42331951b3d2e4a603a2b5cc3a0b58a0b29da4184fffd695f3dc504a1dd6d16f6f7fd89f9b6e835419a8aed35101b58965cfce521921aef73daeb3165f3a611ee4cace81fd131bf78ca40060524229b6c7cccffe585d0e0ef060295930e7323e921ed7dcee94d0d6b4a68d2faf4b61bd855250528791c0f1ad44ef851842608ef0cbda530dd10dd49e8811c3cdb579921d5db89886a99fb292c27e0b3928085ef954c89ce7d5e221c069773c52ca036c9560923d3dc7f037a1020e3c420cc95435618fcc2af9d4c163ce656fe262e622eb35e8be30664a38980c13a976899faef8e0ccea9ed7dc231568aecfdcd1809dfec205ea3eca9f9705c26a93e48629a2689033620f4651a1bba0920665d3846698442e2e2326e719189fc810d208ec7c29d864ca9af939c3dc2d31ba87b1dbf4c0bce0a30703c9450f4c3bbb132402fafaf9f057e467a5d68b6115585b0dcfb6cb9a586292c73d57a5442587916c23a067616402f7480920b1d0f3dd6a7498c6331c5801657528acdccb79a839f127eb6980777ab76fe140c" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 37 - }, - "commitment": "06d1a9876546ec85626ca7fe974602e6500358ddd45b42b1cf100825bb4b5a3f", - "proof": "7485be3d3784c6ff4a3a449afeed2696cc54d92b4d4ed5ca5de0353d0e6e6e6278082cbdad2c536987877635d9569c2319fc6d4b092c3d9f665e03e65c444507b2d41e584bf05530a3bd01c3f5bd12dde68c36d7201dd329818d4307ac8294065cb167e570a39fd88d0938d30b91581c3471d209431131b0ab767003be752e29639637e216d6ca55ca643531fb9b03f1ef61c08ec96af627739a2cb02589c6007b2effcfa661ad9ee6b91d9864716e51ec51ce7d96684e707f0c0f7f9208190e90d0ec112ac6d9e3385d6fd35c3badf25b0b3c9bd77800225ae8dc50acacaf0a0c883897ffcb6cd8671fc5be18474019a5b766732b3aa927ba19daaae874bd2b68c732adf7f58692a9ed57435b85cbb78459a3bcba12a659f6a14de979949d374ccd1c8ea56ce19f636004377c4d80eee882ab0192addbe359e66d9cadf9e14c8aaa85b8dc5af1e119618beeda5ebd3deb543831a43122fb2ded5ee53544c27ba21d42a917c4588716d93a086abef9ddc20abb0dc07fd7f7a708c79e7f95e91b6098091ed90b54dfb91d2ccc1e88ff135e01c36513e8eb8d74fea43b01bdc213f47362cd95d901aee5bc091d8b6762ed7a7ac0fb35cb8b7604ff923f03f6f85b984cc11aad9ede0ee2e48adc56833f1b6badde0ae2dc1534169208770611df3d08f91968ead2ae4e20e976591ce763d86e49d8ddd951542c1a2d98f5edf96f750064575a216e3e09cbc6dcdd098a2c019fd5bab2e892c52c073bbd6665760e375e7ea57a5cccdd08593b1c6982b1e77e1977942e3ed3cb8fdee2e6e0d491aa1b205bc799604fdb34e176bd5d405c562bf83d184daf9222708165611cd25e6f0a761c68aced57a1590026e31e0a9d26ab52d7b00e0c563d39b5d845ec966c55080c9d9b3d7a7c531dee0465893d62661b9a128c8f4bd5ebd75496d144ce2de906" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "0e01f254b6b1e2ad1497309e4ec546c8f35d22b61564725bcc6c6b93a2c67205", - "excess_sig": { - "public_nonce": "d8946681204012b237fbf6509f47ce0a5ed8a184e1f55c237dbd151e4226e701", - "signature": "9b483439912446bce936dbcd9cd3b747577d962dd9cb3e89aa6c087501ddcc0a" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "386a325f9afc2b2644ea18ed4569ecaad9e5b9904c74ad4b96e1ee8315d1577f", - "excess_sig": { - "public_nonce": "5a062ee79f2a25cc0532af033609818783c040548b3966a8b1ca767fab74fe48", - "signature": "1d6d919e8c191cc9c629b5ba7407b754a911593eb1c4713e75fa64cc7be00109" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "60123616b7f705a62d05c248a144e2d620798e63d55a40b826adc2985b70fe05", - "excess_sig": { - "public_nonce": "e626f7f7d746319aea3e9950dd565cbfef7d9daba8c8344d2261dea8985dba65", - "signature": "6a76aafcac9b74dda7c46206251ace76912a6ea3af724e0f2176fdec219ace0f" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "aa7a26295864cab1c7124b69a255cf3fea30673efcd8c655739c10482690fd1e", - "excess_sig": { - "public_nonce": "64acd0a76cb22008c34679faea34617be67ed887e1fc5d5ce5b185cc4409775c", - "signature": "da90c4c690df664aff8159cd34903bc93bffa5d40ea6700cdc7ac9aed30c9407" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "fe4bf8df481d7385998a9b557ae1c5821a3cb57d39d25dec164a2793e5999246", - "excess_sig": { - "public_nonce": "78760d4a14beffce9b8b646103f19511311151ff7f37304520e89d88c6114367", - "signature": "e67a65963a61795a4257eb44f6da2acf878f32255626b18c68752efdcdd8ce06" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "8472f71e935b3beed2049761ff424a62e8f6e4704b14ac535cf57cc5fa2ab342", - "excess_sig": { - "public_nonce": "e45e68c46402226c31da05d373ff566f5da1f83115cacc5bc3442eec73d64c55", - "signature": "ebd7a7b4c0108efdd2cc378ed156bc6d2043cec3f68261831adaa8f727bac40c" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 37, - "prev_hash": "c1880dda03841fd04f0b893cdf7f450bda5bb58524a9e692134870a01f3f5167", - "timestamp": "2000-01-01T01:38:01Z", - "output_mr": "7d65c1c615d4e873a6eb01c7328e7baab216f4de3bd9b6a5c056f6fb0abc435f", - "range_proof_mr": "9fe25fb7e8abfe30563e6f66ec90ecf7555ee61e2df1401f7c38e874493c4a3a", - "kernel_mr": "d37c46a24606f10509f5f9c230416aefd690c020c10ba927c7f613a107f76b73", - "total_kernel_offset": "754d0a9431e78d0dea1cb382474db0e52a6bce5861014808a263401736940305", - "pow": { - "work": 37 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0ae457336d33c0ed8a3549c81b3de67f21213379c3cbb86dcb754ea6686b505d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "1a9f95849765becf792cd1ee44498add853ce125315e7c3cffd9a244b4669b03" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "4636403e07bb0239324a104ee11e95bfbace92654c14b1d947da9ea736ac7710" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "5cc69f2c639d4e75570f1577bcf5bd058a7dbacdef1405efd44a3196bc34045e" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 19 - }, - "commitment": "68b067c55997156efef9d3f223669d5a4e922511f261b41abf4e3f99d9a0322c" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "1632f3355c53afb55c8f624479e7b8bb1aa890fe61764bed35eda330e5831e6f", - "proof": "48b7e99964a0687d1418fabbb43fd5e62e2e3a3c8fe04cdebb720eee0c50c957622b37113e9c3142514137ee4329b70ecfca746233c67864e21d97d1aafaca6138ec05ffccd024dd617aa2ed3c574addbed4a80b1b88dacd3594b30d8a73a618eee54cf715e10cfe1c803f0ee6369873dfd64d4622c4903eb1553dffaf011036f9ad011ce2382e1e654a62508254a8526967793917d185ca0202d86d03dc9c0eea29e080a51737774b5157b5c45c077d4f1dc2137a8914dc808c1c12f5434c0a469329957cc4e74a764bbf17305dc85cccacdace22b83c9ff61709db6437570d1a472dbe4b180dec969f24bceebf7c8be2f70c7746eae7c4354517bb42593936da7fd3a135db658a268f1754b8bc71755435f47a140f8b6b61dd8c790623da0464c3a19a9f30476e1845e8ecaeccb299731adafaefc2487223381b14531b282b863eec3ef4dc0a5ff067a41ce9ba66a4ffa9bd718cec8c0f7dcb55ec004adc3ae82d967a36ddc8ba122b2ca86d5b99f208165848976f3c2ea53fe71d0d11dc75c412a9f15af91a9a2b7a4845e91656e5fcbe56f082b2cc8ebf84852d14fc9b611cb0c56d70970132fff2d4ac10a506ad02f72e97d4541dbf92b23a8f913429368861c411f68759aac5299a4be85b654ff41e25cda91cf3f3ea300b3c558ff31688a622952feb41a49e5f32961c3afbe875a53a44c64fb0a848bbe48d8f6f9d0812a96ce738ded6dd6469eba7ce38180c86701969a4ab7c09786ee57cdbc8ca4e4cc24a245fca02975a23ef754c7a3ca5a8e166bfaa1d043598fd62140af5b64a929e43867627eccb95b5180a4bf923958c295e7882e77b5c3ba0625950a9a41065c429cb1f83582f39c2b7412f26d7fd3fc64746b296443cccf51c650da5a10ee85d05303964a0d7db035023e621df605fe47dfd9eec5e24fee2e5a8ff471b03" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "22ea1a31438c3c53767f80f798386f8708f65680cb72fdbd5e2239e5d3a33f7d", - "proof": "2a76512c2556b926b277bf86a3fb80f475ea29b47ad1ac31281fc4d707c12522e2b4aad23b2b79a7458d64c3947c2a64d785fe4f298d5b7b54dc22c3cb29627c6646f0afcac757a90b4d86908054203b9c134bf8eb19ca50f6da746ddfd0995df0a995f41cfd07aacdf40430ce996b0c3051690b910de14ea6ed7c0a4bf96152e4a82e93bf63dd0df3490ec0355f5099954afaa11f35e241895b8248b418f20f0e0055da03d1542e172fb0582d71c6953eeba2379f0112ac8b45c4e0a374b001e6aac316e7c3938b64b9f498a3626fc0bb7eb1698c4204f6fd02718619d46f00cee9ee19f92fdf4b2d81f4aec88d47ec4d0834b98dab4057e65e53eea76cb737602aedbdfd46dbd2ffad46a9eadc581a3380856a163ecdc16c8fa3c85d6f902baa7a5fd08b7f20fce894de447caf8d759f8571270478ec9d6f8fff68b39dcc391e3a994c8ecfd8f5d473f84ac9007dea05c6fcfb9f1d1dc7db1482c1b2eefb74bed263eeef090f06b1118611f32f009991c55b53e558248ae645140ae17f4f1d907bef117e741bb8c76fb3884863d62c3b8f12a4342ca991055b42b94c5844368e5a85c42f0b467281e4d6b580c2f1ee582b46739b6a69c06bf390b00b9dab258ad23e4eda811d806f464145c326342688f86d920d86ab15acb6d1bc79661a752a35e4b7a720d8a4653df0e8a1ab51dc85b5908d836f89dedb6aaf33ad63061c4a8da1c8e41320ca583525941be946554e6ab2b305674f390102d253214e7b724c4c87df682e027fde750dbe72ec268f8d68f4be4bf6e794323fea7017dc5c117c8b00d42bfc3350b8c8338ed821c974914e409c65a1f72771677fefff28f4295dec0d0dbe358bd8718b651932c8a1e35f1f39219c5a05f071d739969996a80895aa3607d5145ccadec7e82e985a1a0b2d6fa1f3a1c69f27e04d82337a130700" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "2cf15b00d3071299d70a23842414576ff5a0d3dae253e5022ad2107be942c77c", - "proof": "d87d74d9e1a95085b6fe688e7273e5fa77da9c1504aded739560cca5376ab736a49935fac2098a45009f3a4de3a2351a5fa0c99af7d80532a65b29ef5495647d1c545c2c91e3eb5e09da99f8d66973e6ecfe544250e9a951f9d21d412774ed0c1a44cba472c7fa91c6b2c246051f2bc102a682c355d394b428bc0d7edb083b08e450d8794d84f8c6436dc365f3ec3a0a098d9418d8f21bf03a38be00b5eaee04df060269595ed8b470d2a4bdacc85d7f3704eed09b2cfbd4eebdec8ec58ae50ebbeb65143f81c68b8d68c54fc1f3e8958519c3f043f951102193254df7d57e04b874973c703ac80c24d3c7e7426249088af5927faa53d904fa40f4f0d85db41826a5219cb4a77e269c0a921d4979dbb77fa9f3d346da536cd5ea9f594ab6e64e6630efe550500ca0849dc75d6c5e38b2187baa49cace0152ed241040516bc7475cd59128ebd2cae046df2775a0d1465dd25ea3d2b57d4ae10db807be801dc8358ecbfd0b2cff44b911df68f5a1b4cabe512ff90a5856de4f445e71f447007658a4aa60e04b8876fce6638dc5b297142aa5940571fffb5e4516e0c7fbf0d8ac58540aa2f486246d744ab3c15ad951e3963cd61d1b1256716e3bc7d773b761324ba8de5a63ee131000ca3622d2e472d9badbb0dd5e30b72c1b3029e8a867e61370288f2e3d2a0fbe16eaaca4ef88d2a61684363cd7d40931240428f7fc848db62b96832dd23c68111f085105dc07b2fa279507b7d9a80f0c4e26367daea40fd74468c337c598ef1bc589bdb3a92381983d62237030c3a05f0004d3818cd80b36766a14743b828b927b180b611aaed3452f96b3062478cd5e14ace25244301e4154983315de427d5ad1dba881bc43a4c90798488a55b24bcb5ee9edf0a845d1a8059ef4ee6ffe13b393178d00e38497242fbd8ba6a2638e4bb6714932c7dc942e05" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "382f72ee4bea061553405ced579816164d117d289ce0b532826fc35a24ea6b0c", - "proof": "ce2bfbc288bab717819c2a1338450aa6b63624ed62fbc607505d9538a5826c37c24348b90f13fcf82d27d91d72c11adf6e7b81c641cf313a28fa8e7f267efb31c8e972bb1840458aab09735b17e75d47e473937ad04b7e439f8da98fe946b666de5ea54cfb41ddd909e385873e6f7b2ad054a0c66582d10a3439db218bc3254e6658c4e23104642886f81e6db68063c154b70782c88479172d1e98926c0dad042f6192d27f36af6b06df76ef3db0755d009c430f0597432193e21be39b549e02c1c5b383430cdb7bcd63fc8b00b851799445a0964afca411b02b2db66fef330dda07fe513f2dc12b36ea694dd90f031d22f08a813b5c9a72b76740c4661e751ba8bc1d504d5f41f99282dd0f50fa4bd6dd7c0c9fb66037fb08efa10baa686762b05bc16cb6642d75bd3d24af9143299fd5919ea95db2459f8a2cf1078db4cd655e2b24de8b2c73ed066d978d565a1e5f8ce9b64991aa83d8762d605f333f9d4530848427a3aee24633b3b8d567752117aade90ac7381ea8b8621119b2d0c91393ea389415a3ca578cc5e703218ec64816a63e9035c865170f40bc067c9d5d27ad6300bb5fcee42baefcff3356368917185ed752e25a49fd71a83b79a2a1c4e0808314f5003f96b6bc7f978e3e80a3a46e674da979f98a54d84e2758e9fabb22404551c20c0a8362c29116dee40c0ac540cc29fc94d733014bd5bec47009861265cce192b4265e7b2b0b11f2d014673adde1bc8d31ca28c42c2487cf51cd5146a6efce3572c2a6bcc2df63854953d949e9c47c796b421d5cf4beaea79c9d1ca34922991be1faa1fac8be72f11341715a5a1182d97115be50b176e674c26561a2d924a04b33fd09a4c98a84b982b0868fe5b4346213b74cbd84376bf6ebe690f03c3e86963d265e6c15516b4d19c2307b7e49dcb2fe92eb335d31198dfeacc420b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "3ad0a9fd3a0ac3421060d861872f8f17edb645d55d46b31a8a2a0363a6462b21", - "proof": "de9b2e3aba86d80b9a51649a1cfae99536e6da3c679e32eaee244a32d192ba7998732d5cae18db381320ac6fc138a2c7d7601bb426aaad22802a8b0a5bbfea173e49d2f7d07774adfd9986f4d5e3343c1b0e08e925971ccaf12366077f2cd2750627819df6a80638a7b972887b4935ad37b2496374eff74aa1a1a98649a0ec39afbe37b19eb3b97baaa26af8b983efed90ed18df6e7b3cd6ea5e20527adca60f9bbb89af53f346c41ceb98e566a1a54ff2986eeb53b56476add871b9a6fc2c06c3bd770a25b786eb2a6ac2de33042ae1a3140185e4fa58355972adeda217c90a1850c6e3b58e036cf92225f76eb558d08e0932db8a544d14244c9248cb56f558c2810c5b34d2efdbd54904efcf2f6c74642e8e73fe1e78c769ee5fee202bb97bccacfb31388df2bb5bab5a7090f951b3c48b3dcf15a0177187da5c81ceaff8537cd3864374f84ab5af8d467f791d328ccc01e21f7cc047dd485d90cb5c362e48dc7b1d00a50b9c28c075ed7630d8feaf8ee641983d734b4c0130218a2d0808752a22af71b3ed6349b122afc0456c3f58714f0875a48d96c8f083b22a5ae8435c260be4398bb3b303f91fcc3ddd29ce07664285c895beee81ccf77ef313112769ea673189982119c5ad87aea7e402e312ca9910fc92519b4a8d189d2ee7f4cb5a18e070a773f6d54101528b8fb4610f90241768daeab57f6caf69bee0eca46d2632c1618f00409e93c8098739a246ca352a366557e7664da9d9eaa46a3850e160c6da17ca090b505738321f7d727e77a8732bc653bccd5393bdcfd253041f9138c00e24a95a3aa898492b7d648d6e1b502d9e876ea2a97550a4b27ec7743e3e27551cc10c5f1caabf845e9e3b1879a8376dc0ee420a0b5de436a5ac8c5c12870ea8204aceafb0c940c1b1301d3b96cf02cef96a3240cfe0e93077918dad1a2308" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "3e7ada2c2c931526105e41f42daf045ca827af75e0957bc55fb5710a9c505a53", - "proof": "428d3b1f90dd80be5f39caaeccdc774f37656a933485525557fba31d65d3373678f1f7ada9839b92576ef212ddbab9507a854c9dd00ea84ac95c84e633280346de6cb8b801bcd46f3ba90860fb32917d0d7f32daebc55024f221b4f35bcff91a1ec490350de1017186b3b3275b6e26c2a763cd723b2702d1d3707af8fc8af67b1aa2f29fd8592db0c1e69ce09be99e265fa35f44ee62575290899f9d41e3660c3c071df36449ce3904e910254555e13c04aff06fdd554c60c44f407cc40f020e8071324583ccafe47b2811a7804043a34fa702c7743d4656980ab1c45752d406e268e7a79e9e43f9ed0688357b487870ecd2cc1e34ca66dd55e18ea5ef7928537af1d49e26a0365791910137b1fb92433f5eab4065a2f1bd6b50d9e6ae2ea219e49dab3a8116375ce615adb4269d12b36396f3ccf8a039d4a4e7836dcb9fdf13eaf6ebc18e9997fa59945d9fa8e4efa338fc531787b5a4f50b2da7aa0e3254653cdc7983add941ca6201eb53eff9a7b06588bc55be4b9b73bfde80ef6f24ae45861a6c6309600003f4c6541307befa27ac7cb8208444e6a02e6ce9d9e5e98137b4bd6bcd0bb8dc0d6fef97c1149f08995115c1299dcd0679264e7e98a54b2f58bc77fab0a41b8303edfbd4dcdb6d66c5f816a9c298a06b11a3c693f47070f8250cd47dbde363b57ecba749ce2dbc196a9293616daee89aa2c948b5d7984bf94a4016238b02cf66433a20d95e24ef802fee09d0247ec825dff81f927f6c0334766a338e8f99dd28768ca18cc728c6bde2ed45665feb6839c358214e30e964323eea7e5cf11eaec7037e7eea1362fa34677d3c323591e26b95906420991ecb3a3d38c5fb69bd705393d6e19f7c3e70b16daf7e601c94a1bd613a3d238078f9390e64ef61c06915b38327eace8d11a65ea769b77386764cb3ab097a9f2058333a0d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "64789b34e0aa9609b4fa2840608eecbc2180c2afc66f7479a269eb67e0996827", - "proof": "9aebecb85a9ca2b6be1b0662bdb363b1b3f25ca636e30fc1a3cb9aeb6cbe006e24f03fc24ed7253ff18d67d7e68f8e37cb7a5f5278b94e7768c34a12d406dc6b34a2e58d87462d1052bbfba121c04477ae6159ac4dca2c6f8972c194bb36cb48a0d415bd4ceeb56f17b9ba4680e51e393d7381a9a432e0eaf0f7a910b9c6e77d5ea761b738f896beb43a5f01b9c533141f3ca8eabaa724091e77ad19d9a7090c961f826dd10f6ef71f3d1aba4f9d5796c92e2c0a5cc0c15fbc25af6f3356ce0c563a044c6e038de79fe54787d45a97c1f1929e380fa8bdaffd27368935816007905eb6e42ef1d9d421a9bcd6fbf1af047b2cee433875341e195d088037987b4f526e3ca9eb3f1b1ec8728a44602c7312c20b76567554b625f4fd948e999de34ace782e43513d02c334bf230bd4802673838f3960e82ff86f953c5007d9ca387298cdbb6fcde7b2d59f6353bc9e11e5a7edc62d6383c029230a86b5168498c655dcc62c66124b0f873b29f90d7d6826b2576fd35ae945ae0361041460cd4bd025d4b7b290b5ed1fc25380dacee90fc34b986dd3e881ce758699c357e7af9608288ec0d27432c3eafec47bb8fa91afec4c63c4e42c71f4598cf651753b3c92342e4e6e5024659931d9c6668bb634be4a01b57ec95b9b5c9c0124bbaca074e681329cd05309635152978e45ad872c6c7da4f638f283f626396b2077afea48f5260210e20ff57781befc881c76bb780661c13375de1e94bf36ab7215c4c27524dd009cacee7c41573f58713d4b61ce0b6348e1cef385cd592101dd6487df311cb317607497e7e2076df08e275ecf09c6a63fa4f188e0f50aa86c17a178dbc3ef3e6e6caf134e8004703b02b39a03beacb725d221684f86e68b254684c42498905202ac8689dd0545154a9bf5e237fa05da1c84ee8814fa187316e06f03e9bb5a7c09" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "66fce19b230c7d86eca078e1e161b2a9d32c44a944f85c74bbe8458acc1ac335", - "proof": "bef9fb53ac6a0d59db2b15002a8044eeb4ad128df321669a39c8cb2ab847e477803d7a686a901e541ba3b4da87e9b5a3809a8b18f5503d22c8e2405555469209700ae2001a766ae765cf3e7b57d7e7bf12dcc4db0f6c1857e04a5b944b0fd96a7c83c3d3b06dc7419a1d1e1de7c01fb6b75825501f772fbb61354f7028a8014fea11d32f94398594a8da948ef48ae425638c4d0830c989d9d82c5c5736337c06a336f5f087e28fe21ba8bd3ee71aac31d5846076e837e49f8585bc5ce8abf0076889f9a0d20d003804768c48063ac2ba8cab309a11d3adc2bb7d8f17439d270998c025b1a44c091cdd31b93d0cadd61d6e86454f4e5b1e156a22c6e7b28ada1b3caa58dd4f6b2953f286e90f8b99ee90f803e81ce4af8c10d5cea754f2aba238d07dc29444d2bb3b1ed31c71bfbcbdbdfb184aa6913ac80258d4a6cca5c14a6d56835bedd6a9cfe9d4bd5d0952bf58dd99d43b962173af449301f3e52b15a5680ae065c6083b6a428874bd4a333b7339b9a5ad67f51998d379f1a5e91ecc585574ced65528adaaca7e8077c153ee6ba4d0e73975118a9102f5ab474ec00aba3288c42430387372be13ed6f728f63f58b9e33ccc9de115c779304f506c56c2908b4b0a81956965607ae6f2372fb70bbaeaca57ec010dc7f3e937754a89dc0dd4e1c1708ff1006eb332173a177b2c2a3b74f9e1922cfd525f6780f0d704c4e3d0f18589174160c4c0d358aeb7748096b5e1fda627028d7cf3e0c52e99d29ff770ae6c482a23c912ded04f908f75513edd3582f68c3fa6c59e67271c4c78f4491095602ba5f0fc0b83a3edbd34816bf48a496eae4e37477dafb4eaa6aadc1f27e367af36c4dcc1360befd5c1c3e92f92e3b0fc529997d38894909c6fe5e04416207eef3718dc6272de933d30fb160e9a18d774872de986f29800b12939f5634c808" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "a02449f5b4a7a570e806cd889b5c2d1073b9bc65d771dd66b006b577258c1775", - "proof": "60996d1b76c494446b3492ddeafbdfd8863750024acdbfdae7362939c3fc7739ac4e8f59ca2e6b9b0e18a6eda34204cf1908e022ad6e8fe780826ca3c8e923252429a74fd591b59ef220e447030668e5235340bd7e38f21b631af8c55b3a2021964967cd3a7345d256966df80d711e5ed83c6256ddec0b46f7a4e40fc28bd9228f3300a904fd30c5d452ed1f6a37a0418f4e6c493f20324c4e229b8356d7f100abcd95eccdc482f405913cfd3a4a2ac8cfcc1a496384612dd8b55e9f807afb0ce04d5ab77a6b14d33fb44cc3ee3b1a66a388efa7033653643459374d54290c0800b0145a6a2facc8b6009cd146d0311138fecbdfcf101e43265281bdaf430d6406dc1b57eca30f2bef6fc12600e7ca7ccee1300abfb43b6e334d12c050f947640e15fb844d9f0acde9b57030e613011091baf8a870615f64e4b57a2916c4c97ca2c0312d7361728f27e1176eaf4b6e93b038f7784459618bc405140bf36d2a5cb6f8dfd4e62ef1f59726c1eb757732ae6ff56b0734235fe35c2ac85ac1697e6d6ead100e7a3ea2a07e1cfb2e1699018cfc44ed66d56cee9e545c69f1eb1e993de2adf8a8a94196d26c2ff35d8bc96b3059989ec11cda023798dc73aaaed639132aeeda8f87e3cedfeccfaca54be518fe8c4635414a6124be9025604f73ab566e5acb75acea545d4f48c562b394445c8bc8d94f415f9d7426dbfb0e6aabd7397f2a7310837794a4e715cc2c463fb9497caab66406db718c31a6788acbf0084578a89f5610cd6749829b3e9647876725269081a8d3507679b08c1ba3527b6b4f107064bc198c34ebd6c7e91206d352f2dfbcb6c803e5797f4d0a1c43c2b059fc2da1ef95c9df18f5a7941966acaabd87c2a7287e415a59ecec16e1f30b40ef850171a3f9c81e7eec1176bf3aee4350a8c2baaae6bfb6757968e9ace727cd584700" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "e67da33d5b6c14c809d9421977991909b0985def18c358db891e8b3afc712854", - "proof": "2e82152e7614e706c3b04d78ff2b1c3efe7c6aacffc75b3d4e9de40aaa254c058423578b3996bb7c004d9568b09bf8c6e598e1bc6502f18f2a14f6d932d2b075acb6e5579d19f49cf92da03b9fddc331a0d44137444842884e0bf522dd45e944127bd3414d5e5e03246ba7cd030aae2efe3f16320a1b513a5a1ae98860735f7e72556a810ffdc15c0722a4e2342e17d64c6982c1615c8a0af03c75b9d7647f03f6310da1b9db2262954ec2e1f157ce9901e634f43c45a0fbbbc2fe1bdef5610f0402dfd4dc90e1a9977e5c02f9559f845752e8c42c9543538be5801e9df2020a2a8fa17465887f4c1fc4de30e1fa49ca541fa0a9e18f4f02e57c7920a42fb8116a4f639ef2dc159f38465fbad2e9425ad1d654683a39e0b54e439a419605f021b2c76d28f610b4a87963b6d963e9e2dc8c10f0d99ba409720694d82ef5be11717e370e3f516f5a6efc36390c5dde285dbd577aa7a510283ec6497ff9b4cca13d2609a36f44df72769af95f09bf7bbd18075b6fc131c3667924e24b7e6b87c34b0a62bad34dd5daab045a7ba2afd8a8602208879a516f9a34c62b68666ba17627a407f91a5c3625ddf4576b782e54aedb66cc6d35c546a44536b73511de4f242f9a3afe97daecb40f2aac1a19d69b52a77d24ec9041e09fec8301f526900273277af5bd946c599aaa5dec71eb4ffd7d07071445568391dea4dc9cbd1f6ded3652d0d05cb9bda5d832c7349a7818e2b71477af96ba410ca396aa3731a1103cff0898e7ad2259479e18004a86f534b27623423c028110a32907e62a1daaa630ee270a0e87f4aeead4c6497d4381e4bddc504cdf900ff1c4e2171151a4cee046e0095d8e8e8c3f6f4b8bc76df226441a46cf73adda0afae8e104c0b0c608d24fb1002efc5b1b230a3721e3d58200ce63c3b41592c36cb8a334d032b8920614759307" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 38 - }, - "commitment": "4a207ab981ef638ebbf69ef6f7fc686d8069e44a814c28d1eb86ceea9dec810a", - "proof": "c0563851de47dc4972070b4dd7535472074e06827629409fb011d02f25953e341484ce18ce7ba15f9c6824dd859a90c46d9b3a150042b2f93a473833e2eb022f3011ea387177f79b4e6f7ebe95389efce9aa1396d4355c413706b9105bdeb90d98e6dc2fdb69f80aa909ff46e558fbe1f88cec9644a9aabe200832d2c2e3a46e295aa2f83edc84d3f5bbbdc6918cf90f2332c9f02bb0f0ea31afb3b3c81667053aa274d930d729d9b31df20ae4eef329f4ae2a75e97a394be513bbb31b8f120de23cd92a8545454b39de96eba620802a7d04f1b2cad97782d26126c192d2c10cd272480ae14f59dc26e93a5d680aa942b54d6d82c4391c175c09dfe06fc5120820c47a3e9f3d864dd9d32e8e3f6cc7ed68cc3415b0eabb98472d1a65fc315902ca1ab282a417c967247048dd94ed87fe4ca34ab13b5b1f346323780817b25f356c271d7ab90c0008712dc3c0c04e2c44bca9913815a72e17db3bce309b03ed0b08b140f830cdd9600cef5525e37522ba6760b6b40512f999d928e3d214ef0c2fe6fb92db06c1d2e8c46d17d25c82bf10493385dc7b67f898cb120d8239f1200dac1c80ec9d1cc99d6964c18e2f3b049040d8289c4ac95475df4c7f1051253e7f807b7f2d7e73b37008fe9c3c5512b8b3004ec260bcd57517e8ed3331384a4c0e4c362405627b41b363b6d82c357da5a3396ba745492efec7769ba734d19b706144bc79ceee81b69ba4ae923f291c1599ac54eac47141abe3a72814fa0702c42ba2ec52914e6d27e1ac8b73d64fd658a5466b918003b3a6c7184279055709a013000fee6aae88c23dbd3d0e5ab37088c345b21e05f53c3f14a80780534386de7ca0c60e604e6546fcf2af4e64d2c769ca51c293d0db72b61209d038ce45a97005148c45c3603dd4661aeb903faa72968c6fd78384c2f865c2f88ccfd8c9a09b0c" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "4619b97711d5e07a9b02a14fb584427d626608a888cc4ac499f943b3cbd0ec32", - "excess_sig": { - "public_nonce": "5681efb9b8901bcdd5e7ca7ed7b4ca0c3d52e352430bf869230458015aede142", - "signature": "9e8748ee89c10ed805a3baaaab6486e7f840b6cfadfa0364bc0110d3cd00b70b" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "56c46ce6af4e80bf250a11eb62f56f828d681eee3e9b0e2da4943ff53f385565", - "excess_sig": { - "public_nonce": "28c962e677ee7be15194f7009aa40034b6eff0580780140ca255935ffa236f19", - "signature": "61aef566a8f85014e9f88baa42e795e5d832f8c2f7eadde5ff9e150380a08f0b" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "88ea445f0113b070f02ed6caea403f4be12a6e902d2e3b48dcc02a03d5656403", - "excess_sig": { - "public_nonce": "eec741ee317c348d898f0fff3bc393915929173f51920955c035472c7d070759", - "signature": "aeb50c341b621c25c0813ce33b9e19545afe397f84916adb8d95f95d31d10203" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "c0cc4685928a4ebfaf7b49977bded4987c67cd74d5329856597f5002bbddb76b", - "excess_sig": { - "public_nonce": "6010d4a5523a93be05e129896f4aeee6e4f74f408039ff6aeb61a82235c0f118", - "signature": "2cc41fb1f1d43467e1e7b16bd978f0404443c4266a016559fa317eeb3fd0c10a" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "e65882ef2dc939b17ca24c3b6025ac75a1d0130814b973bea709cf8120139950", - "excess_sig": { - "public_nonce": "dae74800f9a8c82e91e251dd9810ef6254dab6a4d2ce57d9eae1463d8abdc834", - "signature": "392e7b9181d0c4968705f89e10110d7448d8b9803463ebef57ff21ba9830f40a" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "7eaf0635951aa552888f4029daaa9b00bd250a6598c724f7cd07712fe21bf36a", - "excess_sig": { - "public_nonce": "146c6cc58bb2fd2646ea4f697e43ce39f8995e320b0cf6e16ebd2b89313d2d24", - "signature": "5c9d011bfaf215b21a45f9e34611c0f8098b723b6fe4522cd6247872ed18090f" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 38, - "prev_hash": "002b7ebf00533120ddbf7b61eb76f3cd6a4a74624005703ed0a23575cf2f1861", - "timestamp": "2000-01-01T01:39:01Z", - "output_mr": "47bcc5c8f7a553d137ac08cd3bc7c14bf5856f12ef126a9461dfb6dc9f19a60d", - "range_proof_mr": "c617b8349a979d29664ae562ddff3c9648276af6c4546a787095003ed80e38f4", - "kernel_mr": "81220f1ea57406d1009c688d8d944c8214efdafab607dbb427192f3ef75d15fe", - "total_kernel_offset": "39d9c79ce0bd4c6267a089755cd49c90706e19be982c9138ff9b9c77e8cc8809", - "pow": { - "work": 38 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "382f72ee4bea061553405ced579816164d117d289ce0b532826fc35a24ea6b0c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "3ad0a9fd3a0ac3421060d861872f8f17edb645d55d46b31a8a2a0363a6462b21" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "3e7ada2c2c931526105e41f42daf045ca827af75e0957bc55fb5710a9c505a53" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "64789b34e0aa9609b4fa2840608eecbc2180c2afc66f7479a269eb67e0996827" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "e67da33d5b6c14c809d9421977991909b0985def18c358db891e8b3afc712854" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "169cf06e31baa5f5e1649a8fa3deeaa63da2248d0fb0840d5cbc6b78180e4574", - "proof": "9675cd44d6f53c19dc195c33c8fcaf35749609678d45ebbb3a281a8c4588d77280285822f0788ff6cd4dcbc441c2955af9df83d7948aadc816906928072c684cc6632b8db8c3d8efddede590bf1d2341390172d29cb80a2f22d79f6d5ec0b42b2ee463ea598be954ef5bb521a31bb15c74777db20439b5ce8735354081589359c59ce413a1acc0d199e3e75c43ccbc387669bb3ef535eb30ddab7a527494930d883c826bda2033c9bd333cf4841f86b353fc8f7ba836769a323af954d61c4a00ca5e862210bf82865e96c0ef37e7795fd37ae230959906ec044e3ad86ae31c0a706ced8f17fca117b0bb6c36baac777b566f24b9f6ae3227a7c65804773989333ae9919b4ac8731844bdebbacaa46343e1b20341faa433df1704f9117de9cb77a64f3811f95471dfa75fc7402959ca676d005df2b7effe10728aa2b3f826da27caac888560eaab976fd042f47ce0efc9655e1c3f87b751e6f5ba9c79d8525c462e354fbcd0a35b58495971c6b6f283f0330a09dddca58e118f5a6cc980dbcc34ee71ba09cbb76bf867414078a2a9eb157cd9a8698e5b5a27a20c45e918537317c414e2b3f01c7ca23bcd58111d18c44882c25a63abad0cf1ba4e6483a2defb39fea64a810952efa73b4602886b9f8a52561886f9560e36db62f8b1f335404602f0a79d3d184051319dd09db1c4e53ea9bb20df893c9a38246289fcab3b41cb55cebaf6464b251c44a679881963a9ac0bfe6738db1ea5d78e5741f4d6051fbe5128b9a4f1609591d45541159112b2758b6ab3b4bc3671fbe9bc4706b1e4fd9465b07318aa9991b7f194e7fd2b9d915913a4bae39540b0cbd0af932fc3b6b229532d1e28b114038a4891db6cca8a3c852f2847be88cdbb9cbcef47e7247f2ed707b304c0df805cc222e4bfccc787ea006abc6f0935c52faf7c2bc6d906c2a0560b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "18856f0c5d40bad05475fc47e0f15e3b96d9b2e3188a62436cc68c5ee5ddb850", - "proof": "d2f6a8990e5ff30bdc29f741223161b96bfdbba5ee7592ee931365108beaf418c0e8c334c90cfbdb15ef5d4d8e0d83e8e9f212bc9d27cd8df9191dad4692277732317a2164c87f478b20743e31a83fc6a7dd23757a44f54601d0dedb5652b679a657dad5faa29892e9650aa4b2b17e4a997e72e6dc2351ece90130edc5f6ad2ea29521085e93d0c1780bc532b83dec6b39b8238202debd4b890ddaeb428f8c088ddd6bc49ef16435ae4c17b08b901d41bb6c6fc99d377ad55449e4b79ae4010c838a4f0d1b88824c8e2c243b386927f2e908ad6be170afcc00e36a9784b5270ecca3b3f961a5147c8c0c917587f130ad523d80eada6724277de4baed7f149951327e20a7a64a84a773de9d9f8d38722cc6dee3d8bbaee6f451a5d234a5781a560ef919c5058e9bdce0051d120cd8902862aee1ddee58abdb471a81ca2ebc5a08402f357dc55268d5735bb52979016f9cfa224cbb51ff1e0d3befb42ed844ad597c95dfcb3bbb05e35f26f1564bf722820abb1d47ed15e2a1297941ced453183d90d565c473eeeccacee5cce9d96c0de71dceee20a2fb1400de9533802b1e4529a0c8ea1754e51303df92edc15b67219e51c462d78c336cbc80d8b1d2376843681ca2831a0cdcfd3c0821c1c0412a756439c254908a6f7ee6f921dfcfe5e24b6c240e70d9d26aeaa91d913a284fee4f13adef745addd5496894df16ef7a7dd144740ca6fd2610c6cd49effe90385031193f2d8e09b0f72ad4d82dfb9c659a1a011239b410a44d480884d7b63682d62439a9632c4a09e07d98525f1ea7e1f718350e1bb6436fb05c30d5da770527a32123b5e20a3cf9ae6851a13c74de4561892a69e82414b1d00621d462305e49f050c06568449c1f85f10a09ac590ca2ab0c0a3bbf3ea26d8a7ca1d8b0fcb41f4105240a26c5445b7554fab9df8ed7af9a7e04" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "1abdffbcd857c1c388215486299f7fb3f4dd927e1ac4eab601451141299a1957", - "proof": "82855a53ef9287b250be27e90268e0524ac062f36dc1b5768cb4dac85e172e7a90292cafee523d4f3bf4374fea2eca24ae2f2c49d451aa32d7127949b5ef2e067c1a2760ab18bad9b47b3d41044b511f74cdbbfa0f026866cff3c30d91a03f3a607c085a7a8e42718d1af7d5955bff45a5a4a2890da4a541424b13235819171389e2f99deda8b9effeb61fc7afad0b7e31190a0d2a3bd15d5ff036d68c45030edd99841ad4347c0ba97f843748435eaae6f9576014d4a3359c2f9383b8aa7d0d4312cdd4d8523044c951bf2d2d16304714226f9cf2a847160933a2f8770880030080d243da85eb3b76dcb3796bd948aa0eb24b1c07fd09eaa438610b3a1253773c82660b1fa0c3591358c86cee97f4040689dfc7ad143e69f15715cc456f2e2ffa82a4633773e4d9266343d885ab4a5b0164c7efebd46075a60c09b559a48861f65204cb4404398394096d0f9d044eed60c67d2b1759de599835c3e0c7d258289c3cbf7df749a9f2d9edd623a377f6b3901395a8d78d41d1f09d184b9e68e13c94be8890c862e30961db269ca8369a91ffc37a6b4823815e79de8eacf141f456564b80d94c137fa24a76fccc6f4ed44dfe804e75472ffa7654f14e79ab20d768a6658127ffe3b219981c9661c005c85631d6ccdff33e5b6fbb4cfce2bf594105ce0afa3a35f55e50163a8de96e70dc9b1306b9e598944af3a576a45b71d0b472de937452b7095f8b09c62aedd1bfa7cf19d86abcf2e868263cc9ecbc4f5a34279690f33f8e6280b1ad4ff65a759f077f809899e80cce997368237fe23ea2e840785c4cec8a01467fa93dbbdff1c030c018ddcf36d18e14d7cfd402353daa977bdd2917cfb040c140518cff02ea15c476e7eef519719008660d75affa67c36e0d9454748f23065af15714934b4511d0456e4a3722c9737ac96af10f40ee9fee01" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "383c8ce8369c49616a7494a662ba26503c5a758d29d9462f18c483d25f8d6f23", - "proof": "7a2cf2612299a822c7f8e737c5859c57c79190df62a82b5bcf06ce579931f54a363ae12f4c8d405bbf370fa97f627d6e494f0274a9545d445f3348b16c03b549d8d68b9b68a39c5b51f116173024baae8895038036a9de2bb9641b986d10901112878203f222383d070176bc5759af92d81f64e38a0bf25e6f75dd343a1c857f6c8ed660a031b40d5ce6f01e646d83a9567f8a0a1719aea10d3c3a1ebd11840fbb1d44a985c1f509605916b69a16c3fd4bea418dc9a1e5c8a6fe4aeed96faa0ba787b9589632d771beec1782698ef7c350f24faaf71daacc5f2b3d070443c7075470467f86d29a6e9fd1b9d43dc2b18a18763efb568ec08ce8578764a75c0f00be9b3533bda7930c274c53621d5581827084c9638de2f41c5f225e5e21338f749e5202efc0efe7a7c00ce763f271186a4db4ae9572cbe52fae03c295c71b47542a510f76da40ba24f902b5990a7c96b67169e85c7e1a2d9c6208a52c7c58d30fced7d725662048c6c78137d1b1fb4a5524107bace16eb438c9e61466174bc34db4c4527f649ae06c7e3b2c8863d61c681f8b46ccd41d6a7e5c1dea80b3a2ea1a001da1e3c53c3f2be7f26d3beea053212c1c7c3159e7d2962b14c8120438c210784bb4efd9b64c288a340b2d19788637668a25e3acfb1a7b8dc2bf13b345ff6922c22674d83c0fa519898f0c72dabb78f31e8539fae15ba59a1e8a662ec1da028e18f60c903e25790a0f7b7727b77199f464ad60ed1fb4b2a0a3122469585b76a016e9cb9f7a806f9250e809ceae959c9dee2ebf03f7b877d9d78bb23d467f14ee8acd4a4d627f6b24177a894e38b581fbd36badfe018fd4ef8ff99eda407b1b681b33e988d8d97c26338358166162130dd34527375d55544822a9cdbc9f020afa3b5da98a6852dd5f20e7f5034bbde631f18f96a0e8a418cda0dea426cfba00" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "5ce6a024251fc64d43654c914423238b555b6eb8aec668619671d56301b04b0a", - "proof": "2c5d5e149bb09e70215856bb6197c08a9528812db0d77cf2ccf204771dd1cd125e33780e10d49db88ef7d9b0c7b2a39957215cb067df285d9d35faf9a1189d34d887e9c3c283fb1f13cd709b74e62d2829efcb27e9ea603b81d255116ef484726a9b36051b4436715ca4b371039fe1aae68e8daf6111d938c24a1b9d7aeccb4e51372bb2fb3a5ff12c371d5882eebe63697652cb729d39246b1c566a014cac0a0ac67754b941232da691e8eed1625be41e8aefc74656cef1d8803250e7e5ec0bd5bd724d80664809cff40192c45589ec233db245d0eac6e8a51c2572ba50a901263e035f068c9b7984c2775add818fd89bb95f0be684e084c7dcc6f025f00c71a26b886221104a7e409e5cedd80cd920bf5f480e1476c1299e1adb491d5f7f5bf88d36601d1df02e07512783a12f215b468cafbd9206fd9d7010e2eac49b3a192a518f2e23a7b7620c372494b673855aedb23c08d9c123332dd2201e143d9d687a42085cc88a15d4f58db32a47597bc1ffc2aa3eee69ed447514d49f759a917a9694919cc42ab1f4d2f92090d0231e0c47cd01be0b8654aa5f52b5ddaf943c7cc43ad5688b94955a7aadeaff5d6c994a543a1f46f8ed39676d9d3f6562984216da15a818a2a86965888e643434f67def5a542620acf51007a18b6061c59e7d1c508fde6735a0d5fd71b43f9851d0eb1cf889001316f8746032f80bf359110763c64e4d0000678a64c0fb516a971012e7ed0bdd6e2a02b831e20633de14da6b3046b4ba985ed7e011daa326666508eed70e0ee6ef7504a2e0e83a2be5ca72ef466a68a039acab4dfcc0277c5a3abb330b07a7e79e292dfd8b03f0db559f136e770f351d03d057ccd9023c00428e303449cd041f566cd53db2dd80652ee775020193a4850effc78520b383110a9a3cc939651560c328801bca7569cbe6f0ab0f06" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "7ed8142dd4481d96e66ef0698ba8fe158dd2b55631e165a75529caf88100f25f", - "proof": "7688ac15868a6c18ea97b55305d75bf6103e14c2fdb4b9b79fc30469efca87354a153941281ffbc978e1e2d5647dd9eff315fa34c55442772ba3e4cd2575731176b3fc557b4334122f0a6ac36340179007f156150e1d7b28d4790ade1c13051d7e17b2eb2bdcfe19694517a3ef1ace39440cd99e92942c51e98ebe618f430907b92c3858246c8761fd3605391764a56126fa27e189177653249d9394d65229042044a0212f8de082c54453070111446e1fffa464a729671136e5784411fc440a4f7b201f01ca6feff76861b047a1ef17b73606b5d90e4700940e38db1056930796f6cc42dcca9254958968510e8a7d719fe88495e46aa0dfa42a439636af702b68ef87d40efa3a85a6c673a8028e750684737fd81326850994b7c362dcef5e368623aacef08eb568b94e04f85c378eeb0a2feb8f59f07ec0917772adf5f1300b388ee04522818e417dfa223e4b4d7274c14f2402206c6e0a7514490cb2541e2864a0f3fcd3325d09ad2c06eaee8c98456682533e2c9e3ec85cfeeba14425d818865e971999dd8e51ab14a539a1b7a86bcf114664c4a8abc5031594cdb27ef51ef839252d4843413245718d7070535605e68eedfa4847263a053239549829e41d268660cba21c0621d44c1698a5ddf2bc463b4f19d554997ed6f0cc5fe3836d4b602fadfa09d36415be54741416b05f821a0e580f4058c36ecf8d145cbc43af027c15b943ab42c7e9bb6c039249f0715a79bed2725226f0593c8c06b4026f1f4156d99ad14c2f842fc009e0bebc0d776aece70d48adb70f0b0cbc8d02ed112e3b6ad6c913a1d67c60c2f6e65408e5dac5c908ea0bffe4e8fce6300572e6b6f37a08b2aa93ce0972a1d83b689545bdf8c54699541cad0343f4433719ce32ac5e0ff761a1a122be1fee6e31595166f19770c0167f1360ced70cb3487f2af4f24c0e" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "84cde35492d79e62c5119cd78d71cff728d1eb98612e5096566582ecc37da869", - "proof": "fe6b323b6f366270118c1a99e36e4782bfea9c3524f4b3ae12272a36a4f7784836cf2edb5e026546bb4b757e52c2e38ef1c9fca2819a1cf24820a560d802fb5b0c64715e078b63772dfcc3d94bdd5dcd1dd94be8fd102e7b7c383c9d14069d404a0ba3605f650ad7aedbdd5606fa74937103ca488b2f37eaa984dc606dbc4008ee7fb5c24d8835d3450c158373fccd4d94390cb88625c07c9d2fc2bbbd7c0a0dfd1c3fd009baf3c2e5344a0fc42466537b1e287b66d8aa7065b5d67ec107730f687e4b985486ab1e604925d1f8b73f1c7a500c6ae0be48c417ec5f2ca5a9060cb606b0e983ab0bbcfb7459385655814be14f007d4819ca8d05cfce96252c1c5dc6227c1ca14f7dd8d621bff4f3480fea83dfd27c8db87b888e7abc345aba1b6ab6a414d774c2d31eb7b701dd498ef29aec5abe9923ffcfeb34ca38f3b3f4737896f6b2450bb2f664801bcdcebd6f91615b3208f928853950612d9bcae84fe5730afe05d4e151c403a7de1a70e83ac233a1666452e248602e9bbc44b5fb2fd65a42aa309504207af4b296d6742fe10a78c30c1676c66e8b0bd69cc8841f4ab4645ca164dceb08c12da2b27592b4301367e0f4262589f6916bc4e0cc07cecd12100453eb27259b7ae2430b77fb7e0dc15ac9bde5c9de28af3ad9634e4a7d98f71a5c83a5dc21e415121bec53f1bd981239a85161c92b165254c1f6239f1a15a84e525e8f6065c3aa195d547d7a59ac64730ebde728c5c8e8d39b7f260dbe1d4e419e280127c47b8c4fb16d5a5925e5384df4d3e9e208f530422e9c24bbffda071d168339af7d86485185813a75ca85ff610639f4c8f076b9f06c7995b57fea805210ae3983481defa157646a02a4145a78824df9acea150e2a85cdd1eebfa37404f72e6a31079a8aa83ba827b8bd4998ff86096d8fb1b947809aadd219dab52f0a" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "8a55bb4ffef6af3f4861a95920800eabf47e4dde73d7a377c15a1ba329a1763e", - "proof": "96fa3c68445e2052509e116699cd666a6b46508955e8a92b1296d90c147bab33e250d14adcd0b528d62b44b4e72b50f5a201d0f649a805bdd34fdbe3e9fc1609c89dc524ed451d0520f391e6be5a0655f2f1a2e6be405eab9f9cd3dfbf6b8352cced29272a532802c1fa46e5da449b2c759b285061ba386fe5c7e9614bc58f6211f787c2545908faed335c115ce66559715f8a965b32075673834c5c1b5bfc0dec02b25f738f8fbc4472baa8b77d6275440c3dcf0b47b7c2ec5f7e983f9fd403fc2b2cf0f626c0bd8597a74dcdab495b65b430fc3984cea4f07b903fbba11e0d4e4f1ecc55b2646ffbada454c2d5f0bf8533e4db4a1da503e21a89369562301e446b9d1a2671bb5fe63a46f3226d25e70f916be3c6c92691bcb8834b9d0cb17f92ff61fa927fe577d7099d50258a5687c718629937c7e8fa233847bdb53f216e7c79cadd1846a71754da5bb39110cb2e5f2d139bef40d3a477f4a2a83f6f37679e4e7c19ef55aa3cc50e53ef46dd4cca4a0c92dae915e251cb890a4f8f392a6a4cc71240c727718342a8eeb64b681e564cf520fa649dd21fe6bbe6c11c41dc3ef0cfdbb54e11179b1d70db0d680cebbcc5fcb62293e1bcabd029b67fd36ce5183e6a19d535c6c58bcff8dc0eb1eacb907f92ee6b332357a7654428004208ca6268b42afa59916e81cc8605966f7741434cc86635094b7e2430032e9c2ad4a969fe570d80da66a621a93f51bec489c97f8406032051c525719b08ce4a0aed1e1d322525ab842b62ca0d0316e3a01947d24ccbfb214846ef825243e2af3d66ea26f23272b51b39dbedf4cfbe57bea73bbff61abe926b9413b330c59ee13d4f076bf65752039071d2bb3ea98f9ebd6b7089ba5fb096a77b8556e06f4279564b03052fd1833c7dc6c04ac70d3a4011a04f271a0460bc3c720cc2f19d71f3156ca609" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "d02c49d05cded62b2ea4d47efaf08029156907de62a0d694e62599185f0c2b14", - "proof": "648f5c57b451706460f7be2c748cf9504bea8956cfc1a2f695d8a4c8a785422cfc22de0378ecbfaf089aa516af6c20f66500b9e06542684f805648694fac311422fd78450549038f7e8854416b6fdf972d61c1adfb7743a8281008cec1d5667bc0746c548b8a590c227d751134639ae9bf5bd99a8228638ea31850c6daf5c3532e8c4ae656e2b347d545f1c740d9469b6805a39652c0de3b7abc9f4fc3e19a035d513e96855640f5026907f6661682bc9299a8d45ed3638874c0e835709f7008313fab33a614a139f7acaacb02bbf97cda4891648efd06799c9f8fbd545e720d84d0e50e34673f3dcab4a8d0e25745bdf49b98ab50a9a6af8717671faff3c6433a8fa685ae2d7c0e4548caeac4ac012cd768e5df4bf2b23cc9eb91ccd273ac18e6cb5fccf8512b90451602e70983e616f5c9e0796d844a81241054e0e5cefb7bd477ea571870d1f4e2ace1dc3d66d39be8e2c73b0b3ad14ff0c2d1a7c2352b05bccd7c5bc1a8dabb71a4c924f6248447ee38da46aee851d4f0b821c29ab4776d4eb42d719adfac3dfa5098f27b44ca94d4e89126cf1cce1a941a01ce922926039865c64fba7b7bfaf7316a8da07abf30dd5ee1ef8e4106f4910dec728b25bb73a4aacb53ce9b410593f95e4c8f58a2e6c5519254dbf8f3fa6afcd8c01d619206bad94054db41430aabfacf056c3d89df730623048cd0ce0bde9a57627dd3b76ca69aa2f6f521ff4d16370169195045dda5943c05698b47b0c69860266230be5c6049bd2e65bb8ac309f83eb1c22c1393656810ee48a6f066c337f0965e8590514ac0eb0f193a69a1d0c8790c9c893eb540cc57725c16a8bbc4a21ea5e0ee9e79cf7a990d49098e246851afc177ab78de54d3fdb7b969fd33b1fa9a728ae308066307e65a0a99deb918d1e30e446631c9fa54a4154586a8d8ed7f93b8f7e9700a" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "d8b5d7a3ccd5dd38ddd50ba5cf5417fabcdbf4fabc614dfd5efe895da1dba81e", - "proof": "84937554b81d485a1e58af91de8b3a40503ea2cb2e5fb8bcf41e1f816317ac6e76e10353fb03f305af521b9f3094d9f7a00ada97bf48b2a030d159f0cd1c8545603ba942af3b395b6896504a4a76043bfbad1cc31817041cfd2fcae5ad7c572890028571c16f9cb9bb777fa37bb3ac70072a0351dcb31bc98810a63b4ed79b692537f9ce64867248e746ef41d49ac1c67defba09a01e270adfd73e5471e4c00507edb7b874b4255878cc0bd3ea4271e9da7b6c1ab6e4e11e74e6f0c099d7cd0d055ed421daa263a56423e91ab4c4eb21a17362170861e8720649614564dc2b02c2edfa3fe5fb72ae54ab9762c3490becb3d947ec1c1c3d0133b3c4d7709fa6779c7f8fe643e00b5a20e43e042621e946f8847998265a30d1efaf2a34e45ecd793a3f53e5412c143a8e2422d8a6f0eb2ecce05cfd46299dd5cd9a682f11bfd431864a004dd3f425c03756ca9222c38cb99dbc1f5001e26aad245aa70fae4ce90e14222a52b023b4a25acae519d88eb663ac221af064ede553ea6b9abc0b10d45716dace45c4df42cea3946068c96409588748eda5bb3d09ede8e17f533dc9122a14c99a24516d230b5f0ab528a81b9dd0e184aded477fdb3e8547ad1c883478030c32cae64fdf2eda22756ad4072ffeebe3f05534079d7ac37a2d3500ea6b95502cceea8e9693806fde4a9e97329139c8d97d7de686c1b47dda5ef79928f71312a4962294dbf4c3ec7e191c2cb91b1f2e887635e730d69d236b06856239265859bac06972912eb83fa40047d806da242585e578843b4944118f2a0f45e6680632ccdaca8fbde58d7ba75e83d0c69a751be9a4a518b0043a9b01c168d2e1aacc14d1c4f8bad9f4e9a8b9fadd2d6d0ebdf3bbdfcaad392d67db9fc87bba841ac40a6c1f345af458324e50848c721eeb9d7fd43027ed8d6bd12855c1173093e99906" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 39 - }, - "commitment": "9ebf62f0b77adbb576dcf2eb952c24e10414aa5a30bfd96c9db4bd0888c3ea5e", - "proof": "3426109770adb75bc4b42fc1648dd9f560d02efba71ffb8cab4904b53fc96f3972af124f59ac06f6adcb975104925ed9cc9fac3ba2efabe445ed888df040e763dea84064d56b8cb1234750cd49ca3378b2766ac1075c4e4dfe348ac1eb3a285640cd13652c30564403881d8fed820756652e2791020e7c140bf0f7b4462c500890c5485c6ac275fd2aa86336ea8f330f1349bf750be69360b1c4b5269872b008d9be555df1a1fcd250d8e4abca7dd8b8067f9ae14cdbc4739df3d602171b3a03b4739776692b9eaaf26ca798987b5318aa5563045de2d6b0e501b2461a7e9c0764e4e59c8ee8268192e71860a3b43168e994fe345303504ee9e6aba10403231ddc5c8df5f42348ac5a61d635a7f00cbf7df06b77bcd36250cce7b85a22d43526408a7c6b9ca862dd30f9f5eaed849f51366e4c41718bb584093aa29b8501a376962b198de068fbdd2ef1f3e50d6e6133afaa3c5331de04885b7d989090c8ad5288f832bc3c0d37daf36e96a7c1f016d75e86c94d29de2eee0d86f30d1ae82a01f662e594ed3a1c69f65ae60e81a3c70374331051fbc2d231aca33d0aa820a624243b580caf020a952815d139d59d7716089e69aca7a7754d924e7dc7e7e90d17e6281ba627a468e4fadda68d817fa9c5c99f1a826f35f7573eb8011a59995d0f0c66c18e85e8d4ddcf361a939e653144d3e2f8c69728db84bd738b2a9d40c52faaec82544e0fdcbc33ba0ded04f8e38d1f5f41618ce88687a0254f1aee8f492988ed8bccbda5b781162ad834f0f199831eabfaf8db4729db460d29bf1c422d15aecdc21b758b2d55dad81dd7cebf964505b5b3548b298678205dc2fbc402326cff988980a8fcbe883e94400a1a139df3b531a1f1fcf17a1b72cdba3eb8ff86036c2409295f1c99bce7104e04fa6a8b5580c307b7bd76531c4151936468935207" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "1edc08b0b03d17f096a8b04bc96555647ab201bd5ba1381e142f9193d223b915", - "excess_sig": { - "public_nonce": "be3437222a70ce9a4bb52b829dd1181a680e4107ddcf88c5319484c7289e7728", - "signature": "9a1f66a231db6244b75b28faf1204fd06061bb23ab90a745e22e9f4d0d8cdd08" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "845fbc8a567f0e2aea77243de9109e64ecbbc3d3c21c8f9c9c556a6d5d03fc1e", - "excess_sig": { - "public_nonce": "e0aca1abd8ef13fd0c3578c7df50dbb53276fc992da6387697ef17dc0269bc40", - "signature": "c9971c9b52aa9b14e1477dc7f2cddd2a23f9771b0cc12753310f28beb4bc9909" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "8463314dc85402a5c5ef9ce0e6bcbd863f976de9946634eea87deb412663924e", - "excess_sig": { - "public_nonce": "44266aa446cf31558b77d1965655b553e4bf86426aefb0b16f6c99878017f027", - "signature": "396970c9c5c5372a7a3e483f08fc48bb3f70cd5e035ee9cb10a783a9bb2ebc00" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "b802bcbac833892b762a5d621cf16c7b9cf18ce09f0a4b59b1c99ee7798b9277", - "excess_sig": { - "public_nonce": "76432d5d66ea00c4a6bc3f650c765a3cbd8ca647b1abeb1957f86488cbd8c000", - "signature": "f6cb415ffe12ad31fb3c7b44e458819adc0b1ebf351674875eefa2787d77790a" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "ecdb5ec5305fa294a52716ae5e10febd90946f09e91d360bc64c4ca1e6f4131d", - "excess_sig": { - "public_nonce": "deadc0bd69cb7345d3d1e9eda55efb2ce541fc0c940498862d9db0cfd837e673", - "signature": "4acaf3d2ffef9801b95b4c1f86e432a22b39a12cee608fd1fec5097640015507" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "a242566b435439edf454892620e74d3a608b8b41d28f45dcf170565c85650b22", - "excess_sig": { - "public_nonce": "361d72fba7799ec7f5b44509b0bbcd1bcfecad381c1d5ba9a86ea554352dc94c", - "signature": "0e34b2ebd8ab68228f2906792030e568d95ba848623af08dfca8a23f54fb1600" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 39, - "prev_hash": "8d3bc05e277d7cd61028a0d1e3c5b442a5985ca089a03e17c5e5345c184390ec", - "timestamp": "2000-01-01T01:40:01Z", - "output_mr": "0b86846c72890e738c0ac0c8667c9cb3703ff62c2e49ed6d40a24164c1b3ea73", - "range_proof_mr": "f8479cc3df43241008b52c745baf3e1acff7061d8d7ab2c466fee12f6892953f", - "kernel_mr": "c26a21c50039261c87e95d59e3043202033ca51e065ad2dd9ba73f93d585ebf2", - "total_kernel_offset": "6b63f7cba582fc56241fc8f7cae6bf6e74755e5a3308a467c6acde163d9f0e0f", - "pow": { - "work": 39 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "18856f0c5d40bad05475fc47e0f15e3b96d9b2e3188a62436cc68c5ee5ddb850" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "383c8ce8369c49616a7494a662ba26503c5a758d29d9462f18c483d25f8d6f23" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "7ed8142dd4481d96e66ef0698ba8fe158dd2b55631e165a75529caf88100f25f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "d02c49d05cded62b2ea4d47efaf08029156907de62a0d694e62599185f0c2b14" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 20 - }, - "commitment": "eac6bbdc2c2be436e1df778ba20342b4366c81b5f1e450d199698bdc108aae1f" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "08d684457d7d8c5a63b3e383a0a99eaae634d450e3c88f004111f4a1fa795430", - "proof": "80e07fda3cfebcdbca6cf307ec448f49b6ffeece6844c01d592db8a698444b5006979fea4c0383c02541c5d035c7a376f477c6a519ce3d14ca5b4dd885f8d4450ae5c70bb650daabac1fb05aabe59f499d603cfd7dc8798d7b537bf6c39b5a3bd0f31a734e28ae494d2c52e9b6837e80be365a71405e21a9599c46e879f4952acec5908c8b8d37e5558ef52101f998cbdb67b68c5136ccdc00d1c68d6779810a2b980712d068f30274066edd950e304c43337ecaced6f810276b836f1b8a4a0ce7fd7ef30f181cd7e0c291a01a9f5674cfc161ef32196ed8dc2e949512a92209ceafbe72d5c5eb0470d17cdc987448e5d2d4c176a9ca1a2bf28ab30442f8776e0eeeb839c809e22e3ef09174c007f7701958aed60b683fda41511b3f9a938366589b408bf770e7aa7769405fd4121a7d61cf65291529a97f3a93a8e6cf0a813158b1274e275d262e3beba104e37040a6690e5aca7233c9739ded2538aa3d31675282933ec4d01e7b024328c444553d7404672db2b54efafa81a10aa785bd4f1e0aa95644ca72f8ef36ea6bf14811f0e8b8addee5f3c729d37340e63e99fdb87132bff2d4fc5ce590f79c5dcdc6b51763e30c0ef19913df14730ecfa11461c442d6232bb17107eb7e114411907cfaf273f91e51048202358d22e77c79f484ca6f2429d24b201888cf6056f55e55d57c177e53d3eaf6d3b4d6318a2adec0815a6f22a2dc0abe31f0d00513a71c0fd16248840b8e22aa436284696950a88fb72c1c706c8af35cc2fd5d663454db1f83cf46aa936ba33fb9938b483b6f5a5bf47e727eef3106d2b24c8797126ec658d46a703c98cf3171d948555266e102e73b5758ecc003a616b82682efbe3fe7a04d7b2b885e76899ffe6de5ab4c6e3c841c3404026afa15b29f34562ab9f2a7550625a7226e2699a652c4452d0b840fc5eb0806" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0cf7ca8b1eeb45db0cda7bea9b39d77b07426e10f724b8854a00e1dcd1f08c09", - "proof": "e6499fdab2fa1eaac23879685cdf771c77dffca6aa99b1701771b2518378d33f50d3dd127ce24891a5ae8f8d9ea6734666985ff5c6b881a3104a58e041f1185610b7f3a989b4ffbb50766c88e118ae4729fac2414cbd3ffa163206125d0b197e9e5c57642539afd0ff2ab1640461330260cea93fc79f64fb113553b0cae3790cbddef4f07445a1d9d5fed400caeef9442a59ad985f43b4e9fa51fcffdb709f02b66bd58c1791bc36a71c12c47b55ac63aaa1b45627655cfb9d7b205f9b30cb0f67dca0dae136591a1e07f84b7430109bd1bb0df31cfd38a9007b9122d00832009e2fc326c05417b49dceeb6aa2048ad71326ea6f1179a72f8d01ca1ec0c8f639e6cf5fc4b151ddbfea17284600b1fd0f40af3c84aecf3ffac0b526807dfba3052cc5f3ebb5f847ee9f8b2ede0bcef6e2dea117b661a69a1792a7c5cc0d2e8b34f8bced4a55f5a5f4820a9d4b27b590c7f59de0f2c86c9ddc2b6a41108a1d202046dbc1f89c72eab9935460ebeff61dfee77c88054e1cdea389636fa911d9bc79d028eb5dcfe0a55bd1e81bff70d7257b4caa3e44117a405f8b0a50c3a2c2a073429ac00acd673da4f2fdbea46ae2eb708ed8ce3f362ce42135afd52881a0c1294017b1eeed389cdbf5976a732548108422f1e09f4e1680a94a34f98a984fd65d9e6a3f201d811653651fabf4b403a09152273ed871ae3a8d6c694e58be979f188e98ec5f2e1b06f33116c500c7bc6b29b6973041e0fa8c3d29d4cd69971dd07214f46a9a5445760c61527ce0a888658cdd3a015dbd95438441f06e0ef60e4f0d0e52e7884e97754345184c1d83e5de453f930b9f36d58c3dab060b12d7c65b704eda65a3cc097aec3268efdf56a229f2d2cb6a5df71b7476eacb295adccc2b0583988045fc02de5df3873e035f839d273c95da79ae8d7ddeee2f7a82ecea6706" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "22592116a769a1dfa9ffce17aa64c8995da0f9990adacaa0d9e4918d8bc0f826", - "proof": "fc9a8d7df9df23c9ba510e79378ecdbf20b3f3164e4054f9b0a43bafd321f318d6ea164412ce6c4db076f4612d38e1bf119a564067eba9bcc811017e61ee3635b46189921d0d013326fd16f24e82b031167201f2289f8625ca45f84fb8818a1a042049ba787bac2bd2ce57106e4935ad5cc0feea3a45b4535314a86b92ccdb73311a6f8e5400eefbe71942d47fc02ac2d040c2b8d88c9f421ee581f56a18780265dc9703994f1ebeadb16305fe77df3cdda139114e1d8640a8c8159269512909a369c377b85f0c1b66841090fe66d32595c9064aa8adb55f2e52ebe921d30f05829a9b7ca48e5fa77e6bb74952840c0ec8ee8ed3a6c50765b1b4a476f62f4d6dacbce973e9789cfdf0c4902c91fadeb4e540bad29fa95ccdaa66784d8027c220c417c29755e53af4332e918137970cd7c52317d23db09cd9702a8684585d2a3318b37dd2993956c8b381e4c66d4954621b16ba9073394681f1d91322c688351474bba8c37c850f24a5c16a9f40857ea4d933825b0bfdc3975e7865f8480b733f3892c1f20281982f008db051e3c52433569e12c88a8bee8480295dd9fa09bd10a2f14f1a49c249c16c0da9688462b12725ece86d4a7fed12abc43538d6dece4930c04b34b39bf6c077ab599d1fc071c648993b014f05d2bb17c93325353613453e9c1674b58fb8c853adc4123e8c9791a930d2ee9fb645900117b60a07534d60c80169221be6d1c7cb0a658f643698bc0a4b85e71d91a30429ea21bdf6618c66a0715573c073926ddb7a8135f38d92c9a71174794c60692a8c614ade7bf37367c4cababfe162d5f128bffd06bb9aadc027d0737e91fbc49daa90dc8da8da681cf817b296d42098da755951c4324c1688840587f413ce9df49667dcc966afcc01ba16ad24f7d08daa1d3bed7552064cb859c992072eac163e03498f9e4d81fd05" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "38b0c1d73dcf218b40909a67b0f263238e455d0192023e3db0d5b102e8d1dd1e", - "proof": "265469c8ec7f8d854b3472ab04dfba3d76c73bd17e450b9d4a86134230c38651c8a30cc8c763d97d351633778d55c7b8c6a559687b98eb6ab5ae1c1fb81cbf21129db8acd1b7f238ef17de73673af486e5dda98c6125fa0612d32305b7f7ec13e4adaec79285b0215bf743801d149b72cd999a92760dd464bf24f25f25af8700bfcdabcecd1b4dc69d8611eb8f5265a1f0c495539f2708aca1320b1f29ef8a013eaaeff37391f1cb24f0aab162bd93a35d06affe67429dad2cc605e3912ef60198a2b8e1ddb466b5687a563b8650d37b3fe9a17dfb75dff2a4209c1238b68a01f69a15a268fdaee3905e27f5f96fe9dbb243d7897bf69f17b49800feda09637642c0ae6478886d23c380bbf51ed713d7f568d55e779ab7bc87ab24907d3d472840bf653f0625c337e84a9e4384414d1eeb57167873fada315db54130d473d4335c44e0d27de97c07a676be3b6f84d70bb9be28055e0e5d02c1b7b7901bd54e2c60c797133d015ab92230d0e6defab8923668a125da3ba0cfc81153c997018a7e0e9fac514fa842ab685b1e3a1a81730101520849525073fb3a30b927bfc3de64aed52097615428be6483944d53a99448e350cd390ed79e88d02b9c0aeba3b41d3ac84a31907807c2cffba09ecb2b74f24343d43ed2aa6aa626d294d4ee34b10a3008c5826241331ed8399e5d858edbf8e436f474f231092839e083e2a849bb4d04a381fbcbe8cdb6c645a7557a9da9e57b070bd78baf3f8ef26e704bc4f662588cd4c995d34d8f8f44cc7d157a2a84dc86afbc81627741dc94b41aa22d0fcc68a49e2a86b59e3221a9e044f7a4bcc3c66f6a7f53cf516af4b5b6344e6d17835539283be5eb468d9c32ad78436af2c41f7509c8e4db5add232e7e8cdd99620803f6cdd68a706ea066abc31f034c4dd9a93826a7e3b21f1c38e0c5d149f1200702" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "5845b58667e9915f4ebff27c4cbd2ce51fec7d1b709ef992622e9e0b0d11084d", - "proof": "c8ea3936dc2cfb2ccbd3dc7035a106fe69a4541499ee75e57faa1363cd584833f2bfb1926f4fe957e79f626966f885bc55a043c0553e080407beb6da2f80e471702431e379bb6bc03d5f3773b0c689ea6fd59732647433b827ffed13f806c456d0599260b2fad9dedf5eec63b6bcc71c262dc20d29bc318360d5bb4d63daa9729d354944664c45f8ab80f1b4274cfbe22664a5539cb65d7d87fa09cf8512660fa39a6a71e0a5db5cbec7e8e4f98c53b4faa4dd4e48def0b943b098cf7699f101b3b473ee776aa99823b7e622c5f5290f642824955373cf3fad9e506803580606e6d6c6e13db9e9743c584f0b0938822c03643b49972480f3ce47d0817ce18a26c4fd99cf7bb8929327bccbf76bcb06b9cbc2fffdbb1f6361dec5e2f6db68bd6e0cbcd63e6e5b75dd2cf46fdea8e0f1fb80c3c4833080fa191ead973f396c2c274456443ec960f88b3d2065015e293d0b9741abf568b6b8289a149b2e2582cf54f0e9a832e7f01d8f5c5c97bc94e714a865a1d4502d7bf0947e7824bd7c8a8d16d82ced71ab6b90bf5e6696f5fff24c70addebfa12a319dfa78e95d73ea3a096ee4499f7858af4a5f565e7d525a464d3b0ae29bf0ebe4d5c587a61ae22b934f6860a521d46d4281d5834a5a2e9477acc46b7aa75ee719181e013b7ed9fbf4d07212992e1cee9813e318e84e95dad7c63599a98977ae95cc2cd7da68f10103766706cd78287d28d818902a288f97591c5412e166d60af769f042f86a7048ff140b34a8419533426d39207af59957a745b025497bde5d3b4a9af7aece1c46e184537a1e8e312b4661029e1dd7c2f013aa9c68229835de35b50d89547f3e2734635142effb6aa83333695b73294029d6f15235ae1d538432760f1153cd0b7b5e2a060984b61c8baab692c1c0cacb987992630eb2cbfe4fe8b278c1432296803db504" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "90e10914265cf4a761c50caa09db3085306f9390d6c0e7be8a9cc6188425b75a", - "proof": "a69f146a6e9fe2f07437f41f49cba51cfa0ab825b99bf88e5a1138156ac535101069afac7e0eea930d7d590c6a0ed026ad4f5536843131f6b4fbba2b271e9121769c8ca603fa01c965c3fd3e53e8204f23b225cb279d40ec6029b6cd6ff91e0a8c402a37f4e12ac4837c67338d00018814fad50b9f13d179c1125543e01b6d3f3a4b16b03e7c815e89ecb70b8f2ac14dfbca57e52ad79c1ef89088877ce8e8074517c18506272e383161b6910cad1b90acdf154272a57aeebb71442be2b53f0d146f604e4190b8990cb0a05e1130703835cbb2c1247181e8bcb76e589e85aa03b27cf620048c1d2d742ad38b4f5f1089a3a3d38165700d8bafb2b285a42253298e6e1cac1f9e31ebab7e9f88f24c1d27ab4c0b36333e4810658034d9b893a33e545b2ed530952cd6a0d73535e93c9e81c0624e912a8bee6e2bce8481f810c96a14be0025e1acdf07bd170b9473a393200abf974a3d0045069af0ec195c62343794c6f331c91706ad389e65dccf3e9a96d4b843adcfb4b7d53363dc414f4c01680a2d13d4be33989b406dd20489c8d05faa66f0839b6d904efaff2a6589b12e008adfda469da57e5720adf2fca065ad3fe8bf8c852cbe302d081bbc475f9c7f5ece5f16584d7b86b99e432bae0c153d76c5abcfd9e772d70b505b3b1d3b79aa07d82a53789483c777c2d7abb5b0241b1d3fefdabac9505fa61e92a900ccb47a0950e774c60da82385bb1af2ec9ed0b0cd33106b5b5856919bddbf63e97c519535227800352db79d06745d8767f8093de5ab78ab5052417b372c4886a4deb92537aa62432e57c43bfdcb3205b72c8324ccb13b44a2edb8e48d46a1075c5e3c8e0c06ee3f0e5bf3cb3d57ac450740ec2e860ff513d0d6043c3cdda1513a9dfacc00354ae256e4c8746e74625f44299ae192eb65425c89282c6cbf69d94914f79408" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "aad0fdf576af3eab620d844f57e7425fbe3dbbe1f2cc6182c98f55f16d672f71", - "proof": "06aaeb3484a15d5b196e219b5aedde7f4e66f76b9410aec0a276491a25418e4728c10cfa08252c87a676d506d8e8890cafa6767ab8bf217aa44ab715c949d8428c54acff0cfb6e00bf9bf2c4a9053a50eb0156a8f6284c3e257f2e4246ac520c6a298009318a05bc671e1d246078c98ba79d794d53302eb5bc356157b9f0957d806f67f60cafb1b76c02d87c66b30d46345db88a0ddbc4ed0598bbbbed31490c651f123eeabbc1b0d096562077c2e97b39863fbd5398a5a6ef0a0902a65bf4011ee26145b5844d1f2132e967732f4a79e23c4f64e3b3e8ab50530d0032a797035c99c2093684796d8c41feb61ebf1acc743f36880ff9e4a04814774e7230e611144e9b2001295072cdf538a53c8fe124747d63aad327a71b858ae71d9723cb1b7e13723dc02cd1ef2af80d97b63dbe1317aea03263455c9d66369f6f51091d60c4d54b14d352166ac47858bc60bb9e8d55a91952f222c51b5da20a4f0b6fd50edaa211f64c3aa12991b5602ecc0c0ee40f2cb8df358ab6be2c4e9a6bf5bbcd38b899542e2ab2efba0bb15a26b06657726622193ae5e1b414ee7d2262bc87235aa4ba39184461a5b09acbcb4e8b79e9dda6b157feb6d6c8777d9e536ba2cfce112a769483a1af2da63ebd81c8a28ea078c258ed5b37427569f7e77968d5605b47c43727015e2fa4231ce1ee8947ee6ad508f06f8897fc7c7e2b12948464281914d818d09752eae172329654cf8d6d1ecd5c4dfe25e5032aed35aeb8f6ca852f4716953857eb74f8570b2c28844fda3cff1608f447e091afa5191c0752b693d432dade8ca4c64f5497dbe99e78430e671773a9859655b7dbce082c5116419105473f219c5ae0f0b090a25837a57f9bb562307ef162e503b3ecb2cc9320cbcb3605565c2af717372222627ef737db95069bf160f51e11f254db61969e55cf2a6a00" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ee7c8c26b2ece933e6d6da03433dfa785b0278f0f2c862b60f7db2cecad0ba37", - "proof": "e83c1fa8c7bd2481083e511f234a15b56fdc6a120bf35cff4238afc38d26a85740e9ad4c45b068bd328ff0c1782c2a1a4d73bc4010e90bc822ec15225ceab537c20c542bf1256063c8c7d04bcb1a3f89f516c35e3b335b4b04b285412b5a4b326c1e2b669322d3066bef21265db0154dc2ac757e1d8f8d3d60d1efa150e4dd71052be95f260964d78fc78dde1f8e14c0b02dfe52bda76d56fd123f13ea1d5c0277fb60c29a79f727c068e1791b091199772b17fb64b9ead7e1dc042f47f7db0175604dbfb5db5d3d6e11e931a256558b5d303174f276f8e69f27bdc2d2c2c5036a1579d3aef8e2383b71dc539d4823cd3c099c016a0add8e6c28ba8a1ed08a66ea183db6ae03cb640f722d3f7a97de1a7446dbb05d7844aa251006cc109df55ee6a22289df7e6d30973522ea16cceabea72578119cbda0704b844e27b748252aba0af53892523958b2c3883f3e3ee19eaa66826b1b731229fa0e6d97efdfcd1bf6c3bbd520dba25c579d050ec56eba62e1a6778dcf734091a3b9fb95b9e66d7baadd6ae45f7364c947cd6c44f8fa87d8f866431014f89dd48188fd547dacbd40a8dc5ba234e3a02e00d444ae81ddf974cc138c54ade86f9f6144d2956e57fa555e8ca335d249832ebaf8867dc7ab2b42d29445790250456b6ac7b953874617501e82dae08087d49eb450f0f66190f678e3e6106c4cdf7e7d2fe2303aae32890c809b580d8cabf592ff2e0e3ee1afeb49df567b58fb71a8f7447144452d9d7214dcf7df157a927efac750eb5bf52148ce1a48c9aaa1df7200234a85b95da2073f2889722d728d4b0f553b78190d2afe879ddf78d707c08c261c0b6ecf35568916d635bc1b40b2ed88199ff7da4b5f042bf952680e2734cb2fc9f5c9a1386920055523e9afe71d0c1ca6d7ee9842ad0976eafef35465b89763e854fd2db5311706" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "f046f1eb6098cff7c90320d8a2866c0639c931420f9ec7dfbbdbceae033a4f47", - "proof": "a4b0899933f74b38dd3689ca7185d5fb7761fc8e253831a03dfec02b63b28142303381512a78bee11a9994cde9c3f191b2f2711819aa358dd8c16e41a15e7904f681cd0c2a5ec396a5ad6bf4a71c90dbadec20a69fe6d7b62ca4925ef131f82c2e84d6e2469a84e0d96342b21462a9b41a57dd15924d010adef9e8df9099615f83ad71b2c7b37ca8583b62ac542e71c0092927b22c96a5332902d91585c8910a5ea5a8b39fdd44ddfed3799068ad48b3e01682b3013440a7e2f9d6daedd1590afa096a4bb71da7c56f85256b14e72bb9104b7d0f73f59a7201fce0390cefff0d301501be3485a1e1f8d36b814869fe7d0054c391451da85e8b7b709cf53f7f5ae494a3c63bf7a5a29d5a99cac13540f08fd2a700d1dfd182fb2e03fad696bd76a6c3af43bf96b074322322abd5d4b6e70dbb3eb8da22c955050b010e6da4624ff08ea97b00d183751f9d01a09d93df4e299adb97bec6da043bc5950743a58b2800e37dfa322cdee13d436029010702eee8d8b71288c5061f3e3d801bd9de687db2057ce0926a8d129b24a0cb5f46c77bf76d3139d9f6a4233ef2b6b1f8ed7e0620f83efe59e097fa7fdb9b7a4cc2c1c968d91ab67fcde489412208233074855b00cea51edca06987d7d69f7a8fe295bf2033d8d8cd31d924231c48682d56cf7198aeee73ccda3146020a0ef476a0b4445a885604fe001c6eee53c11062c050126096f2ae3ea6d8e0f4c4ba5f83fbc01889a02806374163f24f45e12cc95efe74a8a9a4f6db001cdb1ccd910e2c10c4ac75b2b74444d7adb60919e6c8cede3f1c0c09ab57334bf95b4121f892e4161b456b15bec7dc296ff3f35e6249ca87f8392fba8e196ff78bd03e7744dbce5116852427492f62a4e70ff0f45bb056534309b1ddb619afca2948b3efcfc32753e6cd501c6ac27b94dc3cc30d51c9ff465709" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "fabfb02c65b708f5acd5f277e5c3bd7861a10d5eacf7ee03f9ededee23653025", - "proof": "8cb869a855c9bb32a5c421f64710f646485746862006dafcdf6a3f13d551710bb4bda30f49379fae768de523b8f9f7f3c21cd021b715e1608dcd160254758414a0c6b60fd3a5abfd8352d51d5c6d8b12bcf010706d7123d8edbb6ff4faee7f0ac88db22f55730339f470568b8d2bb4e274bc865ae6758676b6e31860b29cd747c1f67d6eaf1eb0dc016f8c4b9849378acfe383951afe193a7b85e923ada97d0fe5b660704e530ea7d6493ea5099c873bc5d7c11ff3184623788a99021f9a86035b9d086e2404256150a680cbdcce4f3dd0f40628445f45763857cb4bf71b090618330c47c51b4f8c95157ef8743dc00aad76c58f435a50ba1058204c552a867fb868cb4d2a7ef8ff8db4c260f5e59823784264afcfc104df98b762243674d809d49b0db44038e7a15733ba466ddb304d22d1805bd8ebec5643ac6c5baebfb21e8c5dd12f66ece1860c7db62589fb844034f4a52909521794206339757203671df2d2ded63d15ae49927753027e8c16739767281fecba3b7627c41eda8348c51c767cc790843116c84989de7c69bd335df28c93d211fe0df8b368961b9ad67f0654a4103ddf9234fc26a84a578eb8a81930cd8e487d04afac981c714bb980ac57f43c9a9cd84480b75f5847e71484dcd5432587cf5c82d86424267f9badd7e91dc842055bbf2a8646ec0995f7914b93e9499f62740424919c0d939af18d03633d4aaf07b45f1a50329d80d31019da961e6c188993d5f56755853c709ab8acf141dcfe431cfe1d3fd3c52b18b0869cf29e16615c26c55e2c8c06e4a17c3e6c8f67c47e4650d78712174b3b99bafd15e556df19b977c1e9ad4250b70541f6f899254471cca06f14f3c1bc9b2c4581fb9b5413a6d1c8bc3b44a6f15dc4aa6bf48e033f82aad5f17c89d0b6fb7ef2093cd9e7d43fc083b6f89248a178f8bb9aeed006" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 40 - }, - "commitment": "b06c6bd68f8831a08f806c8e7d52bcd79a8637afcd25b6750382face96e0896d", - "proof": "6402482307dbc82e94be6610ad4ce72868568e28e6dcf50d32a008d90462d7021ad4bc2f36a3c6042d9ffffd5590c0e9dec696d914d6ea01968e1217ef4f953c3a99a2b080800aefe6db05f8b6b854976f719127fe787f1d1880a15ba6bc680e848f5efa7973704a98deb70a9fbbeea116ce25927b8fc897eeba16047062aa3df878f9de79c781c3c4663945d806c2a43a0a07fb095d99043020062a2868c806348ece7677a3bb57506adef78567e1789b02c98d42693647ce159890b1e7630ff8d172b96eb300b3e6b3a0668eea19e28ad55a298d42817d06fcb8b8a402750f003eb954a7ca7aec124b86567b599a9c240a53dbe0453b29fa82c066a3b4356faeb0c598bf0617ef96bdc1eef24870ce7949f93d399e71366b2fa14dc638f5490e1ece1252196f866e8d4aaee31fe87f198ed4e101a401525dcd59a753b8fb10c47b8d244690b5c2b7a1bd509856617dc4245775097fb7a7c35884054ca36b7dda9c96cac8cc011227d0cd83d6b32388d6ac6e262b5947f3448718d85cce116128f608f310b1d5fc1dd36eebe3bd02269f98099d91b2b4cf359aae8f74abd3088c7bcae26f6e72a4a5403da877d2f190761eb4172cc948432af7f141a233863fa009b35147a94e856fa52390f49bd9830bd9d8721c91d98c068326c005c6f10ef6ae5894c39f65f712e117455f0111c194945b7247aafb41788844aa890527747669992955656cd9b1d8fc2993eb79168ccb169248116a1d63e89d2c564631734c94a906666364fb9a3c60228fde34c2e44136b438dddadcc48447a37a1ffc4e1484d8bcba470ebc37d6fc7ae458329fd61761ca209d962980cc67aa44ac7e7de7d1b43098991bdb1f4a974b7f555f8010ece88f10d38dbcb7140dc09934960a036d1f9a811f05c78dbb7b2878702d41c859435267f038aa0575d07d3d658b0e" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "14f7c02f3a5171d7124aa5a06ca88e0fa7448a0c319feca3ae133f0d4f3ff318", - "excess_sig": { - "public_nonce": "0c7958682ae13fd0e3c6f3f1797ff775523a58d2f8433aa1ee921b1eb6cadd50", - "signature": "6d1a9c53cf203d224f72c6a6d961a23734d3156b8c17c95bee866a180b6aff07" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "5438367145ca38fee3186812363832a29bcb178c8dd6c6ce182a8f1ea9a4e263", - "excess_sig": { - "public_nonce": "4c9a2d8d68dd6923ad1dbc8041067c2e2361ff2ca22c000bf5eef8847a151420", - "signature": "d8f8239dbdb36922af952d122e9deaeb37132c073c24cc75a3a0d23da098650f" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "98f293cdb65e528d8a767d8915cc3ef4abe339f1e98760903e8d7c7182d9015d", - "excess_sig": { - "public_nonce": "fe64bb366037578874e2af751f1379589a88ad0bbc92338d9ef1765995c7c45e", - "signature": "a997b27250e54d87b2db8b0ef1493a075ab4e298203e726202b3033e7ca05b05" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "ac45d90ac3444ff36643149190453f96f3c38281f7d24cc768f91dd2e7b74e03", - "excess_sig": { - "public_nonce": "f83c452ac081e9616f4d953e0b5acb729ba39d36df3ec22aeb504d339bc75175", - "signature": "6508e05282f857b4a7a479f773ff800a66d6e8c7f2e630eb8103aacdeac1300b" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "c83dc651266e365cd39978922ad1462c914714e0b639c2d8b24504b84433ef76", - "excess_sig": { - "public_nonce": "1ac63424f5f72eb31c95264f4dfed423c34ea7ccb6fd3e8a51f1efe19dfd7f1d", - "signature": "cb649c3e700860db2b6db3075f345886898751d36a24b05de7b4c63e67c8570b" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "4c23244c7530100c077183d600513345a38cebbae237a5f29ee5aac01bad2036", - "excess_sig": { - "public_nonce": "70baf473afda3c05360962bfb48a2644ae7fb27b4caf78a6166d7d1d37ac7710", - "signature": "005ad0a4e109390ad26e7e284771a614d8d8802633e493ca7681e0be9500c90e" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 40, - "prev_hash": "5a62866b26e125e346bf7268e40c1b67540ae1b16d16f80833a92cbc41763c9f", - "timestamp": "2000-01-01T01:41:01Z", - "output_mr": "a68a699af211c981f0e633c2b0eb5744eb38684b317d931fa676e48ec861086e", - "range_proof_mr": "2114fa1e3040faa8b3d4b4b039b6b20bb80cb56984734ba63a3722599dc8a6fd", - "kernel_mr": "29832de71af3dac8a32c8d1033acfd7cd9eb41e259daca2569ea13ec62c1b470", - "total_kernel_offset": "f077717af828542517ff0200581e16c0cecf94d52a29410362adc9b44cdd470d", - "pow": { - "work": 40 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0cf7ca8b1eeb45db0cda7bea9b39d77b07426e10f724b8854a00e1dcd1f08c09" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "22592116a769a1dfa9ffce17aa64c8995da0f9990adacaa0d9e4918d8bc0f826" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "5845b58667e9915f4ebff27c4cbd2ce51fec7d1b709ef992622e9e0b0d11084d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "90e10914265cf4a761c50caa09db3085306f9390d6c0e7be8a9cc6188425b75a" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "fabfb02c65b708f5acd5f277e5c3bd7861a10d5eacf7ee03f9ededee23653025" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "028057ba28d953a04e41c21c34c377db41b257121373b20f74056dc569d61b62", - "proof": "729dfd6aeb73be47ee9e019a728f87adc3008de89b85a17b4e6ad8d829ef1a6c8685388c65f93d8e982de8850cff7fe4fc00d4e132a3cbacc0777c396003681c1e8f555b5933f6c9abea5c9ff4172374dc1d33429d01fce7789c851c8537551d0456b152ccea897d7fd69fbc3f6dd3c3d2e37a1879a5f044d972da51e96deb7e0280bc8c87473ce33ba8cf3a086cc314d69025b9649fa2c1a1d8235c3eb6a908e3e225185b006cc173085ac8f41faa809b13080d6b18563559980163e5f01b0e32ae88ee24d020873a50cbf236d3fe78a42715f4f4a5c12dcaaadc83ff067002bce7f7a442b775aa5d4382c718cadd2f844a6c02c0d749caae1df96062bc14578c625be0753bb9536d464740cd1e3f6b1c1d557ae990f53a5c5e43fb8af97a0b6470e68caf5a00af468fad40c6510d2f8117fd8417692cd631bfdc20b9127f56fa94b9c426c6d5ab25de549008873b764783016125eb31864030a913778697128659f845c328d6bbac3dd4489c2a74f577fc72a1717c78e4848ea4d28db05a05bc13d302a660b0d20e3fbf4d401e9049f9c5408fe217104de6af38b93713397ca8a5cf6caed8735155b8f096b27e25d5cb46563dc01cdd02b6bf3db411b7663fd2863929645befbd2bcb14f9d8d2d286e7a30498ca17d897dbaf28d3dd7abd3c08cce356bc2239dbfc470e7cd514dd22e25844060345410a12a2e6f08fefc856129b7e6d351ff7b8c1a67c83c00ae75068e7982a6d9853ca2d5902d96c75f6436a1f62ad52e8de51a360ce058f4445976d1b3412a72aa913d727bb879f6e4472dc8acda47e9762bfc23144d9e86850294d8841f68b4043654744b3b02ccc8d097fd5cc99626111da67078931ad6a4bc8a33498774d6458f514c9ca0df4a1cb078535ec81070a29ca8054f37b039736a99b4c764ce2bd1797f651a6b373f37a09" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0e1fff2896c17cd8df838fc7c7afe3622cc87099e18618a01d7286759b523f7f", - "proof": "a876187ebc82cef6e9d31785b1496c09253b7d1481218a155acc327436e4e1148aa46c1fc046bb94d7d8cba9763a5d2be153548f739c91aa5015bd1b9fa26f4760e9bdaccd57d9000a480fa46de5ad750e1b5b6be4780dec146dbc7fa8c5aa36dcbd400a05f30a8369ecd0dfacffdd3317e01c2162256dd5b92b900906d8155e6237ed2eacfe748617fb4083dd752831d86e718dbd60fde9dc7f50462736e608729a9c0623089260af1373b9196a7145d2db3f7964d1aefeede303ac8c9487066a35f776a8d087e494089e84f16183a1cfa6ac40bf63ba3f1af9c3ea92c44909a63a0e81d2d1e645eb3f6516c40c3bd408da7b9bc992b94d52aa395f0098fc03d0fdcffd6caa8f6f4907e16d6fdbecd33d20b070d7a84c8742f36ec94bbf426f688f61c4cbc5266f2b44f8ba9abf70829cf0717026d4ad9970e48dcde77a9c706a7f14da2c306f217688534bb75c55ed5b861e4d3371cfa957382402fabf9827c8d88643a70621214428f403a6c82faecf2f9c0d51fc2687d284a487ccc9c86b78cf8becb7768085eeffc7fe27d4b785039b0a5f8d754b04e65d854913c28d3132509ace3d8efa5ea79c2b52ddbdb176f47f6135f94fcbce146f8d8dff37d10878889868c384488ae7c681e674d4a3c6bee75d807067da56fa83f1b74434d1301a50afc9389cb8cd1317a1e056e63ebe517243ebe23abf43375113aa05e5de3b4eba46956cbe95361852520211dd836a3080751ae6f94dd286fdad9309ae50479681fd79e6474b730e3e2c7505d71dea1f8cb34a8dc920dad68dc69c6b13760f448f8db4260e0f7e744c72a8ef7501607262f915a3d6fc5fd07d1cd8f6d4281e0caf83b83cff48618d91b7834a34bb9067a4cab771e4e5030a9a92a6b9e85704b6f4c410ebc6a9a92848a64e4aec95b72953da305ba0afe253dee8eae9204505" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "32ecfbe54ec7ff5c7c2ff3e51b8cb24e891e8371357409d951d03246935ddf1b", - "proof": "de5d11bb882be3d8b48dc08ec229dbe222cb821c24e3c8ffc351b2c50d7faf7da87075b5ff0d73e8cc9a6a980cae572022e0493d4f1c3775e4a278e5a20a4237be50b514bd15c68e1b14cdde8f40328a32a3341cd3c9aa5337a8ec481d4535575cd9acbf977be633af0f1928444a84ce151f4340d7b9cdfbd975a38b195bf56c75aecde18e131969838f3ff49e7479e2bc06365f2536d0e7bb784bfa53bb9000a275facc155003cc2f46f40619222ec8c7e44fbb8870678b37fb6628ca00f0088997734484dee17e68fd6260f431064cc772d3ff27716318e95a0f44f876c90c142a0c63d9e1dbaf81493bcd933218fa20cf9aa3f899e01babae1f6be1776112ee4704ffb27bf4f9a891865accdac407b125f5ea3cd866a044a9dd6341f942534a70cb6d7c378b9bdbfa54bd60a6c47c4389e4b8b89adc65f4585e26b643d01bc80567fe8da4e425d3954b18b78fe9db14d91abdd378fd49c49d1d1f2bd385305a85d960784ab96b6b69806c3d61d189cd8521f76fb4b4749db7971bc4bd5d586ac56b0435f5fa192933b5f9b68ee973285df8ce8268b21185ee7971c04bb07a70de838262885a7309d1e5ac73cddda3662f723e2b1ce88210b50693a34ed56918bd7bea7a82436a3d4a8b1724865bc3893bc73197849b9f41cce49716716f395ab9a221665f11d716bccb511f6d7c781cd2755ccac282b1a2d23d2c9cf455168801af31c067c1fee1688879b0e76799a974630b295846e459b6d8fe64a00b37423febab2f9054b9c97a9aff6951213d5843fd87a5b1fce853f004eb6824bf2c72424459f0015dd3d61d783bc5c7893f9e9a9c7bed39dbefd18a47d2b316131f66e1ebc2bf5a3ee20c1333b932847e4dc9e91cd8978cc6bbd3e252acf3db9004be3eace3e94102f0b4675fd3bca3c8f3a1f726e83075e3f8f77eb00a12c75b08" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "36fd99e06a8360c3a7a387cc0a4dea00d8e886decb2f6614db7ca1b8ce38ea0e", - "proof": "7889b2b7ec6c0706ad017b5bad45303f0c47bf4e258f36797d739eea90fd245190cc59c849283ab9dd09807b5532343b002f54cc47c5e2fc6dece6185a77b22a1ac6c4b1737f9ade08f2ab0c45bc9babca9221c397440d538e528cd1896fcc0f72e918fd284e985174b2e90cd6ffdcc12ab1a5df142c441f330b9a44a0703957467d2847d3c62a420a795d8d4fa4fed6bc76f4aeaa9ed6d4f3647a62aaaf7a02e27140cd700dc43ce57b0b86a4fa23b0bdd83d845dfaa65bc4f608d2ff0d9207a57de119fd8cecca44a3aad35a8f3bfa025351d9d8ed056f7de4fa814e384806e65da20cfd99e9da046529618cb37c97b65f8211057aef6eaf199cf3963c5b37cab0ddbf7b5d3df54e6da846488b6ae40ed04584afa0bc76e2fd68a76040335892492d1e4c7717d4f41861766f7bbae15819663b6d88aaebc2b83133b5897c564065401f9d978af4e91db4cd1432b42e6d6c2f01ff4cd5eebad7c1ffc929617006778e63f3522fc2cf0a5a7aae589d31ab0718b2b2fc654ae1addfce7498050fe426a825f0c0c032500139486550331f89f9a31f0b961b416f1de09dc0668e498ec362855761a49f184a64a14ca3b7c496583469a75e01ebb18143b0a9a12442e2cae776c7005653515df902326c750685b0ae2c6647503282b98f7a09cd1d58d8d982522c5f280b41d16e3c358348895e083604c0ad85afcd6baf299bad3d24bcd10e792053d8cbcb553a171f6d6536bb96ed3965742f276848493a89fe90346879b62d72b07291c680438d27695b51189021dfa2e38f737e76fc2b39135941a005ab3818bb9cb989ad7174a95ced7988a38c4a67a28cdeda6bc16affbe416ebaa478eea1056004a082c71ea68dd2951b8a8efcae89388fa0c8ff92e7a2760912b1920dbb9d2e48c5999f9e1fba30b5154f5d890dc448704c8c36b0a4b5770d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "8adf00b0ce24bf66b30cb1ba98b94e326055ff113edf9c732f02cbfc6cb25e78", - "proof": "9a7573c7939ab50185dd1aa2bb81d165f658e7e19e72486a0693733b2ff8646d2086fc86626ecedeb4831f67f63ab619cbf59e9fad48bd33ea1d62bfa70240681c93c47066b7655cb329b02f5ce647754ff70b2510da804eb3b2dd948469ad2f0efdacf6c0817138c38b571e715294648a10c8c2dd5f6c96f4e50aece5ddb42a655d66647d1ecd432df9845a5456a9a7af47bda2cc261f5caac7d2505860730fe4d2fc19e44883020104d722fbe5338fcb14daca903e5bbcf151100052b50f067eb4a1edac3836533f50424de8ebfe232287b1611a426d8941f359e31b366f0bd4b60d02f9b0d426decc3b9ed0fbc7d3bfc24c0328685f238b7d0e4f8a9677475a1f3e92beccf25432dbc5ed3c451a00e61068593d722aa1463b531a65d21122b240cd21c0a1ca12e6848887bd7b20c781a246141e7c412ee091dc3c443f4c45f4475f6cf63f5c3a7585592ee3c501bcc7b8a9932692e39503fb1903a2ce797d783967751d24ab378e46fbc7eb305152a3cb305bf621506ead3dc6b5b123635bf4b583a7d22098930358ec2fb087408320efbdaaf3c0e501f89387802b91b15a820b2976176bdfa1b0ce9ce54523725c0ef0dc23f27f838ab2ec73847783eb57365c121b20ec91f6bc60e161160c060552b582d426dff31b4f20586b87cb0d304c4a2c1703f8a455b76e5452c2ec2d56568f8678a9cfb5e85feb4f503e83c66a78fa69480a0919c5aa997c785359e0ae6f5cfed7aca60594233d58c041d81a396c84993b7268601bdec2f0fb2b273c2a0694e42ea768d91a82209f7889a68d7248481c67e25f11c20b6a772cadca1f7ac38aa346c7d4610a33cbd14730edca2536d3b7e1381835e835b98a6697a55d0b86c75399e05f3935f6c04bbf9d3b9809eccef3518020e8ed397e64f993a056821b377b344f311753467dc59f2bf6830c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "9a7e3d86ff66843a884c5e17f118a26810b91010e003ab599a6d8c1c38a6b025", - "proof": "ac6d911206982d869b2d2d1e607c2b1205b3f0d51bd1d789ff3b88b804e96532946054ce3651101dd0b0f88b1c8df6adda71fb90f8e621729bf5df4f4ec13e4d2080555b18341d6b2160bf2f7953d900740cc0ab7f8efdb619c82001d24f9865100f27877d9b6e12c0849b006744879ec8dea54c081d83fb6cab05fe187ac46db8dd4eb5054fe26af44827c0047364447095e9bfa2d5d848644d79799a4ab109cac06f17710bcf97c36151ea2e735cd1c938dc2bc460a0c3428e0be03058c20e8bf81946d1ba7e9d1eaefc8d269080c3ebd0650d640d3a3c135514d3c5d4660e0c8f96c51cfbb32f628b236feaefd4583b8a7e6f2d0ddde3f5b7ba7e9e3df80ae28d25a7c6ba9fa56db4ec7eacc2d4b8b135a61d7c0c44ceb3f4872a7c3c1d4826c2e7c64eeaafa5345697504fb0af217d69efe0b8ee05660f1ed13a7ab768369e1086e2e2e298c42c255281059ca2b41fc7cfd4fd5acab222d1707e5f74a139fe0cd1d0da7410cc80ffd7ced17e6dee2e707de360ce1b94e2a2e0c9cfb00f0478e687ca0bf116b9637b33250291390e0ab11e7d5784d1c279406404f219020ea6a508806d82f8d2262d8f890f810b71d624992543d17d65077dbd2cefd2ab24007d1ea105a6c8e85f23ede17cc46c32401b46891c8062a9aa143d7c24266b211a299d52fced15757a4c4d25b76fdbe0d5351000adb35dc443ce2ff7d6bb4570d8de70f61ded864ede8fab75801b506be9485be4f01b7abc2c00711fa45b325fa825c93bd1642741fa416bb962cbcc3fc4e2782cf54a8f8fc61c8f2f2a61ee390a2dc38f6ec309b1c71a8b9368bd319d83b9f1d07c5e720e16b7770b69d09c299c774048e6a5b02f24e4e463048e74ab39f94445d145d6ddb5faa05e8a4f6a042d6d8bd08e9d62751e745d8f861aab482d72131499717d310c6b0a5938e71c01" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "a615ad7e90763895e31312e3fe49458d1baac1a5b97637f66f375dbb418e0453", - "proof": "683a74fb876da3b5756464a3afbd1101ce92f260cb505bf768c018d84577ec0ec805eb2b0423a4a041c2192085974eafcbc51614c56fec5da97b074cdaa16053809f45a1f9d3ce6cd45b193eb45b7f16fa7ebc0731f15fb8efdcaf9e875e1534181b32519e7372569625f0984be80feef04413aa058f1423b0fbfb11dedd99723f1cc4d55ae8a5fe6ca5e99dc48199b8ba73cfee1f5efbfd88362ed000d5cc051a7b6c1bdcdf1d597d918baae46873026a9b103017a721142f34b409fe12750c732f598e213fd3fd9bea757ea6e8be19263be2f3b4751ce220e3bb9b5409320d6aef5869802e9bfcf0b80ef696015f95a507e774060faec5f25066a94450a6658efcbca8a54ca1631d7e3e63522166a03e8477cf150ab2ee7f95fbfacc61327890d4a5c56f6b99494b7bf2b4b33d04dc31527f56ead2da34db7b9c1d4f3d30420ed56d2c45d74357dfe4a211ef24d5e1d340fcb2aa4f9c3ee2bebcd96241766d0cacb316f2c0766be6375ad9409b8141dbfba2327560458345c5311ec9094c2ae6b7e56724b1264cb0a0e26ee96048b89baafc817623fbb76ce26cdd4108ec3920a4ff415204ded68160bee318487060b75b0cef69bef4e72e33b017661ab5380ce73d8e7651e2d9d9ff580e8b21580c57f9d6fcc2585401140f5581f0675247421877355126173f0c9f8d766cff1812d598030ee60bf85a868a9773d3acc43878932090f16b7420b4d3db8899b7381e5a5354fd736e10d81b3590484e894716ba6b652df0b3c55d4271e5fa29bff3aa53fcfba3e8fe9f3a62b3ca0f9bf9ab138a87eb845d3c4d2288d3d762e842c95c4da7eaefcca6f00ee89cfeb5e2ddd811c23eb041c1d1c7588e75833574153d5bd590ccd358faf476e4fa987e33a236070adce8864c7a756d5de57eb766babc6bc11dbb60515a64d5304d9c1c95afb301" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "b2d67240a9f563c2ceee1effa08a419b283a85be5c68f79ba65a7b4abf74044b", - "proof": "f03fa4d9434edc44e553c8ba795dbda77cd4fa7bc995de45d0d7d1630b2969279cf4eeaf55652565b1558b341930ba6b66aa15b1080e496a992db0a6ad482753a60633daf2c7701c01e43882e7dada1ab41fea72491990fcaa8eec01e9474b6c8e7922b288d8cae0be2ad4b55dd980042e9b534d5ab4195df280d0df6b3f6d2b846fbcecb70b1e2fa0e569ef2cdd0601996f788ce1e874ee2707739d42cf52007ad2b48f007306ee4a7b999d5b58c137df0c6f0a467e5d57cb66182625a40b0f76a337f035f2cddcb64cdee333d5ba174497413c2d3d513b02d0aff0fe3480076a1f429d055591c37ae0cf6f235822eb657b0418cb9ec6712b0a9c45b79196759ee42add11a59a2024ce07e578cc9c8cf09a7156cbf2a70ede7195b65ef0666028eb3b238c84248455957b0fc1d3747ccad3eb29f53f200ee5dae475e8db405cc445019de1681c9f21c49d39590cc0de993367a22303858b9b275eb47a14337c32b285c8161144e6f56b261516ab080f8ce6f02d3fcc953f61c952149fbc9512b070eba53b7f781d55ec62b749d96d98a2a3500edbce5eb7baa83dec5e7f946df87e415e7a168e7c0acee14e81476dc912061b40d337586f615fdeb23904f902c22c47c5076a252cbd9e39349fb64af34420559bc53e9adc03ae7655adbdfe2d5637ba1ce8c49b08e461b420b8e6c9d754bbaa710fa85747efb6e972fe71787830ce8c71c42ae460ac991121211840b53b6c6e76f60f3e8845fb4b8dc29edb09d061412d3b6f978bba6c51b9b5bf6d98d77acd995a7106b29b82aa2b7ba3d652e2b5c182d72383423de219529394670ae5f074f95fdfd3c4d9e133a93f975d4122f97d637c1b05309f6f16485ea2ae2a8e61afc7140b193060a0edb4eb895a09d559c12e9dfb83eb86987371d507255620e2e5227492d5baf2ad9519f47a330c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "bec81c5c5898d299a5497340eb17b6b7315285b864331965a8c5218903be9373", - "proof": "14daf417305fd6ed5f64e9cbd97347f0d4a8a208c5f01256c76a268746ce0069cc35d5d7b42d4a101d52a2d2234f8d296e2bb7ec2fb10e68f18cd376efb2a74f9ad3f295cca64d72a1ba2b5eee8d3af9561f29bd2d6ea2b6582df4490a87113c90108f9aa76e2d08d110081d4c18837ca7f51d8357f99c315ec76454d92dcd0cb1a4ba0f1ef203717a3d35c534447f9f5a51f7ae17bb641be7e801688a69b502deb60e87eaf624a55c972411c57b960174f51f48bf0cd48dae4f165fce7c9b0739aa1cec33553f0b0a4e30a2a70285cad0b971435c4735d718490b2df110df09f0c26c918da8ec112b46bada03b1577a6b6a8b37d1905e818f727544ced7da36e0b16eb84e99202ee3c4bfba47b3f3dc8795ceea6c8a85544f26448347e7955e543e116f00d625f9ea187e10a7600c980db7a29600bf82252b9f331b545c2f1dfc9899026464cb75f53b9283296f16816d0d9e52be2dab0fe56146d1f22768141a7a038a5e4882d0ed6415a15f49f2e49cc60246bc9e33ba80f7aeff6257ac7032e57a1ed35afc2d83106eb586ffabbfe037ee234cf67bde5f993a3bebf48844d2d9cd1aff175167a967585e4dce017a0894babe28f523ab56f2995eb9151d37e2539961644fcebb046c330fc1f725edd4508a57db8842d90625a44988f95008786ee895afd932cc70b3bb02b64f345192bba0e234d09cea5892882e511b3b124644b33e97251d4f8cf5fc2525e7c958ab6a7467435baad8272afaf27ee70f28d6794607588fc58b6eff589e08bbef551fc0cd51aa0e0982f486c0828866687c92fa29f4e412b668532bc3a807fa9a8aaaae1d887dd34f4d1d69d62ae5b13d31082a90650ad0452cdbb85add3e4e4c2e230b0f36f7edefeccf05dcb54b22d90f7a929154d00ae5c7c6a241467db7d6ee92ec6d873c42d01f6e860d2bfc8c9f01" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "e8c71e0782b19716fa12ede7d40810090c3c4292a9e4e531970803d7ecba9e26", - "proof": "c00472181365d29e16a62f249561b952e93ef2f38bc30e0e0167f8295bde7172fc0a2f8c96b84ac5eda37ee5739dcaf8d464e5f50863b7f508238bf1b77891117417c78d64e9ac8c6cfc50a111e46fdb7cd499cfeeaefd9979f4fc9f62c6cc630c77fd597be418e6b31eadb6766f78fc90a64f14bcd4844f298b064283f1a9747c7426c23144484e77ea232927b6b664fa174fb4bd850dd9d001ea50c74cd1057ea820be488aa060050a4e4fddc5d27a395a2f8fede4b513702e4daed00eb80e3289844bfc486c041812453c987753eff801f875b2526a61c4f6145f2ab0990904932d33c91c64fb6a9b7ce15df49894be2402d139e1379a615a4a1ed85d5631a413ad3ba00ecdcb3c91e2bd4bbff9217bab566c53683ffbaa6da98ed12c337a82c9a6d0ebd46ae9931aeff1d964b4b9eabb3d3fb6409965d69b8ff2ae9cea195c8d460f359c471816df57daf5df695a913e9e61b43b6e5331a0a84746705918f45b229d30c06647ea317978f89aca3f11500cdff7a65a97fff36be2af2f9272248016b6a3aaa2ffd4085a22e995e91973231b9531bd5da3c52a9c21157a3d411e63a94ae66469a24b8e458b2c5d8e8c0691678f01ec371466e3b46aa476264f02abe258358d30f321d9b77617a45d3fc1b33fe800c554f15588c69ceab2b817c80fff22e893009b42ef6bddc41401326bbacb1a0368e4f50b838cc9834bcf103a84f1f831b3856950e0f0158c845a4f674ba7ef3ab89c990f08e782006f4108b42a12945633a6820264d32277ddfc3032031a571de6a4d6466d015d2fbd874be69378b7f4965dbfb957d54ba6fcf0e1f937af6e7d3b97de65d40ca2572e630867edc79a9d3acc76d9fb54047a7d8744c156f7091965734855710d6d81b7cf021d55cd574141ff662f6ae299506424161055dea25566f8d38aa1a1481927a008" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 41 - }, - "commitment": "96d73daca6c3fdfb3409fa0bc7fc0ff276ed417b68d11656d763d2163fbb411f", - "proof": "8cd36514b2d66b68b13fac2ae067af773d853a167e6c06ab2690e4a9d90f782c3ada09b586033267afd0c80efdb0c2856d4e04e0e8ac96420e30887d39376d0feeb9e7c3927e7d64877996156dc7a9eb09225fb51ab633e0f7f85501ab0d655992a4438b5d0943b70b74a52623cbc8ca86fa65dd1d3ec98223f7545ac43f207e5cee6ff03cafcae4d80705b21fc8e5b049090d98acaa32dc33fbacf8f320d403cf440894e885dcf1d75aa03d58b9bcfec516b85c7beadd544aa97d7e226ad900103e8cc8496fe3e8076ce59c188388b4bc5a61dbdd99da36bb6d4b6b7db897029669f32e66b67fbe6bcd85b5187726a08b9d19c40f974c9b9957ec16b0a8043904011fa31435662d8c59cc9b035dbd3e3a197cd5cee0f1c1a414c75fa7e3b2403cc51308129a5ed69b9de5dd9994c0d8693bd6ad9d3d49eb2053803dfd5d94390c9c2c3c89a6415797945c35cd5e7defe914a3522b47a717db07968468ac571c463da6f2a7a7487a83f6b5409d285e8020afc5545b0968199e9137ade341583ea89bbaa95847a27f7b64ea0c4bdeb42a48668320b2fc7e92ac26c8fbd951f25f026a9f211b5549d84b6d62023f1a1147d0c90273588fa6a6f4e1a4b3eb863c61384d4cf98d21c1b461993593261e67f7213a891d29b42f81f76fc29c5ea04d41de385b54850242038c90a3f86a6137ea21a99c37ce6683c49359119908cf8f3f248070632c82f8b3e7b26879e40dfdf7f7517f4235ad7631268c69bfeda47574e283fef9ede69132241894c0beda8224a1e872958d11e8c7a4a7dd71400ec127a23dc0f945469f361b72260036006feabff5947db55548cd0d20d03a89380e72b69c8e2e641e1e7360667befe380f15fa3f83d54e2f00b7d8f3b4fa72b6eb50cd46052095c646fd5eb4eb2565dd1fc2ad6c19ef74613f8ae6a843df5c61b910d" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "301315aee34016beac49587aa509b9caa3c16c5349d08802cd7737d521675b78", - "excess_sig": { - "public_nonce": "6a923a2a3874921f53ec71adb5e918dd1b2074c88a1c1eb9e95f3c5e59214f22", - "signature": "bfa24146a195f967901bccea02461b15082249e2da14ab5fbc94ff405116c609" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "5c484dee52fbdb17314a1b381d5114c90b662c138a3555007309640fb4686205", - "excess_sig": { - "public_nonce": "9868170349b77507a856c990dd41a2936e9244ccc6e1461acd797ae2cb3b6865", - "signature": "ddbb2cf39d154f6eddda6f065290e25566d74929439f964794ac7e2eca04c102" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "cc3d5bd19c2ffb3dda352f86ecc2ab33bc441b3390f3f467e06b110997707329", - "excess_sig": { - "public_nonce": "78437faabe841a94a504e8a0889de27560f605076054d0af345c93ff54af7c3d", - "signature": "868d9c1c13c6ae209be13e66a2c57e7c1e0d862e468bce3f29bf9e79a3040504" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "ee6c75ee87c807b6d93e7412e69f64a1a73148ac665eafe9a12f9c2b44b01751", - "excess_sig": { - "public_nonce": "7cb4623d9a551da09a3076c2ce784e4a7d655a5fce027e2155a79e922549f127", - "signature": "11f3bd33ab77bff83ed961b016799ccd56b5c521cca6904642048eb950d73000" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "f8331584b576a1cc63f3de04ae27b8c44e149f32c86ffede851a69d119768749", - "excess_sig": { - "public_nonce": "104d255bdfe9ca16c37ad39014124ec2aaf9c1c85050687a169612f0a58c5b50", - "signature": "5ccc9868fe43f8f03ba81199beb3b26cb45d925b2d5295faac13afc348abb905" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "2a635b204ff01eff70d4f91eb317294d90948b4204228bc91ed70cca72d31f5a", - "excess_sig": { - "public_nonce": "0edf2042d32ab73dea89711539a3922326fcc7e66adee8b8c1c054fa33ba834f", - "signature": "7807ead5c38531068141ed7259503debe011e09e94c295ab5fe8ae465aee750c" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 41, - "prev_hash": "77742548a551444ac74585531d3260caa363e65551c0dfdda4afed0151cc8871", - "timestamp": "2000-01-01T01:42:01Z", - "output_mr": "ee4a9844d8a6feb0085444dec086ea5397319951fa416e75a4a04ca160e73ef8", - "range_proof_mr": "fa53a9b7c6f29661132cb5cdf85d66ab494358c73205341c9eaa57af4e967751", - "kernel_mr": "8ba49af9a188c47ea2a68b36878f36f5b33274f6561b6ede3982e9419ef7b78a", - "total_kernel_offset": "09b1638f593ef7a29472ddf28ca5282de218bd7abb3feadab49092cbd8d7fe0b", - "pow": { - "work": 41 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "32ecfbe54ec7ff5c7c2ff3e51b8cb24e891e8371357409d951d03246935ddf1b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "36fd99e06a8360c3a7a387cc0a4dea00d8e886decb2f6614db7ca1b8ce38ea0e" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "8adf00b0ce24bf66b30cb1ba98b94e326055ff113edf9c732f02cbfc6cb25e78" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "a615ad7e90763895e31312e3fe49458d1baac1a5b97637f66f375dbb418e0453" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 21 - }, - "commitment": "c0e32e53349cb25555485dc71b24fe3d6b5f146001ae7126c14e942944ed5b58" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "080945672643dad18edf2c27eaa5624760bbf8d35b0712ed530831025fde4b15", - "proof": "449edd214acaf5253c5a7b893ec0d0d117350a768a2f08ef8ea8a750c140e35d9282c7ab4dfbc564878c32fcfffcaa9ec960133eaf7f5ae146363a8bc0271d36bab663eb775c1cf3cf88763fb94b486c916b64dcff745031e5898525daa471592c747aa80fa2b210569c04be8b264cc62367265ab9dd8d5f47f9833b8ce6ff5dd7a98012cd61a846020d4b7febaf1ed0f35a92a26f109b49b2e864cebe42410a93a47381869e5c71fa2fe86f06eff6425e5d77fb2a9d846c6d59cfe05014130d5aa1a03653c3a3ee68f964212841fef93f8ccb904feeeb8d0e5c83c47562b504d237a1a9a9d6860ef2acb3d2c8d5064f50cc511e22a186a8bf0bef5bb4859107d4d2ba46f1cc46809bc166ba944b53b7162506938f1348d8b1da491da69193799e4b8a5fc028814ed959a37ac58db154c40bba97b4d62ab69f6eb2d7036388178697c03ae6c362f03381fbcc3d7d359113d91f06ec920bf55dcd55e37cf9e42e4af38a6da3a33c462bba86ac1d009f9536a147bf4c645a3b46ba2e31b4e9ea29c05b0777c4b4969861ffbaa33e385a94f3d0d3161fe8340b20ea9b2b15cada448881a2972f3ad7914b57a04bc833faae8bceff504fbe1e1299d8bfebd0c30c1760569330cc65c93b97ddb50f260edf545b5d12fc48c887239bcb3ee1e1af7b7bbcc901e7ad9d2388d0d2c2ee988497aa9ed259dc2cfca23fef4eb3703eae374230db36ef58e099bfed5424abf05fab1cab59d23c769808598c1b2f778204b664a4f08fbeeffb51b1f8707880bd1b3c2241220c2dc7d983c215edc62f4870aa34aad85e5c93805500a94e238652dc9753d44a60032dae9643da0070748c4f540d112ccf6e11bd11d8a9f0b57084f5fe8a6c7fcfd986394417a9fdae8a9ec5560c6ab1eff5a8cfb33d1c1a29275a75e81eeac4d61557388ed13f22e1473462820f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0e5fe67be52cbc67754e0b064005df3d1d4c6465ef95bec0f7eeed8e6adf4c68", - "proof": "7ea305b3955c46ca6a2068048ce3127d306c27216a82e815bfacde34dabcc10a3808596b36630cb7134f847be5ac325fb5391467fe0871141abbaaa3d163c50b7634b5c1950919b0a76c125c83880c305c967cccbae68f15ef63729fc2e298280c3f6917a62259498dc2cd944a37a45fb0ea7dfab9cff44a33b27f3bf8d07a7e6f4e6c2b9b6060517828bf439c7e8e0c450904fac3d2732dac0eae0dd24e100bae32bd7e64a1ed96932c0a47ca1ee156ece148692793f813b7647433c3a2790a60d1b99279936024a89a15e30647d501fb0758dc78c843c43503e006d6c4e103a25a6e95f3c6c9656a2c99ed9993af5a3273fa1a4eaf02d85436d3b35d545634c213b1307a6173e1243eb90fa7cc63cb18c6e4669308beae1a80eb66cde1c84a32fa24f8715200e6482059f8f1f77a2f2705d21dc9e723c92b5315ad1366d403aaf7b1fd8301dbe32d808c4da9fd0c50e7b40ea051ecc8f1f4ba2c7f822bc45d3ce02ad79db0b64b9ab2e99ec2e5bc22cec097360fa9c41fb941b014cba4fa308edba4b1a6aa00062e82f43849f4733f369e7548a626b105cd17eeded82d550f94943cd87ae7036324985ef8d826a1e2ffbfd0d5e1666ccd1c9587b8ea8fb8181e776f9a87a530b5a50033a8df57e0fb9a83c34a6303bde55c91edca1327b90c5a5bb6181cccd4b26a6559a1facd970ae09b10cb9ee83e2165d44b0b9d21c05f76fcdda67c7becdf959e52216955f673b9ec00c39b3098d039f72d6b3e354944d8fab97dd115b6f1fc80f336066457b9a0a4039c52011374901642105cce98276c20a22a90edd7a83aced3fe46e514cf8e30856e3b8f9e06c0c30f0acf5e4361fc7c9e70a6d5fd3403253e0b69617a5a6330a737f0237f46a21ae79a9bba4307897659d50f7a7a1ebf2d6e46df4b420afe81d22ebb14cfbee921d086873d2100" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "28051606a63b54c9c8490b1081a8c78dc009c9cd7d9c284d5825d500d39d4635", - "proof": "1c8710a5317539425f88b383691d7c8e470a54709b2f5f572f7eb0f732ce2b338ef6723b35f6bad37a8ba4c132163bf49807ee0eda421bfc2cf787521e46c6757a9211da0958ef6e0796078fa20f7ffd6e35c73cc17d3514189d5ecf39972c1598d6412ac3c479de67616a7e03801a427203fa4e3393c10c0df6da0586232861672db2dbc2c3dde84ec544e65d53944acb50bf5253b4dc3e8236977f175ce40f38623c719a0fe54b92e98a1954eee19569c03e5f7b1e65dd7ccd07eaea7554017e555df9fbdd5114d1399efafdfcd6527d28c79c86d071df8c5c0e500cbd1d00e624c8576febab0bf8d5bcc76cf134819024f77675480a5624de88a5a0064c6728f2415b9dfe5eb6674ba014b3bc9f8df280080c38fec174294e7df4171253425e3c0ad04cf4eea27a212aa2b17e9ed3dd9a3b4c80623aa733094e769f2ca661d0f8f78558608d2910567f187300e4599aaadde6068a7cf4b4a1f50e4476807dc2d73a94e1ebd4f27bd4ec1c40c4c4090bdbc9f6465e9e7205ea2f555b51694e6481da8e33715c6fc19b507ac4d20ae936a067c319b78875bb63bb6d2350aa7df08c216d06a7d3fdf5a7e96c9cce468d9f7a05606bffce27f700e79e6d7dde7f9c254ba0d4c2dcf22cb1d8c6c32eded80630ef325a9f80a6f5e08a6ce8b995247ac006a1b6f58480db1ea4984f8d7a1730c6925c6cc039bb9438dc019cea4f0a5aa8bffc76108bbf0999741142eb52edc42da0e7776fe6022096e1373d8d2f1f4e674ec8cc16a86a4bb3fe676b62c1fa34b8293300a08a2ecb70441e73e3736ad439015ab23db3fd5c8af00fa50d3f5cbccad092d64d2127b3dd8ff7131b3b59bada89cc24586f5256bccdd6c384f8b0c579f005ce9aba12b5a3450ca43f36051c500ee7797d39e0833eaaa3dc706f33fcd9734a19979fbb03fc6a07f0e6320b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "2c48a1559e22e6a10f089a5464d1085381060b79c58db028dddbcf0360899272", - "proof": "8c10cf736769af53d665b8ea0456230598912306e0b9756245a34fe14a97a80ed6863087a282b1df9a0abb9d295ec20649bc94af9f47f10f2fbf8a0e0754ea112cbc727f761d9d0b27742cbe65d18f60f5bbe7656260726ad8b71a9cb88221797c9abf89356ce88da0c56bd954ca5051ddf9f21e631eecca1a90bd20a70a3f46616e0d27257ff700fa9fffb27cb420e967007ab6449e1b2f4932e85036a2d60e37d66136d10cb2606112078a9f101c7f294ccfe2b084326b02f7ee9c840be2058ac1554c6001ed6f9bc5b2a10197756133cf9b011df12b2fb3a663d33f195a00cab93ff764fa4a2d92b55570fb5afd3de97e85f45f1324dcff352d102b2dc12cc64806975522627e872d8d5fdd42bbd50c0ce99823e01026d2b23818d072f8773cd148bb47d2c9f735cc6a50dbbd1bfbd04a1f22a7979811e8c818cbe6e62b0ac47f04c0b8dd3ae746db40b56e5b12069dfa0f52479812bba6a59dce2943316f3c19eb62c0c3f504632cd3e3571ce37e4b5e74ef4aae4c29d8dd1aebe18828069cd79229a3bbcf7601d42c1fead7c1da64a5f09d063350f24dc2240ae1960433aada6b827a17fe2cef1d3e88b7abbacb640dc924369c72bb0f41bbb39410cf77e2b7004cc17448929c2b67481f43449e85028f2fe2bf7d56e88491605d52b4307262fcb14899c6fb68dd6772d83f43d663942188222cd7c339c52a354789c055880e042c3d78f1d224b3cd4ba2262315e0f447d211d9a9455fbb2995e1104510989b9f63c8c325655626fab0f96ad4e00d18772b2f1b01a2986ff5f0d54c6b37508137b14218c612baa7b5bd91fb1ca3b1fe93f6774c88297aec934dce3ea96972b551c8d50ed548768215bbed8899c6990e7f59b3a28c60ab6bbe9f27120103116ca5262724f6e6b3e0b45de5628a78dce3acc0295c0284ac2de462b5c2430a" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "4a6507c8333b22fd3a6920d62141771333fc1d3cdef7965e617c865f9da3626e", - "proof": "10356e21d27c44c70746f9fd3e013688217a011ea5a7cfb27fbdb6e10e7b8e1290934557a68827453e0a0c38eaab9728b2b7b20f50029504c946880cf31c56031ee79dda2c337d9dba5196808a23efe4bbc7bd6d4df960a6eb6ad345c287a852b6581027610bcc47269a3c378c567744b8ad54787291b5713b3f00d87940ea2a17e580b23cc516684c2ae4ca86d72462ef9e6024cb18bd9d5dec0e9203534204fbffe87a7f3d6555b308808f619ffcb6827ea26c8afd1d3e3ed423229068920099153043b350a3695c49177f4184c3831bd461884ccaa9f8fa633ccadb68fc02b252fe8f3e17023caf875da77175c6720762e4b56de77d7b0eadb290504e902a1238a071fd6d7985c0959b28edf188d01c0d6425c7340a69c8c598f86feff8131cb1fb015e9761be7d9c0ea82ba233a7e05fda6ddf59e2e37f82511b140926605e315a785f0ea0c6b2de9432503e955b3ca6ac2732bee226caf77d4fc51a2049869c7c8ed438dd58adca7f4ac0fe5e3b9f032483d42417095460d4e55cbbf1406a1428a1d223b75a5ab37ab4123c4f03fd0a2563b5fd9bee3e5ffcfdd468932f2c0bc1d72541b3c2d2aedae84cf77a70afe4c26df3683b7f1d1b9a65c9573323e8fef87de99cf8fa8b75df6b181f6e62ea51d84b68887a2a201f9093d14d0071a404131e1de7adaa9851aeb2195ff5c011e1168514c98122c8afd7c7ff51406e9e783664b0448d5530b5cfd593b3d46167358fdff8987d1077f493de68946022867a305d5f2c695461c8ccdb201af1709b118b8a57e76f1e7c929d4f4641ce55a8b2841b076b89aaa55b93679ceaf730bbf18e67b24ca7da025f0befcd29c05940a526868086e82888dbfe99bb8dabafd4a7c5de76442a6b8c64cf56ea0b7e0f676208209bc697c79b3cdc0560190abe01fda4bd50812c4ad0e22dd03b48c106" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "bc01d0bcf1fe08cf96d3b335fca2c24af57c9122488e918a64bcbff50072eb42", - "proof": "2296e3029ca464c002b27e0e511bdf56e7a7f771c8c869f3806c07ac1d49992c980c468edb8df7e6ecdbfbe76e2b6158d9aa88f2fc9b100b98d47cd5a8acef6a02dff55eb5be9d3afd10ee8dbfce1b2dfe13669fead44d4b61b9c8f68a43d55f2cbd14ea460a40d5206dd59b946c0f82a2f8f0e2277affbb053f2ac65442db6fd9c2cd710ca177268ad0e378ea151c46b65c35d5aa7c3cb265003fdb6d92f801dd68b753a850a57e61fafc0290481606bb345c1edcc77644e57f16d37bbc8f0eb5fbf719ab340460f0b6dd2fdd81f95abd366e4ce301faa27b6053c863e5590dfe1b24122c7a6216461e45918e48e4959f4239f1b447619afaf2c9238eadfb237ebf1ea04b1da5c1b33587eda9eca70a41276bdf1c6df91558ff8d5b44d2b45d5c64f95f8fc53957876191acef3368fbe9d86cccd7e4ca8e3367aef5ac1d3a4276f4baf4a83f75c008b02b780caa80249393e149cd5f8072db58e6ff0268fa17beb28a0f08b0fee9c16dc6a727b51922d3b49c4b3391bda07133c4c9b9c8b15cfcf70855e661f87b5f6ebe1b341768c07fe236bfa5de307bea912f3b41ffbc19e0f23a700be9e0d91c6b315bbfb2a5f17fbabfd839cb13353634e60442cd420b1ebf66ee21b598fb53d34c9526f71ae61a979c2987462fbb3f8c64ac2e5b9423707db03a4a39c2dd6c5c3afe11fed1cf10e7c3f62b2fda53b3742203793225505627aa113dad15bf43b13899fdcc73639bd5e6c63c2adc96686e2f0c94cdee133c53fdf90a591688fbaada7876322a7989171cd6bd07a36ceb44473a5f49375b345e4d3aaf2d1debb98277745b6c0509dd8d665bf3667bbac13f12126564ec5ac6d2ed10539d87dab63d4775e23d0dca1c5784232efce81bca0a49a8a35fc40034412c0867f317473097256a8a4c3bc54fca5678ed5f4c6b31bc194c62624d00" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "c0d6e0fa1d51fa324f7645bf43ee58f9985951812a0a80ebaff306d04bf64c39", - "proof": "c8301b66f987c3cc85ae86efb6bb91539bc998972bc670832d52f0b47403930b1608e2a68b4aa6d1d8c8de5273d7befa12dbb0dc472999e78a9fbdad9c9ce15f8c2be94feb1886dbee9c64085ff9f636b002e8c80fb5463b86b46e88b2065060f63a2b002be9dcc4a3920a7410bcf643c091f44462214d6eea2af86802092626283b27ba18a73db61fcf0b6dac6caeecfb1c4b27014dc3a10e385494274fed05f2bda04e5f40e6044e40079bc24265b2467bf9120575ab88e38303561512680d53fc9364c7ca390a004405ff299cfdb6f3f761dd9b12bd960568d7d5e745240b5a623a994ad1f5208a27ef2780e9d53e44b1d19072968b9881e69ac0ffc2717ed47695a371c43ac1c5e41b44ac7c7d9e54100c09791ee42260e33f761071d848a64ccf90e9a6df26a49643b24c38462b765fad0f2335a592317c11a4feef153b74ac7572241aed070ddcc347613a2edb80b3880c7419638a0eb992b19c25d37872c24819ba835cd4e7729e2f0a15e55ee4f3ae1793b17baa15ff34475617bf3e4c5b06bcd944c67da77a533e29324b08b7c1ff83ed127d7a4b410fcf1a449b7754d367ac98d9054d282bd6a984ca6f3eb23e56ef1fb8835f20b3abca48643c28f65aaf85ed4b0e0edb17410a5f8ea7130e46833122e0999cf1afa6a3b882cf0ce0134e59b2da556ebce75424835935c0fbeb192969586de47d0f80f5f4ad1971546d10b1528b5e296f0815e2deeab1e379023bba6f28a1e349b2f55bb9749e2304375e8602588be9f3423ee6c8f8d2141d428327115899a39d219e2a069bf903f4b8d2eed7bfde35d817aaef3814a1d36fbc54f89203559be4fe684fc78af479fd388981826ec43d79289b9a96de1e53af534cb57ac163998c7ed6ea37c12f060bf3feaa6fe5fd720ba4c20bc0a18989add61324eaaf5876cf16edd504bbec0b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "d02769d3ac8fc8c772642e74f64e5effaff6b278462b57129a836d056f67813e", - "proof": "e6629f5c45ea801f38111acd425b64efc97c1e201b0e55859cd9828c01885c27bc0aa82057fd8260a1a681395ee94156c4bd6781f0c066dfb29391d3760c4357806dde35deee47a4c757d137c489801094a4b27e7483057e41c038c323336b38342c37b9d6d690140b675779abfb17da64e877dac05c1edb8ff24811eee451092983c37a7e69aa55bace73d9f0a58029114da10055c9ec3d8b24fc99160d730b0e96df3d8a100f29a1485106602324a56b5aaf44ebc0005df068bb0c6c7f6b053638d1b5e9f34455017d6ec56bccd21d434d7db8150aa1fdc8780a0b4dd73d0e50865c9d740d489f7042d62d066b44988a3c269ad9d037c2e7e6218ce7cabb08903408d1964763e6f6995a7ffc8ade77aa72f3de32b4be95efa6deb547faf565546505bc19cc3c645452f5d2fc6836c88a7b53407a5cbbbd6eef51145fc3183594aade880e12ccbbb9998e00fe9a4fcf4abbfef9168e01d577e35df57717f940d25ea301f56a2a434a912be3f4bb75c2ce68ae084f8eea546909e1bbf1c86363ec4ddbde087f93b66e43850a9d8a39e68b8c5f7617d7e048f3f4698efc44957fa00c537ced55f5773efbea1f720c084a1d60e04ceb5e2dba969bece3c5636a7c0a1346e222231555d988fc0589ca482596b29a6302c5ad8ef9782d6093c1bb01ae5d259e6af88acaacce42edce4fbbaafaf2ca3c89e0f05ca27a762d1a706a400601c84829b61d791e406e55c98aaf7b4f782e7a4f5b759e68213bcdd34a720f9225202d6badcbb3f075221ae889aeef6d892d23eb2964a9535fc79df120490950dee4d1f13a8d7ee938e2dc1a82fc997c639b3ab0488c465bc2e7c5ddd3cd3a16c5376b6cac6b5d7a548434ff60cb192bfc017e4f42d11717dfdb537f9b170873ce068f0e6b1884e72206519d01647f38ecaffb9d1f192e72b6c1ad1b8b7d06" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "e8d75d3f726e6342043848f67741cf50ec6313e82da83566476d97578b4e4244", - "proof": "1cf83a0bbbf26df8fffb5812edb384281c174cd8c9590c7b97d33e086bcfbc102cb9d63de5651b50b06072eca2066ba368c38965c9e93b9b14816d6ae4ecd14e00435576c101f605a6a7f47e30aed4610c1fcea75fe5360df927b6fa21127743c2a0a3ab4a44e2f214db2eea24d417b8ea1890230e939c4ac31b6468d7448c29fa01100f86d9e0b88f9bfd96ac9a8df3744d4840268a5f8d91b3d929433e1c01f78b730d4a45f5412adf519c9a4f3a883869c6b7b8b99cb9172dd3ea9476e50ef40bc08676184afa2748e58c8482492cea8fdfc2282a1bf0fd4ba1f1dcaa150da2e16b514b2f20a310c21572b90f8a31eacb87cdae8fa760f6678eb723d28822c8c4a5edfc69026e633059b1ac16cc688b703790c10be0be0136669ac5cb497ab0c8d4f62752970d5b7aed069d275cea22f27baf5ee68d276957e5384c67045a343e5be4e25f95dad64a1daa7d4dab9cd6b74513346f30d7ca78bb5fae4f140e224a868909fb432c634d6bb4282ea8eea2b92f049967153baf083fd100637c7c3ac28beecad823bcdf9c7896f09b4086e7303350dfd364e3662dd0f45c6b1c2b64ba899668a96b3f2f9c3038c8ab6895d1a115517211c5c37023375a91a00020c81eed9c7727fd508b025e58b5afa389738334f57d379d06bf3d09d9fcc9d54e68f3ea39cc2590e2a892a8ef48bd0935adfde19eff4e83376fa127a5800bf123da6f97f29d940f3246dbf0cebd1b34dce128dfd9f804bce9ecdd2ab29ac11e302ceffa671d14dd2d7e0cd433c65ed19980dacaf96ba84da2268e03ad896bc43e7ce1b8755e2f7e7462eb75c6f41e57dae2e42f030788b8a1e8845aa0220ef47c281a04beaa86757e90ab7ff4f2fd013775a4fe1d8ac44537faada96078b8c801aa1380008ccc7719540b2f7e71b08449c4627165aff6402cfcd79308670ceb09" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ecb501cd0173031770507fbd935cec79fbc91a1e39c4f81e0429dede5c1dfb00", - "proof": "dc3c7bcc6823fab100a0431362a382aceb321e730640d26eac48354fe63a6c167cbf7b8dff5d13a497793fd252ab5791526a4302352377c7e01ae69cf96bdd23c628eaa2f2c84417cabedbe301fd1b507f3e8df64082a9dba5a7cf51eda2f239662f361c1d477fcb58e9d5ded6bee5cbebb2e870e2162c58d33acee3f079aa65d294ef6c52020e11f8ecee27fcae2ecc0f769c063f564db09552c29daa15230e758240ed1152f6cfaaa36a95df136e1d72bb3f54d646c3228dabf5e88bc570011f614b3a6e56f2a9b597b738b16d6d1e0e99491eaf6377c2b23268055d0c0006e240f9eaf251210c32fec49f160979a8e7094e8c8e4b0968fa4cbf00a501950f54af59ae260caa7b8a09dc6ccba91198e6688c18ee3e0e03d430af1a8d28f8059854f4d6252630cd93fa47509a154a60f4d3dd47cf49b8cb8eeb4e99a046fe7118d5adcf01e63bb0e05988a32a0a404a086ee36ddb21e28c74877011467aed594e71e3b10b4dea9d10df9a697034ac0d12918cecdbd921283aa5468436874a4b44a1d862d432081842d638d04942bbf5f70e72f110b9039573b1fa97476cb91a1e366ada2d2e2fc7de200643b7b208d69d1927ed58f4bd02cc1232b6f8b4ef1e4c611eae5456ec4d839c0d969b3ae9a3f10afd2df168d6b72af098c9a0ff461ec8a4c38e96d1675b11c0065aa329e74e5bd7e0685b2d5abaeb0c4804620e0d6cfc659da902b1ad90b8fad5e4beb6738754ae134a277f5d353372707134380140eafa257e060e8e945b74e6e6bdd8850023e468ef8529f56c2356344f54d6ec65d8571773cbb3188056f6488d5ebf0f19f8926dd953b6080cba5805704b987b412d1ce00dec2df8bbf00035c55814ff1b15109ceb34945ec3d1bcac1ec45380033f8970d98e88e93a94fd25b326c3b61943ad36c109ee7b419d184e9dbebcb70e" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 42 - }, - "commitment": "1c44eacd3b4c7e9c9b164012ee69abb88c8ca941e47902baa7c9f1b543a27951", - "proof": "42cc548c1183cedc71057531adda8fcdc70f10845d66c1eb15ba63f50593d21b76c4dfd3f51cfb87c8b8b532aedff5170279ca7b35d7d25676e75fdb6f541833821e284ebe386f5649f185fe327c7752a2df67171e8eb95a8856e149d9f13907208bc578417847b5f0aaa20e1dd98631d836ccf52c30e430f89297094e566428e52bb9eee8fe2c294c15ea47c96589b119feb36953dc65f3ed818719ce6c150349d6b0a20cd4cb47303de9780d4280a4f3114f3f546d88e94ba42fa243471c03dbb11eb85e733c0ff532505a9a112f929ef9d95adc8c63085bd130344a557d09b217cea0c438a4e66880d7d781dbf6738e867407a58cf74017a91b388278160c2c19ea8e0c9b552ed67c29eb79d81d1fb124596f029781c70b4da72cae4f7304841f3f1073dfd1edfcebd9ff339e265c7f95af6c9535596d5f86ef247069b7657c6b52f6f9d6605db4fc51c335c0c534e567459d2d7d11ec20af5df10b6795200a32c2bb433bb78b9d5d62f9a248ee5faa1d130d56890681ce5c86a8a6f03162e08e47252898255093b33cd0a5a259a675c56bd6ca69dc420e9b4021530e5c7926460bee2ed2318d793e01eda2d14d6bf1c8aa6f2bc1970c652f9585f7d4e5345ce17cc599da864fdd897b47df61e41aa508b5dede6bd3846df737b41e17b658ca6470cd8019cb0302bbdfcc0adb514d0f3b3f1e2304536f0cf51658c6c99808c2f2db96778cd134365a1d4aea207650579bc1037a6a04d1496da8cc3b351777105e0a4d9269258cac1d2b4f134eb18da19dd568278c34e1ec15e5ccecf7055f0c7a8605505775dbcb3e5d01ab61490adf4b6bd69fca93e4f4b4a302ccefba25f06c70ad262d37de59c5b0f73ff3323976e04c402f3d923c4064e3e6427f190632ad9aae1bfd95c3bbba9ed820c87ca4aa2d7a067a4b9a4b359198062d92fa06" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "020e264dd79735b64a700b84d16493861ce02b33efabb038f7a4a900e854fb2d", - "excess_sig": { - "public_nonce": "ba0222220e9449768e97d7340341204b41d603211b450ff04c8448fee472f71a", - "signature": "c709fe3061d92c27c227cfd7850f41f8673ba5e785354163f862460322b1f203" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "7a2c14a3aafd4b9a71266d95cecc6192c12a0351ce36c03dc620d1fdfcc8ad36", - "excess_sig": { - "public_nonce": "b095359ff6e67b7dd493c539f1187704f9f10b72436e588c52e0c8d64071811e", - "signature": "65f700dfaf17c71dfefee0066fc57d1207305dcedc03b4af633a8b752e6fa106" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "8818cb9ebda6bf07fad5de814dbde9a71ed98a8e76b0c67d1afe36bcd9382466", - "excess_sig": { - "public_nonce": "948dc69ce564b55bcad709359cd62a42ed1f5cf700f966cddf7ebef8ba38b760", - "signature": "3e52423262d67afccf9d6a1ce5c38616f1a61bef03b1dba9b29aa830a81f0005" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "ced2e66dc4dcfe633b0470827dfd62a78264c54cdf0acca19317dec4946c7d59", - "excess_sig": { - "public_nonce": "00dad6784bb991708d4bf90aff916ce45cc10ed88513ad97421a5ef8b3551716", - "signature": "0f1b419b19688321ef8af59313881d8124e1f1a6cbfc9ecfbd4bf5503b591b00" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "ec30d1d8bb65807a4a37cccf5904d314c7d11b6ba6ae543f6a14cc4a28d87446", - "excess_sig": { - "public_nonce": "42d504c75adacd0feefb2fde856fc632f2f2b4fb62bfbf73ee519bbc9c965a71", - "signature": "cbc62ee451df0e46c947caac613476a42e7edc1e1c17dc663fe7b044980c3909" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "f4726dc6dd4062dc89e6332eeaf7d900978a55d4f7aa939386b213ffb49d4a54", - "excess_sig": { - "public_nonce": "aad2294208f0249fc533edf62abc45e860a76fff88248fb88013da1db0460759", - "signature": "31d36b71df28f79d1313bad41d316182bb1cdfb0abb79b2b91cfe1fdc9272e0f" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 42, - "prev_hash": "550c265c2b5dc514185d67c0dff7142105cfb5593f64227032819d0a4d3cc057", - "timestamp": "2000-01-01T01:43:01Z", - "output_mr": "d0a8761fcf4d0c22d76ed937ad92bb4307c6b9732d64a6eaef8388d270e008db", - "range_proof_mr": "4fa4468325779386c80010afc452430c1f6057dd4d5da1a09cb3b5428106309f", - "kernel_mr": "47c04ce564542e94c53ed09aa85ba33814e9fae4a2a61e62fb00e48864d06669", - "total_kernel_offset": "217f046d94eb2b06821e2a948bd25bed95d729cebe48398174dd1095ac844905", - "pow": { - "work": 42 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "080945672643dad18edf2c27eaa5624760bbf8d35b0712ed530831025fde4b15" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "28051606a63b54c9c8490b1081a8c78dc009c9cd7d9c284d5825d500d39d4635" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "d02769d3ac8fc8c772642e74f64e5effaff6b278462b57129a836d056f67813e" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "e8d75d3f726e6342043848f67741cf50ec6313e82da83566476d97578b4e4244" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "ecb501cd0173031770507fbd935cec79fbc91a1e39c4f81e0429dede5c1dfb00" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0616354a874abb6896ae843b3f1b83e389659ab9c25c83e2d26d9ae097023945", - "proof": "4ed1935ded7bae50c21e4adb5649a7f7fa57f82451656062d9af6c266bcb926fdaa6105ad86ebfc3b7caf74a131f85afe4231e019a9980c9fa35f6b5b5187766d088543d0b091450096036ec694eacee4faef8d80a74eb46f2f2e7cf20c7d039cac4aef99b956a543b1be542f6894450e9df0357b637b4faec89cd2d196b7d2d69b785505f9d83632503dbb443abf6113506a924e822dc9c37e670d0afaa240e7985c927d1327498c374039c3e445efdd68dc2913ad332ea3ec97eea715768005278ae46323ee866d0e8fc721ff74d6c8656ed8b665f78f3a2840f9ddb2ecb0b7cd76c3d3310694bcc90021dfdcc0da02937badb73c24b2f6dabdf96056d187d1a86f43bfde554ce7fba704d4447b8d09b5af2de6ec1eb33c0af7f6b962c215ce67dc605012ad117c6d4a36ac98f6085764b71ad50f0da8f76dfc8a24a1b4938ccee65468500e2603ea1c6fd7e4efd7666d900b99594129ec0c496a4b9e4a823b0df137517f58f95478adf355460710bf52ccc4757b48b2e42a358a376ac78556e2d37eefba6bc8a3674860e986a7183195cccd856dee723c097d3cac80fb606cc073c679238b5b8256699a15f62d9198214a2c830b0afe2233b3d41ad09c7320021843f7ea7013a1318a66f048d2178306dbf1b091a7cd6c48d427f6454750e1af1515e70003636219f110303cc627a8e7dc378f315db415ebcabb40cb19939886c98d842c3458e2f204aa019d8bb4da0ff9e37a5fa3b52646807df49a3392eb04c653dc368d52495c91925364735845f97cd01bc1ff1682ea1e46f23350c68a28958c751b45a8f497405c22c5e9ba27b2a7abbb4a0b342122822f7c17b1439cf9ccd89d42fe43417f74a49562d1b96f11f416b1210d3204e8fe2f4d6d0f809da7074bc7bec125fb8ebfcd144892b3daf6e1fdb5cbcc6952377a5554e90cb0f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "128ca558589cbfa599c0f51e3b6d15ea8b874653e8a346854437b1de9e6cca17", - "proof": "1a2f65bd1bfecac5f635d72431983551f4e36ea275ac21e2c3d4dc7995aa574b2eb89bcb7e78758cb4efc53fbb81f5102dbafc4bba0ec6d42a868048ab8eca574a8758299d82c43335003212a4d3558de89406f1770efe9b9172c97891891a5f0c8df2178b84622621a018e328fa52d82daad076c6483ee6548d709eba914824411cf522ebcd9d7131c177fbe7f46322eee545eabb01e5a9ded5eb98986d9f07ec71d82e3810c953e076742993ab679b1e5c997962dd0c2982b8d45f572e200abf7b6e7c2d0a1b9f0f4e7a6873177f866a58b43a1b701e0e1b621f4f26cd2b017e846f0952c6033c25019b5b60a073700d53f1079ad07244ad37e399f141e753dcb06a63b160f020363817c09024208d5d8d7754d496d9ed07003393c9607016be7a53184b3390783a6a812c6c2ea08b4a4d16ae8f8a7b75b96a1bdee8b49e15a8e590bdd844e364d2f278f61348c0f7e4ce8b0b294a2af078eadcb7d45a44691820e0d3de414e4fd3c7b675205b7b52777fc85ef42cfba8d09e5f5f030dd10db6394de65210f86fe017141329e08b1f9a6aebc6f703b3840e48c4ad92eeee66da3f252cd0f11b56a38a262d84af68ad430280096eff2f4537c272be1c80e65ab6068dd0d62280b68e6e87df48b238da03ec7feb3defe80d23b1876117e72d715cb10949d478d101e137753438cbbc74dfbb1aa766d401b24e7891d5813c760a2076d01ef1a9b2c4467c582608037d749d2e183bb5c17f825685d0396d4e544a90177c9605dcb67a1f7ee4dabeabf326999de36b2415b3bd8c55462c06fb04224a596c09e027a40e513b168ee3fe8233ce4e9697ff6fcba408b91a3a0faf8f526b06498d2daaf90ec94a1b11d48e1928fdc02ad00d1faaf30f1afe2fc1fe0e0390f951ad7d8b59d321173d731eb802d2a07729ff357a07fbb22b61bf9231970c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "1ebf0972576205b33c8ab0c57a263eef115a28097671f246197bbd9a12d8f24c", - "proof": "16f4487942ebf37297a67899d90530bd7e1105ef0ea29540163b063ed8bb7551e86ccb6df767b71d28b52f28b8337d58432b809fff877bd8eb7b59b61702b932ba66cd40d2f1ab5b31c727889f3d9e8bc7942437e5797edaec25e7106c6b636c0433f247ef27b344013bcea1ed6561aaddce83bf33f7a974534eb529ee1a6d62c9101600a802977573d7dbe36ea2c04d3927cfd758e07a2ee59d7cbf0dbd5b0cc140e7bc3ddc4a809c9e5a480248b53c424a17f8a989142333ab4226f738d001831af39a6fcab436c9855f2680e0274c08a2dc982dd14aa1f272ee3a527b0903fef5d54e30e007b2d340028f4618690277e2251522e77690c6afbd1ae91c1e7c08865cd48d5f6750a50c6d7a05cdefbd4403f37644e07a36914fdee38a35670a06b12c09ed4906627450f71f96bca968ffa9c48905ce7af6585459606abe796ae41c44d8d8b1c308b739afafaffdd5aae695914315b2bcc510bf97bb1176de0c3a9f073812cb51f170473cff0d9d0595ff5f2becf7d45600dde74d46fd143b7fec1618abe2396ebddac21e402c70064a33aa0d10fd5fe59b211302800a1b1c79161f14391268e7a0ddaa393f52f7841c364cff3cfe0dd67849917b3e6e27d22c0c905ba03168550f6d530090f84f547e203ecfaa66560eae7065511b889ff418ec1bd34883ff2537f19fce0e726add9486d5308dba16c93524eb1a535866920104788d0cbbd5dab331ec1f634bad20b48018cea0225954388a22c4e01550c108ec80e8ad3d64bbd105f2fcdb529ec28c301f20e021992d3961cc177f67c7aa6a4ef8fc0f3c83f661971299de2dccb46078c56d58e576734668edd5acc568796aa06c8dd2f0f41ef112bd1aa914ce3711e43e87d83eb2dbcd39ccee437e99bb009787cf5798921070bf0aa43744ee0d715a391c6c555359a1294e9a8052e37c00" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "287edca064bb17c445255b6c05b1263f9a87137c2c15a1ffcb4f6e7601bef26e", - "proof": "4e9316e120bcd9e2aca53a3e0502cfdd6f7fd6ac5f8945715124947126fcac0ac6c8d2012350af481c0facdf95d5359edabca41fd1a31d4fe2fa6031907b55765e648ab669bac520d82f66329c7b4fddc1066e4bef10e30699769bbcb6bed53ffe6e6482fb5331948d3d0db40a7805ef36e20fb7c480f250521ef16b215db05dbfaa3439cfdcf4aa97f36d726d8ae0e078a8abc90fcb32bd56d5aa795a1f1b04c7539fc7b62281f8ee1618720983aba10d7a683b84a42729b545b4f15a61380ebf3aedacabb84d5305f05d2c13982019a70eb90c14d6bcf7bb6d71495689430be241948efa241b6697e50df1d9bf5e6094643d728fea23b2fe87e147822e7138a0b201b7b262777e6fa7bdcf1ef817fa91040bca26cbfb915cca85d439173e0fc6ce6cddf6b126500faee5227abe0a3d8bf6888bfe579714adcb737f58d6591a20ee0b8f8c5cca1faa54e49c92f4210a16919a3e8a3ea13ecb4f878f10bb305e6a199972babec53b47fd71971daa3e5b4843e1476b85963636f7f42888674d3dd08b2192c0af1bddbbe36c4fe1c91c3097720df5d5816eab80cddd88684fa12df213b744f303e7dec39cd0d5378dbd2de1a8a97259490ba4dbb00183ba43cf59e0e934a6be78815579afb16db5a61afc32de080b9a7ae5c6bcb5791861a203548a21c69ea30b1a12525eb39ad90f17a17edb63fc052e9d36c0ee1de4870d8f799640c169d9e4b4dc5792c8f18720929ddfa59fc38ffa5258c49f53e4ae9b714aa0569aec72be008aa14ad0aa2c49ba750605e08d3a303a0cf47a7b11b4041e32e256b4ad5022a4078c665ff9040b3ba02e13eabf366362481a859e135cb71b333f54102ea993df507aba7a8e5bdb6e56b44d959a157459e659b8b73ba522ce0ec411f19b305ceaecbab3a9c10a95dce7ce44eab1a2971e3cd7c6fbad50c46804" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "4e3a02301441d276ff0161b692935dd84e8736c7381f2376c9cd65ace56f4149", - "proof": "709658d9af8bfe95e0552d64c6b7b0aaa239465b1214093f8254297dbf9e913b8ce2ec56f4f7fa5bd84c8811db49c0d88974c58022c799b22c2d1ef31d1fdd74e6ff052203db5362170f55a777b09aa5ec2dd40e5be57d11ee6813d94b76f463606adcfbd4761875133b014110de358942dd9cb25e6812f42fa0e396dfd2e55ecfa05d25ba5d0c2739733450f2be0a861cd4ebe8d3748a814822083ededfa303bca58513a0bd19e685450d507bb1fe07893679030686bad1a6aba62303fb91034ddfb5f3b87b4052ad5876a233b1fe02d4bfabcf3607f96340a2015ac2eb7a07aa48e6f7eaddc042c9b806e803df274c78b1369d6f06ef20fc96f59e1302005b465aff5a1ff47f59f0aa480cb7b4ca88bf43d88375b695ccb51f75b2aa32d968f6a4d739e0c109a22d8e0f1f3f3fb951ae504f8b1eedb6f9fcec6d59bda53074261bcbf08caa5f185cc6fff5d478353dc1400ff14ac5cc47b93724bacaa8a56ff04012a0f2d72f72e7d4d7a94fae470b74f4c83ef4fa9b2690a322d5d7cca63dc25226ec2dc4fd28c03f3c5f5d282d2cd85c0bdfa53849b51b02b249b5d3d7360c3035f19d83846fe2ec7981524130974ce52293c11eb296900b0220eb16bc7fd02f748fb02f8b119aa6e4f20bae80521cb016ffca730cbe8b896b86883cc911569db740995a8ee69aca4a4f788385118630a9b420e5286550d64ca2ad68757ce0b802a292a233a51ac85e82dd628e2a0d3eb7bbbd12af48cf8aac1caceb5d5a20be06d38ec0e186a1ca9d61342775821509e23d49f64eb7a63d2a92f4f6a31b78f46e481f759df1f8a8c01667b2ee8e075058247f4bbf92aafc31827e70166e4096ece7670f1f8b692b3988b1a3ce77f78ab6267f0eee38d203b9303369f2074da4659b300c793333f2c062c321bad6cbcbb2e22a41743680ae12630c04b40d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "5a0b9e3ad324739ff36b00c0a9678fd4bf9dee1cb152e2f9f18f2b65dc843a61", - "proof": "9a3c93f6f7d3ae9f210025dc5ba6163f0138fce219bb7a38bd5e958f3b597a556c78a6698d37ab70b89bc6d59229c3c0a2b78342ce7f28c504c1ccbbe91afd0fa0839ffa2e1980edba456e9d491420dc66d55eb339e1d4a91d973bc7fd4b247116b05876f9dea1e576397026da04539db4d76c235eb2f2ec64e25c41ae240d3293400adc05cf2367b833270365b893f54ac9f68d048d4253435c2f6a4cd9e2084fcd996fd7a4fa4d19cb95c0bf815a9d6fb61aa57421a42b2b6e86138a05ac0b3064e30277f7be01b3ab367a9a4e9fbda7d8afef37d63994eadaec20ae471e0892a6e041d7ddb30cd6010a2b95372318b7cbfb5f291c22980662c758448f9c48e2a56decef9ddd60843cf3c78faa0c1e22aa6030ee081194697283348d1c1c6048f8805788b4a87fe2b7aa1ecf460b29b621ebfca82c87688151bf7bc668025134cb480d8a0d84a9cc9da2445e750670a12caafb2cccbea7023e80dfed9c1644522189aeb392a02f6d83bccdb6c440b3a6ffd5c5f330bf257e87b03425e68c29fa5339b06df1b796a386b9d29694744936c037204a241d81aa1d5cfde344403b60bc7cb218d76fb5f2a14172f6ae1d2e1e1548f146919bc5d12345c6031ecc52f0e8afe9335f52b68d769f02f9bb73bafdc6ee869900b18322cfa9e969d95700d8762e8e09970ed61d45c8bbe72a7022ddcebb38f06f435a316920e60070f003b41c947c99f94e10e6e9cf7c0bd5bb458e8c1ab084e1adb4b3fd32177ab4715b380af7989da32ce332cba89dfdfa3ae7cfd3af45728e4e78aa2c3fbbbbb20b641607e7f18b89197c467385dffccd9e92440b290f9b463c7c4c75d6a2589f8769a4bd0d289adfe0940867cc14a15c4ad4addf9b5f8a54a37c08d1cdbe02524c062c0e8991c761ed3f79aba4ff0428500fa5ee79487f713f6526a25c14ef400100" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "60b30a94a72b2a856c8f8fb854e3438e1f3fbe3dc9039cc2ade0ee4559c98d6d", - "proof": "f2766a38c37088776d92bccebad15ed09499969e80370da32b510211dc09f20348441d1e6f3a18bd66dd1e0af10133a60a8f3af18d96e7f78221eb16b6d9cb2a10aa95c810fe485e62119577d8d34f7fdb7c705fc4d828cd210084b33d3d892ddad673a3c182999271d67272b3429a77b57a034ed993e67dbcfe4e13fa6a314a8bbbe6915486a24ae35c5e850bf310a7bd638e33636ca4ebc2d4e8c774d77f012b8fcb290b1c763de4ecb44ceaced7c5a6c17b68b99a84bf2814bdc4e0e2f10af4f40ec06b0cc3c2e266ea83b9237ecb95e4e446b982308ebfad6be60e51ab07e6cd516e4aaa46d7094d55a08b799d978c0c465a93b416b4a4fb0fe1e8678747901b89c2b62c632382233229cd9821395bea46de12d29657bec3737311ce500c24b61d3bcc52d440e0d6932325072f14d5a644c51fbb4eb2e4b5c00f89350c583a83305e94d208d1c39b30b7f98470f653423159df9f03f9534092aa4ad3736552a2ff926f88e1a4e8a1169ffe3e387ca65f3ef7d73c4eba33e607b03e328b6e8a056db399cd92728396fbfd2f34fd32daff33dd11161c91e25b65a0373de6080a612fe02e57548a28941c0a771e18f0bf3b2fc02483ffc6d5d9b9b254ba935b16fe6cd8ff4cea3f2ce6fe5275d0935620f07851f0699cf62f6302512f041a43a4e743e46fd6396c6e9b9dc1694556f79d5a01178d9644aa49e7c41d3850697be4c5b36e9640039bc7cf57168c42cf7ee7f5f682185f8ee889c6bfd80669c75710e99e2b8a7fe9ac4731372ce975309a2d5c6e48fbd6bba37c4497e16150206276650630b6b217ba8de5b448b1593a3bd4c88fc8c966479d6c7fdaadeb40ca1de1693f62363ced0d2284c5b48cf2a1237ba7bd0c03b4fe9362a7863a685482014c00093ec08c824b10d39dfe6e4b2cf296bd950a3d1b8c5484c675705190710c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "6a95080f8ed760bf34dc8f9e624f9127ee9567103e06b20c79e0c0d81dcca47b", - "proof": "ce03a092b6a71c711819d2c5f3ca0a111d78c928433a3dd501af4bef1dfc6d54544f5559a6f589398dc564a9755751b4dfd6b1d1a1bf24a8d3537ab123ace459a8a1a3653907e00cd28b9aa73a9e0a70e5861c58bf175180648184e1a1c3a714f2b14bb88dac405e3375dda93be24524bf90fa601b897802fb32a94f3a0e063b3389b8cdc8d2b945bd3f2ce3fce34a0dd032dcc735eb3233b20646112be87b0aabb5c9dd8c7c8949e3e070b8f8d2b36f0cf354c7e671802d7030ab131b405c023bbf1c1fd05f827613ce4e11096a0698dda21193a980f8957b46384a7aa551008c7ab60e7d9cb929793ca8729b87017335f4ea650550ca80c4dfe15cd854366c7078e1af74c10c8ff2d65891dae0cfd27617c457dddb9d0dc0de3bfc2fffa504ce7b63278d0f6504d7fcaaad26ad5ede2a6bae75a5818f2752d94379cc954134e2f17b3d34284663a2f2957444af192c32faf8d94097be235f3c452ee5742765f21a3cf88aead39f0594104bb2f5542d36746788bd4400d00bd2d03100653f31ee6cee9a521dc2791184e210bfabc2607c9efdd9fcff3491f01a751244617920caa757807b2ca06f46e0ff1442f96aff455aa8173a240f1375c8cea10d667002426e30772aadd3db1c80f693b78f45bd86b28cdfa229e7cf726b1ae7e4411e45263469d0818cf7b8e8e9f39f8e9930baa90291805fa44604035bb4d2499bc161f6f10090e59d037b20ac55fa4a71bdddd901a4f4c656caefdaff6a4971915e072eeff89e49061cdf6c5a988c39b911ad00b53fd01fb41a0cefeaf93b0031bc73d4ca900cd6396d9e8788b30d6c7cc2b49a8fece507f9e4f7c695ad36ec8c6d1df5ad1d3b73adc67a3f3c17617a6789ffcb961bae110ba1c728ad0518b297ec0df889432020bd2a6649290c66bffca3899d31dec9ed23443c958e5038f8c6cc0e" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "9292e58ee0ad329534a9c587ffd0636efeef8fcac96c0d8c81d07a8f8efb120f", - "proof": "76631fe3cd71c1c5e331fe925ceb8871949206ab47d759d3bcb9f9538ef1a66110c6a011df20169601c02b5ffa59902ff2dcd2652d744d3c1c4a6f4cd406fc100e71f07a7a1ef08bad3e1d8ac3cef2b7e352a5ebc2cd4ee700264df30e3ad307bc483c3b152596f526ac8cf0115a3e007ef00b360d0bdf25560516d2addfcc3321d3d863b63d0898d5390e3a678ab4b7ac4a634734bd463af03a4c208337be07db7d8ec5e41d8580a69bdf592998ecde0987ae2cdb5cc80d4932eb4788b9b601a1cab2fc0aba51383e1d26e83704a3a9f9823ea9887913083ff0935f9f78f30f46ed8129a527e116ea2e2259bc9341e3d1eefa978c273422a96ca729f931446e78e44f64ec13e1702a36e5e1b8f0aa3c061629c90073383552f480b093a7b5783a2fc8c5ca2fbba16c32ac9d5264cc2222bb552f2f9d82976365660f67bc5a57ba099f5c6a6a241360a7fb46e437dc7ba1db5f345def6b92197e8c42e2d89350da3d5ae7c89ed6cdb3dc0099cf9f06079f68d65b6777c48a6192df14f200274d02a75f8ddd0dcc83130e1036f7fa24de7c26afb265e9353919c8fae080333019d88ae0c29c1297004607bab7b528cb386a360c72989538eb7f3eb4cac26347414cb91ded861297b3ce33e03b6968dab6cbd6fd1056fc9621bf05e0109f5beb0c1a87bb34386612df659a43a1a6551cd0299facff23a02a9399c6fde1b6408062a86ce1d8173d38270b782c77fb2419721b554727b696ca2ff099f7ba4ab55c105c813db50e4fdf25ae02b2ba5da4db1533ad5c7122c9045a24164dfe024e83404a8c4c88ca2fa826cddfacc0937fdfb35006072efbcb1fcea37d96ee3f3f19082cb2b815f4ba1cf5041b106b938aca9cac5ac621491a996bbc04942a902fbb0d134d18540e0f50aa20f53bd1d7fe5cec506928494d0954ad8c278344d02b9109" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "caa7ed7b940146fe1b65b332a56fd5aebcc1eba77516398a248881762c184e51", - "proof": "e03637f2cf5e45d5fb77a240caaf6c39e4daa599f25426f04d3f990d7bd14f119a12d4218a534d0eb7f718b6c5e7d91b558306c6b7458d1c64e5516d9ab02e483432abc8126b159f0c6dd35178de0306bc55666903eb4ecd4e32e53b4749627eec3d1a2891b8568b15a2eec7a0a0e093dc61cdd4f9b87a09e0572be16ef4a6471944edf57991e481674af0374324e6a4640ed2a092d77c210f57129de65b9c01639e827a3f7ef2e57c2eafc220968db758b4cece0516b3c4243c66874246ce0453389999b3927fa43508f7b4331ec0c0508e0f6e5d778a91436d69f50feec1070afd402d54e4cbb745879ab7d5924e1f7f1795cbb912b2db6c8cf68c1623f031f0c84f8c126645c56f789018a9bdc0c3aa33a59371f7b552461b2215b66cd87a48a7373933c97bfd26b002867d40461d20f03001225b0f6afcfbb01bf1c96c5ceaaecf6834ee8e66a59654da44b57a746c9f35287146cb8caec4ae1af953fa1c562c2d70f4692547a7c66d404c58f10a578f30907e66705649a9f12928cd8150f41504919ff043e7e8f831dca509e25369b03be75dd6a2cac43db36933df3941eeaaab1c914d246a72bea6e0fb01206b638827099fe3af731cce72d76561926976e22cbcc8c783e82228a6507f38ede8e1d057581e62709205f81d611acf39556a1ae0044be1d485a323e2976b50d59e7f719457ff49070e9379590464226264aedf984c55a5e7a4021242b34fde689c65f8472f41f4014abf25d1f65e8e5402d006c6a88c10e63623aa73aedd475b56649062a0858a69b924d9276c8ceddf69a0e9eb667184004bac416955acb0b44d16fa6b042c91805f410cb85360f7e93fdda2c53c7854c9c9eae0018fb48d79942ce4a8a96f5050d6e11ccfdb0c000409053c77620e2dc62a01ce8fa060d7f343a52c0dfbb8a990ced7b046b297ffc60a" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 43 - }, - "commitment": "7e8091dca6e8f0cba091d67979c5370e47f12e2ab56e9b469a5bcfba3e983401", - "proof": "44adfabbe610d86ac2cc80a1c528c84f782567367d15ce50c063ea6346f0905c3067a467dd37a8976fd5951d9ba9e6798974b43666edd1836292504a1871b547a824fbd4b6e2daf6760f2e437c9ed0d6f5d68bd8dc4b14b50ab2436dcf77bf7e940b5518c3a5432160b28a16923dfd4e0b6b8a214a15dd46bef04f79766f5c62ebb763d69ec48001034556db339e8baae1c24b0df56a01f3a576b2d82593940841ebe1dd95b7b3f7ad4be834d4302d253923cdde922eadeec05fc1afce6ae60c97053e7866044f4cd9a7c530d9feb0ed0d5b9a622da85d46760d59fe4f448702007376bd0e064a340e2523b32d95ed84da0092363c12b9aa3fc684a4efcafc1c0ed7a50e71b25682e5fee538f0c53dd96ebe265f009d6e281d9ca228239ec963d6eacea35a011b98818e532653a5e45d3afefe4b65c52b09fc5922d2611da46cc8a64a4aad76713f7a9d71cfef570026a78ad2a00cf786b8a8a4e46e2bc36d3ab0d794e513a32af66004711abff557da62d99d600158cce55d937c909e6b360e9639a2eca8f92991a25bb8042943cf813521b05b06b12b9ae8ca0a8643ff806796a7c182b6061fb62d0363e640270573552a97be008ec5c79910aee4126a86618e4b2829d263c568ef5f57ebf448e0cf77f055cf3183d7d7b6217f134bd94d16d6f85c3259d40ac6b52cda029681729a5a206bd059a4519f4a9eec599817ce2eec3259626126d3d6125fd03ae06f7ed91cd940464bc17158974b2491ee58f222c2826c61364cbb612441660e669cd282822f34cc36a295a4ecf63691e36b360e68c8b30c6d2abfd577cd9751fce7499cf2276c8a1b4d9d01c7b89254465ff21d86528faa414a34fbe207f09517a0d6561b5ac8a7d5cd039488c958589b144206825fec37e949e16ed112db5425bee3bcf40ff81352a1e22ada1f062ba74f3a06" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "0c73b3bd674852e3b8a3d16f05d1eaa09fdecf3a7b427de7d68aa3933b42552a", - "excess_sig": { - "public_nonce": "620d5028c1c92f6468133ba62698f7666b46cb9ededf347f07ce8ac040628f4e", - "signature": "a10c4362d061b291586d715564e6e691a21ccd3efd57c4e4c9ec260c54b09109" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "1e5fe4b3f8036b40c155b1eb8326a0d4552304b1b17ba2c241dea76e35477216", - "excess_sig": { - "public_nonce": "3045cdc6a968e571dc599a4e9ddef1432936ef89ddbaf509c126a6c62e721034", - "signature": "fea1943301a49e88fc44c7adad77ec6d41b05f1137baea88b65264a06ad36f02" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "2c63062095289006f82355c562540334d529c924515e9b708f05c1cd2428f53d", - "excess_sig": { - "public_nonce": "c0bd1ffdc8550bf02dc2d21bafe771254ccdde9c2700d8df076a327b94ca4622", - "signature": "30dcda785352ad7920679b5c4fb141bfc50fda3f1b99da68a218be61428db109" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "708c73fe47f2676825802f819388f79b4c1e646b86c62604d9e5f626432b4002", - "excess_sig": { - "public_nonce": "52fd867be67b9d5d375e8af4b8c9fde07e0744d873de7ec7d6cfb3f0aab55f32", - "signature": "ff9982e7ae020f86417ab53eff4d6bbb9a3492d9953de089a523a749ddaceb06" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "a415b640ccd133d99da14f42776b3230785c6b2902a362add0f36cefb073eb7b", - "excess_sig": { - "public_nonce": "ee624fdb388d056b73ae084456ba71179fbc4ba2e5d1ddf1f3b3bce8cb41e62e", - "signature": "7d1b8b7a579d7321c1103624395c213af532b6f48e99d80bcc2a72087f2cff0d" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "94f5f052de8981ee649c432a1d06f7106f7c7ab57b3a3a356c609d55c9dc254c", - "excess_sig": { - "public_nonce": "b6e9d31ebb31d06ed465415e25527503b25fda1766029c58161281c212a37822", - "signature": "845c5f54ed18b912008c1d80b4e1a80abca7068b8b9b29639bc083e8b0db7703" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 43, - "prev_hash": "f2154d47b578bacd825232845ef5c41bca8037d0ff07e1844e2897c46a799f82", - "timestamp": "2000-01-01T01:44:01Z", - "output_mr": "88ca24549e43817af378961e5f737a38eeb4726d7be232d23bf2f58f63d0bdfe", - "range_proof_mr": "ac3a1b00f103fa95bf1ba68f10e2e55e6ddf5de1dfbdc54db48f3c8addb5149d", - "kernel_mr": "e50a5309a25d4621cd00cde708131cc14acd3e2c186524e8ac9debf9a4f9a466", - "total_kernel_offset": "fef4bd24d1bce8fbb003c9c73128628107ec2986fb9fb3b14ede9bb12202220b", - "pow": { - "work": 43 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0616354a874abb6896ae843b3f1b83e389659ab9c25c83e2d26d9ae097023945" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "60b30a94a72b2a856c8f8fb854e3438e1f3fbe3dc9039cc2ade0ee4559c98d6d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "6a95080f8ed760bf34dc8f9e624f9127ee9567103e06b20c79e0c0d81dcca47b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "caa7ed7b940146fe1b65b332a56fd5aebcc1eba77516398a248881762c184e51" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 22 - }, - "commitment": "044d0c6ac5247a4bbaf89906e13a97dc142c1e59c2f67f9c972697f004506f10" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "2cc4b924be6dad39e61fd57d3652877e00467271c8d7db12e6411b1dc50b3533", - "proof": "92402728e109a177b337705ecc8fec5a3d74317c8be72fbb785a3e4bfae0ce0a3ac575b016406f206a159c558df2afa0d50dfec1f9d3a69e8ed693963b2dd310ae24c3199f15eeeb66126370336b100deef495ec3bd3981f890c02f7a7f62c491e97fd4511869bfd617c6ed115166c216b33d4aba32f1ffc35f750d4b0f64229d90e248b52fe952657d44d36ddeab96e86201969451c7a1f11bd44693c5f5c0a5a1259b770c32b22ef020a83c1d5769c3bd563f36cd6f77ff475522e99a0fb0e8d4aee505de55dfb264ab8af74af3012fffcc8b1af52766b4e9861455a761d079a8ef109eb164ca79818608696d0c3ca25fc2f26610b049a276dd47caf109a34a6c4bd17af59f23cd4ba82a9b5bef358c4eec513d5c893b5ccf02021ffc1690ca0de3f0da13619a5281a2d78a79d64d26d3187f2476110f71083c80e2aa43f3d3af4ea6f42d30b99dcb84580587b0a934362619a88986b12f96a59d2d1d73f2812393b430dcdbff0574ef59b823a5a7c2a1114117a2007e39f6528acbe49a01ee0bb8ea6f28bfce2122bef783baf2ce76bdc1e8a5abddd9f234398549307617ca826fc1cac2b93c7a40671c65b511966b131d4c1c53409113afeee44f6fc8378c429980348ac7adb8e4817e43f8ab3be24fd555761c535d9dc2e7b265f585a5d3e979032e45288b21f5afd893773b8805c7ee260a42e73e619e50ed77de2f341e09929d0d23d4b9cdc12ffeacaa80834f789f21152d7b402738ce2d416bcd041dc7108683e4b50f8cac9e78209f8b2317115338e2b808c0005344b67e7e09e14fae4f877c2373d5060cae9dffc8fa14192f97fa15e7c20a9f9b606d88898da4d76c4124cec5def881706da66fa3d2958e5a6588313f363676f4a3c8bc468a304b4342f4709236d1968b1994db32494e77c816122ab6546cd74eddca59b687a0d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "3ccf80a7b07e226047b4a21f37ccfd2dd4bf985bb3a498ccab3c2cfd90641877", - "proof": "9edc42ee85094ed0da954217b3a976cace807ad2f79219b021353fb6ffe2c501c24f869fbc78c55b67c6df9b91f80e98721c68ad1b0c0440bfba5def56244767e4f13e7fc7b1314d9ab870d3f130e4496e0a71a45130089612546d04e583257a50d9161827ed52ed085b3a357cfaf2ffdd9a5476d78f64eb30ca3d8f78f07f4ddb4eba3a914eea989b62350d4b413acde7f1dca02bd9dc9d6f38397ffb86ff087a3cadbce3b61cd4eef56383fa9cb6bd3e8de76826e983ecf21e0e4f26b6830260ad94618f1b2d8be615d865d2ab53aeac4f5751d72410cc8ffc9b1c29248d0da289b7600d48740ce33d8f19c3a73396ce6437d336a1719adf030c92f0671e15c478c10e2119e2ce8c098f2a47fc62eddb86b7747dcfeb9f05ba7db46d38930a2203471eb25a96d1d833ca9d6ee2cc89dfe15a73847c1ac49c1dab5098d23a141ad980096a61ac0759557ba8e5fadcebf599e53eb67dfc4a1d7c2d86b4b8f50dfa9e1649145438f399fa46f44f62ed2b1028c2bec02c2aae47003ddf8ff1d03c127c359d8bc56826da2f49ba3445ad24b0dd558282956e8fa2449ee5c4b3524d4e9f68a061e11366975a6fb5d626ed8c34f1811c15ddfd76b54c558e053cac1f5ecb820a73893055cc9a61a656713dc627eb57280b8c12d08f1500978c5f9f2e56ac82a47ac364e95118cfa6294b9923421996f448bafb1a733e709e39f7815b20509c5af4d1009b70792919d3fb5b7a02667d8f1d788bc2efb55f3fddb6696768ef7153efc47f6e9863454d511175c181d94448bf6ce1d12481fd715c40e964e45f1db978f8a0b8f1d677003ff429bf40ee0d4ff8b66aded95c67c6bca4683f002350fd60471a201dd770ce60ece5d5d9e6176503af55f8e970af5d9d71ff032036b2bdcb4765998fc7e008a7e5260f63500c15b9849d0a4ce9e8d1ab925c0d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "5a963f3bd17fe2300d1d7277a3c0c1122fa82e9296ec22d5b1f475408bbe8629", - "proof": "363534cd36ee4e79126819bfccd6dadfc60659ded30620c7efe2b293748b4850582283314ec011b3a1d7e3630bf48fe458200858a6921aa1eda8b7378e8a8f4c5ab0d1654ee14926774c85aa4e9f48bb32e8e59cda43a5bbd1c20ed507036208aeea8db46eab10bf68dc068716f14e1fc2c5f706d66312b0d2035a08881f9c1d7102c0c71db772f0af62dc10e2086188c28c35c1443e732bb3fd8e7d65793c0cc6eb00101a3ae8ee16e1fa7007bd1c6e32412c38713a68af1857d8e7a1beba016fa99d33ed4cb58595c48d55e413e1c83cc1f98b6b254e7bc046eb9df801600222c747dc8374a8dec984e8a1d3126e2dfe0913fb015e827aba64a659d461730cc40c92520eaee43db481987e22876dabc79d1619aa29cffa5644a47441027a6304a0e8135dec6f5d393269d48917894e11df3d9364dc892937f0953fd61fe22b000aab8a4d207d786ee5f07822f05a20866749581e5d2fcf678a62e841840d513c4c195412c7d96fcbefc3a37b42a23f159a097271b7b54802788f4e39fd157a04b338b81f425801b2f374f05bcb34e2512db30ea8ca9936a3aa57a6a61ad8232424a648b0c1d45ce5a72b138ded1c6f71778cad1b82c03e3d749a5c98abe33fc2b3d08ef2359f3385676dc967eb927c61d2032c6b2825ad8b7e4e6f4b142c60ceddac90ab15d15361c2e8fa20587e72d20abafbc1fc2e080d213e673ffba772b8d60b3452f6b15caddb57579c5fd6a3c851ad53eaad4405d2018d5b8fb8d760849776626649a7d051452c47d10f8732a6f6a1b786bc29e8467f24d9eacf124580956e20ac8dc7de6854a35517cbf95b166b60f795acb2c33aa971c1e52f872e217d94659b78daf80a98df1158fc0321ae67c3d8a1252ce2a02eb87e0849540dc671d2215bffd1dd871f265627f6482dc7d5188717bf03c1656d69bd53bbc20c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "786fbdeb2937bc42db1bcb1a7236a6ffaff81d7bd198853afe60f623af1e366c", - "proof": "843de2796360c0b986d46d5b2c957b64b1bee424bf6f07ca698c9c3f782d15408a7752f7f27fd8181c7e201819b4aeba414d4de5eb861f414ceb74c5eb79ae3620c658cc87479eb4c9e71e800913ad43da961f20e4887f4c68d4d3f64eca3e47be639adf996640d1513aebcef5c9bcb568f7cb5ee22b72d91606764eaa314057f24dbba7531a7a028e7dd94c6e7a0bf1072e07c3e82a4b724dc9b7f270b1b600c65d0112c8b86fbda0e9738a2ce36722b55178eaffbb0e5d4b98ca9afe34b20aa2d2005f475893f1ffdfa6131ac056657d295f1a2b6915ade65854464077d209426a7cca9f48508e94aac85c3ab343623c26e10093299b0ad303b3ffe354f7694a2247441cdacef5c0289ae61f5106a512362269ceb83d60071b2290b5b01838120d0fc6d3ab580cc94310655b08eee865a1f45b52079cc90ee831c7b511fc23d65da0a95f80e04a47626f687714c8fec1b1187753675080eecdff649c3119772a94b4260e46910651bd2c442a2a33d5b1a99993a4442924a43dc6768565353fe023692936b89d3d45808bfcd08de83b8112f81b66932f0595d2996eea81a158444cc2fb197a19aa30bde80ec9ead384483827a524f2e5b78d3756625794cf138282e71e891eda05516fa28af468a616a8d7b92fca83216d3056b923685e34219a9a0d514665eeff5e3add6a4ec82939198622969dcd1ebd26a9b763cd3a0361cebc161825e9622b5013ba2415cc3583237bf1f6b71f49f0b040b3f771c36d264ed668e03d265ecfb6daff1843b2859879d3dcea4fddc4d24eb94ae1ce1097404a54a2099a6f76384804fed9f92689490637575d9ff80dd8cb8199ab2431c3044b15c39b1df810f2c69573a5116602ce234061691942c4a7d136aa598870c50831c5238b623b808a043107601abf72345986b785300cd22fda3e7cb7d234cd07" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "7ef85b43a1124f4204041dc6b3ce420ca5a6ac314cb6660b8cb66c52645f3608", - "proof": "b2151c6a1e8588a9ae361f728bbcef5d6ff14245e931df824f6bb6eceb2593536880fd932a4b5729814b5298a0035501aa9a4df180cf3c31a611b815d7f6e90466bc932b6bb14ed855e644ba51daf9b116b3779abfd044fb8d87658f0310e416d0c7abd5d1d386a88bbe5e718fe7fbbc9a1c1932a824f8669b2c8bd9b93e3d4ad328a881162c7449325fcc869630915b6482cf20a43617c8b6947d561debcd0708dc6c79db112a6085c885e809ed74175a4d8049169a9f7cfac59a928fa3cc0edef6de3463cf73735a586c850875b94541374f612fc91e02f8b66cb5d39d4d00ecfd16550b4f599bd132b91910309bf0ac6cde745be778accbe5ac47d6176c698a85c67fdbd534efd4e5d8f3bb91d274e08b1c1389b137fc4b4c73cb2c0ee75e36e0e3c821d0d495cfa97e098ae840bfeb2a75d9b798001b082ca2cf7dbe35112e12a3952bc991652731b9de4b4a7dcecec86eca2f93ce3930d4e6e1b7725c7682754036d2e321eb00971a640be0cb91be70cdf20a1272ae8a065e49506924303cbb0de6d88d580c315664abeea4daa6babcece95d52c4d108dd574f0536744f2afc37880611e139d5c9a6fa5e8a4ff73f9f649ffecf2790def23164fa77f806ce5f6590fed33481b838535bd9b25fb54ae4dca08f89910d9013c8b59d57037882176ff27444f5028e606ed2ceadfc4c98c5cef9c37f011640069dc4f3cdb035febc55444b42cfae2fb167f4e6e3b0fa03fc6312aae4c94dae15af54d5377c7288714f8c628833da1fc7497cb1398068f09c46cf431d8a0f567d3a69904c4550de28a6539f61df78610cf204b355252356bbf766a1802d31ba6bf444a5eacf06cd74692f9b64df71d415de00a90f0c7cf5f8b6efac03b92f928592870b66df0881fb872a816150f6682793003545d33ecd27c8e2094e0f1a9c7300a3fedcef0d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "b466e069302e7360fd20b738b423fe9cf835f47c82e3567fa7ed90ceb669a61e", - "proof": "48212206096f459da8cb5ad985ee9fed81afea54d198cc644b4bb3e36625273f14a2fa1ddaf9a14144a5ff44dd8b21892b04c635c624b5975940c7cba6a402681674e867e993a4252aef667806fc3f6f26a4f0e34158190184f58217d33e3d1ccec9a0dd8dc7c78e7df4ed61b8df221488e768699f15c52ff3061adb6d3a490dbf3366b2e9b4f57e90e086eb7cf9e074039a3bea1b20a4dd7348e1bb9650430f53e1d8e430b7379e5b4e598d1a543811e7991ed584b59ad1837c1619586d4f0956c13205678e8ff10bf5638136b56fa0e0190cd0ab7b08034fff27dd109e28056e8951e754bf4b1a72f227f2f64483c75a499a94b04dde99e7a5ca075e71f5704ec60e05a8141b6f632dde8735268e30e27407ec940861bd3f985d245c77fd63fc68d7d532f33ad4dc93468d00489fb9807f60880427573fa7ffcbe5c276f504544573b045b3223b7e9a2e5d0990720db2ca1da839d040ee760e6b07d4a2397372a8798d76409e4453c53050bd9abbef1b938bfdf3b4c94a2984909cbc37835a94eda3c29b04613b46da33b579e9214809d573624976b3564a473b743040827a7ab48f40dd59ee4c2bb8c4aab1d3994105b05123e1476b468a6bc8840eb5ad1c0a5c3107dfb89424d85b622cb2ca95e906ae55a07a5e2193258886af4adf1576d4d3900532ff9f4d1e14c15339a9616f48b92967b064738c905f4d7a30ba9e4b40aae3b555b398120da325401a71af36cd453bf25cb92c49e1ae78a3acc09c4aa288f2d6be167a96e147350fdfc9d93f677b60f6c153db8d1173428407aaa41ea6b8eb4d0ce94ae92089b896a18edc78b28b6d9c0ae4145571c6449845d9cb332bcca3af3516f0e7ec0af0aaa450362b3e9df4866983fc85955da11e7bd2e900c2232888cf5fb5999baab08b5b056747df5a163ae27e8eb886a8dc50acc58f05" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "be96a5e5bba1c9b096e78588992a063009d6e41606246fc2894630bd0bf35d59", - "proof": "925423d2fb0b6b90f0796dfb5fca0b6d9b9b69387c2ce4636f22fafb3d3b9252527e6b49552abe902dc884801eea0cc950ce4569294706a41ea3f6583e59027a72e403f4e6f8181972c3accba9f6e498d68011df156f5daf362304a1564af70432fc8c9201e8f1b8042b18e998f0574597c4e87e18bda7a04430cd960b8cab1428dd6f0581b9df462cd0ce0360ce010460eb4c74eac503fcd6509cb4dca59a0125515270e4c91a43410fe32646c76973cf5c303d10963967f0cf39a9d1a79c0efdae3ec041a3aacec33a03fe344f24dfb0b14e5daaef303075b6c838ab69e40c5e3b9add6a3eff2be67b9be504cddbfb3173797dbe0666ba1aa141039149813b02f28c130ac9bec15f3ce0608a9b7495c2a7afdd2f9b37e57d8bcb35f2725e7db4c25b85ab8ddf486ea79838cdf85db37bcd8b5a0f6cb877eecdc5cd64da032e4066a1d3fc6f99c274f118e34826a572f0372b9b3e9e418c4e93c908013f9c1c2e7843d73f786d0b6b50a253edf4bc920d32d4fb8cd133cd43526a5394503226325cf78eee249439a27866e35283972bac3308f19fdfd5dd5eae3f65b37fcc74dcd8dc46aebd2dcdd15248c06691ec846220335d402989b4a7a26d79cbe338347611715e494095238be3237f70708059c6e44508894a1e5990651d6790b99f48501f042a70230266b0b7d9a33c1f3cbf94cb5bb7edd105d9867977eb8f1a415ab6bfab7162a0eb54298931b33fcd0e802a89fb04566b1f977f862745d5b8fe0c026e334d45fd025757d7bce52a6124c530367ce094fc0a30c850a37577920b6a30544b14a5001efcb539d037048a4271e04563648f84b3120849105b7d58c91caadee3cfa67d3fbbe529e56a42ccb9b44823bf52cf692922570e82e0d58f010137f5d98566f7c7963c9b312a0ea55c37b46c3750b0fd0b04d860eaabcb58e005" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "e6d693966269ed7df45e4e889180ec0655df3f679408555bf0d732b21bc3e363", - "proof": "acf20eecd0ea7d82d428049daf2d424e03fcac3fc5ab2542ba76a9a78b17365170abb0d5b9385472768e4a258a2ea8d0554da4ddef05c4258dad93575e0432250e5bcd4d5df54d6a6ab93d1458e6cb57b127f839db9b62f9f9934789d14c740bd23b4e326744a441aa5edcc82b17df7080f062780079cded807ef49f88adb75a7555f77d18a1bf0c36c14e5379a75bd8cb3be82b5b66d1fc23523499fbe5740cd6783b29a96701c37cc556548b3545ad74b3f495d956b4dfb8202b56bb536701cf7a1df261ab45125084310feba726e5130139a9fa007f17c01d9168147c71014c37f0bce53453582a88b5e86fd274be9aae3b26a39ca492933e554ed8ef1f709ad0426dd266fb235796254b2994e3b64952daeddb7365ab3b868a339d0f386540b8737c740e26bfb867af1f698ec0f987220c132672cd03ea2296ccca8fc54c1e625ba3e6e2ddeedbc6cdb9d8307bb76957d89a539e03bd580fb54034e801621a45fa89b0362825eef39db07662180ffc101a5e130625a29876b39aeb5ee838f0db14210bf8cf1ceb7e61b06c76148850f098e8636dacca94aa6388d796b20050451f2c6bc23f76a20c811c60e18a73ce30a3a38951900ed3c64340054ca76b5603ce33368f9a01067260df747ce14dcc54d65dac8c859d76e8e02f05f4d762e8a2784cd602dbbc8a6e7554a623b13438b2c3b4d2a8269585f89b77371ade40f42f2d6b9d7db2edbaa35781dbad5120188cd14adb0e78e18d053af496be1f066280a8abcd4e588d5c2a459811ed0fa2e98b1895897d314265ef42a38041115a3c09af04125ab0653513fcc359de5b57b11cc1b1c00fa38ffd7d7c721357466c743dc953ff5ff65062488fa92b4d3d7731243fc4c735927d5859164b4acba901e7de87e4b88e9915fd6bdcdb6e2f89ed9636e64a1c0756b262554794215a7e0a" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "e881e71881d734ca15c18bcf696f605e58aa0da4968a76e6a2183c21e76e6c3c", - "proof": "3eae723748837d3bad20299eb4c1113c2563662cb03568ba9afd1e7f98b09a4f06faaae6cdc6626b3ab3883b5d6b1bd21cdff2652efca611abd34eee9ea7ef241215814471ed7265d969f791fac4858e896e187943ec4bdcab2d2d8619e0ea172a4f8cdcc59ca6508baba68f9c978892f5dbd59cd5ec11c81e410d84a9de5e0402eca9b410f8eadc321b6720c241b86be64a7d08df037912586e01382676990fd9852b429f6dd21ef5d7fa16cdc90098159aea8b278169f9cc4d4f2aee49c908a2b8b298a316a26737d3453be3191f97827cee7f205d53e4454445324042a20b2a364402197253c97d5a338ac043b76b7eb46706ffd1c86315e5d77cca607d3d604883762c2417cae0fb0b3f64e6fcaf45dc1417c8a87ea735711669c9bafd0d72c2efae9e55e4f88ad1f24c07de30fb3c02e32270c69f868f50f3371d45391e0a3cebd5bed76b9d17b88883b43f74e71345b5ba7bbef683ccf2bda6c33c1a018ab31a19ae8444424df4dd3bcb74d8b5565a4ef280357daf6f53ccaacdc2a41bd81eeaac478dfab7c635e8bd984a0fd0d85549c0dde9429acc087faab44a201684bd9679d1ca6902082da9311d1e23208547898e1f87eee8dc7861c544835c71fc1684e012a1c3713f9a2c3de6600610448cf85fcbd48d5b373fb07c3e3e0d2f74bca7f1f27a5bbcef82723b482bd38384c5f2152c105bfca4e43fa2edcff9630897f43c43d985b1c6952e03fd0d2f5574a7e0fab025deadadec89a558c4895bce39a6e49fb2165f5df1b39bdd5e77eba8551535db2509fa36b27f61ac8c8e5d1ec0094f039205bfe3eb844610a774d775f3a5ce51cd6836085ef80fa1b88960474d7e0b31fe365352751a93133fbcb18c45dcceb5d9f040ad32bee09faa9905681ea472cba84a258860e15296e1550323537daa82f93becfb80e9e14f3df90f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "f04aca421871231881d35918bb66fe368240c02d3004b93915586c1bb8a93621", - "proof": "6eb48f59ea925134e1b52202d2623a20064cb1c91446ce6484b982243e2f950ec2b8f88bace24f502fc26abc57a80f70066a153af7f8dfbbe27a3c546e15b94f6a1ea7a522b3d284d0372b9b00c84ec5ee984ab7fb259c40033e6f65efecec5a1e528417ef708fa300dd5cf7f73b4ef8a2972236827996abf2d1de6e7afc9a76705a1c152ebac5e9d3b9f1ce836652108156d45519c135aa89b0eccfa8dcb100b0ce8ae31e4490655ee828de86ceb522c48a98d1fa111138013629019f05d7000220c1e0f7ee7a7584df78d497cf6dfedaa2cea7e6fe1e0debf9f7d0c815430c6af537dcb95f83f6c75f2444c52f76478bd41614462ad144f09ed6643edc2236763de5c360a6ab5de265428afb37f5adb22e8e3e0621d11e79d23b6a06b10a32503ed9e70acf3b428798bf6a61f7647bdedfb0a85a67bd4f1eee0ea5ea6cf64af0d7abcd62c744a0a8ec82b38348b76e73bcad81d2b91ebb6fe10d80b569e25a4a8f7f03da5d48f5062c47d150cec9c8f50e0288b7a2e7b357fc7ebc5ae94f44ecb3bc8dd793e791037fe473eb05cf93828fcec7a99cc6ea2c68ddfd69843f6a821bc05489ed673c914828a6a006134f3b966fa694c0675cbb5380e696dc420e9ef142fdcb70a37496b1e53152b92285320d28d97f96acba5b1301c939fe442e4ea82e06a1d2b0416514b2ccdc056ef30f4a434f1f954c323e7ca377e3761b4ca2dc7e54b425263188e7629f7aa3c331ce5f0d896b9cda53692fa5f418bec602503e44967adecc031cc221338ba6e8603e55a73669546e32fa6f8c84f3246d1732d0dbd056dfb3d2aabe634ec4b5b7121a5eda8894511e15d38704e4290b0d7865540f0e53b2663760c82ace6ee41cde4b236538fb7c942c72d3893e0ecdaf05ca5303631c800224357e35fb3223bbd16fef905006383579668f57cdb96d7f04" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 44 - }, - "commitment": "5e50a8203264cd94e2c1f71e98c0be385ee3f54c11841c506e2f693f85ae8737", - "proof": "78ece0de95d7abfb2eb9ff4c6f8d567b1ec67209bf970cf7ffaebfef9374bc130e0f9a463e5529d76a1d897a85bea0fae9502b7f253f772004eb50a9c7f4256f5668a5926d1adf286724dc8b2cba4f73bec0291cf48ddee0426f83972c2f17459e6f95e5bb169e9352424d6fd2cecdfede3f48a1c321851a1f1f635d8ebbaf136658073e0107117f53acea2fe121146b010d21c9cf422f7369f112e7b88a9009ee2e75408c916a0d50ebd9f3a9f116cfd9867ef58a743dbbf78c745ba358bc0b77f4b097910b67d2e9d05c5dd21aaf151dd5bd10b31b08aa41f00b24cebb7e079ae32a980f96aeb660329fd0cd70a2d86079c7f1f53ae82aef8af6cd41829e462c366c0246ccef97069e9eca1779483d410b552efc30b733acdc1248cd82732276116b3bd55655fb6f4a293269380eb7d3bee27089996b13304d8ad1a4bcbe47c020a8234fefc1a1d6004f90b79dfdb8dc9a37044b0eb087c19cebde9d5275418283229741131f236192f8ee627df0c1bdbc85e6f7a2aac388881f4eeb8cfd6136c5ad461c94bd75c7cd98835747fc1ae5b60cbc3211f20b9e3333b9a131b335d4c357658fef34c34d064ecf2cf8269abb79a23536da0facdb9d416894c88f631cbac392b467f0c606a7844a977b19788ef90b98ed1d8144865a23641d317e2c0804f0bcfbfbcf0d9df78127ef0a8c6086498dc7402d6760c978cb13375d2817aac94115fe5922d9da547d309a0b443d372d491cf7bc5a6436bdb79151d75852b21974d34cf2cf777c108aff552039ddb0345a8365618906e4314afa85a02531003ee8b5d514e13a8ee4d26790ecd70f4365c803c6799c908bb49957dd695e0b685751ef208372d1c12c0667a7959f29ac1ac56da73628c108a52dd1d02c6002b2018679a323eb79bd82bc67b7c9542e714b3b949a35a448feb1596fd39ff007" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "062028732c33a5131d8d87f529ccc3f8466b929786c8e682e684706a9b608777", - "excess_sig": { - "public_nonce": "0ab75af6658f82c67dd2690223f38b192cecf7c797be9d962738d2cd25de4272", - "signature": "e16e4107ce3e784b66f680b5bf048a1fda58091d355c7a4c240c760e82c1620c" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "3623041a7e4545690c16a38e5690b69f53214aefeff4b05c90ef8b89435a4718", - "excess_sig": { - "public_nonce": "b813ae8b947d83dcef88b135c02aa37dd55ee483f669285cc5a065181d663727", - "signature": "b0e9d1b3d4357ab8934ff85812a21b9a5dcd86c28be2a2da620b18901bea9207" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "6adb96cbd6a7f086cfdf94148926f59c41ec621eccd8a678f732e749b1a6d85f", - "excess_sig": { - "public_nonce": "2854fe19bebe88400f55946751f836aa78a528eb6df7baa72a9a8ce3b4261611", - "signature": "a635c79e372992d7d3e1ce4c222cb958be0f56fa9533cdf694d0f20c7d936501" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "cc684347fc58d24cdf3a19a2c82742aaa59f380b57b6ab97a733c7ae0a94364f", - "excess_sig": { - "public_nonce": "6cf286685e7d53d2c0574cc57e47ee4875204d30383ea33b279d41775b5ce556", - "signature": "a0b13b8b61b4348e75a1d79935ce49eaad88805e33c22ebe01a4c0bd323e780a" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "d6666ea8d9cb83a6e90db1f2ccb445bd3604526310c0e78d0a2548e90cbe710c", - "excess_sig": { - "public_nonce": "46fe4261f4a020d943a573f48b2202d8f5edf19118f6b56a0693c44b1bcbb672", - "signature": "9bb9f03ad591793ae7f97da63375ca54ca50bb611644b1155b545c26d64c7d0e" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "28bd1492db878e83dc97e04acb0e7ed29342a00df50fab5c5a608704d1f1645d", - "excess_sig": { - "public_nonce": "929ebfea84208a778e0e1af26edfc4e685d66e1da408bd3de2a810175b790f62", - "signature": "ef7f51b6c049ec2b20edac9aab4a25b6d41988c7113821cdcd1c519244fd7405" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 44, - "prev_hash": "d1844a7850644e476c8cee00877f3c387c0746b56dc52346503dffc2d880a9f7", - "timestamp": "2000-01-01T01:45:01Z", - "output_mr": "80688647c6eb2b7e0f827517f8212a9dfa7bcc9b9d242e09087703fbdae2b712", - "range_proof_mr": "5e765525434788bb885369a0dff3b05974b2abfbd1efc245e0d38b2abf0a4fc1", - "kernel_mr": "00087b85c6d643c5ce81785b25320ae123f2e80277a05c095145bb6e098de18f", - "total_kernel_offset": "f655febab3d99fe68f2fc026c250d4c0c99921d8a929181ddc8b2c77efe9b309", - "pow": { - "work": 44 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "2cc4b924be6dad39e61fd57d3652877e00467271c8d7db12e6411b1dc50b3533" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "3ccf80a7b07e226047b4a21f37ccfd2dd4bf985bb3a498ccab3c2cfd90641877" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "7ef85b43a1124f4204041dc6b3ce420ca5a6ac314cb6660b8cb66c52645f3608" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "be96a5e5bba1c9b096e78588992a063009d6e41606246fc2894630bd0bf35d59" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "e881e71881d734ca15c18bcf696f605e58aa0da4968a76e6a2183c21e76e6c3c" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0883c06e3cd78ced2e89a20b609163fe8bcef4327a0f5c0b2eb3a308428e447b", - "proof": "729a6b4ecfc68a8d9287fe412ffaffc911e476937530c7c1a5a11d8d5dfad768ecf42bcef56dd42a7f4ad2eddfc51502342adca050344ad54863a17f939b907760507788c1434dbc6620914e06dfd5b8974a6009e2345a51fd2cdce5c48d0a264844ab4ee3e50860b5bb2b4d7e8c35f3fa56b749e43133e5b04f7fb0d2dbb149cf3fd140347b0fb9ad34df2d219ca4df76c898666dbf72f1178bb97959b0e401e1ed2f3e3deebf677e09c551425f28af849615f54ed080aba709386e2c7efd0b7598c6410f8f33283ec20cf08a31ead8c94dbfcd76e772172ac2830d1b4c3a083c7c086175f5c52e1469372e8845faf556a7cf44283eca2bd8c9ab08152f35301c1fcfe65cf5464c1d6b5607e56c4981c50a359edeb777841b5e0099ea77996eb4ffec9724aacec4bfe55f30d48d8456a627ac47a546c2c1883a2eb927663c406815228e949a6a012e2e7f1b862f99c05de240d59d2c082b5306394d0e3cbc5436c84ab6b6264cd79bdd084e4ce49eaaee96ab3b7f9db007a9048980f4353f4caebafef8e47c56c35a5fdc40990d963961c84f7d5390907f23c8401a0005824b840fcb5995b4d72b07ff37c232eaa42cc487950d4279d622a5c9f387e0482951f04c5eb8937de927289124ccab95249a688b755ef192008c1b6b0fa4e9de70621cdd80b89f7731085a6330d8271b36e53425a8f8da659e15dc2a9771d241ee37dc51fc20c6a9bca7621b7a9652e59d8c9e808ceb0073c2b6f0309e5587720f692a9c49a4f9363c6e9ca12f54f569970eb174950f24dd75a1f047c27c4fd8e57074a7e556080a8d16cb56413c3db99d57f999ba136cd605adc36b8aa1b7021017c64d3676b9864133017d3203ec0e8024407505515a0696e5b32776445f1ca808efc31d9c40ece5cb647140cdeb4684bddc05b2f0e1fca18896a43ea49d4e3106" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "20ceb9591d812cc0deb1ddadafa1f75708be6d6774c36a63605369020d3b1664", - "proof": "c02981fdc2e44c17e64ed7e2d4093889ff49e718d3fdc608809064c3e9432a627e4a4428e911e8a1e8157cde554d44b1434ccb265e4663a8305c9b844af7d5497e1560ac69019f4316e199e238bd602cfa060ae6170a88e5d6322924a952b53ce6c1fcda21f2b0ea0fa6a658ea749eb0d7bb635324f6b29973c3f1b8f16e214340772fc83d0e1751ecaab730663d7c7b80b2bf5c2aec2f300d14a1d55d7861029e1f6fe71a6526d26d803ea7311c986d3fea9e99a41d14679ad3513ef8a3da0f6310aec1d928f6a62b33c9c2dbc861ebf86bce912ef4568564270c0933836805ca1e62b06f190b868e8319bd1f9f81925f74bce09c4d560c76faed0384252b393069ef43f4f0cb03015a747cdc3ca33f9eb58ca6dfeb914ebfe848af1e8524284c987dbd86ac178c2602d6a8e10a5395c6d9f841eeb404f109c2d17cbbec705ebe5e0525b39c62252d2b581e0bbb7158fa87bb84a141ff544239ab08cdeb177bec29f683c7d99419a460b17e944c7ece05b9282a445907e4886f282befa6da6068e31b26fd685bfcc21593233f3eeb80416cea3a290b52d147fed10326bcb81fd2f088a23e0bfa62338ff776d0dd25ceacc09ef2dc5b08a36678b29eeb965e65be74059e57dade287b7d51c694850d237446dccd38d2497f9898e89abd250d5212ee665062e1ea561cf234e22848577aa15a929b50bb00a7a5282e731a994c4e3c511b3ecfb112eafd45238b359e70006ae1b5f0a5b688970b7bd3cdbec91e0a869d7cb6c7e185a2ee80bdec8f4acb43b77845f9bd4c2ecf9f6ca77c2b998502aefdff23848214a680c21f2bd97ad2518891ada82cd5cd975229ff66ffe9825f4b921585ad22dddea8c788b5db05b23317326ee27014c19c92b38be8ecb8f009261595c950214b4431ffa0b427f1fa011febf16ac6dc95fe82f06c0c099e1e06" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "28ab40c27ed9780dccfdaa77f55327daabf99461ca43581f7525d48f41c1db67", - "proof": "d06fefae904c84527bb4252beca3635f80c54daa992ada75f486105c2cd9ee1a6278035661d3e0ae9b2cd5f012b8bf70f0460f371105ee958604075c24c82a0b0e938bf1f42a0aa0d8aa6c8078be8a7f9c9dd911ed46b80fd354ff397c421303e8bddcac81abac5580acc5022ac7b7120ace9281bdf39fb77e4458ee5c999b07c9c850937a5bb448b3de0e3034b8b71b7dfa3261cf1bee9fc763415712e7630e7375080f308c04a9f65c3ab11928e4bc219495c82ef6b0ccc62ab11b1446230ec439decc98a685923008ba35bf1e8ff27da6c796420d23f04522efd02b78210126746d6d63bd275796f876519d6d09fdf66bf60b8f0bcee31275f7b3f635e419685f19f09fcaede17df9f66dbee894a180e79a5f11e22fc45d42d3127e822d57faf47be2617453304a0538c6d0fc3c7ae55ed7f8d7be952a865c7ebb3569a7609e20572d94854f241ba7ad6b5bd54eab4e38762059a4525717aa75b8893e0c44f8a54d20b592741c6704c7b51e71d2615ec8b61dcbff2dc7ffadc198c5db693ca6618af16ba3aa9ba5dd0ce61cee4125ce7629c1c6a3c12cb40bd83a80c9bd19268bfefc1d822e3c09dd495702021dad72527818f12a20c924e1ad41afb88b0ad00c85298c5bc04e406138ada78cc01aeb784a6b2a5dc6f8133948875a19752d42abff0d92f377c695f060584bf5e786b57a252323299f2740ebef972ca6456e1e3ae76998a72e9b0d2465f5e2ebcbaad01a1cca45b101864d76adf85f3feb3c3c7a4441a837d5aac683f1173a13812a15c56ac33c2dcf3400c98a201aac750536d340b9535b540edf884af5ce5054e4e176352b35c70144d3f284ce2951a34f59ec18ccb94b2da9133877a6b0f78ab9d545166d6aeae2de832f832e7026440f0bea54d5198aaad9c4b898e0b2271e5b1c67000e64e21a10836029adb7a39100" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "5ad799d73ff35784d45e4041133ce4716764861f78d2b890defed290089ac207", - "proof": "203268fdd28d4d53c25d01417ba907b49f46fc125f790b4dcb5f2b436419d34d4c08a75cea0a6bf4b39dfa87f7ce7d416420c3597b3672457a35ae3acbf70f38b67cb3f83a8dee9bd76d49e41788268a8cd0ba7fadf19efcfec473c9e7e508788c35a92ae80e65ab02ec1d970f0ea284aa154efa4ae7c8a641735089a974f80fb48b745d9f9f0e509dc11ff9d0d54f06900c264f9308a147bbbf068aca0d0c0252916d950d92bad3413f904a62ba6293a3922afb5d3dc9a439df445deddc5b066ed7941be1873c02f6c307be9c7e933d002d4f0015c64dbecf0040863267ee0376892f1b4f99e338d40589665efbc8e85d30ab9bffe9edf5f166ab7585ee191a826b16ba15bcad2d2b57748c5d3d9489d285c2a0ff0c87bd24be3c877a576c5538be9581a44a740b5819ca4ee1df9f218b211f4ca37489a9fd2cbf0c1e4cc00c76d13a5cd98b46e2b6e1bea73f465fb73837340197eb6d85172a59c781b6db6c6841e95654549c44ebec44e273f55e3adbd5e4371bba299517b5152552cfb522f49ccd01cd5aa4d91bb53fa6266eae8fec6a256cf85c4923aa82eb827dfa4f08b4edb942f3ccf3b154bc21b7a692143339e7beade96e1d4f4390f10ce27211686ec61bf4604a6697ae2d7b7c698ed8d8c3083d4396a602b3cdc032a639190c6d9af4925a6613de5afc3b3a3fc3d27a7caf05205551c7c715a9d7d52d5e25051dc8a870fc6494e19e318bfdf4ea9a907f8fc830e39935d95c82a7f2b1230f53622863a6f4b8ac3025cf3ca9652d28d22661a18bef7d7aad731c9219176d455c09c4a96f846e154f47cd647372bcee27b90157059b14c73767c8d67bb28635d721c8cb7d8d5beb2917dbf44dde6768d5a844847354e2df54d1c6cba774de1ebd0e173b27ff348e9b1356d2544b36f5049754f05282fba720d5f4c1d7a12d1eb603" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "68ab970ccc08e32ed03302829631a63dbaab273b39bbd7d8b1a1c6c692a0f139", - "proof": "5e9fad8f8c89fb0b91c3cb848e4ca9d0f44a5fe6ed43f5663cd9a77fab7a3b49e0b0311fcf9343bdee34df96caea9d880ca04ac97ed08424e3a1f5b2d69da43428c2f7cd0a374eb095658b3ad58a65b739f252076d232a3768e097daa0dffc52ba76110bfbd54b43bb36cb4710a3cf2d1db92d50dbeac023f967481d21deac22985494ec0d06a6e7218e639995d3990b310a07c4e41bb9e6f12ce21c4b3abf0ebd8192cc29d687dffdc121647dccc18cc56c08e154a41b60fc2638b723f8dc0c779769f43946e1282f446b6941e4ff6fe56905f3a3a6adc6c7738c891fe8d903e46eee9a26b5b715b1b60e959a25f714477a86d0ae26c0945c4af97cf7d3b8233852d3270d5b0c81c5a7f4033dbdd660a01198836993efe806af12fdf87dda4540b90df5f387edf6fb3786aff2538b944bf250bc59f9ea2fd8e9b1222a7e58023c8624e22d6ab853b8d4e788e7e7d08fae88e552d0b1d08249770159bb0dc73e3e70bbfc88e9f66d5ce048083fe6b2476162b45a12c0ad89224bfc4a02fc0f75544bfa40275369bf60bae12620e68b67c111599ab664f39c2cb6d735612243509aff0807ba4cd07f04656f8f744cc8bcfe75eb005c549718170dad49f4bf72621446678905a851f685c3ff814d2bd68e83f19c0fb30f99d4b1dd933ccd5f265240f94d977afa7b24a11ea1156ac548a2d0e8c7d414165835dd242249b46d1a31760cf482dfadb9241663b12e2068cfb2df0827fd0d904542967a85c6654dad6196aa5e41e4749d34ce05a692dc1699d2e43c3af578b2ff8b557bc08c4710431ca2c6499ceb80933324ed004a730637f5b817f32eef4a0d91f105801f8b43f246c45842caea5c99cf9346e9d9f0ba143aee0dfb492f962e4b13a925defe292e0df39bdc7df0348adc2859f06d7b70ab2199e8a25f71d0f4b4577abd8066f3710e" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "6a8fbada5db3771dccddbd8df2550fa9a12bf64c45485937203941a5deac721f", - "proof": "7c5b3e5d28c5e8ccbb40e7dfd89d5db3c6df85b6ac1caf8a9f688c721f386e2a0455645d1da03f183e8a09983fb4daf74477f277833c13f9c81c83ffe124792b5e58a8d64e48a220e9b037c23b7a63c3fbfbae7e92cb1177e3c462e5c2432f173e1a433bbc94069c89c88a2074eb537b3c180f4e22f694e54e3b4cb218b24552ebc60760ad057c0283d70b98b7dd53562df5e9b3fdb47feaaa9cb6831c4d190aa1b44dab1537f4624c846cc1e84e2e78fde93461e3d2e389cdcf5a512d67360fcebd95a4f61b6c1f8f2f398f55bdf2ccdc7128759aec97dced9dbc5f269f6c0940339faeb0c5f9d59b003cba0cf59fa1939482fda7d103967a37be6111bcec2b48ac8a44fbb69f0b34dd6a995648e831f7d492a3d1e97f2a1f4d537141ac8c37049a29fe9f0b244833852ee1e1c3ff5755bdf66091736500c970a44d736051697809cb2a15c23517f237714a339d44179533400fcef973e5f42c7efe442e1d149e3df67f6a80f5022570acf9d53e4aebbbe6fd51165aacf3d94aa6c39589825db05f1fab6a7bf3f5cca3c89c5524969e80bc8d3839f2564c19d06ea75541231e56b13d951ab8dbef3528ad2cbaa4b8056bc909bef6729da8a36291cd2a9af965c8f677d7b2df706c601415d7c71dbfb2b0bab8fff305b5389043f0491a893f54a8eb4d2ecd0a17d85a4aa2cf81a096e38ac1835f95ac947d6fdbb550117daa4ef2b9300dfc23d1172f0868d3b93c5d1c705b8d723cea05aa2237959ad64d1e2f108045aa6ed89db91f586165254d7845c5738a8aa66aeeb991ac216f684c0b5bd45cab6c9f39c463070b6bf13e7137207ba3070bb088e1d27e3bceb72553c74a88ee84ec64b82e79aab4cfa25bac9db81517f7e252169caaa8c30afb61f10704d3b4947ca2934efd21867de69e69aa2717d8b8d532488ddc91dd56730ae2a809" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "722009ac3a6788bd645031cf8eec09f8bb7ae632ed1999a8d4f2316f48936b47", - "proof": "a2c320df2b966445fb81dd559a6946fe4cbf3275a949e18a45d999c8ec08de26b02830273b19187096c954a4422601f1ecfd3526fe5089fad851d29c09f3201eb08fd6a22324bc0eb0fd8b054aa98e9e028ba7f48a4f73dafd781602708ec875e6df529fe3d84dfab57dfb017646b17e9b82bffa6d0e6409ee8758a51df3ed655995e18796ddf38e78afda178a4ad4204b70c4076616e139f15cd5e66332240ab96a80288cc9615e4d11f6b8025ebbe80b71a1223b766385a7dd8eb94f29af0ae3f45d0031f4f25ef33afcc6d160d4f20a5a01fc17556d2174094f0967f7160422a36c8023293799c0ae5fe2f98cbaa2b3baefe9f06d9ca8fb8695d352ac2307029e898ae810ee67577f1e39e31c60b1afb7d9304910d167c2b54d145d0a34564ed8a94f87fda9df1ffa10922a0db99a6395a695a4589e310f8e8aee3e18902f30b0eb19ada831e10de2ed02453cf2c6280174c4c4d1181b879748badb6fd937223e44aa1b3844d8befc709a4d291d45072dab1122352df04cae464a8ada362af61966d88c72ba6991ee9b1b74eb78e857f8920254dee58ac556e747cd0eac34724dea9ebe079059d4808ca72f4be3aaefaefa6e0cca3082f02c7e5a5eab2e5d38e9d363efe950419ed04bfa7ff760ae8599f6342463604885fffbe171e3bd39eadebb255dbc1ec6ea8e853d366701a43080e3e1a2b7eabfe60762173ecfe42ee88d72508155d573f9fd533038c3641eaa11f0aef902bd0763a446275aea0f0ed6562ec7f29f174c23dbabb53a0b5cd3cb310e8d09cb7130376b1c57d19318295c51a7d6cee6b1b0d21237eda0f1e51f0d32fb2dd5af8d79c5f78d00c6019f7a9989910e8c1cb3805f3ebb700863e3f27abbab289e16ec21a5ca057e68794101bf5f5ca4cfb574fe180076b017487967c2c8d38cc2ed05b3ceb35f8d7dd09a05" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "b060989a4c557cb44ba18c2901314ebe1a5b4c21bb168121af9a86302e7c9432", - "proof": "5aa8ed1faec3b9f8973504f87120f731a23da665d450cebbaa8fffccfe3c84242e8a79b81e52487ce7ca45707700c3daeaefebb7bec3b092d3a77986474a66564c399df0478765270812adb21e1de439526baa8f1559627944692721af2f034fe2d1edcfe917f5b098ab415464d2cd9d1f67af2c51a59069925387021101610435690602c9c88f24c06139f3c26a5e348c555d866489de4413ebe0f2ef7dce01e9562daac1fa19619496e415c6bf01c17a1410e89003157fef287175be81b2072ec19b39f693728211ac9afd87605e993318da4205c606ddf35f67f7e58a5c0c5a4cc89d14c4e04a91f648d62cae4f81b541ae0ec0e996fd4a6635042ae5c9059059731b0e91d4e6c7ae87919c6019446971d3892e0c092e8a9088188d3b6a3e38a6011a8f6a344981d9139883193470347611b9f5f6456ab5e0e745c6a78c5b0cf0b83c8ac12d7471c33c5823e36a85ec52319b892291e259eb6503d42db91bee5c6373422876f3d03f844d29a193caaa6a67f56ec508b81a50d712427da24b7ed84a8a489718f2aa788526479da6ee91b0acc4ff7737ff7d85cb04317bfe29f445ef57921a626481fa25363f042de6eb4cab56127e916ce5eefc874e990546807095c3f5adf5b43eab0399075ab90006dcaa7e3838a8aec37548d6f8aa6830feccae6901fad811a890732605d21af7f55175ea0c8e43d2cd423aadccd495023cb30dad591b4d7a658625f0a737e47f528227f16a617ba82b801b0bac355f272c9572c6fdf0ff0a4e8952986d3f08c7f033bd20caf29769baa74d62d53a347f38a5d36fc1976320b50531cd672643bc5590a45bd745133501e28fc8e089262d473f8dbf53f06f57883c500ba10d8e237abe94b48bc487f2b328be6f798a810066c241b66f82e48a282ccfee5925e035d2aea4d180865cd6003f1b8710eb180b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "b4bd8b0ffa8a8b77c8eda11fb2c63741cb8dfdc96c624682c18ad3e9e030d32c", - "proof": "c8fce1de3fc96623734793e61cd901416c5a5de906eca42c780fec03cfdb5816366533eeeee4bdc8d737039688a7732425cc9b20d6c2bb8fe70dd42e2a8e1b369e8614253ac831375bb9e54bc5cf6864d069ce0a80dd3f79b4f8602279e0dc25408382bab32d6c30a21dbba0de23c8373979e99c658d416d9104fda78355e120822da0af9dd2b5942328c5095e59afa97495def604d76ceea140b14a82960d01b29b7ec6d699953a6c35d5dfded59c2ae088d7acae0eb75354344afbfb8acf0ce409ff99da6b8c8eed5e663d5aa46c0e259b7b7acc74e547e4b4da6d23b6df0b18437f89d8386102afc51c94b5e30bedf15928f3202c71a40a1cd1d58a51144708a5829c81542964ff397e9cd1cb0343b907294bbc08cbd2236a7ae380bfea1936f858350ff517e9832f4777b334898619e540e20596e17fd282e2c10af3cd37a27dfc929185aa60bcc2aea75d90bbf3c52d18ab8fab9efdd80fa0393e004b75fee0e245b453d9e9fa558eb6ed8d7959f67f85de093cc7ff7caaa0e78989ba60b6b24536acef8b20b2a4131885f7ef3d98259da06fbcff36ae3a904e25d0b15d7c90fe9009c7e57e0500549f083f552a844aa7396eb02f5a56cf8ec9ab66e876f4637a22d5d93f02403bedb1cb5680422ca271970818ca61ada144ff031b9c37964e1b2c1b5c1e7209dff235b1c9970d526fde979f8c4c062d53ba9855674621da1df430dcc24b92526c024ee0913fbdf1bc363762ad05cdaed04f6f5b74be50588c79c2cacfef073b5172d911cd3fbe44b52ee2a7c807bb08ecdd0723044173ccdd8b16d6e6eab570cc88c2fe645f91f6e3ba2b689c565ed097c574b8ecde4ad8d4ada60991e1a218c97b91bf6a1305c6fbe9fcdb3eb8c566429a457a3eda08b38942c8df2481d9c22dd4c70a74b76c12be6c6e8071f97d7fae000fd8305c09" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "be4c08c281b4ba5f637dc9c096ebf94d83de9310470530d055774fc220ef7057", - "proof": "ac88f25cb0156d405870b6c1291d4b8f6d0c1ff303edc9fcc617a54528077c218a9597e60c6e8c9bc32b9111abbe230986504dffa68d9626c02c12fe1923213a846f446ad7bcbd585acc424f28a6b6a226e37f274536407b69dcd5322ea8cb236029e664a8f9fa0762d7837b2e9f115786ec665bbe138876592ab583f4f997710f4eaa1a4b2741d99191aac65f7e268568294b22fc8a884d8e66dc07a32c520a48dc51b318abcc39297809787b45e4ea41f502c2dbc24698d3ed2d95eb1d55077429af6ee56cee3d9d25ad0ac7e4a9ddaef461c8e2b8059a21dea88e16253200967819387e564cc0fb0db974fcb8cdb3c108161dd9052e046813c864ac2fc37e3242d01e9c206639c25c518c3cc7fa5ff132764babc9b81067c384f24a1a953d60d46ff7c0f9e1a65b1dc1ec29852a66be38cf45aeca18271b045931d41dd066a2ad5902afb934d25b43a6691a41375139cbb36195aa9336ce55c84a508145068c0a94b34aafbb0d01c8a8872aeda8fa15b9bcafb50e6fd8b9c301bf8a38311fd0bf70135bcfee4111b30d1226eb53f4bf9fd532585eddc6ac7921c344916c7fe4b27f0a22c0486cefa42e5af3559ab174b3d08e7407441b35294b7fdacfe2086434bb9296aec875c5388178aef8d114a8e3ef0a26667f1f1c8c5907be9d21242490a70043637a6f58210051174ba3c7d6b3c189652cf7c46310e9366030e2149a4393774077cb2e6ad58c5a2432cf3b7224ec032fc5aef46b5161dd80e72f2e10dd4567fa69b11758edccebf47ec2479a62b24b1469e2e726aecc05111a1565222445921b4d4a0410b7ec26dc27f76558a7a299702608f042b888ded26b7a56388092ae0abf2852a2b327314ac11fa6122405bfb6033639a0fcd82d77c787001b562dee3a6688a248b04f1c671e83035ab8962365016ece8faa1a807ba1160d" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 45 - }, - "commitment": "82526d011ae9775cccc64b8d1d6bb30e270703781128977a854cc3747d802d3b", - "proof": "1a9686f480c81a4d343797eae526431a5bf457e9d4f909f9c6f5166b7ec1705120c96bec02a592c5a0ca5b398604b90e94759c1d8d3f08d8f8038a39ca7f16029ea97d85c85c7906488b7ea6915ad494b711f26addfc7f364e6a14931e3f46122c9c9e52a487b6e2bf866c123795e6a95b2a53815e041a96a1f6d984be08ab67948e40f7e6f140ab7e1b3e2eda505bb84d45644cd28673f32f56830e4598080b09873a3064f58c06c9cef3fca7c82b56e7cdfcf83a155c90e003fecc14f50e05e34c66c066e2543eb5a380848f6e4ee3e0a39bdc3de50db6022f694f7bbaef099203fd5dc503dfbf1d555663d3f9d3c105be8c8f4cbd65265f172640ed5fce705622439dacd6504c93747e4dff187ac32122bbe5731df4c13f666f9fb9a14f77a6897cdebbd6d1d1f20b3b3edea9dc32e47826e34e8d7c543e756d58a8343845b60f69cde08ce797f38361af08dc6d44ff4db70946f1d00e9dfa45e54ffd9a720a2f34121aa854a91974ca104c3d224821e8582d924f3e0cf2796b35dcdf0313b820c373d18c3e6eb5ea6f6ec690a8e6e89f038b25dd2db34cf31932b9250c447e09e16fb2d6fb0dd61be278c30ed19a32f38f6260d3cdcaa77afc6d1ecfcf28308fbc11156fd033ed2798607891c15b5930868f780e716160a651035338627caa613ee1bede05ff4fca98f197e5ad1353280df41e6675556376af9f0084524bee0acc2c596fd4004f7c0d1a10afaf51fc494a498e3a567b0e47d736f410692b44db20638e686f286c202b1ffe686b523cc0ebfd6b544a3faf8b8d4158bf61686a836d0be439a6708b2c1e356ff7490fb0e1c3e7cd7dc46468404400a6d17e4725276c749e6e503c4633205e2a1d4c99b2c91a44a6ce634652c15731a71d710493ce2cd2743ba314a2edb1e927d018c597f6a589e146d3e9d7b4d0ff61ace30f" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "48072c5548a1868cdad3cdc067599e8fc9cde4d21b89321904ef7c63ea41a517", - "excess_sig": { - "public_nonce": "e8bdb97853104d8be71793d37709040f0be3d0a0ea85723b5da6b91dd9143945", - "signature": "4b4cf7777c6bdad18643861435228af6da35df565c8dd7f0d09cbd375d337c09" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "86a4faf99ead62f7b7286b02f0bd572da1841ee4d83f2dc276d1e19a99ea4e75", - "excess_sig": { - "public_nonce": "b8dcf2a6732ce7b7ce07bcaf419c832c03ed2a568fd1aaaddb42bb7786140934", - "signature": "d367307f424f997071dc1868e3a3ea0196bbafb36385fe2210192fa610b0960f" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "908ec93b9c412f14889a96c7640eb764b730192443daa2ea4beed127dabe1167", - "excess_sig": { - "public_nonce": "80b360fd5e61c993da30080a5c7971bb63a1c68f4150159b3629a570cf3edc63", - "signature": "e1d70c1ce05aa82446476156eafb8642f922ee5918a3d10321a62c74f0f0f50d" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "da250ed0145627dc396b39a2b36593a41969f0ca970323b06fc175c0634bfc31", - "excess_sig": { - "public_nonce": "ba799d35eb1ea4cf48d7c8d066567c0291f92bb1e18eebceb9b8b81a7dd1ad25", - "signature": "551df9dc08951b2498bd7e6fa2b81bf8108b8d5e42d55cdb454c61f86e79cc05" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "fc903eb24ab7b25224fb351bda3db9bb8faff4686081a80aeb8d05b33240452e", - "excess_sig": { - "public_nonce": "f80b7e758f6414de9df5d03f7e4bf8f075fc9a9e1ce9177909edfb8c3bb1ad2b", - "signature": "46550007a5aff40032b46c8797dcc038502e3c3a50869c34d15b393aa31c4004" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "62288db6095a977cb3c5881150b28f2efc1824728ede92f64f953f7a1949e533", - "excess_sig": { - "public_nonce": "a2dd71ad8c3972a9aa1d03f0fd2ff08177daf2e91f6f30a35c885b34fcc20a05", - "signature": "d05fb9ff8a135a6e1c5c7f298dd0c0d65489d32cf851d21fc7e10fe53332ac03" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 45, - "prev_hash": "b64cbbdf0278aea5d33be1ed264f464d2798b0f31a3575e2b747aff976f63457", - "timestamp": "2000-01-01T01:46:01Z", - "output_mr": "6fc48d093ef1cb335561ccf7faa56c04d14734e933577432c89423c41de41d78", - "range_proof_mr": "a5fe34d11ba2a7f346dc37d14869b079d99812b72c2f6e02bb67c85da9ecd35c", - "kernel_mr": "2b59267c111447ec16aa8ff8364a7dff5016fa045bfd4bb8eb898f4b28b0f7ad", - "total_kernel_offset": "2e99cad1317de7ab9894600b2c00e55ed3c899c3c536560512d5fb766b7b170c", - "pow": { - "work": 45 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "20ceb9591d812cc0deb1ddadafa1f75708be6d6774c36a63605369020d3b1664" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "68ab970ccc08e32ed03302829631a63dbaab273b39bbd7d8b1a1c6c692a0f139" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "6a8fbada5db3771dccddbd8df2550fa9a12bf64c45485937203941a5deac721f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "b060989a4c557cb44ba18c2901314ebe1a5b4c21bb168121af9a86302e7c9432" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 23 - }, - "commitment": "5081312e47ef838bd52100e61a9307ba1c7272e3ab4c4905b9d5bb9b4d8e5674" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "04d648db58377ed65972d1e4fae3483993b5aa92823becb3140601871ed3927c", - "proof": "f2ac778bce248f9460d66978f5ed0fadabc83cd856a6609ea44785e3fe530237dcfdeaac427a13da830fe46131207231cfbf85d3af8b96f64e40fa4230acb03922f1447e89cb4c6bccfc5d021df65e8a8bf80a4e3bed46a316d4c3e701abfb7b561f96d910bf157da0045d003a6319947300962a939ce7846ece9ad329f7bb5f308c7aa2af6f469f905f7a74df762782aee695c31c433771b3507041254b10011478883140358fab80508f437fe700d30ef789583f623f9b5b3500dcdca3c20044a3e417d290be6c6b672a6a6b6b2030c674c33c2d261155e74d4fccda810202e8c8cf4b5b7a3dfda71a69d3bf1bcac214ee96f4e5a4b85a928b2a596d5a626c76f5a9f91ea54d4d76fc51f446e361cb0f5242bbfc30d434d6e82ec3d2db0c20046579c8fe63554ecc83fc9cad265cfe6a49c6f35ef25e1216e9c1a111d13134802db99399025aa04e77f39097a19cc2e884f74624b4d8cf09206fc6fd34af1cf66f797842a0ffe9b653d7bc6dbbd7b1848379e21791cfed9cee284381f92a4a96967f628560387dc3d4bbe30498ef4d5ad14ddad34b639eeeef66f68f5a932960c5fdbc9f401d94460e92bda206512512a5345a735e894dd511f607ba6e702b1a5001fb7e1000104c906b72698fc785a8394da05a27ef950b35ade0e4302523568174c06ac2ae3d770797730a412fcfb375fa28301013b6751193389ad4c84354c2fc7853eb53d17a95d605bbbcb7293261531194792c1e823b58258b14fc142a40b34bb1f1b7b25c42a127a173a5513239403831f258e9c9226e4b1450bb724820ebd85979effc4eb2bcf2cc1ce243870adf763f4cb4da0d92e7d1d669c014f3ce3f74045b8f87500e62054cebc749ac90b8bd51e6d73363238b326ab05c0288c80b0e0e60badb9cd14ce273953db5f220dd33b551fe8d5d9a14ea55c0aa0c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "20bfcf313274b946b5f93d0e5be8d3e1e2e0624d15ceb1bc47b9db1018bc3a63", - "proof": "ac212cf4c9882616b7025f52242b84c067765177dc45003ae8b9436c02f6d23056403f1a85fd38253286d63764b1862a5739832ed247ee144029edac6cf3874912b590bab55bb154162688d87b63962927f0ae92da7f8ccdd67004bbf6ade27968d7a5197a699b6e357344f640119725b932d0800fa374fe83fcd66ef31dfd27c9405261634a92da2c65a7019da2055da46b140d9734daebdb253522c65b3d020e736c45ef35760a95d58246e1844cf4b2762f456aa1dd3dcb35e3fddf0c3d0da5b3051bb2bd288988c15fc169304c7aee3b43fb2258b97f1fdfc4fe917df30dbc59f197097dd4ccb88d5678815b34e5de2cd0205a6c52317855f36e2e635d6e7a68a976e4150d8fb4fe587bb9996b1ce7bbff58a4bc987fb8bbb9ae8529b877609f483c71a943a6a002939bb261cad1d529f996934b7467ed17113f5bb8de26de189f97f4a4d7fe6706ce836d3720dd4aaf2f7992239366e975dfef5c7bec517400f0cce33358d1f356385c3662fa210034f69cd35b1bc18c617ccb97d67112147b56e697e8659d6d22c7cbf93ad257b67007933d4f4282dda41564a5bebb20c4d9746e5e78d4191d1cdea266282fdd962ade40557f7cc64eec855ae9404f660a0e90989e4adc48e93e49579eeb1f5b04038aaf6586835df3ede3c91b3c10759a5da79997c552b253e4408639173e2ea06f34f844900a6a4b992a2ad003e3241447dae79d3e9ad3b3f95ba78a22a040cd63dd1f3013b1435e44ed9253470a2c5c581ce0fd0952e13407653dd2e7cfb4ee09ecae6f74bc0b91c2a7b3d7a079469050b7fad691d764e4e0cbf2f3d4e888435d37ccbb40953dbd9e5e9ff4d1431dbca934353469ceeb1c4d254fcdcfa718645c2b3716e475c949dfd0fa5a420e09c8d2b3fef48234d804dd8a02a4215f78596da39c4401d483679dde488b39940a" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "348f0bfecbe761c93898ba87ae5a9c74a53c07184df638849dae7c9b7291e570", - "proof": "f05f5c5715efd4bc79b8a4b3436af83d028071282ea04c5413c5de6a84ca385118bf02bd32ecd9a124628108dc6429e9b92749185694b7696cfdfe85369fbe354648066003bc8fd65c1fca6492cb57fa07007a2b04b6e44dcba38c7827726c08fe10e97cf41c365789dda2e17404d78737ca8436f14bb353e532f156f31bf358d202ada67a9ee2296f3d8b736df319fc6833a85a39330f18cbcbdd440223060560cf0ed034c840a32ae0dc89b389fd009a4895919f07a7cb3f51709bf4b62d05c78b830fe55af858d19f41b3089ee018a986e8292e691952fff9285bc55af30b403c2eb19447d88fab11d74f433872af2ffe52dea7b0671794ae7112cf032e09d0c4a9cac565a5234eff75f1475fd579f617063db505826295d156ba1cffdf7c9e8eae84071ebbbfa7f6210a15e591281e49c2bda4e100769df75ac5226169171ebf4681b9672cb4a4e8370d1f35a30dcfde1d3fff3e7ceec2835e96fa749b6c424cfbdf8180934be38ae3e98b3bd0abdb75f101691704d6c7eb7e7b2f43557b7cbbd3d05fc4ee5c74a4e0c4d752f9932bea455d1d85a3e470259fed4c316d636e6ba6450667a19243f6962c4aeaf7e294853fe281e92db8c92e00868413447134981c4d0f3fdc320ac98db47ebf7bb60c318782f3cffbe946e9d11cf1d1620ba4c7f435cb25752d310984ae45f233af7172e9569b7a23c04f810c7bd33cd51454bf43d831bf0c1160ec95700064e1952ee65dea169065553063d94a30eb9a31c285e24fd1c1254f3da4670cfb04a895e27ba4e421c93fccd935699c074b6346caae0092938f66a234d02da9dc05a1fb5ec12f4ef344fb282c57d9fa3d99343fd35527b4b28f066002d04066034099da0928728b544369ada325a3184095870b6caff746522b282a35f828465aca521221a08fac6a9d83b456ddc2b1e7d7ec00" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "7cc4f8337e4abf0661f28016426192d7d5dbb00d79f67a6da7f5711f9838f60c", - "proof": "44f9ee7ac6ceb1a7cb45208f979242d288f18e235aa31c1c1c99d5cabc976004f8266a67e09778f069699223fc65c6f644826f56a93460e3cd10c7dafbb87e14d2b523ceaaa4e127b79033ae08c6ea4a1bc4d4b2634cdb8021ea96185b1afa15de8098cfe8897c6e31d9a5a69b996deafc902377c9889b347e15fb2a163f02424b51779b6a8ab670cc29a750c131d8136c207d96a70c297867c2346a7ffac90500d021c38ff8f3493ec0ee59064807674f879e2a26ac2cd453ccaf8efd692801696d5043b4f320a7186b0061f92fc11a830bc1547b98e45e2533cb0101f1fc0c5a076c9b3ebae8954ca71c695bda505656b843873a7d4bc60764eab6f405d942505c3255784f3b7f7dae715ab81369b44f12fb0ab61f08547a9646e7882ce62b6c5f794818db075bf106acff9a8113ebe56a92f5b75359592aa382244ac31e4d0a00845ea09d4df700d314998014bd327da90d29da544fa9042922163563cd24029fdafde5c6e2e08d3809a3ab02f9f377a5f9c2291445c3dafafba6ec81d3728421fde5475b2ba143c9a6dbb23a6039e9671a9c049f86f36a96f40f4b7ead52723c2c1bfe3af17c466f61b16487941eeb22e3c6c3198b1a0f8496555a3cea794ee5cfa8b40ac348698e8f996d6b2735d526b463068e3602c5bc5f83dc07766fc2a76e02f5616b3271207da78cdf0c325c3e6de5d29fd75e6da4b6294ee51c6462b4a6caf8d8140cd0666f7d901884d59ed8a3f42b251d32e5f9604d4f92e646c0938640a29f8c7e87903dabc4f140ee8b290367daad6ebffb46473484810c15dcbe81adc1fe1aa2d648ad1152c85205570da056c62471a5ae33fb6155ad7a4a9b34102d61809f808ece4e9334da3d0d4c98e1fa8b12dbefd4f5102b8fed3906dd49172020108e211b9fd5e3487b954d32f3119729a21a87f06923b280cfbf09" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "9e6c74413b7594e8a61d915ae6739727c4a7ef733c5c6f93f4fbbba3b6b65849", - "proof": "c2cff296c0b4bd91c3422781d7ae7ba39e9f5d39f854904a023dbff454f26e143efdd96e6aa448917766063c9515fc6ef48b79f83309d61d86f3c5f14ada734f88d72f9343b6d3523242a4755618058473d4f1b917db905ab1fe2d35343c812cd26f3652721a90591a06212b839f9600d68be08b354568d23dd5c499520d1a716ebd0161e147889a86e7da33eae1498906be2d0c8bff8fb47173cc6550380102380c42409c74842f67ea03daea765f6bb248b36f026a88abc3dbe30f9c5e9e0bc54e9d0922efbfcd6d4e887193bf6f772d7fd37e483f2ba238ce8c2755d36b08aa75e1f6cb2a0df260b1336e2f803a069a95ba8951462431a9b5b06342298b2e8e9e6ace01c47ca3aea9f6fb8888d508c5c9ce95b6d4ca07daa5749b1de76c19727aff5503c4615beb25558550abc9d64adaa6424f310f717e79c4fb9a4be62c0631fba727b4c249a487393e93a5d0fa3daa75e894dbb6e59838c240e7fec7326056e46df9d0be9d883c768c79fef76345bab6db2d9b576206671d93cee34725f06e0c4ad8c7a44899c920bb8dad427603e2e588749cdc48b5eb64528ae72b1b3ab3c04f2fa6b5b6428b4bb6d4b9401e53fa06206d304e475777ac8b6f7e8c525a962da261dd4ee0830b12f591b0922253b52800ecee2821ca0113de08c6c30b909bbca4ccf18faa5233609ad3e07cec21b58378a9346904f169eb0af960807a868d1603d0fbc9b2216afca80b5fb61bfe0c2ae815249cfda595a6bf642c640402a0e5095010939d43879b0b55836cd2c9d77893f0f0d6e07865b8fec6d59c7ce8e1938e4a88494c54ff50be882702f43aabaa779f5bbe1bbd45047cce658253ab3ae456a3797237bdc7fafc49591e6dea3bd00daaff3a3d350b9e6e390c790e7d34574d808baff167aca0efd5b718f731677d250955efddcc2e894739540d02" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "a27d9572fe1829d8d56d341fb4bca4fed301d7001491232c2c165fcf14405951", - "proof": "aaa6a59144ed1228a2a40d0f47f7ef935a673b23c56355df2fad2d838c93a670d226ba03d6d86c2fe24ef92fa21765f497bdbd5969df3f2b29a30ef4eb96a75adad314d1320ad7c58adf1131739c18ed1d88d15f1445db10c5d762429e46aa2e986861ddfd4eadc1582dbbf3aad5cbc08362241754739c235192ed2a9b52ab3a4e631ba5e2c83c363e06c8871268cb2e4098b4010c04812cce3f08ed4fbaa50904e72f645d770c0b65915a0d5f20d5ef92b085b43ae3851995c222d70f22580f2d6ae48075e661b77021d6d505e24b30e7f093d5220ddfe810e1eb17f573130a6e7567f0d610781dc23d79d94f79a0b0aef476ccabbc5202f0913b08bedaf44b5ebc414d17fa0bf4b9a57e1ef77f354f1afbe387a25d8b1c73aed4cb6303db04a2c9b18d49c729a12f3503d404a43fde0e1ecf1e30bdc834d6e6f63cc202eb6d88a3ba7d5bf65f82dffc20326ad30414d1f420ae798c8ee3e89f9b7e4fbee6181aba92e921cbdb68627f2d48035867ec6a17fa5136ff9e4f3e00867d01a38b15705ccc4e589a6f013608127fb953de21770ef27c961346a3d2f6c5fafadc4e1e04462b9a28f63ef3a5bbb6da5a88de0126b500b4b7ab0c24b6f1c4e68f587740209b06013a4c5a4dad882d69953d35cd87dafd7775be9b9d140648b5192d7478f43e024fc42ce389b0f1a891844e14dc8cd98afc013e526c850776fee48f5070821312ab0cf422358d6cc0735e4cca3fd363eec8fa9dc18a9f1153e696a8690e26d90a07c94680eb1ced30115e02f45e12d4d926ca9f2768b2be507755721d74ac57347f78b2756f17ecd4cbab05e88586c13e2f41de36c76f5fbaf42c2eb233b8be1368e44bb1f9b615b3c579094f35e2ce070d14f68ace1ace63689cb991076df66b7f6c278124e98e5e25df9ce3c149be5fee49921d975f45d605bba89504" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "a832d10edb4e5243fc7446ce898028664c7b64be12a47fa839e9112ca7a79953", - "proof": "90a6addbed349b3b3950dfaf991f31cf615078c53e7321a5406bd1f0e08bff7e76ead2570ae36b98ba3f8d1b089d7233f62bfe73f62913f7e2d75a605830db0fdc76395584904e95abbef63a047f1d9695a86f89e2ebcdfa6238d19d18b9395c7445622e2e8e49e32232502450d4bb15a58f90194795a796d8273663d14bc978b2243e9604f9fe4cf0a057285efcbdd907b64a85e77ee61fcde105757c255f0b30530529f3d6c195b8b5e8d351d5001577c86e736585c5909cd0dd9929f98f0e5de8735da019dc5952bbfefd8577760bfadbfa7178f3a4f3e3c49f3966597a0d4ccafd215ffba06f6bcb8fa2846f0cca97555ac6c39af270db7a5e391de48b09e00e796a3b04334372e4766fe3da0865d9aeded1d565d2a86f8b63c60abf457d941dbace2602ab37948c37f093654a19a4543cf3b218d5fa71c8af50a01406132a1e81f3057969f732dec87c2d29577584e9f17e9288bfd16c3ef90f95ec7633aa8cae22eda43793fc43812f36792ed80f98f2302c0790cee03aa3641d962d7a8cf24d199feabd7a816e3084737e99fe2b99fcdaf29e8809d776a8f8b9ab8c228286fd3317f1fb825694a855952f7a52dd1d59f56ec696579877e08ac7d41109c28996f80326fcdbd16f753e853b34fb1730448b7b8cfc32a01c7a18a1ce957556ef348ccdaa8b3cb4170fee482d0c71892a830f859aad5749459432cdb1ff1b6aabe970de157b24378a6d94bc604ffd84020e4f293f9e5a975355dfc6dbfa5284a57409b239272f0e83eb3b89a7cceb41254bbd2fa15ba1d2535b6aa8c05b2b0c090050948f0ba1edc11fcf2172cb153c861ad784e3e91e73c74dd4a9a15d2dea8e61d676ce7b9a5b2beb74153869e9c96b4876bb7af2a43bd8bf39e110500716c7b78d07d2ebe5db96c59dd8582261a43e7de3d4bc09be7d1e72abf144ec06" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "b6e2c15e467a8875f5edbf7dfce7b637c4d99d0c021ab897134360c03016cd4f", - "proof": "98e10849644aa8b1f488d584b6b5cd1963ac0c6c41a778ea3eae076d6afd35603657b4f95fbf2eee63c297f0df7f0d004f2e2d49cb79feb9f9d1859b16a0850910ac7395d2c238dd92b96d9060db7dbd6ec2b601b333d596971da1f328b3e279c89ce4cb80298e776446e298cc149798d72e46d99c47a695b841445cf08f8d4dd66770c3068a0f4e1a97d8b8bbc4f441c061135cbd684beae6f367f828bbaa09c22acddcc671121f771b777f98e5bf40dbfcd6eef647f9d66474a3597dd6db0e953df0b38d9b7b5fe003e003170e94a9c58ac04ac3e3e9c0c620127a0596100d0caf609e73b763cd2a408d199462384f8a526cbb6cb826885a992e34df8d966f120f353b017608ac6cf5b365e31a24cb3774d92155edfa172c81819a106e2a1328ba9a33a31e34b672d903034aaae743e803ff18a72deed7c8f9065f7d44db3baa91fb068135b8b5b2e190a365179a361fced29ac29e29d459549768eaffd73c1638f53e2ad9c9120b053309024e2d35be92dc33ff517234c30eab6e32546c4c505479389ee4ef8a0f38fc9ba3b2ca96009bd7457aa9e7d43ea45ac002fabb49de223eedc354e88efd7830ec4415b812f93068adadef031a1283b7c2017ee75d722ea043053539018d5fa2b8711b36fbe24d5fe866e263d69ccdcf0356930f7e7cd2dc2325ad01c2451502a1a8260eef1f6353ee73cfaaf4dcc04270adc726537887f65b3b2601165fa8639eda4dbbc597bd8460257fd21e041dbb4849b91a770c6beb723475f563fa2407c3dbf71ec23f3ad91baf678c586f471f54e8320e798400e6ff7daf8e9655cf88e2dde677e0a8c158c2ef94878f5ebbbcafc16864560163d13c830a919c0827e2766da28db8111d90c49ddfc578b94c79018af71302aed96b610726085d337c3125b3a37502bbd0f57ee92e5b048caba26ffa183e09" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "bc9807c078adaa274bbc24a5acde6f09793d78b5f7c3ebbecb064430aa691058", - "proof": "0a3449c2f6423392bdc884e869f3d9dd8b5e13925738cb93a2beb110ecf4375e6aeb15431d6717ddc865a539f7573a3f1ff53975db3f377a501528bdbfb5627666df682ab7f7c89528e6bad08e255b4adccd83ab9f7f8e48cd683f91c490f01d20231afb6b75f4a01ea7aef8f50b571aab6b97f51c4776d8c92c945bd6229b7d5eb896910350cb9cbc906b69278ef9a800b88f2062fce9e974aafb6e2bbdaa0cad57e6786e662a30b20de363343a3bfd4fc9a53f437662ac7ed58c5bb9f74706f87c4e3d656f39037fc8e8341b3f16097a15796235e991c6303f02906f9e5304ba0a8c5bb523f788b6e442e905b6e99d641e31d831ba29b247fa5f9cd29bed095aa07f5ee8598ab8c08d17fefebbabd52e06cc79a140494e3d891b05a8387401820ef2d269ac94eb048b2bee92016d1a1a9535dd90a3f92a6042acb1e795c545767c3eb76ef86fe15a7e6e7543861aaafbf87b3fcba3029b495cd5ee3a5168643a93cc7786ae685a2f347bb47a04a93c14343e2e1d7bbcf7e7de83e3fd51ed56e8648fadc14aa4518d7064c2a4384f6bb45d9a343af22f306957bd9ea14653098afa6ece977d5f4a0884b4ec5d1d7edae5ee0460bf861c7a63609205b9fa6a21889de13db02f55a280ab59dacc7e5d412bafb0b0f2b5e96f9d9118e7358f293f7cc82953b873c5fbe9e4a524137012126367de7f28d5934fc79cb6f21ba015448ee1e3ffff527ba7ef8699a6e6f1d4293cc07b345804c4e594713da05979dc260c7f8cee060d9e8c7533332b14d4cbefee821dffb2eda633339bc14cad95eb4ec2b2598717b0d2818815e1a3b95e71ecf8cd340629ecf3702f7d8075f20f677df611119dd2e808351cdf72adb615eafa31e613ac876c4bb89baa03f8798bcd0093991c20f76fa7b50e910191cde2c7ce53d7c26eb4478420ad96a931f4187a00" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "cef1924c7b7c7e771837bf44c846f210a0098bdb99c30fc6076d994f88bca067", - "proof": "101cd0960d4843bc55068b3957b25564a474e33d6729dd830f5683e7a7be7240c6c228c1b3262a469fa124fa3f01acee84f2d90ada3f7c15755d8189f39e066ef865bfc5671a168db0d96c70064763ed0e6c5b42e3983b0aaddd4c1da9dda322daefced631a0907493861add08ce710058f3d4b77fd553fbb6ef7e3cf4009274185b6be2a245a31d30da7a3acd305e3f1d26926b01e0f747dc59981b7ee54a015fab212d25c6b820c176d2d93fbb36aea8615e58ae06178c74b486c21ae603087efefd7f72d672d443ed77e89dd52b46d5e815debe78026d596f0f0df25841030e64cc9dec94bee5ffb03aaea9c0872a07418fca6d2abb315b8c1a8bc92ed20efecdc3ba945f748e333b1a39a278fccf66c794d2fc76d6d1c25c4de7cdde9e063249cfbd5a9c74ef613c025faf90856647a56eea02f654b86de9fc44f0f8f859f053517f0f18fef7732aac53bf38fc9b81ff342704e0fc21c09d7ed7e108fb3cec8b861902b4793eb0992543e41df8e1e71cf015026d4c0f76d6302db19db43ba8647a1a2396f3d2323d67dcc56b708fb2eb82fff23af46dfd75b55f379d9e2482e25fb9afa5eec9a97853554729068cbb49c4aa00fd94da6d0d711ece466906c0cf5decee85e0d96794cee1caafa86274d78380ab0ba3c7f0e86cc05eb73d49e6e6360dd9e0bd4cb8233ba5d00708034c9aeec3898e3eb81e19b64fa870fe611c791e75fb6866f54eeecbb1da685aa80fa984ee6143bae95a4d4e522f4cf5313c82fcfc618d61a3c6f0bf1334e99cd391216e019e8acab748d5d4e700d01314a8d672929d474a5d36b1465561fc1accc1624370c6572e222cf0206722b69c631ed2896794fb379d82b5044d0d1f3ab7b01574edeb2bb343c263ab3c0824200a76caba13b70bfc916bdd9f099094edc39f3f663ef90fc4f5a42716c0530c3907" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 46 - }, - "commitment": "9a0770bcd3d744790180c871c53ddf02d195920fb147f43ea304f8879a3eb965", - "proof": "c48fe15c7a907b1c180505d60399e4750222725a61fb8566ef2415c2c8900d73469fde2520ecb16cda2874ee347c0f67f1c9068e2678c72548cf4257c088d27bc6947d52a01950442c839781ee941f70fa066fdb85fa3dbc45eecff6c8b3a259605c68e1206ca223e7b313485435a3a398d170f7d7ccef0e346dca6935f47b5a5b2d7f35538c796d175a3077155188b19c2963b358b91707664dbceb8932e009c919052a13ee617560e9053c4581d20e1738c90b3d9c1cba5fcad7ed4fac790fd55b5a6db3dab4f76a080f9c800deea054f7095c8afd0753ad26459842ea950dbc806b1273960a95cdf816ca368da576324db19985ad236df8ec6f8c8d73082ec6cdbe0400889e63e1245e6080c38d7f016ce51ae10e59813e627353387d6a7048b1b363094161a9000dff609ea828bf89d6f2b2902ede366b4f59836e375950e41e13de6069abb27b18ffc6ff3cd3fb73a36a64b1cda052a2bf81f39837b350aa29eecaf2430cd51208111b7fbd536405c00d68ffa02f62fa7b5d80d84af05a64f1dd53fceeba8c7f3d32663861e7bcab52b155b777b37583c7d7724cfdb54c700ee6dfa80393ed21d49aca368e2fab224118cee152b5b45088fe3fe83b975a826ad9a87ef959193f367e20c2172b4fa0d5068d257d393ee36af9d85f2c2613ae55fe81fe3fcfaedd3631f81e3d911878f19eeb271f1b1c9dc273575e59f14e4ef757177fc4e7a52341adf1397ff06169a5065bca056182bd8de335d3cbfc36761f6d4b400b62aaa8981c86a1b6ac526c9e627ba6e5b4c6dd1ab6b0e4b09a67fe660abb11ec2383c4986d0159c55aa6f1681277edb265b0426d5960bc8b2a43d54a981fdf79662729e7c86fe683ce5eda3aecd7397f99e5352a01f86f4e3503671d4b4a7effd9344727ae46fd8a33610339f0c975b20fed6913178db564610b" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "1c0c849501b20e6334bf0570d02dd0f0ee34ba7d521c7c622b44c6972f28c82f", - "excess_sig": { - "public_nonce": "5a4c49bed6c12da0a56b4a67e44493ff9e0b2c067a6946ed505cb4069dd2e421", - "signature": "11a2ca4c78261b0bee62b6023c2cd9d9abe1b44680e23e6b6384e72967044d05" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "201e8c7323bb192eedad2fd4a1e7b4991033b426640a10dee815b90f91890631", - "excess_sig": { - "public_nonce": "22f9f16189ce185f8556564d5301a3510b874fcbaeb90336e7601100cfa5487e", - "signature": "06090b8be302043897a975a42c0c5e203793df31e2a56cbba8ffd5ed0da5e201" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "6a5e23c574502949bdd614ceb0b3ca5aca35f19145517bbaf387827e7979124c", - "excess_sig": { - "public_nonce": "022e49541cca20c3aa7f4e4d352c99122d7ec01ae3fc437eb3c6692ee041d222", - "signature": "8161df57e9bb5ff27b4c36c1a9330ad156aad7f120b6c05f41e7b35daf95da04" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "7ea3ca73bf0d5e03c170022992f8320c0b152ab117dc3b17e88f3c724d661e79", - "excess_sig": { - "public_nonce": "5ea2b672eadbb40c8cf9e17a426b2f0e9571070995cedc82b5ad3d166aa75879", - "signature": "c75a08e02297debfc665659964426d018a7e32156dcbf23f66a05f9fa592e00b" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "9c4f95c017c2d678690f444c34d60b5c7dacb6d7a38b32f6bde12398ae55f37d", - "excess_sig": { - "public_nonce": "cadd033be879ce181f461067b8e71050cdad3852752fd25cf3a5e3c4ec28a636", - "signature": "41dd9369f0d05ca41c80bfe2cfcc5aadba9e01dd5e1831d219cb631c79fdde03" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "0c16ab76466ba8c404bd154978b5b045f8382a72c65a605e29514e068493bd2c", - "excess_sig": { - "public_nonce": "067ee1b50ac4b81695d96ed1f7964c858bd0932a76f970e93145715538a24275", - "signature": "96bd4a6110c2c5e744b7777bdcb5c0a37ae1eff55798369cce956a544f36c602" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 46, - "prev_hash": "00cd1f42f1db8c8b46417876b7152b2b9cdf77f564f07dadab2c7f86ffd418c2", - "timestamp": "2000-01-01T01:47:01Z", - "output_mr": "ca6e7b1ed3cbf26c7bb1bcc98e0e56793c15a20674ea2831486fc2cefd0e2532", - "range_proof_mr": "a0905ed2e6684c976459d5276caff02ac4c68f3d4f24ed699f7ac8406ab6a72f", - "kernel_mr": "15b2da6ccf921c7dda00b8c2fd57b922da9fc6c625416ede8fcda21e57155ba6", - "total_kernel_offset": "abd86af4b3469845c40757e2ed8fbefd8f9dcf6ae50e313fcc9041421d139408", - "pow": { - "work": 46 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "7cc4f8337e4abf0661f28016426192d7d5dbb00d79f67a6da7f5711f9838f60c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "9e6c74413b7594e8a61d915ae6739727c4a7ef733c5c6f93f4fbbba3b6b65849" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "a27d9572fe1829d8d56d341fb4bca4fed301d7001491232c2c165fcf14405951" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "bc9807c078adaa274bbc24a5acde6f09793d78b5f7c3ebbecb064430aa691058" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "cef1924c7b7c7e771837bf44c846f210a0098bdb99c30fc6076d994f88bca067" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "26c34007da48149a9108affe534a0468b128bb97ee60e66be22ba01a8d079052", - "proof": "6e64500a154da8e05a9777a4b14667d5fe857247953ee7fbd299735f0ca7356006409b58fb724de1df1b1e4f990357da533195c990065d456e53380bdb1a104eb484b49779061ac5c900bf3bf00e012527eee1ee22e8dd3c90f78cb990bec10910bb62c84496a8b9363771f385a89aeaac8edc12fa34d56fce4c724f42934e2f9cbaa2bd51b5ee7c1a6755c1e496632ac7a026998d17176d5324c9160174e1027d8f2daa5ea0001be1b38ad2bfe7944459ca8598ff6083c5fc8fd30534ce5f094aa067c6af2406247371dab08b5bd2fe8fa5eac9141f2bd6e79d4cab9140400d325f662039eca424adc91155758ad662beaca7658570e730494098a715ad406288198dae6360f0044235752c895c48711d601f0414384ad47cb95ba57975325392939b2a05f4a6bcba8149422583c0747bd42af2483338a4e4ce86343b11ed7cae8db55e0fb1813a3f0c8c54c88ca22ca1a54bb6fb14d1f1ab783ef0e8587f4dc48bb06e4bc386ae1eefb2e3d9af560881d8b7bfb9a17e56b0400bedaab12d168461324a726fa5776d089737614308836fb2d86238aafe2a48b8d1b8f5aede7ed69e2f46406813944e52f2f0790c4ba3d56d9c3f240c74bbdff40f173680ce387cf9d744a7892f6bf5155a96b53f8f85670ab146ea2edbcfc5084eb0effb4e0b6ae565380b8a9e623c29c5ac5dc951a93bf3db1ee40e73c40a69f5b5e6f9335ee21fd70b6373d4c988d1b66bd34a0ab47b7da533ac3410542cf64613a8db7d19180a94b297c9ff40e5ad86d0ee6bc4ed4f836f4a4fde8fdf23d719eb40fde403687e6bc156081ac2311de1fed3959d70960b5f2a0a2e731d0885a6b9331c1e170a318b4970e7480f4a922110bcd651f1f1e55c22b29dc9380a98ec31e0996008db5eb473c836969004c8f46ddaefe377506f7fa21e427f13e3a04d8c6c7ca601" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "6e5a92e29025984f284f627d064a8c7c815471ea399c870a8f69b59cd4f98265", - "proof": "e218e25adeb3e5c11f68eff9502a84d2e7808d0442ad334b0eadbfa757807664a21f637111ca5c0cc36efddf6d0a6b012aebd5c8f6c6e69a4fc8cab6250f3339868634b6982c9acddcb09a2bf9c2cac0b445612a5fd449e01f7a2dd2a9b1776eee976e28014d1fa77794e340ebb5e31bc5c902443b1213505df12785a0ce56499971c09aed0174f2a7bdad982fcdc86af59704133d327fc24254263fc4d27308115888a8a88c161f93c2522c89524ebc75e374867226a609293f0fd753a9ec07ce52019b9c2b9dd13b3907f6f39a14f875178cc5c3e6e84005db23727ca75a0ef67cc37b6ca1b4243395d2df144ab3c040a94b2bcb3884ea2f8a9761de5c2618606c6ade62520df8f09be5a479739ec4a0adb1f6d61244ae59128214b8ec5a58eec70e6c41b0de4270cb2c0dd5946f7b5ce0b317c690b71a26329d20c476f51ad4bd21c135e47a3e7f7e045c73e2fa9ec103e9ad3768627b7ea63f31a5d6e024a872f7f86724cb27f1c76c7fa363056d62f96ed795a6a43f04da8dbdda8b3362ec4eff2ced47ce82cb0b0c6f3b39e0f607f4aa6397a511705a8ca1977f825a1a7c8dba60a32a21ac4117753d90579434e2616aa793c88dc277092fd00a17ca0f108dfddfc9764d49ab0fff0eded84024fd3ea32d6c3d299fd96f4a993d0f706030333cca9eb581afe722e1a335b78227f30398400fb7998e8bbd59ef88936b2db4c0aa25baee77b3856883763a3f2d56d7d70bf3a99b1e63110ae38bcf1453006e9223f8f41d01c98766d6fa58ab7165e308843d878d12988625c70e1dadbf2cea7c15a4a9f799fc6bedf20e74adfaed9521588b68c42d9becf7ccc290eeac246972fe8294d53c9b8d4fec35c574f24a736faec582d8e8d7851e28cc7934670f52ad28ac486a658532288542dac887160796c52f88a5bf4e2f1a61d8d835520b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "82d13c5c8dd7faf0ec1f7e8b78a50384ef0424f5d37bbd5ad8461401ca3e566c", - "proof": "20d5de9363daa53a7589304e453aeb3ba88a86b17905aa6c41933044b0211849fa11deadc0ccd3baa358343e1db380229a86fc3ebdeee9f3dbf22c4cc1eaa94328a60ebbda9463d608cbd3875bedb747b52010d48e9e0172116ed178d601795cd03b65c6b2f7abaf1822cab50a60180f8b44c21fee5aa6d4dd8699897c710977b0e965a0b1e9a70be09586ff3ffb0c9159d27751b8c3eab6b2a91a948cb1f3053d910d2c11011f3d4ff9396672220c5c899c39f516b6e09cb8a713d66280af0b28dd1c339a933f9e51bcb68abe15ef151be045fa66749923a059d1e1a074ef03762fc23720ada30d3cec057b5a33c644430274df37c0de3a0d54d876cd4062052c1954418a9f7ee702a9f824e8909e8569c8aebe1edef397866111a137a5f312be33378f8317fbc83884e82a6c0c2fc6e8a2df8e0529247cde47f427c0e4023e8825ab4c111434ea7b9d14eeea614b1f6c7097a28919a997a850c649b911162528397fb68cd850594bcac30186c961da9c612fe5f08161ea7eaeb52a312f5e51023e059def3089f3cc3b92e6fcc09aa6d8ade795146b49c9c70401048c0b6d7fb63158c8a508af7a4e582ffc82af29cc0bb2a81373aa6b762856339185c4496dce972383cb7a781cc937bebf16fc55340861cd188c7ccc4c97b703ee0eb9f1512a2a0d46212845253325069ccb0e6cdd3662c526494150910b32ff9057e4e25ae0a1bc50264b79edfb4962de3f1a8bf63fc345984a97b545b951e7bcb56f8905321d06cdfc52f1660b2e87a1564c72f7514f127f0fa84d6ce0e280eb41c52737bc620a2bfdbbdb0784d1985e78de3c9fc721b5a09c72de25f771363e21707d1441a63f4e0bc58e871c3916512a5988bbc3e6a6df01d3be3496e9a460fda56702e0b068c90615482a88ef6b6fa271271c51e465946012802d7d127dc3fd26210d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "96ab2db982a30a5cd42e4078c84ecc4202e013b9a02270683dff84db0ff1e84e", - "proof": "2e0f6590c96ea30112c8ab31e3e9486618ba698e30bc957820e4ed03ed63b950621f6aebe8f76de191e4af42e6db8502916aa92646885111f1f260fc210a226614a4d8a02ea6a229f6059fd4bc52cbf7ebbc7a4ed06a6c92efd618afbe7fb33de438c7e6a65e4a2000a407f50ab33c446b06a79196a6fbb6a5a05316fe0db76fe24b4e6853fb3148b46f2f40ef78b30e6b61c6fe1dbeac3b7b2d31e2acc50202020af7d179dc03d6247ed6c672dfa36858d68bf9e2c3ae95280c20c3d9f6b702f64499340c4b3371be63faffeed4646c02b057e43c614d744db467f916ff890e880c33a0b487194314988b6fd72588c92cde774e1e42d9d3949bfc3d19d1372b301c13f96ff268b242eed8c25d71cabdb74a816d34d36c9271d048abd1ecd736287aba1b836ccb68d3f166a125fc14fdc0420b235d9979212a6cc61c4764f718726fe36322f63a1523aaa208caeba6b195f5f6205ec870ff24628e351955ca16a00e08e3b085e3ffe4243b1da4f30bb1d6e3806aa9adac8728652b229501b40106e0909ba7c56e7bde2c204f6add33d321d9ede754c1cf63cf3a52b5a96c54729a19087e063d7bf681b67b689b2e9256cb61dcee69490ed77170a9070035dd77f2ff76625a3ee3b079fffb5e7fde1c6f7d04eccec7afa901fdb7966946fc9e3d4a265a0fd4b043e4c8c17b9a9424b0399f07bd4e46529157b5c60cb972ffdc5e10ec61c10be4522de81a1d178c446771e64a5cf9026ac81cb6720520b087041964ff0482dbf40c98bc6b87e846855ce6f2e6c68d1f1add0f19e2aa5514b598718a919cf40b28c554864d6e9368cbcae6cc3347aff84d51bc7041f3523b5ad03eaf2a7230126f67d1f26d2c9c821f043dc78209b4d80dc4c969831e5c97d01a0f929a0b3b9e2a46cae7c390b5abf2590254c2398561531ce740bf3abb1bf2ba03" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "9cc42b2a0522eab6db49fae1b4b3169f01d5ac69cba479928f58d4afe283bb3d", - "proof": "147079e616e8a9c0ed555a0aedab382164c48b7fc4c4e1c2f618b14a31a8bb3606f199858035403284e64806eb296dbb5e7631e38872e6530f2c7c2af3351875d4fbd66be2cb78a40edf6d7d6b21111360dc18d790bf470564e39e86d9996c773cfe1dce153be9165b83cbc4dc61e66fe068d6d7a16b42682d323e8c62627624038be4d0e261ca1d9052a6d834e2bf9fca84764de6b2130cf246f8ecdeba2e085b73b89ed8deb2cca536ecd826cc71cd30f11f6f2b56645669297e820b74d104dbe7781fb0153714b9cb26c7f82e1b8351d388d8e093b9a4e77e405a9a190404c407833c75321c904d48bb6bd1301df9a3c5b0bc224e072389e3067543e5e4225034c89183b3b30c7b28036b796ccff7d6eb163a6cc01f56742eb7b363bcb361060d5018e141ecabcfecb38b0ec81c4c1c0c34f97692802666048ae8897fc8267eb211c4021c9a2b1eed741a7b4c84102209e81be08d441900eb6b65cf8f3374b26bb881cc6d666a87437baa7452f526f4b722cc51bffdc0ca9bfa95b7e99a65000ea0d2ef68ba3d746f3da2aa1432471c843b53b5aa6945d5b9a00c9a9083403ed515eae96bc0ee37b077c9bc14151c0aaf7a82623b6dabbc5a1f4dd527b87b3638a9f804bdae6fe58c3b07c57aaad916c6f6c04b18691d4455966784d8f53e1022a765e6ed869dd7157c1e6f62e1ca3da0a4948c285de301e180bc17df570e34d11df65baf6fdcdfe6171405b0ea8c450d0e2a233bb92b307e41f1fb4d7e5378b1de3e5c8c3d2dc9e7953c0f202d3b11fc8795d16b668de24156482298b6183cb20046aae1d74baf5025e696e1db7996a24154ebc022773fb59044b6184518100b1a7dc0cb0c684954a989bbcd118d0cf75f59483443555d4aadd7bfc7540559aee74cbd072b2af6fd4d8138ac403b6345b92d10ef46e034c26c03a27e730f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "b2970c3b140728aea806bfee52f644cb812e2f8ba657a656aeca5bc189917601", - "proof": "8017a7cdca1ffd311e8875528741b3ef43200e3c36270f581d18dc842ee07700160f0879529f09103f4b182327798584a81d4290db78768ab7682344ba44967aae9c857efa53a6fa5a7d80ed5b015782c7745292638c835a9805c3a07e2238673864719bbb2ad3895120eb64bc6667e7e93b309780cfb2a9629a2b5d0525f000f673821971985ad1ddca687700c0fab5f1aeb4ace0c28f1b020a6dc6cdb1e50e736533e4865acf361ff8ac527faf6166dc2db1e66ff19f115df68f7413d1580f2fe67cc5cc0b32c4b633553a779e96c18e209233a052d0b80ceb185c750c1004209b65d6de94d0c26965e10b907bb9abec1f4b9e49420f1c0eb6ca6662e99049d0e765ee549debb5addb031ab896a3914284da13b8ac50e252018cc8e6613228e2dae0d83981f7e1ee21c6bd3eb16d6dfdc40a07ae1efe7c943df8a4a300c15e90259310bc62a40857d41fdb40275a64f5b90a946c5bfb232b3c8ad086a4ca75061ff32717ac6e6084c83290da6556b18eb65e9beb013db6147fb318a5e78b453cb84aff8e23b4f1cc7dcd29f440ce7eeac53985c44c34270d98fa354ace575f4099b0444e73f71a3ec135f911ea3d497e6e5d5e27039d644461aaff74da5141e850aeb704cef1289d193b57a6a1bb5db19885c87fca5afca06bdc37b36f691b2edc065d95be1987be429af452de28f67b63c0f9583a121abbd547f94f814c61d23a8e2b09fffa5289057e83c29e104d9267dc4ee2b9f35af223d12207c0bc696853789c6b183e04d77af7cce775c3ec694b634e469ae0ef03f16d451351db775c06157f8e2d2f6ace950a8d41a2741a6c6b253431c62238e15ddd640bd4807532b5b49a37698e2992478f0916850c225ca211373060834de3d198b415ebdd0188b1d3a489a142d3964306c078fd2575105020bbec8c1c7d36e99e02e748ed06" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "dc71ec373ac098d4df9d9c03bc70a2444b150d035674e40a5039a078f6a82356", - "proof": "ea03295aa69f4688a6a29458cbb61725521c9509a222bff46a89678b30d93222283e97bfb398c71c6c28d7a842b2d633b16398b1855807f8e4ae89310fce8d60422f65c8ab2ef8e536c2dd3b937e4f4e4d3cc569807debcfd5be83c78e006276bcb718fa85653b383ff21a3388496247ff8adac413f0871cd7fd075f2a9b435dcce9e64f584b414a7fc381f261e0187666c56665259c36a9063633eed6dde80e5f260dce38a006a36e0c6b3b33eead6f0b36150ab760af15f815a69e4279810502d828bf17bc3b097ba3feafdf9488ef29b313fd94e0e88d71a2efb99526ce08eecca2e5f8ccf68619065d0a384a0af1dc0a4a31ef7980b806a6cbca3682861f1efcbfadd6b143d8d8e15c9e5e7d216b01112617ad2cb623abfafc795fbe916e10b0d2cb8405fba5b2700ad6d68314ec6ad22b18eb49e30ee3e59fa17ff76a439c1dbf3ff6ca1da6ea66065fa768a33de3e99f9d4a450738cef8de53e465665bb27a8feca6f0c9b280d0361a2aee5bbe822efc39a34c99e39edcbf2854b2e5297adf0ce551f780109f29af3a92c870b68d8befd5af576525b72b2112e0f12e6d8e8bcf21fc8b64746f84eec2fcef0eb338293884da48c72f7d95c4da85a3f1219ad78ef8bfd6f07985e296ea55a4f954acb524d83faa9a5f7410677267b5613d8a3054c0c8d633487a654b1051ec22d73c69a318399a4da2702c1d031384716a02774ad13a2db70b3976ee5cb280b96b21f06dd76883e9c8735c13d4d23889010293e25ba48a18e242833ab65934d93ab9c08ee6edb72cb39e82051c91c51839b0e18e0a68e17c5a82dadc664b105098b4910082c15235f15cd85395b95fbd3e847b4186711fe5bfd0c37fa4ffeb2913d2bd2fdb10a5aacbeb667f92a24e65027eff7e119405dc87c90396510e9be3cb25685556905f6f0d3bb7c830e174880d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "f6f0fcea9108a1e395e19552f86445258dcb27db7aa10346d520c378d823cc3f", - "proof": "1072aee2f2c7d7f5b7840a6fad7a56f40b8c570038e1b6c5eff2544f8e97554742901af52e7a6fb35def137a98260271b17ad4c9cde07116ec2ee7eb9d9aeb7aae47517248d581ca695095f224accd09fc5a324730706e303f25bfbfdc8c1d3bd8a4a39557dee5ac2959fe35537fed01d104c2b6d2d7c06ee338d667d5eba316e408115a2225b0d4e40e90563da7bfca9eced9b9be17ff0fa60d7a26cb1b5c02f364074c47e54ca76eca5b2f8b6c46ee9199f04434075451e0b231da54f7040fb18729070c6405f20cc09eef6501296635ae8fa25083bfbcea01e5ec9ff1f60dd62ef572b555675fb80009c7c145a7ac5e3c5d8fad20b1994f191890c485f544ae29709bb8f1c32bff0120b70b1e9260a7b896315b46ed87870a4883ac7ab61e5e11c0849bb38c66f7b0f12060944e0cd28c53be4526c520a8db722801c1790ab44dbccab3e2d91bddad4365bddd27206b57ccf7b4b7c571dac80dab7fcab764427e3107f12765571b5bd149008d4d0ce2977e6117b235a2e3c90251d97991042077d8ade8756d19787d70a4057f5276007b8f510ffde3656287d92cf82d0f7fa0eb157c7c5774de5d6959062a5da5fd52511cc88a8787b5351896bd1672257994edadab1978e5834c10743395141277124b0c464eb20a812aa6f4ed7b6aa3309e0ef37e99e15245d22da56a2e82ab010c4a44864b6ef40f09b955fb09c3156a0497b2962321bc53fa75f8149ac6c2652e649decad98d07eef2b916dd50773279eea473cef2373d9e34cd0f6bc039187275d1807d620b7a722a53df9ba81e545ccf57de8861e0b8b75d4f4ffcea1afc8314c879706444ae100b021616b213d2fdee96b1b1d421f479f7c969e61ddf7fcd0c7526f7be7df113366ccd391e68b02a1c4c26664a42aa7e433030aace3664d7ea6686e19034e5fb58dbc5a56f1f309" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "f8f7bc373250fbe934be56c084e6792e53fb0eb1a6eeed2adc3a8d9957a74545", - "proof": "5e3871ed697bd99fdd793cd548928ccbf4f6f14122a2a0d670a8bd6ca2cb8f42f47e78201f28545970601c842c8c2e992a6f72d9e783819946712713f943732366bdeb38712a846aa1e246c3e52e5a3efab2d5cf9385477eef8df703c1888d69f038a102b764687e8453436a5bcdfa6b826f7cfc02226ea9c70717f0185d5a113e9e121f737a33074631d63b900f334694af85d9a68c6586d771f442aef8390df26bebdeb5bb07f475c46976d893aad48cfaa3fb89f07cdd06ceaa9c7f829a0817fdd89a89576b5435e6ca048a027466248d4b8cf6f23df0c89661da2b5dc90a243a2642eda220195f620c5ce99b98063b11dd0a4f62b0a127c48194ff04864cb886edc6c753caf5befe16204e0ef55c46fbb08c0355622372a6c11b4044067a2cc33cc14b69ce023ef98ada058d95a309f231508c42d286309768840b7eff625271da8efe868816b6d3cfb61fd563a622095c163969b4a8b3f5d805954a9c3f3e78285552b0f23f19b0e2b03652582317065397697c6638f947280afb4f252dec025c56a94554bfd78e5de01148ae1cea3a6aece877802798657b56cc1e6b41cc71031e645b4905cc69186a4968116a3707bbb2991e9f2a2552176786402c7680943c4ea19258829d0c5c1cd51dbe8d3bdc5f239b644d1f97422c487dcd903ab095f83a0d435fb91abbbf42dc67902aca085322556ce4245d38407384cff51690e2682f6a912e3d3bcf6ac898b84ec8ae1525dd65cedbb8e5a255191b46cc50e2cd7b7ee46d3e1a976cda00d4cfab0a112b0a0aebc43230742dba0709764806f4eccaa61b0c48f0b78df47ce39a88c2e3bf10445bd19fc4e0e9e3dad056166c321ff7150c2365250fe9025a85da501723b855be064eb37cbf14668340b2e70a2c2c49aa2c2e3d32f4eccea548c7b088b2f2c305544ad85fb420c146a7119c0e" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "fe8b57cc3080bdc9df4aec7c02e296e6c4734010f2416a5786f797d2e7db852c", - "proof": "40c6e23d1b099ad4126b6332808367601e923ebf8ea00decf38f13073119882f34119a074ba0092b5531ad6bcdb6110fdd85c90dbb40ac7eb0c00d1aae7114357cad93a25b2406cb14b6dcd8ce31245cbeaea2822893a5ad09c950c9c48ef045ce625d3a5cd629444d6a44c0814e889197b43609083965e3338cea18f5df2e75ae44193ce7b01be35d9c20823985b158bac0be3f3f66c95243db7d20921c1700d600b8d6995cb27fcf54dcf961ed25987c87ab691f97f339684f8016244ca20282f60f5cdb3d968e7198616d951f355693949e9fb4d27af6ad1d5d7f1cebd706cac19905d4226b0d0afc898a39ababc958427e7bfe8a44c3f02088ee3b76895ca6269ace3255142337ec7fa86583f8f77f4ffd8fe2551096647cce38dddca41af63fd5fb51f615f788569bc31765e1c44c5527630c380e0a2a1c26d8c788275fbc5e0b52a5887de1d1324f9cb171a0df418e16b3ddc3ec9b19ca9c598133d15ec281dc9e104fc38d3b6a980d2a3d141b09b2db0f3e50de101c739d32a7991348d609857bf5bb34c5958158a13bbf3c28d14306548b854f2f246763af651da04bf2171d9ae98b3464f7484b50ae695a17e67b221f0fc998ce680d8b0051356a3f4854a036351bcf9d8b63878977b0386650492113428039bb9b55dd97e02c8e3bdac274be70b94f86d0939d2244a012191836d683d894c6ef0349f85bea19b005868b0cc32441867c8623b4a394f68e54a8a367d1cfc2b8bd405421aab7050f0bcc59d372f2bba3e1fa019a27b9f824892cd79842f132c5903cd3c64da27d951df481c6817f542d85aaf7a13d4241bb6dfa7e2ad23ace457d3a8f512ca918c653268cafdee6e8f09872479c645a243934dc59aa49e103b561ad8ac4483e0ccb0f0dce6410116389faba0b4536d325c04a0f8cf738c9bbace0a96b0541cc3efb0f" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 47 - }, - "commitment": "1255acba2103ff4e9c31023e32a7a04369b20837678bd1c660de3a79d4c23a32", - "proof": "5a7ac0d90574b748f95e3a71a32f4a16f89b2f238f4f804e2299e5867ffe4e5904958aed42c3f350ad5c6a458987528886a84b77e501b41de7364f6ea96a6506de9a09c15f5ccf01a0f25555705390cca6934f1950e261eca9c4f6282af81063da706b02af01e6b88397d8222525b493f1235f453015d6e8a4905ed808d70869316d0c14678d1d1c3dad0e296f4a8dae6e3c048e34dd5993152b2c39c8e24d02ef2fd72141f31cc105ccfa05630634b111bf134eba9990250d48ced01135a5046bd0419fe16a00e3348973691484e3f23ed1bf8c79a23f697277a9937b034c03622889a15a4d2258837087cbd5124f3d72801a1269fe2bc75538bfd8561009034caef10ff59ece3faed39910cbefc5d29b0629a30580cbfaff07265dd1d17043760826771f8916e18cb18c3fa6aca066540dcc54c18baa55fcd4b97c343a257818f75379c91ebe5e01d6ec0c69f05eed27e47b17e5107ba728607490f1cc480dd6e4fbfcc76484e283c4b8ee0807b3030da87736df10f53e8994aa45a4cfc809c0d826441161aa14b75454d758db75486270edd66cabddfd5dbe469c100d783b16546ffbb944accfdd011141a67cd2a11e9d7d58a2a19c0e8919bbcb31de7441649629d0a4cd24fd0fbbaa649b784fe79e047b06245c60ae8070fce0ca29f8135e771b5c84115104585c5d0d8292fd8bd81f7f37397d6bad5835914afec5ec3254ca9fe9be388a1d8a1e2c5a54dc95e4a6b5c7cb091b88a1f168d0b11816b232322fe6b9045ca78405abcc5d0ac92916bc9a62c924580e12f11d86258bdc575b1857eb724651003d3d13ec4633b14e1ec874c0fb15e51565995ce3928d3103177009aecdb664a2c09b2dba1e6324b950e46e4316f08640eb8243630972948806709e27b203bd4292a67c26aee94e4e3e85fdf664e4233fb089b1d1b048fb6206" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "1429e1a133ef3b7df9f3bad8585c85d6a949a24a9fed1c51fa94c3503edf1836", - "excess_sig": { - "public_nonce": "401703c44199dc5371669aee02ec9638deef404620b2deb5c6b66708d75c337a", - "signature": "b5730bd4ee953c8ecb4f154372e221b35120e9aaa5dee86038619a23fcd67301" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "20c6cf7a0f352539b2c03396761286c4895edfac0127ea80424e44baf19da208", - "excess_sig": { - "public_nonce": "6c8d139645c1971b27241418260e6bc152879f20b1399915bb0800f25273934d", - "signature": "13e3ee46d9b0dab9d5b796e25dfdc13c8b9c3746ac2daeff596635a05f33e108" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "46f459d4749a488e59196f714257b9619cf24ecba85cd82e6ab26cb0012c4262", - "excess_sig": { - "public_nonce": "eea9e21e993747a8c260043c2e0729b25445b543c34bec80cb4a69ba6cceed56", - "signature": "61151930152dd9b8aba043247f665ec7a43a36bea71dd6035754ff2352643a08" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "668347c10302420570d37e406a8afcd3c055fd305d1971770b04f7699a1c6039", - "excess_sig": { - "public_nonce": "de9848370d3efe397627814b021744aa08e3cb46f44a8851cab2264e7c1f5e6a", - "signature": "f07fbd81f7774d9f3e9924e2708aab2a001c937a32ee0b2c16eaf35faf7b3901" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "d2133ed6ab923ce31d78d1dac3d0aafa2272e1b8724dca43aff7287ea90f5e47", - "excess_sig": { - "public_nonce": "f4393107dca2d9d6795268796e627da43925b6ef6643284e3be8a2039f9a7e18", - "signature": "bb5b448abd55399290d568299073f878b83c31c376b4b6e964f92576dfd26b09" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "f49f6a18dece8e058a10614daf93040da9b0220304cda5f2fba347ba4ef11d6c", - "excess_sig": { - "public_nonce": "c83d1a477c40b003b939b6a5e713096fb738167eb7e4116af1b52ba218cb8f31", - "signature": "de3ad74b3362804ef33d52da5a886ab2011d654692f267a75ebfdbde610b960b" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 47, - "prev_hash": "bf9b4f35fac6d78482d4233cf7d7376cb1073aed3f4255e2c49a31fd110b729a", - "timestamp": "2000-01-01T01:48:01Z", - "output_mr": "7961b92954f289159e15f79a62c61fc0c2eb76fdbd0305ad26e11c585ed928cf", - "range_proof_mr": "878f1790feea107bc0049d096f027cac084c5a611cb8923e977980df9ba9d90d", - "kernel_mr": "03229a17a3a318cc9a20a1eafa660161ac45bbcbc2e1963426efff511e107001", - "total_kernel_offset": "7f010565e72c0cfde3f9d61abed0c2c23aaf844bf7af2c7ae86f64a647b1d405", - "pow": { - "work": 47 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "6e5a92e29025984f284f627d064a8c7c815471ea399c870a8f69b59cd4f98265" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "82d13c5c8dd7faf0ec1f7e8b78a50384ef0424f5d37bbd5ad8461401ca3e566c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "f6f0fcea9108a1e395e19552f86445258dcb27db7aa10346d520c378d823cc3f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "fe8b57cc3080bdc9df4aec7c02e296e6c4734010f2416a5786f797d2e7db852c" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 24 - }, - "commitment": "f481bf13c5263ddb2b2fd61fa0f884a3c5d646790791ef886d4aacae3d5e7f42" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "12b3398c7ddd8ba3cfd52d9b5b1f98922c9e063e55f3a992e4b8d3e15b067e02", - "proof": "66170b8d47a6e65f11ca4c93023263e47cbe62c2302ed07526ead8f416cedc1a343ef6aec25e1836a76887d58dab59cd123faeadd8a642d39c50fe73f34866196642ed6a3f05280a7ef86f35325c821e367434ccd3c774acae104698a9a59c51e2b65486041c73229a2f182c1b995480ee0037c7b73dbe24891d98163997e25574201a0f532996573088fa36303f6c565d92ecadeb6fe8d30c3abef125ddc70331ddc09426b0301e2aef84dd440b7434aa2224ac7fd6f1c498b47f7d7f7b8705c4e493ab14839c6588d758b96e9def522915b3257ef8fae9c345dfe276d6d207b8c7554838bfdf5c7c0687a5926d7f382032dbf62129e0fcd3d7ce3c93e56b638293afb01e5e98aa7ccf6b65fef457214e86354b1afabce6d84c252b363a89506044d25dc9fe46eec141cfc4d55299ca5c229978878630fc1a8d35111355a05ebccf783cccf3c15b1e4aea75a3c5ef4bb9a0fc649ecea8ba5737113b3afbf07ee8bdd842b07955720d5678bcb69193fcceabc9d2678c132f06b61a60f951fa186ee46a2ae34eb83a50d5004728350a13f74c73706ee01b12efd6ee1d1d977420fec404eb0ece5b22e704ac17306cc14f737f51a217b27e20c3e1e36fe6601d431eb4f2bf04372aa7cc82b789efb14da8db4bcf1532eb2468fd04323c021db259aa9e1bc4a76c7660e3b36e740f2d179d43f97ff7e3cc0325c9e1cb7360621f215c57af28730a7abd4f7d40d7d129330d5c7bcc98b1691f7f14b9dd8e13861342a03fffe5ce36d160eb5fad7abe2ea1a412bcd0428d502a06049ff1c23bee9945f8583935fac745d587cc1e6b37fc64be7c28d4c28b447f3ada12a644f95a972efbe0456c81427e7e30fc946ceee82c26f1cc22d247fb18cf8cc5bcbce79ef20f4924910a10a70925482d0c23926c20c7d38084c65a19af8791bc70b47c89490d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "1685fd844f5b6629db0ca74d3d36ef80c484bded9babec29f91a85999340a055", - "proof": "3c2e9e8a00679a284625676c0678fcac96c58cca4809a9fc9286d445dc5256331ab78e5f5ca5d8a1ac7801d0b4227397f894281371424bdf6683e9aa2f5abd09a01884578c2cf6658256f8a3068946fa86bf40c506cc4c6b7ba0394f8c4bfc1770445681bfd439f60fb0c27c54036eaae0b2eebc67fd6ea70cbec8aa682ade5b8d0ce71f3c1f302ceeee75e3b715a6cbc863f906374ceeea9a562ecffff8b30c27315b0f3d2b5f992628e53dc6aee76e3b1438de85c98700a3c2f5e97e4d8d037adbdfb0b1660298e82633621eecdae5c2ca8e4de6570f36aec6c79bb328140f2a798b3a15dcc3319722a75dbaa9a9c699e3190897f72b90af31891f34333138ce7f11048e8ccfd62313e8eee7d84e890b82915bb4c81141518ea613b8213155b470f5b2064814c5116b8898fe38eaf89b2f0b39f6d2383bcb638d85064a53383ceb31425630a28dbfe2e06dd4666e5dd5a90944bafb5e848c9c7606d77c196538b6540b6bb64409533dd90f45fc609f032559d92a4f520ea791e4c6cfb432653e00bda832f3029c456949383f80417fd12ea1867a19bc795b3fff1611e11c00fad278b2a031deb6486d90312a1d65832a168679f291b83dbda022e146081959e4a0b44d5e3f14002fa06b2d67a8350cf4ceed5a6505ea15ce90fdbc0760ed6340c0b85baf05a967e335eb83d46fc37eb9f943b5ffb27da5620847523192132746d2859145af73b16c7cc5b7f67ebd811a432d8da865b581e379261fc0ccd97fde9e5c8bfbeea764354f55ac1dee6ac62ee93db142fb9f30e36951b81763157c185ba7799fe01d0d6005071edce616c21b6f9eb49f43d50fc7ec68dccc414470fba8e319caf579943236d259b83f73801cafe1094a83800632e6ca801eaf3e0b9d342aa2964df2c3146cdb04e134aaa2bf738294562b535d410f6dd490cae90f" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "3800b958850c828c556e204605ca05eff4adf778c47ed42fd78918505c05f731", - "proof": "ac6a6987fa53615c23f37054717ec9b8058f5acd583024130061a446edefcf48763e794a283e4d5a73869e41121d7e11e9e63839796319b7bcddf2636b39d16f8a6b09d1946223596e1d524a2873ce08e1052f65b65f7added462cc52a638741240af2199c8aa5c5f3270689e22dd0ee5290d2f5cf94b24afe532f94c54133225787d6a59390db2350175b6758e29b79f177e864d8fe020448321e93f35ce80cc1c9dd594113f393522c09e507835ee847b1315354b10f4aa81df63733b098069d655c2e8e3cf186904f1f1adcb5d3fc7393e086f47c37c99593ba9598d01d0ef8f3985f68a1c036ab328b59ecf572ee48a81aacf55bc486dfb9decf93db4a2c863702b0905d8fd0d58dddd63a3f24fc7c7335e16c32847bacff41606b88cb5c6619a1faddde01bf38e885b965441f43ac71697c7a0e68b3a47883ced10c0677f8f15eb2cb9f5305dcaec82a00f602fd02ee1aa908bd660e0c615daf345a2c49d232ba4489fb01857b739e5d5d644ca3754af9c00c1b9387fc41f30565931971a2f5db753a097efda7e2e8a70d65098b05be3cfb468cc0f1623ea03eb123cc007405edfd6c6fe3ff04e4a20881a332bb60d71ded2991211f5d39f763a2528b411274789e3ddbd32d06ee5dbe42c35a4a3fa1facd1cbe0ee598f7bde63854991634b94c18de4210a0ed03156b1683ed7c58db6a47b8e11780a7a909b7d765a830d82972059e8539ca40aab8194d0c00ad7010c459dbabaac9ac67d5942404f70aa8988386fedc638609f79d862e7b2f2e2f65c4596efbcfc92003f7c03e416c41d04223361a59d2991c324b68e1e4ac7d91f1b4ef69c021e921e791277f477970e27ebb0d9b24a7e36f979e7196d4ee782f14d356a2603ed1227b4cecdf8024004a67cd550113a8c0f809e1e5abb3ac780595f7f2d55fd8bd841c2e21affc920e" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "3cfaef994e7d400de3bf2eb74952e62c093505571d19f6c93363f73d8169ec33", - "proof": "d040336039db685fd0660a43e3aad4d407b0a290b648ad7e25c21cb9b22101396092e60e85bdda4c759217ccc47a399296ed6f2811faa4ebf24a1b392f339250f2b0a648c6c92cfeac5f28d38d734efe3e07a0fb3ac2ae084f1845321a4fff3d3c5f377abe2133b2092d58159bb51cc995aea29211eaa42dc125d5e5d7e4e37a0a57ce21330a12d1f6a8ce2de0d2574a71dd2901689ab0fb62da396fbca20702553c7613114c992c6be6206f0aecc56af9dbcbb69350f12ca2019c746e62650b52ad010ead90b70a1a966925593ba7dad374399507f46da491969f5d0c7c6501e4d38e5affce45272a8edbfa2aeac63b52090ff62d8d58bebd3079e46e6c953f0e4a5885f33ab90c0507169ebe31a306cdbd7e43b5a03e2ba1e162fe07b4c24cc04f38ca9cf30b1755f3f1e54868dc079a217de8012c0befeab2d236be432c0b3cfd71eca5443fbda3da3d511bc1e65c39e9a1f62715d94d2649489b30972a46a25b725a659bfe287db39735d5471a8654c0852993dbc0334359288f1985bf25ba7daf437c5bccf60f99664308e6b94cd96dc4c9090990b9c69a74443c3a502d5669d251cac1578bb884f14e892b27868b2fe5bed7f2fe1f6c7b58fc94edd70a7e83cf57ebde46816b182bdcb4960011f522e37ae2617b60707774086143ff15120b57630da5daabcfb7cff5126e80921c0c8217aba2fed96b2d70782219db6418885df7ea5b785998147d47c8c9adc10658c2a50b0b311242396ed7c466606cfe71a052add2f96652305f9ad5c12e4f9a9b72e80a50904994d0f2c4bd12db6bc2e44eb5bc55280f850cae04fb5dc836c1110574af2ed69a4654e8bcc3d73267c1b19c0d8fcd4843fa078950f7193e7278a472f346ac696aa7e351afa4664104fd319ccf4a60d6ed2ce11df598993bfc0b58fd798bdc79dfe926794031c6d306" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "504d89a52e666cbf24e51de80e0eb2a2bd8ae0c531c948790353ca6f70b95a57", - "proof": "4c1a0e586d9b12f27390c93c1a03405554437104156dc806af7d93aaefa8173252b2fb340eb222a0299294b810178ebc2b8687a75ab521bbc54f466de97bfe6b50525f5cec6a0a424fe1019164fb117f3e1a2029c250af81de5c83bf98cafb685a9bd9c9b95aa0233b4532e9a829f18af84309521c8793ce583c994b8fd885588c47469a3169b425b799aa71d3827a9b71dd35675a507d2ff786f17b4dc3e20a4b8704f10adea4ccf64d6a51d436d1d4ac150e43d1d8433ee7579de5d8daee0840520159a46511f82e3d361308b022bfafbd3c2936b95f2ef08f5d5b98c73c04bc0628b9ebd3dd513c3c833b1b77c8ed9b9069870f4a20daf75377ddd0e7d218a0697bc122e64a727dcb92350c1817373e7eda9b887164f45690602751a93048c42c94709816e95ae064192e6e4a9e5390881021e291a6050c3c28595df9da460a4aef5077e1ee9101699b307ee12ddd4154100839186b1736f5ba2eafeba948e2713925c6a3f54ca781a03973be56e24077ffb061406b0fc088930e4fb5fd609eb915759ba613eaed0342a3b4373caa1f8ecb878c7372f3c821f47f9b03a44d34ac446db2c242157f5653c6f275a2d3dcbdfcc42bda80a2111cb3df3a2d945c2ca6ef01862b1d46579ccb8156dbbeb86b2506306afa4fca0d5a89afcb9c9938708dc55e2b41c34f2c4928475e30083e5f555fd21e949f46e16715061f26230642b5df6608cedafd827cc111bf2be5263e95d32a4c3264b764ca72ce88a30a744612d8c7c4bca4b4f9d75c2432e74f785e4569083b4d3336cd2e0a948a96f4378ee851b828541e3076bb80be86ed878c5f8efcdb03c4e5f612404330d29d4865ab01c2c71701bd71ac50146e2813bb6f38aad4a23d638a02d376b8090d2b72019c4ec3ce4d37dbda64ff35ee1f4588b071884b484d631b05d6afcfb43792f500" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "b451c151fe80f03e5b274333e5c080fc98c333c09fdf2a5b5928ce304acf7b71", - "proof": "049830832f4ed9545f7ea5d62ea75a9b29ce05a0b8c784f7afcaac00b1ea631c5c3b194e242df605a666f93e332be3de65ec06119762c546a201ecc142f62e2db20987d513440a88a7aa5e8961f572be43458b27d1deee0c1282a9e632992069eed5e215338bc462e73028862cc5de80c1b958c92a0b38c2b7568a20090f535b52ceeb5ded9945c077279aa26d1b2dc6c1e5b1011ebadb90a2497e6c0b1ff50d1257f0c00489849797af589c616c17b5cf188a808634ccd27d6f652d356e1803b67286f026071fd3ced79a24a479114b5557f1101cffb693bb3f2759474a040e20cda450d2f2e25f4cd13aad71abc5bec8c01badb3df4271c90e9707d9ec2202dc2fd31a726c255f4e8e1a66bfec4b8d733216be046fe6448be0e7146a13bf7214444016492187f018fa64ae6f7a9b0002816c3eac605a04f6607f3571840004121a3b2923bf577d9eb2e24e9c68e11765db90910d770cf89ec4b7ab545164780a6bda477951e3183755183bf38544fa7d4aa41834dd428602cb234838bcd43fccdccd9f6ead6c26da250adc874af0ec4e563725c3b51714e0f21e553b926074383b2974ae63eb3da4bf3fa193979f2313cd98b93beb9b04199b0d6112de5071e62da1f3a63bc9aae7573dcbf5dbbab883c0ce5ba8822fde7137c6f58ea4a1789a4a9598e863cdcc2053c5cf48b8ec5aa5afeaddb50e3a134c9d75b0ac84b02ed280e6ba2bfbd643e220c9d70c18487039993de9447095aa87358101168b3812680936ade32d7f1598edb839fb443826d530eb0b832a00f8cb47d1728eef8d5f2e5f5e9454ef2a306ab5d8d6939c6252719ca33ec5f97c9f5f00478726029408408f5b7e86ac870a891e6418dc7317cf61f3dfd74146d597e6e59784cfc0cf044e35f0e5a0f81714241c7c425a3b0537d5be1ded2a359e5923fa6f40824e0d09" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "b82d148ac27fbad72137f518a8929e005ea2d13575ffd4403dc88f9eeb31e125", - "proof": "92969e691a1ed1a6f4ffae493d1be5fb56c872ab56b001db16452cdf0919a36e7c238473a764b8e607c328bafe7792051b40546d6d4812e1d7ee16df5af8a16f8ccaa50fc1f177972c8a8a2956b8ffe215210ee257554c404d19da84d650966d6206e7c4e723717ade25c6016184e6f86e9b01dc5165d7abf555a256cab0ba24d89757af1a82e9f12d8ea368b1ff16fed1130744aa06d3c05db68145302e4c08c55d0bc56165a54656cf2e9b666cf683e0021686730472ae7d1bc2ce29781600a272b3dcc26140e773bd5039595294ed525f59b19d8b31d152000f5944d50f0e9af96e1376b4b81018e93cdbaa2193ca9bb6ad355fe3345e0f0e31d8b4c0826c1a6113af242d95a6888647adfd5b3b2f68f33e6b179727e9cefdc4023d99587226cc8beee56ce8d5ddf1fafc2d5ccdb9a56df18fba2010ffc94c61a29062750c9e9c4c65285dd17173386d42a32f5fa9105724d16441fe439324272ac9e239104ebd1159fb241a687d1e741d72241760263a4b6d59c0273592e76e3ae149dc259cbf63c606e9960210e4a9d70ce39d9b4450cb1c114aa55c8a9b93ff9f4014440a2fdff7afbb3e5f49dddeda38fa23b26b8cc02bad6a6753a0aed970200cf97198cf2c78db18436ee8c1e867f9891e2f3e366743bcfeb7abd9c2e13889ea620e445f53e8f0564b9da6da71300542e695b287083d84a7a8b4dc0f2ac179c25428663421de723ecdbc6c33ee89b2cf6127cb196788ac698993d1a675d03cb21b3ad050fedd9a7cad2b1e361f720a471c6c434f0697452b872f34b12fa87fda875dbaf8af56a65156acc7c041378c6b3983b2182236572afe0862ed8e72897b233c6309f05d92d180144b9daf142dd2c963d2f62f1a748ed81e93b3867ebfa52c01f4ad663a28c53d1445f8b74b8cf6b5cadbec7a147b3f4e2262ea8ce05c456008" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "befce921e1a030c0a9db0db6d9e27d2cd88a0f81f1d492c6b62ae5dfbea92728", - "proof": "420a4201d972dc1db9bb7ae923fce285f32a2b9a9e41752e2bcd10bb86e18b6db6feba3efeffe3572bbb79ecf67a868398ac74fe4b55f1f172acce58d0590735941dba7e1eeb59b0d7ab3c68ece11db11931e584b189790ed04ffae88a018c75a85575b6486e6e375345e9df40ab7a4a0229ba2b5c49dbdc97650e9b7177c7053110e329e627e503f05229c0a35e6cdd3f40d92e259bc11a4e8c89018b2a600d0c89a835b7c58ed2f8890423f0fb6e11c44c3d48c741f423318377b864d0120fe000041754b72ee24ca71e3b3f0d5869f4c6cece3e875df3ee4196ff5668f30bc622d140e1ca6f376c8085c412486bd10c7f4422f12ccd83b7713bfa530ced3b627ef7e9393c379715032089da18789da0b08607572a097bb3cc68f6bb4dd4403ccfe058c8cee182a24069474549ad913d05fb1968b5c7f53da8aee626fbb903daeb375c8a4afa3274c8f47b6393f2a030543228e41b492659ef7ab0e87fd06e82cebb842a7564d394b2914bffd57f6a74fae871936fd888c0a669133c76f06c0c730acf983e9cb8d2eff9c2d388c04c27c9e850235c299ff8f82486b6fd395df0489a6143f656a03237245c830c0d4c5d2a979735c69c06c42b5d53ac011c3eea0b4b967494d38146a74ac4e40116d19af6b8b96e67161c3226e5324d857168f2bac3dc90831e36b94223a11f5a471468bf852cb31781800ec855300a29803f66392d433d9bac638351e29fb29cd9db75e14e6eb0e97be59e7860884150310f0ce68b42054e40da4b30ecd653f51149aa538aa6e9937063a8f37cb553fbc2756cab490e9d71aa24df7ed481ceb6ddb274ddce8a4c104709dbb8abe58388fd7d2ca2e076b794cde2b5fd0620cc6543b638de51ae6f18f4eeea0b898d0b35b1024f25612c4355babf254ea01d03c5d16f3a2c7a1b6f108625148728fc4978d009" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "cecadd74bfd5f874dafb00fe035c674e729f9f730e9542fed43600cebec6f343", - "proof": "2cb83c70a7ae8f7ee8cdccf13b22b25276055ee9c8f1bd5f497dde77ba6df85cb05cde678751e5bc7e945ef5dc6f30958a86d4bddc4437e6d82426d36653a12ee4f393a5c0a593121b6f72661b1e8a095b2882cc132d7fd7438deb48a1a2da48a8e4c7916a9cac1ea7093ed2d319620e5f4261bc94b2951f0f670fb9137d0d17c00d641f3dff5a2bad7bc4c30c5449ee75409b765ef4050fbf1ae8e768da6c03a97434eaf8126f37840a0875c876520876812adce0cd5fd0c7228e8316e0e50c6ccd7086b25e771c7f36fd9ba6b504cdeb9de77f682b718611b412887cc2a80ab6bd08bd7dcf840c7decaf0fa6ceb25b0ad745f115d3463c2e713e9f1ced9415ea575fb2c9e5812efaeb352d55188fa48f6cf54f13508c877a8fa975a6bc0f6b1a02ba7071ac3216c1593cde1dd83d8424c69d45cd06dc633fcc44c3bd7eb7287833bf868c354583dd832fd7427623e64d806828baa0a905a1df91f6d8814624903a0a3762fc6a90daa0120186a4792ac6967a602595ddc71c1434d8aac9f240f466ab812e4ec6b67aeb4724b1a81614e3863c4afaff047d9ffa60cfd35b93373ea67a4b46c220922798fcac23535dfea171d1846aefb0e565179faada250c2a805273bf87cf446d29b9bf972b5a35d40fd8ecf5972df4b2e865c65a8ed6cc6c7075aeb7e24a4d92bc5ccc07553a41962e41e739cd019c872920ba8a2ecd6242d2e4bfcf1879438d252a0051e41fc6c5c79f18d24413d23e6770b2b47677c658b6f222c62f2affdc2e9ad5d2b9428389315ebc1e638fb5a487aa87d7ea0ee320ae080efa2513685e041f1306e7ac89c8718a2a4ea40b389ec94fff3886d60e26951edd5d37b443b16075c96a765e9186b1e087ad6cacdab5d35ae6e1965c5c02591ab55109c05566357131a433373fad990627363ba1da426de090322c7e4505" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "d24096421b53902ee736ce947e800726f8877a711f3b1c9bf38944ee24fde174", - "proof": "b8d80e99e0f84e991440387715f83a1f144e5aae8a9467e869c03e59104752631813ed1b75865293115fd4da7498efad9c43a6b73015f8f2b4ca5dc87fea4668ec6e9a54264ddedc468f52c57d80ada94c0cb0d3b9136ccc08f8b5507605260f60471bbf398f1fd93716329316c1b1b0dc6794f5c91280d90cb012a112dc2b7c53fa87d7fe03257c239e67b95b9b24b9ef04a6cd7fbc699151de63e4cd531e0e318d77914d97c583ff1591967a6fafa82331d8cd8c6fb12f01d89276fd27830b8011079f055781beaccd777e6b302872c6e137aeed586baec93a6e2867d7dc0030ac24a423b45a558aa851f7e3dd533e93b555f974b1638b0ea4be79efd7447eacc02bd3b03a7a82e95ebbb97b61d3ab752bac01fb93c7f6e86fd279d14b30105cd3483408c5627d62416f88d90ce6c9b278cbffa8967bfe5918da7bb780f4793c24d4d739992f7da5ca2a8d9513f4ef7722b0d1f9a0fcacc245275253d61c5bf2b101e2120b320498638bdd554866e2611a88107d09cf8324f04d92d608704d0c3882d642ab506e39c23c0a63546a2fa0c9733ca7a75c002db621d1d0688177a0e12ec259215f4587f7ead0d1cfd62a62f24308ca26cdd45e7cb584871af95b3a292f5f28c64084d70c52af398074b3e95b048f04c822f81f754fcdcc96a533c21d9aa3ec06a8a66d0f4b11e28527ee07c486cf58be59816e8f05d69c755064605765d463bae7139c7e76553edc1adc4d57dde21d8c7c8a3f7b09573b3df832649eab1c0a1656fc015442237056ae7cc7eecab95b043b5dede6ff7a4f15fd45681c74e964d832d86d5a2bfbef27d82bab176533e4d8ad956dc9f36a2f202a7d3734d3d1b5721c8944386acf0b3ab4ba908dfa67c1a0bf920a49990a5cf7af0b75d8ba9ca0acae9a2564e7abd9183fe4a47e9c0ed461e5b9eb0072adb3d4ef0d" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 48 - }, - "commitment": "3efe2afc88ef668c36b947e0122c0c98904e3235b633a59e9635424523864b63", - "proof": "743e259d3b99643c15a42f1f54ed224a1bd9cde82dc1b62713e3c205a91c316af007260b25a682e322b769630d2c27cb499931be2877824db957ceb51913997fb22618d1ea660d48e9ef1ee027ab2f0ba8c43d85e732d51743136694130a476a626e620e0cab08d8abdffe4e388346ea88f78c0f91aa6b7791307d90a2a1d75dd30af67be0a9687df3bcb8ddef32d0e8d78c0e7a6759cfd0bdb77c19537fe60ccdaf9c5dcbcf397eb898ff2551f46e4da4a7e11668a63c6b607bf16a6f78a80d3c73ea6f610b900b17c297ec77754e2e1bf13d1416c7b196b02d66a11a38b80ec4ebeee9769cc06308c22c221eeab5393a56411aae3521002a47807dd7ea442c949cea17164c5b5ca475732cac494f450083542226ccc3b8ef999b5654f257124cad444340c80bd142ef6b5703de89b0c15e4aef10d231ee84172aa8ce8afe4c14bfe51a48df8fe6e90725d13668a061f5456f64bea016827d6161d36a21134ee8286da760151b5bf5b376cf00ec5c20635ca3dc443d6a4b8ff93df8401b0b71c81764d2e9cd589ce8d096778152d76306114f2fa52460d38b3262cde114cd77567141591c88376bfd89c81aae7d937c1eb941234869c60d76b67ab98b8c7b50181981c1ad46c5e335cb5c405e8143835a8cae307d57b6084b5b78130f6e815436a55bc6c051f1fd324bfbeb5ee455be6b617a57ec04c1f54ff6f74f76f7000ef6f9251a62373e52aa77008e915173e4b433133664012972fdc4186a6816a820f099f352a59a2299efff0ca79d4bc05921a3912498a6528513a73ee0bab00f0fd805fb39c41132d09c8ec93b9d8c605a4a03c6d72f336fa69cbeee7339cc6f730ca64d4cbd6bda464397879ececd616c4bba41d077eb13fe3976e8fcad3bee071ecdde7578d466e9757acd2c94ee69bc967fdb5b8f9b22cb4191bdcbacabfd01" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "38512b9c003ff490cf713d8def4b587c58bfce6a4badc2610e83051204387274", - "excess_sig": { - "public_nonce": "9c5e61024ca47dae5dc524252a25234d245235f779415ee1aaaa2313a388b451", - "signature": "4f3d0f443607063d163402947a0ed41bad490880ec64253dc1de3cf0d2137201" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "3c1307f8cc2a6abedb7d706230d9cc28a7b2b58db114d831e2a9dd683f6d426d", - "excess_sig": { - "public_nonce": "c022f44568a6461c382850fc75ec10154699a455356e1a0f41de93153c89bb76", - "signature": "be37b29697120732aa00c9c5bddaa09cce7aea0ec9dc50851f409f6b48f2ef08" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "58c59a5a9c4a078b6fb86999b629a0637f87aee70dc243fdc307570bc384ee56", - "excess_sig": { - "public_nonce": "9819126044c7cecd737fcc8b8067a6cc5e274cdd0a0984301e8d020a6f24ca1f", - "signature": "d98ca093dfd8d013c96254aa594a8c8e4105b3a848f2a57185367df74e33f90f" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "7c907204c46292bf436a712f48cfcce8b9ec8ff171a297ec439afe63665f021e", - "excess_sig": { - "public_nonce": "e245d709f64bb7218e6468660ef79881e65d040df3f5f2cf342f0d47e34b5e71", - "signature": "2352820e5f796111eb764cd8768e7d4368b39cfc05ffa394986c2dc4c6b0f50e" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "f45c9c75538541f304d5d6112b9dc380b84b48db1a56db6523042e5b93037a51", - "excess_sig": { - "public_nonce": "a68cbafe774365794975ae389ad7407849d385d88353af4c78bc8244feb45c26", - "signature": "dfb5ce9b035b97467a4d74489884e1ad2c049215c34def9ee8da02bfb8abdc02" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "263b2c2ffa985d60952acf08c341a8baebd31156759edb387317e3a423fc2315", - "excess_sig": { - "public_nonce": "d080384423e51158b78d6311424b1a6208f92349e52dc0feeca94132200b2d73", - "signature": "08332a8d354239b5b38d55fca93763a35df944d0fa6a8f92342ea8c7669b4701" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 48, - "prev_hash": "54f4720ecff8747de4959c5230747631bcc00e4a8dbfabdc001edd364aad1574", - "timestamp": "2000-01-01T01:49:01Z", - "output_mr": "3ced17079263406d531afc0717e1aea6802be1d3eb8c9004b9429226dcad3a3c", - "range_proof_mr": "74c27e16b227bb369ecbe022ee8afdd3149f6ce47e8c7ec292c752d9370daee7", - "kernel_mr": "2fec52e01c107728eacdf4124cc22cf3535f96b9731964ec3cbd21300e4a2fef", - "total_kernel_offset": "c4a040b6d159ca68b2f551bf856b88c99473b686f781c06ddac3be5b91d5fa08", - "pow": { - "work": 48 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "1685fd844f5b6629db0ca74d3d36ef80c484bded9babec29f91a85999340a055" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "3cfaef994e7d400de3bf2eb74952e62c093505571d19f6c93363f73d8169ec33" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "b82d148ac27fbad72137f518a8929e005ea2d13575ffd4403dc88f9eeb31e125" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "befce921e1a030c0a9db0db6d9e27d2cd88a0f81f1d492c6b62ae5dfbea92728" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "cecadd74bfd5f874dafb00fe035c674e729f9f730e9542fed43600cebec6f343" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "0226064fb27a9b99637867c8d7e034d98bce9be170be4d538436f0443b5bbd22", - "proof": "5e4868f7e7cbed3f71479601308c9a87646df2c362ed149b085ae271d0e6cc43dec3e05f6c1696487770df46d0700e56aa8680605551238e4d366c8ea3fa6b1d505d4ca0f72de65c014e4958f99472bce609ba22b6ee4f3a77c8412b27278667420e46267375ca32e16ba41e4a86d0237bda4f1f2dd85fc9d914ab7cf8bf9506ee287435962cc3b3e67e6eb9c86ea70ee7e6206a8bca701b87a6319842bf8d025a44be846b0473de24fc4b75414389d9f143ee1d32de2354f3963af9c37f5502be40ad2a5bf8c6736417e1f794094a39cfc1b6118d0c27bfaa05c7b4ce7fd10bf4553cbd23381b79cac1f78169a92e807a6cfb98e8d429ff003168ae29b41121406349724c7530ae904c6b7f694d33da07d42cf02585ad46540b482a7856e901b8d4c0121752b4d0164b14747d73202908bd4437e6b0d046a74b75d3fa51ea2a36c875a9daae6124d6620c610051b0c4d0df62feb44f0d4ef09bb600b62a570f9ae99169f5ac00e701ef9d1b39de1ecdb1d3a20b6fd2d6d59d14ae851890de53d077bf8794d00f6126e224c13d9e47a6262f6a415e385cbf83ba3eee2fcd1e08eae44f20d3b7eb4168a7145da964940beccbb9929d86a9a0fb8942c328ea89580abd19978c3cd9f9eaad6c37b7438ec2ebcdf002b5279743bb7009219503da15c8cf8c079e89c807eacb2382ef5b55bf03c947c711f5614790d08ed12a9e75340e150e8459417bc17dfae7b5d919513b9dd4b4da83569d2949e84f4ac632863320624d7d38d18e500ce3519419e4272f04ba7c40509048cc3eb5a8cf42ed3d3720cc41d9e6a4a51677d6bddc8f60b6e1db70348cbf42658527ed8c4cfbd2976f8af1fe099b3efd9c22d2cb83a5876b3cf4112e360bcbd1fb2f2f0fb371f2be03c5625632795659cd7ad87b886af89b5134d859b7ec9c14a39864f7f45f1a3704" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "36aa9371e33da44baf5faeb65d5ef0b5f88c814663b5fee6061d492112efc76a", - "proof": "b4c5a58324c8112d719ade9e9424447376714ddad6901b23f34a673f6efd2a1f14075114a46fbfdcbc0094eeeef31b4228fa7dc225a927e95e4fd8fe3edcab0d04c0df050d6171fe35c80116d4e5c74dd0926ff5f3d22f3e183a4a1f07159732d88576c1d88592165d3734e42a327fff925e87a4a15d1041854ee55fb14c8a4923bb410a076fe02bb63448a0f3b3501bc9dc6411b5d089ab68cb08c0594f7d0b9abeaff5b9711cc7fcc9572c74fba973a4934aec93166faf612e174d876a2d0ea0394bace978e14e009e79ec0dc98040096df8e9e93d221437b75815208474078a49963dbd20fdd81bd29ab2c58b5c8fd7ff27ee9a7a48ec563cd018db5d2e6b0e8781ea9cb1a1c0069151c784541864db3d849ad827738a2b15ee6fc784117500389d8314dd5d4d1d85a68edac92db4d45b63caf43e5e94092243c9c185583e686fc66c016dc77a3f17e91256d4b856f6faf97b4c0ad05d55f06f04a676a9241e038b0820a33a768ff791f9277ba3a7f7ccd0ba2157af70ecd73aefffda980fee05006a22eb455ab2330b082dd0c8deb883f3017c223ad817db6f125e1672007af835849863ef16ac50673cd189af1e155757950a8cd01e1318374b90f417462ef01cd910944560efe6659410ed172af2da29f0bc717b2fa678523ebfede05984029c040dad60a07b50e6a87fb31da68340b6abf7561cc944437a56f75f2437d87441d12f9fbbba7410d57b2e49b33684386e166059698b846d4f0df592941156d9ab85dbc1f0fee66cdc38a76790e2d6075c5f38a10c6fcccc8074c098932c12df2db938f66df367506a0f9bfd53ed32406551b29cee670b744a45d80a160ca6ed5f7ed579ceff65cb520b8b1e7df7d431cf7ebf71a2c6f4500e30dfb7a40e1df50a408f136739c1b97e749ab992f3cc298a640dff04d27fa09183a001f804" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "52e7bc3a2431304e36313da59b9b2b7ccfbd537769435004691efd3a21396329", - "proof": "122b0f1ae43ef8fcbc6f8e4b56d006a9402e419fdc1e420d5c51e3eaf3fb61311a057a0caa12e4942c2e45bf3f6b345743584365f23e9d4a88ddf68140a82e0cd456d001dfc204839fa134725a5118f464f8bea48f8f13e86b1419667cf7ea19ea0238ab45738da97a70ba7a7851135356b68052ba9b869ae6e9e0257714fa56ce9f68aa1a6ef1021baebc2467df2b551db109166255795e41524eeb4db3c009b0cc94013d152de60ce42a81e197313818937fab99085b5db76b0562ed5e0c023b9b8f5e15e129e7f18976964556f3bc4efb1444dc1f955bdfc8150221d7790cf0bb5412e2b8eac66210420fbc1546e8221e754a73bf36b0b6128e47db1ef156b4185532a8e7373e81a034c15fb64e89eb4897d8e5f83bb81ffa15c244b0b76f0084d3c9f1e836f14622660938af53c44fa3f0bce904aece32ef93fb91e0874684f6f129ed44d5f3095b4bb304cd6e43b9c56f12d8850a348b578c0e6301ef4142e6be2f2ef57979b6858473f893431381f4137aacbc3f881121858a37a99e2068fa8bce54b27a724355b270b1eeb7c58e03cc070666fa02b5174c86a2c36e453280343391c7124e98d52ce2c7e6208b610dda807fedfdf13d540c030b422214807b43a310b29204827837b5beeee0f720ba7a449968054e5ae4909713644d094a616f8e5f6018d7edfe6c162da651a959136dd19e9644b74c5501d7687f1f191eae315363699a684b8337a9d533787bd7bdc0d2584b6ff9c6179d35d4531b0ecca17b5b1b83878db0b65af81e74550eb6d2edd9c25abed76c1d7a90d9b3e81a70514c063102f3679e3335d8480c594b9975a48d7a30b1a71f09cfec9f015935249ce699326786ca27717548c161416a185703089f21623a91119b7bf5f7fa0992ea7dffa1871a90dd629080495b1a6ca105e653393d7ea3700c678ba2f1da0d" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "56e24dfb24a61cea7754a853f2b6958f4e549f6d92fd57382ae51dc94e144553", - "proof": "d4e6b1bf83d396597719e850d120d817ccf88c9b1783477fb0ca48331c93b06f58ebab6fe71eee51a2feba6761f8927e66e2e01e6b2d4a160d46ab5cc8e39b48dec84923307b81d802124e6d2ec51d8110cdfde64809fe73f0e2fcce5769624a6eed4891d465fdd74cbdf2f9abe4dda9e32de830009dff41135707f46d3a981d9c53f1483e9ba81c8a57b91f2af52e35b19537d0e20fcba80bf6d9dbbb1937029c90e0a3a224fa455f203e9bebeab7618533e8fdc818d8d646c28f0e0385a000f54374c33cba65b056578ff23ace70299fc927b326ba5ee315c1c349715e3102dc87806e75ea3aa2a1ba91d8968503e9960f64bbdd6dc08dd6a75c4bba769c75d29b65d1851cf54efff325cc9b76dc7299a964a7d77ee9e0a100002404377057163a91717866560efc910c83087d75dbb7ee76167c1e0c7b74a9d9cca654b2680846ce58f68c1a1d4dacb06cbe01433d4713dd30639b8e6bd7c542c41a415c595611b3a40e6b2857e6f446bf52e079c7d8ed0a15dea4088f5c1be1a11d73f03d082a1e46646e3c60ff1b49b88d6d9762c5de26d37e1ba8afeefb22a8ea43c26fc67fae809a1ad49bb9cf57b6f8ab9e4aec4388931b72d9554f7b49d7c431c33cb2efc4cd629c7f051cd4d1cb5300c5a879187ebe58e321a18d44913df2177c5ff84f01182e94458f3bd50795453b0bc286321466e33ce025de3bbb5abc96a60a547ccaca2982338ed5b6c187cdbda8b9f06b9101b4dbaf8a3b41f62910b4863eee5aa847249220193647efd306fda24f81a143000050f9729b4c7a8391ff613d86ea39bb0688344651bfe9a8dc40556df2a1d7161c7de6de867efca462085f77d09a16b599a0b7d68ae25de99806ee36394c60256dbddf73334dacbc12b91406177141f3a2d42f5c7bba351f2d5ea543ece37910cd2a98ae3d83470c8038580b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "62bc13507f9c05e5e39a28f6b5a0d36c6ff0e19f1f0604015e8aab8d6411eb28", - "proof": "2028d8266f76ab78dc6af3855120c02aa7f29f8fc4563da3789f232c4445200490ec15cfffd73522157481d0c33aa47c0347f0210edb3fa0d3f88e27caf74e16fa22a39fc2a48074065a4137fa71ebe5da38cb9bc0213b78e6b25d3ee609606e8aa208b1bc5ef7036fb4a999814cf1c4e0e6fff1833206c8cffdc1fc2be4f33e3946879cc2a7c5118fa52f910b0e208f66b0ca35b58389a15a11b737ae85d101108d625be72a0988a08a7beeb1557b4758692b34cef5308e9f6336a2cb207909f4e7123ca2c80b3be3002f1e5f873e46e5cf1a4a18a28a9c60742a725e8e400af4ce310729d2354545a73185245661b1934218c6f519bf93ca6dcd6f88a0193f50512f9b7d1025b4249bf7ad4acec69dcfc33e7ef1addd6c8faedcd4c8429379ee0e0aa6e360310016f6e9239b76bee065aa93c4569eece480bba4a46f71b51340f3ed7873ebbd42de2ac77c4945eb14a01887b00c69f7cf4c88371666f51c5e662a599218b0dc139b84c0e6453ec0eb9014701a7e24562592b9bcfd7dabff520841538c42888dbb18a7ecc9c521429f1527535be38df440b43c3f327972cb26d605983ec6acf8f674e51a7adc7a8d6c019e59a8af2bd25d8bd023490fee490ada04f0481cfcefba557a278d01f425f5d996f3511c053ee0321c5fb89052aa0bd6bd6a943477a201b3e95e4f662479ac28c44a06248cc3fd407c2a5ed98a250c9ae0eef7d471f3d908fc29f17cbd342f46253deb6c08f23cd3771a22a092c73efaca7ab56cafa778febc1ad393d098886ebdb4e3d4516d9960561ae0ec39227aaca5c3efec639ff64969a3478db2aa8d95938e24c501d1aa3ba8e9511a93c044c436592c03a0bc68fb6b805d33122b66c3cfb2a573b6bfdb658922f1a062310fee745a41c6dced243aa6ca003b437198b404ea38810c3b60318f29ad0b0b2e02" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "688590b9da63911958234da5a916495e44c21158b3087899bc339411d10c271c", - "proof": "e68e6e7344253801dbacac8cbe784514946beea79ab5eb3440053b8120bedf1208bc3581662523866beaf5c565e57b4a6efb79b619a48ef6a284aa444138fa0b9649657d13de61afa33ddfb8d4a95afa93c9ac6c5d68aeb46859bfec888582668cebfc64a5d499e5fb4557469867aac6f39355ea86fb1714aee68a0584173e3be0453a662b6b61ad0fadb24a8bd007453731504a8963257c8206f0b00c4e2c03d5de3eb5106f47f6e23e6222ae3c4f53f3869bef3eb56dd6a2bd7ac15d954107f3f82863e819065df66b01e0d83dcb09d451ed62d50c2eeca3a7ae5edf7699000e9d85c41f352fd97c5ed52a3cb289f6b0abd0209fb78cb06c4d9a30ff22903372a4cdd76640d3e675d43327543d2466c0d09c69b86168586641d5481dae002ac2460b4f74f98a6c896829212bd2af849e866c0b31bf2bc9d885f439bff9ff7e20a6383c5b86723bd2826a763f90f5b2f282b778c861977f512e5f6207074a083ae9ed90652378817c3046be8248f214d3b6e9e35ab72ef4c16eada8da97fc64005a912ef6acfe2398fc366144e84323d20b6050df2988d4dc887339a16eb66becb485043a1e721915b32052770a6ec70bcb12382bb8eae7f25381dcb78a1f0616bbd6c6313f2c5783a0753f6200546c2bee7aca1af66a9648ded8a6fb30b56422cc85b3863a10c88ce18f56b3d6025f7cd095b7799ae1fbb658d35075c2e0384af4d714597167c03f77f82f9fe47ec151152e609c1de575d24e888323cba7352c1389738eabc6ba9b81651799374ac038d668a57730c67c5d9dbd91b401ea1318c17d4474af23bb510b85420543d6edb3ff5b3cc420372b626bd80b3e5be858ae9d55190d2840c67f59232fe4c5830455a8c8380c63effe658129b76eae3b0ecc00947cf4012cf73b161788852a02306243ed7a5c9dd84c7cf9df310a71fb0b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "6e1e0a522d99fefcfe3cabf834ed91d8f930ac349a09d88df4271f85cbd2d60f", - "proof": "ecbbcdfa1268456b2773217dc7337fb2d1489ceffb4698f888a260a4d992db345a2edd107c2fd757aebde1a4d83c4d3e9b6ee92dd7854de99ed9441d987edb45a03328034dfb31d2ab479ddb8a642a4fecc2e889c3ba6836dc34dbaa98aab02882ccb2aa187618db1c37b7e0c9e31252493472f6ae9a33dcab3366dc4463fd4fbebf9bf9c06a1348c8f0995ee11cb63bad03e07ff05e4e944acbbd986d28a4009c211d2219d09402c02ab323f1ee26504168c8c0d5486fb878e86f8b6776b702f262976ebdf1af8864df1a9c111cfc56111fe89cd4e7cb1484bf93c24410ba0e58f96083b777b81c0fb9de1074b4c2bb8e308924005f9039307d97720ef51c5d5645ffe23fb8aa4241b8e281018f1a0fa8638514be8cb3f6df4385ff5e08814520cf76e307ce9858d5c211c207cc56f2b9a04685e076337b17edfe23b6bcc74bcce089a3433ec2c9d55ace65b2c6648c03e1ce82da41e3c68a38d69f7ad89c2e56ce87f57052830b862e1790ff454410dbbb2c0d3234be7ec1b8cb4757162778a4ed3db81680d1fc061b59048e8ee4b4c3c8bc7f114f4fcd4cf0d29fddc78a5c5e25787a8a5cf00ac2af506885f38f3e0036dd83401427e12dcfddea5b03ce25b8277d7c9c4cdc4cd0e7271efac7d36e7759a4e5c5edb31cbaaa736745a09779929eb816247b8d96e9039c7f45eb0b122eb26da8e0df9b4dfbe945803be49271128b10fe859442497656ac6e8afdad3f82dd69b67132ad8d42b22f60f3245720da5e04ea254e21a604c370c2071f3c74232da0e641c7fa51db3ed65761da8e31dc8490a1004e9be1bb6243111dd1a7e9b87cbab84fc1a65afa035d665327cd6784cfc97dd30f3c244bfc98725b06f094dcb3fbd1b06572a34017148265c5c4041357bbf62ebf8a055bf2473a183ed41e18db76703bb0256d06afd30e7a124201" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "8e138968d35d720937819880b95f40e1f1ad27d1782518d1d0df9aeb4a4fa80e", - "proof": "44cf2808a449cc8600a3f5297c82aa552eded5ca93f1874021d6b9be530c757606bb5d3b5bd74703222aab4734e7d904bad911f65f8c6aee45a30192e7518b505e40fe0b8e67cac5f837d661fc2d862e8e67d8a3a3b3eef6bbc9d21554e9bc36c84012cdffca8e3d0af4eaff86c086e74d6aa3980d8d2e7fd46e695e4a17a84ba0d5aa9e80e2e903128e0fa3b6289ba477efec5f8b9c001b6fef57b9d3f2050cdbe1962bdf1fdcf8483848b312a57d9259d7a7b7275136ed74a8e6b20e50130d00406b533d5fb6b013886a026fc67c6ecee9ef79d4163f0e5d9b5ecbf39eca0730c478d0d256ed91b0b06c6c292e650f043f0c79dff492444d4976e91a07ce70de0b9b1ad39c00df67162d5b715f0dbb3f200e5c3003de1bfa96daa323153a32200f86e2275178c516bec9ebdb376a068586afe19a6482a590d36ada5e769b3d5ae01ff26af45c7d27eec2d737c5a954e8f68e3e03dc16ab47cfa928b250784d1a998239e8d356b7a5333a13f20102639a3379b673451efc02a55a2e45bb69581e877c0b2f7080a93860f0454621d9cdb2caab528f27e71c0b2f5926c1bc0750cee9acbb8bb9ec3a1e58b32ec628472279c5c5a001b0e0ed9e6531c77c8d3601d8447b6b4152570963422e8286fc86daf015b47458ad9be5c5ca27590453f211da8b96f5ecaf85714525ad97a290e440dfe3ac9ef501659a8b51b579184f4579b0873b68d2decf8d64ee8fea9da660d6fff3e157bc6fffbfcbd4d5a05b157238367b2fac5c2cdd8ab07e83dc59e28a4bd9ce9d7cb28beb617b225dda994ddb2ba6146d27eed30d7ed6e89606e32ea06b01204744d43b332978106ad0668065465236cd20d304466ce5d8dd26bea19e990576f5fc5e42dd1c6814e399b0028902d2ec619b15779c6c9f83b17f544c8e861932e88dbfffa1a3fbcc44ece3d05c07" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "d4355fad7336e1e2a9ebf517be28246b864ced7de46a010d1f8c3ddf09a1ae4c", - "proof": "8e5f093d281019bd119e17476f506fb21ba10a1396fcf76f3321b97d62d81377beee0acb728023b541fde500fbd3195aa9097b1b6e9393bea7622aae47bad84f20ca133d768466ec8a294f4c7641cc311316ecffdb39730fb5c274e4304ed4710ad804c09d3c3b001f322d33229ab6a1ddc7609100e8238c70cc6fc24944bf662a704e829f379e7ff6beca9857130dfeab3704f54e93fdfb0ddfd5022c1b5805fe209f1dd36dc99b207cc22951b28a48c35d7d3fd47086d010162df01ab2790405952f5dad10d0e2f4231a0a4b39372cbf59837533469b76422177ee0d91d502626d09957eefbb7e9c23c2109b30d23924ae6ba2efe78c0693184b0716962743f2714fa0f6b6bc1219e2a5daba1dc095432f4d2a7c9ffdb6af6223762d32115746342b14ade20c1fada4e48ea3366b5aeb681f372376895661d67ad0d3aeef68a05f33461765525c06069a3455e5e4bc6bb8dd61c4d7ad988c5cdbd30923472c70ce85d41e872d3ccfa6bed1d9fe71fd9071012599fd46a405794ab83392c178ca3b568b7d49bc905be43c84fa5dc2fcf40a0bddcbf6d957a36a906e0cb4d860d82254b6120303379411d8d6b30b1f0e9eb611d7a157ebfe966c7c72486bba5f8cc2152f4483f2c88e3d1de7eb8c23e44edbabd2d8ccd83b0bfca8d4a8f5a84a50dfbe4218f6d3b391b5eaa6217ffc143c5a74cd494896a5fd9998b7d8ded018cc48a732cf5bdb23130e4d5f07fc27d65ea196745587677e362e13499fabb530fef9f0237e8cc4fa201c551b176f6a18204fff22408fd4933171b16f50dde13d8e572e8790b32fc56a27168b6e7f2a5892fc92e22b551035cf896c6500bd1c0e5aa6d68bede4f32245a945ea32674d5d03b252192c31a258b95f2633d48b2d08457f9b38f84627c3a84e0ae5acf81e59e9255ed2bde8ec8270a8f641345c7206" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "e69b72629f9370c33a182bed61bba3e5b95dcb34c65f5f5f7ecfd6545b4ba079", - "proof": "48735924ee09ac808a806f8489d4b53202d876cecd332404b4b807e98c9f65668a27405986ba3e43f0a315eabd390be07702e740786f7cf3680b73f6b9014f5ad2900688b0c018b7793beffea50619979d669f7b2f3b5d5e4e48c72d748ef116ba56076889417f4de93517a91f3e67d22847d9df7ba0b0feb43aff2891825b17926ca64f7e8a21ae85fafb1c889b3b30b8f7d775e41d2c428b2afba46e9e370b4021d310893ef0b3e5dddfbb727650c673f94626a7cd91254caf24698ec6ec082ce4da8d7efc94fbc9f5f6723b74ff14ff18d53b21d3b9e7ea1ae7de21b8a004de047c7a82c18b61571dc3d64ae3c3000ae3c63cc6aa2cb733e1f5faf9d7d978025fbe5d4f354521b0b323cad68b35cb49136498df2febbb2a2aa3c1fb796b29ba9d8eb1dc14a42c4583e422739042c56cfd790dfcfbe5c5b2ee6bc2bd1a9e42e6bf028b22a588244fe8408e3d7f0aff4b4e80461696ccc841bac6a81b473e0e087c5eca6a2a6906b7936a286790fa01ffe3d604f0f75a440a31a46d0ee60f036aee196c36b092916b6a4084f2e1e523bd38a538c358d89906836d9601f12a31b04c49b9115e6a7d3e1f9426a7f13284954e3f3da689ed6299c86dc1b07e066af889218dcd92cd23881ac7ef5a2e55971ffbc7e034e999332f08f881396a477d7efbf7be331b056f974ab12d2927d9a4e1eafc1337e8eb4e468924175782d436fc7da21d19de7fc8702ed27f6f420bcadde8c76b8e17ece079807a65b7c91d4cc6c7280c8b18413faf4541a1a0a8a36ff5edca3f19c25f4450ddd1c1feea05685af983b4ad385cc054e647969dde374df3d6e63233fde069c0f45d7c9711c00751d6346eaa4d02792c120b13f3f798b1fa172c7545892beeb4b59a84b660500c152bc05ad5173ca0119837006348a9d4db40bda1ac97a23ade7cc2c85e599b01" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 49 - }, - "commitment": "8691a4e1d4ac439ece74d5a6002171b3cf220cac97dded3e4ab59ca3c82f4676", - "proof": "28dadb981f7ecf63148320c304aae990faf426b0fe0f2593ed7c922a75efbb36d6daf177f7b21d53e210b8c92634e548a93c3bf40347251b080036055a1fcd6afc6e742257ba7f643f8537833388048adada9f9060de8d036d54f5c9afda1732bc95a5cdb68dbbe95f760210f8679184216fc013b4bfc7f2cc36b1a697a98879a142f93f912aba904c4c879ed5304cae2a82d0baded3f436d19204c11def1800b4ee0728e92d2f8abcaba7f6513396f34aa66348c228b1a7683d5e37afd6f509d85d568ef8bd422fcaa0b38252f283f2bd1702688f29a3d3cd0ccbb7542862007c5798bde22fb8f70c9434ec201f0ba5869c2c7b7d43a85f8d84e31eb9a96f115261362161674a772e9f05ad5784702cbd21cf017d3ff5f23454ac703b7f5207e0698f845ba4349970e6866f2f37cb345f4cc098369b9beec179bdb9f9cd0c5d626bed25c4b741d7b4d64ecd46be56f52b34375c5d426f62c76646677d0e9f2bc6a1ad1ebf41778ef2e0ff6bf2152e3800b967fcd55517e2bc93dba7231aec6854b441bf1ec021eeb52863260245db5c4a231865de115ed83eb2ef43d2bf3d45badb5f151fadde104fbaf2c21facd07d2884535319c2612821453c7efa100b45966b4ae072bb91cbd730c9b60c632e70a92e2bcabd601b79cfd509a099a1094f187964c3218e8591876d302f52cb1c31db6e903af37492756e507716c1e2cc23024bf2b137657e2ba4bc5b38018f8bcf2a1d695695b77caf92be581d20f59715a8c407d6c49d53c4a14c435898ed028fc0c1c9b0a7f8260b1f1ac2cd90da9b0e10401e0f7eb6ae763f16ed5b86876e01d57ae0b9ca3ef9ddb7ed94a2c1c3cc172c0ffd3770c0efe8650e0bd2cb0a6d6fd033eef9b34f369089c3e47f73e66809a7ecf394fb8c0d6f5ec1da42aeaa978ec2f189774f30e62034b03ff541a46405" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "1cfe88883741c7f2b29424aef0283074c9076f0b30f8c57a3e9e3f82b36faf03", - "excess_sig": { - "public_nonce": "480bbbf6c5d6519cb6fe9e798161bd12981679b6c16331e692534122a9c8bf03", - "signature": "a1126556690d45e6f6ac52487679af1a31a407fc60b055a7cb69c25d5afdea0f" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "689c4beb163180d3688fd885d7fb137eee39c331935147b732e8fb71f84d7756", - "excess_sig": { - "public_nonce": "aa97bfe4dbfb0e2412e3719e583d9ce02ff3ae055f0b5c5a7cc1e84a125c7335", - "signature": "1d145b87118ec3f6708ab194c93c9e81455b24048829835a839974581129a605" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "8611eab8c05c5fd9300b6fbbc9999a9cd89d8ffce57278b2edd83c7b22ad2225", - "excess_sig": { - "public_nonce": "0e4dda86ba2c934db3b8c449e0c63fafcaf3dc6913fa8ef6581367d8d27e1c0e", - "signature": "9f729e1566216bdfe5c832a91c85bf3da7d261190f39ba476bae63fc37f7e103" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "9255c2dd501b8d0cce21b7dfaccb72ed078762ab03c8b4e4a37aa5a7e4785f18", - "excess_sig": { - "public_nonce": "5cb159e6c97161513f74a30cd9a6ed3831317cb6d0b871cb632c0dbd45094b7c", - "signature": "f95673e26148b5382c4a76d8fa76d9b4392503c43448e812788c336e97057101" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "dc8f29a5433971cfd78721bb3100a389d52b961f8b4e35d6b7264f52cddeb065", - "excess_sig": { - "public_nonce": "76a77dd9bad1ad62e9931065aebf2fa965dae5a2ce862662064d5f9fe7f6600d", - "signature": "815bdb3c42ad61d9bb1b558b186a80084309dfde8c21db8d28011df23f66ec08" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "de0c5e1846bf32e5563200a04112e395bc506e94c05e7c93b9ceba5284c84e69", - "excess_sig": { - "public_nonce": "22e7f4c392c93cf70e6a7b5b9d10f4b625cc26097e0ab78e17b693a3f6801463", - "signature": "99979fab99b1c286ee3677de2a95d08b4358c24567d4504c65d6d5193d90db0c" - } - } - ] - } - }, - { - "header": { - "version": 0, - "height": 49, - "prev_hash": "8ed0b72c735d7b2711c69f6d9a63d7c44ba99208a2f6c90e5ba3647717a7f89b", - "timestamp": "2000-01-01T01:50:01Z", - "output_mr": "e50d6bf1edcc47e9df4f9046ab507f6ca315ff26839ef4a1e3982a40dfc0c5b3", - "range_proof_mr": "ec10f6fa7f2e826def5fc0769ef5622972c3593ec1de0ccfddfdf02b3838ab11", - "kernel_mr": "fd0731fe5642f940318dd6073f18b09c9718d161dd3c239ca41f712a51c0868e", - "total_kernel_offset": "b9bbb0a74fb1edc56aec8e40a0f9c3e4e70c6f14f956919f72dcb82b5ccb080f", - "pow": { - "work": 49 - } - }, - "body": { - "sorted": true, - "inputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "36aa9371e33da44baf5faeb65d5ef0b5f88c814663b5fee6061d492112efc76a" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "62bc13507f9c05e5e39a28f6b5a0d36c6ff0e19f1f0604015e8aab8d6411eb28" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "688590b9da63911958234da5a916495e44c21158b3087899bc339411d10c271c" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "d4355fad7336e1e2a9ebf517be28246b864ced7de46a010d1f8c3ddf09a1ae4c" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 25 - }, - "commitment": "0678728e31adc97eedf2840ba93bb4d7fb9a17c55a60eccf5048dc8dddbd5148" - } - ], - "outputs": [ - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "34c0c7664d9b8b660efcee5dfb225bec4b4e26c4bb429e041b27a4402b92574e", - "proof": "1464d2f266688e3c4986970f5f0375735e835cd67ecd07e85d5558eeaa8a1d2aa02bb5c3f240bab87ce859d28cda9142127c9e8a01f6df5a713a2acd5a12af3c4ce9dde1d4069cb36f19ab6be043ad65bcf41483261edd79cd7fadeaa0786f6880834d1e5ef74296328e1fc66f438af649ba539c5f7830a61178e5696b55200734eb8b0cb6432731977229c7932ab170d5fba3ea305f1ef92403bb8b7824d105150a46e5da3c1490f8d9e533b74111f70ef37ba6625a902069bd0dabd091190ee1c3b3d4b249e3e7d375d74ff0ac7669e273e8eed1828408ed6baa1348a246027e15caa8ebcffcdcaa832a1db7c02da6e80e00e3df5a7de5174233b8d90db11df676701d84834247e7537e863104a2f22479ef7d684446c282ba0316a6999762ea337aa6b9b9f27a77fd3072e453c3304511c87cfbb79c372dbf8af847898808a88d709443939ff7174c22eb7fc09dbf2746f80c196a1dab6690200bcd5fd03c1849a24a7ebb1df37f0cc03b3694055a6f8a83da794c8b5c0686b33dde474423b00867717560344768130d18e4f199c57f87eab42b5778089cbb49910d113154d82292c66528523a633d3820efc9527729ddffcada276d63819bef097d49542ea23cb950c4cf032868b2e7724543fc1e447f5cb49f0f0b95c294d8d75fd93766806450e979f0fb1b0a050f4f01de8f1103e1add9cf39449e94b88b652e9f7e1f24f335ef66e9b94b4b8ebdae692652675f34938a93f681e6ca263db18e35d07d3e869ea4dcc0caa59d1d4015ab42d405d463a5506dbe4c5b37536d8d37607e069cae94760032db714b394fa9cb03d6a00aed602c3469b3861319b1a503dbbf491ae5e1cc0bc8fe362f50ff2097e8ac01f0364ca9402eeac0709f3536cad1c4037dc11582dc27535b3cdebfed7859e5ea058e1f709026996b4e16016a3ef16404" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "5af246946c4714d9ccf9b2249ab3dc084da36f4b0d5b421a60e41dfc44600a75", - "proof": "74889c3ce4bce9f499b54851add9017951998d499cb3850ccee5a557ad6a515876a63530b585ca9aba921013a334a98f15f395f721fde0e6b68248cd89fac343beac95db6039e5099194bafbf72c1bc70e6506eb3c12c709871e673c25a29c14ce5d14e8bc9d384473dc587fcc23ea7630db439c5bc7e0f71b7d4d6579c0792f9e7de615b14483a946c4621e9917e78b396e8d13e55d53a3644088f02946fc073bdc03017a850e15721f4b75dcbd7449e486d90e4133f00cdc837014d46fbf04502ea4a5289095d304fbc0344d339ffc057da995b110fdd53b8b660f002c1404cea3ef5143d654e700855dac6220d55b4011db1704a3a33d7651498cc122ae749a9c475e8bd74d88faf7ab1edcb08dcddc25e7e356671c08af523a5ed631d53f3c48481dd898d6b0f2673aacb3dd012190860a66ca850f7a5e332afa67ecce77d475a2364d98749c7f2421eb50f01f7fb9e78fbb6441678aab57257b3821a706d0b361b8a19703baecbbcad4cbd2cbb57935e48c0bd8606749972a3e3e64ac2ae8c5ec236ef655ede8dc31840f0d240b412d2f5602bfb73422579cbebefe9d5eb02ebb2e2668d5b481cb40c64357c116ce9c8da8f7016878c44764f92e9a63291c808fe885a1ccb77a97cc6857343e0283e3ca465065abbe6f1e0bf0a0c39d4adcc3974404aff3de9723e1997455b37745d0ddb7053bf66656abe7670761a92a4453adaa7ae934b79b5ddfc13a56d963c0409aa4f8964a274a1dd0ffd1920a76c46593d35edb2faaa292c649b679d7e29004e7ba4e61c0e1dd4821df8fd1eb32fc179bbe63bf74b48e30cdf9ca20b07402715d6dc0b289c83805dac78a55b40b9011b9a88364240a276d78c5bfe55c430e715179ad8420173580de90b087c508a47ae4bb20f47481c06f0b11e5f6ed9927296b2030469e1d35c6bf7c30683a0e" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "66fa4bb270ab40b59def0871c938239d0b0a0e370f81301bb37436c016f20542", - "proof": "8e2d49f9516317c1cf2d9cd7a7077677d0763c2e77e9371956285d235839f9262e34e884af1323fa6f698f90dfd7f0e3714ddb35bfce6392f8fbea84e244e15c429487b2bb18fa42faa6673115b55cd5cc1f0e57de2e1e2964c86aadcd387805422b65e995bbceea812f6a181ea1cd5f375530e133bab070b05d175e71dfc478439599241934f91a8578261fd1bddb942cd4ec8fa08168ac6563402c3e432f00159ae4e2d84c1512aa86148499855d2fd8a3e216951ac2d1d18c7c2064dec406fbf6c72126fdfef168cc022e104a738ab82fef20a0786f9fbcc6b1b4dfc2b10280b06a29898c801259a22c62186742c44193e92e05f63d3c652acaeca4469a2eb8210f56d020ab2735ed1066ab988e906acd3e4a69ae438f7ff7aec6f85b48287a709f9a48a2390ebb43219ec6e2cc6f2f152b7aa4e2d60c4d446847bb25583deec4f6c3b719f4d5ae84d2733dafdb8516d6a4f27941dfab8388443a3e022a27f697e6ab0b4f0f57d9262211e063716213f27c4a0fea63d6cf65f961d93655368e9bd9c3be3be3a86a26e56615c2a0c6091d8f7134932beb63fd0b30fe4a7e3a32b26dc9838db7fe83c52a647121dadf7c96af97d5a0e5fd9e224ac0090fa569305ba50b0371e8f838eb605e89ecbb502a8d0956d7fed01b532797f8ef6e280310dd2b0ee113a7f167095b757dc884d424209439753289420924b3b323181e4bdeb6c76bc301edcc3f42a48c354d731069a2901e0bee38eff6137951e008fa024a93d9fb22596d77240fb4015cd6bb1009dbebb8249b7d0e32506ebd04eace0c625ca8d0b0b86ecd69e910aafd4c9c22101afb3a8f786d4d6da5535066f0772544dc3ab7f0cc5cd1646ac363f5ecdc12afb8b12f65d0253fe880164cb375f208855d8364be258e1b119cbeaf737e29f3fc930d383c4c4f9484a6b07d97efc400" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "68581436d456cdceb9907cc66d45942453dbaa6961e027062ffeb01c3e01247b", - "proof": "787d683e24183c679f72e47192ed353db43dea3c6570fe198077f33f72ec207b56e280aa1ed025328df1132408d6fe5003ac7a915617f795c238b3c1a771c51748df05c61b116ad41b6592236b0f6ed4bbc042f2459951b316e01451ade41e2370e16a3f5a7bd3c2f001849206c900a024de4725e268e32f5fefe58e1bd192197bfd4303738224df4027270add5ff1e94f16a704ca9dd94da946f0350eebf200fe5c9489d4d4c4c999f9d14077475ccd224803c8a76c39eceb8d4e78707cf90fd1a2e335824967aca24d3cdf06e4b437d9e4c49ed5eb5011db6452f7e504310e926e5fbaccc2ca0777149b90d09e4acdffd5b1d49bac67929f966b6e6e9ef3656e69e96d4312ec901d724e31d75065c03f0e82c7ff1848f2bc2707a5ae71be2fb067dcfca76dafebfa15c9b92244790db26310517eba6834f9ec9db8cde2394e3273e0a2696ef05b434cfe7c32580f255047cb272007e64c4f7b9ed9bc6fed48f80683ec0ed81c8befa9078c079e4cf8ae9ebae6cf18636e2e93cc042cea934fd67b36aa5a52f1b3a64236ca1df53a1f8c532a3ee5152665346887368bc1c349d2ad6e2551a345975c2cdd186cfc845dc52b6fb310c9712b6afd42cf48f9dc618c4a3df2958859054abd8f7ecd7ba6c2c9d78ab138cdb2e7ef40f6b4164e5c3faae1ff507f8489f09fbbb0ebfae97ea84de47a63ec08b2f32c3af442cfb0e87406812fe63f50aade895b37fc4f8a9be7b6c3c27b501ca4abcb949cda9f67901982fd75f2b0393f143cf2785a536372a502740aef04fc6482b129dbbb31fa2149ae6575df9b1ca819bd7b9511d36c8e80fdb5a2b177f904cc5b6a6e520fd06e5086a93f65109fc408460d4acb87403d1e66e5b9bd0fb2d138638374e03785c90d1a71500b0d7e42aa3f886d6ca4f691ee413a8d70029c5019bcf45abaf8ad6e02" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "6e543b4516f3fc0d69f0b41eac8749f94adf229df714a192c3fc3cdce943cd00", - "proof": "ec8ac78a3ce2cd85aef458691471c2ad1fd431adac2ddb7c834d3267b0d66e6dd8ce3b733f5aa1dc940ddbaf18d5dc3d5e6b9accef3f9ab6b8b5b6a9f72b94492837abcf927d00e04008d2f84251bf700a31440e801b1118650fcb678f2896761a94bdce92c1b094c48443945f268a1177d73f03127280eda607f728f16d4c7a0a08e6c84826e2b74667b44359e9bec497d57699e38b4968c8b5d92f3ab9340f225bb8818906f0341129031df8787d1d62638dd491d20b9d5b1569e5dbd9b5099dd5b07997f22396cfdd13628514b926022296c3e3f09fff859a3abad2d19202c0768653c03e66a4f16f3ed8724955d1c52a3316073e275fd2eecd5c7328896960db0ccd1f65d0b665f3d5d45ef6acec7b42645e7092af0034972d814370ca2f086ffed548988ea8dbf3d7cd77c852b416664d4e4bea90da80d57ae1457fed27f29033419d039ae78809f7b9fbc202a45befd644fbbc164ef49766115941091364188daf4b02c406664a8a02c0f4d67f0afccc7eb175e82a8e78680ee71062620a46e63bcf03d353a3d099273a972a6ad8220bc251020ffd1067c9f3c0ef8001deb203c587478af35b5b4fb18c47592a312afebd7839dba5794c1b307401780b04dc9a8517258f00513ca9767ba0a82adb5e0456ab3eb58c8a371c125ff1b47d06880fb5b8e1ccd69cce5d1175a63a0a7f597640e5ec91c894c9e0b0b862176d60352963a9d0c172f1c60d34d6a814959564d77f545d99de98f52ac4e8685f039cdfa1d5ee74916406ddf884ea1a2493ba5d53e3d44e0ac6dfe8ce7478845771d0357f93910243677c40c8804836b980fc884dde89a48e4e9731a8b5c7e18f1bfbd217d19d1139f67d0f9b150ed15a45057e31a73d973b4c978c0572af7bd80e4d7dc2450770eaf1e63757b340c0f7850ea337a9fa6f8ee76317fb3ff8214d05" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "7049ee5d4ded7b9c8cd7557215afcd812d7173821a34c14a2ecc0adb1f827308", - "proof": "9a2d598974fac648f2ac6a51ebf465d611b4a9732a6cf3a831ce3ebb97fd9060b8c8f4138fd8590d6ff8174b1aecdd8c039c3eac45422126af0fc39cee22986606a9ebd08a68c227787b4cc7b4bf86d0fee9389ea695bdc3255d98b0f4ae1402f06712c42678e4146e99f570a82d48d7292e688f3650c14d2f1469ad304f5c2aff7140dd3e3558b63a011321a14d3e9fcae238c85e7a549898da95a1b17fd702aabd8d6f24888b1d027bef80d3a65926adf25b50e84919082f440bbedd71010dd5ddc8ab723a00c428f1a5e5a958ef74b90a4cdd2d6e1952410347d9aebbc909a6fe692eec329a64a8e20d1c08f33876773a9304839441b64bff2b18785e2b66da3bccba6eee89ba22d25c6abb54c0ffcf12009556d4390a8cf31fb7fca12b7c78f18ea6782e95fba02c2b3ca46aa50d64ecb0ac55e2026de96d120c4ed8561b30bad3ec1923f1fca3320ff33bf92ad37d297ba972871bbca649614346ffbd7764d2ab25648674f4e51aa4b70eb5d0eaa2b45fcc649fbbb5c77ff5feeb6979799219b09e64a2e88e85b40530f60d8736f6636c821d4597dd93e3e7ccf97204664e3875baa8cbad824d8ed302a3e69d6cd1cae015b093fe8e255fdb57eb6bc51ae8ce93da31c6e16f26d04e990952c3289e177bf810215731163505152cb1360524ce05df9f1acda218445791589e0efe538c9aac90a488ff41b67a62e2c5a07082d7f4c35b41f1f6c86bd92d4e8d8e3afb1052213ab3a891c3855e3a143e8721b88b6dfe0bf9c755f33abbfd34ba5c05105e5d3e98caf94b0bba806f72204a2a6a5aec422a64aa51ea0008ba677e5d0439dc3852808a43802bfd50e2d9e9b9577af86eda8b4b45835023ec865d03d31653807c1c726cb742a2a9ddc7fdda7c0790a80d1941040f0e7f6e354e6e95949a4d434f9cb9d63d36c00b5255996ec703" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "b29bb8709d262ed0a773e4f34541046530fe2991683e5637e63b11f557a1997e", - "proof": "c0fc0996c659710849e51d31253c480bcc11ca6bfc61926face340f0a797160d7e71510a4a9b9ecf2420edb0dbb701ef01aead45e8a9a665482a4f4716ab51011ea7193ec3d4abf46ba03fd7edefc5d6ee72a17ccccffb4c20333d8b94953f5d5427eef04fd2e4dcb52b246777a52756e22667666a86e31510e20a339d15de23e0f55eab8e9e569230a8a2252bfde985d211940e5ed49e5081660d9cf67eb00334704638eb9e6c2ab4111aaa6f6252f4f9a7ae5bb6465668bb33b19c4167780e8f9f5329865aa0d676b2a1e435f4d4577cdf6291ab84211eb1602a7247967e09249d6f68157c68ba73aeb5e8f64ea8cdf0359eb8a496e0b0e339a65eecef75692c73494026a3866f1a4cceaa5efb65cdd4d68f237400aee0ce7a6cb0c6d8e327b42cacaae3627bddd775cf7fbb110b4faf6a5737813aa973b549a8f7a5657077188cac0e99f5a8238ba2497666aff6fd72abebeaf5ef4f153bff31057a9b377918e9b80f78e14c05150900c06cbbba9fa895b766e0c84d781554622d6c1b3c189ec80184a7696d5e252f4deecaed952cd30de37dbc6752e60b250bbbb1ff1b15b88511fb996ab036d2d22dbd9a24afa2395ece9be6cd9cd5600c50e6b0d0d65dda14e3651fa12888dde184baa8bada38189f38b22061c94b8bb70f872dab8619c476f5e0cfa5caf3d720099a2fa7d5713f022e28dd4f35ebc86574b619cb876b8acdf36f423e1986d4be5ecd11da14dff86a21612a1cba5c3d74699590b4ae72402e9b5c93c6905678eab1a81c8ed8cef25ef38685c30e41732ce83468354c228edbb8ddc689fed729083d219e8e39ae2309c92b93656e9788e2018e5f754311359c1e03436b161e8bff55eb2001103b3981074366118d2ee45cc0eca37e6f090426979d0e39f2dcce232510891c2e77d7e0a48f27531438c4f49a3baec79e03" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "de50737770cf36d6412301899a9427ad6743f09f87fc96b57048a66bfc72343a", - "proof": "80404b1121406d4a585ed6384dbf7ecceab83eaba60eb0e5c6f6fd4e7024d534aaf91cd07374f8d844246f802d9875a05ddfce968b38d12a6c440af5ade67d11881cadfb7c18e32538a90581871f36747bfe6f60f51390e0639a104ad310d86d38fbb21c99b9adf6f30fd95e2a9f78a5d212a75f34c0738156f8415f2abd4f61111e90bb6e1d254e202c3b119769fbe1256d78003f47fecc8d61add9a7cfa70bbf185225dc36d0a4b00cb4761f14640eedba908acdf8ece87500349aad59bd0e444fdfa2f4796eb66a971de3c860fd696c0b2bd1b74a6f1b6960a00a71c062078820292e46f63f629d1012ec9bddc4c1d454ff0d3fdae5824ec6dcf5383c5c33e489c4186a8ddb78fb3c206947264e88d5e9ee83a634cb24571e3be35de92b66b6a4cdda62944d18edde86535c7d09833cc6c4268d4be06b5dc89fb3102ab8636693d1345aedab87635e5e2ec9a0f047cb6e04a8abe873a68647ef481c0a4e5eee04c38935a4938bbd612a28bd842702f33ecf53cc00aa11033a04b081eae52ddee9914ad95133f0610f866640d60c362799c0c2ad09fdf3e308fe6948c5433452640e8b52c2ecf0b8601e3dffeefe1d2e63dddcb216e03e532f19a5da28df06302151e28c8e2d1a83c8d874269e0e6fef2dfb92aeb84c260fc59a0135130676ce0e22505cc5e3380d46f345aca92a2d20f801113e2754aa01ade05085160c304642cf6dd25515488dbe844997676189f1ddb45cbdd1456b4ec2e674abf3886f88c5c19dc2fbb0f6b1a81c085e2ada1730f0d0ff407f81ea75f12c7b80547b0830bbabdf68597c453594855054dfa587fd2cbbf327ae5ea137e5b033eca3f803fc95e1f9a07126768649e726464add9928ce2764bbbf962c7a137d61cd91490cf28024b5c28fc5dab83a8fcab402a7218ee6b3734cc1d8b23a0d3ce64032400b" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "e6201e1a7909d19be521114429f15787ce5a4de17d87f677a771139000d95b27", - "proof": "d6a008156b6269acb102cb3d2440ed14106a67f1feac64939b6d34f9f0b0c94ee29970b3b9e48dcd7bf49612b78fafa75dbd9a3e2bf51a88dae4a45f68d0204500c1119857f1a4456058aaffd0f0d4d3e76df670398dbb59bae35774c88f1442aa27b9cb45ab39f051889b47bf0faeb67bd380a83cba943151b12b93c193047ec63f8e81afb05eaee84f047179ef298a55b262e2a5dca0b3fb3db09ca3445e0df7580a465c943d7a53363e3490fe72e8307d0796596d1b7e8c14b5a9f53a2600a9ec9640451281b28cf36c77519ccdb7545871e703a83a8ca6e02502662fa30ff48bb3a70b558a922ea2494a05f1e626cf0bb4e1cf71697ace14152f7732507282d4125bf3bb87148c014b8d659edaedcbae46bdcd9fd0c90a9c563c123e2454f6312b22ce6a042d73611de235b1cc36549c87c0e655f66a48ab67bb0e9f496986972a6d55b2f8cb0a2a64bbae3e0b98e46f7e3b80d272ca052dfa431cd980736cd303514c2e3e75da91952ebfebedec0802125daae6706346e96b52f6845f18b85fd1d8fa6969bd53ac8d379e83a893502232c50849e899105a5395b2089932ced591918e07c8029e8b5738e847c3c645c4d56ff85ef59a9e362a592eea8a267a522d3922407f91623bae68737efcf592a41f8dc0d94c8ab50651899cc3ed2a9c360c12b7f3e243f91f6bd4875227766bc84b4e826fdd8a71ecaf027c7b320be89abb6ebe1929063a935167ece73710762e5e9abcbbb8f9ed5851bfd8f2d84c82ca125893a2721a10a57637ad26090161c4b1cc8cccab4252706df2db3c4c7c50e17e3d900550f1ae82a0535ec5e9f55d106d2e642e4648377bf6be93729c4f32604608cf6a86118edcf3383f6ef4f303594f32031c18ab87fbd32a819527094ddb19b87af2e973fc05f9fb62d5ed43e063a0e0fd68ac2a585bf387eb2b740a" - }, - { - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - }, - "commitment": "f0395085c7c4689eb39ce1788899941589820b297ef9410131e3bddb7bba3a27", - "proof": "ca811f3e583bd0e37b5d4be57d55d858a6d4f59c0019263827a1acd395c3ca3c6099053905be69e94ccb82c5ffe038b62354db6b263a4025c309344b4325125bfe05b2842636c9e3866a12a1b9556967df351ee3c5489448dfc6bea9bf5871487ec0669098e7f2b23d5ec2d2acaae30302d383d29a985a5a3306dddb04823a2c02932f07f3318932b4207d93dbd0f7fa4af33c2cdedc9a5e4bef8d6772f66005986685486ef1b2462e93a248641cd2599d980301a29697ac38214e5a42504a03cb6fa6ae14fa535d0e12fe5f62b2dba8a07ba6757a1fcb27ec59fb46b2c1150cf2b10542d49188c3721f5368baa32f9ab1c23ab534b129ce5674351cae10ff0750f6c968dabbc1293f5fa5d7e8650ab7ed83bcb42cdb2c1ccba39fd70d6bd07d681290a1ed9368bd0e8bb6d1c62526048ea933ff530f43e286296fcfa4c27d0e48374f07109391b57426664b679802addea4570a660313815254a808bfe3587d90060b3ae743d6766ec204a20a071ca74f3147841113b8ba27bb296fde734e468654434b0f990107c7c63f481c856acdda291f8ab4ddabc659e27b2ed2bd4163aa964daa11fdb94c31d25dc4c8ebb9f34af2b33dc02f4ea6ff33207f8813e1324ad3dff56fdb2a73dafeaf92aea54b1597ff3b98fae2d52abcf1f04e97fd712fa4104a01088a2d6e253ca6b22aa4b10052144674c3b5c791e7931e66e4d99270041194f2f8b660182154fe251ecb9047348986dbdcb5c20cc03f590f2465a070768f17b91c2907a371e23f4dd8d74c53bfebbc042bb02d5e4b41b01595b3ec4e34c6d6c30e8958a47b7bd50d01a513cf51a8084734d98c8e5cd4a2471d66cd43316eb316b914511d125039ea826d440b972932629a97dc4a3f314ceca51eaf0bc585bb3a90959b07ef8f8f11b523d1308467916649c7fd8330ba0a5948f9f605" - }, - { - "features": { - "flags": { - "bits": 1 - }, - "maturity": 50 - }, - "commitment": "e043d8f8881190847bbe7d295b2295cc3adfc2e9f26355863698dd1ef9806e6c", - "proof": "602626b3e6bf5b40dd6c6b7cd9d7ad9e9a4989a26e1f32ee248f7da71dbe1b4a4cb63beb6c020efeb4d2e9e67fdf3522b5e2dd67a0ea616eafacc32424065d7ecaf1d478a53acda412ac0ee0dfb1c9563af03e92ba9136149aa6cf0c73d9531426f97f7fac6b82a68ed0dfcfc63d09091186129bc7de2fe2d1c8240b9a3ba705eaa394df865eef6560eb40295fef6d032c10a803c42a343bdead10157d8be40194794706090d55bb524caf905ac172c5e3bdfd9e5507e29bddbea5b715bb380ceb02f260a4532068a2fcc85f54f712657741f406a24851eb8d68608c24fabd035cadc2d023c1a5f806aac4372ae103ed839e72cc3496713749d9fc48ae920b67d02d9fbeee5826db565679e4c4b578e95cb80eade02bc3a794547a737d363b4720d151b3be2a7a7e74cea724502f7f82ed42ee7862c8f4aeb88a6cc030be812284d038d56dde80c52b381518b8c84fab8ca06a909b2cfb69c504343646bb86514a56d6cfa8f68a6c7e7fe0988d311632006dd7d009aae2718cbcb9608837cc68809c664078f7c85858dc4bae7fb6e54df8b179fc18ef7451d16d81ba6f907843a4eb8975e8c8ab79d372bc35544bcee8ac01d297d1c1de771bf0d72aefacf25722b51755ea4b136a0eb0979255fa228c597b43b6614ecd15d744df8ffc7fc8693444adbab11a8a2b9c5223f15dc15c81d1ed7740fd5c86229ea95c7ed7b8d922766aefef39016cb206e9db91e9dbf71349076e27bc27dec8cdef66f15e0690098e3b533e444dc61a6d41352e70cbba889588d929d77b4d3f3a59617a1c563c335e178670734af05fc8bd71307f17d8f666a3736a5feaa69af7fc70c243d9e45d42f74f906dbc00d1849d0d710e2fcc07bd15002d0416bafe24d24ddfb6b66804537723424f1847208a161afb59f0a2c1c6ae4d1a7ebda6a32f0eaf0ced41c907" - } - ], - "kernels": [ - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "0a5aa40175cdc146d2b09719d85f6b4830f9c62645831f451975ae3541f87c0e", - "excess_sig": { - "public_nonce": "845e1d72172bd92137fcdda8889ad8e6eb82f5939e0fa19edf8c74cdf5437758", - "signature": "69533ba9a897f5a90256ec98dfe38f76045db0518047114f71a479f9ffcb4502" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "3a1c40117e90452082ad303937f65445f8d81f45a75ac6ebf9b20b4cc420be4e", - "excess_sig": { - "public_nonce": "82a4b02da53aa3c46dc92d2cacdd2470738e698291759c440c09fe8ebf9fae35", - "signature": "76fa18e9d4e52bb0f817e2e7bd07c0a634e57bd2b2fc973ddb5980903f7d9804" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "6eafd3677601df609089eddd5e15dd3df580296762497b889559261ca9160747", - "excess_sig": { - "public_nonce": "26f1b29e55646918de76412d6f72cbdc045deb8a40c6919c6e1024ce95f7700b", - "signature": "b65f4df9da8337f6d35a94ff98380fecf2811e31f1070580bb7eeaf43e34eb0c" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "cead0b51a61fba3acd676391bedbc2059bc5b18170a9d4a96fb8c54c0fd71306", - "excess_sig": { - "public_nonce": "94baa0ac78f37da3581b0a31e1c17843798a297f74ff96b9246b674c63a06b05", - "signature": "8eb2ba1bf42a0ff9dcbea30bd43a272fc86509384f48c2569a462ec4ecdfed0d" - } - }, - { - "features": { - "bits": 0 - }, - "fee": 181, - "lock_height": 0, - "excess": "f6d66930cb58f341b7fde8a8ccc9abd71aad2100458299ea89630c5b8c5be436", - "excess_sig": { - "public_nonce": "04d3865501e4a493eabdd0fa84dd5ea8c5da5fdc0c59f6b77e730a4c12f1b215", - "signature": "c62c2644f3761a2cdefb61ea1db1fdfd1fa50bb625fa84e0f1059cdf650be20d" - } - }, - { - "features": { - "bits": 1 - }, - "fee": 0, - "lock_height": 0, - "excess": "8a6d4ad863ac3f8f07ecd1d31a0b1d5d9e3540887fdec6534c0824ee128e7b6d", - "excess_sig": { - "public_nonce": "f8b188e5fc26b35309277e4b811b4a290287518358708c0c88908175dfc32b77", - "signature": "805ad61d0f98f8d978aa8cbf55b9af6371b0fa9795686729b1e1154394caac0b" - } - } - ] - } - } - ], - "spending_keys": [ - [ - { - "key": "b0bc8068b51f1cbf82a22e8c5299442b4b5b0566712511373cbec715e2a8da04", - "value": 10000100, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 1 - } - } - ], - [ - { - "key": "e3746cd3107293a70d787e67f6208d3ff02f016d382a03b3e4b39d5d598c1c01", - "value": 5000050, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "087b65c95c89329819fc21aa97f750225123a550450facc57418a19a8b06740d", - "value": 4999869, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "7227a8d8d14b86ac8a98bf4084d5bec46fa681d557c7a24cdc3e61563881330d", - "value": 9990281, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 2 - } - }, - { - "key": "a9205e2c9000f26865322d157f06089a234e1b3807d9b41b6d01442155c5b805", - "value": 2500025, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "4fb4d66aeec22b34888de844884818e3d5066d53b6554d34e56afc5d75244c02", - "value": 2499844, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "4103908accdb3d1db3d04d7c6c770d5c48ac318785a53bddfc06c3feaeb06a08", - "value": 2499934, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "7e05f69b4e2f4101dc44687791550e7df787678b9135e7fc4979a9fa083def0d", - "value": 2499754, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "e392c8c8f9c9ff0ebd280eacc5e81029191ef3fee5357433cd704bced00e1a00", - "value": 4995140, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "1899f78556d2e0d3a0f742ee20701d5d746cb457549b2e112dbfa7aecb721f07", - "value": 4994960, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "978d60473cd41beacdd95db532c2b7200c2ae86185f2ae6e6cd0e60bb53b9507", - "value": 1250012, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "1930367e34ec7e18d758f8223f6b064948dbce48934165d6fa5e1410c66b3c05", - "value": 1249832, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "3adf9c029b06d0a41b383a52b31d69ceb88c54d054ff6cb8bd2de18da51cc708", - "value": 1249922, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "bfcce60ff0a7a51c8779a05a04ad976e9764e2eb583ff64493d430510dbe030b", - "value": 1249741, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "a5e269b2ef5827a5c90830bbbc1a195337a26b2f3e4a254d20b8fd4bacd0b00e", - "value": 1249967, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "7ddb65c48111602fdf7a97b91e023b8d3009d780c47e8634dba42156e700e608", - "value": 1249786, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "57b07681c4f3eda8b1b72d164542575b570b47bebefc21104011b481c69e0b0b", - "value": 1249877, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "d9c057a317439be0cf065066503ae44cc9a859196bc8c24616ccdbc4dee62b0f", - "value": 1249696, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "035a03bd70c5458b053003a89ace49b7fae107e8bbd39e543dcfbb1dcca6160a", - "value": 9980472, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 3 - } - }, - { - "key": "3ead8eea7941c8d9f024071617fcc49c3513f13f822f9c1b5fd47c7c2f470106", - "value": 2497570, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "a776faf6d8190729bca41cac9b5163d8fd46d3a85c03f55ea3bbfaae59c38904", - "value": 2497389, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "edb0ac227a5cfb83bda1a6e8c4aa2b2a73692bd3c7eb8f7f5b92fcde983bba05", - "value": 2497480, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "f2399d65a21f06e90df12faec87f83c71bf44eebc15186e980db876e46438d04", - "value": 2497299, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "b890864c7b52cfe71fc039dbba17fd833c1878cec39692955cd93f96a9a6c10f", - "value": 625006, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "b53676847a3dfcb83c0dac191854f09da0a33a454f46966c795ff2be89048f07", - "value": 624825, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "72d8d21b229819fb3e3332105a30d6c1a91ae49c84b1347b8ab5f262b30d4d04", - "value": 624916, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "4eb89eb97faa91b4da510a1fdf3be28eb80b53f939a45f910978ecf2ee84ce02", - "value": 624735, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "3a2428f40a0270ec2cf9c2ecdeda84697fb251d476cd2eead1c6d961d9d5f301", - "value": 624961, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "2ec13915a03a97e0b9fda4efc839700efec28728d07f0c57389501e45f57c008", - "value": 624780, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "e3f3e28d21dd9343ceb8157c1ed52872b7fe8154ba114dc45fbe544ce979610b", - "value": 4990236, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "65e87c7454a9a027514086beffb9cc8faed09c2cb6b514c509489f3ee1bf940b", - "value": 4990055, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "e72d65303719223d145040f6f8289fb5bf8dfccddb5032b591eb71a281691e0e", - "value": 1248785, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "08766331efcc8d1220ad081fa743745f406022e5aa3319d3afc7a0a9d22bb101", - "value": 1248604, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "ff7d0d1f0b2ec0e13b47a757ebdef2d03110d8851f391aa74b9e3bb3c2c64804", - "value": 1248694, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "26889b78f6bc856770f8f6a5572bd5965e8ba9965493a9ec8ff13d568860760c", - "value": 1248514, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "067ddf811cc5e8a6c805468dd98e911d7c360ac7000497d95688ebaaeb349a0d", - "value": 1248740, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "05c645c75562a6f53473845e7791abba3da5d6dbaa5eb638f6c902a602f3be07", - "value": 1248559, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "b6bd45ade6a64d1d5bdfd4f7eaedc344fc243e3ff557f74fedfda411ca952f09", - "value": 1248649, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "b23e8a994d23f8f86f23f9c5b7edbbff8a5b3a5ff6f6e99e598c2f615125870f", - "value": 1248469, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "ca776b6ac1bf81590df09df4844f209ca65f33af6f0874f019fbf29089a3d506", - "value": 9971034, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 4 - } - }, - { - "key": "5246990a86976a0e1d47feed1a508ca59f07a3bea747761673743c66f204420d", - "value": 2495118, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "c84d9a529c7ebb950a43251a947ba9efe9d7c6970da2a847824ac9653b52280d", - "value": 2494937, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "18eb04bcc5ab9a7b7555a153a33c3adda132771212bdac1a1ff3fc01789dcd0a", - "value": 2495027, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "98e53533092932962167b877ce0c65cba3e9ed45c61b7c8c704e011da362fd00", - "value": 2494847, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "73776fa15dc3e8a55e2291b92f3223e5e3f8a0042ba590cf89991ec872675504", - "value": 624392, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "1df4d59fbd63d03b5612c805a3a4073a899e110cb09e40fe23f8218d63a3b40c", - "value": 624212, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "54a0c7288ccc162852fc6cc563bec6b8b8a9ba6f8e6630cc90cb6c06dd2f1101", - "value": 624302, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "027bb8781e2289cf648bd9dae544b34f01757c16e3ba39c4298896cf59ed5508", - "value": 624121, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "297d1c523d031ec1ff44af1f43b9d68ae51f5523cff5b2a5352a11b0e98ca401", - "value": 624347, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "05ec2ea57de98fc096642300095587532f4c159a3dff73d934897590ed14aa07", - "value": 624166, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "655da97495bdcd839e60e0eb99aa604908e9ae568de8c6c620deaa1b7504e007", - "value": 4985517, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "b9e4547bbb506f1d15f1950c7c80ef42a55d86e1979597f68437e776d8a99e05", - "value": 4985336, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "21bba80b67070293807a53c2f781b0a54d6ca74931101b7d02ced22922258905", - "value": 1247559, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "b84d7c58641b3968f38d1b44012e84facc14573d5509a491ac3f86689d310f06", - "value": 1247378, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "2750aa1921b2372c5b3f508505e1d24c5ff3605b869ba8c6739d5cac12dad50a", - "value": 1247468, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "5563b33f7bedcb908e2eb0f27aeb934668212d4fddfd87022355c526ca1ad909", - "value": 1247288, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "2b2bb82834ec9a3236eaafb550145cf9ddf1f6b1bf053da9106672e8b8b9440b", - "value": 1247513, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "1162d5043408e305f28c46048762cfc08b7e55c2860057025d34310112f8480a", - "value": 1247333, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "439012eca13f8a2d636a0dba2e9504a6a2defe533d35d8396d9becde0a92330d", - "value": 1247423, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "1aaabd0c5d0e6792a05988b29ccd1a35d83ffb441bfc50dbf97062cef5dc3505", - "value": 1247243, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "d0e28be7f5b7a860f9784122286988e992b0b9947e85d281713729c920a4ff0a", - "value": 9961064, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 5 - } - }, - { - "key": "abeaa604a50a4a24ff5cb8e8dd1fb701d183b0c4259a5e82d4bc130a0c04700f", - "value": 2492758, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "023f151560589bc6baf11cf84c4b9934d6114f68f477b255ceead30266caf30b", - "value": 2492578, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "dea87df985a1cc846f275f0e369c27a1cfa0d0106b33590ca58cfe6500ed6f01", - "value": 2492668, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "0de721c2b3cf524b9c5fd980d59eb2d23c646941515fc83d2ded91b4ee269f08", - "value": 2492487, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "8a38caed795995b98f70126eae2f2357b29fb9c1ac739bb171f4578a30bc6708", - "value": 623779, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "f901b3ab57255363e4dee80595f394f230e7fd1b4922c4c3e8ac4ac9ad28160b", - "value": 623599, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "bab40ada2738168f19f54506a79ad26004b2c1a37d58bdd9e9fce4a21708610f", - "value": 623689, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "0dbdb525768f3d321f4060b5de8047815fccaa26be424a3efe9cbc6109649b08", - "value": 623508, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "5ee9391234a97d2fb7c46d66bb23634bd74bbace82f5b6625631be5df1b22609", - "value": 623734, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "6ced749dc4edaa0289628a2207bfe4d158d8f776d544257a864816617481460f", - "value": 623553, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "83cc4554bb42092e29cb87c5cdcade7a7d64948b3312ba1be1cec9b96748180f", - "value": 4980532, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "f1e23ca675044cf5578d06426641a5e1792208c8b0aaa638803dcf0f1b3a8b06", - "value": 4980351, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "4952b1dbe8099600edaae947439372283edc9e29d5765409a87c994f6b960f01", - "value": 1246379, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "1f843152d35a2f43778550fdafe965fd6abf5fee2a509557a9351c6a60d5570e", - "value": 1246198, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "f16d4a74c3988d9cf5a171eaf53d77c5e3f73c7269c0dedb4d267fed056ff607", - "value": 1246289, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "babb054a4e7bf82b2728f9a7e40cc840827c5c2fd9dfc4e477d1d8c53bc6b905", - "value": 1246108, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "68f4c8d1dde03dea1de24b1fc630d0c7a10bb145a58c6872e17ef20257341104", - "value": 1246334, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "40159c4c887096f0c11424ea8c04ede2c1adf37575fecdc601cb855671eb150e", - "value": 1246153, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "342962de9efda18568a357f20768160aee80a6b884eeb96899bdb60151883108", - "value": 1246243, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "eb3813c45a901c4380cea9f7e0e2a10058b180564f1f01f2b2aa8fbe8c7d8405", - "value": 1246063, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "662f3ff8f752ed2e08476e6105a93d2ee5341f4b826a71d85719eec57f225b05", - "value": 9951104, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 6 - } - }, - { - "key": "d4fc6dd5fa7dcb57090413dfd5a136a090101def08a71ec0adb69b3e41893d03", - "value": 2490266, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "d8f13675d661a8ca77e6fcf4058a3fdea48f096c66b210904b2c96a38b63de08", - "value": 2490085, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "0692e36d7eb78dd345c3c820404960abb0e389f72cfdb69cdb30dc8b74bf7301", - "value": 2490175, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "87429420efc93a63ca2e3eadfd2d4dcab4bc531342a306afcd435dd9d560eb0e", - "value": 2489995, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "8d7dfea4de8808152a22e3aa077d542217350cfd38717cae4df98425c5eeca05", - "value": 623189, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "1734cca8ddab18a403bfc4be8f1b2cb5649a3c8cfd9b3295da8b3dd7f30b600d", - "value": 623009, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "607ea3ab8485987cf751760c545a6b556bb155a5038ddf0d943eb2d9c81e210c", - "value": 623099, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "411c519ade9abeaeb32b699e7aaebaa1a934204f7f43bc8d13990725b2d5d70b", - "value": 622918, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "6fa1f10aae3d68a7b09a85a45935bbfed78623775c6db2a3f3f760cd28c57e0a", - "value": 623144, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "c94bbf757194417c054eac82b7b1b4df375b3bc79a7be59b21e07fa448d5b701", - "value": 622964, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "6a7ff52aec05949d65f0803d1114dae6c7ec248c77ed9a18dc3664aad94a5006", - "value": 4975552, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "341e8d143c124c3fdd26219dcad6ed1aecc30c2601f16808dea46179d7859704", - "value": 4975371, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "65bf3866177612e58552a32e5584bf8918216e30232d2946d44b27f09027f605", - "value": 1245133, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "eb47960b4d2ffb54f5267f3fcb14c7c9f096751152af9fd3fa2cc8147fda0c04", - "value": 1244952, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "40e61f97fae867fbe66afe9a6560af1595605b046f88440718249f323316f300", - "value": 1245042, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "e515a94dfc580197ea81f675eb0520c6a45eb1a72381450e09b27430268cde00", - "value": 1244862, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "f3158e5752aac2866b46be01ee26c61d27c955e722062ecc8d6fd93b95727a07", - "value": 1245087, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "9861584ad47a1c1284155e5d121ddb835abb7398704ea9971d9d1a29a1cf870e", - "value": 1244907, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "77381859dc2c60d4966cd5548ef1b34daeb212a966b04fadcde9205983b55603", - "value": 1244997, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "06446b21510e8d11541cab5e697dd85e01838eca7ebd812b6bec317488fda309", - "value": 1244817, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "d46df4e969e7f48f32e011828827ac086e41520f25cd0931320726180c58ec01", - "value": 9941154, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 7 - } - }, - { - "key": "936c1ef60020959a54b054f5965f5936ac6e16436b407d83bfa168b56d199a01", - "value": 2487776, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "762405ef8f36d3080ab0d0dcb2995ec70a22236ef949e1e4e4616d3bf075850a", - "value": 2487595, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "f9f41eeff6c1e61f3eb3c64541122bc0c26ad0f1c8728b2a7c7d957bbee81c0e", - "value": 2487685, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "e00b2053beb40633666c1f1037742d07fa6358f7a00a2a2db64ca5d21da0350a", - "value": 2487505, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "7abf5adc31d1dd9ed0a6b10507ab5a71ac2f12cb55f31a6c4a7c59ecd0007207", - "value": 622566, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "4e374b74466f865bf7a79ad777da1ec9268fe430d76b52b3050cf6ab515d7108", - "value": 622386, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "9ce3a8ff602dd2c26d025ce25732ef216543b8dd1fc72581312c81f6547c6206", - "value": 622476, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "b205a1e053506980c737363e311456791c56129fa7d9689ad63e5bd1b2e95402", - "value": 622295, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "a44282fcd739e852136453b4d16b7cb6a1d1b9120769b5233824c287ff443a0a", - "value": 622521, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "7761f570c788d97af57cf4cf84bbeda95e5094319fd538fad0920692667c3908", - "value": 622340, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "24c4ca5ab0a95fa632108cc77510270dbf35db519226d6d1d35b19c576179302", - "value": 4970577, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "45e7bf789c75bfb6b24f4df8ed85d2419fd67cb026d962bb2af34fa24bb33e03", - "value": 4970396, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "aacee969f71859fd8dcc189e0ffcd6dff25e4c51109c7696fd9aedbfa8fb460f", - "value": 1243888, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "f69e1b6c70f05e382cbac377db7785c98dadf6248485e20a743c99b759cfed0f", - "value": 1243707, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "5099a410cbb3fbde888d84de6ee6cfa5c344b3ba492254cff61612f21cbaf904", - "value": 1243797, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "badc550661c1d07e0d5909f9417c66715fed17dddd28b977fa6d3aa53f02e70f", - "value": 1243617, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "366af02556d9ece29a906aa674d1a8854f3a6150a2b6772e4d2d043b72d66f00", - "value": 1243842, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "edf5716fc5cd40493c0daf19a69a52f46de726ad0e475a3d5aa148b359f43405", - "value": 1243662, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "7bd53f71b71449fb3850106e2c5749d72c72953eddc019a5c6267bf0a2c0e10a", - "value": 1243752, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "1eb4653237f80ff35132e11b9b3fb0649c07ebbe7a18c09216580fb904ea7803", - "value": 1243572, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "62020d1b252d82ea8ffe75e69076b12ad1c0123d79303bcbf83e92631d28160b", - "value": 9931214, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 8 - } - }, - { - "key": "bfcce96e4fdb8400123612e82df5d8833b967c063f1363d6d46216e6b7662d0f", - "value": 2485288, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "400d36d3f48e6039f2aa885eb2c40f8f15c2e0e9b548a6caee5ef1ce8ac8b602", - "value": 2485108, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "d88ba19a77259d05a1d496931a1e3675eeb9d732ca3e5a995579c7060884e90f", - "value": 2485198, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "af378f9d03dfc1b6b3523373dcd16d8faa3a76a6cd40e3b8d5b5ac82cf7b4a0e", - "value": 2485017, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "16dcbca7a7038fed54a5be952c696b6f274787d888b94cb464c43b4b316dae06", - "value": 621944, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "9c7c9682303b9c01c80ab2d0ac0af4215b597e6f0372eb3b11d48ddf2435610e", - "value": 621763, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "21b9706d6580e1a0be73ef98e14bc03fe2488ca845b12414c471136c22618c00", - "value": 621853, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "a8926db8b997e474be6ae2a64854c047c6bcaa28b591590e0f223f8efb8b0905", - "value": 621673, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "b926e15ed88971dfc86f9630c6597579a62b8954b06c81e7712bda8b0d77e102", - "value": 621898, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "1943a853123f2588aa133a55e4b7e6125d94cd486bb83f030d484dcc07fe7308", - "value": 621718, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "06bf5a0543266cd8975495d0f4ee4d9102d82f4b709ae053c5bbb2907509f90a", - "value": 4965607, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "f9b9fccf0f756cb7bd4534a68458a377fdc0c173028dfa25ad23dbdefca0fa0c", - "value": 4965426, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "cfaed525b7077881d96cb3a06186f3d3fbc5c457e1bd86d1fbc1ba7fcfd9300f", - "value": 1242644, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "c66d4af74040bb2d169a6af86e6f73cf5b1de304978d8571c8abec424d418e09", - "value": 1242463, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "dd0832824838c6982f00fa1a5f9cf9536b67f62f0d6e3b6957647a777e234f09", - "value": 1242554, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "47049b046fafef56adbb27301cd3e5774ad9ac186a88e8c3cfd0d0d4b525c50f", - "value": 1242373, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "026154352f95d5d440b6386bba90de722e08eb11960bb3c7accd0018786f9a01", - "value": 1242599, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "db883f98ba7e17da6fef33bb9ac4ae63c646222e21262fa057d61878781ea30b", - "value": 1242418, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "8a701e663bb20ccd3ade277e4bebfe8615ba990b1777061fcd6d3d9cf58e7d00", - "value": 1242508, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "a225f66620c9c6d30ea0907d0844049ed9c6f6a18c608e52dc33b6d10e04e90b", - "value": 1242328, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "d490127de1f1568843980a665ba86fe416f01092caa64fd0bec037dc5e98dd0d", - "value": 9921284, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 9 - } - }, - { - "key": "84b6f9a0dd779eb9e2a471795be422d358ad82072ad9b6e9d7cbf0f335301f0e", - "value": 2482803, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "dcaf6c35e013dfe1361dd25cfb1ab9922fac9164178b209efb36dc837da72509", - "value": 2482623, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "e2822ea40e5abe1db7e1c24e3dac7ee8571ee8181f881059a7666f75cd969407", - "value": 2482713, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "a55c72ab033f9233ea636fa479173fffbe289e7367d17fb6377ec6869d02e808", - "value": 2482532, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "c2d6273d33bc94d5bfdbf70305e6eacc225bc132298cdc75452019290b53be0e", - "value": 621322, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "6cb11d7550df3e62ff9382189deb2a5340e696bfa757ef6a4455ae998e8f950f", - "value": 621141, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "e0c4aaa99994f4f9bedd3c8d1eaa80d5fd0efecf21e081cff5860b3f6773ef07", - "value": 621231, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "ee984d844349a17015ba4fafbdb1d381a6906bc5c5a0024ed2b058821244f20e", - "value": 621051, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "a4f18e4e4f28b350a2718f4ad81509c0d3e121606085e6fa619e883e4402a402", - "value": 621277, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "078a669af4bfb645617962b5c2670a19e71bb136614029f7ba4a5aaae2701f0d", - "value": 621096, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "58ce09da6f1fce9e83eaa5ce25ef96de1a52e7fc3a6bc251ecdfba389921330f", - "value": 4960642, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "337cfdc47221ae98b90d757c82aa1b7a0cf4d224e4791d4c5e9dcb88f094fd07", - "value": 4960461, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "670707bdcff0e0404b66d59fd993c2f45c3c2ecef68180c15bdae5ed1be49c0e", - "value": 1241401, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "b93f6a7016458db0134778964455794d6a55896ffe9519720963a7e2fc205401", - "value": 1241221, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "fe642beacb55063126f18bc74da5d6114026dcdcbaf18a29b23a3589057c9805", - "value": 1241311, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "7c760fb109178dcc113830b123b7289067efc1495176055abd04f2064133e603", - "value": 1241131, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "00a0b16e58d31975909179b96609abc5d285756241651793ae3cdfe6059cac01", - "value": 1241356, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "f2075b1342b5fcc342dc2849c19a283138020524d3b15c2dd5136426071eaa06", - "value": 1241176, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "30f02f4e3cfe5139cbfb81516e87c8a09470e916effb2547d063a95b006c430b", - "value": 1241266, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "3b46f4870ce473e2b2ec6b1e6bfee7e3bd13286649680a1d9761632dc06c620c", - "value": 1241085, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "9bc54e20e97c07ff05b04729b5b5272c0bb216e2c6e8d1861d5163148def2904", - "value": 9911364, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 10 - } - }, - { - "key": "e7200857e62e156e35ee094cb5346f5e78e154bd308c39f7dc8c05cf9747070d", - "value": 2480321, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "01d751ada616e750fe3dd484eef8165723cd96a1cabe369e488a54e49622f50d", - "value": 2480140, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "6811575c99d4f6699035bb132d09ecc3147ff72f4161569c66fc4a2cf0b5630c", - "value": 2480230, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "1cfad7beaa60caa099d71868328f5eac9637ca41d7b81cfe6012c00781be240d", - "value": 2480050, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "b6245c53bdb152096fdd7cd4a7dd2fcd9a3ca36856d421065e3d4558167b100c", - "value": 620700, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "644b09f94d7c8cbce5c2d129720d454e89776c63d52c0621e445925eb1f1d60b", - "value": 620520, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "cdd09a40cfd3f086121ffda98cae6c96e5772be32ee1a1be7f4cf01ad3da580c", - "value": 620610, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "314cc65d595dd3d90eca48628343def00c2aec8d8a0fd9eb55fa5dbdb0a2410b", - "value": 620430, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "d28a0a224b8cfeefb99940882f3ee1d6f5e9108ec1b48f92ad7c9ba48d9b990d", - "value": 620655, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "992f0e20216510950dfe2cb6976625df8485495a79ecbf98e283057c4c636e03", - "value": 620475, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "62b48a910f5a2539a8880c7365028343aa39fa5c2067497117bd9e17b8e5c408", - "value": 4955682, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "f4d187f7c08c4cf098bece4f1a66cd3f21eff05523c791bdbd07ca46ae12f402", - "value": 4955501, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "deda48c62de6b9767cdfa78599a8e635be95e9915f02f2f5de93168eeb2aa30b", - "value": 1240160, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "f300994685db657a78984c14cb7e30015c94e6277d1ed09d219979f75959f600", - "value": 1239980, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "ff1e6db38acd53318de0ce62c044b0f2e1f8daf08fdb3927e92043d3dd0e860f", - "value": 1240070, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "da0b72472cf8a97c7b8454accd9dc076a1a0440dc77ee859ab36437a5c2aa50a", - "value": 1239889, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "28928c0751f57fa91dd77c10dbbb8cd3d9495a864f9fc921f66204f40ad4970d", - "value": 1240115, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "2778fb5117d0497024b495d76f4c5874eafdd502f581d61af5329c9b719ea009", - "value": 1239934, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "077e759bd0573d5c2cf9bb6bbd6d3c1eb6d54bcf25297be0846b445ebb0a6502", - "value": 1240025, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "16215a640cc9b0ebc36aee8931af134dbdb7edaa683c4196fbb8c55993cbe10f", - "value": 1239844, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "bf2a4952f4e19554001d167ad9368bf9255cbea5aed334a4b054f87a6be9030d", - "value": 9901453, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 11 - } - }, - { - "key": "80f7b435cf41045bad1256044380add99dddf575911d59498633c713be63f50d", - "value": 2477841, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "60f466c48cb3bcacd6842ceb1aa0b5af94f052862c6a2f39fbbf0d4695e5d708", - "value": 2477660, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "e081047ee7a42ae3f11bed3fe5ff3218537ebf25a1012d01e18fe8851e40f607", - "value": 2477750, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "83cee610a0705626b5f7fa7f45fecc568e2063040476e5378a38d3ffc37b2d09", - "value": 2477570, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "1280b7f1912c972a5c2158132b9be121d2d9150f2ef9ce4a0a0e6b7f9ae08203", - "value": 620080, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "60a6ee3b43a19b45a18e15f3722684e1141e49dcd9b7ec37f189f8f1455b5502", - "value": 619899, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "39b834fb83bce68156b23858152ccf2c179abc75ba3edb177b0c5f15616bb605", - "value": 619990, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "27432c67e70696d094ee37f3a813eabc401c9669657140298739adf9d0ccac01", - "value": 619809, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "e739ee16ecb219f17cb1447b29a8449c4a601df3eaf73ba6a70fc2a02953870a", - "value": 620035, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "7804d3887f63f0ed8832f90f81331c3c90e6be5b110d699c525e0ed49241eb07", - "value": 619854, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "51025af1f29401f7dc0587a5cda030ea03b0558c5e6f49dbb257dc01b1b44800", - "value": 4950726, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "c6f081ff4089610fe1b231dcc8b4cf8da518ff27954c83eba7c95f192baa8804", - "value": 4950546, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "e405986f2f6a67d00edb699d4e2d558868bd56e0dbfd7737f916504115c88c01", - "value": 1238920, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "4f9b2af87165ccc3aefee6561413fa0f1d7185ac66dd42928a1eb07d49855d0e", - "value": 1238740, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "0fc2244e015f30abb8e0f262c7b2f19be7d41cb9aeb3f3849c281c1805579903", - "value": 1238830, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "e7caf2d860afde9c67a36e14748701ce442daf035cf5bcbd766265f46db50e0e", - "value": 1238649, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "956f46d8fe08c9fa9babdbd3bf5f0a2173c32e6ef270c38c86a1862d68682209", - "value": 1238875, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "addccf048e72b9f65307af1bdb3a5bd727adf07ac0b2dd68418426dd01646205", - "value": 1238694, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "a9d7080aefdddd4ebdf243da3b6493f8355aade1e17d20bcae63aec9a24ec702", - "value": 1238785, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "3bfe5eafeb4bf514b275cf47474d40ecb971a34d70ff8aa0b77466cc202aff0f", - "value": 1238604, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "b4c893d29093d699f18e92966b20447a7e7934f00d3784673c0b03096da8ca01", - "value": 9891553, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 12 - } - }, - { - "key": "8a8c834516fdaf80364539ec0dc100bd0a6d7b1dd8f1a9c288efb201217d4702", - "value": 2475363, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "6f6069a606576b010742f67d8f0e5eb8461957db37d5f147abf02768ffddf603", - "value": 2475182, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "ee100e26d33632173c6052a57d8736ab5f54c87fcf8be2b1b08083359d3fc600", - "value": 2475273, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "0c571931beb8655d65dc57f8ebcc2537c3c7abb5c366456dac3d0aa0a715cd00", - "value": 2475092, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "7b37c48c30fde6adfc985ef8f74656109f410cd50c1a3d0df2287a5d4d5de503", - "value": 619460, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "b8bf53ac725e4289f5cf60cd327fc9b3da788169f39fa8f718294decd7e46d00", - "value": 619279, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "0317180e90e9c4ba8302d9e2aa01243d15c19588e7620b92a33e5e07bf08e00d", - "value": 619370, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "43e3b024aa73ca927007ebd4038d0916dec1522bf81f3eaed816f2f14b1b2a0d", - "value": 619189, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "163a586465a56cc7f7fb55689be153b5c859f98bf53c9fd218195c66dd81d60d", - "value": 619415, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "397def995f6017c6f813b027540d32d74f0361abdb4f696fca669e778d5c120d", - "value": 619234, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "1fb1cecdbb0735b5514631dbcaa2e1d8adda8be7b546bf2fa3d3b9c4a8d2250d", - "value": 4945776, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "6d213971ebabaeb029698f0a34286cf5aadea0e6403c6dc2b6b4d2e0a163850a", - "value": 4945596, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "be0ecbbaaad41d8c6b07e89ff05a2a71b544fd362d8c7167f2681d05f7c86507", - "value": 1237681, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "2dea63d9051d1198f2c22a9b6f23e01846cf0cffd46644b1deaf115011dea802", - "value": 1237501, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "7e44af149c13fc0b8ffdd99b2410466abc4179950896f7e7600642f8b7acbf08", - "value": 1237591, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "a298365312dc71d60aac8569ab1b8c53261aecce51bf2c5d155828ddbec52006", - "value": 1237410, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "3befd1204989cc8eb92ff90d7dc8ebfa63d65238a4d119c5cacba7a4614d3102", - "value": 1237636, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "2f4095473d17f80b0d4a0c13ca5c597a34c581a8298492d5fe27999eda3c7a0e", - "value": 1237456, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "e172a42790caf39879b2d47b0a34ebfd0fe61b28522b34371af50605d09c6b03", - "value": 1237546, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "30bc7aeb73626c64faa1411450f3c491c3f4b81da180922ffa7448bdbf042506", - "value": 1237365, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "2ff1be02a7cb208ef5f9b56e200fcef534a61b3ac9bfe5b915f4d5332590b204", - "value": 9881662, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 13 - } - }, - { - "key": "efabfb9333fa526f7149aac29e41c046b1390b399efe74f2ac7f7df8c98dc608", - "value": 2472888, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "39856555403e09737f5060d20107c22302c4d0c004d642fe1de0feb57b015d0a", - "value": 2472707, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "7a0ce064bfafaa45fd39726ca31f694dae387f74ceeb01d1fb988b175676070f", - "value": 2472798, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "0cadc2b9f80f3fe2ed84ea5731442b50ae540642371860637082b2eb3ddae305", - "value": 2472617, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "c27054e3ee4b2c8e87d96929870ce60390c831685e4e36216626bc421932ad06", - "value": 618840, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "006d8d97e21c7644ba4b9f71dfd510c618a4974cb3d85e1ac9db684eb61fdb05", - "value": 618660, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "cf06ec7ba2e210a78f309a21c574f0b45827ed52cf4da939008c212ce6812b0a", - "value": 618750, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "50736836ca2cc7557faf3b7e35974272ad8dac5040ad5181b930fad8239fbd04", - "value": 618570, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "30340fb83038c2c155f00a9116501f47352e39c47ea96364d95682c05b68c801", - "value": 618795, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "46b6d4ce545fbfbc9f016553fb9cf59803c3b76576d2a1827a81f1f7e9142704", - "value": 618615, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "bad9506d7a4524811b346c6f99307bb6c2a7e287e0914b9f8240552f1cb1040f", - "value": 4940831, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "33a8cd92651683ecf200ae234b320dcf1b612c7dd73946589298a5ac770edb07", - "value": 4940650, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "b68cdd9a328f9a963145682cbe89a0a1ead85e30855e978c32e7823e07582702", - "value": 1236444, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "8e37ebd5de43b5ca6da8d632d07962339dd783c0ece17f55dd74d605cbf0d208", - "value": 1236263, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "05233093a678a63821e9e66e74b30c8095e485319c50d626d2f5caab6be2e10e", - "value": 1236353, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "b81908eb81aba037376a442c4b3fe43ab3ff6467c3d919052d861f40f8c8bc05", - "value": 1236173, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "6922c6f80e7f819cc646e09df1dd40bbacbd53a98f4d23097ca89c9711ac1806", - "value": 1236399, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "a3c02c00d41434ff04757cb7406cf4e044946706218afdcf9c7d6fe7bdd3a107", - "value": 1236218, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "c49351383d53101dde2aeeb39c4a4839b6506493b03749c9077edb08fb09b004", - "value": 1236308, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "8b7662fc54892be7f4e6bbb2dc1000c8143e409d6bfb6fb18df205cebc92e80e", - "value": 1236128, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "dcb5c289b63da4f6a97cc7fa66a8c2c7b81004e3a4d4458a499b23c9b8a8990f", - "value": 9871782, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 14 - } - }, - { - "key": "889ca832d6403275d8fb3c535bffbc40de69be780740b42a51d5a3949099be07", - "value": 2470415, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "84e8658789b118df91c3bce818af7d22a5999c4234c091335ddd5b3e6bb49401", - "value": 2470235, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "4d60fd615105a9e1f5fdb2195213a0ff11b9ac71d61b50a2c7e98857ac4a7b0b", - "value": 2470325, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "8e02b00fc3bbe6b60ab24b515da480146b8575154c8b3db0b4a432602b62ad05", - "value": 2470144, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "068f7606f4dcf7bffe30cf0d2f7484e54246b5068e0542d06055e8b8370c4c02", - "value": 618222, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "9463d42eea36a07cd7075b732573439c50ce5172802ea0f07ec8a8a9d674360f", - "value": 618041, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "d1770df4ae7a8b6a997901699418590c19e7105e1c0842eaa90d07b856cb5808", - "value": 618131, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "7b1d8d042e78b4f9ab803ee8d25446001b18317556c61f412718584c3299c000", - "value": 617951, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "f676b9203f68586437591e5d23ffd20ba3b95cc67793de126181a35d4cbf150b", - "value": 618176, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "3e837f5dc9ac77776bde6ea3d347bc1cc1ce7984376f9c8d544b091c5c4c450c", - "value": 617996, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "4449487f1eb5231486428147c8d5555694627249f911380595bb91598defb30c", - "value": 4935891, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "8728df3bd953c5de4b60e0eea9427f95b6c2ecbbd8bfd4d5dc85af16d8773908", - "value": 4935710, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "ac70d707fc9b7ef537308492d949b3983b3c9ca4f15ea9cd11104563ca3ba90a", - "value": 1235207, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "b3e35fe77990d166f16be4b0b422acd9fad28b632d04fb24cf817c187774f40b", - "value": 1235027, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "2196d5b2e9f0c53f0366df12142fa3d7dabeac08b7952ec7005d69edfe412c0a", - "value": 1235117, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "04b05473be19736dc253cc2832455b5855a6a63a411c32031187d196b9c3af06", - "value": 1234937, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "05e4a264e91a7b5adda3798a9d3073ea9fe8d1194ba9f9a1b8b6a66aa152b304", - "value": 1235162, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "035b8b76f98045607e53e7b50ad7dbaa2ccaee6441135e564376ade0b8c39503", - "value": 1234982, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "1753268ad954d89ce5e46c645a558941ffad541712b0d5f7604d2cc394f11c04", - "value": 1235072, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "6e0a8926761530db78979b951ab6eee40084ac6c0e61a90eed8c0ffb35151a0e", - "value": 1234891, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "bced2ba4c4dd5e1127adcfbc91d27301482348b55c8be367bed7570507eafd00", - "value": 9861911, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 15 - } - }, - { - "key": "458e2d9a4f8567d0d5c49d2606b256d3adb6cb2513621e40f8e9748d73c5940b", - "value": 2467945, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "37814ad433b6bb94e561aabc33be0cfd2286e955ed64600cbba3495e3a62c308", - "value": 2467765, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "fe1198c381b83f4450656bc79c9ce55b37fa8085ba3a04c88f075d3100e99c0e", - "value": 2467855, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "db95d6ac3effa0fd3967b13aec69f86b62ea621218cc661df6672485bb551d07", - "value": 2467674, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "fe1f013dd7e424ad05bb4242b49517492245db0ea6fc34398b1e64ec7327a302", - "value": 617603, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "9900edbc1f2190569dd22ea60c7fc3e251fdf1d454171303772a193bb69b2c0c", - "value": 617423, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "ba4fc3528b3a093e0ddc99a5bdf4e9188eb454f9a21bfd3ae45b373d66b67601", - "value": 617513, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "f46bdf483f2e72ee2919da9d42e69f4cef4bc57886782e95c27cf9a41f918202", - "value": 617333, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "b9427d5b414540d770cc7a8c898bd74c2fe9f7ae3b4f8a78a51dd3b8e93b2702", - "value": 617558, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "c88f82392f9e716ac66bd6f9ad8df16eb5ddf9f57543a466bce4868d445ce30b", - "value": 617378, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "a209e8b8e0a8a82313fcc7e4dcd9a6d6853c6a782ac786ad24eb2fc83850ef0a", - "value": 4930955, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "aee223c45729660b2bf13848f9c209263cf7752c25f790776c9d761fcfbccd01", - "value": 4930775, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "721a6ee97816bf195289964bc0fd21e47e97f639b92a79404df81bfbe08f2c03", - "value": 1233972, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "db9801c7fe4ef8f772c2b3e8cdd9b15787678976e4772f65a7ce0f67206c970a", - "value": 1233792, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "b867d93b5496cdbd64354acf7dac69f25eaa40491f16e971ddcaabcc54f6e00c", - "value": 1233882, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "67a68fe367ed5ddc02b0e8b3283b53277619a768827196517bfcd3b3e275e202", - "value": 1233702, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "7f0fa83e14a2ab82dec73a2a263c07cf0bd2b2553244f938f4d6b260d5723604", - "value": 1233927, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "4b46860aa4b2e215fcd45c0ebf49e67c24b89baf5287b3fcd660a7e9f9bafa08", - "value": 1233747, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "045fbfd55f21c5726b886947d9ac234b42f25ac4c32b21c632fde82064e6ce0b", - "value": 1233837, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "82c3f46ef8fa1d110411fc214fdc7cbc5be1acc2a15e20b2a0d28bef0aeb870e", - "value": 1233656, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "a95c01c86ea0328030f940a46c21a332b0bd3a8fdc308edbe148fe94d5b2330f", - "value": 9852050, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 16 - } - }, - { - "key": "08360decdc72989f859f8a365b8c87a83761b005aefdd26d3930aaa8b738320f", - "value": 2465477, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "396c8d8a9fe1855230834e19c52070f525e8ca9e9b2503533221f4201109230d", - "value": 2465297, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "4696dc11dd1f39db87b79e14bac3423de9680d2887e553d6b78f3c7d2bc7b701", - "value": 2465387, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "38a83edb98fc757da4c4ec67539e5b11f5a434153959cb0b255316543b967d0f", - "value": 2465207, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "c968bcc35d741e8ed87ff8a6b3f6df2dee3dc2f802df787fb6bfbe4a6eb34001", - "value": 616986, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "4c4ea032ff444f8aec35fb0ffcad51dd5bb2827b274474a4956782dd6665c706", - "value": 616805, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "144b88f0630d3aedf7ddf38582a0d45dc550c129365fca25b75758897b298c0e", - "value": 616896, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "89f927f40ccccab140a43dbd11cb82d57e7ef8efc72411d0c3f25e3987ea2e0b", - "value": 616715, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "12591281d763d29a02188dbdf8e26f9194b13d9834f9f28b95b3d214136ab408", - "value": 616941, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "9d2e907dce020133f045da77ced222baa3f74bbd6601e774b36a387bc05cc902", - "value": 616760, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "ad29381e1c11c4dd92321143540f737e2079306c68c440803c044e82dfa7ce0b", - "value": 4926025, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "7fc69a7c3d14703c8c262ed77c55bc6fd5469354dea16eaffae2caa1d1b68503", - "value": 4925844, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "2fb52fd7845799f34f967124c8d52763a0233d46fe006ba13454aa6c2ac6e106", - "value": 1232738, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "4adb5e468ed0b14abcd81083795881ea6c379b791af810ba1a5a9b80e5eeea0c", - "value": 1232558, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "4ed493c52a892d0acde41cfde786e7b62c4f70f6502080d468c3526a6ccc620c", - "value": 1232648, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "164600af661edf992ceeb955535c2ff6c4d6a8fc74724988ba2482f4bf35dd0b", - "value": 1232468, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "983baafd1c1e06c690ce6887fd29fa04c06fa1bf060cd6195506738980837d01", - "value": 1232693, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "37da8066ac0d6b7f414a2141ad724fe3b4fe8953ba630cdb2a9e2ce962765b0a", - "value": 1232513, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "99559fbea709fa5a779edae4104b577fae9a21cb4dacd1568ccfce52861c4a0a", - "value": 1232603, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "f443168a9ac04715a760005373002a3e05ae1fc73f7fae6cfa488eb0ff1a240b", - "value": 1232423, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "1a51cf7fb410963f20f58625c909fc1e3b3f7a973d4c0828ab2f52e3bf780308", - "value": 9842199, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 17 - } - }, - { - "key": "e816d45b606cfda0166a6cd62a68310cda51290686a254f1f7fa6af6049a590e", - "value": 2463012, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "f15c32ae168ff4f5e96d55afb786b4168f5cc6bd65edc9a3285aa9b42a06a30c", - "value": 2462832, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "e83a2af37055689526f85679d92a564c8cb6dc93ea5f0edc8f85b12eed34050c", - "value": 2462922, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "b01cc5d36b87ad25afaa5166b0de60f99331c9a8d475cc0074a1e027b9b1810b", - "value": 2462741, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "b8506482b3b8593aec70f0ed9b0b45c4e5c88ee95c9dcc7d669e207161b5f80f", - "value": 616369, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "b93ce91cd1110e5710ef13d0503e391ef06d8df93ee1c70bdf2d39a3794df80e", - "value": 616188, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "51fbd98d1318c2569d6b6fedc0fe01516af3a10839a3f4aeac5532c6ae19eb0e", - "value": 616279, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "8c52cf98bd54bfd8caea25983b05729df636d5be32732794552378668a5d070a", - "value": 616098, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "e1a78c1e9ba5157286fc15e0552905e19026b08bffdad8f0f55f8f68cfdc0a0f", - "value": 616324, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "4fdfb00c266bbda5ee389673286b07645e8753f16e80dfa6066b6b0912bc6105", - "value": 616143, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "67e17fa7823643ceee885458caaec22086c1a1795ca30aa7ee6a6b17128b9b08", - "value": 4921099, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "8d67df0c745b37b797a431a317c35233bfe08614095b4eb9dae8d5e8b431b80f", - "value": 4920919, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "6b6cf183eb0ca0b40fc91f1675450f9b8ea590b75d6bd78055b4b2d186756f0d", - "value": 1231506, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "e99599964dfbba69b76d975e905ba16a50610e04cc8f36ea62b43904e2431705", - "value": 1231325, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "0484efaf55a0e6c1ad5a3fee916c11c4d2e73578a6201defe102188ee2b45809", - "value": 1231416, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "044a733fbf58a35ee0573fd43899d20d31530c08127c521ce648b915fa28410e", - "value": 1231235, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "b4bfdbdd4c69331cbd71a28d71b211afccb1f4be9ddde10cfa95cccde474f807", - "value": 1231461, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "bf5872a489c36925778da2484dd05f0fac404f36214c07122eb075f92b8fb401", - "value": 1231280, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "92268c55419d75900c888dfab5cb24700efcd9b05e9434a1a660adb653cc4c05", - "value": 1231370, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "834b8d8e5e30e5395d9f3a6fc284f7e47a33172b5c76fb1a88f68c9073d79901", - "value": 1231190, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "19610f14583a60ca7bbf604d877fba35a5568759918a5732256ee40130141303", - "value": 9832358, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 18 - } - }, - { - "key": "ee45d87e23189bb4f36cb024c4fe4dbb80e9376ba0c412ab94b4eb70eb37c306", - "value": 2460549, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "3376e02708fe7e77afdc7df849c8c30749f746e5356d37f6f9013ae6573a2402", - "value": 2460369, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "ebe2b90392766326dbc88cba0e386382b2190596847838bdee51b99bf4e04803", - "value": 2460459, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "8e3e0fd7a61cc9ae26c6976cb21bdbca1772af770590bc7c52e826db0d419e0c", - "value": 2460279, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "3f954862f104addcdf4c85b5cb8a6b3fa469d40c197ef1e00681ae04cb7b570e", - "value": 615753, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "acfbfaea32683583b739f6a383339a382884363894acb1fd6bf1158c3f757009", - "value": 615572, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "e0a14895bbfc6b08fc5868c54e11d87a6c2a4592fc993f247a473963e16e180b", - "value": 615662, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "3e1572bc287fbac145321dedaf2e155443a9e461b4d4250f94f7115408030c09", - "value": 615482, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "a54a8158a6c2eb907fef274c466d655f87c2e9db76fe085d1509093d88384c02", - "value": 615708, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "f8fac78727621bd2c9aa5707b5e0cda8e599441f66cf03f14d777ce9a884bd02", - "value": 615527, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "182acf52cd63f1a1dbff4b046a5d408ea979fbd698471d380017c3cf005e9d07", - "value": 4916179, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "aada92fc657bb44b071fb4ea8ac2c1fc9c7a8333adcf78b68cd7e579836bd103", - "value": 4915998, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "13fd0d7771f88b4958a1fb9ddfb662c3ba6b1a912cc2177193b5b986e565e605", - "value": 1230274, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "4b58a48fe7c0f262c44cf3cfe896e7bf654e13d9bd29466a9f65e21c06a66b03", - "value": 1230094, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "e3969ead29e82460a860a12ff80a8cbbc6c0abed9df1961ed00ce037be3aed0e", - "value": 1230184, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "17d7361fec911553e0252044125785d31b0d033b4a97e6c3568260712d2c7a0b", - "value": 1230004, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "a35b57ae229eea14cc7faf351ad1a44f55f3ee085b16308f177177dd0ad41800", - "value": 1230229, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "ed41123010b33b9e3c856923d646ba79590710f2465aabf02c986a9349c63e02", - "value": 1230049, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "de9a7aeaaf3879f14d909e53fe7ceb27a784e90518f7abbed66c6f710b6c3a06", - "value": 1230139, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "3577435a1e66f985212148b589e17c0f9b83d66f987f243555898ab1eb670307", - "value": 1229959, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "7a33a7daec92b902ac193081e5f4ed1708b72f9b9319a23b884821b4b93c3205", - "value": 9822526, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 19 - } - }, - { - "key": "6d8c46d2da26101d7bc5b2bdab88232a8df03d4db83eb5253a6100cf01a3110c", - "value": 2458089, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "072abfb04607f1cf7766fa18fcf2e0d41f48efbd2198394eab080b036996ab06", - "value": 2457909, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "3c4dbe36850824108e26aa422c2a114074840a300dd2f85d75b43c6a9e34a406", - "value": 2457999, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "16b27394b2b6dfa38c55ceedfd68009f04ad07a790f509477b20ded2dd949802", - "value": 2457818, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "0113ec0384a70baba6248ddb1d15119c37df86d1261c11236c36b1a829885507", - "value": 615137, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "71f04dade26af18b623531e800f6d088d4aa0628b0a55f52c5dcb61e577b200e", - "value": 614956, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "828a7e4dbd7f57c820afc7ef7600f2438135c74146181134deee670acc712e02", - "value": 615047, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "103efe8d4ab54b5409ccbc23276fac33c0e981b5603ebec79db2e4dd917d0004", - "value": 614866, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "e472a2f141f8c9c4da1a63fa13f712940108c4867af62aa1ac7dfef9f6b8b40f", - "value": 615092, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "d5bc867679b7852160f85ad225330a9db0326cecab9bcb7ce5689b013a0bf90b", - "value": 614911, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "320b75f57fd304d8bb752eebb77e154797906be99abf629a8e50cc22abd7ab03", - "value": 4911263, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "a263b0e2c725f245d4b7646323d779ccefb35f51fc17bcbdf0da54e75b701f0f", - "value": 4911082, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "8554e0c2681bda84e3ccb48af462831da3f098bd3ef58ea7c965a9d95c94120d", - "value": 1229044, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "bdeba4238c12e8b287a497c1bae79d8c4b03f0d6a5fe71787a2862d532084e09", - "value": 1228864, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "41372c665bd03f8b097d37bc19a546b1f899b2c3dd0df5c19b36fc3e39afae0b", - "value": 1228954, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "1a0abc752c8214e30beb7a2ec972436aba4e7cbe11befb9357871b4260074206", - "value": 1228774, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "31a9b116866d28455e2fed748f7f604236e90f013a3be78078f9593ccced2e0d", - "value": 1228999, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "68d080279032c14a6ef73711c9763e15524833dadba1e4fc5cd2a0c452f7ae08", - "value": 1228819, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "72431d537fc4c1e1b4eb2cc9b2d9f9e78f3cb1cfe8038c4d69e344f6d0d8df01", - "value": 1228909, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "314d3ef443c5f528b4df6f5154b64a1a79fd952321cd14466132516cf5c6e703", - "value": 1228728, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "c8facc6e5a11f2a63d750f8d41a008d3ee1ae2f6d74c4a5dd5d1a3758b1a9202", - "value": 9812705, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 20 - } - }, - { - "key": "ea050c91d9733c9cd7c10f5af26f25eb8cf93df7ab155e9ec5f583cc0d905502", - "value": 2455631, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "7d1d92301855cc6afbf3c25429905d015a296c2abe1b5ba2219f163b4bbd8004", - "value": 2455451, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "1b53a39bcd19286652445e50acb312ef10a901a0b421850daae8b793ad96e006", - "value": 2455541, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "4148ae29456045c8de5a0c25415630d4defbc92b001d9489b6f9fa9acf5f110f", - "value": 2455360, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "4ffc481f78412c8ca5a245034d118147f4c0b6dfe1bc84608cedd23c0440900a", - "value": 614522, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "e4c4a484d68226136f8fd5015ad33325e814dc835a66ecf7899c9d5988c4360c", - "value": 614341, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "e4c813bfc0bc0e53918ca4c62b8a985965e438b7a0b4dd6e732353a7ca07420f", - "value": 614432, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "e307ac829a3ae11a20e17955f743fe4f1299077161a7e4312ef408a2e3a5ae05", - "value": 614251, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "56e2e20bee10bb3e913c2d79c78c622039b5fcfd0b50a99000b3bb0edd47ef0d", - "value": 614477, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "d6f1bbe37b0f2c2859558f5f2f12db4b51a8fe68df2b79376661c401840a5307", - "value": 614296, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "2718c23bcd94424325f4f73acc63f93774289342218d1045755c6e109cc3f300", - "value": 4906352, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "b0aa3c2c137701406f753b54a0978c6795d19700f76de40638f1d95f992ea108", - "value": 4906172, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "cdaf6817cf474c2ece34ef66399adecc8670adea3dada14264fed009ee6ae70c", - "value": 1227815, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "6d3ba16d86bfe64789db58a3a5d09f71bd4f98d7f7f08db0539420854ce5e603", - "value": 1227635, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "1bc6d6ffab3cd82c4e8af42b939dbff94a7d22e34758746a8bee9b64a3483407", - "value": 1227725, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "555e18155cbebf294ab52f2000680fe19b6c8c03c2a3271551d4d373a308fe0e", - "value": 1227545, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "4520329bf60c3cfaabd3c246dd0081668c510b6b966e65973a19e0fe3c5b7a07", - "value": 1227770, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "15d295f683e612f97f6b7c5d3c788c080193eface863aafe9a31b5c48809a304", - "value": 1227590, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "852d808743b27b4408c7c42ef9d30a09fc9edea90b27538dcc586084ac067a00", - "value": 1227680, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "0ab8cb89a7d37a813e03809d2ac98aeb875378b218527241c9e1567a1bfcd20b", - "value": 1227499, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "d7d63961685a89a87be7c86583ce7c935d94004e354f04a7609bf5016146e109", - "value": 9802893, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 21 - } - }, - { - "key": "44c3a2a57de3fc45e409dd052d522df4b9fd9fe67a42dfc895ee2f08dc107004", - "value": 2453176, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "499bf3fe78f1edc2c5965faaf4ed4422cb40c31a51375db63eb6a97d6f39540f", - "value": 2452995, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "952dcc886e115e7c81f521c7705ecdfeadd6865f60b5e49857d3490ef3afef0c", - "value": 2453086, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "4ae7c45a73057b45f59884bbfe11d6cfae5eb84cff5ace1cd467fc965a80bd05", - "value": 2452905, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "25178947edb495b97d7b866eda22845077e71b33ee82b63f284ce6fc76e27408", - "value": 613907, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "9597cd332e09a435adb62fddf50eed8b07b23d2780f0a168b467785fa834730a", - "value": 613727, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "1803c1136113c07f0f1b7d249f78f8c8c97b8f537eb3712bd207ead946eb6203", - "value": 613817, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "941b4f492f516ed5442e4218292c8860dd8d268aa5d84883fbb0255c22ead80d", - "value": 613637, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "158bedb110c7bc937e6252ef6b0ca5b0a2e9cb2295422fef409bf17743c89706", - "value": 613862, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "92312848ecccadc543210f92e0b3f2033a1eb503b9293d4d5ddd939d685fce0e", - "value": 613682, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "43a3c12ecd76d4a277c49a59a200ea26ebb20940696b50a3d75166184aec7e02", - "value": 4901446, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "f81620a56eb69709a3d797794a1150f4957037b82f890130159ca448cf49d301", - "value": 4901266, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "3f88a5805a8841b5d6a6e72b5002e8e0882837f88e36332a1f42de6f0e0f170d", - "value": 1226588, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "ea2ebe3572e04c35592c4c33840232f78e0f97a22b677098d99b81b92eeea409", - "value": 1226407, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "7851a866cb45e4829ed5f9372eac5c0c29c4211963ff5d8ea97203b002dcbd04", - "value": 1226497, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "5a9699af46e6b2a4bf8fdf60187626a6b28db0c54a1cff4163b12864810e8c0b", - "value": 1226317, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "bf54de7f74bea1089217061660c85e0a9c3e94f68ae3c1c51f84d41291427b08", - "value": 1226543, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "9d2ad3d1fdb1c7473752096b7a79ce9d24ec20768f0203b56e32be02d997aa04", - "value": 1226362, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "1816109b81f7a66aeaa2f671acbef989583c2d60fced78d01517966b138f750a", - "value": 1226452, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "96232c540aaf2ef8656b683ee81db77652cbe781c56b84bc00a5d68524842208", - "value": 1226272, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "cd16f31cddf8bef5b155e16869945a4564e175af927580038dceadb923ceed0c", - "value": 9793091, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 22 - } - }, - { - "key": "b6239c4961dc93256d90b10439664b6872eb1a3fab616faf939da26ad924170b", - "value": 2450723, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "a64d9127c2804f16808d99bca6fee8497dedc6fc37b7310ae87f1e3984d36508", - "value": 2450542, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "6637c4d18b20037aa0ef5006b139d97390e29721dc1f8b32adf35e4217e26905", - "value": 2450633, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "024477e14291fa39da6419f1e3250296d74e4ba67c9b5e0e7b2c288e07fd5109", - "value": 2450452, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "069517b55d675b59dd4689368e75fbf412e44fa9f551a725c857b89e98c5b606", - "value": 613294, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "e5ec779cb8433af6b7d9a41e68c5ffe67383675fdf1e3e354b5f4a4b941bed09", - "value": 613113, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "d0bd80fc87dd272b4a9a032149fd830defb312cbcfc7ab15a73ae637d75b2602", - "value": 613203, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "0a8ea5578c1eb922698e8051460d9472ee407570b112f88f37f2dc1de4c47701", - "value": 613023, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "5406268311ba139024d107cfff7614e29a8f0b25d438977151166645bfbecb0a", - "value": 613248, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "e905def83229fcda9ffa2718d327f4267453e4d0e1daf1138a8c8cd9f14c0604", - "value": 613068, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "ecf6308c2e1d473206e2ad40302dab39453352a4154b74b8155343c1de07b304", - "value": 4896545, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "3c1e9a6b32afefc2bb4fbf1220e6ff13c6be3ee137003dba421ba53e10820e0b", - "value": 4896365, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "d432d85c3a0ac312627652684de38b3d2eafd86211df33483edf0452c1d5a209", - "value": 1225361, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "fa27fca1e990f64ac498456f9d545e4a8b99eab42cf5844346c1570ca9fad104", - "value": 1225181, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "3763c0e53fd201f828629d96157471d7d03f6fc4818c67563f7fda4b50c8a809", - "value": 1225271, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "582d5c0d8480e4771d7f47998a13dc09858da1ad7786626b17f7f838469c650f", - "value": 1225090, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "c15e30d7a12f7cc72628d013d2f7c6c870b51febcdde08eb5bcb6fcb86720a0e", - "value": 1225316, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "fc669c704795fed18016b221d6bb28545a4aa787e8d19b8d62b5a727adb6a20f", - "value": 1225136, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "6daad60059d175513c6fd8dd178fb3ffd96c40442ee45c234becc86e39e04900", - "value": 1225226, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "25cfa5cf9d1227552500f4454acaaaf88f9b0e057125d8567114b9f50d57680a", - "value": 1225045, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "70198dc404c50ec97e63375408be059d7134f420bdc19776948b759854e83d0f", - "value": 9783299, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 23 - } - }, - { - "key": "a45a79f3ab6c7f80d8a257d4be622eb8068bf441e7ce2a262e059c36575d9d03", - "value": 2448272, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "7d6d003aad5cd2b7fd12c3aebdac5c5e77e8bcb359aab42255fb0cd4378af807", - "value": 2448092, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "987e24445bfd70188cf4d37d9b02202a3629a9d0ed4bec70236e64b911d4cf07", - "value": 2448182, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "8879706c16bce19329a4209fadf6950c30fbad50f7f3a9daf0575b0f6c6b9b06", - "value": 2448002, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "22e9aac8f429fe71a375f09ff2cab06d42f5d3f46ffe90111537de7475921509", - "value": 612680, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "d849935d96e0b8f4b21d112fc72e85b5bd9d0d98a79c647b35638940b578fd08", - "value": 612500, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "87b6a04718d77783f87c14b08bfcc4317f664dcf38ec0a891119c4798fd7db08", - "value": 612590, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "1b062144c3d917a25a913c53d483e6115b83630ee086315c4832667a84777b08", - "value": 612410, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "4bd0a5bea92d70f9946085fb68c83fa37333afd4411c10ee56f151dfff18fb0c", - "value": 612635, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "f5ecdd430fb3ff4694969cc38912397d2908e05ef4bf0647470e0ef724baa302", - "value": 612455, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "4a60363ccd7abfa109432dc91037d0bd19f979ebe705f8df4d711f40f8894600", - "value": 4891649, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "4f2a28b37718aeb03e13f90186aa2626629d0efefb2563dc7d57b43a9ead8001", - "value": 4891469, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "e7175a859c52f60aa6ddf651f46736d9d41bc45b69b56c35be34d2fb1e7c8c04", - "value": 1224136, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "73524b9a96a441fa71ed112647e0f15ca97fe82b2adf52b39619124e6533ff02", - "value": 1223955, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "28162073d59cc4a97b41e6f14077d6c72d9233b750b75310df26fdc546f14b08", - "value": 1224046, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "719fdf90ffe22ff8161559347d689e592a78eecc5576222d166afd9482d02c07", - "value": 1223865, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "12ca4d5c9bd3c1b7a98d16813d14926ffdf3728ea5fddc0751038207f530a809", - "value": 1224091, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "0a1cf49400dc0b98a8be9739dc59a3cef4513cf0b185d2d660c2041d3399000a", - "value": 1223910, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "ac69b113937223a40fa1c87b85bd4f75b431bd89184ca1d7eb7be4351a55b60f", - "value": 1224001, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "8f917c787fccc2a443cf995221f910eb06a5312efda1c96017f733887b056806", - "value": 1223820, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "5690f33ee672fbe295dc1630c57bfe5486ecbe593f3c1b037708315bf0191909", - "value": 9773517, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 24 - } - }, - { - "key": "9c72428291633c2c9e5a912f55dcaab8d9a28afb60a354a69cea1dc010897d05", - "value": 2445824, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "516a829728d830678c539cbdecc9c24417a53cd54ae8edc18fbe00ad34320609", - "value": 2445644, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "cfc7bf1d0c1c6442570d15a2f6d302466d11108cc1947c28670596bfad0a7208", - "value": 2445734, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "1c601c797d33cd57cd2811145871766b3416024c77992d71a08a65e32acdba00", - "value": 2445554, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "be53897c8eb8504bc50f29b473e2e2c6ae76ed8b1b9bbbe19559ce285bc4f401", - "value": 612068, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "a85ac9365660f36e91fbb63c1bb0ac80dfee0a5e5602fd5d14562705aaeed203", - "value": 611887, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "e366fa7b28b878b8883848270932f58c6d290fd8cb0358e0b2d60069e6ae7304", - "value": 611977, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "ee5b56d7a744b1fbac8b3585347dc71e341e588e6ee29883fbcb0727a7901f01", - "value": 611797, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "2a3f1fa9b860e05163311c393d0f80a9331eba71e92bed5a9e301af88ffa3803", - "value": 612023, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "be976899377fe797d19f3b9218b163dc24eb49507c4db61a1a938dec7b0e8506", - "value": 611842, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "cbfe99fad9b48f7928da661860b25d41bdea57b28367f498f5ca5670d302dd07", - "value": 4886758, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "9c030be99cdab7199c081a8815a89eb748523480c05af983fe8a2fa159d95509", - "value": 4886578, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "63fb76e0debcafbdf626058e931548934070814b620a183447ea7f496a9bfa03", - "value": 1222912, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "7da7fa74c53c4146651557f0a558f1af356e0a01464ec62bc9d491e532b2cb0f", - "value": 1222731, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "fdb9cc6cb379e6e2873afb3f5a5ec37cd357e498b74de2585bb88eb5df283a02", - "value": 1222822, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "d2707d0958520231b4c282f93002e09d637f2d9eddc715afa94a3b5e62d39004", - "value": 1222641, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "8013c181d03dab8fe92dbd0500686b99bf7b1b6d342404ebecd089b6b0dce905", - "value": 1222867, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "b56153afb125f7cb957c2971e89a90328ed8959b03521fd7bf6ea436a0087e01", - "value": 1222686, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "15e556421e0b5c5acb4da969dd6ccd4a6656f3ab077dfb8cbca9cd4359c3ad0f", - "value": 1222777, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "5c8951ad1b0c67449314469ece6aaa2541c02efa985a5bfd7cd45c8cdd1e8e0b", - "value": 1222596, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "714be9c37e00db407f0aa795eaa4527c21f7c312a801dcd009049c4015ce5400", - "value": 9763744, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 25 - } - }, - { - "key": "8b5cb83f12f21d5ccba5268baad1ec9492610bdff226cedffe4c6fb3b962fb09", - "value": 2443379, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "9f2ed776b8787a39e234a974ea5714099cb891d45bf2b0ddfe28dda3bfd77109", - "value": 2443198, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "b5549ca6230d996cc9a569476e307393db2d903673c58c8f5b51b00fd66fbe06", - "value": 2443289, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "64501a94d536e825f2aadcf02ffdd75b081b02aaa3b374d2f71468e711386c02", - "value": 2443108, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "06ef70eb15bf638d65d86d2cf797b544ea2900a1f0c305c22c700075a65bb504", - "value": 611456, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "94ec548922289e9f935057a312b6cfc7e6711bb25916905403d09dc185f9a80e", - "value": 611275, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "702f3c053e99ab5281bc25b83da5aa7773c0b380e2c85d6df3afa21ffef6020e", - "value": 611365, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "09b55f66f0913d06e2436d665946999d097ceb69889af42a196e14b524a5ff0a", - "value": 611185, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "905b9432f653c461dc9c15bccbc1c3bd7f1c6d47d04064fc452ce9330ae9060b", - "value": 611411, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "1b3185858007271f65a3b1f70054fbb67eb870dcadc190efdd7018e3de71440b", - "value": 611230, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "7dad8cff1ed9ccac75aaf472ae93dec313a7347f8a9bd1fde8fd48f99dab220e", - "value": 4881872, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "bb1aa846d4c55a67003b01f7d7cf538d347bba6023e8dec63cca519342e8ea08", - "value": 4881691, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "54199500391497745538792cc55b02aa488aad9bfba2e2e2b2424a28cb30e601", - "value": 1221689, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "e44569b98103dfc3fd25159700f3592f026bc8ca26e1f6b26434cadcecc6290a", - "value": 1221509, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "09d7136ffc57cd6d223494e4159a0806d0b3a7565bbba5d737ef60f1a85caf00", - "value": 1221599, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "f6d251d6f491b555584d822ae05c256afa9e3ea2ddee3176c9fa89e41e233601", - "value": 1221418, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "6771f486079b7ddfe8f91ee41b372295179e82648062362a3af930c88914df05", - "value": 1221644, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "c9f65eac21b02865799a1fa030df86a876a4b266df36ba1d7dfda3ee81fd6300", - "value": 1221464, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "b58558edc584f460d96735f2408749fefaf16fb5104beaa834e7dd990c4d5300", - "value": 1221554, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - }, - { - "key": "7528f8d7ed1c17bce9a433e969dc657576310a29f3e64166a8321388017ed003", - "value": 1221373, - "features": { - "flags": { - "bits": 0 - }, - "maturity": 0 - } - } - ], - [ - { - "key": "951ce530cb974b2d051e5a5fb32fd1306f7bec8ffcb57077a5538b73460ebc01", - "value": 9753982, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 26 - } - } - ], - [], - [ - { - "key": "6174b249e770d127fccad72ec28ec19d3e9fc4445bf55285977b980d48d78f0f", - "value": 9744229, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 27 - } - } - ], - [], - [ - { - "key": "79433f4b9fd7b68a9c8c5e8e220e0b41885ea48f9b9c0e82bdeb0be6eface802", - "value": 9734485, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 28 - } - } - ], - [], - [ - { - "key": "833f68f37ff0fa759f96464b8d343dccef28bf8fdd1365f52ea3552b0081ef0b", - "value": 9724752, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 29 - } - } - ], - [], - [ - { - "key": "2e3ffa6757cfedcb1c8ef7a58d93b3918966a878b42e0c9d63a68b6bbb1f9705", - "value": 9715028, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 30 - } - } - ], - [], - [ - { - "key": "d56e829768413a00133d8ee9f321e28fe799b612f914f92e62b3a7907ce8320e", - "value": 9705314, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 31 - } - } - ], - [], - [ - { - "key": "f901288d280f86e7b69121c289a78bec8afd37c553b1e101de08764c7c037a07", - "value": 9695610, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 32 - } - } - ], - [], - [ - { - "key": "b2956cd360d863cdcd7caf77c066a2b7c627ba3cd1297bb677f8c1d1b432de00", - "value": 9685915, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 33 - } - } - ], - [], - [ - { - "key": "e1013d9c55ba074406e11289c8635939a4234acc44a732b626715a430b8ef307", - "value": 9676230, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 34 - } - } - ], - [], - [ - { - "key": "ff2bcb5c99ce3b4590678a595d3766223028bf2a48c93b99922c767d61736100", - "value": 9666555, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 35 - } - } - ], - [], - [ - { - "key": "b65382dc07088006288cb51d1cb1a546b77d1830b362176e3e21e4a2b741f204", - "value": 9656890, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 36 - } - } - ], - [], - [ - { - "key": "c2c78eddddc379376fa963c4093b415db8108ce22f20d56aa020373864407f08", - "value": 9647234, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 37 - } - } - ], - [], - [ - { - "key": "d5859554b255448e4b99dd5be6b36a5db1d710f2e55550c76d0621819c2fe200", - "value": 9637587, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 38 - } - } - ], - [], - [ - { - "key": "7af006e6bad761b78d5e7710542a4bba4cfd677c57ea6f8c29f5ebc55a231d0c", - "value": 9627951, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 39 - } - } - ], - [], - [ - { - "key": "8a093395ed853e979fe04f8348de019890cb2ab0759a83570ef4b19b3e8e140a", - "value": 9618324, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 40 - } - } - ], - [], - [ - { - "key": "e791e37b4f364522c7e4023e57cadb0734b3c4bf9b8fba379c393391e279110e", - "value": 9608707, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 41 - } - } - ], - [], - [ - { - "key": "6445d8e237bce2c3736c3efa819d075a40322e3a5752a96dc7e10b5a7a3b5209", - "value": 9599099, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 42 - } - } - ], - [], - [ - { - "key": "5232048c5c84f2c54ec2ad9741333f2e42859afec243b27230bc3e619b5e7d0c", - "value": 9589501, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 43 - } - } - ], - [], - [ - { - "key": "3cdd7adc0f21af72e59e47e8234c6642b3b00a132a345d576d4c51365a44dd0a", - "value": 9579912, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 44 - } - } - ], - [], - [ - { - "key": "6b25574604b9e442058bebf73d6e612a0e0706a7b07bbf561307d8ebc800f102", - "value": 9570333, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 45 - } - } - ], - [], - [ - { - "key": "45be520a451e9af0b284eba925d81bc69f58743c5ab949b21c64b14efb6f7801", - "value": 9560764, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 46 - } - } - ], - [], - [ - { - "key": "d0cc2c148987762d8695de17558c981b15ba1ca388ae75e2636b1e95e94f1904", - "value": 9551204, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 47 - } - } - ], - [], - [ - { - "key": "c6e10ecb14ff6017fbdc6a7d3d0ca80b7750542415d6e41631aedf045191ff04", - "value": 9541654, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 48 - } - } - ], - [], - [ - { - "key": "c0af1c4a0a215be40a55bd8aae96ceab959535a28941b31de92d51315c58c00c", - "value": 9532113, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 49 - } - } - ], - [], - [ - { - "key": "c0be49720b4c598e31f42295dafb292c4adce44d59ce16ffb283027a96e8b306", - "value": 9522582, - "features": { - "flags": { - "bits": 1 - }, - "maturity": 50 - } - } - ] - ] -} \ No newline at end of file diff --git a/applications/grpc_wallet/src/lib.rs b/base_layer/core/tests/chain_storage.rs similarity index 96% rename from applications/grpc_wallet/src/lib.rs rename to base_layer/core/tests/chain_storage.rs index 48b472e08d..da38a9fbd7 100644 --- a/applications/grpc_wallet/src/lib.rs +++ b/base_layer/core/tests/chain_storage.rs @@ -20,5 +20,6 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -pub mod grpc_interface; -pub mod wallet_server; +pub mod chain_storage_tests; +#[allow(dead_code)] +mod helpers; diff --git a/base_layer/core/tests/chain_storage_tests/chain_backend.rs b/base_layer/core/tests/chain_storage_tests/chain_backend.rs new file mode 100644 index 0000000000..18a0375be9 --- /dev/null +++ b/base_layer/core/tests/chain_storage_tests/chain_backend.rs @@ -0,0 +1,1161 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use croaring::Bitmap; +use tari_core::{ + blocks::BlockHeader, + chain_storage::{ + create_lmdb_database, + BlockchainBackend, + DbKey, + DbKeyValuePair, + DbTransaction, + DbValue, + MemoryDatabase, + MetadataKey, + MetadataValue, + MmrTree, + }, + consensus::{ConsensusConstants, Network}, + helpers::create_orphan_block, + transactions::{ + helpers::{create_test_kernel, create_utxo}, + tari_amount::MicroTari, + types::{CryptoFactories, HashDigest}, + }, + tx, +}; +use tari_crypto::tari_utilities::{hex::Hex, Hashable}; +use tari_mmr::{MmrCacheConfig, MutableMmr}; +use tari_test_utils::paths::create_temporary_data_path; + +fn insert_contains_delete_and_fetch_header(db: T) { + let mut header = BlockHeader::new(0); + header.height = 42; + let hash = header.hash(); + assert_eq!(db.contains(&DbKey::BlockHeader(header.height)), Ok(false)); + assert_eq!(db.contains(&DbKey::BlockHash(hash.clone())), Ok(false)); + + let mut txn = DbTransaction::new(); + txn.insert_header(header.clone()); + assert!(db.write(txn).is_ok()); + assert_eq!(db.contains(&DbKey::BlockHeader(header.height)), Ok(true)); + assert_eq!(db.contains(&DbKey::BlockHash(hash.clone())), Ok(true)); + if let Some(DbValue::BlockHeader(retrieved_header)) = db.fetch(&DbKey::BlockHeader(header.height)).unwrap() { + assert_eq!(*retrieved_header, header); + } else { + assert!(false); + } + if let Some(DbValue::BlockHash(retrieved_header)) = db.fetch(&DbKey::BlockHash(hash.clone())).unwrap() { + assert_eq!(*retrieved_header, header); + } else { + assert!(false); + } + + let mut txn = DbTransaction::new(); + txn.delete(DbKey::BlockHash(hash.clone())); + assert!(db.write(txn).is_ok()); + assert_eq!(db.contains(&DbKey::BlockHeader(header.height)), Ok(false)); + assert_eq!(db.contains(&DbKey::BlockHash(hash)), Ok(false)); +} + +#[test] +fn memory_insert_contains_delete_and_fetch_header() { + let db = MemoryDatabase::::default(); + insert_contains_delete_and_fetch_header(db); +} + +#[test] +fn lmdb_insert_contains_delete_and_fetch_header() { + let db = create_lmdb_database(&create_temporary_data_path(), MmrCacheConfig::default()).unwrap(); + insert_contains_delete_and_fetch_header(db); +} + +fn insert_contains_delete_and_fetch_utxo(db: T) { + let factories = CryptoFactories::default(); + let (utxo, _) = create_utxo(MicroTari(10_000), &factories, None); + let hash = utxo.hash(); + assert_eq!(db.contains(&DbKey::UnspentOutput(hash.clone())), Ok(false)); + + let mut txn = DbTransaction::new(); + txn.insert_utxo(utxo.clone(), true); + assert!(db.write(txn).is_ok()); + assert_eq!(db.contains(&DbKey::UnspentOutput(hash.clone())), Ok(true)); + if let Some(DbValue::UnspentOutput(retrieved_utxo)) = db.fetch(&DbKey::UnspentOutput(hash.clone())).unwrap() { + assert_eq!(*retrieved_utxo, utxo); + } else { + assert!(false); + } + + let mut txn = DbTransaction::new(); + txn.delete(DbKey::UnspentOutput(hash.clone())); + assert!(db.write(txn).is_ok()); + assert_eq!(db.contains(&DbKey::UnspentOutput(hash)), Ok(false)); +} + +#[test] +fn memory_insert_contains_delete_and_fetch_utxo() { + let db = MemoryDatabase::::default(); + insert_contains_delete_and_fetch_utxo(db); +} + +#[test] +fn lmdb_insert_contains_delete_and_fetch_utxo() { + let db = create_lmdb_database(&create_temporary_data_path(), MmrCacheConfig::default()).unwrap(); + insert_contains_delete_and_fetch_utxo(db); +} + +fn insert_contains_delete_and_fetch_kernel(db: T) { + let kernel = create_test_kernel(5.into(), 0); + let hash = kernel.hash(); + assert_eq!(db.contains(&DbKey::TransactionKernel(hash.clone())), Ok(false)); + + let mut txn = DbTransaction::new(); + txn.insert_kernel(kernel.clone(), true); + assert!(db.write(txn).is_ok()); + assert_eq!(db.contains(&DbKey::TransactionKernel(hash.clone())), Ok(true)); + if let Some(DbValue::TransactionKernel(retrieved_kernel)) = + db.fetch(&DbKey::TransactionKernel(hash.clone())).unwrap() + { + assert_eq!(*retrieved_kernel, kernel); + } else { + assert!(false); + } + + let mut txn = DbTransaction::new(); + txn.delete(DbKey::TransactionKernel(hash.clone())); + assert!(db.write(txn).is_ok()); + assert_eq!(db.contains(&DbKey::TransactionKernel(hash)), Ok(false)); +} + +#[test] +fn memory_insert_contains_delete_and_fetch_kernel() { + let db = MemoryDatabase::::default(); + insert_contains_delete_and_fetch_kernel(db); +} + +#[test] +fn lmdb_insert_contains_delete_and_fetch_kernel() { + let db = create_lmdb_database(&create_temporary_data_path(), MmrCacheConfig::default()).unwrap(); + insert_contains_delete_and_fetch_kernel(db); +} + +fn insert_contains_delete_and_fetch_orphan(db: T, consensus_constants: &ConsensusConstants) { + let txs = vec![ + (tx!(1000.into(), fee: 20.into(), inputs: 2, outputs: 1)).0, + (tx!(2000.into(), fee: 30.into(), inputs: 1, outputs: 1)).0, + ]; + let orphan = create_orphan_block(10, txs, consensus_constants); + let hash = orphan.hash(); + assert_eq!(db.contains(&DbKey::OrphanBlock(hash.clone())), Ok(false)); + + let mut txn = DbTransaction::new(); + txn.insert_orphan(orphan.clone()); + assert!(db.write(txn).is_ok()); + + assert_eq!(db.contains(&DbKey::OrphanBlock(hash.clone())), Ok(true)); + if let Some(DbValue::OrphanBlock(retrieved_orphan)) = db.fetch(&DbKey::OrphanBlock(hash.clone())).unwrap() { + assert_eq!(*retrieved_orphan, orphan); + } else { + assert!(false); + } + + let mut txn = DbTransaction::new(); + txn.delete(DbKey::OrphanBlock(hash.clone())); + assert!(db.write(txn).is_ok()); + assert_eq!(db.contains(&DbKey::OrphanBlock(hash)), Ok(false)); +} + +#[test] +fn memory_insert_contains_delete_and_fetch_orphan() { + let network = Network::LocalNet; + let consensus_constants = network.create_consensus_constants(); + let db = MemoryDatabase::::default(); + insert_contains_delete_and_fetch_orphan(db, &consensus_constants); +} + +#[test] +fn lmdb_insert_contains_delete_and_fetch_orphan() { + let network = Network::LocalNet; + let consensus_constants = network.create_consensus_constants(); + let db = create_lmdb_database(&create_temporary_data_path(), MmrCacheConfig::default()).unwrap(); + insert_contains_delete_and_fetch_orphan(db, &consensus_constants); +} + +fn spend_utxo_and_unspend_stxo(db: T) { + let factories = CryptoFactories::default(); + let (utxo1, _) = create_utxo(MicroTari(10_000), &factories, None); + let (utxo2, _) = create_utxo(MicroTari(15_000), &factories, None); + let hash1 = utxo1.hash(); + let hash2 = utxo2.hash(); + + let mut txn = DbTransaction::new(); + txn.insert_utxo(utxo1.clone(), true); + txn.insert_utxo(utxo2.clone(), true); + assert!(db.write(txn).is_ok()); + + let mut txn = DbTransaction::new(); + txn.spend_utxo(hash1.clone()); + assert!(db.write(txn).is_ok()); + assert_eq!(db.contains(&DbKey::UnspentOutput(hash1.clone())), Ok(false)); + assert_eq!(db.contains(&DbKey::UnspentOutput(hash2.clone())), Ok(true)); + assert_eq!(db.contains(&DbKey::SpentOutput(hash1.clone())), Ok(true)); + assert_eq!(db.contains(&DbKey::SpentOutput(hash2.clone())), Ok(false)); + + let mut txn = DbTransaction::new(); + txn.spend_utxo(hash2.clone()); + txn.unspend_stxo(hash1.clone()); + assert!(db.write(txn).is_ok()); + assert_eq!(db.contains(&DbKey::UnspentOutput(hash1.clone())), Ok(true)); + assert_eq!(db.contains(&DbKey::UnspentOutput(hash2.clone())), Ok(false)); + assert_eq!(db.contains(&DbKey::SpentOutput(hash1.clone())), Ok(false)); + assert_eq!(db.contains(&DbKey::SpentOutput(hash2.clone())), Ok(true)); + + if let Some(DbValue::UnspentOutput(retrieved_utxo)) = db.fetch(&DbKey::UnspentOutput(hash1.clone())).unwrap() { + assert_eq!(*retrieved_utxo, utxo1); + } else { + assert!(false); + } + if let Some(DbValue::SpentOutput(retrieved_utxo)) = db.fetch(&DbKey::SpentOutput(hash2.clone())).unwrap() { + assert_eq!(*retrieved_utxo, utxo2); + } else { + assert!(false); + } + + let mut txn = DbTransaction::new(); + txn.delete(DbKey::SpentOutput(hash2.clone())); + assert!(db.write(txn).is_ok()); + assert_eq!(db.contains(&DbKey::UnspentOutput(hash1.clone())), Ok(true)); + assert_eq!(db.contains(&DbKey::UnspentOutput(hash2.clone())), Ok(false)); + assert_eq!(db.contains(&DbKey::SpentOutput(hash1)), Ok(false)); + assert_eq!(db.contains(&DbKey::SpentOutput(hash2)), Ok(false)); +} + +#[test] +fn memory_spend_utxo_and_unspend_stxo() { + let db = MemoryDatabase::::default(); + spend_utxo_and_unspend_stxo(db); +} + +#[test] +fn lmdb_spend_utxo_and_unspend_stxo() { + let db = create_lmdb_database(&create_temporary_data_path(), MmrCacheConfig::default()).unwrap(); + spend_utxo_and_unspend_stxo(db); +} + +fn insert_fetch_metadata(db: T) { + assert!(db.fetch(&DbKey::Metadata(MetadataKey::ChainHeight)).unwrap().is_none()); + assert!(db + .fetch(&DbKey::Metadata(MetadataKey::AccumulatedWork)) + .unwrap() + .is_none()); + assert!(db + .fetch(&DbKey::Metadata(MetadataKey::PruningHorizon)) + .unwrap() + .is_none()); + assert!(db.fetch(&DbKey::Metadata(MetadataKey::BestBlock)).unwrap().is_none()); + + let header = BlockHeader::new(0); + let hash = header.hash(); + let pruning_horizon = 1u64; + let chain_height = 2u64; + let accumulated_work = 3u64; + + let mut txn = DbTransaction::new(); + txn.insert(DbKeyValuePair::Metadata( + MetadataKey::ChainHeight, + MetadataValue::ChainHeight(Some(chain_height)), + )); + txn.insert(DbKeyValuePair::Metadata( + MetadataKey::AccumulatedWork, + MetadataValue::AccumulatedWork(accumulated_work), + )); + txn.insert(DbKeyValuePair::Metadata( + MetadataKey::PruningHorizon, + MetadataValue::PruningHorizon(pruning_horizon), + )); + txn.insert(DbKeyValuePair::Metadata( + MetadataKey::BestBlock, + MetadataValue::BestBlock(Some(hash.clone())), + )); + assert!(db.write(txn).is_ok()); + + if let Some(DbValue::Metadata(MetadataValue::ChainHeight(Some(retrieved_chain_height)))) = + db.fetch(&DbKey::Metadata(MetadataKey::ChainHeight)).unwrap() + { + assert_eq!(retrieved_chain_height, chain_height); + } else { + assert!(false); + } + if let Some(DbValue::Metadata(MetadataValue::AccumulatedWork(retrieved_accumulated_work))) = + db.fetch(&DbKey::Metadata(MetadataKey::AccumulatedWork)).unwrap() + { + assert_eq!(retrieved_accumulated_work, accumulated_work); + } else { + assert!(false); + } + if let Some(DbValue::Metadata(MetadataValue::PruningHorizon(retrieved_pruning_horizon))) = + db.fetch(&DbKey::Metadata(MetadataKey::PruningHorizon)).unwrap() + { + assert_eq!(retrieved_pruning_horizon, pruning_horizon); + } else { + assert!(false); + } + if let Some(DbValue::Metadata(MetadataValue::BestBlock(Some(retrieved_hash)))) = + db.fetch(&DbKey::Metadata(MetadataKey::BestBlock)).unwrap() + { + assert_eq!(retrieved_hash, hash); + } else { + assert!(false); + } +} + +#[test] +fn memory_insert_fetch_metadata() { + let db = MemoryDatabase::::default(); + insert_fetch_metadata(db); +} + +#[test] +fn lmdb_insert_fetch_metadata() { + let db = create_lmdb_database(&create_temporary_data_path(), MmrCacheConfig::default()).unwrap(); + insert_fetch_metadata(db); +} + +fn fetch_mmr_root_and_proof_for_utxo_and_rp(db: T) { + // This is the zero-length MMR of a mutable MMR with Blake256 as hasher + assert_eq!( + db.fetch_mmr_root(MmrTree::Utxo).unwrap().to_hex(), + "26146a5435ef15e8cf7dc3354cb7268137e8be211794e93d04551576c6561565" + ); + assert_eq!( + db.fetch_mmr_root(MmrTree::RangeProof).unwrap().to_hex(), + "26146a5435ef15e8cf7dc3354cb7268137e8be211794e93d04551576c6561565" + ); + let factories = CryptoFactories::default(); + + let (utxo1, _) = create_utxo(MicroTari(10_000), &factories, None); + let (utxo2, _) = create_utxo(MicroTari(15_000), &factories, None); + let (utxo3, _) = create_utxo(MicroTari(20_000), &factories, None); + let utxo_hash1 = utxo1.hash(); + let utxo_hash2 = utxo2.hash(); + let utxo_hash3 = utxo3.hash(); + let rp_hash1 = utxo1.proof.hash(); + let rp_hash2 = utxo2.proof.hash(); + let rp_hash3 = utxo3.proof.hash(); + + let mut txn = DbTransaction::new(); + txn.insert_utxo(utxo1.clone(), true); + txn.insert_utxo(utxo2.clone(), true); + txn.insert_utxo(utxo3.clone(), true); + assert!(db.write(txn).is_ok()); + + let mut utxo_mmr_check = MutableMmr::::new(Vec::new(), Bitmap::create()); + assert!(utxo_mmr_check.push(&utxo_hash1).is_ok()); + assert!(utxo_mmr_check.push(&utxo_hash2).is_ok()); + assert!(utxo_mmr_check.push(&utxo_hash3).is_ok()); + assert_eq!( + db.fetch_mmr_root(MmrTree::Utxo).unwrap().to_hex(), + utxo_mmr_check.get_merkle_root().unwrap().to_hex() + ); + + let mmr_only_root = db.fetch_mmr_only_root(MmrTree::Utxo).unwrap(); + let proof1 = db.fetch_mmr_proof(MmrTree::Utxo, 0).unwrap(); + let proof2 = db.fetch_mmr_proof(MmrTree::Utxo, 1).unwrap(); + let proof3 = db.fetch_mmr_proof(MmrTree::Utxo, 2).unwrap(); + assert!(proof1.verify_leaf::(&mmr_only_root, &utxo_hash1, 0).is_ok()); + assert!(proof2.verify_leaf::(&mmr_only_root, &utxo_hash2, 1).is_ok()); + assert!(proof3.verify_leaf::(&mmr_only_root, &utxo_hash3, 2).is_ok()); + + let mut rp_mmr_check = MutableMmr::::new(Vec::new(), Bitmap::create()); + assert_eq!(rp_mmr_check.push(&rp_hash1), Ok(1)); + assert_eq!(rp_mmr_check.push(&rp_hash2), Ok(2)); + assert_eq!(rp_mmr_check.push(&rp_hash3), Ok(3)); + assert_eq!( + db.fetch_mmr_root(MmrTree::RangeProof).unwrap().to_hex(), + rp_mmr_check.get_merkle_root().unwrap().to_hex() + ); + + let mmr_only_root = db.fetch_mmr_only_root(MmrTree::RangeProof).unwrap(); + let proof1 = db.fetch_mmr_proof(MmrTree::RangeProof, 0).unwrap(); + let proof2 = db.fetch_mmr_proof(MmrTree::RangeProof, 1).unwrap(); + let proof3 = db.fetch_mmr_proof(MmrTree::RangeProof, 2).unwrap(); + assert!(proof1.verify_leaf::(&mmr_only_root, &rp_hash1, 0).is_ok()); + assert!(proof2.verify_leaf::(&mmr_only_root, &rp_hash2, 1).is_ok()); + assert!(proof3.verify_leaf::(&mmr_only_root, &rp_hash3, 2).is_ok()); +} + +#[test] +fn memory_fetch_mmr_root_and_proof_for_utxo_and_rp() { + let db = MemoryDatabase::::default(); + fetch_mmr_root_and_proof_for_utxo_and_rp(db); +} + +#[test] +fn lmdb_fetch_mmr_root_and_proof_for_utxo_and_rp() { + let db = create_lmdb_database(&create_temporary_data_path(), MmrCacheConfig::default()).unwrap(); + fetch_mmr_root_and_proof_for_utxo_and_rp(db); +} + +fn fetch_mmr_root_and_proof_for_kernel(db: T) { + // This is the zero-length MMR of a mutable MMR with Blake256 as hasher + assert_eq!( + db.fetch_mmr_root(MmrTree::Kernel).unwrap().to_hex(), + "26146a5435ef15e8cf7dc3354cb7268137e8be211794e93d04551576c6561565" + ); + + let kernel1 = create_test_kernel(100.into(), 0); + let kernel2 = create_test_kernel(200.into(), 1); + let kernel3 = create_test_kernel(300.into(), 2); + let hash1 = kernel1.hash(); + let hash2 = kernel2.hash(); + let hash3 = kernel3.hash(); + + let mut txn = DbTransaction::new(); + txn.insert_kernel(kernel1, true); + txn.insert_kernel(kernel2, true); + txn.insert_kernel(kernel3, true); + assert!(db.write(txn).is_ok()); + + let mut kernel_mmr_check = MutableMmr::::new(Vec::new(), Bitmap::create()); + assert!(kernel_mmr_check.push(&hash1).is_ok()); + assert!(kernel_mmr_check.push(&hash2).is_ok()); + assert!(kernel_mmr_check.push(&hash3).is_ok()); + assert_eq!( + db.fetch_mmr_root(MmrTree::Kernel).unwrap().to_hex(), + kernel_mmr_check.get_merkle_root().unwrap().to_hex() + ); + + let mmr_only_root = db.fetch_mmr_only_root(MmrTree::Kernel).unwrap(); + let proof1 = db.fetch_mmr_proof(MmrTree::Kernel, 0).unwrap(); + let proof2 = db.fetch_mmr_proof(MmrTree::Kernel, 1).unwrap(); + let proof3 = db.fetch_mmr_proof(MmrTree::Kernel, 2).unwrap(); + assert!(proof1.verify_leaf::(&mmr_only_root, &hash1, 0).is_ok()); + assert!(proof2.verify_leaf::(&mmr_only_root, &hash2, 1).is_ok()); + assert!(proof3.verify_leaf::(&mmr_only_root, &hash3, 2).is_ok()); +} + +#[test] +fn memory_fetch_mmr_root_and_proof_for_kernel() { + let db = MemoryDatabase::::default(); + fetch_mmr_root_and_proof_for_kernel(db); +} + +#[test] +fn lmdb_fetch_mmr_root_and_proof_for_kernel() { + let db = create_lmdb_database(&create_temporary_data_path(), MmrCacheConfig::default()).unwrap(); + fetch_mmr_root_and_proof_for_kernel(db); +} + +fn fetch_future_mmr_root_for_utxo_and_rp(db: T) { + let factories = CryptoFactories::default(); + + let (utxo1, _) = create_utxo(MicroTari(10_000), &factories, None); + let (utxo2, _) = create_utxo(MicroTari(15_000), &factories, None); + let (utxo3, _) = create_utxo(MicroTari(20_000), &factories, None); + let (utxo4, _) = create_utxo(MicroTari(24_000), &factories, None); + let utxo_hash1 = utxo1.hash(); + let utxo_hash3 = utxo3.hash(); + let utxo_hash4 = utxo4.hash(); + let rp_hash3 = utxo3.proof.hash(); + let rp_hash4 = utxo4.proof.hash(); + + let mut txn = DbTransaction::new(); + txn.insert_utxo(utxo1, true); + txn.insert_utxo(utxo2, true); + assert!(db.write(txn).is_ok()); + + let utxo_future_root = db + .calculate_mmr_root(MmrTree::Utxo, vec![utxo_hash3, utxo_hash4], vec![utxo_hash1.clone()]) + .unwrap() + .to_hex(); + let rp_future_root = db + .calculate_mmr_root(MmrTree::RangeProof, vec![rp_hash3, rp_hash4], Vec::new()) + .unwrap() + .to_hex(); + assert_ne!(utxo_future_root, db.fetch_mmr_root(MmrTree::Utxo).unwrap().to_hex()); + assert_ne!(rp_future_root, db.fetch_mmr_root(MmrTree::RangeProof).unwrap().to_hex()); + + let mut txn = DbTransaction::new(); + txn.insert_utxo(utxo3, true); + txn.insert_utxo(utxo4, true); + txn.spend_utxo(utxo_hash1); + assert!(db.write(txn).is_ok()); + + assert_eq!(utxo_future_root, db.fetch_mmr_root(MmrTree::Utxo).unwrap().to_hex()); + assert_eq!(rp_future_root, db.fetch_mmr_root(MmrTree::RangeProof).unwrap().to_hex()); +} + +#[test] +fn memory_fetch_future_mmr_root_for_utxo_and_rp() { + let db = MemoryDatabase::::default(); + fetch_future_mmr_root_for_utxo_and_rp(db); +} + +#[test] +fn lmdb_fetch_future_mmr_root_for_utxo_and_rp() { + let db = create_lmdb_database(&create_temporary_data_path(), MmrCacheConfig::default()).unwrap(); + fetch_future_mmr_root_for_utxo_and_rp(db); +} + +fn fetch_future_mmr_root_for_for_kernel(db: T) { + let kernel1 = create_test_kernel(100.into(), 0); + let kernel2 = create_test_kernel(200.into(), 1); + let kernel3 = create_test_kernel(300.into(), 2); + let kernel4 = create_test_kernel(400.into(), 3); + let hash3 = kernel3.hash(); + let hash4 = kernel4.hash(); + + let mut txn = DbTransaction::new(); + txn.insert_kernel(kernel1, true); + txn.insert_kernel(kernel2, true); + assert!(db.write(txn).is_ok()); + + let future_root = db + .calculate_mmr_root(MmrTree::Kernel, vec![hash3, hash4], Vec::new()) + .unwrap() + .to_hex(); + assert_ne!(future_root, db.fetch_mmr_root(MmrTree::Kernel).unwrap().to_hex()); + + let mut txn = DbTransaction::new(); + txn.insert_kernel(kernel3, true); + txn.insert_kernel(kernel4, true); + assert!(db.write(txn).is_ok()); + + assert_eq!(future_root, db.fetch_mmr_root(MmrTree::Kernel).unwrap().to_hex()); +} + +#[test] +fn memory_fetch_future_mmr_root_for_for_kernel() { + let db = MemoryDatabase::::default(); + fetch_future_mmr_root_for_for_kernel(db); +} + +#[test] +fn lmdb_fetch_future_mmr_root_for_for_kernel() { + let db = create_lmdb_database(&create_temporary_data_path(), MmrCacheConfig::default()).unwrap(); + fetch_future_mmr_root_for_for_kernel(db); +} + +fn commit_block_and_create_fetch_checkpoint_and_rewind_mmr(db: T) { + let factories = CryptoFactories::default(); + let (utxo1, _) = create_utxo(MicroTari(10_000), &factories, None); + let kernel1 = create_test_kernel(100.into(), 0); + let header1 = BlockHeader::new(0); + let utxo_hash1 = utxo1.hash(); + let kernel_hash1 = kernel1.hash(); + let rp_hash1 = utxo1.proof.hash(); + + let mut txn = DbTransaction::new(); + txn.insert_utxo(utxo1, true); + txn.insert_kernel(kernel1, true); + txn.insert_header(header1.clone()); + txn.commit_block(); + assert!(db.write(txn).is_ok()); + + let (utxo2, _) = create_utxo(MicroTari(15_000), &factories, None); + let kernel2 = create_test_kernel(200.into(), 0); + let header2 = BlockHeader::from_previous(&header1); + let utxo_hash2 = utxo2.hash(); + let kernel_hash2 = kernel2.hash(); + let rp_hash2 = utxo2.proof.hash(); + + let mut txn = DbTransaction::new(); + txn.insert_utxo(utxo2, true); + txn.spend_utxo(utxo_hash1.clone()); + txn.insert_kernel(kernel2, true); + txn.insert_header(header2); + txn.commit_block(); + assert!(db.write(txn).is_ok()); + + let utxo_cp0 = db.fetch_checkpoint(MmrTree::Utxo, 0).unwrap(); + let kernel_cp0 = db.fetch_checkpoint(MmrTree::Kernel, 0).unwrap(); + let range_proof_cp0 = db.fetch_checkpoint(MmrTree::RangeProof, 0).unwrap(); + let utxo_cp1 = db.fetch_checkpoint(MmrTree::Utxo, 1).unwrap(); + let kernel_cp1 = db.fetch_checkpoint(MmrTree::Kernel, 1).unwrap(); + let range_proof_cp1 = db.fetch_checkpoint(MmrTree::RangeProof, 1).unwrap(); + assert_eq!(utxo_cp0.nodes_added()[0], utxo_hash1); + assert_eq!(utxo_cp0.nodes_deleted().to_vec().len(), 0); + assert_eq!(kernel_cp0.nodes_added()[0], kernel_hash1); + assert_eq!(range_proof_cp0.nodes_added()[0], rp_hash1); + assert_eq!(utxo_cp1.nodes_added()[0], utxo_hash2); + assert_eq!(utxo_cp1.nodes_deleted().to_vec()[0], 0); + assert_eq!(kernel_cp1.nodes_added()[0], kernel_hash2); + assert_eq!(range_proof_cp1.nodes_added()[0], rp_hash2); + assert_eq!(db.contains(&DbKey::UnspentOutput(utxo_hash1.clone())), Ok(false)); + assert_eq!(db.contains(&DbKey::UnspentOutput(utxo_hash2.clone())), Ok(true)); + assert_eq!(db.contains(&DbKey::SpentOutput(utxo_hash1.clone())), Ok(true)); + assert_eq!(db.contains(&DbKey::SpentOutput(utxo_hash2.clone())), Ok(false)); + assert_eq!(db.contains(&DbKey::TransactionKernel(kernel_hash1.clone())), Ok(true)); + assert_eq!(db.contains(&DbKey::TransactionKernel(kernel_hash2.clone())), Ok(true)); + assert_eq!(db.contains(&DbKey::BlockHeader(0)), Ok(true)); + assert_eq!(db.contains(&DbKey::BlockHeader(1)), Ok(true)); + + let mut txn = DbTransaction::new(); + txn.delete(DbKey::BlockHeader(1)); + txn.delete(DbKey::TransactionKernel(kernel_hash2.clone())); + txn.delete(DbKey::UnspentOutput(utxo_hash2.clone())); + txn.unspend_stxo(utxo_hash1.clone()); + txn.rewind_kernel_mmr(1); + txn.rewind_utxo_mmr(1); + txn.rewind_rp_mmr(1); + assert!(db.write(txn).is_ok()); + + let utxo_cp0 = db.fetch_checkpoint(MmrTree::Utxo, 0).unwrap(); + let kernel_cp0 = db.fetch_checkpoint(MmrTree::Kernel, 0).unwrap(); + let range_proof_cp0 = db.fetch_checkpoint(MmrTree::RangeProof, 0).unwrap(); + assert_eq!(utxo_cp0.nodes_added()[0], utxo_hash1); + assert_eq!(utxo_cp0.nodes_deleted().to_vec().len(), 0); + assert_eq!(kernel_cp0.nodes_added()[0], kernel_hash1); + assert_eq!(range_proof_cp0.nodes_added()[0], rp_hash1); + assert!(db.fetch_checkpoint(MmrTree::Utxo, 1).is_err()); + assert!(db.fetch_checkpoint(MmrTree::Kernel, 1).is_err()); + assert!(db.fetch_checkpoint(MmrTree::RangeProof, 1).is_err()); + + assert_eq!(db.contains(&DbKey::UnspentOutput(utxo_hash1.clone())), Ok(true)); + assert_eq!(db.contains(&DbKey::UnspentOutput(utxo_hash2.clone())), Ok(false)); + assert_eq!(db.contains(&DbKey::SpentOutput(utxo_hash1)), Ok(false)); + assert_eq!(db.contains(&DbKey::SpentOutput(utxo_hash2)), Ok(false)); + assert_eq!(db.contains(&DbKey::TransactionKernel(kernel_hash1)), Ok(true)); + assert_eq!(db.contains(&DbKey::TransactionKernel(kernel_hash2)), Ok(false)); + assert_eq!(db.contains(&DbKey::BlockHeader(0)), Ok(true)); + assert_eq!(db.contains(&DbKey::BlockHeader(1)), Ok(false)); +} + +#[test] +fn memory_commit_block_and_create_fetch_checkpoint_and_rewind_mmr() { + let db = MemoryDatabase::::default(); + commit_block_and_create_fetch_checkpoint_and_rewind_mmr(db); +} + +#[test] +fn lmdb_commit_block_and_create_fetch_checkpoint_and_rewind_mmr() { + let db = create_lmdb_database(&create_temporary_data_path(), MmrCacheConfig::default()).unwrap(); + commit_block_and_create_fetch_checkpoint_and_rewind_mmr(db); +} + +// TODO: Test Needed: fetch_mmr_node + +fn for_each_orphan(db: T, consensus_constants: &ConsensusConstants) { + let orphan1 = create_orphan_block( + 5, + vec![(tx!(1000.into(), fee: 20.into(), inputs: 2, outputs: 1)).0], + consensus_constants, + ); + let orphan2 = create_orphan_block( + 10, + vec![(tx!(2000.into(), fee: 30.into(), inputs: 1, outputs: 1)).0], + consensus_constants, + ); + let orphan3 = create_orphan_block( + 15, + vec![(tx!(3000.into(), fee: 40.into(), inputs: 1, outputs: 2)).0], + consensus_constants, + ); + let hash1 = orphan1.hash(); + let hash2 = orphan2.hash(); + let hash3 = orphan3.hash(); + + let mut txn = DbTransaction::new(); + txn.insert_orphan(orphan1.clone()); + txn.insert_orphan(orphan2.clone()); + txn.insert_orphan(orphan3.clone()); + assert!(db.write(txn).is_ok()); + assert_eq!(db.contains(&DbKey::OrphanBlock(hash1.clone())), Ok(true)); + assert_eq!(db.contains(&DbKey::OrphanBlock(hash2.clone())), Ok(true)); + assert_eq!(db.contains(&DbKey::OrphanBlock(hash3.clone())), Ok(true)); + + let mut orphan1_found = false; + let mut orphan2_found = false; + let mut orphan3_found = false; + assert!(db + .for_each_orphan(|pair| { + let (hash, block) = pair.unwrap(); + if (hash == hash1) && (block == orphan1) { + orphan1_found = true; + } else if (hash == hash2) && (block == orphan2) { + orphan2_found = true; + } else if (hash == hash3) && (block == orphan3) { + orphan3_found = true; + } + }) + .is_ok()); + assert!(orphan1_found & orphan2_found & orphan3_found); +} + +#[test] +fn memory_for_each_orphan() { + let network = Network::LocalNet; + let consensus_constants = network.create_consensus_constants(); + let db = MemoryDatabase::::default(); + for_each_orphan(db, &consensus_constants); +} + +#[test] +fn lmdb_for_each_orphan() { + let network = Network::LocalNet; + let consensus_constants = network.create_consensus_constants(); + let db = create_lmdb_database(&create_temporary_data_path(), MmrCacheConfig::default()).unwrap(); + for_each_orphan(db, &consensus_constants); +} + +fn for_each_kernel(db: T) { + let kernel1 = create_test_kernel(100.into(), 0); + let kernel2 = create_test_kernel(200.into(), 1); + let kernel3 = create_test_kernel(300.into(), 2); + let hash1 = kernel1.hash(); + let hash2 = kernel2.hash(); + let hash3 = kernel3.hash(); + + let mut txn = DbTransaction::new(); + txn.insert_kernel(kernel1.clone(), false); + txn.insert_kernel(kernel2.clone(), false); + txn.insert_kernel(kernel3.clone(), false); + assert!(db.write(txn).is_ok()); + assert_eq!(db.contains(&DbKey::TransactionKernel(hash1.clone())), Ok(true)); + assert_eq!(db.contains(&DbKey::TransactionKernel(hash2.clone())), Ok(true)); + assert_eq!(db.contains(&DbKey::TransactionKernel(hash3.clone())), Ok(true)); + + let mut kernel1_found = false; + let mut kernel2_found = false; + let mut kernel3_found = false; + assert!(db + .for_each_kernel(|pair| { + let (hash, kernel) = pair.unwrap(); + if (hash == hash1) && (kernel == kernel1) { + kernel1_found = true; + } else if (hash == hash2) && (kernel == kernel2) { + kernel2_found = true; + } else if (hash == hash3) && (kernel == kernel3) { + kernel3_found = true; + } + }) + .is_ok()); + assert!(kernel1_found & kernel2_found & kernel3_found); +} + +#[test] +fn memory_for_each_kernel() { + let db = MemoryDatabase::::default(); + for_each_kernel(db); +} + +#[test] +fn lmdb_for_each_kernel() { + let db = create_lmdb_database(&create_temporary_data_path(), MmrCacheConfig::default()).unwrap(); + for_each_kernel(db); +} + +fn for_each_header(db: T) { + let header1 = BlockHeader::new(0); + let header2 = BlockHeader::from_previous(&header1); + let header3 = BlockHeader::from_previous(&header2); + let key1 = header1.height; + let key2 = header2.height; + let key3 = header3.height; + + let mut txn = DbTransaction::new(); + txn.insert_header(header1.clone()); + txn.insert_header(header2.clone()); + txn.insert_header(header3.clone()); + assert!(db.write(txn).is_ok()); + assert_eq!(db.contains(&DbKey::BlockHeader(key1)), Ok(true)); + assert_eq!(db.contains(&DbKey::BlockHeader(key2)), Ok(true)); + assert_eq!(db.contains(&DbKey::BlockHeader(key3)), Ok(true)); + + let mut header1_found = false; + let mut header2_found = false; + let mut header3_found = false; + assert!(db + .for_each_header(|pair| { + let (key, header) = pair.unwrap(); + if (key == key1) && (header == header1) { + header1_found = true; + } else if (key == key2) && (header == header2) { + header2_found = true; + } else if (key == key3) && (header == header3) { + header3_found = true; + } + }) + .is_ok()); + assert!(header1_found & header2_found & header3_found); +} + +#[test] +fn memory_for_each_header() { + let db = MemoryDatabase::::default(); + for_each_header(db); +} + +#[test] +fn lmdb_for_each_header() { + let db = create_lmdb_database(&create_temporary_data_path(), MmrCacheConfig::default()).unwrap(); + for_each_header(db); +} + +fn for_each_utxo(db: T) { + let factories = CryptoFactories::default(); + let (utxo1, _) = create_utxo(MicroTari(10_000), &factories, None); + let (utxo2, _) = create_utxo(MicroTari(15_000), &factories, None); + let (utxo3, _) = create_utxo(MicroTari(20_000), &factories, None); + let hash1 = utxo1.hash(); + let hash2 = utxo2.hash(); + let hash3 = utxo3.hash(); + + let mut txn = DbTransaction::new(); + txn.insert_utxo(utxo1.clone(), true); + txn.insert_utxo(utxo2.clone(), true); + txn.insert_utxo(utxo3.clone(), true); + assert!(db.write(txn).is_ok()); + assert_eq!(db.contains(&DbKey::UnspentOutput(hash1.clone())), Ok(true)); + assert_eq!(db.contains(&DbKey::UnspentOutput(hash2.clone())), Ok(true)); + assert_eq!(db.contains(&DbKey::UnspentOutput(hash3.clone())), Ok(true)); + + let mut utxo1_found = false; + let mut utxo2_found = false; + let mut utxo3_found = false; + assert!(db + .for_each_utxo(|pair| { + let (hash, utxo) = pair.unwrap(); + if (hash == hash1) && (utxo == utxo1) { + utxo1_found = true; + } else if (hash == hash2) && (utxo == utxo2) { + utxo2_found = true; + } else if (hash == hash3) && (utxo == utxo3) { + utxo3_found = true; + } + }) + .is_ok()); + assert!(utxo1_found & utxo2_found & utxo3_found); +} + +#[test] +fn memory_for_each_utxo() { + let db = MemoryDatabase::::default(); + for_each_utxo(db); +} + +#[test] +fn lmdb_for_each_utxo() { + let db = create_lmdb_database(&create_temporary_data_path(), MmrCacheConfig::default()).unwrap(); + for_each_utxo(db); +} + +#[test] +fn lmdb_backend_restore() { + let factories = CryptoFactories::default(); + let network = Network::LocalNet; + let consensus_constants = network.create_consensus_constants(); + + let txs = vec![(tx!(1000.into(), fee: 20.into(), inputs: 2, outputs: 1)).0]; + let orphan = create_orphan_block(10, txs, &consensus_constants); + let (utxo1, _) = create_utxo(MicroTari(10_000), &factories, None); + let (utxo2, _) = create_utxo(MicroTari(15_000), &factories, None); + let kernel = create_test_kernel(100.into(), 0); + let mut header = BlockHeader::new(0); + header.height = 1; + let orphan_hash = orphan.hash(); + let utxo_hash = utxo1.hash(); + let stxo_hash = utxo2.hash(); + let kernel_hash = kernel.hash(); + let header_hash = header.hash(); + + // Create backend storage + let path = create_temporary_data_path(); + { + let db = create_lmdb_database(&path, MmrCacheConfig::default()).unwrap(); + let mut txn = DbTransaction::new(); + txn.insert_orphan(orphan.clone()); + txn.insert_utxo(utxo1, true); + txn.insert_utxo(utxo2, true); + txn.insert_kernel(kernel, true); + txn.insert_header(header.clone()); + txn.commit_block(); + assert!(db.write(txn).is_ok()); + let mut txn = DbTransaction::new(); + txn.spend_utxo(stxo_hash.clone()); + assert!(db.write(txn).is_ok()); + + assert_eq!(db.contains(&DbKey::BlockHeader(header.height)), Ok(true)); + assert_eq!(db.contains(&DbKey::BlockHash(header_hash.clone())), Ok(true)); + assert_eq!(db.contains(&DbKey::UnspentOutput(utxo_hash.clone())), Ok(true)); + assert_eq!(db.contains(&DbKey::SpentOutput(stxo_hash.clone())), Ok(true)); + assert_eq!(db.contains(&DbKey::TransactionKernel(kernel_hash.clone())), Ok(true)); + assert_eq!(db.contains(&DbKey::OrphanBlock(orphan_hash.clone())), Ok(true)); + } + // Restore backend storage + let db = create_lmdb_database(&path, MmrCacheConfig::default()).unwrap(); + assert_eq!(db.contains(&DbKey::BlockHeader(header.height)), Ok(true)); + assert_eq!(db.contains(&DbKey::BlockHash(header_hash)), Ok(true)); + assert_eq!(db.contains(&DbKey::UnspentOutput(utxo_hash)), Ok(true)); + assert_eq!(db.contains(&DbKey::SpentOutput(stxo_hash)), Ok(true)); + assert_eq!(db.contains(&DbKey::TransactionKernel(kernel_hash)), Ok(true)); + assert_eq!(db.contains(&DbKey::OrphanBlock(orphan_hash)), Ok(true)); +} + +#[test] +fn lmdb_mmr_reset_and_commit() { + let factories = CryptoFactories::default(); + let db = create_lmdb_database(&create_temporary_data_path(), MmrCacheConfig::default()).unwrap(); + + let (utxo1, _) = create_utxo(MicroTari(10_000), &factories, None); + let (utxo2, _) = create_utxo(MicroTari(15_000), &factories, None); + let kernel1 = create_test_kernel(100.into(), 0); + let kernel2 = create_test_kernel(200.into(), 0); + let mut header1 = BlockHeader::new(0); + header1.height = 1; + let utxo_hash1 = utxo1.hash(); + let utxo_hash2 = utxo2.hash(); + let kernel_hash1 = kernel1.hash(); + let kernel_hash2 = kernel2.hash(); + let rp_hash1 = utxo1.proof.hash(); + let header_hash1 = header1.hash(); + + let mut txn = DbTransaction::new(); + txn.insert_utxo(utxo1, true); + txn.insert_kernel(kernel1, true); + txn.insert_header(header1); + txn.commit_block(); + assert!(db.write(txn).is_ok()); + + // Reset mmrs as a mmr txn failed without applying storage txns. + let mut txn = DbTransaction::new(); + txn.spend_utxo(utxo_hash2.clone()); + txn.commit_block(); + assert!(db.write(txn).is_err()); + + assert_eq!(db.contains(&DbKey::UnspentOutput(utxo_hash1.clone())), Ok(true)); + assert_eq!(db.contains(&DbKey::UnspentOutput(utxo_hash2.clone())), Ok(false)); + assert_eq!(db.contains(&DbKey::SpentOutput(utxo_hash1.clone())), Ok(false)); + assert_eq!(db.contains(&DbKey::SpentOutput(utxo_hash2.clone())), Ok(false)); + assert_eq!(db.contains(&DbKey::TransactionKernel(kernel_hash1.clone())), Ok(true)); + assert_eq!(db.contains(&DbKey::TransactionKernel(kernel_hash2.clone())), Ok(false)); + assert_eq!(db.contains(&DbKey::BlockHash(header_hash1.clone())), Ok(true)); + assert_eq!( + db.fetch_checkpoint(MmrTree::Utxo, 0).unwrap().nodes_added()[0], + utxo_hash1 + ); + assert_eq!( + db.fetch_checkpoint(MmrTree::Kernel, 0).unwrap().nodes_added()[0], + kernel_hash1 + ); + assert_eq!( + db.fetch_checkpoint(MmrTree::RangeProof, 0).unwrap().nodes_added()[0], + rp_hash1 + ); + assert!(db.fetch_checkpoint(MmrTree::Utxo, 1).is_err()); + assert!(db.fetch_checkpoint(MmrTree::Kernel, 1).is_err()); + assert!(db.fetch_checkpoint(MmrTree::RangeProof, 1).is_err()); + + // Reset mmrs as a storage txn failed after the mmr txns were applied, ensure the previous state was preserved. + let mut txn = DbTransaction::new(); + txn.spend_utxo(utxo_hash1.clone()); + txn.delete(DbKey::TransactionKernel(kernel_hash1.clone())); + txn.delete(DbKey::TransactionKernel(kernel_hash2.clone())); + txn.commit_block(); + assert!(db.write(txn).is_err()); + + assert_eq!(db.contains(&DbKey::UnspentOutput(utxo_hash1.clone())), Ok(true)); + assert_eq!(db.contains(&DbKey::UnspentOutput(utxo_hash2.clone())), Ok(false)); + assert_eq!(db.contains(&DbKey::SpentOutput(utxo_hash1.clone())), Ok(false)); + assert_eq!(db.contains(&DbKey::SpentOutput(utxo_hash2)), Ok(false)); + assert_eq!(db.contains(&DbKey::TransactionKernel(kernel_hash1.clone())), Ok(true)); + assert_eq!(db.contains(&DbKey::TransactionKernel(kernel_hash2)), Ok(false)); + assert_eq!(db.contains(&DbKey::BlockHash(header_hash1.clone())), Ok(true)); + assert_eq!( + db.fetch_checkpoint(MmrTree::Utxo, 0).unwrap().nodes_added()[0], + utxo_hash1 + ); + assert_eq!( + db.fetch_checkpoint(MmrTree::Kernel, 0).unwrap().nodes_added()[0], + kernel_hash1 + ); + assert_eq!( + db.fetch_checkpoint(MmrTree::RangeProof, 0).unwrap().nodes_added()[0], + rp_hash1 + ); + assert!(db.fetch_checkpoint(MmrTree::Utxo, 1).is_err()); + assert!(db.fetch_checkpoint(MmrTree::Kernel, 1).is_err()); + assert!(db.fetch_checkpoint(MmrTree::RangeProof, 1).is_err()); +} + +fn fetch_checkpoint(db: T) { + let factories = CryptoFactories::default(); + let (utxo1, _) = create_utxo(MicroTari(10_000), &factories, None); + let kernel1 = create_test_kernel(100.into(), 0); + let mut header1 = BlockHeader::new(0); + header1.height = 0; + let utxo_hash1 = utxo1.hash(); + let kernel_hash1 = kernel1.hash(); + let rp_hash1 = utxo1.proof.hash(); + + let mut txn = DbTransaction::new(); + txn.insert_utxo(utxo1, true); + txn.insert_kernel(kernel1, true); + txn.insert_header(header1.clone()); + txn.commit_block(); + assert!(db.write(txn).is_ok()); + + let (utxo2, _) = create_utxo(MicroTari(15_000), &factories, None); + let kernel2 = create_test_kernel(200.into(), 0); + let header2 = BlockHeader::from_previous(&header1); + let utxo_hash2 = utxo2.hash(); + let kernel_hash2 = kernel2.hash(); + let rp_hash2 = utxo2.proof.hash(); + + let mut txn = DbTransaction::new(); + txn.insert_utxo(utxo2, true); + txn.insert_kernel(kernel2, true); + txn.insert_header(header2.clone()); + txn.commit_block(); + assert!(db.write(txn).is_ok()); + + let utxo_cp0 = db.fetch_checkpoint(MmrTree::Utxo, 0); + let utxo_cp1 = db.fetch_checkpoint(MmrTree::Utxo, 1); + let kernel_cp0 = db.fetch_checkpoint(MmrTree::Kernel, 0); + let kernel_cp1 = db.fetch_checkpoint(MmrTree::Kernel, 1); + let rp_cp0 = db.fetch_checkpoint(MmrTree::RangeProof, 0); + let rp_cp1 = db.fetch_checkpoint(MmrTree::RangeProof, 1); + assert!(utxo_cp0.unwrap().nodes_added().contains(&utxo_hash1)); + assert!(utxo_cp1.unwrap().nodes_added().contains(&utxo_hash2)); + assert!(kernel_cp0.unwrap().nodes_added().contains(&kernel_hash1)); + assert!(kernel_cp1.unwrap().nodes_added().contains(&kernel_hash2)); + assert!(rp_cp0.unwrap().nodes_added().contains(&rp_hash1)); + assert!(rp_cp1.unwrap().nodes_added().contains(&rp_hash2)); + + let (utxo3, _) = create_utxo(MicroTari(20_000), &factories, None); + let kernel3 = create_test_kernel(300.into(), 0); + let header3 = BlockHeader::from_previous(&header2); + let utxo_hash3 = utxo3.hash(); + let kernel_hash3 = kernel3.hash(); + let rp_hash3 = utxo3.proof.hash(); + + let mut txn = DbTransaction::new(); + txn.insert_utxo(utxo3, true); + txn.insert_kernel(kernel3, true); + txn.insert_header(header3); + txn.commit_block(); + assert!(db.write(txn).is_ok()); + + let utxo_cp0 = db.fetch_checkpoint(MmrTree::Utxo, 0); + let utxo_cp1 = db.fetch_checkpoint(MmrTree::Utxo, 1); + let utxo_cp2 = db.fetch_checkpoint(MmrTree::Utxo, 2); + let kernel_cp0 = db.fetch_checkpoint(MmrTree::Kernel, 0); + let kernel_cp1 = db.fetch_checkpoint(MmrTree::Kernel, 1); + let kernel_cp2 = db.fetch_checkpoint(MmrTree::Kernel, 2); + let rp_cp0 = db.fetch_checkpoint(MmrTree::RangeProof, 0); + let rp_cp1 = db.fetch_checkpoint(MmrTree::RangeProof, 1); + let rp_cp2 = db.fetch_checkpoint(MmrTree::RangeProof, 2); + assert!(utxo_cp0.unwrap().nodes_added().contains(&utxo_hash1)); + assert!(utxo_cp1.unwrap().nodes_added().contains(&utxo_hash2)); + assert!(utxo_cp2.unwrap().nodes_added().contains(&utxo_hash3)); + assert!(kernel_cp0.unwrap().nodes_added().contains(&kernel_hash1)); + assert!(kernel_cp1.unwrap().nodes_added().contains(&kernel_hash2)); + assert!(kernel_cp2.unwrap().nodes_added().contains(&kernel_hash3)); + assert!(rp_cp0.unwrap().nodes_added().contains(&rp_hash1)); + assert!(rp_cp1.unwrap().nodes_added().contains(&rp_hash2)); + assert!(rp_cp2.unwrap().nodes_added().contains(&rp_hash3)); +} + +#[test] +fn memory_fetch_checkpoint() { + let mmr_cache_config = MmrCacheConfig { rewind_hist_len: 1 }; + let db = MemoryDatabase::::new(mmr_cache_config); + fetch_checkpoint(db); +} + +#[test] +fn lmdb_fetch_checkpoint() { + let mmr_cache_config = MmrCacheConfig { rewind_hist_len: 1 }; + let db = create_lmdb_database(&create_temporary_data_path(), mmr_cache_config).unwrap(); + fetch_checkpoint(db); +} + +fn duplicate_utxo(db: T) { + let factories = CryptoFactories::default(); + let (utxo1, _) = create_utxo(MicroTari(10_000), &factories, None); + let (utxo2, _) = create_utxo(MicroTari(15_000), &factories, None); + let hash1 = utxo1.hash(); + + let mut txn = DbTransaction::new(); + txn.insert_utxo_with_hash(hash1.clone(), utxo1.clone(), true); + assert!(db.write(txn).is_ok()); + assert_eq!(db.contains(&DbKey::UnspentOutput(hash1.clone())), Ok(true)); + if let Some(DbValue::UnspentOutput(retrieved_utxo)) = db.fetch(&DbKey::UnspentOutput(hash1.clone())).unwrap() { + assert_eq!(*retrieved_utxo, utxo1); + } else { + assert!(false); + } + let mut txn = DbTransaction::new(); + txn.insert_utxo_with_hash(hash1.clone(), utxo2.clone(), true); + assert!(db.write(txn).is_err()); // This should fail + if let Some(DbValue::UnspentOutput(retrieved_utxo)) = db.fetch(&DbKey::UnspentOutput(hash1.clone())).unwrap() { + assert_eq!(*retrieved_utxo, utxo1); // original data should still be there + } else { + assert!(false); + } +} + +#[test] +fn memory_duplicate_utxo() { + let db = MemoryDatabase::::default(); + duplicate_utxo(db); +} + +#[test] +fn lmdb_duplicate_utxo() { + let db = create_lmdb_database(&create_temporary_data_path(), MmrCacheConfig::default()).unwrap(); + duplicate_utxo(db); +} + +fn fetch_last_header(db: T) { + let mut header0 = BlockHeader::new(0); + header0.height = 0; + let mut header1 = BlockHeader::new(0); + header1.height = 1; + let mut header2 = BlockHeader::new(0); + header2.height = 2; + assert_eq!(db.fetch_last_header(), Ok(None)); + + let mut txn = DbTransaction::new(); + txn.insert_header(header0); + txn.insert_header(header1.clone()); + assert!(db.write(txn).is_ok()); + assert_eq!(db.fetch_last_header(), Ok(Some(header1))); + + let mut txn = DbTransaction::new(); + txn.insert_header(header2.clone()); + assert!(db.write(txn).is_ok()); + assert_eq!(db.fetch_last_header(), Ok(Some(header2))); +} + +#[test] +fn memory_fetch_last_header() { + let db = MemoryDatabase::::default(); + fetch_last_header(db); +} + +#[test] +fn lmdb_fetch_last_header() { + let db = create_lmdb_database(&create_temporary_data_path(), MmrCacheConfig::default()).unwrap(); + fetch_last_header(db); +} diff --git a/base_layer/core/tests/chain_storage_tests/chain_storage.rs b/base_layer/core/tests/chain_storage_tests/chain_storage.rs new file mode 100644 index 0000000000..54cbc14539 --- /dev/null +++ b/base_layer/core/tests/chain_storage_tests/chain_storage.rs @@ -0,0 +1,872 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::helpers::{ + block_builders::{append_block, generate_new_block, generate_new_block_with_achieved_difficulty}, + sample_blockchains::create_new_blockchain, +}; +use croaring::Bitmap; +use env_logger; +use std::thread; +use tari_core::{ + blocks::{genesis_block, Block, BlockHash, BlockHeader}, + chain_storage::{ + create_lmdb_database, + BlockAddResult, + BlockchainDatabase, + ChainStorageError, + DbKey, + DbTransaction, + MemoryDatabase, + MmrTree, + Validators, + }, + consensus::{ConsensusManagerBuilder, Network}, + helpers::{create_mem_db, create_orphan_block}, + proof_of_work::Difficulty, + transactions::{ + helpers::{create_test_kernel, create_utxo, spend_utxos}, + tari_amount::{uT, MicroTari, T}, + types::{CryptoFactories, HashDigest}, + }, + tx, + txn_schema, + validation::mocks::MockValidator, +}; +use tari_crypto::tari_utilities::{hex::Hex, Hashable}; +use tari_mmr::{MmrCacheConfig, MutableMmr}; +use tari_test_utils::paths::create_temporary_data_path; + +fn init_log() { + let _ = env_logger::builder().is_test(true).try_init(); +} + +#[test] +fn fetch_nonexistent_kernel() { + let network = Network::LocalNet; + let consensus_manager = ConsensusManagerBuilder::new(network).build(); + let store = create_mem_db(consensus_manager.clone()); + let h = vec![0u8; 32]; + assert_eq!( + store.fetch_kernel(h.clone()), + Err(ChainStorageError::ValueNotFound(DbKey::TransactionKernel(h))) + ); +} + +#[test] +fn insert_and_fetch_kernel() { + let network = Network::LocalNet; + let consensus_manager = ConsensusManagerBuilder::new(network).build(); + let store = create_mem_db(consensus_manager.clone()); + let kernel = create_test_kernel(5.into(), 0); + let hash = kernel.hash(); + + let mut txn = DbTransaction::new(); + txn.insert_kernel(kernel.clone(), true); + assert!(store.commit(txn).is_ok()); + assert_eq!(store.fetch_kernel(hash), Ok(kernel)); +} + +#[test] +fn fetch_nonexistent_header() { + let network = Network::LocalNet; + let consensus_manager = ConsensusManagerBuilder::new(network).build(); + let store = create_mem_db(consensus_manager.clone()); + assert_eq!( + store.fetch_header(1), + Err(ChainStorageError::ValueNotFound(DbKey::BlockHeader(1))) + ); +} + +#[test] +fn insert_and_fetch_header() { + let network = Network::LocalNet; + let consensus_manager = ConsensusManagerBuilder::new(network).build(); + let store = create_mem_db(consensus_manager.clone()); + let mut header = BlockHeader::new(0); + header.height = 42; + + let mut txn = DbTransaction::new(); + txn.insert_header(header.clone()); + assert!(store.commit(txn).is_ok()); + assert!(store.fetch_header(0).is_ok()); + assert_eq!( + store.fetch_header(1), + Err(ChainStorageError::ValueNotFound(DbKey::BlockHeader(1))) + ); + assert_eq!(store.fetch_header(42), Ok(header)); +} + +#[test] +fn insert_and_fetch_utxo() { + let factories = CryptoFactories::default(); + let network = Network::LocalNet; + let consensus_manager = ConsensusManagerBuilder::new(network).build(); + let store = create_mem_db(consensus_manager.clone()); + let (utxo, _) = create_utxo(MicroTari(10_000), &factories, None); + let hash = utxo.hash(); + assert_eq!(store.is_utxo(hash.clone()).unwrap(), false); + let mut txn = DbTransaction::new(); + txn.insert_utxo(utxo.clone(), true); + assert!(store.commit(txn).is_ok()); + assert_eq!(store.is_utxo(hash.clone()).unwrap(), true); + assert_eq!(store.fetch_utxo(hash), Ok(utxo)); +} + +#[test] +fn insert_and_fetch_orphan() { + let network = Network::LocalNet; + let consensus_manager = ConsensusManagerBuilder::new(network).build(); + let store = create_mem_db(consensus_manager.clone()); + let txs = vec![ + (tx!(1000.into(), fee: 20.into(), inputs: 2, outputs: 1)).0, + (tx!(2000.into(), fee: 30.into(), inputs: 1, outputs: 1)).0, + ]; + let orphan = create_orphan_block(10, txs, &consensus_manager.consensus_constants()); + let orphan_hash = orphan.hash(); + let mut txn = DbTransaction::new(); + txn.insert_orphan(orphan.clone()); + assert!(store.commit(txn).is_ok()); + assert_eq!(store.fetch_orphan(orphan_hash), Ok(orphan)); +} + +#[test] +fn multiple_threads() { + let network = Network::LocalNet; + let consensus_manager = ConsensusManagerBuilder::new(network).build(); + let store = create_mem_db(consensus_manager.clone()); + // Save a kernel in thread A + let store_a = store.clone(); + let a = thread::spawn(move || { + let kernel = create_test_kernel(5.into(), 0); + let hash = kernel.hash(); + let mut txn = DbTransaction::new(); + txn.insert_kernel(kernel.clone(), true); + assert!(store_a.commit(txn).is_ok()); + hash + }); + // Save a kernel in thread B + let store_b = store.clone(); + let b = thread::spawn(move || { + let kernel = create_test_kernel(10.into(), 0); + let hash = kernel.hash(); + let mut txn = DbTransaction::new(); + txn.insert_kernel(kernel.clone(), true); + assert!(store_b.commit(txn).is_ok()); + hash + }); + let hash_a = a.join().unwrap(); + let hash_b = b.join().unwrap(); + // Get the kernels back + let kernel_a = store.fetch_kernel(hash_a).unwrap(); + assert_eq!(kernel_a.fee, 5.into()); + let kernel_b = store.fetch_kernel(hash_b).unwrap(); + assert_eq!(kernel_b.fee, 10.into()); +} + +#[test] +fn utxo_and_rp_merkle_root() { + let network = Network::LocalNet; + let gen_block = genesis_block::get_rincewind_genesis_block_raw(); + let consensus_manager = ConsensusManagerBuilder::new(network).with_block(gen_block).build(); + let store = create_mem_db(consensus_manager.clone()); + let factories = CryptoFactories::default(); + let block0 = store.fetch_block(0).unwrap().block().clone(); + + let utxo0 = block0.body.outputs()[0].clone(); + let (utxo1, _) = create_utxo(MicroTari(10_000), &factories, None); + let (utxo2, _) = create_utxo(MicroTari(10_000), &factories, None); + let hash0 = utxo0.hash(); + let hash1 = utxo1.hash(); + let hash2 = utxo2.hash(); + // Calculate the Range proof MMR root as a check + let mut rp_mmr_check = MutableMmr::::new(Vec::new(), Bitmap::create()); + assert_eq!(rp_mmr_check.push(&utxo0.proof.hash()).unwrap(), 1); + assert_eq!(rp_mmr_check.push(&utxo1.proof.hash()).unwrap(), 2); + assert_eq!(rp_mmr_check.push(&utxo2.proof.hash()).unwrap(), 3); + // Store the UTXOs + let mut txn = DbTransaction::new(); + txn.insert_utxo(utxo1, true); + txn.insert_utxo(utxo2, true); + assert!(store.commit(txn).is_ok()); + let root = store.fetch_mmr_root(MmrTree::Utxo).unwrap(); + let rp_root = store.fetch_mmr_root(MmrTree::RangeProof).unwrap(); + let mut mmr_check = MutableMmr::::new(Vec::new(), Bitmap::create()); + assert!(mmr_check.push(&hash0).is_ok()); + assert!(mmr_check.push(&hash1).is_ok()); + assert!(mmr_check.push(&hash2).is_ok()); + assert_eq!(root.to_hex(), mmr_check.get_merkle_root().unwrap().to_hex()); + assert_eq!(rp_root.to_hex(), rp_mmr_check.get_merkle_root().unwrap().to_hex()); +} + +#[test] +fn kernel_merkle_root() { + let network = Network::LocalNet; + let consensus_manager = ConsensusManagerBuilder::new(network).build(); + let store = create_mem_db(consensus_manager.clone()); + let block0 = store.fetch_block(0).unwrap().block().clone(); + + let kernel1 = create_test_kernel(100.into(), 0); + let kernel2 = create_test_kernel(200.into(), 0); + let kernel3 = create_test_kernel(300.into(), 0); + let hash0 = block0.body.kernels()[0].hash(); + let hash1 = kernel1.hash(); + let hash2 = kernel2.hash(); + let hash3 = kernel3.hash(); + let mut txn = DbTransaction::new(); + txn.insert_kernel(kernel1, true); + txn.insert_kernel(kernel2, true); + txn.insert_kernel(kernel3, true); + assert!(store.commit(txn).is_ok()); + let root = store.fetch_mmr_root(MmrTree::Kernel).unwrap(); + let mut mmr_check = MutableMmr::::new(Vec::new(), Bitmap::create()); + assert!(mmr_check.push(&hash0).is_ok()); + assert!(mmr_check.push(&hash1).is_ok()); + assert!(mmr_check.push(&hash2).is_ok()); + assert!(mmr_check.push(&hash3).is_ok()); + assert_eq!(root.to_hex(), mmr_check.get_merkle_root().unwrap().to_hex()); +} + +#[test] +fn utxo_and_rp_future_merkle_root() { + let network = Network::LocalNet; + let gen_block = genesis_block::get_rincewind_genesis_block_raw(); + let consensus_manager = ConsensusManagerBuilder::new(network).with_block(gen_block).build(); + let store = create_mem_db(consensus_manager.clone()); + let factories = CryptoFactories::default(); + + let (utxo1, _) = create_utxo(MicroTari(10_000), &factories, None); + let (utxo2, _) = create_utxo(MicroTari(15_000), &factories, None); + let utxo_hash2 = utxo2.hash(); + let rp_hash2 = utxo2.proof.hash(); + + let mut txn = DbTransaction::new(); + txn.insert_utxo(utxo1, true); + assert!(store.commit(txn).is_ok()); + + let utxo_future_root = store + .calculate_mmr_root(MmrTree::Utxo, vec![utxo_hash2], Vec::new()) + .unwrap() + .to_hex(); + let rp_future_root = store + .calculate_mmr_root(MmrTree::RangeProof, vec![rp_hash2], Vec::new()) + .unwrap() + .to_hex(); + assert_ne!(utxo_future_root, store.fetch_mmr_root(MmrTree::Utxo).unwrap().to_hex()); + assert_ne!( + rp_future_root, + store.fetch_mmr_root(MmrTree::RangeProof).unwrap().to_hex() + ); + + let mut txn = DbTransaction::new(); + txn.insert_utxo(utxo2, true); + assert!(store.commit(txn).is_ok()); + + assert_eq!(utxo_future_root, store.fetch_mmr_root(MmrTree::Utxo).unwrap().to_hex()); + assert_eq!( + rp_future_root, + store.fetch_mmr_root(MmrTree::RangeProof).unwrap().to_hex() + ); +} + +#[test] +fn kernel_future_merkle_root() { + let network = Network::LocalNet; + let gen_block = genesis_block::get_rincewind_genesis_block_raw(); + let consensus_manager = ConsensusManagerBuilder::new(network).with_block(gen_block).build(); + let store = create_mem_db(consensus_manager.clone()); + + let kernel1 = create_test_kernel(100.into(), 0); + let kernel2 = create_test_kernel(200.into(), 0); + let hash2 = kernel2.hash(); + + let mut txn = DbTransaction::new(); + txn.insert_kernel(kernel1, true); + assert!(store.commit(txn).is_ok()); + + let future_root = store + .calculate_mmr_root(MmrTree::Kernel, vec![hash2], Vec::new()) + .unwrap() + .to_hex(); + assert_ne!(future_root, store.fetch_mmr_root(MmrTree::Kernel).unwrap().to_hex()); + + let mut txn = DbTransaction::new(); + txn.insert_kernel(kernel2, true); + assert!(store.commit(txn).is_ok()); + + assert_eq!(future_root, store.fetch_mmr_root(MmrTree::Kernel).unwrap().to_hex()); +} + +#[test] +fn utxo_and_rp_mmr_proof() { + let network = Network::LocalNet; + let gen_block = genesis_block::get_rincewind_genesis_block_raw(); + let consensus_manager = ConsensusManagerBuilder::new(network).with_block(gen_block).build(); + let store = create_mem_db(consensus_manager.clone()); + let factories = CryptoFactories::default(); + + let (utxo1, _) = create_utxo(MicroTari(5_000), &factories, None); + let (utxo2, _) = create_utxo(MicroTari(10_000), &factories, None); + let (utxo3, _) = create_utxo(MicroTari(15_000), &factories, None); + let mut txn = DbTransaction::new(); + txn.insert_utxo(utxo1.clone(), true); + txn.insert_utxo(utxo2.clone(), true); + txn.insert_utxo(utxo3.clone(), true); + assert!(store.commit(txn).is_ok()); + + let root = store.fetch_mmr_only_root(MmrTree::Utxo).unwrap(); + let proof1 = store.fetch_mmr_proof(MmrTree::Utxo, 1).unwrap(); + let proof2 = store.fetch_mmr_proof(MmrTree::Utxo, 2).unwrap(); + let proof3 = store.fetch_mmr_proof(MmrTree::Utxo, 3).unwrap(); + store.fetch_mmr_proof(MmrTree::RangeProof, 1).unwrap(); + store.fetch_mmr_proof(MmrTree::RangeProof, 2).unwrap(); + store.fetch_mmr_proof(MmrTree::RangeProof, 3).unwrap(); + assert!(proof1.verify_leaf::(&root, &utxo1.hash(), 1).is_ok()); + assert!(proof2.verify_leaf::(&root, &utxo2.hash(), 2).is_ok()); + assert!(proof3.verify_leaf::(&root, &utxo3.hash(), 3).is_ok()); +} + +#[test] +fn kernel_mmr_proof() { + let network = Network::LocalNet; + let consensus_manager = ConsensusManagerBuilder::new(network).build(); + let store = create_mem_db(consensus_manager.clone()); + + let kernel1 = create_test_kernel(100.into(), 0); + let kernel2 = create_test_kernel(200.into(), 1); + let kernel3 = create_test_kernel(300.into(), 2); + let mut txn = DbTransaction::new(); + txn.insert_kernel(kernel1.clone(), true); + txn.insert_kernel(kernel2.clone(), true); + txn.insert_kernel(kernel3.clone(), true); + assert!(store.commit(txn).is_ok()); + + let root = store.fetch_mmr_only_root(MmrTree::Kernel).unwrap(); + let proof1 = store.fetch_mmr_proof(MmrTree::Kernel, 1).unwrap(); + let proof2 = store.fetch_mmr_proof(MmrTree::Kernel, 2).unwrap(); + let proof3 = store.fetch_mmr_proof(MmrTree::Kernel, 3).unwrap(); + assert!(proof1.verify_leaf::(&root, &kernel1.hash(), 1).is_ok()); + assert!(proof2.verify_leaf::(&root, &kernel2.hash(), 2).is_ok()); + assert!(proof3.verify_leaf::(&root, &kernel3.hash(), 3).is_ok()); +} + +#[test] +fn store_and_retrieve_block() { + let (db, blocks, _, _) = create_new_blockchain(Network::LocalNet); + let hash = blocks[0].hash(); + // Check the metadata + let metadata = db.get_metadata().unwrap(); + assert_eq!(metadata.height_of_longest_chain, Some(0)); + assert_eq!(metadata.best_block, Some(hash)); + assert_eq!(metadata.horizon_block(metadata.height_of_longest_chain.unwrap()), 0); + // Fetch the block back + let block0 = db.fetch_block(0).unwrap(); + assert_eq!(block0.confirmations(), 1); + // Compare the blocks + let block0 = Block::from(block0); + assert_eq!(blocks[0], block0); +} + +#[test] +fn add_multiple_blocks() { + init_log(); + // Create new database with genesis block + let network = Network::LocalNet; + let consensus_manager = ConsensusManagerBuilder::new(network).build(); + let store = create_mem_db(consensus_manager.clone()); + let metadata = store.get_metadata().unwrap(); + assert_eq!(metadata.height_of_longest_chain, Some(0)); + let block0 = store.fetch_block(0).unwrap().block().clone(); + assert_eq!(metadata.best_block, Some(block0.hash())); + // Add another block + let block1 = append_block(&store, &block0, vec![], &consensus_manager.consensus_constants()).unwrap(); + let metadata = store.get_metadata().unwrap(); + let hash = block1.hash(); + assert_eq!(metadata.height_of_longest_chain, Some(1)); + assert_eq!(metadata.best_block.unwrap(), hash); + // Adding blocks is idempotent + assert_eq!(store.add_block(block1.clone()), Ok(BlockAddResult::BlockExists)); + // Check the metadata + let metadata = store.get_metadata().unwrap(); + assert_eq!(metadata.height_of_longest_chain, Some(1)); + assert_eq!(metadata.best_block.unwrap(), hash); +} + +#[test] +fn test_checkpoints() { + let network = Network::LocalNet; + let (db, blocks, outputs, consensus_manager) = create_new_blockchain(network); + + let txn = txn_schema!( + from: vec![outputs[0][0].clone()], + to: vec![MicroTari(5_000), MicroTari(6_000)] + ); + let (txn, _, _) = spend_utxos(txn); + let block1 = append_block(&db, &blocks[0], vec![txn], &consensus_manager.consensus_constants()).unwrap(); + // Get the checkpoint + let block_a = db.fetch_block(0).unwrap(); + assert_eq!(block_a.confirmations(), 2); + assert_eq!(blocks[0], Block::from(block_a)); + let block_b = db.fetch_block(1).unwrap(); + assert_eq!(block_b.confirmations(), 1); + let block1 = serde_json::to_string(&block1).unwrap(); + let block_b = serde_json::to_string(&Block::from(block_b)).unwrap(); + assert_eq!(block1, block_b); +} + +#[test] +fn rewind_to_height() { + let network = Network::LocalNet; + let (mut db, mut blocks, mut outputs, consensus_manager) = create_new_blockchain(network); + + // Block 1 + let schema = vec![txn_schema!(from: vec![outputs[0][0].clone()], to: vec![6 * T, 3 * T])]; + generate_new_block( + &mut db, + &mut blocks, + &mut outputs, + schema, + &consensus_manager.consensus_constants(), + ) + .unwrap(); + // Block 2 + let schema = vec![txn_schema!(from: vec![outputs[1][0].clone()], to: vec![3 * T, 1 * T])]; + generate_new_block( + &mut db, + &mut blocks, + &mut outputs, + schema, + &consensus_manager.consensus_constants(), + ) + .unwrap(); + // Block 3 + let schema = vec![ + txn_schema!(from: vec![outputs[2][0].clone()], to: vec![2 * T, 500_000 * uT]), + txn_schema!(from: vec![outputs[1][1].clone()], to: vec![500_000 * uT]), + ]; + generate_new_block( + &mut db, + &mut blocks, + &mut outputs, + schema, + &consensus_manager.consensus_constants(), + ) + .unwrap(); + + assert!(db.rewind_to_height(3).is_ok()); + // Check MMRs are correct + let mmr_check = blocks[3].header.kernel_mr.clone(); + let mmr = db.fetch_mmr_root(MmrTree::Kernel).unwrap(); + assert_eq!(mmr, mmr_check); + let mmr_check = blocks[3].header.range_proof_mr.clone(); + let mmr = db.fetch_mmr_root(MmrTree::RangeProof).unwrap(); + assert_eq!(mmr, mmr_check); + let mmr_check = blocks[3].header.output_mr.clone(); + let mmr = db.fetch_mmr_root(MmrTree::Utxo).unwrap(); + assert_eq!(mmr, mmr_check); + // Invalid rewind + assert!(db.rewind_to_height(4).is_err()); + assert!(db.rewind_to_height(1).is_ok()); + // Check MMRs are correct + let mmr_check = blocks[1].header.kernel_mr.clone(); + let mmr = db.fetch_mmr_root(MmrTree::Kernel).unwrap(); + assert_eq!(mmr, mmr_check); + let mmr_check = blocks[1].header.range_proof_mr.clone(); + let mmr = db.fetch_mmr_root(MmrTree::RangeProof).unwrap(); + assert_eq!(mmr, mmr_check); + let mmr_check = blocks[1].header.output_mr.clone(); + let mmr = db.fetch_mmr_root(MmrTree::Utxo).unwrap(); + assert_eq!(mmr, mmr_check); +} + +#[test] +fn handle_tip_reorg() { + // GB --> A1 --> A2(Low PoW) [Main Chain] + // \--> B2(Highest PoW) [Forked Chain] + // Initially, the main chain is GB->A1->A2. B2 has a higher accumulated PoW and when B2 is added the main chain is + // reorged to GB->A1->B2 + + // Create Main Chain + let network = Network::LocalNet; + let (mut store, mut blocks, mut outputs, consensus_manager) = create_new_blockchain(network); + // Block A1 + let txs = vec![txn_schema!( + from: vec![outputs[0][0].clone()], + to: vec![10 * T, 10 * T, 10 * T, 10 * T] + )]; + assert!(generate_new_block_with_achieved_difficulty( + &mut store, + &mut blocks, + &mut outputs, + txs, + Difficulty::from(1), + &consensus_manager.consensus_constants() + ) + .is_ok()); + // Block A2 + let txs = vec![txn_schema!(from: vec![outputs[1][3].clone()], to: vec![6 * T])]; + assert!(generate_new_block_with_achieved_difficulty( + &mut store, + &mut blocks, + &mut outputs, + txs, + Difficulty::from(3), + &consensus_manager.consensus_constants() + ) + .is_ok()); + + // Create Forked Chain + let consensus_manager_fork = ConsensusManagerBuilder::new(network) + .with_block(blocks[0].clone()) + .build(); + let mut orphan_store = create_mem_db(consensus_manager_fork.clone()); + orphan_store.add_block(blocks[1].clone()).unwrap(); + let mut orphan_blocks = vec![blocks[0].clone(), blocks[1].clone()]; + let mut orphan_outputs = vec![outputs[0].clone(), outputs[1].clone()]; + // Block B2 + let txs = vec![txn_schema!(from: vec![orphan_outputs[1][0].clone()], to: vec![5 * T])]; + assert!(generate_new_block_with_achieved_difficulty( + &mut orphan_store, + &mut orphan_blocks, + &mut orphan_outputs, + txs, + Difficulty::from(7), + &consensus_manager_fork.consensus_constants() + ) + .is_ok()); + + // Adding B2 to the main chain will produce a reorg to GB->A1->B2. + if let Ok(BlockAddResult::ChainReorg(_)) = store.add_block(orphan_blocks[2].clone()) { + assert!(true); + } else { + assert!(false); + } + assert_eq!(store.fetch_tip_header(), Ok(orphan_blocks[2].header.clone())); + + // Check that B2 was removed from the block orphans and A2 has been orphaned. + assert!(store.fetch_orphan(orphan_blocks[2].hash()).is_err()); + assert!(store.fetch_orphan(blocks[2].hash()).is_ok()); +} + +#[test] +fn handle_reorg() { + // GB --> A1 --> A2 --> A3 -----> A4(Low PoW) [Main Chain] + // \--> B2 --> B3(?) --> B4(Medium PoW) [Forked Chain 1] + // \-----> C4(Highest PoW) [Forked Chain 2] + // Initially, the main chain is GB->A1->A2->A3->A4 with orphaned blocks B2, B4, C4. When B3 arrives late and is + // added to the blockchain then a reorg is triggered and the main chain is reorganized to GB->A1->B2->B3->C4. + + // Create Main Chain + let network = Network::LocalNet; + let (mut store, mut blocks, mut outputs, consensus_manager) = create_new_blockchain(network); + // Block A1 + let txs = vec![txn_schema!( + from: vec![outputs[0][0].clone()], + to: vec![10 * T, 10 * T, 10 * T, 10 * T] + )]; + assert!(generate_new_block_with_achieved_difficulty( + &mut store, + &mut blocks, + &mut outputs, + txs, + Difficulty::from(1), + &consensus_manager.consensus_constants() + ) + .is_ok()); + // Block A2 + let txs = vec![txn_schema!(from: vec![outputs[1][3].clone()], to: vec![6 * T])]; + assert!(generate_new_block_with_achieved_difficulty( + &mut store, + &mut blocks, + &mut outputs, + txs, + Difficulty::from(3), + &consensus_manager.consensus_constants() + ) + .is_ok()); + // Block A3 + let txs = vec![txn_schema!(from: vec![outputs[2][0].clone()], to: vec![2 * T])]; + assert!(generate_new_block_with_achieved_difficulty( + &mut store, + &mut blocks, + &mut outputs, + txs, + Difficulty::from(1), + &consensus_manager.consensus_constants() + ) + .is_ok()); + // Block A4 + let txs = vec![txn_schema!(from: vec![outputs[1][0].clone()], to: vec![2 * T])]; + assert!(generate_new_block_with_achieved_difficulty( + &mut store, + &mut blocks, + &mut outputs, + txs, + Difficulty::from(1), + &consensus_manager.consensus_constants() + ) + .is_ok()); + + // Create Forked Chain 1 + let consensus_manager_fork = ConsensusManagerBuilder::new(network) + .with_block(blocks[0].clone()) + .build(); + let mut orphan1_store = create_mem_db(consensus_manager_fork); // GB + orphan1_store.add_block(blocks[1].clone()).unwrap(); // A1 + let mut orphan1_blocks = vec![blocks[0].clone(), blocks[1].clone()]; + let mut orphan1_outputs = vec![outputs[0].clone(), outputs[1].clone()]; + // Block B2 + let txs = vec![txn_schema!(from: vec![orphan1_outputs[1][0].clone()], to: vec![5 * T])]; + assert!(generate_new_block_with_achieved_difficulty( + &mut orphan1_store, + &mut orphan1_blocks, + &mut orphan1_outputs, + txs, + Difficulty::from(1), + &consensus_manager.consensus_constants() + ) + .is_ok()); + // Block B3 + let txs = vec![ + txn_schema!(from: vec![orphan1_outputs[1][3].clone()], to: vec![3 * T]), + txn_schema!(from: vec![orphan1_outputs[2][0].clone()], to: vec![3 * T]), + ]; + assert!(generate_new_block_with_achieved_difficulty( + &mut orphan1_store, + &mut orphan1_blocks, + &mut orphan1_outputs, + txs, + Difficulty::from(1), + &consensus_manager.consensus_constants() + ) + .is_ok()); + // Block B4 + let txs = vec![txn_schema!(from: vec![orphan1_outputs[3][0].clone()], to: vec![1 * T])]; + assert!(generate_new_block_with_achieved_difficulty( + &mut orphan1_store, + &mut orphan1_blocks, + &mut orphan1_outputs, + txs, + Difficulty::from(5), + &consensus_manager.consensus_constants() + ) + .is_ok()); + + // Create Forked Chain 2 + let consensus_manager_fork2 = ConsensusManagerBuilder::new(network) + .with_block(blocks[0].clone()) + .build(); + let mut orphan2_store = create_mem_db(consensus_manager_fork2); // GB + orphan2_store.add_block(blocks[1].clone()).unwrap(); // A1 + orphan2_store.add_block(orphan1_blocks[2].clone()).unwrap(); // B2 + orphan2_store.add_block(orphan1_blocks[3].clone()).unwrap(); // B3 + let mut orphan2_blocks = vec![ + blocks[0].clone(), + blocks[1].clone(), + orphan1_blocks[2].clone(), + orphan1_blocks[3].clone(), + ]; + let mut orphan2_outputs = vec![ + outputs[0].clone(), + outputs[1].clone(), + orphan1_outputs[2].clone(), + orphan1_outputs[3].clone(), + ]; + // Block C4 + let txs = vec![txn_schema!(from: vec![orphan2_outputs[3][1].clone()], to: vec![1 * T])]; + assert!(generate_new_block_with_achieved_difficulty( + &mut orphan2_store, + &mut orphan2_blocks, + &mut orphan2_outputs, + txs, + Difficulty::from(20), + &consensus_manager.consensus_constants() + ) + .is_ok()); + + // Now add the fork blocks C4, B2, B4 and B3 (out of order) to the first DB and observe a re-org. Blocks are added + // out of order to test the forward and reverse chaining. + store.add_block(orphan2_blocks[4].clone()).unwrap(); // C4 + store.add_block(orphan1_blocks[2].clone()).unwrap(); // B2 + store.add_block(orphan1_blocks[4].clone()).unwrap(); // B4 + store.add_block(orphan1_blocks[3].clone()).unwrap(); // B3 + assert_eq!(store.fetch_tip_header(), Ok(orphan2_blocks[4].header.clone())); + + // Check that B2,B3 and C4 were removed from the block orphans and A2,A3,A4 and B4 has been orphaned. + assert!(store.fetch_orphan(orphan1_blocks[2].hash()).is_err()); // B2 + assert!(store.fetch_orphan(orphan1_blocks[3].hash()).is_err()); // B3 + assert!(store.fetch_orphan(orphan2_blocks[4].hash()).is_err()); // C4 + assert!(store.fetch_orphan(blocks[2].hash()).is_ok()); // A2 + assert!(store.fetch_orphan(blocks[3].hash()).is_ok()); // A3 + assert!(store.fetch_orphan(blocks[4].hash()).is_ok()); // A4 + assert!(store.fetch_orphan(blocks[4].hash()).is_ok()); // B4 +} + +#[test] +fn store_and_retrieve_blocks() { + let mmr_cache_config = MmrCacheConfig { rewind_hist_len: 2 }; + let validators = Validators::new(MockValidator::new(true), MockValidator::new(true)); + let network = Network::LocalNet; + let rules = ConsensusManagerBuilder::new(network).build(); + let db = MemoryDatabase::::new(mmr_cache_config); + let mut store = BlockchainDatabase::new(db, rules.clone()).unwrap(); + store.set_validators(validators); + + let block0 = store.fetch_block(0).unwrap().block().clone(); + let block1 = append_block(&store, &block0, vec![], &rules.consensus_constants()).unwrap(); + let block2 = append_block(&store, &block1, vec![], &rules.consensus_constants()).unwrap(); + assert_eq!(*store.fetch_block(0).unwrap().block(), block0); + assert_eq!(*store.fetch_block(1).unwrap().block(), block1); + assert_eq!(*store.fetch_block(2).unwrap().block(), block2); + + let block3 = append_block(&store, &block2, vec![], &rules.consensus_constants()).unwrap(); + assert_eq!(*store.fetch_block(0).unwrap().block(), block0); + assert_eq!(*store.fetch_block(1).unwrap().block(), block1); + assert_eq!(*store.fetch_block(2).unwrap().block(), block2); + assert_eq!(*store.fetch_block(3).unwrap().block(), block3); +} + +#[test] +fn store_and_retrieve_chain_and_orphan_blocks_with_hashes() { + let mmr_cache_config = MmrCacheConfig { rewind_hist_len: 2 }; + let validators = Validators::new(MockValidator::new(true), MockValidator::new(true)); + let network = Network::LocalNet; + let rules = ConsensusManagerBuilder::new(network).build(); + let db = MemoryDatabase::::new(mmr_cache_config); + let mut store = BlockchainDatabase::new(db, rules.clone()).unwrap(); + store.set_validators(validators); + + let block0 = store.fetch_block(0).unwrap().block().clone(); + let block1 = append_block(&store, &block0, vec![], &rules.consensus_constants()).unwrap(); + let orphan = create_orphan_block(10, vec![], &rules.consensus_constants()); + let mut txn = DbTransaction::new(); + txn.insert_orphan(orphan.clone()); + assert!(store.commit(txn).is_ok()); + + let hash0 = block0.hash(); + let hash1 = block1.hash(); + let hash2 = orphan.hash(); + assert_eq!(*store.fetch_block_with_hash(hash0).unwrap().unwrap().block(), block0); + assert_eq!(*store.fetch_block_with_hash(hash1).unwrap().unwrap().block(), block1); + assert_eq!(*store.fetch_block_with_hash(hash2).unwrap().unwrap().block(), orphan); +} + +#[test] +fn total_kernel_excess() { + let network = Network::LocalNet; + let consensus_manager = ConsensusManagerBuilder::new(network).build(); + let store = create_mem_db(consensus_manager.clone()); + let block0 = store.fetch_block(0).unwrap().block().clone(); + + let kernel1 = create_test_kernel(100.into(), 0); + let kernel2 = create_test_kernel(200.into(), 0); + let kernel3 = create_test_kernel(300.into(), 0); + + let mut txn = DbTransaction::new(); + txn.insert_kernel(kernel1.clone(), false); + txn.insert_kernel(kernel2.clone(), false); + txn.insert_kernel(kernel3.clone(), false); + assert!(store.commit(txn).is_ok()); + + let total_kernel_excess = store.total_kernel_excess().unwrap(); + assert_eq!( + total_kernel_excess, + &(&(block0.body.kernels()[0].excess) + &kernel1.excess) + &(&kernel2.excess + &kernel3.excess) + ); +} + +#[test] +fn total_kernel_offset() { + let network = Network::LocalNet; + let consensus_manager = ConsensusManagerBuilder::new(network).build(); + let store = create_mem_db(consensus_manager.clone()); + let block0 = store.fetch_block(0).unwrap().block().clone(); + + let header2 = BlockHeader::from_previous(&block0.header); + let header3 = BlockHeader::from_previous(&header2); + let mut txn = DbTransaction::new(); + txn.insert_header(header2.clone()); + txn.insert_header(header3.clone()); + assert!(store.commit(txn).is_ok()); + + let total_kernel_offset = store.total_kernel_offset().unwrap(); + assert_eq!( + total_kernel_offset, + &(&block0.header.total_kernel_offset + &header2.total_kernel_offset) + &header3.total_kernel_offset + ); +} + +#[test] +fn total_utxo_commitment() { + let factories = CryptoFactories::default(); + let network = Network::LocalNet; + let gen_block = genesis_block::get_rincewind_genesis_block_raw(); + let consensus_manager = ConsensusManagerBuilder::new(network).with_block(gen_block).build(); + let store = create_mem_db(consensus_manager.clone()); + let block0 = store.fetch_block(0).unwrap().block().clone(); + + let (utxo1, _) = create_utxo(MicroTari(10_000), &factories, None); + let (utxo2, _) = create_utxo(MicroTari(15_000), &factories, None); + let (utxo3, _) = create_utxo(MicroTari(20_000), &factories, None); + + let mut txn = DbTransaction::new(); + txn.insert_utxo(utxo1.clone(), true); + txn.insert_utxo(utxo2.clone(), true); + txn.insert_utxo(utxo3.clone(), true); + assert!(store.commit(txn).is_ok()); + + let total_utxo_commitment = store.total_utxo_commitment().unwrap(); + assert_eq!( + total_utxo_commitment, + &(&(block0.body.outputs()[0].commitment) + &utxo1.commitment) + &(&utxo2.commitment + &utxo3.commitment) + ); +} + +#[test] +fn restore_metadata() { + let validators = Validators::new(MockValidator::new(true), MockValidator::new(true)); + let network = Network::LocalNet; + let rules = ConsensusManagerBuilder::new(network).build(); + let block_hash: BlockHash; + let path = create_temporary_data_path(); + { + let db = create_lmdb_database(&path, MmrCacheConfig::default()).unwrap(); + let mut db = BlockchainDatabase::new(db, rules.clone()).unwrap(); + db.set_validators(validators.clone()); + + let block0 = db.fetch_block(0).unwrap().block().clone(); + let block1 = append_block(&db, &block0, vec![], &rules.consensus_constants()).unwrap(); + db.add_block(block1.clone()).unwrap(); + block_hash = block1.hash(); + let metadata = db.get_metadata().unwrap(); + assert_eq!(metadata.height_of_longest_chain, Some(1)); + assert_eq!(metadata.best_block, Some(block_hash.clone())); + } + // Restore blockchain db + let db = create_lmdb_database(&path, MmrCacheConfig::default()).unwrap(); + let mut db = BlockchainDatabase::new(db, rules.clone()).unwrap(); + db.set_validators(validators); + + let metadata = db.get_metadata().unwrap(); + assert_eq!(metadata.height_of_longest_chain, Some(1)); + assert_eq!(metadata.best_block, Some(block_hash)); +} diff --git a/base_layer/core/src/chain_storage/test/mod.rs b/base_layer/core/tests/chain_storage_tests/mod.rs similarity index 98% rename from base_layer/core/src/chain_storage/test/mod.rs rename to base_layer/core/tests/chain_storage_tests/mod.rs index 8b274e1d65..f8ad0268c1 100644 --- a/base_layer/core/src/chain_storage/test/mod.rs +++ b/base_layer/core/tests/chain_storage_tests/mod.rs @@ -21,4 +21,5 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // +mod chain_backend; mod chain_storage; diff --git a/base_layer/core/tests/diff_adj_manager.rs b/base_layer/core/tests/diff_adj_manager.rs new file mode 100644 index 0000000000..c5730fb52c --- /dev/null +++ b/base_layer/core/tests/diff_adj_manager.rs @@ -0,0 +1,462 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#[allow(dead_code)] +mod helpers; + +use helpers::block_builders::chain_block; +use tari_core::{ + blocks::Block, + chain_storage::{BlockchainDatabase, MemoryDatabase}, + consensus::{ConsensusConstants, ConsensusManagerBuilder, Network}, + helpers::create_mem_db, + proof_of_work::{ + lwma_diff::LinearWeightedMovingAverage, + DiffAdjManager, + Difficulty, + DifficultyAdjustment, + PowAlgorithm, + }, + transactions::types::HashDigest, +}; +use tari_crypto::tari_utilities::epoch_time::EpochTime; + +fn create_test_pow_blockchain( + db: &BlockchainDatabase>, + mut pow_algos: Vec, + consensus_constants: &ConsensusConstants, +) +{ + pow_algos.remove(0); + let block0 = db.fetch_block(0).unwrap().block().clone(); + append_to_pow_blockchain(db, block0, pow_algos, consensus_constants); +} + +fn append_to_pow_blockchain( + db: &BlockchainDatabase>, + chain_tip: Block, + pow_algos: Vec, + consensus: &ConsensusConstants, +) +{ + let mut prev_block = chain_tip; + for pow_algo in pow_algos { + let new_block = chain_block(&prev_block, Vec::new(), consensus); + let mut new_block = db.calculate_mmr_roots(new_block).unwrap(); + new_block.header.timestamp = prev_block + .header + .timestamp + .increase(consensus.get_target_block_interval()); + new_block.header.pow.pow_algo = pow_algo; + db.add_block(new_block.clone()).unwrap(); + prev_block = new_block; + } +} + +// Calculated the accumulated difficulty for the selected blocks in the blockchain db. +fn calculate_accumulated_difficulty( + db: &BlockchainDatabase>, + heights: Vec, + consensus_constants: &ConsensusConstants, +) -> Difficulty +{ + let mut lwma = LinearWeightedMovingAverage::new( + consensus_constants.get_difficulty_block_window() as usize, + consensus_constants.get_diff_target_block_interval(), + ); + for height in heights { + let header = db.fetch_header(height).unwrap(); + let accumulated_difficulty = header.achieved_difficulty() + + match header.pow.pow_algo { + PowAlgorithm::Monero => header.pow.accumulated_monero_difficulty, + PowAlgorithm::Blake => header.pow.accumulated_blake_difficulty, + }; + lwma.add(header.timestamp, accumulated_difficulty).unwrap(); + } + lwma.get_difficulty() +} + +#[test] +fn test_initial_sync() { + let network = Network::LocalNet; + let consensus_manager = ConsensusManagerBuilder::new(network).build(); + let store = create_mem_db(consensus_manager.clone()); + + let pow_algos = vec![ + PowAlgorithm::Blake, // GB default + PowAlgorithm::Blake, + PowAlgorithm::Monero, + PowAlgorithm::Blake, + PowAlgorithm::Blake, + PowAlgorithm::Monero, + PowAlgorithm::Monero, + PowAlgorithm::Blake, + ]; + create_test_pow_blockchain(&store, pow_algos.clone(), &consensus_manager.consensus_constants()); + let diff_adj_manager = DiffAdjManager::new(store.clone(), &consensus_manager.consensus_constants()).unwrap(); + + // dbg!(&consensus_manager.consensus_constants().get_target_block_interval()); + assert_eq!( + diff_adj_manager.get_target_difficulty(PowAlgorithm::Monero), + Ok(calculate_accumulated_difficulty( + &store, + vec![2, 5, 6], + &consensus_manager.consensus_constants() + )) + ); + assert_eq!( + diff_adj_manager.get_target_difficulty(PowAlgorithm::Blake), + Ok(calculate_accumulated_difficulty( + &store, + vec![0, 1, 3, 4, 7], + &consensus_manager.consensus_constants() + )) + ); +} + +#[test] +fn test_sync_to_chain_tip() { + let network = Network::LocalNet; + let consensus_manager = ConsensusManagerBuilder::new(network).build(); + let store = create_mem_db(consensus_manager.clone()); + let diff_adj_manager = DiffAdjManager::new(store.clone(), &consensus_manager.consensus_constants()).unwrap(); + let _ = consensus_manager.set_diff_manager(diff_adj_manager); + + let pow_algos = vec![ + PowAlgorithm::Blake, // GB default + PowAlgorithm::Monero, + PowAlgorithm::Blake, + PowAlgorithm::Blake, + PowAlgorithm::Monero, + PowAlgorithm::Blake, + ]; + create_test_pow_blockchain(&store, pow_algos, &consensus_manager.consensus_constants()); + assert_eq!(store.get_height(), Ok(Some(5))); + assert_eq!( + consensus_manager.get_target_difficulty(PowAlgorithm::Monero), + Ok(calculate_accumulated_difficulty( + &store, + vec![1, 4], + &consensus_manager.consensus_constants() + )) + ); + assert_eq!( + consensus_manager.get_target_difficulty(PowAlgorithm::Blake), + Ok(calculate_accumulated_difficulty( + &store, + vec![0, 2, 3, 5], + &consensus_manager.consensus_constants() + )) + ); + + let pow_algos = vec![ + PowAlgorithm::Blake, + PowAlgorithm::Monero, + PowAlgorithm::Blake, + PowAlgorithm::Monero, + ]; + let tip = store.fetch_block(store.get_height().unwrap().unwrap()).unwrap().block; + append_to_pow_blockchain(&store, tip, pow_algos, &consensus_manager.consensus_constants()); + assert_eq!(store.get_height(), Ok(Some(9))); + assert_eq!( + consensus_manager.get_target_difficulty(PowAlgorithm::Monero), + Ok(calculate_accumulated_difficulty( + &store, + vec![1, 4, 7, 9], + &consensus_manager.consensus_constants() + )) + ); + assert_eq!( + consensus_manager.get_target_difficulty(PowAlgorithm::Blake), + Ok(calculate_accumulated_difficulty( + &store, + vec![0, 2, 3, 5, 6, 8], + &consensus_manager.consensus_constants() + )) + ); +} + +#[test] +fn test_target_difficulty_with_height() { + let network = Network::LocalNet; + let consensus_manager = ConsensusManagerBuilder::new(network).build(); + let store = create_mem_db(consensus_manager.clone()); + let diff_adj_manager = DiffAdjManager::new(store.clone(), &consensus_manager.consensus_constants()).unwrap(); + let _ = consensus_manager.set_diff_manager(diff_adj_manager); + assert!(consensus_manager + .get_target_difficulty_with_height(PowAlgorithm::Monero, 5) + .is_err()); + assert!(consensus_manager + .get_target_difficulty_with_height(PowAlgorithm::Blake, 5) + .is_err()); + + let pow_algos = vec![ + PowAlgorithm::Blake, // GB default + PowAlgorithm::Monero, + PowAlgorithm::Blake, + PowAlgorithm::Blake, + PowAlgorithm::Monero, + PowAlgorithm::Blake, + ]; + create_test_pow_blockchain(&store, pow_algos, &consensus_manager.consensus_constants()); + let diff_adj_manager = DiffAdjManager::new(store.clone(), &consensus_manager.consensus_constants()).unwrap(); + let _ = consensus_manager.set_diff_manager(diff_adj_manager); + + assert_eq!( + consensus_manager.get_target_difficulty_with_height(PowAlgorithm::Monero, 5), + Ok(calculate_accumulated_difficulty( + &store, + vec![1, 4], + &consensus_manager.consensus_constants() + )) + ); + assert_eq!( + consensus_manager.get_target_difficulty_with_height(PowAlgorithm::Blake, 5), + Ok(calculate_accumulated_difficulty( + &store, + vec![0, 2, 3, 5], + &consensus_manager.consensus_constants() + )) + ); + + assert_eq!( + consensus_manager.get_target_difficulty_with_height(PowAlgorithm::Monero, 2), + Ok(calculate_accumulated_difficulty( + &store, + vec![1], + &consensus_manager.consensus_constants() + )) + ); + assert_eq!( + consensus_manager.get_target_difficulty_with_height(PowAlgorithm::Blake, 2), + Ok(calculate_accumulated_difficulty( + &store, + vec![0, 2], + &consensus_manager.consensus_constants() + )) + ); + + assert_eq!( + consensus_manager.get_target_difficulty_with_height(PowAlgorithm::Monero, 3), + Ok(calculate_accumulated_difficulty( + &store, + vec![1], + &consensus_manager.consensus_constants() + )) + ); + assert_eq!( + consensus_manager.get_target_difficulty_with_height(PowAlgorithm::Blake, 3), + Ok(calculate_accumulated_difficulty( + &store, + vec![0, 2, 3], + &consensus_manager.consensus_constants() + )) + ); +} + +#[test] +#[ignore] // TODO Wait for reorg logic to be refactored +fn test_full_sync_on_reorg() { + let network = Network::LocalNet; + let consensus_manager = ConsensusManagerBuilder::new(network).build(); + let store = create_mem_db(consensus_manager.clone()); + let diff_adj_manager = DiffAdjManager::new(store.clone(), &consensus_manager.consensus_constants()).unwrap(); + + let pow_algos = vec![ + PowAlgorithm::Blake, // GB default + PowAlgorithm::Blake, + PowAlgorithm::Blake, + PowAlgorithm::Blake, + PowAlgorithm::Monero, + ]; + create_test_pow_blockchain(&store, pow_algos, &consensus_manager.consensus_constants()); + assert_eq!(store.get_height(), Ok(Some(4))); + assert_eq!( + diff_adj_manager.get_target_difficulty(PowAlgorithm::Monero), + Ok(Difficulty::from(1)) + ); + assert_eq!( + diff_adj_manager.get_target_difficulty(PowAlgorithm::Blake), + Ok(Difficulty::from(18)) + ); + + let pow_algos = vec![ + PowAlgorithm::Blake, + PowAlgorithm::Blake, + PowAlgorithm::Monero, + PowAlgorithm::Monero, + PowAlgorithm::Blake, + PowAlgorithm::Monero, + PowAlgorithm::Blake, + PowAlgorithm::Monero, + ]; + assert_eq!(store.get_height(), Ok(Some(8))); + let tip = store.fetch_block(8).unwrap().block; + append_to_pow_blockchain(&store, tip, pow_algos, &consensus_manager.consensus_constants()); + assert_eq!( + diff_adj_manager.get_target_difficulty(PowAlgorithm::Monero), + Ok(Difficulty::from(2)) + ); + assert_eq!( + diff_adj_manager.get_target_difficulty(PowAlgorithm::Blake), + Ok(Difficulty::from(9)) + ); +} + +#[test] +fn test_median_timestamp() { + let network = Network::LocalNet; + let consensus_manager = ConsensusManagerBuilder::new(network).build(); + let store = create_mem_db(consensus_manager.clone()); + let diff_adj_manager = DiffAdjManager::new(store.clone(), &consensus_manager.consensus_constants()).unwrap(); + let pow_algos = vec![PowAlgorithm::Blake]; // GB default + create_test_pow_blockchain(&store, pow_algos, &consensus_manager.consensus_constants()); + let start_timestamp = store.fetch_block(0).unwrap().block().header.timestamp.clone(); + let mut timestamp = diff_adj_manager + .get_median_timestamp() + .expect("median returned an error"); + assert_eq!(timestamp, start_timestamp); + + let pow_algos = vec![PowAlgorithm::Blake]; + // lets add 1 + let tip = store.fetch_block(store.get_height().unwrap().unwrap()).unwrap().block; + append_to_pow_blockchain(&store, tip, pow_algos.clone(), &consensus_manager.consensus_constants()); + let mut prev_timestamp: EpochTime = + start_timestamp.increase(consensus_manager.consensus_constants().get_target_block_interval()); + timestamp = diff_adj_manager + .get_median_timestamp() + .expect("median returned an error"); + assert_eq!(timestamp, prev_timestamp); + // lets add 1 + let tip = store.fetch_block(store.get_height().unwrap().unwrap()).unwrap().block; + append_to_pow_blockchain(&store, tip, pow_algos.clone(), &consensus_manager.consensus_constants()); + prev_timestamp = start_timestamp.increase(consensus_manager.consensus_constants().get_target_block_interval()); + timestamp = diff_adj_manager + .get_median_timestamp() + .expect("median returned an error"); + assert_eq!(timestamp, prev_timestamp); + + // lets build up 11 blocks + for i in 4..12 { + let tip = store.fetch_block(store.get_height().unwrap().unwrap()).unwrap().block; + append_to_pow_blockchain(&store, tip, pow_algos.clone(), &consensus_manager.consensus_constants()); + prev_timestamp = + start_timestamp.increase(consensus_manager.consensus_constants().get_target_block_interval() * (i / 2)); + timestamp = diff_adj_manager + .get_median_timestamp() + .expect("median returned an error"); + assert_eq!(timestamp, prev_timestamp); + } + + // lets add many1 blocks + for _i in 1..20 { + let tip = store.fetch_block(store.get_height().unwrap().unwrap()).unwrap().block; + append_to_pow_blockchain(&store, tip, pow_algos.clone(), &consensus_manager.consensus_constants()); + prev_timestamp = prev_timestamp.increase(consensus_manager.consensus_constants().get_target_block_interval()); + timestamp = diff_adj_manager + .get_median_timestamp() + .expect("median returned an error"); + assert_eq!(timestamp, prev_timestamp); + } +} + +#[test] +fn test_median_timestamp_with_height() { + let network = Network::LocalNet; + let consensus_manager = ConsensusManagerBuilder::new(network).build(); + let store = create_mem_db(consensus_manager.clone()); + let diff_adj_manager = DiffAdjManager::new(store.clone(), &consensus_manager.consensus_constants()).unwrap(); + let pow_algos = vec![ + PowAlgorithm::Blake, // GB default + PowAlgorithm::Monero, + PowAlgorithm::Blake, + PowAlgorithm::Monero, + PowAlgorithm::Blake, + ]; + create_test_pow_blockchain(&store, pow_algos, &consensus_manager.consensus_constants()); + + let header0_timestamp = store.fetch_header(0).unwrap().timestamp; + let header1_timestamp = store.fetch_header(1).unwrap().timestamp; + let header2_timestamp = store.fetch_header(2).unwrap().timestamp; + + let timestamp = diff_adj_manager + .get_median_timestamp_at_height(0) + .expect("median returned an error"); + assert_eq!(timestamp, header0_timestamp); + + let timestamp = diff_adj_manager + .get_median_timestamp_at_height(3) + .expect("median returned an error"); + assert_eq!(timestamp, header2_timestamp); + + let timestamp = diff_adj_manager + .get_median_timestamp_at_height(2) + .expect("median returned an error"); + assert_eq!(timestamp, header1_timestamp); + + let timestamp = diff_adj_manager + .get_median_timestamp_at_height(4) + .expect("median returned an error"); + assert_eq!(timestamp, header2_timestamp); +} + +#[test] +fn test_median_timestamp_odd_order() { + let network = Network::LocalNet; + let consensus_manager = ConsensusManagerBuilder::new(network).build(); + let store = create_mem_db(consensus_manager.clone()); + let diff_adj_manager = DiffAdjManager::new(store.clone(), &consensus_manager.consensus_constants()).unwrap(); + let pow_algos = vec![PowAlgorithm::Blake]; // GB default + create_test_pow_blockchain(&store, pow_algos, &consensus_manager.consensus_constants()); + let start_timestamp = store.fetch_block(0).unwrap().block().header.timestamp.clone(); + let mut timestamp = diff_adj_manager + .get_median_timestamp() + .expect("median returned an error"); + assert_eq!(timestamp, start_timestamp); + let pow_algos = vec![PowAlgorithm::Blake]; + // lets add 1 + let tip = store.fetch_block(store.get_height().unwrap().unwrap()).unwrap().block; + append_to_pow_blockchain(&store, tip, pow_algos.clone(), &consensus_manager.consensus_constants()); + let mut prev_timestamp: EpochTime = + start_timestamp.increase(consensus_manager.consensus_constants().get_target_block_interval()); + timestamp = diff_adj_manager + .get_median_timestamp() + .expect("median returned an error"); + assert_eq!(timestamp, prev_timestamp); + + // lets add 1 that's further back then + let append_height = store.get_height().unwrap().unwrap(); + let prev_block = store.fetch_block(append_height).unwrap().block().clone(); + let new_block = chain_block(&prev_block, Vec::new(), &consensus_manager.consensus_constants()); + let mut new_block = store.calculate_mmr_roots(new_block).unwrap(); + new_block.header.timestamp = + start_timestamp.increase(&consensus_manager.consensus_constants().get_target_block_interval() / 2); + new_block.header.pow.pow_algo = PowAlgorithm::Blake; + store.add_block(new_block).unwrap(); + + prev_timestamp = start_timestamp.increase(consensus_manager.consensus_constants().get_target_block_interval() / 2); + timestamp = diff_adj_manager + .get_median_timestamp() + .expect("median returned an error"); + // Median timestamp should be block 3 and not block 2 + assert_eq!(timestamp, prev_timestamp); +} diff --git a/base_layer/core/tests/helpers/block_builders.rs b/base_layer/core/tests/helpers/block_builders.rs new file mode 100644 index 0000000000..fb80e93146 --- /dev/null +++ b/base_layer/core/tests/helpers/block_builders.rs @@ -0,0 +1,387 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use croaring::Bitmap; +use rand::{rngs::OsRng, RngCore}; +use tari_core::{ + blocks::{Block, BlockBuilder, BlockHeader, NewBlockTemplate}, + chain_storage::{BlockAddResult, BlockchainBackend, BlockchainDatabase, ChainStorageError, MemoryDatabase}, + consensus::{ConsensusConstants, ConsensusManager, ConsensusManagerBuilder, Network}, + helpers::MockBackend, + proof_of_work::Difficulty, + transactions::{ + helpers::{ + create_random_signature, + create_random_signature_from_s_key, + create_utxo, + spend_utxos, + TransactionSchema, + }, + tari_amount::MicroTari, + transaction::{ + KernelBuilder, + KernelFeatures, + OutputFeatures, + Transaction, + TransactionKernel, + TransactionOutput, + UnblindedOutput, + }, + types::{Commitment, CryptoFactories, HashDigest, HashOutput, PublicKey}, + }, +}; +use tari_crypto::{ + keys::PublicKey as PublicKeyTrait, + tari_utilities::{hash::Hashable, hex::Hex}, +}; +use tari_mmr::MutableMmr; + +const MAINNET: Network = Network::MainNet; + +fn create_coinbase( + factories: &CryptoFactories, + value: MicroTari, + maturity_height: u64, +) -> (TransactionOutput, TransactionKernel, UnblindedOutput) +{ + let features = OutputFeatures::create_coinbase(maturity_height); + let (mut utxo, key) = create_utxo(value, &factories, None); + utxo.features = features.clone(); + let excess = Commitment::from_public_key(&PublicKey::from_secret_key(&key)); + let (_pk, sig) = create_random_signature(0.into(), 0); + let kernel = KernelBuilder::new() + .with_signature(&sig) + .with_excess(&excess) + .with_features(KernelFeatures::COINBASE_KERNEL) + .build() + .unwrap(); + let output = UnblindedOutput::new(value, key, Some(features)); + (utxo, kernel, output) +} + +fn genesis_template( + factories: &CryptoFactories, + coinbase_value: MicroTari, + consensus_constants: &ConsensusConstants, +) -> (NewBlockTemplate, UnblindedOutput) +{ + let header = BlockHeader::new(0); + let (utxo, kernel, output) = create_coinbase(factories, coinbase_value, consensus_constants.coinbase_lock_height()); + let block = NewBlockTemplate::from( + BlockBuilder::new(consensus_constants) + .with_header(header) + .with_coinbase_utxo(utxo, kernel) + .build(), + ); + (block, output) +} + +// This is a helper function to generate and print out a block that can be used as the genesis block. +pub fn create_act_gen_block() { + let network = MAINNET; + let consensus_manager: ConsensusManager = ConsensusManagerBuilder::new(network).build(); + let factories = CryptoFactories::default(); + let mut header = BlockHeader::new(0); + let value = consensus_manager.emission_schedule().supply_at_block(0); + let (mut utxo, key) = create_utxo(value, &factories, None); + utxo.features = OutputFeatures::create_coinbase(1); + let (pk, sig) = create_random_signature_from_s_key(key.clone(), 0.into(), 0); + let excess = Commitment::from_public_key(&pk); + let kernel = KernelBuilder::new() + .with_signature(&sig) + .with_excess(&excess) + .with_features(KernelFeatures::COINBASE_KERNEL) + .build() + .unwrap(); + + let utxo_hash = utxo.hash(); + let rp = utxo.proof().hash(); + let kern = kernel.hash(); + header.kernel_mr = kern; + header.output_mr = utxo_hash; + header.range_proof_mr = rp; + let block = BlockBuilder::new(&consensus_manager.consensus_constants()) + .with_header(header) + .with_coinbase_utxo(utxo, kernel) + .build(); + println!("{}", &block); + dbg!(&key.to_hex()); + dbg!(&block.body.outputs()[0].proof.to_hex()); + assert!(false); // this is so that the output is printed +} + +/// Create a genesis block returning it with the spending key for the coinbase utxo +/// +/// Right now this function does not use consensus rules to generate the block. The coinbase output has an arbitrary +/// value, and the maturity is zero. +pub fn create_genesis_block( + factories: &CryptoFactories, + consensus_constants: &ConsensusConstants, +) -> (Block, UnblindedOutput) +{ + create_genesis_block_with_coinbase_value(factories, consensus_constants.emission_amounts().0, consensus_constants) +} + +// Calculate the MMR Merkle roots for the genesis block template and update the header. +fn update_genesis_block_mmr_roots(template: NewBlockTemplate) -> Result { + let NewBlockTemplate { header, mut body } = template; + // Make sure the body components are sorted. If they already are, this is a very cheap call. + body.sort(); + let kernel_hashes: Vec = body.kernels().iter().map(|k| k.hash()).collect(); + let out_hashes: Vec = body.outputs().iter().map(|out| out.hash()).collect(); + let rp_hashes: Vec = body.outputs().iter().map(|out| out.proof().hash()).collect(); + + let mut header = BlockHeader::from(header); + header.kernel_mr = MutableMmr::::new(kernel_hashes, Bitmap::create()).get_merkle_root()?; + header.output_mr = MutableMmr::::new(out_hashes, Bitmap::create()).get_merkle_root()?; + header.range_proof_mr = MutableMmr::::new(rp_hashes, Bitmap::create()).get_merkle_root()?; + Ok(Block { header, body }) +} + +/// Create a genesis block with the specified coinbase value, returning it with the spending key for the coinbase utxo. +pub fn create_genesis_block_with_coinbase_value( + factories: &CryptoFactories, + coinbase_value: MicroTari, + consensus_constants: &ConsensusConstants, +) -> (Block, UnblindedOutput) +{ + let (template, output) = genesis_template(&factories, coinbase_value, consensus_constants); + let mut block = update_genesis_block_mmr_roots(template).unwrap(); + find_header_with_achieved_difficulty(&mut block.header, Difficulty::from(1)); + (block, output) +} + +/// Create a Genesis block with additional utxos that are immediately available for spending. This is useful for +/// writing tests without having to add blocks just so the coinbase output can mature. +pub fn create_genesis_block_with_utxos( + factories: &CryptoFactories, + values: &[MicroTari], + consensus_constants: &ConsensusConstants, +) -> (Block, Vec) +{ + let (mut template, coinbase) = genesis_template(&factories, 100_000_000.into(), consensus_constants); + let outputs = values.iter().fold(vec![coinbase], |mut secrets, v| { + let (t, k) = create_utxo(*v, factories, None); + template.body.add_output(t); + secrets.push(UnblindedOutput::new(v.clone(), k, None)); + secrets + }); + let mut block = update_genesis_block_mmr_roots(template).unwrap(); + find_header_with_achieved_difficulty(&mut block.header, Difficulty::from(1)); + (block, outputs) +} + +/// Create a new block using the provided transactions that adds to the blockchain given in `prev_block`. +pub fn chain_block( + prev_block: &Block, + transactions: Vec, + consensus_constants: &ConsensusConstants, +) -> NewBlockTemplate +{ + let header = BlockHeader::from_previous(&prev_block.header); + NewBlockTemplate::from( + BlockBuilder::new(consensus_constants) + .with_header(header) + .with_transactions(transactions) + .build(), + ) +} + +/// Create a new block using the provided coinbase and transactions that adds to the blockchain given in `prev_block`. +pub fn chain_block_with_coinbase( + prev_block: &Block, + transactions: Vec, + coinbase_utxo: TransactionOutput, + coinbase_kernel: TransactionKernel, + consensus_constants: &ConsensusConstants, +) -> NewBlockTemplate +{ + let header = BlockHeader::from_previous(&prev_block.header); + NewBlockTemplate::from( + BlockBuilder::new(consensus_constants) + .with_header(header) + .with_transactions(transactions) + .with_coinbase_utxo(coinbase_utxo, coinbase_kernel) + .build(), + ) +} + +/// Create a new block with the provided transactions. The new MMR roots are calculated, and then the new block is +/// added to the database. The newly created block is returned as the result. +pub fn append_block( + db: &BlockchainDatabase, + prev_block: &Block, + txns: Vec, + consensus_constants: &ConsensusConstants, +) -> Result +{ + let template = chain_block(prev_block, txns, consensus_constants); + let mut block = db.calculate_mmr_roots(template)?; + block.header.nonce = OsRng.next_u64(); + find_header_with_achieved_difficulty(&mut block.header, Difficulty::from(1)); + db.add_block(block.clone())?; + Ok(block) +} + +/// Generate a new block using the given transaction schema and add it to the provided database. +/// The blocks and UTXO vectors are also updated with the info from the new block. +pub fn generate_new_block( + db: &mut BlockchainDatabase>, + blocks: &mut Vec, + outputs: &mut Vec>, + schemas: Vec, + consensus_constants: &ConsensusConstants, +) -> Result +{ + let mut txns = Vec::new(); + let mut block_utxos = Vec::new(); + let mut keys = Vec::new(); + for schema in schemas { + let (tx, mut utxos, param) = spend_utxos(schema); + txns.push(tx); + block_utxos.append(&mut utxos); + keys.push(param); + } + outputs.push(block_utxos); + generate_block(db, blocks, txns, consensus_constants) +} + +pub fn generate_new_block_with_achieved_difficulty( + db: &mut BlockchainDatabase>, + blocks: &mut Vec, + outputs: &mut Vec>, + schemas: Vec, + achieved_difficulty: Difficulty, + consensus_constants: &ConsensusConstants, +) -> Result +{ + let mut txns = Vec::new(); + let mut block_utxos = Vec::new(); + let mut keys = Vec::new(); + for schema in schemas { + let (tx, mut utxos, param) = spend_utxos(schema); + txns.push(tx); + block_utxos.append(&mut utxos); + keys.push(param); + } + outputs.push(block_utxos); + generate_block_with_achieved_difficulty(db, blocks, txns, achieved_difficulty, consensus_constants) +} + +/// Generate a new block using the given transaction schema and coinbase value and add it to the provided database. +/// The blocks and UTXO vectors are also updated with the info from the new block. +pub fn generate_new_block_with_coinbase( + db: &mut BlockchainDatabase>, + factories: &CryptoFactories, + blocks: &mut Vec, + outputs: &mut Vec>, + schemas: Vec, + coinbase_value: MicroTari, + consensus_constants: &ConsensusConstants, +) -> Result +{ + let mut txns = Vec::new(); + let mut block_utxos = Vec::new(); + let mut keys = Vec::new(); + for schema in schemas { + let (tx, mut utxos, param) = spend_utxos(schema); + txns.push(tx); + block_utxos.append(&mut utxos); + keys.push(param); + } + let (coinbase_utxo, coinbase_kernel, coinbase_output) = create_coinbase(factories, coinbase_value, 100); + block_utxos.push(coinbase_output); + + outputs.push(block_utxos); + generate_block_with_coinbase(db, blocks, txns, coinbase_utxo, coinbase_kernel, consensus_constants) +} + +pub fn find_header_with_achieved_difficulty(header: &mut BlockHeader, achieved_difficulty: Difficulty) { + while header.achieved_difficulty() != achieved_difficulty { + header.nonce += 1; + } +} + +/// Generate a block and add it to the database using the transactions provided. The header will be updated with the +/// correct MMR roots. +/// This function is not able to determine the unblinded outputs of a transaction, so if you are mixing using this +/// with [generate_new_block], you must update the unblinded UTXO vector yourself. +pub fn generate_block( + db: &mut BlockchainDatabase>, + blocks: &mut Vec, + transactions: Vec, + consensus_constants: &ConsensusConstants, +) -> Result +{ + let template = chain_block(&blocks.last().unwrap(), transactions, consensus_constants); + let new_block = db.calculate_mmr_roots(template)?; + let result = db.add_block(new_block.clone()); + if let Ok(BlockAddResult::Ok) = result { + blocks.push(new_block); + } + result +} + +pub fn generate_block_with_achieved_difficulty( + db: &mut BlockchainDatabase>, + blocks: &mut Vec, + transactions: Vec, + achieved_difficulty: Difficulty, + consensus_constants: &ConsensusConstants, +) -> Result +{ + let template = chain_block(&blocks.last().unwrap(), transactions, consensus_constants); + let mut new_block = db.calculate_mmr_roots(template)?; + new_block.header.nonce = OsRng.next_u64(); + find_header_with_achieved_difficulty(&mut new_block.header, achieved_difficulty); + let result = db.add_block(new_block.clone()); + if let Ok(BlockAddResult::Ok) = result { + blocks.push(new_block); + } + result +} + +/// Generate a block and add it to the database using the provided transactions and coinbase. The header will be updated +/// with the correct MMR roots. +pub fn generate_block_with_coinbase( + db: &mut BlockchainDatabase>, + blocks: &mut Vec, + transactions: Vec, + coinbase_utxo: TransactionOutput, + coinbase_kernel: TransactionKernel, + consensus_constants: &ConsensusConstants, +) -> Result +{ + let template = chain_block_with_coinbase( + &blocks.last().unwrap(), + transactions, + coinbase_utxo, + coinbase_kernel, + consensus_constants, + ); + let new_block = db.calculate_mmr_roots(template)?; + let result = db.add_block(new_block.clone()); + if let Ok(BlockAddResult::Ok) = result { + blocks.push(new_block); + } + result +} diff --git a/base_layer/core/tests/helpers/mod.rs b/base_layer/core/tests/helpers/mod.rs new file mode 100644 index 0000000000..fad7278a5c --- /dev/null +++ b/base_layer/core/tests/helpers/mod.rs @@ -0,0 +1,8 @@ +//! This module provides a large set of useful functions and utilities for creating and playing with aspects of the +//! Tari base blockchain. +//! There are macros, such as `txn_schema!` that help you to easily construct valid transactions in test blockchains, +//! through to functions that bootstrap entire blockchains in `sample_blockchains`. + +pub mod block_builders; +pub mod nodes; +pub mod sample_blockchains; diff --git a/base_layer/core/tests/helpers/nodes.rs b/base_layer/core/tests/helpers/nodes.rs new file mode 100644 index 0000000000..db824a927b --- /dev/null +++ b/base_layer/core/tests/helpers/nodes.rs @@ -0,0 +1,514 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use futures::Sink; +use rand::{distributions::Alphanumeric, rngs::OsRng, Rng}; +use std::{error::Error, iter, sync::Arc}; +use tari_comms::{ + peer_manager::{NodeIdentity, Peer, PeerFeatures, PeerFlags}, + transports::MemoryTransport, + CommsNode, +}; +use tari_comms_dht::{outbound::OutboundMessageRequester, Dht}; +use tari_core::{ + base_node::{ + chain_metadata_service::{ChainMetadataHandle, ChainMetadataServiceInitializer}, + service::{BaseNodeServiceConfig, BaseNodeServiceInitializer}, + LocalNodeCommsInterface, + OutboundNodeCommsInterface, + }, + blocks::Block, + chain_storage::{BlockchainDatabase, MemoryDatabase, Validators}, + consensus::{ConsensusManager, ConsensusManagerBuilder, Network}, + mempool::{ + Mempool, + MempoolConfig, + MempoolServiceConfig, + MempoolServiceInitializer, + MempoolValidators, + OutboundMempoolServiceInterface, + }, + proof_of_work::DiffAdjManager, + transactions::types::HashDigest, + validation::{mocks::MockValidator, transaction_validators::TxInputAndMaturityValidator, Validation}, +}; +use tari_mmr::MmrCacheConfig; +use tari_p2p::{ + comms_connector::{pubsub_connector, InboundDomainConnector, PeerMessage}, + initialization::{initialize_comms, CommsConfig}, + services::{ + comms_outbound::CommsOutboundServiceInitializer, + liveness::{LivenessConfig, LivenessInitializer}, + }, + transport::TransportType, +}; +use tari_service_framework::StackBuilder; +use tokio::runtime::Runtime; + +/// The NodeInterfaces is used as a container for providing access to all the services and interfaces of a single node. +pub struct NodeInterfaces { + pub node_identity: Arc, + pub outbound_nci: OutboundNodeCommsInterface, + pub local_nci: LocalNodeCommsInterface, + pub outbound_mp_interface: OutboundMempoolServiceInterface, + pub outbound_message_service: OutboundMessageRequester, + pub blockchain_db: BlockchainDatabase>, + pub mempool: Mempool>, + pub chain_metadata_handle: ChainMetadataHandle, + pub comms: CommsNode, +} + +/// The BaseNodeBuilder can be used to construct a test Base Node with all its relevant services and interfaces for +/// testing. +pub struct BaseNodeBuilder { + node_identity: Option>, + peers: Option>>, + base_node_service_config: Option, + mmr_cache_config: Option, + mempool_config: Option, + mempool_service_config: Option, + liveness_service_config: Option, + validators: Option>>, + consensus_manager: Option>>, + network: Network, +} + +impl BaseNodeBuilder { + /// Create a new BaseNodeBuilder + pub fn new(network: Network) -> Self { + Self { + node_identity: None, + peers: None, + base_node_service_config: None, + mmr_cache_config: None, + mempool_config: None, + mempool_service_config: None, + liveness_service_config: None, + validators: None, + consensus_manager: None, + network, + } + } + + /// Set node identity that should be used for the Base Node. If not specified a random identity will be used. + pub fn with_node_identity(mut self, node_identity: Arc) -> Self { + self.node_identity = Some(node_identity); + self + } + + /// Set the initial peers that will be available in the peer manager. + pub fn with_peers(mut self, peers: Vec>) -> Self { + self.peers = Some(peers); + self + } + + /// Set the configuration of the Base Node Service + pub fn with_base_node_service_config(mut self, config: BaseNodeServiceConfig) -> Self { + self.base_node_service_config = Some(config); + self + } + + /// Set the configuration of the MerkleChangeTracker of the Base Node Backend + pub fn with_mmr_cache_config(mut self, config: MmrCacheConfig) -> Self { + self.mmr_cache_config = Some(config); + self + } + + /// Set the configuration of the Mempool + pub fn with_mempool_config(mut self, config: MempoolConfig) -> Self { + self.mempool_config = Some(config); + self + } + + /// Set the configuration of the Mempool Service + pub fn with_mempool_service_config(mut self, config: MempoolServiceConfig) -> Self { + self.mempool_service_config = Some(config); + self + } + + /// Set the configuration of the Liveness Service + pub fn with_liveness_service_config(mut self, config: LivenessConfig) -> Self { + self.liveness_service_config = Some(config); + self + } + + pub fn with_validators( + mut self, + block: impl Validation> + 'static, + orphan: impl Validation> + 'static, + ) -> Self + { + let validators = Validators::new(block, orphan); + self.validators = Some(validators); + self + } + + /// Set the configuration of the Consensus Manager + pub fn with_consensus_manager(mut self, consensus_manager: ConsensusManager>) -> Self { + self.consensus_manager = Some(consensus_manager); + self + } + + /// Build the test base node and start its services. + pub fn start( + self, + runtime: &mut Runtime, + data_path: &str, + ) -> (NodeInterfaces, ConsensusManager>) + { + let mmr_cache_config = self.mmr_cache_config.unwrap_or(MmrCacheConfig { rewind_hist_len: 10 }); + let validators = self + .validators + .unwrap_or(Validators::new(MockValidator::new(true), MockValidator::new(true))); + let consensus_manager = self + .consensus_manager + .unwrap_or(ConsensusManagerBuilder::new(self.network).build()); + let db = MemoryDatabase::::new(mmr_cache_config); + let mut blockchain_db = BlockchainDatabase::new(db, consensus_manager.clone()).unwrap(); + blockchain_db.set_validators(validators); + let mempool_validator = MempoolValidators::new( + TxInputAndMaturityValidator::new(blockchain_db.clone()), + TxInputAndMaturityValidator::new(blockchain_db.clone()), + ); + let mempool = Mempool::new( + blockchain_db.clone(), + self.mempool_config.unwrap_or(MempoolConfig::default()), + mempool_validator, + ); + let diff_adj_manager = + DiffAdjManager::new(blockchain_db.clone(), &consensus_manager.consensus_constants()).unwrap(); + consensus_manager.set_diff_manager(diff_adj_manager).unwrap(); + let node_identity = self.node_identity.unwrap_or(random_node_identity()); + let (outbound_nci, local_nci, outbound_mp_interface, outbound_message_service, chain_metadata_handle, comms) = + setup_base_node_services( + runtime, + node_identity.clone(), + self.peers.unwrap_or(Vec::new()), + blockchain_db.clone(), + mempool.clone(), + consensus_manager.clone(), + self.base_node_service_config + .unwrap_or(BaseNodeServiceConfig::default()), + self.mempool_service_config.unwrap_or(MempoolServiceConfig::default()), + self.liveness_service_config.unwrap_or(LivenessConfig::default()), + data_path, + ); + + ( + NodeInterfaces { + node_identity, + outbound_nci, + local_nci, + outbound_mp_interface, + outbound_message_service, + blockchain_db, + mempool, + chain_metadata_handle, + comms, + }, + consensus_manager, + ) + } +} + +// Creates a network with two Base Nodes where each node in the network knows the other nodes in the network. +pub fn create_network_with_2_base_nodes( + runtime: &mut Runtime, + data_path: &str, +) -> ( + NodeInterfaces, + NodeInterfaces, + ConsensusManager>, +) +{ + let alice_node_identity = random_node_identity(); + let bob_node_identity = random_node_identity(); + + let network = Network::LocalNet; + let (alice_node, consensus_manager) = BaseNodeBuilder::new(network) + .with_node_identity(alice_node_identity.clone()) + .with_peers(vec![bob_node_identity.clone()]) + .start(runtime, data_path); + let (bob_node, consensus_manager) = BaseNodeBuilder::new(network) + .with_node_identity(bob_node_identity) + .with_peers(vec![alice_node_identity]) + .with_consensus_manager(consensus_manager) + .start(runtime, data_path); + + (alice_node, bob_node, consensus_manager) +} + +// Creates a network with two Base Nodes where each node in the network knows the other nodes in the network. +pub fn create_network_with_2_base_nodes_with_config( + runtime: &mut Runtime, + base_node_service_config: BaseNodeServiceConfig, + mmr_cache_config: MmrCacheConfig, + mempool_service_config: MempoolServiceConfig, + liveness_service_config: LivenessConfig, + consensus_manager: ConsensusManager>, + data_path: &str, +) -> ( + NodeInterfaces, + NodeInterfaces, + ConsensusManager>, +) +{ + let alice_node_identity = random_node_identity(); + let bob_node_identity = random_node_identity(); + let network = Network::LocalNet; + let (alice_node, consensus_manager) = BaseNodeBuilder::new(network) + .with_node_identity(alice_node_identity.clone()) + .with_peers(vec![bob_node_identity.clone()]) + .with_base_node_service_config(base_node_service_config) + .with_mmr_cache_config(mmr_cache_config) + .with_mempool_service_config(mempool_service_config) + .with_liveness_service_config(liveness_service_config) + .with_consensus_manager(consensus_manager) + .start(runtime, data_path); + let (bob_node, consensus_manager) = BaseNodeBuilder::new(network) + .with_node_identity(bob_node_identity) + .with_peers(vec![alice_node_identity]) + .with_base_node_service_config(base_node_service_config) + .with_mmr_cache_config(mmr_cache_config) + .with_mempool_service_config(mempool_service_config) + .with_liveness_service_config(liveness_service_config) + .with_consensus_manager(consensus_manager) + .start(runtime, data_path); + + let _ = runtime.block_on( + alice_node + .comms + .connection_manager() + .dial_peer(bob_node.node_identity.node_id().clone()), + ); + + (alice_node, bob_node, consensus_manager) +} + +// Creates a network with three Base Nodes where each node in the network knows the other nodes in the network. +pub fn create_network_with_3_base_nodes( + runtime: &mut Runtime, + data_path: &str, +) -> ( + NodeInterfaces, + NodeInterfaces, + NodeInterfaces, + ConsensusManager>, +) +{ + let network = Network::LocalNet; + let consensus_manager = ConsensusManagerBuilder::new(network).build(); + let mmr_cache_config = MmrCacheConfig { rewind_hist_len: 10 }; + create_network_with_3_base_nodes_with_config( + runtime, + BaseNodeServiceConfig::default(), + mmr_cache_config, + MempoolServiceConfig::default(), + LivenessConfig::default(), + consensus_manager, + data_path, + ) +} + +// Creates a network with three Base Nodes where each node in the network knows the other nodes in the network. +pub fn create_network_with_3_base_nodes_with_config( + runtime: &mut Runtime, + base_node_service_config: BaseNodeServiceConfig, + mmr_cache_config: MmrCacheConfig, + mempool_service_config: MempoolServiceConfig, + liveness_service_config: LivenessConfig, + consensus_manager: ConsensusManager>, + data_path: &str, +) -> ( + NodeInterfaces, + NodeInterfaces, + NodeInterfaces, + ConsensusManager>, +) +{ + let alice_node_identity = random_node_identity(); + let bob_node_identity = random_node_identity(); + let carol_node_identity = random_node_identity(); + let network = Network::LocalNet; + + let (alice_node, consensus_manager) = BaseNodeBuilder::new(network) + .with_node_identity(alice_node_identity.clone()) + .with_peers(vec![bob_node_identity.clone(), carol_node_identity.clone()]) + .with_base_node_service_config(base_node_service_config) + .with_mmr_cache_config(mmr_cache_config) + .with_mempool_service_config(mempool_service_config) + .with_liveness_service_config(liveness_service_config) + .with_consensus_manager(consensus_manager) + .start(runtime, data_path); + let (bob_node, consensus_manager) = BaseNodeBuilder::new(network) + .with_node_identity(bob_node_identity.clone()) + .with_peers(vec![alice_node_identity.clone(), carol_node_identity.clone()]) + .with_base_node_service_config(base_node_service_config) + .with_mmr_cache_config(mmr_cache_config) + .with_mempool_service_config(mempool_service_config) + .with_liveness_service_config(liveness_service_config) + .with_consensus_manager(consensus_manager) + .start(runtime, data_path); + let (carol_node, consensus_manager) = BaseNodeBuilder::new(network) + .with_node_identity(carol_node_identity.clone()) + .with_peers(vec![alice_node_identity, bob_node_identity.clone()]) + .with_base_node_service_config(base_node_service_config) + .with_mmr_cache_config(mmr_cache_config) + .with_mempool_service_config(mempool_service_config) + .with_liveness_service_config(liveness_service_config) + .with_consensus_manager(consensus_manager) + .start(runtime, data_path); + + runtime.block_on(async { + // Alice (pre)connects to bob and carol + let mut conn_man = alice_node.comms.connection_manager(); + let _ = conn_man.dial_peer(bob_node.node_identity.node_id().clone()).await; + let _ = conn_man.dial_peer(carol_node.node_identity.node_id().clone()).await; + + // Bob (pre)connects to carol + let mut conn_man = bob_node.comms.connection_manager(); + let _ = conn_man.dial_peer(carol_node.node_identity.node_id().clone()).await; + + // All node have an existing connection + }); + + (alice_node, bob_node, carol_node, consensus_manager) +} + +fn random_string(len: usize) -> String { + iter::repeat(()).map(|_| OsRng.sample(Alphanumeric)).take(len).collect() +} + +// Helper function for creating a random node indentity. +pub fn random_node_identity() -> Arc { + let next_port = MemoryTransport::acquire_next_memsocket_port(); + Arc::new( + NodeIdentity::random( + &mut OsRng, + format!("/memory/{}", next_port).parse().unwrap(), + PeerFeatures::COMMUNICATION_NODE, + ) + .unwrap(), + ) +} + +// Helper function for starting the comms stack. +async fn setup_comms_services( + node_identity: Arc, + peers: Vec>, + publisher: InboundDomainConnector, + data_path: &str, +) -> (CommsNode, Dht) +where + TSink: Sink> + Clone + Unpin + Send + Sync + 'static, + TSink::Error: Error + Send + Sync, +{ + let transport_type = TransportType::Memory { + listener_address: node_identity.public_address(), + }; + let comms_config = CommsConfig { + node_identity, + transport_type, + datastore_path: data_path.to_string(), + peer_database_name: random_string(8), + max_concurrent_inbound_tasks: 10, + outbound_buffer_size: 10, + dht: Default::default(), + }; + + let (comms, dht) = initialize_comms(comms_config, publisher).await.unwrap(); + + for p in peers { + let addr = p.public_address(); + comms + .async_peer_manager() + .add_peer(Peer::new( + p.public_key().clone(), + p.node_id().clone(), + addr.into(), + PeerFlags::empty(), + p.features().clone(), + )) + .await + .unwrap(); + } + + (comms, dht) +} + +// Helper function for starting the services of the Base node. +fn setup_base_node_services( + runtime: &mut Runtime, + node_identity: Arc, + peers: Vec>, + blockchain_db: BlockchainDatabase>, + mempool: Mempool>, + consensus_manager: ConsensusManager>, + base_node_service_config: BaseNodeServiceConfig, + mempool_service_config: MempoolServiceConfig, + liveness_service_config: LivenessConfig, + data_path: &str, +) -> ( + OutboundNodeCommsInterface, + LocalNodeCommsInterface, + OutboundMempoolServiceInterface, + OutboundMessageRequester, + ChainMetadataHandle, + CommsNode, +) +{ + let (publisher, subscription_factory) = pubsub_connector(runtime.handle().clone(), 100); + let subscription_factory = Arc::new(subscription_factory); + let (comms, dht) = runtime.block_on(setup_comms_services(node_identity, peers, publisher, data_path)); + + let fut = StackBuilder::new(runtime.handle().clone(), comms.shutdown_signal()) + .add_initializer(CommsOutboundServiceInitializer::new(dht.outbound_requester())) + .add_initializer(LivenessInitializer::new( + liveness_service_config, + Arc::clone(&subscription_factory), + dht.dht_requester(), + )) + .add_initializer(BaseNodeServiceInitializer::new( + subscription_factory.clone(), + blockchain_db, + mempool.clone(), + consensus_manager, + base_node_service_config, + )) + .add_initializer(MempoolServiceInitializer::new( + subscription_factory, + mempool, + mempool_service_config, + )) + .add_initializer(ChainMetadataServiceInitializer) + .finish(); + + let handles = runtime.block_on(fut).expect("Service initialization failed"); + ( + handles.get_handle::().unwrap(), + handles.get_handle::().unwrap(), + handles.get_handle::().unwrap(), + handles.get_handle::().unwrap(), + handles.get_handle::().unwrap(), + comms, + ) +} diff --git a/base_layer/core/tests/helpers/sample_blockchains.rs b/base_layer/core/tests/helpers/sample_blockchains.rs new file mode 100644 index 0000000000..16daca0415 --- /dev/null +++ b/base_layer/core/tests/helpers/sample_blockchains.rs @@ -0,0 +1,176 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +use crate::helpers::block_builders::{create_genesis_block, generate_new_block}; + +use tari_core::{ + blocks::Block, + chain_storage::{BlockchainDatabase, MemoryDatabase}, + consensus::{ConsensusConstantsBuilder, ConsensusManager, ConsensusManagerBuilder, Network}, + helpers::create_mem_db, + transactions::{ + tari_amount::{uT, T}, + transaction::UnblindedOutput, + types::{CryptoFactories, HashDigest}, + }, + txn_schema, +}; + +/// Create a simple 6 block memory-backed database. +/// Genesis block: +/// 100_000_000 -> utxo_0 (0.0) +/// Block 1: +/// 0.0 -> 60_000_000 (1.0) +/// -> change (1.1) +/// -> 100 fee/g +/// Block 2: +/// (1.0) -> 20_000_000 (2.0) +/// 5_000_000 (2.1) +/// 1_000_000 (2.2) +/// change (2.3) +/// 120 fee/g +/// (1.1) -> 15_000_000 (2.4) +/// change +/// 75 fee/g +/// Block 3: +/// (2.1) + (2.2) -> 6_000_000 - fee (3.0) +/// 25 uT fee/g +/// (2.4) + (2.3) -> 40_000_000 (3.1) +/// change (3.2) +/// 100 fee/g +/// Block 4: +/// (2.0) -> 1_000_000 (4.0) +/// -> 2_000_000 (4.1) +/// -> 3_000_000 (4.2) +/// -> 4_000_000 (4.3) +/// -> change (4.4) +/// Block 5: +/// (4.3 + 3.1)-> 20_000_000 (5.0) +/// -> 21_000_000 (5.1) +/// -> change (5.2) +/// (4.1) -> 500_000 (5.3) +/// -> 1_300_00 (5.4) +/// -> change (5.5) +/// (3.2) -> 500_000 (5.6) +/// -> change (5.7) +pub fn create_blockchain_db_no_cut_through() -> ( + BlockchainDatabase>, + Vec, + Vec>, + ConsensusManager>, +) { + let network = Network::LocalNet; + let (mut db, mut blocks, mut outputs, consensus_manager) = create_new_blockchain(network); + // Block 1 + let txs = vec![txn_schema!(from: vec![outputs[0][0].clone()], to: vec![60*T], fee: 100*uT)]; + assert!(generate_new_block( + &mut db, + &mut blocks, + &mut outputs, + txs, + &consensus_manager.consensus_constants() + ) + .is_ok()); + // Block 2 + let txs = vec![ + txn_schema!(from: vec![outputs[1][0].clone()], to: vec![20*T, 5*T, 1*T], fee: 120*uT), + txn_schema!(from: vec![outputs[1][1].clone()], to: vec![15*T], fee: 75*uT), + ]; + assert!(generate_new_block( + &mut db, + &mut blocks, + &mut outputs, + txs, + &consensus_manager.consensus_constants() + ) + .is_ok()); + // Block 3 + let txs = vec![ + txn_schema!(from: vec![outputs[2][1].clone(), outputs[2][2].clone()], to: vec![]), + txn_schema!(from: vec![outputs[2][4].clone(), outputs[2][3].clone()], to: vec![40*T], fee: 100*uT), + ]; + assert!(generate_new_block( + &mut db, + &mut blocks, + &mut outputs, + txs, + &consensus_manager.consensus_constants() + ) + .is_ok()); + // Block 4 + let txs = vec![txn_schema!( + from: vec![outputs[2][0].clone()], + to: vec![1 * T, 2 * T, 3 * T, 4 * T] + )]; + assert!(generate_new_block( + &mut db, + &mut blocks, + &mut outputs, + txs, + &consensus_manager.consensus_constants() + ) + .is_ok()); + // Block 5 + let txs = vec![ + txn_schema!( + from: vec![outputs[4][3].clone(), outputs[3][1].clone()], + to: vec![20 * T, 21 * T] + ), + txn_schema!( + from: vec![outputs[4][1].clone()], + to: vec![500_000 * uT, 1_300_000 * uT] + ), + txn_schema!(from: vec![outputs[3][2].clone()], to: vec![500_000 * uT]), + ]; + assert!(generate_new_block( + &mut db, + &mut blocks, + &mut outputs, + txs, + &consensus_manager.consensus_constants() + ) + .is_ok()); + (db, blocks, outputs, consensus_manager) +} + +/// Create a new blockchain database containing only the Genesis block +pub fn create_new_blockchain( + network: Network, +) -> ( + BlockchainDatabase>, + Vec, + Vec>, + ConsensusManager>, +) { + let factories = CryptoFactories::default(); + let consensus_constants = ConsensusConstantsBuilder::new(network) + .with_emission_amounts(100_000_000.into(), 0.999, 100.into()) + .build(); + let (block0, output) = create_genesis_block(&factories, &consensus_constants); + let consensus_manager = ConsensusManagerBuilder::new(network) + .with_consensus_constants(consensus_constants) + .with_block(block0.clone()) + .build(); + let db = create_mem_db(consensus_manager.clone()); + (db, vec![block0], vec![vec![output]], consensus_manager) +} diff --git a/base_layer/core/tests/mempool.rs b/base_layer/core/tests/mempool.rs new file mode 100644 index 0000000000..b84f7f2493 --- /dev/null +++ b/base_layer/core/tests/mempool.rs @@ -0,0 +1,859 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#[allow(dead_code)] +mod helpers; + +use helpers::{ + block_builders::{ + chain_block, + create_genesis_block, + create_genesis_block_with_coinbase_value, + find_header_with_achieved_difficulty, + generate_block, + generate_new_block, + }, + nodes::{create_network_with_2_base_nodes_with_config, create_network_with_3_base_nodes_with_config}, + sample_blockchains::create_new_blockchain, +}; +use std::{ops::Deref, sync::Arc, time::Duration}; +use tari_comms_dht::{domain_message::OutboundDomainMessage, outbound::OutboundEncryption}; +use tari_core::{ + base_node::service::BaseNodeServiceConfig, + consensus::{ConsensusConstantsBuilder, ConsensusManagerBuilder, Network}, + helpers::create_mem_db, + mempool::{ + Mempool, + MempoolConfig, + MempoolServiceConfig, + MempoolServiceError, + MempoolValidators, + TxStorageResponse, + }, + proof_of_work::Difficulty, + transactions::{ + helpers::{schema_to_transaction, spend_utxos}, + proto, + tari_amount::{uT, T}, + transaction::{OutputFeatures, Transaction}, + types::CryptoFactories, + }, + tx, + txn_schema, + validation::transaction_validators::TxInputAndMaturityValidator, +}; +use tari_mmr::MmrCacheConfig; +use tari_p2p::{services::liveness::LivenessConfig, tari_message::TariMessageType}; +use tari_test_utils::{async_assert_eventually, random::string}; +use tempdir::TempDir; +use tokio::runtime::Runtime; + +#[test] +fn test_insert_and_process_published_block() { + let network = Network::LocalNet; + let (mut store, mut blocks, mut outputs, consensus_manager) = create_new_blockchain(network); + let mempool_validator = MempoolValidators::new( + TxInputAndMaturityValidator::new(store.clone()), + TxInputAndMaturityValidator::new(store.clone()), + ); + let mempool = Mempool::new(store.clone(), MempoolConfig::default(), mempool_validator); + // Create a block with 4 outputs + let txs = vec![txn_schema!( + from: vec![outputs[0][0].clone()], + to: vec![2 * T, 2 * T, 2 * T, 2 * T] + )]; + generate_new_block( + &mut store, + &mut blocks, + &mut outputs, + txs, + &consensus_manager.consensus_constants(), + ) + .unwrap(); + // Create 6 new transactions to add to the mempool + let (orphan, _, _) = tx!(1*T, fee: 100*uT); + let orphan = Arc::new(orphan); + + let tx2 = txn_schema!(from: vec![outputs[1][0].clone()], to: vec![1*T], fee: 20*uT); + let tx2 = Arc::new(spend_utxos(tx2).0); + + let tx3 = txn_schema!( + from: vec![outputs[1][1].clone()], + to: vec![1*T], + fee: 20*uT, + lock: 4, + OutputFeatures::with_maturity(1) + ); + let tx3 = Arc::new(spend_utxos(tx3).0); + + let tx5 = txn_schema!( + from: vec![outputs[1][2].clone()], + to: vec![1*T], + fee: 20*uT, + lock: 3, + OutputFeatures::with_maturity(2) + ); + let tx5 = Arc::new(spend_utxos(tx5).0); + let tx6 = txn_schema!(from: vec![outputs[1][3].clone()], to: vec![1 * T]); + let tx6 = spend_utxos(tx6).0; + + mempool.insert(orphan.clone()).unwrap(); + mempool.insert(tx2.clone()).unwrap(); + mempool.insert(tx3.clone()).unwrap(); + mempool.insert(tx5.clone()).unwrap(); + mempool.process_published_block(&blocks[1]).unwrap(); + + assert_eq!( + mempool + .has_tx_with_excess_sig(&orphan.body.kernels()[0].excess_sig) + .unwrap(), + TxStorageResponse::OrphanPool + ); + assert_eq!( + mempool + .has_tx_with_excess_sig(&tx2.body.kernels()[0].excess_sig) + .unwrap(), + TxStorageResponse::UnconfirmedPool + ); + assert_eq!( + mempool + .has_tx_with_excess_sig(&tx3.body.kernels()[0].excess_sig) + .unwrap(), + TxStorageResponse::PendingPool + ); + + assert_eq!( + mempool + .has_tx_with_excess_sig(&tx5.body.kernels()[0].excess_sig) + .unwrap(), + TxStorageResponse::PendingPool + ); + assert_eq!( + mempool + .has_tx_with_excess_sig(&tx6.body.kernels()[0].excess_sig) + .unwrap(), + TxStorageResponse::NotStored + ); + + let snapshot_txs = mempool.snapshot().unwrap(); + assert_eq!(snapshot_txs.len(), 4); + assert!(snapshot_txs.contains(&orphan)); + assert!(snapshot_txs.contains(&tx2)); + assert!(snapshot_txs.contains(&tx3)); + assert!(snapshot_txs.contains(&tx5)); + + let stats = mempool.stats().unwrap(); + assert_eq!(stats.total_txs, 4); + assert_eq!(stats.unconfirmed_txs, 1); + assert_eq!(stats.orphan_txs, 1); + assert_eq!(stats.timelocked_txs, 2); + assert_eq!(stats.published_txs, 0); + assert_eq!(stats.total_weight, 36); + + // Spend tx2, so it goes in Reorg pool, tx5 matures, so goes in Unconfirmed pool + generate_block( + &mut store, + &mut blocks, + vec![tx2.deref().clone()], + &consensus_manager.consensus_constants(), + ) + .unwrap(); + mempool.process_published_block(&blocks[2]).unwrap(); + + assert_eq!( + mempool + .has_tx_with_excess_sig(&orphan.body.kernels()[0].excess_sig) + .unwrap(), + TxStorageResponse::OrphanPool + ); + assert_eq!( + mempool + .has_tx_with_excess_sig(&tx2.body.kernels()[0].excess_sig) + .unwrap(), + TxStorageResponse::ReorgPool + ); + assert_eq!( + mempool + .has_tx_with_excess_sig(&tx3.body.kernels()[0].excess_sig) + .unwrap(), + TxStorageResponse::PendingPool + ); + assert_eq!( + mempool + .has_tx_with_excess_sig(&tx5.body.kernels()[0].excess_sig) + .unwrap(), + TxStorageResponse::UnconfirmedPool + ); + assert_eq!( + mempool + .has_tx_with_excess_sig(&tx6.body.kernels()[0].excess_sig) + .unwrap(), + TxStorageResponse::NotStored + ); + + let snapshot_txs = mempool.snapshot().unwrap(); + assert_eq!(snapshot_txs.len(), 3); + assert!(snapshot_txs.contains(&orphan)); + assert!(snapshot_txs.contains(&tx3)); + assert!(snapshot_txs.contains(&tx5)); + + let stats = mempool.stats().unwrap(); + assert_eq!(stats.total_txs, 4); + assert_eq!(stats.unconfirmed_txs, 1); + assert_eq!(stats.orphan_txs, 1); + assert_eq!(stats.timelocked_txs, 1); + assert_eq!(stats.published_txs, 1); + assert_eq!(stats.total_weight, 36); +} + +#[test] +fn test_retrieve() { + let network = Network::LocalNet; + let (mut store, mut blocks, mut outputs, consensus_manager) = create_new_blockchain(network); + let mempool_validator = MempoolValidators::new( + TxInputAndMaturityValidator::new(store.clone()), + TxInputAndMaturityValidator::new(store.clone()), + ); + let mempool = Mempool::new(store.clone(), MempoolConfig::default(), mempool_validator); + let txs = vec![txn_schema!( + from: vec![outputs[0][0].clone()], + to: vec![1 * T, 1 * T, 1 * T, 1 * T, 1 * T, 1 * T, 1 * T] + )]; + // "Mine" Block 1 + generate_new_block( + &mut store, + &mut blocks, + &mut outputs, + txs, + &consensus_manager.consensus_constants(), + ) + .unwrap(); + mempool.process_published_block(&blocks[1]).unwrap(); + // 1-Block, 8 UTXOs, empty mempool + let txs = vec![ + txn_schema!(from: vec![outputs[1][0].clone()], to: vec![], fee: 30*uT), + txn_schema!(from: vec![outputs[1][1].clone()], to: vec![], fee: 20*uT), + txn_schema!(from: vec![outputs[1][2].clone()], to: vec![], fee: 40*uT), + txn_schema!(from: vec![outputs[1][3].clone()], to: vec![], fee: 50*uT), + txn_schema!(from: vec![outputs[1][4].clone()], to: vec![], fee: 20*uT, lock: 2, OutputFeatures::default()), + txn_schema!(from: vec![outputs[1][5].clone()], to: vec![], fee: 20*uT, lock: 3, OutputFeatures::default()), + // Will be time locked when a tx is added to mempool with this as an input: + txn_schema!(from: vec![outputs[1][6].clone()], to: vec![800_000*uT], fee: 60*uT, lock: 0, + OutputFeatures::with_maturity(4)), + // Will be time locked when a tx is added to mempool with this as an input: + txn_schema!(from: vec![outputs[1][7].clone()], to: vec![800_000*uT], fee: 25*uT, lock: 0, + OutputFeatures::with_maturity(3)), + ]; + let (tx, utxos) = schema_to_transaction(&txs); + tx.iter().for_each(|t| { + mempool.insert(t.clone()).unwrap(); + }); + // 1-block, 8 UTXOs, 8 txs in mempool + let weight = tx[6].calculate_weight() + tx[2].calculate_weight() + tx[3].calculate_weight(); + let retrieved_txs = mempool.retrieve(weight).unwrap(); + assert_eq!(retrieved_txs.len(), 3); + assert!(retrieved_txs.contains(&tx[6])); + assert!(retrieved_txs.contains(&tx[2])); + assert!(retrieved_txs.contains(&tx[3])); + let stats = mempool.stats().unwrap(); + println!("After block 1: {:?}", stats); + assert_eq!(stats.unconfirmed_txs, 7); + assert_eq!(stats.timelocked_txs, 1); + assert_eq!(stats.published_txs, 0); + + let block2_txns = vec![ + tx[0].deref().clone(), + tx[1].deref().clone(), + tx[2].deref().clone(), + tx[6].deref().clone(), + tx[7].deref().clone(), + ]; + // "Mine" block 2 + generate_block( + &mut store, + &mut blocks, + block2_txns, + &consensus_manager.consensus_constants(), + ) + .unwrap(); + println!("{}", blocks[2]); + outputs.push(utxos); + mempool.process_published_block(&blocks[2]).unwrap(); + // 2-blocks, 2 unconfirmed txs in mempool, 0 time locked (tx5 time-lock will expire) + let stats = mempool.stats().unwrap(); + assert_eq!(stats.unconfirmed_txs, 3); + assert_eq!(stats.timelocked_txs, 0); + assert_eq!(stats.published_txs, 5); + // Create transactions wih time-locked inputs + let txs = vec![ + txn_schema!(from: vec![outputs[2][6].clone()], to: vec![], fee: 80*uT), + // account for change output + txn_schema!(from: vec![outputs[2][8].clone()], to: vec![], fee: 40*uT), + ]; + let (tx2, _) = schema_to_transaction(&txs); + tx2.iter().for_each(|t| { + mempool.insert(t.clone()).unwrap(); + }); + // 2 blocks, 3 unconfirmed txs in mempool, 2 time locked + + // Top 2 txs are tx[3] (fee/g = 50) and tx2[1] (fee/g = 40). tx2[0] (fee/g = 80) is still not matured. + let weight = tx[3].calculate_weight() + tx2[1].calculate_weight(); + let retrieved_txs = mempool.retrieve(weight).unwrap(); + let stats = mempool.stats().unwrap(); + + assert_eq!(stats.unconfirmed_txs, 4); + assert_eq!(stats.timelocked_txs, 1); + assert_eq!(stats.published_txs, 5); + assert_eq!(retrieved_txs.len(), 2); + assert!(retrieved_txs.contains(&tx[3])); + assert!(retrieved_txs.contains(&tx2[1])); +} + +#[test] +fn test_reorg() { + let network = Network::LocalNet; + let (mut db, mut blocks, mut outputs, consensus_manager) = create_new_blockchain(network); + let mempool_validator = MempoolValidators::new( + TxInputAndMaturityValidator::new(db.clone()), + TxInputAndMaturityValidator::new(db.clone()), + ); + let mempool = Mempool::new(db.clone(), MempoolConfig::default(), mempool_validator); + + // "Mine" Block 1 + let txs = vec![txn_schema!(from: vec![outputs[0][0].clone()], to: vec![1 * T, 1 * T])]; + generate_new_block( + &mut db, + &mut blocks, + &mut outputs, + txs, + &consensus_manager.consensus_constants(), + ) + .unwrap(); + mempool.process_published_block(&blocks[1]).unwrap(); + + // "Mine" block 2 + let schemas = vec![ + txn_schema!(from: vec![outputs[1][0].clone()], to: vec![]), + txn_schema!(from: vec![outputs[1][1].clone()], to: vec![]), + txn_schema!(from: vec![outputs[1][2].clone()], to: vec![]), + ]; + let (txns2, utxos) = schema_to_transaction(&schemas); + outputs.push(utxos); + txns2.iter().for_each(|tx| { + mempool.insert(tx.clone()).unwrap(); + }); + let stats = mempool.stats().unwrap(); + assert_eq!(stats.unconfirmed_txs, 3); + let txns2 = txns2.iter().map(|t| t.deref().clone()).collect(); + generate_block(&mut db, &mut blocks, txns2, &consensus_manager.consensus_constants()).unwrap(); + mempool.process_published_block(&blocks[2]).unwrap(); + + // "Mine" block 3 + let schemas = vec![ + txn_schema!(from: vec![outputs[2][0].clone()], to: vec![]), + txn_schema!(from: vec![outputs[2][1].clone()], to: vec![], fee: 25*uT, lock: 5, OutputFeatures::default()), + txn_schema!(from: vec![outputs[2][2].clone()], to: vec![], fee: 25*uT), + ]; + let (txns3, utxos) = schema_to_transaction(&schemas); + outputs.push(utxos); + txns3.iter().for_each(|tx| { + mempool.insert(tx.clone()).unwrap(); + }); + let txns3: Vec = txns3.iter().map(|t| t.deref().clone()).collect(); + + generate_block( + &mut db, + &mut blocks, + vec![txns3[0].clone(), txns3[2].clone()], + &consensus_manager.consensus_constants(), + ) + .unwrap(); + mempool.process_published_block(&blocks[3]).unwrap(); + + let stats = mempool.stats().unwrap(); + assert_eq!(stats.unconfirmed_txs, 0); + assert_eq!(stats.timelocked_txs, 1); + + db.rewind_to_height(2).unwrap(); + + mempool.process_reorg(vec![blocks[3].clone()], vec![]).unwrap(); + let stats = mempool.stats().unwrap(); + assert_eq!(stats.unconfirmed_txs, 2); + assert_eq!(stats.timelocked_txs, 1); + assert_eq!(stats.published_txs, 3); +} + +#[test] +fn test_orphaned_mempool_transactions() { + let network = Network::LocalNet; + let (store, mut blocks, mut outputs, consensus_manager) = create_new_blockchain(network); + // A parallel store that will "mine" the orphan chain + let mut miner = create_mem_db(consensus_manager.clone()); + let schemas = vec![txn_schema!( + from: vec![outputs[0][0].clone()], + to: vec![2 * T, 2 * T, 2 * T, 2 * T, 2 * T] + )]; + generate_new_block( + &mut miner, + &mut blocks, + &mut outputs, + schemas.clone(), + &consensus_manager.consensus_constants(), + ) + .unwrap(); + store.add_block(blocks[1].clone()).unwrap(); + let schemas = vec![ + txn_schema!(from: vec![outputs[1][0].clone(), outputs[1][1].clone()], to: vec![], fee: 500*uT, lock: 1100, OutputFeatures::default()), + txn_schema!(from: vec![outputs[1][2].clone()], to: vec![], fee: 300*uT, lock: 1700, OutputFeatures::default()), + txn_schema!(from: vec![outputs[1][3].clone()], to: vec![], fee: 100*uT), + ]; + let (txns, _) = schema_to_transaction(&schemas.clone()); + generate_new_block( + &mut miner, + &mut blocks, + &mut outputs, + schemas, + &consensus_manager.consensus_constants(), + ) + .unwrap(); + // tx3 and tx4 depend on tx0 and tx1 + let schemas = vec![ + txn_schema!(from: vec![outputs[2][0].clone()], to: vec![], fee: 200*uT), + txn_schema!(from: vec![outputs[2][2].clone()], to: vec![], fee: 500*uT, lock: 1000, OutputFeatures::default()), + txn_schema!(from: vec![outputs[1][4].clone()], to: vec![], fee: 600*uT, lock: 5200, OutputFeatures::default()), + ]; + let (txns2, _) = schema_to_transaction(&schemas.clone()); + generate_new_block( + &mut miner, + &mut blocks, + &mut outputs, + schemas, + &consensus_manager.consensus_constants(), + ) + .unwrap(); + let mempool_validator = MempoolValidators::new( + TxInputAndMaturityValidator::new(store.clone()), + TxInputAndMaturityValidator::new(store.clone()), + ); + let mempool = Mempool::new(store.clone(), MempoolConfig::default(), mempool_validator); + // There are 2 orphan txs + vec![txns[2].clone(), txns2[0].clone(), txns2[1].clone(), txns2[2].clone()] + .into_iter() + .for_each(|t| mempool.insert(t).unwrap()); + + let stats = mempool.stats().unwrap(); + assert_eq!(stats.total_txs, 4); + assert_eq!(stats.unconfirmed_txs, 1); + assert_eq!(stats.timelocked_txs, 1); + assert_eq!(stats.orphan_txs, 2); + store.add_block(blocks[1].clone()).unwrap(); + store.add_block(blocks[2].clone()).unwrap(); + mempool.process_published_block(&blocks[1]).unwrap(); + mempool.process_published_block(&blocks[2]).unwrap(); + let stats = mempool.stats().unwrap(); + assert_eq!(stats.total_txs, 3); + assert_eq!(stats.unconfirmed_txs, 1); + assert_eq!(stats.orphan_txs, 0); +} + +#[test] +fn request_response_get_stats() { + let factories = CryptoFactories::default(); + let mut runtime = Runtime::new().unwrap(); + let temp_dir = TempDir::new(string(8).as_str()).unwrap(); + let network = Network::LocalNet; + let consensus_constants = ConsensusConstantsBuilder::new(network) + .with_coinbase_lockheight(100) + .with_emission_amounts(100_000_000.into(), 0.999, 100.into()) + .build(); + let (block0, utxo) = create_genesis_block(&factories, &consensus_constants); + let consensus_manager = ConsensusManagerBuilder::new(network) + .with_consensus_constants(consensus_constants) + .with_block(block0.clone()) + .build(); + let (mut alice, bob, _consensus_manager) = create_network_with_2_base_nodes_with_config( + &mut runtime, + BaseNodeServiceConfig::default(), + MmrCacheConfig { rewind_hist_len: 10 }, + MempoolServiceConfig::default(), + LivenessConfig::default(), + consensus_manager, + temp_dir.path().to_str().unwrap(), + ); + + // Create a tx spending the genesis output. Then create 2 orphan txs + let (tx1, _, _) = spend_utxos(txn_schema!(from: vec![utxo], to: vec![2 * T, 2 * T, 2 * T])); + let tx1 = Arc::new(tx1); + let (orphan1, _, _) = tx!(1*T, fee: 100*uT); + let orphan1 = Arc::new(orphan1); + let (orphan2, _, _) = tx!(2*T, fee: 200*uT); + let orphan2 = Arc::new(orphan2); + + bob.mempool.insert(tx1.clone()).unwrap(); + bob.mempool.insert(orphan1.clone()).unwrap(); + bob.mempool.insert(orphan2.clone()).unwrap(); + + // The coinbase tx cannot be spent until maturity, so rxn1 will be in the timelocked pool. The other 2 txns are + // orphans. + let stats = bob.mempool.stats().unwrap(); + assert_eq!(stats.total_txs, 3); + assert_eq!(stats.orphan_txs, 2); + assert_eq!(stats.unconfirmed_txs, 0); + assert_eq!(stats.timelocked_txs, 1); + assert_eq!(stats.published_txs, 0); + assert_eq!(stats.total_weight, 35); + + runtime.block_on(async { + // Alice will request mempool stats from Bob, and thus should be identical + let received_stats = alice.outbound_mp_interface.get_stats().await.unwrap(); + assert_eq!(received_stats.total_txs, 3); + assert_eq!(received_stats.unconfirmed_txs, 0); + assert_eq!(received_stats.orphan_txs, 2); + assert_eq!(received_stats.timelocked_txs, 1); + assert_eq!(received_stats.published_txs, 0); + assert_eq!(received_stats.total_weight, 35); + + alice.comms.shutdown().await; + bob.comms.shutdown().await; + }); +} + +#[test] +fn request_response_get_tx_state_with_excess_sig() { + let factories = CryptoFactories::default(); + let mut runtime = Runtime::new().unwrap(); + let temp_dir = TempDir::new(string(8).as_str()).unwrap(); + let network = Network::LocalNet; + let consensus_constants = ConsensusConstantsBuilder::new(network) + .with_coinbase_lockheight(100) + .with_emission_amounts(100_000_000.into(), 0.999, 100.into()) + .build(); + let (block0, utxo) = create_genesis_block(&factories, &consensus_constants); + let consensus_manager = ConsensusManagerBuilder::new(network) + .with_consensus_constants(consensus_constants) + .with_block(block0.clone()) + .build(); + let (mut alice_node, bob_node, carol_node, _consensus_manager) = create_network_with_3_base_nodes_with_config( + &mut runtime, + BaseNodeServiceConfig::default(), + MmrCacheConfig { rewind_hist_len: 10 }, + MempoolServiceConfig::default(), + LivenessConfig::default(), + consensus_manager, + temp_dir.path().to_str().unwrap(), + ); + + let (tx, _, _) = spend_utxos(txn_schema!(from: vec![utxo.clone()], to: vec![2 * T, 2 * T, 2 * T])); + let (unpublished_tx, _, _) = spend_utxos(txn_schema!(from: vec![utxo], to: vec![3 * T])); + let (orphan_tx, _, _) = tx!(1*T, fee: 100*uT); + let tx = Arc::new(tx); + let orphan_tx = Arc::new(orphan_tx); + bob_node.mempool.insert(tx.clone()).unwrap(); + carol_node.mempool.insert(tx.clone()).unwrap(); + bob_node.mempool.insert(orphan_tx.clone()).unwrap(); + carol_node.mempool.insert(orphan_tx.clone()).unwrap(); + + // Check that the transactions are in the expected pools. + // Spending the coinbase utxo will be in the pending pool, because cb utxos have a maturity. + // The orphan tx will be in the orphan pool, while the unadded tx won't be found + runtime.block_on(async { + let tx_excess_sig = tx.body.kernels()[0].excess_sig.clone(); + let unpublished_tx_excess_sig = unpublished_tx.body.kernels()[0].excess_sig.clone(); + let orphan_tx_excess_sig = orphan_tx.body.kernels()[0].excess_sig.clone(); + assert_eq!( + alice_node + .outbound_mp_interface + .get_tx_state_with_excess_sig(tx_excess_sig) + .await + .unwrap(), + TxStorageResponse::PendingPool + ); + assert_eq!( + alice_node + .outbound_mp_interface + .get_tx_state_with_excess_sig(unpublished_tx_excess_sig) + .await + .unwrap(), + TxStorageResponse::NotStored + ); + assert_eq!( + alice_node + .outbound_mp_interface + .get_tx_state_with_excess_sig(orphan_tx_excess_sig) + .await + .unwrap(), + TxStorageResponse::OrphanPool + ); + + alice_node.comms.shutdown().await; + bob_node.comms.shutdown().await; + carol_node.comms.shutdown().await; + }); +} + +#[test] +fn receive_and_propagate_transaction() { + let factories = CryptoFactories::default(); + let mut runtime = Runtime::new().unwrap(); + let temp_dir = TempDir::new(string(8).as_str()).unwrap(); + let network = Network::LocalNet; + let consensus_constants = ConsensusConstantsBuilder::new(network) + .with_coinbase_lockheight(100) + .with_emission_amounts(100_000_000.into(), 0.999, 100.into()) + .build(); + let (block0, utxo) = create_genesis_block(&factories, &consensus_constants); + let consensus_manager = ConsensusManagerBuilder::new(network) + .with_consensus_constants(consensus_constants) + .with_block(block0.clone()) + .build(); + let (mut alice_node, bob_node, carol_node, _consensus_manager) = create_network_with_3_base_nodes_with_config( + &mut runtime, + BaseNodeServiceConfig::default(), + MmrCacheConfig { rewind_hist_len: 10 }, + MempoolServiceConfig::default(), + LivenessConfig::default(), + consensus_manager, + temp_dir.path().to_str().unwrap(), + ); + + let (tx, _, _) = spend_utxos(txn_schema!(from: vec![utxo], to: vec![2 * T, 2 * T, 2 * T])); + let (orphan, _, _) = tx!(1*T, fee: 100*uT); + let tx_excess_sig = tx.body.kernels()[0].excess_sig.clone(); + let orphan_excess_sig = orphan.body.kernels()[0].excess_sig.clone(); + assert!(alice_node.mempool.insert(Arc::new(tx.clone())).is_ok()); + assert!(alice_node.mempool.insert(Arc::new(orphan.clone())).is_ok()); + + runtime.block_on(async { + alice_node + .outbound_message_service + .send_direct( + bob_node.node_identity.public_key().clone(), + OutboundEncryption::None, + OutboundDomainMessage::new(TariMessageType::NewTransaction, proto::types::Transaction::from(tx)), + ) + .await + .unwrap(); + alice_node + .outbound_message_service + .send_direct( + carol_node.node_identity.public_key().clone(), + OutboundEncryption::None, + OutboundDomainMessage::new(TariMessageType::NewTransaction, proto::types::Transaction::from(orphan)), + ) + .await + .unwrap(); + + async_assert_eventually!( + bob_node.mempool.has_tx_with_excess_sig(&tx_excess_sig).unwrap(), + expect = TxStorageResponse::PendingPool, + max_attempts = 20, + interval = Duration::from_millis(1000) + ); + async_assert_eventually!( + bob_node.mempool.has_tx_with_excess_sig(&orphan_excess_sig).unwrap(), + expect = TxStorageResponse::OrphanPool, + max_attempts = 10, + interval = Duration::from_millis(1000) + ); + async_assert_eventually!( + carol_node.mempool.has_tx_with_excess_sig(&tx_excess_sig).unwrap(), + expect = TxStorageResponse::PendingPool, + max_attempts = 10, + interval = Duration::from_millis(1000) + ); + async_assert_eventually!( + carol_node.mempool.has_tx_with_excess_sig(&orphan_excess_sig).unwrap(), + expect = TxStorageResponse::OrphanPool, + max_attempts = 10, + interval = Duration::from_millis(1000) + ); + + alice_node.comms.shutdown().await; + bob_node.comms.shutdown().await; + carol_node.comms.shutdown().await; + }); +} + +#[test] +fn service_request_timeout() { + let mut runtime = Runtime::new().unwrap(); + let network = Network::LocalNet; + let consensus_manager = ConsensusManagerBuilder::new(network).build(); + let mempool_service_config = MempoolServiceConfig { + request_timeout: Duration::from_millis(1), + }; + let temp_dir = TempDir::new(string(8).as_str()).unwrap(); + let (mut alice_node, bob_node, _consensus_manager) = create_network_with_2_base_nodes_with_config( + &mut runtime, + BaseNodeServiceConfig::default(), + MmrCacheConfig::default(), + mempool_service_config, + LivenessConfig::default(), + consensus_manager, + temp_dir.path().to_str().unwrap(), + ); + + runtime.block_on(async { + match alice_node.outbound_mp_interface.get_stats().await { + Err(MempoolServiceError::RequestTimedOut) => assert!(true), + _ => assert!(false), + } + + alice_node.comms.shutdown().await; + bob_node.comms.shutdown().await; + }); +} + +#[test] +fn block_event_and_reorg_event_handling() { + let factories = CryptoFactories::default(); + let network = Network::LocalNet; + let consensus_constants = network.create_consensus_constants(); + + let mut runtime = Runtime::new().unwrap(); + let temp_dir = TempDir::new(string(8).as_str()).unwrap(); + let (block0, utxos0) = + create_genesis_block_with_coinbase_value(&factories, 100_000_000.into(), &consensus_constants); + let consensus_manager = ConsensusManagerBuilder::new(network) + .with_consensus_constants(consensus_constants) + .with_block(block0.clone()) + .build(); + let (alice, mut bob, consensus_manager) = create_network_with_2_base_nodes_with_config( + &mut runtime, + BaseNodeServiceConfig::default(), + MmrCacheConfig { rewind_hist_len: 10 }, + MempoolServiceConfig::default(), + LivenessConfig::default(), + consensus_manager, + temp_dir.path().to_str().unwrap(), + ); + + // Bob creates Block 1 and sends it to Alice. Alice adds it to her chain and creates a block event that the Mempool + // service will receive. + let (tx1, utxos1) = schema_to_transaction(&vec![txn_schema!(from: vec![utxos0.clone()], to: vec![1 * T, 1 * T])]); + let (txs2, _utxos2) = schema_to_transaction(&vec![ + txn_schema!(from: vec![utxos1[0].clone()], to: vec![400_000 * uT, 590_000 * uT]), + txn_schema!(from: vec![utxos1[1].clone()], to: vec![750_000 * uT, 240_000 * uT]), + ]); + let (txs3, _utxos3) = schema_to_transaction(&vec![ + txn_schema!(from: vec![utxos1[0].clone()], to: vec![100_000 * uT, 890_000 * uT]), + txn_schema!(from: vec![utxos1[1].clone()], to: vec![850_000 * uT, 140_000 * uT]), + ]); + let tx1 = (*tx1[0]).clone(); + let tx2 = (*txs2[0]).clone(); + let tx3 = (*txs2[1]).clone(); + let tx4 = (*txs3[0]).clone(); + let tx5 = (*txs3[1]).clone(); + let tx1_excess_sig = tx1.body.kernels()[0].excess_sig.clone(); + let tx2_excess_sig = tx2.body.kernels()[0].excess_sig.clone(); + let tx3_excess_sig = tx3.body.kernels()[0].excess_sig.clone(); + let tx4_excess_sig = tx4.body.kernels()[0].excess_sig.clone(); + let tx5_excess_sig = tx5.body.kernels()[0].excess_sig.clone(); + alice.mempool.insert(Arc::new(tx1.clone())).unwrap(); + bob.mempool.insert(Arc::new(tx1.clone())).unwrap(); + alice.mempool.insert(Arc::new(tx2.clone())).unwrap(); + alice.mempool.insert(Arc::new(tx3.clone())).unwrap(); + alice.mempool.insert(Arc::new(tx4.clone())).unwrap(); + alice.mempool.insert(Arc::new(tx5.clone())).unwrap(); + bob.mempool.insert(Arc::new(tx2.clone())).unwrap(); + bob.mempool.insert(Arc::new(tx3.clone())).unwrap(); + bob.mempool.insert(Arc::new(tx4.clone())).unwrap(); + bob.mempool.insert(Arc::new(tx5.clone())).unwrap(); + + // These blocks are manually constructed to allow the block event system to be used. + let mut block1 = bob + .blockchain_db + .calculate_mmr_roots(chain_block( + &block0, + vec![tx1], + &consensus_manager.consensus_constants(), + )) + .unwrap(); + find_header_with_achieved_difficulty(&mut block1.header, Difficulty::from(1)); + + let mut block2a = bob + .blockchain_db + .calculate_mmr_roots(chain_block( + &block1, + vec![tx2, tx3], + &consensus_manager.consensus_constants(), + )) + .unwrap(); + find_header_with_achieved_difficulty(&mut block2a.header, Difficulty::from(1)); + // Block2b also builds on Block1 but has a stronger PoW + let mut block2b = bob + .blockchain_db + .calculate_mmr_roots(chain_block( + &block1, + vec![tx4, tx5], + &consensus_manager.consensus_constants(), + )) + .unwrap(); + find_header_with_achieved_difficulty(&mut block2b.header, Difficulty::from(10)); + + runtime.block_on(async { + // Add Block1 - tx1 will be moved to the ReorgPool. + assert!(bob.local_nci.submit_block(block1.clone()).await.is_ok()); + async_assert_eventually!( + alice.mempool.has_tx_with_excess_sig(&tx1_excess_sig).unwrap(), + expect = TxStorageResponse::ReorgPool, + max_attempts = 20, + interval = Duration::from_millis(1000) + ); + + // Add Block2a - tx4 and tx5 will be discarded as double spends. + assert!(bob.local_nci.submit_block(block2a.clone()).await.is_ok()); + async_assert_eventually!( + alice.mempool.has_tx_with_excess_sig(&tx2_excess_sig).unwrap(), + expect = TxStorageResponse::ReorgPool, + max_attempts = 20, + interval = Duration::from_millis(1000) + ); + assert_eq!( + alice.mempool.has_tx_with_excess_sig(&tx3_excess_sig).unwrap(), + TxStorageResponse::ReorgPool + ); + assert_eq!( + alice.mempool.has_tx_with_excess_sig(&tx4_excess_sig).unwrap(), + TxStorageResponse::NotStored + ); + assert_eq!( + alice.mempool.has_tx_with_excess_sig(&tx5_excess_sig).unwrap(), + TxStorageResponse::NotStored + ); + + // Re-org chain by adding Block2b - tx2 and tx3 will be discarded as double spends. + assert!(bob.local_nci.submit_block(block2b.clone()).await.is_ok()); + async_assert_eventually!( + alice.mempool.has_tx_with_excess_sig(&tx2_excess_sig).unwrap(), + expect = TxStorageResponse::NotStored, + max_attempts = 20, + interval = Duration::from_millis(1000) + ); + assert_eq!( + alice.mempool.has_tx_with_excess_sig(&tx3_excess_sig).unwrap(), + TxStorageResponse::NotStored + ); + + alice.comms.shutdown().await; + bob.comms.shutdown().await; + }); +} diff --git a/base_layer/core/tests/node_comms_interface.rs b/base_layer/core/tests/node_comms_interface.rs new file mode 100644 index 0000000000..4ef16277ba --- /dev/null +++ b/base_layer/core/tests/node_comms_interface.rs @@ -0,0 +1,383 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#[allow(dead_code)] +mod helpers; + +use futures::{channel::mpsc::unbounded as futures_mpsc_channel_unbounded, executor::block_on, StreamExt}; +use tari_broadcast_channel::bounded; +use tari_core::{ + base_node::{ + comms_interface::{ + CommsInterfaceError, + InboundNodeCommsHandlers, + NodeCommsRequest, + NodeCommsRequestType, + NodeCommsResponse, + }, + OutboundNodeCommsInterface, + }, + blocks::{BlockBuilder, BlockHeader}, + chain_storage::{BlockchainDatabase, ChainMetadata, DbTransaction, HistoricalBlock, MemoryDatabase}, + consensus::{ConsensusManagerBuilder, Network}, + helpers::create_mem_db, + mempool::{Mempool, MempoolConfig, MempoolValidators}, + proof_of_work::DiffAdjManager, + transactions::{ + helpers::{create_test_kernel, create_utxo}, + tari_amount::MicroTari, + types::{CryptoFactories, HashDigest}, + }, + validation::transaction_validators::TxInputAndMaturityValidator, +}; +use tari_crypto::tari_utilities::hash::Hashable; +use tari_service_framework::{reply_channel, reply_channel::Receiver}; +use tari_test_utils::runtime::test_async; + +async fn test_request_responder( + receiver: &mut Receiver< + (NodeCommsRequest, NodeCommsRequestType), + Result, CommsInterfaceError>, + >, + response: Vec, +) +{ + let req_context = receiver.next().await.unwrap(); + req_context.reply(Ok(response)).unwrap() +} + +fn new_mempool() -> ( + Mempool>, + BlockchainDatabase>, +) { + let network = Network::LocalNet; + let consensus_manager = ConsensusManagerBuilder::new(network).build(); + let store = create_mem_db(consensus_manager); + let mempool_validator = MempoolValidators::new( + TxInputAndMaturityValidator::new(store.clone()), + TxInputAndMaturityValidator::new(store.clone()), + ); + let mempool = Mempool::new(store.clone(), MempoolConfig::default(), mempool_validator); + (mempool, store) +} + +#[test] +fn outbound_get_metadata() { + let (request_sender, mut request_receiver) = reply_channel::unbounded(); + let (block_sender, _) = futures_mpsc_channel_unbounded(); + let mut outbound_nci = OutboundNodeCommsInterface::new(request_sender, block_sender); + + block_on(async { + let metadata1 = ChainMetadata::new(5, vec![0u8], 3); + let metadata2 = ChainMetadata::new(6, vec![1u8], 4); + let metadata_response: Vec = vec![ + NodeCommsResponse::ChainMetadata(metadata1.clone()), + NodeCommsResponse::ChainMetadata(metadata2.clone()), + ]; + let (received_metadata, _) = futures::join!( + outbound_nci.get_metadata(), + test_request_responder(&mut request_receiver, metadata_response) + ); + let received_metadata = received_metadata.unwrap(); + assert_eq!(received_metadata.len(), 2); + assert!(received_metadata.contains(&metadata1)); + assert!(received_metadata.contains(&metadata2)); + }); +} + +#[test] +fn inbound_get_metadata() { + let (mempool, store) = new_mempool(); + + let network = Network::LocalNet; + let consensus_manager = ConsensusManagerBuilder::new(network).build(); + let diff_adj_manager = DiffAdjManager::new(store.clone(), &consensus_manager.consensus_constants()).unwrap(); + let (block_event_publisher, _block_event_subscriber) = bounded(100); + assert!(consensus_manager.set_diff_manager(diff_adj_manager).is_ok()); + let (request_sender, _) = reply_channel::unbounded(); + let (block_sender, _) = futures_mpsc_channel_unbounded(); + let outbound_nci = OutboundNodeCommsInterface::new(request_sender, block_sender.clone()); + let inbound_nch = InboundNodeCommsHandlers::new( + block_event_publisher, + store.clone(), + mempool, + consensus_manager, + outbound_nci, + ); + let block = store.fetch_block(0).unwrap().block().clone(); + + test_async(move |rt| { + rt.spawn(async move { + if let Ok(NodeCommsResponse::ChainMetadata(received_metadata)) = + inbound_nch.handle_request(&NodeCommsRequest::GetChainMetadata).await + { + assert_eq!(received_metadata.height_of_longest_chain, Some(0)); + assert_eq!(received_metadata.best_block, Some(block.hash())); + assert_eq!(received_metadata.pruning_horizon, 2880); + } else { + assert!(false); + } + }); + }); +} + +#[test] +fn outbound_fetch_kernels() { + let (request_sender, mut request_receiver) = reply_channel::unbounded(); + let (block_sender, _) = futures_mpsc_channel_unbounded(); + let mut outbound_nci = OutboundNodeCommsInterface::new(request_sender, block_sender); + + block_on(async { + let kernel = create_test_kernel(5.into(), 0); + let hash = kernel.hash(); + let kernel_response: Vec = vec![NodeCommsResponse::TransactionKernels(vec![kernel.clone()])]; + let (received_kernels, _) = futures::join!( + outbound_nci.fetch_kernels(vec![hash]), + test_request_responder(&mut request_receiver, kernel_response) + ); + let received_kernels = received_kernels.unwrap(); + assert_eq!(received_kernels.len(), 1); + assert_eq!(received_kernels[0], kernel); + }); +} + +#[test] +fn inbound_fetch_kernels() { + let (mempool, store) = new_mempool(); + let network = Network::LocalNet; + let consensus_manager = ConsensusManagerBuilder::new(network).build(); + let diff_adj_manager = DiffAdjManager::new(store.clone(), &consensus_manager.consensus_constants()).unwrap(); + let (block_event_publisher, _block_event_subscriber) = bounded(100); + assert!(consensus_manager.set_diff_manager(diff_adj_manager).is_ok()); + let (request_sender, _) = reply_channel::unbounded(); + let (block_sender, _) = futures_mpsc_channel_unbounded(); + let outbound_nci = OutboundNodeCommsInterface::new(request_sender, block_sender); + let inbound_nch = InboundNodeCommsHandlers::new( + block_event_publisher, + store.clone(), + mempool, + consensus_manager, + outbound_nci, + ); + + let kernel = create_test_kernel(5.into(), 0); + let hash = kernel.hash(); + let mut txn = DbTransaction::new(); + txn.insert_kernel(kernel.clone(), true); + assert!(store.commit(txn).is_ok()); + + test_async(move |rt| { + rt.spawn(async move { + if let Ok(NodeCommsResponse::TransactionKernels(received_kernels)) = inbound_nch + .handle_request(&NodeCommsRequest::FetchKernels(vec![hash])) + .await + { + assert_eq!(received_kernels.len(), 1); + assert_eq!(received_kernels[0], kernel); + } else { + assert!(false); + } + }); + }); +} + +#[test] +fn outbound_fetch_headers() { + let (request_sender, mut request_receiver) = reply_channel::unbounded(); + let (block_sender, _) = futures_mpsc_channel_unbounded(); + let mut outbound_nci = OutboundNodeCommsInterface::new(request_sender, block_sender); + + block_on(async { + let mut header = BlockHeader::new(0); + header.height = 0; + let header_response: Vec = vec![NodeCommsResponse::BlockHeaders(vec![header.clone()])]; + let (received_headers, _) = futures::join!( + outbound_nci.fetch_headers(vec![0]), + test_request_responder(&mut request_receiver, header_response) + ); + let received_headers = received_headers.unwrap(); + assert_eq!(received_headers.len(), 1); + assert_eq!(received_headers[0], header); + }); +} + +#[test] +fn inbound_fetch_headers() { + let (mempool, store) = new_mempool(); + let network = Network::LocalNet; + let consensus_constants = network.create_consensus_constants(); + let consensus_manager = ConsensusManagerBuilder::new(network) + .with_consensus_constants(consensus_constants) + .build(); + let diff_adj_manager = DiffAdjManager::new(store.clone(), &consensus_manager.consensus_constants()).unwrap(); + let (block_event_publisher, _block_event_subscriber) = bounded(100); + assert!(consensus_manager.set_diff_manager(diff_adj_manager).is_ok()); + let (request_sender, _) = reply_channel::unbounded(); + let (block_sender, _) = futures_mpsc_channel_unbounded(); + let outbound_nci = OutboundNodeCommsInterface::new(request_sender, block_sender); + let inbound_nch = InboundNodeCommsHandlers::new( + block_event_publisher, + store.clone(), + mempool, + consensus_manager, + outbound_nci, + ); + let header = store.fetch_block(0).unwrap().block().header.clone(); + + test_async(move |rt| { + rt.spawn(async move { + if let Ok(NodeCommsResponse::BlockHeaders(received_headers)) = inbound_nch + .handle_request(&NodeCommsRequest::FetchHeaders(vec![0])) + .await + { + assert_eq!(received_headers.len(), 1); + assert_eq!(received_headers[0], header); + } else { + assert!(false); + } + }); + }); +} + +#[test] +fn outbound_fetch_utxos() { + let factories = CryptoFactories::default(); + let (request_sender, mut request_receiver) = reply_channel::unbounded(); + let (block_sender, _) = futures_mpsc_channel_unbounded(); + let mut outbound_nci = OutboundNodeCommsInterface::new(request_sender, block_sender); + + block_on(async { + let (utxo, _) = create_utxo(MicroTari(10_000), &factories, None); + let hash = utxo.hash(); + let utxo_response: Vec = vec![NodeCommsResponse::TransactionOutputs(vec![utxo.clone()])]; + let (received_utxos, _) = futures::join!( + outbound_nci.fetch_utxos(vec![hash]), + test_request_responder(&mut request_receiver, utxo_response) + ); + let received_utxos = received_utxos.unwrap(); + assert_eq!(received_utxos.len(), 1); + assert_eq!(received_utxos[0], utxo); + }); +} + +#[test] +fn inbound_fetch_utxos() { + let factories = CryptoFactories::default(); + let (mempool, store) = new_mempool(); + let (block_event_publisher, _block_event_subscriber) = bounded(100); + let network = Network::LocalNet; + let consensus_constants = network.create_consensus_constants(); + let consensus_manager = ConsensusManagerBuilder::new(network) + .with_consensus_constants(consensus_constants) + .build(); + let diff_adj_manager = DiffAdjManager::new(store.clone(), &consensus_manager.consensus_constants()).unwrap(); + assert!(consensus_manager.set_diff_manager(diff_adj_manager).is_ok()); + let (request_sender, _) = reply_channel::unbounded(); + let (block_sender, _) = futures_mpsc_channel_unbounded(); + let outbound_nci = OutboundNodeCommsInterface::new(request_sender, block_sender); + let inbound_nch = InboundNodeCommsHandlers::new( + block_event_publisher, + store.clone(), + mempool, + consensus_manager, + outbound_nci, + ); + + let (utxo, _) = create_utxo(MicroTari(10_000), &factories, None); + let hash = utxo.hash(); + let mut txn = DbTransaction::new(); + txn.insert_utxo(utxo.clone(), true); + assert!(store.commit(txn).is_ok()); + + test_async(move |rt| { + rt.spawn(async move { + if let Ok(NodeCommsResponse::TransactionOutputs(received_utxos)) = inbound_nch + .handle_request(&NodeCommsRequest::FetchUtxos(vec![hash])) + .await + { + assert_eq!(received_utxos.len(), 1); + assert_eq!(received_utxos[0], utxo); + } else { + assert!(false); + } + }); + }); +} + +#[test] +fn outbound_fetch_blocks() { + let (request_sender, mut request_receiver) = reply_channel::unbounded(); + let (block_sender, _) = futures_mpsc_channel_unbounded(); + let mut outbound_nci = OutboundNodeCommsInterface::new(request_sender, block_sender); + let network = Network::LocalNet; + let consensus_constants = network.create_consensus_constants(); + block_on(async { + let gb = BlockBuilder::new(&consensus_constants).build(); + let block = HistoricalBlock::new(gb, 0, Vec::new()); + let block_response: Vec = vec![NodeCommsResponse::HistoricalBlocks(vec![block.clone()])]; + let (received_blocks, _) = futures::join!( + outbound_nci.fetch_blocks(vec![0]), + test_request_responder(&mut request_receiver, block_response) + ); + let received_blocks = received_blocks.unwrap(); + assert_eq!(received_blocks.len(), 1); + assert_eq!(received_blocks[0], block); + }); +} + +#[test] +fn inbound_fetch_blocks() { + let (mempool, store) = new_mempool(); + let (block_event_publisher, _block_event_subscriber) = bounded(100); + let network = Network::LocalNet; + let consensus_constants = network.create_consensus_constants(); + let consensus_manager = ConsensusManagerBuilder::new(network) + .with_consensus_constants(consensus_constants) + .build(); + let diff_adj_manager = DiffAdjManager::new(store.clone(), &consensus_manager.consensus_constants()).unwrap(); + assert!(consensus_manager.set_diff_manager(diff_adj_manager).is_ok()); + let (request_sender, _) = reply_channel::unbounded(); + let (block_sender, _) = futures_mpsc_channel_unbounded(); + let outbound_nci = OutboundNodeCommsInterface::new(request_sender, block_sender); + let inbound_nch = InboundNodeCommsHandlers::new( + block_event_publisher, + store.clone(), + mempool, + consensus_manager, + outbound_nci, + ); + let block = store.fetch_block(0).unwrap().block().clone(); + + test_async(move |rt| { + rt.spawn(async move { + if let Ok(NodeCommsResponse::HistoricalBlocks(received_blocks)) = inbound_nch + .handle_request(&NodeCommsRequest::FetchBlocks(vec![0])) + .await + { + assert_eq!(received_blocks.len(), 1); + assert_eq!(*received_blocks[0].block(), block); + } else { + assert!(false); + } + }); + }); +} diff --git a/base_layer/core/tests/node_service.rs b/base_layer/core/tests/node_service.rs new file mode 100644 index 0000000000..b4566d48c5 --- /dev/null +++ b/base_layer/core/tests/node_service.rs @@ -0,0 +1,698 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#[allow(dead_code)] +mod helpers; + +use futures::{future, future::Either, join, stream::FusedStream, FutureExt, Stream, StreamExt}; +use helpers::{ + block_builders::{ + append_block, + chain_block, + create_genesis_block, + create_genesis_block_with_utxos, + generate_block, + }, + nodes::{ + create_network_with_2_base_nodes_with_config, + create_network_with_3_base_nodes, + create_network_with_3_base_nodes_with_config, + random_node_identity, + BaseNodeBuilder, + }, +}; +use std::time::Duration; +use tari_core::{ + base_node::{ + comms_interface::{BlockEvent, CommsInterfaceError}, + consts::BASE_NODE_SERVICE_DESIRED_RESPONSE_FRACTION, + service::BaseNodeServiceConfig, + }, + blocks::BlockHeader, + chain_storage::{BlockAddResult, DbTransaction}, + consensus::{ConsensusConstantsBuilder, ConsensusManagerBuilder, Network}, + mempool::MempoolServiceConfig, + proof_of_work::{Difficulty, PowAlgorithm}, + transactions::{ + helpers::{create_test_kernel, create_utxo, schema_to_transaction}, + tari_amount::{uT, MicroTari, T}, + types::CryptoFactories, + }, + txn_schema, + validation::block_validators::StatelessValidator, +}; +use tari_crypto::tari_utilities::hash::Hashable; +use tari_mmr::MmrCacheConfig; +use tari_p2p::services::liveness::LivenessConfig; +use tari_test_utils::random::string; +use tempdir::TempDir; +use tokio::runtime::Runtime; + +#[test] +fn request_response_get_metadata() { + let mut runtime = Runtime::new().unwrap(); + let factories = CryptoFactories::default(); + let temp_dir = TempDir::new(string(8).as_str()).unwrap(); + let network = Network::LocalNet; + let consensus_constants = ConsensusConstantsBuilder::new(network) + .with_emission_amounts(100_000_000.into(), 0.999, 100.into()) + .build(); + let (block0, _) = create_genesis_block(&factories, &consensus_constants); + let consensus_manager = ConsensusManagerBuilder::new(network) + .with_consensus_constants(consensus_constants) + .with_block(block0.clone()) + .build(); + let (mut alice_node, bob_node, carol_node, _consensus_manager) = create_network_with_3_base_nodes_with_config( + &mut runtime, + BaseNodeServiceConfig::default(), + MmrCacheConfig { rewind_hist_len: 10 }, + MempoolServiceConfig::default(), + LivenessConfig::default(), + consensus_manager, + temp_dir.path().to_str().unwrap(), + ); + + runtime.block_on(async { + let received_metadata = alice_node.outbound_nci.get_metadata().await.unwrap(); + assert_eq!(received_metadata.len(), 2); + assert_eq!(received_metadata[0].height_of_longest_chain, Some(0)); + assert_eq!(received_metadata[1].height_of_longest_chain, Some(0)); + + alice_node.comms.shutdown().await; + bob_node.comms.shutdown().await; + carol_node.comms.shutdown().await; + }); +} + +#[test] +fn request_and_response_fetch_headers() { + let mut runtime = Runtime::new().unwrap(); + let temp_dir = TempDir::new(string(8).as_str()).unwrap(); + let (mut alice_node, bob_node, carol_node, _consensus_manager) = + create_network_with_3_base_nodes(&mut runtime, temp_dir.path().to_str().unwrap()); + + let mut headerb1 = BlockHeader::new(0); + headerb1.height = 1; + let mut headerb2 = BlockHeader::new(0); + headerb2.height = 2; + let mut txn = DbTransaction::new(); + txn.insert_header(headerb1.clone()); + txn.insert_header(headerb2.clone()); + assert!(bob_node.blockchain_db.commit(txn).is_ok()); + + let mut headerc1 = BlockHeader::new(0); + headerc1.height = 1; + let mut headerc2 = BlockHeader::new(0); + headerc2.height = 2; + let mut txn = DbTransaction::new(); + txn.insert_header(headerc1.clone()); + txn.insert_header(headerc2.clone()); + assert!(carol_node.blockchain_db.commit(txn).is_ok()); + + // The request is sent to a random remote base node so the returned headers can be from bob or carol + runtime.block_on(async { + let received_headers = alice_node.outbound_nci.fetch_headers(vec![1]).await.unwrap(); + assert_eq!(received_headers.len(), 1); + assert!(received_headers.contains(&headerb1) || received_headers.contains(&headerc1)); + + let received_headers = alice_node.outbound_nci.fetch_headers(vec![1, 2]).await.unwrap(); + assert_eq!(received_headers.len(), 2); + assert!( + (received_headers.contains(&headerb1) && (received_headers.contains(&headerb2))) || + (received_headers.contains(&headerc1) && (received_headers.contains(&headerc2))) + ); + + alice_node.comms.shutdown().await; + bob_node.comms.shutdown().await; + carol_node.comms.shutdown().await; + }); +} + +#[test] +fn request_and_response_fetch_kernels() { + let mut runtime = Runtime::new().unwrap(); + let temp_dir = TempDir::new(string(8).as_str()).unwrap(); + let (mut alice_node, bob_node, carol_node, _consensus_manager) = + create_network_with_3_base_nodes(&mut runtime, temp_dir.path().to_str().unwrap()); + + let kernel1 = create_test_kernel(5.into(), 0); + let kernel2 = create_test_kernel(10.into(), 1); + let hash1 = kernel1.hash(); + let hash2 = kernel2.hash(); + + let mut txn = DbTransaction::new(); + txn.insert_kernel(kernel1.clone(), true); + txn.insert_kernel(kernel2.clone(), true); + assert!(bob_node.blockchain_db.commit(txn).is_ok()); + let mut txn = DbTransaction::new(); + txn.insert_kernel(kernel1.clone(), true); + txn.insert_kernel(kernel2.clone(), true); + assert!(carol_node.blockchain_db.commit(txn).is_ok()); + + runtime.block_on(async { + let received_kernels = alice_node + .outbound_nci + .fetch_kernels(vec![hash1.clone()]) + .await + .unwrap(); + assert_eq!(received_kernels.len(), 1); + assert_eq!(received_kernels[0], kernel1); + + let received_kernels = alice_node.outbound_nci.fetch_kernels(vec![hash1, hash2]).await.unwrap(); + assert_eq!(received_kernels.len(), 2); + assert!(received_kernels.contains(&kernel1)); + assert!(received_kernels.contains(&kernel2)); + + alice_node.comms.shutdown().await; + bob_node.comms.shutdown().await; + carol_node.comms.shutdown().await; + }); +} + +#[test] +fn request_and_response_fetch_utxos() { + let mut runtime = Runtime::new().unwrap(); + let factories = CryptoFactories::default(); + let temp_dir = TempDir::new(string(8).as_str()).unwrap(); + let (mut alice_node, bob_node, carol_node, _consensus_manager) = + create_network_with_3_base_nodes(&mut runtime, temp_dir.path().to_str().unwrap()); + + let (utxo1, _) = create_utxo(MicroTari(10_000), &factories, None); + let (utxo2, _) = create_utxo(MicroTari(15_000), &factories, None); + let hash1 = utxo1.hash(); + let hash2 = utxo2.hash(); + + let mut txn = DbTransaction::new(); + txn.insert_utxo(utxo1.clone(), true); + txn.insert_utxo(utxo2.clone(), true); + assert!(bob_node.blockchain_db.commit(txn).is_ok()); + let mut txn = DbTransaction::new(); + txn.insert_utxo(utxo1.clone(), true); + txn.insert_utxo(utxo2.clone(), true); + assert!(carol_node.blockchain_db.commit(txn).is_ok()); + + runtime.block_on(async { + let received_utxos = alice_node.outbound_nci.fetch_utxos(vec![hash1.clone()]).await.unwrap(); + assert_eq!(received_utxos.len(), 1); + assert_eq!(received_utxos[0], utxo1); + + let received_utxos = alice_node.outbound_nci.fetch_utxos(vec![hash1, hash2]).await.unwrap(); + assert_eq!(received_utxos.len(), 2); + assert!(received_utxos.contains(&utxo1)); + assert!(received_utxos.contains(&utxo2)); + + alice_node.comms.shutdown().await; + bob_node.comms.shutdown().await; + carol_node.comms.shutdown().await; + }); +} + +#[test] +fn request_and_response_fetch_blocks() { + let mut runtime = Runtime::new().unwrap(); + let factories = CryptoFactories::default(); + let temp_dir = TempDir::new(string(8).as_str()).unwrap(); + let network = Network::LocalNet; + let consensus_constants = ConsensusConstantsBuilder::new(network) + .with_emission_amounts(100_000_000.into(), 0.999, 100.into()) + .build(); + let (block0, _) = create_genesis_block(&factories, &consensus_constants); + let consensus_manager = ConsensusManagerBuilder::new(network) + .with_consensus_constants(consensus_constants) + .with_block(block0.clone()) + .build(); + let (mut alice_node, mut bob_node, carol_node, _) = create_network_with_3_base_nodes_with_config( + &mut runtime, + BaseNodeServiceConfig::default(), + MmrCacheConfig { rewind_hist_len: 10 }, + MempoolServiceConfig::default(), + LivenessConfig::default(), + consensus_manager.clone(), + temp_dir.path().to_str().unwrap(), + ); + + let mut blocks = vec![block0]; + let db = &mut bob_node.blockchain_db; + generate_block(db, &mut blocks, vec![], &consensus_manager.consensus_constants()).unwrap(); + generate_block(db, &mut blocks, vec![], &consensus_manager.consensus_constants()).unwrap(); + generate_block(db, &mut blocks, vec![], &consensus_manager.consensus_constants()).unwrap(); + + carol_node.blockchain_db.add_block(blocks[1].clone()).unwrap(); + carol_node.blockchain_db.add_block(blocks[2].clone()).unwrap(); + + runtime.block_on(async { + let received_blocks = alice_node.outbound_nci.fetch_blocks(vec![0]).await.unwrap(); + assert_eq!(received_blocks.len(), 1); + assert_eq!(*received_blocks[0].block(), blocks[0]); + + let received_blocks = alice_node.outbound_nci.fetch_blocks(vec![0, 1]).await.unwrap(); + assert_eq!(received_blocks.len(), 2); + assert_ne!(*received_blocks[0].block(), *received_blocks[1].block()); + assert!((*received_blocks[0].block() == blocks[0]) || (*received_blocks[1].block() == blocks[0])); + assert!((*received_blocks[0].block() == blocks[1]) || (*received_blocks[1].block() == blocks[1])); + + alice_node.comms.shutdown().await; + bob_node.comms.shutdown().await; + carol_node.comms.shutdown().await; + }); +} + +#[test] +fn request_and_response_fetch_blocks_with_hashes() { + let mut runtime = Runtime::new().unwrap(); + let factories = CryptoFactories::default(); + let temp_dir = TempDir::new(string(8).as_str()).unwrap(); + let network = Network::LocalNet; + let consensus_constants = ConsensusConstantsBuilder::new(network) + .with_emission_amounts(100_000_000.into(), 0.999, 100.into()) + .build(); + let (block0, _) = create_genesis_block(&factories, &consensus_constants); + let consensus_manager = ConsensusManagerBuilder::new(network) + .with_consensus_constants(consensus_constants) + .with_block(block0.clone()) + .build(); + let (mut alice_node, mut bob_node, carol_node, _) = create_network_with_3_base_nodes_with_config( + &mut runtime, + BaseNodeServiceConfig::default(), + MmrCacheConfig { rewind_hist_len: 10 }, + MempoolServiceConfig::default(), + LivenessConfig::default(), + consensus_manager.clone(), + temp_dir.path().to_str().unwrap(), + ); + + let mut blocks = vec![block0]; + let db = &mut bob_node.blockchain_db; + generate_block(db, &mut blocks, vec![], &consensus_manager.consensus_constants()).unwrap(); + generate_block(db, &mut blocks, vec![], &consensus_manager.consensus_constants()).unwrap(); + generate_block(db, &mut blocks, vec![], &consensus_manager.consensus_constants()).unwrap(); + let block0_hash = blocks[0].hash(); + let block1_hash = blocks[1].hash(); + + carol_node.blockchain_db.add_block(blocks[1].clone()).unwrap(); + carol_node.blockchain_db.add_block(blocks[2].clone()).unwrap(); + + runtime.block_on(async { + let received_blocks = alice_node + .outbound_nci + .fetch_blocks_with_hashes(vec![block0_hash.clone()]) + .await + .unwrap(); + assert_eq!(received_blocks.len(), 1); + assert_eq!(*received_blocks[0].block(), blocks[0]); + + let received_blocks = alice_node + .outbound_nci + .fetch_blocks_with_hashes(vec![block0_hash.clone(), block1_hash]) + .await + .unwrap(); + assert_eq!(received_blocks.len(), 2); + assert_ne!(received_blocks[0], received_blocks[1]); + assert!((*received_blocks[0].block() == blocks[0]) || (*received_blocks[1].block() == blocks[0])); + assert!((*received_blocks[0].block() == blocks[1]) || (*received_blocks[1].block() == blocks[1])); + + alice_node.comms.shutdown().await; + bob_node.comms.shutdown().await; + carol_node.comms.shutdown().await; + }); +} + +pub async fn event_stream_next(mut stream: TStream, timeout: Duration) -> Option +where TStream: Stream + FusedStream + Unpin { + let either = future::select(stream.select_next_some(), tokio::time::delay_for(timeout).fuse()).await; + + match either { + Either::Left((v, _)) => Some(v), + Either::Right(_) => None, + } +} + +#[test] +fn propagate_and_forward_valid_block() { + let mut runtime = Runtime::new().unwrap(); + let temp_dir = TempDir::new(string(8).as_str()).unwrap(); + let factories = CryptoFactories::default(); + // Alice will propagate block to bob, bob will receive it, verify it and then propagate it to carol and dan. Dan and + // Carol will also try to propagate the block to each other, as they dont know that bob sent it to the other node. + // These duplicate blocks will be discarded and wont be propagated again. + // /-> carol <-\ + // / | + // alice -> bob | + // \ | + // \-> dan <-/ + let alice_node_identity = random_node_identity(); + let bob_node_identity = random_node_identity(); + let carol_node_identity = random_node_identity(); + let dan_node_identity = random_node_identity(); + let network = Network::LocalNet; + let consensus_constants = ConsensusConstantsBuilder::new(network) + .with_emission_amounts(100_000_000.into(), 0.999, 100.into()) + .build(); + let (block0, _) = create_genesis_block(&factories, &consensus_constants); + let rules = ConsensusManagerBuilder::new(network) + .with_consensus_constants(consensus_constants) + .with_block(block0.clone()) + .build(); + let (mut alice_node, rules) = BaseNodeBuilder::new(network) + .with_node_identity(alice_node_identity.clone()) + .with_peers(vec![bob_node_identity.clone()]) + .with_consensus_manager(rules) + .start(&mut runtime, temp_dir.path().to_str().unwrap()); + let (bob_node, rules) = BaseNodeBuilder::new(network) + .with_node_identity(bob_node_identity.clone()) + .with_peers(vec![ + alice_node_identity, + carol_node_identity.clone(), + dan_node_identity.clone(), + ]) + .with_consensus_manager(rules) + .start(&mut runtime, temp_dir.path().to_str().unwrap()); + let (carol_node, rules) = BaseNodeBuilder::new(network) + .with_node_identity(carol_node_identity.clone()) + .with_peers(vec![bob_node_identity.clone(), dan_node_identity.clone()]) + .with_consensus_manager(rules) + .start(&mut runtime, temp_dir.path().to_str().unwrap()); + let (dan_node, rules) = BaseNodeBuilder::new(network) + .with_node_identity(dan_node_identity) + .with_peers(vec![bob_node_identity, carol_node_identity]) + .with_consensus_manager(rules) + .start(&mut runtime, temp_dir.path().to_str().unwrap()); + + let block1 = append_block(&alice_node.blockchain_db, &block0, vec![], &rules.consensus_constants()).unwrap(); + let block1_hash = block1.hash(); + + runtime.block_on(async { + // Alice will start the propagation. Bob, Carol and Dan will propagate based on the logic in their inbound + // handle_block handlers + assert!(alice_node + .outbound_nci + .propagate_block(block1.clone(), vec![]) + .await + .is_ok()); + + let bob_block_event_stream = bob_node.local_nci.get_block_event_stream_fused(); + let bob_block_event_fut = event_stream_next(bob_block_event_stream, Duration::from_millis(20000)); + let carol_block_event_stream = carol_node.local_nci.get_block_event_stream_fused(); + let carol_block_event_fut = event_stream_next(carol_block_event_stream, Duration::from_millis(20000)); + let dan_block_event_stream = dan_node.local_nci.get_block_event_stream_fused(); + let dan_block_event_fut = event_stream_next(dan_block_event_stream, Duration::from_millis(20000)); + let (bob_block_event, carol_block_event, dan_block_event) = + join!(bob_block_event_fut, carol_block_event_fut, dan_block_event_fut); + + if let BlockEvent::Verified((received_block, _)) = &*bob_block_event.unwrap() { + assert_eq!(received_block.hash(), block1_hash); + } else { + panic!("Bob's node did not receive and validate the expected block"); + } + if let BlockEvent::Verified((received_block, _block_add_result)) = &*carol_block_event.unwrap() { + assert_eq!(received_block.hash(), block1_hash); + } else { + panic!("Carol's node did not receive and validate the expected block"); + } + if let BlockEvent::Verified((received_block, _block_add_result)) = &*dan_block_event.unwrap() { + assert_eq!(received_block.hash(), block1_hash); + } else { + panic!("Dan's node did not receive and validate the expected block"); + } + + alice_node.comms.shutdown().await; + bob_node.comms.shutdown().await; + carol_node.comms.shutdown().await; + dan_node.comms.shutdown().await; + }); +} + +#[test] +fn propagate_and_forward_invalid_block() { + let mut runtime = Runtime::new().unwrap(); + let temp_dir = TempDir::new(string(8).as_str()).unwrap(); + let factories = CryptoFactories::default(); + // Alice will propagate an invalid block to Carol and Bob, they will check the received block and not propagate the + // block to dan. + // /-> bob -\ + // / \ + // alice -> dan + // \ / + // \-> carol -/ + let alice_node_identity = random_node_identity(); + let bob_node_identity = random_node_identity(); + let carol_node_identity = random_node_identity(); + let dan_node_identity = random_node_identity(); + let network = Network::LocalNet; + let consensus_constants = ConsensusConstantsBuilder::new(network) + .with_emission_amounts(100_000_000.into(), 0.999, 100.into()) + .build(); + let (block0, _) = create_genesis_block(&factories, &consensus_constants); + let rules = ConsensusManagerBuilder::new(network) + .with_consensus_constants(consensus_constants) + .with_block(block0.clone()) + .build(); + let stateless_validator = StatelessValidator::new(&rules.consensus_constants()); + let (mut alice_node, rules) = BaseNodeBuilder::new(network) + .with_node_identity(alice_node_identity.clone()) + .with_peers(vec![bob_node_identity.clone(), carol_node_identity.clone()]) + .with_consensus_manager(rules) + .start(&mut runtime, temp_dir.path().to_str().unwrap()); + let (bob_node, rules) = BaseNodeBuilder::new(network) + .with_node_identity(bob_node_identity.clone()) + .with_peers(vec![alice_node_identity.clone(), dan_node_identity.clone()]) + .with_consensus_manager(rules) + .with_validators(stateless_validator.clone(), stateless_validator.clone()) + .start(&mut runtime, temp_dir.path().to_str().unwrap()); + let (carol_node, rules) = BaseNodeBuilder::new(network) + .with_node_identity(carol_node_identity.clone()) + .with_peers(vec![alice_node_identity, dan_node_identity.clone()]) + .with_consensus_manager(rules) + .with_validators(stateless_validator.clone(), stateless_validator) + .start(&mut runtime, temp_dir.path().to_str().unwrap()); + let (dan_node, rules) = BaseNodeBuilder::new(network) + .with_node_identity(dan_node_identity) + .with_peers(vec![bob_node_identity, carol_node_identity]) + .with_consensus_manager(rules) + .start(&mut runtime, temp_dir.path().to_str().unwrap()); + + // Make block 1 invalid + let mut block1 = append_block(&alice_node.blockchain_db, &block0, vec![], &rules.consensus_constants()).unwrap(); + block1.header.height = 0; + let block1_hash = block1.hash(); + runtime.block_on(async { + let bob_block_event_stream = bob_node.local_nci.get_block_event_stream_fused(); + let carol_block_event_stream = carol_node.local_nci.get_block_event_stream_fused(); + let dan_block_event_stream = dan_node.local_nci.get_block_event_stream_fused(); + + assert!(alice_node + .outbound_nci + .propagate_block(block1.clone(), vec![]) + .await + .is_ok()); + + let bob_block_event_fut = event_stream_next(bob_block_event_stream, Duration::from_millis(20000)); + let carol_block_event_fut = event_stream_next(carol_block_event_stream, Duration::from_millis(20000)); + let dan_block_event_fut = event_stream_next(dan_block_event_stream, Duration::from_millis(5000)); + let (bob_block_event, carol_block_event, dan_block_event) = + join!(bob_block_event_fut, carol_block_event_fut, dan_block_event_fut); + + if let BlockEvent::Invalid((received_block, _err)) = &*bob_block_event.unwrap() { + assert_eq!(received_block.hash(), block1_hash); + } else { + panic!("Bob's node should have detected an invalid block"); + } + if let BlockEvent::Invalid((received_block, _err)) = &*carol_block_event.unwrap() { + assert_eq!(received_block.hash(), block1_hash); + } else { + panic!("Carol's node should have detected an invalid block"); + } + assert!(dan_block_event.is_none()); + + alice_node.comms.shutdown().await; + bob_node.comms.shutdown().await; + carol_node.comms.shutdown().await; + dan_node.comms.shutdown().await; + }); +} + +#[test] +fn service_request_timeout() { + let mut runtime = Runtime::new().unwrap(); + let network = Network::LocalNet; + let consensus_manager = ConsensusManagerBuilder::new(network).build(); + let base_node_service_config = BaseNodeServiceConfig { + request_timeout: Duration::from_millis(1), + desired_response_fraction: BASE_NODE_SERVICE_DESIRED_RESPONSE_FRACTION, + }; + let temp_dir = TempDir::new(string(8).as_str()).unwrap(); + let (mut alice_node, bob_node, _consensus_manager) = create_network_with_2_base_nodes_with_config( + &mut runtime, + base_node_service_config, + MmrCacheConfig::default(), + MempoolServiceConfig::default(), + LivenessConfig::default(), + consensus_manager, + temp_dir.path().to_str().unwrap(), + ); + + runtime.block_on(async { + assert_eq!( + alice_node.outbound_nci.get_metadata().await, + Err(CommsInterfaceError::RequestTimedOut) + ); + + alice_node.comms.shutdown().await; + bob_node.comms.shutdown().await; + }); +} + +#[test] +fn local_get_metadata() { + let mut runtime = Runtime::new().unwrap(); + let temp_dir = TempDir::new(string(8).as_str()).unwrap(); + let network = Network::LocalNet; + let (mut node, consensus_manager) = + BaseNodeBuilder::new(network).start(&mut runtime, temp_dir.path().to_str().unwrap()); + let db = &node.blockchain_db; + let block0 = db.fetch_block(0).unwrap().block().clone(); + let block1 = append_block(db, &block0, vec![], &consensus_manager.consensus_constants()).unwrap(); + let block2 = append_block(db, &block1, vec![], &consensus_manager.consensus_constants()).unwrap(); + + runtime.block_on(async { + let metadata = node.local_nci.get_metadata().await.unwrap(); + assert_eq!(metadata.height_of_longest_chain, Some(2)); + assert_eq!(metadata.best_block, Some(block2.hash())); + + node.comms.shutdown().await; + }); +} + +#[test] +fn local_get_new_block_template_and_get_new_block() { + let factories = CryptoFactories::default(); + let mut runtime = Runtime::new().unwrap(); + let temp_dir = TempDir::new(string(8).as_str()).unwrap(); + let network = Network::LocalNet; + let consensus_constants = network.create_consensus_constants(); + let (block0, outputs) = create_genesis_block_with_utxos(&factories, &[T, T], &consensus_constants); + let rules = ConsensusManagerBuilder::new(network) + .with_consensus_constants(consensus_constants) + .with_block(block0) + .build(); + let (mut node, _rules) = BaseNodeBuilder::new(network) + .with_consensus_manager(rules) + .start(&mut runtime, temp_dir.path().to_str().unwrap()); + + let schema = [ + txn_schema!(from: vec![outputs[1].clone()], to: vec![10_000 * uT, 20_000 * uT]), + txn_schema!(from: vec![outputs[2].clone()], to: vec![30_000 * uT, 40_000 * uT]), + ]; + let (txs, _) = schema_to_transaction(&schema); + assert!(node.mempool.insert(txs[0].clone()).is_ok()); + assert!(node.mempool.insert(txs[1].clone()).is_ok()); + + runtime.block_on(async { + let block_template = node.local_nci.get_new_block_template().await.unwrap(); + assert_eq!(block_template.header.height, 1); + assert_eq!(block_template.body.kernels().len(), 2); + + let mut block = node.local_nci.get_new_block(block_template.clone()).await.unwrap(); + block.header.pow.accumulated_blake_difficulty = Difficulty::from(100); + assert_eq!(block.header.height, 1); + assert_eq!(block.body, block_template.body); + + assert!(node.blockchain_db.add_block(block.clone()).is_ok()); + + node.comms.shutdown().await; + }); +} + +#[test] +fn local_get_target_difficulty() { + let network = Network::LocalNet; + let mut runtime = Runtime::new().unwrap(); + let temp_dir = TempDir::new(string(8).as_str()).unwrap(); + let (mut node, consensus_manager) = + BaseNodeBuilder::new(network).start(&mut runtime, temp_dir.path().to_str().unwrap()); + + let db = &node.blockchain_db; + let block0 = db.fetch_block(0).unwrap().block().clone(); + assert_eq!(node.blockchain_db.get_height(), Ok(Some(0))); + + runtime.block_on(async { + let monero_target_difficulty1 = node + .local_nci + .get_target_difficulty(PowAlgorithm::Monero) + .await + .unwrap(); + let blake_target_difficulty1 = node.local_nci.get_target_difficulty(PowAlgorithm::Blake).await.unwrap(); + assert_ne!(monero_target_difficulty1, Difficulty::from(0)); + assert_ne!(blake_target_difficulty1, Difficulty::from(0)); + + let block1 = chain_block(&block0, Vec::new(), &consensus_manager.consensus_constants()); + let mut block1 = node.blockchain_db.calculate_mmr_roots(block1).unwrap(); + block1.header.timestamp = block0 + .header + .timestamp + .increase(consensus_manager.consensus_constants().get_target_block_interval()); + block1.header.pow.pow_algo = PowAlgorithm::Blake; + node.blockchain_db.add_block(block1).unwrap(); + assert_eq!(node.blockchain_db.get_height(), Ok(Some(1))); + + let monero_target_difficulty2 = node + .local_nci + .get_target_difficulty(PowAlgorithm::Monero) + .await + .unwrap(); + let blake_target_difficulty2 = node.local_nci.get_target_difficulty(PowAlgorithm::Blake).await.unwrap(); + assert!(monero_target_difficulty1 <= monero_target_difficulty2); + assert!(blake_target_difficulty1 <= blake_target_difficulty2); + + node.comms.shutdown().await; + }); +} + +#[test] +fn local_submit_block() { + let mut runtime = Runtime::new().unwrap(); + let temp_dir = TempDir::new(string(8).as_str()).unwrap(); + let network = Network::LocalNet; + let (mut node, consensus_manager) = + BaseNodeBuilder::new(network).start(&mut runtime, temp_dir.path().to_str().unwrap()); + + let db = &node.blockchain_db; + let block0 = db.fetch_block(0).unwrap().block().clone(); + let block1 = db + .calculate_mmr_roots(chain_block(&block0, vec![], &consensus_manager.consensus_constants())) + .unwrap(); + runtime.block_on(async { + assert!(node.local_nci.submit_block(block1.clone()).await.is_ok()); + + let event_stream = node.local_nci.get_block_event_stream_fused(); + let event = event_stream_next(event_stream, Duration::from_millis(20000)).await; + + if let BlockEvent::Verified((received_block, result)) = &*event.unwrap() { + assert_eq!(received_block.hash(), block1.hash()); + assert_eq!(*result, BlockAddResult::Ok); + } else { + panic!("Block validation failed"); + } + + node.comms.shutdown().await; + }); +} diff --git a/base_layer/core/tests/node_state_machine.rs b/base_layer/core/tests/node_state_machine.rs new file mode 100644 index 0000000000..155f9bc443 --- /dev/null +++ b/base_layer/core/tests/node_state_machine.rs @@ -0,0 +1,506 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#[allow(dead_code)] +mod helpers; + +use futures::StreamExt; +use helpers::{ + block_builders::{append_block, chain_block, create_genesis_block}, + nodes::{ + create_network_with_2_base_nodes, + create_network_with_2_base_nodes_with_config, + create_network_with_3_base_nodes_with_config, + }, +}; +use std::time::Duration; +use tari_core::{ + base_node::{ + service::BaseNodeServiceConfig, + states::{ + BaseNodeState, + BlockSyncConfig, + BlockSyncInfo, + ListeningConfig, + ListeningInfo, + StateEvent, + SyncStatus::Lagging, + }, + BaseNodeStateMachine, + BaseNodeStateMachineConfig, + }, + consensus::{ConsensusConstantsBuilder, ConsensusManagerBuilder, Network}, + mempool::MempoolServiceConfig, + transactions::types::CryptoFactories, +}; +use tari_mmr::MmrCacheConfig; +use tari_p2p::services::liveness::LivenessConfig; +use tari_test_utils::random::string; +use tempdir::TempDir; +use tokio::runtime::Runtime; + +#[ignore] +fn test_listening_lagging() { + let mut runtime = Runtime::new().unwrap(); + let factories = CryptoFactories::default(); + let network = Network::LocalNet; + let temp_dir = TempDir::new(string(8).as_str()).unwrap(); + let consensus_constants = ConsensusConstantsBuilder::new(network) + .with_emission_amounts(100_000_000.into(), 0.999, 100.into()) + .build(); + let (mut prev_block, _) = create_genesis_block(&factories, &consensus_constants); + let consensus_manager = ConsensusManagerBuilder::new(network) + .with_consensus_constants(consensus_constants) + .with_block(prev_block.clone()) + .build(); + let (alice_node, bob_node, consensus_manager) = create_network_with_2_base_nodes_with_config( + &mut runtime, + BaseNodeServiceConfig::default(), + MmrCacheConfig::default(), + MempoolServiceConfig::default(), + LivenessConfig { + enable_auto_join: false, + enable_auto_stored_message_request: false, + auto_ping_interval: Some(Duration::from_millis(100)), + refresh_neighbours_interval: Duration::from_secs(60), + }, + consensus_manager, + temp_dir.path().to_str().unwrap(), + ); + let mut alice_state_machine = BaseNodeStateMachine::new( + &alice_node.blockchain_db, + &alice_node.outbound_nci, + runtime.handle().clone(), + alice_node.chain_metadata_handle.get_event_stream(), + BaseNodeStateMachineConfig::default(), + ); + + runtime.block_on(async move { + let bob_db = bob_node.blockchain_db; + let mut bob_local_nci = bob_node.local_nci; + + // Bob Block 1 - no block event + prev_block = append_block(&bob_db, &prev_block, vec![], &consensus_manager.consensus_constants()).unwrap(); + // Bob Block 2 - with block event and liveness service metadata update + let prev_block = bob_db + .calculate_mmr_roots(chain_block( + &prev_block, + vec![], + &consensus_manager.consensus_constants(), + )) + .unwrap(); + bob_local_nci.submit_block(prev_block.clone()).await.unwrap(); + assert_eq!(bob_db.get_height(), Ok(Some(2))); + + let state_event = ListeningInfo.next_event(&mut alice_state_machine).await; + assert_eq!(state_event, StateEvent::FallenBehind(Lagging(2))); + + alice_node.comms.shutdown().await; + bob_node.comms.shutdown().await; + }); +} + +#[test] +fn test_event_channel() { + let mut runtime = Runtime::new().unwrap(); + let factories = CryptoFactories::default(); + let temp_dir = TempDir::new(string(8).as_str()).unwrap(); + let network = Network::LocalNet; + let consensus_constants = ConsensusConstantsBuilder::new(network) + .with_emission_amounts(100_000_000.into(), 0.999, 100.into()) + .build(); + let (mut prev_block, _) = create_genesis_block(&factories, &consensus_constants); + let consensus_manager = ConsensusManagerBuilder::new(network) + .with_consensus_constants(consensus_constants) + .with_block(prev_block.clone()) + .build(); + let (alice_node, bob_node, consensus_manager) = create_network_with_2_base_nodes_with_config( + &mut runtime, + BaseNodeServiceConfig::default(), + MmrCacheConfig::default(), + MempoolServiceConfig::default(), + LivenessConfig { + enable_auto_join: false, + enable_auto_stored_message_request: false, + auto_ping_interval: Some(Duration::from_millis(100)), + refresh_neighbours_interval: Duration::from_secs(60), + }, + consensus_manager, + temp_dir.path().to_str().unwrap(), + ); + let alice_state_machine = BaseNodeStateMachine::new( + &alice_node.blockchain_db, + &alice_node.outbound_nci, + runtime.handle().clone(), + alice_node.chain_metadata_handle.get_event_stream(), + BaseNodeStateMachineConfig::default(), + ); + let rx = alice_state_machine.get_state_change_event_stream(); + + runtime.spawn(async move { + alice_state_machine.run().await; + }); + + runtime.block_on(async { + let bob_db = bob_node.blockchain_db; + let mut bob_local_nci = bob_node.local_nci; + + // Bob Block 1 - no block event + prev_block = append_block(&bob_db, &prev_block, vec![], &consensus_manager.consensus_constants()).unwrap(); + // Bob Block 2 - with block event and liveness service metadata update + let prev_block = bob_db + .calculate_mmr_roots(chain_block( + &prev_block, + vec![], + &consensus_manager.consensus_constants(), + )) + .unwrap(); + bob_local_nci.submit_block(prev_block.clone()).await.unwrap(); + assert_eq!(bob_db.get_height(), Ok(Some(2))); + let state = rx.fuse().select_next_some().await; + if let BaseNodeState::InitialSync(_) = *state { + assert!(true); + } else { + assert!(false); + } + + alice_node.comms.shutdown().await; + bob_node.comms.shutdown().await; + }); +} +#[test] +fn test_listening_network_silence() { + let mut runtime = Runtime::new().unwrap(); + let temp_dir = TempDir::new(string(8).as_str()).unwrap(); + let (alice_node, bob_node, _consensus_manager) = + create_network_with_2_base_nodes(&mut runtime, temp_dir.path().to_str().unwrap()); + let state_machine_config = BaseNodeStateMachineConfig { + block_sync_config: BlockSyncConfig::default(), + listening_config: ListeningConfig { + listening_silence_timeout: Duration::from_millis(100), + }, + }; + let mut alice_state_machine = BaseNodeStateMachine::new( + &alice_node.blockchain_db, + &alice_node.outbound_nci, + runtime.handle().clone(), + alice_node.chain_metadata_handle.get_event_stream(), + state_machine_config, + ); + + runtime.block_on(async { + let state_event = ListeningInfo {}.next_event(&mut alice_state_machine).await; + assert_eq!(state_event, StateEvent::NetworkSilence); + + alice_node.comms.shutdown().await; + bob_node.comms.shutdown().await; + }); +} + +#[test] +fn test_block_sync() { + let mut runtime = Runtime::new().unwrap(); + let factories = CryptoFactories::default(); + let temp_dir = TempDir::new(string(8).as_str()).unwrap(); + let network = Network::LocalNet; + let consensus_constants = ConsensusConstantsBuilder::new(network) + .with_emission_amounts(100_000_000.into(), 0.999, 100.into()) + .build(); + let (mut prev_block, _) = create_genesis_block(&factories, &consensus_constants); + let consensus_manager = ConsensusManagerBuilder::new(network) + .with_consensus_constants(consensus_constants) + .with_block(prev_block.clone()) + .build(); + let (alice_node, bob_node, consensus_manager) = create_network_with_2_base_nodes_with_config( + &mut runtime, + BaseNodeServiceConfig::default(), + MmrCacheConfig::default(), + MempoolServiceConfig::default(), + LivenessConfig::default(), + consensus_manager, + temp_dir.path().to_str().unwrap(), + ); + let state_machine_config = BaseNodeStateMachineConfig::default(); + let mut alice_state_machine = BaseNodeStateMachine::new( + &alice_node.blockchain_db, + &alice_node.outbound_nci, + runtime.handle().clone(), + alice_node.chain_metadata_handle.get_event_stream(), + state_machine_config, + ); + + runtime.block_on(async { + let adb = &alice_node.blockchain_db; + let db = &bob_node.blockchain_db; + for _ in 1..6 { + prev_block = append_block(db, &prev_block, vec![], &consensus_manager.consensus_constants()).unwrap(); + } + + // Sync Blocks from genesis block to tip + let state_event = BlockSyncInfo {}.next_event(&mut alice_state_machine).await; + assert_eq!(state_event, StateEvent::BlocksSynchronized); + assert_eq!(adb.get_height(), db.get_height()); + + let bob_tip_height = db.get_height().unwrap().unwrap(); + for height in 1..=bob_tip_height { + assert_eq!(adb.fetch_block(height), db.fetch_block(height)); + } + + alice_node.comms.shutdown().await; + bob_node.comms.shutdown().await; + }); +} + +#[test] +fn test_lagging_block_sync() { + let mut runtime = Runtime::new().unwrap(); + let factories = CryptoFactories::default(); + let temp_dir = TempDir::new(string(8).as_str()).unwrap(); + let network = Network::LocalNet; + let consensus_constants = ConsensusConstantsBuilder::new(network) + .with_emission_amounts(100_000_000.into(), 0.999, 100.into()) + .build(); + let (mut prev_block, _) = create_genesis_block(&factories, &consensus_constants); + let consensus_manager = ConsensusManagerBuilder::new(network) + .with_consensus_constants(consensus_constants) + .with_block(prev_block.clone()) + .build(); + let (alice_node, bob_node, consensus_manager) = create_network_with_2_base_nodes_with_config( + &mut runtime, + BaseNodeServiceConfig::default(), + MmrCacheConfig::default(), + MempoolServiceConfig::default(), + LivenessConfig::default(), + consensus_manager, + temp_dir.path().to_str().unwrap(), + ); + let state_machine_config = BaseNodeStateMachineConfig::default(); + let mut alice_state_machine = BaseNodeStateMachine::new( + &alice_node.blockchain_db, + &alice_node.outbound_nci, + runtime.handle().clone(), + alice_node.chain_metadata_handle.get_event_stream(), + state_machine_config, + ); + + let db = &bob_node.blockchain_db; + for _ in 0..4 { + prev_block = append_block(db, &prev_block, vec![], &consensus_manager.consensus_constants()).unwrap(); + alice_node.blockchain_db.add_block(prev_block.clone()).unwrap(); + } + for _ in 0..4 { + prev_block = append_block(db, &prev_block, vec![], &consensus_manager.consensus_constants()).unwrap(); + } + assert_eq!(alice_node.blockchain_db.get_height(), Ok(Some(4))); + assert_eq!(bob_node.blockchain_db.get_height(), Ok(Some(8))); + + runtime.block_on(async { + // Lagging state beyond horizon, sync remaining Blocks to tip + let state_event = BlockSyncInfo {}.next_event(&mut alice_state_machine).await; + assert_eq!(state_event, StateEvent::BlocksSynchronized); + + assert_eq!( + alice_node.blockchain_db.get_height(), + bob_node.blockchain_db.get_height() + ); + + let bob_tip_height = bob_node.blockchain_db.get_height().unwrap().unwrap(); + for height in 0..=bob_tip_height { + assert_eq!( + alice_node.blockchain_db.fetch_block(height), + bob_node.blockchain_db.fetch_block(height) + ); + } + + alice_node.comms.shutdown().await; + bob_node.comms.shutdown().await; + }); +} + +#[test] +fn test_block_sync_recovery() { + let mut runtime = Runtime::new().unwrap(); + let factories = CryptoFactories::default(); + let temp_dir = TempDir::new(string(8).as_str()).unwrap(); + let network = Network::LocalNet; + let consensus_constants = ConsensusConstantsBuilder::new(network) + .with_emission_amounts(100_000_000.into(), 0.999, 100.into()) + .build(); + let (mut prev_block, _) = create_genesis_block(&factories, &consensus_constants); + let consensus_manager = ConsensusManagerBuilder::new(network) + .with_consensus_constants(consensus_constants) + .with_block(prev_block.clone()) + .build(); + let (alice_node, bob_node, carol_node, _) = create_network_with_3_base_nodes_with_config( + &mut runtime, + BaseNodeServiceConfig::default(), + MmrCacheConfig::default(), + MempoolServiceConfig::default(), + LivenessConfig::default(), + consensus_manager.clone(), + temp_dir.path().to_str().unwrap(), + ); + let state_machine_config = BaseNodeStateMachineConfig::default(); + let mut alice_state_machine = BaseNodeStateMachine::new( + &alice_node.blockchain_db, + &alice_node.outbound_nci, + runtime.handle().clone(), + alice_node.chain_metadata_handle.get_event_stream(), + state_machine_config, + ); + + runtime.block_on(async { + // Connect Alice to Bob and Carol + alice_node + .comms + .connection_manager() + .dial_peer(bob_node.node_identity.node_id().clone()) + .await + .unwrap(); + alice_node + .comms + .connection_manager() + .dial_peer(carol_node.node_identity.node_id().clone()) + .await + .unwrap(); + + let alice_db = &alice_node.blockchain_db; + let bob_db = &bob_node.blockchain_db; + let carol_db = &carol_node.blockchain_db; + // Bob and Carol is ahead of Alice and Bob is ahead of Carol + prev_block = append_block(bob_db, &prev_block, vec![], &consensus_manager.consensus_constants()).unwrap(); + carol_db.add_block(prev_block.clone()).unwrap(); + for _ in 0..2 { + prev_block = append_block(bob_db, &prev_block, vec![], &consensus_manager.consensus_constants()).unwrap(); + } + + // Sync Blocks from genesis block to tip. Alice will notice that the chain tip is equivalent to Bobs tip and + // start to request blocks from her random peers. When Alice requests these blocks from Carol, Carol + // won't always have these blocks and Alice will have to request these blocks again until her maximum attempts + // have been reached. + let state_event = BlockSyncInfo.next_event(&mut alice_state_machine).await; + assert_eq!(state_event, StateEvent::BlocksSynchronized); + + let bob_tip_height = bob_db.get_height().unwrap().unwrap(); + for height in 1..=bob_tip_height { + assert_eq!( + alice_db.fetch_block(height).unwrap().block(), + bob_db.fetch_block(height).unwrap().block() + ); + } + + alice_node.comms.shutdown().await; + bob_node.comms.shutdown().await; + carol_node.comms.shutdown().await; + }); +} + +#[test] +fn test_forked_block_sync() { + let mut runtime = Runtime::new().unwrap(); + let factories = CryptoFactories::default(); + let temp_dir = TempDir::new(string(8).as_str()).unwrap(); + let network = Network::LocalNet; + let consensus_constants = ConsensusConstantsBuilder::new(network) + .with_emission_amounts(100_000_000.into(), 0.999, 100.into()) + .build(); + let (mut prev_block, _) = create_genesis_block(&factories, &consensus_constants); + let consensus_manager = ConsensusManagerBuilder::new(network) + .with_consensus_constants(consensus_constants) + .with_block(prev_block.clone()) + .build(); + let (alice_node, bob_node, consensus_manager) = create_network_with_2_base_nodes_with_config( + &mut runtime, + BaseNodeServiceConfig::default(), + MmrCacheConfig::default(), + MempoolServiceConfig::default(), + LivenessConfig::default(), + consensus_manager, + temp_dir.path().to_str().unwrap(), + ); + let state_machine_config = BaseNodeStateMachineConfig::default(); + let mut alice_state_machine = BaseNodeStateMachine::new( + &alice_node.blockchain_db, + &alice_node.outbound_nci, + runtime.handle().clone(), + alice_node.chain_metadata_handle.get_event_stream(), + state_machine_config, + ); + // Shared chain + let alice_db = &alice_node.blockchain_db; + let bob_db = &bob_node.blockchain_db; + for _ in 0..2 { + prev_block = append_block(bob_db, &prev_block, vec![], &consensus_manager.consensus_constants()).unwrap(); + alice_db.add_block(prev_block.clone()).unwrap(); + } + + assert_eq!(alice_node.blockchain_db.get_height(), Ok(Some(2))); + assert_eq!(bob_node.blockchain_db.get_height(), Ok(Some(2))); + + let mut alice_prev_block = prev_block.clone(); + let mut bob_prev_block = prev_block; + // Alice fork + for _ in 0..2 { + alice_prev_block = append_block( + alice_db, + &alice_prev_block, + vec![], + &consensus_manager.consensus_constants(), + ) + .unwrap(); + } + // Bob fork + for _ in 0..7 { + println!(" "); + println!("add block"); + bob_prev_block = append_block( + bob_db, + &bob_prev_block, + vec![], + &consensus_manager.consensus_constants(), + ) + .unwrap(); + } + assert_eq!(alice_node.blockchain_db.get_height(), Ok(Some(4))); + assert_eq!(bob_node.blockchain_db.get_height(), Ok(Some(9))); + + runtime.block_on(async { + let state_event = BlockSyncInfo {}.next_event(&mut alice_state_machine).await; + assert_eq!(state_event, StateEvent::BlocksSynchronized); + + assert_eq!( + alice_node.blockchain_db.get_height(), + bob_node.blockchain_db.get_height() + ); + + let bob_tip_height = bob_node.blockchain_db.get_height().unwrap().unwrap(); + for height in 0..=bob_tip_height { + assert_eq!( + alice_node.blockchain_db.fetch_block(height), + bob_node.blockchain_db.fetch_block(height) + ); + } + + alice_node.comms.shutdown().await; + bob_node.comms.shutdown().await; + }); +} diff --git a/base_layer/core/tests/support/simple_block_chain.rs b/base_layer/core/tests/support/simple_block_chain.rs deleted file mode 100644 index 65c13433ef..0000000000 --- a/base_layer/core/tests/support/simple_block_chain.rs +++ /dev/null @@ -1,480 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use chrono::Duration; - -use digest::Digest; -use merklemountainrange::mmr::*; -use rand::{CryptoRng, OsRng, Rng}; -use serde::{Deserialize, Serialize}; -use std::{fs::File, io::prelude::*}; -use tari_core::{ - blocks::{block::*, blockheader::*}, - consensus::ConsensusRules, - fee::Fee, - tari_amount::MicroTari, - transaction::*, - transaction_protocol::{ - build_challenge, - sender::*, - single_receiver::SingleReceiverTransactionProtocol, - TransactionMetadata, - }, - types::*, -}; - -use tari_crypto::{ - commitment::HomomorphicCommitmentFactory, - common::Blake256, - keys::{PublicKey, SecretKey}, - range_proof::RangeProofService, - ristretto::*, -}; -use tari_utilities::{hash::Hashable, ByteArray}; - -/// This struct is used to keep track of what the value and private key of a UTXO is. -#[derive(Serialize, Deserialize, PartialEq, Debug)] -pub struct SpendInfo { - pub key: PrivateKey, - pub value: MicroTari, - pub features: OutputFeatures, -} - -impl SpendInfo { - pub fn new(key: PrivateKey, value: MicroTari, features: OutputFeatures) -> SpendInfo { - SpendInfo { key, value, features } - } -} - -/// This is used to represent a block chain in memory for testing purposes -#[derive(Serialize, Deserialize, PartialEq, Debug)] -pub struct SimpleBlockChain { - pub blocks: Vec, - pub spending_keys: Vec>, -} - -/// This is used to represent a block chain in memory for testing purposes -pub struct SimpleBlockChainBuilder { - blockchain: SimpleBlockChain, - headers: MerkleMountainRange, - utxos: MerkleMountainRange, - kernels: MerkleMountainRange, - rangeproofs: MerkleMountainRange, - rules: ConsensusRules, -} - -impl SimpleBlockChainBuilder { - /// This will create a new test block_chain with a Genesis block - pub fn new() -> SimpleBlockChainBuilder { - let mut chain = SimpleBlockChainBuilder { - blockchain: Default::default(), - headers: Default::default(), - utxos: Default::default(), - kernels: Default::default(), - rangeproofs: Default::default(), - rules: ConsensusRules::current(), - }; - let mut rng = OsRng::new().unwrap(); - // create Genesis block - chain.add_block(&mut rng, Vec::new()); - chain - } - - /// This will create a new test block_chain with random txs spending all the utxo's at the spend height - pub fn new_with_spending(block_amount: u64, spending_height: u64) -> SimpleBlockChainBuilder { - let mut chain = SimpleBlockChainBuilder::new(); - - let mut rng = OsRng::new().unwrap(); - // create gen block - let priv_key = PrivateKey::random(&mut rng); - chain.blockchain.spending_keys.push(vec![SpendInfo::new( - priv_key.clone(), - chain.rules.emission_schedule().block_reward(0), - OutputFeatures::create_coinbase(0, &chain.rules), - )]); - let (cb_utxo, cb_kernel) = create_coinbase(priv_key, 0, 0.into(), &chain.rules); - let block = BlockBuilder::new().with_coinbase_utxo(cb_utxo, cb_kernel).build(); - chain.processes_new_block(block); - - // lets mine some empty blocks - if spending_height > 1 { - chain.add_empty_blocks(&mut rng, spending_height - 1); - } - - // lets mine some more blocks, but spending the utxo's in the older blocks - for i in spending_height..(block_amount) { - chain.blockchain.spending_keys.push(Vec::new()); - let (tx, mut spends) = chain.spend_block_utxos((i - spending_height) as usize); - chain.add_block(&mut rng, tx); - chain.blockchain.spending_keys[i as usize].append(&mut spends); - } - chain - } - - /// This will add empty blocks to the chain - pub fn add_empty_blocks(&mut self, rng: &mut R, count: u64) { - for _ in 0..count { - self.add_block(rng, Vec::new()) - } - } - - /// Add a block to the chain with the given metadata - fn add_block(&mut self, rng: &mut R, tx: Vec) { - let priv_key = PrivateKey::random(rng); - let height = self.blockchain.blocks.len() as u64; - let header = if height > 0 { - self.generate_new_header() - } else { - self.generate_genesis_block_header() - }; - let total_fee = tx - .iter() - .fold(MicroTari::default(), |tot, tx| tot + tx.get_body().get_total_fee()); - let (cb_utxo, cb_kernel) = create_coinbase(priv_key.clone(), header.height, total_fee, &self.rules); - self.blockchain.spending_keys.push(vec![SpendInfo::new( - priv_key, - self.rules.emission_schedule().block_reward(height) + total_fee, - OutputFeatures::create_coinbase(height, &self.rules), - )]); - let block = BlockBuilder::new() - .with_header(header) - .with_coinbase_utxo(cb_utxo, cb_kernel) - .with_transactions(tx) - .build(); - self.processes_new_block(block); - } - - /// This will blocks to the chain with random txs spending all the utxo's at the spend height - pub fn add_with_spending(&mut self, block_amount: u64, spending_height: u64) { - let mut rng = OsRng::new().unwrap(); - let len = self.blockchain.blocks.len() as u64; - let mut blocks_added = 0; - if len < spending_height { - self.add_empty_blocks(&mut rng, spending_height - len); - blocks_added += 1; - }; - // lets mine some more blocks, but spending the utxo's in the older blocks - let len = self.blockchain.blocks.len() as u64; - for i in len..(len + block_amount - blocks_added) { - self.blockchain.spending_keys.push(Vec::new()); - let (tx, mut spends) = self.spend_block_utxos((i - spending_height) as usize); - self.add_block(&mut rng, tx); - self.blockchain.spending_keys[i as usize].append(&mut spends); - } - } - - /// This function will just add the content of the block to the MMR's - fn processes_new_block(&mut self, block: Block) { - println!("Proc block nr: {:?}", self.blockchain.blocks.len()); - self.headers - .push(block.header.clone()) - .expect("failed to add header to test chain"); - self.kernels - .append(block.body.kernels.clone()) - .expect("failed to add kernels to test chain"); - - for input in &block.body.inputs { - let hash = input.clone().hash(); - self.utxos - .prune_object_hash(&hash) - .expect("failed to add pruned inputs"); - } - for output in &block.body.outputs { - self.rangeproofs - .push(output.clone().proof) - .expect("failed to add proofs to test chain"); - self.utxos - .push(output.clone().into()) - .expect("failed to add outputs to test chain"); - } - self.blockchain.blocks.push(block); - } - - fn generate_genesis_block_header(&self) -> BlockHeader { - BlockHeader::new(self.rules.blockchain_version()) - } - - /// This function will generate a new header, assuming it will follow on the last created block. - fn generate_new_header(&self) -> BlockHeader { - let counter = self.blockchain.blocks.len() - 1; - let mut hash = [0; 32]; - hash.copy_from_slice(&self.blockchain.blocks[counter].header.hash()); - let mut hasher = SignatureHasher::new(); - hasher.input(&self.utxos.get_merkle_root()[..]); - hasher.input(&self.utxos.get_unpruned_hash()); - let output_mr = hasher.result().to_vec(); - let kernal_mmr = self.kernels.get_merkle_root(); - let rr_mmr = self.rangeproofs.get_merkle_root(); - BlockHeader { - version: self.rules.blockchain_version(), - height: self.blockchain.blocks[counter].header.height + 1, - prev_hash: hash, - timestamp: self.blockchain.blocks[counter] - .header - .timestamp - .clone() - .checked_add_signed(Duration::minutes(1)) - .unwrap(), - output_mr: array_ref!(output_mr, 0, 32).clone(), - range_proof_mr: array_ref!(rr_mmr, 0, 32).clone(), - kernel_mr: array_ref!(kernal_mmr, 0, 32).clone(), - total_kernel_offset: RistrettoSecretKey::from(0), - pow: ProofOfWork { - work: self.blockchain.blocks[counter].header.pow.work + 1, - }, - } - } - - /// This function will spend the utxo's in the mentioned block - fn spend_block_utxos(&mut self, block_index: usize) -> (Vec, Vec) { - let utxo_count = self.blockchain.spending_keys[block_index as usize].len(); - let mut txs = Vec::new(); - let mut spends = Vec::new(); - let mut counter = 0; - for i in 0..utxo_count { - let result = self.create_tx(block_index, i, &mut counter); - if result.is_some() { - let (tx, mut spending_info) = result.unwrap(); - txs.push(tx); - spends.append(&mut spending_info) - } - } - (txs, spends) - } - - /// This function will create a new transaction, spending the utxo specified by the block and utxo index - fn create_tx( - &self, - block_index: usize, - utxo_index: usize, - counter: &mut usize, - ) -> Option<(Transaction, Vec)> - { - let mut rng = OsRng::new().unwrap(); - let mut spend_info = Vec::new(); - - // create keys - let old_spend_key = self.blockchain.spending_keys[block_index][utxo_index].key.clone(); - let new_spend_key = PrivateKey::random(&mut rng); - let new_spend_key2 = PrivateKey::random(&mut rng); - // create values - let old_value = self.blockchain.spending_keys[block_index][utxo_index].value; - if old_value <= MicroTari(100) || *counter > 4 { - // we dont want to keep dividing for ever on a single utxo, or create very large blocks - return None; - } - let new_value = self.blockchain.spending_keys[block_index][utxo_index].value / 2; - let fee = Fee::calculate(20.into(), 1, 2); - if (new_value + fee + MicroTari(100)) >= (old_value) { - // we dont want values smaller than 100 micro tari - return None; - } - let new_value2 = old_value - new_value - fee; - - // save spend info - spend_info.push(SpendInfo::new( - new_spend_key.clone(), - new_value, - OutputFeatures::default(), - )); - spend_info.push(SpendInfo::new( - new_spend_key2.clone(), - new_value2, - OutputFeatures::default(), - )); - - // recreate input commitment - let v_input = PrivateKey::from(old_value); - let commitment_in = COMMITMENT_FACTORY.commit(&old_spend_key, &v_input); - let input = TransactionInput::new( - self.blockchain.spending_keys[block_index][utxo_index].features.clone(), - commitment_in, - ); - // create unblinded value - let old_value = UnblindedOutput::new(old_value, old_spend_key, None); - - // generate kernel stuff - let sender_offset = PrivateKey::random(&mut rng); - let sender_r = PrivateKey::random(&mut rng); - let receiver_r = PrivateKey::random(&mut rng); - let mut builder = SenderTransactionProtocol::builder(1); - builder - .with_lock_height(0) - .with_fee_per_gram(MicroTari(20)) - .with_offset(sender_offset) - .with_private_nonce(sender_r) - .with_change_secret(new_spend_key.clone()) - .with_input(input, old_value) - .with_amount(0, new_value2); - let mut sender = builder.build::(&PROVER, &COMMITMENT_FACTORY).unwrap(); - - let msg = sender.build_single_round_message().unwrap(); - let receiver_info = SingleReceiverTransactionProtocol::create( - &msg, - receiver_r, - new_spend_key2, - OutputFeatures::default(), - &PROVER, - &COMMITMENT_FACTORY, - ) - .unwrap(); - - sender - .add_single_recipient_info(receiver_info.clone(), &PROVER) - .unwrap(); - match sender.finalize(KernelFeatures::empty(), &PROVER, &COMMITMENT_FACTORY) { - Ok(true) => (), - _ => { - return None; - }, - }; - - let tx = sender.get_transaction().unwrap().clone(); - *counter += 1; - Some((tx, spend_info)) - } -} - -impl Default for SimpleBlockChain { - fn default() -> Self { - SimpleBlockChain { - blocks: Vec::new(), - spending_keys: Vec::new(), - } - } -} - -/// This function will create a coinbase from the provided secret key. The coinbase will be added to the outputs and -/// kernels. -fn create_coinbase( - key: PrivateKey, - height: u64, - total_fee: MicroTari, - rules: &ConsensusRules, -) -> (TransactionOutput, TransactionKernel) -{ - let mut rng = rand::OsRng::new().unwrap(); - // build output - let amount = total_fee + rules.emission_schedule().block_reward(height); - let v = PrivateKey::from(u64::from(amount)); - let commitment = COMMITMENT_FACTORY.commit(&key, &v); - let rr = PROVER.construct_proof(&key, amount.into()).unwrap(); - let output = TransactionOutput::new( - OutputFeatures::create_coinbase(height, rules), - commitment, - RangeProof::from_bytes(&rr).unwrap(), - ); - - // create kernel - let tx_meta = TransactionMetadata { - fee: 0.into(), - lock_height: 0, - }; - let r = PrivateKey::random(&mut rng); - let e = build_challenge(&PublicKey::from_secret_key(&r), &tx_meta); - let s = Signature::sign(key.clone(), r, &e).unwrap(); - let excess = COMMITMENT_FACTORY.commit_value(&key, 0); - let kernel = KernelBuilder::new() - .with_features(KernelFeatures::COINBASE_KERNEL) - .with_fee(0.into()) - .with_lock_height(0) - .with_excess(&excess) - .with_signature(&s) - .build() - .unwrap(); - (output, kernel) -} - -#[cfg(test)] -mod test { - use super::*; - use std::fs; - - //#[test] - fn create_simple_block_chain() { - let mut rng = rand::OsRng::new().unwrap(); - let mut chain = SimpleBlockChainBuilder::new(); - assert_eq!(chain.blockchain.blocks.len(), 1); - chain.add_empty_blocks(&mut rng, 5); - assert_eq!(chain.blockchain.blocks.len(), 6); - - // Check that the blocks form a chain - assert_eq!(chain.blockchain.blocks[0].header.height, 0); - for i in 1..chain.blockchain.blocks.len() { - let mut hash = [0; 32]; - hash.copy_from_slice(&chain.blockchain.blocks[i - 1].header.hash()); - assert_eq!(chain.blockchain.blocks[i].header.prev_hash, hash); - assert_eq!(chain.blockchain.blocks[i].header.height, i as u64); - } - } - - // we dont want to run this function function every time as it basically tests, test code and it runs slow. - // #[test] - #[allow(dead_code)] - fn create_simple_block_chain_with_spend() { - let mut chain = SimpleBlockChainBuilder::new_with_spending(5, 1); - assert_eq!(chain.blockchain.blocks.len(), 5); - chain.add_with_spending(5, 1); - assert_eq!(chain.blockchain.blocks.len(), 10); - - assert_eq!(chain.blockchain.blocks[0].header.height, 0); - for i in 1..10 { - let mut hash = [0; 32]; - hash.copy_from_slice(&chain.blockchain.blocks[i - 1].header.hash()); - assert_eq!(chain.blockchain.blocks[i].header.prev_hash, hash); - assert_eq!(chain.blockchain.blocks[i].header.height, i as u64); - for input in &chain.blockchain.blocks[i].body.inputs { - assert!(chain.blockchain.blocks[i - 1] - .body - .outputs - .iter() - .any(|x| x.commitment == input.commitment)); - } - } - } - - // we dont want to run this function function every time as it basically tests, test code and it runs slow. - //#[test] - #[allow(dead_code)] - fn test_json_file() { - let mut chain = SimpleBlockChainBuilder::new_with_spending(5, 1); - chain.add_with_spending(5, 1); - let mut file = File::create("tests/chain/test_chain.json").unwrap(); - let json = serde_json::to_string_pretty(&chain.blockchain).unwrap(); - file.write_all(json.as_bytes()).unwrap(); - let read_json = fs::read_to_string("tests/chain/test_chain.json").unwrap(); - let blockchain: SimpleBlockChain = serde_json::from_str(&read_json).unwrap(); - assert_eq!(blockchain, chain.blockchain); - fs::remove_file("tests/chain/test_chain.json").unwrap(); - } - - // we dont want to run this function function every time as it create a test file for use in testing - //#[test] - #[allow(dead_code)] - fn create_json_file() { - let mut chain = SimpleBlockChainBuilder::new_with_spending(5, 1); - chain.add_with_spending(45, 1); - let mut file = File::create("tests/chain/chain.json").unwrap(); - let json = serde_json::to_string_pretty(&chain.blockchain).unwrap(); - file.write_all(json.as_bytes()).unwrap(); - } -} diff --git a/base_layer/key_manager/Cargo.toml b/base_layer/key_manager/Cargo.toml new file mode 100644 index 0000000000..cc1c14f300 --- /dev/null +++ b/base_layer/key_manager/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "tari_key_manager" +authors = ["The Tari Development Community"] +description = "Tari cryptocurrency wallet key management" +repository = "https://github.com/tari-project/tari" +license = "BSD-3-Clause" +version = "0.0.9" +edition = "2018" + +[dependencies] +tari_crypto = { version = "^0.3" } +rand = "0.7.2" +digest = "0.8.0" +sha2 = "0.8.0" +derive-error = "0.0.4" +serde = "1.0.89" +serde_derive = "1.0.89" +serde_json = "1.0.39" + diff --git a/base_layer/key_manager/README.md b/base_layer/key_manager/README.md new file mode 100644 index 0000000000..b3ec2b81a7 --- /dev/null +++ b/base_layer/key_manager/README.md @@ -0,0 +1 @@ +# Tari Key manager \ No newline at end of file diff --git a/base_layer/keymanager/src/diacritics.rs b/base_layer/key_manager/src/diacritics.rs similarity index 99% rename from base_layer/keymanager/src/diacritics.rs rename to base_layer/key_manager/src/diacritics.rs index 26e5a03379..d08014252b 100644 --- a/base_layer/keymanager/src/diacritics.rs +++ b/base_layer/key_manager/src/diacritics.rs @@ -76,7 +76,7 @@ pub fn remove_diacritics(word: &str) -> String { }) .collect(); // Remove any remaining non-ascii characters - (clean_string.replace(|c: char| !c.is_ascii(), "")) + clean_string.replace(|c: char| !c.is_ascii(), "") } #[cfg(test)] diff --git a/base_layer/keymanager/src/file_backup.rs b/base_layer/key_manager/src/file_backup.rs similarity index 100% rename from base_layer/keymanager/src/file_backup.rs rename to base_layer/key_manager/src/file_backup.rs diff --git a/base_layer/keymanager/src/keymanager.rs b/base_layer/key_manager/src/key_manager.rs similarity index 94% rename from base_layer/keymanager/src/keymanager.rs rename to base_layer/key_manager/src/key_manager.rs index 31679e74d0..52ff6b308e 100644 --- a/base_layer/keymanager/src/keymanager.rs +++ b/base_layer/key_manager/src/key_manager.rs @@ -27,10 +27,12 @@ use rand::{CryptoRng, Rng}; use serde::de::DeserializeOwned; use serde_derive::{Deserialize, Serialize}; use std::marker::PhantomData; -use tari_crypto::keys::SecretKey; -use tari_utilities::{byte_array::ByteArrayError, hex::Hex}; +use tari_crypto::{ + keys::SecretKey, + tari_utilities::{byte_array::ByteArrayError, hex::Hex}, +}; -#[derive(Debug, Error)] +#[derive(Debug, Error, PartialEq)] pub enum KeyManagerError { // Could not convert into byte array ByteArrayError(ByteArrayError), @@ -100,7 +102,7 @@ where /// Creates a KeyManager from the provided sequence of mnemonic words, the language of the mnemonic sequence will be /// auto detected pub fn from_mnemonic( - mnemonic_seq: &Vec, + mnemonic_seq: &[String], branch_seed: String, primary_key_index: usize, ) -> Result, KeyManagerError> @@ -128,22 +130,22 @@ where /// Generate next deterministic private key derived from master key pub fn next_key(&mut self) -> Result, ByteArrayError> { self.primary_key_index += 1; - (self.derive_key(self.primary_key_index)) + self.derive_key(self.primary_key_index) } } #[cfg(test)] mod test { - use crate::{file_backup::*, keymanager::*}; + use crate::{file_backup::*, key_manager::*}; + use rand::rngs::OsRng; use sha2::Sha256; use std::fs::remove_file; use tari_crypto::ristretto::RistrettoSecretKey; #[test] fn test_new_keymanager() { - let mut rng = rand::OsRng::new().unwrap(); - let km1 = KeyManager::::new(&mut rng); - let km2 = KeyManager::::new(&mut rng); + let km1 = KeyManager::::new(&mut OsRng); + let km2 = KeyManager::::new(&mut OsRng); assert_ne!(km1.master_key, km2.master_key); } @@ -192,8 +194,7 @@ mod test { #[test] fn test_derive_and_next_key() { - let mut rng = rand::OsRng::new().unwrap(); - let mut km = KeyManager::::new(&mut rng); + let mut km = KeyManager::::new(&mut OsRng); let next_key1_result = km.next_key(); let next_key2_result = km.next_key(); let desired_key_index1 = 1; @@ -219,8 +220,7 @@ mod test { #[test] fn test_to_file_and_from_file() { - let mut rng = rand::OsRng::new().unwrap(); - let desired_km = KeyManager::::new(&mut rng); + let desired_km = KeyManager::::new(&mut OsRng); let backup_filename = "test_km_backup.json".to_string(); // Backup KeyManager to file match desired_km.to_file(&backup_filename) { @@ -230,7 +230,7 @@ mod test { KeyManager::from_file(&backup_filename); match backup_km_result { Ok(backup_km) => { - // Remove temp keymanager backup file + // Remove temp key_manager backup file remove_file(backup_filename).unwrap(); assert_eq!(desired_km.branch_seed, backup_km.branch_seed); diff --git a/base_layer/keymanager/src/lib.rs b/base_layer/key_manager/src/lib.rs similarity index 80% rename from base_layer/keymanager/src/lib.rs rename to base_layer/key_manager/src/lib.rs index c7d870b2ad..db74d2a9e1 100644 --- a/base_layer/keymanager/src/lib.rs +++ b/base_layer/key_manager/src/lib.rs @@ -1,5 +1,5 @@ pub mod diacritics; pub mod file_backup; -pub mod keymanager; +pub mod key_manager; pub mod mnemonic; pub mod mnemonic_wordlists; diff --git a/base_layer/keymanager/src/mnemonic.rs b/base_layer/key_manager/src/mnemonic.rs similarity index 92% rename from base_layer/keymanager/src/mnemonic.rs rename to base_layer/key_manager/src/mnemonic.rs index 51fc156afb..03a482e64d 100644 --- a/base_layer/keymanager/src/mnemonic.rs +++ b/base_layer/key_manager/src/mnemonic.rs @@ -23,14 +23,16 @@ use crate::{diacritics::*, mnemonic_wordlists::*}; use derive_error::Error; use std::slice::Iter; -use tari_crypto::keys::SecretKey; -use tari_utilities::{bit::*, byte_array::ByteArrayError}; +use tari_crypto::{ + keys::SecretKey, + tari_utilities::{bit::*, byte_array::ByteArrayError}, +}; /// The Mnemonic system simplifies the encoding and decoding of a secret key into and from a Mnemonic word sequence /// It can autodetect the language of the Mnemonic word sequence // TODO: Develop a language autodetection mechanism to distinguish between ChineseTraditional and ChineseSimplified -#[derive(Debug, Error)] +#[derive(Debug, Error, PartialEq)] pub enum MnemonicError { // Only ChineseSimplified, ChineseTraditional, English, French, Italian, Japanese, Korean and Spanish are defined // natural languages @@ -78,7 +80,7 @@ impl MnemonicLanguage { MnemonicLanguage::Korean, MnemonicLanguage::Spanish, ]; - (MNEMONIC_LANGUAGES.iter()) + MNEMONIC_LANGUAGES.iter() } } @@ -154,27 +156,23 @@ pub fn from_bytes(bytes: Vec, language: &MnemonicLanguage) -> Result return Err(err), } } - (Ok(mnemonic_sequence)) + Ok(mnemonic_sequence) } /// Generates a mnemonic sequence of words from the provided secret key -pub fn from_secretkey(k: &K, language: &MnemonicLanguage) -> Result, MnemonicError> { - (from_bytes(k.to_vec(), language)) +pub fn from_secret_key(k: &K, language: &MnemonicLanguage) -> Result, MnemonicError> { + from_bytes(k.to_vec(), language) } /// Generates a vector of bytes that represent the provided mnemonic sequence of words, the language of the mnemonic /// sequence is autodetected -pub fn to_bytes(mnemonic_seq: &Vec) -> Result, MnemonicError> { +pub fn to_bytes(mnemonic_seq: &[String]) -> Result, MnemonicError> { let language = MnemonicLanguage::from(&mnemonic_seq[0])?; // Autodetect language - (to_bytes_with_language(mnemonic_seq, &language)) + to_bytes_with_language(mnemonic_seq, &language) } /// Generates a vector of bytes that represent the provided mnemonic sequence of words using the specified language -pub fn to_bytes_with_language( - mnemonic_seq: &Vec, - language: &MnemonicLanguage, -) -> Result, MnemonicError> -{ +pub fn to_bytes_with_language(mnemonic_seq: &[String], language: &MnemonicLanguage) -> Result, MnemonicError> { let mut bits: Vec = Vec::new(); for curr_word in mnemonic_seq { match find_mnemonic_index_from_word(curr_word, &language) { @@ -200,7 +198,7 @@ pub fn to_bytes_with_language( /// Generates a SecretKey that represent the provided mnemonic sequence of words, the language of the mnemonic sequence /// is autodetected -pub fn to_secretkey(mnemonic_seq: &Vec) -> Result { +pub fn to_secretkey(mnemonic_seq: &[String]) -> Result { let bytes = to_bytes(mnemonic_seq)?; match K::from_bytes(&bytes) { Ok(k) => Ok(k), @@ -210,7 +208,7 @@ pub fn to_secretkey(mnemonic_seq: &Vec) -> Result( - mnemonic_seq: &Vec, + mnemonic_seq: &[String], language: &MnemonicLanguage, ) -> Result { @@ -222,31 +220,26 @@ pub fn to_secretkey_with_language( } pub trait Mnemonic { - fn from_mnemonic(mnemonic_seq: &Vec) -> Result; - fn from_mnemonic_with_language(mnemonic_seq: &Vec, language: &MnemonicLanguage) - -> Result; + fn from_mnemonic(mnemonic_seq: &[String]) -> Result; + fn from_mnemonic_with_language(mnemonic_seq: &[String], language: &MnemonicLanguage) -> Result; fn to_mnemonic(&self, language: &MnemonicLanguage) -> Result, MnemonicError>; } impl Mnemonic for T { /// Generates a SecretKey that represent the provided mnemonic sequence of words, the language of the mnemonic /// sequence is autodetected - fn from_mnemonic(mnemonic_seq: &Vec) -> Result { - (to_secretkey(mnemonic_seq)) + fn from_mnemonic(mnemonic_seq: &[String]) -> Result { + to_secretkey(mnemonic_seq) } /// Generates a SecretKey that represent the provided mnemonic sequence of words using the specified language - fn from_mnemonic_with_language( - mnemonic_seq: &Vec, - language: &MnemonicLanguage, - ) -> Result - { - (to_secretkey_with_language(mnemonic_seq, language)) + fn from_mnemonic_with_language(mnemonic_seq: &[String], language: &MnemonicLanguage) -> Result { + to_secretkey_with_language(mnemonic_seq, language) } /// Generates a mnemonic sequence of words from the provided secret key fn to_mnemonic(&self, language: &MnemonicLanguage) -> Result, MnemonicError> { - (from_secretkey(self, language)) + from_secret_key(self, language) } } @@ -254,9 +247,8 @@ impl Mnemonic for T { mod test { use super::*; use crate::mnemonic; - use rand; - use tari_crypto::{keys::SecretKey, ristretto::RistrettoSecretKey}; - use tari_utilities::byte_array::ByteArray; + use rand::{self, rngs::OsRng}; + use tari_crypto::{keys::SecretKey, ristretto::RistrettoSecretKey, tari_utilities::byte_array::ByteArray}; #[test] fn test_check_wordlists_sorted() { @@ -406,8 +398,7 @@ mod test { #[test] fn test_mnemonic_from_bytes_and_to_bytes() { - let mut rng = rand::OsRng::new().unwrap(); - let secretkey_bytes = RistrettoSecretKey::random(&mut rng).to_vec(); + let secretkey_bytes = RistrettoSecretKey::random(&mut OsRng).to_vec(); match mnemonic::from_bytes(secretkey_bytes.clone(), &MnemonicLanguage::English) { Ok(mnemonic_seq) => match mnemonic::to_bytes(&mnemonic_seq) { Ok(mnemonic_bytes) => { @@ -427,8 +418,7 @@ mod test { #[test] fn test_secretkey_to_mnemonic_and_from_mnemonic() { // Valid Mnemonic sequence - let mut rng = rand::OsRng::new().unwrap(); - let desired_k = RistrettoSecretKey::random(&mut rng); + let desired_k = RistrettoSecretKey::random(&mut OsRng); match desired_k.to_mnemonic(&MnemonicLanguage::Japanese) { Ok(mnemonic_seq) => { match RistrettoSecretKey::from_mnemonic(&mnemonic_seq) { diff --git a/base_layer/keymanager/src/mnemonic_wordlists.rs b/base_layer/key_manager/src/mnemonic_wordlists.rs similarity index 100% rename from base_layer/keymanager/src/mnemonic_wordlists.rs rename to base_layer/key_manager/src/mnemonic_wordlists.rs diff --git a/base_layer/keymanager/Cargo.toml b/base_layer/keymanager/Cargo.toml deleted file mode 100644 index 31bd954356..0000000000 --- a/base_layer/keymanager/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "tari_key_manager" -version = "0.0.5" -edition = "2018" - -[dependencies] -tari_crypto = { path = "../../infrastructure/crypto"} -tari_utilities = { path = "../../infrastructure/tari_util"} -rand = "0.5.5" -digest = "0.8.0" -sha2 = "0.8.0" -derive-error = "0.0.4" -serde = "1.0.89" -serde_derive = "1.0.89" -serde_json = "1.0.39" - diff --git a/base_layer/mining/Cargo.toml b/base_layer/mining/Cargo.toml deleted file mode 100644 index 892e9252fd..0000000000 --- a/base_layer/mining/Cargo.toml +++ /dev/null @@ -1,5 +0,0 @@ -[package] -name = "mining" -version = "0.0.5" - -[dependencies] diff --git a/base_layer/mmr/Cargo.toml b/base_layer/mmr/Cargo.toml index a3383476a9..296f3c1e15 100644 --- a/base_layer/mmr/Cargo.toml +++ b/base_layer/mmr/Cargo.toml @@ -1,23 +1,27 @@ [package] name = "tari_mmr" -version = "0.0.5" +authors = ["The Tari Development Community"] +description = "A Merkle Mountain Range implementation" +repository = "https://github.com/tari-project/tari" +license = "BSD-3-Clause" +version = "0.0.9" edition = "2018" [dependencies] -tari_utilities = { path = "../../infrastructure/tari_util", version = "^0.0" } +tari_utilities = "^0.1" derive-error = "0.0.4" digest = "0.8.0" log = "0.4" serde = { version = "1.0.97", features = ["derive"] } -croaring = "0.4.0" +croaring = "=0.3.9" tari_storage = { path = "../../infrastructure/storage", version = "^0.0" } [dev-dependencies] criterion = "0.2" rand="0.7.0" blake2 = "0.8.0" -tari_infra_derive= {path = "../../infrastructure/derive"} -tari_crypto = {path = "../../infrastructure/crypto"} +tari_infra_derive= { path = "../../infrastructure/derive", version = "^0.0" } +tari_crypto = { version = "^0.3" } serde_json = "1.0" bincode = "1.1" [lib] diff --git a/base_layer/mmr/benches/bench.rs b/base_layer/mmr/benches/bench.rs index fec253ed9d..208fb762dd 100644 --- a/base_layer/mmr/benches/bench.rs +++ b/base_layer/mmr/benches/bench.rs @@ -24,8 +24,8 @@ use blake2::Blake2b; use criterion::{criterion_group, criterion_main, Criterion}; use digest::Digest; -use std::{time::Duration, u64}; -use tari_mmr::{MerkleMountainRange, VectorBackend}; +use std::time::Duration; +use tari_mmr::MerkleMountainRange; fn get_hashes(n: usize) -> Vec> { let mut result = Vec::with_capacity(n); @@ -39,7 +39,7 @@ fn get_hashes(n: usize) -> Vec> { fn build_mmr(c: &mut Criterion) { c.bench_function("Build MMR", move |b| { let hashes = get_hashes(1000); - let mut mmr = MerkleMountainRange::::new(VectorBackend::default()); + let mut mmr = MerkleMountainRange::::new(Vec::default()); b.iter(|| { for i in 0..1000 { let _ = mmr.push(&hashes[i]); diff --git a/base_layer/mmr/src/backend.rs b/base_layer/mmr/src/backend.rs index 23375c3de1..2d1414c03f 100644 --- a/base_layer/mmr/src/backend.rs +++ b/base_layer/mmr/src/backend.rs @@ -21,6 +21,7 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::error::MerkleMountainRangeError; +use std::cmp::min; /// A trait describing generic array-like behaviour, without imposing any specific details on how this is actually done. pub trait ArrayLike { @@ -28,17 +29,23 @@ pub trait ArrayLike { type Error: std::error::Error; /// Returns the number of hashes stored in the backend - fn len(&self) -> usize; + fn len(&self) -> Result; + + /// Returns if empty + fn is_empty(&self) -> Result; /// Store a new item and return the index of the stored item fn push(&mut self, item: Self::Value) -> Result; /// Return the item at the given index - fn get(&self, index: usize) -> Option<&Self::Value>; + fn get(&self, index: usize) -> Result, Self::Error>; /// Return the item at the given index. Use this if you *know* that the index is valid. Requesting a hash for an /// invalid index may cause the a panic - fn get_or_panic(&self, index: usize) -> &Self::Value; + fn get_or_panic(&self, index: usize) -> Self::Value; + + /// Remove all stored items from the the backend. + fn clear(&mut self) -> Result<(), Self::Error>; } pub trait ArrayLikeExt { @@ -47,17 +54,24 @@ pub trait ArrayLikeExt { /// Shortens the array, keeping the first len elements and dropping the rest. fn truncate(&mut self, _len: usize) -> Result<(), MerkleMountainRangeError>; + /// Shift the array, by discarding the first n elements from the front. + fn shift(&mut self, n: usize) -> Result<(), MerkleMountainRangeError>; + /// Execute the given closure for each value in the array fn for_each(&self, f: F) -> Result<(), MerkleMountainRangeError> - where F: FnMut(Result<&Self::Value, MerkleMountainRangeError>); + where F: FnMut(Result); } -impl ArrayLike for Vec { +impl ArrayLike for Vec { type Error = MerkleMountainRangeError; type Value = T; - fn len(&self) -> usize { - Vec::len(self) + fn len(&self) -> Result { + Ok(Vec::len(self)) + } + + fn is_empty(&self) -> Result { + Ok(Vec::is_empty(self)) } fn push(&mut self, item: Self::Value) -> Result { @@ -65,16 +79,21 @@ impl ArrayLike for Vec { Ok(self.len() - 1) } - fn get(&self, index: usize) -> Option<&Self::Value> { - (self as &[Self::Value]).get(index) + fn get(&self, index: usize) -> Result, Self::Error> { + Ok((self as &[Self::Value]).get(index).map(Clone::clone)) } - fn get_or_panic(&self, index: usize) -> &Self::Value { - &self[index] + fn get_or_panic(&self, index: usize) -> Self::Value { + self[index].clone() + } + + fn clear(&mut self) -> Result<(), Self::Error> { + Vec::clear(self); + Ok(()) } } -impl ArrayLikeExt for Vec { +impl ArrayLikeExt for Vec { type Value = T; fn truncate(&mut self, len: usize) -> Result<(), MerkleMountainRangeError> { @@ -82,9 +101,15 @@ impl ArrayLikeExt for Vec { Ok(()) } + fn shift(&mut self, n: usize) -> Result<(), MerkleMountainRangeError> { + let drain_n = min(n, self.len()); + self.drain(0..drain_n); + Ok(()) + } + fn for_each(&self, f: F) -> Result<(), MerkleMountainRangeError> - where F: FnMut(Result<&Self::Value, MerkleMountainRangeError>) { - self.iter().map(|v| Ok(v)).for_each(f); + where F: FnMut(Result) { + self.iter().map(|v| Ok(v.clone())).for_each(f); Ok(()) } } diff --git a/base_layer/mmr/src/change_tracker.rs b/base_layer/mmr/src/change_tracker.rs deleted file mode 100644 index b7be1f2769..0000000000 --- a/base_layer/mmr/src/change_tracker.rs +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright 2019. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::{ - backend::{ArrayLike, ArrayLikeExt}, - error::MerkleMountainRangeError, - pruned_mmr::{prune_mutable_mmr, PrunedMutableMmr}, - Hash, - MutableMmr, -}; -use croaring::Bitmap; -use digest::Digest; -use std::{mem, ops::Deref}; - -/// A struct that wraps an MMR to keep track of changes to the MMR over time. This enables one to roll -/// back changes to a point in history. Think of `MerkleChangeTracker` as 'git' for MMRs. -/// -/// [MutableMMr] implements [std::ops::Deref], so that once you've wrapped the MMR, all the immutable methods are -/// available through the auto-dereferencing. -/// -/// The basic philosophy of `MerkleChangeTracker` is as follows: -/// * Start with a 'base' MMR. For efficiency, you usually want to make this a [pruned_mmr::PrunedMmr], but it -/// doesn't have to be. -/// * We then maintain a change-list for every append and delete that is made on the MMR. -/// * You can `commit` the change-set at any time, which will create a new [MerkleCheckPoint] summarising the -/// changes, and the current change-set is reset. -/// * You can `rewind` to a previously committed checkpoint, p. This entails resetting the MMR to the base state and -/// then replaying every checkpoint in sequence until checkpoint p is reached. `rewind_to_start` and `replay` perform -/// similar functions. -/// * You can `reset` the ChangeTracker, which clears the current change-set and moves you back to the most recent -/// checkpoint ('HEAD') -pub struct MerkleChangeTracker -where - D: Digest, - BaseBackend: ArrayLike, -{ - base: MutableMmr, - mmr: PrunedMutableMmr, - checkpoints: CpBackend, - // The hashes added since the last commit - current_additions: Vec, - // The deletions since the last commit - current_deletions: Bitmap, -} - -impl MerkleChangeTracker -where - D: Digest, - BaseBackend: ArrayLike, - CpBackend: ArrayLike + ArrayLikeExt, -{ - /// Wrap an MMR inside a change tracker. - /// - /// # Parameters - /// * `base`: The base, or anchor point of the change tracker. This represents the earliest point that you can - /// [MerkleChangeTracker::rewind] to. - /// * `mmr`: An empty MMR instance that will be used to maintain the current state of the MMR. - /// * `diffs`: The (usually empty) collection of diffs that will be used to store the MMR checkpoints. - /// - /// # Returns - /// A new `MerkleChangeTracker` instance that is configured using the MMR and ChangeTracker instances provided. - pub fn new( - base: MutableMmr, - diffs: CpBackend, - ) -> Result, MerkleMountainRangeError> - { - let mmr = prune_mutable_mmr::(&base)?; - Ok(MerkleChangeTracker { - base, - mmr, - checkpoints: diffs, - current_additions: Vec::new(), - current_deletions: Bitmap::create(), - }) - } - - /// Return the number of Checkpoints this change tracker has recorded - pub fn checkpoint_count(&self) -> usize { - self.checkpoints.len() - } - - /// Push the given hash into the MMR and update the current change-set - pub fn push(&mut self, hash: &Hash) -> Result { - let result = self.mmr.push(hash)?; - self.current_additions.push(hash.clone()); - Ok(result) - } - - /// Discards the current change-set and resets the MMR state to that of the last checkpoint - pub fn reset(&mut self) -> Result<(), MerkleMountainRangeError> { - self.replay(self.checkpoint_count()) - } - - /// Mark a node for deletion and optionally compress the deletion bitmap. See [MutableMmr::delete_and_compress] - /// for more details - pub fn delete_and_compress(&mut self, leaf_node_index: u32, compress: bool) -> bool { - let result = self.mmr.delete_and_compress(leaf_node_index, compress); - if result { - self.current_deletions.add(leaf_node_index) - } - result - } - - /// Mark a node for completion, and compress the roaring bitmap. See [delete_and_compress] for details. - pub fn delete(&mut self, leaf_node_index: u32) -> bool { - self.delete_and_compress(leaf_node_index, true) - } - - /// Compress the roaring bitmap mapping deleted nodes. You never have to call this method unless you have been - /// calling [delete_and_compress] with `compress` set to `false` ahead of a call to [get_merkle_root]. - pub fn compress(&mut self) -> bool { - self.mmr.compress() - } - - /// Commit the change history since the last commit to a new [MerkleCheckPoint] and clear the current change set. - pub fn commit(&mut self) -> Result<(), CpBackend::Error> { - let mut hash_set = Vec::new(); - mem::swap(&mut hash_set, &mut self.current_additions); - let mut deleted_set = Bitmap::create(); - mem::swap(&mut deleted_set, &mut self.current_deletions); - let diff = MerkleCheckPoint::new(hash_set, deleted_set); - self.checkpoints.push(diff)?; - Ok(()) - } - - /// Rewind the MMR state by the given number of Checkpoints. - /// - /// Example: - /// - /// Assuming we start with an empty Mutable MMR, and apply the following: - /// push(1), push(2), delete(1), *Checkpoint* (1) - /// push(3), push(4) *Checkpoint* (2) - /// push(5), delete(4) *Checkpoint* (3) - /// push(6) - /// - /// The state is now: - /// ```text - /// 1 2 3 4 5 6 - /// x x - /// ``` - /// - /// After calling `rewind(1)`, The push of 6 wasn't check-pointed, so it will be discarded, and rewinding back one - /// point to checkpoint 2 the state will be: - /// ```text - /// 1 2 3 4 - /// x - /// ``` - /// - /// Calling `rewind(1)` again will yield: - /// ```text - /// 1 2 - /// x - /// ``` - pub fn rewind(&mut self, steps_back: usize) -> Result<(), MerkleMountainRangeError> { - self.replay(self.checkpoint_count() - steps_back) - } - - /// Rewinds the MMR back to the state of the base MMR; essentially discarding all the history accumulated to date. - pub fn rewind_to_start(&mut self) -> Result<(), MerkleMountainRangeError> { - self.mmr = self.revert_mmr_to_base()?; - Ok(()) - } - - // Common function for rewind_to_start and replay - fn revert_mmr_to_base(&mut self) -> Result, MerkleMountainRangeError> { - let mmr = prune_mutable_mmr::(&self.base)?; - self.current_deletions = Bitmap::create(); - self.current_additions = Vec::new(); - Ok(mmr) - } - - /// Similar to [MerkleChangeTracker::rewind], `replay` moves the MMR state through checkpoints, but uses the base - /// MMR as the starting point and steps forward through `num_checkpoints` checkpoints, rather than rewinding from - /// the current state. - pub fn replay(&mut self, num_checkpoints: usize) -> Result<(), MerkleMountainRangeError> { - let mut mmr = self.revert_mmr_to_base()?; - self.checkpoints.truncate(num_checkpoints)?; - let mut result = Ok(()); - self.checkpoints.for_each(|v| { - if result.is_err() { - return; - } - result = match v { - Ok(cp) => cp.apply(&mut mmr), - Err(e) => Err(e), - }; - })?; - mmr.compress(); - self.mmr = mmr; - result - } - - pub fn get_checkpoint(&self, index: usize) -> Result { - match self.checkpoints.get(index) { - None => Err(MerkleMountainRangeError::OutOfRange), - Some(cp) => Ok(cp.clone()), - } - } -} - -impl Deref for MerkleChangeTracker -where - D: Digest, - BaseBackend: ArrayLike, -{ - type Target = PrunedMutableMmr; - - fn deref(&self) -> &Self::Target { - &self.mmr - } -} - -#[derive(Debug, Clone)] -pub struct MerkleCheckPoint { - nodes_added: Vec, - nodes_deleted: Bitmap, -} - -impl MerkleCheckPoint { - pub fn new(nodes_added: Vec, nodes_deleted: Bitmap) -> MerkleCheckPoint { - MerkleCheckPoint { - nodes_added, - nodes_deleted, - } - } - - /// Apply this checkpoint to the MMR provided. Take care: The `deleted` set is not compressed after returning - /// from here. - fn apply(&self, mmr: &mut MutableMmr) -> Result<(), MerkleMountainRangeError> - where - D: Digest, - B2: ArrayLike, - { - for node in &self.nodes_added { - mmr.push(node)?; - } - mmr.deleted.or_inplace(&self.nodes_deleted); - Ok(()) - } - - /// Return a reference to the hashes of the nodes added in the checkpoint - pub fn nodes_added(&self) -> &Vec { - &self.nodes_added - } - - /// Return a reference to the roaring bitmap of nodes that were deleted in this checkpoint - pub fn nodes_deleted(&self) -> &Bitmap { - &self.nodes_deleted - } - - /// Break a checkpoint up into its constituent parts - pub fn into_parts(self) -> (Vec, Bitmap) { - (self.nodes_added, self.nodes_deleted) - } -} diff --git a/base_layer/mmr/src/common.rs b/base_layer/mmr/src/common.rs index 6263467b68..a36d556ac3 100644 --- a/base_layer/mmr/src/common.rs +++ b/base_layer/mmr/src/common.rs @@ -19,7 +19,7 @@ // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// + // Portions of this file were originally copyrighted (c) 2018 The Grin Developers, issued under the Apache License, // Version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0. @@ -28,12 +28,12 @@ use digest::Digest; const ALL_ONES: usize = std::usize::MAX; -/// Returns the MMR index of the nth leaf node -pub fn leaf_index(n: usize) -> usize { - if n == 0 { +/// Returns the MMR node index derived from the leaf index. +pub fn node_index(leaf_index: usize) -> usize { + if leaf_index == 0 { return 0; } - 2 * n - n.count_ones() as usize + 2 * leaf_index - leaf_index.count_ones() as usize } /// Is this position a leaf in the MMR? @@ -191,15 +191,15 @@ mod test { use super::*; #[test] - fn leaf_indices() { - assert_eq!(leaf_index(0), 0); - assert_eq!(leaf_index(1), 1); - assert_eq!(leaf_index(2), 3); - assert_eq!(leaf_index(3), 4); - assert_eq!(leaf_index(5), 8); - assert_eq!(leaf_index(6), 10); - assert_eq!(leaf_index(7), 11); - assert_eq!(leaf_index(8), 15); + fn leaf_to_node_indices() { + assert_eq!(node_index(0), 0); + assert_eq!(node_index(1), 1); + assert_eq!(node_index(2), 3); + assert_eq!(node_index(3), 4); + assert_eq!(node_index(5), 8); + assert_eq!(node_index(6), 10); + assert_eq!(node_index(7), 11); + assert_eq!(node_index(8), 15); } #[test] @@ -306,10 +306,10 @@ mod test { (16382, 16381), (32766, 32765), (65534, 65533), - (131070, 131069), - (262142, 262141), - (524286, 524285), - (1048574, 1048573), + (131_070, 131_069), + (262_142, 262_141), + (524_286, 524_285), + (1_048_574, 1_048_573), ]); } } diff --git a/base_layer/mmr/src/error.rs b/base_layer/mmr/src/error.rs index f99aa19370..12eb62edb3 100644 --- a/base_layer/mmr/src/error.rs +++ b/base_layer/mmr/src/error.rs @@ -22,12 +22,13 @@ use derive_error::Error; -#[derive(Debug, Error)] +#[derive(Debug, Error, PartialEq, Clone)] pub enum MerkleMountainRangeError { // The next position was not a leaf node as expected CorruptDataStructure, - // Failed to add a new hash to the backend - BackendPushError, + // A problem has been encountered with the backend + #[error(non_std, no_from)] + BackendError(String), // The Merkle tree is not internally consistent. A parent hash isn't equal to the hash of its children InvalidMerkleTree, // The tree has reached its maximum size @@ -37,4 +38,6 @@ pub enum MerkleMountainRangeError { HashNotFound(usize), // A request was out of range OutOfRange, + // Conflicting or invalid configuration parameters provided. + InvalidConfig, } diff --git a/base_layer/mmr/src/pruned_mmr.rs b/base_layer/mmr/src/functions.rs similarity index 68% rename from base_layer/mmr/src/pruned_mmr.rs rename to base_layer/mmr/src/functions.rs index b9e8398fc3..aae7e3a9c9 100644 --- a/base_layer/mmr/src/pruned_mmr.rs +++ b/base_layer/mmr/src/functions.rs @@ -65,3 +65,50 @@ where size: mmr.size, }) } + +/// `calculate_mmr_root`` takes an MMR instance and efficiently calculates the new MMR root by applying the given +/// additions to calculate a new MMR root without changing the original MMR. +/// +/// This is done by creating a memory-backed sparse (pruned) copy of the original MMR, applying the changes and then +/// calculating a new root. +/// +/// # Parameters +/// * `src`: A reference to the original MMR +/// * `additions`: A vector of leaf node hashes to append to the MMR +/// * `deletions`: A vector of leaf node _indices_ that will be marked as deleted. +/// +/// # Returns +/// The new MMR root as a result of applying the given changes +pub fn calculate_pruned_mmr_root( + src: &MutableMmr, + additions: Vec, + deletions: Vec, +) -> Result +where + D: Digest, + B: ArrayLike, +{ + let mut pruned_mmr = prune_mutable_mmr(src)?; + for hash in additions { + pruned_mmr.push(&hash)?; + } + for index in deletions { + pruned_mmr.delete(index); + } + Ok(pruned_mmr.get_merkle_root()?) +} + +pub fn calculate_mmr_root( + src: &MerkleMountainRange, + additions: Vec, +) -> Result +where + D: Digest, + B: ArrayLike, +{ + let mut mmr = prune_mmr(src)?; + for hash in additions { + mmr.push(&hash)?; + } + Ok(mmr.get_merkle_root()?) +} diff --git a/base_layer/mmr/src/lib.rs b/base_layer/mmr/src/lib.rs index cbc7c5626b..19f7e29d9b 100644 --- a/base_layer/mmr/src/lib.rs +++ b/base_layer/mmr/src/lib.rs @@ -136,27 +136,37 @@ pub type Hash = Vec; pub type HashSlice = [u8]; mod backend; -mod change_tracker; +mod mem_backend_vec; +mod merkle_checkpoint; mod merkle_mountain_range; mod merkle_proof; +mod mmr_cache; mod mutable_mmr; -mod pruned_hashset; +mod mutable_mmr_leaf_nodes; mod serde_support; // Less commonly used exports pub mod common; pub mod error; +pub mod functions; /// A function for snapshotting and pruning a Merkle Mountain Range -pub mod pruned_mmr; +pub mod pruned_hashset; // Commonly used exports /// A vector-based backend for [MerkleMountainRange] -pub use backend::ArrayLike; -/// A data structure that maintains a list of diffs on an MMR, enabling you to rewind to a previous state -pub use change_tracker::{MerkleChangeTracker, MerkleCheckPoint}; +pub use backend::{ArrayLike, ArrayLikeExt}; +/// MemBackendVec is a shareable, memory only, vector that can be be used with MmrCache to store checkpoints. +pub use mem_backend_vec::MemBackendVec; +/// A Merkle checkpoint contains the set of hash additions and deletion indices. +pub use merkle_checkpoint::MerkleCheckPoint; /// An immutable, append-only Merkle Mountain range (MMR) data structure pub use merkle_mountain_range::MerkleMountainRange; /// A data structure for proving a hash inclusion in an MMR pub use merkle_proof::{MerkleProof, MerkleProofError}; +/// The MMR cache is used to calculate Merkle and Merklish roots based on the state of the set of shared +/// checkpoints. +pub use mmr_cache::{MmrCache, MmrCacheConfig}; /// An append-only Merkle Mountain range (MMR) data structure that allows deletion of existing leaf nodes. pub use mutable_mmr::MutableMmr; +/// A data structure for storing all the data required to restore the state of an MMR. +pub use mutable_mmr_leaf_nodes::MutableMmrLeafNodes; diff --git a/base_layer/mmr/src/mem_backend_vec.rs b/base_layer/mmr/src/mem_backend_vec.rs new file mode 100644 index 0000000000..a90fe64b1f --- /dev/null +++ b/base_layer/mmr/src/mem_backend_vec.rs @@ -0,0 +1,130 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + backend::{ArrayLike, ArrayLikeExt}, + error::MerkleMountainRangeError, +}; +use std::{ + cmp::min, + sync::{Arc, RwLock}, +}; + +/// MemBackendVec is a shareable, memory only, vector that can be be used with MmrCache to store checkpoints. +#[derive(Debug, Clone, Default)] +pub struct MemBackendVec { + db: Arc>>, +} + +impl MemBackendVec { + pub fn new() -> Self { + Self { + db: Arc::new(RwLock::new(Vec::::new())), + } + } +} + +impl ArrayLike for MemBackendVec { + type Error = MerkleMountainRangeError; + type Value = T; + + fn len(&self) -> Result { + Ok(self + .db + .read() + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))? + .len()) + } + + fn is_empty(&self) -> Result { + Ok(self + .db + .read() + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))? + .is_empty()) + } + + fn push(&mut self, item: Self::Value) -> Result { + self.db + .write() + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))? + .push(item); + Ok(self.len()? - 1) + } + + fn get(&self, index: usize) -> Result, Self::Error> { + Ok(self + .db + .read() + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))? + .get(index) + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))?) + } + + fn get_or_panic(&self, index: usize) -> Self::Value { + self.db.read().unwrap()[index].clone() + } + + fn clear(&mut self) -> Result<(), Self::Error> { + self.db + .write() + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))? + .clear(); + Ok(()) + } +} + +impl ArrayLikeExt for MemBackendVec { + type Value = T; + + fn truncate(&mut self, len: usize) -> Result<(), MerkleMountainRangeError> { + self.db + .write() + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))? + .truncate(len); + Ok(()) + } + + fn shift(&mut self, n: usize) -> Result<(), MerkleMountainRangeError> { + let drain_n = min( + n, + self.len() + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))?, + ); + self.db + .write() + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))? + .drain(0..drain_n); + Ok(()) + } + + fn for_each(&self, f: F) -> Result<(), MerkleMountainRangeError> + where F: FnMut(Result) { + self.db + .read() + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))? + .iter() + .map(|v| Ok(v.clone())) + .for_each(f); + Ok(()) + } +} diff --git a/base_layer/mmr/src/merkle_checkpoint.rs b/base_layer/mmr/src/merkle_checkpoint.rs new file mode 100644 index 0000000000..985762803a --- /dev/null +++ b/base_layer/mmr/src/merkle_checkpoint.rs @@ -0,0 +1,184 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{backend::ArrayLike, error::MerkleMountainRangeError, mutable_mmr::MutableMmr, Hash}; +use croaring::Bitmap; +use digest::Digest; +use serde::{ + de::{self, Deserialize, Deserializer, MapAccess, SeqAccess, Visitor}, + ser::{Serialize, SerializeStruct, Serializer}, +}; +use std::fmt; + +#[derive(Debug, Clone)] +pub struct MerkleCheckPoint { + nodes_added: Vec, + nodes_deleted: Bitmap, +} + +impl MerkleCheckPoint { + pub fn new(nodes_added: Vec, nodes_deleted: Bitmap) -> MerkleCheckPoint { + MerkleCheckPoint { + nodes_added, + nodes_deleted, + } + } + + /// Apply this checkpoint to the MMR provided. Take care: The `deleted` set is not compressed after returning + /// from here. + pub fn apply(&self, mmr: &mut MutableMmr) -> Result<(), MerkleMountainRangeError> + where + D: Digest, + B2: ArrayLike, + { + for node in &self.nodes_added { + mmr.push(node)?; + } + mmr.deleted.or_inplace(&self.nodes_deleted); + Ok(()) + } + + /// Resets the current MerkleCheckpoint. + pub fn clear(&mut self) { + self.nodes_added.clear(); + self.nodes_deleted = Bitmap::create(); + } + + /// Add a hash to the set of nodes added. + pub fn push_addition(&mut self, hash: Hash) { + self.nodes_added.push(hash); + } + + /// Add a a deleted index to the set of deleted nodes. + pub fn push_deletion(&mut self, leaf_index: u32) { + self.nodes_deleted.add(leaf_index); + } + + /// Return a reference to the hashes of the nodes added in the checkpoint + pub fn nodes_added(&self) -> &Vec { + &self.nodes_added + } + + /// Return a reference to the roaring bitmap of nodes that were deleted in this checkpoint + pub fn nodes_deleted(&self) -> &Bitmap { + &self.nodes_deleted + } + + /// Break a checkpoint up into its constituent parts + pub fn into_parts(self) -> (Vec, Bitmap) { + (self.nodes_added, self.nodes_deleted) + } +} + +impl Serialize for MerkleCheckPoint { + fn serialize(&self, serializer: S) -> Result + where S: Serializer { + let mut state = serializer.serialize_struct("MerkleCheckPoint", 2)?; + state.serialize_field("nodes_added", &self.nodes_added)?; + state.serialize_field("nodes_deleted", &self.nodes_deleted.serialize())?; + state.end() + } +} + +impl<'de> Deserialize<'de> for MerkleCheckPoint { + fn deserialize(deserializer: D) -> Result + where D: Deserializer<'de> { + enum Field { + NodesAdded, + NodesDeleted, + }; + + impl<'de> Deserialize<'de> for Field { + fn deserialize(deserializer: D) -> Result + where D: Deserializer<'de> { + struct FieldVisitor; + + impl<'de> Visitor<'de> for FieldVisitor { + type Value = Field; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("`nodes_added` or `nodes_deleted`") + } + + fn visit_str(self, value: &str) -> Result + where E: de::Error { + match value { + "nodes_added" => Ok(Field::NodesAdded), + "nodes_deleted" => Ok(Field::NodesDeleted), + _ => Err(de::Error::unknown_field(value, FIELDS)), + } + } + } + + deserializer.deserialize_identifier(FieldVisitor) + } + } + + struct MerkleCheckPointVisitor; + + impl<'de> Visitor<'de> for MerkleCheckPointVisitor { + type Value = MerkleCheckPoint; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("struct MerkleCheckPoint") + } + + fn visit_seq(self, mut seq: V) -> Result + where V: SeqAccess<'de> { + let nodes_added = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(0, &self))?; + let nodes_deleted_buf: Vec = + seq.next_element()?.ok_or_else(|| de::Error::invalid_length(1, &self))?; + let nodes_deleted: Bitmap = Bitmap::deserialize(&nodes_deleted_buf); + Ok(MerkleCheckPoint::new(nodes_added, nodes_deleted)) + } + + fn visit_map(self, mut map: V) -> Result + where V: MapAccess<'de> { + let mut nodes_added = None; + let mut nodes_deleted = None; + while let Some(key) = map.next_key()? { + match key { + Field::NodesAdded => { + if nodes_added.is_some() { + return Err(de::Error::duplicate_field("nodes_added")); + } + nodes_added = Some(map.next_value()?); + }, + Field::NodesDeleted => { + if nodes_deleted.is_some() { + return Err(de::Error::duplicate_field("nodes_deleted")); + } + let nodes_deleted_buf: Vec = map.next_value()?; + nodes_deleted = Some(Bitmap::deserialize(&nodes_deleted_buf)); + }, + } + } + let nodes_added = nodes_added.ok_or_else(|| de::Error::missing_field("nodes_added"))?; + let nodes_deleted = nodes_deleted.ok_or_else(|| de::Error::missing_field("nodes_deleted"))?; + Ok(MerkleCheckPoint::new(nodes_added, nodes_deleted)) + } + } + + const FIELDS: &[&str] = &["nodes_added", "nodes_deleted"]; + deserializer.deserialize_struct("MerkleCheckPoint", FIELDS, MerkleCheckPointVisitor) + } +} diff --git a/base_layer/mmr/src/merkle_mountain_range.rs b/base_layer/mmr/src/merkle_mountain_range.rs index 090f3138fe..6cbc191de5 100644 --- a/base_layer/mmr/src/merkle_mountain_range.rs +++ b/base_layer/mmr/src/merkle_mountain_range.rs @@ -19,25 +19,26 @@ // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Portions of this file were originally copyrighted (c) 2018 The Grin Developers, issued under the Apache License, -// Version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0. use crate::{ backend::ArrayLike, - common::{bintree_height, find_peaks, hash_together, leaf_index, peak_map_height}, + common::{bintree_height, find_peaks, hash_together, n_leaves, node_index, peak_map_height}, error::MerkleMountainRangeError, Hash, }; use digest::Digest; use log::*; -use std::marker::PhantomData; +use std::{ + cmp::{max, min}, + marker::PhantomData, +}; const LOG_TARGET: &str = "mmr::merkle_mountain_range"; /// An implementation of a Merkle Mountain Range (MMR). The MMR is append-only and immutable. Only the hashes are /// stored in this data structure. The data itself can be stored anywhere as long as you can maintain a 1:1 mapping /// of the hash of that data to the leaf nodes in the MMR. +#[derive(Debug)] pub struct MerkleMountainRange where B: ArrayLike { @@ -58,53 +59,94 @@ where } } + /// Clears the MMR and assigns its state from the list of leaf hashes given in `leaf_hashes`. + pub fn assign(&mut self, leaf_hashes: Vec) -> Result<(), MerkleMountainRangeError> { + self.hashes + .clear() + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))?; + for hash in leaf_hashes { + self.push(&hash)?; + } + Ok(()) + } + /// Return the number of nodes in the full Merkle Mountain range, excluding bagged hashes #[inline(always)] - pub fn len(&self) -> usize { - self.hashes.len() + pub fn len(&self) -> Result { + self.hashes + .len() + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string())) } /// Returns true if the MMR contains no hashes - pub fn is_empty(&self) -> bool { - self.len() == 0 + pub fn is_empty(&self) -> Result { + Ok(self.len()? == 0) } /// This function returns the hash of the node index provided indexed from 0 - pub fn get_node_hash(&self, node_index: usize) -> Option<&Hash> { - self.hashes.get(node_index) + pub fn get_node_hash(&self, node_index: usize) -> Result, MerkleMountainRangeError> { + self.hashes + .get(node_index) + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string())) + } + + /// Returns the number of leaf nodes in the MMR. + pub fn get_leaf_count(&self) -> Result { + Ok(n_leaves(self.len()?)) } /// This function returns the hash of the leaf index provided, indexed from 0 - pub fn get_leaf_hash(&self, leaf_node_index: usize) -> Option<&Hash> { - self.get_node_hash(leaf_index(leaf_node_index)) + pub fn get_leaf_hash(&self, leaf_index: usize) -> Result, MerkleMountainRangeError> { + self.get_node_hash(node_index(leaf_index)) + } + + /// Returns a set of leaf hashes from the MMR. + pub fn get_leaf_hashes(&self, leaf_index: usize, count: usize) -> Result, MerkleMountainRangeError> { + let leaf_count = self.get_leaf_count()?; + if leaf_index >= leaf_count { + return Ok(Vec::new()); + } + let count = max(1, count); + let last_leaf_index = min(leaf_index + count - 1, leaf_count); + let mut leaf_hashes = Vec::with_capacity((last_leaf_index - leaf_index + 1) as usize); + for leaf_index in leaf_index..=last_leaf_index { + if let Some(hash) = self.get_leaf_hash(leaf_index)? { + leaf_hashes.push(hash); + } + } + Ok(leaf_hashes) } /// This function will return the single merkle root of the MMR by simply hashing the peaks together. /// /// Note that this differs from the bagging strategy used in other MMR implementations, and saves you a few hashes - pub fn get_merkle_root(&self) -> Hash { - if self.is_empty() { - return MerkleMountainRange::::null_hash(); + pub fn get_merkle_root(&self) -> Result { + if self.is_empty()? { + return Ok(MerkleMountainRange::::null_hash()); } let hasher = D::new(); - self.hash_to_root(hasher).result().to_vec() + Ok(self.hash_to_root(hasher)?.result().to_vec()) } - pub(crate) fn hash_to_root(&self, hasher: D) -> D { - let peaks = find_peaks(self.hashes.len()); - peaks + pub(crate) fn hash_to_root(&self, hasher: D) -> Result { + let peaks = find_peaks( + self.hashes + .len() + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))?, + ); + Ok(peaks .into_iter() .map(|i| self.hashes.get_or_panic(i)) - .fold(hasher, |hasher, h| hasher.chain(h)) + .fold(hasher, |hasher, h| hasher.chain(h))) } /// Push a new element into the MMR. Computes new related peaks at the same time if applicable. /// Returns the new length of the merkle mountain range (the number of all nodes, not just leaf nodes). pub fn push(&mut self, hash: &Hash) -> Result { - if self.is_empty() { + if self.is_empty()? { return self.push_hash(hash.clone()); } - let mut pos = self.len(); + let mut pos = self.len()?; let (peak_map, height) = peak_map_height(pos); if height != 0 { return Err(MerkleMountainRangeError::CorruptDataStructure); @@ -117,7 +159,11 @@ where let left_hash = &self.hashes.get_or_panic(left_sibling); peak *= 2; pos += 1; - let last_hash = &self.hashes.get_or_panic(self.hashes.len() - 1); + let hash_count = self + .hashes + .len() + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))?; + let last_hash = &self.hashes.get_or_panic(hash_count - 1); let new_hash = hash_together::(left_hash, last_hash); self.push_hash(new_hash)?; } @@ -127,23 +173,26 @@ where /// Walks the nodes in the MMR and revalidates all parent hashes pub fn validate(&self) -> Result<(), MerkleMountainRangeError> { // iterate on all parent nodes - for n in 0..self.len() { + for n in 0..self + .len() + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))? + { let height = bintree_height(n); if height > 0 { let hash = self - .get_node_hash(n) - .ok_or(MerkleMountainRangeError::CorruptDataStructure)?; + .get_node_hash(n)? + .ok_or_else(|| MerkleMountainRangeError::CorruptDataStructure)?; let left_pos = n - (1 << height); let right_pos = n - 1; let left_child_hash = self - .get_node_hash(left_pos) - .ok_or(MerkleMountainRangeError::CorruptDataStructure)?; + .get_node_hash(left_pos)? + .ok_or_else(|| MerkleMountainRangeError::CorruptDataStructure)?; let right_child_hash = self - .get_node_hash(right_pos) - .ok_or(MerkleMountainRangeError::CorruptDataStructure)?; + .get_node_hash(right_pos)? + .ok_or_else(|| MerkleMountainRangeError::CorruptDataStructure)?; // hash the two child nodes together with parent_pos and compare - let hash_check = hash_together::(left_child_hash, right_child_hash); - if &hash_check != hash { + let hash_check = hash_together::(&left_child_hash, &right_child_hash); + if hash_check != hash { return Err(MerkleMountainRangeError::InvalidMerkleTree); } } @@ -151,6 +200,34 @@ where Ok(()) } + /// Search for the node index of the given hash in the MMR. This is a very slow function, being O(n). In general, + /// it's better to cache the index of the hash when storing it rather than using this function, but it's here + /// for completeness. + pub fn find_node_index(&self, hash: &Hash) -> Result, MerkleMountainRangeError> { + for i in 0..self + .hashes + .len() + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))? + { + if *hash == self.hashes.get_or_panic(i) { + return Ok(Some(i)); + } + } + Ok(None) + } + + /// Search for the leaf index of the given hash in the leaf nodes of the MMR. + pub fn find_leaf_index(&self, hash: &Hash) -> Result, MerkleMountainRangeError> { + for index in 0..self.get_leaf_count()? { + if let Some(retrieved_hash) = self.get_leaf_hash(index)? { + if *hash == retrieved_hash { + return Ok(Some(index)); + } + } + } + Ok(None) + } + pub(crate) fn null_hash() -> Hash { D::digest(b"").to_vec() } @@ -158,9 +235,15 @@ where fn push_hash(&mut self, hash: Hash) -> Result { self.hashes.push(hash).map_err(|e| { error!(target: LOG_TARGET, "{:?}", e); - MerkleMountainRangeError::BackendPushError + MerkleMountainRangeError::BackendError(e.to_string()) }) } + + pub fn clear(&mut self) -> Result<(), MerkleMountainRangeError> { + self.hashes + .clear() + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string())) + } } impl PartialEq> for MerkleMountainRange @@ -170,6 +253,6 @@ where B2: ArrayLike, { fn eq(&self, other: &MerkleMountainRange) -> bool { - (self.get_merkle_root() == other.get_merkle_root()) + self.get_merkle_root() == other.get_merkle_root() } } diff --git a/base_layer/mmr/src/merkle_proof.rs b/base_layer/mmr/src/merkle_proof.rs index d826ea9f97..072695466a 100644 --- a/base_layer/mmr/src/merkle_proof.rs +++ b/base_layer/mmr/src/merkle_proof.rs @@ -19,13 +19,11 @@ // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Portions of this file were originally copyrighted (c) 2018 The Grin Developers, issued under the Apache License, -// Version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0. use crate::{ backend::ArrayLike, - common::{family, family_branch, find_peaks, hash_together, is_leaf, is_left_sibling, leaf_index}, + common::{family, family_branch, find_peaks, hash_together, is_leaf, is_left_sibling, node_index}, + error::MerkleMountainRangeError, serde_support, Hash, HashSlice, @@ -52,6 +50,7 @@ pub enum MerkleProofError { IncorrectPeakMap, // Unexpected Unexpected, + MerkleMountainRangeError(MerkleMountainRangeError), } /// A Merkle proof that proves a particular element at a particular position exists in an MMR. @@ -86,13 +85,13 @@ impl MerkleProof { /// See [MerkleProof::for_node] for more details on how the proof is constructed. pub fn for_leaf_node( mmr: &MerkleMountainRange, - leaf_pos: usize, + leaf_index: usize, ) -> Result where D: Digest, B: ArrayLike, { - let pos = leaf_index(leaf_pos); + let pos = node_index(leaf_index); MerkleProof::generate_proof(mmr, pos) } @@ -125,17 +124,17 @@ impl MerkleProof { B: ArrayLike, { // check we actually have a hash in the MMR at this pos - mmr.get_node_hash(pos).ok_or(MerkleProofError::HashNotFound(pos))?; - let mmr_size = mmr.len(); + mmr.get_node_hash(pos)? + .ok_or_else(|| MerkleProofError::HashNotFound(pos))?; + let mmr_size = mmr.len()?; let family_branch = family_branch(pos, mmr_size); // Construct a vector of sibling hashes from the candidate node's position to the local peak let path = family_branch .iter() .map(|(_, sibling)| { - mmr.get_node_hash(*sibling) - .map(|v| v.clone()) - .ok_or(MerkleProofError::HashNotFound(*sibling)) + mmr.get_node_hash(*sibling)? + .ok_or_else(|| MerkleProofError::HashNotFound(*sibling)) }) .collect::>()?; @@ -151,8 +150,8 @@ impl MerkleProof { for peak_index in peaks { if peak_index != peak_pos { let hash = mmr - .get_node_hash(peak_index) - .ok_or(MerkleProofError::HashNotFound(peak_index))? + .get_node_hash(peak_index)? + .ok_or_else(|| MerkleProofError::HashNotFound(peak_index))? .clone(); peak_hashes.push(hash); } @@ -168,10 +167,10 @@ impl MerkleProof { &self, root: &HashSlice, hash: &HashSlice, - leaf_pos: usize, + leaf_index: usize, ) -> Result<(), MerkleProofError> { - let pos = leaf_index(leaf_pos); + let pos = node_index(leaf_index); self.verify::(root, hash, pos) } @@ -256,7 +255,7 @@ impl MerkleProof { "Found edge case. pos: {}, peaks: {:?}, mmr_size: {}, siblings: {:?}, peak_path: {:?}", pos, peaks, self.mmr_size, &self.path, &self.peaks ); - return Err(MerkleProofError::Unexpected); + Err(MerkleProofError::Unexpected) } else { let parent = if is_left_sibling(sibling_pos) { hash_together::(&sibling, hash) diff --git a/base_layer/mmr/src/mmr_cache.rs b/base_layer/mmr/src/mmr_cache.rs new file mode 100644 index 0000000000..39b6f08ff9 --- /dev/null +++ b/base_layer/mmr/src/mmr_cache.rs @@ -0,0 +1,218 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + backend::ArrayLike, + error::MerkleMountainRangeError, + functions::{prune_mutable_mmr, PrunedMutableMmr}, + merkle_checkpoint::MerkleCheckPoint, + Hash, + MutableMmr, +}; +use croaring::Bitmap; +use digest::Digest; +use std::ops::Deref; + +/// Configuration for the MmrCache. +#[derive(Debug, Clone, Copy)] +pub struct MmrCacheConfig { + /// The rewind_hist_len specifies the point in history upto where the MMR can be efficiently rewound before the + /// base mmr needs to be reconstructed. + pub rewind_hist_len: usize, +} + +impl Default for MmrCacheConfig { + fn default() -> Self { + Self { rewind_hist_len: 100 } + } +} + +/// The MMR cache is used to calculate Merkle and Merklish roots based on the state of the set of shared checkpoints. It +/// can efficiently create an updated cache state when small checkpoint rewinds were detected or the checkpoint state +/// has been expanded. +#[derive(Debug)] +pub struct MmrCache +where + D: Digest, + BaseBackend: ArrayLike, +{ + // The last checkpoint index applied to the base MMR. + base_cp_index: usize, + // One more than the last checkpoint index applied to the current MMR. + curr_cp_index: usize, + // The base MMR is the anchor point of the mmr cache. A rewind can start at this state if the checkpoint tip is + // beyond the base checkpoint index. It will have to rebuild the base MMR if the checkpoint tip index is less + // than the base MMR index. + base_mmr: MutableMmr, + // The current mmr represents the latest mmr with all checkpoints applied. + pub curr_mmr: PrunedMutableMmr, + // Access to the checkpoint set. + checkpoints: CpBackend, + // Configuration for the MMR cache. + config: MmrCacheConfig, +} + +impl MmrCache +where + D: Digest, + BaseBackend: ArrayLike, + CpBackend: ArrayLike, +{ + /// Creates a new MMR cache with access to the provided set of shared checkpoints. + pub fn new( + base_mmr: BaseBackend, + checkpoints: CpBackend, + config: MmrCacheConfig, + ) -> Result, MerkleMountainRangeError> + { + let base_mmr = MutableMmr::new(base_mmr, Bitmap::create()); + let curr_mmr = prune_mutable_mmr::(&base_mmr)?; + let mut mmr_cache = MmrCache { + base_cp_index: 0, + curr_cp_index: 0, + base_mmr, + curr_mmr, + checkpoints, + config, + }; + mmr_cache.reset()?; + Ok(mmr_cache) + } + + // Calculate the base checkpoint index based on the rewind history length and the number of checkpoints. + fn calculate_base_cp_index(&mut self) -> Result { + let cp_count = self + .checkpoints + .len() + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))?; + if cp_count > self.config.rewind_hist_len { + return Ok(cp_count - self.config.rewind_hist_len); + } + Ok(0) + } + + // Reconstruct the base MMR using the shared checkpoints. The base MMR contains the state from the the first + // checkpoint to the checkpoint tip minus the minimum history length. + fn create_base_mmr(&mut self) -> Result<(), MerkleMountainRangeError> { + self.base_mmr.clear()?; + self.base_cp_index = self.calculate_base_cp_index()?; + for cp_index in 0..=self.base_cp_index { + if let Some(cp) = self + .checkpoints + .get(cp_index) + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))? + { + cp.apply(&mut self.base_mmr)?; + } + } + Ok(()) + } + + // Reconstruct the current MMR from the next checkpoint after the base MMR to the last checkpoints. + fn create_curr_mmr(&mut self) -> Result<(), MerkleMountainRangeError> { + self.curr_cp_index = self + .checkpoints + .len() + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))?; + self.curr_mmr = prune_mutable_mmr::(&self.base_mmr)?; + for cp_index in self.base_cp_index + 1..self.curr_cp_index { + if let Some(cp) = self + .checkpoints + .get(cp_index) + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))? + { + cp.apply(&mut self.curr_mmr)?; + } + } + Ok(()) + } + + // An update to the checkpoints have been detected, update the base MMR to the correct position. + fn update_base_mmr(&mut self) -> Result<(), MerkleMountainRangeError> { + let prev_cp_index = self.base_cp_index; + self.base_cp_index = self.calculate_base_cp_index()?; + if prev_cp_index < self.base_cp_index { + for cp_index in prev_cp_index + 1..=self.base_cp_index { + if let Some(cp) = self + .checkpoints + .get(cp_index) + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))? + { + cp.apply(&mut self.base_mmr)?; + } + } + } else { + self.create_base_mmr()?; + } + Ok(()) + } + + /// This function updates the state of the MMR cache based on the current state of the shared checkpoints. + pub fn update(&mut self) -> Result<(), MerkleMountainRangeError> { + let cp_count = self + .checkpoints + .len() + .map_err(|e| MerkleMountainRangeError::BackendError(e.to_string()))?; + if cp_count < self.base_cp_index { + // Checkpoint before the base MMR index, this will require a full reconstruction of the cache. + self.create_base_mmr()?; + self.create_curr_mmr()?; + } else if cp_count < self.curr_cp_index { + // A short checkpoint reorg has occured, and requires the current MMR to be reconstructed. + self.create_curr_mmr()?; + } else if cp_count > self.curr_cp_index { + // The cache has fallen behind and needs to update to the new checkpoint state. + self.update_base_mmr()?; + self.create_curr_mmr()?; + } + Ok(()) + } + + /// Reset the MmrCache and rebuild the base and current MMR state. + pub fn reset(&mut self) -> Result<(), MerkleMountainRangeError> { + self.create_base_mmr()?; + self.create_curr_mmr() + } + + /// Returns the hash of the leaf index provided, as well as its deletion status. The node has been marked for + /// deletion if the boolean value is true. + pub fn fetch_mmr_node(&self, leaf_index: u32) -> Result<(Option, bool), MerkleMountainRangeError> { + let (base_hash, base_deleted) = self.base_mmr.get_leaf_status(leaf_index)?; + let (curr_hash, curr_deleted) = self.curr_mmr.get_leaf_status(leaf_index)?; + if let Some(base_hash) = base_hash { + return Ok((Some(base_hash), base_deleted | curr_deleted)); + } + Ok((curr_hash, base_deleted | curr_deleted)) + } +} + +impl Deref for MmrCache +where + D: Digest, + BaseBackend: ArrayLike, +{ + type Target = PrunedMutableMmr; + + fn deref(&self) -> &Self::Target { + &self.curr_mmr + } +} diff --git a/base_layer/mmr/src/mutable_mmr.rs b/base_layer/mmr/src/mutable_mmr.rs index e18aa21246..a39feef7c6 100644 --- a/base_layer/mmr/src/mutable_mmr.rs +++ b/base_layer/mmr/src/mutable_mmr.rs @@ -22,8 +22,9 @@ use crate::{ backend::ArrayLike, - common::{leaf_index, n_leaves}, + common::{n_leaves, node_index}, error::MerkleMountainRangeError, + mutable_mmr_leaf_nodes::MutableMmrLeafNodes, Hash, MerkleMountainRange, }; @@ -37,6 +38,7 @@ use digest::Digest; /// /// The `MutableMmr` API maps nearly 1:1 to that of MerkleMountainRange so that you should be able to use it as a /// drop-in replacement for the latter in most cases. +#[derive(Debug)] pub struct MutableMmr where D: Digest, @@ -56,13 +58,17 @@ where B: ArrayLike, { /// Create a new mutable MMR using the backend provided - pub fn new(mmr_backend: B) -> MutableMmr { + pub fn new(mmr_backend: B, deleted: Bitmap) -> MutableMmr { let mmr = MerkleMountainRange::new(mmr_backend); - MutableMmr { - mmr, - deleted: Bitmap::create(), - size: 0, - } + MutableMmr { mmr, deleted, size: 0 } + } + + /// Clear the MutableMmr and assign the MMR state from the set of leaf_hashes and deleted nodes given in `state`. + pub fn assign(&mut self, state: MutableMmrLeafNodes) -> Result<(), MerkleMountainRangeError> { + self.mmr.assign(state.leaf_hashes)?; + self.deleted = state.deleted; + self.size = self.mmr.get_leaf_count()? as u32; + Ok(()) } /// Return the number of leaf nodes in the `MutableMmr` that have not been marked as deleted. @@ -76,45 +82,65 @@ where } /// Returns true if the the MMR contains no nodes, OR all nodes have been marked for deletion - pub fn is_empty(&self) -> bool { - self.mmr.is_empty() || self.deleted.cardinality() == self.size as u64 + pub fn is_empty(&self) -> Result { + Ok(self.mmr.is_empty()? || self.deleted.cardinality() == self.size as u64) } /// This function returns the hash of the leaf index provided, indexed from 0. If the hash does not exist, or if it /// has been marked for deletion, `None` is returned. - pub fn get_leaf_hash(&self, leaf_node_index: u32) -> Option<&Hash> { - if self.deleted.contains(leaf_node_index) { - return None; + pub fn get_leaf_hash(&self, leaf_index: u32) -> Result, MerkleMountainRangeError> { + if self.deleted.contains(leaf_index) { + return Ok(None); } - self.mmr.get_node_hash(leaf_index(leaf_node_index as usize)) + self.mmr.get_node_hash(node_index(leaf_index as usize)) } /// Returns the hash of the leaf index provided, as well as its deletion status. The node has been marked for /// deletion if the boolean value is true. - pub fn get_leaf_status(&self, leaf_node_index: u32) -> (Option<&Hash>, bool) { - let hash = self.mmr.get_node_hash(leaf_index(leaf_node_index as usize)); - let deleted = self.deleted.contains(leaf_node_index); - (hash, deleted) + pub fn get_leaf_status(&self, leaf_index: u32) -> Result<(Option, bool), MerkleMountainRangeError> { + let hash = self.mmr.get_node_hash(node_index(leaf_index as usize))?; + let deleted = self.deleted.contains(leaf_index); + Ok((hash, deleted)) + } + + /// Returns the number of leave nodes in the MMR. + pub fn get_leaf_count(&self) -> usize { + self.size as usize } /// Returns a merkle(ish) root for this merkle set. /// /// The root is calculated by concatenating the MMR merkle root with the compressed serialisation of the bitmap /// and then hashing the result. - pub fn get_merkle_root(&self) -> Hash { + pub fn get_merkle_root(&self) -> Result { // Note that two MutableMmrs could both return true for `is_empty()`, but have different merkle roots by // virtue of the fact that the underlying MMRs could be different, but all elements are marked as deleted in // both sets. - let mmr_root = self.mmr.get_merkle_root(); + let mmr_root = self.mmr.get_merkle_root()?; let mut hasher = D::new(); hasher.input(&mmr_root); - self.hash_deleted(hasher).result().to_vec() + Ok(self.hash_deleted(hasher).result().to_vec()) + } + + /// Returns only the MMR merkle root without the compressed serialisation of the bitmap + pub fn get_mmr_only_root(&self) -> Result { + self.mmr.get_merkle_root() + } + + /// See [MerkleMountainRange::find_node_index] + pub fn find_node_index(&self, hash: &Hash) -> Result, MerkleMountainRangeError> { + self.mmr.find_node_index(hash) + } + + /// See [MerkleMountainRange::find_leaf_index] + pub fn find_leaf_index(&self, hash: &Hash) -> Result, MerkleMountainRangeError> { + self.mmr.find_leaf_index(hash) } /// Push a new element into the MMR. Computes new related peaks at the same time if applicable. /// Returns the new number of leaf nodes (regardless of deleted state) in the mutable MMR pub fn push(&mut self, hash: &Hash) -> Result { - if self.size >= std::u32::MAX { + if self.size == std::u32::MAX { return Err(MerkleMountainRangeError::MaximumSizeReached); } let result = self.mmr.push(hash); @@ -143,11 +169,11 @@ where /// # Return /// The function returns true if a node was actually marked for deletion. If the index is out of bounds, or was /// already deleted, the function returns false. - pub fn delete_and_compress(&mut self, leaf_node_index: u32, compress: bool) -> bool { - if (leaf_node_index >= self.size) || self.deleted.contains(leaf_node_index) { + pub fn delete_and_compress(&mut self, leaf_index: u32, compress: bool) -> bool { + if (leaf_index >= self.size) || self.deleted.contains(leaf_index) { return false; } - self.deleted.add(leaf_node_index); + self.deleted.add(leaf_index); // The serialization is different in compressed vs. uncompressed form, but the merkle root must be 100% // deterministic based on input, so just be consistent an use the compressed form all the time. if compress { @@ -157,8 +183,8 @@ where } /// Mark a node for completion, and compress the roaring bitmap. See [delete_and_compress] for details. - pub fn delete(&mut self, leaf_node_index: u32) -> bool { - self.delete_and_compress(leaf_node_index, true) + pub fn delete(&mut self, leaf_index: u32) -> bool { + self.delete_and_compress(leaf_index, true) } /// Compress the roaring bitmap mapping deleted nodes. You never have to call this method unless you have been @@ -182,6 +208,52 @@ where hasher.input(&bitmap_ser); hasher } + + // Returns a bitmap with only the deleted nodes for the specified region in the MMR. + fn get_sub_bitmap(&self, leaf_index: usize, count: usize) -> Result { + let mut deleted = self.deleted.clone(); + if leaf_index > 0 { + deleted.remove_range_closed(0..(leaf_index - 1) as u32) + } + let leaf_count = self.mmr.get_leaf_count()?; + if leaf_count > 1 { + let last_index = leaf_index + count - 1; + if last_index < leaf_count - 1 { + deleted.remove_range_closed((last_index + 1) as u32..leaf_count as u32); + } + } + Ok(deleted) + } + + /// Returns the state of the MMR that consists of the leaf hashes and the deleted nodes. + pub fn to_leaf_nodes( + &self, + leaf_index: usize, + count: usize, + ) -> Result + { + Ok(MutableMmrLeafNodes { + leaf_hashes: self.mmr.get_leaf_hashes(leaf_index, count)?, + deleted: self.get_sub_bitmap(leaf_index, count)?, + }) + } + + /// Expose the MerkleMountainRange for verifying proofs + pub fn mmr(&self) -> &MerkleMountainRange { + &self.mmr + } + + /// Return a reference to the bitmap of deleted nodes + pub fn deleted(&self) -> &Bitmap { + &self.deleted + } + + pub fn clear(&mut self) -> Result<(), MerkleMountainRangeError> { + self.mmr.clear()?; + self.deleted = Bitmap::create(); + self.size = 0; + Ok(()) + } } impl PartialEq> for MutableMmr @@ -191,7 +263,7 @@ where B2: ArrayLike, { fn eq(&self, other: &MutableMmr) -> bool { - (self.get_merkle_root() == other.get_merkle_root()) + self.get_merkle_root() == other.get_merkle_root() } } @@ -201,7 +273,7 @@ where B: ArrayLike, { fn from(mmr: MerkleMountainRange) -> Self { - let size = n_leaves(mmr.len()) as u32; + let size = n_leaves(mmr.len().unwrap()) as u32; // TODO: fix unwrap MutableMmr { mmr, deleted: Bitmap::create(), diff --git a/base_layer/mmr/src/mutable_mmr_leaf_nodes.rs b/base_layer/mmr/src/mutable_mmr_leaf_nodes.rs new file mode 100644 index 0000000000..cd66b545ad --- /dev/null +++ b/base_layer/mmr/src/mutable_mmr_leaf_nodes.rs @@ -0,0 +1,155 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::Hash; +use croaring::Bitmap; +use serde::{ + de::{self, Deserialize, Deserializer, MapAccess, SeqAccess, Visitor}, + ser::{Serialize, SerializeStruct, Serializer}, +}; +use std::fmt; + +/// The MutableMmrLeafNodes is used to create and share a restorable state for an MMR. +#[derive(Debug, Clone, PartialEq)] +pub struct MutableMmrLeafNodes { + pub leaf_hashes: Vec, + pub deleted: Bitmap, +} + +impl MutableMmrLeafNodes { + /// Create a new MutableMmrLeafNodes using the set of leaf hashes and deleted nodes. + pub fn new(leaf_hashes: Vec, deleted: Bitmap) -> Self { + Self { leaf_hashes, deleted } + } + + /// Merge the current state with the next state bundle + pub fn combine(&mut self, next_state: MutableMmrLeafNodes) { + let MutableMmrLeafNodes { + mut leaf_hashes, + deleted, + } = next_state; + self.leaf_hashes.append(&mut leaf_hashes); + self.deleted.or_inplace(&deleted); + } +} + +impl From> for MutableMmrLeafNodes { + fn from(leaf_hashes: Vec) -> Self { + Self { + leaf_hashes, + deleted: Bitmap::create(), + } + } +} + +impl Serialize for MutableMmrLeafNodes { + fn serialize(&self, serializer: S) -> Result + where S: Serializer { + let mut state = serializer.serialize_struct("MutableMmrLeafNodes", 2)?; + state.serialize_field("leaf_hashes", &self.leaf_hashes)?; + state.serialize_field("deleted", &self.deleted.serialize())?; + state.end() + } +} + +impl<'de> Deserialize<'de> for MutableMmrLeafNodes { + fn deserialize(deserializer: D) -> Result + where D: Deserializer<'de> { + enum Field { + LeafHashes, + Deleted, + }; + + impl<'de> Deserialize<'de> for Field { + fn deserialize(deserializer: D) -> Result + where D: Deserializer<'de> { + struct FieldVisitor; + + impl<'de> Visitor<'de> for FieldVisitor { + type Value = Field; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("`leaf_hashes` or `deleted`") + } + + fn visit_str(self, value: &str) -> Result + where E: de::Error { + match value { + "leaf_hashes" => Ok(Field::LeafHashes), + "deleted" => Ok(Field::Deleted), + _ => Err(de::Error::unknown_field(value, FIELDS)), + } + } + } + + deserializer.deserialize_identifier(FieldVisitor) + } + } + + struct MutableMmrLeafNodesVisitor; + + impl<'de> Visitor<'de> for MutableMmrLeafNodesVisitor { + type Value = MutableMmrLeafNodes; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("struct MutableMmrLeafNodes") + } + + fn visit_seq(self, mut seq: V) -> Result + where V: SeqAccess<'de> { + let leaf_hashes = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(0, &self))?; + let deleted_buf: Vec = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(1, &self))?; + let deleted: Bitmap = Bitmap::deserialize(&deleted_buf); + Ok(MutableMmrLeafNodes::new(leaf_hashes, deleted)) + } + + fn visit_map(self, mut map: V) -> Result + where V: MapAccess<'de> { + let mut leaf_hashes = None; + let mut deleted = None; + while let Some(key) = map.next_key()? { + match key { + Field::LeafHashes => { + if leaf_hashes.is_some() { + return Err(de::Error::duplicate_field("nodes_added")); + } + leaf_hashes = Some(map.next_value()?); + }, + Field::Deleted => { + if deleted.is_some() { + return Err(de::Error::duplicate_field("nodes_deleted")); + } + let deleted_buf: Vec = map.next_value()?; + deleted = Some(Bitmap::deserialize(&deleted_buf)); + }, + } + } + let leaf_hashes = leaf_hashes.ok_or_else(|| de::Error::missing_field("leaf_hashes"))?; + let deleted = deleted.ok_or_else(|| de::Error::missing_field("deleted"))?; + Ok(MutableMmrLeafNodes::new(leaf_hashes, deleted)) + } + } + + const FIELDS: &[&str] = &["leaf_hashes", "deleted"]; + deserializer.deserialize_struct("MutableMmrLeafNodes", FIELDS, MutableMmrLeafNodesVisitor) + } +} diff --git a/base_layer/mmr/src/pruned_hashset.rs b/base_layer/mmr/src/pruned_hashset.rs index 3f0d80e14f..17e6ad090a 100644 --- a/base_layer/mmr/src/pruned_hashset.rs +++ b/base_layer/mmr/src/pruned_hashset.rs @@ -33,6 +33,7 @@ use std::convert::TryFrom; /// MMR with n_0 leaf nodes. /// /// The awesome thing is that this struct can be dropped into [MerkleMountainRange] as a backend and it. just. works. +#[derive(Debug)] pub struct PrunedHashSet { /// The size of the base MMR. Only peaks are available for indices less than this value base_offset: usize, @@ -52,12 +53,12 @@ where type Error = MerkleMountainRangeError; fn try_from(base_mmr: &MerkleMountainRange) -> Result { - let base_offset = base_mmr.len(); + let base_offset = base_mmr.len()?; let peak_indices = find_peaks(base_offset); let peak_hashes = peak_indices .iter() - .map(|i| match base_mmr.get_node_hash(*i) { - Some(h) => Ok(h.clone()), + .map(|i| match base_mmr.get_node_hash(*i)? { + Some(h) => Ok(h), None => Err(MerkleMountainRangeError::HashNotFound(*i)), }) .collect::>()?; @@ -75,28 +76,41 @@ impl ArrayLike for PrunedHashSet { type Value = Hash; #[inline(always)] - fn len(&self) -> usize { - self.base_offset + self.hashes.len() + fn len(&self) -> Result { + Ok(self.base_offset + self.hashes.len()) + } + + fn is_empty(&self) -> Result { + Ok(self.len()? == 0) } fn push(&mut self, item: Self::Value) -> Result { self.hashes.push(item); - Ok(self.len() - 1) + Ok(self.len()? - 1) } - fn get(&self, index: usize) -> Option<&Self::Value> { + fn get(&self, index: usize) -> Result, Self::Error> { // If the index is from before we started adding hashes, we can return the hash *if and only if* it is a peak if index < self.base_offset { - return match self.peak_indices.binary_search(&index) { - Ok(nth_peak) => Some(&self.peak_hashes[nth_peak]), + return Ok(match self.peak_indices.binary_search(&index) { + Ok(nth_peak) => Some(self.peak_hashes[nth_peak].clone()), Err(_) => None, - }; + }); } - self.hashes.get(index - self.base_offset) + Ok(self.hashes.get(index - self.base_offset)?) } - fn get_or_panic(&self, index: usize) -> &Self::Value { + fn get_or_panic(&self, index: usize) -> Self::Value { self.get(index) + .unwrap() .expect("PrunedHashSet only tracks peaks before the offset") } + + fn clear(&mut self) -> Result<(), Self::Error> { + self.base_offset = 0; + self.peak_indices.clear(); + self.peak_hashes.clear(); + self.hashes.clear(); + Ok(()) + } } diff --git a/base_layer/mmr/tests/change_tracker.rs b/base_layer/mmr/tests/change_tracker.rs deleted file mode 100644 index 95a89fb355..0000000000 --- a/base_layer/mmr/tests/change_tracker.rs +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2019. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -mod support; - -use croaring::Bitmap; -use support::{create_mmr, int_to_hash, Hasher}; -use tari_mmr::{MerkleChangeTracker, MutableMmr}; -use tari_utilities::hex::Hex; - -#[test] -fn change_tracker() { - let mmr = MutableMmr::::new(Vec::default()); - let mmr = MerkleChangeTracker::new(mmr, Vec::new()).unwrap(); - assert_eq!(mmr.checkpoint_count(), 0); - assert!(mmr.is_empty()); -} - -#[test] -/// Test the same MMR structure as the test in mutable_mmr, but add in rewinding and restoring of state -fn checkpoints() { - //----------- Construct and populate the initial MMR -------------------------- - let base = MutableMmr::::new(Vec::default()); - let mut mmr = MerkleChangeTracker::new(base, Vec::new()).unwrap(); - for i in 0..5 { - assert!(mmr.push(&int_to_hash(i)).is_ok()); - } - assert_eq!(mmr.len(), 5); - assert_eq!(mmr.checkpoint_count(), 0); - //----------- Commit the history thus far ----------------------------------- - assert!(mmr.commit().is_ok()); - assert_eq!(mmr.checkpoint_count(), 1); - let root_at_1 = mmr.get_merkle_root(); - assert_eq!( - &root_at_1.to_hex(), - "7b7ddec2af4f3d0b9b165750cf2ff15813e965d29ecd5318e0c8fea901ceaef4" - ); - //----------- Add a node and delete a few nodes ----------------------------- - assert!(mmr.push(&int_to_hash(5)).is_ok()); - assert!(mmr.delete_and_compress(0, false)); - assert!(mmr.delete_and_compress(2, false)); - assert!(mmr.delete_and_compress(4, true)); - //----------- Commit the history again, and check the expected sizes -------- - let root_at_2 = mmr.get_merkle_root(); - assert_eq!( - &root_at_2.to_hex(), - "69e69ba0c6222f2d9caa68282de0ba7f1259a0fa2b8d84af68f907ef4ec05054" - ); - assert!(mmr.commit().is_ok()); - assert_eq!(mmr.len(), 3); - assert_eq!(mmr.checkpoint_count(), 2); - //----------- Generate another checkpoint, the MMR is now empty -------- - assert!(mmr.delete_and_compress(1, false)); - assert!(mmr.delete_and_compress(5, false)); - assert!(mmr.delete(3)); - assert!(mmr.commit().is_ok()); - assert!(mmr.is_empty()); - assert_eq!(mmr.checkpoint_count(), 3); - let root = mmr.get_merkle_root(); - assert_eq!( - &root.to_hex(), - "2a540797d919e63cff8051e54ae13197315000bcfde53efd3f711bb3d24995bc" - ); - //----------- Create an empty checkpoint ------------------------------- - assert!(mmr.commit().is_ok()); - assert_eq!(mmr.checkpoint_count(), 4); - assert_eq!( - &mmr.get_merkle_root().to_hex(), - "2a540797d919e63cff8051e54ae13197315000bcfde53efd3f711bb3d24995bc" - ); - //----------- Rewind the MMR two commits---------------------------------- - assert!(mmr.rewind(2).is_ok()); - assert_eq!(mmr.get_merkle_root().to_hex(), root_at_2.to_hex()); - //----------- Perform an empty commit ------------------------------------ - assert!(mmr.commit().is_ok()); - assert_eq!(mmr.len(), 3); - assert_eq!(mmr.checkpoint_count(), 3); -} - -#[test] -fn reset_and_replay() { - // You don't have to use a Pruned MMR... any MMR implementation is fine - let base = MutableMmr::from(create_mmr(5)); - let mut mmr = MerkleChangeTracker::new(base, Vec::new()).unwrap(); - let root = mmr.get_merkle_root(); - // Add some new nodes etc - assert!(mmr.push(&int_to_hash(10)).is_ok()); - assert!(mmr.push(&int_to_hash(11)).is_ok()); - assert!(mmr.push(&int_to_hash(12)).is_ok()); - assert!(mmr.delete(7)); - // Reset - should be back to base state - assert!(mmr.reset().is_ok()); - assert_eq!(mmr.get_merkle_root(), root); - - // Change some more state - assert!(mmr.delete(1)); - assert!(mmr.delete(3)); - assert!(mmr.commit().is_ok()); //--- Checkpoint 0 --- - let root = mmr.get_merkle_root(); - - // Change a bunch more things - let hash_5 = int_to_hash(5); - assert!(mmr.push(&hash_5).is_ok()); - assert!(mmr.commit().is_ok()); //--- Checkpoint 1 --- - assert!(mmr.push(&int_to_hash(6)).is_ok()); - assert!(mmr.commit().is_ok()); //--- Checkpoint 2 --- - - assert!(mmr.push(&int_to_hash(7)).is_ok()); - assert!(mmr.commit().is_ok()); //--- Checkpoint 3 --- - assert!(mmr.delete(0)); - assert!(mmr.delete(6)); - assert!(mmr.commit().is_ok()); //--- Checkpoint 4 --- - - // Get checkpoint 1 - let cp = mmr.get_checkpoint(1).unwrap(); - assert_eq!(cp.nodes_added(), &[hash_5]); - assert_eq!(*cp.nodes_deleted(), Bitmap::create()); - - // Get checkpoint 0 - let cp = mmr.get_checkpoint(0).unwrap(); - assert!(cp.nodes_added().is_empty()); - let mut deleted = Bitmap::create(); - deleted.add(1); - deleted.add(3); - assert_eq!(*cp.nodes_deleted(), deleted); - - // Roll back to last time we save the root - assert!(mmr.replay(1).is_ok()); - assert_eq!(mmr.len(), 3); - - assert_eq!(mmr.get_merkle_root(), root); -} diff --git a/base_layer/mmr/tests/mem_backend_vec.rs b/base_layer/mmr/tests/mem_backend_vec.rs new file mode 100644 index 0000000000..46b9c51350 --- /dev/null +++ b/base_layer/mmr/tests/mem_backend_vec.rs @@ -0,0 +1,53 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use tari_mmr::{ArrayLike, ArrayLikeExt, MemBackendVec}; + +#[test] +fn len_push_get_truncate_for_each_shift_clear() { + let mut db_vec = MemBackendVec::::new(); + let mut mem_vec = vec![100, 200, 300, 400, 500, 600]; + assert_eq!(db_vec.len().unwrap(), 0); + + mem_vec.iter().for_each(|val| assert!(db_vec.push(val.clone()).is_ok())); + assert_eq!(db_vec.len().unwrap(), mem_vec.len()); + + mem_vec + .iter() + .enumerate() + .for_each(|(i, val)| assert_eq!(db_vec.get(i).unwrap(), Some(val.clone()))); + assert_eq!(db_vec.get(mem_vec.len()).unwrap(), None); + + mem_vec.truncate(4); + assert!(db_vec.truncate(4).is_ok()); + assert_eq!(db_vec.len().unwrap(), mem_vec.len()); + db_vec.for_each(|val| assert!(mem_vec.contains(&val.unwrap()))).unwrap(); + + assert!(mem_vec.shift(2).is_ok()); + assert!(db_vec.shift(2).is_ok()); + assert_eq!(db_vec.len().unwrap(), 2); + assert_eq!(db_vec.get(0).unwrap(), Some(300)); + assert_eq!(db_vec.get(1).unwrap(), Some(400)); + + assert!(db_vec.clear().is_ok()); + assert_eq!(db_vec.len().unwrap(), 0); +} diff --git a/base_layer/mmr/tests/merkle_mountain_range.rs b/base_layer/mmr/tests/merkle_mountain_range.rs index 10632d40d3..14ac7b3242 100644 --- a/base_layer/mmr/tests/merkle_mountain_range.rs +++ b/base_layer/mmr/tests/merkle_mountain_range.rs @@ -20,6 +20,7 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#[allow(dead_code)] mod support; use digest::Digest; @@ -30,10 +31,10 @@ use tari_mmr::MerkleMountainRange; #[test] fn zero_length_mmr() { let mmr = MerkleMountainRange::::new(Vec::default()); - assert_eq!(mmr.len(), 0); - assert!(mmr.is_empty()); + assert_eq!(mmr.len(), Ok(0)); + assert_eq!(mmr.is_empty(), Ok(true)); let empty_hash = Hasher::digest(b"").to_vec(); - assert_eq!(&mmr.get_merkle_root(), &empty_hash); + assert_eq!(mmr.get_merkle_root(), Ok(empty_hash)); } /// Successively build up an MMR and check that the roots, heights and indices are all correct. @@ -45,16 +46,16 @@ fn build_mmr() { assert!(mmr.push(&h0).is_ok()); // The root of a single hash is the hash of that hash - assert_eq!(mmr.len(), 1); - assert_eq!(mmr.get_merkle_root(), combine_hashes(&[&h0])); + assert_eq!(mmr.len(), Ok(1)); + assert_eq!(mmr.get_merkle_root(), Ok(combine_hashes(&[&h0]))); // Two leaf item items: // 2 // 0 1 let h1 = int_to_hash(1); assert!(mmr.push(&h1).is_ok()); let h_2 = combine_hashes(&[&h0, &h1]); - assert_eq!(mmr.get_merkle_root(), combine_hashes(&[&h_2])); - assert_eq!(mmr.len(), 3); + assert_eq!(mmr.get_merkle_root(), Ok(combine_hashes(&[&h_2]))); + assert_eq!(mmr.len(), Ok(3)); // Three leaf item items: // 2 // 0 1 3 @@ -62,8 +63,8 @@ fn build_mmr() { assert!(mmr.push(&h3).is_ok()); // The root is a bagged root let root = combine_hashes(&[&h_2, &h3]); - assert_eq!(mmr.get_merkle_root(), root); - assert_eq!(mmr.len(), 4); + assert_eq!(mmr.get_merkle_root(), Ok(root)); + assert_eq!(mmr.len(), Ok(4)); // Four leaf items: // 6 // 2 5 @@ -72,8 +73,8 @@ fn build_mmr() { assert!(mmr.push(&h4).is_ok()); let h_5 = combine_hashes(&[&h3, &h4]); let h_6 = combine_hashes(&[&h_2, &h_5]); - assert_eq!(mmr.get_merkle_root(), combine_hashes(&[&h_6])); - assert_eq!(mmr.len(), 7); + assert_eq!(mmr.get_merkle_root(), Ok(combine_hashes(&[&h_6]))); + assert_eq!(mmr.len(), Ok(7)); // Five leaf items: // 6 // 2 5 @@ -81,8 +82,8 @@ fn build_mmr() { let h7 = int_to_hash(7); assert!(mmr.push(&h7).is_ok()); let root = combine_hashes(&[&h_6, &h7]); - assert_eq!(mmr.get_merkle_root(), root); - assert_eq!(mmr.len(), 8); + assert_eq!(mmr.get_merkle_root(), Ok(root)); + assert_eq!(mmr.len(), Ok(8)); // Six leaf item items: // 6 // 2 5 9 @@ -91,8 +92,8 @@ fn build_mmr() { let h_9 = combine_hashes(&[&h7, &h8]); assert!(mmr.push(&h8).is_ok()); let root = combine_hashes(&[&h_6, &h_9]); - assert_eq!(mmr.get_merkle_root(), root); - assert_eq!(mmr.len(), 10); + assert_eq!(mmr.get_merkle_root(), Ok(root)); + assert_eq!(mmr.len(), Ok(10)); } #[test] @@ -114,3 +115,42 @@ fn validate() { let mmr = create_mmr(65); assert!(mmr.validate().is_ok()); } + +#[test] +fn restore_from_leaf_hashes() { + let mut mmr = MerkleMountainRange::::new(Vec::default()); + let leaf_hashes = mmr.get_leaf_hashes(0, 1).unwrap(); + assert_eq!(leaf_hashes.len(), 0); + + let h0 = int_to_hash(0); + let h1 = int_to_hash(1); + let h2 = int_to_hash(2); + let h3 = int_to_hash(3); + assert!(mmr.push(&h0).is_ok()); + assert!(mmr.push(&h1).is_ok()); + assert!(mmr.push(&h2).is_ok()); + assert!(mmr.push(&h3).is_ok()); + assert_eq!(mmr.len(), Ok(7)); + + // Construct MMR state from multiple leaf hash queries. + let leaf_count = mmr.get_leaf_count().unwrap(); + let mut leaf_hashes = mmr.get_leaf_hashes(0, 2).unwrap(); + leaf_hashes.append(&mut mmr.get_leaf_hashes(2, leaf_count - 2).unwrap()); + assert_eq!(leaf_hashes.len(), 4); + assert_eq!(leaf_hashes[0], h0); + assert_eq!(leaf_hashes[1], h1); + assert_eq!(leaf_hashes[2], h2); + assert_eq!(leaf_hashes[3], h3); + + assert!(mmr.push(&int_to_hash(4)).is_ok()); + assert!(mmr.push(&int_to_hash(5)).is_ok()); + assert_eq!(mmr.len(), Ok(10)); + + assert!(mmr.assign(leaf_hashes).is_ok()); + assert_eq!(mmr.len(), Ok(7)); + assert_eq!(mmr.get_leaf_hash(0), Ok(Some(h0))); + assert_eq!(mmr.get_leaf_hash(1), Ok(Some(h1))); + assert_eq!(mmr.get_leaf_hash(2), Ok(Some(h2))); + assert_eq!(mmr.get_leaf_hash(3), Ok(Some(h3))); + assert_eq!(mmr.get_leaf_hash(4), Ok(None)); +} diff --git a/base_layer/mmr/tests/merkle_proof.rs b/base_layer/mmr/tests/merkle_proof.rs index 06d3dd7f52..897d74681d 100644 --- a/base_layer/mmr/tests/merkle_proof.rs +++ b/base_layer/mmr/tests/merkle_proof.rs @@ -21,15 +21,16 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // +#[allow(dead_code)] mod support; use support::{create_mmr, int_to_hash, Hasher}; +use tari_crypto::tari_utilities::hex::{self, Hex}; use tari_mmr::{ - common::{is_leaf, leaf_index}, + common::{is_leaf, node_index}, MerkleProof, MerkleProofError, }; -use tari_utilities::hex::{self, Hex}; #[test] fn zero_size_mmr() { @@ -45,9 +46,9 @@ fn zero_size_mmr() { fn merkle_proof_small_mmrs() { for size in 1..32 { let mmr = create_mmr(size); - let root = mmr.get_merkle_root(); + let root = mmr.get_merkle_root().unwrap(); let mut hash_value = 0usize; - for pos in 0..mmr.len() { + for pos in 0..mmr.len().unwrap() { if is_leaf(pos) { let hash = int_to_hash(hash_value); hash_value += 1; @@ -64,9 +65,9 @@ fn merkle_proof_small_mmrs() { fn med_mmr() { let size = 500; let mmr = create_mmr(size); - let root = mmr.get_merkle_root(); + let root = mmr.get_merkle_root().unwrap(); let i = 499; - let pos = leaf_index(i); + let pos = node_index(i); let hash = int_to_hash(i); let proof = MerkleProof::for_node(&mmr, pos).unwrap(); assert!(proof.verify::(&root, &hash, pos).is_ok()); @@ -76,8 +77,8 @@ fn med_mmr() { fn a_big_proof() { let mmr = create_mmr(100_000); let leaf_pos = 28_543; - let mmr_index = leaf_index(leaf_pos); - let root = mmr.get_merkle_root(); + let mmr_index = node_index(leaf_pos); + let root = mmr.get_merkle_root().unwrap(); let hash = int_to_hash(leaf_pos); let proof = MerkleProof::for_node(&mmr, mmr_index).unwrap(); assert!(proof.verify::(&root, &hash, mmr_index).is_ok()) @@ -86,7 +87,7 @@ fn a_big_proof() { #[test] fn for_leaf_node() { let mmr = create_mmr(100); - let root = mmr.get_merkle_root(); + let root = mmr.get_merkle_root().unwrap(); let leaf_pos = 28; let hash = int_to_hash(leaf_pos); let proof = MerkleProof::for_leaf_node(&mmr, leaf_pos).unwrap(); diff --git a/base_layer/mmr/tests/mmr_cache.rs b/base_layer/mmr/tests/mmr_cache.rs new file mode 100644 index 0000000000..c002f94642 --- /dev/null +++ b/base_layer/mmr/tests/mmr_cache.rs @@ -0,0 +1,91 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#[allow(dead_code)] +mod support; + +use croaring::Bitmap; +use support::{combine_hashes, int_to_hash, Hasher}; +use tari_mmr::{ArrayLike, ArrayLikeExt, MemBackendVec, MerkleCheckPoint, MmrCache, MmrCacheConfig}; + +#[test] +fn create_cache_update_and_rewind() { + let config = MmrCacheConfig { rewind_hist_len: 2 }; + let mut checkpoint_db = MemBackendVec::::new(); + let mut mmr_cache = MmrCache::::new(Vec::new(), checkpoint_db.clone(), config).unwrap(); + + let h1 = int_to_hash(1); + let h2 = int_to_hash(2); + let h3 = int_to_hash(3); + let h4 = int_to_hash(4); + let h5 = int_to_hash(5); + let h6 = int_to_hash(6); + let h7 = int_to_hash(7); + let h8 = int_to_hash(8); + let ha = combine_hashes(&[&h1, &h2]); + let hb = combine_hashes(&[&h3, &h4]); + let hc = combine_hashes(&[&h5, &h6]); + let hd = combine_hashes(&[&h7, &h8]); + let hahb = combine_hashes(&[&ha, &hb]); + let hchd = combine_hashes(&[&hc, &hd]); + let cp1_mmr_only_root = combine_hashes(&[&ha]); + let cp2_mmr_only_root = combine_hashes(&[&hahb]); + let cp3_mmr_only_root = combine_hashes(&[&hahb, &hc]); + let cp4_mmr_only_root = combine_hashes(&[&combine_hashes(&[&hahb, &hchd])]); + + checkpoint_db + .push(MerkleCheckPoint::new(vec![h1.clone(), h2.clone()], Bitmap::create())) + .unwrap(); + assert!(mmr_cache.update().is_ok()); + assert_eq!(mmr_cache.get_mmr_only_root(), Ok(cp1_mmr_only_root.clone())); + + checkpoint_db + .push(MerkleCheckPoint::new(vec![h3.clone(), h4.clone()], Bitmap::create())) + .unwrap(); + assert!(mmr_cache.update().is_ok()); + assert_eq!(mmr_cache.get_mmr_only_root(), Ok(cp2_mmr_only_root.clone())); + + // Two checkpoint update + checkpoint_db + .push(MerkleCheckPoint::new(vec![h5.clone(), h6.clone()], Bitmap::create())) + .unwrap(); + checkpoint_db + .push(MerkleCheckPoint::new(vec![h7.clone(), h8.clone()], Bitmap::create())) + .unwrap(); + assert!(mmr_cache.update().is_ok()); + assert_eq!(mmr_cache.get_mmr_only_root(), Ok(cp4_mmr_only_root.clone())); + + // No rewind + checkpoint_db.truncate(4).unwrap(); + assert!(mmr_cache.update().is_ok()); + assert_eq!(mmr_cache.get_mmr_only_root(), Ok(cp4_mmr_only_root)); + + // Only current MMR update + checkpoint_db.truncate(3).unwrap(); + assert!(mmr_cache.update().is_ok()); + assert_eq!(mmr_cache.get_mmr_only_root(), Ok(cp3_mmr_only_root)); + + // Full cache update + checkpoint_db.truncate(1).unwrap(); + assert!(mmr_cache.update().is_ok()); + assert_eq!(mmr_cache.get_mmr_only_root(), Ok(cp1_mmr_only_root)); +} diff --git a/base_layer/mmr/tests/mutable_mmr.rs b/base_layer/mmr/tests/mutable_mmr.rs index 4a7719018c..c13c0d3179 100644 --- a/base_layer/mmr/tests/mutable_mmr.rs +++ b/base_layer/mmr/tests/mutable_mmr.rs @@ -20,13 +20,14 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#[allow(dead_code)] mod support; use croaring::Bitmap; use digest::Digest; use support::{create_mmr, int_to_hash, Hasher}; +use tari_crypto::tari_utilities::hex::Hex; use tari_mmr::{Hash, HashSlice, MutableMmr}; -use tari_utilities::hex::Hex; fn hash_with_bitmap(hash: &HashSlice, bitmap: &mut Bitmap) -> Hash { bitmap.run_optimize(); @@ -37,26 +38,26 @@ fn hash_with_bitmap(hash: &HashSlice, bitmap: &mut Bitmap) -> Hash { /// MMRs with no elements should provide sane defaults. The merkle root must be the hash of an empty string, b"". #[test] fn zero_length_mmr() { - let mmr = MutableMmr::::new(Vec::default()); + let mmr = MutableMmr::::new(Vec::default(), Bitmap::create()); assert_eq!(mmr.len(), 0); - assert!(mmr.is_empty()); + assert_eq!(mmr.is_empty(), Ok(true)); let empty_hash = Hasher::digest(b"").to_vec(); assert_eq!( mmr.get_merkle_root(), - hash_with_bitmap(&empty_hash, &mut Bitmap::create()) + Ok(hash_with_bitmap(&empty_hash, &mut Bitmap::create())) ); } #[test] // Note the hardcoded hashes are only valid when using Blake256 as the Hasher fn delete() { - let mut mmr = MutableMmr::::new(Vec::default()); - assert!(mmr.is_empty()); + let mut mmr = MutableMmr::::new(Vec::default(), Bitmap::create()); + assert_eq!(mmr.is_empty(), Ok(true)); for i in 0..5 { assert!(mmr.push(&int_to_hash(i)).is_ok()); } assert_eq!(mmr.len(), 5); - let root = mmr.get_merkle_root(); + let root = mmr.get_merkle_root().unwrap(); assert_eq!( &root.to_hex(), "7b7ddec2af4f3d0b9b165750cf2ff15813e965d29ecd5318e0c8fea901ceaef4" @@ -64,20 +65,20 @@ fn delete() { // Can't delete past bounds assert_eq!(mmr.delete_and_compress(5, true), false); assert_eq!(mmr.len(), 5); - assert!(!mmr.is_empty()); - assert_eq!(mmr.get_merkle_root(), root); + assert_eq!(mmr.is_empty(), Ok(false)); + assert_eq!(mmr.get_merkle_root(), Ok(root)); // Delete some nodes assert!(mmr.push(&int_to_hash(5)).is_ok()); assert!(mmr.delete_and_compress(0, false)); assert!(mmr.delete_and_compress(2, false)); assert!(mmr.delete_and_compress(4, true)); - let root = mmr.get_merkle_root(); + let root = mmr.get_merkle_root().unwrap(); assert_eq!( &root.to_hex(), "69e69ba0c6222f2d9caa68282de0ba7f1259a0fa2b8d84af68f907ef4ec05054" ); assert_eq!(mmr.len(), 3); - assert!(!mmr.is_empty()); + assert_eq!(mmr.is_empty(), Ok(false)); // Can't delete that which has already been deleted assert!(!mmr.delete_and_compress(0, false)); assert!(!mmr.delete_and_compress(2, false)); @@ -85,15 +86,15 @@ fn delete() { // .. or beyond bounds of MMR assert!(!mmr.delete_and_compress(99, true)); assert_eq!(mmr.len(), 3); - assert!(!mmr.is_empty()); + assert_eq!(mmr.is_empty(), Ok(false)); // Merkle root should not have changed: - assert_eq!(mmr.get_merkle_root(), root); + assert_eq!(mmr.get_merkle_root(), Ok(root)); assert!(mmr.delete_and_compress(1, false)); assert!(mmr.delete_and_compress(5, false)); assert!(mmr.delete(3)); assert_eq!(mmr.len(), 0); - assert!(mmr.is_empty()); - let root = mmr.get_merkle_root(); + assert_eq!(mmr.is_empty(), Ok(true)); + let root = mmr.get_merkle_root().unwrap(); assert_eq!( &root.to_hex(), "2a540797d919e63cff8051e54ae13197315000bcfde53efd3f711bb3d24995bc" @@ -105,29 +106,29 @@ fn delete() { fn build_mmr() { // Check the mutable MMR against a standard MMR and a roaring bitmap. Create one with 5 leaf nodes *8 MMR nodes) let mmr_check = create_mmr(5); - assert_eq!(mmr_check.len(), 8); + assert_eq!(mmr_check.len(), Ok(8)); let mut bitmap = Bitmap::create(); // Create a small mutable MMR - let mut mmr = MutableMmr::::new(Vec::default()); + let mut mmr = MutableMmr::::new(Vec::default(), Bitmap::create()); for i in 0..5 { assert!(mmr.push(&int_to_hash(i)).is_ok()); } // MutableMmr::len gives the size in terms of leaf nodes: assert_eq!(mmr.len(), 5); - let mmr_root = mmr_check.get_merkle_root(); + let mmr_root = mmr_check.get_merkle_root().unwrap(); let root_check = hash_with_bitmap(&mmr_root, &mut bitmap); - assert_eq!(mmr.get_merkle_root(), root_check); + assert_eq!(mmr.get_merkle_root(), Ok(root_check)); // Delete a node assert!(mmr.delete_and_compress(3, true)); bitmap.add(3); let root_check = hash_with_bitmap(&mmr_root, &mut bitmap); - assert_eq!(mmr.get_merkle_root(), root_check); + assert_eq!(mmr.get_merkle_root(), Ok(root_check)); } #[test] fn equality_check() { - let mut ma = MutableMmr::::new(Vec::default()); - let mut mb = MutableMmr::::new(Vec::default()); + let mut ma = MutableMmr::::new(Vec::default(), Bitmap::create()); + let mut mb = MutableMmr::::new(Vec::default(), Bitmap::create()); assert!(ma == mb); assert!(ma.push(&int_to_hash(1)).is_ok()); assert!(ma != mb); @@ -145,3 +146,36 @@ fn equality_check() { // Now they're equal! assert!(ma == mb); } + +#[test] +fn restore_from_leaf_nodes() { + let mut mmr = MutableMmr::::new(Vec::default(), Bitmap::create()); + for i in 0..12 { + assert!(mmr.push(&int_to_hash(i)).is_ok()); + } + assert!(mmr.delete_and_compress(2, true)); + assert!(mmr.delete_and_compress(4, true)); + assert!(mmr.delete_and_compress(5, true)); + + // Request state of MMR with single call + let leaf_count = mmr.get_leaf_count(); + let mmr_state1 = mmr.to_leaf_nodes(0, leaf_count).unwrap(); + + // Request state of MMR with multiple calls + let mut mmr_state2 = mmr.to_leaf_nodes(0, 3).unwrap(); + mmr_state2.combine(mmr.to_leaf_nodes(3, 3).unwrap()); + mmr_state2.combine(mmr.to_leaf_nodes(6, leaf_count - 6).unwrap()); + assert_eq!(mmr_state1, mmr_state2); + + // Change the state more before the restore + let mmr_root = mmr.get_merkle_root(); + assert!(mmr.push(&int_to_hash(7)).is_ok()); + assert!(mmr.push(&int_to_hash(8)).is_ok()); + assert!(mmr.delete_and_compress(3, true)); + + // Restore from compact state + assert!(mmr.assign(mmr_state1.clone()).is_ok()); + assert_eq!(mmr.get_merkle_root(), mmr_root); + let restored_mmr_state = mmr.to_leaf_nodes(0, mmr.get_leaf_count()).unwrap(); + assert_eq!(restored_mmr_state, mmr_state2); +} diff --git a/base_layer/mmr/tests/pruned_mmr.rs b/base_layer/mmr/tests/pruned_mmr.rs index 2e6e4166b3..bf722e661b 100644 --- a/base_layer/mmr/tests/pruned_mmr.rs +++ b/base_layer/mmr/tests/pruned_mmr.rs @@ -20,17 +20,25 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#[allow(dead_code)] mod support; -use support::{create_mmr, int_to_hash}; -use tari_mmr::pruned_mmr::prune_mmr; +use rand::{ + distributions::{Distribution, Uniform}, + Rng, +}; +use support::{create_mmr, create_mutable_mmr, int_to_hash}; +use tari_mmr::{ + functions::{calculate_mmr_root, calculate_pruned_mmr_root, prune_mmr}, + Hash, +}; #[test] fn pruned_mmr_empty() { let mmr = create_mmr(0); let root = mmr.get_merkle_root(); let pruned = prune_mmr(&mmr).expect("Could not create empty pruned MMR"); - assert!(pruned.is_empty()); + assert_eq!(pruned.is_empty(), Ok(true)); assert_eq!(pruned.get_merkle_root(), root); } @@ -50,7 +58,62 @@ fn pruned_mmrs() { assert!(pruned.push(&int_to_hash(*size + 1)).is_ok()); assert_eq!(pruned.get_merkle_root(), mmr2.get_merkle_root()); // But you can only get recent hashes - assert!(pruned.get_leaf_hash(*size / 2).is_none()); - assert_eq!(pruned.get_leaf_hash(*size).unwrap(), &new_hash) + assert_eq!(pruned.get_leaf_hash(*size / 2), Ok(None)); + assert_eq!(pruned.get_leaf_hash(*size), Ok(Some(new_hash))) } } + +fn get_changes() -> (usize, Vec, Vec) { + let mut rng = rand::thread_rng(); + let src_size: usize = rng.gen_range(25, 150); + let addition_length = rng.gen_range(1, 100); + let additions: Vec = Uniform::from(1..1000) + .sample_iter(rng) + .take(addition_length) + .map(int_to_hash) + .collect(); + let deletions: Vec = Uniform::from(0..src_size) + .sample_iter(rng) + .take(src_size / 5) + .map(|v| v as u32) + .collect(); + (src_size, additions, deletions) +} + +/// Create a random-sized MMR. Add a random number of additions and deletions; and check the new root against the +/// result of `calculate_pruned_mmr_root` +#[test] +pub fn calculate_pruned_mmr_roots() { + let (src_size, additions, deletions) = get_changes(); + let mut src = create_mutable_mmr(src_size); + let src_root = src.get_merkle_root().expect("Did not get source root"); + let root = + calculate_pruned_mmr_root(&src, additions.clone(), deletions.clone()).expect("Did not calculate new root"); + assert_ne!(src_root, root); + // Double check + additions.iter().for_each(|h| { + src.push(h).unwrap(); + }); + deletions.iter().for_each(|i| { + src.delete(*i); + }); + let new_root = src.get_merkle_root().expect("Did not calculate new root"); + assert_eq!(root, new_root); +} + +/// Create a random-sized MMR. Add a random number of additions; and check the new root against the +/// result of `calculate_mmr_root` +#[test] +pub fn calculate_mmr_roots() { + let (src_size, additions, _) = get_changes(); + let mut src = create_mmr(src_size); + let src_root = src.get_merkle_root().expect("Did not get source root"); + let root = calculate_mmr_root(&src, additions.clone()).expect("Did not calculate new root"); + assert_ne!(src_root, root); + // Double check + additions.iter().for_each(|h| { + src.push(h).unwrap(); + }); + let new_root = src.get_merkle_root().expect("Did not calculate new root"); + assert_eq!(root, new_root); +} diff --git a/base_layer/mmr/tests/support/mod.rs b/base_layer/mmr/tests/support/mod.rs index 6a633731ac..783eac75af 100644 --- a/base_layer/mmr/tests/support/mod.rs +++ b/base_layer/mmr/tests/support/mod.rs @@ -21,9 +21,10 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // +use croaring::Bitmap; use digest::Digest; use tari_crypto::common::Blake256; -use tari_mmr::{Hash, HashSlice, MerkleMountainRange}; +use tari_mmr::{Hash, HashSlice, MerkleMountainRange, MutableMmr}; pub type Hasher = Blake256; @@ -36,6 +37,15 @@ pub fn create_mmr(size: usize) -> MerkleMountainRange> { mmr } +pub fn create_mutable_mmr(size: usize) -> MutableMmr> { + let mut mmr = MutableMmr::::new(Vec::default(), Bitmap::create()); + for i in 0..size { + let hash = int_to_hash(i); + assert!(mmr.push(&hash).is_ok()); + } + mmr +} + pub fn int_to_hash(n: usize) -> Vec { Hasher::digest(&n.to_le_bytes()).to_vec() } diff --git a/base_layer/mmr/tests/with_blake512_hash.rs b/base_layer/mmr/tests/with_blake512_hash.rs index 4563ec1f0e..1880ff2e23 100644 --- a/base_layer/mmr/tests/with_blake512_hash.rs +++ b/base_layer/mmr/tests/with_blake512_hash.rs @@ -23,8 +23,8 @@ use blake2::Blake2b; use digest::Digest; use std::string::ToString; +use tari_crypto::tari_utilities::hex::Hex; use tari_mmr::MerkleMountainRange; -use tari_utilities::hex::Hex; pub fn hash_values() -> Vec { let mut hashvalues = Vec::new(); @@ -92,9 +92,9 @@ fn create_mmr() -> MerkleMountainRange>> { fn check_mmr_hashes() { let mmr = create_mmr(); let hashes = hash_values(); - assert_eq!(mmr.len(), 42); + assert_eq!(mmr.len(), Ok(42)); for i in 0..42 { - let hash = mmr.get_node_hash(i).unwrap(); + let hash = mmr.get_node_hash(i).unwrap().unwrap(); assert_eq!(hash.to_hex(), hashes[i]); } } diff --git a/base_layer/p2p/Cargo.toml b/base_layer/p2p/Cargo.toml index 86f3f19078..b132bfa65e 100644 --- a/base_layer/p2p/Cargo.toml +++ b/base_layer/p2p/Cargo.toml @@ -1,43 +1,66 @@ [package] name = "tari_p2p" -version = "0.0.5" +version = "0.0.9" +authors = ["The Tari Development community"] +description = "Tari base layer-specific peer-to-peer communication features" +repository = "https://github.com/tari-project/tari" +homepage = "https://tari.com" +readme = "README.md" +license = "BSD-3-Clause" edition = "2018" +[features] +test-mocks = [] +pingpong-example = ["cursive"] + [dependencies] +tari_broadcast_channel = "^0.1" tari_comms = { version = "^0.0", path = "../../comms"} -tari_crypto = { version = "^0.0", path = "../../infrastructure/crypto"} -tari_utilities = { version = "^0.0", path = "../../infrastructure/tari_util"} +tari_comms_dht = { version = "^0.0", path = "../../comms/dht"} +tari_crypto = { version = "^0.3" } +tari_pubsub = "^0.1" tari_service_framework = { version = "^0.0", path = "../service_framework"} +tari_shutdown = { version = "^0.0", path="../../infrastructure/shutdown" } +tari_storage = {version = "^0.0", path = "../../infrastructure/storage"} +tari_utilities = "^0.1" -chrono = { version = "0.4.6", features = ["serde"]} -crossbeam-channel = "0.3.8" +bytes = "0.4.12" +chrono = {version = "0.4.6", features = ["serde"]} +cursive = {version = "0.12.0", optional = true} derive-error = "0.0.4" -futures = "0.1.28" +futures = {version = "^0.3.1"} lmdb-zero = "0.4.4" log = "0.4.6" -rand = "0.6.5" -rmp-serde = "0.13.7" +prost = "0.6.1" +rand = "0.7.2" serde = "1.0.90" serde_derive = "1.0.90" -tari_storage = {version = "^0.0", path = "../../infrastructure/storage"} -threadpool = "1.7.1" -tokio = "0.1.22" -tokio-threadpool = "0.1.15" -tower-service = "0.2.0" -tower-util = "0.1.0" -tracing = "0.1.5" -ttl_cache = "0.5.1" +tokio = {version="0.2.10", features=["blocking"]} +tower = "0.3.0-alpha.2" +tower-service = { version="0.3.0-alpha.2" } [dev-dependencies] +tari_test_utils = { version = "^0.0", path="../../infrastructure/test_utils" } + clap = "2.33.0" -cursive = "0.12.0" +crossbeam-channel = "0.3.8" +env_logger = "0.6.2" +futures-test = { version = "0.3.0-alpha.19", package = "futures-test-preview" } +futures-timer = "0.3.0" lazy_static = "1.3.0" -simple_logger = "1.3.0" +multiaddr = {version = "0.7.0", package = "parity-multiaddr"} stream-cancel = "0.4.4" tempdir = "0.3.7" -tokio-mock-task = "0.1.1" +tokio-macros = "0.2.4" [dev-dependencies.log4rs] version ="0.8.3" features = ["console_appender", "file_appender", "file", "yaml_format"] default-features = false + +[build-dependencies] +tari_common = { version = "^0.0", path="../../common"} + +[[example]] +name = "pingpong" +required-features = ["pingpong-example"] diff --git a/base_layer/p2p/README.md b/base_layer/p2p/README.md new file mode 100644 index 0000000000..cdf3810bb5 --- /dev/null +++ b/base_layer/p2p/README.md @@ -0,0 +1 @@ +# Tari P2P \ No newline at end of file diff --git a/base_layer/p2p/build.rs b/base_layer/p2p/build.rs new file mode 100644 index 0000000000..b7d1b6449e --- /dev/null +++ b/base_layer/p2p/build.rs @@ -0,0 +1,31 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +fn main() { + tari_common::protobuf_build::ProtoCompiler::new() + .proto_paths(&["src/proto"]) + .out_dir("src/proto") + .compile() + .unwrap(); + println!("cargo:rerun-if-changed=src/proto/liveness.proto"); + println!("cargo:rerun-if-changed=src/proto/message_type.proto"); +} diff --git a/base_layer/p2p/examples/README.md b/base_layer/p2p/examples/README.md index 57a34acb96..27799bc2c4 100644 --- a/base_layer/p2p/examples/README.md +++ b/base_layer/p2p/examples/README.md @@ -10,7 +10,6 @@ cargo run --example $name -- [args] ## C Dependencies -- [libzmq](https://github.com/zeromq/libzmq) - [ncurses](https://github.com/mirror/ncurses) --- @@ -34,6 +33,6 @@ A basic ncurses UI that sends ping and receives pong messages to a single peer u Press 'p' to send a ping. ```bash -cargo run --example pingpong -- --help -cargo run --example pingpong -- --node-identity examples/sample_identities/node-identity1.json --peer-identity examples/sample_identities/node-identity2.json +cargo run --example pingpong --features pingpong-example -- --help +cargo run --example pingpong --features pingpong-example -- --node-identity examples/sample_identities/node-identity1.json --peer-identity examples/sample_identities/node-identity2.json ``` diff --git a/base_layer/p2p/examples/example-log-config.yml b/base_layer/p2p/examples/example-log-config.yml index 930657fbf2..4622e356d9 100644 --- a/base_layer/p2p/examples/example-log-config.yml +++ b/base_layer/p2p/examples/example-log-config.yml @@ -16,14 +16,14 @@ appenders: # Set the default logging level to "debug" and attach the "base_layer" appender to the root root: - level: debug + level: trace appenders: - pingpong loggers: # Route log events sent to the "comms" logger to the "network" appender comms: - level: debug + level: trace appenders: - network additive: false \ No newline at end of file diff --git a/base_layer/p2p/examples/gen_node_identity.rs b/base_layer/p2p/examples/gen_node_identity.rs index edfb33ce1c..ff90788f8c 100644 --- a/base_layer/p2p/examples/gen_node_identity.rs +++ b/base_layer/p2p/examples/gen_node_identity.rs @@ -25,18 +25,23 @@ /// populate the peer manager in other examples. use clap::{App, Arg}; use rand::{rngs::OsRng, Rng}; -use std::{env::current_dir, fs, net::Ipv4Addr, path::Path}; +use std::{ + env::current_dir, + fs, + net::{Ipv4Addr, SocketAddr}, + path::Path, +}; use tari_comms::{ - connection::{net_address::ip::SocketAddress, NetAddress}, - peer_manager::NodeIdentity, + multiaddr::Multiaddr, + peer_manager::{NodeIdentity, PeerFeatures}, + utils::multiaddr::socketaddr_to_multiaddr, }; -use tari_utilities::message_format::MessageFormat; +use tari_crypto::tari_utilities::message_format::MessageFormat; -fn random_address() -> NetAddress { - let mut rng = OsRng::new().unwrap(); - let port = rng.gen_range(9000, std::u16::MAX); - let socket_addr: SocketAddress = (Ipv4Addr::LOCALHOST, port).into(); - socket_addr.into() +fn random_address() -> Multiaddr { + let port = OsRng.gen_range(9000, std::u16::MAX); + let socket_addr: SocketAddr = (Ipv4Addr::LOCALHOST, port).into(); + socketaddr_to_multiaddr(&socket_addr) } fn to_abs_path(path: &str) -> String { @@ -65,9 +70,8 @@ fn main() { ) .get_matches(); - let mut rng = OsRng::new().unwrap(); let address = random_address(); - let node_identity = NodeIdentity::random(&mut rng, address).unwrap(); + let node_identity = NodeIdentity::random(&mut OsRng, address, PeerFeatures::COMMUNICATION_NODE).unwrap(); let json = node_identity.to_json().unwrap(); let out_path = to_abs_path(matches.value_of("output").unwrap()); fs::write(out_path, json).unwrap(); diff --git a/base_layer/p2p/examples/gen_tor_identity.rs b/base_layer/p2p/examples/gen_tor_identity.rs new file mode 100644 index 0000000000..75e1513c06 --- /dev/null +++ b/base_layer/p2p/examples/gen_tor_identity.rs @@ -0,0 +1,98 @@ +// Copyright 2019 The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/// Generates a random node identity JSON file. A node identity contains a node's public and secret keys, it's node +/// id and an address used to establish peer connections. The files generated from this example are used to +/// populate the peer manager in other examples. +use clap::{App, Arg}; +use std::{env::current_dir, fs, path::Path}; +use tari_comms::{multiaddr::Multiaddr, tor}; +use tari_crypto::tari_utilities::message_format::MessageFormat; + +fn to_abs_path(path: &str) -> String { + let path = Path::new(path); + if path.is_absolute() { + path.to_str().unwrap().to_string() + } else { + let mut abs_path = current_dir().unwrap(); + abs_path.push(path); + abs_path.to_str().unwrap().to_string() + } +} + +#[tokio_macros::main] +async fn main() { + let matches = App::new("Tor identity file generator") + .version("1.0") + .about("Generates peer json files") + .arg( + Arg::with_name("tor-control-addr") + .value_name("TOR_CONTROL_ADDR") + .long("tor-control-addr") + .short("t") + .help("The address of the tor control server") + .takes_value(true) + .default_value("/ip4/127.0.0.1/tcp/9051"), + ) + .arg( + Arg::with_name("onion-port") + .value_name("ONION_PORT") + .long("onion-port") + .short("p") + .help("The port to use for the onion address") + .takes_value(true) + .default_value("9999"), + ) + .arg( + Arg::with_name("output") + .value_name("FILE") + .long("output") + .short("o") + .help("The relative path of the file to output") + .takes_value(true) + .required(true), + ) + .get_matches(); + + let tor_control_addr = matches + .value_of("tor-control-addr") + .unwrap() + .parse::() + .expect("Invalid tor-control-addr"); + + let port = matches + .value_of("onion-port") + .unwrap() + .parse::() + .expect("Invalid port"); + + let hidden_service = tor::HiddenServiceBuilder::new() + .with_port_mapping(port) + .with_control_server_address(tor_control_addr) + .finish() + .await + .unwrap(); + + let json = hidden_service.get_tor_identity().to_json().unwrap(); + let out_path = to_abs_path(matches.value_of("output").unwrap()); + fs::write(out_path, json).unwrap(); +} diff --git a/base_layer/p2p/examples/pingpong.rs b/base_layer/p2p/examples/pingpong.rs index 33e26eb9ad..9125df58fb 100644 --- a/base_layer/p2p/examples/pingpong.rs +++ b/base_layer/p2p/examples/pingpong.rs @@ -20,6 +20,9 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// Required to use futures::select! macro +#![recursion_limit = "256"] + /// A basic ncurses UI that sends ping and receives pong messages to a single peer using the `tari_p2p` library. /// Press 'p' to send a ping. @@ -30,40 +33,46 @@ use clap::{App, Arg}; use cursive::{ view::Identifiable, views::{Dialog, TextView}, + CbFunc, Cursive, }; +use futures::{ + channel::mpsc, + stream::{FusedStream, StreamExt}, + Stream, +}; use rand::{distributions::Alphanumeric, rngs::OsRng, Rng}; use std::{ fs, iter, - sync::{ - mpsc::{channel, RecvTimeoutError}, - Arc, - RwLock, - }, - thread, - time::Duration, + sync::{Arc, RwLock}, }; use tari_comms::{ - control_service::ControlServiceConfig, - peer_manager::{NodeIdentity, Peer, PeerFlags, PeerNodeIdentity}, + peer_manager::{NodeId, NodeIdentity, Peer, PeerFlags}, + tor, }; +use tari_crypto::tari_utilities::message_format::MessageFormat; use tari_p2p::{ + comms_connector::pubsub_connector, initialization::{initialize_comms, CommsConfig}, - ping_pong::{PingPongService, PingPongServiceApi}, - sync_services::{ServiceError, ServiceExecutor, ServiceRegistry}, + services::{ + comms_outbound::CommsOutboundServiceInitializer, + liveness::{LivenessConfig, LivenessEvent, LivenessHandle, LivenessInitializer}, + }, + transport::{TorConfig, TransportType}, }; -use tari_utilities::message_format::MessageFormat; +use tari_service_framework::StackBuilder; +use tari_shutdown::ShutdownSignal; use tempdir::TempDir; +use tokio::runtime::Runtime; -fn load_identity(path: &str) -> NodeIdentity { +fn load_file(path: &str) -> T { let contents = fs::read_to_string(path).unwrap(); - NodeIdentity::from_json(contents.as_str()).unwrap() + T::from_json(contents.as_str()).unwrap() } pub fn random_string(len: usize) -> String { - let mut rng = OsRng::new().unwrap(); - iter::repeat(()).map(|_| rng.sample(Alphanumeric)).take(len).collect() + iter::repeat(()).map(|_| OsRng.sample(Alphanumeric)).take(len).collect() } fn main() { @@ -82,8 +91,8 @@ fn main() { .arg( Arg::with_name("peer-identity") .value_name("FILE") - .long("peer-identity") - .short("p") + .long("remote-identity") + .short("r") .help("The relative path of the node identity file of the other node") .takes_value(true) .required(true), @@ -97,64 +106,139 @@ fn main() { .takes_value(true) .default_value("base_layer/p2p/examples/example-log-config.yml"), ) + .arg( + Arg::with_name("enable-tor") + .long("enable-tor") + .help("Set to enable tor"), + ) + .arg( + Arg::with_name("tor-identity") + .long("tor-identity") + .help("Path to the file containing the onion address and private key") + .takes_value(true), + ) + .arg( + Arg::with_name("tor-control-address") + .value_name("TORADDR") + .long("tor-control-addr") + .short("t") + .help("Address of the control server") + .takes_value(true) + .default_value("/ip4/127.0.0.1/tcp/9051"), + ) .get_matches(); + let mut rt = Runtime::new().expect("Failed to initialize tokio runtime"); + log4rs::init_file(matches.value_of("log-config").unwrap(), Default::default()).unwrap(); - let node_identity = load_identity(matches.value_of("node-identity").unwrap()); - let peer_identity = load_identity(matches.value_of("peer-identity").unwrap()); + let node_identity = Arc::new(load_file::(matches.value_of("node-identity").unwrap())); + let peer_identity = load_file::(matches.value_of("peer-identity").unwrap()); + let tor_control_server_addr = matches.value_of("tor-control-address").unwrap(); + let is_tor_enabled = matches.is_present("enable-tor"); + + let tor_identity = if is_tor_enabled { + Some(load_file::(matches.value_of("tor-identity").unwrap())) + } else { + None + }; + + let datastore_path = TempDir::new(random_string(8).as_str()).unwrap(); + + let (publisher, subscription_factory) = pubsub_connector(rt.handle().clone(), 100); + let subscription_factory = Arc::new(subscription_factory); + + let transport_type = if is_tor_enabled { + let tor_identity = tor_identity.unwrap(); + TransportType::Tor(TorConfig { + control_server_addr: tor_control_server_addr.parse().expect("Invalid tor-control-addr value"), + control_server_auth: Default::default(), + port_mapping: tor_identity.onion_port.into(), + identity: Some(Box::new(tor_identity)), + socks_auth: Default::default(), + }) + } else { + TransportType::Tcp { + listener_address: node_identity.public_address(), + } + }; let comms_config = CommsConfig { - public_key: node_identity.identity.public_key.clone(), - host: "0.0.0.0".parse().unwrap(), - socks_proxy_address: None, - control_service: ControlServiceConfig { - listener_address: node_identity.control_service_address().unwrap(), - socks_proxy_address: None, - requested_connection_timeout: Duration::from_millis(2000), - }, - secret_key: node_identity.secret_key.clone(), - public_address: node_identity.control_service_address().unwrap(), - datastore_path: TempDir::new(random_string(8).as_str()) - .unwrap() - .path() - .to_str() - .unwrap() - .to_string(), + transport_type, + node_identity, + datastore_path: datastore_path.path().to_str().unwrap().to_string(), peer_database_name: random_string(8), + max_concurrent_inbound_tasks: 10, + outbound_buffer_size: 10, + dht: Default::default(), }; - let pingpong_service = PingPongService::new(); - let pingpong = pingpong_service.get_api(); - let services = ServiceRegistry::new().register(pingpong_service); + let (comms, dht) = rt.block_on(initialize_comms(comms_config, publisher)).unwrap(); + + println!("Comms listening on {}", comms.listening_address()); - let comms = initialize_comms(comms_config).unwrap(); let peer = Peer::new( - peer_identity.identity.public_key.clone(), - peer_identity.identity.node_id.clone(), - peer_identity.control_service_address().unwrap().into(), + peer_identity.public_key().clone(), + peer_identity.node_id().clone(), + peer_identity.public_address().into(), PeerFlags::empty(), + peer_identity.features().clone(), ); comms.peer_manager().add_peer(peer).unwrap(); + let shutdown_signal = comms.shutdown_signal(); - let services = ServiceExecutor::execute(&comms, services); + let handles = rt + .block_on( + StackBuilder::new(rt.handle().clone(), comms.shutdown_signal()) + .add_initializer(CommsOutboundServiceInitializer::new(dht.outbound_requester())) + .add_initializer(LivenessInitializer::new( + LivenessConfig { + auto_ping_interval: None, // Some(Duration::from_secs(5)), + enable_auto_stored_message_request: false, + enable_auto_join: true, + ..Default::default() + }, + Arc::clone(&subscription_factory), + dht.dht_requester(), + )) + .finish(), + ) + .expect("Service initialization failed"); - run_ui(services, peer_identity.identity, pingpong); + let mut app = setup_ui(); - let comms = Arc::try_unwrap(comms) - .map_err(|_| ServiceError::CommsServiceOwnershipError) - .unwrap(); + // Updates the UI when pings/pongs are received + let ui_update_signal = app.cb_sink().clone(); + let liveness_handle = handles.get_handle::().unwrap(); + rt.spawn(update_ui( + ui_update_signal, + liveness_handle.clone(), + shutdown_signal.clone(), + )); - comms.shutdown().unwrap(); -} + // Send pings when 'p' is pressed + let (mut send_ping_tx, send_ping_rx) = mpsc::channel(10); + app.add_global_callback('p', move |_| { + let _ = send_ping_tx.start_send(()); + }); -lazy_static! { - /// Used to keep track of the counts displayed in the UI - /// (sent ping count, recv ping count, recv pong count) - static ref COUNTER_STATE: Arc> = Arc::new(RwLock::new((0, 0, 0))); + let ui_update_signal = app.cb_sink().clone(); + let node_to_ping = peer_identity.node_id().clone(); + rt.spawn(send_ping_on_trigger( + send_ping_rx.fuse(), + ui_update_signal, + liveness_handle, + node_to_ping, + shutdown_signal.clone(), + )); + + app.add_global_callback('q', |s| s.quit()); + app.run(); + + rt.block_on(comms.shutdown()); } -fn run_ui(services: ServiceExecutor, peer_identity: PeerNodeIdentity, pingpong_api: Arc) { +fn setup_ui() -> Cursive { let mut app = Cursive::default(); app.add_layer( @@ -163,54 +247,117 @@ fn run_ui(services: ServiceExecutor, peer_identity: PeerNodeIdentity, pingpong_a .button("Quit", |s| s.quit()), ); - let update_sink = app.cb_sink().clone(); - - let inner_api = pingpong_api.clone(); - let (shutdown_tx, shutdown_rx) = channel(); - let app_handle = thread::spawn(move || loop { - { - let mut lock = COUNTER_STATE.write().unwrap(); - *lock = ( - lock.0, - pingpong_api.ping_count().unwrap(), - pingpong_api.pong_count().unwrap(), - ); - } - update_sink.send(Box::new(update_count)).unwrap(); - match shutdown_rx.recv_timeout(Duration::from_millis(100)) { - Ok(_) => break, - Err(RecvTimeoutError::Timeout) => {}, - Err(RecvTimeoutError::Disconnected) => break, - } - }); + app +} - let ping_update_cb = app.cb_sink().clone(); +#[derive(Default)] +struct UiState { + num_pings_sent: usize, + num_pings_recv: usize, + num_pongs_recv: usize, + avg_latency: u32, +} - let pk_to_ping = peer_identity.public_key.clone(); - app.add_global_callback('p', move |_| { - inner_api.ping(pk_to_ping.clone()).unwrap(); - { - let mut lock = COUNTER_STATE.write().unwrap(); - *lock = (lock.0 + 1, lock.1, lock.2); - } - ping_update_cb.send(Box::new(update_count)).unwrap(); - }); - app.add_global_callback('q', |s| s.quit()); +lazy_static! { + /// Used to keep track of the counts displayed in the UI + /// (sent ping count, recv ping count, recv pong count) + static ref UI_STATE: Arc> = Arc::new(Default::default()); +} +type CursiveSignal = crossbeam_channel::Sender>; - app.run(); +async fn update_ui(update_sink: CursiveSignal, mut liveness_handle: LivenessHandle, mut shutdown: ShutdownSignal) { + let mut event_stream = liveness_handle.get_event_stream_fused(); + let ping_count = liveness_handle.get_ping_count().await.unwrap_or(0); + let pong_count = liveness_handle.get_pong_count().await.unwrap_or(0); + + { + let mut lock = UI_STATE.write().unwrap(); + *lock = UiState { + num_pings_sent: lock.num_pings_sent, + num_pings_recv: ping_count, + num_pongs_recv: pong_count, + avg_latency: 0, + }; + } + let _ = update_sink.send(Box::new(update_count)); - shutdown_tx.send(()).unwrap(); - services.shutdown().unwrap(); - services.join_timeout(Duration::from_millis(1000)).unwrap(); - app_handle.join().unwrap(); + loop { + ::futures::select! { + event = event_stream.select_next_some() => { + match &*event { + LivenessEvent::ReceivedPing => { + { + let mut lock = UI_STATE.write().unwrap(); + *lock = UiState { + num_pings_sent: lock.num_pings_sent, + num_pings_recv: lock.num_pings_recv + 1, + num_pongs_recv: lock.num_pongs_recv, + avg_latency: lock.avg_latency, + }; + } + + let _ = update_sink.send(Box::new(update_count)); + }, + LivenessEvent::ReceivedPong(event) => { + { + let mut lock = UI_STATE.write().unwrap(); + *lock = UiState { + num_pings_sent: lock.num_pings_sent, + num_pings_recv: lock.num_pings_recv, + num_pongs_recv: lock.num_pongs_recv + 1, + avg_latency: event.latency.unwrap_or(0), + }; + } + + let _ = update_sink.send(Box::new(update_count)); + }, + _ => {}, + } + + }, + _ = shutdown => { + log::debug!("Ping pong example UI exiting"); + break; + } + } + } } +async fn send_ping_on_trigger( + mut send_ping_trigger: impl Stream + FusedStream + Unpin, + update_signal: CursiveSignal, + mut liveness_handle: LivenessHandle, + node_to_ping: NodeId, + mut shutdown: ShutdownSignal, +) +{ + loop { + futures::select! { + _ = send_ping_trigger.next() => { + liveness_handle.send_ping(node_to_ping.clone()).await.unwrap(); + { + let mut lock = UI_STATE.write().unwrap(); + *lock = UiState { + num_pings_sent: lock.num_pings_sent + 1, + num_pings_recv: lock.num_pings_recv, + num_pongs_recv: lock.num_pongs_recv, + avg_latency: lock.avg_latency, + }; + } + update_signal.send(Box::new(update_count)).unwrap(); + }, + _ = shutdown => { + break; + } + } + } +} fn update_count(s: &mut Cursive) { s.call_on_id("counter", move |view: &mut TextView| { - let lock = COUNTER_STATE.read().unwrap(); + let lock = UI_STATE.read().unwrap(); view.set_content(format!( - "Pings sent: {}\nPings Received: {}\nPongs Received: {}\n\n(p) Send ping (q) Quit", - lock.0, lock.1, lock.2 + "Pings sent: {}\nPings Received: {}\nPongs Received: {}\nAvg. Latency: {}ms\n\n(p) Send ping (q) Quit", + lock.num_pings_sent, lock.num_pings_recv, lock.num_pongs_recv, lock.avg_latency )); }); s.set_autorefresh(true); diff --git a/base_layer/p2p/examples/pingpong_async.rs b/base_layer/p2p/examples/pingpong_async.rs deleted file mode 100644 index fea3710f53..0000000000 --- a/base_layer/p2p/examples/pingpong_async.rs +++ /dev/null @@ -1,249 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -/// A basic ncurses UI that sends ping and receives pong messages to a single peer using the `tari_p2p` library. -/// Press 'p' to send a ping. - -#[macro_use] -extern crate lazy_static; - -use clap::{App, Arg}; -use cursive::{ - view::Identifiable, - views::{Dialog, TextView}, - Cursive, -}; -use futures::{future, sync::mpsc, Future, Sink, Stream}; -use rand::{distributions::Alphanumeric, rngs::OsRng, Rng}; -use std::{ - fs, - iter, - sync::{Arc, RwLock}, - time::{Duration, Instant}, -}; -use stream_cancel::{StreamExt, Tripwire}; -use tari_comms::{ - control_service::ControlServiceConfig, - peer_manager::{NodeIdentity, Peer, PeerFlags, PeerNodeIdentity}, -}; -use tari_p2p::{ - initialization::{initialize_comms, CommsConfig}, - services::{ - comms_outbound::CommsOutboundServiceInitializer, - liveness::{LivenessHandle, LivenessInitializer, LivenessRequest, LivenessResponse}, - ServiceHandles, - ServiceName, - }, -}; -use tari_service_framework::StackBuilder; -use tari_utilities::message_format::MessageFormat; -use tempdir::TempDir; -use tokio::{runtime::Runtime, timer::Interval}; -use tower_service::Service; - -fn load_identity(path: &str) -> NodeIdentity { - let contents = fs::read_to_string(path).unwrap(); - NodeIdentity::from_json(contents.as_str()).unwrap() -} - -pub fn random_string(len: usize) -> String { - let mut rng = OsRng::new().unwrap(); - iter::repeat(()).map(|_| rng.sample(Alphanumeric)).take(len).collect() -} - -fn main() { - let matches = App::new("Tari comms peer to peer ping pong example") - .version("1.0") - .about("PingPong between two peers") - .arg( - Arg::with_name("node-identity") - .value_name("FILE") - .long("node-identity") - .short("n") - .help("The relative path of the node identity file to use for this node") - .takes_value(true) - .required(true), - ) - .arg( - Arg::with_name("peer-identity") - .value_name("FILE") - .long("peer-identity") - .short("p") - .help("The relative path of the node identity file of the other node") - .takes_value(true) - .required(true), - ) - .arg( - Arg::with_name("log-config") - .value_name("FILE") - .long("log-config") - .short("l") - .help("The relative path of the log config file of the other node") - .takes_value(true) - .default_value("base_layer/p2p/examples/example-log-config.yml"), - ) - .get_matches(); - - log4rs::init_file(matches.value_of("log-config").unwrap(), Default::default()).unwrap(); - - let node_identity = load_identity(matches.value_of("node-identity").unwrap()); - let peer_identity = load_identity(matches.value_of("peer-identity").unwrap()); - - let comms_config = CommsConfig { - public_key: node_identity.identity.public_key.clone(), - host: "0.0.0.0".parse().unwrap(), - socks_proxy_address: None, - control_service: ControlServiceConfig { - listener_address: node_identity.control_service_address().unwrap(), - socks_proxy_address: None, - requested_connection_timeout: Duration::from_millis(2000), - }, - secret_key: node_identity.secret_key.clone(), - public_address: node_identity.control_service_address().unwrap(), - datastore_path: TempDir::new(random_string(8).as_str()) - .unwrap() - .path() - .to_str() - .unwrap() - .to_string(), - peer_database_name: random_string(8), - }; - - let comms = initialize_comms(comms_config).unwrap(); - let peer = Peer::new( - peer_identity.identity.public_key.clone(), - peer_identity.identity.node_id.clone(), - peer_identity.control_service_address().unwrap().into(), - PeerFlags::empty(), - ); - comms.peer_manager().add_peer(peer).unwrap(); - - let mut rt = Runtime::new().unwrap(); - - let initialize = StackBuilder::new() - .add_initializer(CommsOutboundServiceInitializer::new(comms.outbound_message_service())) - .add_initializer(LivenessInitializer::new(Arc::clone(&comms))) - .finish(); - - let handles = rt.block_on(initialize).unwrap(); - - run_ui(peer_identity.identity, handles); - - let comms = Arc::try_unwrap(comms).map_err(|_| ()).unwrap(); - - comms.shutdown().unwrap(); -} - -lazy_static! { - /// Used to keep track of the counts displayed in the UI - /// (sent ping count, recv ping count, recv pong count) - static ref COUNTER_STATE: Arc> = Arc::new(RwLock::new((0, 0, 0))); -} - -fn run_ui(peer_identity: PeerNodeIdentity, handles: Arc) { - tokio::run(future::lazy(move || { - let mut app = Cursive::default(); - - app.add_layer( - Dialog::around(TextView::new("Loading...").with_id("counter")) - .title("PingPong") - .button("Quit", |s| s.quit()), - ); - - let (trigger, trip_wire) = Tripwire::new(); - - let update_sink = app.cb_sink().clone(); - - let mut liveness_handle = handles.get_handle::(ServiceName::Liveness).unwrap(); - let update_ui_stream = Interval::new(Instant::now(), Duration::from_millis(100)) - .take_until(trip_wire.clone()) - .map_err(|_| ()) - .for_each(move |_| { - let update_inner = update_sink.clone(); - liveness_handle - .call(LivenessRequest::GetPingCount) - .join(liveness_handle.call(LivenessRequest::GetPongCount)) - .and_then(move |(pings, pongs)| { - match (pings, pongs) { - (Ok(LivenessResponse::Count(num_pings)), Ok(LivenessResponse::Count(num_pongs))) => { - { - let mut lock = COUNTER_STATE.write().unwrap(); - *lock = (lock.0, num_pings, num_pongs); - } - let _ = update_inner.send(Box::new(update_count)); - }, - _ => {}, - } - - future::ok(()) - }) - .map_err(|_| ()) - }); - - tokio::spawn(update_ui_stream); - - let ping_update_cb = app.cb_sink().clone(); - let mut liveness_handle = handles.get_handle::(ServiceName::Liveness).unwrap(); - let pk_to_ping = peer_identity.public_key.clone(); - - let (mut send_ping_tx, send_ping_rx) = mpsc::channel(10); - app.add_global_callback('p', move |_| { - let _ = send_ping_tx.start_send(()); - }); - - let p_stream = send_ping_rx.take_until(trip_wire).for_each(move |_| { - let ping_update_inner = ping_update_cb.clone(); - liveness_handle - .call(LivenessRequest::SendPing(pk_to_ping.clone())) - .map_err(|_| ()) - .and_then(move |_| { - { - let mut lock = COUNTER_STATE.write().unwrap(); - *lock = (lock.0 + 1, lock.1, lock.2); - } - ping_update_inner.send(Box::new(update_count)).unwrap(); - future::ok(()) - }) - }); - - tokio::spawn(p_stream); - - app.add_global_callback('q', |s| s.quit()); - - app.run(); - - // Signal UI streams to stop - trigger.cancel(); - future::ok(()) - })); -} - -fn update_count(s: &mut Cursive) { - s.call_on_id("counter", move |view: &mut TextView| { - let lock = COUNTER_STATE.read().unwrap(); - view.set_content(format!( - "Pings sent: {}\nPings Received: {}\nPongs Received: {}\n\n(p) Send ping (q) Quit", - lock.0, lock.1, lock.2 - )); - }); - s.set_autorefresh(true); -} diff --git a/base_layer/p2p/examples/sample_identities/node-identity1.json b/base_layer/p2p/examples/sample_identities/node-identity1.json index 7351da716f..4370790f3d 100644 --- a/base_layer/p2p/examples/sample_identities/node-identity1.json +++ b/base_layer/p2p/examples/sample_identities/node-identity1.json @@ -1,8 +1,9 @@ { - "identity": { - "node_id": "b3892370783330158ac6f40b7d38029ee33d554cd4522d0548058c6d9cb0ce7b", - "public_key": "b6663ccdec9546d8bc528cba869cb57c28826da2dbe2df2735b795eff6605117" + "node_id": "62bb030909d58160624e52af48", + "public_key": "d4b5066214a8568cb636cb8b734117f213a59336a73ce3fc012eaf858a3fda38", + "features": { + "bits": 3 }, - "secret_key": "7756a61d2911481492e7156ce6ddb1e46c5ea24a24157c599486f929ea288c02", - "control_service_address": {"IP": "127.0.0.1:57203"} -} + "secret_key": "019388b67c5bfba732b6441f8cc5118a5547f8dea85e110da4fdd4c19936970a", + "public_address": "/ip4/127.0.0.1/tcp/33445" +} \ No newline at end of file diff --git a/base_layer/p2p/examples/sample_identities/node-identity2.json b/base_layer/p2p/examples/sample_identities/node-identity2.json index fef3473d8f..2cc657df94 100644 --- a/base_layer/p2p/examples/sample_identities/node-identity2.json +++ b/base_layer/p2p/examples/sample_identities/node-identity2.json @@ -1,8 +1,9 @@ { - "identity": { - "node_id": "037ff81d7e04338961dedbc6750eaea87b5aa5f42b90ef0b27681a78f9673c54", - "public_key": "c4b2f064fdc61229f9d3e7cc67a2e326e252ace50885f315f4d6f262fff1f869" + "node_id": "5e5c71f749f27e4defa7c1df76", + "public_key": "b646914beb88668208c18a8e73bc5ad74b834eae98ac8c2e02c85fbb58260d24", + "features": { + "bits": 3 }, - "secret_key": "b1e8f6e901c6722e71e36bf7fb8255cf5a8c31803913cab3c10428e76cca3e0e", - "control_service_address": {"IP": "127.0.0.1:48579"} -} + "secret_key": "b45aa8c64b444e861f0ac299747ff233a7572204e395baceb10082d54bd8bf02", + "public_address": "/ip4/127.0.0.1/tcp/22334" +} \ No newline at end of file diff --git a/base_layer/p2p/src/comms_connector/inbound_connector.rs b/base_layer/p2p/src/comms_connector/inbound_connector.rs new file mode 100644 index 0000000000..7f2ca4f2dd --- /dev/null +++ b/base_layer/p2p/src/comms_connector/inbound_connector.rs @@ -0,0 +1,159 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::peer_message::PeerMessage; +use futures::{task::Context, Future, Sink, SinkExt}; +use std::{error::Error, pin::Pin, sync::Arc, task::Poll}; +use tari_comms_dht::{domain_message::MessageHeader, inbound::DecryptedDhtMessage, PipelineError}; +use tower::Service; + +/// This service receives DecryptedInboundMessages, deserializes the MessageHeader and +/// sends a `PeerMessage` on the given sink. +#[derive(Clone)] +pub struct InboundDomainConnector { + sink: TSink, +} + +impl InboundDomainConnector { + pub fn new(sink: TSink) -> Self { + Self { sink } + } +} + +impl Service for InboundDomainConnector +where + TSink: Sink> + Unpin + Clone, + TSink::Error: Error + Send + Sync + 'static, +{ + type Error = PipelineError; + type Response = (); + + type Future = impl Future>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.sink).poll_ready(cx).map_err(Into::into) + } + + fn call(&mut self, msg: DecryptedDhtMessage) -> Self::Future { + Self::handle_message(self.sink.clone(), msg) + } +} + +impl InboundDomainConnector +where + TSink: Sink> + Unpin, + TSink::Error: Error + Send + Sync + 'static, +{ + async fn handle_message(mut sink: TSink, mut inbound_message: DecryptedDhtMessage) -> Result<(), PipelineError> { + let envelope_body = inbound_message + .success_mut() + .ok_or_else(|| "Message failed to decrypt")?; + let header = envelope_body + .decode_part::(0)? + .ok_or_else(|| "envelope body did not contain a header")?; + + let msg_bytes = envelope_body + .take_part(1) + .ok_or_else(|| "envelope body did not contain a message body")?; + + let DecryptedDhtMessage { + source_peer, + dht_header, + .. + } = inbound_message; + + let peer_message = PeerMessage { + message_header: header, + source_peer: Clone::clone(&*source_peer), + dht_header, + body: msg_bytes, + }; + + // If this fails there is something wrong with the sink and the pubsub middleware should not + // continue + sink.send(Arc::new(peer_message)).await.map_err(Box::new)?; + + Ok(()) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::test_utils::{make_dht_inbound_message, make_node_identity}; + use futures::{channel::mpsc, executor::block_on, StreamExt}; + use tari_comms::{message::MessageExt, wrap_in_envelope_body}; + use tari_comms_dht::{domain_message::MessageHeader, envelope::DhtMessageFlags}; + + #[test] + fn handle_message() { + let (tx, mut rx) = mpsc::channel(1); + let header = MessageHeader::new(123); + let msg = wrap_in_envelope_body!(header, b"my message".to_vec()).unwrap(); + + let inbound_message = make_dht_inbound_message( + &make_node_identity(), + msg.to_encoded_bytes().unwrap(), + DhtMessageFlags::empty(), + ); + let decrypted = DecryptedDhtMessage::succeeded(msg, inbound_message); + block_on(InboundDomainConnector::handle_message(tx, decrypted)).unwrap(); + + let peer_message = block_on(rx.next()).unwrap(); + assert_eq!(peer_message.message_header.message_type, 123); + assert_eq!(peer_message.decode_message::().unwrap(), "my message"); + } + + #[test] + fn handle_message_fail_deserialize() { + let (tx, mut rx) = mpsc::channel(1); + let header = b"dodgy header".to_vec(); + let msg = wrap_in_envelope_body!(header, b"message".to_vec()).unwrap(); + + let inbound_message = make_dht_inbound_message( + &make_node_identity(), + msg.to_encoded_bytes().unwrap(), + DhtMessageFlags::empty(), + ); + let decrypted = DecryptedDhtMessage::succeeded(msg, inbound_message); + block_on(InboundDomainConnector::handle_message(tx, decrypted)).unwrap_err(); + + assert!(rx.try_next().unwrap().is_none()); + } + + #[test] + fn handle_message_fail_send() { + // Drop the receiver of the channel, this is the only reason this middleware should return an error + // from it's call function + let (tx, _) = mpsc::channel(1); + let header = MessageHeader::new(123); + let msg = wrap_in_envelope_body!(header, b"my message".to_vec()).unwrap(); + let inbound_message = make_dht_inbound_message( + &make_node_identity(), + msg.to_encoded_bytes().unwrap(), + DhtMessageFlags::empty(), + ); + let decrypted = DecryptedDhtMessage::succeeded(msg, inbound_message); + let result = block_on(InboundDomainConnector::handle_message(tx, decrypted)); + assert!(result.is_err()); + } +} diff --git a/infrastructure/tari_util/src/protobuf.rs b/base_layer/p2p/src/comms_connector/mod.rs similarity index 87% rename from infrastructure/tari_util/src/protobuf.rs rename to base_layer/p2p/src/comms_connector/mod.rs index f6cd2fac84..96b99fcc74 100644 --- a/infrastructure/tari_util/src/protobuf.rs +++ b/base_layer/p2p/src/comms_connector/mod.rs @@ -20,10 +20,12 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -/// A small wrapper macro which includes rust files generated from .proto files -#[macro_export] -macro_rules! include_proto_package { - ($path:expr) => { - include!(concat!(env!("OUT_DIR"), "/", $path, ".rs")); - }; -} +mod inbound_connector; +mod peer_message; +mod pubsub; + +pub use self::{ + inbound_connector::InboundDomainConnector, + peer_message::PeerMessage, + pubsub::{pubsub_connector, PubsubDomainConnector, SubscriptionFactory}, +}; diff --git a/base_layer/p2p/src/comms_connector/peer_message.rs b/base_layer/p2p/src/comms_connector/peer_message.rs new file mode 100644 index 0000000000..ca6e60dd81 --- /dev/null +++ b/base_layer/p2p/src/comms_connector/peer_message.rs @@ -0,0 +1,60 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use tari_comms::{peer_manager::Peer, types::CommsPublicKey}; +use tari_comms_dht::{domain_message::MessageHeader, envelope::DhtMessageHeader}; + +/// A domain-level message +pub struct PeerMessage { + /// The message envelope header + pub dht_header: DhtMessageHeader, + /// The connected peer which sent this message + pub source_peer: Peer, + /// Domain message header + pub message_header: MessageHeader, + /// Serialized message data + pub body: Vec, +} + +impl PeerMessage { + pub fn new(dht_header: DhtMessageHeader, source_peer: Peer, message_header: MessageHeader, body: Vec) -> Self { + Self { + body, + message_header, + dht_header, + source_peer, + } + } + + pub fn origin_public_key(&self) -> &CommsPublicKey { + self.dht_header + .origin + .as_ref() + .map(|o| &o.public_key) + .unwrap_or(&self.source_peer.public_key) + } + + pub fn decode_message(&self) -> Result + where T: prost::Message + Default { + T::decode(self.body.as_slice()) + } +} diff --git a/base_layer/p2p/src/comms_connector/pubsub.rs b/base_layer/p2p/src/comms_connector/pubsub.rs new file mode 100644 index 0000000000..e86d4b54b1 --- /dev/null +++ b/base_layer/p2p/src/comms_connector/pubsub.rs @@ -0,0 +1,64 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::peer_message::PeerMessage; +use crate::{comms_connector::InboundDomainConnector, tari_message::TariMessageType}; +use futures::{channel::mpsc, FutureExt, SinkExt, StreamExt}; +use log::*; +use std::sync::Arc; +use tari_pubsub::{pubsub_channel, TopicPayload, TopicSubscriptionFactory}; +use tokio::runtime::Handle; + +const LOG_TARGET: &str = "comms::middleware::pubsub"; + +/// Alias for a pubsub-type domain connector +pub type PubsubDomainConnector = InboundDomainConnector>>; +pub type SubscriptionFactory = TopicSubscriptionFactory>; + +/// Connects `InboundDomainConnector` to a `tari_pubsub::TopicPublisher` through a buffered channel +pub fn pubsub_connector(executor: Handle, buf_size: usize) -> (PubsubDomainConnector, SubscriptionFactory) { + let (publisher, subscription_factory) = pubsub_channel(buf_size); + let (sender, receiver) = mpsc::channel(buf_size); + + // Spawn a task which forwards messages from the pubsub service to the TopicPublisher + let forwarder = receiver + // Map DomainMessage into a TopicPayload + .map(|msg: Arc| { + TariMessageType::from_i32(msg.message_header.message_type) + .map(|msg_type| TopicPayload::new(msg_type, msg)) + .ok_or_else(|| "Invalid or unrecognised Tari message type".to_string()) + }) + // Forward TopicPayloads to the publisher + .forward(publisher.sink_map_err(|err| err.to_string())) + // Log error and return unit + .map(|result| { + if let Err(err) = result { + error!( + target: LOG_TARGET, + "Error forwarding pubsub messages to publisher: {}", err + ); + } + }); + executor.spawn(forwarder); + + (InboundDomainConnector::new(sender), subscription_factory) +} diff --git a/base_layer/p2p/src/consts.rs b/base_layer/p2p/src/consts.rs deleted file mode 100644 index 1472ae129c..0000000000 --- a/base_layer/p2p/src/consts.rs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use std::time::Duration; - -/// The maximum number of peer nodes that a message will be sent to -pub const DHT_BROADCAST_NODE_COUNT: usize = 8; - -/// The maximum number of messages that can be stored using the Store-and-forward service -pub const SAF_MSG_CACHE_STORAGE_CAPACITY: usize = 10000; -/// The time-to-live duration used for storage of low priority messages by the Store-and-forward service -pub const SAF_LOW_PRIORITY_MSG_STORAGE_TTL: Duration = Duration::from_secs(6 * 60 * 60); -/// The time-to-live duration used for storage of high priority messages by the Store-and-forward service -pub const SAF_HIGH_PRIORITY_MSG_STORAGE_TTL: Duration = Duration::from_secs(24 * 60 * 60); diff --git a/base_layer/p2p/src/dht_service/dht_messages.rs b/base_layer/p2p/src/dht_service/dht_messages.rs deleted file mode 100644 index 21b887decd..0000000000 --- a/base_layer/p2p/src/dht_service/dht_messages.rs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::tari_message::{NetMessage, TariMessageType}; -use serde::{Deserialize, Serialize}; -use std::convert::TryInto; -use tari_comms::{ - connection::NetAddress, - message::{Message, MessageError}, - peer_manager::NodeId, -}; - -/// The JoinMessage stores the information required for a network join request. It has all the information required to -/// locate and contact the source node, but network behaviour is different compared to DiscoverMessage. -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] -pub struct JoinMessage { - pub node_id: NodeId, - // TODO: node_type - pub net_address: Vec, -} - -impl TryInto for JoinMessage { - type Error = MessageError; - - fn try_into(self) -> Result { - Ok((TariMessageType::new(NetMessage::Join), self).try_into()?) - } -} - -/// The DiscoverMessage stores the information required for a network discover request. It has all the information -/// required to locate and contact the source node, but network behaviour is different compared to JoinMessage. -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] -pub struct DiscoverMessage { - pub node_id: NodeId, - // TODO: node_type - pub net_address: Vec, -} - -impl TryInto for DiscoverMessage { - type Error = MessageError; - - fn try_into(self) -> Result { - Ok((TariMessageType::new(NetMessage::Discover), self).try_into()?) - } -} diff --git a/base_layer/p2p/src/dht_service/dht_service.rs b/base_layer/p2p/src/dht_service/dht_service.rs deleted file mode 100644 index c8c619a0a5..0000000000 --- a/base_layer/p2p/src/dht_service/dht_service.rs +++ /dev/null @@ -1,455 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::{ - consts::DHT_BROADCAST_NODE_COUNT, - dht_service::{ - dht_messages::{DiscoverMessage, JoinMessage}, - DHTError, - }, - sync_services::{ - Service, - ServiceApiWrapper, - ServiceContext, - ServiceControlMessage, - ServiceError, - DEFAULT_API_TIMEOUT_MS, - }, - tari_message::{NetMessage, TariMessageType}, -}; -use crossbeam_channel as channel; -use log::*; -use std::{ - convert::TryInto, - sync::{Arc, Mutex}, - time::Duration, -}; -use tari_comms::{ - connection::NetAddress, - domain_connector::MessageInfo, - message::{Frame, Message, MessageEnvelope, MessageFlags, NodeDestination}, - outbound_message_service::{outbound_message_service::OutboundMessageService, BroadcastStrategy, ClosestRequest}, - peer_manager::{NodeId, NodeIdentity, Peer, PeerFlags, PeerManager}, - types::CommsPublicKey, - DomainConnector, -}; -use tari_utilities::message_format::MessageFormat; - -const LOG_TARGET: &str = "base_layer::p2p::dht"; - -/// The DHTService manages joining the network and discovery of peers. -pub struct DHTService { - node_identity: Option>, - prev_control_service_address: Option, - oms: Option>, - peer_manager: Option>, - api: ServiceApiWrapper, -} - -impl DHTService { - /// Create a new DHT service - pub fn new() -> Self { - Self { - node_identity: None, - prev_control_service_address: None, - oms: None, - peer_manager: None, - api: Self::setup_api(), - } - } - - /// Return this services API - pub fn get_api(&self) -> Arc { - self.api.get_api() - } - - fn setup_api() -> ServiceApiWrapper { - let (api_sender, service_receiver) = channel::bounded(0); - let (service_sender, api_receiver) = channel::bounded(0); - - let api = Arc::new(DHTServiceApi::new(api_sender, api_receiver)); - ServiceApiWrapper::new(service_receiver, service_sender, api) - } - - /// Construct a new join message that contains the current nodes identity and net_addresses - fn construct_join_msg(&self) -> Result { - let node_identity = self.node_identity.as_ref().ok_or(DHTError::NodeIdentityUndefined)?; - Ok(JoinMessage { - node_id: node_identity.identity.node_id.clone(), - net_address: vec![node_identity.control_service_address()?], - }) - } - - /// Construct a new discover message that contains the current nodes identity and net_addresses - fn construct_discover_msg(&self) -> Result { - let node_identity = self.node_identity.as_ref().ok_or(DHTError::NodeIdentityUndefined)?; - Ok(DiscoverMessage { - node_id: node_identity.identity.node_id.clone(), - net_address: vec![node_identity.control_service_address()?], - }) - } - - /// Send a new network join request to the peers that are closest to the current nodes network location. The Join - /// Request will allow other peers to be able to find this node on the network. - fn send_join(&self) -> Result<(), DHTError> { - let oms = self.oms.as_ref().ok_or(DHTError::OMSUndefined)?; - let node_identity = self.node_identity.as_ref().ok_or(DHTError::NodeIdentityUndefined)?; - - oms.send_message( - BroadcastStrategy::Closest(ClosestRequest { - n: DHT_BROADCAST_NODE_COUNT, - node_id: node_identity.identity.node_id.clone(), - excluded_peers: Vec::new(), - }), - MessageFlags::NONE, - self.construct_join_msg()?, - )?; - trace!(target: LOG_TARGET, "Join Request Sent"); - - Ok(()) - } - - /// Send a network join update request directly to a specific known peer - fn send_join_direct(&self, dest_public_key: CommsPublicKey) -> Result<(), DHTError> { - let oms = self.oms.as_ref().ok_or(DHTError::OMSUndefined)?; - - oms.send_message( - BroadcastStrategy::DirectPublicKey(dest_public_key), - MessageFlags::ENCRYPTED, - self.construct_join_msg()?, - )?; - trace!(target: LOG_TARGET, "Direct Join Request Sent"); - - Ok(()) - } - - /// Send a discover request to find a specific peer on the network - fn send_discover( - &self, - dest_public_key: CommsPublicKey, - dest_node_id: Option, - header_dest: NodeDestination, - ) -> Result<(), DHTError> - { - let oms = self.oms.as_ref().ok_or(DHTError::OMSUndefined)?; - let node_identity = self.node_identity.as_ref().ok_or(DHTError::NodeIdentityUndefined)?; - - let discover_msg: Message = self - .construct_discover_msg()? - .try_into() - .map_err(DHTError::MessageSerializationError)?; - let message_envelope_body: Frame = discover_msg.to_binary().map_err(DHTError::MessageFormatError)?; - let message_envelope = MessageEnvelope::construct( - &node_identity, - dest_public_key, - header_dest.clone(), - message_envelope_body.clone(), - MessageFlags::ENCRYPTED, - ) - .map_err(DHTError::MessageSerializationError)?; - - let broadcast_strategy = BroadcastStrategy::discover( - node_identity.identity.node_id.clone(), - dest_node_id, - header_dest, - Vec::new(), - ); - oms.forward_message(broadcast_strategy, message_envelope)?; - - Ok(()) - } - - /// Process an incoming join request. The peer specified in the join request will be added to the PeerManager. If - /// the current Node and the join request Node are from the same region of the network then the current node will - /// send a join request back to that peer informing it that the current node is a neighbouring node. The join - /// request is then forwarded to closer nodes. - fn receive_join(&mut self, connector: &DomainConnector<'static>) -> Result<(), DHTError> { - let oms = self.oms.as_ref().ok_or(DHTError::OMSUndefined)?; - let peer_manager = self.peer_manager.as_ref().ok_or(DHTError::PeerManagerUndefined)?; - let node_identity = self.node_identity.as_ref().ok_or(DHTError::NodeIdentityUndefined)?; - - let incoming_msg: Option<(MessageInfo, JoinMessage)> = connector - .receive_timeout(Duration::from_millis(1)) - .map_err(DHTError::ConnectorError)?; - if let Some((info, join_msg)) = incoming_msg { - // TODO: Check/Verify the received peers information - - // Add peer or modify existing peer using received join request - if peer_manager.exists(&info.origin_source)? { - peer_manager.update_peer( - &info.origin_source, - Some(join_msg.node_id.clone()), - Some(join_msg.net_address.clone()), - None, - )?; - } else { - let peer = Peer::new( - info.origin_source.clone(), - join_msg.node_id.clone(), - join_msg.net_address.clone().into(), - PeerFlags::default(), - ); - peer_manager.add_peer(peer)?; - } - - // Send a join request back to the source peer of the join request if that peer is from the same region - // of network. Also, only Send a join request back if this copy of the received join - // request was not sent directly from the original source peer but was forwarded. If it - // was not forwarded then that source peer already has the current peers info in its - // PeerManager. - if (info.origin_source != info.peer_source.public_key) && - (peer_manager.in_network_region( - &join_msg.node_id, - &node_identity.identity.node_id, - DHT_BROADCAST_NODE_COUNT, - )?) - { - self.send_join_direct(info.origin_source.clone())?; - } - - // Propagate message to closer peers - // oms.forward_message( - // BroadcastStrategy::Closest(ClosestRequest { - // n: DHT_BROADCAST_NODE_COUNT, - // node_id: join_msg.node_id.clone(), - // excluded_peers: vec![info.origin_source, info.peer_source.public_key], - // }), - // info.message_envelope, - // )?; - } - - Ok(()) - } - - /// Process an incoming discover request that was meant for the current node - fn receive_discover(&mut self, connector: &DomainConnector<'static>) -> Result<(), DHTError> { - let peer_manager = self.peer_manager.as_ref().ok_or(DHTError::PeerManagerUndefined)?; - - let incoming_msg: Option<(MessageInfo, DiscoverMessage)> = connector - .receive_timeout(Duration::from_millis(1)) - .map_err(DHTError::ConnectorError)?; - if let Some((info, discover_msg)) = incoming_msg { - // TODO: Check/Verify the received peers information - - // Add peer or modify existing peer using received discover request - if peer_manager.exists(&info.origin_source)? { - peer_manager.update_peer( - &info.origin_source, - Some(discover_msg.node_id.clone()), - Some(discover_msg.net_address.clone()), - None, - )?; - } else { - let peer = Peer::new( - info.origin_source.clone(), - discover_msg.node_id.clone(), - discover_msg.net_address.clone().into(), - PeerFlags::default(), - ); - peer_manager.add_peer(peer)?; - } - - // Send the origin the current nodes latest contact info - self.send_join_direct(info.origin_source)?; - } - - Ok(()) - } - - /// The auto_join function sends a join request on startup or on the detection of a control_service_address change - fn auto_join(&mut self) -> Result<(), DHTError> { - let node_identity = self.node_identity.as_ref().ok_or(DHTError::NodeIdentityUndefined)?; - - if match self.prev_control_service_address.as_ref() { - Some(control_service_address) => *control_service_address != node_identity.control_service_address()?, /* Identity change detected */ - None => true, // Startup detected - } { - self.prev_control_service_address = Some(node_identity.control_service_address()?); - self.send_join()?; - } - - Ok(()) - } - - /// This handler is called when the Service executor loops receives an API request - fn handle_api_message(&self, msg: DHTApiRequest) -> Result<(), ServiceError> { - trace!( - target: LOG_TARGET, - "[{}] Received API message: {:?}", - self.get_name(), - msg - ); - let resp = match msg { - DHTApiRequest::SendJoin => self.send_join().map(|_| DHTApiResponse::JoinSent), - DHTApiRequest::SendDiscover(dest_public_key, dest_node_id, header_dest) => self - .send_discover(dest_public_key, dest_node_id, header_dest) - .map(|_| DHTApiResponse::DiscoverSent), - }; - - trace!(target: LOG_TARGET, "[{}] Replying to API: {:?}", self.get_name(), resp); - self.api - .send_reply(resp) - .map_err(ServiceError::internal_service_error()) - } -} - -/// The Domain Service trait implementation for the DHTService -impl Service for DHTService { - fn get_name(&self) -> String { - "dht".to_string() - } - - fn get_message_types(&self) -> Vec { - vec![NetMessage::Join.into(), NetMessage::Discover.into()] - } - - fn execute(&mut self, context: ServiceContext) -> Result<(), ServiceError> { - let connector_join = context.create_connector(&NetMessage::Join.into()).map_err(|err| { - ServiceError::ServiceInitializationFailed(format!("Failed to create connector for service: {}", err)) - })?; - - let connector_discover = context.create_connector(&NetMessage::Discover.into()).map_err(|err| { - ServiceError::ServiceInitializationFailed(format!("Failed to create connector for service: {}", err)) - })?; - - self.oms = Some(context.outbound_message_service()); - self.peer_manager = Some(context.peer_manager()); - self.node_identity = Some(context.node_identity()); - debug!(target: LOG_TARGET, "Starting DHT Service executor"); - loop { - if let Some(msg) = context.get_control_message(Duration::from_millis(5)) { - match msg { - ServiceControlMessage::Shutdown => break, - } - } - - match self.receive_join(&connector_join) { - Ok(_) => {}, - Err(err) => { - error!(target: LOG_TARGET, "DHT service had error: {:?}", err); - }, - } - - match self.receive_discover(&connector_discover) { - Ok(_) => {}, - Err(err) => { - error!(target: LOG_TARGET, "DHT service had error: {:?}", err); - }, - } - - match self.auto_join() { - Ok(_) => {}, - Err(err) => { - error!(target: LOG_TARGET, "DHT service had an auto join error: {:?}", err); - }, - } - - if let Some(msg) = self - .api - .recv_timeout(Duration::from_millis(5)) - .map_err(ServiceError::internal_service_error())? - { - self.handle_api_message(msg)?; - } - } - - Ok(()) - } -} - -/// API Request enum -#[derive(Debug)] -pub enum DHTApiRequest { - /// Send a join request to neighbouring peers on the network - SendJoin, - /// Send a discovery request to find a selected peer - SendDiscover(CommsPublicKey, Option, NodeDestination), -} - -/// API Response enum -#[derive(Debug)] -pub enum DHTApiResponse { - JoinSent, - DiscoverSent, -} - -/// Result for all API requests -pub type DHTApiResult = Result; - -/// The DHT service public API that other services and application will use to interact with this service. -/// The requests and responses are transmitted via channels into the Service Executor thread where this service is -/// running -pub struct DHTServiceApi { - sender: channel::Sender, - receiver: channel::Receiver, - mutex: Mutex<()>, - timeout: Duration, -} - -impl DHTServiceApi { - fn new(sender: channel::Sender, receiver: channel::Receiver) -> Self { - Self { - sender, - receiver, - mutex: Mutex::new(()), - timeout: Duration::from_millis(DEFAULT_API_TIMEOUT_MS), - } - } - - pub fn send_join(&self) -> Result<(), DHTError> { - self.send_recv(DHTApiRequest::SendJoin).and_then(|resp| match resp { - DHTApiResponse::JoinSent => Ok(()), - _ => Err(DHTError::UnexpectedApiResponse), - }) - } - - pub fn send_discover( - &self, - dest_public_key: CommsPublicKey, - dest_node_id: Option, - header_dest: NodeDestination, - ) -> Result<(), DHTError> - { - self.send_recv(DHTApiRequest::SendDiscover(dest_public_key, dest_node_id, header_dest)) - .and_then(|resp| match resp { - DHTApiResponse::DiscoverSent => Ok(()), - _ => Err(DHTError::UnexpectedApiResponse), - }) - } - - fn send_recv(&self, msg: DHTApiRequest) -> DHTApiResult { - self.lock(|| -> DHTApiResult { - self.sender.send(msg).map_err(|_| DHTError::ApiSendFailed)?; - self.receiver - .recv_timeout(self.timeout) - .map_err(|_| DHTError::ApiReceiveFailed)? - }) - } - - fn lock(&self, func: F) -> T - where F: FnOnce() -> T { - let lock = acquire_lock!(self.mutex); - let res = func(); - drop(lock); - res - } -} diff --git a/base_layer/p2p/src/dht_service/error.rs b/base_layer/p2p/src/dht_service/error.rs deleted file mode 100644 index 53f67c4944..0000000000 --- a/base_layer/p2p/src/dht_service/error.rs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use derive_error::Error; -use tari_comms::{ - domain_connector::ConnectorError, - outbound_message_service::OutboundError, - peer_manager::PeerManagerError, -}; - -use tari_comms::{message::MessageError, peer_manager::node_identity::NodeIdentityError}; -use tari_utilities::message_format::MessageFormatError; - -#[derive(Debug, Error)] -pub enum DHTError { - OutboundError(OutboundError), - ConnectorError(ConnectorError), - /// OMS has not been initialized - OMSUndefined, - /// The current nodes identity is undefined - NodeIdentityUndefined, - /// PeerManager has not been initialized - PeerManagerUndefined, - PeerManagerError(PeerManagerError), - /// Failed to send from API - ApiSendFailed, - /// Failed to receive in API from service - ApiReceiveFailed, - /// Received an unexpected response type from the API - UnexpectedApiResponse, - MessageFormatError(MessageFormatError), - MessageSerializationError(MessageError), - NodeIdentityError(NodeIdentityError), -} diff --git a/base_layer/p2p/src/dht_service/mod.rs b/base_layer/p2p/src/dht_service/mod.rs deleted file mode 100644 index 313c62fd3e..0000000000 --- a/base_layer/p2p/src/dht_service/mod.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -mod dht_messages; -mod dht_service; -mod error; - -pub use self::{ - dht_messages::{DiscoverMessage, JoinMessage}, - dht_service::{DHTService, DHTServiceApi}, - error::DHTError, -}; diff --git a/base_layer/p2p/src/domain_message.rs b/base_layer/p2p/src/domain_message.rs new file mode 100644 index 0000000000..7aea2a50c8 --- /dev/null +++ b/base_layer/p2p/src/domain_message.rs @@ -0,0 +1,109 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::convert::{From, TryFrom}; +use tari_comms::{peer_manager::Peer, types::CommsPublicKey}; +use tari_comms_dht::envelope::DhtMessageHeader; + +/// Wrapper around a received message. Provides source peer and origin information +#[derive(Debug, Clone)] +pub struct DomainMessage { + /// The peer which sent this message + pub source_peer: Peer, + /// This DHT header of this message. If `DhtMessageHeader::origin_public_key` is different from the + /// `source_peer.public_key`, this message was forwarded. + pub dht_header: DhtMessageHeader, + /// The domain-level message + pub inner: T, +} + +impl DomainMessage { + pub fn inner(&self) -> &T { + &self.inner + } + + pub fn into_inner(self) -> T { + self.inner + } + + /// Consumes this object returning the public key of the original sender of this message and the message itself + pub fn into_origin_and_inner(self) -> (CommsPublicKey, T) { + let inner = self.inner; + let pk = self + .dht_header + .origin + .map(|o| o.public_key) + .unwrap_or(self.source_peer.public_key); + (pk, inner) + } + + /// Returns true of this message was forwarded from another peer, otherwise false + pub fn is_forwarded(&self) -> bool { + self.dht_header + .origin + .as_ref() + // If the source and origin are different, then the message was forwarded + .map(|o| o.public_key != self.source_peer.public_key) + // Otherwise, if no origin is specified, the message was sent directly from the peer + .unwrap_or(false) + } + + /// Returns the public key that sent this message. If no origin is specified, then the source peer + /// sent this message. + pub fn origin_public_key(&self) -> &CommsPublicKey { + self.dht_header + .origin + .as_ref() + .map(|o| &o.public_key) + .unwrap_or(&self.source_peer.public_key) + } + + /// Converts the wrapped value of a DomainMessage to another compatible type. + /// + /// Note: + /// The Rust compiler doesn't seem to be able to recognise that DomainMessage != DomainMessage, so a blanket + /// `From` implementation isn't possible at this time + pub fn convert(self) -> DomainMessage + where U: From { + let inner = U::from(self.inner); + DomainMessage { + source_peer: self.source_peer, + dht_header: self.dht_header, + inner, + } + } + + /// Converts the wrapped value of a DomainMessage to another compatible type. + /// + /// Note: + /// The Rust compiler doesn't seem to be able to recognise that DomainMessage != DomainMessage, so a blanket + /// `From` implementation isn't possible at this time + pub fn try_convert(self) -> Result, U::Error> + where U: TryFrom { + let inner = U::try_from(self.inner)?; + Ok(DomainMessage { + source_peer: self.source_peer, + dht_header: self.dht_header, + inner, + }) + } +} diff --git a/base_layer/p2p/src/initialization.rs b/base_layer/p2p/src/initialization.rs index d1796dc3d3..7845d26666 100644 --- a/base_layer/p2p/src/initialization.rs +++ b/base_layer/p2p/src/initialization.rs @@ -20,44 +20,208 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::tari_message::TariMessageType; +use crate::{ + comms_connector::{InboundDomainConnector, PeerMessage}, + transport::{TorConfig, TransportType}, +}; use derive_error::Error; -use std::{net::IpAddr, sync::Arc}; +use futures::{channel::mpsc, AsyncRead, AsyncWrite, Sink}; +use rand::{distributions::Alphanumeric, thread_rng, Rng}; +use std::{error::Error, iter, sync::Arc, time::Duration}; use tari_comms::{ - builder::{CommsBuilderError, CommsServices, CommsServicesError}, - connection::{net_address::ip::SocketAddress, NetAddress}, - connection_manager::PeerConnectionConfig, - control_service::ControlServiceConfig, - peer_manager::{node_identity::NodeIdentityError, NodeIdentity}, - types::{CommsPublicKey, CommsSecretKey}, + backoff::ConstantBackoff, + peer_manager::NodeIdentity, + pipeline, + tor, + transports::{MemoryTransport, SocksTransport, TcpTransport, Transport}, CommsBuilder, + CommsBuilderError, + CommsNode, }; +use tari_comms_dht::{Dht, DhtBuilder, DhtConfig}; use tari_storage::{lmdb_store::LMDBBuilder, LMDBWrapper}; +use tower::ServiceBuilder; #[derive(Debug, Error)] pub enum CommsInitializationError { - NodeIdentityError(NodeIdentityError), CommsBuilderError(CommsBuilderError), - CommsServicesError(CommsServicesError), + HiddenServiceBuilderError(tor::HiddenServiceBuilderError), } +/// Configuration for a comms node #[derive(Clone)] pub struct CommsConfig { - pub control_service: ControlServiceConfig, - pub socks_proxy_address: Option, - pub host: IpAddr, - pub public_key: CommsPublicKey, - pub secret_key: CommsSecretKey, - pub public_address: NetAddress, + /// Path to the LMDB data files. pub datastore_path: String, + /// Name to use for the peer database pub peer_database_name: String, + /// The maximum number of concurrent Inbound tasks allowed before back-pressure is applied to peers + pub max_concurrent_inbound_tasks: usize, + /// The size of the buffer (channel) which holds pending outbound message requests + pub outbound_buffer_size: usize, + /// Configuration for DHT + pub dht: DhtConfig, + /// The identity of this node on the network + pub node_identity: Arc, + /// The type of transport to use + pub transport_type: TransportType, +} + +/// Initialize Tari Comms configured for tests +pub async fn initialize_local_test_comms( + node_identity: Arc, + connector: InboundDomainConnector, + data_path: &str, + discovery_request_timeout: Duration, +) -> Result<(CommsNode, Dht), CommsInitializationError> +where + TSink: Sink> + Unpin + Clone + Send + Sync + 'static, + TSink::Error: Error + Send + Sync, +{ + let peer_database_name = { + let mut rng = thread_rng(); + iter::repeat(()) + .map(|_| rng.sample(Alphanumeric)) + .take(8) + .collect::() + }; + let datastore = LMDBBuilder::new() + .set_path(data_path) + .set_environment_size(10) + .set_max_number_of_databases(1) + .add_database(&peer_database_name, lmdb_zero::db::CREATE) + .build() + .unwrap(); + let peer_database = datastore.get_handle(&peer_database_name).unwrap(); + let peer_database = LMDBWrapper::new(Arc::new(peer_database)); + + //---------------------------------- Comms --------------------------------------------// + + let comms = CommsBuilder::new() + .with_transport(MemoryTransport) + .with_listener_address(node_identity.public_address()) + .with_node_identity(node_identity) + .with_peer_storage(peer_database) + .with_dial_backoff(ConstantBackoff::new(Duration::from_millis(500))) + .build()?; + + // Create outbound channel + let (outbound_tx, outbound_rx) = mpsc::channel(10); + + let dht = DhtBuilder::new( + comms.node_identity(), + comms.peer_manager(), + outbound_tx, + comms.shutdown_signal(), + ) + .local_test() + .with_discovery_timeout(discovery_request_timeout) + .finish(); + + let dht_outbound_layer = dht.outbound_middleware_layer(); + + let comms = comms + .with_messaging_pipeline( + pipeline::Builder::new() + .outbound_buffer_size(10) + .with_outbound_pipeline(outbound_rx, |sink| { + ServiceBuilder::new().layer(dht_outbound_layer).service(sink) + }) + .max_concurrent_inbound_tasks(10) + .with_inbound_pipeline( + ServiceBuilder::new() + .layer(dht.inbound_middleware_layer()) + .service(connector), + ) + .finish(), + ) + .spawn() + .await?; + + Ok((comms, dht)) +} + +/// Initialize Tari Comms +pub async fn initialize_comms( + config: CommsConfig, + connector: InboundDomainConnector, +) -> Result<(CommsNode, Dht), CommsInitializationError> +where + TSink: Sink> + Unpin + Clone + Send + Sync + 'static, + TSink::Error: Error + Send + Sync, +{ + let builder = CommsBuilder::new().with_node_identity(config.node_identity.clone()); + + match &config.transport_type { + TransportType::Memory { listener_address } => { + let comms = builder + .with_transport(MemoryTransport) + .with_listener_address(listener_address.clone()); + configure_comms_and_dht(comms, config, connector).await + }, + TransportType::Tcp { listener_address } => { + let comms = builder + .with_transport(TcpTransport::default()) + .with_listener_address(listener_address.clone()); + configure_comms_and_dht(comms, config, connector).await + }, + TransportType::Tor(tor_config) => { + let hidden_service = initialize_hidden_service(tor_config.clone()).await?; + let comms = builder.configure_from_hidden_service(hidden_service); + + let (comms, dht) = configure_comms_and_dht(comms, config, connector).await?; + // Set the public address to the onion address that comms is using + comms + .node_identity() + .set_public_address( + comms + .hidden_service() + .expect("hidden_service is must be set because a tor hidden service is set") + .get_onion_address() + .clone(), + ) + .expect("Poisoned NodeIdentity"); + Ok((comms, dht)) + }, + TransportType::Socks { + proxy_address, + listener_address, + authentication, + } => { + let comms = builder + .with_transport(SocksTransport::new(proxy_address.clone(), authentication.clone())) + .with_listener_address(listener_address.clone()); + configure_comms_and_dht(comms, config, connector).await + }, + } } -pub fn initialize_comms(config: CommsConfig) -> Result>, CommsInitializationError> { - let node_identity = NodeIdentity::new(config.secret_key, config.public_key, config.public_address) - .map_err(CommsInitializationError::NodeIdentityError)?; +async fn initialize_hidden_service(config: TorConfig) -> Result { + let mut builder = tor::HiddenServiceBuilder::new() + .with_hs_flags(tor::HsFlags::DETACH) + .with_port_mapping(config.port_mapping) + .with_socks_authentication(config.socks_auth) + .with_control_server_auth(config.control_server_auth) + .with_control_server_address(config.control_server_addr); + + if let Some(identity) = config.identity { + builder = builder.with_tor_identity(*identity); + } + + builder.finish().await +} - let _ = std::fs::create_dir(&config.datastore_path).unwrap_or_default(); +async fn configure_comms_and_dht( + builder: CommsBuilder, + config: CommsConfig, + connector: InboundDomainConnector, +) -> Result<(CommsNode, Dht), CommsInitializationError> +where + TTransport: Transport + Unpin + Send + Sync + Clone + 'static, + TTransport::Output: AsyncRead + AsyncWrite + Send + Sync + Unpin + 'static, + TSink: Sink> + Unpin + Clone + Send + Sync + 'static, + TSink::Error: Error + Send + Sync, +{ let datastore = LMDBBuilder::new() .set_path(&config.datastore_path) .set_environment_size(10) @@ -68,22 +232,39 @@ pub fn initialize_comms(config: CommsConfig) -> Result for PingPong { - type Error = MessageError; - - fn try_into(self) -> Result { - Ok((TariMessageType::new(NetMessage::PingPong), self).try_into()?) - } -} - -pub struct PingPongService { - // Needed because the public ping method needs OMS - oms: Option>, - ping_count: usize, - pong_count: usize, - api: ServiceApiWrapper, -} - -impl PingPongService { - /// Create a new ping pong service - pub fn new() -> Self { - Self { - oms: None, - ping_count: 0, - pong_count: 0, - api: Self::setup_api(), - } - } - - /// Return this services API - pub fn get_api(&self) -> Arc { - self.api.get_api() - } - - fn setup_api() -> ServiceApiWrapper { - let (api_sender, service_receiver) = channel::bounded(0); - let (service_sender, api_receiver) = channel::bounded(0); - - let api = Arc::new(PingPongServiceApi::new(api_sender, api_receiver)); - ServiceApiWrapper::new(service_receiver, service_sender, api) - } - - fn send_msg(&self, broadcast_strategy: BroadcastStrategy, msg: PingPong) -> Result<(), PingPongError> { - let oms = self.oms.as_ref().ok_or(PingPongError::OMSNotInitialized)?; - oms.send_message(broadcast_strategy, MessageFlags::empty(), msg) - .map_err(PingPongError::OutboundError) - } - - fn receive_ping(&mut self, info: MessageInfo, message: PingPong) -> Result<(), PingPongError> { - match message { - PingPong::Ping => { - debug!( - target: LOG_TARGET, - "Received ping from {}", - info.peer_source.public_key.to_hex(), - ); - - self.ping_count += 1; - - // Reply with Pong - self.send_msg(BroadcastStrategy::DirectPublicKey(info.origin_source), PingPong::Pong)?; - }, - PingPong::Pong => { - debug!( - target: LOG_TARGET, - "Received pong from {}", - info.peer_source.public_key.to_hex() - ); - - self.pong_count += 1; - }, - } - - Ok(()) - } - - fn ping(&self, pub_key: CommsPublicKey) -> Result<(), PingPongError> { - self.send_msg(BroadcastStrategy::DirectPublicKey(pub_key), PingPong::Ping) - } - - fn handle_api_message(&self, msg: PingPongApiRequest) -> Result<(), ServiceError> { - trace!(target: LOG_TARGET, "[{}] Received API message", self.get_name()); - let resp = match msg { - PingPongApiRequest::Ping(pk) => self.ping(pk).map(|_| PingPongApiResponse::PingSent), - PingPongApiRequest::GetPingCount => Ok(PingPongApiResponse::Count(self.ping_count)), - PingPongApiRequest::GetPongCount => Ok(PingPongApiResponse::Count(self.pong_count)), - }; - - trace!(target: LOG_TARGET, "[{}] Replying to API", self.get_name()); - self.api - .send_reply(resp) - .map_err(ServiceError::internal_service_error()) - } -} - -impl Service for PingPongService { - fn get_name(&self) -> String { - "ping-pong".to_string() - } - - fn get_message_types(&self) -> Vec { - vec![NetMessage::PingPong.into()] - } - - fn execute(&mut self, context: ServiceContext) -> Result<(), ServiceError> { - let mut subscription = SyncDomainSubscription::new( - context - .inbound_message_subscription_factory() - .get_subscription_fused(NetMessage::PingPong.into()), - ); - - self.oms = Some(context.outbound_message_service()); - - loop { - if let Some(msg) = context.get_control_message(Duration::from_millis(5)) { - match msg { - ServiceControlMessage::Shutdown => break, - } - } - - for m in subscription.receive_messages()?.drain(..) { - match self.receive_ping(m.0, m.1) { - Ok(_) => {}, - Err(err) => { - error!(target: LOG_TARGET, "PingPong service had error: {}", err); - }, - } - } - - if let Some(msg) = self - .api - .recv_timeout(Duration::from_millis(5)) - .map_err(ServiceError::internal_service_error())? - { - self.handle_api_message(msg)?; - } - } - - Ok(()) - } -} - -/// API Request enum -#[derive(Debug)] -pub enum PingPongApiRequest { - /// Send a ping to the given public key - Ping(CommsPublicKey), - /// Retrieve the total number of pings received - GetPingCount, - /// Retrieve the total number of pongs received - GetPongCount, -} - -/// API Response enum -#[derive(Debug)] -pub enum PingPongApiResponse { - PingSent, - Count(usize), -} - -impl fmt::Display for PingPongApiResponse { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - PingPongApiResponse::PingSent => write!(f, "PingSent"), - PingPongApiResponse::Count(n) => write!(f, "Count({})", n), - } - } -} - -/// Result for all API requests -pub type PingPongApiResult = Result; - -/// The PingPong service public api -pub struct PingPongServiceApi { - sender: channel::Sender, - receiver: channel::Receiver, - mutex: Mutex<()>, - timeout: Duration, -} - -impl PingPongServiceApi { - fn new(sender: channel::Sender, receiver: channel::Receiver) -> Self { - Self { - sender, - receiver, - mutex: Mutex::new(()), - timeout: Duration::from_millis(DEFAULT_API_TIMEOUT_MS), - } - } - - /// Send a ping message to the given peer - pub fn ping(&self, public_key: CommsPublicKey) -> Result<(), PingPongError> { - self.send_recv(PingPongApiRequest::Ping(public_key)) - .and_then(|resp| match resp { - PingPongApiResponse::PingSent => Ok(()), - _ => Err(PingPongError::UnexpectedApiResponse), - }) - } - - /// Fetch the ping count from the service - pub fn ping_count(&self) -> Result { - self.send_recv(PingPongApiRequest::GetPingCount) - .and_then(|resp| match resp { - PingPongApiResponse::Count(n) => Ok(n), - _ => Err(PingPongError::UnexpectedApiResponse), - }) - } - - /// Fetch the pong count from the service - pub fn pong_count(&self) -> Result { - self.send_recv(PingPongApiRequest::GetPongCount) - .and_then(|resp| match resp { - PingPongApiResponse::Count(n) => Ok(n), - _ => Err(PingPongError::UnexpectedApiResponse), - }) - } - - fn send_recv(&self, msg: PingPongApiRequest) -> PingPongApiResult { - self.lock(|| -> PingPongApiResult { - self.sender.send(msg).map_err(|_| PingPongError::ApiSendFailed)?; - self.receiver - .recv_timeout(self.timeout) - .map_err(|_| PingPongError::ApiReceiveFailed)? - }) - } - - fn lock(&self, func: F) -> T - where F: FnOnce() -> T { - let lock = acquire_lock!(self.mutex); - let res = func(); - drop(lock); - res - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn new() { - let service = PingPongService::new(); - assert_eq!(service.get_name(), "ping-pong"); - assert_eq!(service.get_message_types(), vec![NetMessage::PingPong.into()]); - assert_eq!(service.ping_count, 0); - assert_eq!(service.pong_count, 0); - } -} diff --git a/base_layer/p2p/src/proto/liveness.proto b/base_layer/p2p/src/proto/liveness.proto new file mode 100644 index 0000000000..f0f4c9bfe7 --- /dev/null +++ b/base_layer/p2p/src/proto/liveness.proto @@ -0,0 +1,30 @@ +syntax = "proto3"; + +package tari.p2p.liveness; + +enum PingPong { + PingPongPing = 0; + PingPongPong = 1; +} + + +// A ping or pong message +message PingPongMessage { + // Indicates if this message is a ping or pong + PingPong ping_pong = 1; + // The nonce of the ping. Pong messages MUST use the nonce from a corresponding ping + uint64 nonce = 2; + // Metadata attached to the message. The int32 key SHOULD always be one of the keys in `MetadataKey`. + map metadata = 3; +} + +// This enum represents all the possible metadata keys that can be used with a ping/pong message. +// MetadataKey may be extended as the need arises. +// +// _NOTE: Key values should NEVER be re-used_ +enum MetadataKey { + // The default key. This should never be used as it represents the absence of a key. + MetadataKeyNone = 0; + // The value for this key contains chain metadata + MetadataKeyChainMetadata = 1; +} diff --git a/base_layer/p2p/src/proto/message_type.proto b/base_layer/p2p/src/proto/message_type.proto new file mode 100644 index 0000000000..1692dead44 --- /dev/null +++ b/base_layer/p2p/src/proto/message_type.proto @@ -0,0 +1,31 @@ +syntax = "proto3"; + +package tari.p2p.message_type; + +// A tari message type is an immutable 32-bit signed integer indicating the type of message being received or sent +// over the network. +enum TariMessageType { + TariMessageTypeNone = 0; + + // -- NetMessages -- + + TariMessageTypePingPong = 1; + + // -- Blockchain messages -- + + TariMessageTypeNewTransaction = 65; + TariMessageTypeNewBlock = 66; + TariMessageTypeSenderPartialTransaction = 67; + TariMessageTypeReceiverPartialTransactionReply = 68; + TariMessageTypeBaseNodeRequest = 69; + TariMessageTypeBaseNodeResponse = 70; + TariMessageTypeMempoolRequest= 71; + TariMessageTypeMempoolResponse = 72; + TariMessageTypeTransactionFinalized = 73; + // -- DAN Messages -- + + // -- Extended -- + + TariMessageTypeText = 225; + TariMessageTypeTextAck = 226; +} diff --git a/base_layer/p2p/src/proto/mod.rs b/base_layer/p2p/src/proto/mod.rs new file mode 100644 index 0000000000..06ccdd0ebf --- /dev/null +++ b/base_layer/p2p/src/proto/mod.rs @@ -0,0 +1,27 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#[path = "tari.p2p.liveness.rs"] +pub(crate) mod liveness; + +#[path = "tari.p2p.message_type.rs"] +pub(crate) mod message_type; diff --git a/base_layer/p2p/src/proto/tari.p2p.liveness.rs b/base_layer/p2p/src/proto/tari.p2p.liveness.rs new file mode 100644 index 0000000000..c6e6290d57 --- /dev/null +++ b/base_layer/p2p/src/proto/tari.p2p.liveness.rs @@ -0,0 +1,31 @@ +/// A ping or pong message +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PingPongMessage { + /// Indicates if this message is a ping or pong + #[prost(enumeration = "PingPong", tag = "1")] + pub ping_pong: i32, + /// The nonce of the ping. Pong messages MUST use the nonce from a corresponding ping + #[prost(uint64, tag = "2")] + pub nonce: u64, + /// Metadata attached to the message. The int32 key SHOULD always be one of the keys in `MetadataKey`. + #[prost(map = "int32, bytes", tag = "3")] + pub metadata: ::std::collections::HashMap>, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum PingPong { + Ping = 0, + Pong = 1, +} +/// This enum represents all the possible metadata keys that can be used with a ping/pong message. +/// MetadataKey may be extended as the need arises. +/// +/// _NOTE: Key values should NEVER be re-used_ +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum MetadataKey { + /// The default key. This should never be used as it represents the absence of a key. + None = 0, + /// The value for this key contains chain metadata + ChainMetadata = 1, +} diff --git a/base_layer/p2p/src/proto/tari.p2p.message_type.rs b/base_layer/p2p/src/proto/tari.p2p.message_type.rs new file mode 100644 index 0000000000..c82ed79443 --- /dev/null +++ b/base_layer/p2p/src/proto/tari.p2p.message_type.rs @@ -0,0 +1,23 @@ +/// A tari message type is an immutable 32-bit signed integer indicating the type of message being received or sent +/// over the network. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum TariMessageType { + None = 0, + // -- NetMessages -- + PingPong = 1, + // -- Blockchain messages -- + NewTransaction = 65, + NewBlock = 66, + SenderPartialTransaction = 67, + ReceiverPartialTransactionReply = 68, + BaseNodeRequest = 69, + BaseNodeResponse = 70, + MempoolRequest = 71, + MempoolResponse = 72, + /// -- DAN Messages -- + TransactionFinalized = 73, + // -- Extended -- + Text = 225, + TextAck = 226, +} diff --git a/base_layer/p2p/src/saf_service/error.rs b/base_layer/p2p/src/saf_service/error.rs deleted file mode 100644 index 090283f782..0000000000 --- a/base_layer/p2p/src/saf_service/error.rs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use derive_error::Error; -use tari_comms::{ - connection::ConnectionError, - domain_connector::ConnectorError, - message::MessageError, - outbound_message_service::OutboundError, - peer_manager::PeerManagerError, -}; - -#[derive(Debug, Error)] -pub enum SAFError { - OutboundError(OutboundError), - ConnectorError(ConnectorError), - /// OMS has not been initialized - OMSUndefined, - /// The current nodes identity is undefined - NodeIdentityUndefined, - /// PeerManager has not been initialized - PeerManagerUndefined, - PeerManagerError(PeerManagerError), - /// ZMQContext has not been initialized - ZMQContextUndefined, - /// Message Sink Address for InboundMessageService has not been initialized - IMSMessageSinkAddressUndefined, - /// Failed to send from API - ApiSendFailed, - /// Failed to receive in API from service - ApiReceiveFailed, - /// Received an unexpected response type from the API - UnexpectedApiResponse, - MessageError(MessageError), - ConnectionError(ConnectionError), -} diff --git a/base_layer/p2p/src/saf_service/mod.rs b/base_layer/p2p/src/saf_service/mod.rs deleted file mode 100644 index 07d806b644..0000000000 --- a/base_layer/p2p/src/saf_service/mod.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -mod error; -mod saf_messages; -mod saf_service; - -pub use self::{ - error::SAFError, - saf_messages::{RetrieveMsgsMessage, StoredMsgsMessage}, - saf_service::{SAFService, SAFServiceApi}, -}; diff --git a/base_layer/p2p/src/saf_service/saf_messages.rs b/base_layer/p2p/src/saf_service/saf_messages.rs deleted file mode 100644 index b9b233bfea..0000000000 --- a/base_layer/p2p/src/saf_service/saf_messages.rs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::tari_message::{NetMessage, TariMessageType}; -use chrono::prelude::*; -use serde::{Deserialize, Serialize}; -use std::convert::TryInto; -use tari_comms::message::{Message, MessageEnvelope, MessageError}; - -/// The RetrieveMsgsMessage is used for requesting the set of stored messages from neighbouring peer nodes. If a -/// start_time is provided then only messages after the specified time will be sent, otherwise all applicable messages -/// will be sent. -#[derive(Serialize, Deserialize)] -pub struct RetrieveMsgsMessage { - pub start_time: Option>, -} - -impl TryInto for RetrieveMsgsMessage { - type Error = MessageError; - - fn try_into(self) -> Result { - Ok((TariMessageType::new(NetMessage::RetrieveMessages), self).try_into()?) - } -} - -/// The StoredMsgsMessage contains the set of applicable messages retrieved from a neighbouring peer node. -#[derive(Serialize, Deserialize)] -pub struct StoredMsgsMessage { - pub message_envelopes: Vec, -} - -impl TryInto for StoredMsgsMessage { - type Error = MessageError; - - fn try_into(self) -> Result { - Ok((TariMessageType::new(NetMessage::StoredMessages), self).try_into()?) - } -} diff --git a/base_layer/p2p/src/saf_service/saf_service.rs b/base_layer/p2p/src/saf_service/saf_service.rs deleted file mode 100644 index d981a5481d..0000000000 --- a/base_layer/p2p/src/saf_service/saf_service.rs +++ /dev/null @@ -1,437 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::{ - consts::{ - DHT_BROADCAST_NODE_COUNT, - SAF_HIGH_PRIORITY_MSG_STORAGE_TTL, - SAF_LOW_PRIORITY_MSG_STORAGE_TTL, - SAF_MSG_CACHE_STORAGE_CAPACITY, - }, - saf_service::{RetrieveMsgsMessage, SAFError, StoredMsgsMessage}, - sync_services::{ - Service, - ServiceApiWrapper, - ServiceContext, - ServiceControlMessage, - ServiceError, - DEFAULT_API_TIMEOUT_MS, - }, - tari_message::{NetMessage, TariMessageType}, -}; -use chrono::prelude::*; -use crossbeam_channel as channel; -use log::*; -use std::{ - sync::{Arc, Mutex}, - time::Duration, -}; -use tari_comms::{ - connection::{Connection, Direction, InprocAddress, SocketEstablishment, ZmqContext}, - domain_connector::MessageInfo, - message::{Frame, MessageData, MessageEnvelope, MessageEnvelopeHeader, MessageFlags, NodeDestination}, - outbound_message_service::{outbound_message_service::OutboundMessageService, BroadcastStrategy, ClosestRequest}, - peer_manager::{NodeIdentity, PeerManager}, - DomainConnector, -}; -use ttl_cache::TtlCache; - -const LOG_TARGET: &str = "base_layer::p2p::saf"; - -/// Storage for a single message envelope, including the date and time when the element was stored -pub struct StoredMessage { - store_time: DateTime, - message_envelope: MessageEnvelope, - message_envelope_header: MessageEnvelopeHeader, -} - -impl StoredMessage { - /// Create a new StorageMessage from a MessageEnvelope - pub fn from(message_envelope: MessageEnvelope, message_envelope_header: MessageEnvelopeHeader) -> Self { - Self { - store_time: Utc::now(), - message_envelope, - message_envelope_header, - } - } -} - -/// The Store-and-forward Service manages the storage of forwarded message and provides an api for neighbouring peers to -/// retrieve the stored messages. -pub struct SAFService { - node_identity: Option>, - oms: Option>, - peer_manager: Option>, - zmq_context: Option, - ims_message_sink_address: Option, - api: ServiceApiWrapper, - msg_storage: TtlCache, -} - -impl SAFService { - /// Create a new Store-and-forward service. - pub fn new() -> Self { - Self { - node_identity: None, - oms: None, - peer_manager: None, - zmq_context: None, - ims_message_sink_address: None, - api: Self::setup_api(), - msg_storage: TtlCache::new(SAF_MSG_CACHE_STORAGE_CAPACITY), - } - } - - /// Return this services API. - pub fn get_api(&self) -> Arc { - self.api.get_api() - } - - fn setup_api() -> ServiceApiWrapper { - let (api_sender, service_receiver) = channel::bounded(0); - let (service_sender, api_receiver) = channel::bounded(0); - - let api = Arc::new(SAFServiceApi::new(api_sender, api_receiver)); - ServiceApiWrapper::new(service_receiver, service_sender, api) - } - - /// Send a message retrieval request to all neighbouring peers that are in the same network region. - fn send_retrieval_request(&self, start_time: Option>) -> Result<(), SAFError> { - let oms = self.oms.as_ref().ok_or(SAFError::OMSUndefined)?; - let node_identity = self.node_identity.as_ref().ok_or(SAFError::NodeIdentityUndefined)?; - - oms.send_message( - BroadcastStrategy::Closest(ClosestRequest { - n: DHT_BROADCAST_NODE_COUNT, - node_id: node_identity.identity.node_id.clone(), - excluded_peers: Vec::new(), - }), - MessageFlags::ENCRYPTED, - RetrieveMsgsMessage { start_time }, - )?; - trace!(target: LOG_TARGET, "Message retrieval request sent"); - - Ok(()) - } - - /// Process an incoming message retrieval request. - fn receive_retrieval_request(&mut self, connector: &DomainConnector<'static>) -> Result<(), SAFError> { - let oms = self.oms.as_ref().ok_or(SAFError::OMSUndefined)?; - let peer_manager = self.peer_manager.as_ref().ok_or(SAFError::PeerManagerUndefined)?; - let node_identity = self.node_identity.as_ref().ok_or(SAFError::NodeIdentityUndefined)?; - - let incoming_msg: Option<(MessageInfo, RetrieveMsgsMessage)> = connector - .receive_timeout(Duration::from_millis(1)) - .map_err(SAFError::ConnectorError)?; - if let Some((info, retrieval_request_msg)) = incoming_msg { - if peer_manager.in_network_region( - &info.peer_source.node_id, - &node_identity.identity.node_id, - DHT_BROADCAST_NODE_COUNT, - )? { - // Compile a set of stored messages for the requesting peer - // TODO: compiling the bundle of messages is slow, especially when there are many stored messages, a - // better approach should be used - let mut stored_msgs_response = StoredMsgsMessage { - message_envelopes: Vec::new(), - }; - for (_, stored_message) in self.msg_storage.iter() { - if retrieval_request_msg - .start_time - .map(|start_time| start_time <= stored_message.store_time) - .unwrap_or(true) - { - match stored_message.message_envelope_header.dest.clone() { - NodeDestination::Unknown => { - stored_msgs_response - .message_envelopes - .push(stored_message.message_envelope.clone()); - }, - NodeDestination::PublicKey(dest_public_key) => { - if dest_public_key == info.peer_source.public_key { - stored_msgs_response - .message_envelopes - .push(stored_message.message_envelope.clone()); - } - }, - NodeDestination::NodeId(dest_node_id) => { - if dest_node_id == info.peer_source.node_id { - stored_msgs_response - .message_envelopes - .push(stored_message.message_envelope.clone()); - } - }, - }; - } - } - - oms.send_message( - BroadcastStrategy::DirectPublicKey(info.peer_source.public_key), - MessageFlags::ENCRYPTED, - stored_msgs_response, - )?; - trace!(target: LOG_TARGET, "Responded to received message retrieval request"); - } - } - - Ok(()) - } - - /// Process an incoming set of retrieved messages. - fn receive_stored_messages(&mut self, connector: &DomainConnector<'static>) -> Result<(), SAFError> { - let ims_message_sink_address = self - .ims_message_sink_address - .as_ref() - .ok_or(SAFError::IMSMessageSinkAddressUndefined)?; - let zmq_context = self.zmq_context.as_ref().ok_or(SAFError::ZMQContextUndefined)?; - - let incoming_msg: Option<(MessageInfo, StoredMsgsMessage)> = connector - .receive_timeout(Duration::from_millis(1)) - .map_err(SAFError::ConnectorError)?; - if let Some((info, stored_msgs)) = incoming_msg { - // Send each received MessageEnvelope to the InboundMessageService - let ims_connection = Connection::new(&zmq_context, Direction::Outbound) - .set_socket_establishment(SocketEstablishment::Connect) - .establish(&ims_message_sink_address)?; - for message_envelope in stored_msgs.message_envelopes { - let message_data = MessageData::new(info.peer_source.node_id.clone(), false, message_envelope); - let message_data_frame_set = message_data.into_frame_set(); - ims_connection.send(message_data_frame_set.clone())?; - } - trace!(target: LOG_TARGET, "Received stored messages from neighbouring peer"); - } - - Ok(()) - } - - /// Store messages of known neighbouring peers, this network region and messages with undefined destinations. - /// Undefined destinations have a lower priority TTL. - fn store_message(&mut self, message_envelope: MessageEnvelope) -> Result<(), SAFError> { - let peer_manager = self.peer_manager.as_ref().ok_or(SAFError::PeerManagerUndefined)?; - let node_identity = self.node_identity.as_ref().ok_or(SAFError::NodeIdentityUndefined)?; - - let message_envelope_header = message_envelope.deserialize_header()?; - match message_envelope_header.dest.clone() { - NodeDestination::Unknown => { - self.msg_storage.insert( - message_envelope.body_frame().clone(), - StoredMessage::from(message_envelope, message_envelope_header), - SAF_LOW_PRIORITY_MSG_STORAGE_TTL, - ); - }, - NodeDestination::PublicKey(dest_public_key) => { - if peer_manager.exists(&dest_public_key)? { - self.msg_storage.insert( - message_envelope.body_frame().clone(), - StoredMessage::from(message_envelope, message_envelope_header), - SAF_HIGH_PRIORITY_MSG_STORAGE_TTL, - ); - } - }, - NodeDestination::NodeId(dest_node_id) => { - if (peer_manager.exists_node_id(&dest_node_id)?) | - (peer_manager.in_network_region( - &dest_node_id, - &node_identity.identity.node_id, - DHT_BROADCAST_NODE_COUNT, - )?) - { - self.msg_storage.insert( - message_envelope.body_frame().clone(), - StoredMessage::from(message_envelope, message_envelope_header), - SAF_HIGH_PRIORITY_MSG_STORAGE_TTL, - ); - } - }, - }; - - Ok(()) - } - - /// This handler is called when the Service executor loops receives an API request - fn handle_api_message(&mut self, msg: SAFApiRequest) -> Result<(), ServiceError> { - trace!( - target: LOG_TARGET, - "[{}] Received API message: {:?}", - self.get_name(), - msg - ); - let resp = match msg { - SAFApiRequest::SendRetrievalRequest(start_time) => self - .send_retrieval_request(start_time) - .map(|_| SAFApiResponse::RetrievalRequestSent), - SAFApiRequest::StoreMessage(message_envelope) => self - .store_message(message_envelope) - .map(|_| SAFApiResponse::MessageStored), - }; - - trace!(target: LOG_TARGET, "[{}] Replying to API: {:?}", self.get_name(), resp); - self.api - .send_reply(resp) - .map_err(ServiceError::internal_service_error()) - } -} - -/// The Domain Service trait implementation for the Store-and-forward Service -impl Service for SAFService { - fn get_name(&self) -> String { - "store-and-forward".to_string() - } - - fn get_message_types(&self) -> Vec { - vec![NetMessage::RetrieveMessages.into(), NetMessage::StoredMessages.into()] - } - - fn execute(&mut self, context: ServiceContext) -> Result<(), ServiceError> { - let connector_retrieve_messages = - context - .create_connector(&NetMessage::RetrieveMessages.into()) - .map_err(|err| { - ServiceError::ServiceInitializationFailed(format!( - "Failed to create connector for service: {}", - err - )) - })?; - - let connector_stored_messages = - context - .create_connector(&NetMessage::StoredMessages.into()) - .map_err(|err| { - ServiceError::ServiceInitializationFailed(format!( - "Failed to create connector for service: {}", - err - )) - })?; - - self.node_identity = Some(context.node_identity()); - self.oms = Some(context.outbound_message_service()); - self.peer_manager = Some(context.peer_manager()); - self.zmq_context = Some(context.zmq_context().clone()); - self.ims_message_sink_address = Some(context.ims_message_sink_address().clone()); - - debug!(target: LOG_TARGET, "Starting Store-and-forward Service executor"); - loop { - if let Some(msg) = context.get_control_message(Duration::from_millis(5)) { - match msg { - ServiceControlMessage::Shutdown => break, - } - } - - match self.receive_retrieval_request(&connector_retrieve_messages) { - Ok(_) => {}, - Err(err) => { - error!(target: LOG_TARGET, "Store-and-forward service had error: {:?}", err); - }, - } - - match self.receive_stored_messages(&connector_stored_messages) { - Ok(_) => {}, - Err(err) => { - error!(target: LOG_TARGET, "Store-and-forward service had error: {:?}", err); - }, - } - - if let Some(msg) = self - .api - .recv_timeout(Duration::from_millis(5)) - .map_err(ServiceError::internal_service_error())? - { - self.handle_api_message(msg)?; - } - } - - Ok(()) - } -} - -/// API Request enum -#[derive(Debug)] -pub enum SAFApiRequest { - /// Send a request to retrieve stored messages from neighbouring peers - SendRetrievalRequest(Option>), - /// Store message of known neighbouring peers and forwarded messages - StoreMessage(MessageEnvelope), -} - -/// API Response enum -#[derive(Debug)] -pub enum SAFApiResponse { - RetrievalRequestSent, - MessageStored, -} - -/// Result for all API requests -pub type SAFApiResult = Result; - -/// The Store-and-forward service public API that other services and application will use to interact with this service. -/// The requests and responses are transmitted via channels into the Service Executor thread where this service is -/// running -pub struct SAFServiceApi { - sender: channel::Sender, - receiver: channel::Receiver, - mutex: Mutex<()>, - timeout: Duration, -} - -impl SAFServiceApi { - fn new(sender: channel::Sender, receiver: channel::Receiver) -> Self { - Self { - sender, - receiver, - mutex: Mutex::new(()), - timeout: Duration::from_millis(DEFAULT_API_TIMEOUT_MS), - } - } - - pub fn retrieve(&self, start_time: Option>) -> Result<(), SAFError> { - self.send_recv(SAFApiRequest::SendRetrievalRequest(start_time)) - .and_then(|resp| match resp { - SAFApiResponse::RetrievalRequestSent => Ok(()), - _ => Err(SAFError::UnexpectedApiResponse), - }) - } - - pub fn store(&self, message_envelope: MessageEnvelope) -> Result<(), SAFError> { - self.send_recv(SAFApiRequest::StoreMessage(message_envelope)) - .and_then(|resp| match resp { - SAFApiResponse::MessageStored => Ok(()), - _ => Err(SAFError::UnexpectedApiResponse), - }) - } - - fn send_recv(&self, msg: SAFApiRequest) -> SAFApiResult { - self.lock(|| -> SAFApiResult { - self.sender.send(msg).map_err(|_| SAFError::ApiSendFailed)?; - self.receiver - .recv_timeout(self.timeout) - .map_err(|_| SAFError::ApiReceiveFailed)? - }) - } - - fn lock(&self, func: F) -> T - where F: FnOnce() -> T { - let lock = acquire_lock!(self.mutex); - let res = func(); - drop(lock); - res - } -} diff --git a/base_layer/p2p/src/services/comms_outbound/mod.rs b/base_layer/p2p/src/services/comms_outbound.rs similarity index 59% rename from base_layer/p2p/src/services/comms_outbound/mod.rs rename to base_layer/p2p/src/services/comms_outbound.rs index bbc7bb78ad..0abcf41a1d 100644 --- a/base_layer/p2p/src/services/comms_outbound/mod.rs +++ b/base_layer/p2p/src/services/comms_outbound.rs @@ -20,37 +20,36 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -mod error; -mod handle; -mod messages; -mod service; +use futures::future::{self, Future}; +use tari_comms_dht::outbound::OutboundMessageRequester; +use tari_service_framework::{handles::ServiceHandlesFuture, ServiceInitializationError, ServiceInitializer}; +use tari_shutdown::ShutdownSignal; +use tokio::runtime; -use self::service::CommsOutboundService; -use crate::services::{ServiceHandlesFuture, ServiceName}; -use std::sync::Arc; -use tari_comms::outbound_message_service::outbound_message_service::OutboundMessageService; -use tari_service_framework::{transport, ServiceInitializationError, ServiceInitializer}; +/// Convenience type alias for external services that want to use this services handle +pub type CommsOutboundHandle = OutboundMessageRequester; -pub use self::{error::CommsOutboundServiceError, handle::CommsOutboundHandle, messages::CommsOutboundRequest}; - -/// Initializer for CommsOutbound service +/// This initializer simply adds a comms OutboundMessageRequester as a handle for use in services. pub struct CommsOutboundServiceInitializer { - oms: Arc, + oms: Option, } impl CommsOutboundServiceInitializer { - pub fn new(oms: Arc) -> Self { - Self { oms } + pub fn new(oms: OutboundMessageRequester) -> Self { + Self { oms: Some(oms) } } } -impl ServiceInitializer for CommsOutboundServiceInitializer { - fn initialize(self: Box, handles: ServiceHandlesFuture) -> Result<(), ServiceInitializationError> { - let service = CommsOutboundService::new(self.oms); - let (requester, responder) = transport::channel(service); +impl ServiceInitializer for CommsOutboundServiceInitializer { + type Future = impl Future>; + + fn initialize(&mut self, _: runtime::Handle, handles: ServiceHandlesFuture, _: ShutdownSignal) -> Self::Future { + handles.register( + self.oms + .take() + .expect("CommsOutboundServiceInitializer initialized without OutboundMessageRequester"), + ); - tokio::spawn(responder); - handles.insert(ServiceName::CommsOutbound, CommsOutboundHandle::new(requester)); - Ok(()) + future::ready(Ok(())) } } diff --git a/base_layer/p2p/src/services/comms_outbound/handle.rs b/base_layer/p2p/src/services/comms_outbound/handle.rs deleted file mode 100644 index f263f6cbf8..0000000000 --- a/base_layer/p2p/src/services/comms_outbound/handle.rs +++ /dev/null @@ -1,226 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::{ - services::comms_outbound::{ - error::CommsOutboundServiceError, - messages::{CommsOutboundRequest, CommsOutboundResponse}, - }, - tari_message::TariMessageType, -}; -use futures::{ - future::{self, Either, Future}, - Poll, -}; -use tari_comms::{ - message::{Frame, Message, MessageEnvelope, MessageFlags, MessageHeader}, - outbound_message_service::BroadcastStrategy, -}; -use tari_service_framework::transport::{AwaitResponseError, Requester}; -use tari_utilities::message_format::MessageFormat; -use tokio_threadpool::{blocking, BlockingError}; -use tower_service::Service; - -type CommsOutboundRequester = Requester>; - -/// Handle for the CommsOutboundService. -#[derive(Clone)] -pub struct CommsOutboundHandle { - requester: CommsOutboundRequester, -} - -impl CommsOutboundHandle { - /// Create a new CommsOutboundHandle, which makes requests using the - /// given Requester - pub fn new(requester: CommsOutboundRequester) -> Self { - Self { requester } - } - - /// Send a comms message - pub fn send_message( - &mut self, - broadcast_strategy: BroadcastStrategy, - flags: MessageFlags, - message_type: TariMessageType, - message: T, - ) -> impl Future, Error = AwaitResponseError> + 'static - where - T: MessageFormat + 'static, - { - let mut requester = self.requester.clone(); - Self::message_body_serializer(message_type, message) - .or_else(|err| future::ok(Err(CommsOutboundServiceError::BlockingError(err)))) - .and_then(move |res| match res { - Ok(body) => Either::A(requester.call(CommsOutboundRequest::SendMsg { - broadcast_strategy, - flags, - body: Box::new(body), - })), - Err(err) => Either::B(future::ok(Err(err))), - }) - } - - /// Forward a comms message - pub fn forward_message( - mut self, - broadcast_strategy: BroadcastStrategy, - envelope: MessageEnvelope, - ) -> impl Future, Error = AwaitResponseError> - { - self.requester.call(CommsOutboundRequest::Forward { - broadcast_strategy, - message_envelope: Box::new(envelope), - }) - } - - /// Return a message body serializer future - fn message_body_serializer(message_type: TariMessageType, message: T) -> MessageBodySerializer - where T: MessageFormat { - MessageBodySerializer::new(message_type, message) - } -} - -#[must_use = "futures do nothing unless polled"] -struct MessageBodySerializer { - message: Option, - message_type: Option, -} - -impl MessageBodySerializer { - fn new(message_type: TariMessageType, message: T) -> Self { - Self { - message: Some(message), - message_type: Some(message_type), - } - } -} - -impl Future for MessageBodySerializer -where T: MessageFormat -{ - type Error = BlockingError; - type Item = Result; - - fn poll(&mut self) -> Poll { - let message_type = self.message_type.take().expect("called poll twice"); - let message = self.message.take().expect("called poll twice"); - blocking(move || { - let header = - MessageHeader::new(message_type).map_err(CommsOutboundServiceError::MessageSerializationError)?; - let msg = Message::from_message_format(header, message) - .map_err(CommsOutboundServiceError::MessageSerializationError)?; - - msg.to_binary().map_err(CommsOutboundServiceError::MessageFormatError) - }) - } -} - -#[cfg(test)] -mod test { - use super::*; - use rand::rngs::OsRng; - use tari_comms::{ - message::{MessageEnvelopeHeader, NodeDestination}, - types::CommsPublicKey, - }; - use tari_crypto::keys::PublicKey; - use tari_service_framework::transport; - use tokio::runtime::Runtime; - use tower_util::service_fn; - - #[test] - fn message_body_serializer() { - // Require tokio threadpool for blocking call - let mut rt = Runtime::new().unwrap(); - - let message_type = TariMessageType::new(0); - let message = "FOO".to_string(); - let fut = CommsOutboundHandle::message_body_serializer(message_type.clone(), message); - - let body = rt.block_on(fut).unwrap().unwrap(); - - let msg = Message::from_binary(&body).unwrap(); - let header: MessageHeader = msg.deserialize_header().unwrap(); - let body_msg: String = msg.deserialize_message().unwrap(); - assert_eq!(header.message_type, message_type); - assert_eq!(body_msg, "FOO"); - } - - #[test] - fn send_message() { - let mut rt = Runtime::new().unwrap(); - - let (req, res) = transport::channel(service_fn(|req| { - match req { - CommsOutboundRequest::SendMsg { .. } => {}, - _ => panic!("Unexpected request"), - } - future::ok::<_, ()>(Ok(())) - })); - - rt.spawn(res); - - let mut handle = CommsOutboundHandle::new(req); - let fut = handle.send_message( - BroadcastStrategy::Flood, - MessageFlags::empty(), - TariMessageType::new(0), - "FOO".to_string(), - ); - - rt.block_on(fut).unwrap().unwrap(); - } - - #[test] - fn forward() { - let mut rt = Runtime::new().unwrap(); - - let (req, res) = transport::channel(service_fn(|req| { - match req { - CommsOutboundRequest::Forward { .. } => {}, - _ => panic!("Unexpected request"), - } - future::ok::<_, ()>(Ok(())) - })); - - rt.spawn(res); - - let handle = CommsOutboundHandle::new(req); - let mut rng = OsRng::new().unwrap(); - let header = MessageEnvelopeHeader { - version: 0, - origin_source: CommsPublicKey::random_keypair(&mut rng).1, - peer_source: CommsPublicKey::random_keypair(&mut rng).1, - dest: NodeDestination::Unknown, - origin_signature: vec![], - peer_signature: vec![], - flags: MessageFlags::empty(), - }; - - let fut = handle.forward_message( - BroadcastStrategy::Flood, - MessageEnvelope::new(vec![0], header.to_binary().unwrap(), vec![]), - ); - - rt.block_on(fut).unwrap().unwrap(); - } -} diff --git a/base_layer/p2p/src/services/comms_outbound/service.rs b/base_layer/p2p/src/services/comms_outbound/service.rs deleted file mode 100644 index 1600c5b864..0000000000 --- a/base_layer/p2p/src/services/comms_outbound/service.rs +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::services::comms_outbound::{ - error::CommsOutboundServiceError, - messages::{CommsOutboundRequest, CommsOutboundResponse}, -}; -use futures::{ - future::{self, Either}, - Future, - Poll, -}; -use std::sync::Arc; -use tari_comms::{ - message::{Frame, MessageEnvelope, MessageFlags}, - outbound_message_service::{outbound_message_service::OutboundMessageService, BroadcastStrategy}, -}; -use tower_service::Service; - -/// Service responsible for sending messages to the comms OMS -pub struct CommsOutboundService { - oms: Arc, -} - -impl CommsOutboundService { - pub fn new(oms: Arc) -> Self { - Self { oms } - } - - fn send_msg( - &self, - broadcast_strategy: BroadcastStrategy, - flags: MessageFlags, - body: Frame, - ) -> impl Future, Error = CommsOutboundServiceError> - { - // TODO(sdbondi): Change required when oms is async - future::ok( - self.oms - .send_raw(broadcast_strategy, flags, body) - .map_err(CommsOutboundServiceError::OutboundError), - ) - } - - fn forward_message( - &self, - broadcast_strategy: BroadcastStrategy, - envelope: MessageEnvelope, - ) -> impl Future, Error = CommsOutboundServiceError> - { - // TODO(sdbondi): Change required when oms is async - future::ok( - self.oms - .forward_message(broadcast_strategy, envelope) - .map_err(CommsOutboundServiceError::OutboundError), - ) - } -} - -impl Service for CommsOutboundService { - type Error = CommsOutboundServiceError; - type Future = impl Future; - type Response = Result; - - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(().into()) - } - - fn call(&mut self, req: CommsOutboundRequest) -> Self::Future { - match req { - // Send a ping synchronously for now until comms is async - CommsOutboundRequest::SendMsg { - broadcast_strategy, - flags, - body, - } => Either::A(self.send_msg(broadcast_strategy, flags, *body)), - CommsOutboundRequest::Forward { - broadcast_strategy, - message_envelope, - } => Either::B(self.forward_message(broadcast_strategy, *message_envelope)), - } - } -} - -#[cfg(test)] -mod test { - use super::*; - use crossbeam_channel as channel; - use futures::Async; - use rand::{distributions::Alphanumeric, rngs::OsRng, Rng}; - use std::iter; - use tari_comms::{ - connection::NetAddress, - message::{MessageEnvelopeHeader, NodeDestination}, - outbound_message_service::OutboundMessage, - peer_manager::{NodeId, NodeIdentity, Peer, PeerFlags, PeerManager}, - types::CommsPublicKey, - }; - use tari_crypto::keys::PublicKey; - use tari_storage::{lmdb_store::LMDBBuilder, LMDBWrapper}; - use tari_utilities::message_format::MessageFormat; - use tempdir::TempDir; - - pub fn random_string(len: usize) -> String { - let mut rng = OsRng::new().unwrap(); - iter::repeat(()).map(|_| rng.sample(Alphanumeric)).take(len).collect() - } - - // TODO: Thankfully, this won't be needed in 'Future' :P - Remove this once the OMS is a Sink. - fn setup_oms() -> (Arc, channel::Receiver) { - let tmpdir = TempDir::new(random_string(8).as_str()).unwrap(); - let mut rng = OsRng::new().unwrap(); - let node_identity = NodeIdentity::random(&mut rng, "127.0.0.1:9000".parse().unwrap()) - .map(Arc::new) - .unwrap(); - - let (_, pk) = CommsPublicKey::random_keypair(&mut rng); - let node_id = NodeId::from_key(&pk).unwrap(); - let net_addresses = "127.0.0.1:55445".parse::().unwrap().into(); - let dest_peer = Peer::new(pk, node_id, net_addresses, PeerFlags::default()); - let database_name = random_string(8); - let datastore = LMDBBuilder::new() - .set_path(tmpdir.path().to_str().unwrap()) - .set_environment_size(10) - .set_max_number_of_databases(1) - .add_database(&database_name, lmdb_zero::db::CREATE) - .build() - .unwrap(); - - let peer_database = datastore.get_handle(&database_name).unwrap(); - let peer_database = LMDBWrapper::new(Arc::new(peer_database)); - - // Add a peer so that something will be sent - let peer_manager = PeerManager::new(peer_database).map(Arc::new).unwrap(); - peer_manager.add_peer(dest_peer.clone()).unwrap(); - - let (message_sender, message_receiver) = channel::unbounded(); - ( - OutboundMessageService::new(node_identity.clone(), message_sender, peer_manager) - .map(Arc::new) - .unwrap(), - message_receiver, - ) - } - - #[test] - fn poll_ready() { - let (oms, _) = setup_oms(); - let mut service = CommsOutboundService::new(oms); - - // Always ready - assert!(service.poll_ready().unwrap().is_ready()); - } - - #[test] - fn call_send_message() { - let (oms, oms_rx) = setup_oms(); - let mut service = CommsOutboundService::new(oms); - - let mut fut = service.call(CommsOutboundRequest::SendMsg { - broadcast_strategy: BroadcastStrategy::Flood, - flags: MessageFlags::empty(), - body: Box::new(Vec::new()), - }); - - match fut.poll().unwrap() { - Async::Ready(Ok(_)) => {}, - Async::Ready(Err(err)) => panic!("unexpected failed result for send_message: {:?}", err), - _ => panic!("future is not ready"), - } - - // We only care that OMS got called (i.e the Receiver received something) - assert!(!oms_rx.is_empty()); - } - - #[test] - fn call_forward() { - let (oms, oms_rx) = setup_oms(); - let mut service = CommsOutboundService::new(oms); - let mut rng = OsRng::new().unwrap(); - let header = MessageEnvelopeHeader { - version: 0, - origin_source: CommsPublicKey::random_keypair(&mut rng).1, - peer_source: CommsPublicKey::random_keypair(&mut rng).1, - dest: NodeDestination::Unknown, - origin_signature: vec![], - peer_signature: vec![], - flags: MessageFlags::empty(), - }; - - let mut fut = service.call(CommsOutboundRequest::Forward { - broadcast_strategy: BroadcastStrategy::Flood, - message_envelope: Box::new(MessageEnvelope::new(vec![0], header.to_binary().unwrap(), vec![])), - }); - - match fut.poll().unwrap() { - Async::Ready(Ok(_)) => {}, - Async::Ready(Err(err)) => panic!("unexpected failed result for forward: {:?}", err), - _ => panic!("future is not ready"), - } - - // We only care that OMS got called (i.e the Receiver received something) - assert!(!oms_rx.is_empty()); - } -} diff --git a/base_layer/p2p/src/services/domain_deserializer.rs b/base_layer/p2p/src/services/domain_deserializer.rs deleted file mode 100644 index 11f9046445..0000000000 --- a/base_layer/p2p/src/services/domain_deserializer.rs +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use futures::{Future, Poll}; -use serde::export::PhantomData; -use tari_comms::{ - domain_subscriber::MessageInfo, - message::{InboundMessage, MessageError}, -}; -use tari_utilities::message_format::MessageFormat; -use tokio_threadpool::{blocking, BlockingError}; - -/// Future which asynchonously attempts to deserialize InboundMessage into -/// a `(MessageInfo, T)` tuple where T is [MessageFormat]. -pub struct DomainMessageDeserializer { - message: Option, - _t: PhantomData, -} - -impl DomainMessageDeserializer { - /// Create a new DomainMessageDeserializer from the given InboundMessage - pub fn new(message: InboundMessage) -> Self { - Self { - message: Some(message), - _t: PhantomData, - } - } -} - -impl Future for DomainMessageDeserializer { - type Error = BlockingError; - type Item = Result<(MessageInfo, T), MessageError>; - - fn poll(&mut self) -> Poll { - let msg = self.message.take().expect("poll called twice on Deserializer"); - blocking(|| { - let deserialized: T = msg.message.deserialize_message()?; - let info = MessageInfo { - peer_source: msg.peer_source, - origin_source: msg.origin_source, - }; - Ok((info, deserialized)) - }) - } -} - -#[cfg(test)] -mod test { - use super::*; - use rand::rngs::OsRng; - use tari_comms::{ - message::{Message, MessageHeader}, - peer_manager::{NodeId, PeerNodeIdentity}, - types::CommsPublicKey, - }; - use tari_crypto::keys::PublicKey; - use tokio::runtime::Runtime; - - fn create_domain_message(message_type: u8, inner_msg: T) -> InboundMessage { - let mut rng = OsRng::new().unwrap(); - let (_, pk) = CommsPublicKey::random_keypair(&mut rng); - let peer_source = PeerNodeIdentity::new(NodeId::from_key(&pk).unwrap(), pk.clone()); - let header = MessageHeader::new(message_type).unwrap(); - let msg = Message::from_message_format(header, inner_msg).unwrap(); - InboundMessage::new(peer_source, pk, msg) - } - - #[test] - fn deserialize_success() { - let mut rt = Runtime::new().unwrap(); - let domain_msg = create_domain_message(1, "wubalubadubdub".to_string()); - let fut = DomainMessageDeserializer::::new(domain_msg.clone()); - - let (info, msg) = rt.block_on(fut).unwrap().unwrap(); - assert_eq!(msg, "wubalubadubdub"); - assert_eq!(info.peer_source, domain_msg.peer_source); - assert_eq!(info.origin_source, domain_msg.origin_source); - } - - #[test] - fn deserialize_fail() { - let mut rt = Runtime::new().unwrap(); - let domain_msg = create_domain_message(1, "wubalubadubdub".to_string()); - let fut = DomainMessageDeserializer::::new(domain_msg.clone()); - - match rt.block_on(fut).unwrap() { - Ok(_) => panic!("unexpected success when deserializing to mismatched type"), - Err(MessageError::MessageFormatError(_)) => {}, - Err(err) => panic!("unexpected error when deserializing mismatched types: {:?}", err), - } - } -} diff --git a/base_layer/p2p/src/services/liveness/config.rs b/base_layer/p2p/src/services/liveness/config.rs new file mode 100644 index 0000000000..e5bdd611b1 --- /dev/null +++ b/base_layer/p2p/src/services/liveness/config.rs @@ -0,0 +1,47 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::time::Duration; + +/// Configuration for liveness service +#[derive(Debug, Clone, Copy)] +pub struct LivenessConfig { + /// The interval to send Ping messages, or None to disable periodic pinging (default: None (disabled)) + pub auto_ping_interval: Option, + /// Set to true to enable automatically joining the network on node startup (default: false) + pub enable_auto_join: bool, + /// Set to true to enable a request for stored messages on node startup (default: false) + pub enable_auto_stored_message_request: bool, + /// The length of time between querying peer manager for closest neighbours. (default: 5mins) + pub refresh_neighbours_interval: Duration, +} + +impl Default for LivenessConfig { + fn default() -> Self { + Self { + auto_ping_interval: None, + enable_auto_join: false, + enable_auto_stored_message_request: false, + refresh_neighbours_interval: Duration::from_secs(3 * 60), + } + } +} diff --git a/base_layer/p2p/src/services/liveness/error.rs b/base_layer/p2p/src/services/liveness/error.rs index 79f491f2f3..c4a8baa551 100644 --- a/base_layer/p2p/src/services/liveness/error.rs +++ b/base_layer/p2p/src/services/liveness/error.rs @@ -20,17 +20,28 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::services::comms_outbound::CommsOutboundServiceError; use derive_error::Error; use tari_comms::message::MessageError; +use tari_comms_dht::{outbound::DhtOutboundError, DhtActorError}; +use tari_service_framework::reply_channel::TransportChannelError; #[derive(Debug, Error)] pub enum LivenessError { - CommsOutboundError(CommsOutboundServiceError), + DhtOutboundError(DhtOutboundError), + DhtActorError(DhtActorError), /// Failed to send a pong message SendPongFailed, /// Failed to send a ping message SendPingFailed, - // Occurs when a message cannot deserialize into a PingPong message + /// Occurs when a message cannot deserialize into a PingPong message MessageError(MessageError), + /// The Handle repsonse was not what was expected for this request + UnexpectedApiResponse, + /// An error has occurred reading from the event subscriber stream + EventStreamError, + TransportChannelError(TransportChannelError), + /// Ping pong type was invalid or unrecognised + InvalidPingPongType, + /// NodeId does not exist + NodeIdDoesNotExist, } diff --git a/base_layer/p2p/src/services/liveness/handle.rs b/base_layer/p2p/src/services/liveness/handle.rs new file mode 100644 index 0000000000..6709028a18 --- /dev/null +++ b/base_layer/p2p/src/services/liveness/handle.rs @@ -0,0 +1,179 @@ +// Copyright 2019 The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::{error::LivenessError, state::Metadata}; +use crate::{proto::liveness::MetadataKey, services::liveness::state::NodeStats}; +use futures::{stream::Fuse, StreamExt}; +use tari_broadcast_channel::Subscriber; +use tari_comms::peer_manager::NodeId; +use tari_service_framework::reply_channel::SenderService; +use tower::Service; + +/// Request types made through the `LivenessHandle` and are handled by the `LivenessService` +#[derive(Debug, Clone)] +pub enum LivenessRequest { + /// Send a ping to the given node ID + SendPing(NodeId), + /// Retrieve the total number of pings received + GetPingCount, + /// Retrieve the total number of pongs received + GetPongCount, + /// Get average latency for node ID + GetAvgLatency(NodeId), + /// Set the metadata attached to each pong message + SetPongMetadata(MetadataKey, Vec), + /// Request the number of active neighbours + GetNumActiveNeighbours, + /// Add NodeId to be monitored + AddNodeId(NodeId), + /// Get stats for a monitored NodeId + GetNodeIdStats(NodeId), +} + +/// Response type for `LivenessService` +#[derive(Debug)] +pub enum LivenessResponse { + /// Indicates that the request succeeded + Ok, + /// Used to return a counter value from `GetPingCount` and `GetPongCount` + Count(usize), + /// Response for GetAvgLatency + AvgLatency(Option), + /// The number of active neighbouring peers + NumActiveNeighbours(usize), + NodeIdAdded, + NodeIdStats(NodeStats), +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum LivenessEvent { + /// A ping was received + ReceivedPing, + /// A pong was received. The latency to the peer (if available) and the metadata contained + /// within the received pong message are included as part of the event + ReceivedPong(Box), + BroadcastedNeighbourPings(usize), + BroadcastedMonitoredNodeIdPings(usize), +} + +/// Repressents a pong event +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PongEvent { + /// The node id of the node which sent this pong + pub node_id: NodeId, + /// Latency if available (i.e. a corresponding ping was sent within the Liveness state inflight ping TTL) + pub latency: Option, + /// Pong metadata + pub metadata: Metadata, + /// True if the pong was from a neighbouring peer, otherwise false + pub is_neighbour: bool, + /// True if the pong was from a monitored node, otherwise false + pub is_monitored: bool, +} + +impl PongEvent { + pub(super) fn new( + node_id: NodeId, + latency: Option, + metadata: Metadata, + is_neighbour: bool, + is_monitored: bool, + ) -> Self + { + Self { + node_id, + latency, + metadata, + is_neighbour, + is_monitored, + } + } +} + +#[derive(Clone)] +pub struct LivenessHandle { + handle: SenderService>, + event_stream: Subscriber, +} + +impl LivenessHandle { + pub fn new( + handle: SenderService>, + event_stream: Subscriber, + ) -> Self + { + Self { handle, event_stream } + } + + /// Returns a fused event stream for the liveness service + pub fn get_event_stream_fused(&self) -> Fuse> { + self.event_stream.clone().fuse() + } + + /// Send a ping to a given node ID + pub async fn send_ping(&mut self, node_id: NodeId) -> Result<(), LivenessError> { + match self.handle.call(LivenessRequest::SendPing(node_id)).await?? { + LivenessResponse::Ok => Ok(()), + _ => Err(LivenessError::UnexpectedApiResponse), + } + } + + /// Retrieve the global ping count + pub async fn get_ping_count(&mut self) -> Result { + match self.handle.call(LivenessRequest::GetPingCount).await?? { + LivenessResponse::Count(c) => Ok(c), + _ => Err(LivenessError::UnexpectedApiResponse), + } + } + + /// Retrieve the global pong count + pub async fn get_pong_count(&mut self) -> Result { + match self.handle.call(LivenessRequest::GetPongCount).await?? { + LivenessResponse::Count(c) => Ok(c), + _ => Err(LivenessError::UnexpectedApiResponse), + } + } + + /// Set metadata entry for the pong message + pub async fn set_pong_metadata_entry(&mut self, key: MetadataKey, value: Vec) -> Result<(), LivenessError> { + match self.handle.call(LivenessRequest::SetPongMetadata(key, value)).await?? { + LivenessResponse::Ok => Ok(()), + _ => Err(LivenessError::UnexpectedApiResponse), + } + } + + /// Add NodeId to be monitored + pub async fn add_node_id(&mut self, node_id: NodeId) -> Result<(), LivenessError> { + match self.handle.call(LivenessRequest::AddNodeId(node_id)).await?? { + LivenessResponse::NodeIdAdded => Ok(()), + _ => Err(LivenessError::UnexpectedApiResponse), + } + } + + /// Get stats for NodeId that is being monitored + pub async fn get_node_id_stats(&mut self, node_id: NodeId) -> Result { + match self.handle.call(LivenessRequest::GetNodeIdStats(node_id)).await?? { + LivenessResponse::NodeIdStats(n) => Ok(n), + _ => Err(LivenessError::UnexpectedApiResponse), + } + } +} diff --git a/base_layer/p2p/src/services/liveness/handler.rs b/base_layer/p2p/src/services/liveness/handler.rs deleted file mode 100644 index 77967dedec..0000000000 --- a/base_layer/p2p/src/services/liveness/handler.rs +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use super::messages::PingPong; -use crate::{ - services::{ - comms_outbound::CommsOutboundHandle, - liveness::{error::LivenessError, state::LivenessState}, - }, - tari_message::{NetMessage, TariMessageType}, -}; -use futures::{ - future::{self, Either}, - Future, -}; -use std::sync::Arc; -use tari_comms::{ - domain_subscriber::MessageInfo, - message::MessageFlags, - outbound_message_service::BroadcastStrategy, - types::CommsPublicKey, -}; - -pub struct LivenessHandler { - state: Arc, - outbound_handle: CommsOutboundHandle, -} - -impl LivenessHandler { - pub fn new(state: Arc, outbound_handle: CommsOutboundHandle) -> Self { - Self { state, outbound_handle } - } - - pub fn handle_message( - &mut self, - info: MessageInfo, - msg: PingPong, - ) -> impl Future - { - match msg { - PingPong::Ping => { - let state = self.state.clone(); - state.inc_pings_received(); - Either::A(self.send_pong(info.origin_source).and_then(move |_| { - state.inc_pongs_sent(); - future::ok(()) - })) - }, - PingPong::Pong => { - self.state.inc_pongs_received(); - Either::B(future::ok(())) - }, - } - } - - fn send_pong(&mut self, dest: CommsPublicKey) -> impl Future { - self.outbound_handle - .send_message( - BroadcastStrategy::DirectPublicKey(dest), - MessageFlags::empty(), - TariMessageType::new(NetMessage::PingPong), - PingPong::Pong, - ) - .or_else(|_| future::err(LivenessError::SendPongFailed)) - .and_then(|res| match res { - Ok(_) => future::ok(()), - Err(err) => future::err(LivenessError::CommsOutboundError(err)), - }) - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::services::comms_outbound::CommsOutboundRequest; - use rand::rngs::OsRng; - use std::sync::mpsc; - use tari_comms::peer_manager::{NodeId, PeerNodeIdentity}; - use tari_crypto::keys::PublicKey; - use tari_service_framework::transport; - use tokio::runtime::Runtime; - use tower_util::service_fn; - - fn create_dummy_message_info() -> MessageInfo { - let mut rng = OsRng::new().unwrap(); - let (_, pk) = CommsPublicKey::random_keypair(&mut rng); - let peer_source = PeerNodeIdentity::new(NodeId::from_key(&pk).unwrap(), pk.clone()); - MessageInfo { - origin_source: peer_source.public_key.clone(), - peer_source, - } - } - - #[test] - fn handle_message_ping() { - let mut rt = Runtime::new().unwrap(); - let state = Arc::new(LivenessState::new()); - let (tx, rx) = mpsc::channel(); - - let (req, res) = transport::channel(service_fn(move |req| { - // Send this out so that we can assert some things about it - tx.send(req).unwrap(); - future::ok::<_, ()>(Ok(())) - })); - - rt.spawn(res); - - let outbound_handle = CommsOutboundHandle::new(req); - - let mut handler = LivenessHandler::new(state, outbound_handle); - - let info = create_dummy_message_info(); - let fut = handler.handle_message(info.clone(), PingPong::Ping); - - let result = rt.block_on(fut); - result.unwrap(); - - assert_eq!(handler.state.pings_received(), 1); - assert_eq!(handler.state.pongs_sent(), 1); - - match rx.try_recv().unwrap() { - CommsOutboundRequest::SendMsg { broadcast_strategy, .. } => match broadcast_strategy { - BroadcastStrategy::DirectPublicKey(pk) => assert_eq!(pk, info.origin_source), - _ => panic!("unexpected broadcast strategy used"), - }, - _ => panic!("liveness service sent unexpected message to outbound handle"), - } - } - - #[test] - fn handle_message_pong() { - let mut rt = Runtime::new().unwrap(); - let state = Arc::new(LivenessState::new()); - - let (req, res) = transport::channel(service_fn(|_| future::ok::<_, ()>(Ok(())))); - - rt.spawn(res); - - let outbound_handle = CommsOutboundHandle::new(req); - - let mut handler = LivenessHandler::new(state, outbound_handle); - - let info = create_dummy_message_info(); - let fut = handler.handle_message(info, PingPong::Pong); - - let result = rt.block_on(fut); - result.unwrap(); - - assert_eq!(handler.state.pongs_received(), 1); - } -} diff --git a/base_layer/p2p/src/services/liveness/message.rs b/base_layer/p2p/src/services/liveness/message.rs new file mode 100644 index 0000000000..26c868e120 --- /dev/null +++ b/base_layer/p2p/src/services/liveness/message.rs @@ -0,0 +1,52 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::services::liveness::state::Metadata; + +pub use crate::proto::liveness::{PingPong, PingPongMessage}; +use rand::{rngs::OsRng, RngCore}; + +impl PingPongMessage { + pub fn new(ping_pong: PingPong, nonce: u64, metadata: Metadata) -> Self { + PingPongMessage { + ping_pong: ping_pong as i32, + nonce, + metadata: metadata.into(), + } + } + + /// Construct a ping message + pub fn ping() -> Self { + let nonce = OsRng.next_u64(); + Self::new(PingPong::Ping, nonce, Default::default()) + } + + /// Construct a pong message with metadata + pub fn pong_with_metadata(nonce: u64, metadata: Metadata) -> Self { + Self::new(PingPong::Pong, nonce, metadata) + } + + /// Return the kind of PingPong message. Either a ping or pong. + pub fn kind(&self) -> Option { + PingPong::from_i32(self.ping_pong) + } +} diff --git a/base_layer/p2p/src/services/liveness/mock.rs b/base_layer/p2p/src/services/liveness/mock.rs new file mode 100644 index 0000000000..292617c618 --- /dev/null +++ b/base_layer/p2p/src/services/liveness/mock.rs @@ -0,0 +1,147 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::services::liveness::{ + error::LivenessError, + state::NodeStats, + LivenessEvent, + LivenessHandle, + LivenessRequest, + LivenessResponse, +}; +use futures::{SinkExt, StreamExt}; +use log::*; +use std::sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, + RwLock, +}; +use tari_broadcast_channel as broadcast_channel; +use tari_broadcast_channel::{Publisher, SendError}; +use tari_crypto::tari_utilities::acquire_write_lock; +use tari_service_framework::{reply_channel, RequestContext}; + +const LOG_TARGET: &str = "base_layer::p2p::liveness_mock"; + +pub fn create_p2p_liveness_mock(buf_size: usize) -> (LivenessHandle, LivenessMock) { + let (sender, receiver) = reply_channel::unbounded(); + let (publisher, subscriber) = broadcast_channel::bounded(buf_size); + ( + LivenessHandle::new(sender, subscriber), + LivenessMock::new(receiver, LivenessMockState::new(publisher)), + ) +} + +#[derive(Debug, Clone)] +pub struct LivenessMockState { + call_count: Arc, + event_publisher: Arc>>, + calls: Arc>>, +} + +impl LivenessMockState { + pub fn new(event_publisher: Publisher) -> Self { + Self { + call_count: Arc::new(AtomicUsize::new(0)), + event_publisher: Arc::new(RwLock::new(event_publisher)), + calls: Arc::new(RwLock::new(Vec::new())), + } + } + + pub async fn publish_event(&self, event: LivenessEvent) -> Result<(), SendError> { + acquire_write_lock!(self.event_publisher).send(event).await + } + + pub fn add_request_call(&self, req: LivenessRequest) { + self.call_count.fetch_add(1, Ordering::SeqCst); + acquire_write_lock!(self.calls).push(req); + } + + pub fn take_calls(&self) -> Vec { + acquire_write_lock!(self.calls).drain(..).collect() + } + + pub fn call_count(&self) -> usize { + self.call_count.load(Ordering::SeqCst) + } +} + +pub struct LivenessMock { + receiver: reply_channel::Receiver>, + mock_state: LivenessMockState, +} + +impl LivenessMock { + pub fn new( + receiver: reply_channel::Receiver>, + mock_state: LivenessMockState, + ) -> Self + { + Self { receiver, mock_state } + } + + pub fn get_mock_state(&self) -> LivenessMockState { + self.mock_state.clone() + } + + pub fn set_mock_state(&mut self, mock_state: LivenessMockState) { + self.mock_state = mock_state; + } + + pub async fn run(mut self) { + while let Some(req) = self.receiver.next().await { + self.handle_request(req).await; + } + } + + async fn handle_request(&self, req: RequestContext>) { + use LivenessRequest::*; + let (req, reply_tx) = req.split(); + trace!(target: LOG_TARGET, "LivenessMock received request {:?}", req); + self.mock_state.add_request_call(req.clone()); + // TODO: Make these responses configurable + match req { + SendPing(_) => { + reply_tx.send(Ok(LivenessResponse::Ok)).unwrap(); + }, + GetPingCount => { + reply_tx.send(Ok(LivenessResponse::Count(1))).unwrap(); + }, + GetPongCount => { + reply_tx.send(Ok(LivenessResponse::Count(1))).unwrap(); + }, + GetAvgLatency(_) => { + reply_tx.send(Ok(LivenessResponse::AvgLatency(None))).unwrap(); + }, + SetPongMetadata(_, _) => { + reply_tx.send(Ok(LivenessResponse::Ok)).unwrap(); + }, + GetNumActiveNeighbours => { + reply_tx.send(Ok(LivenessResponse::NumActiveNeighbours(8))).unwrap(); + }, + AddNodeId(_n) => reply_tx.send(Ok(LivenessResponse::NodeIdAdded)).unwrap(), + GetNodeIdStats(_n) => reply_tx + .send(Ok(LivenessResponse::NodeIdStats(NodeStats::new()))) + .unwrap(), + } + } +} diff --git a/base_layer/p2p/src/services/liveness/mod.rs b/base_layer/p2p/src/services/liveness/mod.rs index f258df5b5f..badd4eb846 100644 --- a/base_layer/p2p/src/services/liveness/mod.rs +++ b/base_layer/p2p/src/services/liveness/mod.rs @@ -25,10 +25,9 @@ //! This service is responsible for sending pings to any peer as well as maintaining //! some very basic counters for the number of ping/pongs sent and received. //! -//! It consists of: -//! - A service handle which makes requests to the Liveness backend. Types of requests can be found in the -//! [LivenessRequest] enum. -//! - A handler for incoming [PingPong] messages. +//! It is responsible for: +//! - handling requests to the Liveness backend. Types of requests can be found in the [LivenessRequest] enum, and +//! - reading incoming [PingPong] messages and processing them. //! //! In future, this service may be expanded to included periodic pings to maintain //! latency and availability statistics for peers. @@ -36,127 +35,171 @@ //! [LivenessRequest]: ./messages/enum.LivenessRequets.html //! [PingPong]: ./messages/enum.PingPong.html -mod error; -mod handler; -mod messages; +mod config; +pub mod error; +mod handle; +mod message; +mod neighbours; mod service; mod state; -use self::{error::LivenessError, handler::LivenessHandler, service::LivenessService, state::LivenessState}; +use self::{message::PingPongMessage, service::LivenessService, state::LivenessState}; use crate::{ - services::{ - comms_outbound::CommsOutboundHandle, - domain_deserializer::DomainMessageDeserializer, - ServiceHandlesFuture, - ServiceName, - }, - tari_message::{NetMessage, TariMessageType}, + comms_connector::PeerMessage, + domain_message::DomainMessage, + services::utils::{map_decode, ok_or_skip_result}, + tari_message::TariMessageType, }; -use futures::{future, Future, Stream}; +use futures::{future, Future, Stream, StreamExt}; use log::*; -use std::{fmt::Debug, sync::Arc}; -use tari_comms::{builder::CommsServices, domain_subscriber::MessageInfo}; +use std::sync::Arc; +use tari_broadcast_channel as broadcast_channel; +use tari_comms_dht::{outbound::OutboundMessageRequester, DhtRequester}; +use tari_pubsub::TopicSubscriptionFactory; use tari_service_framework::{ - transport::{self, Requester}, + handles::ServiceHandlesFuture, + reply_channel, ServiceInitializationError, ServiceInitializer, }; +use tari_shutdown::ShutdownSignal; +use tokio::runtime; -pub use self::messages::{LivenessRequest, LivenessResponse, PingPong}; -use tari_comms::inbound_message_service::InboundTopicSubscriptionFactory; +#[cfg(feature = "test-mocks")] +pub mod mock; -pub type LivenessHandle = Requester>; +// Public exports +pub use self::{ + config::LivenessConfig, + handle::{LivenessEvent, LivenessHandle, LivenessRequest, LivenessResponse, PongEvent}, + state::Metadata, +}; +pub use crate::proto::liveness::MetadataKey; -const LOG_TARGET: &'static str = "base_layer::p2p::services::liveness"; +const LOG_TARGET: &str = "p2p::services::liveness"; /// Initializer for the Liveness service handle and service future. pub struct LivenessInitializer { - inbound_message_subscription_factory: Arc>, + config: Option, + inbound_message_subscription_factory: Arc>>, + dht_requester: Option, } impl LivenessInitializer { - /// Create a new LivenessInitializer from comms - pub fn new(comms: Arc>) -> Self { - Self { - inbound_message_subscription_factory: comms.inbound_message_subscription_factory(), - } - } - /// Create a new LivenessInitializer from the inbound message subscriber - #[cfg(test)] - pub fn inbound_message_subscription_factory( - inbound_message_subscription_factory: Arc>, - ) -> Self { + pub fn new( + config: LivenessConfig, + inbound_message_subscription_factory: Arc>>, + dht_requester: DhtRequester, + ) -> Self + { Self { + config: Some(config), inbound_message_subscription_factory, + dht_requester: Some(dht_requester), } } /// Get a stream of inbound PingPong messages - fn ping_stream(&self) -> impl Stream { + fn ping_stream(&self) -> impl Stream> { self.inbound_message_subscription_factory - .get_subscription_compat(TariMessageType::new(NetMessage::PingPong)) - .and_then(|msg| { - DomainMessageDeserializer::::new(msg).or_else(|_| { - error!(target: LOG_TARGET, "thread pool shut down"); - future::err(()) - }) - }) + .get_subscription(TariMessageType::PingPong) + .map(map_decode::) .filter_map(ok_or_skip_result) } } -impl ServiceInitializer for LivenessInitializer { - fn initialize(self: Box, handles: ServiceHandlesFuture) -> Result<(), ServiceInitializationError> { - let liveness_service = handles.lazy_service(move |handles| { - // All handles are ready - let state = Arc::new(LivenessState::new()); - let outbound_handle = handles - .get_handle::(ServiceName::CommsOutbound) - .expect("Liveness service requires CommsOutbound service handle"); +impl ServiceInitializer for LivenessInitializer { + type Future = impl Future>; - // Setup and start the inbound message handler - let mut handler = LivenessHandler::new(Arc::clone(&state), outbound_handle.clone()); - let inbound_handler = self.ping_stream().for_each(move |(info, msg)| { - handler.handle_message(info, msg).or_else(|err| { - error!("Error when processing message: {:?}", err); - future::err(()) - }) - }); + fn initialize( + &mut self, + executor: runtime::Handle, + handles_fut: ServiceHandlesFuture, + shutdown: ShutdownSignal, + ) -> Self::Future + { + let (sender, receiver) = reply_channel::unbounded(); - tokio::spawn(inbound_handler); + let (publisher, subscriber) = broadcast_channel::bounded(100); - LivenessService::new(state, outbound_handle) - }); + let liveness_handle = LivenessHandle::new(sender, subscriber); - let (requester, responder) = transport::channel(liveness_service); - // Register handle and spawn the responder service - handles.insert(ServiceName::Liveness, requester); - tokio::spawn(responder); + // Saving a clone + let config = self + .config + .take() + .expect("Liveness service initialized more than once."); - Ok(()) - } -} + let mut dht_requester = self + .dht_requester + .take() + .expect("Liveness service initialized more than once."); -fn ok_or_skip_result(res: Result) -> Option -where E: Debug { - match res { - Ok(t) => Some(t), - Err(err) => { - tracing::error!(target: LOG_TARGET, "{:?}", err); - None - }, - } -} + // Register handle before waiting for handles to be ready + handles_fut.register(liveness_handle); + + // Create a stream which receives PingPong messages from comms + let ping_stream = self.ping_stream(); + + // Spawn the Liveness service on the executor + executor.spawn(async move { + // Wait for all handles to become available + let handles = handles_fut.await; -#[cfg(test)] -mod test { - #[test] - fn ok_or_skip_result() { - let res = Result::<_, ()>::Ok(()); - assert_eq!(super::ok_or_skip_result(res).unwrap(), ()); + let outbound_handle = handles + .get_handle::() + .expect("Liveness service requires CommsOutbound service handle"); + + if config.enable_auto_join { + match dht_requester.send_join().await { + Ok(_) => { + trace!(target: LOG_TARGET, "Join message has been sent to closest peers",); + }, + Err(err) => { + error!( + target: LOG_TARGET, + "Failed to send join message on startup because '{}'", err + ); + }, + } + } + + if config.enable_auto_stored_message_request { + // TODO: Record when store message request was last requested + // and request messages from after that time + match dht_requester.send_request_stored_messages().await { + Ok(_) => { + trace!( + target: LOG_TARGET, + "Stored message request has been sent to closest peers", + ); + }, + Err(err) => { + error!( + target: LOG_TARGET, + "Failed to send stored message on startup because '{}'", err + ); + }, + } + } + + let state = LivenessState::new(); + + let service = LivenessService::new( + config, + receiver, + ping_stream, + state, + dht_requester, + outbound_handle, + publisher, + shutdown, + ); + service.run().await; + debug!(target: LOG_TARGET, "Liveness service has shut down"); + }); - let res = Result::<(), _>::Err(()); - assert!(super::ok_or_skip_result(res).is_none()); + future::ready(Ok(())) } } diff --git a/base_layer/p2p/src/services/liveness/neighbours.rs b/base_layer/p2p/src/services/liveness/neighbours.rs new file mode 100644 index 0000000000..b7ac397c73 --- /dev/null +++ b/base_layer/p2p/src/services/liveness/neighbours.rs @@ -0,0 +1,66 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use chrono::{NaiveDateTime, Utc}; +use std::time::Duration; +use tari_comms::peer_manager::{NodeId, Peer}; + +pub struct Neighbours { + last_updated: Option, + peers: Vec, + stale_interval: Duration, +} + +impl Neighbours { + pub fn new(stale_interval: Duration) -> Self { + Self { + last_updated: None, + peers: Vec::default(), + stale_interval, + } + } + + pub fn is_fresh(&self) -> bool { + self.last_updated + .map(|dt| { + let chrono_dt = chrono::Duration::from_std(self.stale_interval) + .expect("Neighbours::stale_interval is too large (overflows chrono::Duration::from_std)"); + dt.checked_add_signed(chrono_dt) + .map(|dt| dt < Utc::now().naive_utc()) + .expect("Neighbours::stale_interval is too large (overflows i32 when added to NaiveDateTime)") + }) + .unwrap_or(false) + } + + pub fn set_peers(&mut self, peers: Vec) { + self.peers = peers; + self.last_updated = Some(Utc::now().naive_utc()); + } + + pub fn peers(&self) -> &[Peer] { + &self.peers + } + + pub fn contains(&self, node_id: &NodeId) -> bool { + self.peers.iter().map(|p| &p.node_id).any(|n| n == node_id) + } +} diff --git a/base_layer/p2p/src/services/liveness/service.rs b/base_layer/p2p/src/services/liveness/service.rs index bd9fcb7976..6b8b90993b 100644 --- a/base_layer/p2p/src/services/liveness/service.rs +++ b/base_layer/p2p/src/services/liveness/service.rs @@ -20,55 +20,315 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use super::{error::LivenessError, state::LivenessState, LivenessRequest, LivenessResponse}; +use super::{ + config::LivenessConfig, + error::LivenessError, + message::{PingPong, PingPongMessage}, + state::LivenessState, + LivenessRequest, + LivenessResponse, + LOG_TARGET, +}; use crate::{ - services::{comms_outbound::CommsOutboundHandle, liveness::messages::PingPong}, - tari_message::{NetMessage, TariMessageType}, + domain_message::DomainMessage, + services::liveness::{neighbours::Neighbours, LivenessEvent, PongEvent}, + tari_message::TariMessageType, +}; +use futures::{pin_mut, stream::StreamExt, task::Context, SinkExt, Stream}; +use log::*; +use std::{pin::Pin, task::Poll, time::Instant}; +use tari_broadcast_channel::Publisher; +use tari_comms::{ + peer_manager::{NodeId, Peer}, + types::CommsPublicKey, }; -use futures::{ - future::{self, Either}, - Future, - Poll, +use tari_comms_dht::{ + broadcast_strategy::BroadcastStrategy, + domain_message::OutboundDomainMessage, + outbound::{DhtOutboundError, OutboundEncryption, OutboundMessageRequester}, + DhtRequester, }; -use std::sync::Arc; -use tari_comms::{message::MessageFlags, outbound_message_service::BroadcastStrategy, types::CommsPublicKey}; -use tower_service::Service; +use tari_service_framework::RequestContext; +use tari_shutdown::ShutdownSignal; +use tokio::time; /// Service responsible for testing Liveness for Peers. /// /// Very basic global ping and pong counter stats are implemented. In future, /// peer latency and availability stats will be added. -pub struct LivenessService { - state: Arc, - oms_handle: CommsOutboundHandle, +pub struct LivenessService { + config: LivenessConfig, + request_rx: Option, + ping_stream: Option, + state: LivenessState, + dht_requester: DhtRequester, + oms_handle: OutboundMessageRequester, + event_publisher: Publisher, + shutdown_signal: Option, + neighbours: Neighbours, } -impl LivenessService { - pub fn new(state: Arc, oms_handle: CommsOutboundHandle) -> Self { - Self { state, oms_handle } +impl LivenessService { + pub fn new( + config: LivenessConfig, + request_rx: THandleStream, + ping_stream: TPingStream, + state: LivenessState, + dht_requester: DhtRequester, + oms_handle: OutboundMessageRequester, + event_publisher: Publisher, + shutdown_signal: ShutdownSignal, + ) -> Self + { + Self { + request_rx: Some(request_rx), + ping_stream: Some(ping_stream), + state, + dht_requester, + oms_handle, + event_publisher, + shutdown_signal: Some(shutdown_signal), + neighbours: Neighbours::new(config.refresh_neighbours_interval), + config, + } } +} - fn send_ping( - &mut self, - pub_key: CommsPublicKey, - ) -> impl Future, Error = ()> - { - let state = self.state.clone(); +impl LivenessService +where + TPingStream: Stream>, + THandleStream: Stream>>, +{ + pub async fn run(mut self) { + let ping_stream = self.ping_stream.take().expect("ping_stream cannot be None").fuse(); + pin_mut!(ping_stream); + + let request_stream = self.request_rx.take().expect("ping_stream cannot be None").fuse(); + pin_mut!(request_stream); + + let mut ping_tick = match self.config.auto_ping_interval { + Some(interval) => EitherStream::Left(time::interval_at((Instant::now() + interval).into(), interval)), + None => EitherStream::Right(futures::stream::iter(Vec::new())), + } + .fuse(); + + let mut shutdown_signal = self + .shutdown_signal + .take() + .expect("Liveness service initialized without shutdown signal"); + + loop { + futures::select! { + // Requests from the handle + request_context = request_stream.select_next_some() => { + let (request, reply_tx) = request_context.split(); + let _ = reply_tx.send(self.handle_request(request).await).or_else(|resp| { + error!(target: LOG_TARGET, "Failed to send reply"); + Err(resp) + }); + }, + + _ = ping_tick.select_next_some() => { + let _ = self.ping_neighbours().await.or_else(|err| { + error!(target: LOG_TARGET, "Error when pinging neighbours: {}", err); + Err(err) + }); + let _ = self.ping_monitored_node_ids().await.or_else(|err| { + error!(target: LOG_TARGET, "Error when pinging monitored nodes: {}", err); + Err(err) + }); + }, + // Incoming messages from the Comms layer + msg = ping_stream.select_next_some() => { + let _ = self.handle_incoming_message(msg).await.or_else(|err| { + error!(target: LOG_TARGET, "Failed to handle incoming PingPong message: {:?}", err); + Err(err) + }); + }, + _ = shutdown_signal => { + info!(target: LOG_TARGET, "Liveness service shutting down because the shutdown signal was received"); + break; + } + } + } + } + + async fn handle_incoming_message(&mut self, msg: DomainMessage) -> Result<(), LivenessError> { + let inner_msg = msg.inner(); + match inner_msg.kind().ok_or_else(|| LivenessError::InvalidPingPongType)? { + PingPong::Ping => { + self.state.inc_pings_received(); + self.send_pong(msg.inner.nonce, msg.source_peer.public_key.clone()) + .await + .unwrap(); + self.state.inc_pongs_sent(); + + self.publish_event(LivenessEvent::ReceivedPing).await?; + }, + PingPong::Pong => { + let maybe_latency = self.state.record_pong(inner_msg.nonce); + trace!(target: LOG_TARGET, "Recorded latency: {:?}", maybe_latency); + let is_neighbour = self.neighbours.contains(&msg.source_peer.node_id); + let is_monitored = self.state.is_monitored_node_id(&msg.source_peer.node_id); + let pong_event = PongEvent::new( + msg.source_peer.node_id.clone(), + maybe_latency, + msg.inner.metadata.into(), + is_neighbour, + is_monitored, + ); + + self.publish_event(LivenessEvent::ReceivedPong(Box::new(pong_event))) + .await?; + }, + } + Ok(()) + } + + async fn send_pong(&mut self, nonce: u64, dest: CommsPublicKey) -> Result<(), LivenessError> { + let msg = PingPongMessage::pong_with_metadata(nonce, self.state.pong_metadata().clone()); self.oms_handle - .send_message( - BroadcastStrategy::DirectPublicKey(pub_key), - MessageFlags::empty(), - TariMessageType::new(NetMessage::PingPong), - PingPong::Ping, + .send_direct( + dest, + OutboundEncryption::None, + OutboundDomainMessage::new(TariMessageType::PingPong, msg), ) - .and_then(move |res| { - state.inc_pings_sent(); - future::ok( - res.map(|_| LivenessResponse::PingSent) - .map_err(LivenessError::CommsOutboundError), + .await + .map(|_| ()) + .map_err(Into::into) + } + + async fn handle_request(&mut self, request: LivenessRequest) -> Result { + use LivenessRequest::*; + match request { + SendPing(node_id) => { + self.send_ping(node_id).await?; + self.state.inc_pings_sent(); + Ok(LivenessResponse::Ok) + }, + GetPingCount => { + let ping_count = self.get_ping_count(); + Ok(LivenessResponse::Count(ping_count)) + }, + GetPongCount => { + let pong_count = self.get_pong_count(); + Ok(LivenessResponse::Count(pong_count)) + }, + GetAvgLatency(node_id) => { + let latency = self.state.get_avg_latency_ms(&node_id); + Ok(LivenessResponse::AvgLatency(latency)) + }, + SetPongMetadata(key, value) => { + self.state.set_pong_metadata_entry(key, value); + Ok(LivenessResponse::Ok) + }, + GetNumActiveNeighbours => { + let num_active_neighbours = self.state.num_active_neighbours(); + Ok(LivenessResponse::NumActiveNeighbours(num_active_neighbours)) + }, + AddNodeId(node_id) => { + self.state.add_node_id(&node_id); + self.send_ping(node_id.clone()).await?; + Ok(LivenessResponse::NodeIdAdded) + }, + GetNodeIdStats(node_id) => self + .state + .get_node_id_stats(&node_id) + .map(LivenessResponse::NodeIdStats), + } + } + + async fn send_ping(&mut self, node_id: NodeId) -> Result<(), LivenessError> { + let msg = PingPongMessage::ping(); + self.state.add_inflight_ping(msg.nonce, &node_id); + self.oms_handle + .send_direct_node_id( + node_id, + OutboundEncryption::None, + OutboundDomainMessage::new(TariMessageType::PingPong, msg), + ) + .await + .map_err(Into::::into)?; + + Ok(()) + } + + async fn update_neighbours_if_stale(&mut self) -> Result<&[Peer], LivenessError> { + if self.neighbours.is_fresh() { + return Ok(self.neighbours.peers()); + } + + let peers = self + .dht_requester + .select_peers(BroadcastStrategy::Neighbours(Vec::new())) + .await?; + + self.state.set_num_active_neighbours(peers.len()); + self.neighbours.set_peers(peers); + + Ok(self.neighbours.peers()) + } + + async fn ping_neighbours(&mut self) -> Result<(), LivenessError> { + self.update_neighbours_if_stale().await?; + let peers = self.neighbours.peers(); + let len_peers = peers.len(); + trace!( + target: LOG_TARGET, + "Sending liveness ping to {} neighbour(s)", + len_peers + ); + + for peer in peers { + let msg = PingPongMessage::ping(); + self.state.add_inflight_ping(msg.nonce, &peer.node_id); + self.oms_handle + .send_direct( + peer.public_key.clone(), + OutboundEncryption::None, + OutboundDomainMessage::new(TariMessageType::PingPong, msg), ) - }) - .or_else(|_| future::ok(Err(LivenessError::SendPingFailed))) + .await?; + } + + self.publish_event(LivenessEvent::BroadcastedNeighbourPings(len_peers)) + .await?; + + Ok(()) + } + + async fn ping_monitored_node_ids(&mut self) -> Result<(), LivenessError> { + let num_nodes = self.state.get_num_monitored_nodes(); + if num_nodes > 0 { + trace!( + target: LOG_TARGET, + "Sending liveness ping to {} monitored nodes", + num_nodes, + ); + for node_id in self.state.get_monitored_node_ids() { + let msg = PingPongMessage::ping(); + self.state.add_inflight_ping(msg.nonce, &node_id); + self.oms_handle + .send_direct_node_id( + node_id, + OutboundEncryption::None, + OutboundDomainMessage::new(TariMessageType::PingPong, msg), + ) + .await + .map_err(Into::::into)?; + } + + self.publish_event(LivenessEvent::BroadcastedMonitoredNodeIdPings(num_nodes)) + .await?; + } + Ok(()) + } + + async fn publish_event(&mut self, event: LivenessEvent) -> Result<(), LivenessError> { + self.event_publisher + .send(event) + .await + .map_err(|_| LivenessError::EventStreamError) } fn get_ping_count(&self) -> usize { @@ -80,27 +340,29 @@ impl LivenessService { } } -impl Service for LivenessService { - type Error = (); - type Future = impl Future; - type Response = Result; +// Unfortunately, `stream::Either` doesn't exist yet in futures-0.3.0 +enum EitherStream { + Left(A), + Right(B), +} - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(().into()) - } +impl Stream for EitherStream +where + A: Stream + Unpin, + B: Stream + Unpin, +{ + type Item = A::Item; - fn call(&mut self, req: LivenessRequest) -> Self::Future { - match req { - LivenessRequest::SendPing(pub_key) => Either::A( - self.send_ping(pub_key) - .or_else(|_| future::ok(Err(LivenessError::SendPingFailed))), - ), - LivenessRequest::GetPingCount => Either::B(future::ok(Result::<_, LivenessError>::Ok( - LivenessResponse::Count(self.get_ping_count()), - ))), - LivenessRequest::GetPongCount => Either::B(future::ok(Result::<_, LivenessError>::Ok( - LivenessResponse::Count(self.get_pong_count()), - ))), + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + match &mut *self { + EitherStream::Left(stream) => { + pin_mut!(stream); + stream.poll_next(cx) + }, + EitherStream::Right(stream) => { + pin_mut!(stream); + stream.poll_next(cx) + }, } } } @@ -108,64 +370,247 @@ impl Service for LivenessService { #[cfg(test)] mod test { use super::*; - use futures::Async; + use crate::{ + proto::liveness::MetadataKey, + services::liveness::{handle::LivenessHandle, state::Metadata}, + }; + use futures::{channel::mpsc, stream}; use rand::rngs::OsRng; + use std::{ + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + time::Duration, + }; + use tari_broadcast_channel as broadcast_channel; + use tari_comms::{ + multiaddr::Multiaddr, + peer_manager::{NodeId, Peer, PeerFeatures, PeerFlags}, + }; + use tari_comms_dht::{ + envelope::{DhtMessageHeader, DhtMessageType, Network}, + outbound::{DhtOutboundRequest, SendMessageResponse}, + }; use tari_crypto::keys::PublicKey; - use tari_service_framework::transport; - use tokio::runtime::Runtime; - use tower_util::service_fn; + use tari_service_framework::reply_channel; + use tari_shutdown::Shutdown; + use tari_test_utils::runtime; #[test] fn get_ping_pong_count() { - let state = Arc::new(LivenessState::new()); - state.inc_pings_received(); - state.inc_pongs_received(); - state.inc_pongs_received(); + runtime::test_async(|rt| { + let state = LivenessState::new(); + state.inc_pings_received(); + state.inc_pongs_received(); + state.inc_pongs_received(); - let outbound_service = service_fn(|_| future::ok::<_, ()>(Ok(()))); - let (req, _res) = transport::channel(outbound_service); - let oms_handle = CommsOutboundHandle::new(req); + // Setup a CommsOutbound service handle which is not connected to the actual CommsOutbound service + let (outbound_tx, _) = mpsc::channel(10); + let oms_handle = OutboundMessageRequester::new(outbound_tx); - let mut service = LivenessService::new(state, oms_handle); + // Setup liveness service + let (sender_service, receiver) = reply_channel::unbounded(); + let (publisher, subscriber) = broadcast_channel::bounded(100); + let mut liveness_handle = LivenessHandle::new(sender_service, subscriber); - let mut fut = service.call(LivenessRequest::GetPingCount); - match fut.poll().unwrap() { - Async::Ready(Ok(LivenessResponse::Count(n))) => assert_eq!(n, 1), - _ => panic!(), - } + let (dht_tx, _) = mpsc::channel(10); + let dht_requester = DhtRequester::new(dht_tx); - let mut fut = service.call(LivenessRequest::GetPongCount); - match fut.poll().unwrap() { - Async::Ready(Ok(LivenessResponse::Count(n))) => assert_eq!(n, 2), - _ => panic!(), - } + let mut shutdown = Shutdown::new(); + let service = LivenessService::new( + Default::default(), + receiver, + stream::empty(), + state, + dht_requester, + oms_handle, + publisher, + shutdown.to_signal(), + ); + + // Run the service + rt.spawn(service.run()); + + let res = rt.block_on(liveness_handle.get_ping_count()).unwrap(); + assert_eq!(res, 1); + + let res = rt.block_on(liveness_handle.get_pong_count()).unwrap(); + assert_eq!(res, 2); + + shutdown.trigger().unwrap(); + }); } #[test] fn send_ping() { - let mut rt = Runtime::new().unwrap(); - let state = Arc::new(LivenessState::new()); - - // This service stubs out CommsOutboundService and always returns a successful result. - // Therefore, LivenessService will behave as if it was able to send the ping - // without actually sending it. - let outbound_service = service_fn(|_| future::ok::<_, ()>(Ok(()))); - let (req, res) = transport::channel(outbound_service); - rt.spawn(res); - - let oms_handle = CommsOutboundHandle::new(req); - - let mut service = LivenessService::new(Arc::clone(&state), oms_handle); - - let mut rng = OsRng::new().unwrap(); - let (_, pk) = CommsPublicKey::random_keypair(&mut rng); - let fut = service.call(LivenessRequest::SendPing(pk)); - match rt.block_on(fut).unwrap() { - Ok(LivenessResponse::PingSent) => {}, - Ok(_) => panic!("received unexpected response from liveness service"), - Err(err) => panic!("received unexpected error from liveness service: {:?}", err), + runtime::test_async(|rt| { + let state = LivenessState::new(); + + // Setup a CommsOutbound service handle which is not connected to the actual CommsOutbound service + let (outbound_tx, mut outbound_rx) = mpsc::channel(10); + let oms_handle = OutboundMessageRequester::new(outbound_tx); + + // Setup liveness service + let (sender_service, receiver) = reply_channel::unbounded(); + let (publisher, subscriber) = broadcast_channel::bounded(100); + let mut liveness_handle = LivenessHandle::new(sender_service, subscriber); + + let (dht_tx, _) = mpsc::channel(10); + let dht_requester = DhtRequester::new(dht_tx); + + let mut shutdown = Shutdown::new(); + let service = LivenessService::new( + Default::default(), + receiver, + stream::empty(), + state, + dht_requester, + oms_handle, + publisher, + shutdown.to_signal(), + ); + + // Run the LivenessService + rt.spawn(service.run()); + + let (_, pk) = CommsPublicKey::random_keypair(&mut rand::rngs::OsRng); + let node_id = NodeId::from_key(&pk).unwrap(); + // Receive outbound request + rt.spawn(async move { + match outbound_rx.select_next_some().await { + DhtOutboundRequest::SendMessage(_, _, reply_tx) => { + reply_tx.send(SendMessageResponse::Queued(vec![])).unwrap(); + }, + } + }); + + let _res = rt.block_on(liveness_handle.send_ping(node_id)).unwrap(); + + shutdown.trigger().unwrap(); + }) + } + + fn create_dummy_message(inner: T) -> DomainMessage { + let (_, pk) = CommsPublicKey::random_keypair(&mut OsRng); + let source_peer = Peer::new( + pk.clone(), + NodeId::from_key(&pk).unwrap(), + Vec::::new().into(), + PeerFlags::empty(), + PeerFeatures::COMMUNICATION_NODE, + ); + DomainMessage { + dht_header: DhtMessageHeader::new( + Default::default(), + DhtMessageType::None, + None, + Network::LocalTest, + Default::default(), + ), + source_peer, + inner, } + } + + #[test] + fn handle_message_ping() { + runtime::test_async(|rt| { + let state = LivenessState::new(); + + // Setup a CommsOutbound service handle which is not connected to the actual CommsOutbound service + let (outbound_tx, mut outbound_rx) = mpsc::channel(10); + let oms_handle = OutboundMessageRequester::new(outbound_tx); + + let msg = create_dummy_message(PingPongMessage::ping()); + // A stream which emits one message and then closes + let pingpong_stream = stream::iter(std::iter::once(msg)); + + let (dht_tx, _) = mpsc::channel(10); + let dht_requester = DhtRequester::new(dht_tx); + // Setup liveness service + let (publisher, _subscriber) = broadcast_channel::bounded(100); + let mut shutdown = Shutdown::new(); + let service = LivenessService::new( + Default::default(), + stream::empty(), + pingpong_stream, + state, + dht_requester, + oms_handle, + publisher, + shutdown.to_signal(), + ); + + rt.spawn(service.run()); + + rt.spawn(async move { + // Test oms got request to send message + unwrap_oms_send_msg!(outbound_rx.select_next_some().await); + shutdown.trigger().unwrap(); + }); + }); + } + + #[test] + fn handle_message_pong() { + runtime::test_async(|rt| { + rt.spawn(async { + let mut state = LivenessState::new(); + + let (outbound_tx, _) = mpsc::channel(10); + let oms_handle = OutboundMessageRequester::new(outbound_tx); + + let mut metadata = Metadata::new(); + metadata.insert(MetadataKey::ChainMetadata, b"dummy-data".to_vec()); + let msg = create_dummy_message(PingPongMessage::pong_with_metadata(123, metadata)); + + state.add_inflight_ping(msg.inner.nonce, &msg.source_peer.node_id); + // A stream which emits one message and then closes + let pingpong_stream = stream::iter(std::iter::once(msg)); + + let (dht_tx, _) = mpsc::channel(10); + let dht_requester = DhtRequester::new(dht_tx); + // Setup liveness service + let (publisher, subscriber) = broadcast_channel::bounded(100); + let mut shutdown = Shutdown::new(); + let service = LivenessService::new( + Default::default(), + stream::empty(), + pingpong_stream, + state, + dht_requester, + oms_handle, + publisher, + shutdown.to_signal(), + ); + + // Create a flag that gets flipped when the subscribed event is received + let received_event = Arc::new(AtomicBool::new(false)); + let rec_event_clone = received_event.clone(); + + // Listen for the pong event + futures::join!( + async move { + let event = time::timeout(Duration::from_secs(10), subscriber.fuse().select_next_some()) + .await + .unwrap(); + + match &*event { + LivenessEvent::ReceivedPong(event) => { + rec_event_clone.store(true, Ordering::SeqCst); + assert_eq!(event.metadata.get(MetadataKey::ChainMetadata).unwrap(), b"dummy-data"); + }, + _ => panic!("Unexpected event"), + } + + shutdown.trigger().unwrap(); + }, + service.run() + ); - assert_eq!(state.pings_sent(), 1); + assert_eq!(received_event.load(Ordering::SeqCst), true); + }); + }); } } diff --git a/base_layer/p2p/src/services/liveness/state.rs b/base_layer/p2p/src/services/liveness/state.rs index 0d08f55516..74698dc4ec 100644 --- a/base_layer/p2p/src/services/liveness/state.rs +++ b/base_layer/p2p/src/services/liveness/state.rs @@ -20,16 +20,64 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::sync::atomic::{AtomicUsize, Ordering}; +use crate::{proto::liveness::MetadataKey, services::liveness::error::LivenessError}; +use chrono::{NaiveDateTime, Utc}; +use std::{ + collections::HashMap, + sync::atomic::{AtomicUsize, Ordering}, + time::Duration, +}; +use tari_comms::peer_manager::NodeId; + +const LATENCY_SAMPLE_WINDOW_SIZE: usize = 25; +const MAX_INFLIGHT_TTL: Duration = Duration::from_secs(20); + +/// Represents metadata in a ping/pong message. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct Metadata { + inner: HashMap>, +} + +impl Metadata { + pub fn new() -> Self { + Default::default() + } + + pub fn insert(&mut self, key: MetadataKey, value: Vec) { + self.inner.insert(key as i32, value); + } + + pub fn get(&self, key: MetadataKey) -> Option<&Vec> { + self.inner.get(&(key as i32)) + } +} + +impl From>> for Metadata { + fn from(inner: HashMap>) -> Self { + Self { inner } + } +} + +impl From for HashMap> { + fn from(metadata: Metadata) -> Self { + metadata.inner + } +} /// State for the LivenessService. #[derive(Default)] pub struct LivenessState { + inflight_pings: HashMap, + peer_latency: HashMap, + pings_received: AtomicUsize, pongs_received: AtomicUsize, - pings_sent: AtomicUsize, pongs_sent: AtomicUsize, + num_active_neighbours: AtomicUsize, + + pong_metadata: Metadata, + nodes_to_monitor: HashMap, } impl LivenessState { @@ -45,14 +93,6 @@ impl LivenessState { self.pongs_sent.fetch_add(1, Ordering::Relaxed) } - pub fn pings_sent(&self) -> usize { - self.pings_sent.load(Ordering::Relaxed) - } - - pub fn pongs_sent(&self) -> usize { - self.pongs_sent.load(Ordering::Relaxed) - } - pub fn inc_pings_received(&self) -> usize { self.pings_received.fetch_add(1, Ordering::Relaxed) } @@ -68,6 +108,173 @@ impl LivenessState { pub fn pongs_received(&self) -> usize { self.pongs_received.load(Ordering::Relaxed) } + + pub fn num_active_neighbours(&self) -> usize { + self.num_active_neighbours.load(Ordering::Relaxed) + } + + pub fn set_num_active_neighbours(&self, num_active_neighbours: usize) { + self.num_active_neighbours + .store(num_active_neighbours, Ordering::Relaxed); + } + + #[cfg(test)] + pub fn pings_sent(&self) -> usize { + self.pings_sent.load(Ordering::Relaxed) + } + + #[cfg(test)] + pub fn pongs_sent(&self) -> usize { + self.pongs_sent.load(Ordering::Relaxed) + } + + /// Returns a reference to pong metadata + pub fn pong_metadata(&self) -> &Metadata { + &self.pong_metadata + } + + /// Set a pong metadata entry. Duplicate entries are replaced. + pub fn set_pong_metadata_entry(&mut self, key: MetadataKey, value: Vec) { + self.pong_metadata.insert(key, value); + } + + /// Adds a ping to the inflight ping list, while noting the current time that a ping was sent. + pub fn add_inflight_ping(&mut self, nonce: u64, node_id: &NodeId) { + let now = Utc::now().naive_utc(); + self.inflight_pings.insert(nonce, ((*node_id).clone(), now.clone())); + if let Some(ns) = self.nodes_to_monitor.get_mut(node_id) { + ns.last_ping_sent = Some(now); + } + self.clear_stale_inflight_pings(); + } + + /// Clears inflight ping requests which have not responded + fn clear_stale_inflight_pings(&mut self) { + self.inflight_pings = self + .inflight_pings + .drain() + .filter(|(_, (_, time))| convert_to_std_duration(Utc::now().naive_utc() - *time) <= MAX_INFLIGHT_TTL) + .collect(); + } + + /// Records a pong. Specifically, the pong counter is incremented and + /// a latency sample is added and calculated. + pub fn record_pong(&mut self, nonce: u64) -> Option { + self.inc_pongs_received(); + + match self.inflight_pings.remove_entry(&nonce) { + Some((_, (node_id, sent_time))) => { + let now = Utc::now().naive_utc(); + if let Some(ns) = self.nodes_to_monitor.get_mut(&node_id) { + ns.last_pong_received = Some(sent_time); + ns.average_latency.add_sample(convert_to_std_duration(now - sent_time)); + } + let latency = self + .add_latency_sample(node_id, convert_to_std_duration(now - sent_time)) + .calc_average(); + Some(latency) + }, + None => None, + } + } + + fn add_latency_sample(&mut self, node_id: NodeId, duration: Duration) -> &mut AverageLatency { + let latency = self + .peer_latency + .entry(node_id) + .or_insert_with(|| AverageLatency::new(LATENCY_SAMPLE_WINDOW_SIZE)); + + latency.add_sample(duration); + latency + } + + pub fn get_avg_latency_ms(&self, node_id: &NodeId) -> Option { + self.peer_latency.get(node_id).map(|latency| latency.calc_average()) + } + + pub fn add_node_id(&mut self, node_id: &NodeId) { + if self.nodes_to_monitor.contains_key(node_id) { + return; + } + let _ = self.nodes_to_monitor.insert(node_id.clone(), NodeStats::new()); + } + + pub fn get_num_monitored_nodes(&self) -> usize { + self.nodes_to_monitor.len() + } + + pub fn get_monitored_node_ids(&self) -> Vec { + self.nodes_to_monitor.keys().cloned().collect() + } + + pub fn is_monitored_node_id(&self, node_id: &NodeId) -> bool { + self.nodes_to_monitor.contains_key(node_id) + } + + pub fn get_node_id_stats(&self, node_id: &NodeId) -> Result { + match self.nodes_to_monitor.get(node_id) { + None => Err(LivenessError::NodeIdDoesNotExist), + Some(s) => Ok((*s).clone()), + } + } +} + +/// Convert `chrono::Duration` to `std::time::Duration` +pub(super) fn convert_to_std_duration(old_duration: chrono::Duration) -> Duration { + Duration::from_millis(old_duration.num_milliseconds() as u64) +} + +/// A very simple implementation for calculating average latency. Samples are added in milliseconds and the mean average +/// is calculated for those samples. If more than [LATENCY_SAMPLE_WINDOW_SIZE](self::LATENCY_SAMPLE_WINDOW_SIZE) samples +/// are added the oldest sample is discarded. +#[derive(Clone, Debug, Default)] +pub struct AverageLatency { + samples: Vec, +} + +impl AverageLatency { + /// Create a new AverageLatency + pub fn new(num_samples: usize) -> Self { + Self { + samples: Vec::with_capacity(num_samples), + } + } + + /// Add a sample `Duration`. The number of milliseconds is capped at `u32::MAX`. + pub fn add_sample(&mut self, sample: Duration) { + if self.samples.len() == self.samples.capacity() { + self.samples.remove(0); + } + self.samples.push(sample.as_millis() as u32) + } + + /// Calculate the average of the recorded samples + pub fn calc_average(&self) -> u32 { + let samples = &self.samples; + if samples.is_empty() { + return 0; + } + + samples.iter().fold(0, |sum, x| sum + *x) / samples.len() as u32 + } +} + +/// This struct contains the stats about a Node that is being monitored by the Liveness Service +#[derive(Clone, Debug, Default)] +pub struct NodeStats { + last_ping_sent: Option, + last_pong_received: Option, + average_latency: AverageLatency, +} + +impl NodeStats { + pub fn new() -> NodeStats { + Self { + last_ping_sent: None, + last_pong_received: None, + average_latency: AverageLatency::new(LATENCY_SAMPLE_WINDOW_SIZE), + } + } } #[cfg(test)] @@ -124,4 +331,44 @@ mod test { assert_eq!(state.inc_pongs_received(), 0); assert_eq!(state.pongs_received.load(Ordering::SeqCst), 1); } + + #[test] + fn record_pong() { + let mut state = LivenessState::new(); + + let node_id = NodeId::default(); + state.add_inflight_ping(123, &node_id); + + let latency = state.record_pong(123).unwrap(); + assert!(latency < 50); + } + + #[test] + fn set_pong_metadata_entry() { + let mut state = LivenessState::new(); + state.set_pong_metadata_entry(MetadataKey::ChainMetadata, b"dummy-data".to_vec()); + assert_eq!( + state.pong_metadata().get(MetadataKey::ChainMetadata).unwrap(), + b"dummy-data" + ); + } + + #[test] + fn monitor_node_id() { + let node_id = NodeId::default(); + let mut state = LivenessState::new(); + state.add_node_id(&node_id); + + state.add_inflight_ping(123, &node_id); + + let latency = state.record_pong(123).unwrap(); + assert!(latency < 50); + + assert_eq!(state.get_num_monitored_nodes(), 1); + assert_eq!(state.get_monitored_node_ids().len(), 1); + assert!(state.is_monitored_node_id(&node_id)); + let stats = state.get_node_id_stats(&node_id).unwrap(); + + assert_eq!(stats.average_latency.calc_average(), latency); + } } diff --git a/base_layer/p2p/src/services/mod.rs b/base_layer/p2p/src/services/mod.rs index 2154dfad4f..809e1932c3 100644 --- a/base_layer/p2p/src/services/mod.rs +++ b/base_layer/p2p/src/services/mod.rs @@ -20,20 +20,6 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// mod initialization; pub mod comms_outbound; -mod domain_deserializer; pub mod liveness; -mod service_name; - -use tari_service_framework::handles; - -pub use self::service_name::ServiceName; - -/// ServiceHandles collection -pub type ServiceHandles = handles::ServiceHandles; -/// ServiceHandles future. -/// -/// This future wraps a ServiceHandles collection and will resolve to the handles -/// collection once `notify_ready` is called. -pub type ServiceHandlesFuture = handles::ServiceHandlesFuture; +pub mod utils; diff --git a/base_layer/p2p/tests/support/comms_outbound.rs b/base_layer/p2p/src/services/utils.rs similarity index 56% rename from base_layer/p2p/tests/support/comms_outbound.rs rename to base_layer/p2p/src/services/utils.rs index a35b64499c..b6c766ce1d 100644 --- a/base_layer/p2p/tests/support/comms_outbound.rs +++ b/base_layer/p2p/src/services/utils.rs @@ -20,37 +20,45 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use futures::future; -use std::sync::mpsc; -use tari_p2p::{ - executor::{transport, ServiceInitializationError, ServiceInitializer}, - services::{ - comms_outbound::{CommsOutboundHandle, CommsOutboundRequest}, - ServiceHandlesFuture, - ServiceName, - }, -}; -use tower_util::service_fn; +use crate::{comms_connector::PeerMessage, domain_message::DomainMessage}; +use log::*; +use std::{fmt::Debug, sync::Arc}; -pub struct TestCommsOutboundInitializer { - sender: Option>, -} +const LOG_TARGET: &str = "base_layer::p2p::services"; -impl TestCommsOutboundInitializer { - pub fn new(sender: mpsc::Sender) -> Self { - Self { sender: Some(sender) } +/// For use with `StreamExt::filter_map`. Log and filter any errors. +pub async fn ok_or_skip_result(res: Result) -> Option +where E: Debug { + match res { + Ok(t) => Some(t), + Err(err) => { + error!(target: LOG_TARGET, "{:?}", err); + None + }, } } -impl ServiceInitializer for TestCommsOutboundInitializer { - fn initialize(mut self: Box, handles: ServiceHandlesFuture) -> Result<(), ServiceInitializationError> { - let sender = self.sender.take().expect("cannot be None"); - let (oms_requester, oms_responder) = transport::channel(service_fn(move |req| { - sender.send(req).unwrap(); - future::ok::<_, ()>(Ok(())) - })); - tokio::spawn(oms_responder); - handles.insert(ServiceName::CommsOutbound, CommsOutboundHandle::new(oms_requester)); - Ok(()) +pub fn map_decode(serialized: Arc) -> Result, prost::DecodeError> +where T: prost::Message + Default { + Ok(DomainMessage { + source_peer: serialized.source_peer.clone(), + dht_header: serialized.dht_header.clone(), + inner: serialized.decode_message()?, + }) +} + +#[cfg(test)] +mod test { + use futures::executor::block_on; + + #[test] + fn ok_or_skip_result() { + block_on(async { + let res = Result::<_, ()>::Ok(()); + assert_eq!(super::ok_or_skip_result(res).await.unwrap(), ()); + + let res = Result::<(), _>::Err(()); + assert!(super::ok_or_skip_result(res).await.is_none()); + }); } } diff --git a/base_layer/p2p/src/sync_services/error.rs b/base_layer/p2p/src/sync_services/error.rs deleted file mode 100644 index e5d041f4bd..0000000000 --- a/base_layer/p2p/src/sync_services/error.rs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use derive_error::Error; -use serde::export::fmt::Debug; -use tari_comms::{builder::CommsServicesError, domain_subscriber::DomainSubscriberError}; - -#[derive(Debug, Error)] -pub enum ServiceError { - #[error(msg_embedded, non_std, no_from)] - ServiceInitializationFailed(String), - CommsServicesError(CommsServicesError), - /// Timeout waiting for service threads to complete - JoinTimedOut, - /// Failed to send shut - ShutdownSendFailed, - #[error(msg_embedded, non_std, no_from)] - InternalServiceError(String), - /// Unable to get sole ownership of comms services. Another thread still has a handle. - CommsServiceOwnershipError, - DomainSubscriberError(DomainSubscriberError), -} - -impl ServiceError { - pub fn internal_service_error() -> impl Fn(E) -> Self - where E: Debug { - |err| ServiceError::InternalServiceError(format!("Internal service error: {:?}", err)) - } -} diff --git a/base_layer/p2p/src/sync_services/executor.rs b/base_layer/p2p/src/sync_services/executor.rs deleted file mode 100644 index 08dc9c7345..0000000000 --- a/base_layer/p2p/src/sync_services/executor.rs +++ /dev/null @@ -1,301 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use super::{error::ServiceError, registry::ServiceRegistry}; -use crate::tari_message::TariMessageType; -use log::*; -use std::{ - sync::{Arc, Mutex}, - thread, - time::Duration, -}; -use tari_comms::{ - builder::CommsServices, - outbound_message_service::outbound_message_service::OutboundMessageService, - peer_manager::{NodeIdentity, PeerManager}, -}; -use threadpool::ThreadPool; - -use crossbeam_channel as channel; -use crossbeam_channel::{Receiver, RecvTimeoutError, Sender}; - -use tari_comms::{connection::InprocAddress, inbound_message_service::InboundTopicSubscriptionFactory}; -const LOG_TARGET: &str = "base_layer::p2p::services"; - -/// Control messages for services -pub enum ServiceControlMessage { - /// Service should shut down - Shutdown, -} - -/// This is responsible for creating and managing the thread pool for -/// services that should be executed. -pub struct ServiceExecutor { - thread_pool: Mutex, - senders: Vec>, -} - -impl ServiceExecutor { - /// Execute the services contained in the given [ServiceRegistry]. - pub fn execute(comms_services: &CommsServices, registry: ServiceRegistry) -> Self { - let thread_pool = threadpool::Builder::new() - .thread_name("DomainServices".to_string()) - .num_threads(registry.num_services()) - .thread_stack_size(1_000_000) - .build(); - - let mut senders = Vec::new(); - - for mut service in registry.services.into_iter() { - let (sender, receiver) = channel::unbounded(); - senders.push(sender); - - let service_context = ServiceContext { - oms: comms_services.outbound_message_service(), - ims_message_sink_address: comms_services.connection_manager().get_message_sink_address().clone(), - peer_manager: comms_services.peer_manager(), - node_identity: comms_services.node_identity(), - receiver, - inbound_message_subscription_factory: comms_services.inbound_message_subscription_factory(), - }; - - thread_pool.execute(move || { - info!(target: LOG_TARGET, "Starting service {}", service.get_name()); - - match service.execute(service_context) { - Ok(_) => { - info!( - target: LOG_TARGET, - "Service '{}' has successfully shut down", - service.get_name(), - ); - }, - Err(err) => { - error!( - target: LOG_TARGET, - "Service '{}' has exited with an error: {:?}", - service.get_name(), - err - ); - }, - } - }); - } - - Self { - thread_pool: Mutex::new(thread_pool), - senders, - } - } - - /// Send a [ServiceControlMessage::Shutdown] message to all services. - pub fn shutdown(&self) -> Result<(), ServiceError> { - let mut failed = false; - for sender in &self.senders { - if sender.send(ServiceControlMessage::Shutdown).is_err() { - failed = true; - } - } - - // TODO: Wait for services to exit and then shutdown the comms - // self.comms_services - // .shutdown() - // .map_err(ServiceError::CommsServicesError)?; - - if failed { - Err(ServiceError::ShutdownSendFailed) - } else { - Ok(()) - } - } - - /// Join on all threads in the thread pool until they all exit or a given timeout is reached. - pub fn join_timeout(self, timeout: Duration) -> Result<(), ServiceError> { - let (tx, rx) = channel::unbounded(); - let thread_pool = self.thread_pool; - thread::spawn(move || { - acquire_lock!(thread_pool).join(); - let _ = tx.send(()); - }); - - rx.recv_timeout(timeout).map_err(|_| ServiceError::JoinTimedOut)?; - - Ok(()) - } -} - -/// The context object given to each service. This allows the service to receive [ServiceControlMessage]s, -/// access the outbound message service and create [DomainConnector]s to receive comms messages of -/// a particular [TariMessageType]. -pub struct ServiceContext { - oms: Arc, - ims_message_sink_address: InprocAddress, - peer_manager: Arc, - node_identity: Arc, - receiver: Receiver, - inbound_message_subscription_factory: Arc>, -} - -impl ServiceContext { - /// Attempt to retrieve a control message. Returns `Some(ServiceControlMessage)` if there - /// is a message on the channel or `None` if the channel is empty and the timeout is reached. - pub fn get_control_message(&self, timeout: Duration) -> Option { - match self.receiver.recv_timeout(timeout) { - Ok(msg) => Some(msg), - // Sender has disconnected (dropped) so return a shutdown signal - // This should never happen in normal operation - Err(RecvTimeoutError::Disconnected) => Some(ServiceControlMessage::Shutdown), - Err(RecvTimeoutError::Timeout) => None, - } - } - - /// Retrieve and `Arc` of the outbound message service. Used for sending outbound messages. - pub fn outbound_message_service(&self) -> Arc { - Arc::clone(&self.oms) - } - - /// Retrieve and `Arc` of the PeerManager. Used for managing peers. - pub fn peer_manager(&self) -> Arc { - Arc::clone(&self.peer_manager) - } - - /// Retrieve and `Arc` of the NodeIdentity. Used for managing the current Nodes Identity. - pub fn node_identity(&self) -> Arc { - Arc::clone(&self.node_identity) - } - - /// Retrieve a reference to the message sink address of the InboundMessageService - pub fn ims_message_sink_address(&self) -> &InprocAddress { - &self.ims_message_sink_address - } - - pub fn inbound_message_subscription_factory(&self) -> Arc> { - Arc::clone(&self.inbound_message_subscription_factory) - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::{sync_services::Service, tari_message::NetMessage}; - use rand::rngs::OsRng; - use std::{path::PathBuf, sync::RwLock}; - use tari_comms::{peer_manager::NodeIdentity, CommsBuilder}; - use tari_storage::{ - lmdb_store::{LMDBBuilder, LMDBError, LMDBStore}, - LMDBWrapper, - }; - - #[derive(Clone)] - struct AddWordService(Arc>, &'static str); - - impl Service for AddWordService { - fn get_name(&self) -> String { - "tick service".to_string() - } - - fn get_message_types(&self) -> Vec { - vec![NetMessage::PingPong.into()] - } - - fn execute(&mut self, context: ServiceContext) -> Result<(), ServiceError> { - let mut added_word = false; - loop { - if !added_word { - let mut lock = self.0.write().unwrap(); - *lock = format!("{} {}", *lock, self.1); - added_word = true; - } - if let Some(msg) = context.get_control_message(Duration::from_millis(1000)) { - match msg { - ServiceControlMessage::Shutdown => break, - } - } - } - - Ok(()) - } - } - - fn get_path(name: &str) -> String { - let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - path.push("tests/data"); - path.push(name); - path.to_str().unwrap().to_string() - } - - fn init_datastore(name: &str) -> Result { - let path = get_path(name); - let _ = std::fs::create_dir(&path).unwrap_or_default(); - LMDBBuilder::new() - .set_path(&path) - .set_environment_size(10) - .set_max_number_of_databases(2) - .add_database(name, lmdb_zero::db::CREATE) - .build() - } - - fn clean_up_datastore(name: &str) { - std::fs::remove_dir_all(get_path(name)).unwrap(); - } - - #[test] - fn execute() { - let node_identity = - NodeIdentity::random(&mut OsRng::new().unwrap(), "127.0.0.1:9000".parse().unwrap()).unwrap(); - - let state = Arc::new(RwLock::new("Hello".to_string())); - let service = AddWordService(state.clone(), "Tari"); - let registry = ServiceRegistry::new().register(service); - - let database_name = "executor_execute"; // Note: every test should have unique database - let datastore = init_datastore(database_name).unwrap(); - let peer_database = datastore.get_handle(database_name).unwrap(); - let peer_database = LMDBWrapper::new(Arc::new(peer_database)); - - let comms_services = CommsBuilder::new() - .with_node_identity(node_identity) - .with_peer_storage(peer_database) - .build() - .unwrap() - .start() - .map(Arc::new) - .unwrap(); - - let services = ServiceExecutor::execute(&comms_services, registry); - - services.shutdown().unwrap(); - services.join_timeout(Duration::from_millis(100)).unwrap(); - let comms = Arc::try_unwrap(comms_services) - .map_err(|_| ServiceError::CommsServiceOwnershipError) - .unwrap(); - - comms.shutdown().unwrap(); - - { - let lock = acquire_read_lock!(state); - assert_eq!(*lock, "Hello Tari"); - } - - clean_up_datastore(database_name); - } -} diff --git a/base_layer/p2p/src/sync_services/mod.rs b/base_layer/p2p/src/sync_services/mod.rs deleted file mode 100644 index 2e8668f354..0000000000 --- a/base_layer/p2p/src/sync_services/mod.rs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -mod error; -mod executor; -mod registry; -mod service; - -pub use self::{ - error::ServiceError, - executor::{ServiceContext, ServiceControlMessage, ServiceExecutor}, - registry::ServiceRegistry, - service::{Service, ServiceApiWrapper, DEFAULT_API_TIMEOUT_MS}, -}; diff --git a/base_layer/p2p/src/sync_services/registry.rs b/base_layer/p2p/src/sync_services/registry.rs deleted file mode 100644 index 5ce96d4d3f..0000000000 --- a/base_layer/p2p/src/sync_services/registry.rs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use super::service::Service; - -/// This is a container for services. Services can be registered here -/// and given to the [ServiceExecutor] for execution. This also -/// builds [CommsRoutes] for the given services. -#[derive(Default)] -pub struct ServiceRegistry { - pub(super) services: Vec>, -} - -impl ServiceRegistry { - /// Create a new [ServiceRegistry]. - pub fn new() -> Self { - Self { services: Vec::new() } - } - - /// Register a [Service] - pub fn register(mut self, service: S) -> Self - where S: Service + 'static { - self.services.push(Box::new(service)); - self - } - - /// Retrieves the number of services registered. - pub fn num_services(&self) -> usize { - self.services.len() - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::{ - sync_services::{ServiceContext, ServiceError}, - tari_message::{NetMessage, TariMessageType}, - }; - - struct DummyService; - - impl Service for DummyService { - fn get_name(&self) -> String { - "dummy".to_string() - } - - fn get_message_types(&self) -> Vec { - vec![NetMessage::PingPong.into()] - } - - fn execute(&mut self, _context: ServiceContext) -> Result<(), ServiceError> { - // do nothing - Ok(()) - } - } - - #[test] - fn num_services() { - let mut registry = ServiceRegistry::new(); - assert_eq!(registry.num_services(), 0); - let service = DummyService {}; - registry = registry.register(service); - assert_eq!(registry.num_services(), 1); - } -} diff --git a/base_layer/p2p/src/sync_services/service.rs b/base_layer/p2p/src/sync_services/service.rs deleted file mode 100644 index 34dc54c7b2..0000000000 --- a/base_layer/p2p/src/sync_services/service.rs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use super::executor::ServiceContext; -use crate::{sync_services::ServiceError, tari_message::TariMessageType}; -use crossbeam_channel as channel; -use std::{sync::Arc, time::Duration}; - -/// This trait should be implemented for services -pub trait Service: Send + Sync { - /// A 'friendly' name used for logging purposes - fn get_name(&self) -> String; - /// Returns the message types this service requires. These will be subscribed to. - fn get_message_types(&self) -> Vec; - /// The entry point of the service. This will be executed in a dedicated thread. - /// The service should use `context.create_connector(message_type)` to create a `DomainConnector` - /// for the registered message types returned from `Service::get_message_types`. - /// This should contain a loop which reads control messages (`context.get_control_message`) - /// and connector messages and processes them. - fn execute(&mut self, context: ServiceContext) -> Result<(), ServiceError>; -} - -/// Default duration that a API 'client' will wait for a response from the service before returning a timeout error -pub const DEFAULT_API_TIMEOUT_MS: u64 = 3000; - -/// Thin convenience wrapper for any service api -pub struct ServiceApiWrapper { - api: Arc, - receiver: channel::Receiver, - sender: channel::Sender, -} - -impl ServiceApiWrapper { - /// Create a new service API - pub fn new(receiver: channel::Receiver, sender: channel::Sender, api: Arc) -> Self { - Self { api, receiver, sender } - } - - /// Send a reply to the calling API - pub fn send_reply(&self, msg: Res) -> Result<(), channel::SendError> { - self.sender.send(msg) - } - - /// Attempt to receive a service API message - pub fn recv_timeout(&self, timeout: Duration) -> Result, channel::RecvTimeoutError> { - match self.receiver.recv_timeout(timeout) { - Ok(msg) => Ok(Some(msg)), - Err(channel::RecvTimeoutError::Timeout) => Ok(None), - Err(err) => Err(err), - } - } - - /// Return the API - pub fn get_api(&self) -> Arc { - self.api.clone() - } -} diff --git a/base_layer/p2p/src/tari_message.rs b/base_layer/p2p/src/tari_message.rs index a92193dbad..a305bccc6f 100644 --- a/base_layer/p2p/src/tari_message.rs +++ b/base_layer/p2p/src/tari_message.rs @@ -1,4 +1,4 @@ -// Copyright 2019. The Tari Project +// Copyright 2019, The Tari Project // // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the // following conditions are met: @@ -20,125 +20,12 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use serde::{Deserialize, Serialize}; +use tari_comms_dht::domain_message::ToProtoEnum; -/// Reduce repetitive boilerplate by defining a `is_xxx_message() -> bool` function for each class of message -macro_rules! is_type { - ($m:ident, $f:ident) => { - pub fn $f(&self) -> bool { - self.0 >= $m::START_RANGE && self.0 <= $m::END_RANGE - } - } -} - -/// A tari message type is an immutable 8-bit unsigned integer indicating the type of message being received or sent -/// over the network. Details are in -/// [RFC-0172](https://rfc.tari.com/RFC-0172_PeerToPeerMessagingProtocol.html#messagetype). -#[derive(Serialize, Deserialize, Eq, PartialEq, Hash, Clone, Debug)] -pub struct TariMessageType(u8); - -#[allow(non_snake_case, non_upper_case_globals)] -pub mod NetMessage { - pub(super) const START_RANGE: u8 = 1; - pub(super) const END_RANGE: u8 = 5; // Can be extended to 32 - /// DHT network join, and discovery of nodes and peers - pub const Join: u8 = 1; - pub const Discover: u8 = 2; - /// Message sent for PingPong and liveness checks - pub const PingPong: u8 = 3; - /// Messages sent for Store-and-forward functionality - pub const RetrieveMessages: u8 = 4; - pub const StoredMessages: u8 = 5; -} - -#[allow(non_snake_case, non_upper_case_globals)] -pub mod PeerMessage { - pub(super) const START_RANGE: u8 = 33; - pub(super) const END_RANGE: u8 = 33; // Can be extended to 64 - pub const Connect: u8 = 33; -} - -#[allow(non_snake_case, non_upper_case_globals)] -pub mod BlockchainMessage { - pub(super) const START_RANGE: u8 = 65; - pub(super) const END_RANGE: u8 = 67; // Can be extended to 96 - pub const NewBlock: u8 = 65; - pub const Transaction: u8 = 66; - pub const TransactionReply: u8 = 67; -} - -#[allow(non_snake_case, non_upper_case_globals)] -pub mod ValidatorNodeMessage { - pub(super) const START_RANGE: u8 = 97; - pub(super) const END_RANGE: u8 = 97; // Can be extended to 224 - pub const Instruction: u8 = 97; -} - -#[allow(non_snake_case, non_upper_case_globals)] -pub mod ExtendedMessage { - pub(super) const START_RANGE: u8 = 225; - pub(super) const END_RANGE: u8 = 226; // Can be extended to 255 - pub const Text: u8 = 225; - pub const TextAck: u8 = 226; -} - -impl TariMessageType { - is_type!(NetMessage, is_net_message); - - is_type!(PeerMessage, is_peer_message); - - is_type!(BlockchainMessage, is_blockchain_message); - - is_type!(ValidatorNodeMessage, is_vn_message); - - is_type!(ExtendedMessage, is_extended_message); - - pub fn new(value: u8) -> TariMessageType { - TariMessageType(value) - } - - pub fn value(&self) -> u8 { - self.0 - } - - pub fn is_known_message(&self) -> bool { - self.is_net_message() || self.is_peer_message() || self.is_blockchain_message() || self.is_vn_message() - } -} - -impl From for TariMessageType { - fn from(v: u8) -> Self { - TariMessageType::new(v) - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn message_type_definition() { - // When constructing messages, we want to use human-readable definitions as defined in RFC-0172 - let t = TariMessageType::new(PeerMessage::Connect); - assert_eq!(t.value(), 33); - let t = TariMessageType::new(ValidatorNodeMessage::Instruction); - assert_eq!(t.value(), 97); - let t = TariMessageType::new(BlockchainMessage::NewBlock); - assert_eq!(t.value(), 65); - } - - #[test] - fn create_message() { - // When reading from the wire, the message type will be a byte value - let t = TariMessageType::from(2); - assert_eq!(t.value(), NetMessage::Discover); - assert!(t.is_net_message()); - assert!(t.is_known_message()); - } +pub use crate::proto::message_type::TariMessageType; - #[test] - fn unknown_message_type() { - let t = TariMessageType::from(30); - assert_eq!(t.is_known_message(), false); +impl ToProtoEnum for TariMessageType { + fn as_i32(&self) -> i32 { + *self as i32 } } diff --git a/base_layer/p2p/src/test_utils.rs b/base_layer/p2p/src/test_utils.rs new file mode 100644 index 0000000000..3a56b69a64 --- /dev/null +++ b/base_layer/p2p/src/test_utils.rs @@ -0,0 +1,98 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use rand::rngs::OsRng; +use std::sync::Arc; +use tari_comms::{ + multiaddr::Multiaddr, + peer_manager::{NodeIdentity, Peer, PeerFeatures, PeerFlags}, + utils::signature, +}; +use tari_comms_dht::{ + envelope::{DhtMessageFlags, DhtMessageHeader, DhtMessageOrigin, DhtMessageType, Network, NodeDestination}, + inbound::DhtInboundMessage, +}; +use tari_crypto::tari_utilities::message_format::MessageFormat; + +macro_rules! unwrap_oms_send_msg { + ($var:expr, reply_value=$reply_value:expr) => { + match $var { + tari_comms_dht::outbound::DhtOutboundRequest::SendMessage(boxed, body, reply_tx) => { + let _ = reply_tx.send($reply_value); + (*boxed, body) + }, + } + }; + ($var:expr) => { + unwrap_oms_send_msg!( + $var, + reply_value = tari_comms_dht::outbound::SendMessageResponse::Queued(vec![]) + ); + }; +} + +pub fn make_node_identity() -> Arc { + Arc::new( + NodeIdentity::random( + &mut OsRng, + "/ip4/127.0.0.1/tcp/9000".parse().unwrap(), + PeerFeatures::COMMUNICATION_NODE, + ) + .unwrap(), + ) +} + +pub fn make_dht_header(node_identity: &NodeIdentity, message: &Vec, flags: DhtMessageFlags) -> DhtMessageHeader { + DhtMessageHeader { + version: 0, + destination: NodeDestination::Unknown, + origin: Some(DhtMessageOrigin { + public_key: node_identity.public_key().clone(), + signature: signature::sign(&mut OsRng, node_identity.secret_key().clone(), message) + .unwrap() + .to_binary() + .unwrap(), + }), + message_type: DhtMessageType::None, + network: Network::LocalTest, + flags, + } +} + +pub fn make_dht_inbound_message( + node_identity: &NodeIdentity, + message: Vec, + flags: DhtMessageFlags, +) -> DhtInboundMessage +{ + DhtInboundMessage::new( + make_dht_header(node_identity, &message, flags), + Arc::new(Peer::new( + node_identity.public_key().clone(), + node_identity.node_id().clone(), + Vec::::new().into(), + PeerFlags::empty(), + PeerFeatures::COMMUNICATION_NODE, + )), + message, + ) +} diff --git a/base_layer/p2p/src/transport.rs b/base_layer/p2p/src/transport.rs new file mode 100644 index 0000000000..85217c26e0 --- /dev/null +++ b/base_layer/p2p/src/transport.rs @@ -0,0 +1,55 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use tari_comms::{multiaddr::Multiaddr, socks, tor}; + +#[derive(Debug, Clone)] +pub enum TransportType { + /// Use a memory transport. This transport recognises /memory addresses primarily used for local testing. + Memory { listener_address: Multiaddr }, + /// Use a TcpTransport. This transport recognises /ip{4|6}/.../tcp/xxxx addresses. + Tcp { listener_address: Multiaddr }, + /// This does not directly map to a transport, but will configure comms to run over a tor hidden service using the + /// Tor proxy. This transport recognises ip/tcp, onion v2, onion v3 and dns addresses. + Tor(TorConfig), + /// Use a SOCKS5 proxy transport. This transport recognises ip/tcp and dns addresses. + Socks { + proxy_address: Multiaddr, + listener_address: Multiaddr, + authentication: socks::Authentication, + }, +} + +#[derive(Debug, Clone)] +pub struct TorConfig { + /// The Tor control server address + pub control_server_addr: Multiaddr, + /// Authentication for the Tor control server + pub control_server_auth: tor::Authentication, + /// The private key and service ID for the Tor hidden service. If not supplied, a new address and private key will + /// be generated + pub identity: Option>, + /// The onion -> local address mapping to use. + pub port_mapping: tor::PortMapping, + /// Authentication for the Tor SOCKS5 proxy + pub socks_auth: socks::Authentication, +} diff --git a/base_layer/p2p/tests/dht/mod.rs b/base_layer/p2p/tests/dht/mod.rs deleted file mode 100644 index b08895cba7..0000000000 --- a/base_layer/p2p/tests/dht/mod.rs +++ /dev/null @@ -1,311 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// NOTE: These tests use ports 11113 to 11122 -use crate::support::random_string; -use rand::rngs::OsRng; -use std::{sync::Arc, thread, time::Duration}; -use tari_comms::{ - builder::CommsServices, - connection::NetAddress, - connection_manager::PeerConnectionConfig, - control_service::ControlServiceConfig, - message::NodeDestination, - peer_manager::{peer_storage::PeerStorage, NodeIdentity, Peer, PeerManager}, - types::CommsDatabase, - CommsBuilder, -}; -use tari_p2p::{ - dht_service::{DHTService, DHTServiceApi}, - sync_services::{ServiceExecutor, ServiceRegistry}, - tari_message::TariMessageType, -}; -use tari_storage::{LMDBWrapper, lmdb_store::LMDBBuilder}; -use tempdir::TempDir; - -fn new_node_identity(control_service_address: NetAddress) -> NodeIdentity { - NodeIdentity::random(&mut OsRng::new().unwrap(), control_service_address).unwrap() -} - -fn create_peer_storage(tmpdir: &TempDir, database_name: &str, peers: Vec) -> CommsDatabase { - let datastore = LMDBBuilder::new() - .set_path(tmpdir.path().to_str().unwrap()) - .set_environment_size(10) - .set_max_number_of_databases(1) - .add_database(database_name, lmdb_zero::db::CREATE) - .build() - .unwrap(); - - let peer_database = datastore.get_handle(database_name).unwrap(); - let peer_database = LMDBWrapper::new(Arc::new(peer_database)); - let mut storage = PeerStorage::new(peer_database).unwrap(); - for peer in peers { - storage.add_peer(peer).unwrap(); - } - - storage.into_datastore() -} - -fn setup_dht_service( - node_identity: NodeIdentity, - peer_storage: CommsDatabase, -) -> (ServiceExecutor, Arc, Arc>) -{ - let control_service_address = node_identity.control_service_address().unwrap(); - let dht_service = DHTService::new(); - let dht_api = dht_service.get_api(); - - let services = ServiceRegistry::new().register(dht_service); - let comms = CommsBuilder::new() - .with_routes(services.build_comms_routes()) - .with_node_identity(node_identity) - .with_peer_storage(peer_storage) - .configure_peer_connections(PeerConnectionConfig { - host: "127.0.0.1".parse().unwrap(), - ..Default::default() - }) - .configure_control_service(ControlServiceConfig { - socks_proxy_address: None, - listener_address: control_service_address, - requested_connection_timeout: Duration::from_millis(5000), - }) - .build() - .unwrap() - .start() - .map(Arc::new) - .unwrap(); - - (ServiceExecutor::execute(&comms, services), dht_api, comms) -} - -fn pause() { - thread::sleep(Duration::from_millis(3000)); -} - -#[test] -#[allow(non_snake_case)] -fn test_dht_join_propagation() { - // Create 3 nodes where only Node B knows A and C, but A and C want to talk to each other - let node_A_identity = new_node_identity("127.0.0.1:11113".parse().unwrap()); - let node_B_identity = new_node_identity("127.0.0.1:11114".parse().unwrap()); - let node_C_identity = new_node_identity("127.0.0.1:11115".parse().unwrap()); - - // Setup Node A - let node_A_tmpdir = TempDir::new(random_string(8).as_str()).unwrap(); - let node_A_database_name = "node_A"; - let (node_A_services, node_A_dht_service_api, _comms_A) = setup_dht_service( - node_A_identity.clone(), - create_peer_storage(&node_A_tmpdir, node_A_database_name, vec![node_B_identity - .clone() - .into()]), - ); - // Setup Node B - let node_B_tmpdir = TempDir::new(random_string(8).as_str()).unwrap(); - let node_B_database_name = "node_B"; - let (node_B_services, _node_B_dht_service_api, _comms_B) = setup_dht_service( - node_B_identity.clone(), - create_peer_storage(&node_B_tmpdir, node_B_database_name, vec![ - node_A_identity.clone().into(), - node_C_identity.clone().into(), - ]), - ); - // Setup Node C - let node_C_tmpdir = TempDir::new(random_string(8).as_str()).unwrap(); - let node_C_database_name = "node_C"; - let (node_C_services, _node_C_dht_service_api, _comms_C) = setup_dht_service( - node_C_identity.clone(), - create_peer_storage(&node_C_tmpdir, node_C_database_name, vec![node_B_identity - .clone() - .into()]), - ); - - // Send a join request from Node A, through B to C. As all Nodes are in the same network region, once Node C - // receives the join request from Node A, it will send a direct join request back to A. - pause(); - assert!(node_A_dht_service_api.send_join().is_ok()); - - pause(); - node_A_services.shutdown().unwrap(); - node_B_services.shutdown().unwrap(); - node_C_services.shutdown().unwrap(); - - // Restore PeerStorage of Node A and Node C and check that they are aware of each other - pause(); - let node_A_peer_manager = - PeerManager::new(create_peer_storage(&node_A_tmpdir, node_A_database_name, vec![])).unwrap(); - let node_C_peer_manager = - PeerManager::new(create_peer_storage(&node_C_tmpdir, node_C_database_name, vec![])).unwrap(); - assert!(node_C_peer_manager - .exists(&node_A_identity.identity.public_key) - .unwrap()); - assert!(node_A_peer_manager - .exists(&node_C_identity.identity.public_key) - .unwrap()); -} - -#[test] -#[allow(non_snake_case)] -fn test_dht_discover_propagation() { - // Create 3 nodes where only Node B knows A and C, but A and C want to talk to each other - let node_A_identity = new_node_identity("127.0.0.1:11116".parse().unwrap()); - let node_B_identity = new_node_identity("127.0.0.1:11117".parse().unwrap()); - let node_C_identity = new_node_identity("127.0.0.1:11118".parse().unwrap()); - let node_D_identity = new_node_identity("127.0.0.1:11119".parse().unwrap()); - - // Setup Node A - let node_A_tmpdir = TempDir::new(random_string(8).as_str()).unwrap(); - let node_A_database_name = "node_A"; - let (node_A_services, node_A_dht_service_api, _comms_A) = setup_dht_service( - node_A_identity.clone(), - create_peer_storage(&node_A_tmpdir, node_A_database_name, vec![node_B_identity - .clone() - .into()]), - ); - // Setup Node B - let node_B_tmpdir = TempDir::new(random_string(8).as_str()).unwrap(); - let node_B_database_name = "node_B"; - let (node_B_services, _node_B_dht_service_api, _comms_B) = setup_dht_service( - node_B_identity.clone(), - create_peer_storage(&node_B_tmpdir, node_B_database_name, vec![ - node_A_identity.clone().into(), - node_C_identity.clone().into(), - ]), - ); - // Setup Node C - let node_C_tmpdir = TempDir::new(random_string(8).as_str()).unwrap(); - let node_C_database_name = "node_C"; - let (node_C_services, _node_C_dht_service_api, _comms_C) = setup_dht_service( - node_C_identity.clone(), - create_peer_storage(&node_C_tmpdir, node_C_database_name, vec![ - node_B_identity.clone().into(), - node_D_identity.clone().into(), - ]), - ); - // Setup Node D - let node_D_tmpdir = TempDir::new(random_string(8).as_str()).unwrap(); - let node_D_database_name = "node_D"; - let (node_D_services, _node_D_dht_service_api, _comms_D) = setup_dht_service( - node_D_identity.clone(), - create_peer_storage(&node_D_tmpdir, node_D_database_name, vec![node_C_identity - .clone() - .into()]), - ); - - // Send a discover request from Node A, through B and C, to D. Once Node D - // receives the discover request from Node A, it will send a direct join request back to A. - pause(); - assert!(node_A_dht_service_api - .send_discover( - node_D_identity.identity.public_key.clone(), - None, - NodeDestination::Unknown - ) - .is_ok()); - - pause(); - node_A_services.shutdown().unwrap(); - node_B_services.shutdown().unwrap(); - node_C_services.shutdown().unwrap(); - node_D_services.shutdown().unwrap(); - - // Restore PeerStorage of Node A and Node D and check that they are aware of each other - pause(); - let node_A_peer_manager = - PeerManager::new(create_peer_storage(&node_A_tmpdir, node_A_database_name, vec![])).unwrap(); - let node_D_peer_manager = - PeerManager::new(create_peer_storage(&node_D_tmpdir, node_D_database_name, vec![])).unwrap(); - assert!(node_A_peer_manager - .exists(&node_D_identity.identity.public_key) - .unwrap()); - assert!(node_D_peer_manager - .exists(&node_A_identity.identity.public_key) - .unwrap()); -} - -#[test] -#[allow(non_snake_case)] -fn test_dht_join_on_service_start() { - // Create 2 nodes where Node A has a old control_service_address for Node B - let node_A_identity = new_node_identity("127.0.0.1:11120".parse().unwrap()); - let outdated_node_B_identity = new_node_identity("127.0.0.1:11121".parse().unwrap()); - let node_B_identity = outdated_node_B_identity.clone(); // new_node_identity("127.0.0.1:11122".parse().unwrap()); - node_B_identity - .set_control_service_address("127.0.0.1:11121".parse().unwrap()) - .unwrap(); - - // Setup Node A - let node_A_tmpdir = TempDir::new(random_string(8).as_str()).unwrap(); - let node_A_database_name = "node_A"; - let (node_A_services, _node_A_dht_service_api, comms_A) = setup_dht_service( - node_A_identity.clone(), - create_peer_storage(&node_A_tmpdir, node_A_database_name, vec![outdated_node_B_identity - .clone() - .into()]), - ); - // Wait for Node A to startup - pause(); - - // Setup Node B - let node_B_tmpdir = TempDir::new(random_string(8).as_str()).unwrap(); - let node_B_database_name = "node_B"; - let (node_B_services, _node_B_dht_service_api, _comms_B) = setup_dht_service( - node_B_identity.clone(), - create_peer_storage(&node_B_tmpdir, node_B_database_name, vec![node_A_identity - .clone() - .into()]), - ); - // Node A and B are aware of each other on startup but Node A has outdated information for Node B - // As Node B comes online it will send a join request to all its neighbouring peers - - pause(); - // The NetAddress of Node A has changed, the DHT Service will detect the change and inform the neighbouring peers - // using a join request - let node_A_updated_net_address: NetAddress = "127.0.0.1:11122".parse().unwrap(); - comms_A - .node_identity() - .set_control_service_address(node_A_updated_net_address.clone()) - .unwrap(); - - pause(); - node_A_services.shutdown().unwrap(); - node_B_services.shutdown().unwrap(); - - // Restore PeerStorage of Node A and B - pause(); - let node_A_peer_manager = - PeerManager::new(create_peer_storage(&node_A_tmpdir, node_A_database_name, vec![])).unwrap(); - let node_B_peer_manager = - PeerManager::new(create_peer_storage(&node_B_tmpdir, node_B_database_name, vec![])).unwrap(); - // Check that Node A is aware of the updated NetAddress of Node B - let mut peer = node_A_peer_manager - .find_with_public_key(&node_B_identity.identity.public_key) - .unwrap(); - assert!(peer - .addresses - .find_address_mut(&node_B_identity.control_service_address().unwrap()) - .is_ok()); - // Check that Node B is aware of the updated NetAddress of Node A - let mut peer = node_B_peer_manager - .find_with_public_key(&node_A_identity.identity.public_key) - .unwrap(); - assert!(peer.addresses.find_address_mut(&node_A_updated_net_address).is_ok()); -} diff --git a/base_layer/p2p/tests/mod.rs b/base_layer/p2p/tests/mod.rs index 6c1867d607..2bee3cd91c 100644 --- a/base_layer/p2p/tests/mod.rs +++ b/base_layer/p2p/tests/mod.rs @@ -20,8 +20,5 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// TODO Put this back in after Futures Comms stack refactor -// mod dht; -// mod saf; -mod ping_pong; +mod services; mod support; diff --git a/base_layer/p2p/tests/ping_pong/mod.rs b/base_layer/p2p/tests/ping_pong/mod.rs deleted file mode 100644 index d8d9034bef..0000000000 --- a/base_layer/p2p/tests/ping_pong/mod.rs +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// NOTE: This test uses ports 11111 and 11112 - -use crate::support::{assert_change, random_string}; -use rand::rngs::OsRng; -use std::{sync::Arc, time::Duration}; -use tari_comms::{ - builder::CommsServices, - connection::NetAddress, - connection_manager::PeerConnectionConfig, - control_service::ControlServiceConfig, - peer_manager::{peer_storage::PeerStorage, NodeIdentity, Peer}, - types::CommsDatabase, - CommsBuilder, -}; -use tari_p2p::{ - ping_pong::{PingPongService, PingPongServiceApi}, - sync_services::{ServiceExecutor, ServiceRegistry}, - tari_message::TariMessageType, -}; -use tari_storage::{lmdb_store::LMDBBuilder, LMDBWrapper}; -use tempdir::TempDir; - -fn new_node_identity(control_service_address: NetAddress) -> NodeIdentity { - NodeIdentity::random(&mut OsRng::new().unwrap(), control_service_address).unwrap() -} - -fn create_peer_storage(tmpdir: &TempDir, database_name: &str, peers: Vec) -> CommsDatabase { - let datastore = LMDBBuilder::new() - .set_path(tmpdir.path().to_str().unwrap()) - .set_environment_size(10) - .set_max_number_of_databases(1) - .add_database(database_name, lmdb_zero::db::CREATE) - .build() - .unwrap(); - - let peer_database = datastore.get_handle(database_name).unwrap(); - let peer_database = LMDBWrapper::new(Arc::new(peer_database)); - let mut storage = PeerStorage::new(peer_database).unwrap(); - for peer in peers { - storage.add_peer(peer).unwrap(); - } - - storage.into_datastore() -} - -fn setup_ping_pong_service( - node_identity: NodeIdentity, - peer_storage: CommsDatabase, -) -> ( - ServiceExecutor, - Arc, - Arc>, -) -{ - let ping_pong = PingPongService::new(); - let pingpong_api = ping_pong.get_api(); - - let services = ServiceRegistry::new().register(ping_pong); - let comms = CommsBuilder::new() - .with_node_identity(node_identity.clone()) - .with_peer_storage(peer_storage) - .configure_peer_connections(PeerConnectionConfig { - host: "127.0.0.1".parse().unwrap(), - ..Default::default() - }) - .configure_control_service(ControlServiceConfig { - socks_proxy_address: None, - listener_address: node_identity.control_service_address().unwrap(), - requested_connection_timeout: Duration::from_millis(5000), - }) - .build() - .unwrap() - .start() - .map(Arc::new) - .unwrap(); - - (ServiceExecutor::execute(&comms, services), pingpong_api, comms) -} - -#[test] -#[allow(non_snake_case)] -fn end_to_end() { - let node_A_tmpdir = TempDir::new(random_string(8).as_str()).unwrap(); - - let node_B_tmpdir = TempDir::new(random_string(8).as_str()).unwrap(); - - let node_A_identity = new_node_identity("127.0.0.1:11111".parse().unwrap()); - let node_B_identity = new_node_identity("127.0.0.1:11112".parse().unwrap()); - - let (node_A_services, node_A_pingpong, _comms_A) = setup_ping_pong_service( - node_A_identity.clone(), - create_peer_storage(&node_A_tmpdir, "node_A", vec![node_B_identity.clone().into()]), - ); - - let (node_B_services, node_B_pingpong, _comms_B) = setup_ping_pong_service( - node_B_identity.clone(), - create_peer_storage(&node_B_tmpdir, "node_B", vec![node_A_identity.clone().into()]), - ); - - // Ping node B - node_A_pingpong - .ping(node_B_identity.identity.public_key.clone()) - .unwrap(); - - assert_change(|| node_B_pingpong.ping_count().unwrap(), 1, 20); - assert_change(|| node_A_pingpong.pong_count().unwrap(), 1, 20); - - // Ping node A - node_B_pingpong - .ping(node_A_identity.identity.public_key.clone()) - .unwrap(); - - assert_change(|| node_B_pingpong.pong_count().unwrap(), 1, 20); - assert_change(|| node_A_pingpong.ping_count().unwrap(), 1, 20); - - node_A_services.shutdown().unwrap(); - node_B_services.shutdown().unwrap(); -} diff --git a/base_layer/p2p/tests/saf/mod.rs b/base_layer/p2p/tests/saf/mod.rs deleted file mode 100644 index f258b19c51..0000000000 --- a/base_layer/p2p/tests/saf/mod.rs +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// NOTE: These tests use ports 11123 to 11125 -use crate::support::random_string; -use rand::rngs::OsRng; -use std::{convert::TryInto, sync::Arc, thread, time::Duration}; -use tari_comms::{ - builder::CommsServices, - connection::NetAddress, - connection_manager::PeerConnectionConfig, - control_service::ControlServiceConfig, - message::{Frame, Message, MessageEnvelope, MessageFlags, NodeDestination}, - peer_manager::{peer_storage::PeerStorage, NodeIdentity, Peer, PeerManager}, - types::CommsDatabase, - CommsBuilder, -}; -use tari_p2p::{ - dht_service::{DHTService, DHTServiceApi, DiscoverMessage}, - saf_service::{SAFService, SAFServiceApi}, - sync_services::{ServiceExecutor, ServiceRegistry}, - tari_message::TariMessageType, -}; -use tari_storage::{LMDBWrapper, lmdb_store::LMDBBuilder}; -use tari_utilities::message_format::MessageFormat; -use tempdir::TempDir; - -fn new_node_identity(control_service_address: NetAddress) -> NodeIdentity { - NodeIdentity::random(&mut OsRng::new().unwrap(), control_service_address).unwrap() -} - -fn create_peer_storage(tmpdir: &TempDir, database_name: &str, peers: Vec) -> CommsDatabase { - let datastore = LMDBBuilder::new() - .set_path(tmpdir.path().to_str().unwrap()) - .set_environment_size(10) - .set_max_number_of_databases(1) - .add_database(database_name, lmdb_zero::db::CREATE) - .build() - .unwrap(); - - let peer_database = datastore.get_handle(database_name).unwrap(); - let peer_database = LMDBWrapper::new(Arc::new(peer_database)); - let mut storage = PeerStorage::new(peer_database).unwrap(); - for peer in peers { - storage.add_peer(peer).unwrap(); - } - - storage.into_datastore() -} - -fn setup_services( - node_identity: NodeIdentity, - peer_storage: CommsDatabase, -) -> ( - ServiceExecutor, - Arc, - Arc, - Arc>, -) -{ - let control_service_address = node_identity.control_service_address().unwrap(); - let saf_service = SAFService::new(); - let saf_api = saf_service.get_api(); - let dht_service = DHTService::new(); - let dht_api = dht_service.get_api(); - - let services = ServiceRegistry::new().register(saf_service).register(dht_service); - let comms = CommsBuilder::new() - .with_routes(services.build_comms_routes()) - .with_node_identity(node_identity) - .with_peer_storage(peer_storage) - .configure_peer_connections(PeerConnectionConfig { - host: "127.0.0.1".parse().unwrap(), - ..Default::default() - }) - .configure_control_service(ControlServiceConfig { - socks_proxy_address: None, - listener_address: control_service_address, - requested_connection_timeout: Duration::from_millis(5000), - }) - .build() - .unwrap() - .start() - .map(Arc::new) - .unwrap(); - - (ServiceExecutor::execute(&comms, services), saf_api, dht_api, comms) -} - -fn pause() { - thread::sleep(Duration::from_millis(3000)); -} - -#[test] -#[allow(non_snake_case)] -fn test_saf_store() { - // Create 3 nodes where only Node B knows A and C, but A wants to talk to Node C. Node A and C are not online at the - // same time. - let node_A_identity = new_node_identity("127.0.0.1:11123".parse().unwrap()); - let node_B_identity = new_node_identity("127.0.0.1:11124".parse().unwrap()); - let node_C_identity = new_node_identity("127.0.0.1:11125".parse().unwrap()); - - // Setup Node B - let node_B_tmpdir = TempDir::new(random_string(8).as_str()).unwrap(); - let node_B_database_name = "node_B"; - let (node_B_services, node_B_saf_service_api, _node_B_dht_service_api, _comms_B) = setup_services( - node_B_identity.clone(), - create_peer_storage(&node_B_tmpdir, node_B_database_name, vec![ - node_A_identity.clone().into(), - node_C_identity.clone().into(), - ]), - ); - - // Node A is sending a discovery message to Node C through Node B, but Node C is offline - // TODO: The comms stack of Node B should automatically store the forwarded message, this is not yet implemented. - // This storage behaviour is mimicked by manually storing the message from Node A in Node B. - let discover_msg: Message = DiscoverMessage { - node_id: node_A_identity.identity.node_id.clone(), - net_address: vec![node_A_identity.control_service_address().unwrap()], - } - .try_into() - .unwrap(); - let message_envelope_body: Frame = discover_msg.to_binary().unwrap(); - let message_envelope = MessageEnvelope::construct( - &node_A_identity, - node_C_identity.identity.public_key.clone(), - NodeDestination::Unknown, - message_envelope_body.clone(), - MessageFlags::ENCRYPTED, - ) - .unwrap(); - node_B_saf_service_api.store(message_envelope).unwrap(); // This should happen automatically when node B tries to forward the message - - pause(); - - // Node C comes online - let node_C_tmpdir = TempDir::new(random_string(8).as_str()).unwrap(); - let node_C_database_name = "node_C"; - let (node_C_services, node_C_saf_service_api, _node_C_dht_service_api, _comms_C) = setup_services( - node_C_identity.clone(), - create_peer_storage(&node_C_tmpdir, node_C_database_name, vec![node_B_identity - .clone() - .into()]), - ); - // Retrieve messages from Node B - node_C_saf_service_api.retrieve(None).unwrap(); - - pause(); - node_B_services.shutdown().unwrap(); - node_C_services.shutdown().unwrap(); - - // Restore PeerStorage of Node C and check that it is aware of Node A - pause(); - let node_C_peer_manager = - PeerManager::new(create_peer_storage(&node_C_tmpdir, node_C_database_name, vec![])).unwrap(); - assert!(node_C_peer_manager - .exists(&node_A_identity.identity.public_key) - .unwrap()); -} diff --git a/base_layer/p2p/tests/services/liveness.rs b/base_layer/p2p/tests/services/liveness.rs index 0b40f1da34..a413801313 100644 --- a/base_layer/p2p/tests/services/liveness.rs +++ b/base_layer/p2p/tests/services/liveness.rs @@ -20,77 +20,169 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::support::TestCommsOutboundInitializer; -use futures::Sink; +use crate::support::comms_and_services::setup_comms_services; use rand::rngs::OsRng; -use std::{ - sync::{mpsc, Arc}, - time::Duration, -}; +use std::{sync::Arc, time::Duration}; use tari_comms::{ - message::{DomainMessageContext, Message, MessageHeader}, - peer_manager::{NodeId, PeerNodeIdentity}, - pub_sub_channel::{pubsub_channel, TopicPayload}, - types::CommsPublicKey, + peer_manager::{NodeIdentity, PeerFeatures}, + transports::MemoryTransport, + CommsNode, }; -use tari_crypto::keys::PublicKey; +use tari_comms_dht::Dht; use tari_p2p::{ - executor::StackBuilder, + comms_connector::pubsub_connector, services::{ - comms_outbound::CommsOutboundRequest, - liveness::{LivenessHandle, LivenessInitializer, LivenessRequest, LivenessResponse, PingPong}, - ServiceName, + comms_outbound::CommsOutboundServiceInitializer, + liveness::{LivenessConfig, LivenessEvent, LivenessHandle, LivenessInitializer}, }, - tari_message::{NetMessage, TariMessageType}, }; -use tari_utilities::message_format::MessageFormat; -use tokio::runtime::Runtime; -use tower_service::Service; - -fn create_domain_message(message_type: TariMessageType, inner_msg: T) -> DomainMessageContext { - let mut rng = OsRng::new().unwrap(); - let (_, pk) = CommsPublicKey::random_keypair(&mut rng); - let peer_source = PeerNodeIdentity::new(NodeId::from_key(&pk).unwrap(), pk.clone()); - let header = MessageHeader::new(message_type).unwrap(); - let msg = Message::from_message_format(header, inner_msg).unwrap(); - DomainMessageContext::new(peer_source, pk, msg) +use tari_service_framework::StackBuilder; +use tari_test_utils::{collect_stream, random::string}; +use tempdir::TempDir; +use tokio::runtime; + +pub async fn setup_liveness_service( + node_identity: Arc, + peers: Vec>, + data_path: &str, +) -> (LivenessHandle, CommsNode, Dht) +{ + let rt_handle = runtime::Handle::current(); + let (publisher, subscription_factory) = pubsub_connector(rt_handle.clone(), 100); + let subscription_factory = Arc::new(subscription_factory); + let (comms, dht) = setup_comms_services(node_identity.clone(), peers, publisher, data_path).await; + + let handles = StackBuilder::new(rt_handle.clone(), comms.shutdown_signal()) + .add_initializer(CommsOutboundServiceInitializer::new(dht.outbound_requester())) + .add_initializer(LivenessInitializer::new( + LivenessConfig { + enable_auto_join: false, + enable_auto_stored_message_request: false, + auto_ping_interval: None, + refresh_neighbours_interval: Duration::from_secs(60), + }, + Arc::clone(&subscription_factory), + dht.dht_requester(), + )) + .finish() + .await + .expect("Service initialization failed"); + + let liveness_handle = handles.get_handle::().unwrap(); + + (liveness_handle, comms, dht) +} + +fn make_node_identity() -> Arc { + let next_port = MemoryTransport::acquire_next_memsocket_port(); + Arc::new( + NodeIdentity::random( + &mut OsRng, + format!("/memory/{}", next_port).parse().unwrap(), + PeerFeatures::COMMUNICATION_NODE, + ) + .unwrap(), + ) } -/// Receive a Ping message and query the PingCount -#[test] -fn send_ping_query_count() { - let mut rt = Runtime::new().unwrap(); - let (mut publisher, subscriber) = pubsub_channel(2); - let (tx, rx) = mpsc::channel(); - - // Setup the stack - let stack = StackBuilder::new() - .add_initializer(LivenessInitializer::from_inbound_message_subscriber(Arc::new( - subscriber, - ))) - .add_initializer(TestCommsOutboundInitializer::new(tx)); - let handles = rt.block_on(stack.finish()).unwrap(); - - // Publish a Ping message - let msg = create_domain_message(TariMessageType::new(NetMessage::PingPong), PingPong::Ping); - let payload = TopicPayload::new(TariMessageType::new(NetMessage::PingPong), msg); - assert!(publisher.start_send(payload).unwrap().is_ready()); - - // Check that the CommsOutbound service received a SendMsg request - let outbound_req = rx.recv_timeout(Duration::from_millis(100)).unwrap(); - match outbound_req { - CommsOutboundRequest::SendMsg { .. } => {}, - _ => panic!("Unexpected request sent to comms outbound service"), +#[tokio_macros::test_basic] +async fn end_to_end() { + let node_1_identity = make_node_identity(); + let node_2_identity = make_node_identity(); + + let alice_temp_dir = TempDir::new(string(8).as_str()).unwrap(); + let (mut liveness1, comms_1, _dht_1) = setup_liveness_service( + node_1_identity.clone(), + vec![node_2_identity.clone()], + alice_temp_dir.path().to_str().unwrap(), + ) + .await; + let bob_temp_dir = TempDir::new(string(8).as_str()).unwrap(); + let (mut liveness2, comms_2, _dht_2) = setup_liveness_service( + node_2_identity.clone(), + vec![node_1_identity.clone()], + bob_temp_dir.path().to_str().unwrap(), + ) + .await; + + for _ in 0..5 { + liveness2.send_ping(node_1_identity.node_id().clone()).await.unwrap(); + } + + for _ in 0..4 { + liveness1.send_ping(node_2_identity.node_id().clone()).await.unwrap(); } - // Query the ping count using the Liveness service handle - let mut liveness_handle = handles.get_handle::(ServiceName::Liveness).unwrap(); - let resp = rt - .block_on(liveness_handle.call(LivenessRequest::GetPingCount)) - .unwrap(); + for _ in 0..5 { + liveness2.send_ping(node_1_identity.node_id().clone()).await.unwrap(); + } - match resp.unwrap() { - LivenessResponse::Count(n) => assert_eq!(n, 1), - _ => panic!("unexpected response from liveness service"), + for _ in 0..4 { + liveness1.send_ping(node_2_identity.node_id().clone()).await.unwrap(); } + + let events = collect_stream!( + liveness1.get_event_stream_fused(), + take = 18, + timeout = Duration::from_secs(20), + ); + + let ping_count = events + .iter() + .filter(|event| match ***event { + LivenessEvent::ReceivedPing => true, + _ => false, + }) + .count(); + + assert_eq!(ping_count, 10); + + let pong_count = events + .iter() + .filter(|event| match ***event { + LivenessEvent::ReceivedPong(_) => true, + _ => false, + }) + .count(); + + assert_eq!(pong_count, 8); + + let events = collect_stream!( + liveness2.get_event_stream_fused(), + take = 18, + timeout = Duration::from_secs(10), + ); + + let ping_count = events + .iter() + .filter(|event| match ***event { + LivenessEvent::ReceivedPing => true, + _ => false, + }) + .count(); + + assert_eq!(ping_count, 8); + + let pong_count = events + .iter() + .filter(|event| match ***event { + LivenessEvent::ReceivedPong(_) => true, + _ => false, + }) + .count(); + + assert_eq!(pong_count, 10); + + let pingcount1 = liveness1.get_ping_count().await.unwrap(); + let pongcount1 = liveness1.get_pong_count().await.unwrap(); + let pingcount2 = liveness2.get_ping_count().await.unwrap(); + let pongcount2 = liveness2.get_pong_count().await.unwrap(); + + assert_eq!(pingcount1, 10); + assert_eq!(pongcount1, 8); + assert_eq!(pingcount2, 8); + assert_eq!(pongcount2, 10); + + comms_1.shutdown().await; + comms_2.shutdown().await; } diff --git a/base_layer/p2p/tests/support/comms_and_services.rs b/base_layer/p2p/tests/support/comms_and_services.rs new file mode 100644 index 0000000000..3f6ed97e97 --- /dev/null +++ b/base_layer/p2p/tests/support/comms_and_services.rs @@ -0,0 +1,66 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use futures::Sink; +use std::{error::Error, sync::Arc, time::Duration}; +use tari_comms::{ + peer_manager::{NodeIdentity, Peer, PeerFlags}, + CommsNode, +}; +use tari_comms_dht::Dht; +use tari_p2p::{ + comms_connector::{InboundDomainConnector, PeerMessage}, + initialization::initialize_local_test_comms, +}; + +pub async fn setup_comms_services( + node_identity: Arc, + peers: Vec>, + publisher: InboundDomainConnector, + data_path: &str, +) -> (CommsNode, Dht) +where + TSink: Sink> + Clone + Unpin + Send + Sync + 'static, + TSink::Error: Error + Send + Sync, +{ + let (comms, dht) = initialize_local_test_comms(node_identity, publisher, data_path, Duration::from_secs(1)) + .await + .unwrap(); + + let peer_manager = comms.async_peer_manager(); + + for p in peers { + let addr = p.public_address().clone(); + peer_manager + .add_peer(Peer::new( + p.public_key().clone(), + p.node_id().clone(), + addr.into(), + PeerFlags::default(), + p.features().clone(), + )) + .await + .unwrap(); + } + + (comms, dht) +} diff --git a/base_layer/p2p/tests/support/mod.rs b/base_layer/p2p/tests/support/mod.rs index 24f21cd025..936966a873 100644 --- a/base_layer/p2p/tests/support/mod.rs +++ b/base_layer/p2p/tests/support/mod.rs @@ -20,36 +20,4 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use rand::{distributions::Alphanumeric, rngs::OsRng, Rng}; -use std::{fmt::Debug, iter, thread, time::Duration}; - -pub fn random_string(len: usize) -> String { - let mut rng = OsRng::new().unwrap(); - iter::repeat(()).map(|_| rng.sample(Alphanumeric)).take(len).collect() -} - -pub fn assert_change(func: F, to: T, poll_count: usize) -where - F: Fn() -> T, - T: Eq + Debug, -{ - let mut i = 0; - loop { - let new_val = func(); - if new_val == to { - break; - } - - i += 1; - if i >= poll_count { - panic!( - "Value did not change to {:?} within {}ms (last value: {:?})", - to, - poll_count * 100, - new_val, - ); - } - - thread::sleep(Duration::from_millis(100)); - } -} +pub mod comms_and_services; diff --git a/base_layer/service_framework/Cargo.toml b/base_layer/service_framework/Cargo.toml index d74a50b7f0..22e0c99d76 100644 --- a/base_layer/service_framework/Cargo.toml +++ b/base_layer/service_framework/Cargo.toml @@ -1,18 +1,24 @@ [package] name = "tari_service_framework" -version = "0.0.5" +version = "0.0.9" authors = ["The Tari Development Community"] +description = "The Tari communication stack service framework" repository = "https://github.com/tari-project/tari" edition = "2018" +license = "BSD-3-Clause" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -futures = "0.1.28" -tower-service = "0.2.0" +tari_shutdown = { version = "^0.0", path="../../infrastructure/shutdown" } + derive-error = "0.0.4" +futures = { version = "^0.3.1", features=["async-await"]} +tower-service = { version="0.3.0" } +tokio = "0.2.10" +log = "0.4.8" [dev-dependencies] -tokio-mock-task = "0.1.1" -tower-util = "0.1.0" -tokio = "0.1.22" +tari_test_utils = { version = "^0.0", path="../../infrastructure/test_utils" } +futures-test = { version = "0.3.3" } +tower = "0.3.1" diff --git a/base_layer/service_framework/README.md b/base_layer/service_framework/README.md new file mode 100644 index 0000000000..1a2d88f920 --- /dev/null +++ b/base_layer/service_framework/README.md @@ -0,0 +1 @@ +# Tari Service framework \ No newline at end of file diff --git a/base_layer/service_framework/src/handles/future.rs b/base_layer/service_framework/src/handles/future.rs index 2fff75b053..1eaa57bf2b 100644 --- a/base_layer/service_framework/src/handles/future.rs +++ b/base_layer/service_framework/src/handles/future.rs @@ -20,81 +20,125 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use super::{LazyService, ServiceHandles}; -use futures::{task::AtomicTask, Async, Future, Poll}; +use super::ServiceHandles; +use crate::handles::LazyService; +use futures::{ + task::{AtomicWaker, Context}, + Future, +}; use std::{ any::Any, - hash::Hash, + pin::Pin, sync::{ atomic::{AtomicBool, Ordering}, + mpsc, Arc, }, + task::Poll, }; +/// Create a Notifier, ServiceHandlesFuture pair. +/// +/// The `Notifier::notify` method will notify all cloned `ServiceHandlesFuture`s +/// and which will resolve with the collected `ServiceHandles`. +pub fn handle_notifier_pair() -> (Notifier, ServiceHandlesFuture) { + let (tx, rx) = mpsc::channel(); + let ready_flag = Arc::new(AtomicBool::new(false)); + ( + Notifier::new(Arc::clone(&ready_flag), rx), + ServiceHandlesFuture::new(ready_flag, tx), + ) +} + +pub struct Notifier { + ready_flag: Arc, + waker_receiver: mpsc::Receiver>, +} + +impl Notifier { + pub fn new(ready_flag: Arc, waker_receiver: mpsc::Receiver>) -> Self { + Self { + ready_flag, + waker_receiver, + } + } + + /// Notify that all handles are collected and the task should resolve + pub fn notify(&self) { + self.ready_flag.store(true, Ordering::SeqCst); + while let Ok(waker) = self.waker_receiver.try_recv() { + waker.wake(); + } + } +} + /// Future which resolves to `ServiceHandles` once it is signaled to /// do so. -pub struct ServiceHandlesFuture { - handles: Arc>, - is_ready: Arc, - task: Arc, +pub struct ServiceHandlesFuture { + handles: Arc, + ready_flag: Arc, + wake_sender: mpsc::Sender>, + waker: Arc, } -impl Clone for ServiceHandlesFuture { +impl Clone for ServiceHandlesFuture { fn clone(&self) -> Self { + let waker = Arc::new(AtomicWaker::new()); + self.wake_sender + .send(Arc::clone(&waker)) + .expect("notifier receiver has been dropped"); Self { handles: Arc::clone(&self.handles), - is_ready: Arc::clone(&self.is_ready), - task: Arc::clone(&self.task), + ready_flag: Arc::clone(&self.ready_flag), + wake_sender: self.wake_sender.clone(), + waker, } } } -impl ServiceHandlesFuture -where N: Eq + Hash -{ +impl ServiceHandlesFuture { /// Create a new ServiceHandlesFuture with empty handles - pub fn new() -> Self { + pub fn new(ready_flag: Arc, wake_sender: mpsc::Sender>) -> Self { Self { handles: Arc::new(ServiceHandles::new()), - is_ready: Arc::new(AtomicBool::new(false)), - task: Arc::new(AtomicTask::new()), + ready_flag, + wake_sender, + waker: Arc::new(AtomicWaker::new()), } } /// Insert a service handle with the given name - pub fn insert(&self, service_name: N, value: impl Any + Send + Sync) { - self.handles.insert(service_name, value); + pub fn register(&self, handle: H) + where H: Any + Send + Sync { + self.handles.register(handle); } /// Retrieve a handle and downcast it to return type and return a copy, otherwise None is returned - pub fn get_handle(&self, service_name: N) -> Option - where V: Clone + 'static { - self.handles.get_handle(service_name) + pub fn get_handle(&self) -> Option + where H: Clone + 'static { + self.handles.get_handle() } - /// Call the given function with the final handles once this future is ready (`notify_ready` is called). + // /// Call the given function with the final handles once this future is ready (`notify_ready` is called). pub fn lazy_service(&self, service_fn: F) -> LazyService - where F: FnOnce(Arc>) -> S { + where F: FnOnce(Arc) -> S { LazyService::new(self.clone(), service_fn) } - /// Notify that all handles are collected and the task should resolve - pub fn notify_ready(&self) { - self.is_ready.store(true, Ordering::SeqCst); - self.task.notify(); + pub fn into_inner(self) -> Arc { + self.handles } } -impl Future for ServiceHandlesFuture { - type Error = (); - type Item = Arc>; +impl Future for ServiceHandlesFuture { + type Output = Arc; - fn poll(&mut self) -> Poll { - if self.is_ready.load(Ordering::SeqCst) { - Ok(Async::Ready(Arc::clone(&self.handles))) + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if self.ready_flag.load(Ordering::SeqCst) { + Poll::Ready(Arc::clone(&self.handles)) } else { - self.task.register(); - Ok(Async::NotReady) + self.waker.register(cx.waker()); + Poll::Pending } } } @@ -102,31 +146,54 @@ impl Future for ServiceHandlesFuture { #[cfg(test)] mod test { use super::*; - use tokio_mock_task::MockTask; + use futures::FutureExt; + use std::iter::repeat_with; + use tari_test_utils::counter_context; #[test] fn insert_get() { #[derive(Clone)] struct TestHandle; - let handles = ServiceHandlesFuture::new(); - handles.insert(1, TestHandle); - handles.get_handle::(1).unwrap(); - assert!(handles.get_handle::<()>(1).is_none()); - assert!(handles.get_handle::<()>(2).is_none()); + let (_, handles) = handle_notifier_pair(); + handles.register(TestHandle); + handles.get_handle::().unwrap(); + assert!(handles.get_handle::<()>().is_none()); } #[test] fn notify_ready() { - let mut task = MockTask::new(); - task.enter(|| { - let mut handles = ServiceHandlesFuture::<()>::new(); - let mut clone = handles.clone(); - - assert!(handles.poll().unwrap().is_not_ready()); - assert!(clone.poll().unwrap().is_not_ready()); - handles.notify_ready(); - assert!(handles.poll().unwrap().is_ready()); - assert!(clone.poll().unwrap().is_ready()); - }) + let (notifier, mut handles) = handle_notifier_pair(); + let mut clone = handles.clone(); + + counter_context!(cx, wake_count); + + assert!(handles.poll_unpin(&mut cx).is_pending()); + assert!(clone.poll_unpin(&mut cx).is_pending()); + assert_eq!(wake_count.get(), 0); + notifier.notify(); + assert_eq!(wake_count.get(), 1); + assert!(handles.poll_unpin(&mut cx).is_ready()); + assert!(clone.poll_unpin(&mut cx).is_ready()); + } + + #[test] + fn notify_many() { + let (notifier, mut handles) = handle_notifier_pair(); + let mut clones = repeat_with(|| handles.clone()).take(10).collect::>(); + + counter_context!(cx, wake_count); + assert!(handles.poll_unpin(&mut cx).is_pending()); + + for clone in clones.iter_mut() { + assert!(clone.poll_unpin(&mut cx).is_pending()); + } + + notifier.notify(); + + for clone in clones.iter_mut() { + assert!(clone.poll_unpin(&mut cx).is_ready()); + } + + assert_eq!(wake_count.get(), 10); } } diff --git a/base_layer/service_framework/src/handles/lazy_service.rs b/base_layer/service_framework/src/handles/lazy_service.rs index 9731077ab1..88adb139fe 100644 --- a/base_layer/service_framework/src/handles/lazy_service.rs +++ b/base_layer/service_framework/src/handles/lazy_service.rs @@ -20,7 +20,8 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use futures::{Future, Poll}; +use futures::{ready, task::Context, Future, FutureExt}; +use std::task::Poll; use tower_service::Service; /// LazyService state @@ -33,10 +34,11 @@ enum State { /// /// Implements the `tower_service::Service` trait. The `poll_ready` function will poll /// the given future. Once that future is ready, the resulting value is passed into the -/// given function which must return a service. Subsequent calls to `poll_ready` and `call` -/// are delegated to that service. +/// given `service_fn` function which must return a service. Subsequent calls to +/// `poll_ready` and `call` are delegated to that service. /// -/// This is used by the `lazy_service` combinator in `ServiceHandlesFuture`. +/// This is instantiated by the `lazy_service` combinator in `ServiceHandlesFuture`. +#[must_use = "futures do nothing unless you `.await` or poll them"] pub struct LazyService { future: F, service_fn: Option, @@ -54,21 +56,21 @@ impl LazyService { } } -impl Service for LazyService +impl Service for LazyService where - F: Future, - TFn: FnOnce(F::Item) -> S, + F: Future + Unpin, + TFn: FnOnce(F::Output) -> S, S: Service, { type Error = S::Error; type Future = S::Future; type Response = S::Response; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { loop { match self.state { State::Pending => { - let item = try_ready!(self.future.poll()); + let item = ready!(self.future.poll_unpin(cx)); let service_fn = self .service_fn .take() @@ -76,7 +78,7 @@ where self.state = State::Ready((service_fn)(item)); }, State::Ready(ref mut service) => { - return service.poll_ready(); + return service.poll_ready(cx); }, } } @@ -93,22 +95,23 @@ where #[cfg(test)] mod test { use super::*; - use futures::{ - future::{self, poll_fn}, - Async, + use futures::future::{self, poll_fn}; + use futures_test::task::panic_context; + use std::{ + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + task::Poll, }; - use std::sync::{ - atomic::{AtomicBool, Ordering}, - Arc, - }; - use tower_util::service_fn; + use tower::service_fn; - fn mock_fut(flag: Arc) -> impl Future { - poll_fn::<_, (), _>(move || { + fn mock_fut(flag: Arc) -> impl Future { + poll_fn::<_, _>(move |_: &mut Context<'_>| { if flag.load(Ordering::SeqCst) { - Ok(().into()) + ().into() } else { - Ok(Async::NotReady) + Poll::Pending } }) } @@ -118,33 +121,43 @@ mod test { let flag = Arc::new(AtomicBool::new(false)); let fut = mock_fut(flag.clone()); - let mut service = LazyService::new(fut, |_: ()| service_fn(|_: ()| future::ok::<_, ()>(()))); + let mut cx = panic_context(); + + let mut service = LazyService::new(fut, |_: ()| service_fn(|num: u8| future::ok::<_, ()>(num))); - assert!(service.poll_ready().unwrap().is_not_ready()); + assert!(service.poll_ready(&mut cx).is_pending()); flag.store(true, Ordering::SeqCst); - assert!(service.poll_ready().unwrap().is_ready()); + match service.poll_ready(&mut cx) { + Poll::Ready(Ok(_)) => {}, + _ => panic!("Unexpected poll result"), + } } #[test] fn call_after_ready() { let flag = Arc::new(AtomicBool::new(true)); let fut = mock_fut(flag.clone()); - let mut service = LazyService::new(fut, |_: ()| service_fn(|_: ()| future::ok::<_, ()>(()))); + let mut service = LazyService::new(fut, |_: ()| service_fn(|num: u8| future::ok::<_, ()>(num))); - assert!(service.poll_ready().unwrap().is_ready()); - let mut fut = service.call(()); - assert!(fut.poll().unwrap().is_ready()); + let mut cx = panic_context(); + + assert!(service.poll_ready(&mut cx).is_ready()); + let mut fut = service.call(123); + assert!(fut.poll_unpin(&mut cx).is_ready()); } #[test] #[should_panic] - fn call_before_ready() { + fn call_before_ready_should_panic() { let flag = Arc::new(AtomicBool::new(false)); let fut = mock_fut(flag.clone()); - let mut service = LazyService::new(fut, |_: ()| service_fn(|_: ()| future::ok::<_, ()>(()))); - assert!(service.poll_ready().unwrap().is_not_ready()); - let _ = service.call(()); + let mut service = LazyService::new(fut, |_: ()| service_fn(|num: u8| future::ok::<_, ()>(num))); + + let mut cx = panic_context(); + + assert!(service.poll_ready(&mut cx).is_pending()); + let _ = service.call(123); } } diff --git a/base_layer/service_framework/src/handles/mod.rs b/base_layer/service_framework/src/handles/mod.rs index af06541c05..a139689bd3 100644 --- a/base_layer/service_framework/src/handles/mod.rs +++ b/base_layer/service_framework/src/handles/mod.rs @@ -20,12 +20,16 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::{any::Any, collections::HashMap, hash::Hash, sync::Mutex}; - mod future; mod lazy_service; +use std::{ + any::{Any, TypeId}, + collections::HashMap, + sync::Mutex, +}; pub use self::{future::ServiceHandlesFuture, lazy_service::LazyService}; +pub(crate) use future::handle_notifier_pair; /// This macro unlocks a Mutex or RwLock. If the lock is /// poisoned (i.e. panic while unlocked) the last value @@ -34,7 +38,10 @@ macro_rules! acquire_lock { ($e:expr, $m:ident) => { match $e.$m() { Ok(lock) => lock, - Err(poisoned) => poisoned.into_inner(), + Err(poisoned) => { + log::warn!(target: "service_framework", "Lock has been POISONED and will be silently recovered"); + poisoned.into_inner() + }, } }; ($e:expr) => { @@ -43,13 +50,12 @@ macro_rules! acquire_lock { } /// Simple collection for named handles -pub struct ServiceHandles { - handles: Mutex>>, +#[derive(Default)] +pub struct ServiceHandles { + handles: Mutex>>, } -impl ServiceHandles -where N: Eq + Hash -{ +impl ServiceHandles { /// Create a new ServiceHandles pub fn new() -> Self { Self { @@ -57,18 +63,26 @@ where N: Eq + Hash } } - /// Add a named ServiceHandle - pub fn insert(&self, service_name: N, value: impl Any + Send + Sync) { - acquire_lock!(self.handles).insert(service_name, Box::new(value)); + /// Register a handle + pub fn register(&self, handle: H) + where H: Any + Send + Sync { + acquire_lock!(self.handles).insert(TypeId::of::(), Box::new(handle)); + } + + /// Get a handle from the given type (`TypeId`) and downcast it to a type `H`. + /// If the item does not exist or the downcast fails, `None` is returned. + pub fn get_handle(&self) -> Option + where H: Clone + 'static { + self.get_handle_by_type_id(TypeId::of::()) } - /// Get a ServiceHandle and downcast it to a type `V`. If the item + /// Get a ServiceHandle by name and downcast it to a type `H`. If the item /// does not exist or the downcast fails, `None` is returned. - pub fn get_handle(&self, service_name: N) -> Option - where V: Clone + 'static { + pub fn get_handle_by_type_id(&self, type_id: TypeId) -> Option + where H: Clone + 'static { acquire_lock!(self.handles) - .get(&service_name) - .and_then(|b| b.downcast_ref::()) + .get(&type_id) + .and_then(|b| b.downcast_ref::()) .map(Clone::clone) } } @@ -82,9 +96,9 @@ mod test { #[derive(Clone)] struct TestHandle; let handles = ServiceHandles::new(); - handles.insert(1, TestHandle); - handles.get_handle::(1).unwrap(); - assert!(handles.get_handle::<()>(1).is_none()); - assert!(handles.get_handle::<()>(2).is_none()); + handles.register(TestHandle); + handles.get_handle::().unwrap(); + assert!(handles.get_handle::<()>().is_none()); + assert!(handles.get_handle::().is_none()); } } diff --git a/base_layer/service_framework/src/initializer.rs b/base_layer/service_framework/src/initializer.rs new file mode 100644 index 0000000000..0871bedc83 --- /dev/null +++ b/base_layer/service_framework/src/initializer.rs @@ -0,0 +1,161 @@ +// Copyright 2019 The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::handles::ServiceHandlesFuture; +use derive_error::Error; +use futures::{Future, FutureExt}; +use std::pin::Pin; +use tari_shutdown::ShutdownSignal; +use tokio::runtime; + +#[derive(Debug, Error)] +pub enum ServiceInitializationError { + // General error for failed initialization. + // Specialized errors should be added and used if appropriate. + #[error(msg_embedded, non_std, no_from)] + Failed(String), +} + +/// Implementors of this trait will initialize a service +/// The `StackBuilder` builds impls of this trait. +pub trait ServiceInitializer { + /// The future returned from the initialize function + type Future: Future>; + + /// Async initialization code for a service + fn initialize( + &mut self, + executor: runtime::Handle, + handles_fut: ServiceHandlesFuture, + shutdown: ShutdownSignal, + ) -> Self::Future; + + /// Create a boxed version of this ServiceInitializer. + fn boxed(self) -> BoxedServiceInitializer + where + Self: Sized + Send + 'static, + Self::Future: Send + 'static, + { + BoxedServiceInitializer::new(self) + } +} + +/// Implementation of ServiceInitializer for any function matching the signature of `ServiceInitializer::initialize` +/// This allows the following "short-hand" syntax to be used: +/// +/// ```edition2018 +/// # use tari_service_framework::handles::ServiceHandlesFuture; +/// # use tokio::runtime; +/// let my_initializer = |executor: runtime::Handle, handles_fut: ServiceHandlesFuture| { +/// // initialization code +/// futures::future::ready(Result::<_, ()>::Ok(())) +/// }; +/// ``` +impl ServiceInitializer for TFunc +where + TFunc: FnMut(runtime::Handle, ServiceHandlesFuture, ShutdownSignal) -> TFut, + TFut: Future>, +{ + type Future = TFut; + + fn initialize( + &mut self, + executor: runtime::Handle, + handles: ServiceHandlesFuture, + shutdown: ShutdownSignal, + ) -> Self::Future + { + (self)(executor, handles, shutdown) + } +} + +//---------------------------------- Boxed Service Initializer --------------------------------------------// +// The following code is essentially a substitute for async trait functions. Any initializer can +// converted to the boxed form by using ServiceInitializer::boxed(). This is done for you when +// using `StackBuilder::add_initializer`. + +/// A pinned, boxed form of the future resulting from a boxed ServiceInitializer +type ServiceInitializationFuture = Pin> + Send>>; + +/// This trait mirrors the ServiceInitializer trait, with the exception +/// of always returning a boxed future (aliased ServiceInitializationFuture type), +/// therefore it does not need the `Future` associated type. This makes it +/// possible to store a boxed dyn `AbstractServiceInitializer`. +pub trait AbstractServiceInitializer { + fn initialize( + &mut self, + executor: runtime::Handle, + handles_fut: ServiceHandlesFuture, + shutdown: ShutdownSignal, + ) -> ServiceInitializationFuture; +} + +/// AbstractServiceInitializer impl for every T: ServiceInitializer. +impl AbstractServiceInitializer for T +where + T: ServiceInitializer, + T::Future: Send + 'static, +{ + fn initialize( + &mut self, + executor: runtime::Handle, + handles: ServiceHandlesFuture, + shutdown: ShutdownSignal, + ) -> ServiceInitializationFuture + { + let initialization = self.initialize(executor, handles, shutdown); + initialization.boxed() as ServiceInitializationFuture + } +} + +/// A concrete boxed version of a ServiceInitializer. This makes it possible +/// to have a collection of ServiceInitializers which return various boxed future types. +/// This type is used in StackBuilder's internal vec. +pub struct BoxedServiceInitializer { + inner: Box, +} + +impl BoxedServiceInitializer { + pub(super) fn new(initializer: T) -> Self + where + T: ServiceInitializer + Send + 'static, + T::Future: Send + 'static, + { + Self { + inner: Box::new(initializer), + } + } +} + +impl ServiceInitializer for BoxedServiceInitializer { + type Future = ServiceInitializationFuture; + + fn initialize( + &mut self, + executor: runtime::Handle, + handles_fut: ServiceHandlesFuture, + shutdown: ShutdownSignal, + ) -> Self::Future + { + self.inner.initialize(executor, handles_fut, shutdown) + } +} diff --git a/base_layer/service_framework/src/lib.rs b/base_layer/service_framework/src/lib.rs index 47e0d50268..39df456732 100644 --- a/base_layer/service_framework/src/lib.rs +++ b/base_layer/service_framework/src/lib.rs @@ -1,33 +1,81 @@ -//! Service framework +//! # Service framework //! //! This module contains the building blocks for async services. //! //! It consists of the following modules: //! -//! - `builder`: contains the `MakeServicePair` trait which should be implemented by a service builder and the -//! `StackBuilder` struct which is responsible for building the service and making service _handles_ available to all -//! the other services. Handles are any object which is able to control a service in some way. Most commonly the -//! handle will be a `transport::Requester`. +//! ## `initializer` //! -//! - `handles`: struct for collecting named handles for services. The `StackBuilder` uses this to make all handles -//! available to services. +//! This module contains the [ServiceInitializer] trait. Service modules should implement this trait and pass +//! that implementation to the [StackBuilder]. //! -//! - `transport`: This allows messages to be reliably send/received to/from services. A `Requester`/`Responder` pair is -//! created using the `transport::channel` function which takes an impl of `tower_service::Service` as it's first -//! parameter. A `Requester` implements `tower_service::Service` and is used to send requests which return a Future -//! which resolves to a response. The `Requester` uses a `oneshot` channel allow responses to be sent back. A -//! `Responder` receives a `(request, oneshot::Sender)` tuple, calls the given tower service with that request and -//! sends the result on the `oneshot::Sender`. The `Responder` handles many requests simultaneously. +//! ## `stack` +//! +//! Contains the [StackBuilder] that is responsible for collecting and 'executing' the implementations of +//! [ServiceInitializer]. +//! +//! ## `handles` +//! +//! A set of utilities used to collect and share handles between services. The [StackBuilder] is responsible for +//! initializing a [ServiceHandlesFuture] and making it available to [ServiceInitializer] implementations. +//! +//! Handles are simply a way to communicate with their corresponding service. Typically, a [SenderService] would +//! be used for this purpose but a handle can be implemented in any way the implementor sees fit. +//! +//! ## `reply_channel` +//! +//! This provides for query messages to be sent to services along with a "reply channel" for the service to send back +//! results. The `reply_channel::unbounded` function is used to create a sender/receiver pair. The sender +//! implements `tower_service::Service` and can be used to make requests of a applicable type. The receiver +//! implements `futures::Stream` and will provide a `RequestContext` object that contains a `oneshot` reply channel +//! that the service can use to reply back to the caller. +//! +//! ## Examples +//! +//! ### `reply_channel` +//! +//! ```edition2018 +//! # use futures::executor::block_on; +//! # use futures::StreamExt; +//! # use futures::join; +//! use tari_service_framework::{reply_channel, tower::ServiceExt}; +//! +//! block_on(async { +//! let (mut sender, mut receiver) = reply_channel::unbounded(); +//! +//! let (result, _) = futures::join!( +//! // Make the request and make progress on the resulting future +//! sender.call_ready("upper"), +//! // At the same time receive the request and reply +//! async move { +//! let req_context = receiver.next().await.unwrap(); +//! let msg = req_context.request().unwrap().clone(); +//! req_context.reply(msg.to_uppercase()); +//! } +//! ); +//! +//! assert_eq!(result.unwrap(), "UPPER"); +//! }); +//! ``` +//! +//! [ServiceInitializer]: ./initializer/trait.ServiceInitializer.html +//! [StackBuilder]: ./stack/struct.StackBuilder.html +//! [ServiceHandlesFuture]: ./handles/future/struct.ServiceHandlesFuture.html +//! [SenderService]: ./reply_channel/struct.SenderService.html // Used to eliminate the need for boxing futures in many cases. // Tracking issue: https://github.com/rust-lang/rust/issues/63063 #![feature(type_alias_impl_trait)] -#[macro_use] -extern crate futures; +mod initializer; +mod stack; pub mod handles; -mod stack; -pub mod transport; +pub mod reply_channel; +pub mod tower; -pub use self::stack::{ServiceInitializationError, ServiceInitializer, StackBuilder}; +pub use self::{ + initializer::{ServiceInitializationError, ServiceInitializer}, + reply_channel::RequestContext, + stack::StackBuilder, +}; diff --git a/base_layer/service_framework/src/reply_channel.rs b/base_layer/service_framework/src/reply_channel.rs new file mode 100644 index 0000000000..ec0f75fc4a --- /dev/null +++ b/base_layer/service_framework/src/reply_channel.rs @@ -0,0 +1,324 @@ +// Copyright 2019 The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use derive_error::Error; +use futures::{ + channel::{ + mpsc::{self, SendError}, + oneshot, + }, + ready, + stream::FusedStream, + task::Context, + Future, + FutureExt, + Stream, + StreamExt, +}; +use std::{pin::Pin, task::Poll}; +use tower_service::Service; + +/// Create a new Requester/Responder pair which wraps and calls the given service +pub fn unbounded() -> (SenderService, Receiver) { + let (tx, rx) = mpsc::unbounded(); + (SenderService::new(tx), Receiver::new(rx)) +} + +/// Receiver for a (Request, Reply) tuple, where Reply is a oneshot::Sender +pub type Rx = mpsc::UnboundedReceiver<(TReq, oneshot::Sender)>; +/// Sender for a (Request, Reply) tuple, where Reply is a oneshot::Sender +pub type Tx = mpsc::UnboundedSender<(TReq, oneshot::Sender)>; + +/// Requester is sends requests on a given `Tx` sender and returns a +/// AwaitResponseFuture which will resolve to the generic `TRes`. +/// +/// This should be used to make requests which require a response. +/// +/// This implements `tower_service::Service`, therefore the `poll_ready` and `call` +/// methods should be used to make a request. +pub struct SenderService { + /// Used to send the request + tx: Tx, +} + +impl SenderService { + /// Create a new Requester + pub fn new(tx: Tx) -> Self { + Self { tx } + } +} + +impl Clone for SenderService { + fn clone(&self) -> Self { + Self { tx: self.tx.clone() } + } +} + +impl Service for SenderService { + type Error = TransportChannelError; + type Future = TransportResponseFuture; + type Response = TRes; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.tx.poll_ready(cx).map_err(|err| { + if err.is_disconnected() { + return TransportChannelError::ChannelClosed; + } + + unreachable!("unbounded channels can never be full"); + }) + } + + fn call(&mut self, request: TReq) -> Self::Future { + let (tx, rx) = oneshot::channel(); + + if self.tx.unbounded_send((request, tx)).is_ok() { + TransportResponseFuture::new(rx) + } else { + // We're not able to send (rx closed) so return a future which resolves to + // a ChannelClosed error + TransportResponseFuture::closed() + } + } +} + +#[derive(Debug, Error, Eq, PartialEq, Clone)] +pub enum TransportChannelError { + /// Error occurred when sending + SendError(SendError), + /// Request was canceled + Canceled, + /// The response channel has closed + ChannelClosed, +} + +/// Response future for Results received over a given oneshot channel Receiver. +pub struct TransportResponseFuture { + rx: Option>, +} + +impl TransportResponseFuture { + /// Create a new AwaitResponseFuture + pub fn new(rx: oneshot::Receiver) -> Self { + Self { rx: Some(rx) } + } + + /// Create a closed AwaitResponseFuture. If this is polled + /// an RequestorError::ChannelClosed error is returned. + pub fn closed() -> Self { + Self { rx: None } + } +} + +impl Future for TransportResponseFuture { + type Output = Result; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match self.rx { + Some(ref mut rx) => rx.poll_unpin(cx).map_err(|_| TransportChannelError::Canceled), + None => Poll::Ready(Err(TransportChannelError::ChannelClosed)), + } + } +} + +/// This is the object received on the Receiver-side of this channel. +/// It's a simple wrapper with some convenience functions used to reply to the +/// request. +pub struct RequestContext { + reply_tx: oneshot::Sender, + request: Option, +} + +impl RequestContext { + /// Create a new RequestContect + pub fn new(request: TReq, reply_tx: oneshot::Sender) -> Self { + Self { + request: Some(request), + reply_tx, + } + } + + /// Return a reference to the request object. None is returned after take_request has + /// been called. + pub fn request(&self) -> Option<&TReq> { + self.request.as_ref() + } + + /// Take ownership of the request object, if ownership has not already been taken, + /// otherwise None is returned. + pub fn take_request(&mut self) -> Option { + self.request.take() + } + + /// Consume this object and return it's parts. Namely, the request object and + /// the reply oneshot channel. + pub fn split(self) -> (TReq, oneshot::Sender) { + ( + self.request.expect("RequestContext must be initialized with a request"), + self.reply_tx, + ) + } + + /// Sends a reply to the caller + pub fn reply(self, resp: TResp) -> Result<(), TResp> { + self.reply_tx.send(resp) + } +} + +/// Receiver side of the reply channel. +/// This is functionally equivalent to `rx.map(|(req, reply_tx)| RequestContext::new(req, reply_tx))` +/// but is ergonomically better to use with the `futures::select` macro (implements FusedStream) +/// and has a short type signature. +pub struct Receiver { + rx: Rx, +} + +impl FusedStream for Receiver { + fn is_terminated(&self) -> bool { + self.rx.is_terminated() + } +} + +impl Receiver { + // Create a new Responder + pub fn new(rx: Rx) -> Self { + Self { rx } + } + + pub fn close(&mut self) { + self.rx.close(); + } +} + +impl Stream for Receiver { + type Item = RequestContext; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + match ready!(self.rx.poll_next_unpin(cx)) { + Some((req, tx)) => Poll::Ready(Some(RequestContext::new(req, tx))), + // Stream has closed, so we're done + None => Poll::Ready(None), + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use futures::{executor::block_on, future}; + use std::fmt::Debug; + use tari_test_utils::unpack_enum; + use tower::ServiceExt; + + #[test] + fn await_response_future_new() { + let (tx, rx) = oneshot::channel::>(); + tx.send(Ok(())).unwrap(); + block_on(TransportResponseFuture::new(rx)).unwrap().unwrap(); + } + + #[test] + fn await_response_future_closed() { + let err = block_on(TransportResponseFuture::<()>::closed()).unwrap_err(); + unpack_enum!(TransportChannelError::ChannelClosed = err); + } + + async fn reply(mut rx: Rx, msg: TResp) + where TResp: Debug { + match rx.next().await { + Some((_, tx)) => { + tx.send(msg).unwrap(); + }, + _ => panic!("Expected receiver to have something to receive"), + } + } + + #[test] + fn requestor_call() { + let (tx, rx) = mpsc::unbounded(); + let mut requestor = SenderService::<_, _>::new(tx); + + block_on(requestor.ready()).unwrap(); + let fut = future::join(requestor.call("PING"), reply(rx, "PONG")); + + let msg = block_on(fut.map(|(r, _)| r.unwrap())); + assert_eq!(msg, "PONG"); + } + + #[test] + fn requestor_channel_closed() { + let (requestor, mut request_stream) = super::unbounded::<_, ()>(); + request_stream.close(); + + let err = block_on(requestor.oneshot(())).unwrap_err(); + // Behaviour change in futures 0.3 - the sender does not indicate that the channel is disconnected + unpack_enum!(TransportChannelError::ChannelClosed = err); + } + + #[test] + fn request_response_request_abort() { + let (mut requestor, mut request_stream) = super::unbounded::<_, &str>(); + + block_on(future::join( + async move { + requestor.ready().await.unwrap(); + // `_` drops the response receiver, so when a reply is sent it will fail + let _ = requestor.call("PING"); + }, + async move { + let a = request_stream.next().await.unwrap(); + let req = a.reply_tx.send("PONG").unwrap_err(); + assert_eq!(req, "PONG"); + }, + )); + } + + #[test] + fn request_response_response_canceled() { + let (mut requestor, mut request_stream) = super::unbounded::<_, &str>(); + + block_on(future::join( + async move { + requestor.ready().await.unwrap(); + let err = requestor.call("PING").await.unwrap_err(); + assert_eq!(err, TransportChannelError::Canceled); + }, + async move { + let req = request_stream.next().await.unwrap(); + drop(req); + }, + )); + } + + #[test] + fn request_response_success() { + let (mut requestor, mut request_stream) = super::unbounded::<_, &str>(); + + block_on(requestor.ready()).unwrap(); + let (result, _) = block_on(future::join(requestor.call("PING"), async move { + let req = request_stream.next().await.unwrap(); + req.reply("PONG").unwrap(); + })); + + assert_eq!(result.unwrap(), "PONG"); + } +} diff --git a/base_layer/service_framework/src/stack.rs b/base_layer/service_framework/src/stack.rs index 1fb58b50cd..0157473f33 100644 --- a/base_layer/service_framework/src/stack.rs +++ b/base_layer/service_framework/src/stack.rs @@ -20,121 +20,179 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::handles::{ServiceHandles, ServiceHandlesFuture}; -use derive_error::Error; -use futures::{ - future::{self, Either}, - Future, +use crate::{ + handles::{handle_notifier_pair, ServiceHandles}, + initializer::{BoxedServiceInitializer, ServiceInitializationError, ServiceInitializer}, }; -use std::{hash::Hash, sync::Arc}; - -#[derive(Debug, Error)] -pub enum ServiceInitializationError { - #[error(msg_embedded, non_std, no_from)] - InvariantError(String), -} - -/// Builder trait for creating a service/handle pair. -/// The `StackBuilder` builds impls of this trait. -pub trait ServiceInitializer { - fn initialize(self: Box, handles: ServiceHandlesFuture) -> Result<(), ServiceInitializationError>; -} - -/// Implementation of MakeServicePair for any function taking a ServiceHandle and returning a (Handle, Future) pair. -impl ServiceInitializer for TFunc -where - N: Eq + Hash, - TFunc: FnOnce(ServiceHandlesFuture) -> Result<(), ServiceInitializationError>, -{ - fn initialize(self: Box, handles: ServiceHandlesFuture) -> Result<(), ServiceInitializationError> { - (self)(handles) - } -} +use futures::future::join_all; +use std::sync::Arc; +use tari_shutdown::ShutdownSignal; +use tokio::runtime; /// Responsible for building and collecting handles and (usually long-running) service futures. -/// This can be converted into a future which resolves once all contained service futures are complete -/// by using the `IntoFuture` implementation. -pub struct StackBuilder { - initializers: Vec + Send>>, +/// `finish` is an async function which resolves once all the services are initialized, or returns +/// an error if any one of the services fails to initialize. +pub struct StackBuilder { + initializers: Vec, + executor: runtime::Handle, + shutdown_signal: ShutdownSignal, } -impl StackBuilder -where N: Eq + Hash -{ - pub fn new() -> Self { +impl StackBuilder { + pub fn new(executor: runtime::Handle, shutdown_signal: ShutdownSignal) -> Self { Self { initializers: Vec::new(), + executor, + shutdown_signal, } } +} - pub fn add_initializer(mut self, initializer: impl ServiceInitializer + Send + 'static) -> Self { - self.initializers.push(Box::new(initializer)); - self +impl StackBuilder { + /// Add an impl of ServiceInitializer to the stack + pub fn add_initializer(self, initializer: I) -> Self + where + I: ServiceInitializer + Send + 'static, + I::Future: Send + 'static, + { + self.add_initializer_boxed(initializer.boxed()) } - pub fn finish(self) -> impl Future>, Error = ServiceInitializationError> { - future::lazy(move || { - let handles = ServiceHandlesFuture::new(); + /// Add a ServiceInitializer which has been boxed using `ServiceInitializer::boxed` + pub fn add_initializer_boxed(mut self, initializer: BoxedServiceInitializer) -> Self { + self.initializers.push(initializer); + self + } - for init in self.initializers.into_iter() { - if let Err(err) = init.initialize(handles.clone()) { - return Either::B(future::err(err)); - } - } + /// Concurrently initialize the services. Once all service have been initialized, `notify_ready` + /// is called, which completes initialization for those services. The resulting service handles are + /// returned. If ANY of the services fail to initialize, an error is returned. + pub async fn finish(self) -> Result, ServiceInitializationError> { + let (notifier, handles_fut) = handle_notifier_pair(); + + let StackBuilder { + executor, + shutdown_signal, + initializers, + } = self; + + // Collect all the initialization futures + let init_futures = initializers.into_iter().map(|mut init| { + ServiceInitializer::initialize( + &mut init, + executor.clone(), + handles_fut.clone(), + shutdown_signal.clone(), + ) + }); + + // Run all the initializers concurrently and check each Result returning an error + // on the first one that failed. + for result in join_all(init_futures).await { + result?; + } - handles.notify_ready(); + notifier.notify(); - Either::A(handles.map_err(|_| { - ServiceInitializationError::InvariantError("ServiceHandlesFuture cannot fail".to_string()) - })) - }) + Ok(handles_fut.into_inner()) } } #[cfg(test)] mod test { use super::*; - use futures::{future::poll_fn, Async}; - use std::sync::atomic::{AtomicBool, Ordering}; + use crate::{handles::ServiceHandlesFuture, initializer::ServiceInitializer}; + use futures::{executor::block_on, future, Future}; + use std::sync::atomic::{AtomicUsize, Ordering}; + use tari_shutdown::Shutdown; + use tokio::runtime::Runtime; + use tower::service_fn; #[test] - fn service_stack_new() { - let state = Arc::new(AtomicBool::new(false)); - let state_inner = Arc::clone(&state.clone()); - let service_initializer = |handles: ServiceHandlesFuture<&'static str>| { - handles.insert("test-service", "Fake Handle"); - - let fut = poll_fn(move || { - // Test that this futures own handle is available - let fake_handle = handles.get_handle::<&str>(&"test-service").unwrap(); - assert_eq!(fake_handle, "Fake Handle"); - let not_found = handles.get_handle::<&str>(&"not-found"); - assert!(not_found.is_none()); - - // Any panics above won't fail the test so a marker bool is set - // if there are no panics. - // catch_unwind could be used but then the UnwindSafe trait bound - // needs to be added. TODO: handle panics in service poll functions - state_inner.store(true, Ordering::Release); - Ok(Async::Ready(())) - }); - - tokio::spawn(fut); - Ok(()) + fn service_defn_simple() { + let rt = Runtime::new().unwrap(); + // This is less of a test and more of a demo of using the short-hand implementation of ServiceInitializer + let simple_initializer = |executor: runtime::Handle, _: ServiceHandlesFuture, _: ShutdownSignal| { + executor.spawn(future::ready(())); + future::ok(()) }; - tokio::run( - StackBuilder::new() - .add_initializer(service_initializer) - .finish() - .map(|_| ()) - .or_else(|err| { - panic!("{:?}", err); - #[allow(unreachable_code)] - future::err(()) - }), + let shutdown = Shutdown::new(); + + let handles = block_on( + StackBuilder::new(rt.handle().clone(), shutdown.to_signal()) + .add_initializer(simple_initializer) + .finish(), ); - assert!(state.load(Ordering::Acquire)) + assert!(handles.is_ok()); + } + + #[derive(Clone)] + struct DummyServiceHandle(usize); + struct DummyInitializer { + state: Arc, + } + + impl DummyInitializer { + fn new(state: Arc) -> Self { + Self { state } + } + } + + impl ServiceInitializer for DummyInitializer { + type Future = impl Future>; + + fn initialize( + &mut self, + executor: runtime::Handle, + handles_fut: ServiceHandlesFuture, + _shutdown: ShutdownSignal, + ) -> Self::Future + { + // Spawn some task on the given runtime::Handle + executor.spawn(future::ready(())); + // Add a handle + handles_fut.register(DummyServiceHandle(123)); + + // This demonstrates the chicken and egg problem with services and handles. Specifically, + // that we have a service which requires the handles of other services to be able to + // create it's own handle. Here we wait for the handles_fut to resolve before continuing + // to initialize the service. + // + // Critically, you should never wait for handles in the initialize method because + // handles are only resolved after all initialization methods have completed. + executor.spawn(async move { + let final_handles = handles_fut.await; + + let handle = final_handles.get_handle::().unwrap(); + assert_eq!(handle.0, 123); + // Something which uses the handle + service_fn(|_: ()| future::ok::<_, ()>(handle.0)); + }); + + self.state.fetch_add(1, Ordering::AcqRel); + future::ready(Ok(())) + } + } + + #[test] + fn service_stack_new() { + let rt = Runtime::new().unwrap(); + let shared_state = Arc::new(AtomicUsize::new(0)); + + let shutdown = Shutdown::new(); + let initializer = DummyInitializer::new(Arc::clone(&shared_state)); + + let handles = block_on( + StackBuilder::new(rt.handle().clone(), shutdown.to_signal()) + .add_initializer(initializer) + .finish(), + ) + .unwrap(); + + handles.get_handle::().unwrap(); + + assert_eq!(shared_state.load(Ordering::SeqCst), 1); } } diff --git a/base_layer/core/tests/mod.rs b/base_layer/service_framework/src/tower/mod.rs similarity index 95% rename from base_layer/core/tests/mod.rs rename to base_layer/service_framework/src/tower/mod.rs index 83b0045954..3e7194817c 100644 --- a/base_layer/core/tests/mod.rs +++ b/base_layer/service_framework/src/tower/mod.rs @@ -20,7 +20,6 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -//#[macro_use] -// extern crate arrayref; -// pub mod support; -// pub mod tests; +mod service_ext; + +pub use self::service_ext::{ServiceCallReady, ServiceExt}; diff --git a/base_layer/service_framework/src/tower/service_ext.rs b/base_layer/service_framework/src/tower/service_ext.rs new file mode 100644 index 0000000000..9ab33f59a1 --- /dev/null +++ b/base_layer/service_framework/src/tower/service_ext.rs @@ -0,0 +1,161 @@ +// Copyright 2019 The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use futures::{ready, task::Context, Future, FutureExt}; +use std::{pin::Pin, task::Poll}; +use tower_service::Service; + +impl ServiceExt for T where T: Service {} + +pub trait ServiceExt: Service { + /// The service combinator combines calling `poll_ready` and `call` into a single call. + /// It returns a [ServiceCallReady](./struct.ServiceCallReady.html) future that + /// calls `poll_ready` on the given service, once the service is ready to + /// receive a request, `call` is called and the resulting future is polled. + fn call_ready(&mut self, req: TRequest) -> ServiceCallReady<'_, Self, TRequest> + where Self::Future: Unpin { + ServiceCallReady::new(self, req) + } +} + +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct ServiceCallReady<'a, S, TRequest> +where S: Service + ?Sized +{ + service: &'a mut S, + request: Option, + pending: Option, +} + +impl + Unpin, TRequest> Unpin for ServiceCallReady<'_, S, TRequest> {} + +impl<'a, S, TRequest> ServiceCallReady<'a, S, TRequest> +where + S: Service + ?Sized, + S::Future: Unpin, +{ + fn new(service: &'a mut S, request: TRequest) -> Self { + Self { + service, + request: Some(request), + pending: None, + } + } +} + +impl Future for ServiceCallReady<'_, S, TRequest> +where + S: Service + ?Sized + Unpin, + S::Future: Unpin, +{ + type Output = Result; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = &mut *self; + loop { + match this.pending { + Some(ref mut fut) => return fut.poll_unpin(cx), + None => { + // Poll the service to check if it's ready. If so, make the call + ready!(this.service.poll_ready(cx))?; + let req = this.request.take().expect("the request cannot be made twice"); + this.pending = Some(this.service.call(req)); + }, + } + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use futures::{future, FutureExt}; + use futures_test::task::panic_context; + use std::sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }; + use tower::service_fn; + + #[test] + fn service_ready() { + let mut double_service = service_fn(|req: u32| future::ok::<_, ()>(req + req)); + + let mut cx = panic_context(); + + match ServiceCallReady::new(&mut double_service, 157).poll_unpin(&mut cx) { + Poll::Ready(Ok(v)) => assert_eq!(v, 314), + _ => panic!("Expected future to be ready"), + } + } + + #[test] + fn service_ready_later() { + struct ReadyLater { + call_count: u32, + flag: Arc, + } + + impl Service for ReadyLater { + type Error = (); + type Response = u32; + + type Future = impl Future>; + + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { + if self.flag.load(Ordering::Acquire) { + Ok(()).into() + } else { + Poll::Pending + } + } + + fn call(&mut self, req: u32) -> Self::Future { + self.call_count += 1; + future::ok(req + req) + } + } + + let mut cx = panic_context(); + let ready_flag = Arc::new(AtomicBool::new(false)); + let mut service = ReadyLater { + flag: ready_flag.clone(), + call_count: 0, + }; + + let mut fut = ServiceCallReady::new(&mut service, 157); + + match fut.poll_unpin(&mut cx) { + Poll::Pending => {}, + _ => panic!("Expected future to be pending"), + } + + ready_flag.store(true, Ordering::Release); + + match fut.poll_unpin(&mut cx) { + Poll::Ready(Ok(v)) => assert_eq!(v, 314), + _ => panic!("Expected future to be ready"), + } + + assert_eq!(service.call_count, 1); + } +} diff --git a/base_layer/service_framework/src/transport.rs b/base_layer/service_framework/src/transport.rs deleted file mode 100644 index fb71df7d93..0000000000 --- a/base_layer/service_framework/src/transport.rs +++ /dev/null @@ -1,439 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use derive_error::Error; -use futures::{ - stream::FuturesUnordered, - sync::{mpsc, oneshot}, - Async, - Future, - Poll, - Stream, -}; -use std::marker::PhantomData; -use tower_service::Service; - -/// Create a new Requester/Responder pair which wraps and calls the given service -pub fn channel(service: S) -> (Requester, Responder) -where S: Service { - let (tx, rx) = mpsc::unbounded(); - (Requester::new(tx), Responder::new(rx, service)) -} - -/// Receiver for a (Request, Reply) tuple, where Reply is a oneshot::Sender -pub type Rx = mpsc::UnboundedReceiver<(TReq, oneshot::Sender)>; -/// Sender for a (Request, Reply) tuple, where Reply is a oneshot::Sender -pub type Tx = mpsc::UnboundedSender<(TReq, oneshot::Sender)>; - -/// Requester is sends requests on a given `Tx` sender and returns a -/// AwaitResponseFuture which will resolve to the generic `TRes`. -/// -/// This should be used to make requests which require a response. -/// -/// This implements `tower_service::Service`, therefore the `poll_ready` and `call` -/// methods should be used to make a request. -pub struct Requester { - /// Used to send the request - tx: Tx, -} - -impl Requester { - /// Create a new Requester - pub fn new(tx: Tx) -> Self { - Self { tx } - } -} - -impl Clone for Requester { - fn clone(&self) -> Self { - Self { tx: self.tx.clone() } - } -} - -impl Service for Requester { - type Error = AwaitResponseError; - type Future = AwaitResponseFuture; - type Response = TRes; - - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(().into()) - } - - fn call(&mut self, request: TReq) -> Self::Future { - let (tx, rx) = oneshot::channel(); - - if self.tx.unbounded_send((request, tx)).is_ok() { - AwaitResponseFuture::new(rx) - } else { - // We're not able to send (rx closed) so return a future which resolves to - // a ChannelClosed error - AwaitResponseFuture::closed() - } - } -} - -#[derive(Debug, Error, Eq, PartialEq)] -pub enum AwaitResponseError { - /// Request was canceled - Canceled, - /// The response channel has closed - ChannelClosed, -} - -/// Response future for Results received over a given oneshot channel Receiver. -pub struct AwaitResponseFuture { - rx: Option>, -} - -impl AwaitResponseFuture { - /// Create a new AwaitResponseFuture - pub fn new(rx: oneshot::Receiver) -> Self { - Self { rx: Some(rx) } - } - - /// Create a closed AwaitResponseFuture. If this is polled - /// an RequestorError::ChannelClosed error is returned. - pub fn closed() -> Self { - Self { rx: None } - } -} - -impl Future for AwaitResponseFuture { - type Error = AwaitResponseError; - type Item = T; - - fn poll(&mut self) -> Poll { - match self.rx { - Some(ref mut rx) => rx.poll().map_err(|_| AwaitResponseError::Canceled), - None => Err(AwaitResponseError::ChannelClosed), - } - } -} - -/// This wraps an inner future and sends the result on a oneshot sender -pub struct ResponseFuture { - inner: F, - tx: Option>, - _err: PhantomData, -} - -impl ResponseFuture { - /// Create a new ResponderFuture from a Future and a oneshot Sender - pub fn new(inner: F, tx: oneshot::Sender) -> Self { - Self { - inner, - tx: Some(tx), - _err: PhantomData, - } - } -} - -impl Future for ResponseFuture -where F: Future -{ - type Error = E; - type Item = (); - - fn poll(&mut self) -> Poll { - match self - .tx - .as_mut() - .expect("ResponderFuture cannot be polled after inner future is ready") - .poll_cancel() - { - Err(_) | Ok(Async::Ready(_)) => { - // The receiver will not receive a response, - // so let's abandon processing the inner future - return Ok(().into()); - }, - _ => {}, - } - - // Progress on the inner future - let res = try_ready!(self.inner.poll()); - - let tx = self.tx.take().expect("cannot happen (ResponderFuture)"); - // Send the response - // If we get an error here, the receiver cancelled/closed so discard the Result - // TODO: Add tracing logs - let _ = tx.send(res); - Ok(().into()) - } -} - -/// Future that calls a given Service with requests that are received from a mpsc Receiver -/// and sends the response back on the requests oneshot channel. -/// -/// As requests come through the futures resulting from Service::call is added to a pending queue -/// for concurrent processing. -pub struct Responder -where S: Service -{ - service: S, - rx: Rx, - in_flight: usize, - pending: FuturesUnordered>, -} - -impl Responder -where S: Service -{ - /// Create a new Responder - pub fn new(rx: Rx, service: S) -> Self { - Self { - rx, - service, - in_flight: 0, - pending: FuturesUnordered::new(), - } - } -} - -impl Future for Responder -where S: Service -{ - type Error = (); - type Item = (); - - fn poll(&mut self) -> Poll { - loop { - if !self.pending.is_empty() { - loop { - // Make progress on the pending futures - match self.pending.poll() { - // Continue polling the pending futures until empty or NotReady - Ok(Async::Ready(Some(_))) => { - self.in_flight -= 1; - continue; - }, - Err(_) => { - // Service error occurred - // TODO: Deal with this error - self.in_flight -= 1; - continue; - }, - _ => break, - } - } - } - - // Check the service is ready - // TODO: Log an error returned from a service or deal with it in some way - match self.service.poll_ready().map_err(|_| ())? { - Async::Ready(_) => { - // Receive any new requests - match self.rx.poll().expect("poll error not possible for unbounded receiver") { - Async::Ready(Some((req, tx))) => { - // Call the service and add the resultant future to the pending queue - let fut = ResponseFuture::new(self.service.call(req), tx); - self.in_flight += 1; - self.pending.push(fut); - }, - // Stream has closed, so we're done - Async::Ready(None) => { - return Ok(Async::Ready(())); - }, - Async::NotReady => { - return Ok(Async::NotReady); - }, - } - }, - Async::NotReady => return Ok(Async::NotReady), - } - } - } -} - -#[cfg(test)] -mod test { - use super::*; - use futures::{ - future::{self, Either}, - Async, - Stream, - }; - use std::{fmt::Debug, iter::repeat_with}; - use tokio_mock_task::MockTask; - use tower_util::service_fn; - - #[test] - fn await_response_future_new() { - let (tx, rx) = oneshot::channel::>(); - tx.send(Ok(())).unwrap(); - let mut fut = AwaitResponseFuture::new(rx); - match fut.poll().unwrap() { - Async::Ready(res) => assert!(res.is_ok()), - _ => panic!("expected future to be ready"), - } - } - - #[test] - fn await_response_future_closed() { - let mut fut = AwaitResponseFuture::<()>::closed(); - assert_eq!(fut.poll().unwrap_err(), AwaitResponseError::ChannelClosed); - } - - fn reply(mut rx: Rx>, msg: Result) - where - TResp: Debug, - TErr: Debug, - { - match rx.poll().unwrap() { - Async::Ready(Some((_, tx))) => { - tx.send(msg).unwrap(); - }, - _ => panic!("expected future to be ready"), - } - } - - #[test] - fn requestor_call() { - // task::current() used by unbounded channel - let mut task = MockTask::new(); - task.enter(|| { - let (tx, rx) = mpsc::unbounded(); - let mut requestor = Requester::<_, _>::new(tx); - - let mut fut = requestor.call("PING"); - assert!(fut.poll().unwrap().is_not_ready()); - reply::<_, _, ()>(rx, Ok("PONG")); - match fut.poll().unwrap() { - Async::Ready(Ok(msg)) => assert_eq!(msg, "PONG"), - _ => panic!("Unexpected poll result"), - } - }); - } - - #[test] - fn requestor_channel_closed() { - let (mut requestor, responder) = super::channel(service_fn(|_: ()| future::ok::<_, ()>(()))); - drop(responder); - - let mut fut = requestor.call(()); - assert_eq!(fut.poll().unwrap_err(), AwaitResponseError::ChannelClosed); - } - - #[test] - fn channel_request_response() { - let mut task = MockTask::new(); - task.enter(|| { - let (mut requestor, mut responder) = super::channel(service_fn(|_| future::ok::<_, ()>("PONG"))); - - let mut fut = requestor.call("PING"); - // Allow responder to receive the request and respond - let _ = responder.poll(); - match fut.poll().unwrap() { - Async::Ready(msg) => assert_eq!(msg, "PONG"), - Async::NotReady => panic!("expected future to be Ready"), - } - }); - } - - #[test] - fn channel_responder_inflight_out_of_order() { - let mut task = MockTask::new(); - task.enter(|| { - let (tx, rx) = oneshot::channel(); - struct EchoService(Option>); - impl Service for EchoService { - type Error = (); - type Future = impl Future; - type Response = String; - - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(().into()) - } - - fn call(&mut self, msg: String) -> Self::Future { - if let Some(rx) = self.0.take() { - Either::A(rx.map(|_| msg).map_err(|_| ())) - } else { - // Called more than once, return a future which resolves immediately - Either::B(future::ok(msg)) - } - } - } - - let service = EchoService(Some(rx)); - let (mut requestor, mut responder) = super::channel(service); - - // Make concurrent requests to the service - let mut fut1 = requestor.call("first".to_string()); - let mut fut2 = requestor.call("second".to_string()); - assert_eq!(responder.in_flight, 0); - - // When Responder is polled it will: - // Receive all the requests, - // call the service and then, - // poll pending futures (that is, response is sent to fut2) - responder.poll().unwrap(); - assert!(fut1.poll().unwrap().is_not_ready()); - match fut2.poll().unwrap() { - Async::Ready(v) => assert_eq!(v, "second"), - _ => panic!(), - } - assert_eq!(responder.in_flight, 1); - - // Signal the first call to be Ready so that the result is sent to fut1 - tx.send(()).unwrap(); - // Progress on the pending futures (i.e response for fut1) - responder.poll().unwrap(); - - match fut1.poll().unwrap() { - Async::Ready(v) => assert_eq!(v, "first"), - _ => panic!(), - } - }); - } - - #[test] - fn channel_responder_inflight() { - let mut task = MockTask::new(); - task.enter(|| { - let service = service_fn(|rx: oneshot::Receiver<()>| rx); - let (mut requestor, mut responder) = super::channel(service); - - // Make 100 concurrent requests - let (txs, futs): (Vec<_>, Vec<_>) = repeat_with(|| { - let (tx, rx) = oneshot::channel(); - (tx, requestor.call(rx)) - }) - .take(100) - .unzip(); - - // Call the service and collect the futures - responder.poll().unwrap(); - // Check that all are in-flight - assert_eq!(responder.in_flight, 100); - // Send the ready signal - for tx in txs.into_iter() { - tx.send(()).unwrap(); - } - // Ensure progress is made on tasks - responder.poll().unwrap(); - // Ensure we have no more unresolved requests - assert_eq!(responder.in_flight, 0); - // Check that all futures have completed - assert!(futs.into_iter().all(|mut f| f.poll().unwrap().is_ready())); - }); - } -} diff --git a/base_layer/wallet/Cargo.toml b/base_layer/wallet/Cargo.toml index 44254d8702..24d2b7470e 100644 --- a/base_layer/wallet/Cargo.toml +++ b/base_layer/wallet/Cargo.toml @@ -1,27 +1,57 @@ [package] name = "tari_wallet" -version = "0.0.5" +authors = ["The Tari Development Community"] +description = "Tari cryptocurrency wallet library" +license = "BSD-3-Clause" +version = "0.0.9" edition = "2018" +[features] +test_harness = ["tari_test_utils"] +c_integration = [] + [dependencies] -tari_core = { path = "../core", version="^0.0"} -tari_crypto = { path = "../../infrastructure/crypto", version = "^0.0" } -tari_utilities = { path = "../../infrastructure/tari_util", version = "^0.0"} +tari_broadcast_channel = "^0.1" tari_comms = { path = "../../comms", version = "^0.0"} +tari_comms_dht = { path = "../../comms/dht", version = "^0.0"} +tari_crypto = { version = "^0.3" } +tari_key_manager = {path = "../key_manager", version = "^0.0"} tari_p2p = {path = "../p2p", version = "^0.0"} -tari_key_manager = {path = "../keymanager", version = "^0.0"} +tari_pubsub = "^0.1" +tari_service_framework = { version = "^0.0", path = "../service_framework"} +tari_shutdown = { path = "../../infrastructure/shutdown", version = "^0.0"} +tari_storage = { version = "^0.0", path = "../../infrastructure/storage"} + chrono = { version = "0.4.6", features = ["serde"]} +time = {version = "0.1.39"} derive-error = "0.0.4" digest = "0.8.0" +blake2 = "0.8.0" serde = {version = "1.0.89", features = ["derive"] } +serde_json = "1.0.39" crossbeam-channel = "0.3.8" log = "0.4.6" +log4rs = {version = "0.8.3", features = ["console_appender", "file_appender", "file", "yaml_format"]} lmdb-zero = "0.4.4" -tari_storage = { version = "^0.0", path = "../../infrastructure/storage"} diesel_migrations = "1.4" -diesel = {version="1.4", features = ["sqlite", "serde_json", "chrono"]} -rand = "0.5.5" +diesel = {version="1.4", features = ["sqlite", "serde_json", "chrono", "r2d2"]} +rand = "0.7.2" +futures = { version = "^0.3.1", features =["compat", "std"]} +tokio = { version = "0.2.10", features = ["blocking", "sync"]} +tower = "0.3.0-alpha.2" +tempdir = "0.3.7" +tari_test_utils = { path = "../../infrastructure/test_utils", version = "^0.0", optional = true} -[dev-dependencies] -simple_logger = "1.3.0" +[dependencies.tari_core] +path = "../../base_layer/core" +version = "^0.0" +default-features = false +features = ["transactions", "mempool_proto", "base_node_proto"] +[dev-dependencies] +tari_comms_dht = { path = "../../comms/dht", version = "^0.0", features=["test-mocks"]} +tari_test_utils = { path = "../../infrastructure/test_utils", version = "^0.0"} +lazy_static = "1.3.0" +env_logger = "0.7.1" +prost = "0.6.1" +tokio-macros = "0.2.4" diff --git a/base_layer/wallet/README.md b/base_layer/wallet/README.md new file mode 100644 index 0000000000..0158fd19a3 --- /dev/null +++ b/base_layer/wallet/README.md @@ -0,0 +1 @@ +# Tari Wallet \ No newline at end of file diff --git a/base_layer/wallet/migrations/2019-06-26-130555_initial/down.sql b/base_layer/wallet/migrations/2019-06-26-130555_initial/down.sql index 9b7e8d3da1..1957bf7527 100644 --- a/base_layer/wallet/migrations/2019-06-26-130555_initial/down.sql +++ b/base_layer/wallet/migrations/2019-06-26-130555_initial/down.sql @@ -1,4 +1,3 @@ -DROP TABLE IF EXISTS sent_messages; -DROP TABLE IF EXISTS received_messages; -DROP TABLE IF EXISTS contacts; -DROP TABLE IF EXISTS settings; \ No newline at end of file +-- DROP TABLE IF EXISTS sent_messages; +-- DROP TABLE IF EXISTS received_messages; +-- DROP TABLE IF EXISTS settings; \ No newline at end of file diff --git a/base_layer/wallet/migrations/2019-06-26-130555_initial/up.sql b/base_layer/wallet/migrations/2019-06-26-130555_initial/up.sql index 23a1ed10ab..45915fb256 100644 --- a/base_layer/wallet/migrations/2019-06-26-130555_initial/up.sql +++ b/base_layer/wallet/migrations/2019-06-26-130555_initial/up.sql @@ -1,28 +1,25 @@ -CREATE TABLE sent_messages ( - id TEXT PRIMARY KEY NOT NULL, - source_pub_key TEXT NOT NULL, - dest_pub_key TEXT NOT NULL, - message TEXT NOT NULL, - timestamp DATETIME NOT NULL, - acknowledged INTEGER NOT NULL DEFAULT 0, - FOREIGN KEY(dest_pub_key) REFERENCES contacts(pub_key) -); +-- CREATE TABLE sent_messages ( +-- id TEXT PRIMARY KEY NOT NULL, +-- source_pub_key TEXT NOT NULL, +-- dest_pub_key TEXT NOT NULL, +-- message TEXT NOT NULL, +-- timestamp DATETIME NOT NULL, +-- acknowledged INTEGER NOT NULL DEFAULT 0, +-- is_read INTEGER NOT NULL DEFAULT 0, +-- FOREIGN KEY(dest_pub_key) REFERENCES contacts(pub_key) +-- ); -CREATE TABLE received_messages ( - id BLOB PRIMARY KEY NOT NULL, - source_pub_key TEXT NOT NULL, - dest_pub_key TEXT NOT NULL, - message TEXT NOT NULL, - timestamp DATETIME NOT NULL -); +-- CREATE TABLE received_messages ( +-- id BLOB PRIMARY KEY NOT NULL, +-- source_pub_key TEXT NOT NULL, +-- dest_pub_key TEXT NOT NULL, +-- message TEXT NOT NULL, +-- timestamp DATETIME NOT NULL +-- ); + +-- CREATE TABLE settings ( +-- pub_key TEXT PRIMARY KEY NOT NULL, +-- screen_name TEXT NOT NULL +-- ) -CREATE TABLE contacts ( - pub_key TEXT PRIMARY KEY NOT NULL UNIQUE, - screen_name TEXT NOT NULL, - address TEXT NOT NULL -); -CREATE TABLE settings ( - pub_key TEXT PRIMARY KEY NOT NULL, - screen_name TEXT NOT NULL -) \ No newline at end of file diff --git a/base_layer/wallet/migrations/2019-10-30-084148_output_manager_service/down.sql b/base_layer/wallet/migrations/2019-10-30-084148_output_manager_service/down.sql new file mode 100644 index 0000000000..d22ffee6b8 --- /dev/null +++ b/base_layer/wallet/migrations/2019-10-30-084148_output_manager_service/down.sql @@ -0,0 +1,3 @@ +DROP TABLE IF EXISTS outputs; +DROP TABLE IF EXISTS pending_transaction_outputs; +DROP TABLE IF EXISTS key_manager_states; \ No newline at end of file diff --git a/base_layer/wallet/migrations/2019-10-30-084148_output_manager_service/up.sql b/base_layer/wallet/migrations/2019-10-30-084148_output_manager_service/up.sql new file mode 100644 index 0000000000..f4c105f0b0 --- /dev/null +++ b/base_layer/wallet/migrations/2019-10-30-084148_output_manager_service/up.sql @@ -0,0 +1,22 @@ +CREATE TABLE outputs ( + spending_key BLOB PRIMARY KEY NOT NULL, + value INTEGER NOT NULL, + flags INTEGER NOT NULL, + maturity INTEGER NOT NULL, + status INTEGER NOT NULL, + tx_id INTEGER NULL, + FOREIGN KEY(tx_id) REFERENCES pending_transaction_outputs(tx_id) +); + +CREATE TABLE pending_transaction_outputs ( + tx_id INTEGER PRIMARY KEY NOT NULL, + timestamp DATETIME NOT NULL +); + +CREATE TABLE key_manager_states ( + id INTEGER PRIMARY KEY, + master_seed BLOB NOT NULL, + branch_seed TEXT NOT NULL, + primary_key_index INTEGER NOT NULL, + timestamp DATETIME NOT NULL +); \ No newline at end of file diff --git a/base_layer/wallet/migrations/2019-11-20-090620_transaction_service/down.sql b/base_layer/wallet/migrations/2019-11-20-090620_transaction_service/down.sql new file mode 100644 index 0000000000..ede4f8ef76 --- /dev/null +++ b/base_layer/wallet/migrations/2019-11-20-090620_transaction_service/down.sql @@ -0,0 +1,4 @@ +DROP TABLE IF EXISTS pending_outbound_transactions; +DROP TABLE IF EXISTS pending_inbound_transactions; +DROP TABLE IF EXISTS pending_coinbase_transactions; +DROP TABLE IF EXISTS completed_transactions; \ No newline at end of file diff --git a/base_layer/wallet/migrations/2019-11-20-090620_transaction_service/up.sql b/base_layer/wallet/migrations/2019-11-20-090620_transaction_service/up.sql new file mode 100644 index 0000000000..29eb8b0935 --- /dev/null +++ b/base_layer/wallet/migrations/2019-11-20-090620_transaction_service/up.sql @@ -0,0 +1,37 @@ +CREATE TABLE outbound_transactions ( + tx_id INTEGER PRIMARY KEY NOT NULL, + destination_public_key BLOB NOT NULL, + amount INTEGER NOT NULL, + fee INTEGER NOT NULL, + sender_protocol TEXT NOT NULL, + message TEXT NOT NULL, + timestamp DATETIME NOT NULL +); + +CREATE TABLE inbound_transactions ( + tx_id INTEGER PRIMARY KEY NOT NULL, + source_public_key BLOB NOT NULL, + amount INTEGER NOT NULL, + receiver_protocol TEXT NOT NULL, + message TEXT NOT NULL, + timestamp DATETIME NOT NULL +); + +CREATE TABLE coinbase_transactions ( + tx_id INTEGER PRIMARY KEY NOT NULL, + amount INTEGER NOT NULL, + commitment BLOB NOT NULL, + timestamp DATETIME NOT NULL +); + +CREATE TABLE completed_transactions ( + tx_id INTEGER PRIMARY KEY NOT NULL, + source_public_key BLOB NOT NULL, + destination_public_key BLOB NOT NULL, + amount INTEGER NOT NULL, + fee INTEGER NOT NULL, + transaction_protocol TEXT NOT NULL, + status INTEGER NOT NULL, + message TEXT NOT NULL, + timestamp DATETIME NOT NULL +); \ No newline at end of file diff --git a/base_layer/wallet/migrations/2019-11-26-105357_contacts/down.sql b/base_layer/wallet/migrations/2019-11-26-105357_contacts/down.sql new file mode 100644 index 0000000000..8a6ab255e9 --- /dev/null +++ b/base_layer/wallet/migrations/2019-11-26-105357_contacts/down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS contacts; diff --git a/base_layer/wallet/migrations/2019-11-26-105357_contacts/up.sql b/base_layer/wallet/migrations/2019-11-26-105357_contacts/up.sql new file mode 100644 index 0000000000..ddb867a673 --- /dev/null +++ b/base_layer/wallet/migrations/2019-11-26-105357_contacts/up.sql @@ -0,0 +1,4 @@ +CREATE TABLE contacts ( + public_key BLOB PRIMARY KEY NOT NULL UNIQUE, + alias TEXT NOT NULL +); \ No newline at end of file diff --git a/base_layer/wallet/migrations/2019-11-26-120903_peers/down.sql b/base_layer/wallet/migrations/2019-11-26-120903_peers/down.sql new file mode 100644 index 0000000000..f4ce4b526e --- /dev/null +++ b/base_layer/wallet/migrations/2019-11-26-120903_peers/down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS peers; \ No newline at end of file diff --git a/base_layer/wallet/migrations/2019-11-26-120903_peers/up.sql b/base_layer/wallet/migrations/2019-11-26-120903_peers/up.sql new file mode 100644 index 0000000000..4a95468993 --- /dev/null +++ b/base_layer/wallet/migrations/2019-11-26-120903_peers/up.sql @@ -0,0 +1,4 @@ +CREATE TABLE peers ( + public_key BLOB PRIMARY KEY NOT NULL UNIQUE, + peer TEXT NOT NULL +); \ No newline at end of file diff --git a/base_layer/wallet/src/contacts_service/error.rs b/base_layer/wallet/src/contacts_service/error.rs new file mode 100644 index 0000000000..abc53d2c4a --- /dev/null +++ b/base_layer/wallet/src/contacts_service/error.rs @@ -0,0 +1,57 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::contacts_service::storage::database::DbKey; +use derive_error::Error; +use diesel::result::Error as DieselError; +use tari_service_framework::reply_channel::TransportChannelError; + +#[derive(Debug, Error, PartialEq)] +pub enum ContactsServiceError { + /// Contact is not found + ContactNotFound, + /// Received incorrect response from service request + UnexpectedApiResponse, + ContactsServiceStorageError(ContactsServiceStorageError), + TransportChannelError(TransportChannelError), +} + +#[derive(Debug, Error, PartialEq)] +pub enum ContactsServiceStorageError { + /// This write operation is not supported for provided DbKey + OperationNotSupported, + /// Error converting a type + ConversionError, + /// Could not find all values specified for batch operation + ValuesNotFound, + #[error(non_std, no_from)] + ValueNotFound(DbKey), + #[error(msg_embedded, non_std, no_from)] + UnexpectedResult(String), + R2d2Error, + DieselError(DieselError), + DieselConnectionError(diesel::ConnectionError), + #[error(msg_embedded, no_from, non_std)] + DatabaseMigrationError(String), + #[error(msg_embedded, non_std, no_from)] + BlockingTaskSpawnError(String), +} diff --git a/base_layer/wallet/src/contacts_service/handle.rs b/base_layer/wallet/src/contacts_service/handle.rs new file mode 100644 index 0000000000..9a5c3f44f9 --- /dev/null +++ b/base_layer/wallet/src/contacts_service/handle.rs @@ -0,0 +1,90 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::contacts_service::{error::ContactsServiceError, storage::database::Contact}; +use tari_comms::types::CommsPublicKey; +use tari_service_framework::reply_channel::SenderService; +use tower::Service; + +#[derive(Debug)] +pub enum ContactsServiceRequest { + GetContact(CommsPublicKey), + UpsertContact(Contact), + RemoveContact(CommsPublicKey), + GetContacts, +} + +#[derive(Debug)] +pub enum ContactsServiceResponse { + ContactSaved, + ContactRemoved(Contact), + Contact(Contact), + Contacts(Vec), +} + +#[derive(Clone)] +pub struct ContactsServiceHandle { + handle: SenderService>, +} +impl ContactsServiceHandle { + pub fn new( + handle: SenderService>, + ) -> Self { + Self { handle } + } + + pub async fn get_contact(&mut self, pub_key: CommsPublicKey) -> Result { + match self.handle.call(ContactsServiceRequest::GetContact(pub_key)).await?? { + ContactsServiceResponse::Contact(c) => Ok(c), + _ => Err(ContactsServiceError::UnexpectedApiResponse), + } + } + + pub async fn get_contacts(&mut self) -> Result, ContactsServiceError> { + match self.handle.call(ContactsServiceRequest::GetContacts).await?? { + ContactsServiceResponse::Contacts(c) => Ok(c), + _ => Err(ContactsServiceError::UnexpectedApiResponse), + } + } + + pub async fn upsert_contact(&mut self, contact: Contact) -> Result<(), ContactsServiceError> { + match self + .handle + .call(ContactsServiceRequest::UpsertContact(contact)) + .await?? + { + ContactsServiceResponse::ContactSaved => Ok(()), + _ => Err(ContactsServiceError::UnexpectedApiResponse), + } + } + + pub async fn remove_contact(&mut self, pub_key: CommsPublicKey) -> Result { + match self + .handle + .call(ContactsServiceRequest::RemoveContact(pub_key)) + .await?? + { + ContactsServiceResponse::ContactRemoved(c) => Ok(c), + _ => Err(ContactsServiceError::UnexpectedApiResponse), + } + } +} diff --git a/base_layer/wallet/src/contacts_service/mod.rs b/base_layer/wallet/src/contacts_service/mod.rs new file mode 100644 index 0000000000..e5620dd6bb --- /dev/null +++ b/base_layer/wallet/src/contacts_service/mod.rs @@ -0,0 +1,93 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::contacts_service::{ + handle::ContactsServiceHandle, + service::ContactsService, + storage::database::{ContactsBackend, ContactsDatabase}, +}; +use futures::{future, Future}; +use log::*; +use tari_service_framework::{ + handles::ServiceHandlesFuture, + reply_channel, + ServiceInitializationError, + ServiceInitializer, +}; +use tari_shutdown::ShutdownSignal; +use tokio::runtime; + +pub mod error; +pub mod handle; +pub mod service; +pub mod storage; + +const LOG_TARGET: &str = "base_layer::wallet::contacts_service::initializer"; + +pub struct ContactsServiceInitializer +where T: ContactsBackend +{ + backend: Option, +} + +impl ContactsServiceInitializer +where T: ContactsBackend +{ + pub fn new(backend: T) -> Self { + Self { backend: Some(backend) } + } +} + +impl ServiceInitializer for ContactsServiceInitializer +where T: ContactsBackend + 'static +{ + type Future = impl Future>; + + fn initialize( + &mut self, + executor: runtime::Handle, + handles_fut: ServiceHandlesFuture, + shutdown: ShutdownSignal, + ) -> Self::Future + { + let (sender, receiver) = reply_channel::unbounded(); + + let contacts_handle = ContactsServiceHandle::new(sender); + + // Register handle before waiting for handles to be ready + handles_fut.register(contacts_handle); + + let backend = self + .backend + .take() + .expect("Cannot start Contacts Service without setting a storage backend"); + + executor.spawn(async move { + let service = ContactsService::new(receiver, ContactsDatabase::new(backend)).start(); + + futures::pin_mut!(service); + future::select(service, shutdown).await; + info!(target: LOG_TARGET, "Contacts service shutdown"); + }); + future::ready(Ok(())) + } +} diff --git a/base_layer/wallet/src/contacts_service/service.rs b/base_layer/wallet/src/contacts_service/service.rs new file mode 100644 index 0000000000..e78b12a287 --- /dev/null +++ b/base_layer/wallet/src/contacts_service/service.rs @@ -0,0 +1,115 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::contacts_service::{ + error::ContactsServiceError, + handle::{ContactsServiceRequest, ContactsServiceResponse}, + storage::database::{ContactsBackend, ContactsDatabase}, +}; +use futures::{pin_mut, StreamExt}; +use log::*; +use tari_service_framework::reply_channel; + +const LOG_TARGET: &str = "base_layer::wallet:contacts_service"; + +pub struct ContactsService +where T: ContactsBackend + 'static +{ + db: ContactsDatabase, + request_stream: + Option>>, +} + +impl ContactsService +where T: ContactsBackend + 'static +{ + pub fn new( + request_stream: reply_channel::Receiver< + ContactsServiceRequest, + Result, + >, + + db: ContactsDatabase, + ) -> Self + { + Self { + db, + request_stream: Some(request_stream), + } + } + + pub async fn start(mut self) -> Result<(), ContactsServiceError> { + let request_stream = self + .request_stream + .take() + .expect("Contacts Service initialized without request_stream") + .fuse(); + pin_mut!(request_stream); + + info!("Contacts Service started"); + loop { + futures::select! { + request_context = request_stream.select_next_some() => { + let (request, reply_tx) = request_context.split(); + let _ = reply_tx.send(self.handle_request(request).await.or_else(|resp| { + error!(target: LOG_TARGET, "Error handling request: {:?}", resp); + Err(resp) + })).or_else(|resp| { + error!(target: LOG_TARGET, "Failed to send reply"); + Err(resp) + }); + }, + complete => { + info!(target: LOG_TARGET, "Contacts service shutting down"); + break; + } + } + } + info!("Contacts Service ended"); + Ok(()) + } + + async fn handle_request( + &mut self, + request: ContactsServiceRequest, + ) -> Result + { + Ok(match request { + ContactsServiceRequest::GetContact(pk) => { + self.db.get_contact(pk).await.map(ContactsServiceResponse::Contact)? + }, + ContactsServiceRequest::UpsertContact(c) => self + .db + .upsert_contact(c) + .await + .map(|_| ContactsServiceResponse::ContactSaved)?, + ContactsServiceRequest::RemoveContact(pk) => self + .db + .remove_contact(pk) + .await + .map(ContactsServiceResponse::ContactRemoved)?, + ContactsServiceRequest::GetContacts => { + self.db.get_contacts().await.map(ContactsServiceResponse::Contacts)? + }, + }) + } +} diff --git a/base_layer/wallet/src/contacts_service/storage/database.rs b/base_layer/wallet/src/contacts_service/storage/database.rs new file mode 100644 index 0000000000..52e6dcb607 --- /dev/null +++ b/base_layer/wallet/src/contacts_service/storage/database.rs @@ -0,0 +1,183 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::contacts_service::error::ContactsServiceStorageError; +use log::*; +use std::{ + fmt::{Display, Error, Formatter}, + sync::Arc, +}; +use tari_comms::types::CommsPublicKey; + +const LOG_TARGET: &str = "wallet::contacts_service::database"; + +#[derive(Debug, Clone, PartialEq)] +pub struct Contact { + pub alias: String, + pub public_key: CommsPublicKey, +} + +/// This trait defines the functionality that a database backend need to provide for the Contacts Service +pub trait ContactsBackend: Send + Sync { + /// Retrieve the record associated with the provided DbKey + fn fetch(&self, key: &DbKey) -> Result, ContactsServiceStorageError>; + /// Modify the state the of the backend with a write operation + fn write(&self, op: WriteOperation) -> Result, ContactsServiceStorageError>; +} + +#[derive(Debug, Clone, PartialEq)] +pub enum DbKey { + Contact(CommsPublicKey), + Contacts, +} + +pub enum DbValue { + Contact(Box), + Contacts(Vec), +} + +pub enum DbKeyValuePair { + Contact(CommsPublicKey, Contact), +} + +pub enum WriteOperation { + Upsert(DbKeyValuePair), + Remove(DbKey), +} + +// Private macro that pulls out all the boiler plate of extracting a DB query result from its variants +macro_rules! fetch { + ($db:ident, $key_val:expr, $key_var:ident) => {{ + let key = DbKey::$key_var($key_val); + match $db.fetch(&key) { + Ok(None) => Err(ContactsServiceStorageError::ValueNotFound(key)), + Ok(Some(DbValue::$key_var(k))) => Ok(*k), + Ok(Some(other)) => unexpected_result(key, other), + Err(e) => log_error(key, e), + } + }}; +} + +pub struct ContactsDatabase +where T: ContactsBackend +{ + db: Arc, +} + +impl ContactsDatabase +where T: ContactsBackend + 'static +{ + pub fn new(db: T) -> Self { + Self { db: Arc::new(db) } + } + + pub async fn get_contact(&self, pub_key: CommsPublicKey) -> Result { + let db_clone = self.db.clone(); + tokio::task::spawn_blocking(move || fetch!(db_clone, pub_key.clone(), Contact)) + .await + .or_else(|err| Err(ContactsServiceStorageError::BlockingTaskSpawnError(err.to_string()))) + .and_then(|inner_result| inner_result) + } + + pub async fn get_contacts(&self) -> Result, ContactsServiceStorageError> { + let db_clone = self.db.clone(); + + let c = tokio::task::spawn_blocking(move || match db_clone.fetch(&DbKey::Contacts) { + Ok(None) => log_error( + DbKey::Contacts, + ContactsServiceStorageError::UnexpectedResult("Could not retrieve contacts".to_string()), + ), + Ok(Some(DbValue::Contacts(c))) => Ok(c), + Ok(Some(other)) => unexpected_result(DbKey::Contacts, other), + Err(e) => log_error(DbKey::Contacts, e), + }) + .await + .or_else(|err| Err(ContactsServiceStorageError::BlockingTaskSpawnError(err.to_string())))??; + Ok(c) + } + + pub async fn upsert_contact(&self, contact: Contact) -> Result<(), ContactsServiceStorageError> { + let db_clone = self.db.clone(); + + tokio::task::spawn_blocking(move || { + db_clone.write(WriteOperation::Upsert(DbKeyValuePair::Contact( + contact.public_key.clone(), + contact, + ))) + }) + .await + .or_else(|err| Err(ContactsServiceStorageError::BlockingTaskSpawnError(err.to_string())))??; + Ok(()) + } + + pub async fn remove_contact(&self, pub_key: CommsPublicKey) -> Result { + let db_clone = self.db.clone(); + let pub_key_clone = pub_key.clone(); + let result = + tokio::task::spawn_blocking(move || db_clone.write(WriteOperation::Remove(DbKey::Contact(pub_key_clone)))) + .await + .or_else(|err| Err(ContactsServiceStorageError::BlockingTaskSpawnError(err.to_string()))) + .and_then(|inner_result| inner_result)? + .ok_or_else(|| ContactsServiceStorageError::ValueNotFound(DbKey::Contact(pub_key.clone())))?; + + match result { + DbValue::Contact(c) => Ok(*c), + DbValue::Contacts(_) => Err(ContactsServiceStorageError::UnexpectedResult( + "Incorrect response from backend.".to_string(), + )), + } + } +} + +fn unexpected_result(req: DbKey, res: DbValue) -> Result { + let msg = format!("Unexpected result for database query {}. Response: {}", req, res); + error!(target: LOG_TARGET, "{}", msg); + Err(ContactsServiceStorageError::UnexpectedResult(msg)) +} + +impl Display for DbKey { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + match self { + DbKey::Contact(c) => f.write_str(&format!("Contact: {:?}", c)), + DbKey::Contacts => f.write_str(&"Contacts".to_string()), + } + } +} + +impl Display for DbValue { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + match self { + DbValue::Contact(_) => f.write_str(&"Contact".to_string()), + DbValue::Contacts(_) => f.write_str(&"Contacts".to_string()), + } + } +} + +fn log_error(req: DbKey, err: ContactsServiceStorageError) -> Result { + error!( + target: LOG_TARGET, + "Database access error on request: {}: {}", + req, + err.to_string() + ); + Err(err) +} diff --git a/base_layer/wallet/src/contacts_service/storage/memory_db.rs b/base_layer/wallet/src/contacts_service/storage/memory_db.rs new file mode 100644 index 0000000000..a296b421c3 --- /dev/null +++ b/base_layer/wallet/src/contacts_service/storage/memory_db.rs @@ -0,0 +1,90 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::contacts_service::{ + error::ContactsServiceStorageError, + storage::database::{Contact, ContactsBackend, DbKey, DbKeyValuePair, DbValue, WriteOperation}, +}; +use std::sync::{Arc, RwLock}; + +#[derive(Default)] +pub struct InnerDatabase { + contacts: Vec, +} + +impl InnerDatabase { + pub fn new() -> Self { + Self { contacts: Vec::new() } + } +} + +#[derive(Default)] +pub struct ContactsServiceMemoryDatabase { + db: Arc>, +} + +impl ContactsServiceMemoryDatabase { + pub fn new() -> Self { + Self { + db: Arc::new(RwLock::new(InnerDatabase::new())), + } + } +} + +impl ContactsBackend for ContactsServiceMemoryDatabase { + fn fetch(&self, key: &DbKey) -> Result, ContactsServiceStorageError> { + let db = acquire_read_lock!(self.db); + let result = match key { + DbKey::Contact(pk) => db + .contacts + .iter() + .find(|v| &v.public_key == pk) + .map(|c| DbValue::Contact(Box::new(c.clone()))), + DbKey::Contacts => Some(DbValue::Contacts(db.contacts.clone())), + }; + + Ok(result) + } + + fn write(&self, op: WriteOperation) -> Result, ContactsServiceStorageError> { + let mut db = acquire_write_lock!(self.db); + match op { + WriteOperation::Upsert(kvp) => match kvp { + DbKeyValuePair::Contact(pk, c) => match db.contacts.iter_mut().find(|i| i.public_key == pk) { + None => db.contacts.push(c), + Some(existing_contact) => existing_contact.alias = c.alias, + }, + }, + WriteOperation::Remove(k) => match k { + DbKey::Contact(pk) => match db.contacts.iter().position(|c| c.public_key == pk) { + None => return Err(ContactsServiceStorageError::ValueNotFound(DbKey::Contact(pk))), + Some(pos) => return Ok(Some(DbValue::Contact(Box::new(db.contacts.remove(pos))))), + }, + DbKey::Contacts => { + return Err(ContactsServiceStorageError::OperationNotSupported); + }, + }, + } + + Ok(None) + } +} diff --git a/base_layer/core/src/base_node/block_validation_service.rs b/base_layer/wallet/src/contacts_service/storage/mod.rs similarity index 96% rename from base_layer/core/src/base_node/block_validation_service.rs rename to base_layer/wallet/src/contacts_service/storage/mod.rs index 7183d94666..c6c73087f7 100644 --- a/base_layer/core/src/base_node/block_validation_service.rs +++ b/base_layer/wallet/src/contacts_service/storage/mod.rs @@ -20,4 +20,6 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -pub struct BlockValidationService; +pub mod database; +pub mod memory_db; +pub mod sqlite_db; diff --git a/base_layer/wallet/src/contacts_service/storage/sqlite_db.rs b/base_layer/wallet/src/contacts_service/storage/sqlite_db.rs new file mode 100644 index 0000000000..5e7ce2a7c7 --- /dev/null +++ b/base_layer/wallet/src/contacts_service/storage/sqlite_db.rs @@ -0,0 +1,297 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + contacts_service::{ + error::ContactsServiceStorageError, + storage::database::{Contact, ContactsBackend, DbKey, DbKeyValuePair, DbValue, WriteOperation}, + }, + schema::contacts, +}; +use diesel::{ + prelude::*, + r2d2::{ConnectionManager, Pool, PooledConnection}, + result::Error as DieselError, + SqliteConnection, +}; +use std::convert::TryFrom; +use tari_core::transactions::types::PublicKey; +use tari_crypto::tari_utilities::ByteArray; + +/// A Sqlite backend for the Output Manager Service. The Backend is accessed via a connection pool to the Sqlite file. +pub struct ContactsServiceSqliteDatabase { + database_connection_pool: Pool>, +} +impl ContactsServiceSqliteDatabase { + pub fn new(database_connection_pool: Pool>) -> Self { + Self { + database_connection_pool, + } + } +} + +impl ContactsBackend for ContactsServiceSqliteDatabase { + fn fetch(&self, key: &DbKey) -> Result, ContactsServiceStorageError> { + let conn = self + .database_connection_pool + .clone() + .get() + .map_err(|_| ContactsServiceStorageError::R2d2Error)?; + + let result = match key { + DbKey::Contact(pk) => match ContactSql::find(&pk.to_vec(), &conn) { + Ok(c) => Some(DbValue::Contact(Box::new(Contact::try_from(c)?))), + Err(ContactsServiceStorageError::DieselError(DieselError::NotFound)) => None, + Err(e) => return Err(e), + }, + DbKey::Contacts => Some(DbValue::Contacts( + ContactSql::index(&conn)? + .iter() + .map(|c| Contact::try_from(c.clone())) + .collect::, _>>()?, + )), + }; + + Ok(result) + } + + fn write(&self, op: WriteOperation) -> Result, ContactsServiceStorageError> { + let conn = self + .database_connection_pool + .clone() + .get() + .map_err(|_| ContactsServiceStorageError::R2d2Error)?; + + match op { + WriteOperation::Upsert(kvp) => match kvp { + DbKeyValuePair::Contact(k, c) => match ContactSql::find(&k.to_vec(), &conn) { + Ok(found_c) => { + let _ = found_c.update(UpdateContact { alias: Some(c.alias) }, &conn)?; + }, + Err(_) => { + ContactSql::from(c).commit(&conn)?; + }, + }, + }, + WriteOperation::Remove(k) => match k { + DbKey::Contact(k) => match ContactSql::find(&k.to_vec(), &conn) { + Ok(c) => { + c.clone().delete(&conn)?; + return Ok(Some(DbValue::Contact(Box::new(Contact::try_from(c)?)))); + }, + Err(ContactsServiceStorageError::DieselError(DieselError::NotFound)) => (), + Err(e) => return Err(e), + }, + DbKey::Contacts => return Err(ContactsServiceStorageError::OperationNotSupported), + }, + } + + Ok(None) + } +} + +/// A Sql version of the Contact struct +#[derive(Clone, Debug, Queryable, Insertable, PartialEq)] +#[table_name = "contacts"] +struct ContactSql { + public_key: Vec, + alias: String, +} + +impl ContactSql { + /// Write this struct to the database + pub fn commit( + &self, + conn: &PooledConnection>, + ) -> Result<(), ContactsServiceStorageError> + { + diesel::insert_into(contacts::table) + .values(self.clone()) + .execute(conn)?; + Ok(()) + } + + /// Return all contacts + pub fn index( + conn: &PooledConnection>, + ) -> Result, ContactsServiceStorageError> { + Ok(contacts::table.load::(conn)?) + } + + /// Find a particular Contact, if it exists + pub fn find( + public_key: &[u8], + conn: &PooledConnection>, + ) -> Result + { + Ok(contacts::table + .filter(contacts::public_key.eq(public_key)) + .first::(conn)?) + } + + pub fn delete( + &self, + conn: &PooledConnection>, + ) -> Result<(), ContactsServiceStorageError> + { + let num_deleted = + diesel::delete(contacts::table.filter(contacts::public_key.eq(&self.public_key))).execute(conn)?; + + if num_deleted == 0 { + return Err(ContactsServiceStorageError::ValuesNotFound); + } + + Ok(()) + } + + pub fn update( + &self, + updated_contact: UpdateContact, + conn: &PooledConnection>, + ) -> Result + { + let num_updated = diesel::update(contacts::table.filter(contacts::public_key.eq(&self.public_key))) + .set(updated_contact) + .execute(conn)?; + + if num_updated == 0 { + return Err(ContactsServiceStorageError::UnexpectedResult( + "Database update error".to_string(), + )); + } + + Ok(ContactSql::find(&self.public_key, conn)?) + } +} + +/// Conversion from an Contact to the Sql datatype form +impl TryFrom for Contact { + type Error = ContactsServiceStorageError; + + fn try_from(o: ContactSql) -> Result { + Ok(Self { + public_key: PublicKey::from_vec(&o.public_key).map_err(|_| ContactsServiceStorageError::ConversionError)?, + alias: o.alias, + }) + } +} + +/// Conversion from an Contact to the Sql datatype form +impl From for ContactSql { + fn from(o: Contact) -> Self { + Self { + public_key: o.public_key.to_vec(), + alias: o.alias, + } + } +} + +#[derive(AsChangeset)] +#[table_name = "contacts"] +pub struct UpdateContact { + alias: Option, +} + +#[cfg(test)] +mod test { + use crate::contacts_service::storage::{ + database::Contact, + sqlite_db::{ContactSql, UpdateContact}, + }; + use diesel::{r2d2::ConnectionManager, Connection, SqliteConnection}; + use rand::rngs::OsRng; + use std::convert::TryFrom; + use tari_core::transactions::types::{PrivateKey, PublicKey}; + use tari_crypto::{ + keys::{PublicKey as PublicKeyTrait, SecretKey as SecretKeyTrait}, + tari_utilities::ByteArray, + }; + use tari_test_utils::{paths::with_temp_dir, random::string}; + + #[test] + fn test_crud() { + with_temp_dir(|dir_path| { + let db_name = format!("{}.sqlite3", string(8).as_str()); + let db_path = format!("{}/{}", dir_path.to_str().unwrap(), db_name); + + embed_migrations!("./migrations"); + let conn = + SqliteConnection::establish(&db_path).unwrap_or_else(|_| panic!("Error connecting to {}", db_path)); + + embedded_migrations::run_with_output(&conn, &mut std::io::stdout()).expect("Migration failed"); + + let manager = ConnectionManager::::new(db_path); + let pool = diesel::r2d2::Pool::builder().max_size(1).build(manager).unwrap(); + + let conn = pool.get().unwrap(); + conn.execute("PRAGMA foreign_keys = ON").unwrap(); + + let names = ["Alice".to_string(), "Bob".to_string(), "Carol".to_string()]; + + let mut contacts = Vec::new(); + for i in 0..names.len() { + let pub_key = PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)); + contacts.push(Contact { + alias: names[i].clone(), + public_key: pub_key, + }); + ContactSql::from(contacts[i].clone()).commit(&conn).unwrap(); + } + + let retrieved_contacts = ContactSql::index(&conn).unwrap(); + + for i in 0..contacts.len() { + assert!(retrieved_contacts + .iter() + .find(|v| v == &&ContactSql::from(contacts[i].clone())) + .is_some()); + } + + assert_eq!( + contacts[1], + Contact::try_from(ContactSql::find(&contacts[1].public_key.to_vec(), &conn).unwrap()).unwrap() + ); + + ContactSql::from(contacts[0].clone()).delete(&conn).unwrap(); + + let retrieved_contacts = ContactSql::index(&conn).unwrap(); + assert_eq!(retrieved_contacts.len(), 2); + + assert!(retrieved_contacts + .iter() + .find(|v| v == &&ContactSql::from(contacts[0].clone())) + .is_none()); + + let c = ContactSql::find(&contacts[1].public_key.to_vec(), &conn).unwrap(); + c.update( + UpdateContact { + alias: Some("Fred".to_string()), + }, + &conn, + ) + .unwrap(); + + let c_updated = ContactSql::find(&contacts[1].public_key.to_vec(), &conn).unwrap(); + assert_eq!(c_updated.alias, "Fred".to_string()); + }); + } +} diff --git a/base_layer/wallet/src/error.rs b/base_layer/wallet/src/error.rs new file mode 100644 index 0000000000..971b2634e0 --- /dev/null +++ b/base_layer/wallet/src/error.rs @@ -0,0 +1,71 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + contacts_service::error::ContactsServiceError, + output_manager_service::error::OutputManagerError, + storage::database::DbKey, + transaction_service::error::TransactionServiceError, +}; +use derive_error::Error; +use diesel::result::Error as DieselError; +use log::SetLoggerError; +use serde_json::Error as SerdeJsonError; +use tari_comms::{multiaddr, peer_manager::PeerManagerError}; +use tari_p2p::{initialization::CommsInitializationError, services::liveness::error::LivenessError}; + +#[derive(Debug, Error)] +pub enum WalletError { + CommsInitializationError(CommsInitializationError), + OutputManagerError(OutputManagerError), + TransactionServiceError(TransactionServiceError), + PeerManagerError(PeerManagerError), + MultiaddrError(multiaddr::Error), + WalletStorageError(WalletStorageError), + SetLoggerError(SetLoggerError), + ContactsServiceError(ContactsServiceError), + LivenessServiceError(LivenessError), +} + +#[derive(Debug, Error)] +pub enum WalletStorageError { + /// Tried to insert an output that already exists in the database + DuplicateContact, + /// This write operation is not supported for provided DbKey + OperationNotSupported, + /// Error converting a type + ConversionError, + /// Could not find all values specified for batch operation + ValuesNotFound, + SerdeJsonError(SerdeJsonError), + R2d2Error, + DieselError(DieselError), + DieselConnectionError(diesel::ConnectionError), + #[error(msg_embedded, no_from, non_std)] + DatabaseMigrationError(String), + #[error(non_std, no_from)] + ValueNotFound(DbKey), + #[error(msg_embedded, non_std, no_from)] + UnexpectedResult(String), + #[error(msg_embedded, non_std, no_from)] + BlockingTaskSpawnError(String), +} diff --git a/base_layer/wallet/src/lib.rs b/base_layer/wallet/src/lib.rs index f541eadca3..458099d892 100644 --- a/base_layer/wallet/src/lib.rs +++ b/base_layer/wallet/src/lib.rs @@ -1,17 +1,26 @@ +#![recursion_limit = "1024"] #![feature(drain_filter)] - -#[macro_use] -extern crate diesel; -#[macro_use] -extern crate diesel_migrations; +#![feature(type_alias_impl_trait)] #[macro_use] mod macros; +pub mod contacts_service; +pub mod error; pub mod output_manager_service; -pub mod schema; -pub mod text_message_service; +pub mod storage; pub mod transaction_service; pub mod types; +pub mod util; pub mod wallet; +#[cfg(feature = "test_harness")] +pub mod testnet_utils; + pub use wallet::Wallet; + +#[macro_use] +extern crate diesel; +#[macro_use] +extern crate diesel_migrations; +pub mod schema; +// pub mod text_message_service; diff --git a/base_layer/wallet/src/macros.rs b/base_layer/wallet/src/macros.rs index 0be261e386..38fbaef9eb 100644 --- a/base_layer/wallet/src/macros.rs +++ b/base_layer/wallet/src/macros.rs @@ -24,10 +24,25 @@ macro_rules! acquire_lock { ($e:expr, $m:ident) => { match $e.$m() { Ok(lock) => lock, - Err(poisoned) => poisoned.into_inner(), + Err(poisoned) => { + log::warn!(target: "wallet", "Lock has been POISONED and will be silently recovered"); + poisoned.into_inner() + }, } }; ($e:expr) => { acquire_lock!($e, lock) }; } + +macro_rules! acquire_write_lock { + ($e:expr) => { + acquire_lock!($e, write) + }; +} + +macro_rules! acquire_read_lock { + ($e:expr) => { + acquire_lock!($e, read) + }; +} diff --git a/applications/grpc_wallet/build.rs b/base_layer/wallet/src/output_manager_service/config.rs similarity index 83% rename from applications/grpc_wallet/build.rs rename to base_layer/wallet/src/output_manager_service/config.rs index 4054b842da..4cdf208da2 100644 --- a/applications/grpc_wallet/build.rs +++ b/base_layer/wallet/src/output_manager_service/config.rs @@ -1,4 +1,4 @@ -// Copyright 2019. The Tari Project +// Copyright 2020. The Tari Project // // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the // following conditions are met: @@ -20,11 +20,15 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -fn main() { - // Build wallet - tower_grpc_build::Config::new() - .enable_server(true) - .enable_client(true) - .build(&["proto/wallet_rpc.proto"], &["proto/"]) - .unwrap_or_else(|e| panic!("protobuf compilation failed: {}", e)); +#[derive(Clone)] +pub struct OutputManagerServiceConfig { + pub base_node_query_timeout_in_secs: u64, +} + +impl Default for OutputManagerServiceConfig { + fn default() -> Self { + Self { + base_node_query_timeout_in_secs: 30, + } + } } diff --git a/base_layer/wallet/src/output_manager_service/error.rs b/base_layer/wallet/src/output_manager_service/error.rs index 8498d06638..42b9c36a95 100644 --- a/base_layer/wallet/src/output_manager_service/error.rs +++ b/base_layer/wallet/src/output_manager_service/error.rs @@ -20,28 +20,79 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::output_manager_service::storage::database::DbKey; use derive_error::Error; -use tari_core::transaction_protocol::TransactionProtocolError; -use tari_utilities::ByteArrayError; +use diesel::result::Error as DieselError; +use tari_comms_dht::outbound::DhtOutboundError; +use tari_core::transactions::{transaction::TransactionError, transaction_protocol::TransactionProtocolError}; +use tari_crypto::tari_utilities::ByteArrayError; +use tari_key_manager::{key_manager::KeyManagerError, mnemonic::MnemonicError}; +use tari_service_framework::reply_channel::TransportChannelError; +use time::OutOfRangeError; -#[derive(Debug, Error, PartialEq)] +#[derive(Debug, Error)] pub enum OutputManagerError { #[error(msg_embedded, no_from, non_std)] BuildError(String), ByteArrayError(ByteArrayError), TransactionProtocolError(TransactionProtocolError), - /// If an pending transaction does not exist to be confirmed - PendingTransactionNotFound, + TransportChannelError(TransportChannelError), + OutOfRangeError(OutOfRangeError), + OutputManagerStorageError(OutputManagerStorageError), + MnemonicError(MnemonicError), + KeyManagerError(KeyManagerError), + TransactionError(TransactionError), + DhtOutboundError(DhtOutboundError), + #[error(msg_embedded, no_from, non_std)] + ConversionError(String), /// Not all the transaction inputs and outputs are present to be confirmed IncompleteTransaction, - /// Not enough funds to fulfill transaction + /// Not enough funds to fulfil transaction NotEnoughFunds, /// Output already exists DuplicateOutput, /// Error sending a message to the public API ApiSendFailed, - /// Error receiving a message from the publcic API + /// Error receiving a message from the public API ApiReceiveFailed, /// API returned something unexpected. UnexpectedApiResponse, + /// Invalid config provided to Output Manager + InvalidConfig, + /// The response received from another service is an incorrect variant + InvalidResponseError, + /// No Base Node public key has been provided for this service to use for contacting a base node + NoBaseNodeKeysProvided, + /// An error occured sending an event out on the event stream + EventStreamError, +} + +#[derive(Debug, Error, PartialEq)] +pub enum OutputManagerStorageError { + /// Tried to insert an output that already exists in the database + DuplicateOutput, + #[error(non_std, no_from)] + ValueNotFound(DbKey), + #[error(msg_embedded, non_std, no_from)] + UnexpectedResult(String), + /// If an pending transaction does not exist to be confirmed + PendingTransactionNotFound, + /// This write operation is not supported for provided DbKey + OperationNotSupported, + /// Could not find all values specified for batch operation + ValuesNotFound, + /// Error converting a type + ConversionError, + /// Output has already been spent + OutputAlreadySpent, + /// Key Manager not initialized + KeyManagerNotInitialized, + OutOfRangeError(OutOfRangeError), + R2d2Error, + DieselError(DieselError), + DieselConnectionError(diesel::ConnectionError), + #[error(msg_embedded, no_from, non_std)] + DatabaseMigrationError(String), + #[error(msg_embedded, non_std, no_from)] + BlockingTaskSpawnError(String), } diff --git a/base_layer/wallet/src/output_manager_service/handle.rs b/base_layer/wallet/src/output_manager_service/handle.rs new file mode 100644 index 0000000000..18e3a369ed --- /dev/null +++ b/base_layer/wallet/src/output_manager_service/handle.rs @@ -0,0 +1,274 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::output_manager_service::{ + error::OutputManagerError, + service::Balance, + storage::database::PendingTransactionOutputs, +}; +use futures::{stream::Fuse, StreamExt}; +use std::{collections::HashMap, time::Duration}; +use tari_broadcast_channel::Subscriber; +use tari_comms::types::CommsPublicKey; +use tari_core::transactions::{ + tari_amount::MicroTari, + transaction::{TransactionInput, TransactionOutput, UnblindedOutput}, + types::PrivateKey, + SenderTransactionProtocol, +}; +use tari_service_framework::reply_channel::SenderService; +use tower::Service; + +/// API Request enum +#[derive(Debug)] +pub enum OutputManagerRequest { + GetBalance, + AddOutput(UnblindedOutput), + GetRecipientKey((u64, MicroTari)), + GetCoinbaseKey((u64, MicroTari, u64)), + ConfirmTransaction((u64, Vec, Vec)), + PrepareToSendTransaction((MicroTari, MicroTari, Option, String)), + CancelTransaction(u64), + TimeoutTransactions(Duration), + GetPendingTransactions, + GetSpentOutputs, + GetUnspentOutputs, + GetInvalidOutputs, + GetSeedWords, + SetBaseNodePublicKey(CommsPublicKey), + SyncWithBaseNode, +} + +/// API Reply enum +pub enum OutputManagerResponse { + Balance(Balance), + OutputAdded, + RecipientKeyGenerated(PrivateKey), + OutputConfirmed, + TransactionConfirmed, + TransactionToSend(SenderTransactionProtocol), + TransactionCancelled, + TransactionsTimedOut, + PendingTransactions(HashMap), + SpentOutputs(Vec), + UnspentOutputs(Vec), + InvalidOutputs(Vec), + SeedWords(Vec), + BaseNodePublicKeySet, + StartedBaseNodeSync, +} + +/// Events that can be published on the Text Message Service Event Stream +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub enum OutputManagerEvent { + BaseNodeSyncRequestTimedOut(u64), + ReceiveBaseNodeResponse(u64), + Error(String), +} + +#[derive(Clone)] +pub struct OutputManagerHandle { + handle: SenderService>, + event_stream: Subscriber, +} + +impl OutputManagerHandle { + pub fn new( + handle: SenderService>, + event_stream: Subscriber, + ) -> Self + { + OutputManagerHandle { handle, event_stream } + } + + pub fn get_event_stream_fused(&self) -> Fuse> { + self.event_stream.clone().fuse() + } + + pub async fn add_output(&mut self, output: UnblindedOutput) -> Result<(), OutputManagerError> { + match self.handle.call(OutputManagerRequest::AddOutput(output)).await?? { + OutputManagerResponse::OutputAdded => Ok(()), + _ => Err(OutputManagerError::UnexpectedApiResponse), + } + } + + pub async fn get_balance(&mut self) -> Result { + match self.handle.call(OutputManagerRequest::GetBalance).await?? { + OutputManagerResponse::Balance(b) => Ok(b), + _ => Err(OutputManagerError::UnexpectedApiResponse), + } + } + + pub async fn get_recipient_spending_key( + &mut self, + tx_id: u64, + amount: MicroTari, + ) -> Result + { + match self + .handle + .call(OutputManagerRequest::GetRecipientKey((tx_id, amount))) + .await?? + { + OutputManagerResponse::RecipientKeyGenerated(k) => Ok(k), + _ => Err(OutputManagerError::UnexpectedApiResponse), + } + } + + pub async fn get_coinbase_spending_key( + &mut self, + tx_id: u64, + amount: MicroTari, + maturity_height: u64, + ) -> Result + { + match self + .handle + .call(OutputManagerRequest::GetCoinbaseKey((tx_id, amount, maturity_height))) + .await?? + { + OutputManagerResponse::RecipientKeyGenerated(k) => Ok(k), + _ => Err(OutputManagerError::UnexpectedApiResponse), + } + } + + pub async fn prepare_transaction_to_send( + &mut self, + amount: MicroTari, + fee_per_gram: MicroTari, + lock_height: Option, + message: String, + ) -> Result + { + match self + .handle + .call(OutputManagerRequest::PrepareToSendTransaction(( + amount, + fee_per_gram, + lock_height, + message, + ))) + .await?? + { + OutputManagerResponse::TransactionToSend(stp) => Ok(stp), + _ => Err(OutputManagerError::UnexpectedApiResponse), + } + } + + pub async fn confirm_transaction( + &mut self, + tx_id: u64, + spent_outputs: Vec, + received_outputs: Vec, + ) -> Result<(), OutputManagerError> + { + match self + .handle + .call(OutputManagerRequest::ConfirmTransaction(( + tx_id, + spent_outputs, + received_outputs, + ))) + .await?? + { + OutputManagerResponse::TransactionConfirmed => Ok(()), + _ => Err(OutputManagerError::UnexpectedApiResponse), + } + } + + pub async fn cancel_transaction(&mut self, tx_id: u64) -> Result<(), OutputManagerError> { + match self + .handle + .call(OutputManagerRequest::CancelTransaction(tx_id)) + .await?? + { + OutputManagerResponse::TransactionCancelled => Ok(()), + _ => Err(OutputManagerError::UnexpectedApiResponse), + } + } + + pub async fn timeout_transactions(&mut self, period: Duration) -> Result<(), OutputManagerError> { + match self + .handle + .call(OutputManagerRequest::TimeoutTransactions(period)) + .await?? + { + OutputManagerResponse::TransactionsTimedOut => Ok(()), + _ => Err(OutputManagerError::UnexpectedApiResponse), + } + } + + pub async fn get_pending_transactions( + &mut self, + ) -> Result, OutputManagerError> { + match self.handle.call(OutputManagerRequest::GetPendingTransactions).await?? { + OutputManagerResponse::PendingTransactions(p) => Ok(p), + _ => Err(OutputManagerError::UnexpectedApiResponse), + } + } + + pub async fn get_spent_outputs(&mut self) -> Result, OutputManagerError> { + match self.handle.call(OutputManagerRequest::GetSpentOutputs).await?? { + OutputManagerResponse::SpentOutputs(s) => Ok(s), + _ => Err(OutputManagerError::UnexpectedApiResponse), + } + } + + pub async fn get_unspent_outputs(&mut self) -> Result, OutputManagerError> { + match self.handle.call(OutputManagerRequest::GetUnspentOutputs).await?? { + OutputManagerResponse::UnspentOutputs(s) => Ok(s), + _ => Err(OutputManagerError::UnexpectedApiResponse), + } + } + + pub async fn get_invalid_outputs(&mut self) -> Result, OutputManagerError> { + match self.handle.call(OutputManagerRequest::GetInvalidOutputs).await?? { + OutputManagerResponse::InvalidOutputs(s) => Ok(s), + _ => Err(OutputManagerError::UnexpectedApiResponse), + } + } + + pub async fn get_seed_words(&mut self) -> Result, OutputManagerError> { + match self.handle.call(OutputManagerRequest::GetSeedWords).await?? { + OutputManagerResponse::SeedWords(s) => Ok(s), + _ => Err(OutputManagerError::UnexpectedApiResponse), + } + } + + pub async fn set_base_node_public_key(&mut self, public_key: CommsPublicKey) -> Result<(), OutputManagerError> { + match self + .handle + .call(OutputManagerRequest::SetBaseNodePublicKey(public_key)) + .await?? + { + OutputManagerResponse::BaseNodePublicKeySet => Ok(()), + _ => Err(OutputManagerError::UnexpectedApiResponse), + } + } + + pub async fn sync_with_base_node(&mut self) -> Result<(), OutputManagerError> { + match self.handle.call(OutputManagerRequest::SyncWithBaseNode).await?? { + OutputManagerResponse::StartedBaseNodeSync => Ok(()), + _ => Err(OutputManagerError::UnexpectedApiResponse), + } + } +} diff --git a/base_layer/wallet/src/output_manager_service/mod.rs b/base_layer/wallet/src/output_manager_service/mod.rs index 65e8e13e5f..0045b0f483 100644 --- a/base_layer/wallet/src/output_manager_service/mod.rs +++ b/base_layer/wallet/src/output_manager_service/mod.rs @@ -20,5 +20,133 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::output_manager_service::{handle::OutputManagerHandle, service::OutputManagerService}; + +use crate::output_manager_service::{ + config::OutputManagerServiceConfig, + storage::database::{OutputManagerBackend, OutputManagerDatabase}, +}; +use futures::{future, Future, Stream, StreamExt}; +use log::*; +use std::sync::Arc; +use tari_broadcast_channel::bounded; +use tari_comms_dht::outbound::OutboundMessageRequester; +use tari_core::{base_node::proto::base_node as BaseNodeProto, transactions::types::CryptoFactories}; +use tari_p2p::{ + comms_connector::PeerMessage, + domain_message::DomainMessage, + services::utils::{map_decode, ok_or_skip_result}, + tari_message::TariMessageType, +}; +use tari_pubsub::TopicSubscriptionFactory; +use tari_service_framework::{ + handles::ServiceHandlesFuture, + reply_channel, + ServiceInitializationError, + ServiceInitializer, +}; +use tari_shutdown::ShutdownSignal; +use tokio::runtime; + +pub mod config; pub mod error; -pub mod output_manager_service; +pub mod handle; +#[allow(unused_assignments)] +pub mod service; +pub mod storage; + +const LOG_TARGET: &str = "wallet::output_manager_service::initializer"; + +pub type TxId = u64; + +pub struct OutputManagerServiceInitializer +where T: OutputManagerBackend +{ + config: OutputManagerServiceConfig, + subscription_factory: Arc>>, + backend: Option, + factories: CryptoFactories, +} + +impl OutputManagerServiceInitializer +where T: OutputManagerBackend +{ + pub fn new( + config: OutputManagerServiceConfig, + subscription_factory: Arc>>, + backend: T, + factories: CryptoFactories, + ) -> Self + { + Self { + config, + subscription_factory, + backend: Some(backend), + factories, + } + } + + fn base_node_response_stream(&self) -> impl Stream> { + self.subscription_factory + .get_subscription(TariMessageType::BaseNodeResponse) + .map(map_decode::) + .filter_map(ok_or_skip_result) + } +} + +impl ServiceInitializer for OutputManagerServiceInitializer +where T: OutputManagerBackend + 'static +{ + type Future = impl Future>; + + fn initialize( + &mut self, + executor: runtime::Handle, + handles_fut: ServiceHandlesFuture, + shutdown: ShutdownSignal, + ) -> Self::Future + { + let base_node_response_stream = self.base_node_response_stream(); + + let (sender, receiver) = reply_channel::unbounded(); + let (publisher, subscriber) = bounded(100); + + let oms_handle = OutputManagerHandle::new(sender, subscriber); + + // Register handle before waiting for handles to be ready + handles_fut.register(oms_handle); + + let backend = self + .backend + .take() + .expect("Cannot start Output Manager Service without setting a storage backend"); + let factories = self.factories.clone(); + let config = self.config.clone(); + + executor.spawn(async move { + let handles = handles_fut.await; + + let outbound_message_service = handles + .get_handle::() + .expect("OMS handle required for Output Manager Service"); + + let service = OutputManagerService::new( + config, + outbound_message_service, + receiver, + base_node_response_stream, + OutputManagerDatabase::new(backend), + publisher, + factories, + ) + .await + .expect("Could not initialize Output Manager Service") + .start(); + + futures::pin_mut!(service); + future::select(service, shutdown).await; + info!(target: LOG_TARGET, "Output manager service shutdown"); + }); + future::ready(Ok(())) + } +} diff --git a/base_layer/wallet/src/output_manager_service/output_manager_service.rs b/base_layer/wallet/src/output_manager_service/output_manager_service.rs deleted file mode 100644 index c83512dca9..0000000000 --- a/base_layer/wallet/src/output_manager_service/output_manager_service.rs +++ /dev/null @@ -1,676 +0,0 @@ -// Copyright 2019. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::{ - output_manager_service::error::OutputManagerError, - types::{HashDigest, KeyDigest, TransactionRng}, -}; -use chrono::{Duration as ChronoDuration, NaiveDateTime, Utc}; -use crossbeam_channel as channel; -use std::{collections::HashMap, sync::Mutex, time::Duration}; - -use log::*; -use std::sync::Arc; -use tari_core::{ - fee::Fee, - tari_amount::MicroTari, - transaction::{OutputFeatures, TransactionInput, TransactionOutput, UnblindedOutput}, - types::{PrivateKey, COMMITMENT_FACTORY, PROVER}, - SenderTransactionProtocol, -}; -use tari_crypto::keys::SecretKey; -use tari_key_manager::keymanager::KeyManager; -use tari_p2p::{ - sync_services::{ - Service, - ServiceApiWrapper, - ServiceContext, - ServiceControlMessage, - ServiceError, - DEFAULT_API_TIMEOUT_MS, - }, - tari_message::TariMessageType, -}; - -const LOG_TARGET: &'static str = "base_layer::wallet::output_manager_service"; - -/// This service will manage a wallet's available outputs and the key manager that produces the keys for these outputs. -/// The service will assemble transactions to be sent from the wallets available outputs and provide keys to receive -/// outputs. When the outputs are detected on the blockchain the Transaction service will call this Service to confirm -/// them to be moved to the spent and unspent output lists respectively. -pub struct OutputManagerService { - key_manager: Mutex>, - unspent_outputs: Vec, - spent_outputs: Vec, - pending_transactions: HashMap, - api: ServiceApiWrapper, -} - -impl OutputManagerService { - pub fn new(master_key: PrivateKey, branch_seed: String, primary_key_index: usize) -> OutputManagerService { - OutputManagerService { - key_manager: Mutex::new(KeyManager::::from( - master_key, - branch_seed, - primary_key_index, - )), - unspent_outputs: Vec::new(), - spent_outputs: Vec::new(), - pending_transactions: HashMap::new(), - api: Self::setup_api(), - } - } - - /// Return this service's API - pub fn get_api(&self) -> Arc { - self.api.get_api() - } - - fn setup_api() -> ServiceApiWrapper { - let (api_sender, service_receiver) = channel::bounded(0); - let (service_sender, api_receiver) = channel::bounded(0); - - let api = Arc::new(OutputManagerServiceApi::new(api_sender, api_receiver)); - ServiceApiWrapper::new(service_receiver, service_sender, api) - } - - /// Add an unblinded output to the unspent outputs list - pub fn add_output(&mut self, output: UnblindedOutput) -> Result<(), OutputManagerError> { - // Check it is not already present in the various output sets - if self.contains_output(&output) { - return Err(OutputManagerError::DuplicateOutput); - } - - self.unspent_outputs.push(output); - - Ok(()) - } - - pub fn get_balance(&self) -> MicroTari { - self.unspent_outputs - .iter() - .fold(MicroTari::from(0), |acc, x| acc + x.value) - } - - /// Request a spending key to be used to accept a transaction from a sender. - pub fn get_recipient_spending_key( - &mut self, - tx_id: u64, - amount: MicroTari, - ) -> Result - { - let mut km = acquire_lock!(self.key_manager); - - let key = km.next_key()?.k; - - self.pending_transactions.insert(tx_id, PendingTransactionOutputs { - tx_id, - outputs_to_be_spent: Vec::new(), - outputs_to_be_received: vec![UnblindedOutput { - value: amount, - spending_key: key.clone(), - features: OutputFeatures::default(), - }], - timestamp: Utc::now().naive_utc(), - }); - - Ok(key) - } - - /// Confirm the reception of an expect transaction output. This will be called by the Transaction Service when it - /// detects the output on the blockchain - pub fn confirm_received_transaction_output( - &mut self, - tx_id: u64, - received_output: &TransactionOutput, - ) -> Result<(), OutputManagerError> - { - let pending_transaction = self - .pending_transactions - .get_mut(&tx_id) - .ok_or(OutputManagerError::PendingTransactionNotFound)?; - - // Assumption: We are only allowing a single output per receiver in the current transaction protocols. - if pending_transaction.outputs_to_be_received.len() != 1 || - pending_transaction.outputs_to_be_received[0] - .as_transaction_input(&COMMITMENT_FACTORY, OutputFeatures::default()) - .commitment != - received_output.commitment - { - return Err(OutputManagerError::IncompleteTransaction); - } - - self.unspent_outputs - .append(&mut pending_transaction.outputs_to_be_received); - let _ = self.pending_transactions.remove(&tx_id); - Ok(()) - } - - /// Prepare a Sender Transaction Protocol for the amount and fee_per_gram specified. If required a change output - /// will be produced. - pub fn prepare_transaction_to_send( - &mut self, - amount: MicroTari, - fee_per_gram: MicroTari, - lock_height: Option, - ) -> Result - { - let mut rng = TransactionRng::new().unwrap(); - let outputs = self.select_outputs(amount, fee_per_gram, UTXOSelectionStrategy::Smallest)?; - let total = outputs.iter().fold(MicroTari::from(0), |acc, x| acc + x.value); - - let offset = PrivateKey::random(&mut rng); - let nonce = PrivateKey::random(&mut rng); - - let mut builder = SenderTransactionProtocol::builder(1); - builder - .with_lock_height(lock_height.unwrap_or(0)) - .with_fee_per_gram(fee_per_gram) - .with_offset(offset.clone()) - .with_private_nonce(nonce.clone()) - .with_amount(0, amount); - - for uo in outputs.iter() { - builder.with_input( - uo.as_transaction_input(&COMMITMENT_FACTORY, OutputFeatures::default()), - uo.clone(), - ); - } - - let fee_without_change = Fee::calculate(fee_per_gram, outputs.len(), 1); - let mut change_key: Option = None; - // If the input values > the amount to be sent + fees_without_change then we will need to include a change - // output - if total > amount + fee_without_change { - let mut km = acquire_lock!(self.key_manager); - let key = km.next_key()?.k; - change_key = Some(key.clone()); - builder.with_change_secret(key); - } - - let stp = builder - .build::(&PROVER, &COMMITMENT_FACTORY) - .map_err(|e| OutputManagerError::BuildError(e.message))?; - - // The Transaction Protocol built successfully so we will pull the unspent outputs out of the unspent list and - // store them until the transaction times out OR is confirmed - let outputs_to_be_spent = self - .unspent_outputs - .drain_filter(|uo| outputs.iter().any(|o| uo.spending_key == o.spending_key)) - .collect(); - - let mut pending_transaction = PendingTransactionOutputs { - tx_id: stp.get_tx_id()?, - outputs_to_be_spent, - outputs_to_be_received: Vec::new(), - timestamp: Utc::now().naive_utc(), - }; - - // If a change output was created add it to the pending_outputs list. - if let Some(key) = change_key { - pending_transaction.outputs_to_be_received.push(UnblindedOutput { - value: stp.get_amount_to_self()?, - spending_key: key, - features: OutputFeatures::default(), - }) - } - - self.pending_transactions - .insert(pending_transaction.tx_id, pending_transaction); - - Ok(stp) - } - - /// Confirm that a received or sent transaction and its outputs have been detected on the base chain. This will - /// usually be called by the Transaction Service which monitors the base chain. - pub fn confirm_sent_transaction( - &mut self, - tx_id: u64, - spent_outputs: &Vec, - received_outputs: &Vec, - ) -> Result<(), OutputManagerError> - { - let pending_transaction = self - .pending_transactions - .get_mut(&tx_id) - .ok_or(OutputManagerError::PendingTransactionNotFound)?; - - // Check that the set of TransactionInputs and TransactionOutputs provided contain all the spent and received - // outputs in the PendingTransaction - // Assumption: There will only be ONE extra output which belongs to the receiver - if spent_outputs.len() != pending_transaction.outputs_to_be_spent.len() || - !pending_transaction.outputs_to_be_spent.iter().fold(true, |acc, i| { - acc && spent_outputs.iter().any(|o| { - o.commitment == - i.as_transaction_input(&COMMITMENT_FACTORY, OutputFeatures::default()) - .commitment - }) - }) || - received_outputs.len() - 1 != pending_transaction.outputs_to_be_received.len() || - !pending_transaction.outputs_to_be_received.iter().fold(true, |acc, i| { - acc && received_outputs.iter().any(|o| { - o.commitment == - i.as_transaction_input(&COMMITMENT_FACTORY, OutputFeatures::default()) - .commitment - }) - }) - { - return Err(OutputManagerError::IncompleteTransaction); - } - - self.unspent_outputs - .append(&mut pending_transaction.outputs_to_be_received); - self.spent_outputs.append(&mut pending_transaction.outputs_to_be_spent); - let _ = self.pending_transactions.remove(&tx_id); - - Ok(()) - } - - /// Cancel a pending transaction and place the encumbered outputs back into the unspent pool - pub fn cancel_transaction(&mut self, tx_id: u64) -> Result<(), OutputManagerError> { - let pending_transaction = self - .pending_transactions - .get_mut(&tx_id) - .ok_or(OutputManagerError::PendingTransactionNotFound)?; - - self.unspent_outputs - .append(&mut pending_transaction.outputs_to_be_spent); - - Ok(()) - } - - /// Go through the pending transaction and if any have existed longer than the specified duration, cancel them - pub fn timeout_pending_transactions(&mut self, period: ChronoDuration) -> Result<(), OutputManagerError> { - let mut transactions_to_be_cancelled = Vec::new(); - for (tx_id, pt) in self.pending_transactions.iter() { - if pt.timestamp + period < Utc::now().naive_utc() { - transactions_to_be_cancelled.push(tx_id.clone()); - } - } - - for t in transactions_to_be_cancelled { - self.cancel_transaction(t.clone())? - } - - Ok(()) - } - - /// Select which outputs to use to send a transaction of the specified amount. Use the specified selection strategy - /// to choose the outputs - fn select_outputs( - &mut self, - amount: MicroTari, - fee_per_gram: MicroTari, - strategy: UTXOSelectionStrategy, - ) -> Result, OutputManagerError> - { - let mut outputs = Vec::new(); - let mut total = MicroTari::from(0); - let mut fee_without_change = MicroTari::from(0); - let mut fee_with_change = MicroTari::from(0); - - match strategy { - UTXOSelectionStrategy::Smallest => { - self.unspent_outputs.sort(); - for o in self.unspent_outputs.iter() { - outputs.push(o.clone()); - total += o.value.clone(); - // I am assuming that the only output will be the payment output and change if required - fee_without_change = Fee::calculate(fee_per_gram, outputs.len(), 1); - fee_with_change = Fee::calculate(fee_per_gram, outputs.len(), 2); - - if total == amount + fee_without_change || total >= amount + fee_with_change { - break; - } - } - }, - } - - if (total != amount + fee_without_change) && (total < amount + fee_with_change) { - return Err(OutputManagerError::NotEnoughFunds); - } - - Ok(outputs) - } - - pub fn pending_transactions(&self) -> &HashMap { - &self.pending_transactions - } - - pub fn spent_outputs(&self) -> &Vec { - &self.spent_outputs - } - - pub fn unspent_outputs(&self) -> &Vec { - &self.unspent_outputs - } - - /// Utility function to determine if an output exists in the spent, unspent or pending output sets - pub fn contains_output(&self, output: &UnblindedOutput) -> bool { - self.unspent_outputs - .iter() - .any(|o| o.value == output.value && o.spending_key == output.spending_key) || - self.spent_outputs - .iter() - .any(|o| o.value == output.value && o.spending_key == output.spending_key) || - self.pending_transactions.values().fold(false, |acc, pt| { - acc || pt - .outputs_to_be_spent - .iter() - .chain(pt.outputs_to_be_received.iter()) - .any(|o| o.value == output.value && o.spending_key == output.spending_key) - }) - } - - /// This handler is called when the Service executor loops receives an API request - fn handle_api_message(&mut self, msg: OutputManagerApiRequest) -> Result<(), ServiceError> { - debug!(target: LOG_TARGET, "[{}] Received API message", self.get_name(),); - let resp = match msg { - OutputManagerApiRequest::AddOutput(uo) => { - self.add_output(uo).map(|_| OutputManagerApiResponse::OutputAdded) - }, - OutputManagerApiRequest::GetBalance => Ok(OutputManagerApiResponse::Balance(self.get_balance())), - OutputManagerApiRequest::GetRecipientKey((tx_id, amount)) => self - .get_recipient_spending_key(tx_id, amount) - .map(|k| OutputManagerApiResponse::RecipientKeyGenerated(k)), - OutputManagerApiRequest::PrepareToSendTransaction((amount, fee_per_gram, lock_height)) => self - .prepare_transaction_to_send(amount, fee_per_gram, lock_height) - .map(|stp| OutputManagerApiResponse::TransactionToSend(stp)), - OutputManagerApiRequest::ConfirmReceivedOutput((tx_id, output)) => self - .confirm_received_transaction_output(tx_id, &output) - .map(|_| OutputManagerApiResponse::OutputConfirmed), - OutputManagerApiRequest::ConfirmSentTransaction((tx_id, spent_outputs, received_outputs)) => self - .confirm_sent_transaction(tx_id, &spent_outputs, &received_outputs) - .map(|_| OutputManagerApiResponse::TransactionConfirmed), - OutputManagerApiRequest::CancelTransaction(tx_id) => self - .cancel_transaction(tx_id) - .map(|_| OutputManagerApiResponse::TransactionCancelled), - OutputManagerApiRequest::TimeoutTransactions(period) => self - .timeout_pending_transactions(period) - .map(|_| OutputManagerApiResponse::TransactionsTimedOut), - }; - debug!(target: LOG_TARGET, "[{}] Replying to API", self.get_name()); - self.api - .send_reply(resp) - .map_err(ServiceError::internal_service_error()) - } -} - -/// Holds the outputs that have been selected for a given pending transaction waiting for confirmation -pub struct PendingTransactionOutputs { - pub tx_id: u64, - pub outputs_to_be_spent: Vec, - pub outputs_to_be_received: Vec, - pub timestamp: NaiveDateTime, -} - -/// Different UTXO selection strategies for choosing which UTXO's are used to fulfill a transaction -/// TODO Investigate and implement more optimal strategies -pub enum UTXOSelectionStrategy { - // Start from the smallest UTXOs and work your way up until the amount is covered. Main benefit is removing small - // UTXOs from the blockchain, con is that it costs more in fees - Smallest, -} - -/// The Domain Service trait implementation for the TestMessageService -impl Service for OutputManagerService { - fn get_name(&self) -> String { - "Output Manager service".to_string() - } - - fn get_message_types(&self) -> Vec { - Vec::new() - } - - /// Function called by the Service Executor in its own thread. This function polls for both API request and Comms - /// layer messages from the Message Broker - fn execute(&mut self, context: ServiceContext) -> Result<(), ServiceError> { - debug!(target: LOG_TARGET, "Starting Output Manager Service executor"); - loop { - if let Some(msg) = context.get_control_message(Duration::from_millis(5)) { - match msg { - ServiceControlMessage::Shutdown => break, - } - } else { - } - if let Some(msg) = self - .api - .recv_timeout(Duration::from_millis(50)) - .map_err(ServiceError::internal_service_error())? - { - self.handle_api_message(msg)?; - } - } - - Ok(()) - } -} - -/// API Request enum -#[derive(Debug)] -pub enum OutputManagerApiRequest { - GetBalance, - AddOutput(UnblindedOutput), - GetRecipientKey((u64, MicroTari)), - ConfirmReceivedOutput((u64, TransactionOutput)), - ConfirmSentTransaction((u64, Vec, Vec)), - PrepareToSendTransaction((MicroTari, MicroTari, Option)), - CancelTransaction(u64), - TimeoutTransactions(ChronoDuration), -} - -/// API Reply enum -#[derive(Debug)] -pub enum OutputManagerApiResponse { - Balance(MicroTari), - OutputAdded, - RecipientKeyGenerated(PrivateKey), - OutputConfirmed, - TransactionConfirmed, - TransactionToSend(SenderTransactionProtocol), - TransactionCancelled, - TransactionsTimedOut, -} - -/// Result for all API requests -pub type OutputManagerApiResult = Result; - -/// The Output Manager service public API that other services and application will use to interact with this service. -/// The requests and responses are transmitted via channels into the Service Executor thread where this service is -/// running -pub struct OutputManagerServiceApi { - sender: channel::Sender, - receiver: channel::Receiver, - mutex: Mutex<()>, - timeout: Duration, -} - -impl OutputManagerServiceApi { - fn new( - sender: channel::Sender, - receiver: channel::Receiver, - ) -> Self - { - Self { - sender, - receiver, - mutex: Mutex::new(()), - timeout: Duration::from_millis(DEFAULT_API_TIMEOUT_MS), - } - } - - pub fn add_output(&self, output: UnblindedOutput) -> Result<(), OutputManagerError> { - self.send_recv(OutputManagerApiRequest::AddOutput(output)) - .and_then(|resp| match resp { - OutputManagerApiResponse::OutputAdded => Ok(()), - _ => Err(OutputManagerError::UnexpectedApiResponse), - }) - } - - pub fn get_balance(&self) -> Result { - self.send_recv(OutputManagerApiRequest::GetBalance) - .and_then(|resp| match resp { - OutputManagerApiResponse::Balance(b) => Ok(b), - _ => Err(OutputManagerError::UnexpectedApiResponse), - }) - } - - pub fn get_recipient_spending_key(&self, tx_id: u64, amount: MicroTari) -> Result { - self.send_recv(OutputManagerApiRequest::GetRecipientKey((tx_id, amount))) - .and_then(|resp| match resp { - OutputManagerApiResponse::RecipientKeyGenerated(k) => Ok(k), - _ => Err(OutputManagerError::UnexpectedApiResponse), - }) - } - - pub fn prepare_transaction_to_send( - &self, - amount: MicroTari, - fee_per_gram: MicroTari, - lock_height: Option, - ) -> Result - { - self.send_recv(OutputManagerApiRequest::PrepareToSendTransaction(( - amount, - fee_per_gram, - lock_height, - ))) - .and_then(|resp| match resp { - OutputManagerApiResponse::TransactionToSend(stp) => Ok(stp), - _ => Err(OutputManagerError::UnexpectedApiResponse), - }) - } - - pub fn confirm_received_output(&self, tx_id: u64, output: TransactionOutput) -> Result<(), OutputManagerError> { - self.send_recv(OutputManagerApiRequest::ConfirmReceivedOutput((tx_id, output))) - .and_then(|resp| match resp { - OutputManagerApiResponse::OutputConfirmed => Ok(()), - _ => Err(OutputManagerError::UnexpectedApiResponse), - }) - } - - pub fn confirm_sent_transaction( - &self, - tx_id: u64, - spent_outputs: Vec, - received_outputs: Vec, - ) -> Result<(), OutputManagerError> - { - self.send_recv(OutputManagerApiRequest::ConfirmSentTransaction(( - tx_id, - spent_outputs, - received_outputs, - ))) - .and_then(|resp| match resp { - OutputManagerApiResponse::TransactionConfirmed => Ok(()), - _ => Err(OutputManagerError::UnexpectedApiResponse), - }) - } - - pub fn cancel_transaction(&self, tx_id: u64) -> Result<(), OutputManagerError> { - self.send_recv(OutputManagerApiRequest::CancelTransaction(tx_id)) - .and_then(|resp| match resp { - OutputManagerApiResponse::TransactionCancelled => Ok(()), - _ => Err(OutputManagerError::UnexpectedApiResponse), - }) - } - - pub fn timeout_transactions(&self, period: ChronoDuration) -> Result<(), OutputManagerError> { - self.send_recv(OutputManagerApiRequest::TimeoutTransactions(period)) - .and_then(|resp| match resp { - OutputManagerApiResponse::TransactionsTimedOut => Ok(()), - _ => Err(OutputManagerError::UnexpectedApiResponse), - }) - } - - fn send_recv(&self, msg: OutputManagerApiRequest) -> OutputManagerApiResult { - self.lock(|| -> OutputManagerApiResult { - self.sender.send(msg).map_err(|_| OutputManagerError::ApiSendFailed)?; - self.receiver - .recv_timeout(self.timeout.clone()) - .map_err(|_| OutputManagerError::ApiReceiveFailed)? - }) - } - - fn lock(&self, func: F) -> T - where F: FnOnce() -> T { - let lock = acquire_lock!(self.mutex); - let res = func(); - drop(lock); - res - } -} - -#[cfg(test)] -mod test { - use crate::output_manager_service::output_manager_service::{OutputManagerService, PendingTransactionOutputs}; - use chrono::Utc; - use rand::{CryptoRng, Rng, RngCore}; - use tari_core::{ - tari_amount::MicroTari, - transaction::UnblindedOutput, - types::{PrivateKey, PublicKey}, - }; - use tari_crypto::keys::{PublicKey as PublicKeyTrait, SecretKey}; - - fn make_output(rng: &mut R, val: MicroTari) -> UnblindedOutput { - let key = PrivateKey::random(rng); - UnblindedOutput::new(val, key, None) - } - - #[test] - fn test_contains_output_function() { - let mut rng = rand::OsRng::new().unwrap(); - let (secret_key, _public_key) = PublicKey::random_keypair(&mut rng); - - let mut oms = OutputManagerService::new(secret_key, "".to_string(), 0); - let mut balance = MicroTari::from(0); - for _i in 0..3 { - let uo = make_output(&mut rng.clone(), MicroTari::from(100 + rng.next_u64() % 1000)); - balance += uo.value.clone(); - oms.add_output(uo).unwrap(); - } - - let uo1 = make_output(&mut rng.clone(), MicroTari::from(100 + rng.next_u64() % 1000)); - - assert!(!oms.contains_output(&uo1)); - oms.add_output(uo1.clone()).unwrap(); - assert!(oms.contains_output(&uo1)); - - let uo2 = make_output(&mut rng.clone(), MicroTari::from(100 + rng.next_u64() % 1000)); - assert!(!oms.contains_output(&uo2)); - oms.spent_outputs.push(uo2.clone()); - assert!(oms.contains_output(&uo2)); - - let uo3 = make_output(&mut rng.clone(), MicroTari::from(100 + rng.next_u64() % 1000)); - assert!(!oms.contains_output(&uo3)); - oms.pending_transactions.insert(1, PendingTransactionOutputs { - tx_id: 1, - outputs_to_be_received: vec![uo3.clone()], - outputs_to_be_spent: Vec::new(), - timestamp: Utc::now().naive_utc(), - }); - assert!(oms.contains_output(&uo3)); - - assert_eq!(uo1.value + balance, oms.get_balance()); - } -} diff --git a/base_layer/wallet/src/output_manager_service/service.rs b/base_layer/wallet/src/output_manager_service/service.rs new file mode 100644 index 0000000000..b0870290a6 --- /dev/null +++ b/base_layer/wallet/src/output_manager_service/service.rs @@ -0,0 +1,722 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + output_manager_service::{ + config::OutputManagerServiceConfig, + error::OutputManagerError, + handle::{OutputManagerEvent, OutputManagerRequest, OutputManagerResponse}, + storage::database::{KeyManagerState, OutputManagerBackend, OutputManagerDatabase, PendingTransactionOutputs}, + TxId, + }, + types::{HashDigest, KeyDigest}, + util::futures::StateDelay, +}; +use futures::{future::BoxFuture, pin_mut, stream::FuturesUnordered, FutureExt, SinkExt, Stream, StreamExt}; +use log::*; +use rand::{rngs::OsRng, RngCore}; +use std::{ + collections::{HashMap, HashSet}, + convert::TryFrom, + fmt, + sync::Mutex, + time::Duration, +}; +use tari_broadcast_channel::Publisher; +use tari_comms::types::CommsPublicKey; +use tari_comms_dht::{ + domain_message::OutboundDomainMessage, + outbound::{OutboundEncryption, OutboundMessageRequester}, +}; +use tari_core::{ + base_node::proto::{ + base_node as BaseNodeProto, + base_node::{ + base_node_service_request::Request as BaseNodeRequestProto, + base_node_service_response::Response as BaseNodeResponseProto, + }, + }, + transactions::{ + fee::Fee, + tari_amount::MicroTari, + transaction::{OutputFeatures, TransactionInput, TransactionOutput, UnblindedOutput}, + types::{CryptoFactories, PrivateKey}, + SenderTransactionProtocol, + }, +}; +use tari_crypto::{keys::SecretKey as SecretKeyTrait, tari_utilities::hash::Hashable}; +use tari_key_manager::{ + key_manager::KeyManager, + mnemonic::{from_secret_key, MnemonicLanguage}, +}; +use tari_p2p::{domain_message::DomainMessage, tari_message::TariMessageType}; +use tari_service_framework::reply_channel; + +const LOG_TARGET: &str = "base_layer::wallet::output_manager_service"; + +/// This service will manage a wallet's available outputs and the key manager that produces the keys for these outputs. +/// The service will assemble transactions to be sent from the wallets available outputs and provide keys to receive +/// outputs. When the outputs are detected on the blockchain the Transaction service will call this Service to confirm +/// them to be moved to the spent and unspent output lists respectively. +pub struct OutputManagerService +where TBackend: OutputManagerBackend + 'static +{ + config: OutputManagerServiceConfig, + key_manager: Mutex>, + db: OutputManagerDatabase, + outbound_message_service: OutboundMessageRequester, + request_stream: + Option>>, + base_node_response_stream: Option, + factories: CryptoFactories, + base_node_public_key: Option, + pending_utxo_query_keys: HashSet, + event_publisher: Publisher, +} + +impl OutputManagerService +where + TBackend: OutputManagerBackend, + BNResponseStream: Stream>, +{ + pub async fn new( + config: OutputManagerServiceConfig, + outbound_message_service: OutboundMessageRequester, + request_stream: reply_channel::Receiver< + OutputManagerRequest, + Result, + >, + base_node_response_stream: BNResponseStream, + db: OutputManagerDatabase, + event_publisher: Publisher, + factories: CryptoFactories, + ) -> Result, OutputManagerError> + { + // Check to see if there is any persisted state, otherwise start fresh + let key_manager_state = match db.get_key_manager_state().await? { + None => { + let starting_state = KeyManagerState { + master_seed: PrivateKey::random(&mut OsRng), + branch_seed: "".to_string(), + primary_key_index: 0, + }; + db.set_key_manager_state(starting_state.clone()).await?; + starting_state + }, + Some(km) => km, + }; + + Ok(OutputManagerService { + config, + outbound_message_service, + key_manager: Mutex::new(KeyManager::::from( + key_manager_state.master_seed, + key_manager_state.branch_seed, + key_manager_state.primary_key_index, + )), + db, + request_stream: Some(request_stream), + base_node_response_stream: Some(base_node_response_stream), + factories, + base_node_public_key: None, + pending_utxo_query_keys: HashSet::new(), + event_publisher, + }) + } + + pub async fn start(mut self) -> Result<(), OutputManagerError> { + let request_stream = self + .request_stream + .take() + .expect("OutputManagerService initialized without request_stream") + .fuse(); + pin_mut!(request_stream); + + let base_node_response_stream = self + .base_node_response_stream + .take() + .expect("Output Manager Service initialized without base_node_response_stream") + .fuse(); + pin_mut!(base_node_response_stream); + + let mut utxo_query_timeout_futures: FuturesUnordered> = FuturesUnordered::new(); + + info!("Output Manager Service started"); + loop { + futures::select! { + request_context = request_stream.select_next_some() => { + let (request, reply_tx) = request_context.split(); + let _ = reply_tx.send(self.handle_request(request, &mut utxo_query_timeout_futures).await.or_else(|resp| { + error!(target: LOG_TARGET, "Error handling request: {:?}", resp); + Err(resp) + })).or_else(|resp| { + error!(target: LOG_TARGET, "Failed to send reply"); + Err(resp) + }); + }, + // Incoming messages from the Comms layer + msg = base_node_response_stream.select_next_some() => { + let (origin_public_key, inner_msg) = msg.into_origin_and_inner(); + let result = self.handle_base_node_response(inner_msg).await.or_else(|resp| { + error!(target: LOG_TARGET, "Error handling base node service response : {:?}", resp); + Err(resp) + }); + + if result.is_err() { + let _ = self.event_publisher + .send(OutputManagerEvent::Error( + "Error handling Base Node Response message".to_string(), + )) + .await; + } + } + utxo_hash = utxo_query_timeout_futures.select_next_some() => { + let _ = self.handle_utxo_query_timeout(utxo_hash, &mut utxo_query_timeout_futures).await.or_else(|resp| { + error!(target: LOG_TARGET, "Error handling UTXO query timeout : {:?}", resp); + Err(resp) + }); + } + complete => { + info!(target: LOG_TARGET, "Output manager service shutting down"); + break; + } + } + } + info!("Output Manager Service ended"); + Ok(()) + } + + /// This handler is called when the Service executor loops receives an API request + async fn handle_request( + &mut self, + request: OutputManagerRequest, + utxo_query_timeout_futures: &mut FuturesUnordered>, + ) -> Result + { + match request { + OutputManagerRequest::AddOutput(uo) => { + self.add_output(uo).await.map(|_| OutputManagerResponse::OutputAdded) + }, + OutputManagerRequest::GetBalance => self.get_balance().await.map(OutputManagerResponse::Balance), + OutputManagerRequest::GetRecipientKey((tx_id, amount)) => self + .get_recipient_spending_key(tx_id, amount) + .await + .map(OutputManagerResponse::RecipientKeyGenerated), + OutputManagerRequest::PrepareToSendTransaction((amount, fee_per_gram, lock_height, message)) => self + .prepare_transaction_to_send(amount, fee_per_gram, lock_height, message) + .await + .map(OutputManagerResponse::TransactionToSend), + OutputManagerRequest::ConfirmTransaction((tx_id, spent_outputs, received_outputs)) => self + .confirm_transaction(tx_id, &spent_outputs, &received_outputs) + .await + .map(|_| OutputManagerResponse::TransactionConfirmed), + OutputManagerRequest::CancelTransaction(tx_id) => self + .cancel_transaction(tx_id) + .await + .map(|_| OutputManagerResponse::TransactionCancelled), + OutputManagerRequest::TimeoutTransactions(period) => self + .timeout_pending_transactions(period) + .await + .map(|_| OutputManagerResponse::TransactionsTimedOut), + OutputManagerRequest::GetPendingTransactions => self + .fetch_pending_transaction_outputs() + .await + .map(OutputManagerResponse::PendingTransactions), + OutputManagerRequest::GetSpentOutputs => self + .fetch_spent_outputs() + .await + .map(OutputManagerResponse::SpentOutputs), + OutputManagerRequest::GetUnspentOutputs => self + .fetch_unspent_outputs() + .await + .map(OutputManagerResponse::UnspentOutputs), + OutputManagerRequest::GetSeedWords => self.get_seed_words().map(OutputManagerResponse::SeedWords), + OutputManagerRequest::GetCoinbaseKey((tx_id, amount, maturity_height)) => self + .get_coinbase_spending_key(tx_id, amount, maturity_height) + .await + .map(OutputManagerResponse::RecipientKeyGenerated), + OutputManagerRequest::SetBaseNodePublicKey(pk) => self + .set_base_node_public_key(pk, utxo_query_timeout_futures) + .await + .map(|_| OutputManagerResponse::BaseNodePublicKeySet), + OutputManagerRequest::SyncWithBaseNode => self + .query_unspent_outputs_status(utxo_query_timeout_futures) + .await + .map(|_| OutputManagerResponse::StartedBaseNodeSync), + OutputManagerRequest::GetInvalidOutputs => self + .fetch_invalid_outputs() + .await + .map(|o| OutputManagerResponse::InvalidOutputs(o)), + } + } + + /// Handle an incoming basenode response message + pub async fn handle_base_node_response( + &mut self, + response: BaseNodeProto::BaseNodeServiceResponse, + ) -> Result<(), OutputManagerError> + { + let request_key = response.request_key.clone(); + let response = match response.response { + Some(BaseNodeResponseProto::TransactionOutputs(outputs)) => Ok(outputs.outputs), + _ => Err(OutputManagerError::InvalidResponseError), + }?; + + // Only process requests with a request_key that we are expecting. + if !self.pending_utxo_query_keys.remove(&request_key) { + debug!( + target: LOG_TARGET, + "Ignoring Base Node Repsonse with unexpected request key" + ); + return Ok(()); + } + + // Construct a HashMap of all the unspent outputs + let unspent_outputs: Vec = self.db.get_unspent_outputs().await?; + + let mut output_hashes = HashMap::new(); + for uo in unspent_outputs.iter() { + let hash = uo.as_transaction_output(&self.factories)?.hash(); + output_hashes.insert(hash.clone(), uo.clone()); + } + + // Go through all the returned UTXOs and if they are in the hashmap remove them + for output in response.iter() { + let response_hash = TransactionOutput::try_from(output.clone()) + .map_err(|e| OutputManagerError::ConversionError(e))? + .hash(); + + let _ = output_hashes.remove(&response_hash); + } + + // If there are any remaining Unspent Outputs we will move them to the invalid collection + for (_k, v) in output_hashes { + self.db.invalidate_output(v).await?; + } + + debug!( + target: LOG_TARGET, + "Handled Base Node response for Query {}", request_key + ); + + self.event_publisher + .send(OutputManagerEvent::ReceiveBaseNodeResponse(request_key)) + .await + .map_err(|_| OutputManagerError::EventStreamError)?; + + Ok(()) + } + + /// Handle the timeout of a pending UTXO query. + pub async fn handle_utxo_query_timeout( + &mut self, + query_key: u64, + utxo_query_timeout_futures: &mut FuturesUnordered>, + ) -> Result<(), OutputManagerError> + { + if self.pending_utxo_query_keys.remove(&query_key) { + debug!(target: LOG_TARGET, "UTXO Query {} timed out", query_key); + self.query_unspent_outputs_status(utxo_query_timeout_futures).await?; + + self.event_publisher + .send(OutputManagerEvent::BaseNodeSyncRequestTimedOut(query_key)) + .await + .map_err(|_| OutputManagerError::EventStreamError)?; + } + Ok(()) + } + + /// Send queries to the base node to check the status of all unspent outputs. If the outputs are no longer + /// available their status will be updated in the wallet. + pub async fn query_unspent_outputs_status( + &mut self, + utxo_query_timeout_futures: &mut FuturesUnordered>, + ) -> Result<(), OutputManagerError> + { + match self.base_node_public_key.as_ref() { + None => return Err(OutputManagerError::NoBaseNodeKeysProvided), + Some(pk) => { + let unspent_outputs: Vec = self.db.get_unspent_outputs().await?; + let mut output_hashes = Vec::new(); + for uo in unspent_outputs.iter() { + let hash = uo.as_transaction_output(&self.factories)?.hash(); + output_hashes.push(hash.clone()); + } + + let request_key = OsRng.next_u64(); + + let request = BaseNodeRequestProto::FetchUtxos(BaseNodeProto::HashOutputs { outputs: output_hashes }); + let service_request = BaseNodeProto::BaseNodeServiceRequest { + request_key: request_key.clone(), + request: Some(request), + }; + self.outbound_message_service + .send_direct( + pk.clone(), + OutboundEncryption::EncryptForPeer, + OutboundDomainMessage::new(TariMessageType::BaseNodeRequest, service_request), + ) + .await?; + + self.pending_utxo_query_keys.insert(request_key.clone()); + let state_timeout = StateDelay::new( + Duration::from_secs(self.config.base_node_query_timeout_in_secs), + request_key.clone(), + ); + utxo_query_timeout_futures.push(state_timeout.delay().boxed()); + debug!( + target: LOG_TARGET, + "Output Manager Sync query ({}) sent to Base Node", request_key + ); + }, + } + Ok(()) + } + + /// Add an unblinded output to the unspent outputs list + pub async fn add_output(&mut self, output: UnblindedOutput) -> Result<(), OutputManagerError> { + Ok(self.db.add_unspent_output(output).await?) + } + + pub async fn get_balance(&self) -> Result { + Ok(self.db.get_balance().await?) + } + + /// Request a spending key to be used to accept a transaction from a sender. + pub async fn get_recipient_spending_key( + &mut self, + tx_id: TxId, + amount: MicroTari, + ) -> Result + { + let mut key = PrivateKey::default(); + { + let mut km = acquire_lock!(self.key_manager); + key = km.next_key()?.k; + } + + self.db.increment_key_index().await?; + self.db + .accept_incoming_pending_transaction(tx_id, amount, key.clone(), OutputFeatures::default()) + .await?; + + Ok(key) + } + + /// Request a spending key to be used to accept a coinbase output to be mined with the specified maturity height + /// # Arguments: + /// 'tx_id': the TxId that this coinbase transaction has been assigned + /// 'amount': Amount of MicroTari the coinbase has as a value + /// 'maturity_height': The block height at which this coinbase output becomes spendable + pub async fn get_coinbase_spending_key( + &mut self, + tx_id: TxId, + amount: MicroTari, + maturity_height: u64, + ) -> Result + { + let mut key = PrivateKey::default(); + + { + let mut km = acquire_lock!(self.key_manager); + key = km.next_key()?.k; + } + + self.db.increment_key_index().await?; + self.db + .accept_incoming_pending_transaction( + tx_id, + amount, + key.clone(), + OutputFeatures::create_coinbase(maturity_height), + ) + .await?; + + Ok(key) + } + + /// Confirm the reception of an expected transaction output. This will be called by the Transaction Service when it + /// detects the output on the blockchain + pub async fn confirm_received_transaction_output( + &mut self, + tx_id: u64, + received_output: &TransactionOutput, + ) -> Result<(), OutputManagerError> + { + let pending_transaction = self.db.fetch_pending_transaction_outputs(tx_id.clone()).await?; + + // Assumption: We are only allowing a single output per receiver in the current transaction protocols. + if pending_transaction.outputs_to_be_received.len() != 1 || + pending_transaction.outputs_to_be_received[0] + .as_transaction_input(&self.factories.commitment, OutputFeatures::default()) + .commitment != + received_output.commitment + { + return Err(OutputManagerError::IncompleteTransaction); + } + + self.db + .confirm_pending_transaction_outputs(pending_transaction.tx_id.clone()) + .await?; + + Ok(()) + } + + /// Prepare a Sender Transaction Protocol for the amount and fee_per_gram specified. If required a change output + /// will be produced. + pub async fn prepare_transaction_to_send( + &mut self, + amount: MicroTari, + fee_per_gram: MicroTari, + lock_height: Option, + message: String, + ) -> Result + { + let outputs = self + .select_outputs(amount, fee_per_gram, UTXOSelectionStrategy::Smallest) + .await?; + let total = outputs.iter().fold(MicroTari::from(0), |acc, x| acc + x.value); + + let offset = PrivateKey::random(&mut OsRng); + let nonce = PrivateKey::random(&mut OsRng); + + let mut builder = SenderTransactionProtocol::builder(1); + builder + .with_lock_height(lock_height.unwrap_or(0)) + .with_fee_per_gram(fee_per_gram) + .with_offset(offset.clone()) + .with_private_nonce(nonce.clone()) + .with_amount(0, amount) + .with_message(message); + + for uo in outputs.iter() { + builder.with_input( + uo.as_transaction_input(&self.factories.commitment, OutputFeatures::default()), + uo.clone(), + ); + } + + let fee_without_change = Fee::calculate(fee_per_gram, outputs.len(), 1); + let mut change_key: Option = None; + // If the input values > the amount to be sent + fees_without_change then we will need to include a change + // output + if total > amount + fee_without_change { + let mut key = PrivateKey::default(); + { + let mut km = acquire_lock!(self.key_manager); + key = km.next_key()?.k; + } + self.db.increment_key_index().await?; + change_key = Some(key.clone()); + builder.with_change_secret(key); + } + + let stp = builder + .build::(&self.factories) + .map_err(|e| OutputManagerError::BuildError(e.message))?; + + // If a change output was created add it to the pending_outputs list. + let change_output = match change_key { + Some(key) => Some(UnblindedOutput { + value: stp.get_amount_to_self()?, + spending_key: key, + features: OutputFeatures::default(), + }), + None => None, + }; + + // The Transaction Protocol built successfully so we will pull the unspent outputs out of the unspent list and + // store them until the transaction times out OR is confirmed + self.db + .encumber_outputs(stp.get_tx_id()?, outputs, change_output) + .await?; + + Ok(stp) + } + + /// Confirm that a received or sent transaction and its outputs have been detected on the base chain. The inputs and + /// outputs are checked to see that they match what the stored PendingTransaction contains. This will + /// be called by the Transaction Service which monitors the base chain. + pub async fn confirm_transaction( + &mut self, + tx_id: u64, + inputs: &[TransactionInput], + outputs: &[TransactionOutput], + ) -> Result<(), OutputManagerError> + { + let pending_transaction = self.db.fetch_pending_transaction_outputs(tx_id.clone()).await?; + + // Check that outputs to be spent can all be found in the provided transaction inputs + let mut inputs_confirmed = true; + for output_to_spend in pending_transaction.outputs_to_be_spent.iter() { + let input_to_check = output_to_spend + .clone() + .as_transaction_input(&self.factories.commitment, OutputFeatures::default()); + inputs_confirmed = + inputs_confirmed && inputs.iter().any(|input| input.commitment == input_to_check.commitment); + } + + // Check that outputs to be received can all be found in the provided transaction outputs + let mut outputs_confirmed = true; + for output_to_receive in pending_transaction.outputs_to_be_received.iter() { + let output_to_check = output_to_receive + .clone() + .as_transaction_input(&self.factories.commitment, OutputFeatures::default()); + outputs_confirmed = outputs_confirmed && + outputs + .iter() + .any(|output| output.commitment == output_to_check.commitment); + } + + if !inputs_confirmed || !outputs_confirmed { + return Err(OutputManagerError::IncompleteTransaction); + } + + self.db + .confirm_pending_transaction_outputs(pending_transaction.tx_id) + .await?; + + Ok(()) + } + + /// Cancel a pending transaction and place the encumbered outputs back into the unspent pool + pub async fn cancel_transaction(&mut self, tx_id: u64) -> Result<(), OutputManagerError> { + Ok(self.db.cancel_pending_transaction_outputs(tx_id).await?) + } + + /// Go through the pending transaction and if any have existed longer than the specified duration, cancel them + pub async fn timeout_pending_transactions(&mut self, period: Duration) -> Result<(), OutputManagerError> { + Ok(self.db.timeout_pending_transaction_outputs(period).await?) + } + + /// Select which outputs to use to send a transaction of the specified amount. Use the specified selection strategy + /// to choose the outputs + async fn select_outputs( + &mut self, + amount: MicroTari, + fee_per_gram: MicroTari, + strategy: UTXOSelectionStrategy, + ) -> Result, OutputManagerError> + { + let mut outputs = Vec::new(); + let mut total = MicroTari::from(0); + let mut fee_without_change = MicroTari::from(0); + let mut fee_with_change = MicroTari::from(0); + + let uo = self.db.fetch_sorted_unspent_outputs().await?; + + match strategy { + UTXOSelectionStrategy::Smallest => { + for o in uo.iter() { + outputs.push(o.clone()); + total += o.value; + // I am assuming that the only output will be the payment output and change if required + fee_without_change = Fee::calculate(fee_per_gram, outputs.len(), 1); + fee_with_change = Fee::calculate(fee_per_gram, outputs.len(), 2); + + if total == amount + fee_without_change || total >= amount + fee_with_change { + break; + } + } + }, + } + + if (total != amount + fee_without_change) && (total < amount + fee_with_change) { + return Err(OutputManagerError::NotEnoughFunds); + } + + Ok(outputs) + } + + /// Set the base node public key to the list that will be used to check the status of UTXO's on the base chain. If + /// this is the first time the base node public key is set do the UTXO queries. + async fn set_base_node_public_key( + &mut self, + base_node_public_key: CommsPublicKey, + utxo_query_timeout_futures: &mut FuturesUnordered>, + ) -> Result<(), OutputManagerError> + { + let startup_query = self.base_node_public_key.is_none(); + + self.base_node_public_key = Some(base_node_public_key); + + if startup_query { + self.query_unspent_outputs_status(utxo_query_timeout_futures).await?; + } + Ok(()) + } + + pub async fn fetch_pending_transaction_outputs( + &self, + ) -> Result, OutputManagerError> { + Ok(self.db.fetch_all_pending_transaction_outputs().await?) + } + + pub async fn fetch_spent_outputs(&self) -> Result, OutputManagerError> { + Ok(self.db.fetch_spent_outputs().await?) + } + + pub async fn fetch_unspent_outputs(&self) -> Result, OutputManagerError> { + Ok(self.db.fetch_sorted_unspent_outputs().await?) + } + + pub async fn fetch_invalid_outputs(&self) -> Result, OutputManagerError> { + Ok(self.db.get_invalid_outputs().await?) + } + + /// Return the Seed words for the current Master Key set in the Key Manager + pub fn get_seed_words(&self) -> Result, OutputManagerError> { + Ok(from_secret_key( + &acquire_lock!(self.key_manager).master_key, + &MnemonicLanguage::English, + )?) + } +} + +/// Different UTXO selection strategies for choosing which UTXO's are used to fulfill a transaction +/// TODO Investigate and implement more optimal strategies +pub enum UTXOSelectionStrategy { + // Start from the smallest UTXOs and work your way up until the amount is covered. Main benefit is removing small + // UTXOs from the blockchain, con is that it costs more in fees + Smallest, +} + +/// This struct holds the detailed balance of the Output Manager Service. +#[derive(Debug, Clone, PartialEq)] +pub struct Balance { + /// The current balance that is available to spend + pub available_balance: MicroTari, + /// The current balance of funds that are due to be received but have not yet been confirmed + pub pending_incoming_balance: MicroTari, + /// The current balance of funds encumbered in pending outbound transactions that have not been confirmed + pub pending_outgoing_balance: MicroTari, +} + +impl fmt::Display for Balance { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "You current balance is:")?; + write!(f, "Available balance: {}", self.available_balance)?; + write!(f, "Pending incoming balance: {}", self.pending_incoming_balance)?; + write!(f, "Pending outgoing balance: {}", self.pending_outgoing_balance)?; + Ok(()) + } +} diff --git a/base_layer/wallet/src/output_manager_service/storage/database.rs b/base_layer/wallet/src/output_manager_service/storage/database.rs new file mode 100644 index 0000000000..3c159cd82c --- /dev/null +++ b/base_layer/wallet/src/output_manager_service/storage/database.rs @@ -0,0 +1,508 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::output_manager_service::{error::OutputManagerStorageError, service::Balance, TxId}; +use chrono::{NaiveDateTime, Utc}; +use log::*; +use std::{ + collections::HashMap, + fmt::{Display, Error, Formatter}, + sync::Arc, + time::Duration, +}; +use tari_core::transactions::{ + tari_amount::MicroTari, + transaction::{OutputFeatures, UnblindedOutput}, + types::{BlindingFactor, PrivateKey}, +}; + +const LOG_TARGET: &str = "wallet::output_manager_service::database"; + +/// This trait defines the required behaviour that a storage backend must provide for the Output Manager service. +/// Data is passed to and from the backend via the [DbKey], [DbValue], and [DbValueKey] enums. If new data types are +/// required to be supported by the backends then these enums can be updated to reflect this requirement and the trait +/// will remain the same +pub trait OutputManagerBackend: Send + Sync { + /// Retrieve the record associated with the provided DbKey + fn fetch(&self, key: &DbKey) -> Result, OutputManagerStorageError>; + /// Modify the state the of the backend with a write operation + fn write(&self, op: WriteOperation) -> Result, OutputManagerStorageError>; + /// This method is called when a pending transaction is to be confirmed. It must move the `outputs_to_be_spent` and + /// `outputs_to_be_received` from a `PendingTransactionOutputs` record into the `unspent_outputs` and + /// `spent_outputs` collections. + fn confirm_transaction(&self, tx_id: TxId) -> Result<(), OutputManagerStorageError>; + /// This method encumbers the specified outputs into a `PendingTransactionOutputs` record. This reserves these + /// outputs until the transaction is confirmed or cancelled + fn encumber_outputs( + &self, + tx_id: TxId, + outputs_to_send: &[UnblindedOutput], + change_output: Option, + ) -> Result<(), OutputManagerStorageError>; + /// This method must take all the `outputs_to_be_spent` from the specified transaction and move them back into the + /// `UnspentOutputs` pool. + fn cancel_pending_transaction(&self, tx_id: TxId) -> Result<(), OutputManagerStorageError>; + /// This method must run through all the `PendingTransactionOutputs` and test if any have existed for longer that + /// the specified duration. If they have they should be cancelled. + fn timeout_pending_transactions(&self, period: Duration) -> Result<(), OutputManagerStorageError>; + /// This method will increment the currently stored key index for the key manager config. Increment this after eac + /// key is generated + fn increment_key_index(&self) -> Result<(), OutputManagerStorageError>; + /// If an unspent output is detected as invalid (i.e. not available on the blockchain) then it should be moved to + /// the invalid outputs collection + fn invalidate_unspent_output(&self, output: &UnblindedOutput) -> Result<(), OutputManagerStorageError>; +} + +/// Holds the outputs that have been selected for a given pending transaction waiting for confirmation +#[derive(Debug, Clone, PartialEq)] +pub struct PendingTransactionOutputs { + pub tx_id: u64, + pub outputs_to_be_spent: Vec, + pub outputs_to_be_received: Vec, + pub timestamp: NaiveDateTime, +} + +/// Holds the state of the KeyManager being used by the Output Manager Service +#[derive(Clone, Debug, PartialEq)] +pub struct KeyManagerState { + pub master_seed: PrivateKey, + pub branch_seed: String, + pub primary_key_index: usize, +} + +#[derive(Debug, Clone, PartialEq)] +pub enum DbKey { + SpentOutput(BlindingFactor), + UnspentOutput(BlindingFactor), + PendingTransactionOutputs(TxId), + UnspentOutputs, + SpentOutputs, + AllPendingTransactionOutputs, + KeyManagerState, + InvalidOutputs, +} + +#[derive(Debug)] +pub enum DbValue { + SpentOutput(Box), + UnspentOutput(Box), + PendingTransactionOutputs(Box), + UnspentOutputs(Vec), + SpentOutputs(Vec), + InvalidOutputs(Vec), + AllPendingTransactionOutputs(HashMap), + KeyManagerState(KeyManagerState), +} + +pub enum DbKeyValuePair { + SpentOutput(BlindingFactor, Box), + UnspentOutput(BlindingFactor, Box), + PendingTransactionOutputs(TxId, Box), + KeyManagerState(KeyManagerState), +} + +pub enum WriteOperation { + Insert(DbKeyValuePair), + Remove(DbKey), +} + +// Private macro that pulls out all the boiler plate of extracting a DB query result from its variants +macro_rules! fetch { + ($db:ident, $key_val:expr, $key_var:ident) => {{ + let key = DbKey::$key_var($key_val); + match $db.fetch(&key) { + Ok(None) => Err(OutputManagerStorageError::ValueNotFound(key)), + Ok(Some(DbValue::$key_var(k))) => Ok(*k), + Ok(Some(other)) => unexpected_result(key, other), + Err(e) => log_error(key, e), + } + }}; +} + +/// This structure holds an inner type that implements the `OutputManagerBackend` trait and contains the more complex +/// data access logic required by the module built onto the functionality defined by the trait +pub struct OutputManagerDatabase +where T: OutputManagerBackend + 'static +{ + db: Arc, +} + +impl OutputManagerDatabase +where T: OutputManagerBackend + 'static +{ + pub fn new(db: T) -> Self { + Self { db: Arc::new(db) } + } + + pub async fn get_key_manager_state(&self) -> Result, OutputManagerStorageError> { + let db_clone = self.db.clone(); + tokio::task::spawn_blocking(move || match db_clone.fetch(&DbKey::KeyManagerState) { + Ok(None) => Ok(None), + Ok(Some(DbValue::KeyManagerState(c))) => Ok(Some(c)), + Ok(Some(other)) => unexpected_result(DbKey::KeyManagerState, other), + Err(e) => log_error(DbKey::KeyManagerState, e), + }) + .await + .or_else(|err| Err(OutputManagerStorageError::BlockingTaskSpawnError(err.to_string()))) + .and_then(|inner_result| inner_result) + } + + pub async fn set_key_manager_state(&self, state: KeyManagerState) -> Result<(), OutputManagerStorageError> { + let db_clone = self.db.clone(); + tokio::task::spawn_blocking(move || { + db_clone.write(WriteOperation::Insert(DbKeyValuePair::KeyManagerState(state))) + }) + .await + .or_else(|err| Err(OutputManagerStorageError::BlockingTaskSpawnError(err.to_string())))??; + + Ok(()) + } + + pub async fn increment_key_index(&self) -> Result<(), OutputManagerStorageError> { + let db_clone = self.db.clone(); + tokio::task::spawn_blocking(move || db_clone.increment_key_index()) + .await + .or_else(|err| Err(OutputManagerStorageError::BlockingTaskSpawnError(err.to_string())))??; + Ok(()) + } + + pub async fn add_unspent_output(&self, output: UnblindedOutput) -> Result<(), OutputManagerStorageError> { + let db_clone = self.db.clone(); + tokio::task::spawn_blocking(move || { + db_clone.write(WriteOperation::Insert(DbKeyValuePair::UnspentOutput( + output.spending_key.clone(), + Box::new(output), + ))) + }) + .await + .or_else(|err| Err(OutputManagerStorageError::BlockingTaskSpawnError(err.to_string())))??; + + Ok(()) + } + + pub async fn get_balance(&self) -> Result { + let db_clone = self.db.clone(); + let db_clone2 = self.db.clone(); + + let pending_txs = tokio::task::spawn_blocking(move || { + db_clone.fetch(&DbKey::AllPendingTransactionOutputs)?.ok_or_else(|| { + OutputManagerStorageError::UnexpectedResult( + "Pending Transaction Outputs cannot be retrieved".to_string(), + ) + }) + }) + .await + .or_else(|err| Err(OutputManagerStorageError::BlockingTaskSpawnError(err.to_string())))??; + + let unspent_outputs = tokio::task::spawn_blocking(move || { + db_clone2.fetch(&DbKey::UnspentOutputs)?.ok_or_else(|| { + OutputManagerStorageError::UnexpectedResult("Unspent Outputs cannot be retrieved".to_string()) + }) + }) + .await + .or_else(|err| Err(OutputManagerStorageError::BlockingTaskSpawnError(err.to_string())))??; + + if let DbValue::UnspentOutputs(uo) = unspent_outputs { + if let DbValue::AllPendingTransactionOutputs(pto) = pending_txs { + let available_balance = uo.iter().fold(MicroTari::from(0), |acc, x| acc + x.value); + let mut pending_incoming = MicroTari::from(0); + let mut pending_outgoing = MicroTari::from(0); + + for v in pto.values() { + pending_incoming += v + .outputs_to_be_received + .iter() + .fold(MicroTari::from(0), |acc, x| acc + x.value); + pending_outgoing += v + .outputs_to_be_spent + .iter() + .fold(MicroTari::from(0), |acc, x| acc + x.value); + } + + return Ok(Balance { + available_balance, + pending_incoming_balance: pending_incoming, + pending_outgoing_balance: pending_outgoing, + }); + } + } + + Err(OutputManagerStorageError::UnexpectedResult( + "Unexpected result from database backend".to_string(), + )) + } + + pub async fn add_pending_transaction_outputs( + &self, + pending_transaction_outputs: PendingTransactionOutputs, + ) -> Result<(), OutputManagerStorageError> + { + let db_clone = self.db.clone(); + tokio::task::spawn_blocking(move || { + db_clone.write(WriteOperation::Insert(DbKeyValuePair::PendingTransactionOutputs( + pending_transaction_outputs.tx_id, + Box::new(pending_transaction_outputs), + ))) + }) + .await + .or_else(|err| Err(OutputManagerStorageError::BlockingTaskSpawnError(err.to_string())))??; + + Ok(()) + } + + pub async fn fetch_pending_transaction_outputs( + &self, + tx_id: TxId, + ) -> Result + { + let db_clone = self.db.clone(); + tokio::task::spawn_blocking(move || fetch!(db_clone, tx_id, PendingTransactionOutputs)) + .await + .or_else(|err| Err(OutputManagerStorageError::BlockingTaskSpawnError(err.to_string()))) + .and_then(|inner_result| inner_result) + } + + /// This method is called when a pending transaction is confirmed. It moves the `outputs_to_be_spent` and + /// `outputs_to_be_received` from a `PendingTransactionOutputs` record into the `unspent_outputs` and + /// `spent_outputs` collections. + pub async fn confirm_pending_transaction_outputs(&self, tx_id: TxId) -> Result<(), OutputManagerStorageError> { + let db_clone = self.db.clone(); + tokio::task::spawn_blocking(move || db_clone.confirm_transaction(tx_id)) + .await + .or_else(|err| Err(OutputManagerStorageError::BlockingTaskSpawnError(err.to_string()))) + .and_then(|inner_result| inner_result) + } + + /// This method accepts and stores a pending inbound transaction and creates the `output_to_be_received` from the + /// amount and provided spending key. + pub async fn accept_incoming_pending_transaction( + &self, + tx_id: TxId, + amount: MicroTari, + spending_key: PrivateKey, + output_features: OutputFeatures, + ) -> Result<(), OutputManagerStorageError> + { + let db_clone = self.db.clone(); + tokio::task::spawn_blocking(move || { + db_clone.write(WriteOperation::Insert(DbKeyValuePair::PendingTransactionOutputs( + tx_id, + Box::new(PendingTransactionOutputs { + tx_id, + outputs_to_be_spent: Vec::new(), + outputs_to_be_received: vec![UnblindedOutput { + value: amount, + spending_key: spending_key.clone(), + features: output_features, + }], + timestamp: Utc::now().naive_utc(), + }), + ))) + }) + .await + .or_else(|err| Err(OutputManagerStorageError::BlockingTaskSpawnError(err.to_string())))??; + Ok(()) + } + + /// This method is called when a transaction is built to be sent. It will encumber unspent outputs against a pending + /// transaction + pub async fn encumber_outputs( + &self, + tx_id: TxId, + outputs_to_send: Vec, + change_output: Option, + ) -> Result<(), OutputManagerStorageError> + { + let db_clone = self.db.clone(); + tokio::task::spawn_blocking(move || db_clone.encumber_outputs(tx_id, &outputs_to_send, change_output)) + .await + .or_else(|err| Err(OutputManagerStorageError::BlockingTaskSpawnError(err.to_string()))) + .and_then(|inner_result| inner_result) + } + + /// When a pending transaction is cancelled the encumbered outputs are moved back to the `unspent_outputs` + /// collection. + pub async fn cancel_pending_transaction_outputs(&self, tx_id: TxId) -> Result<(), OutputManagerStorageError> { + let db_clone = self.db.clone(); + tokio::task::spawn_blocking(move || db_clone.cancel_pending_transaction(tx_id)) + .await + .or_else(|err| Err(OutputManagerStorageError::BlockingTaskSpawnError(err.to_string()))) + .and_then(|inner_result| inner_result) + } + + /// This method is check all pending transactions to see if any are older that the provided duration. If they are + /// they will be cancelled. + pub async fn timeout_pending_transaction_outputs(&self, period: Duration) -> Result<(), OutputManagerStorageError> { + let db_clone = self.db.clone(); + tokio::task::spawn_blocking(move || db_clone.timeout_pending_transactions(period)) + .await + .or_else(|err| Err(OutputManagerStorageError::BlockingTaskSpawnError(err.to_string()))) + .and_then(|inner_result| inner_result) + } + + pub async fn fetch_sorted_unspent_outputs(&self) -> Result, OutputManagerStorageError> { + let db_clone = self.db.clone(); + + let mut uo = tokio::task::spawn_blocking(move || match db_clone.fetch(&DbKey::UnspentOutputs) { + Ok(None) => log_error( + DbKey::UnspentOutputs, + OutputManagerStorageError::UnexpectedResult("Could not retrieve unspent outputs".to_string()), + ), + Ok(Some(DbValue::UnspentOutputs(uo))) => Ok(uo), + Ok(Some(other)) => unexpected_result(DbKey::UnspentOutputs, other), + Err(e) => log_error(DbKey::UnspentOutputs, e), + }) + .await + .or_else(|err| Err(OutputManagerStorageError::BlockingTaskSpawnError(err.to_string())))??; + + uo.sort(); + Ok(uo) + } + + pub async fn fetch_spent_outputs(&self) -> Result, OutputManagerStorageError> { + let db_clone = self.db.clone(); + + let uo = tokio::task::spawn_blocking(move || match db_clone.fetch(&DbKey::SpentOutputs) { + Ok(None) => log_error( + DbKey::UnspentOutputs, + OutputManagerStorageError::UnexpectedResult("Could not retrieve spent outputs".to_string()), + ), + Ok(Some(DbValue::SpentOutputs(uo))) => Ok(uo), + Ok(Some(other)) => unexpected_result(DbKey::SpentOutputs, other), + Err(e) => log_error(DbKey::SpentOutputs, e), + }) + .await + .or_else(|err| Err(OutputManagerStorageError::BlockingTaskSpawnError(err.to_string())))??; + Ok(uo) + } + + pub async fn fetch_all_pending_transaction_outputs( + &self, + ) -> Result, OutputManagerStorageError> { + let db_clone = self.db.clone(); + + let uo = tokio::task::spawn_blocking(move || match db_clone.fetch(&DbKey::AllPendingTransactionOutputs) { + Ok(None) => log_error( + DbKey::AllPendingTransactionOutputs, + OutputManagerStorageError::UnexpectedResult( + "Could not retrieve pending transaction outputs".to_string(), + ), + ), + Ok(Some(DbValue::AllPendingTransactionOutputs(pt))) => Ok(pt), + Ok(Some(other)) => unexpected_result(DbKey::AllPendingTransactionOutputs, other), + Err(e) => log_error(DbKey::AllPendingTransactionOutputs, e), + }) + .await + .or_else(|err| Err(OutputManagerStorageError::BlockingTaskSpawnError(err.to_string())))??; + Ok(uo) + } + + pub async fn get_unspent_outputs(&self) -> Result, OutputManagerStorageError> { + let db_clone = self.db.clone(); + + let uo = tokio::task::spawn_blocking(move || match db_clone.fetch(&DbKey::UnspentOutputs) { + Ok(None) => log_error( + DbKey::UnspentOutputs, + OutputManagerStorageError::UnexpectedResult("Could not retrieve unspent outputs".to_string()), + ), + Ok(Some(DbValue::UnspentOutputs(uo))) => Ok(uo), + Ok(Some(other)) => unexpected_result(DbKey::UnspentOutputs, other), + Err(e) => log_error(DbKey::UnspentOutputs, e), + }) + .await + .or_else(|err| Err(OutputManagerStorageError::BlockingTaskSpawnError(err.to_string())))??; + Ok(uo) + } + + pub async fn get_invalid_outputs(&self) -> Result, OutputManagerStorageError> { + let db_clone = self.db.clone(); + + let uo = tokio::task::spawn_blocking(move || match db_clone.fetch(&DbKey::InvalidOutputs) { + Ok(None) => log_error( + DbKey::InvalidOutputs, + OutputManagerStorageError::UnexpectedResult("Could not retrieve invalid outputs".to_string()), + ), + Ok(Some(DbValue::InvalidOutputs(uo))) => Ok(uo), + Ok(Some(other)) => unexpected_result(DbKey::InvalidOutputs, other), + Err(e) => log_error(DbKey::InvalidOutputs, e), + }) + .await + .or_else(|err| Err(OutputManagerStorageError::BlockingTaskSpawnError(err.to_string())))??; + Ok(uo) + } + + pub async fn invalidate_output(&self, output: UnblindedOutput) -> Result<(), OutputManagerStorageError> { + let db_clone = self.db.clone(); + tokio::task::spawn_blocking(move || db_clone.invalidate_unspent_output(&output)) + .await + .or_else(|err| Err(OutputManagerStorageError::BlockingTaskSpawnError(err.to_string()))) + .and_then(|inner_result| inner_result) + } +} + +fn unexpected_result(req: DbKey, res: DbValue) -> Result { + let msg = format!("Unexpected result for database query {}. Response: {}", req, res); + error!(target: LOG_TARGET, "{}", msg); + Err(OutputManagerStorageError::UnexpectedResult(msg)) +} + +impl Display for DbKey { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + match self { + DbKey::SpentOutput(_) => f.write_str(&"Spent Output Key".to_string()), + DbKey::UnspentOutput(_) => f.write_str(&"Unspent Output Key".to_string()), + DbKey::PendingTransactionOutputs(tx_id) => { + f.write_str(&format!("Pending Transaction Outputs TX_ID: {}", tx_id)) + }, + DbKey::UnspentOutputs => f.write_str(&"Unspent Outputs Key".to_string()), + DbKey::SpentOutputs => f.write_str(&"Spent Outputs Key".to_string()), + DbKey::AllPendingTransactionOutputs => f.write_str(&"All Pending Transaction Outputs".to_string()), + DbKey::KeyManagerState => f.write_str(&"Key Manager State".to_string()), + DbKey::InvalidOutputs => f.write_str(&"Invalid Outputs Key"), + } + } +} + +impl Display for DbValue { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + match self { + DbValue::SpentOutput(_) => f.write_str("Spent Output"), + DbValue::UnspentOutput(_) => f.write_str("Unspent Output"), + DbValue::PendingTransactionOutputs(_) => f.write_str("Pending Transaction Outputs"), + DbValue::UnspentOutputs(_) => f.write_str("Unspent Outputs"), + DbValue::SpentOutputs(_) => f.write_str("Spent Outputs"), + DbValue::AllPendingTransactionOutputs(_) => f.write_str("All Pending Transaction Outputs"), + DbValue::KeyManagerState(_) => f.write_str("Key Manager State"), + DbValue::InvalidOutputs(_) => f.write_str("Invalid Outputs"), + } + } +} + +fn log_error(req: DbKey, err: OutputManagerStorageError) -> Result { + error!( + target: LOG_TARGET, + "Database access error on request: {}: {}", + req, + err.to_string() + ); + Err(err) +} diff --git a/base_layer/wallet/src/output_manager_service/storage/memory_db.rs b/base_layer/wallet/src/output_manager_service/storage/memory_db.rs new file mode 100644 index 0000000000..15fa5f46b7 --- /dev/null +++ b/base_layer/wallet/src/output_manager_service/storage/memory_db.rs @@ -0,0 +1,281 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::output_manager_service::{ + error::OutputManagerStorageError, + storage::database::{ + DbKey, + DbKeyValuePair, + DbValue, + KeyManagerState, + OutputManagerBackend, + PendingTransactionOutputs, + WriteOperation, + }, + TxId, +}; +use chrono::{Duration as ChronoDuration, Utc}; +use std::{ + collections::HashMap, + sync::{Arc, RwLock}, + time::Duration, +}; +use tari_core::transactions::transaction::UnblindedOutput; + +/// This structure is an In-Memory database backend that implements the `OutputManagerBackend` trait and provides all +/// the functionality required by the trait. +#[derive(Default)] +pub struct InnerDatabase { + unspent_outputs: Vec, + spent_outputs: Vec, + invalid_outputs: Vec, + pending_transactions: HashMap, + key_manager_state: Option, +} + +impl InnerDatabase { + pub fn new() -> Self { + Self { + unspent_outputs: Vec::new(), + spent_outputs: Vec::new(), + invalid_outputs: Vec::new(), + pending_transactions: HashMap::new(), + key_manager_state: None, + } + } +} + +#[derive(Clone)] +pub struct OutputManagerMemoryDatabase { + db: Arc>, +} + +impl OutputManagerMemoryDatabase { + pub fn new() -> Self { + Self { + db: Arc::new(RwLock::new(InnerDatabase::new())), + } + } +} + +impl OutputManagerBackend for OutputManagerMemoryDatabase { + fn fetch(&self, key: &DbKey) -> Result, OutputManagerStorageError> { + let db = acquire_read_lock!(self.db); + let result = match key { + DbKey::SpentOutput(k) => db + .spent_outputs + .iter() + .find(|v| &v.spending_key == k) + .map(|v| DbValue::SpentOutput(Box::new(v.clone()))), + DbKey::UnspentOutput(k) => db + .unspent_outputs + .iter() + .find(|v| &v.spending_key == k) + .map(|v| DbValue::UnspentOutput(Box::new(v.clone()))), + DbKey::PendingTransactionOutputs(tx_id) => db + .pending_transactions + .get(tx_id) + .map(|v| DbValue::PendingTransactionOutputs(Box::new(v.clone()))), + DbKey::UnspentOutputs => Some(DbValue::UnspentOutputs(db.unspent_outputs.clone())), + DbKey::SpentOutputs => Some(DbValue::SpentOutputs(db.spent_outputs.clone())), + DbKey::AllPendingTransactionOutputs => { + Some(DbValue::AllPendingTransactionOutputs(db.pending_transactions.clone())) + }, + DbKey::KeyManagerState => db + .key_manager_state + .as_ref() + .map(|km| DbValue::KeyManagerState(km.clone())), + DbKey::InvalidOutputs => Some(DbValue::InvalidOutputs(db.invalid_outputs.clone())), + }; + + Ok(result) + } + + fn write(&self, op: WriteOperation) -> Result, OutputManagerStorageError> { + let mut db = acquire_write_lock!(self.db); + match op { + WriteOperation::Insert(kvp) => match kvp { + DbKeyValuePair::SpentOutput(k, o) => { + if db.spent_outputs.iter().any(|v| v.spending_key == k) || + db.unspent_outputs.iter().any(|v| v.spending_key == k) + { + return Err(OutputManagerStorageError::DuplicateOutput); + } + db.spent_outputs.push(*o); + }, + DbKeyValuePair::UnspentOutput(k, o) => { + if db.unspent_outputs.iter().any(|v| v.spending_key == k) || + db.spent_outputs.iter().any(|v| v.spending_key == k) + { + return Err(OutputManagerStorageError::DuplicateOutput); + } + db.unspent_outputs.push(*o); + }, + DbKeyValuePair::PendingTransactionOutputs(t, p) => { + db.pending_transactions.insert(t, *p); + }, + DbKeyValuePair::KeyManagerState(km) => db.key_manager_state = Some(km), + }, + WriteOperation::Remove(k) => match k { + DbKey::SpentOutput(k) => match db.spent_outputs.iter().position(|v| v.spending_key == k) { + None => return Err(OutputManagerStorageError::ValueNotFound(DbKey::SpentOutput(k))), + Some(pos) => { + return Ok(Some(DbValue::SpentOutput(Box::new(db.spent_outputs.remove(pos))))); + }, + }, + DbKey::UnspentOutput(k) => match db.unspent_outputs.iter().position(|v| v.spending_key == k) { + None => return Err(OutputManagerStorageError::ValueNotFound(DbKey::UnspentOutput(k))), + Some(pos) => { + return Ok(Some(DbValue::UnspentOutput(Box::new(db.unspent_outputs.remove(pos))))); + }, + }, + DbKey::PendingTransactionOutputs(tx_id) => { + if let Some(p) = db.pending_transactions.remove(&tx_id) { + return Ok(Some(DbValue::PendingTransactionOutputs(Box::new(p)))); + } else { + return Err(OutputManagerStorageError::ValueNotFound( + DbKey::PendingTransactionOutputs(tx_id), + )); + } + }, + DbKey::UnspentOutputs => return Err(OutputManagerStorageError::OperationNotSupported), + DbKey::SpentOutputs => return Err(OutputManagerStorageError::OperationNotSupported), + DbKey::AllPendingTransactionOutputs => return Err(OutputManagerStorageError::OperationNotSupported), + DbKey::KeyManagerState => return Err(OutputManagerStorageError::OperationNotSupported), + DbKey::InvalidOutputs => return Err(OutputManagerStorageError::OperationNotSupported), + }, + } + Ok(None) + } + + fn confirm_transaction(&self, tx_id: TxId) -> Result<(), OutputManagerStorageError> { + let mut db = acquire_write_lock!(self.db); + let mut pending_tx = db + .pending_transactions + .remove(&tx_id) + .ok_or_else(|| OutputManagerStorageError::ValueNotFound(DbKey::PendingTransactionOutputs(tx_id)))?; + + // Add Spent outputs + for o in pending_tx.outputs_to_be_spent.drain(..) { + db.spent_outputs.push(o) + } + + // Add Unspent outputs + for o in pending_tx.outputs_to_be_received.drain(..) { + db.unspent_outputs.push(o); + } + + Ok(()) + } + + fn encumber_outputs( + &self, + tx_id: TxId, + outputs_to_send: &[UnblindedOutput], + change_output: Option, + ) -> Result<(), OutputManagerStorageError> + { + let mut db = acquire_write_lock!(self.db); + let mut outputs_to_be_spent = Vec::new(); + for i in outputs_to_send { + if let Some(pos) = db.unspent_outputs.iter().position(|v| v.spending_key == i.spending_key) { + outputs_to_be_spent.push(db.unspent_outputs.remove(pos)); + } else { + return Err(OutputManagerStorageError::ValuesNotFound); + } + } + + let mut pending_transaction = PendingTransactionOutputs { + tx_id, + outputs_to_be_spent, + outputs_to_be_received: Vec::new(), + timestamp: Utc::now().naive_utc(), + }; + + if let Some(co) = change_output { + pending_transaction.outputs_to_be_received.push(co); + } + + db.pending_transactions.insert(tx_id, pending_transaction); + + Ok(()) + } + + fn cancel_pending_transaction(&self, tx_id: TxId) -> Result<(), OutputManagerStorageError> { + let mut db = acquire_write_lock!(self.db); + let mut pending_tx = db + .pending_transactions + .remove(&tx_id) + .ok_or_else(|| OutputManagerStorageError::ValueNotFound(DbKey::PendingTransactionOutputs(tx_id)))?; + for o in pending_tx.outputs_to_be_spent.drain(..) { + db.unspent_outputs.push(o); + } + + Ok(()) + } + + fn timeout_pending_transactions(&self, period: Duration) -> Result<(), OutputManagerStorageError> { + let db = acquire_write_lock!(self.db); + let mut transactions_to_be_cancelled = Vec::new(); + for (tx_id, pt) in db.pending_transactions.iter() { + if pt.timestamp + ChronoDuration::from_std(period)? < Utc::now().naive_utc() { + transactions_to_be_cancelled.push(tx_id.clone()); + } + } + drop(db); + for t in transactions_to_be_cancelled { + self.cancel_pending_transaction(t.clone())?; + } + + Ok(()) + } + + fn invalidate_unspent_output(&self, output: &UnblindedOutput) -> Result<(), OutputManagerStorageError> { + let mut db = acquire_write_lock!(self.db); + match db + .unspent_outputs + .iter() + .position(|v| v.spending_key == output.spending_key) + { + Some(pos) => { + let output = db.unspent_outputs.remove(pos); + db.invalid_outputs.push(output); + }, + None => return Err(OutputManagerStorageError::ValuesNotFound), + } + Ok(()) + } + + fn increment_key_index(&self) -> Result<(), OutputManagerStorageError> { + let mut db = acquire_write_lock!(self.db); + + if db.key_manager_state.is_none() { + return Err(OutputManagerStorageError::KeyManagerNotInitialized); + } + db.key_manager_state = db.key_manager_state.clone().map(|mut state| { + state.primary_key_index += 1; + state + }); + + Ok(()) + } +} diff --git a/base_layer/core/src/base_node/transaction_validation_service.rs b/base_layer/wallet/src/output_manager_service/storage/mod.rs similarity index 96% rename from base_layer/core/src/base_node/transaction_validation_service.rs rename to base_layer/wallet/src/output_manager_service/storage/mod.rs index 48a5ca80d3..c6c73087f7 100644 --- a/base_layer/core/src/base_node/transaction_validation_service.rs +++ b/base_layer/wallet/src/output_manager_service/storage/mod.rs @@ -20,4 +20,6 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -pub struct TransactionValidationService; +pub mod database; +pub mod memory_db; +pub mod sqlite_db; diff --git a/base_layer/wallet/src/output_manager_service/storage/sqlite_db.rs b/base_layer/wallet/src/output_manager_service/storage/sqlite_db.rs new file mode 100644 index 0000000000..f07a1e0c35 --- /dev/null +++ b/base_layer/wallet/src/output_manager_service/storage/sqlite_db.rs @@ -0,0 +1,1091 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + output_manager_service::{ + error::OutputManagerStorageError, + storage::database::{ + DbKey, + DbKeyValuePair, + DbValue, + KeyManagerState, + OutputManagerBackend, + PendingTransactionOutputs, + WriteOperation, + }, + TxId, + }, + schema::{key_manager_states, outputs, pending_transaction_outputs}, +}; +use chrono::{Duration as ChronoDuration, NaiveDateTime, Utc}; +#[cfg(test)] +use diesel::expression::dsl::not; +use diesel::{ + prelude::*, + r2d2::{ConnectionManager, Pool, PooledConnection}, + result::Error as DieselError, + SqliteConnection, +}; +use std::{collections::HashMap, convert::TryFrom, time::Duration}; +use tari_core::transactions::{ + tari_amount::MicroTari, + transaction::{OutputFeatures, OutputFlags, UnblindedOutput}, + types::PrivateKey, +}; +use tari_crypto::tari_utilities::ByteArray; +/// A Sqlite backend for the Output Manager Service. The Backend is accessed via a connection pool to the Sqlite file. +#[derive(Clone)] +pub struct OutputManagerSqliteDatabase { + database_connection_pool: Pool>, +} +impl OutputManagerSqliteDatabase { + pub fn new(database_connection_pool: Pool>) -> Self { + Self { + database_connection_pool, + } + } +} +impl OutputManagerBackend for OutputManagerSqliteDatabase { + fn fetch(&self, key: &DbKey) -> Result, OutputManagerStorageError> { + let conn = self + .database_connection_pool + .clone() + .get() + .map_err(|_| OutputManagerStorageError::R2d2Error)?; + + let result = match key { + DbKey::SpentOutput(k) => match OutputSql::find_status(&k.to_vec(), OutputStatus::Spent, &conn) { + Ok(o) => Some(DbValue::SpentOutput(Box::new(UnblindedOutput::try_from(o)?))), + Err(e) => { + match e { + OutputManagerStorageError::DieselError(DieselError::NotFound) => (), + e => return Err(e), + }; + None + }, + }, + DbKey::UnspentOutput(k) => match OutputSql::find_status(&k.to_vec(), OutputStatus::Unspent, &conn) { + Ok(o) => Some(DbValue::UnspentOutput(Box::new(UnblindedOutput::try_from(o)?))), + Err(e) => { + match e { + OutputManagerStorageError::DieselError(DieselError::NotFound) => (), + e => return Err(e), + }; + None + }, + }, + DbKey::PendingTransactionOutputs(tx_id) => match PendingTransactionOutputSql::find(*tx_id, &conn) { + Ok(p) => { + let outputs = OutputSql::find_by_tx_id_and_encumbered(*tx_id, &conn)?; + Some(DbValue::PendingTransactionOutputs(Box::new( + pending_transaction_outputs_from_sql_outputs(p.tx_id as u64, &p.timestamp, outputs)?, + ))) + }, + Err(e) => { + match e { + OutputManagerStorageError::DieselError(DieselError::NotFound) => (), + e => return Err(e), + }; + None + }, + }, + DbKey::UnspentOutputs => Some(DbValue::UnspentOutputs( + OutputSql::index_status(OutputStatus::Unspent, &conn)? + .iter() + .map(|o| UnblindedOutput::try_from(o.clone())) + .collect::, _>>()?, + )), + DbKey::SpentOutputs => Some(DbValue::SpentOutputs( + OutputSql::index_status(OutputStatus::Spent, &conn)? + .iter() + .map(|o| UnblindedOutput::try_from(o.clone())) + .collect::, _>>()?, + )), + DbKey::AllPendingTransactionOutputs => { + let pending_sql_txs = PendingTransactionOutputSql::index(&conn)?; + let mut pending_txs = HashMap::new(); + for p_tx in pending_sql_txs { + let outputs = OutputSql::find_by_tx_id_and_encumbered(p_tx.tx_id as u64, &conn)?; + pending_txs.insert( + p_tx.tx_id as u64, + pending_transaction_outputs_from_sql_outputs(p_tx.tx_id as u64, &p_tx.timestamp, outputs)?, + ); + } + Some(DbValue::AllPendingTransactionOutputs(pending_txs)) + }, + DbKey::KeyManagerState => match KeyManagerStateSql::get_state(&conn).ok() { + None => None, + Some(km) => Some(DbValue::KeyManagerState(KeyManagerState::try_from(km)?)), + }, + DbKey::InvalidOutputs => Some(DbValue::InvalidOutputs( + OutputSql::index_status(OutputStatus::Invalid, &conn)? + .iter() + .map(|o| UnblindedOutput::try_from(o.clone())) + .collect::, _>>()?, + )), + }; + + Ok(result) + } + + fn write(&self, op: WriteOperation) -> Result, OutputManagerStorageError> { + let conn = self + .database_connection_pool + .clone() + .get() + .map_err(|_| OutputManagerStorageError::R2d2Error)?; + + match op { + WriteOperation::Insert(kvp) => match kvp { + DbKeyValuePair::SpentOutput(k, o) => { + if let Ok(_) = OutputSql::find(&k.to_vec(), &conn) { + return Err(OutputManagerStorageError::DuplicateOutput); + } + OutputSql::new(*o, OutputStatus::Spent, None).commit(&conn)? + }, + DbKeyValuePair::UnspentOutput(k, o) => { + if let Ok(_) = OutputSql::find(&k.to_vec(), &conn) { + return Err(OutputManagerStorageError::DuplicateOutput); + } + OutputSql::new(*o, OutputStatus::Unspent, None).commit(&conn)? + }, + DbKeyValuePair::PendingTransactionOutputs(tx_id, p) => { + if let Ok(_) = PendingTransactionOutputSql::find(tx_id, &conn) { + return Err(OutputManagerStorageError::DuplicateOutput); + } + PendingTransactionOutputSql::new(p.tx_id, p.timestamp).commit(&conn)?; + for o in p.outputs_to_be_spent { + OutputSql::new(o.clone(), OutputStatus::EncumberedToBeSpent, Some(p.tx_id)).commit(&conn)?; + } + for o in p.outputs_to_be_received { + OutputSql::new(o.clone(), OutputStatus::EncumberedToBeReceived, Some(p.tx_id)).commit(&conn)?; + } + }, + DbKeyValuePair::KeyManagerState(km) => KeyManagerStateSql::set_state(km, &conn)?, + }, + WriteOperation::Remove(k) => match k { + DbKey::SpentOutput(s) => match OutputSql::find_status(&s.to_vec(), OutputStatus::Spent, &conn) { + Ok(o) => { + o.clone().delete(&conn)?; + return Ok(Some(DbValue::SpentOutput(Box::new(UnblindedOutput::try_from(o)?)))); + }, + Err(e) => { + match e { + OutputManagerStorageError::DieselError(DieselError::NotFound) => (), + e => return Err(e), + }; + }, + }, + DbKey::UnspentOutput(k) => match OutputSql::find_status(&k.to_vec(), OutputStatus::Unspent, &conn) { + Ok(o) => { + o.clone().delete(&conn)?; + return Ok(Some(DbValue::UnspentOutput(Box::new(UnblindedOutput::try_from(o)?)))); + }, + Err(e) => { + match e { + OutputManagerStorageError::DieselError(DieselError::NotFound) => (), + e => return Err(e), + }; + }, + }, + DbKey::PendingTransactionOutputs(tx_id) => match PendingTransactionOutputSql::find(tx_id, &conn) { + Ok(p) => { + let outputs = OutputSql::find_by_tx_id_and_encumbered(p.tx_id as u64, &conn)?; + p.clone().delete(&conn)?; + return Ok(Some(DbValue::PendingTransactionOutputs(Box::new( + pending_transaction_outputs_from_sql_outputs(p.tx_id as u64, &p.timestamp, outputs)?, + )))); + }, + Err(e) => { + match e { + OutputManagerStorageError::DieselError(DieselError::NotFound) => (), + e => return Err(e), + }; + }, + }, + DbKey::UnspentOutputs => return Err(OutputManagerStorageError::OperationNotSupported), + DbKey::SpentOutputs => return Err(OutputManagerStorageError::OperationNotSupported), + DbKey::AllPendingTransactionOutputs => return Err(OutputManagerStorageError::OperationNotSupported), + DbKey::KeyManagerState => return Err(OutputManagerStorageError::OperationNotSupported), + DbKey::InvalidOutputs => {}, + }, + } + + Ok(None) + } + + fn confirm_transaction(&self, tx_id: u64) -> Result<(), OutputManagerStorageError> { + let conn = self + .database_connection_pool + .clone() + .get() + .map_err(|_| OutputManagerStorageError::R2d2Error)?; + + match PendingTransactionOutputSql::find(tx_id, &conn) { + Ok(p) => { + let outputs = OutputSql::find_by_tx_id_and_encumbered(tx_id, &conn)?; + + for o in outputs { + if o.status == (OutputStatus::EncumberedToBeReceived as i32) { + o.update( + UpdateOutput { + status: Some(OutputStatus::Unspent), + tx_id: None, + }, + &conn, + )?; + } else if o.status == (OutputStatus::EncumberedToBeSpent as i32) { + o.update( + UpdateOutput { + status: Some(OutputStatus::Spent), + tx_id: None, + }, + &conn, + )?; + } + } + + p.delete(&conn)?; + }, + Err(e) => { + match e { + OutputManagerStorageError::DieselError(DieselError::NotFound) => (), + e => return Err(e), + }; + }, + } + + Ok(()) + } + + fn encumber_outputs( + &self, + tx_id: u64, + outputs_to_send: &[UnblindedOutput], + change_output: Option, + ) -> Result<(), OutputManagerStorageError> + { + let conn = self + .database_connection_pool + .clone() + .get() + .map_err(|_| OutputManagerStorageError::R2d2Error)?; + + let mut outputs_to_be_spent = Vec::new(); + for i in outputs_to_send { + let output = OutputSql::find(&i.spending_key.to_vec(), &conn)?; + if output.status == (OutputStatus::Spent as i32) { + return Err(OutputManagerStorageError::OutputAlreadySpent); + } + outputs_to_be_spent.push(output); + } + + PendingTransactionOutputSql::new(tx_id, Utc::now().naive_utc()).commit(&conn)?; + + for o in outputs_to_be_spent { + o.update( + UpdateOutput { + status: Some(OutputStatus::EncumberedToBeSpent), + tx_id: Some(tx_id), + }, + &conn, + )?; + } + + if let Some(co) = change_output { + OutputSql::new(co, OutputStatus::EncumberedToBeReceived, Some(tx_id)).commit(&conn)?; + } + + Ok(()) + } + + fn cancel_pending_transaction(&self, tx_id: u64) -> Result<(), OutputManagerStorageError> { + let conn = self + .database_connection_pool + .clone() + .get() + .map_err(|_| OutputManagerStorageError::R2d2Error)?; + + match PendingTransactionOutputSql::find(tx_id, &conn) { + Ok(p) => { + let outputs = OutputSql::find_by_tx_id_and_encumbered(tx_id, &conn)?; + + for o in outputs { + if o.status == (OutputStatus::EncumberedToBeReceived as i32) { + o.delete(&conn)?; + } else if o.status == (OutputStatus::EncumberedToBeSpent as i32) { + o.update( + UpdateOutput { + status: Some(OutputStatus::Unspent), + tx_id: None, + }, + &conn, + )?; + o.update_null(NullOutputSql { tx_id: None }, &conn)?; + } + } + + p.delete(&conn)?; + }, + Err(e) => { + match e { + OutputManagerStorageError::DieselError(DieselError::NotFound) => { + return Err(OutputManagerStorageError::ValueNotFound( + DbKey::PendingTransactionOutputs(tx_id), + )) + }, + e => return Err(e), + }; + }, + } + + Ok(()) + } + + fn timeout_pending_transactions(&self, period: Duration) -> Result<(), OutputManagerStorageError> { + let conn = self + .database_connection_pool + .clone() + .get() + .map_err(|_| OutputManagerStorageError::R2d2Error)?; + + let older_pending_txs = PendingTransactionOutputSql::index_older( + Utc::now().naive_utc() - ChronoDuration::from_std(period)?, + &conn, + )?; + drop(conn); + for ptx in older_pending_txs { + self.cancel_pending_transaction(ptx.tx_id as u64)?; + } + Ok(()) + } + + fn increment_key_index(&self) -> Result<(), OutputManagerStorageError> { + let conn = self + .database_connection_pool + .clone() + .get() + .map_err(|_| OutputManagerStorageError::R2d2Error)?; + + KeyManagerStateSql::increment_index(&conn)?; + + Ok(()) + } + + fn invalidate_unspent_output(&self, output: &UnblindedOutput) -> Result<(), OutputManagerStorageError> { + let conn = self + .database_connection_pool + .get() + .map_err(|_| OutputManagerStorageError::R2d2Error)?; + + let output = OutputSql::find(&output.spending_key.to_vec(), &conn)?; + + let _ = output.update( + UpdateOutput { + status: Some(OutputStatus::Invalid), + tx_id: None, + }, + &conn, + )?; + + Ok(()) + } +} + +/// A utility function to construct a PendingTransactionOutputs structure for a TxId, set of Outputs and a Timestamp +fn pending_transaction_outputs_from_sql_outputs( + tx_id: TxId, + timestamp: &NaiveDateTime, + outputs: Vec, +) -> Result +{ + let mut outputs_to_be_spent = Vec::new(); + let mut outputs_to_be_received = Vec::new(); + for o in outputs { + if o.status == (OutputStatus::EncumberedToBeReceived as i32) { + outputs_to_be_received.push(UnblindedOutput::try_from(o.clone())?); + } else if o.status == (OutputStatus::EncumberedToBeSpent as i32) { + outputs_to_be_spent.push(UnblindedOutput::try_from(o.clone())?); + } + } + + Ok(PendingTransactionOutputs { + tx_id, + outputs_to_be_spent, + outputs_to_be_received, + timestamp: *timestamp, + }) +} + +/// The status of a given output +#[derive(PartialEq)] +enum OutputStatus { + Unspent, + Spent, + EncumberedToBeReceived, + EncumberedToBeSpent, + Invalid, +} + +impl TryFrom for OutputStatus { + type Error = OutputManagerStorageError; + + fn try_from(value: i32) -> Result { + match value { + 0 => Ok(OutputStatus::Unspent), + 1 => Ok(OutputStatus::Spent), + 2 => Ok(OutputStatus::EncumberedToBeReceived), + 3 => Ok(OutputStatus::EncumberedToBeSpent), + 4 => Ok(OutputStatus::Invalid), + _ => Err(OutputManagerStorageError::ConversionError), + } + } +} + +/// This struct represents an Output in the Sql database. A distinct struct is required to define the Sql friendly +/// equivalent datatypes for the members. +#[derive(Clone, Debug, Queryable, Insertable, PartialEq)] +#[table_name = "outputs"] +struct OutputSql { + spending_key: Vec, + value: i64, + flags: i32, + maturity: i64, + status: i32, + tx_id: Option, +} + +impl OutputSql { + pub fn new(output: UnblindedOutput, status: OutputStatus, tx_id: Option) -> Self { + Self { + spending_key: output.spending_key.to_vec(), + value: (u64::from(output.value)) as i64, + flags: output.features.flags.bits() as i32, + maturity: output.features.maturity as i64, + status: status as i32, + tx_id: tx_id.map(|i| i as i64), + } + } + + /// Write this struct to the database + pub fn commit( + &self, + conn: &PooledConnection>, + ) -> Result<(), OutputManagerStorageError> + { + diesel::insert_into(outputs::table).values(self.clone()).execute(conn)?; + Ok(()) + } + + /// Return all unencumbered outputs + #[cfg(test)] + pub fn index( + conn: &PooledConnection>, + ) -> Result, OutputManagerStorageError> { + Ok(outputs::table + .filter(not(outputs::status.eq(OutputStatus::EncumberedToBeReceived as i32))) + .filter(not(outputs::status.eq(OutputStatus::EncumberedToBeSpent as i32))) + .load::(conn)?) + } + + /// Return all outputs with a given status + pub fn index_status( + status: OutputStatus, + conn: &PooledConnection>, + ) -> Result, OutputManagerStorageError> + { + Ok(outputs::table.filter(outputs::status.eq(status as i32)).load(conn)?) + } + + /// Find a particular Output, if it exists + pub fn find( + spending_key: &[u8], + conn: &PooledConnection>, + ) -> Result + { + Ok(outputs::table + .filter(outputs::spending_key.eq(spending_key)) + .first::(conn)?) + } + + /// Find outputs via tx_id that are encumbered. Any outputs that are encumbered cannot be marked as spent. + pub fn find_by_tx_id_and_encumbered( + tx_id: TxId, + conn: &PooledConnection>, + ) -> Result, OutputManagerStorageError> + { + Ok(outputs::table + .filter(outputs::tx_id.eq(Some(tx_id as i64))) + .filter( + outputs::status + .eq(OutputStatus::EncumberedToBeReceived as i32) + .or(outputs::status.eq(OutputStatus::EncumberedToBeSpent as i32)), + ) + .load(conn)?) + } + + /// Find a particular Output, if it exists and is in the specified Spent state + pub fn find_status( + spending_key: &[u8], + status: OutputStatus, + conn: &PooledConnection>, + ) -> Result + { + Ok(outputs::table + .filter(outputs::status.eq(status as i32)) + .filter(outputs::spending_key.eq(spending_key)) + .first::(conn)?) + } + + pub fn delete( + &self, + conn: &PooledConnection>, + ) -> Result<(), OutputManagerStorageError> + { + let num_deleted = + diesel::delete(outputs::table.filter(outputs::spending_key.eq(&self.spending_key))).execute(conn)?; + + if num_deleted == 0 { + return Err(OutputManagerStorageError::ValuesNotFound); + } + + Ok(()) + } + + pub fn update( + &self, + updated_output: UpdateOutput, + conn: &PooledConnection>, + ) -> Result + { + let num_updated = diesel::update(outputs::table.filter(outputs::spending_key.eq(&self.spending_key))) + .set(UpdateOutputSql::from(updated_output)) + .execute(conn)?; + + if num_updated == 0 { + return Err(OutputManagerStorageError::UnexpectedResult( + "Database update error".to_string(), + )); + } + + Ok(OutputSql::find(&self.spending_key, conn)?) + } + + /// This function is used to update an existing record to set fields to null + pub fn update_null( + &self, + updated_null: NullOutputSql, + conn: &PooledConnection>, + ) -> Result + { + let num_updated = diesel::update(outputs::table.filter(outputs::spending_key.eq(&self.spending_key))) + .set(updated_null) + .execute(conn)?; + + if num_updated == 0 { + return Err(OutputManagerStorageError::UnexpectedResult( + "Database update error".to_string(), + )); + } + + Ok(OutputSql::find(&self.spending_key, conn)?) + } +} + +/// Conversion from an UnblindedOutput to the Sql datatype form +impl TryFrom for UnblindedOutput { + type Error = OutputManagerStorageError; + + fn try_from(o: OutputSql) -> Result { + Ok(Self { + value: MicroTari::from(o.value as u64), + spending_key: PrivateKey::from_vec(&o.spending_key) + .map_err(|_| OutputManagerStorageError::ConversionError)?, + features: OutputFeatures { + flags: OutputFlags::from_bits(o.flags as u8) + .ok_or_else(|| OutputManagerStorageError::ConversionError)?, + maturity: o.maturity as u64, + }, + }) + } +} + +/// These are the fields that can be updated for an Output +pub struct UpdateOutput { + status: Option, + tx_id: Option, +} + +#[derive(AsChangeset)] +#[table_name = "outputs"] +pub struct UpdateOutputSql { + status: Option, + tx_id: Option, +} + +#[derive(AsChangeset)] +#[table_name = "outputs"] +#[changeset_options(treat_none_as_null = "true")] +/// This struct is used to set the contained field to null +pub struct NullOutputSql { + tx_id: Option, +} + +/// Map a Rust friendly UpdateOutput to the Sql data type form +impl From for UpdateOutputSql { + fn from(u: UpdateOutput) -> Self { + Self { + status: u.status.map(|t| t as i32), + tx_id: u.tx_id.map(|t| t as i64), + } + } +} + +/// This struct represents a PendingTransactionOutputs in the Sql database. A distinct struct is required to define the +/// Sql friendly equivalent datatypes for the members. +#[derive(Clone, Queryable, Insertable)] +#[table_name = "pending_transaction_outputs"] +struct PendingTransactionOutputSql { + tx_id: i64, + timestamp: NaiveDateTime, +} +impl PendingTransactionOutputSql { + pub fn new(tx_id: TxId, timestamp: NaiveDateTime) -> Self { + Self { + tx_id: tx_id as i64, + timestamp, + } + } + + pub fn commit( + &self, + conn: &PooledConnection>, + ) -> Result<(), OutputManagerStorageError> + { + diesel::insert_into(pending_transaction_outputs::table) + .values(self.clone()) + .execute(conn)?; + Ok(()) + } + + pub fn find( + tx_id: TxId, + conn: &PooledConnection>, + ) -> Result + { + Ok(pending_transaction_outputs::table + .filter(pending_transaction_outputs::tx_id.eq(tx_id as i64)) + .first::(conn)?) + } + + pub fn index( + conn: &PooledConnection>, + ) -> Result, OutputManagerStorageError> { + Ok(pending_transaction_outputs::table.load::(conn)?) + } + + pub fn index_older( + timestamp: NaiveDateTime, + conn: &PooledConnection>, + ) -> Result, OutputManagerStorageError> + { + Ok(pending_transaction_outputs::table + .filter(pending_transaction_outputs::timestamp.lt(timestamp)) + .load::(conn)?) + } + + pub fn delete( + &self, + conn: &PooledConnection>, + ) -> Result<(), OutputManagerStorageError> + { + let num_deleted = diesel::delete( + pending_transaction_outputs::table.filter(pending_transaction_outputs::tx_id.eq(&self.tx_id)), + ) + .execute(conn)?; + + if num_deleted == 0 { + return Err(OutputManagerStorageError::ValuesNotFound); + } + + let outputs = OutputSql::find_by_tx_id_and_encumbered(self.tx_id as u64, &conn)?; + for o in outputs { + o.delete(&conn)?; + } + + Ok(()) + } +} + +#[derive(Clone, Debug, Queryable, Insertable)] +#[table_name = "key_manager_states"] +struct KeyManagerStateSql { + id: Option, + master_seed: Vec, + branch_seed: String, + primary_key_index: i64, + timestamp: NaiveDateTime, +} + +impl From for KeyManagerStateSql { + fn from(km: KeyManagerState) -> Self { + Self { + id: None, + master_seed: km.master_seed.to_vec(), + branch_seed: km.branch_seed, + primary_key_index: km.primary_key_index as i64, + timestamp: Utc::now().naive_utc(), + } + } +} + +impl TryFrom for KeyManagerState { + type Error = OutputManagerStorageError; + + fn try_from(km: KeyManagerStateSql) -> Result { + Ok(Self { + master_seed: PrivateKey::from_vec(&km.master_seed) + .map_err(|_| OutputManagerStorageError::ConversionError)?, + branch_seed: km.branch_seed, + primary_key_index: km.primary_key_index as usize, + }) + } +} + +impl KeyManagerStateSql { + fn commit( + &self, + conn: &PooledConnection>, + ) -> Result<(), OutputManagerStorageError> + { + diesel::insert_into(key_manager_states::table) + .values(self.clone()) + .execute(conn)?; + Ok(()) + } + + pub fn get_state( + conn: &PooledConnection>, + ) -> Result { + Ok(key_manager_states::table + .first::(conn) + .map_err(|_| OutputManagerStorageError::KeyManagerNotInitialized)?) + } + + pub fn set_state( + key_manager_state: KeyManagerState, + conn: &PooledConnection>, + ) -> Result<(), OutputManagerStorageError> + { + match KeyManagerStateSql::get_state(conn) { + Ok(km) => { + let update = KeyManagerStateUpdate { + master_seed: Some(key_manager_state.master_seed), + branch_seed: Some(key_manager_state.branch_seed), + primary_key_index: Some(key_manager_state.primary_key_index), + }; + + let num_updated = diesel::update(key_manager_states::table.filter(key_manager_states::id.eq(&km.id))) + .set(KeyManagerStateUpdateSql::from(update)) + .execute(conn)?; + if num_updated == 0 { + return Err(OutputManagerStorageError::UnexpectedResult( + "Database update error".to_string(), + )); + } + }, + Err(_) => KeyManagerStateSql::from(key_manager_state).commit(conn)?, + } + Ok(()) + } + + pub fn increment_index( + conn: &PooledConnection>, + ) -> Result { + Ok(match KeyManagerStateSql::get_state(conn) { + Ok(km) => { + let current_index = (km.primary_key_index + 1) as usize; + let update = KeyManagerStateUpdate { + master_seed: None, + branch_seed: None, + primary_key_index: Some(current_index), + }; + let num_updated = diesel::update(key_manager_states::table.filter(key_manager_states::id.eq(&km.id))) + .set(KeyManagerStateUpdateSql::from(update)) + .execute(conn)?; + if num_updated == 0 { + return Err(OutputManagerStorageError::UnexpectedResult( + "Database update error".to_string(), + )); + } + current_index + }, + Err(_) => return Err(OutputManagerStorageError::KeyManagerNotInitialized), + }) + } +} + +struct KeyManagerStateUpdate { + master_seed: Option, + branch_seed: Option, + primary_key_index: Option, +} + +#[derive(AsChangeset)] +#[table_name = "key_manager_states"] +struct KeyManagerStateUpdateSql { + master_seed: Option>, + branch_seed: Option, + primary_key_index: Option, +} + +impl From for KeyManagerStateUpdateSql { + fn from(km: KeyManagerStateUpdate) -> Self { + Self { + master_seed: km.master_seed.map(|ms| ms.to_vec()), + branch_seed: km.branch_seed, + primary_key_index: km.primary_key_index.map(|i| i as i64), + } + } +} + +#[cfg(test)] +mod test { + use crate::output_manager_service::storage::{ + database::KeyManagerState, + sqlite_db::{KeyManagerStateSql, OutputSql, OutputStatus, PendingTransactionOutputSql, UpdateOutput}, + }; + use chrono::{Duration as ChronoDuration, Utc}; + use diesel::{r2d2::ConnectionManager, Connection, SqliteConnection}; + use rand::{distributions::Alphanumeric, rngs::OsRng, CryptoRng, Rng, RngCore}; + use std::{convert::TryFrom, iter, time::Duration}; + use tari_core::transactions::{ + tari_amount::MicroTari, + transaction::{OutputFeatures, TransactionInput, UnblindedOutput}, + types::{CommitmentFactory, PrivateKey}, + }; + use tari_crypto::{commitment::HomomorphicCommitmentFactory, keys::SecretKey}; + use tempdir::TempDir; + + pub fn random_string(len: usize) -> String { + iter::repeat(()).map(|_| OsRng.sample(Alphanumeric)).take(len).collect() + } + + pub fn make_input(rng: &mut R, val: MicroTari) -> (TransactionInput, UnblindedOutput) { + let key = PrivateKey::random(rng); + let factory = CommitmentFactory::default(); + let commitment = factory.commit_value(&key, val.into()); + let input = TransactionInput::new(OutputFeatures::default(), commitment); + (input, UnblindedOutput::new(val, key, None)) + } + + #[test] + fn test_crud() { + let db_name = format!("{}.sqlite3", random_string(8).as_str()); + let temp_dir = TempDir::new(random_string(8).as_str()).unwrap(); + let db_folder = temp_dir.path().to_str().unwrap().to_string(); + let db_path = format!("{}{}", db_folder, db_name); + + embed_migrations!("./migrations"); + let conn = SqliteConnection::establish(&db_path).unwrap_or_else(|_| panic!("Error connecting to {}", db_path)); + + embedded_migrations::run_with_output(&conn, &mut std::io::stdout()).expect("Migration failed"); + + let manager = ConnectionManager::::new(db_path); + let pool = diesel::r2d2::Pool::builder().max_size(1).build(manager).unwrap(); + + let conn = pool.get().unwrap(); + conn.execute("PRAGMA foreign_keys = ON").unwrap(); + + let mut outputs = Vec::new(); + let mut outputs_spent = Vec::new(); + let mut outputs_unspent = Vec::new(); + + for _i in 0..2 { + let (_, uo) = make_input(&mut OsRng.clone(), MicroTari::from(100 + OsRng.next_u64() % 1000)); + let o = OutputSql::new(uo, OutputStatus::Unspent, None); + outputs.push(o.clone()); + outputs_unspent.push(o.clone()); + o.commit(&conn).unwrap(); + } + + for _i in 0..3 { + let (_, uo) = make_input(&mut OsRng.clone(), MicroTari::from(100 + OsRng.next_u64() % 1000)); + let o = OutputSql::new(uo, OutputStatus::Spent, None); + outputs.push(o.clone()); + outputs_spent.push(o.clone()); + o.commit(&conn).unwrap(); + } + + assert_eq!(OutputSql::index(&conn).unwrap(), outputs); + assert_eq!( + OutputSql::index_status(OutputStatus::Unspent, &conn).unwrap(), + outputs_unspent + ); + assert_eq!( + OutputSql::index_status(OutputStatus::Spent, &conn).unwrap(), + outputs_spent + ); + + assert_eq!( + OutputSql::find(&outputs[0].spending_key, &conn).unwrap().spending_key, + outputs[0].spending_key + ); + + assert_eq!( + OutputSql::find_status(&outputs[0].spending_key, OutputStatus::Unspent, &conn) + .unwrap() + .spending_key, + outputs[0].spending_key + ); + + assert!(OutputSql::find_status(&outputs[0].spending_key, OutputStatus::Spent, &conn).is_err()); + + let _ = OutputSql::find(&outputs[4].spending_key, &conn).unwrap().delete(&conn); + + assert_eq!(OutputSql::index(&conn).unwrap().len(), 4); + + let tx_id = 44u64; + + assert!(OutputSql::find(&outputs[0].spending_key, &conn) + .unwrap() + .update( + UpdateOutput { + status: None, + tx_id: Some(tx_id), + }, + &conn, + ) + .is_err()); + + PendingTransactionOutputSql::new(tx_id, Utc::now().naive_utc()) + .commit(&conn) + .unwrap(); + + PendingTransactionOutputSql::new(11u64, Utc::now().naive_utc()) + .commit(&conn) + .unwrap(); + + let pt = PendingTransactionOutputSql::find(tx_id, &conn).unwrap(); + + assert_eq!(pt.tx_id as u64, tx_id); + + let pts = PendingTransactionOutputSql::index(&conn).unwrap(); + + assert_eq!(pts.len(), 2); + + let _updated1 = OutputSql::find(&outputs[0].spending_key, &conn) + .unwrap() + .update( + UpdateOutput { + status: Some(OutputStatus::Unspent), + tx_id: Some(44u64), + }, + &conn, + ) + .unwrap(); + + let _updated2 = OutputSql::find(&outputs[1].spending_key, &conn) + .unwrap() + .update( + UpdateOutput { + status: Some(OutputStatus::EncumberedToBeReceived), + tx_id: Some(44u64), + }, + &conn, + ) + .unwrap(); + + let result = OutputSql::find_by_tx_id_and_encumbered(44u64, &conn).unwrap(); + assert_eq!(result.len(), 1); + assert_eq!(result[0].spending_key, outputs[1].spending_key); + + PendingTransactionOutputSql::new( + 12u64, + Utc::now().naive_utc() - ChronoDuration::from_std(Duration::from_millis(600_000)).unwrap(), + ) + .commit(&conn) + .unwrap(); + + let pending_older1 = PendingTransactionOutputSql::index_older(Utc::now().naive_utc(), &conn).unwrap(); + assert_eq!(pending_older1.len(), 3); + + let pending_older2 = PendingTransactionOutputSql::index_older( + Utc::now().naive_utc() - ChronoDuration::from_std(Duration::from_millis(200_000)).unwrap(), + &conn, + ) + .unwrap(); + assert_eq!(pending_older2.len(), 1); + } + + #[test] + fn test_key_manager_crud() { + let db_name = format!("{}.sqlite3", random_string(8).as_str()); + let temp_dir = TempDir::new(random_string(8).as_str()).unwrap(); + let db_folder = temp_dir.path().to_str().unwrap().to_string(); + let db_path = format!("{}{}", db_folder, db_name); + + embed_migrations!("./migrations"); + let conn = SqliteConnection::establish(&db_path).unwrap_or_else(|_| panic!("Error connecting to {}", db_path)); + + embedded_migrations::run_with_output(&conn, &mut std::io::stdout()).expect("Migration failed"); + + let manager = ConnectionManager::::new(db_path); + let pool = diesel::r2d2::Pool::builder().max_size(1).build(manager).unwrap(); + + let conn = pool.get().unwrap(); + conn.execute("PRAGMA foreign_keys = ON").unwrap(); + + assert!(KeyManagerStateSql::get_state(&conn).is_err()); + + let state1 = KeyManagerState { + master_seed: PrivateKey::random(&mut OsRng), + branch_seed: random_string(8), + primary_key_index: 0, + }; + + KeyManagerStateSql::set_state(state1.clone(), &conn).unwrap(); + + let state1_read = KeyManagerStateSql::get_state(&conn).unwrap(); + + assert_eq!(state1, KeyManagerState::try_from(state1_read).unwrap()); + + let state2 = KeyManagerState { + master_seed: PrivateKey::random(&mut OsRng), + branch_seed: random_string(8), + primary_key_index: 0, + }; + + KeyManagerStateSql::set_state(state2.clone(), &conn).unwrap(); + + let state2_read = KeyManagerStateSql::get_state(&conn).unwrap(); + + assert_eq!(state2, KeyManagerState::try_from(state2_read).unwrap()); + + KeyManagerStateSql::increment_index(&conn).unwrap(); + KeyManagerStateSql::increment_index(&conn).unwrap(); + + let state3_read = KeyManagerStateSql::get_state(&conn).unwrap(); + + assert_eq!(state3_read.primary_key_index, 2); + } +} diff --git a/base_layer/wallet/src/schema.rs b/base_layer/wallet/src/schema.rs index c2917b6169..36d45333eb 100644 --- a/base_layer/wallet/src/schema.rs +++ b/base_layer/wallet/src/schema.rs @@ -1,39 +1,101 @@ table! { - contacts (pub_key) { - pub_key -> Text, - screen_name -> Text, - address -> Text, + coinbase_transactions (tx_id) { + tx_id -> BigInt, + amount -> BigInt, + commitment -> Binary, + timestamp -> Timestamp, } } table! { - received_messages (id) { - id -> Binary, - source_pub_key -> Text, - dest_pub_key -> Text, + completed_transactions (tx_id) { + tx_id -> BigInt, + source_public_key -> Binary, + destination_public_key -> Binary, + amount -> BigInt, + fee -> BigInt, + transaction_protocol -> Text, + status -> Integer, message -> Text, timestamp -> Timestamp, } } table! { - sent_messages (id) { - id -> Text, - source_pub_key -> Text, - dest_pub_key -> Text, + contacts (public_key) { + public_key -> Binary, + alias -> Text, + } +} + +table! { + inbound_transactions (tx_id) { + tx_id -> BigInt, + source_public_key -> Binary, + amount -> BigInt, + receiver_protocol -> Text, message -> Text, timestamp -> Timestamp, - acknowledged -> Integer, } } table! { - settings (pub_key) { - pub_key -> Text, - screen_name -> Text, + key_manager_states (id) { + id -> Nullable, + master_seed -> Binary, + branch_seed -> Text, + primary_key_index -> BigInt, + timestamp -> Timestamp, + } +} + +table! { + outbound_transactions (tx_id) { + tx_id -> BigInt, + destination_public_key -> Binary, + amount -> BigInt, + fee -> BigInt, + sender_protocol -> Text, + message -> Text, + timestamp -> Timestamp, + } +} + +table! { + outputs (spending_key) { + spending_key -> Binary, + value -> BigInt, + flags -> Integer, + maturity -> BigInt, + status -> Integer, + tx_id -> Nullable, + } +} + +table! { + peers (public_key) { + public_key -> Binary, + peer -> Text, + } +} + +table! { + pending_transaction_outputs (tx_id) { + tx_id -> BigInt, + timestamp -> Timestamp, } } -joinable!(sent_messages -> contacts (dest_pub_key)); +joinable!(outputs -> pending_transaction_outputs (tx_id)); -allow_tables_to_appear_in_same_query!(contacts, received_messages, sent_messages, settings,); +allow_tables_to_appear_in_same_query!( + coinbase_transactions, + completed_transactions, + contacts, + inbound_transactions, + key_manager_states, + outbound_transactions, + outputs, + peers, + pending_transaction_outputs, +); diff --git a/base_layer/wallet/src/storage/connection_manager.rs b/base_layer/wallet/src/storage/connection_manager.rs new file mode 100644 index 0000000000..caf8e2b283 --- /dev/null +++ b/base_layer/wallet/src/storage/connection_manager.rs @@ -0,0 +1,52 @@ +// Copyright 2020. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::error::WalletStorageError; +use diesel::{ + r2d2::{ConnectionManager, Pool}, + Connection, + SqliteConnection, +}; +use std::{io, path::Path, time::Duration}; + +pub type WalletConnection = Pool>; +const DATABASE_CONNECTION_TIMEOUT_MS: u64 = 2000; + +pub fn run_migration_and_create_connection_pool(db_path: &str) -> Result { + let db_exists = Path::new(db_path).exists(); + if !db_exists { + let connection = SqliteConnection::establish(db_path)?; + connection.execute("PRAGMA foreign_keys = ON; PRAGMA busy_timeout = 60000;")?; + + embed_migrations!("./migrations"); + embedded_migrations::run_with_output(&connection, &mut io::stdout()) + .map_err(|err| WalletStorageError::DatabaseMigrationError(format!("Database migration failed {}", err)))?; + } + + let manager = ConnectionManager::::new(db_path); + Ok(diesel::r2d2::Pool::builder() + .connection_timeout(Duration::from_millis(DATABASE_CONNECTION_TIMEOUT_MS)) + .idle_timeout(Some(Duration::from_millis(DATABASE_CONNECTION_TIMEOUT_MS))) + .max_size(1) + .build(manager) + .map_err(|_| WalletStorageError::R2d2Error)?) +} diff --git a/base_layer/wallet/src/storage/database.rs b/base_layer/wallet/src/storage/database.rs new file mode 100644 index 0000000000..c8ed4ff0b3 --- /dev/null +++ b/base_layer/wallet/src/storage/database.rs @@ -0,0 +1,273 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::error::WalletStorageError; +use log::*; +use std::{ + fmt::{Display, Error, Formatter}, + sync::Arc, +}; +use tari_comms::{peer_manager::Peer, types::CommsPublicKey}; + +const LOG_TARGET: &str = "wallet::database"; + +/// This trait defines the functionality that a database backend need to provide for the Contacts Service +pub trait WalletBackend: Send + Sync { + /// Retrieve the record associated with the provided DbKey + fn fetch(&self, key: &DbKey) -> Result, WalletStorageError>; + /// Modify the state the of the backend with a write operation + fn write(&self, op: WriteOperation) -> Result, WalletStorageError>; +} + +#[derive(Debug, Clone, PartialEq)] +pub enum DbKey { + Peer(CommsPublicKey), + Peers, +} + +pub enum DbValue { + Peer(Box), + Peers(Vec), +} + +pub enum DbKeyValuePair { + Peer(CommsPublicKey, Peer), +} + +pub enum WriteOperation { + Insert(DbKeyValuePair), + Remove(DbKey), +} + +// Private macro that pulls out all the boiler plate of extracting a DB query result from its variants +macro_rules! fetch { + ($db:ident, $key_val:expr, $key_var:ident) => {{ + let key = DbKey::$key_var($key_val); + match $db.fetch(&key) { + Ok(None) => Err(WalletStorageError::ValueNotFound(key)), + Ok(Some(DbValue::$key_var(k))) => Ok(*k), + Ok(Some(other)) => unexpected_result(key, other), + Err(e) => log_error(key, e), + } + }}; +} + +pub struct WalletDatabase +where T: WalletBackend + 'static +{ + db: Arc, +} + +impl WalletDatabase +where T: WalletBackend + 'static +{ + pub fn new(db: T) -> Self { + Self { db: Arc::new(db) } + } + + pub async fn get_peer(&self, pub_key: CommsPublicKey) -> Result { + let db_clone = self.db.clone(); + + tokio::task::spawn_blocking(move || fetch!(db_clone, pub_key.clone(), Peer)) + .await + .or_else(|err| Err(WalletStorageError::BlockingTaskSpawnError(err.to_string()))) + .and_then(|inner_result| inner_result) + } + + pub async fn get_peers(&self) -> Result, WalletStorageError> { + let db_clone = self.db.clone(); + + let c = tokio::task::spawn_blocking(move || match db_clone.fetch(&DbKey::Peers) { + Ok(None) => log_error( + DbKey::Peers, + WalletStorageError::UnexpectedResult("Could not retrieve peers".to_string()), + ), + Ok(Some(DbValue::Peers(c))) => Ok(c), + Ok(Some(other)) => unexpected_result(DbKey::Peers, other), + Err(e) => log_error(DbKey::Peers, e), + }) + .await + .or_else(|err| Err(WalletStorageError::BlockingTaskSpawnError(err.to_string())))??; + Ok(c) + } + + pub async fn save_peer(&self, peer: Peer) -> Result<(), WalletStorageError> { + let db_clone = self.db.clone(); + + tokio::task::spawn_blocking(move || { + db_clone.write(WriteOperation::Insert(DbKeyValuePair::Peer( + peer.public_key.clone(), + peer, + ))) + }) + .await + .or_else(|err| Err(WalletStorageError::BlockingTaskSpawnError(err.to_string())))??; + Ok(()) + } + + pub async fn remove_peer(&self, pub_key: CommsPublicKey) -> Result { + let db_clone = self.db.clone(); + + tokio::task::spawn_blocking(move || { + match db_clone + .write(WriteOperation::Remove(DbKey::Peer(pub_key.clone())))? + .ok_or_else(|| WalletStorageError::ValueNotFound(DbKey::Peer(pub_key.clone())))? + { + DbValue::Peer(c) => Ok(*c), + DbValue::Peers(_) => Err(WalletStorageError::UnexpectedResult( + "Incorrect response from backend.".to_string(), + )), + } + }) + .await + .or_else(|err| Err(WalletStorageError::BlockingTaskSpawnError(err.to_string()))) + .and_then(|inner_result| inner_result) + } +} + +fn unexpected_result(req: DbKey, res: DbValue) -> Result { + let msg = format!("Unexpected result for database query {}. Response: {}", req, res); + error!(target: LOG_TARGET, "{}", msg); + Err(WalletStorageError::UnexpectedResult(msg)) +} + +impl Display for DbKey { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + match self { + DbKey::Peer(c) => f.write_str(&format!("Peer: {:?}", c)), + DbKey::Peers => f.write_str(&"Peers".to_string()), + } + } +} + +impl Display for DbValue { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + match self { + DbValue::Peer(_) => f.write_str(&"Peer".to_string()), + DbValue::Peers(_) => f.write_str(&"Peers".to_string()), + } + } +} + +fn log_error(req: DbKey, err: WalletStorageError) -> Result { + error!( + target: LOG_TARGET, + "Database access error on request: {}: {}", + req, + err.to_string() + ); + Err(err) +} + +#[cfg(test)] +mod test { + use crate::{ + error::WalletStorageError, + storage::{ + connection_manager::run_migration_and_create_connection_pool, + database::{DbKey, WalletBackend, WalletDatabase}, + memory_db::WalletMemoryDatabase, + sqlite_db::WalletSqliteDatabase, + }, + }; + use rand::rngs::OsRng; + use tari_comms::{ + multiaddr::Multiaddr, + peer_manager::{NodeId, Peer, PeerFeatures, PeerFlags}, + types::{CommsPublicKey, CommsSecretKey}, + }; + use tari_core::transactions::types::PublicKey; + use tari_crypto::keys::PublicKey as PublicKeyTrait; + use tari_test_utils::random::string; + use tempdir::TempDir; + use tokio::runtime::Runtime; + + pub fn test_database_crud(backend: T) { + let mut runtime = Runtime::new().unwrap(); + + let db = WalletDatabase::new(backend); + let mut peers = Vec::new(); + for i in 0..5 { + let (_secret_key, public_key): (CommsSecretKey, CommsPublicKey) = PublicKey::random_keypair(&mut OsRng); + + let peer = Peer::new( + public_key.clone(), + NodeId::from_key(&public_key).unwrap(), + "/ip4/1.2.3.4/tcp/9000".parse::().unwrap().into(), + PeerFlags::empty(), + PeerFeatures::COMMUNICATION_NODE, + ); + + peers.push(peer); + + runtime.block_on(db.save_peer(peers[i].clone())).unwrap(); + + match runtime.block_on(db.save_peer(peers[i].clone())) { + Err(WalletStorageError::DuplicateContact) => (), + _ => assert!(false), + } + } + + let got_peers = runtime.block_on(db.get_peers()).unwrap(); + assert_eq!(peers, got_peers); + + let peer = runtime.block_on(db.get_peer(peers[0].public_key.clone())).unwrap(); + assert_eq!(peer, peers[0]); + + let (_secret_key, public_key): (_, PublicKey) = PublicKeyTrait::random_keypair(&mut OsRng); + + match runtime.block_on(db.get_peer(public_key.clone())) { + Err(WalletStorageError::ValueNotFound(DbKey::Peer(_p))) => (), + _ => assert!(false), + } + + match runtime.block_on(db.remove_peer(public_key.clone())) { + Err(WalletStorageError::ValueNotFound(DbKey::Peer(_p))) => (), + _ => assert!(false), + } + + let _ = runtime.block_on(db.remove_peer(peers[0].public_key.clone())).unwrap(); + peers.remove(0); + let got_peers = runtime.block_on(db.get_peers()).unwrap(); + + assert_eq!(peers, got_peers); + } + + #[test] + fn test_database_crud_memory_db() { + test_database_crud(WalletMemoryDatabase::new()); + } + + #[test] + fn test_database_crud_sqlite_db() { + let db_name = format!("{}.sqlite3", string(8).as_str()); + let db_folder = TempDir::new(string(8).as_str()) + .unwrap() + .path() + .to_str() + .unwrap() + .to_string(); + let connection_pool = run_migration_and_create_connection_pool(&format!("{}{}", db_folder, db_name)).unwrap(); + + test_database_crud(WalletSqliteDatabase::new(connection_pool)); + } +} diff --git a/base_layer/wallet/src/storage/memory_db.rs b/base_layer/wallet/src/storage/memory_db.rs new file mode 100644 index 0000000000..c1e3bdc5ce --- /dev/null +++ b/base_layer/wallet/src/storage/memory_db.rs @@ -0,0 +1,97 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + error::WalletStorageError, + storage::database::{DbKey, DbKeyValuePair, DbValue, WalletBackend, WriteOperation}, +}; +use std::sync::{Arc, RwLock}; +use tari_comms::peer_manager::Peer; + +pub struct InnerDatabase { + peers: Vec, +} + +impl InnerDatabase { + pub fn new() -> Self { + Self { peers: Vec::new() } + } +} + +pub struct WalletMemoryDatabase { + db: Arc>, +} + +impl WalletMemoryDatabase { + pub fn new() -> Self { + Self { + db: Arc::new(RwLock::new(InnerDatabase::new())), + } + } +} + +impl Default for WalletMemoryDatabase { + fn default() -> Self { + Self::new() + } +} + +impl WalletBackend for WalletMemoryDatabase { + fn fetch(&self, key: &DbKey) -> Result, WalletStorageError> { + let db = acquire_read_lock!(self.db); + let result = match key { + DbKey::Peer(pk) => db + .peers + .iter() + .find(|v| &v.public_key == pk) + .map(|p| DbValue::Peer(Box::new(p.clone()))), + DbKey::Peers => Some(DbValue::Peers(db.peers.clone())), + }; + + Ok(result) + } + + fn write(&self, op: WriteOperation) -> Result, WalletStorageError> { + let mut db = acquire_write_lock!(self.db); + match op { + WriteOperation::Insert(kvp) => match kvp { + DbKeyValuePair::Peer(pk, p) => { + if db.peers.iter().any(|p| p.public_key == pk) { + return Err(WalletStorageError::DuplicateContact); + } + db.peers.push(p) + }, + }, + WriteOperation::Remove(k) => match k { + DbKey::Peer(pk) => match db.peers.iter().position(|p| p.public_key == pk) { + None => return Err(WalletStorageError::ValueNotFound(DbKey::Peer(pk))), + Some(pos) => return Ok(Some(DbValue::Peer(Box::new(db.peers.remove(pos))))), + }, + DbKey::Peers => { + return Err(WalletStorageError::OperationNotSupported); + }, + }, + } + + Ok(None) + } +} diff --git a/base_layer/core/src/test_utils/mod.rs b/base_layer/wallet/src/storage/mod.rs similarity index 94% rename from base_layer/core/src/test_utils/mod.rs rename to base_layer/wallet/src/storage/mod.rs index 85a7c558f9..062521cbe1 100644 --- a/base_layer/core/src/test_utils/mod.rs +++ b/base_layer/wallet/src/storage/mod.rs @@ -20,5 +20,7 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -/// Helper functions to simplify generated test blockchain data -pub mod builders; +pub mod connection_manager; +pub mod database; +pub mod memory_db; +pub mod sqlite_db; diff --git a/base_layer/wallet/src/storage/sqlite_db.rs b/base_layer/wallet/src/storage/sqlite_db.rs new file mode 100644 index 0000000000..4eb07c5921 --- /dev/null +++ b/base_layer/wallet/src/storage/sqlite_db.rs @@ -0,0 +1,179 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + error::WalletStorageError, + schema::peers, + storage::database::{DbKey, DbKeyValuePair, DbValue, WalletBackend, WriteOperation}, +}; +use diesel::{ + prelude::*, + r2d2::{ConnectionManager, Pool, PooledConnection}, + result::Error as DieselError, + SqliteConnection, +}; +use std::convert::TryFrom; +use tari_comms::peer_manager::Peer; +use tari_crypto::tari_utilities::ByteArray; + +/// A Sqlite backend for the Output Manager Service. The Backend is accessed via a connection pool to the Sqlite file. +pub struct WalletSqliteDatabase { + database_connection_pool: Pool>, +} +impl WalletSqliteDatabase { + pub fn new(database_connection_pool: Pool>) -> Self { + Self { + database_connection_pool, + } + } +} + +impl WalletBackend for WalletSqliteDatabase { + fn fetch(&self, key: &DbKey) -> Result, WalletStorageError> { + let conn = self + .database_connection_pool + .clone() + .get() + .map_err(|_| WalletStorageError::R2d2Error)?; + + let result = match key { + DbKey::Peer(pk) => match PeerSql::find(&pk.to_vec(), &conn) { + Ok(c) => Some(DbValue::Peer(Box::new(Peer::try_from(c)?))), + Err(WalletStorageError::DieselError(DieselError::NotFound)) => None, + Err(e) => return Err(e), + }, + DbKey::Peers => Some(DbValue::Peers( + PeerSql::index(&conn)? + .iter() + .map(|c| Peer::try_from(c.clone())) + .collect::, _>>()?, + )), + }; + + Ok(result) + } + + fn write(&self, op: WriteOperation) -> Result, WalletStorageError> { + let conn = self + .database_connection_pool + .clone() + .get() + .map_err(|_| WalletStorageError::R2d2Error)?; + + match op { + WriteOperation::Insert(kvp) => match kvp { + DbKeyValuePair::Peer(k, p) => { + if let Ok(_) = PeerSql::find(&k.to_vec(), &conn) { + return Err(WalletStorageError::DuplicateContact); + } + PeerSql::try_from(p)?.commit(&conn)?; + }, + }, + WriteOperation::Remove(k) => match k { + DbKey::Peer(k) => match PeerSql::find(&k.to_vec(), &conn) { + Ok(p) => { + p.clone().delete(&conn)?; + return Ok(Some(DbValue::Peer(Box::new(Peer::try_from(p)?)))); + }, + Err(WalletStorageError::DieselError(DieselError::NotFound)) => (), + Err(e) => return Err(e), + }, + DbKey::Peers => return Err(WalletStorageError::OperationNotSupported), + }, + } + + Ok(None) + } +} + +/// A Sql version of the Peer struct +#[derive(Clone, Debug, Queryable, Insertable, PartialEq)] +#[table_name = "peers"] +struct PeerSql { + public_key: Vec, + peer: String, +} + +impl PeerSql { + /// Write this struct to the database + pub fn commit( + &self, + conn: &PooledConnection>, + ) -> Result<(), WalletStorageError> + { + diesel::insert_into(peers::table).values(self.clone()).execute(conn)?; + Ok(()) + } + + /// Return all peers + pub fn index( + conn: &PooledConnection>, + ) -> Result, WalletStorageError> { + Ok(peers::table.load::(conn)?) + } + + /// Find a particular Peer, if it exists + pub fn find( + public_key: &[u8], + conn: &PooledConnection>, + ) -> Result + { + Ok(peers::table + .filter(peers::public_key.eq(public_key)) + .first::(conn)?) + } + + pub fn delete( + &self, + conn: &PooledConnection>, + ) -> Result<(), WalletStorageError> + { + let num_deleted = diesel::delete(peers::table.filter(peers::public_key.eq(&self.public_key))).execute(conn)?; + + if num_deleted == 0 { + return Err(WalletStorageError::ValuesNotFound); + } + + Ok(()) + } +} + +/// Conversion from an Contact to the Sql datatype form +impl TryFrom for Peer { + type Error = WalletStorageError; + + fn try_from(p: PeerSql) -> Result { + Ok(serde_json::from_str(&p.peer)?) + } +} + +/// Conversion from an Contact to the Sql datatype form +impl TryFrom for PeerSql { + type Error = WalletStorageError; + + fn try_from(p: Peer) -> Result { + Ok(Self { + public_key: p.public_key.to_vec(), + peer: serde_json::to_string(&p)?, + }) + } +} diff --git a/base_layer/wallet/src/testnet_utils.rs b/base_layer/wallet/src/testnet_utils.rs new file mode 100644 index 0000000000..3baa7f9a73 --- /dev/null +++ b/base_layer/wallet/src/testnet_utils.rs @@ -0,0 +1,753 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::{ + contacts_service::storage::{ + database::{Contact, ContactsBackend}, + memory_db::ContactsServiceMemoryDatabase, + }, + error::{WalletError, WalletStorageError}, + output_manager_service::{ + storage::{database::OutputManagerBackend, memory_db::OutputManagerMemoryDatabase}, + TxId, + }, + storage::{database::WalletBackend, memory_db::WalletMemoryDatabase}, + transaction_service::storage::{ + database::{CompletedTransaction, TransactionBackend, TransactionStatus}, + memory_db::TransactionMemoryDatabase, + }, + wallet::WalletConfig, + Wallet, +}; +use chrono::{Duration as ChronoDuration, Utc}; +use log::*; +use rand::{distributions::Alphanumeric, rngs::OsRng, CryptoRng, Rng, RngCore}; +use std::{iter, sync::Arc, thread, time::Duration}; +use tari_comms::{ + multiaddr::Multiaddr, + peer_manager::{NodeIdentity, Peer, PeerFeatures, PeerFlags}, + transports::MemoryTransport, + types::{CommsPublicKey, CommsSecretKey}, +}; +use tari_comms_dht::DhtConfig; +use tari_core::transactions::{ + tari_amount::MicroTari, + transaction::{OutputFeatures, Transaction, TransactionInput, UnblindedOutput}, + types::{BlindingFactor, CryptoFactories, PrivateKey, PublicKey}, +}; +use tari_crypto::{ + commitment::HomomorphicCommitmentFactory, + keys::{PublicKey as PublicKeyTrait, SecretKey as SecretKeyTrait}, + tari_utilities::hex::Hex, +}; +use tari_p2p::{initialization::CommsConfig, transport::TransportType}; +use tari_test_utils::collect_stream; +use tokio::runtime::Runtime; + +// Used to generate test wallet data + +const LOG_TARGET: &str = "wallet::test_utils"; + +pub struct TestParams { + pub spend_key: PrivateKey, + pub change_key: PrivateKey, + pub offset: PrivateKey, + pub nonce: PrivateKey, + pub public_nonce: PublicKey, +} +impl TestParams { + pub fn new(rng: &mut R) -> TestParams { + let r = PrivateKey::random(rng); + TestParams { + spend_key: PrivateKey::random(rng), + change_key: PrivateKey::random(rng), + offset: PrivateKey::random(rng), + public_nonce: PublicKey::from_secret_key(&r), + nonce: r, + } + } +} +pub fn make_input( + rng: &mut R, + val: MicroTari, + factories: &CryptoFactories, +) -> (TransactionInput, UnblindedOutput) +{ + let key = PrivateKey::random(rng); + let commitment = factories.commitment.commit_value(&key, val.into()); + let input = TransactionInput::new(OutputFeatures::default(), commitment); + (input, UnblindedOutput::new(val, key, None)) +} + +pub fn random_string(len: usize) -> String { + iter::repeat(()).map(|_| OsRng.sample(Alphanumeric)).take(len).collect() +} + +/// Create a wallet for testing purposes +pub fn create_wallet( + secret_key: CommsSecretKey, + public_address: Multiaddr, + data_path: String, +) -> Wallet +{ + let runtime = Runtime::new().unwrap(); + let factories = CryptoFactories::default(); + + let node_identity = Arc::new( + NodeIdentity::new(secret_key, public_address.clone(), PeerFeatures::COMMUNICATION_NODE) + .expect("Could not construct Node Identity"), + ); + let comms_config = CommsConfig { + transport_type: TransportType::Memory { + listener_address: public_address, + }, + node_identity, + datastore_path: data_path, + peer_database_name: random_string(8), + max_concurrent_inbound_tasks: 100, + outbound_buffer_size: 100, + dht: DhtConfig { + discovery_request_timeout: Duration::from_millis(500), + ..Default::default() + }, + }; + + let config = WalletConfig { + comms_config, + logging_path: None, + factories, + }; + + Wallet::new( + config, + runtime, + WalletMemoryDatabase::new(), + TransactionMemoryDatabase::new(), + OutputManagerMemoryDatabase::new(), + ContactsServiceMemoryDatabase::new(), + ) + .expect("Could not create Wallet") +} + +pub fn get_next_memory_address() -> Multiaddr { + let port = MemoryTransport::acquire_next_memsocket_port(); + format!("/memory/{}", port).parse().unwrap() +} + +/// This function will generate a set of test data for the supplied wallet. Takes a few seconds to complete +pub fn generate_wallet_test_data< + T: WalletBackend, + U: TransactionBackend + Clone, + V: OutputManagerBackend, + W: ContactsBackend, +>( + wallet: &mut Wallet, + data_path: &str, + transaction_service_backend: U, +) -> Result<(), WalletError> +{ + let factories = CryptoFactories::default(); + let names = ["Alice", "Bob", "Carol", "Dave"]; + let private_keys = [ + "3264e7a05ff669c1b71f691ab181ba3dd915306114a26c4a84c8da1dc1c40209", + "fdad65858c7e7985168972f3117e31f7cee5a1d961fce690bd05a2a15ca6f00e", + "07beb0d0d1eef08c246b70da8b060f7f8e885f5c0f2fd04b10607dc744b5f502", + "bb2dcd0b477c8d709afe2547122a7199d6d4516bc6f35c2adb1a8afedbf97e0e", + ]; + + let messages: Vec = vec![ + "My half of dinner", + "Cheers", + "April's rent", + "Thanks for the Skywalker skin", + "Here you go", + "💰💰💰", + "For the 'Tacos' 😉", + "😀", + "My share of the movie tickets", + "Enjoy!", + "😎", + "Tickets!!", + "For the cab fare", + "👍👍", + "🥡", + ] + .iter() + .map(|i| (*i).to_string()) + .collect(); + let mut message_index = 0; + + // Generate contacts + let mut generated_contacts = Vec::new(); + for i in 0..names.len() { + let secret_key = CommsSecretKey::from_hex(private_keys[i]).expect("Could not parse hex key"); + let public_key = CommsPublicKey::from_secret_key(&secret_key); + wallet + .runtime + .block_on(wallet.contacts_service.upsert_contact(Contact { + alias: names[i].to_string(), + public_key: public_key.clone(), + }))?; + + let addr = get_next_memory_address(); + generated_contacts.push((secret_key, addr)); + } + let contacts = wallet.runtime.block_on(wallet.contacts_service.get_contacts())?; + assert_eq!(contacts.len(), names.len()); + info!(target: LOG_TARGET, "Added test contacts to wallet"); + + // Generate outputs + let num_outputs = 75; + for i in 0..num_outputs { + let (_ti, uo) = make_input(&mut OsRng.clone(), MicroTari::from(5_000_000 + i * 35_000), &factories); + wallet.runtime.block_on(wallet.output_manager_service.add_output(uo))?; + } + info!(target: LOG_TARGET, "Added test outputs to wallet"); + // Generate some Tx history + info!( + target: LOG_TARGET, + "Spinning up Alice wallet to generate test transactions" + ); + let alice_temp_dir = format!("{}/{}", data_path, random_string(8)); + let _ = std::fs::create_dir(&alice_temp_dir); + + let mut wallet_alice = create_wallet( + generated_contacts[0].0.clone(), + generated_contacts[0].1.clone(), + alice_temp_dir.clone(), + ); + + for i in 0..20 { + let (_ti, uo) = make_input(&mut OsRng.clone(), MicroTari::from(1_500_000 + i * 530_500), &factories); + wallet_alice + .runtime + .block_on(wallet_alice.output_manager_service.add_output(uo))?; + } + info!(target: LOG_TARGET, "Alice Wallet created"); + info!( + target: LOG_TARGET, + "Spinning up Bob wallet to generate test transactions" + ); + let bob_temp_dir = format!("{}/{}", data_path, random_string(8)); + let _ = std::fs::create_dir(&bob_temp_dir); + + let mut wallet_bob = create_wallet( + generated_contacts[1].0.clone(), + generated_contacts[1].1.clone(), + bob_temp_dir.clone(), + ); + for i in 0..20 { + let (_ti, uo) = make_input( + &mut OsRng.clone(), + MicroTari::from(2_000_000 + i * i * 61_050), + &factories, + ); + wallet_bob + .runtime + .block_on(wallet_bob.output_manager_service.add_output(uo))?; + } + info!(target: LOG_TARGET, "Bob Wallet created"); + + let alice_peer = Peer::new( + wallet_alice.comms.node_identity().public_key().clone(), + wallet_alice.comms.node_identity().node_id().clone(), + vec![wallet_alice.comms.listening_address().clone()].into(), + PeerFlags::empty(), + PeerFeatures::COMMUNICATION_NODE, + ); + + wallet.comms.peer_manager().add_peer(alice_peer.clone())?; + let bob_peer = Peer::new( + wallet_bob.comms.node_identity().public_key().clone(), + wallet_bob.comms.node_identity().node_id().clone(), + vec![wallet_bob.comms.listening_address().clone()].into(), + PeerFlags::empty(), + PeerFeatures::COMMUNICATION_NODE, + ); + + wallet.comms.peer_manager().add_peer(bob_peer.clone())?; + + wallet + .runtime + .block_on( + wallet + .comms + .connection_manager() + .dial_peer(wallet_alice.comms.node_identity().node_id().clone()), + ) + .unwrap(); + wallet + .runtime + .block_on( + wallet + .comms + .connection_manager() + .dial_peer(wallet_bob.comms.node_identity().node_id().clone()), + ) + .unwrap(); + info!(target: LOG_TARGET, "Starting to execute test transactions"); + + // Completed TX + wallet.runtime.block_on(wallet.transaction_service.send_transaction( + contacts[0].public_key.clone(), + MicroTari::from(1_100_000), + MicroTari::from(100), + messages[message_index].clone(), + ))?; + message_index = (message_index + 1) % messages.len(); + wallet.runtime.block_on(wallet.transaction_service.send_transaction( + contacts[0].public_key.clone(), + MicroTari::from(2_010_500), + MicroTari::from(110), + messages[message_index].clone(), + ))?; + message_index = (message_index + 1) % messages.len(); + + wallet.runtime.block_on(wallet.transaction_service.send_transaction( + contacts[0].public_key.clone(), + MicroTari::from(10_000_000), + MicroTari::from(110), + messages[message_index].clone(), + ))?; + message_index = (message_index + 1) % messages.len(); + + wallet.runtime.block_on(wallet.transaction_service.send_transaction( + contacts[1].public_key.clone(), + MicroTari::from(3_441_000), + MicroTari::from(105), + messages[message_index].clone(), + ))?; + message_index = (message_index + 1) % messages.len(); + + wallet.runtime.block_on(wallet.transaction_service.send_transaction( + contacts[1].public_key.clone(), + MicroTari::from(14_100_000), + MicroTari::from(100), + messages[message_index].clone(), + ))?; + message_index = (message_index + 1) % messages.len(); + wallet.runtime.block_on(wallet.transaction_service.send_transaction( + contacts[0].public_key.clone(), + MicroTari::from(22_010_500), + MicroTari::from(110), + messages[message_index].clone(), + ))?; + message_index = (message_index + 1) % messages.len(); + + wallet.runtime.block_on(wallet.transaction_service.send_transaction( + contacts[0].public_key.clone(), + MicroTari::from(17_000_000), + MicroTari::from(110), + messages[message_index].clone(), + ))?; + message_index = (message_index + 1) % messages.len(); + + wallet.runtime.block_on(wallet.transaction_service.send_transaction( + contacts[1].public_key.clone(), + MicroTari::from(31_441_000), + MicroTari::from(105), + messages[message_index].clone(), + ))?; + message_index = (message_index + 1) % messages.len(); + + wallet.runtime.block_on(wallet.transaction_service.send_transaction( + contacts[0].public_key.clone(), + MicroTari::from(12_100_000), + MicroTari::from(100), + messages[message_index].clone(), + ))?; + message_index = (message_index + 1) % messages.len(); + wallet.runtime.block_on(wallet.transaction_service.send_transaction( + contacts[1].public_key.clone(), + MicroTari::from(28_010_500), + MicroTari::from(110), + messages[message_index].clone(), + ))?; + message_index = (message_index + 1) % messages.len(); + + // Pending Outbound + let _ = wallet.runtime.block_on(wallet.transaction_service.send_transaction( + contacts[2].public_key.clone(), + MicroTari::from(2_500_000), + MicroTari::from(107), + messages[message_index].clone(), + )); + message_index = (message_index + 1) % messages.len(); + + let _ = wallet.runtime.block_on(wallet.transaction_service.send_transaction( + contacts[3].public_key.clone(), + MicroTari::from(3_512_000), + MicroTari::from(117), + messages[message_index].clone(), + )); + message_index = (message_index + 1) % messages.len(); + + // Make sure that the messages have been received by the alice and bob wallets before they start sending messages so + // that they have the wallet in their peer_managers + let alice_event_stream = wallet_alice.transaction_service.get_event_stream_fused(); + let bob_event_stream = wallet_bob.transaction_service.get_event_stream_fused(); + + let _alice_stream = wallet_alice.runtime.block_on(async { + collect_stream!( + alice_event_stream.map(|i| (*i).clone()), + take = 12, + timeout = Duration::from_secs(60) + ) + }); + + let _bob_stream = wallet_bob.runtime.block_on(async { + collect_stream!( + bob_event_stream.map(|i| (*i).clone()), + take = 8, + timeout = Duration::from_secs(60) + ) + }); + // Make sure that the messages have been received by the alice and bob wallets before they start sending messages so + // that they have the wallet in their peer_managers + let alice_event_stream = wallet_alice.transaction_service.get_event_stream_fused(); + let bob_event_stream = wallet_bob.transaction_service.get_event_stream_fused(); + + let _alice_stream = wallet_bob.runtime.block_on(async { + collect_stream!( + alice_event_stream.map(|i| (*i).clone()), + take = 6, + timeout = Duration::from_secs(60) + ) + }); + + let _bob_stream = wallet_bob.runtime.block_on(async { + collect_stream!( + bob_event_stream.map(|i| (*i).clone()), + take = 2, + timeout = Duration::from_secs(60) + ) + }); + + // Pending Inbound + wallet_alice + .runtime + .block_on(wallet_alice.transaction_service.send_transaction( + wallet.comms.node_identity().public_key().clone(), + MicroTari::from(1_235_000), + MicroTari::from(117), + messages[message_index].clone(), + ))?; + message_index = (message_index + 1) % messages.len(); + + wallet_alice + .runtime + .block_on(wallet_alice.transaction_service.send_transaction( + wallet.comms.node_identity().public_key().clone(), + MicroTari::from(3_500_000), + MicroTari::from(117), + messages[message_index].clone(), + ))?; + message_index = (message_index + 1) % messages.len(); + + wallet_alice + .runtime + .block_on(wallet_alice.transaction_service.send_transaction( + wallet.comms.node_identity().public_key().clone(), + MicroTari::from(2_335_000), + MicroTari::from(117), + messages[message_index].clone(), + ))?; + message_index = (message_index + 1) % messages.len(); + + wallet_bob + .runtime + .block_on(wallet_bob.transaction_service.send_transaction( + wallet.comms.node_identity().public_key().clone(), + MicroTari::from(8_035_000), + MicroTari::from(117), + messages[message_index].clone(), + ))?; + message_index = (message_index + 1) % messages.len(); + + wallet_bob + .runtime + .block_on(wallet_bob.transaction_service.send_transaction( + wallet.comms.node_identity().public_key().clone(), + MicroTari::from(5_135_000), + MicroTari::from(117), + messages[message_index].clone(), + ))?; + + let wallet_event_stream = wallet.transaction_service.get_event_stream_fused(); + let _wallet_stream = wallet.runtime.block_on(async { + collect_stream!( + wallet_event_stream.map(|i| (*i).clone()), + take = 20, + timeout = Duration::from_secs(60) + ) + }); + + let txs = wallet + .runtime + .block_on(wallet.transaction_service.get_completed_transactions()) + .unwrap(); + + let timestamps = vec![ + Utc::now() + .naive_utc() + .checked_sub_signed(ChronoDuration::seconds(60)) + .unwrap(), + Utc::now() + .naive_utc() + .checked_sub_signed(ChronoDuration::minutes(5)) + .unwrap(), + Utc::now() + .naive_utc() + .checked_sub_signed(ChronoDuration::minutes(11)) + .unwrap(), + Utc::now() + .naive_utc() + .checked_sub_signed(ChronoDuration::hours(2)) + .unwrap(), + Utc::now() + .naive_utc() + .checked_sub_signed(ChronoDuration::hours(3)) + .unwrap(), + Utc::now() + .naive_utc() + .checked_sub_signed(ChronoDuration::hours(8)) + .unwrap(), + Utc::now() + .naive_utc() + .checked_sub_signed(ChronoDuration::hours(27)) + .unwrap(), + Utc::now() + .naive_utc() + .checked_sub_signed(ChronoDuration::hours(34)) + .unwrap(), + Utc::now() + .naive_utc() + .checked_sub_signed(ChronoDuration::hours(51)) + .unwrap(), + Utc::now() + .naive_utc() + .checked_sub_signed(ChronoDuration::hours(59)) + .unwrap(), + Utc::now() + .naive_utc() + .checked_sub_signed(ChronoDuration::days(9)) + .unwrap() + .checked_sub_signed(ChronoDuration::hours(3)) + .unwrap(), + Utc::now() + .naive_utc() + .checked_sub_signed(ChronoDuration::days(10)) + .unwrap() + .checked_sub_signed(ChronoDuration::hours(6)) + .unwrap(), + Utc::now() + .naive_utc() + .checked_sub_signed(ChronoDuration::days(12)) + .unwrap() + .checked_sub_signed(ChronoDuration::hours(2)) + .unwrap(), + Utc::now() + .naive_utc() + .checked_sub_signed(ChronoDuration::days(15)) + .unwrap() + .checked_sub_signed(ChronoDuration::hours(2)) + .unwrap(), + Utc::now() + .naive_utc() + .checked_sub_signed(ChronoDuration::days(16)) + .unwrap() + .checked_sub_signed(ChronoDuration::hours(2)) + .unwrap(), + ]; + let mut timestamp_index = 0; + + for k in txs.keys() { + let _ = transaction_service_backend + .update_completed_transaction_timestamp((*k).clone(), timestamps[timestamp_index].clone()); + timestamp_index = (timestamp_index + 1) % timestamps.len(); + } + + let mut keys = Vec::new(); + + for k in txs.keys().take(2) { + keys.push(k); + } + + // Broadcast a tx + wallet + .runtime + .block_on(wallet.transaction_service.test_broadcast_transaction(keys[0].clone())) + .unwrap(); + + // Mine a tx + wallet + .runtime + .block_on(wallet.transaction_service.test_mine_transaction(keys[1].clone())) + .unwrap(); + + thread::sleep(Duration::from_millis(1000)); + + let _ = wallet_alice.shutdown(); + let _ = wallet_bob.shutdown(); + + let _ = std::fs::remove_dir_all(&alice_temp_dir); + let _ = std::fs::remove_dir_all(&bob_temp_dir); + + info!(target: LOG_TARGET, "Finished generating test data"); + + Ok(()) +} + +/// This function is only available for testing and development by the client of LibWallet. It simulates a this node, +/// who sent a transaction out, accepting a reply to the Pending Outbound Transaction. That transaction then becomes a +/// CompletedTransaction with the Broadcast status indicating it is in a base node Mempool but not yet mined +pub fn complete_sent_transaction< + T: WalletBackend, + U: TransactionBackend + Clone, + V: OutputManagerBackend, + W: ContactsBackend, +>( + wallet: &mut Wallet, + tx_id: TxId, +) -> Result<(), WalletError> +{ + let pending_outbound_tx = wallet + .runtime + .block_on(wallet.transaction_service.get_pending_outbound_transactions())?; + match pending_outbound_tx.get(&tx_id) { + Some(p) => { + let completed_tx: CompletedTransaction = CompletedTransaction { + tx_id: p.tx_id, + source_public_key: wallet.comms.node_identity().public_key().clone(), + destination_public_key: p.destination_public_key.clone(), + amount: p.amount, + fee: p.fee, + transaction: Transaction::new(Vec::new(), Vec::new(), Vec::new(), BlindingFactor::default()), + message: p.message.clone(), + status: TransactionStatus::Completed, + timestamp: Utc::now().naive_utc(), + }; + wallet.runtime.block_on( + wallet + .transaction_service + .test_complete_pending_transaction(completed_tx), + )?; + }, + None => { + return Err(WalletError::WalletStorageError(WalletStorageError::UnexpectedResult( + "Pending outbound transaction does not exist".to_string(), + ))) + }, + } + + Ok(()) +} + +/// This function is only available for testing by the client of LibWallet. This function simulates an external +/// wallet sending a transaction to this wallet which will become a PendingInboundTransaction +pub fn receive_test_transaction< + T: WalletBackend, + U: TransactionBackend + Clone, + V: OutputManagerBackend, + W: ContactsBackend, +>( + wallet: &mut Wallet, +) -> Result<(), WalletError> { + let contacts = wallet.runtime.block_on(wallet.contacts_service.get_contacts()).unwrap(); + let (_secret_key, mut public_key): (CommsSecretKey, CommsPublicKey) = PublicKey::random_keypair(&mut OsRng); + + if !contacts.is_empty() { + public_key = contacts[0].public_key.clone(); + } + + wallet + .runtime + .block_on(wallet.transaction_service.test_accept_transaction( + OsRng.next_u64(), + MicroTari::from(10_000 + OsRng.next_u64() % 10_1000), + public_key, + ))?; + + Ok(()) +} + +/// This function is only available for testing and development by the client of LibWallet. It simulates this node, +/// who received a prior inbound transaction, accepting the Finalized Completed transaction from the Sender. That +/// transaction then becomes a CompletedTransaction with the Broadcast status indicating it is in a base node Mempool +/// but not yet mined +pub fn finalize_received_transaction< + T: WalletBackend, + U: TransactionBackend + Clone, + V: OutputManagerBackend, + W: ContactsBackend, +>( + wallet: &mut Wallet, + tx_id: TxId, +) -> Result<(), WalletError> +{ + wallet + .runtime + .block_on(wallet.transaction_service.test_finalize_transaction(tx_id))?; + + Ok(()) +} + +/// This function is only available for testing and development by the client of LibWallet. This function will simulate +/// the event when a CompletedTransaction that is in the Complete status is broadcast to the Mempool and its status +/// moves to Broadcast. After this function is called the status of the CompletedTransaction becomes `Mined` and the +/// funds that were pending become spent and available respectively. +pub fn broadcast_transaction< + T: WalletBackend, + U: TransactionBackend + Clone, + V: OutputManagerBackend, + W: ContactsBackend, +>( + wallet: &mut Wallet, + tx_id: TxId, +) -> Result<(), WalletError> +{ + wallet + .runtime + .block_on(wallet.transaction_service.test_broadcast_transaction(tx_id))?; + + Ok(()) +} + +/// This function is only available for testing and development by the client of LibWallet. This function will simulate +/// the event when a CompletedTransaction that is in the Broadcast status, is in a mempool but not mined, beocmes +/// mined/confirmed. After this function is called the status of the CompletedTransaction becomes `Mined` and the funds +/// that were pending become spent and available respectively. +pub fn mine_transaction< + T: WalletBackend, + U: TransactionBackend + Clone, + V: OutputManagerBackend, + W: ContactsBackend, +>( + wallet: &mut Wallet, + tx_id: TxId, +) -> Result<(), WalletError> +{ + wallet + .runtime + .block_on(wallet.transaction_service.test_mine_transaction(tx_id))?; + + Ok(()) +} diff --git a/base_layer/wallet/src/text_message_service/error.rs b/base_layer/wallet/src/text_message_service/error.rs index 1e8d59007e..1e77ac0cbf 100644 --- a/base_layer/wallet/src/text_message_service/error.rs +++ b/base_layer/wallet/src/text_message_service/error.rs @@ -22,28 +22,31 @@ use derive_error::Error; use diesel::result::{ConnectionError as DieselConnectionError, Error as DieselError}; -use tari_comms::{ - builder::CommsServicesError, - connection::NetAddressError, - dispatcher::DispatchError, - message::MessageError, - outbound_message_service::OutboundError, -}; -use tari_p2p::sync_services::ServiceError; -use tari_utilities::{hex::HexError, message_format::MessageFormatError}; +use tari_comms::{builder::CommsError, connection::NetAddressError, message::MessageError}; +use tari_comms_dht::outbound::DhtOutboundError; +use tari_p2p::services::liveness::error::LivenessError; +use tari_service_framework::reply_channel::TransportChannelError; +use tari_crypto::tari_utilities::{hex::HexError, message_format::MessageFormatError}; +use tokio_executor::threadpool::BlockingError; #[derive(Debug, Error)] pub enum TextMessageError { MessageFormatError(MessageFormatError), - DispatchError(DispatchError), MessageError(MessageError), - OutboundError(OutboundError), - ServiceError(ServiceError), - CommsServicesError(CommsServicesError), + OutboundError(DhtOutboundError), + CommsServicesError(CommsError), HexError(HexError), DatabaseError(DieselError), + TransportChannelError(TransportChannelError), + #[error(msg_embedded, no_from, non_std)] + DatabaseMigrationError(String), NetAddressError(NetAddressError), DatabaseConnectionError(DieselConnectionError), + BlockingError(BlockingError), + R2d2Error, + LivenessError(LivenessError), + /// An error has occurred reading or writing the event subscriber stream + EventStreamError, /// If a received TextMessageAck doesn't matching any pending messages MessageNotFound, /// Failed to send from API diff --git a/base_layer/wallet/src/text_message_service/handle.rs b/base_layer/wallet/src/text_message_service/handle.rs new file mode 100644 index 0000000000..db56899413 --- /dev/null +++ b/base_layer/wallet/src/text_message_service/handle.rs @@ -0,0 +1,175 @@ +// Copyright 2019 The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::{ + model::{Contact, UpdateContact}, + service::TextMessages, +}; +use crate::text_message_service::error::TextMessageError; +use futures::{stream::Fuse, StreamExt}; +use tari_broadcast_channel::Subscriber; +use tari_comms::types::CommsPublicKey; +use tari_service_framework::reply_channel::SenderService; +use tower::Service; + +/// API Request enum +#[derive(Debug, PartialEq)] +pub enum TextMessageRequest { + SendTextMessage((CommsPublicKey, String)), + GetTextMessages, + GetTextMessagesByPubKey(CommsPublicKey), + SetScreenName(String), + GetScreenName, + AddContact(Contact), + RemoveContact(Contact), + GetContacts, + UpdateContact((CommsPublicKey, UpdateContact)), +} + +/// API Response enum +#[derive(Debug)] +pub enum TextMessageResponse { + MessageSent, + TextMessages(TextMessages), + ScreenName(Option), + ScreenNameSet, + ContactAdded, + ContactRemoved, + Contacts(Vec), + ContactUpdated, +} + +/// Events that can be published on the Text Message Service Event Stream +#[derive(Debug, Hash, PartialEq, Eq)] +pub enum TextMessageEvent { + ReceivedTextMessage, + ReceivedTextMessageAck, +} + +#[derive(Clone)] +pub struct TextMessageHandle { + handle: SenderService>, + event_stream: Subscriber, +} + +impl TextMessageHandle { + pub fn new( + handle: SenderService>, + event_stream: Subscriber, + ) -> Self + { + Self { handle, event_stream } + } + + pub fn get_event_stream_fused(&self) -> Fuse> { + self.event_stream.clone().fuse() + } + + pub async fn send_text_message( + &mut self, + dest_pubkey: CommsPublicKey, + message: String, + ) -> Result<(), TextMessageError> + { + match self + .handle + .call(TextMessageRequest::SendTextMessage((dest_pubkey, message))) + .await?? + { + TextMessageResponse::MessageSent => Ok(()), + _ => Err(TextMessageError::UnexpectedApiResponse), + } + } + + pub async fn get_text_messages(&mut self) -> Result { + match self.handle.call(TextMessageRequest::GetTextMessages).await?? { + TextMessageResponse::TextMessages(t) => Ok(t), + _ => Err(TextMessageError::UnexpectedApiResponse), + } + } + + pub async fn get_text_messages_by_pub_key( + &mut self, + pubkey: CommsPublicKey, + ) -> Result + { + match self + .handle + .call(TextMessageRequest::GetTextMessagesByPubKey(pubkey)) + .await?? + { + TextMessageResponse::TextMessages(t) => Ok(t), + _ => Err(TextMessageError::UnexpectedApiResponse), + } + } + + pub async fn set_screen_name(&mut self, name: String) -> Result<(), TextMessageError> { + match self.handle.call(TextMessageRequest::SetScreenName(name)).await?? { + TextMessageResponse::ScreenNameSet => Ok(()), + _ => Err(TextMessageError::UnexpectedApiResponse), + } + } + + pub async fn get_screen_name(&mut self) -> Result, TextMessageError> { + match self.handle.call(TextMessageRequest::GetScreenName).await?? { + TextMessageResponse::ScreenName(s) => Ok(s), + _ => Err(TextMessageError::UnexpectedApiResponse), + } + } + + pub async fn add_contact(&mut self, contact: Contact) -> Result<(), TextMessageError> { + match self.handle.call(TextMessageRequest::AddContact(contact)).await?? { + TextMessageResponse::ContactAdded => Ok(()), + _ => Err(TextMessageError::UnexpectedApiResponse), + } + } + + pub async fn remove_contact(&mut self, contact: Contact) -> Result<(), TextMessageError> { + match self.handle.call(TextMessageRequest::RemoveContact(contact)).await?? { + TextMessageResponse::ContactRemoved => Ok(()), + _ => Err(TextMessageError::UnexpectedApiResponse), + } + } + + pub async fn get_contacts(&mut self) -> Result, TextMessageError> { + match self.handle.call(TextMessageRequest::GetContacts).await?? { + TextMessageResponse::Contacts(v) => Ok(v), + _ => Err(TextMessageError::UnexpectedApiResponse), + } + } + + pub async fn update_contact( + &mut self, + pubkey: CommsPublicKey, + update_contact: UpdateContact, + ) -> Result<(), TextMessageError> + { + match self + .handle + .call(TextMessageRequest::UpdateContact((pubkey, update_contact))) + .await?? + { + TextMessageResponse::ContactUpdated => Ok(()), + _ => Err(TextMessageError::UnexpectedApiResponse), + } + } +} diff --git a/base_layer/wallet/src/text_message_service/mod.rs b/base_layer/wallet/src/text_message_service/mod.rs index 48e3067c7a..e793559a45 100644 --- a/base_layer/wallet/src/text_message_service/mod.rs +++ b/base_layer/wallet/src/text_message_service/mod.rs @@ -20,9 +20,131 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -mod error; -mod model; +pub mod error; +pub mod handle; +pub mod model; mod service; -pub use model::{Contact, ReceivedTextMessage, SentTextMessage, UpdateContact}; -pub use service::{TextMessageApiResponse, TextMessageService, TextMessageServiceApi, TextMessages}; +use self::service::TextMessageService; +use crate::text_message_service::{handle::TextMessageHandle, model::ReceivedTextMessage, service::TextMessageAck}; +use futures::{future, stream::StreamExt, Future, Stream}; +use log::*; +use std::sync::Arc; +use tari_broadcast_channel::bounded; +use tari_comms::types::CommsPublicKey; +use tari_comms_dht::outbound::OutboundMessageRequester; +use tari_p2p::{ + comms_connector::PeerMessage, + domain_message::DomainMessage, + services::{ + liveness::handle::LivenessHandle, + utils::{map_deserialized, ok_or_skip_result}, + }, + tari_message::{ExtendedMessage, TariMessageType}, +}; +use tari_pubsub::TopicSubscriptionFactory; +use tari_service_framework::{ + handles::ServiceHandlesFuture, + reply_channel, + ServiceInitializationError, + ServiceInitializer, +}; +use tokio::runtime::runtime::Handle; + +const LOG_TARGET: &str = "wallet::text_message_service::initializer"; + +pub struct TextMessageServiceInitializer { + pub_key: Option, + database_path: Option, + subscription_factory: Arc>>>, +} + +impl TextMessageServiceInitializer { + pub fn new( + subscription_factory: Arc>>>, + pub_key: CommsPublicKey, + database_path: String, + ) -> Self + { + Self { + pub_key: Some(pub_key), + database_path: Some(database_path), + subscription_factory, + } + } + + /// Get a stream of inbound Text messages + fn text_message_stream(&self) -> impl Stream> { + self.subscription_factory + .get_subscription(TariMessageType::new(ExtendedMessage::Text)) + .map(map_deserialized::) + .filter_map(ok_or_skip_result) + } + + fn text_message_ack_stream(&self) -> impl Stream> { + self.subscription_factory + .get_subscription(TariMessageType::new(ExtendedMessage::TextAck)) + .map(map_deserialized::) + .filter_map(ok_or_skip_result) + } +} + +impl ServiceInitializer for TextMessageServiceInitializer { + type Future = impl Future>; + + fn initialize(&mut self, executor: runtime::Handle, handles_fut: ServiceHandlesFuture) -> Self::Future { + let pub_key = self + .pub_key + .take() + .expect("text message service initializer already called"); + + let database_path = self + .database_path + .take() + .expect("text message service initializer already called"); + + let (sender, receiver) = reply_channel::unbounded(); + let (publisher, subscriber) = bounded(100); + + let text_message_stream = self.text_message_stream(); + let text_message_ack_stream = self.text_message_ack_stream(); + + let tms_handle = TextMessageHandle::new(sender, subscriber); + + // Register handle before waiting for handles to be ready + handles_fut.register(tms_handle); + + executor.spawn(async move { + let handles = handles_fut.await; + + let oms = handles + .get_handle::() + .expect("OMS handle required for TextMessageService"); + let liveness = handles + .get_handle::() + .expect("Liveness handle required for TextMessageService"); + + let service = TextMessageService::new( + receiver, + text_message_stream, + text_message_ack_stream, + pub_key, + database_path, + oms, + liveness, + publisher, + ); + + match service.start().await { + Ok(_) => { + info!(target: LOG_TARGET, "Text message service initializer exited cleanly"); + }, + Err(err) => { + error!(target: LOG_TARGET, "Text message service failed to start: {}", err); + }, + } + }); + + future::ready(Ok(())) + } +} diff --git a/base_layer/wallet/src/text_message_service/model.rs b/base_layer/wallet/src/text_message_service/model.rs index 8e1607d3db..c4ffd2c0f4 100644 --- a/base_layer/wallet/src/text_message_service/model.rs +++ b/base_layer/wallet/src/text_message_service/model.rs @@ -29,22 +29,23 @@ use crate::{ types::HashDigest, }; -use diesel::{dsl::count, prelude::*, query_dsl::RunQueryDsl, result::Error as DieselError, SqliteConnection}; +use diesel::{ + dsl::count, + prelude::*, + query_dsl::RunQueryDsl, + r2d2::{ConnectionManager, PooledConnection}, + result::Error as DieselError, + SqliteConnection, +}; use digest::Digest; use serde::{Deserialize, Serialize}; -use std::{ - cmp::Ordering, - convert::{TryFrom, TryInto}, -}; -use tari_comms::{ - connection::NetAddress, - message::{Message, MessageError}, -}; -use tari_p2p::tari_message::{ExtendedMessage, TariMessageType}; -use tari_utilities::{ +use std::{cmp::Ordering, convert::TryFrom}; +use tari_comms::connection::NetAddress; +use tari_crypto::tari_utilities::{ byte_array::ByteArray, hex::{from_hex, Hex}, }; +use rand::rngs::OsRng; /// This function generates a unique ID hash for a Text Message from the message components and an index integer /// @@ -69,7 +70,7 @@ pub fn generate_id( } /// Represents a single Text Message to be sent that includes an acknowledged field -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct SentTextMessage { pub id: Vec, pub source_pub_key: CommsPublicKey, @@ -77,6 +78,7 @@ pub struct SentTextMessage { pub message: String, pub timestamp: NaiveDateTime, pub acknowledged: bool, + pub is_read: bool, } /// The Native Sql version of the SentTextMessage model @@ -89,6 +91,7 @@ struct SentTextMessageSql { pub message: String, pub timestamp: NaiveDateTime, pub acknowledged: i32, + pub is_read: i32, } impl SentTextMessage { @@ -113,17 +116,22 @@ impl SentTextMessage { message, timestamp, acknowledged: false, + is_read: false, } } - pub fn commit(&self, conn: &SqliteConnection) -> Result<(), TextMessageError> { + pub fn commit(&self, conn: &PooledConnection>) -> Result<(), TextMessageError> { diesel::insert_into(sent_messages::table) .values(SentTextMessageSql::from(self.clone())) .execute(conn)?; Ok(()) } - pub fn find(id: &Vec, conn: &SqliteConnection) -> Result { + pub fn find( + id: &Vec, + conn: &PooledConnection>, + ) -> Result + { SentTextMessage::try_from( sent_messages::table .filter(sent_messages::id.eq(id.to_hex())) @@ -133,7 +141,7 @@ impl SentTextMessage { pub fn find_by_dest_pub_key( dest_pub_key: &CommsPublicKey, - conn: &SqliteConnection, + conn: &PooledConnection>, ) -> Result, TextMessageError> { let messages = sent_messages::table @@ -149,7 +157,9 @@ impl SentTextMessage { Ok(result) } - pub fn index(conn: &SqliteConnection) -> Result, TextMessageError> { + pub fn index( + conn: &PooledConnection>, + ) -> Result, TextMessageError> { let messages = sent_messages::table.load::(conn)?; let mut result: Vec = Vec::new(); @@ -162,7 +172,7 @@ impl SentTextMessage { pub fn count_by_dest_pub_key( dest_pub_key: &CommsPublicKey, - conn: &SqliteConnection, + conn: &PooledConnection>, ) -> Result { Ok(sent_messages::table @@ -171,7 +181,11 @@ impl SentTextMessage { .first(conn)?) } - pub fn mark_sent_message_ack(id: Vec, conn: &SqliteConnection) -> Result<(), TextMessageError> { + pub fn mark_sent_message_ack( + id: &Vec, + conn: &PooledConnection>, + ) -> Result<(), TextMessageError> + { let num_updated = diesel::update(sent_messages::table.filter(sent_messages::id.eq(&id.to_hex()))) .set(UpdateAckSentTextMessage { acknowledged: Some(1i32), @@ -184,6 +198,22 @@ impl SentTextMessage { Ok(()) } + + pub fn mark_sent_message_opened( + id: Vec, + conn: &PooledConnection>, + ) -> Result<(), TextMessageError> + { + let num_updated = diesel::update(sent_messages::table.filter(sent_messages::id.eq(&id.to_hex()))) + .set(UpdateOpenedSentTextMessage { is_read: Some(1i32) }) + .execute(conn)?; + + if num_updated == 0 { + return Err(TextMessageError::DatabaseUpdateError); + } + + Ok(()) + } } impl From for SentTextMessageSql { @@ -195,6 +225,7 @@ impl From for SentTextMessageSql { message: msg.message, timestamp: msg.timestamp, acknowledged: msg.acknowledged as i32, + is_read: msg.is_read as i32, } } } @@ -210,18 +241,11 @@ impl TryFrom for SentTextMessage { message: msg.message, timestamp: msg.timestamp, acknowledged: msg.acknowledged != 0, + is_read: msg.is_read != 0, }) } } -impl TryInto for SentTextMessage { - type Error = MessageError; - - fn try_into(self) -> Result { - (TariMessageType::new(ExtendedMessage::Text), self).try_into() - } -} - /// The changeset to mark a SentTextMessage as acknowledged #[derive(AsChangeset)] #[table_name = "sent_messages"] @@ -229,6 +253,13 @@ pub struct UpdateAckSentTextMessage { pub acknowledged: Option, } +/// The changeset to mark a SentTextMessage as read +#[derive(AsChangeset)] +#[table_name = "sent_messages"] +pub struct UpdateOpenedSentTextMessage { + pub is_read: Option, +} + /// Represents a single received Text Message #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] pub struct ReceivedTextMessage { @@ -252,14 +283,16 @@ struct ReceivedTextMessageSql { impl ReceivedTextMessage { // Does not require new as these will only ever be received - pub fn commit(&self, conn: &SqliteConnection) -> Result<(), TextMessageError> { + pub fn commit(&self, conn: &PooledConnection>) -> Result<(), TextMessageError> { diesel::insert_into(received_messages::table) .values(ReceivedTextMessageSql::from(self.clone())) .execute(conn)?; Ok(()) } - pub fn index(conn: &SqliteConnection) -> Result, TextMessageError> { + pub fn index( + conn: &PooledConnection>, + ) -> Result, TextMessageError> { let messages = received_messages::table.load::(conn)?; let mut result: Vec = Vec::new(); @@ -270,7 +303,11 @@ impl ReceivedTextMessage { Ok(result) } - pub fn find(id: &Vec, conn: &SqliteConnection) -> Result { + pub fn find( + id: &Vec, + conn: &PooledConnection>, + ) -> Result + { ReceivedTextMessage::try_from( received_messages::table .filter(received_messages::id.eq(id)) @@ -280,7 +317,7 @@ impl ReceivedTextMessage { pub fn find_by_source_pub_key( source_pub_key: &CommsPublicKey, - conn: &SqliteConnection, + conn: &PooledConnection>, ) -> Result, TextMessageError> { let messages = received_messages::table @@ -332,6 +369,7 @@ impl From for SentTextMessage { message: t.message, timestamp: t.timestamp, acknowledged: false, + is_read: false, } } } @@ -348,14 +386,6 @@ impl From for ReceivedTextMessage { } } -impl TryInto for ReceivedTextMessage { - type Error = MessageError; - - fn try_into(self) -> Result { - (TariMessageType::new(ExtendedMessage::Text), self).try_into() - } -} - impl PartialOrd for ReceivedTextMessage { /// Orders OutboundMessage from least to most time remaining from being scheduled fn partial_cmp(&self, other: &Self) -> Option { @@ -370,6 +400,20 @@ impl Ord for ReceivedTextMessage { } } +impl PartialOrd for SentTextMessage { + /// Orders InboundMessage from least to most time remaining from being scheduled + fn partial_cmp(&self, other: &Self) -> Option { + self.timestamp.partial_cmp(&other.timestamp) + } +} + +impl Ord for SentTextMessage { + /// Orders InboundMessage from least to most time remaining from being scheduled + fn cmp(&self, other: &Self) -> Ordering { + self.timestamp.cmp(&other.timestamp) + } +} + /// A message service contact #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub struct Contact { @@ -396,14 +440,16 @@ impl Contact { } } - pub fn commit(&self, conn: &SqliteConnection) -> Result<(), TextMessageError> { + pub fn commit(&self, conn: &PooledConnection>) -> Result<(), TextMessageError> { diesel::insert_into(contacts::table) .values(ContactSql::from(self.clone())) .execute(conn)?; Ok(()) } - pub fn index(conn: &SqliteConnection) -> Result, TextMessageError> { + pub fn index( + conn: &PooledConnection>, + ) -> Result, TextMessageError> { let contacts = contacts::table.load::(conn)?; let mut result: Vec = Vec::new(); @@ -414,7 +460,11 @@ impl Contact { Ok(result) } - pub fn find(pub_key: &CommsPublicKey, conn: &SqliteConnection) -> Result { + pub fn find( + pub_key: &CommsPublicKey, + conn: &PooledConnection>, + ) -> Result + { Ok(Contact::try_from( contacts::table .filter(contacts::pub_key.eq(pub_key.to_hex())) @@ -422,7 +472,12 @@ impl Contact { )?) } - pub fn update(self, updated_contact: UpdateContact, conn: &SqliteConnection) -> Result { + pub fn update( + &self, + updated_contact: UpdateContact, + conn: &PooledConnection>, + ) -> Result + { let num_updated = diesel::update(contacts::table.filter(contacts::pub_key.eq(&self.pub_key.to_hex()))) .set(UpdateContactSql::from(updated_contact)) .execute(conn)?; @@ -434,7 +489,7 @@ impl Contact { Ok(Contact::find(&self.pub_key, conn)?) } - pub fn delete(self, conn: &SqliteConnection) -> Result<(), TextMessageError> { + pub fn delete(&self, conn: &PooledConnection>) -> Result<(), TextMessageError> { let num_deleted = diesel::delete(contacts::table.filter(contacts::pub_key.eq(&self.pub_key.to_hex()))).execute(conn)?; if num_deleted == 0 { @@ -509,7 +564,7 @@ impl TextMessageSettings { TextMessageSettings { screen_name, pub_key } } - pub fn commit(&self, conn: &SqliteConnection) -> Result<(), TextMessageError> { + pub fn commit(&self, conn: &PooledConnection>) -> Result<(), TextMessageError> { conn.transaction::<_, DieselError, _>(|| { // There should only be one row in this table (until we support revisions) so first clean out the table diesel::delete(settings::table).execute(conn)?; @@ -525,7 +580,9 @@ impl TextMessageSettings { Ok(()) } - pub fn read(conn: &SqliteConnection) -> Result { + pub fn read( + conn: &PooledConnection>, + ) -> Result { let read_settings = settings::table.load::(conn)?; let mut result: Vec = Vec::new(); @@ -564,12 +621,7 @@ impl TryFrom for TextMessageSettings { #[cfg(test)] mod test { - use crate::text_message_service::{ - model::{SentTextMessage, TextMessageSettings}, - Contact, - ReceivedTextMessage, - UpdateContact, - }; + use super::*; use chrono::Utc; use diesel::{Connection, SqliteConnection}; use std::path::PathBuf; @@ -597,11 +649,10 @@ mod test { #[test] fn db_model_tests() { - let mut rng = rand::OsRng::new().unwrap(); - let (_secret_key1, public_key1) = CommsPublicKey::random_keypair(&mut rng); - let (_secret_key2, public_key2) = CommsPublicKey::random_keypair(&mut rng); - let (_secret_key3, public_key3) = CommsPublicKey::random_keypair(&mut rng); - let (_secret_key4, public_key4) = CommsPublicKey::random_keypair(&mut rng); + let (_secret_key1, public_key1) = CommsPublicKey::random_keypair(&mut OsRng); + let (_secret_key2, public_key2) = CommsPublicKey::random_keypair(&mut OsRng); + let (_secret_key3, public_key3) = CommsPublicKey::random_keypair(&mut OsRng); + let (_secret_key4, public_key4) = CommsPublicKey::random_keypair(&mut OsRng); let db_name = "test.sqlite3"; let db_path = get_path(Some(db_name)); @@ -609,10 +660,14 @@ mod test { embed_migrations!("./migrations"); let conn = SqliteConnection::establish(&db_path).unwrap_or_else(|_| panic!("Error connecting to {}", db_path)); - conn.execute("PRAGMA foreign_keys = ON").unwrap(); embedded_migrations::run_with_output(&conn, &mut std::io::stdout()).expect("Migration failed"); + let manager = ConnectionManager::::new(db_path); + let pool = diesel::r2d2::Pool::builder().max_size(1).build(manager).unwrap(); + + let conn = pool.get().unwrap(); + conn.execute("PRAGMA foreign_keys = ON").unwrap(); let _settings1 = TextMessageSettings::new("Bob".to_string(), public_key1.clone()).commit(&conn); let read_settings1 = TextMessageSettings::read(&conn).unwrap(); assert_eq!(read_settings1.screen_name, "Bob".to_string()); @@ -688,11 +743,16 @@ mod test { let count = SentTextMessage::count_by_dest_pub_key(&public_key3.clone(), &conn).unwrap(); assert_eq!(count, 2); - assert!(SentTextMessage::mark_sent_message_ack(vec![2u8; 32], &conn).is_err()); - SentTextMessage::mark_sent_message_ack(sent_msg1.clone().id, &conn).unwrap(); + assert!(SentTextMessage::mark_sent_message_ack(&vec![2u8; 32], &conn).is_err()); + SentTextMessage::mark_sent_message_ack(&sent_msg1.id.clone(), &conn).unwrap(); let find3 = SentTextMessage::find(&sent_msg1.id, &conn).unwrap(); assert!(find3.acknowledged); + assert!(SentTextMessage::mark_sent_message_opened(vec![2u8; 32], &conn).is_err()); + SentTextMessage::mark_sent_message_opened(sent_msg1.id.clone(), &conn).unwrap(); + let find4 = SentTextMessage::find(&sent_msg1.id, &conn).unwrap(); + assert!(find4.acknowledged); + let recv_msg1 = ReceivedTextMessage { id: vec![1u8; 32], source_pub_key: public_key1.clone(), diff --git a/base_layer/wallet/src/text_message_service/service.rs b/base_layer/wallet/src/text_message_service/service.rs index 48b5774f8b..fbd7b5919f 100644 --- a/base_layer/wallet/src/text_message_service/service.rs +++ b/base_layer/wallet/src/text_message_service/service.rs @@ -20,111 +20,232 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::text_message_service::{ +use super::{ error::TextMessageError, - model::{ReceivedTextMessage, SentTextMessage}, - Contact, - UpdateContact, + handle::{TextMessageRequest, TextMessageResponse}, + model::{Contact, ReceivedTextMessage, SentTextMessage, UpdateContact}, }; -use crossbeam_channel as channel; -use diesel::{connection::Connection, SqliteConnection}; + +use crate::text_message_service::handle::TextMessageEvent; +use diesel::{ + r2d2::{ConnectionManager, Pool}, + Connection, + SqliteConnection, +}; +use futures::{future::poll_fn, pin_mut, SinkExt, Stream, StreamExt}; use log::*; use serde::{Deserialize, Serialize}; -use std::{ - convert::TryInto, - sync::{Arc, Mutex}, - time::Duration, -}; -use tari_comms::{ - domain_subscriber::{MessageInfo, SyncDomainSubscription}, - message::{Message, MessageError, MessageFlags}, - outbound_message_service::{outbound_message_service::OutboundMessageService, BroadcastStrategy}, - types::CommsPublicKey, +use std::{io, path::Path, time::Duration}; +use tari_broadcast_channel::Publisher; +use tari_comms::types::CommsPublicKey; +use tari_comms_dht::{ + envelope::NodeDestination, + outbound::{BroadcastStrategy, OutboundEncryption, OutboundMessageRequester}, }; use tari_p2p::{ - ping_pong::PingPong, - sync_services::{ - Service, - ServiceApiWrapper, - ServiceContext, - ServiceControlMessage, - ServiceError, - DEFAULT_API_TIMEOUT_MS, - }, + domain_message::DomainMessage, + services::liveness::handle::LivenessHandle, tari_message::{ExtendedMessage, TariMessageType}, }; +use tari_service_framework::reply_channel; +use tokio_executor::threadpool::blocking; -const LOG_TARGET: &'static str = "base_layer::wallet::text_messsage_service"; +const LOG_TARGET: &str = "base_layer::wallet::text_messsage_service"; /// Represents an Acknowledgement of receiving a Text Message -#[derive(Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct TextMessageAck { id: Vec, } -impl TryInto for TextMessageAck { - type Error = MessageError; - - fn try_into(self) -> Result { - Ok((TariMessageType::new(ExtendedMessage::TextAck), self).try_into()?) - } +/// A collection to hold a text message state +#[derive(Debug)] +pub struct TextMessages { + pub received_messages: Vec, + pub sent_messages: Vec, } /// The TextMessageService manages the local node's text messages. It keeps track of sent messages that require an Ack /// (pending messages), Ack'ed sent messages and received messages. -pub struct TextMessageService { +pub struct TextMessageService { pub_key: CommsPublicKey, screen_name: Option, - oms: Option>, - api: ServiceApiWrapper, - database_path: String, + oms: OutboundMessageRequester, + database_connection_pool: Pool>, + request_stream: Option>>, + text_message_stream: Option, + text_message_ack_stream: Option, + liveness: LivenessHandle, + event_publisher: Publisher, } -impl TextMessageService { - pub fn new(pub_key: CommsPublicKey, database_path: String) -> TextMessageService { - TextMessageService { - pub_key, +impl TextMessageService +where + TTextStream: Stream>, + TAckStream: Stream>, +{ + pub fn new( + request_stream: reply_channel::Receiver>, + text_message_stream: TTextStream, + text_message_ack_stream: TAckStream, + pub_key: CommsPublicKey, + database_path: String, + oms: OutboundMessageRequester, + liveness: LivenessHandle, + event_publisher: Publisher, + ) -> Self + { + let pool = Self::establish_db_connection_pool(database_path).expect("Could not establish database connection"); + + Self { + request_stream: Some(request_stream), + text_message_stream: Some(text_message_stream), + text_message_ack_stream: Some(text_message_ack_stream), + pub_key: pub_key.clone(), screen_name: None, - oms: None, - api: Self::setup_api(), - database_path, + oms, + database_connection_pool: pool, + liveness, + event_publisher, } } - /// Return this service's API - pub fn get_api(&self) -> Arc { - self.api.get_api() + pub async fn start(mut self) -> Result<(), TextMessageError> { + let request_stream = self + .request_stream + .take() + .expect("TextMessageService initialized without request_stream") + .fuse(); + pin_mut!(request_stream); + let text_message_stream = self + .text_message_stream + .take() + .expect("TextMessageService initialized without text_message_stream") + .fuse(); + pin_mut!(text_message_stream); + let text_message_ack_stream = self + .text_message_ack_stream + .take() + .expect("TextMessageService initialized without text_message_ack_stream") + .fuse(); + pin_mut!(text_message_ack_stream); + loop { + futures::select! { + request_context = request_stream.select_next_some() => { + let (request, reply_tx) = request_context.split(); + let _ = reply_tx.send(self.handle_request(request).await).or_else(|resp| { + error!(target: LOG_TARGET, "Failed to send reply"); + Err(resp) + }); + }, + // Incoming messages from the Comms layer + msg = text_message_stream.select_next_some() => { + let _ = self.handle_incoming_text_message(msg).await.or_else(|err| { + error!(target: LOG_TARGET, "Failed to handle incoming message: {:?}", err); + Err(err) + }); + }, + // Incoming messages from the Comms layer + msg = text_message_ack_stream.select_next_some() => { + let _ = self.handle_incoming_ack_message(msg).await.or_else(|err| { + error!(target: LOG_TARGET, "Failed to handle incoming message: {:?}", err); + Err(err) + }); + }, + complete => { + info!(target: LOG_TARGET, "Text message service shutting down"); + break; + } + } + } + Ok(()) } - fn setup_api() -> ServiceApiWrapper { - let (api_sender, service_receiver) = channel::bounded(0); - let (service_sender, api_receiver) = channel::bounded(0); + pub fn establish_db_connection_pool( + database_path: String, + ) -> Result>, TextMessageError> { + let db_exists = Path::new(&database_path).exists(); + + let connection = SqliteConnection::establish(&database_path)?; - let api = Arc::new(TextMessageServiceApi::new(api_sender, api_receiver)); - ServiceApiWrapper::new(service_receiver, service_sender, api) + connection.execute("PRAGMA foreign_keys = ON")?; + if !db_exists { + embed_migrations!("./migrations"); + embedded_migrations::run_with_output(&connection, &mut io::stdout()).map_err(|err| { + TextMessageError::DatabaseMigrationError(format!("Database migration failed {}", err)) + })?; + } + drop(connection); + + let manager = ConnectionManager::::new(database_path); + let pool = diesel::r2d2::Pool::builder() + .connection_timeout(Duration::from_millis(2000)) + .idle_timeout(Some(Duration::from_millis(2000))) + .max_size(1) + .build(manager) + .map_err(|_| TextMessageError::R2d2Error)?; + Ok(pool) + } + + async fn handle_request(&mut self, request: TextMessageRequest) -> Result { + match request { + TextMessageRequest::SendTextMessage((destination, message)) => self + .send_text_message(destination, message) + .await + .map(|_| TextMessageResponse::MessageSent), + TextMessageRequest::GetTextMessages => self + .get_current_messages() + .await + .map(|tm| TextMessageResponse::TextMessages(tm)), + TextMessageRequest::GetTextMessagesByPubKey(pk) => self + .get_current_messages_by_pub_key(pk) + .await + .map(|tm| TextMessageResponse::TextMessages(tm)), + TextMessageRequest::GetScreenName => Ok(TextMessageResponse::ScreenName(self.get_screen_name())), + TextMessageRequest::SetScreenName(s) => { + self.set_screen_name(s); + Ok(TextMessageResponse::ScreenNameSet) + }, + TextMessageRequest::AddContact(c) => self.add_contact(c).await.map(|_| TextMessageResponse::ContactAdded), + TextMessageRequest::RemoveContact(c) => self + .remove_contact(c) + .await + .map(|_| TextMessageResponse::ContactRemoved), + TextMessageRequest::GetContacts => self.get_contacts().await.map(|c| TextMessageResponse::Contacts(c)), + TextMessageRequest::UpdateContact((pk, c)) => self + .update_contact(pk, c) + .await + .map(|_| TextMessageResponse::ContactUpdated), + } } /// Send a text message to the specified node using the provided OMS - fn send_text_message( + async fn send_text_message( &mut self, dest_pub_key: CommsPublicKey, message: String, - conn: &SqliteConnection, ) -> Result<(), TextMessageError> { - let oms = self.oms.clone().ok_or(TextMessageError::OMSNotInitialized)?; - - let count = SentTextMessage::count_by_dest_pub_key(&dest_pub_key.clone(), conn)?; + let conn = self + .database_connection_pool + .clone() + .get() + .map_err(|_| TextMessageError::R2d2Error)?; + let count = SentTextMessage::count_by_dest_pub_key(&dest_pub_key.clone(), &conn)?; let text_message = SentTextMessage::new(self.pub_key.clone(), dest_pub_key, message, Some(count as usize)); - oms.send_message( - BroadcastStrategy::DirectPublicKey(text_message.dest_pub_key.clone()), - MessageFlags::ENCRYPTED, - text_message.clone(), - )?; + self.oms + .send_message( + BroadcastStrategy::DirectPublicKey(text_message.dest_pub_key.clone()), + NodeDestination::Undisclosed, + OutboundEncryption::EncryptForDestination, + TariMessageType::new(ExtendedMessage::Text), + text_message.clone(), + ) + .await?; - text_message.commit(conn)?; + text_message.commit(&conn)?; trace!(target: LOG_TARGET, "Text Message Sent to {}", text_message.dest_pub_key); @@ -132,78 +253,127 @@ impl TextMessageService { } /// Process an incoming text message - fn receive_text_message( + async fn handle_incoming_text_message( &mut self, - info: MessageInfo, - message: ReceivedTextMessage, - conn: &SqliteConnection, + message: DomainMessage, ) -> Result<(), TextMessageError> { - let oms = self.oms.clone().ok_or(TextMessageError::OMSNotInitialized)?; - trace!( target: LOG_TARGET, "Text Message received with ID: {:?} from {} with message: {:?}", - message.id.clone(), - message.source_pub_key, - message.message.clone() + message.inner.id, + message.inner.source_pub_key, + message.inner.message, ); - let text_message_ack = TextMessageAck { id: message.clone().id }; - oms.send_message( - BroadcastStrategy::DirectPublicKey(info.origin_source), - MessageFlags::ENCRYPTED, - text_message_ack, - )?; - - message.commit(conn)?; + let text_message_ack = TextMessageAck { + id: message.inner.id.clone(), + }; + self.oms + .send_message( + BroadcastStrategy::DirectPublicKey(message.clone().origin_pubkey), + NodeDestination::Undisclosed, + OutboundEncryption::EncryptForDestination, + TariMessageType::new(ExtendedMessage::TextAck), + text_message_ack, + ) + .await?; + let conn = self + .database_connection_pool + .clone() + .get() + .map_err(|_| TextMessageError::R2d2Error)?; + + let message_inner = message.clone().into_inner(); + poll_fn(move |_| blocking(|| message_inner.commit(&conn))).await??; + + self.event_publisher + .send(TextMessageEvent::ReceivedTextMessage) + .await + .map_err(|_| TextMessageError::EventStreamError)?; Ok(()) } /// Process an incoming text message Ack - fn receive_text_message_ack( + async fn handle_incoming_ack_message( &mut self, - message_ack: TextMessageAck, - conn: &SqliteConnection, + message_ack: DomainMessage, ) -> Result<(), TextMessageError> { + let message_ack_inner = message_ack.clone().into_inner(); + + let conn = self + .database_connection_pool + .clone() + .get() + .map_err(|_| TextMessageError::R2d2Error)?; + debug!( target: LOG_TARGET, - "Text Message Ack received with ID: {:?}", - message_ack.id.clone(), + "Text Message Ack received with ID: {:?}", message_ack_inner.id, ); - SentTextMessage::mark_sent_message_ack(message_ack.id.clone(), conn)?; + poll_fn(move |_| blocking(|| SentTextMessage::mark_sent_message_ack(&message_ack_inner.id, &conn))).await??; + self.event_publisher + .send(TextMessageEvent::ReceivedTextMessageAck) + .await + .map_err(|_| TextMessageError::EventStreamError)?; Ok(()) } /// Return a copy of the current lists of messages - fn get_current_messages(&self, conn: &SqliteConnection) -> Result { - Ok(TextMessages { - sent_messages: SentTextMessage::index(conn)?, - received_messages: ReceivedTextMessage::index(conn)?, - }) - } + async fn get_current_messages(&mut self) -> Result { + let sent_messages; + let received_messages; - fn get_current_messages_by_pub_key( - &self, - pub_key: CommsPublicKey, - conn: &SqliteConnection, - ) -> Result - { + let conn1 = self + .database_connection_pool + .clone() + .get() + .map_err(|_| TextMessageError::R2d2Error)?; + + sent_messages = poll_fn(move |_| blocking(|| SentTextMessage::index(&conn1))).await??; + + let conn2 = self + .database_connection_pool + .clone() + .get() + .map_err(|_| TextMessageError::R2d2Error)?; + + received_messages = poll_fn(move |_| blocking(|| ReceivedTextMessage::index(&conn2))).await??; Ok(TextMessages { - sent_messages: SentTextMessage::find_by_dest_pub_key(&pub_key, conn)?, - received_messages: ReceivedTextMessage::find_by_source_pub_key(&pub_key, conn)?, + sent_messages, + received_messages, }) } - pub fn get_pub_key(&self) -> CommsPublicKey { - self.pub_key.clone() - } + async fn get_current_messages_by_pub_key(&self, pub_key: CommsPublicKey) -> Result { + let sent_messages; + let received_messages; + + let conn1 = self + .database_connection_pool + .clone() + .get() + .map_err(|_| TextMessageError::R2d2Error)?; + let pub_key1 = pub_key.clone(); + sent_messages = + poll_fn(move |_| blocking(|| SentTextMessage::find_by_dest_pub_key(&pub_key1, &conn1))).await??; + + let conn2 = self + .database_connection_pool + .clone() + .get() + .map_err(|_| TextMessageError::R2d2Error)?; + let pub_key2 = pub_key.clone(); + received_messages = + poll_fn(move |_| blocking(|| ReceivedTextMessage::find_by_source_pub_key(&pub_key2, &conn2))).await??; - pub fn set_pub_key(&mut self, pub_key: CommsPublicKey) { - self.pub_key = pub_key; + Ok(TextMessages { + sent_messages, + received_messages, + }) } pub fn get_screen_name(&self) -> Option { @@ -214,451 +384,105 @@ impl TextMessageService { self.screen_name = Some(screen_name); } - pub fn add_contact(&mut self, contact: Contact, conn: &SqliteConnection) -> Result<(), TextMessageError> { - let found_contact = Contact::find(&contact.pub_key, conn); + pub async fn add_contact(&mut self, contact: Contact) -> Result<(), TextMessageError> { + let conn1 = self + .database_connection_pool + .clone() + .get() + .map_err(|_| TextMessageError::R2d2Error)?; + let contact_clone = contact.clone(); + let found_contact = poll_fn(move |_| blocking(|| Contact::find(&contact_clone.pub_key, &conn1))).await?; if let Ok(c) = found_contact { if c.pub_key == contact.pub_key { return Err(TextMessageError::ContactAlreadyExists); } } - contact.commit(&conn)?; - - // Send ping to the contact so that if they are online they will flush all outstanding messages for this node - let oms = self.oms.clone().ok_or(TextMessageError::OMSNotInitialized)?; - oms.send_message( - BroadcastStrategy::DirectPublicKey(contact.pub_key.clone()), - MessageFlags::empty(), - PingPong::Ping, - )?; + let conn2 = self + .database_connection_pool + .clone() + .get() + .map_err(|_| TextMessageError::R2d2Error)?; + let contact_clone2 = contact.clone(); + poll_fn(move |_| blocking(|| contact_clone2.commit(&conn2))).await??; trace!( target: LOG_TARGET, "Contact Added: Screen name: {:?} - Pub-key: {} - Address: {:?}", - contact.screen_name.clone(), - contact.pub_key.clone(), - contact.address.clone() + contact.screen_name, + contact.pub_key, + contact.address, ); + + // Send ping to the contact so that if they are online they will flush all outstanding messages for this node + self.liveness.send_ping(contact.pub_key).await?; + Ok(()) } - pub fn remove_contact(&mut self, contact: Contact, conn: &SqliteConnection) -> Result<(), TextMessageError> { - contact.clone().delete(conn)?; + pub async fn remove_contact(&mut self, contact: Contact) -> Result<(), TextMessageError> { + let conn = self + .database_connection_pool + .clone() + .get() + .map_err(|_| TextMessageError::R2d2Error)?; + let contact_clone = contact.clone(); + poll_fn(move |_| blocking(|| contact_clone.delete(&conn))).await??; trace!( target: LOG_TARGET, "Contact Deleted: Screen name: {:?} - Pub-key: {} - Address: {:?}", - contact.screen_name.clone(), - contact.pub_key.clone(), - contact.address.clone() + contact.screen_name, + contact.pub_key, + contact.address, ); Ok(()) } - pub fn get_contacts(&self, conn: &SqliteConnection) -> Result, TextMessageError> { - Contact::index(conn) + pub async fn get_contacts(&self) -> Result, TextMessageError> { + let conn = self + .database_connection_pool + .clone() + .get() + .map_err(|_| TextMessageError::R2d2Error)?; + poll_fn(move |_| blocking(|| Contact::index(&conn))).await? } /// Updates the screen_name of a contact if an existing contact with the same pub_key is found - pub fn update_contact( + pub async fn update_contact( &mut self, pub_key: CommsPublicKey, contact_update: UpdateContact, - conn: &SqliteConnection, ) -> Result<(), TextMessageError> { - let contact = Contact::find(&pub_key, conn)?; - - contact.clone().update(contact_update, conn)?; + let conn1 = self + .database_connection_pool + .clone() + .get() + .map_err(|_| TextMessageError::R2d2Error)?; + let pub_key1 = pub_key.clone(); + let contact = poll_fn(move |_| blocking(|| Contact::find(&pub_key1, &conn1))).await??; + + let conn2 = self + .database_connection_pool + .clone() + .get() + .map_err(|_| TextMessageError::R2d2Error)?; + let contact_update = contact_update.clone(); + let contact_clone = contact.clone(); + poll_fn(move |_| blocking(|| contact_clone.update(contact_update.clone(), &conn2))).await??; trace!( target: LOG_TARGET, "Contact Updated: Screen name: {:?} - Pub-key: {} - Address: {:?}", - contact.screen_name.clone(), - contact.pub_key.clone(), - contact.address.clone() + contact.screen_name, + contact.pub_key, + contact.address, ); Ok(()) } - /// This handler is called when the Service executor loops receives an API request - fn handle_api_message( - &mut self, - msg: TextMessageApiRequest, - connection: &SqliteConnection, - ) -> Result<(), ServiceError> - { - trace!(target: LOG_TARGET, "[{}] Received API message", self.get_name(),); - let resp = match msg { - TextMessageApiRequest::SendTextMessage((destination, message)) => self - .send_text_message(destination, message, connection) - .map(|_| TextMessageApiResponse::MessageSent), - TextMessageApiRequest::GetTextMessages => self - .get_current_messages(connection) - .map(|tm| TextMessageApiResponse::TextMessages(tm)), - TextMessageApiRequest::GetTextMessagesByPubKey(pk) => self - .get_current_messages_by_pub_key(pk, connection) - .map(|tm| TextMessageApiResponse::TextMessages(tm)), - TextMessageApiRequest::GetScreenName => Ok(TextMessageApiResponse::ScreenName(self.get_screen_name())), - TextMessageApiRequest::SetScreenName(s) => { - self.set_screen_name(s); - Ok(TextMessageApiResponse::ScreenNameSet) - }, - TextMessageApiRequest::AddContact(c) => self - .add_contact(c, connection) - .map(|_| TextMessageApiResponse::ContactAdded), - TextMessageApiRequest::RemoveContact(c) => self - .remove_contact(c, connection) - .map(|_| TextMessageApiResponse::ContactRemoved), - TextMessageApiRequest::GetContacts => self - .get_contacts(connection) - .map(|c| TextMessageApiResponse::Contacts(c)), - TextMessageApiRequest::UpdateContact((pk, c)) => self - .update_contact(pk, c, connection) - .map(|_| TextMessageApiResponse::ContactUpdated), - }; - - trace!(target: LOG_TARGET, "[{}] Replying to API", self.get_name()); - self.api - .send_reply(resp) - .map_err(ServiceError::internal_service_error()) - } - // TODO Some sort of accessor that allows for pagination of messages } - -/// A collection to hold a text message state -#[derive(Debug)] -pub struct TextMessages { - pub received_messages: Vec, - pub sent_messages: Vec, -} - -/// The Domain Service trait implementation for the TestMessageService -impl Service for TextMessageService { - fn get_name(&self) -> String { - "Text Message service".to_string() - } - - fn get_message_types(&self) -> Vec { - vec![ExtendedMessage::Text.into(), ExtendedMessage::TextAck.into()] - } - - /// Function called by the Service Executor in its own thread. This function polls for both API request and Comms - /// layer messages from the Message Broker - fn execute(&mut self, context: ServiceContext) -> Result<(), ServiceError> { - let mut subscription_text = SyncDomainSubscription::new( - context - .inbound_message_subscription_factory() - .get_subscription_fused(ExtendedMessage::Text.into()), - ); - let mut subscription_text_ack = SyncDomainSubscription::new( - context - .inbound_message_subscription_factory() - .get_subscription_fused(ExtendedMessage::TextAck.into()), - ); - - self.oms = Some(context.outbound_message_service()); - - // Check if the database file exists - let mut exists = false; - if std::fs::metadata(self.database_path.clone()).is_ok() { - exists = true; - } - - let connection = SqliteConnection::establish(&self.database_path) - .map_err(|e| ServiceError::ServiceInitializationFailed(format!("{}", e).to_string()))?; - - connection - .execute("PRAGMA foreign_keys = ON") - .map_err(|e| ServiceError::ServiceInitializationFailed(format!("{}", e).to_string()))?; - - if !exists { - embed_migrations!("./migrations"); - embedded_migrations::run_with_output(&connection, &mut std::io::stdout()).map_err(|e| { - ServiceError::ServiceInitializationFailed(format!("Database migration failed {}", e).to_string()) - })?; - } - - debug!(target: LOG_TARGET, "Starting Text Message Service executor"); - loop { - if let Some(msg) = context.get_control_message(Duration::from_millis(5)) { - match msg { - ServiceControlMessage::Shutdown => break, - } - } - for m in subscription_text.receive_messages()?.drain(..) { - match self.receive_text_message(m.0, m.1, &connection) { - Ok(_) => {}, - Err(err) => { - error!(target: LOG_TARGET, "Text Message service had error: {:?}", err); - }, - } - } - for m in subscription_text_ack.receive_messages()?.drain(..) { - match self.receive_text_message_ack(m.1, &connection) { - Ok(_) => {}, - Err(err) => { - error!(target: LOG_TARGET, "Text Message service had error: {:?}", err); - }, - } - } - if let Some(msg) = self - .api - .recv_timeout(Duration::from_millis(50)) - .map_err(ServiceError::internal_service_error())? - { - self.handle_api_message(msg, &connection)?; - } - } - - Ok(()) - } -} - -/// API Request enum -#[derive(Debug)] -pub enum TextMessageApiRequest { - SendTextMessage((CommsPublicKey, String)), - GetTextMessages, - GetTextMessagesByPubKey(CommsPublicKey), - SetScreenName(String), - GetScreenName, - AddContact(Contact), - RemoveContact(Contact), - GetContacts, - UpdateContact((CommsPublicKey, UpdateContact)), -} - -/// API Response enum -#[derive(Debug)] -pub enum TextMessageApiResponse { - MessageSent, - TextMessages(TextMessages), - ScreenName(Option), - ScreenNameSet, - ContactAdded, - ContactRemoved, - Contacts(Vec), - ContactUpdated, -} - -/// Result for all API requests -pub type TextMessageApiResult = Result; - -/// The TextMessage service public API that other services and application will use to interact with this service. -/// The requests and responses are transmitted via channels into the Service Executor thread where this service is -/// running -pub struct TextMessageServiceApi { - sender: channel::Sender, - receiver: channel::Receiver, - mutex: Mutex<()>, - timeout: Duration, -} - -impl TextMessageServiceApi { - fn new(sender: channel::Sender, receiver: channel::Receiver) -> Self { - Self { - sender, - receiver, - mutex: Mutex::new(()), - timeout: Duration::from_millis(DEFAULT_API_TIMEOUT_MS), - } - } - - pub fn send_text_message(&self, destination: CommsPublicKey, message: String) -> Result<(), TextMessageError> { - self.send_recv(TextMessageApiRequest::SendTextMessage((destination, message))) - .and_then(|resp| match resp { - TextMessageApiResponse::MessageSent => Ok(()), - _ => Err(TextMessageError::UnexpectedApiResponse), - }) - } - - pub fn get_text_messages(&self) -> Result { - self.send_recv(TextMessageApiRequest::GetTextMessages) - .and_then(|resp| match resp { - TextMessageApiResponse::TextMessages(msgs) => Ok(msgs), - _ => Err(TextMessageError::UnexpectedApiResponse), - }) - } - - pub fn get_text_messages_by_pub_key(&self, pub_key: CommsPublicKey) -> Result { - self.send_recv(TextMessageApiRequest::GetTextMessagesByPubKey(pub_key)) - .and_then(|resp| match resp { - TextMessageApiResponse::TextMessages(msgs) => Ok(msgs), - _ => Err(TextMessageError::UnexpectedApiResponse), - }) - } - - pub fn get_screen_name(&self) -> Result, TextMessageError> { - self.send_recv(TextMessageApiRequest::GetScreenName) - .and_then(|resp| match resp { - TextMessageApiResponse::ScreenName(s) => Ok(s), - _ => Err(TextMessageError::UnexpectedApiResponse), - }) - } - - pub fn set_screen_name(&self, screen_name: String) -> Result<(), TextMessageError> { - self.send_recv(TextMessageApiRequest::SetScreenName(screen_name)) - .and_then(|resp| match resp { - TextMessageApiResponse::ScreenNameSet => Ok(()), - _ => Err(TextMessageError::UnexpectedApiResponse), - }) - } - - pub fn add_contact(&self, contact: Contact) -> Result<(), TextMessageError> { - self.send_recv(TextMessageApiRequest::AddContact(contact)) - .and_then(|resp| match resp { - TextMessageApiResponse::ContactAdded => Ok(()), - _ => Err(TextMessageError::UnexpectedApiResponse), - }) - } - - pub fn remove_contact(&self, contact: Contact) -> Result<(), TextMessageError> { - self.send_recv(TextMessageApiRequest::RemoveContact(contact)) - .and_then(|resp| match resp { - TextMessageApiResponse::ContactRemoved => Ok(()), - _ => Err(TextMessageError::UnexpectedApiResponse), - }) - } - - pub fn get_contacts(&self) -> Result, TextMessageError> { - self.send_recv(TextMessageApiRequest::GetContacts) - .and_then(|resp| match resp { - TextMessageApiResponse::Contacts(v) => Ok(v), - _ => Err(TextMessageError::UnexpectedApiResponse), - }) - } - - pub fn update_contact(&self, pub_key: CommsPublicKey, contact: UpdateContact) -> Result<(), TextMessageError> { - self.send_recv(TextMessageApiRequest::UpdateContact((pub_key, contact))) - .and_then(|resp| match resp { - TextMessageApiResponse::ContactUpdated => Ok(()), - _ => Err(TextMessageError::UnexpectedApiResponse), - }) - } - - fn send_recv(&self, msg: TextMessageApiRequest) -> TextMessageApiResult { - self.lock(|| -> TextMessageApiResult { - self.sender.send(msg).map_err(|_| TextMessageError::ApiSendFailed)?; - self.receiver - .recv_timeout(self.timeout.clone()) - .map_err(|_| TextMessageError::ApiReceiveFailed)? - }) - } - - fn lock(&self, func: F) -> T - where F: FnOnce() -> T { - let lock = acquire_lock!(self.mutex); - let res = func(); - drop(lock); - res - } -} - -#[cfg(test)] -mod test { - use crate::{ - diesel::Connection, - text_message_service::{error::TextMessageError, Contact, TextMessageService, UpdateContact}, - }; - use diesel::{result::Error as DieselError, SqliteConnection}; - use std::path::PathBuf; - use tari_comms::types::CommsPublicKey; - use tari_crypto::keys::PublicKey; - - fn get_path(name: Option<&str>) -> String { - let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - path.push("tests/data"); - path.push(name.unwrap_or("")); - path.to_str().unwrap().to_string() - } - - fn clean_up(name: &str) { - if std::fs::metadata(get_path(Some(name))).is_ok() { - std::fs::remove_file(get_path(Some(name))).unwrap(); - } - } - - fn init(name: &str) { - clean_up(name); - let path = get_path(None); - let _ = std::fs::create_dir(&path).unwrap_or_default(); - } - - #[test] - fn test_contacts_crud() { - let mut rng = rand::OsRng::new().unwrap(); - - let (_secret_key, public_key) = CommsPublicKey::random_keypair(&mut rng); - - let db_name = "test_crud.sqlite3"; - let db_path = get_path(Some(db_name)); - init(db_name); - - let conn = SqliteConnection::establish(&db_path).unwrap(); - embed_migrations!("./migrations"); - embedded_migrations::run_with_output(&conn, &mut std::io::stdout()).expect("Migration failed"); - - let mut tms = TextMessageService::new(public_key, db_path); - - let mut contacts = Vec::new(); - - let screen_names = vec![ - "Alice".to_string(), - "Bob".to_string(), - "Carol".to_string(), - "Dave".to_string(), - "Eric".to_string(), - ]; - for i in 0..5 { - let (_contact_secret_key, contact_public_key) = CommsPublicKey::random_keypair(&mut rng); - contacts.push(Contact::new( - screen_names[i].clone(), - contact_public_key, - "127.0.0.1:12345".parse().unwrap(), - )); - } - - assert_eq!(tms.get_screen_name(), None); - tms.set_screen_name("Fred".to_string()); - assert_eq!(tms.get_screen_name(), Some("Fred".to_string())); - - for c in contacts.iter() { - let _ = tms.add_contact(c.clone(), &conn); - } - - assert_eq!(tms.get_contacts(&conn).unwrap().len(), 5); - - tms.remove_contact(contacts[0].clone(), &conn).unwrap(); - - assert_eq!(tms.get_contacts(&conn).unwrap().len(), 4); - - let update_contact = UpdateContact { - screen_name: Some("Betty".to_string()), - address: Some(contacts[1].address.clone()), - }; - - tms.update_contact(contacts[1].pub_key.clone(), update_contact, &conn) - .unwrap(); - - let updated_contacts = tms.get_contacts(&conn).unwrap(); - assert_eq!(updated_contacts[0].screen_name, "Betty".to_string()); - - match tms.update_contact( - CommsPublicKey::default(), - UpdateContact { - screen_name: Some("Whatever".to_string()), - address: Some("127.0.0.1:12345".parse().unwrap()), - }, - &conn, - ) { - Err(TextMessageError::DatabaseError(DieselError::NotFound)) => assert!(true), - _ => assert!(false), - } - - clean_up(db_name); - } -} diff --git a/base_layer/wallet/src/transaction_manager.rs b/base_layer/wallet/src/transaction_manager.rs deleted file mode 100644 index 9e7423e076..0000000000 --- a/base_layer/wallet/src/transaction_manager.rs +++ /dev/null @@ -1,611 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE - -use derive_error::Error; -use std::collections::HashMap; -use tari_core::{ - transaction::{KernelFeatures, OutputFeatures, Transaction}, - transaction_protocol::{ - recipient::RecipientSignedTransactionData, - sender::SenderMessage, - TransactionProtocolError, - }, - types::{CommitmentFactory, PrivateKey, RangeProofService}, - ReceiverTransactionProtocol, - SenderTransactionProtocol, -}; - -#[derive(Debug, Error, PartialEq)] -pub enum TransactionManagerError { - // Transaction protocol is not in the correct state for this operation - InvalidStateError, - // Transaction Protocol Error - TransactionProtocolError(TransactionProtocolError), - // The message being process is not recognized by the Transaction Manager - InvalidMessageTypeError, - // A message for a specific tx_id has been repeated - RepeatedMessageError, - // A recipient reply was received for a non-existent tx_id - TransactionDoesNotExistError, -} - -/// TransactionManager allows for the management of multiple inbound and outbound transaction protocols -/// which are uniquely identified by a tx_id. The TransactionManager generates and accepts the various protocol -/// messages and applies them to the appropriate protocol instances based on the tx_id. -/// The TransactionManager allows for the sending of transactions to single receivers, when the appropriate recipient -/// response is handled the transaction is completed and moved to the completed_transaction buffer. -/// The TransactionManager will accept inbound transactions and generate a reply. Received transactions will remain -/// in the pending_inbound_transactions buffer. -/// TODO Allow for inbound transactions that are detected on the blockchain to be marked as complete. -/// -/// # Fields -/// `pending_outbound_transactions` - List of transaction protocols sent by this client and waiting response from the -/// recipient -/// -/// `pending_inbound_transactions` - List of transaction protocols that have been received and responded to. -/// -/// -/// `completed_transaction` - List of sent transactions that have been responded to and are completed. -#[derive(Default)] -pub struct TransactionManager { - pending_outbound_transactions: HashMap, - pending_inbound_transactions: HashMap, - completed_transactions: HashMap, -} - -impl TransactionManager { - pub fn new() -> TransactionManager { - TransactionManager { - pending_outbound_transactions: HashMap::new(), - pending_inbound_transactions: HashMap::new(), - completed_transactions: HashMap::new(), - } - } - - /// Start to send a new transaction. - /// # Arguments - /// 'sender_transaction_protocol' - A well formed SenderTransactionProtocol ready to generate the SenderMessage. - /// - /// # Returns - /// Public SenderMessage to be transmitted to the recipient. - pub fn start_send_transaction( - &mut self, - mut sender_transaction_protocol: SenderTransactionProtocol, - ) -> Result - { - if !sender_transaction_protocol.is_single_round_message_ready() { - return Err(TransactionManagerError::InvalidStateError); - } - - let msg = sender_transaction_protocol.build_single_round_message()?; - - self.pending_outbound_transactions - .insert(msg.tx_id, sender_transaction_protocol); - - Ok(SenderMessage::Single(Box::new(msg))) - } - - /// Accept the public reply from a recipient and apply the reply to the relevant transaction protocol - /// # Arguments - /// 'recipient_reply' - The public response from a recipient with data required to complete the transaction - pub fn accept_recipient_reply( - &mut self, - recipient_reply: RecipientSignedTransactionData, - prover: &RangeProofService, - factory: &CommitmentFactory, - ) -> Result<(), TransactionManagerError> - { - let mut marked_for_removal = None; - - for (&tx_id, stp) in self.pending_outbound_transactions.iter_mut() { - let recp_tx_id = recipient_reply.tx_id; - if stp.check_tx_id(recp_tx_id) && stp.is_collecting_single_signature() { - stp.add_single_recipient_info(recipient_reply, prover)?; - stp.finalize(KernelFeatures::empty(), prover, factory)?; - let tx = stp.get_transaction()?; - self.completed_transactions.insert(recp_tx_id, tx.clone()); - marked_for_removal = Some(tx_id); - break; - } - } - - if marked_for_removal.is_none() { - return Err(TransactionManagerError::TransactionDoesNotExistError); - } - - if let Some(tx_id) = marked_for_removal { - self.pending_outbound_transactions.remove(&tx_id); - } - - Ok(()) - } - - /// Accept a new transaction from a sender by handling a public SenderMessage - /// # Arguments - /// 'sender_message' - Message from a sender containing the setup of the transaction being sent to you - /// 'nonce' - Your chosen nonce for your signature of this transaction - /// 'spending_key' - Your chosen secret_key for this transaction - /// # Returns - /// Public reply message to be sent back to the sender. - pub fn accept_transaction( - &mut self, - sender_message: SenderMessage, - nonce: PrivateKey, - spending_key: PrivateKey, - prover: &RangeProofService, - factory: &CommitmentFactory, - ) -> Result - { - let rtp = ReceiverTransactionProtocol::new( - sender_message, - nonce, - spending_key, - OutputFeatures::empty(), - prover, - factory, - ); - let recipient_reply = rtp.get_signed_data()?.clone(); - - // Check this is not a repeat message i.e. tx_id doesn't already exist in our pending or completed transactions - if self.pending_outbound_transactions.contains_key(&recipient_reply.tx_id) { - return Err(TransactionManagerError::RepeatedMessageError); - } - - if self.pending_inbound_transactions.contains_key(&recipient_reply.tx_id) { - return Err(TransactionManagerError::RepeatedMessageError); - } - - if self.completed_transactions.contains_key(&recipient_reply.tx_id) { - return Err(TransactionManagerError::RepeatedMessageError); - } - - // Otherwise add it to our pending transaction list and return reply - self.pending_inbound_transactions.insert(recipient_reply.tx_id, rtp); - - Ok(recipient_reply) - } - - /// Returns the list of the completed transactions - pub fn get_completed_transactions(&self) -> &HashMap { - &self.completed_transactions - } - - pub fn num_pending_inbound_transactions(&self) -> usize { - self.pending_inbound_transactions.len() - } - - pub fn num_pending_outbound_transactions(&self) -> usize { - self.pending_outbound_transactions.len() - } -} - -#[cfg(test)] -mod test { - use crate::transaction_manager::{TransactionManager, TransactionManagerError}; - use rand::{CryptoRng, OsRng, Rng}; - use tari_core::{ - tari_amount::*, - transaction::{OutputFeatures, TransactionInput, UnblindedOutput}, - transaction_protocol::{sender::SenderMessage, TransactionProtocolError}, - types::{PrivateKey, PublicKey, RangeProof, COMMITMENT_FACTORY, PROVER}, - SenderTransactionProtocol, - }; - use tari_crypto::{ - commitment::HomomorphicCommitmentFactory, - common::Blake256, - keys::{PublicKey as PK, SecretKey as SK}, - }; - use tari_utilities::ByteArray; - - pub struct TestParams { - pub spend_key: PrivateKey, - pub change_key: PrivateKey, - pub offset: PrivateKey, - pub nonce: PrivateKey, - pub public_nonce: PublicKey, - } - - impl TestParams { - pub fn new(rng: &mut R) -> TestParams { - let r = PrivateKey::random(rng); - TestParams { - spend_key: PrivateKey::random(rng), - change_key: PrivateKey::random(rng), - offset: PrivateKey::random(rng), - public_nonce: PublicKey::from_secret_key(&r), - nonce: r, - } - } - } - - pub fn make_input(rng: &mut R, val: MicroTari) -> (TransactionInput, UnblindedOutput) { - let key = PrivateKey::random(rng); - let commitment = COMMITMENT_FACTORY.commit_value(&key, val.into()); - let input = TransactionInput::new(OutputFeatures::empty(), commitment); - (input, UnblindedOutput::new(val, key, None)) - } - - #[test] - fn manage_single_transaction() { - let mut rng = OsRng::new().unwrap(); - // Alice's parameters - let a = TestParams::new(&mut rng); - // Bob's parameters - let b = TestParams::new(&mut rng); - let (utxo, input) = make_input(&mut rng, MicroTari(2500)); - let mut builder = SenderTransactionProtocol::builder(1); - builder - .with_lock_height(0) - .with_fee_per_gram(MicroTari(20)) - .with_offset(a.offset.clone()) - .with_private_nonce(a.nonce.clone()) - .with_change_secret(a.change_key.clone()) - .with_input(utxo.clone(), input) - .with_amount(0, MicroTari(500)); - let alice_stp = builder.build::(&PROVER, &COMMITMENT_FACTORY).unwrap(); - - let mut alice_tx_manager = TransactionManager::new(); - let mut bob_tx_manager = TransactionManager::new(); - - let send_msg = alice_tx_manager.start_send_transaction(alice_stp).unwrap(); - let mut tx_id = 0; - if let SenderMessage::Single(single_round_sender_data) = send_msg.clone() { - tx_id = single_round_sender_data.tx_id; - } - - assert_eq!(alice_tx_manager.num_pending_outbound_transactions(), 1); - - let receive_msg = bob_tx_manager - .accept_transaction(send_msg, b.nonce, b.spend_key, &PROVER, &COMMITMENT_FACTORY) - .unwrap(); - - assert_eq!(bob_tx_manager.num_pending_inbound_transactions(), 1); - - alice_tx_manager - .accept_recipient_reply(receive_msg, &PROVER, &COMMITMENT_FACTORY) - .unwrap(); - - let txs = alice_tx_manager.get_completed_transactions(); - - assert_eq!(txs.len(), 1); - assert_eq!(alice_tx_manager.num_pending_outbound_transactions(), 0); - assert!(txs.contains_key(&tx_id)); - } - - #[test] - fn manage_multiple_transactions() { - let mut rng = OsRng::new().unwrap(); - // Alice, Bob and Carols various parameters - let a_send1 = TestParams::new(&mut rng); - let a_send2 = TestParams::new(&mut rng); - let a_send3 = TestParams::new(&mut rng); - let a_recv1 = TestParams::new(&mut rng); - let b_send1 = TestParams::new(&mut rng); - let b_recv1 = TestParams::new(&mut rng); - let b_recv2 = TestParams::new(&mut rng); - let c_recv1 = TestParams::new(&mut rng); - - // Initializing all the sending transaction protocols - // Alice - let (utxo_a1, input_a1) = make_input(&mut rng, MicroTari(2500)); - let mut builder_a1 = SenderTransactionProtocol::builder(1); - builder_a1 - .with_lock_height(0) - .with_fee_per_gram(MicroTari(20)) - .with_offset(a_send1.offset.clone()) - .with_private_nonce(a_send1.nonce.clone()) - .with_change_secret(a_send1.change_key.clone()) - .with_input(utxo_a1.clone(), input_a1) - .with_amount(0, MicroTari(500)); - let alice_stp1 = builder_a1.build::(&PROVER, &COMMITMENT_FACTORY).unwrap(); - - let (utxo_a2, input_a2) = make_input(&mut rng, MicroTari(2500)); - let mut builder_a2 = SenderTransactionProtocol::builder(1); - builder_a2 - .with_lock_height(0) - .with_fee_per_gram(MicroTari(20)) - .with_offset(a_send2.offset.clone()) - .with_private_nonce(a_send2.nonce.clone()) - .with_change_secret(a_send2.change_key.clone()) - .with_input(utxo_a2.clone(), input_a2) - .with_amount(0, MicroTari(500)); - let alice_stp2 = builder_a2.build::(&PROVER, &COMMITMENT_FACTORY).unwrap(); - - let (utxo_a3, input_a3) = make_input(&mut rng, MicroTari(2500)); - let mut builder_a3 = SenderTransactionProtocol::builder(1); - builder_a3 - .with_lock_height(0) - .with_fee_per_gram(MicroTari(20)) - .with_offset(a_send3.offset.clone()) - .with_private_nonce(a_send3.nonce.clone()) - .with_change_secret(a_send3.change_key.clone()) - .with_input(utxo_a3.clone(), input_a3) - .with_amount(0, MicroTari(500)); - let alice_stp3 = builder_a3.build::(&PROVER, &COMMITMENT_FACTORY).unwrap(); - - // Bob - let (utxo_b1, input_b1) = make_input(&mut rng, MicroTari(2500)); - let mut builder_b1 = SenderTransactionProtocol::builder(1); - builder_b1 - .with_lock_height(0) - .with_fee_per_gram(MicroTari(20)) - .with_offset(b_send1.offset.clone()) - .with_private_nonce(b_send1.nonce.clone()) - .with_change_secret(b_send1.change_key.clone()) - .with_input(utxo_b1.clone(), input_b1) - .with_amount(0, MicroTari(500)); - let bob_stp1 = builder_b1.build::(&PROVER, &COMMITMENT_FACTORY).unwrap(); - - let mut alice_tx_manager = TransactionManager::new(); - let mut bob_tx_manager = TransactionManager::new(); - let mut carol_tx_manager = TransactionManager::new(); - - // Now a series of interleaved sending and receiving of transactions - let send_msg_a1 = alice_tx_manager.start_send_transaction(alice_stp1).unwrap(); - let mut alice_tx_ids = Vec::new(); - if let SenderMessage::Single(single_round_sender_data) = send_msg_a1.clone() { - alice_tx_ids.push(single_round_sender_data.tx_id); - } - assert_eq!(alice_tx_manager.num_pending_outbound_transactions(), 1); - - let send_msg_a2 = alice_tx_manager.start_send_transaction(alice_stp2).unwrap(); - if let SenderMessage::Single(single_round_sender_data) = send_msg_a2.clone() { - alice_tx_ids.push(single_round_sender_data.tx_id); - } - assert_eq!(alice_tx_manager.num_pending_outbound_transactions(), 2); - - let receive_msg_b1 = bob_tx_manager - .accept_transaction( - send_msg_a1, - b_recv1.nonce, - b_recv1.spend_key, - &PROVER, - &COMMITMENT_FACTORY, - ) - .unwrap(); - assert_eq!(bob_tx_manager.num_pending_inbound_transactions(), 1); - - let receive_msg_c1 = carol_tx_manager - .accept_transaction( - send_msg_a2, - c_recv1.nonce, - c_recv1.spend_key, - &PROVER, - &COMMITMENT_FACTORY, - ) - .unwrap(); - assert_eq!(carol_tx_manager.num_pending_inbound_transactions(), 1); - - let send_msg_b1 = bob_tx_manager.start_send_transaction(bob_stp1).unwrap(); - let mut bob_tx_ids = Vec::new(); - if let SenderMessage::Single(single_round_sender_data) = send_msg_b1.clone() { - bob_tx_ids.push(single_round_sender_data.tx_id); - } - assert_eq!(bob_tx_manager.num_pending_outbound_transactions(), 1); - - let receive_msg_a1 = alice_tx_manager - .accept_transaction( - send_msg_b1, - a_recv1.nonce, - a_recv1.spend_key, - &PROVER, - &COMMITMENT_FACTORY, - ) - .unwrap(); - assert_eq!(alice_tx_manager.num_pending_inbound_transactions(), 1); - - alice_tx_manager - .accept_recipient_reply(receive_msg_c1, &PROVER, &COMMITMENT_FACTORY) - .unwrap(); - assert_eq!(alice_tx_manager.num_pending_outbound_transactions(), 1); - assert_eq!(alice_tx_manager.get_completed_transactions().len(), 1); - - let send_msg_a3 = alice_tx_manager.start_send_transaction(alice_stp3).unwrap(); - if let SenderMessage::Single(single_round_sender_data) = send_msg_a3.clone() { - alice_tx_ids.push(single_round_sender_data.tx_id); - } - assert_eq!(alice_tx_manager.num_pending_outbound_transactions(), 2); - - let receive_msg_b2 = bob_tx_manager - .accept_transaction( - send_msg_a3, - b_recv2.nonce, - b_recv2.spend_key, - &PROVER, - &COMMITMENT_FACTORY, - ) - .unwrap(); - assert_eq!(bob_tx_manager.num_pending_inbound_transactions(), 2); - - alice_tx_manager - .accept_recipient_reply(receive_msg_b2, &PROVER, &COMMITMENT_FACTORY) - .unwrap(); - assert_eq!(alice_tx_manager.num_pending_outbound_transactions(), 1); - assert_eq!(alice_tx_manager.get_completed_transactions().len(), 2); - - bob_tx_manager - .accept_recipient_reply(receive_msg_a1, &PROVER, &COMMITMENT_FACTORY) - .unwrap(); - assert_eq!(bob_tx_manager.num_pending_outbound_transactions(), 0); - assert_eq!(bob_tx_manager.get_completed_transactions().len(), 1); - - alice_tx_manager - .accept_recipient_reply(receive_msg_b1, &PROVER, &COMMITMENT_FACTORY) - .unwrap(); - assert_eq!(alice_tx_manager.num_pending_outbound_transactions(), 0); - assert_eq!(alice_tx_manager.get_completed_transactions().len(), 3); - - for tx_id in alice_tx_ids { - assert!(alice_tx_manager.get_completed_transactions().contains_key(&tx_id)); - } - - for tx_id in bob_tx_ids { - assert!(bob_tx_manager.get_completed_transactions().contains_key(&tx_id)); - } - } - - #[test] - fn accept_repeated_tx_id() { - let mut rng = OsRng::new().unwrap(); - // Alice's parameters - let a = TestParams::new(&mut rng); - // Bob's parameters - let b = TestParams::new(&mut rng); - let (utxo, input) = make_input(&mut rng, MicroTari(2500)); - let mut builder = SenderTransactionProtocol::builder(1); - builder - .with_lock_height(0) - .with_fee_per_gram(MicroTari(20)) - .with_offset(a.offset.clone()) - .with_private_nonce(a.nonce.clone()) - .with_change_secret(a.change_key.clone()) - .with_input(utxo.clone(), input) - .with_amount(0, MicroTari(500)); - let alice_stp = builder.build::(&PROVER, &COMMITMENT_FACTORY).unwrap(); - - let mut alice_tx_manager = TransactionManager::new(); - let mut bob_tx_manager = TransactionManager::new(); - - let send_msg = alice_tx_manager.start_send_transaction(alice_stp).unwrap(); - - let _receive_msg = bob_tx_manager - .accept_transaction( - send_msg.clone(), - b.nonce.clone(), - b.spend_key.clone(), - &PROVER, - &COMMITMENT_FACTORY, - ) - .unwrap(); - - let receive_msg2 = - bob_tx_manager.accept_transaction(send_msg, b.nonce, b.spend_key, &PROVER, &COMMITMENT_FACTORY); - - assert_eq!(receive_msg2, Err(TransactionManagerError::RepeatedMessageError)); - } - - #[test] - fn accept_malformed_sender_message() { - let mut rng = OsRng::new().unwrap(); - // Bob's parameters - let b = TestParams::new(&mut rng); - let send_msg = SenderMessage::None; - let mut bob_tx_manager = TransactionManager::new(); - let receive_msg = - bob_tx_manager.accept_transaction(send_msg, b.nonce, b.spend_key, &PROVER, &COMMITMENT_FACTORY); - - assert_eq!( - receive_msg, - Err(TransactionManagerError::TransactionProtocolError( - TransactionProtocolError::InvalidStateError - )) - ); - } - - #[test] - fn accept_malformed_recipient_reply() { - let mut rng = OsRng::new().unwrap(); - // Alice's parameters - let a = TestParams::new(&mut rng); - // Bob's parameters - let b = TestParams::new(&mut rng); - let (utxo, input) = make_input(&mut rng, MicroTari(2500)); - let mut builder = SenderTransactionProtocol::builder(1); - builder - .with_lock_height(0) - .with_fee_per_gram(MicroTari(20)) - .with_offset(a.offset.clone()) - .with_private_nonce(a.nonce.clone()) - .with_change_secret(a.change_key.clone()) - .with_input(utxo.clone(), input) - .with_amount(0, MicroTari(500)); - let alice_stp = builder.build::(&PROVER, &COMMITMENT_FACTORY).unwrap(); - - let mut alice_tx_manager = TransactionManager::new(); - let mut bob_tx_manager = TransactionManager::new(); - - let send_msg = alice_tx_manager.start_send_transaction(alice_stp).unwrap(); - - let mut receive_msg = bob_tx_manager - .accept_transaction( - send_msg.clone(), - b.nonce.clone(), - b.spend_key.clone(), - &PROVER, - &COMMITMENT_FACTORY, - ) - .unwrap(); - - // Monkey with the range proof - receive_msg.output.proof = RangeProof::from_bytes(&[0u8; 32]).unwrap(); - - assert_eq!( - alice_tx_manager.accept_recipient_reply(receive_msg, &PROVER, &COMMITMENT_FACTORY), - Err(TransactionManagerError::TransactionProtocolError( - TransactionProtocolError::ValidationError("Recipient output range proof failed to verify".to_string()) - )) - ); - } - - #[test] - fn accept_recipient_reply_for_unknown_tx_id() { - let mut rng = OsRng::new().unwrap(); - // Alice's parameters - let a = TestParams::new(&mut rng); - // Bob's parameters - let b = TestParams::new(&mut rng); - let (utxo, input) = make_input(&mut rng, MicroTari(2500)); - let mut builder = SenderTransactionProtocol::builder(1); - builder - .with_lock_height(0) - .with_fee_per_gram(MicroTari(20)) - .with_offset(a.offset.clone()) - .with_private_nonce(a.nonce.clone()) - .with_change_secret(a.change_key.clone()) - .with_input(utxo.clone(), input) - .with_amount(0, MicroTari(500)); - let alice_stp = builder.build::(&PROVER, &COMMITMENT_FACTORY).unwrap(); - - let mut alice_tx_manager = TransactionManager::new(); - let mut bob_tx_manager = TransactionManager::new(); - - let send_msg = alice_tx_manager.start_send_transaction(alice_stp).unwrap(); - - let mut receive_msg = bob_tx_manager - .accept_transaction( - send_msg.clone(), - b.nonce.clone(), - b.spend_key.clone(), - &PROVER, - &COMMITMENT_FACTORY, - ) - .unwrap(); - - receive_msg.tx_id = 0; - - assert_eq!( - alice_tx_manager.accept_recipient_reply(receive_msg, &PROVER, &COMMITMENT_FACTORY), - Err(TransactionManagerError::TransactionDoesNotExistError) - ); - } - -} diff --git a/base_layer/wallet/src/transaction_service.rs b/base_layer/wallet/src/transaction_service.rs deleted file mode 100644 index df229b99d3..0000000000 --- a/base_layer/wallet/src/transaction_service.rs +++ /dev/null @@ -1,490 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE - -use crate::{ - output_manager_service::{error::OutputManagerError, output_manager_service::OutputManagerServiceApi}, - types::TransactionRng, -}; -use crossbeam_channel as channel; -use derive_error::Error; -use log::*; -use std::{ - collections::HashMap, - sync::{Arc, Mutex}, - time::Duration, -}; -use tari_comms::{ - domain_subscriber::SyncDomainSubscription, - message::MessageFlags, - outbound_message_service::{outbound_message_service::OutboundMessageService, BroadcastStrategy, OutboundError}, - types::CommsPublicKey, -}; -use tari_core::{ - tari_amount::MicroTari, - transaction::{KernelFeatures, OutputFeatures, Transaction}, - transaction_protocol::{ - recipient::RecipientSignedMessage, - sender::TransactionSenderMessage, - TransactionProtocolError, - }, - types::{PrivateKey, COMMITMENT_FACTORY, PROVER}, - ReceiverTransactionProtocol, - SenderTransactionProtocol, -}; -use tari_crypto::keys::SecretKey; -use tari_p2p::{ - sync_services::{ - Service, - ServiceApiWrapper, - ServiceContext, - ServiceControlMessage, - ServiceError, - DEFAULT_API_TIMEOUT_MS, - }, - tari_message::{BlockchainMessage, TariMessageType}, -}; - -const LOG_TARGET: &'static str = "base_layer::wallet::transaction_service"; - -#[derive(Debug, Error)] -pub enum TransactionServiceError { - // Transaction protocol is not in the correct state for this operation - InvalidStateError, - // Transaction Protocol Error - TransactionProtocolError(TransactionProtocolError), - // The message being process is not recognized by the Transaction Manager - InvalidMessageTypeError, - // A message for a specific tx_id has been repeated - RepeatedMessageError, - // A recipient reply was received for a non-existent tx_id - TransactionDoesNotExistError, - /// The Outbound Message Service is not initialized - OutboundMessageServiceNotInitialized, - /// Received an unexpected API response - UnexpectedApiResponse, - /// Failed to send from API - ApiSendFailed, - /// Failed to receive in API from service - ApiReceiveFailed, - OutboundError(OutboundError), - OutputManagerError(OutputManagerError), -} - -/// TransactionService allows for the management of multiple inbound and outbound transaction protocols -/// which are uniquely identified by a tx_id. The TransactionService generates and accepts the various protocol -/// messages and applies them to the appropriate protocol instances based on the tx_id. -/// The TransactionService allows for the sending of transactions to single receivers, when the appropriate recipient -/// response is handled the transaction is completed and moved to the completed_transaction buffer. -/// The TransactionService will accept inbound transactions and generate a reply. Received transactions will remain -/// in the pending_inbound_transactions buffer. -/// TODO Allow for inbound transactions that are detected on the blockchain to be marked as complete in the -/// OutputManagerService -/// TODO Detect Completed Transactions on the blockchain before marking them as completed in OutputManagerService -/// # Fields -/// `pending_outbound_transactions` - List of transaction protocols sent by this client and waiting response from the -/// recipient -/// `pending_inbound_transactions` - List of transaction protocols that have been received and responded to. -/// `completed_transaction` - List of sent transactions that have been responded to and are completed. - -pub struct TransactionService { - pending_outbound_transactions: HashMap, - pending_inbound_transactions: HashMap, - completed_transactions: HashMap, - outbound_message_service: Option>, - api: ServiceApiWrapper, - output_manager_service: Arc, -} - -impl TransactionService { - pub fn new(output_manager_service: Arc) -> TransactionService { - TransactionService { - pending_outbound_transactions: HashMap::new(), - pending_inbound_transactions: HashMap::new(), - completed_transactions: HashMap::new(), - outbound_message_service: None, - api: Self::setup_api(), - output_manager_service, - } - } - - fn setup_api() -> ServiceApiWrapper - { - let (api_sender, service_receiver) = channel::bounded(0); - let (service_sender, api_receiver) = channel::bounded(0); - - let api = Arc::new(TransactionServiceApi::new(api_sender, api_receiver)); - ServiceApiWrapper::new(service_receiver, service_sender, api) - } - - /// Return this service's API - pub fn get_api(&self) -> Arc { - self.api.get_api() - } - - /// Sends a new transaction to a recipient - /// # Arguments - /// 'dest_pubkey': The Comms pubkey of the recipient node - /// 'amount': The amount of Tari to send to the recipient - /// 'fee_per_gram': The amount of fee per transaction gram to be included in transaction - pub fn send_transaction( - &mut self, - dest_pubkey: CommsPublicKey, - amount: MicroTari, - fee_per_gram: MicroTari, - ) -> Result<(), TransactionServiceError> - { - let outbound_message_service = self - .outbound_message_service - .clone() - .ok_or(TransactionServiceError::OutboundMessageServiceNotInitialized)?; - - let mut stp = self - .output_manager_service - .prepare_transaction_to_send(amount, fee_per_gram, None)?; - - if !stp.is_single_round_message_ready() { - return Err(TransactionServiceError::InvalidStateError); - } - - let msg = stp.build_single_round_message()?; - outbound_message_service.send_message( - BroadcastStrategy::DirectPublicKey(dest_pubkey.clone()), - MessageFlags::ENCRYPTED, - TransactionSenderMessage::Single(Box::new(msg.clone())), - )?; - - self.pending_outbound_transactions.insert(msg.tx_id.clone(), stp); - - info!( - target: LOG_TARGET, - "Transaction with TX_ID = {} sent to {}", - msg.tx_id.clone(), - dest_pubkey - ); - - Ok(()) - } - - /// Accept the public reply from a recipient and apply the reply to the relevant transaction protocol - /// # Arguments - /// 'recipient_reply' - The public response from a recipient with data required to complete the transaction - pub fn accept_recipient_reply( - &mut self, - recipient_reply: RecipientSignedMessage, - ) -> Result<(), TransactionServiceError> - { - let mut marked_for_removal = None; - - for (tx_id, stp) in self.pending_outbound_transactions.iter_mut() { - let recp_tx_id = recipient_reply.tx_id.clone(); - if stp.check_tx_id(recp_tx_id) && stp.is_collecting_single_signature() { - stp.add_single_recipient_info(recipient_reply, &PROVER)?; - stp.finalize(KernelFeatures::empty(), &PROVER, &COMMITMENT_FACTORY)?; - let tx = stp.get_transaction()?; - self.completed_transactions.insert(recp_tx_id, tx.clone()); - // TODO Broadcast this to the chain - // TODO Only confirm this transaction once it is detected on chain. For now just confirming it directly. - self.output_manager_service.confirm_sent_transaction( - recp_tx_id, - tx.body.inputs.clone(), - tx.body.outputs.clone(), - )?; - - marked_for_removal = Some(tx_id.clone()); - break; - } - } - - if marked_for_removal.is_none() { - return Err(TransactionServiceError::TransactionDoesNotExistError); - } - - if let Some(tx_id) = marked_for_removal { - self.pending_outbound_transactions.remove(&tx_id); - info!( - target: LOG_TARGET, - "Transaction Recipient Reply for TX_ID = {} received", tx_id, - ); - } - - Ok(()) - } - - /// Accept a new transaction from a sender by handling a public SenderMessage. The reply is generated and sent. - /// # Arguments - /// 'source_pubkey' - The pubkey from which the message was sent and to which the reply will be sent. - /// 'sender_message' - Message from a sender containing the setup of the transaction being sent to you - pub fn accept_transaction( - &mut self, - source_pubkey: CommsPublicKey, - sender_message: TransactionSenderMessage, - ) -> Result<(), TransactionServiceError> - { - let outbound_message_service = self - .outbound_message_service - .clone() - .ok_or(TransactionServiceError::OutboundMessageServiceNotInitialized)?; - - // Currently we will only reply to a Single sender transaction protocol - if let TransactionSenderMessage::Single(data) = sender_message.clone() { - let spending_key = self - .output_manager_service - .get_recipient_spending_key(data.tx_id, data.amount)?; - let mut rng = TransactionRng::new().unwrap(); - let nonce = PrivateKey::random(&mut rng); - - let rtp = ReceiverTransactionProtocol::new( - sender_message, - nonce, - spending_key, - OutputFeatures::default(), - &PROVER, - &COMMITMENT_FACTORY, - ); - let recipient_reply = rtp.get_signed_data()?.clone(); - - // Check this is not a repeat message i.e. tx_id doesn't already exist in our pending or completed - // transactions - if self.pending_outbound_transactions.contains_key(&recipient_reply.tx_id) || - self.pending_inbound_transactions.contains_key(&recipient_reply.tx_id) || - self.completed_transactions.contains_key(&recipient_reply.tx_id) - { - return Err(TransactionServiceError::RepeatedMessageError); - } - - outbound_message_service.send_message( - BroadcastStrategy::DirectPublicKey(source_pubkey.clone()), - MessageFlags::ENCRYPTED, - recipient_reply.clone(), - )?; - - // Otherwise add it to our pending transaction list and return reply - self.pending_inbound_transactions - .insert(recipient_reply.tx_id.clone(), rtp); - - info!( - target: LOG_TARGET, - "Transaction with TX_ID = {} received from {}. Reply Sent", - recipient_reply.tx_id.clone(), - source_pubkey.clone() - ); - } - Ok(()) - } - - /// This handler is called when the Service executor loops receives an API request - fn handle_api_message(&mut self, msg: TransactionServiceApiRequest) -> Result<(), ServiceError> { - trace!(target: LOG_TARGET, "[{}] Received API message", self.get_name(),); - let resp = match msg { - TransactionServiceApiRequest::SendTransaction((dest_pubkey, amount, fee_per_gram)) => self - .send_transaction(dest_pubkey, amount, fee_per_gram) - .map(|_| TransactionServiceApiResponse::TransactionSent), - TransactionServiceApiRequest::GetPendingInboundTransactions => Ok( - TransactionServiceApiResponse::PendingInboundTransactions(self.pending_inbound_transactions.clone()), - ), - TransactionServiceApiRequest::GetPendingOutboundTransactions => Ok( - TransactionServiceApiResponse::PendingOutboundTransactions(self.pending_outbound_transactions.clone()), - ), - TransactionServiceApiRequest::GetCompletedTransactions => Ok( - TransactionServiceApiResponse::CompletedTransactions(self.completed_transactions.clone()), - ), - }; - - trace!(target: LOG_TARGET, "[{}] Replying to API", self.get_name()); - self.api - .send_reply(resp) - .map_err(ServiceError::internal_service_error()) - } -} - -/// The Domain Service trait implementation for the TestMessageService -impl Service for TransactionService { - fn get_name(&self) -> String { - "Transaction service".to_string() - } - - fn get_message_types(&self) -> Vec { - vec![ - BlockchainMessage::Transaction.into(), - BlockchainMessage::TransactionReply.into(), - ] - } - - /// Function called by the Service Executor in its own thread. This function polls for both API request and Comms - /// layer messages from the Message Broker - fn execute(&mut self, context: ServiceContext) -> Result<(), ServiceError> { - let mut subscription_transaction = SyncDomainSubscription::new( - context - .inbound_message_subscription_factory() - .get_subscription_fused(BlockchainMessage::Transaction.into()), - ); - - let mut subscription_transaction_reply = SyncDomainSubscription::new( - context - .inbound_message_subscription_factory() - .get_subscription_fused(BlockchainMessage::TransactionReply.into()), - ); - - self.outbound_message_service = Some(context.outbound_message_service()); - debug!(target: LOG_TARGET, "Starting Transaction Service executor"); - loop { - if let Some(msg) = context.get_control_message(Duration::from_millis(5)) { - match msg { - ServiceControlMessage::Shutdown => break, - } - } - - for m in subscription_transaction.receive_messages()?.drain(..) { - if let Err(e) = self.accept_transaction(m.0.origin_source.clone(), m.1) { - error!(target: LOG_TARGET, "Transaction service had error: {:?}", e); - } - } - - for m in subscription_transaction_reply.receive_messages()?.drain(..) { - if let Err(e) = self.accept_recipient_reply(m.1) { - error!(target: LOG_TARGET, "Transaction service had error: {:?}", e); - } - } - - if let Some(msg) = self - .api - .recv_timeout(Duration::from_millis(50)) - .map_err(ServiceError::internal_service_error())? - { - self.handle_api_message(msg)?; - } - } - - Ok(()) - } -} - -/// API Request enum -#[derive(Debug)] -pub enum TransactionServiceApiRequest { - GetPendingInboundTransactions, - GetPendingOutboundTransactions, - GetCompletedTransactions, - SendTransaction((CommsPublicKey, MicroTari, MicroTari)), -} - -/// API Response enum -#[derive(Debug)] -pub enum TransactionServiceApiResponse { - TransactionSent, - PendingInboundTransactions(HashMap), - PendingOutboundTransactions(HashMap), - CompletedTransactions(HashMap), -} - -/// Result for all API requests -pub type TransactionServiceApiResult = Result; - -/// The TextMessage service public API that other services and application will use to interact with this service. -/// The requests and responses are transmitted via channels into the Service Executor thread where this service is -/// running -pub struct TransactionServiceApi { - sender: channel::Sender, - receiver: channel::Receiver, - mutex: Mutex<()>, - timeout: Duration, -} - -impl TransactionServiceApi { - fn new( - sender: channel::Sender, - receiver: channel::Receiver, - ) -> Self - { - Self { - sender, - receiver, - mutex: Mutex::new(()), - timeout: Duration::from_millis(DEFAULT_API_TIMEOUT_MS), - } - } - - pub fn send_transaction( - &self, - dest_pubkey: CommsPublicKey, - amount: MicroTari, - fee_per_gram: MicroTari, - ) -> Result<(), TransactionServiceError> - { - self.send_recv(TransactionServiceApiRequest::SendTransaction(( - dest_pubkey, - amount, - fee_per_gram, - ))) - .and_then(|resp| match resp { - TransactionServiceApiResponse::TransactionSent => Ok(()), - _ => Err(TransactionServiceError::UnexpectedApiResponse), - }) - } - - pub fn get_pending_inbound_transaction( - &self, - ) -> Result, TransactionServiceError> { - self.send_recv(TransactionServiceApiRequest::GetPendingInboundTransactions) - .and_then(|resp| match resp { - TransactionServiceApiResponse::PendingInboundTransactions(p) => Ok(p), - _ => Err(TransactionServiceError::UnexpectedApiResponse), - }) - } - - pub fn get_pending_outbound_transaction( - &self, - ) -> Result, TransactionServiceError> { - self.send_recv(TransactionServiceApiRequest::GetPendingOutboundTransactions) - .and_then(|resp| match resp { - TransactionServiceApiResponse::PendingOutboundTransactions(p) => Ok(p), - _ => Err(TransactionServiceError::UnexpectedApiResponse), - }) - } - - pub fn get_completed_transaction(&self) -> Result, TransactionServiceError> { - self.send_recv(TransactionServiceApiRequest::GetCompletedTransactions) - .and_then(|resp| match resp { - TransactionServiceApiResponse::CompletedTransactions(c) => Ok(c), - _ => Err(TransactionServiceError::UnexpectedApiResponse), - }) - } - - fn send_recv(&self, msg: TransactionServiceApiRequest) -> TransactionServiceApiResult { - self.lock(|| -> TransactionServiceApiResult { - self.sender - .send(msg) - .map_err(|_| TransactionServiceError::ApiSendFailed)?; - self.receiver - .recv_timeout(self.timeout.clone()) - .map_err(|_| TransactionServiceError::ApiReceiveFailed)? - }) - } - - fn lock(&self, func: F) -> T - where F: FnOnce() -> T { - let lock = acquire_lock!(self.mutex); - let res = func(); - drop(lock); - res - } -} diff --git a/base_layer/wallet/src/transaction_service/config.rs b/base_layer/wallet/src/transaction_service/config.rs new file mode 100644 index 0000000000..54bc1dfcf4 --- /dev/null +++ b/base_layer/wallet/src/transaction_service/config.rs @@ -0,0 +1,38 @@ +// Copyright 2020. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::time::Duration; + +#[derive(Clone)] +pub struct TransactionServiceConfig { + pub mempool_broadcast_timeout: Duration, + pub base_node_mined_timeout: Duration, +} + +impl Default for TransactionServiceConfig { + fn default() -> Self { + Self { + mempool_broadcast_timeout: Duration::from_secs(30), + base_node_mined_timeout: Duration::from_secs(30), + } + } +} diff --git a/base_layer/wallet/src/transaction_service/error.rs b/base_layer/wallet/src/transaction_service/error.rs new file mode 100644 index 0000000000..c5b6e7cd67 --- /dev/null +++ b/base_layer/wallet/src/transaction_service/error.rs @@ -0,0 +1,116 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + output_manager_service::{error::OutputManagerError, TxId}, + transaction_service::storage::database::DbKey, +}; +use derive_error::Error; +use diesel::result::Error as DieselError; +use serde_json::Error as SerdeJsonError; +use tari_comms::peer_manager::node_id::NodeIdError; +use tari_comms_dht::outbound::DhtOutboundError; +use tari_core::transactions::{transaction::TransactionError, transaction_protocol::TransactionProtocolError}; +use tari_service_framework::reply_channel::TransportChannelError; +use time::OutOfRangeError; + +#[derive(Debug, Error)] +pub enum TransactionServiceError { + /// Transaction protocol is not in the correct state for this operation + InvalidStateError, + /// Transaction Protocol Error + TransactionProtocolError(TransactionProtocolError), + /// The message being processed is not recognized by the Transaction Manager + InvalidMessageTypeError, + /// A message for a specific tx_id has been repeated + RepeatedMessageError, + /// A recipient reply was received for a non-existent tx_id + TransactionDoesNotExistError, + /// The Outbound Message Service is not initialized + OutboundMessageServiceNotInitialized, + /// Received an unexpected API response + UnexpectedApiResponse, + /// Failed to send from API + ApiSendFailed, + /// Failed to receive in API from service + ApiReceiveFailed, + /// An error has occurred reading or writing the event subscriber stream + EventStreamError, + /// The Source Public Key on the received transaction does not match the transaction with the same TX_ID in the + /// database + InvalidSourcePublicKey, + /// The transaction does not contain the receivers output + ReceiverOutputNotFound, + /// Outbound Service send failed + OutboundSendFailure, + /// Outbound Service Discovery process needed to be conducted before message could be sent. The result of the + /// process will be communicated via the callback at some time in the future (could be minutes) + #[error(no_from, non_std)] + OutboundSendDiscoveryInProgress(TxId), + /// Discovery process failed to return a result + #[error(no_from, non_std)] + DiscoveryProcessFailed(TxId), + /// Invalid Completed Transaction provided + InvalidCompletedTransaction, + /// No Base Node public keys are provided for Base chain broadcast and monitoring + NoBaseNodeKeysProvided, + DhtOutboundError(DhtOutboundError), + OutputManagerError(OutputManagerError), + TransportChannelError(TransportChannelError), + TransactionStorageError(TransactionStorageError), + #[error(msg_embedded, no_from, non_std)] + InvalidMessageError(String), + #[cfg(feature = "test_harness")] + #[error(msg_embedded, no_from, non_std)] + TestHarnessError(String), + TransactionError(TransactionError), + #[error(msg_embedded, no_from, non_std)] + ConversionError(String), + NodeIdError(NodeIdError), +} + +#[derive(Debug, Error)] +pub enum TransactionStorageError { + /// Tried to insert an output that already exists in the database + DuplicateOutput, + #[error(non_std, no_from)] + ValueNotFound(DbKey), + #[error(msg_embedded, non_std, no_from)] + UnexpectedResult(String), + /// This write operation is not supported for provided DbKey + OperationNotSupported, + /// Could not find all values specified for batch operation + ValuesNotFound, + /// Transaction is already present in the database + TransactionAlreadyExists, + OutOfRangeError(OutOfRangeError), + /// Error converting a type + ConversionError, + SerdeJsonError(SerdeJsonError), + R2d2Error, + DieselError(DieselError), + DieselConnectionError(diesel::ConnectionError), + #[error(msg_embedded, no_from, non_std)] + DatabaseMigrationError(String), + #[error(msg_embedded, non_std, no_from)] + BlockingTaskSpawnError(String), +} diff --git a/base_layer/wallet/src/transaction_service/handle.rs b/base_layer/wallet/src/transaction_service/handle.rs new file mode 100644 index 0000000000..081c8627f3 --- /dev/null +++ b/base_layer/wallet/src/transaction_service/handle.rs @@ -0,0 +1,347 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + output_manager_service::TxId, + transaction_service::{ + error::TransactionServiceError, + service::PendingCoinbaseSpendingKey, + storage::database::{CompletedTransaction, InboundTransaction, OutboundTransaction}, + }, +}; +use futures::{stream::Fuse, StreamExt}; +use std::collections::HashMap; +use tari_broadcast_channel::Subscriber; +use tari_comms::types::CommsPublicKey; +use tari_core::transactions::{tari_amount::MicroTari, transaction::Transaction}; +use tari_service_framework::reply_channel::SenderService; +use tower::Service; + +/// API Request enum +#[derive(Debug)] +pub enum TransactionServiceRequest { + GetPendingInboundTransactions, + GetPendingOutboundTransactions, + GetCompletedTransactions, + SetBaseNodePublicKey(CommsPublicKey), + SendTransaction((CommsPublicKey, MicroTari, MicroTari, String)), + RequestCoinbaseSpendingKey((MicroTari, u64)), + CompleteCoinbaseTransaction((TxId, Transaction)), + CancelPendingCoinbaseTransaction(TxId), + ImportUtxo(MicroTari, CommsPublicKey, String), + #[cfg(feature = "test_harness")] + CompletePendingOutboundTransaction(CompletedTransaction), + #[cfg(feature = "test_harness")] + FinalizePendingInboundTransaction(TxId), + #[cfg(feature = "test_harness")] + AcceptTestTransaction((TxId, MicroTari, CommsPublicKey)), + #[cfg(feature = "test_harness")] + MineTransaction(TxId), + #[cfg(feature = "test_harness")] + BroadcastTransaction(TxId), +} + +/// API Response enum +#[derive(Debug)] +pub enum TransactionServiceResponse { + TransactionSent, + PendingInboundTransactions(HashMap), + PendingOutboundTransactions(HashMap), + CompletedTransactions(HashMap), + CoinbaseKey(PendingCoinbaseSpendingKey), + CompletedCoinbaseTransactionReceived, + CoinbaseTransactionCancelled, + BaseNodePublicKeySet, + UtxoImported(TxId), + #[cfg(feature = "test_harness")] + CompletedPendingTransaction, + #[cfg(feature = "test_harness")] + FinalizedPendingInboundTransaction, + #[cfg(feature = "test_harness")] + AcceptedTestTransaction, + #[cfg(feature = "test_harness")] + TransactionMined, + #[cfg(feature = "test_harness")] + TransactionBroadcast, +} + +/// Events that can be published on the Text Message Service Event Stream +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub enum TransactionEvent { + MempoolBroadcastTimedOut(TxId), + ReceivedTransaction(TxId), + ReceivedTransactionReply(TxId), + ReceivedFinalizedTransaction(TxId), + TransactionSendResult(TxId, bool), + TransactionSendDiscoveryComplete(TxId, bool), + TransactionBroadcast(TxId), + TransactionMined(TxId), + TransactionSendDiscoverySuccess(TxId), + TransactionSendDiscoveryFailure(TxId), + TransactionMinedRequestTimedOut(TxId), + Error(String), +} + +/// The Transaction Service Handle is a struct that contains the interfaces used to communicate with a running +/// Transaction Service +#[derive(Clone)] +pub struct TransactionServiceHandle { + handle: SenderService>, + event_stream: Subscriber, +} + +impl TransactionServiceHandle { + pub fn new( + handle: SenderService>, + event_stream: Subscriber, + ) -> Self + { + Self { handle, event_stream } + } + + pub fn get_event_stream_fused(&self) -> Fuse> { + self.event_stream.clone().fuse() + } + + pub async fn send_transaction( + &mut self, + dest_pubkey: CommsPublicKey, + amount: MicroTari, + fee_per_gram: MicroTari, + message: String, + ) -> Result<(), TransactionServiceError> + { + match self + .handle + .call(TransactionServiceRequest::SendTransaction(( + dest_pubkey, + amount, + fee_per_gram, + message, + ))) + .await?? + { + TransactionServiceResponse::TransactionSent => Ok(()), + _ => Err(TransactionServiceError::UnexpectedApiResponse), + } + } + + pub async fn get_pending_inbound_transactions( + &mut self, + ) -> Result, TransactionServiceError> { + match self + .handle + .call(TransactionServiceRequest::GetPendingInboundTransactions) + .await?? + { + TransactionServiceResponse::PendingInboundTransactions(p) => Ok(p), + _ => Err(TransactionServiceError::UnexpectedApiResponse), + } + } + + pub async fn get_pending_outbound_transactions( + &mut self, + ) -> Result, TransactionServiceError> { + match self + .handle + .call(TransactionServiceRequest::GetPendingOutboundTransactions) + .await?? + { + TransactionServiceResponse::PendingOutboundTransactions(p) => Ok(p), + _ => Err(TransactionServiceError::UnexpectedApiResponse), + } + } + + pub async fn get_completed_transactions( + &mut self, + ) -> Result, TransactionServiceError> { + match self + .handle + .call(TransactionServiceRequest::GetCompletedTransactions) + .await?? + { + TransactionServiceResponse::CompletedTransactions(c) => Ok(c), + _ => Err(TransactionServiceError::UnexpectedApiResponse), + } + } + + pub async fn request_coinbase_key( + &mut self, + amount: MicroTari, + maturity_height: u64, + ) -> Result + { + match self + .handle + .call(TransactionServiceRequest::RequestCoinbaseSpendingKey(( + amount, + maturity_height, + ))) + .await?? + { + TransactionServiceResponse::CoinbaseKey(c) => Ok(c), + _ => Err(TransactionServiceError::UnexpectedApiResponse), + } + } + + pub async fn complete_coinbase_transaction( + &mut self, + tx_id: TxId, + completed_transaction: Transaction, + ) -> Result<(), TransactionServiceError> + { + match self + .handle + .call(TransactionServiceRequest::CompleteCoinbaseTransaction(( + tx_id, + completed_transaction, + ))) + .await?? + { + TransactionServiceResponse::CompletedCoinbaseTransactionReceived => Ok(()), + _ => Err(TransactionServiceError::UnexpectedApiResponse), + } + } + + pub async fn cancel_coinbase_transaction(&mut self, tx_id: TxId) -> Result<(), TransactionServiceError> { + match self + .handle + .call(TransactionServiceRequest::CancelPendingCoinbaseTransaction(tx_id)) + .await?? + { + TransactionServiceResponse::CoinbaseTransactionCancelled => Ok(()), + _ => Err(TransactionServiceError::UnexpectedApiResponse), + } + } + + pub async fn set_base_node_public_key( + &mut self, + public_key: CommsPublicKey, + ) -> Result<(), TransactionServiceError> + { + match self + .handle + .call(TransactionServiceRequest::SetBaseNodePublicKey(public_key)) + .await?? + { + TransactionServiceResponse::BaseNodePublicKeySet => Ok(()), + _ => Err(TransactionServiceError::UnexpectedApiResponse), + } + } + + pub async fn import_utxo( + &mut self, + amount: MicroTari, + source_public_key: CommsPublicKey, + message: String, + ) -> Result + { + match self + .handle + .call(TransactionServiceRequest::ImportUtxo( + amount, + source_public_key, + message, + )) + .await?? + { + TransactionServiceResponse::UtxoImported(tx_id) => Ok(tx_id), + _ => Err(TransactionServiceError::UnexpectedApiResponse), + } + } + + #[cfg(feature = "test_harness")] + pub async fn test_complete_pending_transaction( + &mut self, + completed_tx: CompletedTransaction, + ) -> Result<(), TransactionServiceError> + { + match self + .handle + .call(TransactionServiceRequest::CompletePendingOutboundTransaction( + completed_tx, + )) + .await?? + { + TransactionServiceResponse::CompletedPendingTransaction => Ok(()), + _ => Err(TransactionServiceError::UnexpectedApiResponse), + } + } + + #[cfg(feature = "test_harness")] + pub async fn test_accept_transaction( + &mut self, + tx_id: TxId, + amount: MicroTari, + source_public_key: CommsPublicKey, + ) -> Result<(), TransactionServiceError> + { + match self + .handle + .call(TransactionServiceRequest::AcceptTestTransaction(( + tx_id, + amount, + source_public_key, + ))) + .await?? + { + TransactionServiceResponse::AcceptedTestTransaction => Ok(()), + _ => Err(TransactionServiceError::UnexpectedApiResponse), + } + } + + #[cfg(feature = "test_harness")] + pub async fn test_finalize_transaction(&mut self, tx_id: TxId) -> Result<(), TransactionServiceError> { + match self + .handle + .call(TransactionServiceRequest::FinalizePendingInboundTransaction(tx_id)) + .await?? + { + TransactionServiceResponse::FinalizedPendingInboundTransaction => Ok(()), + _ => Err(TransactionServiceError::UnexpectedApiResponse), + } + } + + #[cfg(feature = "test_harness")] + pub async fn test_broadcast_transaction(&mut self, tx_id: TxId) -> Result<(), TransactionServiceError> { + match self + .handle + .call(TransactionServiceRequest::BroadcastTransaction(tx_id)) + .await?? + { + TransactionServiceResponse::TransactionBroadcast => Ok(()), + _ => Err(TransactionServiceError::UnexpectedApiResponse), + } + } + + #[cfg(feature = "test_harness")] + pub async fn test_mine_transaction(&mut self, tx_id: TxId) -> Result<(), TransactionServiceError> { + match self + .handle + .call(TransactionServiceRequest::MineTransaction(tx_id)) + .await?? + { + TransactionServiceResponse::TransactionMined => Ok(()), + _ => Err(TransactionServiceError::UnexpectedApiResponse), + } + } +} diff --git a/base_layer/wallet/src/transaction_service/mod.rs b/base_layer/wallet/src/transaction_service/mod.rs new file mode 100644 index 0000000000..473475e7fb --- /dev/null +++ b/base_layer/wallet/src/transaction_service/mod.rs @@ -0,0 +1,211 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +pub mod config; +pub mod error; +pub mod handle; +pub mod service; +pub mod storage; + +use crate::{ + output_manager_service::handle::OutputManagerHandle, + transaction_service::{ + config::TransactionServiceConfig, + handle::TransactionServiceHandle, + service::TransactionService, + storage::database::{TransactionBackend, TransactionDatabase}, + }, +}; +use futures::{future, Future, Stream, StreamExt}; +use log::*; +use std::sync::Arc; +use tari_broadcast_channel::bounded; +use tari_comms::{peer_manager::NodeIdentity, protocol::messaging::MessagingEventReceiver}; +use tari_comms_dht::outbound::OutboundMessageRequester; +use tari_core::{ + base_node::proto::base_node as BaseNodeProto, + mempool::proto::mempool as MempoolProto, + transactions::{transaction_protocol::proto, types::CryptoFactories}, +}; +use tari_p2p::{ + comms_connector::PeerMessage, + domain_message::DomainMessage, + services::utils::{map_decode, ok_or_skip_result}, + tari_message::TariMessageType, +}; +use tari_pubsub::TopicSubscriptionFactory; +use tari_service_framework::{ + handles::ServiceHandlesFuture, + reply_channel, + ServiceInitializationError, + ServiceInitializer, +}; +use tari_shutdown::ShutdownSignal; +use tokio::runtime; + +const LOG_TARGET: &str = "base_layer::wallet::transaction_service"; + +pub struct TransactionServiceInitializer +where T: TransactionBackend +{ + config: TransactionServiceConfig, + subscription_factory: Arc>>, + message_event_receiver: Option, + backend: Option, + node_identity: Arc, + factories: CryptoFactories, +} + +impl TransactionServiceInitializer +where T: TransactionBackend +{ + pub fn new( + config: TransactionServiceConfig, + subscription_factory: Arc>>, + message_event_receiver: MessagingEventReceiver, + backend: T, + node_identity: Arc, + factories: CryptoFactories, + ) -> Self + { + Self { + config, + subscription_factory, + message_event_receiver: Some(message_event_receiver), + backend: Some(backend), + node_identity, + factories, + } + } + + /// Get a stream of inbound Text messages + fn transaction_stream(&self) -> impl Stream> { + self.subscription_factory + .get_subscription(TariMessageType::SenderPartialTransaction) + .map(map_decode::) + .filter_map(ok_or_skip_result) + } + + fn transaction_reply_stream(&self) -> impl Stream> { + self.subscription_factory + .get_subscription(TariMessageType::ReceiverPartialTransactionReply) + .map(map_decode::) + .filter_map(ok_or_skip_result) + } + + fn transaction_finalized_stream(&self) -> impl Stream> { + self.subscription_factory + .get_subscription(TariMessageType::TransactionFinalized) + .map(map_decode::) + .filter_map(ok_or_skip_result) + } + + fn mempool_response_stream(&self) -> impl Stream> { + self.subscription_factory + .get_subscription(TariMessageType::MempoolResponse) + .map(map_decode::) + .filter_map(ok_or_skip_result) + } + + fn base_node_response_stream(&self) -> impl Stream> { + self.subscription_factory + .get_subscription(TariMessageType::BaseNodeResponse) + .map(map_decode::) + .filter_map(ok_or_skip_result) + } +} + +impl ServiceInitializer for TransactionServiceInitializer +where T: TransactionBackend + Clone + 'static +{ + type Future = impl Future>; + + fn initialize( + &mut self, + executor: runtime::Handle, + handles_fut: ServiceHandlesFuture, + shutdown: ShutdownSignal, + ) -> Self::Future + { + let (sender, receiver) = reply_channel::unbounded(); + let transaction_stream = self.transaction_stream(); + let transaction_reply_stream = self.transaction_reply_stream(); + let transaction_finalized_stream = self.transaction_finalized_stream(); + let mempool_response_stream = self.mempool_response_stream(); + let base_node_response_stream = self.base_node_response_stream(); + + let (publisher, subscriber) = bounded(100); + + let transaction_handle = TransactionServiceHandle::new(sender, subscriber); + + // Register handle before waiting for handles to be ready + handles_fut.register(transaction_handle); + + let backend = self + .backend + .take() + .expect("Cannot start Transaction Service without providing a backend"); + + let message_event_receiver = self + .message_event_receiver + .take() + .expect("Cannot start Transaction Service without providing an Message Event Receiver"); + + let node_identity = self.node_identity.clone(); + let factories = self.factories.clone(); + let config = self.config.clone(); + + executor.spawn(async move { + let handles = handles_fut.await; + + let outbound_message_service = handles + .get_handle::() + .expect("OMS handle required for TransactionService"); + let output_manager_service = handles + .get_handle::() + .expect("Output Manager Service handle required for TransactionService"); + + let service = TransactionService::new( + config, + TransactionDatabase::new(backend), + receiver, + transaction_stream, + transaction_reply_stream, + transaction_finalized_stream, + mempool_response_stream, + base_node_response_stream, + output_manager_service, + outbound_message_service, + message_event_receiver, + publisher, + node_identity, + factories, + ) + .start(); + futures::pin_mut!(service); + future::select(service, shutdown).await; + info!(target: LOG_TARGET, "Transaction Service shutdown"); + }); + + future::ready(Ok(())) + } +} diff --git a/base_layer/wallet/src/transaction_service/service.rs b/base_layer/wallet/src/transaction_service/service.rs new file mode 100644 index 0000000000..71d34a16df --- /dev/null +++ b/base_layer/wallet/src/transaction_service/service.rs @@ -0,0 +1,1625 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + output_manager_service::{handle::OutputManagerHandle, TxId}, + transaction_service::{ + config::TransactionServiceConfig, + error::TransactionServiceError, + handle::{TransactionEvent, TransactionServiceRequest, TransactionServiceResponse}, + storage::database::{ + CompletedTransaction, + InboundTransaction, + OutboundTransaction, + PendingCoinbaseTransaction, + TransactionBackend, + TransactionDatabase, + TransactionStatus, + }, + }, + util::futures::StateDelay, +}; +use chrono::Utc; +use futures::{ + channel::oneshot, + future::{BoxFuture, FutureExt}, + pin_mut, + stream::FuturesUnordered, + SinkExt, + Stream, + StreamExt, +}; +use log::*; +use rand::{rngs::OsRng, RngCore}; +use std::{ + collections::HashMap, + convert::{TryFrom, TryInto}, + sync::Arc, +}; +use tari_broadcast_channel::Publisher; +use tari_comms::{ + message::MessageTag, + peer_manager::NodeIdentity, + protocol::messaging::{MessagingEvent, MessagingEventReceiver}, + types::CommsPublicKey, +}; +use tari_comms_dht::{ + domain_message::OutboundDomainMessage, + outbound::{OutboundEncryption, OutboundMessageRequester, SendMessageResponse}, +}; +#[cfg(feature = "test_harness")] +use tari_core::transactions::{tari_amount::T, types::BlindingFactor}; +use tari_core::{ + base_node::proto::{ + base_node as BaseNodeProto, + base_node::{ + base_node_service_request::Request as BaseNodeRequestProto, + base_node_service_response::Response as BaseNodeResponseProto, + }, + }, + mempool::{ + proto::mempool as MempoolProto, + service::{MempoolResponse, MempoolServiceResponse}, + TxStorageResponse, + }, + transactions::{ + proto as TransactionProto, + tari_amount::MicroTari, + transaction::{KernelFeatures, OutputFeatures, OutputFlags, Transaction, TransactionOutput}, + transaction_protocol::{ + proto, + recipient::{RecipientSignedMessage, RecipientState}, + sender::TransactionSenderMessage, + }, + types::{CryptoFactories, PrivateKey}, + ReceiverTransactionProtocol, + }, +}; +use tari_crypto::{commitment::HomomorphicCommitmentFactory, keys::SecretKey, tari_utilities::hash::Hashable}; +use tari_p2p::{domain_message::DomainMessage, tari_message::TariMessageType}; +use tari_service_framework::{reply_channel, reply_channel::Receiver}; + +const LOG_TARGET: &str = "base_layer::wallet::transaction_service::service"; + +/// Contains the generated TxId and SpendingKey for a Pending Coinbase transaction +#[derive(Debug)] +pub struct PendingCoinbaseSpendingKey { + pub tx_id: TxId, + pub spending_key: PrivateKey, +} + +/// TransactionService allows for the management of multiple inbound and outbound transaction protocols +/// which are uniquely identified by a tx_id. The TransactionService generates and accepts the various protocol +/// messages and applies them to the appropriate protocol instances based on the tx_id. +/// The TransactionService allows for the sending of transactions to single receivers, when the appropriate recipient +/// response is handled the transaction is completed and moved to the completed_transaction buffer. +/// The TransactionService will accept inbound transactions and generate a reply. Received transactions will remain +/// in the pending_inbound_transactions buffer. +/// TODO Allow for inbound transactions that are detected on the blockchain to be marked as complete in the +/// OutputManagerService +/// TODO Detect Completed Transactions on the blockchain before marking them as completed in OutputManagerService +/// # Fields +/// `pending_outbound_transactions` - List of transaction protocols sent by this client and waiting response from the +/// recipient +/// `pending_inbound_transactions` - List of transaction protocols that have been received and responded to. +/// `completed_transaction` - List of sent transactions that have been responded to and are completed. + +pub struct TransactionService +where TBackend: TransactionBackend + Clone + 'static +{ + config: TransactionServiceConfig, + db: TransactionDatabase, + outbound_message_service: OutboundMessageRequester, + message_event_receiver: Option, + output_manager_service: OutputManagerHandle, + transaction_stream: Option, + transaction_reply_stream: Option, + transaction_finalized_stream: Option, + mempool_response_stream: Option, + base_node_response_stream: Option, + request_stream: Option< + reply_channel::Receiver>, + >, + event_publisher: Publisher, + node_identity: Arc, + factories: CryptoFactories, + base_node_public_key: Option, + pending_outbound_message_results: HashMap, +} + +impl + TransactionService +where + TTxStream: Stream>, + TTxReplyStream: Stream>, + TTxFinalizedStream: Stream>, + MReplyStream: Stream>, + BNResponseStream: Stream>, + TBackend: TransactionBackend + Clone + 'static, +{ + pub fn new( + config: TransactionServiceConfig, + db: TransactionDatabase, + request_stream: Receiver< + TransactionServiceRequest, + Result, + >, + transaction_stream: TTxStream, + transaction_reply_stream: TTxReplyStream, + transaction_finalized_stream: TTxFinalizedStream, + mempool_response_stream: MReplyStream, + base_node_response_stream: BNResponseStream, + output_manager_service: OutputManagerHandle, + outbound_message_service: OutboundMessageRequester, + message_event_receiver: MessagingEventReceiver, + event_publisher: Publisher, + node_identity: Arc, + factories: CryptoFactories, + ) -> Self + { + TransactionService { + config, + db, + outbound_message_service, + message_event_receiver: Some(message_event_receiver), + output_manager_service, + transaction_stream: Some(transaction_stream), + transaction_reply_stream: Some(transaction_reply_stream), + transaction_finalized_stream: Some(transaction_finalized_stream), + mempool_response_stream: Some(mempool_response_stream), + base_node_response_stream: Some(base_node_response_stream), + request_stream: Some(request_stream), + event_publisher, + node_identity, + factories, + base_node_public_key: None, + pending_outbound_message_results: HashMap::new(), + } + } + + #[warn(unreachable_code)] + pub async fn start(mut self) -> Result<(), TransactionServiceError> { + let request_stream = self + .request_stream + .take() + .expect("Transaction Service initialized without request_stream") + .fuse(); + pin_mut!(request_stream); + let transaction_stream = self + .transaction_stream + .take() + .expect("Transaction Service initialized without transaction_stream") + .fuse(); + pin_mut!(transaction_stream); + let transaction_reply_stream = self + .transaction_reply_stream + .take() + .expect("Transaction Service initialized without transaction_reply_stream") + .fuse(); + pin_mut!(transaction_reply_stream); + let transaction_finalized_stream = self + .transaction_finalized_stream + .take() + .expect("Transaction Service initialized without transaction_finalized_stream") + .fuse(); + pin_mut!(transaction_finalized_stream); + let mempool_response_stream = self + .mempool_response_stream + .take() + .expect("Transaction Service initialized without mempool_response_stream") + .fuse(); + pin_mut!(mempool_response_stream); + let base_node_response_stream = self + .base_node_response_stream + .take() + .expect("Transaction Service initialized without base_node_response_stream") + .fuse(); + pin_mut!(base_node_response_stream); + let message_event_receiver = self + .message_event_receiver + .take() + .expect("Transaction Service initialized without message_event_subscription") + .fuse(); + pin_mut!(message_event_receiver); + + let mut discovery_process_futures: FuturesUnordered< + BoxFuture<'static, Result<(MessageTag, OutboundTransaction), TransactionServiceError>>, + > = FuturesUnordered::new(); + + let mut broadcast_timeout_futures: FuturesUnordered> = FuturesUnordered::new(); + let mut mined_request_timeout_futures: FuturesUnordered> = FuturesUnordered::new(); + + loop { + futures::select! { + //Incoming request + request_context = request_stream.select_next_some() => { + let (request, reply_tx) = request_context.split(); + let _ = reply_tx.send(self.handle_request(request, &mut discovery_process_futures, &mut broadcast_timeout_futures, &mut mined_request_timeout_futures).await.or_else(|resp| { + error!(target: LOG_TARGET, "Error handling request: {:?}", resp); + Err(resp) + })).or_else(|resp| { + error!(target: LOG_TARGET, "Failed to send reply"); + Err(resp) + }); + }, + // Incoming messages from the Comms layer + msg = transaction_stream.select_next_some() => { + let (origin_public_key, inner_msg) = msg.into_origin_and_inner(); + let result = self.accept_transaction(origin_public_key, inner_msg).await.or_else(|err| { + error!(target: LOG_TARGET, "Failed to handle incoming Transaction message: {:?} for NodeID: {}", err, self.node_identity.node_id().short_str()); + Err(err) + }); + + if result.is_err() { + let _ = self.event_publisher + .send(TransactionEvent::Error( + "Error handling Transaction Sender message".to_string(), + )) + .await; + } + }, + // Incoming messages from the Comms layer + msg = transaction_reply_stream.select_next_some() => { + let (origin_public_key, inner_msg) = msg.into_origin_and_inner(); + let result = self.accept_recipient_reply(origin_public_key, inner_msg, &mut broadcast_timeout_futures).await.or_else(|err| { + error!(target: LOG_TARGET, "Failed to handle incoming Transaction Reply message: {:?} for NodeId: {}", err, self.node_identity.node_id().short_str()); + Err(err) + }); + + if result.is_err() { + let _ = self.event_publisher + .send(TransactionEvent::Error( + "Error handling Transaction Recipient Reply message".to_string(), + )) + .await; + } + }, + // Incoming messages from the Comms layer + msg = transaction_finalized_stream.select_next_some() => { + let (origin_public_key, inner_msg) = msg.into_origin_and_inner(); + let result = self.accept_finalized_transaction(origin_public_key, inner_msg, &mut broadcast_timeout_futures).await.or_else(|err| { + error!(target: LOG_TARGET, "Failed to handle incoming Transaction Finalized message: {:?} for NodeID: {}", err , self.node_identity.node_id().short_str()); + Err(err) + }); + + if result.is_err() { + let _ = self.event_publisher + .send(TransactionEvent::Error( + "Error handling Transaction Finalized message".to_string(), + )) + .await; + } + }, + // Incoming messages from the Comms layer + msg = mempool_response_stream.select_next_some() => { + let (origin_public_key, inner_msg) = msg.into_origin_and_inner(); + let _ = self.handle_mempool_response(inner_msg, &mut mined_request_timeout_futures).await.or_else(|resp| { + error!(target: LOG_TARGET, "Error handling mempool service response: {:?}", resp); + Err(resp) + }); + } + // Incoming messages from the Comms layer + msg = base_node_response_stream.select_next_some() => { + let (origin_public_key, inner_msg) = msg.into_origin_and_inner(); + let _ = self.handle_base_node_response(inner_msg).await.or_else(|resp| { + error!(target: LOG_TARGET, "Error handling base node service response: {:?}", resp); + Err(resp) + }); + } + response = discovery_process_futures.select_next_some() => { + match response { + Ok((message_tag, outbound_tx)) => { + self.db + .add_pending_outbound_transaction(outbound_tx.tx_id, outbound_tx.clone()) + .await?; + self.pending_outbound_message_results.insert(message_tag, outbound_tx.clone()); + let _ = self.event_publisher + .send(TransactionEvent::TransactionSendDiscoveryComplete(outbound_tx.tx_id, true)) + .await; + info!( + target: LOG_TARGET, + "Discovery process completed for TxId: {} with Message Tag: {} now waiting for MessageSent event", + outbound_tx.tx_id, + message_tag, + ); + }, + Err(TransactionServiceError::DiscoveryProcessFailed(tx_id)) => { + if let Err(e) = self.output_manager_service.cancel_transaction(tx_id).await { + error!(target: LOG_TARGET, "Failed to Cancel TX_ID: {} after failed sending attempt", tx_id); + } + error!(target: LOG_TARGET, "Discovery and Send failed for TX_ID: {}", tx_id); + let _ = self.event_publisher + .send(TransactionEvent::TransactionSendDiscoveryComplete(tx_id, false)) + .await; + } + Err(e) => error!(target: LOG_TARGET, "Discovery and Send failed with Error: {:?}", e), + } + }, + message_event = message_event_receiver.select_next_some() => { + match message_event { + Ok(event) => { + let _ = self.handle_message_event((*event).clone()).await.or_else(|resp| { + error!(target: LOG_TARGET, "Error handling outbound message event: {:?}", resp); + Err(resp) + }); + }, + Err(e) => error!(target: LOG_TARGET, "Error handling Outbound Message Event: {:?}", e), + } + } + tx_id = broadcast_timeout_futures.select_next_some() => { + let _ = self.handle_mempool_broadcast_timeout(tx_id, &mut broadcast_timeout_futures).await.or_else(|resp| { + error!(target: LOG_TARGET, "Error handling mempool broadcast timeout : {:?}", resp); + Err(resp) + }); + } + tx_id = mined_request_timeout_futures.select_next_some() => { + let _ = self.handle_transaction_mined_request_timeout(tx_id, &mut mined_request_timeout_futures).await.or_else(|resp| { + error!(target: LOG_TARGET, "Error handling transaction mined? request timeout : {:?}", resp); + Err(resp) + }); + } + complete => { + info!(target: LOG_TARGET, "Transaction service shutting down"); + break; + } + } + } + Ok(()) + } + + /// This handler is called when requests arrive from the various streams + async fn handle_request( + &mut self, + request: TransactionServiceRequest, + discovery_process_futures: &mut FuturesUnordered< + BoxFuture<'static, Result<(MessageTag, OutboundTransaction), TransactionServiceError>>, + >, + broadcast_timeout_futures: &mut FuturesUnordered>, + mined_request_timeout_futures: &mut FuturesUnordered>, + ) -> Result + { + match request { + TransactionServiceRequest::SendTransaction((dest_pubkey, amount, fee_per_gram, message)) => self + .send_transaction(dest_pubkey, amount, fee_per_gram, message, discovery_process_futures) + .await + .map(|_| TransactionServiceResponse::TransactionSent), + TransactionServiceRequest::GetPendingInboundTransactions => Ok( + TransactionServiceResponse::PendingInboundTransactions(self.get_pending_inbound_transactions().await?), + ), + TransactionServiceRequest::GetPendingOutboundTransactions => { + Ok(TransactionServiceResponse::PendingOutboundTransactions( + self.get_pending_outbound_transactions().await?, + )) + }, + TransactionServiceRequest::GetCompletedTransactions => Ok( + TransactionServiceResponse::CompletedTransactions(self.get_completed_transactions().await?), + ), + TransactionServiceRequest::RequestCoinbaseSpendingKey((amount, maturity_height)) => Ok( + TransactionServiceResponse::CoinbaseKey(self.request_coinbase_key(amount, maturity_height).await?), + ), + TransactionServiceRequest::CompleteCoinbaseTransaction((tx_id, completed_transaction)) => { + self.submit_completed_coinbase_transaction(tx_id, completed_transaction) + .await?; + Ok(TransactionServiceResponse::CompletedCoinbaseTransactionReceived) + }, + TransactionServiceRequest::CancelPendingCoinbaseTransaction(tx_id) => { + self.cancel_pending_coinbase_transaction(tx_id).await?; + Ok(TransactionServiceResponse::CoinbaseTransactionCancelled) + }, + TransactionServiceRequest::SetBaseNodePublicKey(public_key) => self + .set_base_node_public_key(public_key, broadcast_timeout_futures, mined_request_timeout_futures) + .await + .map(|_| TransactionServiceResponse::BaseNodePublicKeySet), + TransactionServiceRequest::ImportUtxo(value, source_public_key, message) => self + .add_utxo_import_transaction(value, source_public_key, message) + .await + .map(TransactionServiceResponse::UtxoImported), + #[cfg(feature = "test_harness")] + TransactionServiceRequest::CompletePendingOutboundTransaction(completed_transaction) => { + self.complete_pending_outbound_transaction(completed_transaction) + .await?; + Ok(TransactionServiceResponse::CompletedPendingTransaction) + }, + #[cfg(feature = "test_harness")] + TransactionServiceRequest::FinalizePendingInboundTransaction(tx_id) => { + self.finalize_received_test_transaction(tx_id).await?; + Ok(TransactionServiceResponse::FinalizedPendingInboundTransaction) + }, + #[cfg(feature = "test_harness")] + TransactionServiceRequest::AcceptTestTransaction((tx_id, amount, source_pubkey)) => { + self.receive_test_transaction(tx_id, amount, source_pubkey).await?; + Ok(TransactionServiceResponse::AcceptedTestTransaction) + }, + #[cfg(feature = "test_harness")] + TransactionServiceRequest::BroadcastTransaction(tx_id) => { + self.broadcast_transaction(tx_id).await?; + Ok(TransactionServiceResponse::TransactionBroadcast) + }, + #[cfg(feature = "test_harness")] + TransactionServiceRequest::MineTransaction(tx_id) => { + self.mine_transaction(tx_id).await?; + Ok(TransactionServiceResponse::TransactionMined) + }, + } + } + + async fn handle_message_event(&mut self, message_event: MessagingEvent) -> Result<(), TransactionServiceError> { + let (message_tag, result) = match message_event { + MessagingEvent::MessageSent(message_tag) => (message_tag, true), + MessagingEvent::SendMessageFailed(outbound_message, _reason) => (outbound_message.tag, false), + _ => return Ok(()), + }; + match self.pending_outbound_message_results.remove(&message_tag) { + None => (), + Some(outbound_tx) => { + // If the message was successfully sent then add it to the pending transaction list + if result { + info!( + target: LOG_TARGET, + "Pending Outbound Transaction TxId: {:?} was successfully sent with Message Tag: {:?}", + outbound_tx.tx_id, + message_tag + ); + } else { + error!( + target: LOG_TARGET, + "Pending Outbound Transaction TxId: {:?} with Message Tag {:?} could not be sent", + message_tag, + outbound_tx.tx_id + ); + + self.db.remove_pending_outbound_transaction(outbound_tx.tx_id).await?; + } + + let _ = self + .event_publisher + .send(TransactionEvent::TransactionSendResult(outbound_tx.tx_id, result)) + .await; + }, + } + + Ok(()) + } + + /// Sends a new transaction to a recipient + /// # Arguments + /// 'dest_pubkey': The Comms pubkey of the recipient node + /// 'amount': The amount of Tari to send to the recipient + /// 'fee_per_gram': The amount of fee per transaction gram to be included in transaction + pub async fn send_transaction( + &mut self, + dest_pubkey: CommsPublicKey, + amount: MicroTari, + fee_per_gram: MicroTari, + message: String, + discovery_process_futures: &mut FuturesUnordered< + BoxFuture<'static, Result<(MessageTag, OutboundTransaction), TransactionServiceError>>, + >, + ) -> Result<(), TransactionServiceError> + { + let mut sender_protocol = self + .output_manager_service + .prepare_transaction_to_send(amount, fee_per_gram, None, message.clone()) + .await?; + + if !sender_protocol.is_single_round_message_ready() { + return Err(TransactionServiceError::InvalidStateError); + } + + let msg = sender_protocol.build_single_round_message()?; + let tx_id = msg.tx_id; + let proto_message = proto::TransactionSenderMessage::single(msg.into()); + + match self + .outbound_message_service + .send_direct( + dest_pubkey.clone(), + OutboundEncryption::EncryptForPeer, + OutboundDomainMessage::new(TariMessageType::SenderPartialTransaction, proto_message), + ) + .await? + { + SendMessageResponse::Queued(tags) => match tags.len() { + 0 => error!( + target: LOG_TARGET, + "Queuing Transaction TX_ID: {} for send was unsuccessful and no message was sent", tx_id + ), + 1 => { + info!( + target: LOG_TARGET, + "Transaction (TxId: {}) Send successfully queued for send with Message Tag: {:?}", + tx_id, + tags[0], + ); + + let outbound_tx = OutboundTransaction { + tx_id, + destination_public_key: dest_pubkey.clone(), + amount, + fee: sender_protocol.get_fee_amount()?, + sender_protocol, + message, + timestamp: Utc::now().naive_utc(), + }; + + self.db + .add_pending_outbound_transaction(outbound_tx.tx_id, outbound_tx.clone()) + .await?; + + self.pending_outbound_message_results + .insert(tags[0].clone(), outbound_tx); + }, + _ => error!( + target: LOG_TARGET, + "Send process for TX_ID: {} was unsuccessful due to more than 1 MessageTag being returned", tx_id + ), + }, + SendMessageResponse::Failed => return Err(TransactionServiceError::OutboundSendFailure), + SendMessageResponse::PendingDiscovery(r) => { + // The sending of the message resulted in a long running Discovery process being performed by the Comms + // layer. This can take minutes so we will spawn a task to wait for the result and then act + // appropriately on it + let tx_id_clone = tx_id; + let outbound_tx_clone = OutboundTransaction { + tx_id, + destination_public_key: dest_pubkey.clone(), + amount, + fee: sender_protocol.get_fee_amount()?, + sender_protocol: sender_protocol.clone(), + message: message.clone(), + timestamp: Utc::now().naive_utc(), + }; + let discovery_future = async move { + transaction_send_discovery_process_completion(r, tx_id_clone, outbound_tx_clone).await + }; + discovery_process_futures.push(discovery_future.boxed()); + return Err(TransactionServiceError::OutboundSendDiscoveryInProgress(tx_id)); + }, + } + + info!( + target: LOG_TARGET, + "Transaction with TX_ID = {} queued to be sent to {}", tx_id, dest_pubkey + ); + + Ok(()) + } + + /// Accept the public reply from a recipient and apply the reply to the relevant transaction protocol + /// # Arguments + /// 'recipient_reply' - The public response from a recipient with data required to complete the transaction + pub async fn accept_recipient_reply( + &mut self, + source_pubkey: CommsPublicKey, + recipient_reply: proto::RecipientSignedMessage, + broadcast_timeout_futures: &mut FuturesUnordered>, + ) -> Result<(), TransactionServiceError> + { + let recipient_reply: RecipientSignedMessage = recipient_reply + .try_into() + .map_err(TransactionServiceError::InvalidMessageError)?; + + let mut outbound_tx = self.db.get_pending_outbound_transaction(recipient_reply.tx_id).await?; + + let tx_id = recipient_reply.tx_id; + if !outbound_tx.sender_protocol.check_tx_id(tx_id.clone()) || + !outbound_tx.sender_protocol.is_collecting_single_signature() + { + return Err(TransactionServiceError::InvalidStateError); + } + + outbound_tx + .sender_protocol + .add_single_recipient_info(recipient_reply, &self.factories.range_proof)?; + outbound_tx + .sender_protocol + .finalize(KernelFeatures::empty(), &self.factories)?; + let tx = outbound_tx.sender_protocol.get_transaction()?; + + let completed_transaction = CompletedTransaction { + tx_id, + source_public_key: self.node_identity.public_key().clone(), + destination_public_key: outbound_tx.destination_public_key, + amount: outbound_tx.amount, + fee: outbound_tx.fee, + transaction: tx.clone(), + status: TransactionStatus::Completed, + message: outbound_tx.message.clone(), + timestamp: Utc::now().naive_utc(), + }; + self.db + .complete_outbound_transaction(tx_id.clone(), completed_transaction.clone()) + .await?; + info!( + target: LOG_TARGET, + "Transaction Recipient Reply for TX_ID = {} received", tx_id, + ); + + let finalized_transaction_message = proto::TransactionFinalizedMessage { + tx_id, + transaction: Some(tx.clone().into()), + }; + + self.outbound_message_service + .send_direct( + source_pubkey.clone(), + OutboundEncryption::EncryptForPeer, + OutboundDomainMessage::new(TariMessageType::TransactionFinalized, finalized_transaction_message), + ) + .await?; + + // Logging this error here instead of propogating it up to the select! catchall which generates the Error Event. + let _ = self + .broadcast_completed_transaction_to_mempool(tx_id, broadcast_timeout_futures) + .await + .map_err(|e| { + error!( + target: LOG_TARGET, + "Error broadcasting completed transaction to mempool: {:?}", e + ); + e + }); + + self.event_publisher + .send(TransactionEvent::ReceivedTransactionReply(tx_id)) + .await + .map_err(|_| TransactionServiceError::EventStreamError)?; + + Ok(()) + } + + /// Accept a new transaction from a sender by handling a public SenderMessage. The reply is generated and sent. + /// # Arguments + /// 'source_pubkey' - The pubkey from which the message was sent and to which the reply will be sent. + /// 'sender_message' - Message from a sender containing the setup of the transaction being sent to you + pub async fn accept_transaction( + &mut self, + source_pubkey: CommsPublicKey, + sender_message: proto::TransactionSenderMessage, + ) -> Result<(), TransactionServiceError> + { + let sender_message: TransactionSenderMessage = sender_message + .try_into() + .map_err(TransactionServiceError::InvalidMessageError)?; + + // Currently we will only reply to a Single sender transaction protocol + if let TransactionSenderMessage::Single(data) = sender_message.clone() { + let amount = data.amount; + + let spending_key = self + .output_manager_service + .get_recipient_spending_key(data.tx_id, data.amount) + .await?; + let nonce = PrivateKey::random(&mut OsRng); + + let rtp = ReceiverTransactionProtocol::new( + sender_message, + nonce, + spending_key, + OutputFeatures::default(), + &self.factories, + ); + let recipient_reply = rtp.get_signed_data()?.clone(); + + // Check this is not a repeat message i.e. tx_id doesn't already exist in our pending or completed + // transactions + if self.db.transaction_exists(recipient_reply.tx_id).await? { + return Err(TransactionServiceError::RepeatedMessageError); + } + + let tx_id = recipient_reply.tx_id; + let proto_message: proto::RecipientSignedMessage = recipient_reply.into(); + self.outbound_message_service + .send_direct( + source_pubkey.clone(), + OutboundEncryption::EncryptForPeer, + OutboundDomainMessage::new(TariMessageType::ReceiverPartialTransactionReply, proto_message), + ) + .await?; + + // Otherwise add it to our pending transaction list and return reply + let inbound_transaction = InboundTransaction { + tx_id, + source_public_key: source_pubkey.clone(), + amount, + receiver_protocol: rtp.clone(), + message: data.message, + timestamp: Utc::now().naive_utc(), + }; + self.db + .add_pending_inbound_transaction(tx_id, inbound_transaction.clone()) + .await?; + + info!( + target: LOG_TARGET, + "Transaction with TX_ID = {} received from {}. Reply Sent", tx_id, source_pubkey, + ); + + self.event_publisher + .send(TransactionEvent::ReceivedTransaction(tx_id)) + .await + .map_err(|_| TransactionServiceError::EventStreamError)?; + } + Ok(()) + } + + /// Accept a new transaction from a sender by handling a public SenderMessage. The reply is generated and sent. + /// # Arguments + /// 'source_pubkey' - The pubkey from which the message was sent and to which the reply will be sent. + /// 'sender_message' - Message from a sender containing the setup of the transaction being sent to you + pub async fn accept_finalized_transaction( + &mut self, + source_pubkey: CommsPublicKey, + finalized_transaction: proto::TransactionFinalizedMessage, + broadcast_timeout_futures: &mut FuturesUnordered>, + ) -> Result<(), TransactionServiceError> + { + let tx_id = finalized_transaction.tx_id; + let transaction: Transaction = finalized_transaction + .transaction + .ok_or_else(|| { + TransactionServiceError::InvalidMessageError( + "Finalized Transaction missing Transaction field".to_string(), + ) + })? + .try_into() + .map_err(|_| { + TransactionServiceError::InvalidMessageError( + "Cannot convert Transaction field from TransactionFinalized message".to_string(), + ) + })?; + info!( + target: LOG_TARGET, + "Finalized Transaction with TX_ID = {} received from {}", + tx_id, + source_pubkey.clone() + ); + + let inbound_tx = self + .db + .get_pending_inbound_transaction(tx_id.clone()) + .await + .map_err(|e| { + error!( + target: LOG_TARGET, + "Finalized transaction TxId does not exist in Pending Inbound Transactions" + ); + e + })?; + + if inbound_tx.source_public_key != source_pubkey { + error!( + target: LOG_TARGET, + "Finalized transaction Source Public Key does not correspond to stored value" + ); + return Err(TransactionServiceError::InvalidSourcePublicKey); + } + + let rtp_output = match inbound_tx.receiver_protocol.state { + RecipientState::Finalized(s) => s.output.clone(), + RecipientState::Failed(_) => return Err(TransactionServiceError::InvalidStateError), + }; + + let finalized_outputs = transaction.body.outputs(); + + if finalized_outputs.iter().find(|o| o == &&rtp_output).is_none() { + error!( + target: LOG_TARGET, + "Finalized transaction not contain the Receiver's output" + ); + return Err(TransactionServiceError::ReceiverOutputNotFound); + } + + let completed_transaction = CompletedTransaction { + tx_id, + source_public_key: source_pubkey.clone(), + destination_public_key: self.node_identity.public_key().clone(), + amount: inbound_tx.amount, + fee: transaction.body.get_total_fee(), + transaction: transaction.clone(), + status: TransactionStatus::Completed, + message: inbound_tx.message.clone(), + timestamp: inbound_tx.timestamp, + }; + + self.db + .complete_inbound_transaction(tx_id.clone(), completed_transaction.clone()) + .await?; + + self.event_publisher + .send(TransactionEvent::ReceivedFinalizedTransaction(tx_id)) + .await + .map_err(|_| TransactionServiceError::EventStreamError)?; + + info!( + target: LOG_TARGET, + "Inbound Transaction with TX_ID = {} from {} moved to Completed Transactions", + tx_id, + source_pubkey.clone() + ); + + // Logging this error here instead of propogating it up to the select! catchall which generates the Error Event. + let _ = self + .broadcast_completed_transaction_to_mempool(tx_id, broadcast_timeout_futures) + .await + .map_err(|e| { + error!( + target: LOG_TARGET, + "Error broadcasting completed transaction to mempool: {:?}", e + ); + e + }); + + Ok(()) + } + + /// Request a tx_id and spending_key for a coinbase output to be mined + pub async fn request_coinbase_key( + &mut self, + amount: MicroTari, + maturity_height: u64, + ) -> Result + { + let tx_id: TxId = OsRng.next_u64(); + + let spending_key = self + .output_manager_service + .get_coinbase_spending_key(tx_id.clone(), amount.clone(), maturity_height) + .await?; + + self.db + .add_pending_coinbase_transaction(tx_id.clone(), PendingCoinbaseTransaction { + tx_id, + amount, + commitment: self.factories.commitment.commit_value(&spending_key, u64::from(amount)), + timestamp: Utc::now().naive_utc(), + }) + .await?; + + Ok(PendingCoinbaseSpendingKey { tx_id, spending_key }) + } + + /// Once the miner has constructed the completed Coinbase transaction they will submit it to the Transaction Service + /// which will monitor the chain to see when it has been mined. + pub async fn submit_completed_coinbase_transaction( + &mut self, + tx_id: TxId, + completed_transaction: Transaction, + ) -> Result<(), TransactionServiceError> + { + let coinbase_tx = self + .db + .get_pending_coinbase_transaction(tx_id.clone()) + .await + .map_err(|e| { + error!( + target: LOG_TARGET, + "Finalized coinbase transaction TxId does not exist in Pending Inbound Transactions" + ); + e + })?; + + if !completed_transaction.body.inputs().is_empty() || + completed_transaction.body.outputs().len() != 1 || + completed_transaction.body.kernels().len() != 1 + { + error!( + target: LOG_TARGET, + "Provided Completed Transaction for Coinbase Transaction does not contain just a single output" + ); + return Err(TransactionServiceError::InvalidCompletedTransaction); + } + + if coinbase_tx.commitment != completed_transaction.body.outputs()[0].commitment || + completed_transaction.body.outputs()[0].features.flags != OutputFlags::COINBASE_OUTPUT || + completed_transaction.body.kernels()[0].features != KernelFeatures::COINBASE_KERNEL + { + error!( + target: LOG_TARGET, + "Provided Completed Transaction commitment for Coinbase Transaction does not match the stored \ + commitment for this TxId" + ); + return Err(TransactionServiceError::InvalidCompletedTransaction); + } + + self.db + .complete_coinbase_transaction(tx_id, CompletedTransaction { + tx_id, + source_public_key: self.node_identity.public_key().clone(), + destination_public_key: self.node_identity.public_key().clone(), + amount: coinbase_tx.amount, + fee: MicroTari::from(0), + transaction: completed_transaction, + status: TransactionStatus::Completed, + message: "Coinbase Transaction".to_string(), + timestamp: Utc::now().naive_utc(), + }) + .await?; + + Ok(()) + } + + /// If a specific coinbase transaction will not be mined then the Miner can cancel it + pub async fn cancel_pending_coinbase_transaction(&mut self, tx_id: TxId) -> Result<(), TransactionServiceError> { + let _ = self + .db + .get_pending_coinbase_transaction(tx_id.clone()) + .await + .map_err(|e| { + error!( + target: LOG_TARGET, + "Finalized coinbase transaction TxId does not exist in Pending Inbound Transactions" + ); + e + })?; + + self.output_manager_service.cancel_transaction(tx_id).await?; + + self.db.cancel_coinbase_transaction(tx_id).await?; + + Ok(()) + } + + pub async fn get_pending_inbound_transactions( + &self, + ) -> Result, TransactionServiceError> { + Ok(self.db.get_pending_inbound_transactions().await?) + } + + pub async fn get_pending_outbound_transactions( + &self, + ) -> Result, TransactionServiceError> { + Ok(self.db.get_pending_outbound_transactions().await?) + } + + pub async fn get_completed_transactions( + &self, + ) -> Result, TransactionServiceError> { + Ok(self.db.get_completed_transactions().await?) + } + + /// Add a base node public key to the list that will be used to broadcast transactions and monitor the base chain + /// for the presence of spendable outputs. If this is the first time the base node public key is set do the initial + /// mempool broadcast + async fn set_base_node_public_key( + &mut self, + base_node_public_key: CommsPublicKey, + broadcast_timeout_futures: &mut FuturesUnordered>, + mined_request_timeout_futures: &mut FuturesUnordered>, + ) -> Result<(), TransactionServiceError> + { + let startup_broadcast = self.base_node_public_key.is_none(); + + self.base_node_public_key = Some(base_node_public_key); + + if startup_broadcast { + let _ = self + .broadcast_all_completed_transactions_to_mempool(broadcast_timeout_futures) + .await + .or_else(|resp| { + error!( + target: LOG_TARGET, + "Error broadcasting all completed transactions: {:?}", resp + ); + Err(resp) + }); + + let _ = self + .monitor_all_completed_transactions_for_mining(mined_request_timeout_futures) + .await + .or_else(|resp| { + error!( + target: LOG_TARGET, + "Error querying base_node for all completed transactions: {:?}", resp + ); + Err(resp) + }); + } + Ok(()) + } + + /// Broadcast the specified Completed Transaction to the Base Node. After sending the transaction send a Mempool + /// request to check that the transaction has been received. The final step is to set a timeout future to check on + /// the status of the transaction in the future. + pub async fn broadcast_completed_transaction_to_mempool( + &mut self, + tx_id: TxId, + broadcast_timeout_futures: &mut FuturesUnordered>, + ) -> Result<(), TransactionServiceError> + { + let completed_tx = self.db.get_completed_transaction(tx_id.clone()).await?; + + if completed_tx.status != TransactionStatus::Completed || completed_tx.transaction.body.kernels().is_empty() { + return Err(TransactionServiceError::InvalidCompletedTransaction); + } + + match self.base_node_public_key.clone() { + None => return Err(TransactionServiceError::NoBaseNodeKeysProvided), + Some(pk) => { + // Broadcast Transaction + self.outbound_message_service + .send_direct( + pk.clone(), + OutboundEncryption::EncryptForPeer, + OutboundDomainMessage::new( + TariMessageType::NewTransaction, + TransactionProto::types::Transaction::from(completed_tx.transaction.clone()), + ), + ) + .await?; + + // Send Mempool Request + let tx_excess_sig = completed_tx.transaction.body.kernels()[0].excess_sig.clone(); + let mempool_request = MempoolProto::MempoolServiceRequest { + request_key: completed_tx.tx_id, + request: Some(MempoolProto::mempool_service_request::Request::GetTxStateWithExcessSig( + tx_excess_sig.into(), + )), + }; + self.outbound_message_service + .send_direct( + pk.clone(), + OutboundEncryption::EncryptForPeer, + OutboundDomainMessage::new(TariMessageType::MempoolRequest, mempool_request), + ) + .await?; + // Start Timeout + let state_timeout = StateDelay::new(self.config.mempool_broadcast_timeout.clone(), completed_tx.tx_id); + + broadcast_timeout_futures.push(state_timeout.delay().boxed()); + }, + } + + Ok(()) + } + + /// Go through all completed transactions that have not yet been broadcast and broadcast all of them to the base + /// node followed by mempool requests to confirm that they have been received + async fn broadcast_all_completed_transactions_to_mempool( + &mut self, + broadcast_timeout_futures: &mut FuturesUnordered>, + ) -> Result<(), TransactionServiceError> + { + let completed_txs = self.db.get_completed_transactions().await?; + for completed_tx in completed_txs.values() { + if completed_tx.status == TransactionStatus::Completed { + self.broadcast_completed_transaction_to_mempool(completed_tx.tx_id.clone(), broadcast_timeout_futures) + .await?; + } + } + + Ok(()) + } + + /// Handle the timeout of a pending transaction broadcast request. This will check if the transaction's status has + /// been updated by received MempoolRepsonse during the course of this timeout. If it has not been updated the + /// transaction is broadcast again + pub async fn handle_mempool_broadcast_timeout( + &mut self, + tx_id: TxId, + broadcast_timeout_futures: &mut FuturesUnordered>, + ) -> Result<(), TransactionServiceError> + { + let completed_tx = self.db.get_completed_transaction(tx_id.clone()).await?; + + if completed_tx.status == TransactionStatus::Completed { + info!(target: LOG_TARGET, "Mempool broadcast timed out for TX_ID: {}", tx_id); + + self.broadcast_completed_transaction_to_mempool(tx_id, broadcast_timeout_futures) + .await?; + + self.event_publisher + .send(TransactionEvent::MempoolBroadcastTimedOut(tx_id)) + .await + .map_err(|_| TransactionServiceError::EventStreamError)?; + } + + Ok(()) + } + + /// Handle an incoming mempool response message + pub async fn handle_mempool_response( + &mut self, + response: MempoolProto::MempoolServiceResponse, + mined_request_timeout_futures: &mut FuturesUnordered>, + ) -> Result<(), TransactionServiceError> + { + let response = MempoolServiceResponse::try_from(response).unwrap(); + let tx_id = response.request_key; + match response.response { + MempoolResponse::Stats(_) => Err(TransactionServiceError::InvalidMessageError( + "Mempool Response of invalid type".to_string(), + )), + MempoolResponse::TxStorage(ts) => match ts { + TxStorageResponse::NotStored => { + debug!( + target: LOG_TARGET, + "Mempool response received for TxId: {:?} but requested transaction was not found in mempool", + tx_id + ); + Ok(()) + }, + // Any other variant of this enum means the transaction has been received by the base_node and is in one + // of the various mempools + _ => { + let completed_tx = self.db.get_completed_transaction(response.request_key.clone()).await?; + // If this transaction is still in the Completed State it should be upgraded to the Broadcast state + if completed_tx.status == TransactionStatus::Completed { + self.db.broadcast_completed_transaction(tx_id.clone()).await?; + // Start monitoring the base node to see if this Tx has been mined + self.send_transaction_mined_request(tx_id.clone(), mined_request_timeout_futures) + .await?; + + self.event_publisher + .send(TransactionEvent::TransactionBroadcast(tx_id)) + .await + .map_err(|_| TransactionServiceError::EventStreamError)?; + + info!( + target: LOG_TARGET, + "Completed Transaction with TxId: {} detected as Broadcast to Base Node Mempool", tx_id + ); + } + + Ok(()) + }, + }, + } + } + + /// Send a request to the Base Node to see if the specified transaction has been mined yet. This function will send + /// the request and store a timeout future to check in on the status of the transaction in the future. + async fn send_transaction_mined_request( + &mut self, + tx_id: TxId, + mined_request_timeout_futures: &mut FuturesUnordered>, + ) -> Result<(), TransactionServiceError> + { + let completed_tx = self.db.get_completed_transaction(tx_id.clone()).await?; + + if completed_tx.status != TransactionStatus::Broadcast || completed_tx.transaction.body.kernels().is_empty() { + return Err(TransactionServiceError::InvalidCompletedTransaction); + } + + match self.base_node_public_key.clone() { + None => return Err(TransactionServiceError::NoBaseNodeKeysProvided), + Some(pk) => { + let mut hashes = Vec::new(); + for o in completed_tx.transaction.body.outputs() { + hashes.push(o.hash()); + } + + let request = BaseNodeRequestProto::FetchUtxos(BaseNodeProto::HashOutputs { outputs: hashes }); + let service_request = BaseNodeProto::BaseNodeServiceRequest { + request_key: tx_id, + request: Some(request), + }; + self.outbound_message_service + .send_direct( + pk.clone(), + OutboundEncryption::EncryptForPeer, + OutboundDomainMessage::new(TariMessageType::BaseNodeRequest, service_request), + ) + .await?; + // Start Timeout + let state_timeout = + StateDelay::new(self.config.base_node_mined_timeout.clone(), completed_tx.tx_id.clone()); + + mined_request_timeout_futures.push(state_timeout.delay().boxed()); + }, + } + Ok(()) + } + + /// Handle the timeout of a pending transaction mined? request. This will check if the transaction's status has + /// been updated by received BaseNodeRepsonse during the course of this timeout. If it has not been updated the + /// transaction is broadcast again + pub async fn handle_transaction_mined_request_timeout( + &mut self, + tx_id: TxId, + mined_request_timeout_futures: &mut FuturesUnordered>, + ) -> Result<(), TransactionServiceError> + { + let completed_tx = self.db.get_completed_transaction(tx_id.clone()).await?; + + if completed_tx.status == TransactionStatus::Broadcast { + info!( + target: LOG_TARGET, + "Transaction Mined? request timed out for TX_ID: {}", tx_id + ); + + self.send_transaction_mined_request(tx_id, mined_request_timeout_futures) + .await?; + + self.event_publisher + .send(TransactionEvent::TransactionMinedRequestTimedOut(tx_id)) + .await + .map_err(|_| TransactionServiceError::EventStreamError)?; + } + + Ok(()) + } + + /// Handle an incoming basenode response message + pub async fn handle_base_node_response( + &mut self, + response: BaseNodeProto::BaseNodeServiceResponse, + ) -> Result<(), TransactionServiceError> + { + let tx_id = response.request_key; + let response = match response.response { + Some(BaseNodeResponseProto::TransactionOutputs(outputs)) => Ok(outputs.outputs), + _ => Err(TransactionServiceError::InvalidStateError), + }?; + + let completed_tx = self.db.get_completed_transaction(tx_id.clone()).await?; + // If this transaction is still in the Broadcast State it should be upgraded to the Mined state + if completed_tx.status == TransactionStatus::Broadcast { + // Confirm that all outputs were reported as mined for the transaction + if response.len() != completed_tx.transaction.body.outputs().len() { + error!( + target: LOG_TARGET, + "Base node response received for TxId: {:?} but the response contains a different number of \ + outputs than stored transaction", + tx_id + ); + } else { + let mut check = true; + + for output in response.iter() { + let transaction_output = TransactionOutput::try_from(output.clone()) + .map_err(TransactionServiceError::ConversionError)?; + + check = check && + completed_tx + .transaction + .body + .outputs() + .iter() + .any(|item| item == &transaction_output); + } + // If all outputs are present then mark this transaction as mined. + if check { + self.output_manager_service + .confirm_transaction( + tx_id.clone(), + completed_tx.transaction.body.inputs().clone(), + completed_tx.transaction.body.outputs().clone(), + ) + .await?; + + self.db.mine_completed_transaction(tx_id).await?; + + self.event_publisher + .send(TransactionEvent::TransactionMined(tx_id)) + .await + .map_err(|_| TransactionServiceError::EventStreamError)?; + + info!("Transaction (TxId: {:?}) detected as mined on the Base Layer", tx_id); + } + } + } else { + debug!( + target: LOG_TARGET, + "Base node response received for TxId: {:?} but this transaction is not in the Broadcast state", tx_id + ); + } + + Ok(()) + } + + /// Go through all completed transactions that have been broadcast and start querying the base_node to see if they + /// have been mined + async fn monitor_all_completed_transactions_for_mining( + &mut self, + mined_request_timeout_futures: &mut FuturesUnordered>, + ) -> Result<(), TransactionServiceError> + { + let completed_txs = self.db.get_completed_transactions().await?; + for completed_tx in completed_txs.values() { + if completed_tx.status == TransactionStatus::Broadcast { + self.send_transaction_mined_request(completed_tx.tx_id.clone(), mined_request_timeout_futures) + .await?; + } + } + + Ok(()) + } + + /// Add a completed transaction to the Transaction Manager to record directly importing a spendable UTXO. + pub async fn add_utxo_import_transaction( + &mut self, + value: MicroTari, + source_public_key: CommsPublicKey, + message: String, + ) -> Result + { + let tx_id = OsRng.next_u64(); + self.db + .add_utxo_import_transaction( + tx_id.clone(), + value, + source_public_key, + self.node_identity.public_key().clone(), + message, + ) + .await?; + Ok(tx_id) + } + + /// This function is only available for testing by the client of LibWallet. It simulates a receiver accepting and + /// replying to a Pending Outbound Transaction. This results in that transaction being "completed" and it's status + /// set to `Broadcast` which indicated it is in a base_layer mempool. + #[cfg(feature = "test_harness")] + pub async fn complete_pending_outbound_transaction( + &mut self, + completed_tx: CompletedTransaction, + ) -> Result<(), TransactionServiceError> + { + self.db + .complete_outbound_transaction(completed_tx.tx_id.clone(), completed_tx.clone()) + .await?; + Ok(()) + } + + /// This function is only available for testing by the client of LibWallet. This function will simulate the process + /// when a completed transaction is broadcast in a mempool on the base layer. The function will update the status of + /// the completed transaction. + #[cfg(feature = "test_harness")] + pub async fn broadcast_transaction(&mut self, tx_id: TxId) -> Result<(), TransactionServiceError> { + let completed_txs = self.db.get_completed_transactions().await?; + completed_txs.get(&tx_id.clone()).ok_or_else(|| { + TransactionServiceError::TestHarnessError("Could not find Completed TX to broadcast.".to_string()) + })?; + + self.db.broadcast_completed_transaction(tx_id).await?; + + self.event_publisher + .send(TransactionEvent::TransactionBroadcast(tx_id)) + .await + .map_err(|_| TransactionServiceError::EventStreamError)?; + + Ok(()) + } + + /// This function is only available for testing by the client of LibWallet. This function will simulate the process + /// when a completed transaction is detected as mined on the base layer. The function will update the status of the + /// completed transaction AND complete the transaction on the Output Manager Service which will update the status of + /// the outputs + #[cfg(feature = "test_harness")] + pub async fn mine_transaction(&mut self, tx_id: TxId) -> Result<(), TransactionServiceError> { + let completed_txs = self.db.get_completed_transactions().await?; + let _found_tx = completed_txs.get(&tx_id.clone()).ok_or_else(|| { + TransactionServiceError::TestHarnessError("Could not find Completed TX to mine.".to_string()) + })?; + + let pending_tx_outputs = self.output_manager_service.get_pending_transactions().await?; + let pending_tx = pending_tx_outputs.get(&tx_id.clone()).ok_or_else(|| { + TransactionServiceError::TestHarnessError("Could not find Pending TX to complete.".to_string()) + })?; + + self.output_manager_service + .confirm_transaction( + tx_id.clone(), + pending_tx + .outputs_to_be_spent + .iter() + .map(|o| { + o.as_transaction_input(&self.factories.commitment, OutputFeatures::default()) + .clone() + }) + .collect(), + pending_tx + .outputs_to_be_received + .iter() + .map(|o| { + o.as_transaction_output(&self.factories) + .expect("Failed to convert to Transaction Output") + .clone() + }) + .collect(), + ) + .await?; + + self.db.mine_completed_transaction(tx_id).await?; + + self.event_publisher + .send(TransactionEvent::TransactionMined(tx_id)) + .await + .map_err(|_| TransactionServiceError::EventStreamError)?; + + Ok(()) + } + + /// This function is only available for testing by the client of LibWallet. This function simulates an external + /// wallet sending a transaction to this wallet which will become a PendingInboundTransaction + #[cfg(feature = "test_harness")] + pub async fn receive_test_transaction( + &mut self, + tx_id: TxId, + amount: MicroTari, + source_public_key: CommsPublicKey, + ) -> Result<(), TransactionServiceError> + { + use crate::output_manager_service::{ + config::OutputManagerServiceConfig, + service::OutputManagerService, + storage::{database::OutputManagerDatabase, memory_db::OutputManagerMemoryDatabase}, + }; + use futures::{channel::mpsc, stream}; + use tari_broadcast_channel::bounded; + + let (_sender, receiver) = reply_channel::unbounded(); + let (tx, _rx) = mpsc::channel(20); + let (oms_event_publisher, _oms_event_subscriber) = bounded(100); + + let mut fake_oms = OutputManagerService::new( + OutputManagerServiceConfig::default(), + OutboundMessageRequester::new(tx), + receiver, + stream::empty(), + OutputManagerDatabase::new(OutputManagerMemoryDatabase::new()), + oms_event_publisher, + self.factories.clone(), + ) + .await?; + + use crate::testnet_utils::make_input; + let (_ti, uo) = make_input(&mut OsRng, amount + 1 * T, &self.factories); + + fake_oms.add_output(uo).await?; + + let mut stp = fake_oms + .prepare_transaction_to_send(amount, MicroTari::from(100), None, "".to_string()) + .await?; + + let msg = stp.build_single_round_message()?; + let proto_msg = proto::TransactionSenderMessage::single(msg.into()); + let sender_message: TransactionSenderMessage = proto_msg + .try_into() + .map_err(TransactionServiceError::InvalidMessageError)?; + + let spending_key = self + .output_manager_service + .get_recipient_spending_key(tx_id.clone(), amount.clone()) + .await?; + let nonce = PrivateKey::random(&mut OsRng); + let rtp = ReceiverTransactionProtocol::new( + sender_message, + nonce, + spending_key.clone(), + OutputFeatures::default(), + &self.factories, + ); + + let inbound_transaction = InboundTransaction { + tx_id, + source_public_key, + amount, + receiver_protocol: rtp, + message: "".to_string(), + timestamp: Utc::now().naive_utc(), + }; + + self.db + .add_pending_inbound_transaction(tx_id.clone(), inbound_transaction.clone()) + .await?; + + self.event_publisher + .send(TransactionEvent::ReceivedTransaction(tx_id)) + .await + .map_err(|_| TransactionServiceError::EventStreamError)?; + + Ok(()) + } + + /// This function is only available for testing by the client of LibWallet. This function simulates an external + /// wallet sending a transaction to this wallet which will become a PendingInboundTransaction + #[cfg(feature = "test_harness")] + pub async fn finalize_received_test_transaction(&mut self, tx_id: TxId) -> Result<(), TransactionServiceError> { + let inbound_txs = self.db.get_pending_inbound_transactions().await?; + + let found_tx = inbound_txs.get(&tx_id.clone()).ok_or_else(|| { + TransactionServiceError::TestHarnessError("Could not find Pending Inbound TX to finalize.".to_string()) + })?; + + let completed_transaction = CompletedTransaction { + tx_id, + source_public_key: found_tx.source_public_key.clone(), + destination_public_key: self.node_identity.public_key().clone(), + amount: found_tx.amount, + fee: MicroTari::from(2000), // a placeholder fee for this test function + transaction: Transaction::new(Vec::new(), Vec::new(), Vec::new(), BlindingFactor::default()), + status: TransactionStatus::Completed, + message: found_tx.message.clone(), + timestamp: found_tx.timestamp, + }; + + self.db + .complete_inbound_transaction(tx_id.clone(), completed_transaction.clone()) + .await?; + self.event_publisher + .send(TransactionEvent::ReceivedFinalizedTransaction(tx_id)) + .await + .map_err(|_| TransactionServiceError::EventStreamError)?; + Ok(()) + } +} + +// Asynchronous Tasks + +async fn transaction_send_discovery_process_completion( + response_channel: oneshot::Receiver, + tx_id: TxId, + outbound_tx: OutboundTransaction, +) -> Result<(MessageTag, OutboundTransaction), TransactionServiceError> +{ + let mut message_tag: Option = None; + match response_channel.await { + Ok(response) => match response { + SendMessageResponse::Queued(tags) => match tags.len() { + 0 => error!( + target: LOG_TARGET, + "Send Discovery process for TX_ID: {} was unsuccessful and no message was sent", tx_id + ), + 1 => { + message_tag = Some(tags[0].clone()); + + info!( + target: LOG_TARGET, + "Transaction (TxId: {}) Send Discovery process successful with Message Tag: {:?}", + tx_id, + message_tag, + ); + }, + _ => error!( + target: LOG_TARGET, + "Send Discovery process for TX_ID: {} was unsuccessful due to more than 1 MessageTag being \ + returned", + tx_id + ), + }, + _ => { + error!( + target: LOG_TARGET, + "Transaction (TxId: {}) Send Discovery process failed", tx_id + ); + }, + }, + Err(_) => { + error!( + target: LOG_TARGET, + "Transaction (TxId: {}) Send Response One-shot channel dropped", tx_id + ); + }, + } + + return if let Some(mt) = message_tag { + let updated_outbound_tx = OutboundTransaction { + timestamp: Utc::now().naive_utc(), + ..outbound_tx.clone() + }; + Ok((mt, updated_outbound_tx)) + } else { + Err(TransactionServiceError::DiscoveryProcessFailed(tx_id)) + }; +} diff --git a/base_layer/wallet/src/transaction_service/storage/database.rs b/base_layer/wallet/src/transaction_service/storage/database.rs new file mode 100644 index 0000000000..1a8ba6e0a5 --- /dev/null +++ b/base_layer/wallet/src/transaction_service/storage/database.rs @@ -0,0 +1,590 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{output_manager_service::TxId, transaction_service::error::TransactionStorageError}; +use chrono::{NaiveDateTime, Utc}; +use log::*; +use serde::{Deserialize, Serialize}; +use std::{ + collections::HashMap, + convert::TryFrom, + fmt::{Display, Error, Formatter}, + sync::Arc, +}; +use tari_comms::types::CommsPublicKey; +use tari_core::transactions::{ + tari_amount::{uT, MicroTari}, + transaction::Transaction, + types::{BlindingFactor, Commitment}, + ReceiverTransactionProtocol, + SenderTransactionProtocol, +}; + +const LOG_TARGET: &str = "wallet::transaction_service::database"; + +/// This trait defines the required behaviour that a storage backend must provide for the Transactionservice. +/// Data is passed to and from the backend via the [DbKey], [DbValue], and [DbValueKey] enums. If new data types are +/// required to be supported by the backends then these enums can be updated to reflect this requirement and the trait +/// will remain the same +pub trait TransactionBackend: Send + Sync { + /// Retrieve the record associated with the provided DbKey + fn fetch(&self, key: &DbKey) -> Result, TransactionStorageError>; + /// Check if a record with the provided key exists in the backend. + fn contains(&self, key: &DbKey) -> Result; + /// Modify the state the of the backend with a write operation + fn write(&self, op: WriteOperation) -> Result, TransactionStorageError>; + /// Check if a transaction exists in any of the collections + fn transaction_exists(&self, tx_id: TxId) -> Result; + /// Complete outbound transaction, this operation must delete the `OutboundTransaction` with the provided + /// `TxId` and insert the provided `CompletedTransaction` into `CompletedTransactions`. + fn complete_outbound_transaction( + &self, + tx_id: TxId, + completed_transaction: CompletedTransaction, + ) -> Result<(), TransactionStorageError>; + /// Complete inbound transaction, this operation must delete the `InboundTransaction` with the provided + /// `TxId` and insert the provided `CompletedTransaction` into `CompletedTransactions`. + fn complete_inbound_transaction( + &self, + tx_id: TxId, + completed_transaction: CompletedTransaction, + ) -> Result<(), TransactionStorageError>; + /// Complete pending coinbase transaction, this operation must delete the `PendingCoinbaseTransaction` with the + /// provided `TxId` and insert the provided `CompletedTransaction` into `CompletedTransactions`. + fn complete_coinbase_transaction( + &self, + tx_id: TxId, + completed_transaction: CompletedTransaction, + ) -> Result<(), TransactionStorageError>; + /// Indicated that a completed transaction has been broadcast to the mempools + fn broadcast_completed_transaction(&self, tx_id: TxId) -> Result<(), TransactionStorageError>; + /// Indicated that a completed transaction has been detected as mined on the base layer + fn mine_completed_transaction(&self, tx_id: TxId) -> Result<(), TransactionStorageError>; + /// Update a completed transactions timestamp for use in test data generation + #[cfg(feature = "test_harness")] + fn update_completed_transaction_timestamp( + &self, + tx_id: TxId, + timestamp: NaiveDateTime, + ) -> Result<(), TransactionStorageError>; +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub enum TransactionStatus { + /// This transaction has been completed between the parties but has not been broadcast to the base layer network. + Completed, + /// This transaction has been broadcast to the base layer network and is currently in one or more base node + /// mempools. + Broadcast, + /// This transaction has been mined and included in a block. + Mined, + /// This transaction was generated as part of importing a spendable UTXO + Imported, +} + +impl TryFrom for TransactionStatus { + type Error = TransactionStorageError; + + fn try_from(value: i32) -> Result { + match value { + 0 => Ok(TransactionStatus::Completed), + 1 => Ok(TransactionStatus::Broadcast), + 2 => Ok(TransactionStatus::Mined), + 3 => Ok(TransactionStatus::Imported), + _ => Err(TransactionStorageError::ConversionError), + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct InboundTransaction { + pub tx_id: TxId, + pub source_public_key: CommsPublicKey, + pub amount: MicroTari, + pub receiver_protocol: ReceiverTransactionProtocol, + pub message: String, + pub timestamp: NaiveDateTime, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct OutboundTransaction { + pub tx_id: TxId, + pub destination_public_key: CommsPublicKey, + pub amount: MicroTari, + pub fee: MicroTari, + pub sender_protocol: SenderTransactionProtocol, + pub message: String, + pub timestamp: NaiveDateTime, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct PendingCoinbaseTransaction { + pub tx_id: TxId, + pub amount: MicroTari, + pub commitment: Commitment, + pub timestamp: NaiveDateTime, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct CompletedTransaction { + pub tx_id: TxId, + pub source_public_key: CommsPublicKey, + pub destination_public_key: CommsPublicKey, + pub amount: MicroTari, + pub fee: MicroTari, + pub transaction: Transaction, + pub status: TransactionStatus, + pub message: String, + pub timestamp: NaiveDateTime, +} + +#[derive(Debug, Clone, PartialEq)] +pub enum DbKey { + PendingOutboundTransaction(TxId), + PendingInboundTransaction(TxId), + CompletedTransaction(TxId), + PendingCoinbaseTransaction(TxId), + PendingOutboundTransactions, + PendingInboundTransactions, + PendingCoinbaseTransactions, + CompletedTransactions, +} + +#[derive(Debug)] +pub enum DbValue { + PendingOutboundTransaction(Box), + PendingInboundTransaction(Box), + PendingCoinbaseTransaction(Box), + CompletedTransaction(Box), + PendingOutboundTransactions(HashMap), + PendingInboundTransactions(HashMap), + PendingCoinbaseTransactions(HashMap), + CompletedTransactions(HashMap), +} + +pub enum DbKeyValuePair { + PendingOutboundTransaction(TxId, Box), + PendingInboundTransaction(TxId, Box), + PendingCoinbaseTransaction(TxId, Box), + CompletedTransaction(TxId, Box), +} + +pub enum WriteOperation { + Insert(DbKeyValuePair), + Remove(DbKey), +} + +// Private macro that pulls out all the boiler plate of extracting a DB query result from its variants +macro_rules! fetch { + ($db:ident, $key_val:expr, $key_var:ident) => {{ + let key = DbKey::$key_var($key_val); + match $db.fetch(&key) { + Ok(None) => Err(TransactionStorageError::ValueNotFound(key)), + Ok(Some(DbValue::$key_var(k))) => Ok(*k), + Ok(Some(other)) => unexpected_result(key, other), + Err(e) => log_error(key, e), + } + }}; +} + +/// This structure holds an inner type that implements the `TransactionBackend` trait and contains the more complex +/// data access logic required by the module built onto the functionality defined by the trait +#[derive(Clone)] +pub struct TransactionDatabase +where T: TransactionBackend + 'static +{ + db: Arc, +} + +impl TransactionDatabase +where T: TransactionBackend + 'static +{ + pub fn new(db: T) -> Self { + Self { db: Arc::new(db) } + } + + pub async fn add_pending_inbound_transaction( + &self, + tx_id: TxId, + inbound_tx: InboundTransaction, + ) -> Result<(), TransactionStorageError> + { + let db_clone = self.db.clone(); + tokio::task::spawn_blocking(move || { + db_clone.write(WriteOperation::Insert(DbKeyValuePair::PendingInboundTransaction( + tx_id, + Box::new(inbound_tx), + ))) + }) + .await + .or_else(|err| Err(TransactionStorageError::BlockingTaskSpawnError(err.to_string())))??; + + Ok(()) + } + + pub async fn add_pending_outbound_transaction( + &self, + tx_id: TxId, + outbound_tx: OutboundTransaction, + ) -> Result<(), TransactionStorageError> + { + let db_clone = self.db.clone(); + tokio::task::spawn_blocking(move || { + db_clone.write(WriteOperation::Insert(DbKeyValuePair::PendingOutboundTransaction( + tx_id, + Box::new(outbound_tx), + ))) + }) + .await + .or_else(|err| Err(TransactionStorageError::BlockingTaskSpawnError(err.to_string())))??; + Ok(()) + } + + pub async fn remove_pending_outbound_transaction(&self, tx_id: TxId) -> Result<(), TransactionStorageError> { + let db_clone = self.db.clone(); + tokio::task::spawn_blocking(move || { + db_clone.write(WriteOperation::Remove(DbKey::PendingOutboundTransaction(tx_id))) + }) + .await + .or_else(|err| Err(TransactionStorageError::BlockingTaskSpawnError(err.to_string())))??; + Ok(()) + } + + pub async fn add_pending_coinbase_transaction( + &self, + tx_id: TxId, + coinbase_tx: PendingCoinbaseTransaction, + ) -> Result<(), TransactionStorageError> + { + let db_clone = self.db.clone(); + tokio::task::spawn_blocking(move || { + db_clone.write(WriteOperation::Insert(DbKeyValuePair::PendingCoinbaseTransaction( + tx_id, + Box::new(coinbase_tx), + ))) + }) + .await + .or_else(|err| Err(TransactionStorageError::BlockingTaskSpawnError(err.to_string())))??; + Ok(()) + } + + /// Check if a transaction with the specified TxId exists in any of the collections + pub async fn transaction_exists(&self, tx_id: TxId) -> Result { + let db_clone = self.db.clone(); + let tx_id_clone = tx_id; + tokio::task::spawn_blocking(move || db_clone.transaction_exists(tx_id_clone)) + .await + .or_else(|err| Err(TransactionStorageError::BlockingTaskSpawnError(err.to_string()))) + .and_then(|inner_result| inner_result) + } + + pub async fn get_pending_outbound_transaction( + &self, + tx_id: TxId, + ) -> Result + { + let db_clone = self.db.clone(); + let result = tokio::task::spawn_blocking(move || fetch!(db_clone, tx_id, PendingOutboundTransaction)) + .await + .or_else(|err| Err(TransactionStorageError::BlockingTaskSpawnError(err.to_string())))??; + Ok(result) + } + + pub async fn get_pending_inbound_transaction( + &self, + tx_id: TxId, + ) -> Result + { + let db_clone = self.db.clone(); + + let result = tokio::task::spawn_blocking(move || fetch!(db_clone, tx_id, PendingInboundTransaction)) + .await + .or_else(|err| Err(TransactionStorageError::BlockingTaskSpawnError(err.to_string())))??; + + Ok(result) + } + + pub async fn get_pending_coinbase_transaction( + &self, + tx_id: TxId, + ) -> Result + { + let db_clone = self.db.clone(); + + let result = tokio::task::spawn_blocking(move || fetch!(db_clone, tx_id, PendingCoinbaseTransaction)) + .await + .or_else(|err| Err(TransactionStorageError::BlockingTaskSpawnError(err.to_string())))??; + + Ok(result) + } + + pub async fn get_completed_transaction( + &self, + tx_id: TxId, + ) -> Result + { + let db_clone = self.db.clone(); + + let result = tokio::task::spawn_blocking(move || fetch!(db_clone, tx_id, CompletedTransaction)) + .await + .or_else(|err| Err(TransactionStorageError::BlockingTaskSpawnError(err.to_string())))??; + Ok(result) + } + + pub async fn get_pending_inbound_transactions( + &self, + ) -> Result, TransactionStorageError> { + let db_clone = self.db.clone(); + + let t = tokio::task::spawn_blocking(move || match db_clone.fetch(&DbKey::PendingInboundTransactions) { + Ok(None) => log_error( + DbKey::PendingInboundTransactions, + TransactionStorageError::UnexpectedResult( + "Could not retrieve pending inbound transactions".to_string(), + ), + ), + Ok(Some(DbValue::PendingInboundTransactions(pt))) => Ok(pt), + Ok(Some(other)) => unexpected_result(DbKey::PendingInboundTransactions, other), + Err(e) => log_error(DbKey::PendingInboundTransactions, e), + }) + .await + .or_else(|err| Err(TransactionStorageError::BlockingTaskSpawnError(err.to_string())))??; + Ok(t) + } + + pub async fn get_pending_outbound_transactions( + &self, + ) -> Result, TransactionStorageError> { + let db_clone = self.db.clone(); + + let t = tokio::task::spawn_blocking(move || match db_clone.fetch(&DbKey::PendingOutboundTransactions) { + Ok(None) => log_error( + DbKey::PendingOutboundTransactions, + TransactionStorageError::UnexpectedResult( + "Could not retrieve pending outbound transactions".to_string(), + ), + ), + Ok(Some(DbValue::PendingOutboundTransactions(pt))) => Ok(pt), + Ok(Some(other)) => unexpected_result(DbKey::PendingOutboundTransactions, other), + Err(e) => log_error(DbKey::PendingOutboundTransactions, e), + }) + .await + .or_else(|err| Err(TransactionStorageError::BlockingTaskSpawnError(err.to_string())))??; + Ok(t) + } + + pub async fn get_pending_coinbase_transactions( + &self, + ) -> Result, TransactionStorageError> { + let db_clone = self.db.clone(); + + let t = tokio::task::spawn_blocking(move || match db_clone.fetch(&DbKey::PendingCoinbaseTransactions) { + Ok(None) => log_error( + DbKey::PendingCoinbaseTransactions, + TransactionStorageError::UnexpectedResult( + "Could not retrieve pending coinbase transactions".to_string(), + ), + ), + Ok(Some(DbValue::PendingCoinbaseTransactions(pt))) => Ok(pt), + Ok(Some(other)) => unexpected_result(DbKey::PendingCoinbaseTransactions, other), + Err(e) => log_error(DbKey::PendingCoinbaseTransactions, e), + }) + .await + .or_else(|err| Err(TransactionStorageError::BlockingTaskSpawnError(err.to_string())))??; + Ok(t) + } + + pub async fn get_completed_transactions( + &self, + ) -> Result, TransactionStorageError> { + let db_clone = self.db.clone(); + + let t = tokio::task::spawn_blocking(move || match db_clone.fetch(&DbKey::CompletedTransactions) { + Ok(None) => log_error( + DbKey::CompletedTransactions, + TransactionStorageError::UnexpectedResult("Could not retrieve completed transactions".to_string()), + ), + Ok(Some(DbValue::CompletedTransactions(pt))) => Ok(pt), + Ok(Some(other)) => unexpected_result(DbKey::CompletedTransactions, other), + Err(e) => log_error(DbKey::CompletedTransactions, e), + }) + .await + .or_else(|err| Err(TransactionStorageError::BlockingTaskSpawnError(err.to_string())))??; + Ok(t) + } + + /// This method moves a `PendingOutboundTransaction` to the `CompleteTransaction` collection. + pub async fn complete_outbound_transaction( + &self, + tx_id: TxId, + transaction: CompletedTransaction, + ) -> Result<(), TransactionStorageError> + { + let db_clone = self.db.clone(); + + tokio::task::spawn_blocking(move || db_clone.complete_outbound_transaction(tx_id, transaction)) + .await + .or_else(|err| Err(TransactionStorageError::BlockingTaskSpawnError(err.to_string()))) + .and_then(|inner_result| inner_result) + } + + /// This method moves a `PendingInboundTransaction` to the `CompleteTransaction` collection. + pub async fn complete_inbound_transaction( + &self, + tx_id: TxId, + transaction: CompletedTransaction, + ) -> Result<(), TransactionStorageError> + { + let db_clone = self.db.clone(); + + tokio::task::spawn_blocking(move || db_clone.complete_inbound_transaction(tx_id, transaction)) + .await + .or_else(|err| Err(TransactionStorageError::BlockingTaskSpawnError(err.to_string()))) + .and_then(|inner_result| inner_result) + } + + /// This method moves a `PendingCoinbaseTransaction` to the `CompleteTransaction` collection. + pub async fn complete_coinbase_transaction( + &self, + tx_id: TxId, + transaction: CompletedTransaction, + ) -> Result<(), TransactionStorageError> + { + let db_clone = self.db.clone(); + + tokio::task::spawn_blocking(move || db_clone.complete_coinbase_transaction(tx_id, transaction)) + .await + .or_else(|err| Err(TransactionStorageError::BlockingTaskSpawnError(err.to_string()))) + .and_then(|inner_result| inner_result) + } + + pub async fn cancel_coinbase_transaction(&mut self, tx_id: TxId) -> Result<(), TransactionStorageError> { + let db_clone = self.db.clone(); + + tokio::task::spawn_blocking(move || { + db_clone.write(WriteOperation::Remove(DbKey::PendingCoinbaseTransaction(tx_id))) + }) + .await + .or_else(|err| Err(TransactionStorageError::BlockingTaskSpawnError(err.to_string())))??; + Ok(()) + } + + /// Indicated that the specified completed transaction has been broadcast into the mempool + pub async fn broadcast_completed_transaction(&mut self, tx_id: TxId) -> Result<(), TransactionStorageError> { + let db_clone = self.db.clone(); + + tokio::task::spawn_blocking(move || db_clone.broadcast_completed_transaction(tx_id)) + .await + .or_else(|err| Err(TransactionStorageError::BlockingTaskSpawnError(err.to_string()))) + .and_then(|inner_result| inner_result) + } + + /// Indicated that the specified completed transaction has been detected as mined on the base layer + pub async fn mine_completed_transaction(&mut self, tx_id: TxId) -> Result<(), TransactionStorageError> { + let db_clone = self.db.clone(); + + tokio::task::spawn_blocking(move || db_clone.mine_completed_transaction(tx_id)) + .await + .or_else(|err| Err(TransactionStorageError::BlockingTaskSpawnError(err.to_string()))) + .and_then(|inner_result| inner_result) + } + + #[allow(clippy::erasing_op)] // this is for 0 * uT + pub async fn add_utxo_import_transaction( + &mut self, + tx_id: TxId, + amount: MicroTari, + source_public_key: CommsPublicKey, + comms_public_key: CommsPublicKey, + message: String, + ) -> Result<(), TransactionStorageError> + { + let transaction = CompletedTransaction { + tx_id, + source_public_key: source_public_key.clone(), + destination_public_key: comms_public_key.clone(), + amount, + fee: 0 * uT, + transaction: Transaction::new(Vec::new(), Vec::new(), Vec::new(), BlindingFactor::default()), + status: TransactionStatus::Imported, + message, + timestamp: Utc::now().naive_utc(), + }; + + let db_clone = self.db.clone(); + tokio::task::spawn_blocking(move || { + db_clone.write(WriteOperation::Insert(DbKeyValuePair::CompletedTransaction( + tx_id, + Box::new(transaction), + ))) + }) + .await + .or_else(|err| Err(TransactionStorageError::BlockingTaskSpawnError(err.to_string())))??; + Ok(()) + } +} + +impl Display for DbKey { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + match self { + DbKey::PendingOutboundTransaction(_) => f.write_str(&"Pending Outbound Transaction".to_string()), + DbKey::PendingInboundTransaction(_) => f.write_str(&"Pending Inbound Transaction".to_string()), + DbKey::PendingCoinbaseTransaction(_) => f.write_str(&"Pending Pending Coinbase Transaction".to_string()), + DbKey::CompletedTransaction(_) => f.write_str(&"Completed Transaction".to_string()), + DbKey::PendingOutboundTransactions => f.write_str(&"All Pending Outbound Transactions".to_string()), + DbKey::PendingInboundTransactions => f.write_str(&"All Pending Inbound Transactions".to_string()), + DbKey::CompletedTransactions => f.write_str(&"All Complete Transactions".to_string()), + DbKey::PendingCoinbaseTransactions => f.write_str(&"All Pending Coinbase Transactions".to_string()), + } + } +} + +impl Display for DbValue { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + match self { + DbValue::PendingOutboundTransaction(_) => f.write_str(&"Pending Outbound Transaction".to_string()), + DbValue::PendingInboundTransaction(_) => f.write_str(&"Pending Inbound Transaction".to_string()), + DbValue::PendingCoinbaseTransaction(_) => f.write_str(&"Pending Coinbase Transaction".to_string()), + DbValue::CompletedTransaction(_) => f.write_str(&"Completed Transaction".to_string()), + DbValue::PendingOutboundTransactions(_) => f.write_str(&"All Pending Outbound Transactions".to_string()), + DbValue::PendingInboundTransactions(_) => f.write_str(&"All Pending Inbound Transactions".to_string()), + DbValue::CompletedTransactions(_) => f.write_str(&"All Complete Transactions".to_string()), + DbValue::PendingCoinbaseTransactions(_) => f.write_str(&"All Pending Coinbase Transactions".to_string()), + } + } +} + +fn log_error(req: DbKey, err: TransactionStorageError) -> Result { + error!( + target: LOG_TARGET, + "Database access error on request: {}: {}", + req, + err.to_string() + ); + Err(err) +} + +fn unexpected_result(req: DbKey, res: DbValue) -> Result { + let msg = format!("Unexpected result for database query {}. Response: {}", req, res); + error!(target: LOG_TARGET, "{}", msg); + Err(TransactionStorageError::UnexpectedResult(msg)) +} diff --git a/base_layer/wallet/src/transaction_service/storage/memory_db.rs b/base_layer/wallet/src/transaction_service/storage/memory_db.rs new file mode 100644 index 0000000000..ce29886845 --- /dev/null +++ b/base_layer/wallet/src/transaction_service/storage/memory_db.rs @@ -0,0 +1,319 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + output_manager_service::TxId, + transaction_service::{ + error::TransactionStorageError, + storage::database::{ + CompletedTransaction, + DbKey, + DbKeyValuePair, + DbValue, + InboundTransaction, + OutboundTransaction, + PendingCoinbaseTransaction, + TransactionBackend, + TransactionStatus, + WriteOperation, + }, + }, +}; +#[cfg(feature = "test_harness")] +use chrono::NaiveDateTime; +use std::{ + collections::HashMap, + sync::{Arc, RwLock}, +}; + +#[derive(Default)] +struct InnerDatabase { + pending_outbound_transactions: HashMap, + pending_inbound_transactions: HashMap, + pending_coinbase_transactions: HashMap, + completed_transactions: HashMap, +} + +impl InnerDatabase { + pub fn new() -> Self { + Self { + pending_outbound_transactions: HashMap::new(), + pending_inbound_transactions: HashMap::new(), + pending_coinbase_transactions: HashMap::new(), + completed_transactions: HashMap::new(), + } + } +} + +#[derive(Clone, Default)] +pub struct TransactionMemoryDatabase { + db: Arc>, +} + +impl TransactionMemoryDatabase { + pub fn new() -> Self { + Self { + db: Arc::new(RwLock::new(InnerDatabase::new())), + } + } +} + +impl TransactionBackend for TransactionMemoryDatabase { + fn fetch(&self, key: &DbKey) -> Result, TransactionStorageError> { + let db = acquire_read_lock!(self.db); + let result = match key { + DbKey::PendingOutboundTransaction(t) => db + .pending_outbound_transactions + .get(t) + .map(|v| DbValue::PendingOutboundTransaction(Box::new(v.clone()))), + DbKey::PendingInboundTransaction(t) => db + .pending_inbound_transactions + .get(t) + .map(|v| DbValue::PendingInboundTransaction(Box::new(v.clone()))), + DbKey::CompletedTransaction(t) => db + .completed_transactions + .get(t) + .map(|v| DbValue::CompletedTransaction(Box::new(v.clone()))), + DbKey::PendingCoinbaseTransaction(t) => db + .pending_coinbase_transactions + .get(t) + .map(|v| DbValue::PendingCoinbaseTransaction(Box::new(v.clone()))), + DbKey::PendingOutboundTransactions => Some(DbValue::PendingOutboundTransactions( + db.pending_outbound_transactions.clone(), + )), + DbKey::PendingInboundTransactions => Some(DbValue::PendingInboundTransactions( + db.pending_inbound_transactions.clone(), + )), + DbKey::PendingCoinbaseTransactions => Some(DbValue::PendingCoinbaseTransactions( + db.pending_coinbase_transactions.clone(), + )), + DbKey::CompletedTransactions => Some(DbValue::CompletedTransactions(db.completed_transactions.clone())), + }; + + Ok(result) + } + + fn contains(&self, key: &DbKey) -> Result { + let db = acquire_read_lock!(self.db); + let result = match key { + DbKey::PendingOutboundTransaction(k) => db.pending_outbound_transactions.contains_key(k), + DbKey::PendingInboundTransaction(k) => db.pending_inbound_transactions.contains_key(k), + DbKey::CompletedTransaction(k) => db.completed_transactions.contains_key(k), + DbKey::PendingCoinbaseTransaction(k) => db.pending_coinbase_transactions.contains_key(k), + DbKey::PendingOutboundTransactions => false, + DbKey::PendingInboundTransactions => false, + DbKey::CompletedTransactions => false, + DbKey::PendingCoinbaseTransactions => false, + }; + + Ok(result) + } + + fn write(&self, op: WriteOperation) -> Result, TransactionStorageError> { + let mut db = acquire_write_lock!(self.db); + match op { + WriteOperation::Insert(kvp) => match kvp { + DbKeyValuePair::PendingOutboundTransaction(k, v) => { + if db.pending_outbound_transactions.contains_key(&k) { + return Err(TransactionStorageError::DuplicateOutput); + } + db.pending_outbound_transactions.insert(k, *v); + }, + DbKeyValuePair::PendingInboundTransaction(k, v) => { + if db.pending_inbound_transactions.contains_key(&k) { + return Err(TransactionStorageError::DuplicateOutput); + } + db.pending_inbound_transactions.insert(k, *v); + }, + DbKeyValuePair::PendingCoinbaseTransaction(k, v) => { + if db.pending_coinbase_transactions.contains_key(&k) { + return Err(TransactionStorageError::DuplicateOutput); + } + db.pending_coinbase_transactions.insert(k, *v); + }, + + DbKeyValuePair::CompletedTransaction(k, v) => { + if db.completed_transactions.contains_key(&k) { + return Err(TransactionStorageError::DuplicateOutput); + } + db.completed_transactions.insert(k, *v); + }, + }, + WriteOperation::Remove(k) => match k { + DbKey::PendingOutboundTransaction(k) => { + if let Some(p) = db.pending_outbound_transactions.remove(&k) { + return Ok(Some(DbValue::PendingOutboundTransaction(Box::new(p)))); + } else { + return Err(TransactionStorageError::ValueNotFound( + DbKey::PendingOutboundTransaction(k), + )); + } + }, + DbKey::PendingInboundTransaction(k) => { + if let Some(p) = db.pending_inbound_transactions.remove(&k) { + return Ok(Some(DbValue::PendingInboundTransaction(Box::new(p)))); + } else { + return Err(TransactionStorageError::ValueNotFound( + DbKey::PendingInboundTransaction(k), + )); + } + }, + DbKey::PendingCoinbaseTransaction(k) => { + if let Some(p) = db.pending_coinbase_transactions.remove(&k) { + return Ok(Some(DbValue::PendingCoinbaseTransaction(Box::new(p)))); + } else { + return Err(TransactionStorageError::ValueNotFound( + DbKey::PendingCoinbaseTransaction(k), + )); + } + }, + DbKey::CompletedTransaction(k) => { + if let Some(p) = db.completed_transactions.remove(&k) { + return Ok(Some(DbValue::CompletedTransaction(Box::new(p)))); + } else { + return Err(TransactionStorageError::ValueNotFound(DbKey::CompletedTransaction(k))); + } + }, + DbKey::PendingInboundTransactions => return Err(TransactionStorageError::OperationNotSupported), + DbKey::PendingOutboundTransactions => return Err(TransactionStorageError::OperationNotSupported), + DbKey::CompletedTransactions => return Err(TransactionStorageError::OperationNotSupported), + DbKey::PendingCoinbaseTransactions => return Err(TransactionStorageError::OperationNotSupported), + }, + } + + Ok(None) + } + + fn transaction_exists(&self, tx_id: u64) -> Result { + let db = acquire_read_lock!(self.db); + + Ok(db.pending_outbound_transactions.contains_key(&tx_id) || + db.pending_inbound_transactions.contains_key(&tx_id) || + db.pending_coinbase_transactions.contains_key(&tx_id) || + db.completed_transactions.contains_key(&tx_id)) + } + + fn complete_outbound_transaction( + &self, + tx_id: TxId, + transaction: CompletedTransaction, + ) -> Result<(), TransactionStorageError> + { + let mut db = acquire_write_lock!(self.db); + + if db.completed_transactions.contains_key(&tx_id) { + return Err(TransactionStorageError::TransactionAlreadyExists); + } + + let _ = db + .pending_outbound_transactions + .remove(&tx_id) + .ok_or_else(|| TransactionStorageError::ValueNotFound(DbKey::PendingOutboundTransaction(tx_id)))?; + + db.completed_transactions.insert(tx_id, transaction); + + Ok(()) + } + + fn complete_inbound_transaction( + &self, + tx_id: TxId, + transaction: CompletedTransaction, + ) -> Result<(), TransactionStorageError> + { + let mut db = acquire_write_lock!(self.db); + + if db.completed_transactions.contains_key(&tx_id) { + return Err(TransactionStorageError::TransactionAlreadyExists); + } + let _ = db + .pending_inbound_transactions + .remove(&tx_id) + .ok_or_else(|| TransactionStorageError::ValueNotFound(DbKey::PendingInboundTransaction(tx_id)))?; + + db.completed_transactions.insert(tx_id, transaction); + Ok(()) + } + + fn complete_coinbase_transaction( + &self, + tx_id: u64, + completed_transaction: CompletedTransaction, + ) -> Result<(), TransactionStorageError> + { + let mut db = acquire_write_lock!(self.db); + + if db.completed_transactions.contains_key(&tx_id) { + return Err(TransactionStorageError::TransactionAlreadyExists); + } + let _ = db + .pending_coinbase_transactions + .remove(&tx_id) + .ok_or_else(|| TransactionStorageError::ValueNotFound(DbKey::PendingCoinbaseTransaction(tx_id)))?; + + db.completed_transactions.insert(tx_id, completed_transaction); + Ok(()) + } + + fn broadcast_completed_transaction(&self, tx_id: TxId) -> Result<(), TransactionStorageError> { + let mut db = acquire_write_lock!(self.db); + + let mut completed_tx = db + .completed_transactions + .get_mut(&tx_id) + .ok_or_else(|| TransactionStorageError::ValueNotFound(DbKey::CompletedTransaction(tx_id)))?; + + if completed_tx.status == TransactionStatus::Completed { + completed_tx.status = TransactionStatus::Broadcast; + } + + Ok(()) + } + + fn mine_completed_transaction(&self, tx_id: TxId) -> Result<(), TransactionStorageError> { + let mut db = acquire_write_lock!(self.db); + + let mut completed_tx = db + .completed_transactions + .get_mut(&tx_id) + .ok_or_else(|| TransactionStorageError::ValueNotFound(DbKey::CompletedTransaction(tx_id)))?; + completed_tx.status = TransactionStatus::Mined; + + Ok(()) + } + + #[cfg(feature = "test_harness")] + fn update_completed_transaction_timestamp( + &self, + tx_id: u64, + timestamp: NaiveDateTime, + ) -> Result<(), TransactionStorageError> + { + let mut db = acquire_write_lock!(self.db); + + if let Some(tx) = db.completed_transactions.get_mut(&tx_id) { + tx.timestamp = timestamp; + } + + Ok(()) + } +} diff --git a/base_layer/wallet/src/transaction_service/storage/mod.rs b/base_layer/wallet/src/transaction_service/storage/mod.rs new file mode 100644 index 0000000000..c6c73087f7 --- /dev/null +++ b/base_layer/wallet/src/transaction_service/storage/mod.rs @@ -0,0 +1,25 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +pub mod database; +pub mod memory_db; +pub mod sqlite_db; diff --git a/base_layer/wallet/src/transaction_service/storage/sqlite_db.rs b/base_layer/wallet/src/transaction_service/storage/sqlite_db.rs new file mode 100644 index 0000000000..6aadfd3b71 --- /dev/null +++ b/base_layer/wallet/src/transaction_service/storage/sqlite_db.rs @@ -0,0 +1,1134 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + output_manager_service::TxId, + schema::{coinbase_transactions, completed_transactions, inbound_transactions, outbound_transactions}, + transaction_service::{ + error::TransactionStorageError, + storage::database::{ + CompletedTransaction, + DbKey, + DbKeyValuePair, + DbValue, + InboundTransaction, + OutboundTransaction, + PendingCoinbaseTransaction, + TransactionBackend, + TransactionStatus, + WriteOperation, + }, + }, +}; +use chrono::NaiveDateTime; +use diesel::{ + prelude::*, + r2d2::{ConnectionManager, Pool, PooledConnection}, + result::Error as DieselError, + SqliteConnection, +}; +use std::{collections::HashMap, convert::TryFrom}; +use tari_core::transactions::{ + tari_amount::MicroTari, + types::{Commitment, PublicKey}, +}; +use tari_crypto::tari_utilities::ByteArray; + +/// A Sqlite backend for the Transaction Service. The Backend is accessed via a connection pool to the Sqlite file. +#[derive(Clone)] +pub struct TransactionServiceSqliteDatabase { + database_connection_pool: Pool>, +} +impl TransactionServiceSqliteDatabase { + pub fn new(database_connection_pool: Pool>) -> Self { + Self { + database_connection_pool, + } + } +} + +impl TransactionBackend for TransactionServiceSqliteDatabase { + fn fetch(&self, key: &DbKey) -> Result, TransactionStorageError> { + let conn = self + .database_connection_pool + .clone() + .get() + .map_err(|_| TransactionStorageError::R2d2Error)?; + + let result = match key { + DbKey::PendingOutboundTransaction(t) => match OutboundTransactionSql::find(*t, &conn) { + Ok(o) => Some(DbValue::PendingOutboundTransaction(Box::new( + OutboundTransaction::try_from(o)?, + ))), + Err(TransactionStorageError::DieselError(DieselError::NotFound)) => None, + Err(e) => return Err(e), + }, + DbKey::PendingInboundTransaction(t) => match InboundTransactionSql::find(*t, &conn) { + Ok(o) => Some(DbValue::PendingInboundTransaction(Box::new( + InboundTransaction::try_from(o)?, + ))), + Err(TransactionStorageError::DieselError(DieselError::NotFound)) => None, + Err(e) => return Err(e), + }, + DbKey::PendingCoinbaseTransaction(t) => match PendingCoinbaseTransactionSql::find(*t, &conn) { + Ok(o) => Some(DbValue::PendingCoinbaseTransaction(Box::new( + PendingCoinbaseTransaction::try_from(o)?, + ))), + Err(TransactionStorageError::DieselError(DieselError::NotFound)) => None, + Err(e) => return Err(e), + }, + + DbKey::CompletedTransaction(t) => match CompletedTransactionSql::find(*t, &conn) { + Ok(o) => Some(DbValue::CompletedTransaction(Box::new(CompletedTransaction::try_from( + o, + )?))), + Err(TransactionStorageError::DieselError(DieselError::NotFound)) => None, + Err(e) => return Err(e), + }, + DbKey::PendingOutboundTransactions => Some(DbValue::PendingOutboundTransactions( + OutboundTransactionSql::index(&conn)? + .iter() + .fold(HashMap::new(), |mut acc, x| { + if let Ok(v) = OutboundTransaction::try_from((*x).clone()) { + acc.insert(x.tx_id as u64, v); + } + acc + }), + )), + DbKey::PendingInboundTransactions => Some(DbValue::PendingInboundTransactions( + InboundTransactionSql::index(&conn)? + .iter() + .fold(HashMap::new(), |mut acc, x| { + if let Ok(v) = InboundTransaction::try_from((*x).clone()) { + acc.insert(x.tx_id as u64, v); + } + acc + }), + )), + DbKey::PendingCoinbaseTransactions => Some(DbValue::PendingCoinbaseTransactions( + PendingCoinbaseTransactionSql::index(&conn)? + .iter() + .fold(HashMap::new(), |mut acc, x| { + if let Ok(v) = PendingCoinbaseTransaction::try_from((*x).clone()) { + acc.insert(x.tx_id as u64, v); + } + acc + }), + )), + DbKey::CompletedTransactions => Some(DbValue::CompletedTransactions( + CompletedTransactionSql::index(&conn)? + .iter() + .fold(HashMap::new(), |mut acc, x| { + if let Ok(v) = CompletedTransaction::try_from((*x).clone()) { + acc.insert(x.tx_id as u64, v); + } + acc + }), + )), + }; + + Ok(result) + } + + fn contains(&self, key: &DbKey) -> Result { + let conn = self + .database_connection_pool + .clone() + .get() + .map_err(|_| TransactionStorageError::R2d2Error)?; + + let result = match key { + DbKey::PendingOutboundTransaction(k) => OutboundTransactionSql::find(*k, &conn).is_ok(), + DbKey::PendingInboundTransaction(k) => InboundTransactionSql::find(*k, &conn).is_ok(), + DbKey::PendingCoinbaseTransaction(k) => PendingCoinbaseTransactionSql::find(*k, &conn).is_ok(), + DbKey::CompletedTransaction(k) => CompletedTransactionSql::find(*k, &conn).is_ok(), + DbKey::PendingOutboundTransactions => false, + DbKey::PendingInboundTransactions => false, + DbKey::CompletedTransactions => false, + DbKey::PendingCoinbaseTransactions => false, + }; + + Ok(result) + } + + fn write(&self, op: WriteOperation) -> Result, TransactionStorageError> { + let conn = self + .database_connection_pool + .clone() + .get() + .map_err(|_| TransactionStorageError::R2d2Error)?; + + match op { + WriteOperation::Insert(kvp) => match kvp { + DbKeyValuePair::PendingOutboundTransaction(k, v) => { + if let Ok(_) = OutboundTransactionSql::find(k, &conn) { + return Err(TransactionStorageError::DuplicateOutput); + } + OutboundTransactionSql::try_from(*v)?.commit(&conn)?; + }, + DbKeyValuePair::PendingInboundTransaction(k, v) => { + if let Ok(_) = InboundTransactionSql::find(k, &conn) { + return Err(TransactionStorageError::DuplicateOutput); + } + InboundTransactionSql::try_from(*v)?.commit(&conn)?; + }, + DbKeyValuePair::PendingCoinbaseTransaction(k, v) => { + if let Ok(_) = PendingCoinbaseTransactionSql::find(k, &conn) { + return Err(TransactionStorageError::DuplicateOutput); + } + PendingCoinbaseTransactionSql::from(*v).commit(&conn)?; + }, + DbKeyValuePair::CompletedTransaction(k, v) => { + if let Ok(_) = CompletedTransactionSql::find(k, &conn) { + return Err(TransactionStorageError::DuplicateOutput); + } + CompletedTransactionSql::try_from(*v)?.commit(&conn)?; + }, + }, + WriteOperation::Remove(kvp) => match kvp { + DbKey::PendingOutboundTransaction(k) => match OutboundTransactionSql::find(k, &conn) { + Ok(v) => { + v.delete(&conn)?; + return Ok(Some(DbValue::PendingOutboundTransaction(Box::new( + OutboundTransaction::try_from(v)?, + )))); + }, + Err(TransactionStorageError::DieselError(DieselError::NotFound)) => { + return Err(TransactionStorageError::ValueNotFound( + DbKey::PendingOutboundTransaction(k), + )) + }, + Err(e) => return Err(e), + }, + DbKey::PendingInboundTransaction(k) => match InboundTransactionSql::find(k, &conn) { + Ok(v) => { + v.delete(&conn)?; + return Ok(Some(DbValue::PendingInboundTransaction(Box::new( + InboundTransaction::try_from(v)?, + )))); + }, + Err(TransactionStorageError::DieselError(DieselError::NotFound)) => { + return Err(TransactionStorageError::ValueNotFound( + DbKey::PendingOutboundTransaction(k), + )) + }, + Err(e) => return Err(e), + }, + DbKey::PendingCoinbaseTransaction(k) => match PendingCoinbaseTransactionSql::find(k, &conn) { + Ok(v) => { + v.delete(&conn)?; + return Ok(Some(DbValue::PendingCoinbaseTransaction(Box::new( + PendingCoinbaseTransaction::try_from(v)?, + )))); + }, + Err(TransactionStorageError::DieselError(DieselError::NotFound)) => { + return Err(TransactionStorageError::ValueNotFound( + DbKey::PendingOutboundTransaction(k), + )) + }, + Err(e) => return Err(e), + }, + DbKey::CompletedTransaction(k) => match CompletedTransactionSql::find(k, &conn) { + Ok(v) => { + v.delete(&conn)?; + return Ok(Some(DbValue::CompletedTransaction(Box::new( + CompletedTransaction::try_from(v)?, + )))); + }, + Err(TransactionStorageError::DieselError(DieselError::NotFound)) => { + return Err(TransactionStorageError::ValueNotFound(DbKey::CompletedTransaction(k))) + }, + Err(e) => return Err(e), + }, + DbKey::PendingOutboundTransactions => return Err(TransactionStorageError::OperationNotSupported), + DbKey::PendingInboundTransactions => return Err(TransactionStorageError::OperationNotSupported), + DbKey::CompletedTransactions => return Err(TransactionStorageError::OperationNotSupported), + DbKey::PendingCoinbaseTransactions => return Err(TransactionStorageError::OperationNotSupported), + }, + } + Ok(None) + } + + fn transaction_exists(&self, tx_id: u64) -> Result { + let conn = self + .database_connection_pool + .clone() + .get() + .map_err(|_| TransactionStorageError::R2d2Error)?; + + Ok(OutboundTransactionSql::find(tx_id, &conn).is_ok() || + InboundTransactionSql::find(tx_id, &conn).is_ok() || + PendingCoinbaseTransactionSql::find(tx_id, &conn).is_ok() || + CompletedTransactionSql::find(tx_id, &conn).is_ok()) + } + + fn complete_outbound_transaction( + &self, + tx_id: u64, + completed_transaction: CompletedTransaction, + ) -> Result<(), TransactionStorageError> + { + let conn = self + .database_connection_pool + .clone() + .get() + .map_err(|_| TransactionStorageError::R2d2Error)?; + + if CompletedTransactionSql::find(tx_id, &conn).is_ok() { + return Err(TransactionStorageError::TransactionAlreadyExists); + } + + match OutboundTransactionSql::find(tx_id, &conn) { + Ok(v) => { + let completed_tx_sql = CompletedTransactionSql::try_from(completed_transaction)?; + v.delete(&conn)?; + completed_tx_sql.commit(&conn)?; + }, + Err(TransactionStorageError::DieselError(DieselError::NotFound)) => { + return Err(TransactionStorageError::ValueNotFound( + DbKey::PendingOutboundTransaction(tx_id), + )) + }, + Err(e) => return Err(e), + }; + Ok(()) + } + + fn complete_inbound_transaction( + &self, + tx_id: u64, + completed_transaction: CompletedTransaction, + ) -> Result<(), TransactionStorageError> + { + let conn = self + .database_connection_pool + .clone() + .get() + .map_err(|_| TransactionStorageError::R2d2Error)?; + + if CompletedTransactionSql::find(tx_id, &conn).is_ok() { + return Err(TransactionStorageError::TransactionAlreadyExists); + } + + match InboundTransactionSql::find(tx_id, &conn) { + Ok(v) => { + let completed_tx_sql = CompletedTransactionSql::try_from(completed_transaction)?; + v.delete(&conn)?; + completed_tx_sql.commit(&conn)?; + }, + Err(TransactionStorageError::DieselError(DieselError::NotFound)) => { + return Err(TransactionStorageError::ValueNotFound( + DbKey::PendingInboundTransaction(tx_id), + )) + }, + Err(e) => return Err(e), + }; + Ok(()) + } + + fn complete_coinbase_transaction( + &self, + tx_id: u64, + completed_transaction: CompletedTransaction, + ) -> Result<(), TransactionStorageError> + { + let conn = self + .database_connection_pool + .clone() + .get() + .map_err(|_| TransactionStorageError::R2d2Error)?; + + if CompletedTransactionSql::find(tx_id, &conn).is_ok() { + return Err(TransactionStorageError::TransactionAlreadyExists); + } + + match PendingCoinbaseTransactionSql::find(tx_id, &conn) { + Ok(v) => { + let completed_tx_sql = CompletedTransactionSql::try_from(completed_transaction)?; + v.delete(&conn)?; + completed_tx_sql.commit(&conn)?; + }, + Err(TransactionStorageError::DieselError(DieselError::NotFound)) => { + return Err(TransactionStorageError::ValueNotFound( + DbKey::PendingCoinbaseTransaction(tx_id), + )) + }, + Err(e) => return Err(e), + }; + Ok(()) + } + + fn broadcast_completed_transaction(&self, tx_id: u64) -> Result<(), TransactionStorageError> { + let conn = self + .database_connection_pool + .clone() + .get() + .map_err(|_| TransactionStorageError::R2d2Error)?; + + match CompletedTransactionSql::find(tx_id, &conn) { + Ok(v) => { + if TransactionStatus::try_from(v.status)? == TransactionStatus::Completed { + let _ = v.update( + UpdateCompletedTransaction { + status: Some(TransactionStatus::Broadcast), + timestamp: None, + }, + &conn, + )?; + } + }, + Err(TransactionStorageError::DieselError(DieselError::NotFound)) => { + return Err(TransactionStorageError::ValueNotFound( + DbKey::PendingInboundTransaction(tx_id), + )) + }, + Err(e) => return Err(e), + }; + Ok(()) + } + + fn mine_completed_transaction(&self, tx_id: u64) -> Result<(), TransactionStorageError> { + let conn = self + .database_connection_pool + .clone() + .get() + .map_err(|_| TransactionStorageError::R2d2Error)?; + + match CompletedTransactionSql::find(tx_id, &conn) { + Ok(v) => { + let _ = v.update( + UpdateCompletedTransaction { + status: Some(TransactionStatus::Mined), + timestamp: None, + }, + &conn, + )?; + }, + Err(TransactionStorageError::DieselError(DieselError::NotFound)) => { + return Err(TransactionStorageError::ValueNotFound( + DbKey::PendingInboundTransaction(tx_id), + )) + }, + Err(e) => return Err(e), + }; + Ok(()) + } + + #[cfg(feature = "test_harness")] + fn update_completed_transaction_timestamp( + &self, + tx_id: u64, + timestamp: NaiveDateTime, + ) -> Result<(), TransactionStorageError> + { + let conn = self + .database_connection_pool + .clone() + .get() + .map_err(|_| TransactionStorageError::R2d2Error)?; + + if let Ok(tx) = CompletedTransactionSql::find(tx_id, &conn) { + let _ = tx.update( + UpdateCompletedTransaction { + status: None, + timestamp: Some(timestamp), + }, + &conn, + ); + } + + Ok(()) + } +} + +#[derive(Clone, Debug, Queryable, Insertable, PartialEq)] +#[table_name = "inbound_transactions"] +struct InboundTransactionSql { + tx_id: i64, + source_public_key: Vec, + amount: i64, + receiver_protocol: String, + message: String, + timestamp: NaiveDateTime, +} + +impl InboundTransactionSql { + pub fn commit( + &self, + conn: &PooledConnection>, + ) -> Result<(), TransactionStorageError> + { + diesel::insert_into(inbound_transactions::table) + .values(self.clone()) + .execute(conn)?; + Ok(()) + } + + pub fn index( + conn: &PooledConnection>, + ) -> Result, TransactionStorageError> { + Ok(inbound_transactions::table.load::(conn)?) + } + + pub fn find( + tx_id: TxId, + conn: &PooledConnection>, + ) -> Result + { + Ok(inbound_transactions::table + .filter(inbound_transactions::tx_id.eq(tx_id as i64)) + .first::(conn)?) + } + + pub fn delete( + &self, + conn: &PooledConnection>, + ) -> Result<(), TransactionStorageError> + { + let num_deleted = + diesel::delete(inbound_transactions::table.filter(inbound_transactions::tx_id.eq(&self.tx_id))) + .execute(conn)?; + + if num_deleted == 0 { + return Err(TransactionStorageError::ValuesNotFound); + } + + Ok(()) + } +} + +impl TryFrom for InboundTransactionSql { + type Error = TransactionStorageError; + + fn try_from(i: InboundTransaction) -> Result { + Ok(Self { + tx_id: i.tx_id as i64, + source_public_key: i.source_public_key.to_vec(), + amount: u64::from(i.amount) as i64, + receiver_protocol: serde_json::to_string(&i.receiver_protocol)?, + message: i.message, + timestamp: i.timestamp, + }) + } +} + +impl TryFrom for InboundTransaction { + type Error = TransactionStorageError; + + fn try_from(i: InboundTransactionSql) -> Result { + Ok(Self { + tx_id: i.tx_id as u64, + source_public_key: PublicKey::from_vec(&i.source_public_key) + .map_err(|_| TransactionStorageError::ConversionError)?, + amount: MicroTari::from(i.amount as u64), + receiver_protocol: serde_json::from_str(&i.receiver_protocol)?, + message: i.message, + timestamp: i.timestamp, + }) + } +} + +/// A structure to represent a Sql compatible version of the OutboundTransaction struct +#[derive(Clone, Debug, Queryable, Insertable, PartialEq)] +#[table_name = "outbound_transactions"] +struct OutboundTransactionSql { + tx_id: i64, + destination_public_key: Vec, + amount: i64, + fee: i64, + sender_protocol: String, + message: String, + timestamp: NaiveDateTime, +} + +impl OutboundTransactionSql { + pub fn commit( + &self, + conn: &PooledConnection>, + ) -> Result<(), TransactionStorageError> + { + diesel::insert_into(outbound_transactions::table) + .values(self.clone()) + .execute(conn)?; + Ok(()) + } + + pub fn index( + conn: &PooledConnection>, + ) -> Result, TransactionStorageError> { + Ok(outbound_transactions::table.load::(conn)?) + } + + pub fn find( + tx_id: TxId, + conn: &PooledConnection>, + ) -> Result + { + Ok(outbound_transactions::table + .filter(outbound_transactions::tx_id.eq(tx_id as i64)) + .first::(conn)?) + } + + pub fn delete( + &self, + conn: &PooledConnection>, + ) -> Result<(), TransactionStorageError> + { + let num_deleted = + diesel::delete(outbound_transactions::table.filter(outbound_transactions::tx_id.eq(&self.tx_id))) + .execute(conn)?; + + if num_deleted == 0 { + return Err(TransactionStorageError::ValuesNotFound); + } + + Ok(()) + } +} + +impl TryFrom for OutboundTransactionSql { + type Error = TransactionStorageError; + + fn try_from(i: OutboundTransaction) -> Result { + Ok(Self { + tx_id: i.tx_id as i64, + destination_public_key: i.destination_public_key.to_vec(), + amount: u64::from(i.amount) as i64, + fee: u64::from(i.fee) as i64, + sender_protocol: serde_json::to_string(&i.sender_protocol)?, + message: i.message, + timestamp: i.timestamp, + }) + } +} + +impl TryFrom for OutboundTransaction { + type Error = TransactionStorageError; + + fn try_from(i: OutboundTransactionSql) -> Result { + Ok(Self { + tx_id: i.tx_id as u64, + destination_public_key: PublicKey::from_vec(&i.destination_public_key) + .map_err(|_| TransactionStorageError::ConversionError)?, + amount: MicroTari::from(i.amount as u64), + fee: MicroTari::from(i.fee as u64), + sender_protocol: serde_json::from_str(&i.sender_protocol)?, + message: i.message, + timestamp: i.timestamp, + }) + } +} + +#[derive(Clone, Debug, Queryable, Insertable, PartialEq)] +#[table_name = "coinbase_transactions"] +struct PendingCoinbaseTransactionSql { + tx_id: i64, + amount: i64, + commitment: Vec, + timestamp: NaiveDateTime, +} + +impl PendingCoinbaseTransactionSql { + pub fn commit( + &self, + conn: &PooledConnection>, + ) -> Result<(), TransactionStorageError> + { + diesel::insert_into(coinbase_transactions::table) + .values(self.clone()) + .execute(conn)?; + Ok(()) + } + + pub fn index( + conn: &PooledConnection>, + ) -> Result, TransactionStorageError> { + Ok(coinbase_transactions::table.load::(conn)?) + } + + pub fn find( + tx_id: TxId, + conn: &PooledConnection>, + ) -> Result + { + Ok(coinbase_transactions::table + .filter(coinbase_transactions::tx_id.eq(tx_id as i64)) + .first::(conn)?) + } + + pub fn delete( + &self, + conn: &PooledConnection>, + ) -> Result<(), TransactionStorageError> + { + let num_deleted = + diesel::delete(coinbase_transactions::table.filter(coinbase_transactions::tx_id.eq(&self.tx_id))) + .execute(conn)?; + + if num_deleted == 0 { + return Err(TransactionStorageError::ValuesNotFound); + } + + Ok(()) + } +} + +impl From for PendingCoinbaseTransactionSql { + fn from(i: PendingCoinbaseTransaction) -> Self { + Self { + tx_id: i.tx_id as i64, + amount: u64::from(i.amount) as i64, + commitment: i.commitment.to_vec(), + timestamp: i.timestamp, + } + } +} + +impl TryFrom for PendingCoinbaseTransaction { + type Error = TransactionStorageError; + + fn try_from(i: PendingCoinbaseTransactionSql) -> Result { + Ok(Self { + tx_id: i.tx_id as u64, + amount: MicroTari::from(i.amount as u64), + commitment: Commitment::from_vec(&i.commitment).map_err(|_| TransactionStorageError::ConversionError)?, + timestamp: i.timestamp, + }) + } +} + +/// A structure to represent a Sql compatible version of the CompletedTransaction struct +#[derive(Clone, Debug, Queryable, Insertable, PartialEq)] +#[table_name = "completed_transactions"] +struct CompletedTransactionSql { + tx_id: i64, + source_public_key: Vec, + destination_public_key: Vec, + amount: i64, + fee: i64, + transaction_protocol: String, + status: i32, + message: String, + timestamp: NaiveDateTime, +} + +impl CompletedTransactionSql { + pub fn commit( + &self, + conn: &PooledConnection>, + ) -> Result<(), TransactionStorageError> + { + diesel::insert_into(completed_transactions::table) + .values(self.clone()) + .execute(conn)?; + Ok(()) + } + + pub fn index( + conn: &PooledConnection>, + ) -> Result, TransactionStorageError> { + Ok(completed_transactions::table.load::(conn)?) + } + + pub fn find( + tx_id: TxId, + conn: &PooledConnection>, + ) -> Result + { + Ok(completed_transactions::table + .filter(completed_transactions::tx_id.eq(tx_id as i64)) + .first::(conn)?) + } + + pub fn delete( + &self, + conn: &PooledConnection>, + ) -> Result<(), TransactionStorageError> + { + let num_deleted = + diesel::delete(completed_transactions::table.filter(completed_transactions::tx_id.eq(&self.tx_id))) + .execute(conn)?; + + if num_deleted == 0 { + return Err(TransactionStorageError::ValuesNotFound); + } + + Ok(()) + } + + pub fn update( + &self, + updated_tx: UpdateCompletedTransaction, + conn: &PooledConnection>, + ) -> Result + { + let num_updated = + diesel::update(completed_transactions::table.filter(completed_transactions::tx_id.eq(&self.tx_id))) + .set(UpdateCompletedTransactionSql::from(updated_tx)) + .execute(conn)?; + + if num_updated == 0 { + return Err(TransactionStorageError::UnexpectedResult( + "Database update error".to_string(), + )); + } + + Ok(CompletedTransactionSql::find(self.tx_id as u64, conn)?) + } +} + +impl TryFrom for CompletedTransactionSql { + type Error = TransactionStorageError; + + fn try_from(c: CompletedTransaction) -> Result { + Ok(Self { + tx_id: c.tx_id as i64, + source_public_key: c.source_public_key.to_vec(), + destination_public_key: c.destination_public_key.to_vec(), + amount: u64::from(c.amount) as i64, + fee: u64::from(c.fee) as i64, + transaction_protocol: serde_json::to_string(&c.transaction)?, + status: c.status as i32, + message: c.message, + timestamp: c.timestamp, + }) + } +} + +impl TryFrom for CompletedTransaction { + type Error = TransactionStorageError; + + fn try_from(c: CompletedTransactionSql) -> Result { + Ok(Self { + tx_id: c.tx_id as u64, + source_public_key: PublicKey::from_vec(&c.source_public_key) + .map_err(|_| TransactionStorageError::ConversionError)?, + destination_public_key: PublicKey::from_vec(&c.destination_public_key) + .map_err(|_| TransactionStorageError::ConversionError)?, + amount: MicroTari::from(c.amount as u64), + fee: MicroTari::from(c.fee as u64), + transaction: serde_json::from_str(&c.transaction_protocol)?, + status: TransactionStatus::try_from(c.status)?, + message: c.message, + timestamp: c.timestamp, + }) + } +} + +/// These are the fields that can be updated for a Completed Transaction +pub struct UpdateCompletedTransaction { + status: Option, + timestamp: Option, +} + +#[derive(AsChangeset)] +#[table_name = "completed_transactions"] +pub struct UpdateCompletedTransactionSql { + status: Option, + timestamp: Option, +} + +/// Map a Rust friendly UpdateCompletedTransaction to the Sql data type form +impl From for UpdateCompletedTransactionSql { + fn from(u: UpdateCompletedTransaction) -> Self { + Self { + status: u.status.map(|s| s as i32), + timestamp: u.timestamp, + } + } +} + +#[cfg(test)] +mod test { + #[cfg(feature = "test_harness")] + use crate::transaction_service::storage::sqlite_db::UpdateCompletedTransaction; + use crate::transaction_service::storage::{ + database::{ + CompletedTransaction, + InboundTransaction, + OutboundTransaction, + PendingCoinbaseTransaction, + TransactionStatus, + }, + sqlite_db::{ + CompletedTransactionSql, + InboundTransactionSql, + OutboundTransactionSql, + PendingCoinbaseTransactionSql, + }, + }; + use chrono::Utc; + use diesel::{r2d2::ConnectionManager, Connection, SqliteConnection}; + use rand::rngs::OsRng; + use std::convert::TryFrom; + use tari_core::transactions::{ + tari_amount::MicroTari, + transaction::{OutputFeatures, Transaction, UnblindedOutput}, + transaction_protocol::sender::TransactionSenderMessage, + types::{CommitmentFactory, CryptoFactories, HashDigest, PrivateKey, PublicKey}, + ReceiverTransactionProtocol, + SenderTransactionProtocol, + }; + use tari_crypto::{ + commitment::HomomorphicCommitmentFactory, + keys::{PublicKey as PublicKeyTrait, SecretKey as SecretKeyTrait}, + }; + use tari_test_utils::random::string; + use tempdir::TempDir; + + #[test] + fn test_crud() { + let factories = CryptoFactories::default(); + let db_name = format!("{}.sqlite3", string(8).as_str()); + let temp_dir = TempDir::new(string(8).as_str()).unwrap(); + let db_folder = temp_dir.path().to_str().unwrap().to_string(); + let db_path = format!("{}{}", db_folder, db_name); + + embed_migrations!("./migrations"); + let conn = SqliteConnection::establish(&db_path).unwrap_or_else(|_| panic!("Error connecting to {}", db_path)); + + embedded_migrations::run_with_output(&conn, &mut std::io::stdout()).expect("Migration failed"); + + let manager = ConnectionManager::::new(db_path); + let pool = diesel::r2d2::Pool::builder().max_size(1).build(manager).unwrap(); + + let conn = pool.get().unwrap(); + conn.execute("PRAGMA foreign_keys = ON").unwrap(); + + let mut builder = SenderTransactionProtocol::builder(1); + let amount = MicroTari::from(10_000); + let input = UnblindedOutput::new(MicroTari::from(100_000), PrivateKey::random(&mut OsRng), None); + builder + .with_lock_height(0) + .with_fee_per_gram(MicroTari::from(177)) + .with_offset(PrivateKey::random(&mut OsRng)) + .with_private_nonce(PrivateKey::random(&mut OsRng)) + .with_amount(0, amount) + .with_message("Yo!".to_string()) + .with_input( + input.as_transaction_input(&factories.commitment, OutputFeatures::default()), + input.clone(), + ) + .with_change_secret(PrivateKey::random(&mut OsRng)); + + let stp = builder.build::(&factories).unwrap(); + + let outbound_tx1 = OutboundTransaction { + tx_id: 1u64, + destination_public_key: PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), + amount, + fee: stp.clone().get_fee_amount().unwrap(), + sender_protocol: stp.clone(), + message: "Yo!".to_string(), + timestamp: Utc::now().naive_utc(), + }; + + let outbound_tx2 = OutboundTransactionSql::try_from(OutboundTransaction { + tx_id: 2u64, + destination_public_key: PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), + amount, + fee: stp.clone().get_fee_amount().unwrap(), + sender_protocol: stp.clone(), + message: "Hey!".to_string(), + timestamp: Utc::now().naive_utc(), + }) + .unwrap(); + + OutboundTransactionSql::from(OutboundTransactionSql::try_from(outbound_tx1.clone()).unwrap()) + .commit(&conn) + .unwrap(); + OutboundTransactionSql::from(outbound_tx2.clone()) + .commit(&conn) + .unwrap(); + + let outbound_txs = OutboundTransactionSql::index(&conn).unwrap(); + assert_eq!(outbound_txs.len(), 2); + + let returned_outbound_tx = + OutboundTransaction::try_from(OutboundTransactionSql::find(1u64, &conn).unwrap()).unwrap(); + assert_eq!( + OutboundTransactionSql::try_from(returned_outbound_tx).unwrap(), + OutboundTransactionSql::try_from(outbound_tx1.clone()).unwrap() + ); + + let rtp = ReceiverTransactionProtocol::new( + TransactionSenderMessage::Single(Box::new(stp.clone().build_single_round_message().unwrap())), + PrivateKey::random(&mut OsRng), + PrivateKey::random(&mut OsRng), + OutputFeatures::default(), + &factories, + ); + + let inbound_tx1 = InboundTransaction { + tx_id: 2, + source_public_key: PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), + amount, + receiver_protocol: rtp.clone(), + message: "Yo!".to_string(), + timestamp: Utc::now().naive_utc(), + }; + let inbound_tx2 = InboundTransaction { + tx_id: 3, + source_public_key: PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), + amount, + receiver_protocol: rtp.clone(), + message: "Hey!".to_string(), + timestamp: Utc::now().naive_utc(), + }; + + InboundTransactionSql::try_from(inbound_tx1.clone()) + .unwrap() + .commit(&conn) + .unwrap(); + InboundTransactionSql::try_from(inbound_tx2) + .unwrap() + .commit(&conn) + .unwrap(); + + let inbound_txs = InboundTransactionSql::index(&conn).unwrap(); + assert_eq!(inbound_txs.len(), 2); + + let returned_inbound_tx = + InboundTransaction::try_from(InboundTransactionSql::find(2u64, &conn).unwrap()).unwrap(); + assert_eq!( + InboundTransactionSql::try_from(returned_inbound_tx).unwrap(), + InboundTransactionSql::try_from(inbound_tx1.clone()).unwrap() + ); + + let tx = Transaction::new(vec![], vec![], vec![], PrivateKey::random(&mut OsRng)); + + let completed_tx1 = CompletedTransaction { + tx_id: 2, + source_public_key: PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), + destination_public_key: PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), + amount, + fee: MicroTari::from(100), + transaction: tx.clone(), + status: TransactionStatus::Mined, + message: "Yo!".to_string(), + timestamp: Utc::now().naive_utc(), + }; + let completed_tx2 = CompletedTransaction { + tx_id: 3, + source_public_key: PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), + destination_public_key: PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), + amount, + fee: MicroTari::from(100), + transaction: tx.clone(), + status: TransactionStatus::Broadcast, + message: "Hey!".to_string(), + timestamp: Utc::now().naive_utc(), + }; + + CompletedTransactionSql::try_from(completed_tx1.clone()) + .unwrap() + .commit(&conn) + .unwrap(); + assert!(CompletedTransactionSql::try_from(completed_tx1.clone()) + .unwrap() + .commit(&conn) + .is_err()); + + CompletedTransactionSql::try_from(completed_tx2.clone()) + .unwrap() + .commit(&conn) + .unwrap(); + + let completed_txs = CompletedTransactionSql::index(&conn).unwrap(); + assert_eq!(completed_txs.len(), 2); + + let returned_completed_tx = + CompletedTransaction::try_from(CompletedTransactionSql::find(2u64, &conn).unwrap()).unwrap(); + assert_eq!( + CompletedTransactionSql::try_from(returned_completed_tx).unwrap(), + CompletedTransactionSql::try_from(completed_tx1.clone()).unwrap() + ); + + assert!(InboundTransactionSql::find(inbound_tx1.tx_id, &conn).is_ok()); + InboundTransactionSql::try_from(inbound_tx1.clone()) + .unwrap() + .delete(&conn) + .unwrap(); + assert!(InboundTransactionSql::try_from(inbound_tx1.clone()) + .unwrap() + .delete(&conn) + .is_err()); + assert!(InboundTransactionSql::find(inbound_tx1.tx_id, &conn).is_err()); + + assert!(OutboundTransactionSql::find(inbound_tx1.tx_id, &conn).is_ok()); + OutboundTransactionSql::try_from(outbound_tx1.clone()) + .unwrap() + .delete(&conn) + .unwrap(); + assert!(OutboundTransactionSql::try_from(outbound_tx1.clone()) + .unwrap() + .delete(&conn) + .is_err()); + assert!(OutboundTransactionSql::find(outbound_tx1.tx_id, &conn).is_err()); + + assert!(CompletedTransactionSql::find(completed_tx1.tx_id, &conn).is_ok()); + CompletedTransactionSql::try_from(completed_tx1.clone()) + .unwrap() + .delete(&conn) + .unwrap(); + assert!(CompletedTransactionSql::try_from(completed_tx1.clone()) + .unwrap() + .delete(&conn) + .is_err()); + assert!(CompletedTransactionSql::find(completed_tx1.tx_id, &conn).is_err()); + + let commitment_factory = CommitmentFactory::default(); + let coinbase1 = PendingCoinbaseTransaction { + tx_id: 44, + amount: MicroTari::from(5355), + commitment: commitment_factory.zero(), + timestamp: Utc::now().naive_utc(), + }; + + PendingCoinbaseTransactionSql::from(coinbase1.clone()) + .commit(&conn) + .unwrap(); + assert_eq!( + coinbase1, + PendingCoinbaseTransaction::try_from(PendingCoinbaseTransactionSql::find(44u64, &conn).unwrap()).unwrap() + ); + + PendingCoinbaseTransactionSql::from(coinbase1.clone()) + .delete(&conn) + .unwrap(); + assert!(PendingCoinbaseTransactionSql::find(44u64, &conn).is_err()); + + #[cfg(feature = "test_harness")] + let updated_tx = CompletedTransactionSql::find(completed_tx2.tx_id, &conn) + .unwrap() + .update( + UpdateCompletedTransaction { + status: Some(TransactionStatus::Mined), + timestamp: None, + }, + &conn, + ) + .unwrap(); + #[cfg(feature = "test_harness")] + assert_eq!(updated_tx.status, 2); + } +} diff --git a/base_layer/wallet/src/types.rs b/base_layer/wallet/src/types.rs index ebcd1b605e..8a515a6dad 100644 --- a/base_layer/wallet/src/types.rs +++ b/base_layer/wallet/src/types.rs @@ -20,7 +20,6 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use rand::OsRng; use tari_crypto::common::Blake256; /// Specify the Hash function used by the key manager @@ -28,6 +27,3 @@ pub type KeyDigest = Blake256; /// Specify the Hash function used when constructing challenges during transaction building pub type HashDigest = Blake256; - -/// Specify the Rng to use while building transactions for this wallet -pub type TransactionRng = OsRng; diff --git a/base_layer/wallet/src/util/emoji.rs b/base_layer/wallet/src/util/emoji.rs new file mode 100644 index 0000000000..4cfcdc17f3 --- /dev/null +++ b/base_layer/wallet/src/util/emoji.rs @@ -0,0 +1,106 @@ +// Copyright 2020. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use serde::export::{fmt::Error, Formatter}; +use std::fmt::Display; +use tari_comms::peer_manager::NodeId; +use tari_core::transactions::types::PublicKey; +use tari_crypto::tari_utilities::{ + hex::{Hex, HexError}, + ByteArray, +}; + +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] +pub struct EmojiId(String); + +impl EmojiId { + pub fn from_pubkey(key: &PublicKey) -> Self { + // Temp hacky approach - full spec coming shortly + let node_id = NodeId::from_key(key).unwrap(); + let bytes = node_id.as_bytes(); + let id = bytes.iter().map(|b| EMOJI[*b as usize]).collect(); + Self(id) + } + + pub fn from_hex(hex_key: &str) -> Result { + let key = PublicKey::from_hex(hex_key)?; + Ok(EmojiId::from_pubkey(&key)) + } + + /// Given a emoji string in `value` returns true if this is a representable as a public key + pub fn is_valid(emoji: &str, key: &PublicKey) -> bool { + let eid = EmojiId::from_pubkey(&key); + eid.as_str() == emoji + } + + pub fn as_str(&self) -> &str { + &self.0 + } +} + +impl Display for EmojiId { + fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), Error> { + fmt.write_str(self.as_str()) + } +} + +const EMOJI: [char; 256] = [ + '😀', '😃', '😄', '😁', '😆', '😅', '🤣', '😂', '🙂', '🙃', '😉', '😊', '😇', '🥰', '😍', '🤩', '😘', '😗', '😚', + '😙', '😋', '😛', '😜', '🤪', '😝', '🐠', '🤗', '🤭', '🤫', '🤔', '🤐', '🤨', '😐', '😑', '😶', '😏', '😒', '🙄', + '😬', '🤥', '😌', '😔', '😪', '🤤', '😴', '😷', '🤒', '🤕', '🤢', '🤮', '🤧', '🥵', '🥶', '🥴', '😵', '🤯', '🤠', '🥳', + '😎', '🤓', '🧐', '😕', '😟', '🙁', '😮', '😯', '😲', '😳', '🥺', '😦', '😧', '😨', '😰', '😥', '😢', '😭', '😱', + '😖', '😣', '😞', '😓', '😩', '😫', '😤', '😡', '😠', '🤬', '😈', '👿', '💀', '🐟', '💩', '🤡', '👹', '👺', '👻', + '👽', '👾', '🤖', '😺', '😹', '😻', '😼', '😽', '🙀', '😿', '😾', '💋', '👋', '🤚', '🖐', '✋', '🖖', '👌', '🤞', + '🤟', '🤘', '🤙', '👈', '👉', '👆', '🖕', '👇', '👍', '👎', '✊', '👊', '🤛', '🤜', '👏', '🙌', '👐', '🤲', '🤝', + '🙏', '💅', '🤳', '💪', '🦵', '🦶', '👂', '👃', '🧠', '🦷', '🦴', '👀', '👁', '👅', '👄', '🚶', '👣', '🧳', '🌂', '☂', + '🧵', '🧶', '👓', '🕶', '🥽', '🥼', '👔', '👕', '👖', '🧣', '🧤', '🧥', '🧦', '👗', '👘', '👙', '👚', '👛', '👜', '👝', + '🎒', '👞', '👟', '🥾', '🥿', '👠', '👡', '👢', '👑', '👒', '🎩', '🎓', '🧢', '⛑', '💄', '💍', '💼', '🙈', '🙉', + '🙊', '💥', '💫', '💦', '💨', '🐵', '🐒', '🦍', '🐶', '🐕', '🐩', '🐺', '🦊', '🦝', '🐱', '🐈', '🦁', '🐯', '🐅', + '🐆', '🐴', '🐎', '🦄', '🦓', '🦌', '🐮', '🐂', '🐃', '🐄', '🐷', '🐖', '🐗', '🐽', '🐏', '🐑', '🐐', '🐪', '🐫', + '🦙', '🦒', '🐘', '🦏', '🦛', '🐭', '🐁', '🐀', '🐹', '🐰', '🐇', '🐿', '🦔', '🦇', '🐻', '🐨', '🐼', '🦘', '🦡', '🐾', + '🦃', '🐓', '🐣', '🐋', '🐬', +]; + +#[cfg(test)] +mod test { + use crate::util::emoji::EmojiId; + use tari_core::transactions::types::PublicKey; + use tari_crypto::tari_utilities::hex::Hex; + + #[test] + fn convert_key() { + let key = PublicKey::from_hex("70350e09c474809209824c6e6888707b7dd09959aa227343b5106382b856f73a").unwrap(); + let eid = EmojiId::from_pubkey(&key); + assert_eq!(eid.as_str(), "🤟🥳🤢🧶🖕💦🦒👟👔🤞🤜🐱👠"); + let h_eid = EmojiId::from_hex("70350e09c474809209824c6e6888707b7dd09959aa227343b5106382b856f73a").unwrap(); + assert_eq!(eid, h_eid); + } + + #[test] + fn is_valid() { + let key = PublicKey::from_hex("70350e09c474809209824c6e6888707b7dd09959aa227343b5106382b856f73a").unwrap(); + let eid = EmojiId::from_pubkey(&key); + assert!(EmojiId::is_valid(eid.as_str(), &key)); + assert_eq!(EmojiId::is_valid("😂", &key), false); + assert_eq!(EmojiId::is_valid("Hi", &key), false); + } +} diff --git a/base_layer/wallet/src/util/futures.rs b/base_layer/wallet/src/util/futures.rs new file mode 100644 index 0000000000..fb84e91458 --- /dev/null +++ b/base_layer/wallet/src/util/futures.rs @@ -0,0 +1,74 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::time::Duration; +use tokio::time::delay_for; + +/// The structure can be initialized with some state and an internal Delay future. The struct will resolve to the +/// internal state when the delay elapses. +/// This allows for one to create unique delays that can be await'ed upon in a collection like FuturesUnordered +pub struct StateDelay { + state: T, + period: Duration, +} + +impl StateDelay { + pub fn new(period: Duration, state: T) -> Self { + Self { state, period } + } + + /// The future that will delay for the specified time and then return the internal state + pub async fn delay(self) -> T { + delay_for(self.period).await; + self.state + } +} + +#[cfg(test)] +mod test { + use crate::util::futures::StateDelay; + use chrono::{Duration as ChronoDuration, Utc}; + use std::time::Duration; + use tokio::runtime::Runtime; + + #[derive(Clone, Debug, PartialEq)] + struct Dummy { + a: i32, + b: String, + } + + #[test] + fn test_state_delay() { + let mut runtime = Runtime::new().unwrap(); + let state = Dummy { + a: 22, + b: "Testing".to_string(), + }; + let delay = 1; + let state_delay_future = StateDelay::new(Duration::from_secs(delay), state.clone()); + let tick = Utc::now().naive_utc(); + let result = runtime.block_on(state_delay_future.delay()); + let tock = Utc::now().naive_utc(); + assert!(tock.signed_duration_since(tick) > ChronoDuration::seconds(0i64)); + assert_eq!(result, state); + } +} diff --git a/base_layer/wallet/src/util/luhn.rs b/base_layer/wallet/src/util/luhn.rs new file mode 100644 index 0000000000..170aad4b36 --- /dev/null +++ b/base_layer/wallet/src/util/luhn.rs @@ -0,0 +1,75 @@ +// Copyright 2020. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/// Calculates a checksum using the [Luhn mod n algorithm](https://en.wikipedia.org/wiki/Luhn_mod_N_algorithm). The +/// input to the function is an array of indices, each of which is strictly less than `dict_len`, and the size of the +/// dictionary (`dict_len`). The result is the checksum character, also strictly less than `dict_len`. +pub fn checksum(arr: &[usize], dict_len: usize) -> usize { + // Starting from the right and working leftwards is easier since + let (sum, _) = arr.iter().rev().fold((0usize, 2usize), |(sum, factor), digit| { + let mut addend = factor * *digit; + let factor = factor ^ 3; // Toggles between 1 and 2 + addend = (addend / dict_len) + addend % dict_len; + (sum + addend, factor) + }); + (dict_len - (sum % dict_len)) % dict_len +} + +/// Checks whether the last digit in the array matches the checksum for the array minus the last digit. +pub fn is_valid(arr: &[usize], dict_len: usize) -> bool { + let cs = checksum(&arr[..arr.len() - 1], dict_len); + cs == arr[arr.len() - 1] +} + +#[cfg(test)] +mod test { + use crate::util::luhn::*; + + #[test] + fn luhn_6() { + assert_eq!(checksum(&[0, 1, 2, 3, 4, 5], 6), 4); + for i in 0..6 { + let valid = is_valid(&[0, 1, 2, 3, 4, 5, i], 6); + match i { + 4 => assert!(valid), + _ => assert_eq!(valid, false), + } + } + } + + #[test] + fn luhn_10() { + assert_eq!(checksum(&[7, 9, 9, 2, 7, 3, 9, 8, 7, 1], 10), 3); + for i in 0..10 { + let valid = is_valid(&[7, 9, 9, 2, 7, 3, 9, 8, 7, 1, i], 10); + match i { + 3 => assert!(valid), + _ => assert_eq!(valid, false), + } + } + assert_eq!(checksum(&[1, 0, 4], 10), 0); + assert_eq!(checksum(&[9, 1, 2, 4, 3, 4, 3, 3, 0], 10), 3); + assert!(is_valid(&[9, 1, 2, 4, 3, 4, 3, 3, 0, 3], 10)); + // It doesn't catch some transpose errors + assert!(is_valid(&[0, 1, 2, 4, 3, 4, 3, 3, 9, 3], 10)); + } +} diff --git a/applications/grpc_wallet/tests/mod.rs b/base_layer/wallet/src/util/mod.rs similarity index 97% rename from applications/grpc_wallet/tests/mod.rs rename to base_layer/wallet/src/util/mod.rs index 14d5c72a00..2cf859d113 100644 --- a/applications/grpc_wallet/tests/mod.rs +++ b/base_layer/wallet/src/util/mod.rs @@ -20,4 +20,6 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -pub mod wallet_grpc_server; +pub mod emoji; +pub mod futures; +pub mod luhn; diff --git a/base_layer/wallet/src/wallet.rs b/base_layer/wallet/src/wallet.rs index 16d212a916..da35d57ba3 100644 --- a/base_layer/wallet/src/wallet.rs +++ b/base_layer/wallet/src/wallet.rs @@ -20,60 +20,315 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::text_message_service::{TextMessageService, TextMessageServiceApi}; -use derive_error::Error; -use std::sync::Arc; -use tari_comms::{builder::CommsServices, types::CommsPublicKey}; +use crate::{ + contacts_service::{handle::ContactsServiceHandle, storage::database::ContactsBackend, ContactsServiceInitializer}, + error::WalletError, + output_manager_service::{ + config::OutputManagerServiceConfig, + handle::OutputManagerHandle, + storage::database::OutputManagerBackend, + OutputManagerServiceInitializer, + TxId, + }, + storage::database::{WalletBackend, WalletDatabase}, + transaction_service::{ + config::TransactionServiceConfig, + handle::TransactionServiceHandle, + storage::database::TransactionBackend, + TransactionServiceInitializer, + }, +}; +use blake2::Digest; +use log::{LevelFilter, *}; +use log4rs::{ + append::file::FileAppender, + config::{Appender, Config, Root}, + encode::pattern::PatternEncoder, + Handle as LogHandle, +}; +use std::{marker::PhantomData, sync::Arc, time::Duration}; +use tari_comms::{ + multiaddr::Multiaddr, + peer_manager::{NodeId, Peer, PeerFeatures, PeerFlags}, + types::CommsPublicKey, + CommsNode, +}; +use tari_comms_dht::Dht; +use tari_core::transactions::{ + tari_amount::MicroTari, + transaction::{OutputFeatures, UnblindedOutput}, + types::{CryptoFactories, PrivateKey}, +}; +use tari_crypto::{ + common::Blake256, + ristretto::{RistrettoPublicKey, RistrettoSchnorr, RistrettoSecretKey}, + signatures::{SchnorrSignature, SchnorrSignatureError}, + tari_utilities::hex::Hex, +}; use tari_p2p::{ - initialization::{initialize_comms, CommsConfig, CommsInitializationError}, - ping_pong::{PingPongService, PingPongServiceApi}, - sync_services::{ServiceExecutor, ServiceRegistry}, - tari_message::TariMessageType, + comms_connector::pubsub_connector, + initialization::{initialize_comms, CommsConfig}, + services::{ + comms_outbound::CommsOutboundServiceInitializer, + liveness::{LivenessConfig, LivenessHandle, LivenessInitializer}, + }, }; +use tari_service_framework::StackBuilder; +use tokio::runtime::Runtime; -#[derive(Debug, Error)] -pub enum WalletError { - CommsInitializationError(CommsInitializationError), -} +const LOG_TARGET: &str = "base_layer::wallet"; #[derive(Clone)] pub struct WalletConfig { - pub comms: CommsConfig, - pub public_key: CommsPublicKey, - pub database_path: String, + pub comms_config: CommsConfig, + pub logging_path: Option, + pub factories: CryptoFactories, } /// A structure containing the config and services that a Wallet application will require. This struct will start up all /// the services and provide the APIs that applications will use to interact with the services -pub struct Wallet { - pub ping_pong_service: Arc, - pub text_message_service: Arc, - pub comms_services: Arc>, - pub service_executor: ServiceExecutor, - pub public_key: CommsPublicKey, +pub struct Wallet +where + T: WalletBackend + 'static, + U: TransactionBackend + Clone + 'static, + V: OutputManagerBackend + 'static, + W: ContactsBackend + 'static, +{ + pub comms: CommsNode, + pub dht_service: Dht, + pub liveness_service: LivenessHandle, + pub output_manager_service: OutputManagerHandle, + pub transaction_service: TransactionServiceHandle, + pub contacts_service: ContactsServiceHandle, + pub db: WalletDatabase, + pub runtime: Runtime, + pub log_handle: Option, + pub factories: CryptoFactories, + #[cfg(feature = "test_harness")] + pub transaction_backend: U, + _u: PhantomData, + _v: PhantomData, + _w: PhantomData, } -impl Wallet { - pub fn new(config: WalletConfig) -> Result { - let ping_pong_service = PingPongService::new(); - let ping_pong_service_api = ping_pong_service.get_api(); +impl Wallet +where + T: WalletBackend + 'static, + U: TransactionBackend + Clone + 'static, + V: OutputManagerBackend + 'static, + W: ContactsBackend + 'static, +{ + pub fn new( + config: WalletConfig, + mut runtime: Runtime, + wallet_backend: T, + transaction_backend: U, + output_manager_backend: V, + contacts_backend: W, + ) -> Result, WalletError> + { + let mut log_handle = None; + if let Some(path) = config.logging_path { + let logfile = FileAppender::builder() + .encoder(Box::new(PatternEncoder::new( + "{d(%Y-%m-%d %H:%M:%S.%f)} [{M}#{L}] [{t}] {l:5} {m} (({T}:{I})){n}", + ))) + .append(false) + .build(path.as_str()) + .unwrap(); + + let config = Config::builder() + .appender(Appender::builder().build("logfile", Box::new(logfile))) + .build(Root::builder().appender("logfile").build(LevelFilter::Debug)) + .unwrap(); + + log_handle = Some(log4rs::init_config(config)?); + } - let text_message_service = TextMessageService::new(config.public_key.clone(), config.database_path.clone()); - let text_message_service_api = text_message_service.get_api(); + let db = WalletDatabase::new(wallet_backend); + let base_node_peers = runtime.block_on(db.get_peers())?; - let registry = ServiceRegistry::new() - .register(ping_pong_service) - .register(text_message_service); + #[cfg(feature = "test_harness")] + let transaction_backend_handle = transaction_backend.clone(); - let comms_services = initialize_comms(config.comms.clone())?; - let service_executor = ServiceExecutor::execute(&comms_services, registry); + let factories = config.factories; + let (publisher, subscription_factory) = pubsub_connector( + runtime.handle().clone(), + config.comms_config.max_concurrent_inbound_tasks, + ); + let subscription_factory = Arc::new(subscription_factory); + + let (comms, dht) = runtime.block_on(initialize_comms(config.comms_config.clone(), publisher))?; + + let fut = StackBuilder::new(runtime.handle().clone(), comms.shutdown_signal()) + .add_initializer(CommsOutboundServiceInitializer::new(dht.outbound_requester())) + .add_initializer(LivenessInitializer::new( + LivenessConfig { + auto_ping_interval: Some(Duration::from_secs(30)), + enable_auto_join: false, + enable_auto_stored_message_request: false, + refresh_neighbours_interval: Default::default(), + }, + Arc::clone(&subscription_factory), + dht.dht_requester(), + )) + .add_initializer(OutputManagerServiceInitializer::new( + OutputManagerServiceConfig::default(), + subscription_factory.clone(), + output_manager_backend, + factories.clone(), + )) + .add_initializer(TransactionServiceInitializer::new( + TransactionServiceConfig::default(), + subscription_factory.clone(), + comms.subscribe_messaging_events(), + transaction_backend, + comms.node_identity(), + factories.clone(), + )) + .add_initializer(ContactsServiceInitializer::new(contacts_backend)) + .finish(); + + let handles = runtime.block_on(fut).expect("Service initialization failed"); + + let mut output_manager_handle = handles + .get_handle::() + .expect("Could not get Output Manager Service Handle"); + let mut transaction_service_handle = handles + .get_handle::() + .expect("Could not get Transaction Service Handle"); + let liveness_handle = handles + .get_handle::() + .expect("Could not get Liveness Service Handle"); + let contacts_handle = handles + .get_handle::() + .expect("Could not get Contacts Service Handle"); + + for p in base_node_peers { + runtime.block_on(transaction_service_handle.set_base_node_public_key(p.public_key.clone()))?; + runtime.block_on(output_manager_handle.set_base_node_public_key(p.public_key.clone()))?; + } Ok(Wallet { - text_message_service: text_message_service_api, - ping_pong_service: ping_pong_service_api, - comms_services, - service_executor, - public_key: config.public_key.clone(), + comms, + dht_service: dht, + liveness_service: liveness_handle, + output_manager_service: output_manager_handle, + transaction_service: transaction_service_handle, + contacts_service: contacts_handle, + db, + runtime, + log_handle, + factories, + #[cfg(feature = "test_harness")] + transaction_backend: transaction_backend_handle, + _u: PhantomData, + _v: PhantomData, + _w: PhantomData, }) } + + /// This method consumes the wallet so that the handles are dropped which will result in the services async loops + /// exiting. + pub fn shutdown(mut self) { + self.runtime.block_on(self.comms.shutdown()); + } + + /// This function will set the base_node that the wallet uses to broadcast transactions and monitor the blockchain + /// state + pub fn set_base_node_peer(&mut self, public_key: CommsPublicKey, net_address: String) -> Result<(), WalletError> { + let address = net_address.parse::()?; + let peer = Peer::new( + public_key.clone(), + NodeId::from_key(&public_key).unwrap(), + vec![address].into(), + PeerFlags::empty(), + PeerFeatures::COMMUNICATION_NODE, + ); + + let existing_peers = self.runtime.block_on(self.db.get_peers())?; + self.runtime.block_on(self.db.save_peer(peer.clone()))?; + // Remove any peers in db to only persist a single peer at a time. + for p in existing_peers { + let _ = self.runtime.block_on(self.db.remove_peer(p.public_key.clone()))?; + } + + self.comms.peer_manager().add_peer(peer.clone())?; + self.runtime.block_on( + self.transaction_service + .set_base_node_public_key(peer.public_key.clone()), + )?; + self.runtime.block_on( + self.output_manager_service + .set_base_node_public_key(peer.public_key.clone()), + )?; + + Ok(()) + } + + /// Import an external spendable UTXO into the wallet. The output will be added to the Output Manager and made + /// spendable. A faux incoming transaction will be created to provide a record of the event. The TxId of the + /// generated transaction is returned. + pub fn import_utxo( + &mut self, + amount: MicroTari, + spending_key: &PrivateKey, + source_public_key: &CommsPublicKey, + message: String, + ) -> Result + { + let unblinded_output = UnblindedOutput::new(amount, spending_key.clone(), None); + + self.runtime + .block_on(self.output_manager_service.add_output(unblinded_output.clone()))?; + + let tx_id = self.runtime.block_on(self.transaction_service.import_utxo( + amount.clone(), + source_public_key.clone(), + message, + ))?; + + info!( + target: LOG_TARGET, + "UTXO (Commitment: {}) imported into wallet", + unblinded_output + .as_transaction_input(&self.factories.commitment, OutputFeatures::default()) + .commitment + .to_hex() + ); + + Ok(tx_id) + } + + pub fn sign_message( + &mut self, + secret: RistrettoSecretKey, + nonce: RistrettoSecretKey, + message: &str, + ) -> Result, SchnorrSignatureError> + { + let challenge = Blake256::digest(message.as_bytes()); + RistrettoSchnorr::sign(secret, nonce, challenge.clone().as_slice()) + } + + pub fn verify_message_signature( + &mut self, + public_key: RistrettoPublicKey, + public_nonce: RistrettoPublicKey, + signature: RistrettoSecretKey, + message: String, + ) -> bool + { + let signature = RistrettoSchnorr::new(public_nonce, signature); + let challenge = Blake256::digest(message.as_bytes()); + signature.verify_challenge(&public_key, challenge.clone().as_slice()) + } + + /// Have all the wallet components that need to start a sync process with the set base node to confirm the wallets + /// state is accurately reflected on the blockchain + pub fn sync_with_base_node(&mut self) -> Result<(), WalletError> { + self.runtime + .block_on(self.output_manager_service.sync_with_base_node())?; + Ok(()) + } } diff --git a/base_layer/wallet/tests/contacts_service/mod.rs b/base_layer/wallet/tests/contacts_service/mod.rs new file mode 100644 index 0000000000..44a8f84df2 --- /dev/null +++ b/base_layer/wallet/tests/contacts_service/mod.rs @@ -0,0 +1,187 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::support::utils::random_string; +use rand::rngs::OsRng; +use tari_core::transactions::types::PublicKey; +use tari_crypto::keys::PublicKey as PublicKeyTrait; +use tari_service_framework::StackBuilder; +use tari_shutdown::Shutdown; +use tari_wallet::{ + contacts_service::{ + error::{ContactsServiceError, ContactsServiceStorageError}, + handle::ContactsServiceHandle, + storage::{ + database::{Contact, ContactsBackend, ContactsDatabase, DbKey}, + memory_db::ContactsServiceMemoryDatabase, + sqlite_db::ContactsServiceSqliteDatabase, + }, + ContactsServiceInitializer, + }, + storage::connection_manager::run_migration_and_create_connection_pool, +}; +use tempdir::TempDir; +use tokio::runtime::Runtime; + +pub fn setup_contacts_service( + runtime: &mut Runtime, + backend: T, +) -> (ContactsServiceHandle, Shutdown) +{ + let shutdown = Shutdown::new(); + let fut = StackBuilder::new(runtime.handle().clone(), shutdown.to_signal()) + .add_initializer(ContactsServiceInitializer::new(backend)) + .finish(); + + let handles = runtime.block_on(fut).expect("Service initialization failed"); + + let contacts_api = handles.get_handle::().unwrap(); + + (contacts_api, shutdown) +} + +#[test] +pub fn test_memory_database_crud() { + let mut runtime = Runtime::new().unwrap(); + + let db = ContactsDatabase::new(ContactsServiceMemoryDatabase::new()); + let mut contacts = Vec::new(); + for i in 0..5 { + let (_secret_key, public_key) = PublicKey::random_keypair(&mut OsRng); + + contacts.push(Contact { + alias: random_string(8), + public_key, + }); + + runtime.block_on(db.upsert_contact(contacts[i].clone())).unwrap(); + } + + let got_contacts = runtime.block_on(db.get_contacts()).unwrap(); + assert_eq!(contacts, got_contacts); + + let contact = runtime + .block_on(db.get_contact(contacts[0].public_key.clone())) + .unwrap(); + assert_eq!(contact, contacts[0]); + + let (_secret_key, public_key) = PublicKey::random_keypair(&mut OsRng); + + let contact = runtime.block_on(db.get_contact(public_key.clone())); + assert_eq!( + contact, + Err(ContactsServiceStorageError::ValueNotFound(DbKey::Contact( + public_key.clone() + ))) + ); + assert_eq!( + runtime.block_on(db.remove_contact(public_key.clone())), + Err(ContactsServiceStorageError::ValueNotFound(DbKey::Contact( + public_key.clone() + ))) + ); + + let _ = runtime + .block_on(db.remove_contact(contacts[0].public_key.clone())) + .unwrap(); + contacts.remove(0); + let got_contacts = runtime.block_on(db.get_contacts()).unwrap(); + + assert_eq!(contacts, got_contacts); +} + +pub fn test_contacts_service(backend: T) { + let mut runtime = Runtime::new().unwrap(); + let (mut contacts_service, _shutdown) = setup_contacts_service(&mut runtime, backend); + + let mut contacts = Vec::new(); + for i in 0..5 { + let (_secret_key, public_key) = PublicKey::random_keypair(&mut OsRng); + + contacts.push(Contact { + alias: random_string(8), + public_key, + }); + + runtime + .block_on(contacts_service.upsert_contact(contacts[i].clone())) + .unwrap(); + } + + let got_contacts = runtime.block_on(contacts_service.get_contacts()).unwrap(); + assert_eq!(contacts, got_contacts); + + let contact = runtime + .block_on(contacts_service.get_contact(contacts[0].public_key.clone())) + .unwrap(); + assert_eq!(contact, contacts[0]); + + let (_secret_key, public_key) = PublicKey::random_keypair(&mut OsRng); + + let contact = runtime.block_on(contacts_service.get_contact(public_key.clone())); + assert_eq!( + contact, + Err(ContactsServiceError::ContactsServiceStorageError( + ContactsServiceStorageError::ValueNotFound(DbKey::Contact(public_key.clone())) + )) + ); + assert_eq!( + runtime.block_on(contacts_service.remove_contact(public_key.clone())), + Err(ContactsServiceError::ContactsServiceStorageError( + ContactsServiceStorageError::ValueNotFound(DbKey::Contact(public_key.clone())) + )) + ); + + let _ = runtime + .block_on(contacts_service.remove_contact(contacts[0].public_key.clone())) + .unwrap(); + contacts.remove(0); + let got_contacts = runtime.block_on(contacts_service.get_contacts()).unwrap(); + + assert_eq!(contacts, got_contacts); + + let mut updated_contact = contacts[1].clone(); + updated_contact.alias = "Fred".to_string(); + + runtime + .block_on(contacts_service.upsert_contact(updated_contact.clone())) + .unwrap(); + let new_contact = runtime + .block_on(contacts_service.get_contact(updated_contact.public_key)) + .unwrap(); + + assert_eq!(new_contact.alias, updated_contact.alias); +} + +#[test] +fn contacts_service_memory_db() { + test_contacts_service(ContactsServiceMemoryDatabase::new()); +} + +#[test] +fn contacts_service_sqlite_db() { + let db_name = format!("{}.sqlite3", random_string(8).as_str()); + let temp_dir = TempDir::new(random_string(8).as_str()).unwrap(); + let db_folder = temp_dir.path().to_str().unwrap().to_string(); + let connection_pool = run_migration_and_create_connection_pool(&format!("{}/{}", db_folder, db_name)).unwrap(); + test_contacts_service(ContactsServiceSqliteDatabase::new(connection_pool)); +} diff --git a/base_layer/wallet/tests/mod.rs b/base_layer/wallet/tests/mod.rs index bc8e8609c8..0a1682d99f 100644 --- a/base_layer/wallet/tests/mod.rs +++ b/base_layer/wallet/tests/mod.rs @@ -20,8 +20,11 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#![feature(type_alias_impl_trait)] + pub mod output_manager_service; pub mod support; -pub mod text_message_service; +// pub mod text_message_service; +pub mod contacts_service; pub mod transaction_service; pub mod wallet; diff --git a/base_layer/wallet/tests/output_manager_service/mod.rs b/base_layer/wallet/tests/output_manager_service/mod.rs index 44985eff8e..4e344eb9f5 100644 --- a/base_layer/wallet/tests/output_manager_service/mod.rs +++ b/base_layer/wallet/tests/output_manager_service/mod.rs @@ -19,372 +19,6 @@ // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::support::{ - comms_and_services::setup_comms_services, - data::{clean_up_datastore, init_datastore}, - utils::{make_input, TestParams}, -}; -use chrono::Duration as ChronoDuration; -use log::Level; -use rand::RngCore; -use std::{thread, time::Duration}; -use tari_comms::peer_manager::NodeIdentity; -use tari_core::{ - consensus::ConsensusRules, - fee::Fee, - tari_amount::MicroTari, - transaction::{KernelFeatures, OutputFeatures, TransactionOutput, UnblindedOutput}, - transaction_protocol::single_receiver::SingleReceiverTransactionProtocol, - types::{PrivateKey, PublicKey, RangeProof, COMMITMENT_FACTORY, PROVER}, -}; -use tari_crypto::{ - commitment::HomomorphicCommitmentFactory, - keys::{PublicKey as PublicKeyTrait, SecretKey}, - range_proof::RangeProofService, -}; -use tari_p2p::sync_services::{ServiceExecutor, ServiceRegistry}; -use tari_utilities::ByteArray; -use tari_wallet::output_manager_service::{error::OutputManagerError, output_manager_service::OutputManagerService}; -#[test] -fn sending_transaction_and_confirmation() { - let mut rng = rand::OsRng::new().unwrap(); - let (secret_key, _public_key) = PublicKey::random_keypair(&mut rng); - - let mut oms = OutputManagerService::new(secret_key, "".to_string(), 0); - - let (_ti, uo) = make_input(&mut rng.clone(), MicroTari::from(100 + rng.next_u64() % 1000)); - oms.add_output(uo.clone()).unwrap(); - assert_eq!(oms.add_output(uo), Err(OutputManagerError::DuplicateOutput)); - let num_outputs = 20; - for _i in 0..num_outputs { - let (_ti, uo) = make_input(&mut rng.clone(), MicroTari::from(100 + rng.next_u64() % 1000)); - oms.add_output(uo).unwrap(); - } - - let mut stp = oms - .prepare_transaction_to_send(MicroTari::from(1000), MicroTari::from(20), None) - .unwrap(); - - let sender_tx_id = stp.get_tx_id().unwrap(); - let mut num_change = 0; - // Is there change? Unlikely not to be but the random amounts MIGHT produce a no change output situation - if stp.get_amount_to_self().unwrap() > MicroTari::from(0) { - let pt = oms.pending_transactions(); - assert_eq!(pt.len(), 1); - assert_eq!( - pt.get(&sender_tx_id).unwrap().outputs_to_be_received[0].value, - stp.get_amount_to_self().unwrap() - ); - num_change = 1; - } - - let msg = stp.build_single_round_message().unwrap(); - - let b = TestParams::new(&mut rng); - - let recv_info = SingleReceiverTransactionProtocol::create( - &msg, - b.nonce, - b.spend_key, - OutputFeatures::default(), - &PROVER, - &COMMITMENT_FACTORY, - ) - .unwrap(); - - stp.add_single_recipient_info(recv_info.clone(), &PROVER).unwrap(); - - stp.finalize(KernelFeatures::empty(), &PROVER, &COMMITMENT_FACTORY) - .unwrap(); - - let tx = stp.get_transaction().unwrap(); - - oms.confirm_sent_transaction(sender_tx_id, &tx.body.inputs, &tx.body.outputs) - .unwrap(); - - assert_eq!(oms.pending_transactions().len(), 0); - assert_eq!(oms.spent_outputs().len(), tx.body.inputs.len()); - assert_eq!( - oms.unspent_outputs().len(), - num_outputs + 1 - oms.spent_outputs().len() + num_change - ); -} - -#[test] -fn send_not_enough_funds() { - let mut rng = rand::OsRng::new().unwrap(); - let (secret_key, _public_key) = PublicKey::random_keypair(&mut rng); - - let mut oms = OutputManagerService::new(secret_key, "".to_string(), 0); - - let num_outputs = 20; - for _i in 0..num_outputs { - let (_ti, uo) = make_input(&mut rng.clone(), MicroTari::from(100 + rng.next_u64() % 1000)); - oms.add_output(uo).unwrap(); - } - - match oms.prepare_transaction_to_send(MicroTari::from(num_outputs * 2000), MicroTari::from(20), None) { - Err(OutputManagerError::NotEnoughFunds) => assert!(true), - _ => assert!(false), - } -} - -#[test] -fn send_no_change() { - let mut rng = rand::OsRng::new().unwrap(); - let (secret_key, _public_key) = PublicKey::random_keypair(&mut rng); - - let mut oms = OutputManagerService::new(secret_key, "".to_string(), 0); - let fee_per_gram = MicroTari::from(20); - let fee_without_change = Fee::calculate(fee_per_gram, 2, 1); - let key1 = PrivateKey::random(&mut rng); - let value1 = 500; - oms.add_output(UnblindedOutput::new(MicroTari::from(value1), key1, None)) - .unwrap(); - let key2 = PrivateKey::random(&mut rng); - let value2 = 800; - oms.add_output(UnblindedOutput::new(MicroTari::from(value2), key2, None)) - .unwrap(); - - let mut stp = oms - .prepare_transaction_to_send( - MicroTari::from(value1 + value2) - fee_without_change, - MicroTari::from(20), - None, - ) - .unwrap(); - - let sender_tx_id = stp.get_tx_id().unwrap(); - assert_eq!(stp.get_amount_to_self().unwrap(), MicroTari::from(0)); - assert_eq!(oms.pending_transactions().len(), 1); - - let msg = stp.build_single_round_message().unwrap(); - - let b = TestParams::new(&mut rng); - - let recv_info = SingleReceiverTransactionProtocol::create( - &msg, - b.nonce, - b.spend_key, - OutputFeatures::default(), - &PROVER, - &COMMITMENT_FACTORY, - ) - .unwrap(); - - stp.add_single_recipient_info(recv_info.clone(), &PROVER).unwrap(); - - stp.finalize(KernelFeatures::empty(), &PROVER, &COMMITMENT_FACTORY) - .unwrap(); - - let tx = stp.get_transaction().unwrap(); - - oms.confirm_sent_transaction(sender_tx_id, &tx.body.inputs, &tx.body.outputs) - .unwrap(); - - assert_eq!(oms.pending_transactions().len(), 0); - assert_eq!(oms.spent_outputs().len(), tx.body.inputs.len()); - assert_eq!(oms.unspent_outputs().len(), 0); -} - -#[test] -fn send_not_enough_for_change() { - let mut rng = rand::OsRng::new().unwrap(); - let (secret_key, _public_key) = PublicKey::random_keypair(&mut rng); - - let mut oms = OutputManagerService::new(secret_key, "".to_string(), 0); - let fee_per_gram = MicroTari::from(20); - let fee_without_change = Fee::calculate(fee_per_gram, 2, 1); - let key1 = PrivateKey::random(&mut rng); - let value1 = 500; - oms.add_output(UnblindedOutput::new(MicroTari::from(value1), key1, None)) - .unwrap(); - let key2 = PrivateKey::random(&mut rng); - let value2 = 800; - oms.add_output(UnblindedOutput::new(MicroTari::from(value2), key2, None)) - .unwrap(); - - match oms.prepare_transaction_to_send( - MicroTari::from(value1 + value2 + 1) - fee_without_change, - MicroTari::from(20), - None, - ) { - Err(OutputManagerError::NotEnoughFunds) => assert!(true), - _ => assert!(false), - } -} - -#[test] -fn receiving_and_confirmation() { - let mut rng = rand::OsRng::new().unwrap(); - let (secret_key, _public_key) = PublicKey::random_keypair(&mut rng); - - let mut oms = OutputManagerService::new(secret_key, "".to_string(), 0); - let value = MicroTari::from(5000); - let recv_key = oms.get_recipient_spending_key(1, value).unwrap(); - assert_eq!(oms.unspent_outputs().len(), 0); - assert_eq!(oms.pending_transactions().len(), 1); - - let commitment = COMMITMENT_FACTORY.commit(&recv_key, &value.into()); - let rr = PROVER.construct_proof(&recv_key, value.into()).unwrap(); - let output = TransactionOutput::new( - OutputFeatures::create_coinbase(0, &ConsensusRules::current()), - commitment, - RangeProof::from_bytes(&rr).unwrap(), - ); - - oms.confirm_received_transaction_output(1, &output).unwrap(); - - assert_eq!(oms.pending_transactions().len(), 0); - assert_eq!(oms.unspent_outputs().len(), 1); -} - -#[test] -fn cancel_transaction() { - let mut rng = rand::OsRng::new().unwrap(); - let (secret_key, _public_key) = PublicKey::random_keypair(&mut rng); - - let mut oms = OutputManagerService::new(secret_key, "".to_string(), 0); - - let num_outputs = 20; - for _i in 0..num_outputs { - let (_ti, uo) = make_input(&mut rng.clone(), MicroTari::from(100 + rng.next_u64() % 1000)); - oms.add_output(uo).unwrap(); - } - let stp = oms - .prepare_transaction_to_send(MicroTari::from(1000), MicroTari::from(20), None) - .unwrap(); - - assert_eq!( - oms.cancel_transaction(1), - Err(OutputManagerError::PendingTransactionNotFound) - ); - - oms.cancel_transaction(stp.get_tx_id().unwrap()).unwrap(); - - assert_eq!(oms.unspent_outputs().len(), num_outputs); -} - -#[test] -fn timeout_transaction() { - let mut rng = rand::OsRng::new().unwrap(); - let (secret_key, _public_key) = PublicKey::random_keypair(&mut rng); - - let mut oms = OutputManagerService::new(secret_key, "".to_string(), 0); - - let num_outputs = 20; - for _i in 0..num_outputs { - let (_ti, uo) = make_input(&mut rng.clone(), MicroTari::from(100 + rng.next_u64() % 1000)); - oms.add_output(uo).unwrap(); - } - let _stp = oms - .prepare_transaction_to_send(MicroTari::from(1000), MicroTari::from(20), None) - .unwrap(); - - let remaining_outputs = oms.unspent_outputs().len(); - - thread::sleep(Duration::from_millis(2)); - - oms.timeout_pending_transactions(chrono::Duration::milliseconds(10)) - .unwrap(); - - assert_eq!(oms.unspent_outputs().len(), remaining_outputs); - - oms.timeout_pending_transactions(chrono::Duration::milliseconds(1)) - .unwrap(); - - assert_eq!(oms.unspent_outputs().len(), num_outputs); -} - -#[test] -fn test_api() { - let _ = simple_logger::init_with_level(Level::Debug); - let mut rng = rand::OsRng::new().unwrap(); - let (secret_key, _public_key) = PublicKey::random_keypair(&mut rng); - - let oms = OutputManagerService::new(secret_key, "".to_string(), 0); - let api = oms.get_api(); - let services = ServiceRegistry::new().register(oms); - - // The Service Executor needs a comms stack even though the OMS doesn't use the comms stack. - let node_1_identity = NodeIdentity::random(&mut rng, "127.0.0.1:32563".parse().unwrap()).unwrap(); - let node_1_database_name = "node_1_output_manager_service_api_test"; // Note: every test should have unique database - let node_1_datastore = init_datastore(node_1_database_name).unwrap(); - let node_1_peer_database = node_1_datastore.get_handle(node_1_database_name).unwrap(); - let comms = setup_comms_services(node_1_identity.clone(), Vec::new(), node_1_peer_database); - - let executor = ServiceExecutor::execute(&comms, services); - - assert_eq!(api.get_balance().unwrap(), MicroTari::from(0)); - - let num_outputs = 20; - let mut balance = MicroTari::from(0); - for _i in 0..num_outputs { - let (_ti, uo) = make_input(&mut rng.clone(), MicroTari::from(100 + rng.next_u64() % 1000)); - balance += uo.clone().value; - api.add_output(uo).unwrap(); - } - let amount_to_send = MicroTari::from(1000); - let fee_per_gram = MicroTari::from(20); - let stp = api - .prepare_transaction_to_send(amount_to_send, fee_per_gram, None) - .unwrap(); - - assert_ne!(api.get_balance().unwrap(), balance); - api.cancel_transaction(stp.get_tx_id().unwrap()).unwrap(); - assert_eq!(api.get_balance().unwrap(), balance); - let _stp = api - .prepare_transaction_to_send(amount_to_send, fee_per_gram, None) - .unwrap(); - assert_ne!(api.get_balance().unwrap(), balance); - thread::sleep(Duration::from_millis(10)); - api.timeout_transactions(ChronoDuration::milliseconds(1)).unwrap(); - assert_eq!(api.get_balance().unwrap(), balance); - - let mut stp = api - .prepare_transaction_to_send(amount_to_send, fee_per_gram, None) - .unwrap(); - - let sender_tx_id = stp.get_tx_id().unwrap(); - let msg = stp.build_single_round_message().unwrap(); - - let b = TestParams::new(&mut rng); - - let recv_info = SingleReceiverTransactionProtocol::create( - &msg, - b.nonce, - b.spend_key, - OutputFeatures::default(), - &PROVER, - &COMMITMENT_FACTORY, - ) - .unwrap(); - - stp.add_single_recipient_info(recv_info.clone(), &PROVER).unwrap(); - - stp.finalize(KernelFeatures::empty(), &PROVER, &COMMITMENT_FACTORY) - .unwrap(); - let tx = stp.get_transaction().unwrap(); - let fee = Fee::calculate(fee_per_gram, tx.body.inputs.len(), tx.body.outputs.len()); - - api.confirm_sent_transaction(sender_tx_id, tx.body.inputs.clone(), tx.body.outputs.clone()) - .unwrap(); - - assert_eq!(api.get_balance().unwrap(), balance - amount_to_send - fee); - let balance = api.get_balance().unwrap(); - let value = MicroTari::from(5000); - let recv_key = api.get_recipient_spending_key(1, value).unwrap(); - let commitment = COMMITMENT_FACTORY.commit(&recv_key, &value.into()); - let rr = PROVER.construct_proof(&recv_key, value.into()).unwrap(); - let output = TransactionOutput::new( - OutputFeatures::create_coinbase(0, &ConsensusRules::current()), - commitment, - RangeProof::from_bytes(&rr).unwrap(), - ); - api.confirm_received_output(1, output).unwrap(); - - assert_eq!(api.get_balance().unwrap(), balance + value); - executor.shutdown().unwrap(); - clean_up_datastore(node_1_database_name); -} +pub mod service; +pub mod storage; diff --git a/base_layer/wallet/tests/output_manager_service/service.rs b/base_layer/wallet/tests/output_manager_service/service.rs new file mode 100644 index 0000000000..fccce78c18 --- /dev/null +++ b/base_layer/wallet/tests/output_manager_service/service.rs @@ -0,0 +1,790 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::support::{ + comms_and_services::create_dummy_message, + utils::{make_input, random_string, TestParams}, +}; +use futures::{ + channel::{mpsc, mpsc::Sender}, + SinkExt, +}; +use prost::Message; +use rand::{rngs::OsRng, RngCore}; +use std::{thread, time::Duration}; +use tari_broadcast_channel::bounded; +use tari_comms::{ + message::EnvelopeBody, + peer_manager::{NodeIdentity, PeerFeatures}, +}; +use tari_comms_dht::outbound::mock::{create_outbound_service_mock, OutboundServiceMockState}; +use tari_core::{ + base_node::proto::{ + base_node as BaseNodeProto, + base_node::base_node_service_response::Response as BaseNodeResponseProto, + }, + transactions::{ + fee::Fee, + tari_amount::MicroTari, + transaction::{KernelFeatures, OutputFeatures, TransactionOutput, UnblindedOutput}, + transaction_protocol::single_receiver::SingleReceiverTransactionProtocol, + types::{CryptoFactories, PrivateKey, RangeProof}, + }, +}; +use tari_crypto::{ + commitment::HomomorphicCommitmentFactory, + keys::SecretKey, + range_proof::RangeProofService, + tari_utilities::ByteArray, +}; +use tari_p2p::domain_message::DomainMessage; +use tari_service_framework::reply_channel; +use tari_shutdown::Shutdown; +use tari_test_utils::collect_stream; +use tari_wallet::{ + output_manager_service::{ + config::OutputManagerServiceConfig, + error::{OutputManagerError, OutputManagerStorageError}, + handle::{OutputManagerEvent, OutputManagerHandle}, + service::OutputManagerService, + storage::{ + database::{DbKey, DbValue, OutputManagerBackend, OutputManagerDatabase}, + memory_db::OutputManagerMemoryDatabase, + sqlite_db::OutputManagerSqliteDatabase, + }, + }, + storage::connection_manager::run_migration_and_create_connection_pool, +}; +use tempdir::TempDir; +use tokio::runtime::Runtime; + +pub fn setup_output_manager_service( + runtime: &mut Runtime, + backend: T, +) -> ( + OutputManagerHandle, + OutboundServiceMockState, + Shutdown, + Sender>, +) +{ + let shutdown = Shutdown::new(); + let factories = CryptoFactories::default(); + + let (outbound_message_requester, mock_outbound_service) = create_outbound_service_mock(20); + let (oms_request_sender, oms_request_receiver) = reply_channel::unbounded(); + let (base_node_response_sender, base_node_response_receiver) = mpsc::channel(20); + let (oms_event_publisher, oms_event_subscriber) = bounded(100); + + let output_manager_service = runtime + .block_on(OutputManagerService::new( + OutputManagerServiceConfig { + base_node_query_timeout_in_secs: 3, + }, + outbound_message_requester.clone(), + oms_request_receiver, + base_node_response_receiver, + OutputManagerDatabase::new(backend), + oms_event_publisher, + factories.clone(), + )) + .unwrap(); + let output_manager_service_handle = OutputManagerHandle::new(oms_request_sender, oms_event_subscriber); + + runtime.spawn(async move { output_manager_service.start().await.unwrap() }); + + let outbound_mock_state = mock_outbound_service.get_state(); + runtime.spawn(mock_outbound_service.run()); + + ( + output_manager_service_handle, + outbound_mock_state, + shutdown, + base_node_response_sender, + ) +} + +fn sending_transaction_and_confirmation(backend: T) { + let factories = CryptoFactories::default(); + + let mut runtime = Runtime::new().unwrap(); + + let (mut oms, _, _shutdown, _) = setup_output_manager_service(&mut runtime, backend.clone()); + + let (_ti, uo) = make_input( + &mut OsRng.clone(), + MicroTari::from(100 + OsRng.next_u64() % 1000), + &factories.commitment, + ); + runtime.block_on(oms.add_output(uo.clone())).unwrap(); + match runtime.block_on(oms.add_output(uo)) { + Err(OutputManagerError::OutputManagerStorageError(OutputManagerStorageError::DuplicateOutput)) => assert!(true), + _ => assert!(false, "Incorrect error message"), + }; + let num_outputs = 20; + for _i in 0..num_outputs { + let (_ti, uo) = make_input( + &mut OsRng.clone(), + MicroTari::from(100 + OsRng.next_u64() % 1000), + &factories.commitment, + ); + runtime.block_on(oms.add_output(uo)).unwrap(); + } + + let mut stp = runtime + .block_on(oms.prepare_transaction_to_send(MicroTari::from(1000), MicroTari::from(20), None, "".to_string())) + .unwrap(); + + let sender_tx_id = stp.get_tx_id().unwrap(); + let mut num_change = 0; + // Is there change? Unlikely not to be but the random amounts MIGHT produce a no change output situation + if stp.get_amount_to_self().unwrap() > MicroTari::from(0) { + let pt = runtime.block_on(oms.get_pending_transactions()).unwrap(); + assert_eq!(pt.len(), 1); + assert_eq!( + pt.get(&sender_tx_id).unwrap().outputs_to_be_received[0].value, + stp.get_amount_to_self().unwrap() + ); + num_change = 1; + } + + let msg = stp.build_single_round_message().unwrap(); + + let b = TestParams::new(&mut OsRng); + + let recv_info = + SingleReceiverTransactionProtocol::create(&msg, b.nonce, b.spend_key, OutputFeatures::default(), &factories) + .unwrap(); + + stp.add_single_recipient_info(recv_info.clone(), &factories.range_proof) + .unwrap(); + + stp.finalize(KernelFeatures::empty(), &factories).unwrap(); + + let tx = stp.get_transaction().unwrap(); + + runtime + .block_on(oms.confirm_transaction(sender_tx_id, tx.body.inputs().clone(), tx.body.outputs().clone())) + .unwrap(); + + assert_eq!(runtime.block_on(oms.get_pending_transactions()).unwrap().len(), 0); + assert_eq!( + runtime.block_on(oms.get_spent_outputs()).unwrap().len(), + tx.body.inputs().len() + ); + assert_eq!( + runtime.block_on(oms.get_unspent_outputs()).unwrap().len(), + num_outputs + 1 - runtime.block_on(oms.get_spent_outputs()).unwrap().len() + num_change + ); + + if let DbValue::KeyManagerState(km) = backend.fetch(&DbKey::KeyManagerState).unwrap().unwrap() { + assert_eq!(km.primary_key_index, 1); + } else { + assert!(false, "No Key Manager set"); + } +} + +#[test] +fn sending_transaction_and_confirmation_memory_db() { + sending_transaction_and_confirmation(OutputManagerMemoryDatabase::new()); +} + +#[test] +fn sending_transaction_and_confirmation_sqlite_db() { + let db_name = format!("{}.sqlite3", random_string(8).as_str()); + let db_tempdir = TempDir::new(random_string(8).as_str()).unwrap(); + let db_folder = db_tempdir.path().to_str().unwrap().to_string(); + let db_path = format!("{}/{}", db_folder, db_name); + let connection_pool = run_migration_and_create_connection_pool(&db_path).unwrap(); + + sending_transaction_and_confirmation(OutputManagerSqliteDatabase::new(connection_pool)); +} + +fn send_not_enough_funds(backend: T) { + let factories = CryptoFactories::default(); + + let mut runtime = Runtime::new().unwrap(); + + let (mut oms, _, _shutdown, _) = setup_output_manager_service(&mut runtime, backend); + let num_outputs = 20; + for _i in 0..num_outputs { + let (_ti, uo) = make_input( + &mut OsRng.clone(), + MicroTari::from(100 + OsRng.next_u64() % 1000), + &factories.commitment, + ); + runtime.block_on(oms.add_output(uo)).unwrap(); + } + + match runtime.block_on(oms.prepare_transaction_to_send( + MicroTari::from(num_outputs * 2000), + MicroTari::from(20), + None, + "".to_string(), + )) { + Err(OutputManagerError::NotEnoughFunds) => assert!(true), + _ => assert!(false), + } +} + +#[test] +fn send_not_enough_funds_memory_db() { + send_not_enough_funds(OutputManagerMemoryDatabase::new()); +} + +#[test] +fn send_not_enough_funds_sqlite_db() { + let db_name = format!("{}.sqlite3", random_string(8).as_str()); + let db_tempdir = TempDir::new(random_string(8).as_str()).unwrap(); + let db_folder = db_tempdir.path().to_str().unwrap().to_string(); + let db_path = format!("{}/{}", db_folder, db_name); + let connection_pool = run_migration_and_create_connection_pool(&db_path).unwrap(); + + send_not_enough_funds(OutputManagerSqliteDatabase::new(connection_pool)); +} + +fn send_no_change(backend: T) { + let factories = CryptoFactories::default(); + + let mut runtime = Runtime::new().unwrap(); + + let (mut oms, _, _shutdown, _) = setup_output_manager_service(&mut runtime, backend); + + let fee_per_gram = MicroTari::from(20); + let fee_without_change = Fee::calculate(fee_per_gram, 2, 1); + let key1 = PrivateKey::random(&mut OsRng); + let value1 = 500; + runtime + .block_on(oms.add_output(UnblindedOutput::new(MicroTari::from(value1), key1, None))) + .unwrap(); + let key2 = PrivateKey::random(&mut OsRng); + let value2 = 800; + runtime + .block_on(oms.add_output(UnblindedOutput::new(MicroTari::from(value2), key2, None))) + .unwrap(); + + let mut stp = runtime + .block_on(oms.prepare_transaction_to_send( + MicroTari::from(value1 + value2) - fee_without_change, + MicroTari::from(20), + None, + "".to_string(), + )) + .unwrap(); + + let sender_tx_id = stp.get_tx_id().unwrap(); + assert_eq!(stp.get_amount_to_self().unwrap(), MicroTari::from(0)); + assert_eq!(runtime.block_on(oms.get_pending_transactions()).unwrap().len(), 1); + + let msg = stp.build_single_round_message().unwrap(); + + let b = TestParams::new(&mut OsRng); + + let recv_info = + SingleReceiverTransactionProtocol::create(&msg, b.nonce, b.spend_key, OutputFeatures::default(), &factories) + .unwrap(); + + stp.add_single_recipient_info(recv_info.clone(), &factories.range_proof) + .unwrap(); + + stp.finalize(KernelFeatures::empty(), &factories).unwrap(); + + let tx = stp.get_transaction().unwrap(); + + runtime + .block_on(oms.confirm_transaction(sender_tx_id, tx.body.inputs().clone(), tx.body.outputs().clone())) + .unwrap(); + + assert_eq!(runtime.block_on(oms.get_pending_transactions()).unwrap().len(), 0); + assert_eq!( + runtime.block_on(oms.get_spent_outputs()).unwrap().len(), + tx.body.inputs().len() + ); + assert_eq!(runtime.block_on(oms.get_unspent_outputs()).unwrap().len(), 0); +} + +#[test] +fn send_no_change_memory_db() { + send_no_change(OutputManagerMemoryDatabase::new()); +} + +#[test] +fn send_no_change_sqlite_db() { + let db_name = format!("{}.sqlite3", random_string(8).as_str()); + let db_tempdir = TempDir::new(random_string(8).as_str()).unwrap(); + let db_folder = db_tempdir.path().to_str().unwrap().to_string(); + let db_path = format!("{}/{}", db_folder, db_name); + let connection_pool = run_migration_and_create_connection_pool(&db_path).unwrap(); + + send_no_change(OutputManagerSqliteDatabase::new(connection_pool)); +} + +fn send_not_enough_for_change(backend: T) { + let mut runtime = Runtime::new().unwrap(); + + let (mut oms, _, _shutdown, _) = setup_output_manager_service(&mut runtime, backend); + + let fee_per_gram = MicroTari::from(20); + let fee_without_change = Fee::calculate(fee_per_gram, 2, 1); + let key1 = PrivateKey::random(&mut OsRng); + let value1 = 500; + runtime + .block_on(oms.add_output(UnblindedOutput::new(MicroTari::from(value1), key1, None))) + .unwrap(); + let key2 = PrivateKey::random(&mut OsRng); + let value2 = 800; + runtime + .block_on(oms.add_output(UnblindedOutput::new(MicroTari::from(value2), key2, None))) + .unwrap(); + + match runtime.block_on(oms.prepare_transaction_to_send( + MicroTari::from(value1 + value2 + 1) - fee_without_change, + MicroTari::from(20), + None, + "".to_string(), + )) { + Err(OutputManagerError::NotEnoughFunds) => assert!(true), + _ => assert!(false), + } +} + +#[test] +fn send_not_enough_for_change_memory_db() { + send_not_enough_for_change(OutputManagerMemoryDatabase::new()); +} + +#[test] +fn send_not_enough_for_change_sqlite_db() { + let db_name = format!("{}.sqlite3", random_string(8).as_str()); + let db_tempdir = TempDir::new(random_string(8).as_str()).unwrap(); + let db_folder = db_tempdir.path().to_str().unwrap().to_string(); + let db_path = format!("{}/{}", db_folder, db_name); + let connection_pool = run_migration_and_create_connection_pool(&db_path).unwrap(); + + send_not_enough_for_change(OutputManagerSqliteDatabase::new(connection_pool)); +} + +fn receiving_and_confirmation(backend: T) { + let factories = CryptoFactories::default(); + + let mut runtime = Runtime::new().unwrap(); + + let (mut oms, _, _shutdown, _) = setup_output_manager_service(&mut runtime, backend); + + let value = MicroTari::from(5000); + let recv_key = runtime.block_on(oms.get_recipient_spending_key(1, value)).unwrap(); + assert_eq!(runtime.block_on(oms.get_unspent_outputs()).unwrap().len(), 0); + assert_eq!(runtime.block_on(oms.get_pending_transactions()).unwrap().len(), 1); + + let commitment = factories.commitment.commit(&recv_key, &value.into()); + let rr = factories.range_proof.construct_proof(&recv_key, value.into()).unwrap(); + let output = TransactionOutput::new( + OutputFeatures::create_coinbase(1), + commitment, + RangeProof::from_bytes(&rr).unwrap(), + ); + + runtime + .block_on(oms.confirm_transaction(1, vec![], vec![output])) + .unwrap(); + + assert_eq!(runtime.block_on(oms.get_pending_transactions()).unwrap().len(), 0); + assert_eq!(runtime.block_on(oms.get_unspent_outputs()).unwrap().len(), 1); +} + +#[test] +fn receiving_and_confirmation_memory_db() { + receiving_and_confirmation(OutputManagerMemoryDatabase::new()); +} + +#[test] +fn receiving_and_confirmation_sqlite_db() { + let db_name = format!("{}.sqlite3", random_string(8).as_str()); + let db_tempdir = TempDir::new(random_string(8).as_str()).unwrap(); + let db_folder = db_tempdir.path().to_str().unwrap().to_string(); + let db_path = format!("{}/{}", db_folder, db_name); + let connection_pool = run_migration_and_create_connection_pool(&db_path).unwrap(); + + receiving_and_confirmation(OutputManagerSqliteDatabase::new(connection_pool)); +} + +fn cancel_transaction(backend: T) { + let factories = CryptoFactories::default(); + + let mut runtime = Runtime::new().unwrap(); + + let (mut oms, _, _shutdown, _) = setup_output_manager_service(&mut runtime, backend); + + let num_outputs = 20; + for _i in 0..num_outputs { + let (_ti, uo) = make_input( + &mut OsRng.clone(), + MicroTari::from(100 + OsRng.next_u64() % 1000), + &factories.commitment, + ); + runtime.block_on(oms.add_output(uo)).unwrap(); + } + let stp = runtime + .block_on(oms.prepare_transaction_to_send(MicroTari::from(1000), MicroTari::from(20), None, "".to_string())) + .unwrap(); + + match runtime.block_on(oms.cancel_transaction(1)) { + Err(OutputManagerError::OutputManagerStorageError(OutputManagerStorageError::ValueNotFound(_))) => { + assert!(true) + }, + _ => assert!(false, "Value should not exist"), + } + + runtime + .block_on(oms.cancel_transaction(stp.get_tx_id().unwrap())) + .unwrap(); + + assert_eq!(runtime.block_on(oms.get_unspent_outputs()).unwrap().len(), num_outputs); +} + +#[test] +fn cancel_transaction_memory_db() { + cancel_transaction(OutputManagerMemoryDatabase::new()); +} + +#[test] +fn cancel_transaction_sqlite_db() { + let db_name = format!("{}.sqlite3", random_string(8).as_str()); + let db_tempdir = TempDir::new(random_string(8).as_str()).unwrap(); + let db_folder = db_tempdir.path().to_str().unwrap().to_string(); + let db_path = format!("{}/{}", db_folder, db_name); + let connection_pool = run_migration_and_create_connection_pool(&db_path).unwrap(); + cancel_transaction(OutputManagerSqliteDatabase::new(connection_pool)); +} + +fn timeout_transaction(backend: T) { + let factories = CryptoFactories::default(); + + let mut runtime = Runtime::new().unwrap(); + let (mut oms, _, _shutdown, _) = setup_output_manager_service(&mut runtime, backend); + + let num_outputs = 20; + for _i in 0..num_outputs { + let (_ti, uo) = make_input( + &mut OsRng.clone(), + MicroTari::from(100 + OsRng.next_u64() % 1000), + &factories.commitment, + ); + runtime.block_on(oms.add_output(uo)).unwrap(); + } + let _stp = runtime + .block_on(oms.prepare_transaction_to_send(MicroTari::from(1000), MicroTari::from(20), None, "".to_string())) + .unwrap(); + + let remaining_outputs = runtime.block_on(oms.get_unspent_outputs()).unwrap().len(); + + thread::sleep(Duration::from_millis(2)); + + runtime + .block_on(oms.timeout_transactions(Duration::from_millis(1000))) + .unwrap(); + + assert_eq!( + runtime.block_on(oms.get_unspent_outputs()).unwrap().len(), + remaining_outputs + ); + + runtime + .block_on(oms.timeout_transactions(Duration::from_millis(1))) + .unwrap(); + + assert_eq!(runtime.block_on(oms.get_unspent_outputs()).unwrap().len(), num_outputs); +} + +#[test] +fn timeout_transaction_memory_db() { + timeout_transaction(OutputManagerMemoryDatabase::new()); +} + +#[test] +fn timeout_transaction_sqlite_db() { + let db_name = format!("{}.sqlite3", random_string(8).as_str()); + let db_tempdir = TempDir::new(random_string(8).as_str()).unwrap(); + let db_folder = db_tempdir.path().to_str().unwrap().to_string(); + let db_path = format!("{}/{}", db_folder, db_name); + let connection_pool = run_migration_and_create_connection_pool(&db_path).unwrap(); + timeout_transaction(OutputManagerSqliteDatabase::new(connection_pool)); +} + +fn test_get_balance(backend: T) { + let factories = CryptoFactories::default(); + let mut runtime = Runtime::new().unwrap(); + + let (mut oms, _, _shutdown, _) = setup_output_manager_service(&mut runtime, backend); + + let balance = runtime.block_on(oms.get_balance()).unwrap(); + + assert_eq!(MicroTari::from(0), balance.available_balance); + + let mut total = MicroTari::from(0); + let output_val = MicroTari::from(2000); + let (_ti, uo) = make_input(&mut OsRng.clone(), output_val.clone(), &factories.commitment); + total += uo.value.clone(); + runtime.block_on(oms.add_output(uo)).unwrap(); + + let (_ti, uo) = make_input(&mut OsRng.clone(), output_val.clone(), &factories.commitment); + total += uo.value.clone(); + runtime.block_on(oms.add_output(uo)).unwrap(); + + let send_value = MicroTari::from(1000); + let stp = runtime + .block_on(oms.prepare_transaction_to_send(send_value.clone(), MicroTari::from(20), None, "".to_string())) + .unwrap(); + + let change_val = stp.get_change_amount().unwrap(); + + let recv_value = MicroTari::from(1500); + let _recv_key = runtime.block_on(oms.get_recipient_spending_key(1, recv_value)).unwrap(); + + let balance = runtime.block_on(oms.get_balance()).unwrap(); + + assert_eq!(output_val, balance.available_balance); + assert_eq!(recv_value + change_val, balance.pending_incoming_balance); + assert_eq!(output_val, balance.pending_outgoing_balance); +} + +#[test] +fn test_get_balance_memory_db() { + test_get_balance(OutputManagerMemoryDatabase::new()); +} + +#[test] +fn test_get_balance_sqlite_db() { + let db_name = format!("{}.sqlite3", random_string(8).as_str()); + let db_tempdir = TempDir::new(random_string(8).as_str()).unwrap(); + let db_folder = db_tempdir.path().to_str().unwrap().to_string(); + let db_path = format!("{}/{}", db_folder, db_name); + let connection_pool = run_migration_and_create_connection_pool(&db_path).unwrap(); + test_get_balance(OutputManagerSqliteDatabase::new(connection_pool)); +} + +fn test_confirming_received_output(backend: T) { + let factories = CryptoFactories::default(); + + let mut runtime = Runtime::new().unwrap(); + + let (mut oms, _, _shutdown, _) = setup_output_manager_service(&mut runtime, backend); + + let value = MicroTari::from(5000); + let recv_key = runtime.block_on(oms.get_recipient_spending_key(1, value)).unwrap(); + let commitment = factories.commitment.commit(&recv_key, &value.into()); + + let rr = factories.range_proof.construct_proof(&recv_key, value.into()).unwrap(); + let output = TransactionOutput::new( + OutputFeatures::create_coinbase(1), + commitment, + RangeProof::from_bytes(&rr).unwrap(), + ); + runtime + .block_on(oms.confirm_transaction(1, vec![], vec![output])) + .unwrap(); + assert_eq!(runtime.block_on(oms.get_balance()).unwrap().available_balance, value); +} + +#[test] +fn test_confirming_received_output_memory_db() { + test_confirming_received_output(OutputManagerMemoryDatabase::new()); +} + +#[test] +fn test_confirming_received_output_sqlite_db() { + let db_name = format!("{}.sqlite3", random_string(8).as_str()); + let db_tempdir = TempDir::new(random_string(8).as_str()).unwrap(); + let db_folder = db_tempdir.path().to_str().unwrap().to_string(); + let db_path = format!("{}/{}", db_folder, db_name); + let connection_pool = run_migration_and_create_connection_pool(&db_path).unwrap(); + test_confirming_received_output(OutputManagerSqliteDatabase::new(connection_pool)); +} + +#[test] +fn test_startup_utxo_scan() { + let factories = CryptoFactories::default(); + + let mut runtime = Runtime::new().unwrap(); + + let (mut oms, outbound_service, _shutdown, mut base_node_response_sender) = + setup_output_manager_service(&mut runtime, OutputManagerMemoryDatabase::new()); + let key1 = PrivateKey::random(&mut OsRng); + let value1 = 500; + let output1 = UnblindedOutput::new(MicroTari::from(value1), key1, None); + + runtime.block_on(oms.add_output(output1.clone())).unwrap(); + let key2 = PrivateKey::random(&mut OsRng); + let value2 = 800; + let output2 = UnblindedOutput::new(MicroTari::from(value2), key2, None); + runtime.block_on(oms.add_output(output2.clone())).unwrap(); + + let base_node_identity = NodeIdentity::random( + &mut OsRng, + "/ip4/127.0.0.1/tcp/58217".parse().unwrap(), + PeerFeatures::COMMUNICATION_NODE, + ) + .unwrap(); + + runtime + .block_on(oms.set_base_node_public_key(base_node_identity.public_key().clone())) + .unwrap(); + + let result_stream = runtime.block_on(async { + collect_stream!( + oms.get_event_stream_fused().map(|i| (*i).clone()), + take = 1, + timeout = Duration::from_secs(60) + ) + }); + + assert_eq!( + 1, + result_stream.iter().fold(0, |acc, item| { + if let OutputManagerEvent::BaseNodeSyncRequestTimedOut(_) = item { + acc + 1 + } else { + acc + } + }) + ); + + let call = outbound_service.pop_call().unwrap(); + let envelope_body = EnvelopeBody::decode(&mut call.1.as_slice()).unwrap(); + let bn_request: BaseNodeProto::BaseNodeServiceRequest = envelope_body + .decode_part::(1) + .unwrap() + .unwrap(); + + let invalid_txs = runtime.block_on(oms.get_invalid_outputs()).unwrap(); + assert_eq!(invalid_txs.len(), 0); + + let base_node_response = BaseNodeProto::BaseNodeServiceResponse { + request_key: 1, + response: Some(BaseNodeResponseProto::TransactionOutputs( + BaseNodeProto::TransactionOutputs { + outputs: vec![output1.clone().as_transaction_output(&factories).unwrap().into()].into(), + }, + )), + }; + + runtime + .block_on(base_node_response_sender.send(create_dummy_message( + base_node_response, + base_node_identity.public_key(), + ))) + .unwrap(); + + let invalid_txs = runtime.block_on(oms.get_invalid_outputs()).unwrap(); + assert_eq!(invalid_txs.len(), 0); + + let base_node_response = BaseNodeProto::BaseNodeServiceResponse { + request_key: bn_request.request_key.clone(), + response: Some(BaseNodeResponseProto::TransactionOutputs( + BaseNodeProto::TransactionOutputs { + outputs: vec![output1.clone().as_transaction_output(&factories).unwrap().into()].into(), + }, + )), + }; + + runtime + .block_on(base_node_response_sender.send(create_dummy_message( + base_node_response, + base_node_identity.public_key(), + ))) + .unwrap(); + + let result_stream = runtime.block_on(async { + collect_stream!( + oms.get_event_stream_fused().map(|i| (*i).clone()), + take = 2, + timeout = Duration::from_secs(60) + ) + }); + + assert_eq!( + 1, + result_stream.iter().fold(0, |acc, item| { + if let OutputManagerEvent::ReceiveBaseNodeResponse(_) = item { + acc + 1 + } else { + acc + } + }) + ); + + let invalid_outputs = runtime.block_on(oms.get_invalid_outputs()).unwrap(); + assert_eq!(invalid_outputs.len(), 1); + assert_eq!(invalid_outputs[0], output2); + + let unspent_outputs = runtime.block_on(oms.get_unspent_outputs()).unwrap(); + assert_eq!(unspent_outputs.len(), 1); + assert_eq!(unspent_outputs[0], output1); + + runtime.block_on(oms.sync_with_base_node()).unwrap(); + + let call = outbound_service.pop_call().unwrap(); + let envelope_body = EnvelopeBody::decode(&mut call.1.as_slice()).unwrap(); + let bn_request: BaseNodeProto::BaseNodeServiceRequest = envelope_body + .decode_part::(1) + .unwrap() + .unwrap(); + + let invalid_txs = runtime.block_on(oms.get_invalid_outputs()).unwrap(); + assert_eq!(invalid_txs.len(), 1); + + let base_node_response = BaseNodeProto::BaseNodeServiceResponse { + request_key: bn_request.request_key.clone(), + response: Some(BaseNodeResponseProto::TransactionOutputs( + BaseNodeProto::TransactionOutputs { outputs: vec![].into() }, + )), + }; + + runtime + .block_on(base_node_response_sender.send(create_dummy_message( + base_node_response, + base_node_identity.public_key(), + ))) + .unwrap(); + + let result_stream = runtime.block_on(async { + collect_stream!( + oms.get_event_stream_fused().map(|i| (*i).clone()), + take = 3, + timeout = Duration::from_secs(60) + ) + }); + + assert_eq!( + 2, + result_stream.iter().fold(0, |acc, item| { + if let OutputManagerEvent::ReceiveBaseNodeResponse(_) = item { + acc + 1 + } else { + acc + } + }) + ); + + let invalid_txs = runtime.block_on(oms.get_invalid_outputs()).unwrap(); + assert_eq!(invalid_txs.len(), 2); +} diff --git a/base_layer/wallet/tests/output_manager_service/storage.rs b/base_layer/wallet/tests/output_manager_service/storage.rs new file mode 100644 index 0000000000..0764153aa1 --- /dev/null +++ b/base_layer/wallet/tests/output_manager_service/storage.rs @@ -0,0 +1,348 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::support::utils::{make_input, random_string}; +use chrono::{Duration as ChronoDuration, Utc}; +use rand::{rngs::OsRng, RngCore}; +use std::time::Duration; +use tari_core::transactions::{ + tari_amount::MicroTari, + transaction::OutputFeatures, + types::{CryptoFactories, PrivateKey}, +}; +use tari_crypto::keys::SecretKey; +use tari_wallet::{ + output_manager_service::{ + service::Balance, + storage::{ + database::{KeyManagerState, OutputManagerBackend, OutputManagerDatabase, PendingTransactionOutputs}, + memory_db::OutputManagerMemoryDatabase, + sqlite_db::OutputManagerSqliteDatabase, + }, + }, + storage::connection_manager::run_migration_and_create_connection_pool, +}; +use tempdir::TempDir; +use tokio::runtime::Runtime; + +pub fn test_db_backend(backend: T) { + let mut runtime = Runtime::new().unwrap(); + let db = OutputManagerDatabase::new(backend); + let factories = CryptoFactories::default(); + + // Add some unspent outputs + let mut unspent_outputs = Vec::new(); + for _ in 0..5 { + let (_ti, uo) = make_input( + &mut OsRng, + MicroTari::from(100 + OsRng.next_u64() % 1000), + &factories.commitment, + ); + runtime.block_on(db.add_unspent_output(uo.clone())).unwrap(); + unspent_outputs.push(uo); + } + unspent_outputs.sort(); + // Add some pending transactions + let mut pending_txs = Vec::new(); + for i in 0..3 { + let mut pending_tx = PendingTransactionOutputs { + tx_id: OsRng.next_u64(), + outputs_to_be_spent: vec![], + outputs_to_be_received: vec![], + timestamp: Utc::now().naive_utc() - + ChronoDuration::from_std(Duration::from_millis(120_000_000 * i)).unwrap(), + }; + for _ in 0..(OsRng.next_u64() % 5 + 1) { + let (_ti, uo) = make_input( + &mut OsRng, + MicroTari::from(100 + OsRng.next_u64() % 1000), + &factories.commitment, + ); + pending_tx.outputs_to_be_spent.push(uo); + } + for _ in 0..(OsRng.next_u64() % 5 + 1) { + let (_ti, uo) = make_input( + &mut OsRng, + MicroTari::from(100 + OsRng.next_u64() % 1000), + &factories.commitment, + ); + pending_tx.outputs_to_be_received.push(uo); + } + runtime + .block_on(db.add_pending_transaction_outputs(pending_tx.clone())) + .unwrap(); + pending_txs.push(pending_tx); + } + + let outputs = runtime.block_on(db.fetch_sorted_unspent_outputs()).unwrap(); + assert_eq!(unspent_outputs, outputs); + + let p_tx = runtime.block_on(db.fetch_all_pending_transaction_outputs()).unwrap(); + + for (k, v) in p_tx.iter() { + assert_eq!(v, pending_txs.iter().find(|i| &i.tx_id == k).unwrap()); + } + + assert_eq!( + runtime + .block_on(db.fetch_pending_transaction_outputs(pending_txs[0].tx_id)) + .unwrap(), + pending_txs[0] + ); + + // Test balance calc + let mut available_balance = unspent_outputs.iter().fold(MicroTari::from(0), |acc, x| acc + x.value); + let mut pending_incoming_balance = MicroTari(0); + let mut pending_outgoing_balance = MicroTari(0); + for v in pending_txs.iter() { + pending_outgoing_balance += v + .outputs_to_be_spent + .iter() + .fold(MicroTari::from(0), |acc, x| acc + x.value); + pending_incoming_balance += v + .outputs_to_be_received + .iter() + .fold(MicroTari::from(0), |acc, x| acc + x.value); + } + + let balance = runtime.block_on(db.get_balance()).unwrap(); + assert_eq!(balance, Balance { + available_balance, + pending_incoming_balance, + pending_outgoing_balance + }); + + runtime + .block_on(db.confirm_pending_transaction_outputs(pending_txs[0].tx_id)) + .unwrap(); + + available_balance += pending_txs[0] + .outputs_to_be_received + .iter() + .fold(MicroTari::from(0), |acc, x| acc + x.value); + + pending_incoming_balance -= pending_txs[0] + .outputs_to_be_received + .iter() + .fold(MicroTari::from(0), |acc, x| acc + x.value); + + pending_outgoing_balance -= pending_txs[0] + .outputs_to_be_spent + .iter() + .fold(MicroTari::from(0), |acc, x| acc + x.value); + + let balance = runtime.block_on(db.get_balance()).unwrap(); + assert_eq!(balance, Balance { + available_balance, + pending_incoming_balance, + pending_outgoing_balance + }); + + let spent_outputs = runtime.block_on(db.fetch_spent_outputs()).unwrap(); + + assert!(spent_outputs.len() > 0); + assert_eq!( + spent_outputs.iter().fold(MicroTari::from(0), |acc, x| acc + x.value), + pending_txs[0] + .outputs_to_be_spent + .iter() + .fold(MicroTari::from(0), |acc, x| acc + x.value) + ); + + let (_ti, uo_change) = make_input( + &mut OsRng.clone(), + MicroTari::from(100 + OsRng.next_u64() % 1000), + &factories.commitment, + ); + let outputs_to_encumber = vec![outputs[0].clone(), outputs[1].clone()]; + let total_encumbered = outputs[0].clone().value + outputs[1].clone().value; + runtime + .block_on(db.encumber_outputs(2, outputs_to_encumber, Some(uo_change.clone()))) + .unwrap(); + + available_balance -= total_encumbered; + pending_incoming_balance += uo_change.clone().value; + pending_outgoing_balance += total_encumbered; + + let balance = runtime.block_on(db.get_balance()).unwrap(); + assert_eq!(balance, Balance { + available_balance, + pending_incoming_balance, + pending_outgoing_balance + }); + + let (_ti, uo_incoming) = make_input( + &mut OsRng.clone(), + MicroTari::from(100 + OsRng.next_u64() % 1000), + &factories.commitment, + ); + runtime + .block_on(db.accept_incoming_pending_transaction( + 5, + uo_incoming.value, + uo_incoming.spending_key.clone(), + OutputFeatures::default(), + )) + .unwrap(); + + pending_incoming_balance += uo_incoming.clone().value; + + let balance = runtime.block_on(db.get_balance()).unwrap(); + assert_eq!(balance, Balance { + available_balance, + pending_incoming_balance, + pending_outgoing_balance + }); + + runtime + .block_on(db.cancel_pending_transaction_outputs(pending_txs[1].tx_id)) + .unwrap(); + + let mut cancelled_incoming = MicroTari(0); + let mut cancelled_outgoing = MicroTari(0); + + cancelled_outgoing += pending_txs[1] + .outputs_to_be_spent + .iter() + .fold(MicroTari::from(0), |acc, x| acc + x.value); + cancelled_incoming += pending_txs[1] + .outputs_to_be_received + .iter() + .fold(MicroTari::from(0), |acc, x| acc + x.value); + + available_balance += cancelled_outgoing; + pending_incoming_balance -= cancelled_incoming; + pending_outgoing_balance -= cancelled_outgoing; + + let balance = runtime.block_on(db.get_balance()).unwrap(); + assert_eq!(balance, Balance { + available_balance, + pending_incoming_balance, + pending_outgoing_balance + }); + + let remaining_p_tx = runtime.block_on(db.fetch_all_pending_transaction_outputs()).unwrap(); + + runtime + .block_on(db.timeout_pending_transaction_outputs(Duration::from_millis(120_000_000_000))) + .unwrap(); + + assert_eq!( + runtime + .block_on(db.fetch_all_pending_transaction_outputs()) + .unwrap() + .len(), + remaining_p_tx.len() + ); + + runtime + .block_on(db.timeout_pending_transaction_outputs(Duration::from_millis(6_000_000))) + .unwrap(); + + assert_eq!( + runtime + .block_on(db.fetch_all_pending_transaction_outputs()) + .unwrap() + .len(), + remaining_p_tx.len() - 1 + ); + + assert!(!runtime + .block_on(db.fetch_all_pending_transaction_outputs()) + .unwrap() + .contains_key(&pending_txs[2].tx_id)); + + // Test invalidating an output + let invalid_outputs = runtime.block_on(db.get_invalid_outputs()).unwrap(); + assert_eq!(invalid_outputs.len(), 0); + let unspent_outputs = runtime.block_on(db.get_unspent_outputs()).unwrap(); + runtime + .block_on(db.invalidate_output(unspent_outputs[0].clone())) + .unwrap(); + let invalid_outputs = runtime.block_on(db.get_invalid_outputs()).unwrap(); + assert_eq!(invalid_outputs.len(), 1); + assert_eq!(invalid_outputs[0], unspent_outputs[0]); +} + +#[test] +pub fn test_output_manager_memory_db() { + test_db_backend(OutputManagerMemoryDatabase::new()); +} + +#[test] +pub fn test_output_manager_sqlite_db() { + let db_name = format!("{}.sqlite3", random_string(8).as_str()); + let temp_dir = TempDir::new(random_string(8).as_str()).unwrap(); + let db_folder = temp_dir.path().to_str().unwrap().to_string(); + let connection_pool = run_migration_and_create_connection_pool(&format!("{}/{}", db_folder, db_name)).unwrap(); + test_db_backend(OutputManagerSqliteDatabase::new(connection_pool)); +} + +pub fn test_key_manager_crud(backend: T) { + let mut runtime = Runtime::new().unwrap(); + + let db = OutputManagerDatabase::new(backend); + + assert_eq!(runtime.block_on(db.get_key_manager_state()).unwrap(), None); + assert!(runtime.block_on(db.increment_key_index()).is_err()); + + let state1 = KeyManagerState { + master_seed: PrivateKey::random(&mut OsRng), + branch_seed: "blah".to_string(), + primary_key_index: 0, + }; + + runtime.block_on(db.set_key_manager_state(state1.clone())).unwrap(); + + let read_state1 = runtime.block_on(db.get_key_manager_state()).unwrap().unwrap(); + assert_eq!(state1, read_state1); + + let state2 = KeyManagerState { + master_seed: PrivateKey::random(&mut OsRng), + branch_seed: "blah2".to_string(), + primary_key_index: 0, + }; + + runtime.block_on(db.set_key_manager_state(state2.clone())).unwrap(); + + let read_state2 = runtime.block_on(db.get_key_manager_state()).unwrap().unwrap(); + assert_eq!(state2, read_state2); + + runtime.block_on(db.increment_key_index()).unwrap(); + runtime.block_on(db.increment_key_index()).unwrap(); + + let read_state3 = runtime.block_on(db.get_key_manager_state()).unwrap().unwrap(); + assert_eq!(read_state3.primary_key_index, 2); +} +#[test] +pub fn test_key_manager_crud_memory_db() { + test_key_manager_crud(OutputManagerMemoryDatabase::new()); +} + +#[test] +pub fn test_key_manager_crud_sqlite_db() { + let db_name = format!("{}.sqlite3", random_string(8).as_str()); + let temp_dir = TempDir::new(random_string(8).as_str()).unwrap(); + let db_folder = temp_dir.path().to_str().unwrap().to_string(); + let connection_pool = run_migration_and_create_connection_pool(&format!("{}/{}", db_folder, db_name)).unwrap(); + test_key_manager_crud(OutputManagerSqliteDatabase::new(connection_pool)); +} diff --git a/base_layer/wallet/tests/support/comms_and_services.rs b/base_layer/wallet/tests/support/comms_and_services.rs index 32ca3c00af..2365698383 100644 --- a/base_layer/wallet/tests/support/comms_and_services.rs +++ b/base_layer/wallet/tests/support/comms_and_services.rs @@ -20,49 +20,81 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::{sync::Arc, time::Duration}; +use futures::Sink; +use std::{error::Error, sync::Arc, time::Duration}; use tari_comms::{ - builder::CommsServices, - connection_manager::PeerConnectionConfig, - control_service::ControlServiceConfig, - peer_manager::{NodeIdentity, Peer}, - CommsBuilder, + multiaddr::Multiaddr, + peer_manager::{NodeId, NodeIdentity, Peer, PeerFeatures, PeerFlags}, + transports::MemoryTransport, + types::CommsPublicKey, + CommsNode, }; -use tari_p2p::tari_message::TariMessageType; -use tari_storage::{lmdb_store::LMDBDatabase, LMDBWrapper}; -pub fn setup_comms_services( - node_identity: NodeIdentity, - peers: Vec, - peer_database: LMDBDatabase, -) -> CommsServices +use tari_comms_dht::{ + envelope::{DhtMessageHeader, Network}, + Dht, +}; +use tari_p2p::{ + comms_connector::{InboundDomainConnector, PeerMessage}, + domain_message::DomainMessage, + initialization::initialize_local_test_comms, +}; + +pub fn get_next_memory_address() -> Multiaddr { + let port = MemoryTransport::acquire_next_memsocket_port(); + format!("/memory/{}", port).parse().unwrap() +} + +pub async fn setup_comms_services( + node_identity: Arc, + peers: Vec>, + publisher: InboundDomainConnector, + database_path: String, + discovery_request_timeout: Duration, +) -> (CommsNode, Dht) +where + TSink: Sink> + Clone + Unpin + Send + Sync + 'static, + TSink::Error: Error + Send + Sync, { - let peer_database = LMDBWrapper::new(Arc::new(peer_database)); - let comms = CommsBuilder::new() - .with_node_identity(node_identity.clone()) - .with_peer_storage(peer_database) - .configure_peer_connections(PeerConnectionConfig { - host: "127.0.0.1".parse().unwrap(), - ..Default::default() - }) - .configure_control_service(ControlServiceConfig { - socks_proxy_address: None, - listener_address: node_identity.control_service_address().unwrap(), - requested_connection_timeout: Duration::from_millis(5000), - }) - .build() - .unwrap() - .start() + let (comms, dht) = initialize_local_test_comms(node_identity, publisher, &database_path, discovery_request_timeout) + .await .unwrap(); for p in peers { + let addr = p.public_address(); comms - .peer_manager() - .add_peer( - Peer::from_public_key_and_address(p.identity.public_key.clone(), p.control_service_address().unwrap()) - .unwrap(), - ) + .async_peer_manager() + .add_peer(Peer::new( + p.public_key().clone(), + p.node_id().clone(), + addr.into(), + PeerFlags::empty(), + PeerFeatures::COMMUNICATION_NODE, + )) + .await .unwrap(); } - comms + (comms, dht) +} + +pub fn create_dummy_message(inner: T, public_key: &CommsPublicKey) -> DomainMessage { + let peer_source = Peer::new( + public_key.clone(), + NodeId::from_key(public_key).unwrap(), + Vec::::new().into(), + PeerFlags::empty(), + PeerFeatures::COMMUNICATION_NODE, + ); + DomainMessage { + dht_header: DhtMessageHeader { + origin: None, + version: Default::default(), + message_type: Default::default(), + flags: Default::default(), + network: Network::LocalTest, + destination: Default::default(), + }, + source_peer: peer_source, + inner, + } } diff --git a/base_layer/wallet/tests/support/data.rs b/base_layer/wallet/tests/support/data.rs index 9b2bcf5c57..083e97cd12 100644 --- a/base_layer/wallet/tests/support/data.rs +++ b/base_layer/wallet/tests/support/data.rs @@ -21,7 +21,6 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use std::path::PathBuf; -use tari_storage::lmdb_store::{LMDBBuilder, LMDBError, LMDBStore}; pub fn get_path(name: Option<&str>) -> String { let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); @@ -30,21 +29,6 @@ pub fn get_path(name: Option<&str>) -> String { path.to_str().unwrap().to_string() } -pub fn init_datastore(name: &str) -> Result { - let path = get_path(Some(name)); - let _ = std::fs::create_dir(&path).unwrap_or_default(); - LMDBBuilder::new() - .set_path(&path) - .set_environment_size(10) - .set_max_number_of_databases(1) - .add_database(name, lmdb_zero::db::CREATE) - .build() -} - -pub fn clean_up_datastore(name: &str) { - std::fs::remove_dir_all(get_path(Some(name))).unwrap(); -} - pub fn clean_up_sql_database(name: &str) { if std::fs::metadata(get_path(Some(name))).is_ok() { std::fs::remove_file(get_path(Some(name))).unwrap(); diff --git a/base_layer/wallet/tests/support/utils.rs b/base_layer/wallet/tests/support/utils.rs index 4067d22b24..70dcae1351 100644 --- a/base_layer/wallet/tests/support/utils.rs +++ b/base_layer/wallet/tests/support/utils.rs @@ -20,20 +20,21 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use rand::{CryptoRng, Rng}; -use std::{fmt::Debug, thread, time::Duration}; -use tari_core::{ +use rand::{distributions::Alphanumeric, rngs::OsRng, CryptoRng, Rng}; +use std::{fmt::Debug, iter, thread, time::Duration}; +use tari_core::transactions::{ tari_amount::MicroTari, transaction::{OutputFeatures, TransactionInput, UnblindedOutput}, - types::{PrivateKey, PublicKey, COMMITMENT_FACTORY}, + types::{CommitmentFactory, PrivateKey, PublicKey}, }; use tari_crypto::{ commitment::HomomorphicCommitmentFactory, keys::{PublicKey as PublicKeyTrait, SecretKey as SecretKeyTrait}, }; -pub fn assert_change(func: F, to: T, poll_count: usize) + +pub fn assert_change(mut func: F, to: T, poll_count: usize) where - F: Fn() -> T, + F: FnMut() -> T, T: Eq + Debug, { let mut i = 0; @@ -64,7 +65,6 @@ pub struct TestParams { pub nonce: PrivateKey, pub public_nonce: PublicKey, } - impl TestParams { pub fn new(rng: &mut R) -> TestParams { let r = PrivateKey::random(rng); @@ -77,10 +77,18 @@ impl TestParams { } } } - -pub fn make_input(rng: &mut R, val: MicroTari) -> (TransactionInput, UnblindedOutput) { +pub fn make_input( + rng: &mut R, + val: MicroTari, + factory: &CommitmentFactory, +) -> (TransactionInput, UnblindedOutput) +{ let key = PrivateKey::random(rng); - let commitment = COMMITMENT_FACTORY.commit_value(&key, val.into()); + let commitment = factory.commit_value(&key, val.into()); let input = TransactionInput::new(OutputFeatures::default(), commitment); (input, UnblindedOutput::new(val, key, None)) } + +pub fn random_string(len: usize) -> String { + iter::repeat(()).map(|_| OsRng.sample(Alphanumeric)).take(len).collect() +} diff --git a/base_layer/wallet/tests/text_message_service/mod.rs b/base_layer/wallet/tests/text_message_service/mod.rs index 20f3070ff0..8e9cb8baba 100644 --- a/base_layer/wallet/tests/text_message_service/mod.rs +++ b/base_layer/wallet/tests/text_message_service/mod.rs @@ -20,54 +20,75 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::support::{comms_and_services::setup_comms_services, data::*, utils::assert_change}; -use std::sync::Arc; -use tari_comms::{builder::CommsServices, peer_manager::NodeIdentity}; +use crate::support::{comms_and_services::setup_comms_services, data::*, utils::event_stream_count}; +use std::{sync::Arc, time::Duration}; +use tari_comms::{ + builder::CommsNode, + peer_manager::{NodeIdentity, PeerFeatures}, +}; +use tari_comms_dht::Dht; use tari_p2p::{ - sync_services::{ServiceExecutor, ServiceRegistry}, - tari_message::TariMessageType, + comms_connector::pubsub_connector, + services::{comms_outbound::CommsOutboundServiceInitializer, liveness::LivenessInitializer}, +}; +use tari_service_framework::StackBuilder; +use tari_wallet::text_message_service::{ + handle::{TextMessageEvent, TextMessageHandle}, + model::{Contact, UpdateContact}, + TextMessageServiceInitializer, }; -use tari_storage::lmdb_store::LMDBDatabase; -use tari_wallet::text_message_service::{Contact, TextMessageService, TextMessageServiceApi}; +use tokio::runtime::Runtime; pub fn setup_text_message_service( + runtime: &Runtime, node_identity: NodeIdentity, peers: Vec, - peer_database: LMDBDatabase, database_path: String, -) -> ( - ServiceExecutor, - Arc, - CommsServices, -) +) -> (TextMessageHandle, CommsNode, Dht) { - let tms = TextMessageService::new(node_identity.identity.public_key.clone(), database_path); - let tms_api = tms.get_api(); + let (publisher, subscription_factory) = pubsub_connector(runtime.executor(), 100); + let subscription_factory = Arc::new(subscription_factory); + let (comms, dht) = setup_comms_services(runtime.executor(), Arc::new(node_identity.clone()), peers, publisher); + + let fut = StackBuilder::new(runtime.executor()) + .add_initializer(CommsOutboundServiceInitializer::new(dht.outbound_requester())) + .add_initializer(LivenessInitializer::new(Arc::clone(&subscription_factory))) + .add_initializer(TextMessageServiceInitializer::new( + subscription_factory, + node_identity.identity.public_key.clone(), + database_path, + )) + .finish(); - let services = ServiceRegistry::new().register(tms); + let handles = runtime.block_on(fut).expect("Service initialization failed"); - let comms = setup_comms_services(node_identity, peers, peer_database); + let tms_api = handles.get_handle::().unwrap(); - (ServiceExecutor::execute(&comms, services), tms_api, comms) + (tms_api, comms, dht) } #[test] fn test_text_message_service() { - let mut rng = rand::OsRng::new().unwrap(); - - let node_1_identity = NodeIdentity::random(&mut rng, "127.0.0.1:31523".parse().unwrap()).unwrap(); - let node_2_identity = NodeIdentity::random(&mut rng, "127.0.0.1:31145".parse().unwrap()).unwrap(); - let node_3_identity = NodeIdentity::random(&mut rng, "127.0.0.1:31546".parse().unwrap()).unwrap(); - - let node_1_database_name = "node_1_test_text_message_service"; // Note: every test should have unique database - let node_1_datastore = init_datastore(node_1_database_name).unwrap(); - let node_1_peer_database = node_1_datastore.get_handle(node_1_database_name).unwrap(); - let node_2_database_name = "node_2_test_text_message_service"; // Note: every test should have unique database - let node_2_datastore = init_datastore(node_2_database_name).unwrap(); - let node_2_peer_database = node_2_datastore.get_handle(node_2_database_name).unwrap(); - let node_3_database_name = "node_3_test_text_message_service"; // Note: every test should have unique database - let node_3_datastore = init_datastore(node_3_database_name).unwrap(); - let node_3_peer_database = node_3_datastore.get_handle(node_3_database_name).unwrap(); + let runtime = Runtime::new().unwrap(); + + let node_1_identity = NodeIdentity::random( + &mut OsRng, + "127.0.0.1:31523".parse().unwrap(), + PeerFeatures::communication_node_default(), + ) + .unwrap(); + let node_2_identity = NodeIdentity::random( + &mut OsRng, + "127.0.0.1:31145".parse().unwrap(), + PeerFeatures::communication_node_default(), + ) + .unwrap(); + let node_3_identity = NodeIdentity::random( + &mut OsRng, + "127.0.0.1:31546".parse().unwrap(), + PeerFeatures::communication_node_default(), + ) + .unwrap(); let db_name1 = "test_text_message_service1.sqlite3"; let db_path1 = get_path(Some(db_name1)); @@ -81,117 +102,109 @@ fn test_text_message_service() { let db_path3 = get_path(Some(db_name3)); init_sql_database(db_name3); - let (node_1_services, node_1_tms, _comms_1) = setup_text_message_service( + let (mut node_1_tms, _comms_1, _dht_1) = setup_text_message_service( + &runtime, node_1_identity.clone(), vec![node_2_identity.clone(), node_3_identity.clone()], - node_1_peer_database, db_path1, ); - let (node_2_services, node_2_tms, _comms_2) = setup_text_message_service( + let (mut node_2_tms, _comms_2, _dht_2) = setup_text_message_service( + &runtime, node_2_identity.clone(), vec![node_1_identity.clone()], - node_2_peer_database, db_path2, ); - let (node_3_services, node_3_tms, _comms_3) = setup_text_message_service( + let (mut node_3_tms, _comms_3, _dht_3) = setup_text_message_service( + &runtime, node_3_identity.clone(), vec![node_1_identity.clone()], - node_3_peer_database, db_path3, ); - node_1_tms - .add_contact(Contact::new( + runtime + .block_on(node_1_tms.add_contact(Contact::new( "Bob".to_string(), node_2_identity.identity.public_key.clone(), - node_2_identity.control_service_address().unwrap(), - )) + node_2_identity.control_service_address(), + ))) .unwrap(); - node_1_tms - .add_contact(Contact::new( + + runtime + .block_on(node_1_tms.add_contact(Contact::new( "Carol".to_string(), node_3_identity.identity.public_key.clone(), - node_3_identity.control_service_address().unwrap(), - )) + node_3_identity.control_service_address(), + ))) .unwrap(); - node_2_tms - .add_contact(Contact::new( + runtime + .block_on(node_2_tms.add_contact(Contact::new( "Alice".to_string(), node_1_identity.identity.public_key.clone(), - node_1_identity.control_service_address().unwrap(), - )) + node_1_identity.control_service_address(), + ))) .unwrap(); - node_3_tms - .add_contact(Contact::new( + runtime + .block_on(node_3_tms.add_contact(Contact::new( "Alice".to_string(), node_1_identity.identity.public_key.clone(), - node_1_identity.control_service_address().unwrap(), - )) + node_1_identity.control_service_address(), + ))) .unwrap(); - let mut node1_to_node2_sent_messages = vec!["Say Hello".to_string(), "to my little friend!".to_string()]; - node_1_tms - .send_text_message( + runtime + .block_on(node_1_tms.send_text_message( node_2_identity.identity.public_key.clone(), node1_to_node2_sent_messages[0].clone(), - ) + )) .unwrap(); - node_1_tms - .send_text_message(node_3_identity.identity.public_key.clone(), "Say Hello".to_string()) + + runtime + .block_on(node_1_tms.send_text_message(node_3_identity.identity.public_key.clone(), "Say Hello".to_string())) .unwrap(); - node_2_tms - .send_text_message(node_1_identity.identity.public_key.clone(), "hello?".to_string()) + runtime + .block_on(node_2_tms.send_text_message(node_1_identity.identity.public_key.clone(), "hello?".to_string())) .unwrap(); - node_1_tms - .send_text_message( + runtime + .block_on(node_1_tms.send_text_message( node_2_identity.identity.public_key.clone(), node1_to_node2_sent_messages[1].clone(), - ) + )) .unwrap(); for i in 0..3 { node1_to_node2_sent_messages.push(format!("Message {}", i).to_string()); - node_1_tms - .send_text_message( + runtime + .block_on(node_1_tms.send_text_message( node_2_identity.identity.public_key.clone(), node1_to_node2_sent_messages[2 + i].clone(), - ) + )) .unwrap(); } for i in 0..3 { - node_2_tms - .send_text_message( + runtime + .block_on(node_2_tms.send_text_message( node_1_identity.identity.public_key.clone(), format!("Message {}", i).to_string(), - ) + )) .unwrap(); } - assert_change( - || { - let msgs = node_1_tms.get_text_messages().unwrap(); + let mut result = runtime + .block_on(async { event_stream_count(node_1_tms.get_event_stream_fused(), 10, Duration::from_secs(10)).await }); + assert_eq!(result.remove(&TextMessageEvent::ReceivedTextMessage), Some(4)); + assert_eq!(result.remove(&TextMessageEvent::ReceivedTextMessageAck), Some(6)); - (msgs.sent_messages.len(), msgs.received_messages.len()) - }, - (6, 4), - 50, - ); + let mut result = runtime + .block_on(async { event_stream_count(node_2_tms.get_event_stream_fused(), 9, Duration::from_secs(10)).await }); + assert_eq!(result.remove(&TextMessageEvent::ReceivedTextMessage), Some(5)); + assert_eq!(result.remove(&TextMessageEvent::ReceivedTextMessageAck), Some(4)); - assert_change( - || { - let msgs = node_2_tms.get_text_messages().unwrap(); - (msgs.sent_messages.len(), msgs.received_messages.len()) - }, - (4, 5), - 50, - ); - - let node1_msgs = node_1_tms - .get_text_messages_by_pub_key(node_2_identity.identity.public_key) + let node1_msgs = runtime + .block_on(node_1_tms.get_text_messages_by_pub_key(node_2_identity.identity.public_key)) .unwrap(); assert_eq!(node1_msgs.sent_messages.len(), node1_to_node2_sent_messages.len()); @@ -199,8 +212,8 @@ fn test_text_message_service() { assert_eq!(node1_msgs.sent_messages[i].message, node1_to_node2_sent_messages[i]); } - let node2_msgs = node_2_tms - .get_text_messages_by_pub_key(node_1_identity.identity.public_key) + let node2_msgs = runtime + .block_on(node_2_tms.get_text_messages_by_pub_key(node_1_identity.identity.public_key)) .unwrap(); assert_eq!(node2_msgs.received_messages.len(), node1_to_node2_sent_messages.len()); @@ -208,13 +221,92 @@ fn test_text_message_service() { assert_eq!(node2_msgs.received_messages[i].message, node1_to_node2_sent_messages[i]); } - node_1_services.shutdown().unwrap(); - node_2_services.shutdown().unwrap(); - node_3_services.shutdown().unwrap(); - clean_up_datastore(node_1_database_name); - clean_up_datastore(node_2_database_name); - clean_up_datastore(node_3_database_name); clean_up_sql_database(db_name1); clean_up_sql_database(db_name2); clean_up_sql_database(db_name3); } + +#[test] +fn test_text_message_requester_crud() { + let runtime = Runtime::new().unwrap(); + let node_1_identity = NodeIdentity::random( + &mut OsRng, + "127.0.0.1:30123".parse().unwrap(), + PeerFeatures::communication_node_default(), + ) + .unwrap(); + let node_3_identity = NodeIdentity::random( + &mut OsRng, + "127.0.0.1:30546".parse().unwrap(), + PeerFeatures::communication_node_default(), + ) + .unwrap(); + + // Note: every test should have unique database + let db_name1 = "test_tms_crud1.sqlite3"; + let db_path1 = get_path(Some(db_name1)); + init_sql_database(db_name1); + + let (mut node_1_tms, _comms_1, _dht_1) = setup_text_message_service( + &runtime, + node_1_identity.clone(), + vec![node_3_identity.clone()], + db_path1, + ); + + runtime + .block_on(node_1_tms.set_screen_name("Alice".to_string())) + .unwrap(); + + let sn = runtime.block_on(node_1_tms.get_screen_name()).unwrap(); + + assert_eq!(sn, Some("Alice".to_string())); + + runtime + .block_on(node_1_tms.add_contact(Contact::new( + "Carol".to_string(), + node_3_identity.identity.public_key.clone(), + node_3_identity.control_service_address(), + ))) + .unwrap(); + + assert!(runtime + .block_on(node_1_tms.add_contact(Contact::new( + "Carol".to_string(), + node_3_identity.identity.public_key.clone(), + node_3_identity.control_service_address(), + ))) + .is_err()); + + let contacts = runtime.block_on(node_1_tms.get_contacts()).unwrap(); + assert_eq!(contacts.len(), 1); + assert_eq!(contacts[0].screen_name, "Carol".to_string()); + + runtime + .block_on( + node_1_tms.update_contact(node_3_identity.identity.public_key.clone(), UpdateContact { + screen_name: Some("Dave".to_string()), + address: None, + }), + ) + .unwrap(); + + let contacts = runtime.block_on(node_1_tms.get_contacts()).unwrap(); + + assert_eq!(contacts.len(), 1); + assert_eq!(contacts[0].screen_name, "Dave".to_string()); + + runtime + .block_on(node_1_tms.remove_contact(Contact::new( + "Dave".to_string(), + node_3_identity.identity.public_key.clone(), + node_3_identity.control_service_address(), + ))) + .unwrap(); + + let contacts = runtime.block_on(node_1_tms.get_contacts()).unwrap(); + + assert_eq!(contacts.len(), 0); + + clean_up_sql_database(db_name1); +} diff --git a/base_layer/wallet/tests/transaction_service/mod.rs b/base_layer/wallet/tests/transaction_service/mod.rs index 8aaf20a27a..4e344eb9f5 100644 --- a/base_layer/wallet/tests/transaction_service/mod.rs +++ b/base_layer/wallet/tests/transaction_service/mod.rs @@ -19,294 +19,6 @@ // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::support::{ - comms_and_services::setup_comms_services, - data::{clean_up_datastore, init_datastore}, - utils::assert_change, -}; -use log::Level; -use rand::{CryptoRng, OsRng, Rng}; -use std::sync::Arc; -use tari_comms::{builder::CommsServices, peer_manager::NodeIdentity}; -use tari_core::{ - tari_amount::*, - transaction::{OutputFeatures, TransactionInput, UnblindedOutput}, - transaction_protocol::recipient::RecipientState, - types::{PrivateKey, PublicKey, COMMITMENT_FACTORY}, -}; -use tari_crypto::{ - commitment::HomomorphicCommitmentFactory, - keys::{PublicKey as PK, SecretKey as SK}, -}; -use tari_p2p::{ - sync_services::{ServiceExecutor, ServiceRegistry}, - tari_message::TariMessageType, -}; -use tari_storage::lmdb_store::LMDBDatabase; -use tari_wallet::{ - output_manager_service::output_manager_service::{OutputManagerService, OutputManagerServiceApi}, - transaction_service::{TransactionService, TransactionServiceApi}, -}; -pub fn setup_transaction_service( - seed_key: PrivateKey, - node_identity: NodeIdentity, - peers: Vec, - peer_database: LMDBDatabase, -) -> ( - ServiceExecutor, - Arc, - Arc, - CommsServices, -) -{ - let output_manager = OutputManagerService::new(seed_key, "".to_string(), 0); - let output_manager_api = output_manager.get_api(); - let tx_service = TransactionService::new(output_manager_api.clone()); - let tx_service_api = tx_service.get_api(); - let services = ServiceRegistry::new().register(tx_service).register(output_manager); - let comms = setup_comms_services(node_identity, peers, peer_database); - ( - ServiceExecutor::execute(&comms, services), - tx_service_api, - output_manager_api, - comms, - ) -} -pub fn make_input(rng: &mut R, val: MicroTari) -> (TransactionInput, UnblindedOutput) { - let key = PrivateKey::random(rng); - let commitment = COMMITMENT_FACTORY.commit_value(&key, val.into()); - let input = TransactionInput::new(OutputFeatures::default(), commitment); - (input, UnblindedOutput::new(val, key, None)) -} -pub struct TestParams { - pub spend_key: PrivateKey, - pub change_key: PrivateKey, - pub offset: PrivateKey, - pub nonce: PrivateKey, - pub public_nonce: PublicKey, -} -impl TestParams { - pub fn new(rng: &mut R) -> TestParams { - let r = PrivateKey::random(rng); - TestParams { - spend_key: PrivateKey::random(rng), - change_key: PrivateKey::random(rng), - offset: PrivateKey::random(rng), - public_nonce: PublicKey::from_secret_key(&r), - nonce: r, - } - } -} - -#[test] -fn manage_single_transaction() { - let mut rng = OsRng::new().unwrap(); - // Alice's parameters - let alice_seed = PrivateKey::random(&mut rng); - let alice_node_identity = NodeIdentity::random(&mut rng, "127.0.0.1:31583".parse().unwrap()).unwrap(); - let alice_database_name = "alice_test_tx_service1"; // Note: every test should have unique database - let alice_datastore = init_datastore(alice_database_name).unwrap(); - let alice_peer_database = alice_datastore.get_handle(alice_database_name).unwrap(); - // Bob's parameters - let bob_seed = PrivateKey::random(&mut rng); - let bob_node_identity = NodeIdentity::random(&mut rng, "127.0.0.1:31582".parse().unwrap()).unwrap(); - let bob_database_name = "bob_test_tx_service1"; // Note: every test should have unique database - let bob_datastore = init_datastore(bob_database_name).unwrap(); - let bob_peer_database = bob_datastore.get_handle(bob_database_name).unwrap(); - - let (alice_services, alice_tx_api, alice_oms_api, _alice_comms) = setup_transaction_service( - alice_seed, - alice_node_identity.clone(), - vec![bob_node_identity.clone()], - alice_peer_database, - ); - - let value = MicroTari::from(1000); - let (_utxo, uo1) = make_input(&mut rng, MicroTari(2500)); - - assert!(alice_tx_api - .send_transaction( - bob_node_identity.identity.public_key.clone(), - value, - MicroTari::from(20), - ) - .is_err()); - - alice_oms_api.add_output(uo1).unwrap(); - - alice_tx_api - .send_transaction( - bob_node_identity.identity.public_key.clone(), - value, - MicroTari::from(20), - ) - .unwrap(); - - let alice_pending_outbound = alice_tx_api.get_pending_outbound_transaction().unwrap(); - let alice_completed_tx = alice_tx_api.get_completed_transaction().unwrap(); - assert_eq!(alice_pending_outbound.len(), 1); - assert_eq!(alice_completed_tx.len(), 0); - - let (bob_services, bob_tx_api, bob_oms_api, _bob_comms) = setup_transaction_service( - bob_seed, - bob_node_identity.clone(), - vec![alice_node_identity.clone()], - bob_peer_database, - ); - - assert_change(|| alice_tx_api.get_completed_transaction().unwrap().len(), 1, 50); - - let alice_pending_outbound = alice_tx_api.get_pending_outbound_transaction().unwrap(); - let alice_completed_tx = alice_tx_api.get_completed_transaction().unwrap(); - assert_eq!(alice_pending_outbound.len(), 0); - assert_eq!(alice_completed_tx.len(), 1); - - let bob_pending_inbound_tx = bob_tx_api.get_pending_inbound_transaction().unwrap(); - assert_eq!(bob_pending_inbound_tx.len(), 1); - - let mut alice_tx_id = 0; - for (k, _v) in alice_completed_tx.iter() { - alice_tx_id = k.clone(); - } - for (k, v) in bob_pending_inbound_tx.iter() { - assert_eq!(*k, alice_tx_id); - if let RecipientState::Finalized(rsm) = &v.state { - bob_oms_api - .confirm_received_output(alice_tx_id, rsm.output.clone()) - .unwrap(); - assert_eq!(bob_oms_api.get_balance().unwrap(), value); - } else { - assert!(false); - } - } - - alice_services.shutdown().unwrap(); - bob_services.shutdown().unwrap(); - clean_up_datastore(alice_database_name); - clean_up_datastore(bob_database_name); -} - -#[test] -fn manage_multiple_transactions() { - let _ = simple_logger::init_with_level(Level::Debug); - let mut rng = OsRng::new().unwrap(); - // Alice's parameters - let alice_seed = PrivateKey::random(&mut rng); - let alice_node_identity = NodeIdentity::random(&mut rng, "127.0.0.1:31584".parse().unwrap()).unwrap(); - let alice_database_name = "alice_test_tx_service2"; // Note: every test should have unique database - let alice_datastore = init_datastore(alice_database_name).unwrap(); - let alice_peer_database = alice_datastore.get_handle(alice_database_name).unwrap(); - // Bob's parameters - let bob_seed = PrivateKey::random(&mut rng); - let bob_node_identity = NodeIdentity::random(&mut rng, "127.0.0.1:31585".parse().unwrap()).unwrap(); - let bob_database_name = "bob_test_tx_service2"; // Note: every test should have unique database - let bob_datastore = init_datastore(bob_database_name).unwrap(); - let bob_peer_database = bob_datastore.get_handle(bob_database_name).unwrap(); - // Carols's parameters - let carol_seed = PrivateKey::random(&mut rng); - let carol_node_identity = NodeIdentity::random(&mut rng, "127.0.0.1:31586".parse().unwrap()).unwrap(); - let carol_database_name = "carol_test_tx_service2"; // Note: every test should have unique database - let carol_datastore = init_datastore(carol_database_name).unwrap(); - let carol_peer_database = carol_datastore.get_handle(carol_database_name).unwrap(); - let (alice_services, alice_tx_api, alice_oms_api, _alice_comms) = setup_transaction_service( - alice_seed, - alice_node_identity.clone(), - vec![bob_node_identity.clone(), carol_node_identity.clone()], - alice_peer_database, - ); - - // Add some funds to Alices wallet - let (_utxo, uo1a) = make_input(&mut rng, MicroTari(5500)); - alice_oms_api.add_output(uo1a).unwrap(); - let (_utxo, uo1b) = make_input(&mut rng, MicroTari(3000)); - alice_oms_api.add_output(uo1b).unwrap(); - let (_utxo, uo1c) = make_input(&mut rng, MicroTari(3000)); - alice_oms_api.add_output(uo1c).unwrap(); - - // A series of interleaved transactions. First with Bob and Carol offline and then two with them online - let value_a_to_b_1 = MicroTari::from(1000); - let value_a_to_b_2 = MicroTari::from(800); - let value_b_to_a_1 = MicroTari::from(1100); - let value_a_to_c_1 = MicroTari::from(1400); - alice_tx_api - .send_transaction( - bob_node_identity.identity.public_key.clone(), - value_a_to_b_1, - MicroTari::from(20), - ) - .unwrap(); - alice_tx_api - .send_transaction( - carol_node_identity.identity.public_key.clone(), - value_a_to_c_1, - MicroTari::from(20), - ) - .unwrap(); - let alice_pending_outbound = alice_tx_api.get_pending_outbound_transaction().unwrap(); - let alice_completed_tx = alice_tx_api.get_completed_transaction().unwrap(); - assert_eq!(alice_pending_outbound.len(), 2); - assert_eq!(alice_completed_tx.len(), 0); - - // Spin up Bob and Carol - let (bob_services, bob_tx_api, bob_oms_api, _bob_comms) = setup_transaction_service( - bob_seed, - bob_node_identity.clone(), - vec![alice_node_identity.clone()], - bob_peer_database, - ); - let (carol_services, carol_tx_api, carol_oms_api, _carol_comms) = setup_transaction_service( - carol_seed, - carol_node_identity.clone(), - vec![alice_node_identity.clone()], - carol_peer_database, - ); - let (_utxo, uo2) = make_input(&mut rng, MicroTari(3500)); - bob_oms_api.add_output(uo2).unwrap(); - let (_utxo, uo3) = make_input(&mut rng, MicroTari(4500)); - carol_oms_api.add_output(uo3).unwrap(); - - bob_tx_api - .send_transaction( - alice_node_identity.identity.public_key.clone(), - value_b_to_a_1, - MicroTari::from(20), - ) - .unwrap(); - alice_tx_api - .send_transaction( - bob_node_identity.identity.public_key.clone(), - value_a_to_b_2, - MicroTari::from(20), - ) - .unwrap(); - - assert_change(|| alice_tx_api.get_completed_transaction().unwrap().len(), 3, 50); - - let alice_pending_outbound = alice_tx_api.get_pending_outbound_transaction().unwrap(); - let alice_completed_tx = alice_tx_api.get_completed_transaction().unwrap(); - assert_eq!(alice_pending_outbound.len(), 0); - assert_eq!(alice_completed_tx.len(), 3); - let bob_pending_outbound = bob_tx_api.get_pending_outbound_transaction().unwrap(); - let bob_completed_tx = bob_tx_api.get_completed_transaction().unwrap(); - assert_eq!(bob_pending_outbound.len(), 0); - assert_eq!(bob_completed_tx.len(), 1); - let carol_pending_inbound = carol_tx_api.get_pending_inbound_transaction().unwrap(); - assert_eq!(carol_pending_inbound.len(), 1); - - alice_services.shutdown().unwrap(); - bob_services.shutdown().unwrap(); - carol_services.shutdown().unwrap(); - clean_up_datastore(alice_database_name); - clean_up_datastore(bob_database_name); - clean_up_datastore(carol_database_name); -} - -// TODO Test the following once the Tokio future based service architecture is in place. The current architecture -// makes it impossible to test this service without a running Service and Comms stack but then you cannot access the -// internals of the service as it is running the ServiceExecutor Thread -// -// What happens when repeated tx_id are sent to be accepted -// What happens with malformed sender message -// What happens with malformed recipient message -// What happens when accepting recipient reply for unknown tx_id +pub mod service; +pub mod storage; diff --git a/base_layer/wallet/tests/transaction_service/service.rs b/base_layer/wallet/tests/transaction_service/service.rs new file mode 100644 index 0000000000..a257af2320 --- /dev/null +++ b/base_layer/wallet/tests/transaction_service/service.rs @@ -0,0 +1,2081 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::support::{ + comms_and_services::{create_dummy_message, get_next_memory_address, setup_comms_services}, + utils::{make_input, random_string, TestParams}, +}; +use chrono::Utc; +use futures::{ + channel::{mpsc, mpsc::Sender}, + stream, + SinkExt, + StreamExt, +}; +use prost::Message; +use rand::rngs::OsRng; +use std::{ + convert::{TryFrom, TryInto}, + sync::Arc, + time::Duration, +}; +use tari_broadcast_channel::bounded; +use tari_comms::{ + message::EnvelopeBody, + peer_manager::{NodeIdentity, PeerFeatures}, + protocol::messaging::MessagingEventSender, + CommsNode, +}; +use tari_comms_dht::outbound::mock::{create_outbound_service_mock, OutboundServiceMockState}; +use tari_core::{ + base_node::proto::{ + base_node as BaseNodeProto, + base_node::base_node_service_response::Response as BaseNodeResponseProto, + }, + mempool::{ + proto::mempool as MempoolProto, + service::{MempoolRequest, MempoolResponse, MempoolServiceRequest}, + TxStorageResponse, + }, + transactions::{ + proto::types::TransactionOutput as TransactionOutputProto, + tari_amount::*, + transaction::{KernelBuilder, KernelFeatures, OutputFeatures, Transaction, TransactionOutput}, + transaction_protocol::{proto, recipient::RecipientSignedMessage, sender::TransactionSenderMessage}, + types::{CryptoFactories, PrivateKey, PublicKey, RangeProof, Signature}, + ReceiverTransactionProtocol, + }, +}; +use tari_crypto::{ + commitment::HomomorphicCommitmentFactory, + keys::{PublicKey as PK, SecretKey as SK}, +}; +use tari_p2p::{ + comms_connector::pubsub_connector, + domain_message::DomainMessage, + services::comms_outbound::CommsOutboundServiceInitializer, +}; +use tari_service_framework::{reply_channel, StackBuilder}; +use tari_test_utils::{collect_stream, paths::with_temp_dir, unpack_enum}; +use tari_wallet::{ + output_manager_service::{ + config::OutputManagerServiceConfig, + handle::OutputManagerHandle, + service::OutputManagerService, + storage::{database::OutputManagerDatabase, memory_db::OutputManagerMemoryDatabase}, + OutputManagerServiceInitializer, + }, + storage::connection_manager::run_migration_and_create_connection_pool, + transaction_service::{ + config::TransactionServiceConfig, + error::TransactionServiceError, + handle::{TransactionEvent, TransactionServiceHandle}, + service::TransactionService, + storage::{ + database::{ + CompletedTransaction, + DbKeyValuePair, + TransactionBackend, + TransactionDatabase, + TransactionStatus, + WriteOperation, + }, + memory_db::TransactionMemoryDatabase, + sqlite_db::TransactionServiceSqliteDatabase, + }, + TransactionServiceInitializer, + }, +}; +use tempdir::TempDir; +use tokio::{ + runtime, + runtime::{Builder, Runtime}, + sync::broadcast, +}; + +fn create_runtime() -> Runtime { + Builder::new() + .threaded_scheduler() + .enable_all() + .core_threads(8) + .build() + .unwrap() +} + +pub fn setup_transaction_service( + runtime: &mut Runtime, + node_identity: Arc, + peers: Vec>, + factories: CryptoFactories, + backend: T, + database_path: String, + discovery_request_timeout: Duration, +) -> (TransactionServiceHandle, OutputManagerHandle, CommsNode) +{ + let (publisher, subscription_factory) = pubsub_connector(runtime.handle().clone(), 100); + let subscription_factory = Arc::new(subscription_factory); + let (comms, dht) = runtime.block_on(setup_comms_services( + node_identity, + peers, + publisher, + database_path, + discovery_request_timeout, + )); + + let fut = StackBuilder::new(runtime.handle().clone(), comms.shutdown_signal()) + .add_initializer(CommsOutboundServiceInitializer::new(dht.outbound_requester())) + .add_initializer(OutputManagerServiceInitializer::new( + OutputManagerServiceConfig::default(), + subscription_factory.clone(), + OutputManagerMemoryDatabase::new(), + factories.clone(), + )) + .add_initializer(TransactionServiceInitializer::new( + TransactionServiceConfig { + mempool_broadcast_timeout: Duration::from_secs(5), + base_node_mined_timeout: Duration::from_secs(5), + }, + subscription_factory, + comms.subscribe_messaging_events(), + backend, + comms.node_identity().clone(), + factories.clone(), + )) + .finish(); + + let handles = runtime.block_on(fut).expect("Service initialization failed"); + + let output_manager_handle = handles.get_handle::().unwrap(); + let transaction_service_handle = handles.get_handle::().unwrap(); + + (transaction_service_handle, output_manager_handle, comms) +} + +/// This utility function creates a Transaction service without using the Service Framework Stack and exposes all the +/// streams for testing purposes. +pub fn setup_transaction_service_no_comms( + runtime: &mut Runtime, + factories: CryptoFactories, + backend: T, +) -> ( + TransactionServiceHandle, + OutputManagerHandle, + OutboundServiceMockState, + Sender>, + Sender>, + Sender>, + Sender>, + Sender>, + MessagingEventSender, +) +{ + let (oms_request_sender, oms_request_receiver) = reply_channel::unbounded(); + + let (oms_event_publisher, oms_event_subscriber) = bounded(100); + let (outbound_message_requester, mock_outbound_service) = create_outbound_service_mock(20); + + let output_manager_service = runtime + .block_on(OutputManagerService::new( + OutputManagerServiceConfig::default(), + outbound_message_requester.clone(), + oms_request_receiver, + stream::empty(), + OutputManagerDatabase::new(OutputManagerMemoryDatabase::new()), + oms_event_publisher, + factories.clone(), + )) + .unwrap(); + + let output_manager_service_handle = OutputManagerHandle::new(oms_request_sender, oms_event_subscriber); + + let (ts_request_sender, ts_request_receiver) = reply_channel::unbounded(); + let (event_publisher, event_subscriber) = bounded(100); + let ts_handle = TransactionServiceHandle::new(ts_request_sender, event_subscriber); + let (tx_sender, tx_receiver) = mpsc::channel(20); + let (tx_ack_sender, tx_ack_receiver) = mpsc::channel(20); + let (tx_finalized_sender, tx_finalized_receiver) = mpsc::channel(20); + let (mempool_response_sender, mempool_response_receiver) = mpsc::channel(20); + let (base_node_response_sender, base_node_response_receiver) = mpsc::channel(20); + + let outbound_mock_state = mock_outbound_service.get_state(); + runtime.spawn(mock_outbound_service.run()); + + let (message_event_publisher, message_event_subscriber) = broadcast::channel(30); + + let ts_service = TransactionService::new( + TransactionServiceConfig { + mempool_broadcast_timeout: Duration::from_secs(5), + base_node_mined_timeout: Duration::from_secs(5), + }, + TransactionDatabase::new(backend), + ts_request_receiver, + tx_receiver, + tx_ack_receiver, + tx_finalized_receiver, + mempool_response_receiver, + base_node_response_receiver, + output_manager_service_handle.clone(), + outbound_message_requester.clone(), + message_event_subscriber, + event_publisher, + Arc::new( + NodeIdentity::random(&mut OsRng, get_next_memory_address(), PeerFeatures::COMMUNICATION_NODE).unwrap(), + ), + factories.clone(), + ); + runtime.spawn(async move { output_manager_service.start().await.unwrap() }); + runtime.spawn(async move { ts_service.start().await.unwrap() }); + ( + ts_handle, + output_manager_service_handle, + outbound_mock_state, + tx_sender, + tx_ack_sender, + tx_finalized_sender, + mempool_response_sender, + base_node_response_sender, + message_event_publisher, + ) +} + +fn manage_single_transaction( + alice_backend: T, + bob_backend: T, + database_path: String, +) +{ + let mut runtime = create_runtime(); + + let factories = CryptoFactories::default(); + // Alice's parameters + let alice_node_identity = Arc::new( + NodeIdentity::random(&mut OsRng, get_next_memory_address(), PeerFeatures::COMMUNICATION_NODE).unwrap(), + ); + + // Bob's parameters + let bob_node_identity = Arc::new( + NodeIdentity::random(&mut OsRng, get_next_memory_address(), PeerFeatures::COMMUNICATION_NODE).unwrap(), + ); + + let base_node_identity = Arc::new( + NodeIdentity::random(&mut OsRng, get_next_memory_address(), PeerFeatures::COMMUNICATION_NODE).unwrap(), + ); + + log::info!( + "manage_single_transaction: Alice: '{}', Bob: '{}', Base: '{}'", + alice_node_identity.node_id().short_str(), + bob_node_identity.node_id().short_str(), + base_node_identity.node_id().short_str() + ); + + let (mut alice_ts, mut alice_oms, alice_comms) = setup_transaction_service( + &mut runtime, + alice_node_identity.clone(), + vec![bob_node_identity.clone()], + factories.clone(), + alice_backend, + database_path.clone(), + Duration::from_secs(0), + ); + runtime + .block_on(alice_ts.set_base_node_public_key(base_node_identity.public_key().clone())) + .unwrap(); + + let alice_event_stream = alice_ts.get_event_stream_fused(); + + let (mut bob_ts, mut bob_oms, bob_comms) = setup_transaction_service( + &mut runtime, + bob_node_identity.clone(), + vec![alice_node_identity.clone()], + factories.clone(), + bob_backend, + database_path, + Duration::from_secs(0), + ); + runtime + .block_on(bob_ts.set_base_node_public_key(base_node_identity.public_key().clone())) + .unwrap(); + + let bob_event_stream = bob_ts.get_event_stream_fused(); + + runtime + .block_on( + bob_comms + .connection_manager() + .dial_peer(alice_node_identity.node_id().clone()), + ) + .unwrap(); + + let value = MicroTari::from(1000); + let (_utxo, uo1) = make_input(&mut OsRng, MicroTari(2500), &factories.commitment); + + assert!(runtime + .block_on(alice_ts.send_transaction( + bob_node_identity.public_key().clone(), + value, + MicroTari::from(20), + "".to_string() + )) + .is_err()); + + runtime.block_on(alice_oms.add_output(uo1)).unwrap(); + let message = "TAKE MAH MONEYS!".to_string(); + runtime + .block_on(alice_ts.send_transaction( + bob_node_identity.public_key().clone(), + value, + MicroTari::from(20), + message.clone(), + )) + .unwrap(); + + let _alice_events = runtime.block_on(async { + collect_stream!( + alice_event_stream.map(|i| (*i).clone()), + take = 2, + timeout = Duration::from_secs(20) + ) + }); + + let bob_events = runtime.block_on(async { + collect_stream!( + bob_event_stream.map(|i| (*i).clone()), + take = 2, + timeout = Duration::from_secs(20) + ) + }); + + let tx_id = match bob_events.iter().find(|e| { + if let TransactionEvent::ReceivedFinalizedTransaction(_) = e { + true + } else { + false + } + }) { + Some(TransactionEvent::ReceivedFinalizedTransaction(tx_id)) => tx_id.clone(), + _ => { + assert!(false, "Bob's transaction should be finalized"); + 0u64 + }, + }; + + let mut bob_completed_tx = runtime.block_on(bob_ts.get_completed_transactions()).unwrap(); + + match bob_completed_tx.remove(&tx_id) { + None => assert!(false, "Completed transaction could not be found"), + Some(tx) => { + runtime + .block_on(bob_oms.confirm_transaction(tx_id, vec![], tx.transaction.body.outputs().clone())) + .unwrap(); + }, + } + + assert_eq!( + runtime.block_on(bob_oms.get_balance()).unwrap().available_balance, + value + ); + + runtime.block_on(async move { + alice_comms.shutdown().await; + bob_comms.shutdown().await; + }); +} + +#[test] +fn manage_single_transaction_memory_db() { + let temp_dir = TempDir::new(random_string(8).as_str()).unwrap(); + manage_single_transaction( + TransactionMemoryDatabase::new(), + TransactionMemoryDatabase::new(), + temp_dir.path().to_str().unwrap().to_string(), + ); +} + +#[test] +fn manage_single_transaction_sqlite_db() { + let temp_dir = TempDir::new(random_string(8).as_str()).unwrap(); + let alice_db_name = format!("{}.sqlite3", random_string(8).as_str()); + let alice_db_path = format!("{}/{}", temp_dir.path().to_str().unwrap(), alice_db_name); + let bob_db_name = format!("{}.sqlite3", random_string(8).as_str()); + let bob_db_path = format!("{}/{}", temp_dir.path().to_str().unwrap(), bob_db_name); + let connection_pool_alice = run_migration_and_create_connection_pool(&alice_db_path).unwrap(); + let connection_pool_bob = run_migration_and_create_connection_pool(&bob_db_path).unwrap(); + + manage_single_transaction( + TransactionServiceSqliteDatabase::new(connection_pool_alice), + TransactionServiceSqliteDatabase::new(connection_pool_bob), + temp_dir.path().to_str().unwrap().to_string(), + ); +} + +fn manage_multiple_transactions( + alice_backend: T, + bob_backend: T, + carol_backend: T, + database_path: String, +) +{ + let mut runtime = create_runtime(); + let factories = CryptoFactories::default(); + // Alice's parameters + let alice_node_identity = Arc::new( + NodeIdentity::random(&mut OsRng, get_next_memory_address(), PeerFeatures::COMMUNICATION_NODE).unwrap(), + ); + + // Bob's parameters + let bob_node_identity = Arc::new( + NodeIdentity::random(&mut OsRng, get_next_memory_address(), PeerFeatures::COMMUNICATION_NODE).unwrap(), + ); + + // Carols's parameters + let carol_node_identity = Arc::new( + NodeIdentity::random(&mut OsRng, get_next_memory_address(), PeerFeatures::COMMUNICATION_NODE).unwrap(), + ); + + let (mut alice_ts, mut alice_oms, alice_comms) = setup_transaction_service( + &mut runtime, + alice_node_identity.clone(), + vec![bob_node_identity.clone(), carol_node_identity.clone()], + factories.clone(), + alice_backend, + database_path.clone(), + Duration::from_secs(1), + ); + + let alice_event_stream = alice_ts.get_event_stream_fused(); + + // Add some funds to Alices wallet + let (_utxo, uo1a) = make_input(&mut OsRng, MicroTari(5500), &factories.commitment); + runtime.block_on(alice_oms.add_output(uo1a)).unwrap(); + let (_utxo, uo1b) = make_input(&mut OsRng, MicroTari(3000), &factories.commitment); + runtime.block_on(alice_oms.add_output(uo1b)).unwrap(); + let (_utxo, uo1c) = make_input(&mut OsRng, MicroTari(3000), &factories.commitment); + runtime.block_on(alice_oms.add_output(uo1c)).unwrap(); + + // A series of interleaved transactions. First with Bob and Carol offline and then two with them online + let value_a_to_b_1 = MicroTari::from(1000); + let value_a_to_b_2 = MicroTari::from(800); + let value_b_to_a_1 = MicroTari::from(1100); + let value_a_to_c_1 = MicroTari::from(1400); + runtime + .block_on(alice_ts.send_transaction( + bob_node_identity.public_key().clone(), + value_a_to_b_1, + MicroTari::from(20), + "".to_string(), + )) + .unwrap(); + runtime + .block_on(alice_ts.send_transaction( + carol_node_identity.public_key().clone(), + value_a_to_c_1, + MicroTari::from(20), + "".to_string(), + )) + .unwrap(); + let alice_completed_tx = runtime.block_on(alice_ts.get_completed_transactions()).unwrap(); + assert_eq!(alice_completed_tx.len(), 0); + + // Spin up Bob and Carol + let (mut bob_ts, mut bob_oms, bob_comms) = setup_transaction_service( + &mut runtime, + bob_node_identity.clone(), + vec![alice_node_identity.clone()], + factories.clone(), + bob_backend, + database_path.clone(), + Duration::from_secs(1), + ); + let (mut carol_ts, mut carol_oms, carol_comms) = setup_transaction_service( + &mut runtime, + carol_node_identity.clone(), + vec![alice_node_identity.clone()], + factories.clone(), + carol_backend, + database_path, + Duration::from_secs(1), + ); + + let bob_event_stream = bob_ts.get_event_stream_fused(); + let carol_event_stream = carol_ts.get_event_stream_fused(); + + let (_utxo, uo2) = make_input(&mut OsRng, MicroTari(3500), &factories.commitment); + runtime.block_on(bob_oms.add_output(uo2)).unwrap(); + let (_utxo, uo3) = make_input(&mut OsRng, MicroTari(4500), &factories.commitment); + runtime.block_on(carol_oms.add_output(uo3)).unwrap(); + + runtime + .block_on(bob_ts.send_transaction( + alice_node_identity.public_key().clone(), + value_b_to_a_1, + MicroTari::from(20), + "".to_string(), + )) + .unwrap(); + runtime + .block_on(alice_ts.send_transaction( + bob_node_identity.public_key().clone(), + value_a_to_b_2, + MicroTari::from(20), + "".to_string(), + )) + .unwrap(); + let result = runtime.block_on(async { + collect_stream!( + alice_event_stream.map(|i| (*i).clone()), + take = 8, + timeout = Duration::from_secs(30) + ) + }); + + assert_eq!( + result.iter().fold(0, |acc, x| match x { + TransactionEvent::ReceivedTransactionReply(_) => acc + 1, + _ => acc, + }), + 3 + ); + + let _result = + runtime.block_on(async { collect_stream!(bob_event_stream, take = 6, timeout = Duration::from_secs(30)) }); + + let alice_pending_outbound = runtime.block_on(alice_ts.get_pending_outbound_transactions()).unwrap(); + let alice_completed_tx = runtime.block_on(alice_ts.get_completed_transactions()).unwrap(); + assert_eq!(alice_pending_outbound.len(), 0); + assert_eq!(alice_completed_tx.len(), 4); + let bob_pending_outbound = runtime.block_on(bob_ts.get_pending_outbound_transactions()).unwrap(); + let bob_completed_tx = runtime.block_on(bob_ts.get_completed_transactions()).unwrap(); + assert_eq!(bob_pending_outbound.len(), 0); + assert_eq!(bob_completed_tx.len(), 3); + let _ = + runtime.block_on(async { collect_stream!(carol_event_stream, take = 2, timeout = Duration::from_secs(30)) }); + let carol_pending_inbound = runtime.block_on(carol_ts.get_pending_inbound_transactions()).unwrap(); + let carol_completed_tx = runtime.block_on(carol_ts.get_completed_transactions()).unwrap(); + assert_eq!(carol_pending_inbound.len(), 0); + assert_eq!(carol_completed_tx.len(), 1); + + runtime.block_on(async move { + alice_comms.shutdown().await; + bob_comms.shutdown().await; + carol_comms.shutdown().await; + }); +} + +#[test] +fn manage_multiple_transactions_memory_db() { + let temp_dir = TempDir::new(random_string(8).as_str()).unwrap(); + + manage_multiple_transactions( + TransactionMemoryDatabase::new(), + TransactionMemoryDatabase::new(), + TransactionMemoryDatabase::new(), + temp_dir.path().to_str().unwrap().to_string(), + ); +} + +#[test] +fn manage_multiple_transactions_sqlite_db() { + let temp_dir = TempDir::new(random_string(8).as_str()).unwrap(); + + let path_string = temp_dir.path().to_str().unwrap().to_string(); + let alice_db_name = format!("{}.sqlite3", random_string(8).as_str()); + let alice_db_path = format!("{}/{}", path_string, alice_db_name); + let bob_db_name = format!("{}.sqlite3", random_string(8).as_str()); + let bob_db_path = format!("{}/{}", path_string, bob_db_name); + let carol_db_name = format!("{}.sqlite3", random_string(8).as_str()); + let carol_db_path = format!("{}/{}", path_string, carol_db_name); + let connection_pool_alice = run_migration_and_create_connection_pool(&alice_db_path).unwrap(); + let connection_pool_bob = run_migration_and_create_connection_pool(&bob_db_path).unwrap(); + let connection_pool_carol = run_migration_and_create_connection_pool(&carol_db_path).unwrap(); + manage_multiple_transactions( + TransactionServiceSqliteDatabase::new(connection_pool_alice), + TransactionServiceSqliteDatabase::new(connection_pool_bob), + TransactionServiceSqliteDatabase::new(connection_pool_carol), + path_string, + ); +} + +fn test_sending_repeated_tx_ids(alice_backend: T, bob_backend: T) { + let mut runtime = create_runtime(); + let factories = CryptoFactories::default(); + + let bob_node_identity = NodeIdentity::random( + &mut OsRng, + "/ip4/127.0.0.1/tcp/55741".parse().unwrap(), + PeerFeatures::COMMUNICATION_NODE, + ) + .unwrap(); + + let ( + alice_ts, + _alice_output_manager, + alice_outbound_service, + mut alice_tx_sender, + _alice_tx_ack_sender, + _alice_mempool_response_sender, + _, + _, + _, + ) = setup_transaction_service_no_comms(&mut runtime, factories.clone(), alice_backend); + let (_bob_ts, mut bob_output_manager, _bob_outbound_service, _bob_tx_sender, _bob_tx_ack_sender, _, _, _, _) = + setup_transaction_service_no_comms(&mut runtime, factories.clone(), bob_backend); + let alice_event_stream = alice_ts.get_event_stream_fused(); + + let (_utxo, uo) = make_input(&mut OsRng, MicroTari(250000), &factories.commitment); + + runtime.block_on(bob_output_manager.add_output(uo)).unwrap(); + + let mut stp = runtime + .block_on(bob_output_manager.prepare_transaction_to_send( + MicroTari::from(500), + MicroTari::from(1000), + None, + "".to_string(), + )) + .unwrap(); + let msg = stp.build_single_round_message().unwrap(); + let tx_message = create_dummy_message( + TransactionSenderMessage::Single(Box::new(msg.clone())).into(), + &bob_node_identity.public_key(), + ); + + runtime.block_on(alice_tx_sender.send(tx_message.clone())).unwrap(); + runtime.block_on(alice_tx_sender.send(tx_message.clone())).unwrap(); + + let result = runtime.block_on(async { + collect_stream!( + alice_event_stream.map(|i| (*i).clone()), + take = 2, + timeout = Duration::from_secs(10) + ) + }); + + alice_outbound_service + .wait_call_count(1, Duration::from_secs(10)) + .unwrap(); + + assert_eq!(result.len(), 2); + assert!(result + .iter() + .find(|i| if let TransactionEvent::ReceivedTransaction(_) = i { + true + } else { + false + }) + .is_some()); + assert!(result + .iter() + .find(|i| if let TransactionEvent::Error(s) = i { + s == &"Error handling Transaction Sender message".to_string() + } else { + false + }) + .is_some()); +} + +#[test] +fn test_sending_repeated_tx_ids_memory_db() { + test_sending_repeated_tx_ids(TransactionMemoryDatabase::new(), TransactionMemoryDatabase::new()); +} + +#[test] +fn test_sending_repeated_tx_ids_sqlite_db() { + with_temp_dir(|dir_path| { + let path_string = dir_path.to_str().unwrap().to_string(); + let alice_db_name = format!("{}.sqlite3", random_string(8).as_str()); + let alice_db_path = format!("{}/{}", path_string, alice_db_name); + let bob_db_name = format!("{}.sqlite3", random_string(8).as_str()); + let bob_db_path = format!("{}/{}", path_string, bob_db_name); + let connection_pool_alice = run_migration_and_create_connection_pool(&alice_db_path).unwrap(); + let connection_pool_bob = run_migration_and_create_connection_pool(&bob_db_path).unwrap(); + test_sending_repeated_tx_ids( + TransactionServiceSqliteDatabase::new(connection_pool_alice), + TransactionServiceSqliteDatabase::new(connection_pool_bob), + ); + }); +} + +fn test_accepting_unknown_tx_id_and_malformed_reply(alice_backend: T) { + let mut runtime = create_runtime(); + let factories = CryptoFactories::default(); + + let bob_node_identity = + NodeIdentity::random(&mut OsRng, get_next_memory_address(), PeerFeatures::COMMUNICATION_NODE).unwrap(); + let ( + mut alice_ts, + mut alice_output_manager, + alice_outbound_service, + _alice_tx_sender, + mut alice_tx_ack_sender, + _, + _, + _, + _, + ) = setup_transaction_service_no_comms(&mut runtime, factories.clone(), alice_backend); + + let alice_event_stream = alice_ts.get_event_stream_fused(); + + let (_utxo, uo) = make_input(&mut OsRng, MicroTari(250000), &factories.commitment); + + runtime.block_on(alice_output_manager.add_output(uo)).unwrap(); + + runtime + .block_on(alice_ts.send_transaction( + bob_node_identity.public_key().clone(), + MicroTari::from(500), + MicroTari::from(1000), + "".to_string(), + )) + .unwrap(); + alice_outbound_service + .wait_call_count(1, Duration::from_secs(10)) + .unwrap(); + let (_, body) = alice_outbound_service.pop_call().unwrap(); + let envelope_body = EnvelopeBody::decode(body.as_slice()).unwrap(); + let sender_message = envelope_body + .decode_part::(1) + .unwrap() + .unwrap(); + + let params = TestParams::new(&mut OsRng); + + let rtp = ReceiverTransactionProtocol::new( + sender_message.try_into().unwrap(), + params.nonce, + params.spend_key, + OutputFeatures::default(), + &factories, + ); + + let mut tx_reply = rtp.get_signed_data().unwrap().clone(); + let mut wrong_tx_id = tx_reply.clone(); + wrong_tx_id.tx_id = 2; + let (_p, pub_key) = PublicKey::random_keypair(&mut OsRng); + tx_reply.public_spend_key = pub_key; + runtime + .block_on(alice_tx_ack_sender.send(create_dummy_message( + wrong_tx_id.into(), + &bob_node_identity.public_key(), + ))) + .unwrap(); + + runtime + .block_on(alice_tx_ack_sender.send(create_dummy_message(tx_reply.into(), &bob_node_identity.public_key()))) + .unwrap(); + + assert!(runtime + .block_on(async { + collect_stream!( + alice_event_stream.map(|i| (*i).clone()), + take = 2, + timeout = Duration::from_secs(10) + ) + }) + .iter() + .find(|i| if let TransactionEvent::Error(s) = i { + s == &"Error handling Transaction Recipient Reply message".to_string() + } else { + false + }) + .is_some()); +} + +#[test] +fn test_accepting_unknown_tx_id_and_malformed_reply_memory_db() { + test_accepting_unknown_tx_id_and_malformed_reply(TransactionMemoryDatabase::new()); +} + +#[test] +fn test_accepting_unknown_tx_id_and_malformed_reply_sqlite_db() { + with_temp_dir(|dir_path| { + let path_string = dir_path.to_str().unwrap().to_string(); + let alice_db_name = format!("{}.sqlite3", random_string(8).as_str()); + let alice_db_path = format!("{}/{}", path_string, alice_db_name); + let connection_pool_alice = run_migration_and_create_connection_pool(&alice_db_path).unwrap(); + test_accepting_unknown_tx_id_and_malformed_reply(TransactionServiceSqliteDatabase::new(connection_pool_alice)); + }); +} + +fn finalize_tx_with_nonexistent_txid(alice_backend: T) { + let mut runtime = create_runtime(); + let factories = CryptoFactories::default(); + + let ( + alice_ts, + _alice_output_manager, + _alice_outbound_service, + _alice_tx_sender, + _alice_tx_ack_sender, + mut alice_tx_finalized, + _, + _, + _, + ) = setup_transaction_service_no_comms(&mut runtime, factories.clone(), alice_backend); + let alice_event_stream = alice_ts.get_event_stream_fused(); + + let tx = Transaction::new(vec![], vec![], vec![], PrivateKey::random(&mut OsRng)); + let finalized_transaction_message = proto::TransactionFinalizedMessage { + tx_id: 88u64, + transaction: Some(tx.clone().into()), + }; + + runtime + .block_on(alice_tx_finalized.send(create_dummy_message( + finalized_transaction_message.clone(), + &PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), + ))) + .unwrap(); + + assert!(runtime + .block_on(async { + collect_stream!( + alice_event_stream.map(|i| (*i).clone()), + take = 1, + timeout = Duration::from_secs(10) + ) + }) + .iter() + .find(|i| if let TransactionEvent::Error(s) = i { + s == &"Error handling Transaction Finalized message".to_string() + } else { + false + }) + .is_some()); +} + +#[test] +fn finalize_tx_with_nonexistent_txid_memory_db() { + finalize_tx_with_nonexistent_txid(TransactionMemoryDatabase::new()); +} + +#[test] +fn finalize_tx_with_nonexistent_txid_sqlite_db() { + with_temp_dir(|dir_path| { + let path_string = dir_path.to_str().unwrap().to_string(); + let alice_db_name = format!("{}.sqlite3", random_string(8).as_str()); + let alice_db_path = format!("{}/{}", path_string, alice_db_name); + let connection_pool_alice = run_migration_and_create_connection_pool(&alice_db_path).unwrap(); + + finalize_tx_with_nonexistent_txid(TransactionServiceSqliteDatabase::new(connection_pool_alice)); + }); +} + +fn finalize_tx_with_incorrect_pubkey(alice_backend: T, bob_backend: T) { + let mut runtime = create_runtime(); + let factories = CryptoFactories::default(); + + let ( + alice_ts, + _alice_output_manager, + alice_outbound_service, + mut alice_tx_sender, + _alice_tx_ack_sender, + mut alice_tx_finalized, + _, + _, + _, + ) = setup_transaction_service_no_comms(&mut runtime, factories.clone(), alice_backend); + let alice_event_stream = alice_ts.get_event_stream_fused(); + + let bob_node_identity = + NodeIdentity::random(&mut OsRng, get_next_memory_address(), PeerFeatures::COMMUNICATION_NODE).unwrap(); + let (_bob_ts, mut bob_output_manager, _bob_outbound_service, _bob_tx_sender, _bob_tx_ack_sender, _, _, _, _) = + setup_transaction_service_no_comms(&mut runtime, factories.clone(), bob_backend); + + let (_utxo, uo) = make_input(&mut OsRng, MicroTari(250000), &factories.commitment); + + runtime.block_on(bob_output_manager.add_output(uo)).unwrap(); + + let mut stp = runtime + .block_on(bob_output_manager.prepare_transaction_to_send( + MicroTari::from(500), + MicroTari::from(1000), + None, + "".to_string(), + )) + .unwrap(); + let msg = stp.build_single_round_message().unwrap(); + let tx_message = create_dummy_message( + TransactionSenderMessage::Single(Box::new(msg.clone())).into(), + &bob_node_identity.public_key(), + ); + + runtime.block_on(alice_tx_sender.send(tx_message.clone())).unwrap(); + + alice_outbound_service + .wait_call_count(1, Duration::from_secs(10)) + .unwrap(); + let (_, body) = alice_outbound_service.pop_call().unwrap(); + let envelope_body = EnvelopeBody::decode(body.as_slice()).unwrap(); + let recipient_reply: RecipientSignedMessage = envelope_body + .decode_part::(1) + .unwrap() + .unwrap() + .try_into() + .unwrap(); + + stp.add_single_recipient_info(recipient_reply.clone(), &factories.range_proof) + .unwrap(); + stp.finalize(KernelFeatures::empty(), &factories).unwrap(); + let tx = stp.get_transaction().unwrap(); + + let finalized_transaction_message = proto::TransactionFinalizedMessage { + tx_id: recipient_reply.tx_id, + transaction: Some(tx.clone().into()), + }; + + runtime + .block_on(alice_tx_finalized.send(create_dummy_message( + finalized_transaction_message.clone(), + &PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), + ))) + .unwrap(); + + assert!(runtime + .block_on(async { + collect_stream!( + alice_event_stream.map(|i| (*i).clone()), + take = 2, + timeout = Duration::from_secs(10) + ) + }) + .iter() + .find(|i| if let TransactionEvent::Error(s) = i { + s == &"Error handling Transaction Finalized message".to_string() + } else { + false + }) + .is_some()); +} + +#[test] +fn finalize_tx_with_incorrect_pubkey_memory_db() { + finalize_tx_with_incorrect_pubkey(TransactionMemoryDatabase::new(), TransactionMemoryDatabase::new()); +} + +#[test] +fn finalize_tx_with_incorrect_pubkey_sqlite_db() { + with_temp_dir(|dir_path| { + let path_string = dir_path.to_str().unwrap().to_string(); + let alice_db_name = format!("{}.sqlite3", random_string(8).as_str()); + let alice_db_path = format!("{}/{}", path_string, alice_db_name); + let bob_db_name = format!("{}.sqlite3", random_string(8).as_str()); + let bob_db_path = format!("{}/{}", path_string, bob_db_name); + let connection_pool_alice = run_migration_and_create_connection_pool(&alice_db_path).unwrap(); + let connection_pool_bob = run_migration_and_create_connection_pool(&bob_db_path).unwrap(); + finalize_tx_with_incorrect_pubkey( + TransactionServiceSqliteDatabase::new(connection_pool_alice), + TransactionServiceSqliteDatabase::new(connection_pool_bob), + ); + }); +} + +fn finalize_tx_with_missing_output(alice_backend: T, bob_backend: T) { + let mut runtime = create_runtime(); + let factories = CryptoFactories::default(); + + let ( + alice_ts, + _alice_output_manager, + alice_outbound_service, + mut alice_tx_sender, + _alice_tx_ack_sender, + mut alice_tx_finalized, + _, + _, + _, + ) = setup_transaction_service_no_comms(&mut runtime, factories.clone(), alice_backend); + let alice_event_stream = alice_ts.get_event_stream_fused(); + + let bob_node_identity = + NodeIdentity::random(&mut OsRng, get_next_memory_address(), PeerFeatures::COMMUNICATION_NODE).unwrap(); + let (_bob_ts, mut bob_output_manager, _bob_outbound_service, _bob_tx_sender, _bob_tx_ack_sender, _, _, _, _) = + setup_transaction_service_no_comms(&mut runtime, factories.clone(), bob_backend); + + let (_utxo, uo) = make_input(&mut OsRng, MicroTari(250000), &factories.commitment); + + runtime.block_on(bob_output_manager.add_output(uo)).unwrap(); + + let mut stp = runtime + .block_on(bob_output_manager.prepare_transaction_to_send( + MicroTari::from(500), + MicroTari::from(1000), + None, + "".to_string(), + )) + .unwrap(); + let msg = stp.build_single_round_message().unwrap(); + let tx_message = create_dummy_message( + TransactionSenderMessage::Single(Box::new(msg.clone())).into(), + &bob_node_identity.public_key(), + ); + + runtime.block_on(alice_tx_sender.send(tx_message.clone())).unwrap(); + + alice_outbound_service + .wait_call_count(1, Duration::from_secs(10)) + .unwrap(); + let (_, body) = alice_outbound_service.pop_call().unwrap(); + let envelope_body = EnvelopeBody::decode(body.as_slice()).unwrap(); + let recipient_reply: RecipientSignedMessage = envelope_body + .decode_part::(1) + .unwrap() + .unwrap() + .try_into() + .unwrap(); + + stp.add_single_recipient_info(recipient_reply.clone(), &factories.range_proof) + .unwrap(); + stp.finalize(KernelFeatures::empty(), &factories).unwrap(); + + let finalized_transaction_message = proto::TransactionFinalizedMessage { + tx_id: recipient_reply.tx_id, + transaction: Some(Transaction::new(vec![], vec![], vec![], PrivateKey::random(&mut OsRng)).into()), + }; + + runtime + .block_on(alice_tx_finalized.send(create_dummy_message( + finalized_transaction_message.clone(), + &bob_node_identity.public_key(), + ))) + .unwrap(); + + assert!(runtime + .block_on(async { + collect_stream!( + alice_event_stream.map(|i| (*i).clone()), + take = 2, + timeout = Duration::from_secs(10) + ) + }) + .iter() + .find(|i| if let TransactionEvent::Error(s) = i { + s == &"Error handling Transaction Finalized message".to_string() + } else { + false + }) + .is_some()); +} + +#[test] +fn finalize_tx_with_missing_output_memory_db() { + finalize_tx_with_missing_output(TransactionMemoryDatabase::new(), TransactionMemoryDatabase::new()); +} + +#[test] +fn finalize_tx_with_missing_output_sqlite_db() { + with_temp_dir(|dir_path| { + let path_string = dir_path.to_str().unwrap().to_string(); + let alice_db_name = format!("{}.sqlite3", random_string(8).as_str()); + let alice_db_path = format!("{}/{}", path_string, alice_db_name); + let bob_db_name = format!("{}.sqlite3", random_string(8).as_str()); + let bob_db_path = format!("{}/{}", path_string, bob_db_name); + let connection_pool_alice = run_migration_and_create_connection_pool(&alice_db_path).unwrap(); + let connection_pool_bob = run_migration_and_create_connection_pool(&bob_db_path).unwrap(); + finalize_tx_with_missing_output( + TransactionServiceSqliteDatabase::new(connection_pool_alice), + TransactionServiceSqliteDatabase::new(connection_pool_bob), + ); + }); +} + +#[test] +fn discovery_async_return_test() { + let db_tempdir = TempDir::new(random_string(8).as_str()).unwrap(); + let db_folder = db_tempdir.path().to_str().unwrap().to_string(); + + let mut runtime = runtime::Builder::new() + .basic_scheduler() + .enable_time() + .thread_name("discovery_async_return_test") + .build() + .unwrap(); + let factories = CryptoFactories::default(); + + let alice_backend = TransactionMemoryDatabase::new(); + let bob_backend = TransactionMemoryDatabase::new(); + let dave_backend = TransactionMemoryDatabase::new(); + + // Alice's parameters + let alice_node_identity = Arc::new( + NodeIdentity::random(&mut OsRng, get_next_memory_address(), PeerFeatures::COMMUNICATION_NODE).unwrap(), + ); + + // Bob's parameters + let bob_node_identity = Arc::new( + NodeIdentity::random(&mut OsRng, get_next_memory_address(), PeerFeatures::COMMUNICATION_NODE).unwrap(), + ); + + // Carols's parameters + let carol_node_identity = Arc::new( + NodeIdentity::random(&mut OsRng, get_next_memory_address(), PeerFeatures::COMMUNICATION_NODE).unwrap(), + ); + + // Dave's parameters + let dave_node_identity = Arc::new( + NodeIdentity::random(&mut OsRng, get_next_memory_address(), PeerFeatures::COMMUNICATION_NODE).unwrap(), + ); + + log::info!( + "discovery_async_return_test: Alice: '{}', Bob: '{}', Carol: '{}', Dave: '{}'", + alice_node_identity.node_id().short_str(), + bob_node_identity.node_id().short_str(), + carol_node_identity.node_id().short_str(), + dave_node_identity.node_id().short_str() + ); + + let (mut alice_ts, mut alice_oms, alice_comms) = setup_transaction_service( + &mut runtime, + alice_node_identity.clone(), + vec![bob_node_identity.clone()], + factories.clone(), + alice_backend, + db_folder.clone(), + Duration::from_secs(5), + ); + let mut alice_event_stream = alice_ts.get_event_stream_fused(); + + let (_bob_ts, _bob_oms, bob_comms) = setup_transaction_service( + &mut runtime, + bob_node_identity.clone(), + vec![alice_node_identity.clone(), dave_node_identity.clone()], + factories.clone(), + bob_backend, + db_folder.clone(), + Duration::from_secs(1), + ); + + let (_dave_ts, _dave_oms, dave_comms) = setup_transaction_service( + &mut runtime, + dave_node_identity.clone(), + vec![bob_node_identity.clone()], + factories.clone(), + dave_backend, + db_folder, + Duration::from_secs(1), + ); + + // Establish some connections beforehand, to reduce the amount of work done concurrently in tests + // Connect Bob and Alice + runtime + .block_on( + bob_comms + .connection_manager() + .dial_peer(alice_node_identity.node_id().clone()), + ) + .unwrap(); + + // Connect Dave to Bob + runtime + .block_on( + dave_comms + .connection_manager() + .dial_peer(bob_node_identity.node_id().clone()), + ) + .unwrap(); + log::error!("Finished Dials"); + + let (_utxo, uo1a) = make_input(&mut OsRng, MicroTari(5500), &factories.commitment); + runtime.block_on(alice_oms.add_output(uo1a)).unwrap(); + let (_utxo, uo1b) = make_input(&mut OsRng, MicroTari(3000), &factories.commitment); + runtime.block_on(alice_oms.add_output(uo1b)).unwrap(); + let (_utxo, uo1c) = make_input(&mut OsRng, MicroTari(3000), &factories.commitment); + runtime.block_on(alice_oms.add_output(uo1c)).unwrap(); + + let initial_balance = runtime.block_on(alice_oms.get_balance()).unwrap(); + + let value_a_to_c_1 = MicroTari::from(1400); + + let tx_id = match runtime.block_on(alice_ts.send_transaction( + carol_node_identity.public_key().clone(), + value_a_to_c_1, + MicroTari::from(20), + "Discovery Tx!".to_string(), + )) { + Err(TransactionServiceError::OutboundSendDiscoveryInProgress(tx_id)) => tx_id, + _ => { + assert!(false, "Send should not succeed as Peer is not known"); + 0u64 + }, + }; + assert_ne!(initial_balance, runtime.block_on(alice_oms.get_balance()).unwrap()); + + let event = runtime.block_on(alice_event_stream.next()).unwrap(); + unpack_enum!(TransactionEvent::TransactionSendDiscoveryComplete(txid, is_success) = &*event); + assert_eq!(txid, &tx_id); + assert_eq!(*is_success, false); + + assert_eq!(initial_balance, runtime.block_on(alice_oms.get_balance()).unwrap()); + + let tx_id2 = match runtime.block_on(alice_ts.send_transaction( + dave_node_identity.public_key().clone(), + value_a_to_c_1, + MicroTari::from(20), + "Discovery Tx2!".to_string(), + )) { + Err(TransactionServiceError::OutboundSendDiscoveryInProgress(tx_id)) => tx_id, + _ => { + assert!(false, "Send should not succeed as Peer is not known"); + 0u64 + }, + }; + let event = runtime.block_on(alice_event_stream.next()).unwrap(); + unpack_enum!(TransactionEvent::TransactionSendDiscoveryComplete(txid, is_success) = &*event); + assert_eq!(txid, &tx_id2); + assert!(is_success); + + let event = runtime.block_on(alice_event_stream.next()).unwrap(); + unpack_enum!(TransactionEvent::TransactionSendResult(txid, is_success) = &*event); + assert_eq!(txid, &tx_id2); + assert!(is_success); + + let event = runtime.block_on(alice_event_stream.next()).unwrap(); + unpack_enum!(TransactionEvent::ReceivedTransactionReply(txid) = &*event); + assert_eq!(txid, &tx_id2); + + runtime.block_on(async move { + alice_comms.shutdown().await; + bob_comms.shutdown().await; + dave_comms.shutdown().await; + }); +} + +fn test_coinbase(backend: T) { + let mut runtime = create_runtime(); + let factories = CryptoFactories::default(); + + let ( + mut alice_ts, + mut alice_output_manager, + _alice_outbound_service, + _alice_tx_sender, + _alice_tx_ack_sender, + _, + _, + _, + _, + ) = setup_transaction_service_no_comms(&mut runtime, factories.clone(), backend); + + let balance = runtime.block_on(alice_output_manager.get_balance()).unwrap(); + assert_eq!(balance.pending_incoming_balance, MicroTari(0)); + + let coinbase = runtime + .block_on(alice_ts.request_coinbase_key(MicroTari::from(4000), 7777)) + .unwrap(); + + let balance = runtime.block_on(alice_output_manager.get_balance()).unwrap(); + assert_eq!(balance.pending_incoming_balance, MicroTari(4000)); + + runtime + .block_on(alice_ts.cancel_coinbase_transaction(coinbase.tx_id)) + .unwrap(); + + let balance = runtime.block_on(alice_output_manager.get_balance()).unwrap(); + assert_eq!(balance.pending_incoming_balance, MicroTari(0)); + + let coinbase = runtime + .block_on(alice_ts.request_coinbase_key(MicroTari::from(7000), 7778)) + .unwrap(); + + let output = TransactionOutput::new( + OutputFeatures::create_coinbase(7778), + factories.commitment.commit_value(&coinbase.spending_key, 7000), + RangeProof::default(), + ); + let kernel = KernelBuilder::new() + .with_features(KernelFeatures::create_coinbase()) + .with_excess(&factories.commitment.zero()) + .with_signature(&Signature::default()) + .build() + .unwrap(); + let output_wrong_commitment = TransactionOutput::new( + OutputFeatures::create_coinbase(1000), + factories.commitment.commit_value(&coinbase.spending_key, 2222), + RangeProof::default(), + ); + + let output_wrong_feature = TransactionOutput::new( + OutputFeatures::default(), + factories.commitment.commit_value(&coinbase.spending_key, 7000), + RangeProof::default(), + ); + + let transaction_wrong_commitment = Transaction::new( + Vec::new(), + vec![output_wrong_commitment.clone()], + vec![kernel.clone()], + PrivateKey::default(), + ); + let transaction_wrong_feature = Transaction::new( + Vec::new(), + vec![output_wrong_feature.clone()], + vec![kernel.clone()], + PrivateKey::default(), + ); + let transaction = Transaction::new( + Vec::new(), + vec![output.clone()], + vec![kernel.clone()], + PrivateKey::default(), + ); + + assert!(runtime + .block_on(alice_ts.complete_coinbase_transaction(55, transaction.clone())) + .is_err()); + + assert!(runtime + .block_on(alice_ts.complete_coinbase_transaction(coinbase.tx_id, transaction_wrong_commitment.clone())) + .is_err()); + + assert!(runtime + .block_on(alice_ts.complete_coinbase_transaction(coinbase.tx_id, transaction_wrong_feature.clone())) + .is_err()); + + runtime + .block_on(alice_ts.complete_coinbase_transaction(coinbase.tx_id, transaction)) + .unwrap(); + + let completed_txs = runtime.block_on(alice_ts.get_completed_transactions()).unwrap(); + + assert_eq!(completed_txs.len(), 1); + assert!(completed_txs.get(&coinbase.tx_id).is_some()); +} + +#[test] +fn test_coinbase_memory_db() { + test_coinbase(TransactionMemoryDatabase::new()); +} + +#[test] +fn test_coinbase_sqlite_db() { + with_temp_dir(|dir_path| { + let path_string = dir_path.to_str().unwrap().to_string(); + let alice_db_name = format!("{}.sqlite3", random_string(8).as_str()); + let alice_db_path = format!("{}/{}", path_string, alice_db_name); + let connection_pool_alice = run_migration_and_create_connection_pool(&alice_db_path).unwrap(); + + test_coinbase(TransactionServiceSqliteDatabase::new(connection_pool_alice)); + }); +} + +#[test] +fn transaction_mempool_broadcast() { + let factories = CryptoFactories::default(); + let mut runtime = Runtime::new().unwrap(); + + let alice_node_identity = + NodeIdentity::random(&mut OsRng, get_next_memory_address(), PeerFeatures::COMMUNICATION_NODE).unwrap(); + + let bob_node_identity = + NodeIdentity::random(&mut OsRng, get_next_memory_address(), PeerFeatures::COMMUNICATION_NODE).unwrap(); + + let base_node_identity = + NodeIdentity::random(&mut OsRng, get_next_memory_address(), PeerFeatures::COMMUNICATION_NODE).unwrap(); + + let ( + mut alice_ts, + mut alice_output_manager, + alice_outbound_service, + mut _alice_tx_sender, + mut alice_tx_ack_sender, + _, + mut alice_mempool_response_sender, + _, + _, + ) = setup_transaction_service_no_comms(&mut runtime, factories.clone(), TransactionMemoryDatabase::new()); + + runtime + .block_on(alice_ts.set_base_node_public_key(base_node_identity.public_key().clone())) + .unwrap(); + + let ( + mut bob_ts, + _bob_output_manager, + bob_outbound_service, + mut bob_tx_sender, + _, + mut bob_tx_finalized_sender, + _, + _, + _, + ) = setup_transaction_service_no_comms(&mut runtime, factories.clone(), TransactionMemoryDatabase::new()); + + runtime + .block_on(bob_ts.set_base_node_public_key(base_node_identity.public_key().clone())) + .unwrap(); + + let (_utxo, uo) = make_input(&mut OsRng, MicroTari(250000), &factories.commitment); + runtime.block_on(alice_output_manager.add_output(uo)).unwrap(); + + runtime + .block_on(alice_ts.send_transaction( + bob_node_identity.public_key().clone(), + 10000 * uT, + 100 * uT, + "Testing Message".to_string(), + )) + .unwrap(); + + let call = alice_outbound_service.pop_call().unwrap(); + let envelope_body = EnvelopeBody::decode(&mut call.1.as_slice()).unwrap(); + let tx_sender_msg: TransactionSenderMessage = envelope_body + .decode_part::(1) + .unwrap() + .unwrap() + .try_into() + .unwrap(); + let tx_id = match tx_sender_msg.clone() { + TransactionSenderMessage::Single(s) => s.tx_id, + _ => { + assert!(false, "Transaction is the not a single rounder sender variant"); + 0 + }, + }; + + runtime + .block_on(bob_tx_sender.send(create_dummy_message( + tx_sender_msg.into(), + alice_node_identity.public_key(), + ))) + .unwrap(); + + let _result_stream = runtime.block_on(async { + collect_stream!( + bob_ts.get_event_stream_fused(), + take = 1, + timeout = Duration::from_secs(20) + ) + }); + let call = bob_outbound_service.pop_call().unwrap(); + let envelope_body = EnvelopeBody::decode(&mut call.1.as_slice()).unwrap(); + let tx_reply_msg: RecipientSignedMessage = envelope_body + .decode_part::(1) + .unwrap() + .unwrap() + .try_into() + .unwrap(); + + runtime + .block_on(alice_tx_ack_sender.send(create_dummy_message( + tx_reply_msg.into(), + bob_node_identity.public_key(), + ))) + .unwrap(); + + let result_stream = runtime.block_on(async { + collect_stream!( + alice_ts.get_event_stream_fused().map(|i| (*i).clone()), + take = 3, + timeout = Duration::from_secs(60) + ) + }); + + assert_eq!( + 2, + result_stream.iter().fold(0, |acc, item| { + if let TransactionEvent::MempoolBroadcastTimedOut(_) = item { + acc + 1 + } else { + acc + } + }) + ); + + let alice_completed_tx = runtime + .block_on(alice_ts.get_completed_transactions()) + .unwrap() + .remove(&tx_id) + .expect("Transaction must be in collection"); + + assert_eq!(alice_completed_tx.status, TransactionStatus::Completed); + + let call = alice_outbound_service.pop_call().unwrap(); + let envelope_body = EnvelopeBody::decode(&mut call.1.as_slice()).unwrap(); + let msr = envelope_body + .decode_part::(1) + .unwrap() + .unwrap(); + + let mempool_service_request = MempoolServiceRequest::try_from(msr.clone()).unwrap(); + + let _ = alice_outbound_service.pop_call().unwrap(); // burn a tx broadcast + let _ = alice_outbound_service.pop_call().unwrap(); // burn a mempool request + let _ = alice_outbound_service.pop_call().unwrap(); // burn a tx broadcast + let _ = alice_outbound_service.pop_call().unwrap(); // burn a mempool request + let _ = alice_outbound_service.pop_call().unwrap(); // burn a tx broadcast + let call = alice_outbound_service.pop_call().unwrap(); // this should be the sending of the finalized tx to the receiver + + let envelope_body = EnvelopeBody::decode(&mut call.1.as_slice()).unwrap(); + let tx_finalized = envelope_body + .decode_part::(1) + .unwrap() + .unwrap(); + + runtime + .block_on(bob_tx_finalized_sender.send(create_dummy_message(tx_finalized, alice_node_identity.public_key()))) + .unwrap(); + + let result_stream = runtime.block_on(async { + collect_stream!( + bob_ts.get_event_stream_fused().map(|i| (*i).clone()), + take = 3, + timeout = Duration::from_secs(60) + ) + }); + + assert_eq!( + 1, + result_stream.iter().fold(0, |acc, item| { + if let TransactionEvent::MempoolBroadcastTimedOut(_) = item { + acc + 1 + } else { + acc + } + }) + ); + + let kernel_sig = alice_completed_tx.transaction.body.kernels()[0].clone().excess_sig; + assert_eq!(mempool_service_request.request_key, tx_id); + + match mempool_service_request.request { + MempoolRequest::GetStats => assert!(false, "Invalid Mempool Service Request variant"), + MempoolRequest::GetTxStateWithExcessSig(excess_sig) => assert_eq!(excess_sig, kernel_sig), + } + + let mempool_response = MempoolProto::MempoolServiceResponse { + request_key: tx_id, + response: Some(MempoolResponse::TxStorage(TxStorageResponse::NotStored).into()), + }; + + runtime + .block_on( + alice_mempool_response_sender.send(create_dummy_message(mempool_response, base_node_identity.public_key())), + ) + .unwrap(); + + let result_stream = runtime.block_on(async { + collect_stream!( + alice_ts.get_event_stream_fused().map(|i| (*i).clone()), + take = 4, + timeout = Duration::from_secs(60) + ) + }); + + assert_eq!( + 3, + result_stream.iter().fold(0, |acc, item| { + if let TransactionEvent::MempoolBroadcastTimedOut(_) = item { + acc + 1 + } else { + acc + } + }) + ); + + let mempool_response = MempoolProto::MempoolServiceResponse { + request_key: tx_id, + response: Some(MempoolResponse::TxStorage(TxStorageResponse::UnconfirmedPool).into()), + }; + + runtime + .block_on( + alice_mempool_response_sender.send(create_dummy_message(mempool_response, base_node_identity.public_key())), + ) + .unwrap(); + + let result_stream = runtime.block_on(async { + collect_stream!( + alice_ts.get_event_stream_fused().map(|i| (*i).clone()), + take = 5, + timeout = Duration::from_secs(60) + ) + }); + + assert_eq!( + 3, + result_stream.iter().fold(0, |acc, item| { + if let TransactionEvent::MempoolBroadcastTimedOut(_) = item { + acc + 1 + } else { + acc + } + }) + ); + + assert_eq!( + 1, + result_stream.iter().fold(0, |acc, item| { + if let TransactionEvent::TransactionBroadcast(_) = item { + acc + 1 + } else { + acc + } + }) + ); + + let alice_completed_tx = runtime + .block_on(alice_ts.get_completed_transactions()) + .unwrap() + .remove(&tx_id) + .expect("Transaction must be in collection"); + + assert_eq!(alice_completed_tx.status, TransactionStatus::Broadcast); +} + +#[test] +fn broadcast_all_completed_transactions_on_startup() { + let mut runtime = Runtime::new().unwrap(); + let factories = CryptoFactories::default(); + + let db = TransactionMemoryDatabase::new(); + + let kernel = KernelBuilder::new() + .with_excess(&factories.commitment.zero()) + .with_signature(&Signature::default()) + .build() + .unwrap(); + + let tx = Transaction::new(vec![], vec![], vec![kernel], PrivateKey::random(&mut OsRng)); + + let completed_tx1 = CompletedTransaction { + tx_id: 1, + source_public_key: PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), + destination_public_key: PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), + amount: 5000 * uT, + fee: MicroTari::from(100), + transaction: tx.clone(), + status: TransactionStatus::Completed, + message: "Yo!".to_string(), + timestamp: Utc::now().naive_utc(), + }; + + let completed_tx2 = CompletedTransaction { + tx_id: 2, + status: TransactionStatus::Broadcast, + ..completed_tx1.clone() + }; + + let completed_tx3 = CompletedTransaction { + tx_id: 3, + status: TransactionStatus::Completed, + ..completed_tx1.clone() + }; + + db.write(WriteOperation::Insert(DbKeyValuePair::CompletedTransaction( + completed_tx1.tx_id.clone(), + Box::new(completed_tx1.clone()), + ))) + .unwrap(); + + db.write(WriteOperation::Insert(DbKeyValuePair::CompletedTransaction( + completed_tx2.tx_id.clone(), + Box::new(completed_tx2.clone()), + ))) + .unwrap(); + + db.write(WriteOperation::Insert(DbKeyValuePair::CompletedTransaction( + completed_tx3.tx_id.clone(), + Box::new(completed_tx3.clone()), + ))) + .unwrap(); + + let (mut alice_ts, _, _, _, _, _, _, _, _) = + setup_transaction_service_no_comms(&mut runtime, factories.clone(), db); + + runtime + .block_on(alice_ts.set_base_node_public_key(PublicKey::default())) + .unwrap(); + + let result_stream = runtime.block_on(async { + collect_stream!( + alice_ts.get_event_stream_fused().map(|i| (*i).clone()), + take = 3, + timeout = Duration::from_secs(20) + ) + }); + + assert!(result_stream + .iter() + .find(|v| match v { + TransactionEvent::MempoolBroadcastTimedOut(tx_id) => *tx_id == 1u64, + _ => false, + }) + .is_some()); + assert!(result_stream + .iter() + .find(|v| match v { + TransactionEvent::MempoolBroadcastTimedOut(tx_id) => *tx_id == 3u64, + _ => false, + }) + .is_some()); +} + +#[test] +fn transaction_base_node_monitoring() { + let factories = CryptoFactories::default(); + let mut runtime = Runtime::new().unwrap(); + + let alice_node_identity = + NodeIdentity::random(&mut OsRng, get_next_memory_address(), PeerFeatures::COMMUNICATION_NODE).unwrap(); + + let bob_node_identity = + NodeIdentity::random(&mut OsRng, get_next_memory_address(), PeerFeatures::COMMUNICATION_NODE).unwrap(); + + let base_node_identity = + NodeIdentity::random(&mut OsRng, get_next_memory_address(), PeerFeatures::COMMUNICATION_NODE).unwrap(); + + let ( + mut alice_ts, + mut alice_output_manager, + alice_outbound_service, + mut _alice_tx_sender, + mut alice_tx_ack_sender, + _, + mut alice_mempool_response_sender, + mut alice_base_node_response_sender, + _, + ) = setup_transaction_service_no_comms(&mut runtime, factories.clone(), TransactionMemoryDatabase::new()); + + runtime + .block_on(alice_ts.set_base_node_public_key(base_node_identity.public_key().clone())) + .unwrap(); + + let (mut bob_ts, _, bob_outbound_service, mut bob_tx_sender, _, _, _, _, _) = + setup_transaction_service_no_comms(&mut runtime, factories.clone(), TransactionMemoryDatabase::new()); + + runtime + .block_on(bob_ts.set_base_node_public_key(base_node_identity.public_key().clone())) + .unwrap(); + + let alice_total_available = 250000 * uT; + let (_utxo, uo) = make_input(&mut OsRng, alice_total_available, &factories.commitment); + runtime.block_on(alice_output_manager.add_output(uo)).unwrap(); + + let amount_sent = 10000 * uT; + + runtime + .block_on(alice_ts.send_transaction( + bob_node_identity.public_key().clone(), + amount_sent, + 100 * uT, + "Testing Message".to_string(), + )) + .unwrap(); + + let call = alice_outbound_service.pop_call().unwrap(); + let envelope_body = EnvelopeBody::decode(&mut call.1.as_slice()).unwrap(); + let tx_sender_msg: TransactionSenderMessage = envelope_body + .decode_part::(1) + .unwrap() + .unwrap() + .try_into() + .unwrap(); + let tx_id = match tx_sender_msg.clone() { + TransactionSenderMessage::Single(s) => s.tx_id, + _ => { + assert!(false, "Transaction is the not a single rounder sender variant"); + 0 + }, + }; + + runtime + .block_on(bob_tx_sender.send(create_dummy_message( + tx_sender_msg.into(), + alice_node_identity.public_key(), + ))) + .unwrap(); + + let _result_stream = runtime.block_on(async { + collect_stream!( + bob_ts.get_event_stream_fused(), + take = 1, + timeout = Duration::from_secs(20) + ) + }); + let call = bob_outbound_service.pop_call().unwrap(); + let envelope_body = EnvelopeBody::decode(&mut call.1.as_slice()).unwrap(); + let tx_reply_msg: RecipientSignedMessage = envelope_body + .decode_part::(1) + .unwrap() + .unwrap() + .try_into() + .unwrap(); + + runtime + .block_on(alice_tx_ack_sender.send(create_dummy_message( + tx_reply_msg.into(), + bob_node_identity.public_key(), + ))) + .unwrap(); + + let _result_stream = runtime.block_on(async { + collect_stream!( + alice_ts.get_event_stream_fused().map(|i| (*i).clone()), + take = 3, + timeout = Duration::from_secs(60) + ) + }); + let alice_completed_tx = runtime + .block_on(alice_ts.get_completed_transactions()) + .unwrap() + .remove(&tx_id) + .expect("Transaction must be in collection"); + + assert_eq!(alice_completed_tx.status, TransactionStatus::Completed); + + let call = alice_outbound_service.pop_call().unwrap(); + let envelope_body = EnvelopeBody::decode(&mut call.1.as_slice()).unwrap(); + let msr = envelope_body + .decode_part::(1) + .unwrap() + .unwrap(); + + let mempool_service_request = MempoolServiceRequest::try_from(msr.clone()).unwrap(); + + let _ = alice_outbound_service.pop_call().unwrap(); // burn a tx broadcast + let _ = alice_outbound_service.pop_call().unwrap(); // burn a mempool request + + let kernel_sig = alice_completed_tx.transaction.body.kernels()[0].clone().excess_sig; + let tx_outputs: Vec = alice_completed_tx + .transaction + .body + .outputs() + .iter() + .map(|o| TransactionOutputProto::from(o.clone())) + .collect(); + assert_eq!(mempool_service_request.request_key, tx_id); + + match mempool_service_request.request { + MempoolRequest::GetStats => assert!(false, "Invalid Mempool Service Request variant"), + MempoolRequest::GetTxStateWithExcessSig(excess_sig) => assert_eq!(excess_sig, kernel_sig), + } + + let mempool_response = MempoolProto::MempoolServiceResponse { + request_key: tx_id, + response: Some(MempoolResponse::TxStorage(TxStorageResponse::UnconfirmedPool).into()), + }; + + runtime + .block_on( + alice_mempool_response_sender.send(create_dummy_message(mempool_response, base_node_identity.public_key())), + ) + .unwrap(); + + let result_stream = runtime.block_on(async { + collect_stream!( + alice_ts.get_event_stream_fused().map(|i| (*i).clone()), + take = 6, + timeout = Duration::from_secs(60) + ) + }); + assert!( + result_stream.iter().fold(0, |acc, item| { + if let TransactionEvent::TransactionMinedRequestTimedOut(_) = item { + acc + 1 + } else { + acc + } + }) >= 2 + ); + + let wrong_outputs = vec![tx_outputs[0].clone(), TransactionOutput::default().into()]; + + let base_node_response = BaseNodeProto::BaseNodeServiceResponse { + request_key: tx_id.clone(), + response: Some(BaseNodeResponseProto::TransactionOutputs( + BaseNodeProto::TransactionOutputs { + outputs: wrong_outputs.into(), + }, + )), + }; + + runtime + .block_on(alice_base_node_response_sender.send(create_dummy_message( + base_node_response, + base_node_identity.public_key(), + ))) + .unwrap(); + + let result_stream = runtime.block_on(async { + collect_stream!( + alice_ts.get_event_stream_fused().map(|i| (*i).clone()), + take = 7, + timeout = Duration::from_secs(60) + ) + }); + + assert!( + result_stream.iter().fold(0, |acc, item| { + if let TransactionEvent::TransactionMinedRequestTimedOut(_) = item { + acc + 1 + } else { + acc + } + }) >= 3 + ); + + let alice_completed_tx = runtime + .block_on(alice_ts.get_completed_transactions()) + .unwrap() + .remove(&tx_id) + .expect("Transaction must be in collection"); + + assert_eq!(alice_completed_tx.status, TransactionStatus::Broadcast); + + let base_node_response = BaseNodeProto::BaseNodeServiceResponse { + request_key: tx_id.clone(), + response: Some(BaseNodeResponseProto::TransactionOutputs( + BaseNodeProto::TransactionOutputs { + outputs: tx_outputs.into(), + }, + )), + }; + + runtime + .block_on(alice_base_node_response_sender.send(create_dummy_message( + base_node_response, + base_node_identity.public_key(), + ))) + .unwrap(); + + let result_stream = runtime.block_on(async { + collect_stream!( + alice_ts.get_event_stream_fused().map(|i| (*i).clone()), + take = 8, + timeout = Duration::from_secs(60) + ) + }); + + assert_eq!( + 1, + result_stream.iter().fold(0, |acc, item| { + if let TransactionEvent::TransactionMined(_) = item { + acc + 1 + } else { + acc + } + }) + ); + + let alice_completed_tx = runtime + .block_on(alice_ts.get_completed_transactions()) + .unwrap() + .remove(&tx_id) + .expect("Transaction must be in collection"); + + assert_eq!(alice_completed_tx.status, TransactionStatus::Mined); + + let balance = runtime.block_on(alice_output_manager.get_balance()).unwrap(); + + assert_eq!( + balance.available_balance, + alice_total_available - amount_sent - alice_completed_tx.fee + ); +} + +#[test] +fn query_all_completed_transactions_on_startup() { + let mut runtime = Runtime::new().unwrap(); + let factories = CryptoFactories::default(); + + let db = TransactionMemoryDatabase::new(); + + let kernel = KernelBuilder::new() + .with_excess(&factories.commitment.zero()) + .with_signature(&Signature::default()) + .build() + .unwrap(); + + let tx = Transaction::new(vec![], vec![], vec![kernel], PrivateKey::random(&mut OsRng)); + + let completed_tx1 = CompletedTransaction { + tx_id: 1, + source_public_key: PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), + destination_public_key: PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), + amount: 5000 * uT, + fee: MicroTari::from(100), + transaction: tx.clone(), + status: TransactionStatus::Broadcast, + message: "Yo!".to_string(), + timestamp: Utc::now().naive_utc(), + }; + + let completed_tx2 = CompletedTransaction { + tx_id: 2, + status: TransactionStatus::Broadcast, + ..completed_tx1.clone() + }; + + let completed_tx3 = CompletedTransaction { + tx_id: 3, + status: TransactionStatus::Mined, + ..completed_tx1.clone() + }; + + db.write(WriteOperation::Insert(DbKeyValuePair::CompletedTransaction( + completed_tx1.tx_id.clone(), + Box::new(completed_tx1.clone()), + ))) + .unwrap(); + + db.write(WriteOperation::Insert(DbKeyValuePair::CompletedTransaction( + completed_tx2.tx_id.clone(), + Box::new(completed_tx2.clone()), + ))) + .unwrap(); + + db.write(WriteOperation::Insert(DbKeyValuePair::CompletedTransaction( + completed_tx3.tx_id.clone(), + Box::new(completed_tx3.clone()), + ))) + .unwrap(); + + let (mut alice_ts, _, _, _, _, _, _, _, _) = + setup_transaction_service_no_comms(&mut runtime, factories.clone(), db); + + runtime + .block_on(alice_ts.set_base_node_public_key(PublicKey::default())) + .unwrap(); + + let result_stream = runtime.block_on(async { + collect_stream!( + alice_ts.get_event_stream_fused().map(|i| (*i).clone()), + take = 2, + timeout = Duration::from_secs(20) + ) + }); + + assert!(result_stream + .iter() + .find(|v| match v { + TransactionEvent::TransactionMinedRequestTimedOut(tx_id) => *tx_id == 1u64, + _ => false, + }) + .is_some()); + assert!(result_stream + .iter() + .find(|v| match v { + TransactionEvent::TransactionMinedRequestTimedOut(tx_id) => *tx_id == 2u64, + _ => false, + }) + .is_some()); +} diff --git a/base_layer/wallet/tests/transaction_service/storage.rs b/base_layer/wallet/tests/transaction_service/storage.rs new file mode 100644 index 0000000000..0112280a06 --- /dev/null +++ b/base_layer/wallet/tests/transaction_service/storage.rs @@ -0,0 +1,308 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::support::utils::random_string; +use chrono::Utc; +use rand::rngs::OsRng; +use tari_core::transactions::{ + tari_amount::MicroTari, + transaction::{OutputFeatures, Transaction, UnblindedOutput}, + transaction_protocol::sender::TransactionSenderMessage, + types::{CommitmentFactory, CryptoFactories, HashDigest, PrivateKey, PublicKey}, + ReceiverTransactionProtocol, + SenderTransactionProtocol, +}; +use tari_crypto::{ + commitment::HomomorphicCommitmentFactory, + keys::{PublicKey as PublicKeyTrait, SecretKey as SecretKeyTrait}, +}; +use tari_wallet::{ + storage::connection_manager::run_migration_and_create_connection_pool, + transaction_service::storage::{ + database::{ + CompletedTransaction, + InboundTransaction, + OutboundTransaction, + PendingCoinbaseTransaction, + TransactionBackend, + TransactionDatabase, + TransactionStatus, + }, + memory_db::TransactionMemoryDatabase, + sqlite_db::TransactionServiceSqliteDatabase, + }, +}; +use tempdir::TempDir; +use tokio::runtime::Runtime; + +pub fn test_db_backend(backend: T) { + let mut runtime = Runtime::new().unwrap(); + let mut db = TransactionDatabase::new(backend); + let factories = CryptoFactories::default(); + let mut builder = SenderTransactionProtocol::builder(1); + let amount = MicroTari::from(10_000); + let input = UnblindedOutput::new(MicroTari::from(100_000), PrivateKey::random(&mut OsRng), None); + builder + .with_lock_height(0) + .with_fee_per_gram(MicroTari::from(177)) + .with_offset(PrivateKey::random(&mut OsRng)) + .with_private_nonce(PrivateKey::random(&mut OsRng)) + .with_amount(0, amount) + .with_message("Yo!".to_string()) + .with_input( + input.as_transaction_input(&factories.commitment, OutputFeatures::default()), + input.clone(), + ) + .with_change_secret(PrivateKey::random(&mut OsRng)); + + let stp = builder.build::(&factories).unwrap(); + + let messages = vec!["Hey!".to_string(), "Yo!".to_string(), "Sup!".to_string()]; + let amounts = vec![MicroTari::from(10_000), MicroTari::from(23_000), MicroTari::from(5_000)]; + + let mut outbound_txs = Vec::new(); + + for i in 0..messages.len() { + outbound_txs.push(OutboundTransaction { + tx_id: (i + 10) as u64, + destination_public_key: PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), + amount: amounts[i].clone(), + fee: stp.clone().get_fee_amount().unwrap(), + sender_protocol: stp.clone(), + message: messages[i].clone(), + timestamp: Utc::now().naive_utc(), + }); + assert!( + !runtime.block_on(db.transaction_exists((i + 10) as u64)).unwrap(), + "TxId should not exist" + ); + + runtime + .block_on(db.add_pending_outbound_transaction(outbound_txs[i].tx_id, outbound_txs[i].clone())) + .unwrap(); + assert!( + runtime.block_on(db.transaction_exists((i + 10) as u64)).unwrap(), + "TxId should exist" + ); + } + + let retrieved_outbound_txs = runtime.block_on(db.get_pending_outbound_transactions()).unwrap(); + assert_eq!(outbound_txs.len(), messages.len()); + for i in 0..messages.len() { + let retrieved_outbound_tx = runtime + .block_on(db.get_pending_outbound_transaction(outbound_txs[i].tx_id)) + .unwrap(); + assert_eq!(retrieved_outbound_tx, outbound_txs[i]); + + assert_eq!( + retrieved_outbound_txs.get(&outbound_txs[i].tx_id).unwrap(), + &outbound_txs[i] + ); + } + + let rtp = ReceiverTransactionProtocol::new( + TransactionSenderMessage::Single(Box::new(stp.clone().build_single_round_message().unwrap())), + PrivateKey::random(&mut OsRng), + PrivateKey::random(&mut OsRng), + OutputFeatures::default(), + &factories, + ); + + let mut inbound_txs = Vec::new(); + + for i in 0..messages.len() { + inbound_txs.push(InboundTransaction { + tx_id: i as u64, + source_public_key: PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), + amount: amounts[i].clone(), + receiver_protocol: rtp.clone(), + message: messages[i].clone(), + timestamp: Utc::now().naive_utc(), + }); + assert!( + !runtime.block_on(db.transaction_exists(i as u64)).unwrap(), + "TxId should not exist" + ); + runtime + .block_on(db.add_pending_inbound_transaction(i as u64, inbound_txs[i].clone())) + .unwrap(); + assert!( + runtime.block_on(db.transaction_exists(i as u64)).unwrap(), + "TxId should exist" + ); + } + + let retrieved_inbound_txs = runtime.block_on(db.get_pending_inbound_transactions()).unwrap(); + assert_eq!(inbound_txs.len(), messages.len()); + for i in 0..messages.len() { + assert_eq!( + retrieved_inbound_txs.get(&inbound_txs[i].tx_id).unwrap(), + &inbound_txs[i] + ); + } + + let mut coinbases = Vec::new(); + for i in 0..messages.len() { + coinbases.push(PendingCoinbaseTransaction { + tx_id: (i + 100) as u64, + amount: amounts[i].clone(), + commitment: CommitmentFactory::default().zero(), + timestamp: Utc::now().naive_utc(), + }); + + assert!(!runtime.block_on(db.transaction_exists((i + 100) as u64)).unwrap()); + runtime + .block_on(db.add_pending_coinbase_transaction((i + 100) as u64, coinbases[i].clone())) + .unwrap(); + assert!(runtime.block_on(db.transaction_exists((i + 100) as u64)).unwrap()); + } + + runtime + .block_on( + db.add_pending_coinbase_transaction(9999u64, PendingCoinbaseTransaction { + tx_id: 9999u64, + amount: MicroTari::from(10000), + commitment: CommitmentFactory::default().zero(), + timestamp: Utc::now().naive_utc(), + }), + ) + .unwrap(); + + runtime.block_on(db.cancel_coinbase_transaction(9999u64)).unwrap(); + + let read_coinbases = runtime.block_on(db.get_pending_coinbase_transactions()).unwrap(); + assert_eq!(read_coinbases.len(), messages.len()); + for i in 0..messages.len() { + assert_eq!(read_coinbases.get(&coinbases[i].tx_id).unwrap(), &coinbases[i]); + } + + let mut completed_txs = Vec::new(); + let tx = Transaction::new(vec![], vec![], vec![], PrivateKey::random(&mut OsRng)); + + for i in 0..messages.len() { + completed_txs.push(CompletedTransaction { + tx_id: outbound_txs[i].tx_id, + source_public_key: PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), + destination_public_key: PublicKey::from_secret_key(&PrivateKey::random(&mut OsRng)), + amount: outbound_txs[i].amount, + fee: MicroTari::from(200), + transaction: tx.clone(), + status: match i { + 0 => TransactionStatus::Completed, + 1 => TransactionStatus::Broadcast, + _ => TransactionStatus::Mined, + }, + message: messages[i].clone(), + timestamp: Utc::now().naive_utc(), + }); + runtime + .block_on(db.complete_outbound_transaction(outbound_txs[i].tx_id, completed_txs[i].clone())) + .unwrap(); + runtime + .block_on( + db.complete_inbound_transaction(inbound_txs[i].tx_id, CompletedTransaction { + tx_id: inbound_txs[i].tx_id, + ..completed_txs[i].clone() + }), + ) + .unwrap(); + runtime + .block_on( + db.complete_coinbase_transaction(coinbases[i].tx_id, CompletedTransaction { + tx_id: coinbases[i].tx_id, + ..completed_txs[i].clone() + }), + ) + .unwrap(); + } + + let retrieved_completed_txs = runtime.block_on(db.get_completed_transactions()).unwrap(); + assert_eq!(retrieved_completed_txs.len(), 3 * messages.len()); + + for i in 0..messages.len() { + assert_eq!( + retrieved_completed_txs.get(&inbound_txs[i].tx_id).unwrap(), + &CompletedTransaction { + tx_id: inbound_txs[i].tx_id, + ..completed_txs[i].clone() + } + ); + assert_eq!( + retrieved_completed_txs.get(&outbound_txs[i].tx_id).unwrap(), + &completed_txs[i] + ); + assert_eq!( + retrieved_completed_txs.get(&coinbases[i].tx_id).unwrap(), + &CompletedTransaction { + tx_id: coinbases[i].tx_id, + ..completed_txs[i].clone() + } + ); + } + + if cfg!(feature = "test_harness") { + let retrieved_completed_txs = runtime.block_on(db.get_completed_transactions()).unwrap(); + assert!(retrieved_completed_txs.contains_key(&completed_txs[0].tx_id)); + assert_eq!( + retrieved_completed_txs.get(&completed_txs[0].tx_id).unwrap().status, + TransactionStatus::Completed + ); + #[cfg(feature = "test_harness")] + runtime + .block_on(db.broadcast_completed_transaction(completed_txs[0].tx_id.clone())) + .unwrap(); + let retrieved_completed_txs = runtime.block_on(db.get_completed_transactions()).unwrap(); + + assert!(retrieved_completed_txs.contains_key(&completed_txs[0].tx_id)); + assert_eq!( + retrieved_completed_txs.get(&completed_txs[0].tx_id).unwrap().status, + TransactionStatus::Broadcast + ); + + #[cfg(feature = "test_harness")] + runtime + .block_on(db.mine_completed_transaction(completed_txs[0].tx_id.clone())) + .unwrap(); + let retrieved_completed_txs = runtime.block_on(db.get_completed_transactions()).unwrap(); + + assert!(retrieved_completed_txs.contains_key(&completed_txs[0].tx_id)); + assert_eq!( + retrieved_completed_txs.get(&completed_txs[0].tx_id).unwrap().status, + TransactionStatus::Mined + ); + } +} + +#[test] +pub fn test_transaction_service_memory_db() { + test_db_backend(TransactionMemoryDatabase::new()); +} + +#[test] +pub fn test_transaction_service_sqlite_db() { + let db_name = format!("{}.sqlite3", random_string(8).as_str()); + let db_tempdir = TempDir::new(random_string(8).as_str()).unwrap(); + let db_folder = db_tempdir.path().to_str().unwrap().to_string(); + let db_path = format!("{}/{}", db_folder, db_name); + let connection_pool = run_migration_and_create_connection_pool(&db_path).unwrap(); + test_db_backend(TransactionServiceSqliteDatabase::new(connection_pool)); +} diff --git a/base_layer/wallet/tests/wallet/mod.rs b/base_layer/wallet/tests/wallet/mod.rs index 2649748f17..4d667fea90 100644 --- a/base_layer/wallet/tests/wallet/mod.rs +++ b/base_layer/wallet/tests/wallet/mod.rs @@ -20,180 +20,338 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::support::utils::assert_change; -use std::{path::PathBuf, time::Duration}; +use crate::support::utils::{make_input, random_string}; +use rand::rngs::OsRng; +use std::{sync::Arc, time::Duration}; use tari_comms::{ - connection::{net_address::NetAddressWithStats, NetAddress, NetAddressesWithStats}, - control_service::ControlServiceConfig, - peer_manager::{peer::PeerFlags, NodeId, Peer}, - types::{CommsPublicKey, CommsSecretKey}, + multiaddr::Multiaddr, + peer_manager::{NodeId, NodeIdentity, Peer, PeerFeatures, PeerFlags}, + types::CommsPublicKey, }; -use tari_crypto::keys::{PublicKey, SecretKey}; +#[cfg(feature = "test_harness")] +use tari_comms_dht::DhtConfig; +use tari_core::transactions::{tari_amount::MicroTari, types::CryptoFactories}; +use tari_crypto::keys::PublicKey; use tari_p2p::initialization::CommsConfig; -use tari_wallet::{text_message_service::Contact, wallet::WalletConfig, Wallet}; +use tari_test_utils::{collect_stream, paths::with_temp_dir}; -fn create_peer(public_key: CommsPublicKey, net_address: NetAddress) -> Peer { +use crate::support::comms_and_services::get_next_memory_address; +use tari_core::transactions::{tari_amount::uT, transaction::UnblindedOutput, types::PrivateKey}; +use tari_p2p::transport::TransportType; +use tari_wallet::{ + contacts_service::storage::{database::Contact, memory_db::ContactsServiceMemoryDatabase}, + output_manager_service::storage::memory_db::OutputManagerMemoryDatabase, + storage::memory_db::WalletMemoryDatabase, + transaction_service::{handle::TransactionEvent, storage::memory_db::TransactionMemoryDatabase}, + wallet::WalletConfig, + Wallet, +}; +use tempdir::TempDir; +use tokio::runtime::Runtime; + +fn create_peer(public_key: CommsPublicKey, net_address: Multiaddr) -> Peer { Peer::new( public_key.clone(), NodeId::from_key(&public_key).unwrap(), - NetAddressesWithStats::new(vec![NetAddressWithStats::new(net_address.clone())]), + net_address.into(), PeerFlags::empty(), + PeerFeatures::COMMUNICATION_NODE, ) } -fn get_path(name: Option<&str>) -> String { - let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - path.push("tests/data"); - path.push(name.unwrap_or("")); - path.to_str().unwrap().to_string() -} +#[test] +fn test_wallet() { + with_temp_dir(|dir_path| { + let mut runtime = Runtime::new().unwrap(); + let factories = CryptoFactories::default(); + let alice_identity = NodeIdentity::random( + &mut OsRng, + "/ip4/127.0.0.1/tcp/22523".parse().unwrap(), + PeerFeatures::COMMUNICATION_NODE, + ) + .unwrap(); + let bob_identity = NodeIdentity::random( + &mut OsRng, + "/ip4/127.0.0.1/tcp/22145".parse().unwrap(), + PeerFeatures::COMMUNICATION_NODE, + ) + .unwrap(); -fn clean_up_datastore(name: &str) { - std::fs::remove_dir_all(get_path(Some(name))).unwrap(); -} + let base_node_identity = NodeIdentity::random( + &mut OsRng, + "/ip4/127.0.0.1/tcp/54225".parse().unwrap(), + PeerFeatures::COMMUNICATION_NODE, + ) + .unwrap(); -fn clean_up_sql_database(name: &str) { - if std::fs::metadata(get_path(Some(name))).is_ok() { - std::fs::remove_file(get_path(Some(name))).unwrap(); - } -} + let comms_config1 = CommsConfig { + node_identity: Arc::new(alice_identity.clone()), + transport_type: TransportType::Tcp { + listener_address: alice_identity.public_address(), + }, + datastore_path: dir_path.to_str().unwrap().to_string(), + peer_database_name: random_string(8), + max_concurrent_inbound_tasks: 100, + outbound_buffer_size: 100, + dht: Default::default(), + }; + let comms_config2 = CommsConfig { + node_identity: Arc::new(bob_identity.clone()), + transport_type: TransportType::Tcp { + listener_address: bob_identity.public_address(), + }, + datastore_path: dir_path.to_str().unwrap().to_string(), + peer_database_name: random_string(8), + max_concurrent_inbound_tasks: 100, + outbound_buffer_size: 100, + dht: Default::default(), + }; + let config1 = WalletConfig { + comms_config: comms_config1, + logging_path: None, + factories: factories.clone(), + }; + let config2 = WalletConfig { + comms_config: comms_config2, + logging_path: None, + factories: factories.clone(), + }; + let runtime_node1 = Runtime::new().unwrap(); + let runtime_node2 = Runtime::new().unwrap(); + let mut alice_wallet = Wallet::new( + config1, + runtime_node1, + WalletMemoryDatabase::new(), + TransactionMemoryDatabase::new(), + OutputManagerMemoryDatabase::new(), + ContactsServiceMemoryDatabase::new(), + ) + .unwrap(); + let bob_wallet = Wallet::new( + config2, + runtime_node2, + WalletMemoryDatabase::new(), + TransactionMemoryDatabase::new(), + OutputManagerMemoryDatabase::new(), + ContactsServiceMemoryDatabase::new(), + ) + .unwrap(); + + alice_wallet + .comms + .peer_manager() + .add_peer(create_peer( + bob_identity.public_key().clone(), + bob_identity.public_address(), + )) + .unwrap(); + + bob_wallet + .comms + .peer_manager() + .add_peer(create_peer( + alice_identity.public_key().clone(), + alice_identity.public_address(), + )) + .unwrap(); + + alice_wallet + .set_base_node_peer( + (*base_node_identity.public_key()).clone(), + get_next_memory_address().to_string(), + ) + .unwrap(); + + let alice_event_stream = alice_wallet.transaction_service.get_event_stream_fused(); + + let value = MicroTari::from(1000); + let (_utxo, uo1) = make_input(&mut OsRng, MicroTari(2500), &factories.commitment); + + runtime + .block_on(alice_wallet.output_manager_service.add_output(uo1)) + .unwrap(); + + runtime + .block_on(alice_wallet.transaction_service.send_transaction( + bob_identity.public_key().clone(), + value, + MicroTari::from(20), + "".to_string(), + )) + .unwrap(); + + let result_stream = runtime.block_on(async { + collect_stream!( + alice_event_stream.map(|i| (*i).clone()), + take = 2, + timeout = Duration::from_secs(10) + ) + }); + let received_transaction_reply_count = result_stream.iter().fold(0, |acc, x| match x { + TransactionEvent::ReceivedTransactionReply(_) => acc + 1, + _ => acc, + }); + + assert_eq!( + received_transaction_reply_count, 1, + "Did not received correct numebr of replies" + ); + + let mut contacts = Vec::new(); + for i in 0..2 { + let (_secret_key, public_key) = PublicKey::random_keypair(&mut OsRng); + + contacts.push(Contact { + alias: random_string(8), + public_key, + }); + + runtime + .block_on(alice_wallet.contacts_service.upsert_contact(contacts[i].clone())) + .unwrap(); + } -fn init_sql_database(name: &str) { - clean_up_sql_database(name); - let path = get_path(None); - let _ = std::fs::create_dir(&path).unwrap_or_default(); + let got_contacts = runtime.block_on(alice_wallet.contacts_service.get_contacts()).unwrap(); + assert_eq!(contacts, got_contacts); + }); } #[test] -fn test_wallet() { - let mut rng = rand::OsRng::new().unwrap(); - - let db_name1 = "test_wallet1.sqlite3"; - let db_path1 = get_path(Some(db_name1)); - init_sql_database(db_name1); - - let db_name2 = "test_wallet2.sqlite3"; - let db_path2 = get_path(Some(db_name2)); - init_sql_database(db_name2); - - let listener_address1: NetAddress = "127.0.0.1:32775".parse().unwrap(); - let secret_key1 = CommsSecretKey::random(&mut rng); - let public_key1 = CommsPublicKey::from_secret_key(&secret_key1); - let wallet1_peer_database_name = "wallet1_peer_database".to_string(); - let config1 = WalletConfig { - comms: CommsConfig { - control_service: ControlServiceConfig { - listener_address: listener_address1.clone(), - socks_proxy_address: None, - requested_connection_timeout: Duration::from_millis(5000), - }, - socks_proxy_address: None, - host: "127.0.0.1".parse().unwrap(), - public_key: public_key1.clone(), - secret_key: secret_key1, - public_address: listener_address1.clone(), - datastore_path: get_path(Some(&wallet1_peer_database_name)), - peer_database_name: wallet1_peer_database_name.clone(), +fn test_import_utxo() { + let factories = CryptoFactories::default(); + let alice_identity = NodeIdentity::random( + &mut OsRng, + "/ip4/127.0.0.1/tcp/24521".parse().unwrap(), + PeerFeatures::COMMUNICATION_NODE, + ) + .unwrap(); + let base_node_identity = NodeIdentity::random( + &mut OsRng, + "/ip4/127.0.0.1/tcp/24522".parse().unwrap(), + PeerFeatures::COMMUNICATION_NODE, + ) + .unwrap(); + let temp_dir = TempDir::new(random_string(8).as_str()).unwrap(); + let comms_config = CommsConfig { + node_identity: Arc::new(alice_identity.clone()), + transport_type: TransportType::Tcp { + listener_address: "/ip4/127.0.0.1/tcp/0".parse().unwrap(), }, - public_key: public_key1.clone(), - database_path: db_path1, + datastore_path: temp_dir.path().to_str().unwrap().to_string(), + peer_database_name: random_string(8), + max_concurrent_inbound_tasks: 100, + outbound_buffer_size: 100, + dht: Default::default(), }; - let wallet1 = Wallet::new(config1).unwrap(); - - let listener_address2: NetAddress = "127.0.0.1:32776".parse().unwrap(); - let secret_key2 = CommsSecretKey::random(&mut rng); - let public_key2 = CommsPublicKey::from_secret_key(&secret_key2); - let wallet2_peer_database_name = "wallet2_peer_database".to_string(); - let config2 = WalletConfig { - comms: CommsConfig { - control_service: ControlServiceConfig { - listener_address: listener_address2.clone(), - socks_proxy_address: None, - requested_connection_timeout: Duration::from_millis(5000), - }, - socks_proxy_address: None, - host: "127.0.0.1".parse().unwrap(), - public_key: public_key2.clone(), - secret_key: secret_key2, - public_address: listener_address2.clone(), - datastore_path: get_path(Some(&wallet2_peer_database_name)), - peer_database_name: wallet2_peer_database_name.clone(), - }, - public_key: public_key2.clone(), - database_path: db_path2, + let config = WalletConfig { + comms_config, + logging_path: None, + factories: factories.clone(), }; + let runtime_node = Runtime::new().unwrap(); + let mut alice_wallet = Wallet::new( + config, + runtime_node, + WalletMemoryDatabase::new(), + TransactionMemoryDatabase::new(), + OutputManagerMemoryDatabase::new(), + ContactsServiceMemoryDatabase::new(), + ) + .unwrap(); - let wallet2 = Wallet::new(config2).unwrap(); + let utxo = UnblindedOutput::new(20000 * uT, PrivateKey::default(), None); - wallet1 - .comms_services - .peer_manager() - .add_peer(create_peer(public_key2.clone(), listener_address2.clone())) + let tx_id = alice_wallet + .import_utxo( + utxo.value, + &utxo.spending_key, + base_node_identity.public_key(), + "Testing".to_string(), + ) .unwrap(); - wallet2 - .comms_services - .peer_manager() - .add_peer(create_peer(public_key1.clone(), listener_address1.clone())) + let balance = alice_wallet + .runtime + .block_on(alice_wallet.output_manager_service.get_balance()) .unwrap(); - wallet1 - .text_message_service - .add_contact(Contact::new( - "Alice".to_string(), - public_key2.clone(), - listener_address2, - )) - .unwrap(); + assert_eq!(balance.available_balance, 20000 * uT); - wallet2 - .text_message_service - .add_contact(Contact::new("Bob".to_string(), public_key1.clone(), listener_address1)) - .unwrap(); + let completed_tx = alice_wallet + .runtime + .block_on(alice_wallet.transaction_service.get_completed_transactions()) + .unwrap() + .remove(&tx_id) + .expect("Tx should be in collection"); - wallet1 - .text_message_service - .send_text_message(public_key2.clone(), "Say Hello,".to_string()) - .unwrap(); + assert_eq!(completed_tx.amount, 20000 * uT); +} + +#[cfg(feature = "test_harness")] +#[test] +fn test_data_generation() { + use tari_wallet::testnet_utils::generate_wallet_test_data; + let runtime = Runtime::new().unwrap(); + let factories = CryptoFactories::default(); + let node_id = + NodeIdentity::random(&mut OsRng, get_next_memory_address(), PeerFeatures::COMMUNICATION_NODE).unwrap(); + let temp_dir = TempDir::new(random_string(8).as_str()).unwrap(); + let comms_config = CommsConfig { + node_identity: Arc::new(node_id.clone()), + transport_type: TransportType::Memory { + listener_address: "/memory/0".parse().unwrap(), + }, + datastore_path: temp_dir.path().to_str().unwrap().to_string(), + peer_database_name: random_string(8), + max_concurrent_inbound_tasks: 100, + outbound_buffer_size: 100, + dht: DhtConfig { + discovery_request_timeout: Duration::from_millis(500), + ..Default::default() + }, + }; - wallet2 - .text_message_service - .send_text_message(public_key1.clone(), "hello?".to_string()) + let config = WalletConfig { + comms_config, + factories, + logging_path: None, + }; + + let transaction_backend = TransactionMemoryDatabase::new(); + + let mut wallet = Wallet::new( + config, + runtime, + WalletMemoryDatabase::new(), + transaction_backend.clone(), + OutputManagerMemoryDatabase::new(), + ContactsServiceMemoryDatabase::new(), + ) + .unwrap(); + + generate_wallet_test_data(&mut wallet, temp_dir.path().to_str().unwrap(), transaction_backend).unwrap(); + + let contacts = wallet.runtime.block_on(wallet.contacts_service.get_contacts()).unwrap(); + assert!(contacts.len() > 0); + + let balance = wallet + .runtime + .block_on(wallet.output_manager_service.get_balance()) .unwrap(); + assert!(balance.available_balance > MicroTari::from(0)); - wallet1 - .text_message_service - .send_text_message(public_key2.clone(), "to my little friend!".to_string()) + // TODO Put this back when the new comms goes in and we use the new Event bus + // let outbound_tx = wallet + // .runtime + // .block_on(wallet.transaction_service.get_pending_outbound_transactions()) + // .unwrap(); + // assert!(outbound_tx.len() > 0); + + let completed_tx = wallet + .runtime + .block_on(wallet.transaction_service.get_completed_transactions()) .unwrap(); + assert!(completed_tx.len() > 0); - assert_change( - || { - let msgs = wallet1.text_message_service.get_text_messages().unwrap(); - (msgs.sent_messages.len(), msgs.received_messages.len()) - }, - (2, 1), - 50, - ); - - assert_change( - || { - let msgs = wallet2.text_message_service.get_text_messages().unwrap(); - (msgs.sent_messages.len(), msgs.received_messages.len()) - }, - (1, 2), - 50, - ); - - wallet1.ping_pong_service.ping(public_key2.clone()).unwrap(); - wallet2.ping_pong_service.ping(public_key1.clone()).unwrap(); - - assert_change(|| wallet1.ping_pong_service.ping_count().unwrap(), 2, 20); - assert_change(|| wallet1.ping_pong_service.pong_count().unwrap(), 2, 20); - assert_change(|| wallet2.ping_pong_service.ping_count().unwrap(), 2, 20); - assert_change(|| wallet2.ping_pong_service.pong_count().unwrap(), 2, 20); - - clean_up_datastore(&wallet1_peer_database_name); - clean_up_datastore(&wallet2_peer_database_name); - clean_up_sql_database(db_name1); - clean_up_sql_database(db_name2); + wallet.shutdown(); } diff --git a/base_layer/wallet_ffi/Cargo.toml b/base_layer/wallet_ffi/Cargo.toml new file mode 100644 index 0000000000..b33808c2f2 --- /dev/null +++ b/base_layer/wallet_ffi/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "tari_wallet_ffi" +authors = ["The Tari Development Community"] +description = "Tari cryptocurrency wallet C FFI bindings" +license = "BSD-3-Clause" +version = "0.0.9" +edition = "2018" + +[dependencies] +tari_comms = { path = "../../comms", version = "^0.0"} +tari_comms_dht = { path = "../../comms/dht", version = "^0.0"} +tari_crypto = { version = "^0.3" } +tari_p2p = {path = "../p2p", version = "^0.0"} +tari_wallet = { path = "../wallet", version = "^0.0", features = ["test_harness", "c_integration"]} +tari_shutdown = { path = "../../infrastructure/shutdown", version = "^0.0"} +tari_utilities = "0.1.1" + +futures = { version = "^0.3.1", features =["compat", "std"]} +tokio = "0.2.10" +libc = "0.2.65" +rand = "0.7.2" +chrono = { version = "0.4.6", features = ["serde"]} +tari_broadcast_channel = "^0.1" +derive-error = "0.0.4" +log = "0.4.6" + +[dependencies.tari_core] +path = "../../base_layer/core" +version = "^0.0" +default-features = false +features = ["transactions"] + +[lib] +crate-type = ["staticlib","cdylib"] + +[dev-dependencies] +tempdir = "0.3.7" +lazy_static = "1.3.0" +env_logger = "0.7.1" diff --git a/base_layer/wallet_ffi/README.md b/base_layer/wallet_ffi/README.md new file mode 100644 index 0000000000..001194afd6 --- /dev/null +++ b/base_layer/wallet_ffi/README.md @@ -0,0 +1,115 @@ +# Tari Wallet FFI + +Foreign Function interface for the Tari Android and Tari iOS Wallets. + +This crate is part of the [Tari Cryptocurrency](https://tari.com) project. + +# Build setup (Mac) + +## Homebrew + +Install Brew +```Shell Script +/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" +``` + +Run the following to install the needed bottles +```Shell Script +brew install pkgconfig +brew install git +brew install make +brew install cmake +brew install autoconf +brew install automake +brew install libtool +``` + +## iOS Dependencies + +Install [XCode](https://apps.apple.com/za/app/xcode/id497799835?mt=12) and then the XCode Command Line Tools with the following command +```Shell Script +xcode-select --install +``` + +For macOS Mojave additional headers need to be installed, run +```Shell Script +open /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg +``` +and follow the prompts + +## Android Dependencies + +Download the [Android NDK Bundle](https://developer.android.com/ndk/downloads) + +## Enable Hidden Files + +Run the following to show hidden files and folders +```Shell Script +defaults write com.apple.finder AppleShowAllFiles -bool YES +killall Finder +``` +## The Code + +Clone the following git repositories +1. [Tari](https://github.com/tari-project/tari.git) +2. [Wallet-Android](https://github.com/tari-project/wallet-android.git) +3. [Wallet-iOS](https://github.com/tari-project/wallet-ios.git) + +Afterwards ```cd``` into the Tari repository and run the following +```Shell Script +git submodule init +git config submodule.recurse true +git submodule update --recursive --remote +``` + +## Rust +Install [Rust](https://www.rust-lang.org/tools/install) + +Install the following tools and system images +```Shell Script +rustup toolchain add nightly-2019-10-04 +rustup default nightly-2019-10-04 +rustup component add rustfmt --toolchain nightly +rustup component add clippy +``` + +## Build Configuration + +To configure the build, ```cd``` to the Tari repository and then +```Shell Script +cd base_layer/wallet_ffi +open build.sample.config +``` + +Which will present you with the file contents as follows +```text +BUILD_ANDROID=1 +BUILD_IOS=1 +SQLITE_SOURCE=https://www.sqlite.org/snapshot/sqlite-snapshot-201911192122.tar.gz +NDK_PATH=/Users/user/Desktop/android-ndk-r20 +PKG_PATH=/usr/local/Cellar/zeromq/4.3.2/lib/pkgconfig + +ANDROID_WALLET_PATH=/Users/user/Desktop/wallet-android +IOS_WALLET_PATH=/Users/user/Desktop/wallet-ios +TARI_REPO_PATH=/Users/user/Desktop/tari-main +``` +The following changes need to be made to the file +1. ```NDK_PATH``` needs to be changed to the directory of the Android NDK Bundle. +2. ```ANDROID_WALLET``` needs to be changed to the path of the Android-Wallet repository +3. ```IOS_WALLET_PATH``` needs to be changed to the path of the Wallet-iOS repository +4. ```TARI_REPO_PATH``` needs to be changed to the path of the Tari repository +5. ```BUILD_ANDROID``` can be set to ```0``` to disable Android library build +6. ```BUILD_IOS``` can be set to ```0``` to disable iOS library build + +Save the file and rename it to ```build.config``` + +## Building the Libraries + +To build the libraries, ```cd``` to the Tari repository and then +```Shell Script +cd base_layer/wallet_ffi +sh mobile_build.sh +``` + +The relevant libraries will then be built and placed in the appropriate directories of the Wallet-iOS and Wallet-Android repositories. + diff --git a/base_layer/wallet_ffi/build.sample.config b/base_layer/wallet_ffi/build.sample.config new file mode 100644 index 0000000000..255c3c797c --- /dev/null +++ b/base_layer/wallet_ffi/build.sample.config @@ -0,0 +1,8 @@ +BUILD_ANDROID=1 +BUILD_IOS=1 +SQLITE_SOURCE=https://www.sqlite.org/snapshot/sqlite-snapshot-201911192122.tar.gz +NDK_PATH=/Users/user/Desktop/android-ndk-r20 +PKG_PATH=/usr/local/Cellar/zeromq/4.3.2/lib/pkgconfig +ANDROID_WALLET_PATH=/Users/user/Desktop/wallet-android +IOS_WALLET_PATH=/Users/user/Desktop/wallet-ios +TARI_REPO_PATH=/Users/user/Desktop/tari-main \ No newline at end of file diff --git a/base_layer/wallet_ffi/cbindgen.toml b/base_layer/wallet_ffi/cbindgen.toml new file mode 100644 index 0000000000..2179c6895b --- /dev/null +++ b/base_layer/wallet_ffi/cbindgen.toml @@ -0,0 +1,96 @@ +# This is a template cbindgen.toml file with all of the default values. +# Some values are commented out because their absence is the real default. +# +# See https://github.com/eqrion/cbindgen/blob/master/docs.md#cbindgentoml +# for detailed documentation of every option here. + +language = "C++" + +############## Options for Wrapping the Contents of the Header ################# + +# header = "/* Text to put at the beginning of the generated file. Probably a license. */" +# trailer = "/* Text to put at the end of the generated file */" +include_guard = "WALLET_H" +# autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */" +include_version = false +# namespace = "my_namespace" +namespaces = [] +sys_includes = [] +includes = [] +no_includes = false + +############################ Code Style Options ################################ + +braces = "SameLine" +line_length = 100 +tab_width = 2 +documentation_style = "auto" + +############################# Codegen Options ################################## + +style = "both" + +[defines] +# "target_os = freebsd" = "DEFINE_FREEBSD" +# "feature = serde" = "DEFINE_SERDE" + +[export] +include = [] +exclude = [] +# prefix = "CAPI_" +item_types = [] +renaming_overrides_prefixing = false + +[export.rename] + +[export.body] + +[fn] +rename_args = "None" +# must_use = "MUST_USE_FUNC" +# prefix = "START_FUNC" +# postfix = "END_FUNC" +args = "auto" + +[struct] +rename_fields = "None" +# must_use = "MUST_USE_STRUCT" +derive_constructor = false +derive_eq = false +derive_neq = false +derive_lt = false +derive_lte = false +derive_gt = false +derive_gte = false + +[enum] +rename_variants = "None" +# must_use = "MUST_USE_ENUM" +add_sentinel = false +prefix_with_name = false +derive_helper_methods = false +derive_const_casts = false +derive_mut_casts = false +# cast_assert_name = "ASSERT" +derive_tagged_enum_destructor = false +derive_tagged_enum_copy_constructor = false + +[const] +allow_static_const = true + +[macro_expansion] +bitflags = false + +############## Options for How Your Rust library Should Be Parsed ############## + +[parse] +parse_deps = false +# include = [] +exclude = [] +clean = false + +[parse.expand] +crates = [] +all_features = false +default_features = true +features = [] diff --git a/base_layer/wallet_ffi/mobile_build.sh b/base_layer/wallet_ffi/mobile_build.sh new file mode 100644 index 0000000000..c388e73b01 --- /dev/null +++ b/base_layer/wallet_ffi/mobile_build.sh @@ -0,0 +1,300 @@ +#!/bin/bash +# +# Script to build libraries for Tari Wallet +# + +#Terminal colors +RED='\033[0;31m' +GREEN='\033[0;32m' +PURPLE='\033[0;35m' +CYAN='\033[0;36m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +source build.config +CURRENT_DIR=${TARI_REPO_PATH}/base_layer/wallet_ffi +cd ${CURRENT_DIR} || exit +timestamp=$(date +%s) +mkdir -p logs +cd logs || exit +mkdir -p ${timestamp} +cd ${timestamp} || exit +mkdir -p ios +mkdir -p android +cd ../.. +IOS_LOG_PATH=${CURRENT_DIR}/logs/${timestamp}/ios +ANDROID_LOG_PATH=${CURRENT_DIR}/logs/${timestamp}/android +SQLITE_FOLDER=sqlite +cd ../../.. + +unameOut="$(uname -s)" +case "${unameOut}" in + Linux*) MACHINE=Linux;; + Darwin*) MACHINE=Mac;; + CYGWIN*) MACHINE=Cygwin;; + MINGW*) MACHINE=MinGw;; + *) MACHINE="UNKNOWN:${unameOut}" +esac +export PKG_CONFIG_ALLOW_CROSS=1 + +# Fix for macOS Catalina failing to include correct headers for cross compilation +if [ "${MACHINE}" == "Mac" ]; then + MAC_VERSION=$(sw_vers -productVersion) + MAC_SUB_VERSION=$(cut -d '.' -f2 <<<"${MAC_VERSION}") + if [ "${MAC_SUB_VERSION}" -ge 15 ]; then + unset CPATH + echo "${PURPLE}macOS 15+ Detected${NC}" + else + echo "${PURPLE}macOS 14 Detected${NC}" + fi +fi + +DEPENDENCIES=${IOS_WALLET_PATH} +# PKG_PATH, BUILD_IOS is defined in build.config +# shellcheck disable=SC2153 +if [ -n "${DEPENDENCIES}" ] && [ -n "${PKG_PATH}" ] && [ "${BUILD_IOS}" -eq 1 ] && [ "${MACHINE}" == "Mac" ]; then + echo "${GREEN}Commencing iOS build${NC}" + echo "${YELLOW}Build logs can be found at ${IOS_LOG_PATH}${NC}" + echo "\t${CYAN}Configuring Rust${NC}" + rustup target add aarch64-apple-ios x86_64-apple-ios >> ${IOS_LOG_PATH}/rust.txt 2>&1 + cargo install cargo-lipo >> ${IOS_LOG_PATH}/rust.txt 2>&1 + echo "\t${CYAN}Configuring complete${NC}" + #below line is temporary + cd ${DEPENDENCIES} || exit + mkdir -p build + cd build || exit + BUILD_ROOT=$PWD + cd .. + cd ${CURRENT_DIR} || exit + cargo clean + cp wallet.h "${DEPENDENCIES}/MobileWallet/TariLib/" + export PKG_CONFIG_PATH=${PKG_PATH} + echo "\t${CYAN}Building Wallet FFI${NC}" + cargo-lipo lipo --release > ${IOS_LOG_PATH}/cargo.txt 2>&1 + cd ../.. + cd target || exit + cd universal || exit + cd release || exit + cp libtari_wallet_ffi.a "${DEPENDENCIES}/MobileWallet/TariLib/" + cd ../../.. || exit + rm -rf target + cd ${DEPENDENCIES} || exit + echo "${GREEN}iOS build completed${NC}" +elif [ "${BUILD_IOS}" -eq 1 ]; then + echo "${RED}Cannot configure iOS Wallet Library build${NC}" +else + echo "${GREEN}iOS Wallet is configured not to build${NC}" +fi + +DEPENDENCIES=$ANDROID_WALLET_PATH +# PKG_PATH, BUILD_ANDROID, NDK_PATH is defined in build.config +# shellcheck disable=SC2153 +if [ -n "${DEPENDENCIES}" ] && [ -n "${NDK_PATH}" ] && [ -n "${PKG_PATH}" ] && [ "${BUILD_ANDROID}" -eq 1 ]; then + echo "${GREEN}Commencing Android build${NC}" + echo "${YELLOW}Build logs can be found at ${ANDROID_LOG_PATH}${NC}" + echo "\t${CYAN}Configuring Rust${NC}" + rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi i686-linux-android arm-linux-androideabi > ${ANDROID_LOG_PATH}/rust.txt 2>&1 + if [ "${MAC_SUB_VERSION}" -lt 15 ]; then + cargo install cargo-ndk >> ${ANDROID_LOG_PATH}/rust.txt 2>&1 + fi + echo "\t${CYAN}Configuring complete${NC}" + export NDK_HOME=${NDK_PATH} + export PKG_CONFIG_PATH=${PKG_PATH} + export NDK_TOOLCHAIN_VERSION=clang + DEPENDENCIES=${DEPENDENCIES}/jniLibs + + SQLITE_BUILD_FOUND=0 + if [ -f ${DEPENDENCIES}/x86/libsqlite3.a ] && [ -f ${DEPENDENCIES}/x86_64/libsqlite3.a ] && [ -f ${DEPENDENCIES}/armeabi-v7a/libsqlite3.a ] && [ -f ${DEPENDENCIES}/arm64-v8a/libsqlite3.a ]; then + SQLITE_BUILD_FOUND=1 + fi + + cd ${DEPENDENCIES} || exit + mkdir -p build + cd build || exit + BUILD_ROOT=${PWD} + if [ "${MACHINE}" == "Mac" ]; then + if [ "${MAC_SUB_VERSION}" -ge 15 ]; then + cd ${NDK_HOME}/sources/cxx-stl/llvm-libc++/include || exit + mkdir -p sys + #Fix for missing header, c code should reference limits.h instead of syslimits.h, happens with code that has been around for a long time. + cp "${NDK_HOME}/sources/cxx-stl/llvm-libc++/include/limits.h" "${NDK_HOME}/sources/cxx-stl/llvm-libc++/include/sys/syslimits.h" + cd ${BUILD_ROOT} || exit + fi + fi + cd .. + + if [ ${SQLITE_BUILD_FOUND} -eq 0 ]; then + touch ${ANDROID_LOG_PATH}/sqlite.txt + fi + touch ${ANDROID_LOG_PATH}/cargo.txt + + for PLATFORMABI in "x86_64-linux-android" "aarch64-linux-android" "armv7-linux-androideabi" + do + # Lint warning for loop only running once is acceptable here + # shellcheck disable=SC2043 + for LEVEL in 24 + #21 22 23 26 26 27 28 29 not included at present + do + PLATFORM=$(cut -d'-' -f1 <<<"${PLATFORMABI}") + + PLATFORM_OUTDIR="" + if [ "${PLATFORM}" == "i686" ]; then + PLATFORM_OUTDIR="x86" + elif [ "${PLATFORM}" == "x86_64" ]; then + PLATFORM_OUTDIR="x86_64" + elif [ "${PLATFORM}" == "armv7" ]; then + PLATFORM_OUTDIR="armeabi-v7a" + elif [ "${PLATFORM}" == "aarch64" ]; then + PLATFORM_OUTDIR="arm64-v8a" + else + PLATFORM_OUTDIR=${PLATFORM} + fi + cd ${BUILD_ROOT} || exit + mkdir -p ${PLATFORM_OUTDIR} + OUTPUT_DIR=${BUILD_ROOT}/${PLATFORM_OUTDIR} + + if [ ${SQLITE_BUILD_FOUND} -eq 1 ]; then + mkdir -p ${BUILD_ROOT}/${PLATFORM_OUTDIR}/lib + fi + + cd ${DEPENDENCIES} || exit + + PLATFORMABI_TOOLCHAIN=${PLATFORMABI} + PLATFORMABI_COMPILER=${PLATFORMABI} + if [ "${PLATFORMABI}" == "armv7-linux-androideabi" ]; then + PLATFORMABI_TOOLCHAIN="arm-linux-androideabi" + PLATFORMABI_COMPILER="armv7a-linux-androideabi" + fi + # set toolchain path + export TOOLCHAIN=${NDK_HOME}/toolchains/llvm/prebuilt/darwin-x86_64/${PLATFORMABI_TOOLCHAIN} + + # set the archiver + export AR=${NDK_HOME}/toolchains/llvm/prebuilt/darwin-x86_64/bin/${PLATFORMABI_TOOLCHAIN}$'-'ar + + # set the assembler + export AS=${NDK_HOME}/toolchains/llvm/prebuilt/darwin-x86_64/bin/${PLATFORMABI_TOOLCHAIN}$'-'as + + # set the c and c++ compiler + CC=${NDK_HOME}/toolchains/llvm/prebuilt/darwin-x86_64/bin/${PLATFORMABI_COMPILER} + export CC=${CC}${LEVEL}$'-'clang + export CXX=${CC}++ + + export CXXFLAGS="-stdlib=libstdc++ -isystem ${NDK_HOME}/sources/cxx-stl/llvm-libc++/include" + # set the linker + export LD=${NDK_HOME}/toolchains/llvm/prebuilt/darwin-x86_64/bin/${PLATFORMABI_TOOLCHAIN}$'-'ld + + # set linker flags + export LDFLAGS="-L${NDK_HOME}/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/lib/${PLATFORMABI_TOOLCHAIN}/${LEVEL} -L${OUTPUT_DIR}/lib -lc++" + + # set the archive index generator tool + export RANLIB=${NDK_HOME}/toolchains/llvm/prebuilt/darwin-x86_64/bin/${PLATFORMABI_TOOLCHAIN}$'-'ranlib + + # set the symbol stripping tool + export STRIP=${NDK_HOME}/toolchains/llvm/prebuilt/darwin-x86_64/bin/${PLATFORMABI_TOOLCHAIN}$'-'strip + + # set c flags + #note: Add -v to below to see compiler output, include paths, etc + export CFLAGS="-DMDB_USE_ROBUST=0" + + # set cpp flags + export CPPFLAGS="-fPIC -I${OUTPUT_DIR}/include" + + mkdir -p ${SQLITE_FOLDER} + cd ${SQLITE_FOLDER} || exit + if [ ${SQLITE_BUILD_FOUND} -eq 0 ]; then + echo "\t${CYAN}Fetching Sqlite3 source${NC}" + curl -s ${SQLITE_SOURCE} | tar -xvf - -C ${PWD} >> ${ANDROID_LOG_PATH}/sqlite.txt 2>&1 + echo "\t${CYAN}Source fetched${NC}" + cd * || exit + echo "\t${CYAN}Building Sqlite3${NC}" + make clean >> ${ANDROID_LOG_PATH}/sqlite.txt 2>&1 + ./configure --host=${PLATFORMABI} --prefix=${OUTPUT_DIR} >> ${ANDROID_LOG_PATH}/sqlite.txt 2>&1 + make install >> ${ANDROID_LOG_PATH}/sqlite.txt 2>&1 + echo "\t${CYAN}Sqlite3 built${NC}" + else + echo "\t${CYAN}Sqlite3 located${NC}" + fi + cd ../.. + + if [ "${MACHINE}" == "Mac" ]; then + if [ "${MAC_SUB_VERSION}" -ge 15 ]; then + # Not ideal, however necesary for cargo to pass additional flags + export CFLAGS="${CFLAGS} -I${NDK_HOME}/sources/cxx-stl/llvm-libc++/include -I${NDK_HOME}/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/include -I${NDK_HOME}/sysroot/usr/include/${PLATFORMABI}" + fi + fi + export LDFLAGS="-L${NDK_HOME}/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/lib/${PLATFORMABI_TOOLCHAIN}/${LEVEL} -L${OUTPUT_DIR}/lib -lc++ -lsqilte3" + cd ${OUTPUT_DIR}/lib || exit + + if [ ${SQLITE_BUILD_FOUND} -eq 1 ]; then + cp ${DEPENDENCIES}/${PLATFORM_OUTDIR}/libsqlite3.a ${OUTPUT_DIR}/lib/libsqlite3.a + fi + + echo "\t${CYAN}Configuring Cargo${NC}" + cd ${CURRENT_DIR} || exit + cargo clean >> ${ANDROID_LOG_PATH}/cargo.txt 2>&1 + mkdir -p .cargo + cd .cargo || exit + if [ "${MACHINE}" == "Mac" ]; then + if [ "${MAC_SUB_VERSION}" -ge 15 ]; then + cat > config < config <> ${ANDROID_LOG_PATH}/cargo.txt 2>&1 + else + cargo ndk --target ${PLATFORMABI} --android-platform ${LEVEL} -- build --release >> ${ANDROID_LOG_PATH}/cargo.txt 2>&1 + fi + else + cargo ndk --target ${PLATFORMABI} --android-platform ${LEVEL} -- build --release >> ${ANDROID_LOG_PATH}/cargo.txt 2>&1 + fi + cp wallet.h ${DEPENDENCIES}/ + rm -rf .cargo + cd ../.. + cd target || exit + cd ${PLATFORMABI} || exit + cd release || exit + cp libtari_wallet_ffi.a ${OUTPUT_DIR} + cd ../.. + rm -rf target + cd ${DEPENDENCIES} || exit + mkdir -p ${PLATFORM_OUTDIR} + cd ${PLATFORM_OUTDIR} || exit + if [ ${SQLITE_BUILD_FOUND} -eq 0 ]; then + cp ${OUTPUT_DIR}/lib/libsqlite3.a ${PWD} + fi + cp ${OUTPUT_DIR}/libtari_wallet_ffi.a ${PWD} + echo "\t${GREEN}Wallet library built for android architecture ${PLATFORM_OUTDIR} with minimum platform level support of ${LEVEL}${NC}" + done + done + cd ${DEPENDENCIES} || exit + rm -rf build + rm -rf ${SQLITE_FOLDER} + echo "${GREEN}Android build completed${NC}" +elif [ "${BUILD_ANDROID}" -eq 1 ]; then + echo "${RED}Cannot configure Android Wallet Library build${NC}" +else + echo "${GREEN}Android Wallet is configured not to build${NC}" +fi diff --git a/base_layer/wallet_ffi/src/callback_handler.rs b/base_layer/wallet_ffi/src/callback_handler.rs new file mode 100644 index 0000000000..6cb20e88bc --- /dev/null +++ b/base_layer/wallet_ffi/src/callback_handler.rs @@ -0,0 +1,259 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use futures::{stream::Fuse, StreamExt}; +use log::*; +use tari_broadcast_channel::Subscriber; +use tari_shutdown::ShutdownSignal; +use tari_wallet::{ + output_manager_service::TxId, + transaction_service::{ + handle::TransactionEvent, + storage::database::{CompletedTransaction, InboundTransaction, TransactionBackend, TransactionDatabase}, + }, +}; + +const LOG_TARGET: &str = "base_layer::wallet::transaction_service::callback_handler"; + +pub struct CallbackHandler +where TBackend: TransactionBackend + 'static +{ + callback_received_transaction: unsafe extern "C" fn(*mut InboundTransaction), + callback_received_transaction_reply: unsafe extern "C" fn(*mut CompletedTransaction), + callback_received_finalized_transaction: unsafe extern "C" fn(*mut CompletedTransaction), + callback_transaction_broadcast: unsafe extern "C" fn(*mut CompletedTransaction), + callback_transaction_mined: unsafe extern "C" fn(*mut CompletedTransaction), + callback_discovery_process_complete: unsafe extern "C" fn(TxId, bool), + db: TransactionDatabase, + event_stream: Fuse>, + shutdown_signal: Option, +} + +impl CallbackHandler +where TBackend: TransactionBackend + 'static +{ + pub fn new( + db: TransactionDatabase, + event_stream: Fuse>, + shutdown_signal: ShutdownSignal, + callback_received_transaction: unsafe extern "C" fn(*mut InboundTransaction), + callback_received_transaction_reply: unsafe extern "C" fn(*mut CompletedTransaction), + callback_received_finalized_transaction: unsafe extern "C" fn(*mut CompletedTransaction), + callback_transaction_broadcast: unsafe extern "C" fn(*mut CompletedTransaction), + callback_transaction_mined: unsafe extern "C" fn(*mut CompletedTransaction), + callback_discovery_process_complete: unsafe extern "C" fn(TxId, bool), + ) -> Self + { + info!( + target: LOG_TARGET, + "ReceivedTransactionCallback -> Assigning Fn: {:?}", callback_received_transaction + ); + info!( + target: LOG_TARGET, + "ReceivedTransactionReplyCallback -> Assigning Fn: {:?}", callback_received_transaction_reply + ); + info!( + target: LOG_TARGET, + "ReceivedFinalizedTransactionCallback -> Assigning Fn: {:?}", callback_received_finalized_transaction + ); + info!( + target: LOG_TARGET, + "TransactionBroadcastCallback -> Assigning Fn: {:?}", callback_transaction_broadcast + ); + info!( + target: LOG_TARGET, + "TransactionMinedCallback -> Assigning Fn: {:?}", callback_transaction_mined + ); + info!( + target: LOG_TARGET, + "DiscoveryProcessCompleteCallback -> Assigning Fn: {:?}", callback_discovery_process_complete + ); + + Self { + callback_received_transaction, + callback_received_transaction_reply, + callback_received_finalized_transaction, + callback_transaction_broadcast, + callback_transaction_mined, + callback_discovery_process_complete, + db, + event_stream, + shutdown_signal: Some(shutdown_signal), + } + } + + pub async fn start(mut self) { + let mut shutdown_signal = self + .shutdown_signal + .take() + .expect("Callback Handler started without shutdown signal"); + + info!(target: LOG_TARGET, "Transaction Service Callback Handler starting"); + + loop { + futures::select! { + msg = self.event_stream.select_next_some() => { + trace!(target: LOG_TARGET, "Callback Handler {:?}", msg); + match (*msg).clone() { + TransactionEvent::ReceivedTransaction(tx_id) => { + self.receive_transaction_event(tx_id).await; + }, + TransactionEvent::ReceivedTransactionReply(tx_id) => { + self.receive_transaction_reply_event(tx_id).await; + }, + TransactionEvent::ReceivedFinalizedTransaction(tx_id) => { + self.receive_finalized_transaction_event(tx_id).await; + }, + TransactionEvent::TransactionSendDiscoveryComplete(tx_id, result) => { + // If this event result is false we will return that result via the callback as + // no further action will be taken on this send attempt. However if it is true + // then we must wait for a `TransactionSendResult` which + // will tell us the final result of the send + if !result { + self.receive_discovery_process_result(tx_id, result); + } + }, + TransactionEvent::TransactionBroadcast(tx_id) => { + self.receive_transaction_broadcast_event(tx_id).await; + }, + TransactionEvent::TransactionMined(tx_id) => { + self.receive_transaction_mined_event(tx_id).await; + }, + TransactionEvent::TransactionSendResult(tx_id, result) => { + self.receive_discovery_process_result(tx_id, result); + }, + /// Only the above variants are mapped to callbacks + _ => (), + } + }, + complete => { + info!(target: LOG_TARGET, "Callback Handler is exiting because all tasks have completed"); + break; + } + _ = shutdown_signal => { + info!(target: LOG_TARGET, "Transaction Callback Handler shutting down because the shutdown signal was received"); + break; + }, + } + } + } + + async fn receive_transaction_event(&mut self, tx_id: TxId) { + match self.db.get_pending_inbound_transaction(tx_id).await { + Ok(tx) => { + trace!( + target: LOG_TARGET, + "Calling Received Transaction callback function for TxId: {}", + tx_id + ); + let boxing = Box::into_raw(Box::new(tx)); + unsafe { + (self.callback_received_transaction)(boxing); + } + }, + Err(e) => error!( + target: LOG_TARGET, + "Error retrieving Pending Inbound Transaction: {:?}", e + ), + } + } + + async fn receive_transaction_reply_event(&mut self, tx_id: TxId) { + match self.db.get_completed_transaction(tx_id).await { + Ok(tx) => { + trace!( + target: LOG_TARGET, + "Calling Received Transaction Reply callback function for TxId: {}", + tx_id + ); + let boxing = Box::into_raw(Box::new(tx)); + unsafe { + (self.callback_received_transaction_reply)(boxing); + } + }, + Err(e) => error!(target: LOG_TARGET, "Error retrieving Completed Transaction: {:?}", e), + } + } + + async fn receive_finalized_transaction_event(&mut self, tx_id: TxId) { + match self.db.get_completed_transaction(tx_id).await { + Ok(tx) => { + trace!( + target: LOG_TARGET, + "Calling Received Finalized Transaction callback function for TxId: {}", + tx_id + ); + let boxing = Box::into_raw(Box::new(tx)); + unsafe { + (self.callback_received_finalized_transaction)(boxing); + } + }, + Err(e) => error!(target: LOG_TARGET, "Error retrieving Completed Transaction: {:?}", e), + } + } + + fn receive_discovery_process_result(&mut self, tx_id: TxId, result: bool) { + trace!( + target: LOG_TARGET, + "Calling Discovery Process Completed callback function for TxId: {} with result {}", + tx_id, + result + ); + unsafe { + (self.callback_discovery_process_complete)(tx_id, result); + } + } + + async fn receive_transaction_broadcast_event(&mut self, tx_id: TxId) { + match self.db.get_completed_transaction(tx_id).await { + Ok(tx) => { + trace!( + target: LOG_TARGET, + "Calling Received Transaction Broadcast callback function for TxId: {}", + tx_id + ); + let boxing = Box::into_raw(Box::new(tx)); + unsafe { + (self.callback_transaction_broadcast)(boxing); + } + }, + Err(e) => error!(target: LOG_TARGET, "Error retrieving Completed Transaction: {:?}", e), + } + } + + async fn receive_transaction_mined_event(&mut self, tx_id: TxId) { + match self.db.get_completed_transaction(tx_id).await { + Ok(tx) => { + trace!( + target: LOG_TARGET, + "Calling Received Transaction Mined callback function for TxId: {}", + tx_id + ); + let boxing = Box::into_raw(Box::new(tx)); + unsafe { + (self.callback_transaction_mined)(boxing); + } + }, + Err(e) => error!(target: LOG_TARGET, "Error retrieving Completed Transaction: {:?}", e), + } + } +} diff --git a/base_layer/wallet_ffi/src/error.rs b/base_layer/wallet_ffi/src/error.rs new file mode 100644 index 0000000000..5ff4e5c101 --- /dev/null +++ b/base_layer/wallet_ffi/src/error.rs @@ -0,0 +1,333 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use derive_error::Error; +use log::*; +use tari_comms::{ + multiaddr, + peer_manager::{node_id::NodeIdError, NodeIdentityError}, +}; +use tari_crypto::{ + signatures::SchnorrSignatureError, + tari_utilities::{hex::HexError, ByteArrayError}, +}; +use tari_wallet::{ + contacts_service::error::{ContactsServiceError, ContactsServiceStorageError}, + error::WalletError, + output_manager_service::error::{OutputManagerError, OutputManagerStorageError}, + transaction_service::error::{TransactionServiceError, TransactionStorageError}, +}; + +const LOG_TARGET: &str = "wallet_ffi::error"; + +#[derive(Debug, Error, PartialEq)] +pub enum InterfaceError { + /// An error has occurred due to one of the parameters being null + #[error(msg_embedded, non_std, no_from)] + NullError(String), + /// An error has occurred when checking the length of the allocated object + AllocationError, + /// An error because the supplied position was out of range + PositionInvalidError, + /// An error has occured when trying to create the tokio runtime + #[error(non_std, no_from)] + TokioError(String), +} + +/// This struct is meant to hold an error for use by FFI client applications. The error has an integer code and string +/// message +#[derive(Debug, Clone)] +pub struct LibWalletError { + pub code: i32, + pub message: String, +} + +impl From for LibWalletError { + fn from(v: InterfaceError) -> Self { + error!(target: LOG_TARGET, "{}", format!("{:?}", v)); + match v { + InterfaceError::NullError(_) => Self { + code: 1, + message: format!("{:?}", v), + }, + InterfaceError::AllocationError => Self { + code: 2, + message: format!("{:?}", v), + }, + InterfaceError::PositionInvalidError => Self { + code: 3, + message: format!("{:?}", v), + }, + InterfaceError::TokioError(_) => Self { + code: 4, + message: format!("{:?}", v), + }, + } + } +} + +/// This implementation maps the internal WalletError to a set of LibWalletErrors. The mapping is explicitly manager +/// here and error code 999 is a catch-all code for any errors that are not explicitly mapped +impl From for LibWalletError { + fn from(w: WalletError) -> Self { + error!(target: LOG_TARGET, "{}", format!("{:?}", w)); + match w { + // Output Manager Service Errors + WalletError::OutputManagerError(OutputManagerError::NotEnoughFunds) => Self { + code: 101, + message: format!("{:?}", w), + }, + WalletError::TransactionServiceError(TransactionServiceError::OutputManagerError( + OutputManagerError::NotEnoughFunds, + )) => Self { + code: 101, + message: format!("{:?}", w), + }, + WalletError::OutputManagerError(OutputManagerError::IncompleteTransaction) => Self { + code: 102, + message: format!("{:?}", w), + }, + WalletError::OutputManagerError(OutputManagerError::DuplicateOutput) => Self { + code: 103, + message: format!("{:?}", w), + }, + WalletError::TransactionServiceError(TransactionServiceError::TransactionStorageError( + TransactionStorageError::DuplicateOutput, + )) => Self { + code: 103, + message: format!("{:?}", w), + }, + WalletError::OutputManagerError(OutputManagerError::OutputManagerStorageError( + OutputManagerStorageError::DuplicateOutput, + )) => Self { + code: 103, + message: format!("{:?}", w), + }, + WalletError::OutputManagerError(OutputManagerError::OutputManagerStorageError( + OutputManagerStorageError::ValuesNotFound, + )) => Self { + code: 104, + message: format!("{:?}", w), + }, + WalletError::ContactsServiceError(ContactsServiceError::ContactsServiceStorageError( + ContactsServiceStorageError::ValuesNotFound, + )) => Self { + code: 104, + message: format!("{:?}", w), + }, + WalletError::OutputManagerError(OutputManagerError::OutputManagerStorageError( + OutputManagerStorageError::OutputAlreadySpent, + )) => Self { + code: 105, + message: format!("{:?}", w), + }, + WalletError::OutputManagerError(OutputManagerError::OutputManagerStorageError( + OutputManagerStorageError::PendingTransactionNotFound, + )) => Self { + code: 106, + message: format!("{:?}", w), + }, + WalletError::OutputManagerError(OutputManagerError::OutputManagerStorageError( + OutputManagerStorageError::ValueNotFound(_), + )) => Self { + code: 108, + message: format!("{:?}", w), + }, + WalletError::TransactionServiceError(TransactionServiceError::TransactionStorageError( + TransactionStorageError::ValueNotFound(_), + )) => Self { + code: 108, + message: format!("{:?}", w), + }, + WalletError::OutputManagerError(OutputManagerError::NoBaseNodeKeysProvided) => Self { + code: 109, + message: format!("{:?}", w), + }, + // Transaction Service Errors + WalletError::TransactionServiceError(TransactionServiceError::InvalidStateError) => Self { + code: 201, + message: format!("{:?}", w), + }, + WalletError::TransactionServiceError(TransactionServiceError::TransactionProtocolError(_)) => Self { + code: 202, + message: format!("{:?}", w), + }, + WalletError::TransactionServiceError(TransactionServiceError::RepeatedMessageError) => Self { + code: 203, + message: format!("{:?}", w), + }, + WalletError::TransactionServiceError(TransactionServiceError::TransactionDoesNotExistError) => Self { + code: 204, + message: format!("{:?}", w), + }, + WalletError::TransactionServiceError(TransactionServiceError::OutputManagerError(_)) => Self { + code: 206, + message: format!("{:?}", w), + }, + WalletError::TransactionServiceError(TransactionServiceError::TransactionError(_)) => Self { + code: 207, + message: format!("{:?}", w), + }, + WalletError::TransactionServiceError(TransactionServiceError::OutboundSendDiscoveryInProgress(_)) => Self { + code: 210, + message: format!("{:?}", w), + }, + // Comms Stack errors + WalletError::MultiaddrError(_) => Self { + code: 301, + message: format!("{:?}", w), + }, + WalletError::ContactsServiceError(ContactsServiceError::ContactNotFound) => Self { + code: 401, + message: format!("{:?}", w), + }, + WalletError::ContactsServiceError(ContactsServiceError::ContactsServiceStorageError( + ContactsServiceStorageError::OperationNotSupported, + )) => Self { + code: 403, + message: format!("{:?}", w), + }, + WalletError::ContactsServiceError(ContactsServiceError::ContactsServiceStorageError( + ContactsServiceStorageError::ConversionError, + )) => Self { + code: 404, + message: format!("{:?}", w), + }, + // This is the catch all error code. Any error that is not explicitly mapped above will be given this code + _ => Self { + code: 999, + message: format!("{:?}", w), + }, + } + } +} + +/// This implementation maps the internal HexError to a set of LibWalletErrors. The mapping is explicitly manager +/// here and error code 999 is a catch-all code for any errors that are not explicitly mapped +impl From for LibWalletError { + fn from(h: HexError) -> Self { + error!(target: LOG_TARGET, "{}", format!("{:?}", h)); + match h { + HexError::HexConversionError => Self { + code: 404, + message: format!("{:?}", h), + }, + HexError::LengthError => Self { + code: 501, + message: format!("{:?}", h), + }, + HexError::InvalidCharacter(_) => Self { + code: 503, + message: format!("{:?}", h), + }, + } + } +} + +/// This implementation maps the internal ByteArrayError to a set of LibWalletErrors. The mapping is explicitly manager +/// here and error code 999 is a catch-all code for any errors that are not explicitly mapped +impl From for LibWalletError { + fn from(b: ByteArrayError) -> Self { + error!(target: LOG_TARGET, "{}", format!("{:?}", b)); + match b { + ByteArrayError::ConversionError(_) => Self { + code: 404, + message: format!("{:?}", b), + }, + ByteArrayError::IncorrectLength => Self { + code: 601, + message: format!("{:?}", b), + }, + } + } +} + +impl From for LibWalletError { + fn from(n: NodeIdentityError) -> Self { + error!(target: LOG_TARGET, "{}", format!("{:?}", n)); + match n { + NodeIdentityError::NodeIdError(NodeIdError::IncorrectByteCount) => Self { + code: 701, + message: format!("{:?}", n), + }, + NodeIdentityError::NodeIdError(NodeIdError::OutOfBounds) => Self { + code: 702, + message: format!("{:?}", n), + }, + NodeIdentityError::PoisonedAccess => Self { + code: 703, + message: format!("{:?}", n), + }, + NodeIdentityError::NodeIdError(NodeIdError::DigestError) => Self { + code: 704, + message: format!("{:?}", n).to_string(), + }, + } + } +} + +impl From for LibWalletError { + fn from(err: multiaddr::Error) -> Self { + error!(target: LOG_TARGET, "{}", format!("{:?}", err)); + match err { + multiaddr::Error::ParsingError(_) => Self { + code: 801, + message: format!("{:?}", err), + }, + multiaddr::Error::InvalidMultiaddr => Self { + code: 802, + message: format!("{:?}", err), + }, + multiaddr::Error::DataLessThanLen => Self { + code: 803, + message: format!("{:?}", err), + }, + multiaddr::Error::InvalidProtocolString => Self { + code: 804, + message: format!("{:?}", err), + }, + multiaddr::Error::UnknownProtocolString(_) => Self { + code: 805, + message: format!("{:?}", err), + }, + multiaddr::Error::InvalidUvar(_) => Self { + code: 806, + message: format!("{:?}", err), + }, + err => Self { + code: 810, + message: format!("Multiaddr error: {:?}", err), + }, + } + } +} + +impl From for LibWalletError { + fn from(err: SchnorrSignatureError) -> Self { + error!(target: LOG_TARGET, "{}", format!("{:?}", err)); + match err { + SchnorrSignatureError::InvalidChallenge => Self { + code: 901, + message: format!("{:?}", err), + }, + } + } +} diff --git a/base_layer/wallet_ffi/src/lib.rs b/base_layer/wallet_ffi/src/lib.rs new file mode 100644 index 0000000000..60c5fab77f --- /dev/null +++ b/base_layer/wallet_ffi/src/lib.rs @@ -0,0 +1,3842 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +//! # LibWallet API Definition +//! This module contains the Rust backend implementations of the functionality that a wallet for the Tari Base Layer +//! will require. The module contains a number of sub-modules that are implemented as async services. These services are +//! collected into the main Wallet container struct which manages spinning up all the component services and maintains a +//! collection of the handles required to interact with those services. +//! This files contains the API calls that will be exposed to external systems that make use of this module. The API +//! will be exposed via FFI and will consist of API calls that the FFI client can make into the Wallet module and a set +//! of Callbacks that the client must implement and provide to the Wallet module to receive asynchronous replies and +//! updates. +//! +//! # Wallet Flow Documentation +//! This documentation will described the flows of the core tasks that the Wallet library supports and will then +//! describe how to use the test functions to produce the behaviour of a second wallet without needing to set one up. +//! +//! ## Generate Test Data +//! The `generate_wallet_test_data(...)` function will generate some test data in the wallet. The data generated will be +//! as follows: +//! +//! - Some Contacts +//! - Add outputs to the wallet that make up its Available Balance that can be spent +//! - Create transaction history +//! - Pending Inbound Transactions +//! - Pending Outbound Transactions +//! - Completed Transactions +//! +//! ## Send Transaction +//! To send a transaction your wallet must have available funds and you must had added the recipient's Public Key as a +//! `Contact`. +//! +//! To send a transaction: +//! 1. Call the `send_transaction(dest_public_key, amount, fee_per_gram, message)` function which will result in a +//! `PendingOutboundTransaction` being produced and transmitted to the recipient and the funds becoming +//! encumbered and appearing in the `PendingOutgoingBalance` and any change will appear in the +//! `PendingIncomingBalance`. +//! 2. Wait until the recipient replies to the sent transaction which will result in the `PendingOutboundTransaction` +//! becoming a `CompletedTransaction` with the `Completed` status. This means that the transaction has been +//! negotiated between the parties and is now ready to be broadcast to the Base Layer. The funds are still +//! encumbered as pending because the transaction has not been mined yet. +//! 3. The finalized `CompletedTransaction' will be sent back to the the receiver so that they have a copy. +//! 4. The wallet will broadcast the `CompletedTransaction` to a Base Node to be added to the mempool. its status will +//! from `Completed` to `Broadcast. +//! 5. Wait until the transaction is mined. The `CompleteTransaction` status will then move from `Broadcast` to `Mined` +//! and the pending funds will be spent and received. +//! +//! ## Receive a Transaction +//! 1. When a transaction is received it will appear as an `InboundTransaction` and the amount to be received will +//! appear as a `PendingIncomingBalance`. The wallet backend will be listening for these transactions and will +//! immediately reply to the sending wallet. +//! 2. The sender will send back the finalized `CompletedTransaction` +//! 3. This wallet will also broadcast the `CompletedTransaction` to a Base Node to be added to the mempool, its status +//! will move from `Completed` to `Broadcast`. This is done so that the Receiver can be sure the finalized +//! transaciton is broadcast. +//! 6. This wallet will then monitor the Base Layer to see when the transaction is mined which means the +//! `CompletedTransaction` status will become `Mined` and the funds will then move from the `PendingIncomingBalance` +//! to the `AvailableBalance`. +//! +//! ## Using the test functions +//! The above two flows both require a second wallet for this wallet to interact with. Because we do not yet have a live +//! Test Net and the communications layer is not quite ready the library supplies four functions to help simulate the +//! second wallets role in these flows. The following will describe how to use these functions to produce the flows. +//! +//! ### Send Transaction with test functions +//! 1. Send Transaction as above to produce a `PendingOutboundTransaction`. +//! 2. Call the `complete_sent_transaction(...)` function with the tx_id of the sent transaction to simulate a reply. +//! This will move the `PendingOutboundTransaction` to become a `CompletedTransaction` with the `Completed` status. +//! 3. Call the 'broadcast_transaction(...)` function with the tx_id of the sent transaction and its status will move +//! from 'Completed' to 'Broadcast' which means it has been broadcast to the Base Layer Mempool but not mined yet. +//! from 'Completed' to 'Broadcast' which means it has been broadcast to the Base Layer Mempool but not mined yet. +//! 4. Call the `mined_transaction(...)` function with the tx_id of the sent transaction which will change +//! the status of the `CompletedTransaction` from `Broadcast` to `Mined`. The pending funds will also become +//! finalized as spent and available funds respectively. +//! +//! ### Receive Transaction with test functions +//! Under normal operation another wallet would initiate a Receive Transaction flow by sending you a transaction. We +//! will use the `receive_test_transaction(...)` function to initiate the flow: +//! +//! 1. Calling `receive_test_transaction(...)` will produce an `InboundTransaction`, the amount of the transaction will +//! appear under the `PendingIncomingBalance`. +//! 2. To simulate detecting the `InboundTransaction` being broadcast to the Base Layer Mempool call +//! `broadcast_transaction(...)` function. This will change the `InboundTransaction` to a +//! `CompletedTransaction` with the `Broadcast` status. The funds will still reflect in the pending balance. +//! 3. Call the `mined_transaction(...)` function with the tx_id of the received transaction which will +//! change the status of the `CompletedTransaction` from `Broadcast` to `Mined`. The pending funds will also +//! become finalized as spent and available funds respectively + +#![recursion_limit = "256"] + +#[cfg(test)] +#[macro_use] +extern crate lazy_static; + +extern crate libc; +extern crate tari_wallet; +mod callback_handler; +mod error; + +use crate::{callback_handler::CallbackHandler, error::InterfaceError}; +use core::ptr; +use error::LibWalletError; +use libc::{c_char, c_int, c_longlong, c_uchar, c_uint, c_ulonglong, c_ushort}; +use rand::rngs::OsRng; +use std::{ + boxed::Box, + ffi::{CStr, CString}, + slice, + sync::Arc, +}; +use tari_comms::{ + multiaddr::Multiaddr, + peer_manager::{NodeIdentity, PeerFeatures}, + socks, + tor, +}; +use tari_comms_dht::DhtConfig; +use tari_core::transactions::{tari_amount::MicroTari, types::CryptoFactories}; +use tari_crypto::{ + keys::{PublicKey, SecretKey}, + tari_utilities::{hex::Hex, ByteArray}, +}; + +use tari_p2p::transport::{TorConfig, TransportType}; +use tari_utilities::message_format::MessageFormat; +use tari_wallet::{ + contacts_service::storage::{database::Contact, sqlite_db::ContactsServiceSqliteDatabase}, + error::WalletError, + output_manager_service::storage::sqlite_db::OutputManagerSqliteDatabase, + storage::{connection_manager::run_migration_and_create_connection_pool, sqlite_db::WalletSqliteDatabase}, + testnet_utils::{ + broadcast_transaction, + complete_sent_transaction, + finalize_received_transaction, + generate_wallet_test_data, + get_next_memory_address, + mine_transaction, + receive_test_transaction, + }, + transaction_service::storage::{database::TransactionDatabase, sqlite_db::TransactionServiceSqliteDatabase}, + util::emoji::EmojiId, + wallet::WalletConfig, +}; +use tokio::runtime::Runtime; + +pub type TariWallet = tari_wallet::wallet::Wallet< + WalletSqliteDatabase, + TransactionServiceSqliteDatabase, + OutputManagerSqliteDatabase, + ContactsServiceSqliteDatabase, +>; + +pub type TariTransportType = tari_p2p::transport::TransportType; +pub type TariPublicKey = tari_comms::types::CommsPublicKey; +pub type TariPrivateKey = tari_comms::types::CommsSecretKey; +pub type TariCommsConfig = tari_p2p::initialization::CommsConfig; +pub struct TariContacts(Vec); +pub type TariContact = tari_wallet::contacts_service::storage::database::Contact; +pub type TariCompletedTransaction = tari_wallet::transaction_service::storage::database::CompletedTransaction; +pub struct TariCompletedTransactions(Vec); +pub type TariPendingInboundTransaction = tari_wallet::transaction_service::storage::database::InboundTransaction; +pub struct TariPendingInboundTransactions(Vec); +pub type TariPendingOutboundTransaction = tari_wallet::transaction_service::storage::database::OutboundTransaction; +pub struct TariPendingOutboundTransactions(Vec); +#[derive(Debug, PartialEq)] +pub struct ByteVector(Vec); // declared like this so that it can be exposed to external header + +/// -------------------------------- Strings ------------------------------------------------ /// + +/// Frees memory for a char array +/// +/// ## Arguments +/// `ptr` - The pointer to be freed +/// +/// ## Returns +/// `()` - Does not return a value, equivalent to void in C. + +#[no_mangle] +pub unsafe extern "C" fn string_destroy(ptr: *mut c_char) { + if !ptr.is_null() { + let _ = CString::from_raw(ptr); + } +} +/// -------------------------------------------------------------------------------------------- /// + +/// -------------------------------- ByteVector ------------------------------------------------ /// + +/// Creates a ByteVector +/// +/// ## Arguments +/// `byte_array` - The pointer to the byte array +/// `element_count` - The number of elements in byte_array +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut ByteVector` - Pointer to the created ByteVector. Note that it will be ptr::null_mut() +/// if the byte_array pointer was null or if the elements in the byte_vector don't match +/// element_count when it is created +#[no_mangle] +pub unsafe extern "C" fn byte_vector_create( + byte_array: *const c_uchar, + element_count: c_uint, + error_out: *mut c_int, +) -> *mut ByteVector +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + let mut bytes = ByteVector(Vec::new()); + if byte_array.is_null() { + error = LibWalletError::from(InterfaceError::NullError("byte_array".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } else { + let array: &[c_uchar] = slice::from_raw_parts(byte_array, element_count as usize); + bytes.0 = array.to_vec(); + if bytes.0.len() != element_count as usize { + error = LibWalletError::from(InterfaceError::AllocationError).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + } + Box::into_raw(Box::new(bytes)) +} + +/// Frees memory for a ByteVector +/// +/// ## Arguments +/// `bytes` - The pointer to a ByteVector +/// +/// ## Returns +/// `()` - Does not return a value, equivalent to void in C +#[no_mangle] +pub unsafe extern "C" fn byte_vector_destroy(bytes: *mut ByteVector) { + if !bytes.is_null() { + Box::from_raw(bytes); + } +} + +/// Gets a c_uchar at position in a ByteVector +/// +/// ## Arguments +/// `ptr` - The pointer to a ByteVector +/// `position` - The integer position +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `c_uchar` - Returns a character. Note that the character will be a null terminator (0) if ptr +/// is null or if the position is invalid +#[no_mangle] +pub unsafe extern "C" fn byte_vector_get_at(ptr: *mut ByteVector, position: c_uint, error_out: *mut c_int) -> c_uchar { + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if ptr.is_null() { + error = LibWalletError::from(InterfaceError::NullError("ptr".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return 0 as c_uchar; + } + let len = byte_vector_get_length(ptr, error_out) as c_int - 1; // clamp to length + if len < 0 || position > len as c_uint { + error = LibWalletError::from(InterfaceError::PositionInvalidError).code; + ptr::swap(error_out, &mut error as *mut c_int); + return 0 as c_uchar; + } + (*ptr).0[position as usize] +} + +/// Gets the number of elements in a ByteVector +/// +/// ## Arguments +/// `ptr` - The pointer to a ByteVector +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `c_uint` - Returns the integer number of elements in the ByteVector. Note that it will be zero +/// if ptr is null +#[no_mangle] +pub unsafe extern "C" fn byte_vector_get_length(vec: *const ByteVector, error_out: *mut c_int) -> c_uint { + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if vec.is_null() { + error = LibWalletError::from(InterfaceError::NullError("vec".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return 0; + } + (*vec).0.len() as c_uint +} + +/// -------------------------------------------------------------------------------------------- /// + +/// -------------------------------- Public Key ------------------------------------------------ /// + +/// Creates a TariPublicKey from a ByteVector +/// +/// ## Arguments +/// `bytes` - The pointer to a ByteVector +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `TariPublicKey` - Returns a public key. Note that it will be ptr::null_mut() if bytes is null or +/// if there was an error with the contents of bytes +#[no_mangle] +pub unsafe extern "C" fn public_key_create(bytes: *mut ByteVector, error_out: *mut c_int) -> *mut TariPublicKey { + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + let v; + if bytes.is_null() { + error = LibWalletError::from(InterfaceError::NullError("bytes".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } else { + v = (*bytes).0.clone(); + } + let pk = TariPublicKey::from_bytes(&v); + match pk { + Ok(pk) => Box::into_raw(Box::new(pk)), + Err(e) => { + error = LibWalletError::from(e).code; + ptr::swap(error_out, &mut error as *mut c_int); + ptr::null_mut() + }, + } +} + +/// Frees memory for a TariPublicKey +/// +/// ## Arguments +/// `pk` - The pointer to a TariPublicKey +/// +/// ## Returns +/// `()` - Does not return a value, equivalent to void in C +#[no_mangle] +pub unsafe extern "C" fn public_key_destroy(pk: *mut TariPublicKey) { + if !pk.is_null() { + Box::from_raw(pk); + } +} + +/// Gets a ByteVector from a TariPublicKey +/// +/// ## Arguments +/// `pk` - The pointer to a TariPublicKey +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut ByteVector` - Returns a pointer to a ByteVector. Note that it returns ptr::null_mut() if pk is null +#[no_mangle] +pub unsafe extern "C" fn public_key_get_bytes(pk: *mut TariPublicKey, error_out: *mut c_int) -> *mut ByteVector { + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + let mut bytes = ByteVector(Vec::new()); + if pk.is_null() { + error = LibWalletError::from(InterfaceError::NullError("pk".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } else { + bytes.0 = (*pk).to_vec(); + } + Box::into_raw(Box::new(bytes)) +} + +/// Creates a TariPublicKey from a TariPrivateKey +/// +/// ## Arguments +/// `secret_key` - The pointer to a TariPrivateKey +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut TariPublicKey` - Returns a pointer to a TariPublicKey +#[no_mangle] +pub unsafe extern "C" fn public_key_from_private_key( + secret_key: *mut TariPrivateKey, + error_out: *mut c_int, +) -> *mut TariPublicKey +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if secret_key.is_null() { + error = LibWalletError::from(InterfaceError::NullError("secret_key".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + let m = TariPublicKey::from_secret_key(&(*secret_key)); + Box::into_raw(Box::new(m)) +} + +/// Creates a TariPublicKey from a char array +/// +/// ## Arguments +/// `key` - The pointer to a char array which is hex encoded +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut TariPublicKey` - Returns a pointer to a TariPublicKey. Note that it returns ptr::null_mut() +/// if key is null or if there was an error creating the TariPublicKey from key +#[no_mangle] +pub unsafe extern "C" fn public_key_from_hex(key: *const c_char, error_out: *mut c_int) -> *mut TariPublicKey { + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + let key_str; + if key.is_null() { + error = LibWalletError::from(InterfaceError::NullError("key".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } else { + key_str = CStr::from_ptr(key).to_str().unwrap().to_owned(); + } + + let public_key = TariPublicKey::from_hex(key_str.as_str()); + match public_key { + Ok(public_key) => Box::into_raw(Box::new(public_key)), + Err(e) => { + error = LibWalletError::from(e).code; + ptr::swap(error_out, &mut error as *mut c_int); + ptr::null_mut() + }, + } +} + +/// Creates a char array from a TariPublicKey in emoji format +/// +/// ## Arguments +/// `pk` - The pointer to a TariPublicKey +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut c_char` - Returns a pointer to a char array. Note that it returns empty +/// if emoji is null or if there was an error creating the emoji string from TariPublicKey +#[no_mangle] +pub unsafe extern "C" fn public_key_to_emoji_node_id(pk: *mut TariPublicKey, error_out: *mut c_int) -> *mut c_char { + let mut error = 0; + let mut result = CString::new("").unwrap(); + ptr::swap(error_out, &mut error as *mut c_int); + if pk.is_null() { + error = LibWalletError::from(InterfaceError::NullError("key".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return CString::into_raw(result); + } + + let emoji = EmojiId::from_pubkey(&(*pk)); + result = CString::new(emoji.as_str()).unwrap(); + CString::into_raw(result) +} +/// -------------------------------------------------------------------------------------------- /// + +/// -------------------------------- Private Key ----------------------------------------------- /// + +/// Creates a TariPrivateKey from a ByteVector +/// +/// ## Arguments +/// `bytes` - The pointer to a ByteVector +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut TariPrivateKey` - Returns a pointer to a TariPublicKey. Note that it returns ptr::null_mut() +/// if bytes is null or if there was an error creating the TariPrivateKey from bytes +#[no_mangle] +pub unsafe extern "C" fn private_key_create(bytes: *mut ByteVector, error_out: *mut c_int) -> *mut TariPrivateKey { + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + let v; + if bytes.is_null() { + error = LibWalletError::from(InterfaceError::NullError("bytes".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } else { + v = (*bytes).0.clone(); + } + let pk = TariPrivateKey::from_bytes(&v); + match pk { + Ok(pk) => Box::into_raw(Box::new(pk)), + Err(e) => { + error = LibWalletError::from(e).code; + ptr::swap(error_out, &mut error as *mut c_int); + ptr::null_mut() + }, + } +} + +/// Frees memory for a TariPrivateKey +/// +/// ## Arguments +/// `pk` - The pointer to a TariPrivateKey +/// +/// ## Returns +/// `()` - Does not return a value, equivalent to void in C +#[no_mangle] +pub unsafe extern "C" fn private_key_destroy(pk: *mut TariPrivateKey) { + if !pk.is_null() { + Box::from_raw(pk); + } +} + +/// Gets a ByteVector from a TariPrivateKey +/// +/// ## Arguments +/// `pk` - The pointer to a TariPrivateKey +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut ByteVectror` - Returns a pointer to a ByteVector. Note that it returns ptr::null_mut() +/// if pk is null +#[no_mangle] +pub unsafe extern "C" fn private_key_get_bytes(pk: *mut TariPrivateKey, error_out: *mut c_int) -> *mut ByteVector { + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + let mut bytes = ByteVector(Vec::new()); + if pk.is_null() { + error = LibWalletError::from(InterfaceError::NullError("pk".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } else { + bytes.0 = (*pk).to_vec(); + } + Box::into_raw(Box::new(bytes)) +} + +/// Generates a TariPrivateKey +/// +/// ## Arguments +/// `()` - Does not take any arguments +/// +/// ## Returns +/// `*mut TariPrivateKey` - Returns a pointer to a TariPrivateKey +#[no_mangle] +pub unsafe extern "C" fn private_key_generate() -> *mut TariPrivateKey { + let secret_key = TariPrivateKey::random(&mut OsRng); + Box::into_raw(Box::new(secret_key)) +} + +/// Creates a TariPrivateKey from a char array +/// +/// ## Arguments +/// `key` - The pointer to a char array which is hex encoded +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut TariPrivateKey` - Returns a pointer to a TariPublicKey. Note that it returns ptr::null_mut() +/// if key is null or if there was an error creating the TariPrivateKey from key +#[no_mangle] +pub unsafe extern "C" fn private_key_from_hex(key: *const c_char, error_out: *mut c_int) -> *mut TariPrivateKey { + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + let key_str; + if key.is_null() { + error = LibWalletError::from(InterfaceError::NullError("key".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } else { + key_str = CStr::from_ptr(key).to_str().unwrap().to_owned(); + } + + let secret_key = TariPrivateKey::from_hex(key_str.as_str()); + + match secret_key { + Ok(secret_key) => Box::into_raw(Box::new(secret_key)), + Err(e) => { + error = LibWalletError::from(e).code; + ptr::swap(error_out, &mut error as *mut c_int); + ptr::null_mut() + }, + } +} + +/// -------------------------------------------------------------------------------------------- /// + +/// ----------------------------------- Contact -------------------------------------------------/// + +/// Creates a TariContact +/// +/// ## Arguments +/// `alias` - The pointer to a char array +/// `public_key` - The pointer to a TariPublicKey +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut TariContact` - Returns a pointer to a TariContact. Note that it returns ptr::null_mut() +/// if alias is null or if pk is null +#[no_mangle] +pub unsafe extern "C" fn contact_create( + alias: *const c_char, + public_key: *mut TariPublicKey, + error_out: *mut c_int, +) -> *mut TariContact +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + let alias_string; + if alias.is_null() { + error = LibWalletError::from(InterfaceError::NullError("alias".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } else { + alias_string = CStr::from_ptr(alias).to_str().unwrap().to_owned(); + } + + if public_key.is_null() { + error = LibWalletError::from(InterfaceError::NullError("public_key".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + + let contact = Contact { + alias: alias_string, + public_key: (*public_key).clone(), + }; + Box::into_raw(Box::new(contact)) +} + +/// Gets the alias of the TariContact +/// +/// ## Arguments +/// `contact` - The pointer to a TariContact +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut c_char` - Returns a pointer to a char array. Note that it returns an empty char array if +/// contact is null +#[no_mangle] +pub unsafe extern "C" fn contact_get_alias(contact: *mut TariContact, error_out: *mut c_int) -> *mut c_char { + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + let mut a = CString::new("").unwrap(); + if contact.is_null() { + error = LibWalletError::from(InterfaceError::NullError("contact".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + } else { + a = CString::new((*contact).alias.clone()).unwrap(); + } + CString::into_raw(a) +} + +/// Gets the TariPublicKey of the TariContact +/// +/// ## Arguments +/// `contact` - The pointer to a TariContact +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut TariPublicKey` - Returns a pointer to a TariPublicKey. Note that it returns +/// ptr::null_mut() if contact is null +#[no_mangle] +pub unsafe extern "C" fn contact_get_public_key( + contact: *mut TariContact, + error_out: *mut c_int, +) -> *mut TariPublicKey +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if contact.is_null() { + error = LibWalletError::from(InterfaceError::NullError("contact".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + Box::into_raw(Box::new((*contact).public_key.clone())) +} + +/// Frees memory for a TariContact +/// +/// ## Arguments +/// `contact` - The pointer to a TariContact +/// +/// ## Returns +/// `()` - Does not return a value, equivalent to void in C +#[no_mangle] +pub unsafe extern "C" fn contact_destroy(contact: *mut TariContact) { + if !contact.is_null() { + Box::from_raw(contact); + } +} + +/// ----------------------------------- Contacts -------------------------------------------------/// + +/// Gets the length of TariContacts +/// +/// ## Arguments +/// `contacts` - The pointer to a TariContacts +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `c_uint` - Returns number of elements in , zero if contacts is null +#[no_mangle] +pub unsafe extern "C" fn contacts_get_length(contacts: *mut TariContacts, error_out: *mut c_int) -> c_uint { + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + let mut len = 0; + if contacts.is_null() { + error = LibWalletError::from(InterfaceError::NullError("contacts".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + } else { + len = (*contacts).0.len(); + } + len as c_uint +} + +/// Gets a TariContact from TariContacts at position +/// +/// ## Arguments +/// `contacts` - The pointer to a TariContacts +/// `position` - The integer position +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut TariContact` - Returns a TariContact, note that it returns ptr::null_mut() if contacts is +/// null or position is invalid +#[no_mangle] +pub unsafe extern "C" fn contacts_get_at( + contacts: *mut TariContacts, + position: c_uint, + error_out: *mut c_int, +) -> *mut TariContact +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if contacts.is_null() { + error = LibWalletError::from(InterfaceError::NullError("contacts".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + let len = contacts_get_length(contacts, error_out) as c_int - 1; + if len < 0 || position > len as c_uint { + error = LibWalletError::from(InterfaceError::PositionInvalidError).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + Box::into_raw(Box::new((*contacts).0[position as usize].clone())) +} + +/// Frees memory for a TariContacts +/// +/// ## Arguments +/// `contacts` - The pointer to a TariContacts +/// +/// ## Returns +/// `()` - Does not return a value, equivalent to void in C +#[no_mangle] +pub unsafe extern "C" fn contacts_destroy(contacts: *mut TariContacts) { + if !contacts.is_null() { + Box::from_raw(contacts); + } +} + +/// -------------------------------------------------------------------------------------------- /// + +/// ----------------------------------- CompletedTransactions ----------------------------------- /// + +/// Gets the length of a TariCompletedTransactions +/// +/// ## Arguments +/// `transactions` - The pointer to a TariCompletedTransactions +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `c_uint` - Returns the number of elements in a TariCompletedTransactions, note that it will be +/// zero if transactions is null +#[no_mangle] +pub unsafe extern "C" fn completed_transactions_get_length( + transactions: *mut TariCompletedTransactions, + error_out: *mut c_int, +) -> c_uint +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + let mut len = 0; + if transactions.is_null() { + error = LibWalletError::from(InterfaceError::NullError("transaction".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + } else { + len = (*transactions).0.len(); + } + len as c_uint +} + +/// Gets a TariCompletedTransaction from a TariCompletedTransactions at position +/// +/// ## Arguments +/// `transactions` - The pointer to a TariCompletedTransactions +/// `position` - The integer position +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut TariCompletedTransaction` - Returns a pointer to a TariCompletedTransaction, +/// note that ptr::null_mut() is returned if transactions is null or position is invalid +#[no_mangle] +pub unsafe extern "C" fn completed_transactions_get_at( + transactions: *mut TariCompletedTransactions, + position: c_uint, + error_out: *mut c_int, +) -> *mut TariCompletedTransaction +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if transactions.is_null() { + error = LibWalletError::from(InterfaceError::NullError("transactions".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + let len = completed_transactions_get_length(transactions, error_out) as c_int - 1; + if len < 0 || position > len as c_uint { + error = LibWalletError::from(InterfaceError::PositionInvalidError).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + Box::into_raw(Box::new((*transactions).0[position as usize].clone())) +} + +/// Frees memory for a TariCompletedTransactions +/// +/// ## Arguments +/// `transactions` - The pointer to a TariCompletedTransaction +/// +/// ## Returns +/// `()` - Does not return a value, equivalent to void in C +#[no_mangle] +pub unsafe extern "C" fn completed_transactions_destroy(transactions: *mut TariCompletedTransactions) { + if !transactions.is_null() { + Box::from_raw(transactions); + } +} + +/// -------------------------------------------------------------------------------------------- /// + +/// ----------------------------------- OutboundTransactions ------------------------------------ /// + +/// Gets the length of a TariPendingOutboundTransactions +/// +/// ## Arguments +/// `transactions` - The pointer to a TariPendingOutboundTransactions +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `c_uint` - Returns the number of elements in a TariPendingOutboundTransactions, note that it will be +/// zero if transactions is null +#[no_mangle] +pub unsafe extern "C" fn pending_outbound_transactions_get_length( + transactions: *mut TariPendingOutboundTransactions, + error_out: *mut c_int, +) -> c_uint +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + let mut len = 0; + if transactions.is_null() { + error = LibWalletError::from(InterfaceError::NullError("transaction".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + } else { + len = (*transactions).0.len(); + } + + len as c_uint +} + +/// Gets a TariPendingOutboundTransaction of a TariPendingOutboundTransactions +/// +/// ## Arguments +/// `transactions` - The pointer to a TariPendingOutboundTransactions +/// `position` - The integer position +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut TariPendingOutboundTransaction` - Returns a pointer to a TariPendingOutboundTransaction, +/// note that ptr::null_mut() is returned if transactions is null or position is invalid +#[no_mangle] +pub unsafe extern "C" fn pending_outbound_transactions_get_at( + transactions: *mut TariPendingOutboundTransactions, + position: c_uint, + error_out: *mut c_int, +) -> *mut TariPendingOutboundTransaction +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if transactions.is_null() { + error = LibWalletError::from(InterfaceError::NullError("transaction".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + let len = pending_outbound_transactions_get_length(transactions, error_out) as c_int - 1; + if len < 0 || position > len as c_uint { + error = LibWalletError::from(InterfaceError::PositionInvalidError).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + Box::into_raw(Box::new((*transactions).0[position as usize].clone())) +} + +/// Frees memory for a TariPendingOutboundTransactions +/// +/// ## Arguments +/// `transactions` - The pointer to a TariPendingOutboundTransactions +/// +/// ## Returns +/// `()` - Does not return a value, equivalent to void in C +#[no_mangle] +pub unsafe extern "C" fn pending_outbound_transactions_destroy(transactions: *mut TariPendingOutboundTransactions) { + if !transactions.is_null() { + Box::from_raw(transactions); + } +} + +/// -------------------------------------------------------------------------------------------- /// + +/// ----------------------------------- InboundTransactions ------------------------------------- /// + +/// Gets the length of a TariPendingInboundTransactions +/// +/// ## Arguments +/// `transactions` - The pointer to a TariPendingInboundTransactions +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `c_uint` - Returns the number of elements in a TariPendingInboundTransactions, note that +/// it will be zero if transactions is null +#[no_mangle] +pub unsafe extern "C" fn pending_inbound_transactions_get_length( + transactions: *mut TariPendingInboundTransactions, + error_out: *mut c_int, +) -> c_uint +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + let mut len = 0; + if transactions.is_null() { + error = LibWalletError::from(InterfaceError::NullError("transaction".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + } else { + len = (*transactions).0.len(); + } + len as c_uint +} + +/// Gets a TariPendingInboundTransaction of a TariPendingInboundTransactions +/// +/// ## Arguments +/// `transactions` - The pointer to a TariPendingInboundTransactions +/// `position` - The integer position +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut TariPendingOutboundTransaction` - Returns a pointer to a TariPendingInboundTransaction, +/// note that ptr::null_mut() is returned if transactions is null or position is invalid +#[no_mangle] +pub unsafe extern "C" fn pending_inbound_transactions_get_at( + transactions: *mut TariPendingInboundTransactions, + position: c_uint, + error_out: *mut c_int, +) -> *mut TariPendingInboundTransaction +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if transactions.is_null() { + error = LibWalletError::from(InterfaceError::NullError("transaction".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + let len = pending_inbound_transactions_get_length(transactions, error_out) as c_int - 1; + if len < 0 || position > len as c_uint { + error = LibWalletError::from(InterfaceError::PositionInvalidError).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + Box::into_raw(Box::new((*transactions).0[position as usize].clone())) +} + +/// Frees memory for a TariPendingInboundTransactions +/// +/// ## Arguments +/// `transactions` - The pointer to a TariPendingInboundTransactions +/// +/// ## Returns +/// `()` - Does not return a value, equivalent to void in C +#[no_mangle] +pub unsafe extern "C" fn pending_inbound_transactions_destroy(transactions: *mut TariPendingInboundTransactions) { + if !transactions.is_null() { + Box::from_raw(transactions); + } +} + +/// -------------------------------------------------------------------------------------------- /// + +/// ----------------------------------- CompletedTransaction ------------------------------------- /// + +/// Gets the TransactionID of a TariCompletedTransaction +/// +/// ## Arguments +/// `transaction` - The pointer to a TariCompletedTransaction +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `c_ulonglong` - Returns the TransactionID, note that it will be zero if transaction is null +#[no_mangle] +pub unsafe extern "C" fn completed_transaction_get_transaction_id( + transaction: *mut TariCompletedTransaction, + error_out: *mut c_int, +) -> c_ulonglong +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if transaction.is_null() { + error = LibWalletError::from(InterfaceError::NullError("transaction".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return 0; + } + (*transaction).tx_id as c_ulonglong +} + +/// Gets the destination TariPublicKey of a TariCompletedTransaction +/// +/// ## Arguments +/// `transaction` - The pointer to a TariCompletedTransaction +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut TairPublicKey` - Returns the destination TariPublicKey, note that it will be +/// ptr::null_mut() if transaction is null +#[no_mangle] +pub unsafe extern "C" fn completed_transaction_get_destination_public_key( + transaction: *mut TariCompletedTransaction, + error_out: *mut c_int, +) -> *mut TariPublicKey +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if transaction.is_null() { + error = LibWalletError::from(InterfaceError::NullError("transaction".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + let m = (*transaction).destination_public_key.clone(); + Box::into_raw(Box::new(m)) +} + +/// Gets the source TariPublicKey of a TariCompletedTransaction +/// +/// ## Arguments +/// `transaction` - The pointer to a TariCompletedTransaction +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut TairPublicKey` - Returns the source TariPublicKey, note that it will be +/// ptr::null_mut() if transaction is null +#[no_mangle] +pub unsafe extern "C" fn completed_transaction_get_source_public_key( + transaction: *mut TariCompletedTransaction, + error_out: *mut c_int, +) -> *mut TariPublicKey +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if transaction.is_null() { + error = LibWalletError::from(InterfaceError::NullError("transaction".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + let m = (*transaction).source_public_key.clone(); + Box::into_raw(Box::new(m)) +} + +/// Gets the status of a TariCompletedTransaction +/// +/// ## Arguments +/// `transaction` - The pointer to a TariCompletedTransaction +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `c_int` - Returns the status which corresponds to: +/// | Value | Interpretation | +/// |---|---| +/// | -1 | TxNullError | +/// | 0 | Completed | +/// | 1 | Broadcast | +/// | 2 | Mined | +#[no_mangle] +pub unsafe extern "C" fn completed_transaction_get_status( + transaction: *mut TariCompletedTransaction, + error_out: *mut c_int, +) -> c_int +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if transaction.is_null() { + error = LibWalletError::from(InterfaceError::NullError("transaction".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return -1; + } + let status = (*transaction).status.clone(); + status as c_int +} + +/// Gets the amount of a TariCompletedTransaction +/// +/// ## Arguments +/// `transaction` - The pointer to a TariCompletedTransaction +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `c_ulonglong` - Returns the amount, note that it will be zero if transaction is null +#[no_mangle] +pub unsafe extern "C" fn completed_transaction_get_amount( + transaction: *mut TariCompletedTransaction, + error_out: *mut c_int, +) -> c_ulonglong +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if transaction.is_null() { + error = LibWalletError::from(InterfaceError::NullError("transaction".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return 0; + } + c_ulonglong::from((*transaction).amount) +} + +/// Gets the fee of a TariCompletedTransaction +/// +/// ## Arguments +/// `transaction` - The pointer to a TariCompletedTransaction +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `c_ulonglong` - Returns the fee, note that it will be zero if transaction is null +#[no_mangle] +pub unsafe extern "C" fn completed_transaction_get_fee( + transaction: *mut TariCompletedTransaction, + error_out: *mut c_int, +) -> c_ulonglong +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if transaction.is_null() { + error = LibWalletError::from(InterfaceError::NullError("transaction".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return 0; + } + c_ulonglong::from((*transaction).fee) +} + +/// Gets the timestamp of a TariCompletedTransaction +/// +/// ## Arguments +/// `transaction` - The pointer to a TariCompletedTransaction +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `c_ulonglong` - Returns the timestamp, note that it will be zero if transaction is null +#[no_mangle] +pub unsafe extern "C" fn completed_transaction_get_timestamp( + transaction: *mut TariCompletedTransaction, + error_out: *mut c_int, +) -> c_longlong +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if transaction.is_null() { + error = LibWalletError::from(InterfaceError::NullError("transaction".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return 0; + } + (*transaction).timestamp.timestamp() as c_longlong +} + +/// Gets the message of a TariCompletedTransaction +/// +/// ## Arguments +/// `transaction` - The pointer to a TariCompletedTransaction +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*const c_char` - Returns the pointer to the char array, note that it will return a pointer +/// to an empty char array if transaction is null +#[no_mangle] +pub unsafe extern "C" fn completed_transaction_get_message( + transaction: *mut TariCompletedTransaction, + error_out: *mut c_int, +) -> *const c_char +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + let message = (*transaction).message.clone(); + let mut result = CString::new("").unwrap(); + if transaction.is_null() { + error = LibWalletError::from(InterfaceError::NullError("transaction".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return result.into_raw(); + } + + result = CString::new(message).unwrap(); + result.into_raw() +} + +/// Frees memory for a TariCompletedTransaction +/// +/// ## Arguments +/// `transaction` - The pointer to a TariCompletedTransaction +/// +/// ## Returns +/// `()` - Does not return a value, equivalent to void in C +#[no_mangle] +pub unsafe extern "C" fn completed_transaction_destroy(transaction: *mut TariCompletedTransaction) { + if !transaction.is_null() { + Box::from_raw(transaction); + } +} + +/// -------------------------------------------------------------------------------------------- /// + +/// ----------------------------------- OutboundTransaction ------------------------------------- /// + +/// Gets the TransactionId of a TariPendingOutboundTransaction +/// +/// ## Arguments +/// `transaction` - The pointer to a TariPendingOutboundTransaction +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `c_ulonglong` - Returns the TransactionID, note that it will be zero if transaction is null +#[no_mangle] +pub unsafe extern "C" fn pending_outbound_transaction_get_transaction_id( + transaction: *mut TariPendingOutboundTransaction, + error_out: *mut c_int, +) -> c_ulonglong +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if transaction.is_null() { + error = LibWalletError::from(InterfaceError::NullError("transaction".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return 0; + } + (*transaction).tx_id as c_ulonglong +} + +/// Gets the destination TariPublicKey of a TariPendingOutboundTransaction +/// +/// ## Arguments +/// `transaction` - The pointer to a TariPendingOutboundTransaction +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut TariPublicKey` - Returns the destination TariPublicKey, note that it will be +/// ptr::null_mut() if transaction is null +#[no_mangle] +pub unsafe extern "C" fn pending_outbound_transaction_get_destination_public_key( + transaction: *mut TariPendingOutboundTransaction, + error_out: *mut c_int, +) -> *mut TariPublicKey +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if transaction.is_null() { + error = LibWalletError::from(InterfaceError::NullError("transaction".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + let m = (*transaction).destination_public_key.clone(); + Box::into_raw(Box::new(m)) +} + +/// Gets the amount of a TariPendingOutboundTransaction +/// +/// ## Arguments +/// `transaction` - The pointer to a TariPendingOutboundTransaction +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `c_ulonglong` - Returns the amount, note that it will be zero if transaction is null +#[no_mangle] +pub unsafe extern "C" fn pending_outbound_transaction_get_amount( + transaction: *mut TariPendingOutboundTransaction, + error_out: *mut c_int, +) -> c_ulonglong +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if transaction.is_null() { + error = LibWalletError::from(InterfaceError::NullError("transaction".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return 0; + } + c_ulonglong::from((*transaction).amount) +} + +/// Gets the fee of a TariPendingOutboundTransaction +/// +/// ## Arguments +/// `transaction` - The pointer to a TariPendingOutboundTransaction +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `c_ulonglong` - Returns the fee, note that it will be zero if transaction is null +#[no_mangle] +pub unsafe extern "C" fn pending_outbound_transaction_get_fee( + transaction: *mut TariPendingOutboundTransaction, + error_out: *mut c_int, +) -> c_ulonglong +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if transaction.is_null() { + error = LibWalletError::from(InterfaceError::NullError("transaction".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return 0; + } + c_ulonglong::from((*transaction).fee) +} + +/// Gets the timestamp of a TariPendingOutboundTransaction +/// +/// ## Arguments +/// `transaction` - The pointer to a TariPendingOutboundTransaction +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `c_ulonglong` - Returns the timestamp, note that it will be zero if transaction is null +#[no_mangle] +pub unsafe extern "C" fn pending_outbound_transaction_get_timestamp( + transaction: *mut TariPendingOutboundTransaction, + error_out: *mut c_int, +) -> c_longlong +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if transaction.is_null() { + error = LibWalletError::from(InterfaceError::NullError("transaction".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return 0; + } + (*transaction).timestamp.timestamp() as c_longlong +} + +/// Gets the message of a TariPendingOutboundTransaction +/// +/// ## Arguments +/// `transaction` - The pointer to a TariPendingOutboundTransaction +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*const c_char` - Returns the pointer to the char array, note that it will return a pointer +/// to an empty char array if transaction is null +#[no_mangle] +pub unsafe extern "C" fn pending_outbound_transaction_get_message( + transaction: *mut TariPendingOutboundTransaction, + error_out: *mut c_int, +) -> *const c_char +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + let message = (*transaction).message.clone(); + let mut result = CString::new("").unwrap(); + if transaction.is_null() { + error = LibWalletError::from(InterfaceError::NullError("transaction".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return result.into_raw(); + } + + result = CString::new(message).unwrap(); + result.into_raw() +} + +/// Frees memory for a TariPendingOutboundTransaction +/// +/// ## Arguments +/// `transaction` - The pointer to a TariPendingOutboundTransaction +/// +/// ## Returns +/// `()` - Does not return a value, equivalent to void in C +#[no_mangle] +pub unsafe extern "C" fn pending_outbound_transaction_destroy(transaction: *mut TariPendingOutboundTransaction) { + if !transaction.is_null() { + Box::from_raw(transaction); + } +} + +/// -------------------------------------------------------------------------------------------- /// +/// +/// ----------------------------------- InboundTransaction ------------------------------------- /// + +/// Gets the TransactionId of a TariPendingInboundTransaction +/// +/// ## Arguments +/// `transaction` - The pointer to a TariPendingInboundTransaction +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `c_ulonglong` - Returns the TransactonId, note that it will be zero if transaction is null +#[no_mangle] +pub unsafe extern "C" fn pending_inbound_transaction_get_transaction_id( + transaction: *mut TariPendingInboundTransaction, + error_out: *mut c_int, +) -> c_ulonglong +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if transaction.is_null() { + error = LibWalletError::from(InterfaceError::NullError("transaction".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return 0; + } + (*transaction).tx_id as c_ulonglong +} + +/// Gets the source TariPublicKey of a TariPendingInboundTransaction +/// +/// ## Arguments +/// `transaction` - The pointer to a TariPendingInboundTransaction +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut TariPublicKey` - Returns a pointer to the source TariPublicKey, note that it will be +/// ptr::null_mut() if transaction is null +#[no_mangle] +pub unsafe extern "C" fn pending_inbound_transaction_get_source_public_key( + transaction: *mut TariPendingInboundTransaction, + error_out: *mut c_int, +) -> *mut TariPublicKey +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if transaction.is_null() { + error = LibWalletError::from(InterfaceError::NullError("transaction".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + let m = (*transaction).source_public_key.clone(); + Box::into_raw(Box::new(m)) +} + +/// Gets the amount of a TariPendingInboundTransaction +/// +/// ## Arguments +/// `transaction` - The pointer to a TariPendingInboundTransaction +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `c_ulonglong` - Returns the amount, note that it will be zero if transaction is null +#[no_mangle] +pub unsafe extern "C" fn pending_inbound_transaction_get_amount( + transaction: *mut TariPendingInboundTransaction, + error_out: *mut c_int, +) -> c_ulonglong +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if transaction.is_null() { + error = LibWalletError::from(InterfaceError::NullError("transaction".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return 0; + } + c_ulonglong::from((*transaction).amount) +} + +/// Gets the timestamp of a TariPendingInboundTransaction +/// +/// ## Arguments +/// `transaction` - The pointer to a TariPendingInboundTransaction +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `c_ulonglong` - Returns the timestamp, note that it will be zero if transaction is null +#[no_mangle] +pub unsafe extern "C" fn pending_inbound_transaction_get_timestamp( + transaction: *mut TariPendingInboundTransaction, + error_out: *mut c_int, +) -> c_longlong +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if transaction.is_null() { + error = LibWalletError::from(InterfaceError::NullError("transaction".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return 0; + } + (*transaction).timestamp.timestamp() as c_longlong +} + +/// Gets the message of a TariPendingInboundTransaction +/// +/// ## Arguments +/// `transaction` - The pointer to a TariPendingInboundTransaction +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*const c_char` - Returns the pointer to the char array, note that it will return a pointer +/// to an empty char array if transaction is null +#[no_mangle] +pub unsafe extern "C" fn pending_inbound_transaction_get_message( + transaction: *mut TariPendingInboundTransaction, + error_out: *mut c_int, +) -> *const c_char +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + let message = (*transaction).message.clone(); + let mut result = CString::new("").unwrap(); + if transaction.is_null() { + error = LibWalletError::from(InterfaceError::NullError("transaction".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return result.into_raw(); + } + + result = CString::new(message).unwrap(); + result.into_raw() +} + +/// Frees memory for a TariPendingInboundTransaction +/// +/// ## Arguments +/// `transaction` - The pointer to a TariPendingInboundTransaction +/// +/// ## Returns +/// `()` - Does not return a value, equivalent to void in C +#[no_mangle] +pub unsafe extern "C" fn pending_inbound_transaction_destroy(transaction: *mut TariPendingInboundTransaction) { + if !transaction.is_null() { + Box::from_raw(transaction); + } +} +/// -------------------------------------------------------------------------------------------- /// + +/// ----------------------------------- Transport Types -----------------------------------------/// + +/// Creates a memory transport type +/// +/// ## Arguments +/// `()` - Does not take any arguments +/// +/// ## Returns +/// `*mut TariTransportType` - Returns a pointer to a memory TariTransportType +#[no_mangle] +pub unsafe extern "C" fn transport_memory_create() -> *mut TariTransportType { + let transport = TariTransportType::Memory { + listener_address: get_next_memory_address(), + }; + return Box::into_raw(Box::new(transport)); +} + +/// Creates a tcp transport type +/// +/// ## Arguments +/// `listener_address` - The pointer to a char array +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut TariTransportType` - Returns a pointer to a tcp TariTransportType, null on error. +#[no_mangle] +pub unsafe extern "C" fn transport_tcp_create( + listener_address: *const c_char, + error_out: *mut c_int, +) -> *mut TariTransportType +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + + let listener_address_str; + if !listener_address.is_null() { + listener_address_str = CStr::from_ptr(listener_address).to_str().unwrap().to_owned(); + } else { + error = LibWalletError::from(InterfaceError::NullError("listener_address".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + let transport = TariTransportType::Tcp { + listener_address: listener_address_str.parse::().unwrap(), + }; + return Box::into_raw(Box::new(transport)); +} + +/// Creates a tor transport type +/// +/// ## Arguments +/// `control_server_address` - The pointer to a char array +/// `tor_password` - The pointer to a char array containing the tor password, can be null +/// `tor_identity` - The pointer to a ByteVector containing the tor identity, can be null. +/// `tor_port` - The tor port +/// `socks_username` - The pointer to a char array containing the socks username, can be null +/// `socks_password` - The pointer to a char array containing the socks password, can be null +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut TariTransportType` - Returns a pointer to a tor TariTransportType, null on error. +#[no_mangle] +pub unsafe extern "C" fn transport_tor_create( + control_server_address: *const c_char, + tor_password: *const c_char, + tor_identity: *const ByteVector, + tor_port: c_ushort, + socks_username: *const c_char, + socks_password: *const c_char, + error_out: *mut c_int, +) -> *mut TariTransportType +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + + let control_address_str; + if !control_server_address.is_null() { + control_address_str = CStr::from_ptr(control_server_address).to_str().unwrap().to_owned(); + } else { + error = LibWalletError::from(InterfaceError::NullError("control_address".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + + let username_str; + let password_str; + let mut authentication = socks::Authentication::None; + if !socks_username.is_null() && !socks_password.is_null() { + username_str = CStr::from_ptr(socks_username).to_str().unwrap().to_owned(); + password_str = CStr::from_ptr(socks_password).to_str().unwrap().to_owned(); + authentication = socks::Authentication::Password(username_str, password_str); + } + + let mut tor_authentication = tor::Authentication::None; + if !tor_password.is_null() { + let tor_password_str = CStr::from_ptr(tor_password).to_str().unwrap().to_owned(); + tor_authentication = tor::Authentication::HashedPassword(tor_password_str); + } + + let mut identity = None; + if !tor_identity.is_null() { + let bytes = (*tor_identity).0.as_slice(); + identity = Some(Box::new(tor::TorIdentity::from_binary(bytes.clone()).unwrap())); + } + + let tor_config = TorConfig { + control_server_addr: control_address_str.parse::().unwrap(), + control_server_auth: tor_authentication, + identity, + port_mapping: tor::PortMapping::from_port(tor_port), + socks_auth: authentication, + }; + let transport = TariTransportType::Tor(tor_config); + + return Box::into_raw(Box::new(transport)); +} + +/// Gets the address for a memory transport type +/// +/// ## Arguments +/// `transport` - Pointer to a TariTransportType +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut c_char` - Returns the address as a pointer to a char array, array will be empty on error +#[no_mangle] +pub unsafe extern "C" fn transport_memory_get_address( + transport: *const TariTransportType, + error_out: *mut c_int, +) -> *mut c_char +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + let mut address = CString::new("").unwrap(); + if !transport.is_null() { + match &*transport { + TransportType::Memory { listener_address } => { + address = CString::new(listener_address.to_string()).unwrap(); + }, + _ => { + error = LibWalletError::from(InterfaceError::NullError("transport".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + }, + }; + } else { + error = LibWalletError::from(InterfaceError::NullError("transport".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + } + + return address.into_raw(); +} + +/// Gets the private key for tor +/// +/// ## Arguments +/// `wallet` - Pointer to a TariWallet +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut ByteVector` - Returns the serialized tor identity as a pointer to a ByteVector, contents for ByteVector will +/// be empty on error. +#[no_mangle] +pub unsafe extern "C" fn wallet_get_tor_identity(wallet: *const TariWallet, error_out: *mut c_int) -> *mut ByteVector { + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + let identity_bytes; + if !wallet.is_null() { + let service = (*wallet).comms.hidden_service(); + match service { + Some(s) => { + let tor_identity = s.get_tor_identity().clone(); + identity_bytes = tor_identity.to_binary().unwrap(); + }, + None => { + identity_bytes = Vec::new(); + }, + }; + } else { + identity_bytes = Vec::new(); + error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + } + + let bytes = ByteVector(identity_bytes); + return Box::into_raw(Box::new(bytes)); +} + +/// Frees memory for a TariTransportType +/// +/// ## Arguments +/// `transport` - The pointer to a TariTransportType +/// +/// ## Returns +/// `()` - Does not return a value, equivalent to void in C +#[no_mangle] +pub unsafe extern "C" fn transport_type_destroy(transport: *mut TariTransportType) { + if !transport.is_null() { + Box::from_raw(transport); + } +} +/// ---------------------------------------------------------------------------------------------/// + +/// ----------------------------------- CommsConfig ---------------------------------------------/// + +/// Creates a TariCommsConfig. The result from this function is required when initializing a TariWallet. +/// +/// ## Arguments +/// `public_address` - The public address char array pointer. This is the address that the wallet advertises publicly to +/// peers +/// `listener_address` - The listener address char array pointer. This is the address that inbound peer +/// connections are moved to after initial connection. Default if null is 0.0.0.0:7898 which will accept connections +/// from all IP address on port 7898 +/// `database_name` - The database name char array pointer. This is the unique name of this +/// wallet's database `database_path` - The database path char array pointer which. This is the folder path where the +/// database files will be created and the application has write access to +/// `secret_key` - The TariSecretKey pointer. This is the secret key corresponding to the Public key that represents +/// this node on the Tari comms network +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut TariCommsConfig` - Returns a pointer to a TariCommsConfig, if any of the parameters are +/// null or a problem is encountered when constructing the NetAddress a ptr::null_mut() is returned +#[no_mangle] +pub unsafe extern "C" fn comms_config_create( + public_address: *const c_char, + transport_type: *const TariTransportType, + database_name: *const c_char, + datastore_path: *const c_char, + secret_key: *mut TariPrivateKey, + error_out: *mut c_int, +) -> *mut TariCommsConfig +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + let public_address_str; + if !public_address.is_null() { + public_address_str = CStr::from_ptr(public_address).to_str().unwrap().to_owned(); + } else { + error = LibWalletError::from(InterfaceError::NullError("public_address".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + + let database_name_string; + if !database_name.is_null() { + database_name_string = CStr::from_ptr(database_name).to_str().unwrap().to_owned(); + } else { + error = LibWalletError::from(InterfaceError::NullError("database_name".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + + let datastore_path_string; + if !datastore_path.is_null() { + datastore_path_string = CStr::from_ptr(datastore_path).to_str().unwrap().to_owned(); + } else { + error = LibWalletError::from(InterfaceError::NullError("datastore_path".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + + let public_address = public_address_str.parse::(); + + match public_address { + Ok(public_address) => { + let ni = NodeIdentity::new( + (*secret_key).clone(), + public_address, + PeerFeatures::COMMUNICATION_CLIENT, + ); + match ni { + Ok(ni) => { + let config = TariCommsConfig { + node_identity: Arc::new(ni.clone()), + transport_type: (*transport_type).clone(), + datastore_path: datastore_path_string, + peer_database_name: database_name_string, + max_concurrent_inbound_tasks: 100, + outbound_buffer_size: 100, + dht: DhtConfig::default(), + }; + + Box::into_raw(Box::new(config)) + }, + Err(e) => { + error = LibWalletError::from(e).code; + ptr::swap(error_out, &mut error as *mut c_int); + ptr::null_mut() + }, + } + }, + Err(e) => { + error = LibWalletError::from(e).code; + ptr::swap(error_out, &mut error as *mut c_int); + ptr::null_mut() + }, + } +} + +/// Frees memory for a TariCommsConfig +/// +/// ## Arguments +/// `wc` - The TariCommsConfig pointer +/// +/// ## Returns +/// `()` - Does not return a value, equivalent to void in C +#[no_mangle] +pub unsafe extern "C" fn comms_config_destroy(wc: *mut TariCommsConfig) { + if !wc.is_null() { + Box::from_raw(wc); + } +} + +/// ---------------------------------------------------------------------------------------------- /// + +/// ------------------------------------- Wallet -------------------------------------------------/// + +/// Creates a TariWallet +/// +/// ## Arguments +/// `config` - The TariCommsConfig pointer +/// `log_path` - An optional file path to the file where the logs will be written. If no log is required pass *null* +/// pointer. +/// `callback_received_transaction` - The callback function pointer matching the function signature +/// `callback_received_transaction_reply` - The callback function pointer matching the function signature +/// `callback_received_finalized_transaction` - The callback function pointer matching the function signature +/// `callback_transaction_broadcast` - The callback function pointer matching the function signature +/// `callback_transaction_mined` - The callback function pointer matching the function signature +/// `callback_discovery_process_complete` - The callback function pointer matching the function signature +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// ## Returns +/// `*mut TariWallet` - Returns a pointer to a TariWallet, note that it returns ptr::null_mut() +/// if config is null, a wallet error was encountered or if the runtime could not be created +#[no_mangle] +pub unsafe extern "C" fn wallet_create( + config: *mut TariCommsConfig, + log_path: *const c_char, + callback_received_transaction: unsafe extern "C" fn(*mut TariPendingInboundTransaction), + callback_received_transaction_reply: unsafe extern "C" fn(*mut TariCompletedTransaction), + callback_received_finalized_transaction: unsafe extern "C" fn(*mut TariCompletedTransaction), + callback_transaction_broadcast: unsafe extern "C" fn(*mut TariCompletedTransaction), + callback_transaction_mined: unsafe extern "C" fn(*mut TariCompletedTransaction), + callback_discovery_process_complete: unsafe extern "C" fn(c_ulonglong, bool), + error_out: *mut c_int, +) -> *mut TariWallet +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if config.is_null() { + error = LibWalletError::from(InterfaceError::NullError("config".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + + let mut logging_path_string = None; + if !log_path.is_null() { + logging_path_string = Some(CStr::from_ptr(log_path).to_str().unwrap().to_owned()); + } + + let runtime = Runtime::new(); + let factories = CryptoFactories::default(); + let w; + match runtime { + Ok(runtime) => { + let sql_database_path = format!( + "{}/{}.sqlite3", + (*config).datastore_path.clone(), + (*config).peer_database_name.clone() + ); + let connection_pool = run_migration_and_create_connection_pool(&sql_database_path) + .expect("Could not create Sqlite Connection Pool"); + let wallet_backend = WalletSqliteDatabase::new(connection_pool.clone()); + let transaction_backend = TransactionServiceSqliteDatabase::new(connection_pool.clone()); + let output_manager_backend = OutputManagerSqliteDatabase::new(connection_pool.clone()); + let contacts_backend = ContactsServiceSqliteDatabase::new(connection_pool); + + w = TariWallet::new( + WalletConfig { + comms_config: (*config).clone(), + logging_path: logging_path_string, + factories, + }, + runtime, + wallet_backend, + transaction_backend.clone(), + output_manager_backend, + contacts_backend, + ); + + match w { + Ok(w) => { + // Start Callback Handler + let callback_handler = CallbackHandler::new( + TransactionDatabase::new(transaction_backend), + w.transaction_service.get_event_stream_fused(), + w.comms.shutdown_signal(), + callback_received_transaction, + callback_received_transaction_reply, + callback_received_finalized_transaction, + callback_transaction_broadcast, + callback_transaction_mined, + callback_discovery_process_complete, + ); + + w.runtime.spawn(callback_handler.start()); + + Box::into_raw(Box::new(w)) + }, + Err(e) => { + error = LibWalletError::from(e).code; + ptr::swap(error_out, &mut error as *mut c_int); + ptr::null_mut() + }, + } + }, + Err(e) => { + error = LibWalletError::from(InterfaceError::TokioError(e.to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + ptr::null_mut() + }, + } +} + +/// Signs a message using the public key of the TariWallet +/// +/// ## Arguments +/// `wallet` - The TariWallet pointer. +/// `msg` - The message pointer. +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// ## Returns +/// `*mut c_char` - Returns the pointer to the hexadecimal representation of the signature and +/// public nonce, seperated by a pipe character. Empty if an error occured. +#[no_mangle] +pub unsafe extern "C" fn wallet_sign_message( + wallet: *mut TariWallet, + msg: *const c_char, + error_out: *mut c_int, +) -> *mut c_char +{ + let mut error = 0; + let mut result = CString::new("").unwrap(); + + ptr::swap(error_out, &mut error as *mut c_int); + if wallet.is_null() { + error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return result.into_raw(); + } + + if msg.is_null() { + error = LibWalletError::from(InterfaceError::NullError("message".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return result.into_raw(); + } + + let nonce = TariPrivateKey::random(&mut OsRng); + let secret = (*wallet).comms.node_identity().secret_key().clone(); + let message = CStr::from_ptr(msg).to_str().unwrap().to_owned(); + let signature = (*wallet).sign_message(secret, nonce, &message); + + match signature { + Ok(s) => { + let hex_sig = s.get_signature().to_hex(); + let hex_nonce = s.get_public_nonce().to_hex(); + let hex_return = format!("{}|{}", hex_sig, hex_nonce); + result = CString::new(hex_return).unwrap(); + }, + Err(e) => { + error = LibWalletError::from(e).code; + ptr::swap(error_out, &mut error as *mut c_int); + }, + } + + result.into_raw() +} + +/// Verifies the signature of the message signed by a TariWallet +/// +/// ## Arguments +/// `wallet` - The TariWallet pointer. +/// `public_key` - The pointer to the TariPublicKey of the wallet which originally signed the message +/// `hex_sig_nonce` - The pointer to the sting containing the hexadecimal representation of the +/// signature and public nonce seperated by a pipe character. +/// `msg` - The pointer to the msg the signature will be checked against. +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// ## Returns +/// `bool` - Returns if the signature is valid or not, will be false if an error occurs. +#[no_mangle] +pub unsafe extern "C" fn wallet_verify_message_signature( + wallet: *mut TariWallet, + public_key: *mut TariPublicKey, + hex_sig_nonce: *const c_char, + msg: *const c_char, + error_out: *mut c_int, +) -> bool +{ + let mut error = 0; + let mut result = false; + ptr::swap(error_out, &mut error as *mut c_int); + if wallet.is_null() { + error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return result; + } + if public_key.is_null() { + error = LibWalletError::from(InterfaceError::NullError("public key".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return result; + } + if hex_sig_nonce.is_null() { + error = LibWalletError::from(InterfaceError::NullError("signature".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return result; + } + if msg.is_null() { + error = LibWalletError::from(InterfaceError::NullError("message".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return result; + } + + let message = CStr::from_ptr(msg).to_str().unwrap().to_owned(); + let hex = CStr::from_ptr(hex_sig_nonce).to_str().unwrap().to_owned(); + let hex_keys: Vec<&str> = hex.split('|').collect(); + if hex_keys.len() != 2 { + error = LibWalletError::from(InterfaceError::PositionInvalidError).code; + ptr::swap(error_out, &mut error as *mut c_int); + return result; + } + let secret = TariPrivateKey::from_hex(hex_keys.get(0).unwrap()); + match secret { + Ok(p) => { + let public_nonce = TariPublicKey::from_hex(hex_keys.get(1).unwrap()); + match public_nonce { + Ok(pn) => result = (*wallet).verify_message_signature((*public_key).clone(), pn, p, message), + Err(e) => { + error = LibWalletError::from(e).code; + ptr::swap(error_out, &mut error as *mut c_int); + }, + } + }, + Err(e) => { + error = LibWalletError::from(e).code; + ptr::swap(error_out, &mut error as *mut c_int); + }, + } + + result +} + +/// This function will generate some test data in the wallet. The data generated will be +/// as follows: +/// +/// - Some Contacts +/// - Add outputs to the wallet that make up its Available Balance that can be spent +/// - Create transaction history +/// - Pending Inbound Transactions +/// - Pending Outbound Transactions +/// - Completed Transactions +/// +/// ## Arguments +/// `wallet` - The TariWallet pointer +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `bool` - Returns if successful or not +#[no_mangle] +pub unsafe extern "C" fn wallet_test_generate_data( + wallet: *mut TariWallet, + datastore_path: *const c_char, + error_out: *mut c_int, +) -> bool +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if wallet.is_null() { + error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return false; + } + let datastore_path_string; + if !datastore_path.is_null() { + datastore_path_string = CStr::from_ptr(datastore_path).to_str().unwrap().to_owned(); + } else { + error = LibWalletError::from(InterfaceError::NullError("datastore_path".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return false; + } + + match generate_wallet_test_data( + &mut *wallet, + datastore_path_string.as_str(), + (*wallet).transaction_backend.clone(), + ) { + Ok(_) => true, + Err(e) => { + error = LibWalletError::from(e).code; + ptr::swap(error_out, &mut error as *mut c_int); + false + }, + } +} + +/// This function simulates an external `TariWallet` sending a transaction to this `TariWallet` +/// which will become a `TariPendingInboundTransaction` +/// +/// ## Arguments +/// `wallet` - The TariWallet pointer +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `bool` - Returns if successful or not +#[no_mangle] +pub unsafe extern "C" fn wallet_test_receive_transaction(wallet: *mut TariWallet, error_out: *mut c_int) -> bool { + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if wallet.is_null() { + error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return false; + } + match receive_test_transaction(&mut *wallet) { + Ok(_) => true, + Err(e) => { + error = LibWalletError::from(e).code; + ptr::swap(error_out, &mut error as *mut c_int); + false + }, + } +} + +/// This function simulates a receiver accepting and replying to a `TariPendingOutboundTransaction`. +/// This results in that transaction being "completed" and it's status set to `Broadcast` which +/// indicated it is in a base_layer mempool. +/// +/// ## Arguments +/// `wallet` - The TariWallet pointer +/// `tx` - The TariPendingOutboundTransaction +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `bool` - Returns if successful or not +#[no_mangle] +pub unsafe extern "C" fn wallet_test_complete_sent_transaction( + wallet: *mut TariWallet, + tx: *mut TariPendingOutboundTransaction, + error_out: *mut c_int, +) -> bool +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if wallet.is_null() { + error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return false; + } + if tx.is_null() { + error = LibWalletError::from(InterfaceError::NullError("tx".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return false; + } + match complete_sent_transaction(&mut *wallet, (*tx).tx_id) { + Ok(_) => true, + Err(e) => { + error = LibWalletError::from(e).code; + ptr::swap(error_out, &mut error as *mut c_int); + false + }, + } +} + +/// This function checks to determine if a TariCompletedTransaction was originally a TariPendingOutboundTransaction +/// +/// ## Arguments +/// `wallet` - The TariWallet pointer +/// `tx` - The TariCompletedTransaction +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `bool` - Returns if the transaction was originally sent from the wallet +#[no_mangle] +pub unsafe extern "C" fn wallet_is_completed_transaction_outbound( + wallet: *mut TariWallet, + tx: *mut TariCompletedTransaction, + error_out: *mut c_int, +) -> bool +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if wallet.is_null() { + error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return false; + } + if tx.is_null() { + error = LibWalletError::from(InterfaceError::NullError("tx".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return false; + } + + if (*tx).source_public_key == (*wallet).comms.node_identity().public_key().clone() { + return true; + } + + false +} + +/// This function will simulate the process when a completed transaction is broadcast to +/// the base layer mempool. The function will update the status of the completed transaction +/// +/// ## Arguments +/// `wallet` - The TariWallet pointer +/// `tx` - The pending inbound transaction to operate on +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `bool` - Returns if successful or not +#[no_mangle] +pub unsafe extern "C" fn wallet_test_finalize_received_transaction( + wallet: *mut TariWallet, + tx: *mut TariPendingInboundTransaction, + error_out: *mut c_int, +) -> bool +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if wallet.is_null() { + error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return false; + } + + match finalize_received_transaction(&mut *wallet, (*tx).tx_id) { + Ok(_) => true, + Err(e) => { + error = LibWalletError::from(e).code; + ptr::swap(error_out, &mut error as *mut c_int); + false + }, + } +} + +/// This function will simulate the process when a completed transaction is broadcast to +/// the base layer mempool. The function will update the status of the completed transaction +/// +/// ## Arguments +/// `wallet` - The TariWallet pointer +/// `tx` - The completed transaction to operate on +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `bool` - Returns if successful or not +#[no_mangle] +pub unsafe extern "C" fn wallet_test_broadcast_transaction( + wallet: *mut TariWallet, + tx: *mut TariCompletedTransaction, + error_out: *mut c_int, +) -> bool +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if wallet.is_null() { + error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return false; + } + + if tx.is_null() { + error = LibWalletError::from(InterfaceError::NullError("tx".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return false; + } + + match broadcast_transaction(&mut *wallet, (*tx).tx_id) { + Ok(_) => true, + Err(e) => { + error = LibWalletError::from(e).code; + ptr::swap(error_out, &mut error as *mut c_int); + false + }, + } +} + +/// This function will simulate the process when a completed transaction is detected as mined on +/// the base layer. The function will update the status of the completed transaction AND complete +/// the transaction on the Output Manager Service which will update the status of the outputs +/// +/// ## Arguments +/// `wallet` - The TariWallet pointer +/// `tx` - The completed transaction to operate on +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `bool` - Returns if successful or not +#[no_mangle] +pub unsafe extern "C" fn wallet_test_mine_transaction( + wallet: *mut TariWallet, + tx: *mut TariCompletedTransaction, + error_out: *mut c_int, +) -> bool +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if wallet.is_null() { + error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return false; + } + if tx.is_null() { + error = LibWalletError::from(InterfaceError::NullError("tx".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return false; + } + match mine_transaction(&mut *wallet, (*tx).tx_id) { + Ok(_) => true, + Err(e) => { + error = LibWalletError::from(e).code; + ptr::swap(error_out, &mut error as *mut c_int); + false + }, + } +} + +/// Adds a base node peer to the TariWallet +/// +/// ## Arguments +/// `wallet` - The TariWallet pointer +/// `public_key` - The TariPublicKey pointer +/// `address` - The pointer to a char array +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `bool` - Returns if successful or not +#[no_mangle] +pub unsafe extern "C" fn wallet_add_base_node_peer( + wallet: *mut TariWallet, + public_key: *mut TariPublicKey, + address: *const c_char, + error_out: *mut c_int, +) -> bool +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if wallet.is_null() { + error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return false; + } + + if public_key.is_null() { + error = LibWalletError::from(InterfaceError::NullError("public_key".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return false; + } + + let address_string; + if !address.is_null() { + address_string = CStr::from_ptr(address).to_str().unwrap().to_owned(); + } else { + error = LibWalletError::from(InterfaceError::NullError("address".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return false; + } + + match (*wallet).set_base_node_peer((*public_key).clone(), address_string) { + Ok(_) => true, + Err(e) => { + error = LibWalletError::from(e).code; + ptr::swap(error_out, &mut error as *mut c_int); + false + }, + } +} + +/// Upserts a TariContact to the TariWallet. If the contact does not exist it will be Inserted. If it does exist the +/// Alias will be updated. +/// +/// ## Arguments +/// `wallet` - The TariWallet pointer +/// `contact` - The TariContact pointer +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `bool` - Returns if successful or not +#[no_mangle] +pub unsafe extern "C" fn wallet_upsert_contact( + wallet: *mut TariWallet, + contact: *mut TariContact, + error_out: *mut c_int, +) -> bool +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if wallet.is_null() { + error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return false; + } + if contact.is_null() { + error = LibWalletError::from(InterfaceError::NullError("contact".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return false; + } + + match (*wallet) + .runtime + .block_on((*wallet).contacts_service.upsert_contact((*contact).clone())) + { + Ok(_) => true, + Err(e) => { + error = LibWalletError::from(WalletError::ContactsServiceError(e)).code; + ptr::swap(error_out, &mut error as *mut c_int); + false + }, + } +} + +/// Removes a TariContact from the TariWallet +/// +/// ## Arguments +/// `wallet` - The TariWallet pointer +/// `tx` - The TariPendingInboundTransaction pointer +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `bool` - Returns if successful or not +#[no_mangle] +pub unsafe extern "C" fn wallet_remove_contact( + wallet: *mut TariWallet, + contact: *mut TariContact, + error_out: *mut c_int, +) -> bool +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if wallet.is_null() { + error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return false; + } + if contact.is_null() { + error = LibWalletError::from(InterfaceError::NullError("contact".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return false; + } + + match (*wallet) + .runtime + .block_on((*wallet).contacts_service.remove_contact((*contact).public_key.clone())) + { + Ok(_) => true, + Err(e) => { + error = LibWalletError::from(WalletError::ContactsServiceError(e)).code; + ptr::swap(error_out, &mut error as *mut c_int); + false + }, + } +} + +/// Gets the available balance from a TariWallet. This is the balance the user can spend. +/// +/// ## Arguments +/// `wallet` - The TariWallet pointer +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `c_ulonglong` - The available balance, 0 if wallet is null +#[no_mangle] +pub unsafe extern "C" fn wallet_get_available_balance(wallet: *mut TariWallet, error_out: *mut c_int) -> c_ulonglong { + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if wallet.is_null() { + error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return 0; + } + + match (*wallet) + .runtime + .block_on((*wallet).output_manager_service.get_balance()) + { + Ok(b) => c_ulonglong::from(b.available_balance), + Err(e) => { + error = LibWalletError::from(WalletError::OutputManagerError(e)).code; + ptr::swap(error_out, &mut error as *mut c_int); + 0 + }, + } +} + +/// Gets the incoming balance from a `TariWallet`. This is the uncleared balance of Tari that is +/// expected to come into the `TariWallet` but is not yet spendable. +/// +/// ## Arguments +/// `wallet` - The TariWallet pointer +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `c_ulonglong` - The incoming balance, 0 if wallet is null +#[no_mangle] +pub unsafe extern "C" fn wallet_get_pending_incoming_balance( + wallet: *mut TariWallet, + error_out: *mut c_int, +) -> c_ulonglong +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if wallet.is_null() { + error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return 0; + } + + match (*wallet) + .runtime + .block_on((*wallet).output_manager_service.get_balance()) + { + Ok(b) => c_ulonglong::from(b.pending_incoming_balance), + Err(e) => { + error = LibWalletError::from(WalletError::OutputManagerError(e)).code; + ptr::swap(error_out, &mut error as *mut c_int); + 0 + }, + } +} + +/// Gets the outgoing balance from a `TariWallet`. This is the uncleared balance of Tari that has +/// been spent +/// +/// ## Arguments +/// `wallet` - The TariWallet pointer +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `c_ulonglong` - The outgoing balance, 0 if wallet is null +#[no_mangle] +pub unsafe extern "C" fn wallet_get_pending_outgoing_balance( + wallet: *mut TariWallet, + error_out: *mut c_int, +) -> c_ulonglong +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if wallet.is_null() { + error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return 0; + } + + match (*wallet) + .runtime + .block_on((*wallet).output_manager_service.get_balance()) + { + Ok(b) => c_ulonglong::from(b.pending_outgoing_balance), + Err(e) => { + error = LibWalletError::from(WalletError::OutputManagerError(e)).code; + ptr::swap(error_out, &mut error as *mut c_int); + 0 + }, + } +} + +/// Sends a TariPendingOutboundTransaction +/// +/// ## Arguments +/// `wallet` - The TariWallet pointer +/// `dest_public_key` - The TariPublicKey pointer of the peer +/// `amount` - The amount +/// `fee_per_gram` - The transaction fee +/// `message` - The pointer to a char array +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `bool` - Returns if successful or not +#[no_mangle] +pub unsafe extern "C" fn wallet_send_transaction( + wallet: *mut TariWallet, + dest_public_key: *mut TariPublicKey, + amount: c_ulonglong, + fee_per_gram: c_ulonglong, + message: *const c_char, + error_out: *mut c_int, +) -> bool +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if wallet.is_null() { + error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return false; + } + + if dest_public_key.is_null() { + error = LibWalletError::from(InterfaceError::NullError("dest_public_key".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return false; + } + + let mut message_string = CString::new("").unwrap().to_str().unwrap().to_owned(); + if !message.is_null() { + message_string = CStr::from_ptr(message).to_str().unwrap().to_owned(); + } else { + error = LibWalletError::from(InterfaceError::NullError("message".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + } + + match (*wallet) + .runtime + .block_on((*wallet).transaction_service.send_transaction( + (*dest_public_key).clone(), + MicroTari::from(amount), + MicroTari::from(fee_per_gram), + message_string, + )) { + Ok(_) => true, + Err(e) => { + error = LibWalletError::from(WalletError::TransactionServiceError(e)).code; + ptr::swap(error_out, &mut error as *mut c_int); + false + }, + } +} + +/// Get the TariContacts from a TariWallet +/// +/// ## Arguments +/// `wallet` - The TariWallet pointer +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut TariContacts` - returns the contacts, note that it returns ptr::null_mut() if +/// wallet is null +#[no_mangle] +pub unsafe extern "C" fn wallet_get_contacts(wallet: *mut TariWallet, error_out: *mut c_int) -> *mut TariContacts { + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + let mut contacts = Vec::new(); + if wallet.is_null() { + error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + + let retrieved_contacts = (*wallet).runtime.block_on((*wallet).contacts_service.get_contacts()); + match retrieved_contacts { + Ok(mut retrieved_contacts) => { + contacts.append(&mut retrieved_contacts); + Box::into_raw(Box::new(TariContacts(contacts))) + }, + Err(e) => { + error = LibWalletError::from(WalletError::ContactsServiceError(e)).code; + ptr::swap(error_out, &mut error as *mut c_int); + ptr::null_mut() + }, + } +} + +/// Get the TariCompletedTransactions from a TariWallet +/// +/// ## Arguments +/// `wallet` - The TariWallet pointer +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut TariCompletedTransactions` - returns the transactions, note that it returns ptr::null_mut() if +/// wallet is null or an error is encountered +#[no_mangle] +pub unsafe extern "C" fn wallet_get_completed_transactions( + wallet: *mut TariWallet, + error_out: *mut c_int, +) -> *mut TariCompletedTransactions +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + let mut completed = Vec::new(); + if wallet.is_null() { + error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + + let completed_transactions = (*wallet) + .runtime + .block_on((*wallet).transaction_service.get_completed_transactions()); + match completed_transactions { + Ok(completed_transactions) => { + for (_id, tx) in &completed_transactions { + completed.push(tx.clone()); + } + Box::into_raw(Box::new(TariCompletedTransactions(completed))) + }, + Err(e) => { + error = LibWalletError::from(WalletError::TransactionServiceError(e)).code; + ptr::swap(error_out, &mut error as *mut c_int); + ptr::null_mut() + }, + } +} + +/// Get the TariPendingInboundTransactions from a TariWallet +/// +/// ## Arguments +/// `wallet` - The TariWallet pointer +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut TariPendingInboundTransactions` - returns the transactions, note that it returns ptr::null_mut() if +/// wallet is null or and error is encountered +#[no_mangle] +pub unsafe extern "C" fn wallet_get_pending_inbound_transactions( + wallet: *mut TariWallet, + error_out: *mut c_int, +) -> *mut TariPendingInboundTransactions +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + let mut pending = Vec::new(); + if wallet.is_null() { + error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + + let pending_transactions = (*wallet) + .runtime + .block_on((*wallet).transaction_service.get_pending_inbound_transactions()); + match pending_transactions { + Ok(pending_transactions) => { + for (_id, tx) in &pending_transactions { + pending.push(tx.clone()); + } + Box::into_raw(Box::new(TariPendingInboundTransactions(pending))) + }, + Err(e) => { + error = LibWalletError::from(WalletError::TransactionServiceError(e)).code; + ptr::swap(error_out, &mut error as *mut c_int); + ptr::null_mut() + }, + } +} + +/// Get the TariPendingOutboundTransactions from a TariWallet +/// +/// ## Arguments +/// `wallet` - The TariWallet pointer +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut TariPendingOutboundTransactions` - returns the transactions, note that it returns ptr::null_mut() if +/// wallet is null or and error is encountered +#[no_mangle] +pub unsafe extern "C" fn wallet_get_pending_outbound_transactions( + wallet: *mut TariWallet, + error_out: *mut c_int, +) -> *mut TariPendingOutboundTransactions +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + let mut pending = Vec::new(); + if wallet.is_null() { + error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + + let pending_transactions = (*wallet) + .runtime + .block_on((*wallet).transaction_service.get_pending_outbound_transactions()); + match pending_transactions { + Ok(pending_transactions) => { + for (_id, tx) in &pending_transactions { + pending.push(tx.clone()); + } + Box::into_raw(Box::new(TariPendingOutboundTransactions(pending))) + }, + Err(e) => { + error = LibWalletError::from(WalletError::TransactionServiceError(e)).code; + ptr::swap(error_out, &mut error as *mut c_int); + ptr::null_mut() + }, + } +} + +/// Get the TariCompletedTransaction from a TariWallet by its' TransactionId +/// +/// ## Arguments +/// `wallet` - The TariWallet pointer +/// `transaction_id` - The TransactionId +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut TariCompletedTransaction` - returns the transaction, note that it returns ptr::null_mut() if +/// wallet is null, an error is encountered or if the transaction is not found +#[no_mangle] +pub unsafe extern "C" fn wallet_get_completed_transaction_by_id( + wallet: *mut TariWallet, + transaction_id: c_ulonglong, + error_out: *mut c_int, +) -> *mut TariCompletedTransaction +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if wallet.is_null() { + error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + + let pending_transactions = (*wallet) + .runtime + .block_on((*wallet).transaction_service.get_completed_transactions()); + + match pending_transactions { + Ok(pending_transactions) => { + for (id, tx) in &pending_transactions { + if id == &transaction_id { + let pending = tx.clone(); + return Box::into_raw(Box::new(pending)); + } + } + return ptr::null_mut(); + }, + Err(e) => { + error = LibWalletError::from(WalletError::TransactionServiceError(e)).code; + ptr::swap(error_out, &mut error as *mut c_int); + ptr::null_mut() + }, + } +} + +/// Get the TariPendingInboundTransaction from a TariWallet by its' TransactionId +/// +/// ## Arguments +/// `wallet` - The TariWallet pointer +/// `transaction_id` - The TransactionId +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut TariPendingInboundTransaction` - returns the transaction, note that it returns ptr::null_mut() if +/// wallet is null, an error is encountered or if the transaction is not found +#[no_mangle] +pub unsafe extern "C" fn wallet_get_pending_inbound_transaction_by_id( + wallet: *mut TariWallet, + transaction_id: c_ulonglong, + error_out: *mut c_int, +) -> *mut TariPendingInboundTransaction +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if wallet.is_null() { + error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + + let pending_transactions = (*wallet) + .runtime + .block_on((*wallet).transaction_service.get_pending_inbound_transactions()); + + match pending_transactions { + Ok(pending_transactions) => { + for (id, tx) in &pending_transactions { + if id == &transaction_id { + let pending = tx.clone(); + return Box::into_raw(Box::new(pending)); + } + } + ptr::null_mut() + }, + Err(e) => { + error = LibWalletError::from(WalletError::TransactionServiceError(e)).code; + ptr::swap(error_out, &mut error as *mut c_int); + ptr::null_mut() + }, + } +} + +/// Get the TariPendingOutboundTransaction from a TariWallet by its' TransactionId +/// +/// ## Arguments +/// `wallet` - The TariWallet pointer +/// `transaction_id` - The TransactionId +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut TariPendingOutboundTransaction` - returns the transaction, note that it returns ptr::null_mut() if +/// wallet is null, an error is encountered or if the transaction is not found +#[no_mangle] +pub unsafe extern "C" fn wallet_get_pending_outbound_transaction_by_id( + wallet: *mut TariWallet, + transaction_id: c_ulonglong, + error_out: *mut c_int, +) -> *mut TariPendingOutboundTransaction +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if wallet.is_null() { + error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + + let pending_transactions = (*wallet) + .runtime + .block_on((*wallet).transaction_service.get_pending_outbound_transactions()); + + match pending_transactions { + Ok(pending_transactions) => { + for (id, tx) in &pending_transactions { + if id == &transaction_id { + let pending = tx.clone(); + return Box::into_raw(Box::new(pending)); + } + } + ptr::null_mut() + }, + Err(e) => { + error = LibWalletError::from(WalletError::TransactionServiceError(e)).code; + ptr::swap(error_out, &mut error as *mut c_int); + ptr::null_mut() + }, + } +} + +/// Get the TariPublicKey from a TariWallet +/// +/// ## Arguments +/// `wallet` - The TariWallet pointer +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `*mut TariPublicKey` - returns the public key, note that ptr::null_mut() is returned +/// if wc is null +#[no_mangle] +pub unsafe extern "C" fn wallet_get_public_key(wallet: *mut TariWallet, error_out: *mut c_int) -> *mut TariPublicKey { + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if wallet.is_null() { + error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return ptr::null_mut(); + } + let pk = (*wallet).comms.node_identity().public_key().clone(); + Box::into_raw(Box::new(pk)) +} + +/// Import a UTXO into the wallet. This will add a spendable UTXO and create a faux completed transaction to record the +/// event. +/// +/// ## Arguments +/// `wallet` - The TariWallet pointer +/// `amount` - The value of the UTXO in MicroTari +/// `spending_key` - The private spending key +/// `source_public_key` - The public key of the source of the transaction +/// `message` - The message that the transaction will have +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `c_ulonglong` - Returns the TransactionID of the generated transaction, note that it will be zero if transaction is +/// null +#[no_mangle] +pub unsafe extern "C" fn wallet_import_utxo( + wallet: *mut TariWallet, + amount: c_ulonglong, + spending_key: *mut TariPrivateKey, + source_public_key: *mut TariPublicKey, + message: *const c_char, + error_out: *mut c_int, +) -> c_ulonglong +{ + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if wallet.is_null() { + error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return 0; + } + + if spending_key.is_null() { + error = LibWalletError::from(InterfaceError::NullError("spending_key".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return 0; + } + + if source_public_key.is_null() { + error = LibWalletError::from(InterfaceError::NullError("source_public_key".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return 0; + } + + let mut message_string = CString::new("Imported UTXO").unwrap().to_str().unwrap().to_owned(); + if !message.is_null() { + message_string = CStr::from_ptr(message).to_str().unwrap().to_owned(); + } else { + error = LibWalletError::from(InterfaceError::NullError("message".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + } + + match (*wallet).import_utxo( + MicroTari::from(amount), + &(*spending_key).clone(), + &(*source_public_key).clone(), + message_string, + ) { + Ok(tx_id) => tx_id, + Err(e) => { + error = LibWalletError::from(e).code; + ptr::swap(error_out, &mut error as *mut c_int); + 0 + }, + } +} + +/// This function will tell the wallet to query the set base node to confirm the status of wallet data. For example this +/// will check that Unspent Outputs stored in the wallet are still available as UTXO's on the blockchain +/// +/// ## Arguments +/// `wallet` - The TariWallet pointer +/// `error_out` - Pointer to an int which will be modified to an error code should one occur, may not be null. Functions +/// as an out parameter. +/// +/// ## Returns +/// `bool` - Returns where the sync command was executed successfully +#[no_mangle] +pub unsafe extern "C" fn wallet_sync_with_base_node(wallet: *mut TariWallet, error_out: *mut c_int) -> bool { + let mut error = 0; + ptr::swap(error_out, &mut error as *mut c_int); + if wallet.is_null() { + error = LibWalletError::from(InterfaceError::NullError("wallet".to_string())).code; + ptr::swap(error_out, &mut error as *mut c_int); + return false; + } + + match (*wallet).sync_with_base_node() { + Ok(()) => true, + Err(e) => { + error = LibWalletError::from(e).code; + ptr::swap(error_out, &mut error as *mut c_int); + false + }, + } +} + +/// Frees memory for a TariWallet +/// +/// ## Arguments +/// `wallet` - The TariWallet pointer +/// +/// ## Returns +/// `()` - Does not return a value, equivalent to void in C +#[no_mangle] +pub unsafe extern "C" fn wallet_destroy(wallet: *mut TariWallet) { + if !wallet.is_null() { + let m = Box::from_raw(wallet); + m.shutdown(); + } +} + +#[cfg(test)] +mod test { + extern crate libc; + use crate::*; + use libc::{c_char, c_uchar, c_uint}; + use std::{ffi::CString, sync::Mutex}; + use tari_core::transactions::tari_amount::uT; + use tari_wallet::{testnet_utils::random_string, transaction_service::storage::database::TransactionStatus}; + use tempdir::TempDir; + + fn type_of(_: T) -> String { + std::any::type_name::().to_string() + } + + #[derive(Debug)] + struct CallbackState { + pub received_tx_callback_called: bool, + pub received_tx_reply_callback_called: bool, + pub received_finalized_tx_callback_called: bool, + pub broadcast_tx_callback_called: bool, + pub mined_tx_callback_called: bool, + pub discovery_send_callback_called: bool, + pub base_node_error_callback_called: bool, + } + + impl CallbackState { + fn new() -> Self { + Self { + received_tx_callback_called: false, + received_tx_reply_callback_called: false, + received_finalized_tx_callback_called: false, + broadcast_tx_callback_called: false, + mined_tx_callback_called: false, + discovery_send_callback_called: false, + base_node_error_callback_called: false, + } + } + + fn reset(&mut self) { + self.received_tx_callback_called = false; + self.received_tx_reply_callback_called = false; + self.received_finalized_tx_callback_called = false; + self.broadcast_tx_callback_called = false; + self.mined_tx_callback_called = false; + self.discovery_send_callback_called = false; + self.base_node_error_callback_called = false; + } + } + + lazy_static! { + static ref CALLBACK_STATE_FFI: Mutex = { + let c = Mutex::new(CallbackState::new()); + c + }; + } + + unsafe extern "C" fn received_tx_callback(tx: *mut TariPendingInboundTransaction) { + assert_eq!(tx.is_null(), false); + assert_eq!( + type_of((*tx).clone()), + std::any::type_name::() + ); + let mut lock = CALLBACK_STATE_FFI.lock().unwrap(); + lock.received_tx_callback_called = true; + drop(lock); + pending_inbound_transaction_destroy(tx); + } + + unsafe extern "C" fn received_tx_reply_callback(tx: *mut TariCompletedTransaction) { + assert_eq!(tx.is_null(), false); + assert_eq!( + type_of((*tx).clone()), + std::any::type_name::() + ); + assert_eq!((*tx).status, TransactionStatus::Completed); + let mut lock = CALLBACK_STATE_FFI.lock().unwrap(); + lock.received_tx_reply_callback_called = true; + drop(lock); + completed_transaction_destroy(tx); + } + + unsafe extern "C" fn received_tx_finalized_callback(tx: *mut TariCompletedTransaction) { + assert_eq!(tx.is_null(), false); + assert_eq!( + type_of((*tx).clone()), + std::any::type_name::() + ); + assert_eq!((*tx).status, TransactionStatus::Completed); + let mut lock = CALLBACK_STATE_FFI.lock().unwrap(); + lock.received_finalized_tx_callback_called = true; + drop(lock); + completed_transaction_destroy(tx); + } + + unsafe extern "C" fn broadcast_callback(tx: *mut TariCompletedTransaction) { + assert_eq!(tx.is_null(), false); + assert_eq!( + type_of((*tx).clone()), + std::any::type_name::() + ); + let mut lock = CALLBACK_STATE_FFI.lock().unwrap(); + lock.broadcast_tx_callback_called = true; + drop(lock); + assert_eq!((*tx).status, TransactionStatus::Broadcast); + completed_transaction_destroy(tx); + } + + unsafe extern "C" fn mined_callback(tx: *mut TariCompletedTransaction) { + assert_eq!(tx.is_null(), false); + assert_eq!( + type_of((*tx).clone()), + std::any::type_name::() + ); + assert_eq!((*tx).status, TransactionStatus::Mined); + let mut lock = CALLBACK_STATE_FFI.lock().unwrap(); + lock.mined_tx_callback_called = true; + drop(lock); + completed_transaction_destroy(tx); + } + + unsafe extern "C" fn discovery_process_complete_callback(_tx_id: c_ulonglong, _result: bool) { + assert!(true); + } + + unsafe extern "C" fn received_tx_callback_bob(tx: *mut TariPendingInboundTransaction) { + assert_eq!(tx.is_null(), false); + assert_eq!( + type_of((*tx).clone()), + std::any::type_name::() + ); + pending_inbound_transaction_destroy(tx); + } + + unsafe extern "C" fn received_tx_reply_callback_bob(tx: *mut TariCompletedTransaction) { + assert_eq!(tx.is_null(), false); + assert_eq!( + type_of((*tx).clone()), + std::any::type_name::() + ); + assert_eq!((*tx).status, TransactionStatus::Completed); + completed_transaction_destroy(tx); + } + + unsafe extern "C" fn received_tx_finalized_callback_bob(tx: *mut TariCompletedTransaction) { + assert_eq!(tx.is_null(), false); + assert_eq!( + type_of((*tx).clone()), + std::any::type_name::() + ); + assert_eq!((*tx).status, TransactionStatus::Completed); + completed_transaction_destroy(tx); + } + + unsafe extern "C" fn broadcast_callback_bob(tx: *mut TariCompletedTransaction) { + assert_eq!(tx.is_null(), false); + assert_eq!( + type_of((*tx).clone()), + std::any::type_name::() + ); + assert_eq!((*tx).status, TransactionStatus::Broadcast); + completed_transaction_destroy(tx); + } + + unsafe extern "C" fn mined_callback_bob(tx: *mut TariCompletedTransaction) { + assert_eq!(tx.is_null(), false); + assert_eq!( + type_of((*tx).clone()), + std::any::type_name::() + ); + assert_eq!((*tx).status, TransactionStatus::Mined); + completed_transaction_destroy(tx); + } + + unsafe extern "C" fn discovery_process_complete_callback_bob(_tx_id: c_ulonglong, _result: bool) { + assert!(true); + } + + #[test] + fn test_bytevector() { + unsafe { + let mut error = 0; + let error_ptr = &mut error as *mut c_int; + let bytes: [c_uchar; 4] = [2, 114, 34, 255]; + let bytes_ptr = byte_vector_create(bytes.as_ptr(), bytes.len() as c_uint, error_ptr); + assert_eq!(error, 0); + let length = byte_vector_get_length(bytes_ptr, error_ptr); + assert_eq!(error, 0); + assert_eq!(length, bytes.len() as c_uint); + let byte = byte_vector_get_at(bytes_ptr, 2, error_ptr); + assert_eq!(error, 0); + assert_eq!(byte, bytes[2]); + byte_vector_destroy(bytes_ptr); + } + } + + #[test] + fn test_bytevector_dont_panic() { + unsafe { + let mut error = 0; + let error_ptr = &mut error as *mut c_int; + let bytes_ptr = byte_vector_create(ptr::null_mut(), 20 as c_uint, error_ptr); + assert_eq!( + error, + LibWalletError::from(InterfaceError::NullError("bytes_ptr".to_string())).code + ); + assert_eq!(byte_vector_get_length(bytes_ptr, error_ptr), 0); + assert_eq!( + error, + LibWalletError::from(InterfaceError::NullError("bytes_ptr".to_string())).code + ); + byte_vector_destroy(bytes_ptr); + } + } + + #[test] + fn test_transport_type_memory() { + unsafe { + let mut error = 0; + let error_ptr = &mut error as *mut c_int; + let transport = transport_memory_create(); + let _address = transport_memory_get_address(transport, error_ptr); + assert_eq!(error, 0); + } + } + + #[test] + fn test_transport_type_tcp() { + unsafe { + let mut error = 0; + let error_ptr = &mut error as *mut c_int; + let address_listener = CString::new("/ip4/127.0.0.1/tcp/0").unwrap(); + let address_listener_str: *const c_char = CString::into_raw(address_listener.clone()) as *const c_char; + let _transport = transport_tcp_create(address_listener_str, error_ptr); + assert_eq!(error, 0); + } + } + + #[test] + fn test_transport_type_tor() { + unsafe { + let mut error = 0; + let error_ptr = &mut error as *mut c_int; + let address_control = CString::new("/ip4/127.0.0.1/tcp/8080").unwrap(); + let address_control_str: *const c_char = CString::into_raw(address_control.clone()) as *const c_char; + let _transport = transport_tor_create( + address_control_str, + ptr::null_mut(), + ptr::null_mut(), + 8080, + ptr::null_mut(), + ptr::null_mut(), + error_ptr, + ); + assert_eq!(error, 0); + } + } + + #[test] + fn test_keys() { + unsafe { + let mut error = 0; + let error_ptr = &mut error as *mut c_int; + let private_key = private_key_generate(); + let public_key = public_key_from_private_key(private_key, error_ptr); + assert_eq!(error, 0); + let private_bytes = private_key_get_bytes(private_key, error_ptr); + assert_eq!(error, 0); + let public_bytes = public_key_get_bytes(public_key, error_ptr); + assert_eq!(error, 0); + let private_key_length = byte_vector_get_length(private_bytes, error_ptr); + assert_eq!(error, 0); + let public_key_length = byte_vector_get_length(public_bytes, error_ptr); + assert_eq!(error, 0); + assert_eq!(private_key_length, 32); + assert_eq!(public_key_length, 32); + assert_ne!((*private_bytes), (*public_bytes)); + let emoji = public_key_to_emoji_node_id(public_key, error_ptr) as *mut c_char; + let emoji_str = CStr::from_ptr(emoji).to_str().unwrap().to_owned(); + assert_eq!(EmojiId::is_valid(&emoji_str, &(*public_key)), true); + private_key_destroy(private_key); + public_key_destroy(public_key); + byte_vector_destroy(public_bytes); + byte_vector_destroy(private_bytes); + } + } + + #[test] + fn test_keys_dont_panic() { + unsafe { + let mut error = 0; + let error_ptr = &mut error as *mut c_int; + let private_key = private_key_create(ptr::null_mut(), error_ptr); + assert_eq!( + error, + LibWalletError::from(InterfaceError::NullError("bytes_ptr".to_string())).code + ); + let public_key = public_key_from_private_key(ptr::null_mut(), error_ptr); + assert_eq!( + error, + LibWalletError::from(InterfaceError::NullError("secret_key_ptr".to_string())).code + ); + let private_bytes = private_key_get_bytes(ptr::null_mut(), error_ptr); + assert_eq!( + error, + LibWalletError::from(InterfaceError::NullError("pk_ptr".to_string())).code + ); + let public_bytes = public_key_get_bytes(ptr::null_mut(), error_ptr); + assert_eq!( + error, + LibWalletError::from(InterfaceError::NullError("pk_ptr".to_string())).code + ); + let private_key_length = byte_vector_get_length(ptr::null_mut(), error_ptr); + assert_eq!( + error, + LibWalletError::from(InterfaceError::NullError("vec_ptr".to_string())).code + ); + let public_key_length = byte_vector_get_length(ptr::null_mut(), error_ptr); + assert_eq!( + error, + LibWalletError::from(InterfaceError::NullError("vec_ptr".to_string())).code + ); + assert_eq!(private_key_length, 0); + assert_eq!(public_key_length, 0); + private_key_destroy(private_key); + public_key_destroy(public_key); + byte_vector_destroy(public_bytes); + byte_vector_destroy(private_bytes); + } + } + + #[test] + fn test_contact() { + unsafe { + let mut error = 0; + let error_ptr = &mut error as *mut c_int; + let test_contact_private_key = private_key_generate(); + let test_contact_public_key = public_key_from_private_key(test_contact_private_key, error_ptr); + let test_str = "Test Contact"; + let test_contact_str = CString::new(test_str).unwrap(); + let test_contact_alias: *const c_char = CString::into_raw(test_contact_str) as *const c_char; + let test_contact = contact_create(test_contact_alias, test_contact_public_key, error_ptr); + let alias = contact_get_alias(test_contact, error_ptr); + let alias_string = CString::from_raw(alias).to_str().unwrap().to_owned(); + assert_eq!(alias_string, test_str); + let contact_key = contact_get_public_key(test_contact, error_ptr); + let contact_key_bytes = public_key_get_bytes(contact_key, error_ptr); + let contact_bytes_len = byte_vector_get_length(contact_key_bytes, error_ptr); + assert_eq!(contact_bytes_len, 32); + contact_destroy(test_contact); + public_key_destroy(test_contact_public_key); + private_key_destroy(test_contact_private_key); + string_destroy(test_contact_alias as *mut c_char); + byte_vector_destroy(contact_key_bytes); + } + } + + #[test] + fn test_contact_dont_panic() { + unsafe { + let mut error = 0; + let error_ptr = &mut error as *mut c_int; + let test_contact_private_key = private_key_generate(); + let test_contact_public_key = public_key_from_private_key(test_contact_private_key, error_ptr); + let test_str = "Test Contact"; + let test_contact_str = CString::new(test_str).unwrap(); + let test_contact_alias: *const c_char = CString::into_raw(test_contact_str) as *const c_char; + let mut _test_contact = contact_create(ptr::null_mut(), test_contact_public_key, error_ptr); + assert_eq!( + error, + LibWalletError::from(InterfaceError::NullError("alias_ptr".to_string())).code + ); + _test_contact = contact_create(test_contact_alias, ptr::null_mut(), error_ptr); + assert_eq!( + error, + LibWalletError::from(InterfaceError::NullError("public_key_ptr".to_string())).code + ); + let _alias = contact_get_alias(ptr::null_mut(), error_ptr); + assert_eq!( + error, + LibWalletError::from(InterfaceError::NullError("contact_ptr".to_string())).code + ); + let _contact_key = contact_get_public_key(ptr::null_mut(), error_ptr); + assert_eq!( + error, + LibWalletError::from(InterfaceError::NullError("contact_ptr".to_string())).code + ); + let contact_key_bytes = public_key_get_bytes(ptr::null_mut(), error_ptr); + assert_eq!( + error, + LibWalletError::from(InterfaceError::NullError("contact_ptr".to_string())).code + ); + let contact_bytes_len = byte_vector_get_length(ptr::null_mut(), error_ptr); + assert_eq!( + error, + LibWalletError::from(InterfaceError::NullError("contact_ptr".to_string())).code + ); + assert_eq!(contact_bytes_len, 0); + contact_destroy(_test_contact); + public_key_destroy(test_contact_public_key); + private_key_destroy(test_contact_private_key); + string_destroy(test_contact_alias as *mut c_char); + byte_vector_destroy(contact_key_bytes); + } + } + #[test] + fn test_wallet_ffi() { + unsafe { + { + let mut lock = CALLBACK_STATE_FFI.lock().unwrap(); + lock.reset(); + } + + let mut error = 0; + let error_ptr = &mut error as *mut c_int; + let secret_key_alice = private_key_generate(); + let public_key_alice = public_key_from_private_key(secret_key_alice.clone(), error_ptr); + let db_name_alice = CString::new(random_string(8).as_str()).unwrap(); + let db_name_alice_str: *const c_char = CString::into_raw(db_name_alice.clone()) as *const c_char; + let alice_temp_dir = TempDir::new(random_string(8).as_str()).unwrap(); + let db_path_alice = CString::new(alice_temp_dir.path().to_str().unwrap()).unwrap(); + let db_path_alice_str: *const c_char = CString::into_raw(db_path_alice.clone()) as *const c_char; + let transport_type_alice = transport_memory_create(); + let address_alice = transport_memory_get_address(transport_type_alice, error_ptr); + let address_alice_str = CStr::from_ptr(address_alice).to_str().unwrap().to_owned(); + let address_alice_str: *const c_char = CString::new(address_alice_str).unwrap().into_raw() as *const c_char; + + let alice_config = comms_config_create( + address_alice_str, + transport_type_alice, + db_name_alice_str, + db_path_alice_str, + secret_key_alice, + error_ptr, + ); + let alice_wallet = wallet_create( + alice_config, + ptr::null(), + received_tx_callback, + received_tx_reply_callback, + received_tx_finalized_callback, + broadcast_callback, + mined_callback, + discovery_process_complete_callback, + error_ptr, + ); + let secret_key_bob = private_key_generate(); + let public_key_bob = public_key_from_private_key(secret_key_bob.clone(), error_ptr); + let db_name_bob = CString::new(random_string(8).as_str()).unwrap(); + let db_name_bob_str: *const c_char = CString::into_raw(db_name_bob.clone()) as *const c_char; + let bob_temp_dir = TempDir::new(random_string(8).as_str()).unwrap(); + let db_path_bob = CString::new(bob_temp_dir.path().to_str().unwrap()).unwrap(); + let db_path_bob_str: *const c_char = CString::into_raw(db_path_bob.clone()) as *const c_char; + let transport_type_bob = transport_memory_create(); + let address_bob = transport_memory_get_address(transport_type_bob, error_ptr); + let address_bob_str = CStr::from_ptr(address_bob).to_str().unwrap().to_owned(); + let address_bob_str: *const c_char = CString::new(address_bob_str).unwrap().into_raw() as *const c_char; + let bob_config = comms_config_create( + address_bob_str, + transport_type_bob, + db_name_bob_str, + db_path_bob_str, + secret_key_bob, + error_ptr, + ); + let bob_wallet = wallet_create( + bob_config, + ptr::null(), + received_tx_callback_bob, + received_tx_reply_callback_bob, + received_tx_finalized_callback_bob, + broadcast_callback_bob, + mined_callback_bob, + discovery_process_complete_callback_bob, + error_ptr, + ); + + let sig_msg = CString::new("Test Contact").unwrap(); + let sig_msg_str: *const c_char = CString::into_raw(sig_msg) as *const c_char; + let sig_msg_compare = CString::new("Test Contact").unwrap(); + let sig_msg_compare_str: *const c_char = CString::into_raw(sig_msg_compare) as *const c_char; + let sig_nonce_str: *mut c_char = wallet_sign_message(alice_wallet, sig_msg_str, error_ptr) as *mut c_char; + let alice_wallet_key = wallet_get_public_key(alice_wallet, error_ptr); + let verify_msg = wallet_verify_message_signature( + alice_wallet, + alice_wallet_key, + sig_nonce_str, + sig_msg_compare_str, + error_ptr, + ); + assert_eq!(verify_msg, true); + + let test_contact_private_key = private_key_generate(); + let test_contact_public_key = public_key_from_private_key(test_contact_private_key, error_ptr); + let test_contact_str = CString::new("Test Contact").unwrap(); + let test_contact_alias: *const c_char = CString::into_raw(test_contact_str) as *const c_char; + let test_contact = contact_create(test_contact_alias, test_contact_public_key, error_ptr); + let contact_added = wallet_upsert_contact(alice_wallet, test_contact, error_ptr); + assert_eq!(contact_added, true); + let contact_removed = wallet_remove_contact(alice_wallet, test_contact, error_ptr); + assert_eq!(contact_removed, true); + contact_destroy(test_contact); + public_key_destroy(test_contact_public_key); + private_key_destroy(test_contact_private_key); + string_destroy(test_contact_alias as *mut c_char); + + let generated = wallet_test_generate_data(alice_wallet, db_path_alice_str, error_ptr); + assert_eq!(generated, true); + assert_eq!( + (wallet_get_completed_transactions(&mut (*alice_wallet), error_ptr)).is_null(), + false + ); + assert_eq!( + (wallet_get_pending_inbound_transactions(&mut (*alice_wallet), error_ptr)).is_null(), + false + ); + assert_eq!( + (wallet_get_pending_outbound_transactions(&mut (*alice_wallet), error_ptr)).is_null(), + false + ); + + let inbound_transactions: std::collections::HashMap< + u64, + tari_wallet::transaction_service::storage::database::InboundTransaction, + > = (*alice_wallet) + .runtime + .block_on((*alice_wallet).transaction_service.get_pending_inbound_transactions()) + .unwrap(); + + assert_eq!(inbound_transactions.len(), 0); + + wallet_test_receive_transaction(alice_wallet, error_ptr); + + let inbound_transactions: std::collections::HashMap< + u64, + tari_wallet::transaction_service::storage::database::InboundTransaction, + > = (*alice_wallet) + .runtime + .block_on((*alice_wallet).transaction_service.get_pending_inbound_transactions()) + .unwrap(); + + assert_eq!(inbound_transactions.len(), 1); + + let completed_transactions: std::collections::HashMap< + u64, + tari_wallet::transaction_service::storage::database::CompletedTransaction, + > = (*alice_wallet) + .runtime + .block_on((*alice_wallet).transaction_service.get_completed_transactions()) + .unwrap(); + + let num_completed_tx_pre = completed_transactions.len(); + + for (_k, v) in inbound_transactions { + let tx_ptr = Box::into_raw(Box::new(v.clone())); + wallet_test_finalize_received_transaction(alice_wallet, tx_ptr, error_ptr); + break; + } + + let completed_transactions: std::collections::HashMap< + u64, + tari_wallet::transaction_service::storage::database::CompletedTransaction, + > = (*alice_wallet) + .runtime + .block_on((*alice_wallet).transaction_service.get_completed_transactions()) + .unwrap(); + + assert_eq!(num_completed_tx_pre + 1, completed_transactions.len()); + + // TODO: Test transaction collection and transaction methods + let completed_transactions: std::collections::HashMap< + u64, + tari_wallet::transaction_service::storage::database::CompletedTransaction, + > = (*alice_wallet) + .runtime + .block_on((*alice_wallet).transaction_service.get_completed_transactions()) + .unwrap(); + for (_k, v) in completed_transactions { + if v.status == TransactionStatus::Completed { + let tx_ptr = Box::into_raw(Box::new(v.clone())); + wallet_test_broadcast_transaction(alice_wallet, tx_ptr, error_ptr); + wallet_test_mine_transaction(alice_wallet, tx_ptr, error_ptr); + } + } + + let contacts = wallet_get_contacts(alice_wallet, error_ptr); + assert_eq!(contacts_get_length(contacts, error_ptr), 4); + + let utxo_spending_key = private_key_generate(); + let utxo_value = 20000; + + let pre_balance = (*alice_wallet) + .runtime + .block_on((*alice_wallet).output_manager_service.get_balance()) + .unwrap(); + + let secret_key_base_node = private_key_generate(); + let public_key_base_node = public_key_from_private_key(secret_key_base_node.clone(), error_ptr); + let utxo_message_str = CString::new("UTXO Import").unwrap(); + let utxo_message: *const c_char = CString::into_raw(utxo_message_str) as *const c_char; + + let utxo_tx_id = wallet_import_utxo( + alice_wallet, + utxo_value, + utxo_spending_key, + public_key_base_node, + utxo_message, + error_ptr, + ); + + let post_balance = (*alice_wallet) + .runtime + .block_on((*alice_wallet).output_manager_service.get_balance()) + .unwrap(); + + assert_eq!( + pre_balance.available_balance + utxo_value * uT, + post_balance.available_balance + ); + + let import_transaction = (*alice_wallet) + .runtime + .block_on((*alice_wallet).transaction_service.get_completed_transactions()) + .unwrap() + .remove(&utxo_tx_id) + .expect("Tx should be in collection"); + + assert_eq!(import_transaction.amount, utxo_value * uT); + assert_eq!(wallet_sync_with_base_node(alice_wallet, error_ptr), false); + let mut peer_added = + wallet_add_base_node_peer(alice_wallet, public_key_bob.clone(), address_bob_str, error_ptr); + assert_eq!(peer_added, true); + peer_added = wallet_add_base_node_peer(bob_wallet, public_key_alice.clone(), address_alice_str, error_ptr); + assert_eq!(peer_added, true); + (*alice_wallet) + .runtime + .block_on( + (*alice_wallet) + .comms + .connection_manager() + .dial_peer((*bob_wallet).comms.node_identity().node_id().clone()), + ) + .unwrap(); + assert_eq!(wallet_sync_with_base_node(alice_wallet, error_ptr), true); + + let lock = CALLBACK_STATE_FFI.lock().unwrap(); + assert!(lock.received_tx_callback_called); + assert!(lock.received_tx_reply_callback_called); + assert!(lock.received_finalized_tx_callback_called); + assert!(lock.broadcast_tx_callback_called); + assert!(lock.mined_tx_callback_called); + drop(lock); + // Not testing for the discovery_process_completed callback as its tricky to evoke and it is unit tested + // elsewhere + + // free string memory + string_destroy(db_name_alice_str as *mut c_char); + string_destroy(db_path_alice_str as *mut c_char); + string_destroy(address_alice_str as *mut c_char); + string_destroy(db_name_bob_str as *mut c_char); + string_destroy(db_path_bob_str as *mut c_char); + string_destroy(address_bob_str as *mut c_char); + // free wallet memory + wallet_destroy(alice_wallet); + wallet_destroy(bob_wallet); + // free keys + private_key_destroy(secret_key_alice); + private_key_destroy(secret_key_bob); + public_key_destroy(public_key_alice); + public_key_destroy(public_key_bob); + // free config memory + comms_config_destroy(bob_config); + comms_config_destroy(alice_config); + transport_type_destroy(transport_type_alice); + transport_type_destroy(transport_type_bob); + } + } +} diff --git a/base_layer/wallet_ffi/wallet.h b/base_layer/wallet_ffi/wallet.h new file mode 100644 index 0000000000..3e62a7ceaa --- /dev/null +++ b/base_layer/wallet_ffi/wallet.h @@ -0,0 +1,405 @@ +// Copyright 2019. The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +//! # LibWallet API Definition +//! This module contains the Rust backend implementations of the functionality that a wallet for the Tari Base Layer +//! will require. The module contains a number of sub-modules that are implemented as async services. These services are +//! collected into the main Wallet container struct which manages spinning up all the component services and maintains a +//! collection of the handles required to interact with those services. +//! This files contians the API calls that will be exposed to external systems that make use of this module. The API +//! will be exposed via FFI and will consist of API calls that the FFI client can make into the Wallet module and a set +//! of Callbacks that the client must implement and provide to the Wallet module to receive asynchronous replies and +//! updates. + +// TODO: Improve documentation +#ifndef wallet_ffi_h +#define wallet_ffi_h + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +struct ByteVector; + +struct TariCommsConfig; + +struct TariPrivateKey; + +struct TariWallet; + +struct TariWalletConfig; + +struct TariPublicKey; + +struct TariContacts; + +struct TariContact; + +struct TariCompletedTransactions; + +struct TariCompletedTransaction; + +struct TariPendingOutboundTransactions; + +struct TariPendingOutboundTransaction; + +struct TariPendingInboundTransactions; + +struct TariPendingInboundTransaction; + +struct TariTransportType; + +/// -------------------------------- Transport Types ----------------------------------------------- /// + +// Creates a memory transport type +struct TariTransportType *transport_memory_create(); + +// Creates a tcp transport type +struct TariTransportType *transport_tcp_create(const char *listener_address,int* error_out); + +// Creates a tor transport type +struct TariTransportType *transport_tor_create( + const char *control_server_address, + const char *tor_password, + struct ByteVector *tor_identity, + unsigned short tor_port, + const char *socks_username, + const char *socks_password, + int* error_out); + +// Gets the tor private key from the wallet +struct ByteVector *wallet_get_tor_identity(struct TariWallet *wallet,int* error_out ); + +// Gets the address from a memory transport type +char *transport_memory_get_address(struct TariTransportType *transport,int* error_out); + +// Frees memory for a transport type +void transport_type_destroy(struct TariTransportType *transport); + +/// -------------------------------- Strings ----------------------------------------------- /// + +// Frees memory for a string pointer +void string_destroy(char *s); + +/// -------------------------------- ByteVector ----------------------------------------------- /// + +// Creates a ByteVector +struct ByteVector *byte_vector_create(const unsigned char *byte_array, unsigned int element_count, int* error_out); + +// Gets a char from a ByteVector +unsigned char byte_vector_get_at(struct ByteVector *ptr, unsigned int i, int* error_out); + +// Returns the number of elements in a ByteVector +unsigned int byte_vector_get_length(const struct ByteVector *vec, int* error_out); + +// Frees memory for a ByteVector pointer +void byte_vector_destroy(struct ByteVector *bytes); + +/// -------------------------------- TariPublicKey ----------------------------------------------- /// + +// Creates a TariPublicKey from a ByteVector +struct TariPublicKey *public_key_create(struct ByteVector *bytes,int* error_out); + +// Gets a ByteVector from a TariPublicKey +struct ByteVector *public_key_get_bytes(struct TariPublicKey *public_key,int* error_out); + +// Creates a TariPublicKey from a TariPrivateKey +struct TariPublicKey *public_key_from_private_key(struct TariPrivateKey *secret_key,int* error_out); + +// Creates a TariPublicKey from a const char* filled with hexadecimal characters +struct TariPublicKey *public_key_from_hex(const char *hex,int* error_out); + +// Frees memory for a TariPublicKey pointer +void public_key_destroy(struct TariPublicKey *pk); + +//Converts a TariPublicKey to char array in emoji format +char *public_key_to_emoji_node_id(struct TariPublicKey *pk, int* error_out); + +/// -------------------------------- TariPrivateKey ----------------------------------------------- /// + +// Creates a TariPrivateKey from a ByteVector +struct TariPrivateKey *private_key_create(struct ByteVector *bytes,int* error_out); + +// Generates a TariPrivateKey +struct TariPrivateKey *private_key_generate(void); + +// Creates a ByteVector from a TariPrivateKey +struct ByteVector *private_key_get_bytes(struct TariPrivateKey *private_key,int* error_out); + +// Creates a TariPrivateKey from a const char* filled with hexadecimal characters +struct TariPrivateKey *private_key_from_hex(const char *hex,int* error_out); + +// Frees memory for a TariPrivateKey +void private_key_destroy(struct TariPrivateKey *pk); + +/// -------------------------------- Contact ------------------------------------------------------ /// + +// Creates a TariContact +struct TariContact *contact_create(const char *alias, struct TariPublicKey *public_key,int* error_out); + +// Gets the alias of the TariContact +char *contact_get_alias(struct TariContact *contact,int* error_out); + +/// Gets the TariPublicKey of the TariContact +struct TariPublicKey *contact_get_public_key(struct TariContact *contact,int* error_out); + +// Frees memory for a TariContact +void contact_destroy(struct TariContact *contact); + +/// -------------------------------- Contacts ------------------------------------------------------ /// + +// Gets the number of elements of TariContacts +unsigned int contacts_get_length(struct TariContacts *contacts,int* error_out); + +// Gets a TariContact from TariContacts at position +struct TariContact *contacts_get_at(struct TariContacts *contacts, unsigned int position,int* error_out); + +// Frees memory for TariContacts +void contacts_destroy(struct TariContacts *contacts); + +/// -------------------------------- CompletedTransaction ------------------------------------------------------ /// + +// Gets the destination TariPublicKey of a TariCompletedTransaction +struct TariPublicKey *completed_transaction_get_destination_public_key(struct TariCompletedTransaction *transaction,int* error_out); + +// Gets the source TariPublicKey of a TariCompletedTransaction +struct TariPublicKey *completed_transaction_get_source_public_key(struct TariCompletedTransaction *transaction,int* error_out); + +// Gets the amount of a TariCompletedTransaction +unsigned long long completed_transaction_get_amount(struct TariCompletedTransaction *transaction,int* error_out); + +// Gets the fee of a TariCompletedTransaction +unsigned long long completed_transaction_get_fee(struct TariCompletedTransaction *transaction,int* error_out); + +// Gets the message of a TariCompletedTransaction +const char *completed_transaction_get_message(struct TariCompletedTransaction *transaction,int* error_out); + +// Gets the status of a TariCompletedTransaction +// | Value | Interpretation | +// |---|---| +// | -1 | TxNullError | +// | 0 | Completed | +// | 1 | Broadcast | +// | 2 | Mined | +int completed_transaction_get_status(struct TariCompletedTransaction *transaction,int* error_out); + +// Gets the TransactionID of a TariCompletedTransaction +unsigned long long completed_transaction_get_transaction_id(struct TariCompletedTransaction *transaction,int* error_out); + +// Gets the timestamp of a TariCompletedTransaction +unsigned long long completed_transaction_get_timestamp(struct TariCompletedTransaction *transaction,int* error_out); + +// Frees memory for a TariCompletedTransaction +void completed_transaction_destroy(struct TariCompletedTransaction *transaction); + +/// -------------------------------- CompletedTransactions ------------------------------------------------------ /// + +// Gets number of elements in TariCompletedTransactions +unsigned int completed_transactions_get_length(struct TariCompletedTransactions *transactions,int* error_out); + +// Gets a TariCompletedTransaction from a TariCompletedTransactions at position +struct TariCompletedTransaction *completed_transactions_get_at(struct TariCompletedTransactions *transactions, unsigned int position,int* error_out); + +// Frees memory for a TariCompletedTransactions +void completed_transactions_destroy(struct TariCompletedTransactions *transactions); + +/// -------------------------------- OutboundTransaction ------------------------------------------------------ /// + +// Gets the TransactionId of a TariPendingOutboundTransaction +unsigned long long pending_outbound_transaction_get_transaction_id(struct TariPendingOutboundTransaction *transaction,int* error_out); + +// Gets the destination TariPublicKey of a TariPendingOutboundTransaction +struct TariPublicKey *pending_outbound_transaction_get_destination_public_key(struct TariPendingOutboundTransaction *transaction,int* error_out); + +// Gets the amount of a TariPendingOutboundTransaction +unsigned long long pending_outbound_transaction_get_amount(struct TariPendingOutboundTransaction *transaction,int* error_out); + +// Gets the fee of a TariPendingOutboundTransaction +unsigned long long pending_outbound_transaction_get_fee(struct TariPendingOutboundTransaction *transaction,int* error_out); + +// Gets the message of a TariPendingOutboundTransaction +const char *pending_outbound_transaction_get_message(struct TariPendingOutboundTransaction *transaction,int* error_out); + +// Gets the timestamp of a TariPendingOutboundTransaction +unsigned long long pending_outbound_transaction_get_timestamp(struct TariPendingOutboundTransaction *transaction,int* error_out); + +// Frees memory for a TariPendingOutboundTactions +void pending_outbound_transaction_destroy(struct TariPendingOutboundTransaction *transaction); + +/// -------------------------------- OutboundTransactions ------------------------------------------------------ /// + +// Gets the number of elements in a TariPendingOutboundTactions +unsigned int pending_outbound_transactions_get_length(struct TariPendingOutboundTransactions *transactions,int* error_out); + +// Gets a TariPendingOutboundTransaction of a TariPendingOutboundTransactions at position +struct TariPendingOutboundTransaction *pending_outbound_transactions_get_at(struct TariPendingOutboundTransactions *transactions, unsigned int position,int* error_out); + +// Frees memory of a TariPendingOutboundTransactions +void pending_outbound_transactions_destroy(struct TariPendingOutboundTransactions *transactions); + +/// -------------------------------- InboundTransaction ------------------------------------------------------ /// + +// Gets the TransactionId of a TariPendingInboundTransaction +unsigned long long pending_inbound_transaction_get_transaction_id(struct TariPendingInboundTransaction *transaction,int* error_out); + +// Gets the source TariPublicKey of a TariPendingInboundTransaction +struct TariPublicKey *pending_inbound_transaction_get_source_public_key(struct TariPendingInboundTransaction *transaction,int* error_out); + +// Gets the message of a TariPendingInboundTransaction +const char *pending_inbound_transaction_get_message(struct TariPendingInboundTransaction *transaction,int* error_out); + +// Gets the amount of a TariPendingInboundTransaction +unsigned long long pending_inbound_transaction_get_amount(struct TariPendingInboundTransaction *transaction,int* error_out); + +// Gets the timestamp of a TariPendingInboundTransaction +unsigned long long pending_inbound_transaction_get_timestamp(struct TariPendingInboundTransaction *transaction,int* error_out); + +// Frees memory for a TariPendingInboundTransaction +void pending_inbound_transaction_destroy(struct TariPendingInboundTransaction *transaction); + +/// -------------------------------- InboundTransactions ------------------------------------------------------ /// + +// Gets the number of elements in a TariPendingInboundTransactions +unsigned int pending_inbound_transactions_get_length(struct TariPendingInboundTransactions *transactions,int* error_out); + +// Gets a TariPendingInboundTransaction of a TariPendingInboundTransactions at position +struct TariPendingInboundTransaction *pending_inbound_transactions_get_at(struct TariPendingInboundTransactions *transactions, unsigned int position,int* error_out); + +// Frees memory of a TariPendingInboundTransaction +void pending_inbound_transactions_destroy(struct TariPendingInboundTransactions *transactions); + +/// -------------------------------- TariCommsConfig ----------------------------------------------- /// +// Creates a TariCommsConfig +struct TariCommsConfig *comms_config_create(char *public_address, + struct TariTransportType *transport, + char *database_name, + char *datastore_path, + struct TariPrivateKey *secret_key, + int* error_out); + +// Frees memory for a TariCommsConfig +void comms_config_destroy(struct TariCommsConfig *wc); + +/// -------------------------------- TariWallet ----------------------------------------------- // + +// Creates a TariWallet +struct TariWallet *wallet_create(struct TariWalletConfig *config, + char *log_path, + void (*callback_received_transaction)(struct TariPendingInboundTransaction*), + void (*callback_received_transaction_reply)(struct TariCompletedTransaction*), + void (*callback_received_finalized_transaction)(struct TariCompletedTransaction*), + void (*callback_transaction_broadcast)(struct TariCompletedTransaction*), + void (*callback_transaction_mined)(struct TariCompletedTransaction*), + void (*callback_discovery_process_complete)(unsigned long long, bool), + int* error_out); + +// Signs a message +char* wallet_sign_message(struct TariWallet *wallet, const char* msg, int* error_out); + +// Verifies signature for a signed message +bool wallet_verify_message_signature(struct TariWallet *wallet, struct TariPublicKey *public_key, const char* hex_sig_nonce, const char* msg, int* error_out); + +/// Generates test data +bool wallet_test_generate_data(struct TariWallet *wallet, char *datastore_path,int* error_out); + +// Adds a base node peer to the TariWallet +bool wallet_add_base_node_peer(struct TariWallet *wallet, struct TariPublicKey *public_key, const char *address,int* error_out); + +// Upserts a TariContact to the TariWallet, if the contact does not exist it is inserted and if it does the alias is updated +bool wallet_upsert_contact(struct TariWallet *wallet, struct TariContact *contact,int* error_out); + +// Removes a TariContact form the TariWallet +bool wallet_remove_contact(struct TariWallet *wallet, struct TariContact *contact,int* error_out); + +// Gets the available balance from a TariWallet +unsigned long long wallet_get_available_balance(struct TariWallet *wallet,int* error_out); + +// Gets the incoming balance from a TariWallet +unsigned long long wallet_get_pending_incoming_balance(struct TariWallet *wallet,int* error_out); + +// Gets the outgoing balance from a TariWallet +unsigned long long wallet_get_pending_outgoing_balance(struct TariWallet *wallet,int* error_out); + +// Sends a TariPendingOutboundTransaction +bool wallet_send_transaction(struct TariWallet *wallet, struct TariPublicKey *destination, unsigned long long amount, unsigned long long fee_per_gram,const char *message,int* error_out); + +// Get the TariContacts from a TariWallet +struct TariContacts *wallet_get_contacts(struct TariWallet *wallet,int* error_out); + +// Get the TariCompletedTransactions from a TariWallet +struct TariCompletedTransactions *wallet_get_completed_transactions(struct TariWallet *wallet,int* error_out); + +// Get the TariPendingOutboundTransactions from a TariWallet +struct TariPendingOutboundTransactions *wallet_get_pending_outbound_transactions(struct TariWallet *wallet,int* error_out); + +// Get the TariPublicKey from a TariCommsConfig +struct TariPublicKey *wallet_get_public_key(struct TariWallet *wallet,int* error_out); + +// Get the TariPendingInboundTransactions from a TariWallet +struct TariPendingInboundTransactions *wallet_get_pending_inbound_transactions(struct TariWallet *wallet,int* error_out); + +// Get the TariCompletedTransaction from a TariWallet by its TransactionId +struct TariCompletedTransaction *wallet_get_completed_transaction_by_id(struct TariWallet *wallet, unsigned long long transaction_id,int* error_out); + +// Get the TariPendingOutboundTransaction from a TariWallet by its TransactionId +struct TariPendingOutboundTransaction *wallet_get_pending_outbound_transaction_by_id(struct TariWallet *wallet, unsigned long long transaction_id,int* error_out); + +// Get the TariPendingInboundTransaction from a TariWallet by its TransactionId +struct TariPendingInboundTransaction *wallet_get_pending_inbound_transaction_by_id(struct TariWallet *wallet, unsigned long long transaction_id,int* error_out); + +// Simulates completion of a TariPendingOutboundTransaction +bool wallet_test_complete_sent_transaction(struct TariWallet *wallet, struct TariPendingOutboundTransaction *tx,int* error_out); + +// Checks if a TariCompletedTransaction was originally a TariPendingOutboundTransaction, +// i.e the transaction was originally sent from the wallet +bool wallet_is_completed_transaction_outbound(struct TariWallet *wallet, struct TariCompletedTransaction *tx,int* error_out); + +// Import a UTXO into the wallet. This will add a spendable UTXO and create a faux completed transaction to record the +// event. +unsigned long long wallet_import_utxo(struct TariWallet *wallet, unsigned long long amount, struct TariPrivateKey *spending_key, struct TariPublicKey *source_public_key, const char *message, int* error_out); + +// This function will tell the wallet to query the set base node to confirm the status of wallet data. +bool wallet_sync_with_base_node(struct TariWallet *wallet, int* error_out); + +// Simulates the completion of a broadcasted TariPendingInboundTransaction +bool wallet_test_broadcast_transaction(struct TariWallet *wallet, struct TariCompletedTransaction *tx, int* error_out); + +// Simulates receiving the finalized version of a TariPendingInboundTransaction +bool wallet_test_finalize_received_transaction(struct TariWallet *wallet, struct TariPendingInboundTransaction *tx, int* error_out); + +// Simulates a TariCompletedTransaction that has been mined +bool wallet_test_mine_transaction(struct TariWallet *wallet, struct TariCompletedTransaction *tx, int* error_out); + +// Simulates a TariPendingInboundtransaction being received +bool wallet_test_receive_transaction(struct TariWallet *wallet,int* error_out); + +// Frees memory for a TariWallet +void wallet_destroy(struct TariWallet *wallet); + +#ifdef __cplusplus +} +#endif + +#endif /* wallet_ffi_h */ diff --git a/common/Cargo.toml b/common/Cargo.toml index fa7d3805c8..ba7d8d9c36 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -6,16 +6,20 @@ repository = "https://github.com/tari-project/tari" homepage = "https://tari.com" readme = "README.md" license = "BSD-3-Clause" -version = "0.0.5" +version = "0.0.9" edition = "2018" [dependencies] +clap = "2.33.0" +config = { version = "0.9.3" } dirs = "2.0" +get_if_addrs = "0.5.3" log = "0.4.8" log4rs = "0.8.3" -config = { version = "0.9.3" } -clap = "2.33.0" +multiaddr={package="parity-multiaddr", version = "0.7.2"} +prost-build = "0.6.1" +sha2 = "0.8.0" [dev-dependencies] -tempdir = "0.3.7" \ No newline at end of file +tempdir = "0.3.7" diff --git a/common/logging/log4rs-sample.yml b/common/logging/log4rs-sample.yml index 4b1696b15a..31576f932d 100644 --- a/common/logging/log4rs-sample.yml +++ b/common/logging/log4rs-sample.yml @@ -38,7 +38,7 @@ root: loggers: # Set the maximum console output to "error" - stdout: + base_node: level: error appenders: - stdout diff --git a/common/src/configuration.rs b/common/src/configuration.rs index 723276731b..67ee9eb728 100644 --- a/common/src/configuration.rs +++ b/common/src/configuration.rs @@ -24,12 +24,16 @@ use crate::{dir_utils::default_subdir, ConfigBootstrap}; use config::{Config, Environment}; use log::*; +use multiaddr::{Multiaddr, Protocol}; use std::{ convert::TryFrom, env, error::Error, fmt::{Display, Formatter, Result as FormatResult}, + net::IpAddr, + num::NonZeroU16, path::{Path, PathBuf}, + str::FromStr, }; const LOG_TARGET: &str = "common::config"; @@ -47,7 +51,7 @@ pub fn load_configuration(bootstrap: &ConfigBootstrap) -> Result let filename = bootstrap .config .to_str() - .ok_or("Invalid config file path".to_string())?; + .ok_or_else(|| "Invalid config file path".to_string())?; let config_file = config::File::with_name(filename); match cfg.merge(config_file) { Ok(_) => { @@ -73,7 +77,7 @@ pub fn install_default_config_file(path: &Path) -> Result { #[derive(Clone, Debug, PartialEq)] pub enum Network { MainNet, - TestNet, + Rincewind, } impl TryFrom for Network { @@ -81,8 +85,8 @@ impl TryFrom for Network { fn try_from(value: String) -> Result { let val = value.to_lowercase(); - if &val == "testnet" { - Ok(Self::TestNet) + if &val == "rincewind" { + Ok(Self::Rincewind) } else if &val == "mainnet" { Ok(Self::MainNet) } else { @@ -97,32 +101,170 @@ impl TryFrom for Network { impl Display for Network { fn fmt(&self, f: &mut Formatter) -> FormatResult { let msg = match self { - Self::MainNet => "MainNet", - Self::TestNet => "TestNet", + Self::MainNet => "mainnet", + Self::Rincewind => "rincewind", }; f.write_str(msg) } } +//------------------------------------------- ConfigExtractor trait ------------------------------------------// +/// Extract parts of the global Config file into custom configuration objects that are more specific and localised. +/// The expected use case for this is to use `load_configuration` to load the global configuration file into a Config +/// object. This is then used to generate other, localised configuration objects, for example, `MempoolConfig` etc. +/// +/// # Example +/// +/// ```edition2018 +/// # use tari_common::*; +/// # use config::Config; +/// struct MyConf { +/// foo: usize, +/// } +/// +/// impl ConfigExtractor for MyConf { +/// fn set_default(cfg: &mut Config) { +/// cfg.set_default("main.foo", 5); +/// cfg.set_default("test.foo", 6); +/// } +/// +/// fn extract_configuration(cfg: &Config, network: Network) -> Result { +/// let key = match network { +/// Network::MainNet => "main.foo", +/// Network::Rincewind => "test.foo", +/// }; +/// let foo = cfg.get_int(key).map_err(|e| ConfigurationError::new(&key, &e.to_string()))? as usize; +/// Ok(MyConf { foo }) +/// } +/// } +/// ``` +pub trait ConfigExtractor { + /// Provides the default values for the Config object. This is used before `load_configuration` and ensures that + /// all config parameters have at least the default value set. + fn set_default(cfg: &mut Config); + /// After `load_configuration` has been called, you can construct a specific configuration object by calling + /// `extract_configuration` and it will create the object using values from the config file / environment variables + fn extract_configuration(cfg: &Config, network: Network) -> Result + where Self: Sized; +} //--------------------------------------------- Database type ------------------------------------------// - +#[derive(Debug)] pub enum DatabaseType { LMDB(PathBuf), Memory, } +//--------------------------------------------- Network Transport ------------------------------------------// +#[derive(Debug, Clone)] +pub enum TorControlAuthentication { + None, + Password(String), +} + +fn parse_key_value(s: &str, split_chr: char) -> (String, Option<&str>) { + let mut parts = s.splitn(2, split_chr); + ( + parts + .next() + .expect("splitn always emits at least one part") + .to_lowercase(), + parts.next(), + ) +} + +impl FromStr for TorControlAuthentication { + type Err = String; + + fn from_str(s: &str) -> Result { + let (auth_type, maybe_value) = parse_key_value(s, '='); + match auth_type.as_str() { + "none" => Ok(TorControlAuthentication::None), + "password" => { + let password = maybe_value.ok_or_else(|| { + "Invalid format for 'password' tor authentication type. It should be in the format \ + 'password=xxxxxx'." + .to_string() + })?; + Ok(TorControlAuthentication::Password(password.to_string())) + }, + s => Err(format!("Invalid tor auth type '{}'", s)), + } + } +} + +#[derive(Debug, Clone)] +pub enum SocksAuthentication { + None, + UsernamePassword(String, String), +} + +impl FromStr for SocksAuthentication { + type Err = String; + + fn from_str(s: &str) -> Result { + let (auth_type, maybe_value) = parse_key_value(s, '='); + match auth_type.as_str() { + "none" => Ok(SocksAuthentication::None), + "username_password" => { + let (username, password) = maybe_value + .and_then(|value| { + let (un, pwd) = parse_key_value(value, ':'); + // If pwd is None, return None + pwd.map(|p| (un, p)) + }) + .ok_or_else(|| { + "Invalid format for 'username-password' socks authentication type. It should be in the format \ + 'username_password=my_username:xxxxxx'." + .to_string() + })?; + Ok(SocksAuthentication::UsernamePassword(username, password.to_string())) + }, + s => Err(format!("Invalid tor auth type '{}'", s)), + } + } +} + +#[derive(Debug, Clone)] +pub enum CommsTransport { + /// Use TCP to join the Tari network. This transport can only communicate with TCP/IP addresses, so peers with + /// e.g. tor onion addresses will not be contactable. + Tcp { listener_address: Multiaddr }, + /// Configures the node to run over a tor hidden service using the Tor proxy. This transport recognises ip/tcp, + /// onion v2, onion v3 and dns addresses. + TorHiddenService { + /// The address of the control server + control_server_address: Multiaddr, + /// The address used to receive proxied traffic from the tor proxy to the Tari node. This port must be + /// available + forward_address: Multiaddr, + auth: TorControlAuthentication, + onion_port: NonZeroU16, + }, + /// Use a SOCKS5 proxy transport. This transport recognises any addresses supported by the proxy. + Socks5 { + proxy_address: Multiaddr, + listener_address: Multiaddr, + auth: SocksAuthentication, + }, +} + //------------------------------------- Main Configuration Struct --------------------------------------// +#[derive(Debug)] pub struct GlobalConfig { pub network: Network, + pub comms_transport: CommsTransport, pub data_dir: PathBuf, pub db_type: DatabaseType, pub core_threads: usize, pub blocking_threads: usize, pub identity_file: PathBuf, - pub address: String, + pub public_address: Multiaddr, pub peer_seeds: Vec, pub peer_db_path: String, + pub enable_mining: bool, + pub wallet_file: String, + pub tor_identity_file: String, } impl GlobalConfig { @@ -146,7 +288,7 @@ pub fn sub_dir(data_dir: &Path, sub_dir: &str) -> Result Result { @@ -185,12 +327,25 @@ fn convert_node_config(network: Network, cfg: Config) -> Result() + .map_err(|e| ConfigurationError::new(&key, &e.to_string())) + })?; + // Peer seeds let key = config_string(&net_str, "peer_seeds"); let peer_seeds = cfg @@ -200,19 +355,109 @@ fn convert_node_config(network: Network, cfg: Config) -> Result Result { + let get_conf_str = |key| { + cfg.get_str(key) + .map_err(|err| ConfigurationError::new(key, &err.to_string())) + }; + + let get_conf_multiaddr = |key| { + let path_str = get_conf_str(key)?; + path_str + .parse::() + .map_err(|err| ConfigurationError::new(key, &err.to_string())) + }; + + let transport_key = config_string(network, "transport"); + let transport = get_conf_str(&transport_key)?; + + match transport.to_lowercase().as_str() { + "tcp" => { + let key = config_string(network, "tcp_listener_address"); + let listener_address = get_conf_multiaddr(&key)?; + + Ok(CommsTransport::Tcp { listener_address }) + }, + "tor" => { + let key = config_string(network, "tor_control_address"); + let control_server_address = get_conf_multiaddr(&key)?; + + let key = config_string(network, "tor_control_auth"); + let auth_str = get_conf_str(&key)?; + let auth = auth_str + .parse() + .map_err(|err: String| ConfigurationError::new(&key, &err))?; + + let key = config_string(network, "tor_forward_address"); + let forward_address = get_conf_multiaddr(&key)?; + let key = config_string(network, "tor_onion_port"); + let onion_port = cfg + .get::(&key) + .map_err(|err| ConfigurationError::new(&key, &err.to_string()))?; + + Ok(CommsTransport::TorHiddenService { + control_server_address, + auth, + forward_address, + onion_port, + }) + }, + "socks5" => { + let key = config_string(network, "socks5_proxy_address"); + let proxy_address = get_conf_multiaddr(&key)?; + + let key = config_string(network, "socks5_listener_address"); + let auth_str = get_conf_str(&key)?; + let auth = auth_str + .parse() + .map_err(|err: String| ConfigurationError::new(&key, &err))?; + + let key = config_string(network, "socks5_listener_address"); + let listener_address = get_conf_multiaddr(&key)?; + + Ok(CommsTransport::Socks5 { + proxy_address, + listener_address, + auth, + }) + }, + t => Err(ConfigurationError::new( + &transport_key, + &format!("Invalid transport type '{}'", t), + )), + } +} + fn config_string(network: &str, key: &str) -> String { format!("base_node.{}.{}", network, key) } @@ -225,6 +470,8 @@ fn config_string(network: &str, key: &str) -> String { /// These will typically be overridden by userland settings in envars, the config file, or the command line. pub fn default_config() -> Config { let mut cfg = Config::new(); + let local_ip_addr = get_local_ip().unwrap_or_else(|| "/ip4/1.2.3.4".parse().unwrap()); + // Common settings cfg.set_default("common.message_cache_size", 10).unwrap(); cfg.set_default("common.message_cache_ttl", 1440).unwrap(); @@ -255,34 +502,112 @@ pub fn default_config() -> Config { default_subdir("mainnet/node_id.json"), ) .unwrap(); - cfg.set_default("base_node.mainnet.address", "http://localhost:18089") - .unwrap(); + cfg.set_default( + "base_node.mainnet.tor_identity_file", + default_subdir("mainnet/tor.json"), + ) + .unwrap(); + cfg.set_default( + "base_node.mainnet.public_address", + format!("{}/tcp/18041", local_ip_addr), + ) + .unwrap(); cfg.set_default("base_node.mainnet.grpc_enabled", false).unwrap(); cfg.set_default("base_node.mainnet.grpc_address", "tcp://127.0.0.1:18041") .unwrap(); + cfg.set_default("base_node.mainnet.enable_mining", false).unwrap(); - // Testnet base node defaults - cfg.set_default("base_node.testnet.db_type", "lmdb").unwrap(); - cfg.set_default("base_node.testnet.peer_seeds", Vec::::new()) + // Rincewind base node defaults + cfg.set_default("base_node.rincewind.db_type", "lmdb").unwrap(); + cfg.set_default("base_node.rincewind.peer_seeds", Vec::::new()) .unwrap(); - cfg.set_default("base_node.testnet.blocking_threads", 4).unwrap(); - cfg.set_default("base_node.testnet.core_threads", 4).unwrap(); - cfg.set_default("base_node.testnet.data_dir", default_subdir("testnet/")) + cfg.set_default("base_node.rincewind.blocking_threads", 4).unwrap(); + cfg.set_default("base_node.rincewind.core_threads", 4).unwrap(); + cfg.set_default("base_node.rincewind.data_dir", default_subdir("rincewind/")) .unwrap(); cfg.set_default( - "base_node.testnet.identity_file", - default_subdir("testnet/node_id.json"), + "base_node.rincewind.tor_identity_file", + default_subdir("rincewind/tor.json"), ) .unwrap(); - cfg.set_default("base_node.testnet.address", "http://localhost:18189") - .unwrap(); - cfg.set_default("base_node.testnet.grpc_enabled", false).unwrap(); - cfg.set_default("base_node.testnet.grpc_address", "tcp://127.0.0.1:18141") + cfg.set_default( + "base_node.rincewind.identity_file", + default_subdir("rincewind/node_id.json"), + ) + .unwrap(); + cfg.set_default( + "base_node.rincewind.public_address", + format!("{}/tcp/18141", local_ip_addr), + ) + .unwrap(); + cfg.set_default("base_node.rincewind.grpc_enabled", false).unwrap(); + cfg.set_default("base_node.rincewind.grpc_address", "tcp://127.0.0.1:18141") .unwrap(); + cfg.set_default("base_node.rincewind.enable_mining", false).unwrap(); + + set_transport_defaults(&mut cfg); cfg } +fn set_transport_defaults(cfg: &mut Config) { + // Mainnet + // Default transport for mainnet is tcp + cfg.set_default("base_node.mainnet.transport", "tcp").unwrap(); + cfg.set_default("base_node.mainnet.tcp_listener_address", "/ip4/0.0.0.0/tcp/18089") + .unwrap(); + + cfg.set_default("base_node.mainnet.tor_control_address", "/ip4/127.0.0.1/tcp/9051") + .unwrap(); + cfg.set_default("base_node.mainnet.tor_control_auth", "none").unwrap(); + cfg.set_default("base_node.mainnet.tor_forward_address", "/ip4/127.0.0.1/tcp/18141") + .unwrap(); + + cfg.set_default("base_node.mainnet.socks5_proxy_address", "/ip4/0.0.0.0/tcp/9050") + .unwrap(); + cfg.set_default("base_node.mainnet.socks5_listener_address", "/ip4/0.0.0.0/tcp/18099") + .unwrap(); + cfg.set_default("base_node.mainnet.socks5_auth", "none").unwrap(); + + // rincewind + // Default transport for rincewind is tcp + cfg.set_default("base_node.rincewind.transport", "tcp").unwrap(); + cfg.set_default("base_node.rincewind.tcp_listener_address", "/ip4/0.0.0.0/tcp/18189") + .unwrap(); + + cfg.set_default("base_node.rincewind.tor_control_address", "/ip4/127.0.0.1/tcp/9051") + .unwrap(); + cfg.set_default("base_node.rincewind.tor_control_auth", "none").unwrap(); + cfg.set_default("base_node.rincewind.tor_forward_address", "/ip4/127.0.0.1/tcp/18041") + .unwrap(); + + cfg.set_default("base_node.rincewind.socks5_proxy_address", "/ip4/0.0.0.0/tcp/9150") + .unwrap(); + cfg.set_default("base_node.rincewind.socks5_listener_address", "/ip4/0.0.0.0/tcp/18199") + .unwrap(); + cfg.set_default("base_node.rincewind.socks5_auth", "none").unwrap(); +} + +fn get_local_ip() -> Option { + get_if_addrs::get_if_addrs().ok().and_then(|if_addrs| { + if_addrs + .into_iter() + .find(|if_addr| !if_addr.is_loopback()) + .map(|if_addr| { + let mut addr = Multiaddr::empty(); + match if_addr.ip() { + IpAddr::V4(ip) => { + addr.push(Protocol::Ip4(ip)); + }, + IpAddr::V6(ip) => { + addr.push(Protocol::Ip6(ip)); + }, + } + addr + }) + }) +} + //------------------------------------- Configuration errors --------------------------------------// #[derive(Debug)] diff --git a/common/src/dir_utils.rs b/common/src/dir_utils.rs index 2f8ac857f2..906bfc4bab 100644 --- a/common/src/dir_utils.rs +++ b/common/src/dir_utils.rs @@ -24,7 +24,7 @@ use std::{io::ErrorKind, path::PathBuf}; /// Create the default data directory (`~/.tari` on OSx and Linux, for example) if it doesn't already exist pub fn create_data_directory() -> Result<(), std::io::Error> { - let mut home = dirs::home_dir().ok_or(std::io::Error::from(ErrorKind::NotFound))?; + let mut home = dirs::home_dir().ok_or_else(|| std::io::Error::from(ErrorKind::NotFound))?; home.push(".tari"); if !home.exists() { std::fs::create_dir(home) @@ -46,7 +46,7 @@ pub fn default_subdir(path: &str) -> String { } pub fn default_path(filename: &str) -> PathBuf { - let mut home = dirs::home_dir().unwrap_or(PathBuf::from(".")); + let mut home = dirs::home_dir().unwrap_or_else(|| PathBuf::from(".")); home.push(".tari"); home.push(filename); home diff --git a/common/src/lib.rs b/common/src/lib.rs index 00783db310..2ce1fad977 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -54,18 +54,24 @@ mod configuration; #[macro_use] mod logging; +pub mod protobuf_build; + pub mod dir_utils; pub use configuration::{ default_config, install_default_config_file, load_configuration, + CommsTransport, + ConfigExtractor, ConfigurationError, DatabaseType, GlobalConfig, Network, + SocksAuthentication, + TorControlAuthentication, }; pub use logging::initialize_logging; - +use std::io; pub const DEFAULT_CONFIG: &str = "config.toml"; pub const DEFAULT_LOG_CONFIG: &str = "log4rs.yml"; @@ -92,25 +98,48 @@ pub fn bootstrap_config_from_cli(matches: &ArgMatches) -> ConfigBootstrap { let config = matches .value_of("config") .map(PathBuf::from) - .unwrap_or(dir_utils::default_path(DEFAULT_CONFIG)); + .unwrap_or_else(|| dir_utils::default_path(DEFAULT_CONFIG)); let log_config = matches.value_of("log_config").map(PathBuf::from); let log_config = logging::get_log_configuration_path(log_config); - if !config.exists() && matches.is_present("init") { - println!("Installing new config file at {}", config.to_str().unwrap_or("[??]")); - install_configuration(&config, configuration::install_default_config_file); + if !config.exists() { + let install = if !matches.is_present("init") { + prompt("Config file does not exist. Would you like to create a new one (Y/n)?") + } else { + true + }; + + if install { + println!("Installing new config file at {}", config.to_str().unwrap_or("[??]")); + install_configuration(&config, configuration::install_default_config_file); + } } - if !log_config.exists() && matches.is_present("init") { - println!( - "Installing new logfile configuration at {}", - log_config.to_str().unwrap_or("[??]") - ); - install_configuration(&log_config, logging::install_default_logfile_config); + if !log_config.exists() { + let install = if !matches.is_present("init") { + prompt("Logging configuration file does not exist. Would you like to create a new one (Y/n)?") + } else { + true + }; + if install { + println!( + "Installing new logfile configuration at {}", + log_config.to_str().unwrap_or("[??]") + ); + install_configuration(&log_config, logging::install_default_logfile_config); + } } ConfigBootstrap { config, log_config } } +fn prompt(question: &str) -> bool { + println!("{}", question); + let mut input = "".to_string(); + io::stdin().read_line(&mut input).unwrap(); + let input = input.trim().to_lowercase(); + input == "y" || input.len() == 0 +} + pub fn install_configuration(path: &Path, installer: F) where F: Fn(&Path) -> Result { if let Err(e) = installer(path) { diff --git a/common/src/logging.rs b/common/src/logging.rs index b7c1be9fc9..efbeb55fe1 100644 --- a/common/src/logging.rs +++ b/common/src/logging.rs @@ -55,7 +55,7 @@ pub fn initialize_logging(config_file: &Path) -> bool { "Initializing logging according to {:?}", config_file.to_str().unwrap_or("[??]") ); - if let Err(e) = log4rs::init_file(config_file.clone(), Default::default()) { + if let Err(e) = log4rs::init_file(config_file, Default::default()) { println!("We couldn't load a logging configuration file. {}", e.to_string()); return false; } diff --git a/infrastructure/protobuf_build/src/lib.rs b/common/src/protobuf_build.rs similarity index 96% rename from infrastructure/protobuf_build/src/lib.rs rename to common/src/protobuf_build.rs index ee3ae4b75d..2027da3633 100644 --- a/infrastructure/protobuf_build/src/lib.rs +++ b/common/src/protobuf_build.rs @@ -50,6 +50,7 @@ fn walk_files>(search_path: P, search_ext: &str) -> Vec protos } +#[derive(Default)] pub struct ProtoCompiler { out_dir: Option, type_attributes: HashMap<&'static str, &'static str>, @@ -87,13 +88,13 @@ impl ProtoCompiler { pub fn proto_paths>(&mut self, proto_paths: &[P]) -> &mut Self { self.proto_paths - .extend(proto_paths.into_iter().map(|p| p.as_ref().to_path_buf())); + .extend(proto_paths.iter().map(|p| p.as_ref().to_path_buf())); self } pub fn include_paths>(&mut self, include_paths: &[P]) -> &mut Self { self.include_paths - .extend(include_paths.into_iter().map(|p| p.as_ref().to_path_buf())); + .extend(include_paths.iter().map(|p| p.as_ref().to_path_buf())); self } diff --git a/comms/Cargo.toml b/comms/Cargo.toml index 361003347f..ea0feafdd6 100644 --- a/comms/Cargo.toml +++ b/comms/Cargo.toml @@ -1,46 +1,50 @@ [package] name = "tari_comms" description = "A peer-to-peer messaging system" +authors = ["The Tari Development Community"] repository = "https://github.com/tari-project/tari" homepage = "https://tari.com" readme = "README.md" license = "BSD-3-Clause" -version = "0.0.5" +version = "0.0.9" edition = "2018" [dependencies] -tari_crypto = { version="^0.0", path = "../infrastructure/crypto" } -tari_utilities = { version="^0.0", path = "../infrastructure/tari_util" } +tari_crypto = { version = "^0.3" } tari_storage = { version="^0.0", path = "../infrastructure/storage" } +tari_shutdown = { version="^0.0", path = "../infrastructure/shutdown" } -bitflags ="1.0.4" -bus_queue = { git = "https://github.com/tari-project/bus-queue.git"} +bitflags = "1.0.4" +blake2 = "0.8.1" +bytes = "0.5.x" chrono = { version = "0.4.6", features = ["serde"] } clear_on_drop = "0.2.3" -crossbeam-channel = "0.3.9" -crossbeam-deque = "0.7.1" +data-encoding = "2.2.0" derive-error = "0.0.4" digest = "0.8.0" -futures = { version = "=0.3.0-alpha.18", package = "futures-preview", features = ["async-await", "nightly", "io-compat", "compat"] } +futures = { version = "^0.3", features = ["async-await"]} lazy_static = "1.3.0" lmdb-zero = "0.4.4" log = { version = "0.4.0", features = ["std"] } -rand = "0.5.5" +multiaddr = {version = "0.7.2", package = "parity-multiaddr"} +nom = {version = "5.1.0", features=["std"], default-features=false} +prost = "0.6.1" +rand = "0.7.2" serde = "1.0.90" serde_derive = "1.0.90" -time = "0.1.42" -tokio = "0.1" -ttl_cache = "0.5.1" -zmq = "0.9.1" +snow = {version="0.6.2", features=["default-resolver"]} +tokio = {version="^0.2", features=["blocking", "tcp", "stream", "dns", "sync", "stream", "signal"]} +tokio-util = {version="0.2.0", features=["codec"]} +tower= "0.3.1" +yamux = {version="0.4.3"} [dev-dependencies] -criterion = "0.2" -rand = "0.5.5" -simple_logger = "1.2.0" +tari_test_utils = {version="0.0.9", path="../infrastructure/test_utils"} + +env_logger = "0.7.0" serde_json = "1.0.39" -tari_common = { path = "../common"} -tokio-mock-task = "0.1.1" +tokio-macros = "0.2.3" +tempdir = "0.3.7" -[[bench]] -name = "benches_main" -harness = false +[build-dependencies] +tari_common = { version = "^0.0", path="../common"} diff --git a/comms/benches/benches_main.rs b/comms/benches/benches_main.rs deleted file mode 100644 index 306dc4124b..0000000000 --- a/comms/benches/benches_main.rs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#[macro_use] -extern crate criterion; -#[macro_use] -extern crate lazy_static; - -mod connection; - -criterion_main!(connection::connection::benches, connection::peer_connection::benches,); diff --git a/comms/benches/connection/connection.rs b/comms/benches/connection/connection.rs deleted file mode 100644 index d734fbcf65..0000000000 --- a/comms/benches/connection/connection.rs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use criterion::Criterion; - -use std::{iter::repeat, time::Duration}; -use tari_comms::connection::{Connection, Direction, InprocAddress, ZmqContext}; - -/// Connection benchmark -/// -/// Benchmark a message being sent and received between two connections -fn bench_connection(c: &mut Criterion) { - let ctx = ZmqContext::new(); - let addr = InprocAddress::random(); - - let bytes = repeat(88u8).take(1 * 1024 * 1024 /* 10Mb */).collect::>(); - let receiver = Connection::new(&ctx, Direction::Inbound) - .set_receive_hwm(0) - .establish(&addr) - .unwrap(); - - let sender = Connection::new(&ctx, Direction::Outbound) - .set_send_hwm(0) - .establish(&addr) - .unwrap(); - - c.bench_function("connection: send/recv", move |b| { - b.iter(|| { - sender.send(&[bytes.as_slice()]).unwrap(); - receiver.receive(100).unwrap(); - }); - }); -} - -criterion_group!( - name = benches; - config = Criterion::default().warm_up_time(Duration::from_millis(500)); - targets = bench_connection, -); diff --git a/comms/benches/connection/mod.rs b/comms/benches/connection/mod.rs deleted file mode 100644 index 8d96000d1c..0000000000 --- a/comms/benches/connection/mod.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -pub mod connection; -pub mod peer_connection; diff --git a/comms/benches/connection/peer_connection.rs b/comms/benches/connection/peer_connection.rs deleted file mode 100644 index 683b306a9d..0000000000 --- a/comms/benches/connection/peer_connection.rs +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use criterion::Criterion; - -use std::{ - iter::repeat, - sync::mpsc::{channel, sync_channel, Sender, SyncSender}, - thread, - time::Duration, -}; -use tari_comms::{ - connection::{ - peer_connection::PeerConnectionContext, - Connection, - Direction, - InprocAddress, - NetAddress, - PeerConnection, - PeerConnectionContextBuilder, - ZmqContext, - }, - message::FrameSet, -}; - -const BENCH_SOCKET_ADDRESS: &'static str = "127.0.0.1:9999"; - -/// Set the allocated stack size for each WorkerTask thread -const THREAD_STACK_SIZE: usize = 16 * 1024; // 16kb - -lazy_static! { - static ref DUMMY_DATA: FrameSet = vec![ - repeat(88u8).take(512 * 1024).collect::>(), - repeat(99u8).take(512 * 1024).collect::>(), - ]; // 1 MiB -} - -/// Task messages for the consumer thread -enum WorkerTask { - /// Receive a message - Receive, - /// Receive a message and then send it back - ReceiveSend, - /// Exit the loop (thread terminates) - Exit, -} - -/// Build a context for the connections to be used in the benchmark -fn build_context( - ctx: &ZmqContext, - dir: Direction, - addr: &NetAddress, - consumer_addr: &InprocAddress, -) -> PeerConnectionContext -{ - PeerConnectionContextBuilder::new() - .set_id("benchmark") - .set_direction(dir) - .set_max_msg_size(512 * 1024) - .set_address(addr.clone()) - .set_message_sink_address(consumer_addr.clone()) - .set_context(ctx) - .build() - .unwrap() -} - -/// Start a consumer thread. -/// This listens for control messages, execute the given task and signal when it's done (`signal` field) -fn start_message_sink_consumer( - ctx: &ZmqContext, - addr: &InprocAddress, - peer_conn: &PeerConnection, - signal: Sender<()>, -) -> SyncSender -{ - let ctx = ctx.clone(); - let addr = addr.clone(); - let peer_conn = peer_conn.clone(); - let (tx, rx) = sync_channel(2); - thread::Builder::new() - .name("peer-connection-consumer-thread".to_string()) - .stack_size(THREAD_STACK_SIZE) - .spawn(move || { - let conn = Connection::new(&ctx, Direction::Inbound).establish(&addr).unwrap(); - loop { - match rx.recv().unwrap() { - WorkerTask::Receive => { - conn.receive(1000).unwrap(); - signal.send(()).unwrap(); - }, - WorkerTask::ReceiveSend => { - let data = conn.receive(1000).unwrap(); - peer_conn.send(data).unwrap(); - signal.send(()).unwrap(); - }, - WorkerTask::Exit => break, - } - } - }); - tx -} - -/// Benchmark for PeerConnnection -/// -/// ## Setup Phase -/// -/// 1. Two peer connections (one inbound and one outbound) are "booted up" -/// 2. Consumer workers are started up and set to listen for messages from the peer connection -/// -/// ## Run phase -/// -/// 1. Peer 1's worker is told to go into ReceiveSend mode -/// 2. Peer 2's worker is told to go into Receive mode -/// 3. Peer 2 (outbound) sends DUMMY_DATA to peer 1 (inbound) -/// 4. Peer 1 receives the message and sends it back to Peer 2 and signals done -/// 5. Peer 2 receives the data and signals done -/// 6. Once both have completed their work, the next iteration can begin -/// -/// ## Teardown phase -/// -/// 1. Both consumer threads are given the Exit task -/// 2. All connections go out of scope (i.e. are dropped) -fn bench_peer_connection(c: &mut Criterion) { - // Setup - let ctx = ZmqContext::new(); - let addr = BENCH_SOCKET_ADDRESS.parse::().unwrap(); - let consumer1 = InprocAddress::random(); - let consumer2 = InprocAddress::random(); - - let p1_ctx = build_context(&ctx, Direction::Inbound, &addr, &consumer1); - let p2_ctx = build_context(&ctx, Direction::Outbound, &addr, &consumer2); - - // Start peer connections on either end - let mut p1 = PeerConnection::new(); - p1.start(p1_ctx).unwrap(); - - let mut p2 = PeerConnection::new(); - p2.start(p2_ctx).unwrap(); - - let (done1_tx, done1_rx) = channel(); - let (done2_tx, done2_rx) = channel(); - - // Start consumers - // These act as a threaded worker which consumes messages from peer connections - let signal1 = start_message_sink_consumer(&ctx, &consumer1, &p1, done1_tx); - let signal2 = start_message_sink_consumer(&ctx, &consumer2, &p2, done2_tx); - - // Duplicates which won't be moved into the bench function - let dup_signal1 = signal1.clone(); - let dup_signal2 = signal2.clone(); - - p1.wait_listening_or_failure(&Duration::from_millis(1000)).unwrap(); - p2.wait_connected_or_failure(&Duration::from_millis(1000)).unwrap(); - - c.bench_function("peer_connection: send/recv", move |b| { - // Benchmark - b.iter(|| { - // Signal p2 to receive and send back - signal1.send(WorkerTask::ReceiveSend).unwrap(); - // Signal p1 to receive - signal2.send(WorkerTask::Receive).unwrap(); - - // Send to p2 - p2.send(DUMMY_DATA.to_vec()).unwrap(); - - // Wait for done signals - done1_rx.recv().unwrap(); - done2_rx.recv().unwrap(); - }); - }); - - // Teardown - dup_signal1.send(WorkerTask::Exit).unwrap(); - dup_signal2.send(WorkerTask::Exit).unwrap(); -} - -criterion_group!( - name = benches; - config = Criterion::default().warm_up_time(Duration::from_millis(500)); - targets = bench_peer_connection, -); diff --git a/comms/build.rs b/comms/build.rs new file mode 100644 index 0000000000..521800df67 --- /dev/null +++ b/comms/build.rs @@ -0,0 +1,29 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +fn main() { + tari_common::protobuf_build::ProtoCompiler::new() + .proto_paths(&["src/proto"]) + .out_dir("src/proto") + .compile() + .unwrap(); +} diff --git a/comms/dht/Cargo.toml b/comms/dht/Cargo.toml new file mode 100644 index 0000000000..7f2502e8db --- /dev/null +++ b/comms/dht/Cargo.toml @@ -0,0 +1,56 @@ +[package] +name = "tari_comms_dht" +version = "0.0.9" +authors = ["The Tari Development Community"] +description = "Tari comms DHT module" +repository = "https://github.com/tari-project/tari" +homepage = "https://tari.com" +readme = "README.md" +license = "BSD-3-Clause" +edition = "2018" + +[features] +test-mocks = [] + +[dependencies] +tari_comms = { version = "^0.0", path = "../"} +tari_crypto = { version = "^0.3" } +tari_shutdown = { version = "^0.0", path = "../../infrastructure/shutdown"} + +bitflags = "1.2.0" +bytes = "0.4.12" +chrono = "0.4.9" +derive-error = "0.0.4" +digest = "0.8.1" +futures= {version= "^0.3.1"} +log = "0.4.8" +prost = "0.6.1" +prost-types = "0.6.1" +rand = "0.7.2" +serde = "1.0.90" +serde_derive = "1.0.90" +serde_repr = "0.1.5" +tokio = {version="0.2.10", features=["rt-threaded", "blocking"]} +tower= "0.3.0" +ttl_cache = "0.5.1" +# tower-filter dependencies +pin-project = "0.4" + +[dev-dependencies] +tari_test_utils = { version = "^0.0", path = "../../infrastructure/test_utils"} +tari_storage = { version = "^0.0", path = "../../infrastructure/storage"} + +futures-test = { version = "0.3.0-alpha.19", package = "futures-test-preview" } +lmdb-zero = "0.4.4" +tempdir = "0.3.7" +env_logger = "0.7.0" +tokio-macros = "0.2.3" + +# tower-filter dependencies +tower-test = { version = "^0.3" } +tokio-test = "^0.2" +tokio = "^0.2" +futures-util = "^0.3.1" + +[build-dependencies] +tari_common = { version = "^0.0", path="../../common"} diff --git a/comms/dht/README.md b/comms/dht/README.md new file mode 100644 index 0000000000..6307577dcd --- /dev/null +++ b/comms/dht/README.md @@ -0,0 +1,3 @@ +# Tari comms DHT module + +The Tari network messaging library. See CommsBuilder for more information on using this library. diff --git a/comms/dht/build.rs b/comms/dht/build.rs new file mode 100644 index 0000000000..c2ed565471 --- /dev/null +++ b/comms/dht/build.rs @@ -0,0 +1,33 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +fn main() { + tari_common::protobuf_build::ProtoCompiler::new() + .proto_paths(&["src/proto"]) + .out_dir("src/proto") + .compile() + .unwrap(); + println!("cargo:rerun-if-changed=src/proto/dht.proto"); + println!("cargo:rerun-if-changed=src/proto/envelope.proto"); + println!("cargo:rerun-if-changed=src/proto/message_header.proto"); + println!("cargo:rerun-if-changed=src/proto/store_forward.proto"); +} diff --git a/comms/dht/src/actor.rs b/comms/dht/src/actor.rs new file mode 100644 index 0000000000..ca36f57f56 --- /dev/null +++ b/comms/dht/src/actor.rs @@ -0,0 +1,603 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +//! Actor for DHT functionality. +//! +//! The DhtActor is responsible for sending a join request on startup +//! and furnishing [DhtRequest]s. +//! +//! [DhtRequest]: ./enum.DhtRequest.html + +use crate::{ + broadcast_strategy::BroadcastStrategy, + discovery::DhtDiscoveryError, + outbound::{OutboundMessageRequester, SendMessageParams}, + proto::{dht::JoinMessage, envelope::DhtMessageType, store_forward::StoredMessagesRequest}, + utils::hoist_nested_result, + DhtConfig, +}; +use bitflags::_core::fmt::Formatter; +use chrono::{DateTime, Utc}; +use derive_error::Error; +use futures::{ + channel::{mpsc, mpsc::SendError, oneshot}, + future, + future::BoxFuture, + stream::{Fuse, FuturesUnordered}, + FutureExt, + SinkExt, + StreamExt, +}; +use log::*; +use std::{fmt::Display, sync::Arc}; +use tari_comms::{ + peer_manager::{ + NodeId, + NodeIdentity, + Peer, + PeerFeatures, + PeerManager, + PeerManagerError, + PeerQuery, + PeerQuerySortBy, + }, + types::CommsPublicKey, +}; +use tari_crypto::tari_utilities::ByteArray; +use tari_shutdown::ShutdownSignal; +use tokio::task; +use ttl_cache::TtlCache; + +const LOG_TARGET: &str = "comms::dht::actor"; + +#[derive(Debug, Error)] +pub enum DhtActorError { + /// MPSC channel is disconnected + ChannelDisconnected, + /// MPSC sender was unable to send because the channel buffer is full + SendBufferFull, + /// Reply sender canceled the request + ReplyCanceled, + PeerManagerError(PeerManagerError), + #[error(msg_embedded, no_from, non_std)] + SendFailed(String), + DiscoveryError(DhtDiscoveryError), + BlockingJoinError(tokio::task::JoinError), +} + +impl From for DhtActorError { + fn from(err: SendError) -> Self { + if err.is_disconnected() { + DhtActorError::ChannelDisconnected + } else if err.is_full() { + DhtActorError::SendBufferFull + } else { + unreachable!(); + } + } +} + +#[derive(Debug)] +pub enum DhtRequest { + /// Send a Join request to the network + SendJoin, + /// Send a request for stored messages, optionally specifying a date time that the foreign node should + /// use to filter the returned messages. + SendRequestStoredMessages(Option>), + /// Inserts a message signature to the msg hash cache. This operation replies with a boolean + /// which is true if the signature already exists in the cache, otherwise false + MsgHashCacheInsert(Vec, oneshot::Sender), + /// Fetch selected peers according to the broadcast strategy + SelectPeers(BroadcastStrategy, oneshot::Sender>), +} + +impl Display for DhtRequest { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + match self { + DhtRequest::SendJoin => f.write_str("SendJoin"), + DhtRequest::SendRequestStoredMessages(d) => f.write_str(&format!("SendRequestStoredMessages ({:?})", d)), + DhtRequest::MsgHashCacheInsert(_, _) => f.write_str("MsgHashCacheInsert"), + DhtRequest::SelectPeers(s, _) => f.write_str(&format!("SelectPeers (Strategy={})", s)), + } + } +} + +#[derive(Clone)] +pub struct DhtRequester { + sender: mpsc::Sender, +} + +impl DhtRequester { + pub fn new(sender: mpsc::Sender) -> Self { + Self { sender } + } + + pub async fn send_join(&mut self) -> Result<(), DhtActorError> { + self.sender.send(DhtRequest::SendJoin).await.map_err(Into::into) + } + + pub async fn select_peers(&mut self, broadcast_strategy: BroadcastStrategy) -> Result, DhtActorError> { + let (reply_tx, reply_rx) = oneshot::channel(); + self.sender + .send(DhtRequest::SelectPeers(broadcast_strategy, reply_tx)) + .await?; + reply_rx.await.map_err(|_| DhtActorError::ReplyCanceled) + } + + pub async fn insert_message_hash(&mut self, signature: Vec) -> Result { + let (reply_tx, reply_rx) = oneshot::channel(); + self.sender + .send(DhtRequest::MsgHashCacheInsert(signature, reply_tx)) + .await?; + + reply_rx.await.map_err(|_| DhtActorError::ReplyCanceled) + } + + pub async fn send_request_stored_messages(&mut self) -> Result<(), DhtActorError> { + self.sender + .send(DhtRequest::SendRequestStoredMessages(None)) + .await + .map_err(Into::into) + } +} + +pub struct DhtActor<'a> { + node_identity: Arc, + peer_manager: Arc, + outbound_requester: OutboundMessageRequester, + config: DhtConfig, + shutdown_signal: Option, + request_rx: Fuse>, + msg_hash_cache: TtlCache, ()>, + pending_jobs: FuturesUnordered>>, +} + +impl<'a> DhtActor<'a> { + pub fn new( + config: DhtConfig, + node_identity: Arc, + peer_manager: Arc, + outbound_requester: OutboundMessageRequester, + request_rx: mpsc::Receiver, + shutdown_signal: ShutdownSignal, + ) -> Self + { + Self { + msg_hash_cache: TtlCache::new(config.msg_hash_cache_capacity), + config, + outbound_requester, + peer_manager, + node_identity, + shutdown_signal: Some(shutdown_signal), + request_rx: request_rx.fuse(), + pending_jobs: FuturesUnordered::new(), + } + } + + pub async fn run(mut self) { + let mut shutdown_signal = self + .shutdown_signal + .take() + .expect("DhtActor initialized without shutdown_signal") + .fuse(); + + loop { + futures::select! { + request = self.request_rx.select_next_some() => { + debug!(target: LOG_TARGET, "DhtActor received message: {}", request); + let handler = self.request_handler(request); + self.pending_jobs.push(handler); + }, + + result = self.pending_jobs.select_next_some() => { + match result { + Ok(_) => { + trace!(target: LOG_TARGET, "DHT Actor request succeeded"); + }, + Err(err) => { + error!(target: LOG_TARGET, "Error when handling DHT request message. {}", err); + }, + } + }, + + _ = shutdown_signal => { + info!(target: LOG_TARGET, "DhtActor is shutting down because it received a shutdown signal."); + break; + }, + complete => { + info!(target: LOG_TARGET, "DhtActor is shutting down because the request stream ended."); + break; + } + } + } + } + + fn request_handler(&mut self, request: DhtRequest) -> BoxFuture<'a, Result<(), DhtActorError>> { + use DhtRequest::*; + match request { + SendJoin => { + let node_identity = Arc::clone(&self.node_identity); + let outbound_requester = self.outbound_requester.clone(); + Box::pin(Self::send_join( + node_identity, + outbound_requester, + self.config.num_neighbouring_nodes, + )) + }, + MsgHashCacheInsert(hash, reply_tx) => { + // No locks needed here. Downside is this isn't really async, however this should be + // fine as it is very quick + let already_exists = self + .msg_hash_cache + .insert(hash, (), self.config.msg_hash_cache_ttl) + .is_some(); + let result = reply_tx.send(already_exists).map_err(|_| DhtActorError::ReplyCanceled); + Box::pin(future::ready(result)) + }, + SelectPeers(broadcast_strategy, reply_tx) => { + let peer_manager = Arc::clone(&self.peer_manager); + let node_identity = Arc::clone(&self.node_identity); + let config = self.config.clone(); + Box::pin( + task::spawn_blocking(move || { + match Self::select_peers(config, node_identity, peer_manager, broadcast_strategy) { + Ok(peers) => reply_tx.send(peers).map_err(|_| DhtActorError::ReplyCanceled), + Err(err) => { + error!(target: LOG_TARGET, "Peer selection failed: {:?}", err); + reply_tx.send(Vec::new()).map_err(|_| DhtActorError::ReplyCanceled) + }, + } + }) + .map(hoist_nested_result), + ) + }, + SendRequestStoredMessages(maybe_since) => { + let node_identity = Arc::clone(&self.node_identity); + let outbound_requester = self.outbound_requester.clone(); + Box::pin(Self::request_stored_messages( + node_identity, + outbound_requester, + self.config.num_neighbouring_nodes, + maybe_since, + )) + }, + } + } + + async fn send_join( + node_identity: Arc, + mut outbound_requester: OutboundMessageRequester, + num_neighbouring_nodes: usize, + ) -> Result<(), DhtActorError> + { + let message = JoinMessage { + node_id: node_identity.node_id().to_vec(), + addresses: vec![node_identity.public_address().to_string()], + peer_features: node_identity.features().bits(), + }; + + debug!( + target: LOG_TARGET, + "Sending Join message to (at most) {} closest peers", num_neighbouring_nodes + ); + + outbound_requester + .send_message_no_header( + SendMessageParams::new() + .closest(node_identity.node_id().clone(), num_neighbouring_nodes, Vec::new()) + .with_dht_message_type(DhtMessageType::Join) + .force_origin() + .finish(), + message, + ) + .await + .map_err(|err| DhtActorError::SendFailed(format!("Failed to send join message: {}", err)))?; + + Ok(()) + } + + async fn request_stored_messages( + node_identity: Arc, + mut outbound_requester: OutboundMessageRequester, + num_neighbouring_nodes: usize, + maybe_since: Option>, + ) -> Result<(), DhtActorError> + { + outbound_requester + .send_message_no_header( + SendMessageParams::new() + .closest(node_identity.node_id().clone(), num_neighbouring_nodes, Vec::new()) + .with_dht_message_type(DhtMessageType::SafRequestMessages) + .finish(), + maybe_since.map(StoredMessagesRequest::since).unwrap_or_default(), + ) + .await + .map_err(|err| DhtActorError::SendFailed(format!("Failed to send request for stored messages: {}", err)))?; + + Ok(()) + } + + fn select_peers( + config: DhtConfig, + node_identity: Arc, + peer_manager: Arc, + broadcast_strategy: BroadcastStrategy, + ) -> Result, DhtActorError> + { + use BroadcastStrategy::*; + match broadcast_strategy { + DirectNodeId(node_id) => { + // Send to a particular peer matching the given node ID + peer_manager + .direct_identity_node_id(&node_id) + .map(|peer| peer.map(|p| vec![p]).unwrap_or_default()) + .map_err(Into::into) + }, + DirectPublicKey(public_key) => { + // Send to a particular peer matching the given node ID + peer_manager + .direct_identity_public_key(&public_key) + .map(|peer| peer.map(|p| vec![p]).unwrap_or_default()) + .map_err(Into::into) + }, + Flood => { + // Send to all known Communication Node peers + peer_manager.flood_peers().map_err(Into::into) + }, + Closest(closest_request) => Self::select_closest_peers( + &config, + peer_manager, + &closest_request.node_id, + closest_request.n, + &closest_request.excluded_peers, + ), + Random(n) => { + // Send to a random set of peers of size n that are Communication Nodes + peer_manager.random_peers(n).map_err(Into::into) + }, + // TODO: This is a common and expensive search - values here should be cached + Neighbours(exclude) => { + // Send to a random set of peers of size n that are Communication Nodes + Self::select_closest_peers( + &config, + peer_manager, + node_identity.node_id(), + config.num_neighbouring_nodes, + &*exclude, + ) + }, + } + } + + fn select_closest_peers( + config: &DhtConfig, + peer_manager: Arc, + node_id: &NodeId, + n: usize, + excluded_peers: &[CommsPublicKey], + ) -> Result, DhtActorError> + { + // TODO: This query is expensive. We can probably cache a list of neighbouring peers which are online + // Fetch to all n nearest neighbour Communication Nodes + // which are eligible for connection. + // Currently that means: + // - The peer isn't banned, + // - it didn't recently fail to connect, and + // - it is not in the exclusion list in closest_request + let mut connect_ineligable_count = 0; + let mut banned_count = 0; + let mut excluded_count = 0; + let mut not_propagation_node_count = 0; + let query = PeerQuery::new() + .select_where(|peer| { + trace!(target: LOG_TARGET, "Considering peer for broadcast: {}", peer.node_id); + // This is a quite ugly but is done this way to get the logging + let is_banned = peer.is_banned(); + trace!(target: LOG_TARGET, "[{}] is banned: {}", peer.node_id, is_banned); + if is_banned { + banned_count += 1; + return false; + } + + if !peer.has_features(PeerFeatures::MESSAGE_PROPAGATION) { + trace!( + target: LOG_TARGET, + "[{}] does NOT have the message propagation feature", + peer.node_id + ); + not_propagation_node_count += 1; + return false; + } + + let is_connect_eligible = { + // Check this peer was recently connectable + peer.connection_stats.failed_attempts() <= config.broadcast_cooldown_max_attempts || + peer.connection_stats + .time_since_last_failure() + .map(|failed_since| failed_since >= config.broadcast_cooldown_period) + .unwrap_or(true) + }; + + if !is_connect_eligible { + trace!( + target: LOG_TARGET, + "[{}] suffered too many connection attempt failures", + peer.node_id + ); + connect_ineligable_count += 1; + return false; + } + + let is_excluded = excluded_peers.contains(&peer.public_key); + if is_excluded { + trace!(target: LOG_TARGET, "[{}] is explicitly excluded", peer.node_id); + excluded_count += 1; + return false; + } + + true + }) + .sort_by(PeerQuerySortBy::DistanceFrom(&node_id)) + .limit(n); + + let peers = peer_manager.perform_query(query)?; + let total = banned_count + connect_ineligable_count + excluded_count; + if total > 0 { + debug!( + target: LOG_TARGET, + "\n====================================\n Closest Peer Selection\n\n {num_peers} peer(s) selected\n \ + {total} peer(s) were excluded \n {banned} banned\n {not_propagation_node} not communication node\n \ + {not_connectable} are not connectable\n {excluded} excluded\n====================================\n", + num_peers = peers.len(), + total = total, + banned = banned_count, + not_propagation_node = not_propagation_node_count, + not_connectable = connect_ineligable_count, + excluded = excluded_count + ); + } + + Ok(peers) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::test_utils::{make_node_identity, make_peer_manager}; + use tari_comms::{ + net_address::MultiaddressesWithStats, + peer_manager::{PeerFeatures, PeerFlags}, + }; + use tari_shutdown::Shutdown; + use tari_test_utils::runtime; + + #[test] + fn send_join_request() { + runtime::test_async(|rt| { + let node_identity = make_node_identity(); + let peer_manager = make_peer_manager(); + let (out_tx, mut out_rx) = mpsc::channel(1); + let (actor_tx, actor_rx) = mpsc::channel(1); + let mut requester = DhtRequester::new(actor_tx); + let outbound_requester = OutboundMessageRequester::new(out_tx); + let shutdown = Shutdown::new(); + let actor = DhtActor::new( + Default::default(), + node_identity, + peer_manager, + outbound_requester, + actor_rx, + shutdown.to_signal(), + ); + + rt.spawn(actor.run()); + + rt.block_on(async move { + requester.send_join().await.unwrap(); + let (params, _) = unwrap_oms_send_msg!(out_rx.next().await.unwrap()); + assert_eq!(params.dht_message_type, DhtMessageType::Join); + }); + }); + } + + #[test] + fn insert_message_signature() { + runtime::test_async(|rt| { + let node_identity = make_node_identity(); + let peer_manager = make_peer_manager(); + let (out_tx, _) = mpsc::channel(1); + let (actor_tx, actor_rx) = mpsc::channel(1); + let mut requester = DhtRequester::new(actor_tx); + let outbound_requester = OutboundMessageRequester::new(out_tx); + let shutdown = Shutdown::new(); + let actor = DhtActor::new( + Default::default(), + node_identity, + peer_manager, + outbound_requester, + actor_rx, + shutdown.to_signal(), + ); + + rt.spawn(actor.run()); + + rt.block_on(async move { + let signature = vec![1u8, 2, 3]; + let is_dup = requester.insert_message_hash(signature.clone()).await.unwrap(); + assert_eq!(is_dup, false); + let is_dup = requester.insert_message_hash(signature).await.unwrap(); + assert_eq!(is_dup, true); + let is_dup = requester.insert_message_hash(Vec::new()).await.unwrap(); + assert_eq!(is_dup, false); + }); + }); + } + + #[test] + fn select_peers() { + runtime::test_async(|rt| { + let node_identity = make_node_identity(); + let peer_manager = make_peer_manager(); + peer_manager + .add_peer(Peer::new( + node_identity.public_key().clone(), + node_identity.node_id().clone(), + MultiaddressesWithStats::new(vec![]), + PeerFlags::empty(), + PeerFeatures::COMMUNICATION_CLIENT, + )) + .unwrap(); + let (out_tx, _) = mpsc::channel(1); + let (actor_tx, actor_rx) = mpsc::channel(1); + let mut requester = DhtRequester::new(actor_tx); + let outbound_requester = OutboundMessageRequester::new(out_tx); + let shutdown = Shutdown::new(); + let actor = DhtActor::new( + Default::default(), + Arc::clone(&node_identity), + peer_manager, + outbound_requester, + actor_rx, + shutdown.to_signal(), + ); + + rt.spawn(actor.run()); + + rt.block_on(async move { + let peers = requester + .select_peers(BroadcastStrategy::Neighbours(Vec::new())) + .await + .unwrap(); + + assert_eq!(peers.len(), 0); + + let peers = requester + .select_peers(BroadcastStrategy::DirectNodeId(node_identity.node_id().clone())) + .await + .unwrap(); + + assert_eq!(peers.len(), 1); + }); + }); + } +} diff --git a/comms/dht/src/broadcast_strategy.rs b/comms/dht/src/broadcast_strategy.rs new file mode 100644 index 0000000000..045988aaa2 --- /dev/null +++ b/comms/dht/src/broadcast_strategy.rs @@ -0,0 +1,163 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::{fmt, fmt::Formatter}; +use tari_comms::{peer_manager::node_id::NodeId, types::CommsPublicKey}; + +#[derive(Debug, Clone)] +pub struct BroadcastClosestRequest { + pub n: usize, + pub node_id: NodeId, + pub excluded_peers: Vec, +} + +#[derive(Debug, Clone)] +pub enum BroadcastStrategy { + /// Send to a particular peer matching the given node ID + DirectNodeId(NodeId), + /// Send to a particular peer matching the given Public Key + DirectPublicKey(CommsPublicKey), + /// Send to all known Communication Node peers + Flood, + /// Send to a random set of peers of size n that are Communication Nodes + Random(usize), + /// Send to all n nearest Communication Nodes according to the given BroadcastClosestRequest + Closest(Box), + /// A convenient strategy which behaves the same as the `Closest` strategy with the `NodeId` set + /// to this node and a pre-configured number of neighbours. This strategy excludes the given public keys. + Neighbours(Vec), +} + +impl fmt::Display for BroadcastStrategy { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { + use BroadcastStrategy::*; + match self { + DirectPublicKey(pk) => write!(f, "DirectPublicKey({})", pk), + DirectNodeId(node_id) => write!(f, "DirectNodeId({})", node_id), + Flood => write!(f, "Flood"), + Closest(request) => write!(f, "Closest({})", request.n), + Random(n) => write!(f, "Random({})", n), + Neighbours(excluded) => write!(f, "Neighbours({} excluded)", excluded.len()), + } + } +} + +impl BroadcastStrategy { + pub fn is_direct(&self) -> bool { + use BroadcastStrategy::*; + match self { + DirectNodeId(_) | DirectPublicKey(_) => true, + _ => false, + } + } + + pub fn direct_node_id(&self) -> Option<&NodeId> { + use BroadcastStrategy::*; + match self { + DirectNodeId(node_id) => Some(node_id), + _ => None, + } + } + + pub fn direct_public_key(&self) -> Option<&CommsPublicKey> { + use BroadcastStrategy::*; + match self { + DirectPublicKey(pk) => Some(pk), + _ => None, + } + } + + pub fn into_direct_public_key(self) -> Option { + use BroadcastStrategy::*; + match self { + DirectPublicKey(pk) => Some(pk), + _ => None, + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn is_direct() { + assert!(BroadcastStrategy::DirectPublicKey(CommsPublicKey::default()).is_direct()); + assert!(BroadcastStrategy::DirectNodeId(NodeId::default()).is_direct()); + assert_eq!(BroadcastStrategy::Neighbours(Default::default()).is_direct(), false); + assert_eq!(BroadcastStrategy::Flood.is_direct(), false); + assert_eq!( + BroadcastStrategy::Closest(Box::new(BroadcastClosestRequest { + node_id: NodeId::default(), + n: 0, + excluded_peers: Default::default() + })) + .is_direct(), + false + ); + assert_eq!(BroadcastStrategy::Random(0).is_direct(), false); + } + + #[test] + fn direct_public_key() { + assert!(BroadcastStrategy::DirectPublicKey(CommsPublicKey::default()) + .direct_public_key() + .is_some()); + assert!(BroadcastStrategy::DirectNodeId(NodeId::default()) + .direct_public_key() + .is_none()); + assert!(BroadcastStrategy::Neighbours(Default::default()) + .direct_public_key() + .is_none()); + assert!(BroadcastStrategy::Flood.direct_public_key().is_none()); + assert!(BroadcastStrategy::Closest(Box::new(BroadcastClosestRequest { + node_id: NodeId::default(), + n: 0, + excluded_peers: Default::default() + })) + .direct_public_key() + .is_none(),); + assert!(BroadcastStrategy::Random(0).direct_public_key().is_none(), false); + } + + #[test] + fn direct_node_id() { + assert!(BroadcastStrategy::DirectPublicKey(CommsPublicKey::default()) + .direct_node_id() + .is_none()); + assert!(BroadcastStrategy::DirectNodeId(NodeId::default()) + .direct_node_id() + .is_some()); + assert!(BroadcastStrategy::Neighbours(Default::default()) + .direct_node_id() + .is_none()); + assert!(BroadcastStrategy::Flood.direct_node_id().is_none()); + assert!(BroadcastStrategy::Closest(Box::new(BroadcastClosestRequest { + node_id: NodeId::default(), + n: 0, + excluded_peers: Default::default() + })) + .direct_node_id() + .is_none(),); + assert!(BroadcastStrategy::Random(0).direct_node_id().is_none(), false); + } +} diff --git a/comms/dht/src/builder.rs b/comms/dht/src/builder.rs new file mode 100644 index 0000000000..77d132027c --- /dev/null +++ b/comms/dht/src/builder.rs @@ -0,0 +1,118 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{outbound::DhtOutboundRequest, Dht, DhtConfig}; +use futures::channel::mpsc; +use std::{sync::Arc, time::Duration}; +use tari_comms::peer_manager::{NodeIdentity, PeerManager}; +use tari_shutdown::ShutdownSignal; +use tokio::runtime; + +pub struct DhtBuilder { + node_identity: Arc, + peer_manager: Arc, + config: DhtConfig, + executor: Option, + outbound_tx: mpsc::Sender, + shutdown_signal: ShutdownSignal, +} + +impl DhtBuilder { + pub fn new( + node_identity: Arc, + peer_manager: Arc, + outbound_tx: mpsc::Sender, + shutdown_signal: ShutdownSignal, + ) -> Self + { + Self { + #[cfg(test)] + config: DhtConfig::default_local_test(), + #[cfg(not(test))] + config: Default::default(), + node_identity, + peer_manager, + executor: None, + outbound_tx, + shutdown_signal, + } + } + + pub fn with_config(mut self, config: DhtConfig) -> Self { + self.config = config; + self + } + + pub fn with_executor(mut self, executor: runtime::Handle) -> Self { + self.executor = Some(executor); + self + } + + pub fn local_test(mut self) -> Self { + self.config = DhtConfig::default_local_test(); + self + } + + pub fn testnet(mut self) -> Self { + self.config = DhtConfig::default_testnet(); + self + } + + pub fn mainnet(mut self) -> Self { + self.config = DhtConfig::default_mainnet(); + self + } + + pub fn with_signature_cache_ttl(mut self, ttl: Duration) -> Self { + self.config.msg_hash_cache_ttl = ttl; + self + } + + pub fn with_signature_cache_capacity(mut self, capacity: usize) -> Self { + self.config.msg_hash_cache_capacity = capacity; + self + } + + pub fn with_num_neighbouring_nodes(mut self, num_neighbours: usize) -> Self { + self.config.num_neighbouring_nodes = num_neighbours; + self + } + + pub fn with_discovery_timeout(mut self, timeout: Duration) -> Self { + self.config.discovery_request_timeout = timeout; + self + } + + /// Build a Dht object. + /// + /// Will panic if an executor is not given AND not in a tokio runtime context + pub fn finish(self) -> Dht { + Dht::new( + self.config, + self.executor.unwrap_or(runtime::Handle::current()), + self.node_identity, + self.peer_manager, + self.outbound_tx, + self.shutdown_signal, + ) + } +} diff --git a/comms/dht/src/config.rs b/comms/dht/src/config.rs new file mode 100644 index 0000000000..69dad15892 --- /dev/null +++ b/comms/dht/src/config.rs @@ -0,0 +1,118 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::envelope::Network; +use std::time::Duration; + +/// The default maximum number of messages that can be stored using the Store-and-forward middleware +pub const SAF_MSG_CACHE_STORAGE_CAPACITY: usize = 10_000; +/// The default time-to-live duration used for storage of low priority messages by the Store-and-forward middleware +pub const SAF_LOW_PRIORITY_MSG_STORAGE_TTL: Duration = Duration::from_secs(6 * 60 * 60); +/// The default time-to-live duration used for storage of high priority messages by the Store-and-forward middleware +pub const SAF_HIGH_PRIORITY_MSG_STORAGE_TTL: Duration = Duration::from_secs(24 * 60 * 60); +/// The default number of peer nodes that a message has to be closer to, to be considered a neighbour +pub const DEFAULT_NUM_NEIGHBOURING_NODES: usize = 8; + +#[derive(Debug, Clone)] +pub struct DhtConfig { + /// The size of the buffer (channel) which holds pending outbound message requests. + /// Default: 20 + pub outbound_buffer_size: usize, + /// The maximum number of peer nodes that a message has to be closer to, to be considered a neighbour + /// Default: 8 + pub num_neighbouring_nodes: usize, + /// A request to retrieve stored messages will be ignored if the requesting node is + /// not within one of this nodes _n_ closest nodes. + /// Default 8 + pub saf_num_closest_nodes: usize, + /// The maximum number of messages to return from a store and forward retrieval request. + /// Default: 100 + pub saf_max_returned_messages: usize, + /// The maximum number of messages that can be stored using the Store-and-forward middleware. Default: 10_000 + pub saf_msg_cache_storage_capacity: usize, + /// The time-to-live duration used for storage of low priority messages by the Store-and-forward middleware. + /// Default: 6 hours + pub saf_low_priority_msg_storage_ttl: Duration, + /// The time-to-live duration used for storage of high priority messages by the Store-and-forward middleware. + /// Default: 24 hours + pub saf_high_priority_msg_storage_ttl: Duration, + /// The max capacity of the message hash cache + /// Default: 1000 + pub msg_hash_cache_capacity: usize, + /// The time-to-live for items in the message hash cache + /// Default: 300s + pub msg_hash_cache_ttl: Duration, + /// Sets the number of failed attempts in-a-row to tolerate before temporarily excluding this peer from broadcast + /// messages. + /// Default: 3 + pub broadcast_cooldown_max_attempts: usize, + /// Sets the period to wait before including this peer in broadcast messages after + /// `broadcast_cooldown_max_attempts` failed attempts. This helps prevent thrashing the comms layer + /// with connection attempts to a peer which is offline. + /// Default: 30 minutes + pub broadcast_cooldown_period: Duration, + /// The duration to wait for a peer discovery to complete before giving up. + /// Default: 2 minutes + pub discovery_request_timeout: Duration, + /// The active Network. Default: TestNet + pub network: Network, +} + +impl DhtConfig { + pub fn default_testnet() -> Self { + Default::default() + } + + pub fn default_mainnet() -> Self { + Self { + network: Network::MainNet, + ..Default::default() + } + } + + pub fn default_local_test() -> Self { + Self { + network: Network::LocalTest, + ..Default::default() + } + } +} + +impl Default for DhtConfig { + fn default() -> Self { + Self { + num_neighbouring_nodes: DEFAULT_NUM_NEIGHBOURING_NODES, + saf_num_closest_nodes: 8, + saf_max_returned_messages: 100, + outbound_buffer_size: 20, + saf_msg_cache_storage_capacity: SAF_MSG_CACHE_STORAGE_CAPACITY, + saf_low_priority_msg_storage_ttl: SAF_LOW_PRIORITY_MSG_STORAGE_TTL, + saf_high_priority_msg_storage_ttl: SAF_HIGH_PRIORITY_MSG_STORAGE_TTL, + msg_hash_cache_capacity: 1000, + msg_hash_cache_ttl: Duration::from_secs(300), + broadcast_cooldown_max_attempts: 3, + broadcast_cooldown_period: Duration::from_secs(60 * 30), + discovery_request_timeout: Duration::from_secs(2 * 60), + network: Network::TestNet, + } + } +} diff --git a/comms/dht/src/consts.rs b/comms/dht/src/consts.rs new file mode 100644 index 0000000000..69695780ac --- /dev/null +++ b/comms/dht/src/consts.rs @@ -0,0 +1,24 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/// Version for DHT envelope +pub const DHT_ENVELOPE_HEADER_VERSION: u32 = 0; diff --git a/comms/dht/src/crypt.rs b/comms/dht/src/crypt.rs new file mode 100644 index 0000000000..4f12b1ff81 --- /dev/null +++ b/comms/dht/src/crypt.rs @@ -0,0 +1,72 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use tari_comms::types::CommsPublicKey; +use tari_crypto::{ + keys::{DiffieHellmanSharedSecret, PublicKey}, + tari_utilities::{ + ciphers::{ + chacha20::ChaCha20, + cipher::{Cipher, CipherError}, + }, + ByteArray, + }, +}; + +pub fn generate_ecdh_secret(secret_key: &PK::K, public_key: &PK) -> PK +where PK: PublicKey + DiffieHellmanSharedSecret { + PK::shared_secret(secret_key, public_key) +} + +pub fn decrypt(cipher_key: &CommsPublicKey, cipher_text: &[u8]) -> Result, CipherError> { + ChaCha20::open_with_integral_nonce(cipher_text, cipher_key.as_bytes()) +} + +pub fn encrypt(cipher_key: &CommsPublicKey, plain_text: &Vec) -> Result, CipherError> { + ChaCha20::seal_with_integral_nonce(plain_text, &cipher_key.to_vec()) +} + +#[cfg(test)] +mod test { + use super::*; + use tari_crypto::tari_utilities::hex::from_hex; + + #[test] + fn encrypt_decrypt() { + let key = CommsPublicKey::default(); + let plain_text = "Last enemy position 0830h AJ 9863".as_bytes().to_vec(); + let encrypted = encrypt(&key, &plain_text).unwrap(); + let decrypted = decrypt(&key, &encrypted).unwrap(); + assert_eq!(decrypted, plain_text); + } + + #[test] + fn decrypt_fn() { + let key = CommsPublicKey::default(); + let cipher_text = + from_hex("7ecafb4c0a88325c984517fca1c529b3083e9976290a50c43ff90b2ccb361aeaabfaf680e744b96fc3649a447b") + .unwrap(); + let plain_text = decrypt(&key, &cipher_text).unwrap(); + let secret_msg = "Last enemy position 0830h AJ 9863".as_bytes().to_vec(); + assert_eq!(plain_text, secret_msg); + } +} diff --git a/comms/dht/src/dht.rs b/comms/dht/src/dht.rs new file mode 100644 index 0000000000..efced7265d --- /dev/null +++ b/comms/dht/src/dht.rs @@ -0,0 +1,462 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use self::outbound::OutboundMessageRequester; +use crate::{ + actor::{DhtActor, DhtRequest, DhtRequester}, + discovery::{DhtDiscoveryRequest, DhtDiscoveryRequester, DhtDiscoveryService}, + inbound, + inbound::{DecryptedDhtMessage, DhtInboundMessage}, + outbound, + outbound::DhtOutboundRequest, + proto::envelope::DhtMessageType, + store_forward, + tower_filter, + tower_filter::error::Error as FilterError, + DhtConfig, + PipelineError, +}; +use futures::{channel::mpsc, future, Future}; +use log::*; +use std::sync::Arc; +use tari_comms::{ + message::{InboundMessage, OutboundMessage}, + peer_manager::{NodeIdentity, PeerFeatures, PeerManager}, +}; +use tari_shutdown::ShutdownSignal; +use tokio::runtime; +use tower::{layer::Layer, Service, ServiceBuilder}; + +/// Responsible for starting the DHT actor, building the DHT middleware stack and as a factory +/// for producing DHT requesters. +pub struct Dht { + /// Node identity of this node + node_identity: Arc, + /// Comms peer manager + peer_manager: Arc, + /// Dht configuration + config: DhtConfig, + /// Used to create a OutboundMessageRequester. + outbound_tx: mpsc::Sender, + /// Sender for DHT requests + dht_sender: mpsc::Sender, + /// Sender for DHT requests + discovery_sender: mpsc::Sender, +} + +impl Dht { + pub fn new( + config: DhtConfig, + executor: runtime::Handle, + node_identity: Arc, + peer_manager: Arc, + outbound_tx: mpsc::Sender, + shutdown_signal: ShutdownSignal, + ) -> Self + { + let (dht_sender, dht_receiver) = mpsc::channel(10); + let (discovery_sender, discovery_receiver) = mpsc::channel(10); + + let dht = Self { + node_identity, + peer_manager, + config, + outbound_tx, + dht_sender, + discovery_sender, + }; + + executor.spawn(dht.actor(dht_receiver, shutdown_signal.clone()).run()); + executor.spawn(dht.discovery_service(discovery_receiver, shutdown_signal).run()); + + dht + } + + /// Create a DHT actor + fn actor( + &self, + request_receiver: mpsc::Receiver, + shutdown_signal: ShutdownSignal, + ) -> DhtActor<'static> + { + DhtActor::new( + self.config.clone(), + Arc::clone(&self.node_identity), + Arc::clone(&self.peer_manager), + self.outbound_requester(), + request_receiver, + shutdown_signal, + ) + } + + /// Create the discovery service + fn discovery_service( + &self, + request_receiver: mpsc::Receiver, + shutdown_signal: ShutdownSignal, + ) -> DhtDiscoveryService + { + DhtDiscoveryService::new( + self.config.clone(), + Arc::clone(&self.node_identity), + Arc::clone(&self.peer_manager), + self.outbound_requester(), + request_receiver, + shutdown_signal, + ) + } + + /// Return a new OutboundMessageRequester connected to the receiver + pub fn outbound_requester(&self) -> OutboundMessageRequester { + OutboundMessageRequester::new(self.outbound_tx.clone()) + } + + /// Returns a requester for the DhtActor associated with this instance + pub fn dht_requester(&self) -> DhtRequester { + DhtRequester::new(self.dht_sender.clone()) + } + + /// Returns a requester for the DhtDiscoveryService associated with this instance + pub fn discovery_service_requester(&self) -> DhtDiscoveryRequester { + DhtDiscoveryRequester::new(self.discovery_sender.clone(), self.config.discovery_request_timeout) + } + + /// Returns an the full DHT stack as a `tower::layer::Layer`. This can be composed with + /// other inbound middleware services which expect an DecryptedDhtMessage + pub fn inbound_middleware_layer( + &self, + ) -> impl Layer< + S, + Service = impl Service< + InboundMessage, + Response = (), + Error = PipelineError, + Future = impl Future> + Send, + > + Clone + + Send, + > + where + S: Service + Clone + Send + Sync + 'static, + S::Future: Send, + { + let saf_storage = Arc::new(store_forward::SafStorage::new( + self.config.saf_msg_cache_storage_capacity, + )); + + ServiceBuilder::new() + .layer(inbound::DeserializeLayer::new()) + .layer(inbound::ValidateLayer::new( + self.config.network, + self.outbound_requester(), + )) + .layer(inbound::DedupLayer::new(self.dht_requester())) + .layer(tower_filter::FilterLayer::new(self.unsupported_saf_messages_filter())) + .layer(inbound::DecryptionLayer::new(Arc::clone(&self.node_identity))) + .layer(store_forward::ForwardLayer::new( + Arc::clone(&self.peer_manager), + self.outbound_requester(), + )) + .layer(store_forward::StoreLayer::new( + self.config.clone(), + Arc::clone(&self.peer_manager), + Arc::clone(&self.node_identity), + Arc::clone(&saf_storage), + )) + .layer(store_forward::MessageHandlerLayer::new( + self.config.clone(), + saf_storage, + self.dht_requester(), + Arc::clone(&self.node_identity), + Arc::clone(&self.peer_manager), + self.outbound_requester(), + )) + .layer(inbound::DhtHandlerLayer::new( + self.config.clone(), + Arc::clone(&self.node_identity), + Arc::clone(&self.peer_manager), + self.discovery_service_requester(), + self.outbound_requester(), + )) + .into_inner() + } + + /// Returns an the full DHT stack as a `tower::layer::Layer`. This can be composed with + /// other outbound middleware services which expect an OutboundMessage + pub fn outbound_middleware_layer( + &self, + ) -> impl Layer< + S, + Service = impl Service< + DhtOutboundRequest, + Response = (), + Error = PipelineError, + Future = impl Future> + Send, + > + Clone + + Send, + > + where + S: Service + Clone + Send + 'static, + S::Future: Send, + { + ServiceBuilder::new() + .layer(outbound::BroadcastLayer::new( + Arc::clone(&self.node_identity), + self.dht_requester(), + self.discovery_service_requester(), + self.config.network, + )) + .layer(outbound::EncryptionLayer::new(Arc::clone(&self.node_identity))) + .layer(outbound::SerializeLayer::new(Arc::clone(&self.node_identity))) + .into_inner() + } + + /// Produces a filter predicate which disallows store and forward messages if that feature is not + /// supported by the node. + fn unsupported_saf_messages_filter( + &self, + ) -> impl tower_filter::Predicate>> + Clone + Send + { + let node_identity = Arc::clone(&self.node_identity); + move |msg: &DhtInboundMessage| { + if node_identity.has_peer_features(PeerFeatures::DHT_STORE_FORWARD) { + return future::ready(Ok(())); + } + + match msg.dht_header.message_type { + DhtMessageType::SafRequestMessages | DhtMessageType::SafStoredMessages => { + // TODO: #banheuristic This is an indication of node misbehaviour + warn!( + "Received store and forward message from PublicKey={}. Store and forward feature is not \ + supported by this node. Discarding message.", + msg.source_peer.public_key + ); + future::ready(Err(FilterError::rejected())) + }, + _ => future::ready(Ok(())), + } + } + } +} + +#[cfg(test)] +mod test { + use crate::{ + crypt, + envelope::DhtMessageFlags, + outbound::mock::create_outbound_service_mock, + proto::envelope::DhtMessageType, + test_utils::{ + make_client_identity, + make_comms_inbound_message, + make_dht_envelope, + make_node_identity, + make_peer_manager, + }, + DhtBuilder, + }; + use futures::{channel::mpsc, StreamExt}; + use std::{sync::Arc, time::Duration}; + use tari_comms::{ + message::{MessageExt, MessageFlags}, + pipeline::SinkService, + wrap_in_envelope_body, + }; + use tari_shutdown::Shutdown; + use tokio::{runtime, time}; + use tower::{layer::Layer, Service}; + + #[tokio_macros::test_basic] + async fn stack_unencrypted() { + let node_identity = make_node_identity(); + let peer_manager = make_peer_manager(); + + // Dummy out channel, we are not testing outbound here. + let (out_tx, _) = mpsc::channel(10); + + let shutdown = Shutdown::new(); + let dht = DhtBuilder::new(Arc::clone(&node_identity), peer_manager, out_tx, shutdown.to_signal()) + .local_test() + .finish(); + + let (out_tx, mut out_rx) = mpsc::channel(10); + + let mut service = dht.inbound_middleware_layer().layer(SinkService::new(out_tx)); + + let msg = wrap_in_envelope_body!(b"secret".to_vec()).unwrap(); + let dht_envelope = make_dht_envelope( + &node_identity, + msg.to_encoded_bytes().unwrap(), + DhtMessageFlags::empty(), + ); + let inbound_message = make_comms_inbound_message( + &node_identity, + dht_envelope.to_encoded_bytes().unwrap().into(), + MessageFlags::empty(), + ); + + let msg = { + service.call(inbound_message).await.unwrap(); + let msg = time::timeout(Duration::from_secs(10), out_rx.next()) + .await + .unwrap() + .unwrap(); + msg.success().unwrap().decode_part::>(0).unwrap().unwrap() + }; + + assert_eq!(msg, b"secret"); + } + + #[tokio_macros::test_basic] + async fn stack_encrypted() { + let node_identity = make_node_identity(); + let peer_manager = make_peer_manager(); + + // Dummy out channel, we are not testing outbound here. + let (out_tx, _) = mpsc::channel(10); + + let shutdown = Shutdown::new(); + let dht = DhtBuilder::new(Arc::clone(&node_identity), peer_manager, out_tx, shutdown.to_signal()).finish(); + + let (out_tx, mut out_rx) = mpsc::channel(10); + + let mut service = dht.inbound_middleware_layer().layer(SinkService::new(out_tx)); + + let msg = wrap_in_envelope_body!(b"secret".to_vec()).unwrap(); + // Encrypt for self + let ecdh_key = crypt::generate_ecdh_secret(node_identity.secret_key(), node_identity.public_key()); + let encrypted_bytes = crypt::encrypt(&ecdh_key, &msg.to_encoded_bytes().unwrap()).unwrap(); + let dht_envelope = make_dht_envelope(&node_identity, encrypted_bytes, DhtMessageFlags::ENCRYPTED); + let inbound_message = make_comms_inbound_message( + &node_identity, + dht_envelope.to_encoded_bytes().unwrap().into(), + MessageFlags::empty(), + ); + + let msg = { + service.call(inbound_message).await.unwrap(); + let msg = time::timeout(Duration::from_secs(10), out_rx.next()) + .await + .unwrap() + .unwrap(); + msg.success().unwrap().decode_part::>(0).unwrap().unwrap() + }; + + assert_eq!(msg, b"secret"); + } + + #[tokio_macros::test_basic] + async fn stack_forward() { + let node_identity = make_node_identity(); + let peer_manager = make_peer_manager(); + + let shutdown = Shutdown::new(); + + let (next_service_tx, mut next_service_rx) = mpsc::channel(10); + let (oms_requester, oms_mock) = create_outbound_service_mock(1); + + // Send all outbound requests to the mock + let dht = DhtBuilder::new( + Arc::clone(&node_identity), + peer_manager, + oms_requester.get_mpsc_sender(), + shutdown.to_signal(), + ) + .finish(); + let oms_mock_state = oms_mock.get_state(); + runtime::Handle::current().spawn(oms_mock.run()); + + let mut service = dht.inbound_middleware_layer().layer(SinkService::new(next_service_tx)); + + let msg = wrap_in_envelope_body!(b"unencrypteable".to_vec()).unwrap(); + + // Encrypt for someone else + let node_identity2 = make_node_identity(); + let ecdh_key = crypt::generate_ecdh_secret(node_identity2.secret_key(), node_identity2.public_key()); + let encrypted_bytes = crypt::encrypt(&ecdh_key, &msg.to_encoded_bytes().unwrap()).unwrap(); + let dht_envelope = make_dht_envelope(&node_identity, encrypted_bytes, DhtMessageFlags::ENCRYPTED); + + let origin_sig = dht_envelope + .header + .as_ref() + .unwrap() + .origin + .as_ref() + .unwrap() + .signature + .clone(); + let inbound_message = make_comms_inbound_message( + &node_identity, + dht_envelope.to_encoded_bytes().unwrap().into(), + MessageFlags::empty(), + ); + + service.call(inbound_message).await.unwrap(); + + assert_eq!(oms_mock_state.call_count(), 1); + let (params, _) = oms_mock_state.pop_call().unwrap(); + + // Check that OMS got a request to forward with the original Dht Header + assert_eq!(params.dht_header.unwrap().origin.unwrap().signature, origin_sig); + + // Check the next service was not called + assert!(next_service_rx.try_next().is_err()); + } + + #[tokio_macros::test_basic] + async fn stack_filter_saf_message() { + let node_identity = make_client_identity(); + let peer_manager = make_peer_manager(); + + // Dummy out channel, we are not testing outbound here. + let (out_tx, _) = mpsc::channel(10); + + let shutdown = Shutdown::new(); + let dht = DhtBuilder::new(Arc::clone(&node_identity), peer_manager, out_tx, shutdown.to_signal()).finish(); + + let (next_service_tx, mut next_service_rx) = mpsc::channel(10); + + let mut service = dht.inbound_middleware_layer().layer(SinkService::new(next_service_tx)); + + let msg = wrap_in_envelope_body!(b"secret".to_vec()).unwrap(); + let mut dht_envelope = make_dht_envelope( + &node_identity, + msg.to_encoded_bytes().unwrap(), + DhtMessageFlags::empty(), + ); + dht_envelope.header.as_mut().and_then(|header| { + header.message_type = DhtMessageType::SafStoredMessages as i32; + Some(header) + }); + let inbound_message = make_comms_inbound_message( + &node_identity, + dht_envelope.to_encoded_bytes().unwrap().into(), + MessageFlags::empty(), + ); + + service.call(inbound_message).await.unwrap_err(); + // This seems like the best way to tell that an open channel is empty without the test blocking indefinitely + assert_eq!( + format!("{}", next_service_rx.try_next().unwrap_err()), + "receiver channel is empty" + ); + } +} diff --git a/comms/dht/src/discovery/error.rs b/comms/dht/src/discovery/error.rs new file mode 100644 index 0000000000..c571223781 --- /dev/null +++ b/comms/dht/src/discovery/error.rs @@ -0,0 +1,68 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::outbound::DhtOutboundError; +use derive_error::Error; +use futures::channel::mpsc::SendError; +use tari_comms::peer_manager::PeerManagerError; + +#[derive(Debug, Error)] +pub enum DhtDiscoveryError { + /// The reply channel was canceled + ReplyCanceled, + DhtOutboundError(DhtOutboundError), + /// Received a discovery response which did not match an inflight discovery request + InflightDiscoveryRequestNotFound, + /// Received public key in peer discovery response which does not match the requested public key + DiscoveredPeerMismatch, + /// Received an invalid `NodeId` + InvalidNodeId, + /// MPSC channel is disconnected + ChannelDisconnected, + /// MPSC sender was unable to send because the channel buffer is full + SendBufferFull, + /// The discovery request timed out + DiscoveryTimeout, + PeerManagerError(PeerManagerError), +} + +impl DhtDiscoveryError { + /// Returns true if this error is a `DiscoveryTimeout`, otherwise false + pub fn is_timeout(&self) -> bool { + match self { + DhtDiscoveryError::DiscoveryTimeout => true, + _ => false, + } + } +} + +impl From for DhtDiscoveryError { + fn from(err: SendError) -> Self { + if err.is_disconnected() { + DhtDiscoveryError::ChannelDisconnected + } else if err.is_full() { + DhtDiscoveryError::SendBufferFull + } else { + unreachable!(); + } + } +} diff --git a/comms/dht/src/discovery/mod.rs b/comms/dht/src/discovery/mod.rs new file mode 100644 index 0000000000..0323d81c54 --- /dev/null +++ b/comms/dht/src/discovery/mod.rs @@ -0,0 +1,29 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod error; +mod requester; +mod service; + +pub(crate) use self::requester::DhtDiscoveryRequest; + +pub use self::{error::DhtDiscoveryError, requester::DhtDiscoveryRequester, service::DhtDiscoveryService}; diff --git a/comms/dht/src/discovery/requester.rs b/comms/dht/src/discovery/requester.rs new file mode 100644 index 0000000000..9e4d38f82c --- /dev/null +++ b/comms/dht/src/discovery/requester.rs @@ -0,0 +1,143 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{discovery::DhtDiscoveryError, envelope::NodeDestination, proto::dht::DiscoveryResponseMessage}; +use futures::{ + channel::{mpsc, oneshot}, + SinkExt, +}; +use std::{ + fmt::{Display, Error, Formatter}, + time::Duration, +}; +use tari_comms::{ + peer_manager::{NodeId, Peer}, + types::CommsPublicKey, +}; +use tokio::time; + +#[derive(Debug)] +pub struct DiscoverPeerRequest { + /// The public key of the peer to be discovered. The message will be encrypted with a DH shared + /// secret using this public key. + pub dest_public_key: CommsPublicKey, + /// The node id of the peer to be discovered, if it is known. Providing the `NodeId` allows + /// discovery requests to reach their destination more quickly. + pub dest_node_id: Option, + /// The destination to include in the comms header. + /// `Undisclosed` will require nodes to propagate the message across the network, presumably eventually + /// reaching the destination node (the node which can decrypt the message). This will happen without + /// any intermediary nodes knowing who is being searched for. + /// `NodeId` will direct the discovery request closer to the destination or network region. + /// `PublicKey` will be propagated across the network. If any node knows the peer, the request can be + /// forwarded to them immediately. However, more nodes will know that this node is being searched for + /// which may slightly compromise privacy. + pub destination: NodeDestination, +} + +impl Display for DiscoverPeerRequest { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { + f.debug_struct("DiscoverPeerRequest") + .field("dest_public_key", &format!("{}", self.dest_public_key)) + .field( + "dest_node_id", + &self + .dest_node_id + .as_ref() + .map(|node_id| format!("{}", node_id)) + .unwrap_or_else(|| "None".to_string()), + ) + .field("destination", &format!("{}", self.destination)) + .finish() + } +} + +#[derive(Debug)] +pub enum DhtDiscoveryRequest { + DiscoverPeer(Box<(DiscoverPeerRequest, oneshot::Sender>)>), + NotifyDiscoveryResponseReceived(Box), +} + +impl Display for DhtDiscoveryRequest { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { + use DhtDiscoveryRequest::*; + match self { + DiscoverPeer(boxed) => write!(f, "DiscoverPeer({})", boxed.0), + NotifyDiscoveryResponseReceived(boxed) => write!(f, "NotifyDiscoveryResponseReceived({:#?})", *boxed), + } + } +} + +#[derive(Clone)] +pub struct DhtDiscoveryRequester { + sender: mpsc::Sender, + discovery_timeout: Duration, +} + +impl DhtDiscoveryRequester { + pub fn new(sender: mpsc::Sender, discovery_timeout: Duration) -> Self { + Self { + sender, + discovery_timeout, + } + } + + pub async fn discover_peer( + &mut self, + dest_public_key: CommsPublicKey, + dest_node_id: Option, + destination: NodeDestination, + ) -> Result + { + let (reply_tx, reply_rx) = oneshot::channel(); + let request = DiscoverPeerRequest { + dest_public_key, + dest_node_id, + destination, + }; + self.sender + .send(DhtDiscoveryRequest::DiscoverPeer(Box::new((request, reply_tx)))) + .await?; + + time::timeout( + self.discovery_timeout, + reply_rx + ) + .await + // Timeout? + .map_err(|_| DhtDiscoveryError::DiscoveryTimeout)? + // Channel error? + .map_err(|_| DhtDiscoveryError::ReplyCanceled)? + } + + pub async fn notify_discovery_response_received( + &mut self, + response: DiscoveryResponseMessage, + ) -> Result<(), DhtDiscoveryError> + { + self.sender + .send(DhtDiscoveryRequest::NotifyDiscoveryResponseReceived(Box::new(response))) + .await?; + + Ok(()) + } +} diff --git a/comms/dht/src/discovery/service.rs b/comms/dht/src/discovery/service.rs new file mode 100644 index 0000000000..066f96e504 --- /dev/null +++ b/comms/dht/src/discovery/service.rs @@ -0,0 +1,402 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + discovery::{ + requester::{DhtDiscoveryRequest, DiscoverPeerRequest}, + DhtDiscoveryError, + }, + envelope::{DhtMessageType, NodeDestination}, + outbound::{OutboundEncryption, OutboundMessageRequester, SendMessageParams}, + proto::dht::{DiscoveryMessage, DiscoveryResponseMessage}, + DhtConfig, +}; +use futures::{ + channel::{mpsc, oneshot}, + future::FutureExt, + StreamExt, +}; +use log::*; +use rand::{rngs::OsRng, RngCore}; +use std::{collections::HashMap, sync::Arc}; +use tari_comms::{ + log_if_error, + multiaddr::Multiaddr, + peer_manager::{NodeId, NodeIdentity, Peer, PeerFeatures, PeerFlags, PeerManager}, + types::CommsPublicKey, +}; +use tari_crypto::tari_utilities::{hex::Hex, ByteArray}; +use tari_shutdown::ShutdownSignal; + +const LOG_TARGET: &str = "comms::dht::discovery_service"; + +struct DiscoveryRequestState { + reply_tx: oneshot::Sender>, + public_key: CommsPublicKey, +} + +pub struct DhtDiscoveryService { + config: DhtConfig, + node_identity: Arc, + outbound_requester: OutboundMessageRequester, + peer_manager: Arc, + request_rx: Option>, + shutdown_signal: Option, + inflight_discoveries: HashMap, +} + +impl DhtDiscoveryService { + pub fn new( + config: DhtConfig, + node_identity: Arc, + peer_manager: Arc, + outbound_requester: OutboundMessageRequester, + request_rx: mpsc::Receiver, + shutdown_signal: ShutdownSignal, + ) -> Self + { + Self { + config, + outbound_requester, + node_identity, + peer_manager, + shutdown_signal: Some(shutdown_signal), + request_rx: Some(request_rx), + inflight_discoveries: HashMap::new(), + } + } + + pub async fn run(mut self) { + let mut shutdown_signal = self + .shutdown_signal + .take() + .expect("DiscoveryService initialized without shutdown_signal") + .fuse(); + + let mut request_rx = self + .request_rx + .take() + .expect("DiscoveryService initialized without request_rx") + .fuse(); + + loop { + futures::select! { + request = request_rx.select_next_some() => { + trace!(target: LOG_TARGET, "Received request '{}'", request); + self.handle_request(request).await; + }, + + _ = shutdown_signal => { + info!(target: LOG_TARGET, "Discovery service is shutting down because the shutdown signal was received"); + break; + } + } + } + } + + async fn handle_request(&mut self, request: DhtDiscoveryRequest) { + use DhtDiscoveryRequest::*; + match request { + DiscoverPeer(boxed) => { + let (request, reply_tx) = *boxed; + log_if_error!( + target: LOG_TARGET, + self.initiate_peer_discovery(request, reply_tx).await, + "Failed to initiate a discovery request because '{error}'", + ); + }, + + NotifyDiscoveryResponseReceived(discovery_msg) => self.handle_discovery_response(*discovery_msg), + } + } + + fn collect_all_discovery_requests(&mut self, public_key: &CommsPublicKey) -> Vec { + let mut requests = Vec::new(); + let mut remaining_requests = HashMap::new(); + for (nonce, request) in self.inflight_discoveries.drain() { + // Exclude canceled requests + if request.reply_tx.is_canceled() { + continue; + } + + // Requests for this public key are collected + if &request.public_key == public_key { + requests.push(request); + continue; + } + + // Everything else is put back in inflight_discoveries + remaining_requests.insert(nonce, request); + } + + self.inflight_discoveries = remaining_requests; + requests + } + + fn handle_discovery_response(&mut self, discovery_msg: DiscoveryResponseMessage) { + trace!( + target: LOG_TARGET, + "Received discovery response message from {}", + discovery_msg.node_id.to_hex() + ); + if let Some(request) = log_if_error!( + target: LOG_TARGET, + self.inflight_discoveries + .remove(&discovery_msg.nonce) + .ok_or_else(|| DhtDiscoveryError::InflightDiscoveryRequestNotFound), + "{error}", + ) { + let DiscoveryRequestState { public_key, reply_tx } = request; + + let result = self.validate_then_add_peer(&public_key, discovery_msg); + + // Resolve any other pending discover requests if the peer was found + if let Ok(peer) = &result { + for request in self.collect_all_discovery_requests(&public_key) { + let _ = request.reply_tx.send(Ok(peer.clone())); + } + } + + trace!(target: LOG_TARGET, "Discovery request is recognised and valid"); + + let _ = reply_tx.send(result); + } + } + + fn validate_then_add_peer( + &mut self, + public_key: &CommsPublicKey, + discovery_msg: DiscoveryResponseMessage, + ) -> Result + { + let node_id = self.validate_raw_node_id(&public_key, &discovery_msg.node_id)?; + + let addresses = discovery_msg + .addresses + .into_iter() + .filter_map(|addr| addr.parse().ok()) + .collect::>(); + + let peer = self.add_or_update_peer( + &public_key, + node_id, + addresses, + PeerFeatures::from_bits_truncate(discovery_msg.peer_features), + )?; + + Ok(peer) + } + + fn validate_raw_node_id( + &self, + public_key: &CommsPublicKey, + raw_node_id: &[u8], + ) -> Result + { + // The reason that we check the given node id against what we expect instead of just using the given node id + // is in future the NodeId may not necessarily be derived from the public key (i.e. DAN node is registered on + // the base layer) + let expected_node_id = NodeId::from_key(public_key).map_err(|_| DhtDiscoveryError::InvalidNodeId)?; + let node_id = NodeId::from_bytes(raw_node_id).map_err(|_| DhtDiscoveryError::InvalidNodeId)?; + if expected_node_id == node_id { + Ok(expected_node_id) + } else { + // TODO: Misbehaviour + Err(DhtDiscoveryError::InvalidNodeId) + } + } + + fn add_or_update_peer( + &self, + pubkey: &CommsPublicKey, + node_id: NodeId, + net_addresses: Vec, + peer_features: PeerFeatures, + ) -> Result + { + let peer_manager = &self.peer_manager; + if peer_manager.exists(pubkey) { + peer_manager.update_peer( + pubkey, + Some(node_id), + Some(net_addresses), + None, + Some(peer_features), + None, + )?; + } else { + peer_manager.add_peer(Peer::new( + pubkey.clone(), + node_id, + net_addresses.into(), + PeerFlags::default(), + peer_features, + ))?; + } + + let peer = peer_manager.find_by_public_key(&pubkey)?; + + Ok(peer) + } + + async fn initiate_peer_discovery( + &mut self, + discovery_request: DiscoverPeerRequest, + reply_tx: oneshot::Sender>, + ) -> Result<(), DhtDiscoveryError> + { + let nonce = OsRng.next_u64(); + let public_key = discovery_request.dest_public_key.clone(); + self.send_discover(nonce, discovery_request).await?; + + let inflight_count = self.inflight_discoveries.len(); + + // Take this opportunity to clear cancelled discovery requests (e.g if the caller has timed out the request) + self.inflight_discoveries = self + .inflight_discoveries + .drain() + .filter(|(_, state)| !state.reply_tx.is_canceled()) + .collect(); + + trace!( + target: LOG_TARGET, + "{} inflight request(s) cleared", + inflight_count - self.inflight_discoveries.len() + ); + + // Add the new inflight request. + let key_exists = self + .inflight_discoveries + .insert(nonce, DiscoveryRequestState { reply_tx, public_key }) + .is_some(); + // The nonce should never be chosen more than once + debug_assert!(!key_exists); + + trace!( + target: LOG_TARGET, + "Number of inflight discoveries = {}", + self.inflight_discoveries.len() + ); + + Ok(()) + } + + async fn send_discover( + &mut self, + nonce: u64, + discovery_request: DiscoverPeerRequest, + ) -> Result<(), DhtDiscoveryError> + { + let DiscoverPeerRequest { + dest_node_id, + dest_public_key, + destination, + } = discovery_request; + + // If the destination node is is known, send to the closest peers we know. Otherwise... + let network_location_node_id = dest_node_id + .or_else(|| match &destination { + // ... if the destination is undisclosed or a public key, send discover to our closest peers + NodeDestination::Unknown | NodeDestination::PublicKey(_) => Some(self.node_identity.node_id().clone()), + // otherwise, send it to the closest peers to the given NodeId destination we know + NodeDestination::NodeId(node_id) => Some(node_id.clone()), + }) + .expect("cannot fail"); + + let discover_msg = DiscoveryMessage { + node_id: self.node_identity.node_id().to_vec(), + addresses: vec![self.node_identity.public_address().to_string()], + peer_features: self.node_identity.features().bits(), + nonce, + }; + debug!( + target: LOG_TARGET, + "Sending Discover message to (at most) {} closest peers", self.config.num_neighbouring_nodes + ); + + self.outbound_requester + .send_message_no_header( + SendMessageParams::new() + .closest(network_location_node_id, self.config.num_neighbouring_nodes, Vec::new()) + .with_destination(destination) + .with_encryption(OutboundEncryption::EncryptFor(dest_public_key)) + .with_dht_message_type(DhtMessageType::Discovery) + .finish(), + discover_msg, + ) + .await?; + + Ok(()) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::{ + discovery::DhtDiscoveryRequester, + outbound::mock::create_outbound_service_mock, + test_utils::{make_node_identity, make_peer_manager}, + }; + use std::time::Duration; + use tari_shutdown::Shutdown; + use tari_test_utils::runtime; + + #[test] + fn send_discovery() { + runtime::test_async(|rt| { + let node_identity = make_node_identity(); + let peer_manager = make_peer_manager(); + let (outbound_requester, outbound_mock) = create_outbound_service_mock(10); + let oms_mock_state = outbound_mock.get_state(); + rt.spawn(outbound_mock.run()); + + let (sender, receiver) = mpsc::channel(10); + // Requester which timeout instantly + let mut requester = DhtDiscoveryRequester::new(sender, Duration::from_millis(1)); + let mut shutdown = Shutdown::new(); + + let service = DhtDiscoveryService::new( + DhtConfig::default(), + node_identity, + peer_manager, + outbound_requester, + receiver, + shutdown.to_signal(), + ); + + rt.spawn(service.run()); + + let dest_public_key = CommsPublicKey::default(); + let result = rt.block_on(requester.discover_peer(dest_public_key.clone(), None, NodeDestination::Unknown)); + + assert!(result.unwrap_err().is_timeout()); + + oms_mock_state.wait_call_count(1, Duration::from_secs(5)).unwrap(); + let (params, _) = oms_mock_state.pop_call().unwrap(); + assert_eq!(params.dht_message_type, DhtMessageType::Discovery); + assert_eq!(params.encryption, OutboundEncryption::EncryptFor(dest_public_key)); + + shutdown.trigger().unwrap(); + }) + } +} diff --git a/comms/dht/src/domain_message.rs b/comms/dht/src/domain_message.rs new file mode 100644 index 0000000000..f255634778 --- /dev/null +++ b/comms/dht/src/domain_message.rs @@ -0,0 +1,65 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use rand::RngCore; + +pub trait ToProtoEnum { + fn as_i32(&self) -> i32; +} + +impl ToProtoEnum for i32 { + fn as_i32(&self) -> i32 { + *self + } +} + +pub struct OutboundDomainMessage { + inner: T, + message_type: i32, +} + +impl OutboundDomainMessage { + pub fn new(message_type: M, message: T) -> Self { + Self { + inner: message, + message_type: message_type.as_i32(), + } + } + + pub fn into_inner(self) -> T { + self.inner + } + + pub fn to_header(&self) -> MessageHeader { + MessageHeader::new(self.message_type) + } +} + +pub use crate::proto::message_header::MessageHeader; +use rand::rngs::OsRng; + +impl MessageHeader { + pub fn new(message_type: i32) -> Self { + let nonce = OsRng.next_u64(); + Self { message_type, nonce } + } +} diff --git a/comms/dht/src/envelope.rs b/comms/dht/src/envelope.rs new file mode 100644 index 0000000000..3a77fdcbdf --- /dev/null +++ b/comms/dht/src/envelope.rs @@ -0,0 +1,298 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{consts::DHT_ENVELOPE_HEADER_VERSION, proto::envelope::DhtOrigin}; +use bitflags::bitflags; +use derive_error::Error; +use serde::{Deserialize, Serialize}; +use std::{ + convert::{TryFrom, TryInto}, + fmt, + fmt::Display, +}; +use tari_comms::{peer_manager::NodeId, types::CommsPublicKey, utils::signature}; +use tari_crypto::tari_utilities::{hex::Hex, ByteArray, ByteArrayError}; + +// Re-export applicable protos +pub use crate::proto::envelope::{dht_header::Destination, DhtEnvelope, DhtHeader, DhtMessageType, Network}; + +#[derive(Debug, Error)] +pub enum DhtMessageError { + /// Invalid node destination + InvalidDestination, + /// Invalid origin public key + InvalidOrigin, + /// Invalid or unrecognised DHT message type + InvalidMessageType, + /// Invalid or unrecognised network type + InvalidNetwork, + /// Invalid or unrecognised DHT message flags + InvalidMessageFlags, + /// Header was omitted from the message + HeaderOmitted, +} + +bitflags! { + /// Used to indicate characteristics of the incoming or outgoing message, such + /// as whether the message is encrypted. + #[derive(Deserialize, Serialize, Default)] + pub struct DhtMessageFlags: u32 { + const NONE = 0b0000_0000; + const ENCRYPTED = 0b0000_0001; + } +} + +impl DhtMessageType { + pub fn is_dht_message(self) -> bool { + match self { + DhtMessageType::None => false, + _ => true, + } + } +} + +#[derive(Clone, PartialEq, Eq)] +pub struct DhtMessageOrigin { + pub public_key: CommsPublicKey, + pub signature: Vec, +} + +impl fmt::Debug for DhtMessageOrigin { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("DhtMessageOrigin") + .field("public_key", &self.public_key.to_hex()) + .field("signature", &self.signature.to_hex()) + .finish() + } +} + +impl TryFrom for DhtMessageOrigin { + type Error = DhtMessageError; + + fn try_from(value: DhtOrigin) -> Result { + Ok(Self { + public_key: CommsPublicKey::from_bytes(&value.public_key).map_err(|_| DhtMessageError::InvalidOrigin)?, + signature: value.signature, + }) + } +} + +impl From for DhtOrigin { + fn from(value: DhtMessageOrigin) -> Self { + Self { + public_key: value.public_key.to_vec(), + signature: value.signature, + } + } +} + +/// This struct mirrors the protobuf version of DhtHeader but is more ergonomic to work with. +/// It is preferable to not to expose the generated prost structs publicly. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct DhtMessageHeader { + pub version: u32, + pub destination: NodeDestination, + /// Origin of the message. This can refer to the same peer that sent the message + /// or another peer if the message should be forwarded. + pub origin: Option, + pub message_type: DhtMessageType, + pub network: Network, + pub flags: DhtMessageFlags, +} + +impl DhtMessageHeader { + pub fn new( + destination: NodeDestination, + message_type: DhtMessageType, + origin: Option, + network: Network, + flags: DhtMessageFlags, + ) -> Self + { + Self { + version: DHT_ENVELOPE_HEADER_VERSION, + destination, + origin, + message_type, + network, + flags, + } + } +} + +impl TryFrom for DhtMessageHeader { + type Error = DhtMessageError; + + fn try_from(header: DhtHeader) -> Result { + let destination = header + .destination + .map(|destination| destination.try_into().ok()) + .filter(Option::is_some) + .map(Option::unwrap) + .ok_or_else(|| DhtMessageError::InvalidDestination)?; + + let origin = match header.origin { + Some(origin) => Some(origin.try_into()?), + None => None, + }; + + Ok(Self { + version: header.version, + destination, + origin, + message_type: DhtMessageType::from_i32(header.message_type) + .ok_or_else(|| DhtMessageError::InvalidMessageType)?, + network: Network::from_i32(header.network).ok_or_else(|| DhtMessageError::InvalidNetwork)?, + flags: DhtMessageFlags::from_bits(header.flags).ok_or_else(|| DhtMessageError::InvalidMessageFlags)?, + }) + } +} + +impl TryFrom> for DhtMessageHeader { + type Error = DhtMessageError; + + fn try_from(header: Option) -> Result { + match header { + Some(header) => header.try_into(), + None => Err(DhtMessageError::HeaderOmitted), + } + } +} + +impl From for DhtHeader { + fn from(header: DhtMessageHeader) -> Self { + Self { + version: header.version, + origin: header.origin.map(Into::into), + destination: Some(header.destination.into()), + message_type: header.message_type as i32, + network: header.network as i32, + flags: header.flags.bits(), + } + } +} + +impl DhtEnvelope { + pub fn new(header: DhtHeader, body: Vec) -> Self { + Self { + header: Some(header), + body, + } + } + + /// Returns true if the header and origin are present, otherwise false + pub fn has_origin(&self) -> bool { + self.header.as_ref().map(|h| h.origin.is_some()).unwrap_or(false) + } + + /// Verifies the origin signature and returns true if it is valid. + /// + /// This method panics if called on an envelope without an origin. This should be checked before calling this + /// function by using the `DhtEnvelope::has_origin` method + pub fn is_origin_signature_valid(&self) -> bool { + self.header + .as_ref() + .and_then(|header| { + let origin = header + .origin + .as_ref() + .expect("call is_origin_signature_valid on envelope without origin"); + + CommsPublicKey::from_bytes(&origin.public_key) + .map(|pk| (pk, &origin.signature)) + .ok() + }) + .map(|(origin_public_key, origin_signature)| { + match signature::verify(&origin_public_key, origin_signature, &self.body) { + Ok(is_valid) => is_valid, + // error means that the signature could not deserialize, so is invalid + Err(_) => false, + } + }) + .unwrap_or(false) + } +} + +/// Represents the ways a destination node can be represented. +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] +pub enum NodeDestination { + /// The sender has chosen not to disclose the message destination, or the destination is + /// the peer being sent to. + Unknown, + /// Destined for a particular public key + PublicKey(CommsPublicKey), + /// Destined for a particular node id, or network region + NodeId(NodeId), +} + +impl NodeDestination { + pub fn to_inner_bytes(&self) -> Vec { + match self { + NodeDestination::Unknown => Vec::default(), + NodeDestination::PublicKey(pk) => pk.to_vec(), + NodeDestination::NodeId(node_id) => node_id.to_vec(), + } + } +} + +impl Display for NodeDestination { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + match self { + NodeDestination::Unknown => write!(f, "Unknown"), + NodeDestination::NodeId(node_id) => write!(f, "NodeId({})", node_id), + NodeDestination::PublicKey(public_key) => write!(f, "PublicKey({})", public_key), + } + } +} + +impl Default for NodeDestination { + fn default() -> Self { + NodeDestination::Unknown + } +} + +impl TryFrom for NodeDestination { + type Error = ByteArrayError; + + fn try_from(destination: Destination) -> Result { + match destination { + Destination::Unknown(_) => Ok(NodeDestination::Unknown), + Destination::PublicKey(pk) => { + CommsPublicKey::from_bytes(&pk).and_then(|pk| Ok(NodeDestination::PublicKey(pk))) + }, + Destination::NodeId(node_id) => { + NodeId::from_bytes(&node_id).and_then(|node_id| Ok(NodeDestination::NodeId(node_id))) + }, + } + } +} + +impl From for Destination { + fn from(destination: NodeDestination) -> Self { + use NodeDestination::*; + match destination { + Unknown => Destination::Unknown(true), + PublicKey(pk) => Destination::PublicKey(pk.to_vec()), + NodeId(node_id) => Destination::NodeId(node_id.to_vec()), + } + } +} diff --git a/comms/dht/src/inbound/decryption.rs b/comms/dht/src/inbound/decryption.rs new file mode 100644 index 0000000000..df76670961 --- /dev/null +++ b/comms/dht/src/inbound/decryption.rs @@ -0,0 +1,260 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + crypt, + envelope::DhtMessageFlags, + inbound::message::{DecryptedDhtMessage, DhtInboundMessage}, + PipelineError, +}; +use futures::{task::Context, Future}; +use log::*; +use prost::Message; +use std::{sync::Arc, task::Poll}; +use tari_comms::{message::EnvelopeBody, peer_manager::NodeIdentity}; +use tower::{layer::Layer, Service, ServiceExt}; + +const LOG_TARGET: &str = "comms::middleware::encryption"; + +/// This layer is responsible for attempting to decrypt inbound messages. +pub struct DecryptionLayer { + node_identity: Arc, +} + +impl DecryptionLayer { + pub fn new(node_identity: Arc) -> Self { + Self { node_identity } + } +} + +impl Layer for DecryptionLayer { + type Service = DecryptionService; + + fn layer(&self, service: S) -> Self::Service { + DecryptionService::new(service, Arc::clone(&self.node_identity)) + } +} + +/// Responsible for decrypting InboundMessages and passing a DecryptedInboundMessage to the given service +#[derive(Clone)] +pub struct DecryptionService { + node_identity: Arc, + inner: S, +} + +impl DecryptionService { + pub fn new(service: S, node_identity: Arc) -> Self { + Self { + inner: service, + node_identity, + } + } +} + +impl Service for DecryptionService +where + S: Service + Clone, + S::Error: Into, +{ + type Error = PipelineError; + type Response = (); + + type Future = impl Future>; + + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, msg: DhtInboundMessage) -> Self::Future { + Self::handle_message(self.inner.clone(), Arc::clone(&self.node_identity), msg) + } +} + +impl DecryptionService +where + S: Service, + S::Error: Into, +{ + async fn handle_message( + next_service: S, + node_identity: Arc, + message: DhtInboundMessage, + ) -> Result<(), PipelineError> + { + let dht_header = &message.dht_header; + if !dht_header.flags.contains(DhtMessageFlags::ENCRYPTED) { + return Self::success_not_encrypted(next_service, message).await; + } + + let origin = dht_header + .origin + .as_ref() + // TODO: #banheuristics - this should not have been sent/propagated + .ok_or_else(|| "Message origin field is required for encrypted messages")?; + + debug!(target: LOG_TARGET, "Attempting to decrypt message"); + let shared_secret = crypt::generate_ecdh_secret(node_identity.secret_key(), &origin.public_key); + match crypt::decrypt(&shared_secret, &message.body) { + Ok(decrypted) => Self::decryption_succeeded(next_service, message, &decrypted).await, + Err(err) => { + debug!(target: LOG_TARGET, "Unable to decrypt message: {}", err); + Self::decryption_failed(next_service, message).await + }, + } + } + + async fn decryption_succeeded( + next_service: S, + message: DhtInboundMessage, + decrypted: &[u8], + ) -> Result<(), PipelineError> + { + // Deserialization into an EnvelopeBody is done here to determine if the + // decryption produced valid bytes or not. + let result = EnvelopeBody::decode(decrypted).and_then(|body| { + // Check if we received a body length of zero + // + // In addition to a peer sending a zero-length EnvelopeBody, decoding can erroneously succeed + // if the decrypted bytes happen to be valid protobuf encoding. This is very possible and + // the decrypt_inbound_fail test below _will_ sporadically fail without the following check. + // This is because proto3 will set fields to their default value if they don't exist in a valid encoding. + // + // For the parts of EnvelopeBody to be erroneously populated with bytes, all of these + // conditions would have to be true: + // 1. field type == 2 (length-delimited) + // 2. field number == 1 + // 3. the subsequent byte(s) would have to be varint-encoded length which does not overflow + // 4. the rest of the bytes would have to be valid protobuf encoding + // + // The chance of this happening is extremely negligible. + if body.is_empty() { + return Err(prost::DecodeError::new("EnvelopeBody has no parts")); + } + Ok(body) + }); + match result { + Ok(deserialized) => { + debug!(target: LOG_TARGET, "Message successfully decrypted"); + let msg = DecryptedDhtMessage::succeeded(deserialized, message); + next_service.oneshot(msg).await.map_err(Into::into) + }, + Err(err) => { + debug!(target: LOG_TARGET, "Unable to deserialize message: {}", err); + Self::decryption_failed(next_service, message).await + }, + } + } + + async fn success_not_encrypted(next_service: S, message: DhtInboundMessage) -> Result<(), PipelineError> { + match EnvelopeBody::decode(message.body.as_slice()) { + Ok(deserialized) => { + debug!( + target: LOG_TARGET, + "Message is not encrypted. Passing onto next service" + ); + let msg = DecryptedDhtMessage::succeeded(deserialized, message); + next_service.oneshot(msg).await.map_err(Into::into) + }, + Err(err) => { + // Message was not encrypted but failed to deserialize - immediately discard + // TODO: Bad node behaviour? + debug!( + target: LOG_TARGET, + "Unable to deserialize message: {}. Message will be discarded.", err + ); + Ok(()) + }, + } + } + + async fn decryption_failed(next_service: S, message: DhtInboundMessage) -> Result<(), PipelineError> { + let msg = DecryptedDhtMessage::failed(message); + next_service.oneshot(msg).await.map_err(Into::into) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::{ + envelope::DhtMessageFlags, + test_utils::{make_dht_inbound_message, make_node_identity, service_fn}, + }; + use futures::{executor::block_on, future}; + use std::sync::Mutex; + use tari_comms::{message::MessageExt, wrap_in_envelope_body}; + use tari_test_utils::counter_context; + + #[test] + fn poll_ready() { + let inner = service_fn(|_: DecryptedDhtMessage| future::ready(Result::<(), PipelineError>::Ok(()))); + let node_identity = make_node_identity(); + let mut service = DecryptionService::new(inner, node_identity); + + counter_context!(cx, counter); + + assert!(service.poll_ready(&mut cx).is_ready()); + + assert_eq!(counter.get(), 0); + } + + #[test] + fn decrypt_inbound_success() { + let result = Mutex::new(None); + let inner = service_fn(|msg: DecryptedDhtMessage| { + *result.lock().unwrap() = Some(msg); + future::ready(Result::<(), PipelineError>::Ok(())) + }); + let node_identity = make_node_identity(); + let mut service = DecryptionService::new(inner, Arc::clone(&node_identity)); + + let plain_text_msg = wrap_in_envelope_body!(Vec::new()).unwrap(); + let secret_key = crypt::generate_ecdh_secret(node_identity.secret_key(), node_identity.public_key()); + let encrypted = crypt::encrypt(&secret_key, &plain_text_msg.to_encoded_bytes().unwrap()).unwrap(); + let inbound_msg = make_dht_inbound_message(&node_identity, encrypted, DhtMessageFlags::ENCRYPTED); + + block_on(service.call(inbound_msg)).unwrap(); + let decrypted = result.lock().unwrap().take().unwrap(); + assert_eq!(decrypted.decryption_succeeded(), true); + assert_eq!(decrypted.decryption_result.unwrap(), plain_text_msg); + } + + #[test] + fn decrypt_inbound_fail() { + let result = Mutex::new(None); + let inner = service_fn(|msg: DecryptedDhtMessage| { + *result.lock().unwrap() = Some(msg); + future::ready(Result::<(), PipelineError>::Ok(())) + }); + let node_identity = make_node_identity(); + let mut service = DecryptionService::new(inner, Arc::clone(&node_identity)); + + let nonsense = "Cannot Decrypt this".as_bytes().to_vec(); + let inbound_msg = make_dht_inbound_message(&node_identity, nonsense.clone(), DhtMessageFlags::ENCRYPTED); + + block_on(service.call(inbound_msg)).unwrap(); + let decrypted = result.lock().unwrap().take().unwrap(); + + assert_eq!(decrypted.decryption_succeeded(), false); + assert_eq!(decrypted.decryption_result.unwrap_err(), nonsense); + } +} diff --git a/comms/dht/src/inbound/dedup.rs b/comms/dht/src/inbound/dedup.rs new file mode 100644 index 0000000000..ca32b2a320 --- /dev/null +++ b/comms/dht/src/inbound/dedup.rs @@ -0,0 +1,162 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{actor::DhtRequester, inbound::DhtInboundMessage, PipelineError}; +use digest::Input; +use futures::{task::Context, Future}; +use log::*; +use std::task::Poll; +use tari_comms::types::Challenge; +use tari_crypto::tari_utilities::hex::Hex; +use tower::{layer::Layer, Service, ServiceExt}; + +const LOG_TARGET: &str = "comms::dht::dedup"; + +/// # DHT Deduplication middleware +/// +/// Takes in a `DhtInboundMessage` and checks the message signature cache for duplicates. +/// If a duplicate message is detected, it is discarded. +#[derive(Clone)] +pub struct DedupMiddleware { + next_service: S, + dht_requester: DhtRequester, +} + +impl DedupMiddleware { + pub fn new(service: S, dht_requester: DhtRequester) -> Self { + Self { + next_service: service, + dht_requester, + } + } +} + +impl Service for DedupMiddleware +where + S: Service + Clone + 'static, + S::Error: Into, +{ + type Error = PipelineError; + type Response = (); + + type Future = impl Future>; + + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, msg: DhtInboundMessage) -> Self::Future { + Self::process_message(self.next_service.clone(), self.dht_requester.clone(), msg) + } +} + +impl DedupMiddleware +where + S: Service, + S::Error: Into, +{ + pub async fn process_message( + next_service: S, + mut dht_requester: DhtRequester, + message: DhtInboundMessage, + ) -> Result<(), PipelineError> + { + trace!(target: LOG_TARGET, "Checking inbound message cache for duplicates"); + let hash = Self::hash_message(&message); + if dht_requester.insert_message_hash(hash).await? { + warn!( + target: LOG_TARGET, + "Received duplicate message from peer {} (origin={:?}). Message discarded.", + message.source_peer.node_id, + message + .dht_header + .origin + .map(|o| o.public_key.to_hex()) + .unwrap_or_else(|| "".to_string()), + ); + return Ok(()); + } + next_service.oneshot(message).await.map_err(Into::into) + } + + fn hash_message(message: &DhtInboundMessage) -> Vec { + Challenge::new().chain(&message.body).result().to_vec() + } +} + +pub struct DedupLayer { + dht_requester: DhtRequester, +} + +impl DedupLayer { + pub fn new(dht_requester: DhtRequester) -> Self { + Self { dht_requester } + } +} + +impl Layer for DedupLayer { + type Service = DedupMiddleware; + + fn layer(&self, service: S) -> Self::Service { + DedupMiddleware::new(service, self.dht_requester.clone()) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::{ + envelope::DhtMessageFlags, + test_utils::{create_dht_actor_mock, make_dht_inbound_message, make_node_identity, service_spy, DhtMockState}, + }; + use tari_test_utils::panic_context; + use tokio::runtime::Runtime; + + #[test] + fn process_message() { + let mut rt = Runtime::new().unwrap(); + let spy = service_spy(); + + let (dht_requester, mut mock) = create_dht_actor_mock(1); + let mock_state = DhtMockState::new(); + mock_state.set_signature_cache_insert(false); + mock.set_shared_state(mock_state.clone()); + rt.spawn(mock.run()); + + let mut dedup = DedupLayer::new(dht_requester).layer(spy.to_service::()); + + panic_context!(cx); + + assert!(dedup.poll_ready(&mut cx).is_ready()); + let node_identity = make_node_identity(); + let msg = make_dht_inbound_message(&node_identity, Vec::new(), DhtMessageFlags::empty()); + + rt.block_on(dedup.call(msg.clone())).unwrap(); + assert_eq!(spy.call_count(), 1); + + mock_state.set_signature_cache_insert(true); + rt.block_on(dedup.call(msg)).unwrap(); + assert_eq!(spy.call_count(), 1); + // Drop dedup so that the DhtMock will stop running + drop(dedup); + } +} diff --git a/comms/dht/src/inbound/deserialize.rs b/comms/dht/src/inbound/deserialize.rs new file mode 100644 index 0000000000..487169ae3c --- /dev/null +++ b/comms/dht/src/inbound/deserialize.rs @@ -0,0 +1,159 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{inbound::DhtInboundMessage, proto::envelope::DhtEnvelope, PipelineError}; +use futures::{task::Context, Future}; +use log::*; +use prost::Message; +use std::{convert::TryInto, task::Poll}; +use tari_comms::message::InboundMessage; +use tower::{layer::Layer, Service, ServiceExt}; + +const LOG_TARGET: &str = "comms::dht::deserialize"; + +/// # DHT Deserialization middleware +/// +/// Takes in an `InboundMessage` and deserializes the body into a [DhtEnvelope]. +/// The `next_service` is called with a constructed [DhtInboundMessage] which contains +/// the relevant comms-level and dht-level information. +#[derive(Clone)] +pub struct DhtDeserializeMiddleware { + next_service: S, +} + +impl DhtDeserializeMiddleware { + pub fn new(service: S) -> Self { + Self { next_service: service } + } +} + +impl Service for DhtDeserializeMiddleware +where + S: Service + Clone + 'static, + S::Error: Into, +{ + type Error = PipelineError; + type Response = (); + + type Future = impl Future>; + + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, msg: InboundMessage) -> Self::Future { + Self::deserialize(self.next_service.clone(), msg) + } +} + +impl DhtDeserializeMiddleware +where + S: Service, + S::Error: Into, +{ + pub async fn deserialize(mut next_service: S, message: InboundMessage) -> Result<(), PipelineError> { + trace!(target: LOG_TARGET, "Deserializing InboundMessage"); + next_service.ready().await.map_err(Into::into)?; + + let InboundMessage { + source_peer, mut body, .. + } = message; + + match DhtEnvelope::decode(&mut body) { + Ok(dht_envelope) => { + trace!(target: LOG_TARGET, "Deserialization succeeded. Checking signatures"); + if dht_envelope.has_origin() { + if dht_envelope.is_origin_signature_valid() { + trace!(target: LOG_TARGET, "Origin signature validation passed."); + } else { + // The origin signature is not valid, this message should never have been sent + warn!( + target: LOG_TARGET, + "SECURITY: Origin signature verification failed. Discarding message from NodeId {}", + source_peer.node_id + ); + return Ok(()); + } + } + + let inbound_msg = + DhtInboundMessage::new(dht_envelope.header.try_into()?, source_peer, dht_envelope.body); + next_service.call(inbound_msg).await.map_err(Into::into) + }, + Err(err) => { + error!(target: LOG_TARGET, "DHT deserialization failed: {}", err); + Err(err.into()) + }, + } + } +} + +#[derive(Default)] +pub struct DeserializeLayer; + +impl DeserializeLayer { + pub fn new() -> Self { + DeserializeLayer + } +} + +impl Layer for DeserializeLayer { + type Service = DhtDeserializeMiddleware; + + fn layer(&self, service: S) -> Self::Service { + DhtDeserializeMiddleware::new(service) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::{ + envelope::DhtMessageFlags, + test_utils::{make_comms_inbound_message, make_dht_envelope, make_node_identity, service_spy}, + }; + use futures::executor::block_on; + use tari_comms::message::{MessageExt, MessageFlags}; + use tari_test_utils::panic_context; + + #[test] + fn deserialize() { + let spy = service_spy(); + let mut deserialize = DeserializeLayer::new().layer(spy.to_service::()); + + panic_context!(cx); + + assert!(deserialize.poll_ready(&mut cx).is_ready()); + let node_identity = make_node_identity(); + let dht_envelope = make_dht_envelope(&node_identity, b"A".to_vec(), DhtMessageFlags::empty()); + block_on(deserialize.call(make_comms_inbound_message( + &node_identity, + dht_envelope.to_encoded_bytes().unwrap().into(), + MessageFlags::empty(), + ))) + .unwrap(); + + let msg = spy.pop_request().unwrap(); + assert_eq!(msg.body, b"A".to_vec()); + assert_eq!(msg.dht_header, dht_envelope.header.unwrap().try_into().unwrap()); + } +} diff --git a/comms/dht/src/inbound/dht_handler/layer.rs b/comms/dht/src/inbound/dht_handler/layer.rs new file mode 100644 index 0000000000..cecb76f3b0 --- /dev/null +++ b/comms/dht/src/inbound/dht_handler/layer.rs @@ -0,0 +1,69 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::middleware::DhtHandlerMiddleware; +use crate::{config::DhtConfig, discovery::DhtDiscoveryRequester, outbound::OutboundMessageRequester}; +use std::sync::Arc; +use tari_comms::peer_manager::{NodeIdentity, PeerManager}; +use tower::layer::Layer; + +pub struct DhtHandlerLayer { + config: DhtConfig, + peer_manager: Arc, + node_identity: Arc, + outbound_service: OutboundMessageRequester, + discovery_requester: DhtDiscoveryRequester, +} + +impl DhtHandlerLayer { + pub fn new( + config: DhtConfig, + node_identity: Arc, + peer_manager: Arc, + discovery_requester: DhtDiscoveryRequester, + outbound_service: OutboundMessageRequester, + ) -> Self + { + Self { + config, + node_identity, + peer_manager, + discovery_requester, + outbound_service, + } + } +} + +impl Layer for DhtHandlerLayer { + type Service = DhtHandlerMiddleware; + + fn layer(&self, service: S) -> Self::Service { + DhtHandlerMiddleware::new( + self.config.clone(), + service, + Arc::clone(&self.node_identity), + Arc::clone(&self.peer_manager), + self.outbound_service.clone(), + self.discovery_requester.clone(), + ) + } +} diff --git a/comms/dht/src/inbound/dht_handler/middleware.rs b/comms/dht/src/inbound/dht_handler/middleware.rs new file mode 100644 index 0000000000..d01a5b5f47 --- /dev/null +++ b/comms/dht/src/inbound/dht_handler/middleware.rs @@ -0,0 +1,94 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::task::ProcessDhtMessage; +use crate::{ + config::DhtConfig, + discovery::DhtDiscoveryRequester, + inbound::DecryptedDhtMessage, + outbound::OutboundMessageRequester, + PipelineError, +}; +use futures::{task::Context, Future}; +use std::{sync::Arc, task::Poll}; +use tari_comms::peer_manager::{NodeIdentity, PeerManager}; +use tower::Service; + +#[derive(Clone)] +pub struct DhtHandlerMiddleware { + config: DhtConfig, + next_service: S, + peer_manager: Arc, + node_identity: Arc, + outbound_service: OutboundMessageRequester, + discovery_requester: DhtDiscoveryRequester, +} + +impl DhtHandlerMiddleware { + pub fn new( + config: DhtConfig, + next_service: S, + node_identity: Arc, + peer_manager: Arc, + outbound_service: OutboundMessageRequester, + + discovery_requester: DhtDiscoveryRequester, + ) -> Self + { + Self { + config, + next_service, + node_identity, + peer_manager, + outbound_service, + discovery_requester, + } + } +} + +impl Service for DhtHandlerMiddleware +where + S: Service + Clone, + S::Error: Into, +{ + type Error = PipelineError; + type Response = (); + + type Future = impl Future>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.next_service.poll_ready(cx).map_err(Into::into) + } + + fn call(&mut self, message: DecryptedDhtMessage) -> Self::Future { + ProcessDhtMessage::new( + self.config.clone(), + self.next_service.clone(), + Arc::clone(&self.peer_manager), + self.outbound_service.clone(), + Arc::clone(&self.node_identity), + self.discovery_requester.clone(), + message, + ) + .run() + } +} diff --git a/infrastructure/broadcast_channel/benches/benches_main.rs b/comms/dht/src/inbound/dht_handler/mod.rs similarity index 95% rename from infrastructure/broadcast_channel/benches/benches_main.rs rename to comms/dht/src/inbound/dht_handler/mod.rs index ab0846aff2..19af08ac4a 100644 --- a/infrastructure/broadcast_channel/benches/benches_main.rs +++ b/comms/dht/src/inbound/dht_handler/mod.rs @@ -20,8 +20,8 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use criterion::criterion_main; +mod layer; +mod middleware; +mod task; -mod channel; - -criterion_main!(channel::benches); +pub use layer::DhtHandlerLayer; diff --git a/comms/dht/src/inbound/dht_handler/task.rs b/comms/dht/src/inbound/dht_handler/task.rs new file mode 100644 index 0000000000..0bac22ccda --- /dev/null +++ b/comms/dht/src/inbound/dht_handler/task.rs @@ -0,0 +1,401 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + config::DhtConfig, + discovery::DhtDiscoveryRequester, + envelope::NodeDestination, + inbound::{error::DhtInboundError, message::DecryptedDhtMessage}, + outbound::{OutboundMessageRequester, SendMessageParams}, + proto::{ + dht::{DiscoveryMessage, DiscoveryResponseMessage, JoinMessage}, + envelope::DhtMessageType, + }, + PipelineError, +}; +use log::*; +use std::sync::Arc; +use tari_comms::{ + message::MessageExt, + multiaddr::Multiaddr, + peer_manager::{NodeId, NodeIdentity, Peer, PeerFeatures, PeerFlags, PeerManager}, + types::CommsPublicKey, +}; +use tari_crypto::tari_utilities::{hex::Hex, ByteArray}; +use tower::{Service, ServiceExt}; + +const LOG_TARGET: &str = "comms::dht::dht_handler"; + +pub struct ProcessDhtMessage { + config: DhtConfig, + next_service: S, + peer_manager: Arc, + outbound_service: OutboundMessageRequester, + node_identity: Arc, + message: Option, + discovery_requester: DhtDiscoveryRequester, +} + +impl ProcessDhtMessage +where + S: Service, + S::Error: Into, +{ + pub fn new( + config: DhtConfig, + next_service: S, + peer_manager: Arc, + outbound_service: OutboundMessageRequester, + node_identity: Arc, + discovery_requester: DhtDiscoveryRequester, + message: DecryptedDhtMessage, + ) -> Self + { + Self { + config, + next_service, + peer_manager, + outbound_service, + node_identity, + discovery_requester, + message: Some(message), + } + } + + pub async fn run(mut self) -> Result<(), PipelineError> { + let message = self + .message + .take() + .expect("ProcessDhtMessage initialized without message"); + + // If this message failed to decrypt, this middleware is not interested in it + if message.decryption_failed() { + self.next_service.oneshot(message).await.map_err(Into::into)?; + return Ok(()); + } + + match message.dht_header.message_type { + DhtMessageType::Join => self.handle_join(message).await?, + DhtMessageType::Discovery => self.handle_discover(message).await?, + DhtMessageType::DiscoveryResponse => self.handle_discover_response(message).await?, + DhtMessageType::RejectMsg => self.handle_message_reject(message).await?, + // Not a DHT message, call downstream middleware + _ => { + trace!(target: LOG_TARGET, "Passing message onto next service"); + self.next_service.oneshot(message).await.map_err(Into::into)? + }, + } + + Ok(()) + } + + fn add_or_update_peer( + &self, + pubkey: &CommsPublicKey, + node_id: NodeId, + net_addresses: Vec, + peer_features: PeerFeatures, + ) -> Result + { + let peer_manager = &self.peer_manager; + // Add peer or modify existing peer using received join request + if peer_manager.exists(pubkey) { + peer_manager.update_peer( + pubkey, + Some(node_id), + Some(net_addresses), + None, + Some(peer_features), + None, + )?; + } else { + peer_manager.add_peer(Peer::new( + pubkey.clone(), + node_id, + net_addresses.into(), + PeerFlags::default(), + peer_features, + ))?; + } + + let peer = peer_manager.find_by_public_key(&pubkey)?; + + Ok(peer) + } + + fn validate_raw_node_id(&self, public_key: &CommsPublicKey, raw_node_id: &[u8]) -> Result { + // The reason that we check the given node id against what we expect instead of just using the given node id + // is in future the NodeId may not necessarily be derived from the public key (i.e. DAN node is registered on + // the base layer) + let expected_node_id = NodeId::from_key(public_key).map_err(|_| DhtInboundError::InvalidNodeId)?; + let node_id = NodeId::from_bytes(raw_node_id).map_err(|_| DhtInboundError::InvalidNodeId)?; + if expected_node_id == node_id { + Ok(expected_node_id) + } else { + // TODO: Misbehaviour? + Err(DhtInboundError::InvalidNodeId) + } + } + + async fn handle_join(&mut self, message: DecryptedDhtMessage) -> Result<(), DhtInboundError> { + let DecryptedDhtMessage { + decryption_result, + dht_header, + source_peer, + .. + } = message; + + let origin = dht_header + .origin + .as_ref() + .ok_or_else(|| DhtInboundError::OriginRequired("Origin is required for this message type".to_string()))?; + + if &origin.public_key == self.node_identity.public_key() { + trace!(target: LOG_TARGET, "Received our own join message. Discarding it."); + return Ok(()); + } + + trace!(target: LOG_TARGET, "Received Join Message from {}", origin.public_key); + + let body = decryption_result.expect("already checked that this message decrypted successfully"); + let join_msg = body + .decode_part::(0)? + .ok_or_else(|| DhtInboundError::InvalidJoinNetAddresses)?; + + let addresses = join_msg + .addresses + .into_iter() + .filter_map(|addr| addr.parse().ok()) + .collect::>(); + + if addresses.is_empty() { + return Err(DhtInboundError::InvalidAddresses); + } + + let node_id = self.validate_raw_node_id(&origin.public_key, &join_msg.node_id)?; + + let origin_peer = self.add_or_update_peer( + &origin.public_key, + node_id, + addresses, + PeerFeatures::from_bits_truncate(join_msg.peer_features), + )?; + + // DO NOT propagate this peer if this node has banned them + if origin_peer.is_banned() { + warn!( + target: LOG_TARGET, + "Received Join request for banned peer. This join request will not be propagated." + ); + return Ok(()); + } + + // Send a join request back to the origin peer of the join request if: + // - this join request was not sent directly from the origin peer but was forwarded (from the source peer), and + // - that peer is from the same region of network. + // + // If it was not forwarded then we assume the source peer already has this node's details in + // it's peer list. + if source_peer.public_key != origin_peer.public_key && + self.peer_manager.in_network_region( + &origin_peer.node_id, + self.node_identity.node_id(), + self.config.num_neighbouring_nodes, + )? + { + trace!( + target: LOG_TARGET, + "Sending Join to joining peer with public key '{}'", + origin_peer.public_key + ); + self.send_join_direct(origin_peer.public_key).await?; + } + + trace!( + target: LOG_TARGET, + "Propagating join message to at most {} peer(s)", + self.config.num_neighbouring_nodes + ); + + // Propagate message to closer peers + self.outbound_service + .send_raw( + SendMessageParams::new() + .closest(origin_peer.node_id, self.config.num_neighbouring_nodes, vec![ + origin.public_key.clone(), + source_peer.public_key.clone(), + ]) + .with_dht_header(dht_header) + .finish(), + body.to_encoded_bytes()?, + ) + .await?; + + Ok(()) + } + + async fn handle_message_reject(&mut self, message: DecryptedDhtMessage) -> Result<(), DhtInboundError> { + trace!( + target: LOG_TARGET, + "Received Message reject from {}", + message.source_peer.public_key + ); + + // TODO: Perhaps we'll need some way to let the larger system know that the message was explicitly rejected + + Ok(()) + } + + async fn handle_discover_response(&mut self, message: DecryptedDhtMessage) -> Result<(), DhtInboundError> { + trace!( + target: LOG_TARGET, + "Received Discover Response Message from {}", + message + .dht_header + .origin + .as_ref() + .map(|o| o.public_key.to_hex()) + .unwrap_or_else(|| "".to_string()) + ); + + let msg = message + .success() + .expect("already checked that this message decrypted successfully"); + + let discover_msg = msg + .decode_part::(0)? + .ok_or_else(|| DhtInboundError::InvalidMessageBody)?; + + self.discovery_requester + .notify_discovery_response_received(discover_msg) + .await?; + + Ok(()) + } + + async fn handle_discover(&mut self, message: DecryptedDhtMessage) -> Result<(), DhtInboundError> { + let msg = message + .success() + .expect("already checked that this message decrypted successfully"); + + let discover_msg = msg + .decode_part::(0)? + .ok_or_else(|| DhtInboundError::InvalidMessageBody)?; + + let origin = message.dht_header.origin.ok_or_else(|| { + DhtInboundError::OriginRequired("Origin header required for Discovery message".to_string()) + })?; + + trace!( + target: LOG_TARGET, + "Received Discover Message from {}", + origin.public_key, + ); + + let addresses = discover_msg + .addresses + .into_iter() + .filter_map(|addr| addr.parse().ok()) + .collect::>(); + + if addresses.is_empty() { + return Err(DhtInboundError::InvalidAddresses); + } + + let node_id = self.validate_raw_node_id(&origin.public_key, &discover_msg.node_id)?; + let origin_peer = self.add_or_update_peer( + &origin.public_key, + node_id, + addresses, + PeerFeatures::from_bits_truncate(discover_msg.peer_features), + )?; + + // Don't send a join request to the origin peer if they are banned + if origin_peer.is_banned() { + warn!( + target: LOG_TARGET, + "Received Discovery request for banned peer. This request will be ignored." + ); + return Ok(()); + } + + // Send the origin the current nodes latest contact info + self.send_discovery_response(origin.public_key, discover_msg.nonce) + .await?; + + Ok(()) + } + + /// Send a network join update request directly to a specific known peer + async fn send_join_direct(&mut self, dest_public_key: CommsPublicKey) -> Result<(), DhtInboundError> { + let join_msg = JoinMessage { + node_id: self.node_identity.node_id().to_vec(), + addresses: vec![self.node_identity.public_address().to_string()], + peer_features: self.node_identity.features().bits(), + }; + + trace!("Sending direct join request to {}", dest_public_key); + self.outbound_service + .send_message_no_header( + SendMessageParams::new() + .direct_public_key(dest_public_key.clone()) + .with_destination(NodeDestination::PublicKey(dest_public_key)) + .with_dht_message_type(DhtMessageType::Join) + .force_origin() + .finish(), + join_msg, + ) + .await?; + + Ok(()) + } + + /// Send a `DiscoveryResponseMessage` in response to a `DiscoveryMessage` to the given public key + /// using the given nonce which should come from the `DiscoveryMessage` + async fn send_discovery_response( + &mut self, + dest_public_key: CommsPublicKey, + nonce: u64, + ) -> Result<(), DhtInboundError> + { + let response = DiscoveryResponseMessage { + node_id: self.node_identity.node_id().to_vec(), + addresses: vec![self.node_identity.public_address().to_string()], + peer_features: self.node_identity.features().bits(), + nonce, + }; + + trace!("Sending discovery response to {}", dest_public_key); + self.outbound_service + .send_message_no_header( + SendMessageParams::new() + .direct_public_key(dest_public_key) + .with_destination(NodeDestination::Unknown) + .with_dht_message_type(DhtMessageType::DiscoveryResponse) + .finish(), + response, + ) + .await?; + + Ok(()) + } +} diff --git a/comms/dht/src/inbound/error.rs b/comms/dht/src/inbound/error.rs new file mode 100644 index 0000000000..43e548f4a6 --- /dev/null +++ b/comms/dht/src/inbound/error.rs @@ -0,0 +1,47 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{discovery::DhtDiscoveryError, outbound::DhtOutboundError}; +use derive_error::Error; +use prost::DecodeError; +use tari_comms::{message::MessageError, peer_manager::PeerManagerError}; + +#[derive(Debug, Error)] +pub enum DhtInboundError { + MessageError(MessageError), + // MessageFormatError(MessageFormatError), + PeerManagerError(PeerManagerError), + DhtOutboundError(DhtOutboundError), + /// Failed to decode message + DecodeError(DecodeError), + /// Message body invalid + InvalidMessageBody, + /// Node ID is invalid + InvalidNodeId, + /// All given addresses were invalid + InvalidAddresses, + /// One or more NetAddress in the join message were invalid + InvalidJoinNetAddresses, + DhtDiscoveryError(DhtDiscoveryError), + #[error(msg_embedded, no_from, non_std)] + OriginRequired(String), +} diff --git a/comms/dht/src/inbound/message.rs b/comms/dht/src/inbound/message.rs new file mode 100644 index 0000000000..be2b182742 --- /dev/null +++ b/comms/dht/src/inbound/message.rs @@ -0,0 +1,106 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{consts::DHT_ENVELOPE_HEADER_VERSION, envelope::DhtMessageHeader}; +use std::sync::Arc; +use tari_comms::{message::EnvelopeBody, peer_manager::Peer, types::CommsPublicKey}; + +#[derive(Debug, Clone)] +pub struct DhtInboundMessage { + pub version: u32, + pub source_peer: Arc, + pub dht_header: DhtMessageHeader, + pub body: Vec, +} +impl DhtInboundMessage { + pub fn new(dht_header: DhtMessageHeader, source_peer: Arc, body: Vec) -> Self { + Self { + version: DHT_ENVELOPE_HEADER_VERSION, + dht_header, + source_peer, + body, + } + } +} + +/// Represents a decrypted InboundMessage. +#[derive(Debug, Clone)] +pub struct DecryptedDhtMessage { + pub version: u32, + /// The _connected_ peer which sent or forwarded this message. This may not be the peer + /// which created this message. + pub source_peer: Arc, + pub dht_header: DhtMessageHeader, + pub decryption_result: Result>, +} + +impl DecryptedDhtMessage { + pub fn succeeded(decrypted_message: EnvelopeBody, message: DhtInboundMessage) -> Self { + Self { + version: message.version, + source_peer: message.source_peer, + dht_header: message.dht_header, + decryption_result: Ok(decrypted_message), + } + } + + pub fn failed(message: DhtInboundMessage) -> Self { + Self { + version: message.version, + source_peer: message.source_peer, + dht_header: message.dht_header, + decryption_result: Err(message.body), + } + } + + pub fn fail(&self) -> Option<&Vec> { + self.decryption_result.as_ref().err() + } + + pub fn fail_mut(&mut self) -> Option<&mut Vec> { + self.decryption_result.as_mut().err() + } + + pub fn success(&self) -> Option<&EnvelopeBody> { + self.decryption_result.as_ref().ok() + } + + pub fn success_mut(&mut self) -> Option<&mut EnvelopeBody> { + self.decryption_result.as_mut().ok() + } + + pub fn decryption_succeeded(&self) -> bool { + self.decryption_result.is_ok() + } + + pub fn decryption_failed(&self) -> bool { + self.decryption_result.is_err() + } + + pub fn origin_public_key(&self) -> &CommsPublicKey { + self.dht_header + .origin + .as_ref() + .map(|o| &o.public_key) + .unwrap_or(&self.source_peer.public_key) + } +} diff --git a/comms/dht/src/inbound/mod.rs b/comms/dht/src/inbound/mod.rs new file mode 100644 index 0000000000..f5373f41bf --- /dev/null +++ b/comms/dht/src/inbound/mod.rs @@ -0,0 +1,38 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod decryption; +mod dedup; +mod deserialize; +mod dht_handler; +mod error; +mod message; +mod validate; + +pub use self::{ + decryption::DecryptionLayer, + dedup::DedupLayer, + deserialize::DeserializeLayer, + dht_handler::DhtHandlerLayer, + message::{DecryptedDhtMessage, DhtInboundMessage}, + validate::ValidateLayer, +}; diff --git a/comms/dht/src/inbound/validate.rs b/comms/dht/src/inbound/validate.rs new file mode 100644 index 0000000000..50a6ade5b7 --- /dev/null +++ b/comms/dht/src/inbound/validate.rs @@ -0,0 +1,207 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + inbound::DhtInboundMessage, + outbound::{OutboundMessageRequester, SendMessageParams}, + proto::{ + dht::{RejectMessage, RejectMessageReason}, + envelope::{DhtMessageType, Network}, + }, + PipelineError, +}; +use futures::{task::Context, Future}; +use log::*; +use std::task::Poll; +use tari_comms::message::MessageExt; +use tari_crypto::tari_utilities::ByteArray; +use tower::{layer::Layer, Service, ServiceExt}; + +const LOG_TARGET: &str = "comms::dht::validate"; + +/// # DHT validation middleware +/// +/// Takes in a `DhtInboundMessage` and checks the message header for any invalid fields +/// If an invalid message is detected a rejection message is sent to the sending peer. +#[derive(Clone)] +pub struct ValidateMiddleware { + next_service: S, + target_network: Network, + outbound_requester: OutboundMessageRequester, +} + +impl ValidateMiddleware { + pub fn new(service: S, target_network: Network, outbound_requester: OutboundMessageRequester) -> Self { + Self { + next_service: service, + target_network, + outbound_requester, + } + } +} + +impl Service for ValidateMiddleware +where + S: Service + Clone + 'static, + S::Error: Into, +{ + type Error = PipelineError; + type Response = (); + + type Future = impl Future>; + + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, msg: DhtInboundMessage) -> Self::Future { + Self::process_message( + self.next_service.clone(), + self.target_network, + self.outbound_requester.clone(), + msg, + ) + } +} + +impl ValidateMiddleware +where + S: Service, + S::Error: Into, +{ + pub async fn process_message( + next_service: S, + target_network: Network, + mut outbound_requester: OutboundMessageRequester, + message: DhtInboundMessage, + ) -> Result<(), PipelineError> + { + trace!( + target: LOG_TARGET, + "Checking the message target network is '{:?}'", + target_network + ); + if message.dht_header.network == target_network { + next_service.oneshot(message).await.map_err(Into::into)?; + } else { + debug!( + target: LOG_TARGET, + "Message is for another network (want = {:?} got = {:?}). Explicitly rejecting the message.", + target_network, + message.dht_header.network + ); + outbound_requester + .send_raw( + SendMessageParams::new() + .direct_public_key(message.source_peer.public_key.clone()) + .with_dht_message_type(DhtMessageType::RejectMsg) + .finish(), + RejectMessage { + signature: message + .dht_header + .origin + .map(|o| o.public_key.to_vec()) + .unwrap_or_default(), + reason: RejectMessageReason::UnsupportedNetwork as i32, + } + .to_encoded_bytes()?, + ) + .await?; + } + + Ok(()) + } +} + +pub struct ValidateLayer { + target_network: Network, + outbound_requester: OutboundMessageRequester, +} + +impl ValidateLayer { + pub fn new(target_network: Network, outbound_requester: OutboundMessageRequester) -> Self { + Self { + target_network, + outbound_requester, + } + } +} + +impl Layer for ValidateLayer { + type Service = ValidateMiddleware; + + fn layer(&self, service: S) -> Self::Service { + ValidateMiddleware::new(service, self.target_network, self.outbound_requester.clone()) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::{ + envelope::{DhtMessageFlags, DhtMessageType}, + outbound::mock::create_outbound_service_mock, + test_utils::{make_dht_inbound_message, make_node_identity, service_spy}, + }; + use tari_test_utils::panic_context; + use tokio::runtime::Runtime; + + #[test] + fn process_message() { + let mut rt = Runtime::new().unwrap(); + let spy = service_spy(); + + let (out_requester, mock) = create_outbound_service_mock(1); + let mock_state = mock.get_state(); + rt.spawn(mock.run()); + + let mut validate = + ValidateLayer::new(Network::LocalTest, out_requester).layer(spy.to_service::()); + + panic_context!(cx); + + assert!(validate.poll_ready(&mut cx).is_ready()); + let node_identity = make_node_identity(); + let mut msg = make_dht_inbound_message(&node_identity, Vec::new(), DhtMessageFlags::empty()); + msg.dht_header.network = Network::MainNet; + + rt.block_on(validate.call(msg.clone())).unwrap(); + assert_eq!(spy.call_count(), 0); + + msg.dht_header.network = Network::LocalTest; + + rt.block_on(validate.call(msg.clone())).unwrap(); + assert_eq!(spy.call_count(), 1); + + let calls = mock_state.take_calls(); + assert_eq!(calls.len(), 1); + let params = calls[0].0.clone(); + assert_eq!(params.dht_message_type, DhtMessageType::RejectMsg); + assert_eq!( + params.broadcast_strategy.direct_public_key().unwrap(), + node_identity.public_key() + ); + + // Drop validate so that the mock will stop running + drop(validate); + } +} diff --git a/comms/dht/src/lib.rs b/comms/dht/src/lib.rs new file mode 100644 index 0000000000..fd1f8243ee --- /dev/null +++ b/comms/dht/src/lib.rs @@ -0,0 +1,144 @@ +//! # Tari Comms DHT +//! +//! ## Overview +//! +//! The `tari_comms_dht` crate adds DHT functionality to `tari_comms`. +//! It provides two sets of middleware (_inbound_ and _outbound_) which +//! process incoming requests and outgoing messages respectively. +//! +//! ### Attaching to comms +//! +//! In `tari_comms`, incoming and outgoing messages are connected using two mpsc sender/receiver pairs. +//! One for incoming messages (receiving `InboundMessage`s) and one for outbound messages (sending `OutboundMessage`s). +//! +//! The DHT module consists of two middleware layers (as in `tower_layer::Layer`) which form +//! an inbound and outbound pipeline for augmenting messages. +//! +//! #### Inbound Message Flow +//! +//! `InboundMessage`s are received from the incoming comms channel (as in the receiver side of +//! of the mpsc channel which goes into `CommsBuilder::new().incoming_message_sink(sender)`). +//! Typically, a `ServicePipeline` from the `tari_comms::middleware` crate is used to connect +//! a stream from comms to the middleware service. +//! +//! `InboundMessage`(comms) -> _DHT Inbound Middleware_ -> `DhtInboundMessage`(domain) +//! +//! The DHT inbound middleware consist of: +//! * `DeserializeMiddleware` deserializes the body of an `InboundMessage` into a `DhtEnvelope`. +//! * `DecryptionMiddleware` attempts to decrypt the body of a `DhtEnvelope` if required. The result of that decryption +//! (success or failure) is passed to the next service. +//! * `ForwardMiddleware` uses the result of the decryption to determine if the message is destined for this node or +//! not. If not, the message will be forwarded to the applicable peers using the OutboundRequester (i.e. the outbound +//! DHT middleware). +//! * `DhtHandlerMiddleware` handles DHT messages, such as `Join` and `Discover`. If the messages are _not_ DHT messages +//! the `next_service` is called. +//! +//! #### Outbound Message Flow +//! +//! `OutboundMessage`s are sent to the outgoing comms channel (as in the receiver side of +//! of the mpsc channel which goes into `CommsBuilder::new().outgoing_message_stream(receiver)`). +//! Typically, a `ServicePipeline` from the `tari_comms::middleware` crate is used to connect +//! a stream from the domain-level to the middleware service and a `SinkMiddleware` to connect +//! the middleware to the OMS in comms. Outbound requests to the DHT middleware are furnished by +//! the `OutboundMessageRequester`, obtained from the `Dht::outbound_requester` factory method. +//! +//! `DhtOutboundRequest` (domain) -> _DHT Outbound Middleware_ -> `OutboundMessage` (comms) +//! +//! The DHT outbound middleware consist of: +//! * `BroadcastMiddleware` produces multiple outbound messages according on the `BroadcastStrategy` from the received +//! `DhtOutboundRequest` message. The `next_service` is called for each resulting message. +//! * `EncryptionMiddleware` encrypts the body of a message if `DhtMessagheFlags::ENCRYPTED` is given. The result is +//! passed onto the `next_service`. +//! * `SerializeMiddleware` wraps the body in a `DhtEnvelope`, serializes the result, constructs an `OutboundMessage` +//! and calls `next_service`. Typically, `next_service` will be a `SinkMiddleware` which send the message to the comms +//! OMS. +// +//! ## Usage +//! +//! ```edition2018,compile_fail +//! #use tari_comms::middleware::ServicePipeline; +//! #use tari_comms_dht::DhtBuilder; +//! #use tari_comms::middleware::sink::SinkMiddleware; +//! #use tari_comms::peer_manager::NodeIdentity; +//! #use rand::rngs::OsRng; +//! #use std::sync::Arc; +//! #use tari_comms::CommsBuilder; +//! #use tokio::runtime::Runtime; +//! #use futures::channel::mpsc; +//! +//! let runtime = Runtime::new().unwrap(); +//! // Channel from comms to inbound dht +//! let (comms_in_tx, comms_in_rx)= mpsc::channel(100); +//! let (comms_out_tx, comms_out_rx)= mpsc::channel(100); +//! let node_identity = NodeIdentity::random(&mut OsRng::new().unwrap(), "127.0.0.1:9000".parse().unwrap()) +//! .map(Arc::new).unwrap(); +//! let comms = CommsBuilder::new(runtime.executor()) +//! // Messages coming from comms +//! .with_inbound_sink(comms_in_tx) +//! // Messages going to comms +//! .with_outbound_stream(comms_out_rx) +//! .with_node_identity(node_identity) +//! .build() +//! .unwrap(); +//! let peer_manager = comms.start().unwrap().peer_manager(); +//! let dht = DhtBuilder::new(node_identity, peer_manager).finish(); +//! +//! let inbound_pipeline = ServicePipeline::new( +//! comms_in_rx, +//! // In Tari's case, the service would be a InboundMessageConnector in `tari_p2p` +//! dht.inbound_middleware_layer(/* some service which uses DhtInboundMessage */ ) +//! ); +//! // Use the given executor to spawn calls to the middleware +//! inbound_pipeline.spawn_with(rt.executor()); +//! +//! let outbound_pipeline = ServicePipeline::new( +//! dht.take_outbound_receiver(), +//! // SinkMiddleware sends the resulting OutboundMessages to the comms OMS +//! dht.outbound_middleware_layer(SinkMiddleware::new(comms_out_tx)) +//! ); +//! // Use the given executor to spawn calls to the middleware +//! outbound_pipeline.spawn_with(rt.executor()); +//! +//! let oms = dht.outbound_requester(); +//! oms.send_message(...).await; +//! ``` + +#![recursion_limit = "256"] +// Details: https://doc.rust-lang.org/beta/unstable-book/language-features/type-alias-impl-trait.html +#![feature(type_alias_impl_trait)] + +#[macro_use] +mod macros; + +#[cfg(test)] +#[macro_use] +mod test_utils; + +mod actor; +pub use actor::{DhtActorError, DhtRequest, DhtRequester}; + +mod builder; +pub use builder::DhtBuilder; + +mod config; +pub use config::DhtConfig; + +mod consts; +mod crypt; + +mod dht; +pub use dht::Dht; + +mod discovery; +mod proto; +mod tower_filter; +mod utils; + +pub mod broadcast_strategy; +pub mod domain_message; +pub mod envelope; +pub mod inbound; +pub mod outbound; +pub mod store_forward; + +pub type PipelineError = Box; diff --git a/comms/dht/src/macros.rs b/comms/dht/src/macros.rs new file mode 100644 index 0000000000..ce4cb640fb --- /dev/null +++ b/comms/dht/src/macros.rs @@ -0,0 +1,45 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/// This macro unlocks a Mutex or RwLock. If the lock is +/// poisoned (i.e. panic while unlocked) the last value +/// before the panic is used. +macro_rules! acquire_lock { + ($e:expr, $m:ident) => { + match $e.$m() { + Ok(lock) => lock, + Err(poisoned) => { + log::warn!(target: "dht", "Lock has been POISONED and will be silently recovered"); + poisoned.into_inner() + }, + } + }; + ($e:expr) => { + acquire_lock!($e, lock) + }; +} + +macro_rules! acquire_write_lock { + ($e:expr) => { + acquire_lock!($e, write) + }; +} diff --git a/comms/dht/src/outbound/broadcast.rs b/comms/dht/src/outbound/broadcast.rs new file mode 100644 index 0000000000..d87bb88dd6 --- /dev/null +++ b/comms/dht/src/outbound/broadcast.rs @@ -0,0 +1,628 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::{error::DhtOutboundError, message::DhtOutboundRequest}; +use crate::{ + actor::DhtRequester, + broadcast_strategy::BroadcastStrategy, + discovery::DhtDiscoveryRequester, + envelope::{DhtMessageHeader, DhtMessageOrigin, NodeDestination}, + outbound::{ + message::{DhtOutboundMessage, OutboundEncryption}, + message_params::FinalSendMessageParams, + SendMessageResponse, + }, + proto::envelope::{DhtMessageType, Network}, + PipelineError, +}; +use futures::{ + channel::oneshot, + future, + stream::{self, StreamExt}, + task::Context, + Future, +}; +use log::*; +use std::{sync::Arc, task::Poll}; +use tari_comms::{ + message::MessageFlags, + peer_manager::{NodeId, NodeIdentity, Peer}, + types::CommsPublicKey, +}; +use tower::{layer::Layer, Service, ServiceExt}; + +const LOG_TARGET: &str = "comms::dht::outbound::broadcast_middleware"; + +pub struct BroadcastLayer { + dht_requester: DhtRequester, + dht_discovery_requester: DhtDiscoveryRequester, + node_identity: Arc, + target_network: Network, +} + +impl BroadcastLayer { + pub fn new( + node_identity: Arc, + dht_requester: DhtRequester, + dht_discovery_requester: DhtDiscoveryRequester, + target_network: Network, + ) -> Self + { + BroadcastLayer { + node_identity, + dht_requester, + dht_discovery_requester, + target_network, + } + } +} + +impl Layer for BroadcastLayer { + type Service = BroadcastMiddleware; + + fn layer(&self, service: S) -> Self::Service { + BroadcastMiddleware::new( + service, + Arc::clone(&self.node_identity), + self.dht_requester.clone(), + self.dht_discovery_requester.clone(), + self.target_network, + ) + } +} + +/// Responsible for constructing messages using a broadcast strategy and passing them on to +/// the worker task. +#[derive(Clone)] +pub struct BroadcastMiddleware { + next: S, + dht_requester: DhtRequester, + dht_discovery_requester: DhtDiscoveryRequester, + node_identity: Arc, + target_network: Network, +} + +impl BroadcastMiddleware { + pub fn new( + service: S, + node_identity: Arc, + dht_requester: DhtRequester, + dht_discovery_requester: DhtDiscoveryRequester, + target_network: Network, + ) -> Self + { + Self { + next: service, + dht_requester, + dht_discovery_requester, + node_identity, + target_network, + } + } +} + +impl Service for BroadcastMiddleware +where S: Service + Clone +{ + type Error = PipelineError; + type Response = (); + + type Future = impl Future>; + + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, msg: DhtOutboundRequest) -> Self::Future { + BroadcastTask::new( + self.next.clone(), + Arc::clone(&self.node_identity), + self.dht_requester.clone(), + self.dht_discovery_requester.clone(), + self.target_network, + msg, + ) + .handle() + } +} + +struct BroadcastTask { + service: S, + node_identity: Arc, + dht_requester: DhtRequester, + dht_discovery_requester: DhtDiscoveryRequester, + request: Option, + target_network: Network, +} + +impl BroadcastTask +where S: Service +{ + pub fn new( + service: S, + node_identity: Arc, + dht_requester: DhtRequester, + dht_discovery_requester: DhtDiscoveryRequester, + target_network: Network, + request: DhtOutboundRequest, + ) -> Self + { + Self { + service, + node_identity, + dht_requester, + dht_discovery_requester, + target_network, + request: Some(request), + } + } + + pub async fn handle(mut self) -> Result<(), PipelineError> { + let request = self.request.take().expect("request cannot be None"); + debug!(target: LOG_TARGET, "Processing outbound request {}", request); + let messages = self.generate_outbound_messages(request).await?; + debug!( + target: LOG_TARGET, + "Passing {} message(s) to next_service", + messages.len() + ); + + self.service + .call_all(stream::iter(messages)) + .unordered() + .filter_map(|result| future::ready(result.err())) + .for_each(|err| { + error!(target: LOG_TARGET, "Error when sending broadcast messages: {}", err); + future::ready(()) + }) + .await; + + Ok(()) + } + + pub async fn generate_outbound_messages( + &mut self, + msg: DhtOutboundRequest, + ) -> Result, DhtOutboundError> + { + match msg { + DhtOutboundRequest::SendMessage(params, body, reply_tx) => { + self.handle_send_message(*params, body, reply_tx).await + }, + } + } + + async fn handle_send_message( + &mut self, + params: FinalSendMessageParams, + body: Vec, + reply_tx: oneshot::Sender, + ) -> Result, DhtOutboundError> + { + let FinalSendMessageParams { + broadcast_strategy, + destination, + dht_message_type, + encryption, + is_discovery_enabled, + force_origin, + dht_header, + } = params; + + if broadcast_strategy + .direct_public_key() + .filter(|pk| *pk == self.node_identity.public_key()) + .is_some() + { + warn!(target: LOG_TARGET, "Attempt to send to own peer"); + return Err(DhtOutboundError::SendToOurselves); + } + + match self.select_peers(broadcast_strategy.clone()).await { + Ok(mut peers) => { + if reply_tx.is_canceled() { + return Err(DhtOutboundError::ReplyChannelCanceled); + } + + let mut reply_tx = Some(reply_tx); + + trace!( + target: LOG_TARGET, + "Number of peers selected = {}, is_discovery_enabled = {}", + peers.len(), + is_discovery_enabled, + ); + + // Discovery is required if: + // - Discovery is enabled for this request + // - There where no peers returned + // - A direct public key broadcast strategy is used + if is_discovery_enabled && peers.is_empty() && broadcast_strategy.direct_public_key().is_some() { + let (discovery_reply_tx, discovery_reply_rx) = oneshot::channel(); + let target_public_key = broadcast_strategy.into_direct_public_key().expect("already checked"); + + let _ = reply_tx + .take() + .expect("cannot fail") + .send(SendMessageResponse::PendingDiscovery(discovery_reply_rx)); + + match self.initiate_peer_discovery(target_public_key).await { + Ok(Some(peer)) => { + // Set the reply_tx so that it can be used later + reply_tx = Some(discovery_reply_tx); + peers = vec![peer]; + }, + Ok(None) => { + // Message sent to 0 peers + let _ = discovery_reply_tx.send(SendMessageResponse::Queued(vec![])); + return Ok(Vec::new()); + }, + Err(err) => { + let _ = discovery_reply_tx.send(SendMessageResponse::Failed); + return Err(err); + }, + } + } + + match self + .generate_send_messages( + peers, + destination, + dht_message_type, + encryption, + dht_header, + force_origin, + body, + ) + .await + { + Ok(msgs) => { + // Reply with the number of messages to be sent + let _ = reply_tx + .take() + .expect("cannot fail") + .send(SendMessageResponse::Queued(msgs.iter().map(|m| m.tag).collect())); + Ok(msgs) + }, + Err(err) => { + // Reply 0 messages sent + let _ = reply_tx.take().expect("cannot fail").send(SendMessageResponse::Failed); + Err(err) + }, + } + }, + Err(err) => { + let _ = reply_tx.send(SendMessageResponse::Failed); + Err(err) + }, + } + } + + async fn select_peers(&mut self, broadcast_strategy: BroadcastStrategy) -> Result, DhtOutboundError> { + self.dht_requester + .select_peers(broadcast_strategy) + .await + .map_err(|err| { + error!(target: LOG_TARGET, "{}", err); + DhtOutboundError::PeerSelectionFailed + }) + } + + async fn initiate_peer_discovery( + &mut self, + dest_public_key: CommsPublicKey, + ) -> Result, DhtOutboundError> + { + trace!( + target: LOG_TARGET, + "Initiating peer discovery for public key '{}'", + dest_public_key + ); + + // TODO: This works because we know that all non-DAN node IDs are/should be derived from the public key. + // Once the DAN launches, this may not be the case and we'll need to query the blockchain for the node id + let derived_node_id = NodeId::from_key(&dest_public_key).ok(); + + // Peer not found, let's try and discover it + match self + .dht_discovery_requester + .discover_peer(dest_public_key, derived_node_id, NodeDestination::Unknown) + .await + { + // Peer found! + Ok(peer) => { + if peer.is_banned() { + warn!( + target: LOG_TARGET, + "Peer discovery succeeded however peer with public key '{}' is marked as banned.", + peer.public_key + ); + return Ok(None); + } + + debug!( + target: LOG_TARGET, + "Peer discovery succeeded for public key '{}'.", peer.public_key + ); + Ok(Some(peer)) + }, + // Error during discovery + Err(err) => { + debug!(target: LOG_TARGET, "Peer discovery failed because '{}'.", err); + Ok(None) + }, + } + } + + async fn generate_send_messages( + &mut self, + selected_peers: Vec, + destination: NodeDestination, + dht_message_type: DhtMessageType, + encryption: OutboundEncryption, + custom_header: Option, + force_origin: bool, + body: Vec, + ) -> Result, DhtOutboundError> + { + let dht_flags = encryption.flags(); + + // Create a DHT header + let dht_header = custom_header + .or_else(|| { + // The origin is specified if encryption is turned on, otherwise it is not + let origin = if force_origin || encryption.is_encrypt() { + Some(DhtMessageOrigin { + // Origin public key used to identify the origin and verify the signature + public_key: self.node_identity.public_key().clone(), + // Signing will happen later in the pipeline (SerializeMiddleware), left empty to prevent double + // work + signature: Vec::new(), + }) + } else { + None + }; + + Some(DhtMessageHeader::new( + // Final destination for this message + destination, + dht_message_type, + origin, + self.target_network, + dht_flags, + )) + }) + .expect("always Some"); + + // Construct a MessageEnvelope for each recipient + let messages = selected_peers + .into_iter() + .map(|peer| { + DhtOutboundMessage::new( + peer, + dht_header.clone(), + encryption.clone(), + MessageFlags::NONE, + body.clone(), + ) + }) + .collect::>(); + + Ok(messages) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::{ + outbound::SendMessageParams, + test_utils::{ + create_dht_actor_mock, + create_dht_discovery_mock, + make_peer, + service_spy, + DhtDiscoveryMockState, + DhtMockState, + }, + }; + use futures::channel::oneshot; + use rand::rngs::OsRng; + use std::time::Duration; + use tari_comms::{ + multiaddr::Multiaddr, + peer_manager::{NodeId, Peer, PeerFeatures, PeerFlags}, + types::CommsPublicKey, + }; + use tari_crypto::keys::PublicKey; + use tari_test_utils::unpack_enum; + use tokio::runtime::Runtime; + + #[test] + fn send_message_flood() { + let mut rt = Runtime::new().unwrap(); + + let pk = CommsPublicKey::default(); + let example_peer = Peer::new( + pk.clone(), + NodeId::from_key(&pk).unwrap(), + vec!["/ip4/127.0.0.1/tcp/9999".parse::().unwrap()].into(), + PeerFlags::empty(), + PeerFeatures::COMMUNICATION_NODE, + ); + + let other_peer = { + let mut p = example_peer.clone(); + let (_, pk) = CommsPublicKey::random_keypair(&mut OsRng); + p.node_id = NodeId::from_key(&pk).unwrap(); + p.public_key = pk; + p + }; + + let node_identity = Arc::new( + NodeIdentity::random( + &mut OsRng, + "/ip4/127.0.0.1/tcp/9000".parse().unwrap(), + PeerFeatures::COMMUNICATION_NODE, + ) + .unwrap(), + ); + + let (dht_requester, mut dht_mock) = create_dht_actor_mock(10); + let (dht_discover_requester, _) = create_dht_discovery_mock(10, Duration::from_secs(10)); + + let mock_state = DhtMockState::new(); + mock_state.set_select_peers_response(vec![example_peer.clone(), other_peer.clone()]); + dht_mock.set_shared_state(mock_state); + + rt.spawn(dht_mock.run()); + + let spy = service_spy(); + + let mut service = BroadcastMiddleware::new( + spy.to_service(), + node_identity, + dht_requester, + dht_discover_requester, + Network::LocalTest, + ); + let (reply_tx, _reply_rx) = oneshot::channel(); + + rt.block_on(service.call(DhtOutboundRequest::SendMessage( + Box::new(SendMessageParams::new().flood().finish()), + "custom_msg".as_bytes().to_vec(), + reply_tx, + ))) + .unwrap(); + + assert_eq!(spy.call_count(), 2); + let requests = spy.take_requests(); + assert!(requests + .iter() + .any(|msg| msg.destination_peer.node_id == example_peer.node_id)); + assert!(requests + .iter() + .any(|msg| msg.destination_peer.node_id == other_peer.node_id)); + } + + #[test] + fn send_message_direct_not_found() { + // Test for issue https://github.com/tari-project/tari/issues/959 + let mut rt = Runtime::new().unwrap(); + + let pk = CommsPublicKey::default(); + let node_identity = NodeIdentity::random( + &mut OsRng, + "/ip4/127.0.0.1/tcp/9000".parse().unwrap(), + PeerFeatures::COMMUNICATION_NODE, + ) + .unwrap(); + + let (dht_requester, dht_mock) = create_dht_actor_mock(10); + rt.spawn(dht_mock.run()); + let (dht_discover_requester, _) = create_dht_discovery_mock(10, Duration::from_secs(10)); + let spy = service_spy(); + + let mut service = BroadcastMiddleware::new( + spy.to_service(), + Arc::new(node_identity), + dht_requester, + dht_discover_requester, + Network::LocalTest, + ); + let (reply_tx, reply_rx) = oneshot::channel(); + + rt.block_on( + service.call(DhtOutboundRequest::SendMessage( + Box::new( + SendMessageParams::new() + .direct_public_key(pk) + .with_discovery(false) + .finish(), + ), + "custom_msg".as_bytes().to_vec(), + reply_tx, + )), + ) + .unwrap(); + + let send_message_response = rt.block_on(reply_rx).unwrap(); + unpack_enum!(SendMessageResponse::Queued(tags) = send_message_response); + assert_eq!(tags.len(), 0); + assert_eq!(spy.call_count(), 0); + } + + #[test] + fn send_message_direct_dht_discovery() { + let mut rt = Runtime::new().unwrap(); + + let node_identity = NodeIdentity::random( + &mut OsRng, + "/ip4/127.0.0.1/tcp/9000".parse().unwrap(), + PeerFeatures::COMMUNICATION_NODE, + ) + .unwrap(); + + let (dht_requester, dht_mock) = create_dht_actor_mock(10); + rt.spawn(dht_mock.run()); + let (dht_discover_requester, mut discovery_mock) = create_dht_discovery_mock(10, Duration::from_secs(10)); + let dht_discovery_state = DhtDiscoveryMockState::new(); + discovery_mock.set_shared_state(dht_discovery_state.clone()); + rt.spawn(discovery_mock.run()); + + let peer_to_discover = make_peer(); + dht_discovery_state.set_discover_peer_response(peer_to_discover.clone()); + + let spy = service_spy(); + + let mut service = BroadcastMiddleware::new( + spy.to_service(), + Arc::new(node_identity), + dht_requester, + dht_discover_requester, + Network::LocalTest, + ); + let (reply_tx, reply_rx) = oneshot::channel(); + + rt.block_on( + service.call(DhtOutboundRequest::SendMessage( + Box::new( + SendMessageParams::new() + .direct_public_key(peer_to_discover.public_key.clone()) + .finish(), + ), + "custom_msg".as_bytes().to_vec(), + reply_tx, + )), + ) + .unwrap(); + + let send_message_response = rt.block_on(reply_rx).unwrap(); + + unpack_enum!(SendMessageResponse::PendingDiscovery(await_discovery) = send_message_response); + let discovery_reply = rt.block_on(await_discovery).unwrap(); + assert_eq!(dht_discovery_state.call_count(), 1); + unpack_enum!(SendMessageResponse::Queued(tags) = discovery_reply); + assert_eq!(tags.len(), 1); + assert_eq!(spy.call_count(), 1); + } +} diff --git a/comms/dht/src/outbound/encryption.rs b/comms/dht/src/outbound/encryption.rs new file mode 100644 index 0000000000..9349b2037d --- /dev/null +++ b/comms/dht/src/outbound/encryption.rs @@ -0,0 +1,202 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + crypt, + outbound::message::{DhtOutboundMessage, OutboundEncryption}, + PipelineError, +}; +use futures::{task::Context, Future}; +use log::*; +use std::{sync::Arc, task::Poll}; +use tari_comms::peer_manager::NodeIdentity; +use tower::{layer::Layer, Service, ServiceExt}; + +const LOG_TARGET: &str = "comms::middleware::encryption"; + +/// This layer is responsible for attempting to decrypt inbound messages. +pub struct EncryptionLayer { + node_identity: Arc, +} + +impl EncryptionLayer { + pub fn new(node_identity: Arc) -> Self { + Self { node_identity } + } +} + +impl Layer for EncryptionLayer { + type Service = EncryptionService; + + fn layer(&self, service: S) -> Self::Service { + EncryptionService::new(service, Arc::clone(&self.node_identity)) + } +} + +/// Responsible for decrypting InboundMessages and passing a DecryptedInboundMessage to the given service +#[derive(Clone)] +pub struct EncryptionService { + node_identity: Arc, + inner: S, +} + +impl EncryptionService { + pub fn new(service: S, node_identity: Arc) -> Self { + Self { + inner: service, + node_identity, + } + } +} + +impl Service for EncryptionService +where + S: Service + Clone, + S::Error: Into, +{ + type Error = PipelineError; + type Response = (); + + type Future = impl Future>; + + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, msg: DhtOutboundMessage) -> Self::Future { + Self::handle_message(self.inner.clone(), Arc::clone(&self.node_identity), msg) + } +} + +impl EncryptionService +where + S: Service, + S::Error: Into, +{ + async fn handle_message( + mut next_service: S, + node_identity: Arc, + mut message: DhtOutboundMessage, + ) -> Result<(), PipelineError> + { + trace!(target: LOG_TARGET, "DHT Message flags: {:?}", message.dht_header.flags); + match &message.encryption { + OutboundEncryption::EncryptFor(public_key) => { + debug!(target: LOG_TARGET, "Encrypting message for {}", public_key); + let shared_secret = crypt::generate_ecdh_secret(node_identity.secret_key(), public_key); + message.body = crypt::encrypt(&shared_secret, &message.body)?; + }, + OutboundEncryption::EncryptForPeer => { + debug!( + target: LOG_TARGET, + "Encrypting message for peer with public key {}", message.destination_peer.public_key + ); + let shared_secret = + crypt::generate_ecdh_secret(node_identity.secret_key(), &message.destination_peer.public_key); + message.body = crypt::encrypt(&shared_secret, &message.body)? + }, + OutboundEncryption::None => { + debug!(target: LOG_TARGET, "Encryption not requested for message"); + }, + }; + + next_service.ready().await.map_err(Into::into)?; + next_service.call(message).await.map_err(Into::into) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::{ + envelope::DhtMessageFlags, + test_utils::{make_dht_header, make_node_identity, service_spy}, + }; + use futures::executor::block_on; + use tari_comms::{ + message::MessageFlags, + net_address::MultiaddressesWithStats, + peer_manager::{NodeId, Peer, PeerFeatures, PeerFlags}, + types::CommsPublicKey, + }; + use tari_test_utils::panic_context; + + #[test] + fn no_encryption() { + let spy = service_spy(); + let node_identity = make_node_identity(); + let mut encryption = EncryptionLayer::new(Arc::clone(&node_identity)).layer(spy.to_service::()); + + panic_context!(cx); + assert!(encryption.poll_ready(&mut cx).is_ready()); + + let body = b"A".to_vec(); + let msg = DhtOutboundMessage::new( + Peer::new( + CommsPublicKey::default(), + NodeId::default(), + MultiaddressesWithStats::new(vec![]), + PeerFlags::empty(), + PeerFeatures::COMMUNICATION_NODE, + ), + make_dht_header(&node_identity, &body, DhtMessageFlags::empty()), + OutboundEncryption::None, + MessageFlags::empty(), + body.clone(), + ); + block_on(encryption.call(msg)).unwrap(); + + let msg = spy.pop_request().unwrap(); + assert_eq!(msg.body, body); + assert_eq!(msg.destination_peer.node_id, NodeId::default()); + } + + #[test] + fn encryption() { + let spy = service_spy(); + let node_identity = make_node_identity(); + let mut encryption = EncryptionLayer::new(Arc::clone(&node_identity)).layer(spy.to_service::()); + + panic_context!(cx); + assert!(encryption.poll_ready(&mut cx).is_ready()); + + let body = b"A".to_vec(); + let msg = DhtOutboundMessage::new( + Peer::new( + CommsPublicKey::default(), + NodeId::default(), + MultiaddressesWithStats::new(vec![]), + PeerFlags::empty(), + PeerFeatures::COMMUNICATION_NODE, + ), + make_dht_header(&node_identity, &body, DhtMessageFlags::ENCRYPTED), + OutboundEncryption::EncryptForPeer, + MessageFlags::empty(), + body.clone(), + ); + block_on(encryption.call(msg)).unwrap(); + + let msg = spy.pop_request().unwrap(); + assert_ne!(msg.body, body); + assert_eq!(msg.destination_peer.node_id, NodeId::default()); + } +} diff --git a/comms/src/outbound_message_service/error.rs b/comms/dht/src/outbound/error.rs similarity index 68% rename from comms/src/outbound_message_service/error.rs rename to comms/dht/src/outbound/error.rs index 5a7cfc6d11..4af39d565c 100644 --- a/comms/src/outbound_message_service/error.rs +++ b/comms/dht/src/outbound/error.rs @@ -1,4 +1,4 @@ -// Copyright 2019 The Tari Project +// Copyright 2019, The Tari Project // // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the // following conditions are met: @@ -18,25 +18,27 @@ // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{message::MessageError, peer_manager::PeerManagerError}; use derive_error::Error; -use tari_utilities::{message_format::MessageFormatError, thread_join::ThreadError}; +use futures::channel::mpsc::SendError; +use tari_comms::message::MessageError; +use tari_crypto::{signatures::SchnorrSignatureError, tari_utilities::message_format::MessageFormatError}; -/// Error type for OutboundMessageService subsystem #[derive(Debug, Error)] -pub enum OutboundError { - /// The message could not be serialized +pub enum DhtOutboundError { + SendError(SendError), MessageSerializationError(MessageError), - /// Error during serialization or deserialization MessageFormatError(MessageFormatError), - /// Problem encountered with Broadcast Strategy and PeerManager - PeerManagerError(PeerManagerError), - #[error(msg_embedded, non_std, no_from)] - ShutdownSignalSendError(String), - /// Timeout exceeded while waiting for worker threads to complete - ThreadJoinError(ThreadError), - /// Failed to send message on message sink - SyncSenderError, + SignatureError(SchnorrSignatureError), + /// Requester reply channel closed before response was received + RequesterReplyChannelClosed, + /// Peer selection failed + PeerSelectionFailed, + /// Failed to send broadcast message + BroadcastFailed, + /// Reply channel cancelled + ReplyChannelCanceled, + /// Attempted to send a message to ourselves + SendToOurselves, } diff --git a/comms/dht/src/outbound/message.rs b/comms/dht/src/outbound/message.rs new file mode 100644 index 0000000000..9f2abbca51 --- /dev/null +++ b/comms/dht/src/outbound/message.rs @@ -0,0 +1,164 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + envelope::{DhtMessageFlags, DhtMessageHeader}, + outbound::message_params::FinalSendMessageParams, +}; +use futures::channel::oneshot; +use std::fmt; +use tari_comms::{ + message::{MessageFlags, MessageTag}, + peer_manager::Peer, + types::CommsPublicKey, +}; + +/// Determines if an outbound message should be Encrypted and, if so, for which public key +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum OutboundEncryption { + /// Message should not be encrypted + None, + /// Message should be encrypted using a shared secret derived from the given public key + EncryptFor(CommsPublicKey), + // TODO: Remove this option as it is redundant (message encryption only needed for forwarded private messages) + /// Message should be encrypted using a shared secret derived from the destination peer's + /// public key. Each message sent according to the broadcast strategy will be encrypted for + /// the destination peer. + EncryptForPeer, +} + +impl OutboundEncryption { + /// Return the correct DHT flags for the encryption setting + pub fn flags(&self) -> DhtMessageFlags { + match self { + OutboundEncryption::EncryptFor(_) | OutboundEncryption::EncryptForPeer => DhtMessageFlags::ENCRYPTED, + _ => DhtMessageFlags::NONE, + } + } + + /// Returns true if encryption is turned on, otherwise false + pub fn is_encrypt(&self) -> bool { + use OutboundEncryption::*; + match self { + None => false, + EncryptFor(_) | EncryptForPeer => true, + } + } +} + +impl Default for OutboundEncryption { + fn default() -> Self { + OutboundEncryption::None + } +} + +#[derive(Debug)] +pub enum SendMessageResponse { + /// Returns the message tags which are queued for sending. These tags will be used in a subsequent OutboundEvent to + /// indicate if the message succeeded/failed to send + Queued(Vec), + /// A failure occurred when sending + Failed, + /// DHT Discovery has been initiated. The caller may wait on the receiver + /// to find out of the message was sent. + /// _NOTE: DHT discovery could take minutes (determined by `DhtConfig::discovery_request_timeout)_ + PendingDiscovery(oneshot::Receiver), +} + +impl SendMessageResponse { + /// Returns the result of a send message request. + /// A `SendMessageResponse::Queued(n)` will resolve immediately returning `Some(n)`. + /// A `SendMessageResponse::Failed` will resolve immediately returning a `None`. + /// If DHT discovery is initiated, this will resolve once discovery has completed, either + /// succeeding (`Some(n)`) or failing (`None`). + pub async fn resolve_ok(self) -> Option> { + use SendMessageResponse::*; + match self { + Queued(tags) => Some(tags), + Failed => None, + PendingDiscovery(rx) => rx.await.ok()?.queued_or_failed(), + } + } + + fn queued_or_failed(self) -> Option> { + use SendMessageResponse::*; + match self { + Queued(tags) => Some(tags), + Failed => None, + PendingDiscovery(_) => panic!("ok_or_failed() called on PendingDiscovery"), + } + } +} + +/// Represents a request to the DHT broadcast middleware +#[derive(Debug)] +pub enum DhtOutboundRequest { + /// Send a message using the given broadcast strategy + SendMessage( + Box, + Vec, + oneshot::Sender, + ), +} + +impl fmt::Display for DhtOutboundRequest { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + match self { + DhtOutboundRequest::SendMessage(params, body, _) => { + write!(f, "SendMsg({} - <{} bytes>)", params.broadcast_strategy, body.len()) + }, + } + } +} + +/// DhtOutboundMessage consists of the DHT and comms information required to +/// send a message +#[derive(Clone, Debug)] +pub struct DhtOutboundMessage { + pub tag: MessageTag, + pub destination_peer: Peer, + pub dht_header: DhtMessageHeader, + pub comms_flags: MessageFlags, + pub encryption: OutboundEncryption, + pub body: Vec, +} + +impl DhtOutboundMessage { + /// Create a new DhtOutboundMessage + pub fn new( + destination_peer: Peer, + dht_header: DhtMessageHeader, + encryption: OutboundEncryption, + comms_flags: MessageFlags, + body: Vec, + ) -> Self + { + Self { + tag: MessageTag::new(), + destination_peer, + dht_header, + encryption, + comms_flags, + body, + } + } +} diff --git a/comms/dht/src/outbound/message_params.rs b/comms/dht/src/outbound/message_params.rs new file mode 100644 index 0000000000..223741b5b0 --- /dev/null +++ b/comms/dht/src/outbound/message_params.rs @@ -0,0 +1,170 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + broadcast_strategy::{BroadcastClosestRequest, BroadcastStrategy}, + envelope::{DhtMessageHeader, NodeDestination}, + outbound::OutboundEncryption, + proto::envelope::DhtMessageType, +}; +use tari_comms::{peer_manager::NodeId, types::CommsPublicKey}; + +/// Configuration for outbound messages. +/// +/// ```edition2018 +/// # use tari_comms_dht::outbound::{SendMessageParams, OutboundEncryption}; +/// +/// // These params represent sending to 5 random peers, each encrypted for that peer +/// let params = SendMessageParams::new() +/// .random(5) +/// .with_encryption(OutboundEncryption::EncryptForPeer) +/// .finish(); +/// ``` +#[derive(Debug, Clone)] +pub struct SendMessageParams { + params: Option, +} + +impl Default for SendMessageParams { + fn default() -> Self { + Self { + params: Some(Default::default()), + } + } +} + +#[derive(Debug, Clone)] +pub struct FinalSendMessageParams { + pub broadcast_strategy: BroadcastStrategy, + pub destination: NodeDestination, + pub encryption: OutboundEncryption, + pub is_discovery_enabled: bool, + pub force_origin: bool, + pub dht_message_type: DhtMessageType, + pub dht_header: Option, +} + +impl Default for FinalSendMessageParams { + fn default() -> Self { + Self { + broadcast_strategy: BroadcastStrategy::Flood, + destination: Default::default(), + encryption: Default::default(), + dht_message_type: Default::default(), + force_origin: false, + is_discovery_enabled: true, + dht_header: None, + } + } +} + +impl SendMessageParams { + pub fn new() -> Self { + Default::default() + } + + /// Set broadcast_strategy to DirectPublicKey + pub fn direct_public_key(&mut self, public_key: CommsPublicKey) -> &mut Self { + self.params_mut().broadcast_strategy = BroadcastStrategy::DirectPublicKey(public_key); + self + } + + /// Set broadcast_strategy to DirectNodeId + pub fn direct_node_id(&mut self, node_id: NodeId) -> &mut Self { + self.params_mut().broadcast_strategy = BroadcastStrategy::DirectNodeId(node_id); + self + } + + /// Set broadcast_strategy to Closest + pub fn closest(&mut self, node_id: NodeId, n: usize, excluded_peers: Vec) -> &mut Self { + self.params_mut().broadcast_strategy = BroadcastStrategy::Closest(Box::new(BroadcastClosestRequest { + excluded_peers, + node_id, + n, + })); + self + } + + /// Set broadcast_strategy to Neighbours + pub fn neighbours(&mut self, excluded_peers: Vec) -> &mut Self { + self.params_mut().broadcast_strategy = BroadcastStrategy::Neighbours(excluded_peers); + self + } + + /// Set broadcast_strategy to Flood + pub fn flood(&mut self) -> &mut Self { + self.params_mut().broadcast_strategy = BroadcastStrategy::Flood; + self + } + + /// Set broadcast_strategy to Random + pub fn random(&mut self, n: usize) -> &mut Self { + self.params_mut().broadcast_strategy = BroadcastStrategy::Random(n); + self + } + + /// Set destination field in message header. + pub fn with_destination(&mut self, destination: NodeDestination) -> &mut Self { + self.params_mut().destination = destination; + self + } + + /// Set encryption mode for message. + pub fn with_encryption(&mut self, encryption: OutboundEncryption) -> &mut Self { + self.params_mut().encryption = encryption; + self + } + + /// Set to true to enable discovery, otherwise false + pub fn with_discovery(&mut self, is_enabled: bool) -> &mut Self { + self.params_mut().is_discovery_enabled = is_enabled; + self + } + + /// Set the DHT message type + pub fn with_dht_message_type(&mut self, message_type: DhtMessageType) -> &mut Self { + self.params_mut().dht_message_type = message_type; + self + } + + /// Override the DHtHeader of a message(s) with the given header + pub fn with_dht_header(&mut self, dht_header: DhtMessageHeader) -> &mut Self { + self.params_mut().dht_header = Some(dht_header); + self + } + + /// Force the message origin to be included in the message. The origin is usually not included in messages without + /// encryption, however this setting will force the message origin and signature to be included. + pub fn force_origin(&mut self) -> &mut Self { + self.params_mut().force_origin = true; + self + } + + /// Return the final SendMessageParams + pub fn finish(&mut self) -> FinalSendMessageParams { + self.params.take().expect("cannot be None") + } + + fn params_mut(&mut self) -> &mut FinalSendMessageParams { + self.params.as_mut().expect("cannot be None") + } +} diff --git a/comms/dht/src/outbound/mock.rs b/comms/dht/src/outbound/mock.rs new file mode 100644 index 0000000000..423eb92a0b --- /dev/null +++ b/comms/dht/src/outbound/mock.rs @@ -0,0 +1,191 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::outbound::{ + message::SendMessageResponse, + message_params::FinalSendMessageParams, + DhtOutboundRequest, + OutboundMessageRequester, +}; +use futures::{channel::mpsc, stream::Fuse, StreamExt}; +use std::{ + sync::{Arc, Condvar, Mutex, RwLock}, + time::Duration, +}; +use tari_comms::message::MessageTag; + +/// Creates a mock outbound request "handler" for testing purposes. +/// +/// Each time a request is expected, handle_next should be called. +pub fn create_outbound_service_mock(size: usize) -> (OutboundMessageRequester, OutboundServiceMock) { + let (tx, rx) = mpsc::channel(size); + (OutboundMessageRequester::new(tx), OutboundServiceMock::new(rx.fuse())) +} + +#[derive(Clone, Default)] +pub struct OutboundServiceMockState { + calls: Arc)>>>, + next_response: Arc>>, + call_count_cond_var: Arc, +} + +impl OutboundServiceMockState { + pub fn new() -> Self { + Self { + calls: Arc::new(Mutex::new(Vec::new())), + next_response: Arc::new(RwLock::new(None)), + call_count_cond_var: Arc::new(Condvar::new()), + } + } + + pub fn call_count(&self) -> usize { + acquire_lock!(self.calls).len() + } + + /// Wait for `num_calls` extra calls or timeout. + /// + /// An error will be returned if the timeout expires. + pub fn wait_call_count(&self, expected_calls: usize, timeout: Duration) -> Result { + let call_guard = acquire_lock!(self.calls); + let (call_guard, is_timeout) = + condvar_shim::wait_timeout_until(&self.call_count_cond_var, call_guard, timeout, |calls| { + calls.len() >= expected_calls + }) + .expect("CondVar must never be poisoned"); + + if is_timeout { + Err(format!( + "wait_call_count timed out before before receiving the expected number of calls. (Expected = {}, Got \ + = {})", + expected_calls, + call_guard.len() + )) + } else { + Ok(call_guard.len()) + } + } + + /// Wait for a call to be added or timeout. + /// + /// An error will be returned if the timeout expires. + pub fn wait_pop_call(&self, timeout: Duration) -> Result<(FinalSendMessageParams, Vec), String> { + let call_guard = acquire_lock!(self.calls); + let (mut call_guard, timeout) = self + .call_count_cond_var + .wait_timeout(call_guard, timeout) + .expect("CondVar must never be poisoned"); + + if timeout.timed_out() { + Err("wait_pop_call timed out before before receiving a call.".to_string()) + } else { + Ok(call_guard.pop().expect("calls.len() must be greater than 1")) + } + } + + pub fn take_next_response(&self) -> Option { + acquire_write_lock!(self.next_response).take() + } + + pub fn add_call(&self, req: (FinalSendMessageParams, Vec)) { + acquire_lock!(self.calls).push(req); + self.call_count_cond_var.notify_all(); + } + + pub fn take_calls(&self) -> Vec<(FinalSendMessageParams, Vec)> { + acquire_lock!(self.calls).drain(..).collect() + } + + pub fn pop_call(&self) -> Option<(FinalSendMessageParams, Vec)> { + acquire_lock!(self.calls).pop() + } +} + +pub struct OutboundServiceMock { + receiver: Fuse>, + mock_state: OutboundServiceMockState, +} + +impl OutboundServiceMock { + pub fn new(receiver: Fuse>) -> Self { + Self { + receiver, + mock_state: OutboundServiceMockState::new(), + } + } + + pub fn get_state(&self) -> OutboundServiceMockState { + self.mock_state.clone() + } + + pub async fn run(mut self) { + while let Some(req) = self.receiver.next().await { + match req { + DhtOutboundRequest::SendMessage(params, body, reply_tx) => { + self.mock_state.add_call((*params, body)); + let response = self + .mock_state + .take_next_response() + .or_else(|| Some(SendMessageResponse::Queued(vec![MessageTag::new()]))) + .expect("never none"); + + reply_tx.send(response).expect("Reply channel cancelled"); + }, + } + } + } +} + +mod condvar_shim { + use std::{ + sync::{Condvar, LockResult, MutexGuard, PoisonError}, + time::{Duration, Instant}, + }; + + pub fn wait_timeout_until<'a, T, F>( + condvar: &Condvar, + mut guard: MutexGuard<'a, T>, + dur: Duration, + mut condition: F, + ) -> LockResult<(MutexGuard<'a, T>, bool)> + where + F: FnMut(&mut T) -> bool, + { + let start = Instant::now(); + loop { + if condition(&mut *guard) { + return Ok((guard, false)); + } + let timeout = match dur.checked_sub(start.elapsed()) { + Some(timeout) => timeout, + None => return Ok((guard, true)), + }; + guard = condvar + .wait_timeout(guard, timeout) + .map(|(guard, timeout)| (guard, timeout.timed_out())) + .map_err(|err| { + let (guard, timeout) = err.into_inner(); + PoisonError::new((guard, timeout.timed_out())) + })? + .0; + } + } +} diff --git a/comms/dht/src/outbound/mod.rs b/comms/dht/src/outbound/mod.rs new file mode 100644 index 0000000000..0cdd71f241 --- /dev/null +++ b/comms/dht/src/outbound/mod.rs @@ -0,0 +1,42 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod broadcast; +mod encryption; +mod error; +mod message; +mod message_params; +mod requester; +mod serialize; + +#[cfg(any(test, feature = "test-mocks"))] +pub mod mock; + +pub use self::{ + broadcast::BroadcastLayer, + encryption::EncryptionLayer, + error::DhtOutboundError, + message::{DhtOutboundRequest, OutboundEncryption, SendMessageResponse}, + message_params::SendMessageParams, + requester::OutboundMessageRequester, + serialize::SerializeLayer, +}; diff --git a/comms/dht/src/outbound/requester.rs b/comms/dht/src/outbound/requester.rs new file mode 100644 index 0000000000..79011af026 --- /dev/null +++ b/comms/dht/src/outbound/requester.rs @@ -0,0 +1,225 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::message::DhtOutboundRequest; +use crate::{ + domain_message::OutboundDomainMessage, + envelope::NodeDestination, + outbound::{ + message::{OutboundEncryption, SendMessageResponse}, + message_params::{FinalSendMessageParams, SendMessageParams}, + DhtOutboundError, + }, +}; +use futures::{ + channel::{mpsc, oneshot}, + SinkExt, +}; +use tari_comms::{message::MessageExt, peer_manager::NodeId, types::CommsPublicKey, wrap_in_envelope_body}; + +#[derive(Clone)] +pub struct OutboundMessageRequester { + sender: mpsc::Sender, +} + +impl OutboundMessageRequester { + pub fn new(sender: mpsc::Sender) -> Self { + Self { sender } + } + + /// Send directly to a peer. + pub async fn send_direct( + &mut self, + dest_public_key: CommsPublicKey, + encryption: OutboundEncryption, + message: OutboundDomainMessage, + ) -> Result + where + T: prost::Message, + { + self.send_message( + SendMessageParams::new() + .direct_public_key(dest_public_key) + .with_encryption(encryption) + .with_discovery(true) + .finish(), + message, + ) + .await + } + + /// Send directly to a peer. + pub async fn send_direct_node_id( + &mut self, + dest_node_id: NodeId, + encryption: OutboundEncryption, + message: OutboundDomainMessage, + ) -> Result + where + T: prost::Message, + { + self.send_message( + SendMessageParams::new() + .direct_node_id(dest_node_id.clone()) + .with_destination(NodeDestination::NodeId(dest_node_id)) + .with_encryption(encryption) + .finish(), + message, + ) + .await + } + + /// Send to a pre-configured number of closest peers. + /// + /// Each message is destined for each peer. + pub async fn send_direct_neighbours( + &mut self, + encryption: OutboundEncryption, + exclude_peers: Vec, + message: OutboundDomainMessage, + ) -> Result + where + T: prost::Message, + { + self.propagate(NodeDestination::Unknown, encryption, exclude_peers, message) + .await + } + + /// Send to a pre-configured number of closest peers, for further message propagation. + /// + /// Optionally, the NodeDestination can be set to propagate to a particular peer, or network region + /// in addition to each peer directly (Same as send_direct_neighbours). + pub async fn propagate( + &mut self, + destination: NodeDestination, + encryption: OutboundEncryption, + exclude_peers: Vec, + message: OutboundDomainMessage, + ) -> Result + where + T: prost::Message, + { + self.send_message( + SendMessageParams::new() + .neighbours(exclude_peers) + .with_encryption(encryption) + .with_destination(destination) + .finish(), + message, + ) + .await + } + + /// Send to _ALL_ known peers. + /// + /// This should be used with caution as, depending on the number of known peers, a lot of network + /// traffic could be generated from this node. + pub async fn send_flood( + &mut self, + destination: NodeDestination, + encryption: OutboundEncryption, + message: OutboundDomainMessage, + ) -> Result + where + T: prost::Message, + { + self.send_message( + SendMessageParams::new() + .flood() + .with_destination(destination) + .with_encryption(encryption) + .finish(), + message, + ) + .await + } + + /// Send to a random subset of peers of size _n_. + pub async fn send_random( + &mut self, + n: usize, + destination: NodeDestination, + encryption: OutboundEncryption, + message: OutboundDomainMessage, + ) -> Result + where + T: prost::Message, + { + self.send_message( + SendMessageParams::new() + .random(n) + .with_destination(destination) + .with_encryption(encryption) + .finish(), + message, + ) + .await + } + + /// Send a message with custom parameters + pub async fn send_message( + &mut self, + params: FinalSendMessageParams, + message: OutboundDomainMessage, + ) -> Result + where + T: prost::Message, + { + let body = wrap_in_envelope_body!(message.to_header(), message.into_inner())?.to_encoded_bytes()?; + self.send_raw(params, body).await + } + + /// Send a message without a domain header part + pub async fn send_message_no_header( + &mut self, + params: FinalSendMessageParams, + message: T, + ) -> Result + where + T: prost::Message, + { + let body = wrap_in_envelope_body!(message)?.to_encoded_bytes()?; + self.send_raw(params, body).await + } + + /// Send a raw message + pub async fn send_raw( + &mut self, + params: FinalSendMessageParams, + body: Vec, + ) -> Result + { + let (reply_tx, reply_rx) = oneshot::channel(); + self.sender + .send(DhtOutboundRequest::SendMessage(Box::new(params), body, reply_tx)) + .await?; + + reply_rx + .await + .map_err(|_| DhtOutboundError::RequesterReplyChannelClosed) + } + + #[cfg(test)] + pub fn get_mpsc_sender(&self) -> mpsc::Sender { + self.sender.clone() + } +} diff --git a/comms/dht/src/outbound/serialize.rs b/comms/dht/src/outbound/serialize.rs new file mode 100644 index 0000000000..3366aab993 --- /dev/null +++ b/comms/dht/src/outbound/serialize.rs @@ -0,0 +1,204 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{outbound::message::DhtOutboundMessage, proto::envelope::DhtEnvelope, PipelineError}; +use futures::{task::Context, Future}; +use log::*; +use rand::rngs::OsRng; +use std::{sync::Arc, task::Poll}; +use tari_comms::{ + message::{MessageExt, OutboundMessage}, + peer_manager::NodeIdentity, + utils::signature, + Bytes, +}; +use tari_crypto::tari_utilities::{hex::Hex, message_format::MessageFormat}; +use tower::{layer::Layer, Service, ServiceExt}; + +const LOG_TARGET: &str = "comms::dht::serialize"; + +#[derive(Clone)] +pub struct SerializeMiddleware { + inner: S, + node_identity: Arc, +} + +impl SerializeMiddleware { + pub fn new(service: S, node_identity: Arc) -> Self { + Self { + inner: service, + node_identity, + } + } +} + +impl Service for SerializeMiddleware +where + S: Service + Clone + 'static, + S::Error: Into, +{ + type Error = PipelineError; + type Response = (); + + type Future = impl Future>; + + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, msg: DhtOutboundMessage) -> Self::Future { + Self::serialize(self.inner.clone(), Arc::clone(&self.node_identity), msg) + } +} + +impl SerializeMiddleware +where + S: Service, + S::Error: Into, +{ + pub async fn serialize( + next_service: S, + node_identity: Arc, + message: DhtOutboundMessage, + ) -> Result<(), PipelineError> + { + debug!(target: LOG_TARGET, "Serializing outbound message {:?}", message.tag); + + let DhtOutboundMessage { + mut dht_header, + body, + destination_peer, + comms_flags, + .. + } = message; + + // The message is being forwarded if the origin public_key is specified and it is not this node + let is_forwarded = dht_header + .origin + .as_ref() + .map(|o| &o.public_key != node_identity.public_key()) + .unwrap_or(false); + + // If forwarding the message, the DhtHeader already has a signature that should not change + if is_forwarded { + trace!( + target: LOG_TARGET, + "Forwarded message {:?}. Message will not be signed", + message.tag + ); + } else { + // Sign the body if the origin public key was previously specified. + if let Some(origin) = dht_header.origin.as_mut() { + let signature = signature::sign(&mut OsRng, node_identity.secret_key().clone(), &body)?; + origin.signature = signature.to_binary()?; + trace!( + target: LOG_TARGET, + "Signed message {:?}: {}", + message.tag, + origin.signature.to_hex() + ); + } + } + + let envelope = DhtEnvelope::new(dht_header.into(), body); + + let body = Bytes::from(envelope.to_encoded_bytes()?); + + next_service + .oneshot(OutboundMessage::with_tag( + message.tag, + destination_peer.node_id, + comms_flags, + body, + )) + .await + .map_err(Into::into) + } +} + +pub struct SerializeLayer { + node_identity: Arc, +} + +impl SerializeLayer { + pub fn new(node_identity: Arc) -> Self { + Self { node_identity } + } +} + +impl Layer for SerializeLayer { + type Service = SerializeMiddleware; + + fn layer(&self, service: S) -> Self::Service { + SerializeMiddleware::new(service, Arc::clone(&self.node_identity)) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::{ + envelope::DhtMessageFlags, + outbound::OutboundEncryption, + test_utils::{make_dht_header, make_node_identity, service_spy}, + }; + use futures::executor::block_on; + use prost::Message; + use tari_comms::{ + message::MessageFlags, + net_address::MultiaddressesWithStats, + peer_manager::{NodeId, Peer, PeerFeatures, PeerFlags}, + types::CommsPublicKey, + }; + use tari_test_utils::panic_context; + + #[test] + fn serialize() { + let spy = service_spy(); + let node_identity = make_node_identity(); + let mut serialize = SerializeLayer::new(Arc::clone(&node_identity)).layer(spy.to_service::()); + + panic_context!(cx); + + assert!(serialize.poll_ready(&mut cx).is_ready()); + let body = b"A".to_vec(); + let msg = DhtOutboundMessage::new( + Peer::new( + CommsPublicKey::default(), + NodeId::default(), + MultiaddressesWithStats::new(vec![]), + PeerFlags::empty(), + PeerFeatures::COMMUNICATION_NODE, + ), + make_dht_header(&node_identity, &body, DhtMessageFlags::empty()), + OutboundEncryption::None, + MessageFlags::empty(), + body, + ); + block_on(serialize.call(msg)).unwrap(); + + let mut msg = spy.pop_request().unwrap(); + let dht_envelope = DhtEnvelope::decode(&mut msg.body).unwrap(); + assert_eq!(dht_envelope.body, b"A".to_vec()); + assert_eq!(msg.peer_node_id, NodeId::default()); + } +} diff --git a/comms/dht/src/proto/dht.proto b/comms/dht/src/proto/dht.proto new file mode 100644 index 0000000000..f72b1c6efb --- /dev/null +++ b/comms/dht/src/proto/dht.proto @@ -0,0 +1,47 @@ +syntax = "proto3"; + +package tari.dht; + +// JoinMessage contains the information required for a network join request. +// +// Message containing contact information for a node wishing to join the network. +// When this message is received the node validates the provided information and, +// if everything checks out, the peer is added to the peer list and the join request +// is propagated to the network. +message JoinMessage { + bytes node_id = 1; + repeated string addresses = 2; + uint64 peer_features = 3; +} + +// The DiscoverMessage stores the information required for a network discover request. +// +// When this message is received and can be decrypted, this node verifies the information +// provided and, if everything checks out, a DiscoveryResponse is sent to the origin of this +// Discovery request so that the source node knows about this node. +message DiscoveryMessage { + bytes node_id = 1; + repeated string addresses = 2; + uint64 peer_features = 3; + uint64 nonce = 4; +} + +message DiscoveryResponseMessage { + bytes node_id = 1; + repeated string addresses = 2; + uint64 peer_features = 3; + uint64 nonce = 4; +} + +message RejectMessage { + // The signature of the rejected message + bytes signature = 1; + // The reason for rejection + RejectMessageReason reason = 2; +} + +enum RejectMessageReason { + RejectMessageReasonUnknown = 0; + // The destination node does not support the specified network + RejectMessageReasonUnsupportedNetwork = 1; +} \ No newline at end of file diff --git a/comms/dht/src/proto/envelope.proto b/comms/dht/src/proto/envelope.proto new file mode 100644 index 0000000000..43f1815cee --- /dev/null +++ b/comms/dht/src/proto/envelope.proto @@ -0,0 +1,62 @@ +syntax = "proto3"; + +package tari.dht.envelope; + +enum DhtMessageType { + // Indicated this message is not a DHT message + DhtMessageTypeNone = 0; + // Join Request + DhtMessageTypeJoin = 1; + // Discovery request + DhtMessageTypeDiscovery = 2; + // Response to a discovery request + DhtMessageTypeDiscoveryResponse = 3; + // Message was rejected + DhtMessageTypeRejectMsg = 4; + // Request stored messages from a node + DhtMessageTypeSafRequestMessages = 20; + // Stored messages response + DhtMessageTypeSafStoredMessages = 21; +} + +message DhtHeader { + uint32 version = 1; + oneof destination { + // The sender has chosen not to disclose the message destination, or the destination is + // the peer being sent to. + bool unknown = 2; + // Destined for a particular public key + bytes public_key = 3; + // Destined for a particular node id, or network region + bytes node_id = 4; + } + + // Origin public key of the message. This can be the same peer that sent the message + // or another peer if the message should be forwarded. This is optional but must be specified + // if the ENCRYPTED flag is set. + DhtOrigin origin = 5; + // The type of message + DhtMessageType message_type = 6; + // The network for which this message is intended (e.g. TestNet, MainNet etc.) + Network network = 7; + uint32 flags = 8; +} + +enum Network { + // Main net (default) + NetworkMainNet = 0; + // Test net + NetworkTestNet = 1; + // Network used for local tests + NetworkLocalTest = 2; +} + +message DhtEnvelope { + DhtHeader header = 1; + bytes body = 2; +} + +message DhtOrigin { + bytes public_key = 1; + bytes signature = 2; +} \ No newline at end of file diff --git a/base_layer/mining/src/lib.rs b/comms/dht/src/proto/google.protobuf.rs similarity index 100% rename from base_layer/mining/src/lib.rs rename to comms/dht/src/proto/google.protobuf.rs diff --git a/comms/dht/src/proto/message_header.proto b/comms/dht/src/proto/message_header.proto new file mode 100644 index 0000000000..067636ab30 --- /dev/null +++ b/comms/dht/src/proto/message_header.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +package tari.dht.message_header; + +message MessageHeader { + // Indicates a type of message. This can be any enum type + int32 message_type = 1; + uint64 nonce = 2; +} diff --git a/comms/dht/src/proto/mod.rs b/comms/dht/src/proto/mod.rs new file mode 100644 index 0000000000..a7e0e1fc89 --- /dev/null +++ b/comms/dht/src/proto/mod.rs @@ -0,0 +1,33 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#[path = "tari.dht.envelope.rs"] +pub mod envelope; + +#[path = "tari.dht.rs"] +pub mod dht; + +#[path = "tari.dht.store_forward.rs"] +pub mod store_forward; + +#[path = "tari.dht.message_header.rs"] +pub mod message_header; diff --git a/comms/dht/src/proto/store_forward.proto b/comms/dht/src/proto/store_forward.proto new file mode 100644 index 0000000000..702aee4225 --- /dev/null +++ b/comms/dht/src/proto/store_forward.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; + +import "google/protobuf/struct.proto"; +import "google/protobuf/timestamp.proto"; +import "envelope.proto"; + +package tari.dht.store_forward; + +// The RetrieveMessageRequest is used for requesting the set of stored messages from neighbouring peer nodes. If a +// start_time is provided then only messages after the specified time will be sent, otherwise all applicable messages +// will be sent. +message StoredMessagesRequest { + google.protobuf.Timestamp since = 1; +} + +// Storage for a single message envelope, including the date and time when the element was stored +message StoredMessage { + google.protobuf.Timestamp stored_at = 1; + uint32 version = 2; + tari.dht.envelope.DhtHeader dht_header = 3; + bytes encrypted_body = 4; +} + +// The StoredMessages contains the set of applicable messages retrieved from a neighbouring peer node. +message StoredMessagesResponse { + repeated StoredMessage messages = 1; +} diff --git a/comms/dht/src/proto/tari.dht.envelope.rs b/comms/dht/src/proto/tari.dht.envelope.rs new file mode 100644 index 0000000000..f7e24f31b2 --- /dev/null +++ b/comms/dht/src/proto/tari.dht.envelope.rs @@ -0,0 +1,77 @@ +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DhtHeader { + #[prost(uint32, tag = "1")] + pub version: u32, + /// Origin public key of the message. This can be the same peer that sent the message + /// or another peer if the message should be forwarded. This is optional but must be specified + /// if the ENCRYPTED flag is set. + #[prost(message, optional, tag = "5")] + pub origin: ::std::option::Option, + /// The type of message + #[prost(enumeration = "DhtMessageType", tag = "6")] + pub message_type: i32, + /// The network for which this message is intended (e.g. TestNet, MainNet etc.) + #[prost(enumeration = "Network", tag = "7")] + pub network: i32, + #[prost(uint32, tag = "8")] + pub flags: u32, + #[prost(oneof = "dht_header::Destination", tags = "2, 3, 4")] + pub destination: ::std::option::Option, +} +pub mod dht_header { + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Destination { + /// The sender has chosen not to disclose the message destination, or the destination is + /// the peer being sent to. + #[prost(bool, tag = "2")] + Unknown(bool), + /// Destined for a particular public key + #[prost(bytes, tag = "3")] + PublicKey(std::vec::Vec), + /// Destined for a particular node id, or network region + #[prost(bytes, tag = "4")] + NodeId(std::vec::Vec), + } +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DhtEnvelope { + #[prost(message, optional, tag = "1")] + pub header: ::std::option::Option, + #[prost(bytes, tag = "2")] + pub body: std::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DhtOrigin { + #[prost(bytes, tag = "1")] + pub public_key: std::vec::Vec, + #[prost(bytes, tag = "2")] + pub signature: std::vec::Vec, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum DhtMessageType { + /// Indicated this message is not a DHT message + None = 0, + /// Join Request + Join = 1, + /// Discovery request + Discovery = 2, + /// Response to a discovery request + DiscoveryResponse = 3, + /// Message was rejected + RejectMsg = 4, + /// Request stored messages from a node + SafRequestMessages = 20, + /// Stored messages response + SafStoredMessages = 21, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum Network { + /// Main net (default) + MainNet = 0, + /// Test net + TestNet = 1, + /// Network used for local tests + LocalTest = 2, +} diff --git a/comms/dht/src/proto/tari.dht.message_header.rs b/comms/dht/src/proto/tari.dht.message_header.rs new file mode 100644 index 0000000000..34347d52b1 --- /dev/null +++ b/comms/dht/src/proto/tari.dht.message_header.rs @@ -0,0 +1,8 @@ +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MessageHeader { + /// Indicates a type of message. This can be any enum type + #[prost(int32, tag = "1")] + pub message_type: i32, + #[prost(uint64, tag = "2")] + pub nonce: u64, +} diff --git a/comms/dht/src/proto/tari.dht.rs b/comms/dht/src/proto/tari.dht.rs new file mode 100644 index 0000000000..d1c066ec6f --- /dev/null +++ b/comms/dht/src/proto/tari.dht.rs @@ -0,0 +1,58 @@ +/// JoinMessage contains the information required for a network join request. +/// +/// Message containing contact information for a node wishing to join the network. +/// When this message is received the node validates the provided information and, +/// if everything checks out, the peer is added to the peer list and the join request +/// is propagated to the network. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct JoinMessage { + #[prost(bytes, tag = "1")] + pub node_id: std::vec::Vec, + #[prost(string, repeated, tag = "2")] + pub addresses: ::std::vec::Vec, + #[prost(uint64, tag = "3")] + pub peer_features: u64, +} +/// The DiscoverMessage stores the information required for a network discover request. +/// +/// When this message is received and can be decrypted, this node verifies the information +/// provided and, if everything checks out, a DiscoveryResponse is sent to the origin of this +/// Discovery request so that the source node knows about this node. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DiscoveryMessage { + #[prost(bytes, tag = "1")] + pub node_id: std::vec::Vec, + #[prost(string, repeated, tag = "2")] + pub addresses: ::std::vec::Vec, + #[prost(uint64, tag = "3")] + pub peer_features: u64, + #[prost(uint64, tag = "4")] + pub nonce: u64, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DiscoveryResponseMessage { + #[prost(bytes, tag = "1")] + pub node_id: std::vec::Vec, + #[prost(string, repeated, tag = "2")] + pub addresses: ::std::vec::Vec, + #[prost(uint64, tag = "3")] + pub peer_features: u64, + #[prost(uint64, tag = "4")] + pub nonce: u64, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RejectMessage { + /// The signature of the rejected message + #[prost(bytes, tag = "1")] + pub signature: std::vec::Vec, + /// The reason for rejection + #[prost(enumeration = "RejectMessageReason", tag = "2")] + pub reason: i32, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum RejectMessageReason { + Unknown = 0, + /// The destination node does not support the specified network + UnsupportedNetwork = 1, +} diff --git a/comms/dht/src/proto/tari.dht.store_forward.rs b/comms/dht/src/proto/tari.dht.store_forward.rs new file mode 100644 index 0000000000..f1f881adaf --- /dev/null +++ b/comms/dht/src/proto/tari.dht.store_forward.rs @@ -0,0 +1,26 @@ +/// The RetrieveMessageRequest is used for requesting the set of stored messages from neighbouring peer nodes. If a +/// start_time is provided then only messages after the specified time will be sent, otherwise all applicable messages +/// will be sent. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct StoredMessagesRequest { + #[prost(message, optional, tag = "1")] + pub since: ::std::option::Option<::prost_types::Timestamp>, +} +/// Storage for a single message envelope, including the date and time when the element was stored +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct StoredMessage { + #[prost(message, optional, tag = "1")] + pub stored_at: ::std::option::Option<::prost_types::Timestamp>, + #[prost(uint32, tag = "2")] + pub version: u32, + #[prost(message, optional, tag = "3")] + pub dht_header: ::std::option::Option, + #[prost(bytes, tag = "4")] + pub encrypted_body: std::vec::Vec, +} +/// The StoredMessages contains the set of applicable messages retrieved from a neighbouring peer node. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct StoredMessagesResponse { + #[prost(message, repeated, tag = "1")] + pub messages: ::std::vec::Vec, +} diff --git a/comms/dht/src/store_forward/error.rs b/comms/dht/src/store_forward/error.rs new file mode 100644 index 0000000000..dc0f084949 --- /dev/null +++ b/comms/dht/src/store_forward/error.rs @@ -0,0 +1,61 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{actor::DhtActorError, envelope::DhtMessageError, outbound::DhtOutboundError}; +use derive_error::Error; +use prost::DecodeError; +use std::io; +use tari_comms::{message::MessageError, peer_manager::PeerManagerError}; +use tari_crypto::tari_utilities::ciphers::cipher::CipherError; + +#[derive(Debug, Error)] +pub enum StoreAndForwardError { + DhtMessageError(DhtMessageError), + MessageError(MessageError), + PeerManagerError(PeerManagerError), + DhtOutboundError(DhtOutboundError), + /// Received stored message has an invalid destination + InvalidDestination, + /// Received stored message has an invalid origin signature + InvalidSignature, + /// Invalid envelope body + InvalidEnvelopeBody, + /// Received stored message which is not encrypted + StoredMessageNotEncrypted, + /// Unable to decrypt received stored message + DecryptionFailed, + CipherError(CipherError), + DhtActorError(DhtActorError), + /// Received duplicate stored message + DuplicateMessage, + CurrentThreadRuntimeInitializeFailed(io::Error), + /// Unable to decode message + DecodeError(DecodeError), + /// Dht header was not provided + DhtHeaderNotProvided, + /// Failed to spawn blocking task + JoinError(tokio::task::JoinError), + /// Message origin is for all forwarded messages + MessageOriginRequired, + /// The message was malformed + MalformedMessage, +} diff --git a/comms/dht/src/store_forward/forward.rs b/comms/dht/src/store_forward/forward.rs new file mode 100644 index 0000000000..236f81cfdd --- /dev/null +++ b/comms/dht/src/store_forward/forward.rs @@ -0,0 +1,256 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + envelope::NodeDestination, + inbound::DecryptedDhtMessage, + outbound::{OutboundMessageRequester, SendMessageParams}, + store_forward::error::StoreAndForwardError, + PipelineError, +}; +use futures::{task::Context, Future}; +use log::*; +use std::{sync::Arc, task::Poll}; +use tari_comms::{peer_manager::PeerManager, types::CommsPublicKey}; +use tower::{layer::Layer, Service, ServiceExt}; + +const LOG_TARGET: &str = "comms::store_forward::forward"; + +/// This layer is responsible for forwarding messages which have failed to decrypt +pub struct ForwardLayer { + peer_manager: Arc, + outbound_service: OutboundMessageRequester, +} + +impl ForwardLayer { + pub fn new(peer_manager: Arc, outbound_service: OutboundMessageRequester) -> Self { + Self { + peer_manager, + outbound_service, + } + } +} + +impl Layer for ForwardLayer { + type Service = ForwardMiddleware; + + fn layer(&self, service: S) -> Self::Service { + ForwardMiddleware::new( + service, + // Pass in just the config item needed by the middleware for almost free copies + Arc::clone(&self.peer_manager), + self.outbound_service.clone(), + ) + } +} + +/// # Forward middleware +/// +/// Responsible for forwarding messages which fail to decrypt. +#[derive(Clone)] +pub struct ForwardMiddleware { + next_service: S, + peer_manager: Arc, + outbound_service: OutboundMessageRequester, +} + +impl ForwardMiddleware { + pub fn new(service: S, peer_manager: Arc, outbound_service: OutboundMessageRequester) -> Self { + Self { + next_service: service, + peer_manager, + outbound_service, + } + } +} + +impl Service for ForwardMiddleware +where + S: Service + Clone + 'static, + S::Error: Into, +{ + type Error = PipelineError; + type Response = (); + + type Future = impl Future>; + + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, msg: DecryptedDhtMessage) -> Self::Future { + Forwarder::new( + self.next_service.clone(), + Arc::clone(&self.peer_manager), + self.outbound_service.clone(), + ) + .handle(msg) + } +} + +/// Responsible for processing a single DecryptedDhtMessage, forwarding if necessary or passing the message +/// to the next service. +struct Forwarder { + peer_manager: Arc, + next_service: S, + outbound_service: OutboundMessageRequester, +} + +impl Forwarder { + pub fn new(service: S, peer_manager: Arc, outbound_service: OutboundMessageRequester) -> Self { + Self { + peer_manager, + next_service: service, + outbound_service, + } + } +} + +impl Forwarder +where + S: Service, + S::Error: Into, +{ + async fn handle(mut self, message: DecryptedDhtMessage) -> Result<(), PipelineError> { + if message.decryption_failed() { + debug!(target: LOG_TARGET, "Decryption failed. Forwarding message"); + self.forward(&message).await?; + } + + // The message has been forwarded, but other middleware may be interested (i.e. StoreMiddleware) + trace!(target: LOG_TARGET, "Passing message to next service"); + self.next_service.oneshot(message).await.map_err(Into::into)?; + Ok(()) + } + + async fn forward(&mut self, message: &DecryptedDhtMessage) -> Result<(), StoreAndForwardError> { + let DecryptedDhtMessage { + source_peer, + decryption_result, + dht_header, + .. + } = message; + + let body = decryption_result + .clone() + .err() + .expect("previous check that decryption failed"); + + let mut message_params = + self.get_send_params(dht_header.destination.clone(), vec![source_peer.public_key.clone()])?; + + message_params.with_dht_header(dht_header.clone()); + + self.outbound_service.send_raw(message_params.finish(), body).await?; + + Ok(()) + } + + /// Selects the most appropriate broadcast strategy based on the received messages destination + fn get_send_params( + &self, + header_dest: NodeDestination, + excluded_peers: Vec, + ) -> Result + { + let mut params = SendMessageParams::new(); + match header_dest { + NodeDestination::Unknown => { + // Send to the current nodes nearest neighbours + params.neighbours(excluded_peers); + }, + NodeDestination::PublicKey(dest_public_key) => { + if self.peer_manager.exists(&dest_public_key) { + // Send to destination peer directly if the current node knows that peer + params.direct_public_key(dest_public_key); + } else { + // Send to the current nodes nearest neighbours + params.neighbours(excluded_peers); + } + }, + NodeDestination::NodeId(dest_node_id) => { + match self.peer_manager.find_by_node_id(&dest_node_id) { + Ok(dest_peer) => { + // Send to destination peer directly if the current node knows that peer + params.direct_public_key(dest_peer.public_key); + }, + Err(_) => { + // Send to peers that are closest to the destination network region + params.neighbours(excluded_peers); + }, + } + }, + } + + Ok(params) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::{ + envelope::DhtMessageFlags, + outbound::mock::create_outbound_service_mock, + test_utils::{make_dht_inbound_message, make_node_identity, make_peer_manager, service_spy}, + }; + use futures::{channel::mpsc, executor::block_on}; + use tari_comms::wrap_in_envelope_body; + use tokio::runtime::Runtime; + + #[test] + fn decryption_succeeded() { + let spy = service_spy(); + let peer_manager = make_peer_manager(); + let (oms_tx, mut oms_rx) = mpsc::channel(1); + let oms = OutboundMessageRequester::new(oms_tx); + let mut service = ForwardLayer::new(peer_manager, oms).layer(spy.to_service::()); + + let inbound_msg = make_dht_inbound_message(&make_node_identity(), b"".to_vec(), DhtMessageFlags::empty()); + let msg = DecryptedDhtMessage::succeeded(wrap_in_envelope_body!(Vec::new()).unwrap(), inbound_msg); + block_on(service.call(msg)).unwrap(); + assert!(spy.is_called()); + assert!(oms_rx.try_next().is_err()); + } + + #[test] + fn decryption_failed() { + let mut rt = Runtime::new().unwrap(); + let spy = service_spy(); + let peer_manager = make_peer_manager(); + let (oms_requester, oms_mock) = create_outbound_service_mock(1); + let oms_mock_state = oms_mock.get_state(); + rt.spawn(oms_mock.run()); + + let mut service = ForwardLayer::new(peer_manager, oms_requester).layer(spy.to_service::()); + + let inbound_msg = make_dht_inbound_message(&make_node_identity(), b"".to_vec(), DhtMessageFlags::empty()); + let msg = DecryptedDhtMessage::failed(inbound_msg); + rt.block_on(service.call(msg)).unwrap(); + assert!(spy.is_called()); + + assert_eq!(oms_mock_state.call_count(), 1); + let (params, _) = oms_mock_state.pop_call().unwrap(); + + assert!(params.dht_header.is_some()); + } +} diff --git a/comms/dht/src/store_forward/message.rs b/comms/dht/src/store_forward/message.rs new file mode 100644 index 0000000000..3c98383b82 --- /dev/null +++ b/comms/dht/src/store_forward/message.rs @@ -0,0 +1,71 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + envelope::DhtMessageHeader, + proto::store_forward::{StoredMessage, StoredMessagesRequest, StoredMessagesResponse}, +}; +use chrono::{DateTime, Utc}; +use prost_types::Timestamp; + +/// Utility function that converts a `chrono::DateTime` to a `prost::Timestamp` +pub(crate) fn datetime_to_timestamp(datetime: DateTime) -> Timestamp { + Timestamp { + seconds: datetime.timestamp(), + nanos: datetime.timestamp_subsec_nanos() as i32, + } +} + +impl StoredMessagesRequest { + pub fn since(since: DateTime) -> Self { + Self { + since: Some(datetime_to_timestamp(since)), + } + } +} + +impl StoredMessage { + pub fn new(version: u32, dht_header: DhtMessageHeader, encrypted_body: Vec) -> Self { + Self { + version, + dht_header: Some(dht_header.into()), + encrypted_body, + stored_at: Some(datetime_to_timestamp(Utc::now())), + } + } + + pub fn has_required_fields(&self) -> bool { + self.dht_header.is_some() + } +} + +impl StoredMessagesResponse { + pub fn messages(&self) -> &Vec { + &self.messages + } +} + +impl From> for StoredMessagesResponse { + fn from(messages: Vec) -> Self { + Self { messages } + } +} diff --git a/comms/dht/src/store_forward/mod.rs b/comms/dht/src/store_forward/mod.rs new file mode 100644 index 0000000000..9b9e08b282 --- /dev/null +++ b/comms/dht/src/store_forward/mod.rs @@ -0,0 +1,36 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod error; +mod forward; +mod message; +mod saf_handler; +mod state; +mod store; + +pub use self::{ + error::StoreAndForwardError, + forward::ForwardLayer, + saf_handler::MessageHandlerLayer, + state::SafStorage, + store::StoreLayer, +}; diff --git a/comms/dht/src/store_forward/saf_handler/layer.rs b/comms/dht/src/store_forward/saf_handler/layer.rs new file mode 100644 index 0000000000..6622a3b713 --- /dev/null +++ b/comms/dht/src/store_forward/saf_handler/layer.rs @@ -0,0 +1,73 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::middleware::MessageHandlerMiddleware; +use crate::{actor::DhtRequester, config::DhtConfig, outbound::OutboundMessageRequester, store_forward::SafStorage}; +use std::sync::Arc; +use tari_comms::peer_manager::{NodeIdentity, PeerManager}; +use tower::layer::Layer; + +pub struct MessageHandlerLayer { + config: DhtConfig, + store: Arc, + dht_requester: DhtRequester, + peer_manager: Arc, + node_identity: Arc, + outbound_service: OutboundMessageRequester, +} + +impl MessageHandlerLayer { + pub fn new( + config: DhtConfig, + store: Arc, + dht_requester: DhtRequester, + node_identity: Arc, + peer_manager: Arc, + outbound_service: OutboundMessageRequester, + ) -> Self + { + Self { + config, + store, + dht_requester, + node_identity, + peer_manager, + outbound_service, + } + } +} + +impl Layer for MessageHandlerLayer { + type Service = MessageHandlerMiddleware; + + fn layer(&self, service: S) -> Self::Service { + MessageHandlerMiddleware::new( + self.config.clone(), + service, + Arc::clone(&self.store), + self.dht_requester.clone(), + Arc::clone(&self.node_identity), + Arc::clone(&self.peer_manager), + self.outbound_service.clone(), + ) + } +} diff --git a/comms/dht/src/store_forward/saf_handler/middleware.rs b/comms/dht/src/store_forward/saf_handler/middleware.rs new file mode 100644 index 0000000000..057e4d050c --- /dev/null +++ b/comms/dht/src/store_forward/saf_handler/middleware.rs @@ -0,0 +1,96 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::task::MessageHandlerTask; +use crate::{ + actor::DhtRequester, + config::DhtConfig, + inbound::DecryptedDhtMessage, + outbound::OutboundMessageRequester, + store_forward::SafStorage, + PipelineError, +}; +use futures::{task::Context, Future}; +use std::{sync::Arc, task::Poll}; +use tari_comms::peer_manager::{NodeIdentity, PeerManager}; +use tower::Service; + +#[derive(Clone)] +pub struct MessageHandlerMiddleware { + config: DhtConfig, + next_service: S, + store: Arc, + dht_requester: DhtRequester, + peer_manager: Arc, + node_identity: Arc, + outbound_service: OutboundMessageRequester, +} + +impl MessageHandlerMiddleware { + pub fn new( + config: DhtConfig, + next_service: S, + store: Arc, + dht_requester: DhtRequester, + node_identity: Arc, + peer_manager: Arc, + outbound_service: OutboundMessageRequester, + ) -> Self + { + Self { + config, + store, + dht_requester, + next_service, + node_identity, + peer_manager, + outbound_service, + } + } +} + +impl Service for MessageHandlerMiddleware +where S: Service + Clone + Sync + Send +{ + type Error = PipelineError; + type Response = (); + + type Future = impl Future>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.next_service.poll_ready(cx).map_err(Into::into) + } + + fn call(&mut self, message: DecryptedDhtMessage) -> Self::Future { + MessageHandlerTask::new( + self.config.clone(), + self.next_service.clone(), + Arc::clone(&self.store), + self.dht_requester.clone(), + Arc::clone(&self.peer_manager), + self.outbound_service.clone(), + Arc::clone(&self.node_identity), + message, + ) + .run() + } +} diff --git a/comms/dht/src/store_forward/saf_handler/mod.rs b/comms/dht/src/store_forward/saf_handler/mod.rs new file mode 100644 index 0000000000..df5cb40552 --- /dev/null +++ b/comms/dht/src/store_forward/saf_handler/mod.rs @@ -0,0 +1,27 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod layer; +mod middleware; +mod task; + +pub use layer::MessageHandlerLayer; diff --git a/comms/dht/src/store_forward/saf_handler/task.rs b/comms/dht/src/store_forward/saf_handler/task.rs new file mode 100644 index 0000000000..78c4035639 --- /dev/null +++ b/comms/dht/src/store_forward/saf_handler/task.rs @@ -0,0 +1,595 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + actor::DhtRequester, + config::DhtConfig, + crypt, + envelope::{Destination, DhtMessageFlags, DhtMessageHeader, DhtMessageOrigin, NodeDestination}, + inbound::{DecryptedDhtMessage, DhtInboundMessage}, + outbound::{OutboundMessageRequester, SendMessageParams}, + proto::{ + envelope::DhtMessageType, + store_forward::{StoredMessage, StoredMessagesRequest, StoredMessagesResponse}, + }, + store_forward::{error::StoreAndForwardError, SafStorage}, + utils::hoist_nested_result, + PipelineError, +}; +use digest::Digest; +use futures::{future, stream, Future, FutureExt, StreamExt}; +use log::*; +use prost::Message; +use std::{convert::TryInto, sync::Arc}; +use tari_comms::{ + message::EnvelopeBody, + peer_manager::{NodeIdentity, Peer, PeerManager, PeerManagerError}, + types::Challenge, + utils::signature, +}; +use tari_crypto::tari_utilities::ByteArray; +use tokio::{runtime, task}; +use tower::{Service, ServiceExt}; + +const LOG_TARGET: &str = "comms::dht::store_forward"; + +pub struct MessageHandlerTask { + config: DhtConfig, + next_service: S, + dht_requester: DhtRequester, + peer_manager: Arc, + outbound_service: OutboundMessageRequester, + node_identity: Arc, + message: Option, + store: Arc, +} + +impl MessageHandlerTask +where S: Service +{ + pub fn new( + config: DhtConfig, + next_service: S, + store: Arc, + dht_requester: DhtRequester, + peer_manager: Arc, + outbound_service: OutboundMessageRequester, + node_identity: Arc, + message: DecryptedDhtMessage, + ) -> Self + { + Self { + config, + store, + dht_requester, + next_service, + peer_manager, + outbound_service, + node_identity, + message: Some(message), + } + } + + pub async fn run(mut self) -> Result<(), PipelineError> { + let message = self + .message + .take() + .expect("DhtInboundMessageTask initialized without message"); + + if message.dht_header.message_type.is_dht_message() && message.decryption_failed() { + debug!( + target: LOG_TARGET, + "Received SAFRetrieveMessages message which could not decrypt from NodeId={}. Discarding message.", + message.source_peer.node_id + ); + return Ok(()); + } + + match message.dht_header.message_type { + DhtMessageType::SafRequestMessages => self.handle_stored_messages_request(message).await?, + + DhtMessageType::SafStoredMessages => self.handle_stored_messages(message).await?, + // Not a SAF message, call downstream middleware + _ => { + trace!(target: LOG_TARGET, "Passing message onto next service"); + self.next_service.oneshot(message).await? + }, + } + + Ok(()) + } + + async fn handle_stored_messages_request( + &mut self, + message: DecryptedDhtMessage, + ) -> Result<(), StoreAndForwardError> + { + trace!( + target: LOG_TARGET, + "Received request for stored message from {}", + message.source_peer.public_key + ); + let msg = message + .success() + .expect("already checked that this message decrypted successfully"); + + let retrieve_msgs = msg + .decode_part::(0)? + .ok_or_else(|| StoreAndForwardError::InvalidEnvelopeBody)?; + + if !self.peer_manager.in_network_region( + &message.source_peer.node_id, + self.node_identity.node_id(), + self.config.saf_num_closest_nodes, + )? { + debug!( + target: LOG_TARGET, + "Received store and forward message requests from node outside of this nodes network region" + ); + return Ok(()); + } + + // Compile a set of stored messages for the requesting peer + let messages = self.store.with_lock(|mut store| { + store + .iter() + // All messages within start_time (if specified) + .filter(|(_, msg)| { + retrieve_msgs.since.as_ref().map(|since| msg.stored_at.as_ref().map(|s| since.seconds <= s.seconds).unwrap_or( false)).unwrap_or( true) + }) + .filter(|(_, msg)|{ + if msg.dht_header.is_none() { + warn!(target: LOG_TARGET, "Message was stored without a header. This should never happen!"); + return false; + } + let dht_header = msg.dht_header.as_ref().expect("previously checked"); + + match &dht_header.destination { + None=> false, + // The stored message was sent with an undisclosed recipient. Perhaps this node + // is interested in it + Some(Destination::Unknown(_)) => true, + // Was the stored message sent for the requesting node public key? + Some(Destination::PublicKey(pk)) => pk.as_slice() == message.source_peer.public_key.as_bytes(), + // Was the stored message sent for the requesting node node id? + Some( Destination::NodeId(node_id)) => node_id.as_slice() == message.source_peer.node_id.as_bytes(), + } + }) + .take(self.config.saf_max_returned_messages) + .map(|(_, msg)| msg) + .cloned() + .collect::>() + }); + + let stored_messages: StoredMessagesResponse = messages.into(); + + trace!( + target: LOG_TARGET, + "Responding to received message retrieval request with {} message(s)", + stored_messages.messages().len() + ); + self.outbound_service + .send_message_no_header( + SendMessageParams::new() + .direct_public_key(message.source_peer.public_key.clone()) + .with_dht_message_type(DhtMessageType::SafStoredMessages) + .finish(), + stored_messages, + ) + .await?; + + Ok(()) + } + + async fn handle_stored_messages(self, message: DecryptedDhtMessage) -> Result<(), StoreAndForwardError> { + trace!( + target: LOG_TARGET, + "Received stored messages from {}", + message.source_peer.public_key + ); + // TODO: Should check that stored messages were requested before accepting them + let msg = message + .success() + .expect("already checked that this message decrypted successfully"); + let response = msg + .decode_part::(0)? + .ok_or_else(|| StoreAndForwardError::InvalidEnvelopeBody)?; + let source_peer = Arc::new(message.source_peer); + + debug!( + target: LOG_TARGET, + "Received {} stored messages from peer", + response.messages().len() + ); + + let tasks = response + .messages + .into_iter() + // Map to futures which process the stored message + .map(|msg| self.process_incoming_stored_message(Arc::clone(&source_peer), msg)); + + let successful_msgs_iter = future::join_all(tasks) + .await + .into_iter() + .map(|result| { + match &result { + // Failed decryption is acceptable, the message wasn't for this node so we + // simply discard the message. + // TODO: Should we add this message to our SAF store? + Err(err @ StoreAndForwardError::DecryptionFailed) => { + debug!( + target: LOG_TARGET, + "Unable to decrypt stored message sent by {}: {}", + source_peer.node_id.short_str(), + err + ); + }, + // The peer that originally sent this message is not known to us. + // TODO: Should we try to discover this peer? + Err(StoreAndForwardError::PeerManagerError(PeerManagerError::PeerNotFoundError)) => { + debug!(target: LOG_TARGET, "Origin peer not found. Discarding stored message."); + }, + + // Failed to send request to Dht Actor, something has gone very wrong + Err(StoreAndForwardError::DhtActorError(err)) => { + error!( + target: LOG_TARGET, + "DhtActor returned an error. {}. This could indicate a system malfunction.", err + ); + }, + // Duplicate message detected, no problem it happens. + Err(StoreAndForwardError::DuplicateMessage) => { + debug!( + target: LOG_TARGET, + "Store and forward received a duplicate message. Message discarded." + ); + }, + + // Every other error shouldn't happen if the sending node is behaving + Err(err) => { + // TODO: #banheuristics + warn!( + target: LOG_TARGET, + "SECURITY: invalid store and forward message was discarded from NodeId={}. Reason: {}. \ + These messages should never have been forwarded. This is a sign of a badly behaving node.", + source_peer.node_id.short_str(), + err + ); + }, + _ => {}, + } + + result + }) + .filter(Result::is_ok) + .map(Result::unwrap); + + self.next_service + .call_all(stream::iter(successful_msgs_iter)) + .unordered() + .for_each(|service_result| { + if let Err(err) = service_result { + error!(target: LOG_TARGET, "Error when calling next service: {}", err); + } + future::ready(()) + }) + .await; + + Ok(()) + } + + fn process_incoming_stored_message( + &self, + source_peer: Arc, + message: StoredMessage, + ) -> impl Future> + { + let node_identity = Arc::clone(&self.node_identity); + let peer_manager = Arc::clone(&self.peer_manager); + let config = self.config.clone(); + let mut dht_requester = self.dht_requester.clone(); + task::spawn_blocking(move || { + if message.dht_header.is_none() { + return Err(StoreAndForwardError::DhtHeaderNotProvided); + } + + let dht_header: DhtMessageHeader = message + .dht_header + .expect("previously checked") + .try_into() + .map_err(StoreAndForwardError::DhtMessageError)?; + + let dht_flags = dht_header.flags; + + let origin = dht_header + .origin + .as_ref() + .ok_or_else(|| StoreAndForwardError::MessageOriginRequired)?; + + // Check that the destination is either undisclosed + Self::check_destination(&config, &peer_manager, &node_identity, &dht_header)?; + // Verify the signature + Self::check_signature(origin, &message.encrypted_body)?; + // Check that the message has not already been received. + // The current thread runtime is used because calls to the DHT actor are async + let mut rt = runtime::Builder::new().basic_scheduler().build()?; + rt.block_on(Self::check_duplicate(&mut dht_requester, &message.encrypted_body))?; + + // Attempt to decrypt the message (if applicable), and deserialize it + let decrypted_body = + Self::maybe_decrypt_and_deserialize(&node_identity, origin, dht_flags, &message.encrypted_body)?; + + let inbound_msg = DhtInboundMessage::new(dht_header, Arc::clone(&source_peer), message.encrypted_body); + + Ok(DecryptedDhtMessage::succeeded(decrypted_body, inbound_msg)) + }) + .map(hoist_nested_result) + } + + async fn check_duplicate(dht_requester: &mut DhtRequester, body: &[u8]) -> Result<(), StoreAndForwardError> { + let msg_hash = Challenge::new().chain(body).result().to_vec(); + if dht_requester.insert_message_hash(msg_hash).await? { + Err(StoreAndForwardError::DuplicateMessage) + } else { + Ok(()) + } + } + + fn check_destination( + config: &DhtConfig, + peer_manager: &PeerManager, + node_identity: &NodeIdentity, + dht_header: &DhtMessageHeader, + ) -> Result<(), StoreAndForwardError> + { + Some(&dht_header.destination) + .filter(|destination| match destination { + NodeDestination::Unknown => true, + NodeDestination::PublicKey(pk) => node_identity.public_key() == pk, + NodeDestination::NodeId(node_id) => { + // Pass this check if the node id equals ours or is in this node's region + if node_identity.node_id() == node_id { + return true; + } + + peer_manager + .in_network_region(node_identity.node_id(), node_id, config.num_neighbouring_nodes) + .unwrap_or(false) + }, + }) + .map(|_| ()) + .ok_or_else(|| StoreAndForwardError::InvalidDestination) + } + + fn check_signature(origin: &DhtMessageOrigin, body: &[u8]) -> Result<(), StoreAndForwardError> { + signature::verify(&origin.public_key, &origin.signature, body) + .map_err(|_| StoreAndForwardError::InvalidSignature) + .and_then(|is_valid| { + if is_valid { + Ok(()) + } else { + Err(StoreAndForwardError::InvalidSignature) + } + }) + } + + fn maybe_decrypt_and_deserialize( + node_identity: &NodeIdentity, + origin: &DhtMessageOrigin, + flags: DhtMessageFlags, + body: &[u8], + ) -> Result + { + if flags.contains(DhtMessageFlags::ENCRYPTED) { + let shared_secret = crypt::generate_ecdh_secret(node_identity.secret_key(), &origin.public_key); + let decrypted_bytes = crypt::decrypt(&shared_secret, body)?; + EnvelopeBody::decode(decrypted_bytes.as_slice()).map_err(|_| StoreAndForwardError::DecryptionFailed) + } else { + // Malformed cleartext messages should never have been forwarded by the peer + EnvelopeBody::decode(body).map_err(|_| StoreAndForwardError::MalformedMessage) + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::{ + envelope::DhtMessageFlags, + store_forward::message::datetime_to_timestamp, + test_utils::{ + create_dht_actor_mock, + make_dht_inbound_message, + make_node_identity, + make_peer_manager, + service_spy, + DhtMockState, + }, + PipelineError, + }; + use chrono::Utc; + use futures::channel::mpsc; + use prost::Message; + use std::time::Duration; + use tari_comms::{message::MessageExt, wrap_in_envelope_body}; + use tokio::runtime::Handle; + + // TODO: unit tests for static functions (check_signature, etc) + + #[tokio_macros::test_basic] + async fn request_stored_messages() { + let rt_handle = Handle::current(); + let spy = service_spy(); + let storage = Arc::new(SafStorage::new(10)); + + let peer_manager = make_peer_manager(); + let (oms_tx, mut oms_rx) = mpsc::channel(1); + + let node_identity = make_node_identity(); + + // Recent message + let inbound_msg = make_dht_inbound_message(&node_identity, vec![], DhtMessageFlags::empty()); + storage.insert( + vec![0], + StoredMessage::new(0, inbound_msg.dht_header, b"A".to_vec()), + Duration::from_secs(60), + ); + + // Expired message + let inbound_msg = make_dht_inbound_message(&node_identity, vec![], DhtMessageFlags::empty()); + storage.insert( + vec![1], + StoredMessage::new(0, inbound_msg.dht_header, vec![]), + Duration::from_secs(0), + ); + + // Out of time range + let inbound_msg = make_dht_inbound_message(&node_identity, vec![], DhtMessageFlags::empty()); + let mut msg = StoredMessage::new(0, inbound_msg.dht_header, vec![]); + msg.stored_at = Some(datetime_to_timestamp( + Utc::now().checked_sub_signed(chrono::Duration::days(1)).unwrap(), + )); + + let mut message = DecryptedDhtMessage::succeeded( + wrap_in_envelope_body!(StoredMessagesRequest::since( + Utc::now().checked_sub_signed(chrono::Duration::seconds(60)).unwrap() + )) + .unwrap(), + make_dht_inbound_message(&node_identity, vec![], DhtMessageFlags::ENCRYPTED), + ); + message.dht_header.message_type = DhtMessageType::SafRequestMessages; + + let (tx, _) = mpsc::channel(1); + let dht_requester = DhtRequester::new(tx); + + let task = MessageHandlerTask::new( + Default::default(), + spy.to_service::(), + storage, + dht_requester, + peer_manager, + OutboundMessageRequester::new(oms_tx), + node_identity, + message, + ); + + rt_handle.spawn(task.run()); + + let (_, body) = unwrap_oms_send_msg!(oms_rx.next().await.unwrap()); + let body = EnvelopeBody::decode(body.as_slice()).unwrap(); + let msg = body.decode_part::(0).unwrap().unwrap(); + assert_eq!(msg.messages().len(), 1); + assert_eq!(msg.messages()[0].encrypted_body, b"A"); + assert!(!spy.is_called()); + } + + #[tokio_macros::test_basic] + async fn receive_stored_messages() { + let rt_handle = Handle::current(); + let spy = service_spy(); + let storage = Arc::new(SafStorage::new(10)); + + let peer_manager = make_peer_manager(); + let (oms_tx, _) = mpsc::channel(1); + + let node_identity = make_node_identity(); + + let shared_key = crypt::generate_ecdh_secret(node_identity.secret_key(), node_identity.public_key()); + let msg_a = crypt::encrypt( + &shared_key, + &wrap_in_envelope_body!(&b"A".to_vec()) + .unwrap() + .to_encoded_bytes() + .unwrap(), + ) + .unwrap(); + + let inbound_msg_a = make_dht_inbound_message(&node_identity, msg_a.clone(), DhtMessageFlags::ENCRYPTED); + // Need to know the peer to process a stored message + peer_manager + .add_peer(Clone::clone(&*inbound_msg_a.source_peer)) + .unwrap(); + let msg_b = crypt::encrypt( + &shared_key, + &wrap_in_envelope_body!(b"B".to_vec()) + .unwrap() + .to_encoded_bytes() + .unwrap(), + ) + .unwrap(); + + let inbound_msg_b = make_dht_inbound_message(&node_identity, msg_b.clone(), DhtMessageFlags::ENCRYPTED); + // Need to know the peer to process a stored message + peer_manager + .add_peer(Clone::clone(&*inbound_msg_b.source_peer)) + .unwrap(); + + let msg1 = StoredMessage::new(0, inbound_msg_a.dht_header.clone(), msg_a); + let msg2 = StoredMessage::new(0, inbound_msg_b.dht_header, msg_b); + // Cleartext message + let clear_msg = wrap_in_envelope_body!(b"Clear".to_vec()) + .unwrap() + .to_encoded_bytes() + .unwrap(); + let clear_header = + make_dht_inbound_message(&node_identity, clear_msg.clone(), DhtMessageFlags::empty()).dht_header; + let msg_clear = StoredMessage::new(0, clear_header, clear_msg); + let mut message = DecryptedDhtMessage::succeeded( + wrap_in_envelope_body!(StoredMessagesResponse { + messages: vec![msg1.clone(), msg2, msg_clear], + }) + .unwrap(), + make_dht_inbound_message(&node_identity, vec![], DhtMessageFlags::ENCRYPTED), + ); + message.dht_header.message_type = DhtMessageType::SafStoredMessages; + + let (dht_requester, mut mock) = create_dht_actor_mock(1); + let mock_state = DhtMockState::new(); + mock.set_shared_state(mock_state.clone()); + rt_handle.spawn(mock.run()); + + let task = MessageHandlerTask::new( + Default::default(), + spy.to_service::(), + storage, + dht_requester, + peer_manager, + OutboundMessageRequester::new(oms_tx), + node_identity, + message, + ); + + task.run().await.unwrap(); + assert_eq!(spy.call_count(), 3); + let requests = spy.take_requests(); + assert_eq!(requests.len(), 3); + // Deserialize each request into the message (a vec of a single byte in this case) + let msgs = requests + .into_iter() + .map(|req| req.success().unwrap().decode_part::>(0).unwrap().unwrap()) + .collect::>>(); + assert!(msgs.contains(&b"A".to_vec())); + assert!(msgs.contains(&b"B".to_vec())); + assert!(msgs.contains(&b"Clear".to_vec())); + assert_eq!(mock_state.call_count(), msgs.len()); + } +} diff --git a/comms/dht/src/store_forward/state.rs b/comms/dht/src/store_forward/state.rs new file mode 100644 index 0000000000..40a1bf513b --- /dev/null +++ b/comms/dht/src/store_forward/state.rs @@ -0,0 +1,56 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::proto::store_forward::StoredMessage; +use std::{ + sync::{RwLock, RwLockWriteGuard}, + time::Duration, +}; +use ttl_cache::TtlCache; + +pub type SignatureBytes = Vec; + +pub struct SafStorage { + message_cache: RwLock>, +} + +impl SafStorage { + pub fn new(cache_capacity: usize) -> Self { + Self { + message_cache: RwLock::new(TtlCache::new(cache_capacity)), + } + } + + pub fn insert(&self, key: SignatureBytes, message: StoredMessage, ttl: Duration) -> Option { + acquire_write_lock!(self.message_cache).insert(key, message, ttl) + } + + pub fn with_lock(&self, f: F) -> T + where F: FnOnce(RwLockWriteGuard>) -> T { + f(acquire_write_lock!(self.message_cache)) + } + + #[cfg(test)] + pub fn remove(&self, key: &SignatureBytes) -> Option { + acquire_write_lock!(self.message_cache).remove(key) + } +} diff --git a/comms/dht/src/store_forward/store.rs b/comms/dht/src/store_forward/store.rs new file mode 100644 index 0000000000..392f230873 --- /dev/null +++ b/comms/dht/src/store_forward/store.rs @@ -0,0 +1,364 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + envelope::{DhtMessageFlags, NodeDestination}, + inbound::DecryptedDhtMessage, + proto::store_forward::StoredMessage, + store_forward::{error::StoreAndForwardError, state::SafStorage}, + DhtConfig, + PipelineError, +}; +use futures::{task::Context, Future}; +use log::*; +use std::{sync::Arc, task::Poll}; +use tari_comms::{ + message::MessageExt, + peer_manager::{NodeIdentity, PeerManager}, +}; +use tower::{layer::Layer, Service, ServiceExt}; + +const LOG_TARGET: &str = "comms::middleware::forward"; + +/// This layer is responsible for storing messages which have failed to decrypt +pub struct StoreLayer { + peer_manager: Arc, + config: DhtConfig, + node_identity: Arc, + storage: Arc, +} + +impl StoreLayer { + pub fn new( + config: DhtConfig, + peer_manager: Arc, + node_identity: Arc, + storage: Arc, + ) -> Self + { + Self { + peer_manager, + config, + node_identity, + storage, + } + } +} + +impl Layer for StoreLayer { + type Service = StoreMiddleware; + + fn layer(&self, service: S) -> Self::Service { + StoreMiddleware::new( + service, + self.config.clone(), + Arc::clone(&self.peer_manager), + Arc::clone(&self.node_identity), + Arc::clone(&self.storage), + ) + } +} + +#[derive(Clone)] +pub struct StoreMiddleware { + next_service: S, + config: DhtConfig, + peer_manager: Arc, + node_identity: Arc, + + storage: Arc, +} + +impl StoreMiddleware { + pub fn new( + next_service: S, + config: DhtConfig, + peer_manager: Arc, + node_identity: Arc, + storage: Arc, + ) -> Self + { + Self { + next_service, + config, + peer_manager, + node_identity, + storage, + } + } +} + +impl Service for StoreMiddleware +where + S: Service + Clone + 'static, + S::Error: Into, +{ + type Error = PipelineError; + type Response = (); + + type Future = impl Future>; + + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, msg: DecryptedDhtMessage) -> Self::Future { + StoreTask::new( + self.next_service.clone(), + self.config.clone(), + Arc::clone(&self.peer_manager), + Arc::clone(&self.node_identity), + Arc::clone(&self.storage), + ) + .handle(msg) + } +} + +/// Responsible for processing a single DecryptedDhtMessage, storing if necessary or passing the message +/// to the next service. +struct StoreTask { + next_service: S, + storage: Option, +} + +impl StoreTask { + pub fn new( + next_service: S, + config: DhtConfig, + peer_manager: Arc, + node_identity: Arc, + storage: Arc, + ) -> Self + { + Self { + storage: Some(InnerStorage { + config, + peer_manager, + node_identity, + storage, + }), + next_service, + } + } +} + +impl StoreTask +where + S: Service, + S::Error: Into, +{ + async fn handle(mut self, message: DecryptedDhtMessage) -> Result<(), PipelineError> { + match message.success() { + Some(_) => { + // If message was not originally encrypted and has an origin we want to store a copy for others + if message.dht_header.origin.is_some() && !message.dht_header.flags.contains(DhtMessageFlags::ENCRYPTED) + { + debug!( + target: LOG_TARGET, + "Cleartext message sent from origin {}. Adding to SAF storage.", + message.origin_public_key() + ); + let mut storage = self.storage.take().expect("StoreTask intialized without storage"); + let msg_clone = message.clone(); + tokio::task::spawn_blocking(move || storage.store(msg_clone)).await??; + } + + trace!(target: LOG_TARGET, "Passing message to next service"); + self.next_service.oneshot(message).await.map_err(Into::into)?; + }, + None => { + if message.dht_header.origin.is_none() { + // TODO: #banheuristic + warn!( + target: LOG_TARGET, + "Store task received an encrypted message with no source. This message is invalid and should \ + not be stored or propagated. Dropping message. Sent by node '{}'", + message.source_peer.node_id.short_str() + ); + return Ok(()); + } + debug!( + target: LOG_TARGET, + "Decryption failed for message. Adding to SAF storage." + ); + let mut storage = self.storage.take().expect("StoreTask intialized without storage"); + tokio::task::spawn_blocking(move || storage.store(message)).await??; + }, + } + + Ok(()) + } +} + +struct InnerStorage { + peer_manager: Arc, + config: DhtConfig, + node_identity: Arc, + storage: Arc, +} + +impl InnerStorage { + fn store(&mut self, message: DecryptedDhtMessage) -> Result<(), StoreAndForwardError> { + let DecryptedDhtMessage { + version, + decryption_result, + dht_header, + .. + } = message; + + let origin = dht_header.origin.as_ref().expect("already checked"); + + let body = match decryption_result { + Ok(body) => body.to_encoded_bytes()?, + Err(encrypted_body) => encrypted_body, + }; + + let peer_manager = &self.peer_manager; + let node_identity = &self.node_identity; + + match &dht_header.destination { + NodeDestination::Unknown => { + self.storage.insert( + origin.signature.clone(), + StoredMessage::new(version, dht_header, body), + self.config.saf_low_priority_msg_storage_ttl, + ); + }, + NodeDestination::PublicKey(dest_public_key) => { + if peer_manager.exists(&dest_public_key) { + self.storage.insert( + origin.signature.clone(), + StoredMessage::new(version, dht_header, body), + self.config.saf_high_priority_msg_storage_ttl, + ); + } + }, + NodeDestination::NodeId(dest_node_id) => { + if peer_manager.exists_node_id(&dest_node_id) || + peer_manager.in_network_region( + &dest_node_id, + node_identity.node_id(), + self.config.num_neighbouring_nodes, + )? + { + self.storage.insert( + origin.signature.clone(), + StoredMessage::new(version, dht_header, body), + self.config.saf_high_priority_msg_storage_ttl, + ); + } + }, + }; + + Ok(()) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::{ + envelope::DhtMessageFlags, + test_utils::{make_dht_inbound_message, make_node_identity, make_peer_manager, service_spy}, + }; + use chrono::{DateTime, Utc}; + use std::time::{Duration, UNIX_EPOCH}; + use tari_comms::wrap_in_envelope_body; + + #[tokio_macros::test_basic] + async fn cleartext_message_no_origin() { + let storage = Arc::new(SafStorage::new(1)); + + let spy = service_spy(); + let peer_manager = make_peer_manager(); + let node_identity = make_node_identity(); + let mut service = StoreLayer::new(Default::default(), peer_manager, node_identity, storage.clone()) + .layer(spy.to_service::()); + + let mut inbound_msg = make_dht_inbound_message(&make_node_identity(), b"".to_vec(), DhtMessageFlags::empty()); + inbound_msg.dht_header.origin = None; + let msg = DecryptedDhtMessage::succeeded(wrap_in_envelope_body!(Vec::new()).unwrap(), inbound_msg); + service.call(msg).await.unwrap(); + assert!(spy.is_called()); + storage.with_lock(|mut lock| { + assert_eq!(lock.iter().count(), 0); + }); + } + + #[tokio_macros::test_basic] + async fn cleartext_message_with_origin() { + let storage = Arc::new(SafStorage::new(1)); + + let spy = service_spy(); + let peer_manager = make_peer_manager(); + let node_identity = make_node_identity(); + let mut service = StoreLayer::new(Default::default(), peer_manager, node_identity, storage.clone()) + .layer(spy.to_service::()); + + let inbound_msg = make_dht_inbound_message(&make_node_identity(), b"".to_vec(), DhtMessageFlags::empty()); + let msg = DecryptedDhtMessage::succeeded(wrap_in_envelope_body!(Vec::new()).unwrap(), inbound_msg); + service.call(msg).await.unwrap(); + assert!(spy.is_called()); + storage.with_lock(|mut lock| { + assert_eq!(lock.iter().count(), 1); + }); + } + + #[tokio_macros::test_basic] + async fn decryption_succeeded_no_store() { + let storage = Arc::new(SafStorage::new(1)); + + let spy = service_spy(); + let peer_manager = make_peer_manager(); + let node_identity = make_node_identity(); + let mut service = StoreLayer::new(Default::default(), peer_manager, node_identity, storage.clone()) + .layer(spy.to_service::()); + + let inbound_msg = make_dht_inbound_message(&make_node_identity(), b"".to_vec(), DhtMessageFlags::ENCRYPTED); + let msg = DecryptedDhtMessage::succeeded(wrap_in_envelope_body!(b"secret".to_vec()).unwrap(), inbound_msg); + service.call(msg).await.unwrap(); + assert!(spy.is_called()); + storage.with_lock(|mut lock| { + assert_eq!(lock.iter().count(), 0); + }); + } + + #[tokio_macros::test_basic] + async fn decryption_failed_should_store() { + let storage = Arc::new(SafStorage::new(1)); + let spy = service_spy(); + let peer_manager = make_peer_manager(); + let node_identity = make_node_identity(); + let mut service = StoreLayer::new(Default::default(), peer_manager, node_identity, Arc::clone(&storage)) + .layer(spy.to_service::()); + + let inbound_msg = make_dht_inbound_message(&make_node_identity(), b"".to_vec(), DhtMessageFlags::empty()); + let msg = DecryptedDhtMessage::failed(inbound_msg.clone()); + service.call(msg).await.unwrap(); + assert_eq!(spy.is_called(), false); + let msg = storage + .remove(&inbound_msg.dht_header.origin.unwrap().signature) + .unwrap(); + let timestamp: DateTime = (UNIX_EPOCH + Duration::from_secs(msg.stored_at.unwrap().seconds as u64)).into(); + assert!((Utc::now() - timestamp).num_seconds() <= 5); + } +} diff --git a/comms/dht/src/test_utils/dht_actor_mock.rs b/comms/dht/src/test_utils/dht_actor_mock.rs new file mode 100644 index 0000000000..a30445ce5b --- /dev/null +++ b/comms/dht/src/test_utils/dht_actor_mock.rs @@ -0,0 +1,111 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::actor::{DhtRequest, DhtRequester}; +use futures::{channel::mpsc, stream::Fuse, StreamExt}; +use std::sync::{ + atomic::{AtomicBool, AtomicUsize, Ordering}, + Arc, + RwLock, +}; +use tari_comms::peer_manager::Peer; + +pub fn create_dht_actor_mock(buf_size: usize) -> (DhtRequester, DhtActorMock) { + let (tx, rx) = mpsc::channel(buf_size); + (DhtRequester::new(tx), DhtActorMock::new(rx.fuse())) +} + +#[derive(Default, Debug, Clone)] +pub struct DhtMockState { + signature_cache_insert: Arc, + call_count: Arc, + select_peers: Arc>>, +} + +impl DhtMockState { + pub fn new() -> Self { + Self { + signature_cache_insert: Arc::new(AtomicBool::new(false)), + call_count: Arc::new(AtomicUsize::new(0)), + select_peers: Arc::new(RwLock::new(Vec::new())), + } + } + + pub fn set_signature_cache_insert(&self, v: bool) -> &Self { + self.signature_cache_insert.store(v, Ordering::SeqCst); + self + } + + pub fn set_select_peers_response(&self, peers: Vec) -> &Self { + *acquire_write_lock!(self.select_peers) = peers; + self + } + + pub fn inc_call_count(&self) { + self.call_count.fetch_add(1, Ordering::SeqCst); + } + + pub fn call_count(&self) -> usize { + self.call_count.load(Ordering::SeqCst) + } +} + +pub struct DhtActorMock { + receiver: Fuse>, + state: DhtMockState, +} + +impl DhtActorMock { + pub fn new(receiver: Fuse>) -> Self { + Self { + receiver, + state: DhtMockState::default(), + } + } + + pub fn set_shared_state(&mut self, state: DhtMockState) { + self.state = state; + } + + pub async fn run(mut self) { + while let Some(req) = self.receiver.next().await { + self.handle_request(req).await; + } + } + + async fn handle_request(&self, req: DhtRequest) { + use DhtRequest::*; + self.state.inc_call_count(); + match req { + SendJoin => {}, + MsgHashCacheInsert(_, reply_tx) => { + let v = self.state.signature_cache_insert.load(Ordering::SeqCst); + reply_tx.send(v).unwrap(); + }, + SelectPeers(_, reply_tx) => { + let lock = self.state.select_peers.read().unwrap(); + reply_tx.send(lock.clone()).unwrap(); + }, + SendRequestStoredMessages(_) => {}, + } + } +} diff --git a/comms/dht/src/test_utils/dht_discovery_mock.rs b/comms/dht/src/test_utils/dht_discovery_mock.rs new file mode 100644 index 0000000000..ae07a83cd0 --- /dev/null +++ b/comms/dht/src/test_utils/dht_discovery_mock.rs @@ -0,0 +1,113 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + discovery::{DhtDiscoveryRequest, DhtDiscoveryRequester}, + test_utils::make_peer, +}; +use futures::{channel::mpsc, stream::Fuse, StreamExt}; +use log::*; +use std::{ + sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, + RwLock, + }, + time::Duration, +}; +use tari_comms::peer_manager::Peer; + +const LOG_TARGET: &str = "comms::dht::discovery_mock"; + +pub fn create_dht_discovery_mock(buf_size: usize, timeout: Duration) -> (DhtDiscoveryRequester, DhtDiscoveryMock) { + let (tx, rx) = mpsc::channel(buf_size); + ( + DhtDiscoveryRequester::new(tx, timeout), + DhtDiscoveryMock::new(rx.fuse()), + ) +} + +#[derive(Debug, Clone)] +pub struct DhtDiscoveryMockState { + call_count: Arc, + discover_peer: Arc>, +} + +impl DhtDiscoveryMockState { + pub fn new() -> Self { + Self { + call_count: Arc::new(AtomicUsize::new(0)), + discover_peer: Arc::new(RwLock::new(make_peer())), + } + } + + pub fn set_discover_peer_response(&self, peer: Peer) -> &Self { + *acquire_write_lock!(self.discover_peer) = peer; + self + } + + pub fn inc_call_count(&self) { + self.call_count.fetch_add(1, Ordering::SeqCst); + } + + pub fn call_count(&self) -> usize { + self.call_count.load(Ordering::SeqCst) + } +} + +pub struct DhtDiscoveryMock { + receiver: Fuse>, + state: DhtDiscoveryMockState, +} + +impl DhtDiscoveryMock { + pub fn new(receiver: Fuse>) -> Self { + Self { + receiver, + state: DhtDiscoveryMockState::new(), + } + } + + pub fn set_shared_state(&mut self, state: DhtDiscoveryMockState) { + self.state = state; + } + + pub async fn run(mut self) { + while let Some(req) = self.receiver.next().await { + self.handle_request(req).await; + } + } + + async fn handle_request(&self, req: DhtDiscoveryRequest) { + use DhtDiscoveryRequest::*; + trace!(target: LOG_TARGET, "DhtDiscoveryMock received request {:?}", req); + self.state.inc_call_count(); + match req { + DiscoverPeer(boxed) => { + let (_, reply_tx) = *boxed; + let lock = self.state.discover_peer.read().unwrap(); + reply_tx.send(Ok(lock.clone())).unwrap(); + }, + NotifyDiscoveryResponseReceived(_) => {}, + } + } +} diff --git a/comms/dht/src/test_utils/makers.rs b/comms/dht/src/test_utils/makers.rs new file mode 100644 index 0000000000..c9e4064a20 --- /dev/null +++ b/comms/dht/src/test_utils/makers.rs @@ -0,0 +1,148 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::{ + envelope::{DhtMessageFlags, DhtMessageHeader, DhtMessageOrigin, NodeDestination}, + inbound::DhtInboundMessage, + proto::envelope::{DhtEnvelope, DhtMessageType, Network}, +}; +use rand::rngs::OsRng; +use std::sync::Arc; +use tari_comms::{ + message::{InboundMessage, MessageEnvelopeHeader, MessageFlags}, + multiaddr::Multiaddr, + peer_manager::{NodeIdentity, Peer, PeerFeatures, PeerFlags, PeerManager}, + types::CommsDatabase, + utils::signature, + Bytes, +}; +use tari_crypto::tari_utilities::message_format::MessageFormat; +use tari_storage::lmdb_store::LMDBBuilder; +use tari_test_utils::{paths::create_temporary_data_path, random}; + +pub fn make_node_identity() -> Arc { + Arc::new( + NodeIdentity::random( + &mut OsRng, + "/ip4/127.0.0.1/tcp/9000".parse().unwrap(), + PeerFeatures::COMMUNICATION_NODE, + ) + .unwrap(), + ) +} + +pub fn make_peer() -> Peer { + let node_identity = make_node_identity(); + Peer::new( + node_identity.public_key().clone(), + node_identity.node_id().clone(), + vec![node_identity.public_address()].into(), + PeerFlags::empty(), + PeerFeatures::COMMUNICATION_NODE, + ) +} + +pub fn make_client_identity() -> Arc { + Arc::new( + NodeIdentity::random( + &mut OsRng, + "/ip4/127.0.0.1/tcp/9000".parse().unwrap(), + PeerFeatures::COMMUNICATION_CLIENT, + ) + .unwrap(), + ) +} + +pub fn make_comms_inbound_message(node_identity: &NodeIdentity, message: Bytes, flags: MessageFlags) -> InboundMessage { + InboundMessage::new( + Arc::new(Peer::new( + node_identity.public_key().clone(), + node_identity.node_id().clone(), + Vec::::new().into(), + PeerFlags::empty(), + PeerFeatures::COMMUNICATION_NODE, + )), + MessageEnvelopeHeader { + public_key: node_identity.public_key().clone(), + signature: Bytes::new(), + flags, + }, + message, + ) +} + +pub fn make_dht_header(node_identity: &NodeIdentity, message: &Vec, flags: DhtMessageFlags) -> DhtMessageHeader { + DhtMessageHeader { + version: 0, + destination: NodeDestination::Unknown, + origin: Some(DhtMessageOrigin { + public_key: node_identity.public_key().clone(), + signature: signature::sign(&mut OsRng, node_identity.secret_key().clone(), message) + .unwrap() + .to_binary() + .unwrap(), + }), + message_type: DhtMessageType::None, + network: Network::LocalTest, + flags, + } +} + +pub fn make_dht_inbound_message( + node_identity: &NodeIdentity, + body: Vec, + flags: DhtMessageFlags, +) -> DhtInboundMessage +{ + DhtInboundMessage::new( + make_dht_header(node_identity, &body, flags), + Arc::new(Peer::new( + node_identity.public_key().clone(), + node_identity.node_id().clone(), + Vec::::new().into(), + PeerFlags::empty(), + PeerFeatures::COMMUNICATION_NODE, + )), + body, + ) +} + +pub fn make_dht_envelope(node_identity: &NodeIdentity, message: Vec, flags: DhtMessageFlags) -> DhtEnvelope { + DhtEnvelope::new(make_dht_header(node_identity, &message, flags).into(), message) +} + +pub fn make_peer_manager() -> Arc { + let database_name = random::string(8); + let path = create_temporary_data_path(); + let datastore = LMDBBuilder::new() + .set_path(path.to_str().unwrap()) + .set_environment_size(10) + .set_max_number_of_databases(1) + .add_database(&database_name, lmdb_zero::db::CREATE) + .build() + .unwrap(); + + let peer_database = datastore.get_handle(&database_name).unwrap(); + + PeerManager::new(CommsDatabase::new(Arc::new(peer_database))) + .map(Arc::new) + .unwrap() +} diff --git a/comms/dht/src/test_utils/mod.rs b/comms/dht/src/test_utils/mod.rs new file mode 100644 index 0000000000..90f9358d43 --- /dev/null +++ b/comms/dht/src/test_utils/mod.rs @@ -0,0 +1,48 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +macro_rules! unwrap_oms_send_msg { + ($var:expr, reply_value=$reply_value:expr) => { + match $var { + crate::outbound::DhtOutboundRequest::SendMessage(boxed, body, reply_tx) => { + let _ = reply_tx.send($reply_value); + (*boxed, body) + }, + } + }; + ($var:expr) => { + unwrap_oms_send_msg!( + $var, + reply_value = $crate::outbound::SendMessageResponse::Queued(vec![]) + ); + }; +} + +mod dht_actor_mock; +mod dht_discovery_mock; +mod makers; +mod service; + +pub use dht_actor_mock::{create_dht_actor_mock, DhtMockState}; +pub use dht_discovery_mock::{create_dht_discovery_mock, DhtDiscoveryMockState}; +pub use makers::*; +pub use service::{service_fn, service_spy}; diff --git a/comms/dht/src/test_utils/service.rs b/comms/dht/src/test_utils/service.rs new file mode 100644 index 0000000000..5f34ff9c01 --- /dev/null +++ b/comms/dht/src/test_utils/service.rs @@ -0,0 +1,125 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use futures::future; +use std::{ + future::Future, + sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, + Mutex, + }, + task::{Context, Poll}, +}; +use tower::Service; + +pub fn service_spy() -> ServiceSpy +where TReq: 'static { + ServiceSpy::new() +} + +#[derive(Clone)] +pub struct ServiceSpy { + requests: Arc>>, + call_count: Arc, +} + +impl ServiceSpy +where TReq: 'static +{ + pub fn new() -> Self { + let requests = Arc::new(Mutex::new(Vec::new())); + Self { + requests, + call_count: Arc::new(AtomicUsize::new(0)), + } + } + + #[allow(dead_code)] + pub fn reset(&self) { + self.call_count.store(0, Ordering::SeqCst); + self.requests.lock().unwrap().clear(); + } + + pub fn to_service( + &self, + ) -> impl Service>> + Clone { + let req_inner = Arc::clone(&self.requests); + let call_count = Arc::clone(&self.call_count); + service_fn(move |req: TReq| { + req_inner.lock().unwrap().push(req); + call_count.fetch_add(1, Ordering::SeqCst); + future::ready(Result::<_, TErr>::Ok(())) + }) + } + + #[allow(dead_code)] + pub fn take_requests(&self) -> Vec { + self.requests.lock().unwrap().drain(..).collect() + } + + pub fn pop_request(&self) -> Option { + self.requests.lock().unwrap().pop() + } + + pub fn is_called(&self) -> bool { + self.call_count() > 0 + } + + #[allow(dead_code)] + pub fn call_count(&self) -> usize { + self.call_count.load(Ordering::SeqCst) + } +} + +//---------------------------------- ServiceFn --------------------------------------------// + +// TODO: Remove this when https://github.com/tower-rs/tower/pull/318 is published + +/// Returns a new `ServiceFn` with the given closure. +pub fn service_fn(f: T) -> ServiceFn { + ServiceFn { f } +} + +/// A `Service` implemented by a closure. +#[derive(Copy, Clone, Debug)] +pub struct ServiceFn { + f: T, +} + +impl Service for ServiceFn +where + T: FnMut(Request) -> F, + F: Future>, +{ + type Error = E; + type Future = F; + type Response = R; + + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { + Ok(()).into() + } + + fn call(&mut self, req: Request) -> Self::Future { + (self.f)(req) + } +} diff --git a/comms/dht/src/tower_filter/error.rs b/comms/dht/src/tower_filter/error.rs new file mode 100644 index 0000000000..b2643f77f4 --- /dev/null +++ b/comms/dht/src/tower_filter/error.rs @@ -0,0 +1,46 @@ +//! Error types + +use std::{error, fmt}; + +/// Error produced by `Filter` +#[derive(Debug)] +pub struct Error { + source: Option, +} + +pub(crate) type Source = Box; + +impl Error { + /// Create a new `Error` representing a rejected request. + pub fn rejected() -> Error { + Error { source: None } + } + + /// Create a new `Error` representing an inner service error. + pub fn inner(source: E) -> Error + where E: Into { + Error { + source: Some(source.into()), + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + if self.source.is_some() { + write!(fmt, "inner service errored") + } else { + write!(fmt, "rejected") + } + } +} + +impl error::Error for Error { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + if let Some(ref err) = self.source { + Some(&**err) + } else { + None + } + } +} diff --git a/comms/dht/src/tower_filter/future.rs b/comms/dht/src/tower_filter/future.rs new file mode 100644 index 0000000000..0faa301245 --- /dev/null +++ b/comms/dht/src/tower_filter/future.rs @@ -0,0 +1,91 @@ +//! Future types + +use super::error::{self, Error}; +use futures::ready; +use pin_project::{pin_project, project}; +use std::{ + future::Future, + pin::Pin, + task::{Context, Poll}, +}; +use tower::Service; + +/// Filtered response future +#[pin_project] +#[derive(Debug)] +pub struct ResponseFuture +where S: Service +{ + #[pin] + /// Response future state + state: State, + + #[pin] + /// Predicate future + check: T, + + /// Inner service + service: S, +} + +#[pin_project] +#[derive(Debug)] +enum State { + Check(Option), + WaitResponse(#[pin] U), +} + +impl ResponseFuture +where + F: Future>, + S: Service, + S::Error: Into, +{ + pub(crate) fn new(request: Request, check: F, service: S) -> Self { + ResponseFuture { + state: State::Check(Some(request)), + check, + service, + } + } +} + +impl Future for ResponseFuture +where + F: Future>, + S: Service, + S::Error: Into, +{ + type Output = Result; + + #[project] + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + + loop { + #[project] + match this.state.as_mut().project() { + State::Check(request) => { + let request = request + .take() + .expect("we either give it back or leave State::Check once we take"); + + // Poll predicate + match this.check.as_mut().poll(cx)? { + Poll::Ready(_) => { + let response = this.service.call(request); + this.state.set(State::WaitResponse(response)); + }, + Poll::Pending => { + this.state.set(State::Check(Some(request))); + return Poll::Pending; + }, + } + }, + State::WaitResponse(response) => { + return Poll::Ready(ready!(response.poll(cx)).map_err(Error::inner)); + }, + } + } + } +} diff --git a/comms/dht/src/tower_filter/layer.rs b/comms/dht/src/tower_filter/layer.rs new file mode 100644 index 0000000000..ccbc9a63ef --- /dev/null +++ b/comms/dht/src/tower_filter/layer.rs @@ -0,0 +1,24 @@ +use super::Filter; +use tower::layer::Layer; + +/// Conditionally dispatch requests to the inner service based on a predicate. +#[derive(Debug)] +pub struct FilterLayer { + predicate: U, +} + +impl FilterLayer { + #[allow(missing_docs)] + pub fn new(predicate: U) -> Self { + FilterLayer { predicate } + } +} + +impl Layer for FilterLayer { + type Service = Filter; + + fn layer(&self, service: S) -> Self::Service { + let predicate = self.predicate.clone(); + Filter::new(service, predicate) + } +} diff --git a/comms/dht/src/tower_filter/mod.rs b/comms/dht/src/tower_filter/mod.rs new file mode 100644 index 0000000000..673a855393 --- /dev/null +++ b/comms/dht/src/tower_filter/mod.rs @@ -0,0 +1,63 @@ +#![doc(html_root_url = "https://docs.rs/tower-filter/0.3.0-alpha.2")] +// #![allow(elided_lifetimes_in_paths)] + +//! Conditionally dispatch requests to the inner service based on the result of +//! a predicate. + +pub mod error; +pub mod future; +mod layer; +mod predicate; + +pub use layer::FilterLayer; +pub use predicate::Predicate; + +use error::Error; +use future::ResponseFuture; +use futures::ready; +use std::task::{Context, Poll}; +use tower::Service; + +#[cfg(test)] +mod test; + +/// Conditionally dispatch requests to the inner service based on a predicate. +#[derive(Clone, Debug)] +pub struct Filter { + inner: T, + predicate: U, +} + +impl Filter { + #[allow(missing_docs)] + pub fn new(inner: T, predicate: U) -> Self { + Filter { inner, predicate } + } +} + +impl Service for Filter +where + T: Service + Clone, + T::Error: Into, + U: Predicate, +{ + type Error = Error; + type Future = ResponseFuture; + type Response = T::Response; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + Poll::Ready(ready!(self.inner.poll_ready(cx)).map_err(error::Error::inner)) + } + + fn call(&mut self, request: Request) -> Self::Future { + use std::mem; + + let inner = self.inner.clone(); + let inner = mem::replace(&mut self.inner, inner); + + // Check the request + let check = self.predicate.check(&request); + + ResponseFuture::new(request, check, inner) + } +} diff --git a/comms/dht/src/tower_filter/predicate.rs b/comms/dht/src/tower_filter/predicate.rs new file mode 100644 index 0000000000..52b3936aa6 --- /dev/null +++ b/comms/dht/src/tower_filter/predicate.rs @@ -0,0 +1,25 @@ +use super::error::Error; +use std::future::Future; + +/// Checks a request +pub trait Predicate { + /// The future returned by `check`. + type Future: Future>; + + /// Check whether the given request should be forwarded. + /// + /// If the future resolves with `Ok`, the request is forwarded to the inner service. + fn check(&mut self, request: &Request) -> Self::Future; +} + +impl Predicate for F +where + F: Fn(&T) -> U, + U: Future>, +{ + type Future = U; + + fn check(&mut self, request: &T) -> Self::Future { + self(request) + } +} diff --git a/comms/dht/src/tower_filter/test.rs b/comms/dht/src/tower_filter/test.rs new file mode 100644 index 0000000000..0990ecc11d --- /dev/null +++ b/comms/dht/src/tower_filter/test.rs @@ -0,0 +1,81 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::{error::Error, Filter}; +use futures_util::{future::poll_fn, pin_mut}; +use std::future::Future; +use tokio::runtime::Handle; +use tokio_test::task; +use tower::Service; +use tower_test::{assert_request_eq, mock}; + +#[tokio_macros::test] +async fn passthrough_sync() { + let (mut service, handle) = new_service(|_| async { Ok(()) }); + + let handle = Handle::current().spawn(async move { + // Receive the requests and respond + pin_mut!(handle); + for i in 0..10 { + assert_request_eq!(handle, format!("ping-{}", i)).send_response(format!("pong-{}", i)); + } + }); + + let mut responses = vec![]; + + for i in 0usize..10 { + let request = format!("ping-{}", i); + poll_fn(|cx| service.poll_ready(cx)).await.unwrap(); + let exchange = service.call(request); + let exchange = async move { + let response = exchange.await.unwrap(); + let expect = format!("pong-{}", i); + assert_eq!(response.as_str(), expect.as_str()); + }; + + responses.push(exchange); + } + + futures_util::future::join_all(responses).await; + handle.await.unwrap(); +} + +#[test] +fn rejected_sync() { + task::spawn(async { + let (mut service, _handle) = new_service(|_| async { Err(Error::rejected()) }); + service.call("hello".into()).await.unwrap_err(); + }); +} + +type Mock = mock::Mock; +type MockHandle = mock::Handle; + +fn new_service(f: F) -> (Filter, MockHandle) +where + F: Fn(&String) -> U, + U: Future>, +{ + let (service, handle) = mock::pair(); + let service = Filter::new(service, f); + (service, handle) +} diff --git a/comms/dht/src/utils.rs b/comms/dht/src/utils.rs new file mode 100644 index 0000000000..7a15329eaf --- /dev/null +++ b/comms/dht/src/utils.rs @@ -0,0 +1,30 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/// Returns a "denested" result. The outer Result is converted into the inner Result +pub fn hoist_nested_result(result: Result, E1>) -> Result +where E1: Into { + match result { + Ok(v) => v, + Err(err) => Err(err.into()), + } +} diff --git a/comms/dht/tests/dht.rs b/comms/dht/tests/dht.rs new file mode 100644 index 0000000000..b2742030db --- /dev/null +++ b/comms/dht/tests/dht.rs @@ -0,0 +1,300 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use futures::channel::mpsc; +use rand::rngs::OsRng; +use std::{sync::Arc, time::Duration}; +use tari_comms::{ + backoff::ConstantBackoff, + peer_manager::{NodeIdentity, Peer, PeerFeatures, PeerStorage}, + pipeline, + pipeline::SinkService, + transports::MemoryTransport, + types::CommsDatabase, + CommsBuilder, + CommsNode, +}; +use tari_comms_dht::{envelope::NodeDestination, inbound::DecryptedDhtMessage, Dht, DhtBuilder}; +use tari_storage::{lmdb_store::LMDBBuilder, LMDBWrapper}; +use tari_test_utils::{async_assert_eventually, paths::create_temporary_data_path, random}; +use tower::ServiceBuilder; + +fn new_node_identity() -> NodeIdentity { + let port = MemoryTransport::acquire_next_memsocket_port(); + NodeIdentity::random( + &mut OsRng, + format!("/memory/{}", port).parse().unwrap(), + PeerFeatures::COMMUNICATION_NODE, + ) + .unwrap() +} + +fn create_peer_storage(peers: Vec) -> CommsDatabase { + let database_name = random::string(8); + let datastore = LMDBBuilder::new() + .set_path(create_temporary_data_path().to_str().unwrap()) + .set_environment_size(10) + .set_max_number_of_databases(1) + .add_database(&database_name, lmdb_zero::db::CREATE) + .build() + .unwrap(); + + let peer_database = datastore.get_handle(&database_name).unwrap(); + let peer_database = LMDBWrapper::new(Arc::new(peer_database)); + let mut storage = PeerStorage::new_indexed(peer_database).unwrap(); + for peer in peers { + storage.add_peer(peer).unwrap(); + } + + storage.into() +} + +async fn setup_comms_dht( + node_identity: NodeIdentity, + storage: CommsDatabase, + inbound_tx: mpsc::Sender, +) -> (CommsNode, Dht) +{ + // Create inbound and outbound channels + let (outbound_tx, outbound_rx) = mpsc::channel(10); + + let comms = CommsBuilder::new() + // In this case the listener address and the public address are the same (/memory/...) + .with_listener_address(node_identity.public_address()) + .with_transport(MemoryTransport) + .with_node_identity(Arc::new(node_identity)) + .with_peer_storage(storage) + .with_dial_backoff(ConstantBackoff::new(Duration::from_millis(100))) + .build() + .unwrap(); + + let dht = DhtBuilder::new( + comms.node_identity(), + comms.peer_manager(), + outbound_tx, + comms.shutdown_signal(), + ) + .local_test() + .with_executor(comms.executor().clone()) + .with_discovery_timeout(Duration::from_secs(60)) + .finish(); + + let dht_outbound_layer = dht.outbound_middleware_layer(); + + let comms = comms + .with_messaging_pipeline( + pipeline::Builder::new() + .outbound_buffer_size(10) + .with_outbound_pipeline(outbound_rx, |sink| { + ServiceBuilder::new().layer(dht_outbound_layer).service(sink) + }) + .max_concurrent_inbound_tasks(10) + .with_inbound_pipeline( + ServiceBuilder::new() + .layer(dht.inbound_middleware_layer()) + .service(SinkService::new(inbound_tx)), + ) + .finish(), + ) + .spawn() + .await + .unwrap(); + + (comms, dht) +} + +#[tokio_macros::test] +#[allow(non_snake_case)] +async fn dht_join_propagation() { + // Create 3 nodes where only Node B knows A and C, but A and C want to talk to each other + let node_A_identity = new_node_identity(); + let node_B_identity = new_node_identity(); + let node_C_identity = new_node_identity(); + + // Node A knows about Node B + let (tx, ims_rx_A) = mpsc::channel(1); + let (node_A_comms, node_A_dht) = setup_comms_dht( + node_A_identity.clone(), + create_peer_storage(vec![node_B_identity.clone().into()]), + tx, + ) + .await; + // Node B knows about Node A and C + let (tx, ims_rx_B) = mpsc::channel(1); + let (node_B_comms, node_B_dht) = setup_comms_dht( + node_B_identity.clone(), + create_peer_storage(vec![node_A_identity.clone().into(), node_C_identity.clone().into()]), + tx, + ) + .await; + // Node C knows about Node B + let (tx, ims_rx_C) = mpsc::channel(1); + let (node_C_comms, node_C_dht) = setup_comms_dht( + node_C_identity.clone(), + create_peer_storage(vec![node_B_identity.clone().into()]), + tx, + ) + .await; + + // Send a join request from Node A, through B to C. As all Nodes are in the same network region, once + // Node C receives the join request from Node A, it will send a direct join request back + // to A. + node_A_dht.dht_requester().send_join().await.unwrap(); + + let node_A_peer_manager = node_A_comms.async_peer_manager(); + let node_A_node_identity = node_A_comms.node_identity(); + let node_C_peer_manager = node_C_comms.async_peer_manager(); + let node_C_node_identity = node_C_comms.node_identity(); + + // Check that Node A knows about Node C and vice versa + async_assert_eventually!( + node_A_peer_manager + .exists(node_C_node_identity.public_key()) + .await + .unwrap(), + expect = true, + max_attempts = 10, + interval = Duration::from_millis(1000) + ); + async_assert_eventually!( + node_C_peer_manager + .exists(node_A_node_identity.public_key()) + .await + .unwrap(), + expect = true, + max_attempts = 10, + interval = Duration::from_millis(500) + ); + + let node_C_peer = node_A_peer_manager + .find_by_public_key(node_C_node_identity.public_key()) + .await + .unwrap(); + assert_eq!(&node_C_peer.features, node_C_node_identity.features()); + + // Make sure these variables only drop after the test is done + drop(ims_rx_A); + drop(ims_rx_B); + drop(ims_rx_C); + + drop(node_A_dht); + drop(node_B_dht); + drop(node_C_dht); + + node_A_comms.shutdown().await; + node_B_comms.shutdown().await; + node_C_comms.shutdown().await; +} + +#[tokio_macros::test] +#[allow(non_snake_case)] +async fn dht_discover_propagation() { + // Create 4 nodes where A knows B, B knows A and C, C knows B and D, and D knows C + let node_A_identity = new_node_identity(); + let node_B_identity = new_node_identity(); + let node_C_identity = new_node_identity(); + let node_D_identity = new_node_identity(); + + // Node A knows about Node B + let (tx, ims_rx_A) = mpsc::channel(1); + let (node_A_comms, node_A_dht) = setup_comms_dht( + node_A_identity.clone(), + create_peer_storage(vec![node_B_identity.clone().into()]), + tx, + ) + .await; + // Node B knows about Node C + let (tx, ims_rx_B) = mpsc::channel(1); + let (node_B_comms, node_B_dht) = setup_comms_dht( + node_B_identity.clone(), + create_peer_storage(vec![node_C_identity.clone().into()]), + tx, + ) + .await; + // Node C knows about Node D + let (tx, ims_rx_C) = mpsc::channel(1); + let (node_C_comms, node_C_dht) = setup_comms_dht( + node_C_identity.clone(), + create_peer_storage(vec![node_D_identity.clone().into()]), + tx, + ) + .await; + // Node C knows no one + let (tx, ims_rx_D) = mpsc::channel(1); + let (node_D_comms, node_D_dht) = setup_comms_dht(node_D_identity.clone(), create_peer_storage(vec![]), tx).await; + + // Send a discover request from Node A, through B and C, to D. Once Node D + // receives the discover request from Node A, it should send a discovery response + // request back to A at which time this call will resolve (or timeout). + node_A_dht + .discovery_service_requester() + .discover_peer(node_D_identity.public_key().clone(), None, NodeDestination::Unknown) + .await + .unwrap(); + + let node_A_peer_manager = node_A_comms.async_peer_manager(); + let node_A_node_identity = node_A_comms.node_identity(); + let node_B_peer_manager = node_B_comms.async_peer_manager(); + let node_B_node_identity = node_B_comms.node_identity(); + let node_C_peer_manager = node_C_comms.async_peer_manager(); + let node_C_node_identity = node_C_comms.node_identity(); + let node_D_peer_manager = node_D_comms.async_peer_manager(); + let node_D_node_identity = node_D_comms.node_identity(); + + // Check that all the nodes know about each other in the chain and the discovery worked + assert!(node_A_peer_manager + .exists(node_D_node_identity.public_key()) + .await + .unwrap()); + assert!(node_B_peer_manager + .exists(node_A_node_identity.public_key()) + .await + .unwrap()); + assert!(node_C_peer_manager + .exists(node_B_node_identity.public_key()) + .await + .unwrap()); + assert!(node_D_peer_manager + .exists(node_C_node_identity.public_key()) + .await + .unwrap()); + assert!(node_D_peer_manager + .exists(node_A_node_identity.public_key()) + .await + .unwrap()); + + // Make sure these variables only drop after the test is done + drop(ims_rx_A); + drop(ims_rx_B); + drop(ims_rx_C); + drop(ims_rx_D); + + drop(node_A_dht); + drop(node_B_dht); + drop(node_C_dht); + drop(node_D_dht); + + node_A_comms.shutdown().await; + node_B_comms.shutdown().await; + node_C_comms.shutdown().await; + node_D_comms.shutdown().await; +} diff --git a/infrastructure/tari_util/tests/mod.rs b/comms/dht/tests/mod.rs similarity index 96% rename from infrastructure/tari_util/tests/mod.rs rename to comms/dht/tests/mod.rs index 321474ef11..d8f9b08c66 100644 --- a/infrastructure/tari_util/tests/mod.rs +++ b/comms/dht/tests/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019 The Tari Project +// Copyright 2019, The Tari Project // // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the // following conditions are met: @@ -19,3 +19,5 @@ // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod dht; diff --git a/comms/examples/tor.rs b/comms/examples/tor.rs new file mode 100644 index 0000000000..77c8768a80 --- /dev/null +++ b/comms/examples/tor.rs @@ -0,0 +1,275 @@ +use bytes::Bytes; +use chrono::Utc; +use derive_error::Error; +use futures::{ + channel::{mpsc, mpsc::SendError}, + SinkExt, + StreamExt, +}; +use rand::{rngs::OsRng, thread_rng, RngCore}; +use std::{collections::HashMap, convert::identity, env, path::Path, process, sync::Arc}; +use tari_comms::{ + message::{InboundMessage, OutboundMessage}, + multiaddr::Multiaddr, + peer_manager::{NodeId, NodeIdentity, NodeIdentityError, Peer, PeerFeatures, PeerManagerError}, + pipeline, + pipeline::SinkService, + tor, + CommsBuilder, + CommsBuilderError, + CommsNode, +}; +use tari_crypto::tari_utilities::message_format::MessageFormat; +use tari_storage::{lmdb_store::LMDBBuilder, LMDBWrapper}; +use tempdir::TempDir; +use tokio::{runtime, task}; + +// Tor example for tari_comms. +// +// _Note:_ A running tor proxy with `ControlPort` set is required for this example to work. + +#[derive(Debug, Error)] +enum Error { + /// Invalid control port address provided. USAGE: tor_example 'Tor_multiaddress'. + InvalidArgControlPortAddress, + NodeIdentityError(NodeIdentityError), + HiddenServiceBuilderError(tor::HiddenServiceBuilderError), + CommsBuilderError(CommsBuilderError), + PeerManagerError(PeerManagerError), + /// Failed to send message + SendError(SendError), + JoinError(task::JoinError), + #[error(msg_embedded, no_from, non_std)] + Custom(String), +} + +#[tokio_macros::main] +async fn main() { + env_logger::init(); + if let Err(err) = run().await { + eprintln!("{error:?}: {error}", error = err); + process::exit(1); + } +} + +fn load_tor_identity>(path: P) -> tor::TorIdentity { + let contents = std::fs::read_to_string(path).unwrap(); + tor::TorIdentity::from_json(&contents).unwrap() +} + +async fn run() -> Result<(), Error> { + let mut args_iter = env::args().skip(1); + let control_port_addr = args_iter + .next() + .unwrap_or("/ip4/127.0.0.1/tcp/9095".to_string()) + .parse::() + .map_err(|_| Error::InvalidArgControlPortAddress)?; + + let tor_identity1 = args_iter.next().map(load_tor_identity); + let tor_identity2 = args_iter.next().map(load_tor_identity); + + println!("Starting comms nodes...",); + + let temp_dir1 = TempDir::new("tor-example1").unwrap(); + let (comms_node1, inbound_rx1, mut outbound_tx1) = + setup_node_with_tor(control_port_addr.clone(), temp_dir1.as_ref(), 9098, tor_identity1).await?; + + let temp_dir2 = TempDir::new("tor-example2").unwrap(); + let (comms_node2, inbound_rx2, outbound_tx2) = + setup_node_with_tor(control_port_addr, temp_dir2.as_ref(), 9099, tor_identity2).await?; + + let node_identity1 = comms_node1.node_identity(); + let node_identity2 = comms_node2.node_identity(); + + println!("Comms nodes started!"); + println!( + "Node 1 is '{}' with address '{}' (local_listening_addr='{}')", + node_identity1.node_id().short_str(), + node_identity1.public_address(), + comms_node1.listening_address(), + ); + println!( + "Node 2 is '{}' with address '{}' (local_listening_addr='{}')", + node_identity2.node_id().short_str(), + node_identity2.public_address(), + comms_node2.listening_address(), + ); + + // Let's add node 2 as a peer to node 1 + comms_node1 + .async_peer_manager() + .add_peer(Peer::new( + node_identity2.public_key().clone(), + node_identity2.node_id().clone(), + vec![node_identity2.public_address()].into(), + Default::default(), + PeerFeatures::COMMUNICATION_CLIENT, + )) + .await?; + + // This kicks things off + outbound_tx1 + .send(OutboundMessage::new( + comms_node2.node_identity().node_id().clone(), + Default::default(), + Bytes::from_static(b"START"), + )) + .await?; + + let executor = runtime::Handle::current(); + + println!("Starting ping pong between nodes over tor. This may take a few moments to begin."); + let handle1 = executor.spawn(start_ping_ponger( + comms_node2.node_identity().node_id().clone(), + inbound_rx1, + outbound_tx1, + )); + let handle2 = executor.spawn(start_ping_ponger( + comms_node1.node_identity().node_id().clone(), + inbound_rx2, + outbound_tx2, + )); + + tokio::signal::ctrl_c().await.expect("ctrl-c failed"); + + println!("Tor example is shutting down..."); + comms_node1.shutdown().await; + comms_node2.shutdown().await; + + handle1.await??; + handle2.await??; + + Ok(()) +} + +async fn setup_node_with_tor>( + control_port_addr: Multiaddr, + database_path: &Path, + port_mapping: P, + tor_identity: Option, +) -> Result<(CommsNode, mpsc::Receiver, mpsc::Sender), Error> +{ + let datastore = LMDBBuilder::new() + .set_path(database_path.to_str().unwrap()) + .set_environment_size(10) + .set_max_number_of_databases(1) + .add_database("peerdb", lmdb_zero::db::CREATE) + .build() + .unwrap(); + let peer_database = datastore.get_handle(&"peerdb").unwrap(); + let peer_database = LMDBWrapper::new(Arc::new(peer_database)); + + let (inbound_tx, inbound_rx) = mpsc::channel(10); + let (outbound_tx, outbound_rx) = mpsc::channel(10); + + let mut hs_builder = tor::HiddenServiceBuilder::new() + .with_port_mapping(port_mapping) + .with_control_server_address(control_port_addr); + + if let Some(ident) = tor_identity { + hs_builder = hs_builder.with_tor_identity(ident); + } + + let tor_hidden_service = hs_builder.finish().await?; + + println!( + "Tor hidden service created with address '{}'", + tor_hidden_service.get_onion_address() + ); + + let node_identity = Arc::new(NodeIdentity::random( + &mut OsRng, + tor_hidden_service.get_onion_address().clone(), + PeerFeatures::COMMUNICATION_CLIENT, + )?); + + let comms_node = CommsBuilder::new() + .with_node_identity(node_identity) + .configure_from_hidden_service(tor_hidden_service) + .with_peer_storage(peer_database) + .build() + .unwrap(); + + let comms_node = comms_node + .with_messaging_pipeline( + pipeline::Builder::new() + // Outbound messages will be forwarded "as is" to outbound messaging + .with_outbound_pipeline(outbound_rx, identity) + .max_concurrent_inbound_tasks(1) + // Inbound messages will be forwarded "as is" to inbound_tx + .with_inbound_pipeline(SinkService::new(inbound_tx)) + .finish(), + ) + .spawn() + .await?; + + Ok((comms_node, inbound_rx, outbound_tx)) +} + +async fn start_ping_ponger( + dest_node_id: NodeId, + mut inbound_rx: mpsc::Receiver, + mut outbound_tx: mpsc::Sender, +) -> Result +{ + let mut inflight_pings = HashMap::new(); + let mut counter = 0; + while let Some(msg) = inbound_rx.next().await { + counter += 1; + + let msg_str = String::from_utf8_lossy(&msg.body); + println!("Received '{}' from '{}'", msg_str, msg.source_peer.node_id.short_str()); + + let mut msg_parts = msg_str.split(' '); + match msg_parts.next() { + Some("START") => { + println!("\n-----------------------------------"); + let id = thread_rng().next_u64(); + inflight_pings.insert(id, Utc::now().naive_utc()); + let msg = make_msg(&dest_node_id, format!("PING {}", id)); + outbound_tx.send(msg).await?; + }, + Some("PING") => { + let id = msg_parts + .next() + .ok_or(Error::Custom("Received PING without id".to_string()))?; + + let msg_str = format!("PONG {}", id); + let msg = make_msg(&dest_node_id, msg_str); + + outbound_tx.send(msg).await?; + }, + + Some("PONG") => { + let id = msg_parts + .next() + .ok_or(Error::Custom("Received PING without id".to_string()))?; + + id.parse::() + .ok() + .and_then(|id_num| inflight_pings.remove(&id_num)) + .and_then(|ts| Some((Utc::now().naive_utc().signed_duration_since(ts)).num_milliseconds())) + .and_then(|latency| { + println!("Latency: {}ms", latency); + Some(latency) + }); + + println!("-----------------------------------"); + let new_id = thread_rng().next_u64(); + inflight_pings.insert(new_id, Utc::now().naive_utc()); + let msg = make_msg(&dest_node_id, format!("PING {}", new_id)); + outbound_tx.send(msg).await?; + }, + msg => { + return Err(Error::Custom(format!("Received invalid message '{:?}'", msg))); + }, + } + } + + Ok(counter) +} + +fn make_msg(node_id: &NodeId, msg: String) -> OutboundMessage { + let msg = Bytes::copy_from_slice(msg.as_bytes()); + OutboundMessage::new(node_id.clone(), Default::default(), msg) +} diff --git a/comms/src/backoff.rs b/comms/src/backoff.rs new file mode 100644 index 0000000000..cb6303c045 --- /dev/null +++ b/comms/src/backoff.rs @@ -0,0 +1,109 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::time::Duration; + +pub type BoxedBackoff = Box; + +pub trait Backoff { + fn calculate_backoff(&self, attempts: usize) -> Duration; +} + +impl Backoff for BoxedBackoff { + fn calculate_backoff(&self, attempts: usize) -> Duration { + (**self).calculate_backoff(attempts) + } +} + +#[derive(Debug, Clone)] +pub struct ExponentialBackoff { + factor: f32, +} + +impl ExponentialBackoff { + pub fn new(factor: f32) -> Self { + Self { factor } + } +} + +impl Default for ExponentialBackoff { + fn default() -> Self { + Self::new(1.5) + } +} + +impl Backoff for ExponentialBackoff { + fn calculate_backoff(&self, attempts: usize) -> Duration { + if attempts <= 1 { + return Duration::from_secs(0); + } + let secs = (self.factor as f64) * (f64::powf(2.0, attempts as f64) - 1.0); + Duration::from_secs(secs.ceil() as u64) + } +} + +#[derive(Clone)] +pub struct ConstantBackoff(Duration); + +impl ConstantBackoff { + pub fn new(timeout: Duration) -> Self { + Self(timeout) + } +} + +impl Backoff for ConstantBackoff { + fn calculate_backoff(&self, attempts: usize) -> Duration { + if attempts <= 1 { + return Duration::from_secs(0); + } + self.0 + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn default_backoff() { + let backoff = ExponentialBackoff::default(); + assert_eq!(backoff.calculate_backoff(0).as_secs(), 0); + assert_eq!(backoff.calculate_backoff(1).as_secs(), 0); + assert_eq!(backoff.calculate_backoff(2).as_secs(), 5); + assert_eq!(backoff.calculate_backoff(3).as_secs(), 11); + assert_eq!(backoff.calculate_backoff(4).as_secs(), 23); + assert_eq!(backoff.calculate_backoff(5).as_secs(), 47); + assert_eq!(backoff.calculate_backoff(6).as_secs(), 95); + assert_eq!(backoff.calculate_backoff(7).as_secs(), 191); + assert_eq!(backoff.calculate_backoff(8).as_secs(), 383); + assert_eq!(backoff.calculate_backoff(9).as_secs(), 767); + assert_eq!(backoff.calculate_backoff(10).as_secs(), 1535); + } + + #[test] + fn zero_backoff() { + let backoff = ExponentialBackoff::new(0.0); + assert_eq!(backoff.calculate_backoff(0).as_secs(), 0); + assert_eq!(backoff.calculate_backoff(1).as_secs(), 0); + assert_eq!(backoff.calculate_backoff(200).as_secs(), 0); + } +} diff --git a/comms/src/bounded_executor.rs b/comms/src/bounded_executor.rs new file mode 100644 index 0000000000..b633d567c7 --- /dev/null +++ b/comms/src/bounded_executor.rs @@ -0,0 +1,144 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::{future::Future, sync::Arc}; +use tokio::{runtime, sync::Semaphore, task::JoinHandle}; + +/// A task executor bounded by a semaphore. +/// +/// Use the asynchronous spawn method to spawn a task. If a given number of tasks are already spawned and have not +/// completed, the spawn function will block (asynchronously) until a previously spawned task completes. +pub struct BoundedExecutor { + inner: runtime::Handle, + semaphore: Arc, +} + +impl BoundedExecutor { + pub fn new(executor: runtime::Handle, num_permits: usize) -> Self { + Self { + inner: executor, + semaphore: Arc::new(Semaphore::new(num_permits)), + } + } + + /// Spawn a future onto the Tokio runtime asynchronously blocking if there are too many + /// spawned tasks. + /// + /// This spawns the given future onto the runtime's executor, usually a + /// thread pool. The thread pool is then responsible for polling the future + /// until it completes. + /// + /// If the number of pending tasks exceeds the num_permits value given to `BoundedExecutor::new` + /// the future returned from spawn will block until a permit is released. + /// + /// See [module level][mod] documentation for more details. + /// + /// [mod]: index.html + /// + /// # Examples + /// + /// ``` + /// use tokio::runtime::Runtime; + /// use tari_comms::bounded_executor::BoundedExecutor; + /// + /// # fn dox() { + /// // Create the runtime + /// let mut rt = Runtime::new().unwrap(); + /// let executor = BoundedExecutor::new(rt.handle().clone(), 1); + /// + /// // Spawn a future onto the runtime + /// // NOTE: BoundedExecutor::spawn is an async function and therefore, must be polled/awaited for the task to be spawned + /// let task1 = executor.spawn(async { + /// println!("now running on a worker thread"); + /// }); + /// // This will spawn after task1 + /// let task2 = executor.spawn(async { + /// println!("will always run after the first task"); + /// }); + /// + /// rt.block_on(task1); + /// rt.block_on(task2); + /// # } + /// ``` + /// + /// # Panics + /// + /// This function panics if the spawn fails. Failure occurs if the executor + /// is currently at capacity and is unable to spawn a new future. + pub async fn spawn(&self, future: F) -> JoinHandle + where + F: Future + Send + 'static, + F::Output: Send + 'static, + { + let permit = self.semaphore.acquire().await; + // Forget the permit without releasing it on drop. This is to work around the lifetime of semaphore in the + // permit not being 'static + permit.forget(); + let cloned_semaphore = Arc::clone(&self.semaphore); + + self.inner.spawn(async move { + let ret = future.await; + // Task is finished, re-add the permit + cloned_semaphore.add_permits(1); + ret + }) + } +} + +#[cfg(test)] +mod test { + use super::*; + use std::{ + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + time::Duration, + }; + use tokio::{runtime::Handle, time::delay_for}; + + #[tokio_macros::test_basic] + async fn spawn() { + let flag = Arc::new(AtomicBool::new(false)); + let flag_cloned = flag.clone(); + let executor = BoundedExecutor::new(Handle::current(), 1); + + // Spawn 1 + let task1_fut = executor + .spawn(async move { + delay_for(Duration::from_millis(1)).await; + flag_cloned.store(true, Ordering::SeqCst); + }) + .await; + + // Spawn 2 + let task2_fut = executor + .spawn(async move { + // This will panic if this task is spawned before task1 completes (e.g if num_permitted > 1) + assert_eq!(flag.load(Ordering::SeqCst), true); + }) + .await; + + task2_fut.await.unwrap(); + task1_fut.await.unwrap(); + } +} diff --git a/comms/src/builder/builder.rs b/comms/src/builder/builder.rs index 0830e0410a..26669c80cd 100644 --- a/comms/src/builder/builder.rs +++ b/comms/src/builder/builder.rs @@ -20,489 +20,585 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use super::{consts, placeholder::PlaceholderService, CommsShutdown}; use crate::{ - connection::{ConnectionError, DealerProxyError, InprocAddress, ZmqContext}, - connection_manager::{ConnectionManager, PeerConnectionConfig}, - consts::COMMS_BUILDER_IMS_DEFAULT_PUB_SUB_BUFFER_LENGTH, - control_service::{ControlService, ControlServiceConfig, ControlServiceError, ControlServiceHandle}, - dispatcher::DispatchableKey, - inbound_message_service::{ - comms_msg_handlers::construct_comms_msg_dispatcher, - error::InboundError, - inbound_message_publisher::{InboundMessagePublisher, PublisherError}, - inbound_message_service::{InboundMessageService, InboundMessageServiceConfig}, - InboundTopicSubscriptionFactory, + backoff::{Backoff, BoxedBackoff, ExponentialBackoff}, + bounded_executor::BoundedExecutor, + connection_manager::{ + ConnectionManager, + ConnectionManagerConfig, + ConnectionManagerError, + ConnectionManagerEvent, + ConnectionManagerRequest, + ConnectionManagerRequester, }, message::InboundMessage, - outbound_message_service::{ - outbound_message_pool::{OutboundMessagePoolConfig, OutboundMessagePoolError}, - outbound_message_service::OutboundMessageService, - OutboundError, - OutboundMessage, - OutboundMessagePool, - }, - peer_manager::{NodeIdentity, PeerManager, PeerManagerError}, - pub_sub_channel::{pubsub_channel, TopicPublisher}, - types::CommsDatabase, + multiaddr::Multiaddr, + noise::NoiseConfig, + peer_manager::{AsyncPeerManager, NodeIdentity, PeerManager, PeerManagerError}, + pipeline, + protocol::{messaging, messaging::MessagingProtocol, ProtocolNotification, Protocols}, + tor, + transports::{SocksTransport, TcpTransport, Transport}, + types::{CommsDatabase, CommsSubstream}, }; -use bitflags::_core::marker::PhantomData; -use crossbeam_channel::Sender; use derive_error::Error; +use futures::{channel::mpsc, AsyncRead, AsyncWrite, StreamExt}; use log::*; -use serde::{de::DeserializeOwned, Serialize}; -use std::{ - fmt::Debug, - sync::{Arc, RwLock}, -}; +use std::{fmt, fmt::Debug, sync::Arc, time::Duration}; +use tari_shutdown::{Shutdown, ShutdownSignal}; +use tokio::{runtime, sync::broadcast, time}; +use tower::Service; const LOG_TARGET: &str = "comms::builder"; #[derive(Debug, Error)] pub enum CommsBuilderError { PeerManagerError(PeerManagerError), - InboundMessageServiceError(ConnectionError), - #[error(no_from)] - OutboundMessageServiceError(OutboundError), - #[error(no_from)] - OutboundMessagePoolError(OutboundError), + ConnectionManagerError(ConnectionManagerError), /// Node identity not set. Call `with_node_identity(node_identity)` on [CommsBuilder] NodeIdentityNotSet, - #[error(no_from)] - DealerProxyError(DealerProxyError), - DatastoreUndefined, + /// The PeerStorage was not provided to the CommsBuilder. Use `with_peer_storage` to set it. + PeerStorageNotProvided, + /// The messaging pipeline was not provided to the CommsBuilder. Use `with_messaging_pipeline` to set it. + /// pipeline. + MessagingPiplineNotProvided, + /// Unable to receive a ConnectionManagerEvent within timeout + ConnectionManagerEventStreamTimeout, + /// ConnectionManagerEvent stream unexpectedly closed + ConnectionManagerEventStreamClosed, + /// Receiving on ConnectionManagerEvent stream lagged unexpectedly + ConnectionManagerEventStreamLagged, } /// The `CommsBuilder` provides a simple builder API for getting Tari comms p2p messaging up and running. -/// -/// The [build] method will return an error if any required builder methods are not called. These -/// are detailed further down on the method docs. -#[derive(Default)] -pub struct CommsBuilder { - zmq_context: ZmqContext, +pub struct CommsBuilder { peer_storage: Option, - control_service_config: Option, - omp_config: Option, - ims_config: Option, - node_identity: Option, - peer_conn_config: Option, - inbound_message_buffer_size: Option, - _m: PhantomData, + node_identity: Option>, + transport: Option, + executor: Option, + protocols: Option>, + dial_backoff: Option, + hidden_service: Option, + connection_manager_config: ConnectionManagerConfig, + shutdown: Shutdown, } -impl CommsBuilder -where - MType: DispatchableKey, - MType: Serialize + DeserializeOwned, - MType: Clone + Debug, -{ +impl CommsBuilder { /// Create a new CommsBuilder pub fn new() -> Self { - let zmq_context = ZmqContext::new(); - Self { - zmq_context, - control_service_config: None, - peer_conn_config: None, - omp_config: None, - ims_config: None, peer_storage: None, node_identity: None, - inbound_message_buffer_size: None, - _m: PhantomData, + transport: Some(Self::default_tcp_transport()), + dial_backoff: Some(Box::new(ExponentialBackoff::default())), + executor: None, + protocols: None, + hidden_service: None, + connection_manager_config: ConnectionManagerConfig::default(), + shutdown: Shutdown::new(), } } + fn default_tcp_transport() -> TcpTransport { + let mut tcp = TcpTransport::new(); + tcp.set_nodelay(true); + tcp + } +} + +impl CommsBuilder +where + TTransport: Transport + Unpin + Send + Sync + Clone + 'static, + TTransport::Output: AsyncRead + AsyncWrite + Send + Sync + Unpin + 'static, +{ + /// Set the runtime handle to use for spawning tasks. If this is not set the handle that is executing + /// `CommsBuilder::spawn` will be used, so this will rarely need to be explicitly set. + pub fn with_executor(mut self, handle: runtime::Handle) -> Self { + self.executor = Some(handle); + self + } + /// Set the [NodeIdentity] for this comms instance. This is required. /// /// [OutboundMessagePool]: ../../outbound_message_service/index.html#outbound-message-pool - pub fn with_node_identity(mut self, node_identity: NodeIdentity) -> Self { + pub fn with_node_identity(mut self, node_identity: Arc) -> Self { self.node_identity = Some(node_identity); self } - /// Set the peer storage database to use. This is optional. - pub fn with_peer_storage(mut self, peer_storage: CommsDatabase) -> Self { - self.peer_storage = Some(peer_storage); + pub fn with_listener_address(mut self, listener_address: Multiaddr) -> Self { + self.connection_manager_config.listener_address = listener_address; self } - /// Configure inbound message publisher/subscriber buffer size. This is optional - pub fn configure_inbound_message_publisher_buffer_size(mut self, size: usize) -> Self { - self.inbound_message_buffer_size = Some(size); + /// The maximum number of connection tasks that will be spawned at the same time. Once this limit is reached, peers + /// attempting to connect will have to wait for another connection attempt to complete. + pub fn with_max_simultaneous_inbound_connects(mut self, max_simultaneous_inbound_connects: usize) -> Self { + self.connection_manager_config.max_simultaneous_inbound_connects = max_simultaneous_inbound_connects; self } - /// Configure the [ControlService]. This is optional. - /// - /// [ControlService]: ../../control_service/index.html - pub fn configure_control_service(mut self, config: ControlServiceConfig) -> Self { - self.control_service_config = Some(config); + /// The number of dial attempts to make before giving up. + pub fn with_max_dial_attempts(mut self, max_dial_attempts: usize) -> Self { + self.connection_manager_config.max_dial_attempts = max_dial_attempts; self } - /// Configure the [OutboundMessagePool]. This is optional. If omitted the default configuration is used. - /// - /// [OutboundMessagePool]: ../../outbound_message_service/index.html#outbound-message-pool - pub fn configure_outbound_message_pool(mut self, config: OutboundMessagePoolConfig) -> Self { - self.omp_config = Some(config); - self - } - - /// Common configuration for all [PeerConnection]s. This is optional. - /// If omitted the default configuration is used. - /// - /// [PeerConnection]: ../../connection/peer_connection/index.html - pub fn configure_peer_connections(mut self, config: PeerConnectionConfig) -> Self { - self.peer_conn_config = Some(config); + /// Set the peer storage database to use. + pub fn with_peer_storage(mut self, peer_storage: CommsDatabase) -> Self { + self.peer_storage = Some(peer_storage); self } - fn make_peer_manager(&mut self) -> Result, CommsBuilderError> { - match self.peer_storage.take() { - Some(storage) => { - let peer_manager = PeerManager::new(storage).map_err(CommsBuilderError::PeerManagerError)?; - Ok(Arc::new(peer_manager)) - }, - None => Err(CommsBuilderError::DatastoreUndefined), + /// Configure the `CommsBuilder` to build a node which communicates using the given `tor::HiddenService`. + pub fn configure_from_hidden_service(mut self, hidden_service: tor::HiddenService) -> CommsBuilder { + // Set the listener address to be the address (usually local) to which tor will forward all traffic + self.connection_manager_config.listener_address = hidden_service.proxied_address().clone(); + + CommsBuilder { + // Set the socks transport configured for this hidden service + transport: Some(hidden_service.get_transport()), + // Set the hidden service. + hidden_service: Some(hidden_service), + peer_storage: self.peer_storage, + node_identity: self.node_identity, + executor: self.executor, + protocols: self.protocols, + dial_backoff: self.dial_backoff, + connection_manager_config: self.connection_manager_config, + shutdown: self.shutdown, } } - fn make_control_service(&mut self, node_identity: Arc) -> Option { - self.control_service_config - .take() - .map(|config| ControlService::new(self.zmq_context.clone(), node_identity, config)) + /// Set the backoff that [ConnectionManager] uses when dialing peers. This is optional. If omitted the default + /// ExponentialBackoff is used. [ConnectionManager]: crate::connection_manager::next::ConnectionManager + pub fn with_dial_backoff(mut self, backoff: T) -> Self + where T: Backoff + Send + Sync + 'static { + self.dial_backoff = Some(Box::new(backoff)); + self } - fn make_connection_manager( - &mut self, - node_identity: Arc, - peer_manager: Arc, - config: PeerConnectionConfig, - ) -> Arc + pub fn with_transport(self, transport: T) -> CommsBuilder + where + T: Transport + Unpin + Send + Sync + Clone + 'static, + T::Output: AsyncRead + AsyncWrite + Send + Sync + Unpin + 'static, { - Arc::new(ConnectionManager::new( - self.zmq_context.clone(), - node_identity, - peer_manager, - config, - )) - } - - fn make_peer_connection_config(&mut self) -> PeerConnectionConfig { - let mut config = self.peer_conn_config.take().unwrap_or_default(); - // If the message_sink_address is not set (is default) set it to a random inproc address - if config.message_sink_address.is_default() { - config.message_sink_address = InprocAddress::random(); + CommsBuilder { + transport: Some(transport), + peer_storage: self.peer_storage, + node_identity: self.node_identity, + hidden_service: self.hidden_service, + executor: self.executor, + protocols: self.protocols, + dial_backoff: self.dial_backoff, + connection_manager_config: self.connection_manager_config, + shutdown: self.shutdown, } - config } - fn make_node_identity(&mut self) -> Result, CommsBuilderError> { - self.node_identity - .take() - .map(Arc::new) - .ok_or(CommsBuilderError::NodeIdentityNotSet) + pub fn with_protocols(mut self, protocols: Protocols) -> Self { + self.protocols = Some(protocols); + self } - fn make_outbound_message_service( - &self, - node_identity: Arc, - message_sink: Sender, - peer_manager: Arc, - ) -> Result, CommsBuilderError> - { - OutboundMessageService::new(node_identity, message_sink, peer_manager) - .map(Arc::new) - .map_err(CommsBuilderError::OutboundMessageServiceError) + pub fn on_shutdown(mut self, on_shutdown: F) -> Self + where F: FnOnce() + Send + Sync + 'static { + self.shutdown.on_triggered(on_shutdown); + self } - fn make_outbound_message_pool( - &mut self, + fn make_messaging( + &self, + executor: runtime::Handle, + conn_man_requester: ConnectionManagerRequester, peer_manager: Arc, - connection_manager: Arc, - ) -> OutboundMessagePool + node_identity: Arc, + ) -> ( + messaging::MessagingProtocol, + mpsc::Sender>, + mpsc::Sender, + mpsc::Receiver, + messaging::MessagingEventSender, + ) { - let config = self.omp_config.take().unwrap_or_default(); + let (proto_tx, proto_rx) = mpsc::channel(consts::MESSAGING_PROTOCOL_EVENTS_BUFFER_SIZE); + let (messaging_request_tx, messaging_request_rx) = mpsc::channel(consts::MESSAGING_REQUEST_BUFFER_SIZE); + let (inbound_message_tx, inbound_message_rx) = mpsc::channel(consts::INBOUND_MESSAGE_BUFFER_SIZE); + let (event_tx, _) = broadcast::channel(consts::MESSAGING_EVENTS_BUFFER_SIZE); + let messaging = MessagingProtocol::new( + executor, + conn_man_requester, + peer_manager.into(), + node_identity, + proto_rx, + messaging_request_rx, + event_tx.clone(), + inbound_message_tx, + consts::MESSAGING_MAX_SEND_RETRIES, + self.shutdown.to_signal(), + ); - OutboundMessagePool::new(config, peer_manager, connection_manager) + (messaging, proto_tx, messaging_request_tx, inbound_message_rx, event_tx) } - // TODO Remove this Arc + RwLock when the IMS worker is refactored to be future based. - fn make_inbound_message_publisher( - &mut self, - publisher: TopicPublisher, - ) -> Arc>> - { - Arc::new(RwLock::new(InboundMessagePublisher::new(publisher))) + fn make_peer_manager(&mut self) -> Result, CommsBuilderError> { + match self.peer_storage.take() { + Some(storage) => { + let peer_manager = PeerManager::new(storage).map_err(CommsBuilderError::PeerManagerError)?; + Ok(Arc::new(peer_manager)) + }, + None => Err(CommsBuilderError::PeerStorageNotProvided), + } } - fn make_inbound_message_service( + fn make_connection_manager( &mut self, + executor: runtime::Handle, node_identity: Arc, - message_sink_address: InprocAddress, - inbound_message_publisher: Arc>>, - oms: Arc, peer_manager: Arc, - ) -> InboundMessageService + protocols: Protocols, + request_rx: mpsc::Receiver, + connection_manager_events_tx: broadcast::Sender>, + ) -> ConnectionManager { - let config = self.ims_config.take().unwrap_or_default(); + let backoff = self.dial_backoff.take().expect("always set"); + let noise_config = NoiseConfig::new(Arc::clone(&node_identity)); + let config = self.connection_manager_config.clone(); - InboundMessageService::new( + ConnectionManager::new( config, - self.zmq_context.clone(), + executor, + self.transport.take().expect("transport has already been taken"), + noise_config, + backoff, + request_rx, node_identity, - message_sink_address, - Arc::new(construct_comms_msg_dispatcher()), - inbound_message_publisher, - oms, - peer_manager, + peer_manager.into(), + protocols, + connection_manager_events_tx, + self.shutdown.to_signal(), ) } /// Build the required comms services. Services will not be started. - pub fn build(mut self) -> Result, CommsBuilderError> { - let node_identity = self.make_node_identity()?; + pub fn build(mut self) -> Result, CommsBuilderError> { + let node_identity = self.node_identity.take().ok_or(CommsBuilderError::NodeIdentityNotSet)?; + let executor = self + .executor + .take() + .or_else(|| Some(runtime::Handle::current())) + .unwrap(); let peer_manager = self.make_peer_manager()?; - let peer_conn_config = self.make_peer_connection_config(); - - let control_service = self.make_control_service(node_identity.clone()); - - let connection_manager = - self.make_connection_manager(node_identity.clone(), peer_manager.clone(), peer_conn_config.clone()); + //---------------------------------- Messaging --------------------------------------------// - let outbound_message_pool = self.make_outbound_message_pool(peer_manager.clone(), connection_manager.clone()); + let (conn_man_tx, conn_man_rx) = mpsc::channel(consts::CONNECTION_MANAGER_REQUEST_BUFFER_SIZE); + let (connection_manager_event_tx, _) = broadcast::channel(consts::CONNECTION_MANAGER_EVENTS_BUFFER_SIZE); + let connection_manager_requester = + ConnectionManagerRequester::new(conn_man_tx, connection_manager_event_tx.clone()); - let outbound_message_service = self.make_outbound_message_service( - node_identity.clone(), - outbound_message_pool.sender(), - peer_manager.clone(), - )?; + let (messaging, messaging_proto_tx, messaging_request_tx, inbound_message_rx, messaging_event_tx) = self + .make_messaging( + executor.clone(), + connection_manager_requester.clone(), + peer_manager.clone(), + node_identity.clone(), + ); - // Create pub/sub channel for IMS - let (publisher, inbound_message_subscription_factory) = pubsub_channel( - self.inbound_message_buffer_size - .or(Some(COMMS_BUILDER_IMS_DEFAULT_PUB_SUB_BUFFER_LENGTH)) - .unwrap(), - ); - let inbound_message_publisher = self.make_inbound_message_publisher(publisher); + //---------------------------------- Protocols --------------------------------------------// + let protocols = self + .protocols + .take() + .or_else(|| Some(Protocols::new())) + .map(move |protocols| protocols.add([messaging::MESSAGING_PROTOCOL], messaging_proto_tx)) + .expect("cannot fail"); - let inbound_message_service = self.make_inbound_message_service( + //---------------------------------- ConnectionManager --------------------------------------------// + let connection_manager = self.make_connection_manager( + executor.clone(), node_identity.clone(), - peer_conn_config.message_sink_address, - inbound_message_publisher, - outbound_message_service.clone(), peer_manager.clone(), + protocols, + conn_man_rx, + connection_manager_event_tx.clone(), ); - Ok(CommsServiceContainer { - zmq_context: self.zmq_context, - control_service, - inbound_message_service, + Ok(BuiltCommsNode { + executor, connection_manager, - outbound_message_pool, - outbound_message_service, - peer_manager, + connection_manager_requester, + connection_manager_event_tx, + messaging_request_tx, + messaging_pipeline: None, + messaging, + messaging_event_tx, + inbound_message_rx, node_identity, - inbound_message_subscription_factory: Arc::new(inbound_message_subscription_factory), + peer_manager, + hidden_service: self.hidden_service, + shutdown: self.shutdown, }) } } -#[derive(Debug, Error)] -pub enum CommsServicesError { - ControlServiceError(ControlServiceError), - ConnectionManagerError(ConnectionError), - /// Comms services shut down uncleanly - UncleanShutdown, - /// The message type was not registered - MessageTypeNotRegistered, - OutboundMessagePoolError(OutboundMessagePoolError), - OutboundError(OutboundError), - InboundMessageServiceError(InboundError), - PublisherError(PublisherError), -} - /// Contains the built comms services -pub struct CommsServiceContainer -where - MType: Serialize + DeserializeOwned, - MType: DispatchableKey, - MType: Clone + Debug, -{ - zmq_context: ZmqContext, - connection_manager: Arc, - control_service: Option, - inbound_message_service: InboundMessageService, - outbound_message_pool: OutboundMessagePool, - outbound_message_service: Arc, - peer_manager: Arc, +pub struct BuiltCommsNode< + TTransport, + TInPipe = PlaceholderService, + TOutPipe = PlaceholderService<(), (), ()>, + TOutReq = (), +> { + connection_manager: ConnectionManager, + connection_manager_requester: ConnectionManagerRequester, + connection_manager_event_tx: broadcast::Sender>, + messaging_pipeline: Option>, + executor: runtime::Handle, node_identity: Arc, - inbound_message_subscription_factory: Arc>, + messaging: MessagingProtocol, + messaging_event_tx: messaging::MessagingEventSender, + inbound_message_rx: mpsc::Receiver, + + hidden_service: Option, + messaging_request_tx: mpsc::Sender, + shutdown: Shutdown, + peer_manager: Arc, } -impl CommsServiceContainer +impl BuiltCommsNode where - MType: Serialize + DeserializeOwned, - MType: DispatchableKey, - MType: Clone + Send + Debug, + TTransport: Transport + Unpin + Send + Sync + Clone + 'static, + TTransport::Output: AsyncRead + AsyncWrite + Send + Sync + Unpin + 'static, + TOutPipe: Service + Clone + Send + 'static, + TOutPipe::Error: fmt::Debug + Send, + TOutPipe::Future: Send + 'static, + TInPipe: Service + Clone + Send + 'static, + TInPipe::Error: fmt::Debug + Send, + TInPipe::Future: Send + 'static, + TOutReq: Send + 'static, { - /// Start all the comms services and return a [CommsServices] object - /// - /// [CommsServices]: ./struct.CommsServices.html - pub fn start(mut self) -> Result, CommsServicesError> { - let mut control_service_handle = None; - if let Some(control_service) = self.control_service { - control_service_handle = Some( - control_service - .serve(self.connection_manager.clone()) - .map_err(CommsServicesError::ControlServiceError)?, - ); - } + pub fn with_messaging_pipeline( + self, + messaging_pipeline: pipeline::Config, + ) -> BuiltCommsNode + where + O: Service + Clone + Send + 'static, + O::Error: fmt::Debug + Send, + O::Future: Send + 'static, + I: Service + Clone + Send + 'static, + I::Error: fmt::Debug + Send, + I::Future: Send + 'static, + { + BuiltCommsNode { + messaging_pipeline: Some(messaging_pipeline), - self.inbound_message_service - .start() - .map_err(CommsServicesError::InboundMessageServiceError)?; - self.outbound_message_pool - .start() - .map_err(CommsServicesError::OutboundMessagePoolError)?; - - Ok(CommsServices { - // Transfer ownership to CommsServices - zmq_context: self.zmq_context, - outbound_message_service: self.outbound_message_service, connection_manager: self.connection_manager, - peer_manager: self.peer_manager, - inbound_message_subscription_factory: self.inbound_message_subscription_factory, - outbound_message_pool: self.outbound_message_pool, + connection_manager_requester: self.connection_manager_requester, + connection_manager_event_tx: self.connection_manager_event_tx, node_identity: self.node_identity, - // Add handles for started services - control_service_handle, - }) + messaging: self.messaging, + messaging_event_tx: self.messaging_event_tx, + inbound_message_rx: self.inbound_message_rx, + executor: self.executor, + shutdown: self.shutdown, + messaging_request_tx: self.messaging_request_tx, + hidden_service: self.hidden_service, + peer_manager: self.peer_manager, + } } -} -/// # CommsServices -/// -/// This struct provides a handle to and control over all the running comms services. -/// You can get a [DomainConnector] from which to receive messages by using the `create_connector` -/// method. Use the `shutdown` method to attempt to cleanly shut all comms services down. -pub struct CommsServices -where MType: Send + Sync + Debug -{ - zmq_context: ZmqContext, - outbound_message_service: Arc, - control_service_handle: Option, - outbound_message_pool: OutboundMessagePool, - node_identity: Arc, - connection_manager: Arc, - peer_manager: Arc, - inbound_message_subscription_factory: Arc>, -} + /// Wait until the ConnectionManager emits a Listening event. This is the signal that comms is ready. + async fn wait_listening( + mut events: broadcast::Receiver>, + ) -> Result { + loop { + let event = time::timeout(Duration::from_secs(10), events.next()) + .await + .map_err(|_| CommsBuilderError::ConnectionManagerEventStreamTimeout)? + .ok_or(CommsBuilderError::ConnectionManagerEventStreamClosed)? + .map_err(|_| CommsBuilderError::ConnectionManagerEventStreamLagged)?; + + match &*event { + ConnectionManagerEvent::Listening(addr) => return Ok(addr.clone()), + ConnectionManagerEvent::ListenFailed(err) => return Err(err.clone().into()), + _ => {}, + } + } + } -impl CommsServices -where - MType: DispatchableKey, - MType: Clone + Send + Debug, -{ - pub fn zmq_context(&self) -> &ZmqContext { - &self.zmq_context + pub async fn spawn(self) -> Result { + let BuiltCommsNode { + connection_manager, + connection_manager_requester, + connection_manager_event_tx, + messaging_pipeline, + messaging_request_tx, + inbound_message_rx, + executor, + node_identity, + shutdown, + peer_manager, + messaging, + messaging_event_tx, + hidden_service, + } = self; + + let messaging_pipeline = messaging_pipeline.ok_or(CommsBuilderError::MessagingPiplineNotProvided)?; + + let events_stream = connection_manager_event_tx.subscribe(); + let conn_man_shutdown_signal = connection_manager.complete_signal(); + + executor.spawn(connection_manager.run()); + + // Spawn messaging protocol + let messaging_signal = messaging.complete_signal(); + executor.spawn(messaging.run()); + + // Spawn inbound pipeline + let bounded_executor = BoundedExecutor::new(executor.clone(), messaging_pipeline.max_concurrent_inbound_tasks); + let inbound = pipeline::Inbound::new(bounded_executor, inbound_message_rx, messaging_pipeline.inbound); + executor.spawn(inbound.run()); + + // Spawn outbound pipeline + let outbound = pipeline::Outbound::new(executor.clone(), messaging_pipeline.outbound, messaging_request_tx); + executor.spawn(outbound.run()); + + let listening_addr = Self::wait_listening(events_stream).await?; + + Ok(CommsNode { + shutdown, + connection_manager_event_tx, + connection_manager_requester, + listening_addr, + node_identity, + peer_manager, + messaging_event_tx, + hidden_service, + executor, + complete_signals: vec![messaging_signal, conn_man_shutdown_signal], + }) } + /// Return a cloned atomic reference of the PeerManager pub fn peer_manager(&self) -> Arc { Arc::clone(&self.peer_manager) } + /// Return an asynchronous PeerManager + pub fn async_peer_manager(&self) -> AsyncPeerManager { + Arc::clone(&self.peer_manager).into() + } + + /// Return a cloned atomic reference of the NodeIdentity pub fn node_identity(&self) -> Arc { Arc::clone(&self.node_identity) } - pub fn connection_manager(&self) -> Arc { - Arc::clone(&self.connection_manager) + /// Return a cloned atomic reference of the NodeIdentity + pub fn executor(&self) -> &runtime::Handle { + &self.executor } - pub fn outbound_message_service(&self) -> Arc { - Arc::clone(&self.outbound_message_service) + /// Return a subscription to OMS events. This will emit events sent _after_ this subscription was created. + pub fn subscribe_messaging_events(&self) -> messaging::MessagingEventReceiver { + self.messaging_event_tx.subscribe() } - pub fn inbound_message_subscription_factory(&self) -> Arc> { - Arc::clone(&self.inbound_message_subscription_factory) + /// Return an owned copy of a ConnectionManagerRequester. Used to initiate connections to peers. + pub fn connection_manager_requester(&self) -> ConnectionManagerRequester { + self.connection_manager_requester.clone() } - pub fn shutdown(self) -> Result<(), CommsServicesError> { - info!(target: LOG_TARGET, "Comms is shutting down"); - let mut shutdown_results = Vec::new(); - // Shutdown control service - if let Some(control_service_shutdown_result) = self.control_service_handle.map(|hnd| hnd.shutdown()) { - shutdown_results.push(control_service_shutdown_result.map_err(CommsServicesError::ControlServiceError)); - } + /// Returns a new `ShutdownSignal` + pub fn shutdown_signal(&self) -> ShutdownSignal { + self.shutdown.to_signal() + } +} - // Shutdown outbound message pool - shutdown_results.push( - self.outbound_message_pool - .shutdown() - .map_err(CommsServicesError::OutboundError), - ); +/// CommsNode is a handle to a comms node. +/// +/// It allows communication with the internals of tari_comms. Note that if this handle is dropped, tari_comms will shut +/// down. +pub struct CommsNode { + /// The Shutdown instance for this node. All applicable internal services will use this as a signal to shutdown. + shutdown: Shutdown, + /// Connection manager broadcast event channel. A `broadcast::Sender` is kept because it can create subscriptions + /// as needed. + connection_manager_event_tx: broadcast::Sender>, + /// Requester object for the ConnectionManager + connection_manager_requester: ConnectionManagerRequester, + /// Node identity for this node + node_identity: Arc, + /// Shared PeerManager instance + peer_manager: Arc, + /// Tari messaging broadcast event channel. A `broadcast::Sender` is kept because it can create subscriptions as + /// needed. + messaging_event_tx: messaging::MessagingEventSender, + /// The resolved Ip-Tcp listening address. + listening_addr: Multiaddr, + /// The executor handle used to run the comms stack + executor: runtime::Handle, + /// `Some` if the comms node is configured to run via a hidden service, otherwise `None` + hidden_service: Option, + /// The 'reciprocal' shutdown signals for each comms service + complete_signals: Vec, +} - // Lastly, Shutdown connection manager - match Arc::try_unwrap(self.connection_manager) { - Ok(conn_manager) => { - for result in conn_manager.shutdown() { - shutdown_results.push(result.map_err(CommsServicesError::ConnectionManagerError)); - } - }, - Err(_) => error!( - target: LOG_TARGET, - "Unable to cleanly shutdown connection manager because references are still held by other threads" - ), - } +impl CommsNode { + pub fn subscribe_connection_manager_events(&self) -> broadcast::Receiver> { + self.connection_manager_event_tx.subscribe() + } + + /// Return a cloned atomic reference of the PeerManager + pub fn peer_manager(&self) -> Arc { + Arc::clone(&self.peer_manager) + } - Self::check_clean_shutdown(shutdown_results) + /// Return an asynchronous PeerManager + pub fn async_peer_manager(&self) -> AsyncPeerManager { + Arc::clone(&self.peer_manager).into() } - fn check_clean_shutdown(results: Vec>) -> Result<(), CommsServicesError> { - let mut has_error = false; - for result in results { - if let Err(err) = result { - error!(target: LOG_TARGET, "Error occurred when shutting down {:?}", err); - has_error = true; - } - } + /// Return a cloned atomic reference of the NodeIdentity + pub fn node_identity(&self) -> Arc { + Arc::clone(&self.node_identity) + } - if has_error { - Err(CommsServicesError::UncleanShutdown) - } else { - Ok(()) - } + /// Return a cloned atomic reference of the NodeIdentity + pub fn executor(&self) -> &runtime::Handle { + &self.executor } -} -#[cfg(test)] -mod test { - use super::*; - use tari_storage::HMapDatabase; - - #[test] - fn new_no_control_service() { - let comms_services: CommsServiceContainer = CommsBuilder::new() - .with_node_identity(NodeIdentity::random_for_test(None)) - .with_peer_storage(HMapDatabase::new()) - .build() - .unwrap(); + /// Return the Ip/Tcp address that this node is listening on + pub fn listening_address(&self) -> &Multiaddr { + &self.listening_addr + } - assert!(comms_services.control_service.is_none()); + /// Return the Ip/Tcp address that this node is listening on + pub fn hidden_service(&self) -> Option<&tor::HiddenService> { + self.hidden_service.as_ref() } - #[test] - fn new_with_control_service() { - let comms_services: CommsServiceContainer = CommsBuilder::new() - .with_node_identity(NodeIdentity::random_for_test(None)) - .with_peer_storage(HMapDatabase::new()) - .configure_control_service(ControlServiceConfig::default()) - .build() - .unwrap(); + /// Return a subscription to OMS events. This will emit events sent _after_ this subscription was created. + pub fn subscribe_messaging_events(&self) -> messaging::MessagingEventReceiver { + self.messaging_event_tx.subscribe() + } - assert!(comms_services.control_service.is_some()); + /// Return an owned copy of a ConnectionManagerRequester. Used to initiate connections to peers. + pub fn connection_manager(&self) -> ConnectionManagerRequester { + self.connection_manager_requester.clone() + } + + /// Returns a new `ShutdownSignal` + pub fn shutdown_signal(&self) -> ShutdownSignal { + self.shutdown.to_signal() + } + + /// Shuts comms down. The object is consumed to ensure that no handles/channels are kept after shutdown + pub fn shutdown(mut self) -> CommsShutdown { + info!(target: LOG_TARGET, "Comms is shutting down"); + self.shutdown.trigger().expect("Shutdown failed to trigger signal"); + CommsShutdown::new(self.complete_signals) } } diff --git a/comms/src/builder/consts.rs b/comms/src/builder/consts.rs new file mode 100644 index 0000000000..95bd623063 --- /dev/null +++ b/comms/src/builder/consts.rs @@ -0,0 +1,44 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/// Buffer size for inbound messages from _all_ peers. This should be large enough to buffer quite a few incoming +/// messages before creating backpressure on peers speaking the messaging protocol. +pub const INBOUND_MESSAGE_BUFFER_SIZE: usize = 100; +/// Buffer size for actor requests to connection manager. A lower value is ok because the connection manager shouldn't +/// need to handle a ton of requests concurrently. +pub const CONNECTION_MANAGER_REQUEST_BUFFER_SIZE: usize = 10; +/// Connection manager events buffer size. The size should allow more than enough "time" for slow subscribers to read +/// the events while not being wasteful. +pub const CONNECTION_MANAGER_EVENTS_BUFFER_SIZE: usize = 30; +/// Buffer size notifications that a peer wants to speak /tari/messaging. This buffer is used for all peers, but a low +/// value is ok because this events happen once (or less) per connecting peer. For e.g. a value of 10 would allow 10 +/// peers to concurrently request to speak /tari/messaging. +pub const MESSAGING_PROTOCOL_EVENTS_BUFFER_SIZE: usize = 10; +/// Buffer size of the messaging event channel. The size should allow more than enough "time" for slow subscribers to +/// read the events while not being wasteful. An MessageReceived event is published per message, which could happen +/// quite a lot, so a little more room to buffer here is recommended. +pub const MESSAGING_EVENTS_BUFFER_SIZE: usize = 100; +/// Buffer size for requests to the messaging protocol. All outbound messages will be sent along this channel. Some +/// buffering may be required if the node needs to send many messages out at the same time. +pub const MESSAGING_REQUEST_BUFFER_SIZE: usize = 50; +/// The default maximum number of times to retry sending a failed message before publishing a SendMessageFailed event +pub const MESSAGING_MAX_SEND_RETRIES: usize = 3; diff --git a/comms/src/builder/mod.rs b/comms/src/builder/mod.rs index 554a223645..4017492eaa 100644 --- a/comms/src/builder/mod.rs +++ b/comms/src/builder/mod.rs @@ -24,55 +24,16 @@ //! //! The [CommsBuilder] provides a simple builder API for getting Tari comms p2p messaging up and running. //! -//! ```edition2018 -//! # use tari_comms::builder::CommsBuilder; -//! # use tari_comms::dispatcher::HandlerError; -//! # use tari_comms::message::InboundMessage; -//! # use tari_comms::control_service::ControlServiceConfig; -//! # use tari_comms::peer_manager::NodeIdentity; -//! # use std::sync::Arc; -//! # use rand::OsRng; -//! # use tari_storage::lmdb_store::LMDBBuilder; -//! # use lmdb_zero::db; -//! # use tari_storage::LMDBWrapper; -//! -//! // This should be loaded up from storage -//! let my_node_identity = NodeIdentity::random(&mut OsRng::new().unwrap(), "127.0.0.1:9000".parse().unwrap()).unwrap(); -//! -//! fn my_handler(_: InboundMessage) -> Result<(), HandlerError> { -//! println!("Your handler is called!"); -//! Ok(()) -//! } -//! -//! let database_name = "b_peer_database"; -//! let datastore = LMDBBuilder::new() -//! .set_path("/tmp/") -//! .set_environment_size(10) -//! .set_max_number_of_databases(2) -//! .add_database(database_name, lmdb_zero::db::CREATE) -//! .build().unwrap(); -//! let peer_database = datastore.get_handle(database_name).unwrap(); -//! let peer_database = LMDBWrapper::new(Arc::new(peer_database)); -//! -//! let services = CommsBuilder::::new() -//! // This enables the control service - allowing another peer to connect to this node -//! .configure_control_service(ControlServiceConfig::default()) -//! .with_node_identity(my_node_identity) -//! .with_peer_storage(peer_database) -//! .build() -//! .unwrap(); -//! -//! let handle = services.start().unwrap(); -//! // Call shutdown when program shuts down -//! handle.shutdown(); -//! ``` -//! //! [CommsBuilder]: ./builder/struct.CommsBuilder.html mod builder; -mod routes; +pub use self::builder::{BuiltCommsNode, CommsBuilder, CommsBuilderError, CommsNode}; + +mod shutdown; +pub use shutdown::CommsShutdown; + +mod consts; +mod placeholder; -pub use self::{ - builder::{CommsBuilder, CommsBuilderError, CommsServices, CommsServicesError}, - routes::CommsRoutes, -}; +#[cfg(test)] +mod tests; diff --git a/comms/src/builder/placeholder.rs b/comms/src/builder/placeholder.rs new file mode 100644 index 0000000000..3fc89e62a4 --- /dev/null +++ b/comms/src/builder/placeholder.rs @@ -0,0 +1,51 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use futures::future; +use std::{ + marker::PhantomData, + task::{Context, Poll}, +}; +use tower::Service; + +/// A service which is used as a placeholder type. This service will panic if used. +pub struct PlaceholderService(PhantomData<(TReq, TResp, TErr)>); + +impl Service for PlaceholderService { + type Error = TErr; + type Future = future::Ready>; + type Response = TResp; + + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { + panic!("poll_ready called on PlaceholderService") + } + + fn call(&mut self, _: TReq) -> Self::Future { + panic!("call called on PlaceholderService") + } +} + +impl Clone for PlaceholderService { + fn clone(&self) -> Self { + Self(PhantomData) + } +} diff --git a/comms/src/builder/routes.rs b/comms/src/builder/routes.rs deleted file mode 100644 index 64946ef93f..0000000000 --- a/comms/src/builder/routes.rs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::connection::InprocAddress; - -#[derive(Clone, Debug, Default)] -pub struct CommsRoutes(Vec<(MType, InprocAddress)>); - -impl CommsRoutes -where MType: Clone + Eq -{ - pub fn new() -> Self { - Self(Vec::new()) - } - - pub fn register(mut self, message_type: MType) -> Self { - let addr = InprocAddress::random(); - self.0.push((message_type, addr.clone())); - self - } - - pub fn inner(&self) -> &Vec<(MType, InprocAddress)> { - &self.0 - } - - pub fn get_address(&self, message_type: &MType) -> Option<&InprocAddress> { - self.0.iter().find(|(mt, _)| mt == message_type).map(|(_, addr)| addr) - } -} diff --git a/comms/src/builder/shutdown.rs b/comms/src/builder/shutdown.rs new file mode 100644 index 0000000000..7c96c38034 --- /dev/null +++ b/comms/src/builder/shutdown.rs @@ -0,0 +1,53 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use futures::{future, future::JoinAll, FutureExt}; +use std::{ + future::Future, + pin::Pin, + task::{Context, Poll}, +}; +use tari_shutdown::ShutdownSignal; + +/// Future which resolves once comms has shut down +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct CommsShutdown { + signals: JoinAll, +} + +impl CommsShutdown { + pub fn new(triggers: I) -> Self + where I: IntoIterator { + Self { + signals: future::join_all(triggers), + } + } +} + +impl Future for CommsShutdown { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let _ = futures::ready!(self.signals.poll_unpin(cx)); + Poll::Ready(()) + } +} diff --git a/comms/src/builder/tests.rs b/comms/src/builder/tests.rs new file mode 100644 index 0000000000..744ead9314 --- /dev/null +++ b/comms/src/builder/tests.rs @@ -0,0 +1,335 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + backoff::ConstantBackoff, + builder::builder::{CommsBuilder, CommsNode}, + connection_manager::ConnectionManagerEvent, + memsocket, + message::{InboundMessage, OutboundMessage}, + multiaddr::{Multiaddr, Protocol}, + peer_manager::{Peer, PeerFeatures}, + pipeline, + pipeline::SinkService, + protocol::{messaging::MessagingEvent, ProtocolEvent, Protocols}, + test_utils::node_identity::build_node_identity, + transports::MemoryTransport, + types::CommsSubstream, +}; +use bytes::Bytes; +use futures::{channel::mpsc, AsyncReadExt, AsyncWriteExt, SinkExt, StreamExt}; +use std::{collections::HashSet, convert::identity, hash::Hash, sync::Arc, time::Duration}; +use tari_storage::HashmapDatabase; +use tari_test_utils::{collect_stream, unpack_enum}; +use tokio::runtime; + +async fn spawn_node( + protocols: Protocols, +) -> (CommsNode, mpsc::Receiver, mpsc::Sender) { + let addr = format!("/memory/{}", memsocket::acquire_next_memsocket_port()) + .parse::() + .unwrap(); + let node_identity = build_node_identity(PeerFeatures::COMMUNICATION_NODE); + node_identity.set_public_address(addr.clone()).unwrap(); + + let (inbound_tx, inbound_rx) = mpsc::channel(10); + let (outbound_tx, outbound_rx) = mpsc::channel(10); + + let comms_node = CommsBuilder::new() + // These calls are just to get rid of unused function warnings. + // + .with_executor(runtime::Handle::current()) + .with_dial_backoff(ConstantBackoff::new(Duration::from_millis(500))) + .on_shutdown(|| {}) + // + .with_listener_address(addr) + .with_transport(MemoryTransport) + .with_peer_storage(HashmapDatabase::new()) + + .with_node_identity(node_identity) + .with_protocols(protocols) + .build() + .unwrap(); + + let comms_node = comms_node + .with_messaging_pipeline( + pipeline::Builder::new() + // Outbound messages will be forwarded "as is" to outbound messaging + .with_outbound_pipeline(outbound_rx, identity) + .max_concurrent_inbound_tasks(1) + // Inbound messages will be forwarded "as is" to inbound_tx + .with_inbound_pipeline(SinkService::new(inbound_tx)) + .finish(), + ) + .spawn() + .await + .unwrap(); + + unpack_enum!(Protocol::Memory(_port) = comms_node.listening_address().iter().next().unwrap()); + + // This call is to get rid of unused function warnings + comms_node.peer_manager(); + + (comms_node, inbound_rx, outbound_tx) +} + +#[tokio_macros::test_basic] +async fn peer_to_peer_custom_protocols() { + const TEST_PROTOCOL: Bytes = Bytes::from_static(b"/tari/test"); + const ANOTHER_TEST_PROTOCOL: Bytes = Bytes::from_static(b"/tari/test-again"); + const TEST_MSG: &[u8] = b"Hello Tari"; + const ANOTHER_TEST_MSG: &[u8] = b"Comms is running smoothly"; + + // Setup test protocols + let (test_sender, _test_protocol_rx1) = mpsc::channel(10); + let (another_test_sender, mut another_test_protocol_rx1) = mpsc::channel(10); + let protocols1 = Protocols::new() + .add(&[TEST_PROTOCOL], test_sender) + .add(&[ANOTHER_TEST_PROTOCOL], another_test_sender); + let (test_sender, mut test_protocol_rx2) = mpsc::channel(10); + let (another_test_sender, _another_test_protocol_rx2) = mpsc::channel(10); + let protocols2 = Protocols::new() + .add(&[TEST_PROTOCOL], test_sender) + .add(&[ANOTHER_TEST_PROTOCOL], another_test_sender); + + let (comms_node1, _, _) = spawn_node(protocols1).await; + let (comms_node2, _, _) = spawn_node(protocols2).await; + + let node_identity1 = comms_node1.node_identity(); + let node_identity2 = comms_node2.node_identity(); + comms_node1 + .async_peer_manager() + .add_peer(Peer::new( + node_identity2.public_key().clone(), + node_identity2.node_id().clone(), + node_identity2.public_address().clone().into(), + Default::default(), + Default::default(), + )) + .await + .unwrap(); + + let mut conn_man_events1 = comms_node1.subscribe_connection_manager_events(); + let mut conn_man_requester1 = comms_node1.connection_manager(); + let mut conn_man_events2 = comms_node2.subscribe_connection_manager_events(); + + let mut conn1 = conn_man_requester1 + .dial_peer(node_identity2.node_id().clone()) + .await + .unwrap(); + + // Check that both nodes get the PeerConnected event. We subscribe after the nodes are initialized + // so we miss those events. + let next_event = conn_man_events2.next().await.unwrap().unwrap(); + unpack_enum!(ConnectionManagerEvent::PeerConnected(conn2) = Arc::try_unwrap(next_event).unwrap()); + let next_event = conn_man_events1.next().await.unwrap().unwrap(); + unpack_enum!(ConnectionManagerEvent::PeerConnected(_conn) = &*next_event); + + // Let's speak both our test protocols + let mut negotiated_substream1 = conn1.open_substream(TEST_PROTOCOL).await.unwrap(); + assert_eq!(negotiated_substream1.protocol, TEST_PROTOCOL); + negotiated_substream1.stream.write_all(TEST_MSG).await.unwrap(); + + let mut negotiated_substream2 = conn2.open_substream(ANOTHER_TEST_PROTOCOL).await.unwrap(); + assert_eq!(negotiated_substream2.protocol, ANOTHER_TEST_PROTOCOL); + negotiated_substream2.stream.write_all(ANOTHER_TEST_MSG).await.unwrap(); + + // Read TEST_PROTOCOL message to node 2 from node 1 + let negotiation = test_protocol_rx2.next().await.unwrap(); + assert_eq!(negotiation.protocol, TEST_PROTOCOL); + unpack_enum!(ProtocolEvent::NewInboundSubstream(node_id, substream) = negotiation.event); + assert_eq!(&*node_id, node_identity1.node_id()); + let mut buf = [0u8; TEST_MSG.len()]; + substream.read_exact(&mut buf).await.unwrap(); + assert_eq!(buf, TEST_MSG); + + // Read ANOTHER_TEST_PROTOCOL message to node 1 from node 2 + let negotiation = another_test_protocol_rx1.next().await.unwrap(); + assert_eq!(negotiation.protocol, ANOTHER_TEST_PROTOCOL); + unpack_enum!(ProtocolEvent::NewInboundSubstream(node_id, substream) = negotiation.event); + assert_eq!(&*node_id, node_identity2.node_id()); + let mut buf = [0u8; ANOTHER_TEST_MSG.len()]; + substream.read_exact(&mut buf).await.unwrap(); + assert_eq!(buf, ANOTHER_TEST_MSG); + + comms_node1.shutdown().await; + comms_node2.shutdown().await; +} + +#[tokio_macros::test_basic] +async fn peer_to_peer_messaging() { + const NUM_MSGS: usize = 100; + + let (comms_node1, inbound_rx1, mut outbound_tx1) = spawn_node(Protocols::new()).await; + let (comms_node2, inbound_rx2, mut outbound_tx2) = spawn_node(Protocols::new()).await; + + let messaging_events1 = comms_node1.subscribe_messaging_events(); + let messaging_events2 = comms_node2.subscribe_messaging_events(); + + let node_identity1 = comms_node1.node_identity(); + let node_identity2 = comms_node2.node_identity(); + comms_node1 + .async_peer_manager() + .add_peer(Peer::new( + node_identity2.public_key().clone(), + node_identity2.node_id().clone(), + node_identity2.public_address().clone().into(), + Default::default(), + Default::default(), + )) + .await + .unwrap(); + + // Send NUM_MSGS messages from node 1 to node 2 + for i in 0..NUM_MSGS { + let outbound_msg = OutboundMessage::new( + node_identity2.node_id().clone(), + Default::default(), + format!("#{:0>3} - comms messaging is so hot right now!", i).into(), + ); + outbound_tx1.send(outbound_msg).await.unwrap(); + } + + let messages1_to_2 = collect_stream!(inbound_rx2, take = NUM_MSGS, timeout = Duration::from_secs(10)); + let events = collect_stream!(messaging_events1, take = NUM_MSGS, timeout = Duration::from_secs(10)); + events.into_iter().map(Result::unwrap).for_each(|m| { + unpack_enum!(MessagingEvent::MessageSent(_t) = &*m); + }); + + let events = collect_stream!(messaging_events2, take = NUM_MSGS, timeout = Duration::from_secs(10)); + events.into_iter().map(Result::unwrap).for_each(|m| { + unpack_enum!(MessagingEvent::MessageReceived(_n, _t) = &*m); + }); + + // Send NUM_MSGS messages from node 2 to node 1 + for i in 0..NUM_MSGS { + let outbound_msg = OutboundMessage::new( + node_identity1.node_id().clone(), + Default::default(), + format!("#{:0>3} - comms messaging is so hot right now!", i).into(), + ); + outbound_tx2.send(outbound_msg).await.unwrap(); + } + + let messages2_to_1 = collect_stream!(inbound_rx1, take = NUM_MSGS, timeout = Duration::from_secs(10)); + + // Check that we got all the messages + let check_messages = |msgs: Vec| { + for (i, msg) in msgs.iter().enumerate() { + let expected_msg_prefix = format!("#{:0>3}", i); + // 0..4 zero padded prefix bytes e.g. #003, #023, #100 + assert_eq!(&msg.body[0..4], expected_msg_prefix.as_bytes()); + } + }; + assert_eq!(messages1_to_2.len(), NUM_MSGS); + check_messages(messages1_to_2); + assert_eq!(messages2_to_1.len(), NUM_MSGS); + check_messages(messages2_to_1); + + comms_node1.shutdown().await; + comms_node2.shutdown().await; +} + +#[tokio_macros::test_basic] +async fn peer_to_peer_messaging_simultaneous() { + const NUM_MSGS: usize = 10; + + let (comms_node1, inbound_rx1, mut outbound_tx1) = spawn_node(Protocols::new()).await; + let (comms_node2, inbound_rx2, mut outbound_tx2) = spawn_node(Protocols::new()).await; + + let o1 = outbound_tx1.clone(); + let o2 = outbound_tx2.clone(); + + let node_identity1 = comms_node1.node_identity(); + let node_identity2 = comms_node2.node_identity(); + comms_node1 + .async_peer_manager() + .add_peer(Peer::new( + node_identity2.public_key().clone(), + node_identity2.node_id().clone(), + node_identity2.public_address().clone().into(), + Default::default(), + Default::default(), + )) + .await + .unwrap(); + comms_node2 + .async_peer_manager() + .add_peer(Peer::new( + node_identity1.public_key().clone(), + node_identity1.node_id().clone(), + node_identity1.public_address().clone().into(), + Default::default(), + Default::default(), + )) + .await + .unwrap(); + + // Simultaneously send messages between the two nodes + let rt_handle = runtime::Handle::current(); + let handle1 = rt_handle.spawn(async move { + for i in 0..NUM_MSGS { + let outbound_msg = OutboundMessage::new( + node_identity2.node_id().clone(), + Default::default(), + format!("#{:0>3} - comms messaging is so hot right now!", i).into(), + ); + outbound_tx1.send(outbound_msg).await.unwrap(); + } + }); + + let handle2 = rt_handle.spawn(async move { + for i in 0..NUM_MSGS { + let outbound_msg = OutboundMessage::new( + node_identity1.node_id().clone(), + Default::default(), + format!("#{:0>3} - comms messaging is so hot right now!", i).into(), + ); + outbound_tx2.send(outbound_msg).await.unwrap(); + } + }); + + handle1.await.unwrap(); + handle2.await.unwrap(); + + // Tasks are finished, let's see if all the messages made it though + let messages1_to_2 = collect_stream!(inbound_rx2, take = NUM_MSGS, timeout = Duration::from_secs(10)); + let messages2_to_1 = collect_stream!(inbound_rx1, take = NUM_MSGS, timeout = Duration::from_secs(10)); + + assert!(has_unique_elements(messages1_to_2.into_iter().map(|m| m.body))); + assert!(has_unique_elements(messages2_to_1.into_iter().map(|m| m.body))); + + drop(o1); + drop(o2); + + comms_node1.shutdown().await; + comms_node2.shutdown().await; +} + +fn has_unique_elements(iter: T) -> bool +where + T: IntoIterator, + T::Item: Eq + Hash, +{ + let mut uniq = HashSet::new(); + iter.into_iter().all(move |x| uniq.insert(x)) +} diff --git a/comms/src/compat.rs b/comms/src/compat.rs new file mode 100644 index 0000000000..254876f14d --- /dev/null +++ b/comms/src/compat.rs @@ -0,0 +1,93 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Copyright (c) The Libra Core Contributors +// SPDX-License-Identifier: Apache-2.0 + +//! This module provides a compatibility shim between traits in the `futures` and `tokio` crate. +use std::{ + io, + pin::Pin, + task::{self, Poll}, +}; + +/// `IoCompat` provides a compatibility shim between the `AsyncRead`/`AsyncWrite` traits provided by +/// the `futures` library and those provided by the `tokio` library since they are different and +/// incompatible with one another. +#[derive(Copy, Clone, Debug)] +pub struct IoCompat { + inner: T, +} + +impl IoCompat { + pub fn new(inner: T) -> Self { + IoCompat { inner } + } +} + +impl tokio::io::AsyncRead for IoCompat +where T: futures::io::AsyncRead + Unpin +{ + fn poll_read(mut self: Pin<&mut Self>, cx: &mut task::Context, buf: &mut [u8]) -> Poll> { + futures::io::AsyncRead::poll_read(Pin::new(&mut self.inner), cx, buf) + } +} + +impl futures::io::AsyncRead for IoCompat +where T: tokio::io::AsyncRead + Unpin +{ + fn poll_read(mut self: Pin<&mut Self>, cx: &mut task::Context, buf: &mut [u8]) -> Poll> { + tokio::io::AsyncRead::poll_read(Pin::new(&mut self.inner), cx, buf) + } +} + +impl tokio::io::AsyncWrite for IoCompat +where T: futures::io::AsyncWrite + Unpin +{ + fn poll_write(mut self: Pin<&mut Self>, cx: &mut task::Context, buf: &[u8]) -> Poll> { + futures::io::AsyncWrite::poll_write(Pin::new(&mut self.inner), cx, buf) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut task::Context) -> Poll> { + futures::io::AsyncWrite::poll_flush(Pin::new(&mut self.inner), cx) + } + + fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut task::Context) -> Poll> { + futures::io::AsyncWrite::poll_close(Pin::new(&mut self.inner), cx) + } +} + +impl futures::io::AsyncWrite for IoCompat +where T: tokio::io::AsyncWrite + Unpin +{ + fn poll_write(mut self: Pin<&mut Self>, cx: &mut task::Context, buf: &[u8]) -> Poll> { + tokio::io::AsyncWrite::poll_write(Pin::new(&mut self.inner), cx, buf) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut task::Context) -> Poll> { + tokio::io::AsyncWrite::poll_flush(Pin::new(&mut self.inner), cx) + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut task::Context) -> Poll> { + tokio::io::AsyncWrite::poll_shutdown(Pin::new(&mut self.inner), cx) + } +} diff --git a/comms/src/connection/connection.rs b/comms/src/connection/connection.rs deleted file mode 100644 index 255e3dc081..0000000000 --- a/comms/src/connection/connection.rs +++ /dev/null @@ -1,579 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::{ - connection::{ - net_address::ip::SocketAddress, - types::{Direction, Linger, Result, SocketEstablishment, SocketType}, - zmq::{CurveEncryption, InprocAddress, ZmqContext, ZmqEndpoint}, - ConnectionError, - }, - message::FrameSet, -}; -use log::*; -use std::{borrow::Borrow, cmp, iter::IntoIterator, str::FromStr, time::Duration}; - -const LOG_TARGET: &str = "comms::connection::Connection"; - -/// Represents a low-level connection which can be bound an address -/// supported by [`ZeroMQ`] the `ZMQ_ROUTER` socket. -/// -/// ```edition2018 -/// # use tari_comms::connection::{ -/// # zmq::{ZmqContext, InprocAddress, CurveEncryption}, -/// # connection::Connection, -/// # types::{Linger, Direction}, -/// # }; -/// -/// let ctx = ZmqContext::new(); -/// -/// let (secret_key, _public_key) =CurveEncryption::generate_keypair().unwrap(); -/// -/// let addr = "inproc://docs-comms-inbound-connection".parse::().unwrap(); -/// -/// let conn = Connection::new(&ctx, Direction::Inbound) -/// .set_curve_encryption(CurveEncryption::Server {secret_key}) -/// .set_linger(Linger::Never) -/// .set_max_message_size(Some(123)) -/// .set_receive_hwm(1) -/// .set_send_hwm(2) -/// .establish(&addr) -/// .unwrap(); -/// -/// // Receive timeout is 1 so timeout error is returned -/// let result = conn.receive(1); -/// assert!(result.is_err()); -/// ``` -/// [`ZeroMQ`]: http://zeromq.org/ -pub struct Connection<'a> { - pub(super) context: &'a ZmqContext, - pub(super) name: String, - pub(super) curve_encryption: CurveEncryption, - pub(super) direction: Direction, - pub(super) identity: Option, - pub(super) linger: Linger, - pub(super) max_message_size: Option, - pub(super) monitor_addr: Option, - pub(super) recv_hwm: Option, - pub(super) send_hwm: Option, - pub(super) backlog: Option, - pub(super) socket_establishment: SocketEstablishment, - pub(super) socks_proxy_addr: Option, - pub(super) heartbeat_interval: Option, - pub(super) heartbeat_remote_ttl: Option, - pub(super) heartbeat_timeout: Option, -} - -impl<'a> Connection<'a> { - /// Create a new InboundConnection - pub fn new(context: &'a ZmqContext, direction: Direction) -> Self { - Self { - context, - name: "Unnamed".to_string(), - curve_encryption: Default::default(), - direction, - identity: None, - linger: Linger::Never, - max_message_size: None, - monitor_addr: None, - recv_hwm: None, - send_hwm: None, - backlog: None, - socket_establishment: Default::default(), - socks_proxy_addr: None, - heartbeat_interval: None, - heartbeat_remote_ttl: None, - heartbeat_timeout: None, - } - } - - /// Set receive high water mark - pub fn set_receive_hwm(mut self, hwm: i32) -> Self { - self.recv_hwm = Some(hwm); - self - } - - /// Set send high water mark - pub fn set_send_hwm(mut self, hwm: i32) -> Self { - self.send_hwm = Some(hwm); - self - } - - /// Set the connection identity - pub fn set_identity(mut self, identity: &str) -> Self { - self.identity = Some(identity.to_owned()); - self - } - - /// Set the maximum length of the queue of outstanding peer connections - /// for the specified outbound connection. - pub fn set_backlog(mut self, backlog: i32) -> Self { - self.backlog = Some(backlog); - self - } - - /// Set the period the underling socket connection should - /// continue to send messages after this connection is dropped. - pub fn set_linger(mut self, linger: Linger) -> Self { - self.linger = linger; - self - } - - /// Set a name for the connection. This is used in logs and for debugging purposes. - pub fn set_name(mut self, name: &str) -> Self { - self.name = name.to_string(); - self - } - - /// The maximum size in bytes of the inbound message. If a message is - /// received which is larger, the connection will disconnect. - /// `msg_size` has an upper bound of i64::MAX due to zMQ's usage of a signed 64-bit - /// integer for this socket option. Setting it higher will result in i64::MAX being used. - /// Set to None for no limit - pub fn set_max_message_size(mut self, msg_size: Option) -> Self { - self.max_message_size = msg_size; - self - } - - /// Set the InprocAddress to enable monitoring on the underlying socket. - /// All socket events are sent to sent to this address. - /// The monitor must be connected before the connection is established. - pub fn set_monitor_addr(mut self, addr: InprocAddress) -> Self { - self.monitor_addr = Some(addr); - self - } - - /// Set the ip:port of a SOCKS proxy to use for this connection - pub fn set_socks_proxy_addr(mut self, addr: Option) -> Self { - self.socks_proxy_addr = addr; - self - } - - /// Used to select the method to use when establishing the connection. - pub fn set_socket_establishment(mut self, establishment: SocketEstablishment) -> Self { - self.socket_establishment = establishment; - self - } - - /// Set Curve25519 encryption for this connection. - pub fn set_curve_encryption(mut self, encryption: CurveEncryption) -> Self { - self.curve_encryption = encryption; - self - } - - /// Set the interval in which to send heartbeat pings. - pub fn set_heartbeat_interval(mut self, interval: Duration) -> Self { - self.heartbeat_interval = Some(interval); - self - } - - /// Set the length of time to wait for a pong after sending a ping before closing the connection. - pub fn set_heartbeat_timeout(mut self, timeout: Duration) -> Self { - self.heartbeat_timeout = Some(timeout); - self - } - - /// Set the interval time that the remote peer expect to receive heartbeat/other messages. - /// If the remote peer does not receive any message within the TTL period it should close the connection. - /// More info: http://api.zeromq.org/4-2:zmq-setsockopt#toc17 - pub fn set_heartbeat_remote_ttl(mut self, ttl: Duration) -> Self { - self.heartbeat_remote_ttl = Some(ttl); - self - } - - /// Create the socket, configure it and bind/connect it to the given address - pub fn establish(self, addr: &T) -> Result { - let socket = match self.direction { - Direction::Inbound => self.context.socket(SocketType::Router).unwrap(), - Direction::Outbound => self.context.socket(SocketType::Dealer).unwrap(), - }; - - let config_error_mapper = |e| ConnectionError::SocketError(format!("Unable to configure socket: {}", e)); - - if self.direction == Direction::Inbound { - socket.set_router_mandatory(true).map_err(config_error_mapper)?; - } - - if let Some(v) = self.recv_hwm { - socket.set_rcvhwm(v).map_err(config_error_mapper)?; - } - - if let Some(v) = self.send_hwm { - socket.set_sndhwm(v).map_err(config_error_mapper)?; - } - - if let Some(ident) = self.identity { - socket.set_identity(ident.as_bytes()).map_err(config_error_mapper)?; - } - - if let Some(v) = self.max_message_size { - socket - .set_maxmsgsize(cmp::min(v, std::i64::MAX as u64) as i64) - .map_err(config_error_mapper)?; - } - - set_linger(&socket, self.linger)?; - - if let Some(backlog) = self.backlog { - socket.set_backlog(backlog).map_err(config_error_mapper)?; - } - - match self.curve_encryption { - CurveEncryption::None => {}, - CurveEncryption::Server { secret_key } => { - socket.set_curve_server(true).map_err(config_error_mapper)?; - socket - .set_curve_secretkey(&secret_key.into_inner()) - .map_err(config_error_mapper)?; - }, - CurveEncryption::Client { - secret_key, - public_key, - server_public_key, - } => { - socket - .set_curve_serverkey(&server_public_key.into_inner()) - .map_err(config_error_mapper)?; - socket - .set_curve_secretkey(&secret_key.into_inner()) - .map_err(config_error_mapper)?; - socket - .set_curve_publickey(&public_key.into_inner()) - .map_err(config_error_mapper)?; - }, - } - - // Set heartbeat socket opts - if let Some(interval) = self.heartbeat_interval { - socket - .set_heartbeat_ivl(interval.as_millis() as i32) - .map_err(config_error_mapper)?; - } - - if let Some(timeout) = self.heartbeat_timeout { - socket - .set_heartbeat_timeout(timeout.as_millis() as i32) - .map_err(config_error_mapper)?; - } - - if let Some(ttl) = self.heartbeat_remote_ttl { - socket - .set_heartbeat_ttl(ttl.as_millis() as i32) - .map_err(config_error_mapper)?; - } - - if let Some(v) = self.socks_proxy_addr { - socket - .set_socks_proxy(Some(&v.to_string())) - .map_err(config_error_mapper)?; - } - - if let Some(ref addr) = self.monitor_addr { - socket - .monitor(addr.to_zmq_endpoint().as_str(), zmq::SocketEvent::ALL as i32) - .map_err(|e| ConnectionError::SocketError(format!("Unable to set monitor address: {}", e)))?; - } - - let endpoint = &addr.to_zmq_endpoint(); - match self.socket_establishment { - SocketEstablishment::Bind => socket.bind(endpoint), - SocketEstablishment::Connect => socket.connect(endpoint), - SocketEstablishment::Auto => match self.direction { - Direction::Inbound => socket.bind(endpoint), - Direction::Outbound => socket.connect(endpoint), - }, - } - .map_err(|e| ConnectionError::SocketError(format!("Failed to establish socket: {}", e)))?; - - let connected_address = get_socket_address(&socket); - - debug!( - target: LOG_TARGET, - "Established {} connection on {:?} (name: {})", - self.direction, - connected_address - .borrow() - .as_ref() - .map(ToString::to_string) - .unwrap_or_else(|| endpoint.to_owned()), - self.name, - ); - - Ok(EstablishedConnection { - socket, - connected_address, - name: self.name, - direction: self.direction, - }) - } -} - -fn set_linger(socket: &zmq::Socket, linger: Linger) -> Result<()> { - let config_error_mapper = |e| ConnectionError::SocketError(format!("Unable to configure linger on socket: {}", e)); - match linger { - Linger::Indefinitely => socket.set_linger(-1).map_err(config_error_mapper), - - Linger::Never => socket.set_linger(0).map_err(config_error_mapper), - - Linger::Timeout(t) => socket.set_linger(t as i32).map_err(config_error_mapper), - } -} - -/// Represents an established connection. -pub struct EstablishedConnection { - socket: zmq::Socket, - // If the connection is a TCP connection, it will be stored here, otherwise it is None - connected_address: Option, - name: String, - direction: Direction, -} - -impl EstablishedConnection { - /// Receive a multipart message or return a `ConnectionError::Timeout` if the specified timeout has expired. - /// This method may be repeatably called, probably in a loop in a separate thread, to receive multiple multipart - /// messages. - pub fn receive(&self, timeout_ms: u32) -> Result { - match self.socket.poll(zmq::POLLIN, i64::from(timeout_ms)) { - Ok(rc) => { - match rc { - // Internal error when polling connection - -1 => Err(ConnectionError::SocketError("Failed to poll socket".to_string())), - // Nothing to receive - 0 => Err(ConnectionError::Timeout), - // Ready to receive - _ => self.receive_multipart(), - } - }, - - Err(e) => Err(ConnectionError::SocketError(format!("Failed to poll: {}", e))), - } - } - - /// Return the actual address that we're connected to. On inbound connections, once can delegate port selection to - /// the OS, (e.g. "127.0.0.1:0") which means that the actual port we're connecting to isn't known until the binding - /// has been made. This function queries the socket for the connection info, and extracts the address & port if it - /// was a TCP connection, returning None otherwise - pub fn get_connected_address(&self) -> &Option { - &self.connected_address - } - - /// Read entire multipart message - pub fn receive_multipart(&self) -> Result { - self.socket - .recv_multipart(0) - .and_then(|frames| { - trace!( - target: LOG_TARGET, - "Received {} frame(s) (name: {})", - frames.len(), - self.name - ); - Ok(frames) - }) - .map_err(|e| ConnectionError::SocketError(format!("Error receiving: {} ({})", e, e.to_raw()))) - } - - /// Set the period the underling socket connection should - /// continue to send messages after this connection is dropped. - pub fn set_linger(&self, linger: Linger) -> Result<()> { - set_linger(&self.socket, linger) - } - - /// Sends multipart message frames. This function is non-blocking. - pub fn send(&self, frames: I) -> Result<()> - where - I: IntoIterator, - T: AsRef<[u8]>, - { - self.send_with_flags(frames, zmq::DONTWAIT) - } - - /// Sends multipart message frames. This will block until the message is queued - /// for sending. - pub fn send_sync(&self, frames: I) -> Result<()> - where - I: IntoIterator, - T: AsRef<[u8]>, - { - self.send_with_flags(frames, 0) - } - - fn send_with_flags(&self, frames: I, flags: i32) -> Result<()> - where - I: IntoIterator, - T: AsRef<[u8]>, - { - let mut last_frame: Option = None; - for frame in frames.into_iter() { - if let Some(f) = last_frame.take() { - self.send_frame(f, flags | zmq::SNDMORE)?; - } - last_frame = Some(frame); - } - if let Some(f) = last_frame { - self.send_frame(f, flags)?; - } - - Ok(()) - } - - fn send_frame(&self, frame: T, flags: i32) -> Result<()> - where T: AsRef<[u8]> { - self.socket - .send(frame.as_ref(), flags) - .map_err(|e| ConnectionError::SocketError(format!("Error sending: {} ({})", e, e.to_raw()))) - } - - #[cfg(test)] - pub(crate) fn get_socket(&self) -> &zmq::Socket { - &self.socket - } - - pub(crate) fn get_socket_mut(&mut self) -> &mut zmq::Socket { - &mut self.socket - } - - pub fn direction(&self) -> &Direction { - &self.direction - } -} - -impl Drop for EstablishedConnection { - fn drop(&mut self) { - debug!( - target: LOG_TARGET, - "Dropping {} connection {:?} (name: {})", - self.direction, - self.get_connected_address(), - self.name, - ); - } -} - -/// Extract the actual address that we're connected to. On inbound connections, once can delegate port selection to -/// the OS, (e.g. "127.0.0.1:0") which means that the actual port we're connecting to isn't known until the binding -/// has been made. This function queries the socket for the connection info, and extracts the address & port if it -/// was a TCP connection, returning None otherwise -fn get_socket_address(socket: &zmq::Socket) -> Option { - let addr = match socket.get_last_endpoint() { - Ok(v) => v.unwrap(), - Err(_) => return None, - }; - let parts = &addr.split("//").collect::>(); - if parts.len() < 2 || parts[0] != "tcp:" { - return None; - } - let addr = parts[1]; - SocketAddress::from_str(&addr).ok() -} - -#[cfg(test)] -mod test { - use super::*; - use crate::connection::zmq::{CurveEncryption, InprocAddress}; - - #[test] - fn sets_socketopts() { - let ctx = ZmqContext::new(); - - let addr = InprocAddress::random(); - let monitor_addr = InprocAddress::random(); - - let conn = Connection::new(&ctx, Direction::Inbound) - .set_name("dummy") - .set_heartbeat_remote_ttl(Duration::from_millis(1000)) - .set_heartbeat_timeout(Duration::from_millis(1001)) - .set_heartbeat_interval(Duration::from_millis(1002)) - .set_identity("identity") - .set_linger(Linger::Timeout(200)) - .set_max_message_size(Some(123)) - .set_receive_hwm(1) - .set_send_hwm(2) - .set_socks_proxy_addr(Some("127.0.0.1:9988".parse::().unwrap())) - .set_monitor_addr(monitor_addr) - .establish(&addr) - .unwrap(); - - assert_eq!("dummy", conn.name); - let sock = conn.get_socket(); - assert!(!sock.is_curve_server().unwrap()); - assert_eq!(200, sock.get_linger().unwrap()); - assert_eq!(123, sock.get_maxmsgsize().unwrap()); - assert_eq!(1000, sock.get_heartbeat_ttl().unwrap()); - assert_eq!(1001, sock.get_heartbeat_timeout().unwrap()); - assert_eq!(1002, sock.get_heartbeat_ivl().unwrap()); - assert_eq!("identity".as_bytes(), sock.get_identity().unwrap().as_slice()); - assert_eq!(1, sock.get_rcvhwm().unwrap()); - assert_eq!(2, sock.get_sndhwm().unwrap()); - assert_eq!(Ok("127.0.0.1:9988".to_string()), sock.get_socks_proxy().unwrap()); - } - - #[test] - fn set_server_encryption() { - let ctx = ZmqContext::new(); - - let addr = InprocAddress::random(); - - let (sk, _) = CurveEncryption::generate_keypair().unwrap(); - let expected_sk = sk.clone(); - - let conn = Connection::new(&ctx, Direction::Inbound) - .set_curve_encryption(CurveEncryption::Server { secret_key: sk }) - .establish(&addr) - .unwrap(); - - let sock = conn.get_socket(); - assert!(sock.is_curve_server().unwrap()); - assert_eq!(expected_sk.into_inner(), sock.get_curve_secretkey().unwrap().as_slice()); - } - - #[test] - fn set_client_encryption() { - let ctx = ZmqContext::new(); - - let addr = InprocAddress::random(); - - let (sk, pk) = CurveEncryption::generate_keypair().unwrap(); - let (_, spk) = CurveEncryption::generate_keypair().unwrap(); - let expected_sk = sk.clone(); - let expected_pk = pk.clone(); - let expected_spk = spk.clone(); - - let conn = Connection::new(&ctx, Direction::Inbound) - .set_curve_encryption(CurveEncryption::Client { - secret_key: sk, - public_key: pk, - server_public_key: spk, - }) - .establish(&addr) - .unwrap(); - - let sock = conn.get_socket(); - assert!(!sock.is_curve_server().unwrap()); - assert_eq!(expected_sk.into_inner(), sock.get_curve_secretkey().unwrap().as_slice()); - assert_eq!(expected_pk.into_inner(), sock.get_curve_publickey().unwrap().as_slice()); - assert_eq!( - expected_spk.into_inner(), - sock.get_curve_serverkey().unwrap().as_slice() - ); - } -} diff --git a/comms/src/connection/dealer_proxy.rs b/comms/src/connection/dealer_proxy.rs deleted file mode 100644 index 19ee0dd724..0000000000 --- a/comms/src/connection/dealer_proxy.rs +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use log::*; - -use std::thread; - -use derive_error::Error; - -use crate::connection::{ - types::{Direction, SocketEstablishment, SocketType}, - zmq::ZmqEndpoint, - Connection, - ConnectionError, - InprocAddress, - ZmqContext, -}; -use std::{sync::mpsc::channel, thread::JoinHandle, time::Duration}; -use tari_utilities::thread_join::{ThreadError, ThreadJoinWithTimeout}; - -const LOG_TARGET: &str = "comms::dealer_proxy"; - -/// Set the allocated stack size for the DealerProxy thread -const THREAD_STACK_SIZE: usize = 64 * 1024; // 64kb - -/// Set the maximum waiting time for DealerProxy thread to join -const THREAD_JOIN_TIMEOUT_IN_MS: Duration = Duration::from_millis(100); - -#[derive(Debug, Error)] -pub enum DealerProxyError { - #[error(msg_embedded, no_from, non_std)] - SocketError(String), - ConnectionError(ConnectionError), - /// The dealer [thread::JoinHandle] is unavailable - DealerUndefined, - /// Could not join the dealer thread - ThreadJoinError(ThreadError), - /// Proxy thread failed to start within 10 seconds - ThreadStartFailed, - #[error(msg_embedded, no_from, non_std)] - ZmqError(String), - /// Dealer proxy thread failed to start - ThreadInitializationError, -} - -/// A DealerProxy Result -pub type Result = std::result::Result; - -/// Proxies two addresses, receiving from the source_address and fair dealing to the -/// sink_address. -pub struct DealerProxy { - context: ZmqContext, - source_address: InprocAddress, - sink_address: InprocAddress, - control_address: InprocAddress, - thread_handle: Option>>, -} - -impl DealerProxy { - /// Creates a new DealerProxy. - pub fn new(context: ZmqContext, source_address: InprocAddress, sink_address: InprocAddress) -> Self { - Self { - context, - source_address, - sink_address, - control_address: InprocAddress::random(), - thread_handle: None, - } - } - - /// Proxy the source and sink addresses. This method does not block and returns stores the [thread::JoinHandle] in - /// the DealerProxy. - pub fn spawn_proxy(&mut self) -> Result<()> { - let (ready_tx, ready_rx) = channel(); - - let context = self.context.clone(); - let source_address = self.source_address.clone(); - let sink_address = self.sink_address.clone(); - let control_address = self.control_address.clone(); - - self.thread_handle = Some( - thread::Builder::new() - .name("dealer-proxy-thread".to_string()) - .stack_size(THREAD_STACK_SIZE) - .spawn(move || { - let mut source = Connection::new(&context.clone(), Direction::Inbound) - .set_name("dealer-proxy-source") - .set_socket_establishment(SocketEstablishment::Bind) - .establish(&source_address.clone()) - .map_err(DealerProxyError::ConnectionError)?; - - let mut sink = Connection::new(&context.clone(), Direction::Outbound) - .set_name("dealer-proxy-sink") - .set_socket_establishment(SocketEstablishment::Bind) - .establish(&sink_address.clone()) - .map_err(DealerProxyError::ConnectionError)?; - - let mut control = context - .socket(SocketType::Sub) - .map_err(|err| DealerProxyError::ZmqError(err.to_string()))?; - control - .connect(&control_address.to_zmq_endpoint()) - .map_err(|err| DealerProxyError::ZmqError(err.to_string()))?; - control - .set_subscribe(&[]) - .map_err(|err| DealerProxyError::ZmqError(err.to_string()))?; - - ready_tx.send(()).unwrap(); - - zmq::proxy_steerable(source.get_socket_mut(), sink.get_socket_mut(), &mut control) - .map_err(|err| DealerProxyError::SocketError(err.to_string())) - }) - .map_err(|_| DealerProxyError::ThreadInitializationError)?, - ); - - ready_rx - .recv_timeout(Duration::from_secs(10)) - .map_err(|_| DealerProxyError::ThreadStartFailed) - } - - pub fn is_running(&self) -> bool { - self.thread_handle.is_some() - } - - /// Send a shutdown request to the dealer proxy. If the dealer proxy has not been started - /// this method has no effect. - pub fn shutdown(self) -> Result<()> { - if let Some(thread_handle) = self.thread_handle { - info!(target: LOG_TARGET, "Dealer proxy SHUTDOWN"); - let control = self - .context - .socket(SocketType::Pub) - .map_err(|err| DealerProxyError::ZmqError(err.to_string()))?; - - control - .set_linger(3000) - .map_err(|err| DealerProxyError::ZmqError(err.to_string()))?; - - control - .bind(&self.control_address.to_zmq_endpoint()) - .map_err(|err| DealerProxyError::ZmqError(err.to_string()))?; - - control - .send("TERMINATE", zmq::DONTWAIT) - .map_err(|err| DealerProxyError::ZmqError(err.to_string()))?; - - thread_handle - .timeout_join(THREAD_JOIN_TIMEOUT_IN_MS) - .map_err(DealerProxyError::ThreadJoinError) - .or_else(|err| { - error!( - target: LOG_TARGET, - "Dealer proxy thread exited with an error: {:?}", err - ); - Err(err) - })?; - } - - Ok(()) - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn threaded_proxy() { - let context = ZmqContext::new(); - let sender_addr = InprocAddress::random(); - let receiver_addr = InprocAddress::random(); - - let sender = Connection::new(&context, Direction::Outbound) - .establish(&sender_addr) - .unwrap(); - - let receiver = Connection::new(&context, Direction::Outbound) - .establish(&receiver_addr) - .unwrap(); - - let mut proxy = DealerProxy::new(context, sender_addr, receiver_addr); - proxy.spawn_proxy().unwrap(); - - sender.send_sync(&["HELLO".as_bytes()]).unwrap(); - - let msg = receiver.receive(2000).unwrap(); - assert_eq!("HELLO".as_bytes().to_vec(), msg[1]); - - // You need to attach the identity frame to the head of the message - // so that the internal ZMQ_ROUTER will send messages back to the - // connection which sent the message - receiver.send_sync(&[&msg[0], "WORLD".as_bytes()]).unwrap(); - - let msg = sender.receive(2000).unwrap(); - assert_eq!("WORLD".as_bytes().to_vec(), msg[0]); - - // Test steerable dealer shutdown - proxy.shutdown().unwrap(); - } -} diff --git a/comms/src/connection/error.rs b/comms/src/connection/error.rs deleted file mode 100644 index 30e5c7d52e..0000000000 --- a/comms/src/connection/error.rs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use super::{monitor, NetAddressError, PeerConnectionError}; -use derive_error::Error; -use tari_utilities::thread_join::ThreadError; - -#[derive(Debug, Error, PartialEq)] -pub enum ConnectionError { - NetAddressError(NetAddressError), - #[error(msg_embedded, no_from, non_std)] - SocketError(String), - /// Connection timed out - Timeout, - #[error(msg_embedded, no_from, non_std)] - CurveKeypairError(String), - PeerError(PeerConnectionError), - MonitorError(monitor::ConnectionMonitorError), - #[error(msg_embedded, no_from, non_std)] - InvalidOperation(String), - ThreadJoinError(ThreadError), -} - -impl ConnectionError { - /// Returns true if the error is a Timeout error, otherwise false - pub fn is_timeout(&self) -> bool { - match *self { - ConnectionError::Timeout => true, - _ => false, - } - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn is_timeout() { - let err = ConnectionError::Timeout; - assert!(err.is_timeout()); - - let err = ConnectionError::SocketError("dummy error".to_string()); - assert!(!err.is_timeout()); - } -} diff --git a/comms/src/connection/macros.rs b/comms/src/connection/macros.rs deleted file mode 100644 index ee169c4d51..0000000000 --- a/comms/src/connection/macros.rs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -/// Converts a [connection::Result] into an Option. If the call results in -/// a [ConnectionError::Timeout], `None` is passed back. For any other error, -/// the error `return`ed from containing function. -macro_rules! connection_try { - ($e: expr) => { - match $e { - Ok(d) => Some(d), - Err(e) => match e { - crate::connection::ConnectionError::Timeout => None, - _ => return Err(e.into()), - }, - } - }; -} diff --git a/comms/src/connection/mod.rs b/comms/src/connection/mod.rs deleted file mode 100644 index 1cbe3c1a76..0000000000 --- a/comms/src/connection/mod.rs +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -//! # Connection -//! -//! Related modules that abstract [0MQ] connections and other 0MQ related features, -//! address types and connections to peers. -//! -//! This module consists of: -//! -//! - [NetAddress] -//! -//! Represents an IP, Onion or I2P address. -//! -//! - Zero MQ module -//! -//! Thin wrappers of [0MQ]. Namely, -//! - [ZmqContext] -//! - [CurveEncryption] -//! - [ZmqEndpoint] trait -//! - [InprocAddress] -//! -//! - [Connection] -//! -//! Provides a connection builder and thin wrapper around a [0MQ] Router -//! (Inbound [Direction](./connection/enum.Direction.html) or Dealer (Outbound [Direction]) socket. -//! -//! - [DealerProxy] -//! -//! Async wrapper around [zmq_proxy_steerable] for use with [Connection]. -//! -//! - [ConnectionMonitor] -//! -//! Receives socket events from [0MQ] connections. Wrapper around [zmq_socket_monitor]. -//! -//! - [PeerConnection] -//! -//! Represents a connection to a peer. See [PeerConnection]. -//! -//! [0MQ]: http://zeromq.org/ -//! [NetAddress]: (./net_address/enum.NetAddress.html) -//! [Connection]: (./connection/struct.Connection.html) -//! [DealerProxy]: (./dealer_proxy/struct.DealerProxy.html) -//! [PeerConnection]: (./peer_connection/index.html) -//! [ZmqContext]: (./zmq/context/struct.ZmqContext.html) -//! [CurveEncryption]: (./zmq/curve_keypair/enum.CurveEncryption.html) -//! [ZmqEndpoint]: (./zmq/endpoint/trait.ZmqEndpoint.html) -//! [InprocAddress]: (./zmq/inproc_address/struct.InprocAddress.html) -//! [Direction]: (./connection/enum.Direction.html) -//! [zmq_proxy_steerable]: http://api.zeromq.org/4-1:zmq-proxy-steerable -//! [ConnectionMonitor]: ./monitor/struct.ConnectionMonitor.html -//! [zmq_socket_monitor]: http://api.zeromq.org/4-1:zmq-socket-monitor - -#[macro_use] -mod macros; - -pub mod connection; -pub mod dealer_proxy; -pub mod error; -pub mod monitor; -pub mod net_address; -pub mod peer_connection; -pub mod types; -pub mod zmq; - -pub use self::{ - connection::{Connection, EstablishedConnection}, - dealer_proxy::{DealerProxy, DealerProxyError}, - error::ConnectionError, - net_address::{NetAddress, NetAddressError, NetAddressesWithStats}, - peer_connection::{ - PeerConnection, - PeerConnectionContextBuilder, - PeerConnectionError, - PeerConnectionSimpleState as PeerConnectionState, - }, - types::{Direction, SocketEstablishment}, - zmq::{curve_keypair, CurveEncryption, CurvePublicKey, CurveSecretKey, InprocAddress, ZmqContext}, -}; diff --git a/comms/src/connection/monitor.rs b/comms/src/connection/monitor.rs deleted file mode 100644 index 8d16fc899b..0000000000 --- a/comms/src/connection/monitor.rs +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::{ - connection::{ - types::{Result, SocketType}, - zmq::ZmqEndpoint, - ConnectionError, - InprocAddress, - ZmqContext, - }, - message::FrameSet, -}; -use derive_error::Error; -use log::*; -use std::{ - convert::{TryFrom, TryInto}, - fmt, -}; - -const LOG_TARGET: &str = "comms::connection::monitor"; - -#[derive(Debug, Error, PartialEq)] -pub enum ConnectionMonitorError { - #[error(msg_embedded, non_std, no_from)] - CreateSocketFailed(String), - /// Failed to convert integer type to SocketEvent - SocketEventConversionFailed, - #[error(msg_embedded, non_std, no_from)] - ConnectionFailed(String), - /// Received incorrect number of frames - IncorrectFrameCount, -} - -/// ConnectionMonitor is the read side of the ZMQ_PAIR socket. -/// It allows SocketEvents to be read from the given InprocAddress. -/// -/// More details here: http://api.zeromq.org/4-1:zmq-socket-monitor -/// -/// ```edition2018 -/// # use tari_comms::connection::{ZmqContext, monitor::ConnectionMonitor, Connection, Direction, InprocAddress, NetAddress}; -/// -/// let ctx = ZmqContext::new(); -/// let monitor_addr = InprocAddress::random(); -/// let address = "127.0.0.1:9999".parse::().unwrap(); -/// -/// // Monitor MUST start before the connection is established -/// let monitor = ConnectionMonitor::connect(&ctx, &monitor_addr).unwrap(); -/// -/// { -/// Connection::new(&ctx, Direction::Inbound) -/// .set_monitor_addr(monitor_addr) -/// .establish(&address) -/// .unwrap(); -/// } -/// -/// // Read events -/// while let Ok(event) = monitor.read(100) { -/// println!("Got event: {:?}", event); -/// } -/// ``` -pub struct ConnectionMonitor { - socket: zmq::Socket, -} - -impl ConnectionMonitor { - /// Create a new connected ConnectionMonitor. - /// - /// ## Arguments - /// `context` - Connection context. Must be the same context as the connection being monitored - /// `address` - The inproc address from which to read socket events - pub fn connect(context: &ZmqContext, address: &InprocAddress) -> Result { - let socket = context.socket(SocketType::Pair).map_err(|e| { - ConnectionError::MonitorError(ConnectionMonitorError::CreateSocketFailed(format!( - "Failed to create monitor pair socket: {}", - e - ))) - })?; - - socket.connect(&address.to_zmq_endpoint()).map_err(|e| { - ConnectionError::MonitorError(ConnectionMonitorError::ConnectionFailed(format!( - "Failed to connect: {}", - e - ))) - })?; - - debug!(target: LOG_TARGET, "Connection monitor connected on {}", address); - - Ok(Self { socket }) - } - - /// Read a SocketEvent within the given timeout. - /// If the timeout is reached a `Err(ConnectionError::Timeout)` is returned. - /// - /// ## Arguments - /// `timeout_ms` - The timeout to wait in milliseconds - pub fn read(&self, timeout_ms: u32) -> Result { - let frames = self.read_frames(timeout_ms)?; - - if frames.len() != 2 { - return Err(ConnectionMonitorError::IncorrectFrameCount.into()); - } - - macro_rules! transmute_value { - ($data: expr, $start: expr, $end: expr, $type: ty) => { - unsafe { - let mut a: [u8; $end - $start] = Default::default(); - a.copy_from_slice(&$data[$start..$end]); - std::mem::transmute::<[u8; $end - $start], $type>(a).to_le() - } - }; - } - - // First 2 bytes are the event type - let event_type: SocketEventType = transmute_value!(frames[0], 0, 2, u16).try_into()?; - // Next 4 bytes are the event value - let event_value = transmute_value!(frames[0], 2, 6, u32); - - let address = String::from_utf8_lossy(&frames[1]).into_owned(); - - Ok(SocketEvent { - event_type, - event_value, - address, - }) - } - - fn read_frames(&self, timeout_ms: u32) -> Result { - match self.socket.poll(zmq::POLLIN, i64::from(timeout_ms)) { - Ok(rc) => { - match rc { - // Internal error when polling connection - -1 => Err(ConnectionError::SocketError("Failed to poll socket".to_string())), - // Nothing to receive - 0 => Err(ConnectionError::Timeout), - // Ready to receive - _ => self - .socket - .recv_multipart(0) - .map_err(|e| ConnectionError::SocketError(format!("Error receiving: {} ({})", e, e.to_raw()))), - } - }, - - Err(e) => Err(ConnectionError::SocketError(format!("Failed to poll: {}", e))), - } - } -} - -impl Drop for ConnectionMonitor { - fn drop(&mut self) { - debug!(target: LOG_TARGET, "Connection monitor dropped"); - } -} - -/// Represents an event for a socket -#[derive(Debug)] -pub struct SocketEvent { - /// The type of event received - pub event_type: SocketEventType, - /// The value of the event. This value depends on the event received. - /// Usually nothing (zero) or a file descriptor for the monitored socket. - pub event_value: u32, - /// The address of the connection which triggered this event - pub address: String, -} - -/// Represents the types of socket events which can occur. -#[derive(Debug, Eq, PartialEq, Clone)] -pub enum SocketEventType { - Connected = 0x0001, - ConnectDelayed = 0x0002, - ConnectRetried = 0x0004, - Listening = 0x0008, - BindFailed = 0x0010, - Accepted = 0x0020, - AcceptFailed = 0x0040, - Closed = 0x0080, - CloseFailed = 0x0100, - Disconnected = 0x0200, - MonitorStopped = 0x0400, - HandshakeFailedNoDetail = 0x0800, - HandshakeSucceeded = 0x1000, - HandshakeFailedProtocol = 0x2000, - HandshakeFailedAuth = 0x4000, -} - -impl fmt::Display for SocketEventType { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", &self) - } -} - -impl TryFrom for SocketEventType { - type Error = ConnectionError; - - /// Try to convert from a u16 to a SocketEventType - fn try_from(raw: u16) -> Result { - let event = match raw { - 0x0001 => SocketEventType::Connected, - 0x0002 => SocketEventType::ConnectDelayed, - 0x0004 => SocketEventType::ConnectRetried, - 0x0008 => SocketEventType::Listening, - 0x0010 => SocketEventType::BindFailed, - 0x0020 => SocketEventType::Accepted, - 0x0040 => SocketEventType::AcceptFailed, - 0x0080 => SocketEventType::Closed, - 0x0100 => SocketEventType::CloseFailed, - 0x0200 => SocketEventType::Disconnected, - 0x0400 => SocketEventType::MonitorStopped, - 0x0800 => SocketEventType::HandshakeFailedNoDetail, - 0x1000 => SocketEventType::HandshakeSucceeded, - 0x2000 => SocketEventType::HandshakeFailedProtocol, - 0x4000 => SocketEventType::HandshakeFailedAuth, - _ => return Err(ConnectionMonitorError::SocketEventConversionFailed.into()), - }; - - Ok(event) - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn socket_event_type_try_from() { - let valid_event_types = vec![ - 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, - 0x2000, 0x4000, - ]; - - for raw_evt in valid_event_types { - let evt_type: Result = raw_evt.try_into(); - assert!(evt_type.is_ok()); - } - - let invalid: Result = 0xF000u16.try_into(); - assert!(invalid.is_err()); - } -} diff --git a/comms/src/connection/net_address/i2p.rs b/comms/src/connection/net_address/i2p.rs deleted file mode 100644 index 331788abd8..0000000000 --- a/comms/src/connection/net_address/i2p.rs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use super::{parser::AddressParser, NetAddressError}; -use serde::{Deserialize, Serialize}; -use std::{fmt, str::FromStr}; - -/// Represents an I2P address -#[derive(Clone, PartialEq, Eq, Debug, Hash, Deserialize, Serialize)] -pub struct I2PAddress { - pub name: String, -} - -impl I2PAddress { - pub fn host(&self) -> String { - format!("{}.b32.i2p", self.name) - } -} - -impl FromStr for I2PAddress { - type Err = NetAddressError; - - /// Parses a string into an `I2PAddress` - fn from_str(addr: &str) -> Result { - match AddressParser::new(addr).parse_i2p() { - Some(addr) => Ok(addr), - None => Err(NetAddressError::ParseFailed), - } - } -} - -impl fmt::Display for I2PAddress { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}.b32.i2p", self.name) - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn parse() { - let addr1 = "ukeu3k5oycgaauneqgtnvselmt4yemvoilkln7jpvamvfx7dnkdq.b32.i2p".parse::(); - assert!(addr1.is_ok(), "failed to parse valid I2P address"); - let addr1 = addr1.unwrap(); - assert_eq!("UKEU3K5OYCGAAUNEQGTNVSELMT4YEMVOILKLN7JPVAMVFX7DNKDQ", addr1.name); - - let addr2 = "UKEU3K5OYCGAAUNEQGTNVSELMT4YEMVOILKLN7JPVAMVFX7DNKDQ.b32.i2p".parse::(); - assert!(addr2.is_ok(), "failed to parse valid mixed case I2P address"); - let addr2 = addr2.unwrap(); - assert_eq!("UKEU3K5OYCGAAUNEQGTNVSELMT4YEMVOILKLN7JPVAMVFX7DNKDQ", addr2.name); - - assert_eq!(addr1, addr2); - - let addr = "invalid-address.b32.i2p".parse::(); - assert!(addr.is_err(), "successfully parsed invalid I2P address"); - - let addr = "ukeu3k5oycgaauneqgtnvselmt4yemvoilkln7jpvamvfx7dnkdq.b33.i2p".parse::(); - assert!(addr.is_err(), "successfully parsed invalid I2P address"); - - let addr = "ukeu3k5oycgaauneqgtnvselmt4yemvoilkln7jpvamvfx7dnkdq.b32.i3p".parse::(); - assert!(addr.is_err(), "successfully parsed invalid I2P address"); - } -} diff --git a/comms/src/connection/net_address/ip.rs b/comms/src/connection/net_address/ip.rs deleted file mode 100644 index 9f01b6b199..0000000000 --- a/comms/src/connection/net_address/ip.rs +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::connection::NetAddressError; -use serde::{Deserialize, Serialize}; -use std::{ - fmt, - net::{IpAddr, SocketAddr, ToSocketAddrs}, - str::FromStr, -}; - -/// Represents an {IPv4, IPv6} address and port -#[derive(Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)] -pub struct SocketAddress(SocketAddr); - -impl SocketAddress { - pub fn ip(&self) -> IpAddr { - self.0.ip() - } - - pub fn host(&self) -> String { - self.0.ip().to_string() - } - - pub fn port(&self) -> u16 { - self.0.port() - } -} - -impl FromStr for SocketAddress { - type Err = NetAddressError; - - fn from_str(addr: &str) -> Result { - let socket_addr = addr.parse::().map_err(|_| NetAddressError::ParseFailed)?; - - Ok(Self(socket_addr)) - } -} - -impl> From<(I, u16)> for SocketAddress { - fn from(v: (I, u16)) -> Self { - Self(v.into()) - } -} - -impl ToSocketAddrs for SocketAddress { - type Iter = std::option::IntoIter; - - fn to_socket_addrs(&self) -> std::io::Result { - self.0.to_socket_addrs() - } -} - -impl fmt::Display for SocketAddress { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}:{}", self.ip(), self.port()) - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn string_address_parsing() { - // Testing our implementation of address parsing which uses SocketAddr internally. - // The SocketAddr is used "as is" which means technically we're testing SocketAddr, - // however this shows the expected usage of SocketAddress without knowing about SocketAddr - - // Valid string addresses - let addr = "127.0.0.1:8000".parse::(); - assert!(addr.is_ok(), "Valid IPv4 loopback address parsing failed"); - - let addr = "[::1]:8080".parse::(); - assert!(addr.is_ok(), "Valid IPv6 loopback address parsing failed"); - - let addr = "123.122.234.100:8080".parse::(); - assert!(addr.is_ok(), "Valid IPv4 address parsing failed"); - - let addr = "[fe80::1ff:fe23:4567:890a]:8080".parse::(); - assert!(addr.is_ok(), "Valid IPv6 address parsing failed"); - - // Invalid string addresses - macro_rules! check_addr_fail { - ($addr:ident) => { - match $addr.err().unwrap() { - NetAddressError::ParseFailed => {}, - _ => panic!("Address parsing returned unexpected error type"), - } - }; - } - - let addr = "123.123.123.123".parse::(); - assert!( - addr.is_err(), - "Invalid IPv4 address was erroneously successfully parsed" - ); - check_addr_fail!(addr); - - let addr = "fe80::1ff:fe23:4567:890a:8080".parse::(); - assert!( - addr.is_err(), - "Invalid IPv6 address was erroneously successfully parsed" - ); - check_addr_fail!(addr); - } -} diff --git a/comms/src/connection/net_address/mod.rs b/comms/src/connection/net_address/mod.rs deleted file mode 100644 index 5da5ad96fc..0000000000 --- a/comms/src/connection/net_address/mod.rs +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -pub mod i2p; -pub mod ip; -pub mod net_address_with_stats; -pub mod net_addresses; -pub mod onion; -pub mod parser; - -use self::{i2p::I2PAddress, ip::SocketAddress, onion::OnionAddress}; -use crate::connection::zmq::ZmqEndpoint; -use derive_error::Error; -use serde::{Deserialize, Serialize}; -use std::{fmt, str::FromStr}; - -pub use self::{net_address_with_stats::NetAddressWithStats, net_addresses::NetAddressesWithStats}; - -#[derive(Debug, Error, PartialEq)] -pub enum NetAddressError { - /// Failed to parse address - ParseFailed, - /// Specified port range is invalid - InvalidPortRange, - /// The specified net address does not exist - AddressNotFound, - /// Empty set of net addresses - NoValidAddresses, - /// The number of connection attempts for all net addresses in the set exceeded the threshold - ConnectionAttemptsExceeded, -} - -/// A Tari network address, either IP (v4 or v6), Tor Onion or I2P. -/// -/// # Examples -/// -/// ``` -/// use tari_comms::connection::NetAddress; -/// -/// let address = "propub3r6espa33w.onion:1234".parse::(); -/// -/// assert!(address.is_ok()); -/// assert!(address.unwrap().is_tor()); -/// ``` -#[derive(Clone, PartialEq, Eq, Debug, Hash, Deserialize, Serialize)] -/// Represents an address which can be used to reach a node on the network -pub enum NetAddress { - /// IPv4 and IPv6 - IP(SocketAddress), - Onion(OnionAddress), - I2P(I2PAddress), -} - -impl NetAddress { - /// Returns true if the [`NetAddress`] is an IP address, otherwise false - pub fn is_ip(&self) -> bool { - match *self { - NetAddress::IP(_) => true, - _ => false, - } - } - - /// Returns true if the [`NetAddress`] is a Tor Onion address, otherwise false - pub fn is_tor(&self) -> bool { - match *self { - NetAddress::Onion(_) => true, - _ => false, - } - } - - /// Returns true if the [`NetAddress`] is an I2P address, otherwise false - pub fn is_i2p(&self) -> bool { - match *self { - NetAddress::I2P(_) => true, - _ => false, - } - } - - pub fn host(&self) -> String { - match self { - NetAddress::Onion(addr) => addr.host(), - NetAddress::IP(addr) => addr.host(), - NetAddress::I2P(addr) => addr.host(), - } - } - - /// Returns the port for the NetAddress if applicable, otherwise None - pub fn maybe_port(&self) -> Option { - match self { - NetAddress::Onion(addr) => Some(addr.port()), - NetAddress::IP(addr) => Some(addr.port()), - NetAddress::I2P(_) => None, - } - } -} - -impl FromStr for NetAddress { - type Err = NetAddressError; - - /// Parses a [`str`] into a [`NetAddress`] - fn from_str(address: &str) -> Result { - if let Ok(addr) = address.parse::() { - Ok(addr.into()) - } else if let Ok(addr) = address.parse::() { - Ok(addr.into()) - } else if let Ok(addr) = address.parse::() { - Ok(addr.into()) - } else { - Err(NetAddressError::ParseFailed) - } - } -} - -impl fmt::Display for NetAddress { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use NetAddress::*; - - match *self { - IP(ref addr) => write!(f, "{}", addr), - Onion(ref addr) => write!(f, "{}", addr), - I2P(ref addr) => write!(f, "{}", addr), - } - } -} - -impl From for NetAddress { - /// Converts a [`SocketAddress`] into a [`NetAddress::IP`]. - fn from(addr: SocketAddress) -> Self { - NetAddress::IP(addr) - } -} - -impl From for NetAddress { - /// Converts a [`OnionAddress`] into a [`NetAddress::Tor`]. - fn from(addr: OnionAddress) -> Self { - NetAddress::Onion(addr) - } -} - -impl From for NetAddress { - /// Converts a [`I2PAddress`] into a [`NetAddress::I2P`]. - fn from(addr: I2PAddress) -> Self { - NetAddress::I2P(addr) - } -} - -impl ZmqEndpoint for NetAddress { - fn to_zmq_endpoint(&self) -> String { - match *self { - NetAddress::IP(ref addr) => format!("tcp://{}:{}", addr.ip(), addr.port()), - NetAddress::Onion(ref addr) => format!("tcp://{}:{}", addr.public_key, addr.port), - // TODO: need to confirm this works - NetAddress::I2P(ref addr) => format!("tcp://{}.b32.i2p", addr.name), - } - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn address_parsing() { - // Valid string addresses - let addr = "123.0.0.123:8000".parse::(); - assert!(addr.is_ok(), "Valid IPv4 loopback address parsing failed"); - - let addr = "[::1]:8080".parse::(); - assert!(addr.is_ok(), "Valid IPv6 loopback address parsing failed"); - - let addr = "propub3r6espa33w.onion:1234".parse::(); - assert!(addr.is_ok(), "Valid Tor Onion address parsing failed"); - - let addr = "ukeu3k5oycgaauneqgtnvselmt4yemvoilkln7jpvamvfx7dnkdq.b32.i2p".parse::(); - assert!(addr.is_ok(), "Valid I2P address parsing failed"); - - let addr = "google.com:1234".parse::(); - assert!( - addr.is_err(), - "Invalid net address string should not have successfully parsed" - ); - } -} diff --git a/comms/src/connection/net_address/net_addresses.rs b/comms/src/connection/net_address/net_addresses.rs deleted file mode 100644 index caf2857a92..0000000000 --- a/comms/src/connection/net_address/net_addresses.rs +++ /dev/null @@ -1,382 +0,0 @@ -use crate::connection::{ - net_address::{net_address_with_stats::NetAddressWithStats, NetAddressError}, - NetAddress, -}; -use chrono::prelude::*; -use serde::{Deserialize, Serialize}; -use std::{ops::Index, time::Duration}; - -pub const MAX_CONNECTION_ATTEMPTS: u32 = 3; - -/// This struct is used to store a set of different net addresses such as IPv4, IPv6, Tor or I2P for a single peer. -#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)] -pub struct NetAddressesWithStats { - pub addresses: Vec, - last_attempted: Option>, -} - -impl NetAddressesWithStats { - /// Constructs a new list of addresses with usage stats from a list of net addresses - pub fn new(addresses: Vec) -> NetAddressesWithStats { - NetAddressesWithStats { - addresses, - last_attempted: None, - } - } - - /// Finds the specified address in the set and allow updating of its variables such as its usage stats - pub fn find_address_mut(&mut self, address: &NetAddress) -> Result<&mut NetAddressWithStats, NetAddressError> { - for (i, curr_address) in &mut self.addresses.iter().enumerate() { - if curr_address.net_address == *address { - return self.addresses.get_mut(i).ok_or(NetAddressError::AddressNotFound); - } - } - Err(NetAddressError::AddressNotFound) - } - - /// Provides the date and time of the last successful communication with this peer - pub fn last_seen(&self) -> Option> { - let mut latest_valid_datetime: Option> = None; - for curr_address in &self.addresses { - if curr_address.last_seen.is_none() { - continue; - } - match latest_valid_datetime { - Some(latest_datetime) => { - if latest_datetime < curr_address.last_seen.unwrap() { - latest_valid_datetime = curr_address.last_seen; - } - }, - None => latest_valid_datetime = curr_address.last_seen, - } - } - latest_valid_datetime - } - - /// Return the time of last attempted connection to this collection of addresses - pub fn last_attempted(&self) -> Option> { - self.last_attempted - } - - /// Adds a new net address to the peer. This function will not add a duplicate if the address - /// already exists. - pub fn add_net_address(&mut self, net_address: &NetAddress) { - if !self.addresses.iter().any(|x| x.net_address == *net_address) { - self.addresses.push(net_address.clone().into()); - } - } - - /// Compares the existing set of net_addresses to the provided net_address set and remove missing net_addresses and - /// add new net_addresses without discarding the usage stats of the existing and remaining net_addresses. - pub fn update_net_addresses(&mut self, net_addresses: Vec) { - // Remove missing elements - let mut remove_indices: Vec = Vec::new(); - for index in 0..self.addresses.len() { - if !net_addresses - .iter() - .any(|new_net_address| *new_net_address == self.addresses[index].net_address) - { - remove_indices.push(index); - } - } - for index in remove_indices.iter().rev() { - self.addresses.remove(*index); - } - // Add new elements - for new_net_address in &net_addresses { - if !self - .addresses - .iter() - .any(|curr_net_address| curr_net_address.net_address == *new_net_address) - { - self.add_net_address(new_net_address); - } - } - } - - /// Finds and returns the highest priority net address until all connection attempts for each net address have been - /// reached. Should the node fail to connect to the address, the address should be marked as such - /// using `mark_failed_connection_attempt`. If a maximum number of attempts is reached, for all addresses - /// a `NetAddressError::ConnectionAttemptsExceeded` error is returned. - pub fn get_best_net_address(&mut self) -> Result { - if !self.addresses.is_empty() { - let any_reachable = self - .addresses - .iter() - .any(|a| a.connection_attempts < MAX_CONNECTION_ATTEMPTS); - if any_reachable { - self.addresses.sort(); - Ok(self.addresses[0].net_address.clone()) - } else { - Err(NetAddressError::ConnectionAttemptsExceeded) - } - } else { - Err(NetAddressError::NoValidAddresses) - } - } - - /// The average connection latency of the provided net address will be updated to include the current measured - /// latency sample - pub fn update_latency( - &mut self, - address: &NetAddress, - latency_measurement: Duration, - ) -> Result<(), NetAddressError> - { - let updatable_address = self.find_address_mut(address)?; - updatable_address.update_latency(latency_measurement); - Ok(()) - } - - /// Mark that a message was received from the specified net address - pub fn mark_message_received(&mut self, address: &NetAddress) -> Result<(), NetAddressError> { - let updatable_address = self.find_address_mut(address)?; - updatable_address.mark_message_received(); - Ok(()) - } - - /// Mark that a rejected message was received from the specified net address - pub fn mark_message_rejected(&mut self, address: &NetAddress) -> Result<(), NetAddressError> { - let updatable_address = self.find_address_mut(address)?; - updatable_address.mark_message_rejected(); - Ok(()) - } - - /// Mark that a successful connection was established with the specified net address - pub fn mark_successful_connection_attempt(&mut self, address: &NetAddress) -> Result<(), NetAddressError> { - let updatable_address = self.find_address_mut(address)?; - updatable_address.mark_successful_connection_attempt(); - self.last_attempted = Some(Utc::now()); - Ok(()) - } - - /// Mark that a connection could not be established with the specified net address - pub fn mark_failed_connection_attempt(&mut self, address: &NetAddress) -> Result<(), NetAddressError> { - let updatable_address = self.find_address_mut(address)?; - updatable_address.mark_failed_connection_attempt(); - self.last_attempted = Some(Utc::now()); - Ok(()) - } - - /// Reset the connection attempts stat on all of this Peers net addresses to retry connection - pub fn reset_connection_attempts(&mut self) { - for a in self.addresses.iter_mut() { - a.reset_connection_attempts(); - } - } - - /// Returns the number of addresses - pub fn len(&self) -> usize { - self.addresses.len() - } - - /// Returns if there are addresses or not - pub fn is_empty(&self) -> bool { - self.addresses.is_empty() - } -} - -impl Index for NetAddressesWithStats { - type Output = NetAddressWithStats; - - /// Returns the NetAddressWithStats at the given index - fn index(&self, index: usize) -> &Self::Output { - &self.addresses[index] - } -} - -impl From for NetAddressesWithStats { - /// Constructs a new list of addresses with usage stats from a single net address - fn from(net_address: NetAddress) -> Self { - NetAddressesWithStats { - addresses: vec![NetAddressWithStats::from(net_address)], - last_attempted: None, - } - } -} - -impl From> for NetAddressesWithStats { - /// Constructs a new list of addresses with usage stats from a Vec - fn from(net_addresses: Vec) -> Self { - NetAddressesWithStats { - addresses: net_addresses - .into_iter() - .map(NetAddressWithStats::from) - .collect::>(), - last_attempted: None, - } - } -} - -impl From> for NetAddressesWithStats { - /// Constructs NetAddressesWithStats from a list of addresses with usage stats - fn from(addresses: Vec) -> Self { - NetAddressesWithStats { - addresses, - last_attempted: None, - } - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::connection::{ - net_address::{net_address_with_stats::NetAddressWithStats, net_addresses::NetAddressesWithStats}, - NetAddress, - }; - use std::thread; - - #[test] - fn test_index_impl() { - let net_address1 = "123.0.0.123:8000".parse::().unwrap(); - let net_address2 = "125.1.54.254:7999".parse::().unwrap(); - let net_address3 = "175.6.3.145:8000".parse::().unwrap(); - let net_addresses: NetAddressesWithStats = - vec![net_address1.clone(), net_address2.clone(), net_address3.clone()].into(); - - assert_eq!(net_addresses[0].net_address, net_address1); - assert_eq!(net_addresses[1].net_address, net_address2); - assert_eq!(net_addresses[2].net_address, net_address3); - } - - #[test] - fn test_last_seen() { - let net_address1 = "123.0.0.123:8000".parse::().unwrap(); - let net_address2 = "125.1.54.254:7999".parse::().unwrap(); - let net_address3 = "175.6.3.145:8000".parse::().unwrap(); - let mut net_addresses = NetAddressesWithStats::from(net_address1.clone()); - net_addresses.add_net_address(&net_address2); - net_addresses.add_net_address(&net_address3); - - assert!(net_addresses.mark_successful_connection_attempt(&net_address3).is_ok()); - assert!(net_addresses.mark_successful_connection_attempt(&net_address1).is_ok()); - assert!(net_addresses.mark_successful_connection_attempt(&net_address2).is_ok()); - let desired_last_seen = net_addresses.addresses[1].last_seen; - let last_seen = net_addresses.last_seen(); - assert!(desired_last_seen.is_some()); - assert!(last_seen.is_some()); - assert_eq!(desired_last_seen.unwrap(), last_seen.unwrap()); - } - - #[test] - fn test_add_net_address() { - let net_address1 = "123.0.0.123:8000".parse::().unwrap(); - let net_address2 = "125.1.54.254:7999".parse::().unwrap(); - let net_address3 = "175.6.3.145:8000".parse::().unwrap(); - let mut net_addresses = NetAddressesWithStats::from(net_address1.clone()); - net_addresses.add_net_address(&net_address2); - net_addresses.add_net_address(&net_address3); - // Add duplicate address, test add_net_address is idempotent - net_addresses.add_net_address(&net_address2); - assert_eq!(net_addresses.addresses.len(), 3); - assert_eq!(net_addresses.addresses[0].net_address, net_address1); - assert_eq!(net_addresses.addresses[1].net_address, net_address2); - assert_eq!(net_addresses.addresses[2].net_address, net_address3); - } - - #[test] - fn test_get_net_address() { - let net_address1 = "123.0.0.123:8000".parse::().unwrap(); - let net_address2 = "125.1.54.254:7999".parse::().unwrap(); - let net_address3 = "175.6.3.145:8000".parse::().unwrap(); - let mut net_addresses = NetAddressesWithStats::from(net_address1.clone()); - net_addresses.add_net_address(&net_address2); - net_addresses.add_net_address(&net_address3); - - let mut priority_address = net_addresses.get_best_net_address(); - assert!(priority_address.is_ok()); - assert_eq!(priority_address.unwrap(), net_address1); - - assert!(net_addresses - .update_latency(&net_address1, Duration::from_millis(250)) - .is_ok()); - assert!(net_addresses - .update_latency(&net_address2, Duration::from_millis(50)) - .is_ok()); - assert!(net_addresses - .update_latency(&net_address3, Duration::from_millis(100)) - .is_ok()); - priority_address = net_addresses.get_best_net_address(); - assert!(priority_address.is_ok()); - assert_eq!(priority_address.unwrap(), net_address2); - - assert!(net_addresses.mark_failed_connection_attempt(&net_address2).is_ok()); - priority_address = net_addresses.get_best_net_address(); - assert!(priority_address.is_ok()); - assert_eq!(priority_address.unwrap(), net_address3); - - for _i in 0..MAX_CONNECTION_ATTEMPTS { - assert!(net_addresses.mark_failed_connection_attempt(&net_address1).is_ok()); - assert!(net_addresses.mark_failed_connection_attempt(&net_address2).is_ok()); - assert!(net_addresses.mark_failed_connection_attempt(&net_address3).is_ok()); - } - assert!(net_addresses.get_best_net_address().is_err()); - } - - #[test] - fn test_stats_updates_on_addresses() { - let net_address1 = "123.0.0.123:8000".parse::().unwrap(); - let net_address2 = "125.1.54.254:7999".parse::().unwrap(); - let net_address3 = "175.6.3.145:8000".parse::().unwrap(); - let mut addresses: Vec = Vec::new(); - addresses.push(NetAddressWithStats::from(net_address1.clone())); - addresses.push(NetAddressWithStats::from(net_address2.clone())); - addresses.push(NetAddressWithStats::from(net_address3.clone())); - let mut net_addresses = NetAddressesWithStats::new(addresses); - - assert!(net_addresses - .update_latency(&net_address2, Duration::from_millis(200)) - .is_ok()); - assert_eq!(net_addresses.addresses[0].avg_latency, Duration::from_millis(0)); - assert_eq!(net_addresses.addresses[1].avg_latency, Duration::from_millis(200)); - assert_eq!(net_addresses.addresses[2].avg_latency, Duration::from_millis(0)); - - thread::sleep(Duration::from_millis(1)); - assert!(net_addresses.mark_message_received(&net_address1).is_ok()); - assert!(net_addresses.addresses[0].last_seen.is_some()); - assert!(net_addresses.addresses[1].last_seen.is_some()); - assert!(net_addresses.addresses[2].last_seen.is_none()); - assert!(net_addresses.addresses[0].last_seen.unwrap() > net_addresses.addresses[1].last_seen.unwrap()); - - assert!(net_addresses.mark_message_rejected(&net_address2).is_ok()); - assert!(net_addresses.mark_message_rejected(&net_address3).is_ok()); - assert!(net_addresses.mark_message_rejected(&net_address3).is_ok()); - assert_eq!(net_addresses.addresses[0].rejected_message_count, 0); - assert_eq!(net_addresses.addresses[1].rejected_message_count, 1); - assert_eq!(net_addresses.addresses[2].rejected_message_count, 2); - - assert!(net_addresses.mark_failed_connection_attempt(&net_address1).is_ok()); - assert!(net_addresses.mark_failed_connection_attempt(&net_address2).is_ok()); - assert!(net_addresses.mark_failed_connection_attempt(&net_address3).is_ok()); - assert!(net_addresses.mark_failed_connection_attempt(&net_address1).is_ok()); - assert!(net_addresses.mark_successful_connection_attempt(&net_address2).is_ok()); - assert_eq!(net_addresses.addresses[0].connection_attempts, 2); - assert_eq!(net_addresses.addresses[1].connection_attempts, 0); - assert_eq!(net_addresses.addresses[2].connection_attempts, 1); - } - - #[test] - fn test_resetting_all_connection_attempts() { - let net_address1 = "123.0.0.123:8000".parse::().unwrap(); - let net_address2 = "125.1.54.254:7999".parse::().unwrap(); - let net_address3 = "175.6.3.145:8000".parse::().unwrap(); - let mut addresses: Vec = Vec::new(); - addresses.push(NetAddressWithStats::from(net_address1.clone())); - addresses.push(NetAddressWithStats::from(net_address2.clone())); - addresses.push(NetAddressWithStats::from(net_address3.clone())); - let mut net_addresses = NetAddressesWithStats::new(addresses); - assert!(net_addresses.mark_failed_connection_attempt(&net_address1).is_ok()); - assert!(net_addresses.mark_failed_connection_attempt(&net_address2).is_ok()); - assert!(net_addresses.mark_failed_connection_attempt(&net_address3).is_ok()); - assert!(net_addresses.mark_failed_connection_attempt(&net_address1).is_ok()); - - assert_eq!(net_addresses.addresses[0].connection_attempts, 2); - assert_eq!(net_addresses.addresses[1].connection_attempts, 1); - assert_eq!(net_addresses.addresses[2].connection_attempts, 1); - net_addresses.reset_connection_attempts(); - assert_eq!(net_addresses.addresses[0].connection_attempts, 0); - assert_eq!(net_addresses.addresses[1].connection_attempts, 0); - assert_eq!(net_addresses.addresses[2].connection_attempts, 0); - } -} diff --git a/comms/src/connection/net_address/onion.rs b/comms/src/connection/net_address/onion.rs deleted file mode 100644 index 4c479d6d97..0000000000 --- a/comms/src/connection/net_address/onion.rs +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use std::{fmt, str::FromStr}; - -use serde::{Deserialize, Serialize}; - -use super::{parser::AddressParser, NetAddressError}; - -/// Represents a Tor Onion address -#[derive(Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)] -pub struct OnionAddress { - pub public_key: String, - pub port: u16, -} - -impl OnionAddress { - pub fn host(&self) -> String { - format!("{}.onion", self.public_key) - } - - pub fn port(&self) -> u16 { - self.port - } -} - -impl FromStr for OnionAddress { - type Err = NetAddressError; - - /// String parsing to an `OnionAddress` - fn from_str(addr: &str) -> Result { - match AddressParser::new(addr).parse_onion() { - Some(a) => Ok(a), - None => Err(NetAddressError::ParseFailed), - } - } -} - -impl fmt::Display for OnionAddress { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}.onion:{}", self.public_key, self.port) - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn parse() { - let addr1 = "nqblqa3x7ddnkp664cowka6jx4mlc26vpgdksj6uya2kbyvi77aqpqqd.onion:1234".parse::(); - assert!(addr1.is_ok(), "failed to parse valid onion address"); - let addr1 = addr1.unwrap(); - assert_eq!( - "NQBLQA3X7DDNKP664COWKA6JX4MLC26VPGDKSJ6UYA2KBYVI77AQPQQD", - addr1.public_key - ); - assert_eq!(1234, addr1.port); - - let addr2 = "nqblqa3x7ddnkP664COWKA6JX4MLC26VPGDKSJ6UYA2KBYVI77AQPQQD.onion:1234".parse::(); - assert!(addr2.is_ok(), "failed to parse valid mixed case onion address"); - let addr2 = addr2.unwrap(); - assert_eq!( - "NQBLQA3X7DDNKP664COWKA6JX4MLC26VPGDKSJ6UYA2KBYVI77AQPQQD", - addr2.public_key - ); - assert_eq!(1234, addr2.port); - - assert_eq!(addr1, addr2); - - let addr = "propub3r6espa33w.onion:32123".parse::(); - assert!(addr.is_ok(), "failed to parse valid onion address"); - let addr = addr.unwrap(); - assert_eq!("PROPUB3R6ESPA33W", addr.public_key); - assert_eq!(32123, addr.port); - - let addr = "".parse::(); - assert!(addr.is_err(), "erroneously parsed a blank string"); - - let addr = "文字漢字漢字字字字字字字字字字字.onion:2020".parse::(); - assert!(addr.is_err(), "erroneously parsed an invalid string"); - - let addr = "propub3r6espa33wx.onion:32123".parse::(); - assert!(addr.is_err(), "erroneously parsed invalid onion address"); - - let addr = "propub3r6espa33w.onjon:32123".parse::(); - assert!(addr.is_err(), "erroneously parsed invalid onion address"); - - let addr = "propub3r6espa33w.onion:99999".parse::(); - assert!(addr.is_err(), "erroneously parsed invalid onion address"); - } -} diff --git a/comms/src/connection/net_address/parser.rs b/comms/src/connection/net_address/parser.rs deleted file mode 100644 index 86573688c0..0000000000 --- a/comms/src/connection/net_address/parser.rs +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use super::{I2PAddress, OnionAddress}; - -/// Provides simple parsing functionality for address strings. -/// Currently, it contains parsing implementations for Onion and I2P. -pub(crate) struct AddressParser<'a> { - pos: usize, - data: &'a [u8], -} - -impl<'a> AddressParser<'a> { - /// Create a new address parser - pub fn new(s: &'a str) -> Self { - AddressParser { - pos: 0, - data: s.as_bytes(), - } - } - - /// Parse I2P address - pub fn parse_i2p(&mut self) -> Option { - self.read_atomic(|p| { - let name = match p.read_base32_string() { - Some(n) => { - if n.len() != 52 { - return None; - } - n - }, - None => return None, - }; - - match p.read_until_end() { - Some(s) => { - if s.to_ascii_lowercase() != b".b32.i2p" { - return None; - } - }, - None => return None, - } - - Some(I2PAddress { name }) - }) - } - - /// Parse Onion address - pub fn parse_onion(&mut self) -> Option { - self.read_atomic(|p| { - let public_key = match p.read_base32_string() { - Some(pk) => { - // Valid onion address lengths - if pk.len() != 16 && pk.len() != 56 { - return None; - } - - pk - }, - _ => return None, - }; - - match p.read_until_char(':') { - Some(buf) => { - if buf.to_ascii_lowercase() != b".onion" { - return None; - } - }, - None => return None, - } - - p.consume_char(':')?; - - let port = match p.read_number() { - Some(p) => p, - None => return None, - }; - - if port > u64::from(std::u16::MAX) { - return None; - } - - Some(OnionAddress { - public_key, - port: port as u16, - }) - }) - } - - fn is_base32_char(&self, ch: char) -> bool { - if ch >= 'A' && ch <= 'Z' { - return true; - } - - if ch >= '2' && ch <= '7' { - return true; - } - - false - } - - fn read_base32_string(&mut self) -> Option { - let mut buf = vec![]; - while self.pos < self.data.len() { - let ch = self.data[self.pos].to_ascii_uppercase(); - if !self.is_base32_char(ch as char) { - break; - } - buf.push(ch); - self.pos += 1; - } - - if !buf.is_empty() { - match String::from_utf8(buf) { - Ok(s) => Some(s), - Err(_) => None, - } - } else { - None - } - } - - fn read_char(&mut self) -> Option { - if self.is_end() { - None - } else { - let ch = self.data[self.pos]; - self.pos += 1; - Some(ch as char) - } - } - - fn is_end(&self) -> bool { - self.pos == self.data.len() - } - - fn consume_char(&mut self, ch: char) -> Option { - self.read_char().and_then(|c| if c == ch { Some(ch) } else { None }) - } - - fn read_number(&mut self) -> Option { - let mut pos = self.pos; - let mut number = 0u64; - while pos < self.data.len() { - let ch = self.data[pos]; - - if ch < b'0' || ch > b'9' { - break; - } - number = number * 10u64 + u64::from(ch - b'0'); - pos += 1; - } - - if pos == self.pos { - None - } else { - self.pos = pos; - Some(number) - } - } - - fn read_until_end(&mut self) -> Option> { - let buf = &self.data[self.pos..]; - self.pos = self.data.len() - 1; - Some(buf.to_vec()) - } - - fn read_until_char(&mut self, ch: char) -> Option> { - let mut pos = self.pos; - let mut buf = vec![]; - while pos < self.data.len() { - if self.data[pos] == (ch as u8) { - self.pos = pos; - return Some(buf); - } - buf.push(self.data[pos]); - pos += 1; - } - - None - } - - fn read_atomic(&mut self, f: F) -> Option - where F: FnOnce(&mut Self) -> Option { - let pos = self.pos; - let result = f(self); - if result.is_none() { - self.pos = pos; - } - result - } -} diff --git a/comms/src/connection/peer_connection/connection.rs b/comms/src/connection/peer_connection/connection.rs deleted file mode 100644 index 43e556f763..0000000000 --- a/comms/src/connection/peer_connection/connection.rs +++ /dev/null @@ -1,839 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use super::{ - control::{ControlMessage, ThreadControlMessenger}, - worker::PeerConnectionWorker, - PeerConnectionContext, - PeerConnectionError, -}; -use crate::{ - connection::{ - net_address::ip::SocketAddress, - types::{Linger, Result}, - ConnectionError, - Direction, - NetAddress, - }, - message::FrameSet, -}; -use chrono::{NaiveDateTime, Utc}; -use std::{ - fmt, - sync::{Arc, RwLock}, - thread::{self, JoinHandle}, - time::Duration, -}; -use tari_utilities::hex::to_hex; - -/// Represents messages that must be sent to a PeerConnection. -pub enum PeerConnectionProtoMessage { - /// Sent to establish the identity frame for a PeerConnection. This must be sent by an - /// Outbound connection to an Inbound connection before any other communication occurs. - Identify = 0, - /// A peer message to be forwarded to the message sink (the IMS) - Message = 1, - /// Any other message is invalid and is discarded - Invalid, -} - -impl From for PeerConnectionProtoMessage { - fn from(val: u8) -> Self { - match val { - 0 => PeerConnectionProtoMessage::Identify, - 1 => PeerConnectionProtoMessage::Message, - _ => PeerConnectionProtoMessage::Invalid, - } - } -} - -/// Represents the ID of a PeerConnection. This is sent as the first frame -/// to the message sink on the peer connection. -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct ConnectionId(Vec); - -impl ConnectionId { - pub fn new(id: Vec) -> Self { - Self(id) - } - - pub fn into_inner(self) -> Vec { - self.0 - } - - pub fn as_bytes(&self) -> &[u8] { - self.0.as_slice() - } - - /// Returns a shortened (length of 8 or less) connection ID - /// This would typically be used for display purposes when the connection ID is a - /// sufficiently large random value and you don't want to have large strings displayed. - pub fn to_short_id(&self) -> Self { - let start = match self.0.len().checked_sub(8) { - Some(s) => s, - None => self.0.len(), - }; - Self(self.0[start..].to_vec()) - } -} - -impl Default for ConnectionId { - fn default() -> Self { - Self(Default::default()) - } -} - -impl PartialEq for Vec { - fn eq(&self, other: &ConnectionId) -> bool { - self == &other.0 - } -} - -impl From> for ConnectionId { - fn from(bytes: Vec) -> Self { - Self(bytes) - } -} - -impl From<&[u8]> for ConnectionId { - fn from(bytes: &[u8]) -> Self { - Self(bytes.to_vec()) - } -} - -impl From<&str> for ConnectionId { - fn from(bytes: &str) -> Self { - Self(bytes.as_bytes().to_vec()) - } -} - -impl AsRef<[u8]> for ConnectionId { - fn as_ref(&self) -> &[u8] { - self.0.as_ref() - } -} - -impl fmt::Display for ConnectionId { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", to_hex(self.as_bytes())) - } -} - -pub struct ConnectionInfo { - pub(super) control_messenger: Arc, - pub(super) connected_address: Option, -} - -/// The state of the PeerConnection -#[derive(Clone)] -pub(super) enum PeerConnectionState { - /// The connection object has been created but is not connected - Initial, - /// The connection thread is running, but the connection has not been accepted - Connecting(Arc), - /// The inbound connection is listening for connections - Listening(Arc), - /// The connection thread is running, and has been accepted. - Connected(Arc), - /// The connection has been shut down (node disconnected) - Shutdown, - /// The remote peer has disconnected - Disconnected, - /// Peer connection runner failed - Failed(PeerConnectionError), -} - -impl Default for PeerConnectionState { - fn default() -> Self { - PeerConnectionState::Initial - } -} - -macro_rules! is_state { - ($name: ident, $($e: pat)|*) => { - pub fn $name(&self) -> bool { - use PeerConnectionState::*; - let lock = acquire_read_lock!(self.state); - match *lock { - $($e)|* => true, - _ => false, - } - } - }; -} - -/// Basic stats for peer connections. PeerConnectionStats are updated by the [PeerConnectionWorker] -/// and read by the [PeerConnection]. -/// -/// [PeerConnectionWorker](../worker/struct.PeerConnectionWorker.html) -/// [PeerConnection](./struct.PeerConnection.html) -#[derive(Clone, Debug)] -pub struct PeerConnectionStats { - last_activity: NaiveDateTime, - messages_sent: usize, - messages_recv: usize, -} - -impl PeerConnectionStats { - pub fn new() -> Self { - Default::default() - } - - pub fn incr_message_recv(&mut self) { - self.messages_recv += 1; - self.last_activity = Utc::now().naive_utc(); - } - - pub fn incr_message_sent(&mut self) { - self.messages_sent += 1; - self.last_activity = Utc::now().naive_utc(); - } - - pub fn messages_sent(&self) -> usize { - self.messages_sent - } - - pub fn messages_recv(&self) -> usize { - self.messages_recv - } - - pub fn last_activity(&self) -> &NaiveDateTime { - &self.last_activity - } -} - -impl Default for PeerConnectionStats { - fn default() -> Self { - Self { - last_activity: Utc::now().naive_local(), - messages_sent: 0, - messages_recv: 0, - } - } -} - -/// Represents an asynchonous bi-directional connection to a Peer. -/// A PeerConnectionContext must be given to start the underlying thread -/// This may be easily shared and cloned across threads -/// -/// # Fields -/// -/// `state` - current state of the thread -/// -/// # Example -/// -/// ```edition2018 -/// -/// # use tari_comms::connection::*; -/// # use std::time::Duration; -/// -/// let ctx = ZmqContext::new(); -/// let addr: NetAddress = "127.0.0.1:8080".parse().unwrap(); -/// -/// let peer_context = PeerConnectionContextBuilder::new() -/// .set_id("123") -/// .set_context(&ctx) -/// .set_direction(Direction::Outbound) -/// .set_message_sink_address(InprocAddress::random()) -/// .set_address(addr.clone()) -/// .build() -/// .unwrap(); -/// -/// let mut conn = PeerConnection::new(); -/// -/// assert!(!conn.is_connected()); -/// // Start the peer connection worker thread -/// conn.start(peer_context).unwrap(); -/// // Wait for connection -/// // This will never connect because there is nothing -/// // listening on the other end -/// match conn.wait_connected_or_failure(&Duration::from_millis(100)) { -/// Ok(()) => { -/// assert!(conn.is_connected()); -/// println!("Able to establish connection on {}", addr); -/// } -/// Err(err) => { -/// assert!(!conn.is_connected()); -/// println!("Failed to connect to {} after 100ms (may still be trying if err is Timeout). Error: {:?}", addr, err); -/// } -/// } -/// ``` -#[derive(Default, Clone)] -pub struct PeerConnection { - state: Arc>, - connection_stats: Arc>, - direction: Option, - peer_address: Option, -} - -impl PeerConnection { - /// Returns true if the PeerConnection is in an `Initial` state, otherwise false - is_state!(is_initial, Initial); - - /// Returns true if the PeerConnection is in a `Connected` state, otherwise false - is_state!(is_connected, Connected(_)); - - /// Returns true if the PeerConnection is in a `Shutdown` state, otherwise false - is_state!(is_shutdown, Shutdown); - - /// Returns true if the PeerConnection is in a `Listening` state, otherwise false - is_state!(is_listening, Listening(_)); - - /// Returns true if the PeerConnection is in a `Disconnected`/`Shutdown`/`Failed` state, otherwise false - is_state!(is_disconnected, Disconnected | Shutdown | Failed(_)); - - /// Returns true if the PeerConnection is in a `Failed` state, otherwise false - is_state!(is_failed, Failed(_)); - - /// Returns true if the PeerConnection is in a `Connecting`, `Listening` or `Connected` state, otherwise false - is_state!(is_active, Connecting(_) | Connected(_) | Listening(_)); - - /// Create a new PeerConnection - pub fn new() -> Self { - Default::default() - } - - /// Start the worker thread for the PeerConnection and transition the - /// state to PeerConnectionState::Connected. The PeerConnection now - /// has a ThreadMessenger which is used to send ControlMessages to the - /// underlying thread. - /// - /// # Arguments - /// - /// `context` - The PeerConnectionContext which is owned by the underlying thread - pub fn start(&mut self, context: PeerConnectionContext) -> Result>> { - self.direction = Some(context.direction.clone()); - self.peer_address = Some(context.peer_address.clone()); - - let worker = PeerConnectionWorker::new(context, self.state.clone(), self.connection_stats.clone()); - let handle = worker.spawn()?; - Ok(handle) - } - - /// Tell the underlying thread to shut down. The connection will not immediately - /// be in a `Shutdown` state. [wait_shutdown] can be used to wait for the - /// connection to shut down. If the connection is not active, this method does nothing. - pub fn shutdown(&self) -> Result<()> { - match self.send_control_message(ControlMessage::Shutdown) { - // StateError only returns from send_control_message - // if the connection worker is not active - Ok(_) | Err(ConnectionError::PeerError(PeerConnectionError::StateError(_))) => Ok(()), - e => e, - } - } - - /// Send frames to the connected Peer. An Err will be returned if the - /// connection is not in a Connected state. - /// - /// # Arguments - /// - /// `frames` - The frames to send - pub fn send(&self, frames: FrameSet) -> Result<()> { - self.send_control_message(ControlMessage::SendMsg(frames)) - } - - /// Set the linger for the connection - /// - /// # Arguments - /// - /// `linger` - The Linger to set - pub fn set_linger(&self, linger: Linger) -> Result<()> { - self.send_control_message(ControlMessage::SetLinger(linger)) - } - - /// Temporarily suspend messages from being processed and forwarded to the consumer. - /// Pending messages will be buffered until reaching the receive HWM. Once resumed, - /// buffered messages will be released to the consumer. - /// An Err will be returned if the connection is not in a Connected state. - pub fn pause(&self) -> Result<()> { - self.send_control_message(ControlMessage::Pause) - } - - /// Unpause the connection and resume message processing from the peer. - /// An Err will be returned if the connection is not in a Connected state. - pub fn resume(&self) -> Result<()> { - self.send_control_message(ControlMessage::Resume) - } - - /// Return the actual address this connection is bound to. If the connection is not over a TCP socket, or the - /// connection state is not Connected, this function returns None - pub fn get_connected_address(&self) -> Option { - let lock = acquire_read_lock!(self.state); - match &*lock { - PeerConnectionState::Listening(info) | PeerConnectionState::Connected(info) => { - info.connected_address.clone() - }, - _ => None, - } - } - - /// Return the actual address this connection is bound to. If the connection state is not Connected, - /// this function returns None - pub fn get_address(&self) -> Option { - let lock = acquire_read_lock!(self.state); - match &*lock { - PeerConnectionState::Listening(info) | PeerConnectionState::Connected(info) => info - .connected_address - .clone() - .map_or(self.peer_address.clone(), |addr| Some(addr.into())), - _ => None, - } - } - - /// Returns a snapshot of latest connection stats from this peer connection - pub fn connection_stats(&self) -> PeerConnectionStats { - acquire_read_lock!(self.connection_stats).clone() - } - - /// Returns the last time this connection sent or received a message - pub fn last_activity(&self) -> NaiveDateTime { - *acquire_read_lock!(self.connection_stats).last_activity() - } - - /// Send control message to the ThreadControlMessenger. - /// Will return an error if the connection is not in an active state. - /// - /// # Arguments - /// - /// `msg` - The ControlMessage to send - fn send_control_message(&self, msg: ControlMessage) -> Result<()> { - use PeerConnectionState::*; - let lock = acquire_read_lock!(self.state); - match &*lock { - Connecting(ref thread_ctl) => thread_ctl.send(msg), - Listening(ref info) => info.control_messenger.send(msg), - Connected(ref info) => info.control_messenger.send(msg), - state => Err(PeerConnectionError::StateError(format!( - "Attempt to retrieve thread messenger on peer connection with state '{}'", - PeerConnectionSimpleState::from(state) - )) - .into()), - } - } - - /// Blocks the current thread until the connection is in a `Connected` state (returning `Ok`), - /// the timeout has been reached (returning `Err(ConnectionError::Timeout)`), or the connection - /// is in a `Failed` state (returning the error which caused the failure) - pub fn wait_listening_or_failure(&self, until: &Duration) -> Result<()> { - match self.get_direction() { - Some(direction) => { - if *direction == Direction::Outbound { - return Err(ConnectionError::InvalidOperation( - "Call to wait_listening_or_failure on Outbound connection".to_string(), - )); - } - }, - None => { - return Err(ConnectionError::InvalidOperation( - "Call to wait_listening_or_failure before peer connection has started".to_string(), - )); - }, - } - self.wait_until(until, || !self.is_active() || self.is_listening())?; - if self.is_listening() { - Ok(()) - } else { - match self.failure() { - Some(err) => Err(err), - None => Err(ConnectionError::Timeout), - } - } - } - - /// Blocks the current thread until the connection is in a `Connected` state (returning `Ok`), - /// the timeout has been reached (returning `Err(ConnectionError::Timeout)`), or the connection - /// is in a `Failed` state (returning the error which caused the failure) - pub fn wait_connected_or_failure(&self, until: &Duration) -> Result<()> { - self.wait_until(until, || !self.is_active() || self.is_connected())?; - if self.is_connected() { - Ok(()) - } else { - match self.failure() { - Some(err) => Err(err), - None => Err(ConnectionError::Timeout), - } - } - } - - /// Blocks the current thread until the connection is in a `Shutdown` or `Disconnected` state (Ok) or - /// the timeout is reached (Err). - pub fn wait_disconnected(&self, until: &Duration) -> Result<()> { - self.wait_until(until, || self.is_disconnected()) - } - - /// If the connection is in a `Failed` state, the failure error is returned, otherwise `None` - pub fn failure(&self) -> Option { - let lock = acquire_read_lock!(self.state); - match &*lock { - PeerConnectionState::Failed(err) => Some(err.clone().into()), - _ => None, - } - } - - /// Returns the connection state without the ThreadControlMessenger - /// which should never be leaked. - pub fn get_state(&self) -> PeerConnectionSimpleState { - let lock = acquire_read_lock!(self.state); - PeerConnectionSimpleState::from(&*lock) - } - - /// Gets the direction for this peer connection - pub fn get_direction(&self) -> &Option { - &self.direction - } - - /// Waits until the condition returns true or the timeout (`until`) is reached. - /// If the timeout was reached, an `Err(ConnectionError::Timeout)` is returned, otherwise `Ok(())` - fn wait_until(&self, until: &Duration, condition: impl Fn() -> bool) -> Result<()> { - let mut count = 0; - let timeout_ms = until.as_millis(); - while !condition() && count < timeout_ms { - thread::sleep(Duration::from_millis(1)); - count += 1; - } - - if count < timeout_ms { - Ok(()) - } else { - Err(ConnectionError::Timeout) - } - } - - #[cfg(test)] - pub fn new_with_connecting_state_for_test() -> (Self, std::sync::mpsc::Receiver) { - use std::sync::mpsc::sync_channel; - let (tx, rx) = sync_channel(1); - ( - Self { - state: Arc::new(RwLock::new(PeerConnectionState::Connecting(Arc::new(tx.into())))), - ..Default::default() - }, - rx, - ) - } -} - -/// Represents the states that a peer connection can be in without -/// exposing ThreadControlMessenger which should not be leaked. -#[derive(Debug)] -pub enum PeerConnectionSimpleState { - /// The connection object has been created but is not connected - Initial, - /// The connection thread is running, but the connection has not been accepted - Connecting, - /// The connection is listening, and has been not been accepted. - Listening(Option), - /// The connection is connected, and has been accepted. - Connected(Option), - /// The connection has been shut down (node disconnected) - Shutdown, - /// The remote peer has disconnected - Disconnected, - /// Peer connection failed - Failed(PeerConnectionError), -} - -impl From<&PeerConnectionState> for PeerConnectionSimpleState { - fn from(state: &PeerConnectionState) -> Self { - match state { - PeerConnectionState::Initial => PeerConnectionSimpleState::Initial, - PeerConnectionState::Connecting(_) => PeerConnectionSimpleState::Connecting, - PeerConnectionState::Listening(info) => { - PeerConnectionSimpleState::Listening(info.connected_address.clone()) - }, - PeerConnectionState::Connected(info) => { - PeerConnectionSimpleState::Connected(info.connected_address.clone()) - }, - PeerConnectionState::Shutdown => PeerConnectionSimpleState::Shutdown, - PeerConnectionState::Disconnected => PeerConnectionSimpleState::Disconnected, - PeerConnectionState::Failed(e) => PeerConnectionSimpleState::Failed(e.clone()), - } - } -} - -impl fmt::Display for PeerConnectionSimpleState { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use PeerConnectionSimpleState::*; - match *self { - Initial => write!(f, "Initial"), - Connecting => write!(f, "Connecting"), - Listening(Some(ref addr)) => write!(f, "Listening on {}", addr), - Listening(None) => write!(f, "Listening on non TCP socket"), - Connected(Some(ref addr)) => write!(f, "Connected to {}", addr), - Connected(None) => write!(f, "Connected to non TCP socket"), - Shutdown => write!(f, "Shutdown"), - Disconnected => write!(f, "Disconnected"), - Failed(ref event) => write!(f, "Failed({})", event), - } - } -} - -#[cfg(test)] -mod test { - use super::*; - use std::sync::{ - mpsc::{sync_channel, Receiver}, - Arc, - }; - - fn create_thread_ctl() -> (Arc, Receiver) { - let (tx, rx) = sync_channel::(1); - (Arc::new(tx.into()), rx) - } - - #[test] - fn state_display() { - let addr = "127.0.0.1:8000".parse().ok(); - assert_eq!("Initial", format!("{}", PeerConnectionSimpleState::Initial)); - assert_eq!("Connecting", format!("{}", PeerConnectionSimpleState::Connecting)); - assert_eq!( - "Connected to non TCP socket", - format!("{}", PeerConnectionSimpleState::Connected(None)) - ); - assert_eq!( - "Connected to 127.0.0.1:8000", - format!("{}", PeerConnectionSimpleState::Connected(addr)) - ); - assert_eq!("Shutdown", format!("{}", PeerConnectionSimpleState::Shutdown)); - assert_eq!( - format!("Failed({})", PeerConnectionError::ConnectFailed), - format!( - "{}", - PeerConnectionSimpleState::Failed(PeerConnectionError::ConnectFailed) - ) - ); - } - - #[test] - fn new() { - let conn = PeerConnection::new(); - assert!(!conn.is_connected()); - assert!(!conn.is_listening()); - assert!(!conn.is_disconnected()); - assert!(!conn.is_active()); - assert!(!conn.is_shutdown()); - assert!(!conn.is_failed()); - } - - #[test] - fn state_connected() { - let (thread_ctl, _) = create_thread_ctl(); - - let info = ConnectionInfo { - control_messenger: thread_ctl, - connected_address: Some("127.0.0.1:1000".parse().unwrap()), - }; - let conn = PeerConnection { - state: Arc::new(RwLock::new(PeerConnectionState::Connected(Arc::new(info)))), - connection_stats: Arc::new(RwLock::new(PeerConnectionStats::new())), - direction: None, - peer_address: None, - }; - - assert!(conn.is_connected()); - assert!(!conn.is_listening()); - assert!(!conn.is_disconnected()); - assert!(conn.is_active()); - assert!(!conn.is_shutdown()); - assert!(!conn.is_failed()); - } - - #[test] - fn state_listening() { - let (thread_ctl, _) = create_thread_ctl(); - - let info = ConnectionInfo { - control_messenger: thread_ctl, - connected_address: Some("127.0.0.1:1000".parse().unwrap()), - }; - let conn = PeerConnection { - state: Arc::new(RwLock::new(PeerConnectionState::Listening(Arc::new(info)))), - connection_stats: Arc::new(RwLock::new(PeerConnectionStats::new())), - direction: None, - peer_address: None, - }; - - assert!(!conn.is_connected()); - assert!(conn.is_listening()); - assert!(!conn.is_disconnected()); - assert!(conn.is_active()); - assert!(!conn.is_shutdown()); - assert!(!conn.is_failed()); - } - - #[test] - fn state_connecting() { - let (thread_ctl, _) = create_thread_ctl(); - - let conn = PeerConnection { - state: Arc::new(RwLock::new(PeerConnectionState::Connecting(thread_ctl))), - connection_stats: Arc::new(RwLock::new(PeerConnectionStats::new())), - direction: None, - peer_address: None, - }; - - assert!(!conn.is_connected()); - assert!(!conn.is_listening()); - assert!(!conn.is_disconnected()); - assert!(conn.is_active()); - assert!(!conn.is_shutdown()); - assert!(!conn.is_failed()); - } - - #[test] - fn state_active() { - let (thread_ctl, _) = create_thread_ctl(); - - let conn = PeerConnection { - state: Arc::new(RwLock::new(PeerConnectionState::Connecting(thread_ctl))), - connection_stats: Arc::new(RwLock::new(PeerConnectionStats::new())), - direction: None, - peer_address: None, - }; - - assert!(!conn.is_connected()); - assert!(!conn.is_listening()); - assert!(!conn.is_disconnected()); - assert!(conn.is_active()); - assert!(!conn.is_shutdown()); - assert!(!conn.is_failed()); - } - - #[test] - fn state_failed() { - let conn = PeerConnection { - state: Arc::new(RwLock::new(PeerConnectionState::Failed( - PeerConnectionError::ConnectFailed, - ))), - connection_stats: Arc::new(RwLock::new(PeerConnectionStats::new())), - direction: None, - peer_address: None, - }; - - assert!(!conn.is_connected()); - assert!(!conn.is_listening()); - assert!(conn.is_disconnected()); - assert!(!conn.is_active()); - assert!(!conn.is_shutdown()); - assert!(conn.is_failed()); - } - - #[test] - fn state_disconnected() { - let conn = PeerConnection { - state: Arc::new(RwLock::new(PeerConnectionState::Disconnected)), - connection_stats: Arc::new(RwLock::new(PeerConnectionStats::new())), - direction: None, - peer_address: None, - }; - - assert!(!conn.is_connected()); - assert!(!conn.is_listening()); - assert!(conn.is_disconnected()); - assert!(!conn.is_active()); - assert!(!conn.is_shutdown()); - assert!(!conn.is_failed()); - } - - #[test] - fn state_shutdown() { - let conn = PeerConnection { - state: Arc::new(RwLock::new(PeerConnectionState::Shutdown)), - connection_stats: Arc::new(RwLock::new(PeerConnectionStats::new())), - direction: None, - peer_address: None, - }; - - assert!(!conn.is_connected()); - assert!(!conn.is_listening()); - assert!(conn.is_disconnected()); - assert!(!conn.is_active()); - assert!(conn.is_shutdown()); - assert!(!conn.is_failed()); - } - - fn create_connected_peer_connection() -> (PeerConnection, Receiver) { - let (thread_ctl, rx) = create_thread_ctl(); - let info = ConnectionInfo { - control_messenger: thread_ctl, - connected_address: Some("127.0.0.1:1000".parse().unwrap()), - }; - let conn = PeerConnection { - state: Arc::new(RwLock::new(PeerConnectionState::Connected(Arc::new(info)))), - connection_stats: Arc::new(RwLock::new(PeerConnectionStats::new())), - direction: None, - peer_address: None, - }; - (conn, rx) - } - - #[test] - fn send() { - let (conn, rx) = create_connected_peer_connection(); - - let sample_frames = vec![vec![123u8]]; - conn.send(sample_frames.clone()).unwrap(); - let msg = rx.recv_timeout(Duration::from_millis(10)).unwrap(); - match msg { - ControlMessage::SendMsg(frames) => { - assert_eq!(sample_frames, frames); - }, - m => panic!("Unexpected control message '{}'", m), - } - } - - #[test] - fn pause() { - let (conn, rx) = create_connected_peer_connection(); - - conn.pause().unwrap(); - let msg = rx.recv_timeout(Duration::from_millis(10)).unwrap(); - assert_eq!(ControlMessage::Pause, msg); - } - - #[test] - fn resume() { - let (conn, rx) = create_connected_peer_connection(); - - conn.resume().unwrap(); - let msg = rx.recv_timeout(Duration::from_millis(10)).unwrap(); - assert_eq!(ControlMessage::Resume, msg); - } - - #[test] - fn shutdown() { - let (conn, rx) = create_connected_peer_connection(); - - conn.shutdown().unwrap(); - let msg = rx.recv_timeout(Duration::from_millis(10)).unwrap(); - assert_eq!(ControlMessage::Shutdown, msg); - } - - #[test] - fn connection_stats() { - let (conn, _) = create_connected_peer_connection(); - - let stats = conn.connection_stats(); - assert_eq!(stats.messages_recv, 0); - assert_eq!(stats.messages_sent, 0); - } -} diff --git a/comms/src/connection/peer_connection/context.rs b/comms/src/connection/peer_connection/context.rs deleted file mode 100644 index 5f6b443438..0000000000 --- a/comms/src/connection/peer_connection/context.rs +++ /dev/null @@ -1,327 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use std::convert::{TryFrom, TryInto}; - -use super::{ConnectionId, PeerConnectionError}; - -use crate::connection::{ - net_address::ip::SocketAddress, - types::{Direction, Linger, Result}, - zmq::{CurveEncryption, InprocAddress, ZmqContext}, - ConnectionError, - NetAddress, -}; - -/// The default maximum message size which will be used if no maximum message size is set. -const DEFAULT_MAX_MSG_SIZE: u64 = 500 * 1024; // 500 kb -/// The default maximum number of retries before failing the connection. -const DEFAULT_MAX_RETRY_ATTEMPTS: u16 = 10; - -/// Context for connecting to a Peer. This is handed to a PeerConnection and is used to establish the connection. -/// -/// # Fields -/// -/// `context` - the underlying connection context -/// `peer_address` - the address to listen (Direction::Inbound) or connect(Direction::Outbound) -/// `message_sink_address` - the address to forward all received messages -/// `direction` - the connection direction (Inbound or Outbound) -/// `curve_encryption` - the [CurveEncryption] for the connection -/// `max_msg_size` - the maximum size of a incoming message -/// `socks_address` - optional address for a SOCKS proxy -/// -/// [CurveEncryption]: ./../zmq/CurveEncryption/struct.CurveEncryption.html -pub struct PeerConnectionContext { - pub(crate) context: ZmqContext, - pub(crate) peer_address: NetAddress, - pub(crate) message_sink_address: InprocAddress, - pub(crate) direction: Direction, - pub(crate) id: ConnectionId, - pub(crate) curve_encryption: CurveEncryption, - pub(crate) max_msg_size: u64, - pub(crate) max_retry_attempts: u16, - pub(crate) socks_address: Option, - pub(crate) linger: Linger, -} - -impl<'a> TryFrom> for PeerConnectionContext { - type Error = ConnectionError; - - /// Convert a PeerConnectionContextBuilder to a PeerConnectionContext - fn try_from(builder: PeerConnectionContextBuilder<'a>) -> Result { - builder.check_curve_encryption()?; - - let message_sink_address = unwrap_prop(builder.message_sink_address, "message_sink_address")?; - let context = unwrap_prop(builder.context, "context")?.clone(); - let curve_encryption = builder.curve_encryption; - let direction = unwrap_prop(builder.direction, "direction")?; - let id = unwrap_prop(builder.id, "id")?; - let max_msg_size = builder.max_msg_size.unwrap_or(DEFAULT_MAX_MSG_SIZE); - let max_retry_attempts = builder.max_retry_attempts.unwrap_or(DEFAULT_MAX_RETRY_ATTEMPTS); - let peer_address = unwrap_prop(builder.address, "peer_address")?; - let socks_address = builder.socks_address; - let linger = builder.linger.or(Some(Linger::Timeout(100))).unwrap(); - - Ok(PeerConnectionContext { - message_sink_address, - context, - curve_encryption, - direction, - id, - max_msg_size, - max_retry_attempts, - peer_address, - socks_address, - linger, - }) - } -} - -/// Local utility function to unwrap a builder property, or return a PeerConnectionError::InitializationError -#[inline(always)] -fn unwrap_prop(prop: Option, prop_name: &str) -> Result { - match prop { - Some(t) => Ok(t), - None => Err(ConnectionError::PeerError(PeerConnectionError::InitializationError( - format!("Missing required connection property '{}'", prop_name), - ))), - } -} - -/// Used to build a context for a PeerConnection. Fields -/// are the same as a [PeerConnectionContext]. -/// -/// # Example -/// -/// ```edition2018 -/// # use tari_comms::connection::{ -/// # ZmqContext, -/// # InprocAddress, -/// # Direction, -/// # PeerConnectionContextBuilder, -/// # PeerConnection, -/// # }; -/// -/// let ctx = ZmqContext::new(); -/// -/// let peer_context = PeerConnectionContextBuilder::new() -/// .set_id("123") -/// .set_context(&ctx) -/// .set_direction(Direction::Outbound) -/// .set_message_sink_address(InprocAddress::random()) -/// .set_address("127.0.0.1:8080".parse().unwrap()) -/// .build() -/// .unwrap(); -/// ``` -/// -/// [PeerConnectionContext]: ./struct.PeerConnectionContext.html -#[derive(Default)] -pub struct PeerConnectionContextBuilder<'c> { - pub(super) address: Option, - pub(super) message_sink_address: Option, - pub(super) context: Option<&'c ZmqContext>, - pub(super) curve_encryption: CurveEncryption, - pub(super) direction: Option, - pub(super) id: Option, - pub(super) max_msg_size: Option, - pub(super) max_retry_attempts: Option, - pub(super) socks_address: Option, - pub(super) linger: Option, -} - -impl<'c> PeerConnectionContextBuilder<'c> { - /// Set the peer address - setter!(set_address, address, Option); - - /// Set the address where incoming peer messages are forwarded - setter!(set_message_sink_address, message_sink_address, Option); - - /// Set the zmq context - setter!(set_context, context, Option<&'c ZmqContext>); - - /// Set the connection direction - setter!(set_direction, direction, Option); - - /// Set the maximum connection retry attempts - setter!(set_max_retry_attempts, max_retry_attempts, Option); - - /// Set the maximum message size in bytes - setter!(set_max_msg_size, max_msg_size, Option); - - /// Set the socks proxy address - setter!(set_socks_proxy, socks_address, Option); - - /// Set the [Linger] for this connection - setter!(set_linger, linger, Option); - - /// Return a new PeerConnectionContextBuilder - pub fn new() -> Self { - Default::default() - } - - /// Set CurveEncryption. Defaults to the default of CurveEncryption. - pub fn set_curve_encryption(mut self, enc: CurveEncryption) -> Self { - self.curve_encryption = enc; - self - } - - /// Set the ID for the connection. This will be sent as the first - /// frame to the message sink address - pub fn set_id(mut self, id: T) -> Self - where T: Into { - self.id = Some(id.into()); - self - } - - /// Build the PeerConnectionContext. - /// - /// Will return an Err if any of the required fields are not set or if - /// curve encryption is not set correctly for the connection direction. - /// i.e CurveEncryption::Server must be set with Direction::Inbound and - /// CurveEncryption::Client must be set with Direction::Outbound. - /// CurveEncryption::None will succeed in either direction. - pub fn build(self) -> Result { - self.try_into() - } - - fn check_curve_encryption(&self) -> Result<()> { - match self.direction { - Some(ref direction) => match direction { - Direction::Outbound => match self.curve_encryption { - CurveEncryption::None { .. } => Ok(()), - CurveEncryption::Client { .. } => Ok(()), - CurveEncryption::Server { .. } => Err(PeerConnectionError::InitializationError( - "'Client' curve encryption required for outbound connection".to_string(), - ) - .into()), - }, - Direction::Inbound => match self.curve_encryption { - CurveEncryption::None { .. } => Ok(()), - CurveEncryption::Client { .. } => Err(PeerConnectionError::InitializationError( - "'Server' curve encryption required for inbound connection".to_string(), - ) - .into()), - CurveEncryption::Server { .. } => Ok(()), - }, - }, - - None => Err( - PeerConnectionError::InitializationError("Direction not set for peer connection".to_string()).into(), - ), - } - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::connection::{ - peer_connection::PeerConnectionError, - types::{Direction, Result}, - zmq::{CurveEncryption, InprocAddress, ZmqContext}, - ConnectionError, - NetAddress, - }; - - fn assert_initialization_error(result: Result, expected: &str) { - if let Err(error) = result { - match error { - ConnectionError::PeerError(err) => match err { - PeerConnectionError::InitializationError(s) => { - assert_eq!(expected, s); - }, - _ => panic!("Unexpected PeerConnectionError {:?}", err), - }, - _ => panic!("Unexpected ConnectionError {:?}", error), - } - } else { - panic!("Unexpected success when building invalid PeerConnectionContext"); - } - } - - #[test] - fn valid_build() { - let ctx = ZmqContext::new(); - - let recv_addr = InprocAddress::random(); - let peer_addr = "127.0.0.1:80".parse::().unwrap(); - let conn_id = "123".as_bytes(); - let socks_addr = "127.0.0.1:8080".parse::().unwrap(); - - let peer_ctx = PeerConnectionContextBuilder::new() - .set_id(conn_id.clone()) - .set_direction(Direction::Inbound) - .set_context(&ctx) - .set_socks_proxy(socks_addr.clone()) - .set_message_sink_address(recv_addr.clone()) - .set_address(peer_addr.clone()) - .build() - .unwrap(); - - assert_eq!(conn_id.to_vec(), peer_ctx.id); - assert_eq!(recv_addr, peer_ctx.message_sink_address); - assert_eq!(Direction::Inbound, peer_ctx.direction); - assert_eq!(peer_addr, peer_ctx.peer_address); - assert_eq!(Some(socks_addr), peer_ctx.socks_address); - } - - #[test] - fn invalid_build() { - let (sk, pk) = CurveEncryption::generate_keypair().unwrap(); - let ctx = ZmqContext::new(); - - let result = PeerConnectionContextBuilder::new() - .set_id("123") - .set_direction(Direction::Outbound) - .set_message_sink_address(InprocAddress::random()) - .set_address("127.0.0.1:80".parse().unwrap()) - .build(); - - assert_initialization_error(result, "Missing required connection property 'context'"); - - let result = PeerConnectionContextBuilder::new() - .set_id("123") - .set_direction(Direction::Inbound) - .set_context(&ctx) - .set_message_sink_address(InprocAddress::random()) - .set_curve_encryption(CurveEncryption::Client { - secret_key: sk.clone(), - public_key: pk.clone(), - server_public_key: pk.clone(), - }) - .set_address("127.0.0.1:80".parse().unwrap()) - .build(); - - assert_initialization_error(result, "'Server' curve encryption required for inbound connection"); - - let result = PeerConnectionContextBuilder::new() - .set_id("123") - .set_direction(Direction::Outbound) - .set_context(&ctx) - .set_message_sink_address(InprocAddress::random()) - .set_curve_encryption(CurveEncryption::Server { secret_key: sk.clone() }) - .set_address("127.0.0.1:80".parse().unwrap()) - .build(); - - assert_initialization_error(result, "'Client' curve encryption required for outbound connection"); - } -} diff --git a/comms/src/connection/peer_connection/control.rs b/comms/src/connection/peer_connection/control.rs deleted file mode 100644 index 0df8857b49..0000000000 --- a/comms/src/connection/peer_connection/control.rs +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use log::*; - -use std::{convert::From, fmt, sync::mpsc::SyncSender}; - -use crate::{ - connection::types::{Linger, Result}, - message::FrameSet, -}; - -use super::PeerConnectionError; - -const LOG_TARGET: &str = "comms::connections::peer_connection::control"; - -/// Control messages which are sent by PeerConnection to the underlying thread. -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum ControlMessage { - /// Shut the thread down - Shutdown, - /// Send the given frames to the peer - SendMsg(FrameSet), - /// Temporarily pause receiving messages from this connection - Pause, - /// Resume receiving messages from peer - Resume, - /// Sets the linger on the peer connection - SetLinger(Linger), -} - -impl fmt::Display for ControlMessage { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", *self) - } -} - -/// Send and join handles to the worker thread for a PeerConnection -/// This can be converted from a SyncSender -#[derive(Clone)] -pub(super) struct ThreadControlMessenger(SyncSender); - -impl ThreadControlMessenger { - /// Send a [ControlMessage] to the listening thread. - /// - /// # Arguments - /// `msg` - The [ControlMessage] to send - /// - /// [ControlMessage]: ./enum.ControlMessage.html - pub fn send(&self, msg: ControlMessage) -> Result<()> { - self.0.send(msg).map_err(|e| { - PeerConnectionError::ControlSendError(format!("Failed to send control message: {:?}", e)).into() - }) - } - - pub fn get_sender(&self) -> &SyncSender { - &self.0 - } -} - -impl From> for ThreadControlMessenger { - /// Convert a SyncSender to a ThreadControlMessenger - fn from(sender: SyncSender) -> Self { - Self(sender) - } -} - -impl Drop for ThreadControlMessenger { - /// Send a ControlMessage::Shutdown on drop. - fn drop(&mut self) { - debug!(target: LOG_TARGET, "ThreadControlMessenger dropped"); - // We assume here that the thread responds to the shutdown request. - let _ = self.0.try_send(ControlMessage::Shutdown); - } -} - -#[cfg(test)] -mod test { - use super::*; - use std::{sync::mpsc::sync_channel, thread, time::Duration}; - use tari_utilities::thread_join::ThreadJoinWithTimeout; - - #[test] - fn send_control_message() { - let (tx, rx) = sync_channel::(1); - - let handle = thread::spawn(move || { - let msg = rx - .recv_timeout(Duration::from_millis(100)) - .map_err(|e| format!("{:?}", e))?; - match msg { - ControlMessage::Shutdown => Ok(()), - x => Err(format!("Received unexpected message {}", x)), - } - }); - - let messenger: ThreadControlMessenger = tx.into(); - messenger.send(ControlMessage::Shutdown).unwrap(); - - handle - .timeout_join(Duration::from_millis(3000)) - .map_err(|e| format!("Test thread errored: {:?}", e)) - .unwrap(); - } -} diff --git a/comms/src/connection/peer_connection/error.rs b/comms/src/connection/peer_connection/error.rs deleted file mode 100644 index da2240b28e..0000000000 --- a/comms/src/connection/peer_connection/error.rs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use derive_error::Error; - -/// Represents errors which can occur in a PeerConnection. -#[derive(Debug, Error, Clone, PartialEq)] -pub enum PeerConnectionError { - #[error(msg_embedded, non_std, no_from)] - InitializationError(String), - #[error(msg_embedded, non_std, no_from)] - ControlSendError(String), - /// Peer connection control port has disconnected - ControlPortDisconnected, - /// Unexpected identity received from peer - UnexpectedIdentity, - /// Connection identity of peer has not been established - IdentityNotEstablished, - #[error(msg_embedded, non_std, no_from)] - StateError(String), - /// Error occurred while shutting down the connection - ShutdownError, - /// Failed to establish a connection - ConnectFailed, - #[error(msg_embedded, non_std, no_from)] - UnexpectedConnectionError(String), - /// Connection attempts exceeded max retries - ExceededMaxConnectRetryCount, - /// Peer connection worker thread failed to start - ThreadInitializationError, -} diff --git a/comms/src/connection/peer_connection/mod.rs b/comms/src/connection/peer_connection/mod.rs deleted file mode 100644 index 4c8dab0b7d..0000000000 --- a/comms/src/connection/peer_connection/mod.rs +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -/// # peer_connection -/// -/// A peer connection is a bi-directional connection to a given [NetAddress]. The [Direction] of -/// a [PeerConnection] relates to which side initiate the connection. i.e for Inbound, this node -/// initiated the connection and waits for the peer to connect. For Outbound, this node is connecting -/// out to a listening socket. Frames can be sent and received over a single [Connection]. All received -/// messages are forwarded to a consumer connection. -/// -/// The [PeerConnection] object starts a [Worker] which is responsible for establishing the required -/// connections. Two [Connections] are needed: a connection to/from the given [NetAddress] and a -/// connection to the consumer. A [ConnectionMonitor] is started which receives socket events from the -/// peer connection. -/// -/// A [PeerConnection] consists of these modules: -/// -/// 1. `connection` - responsible for starting and sending control messages to the PeerConnection -/// worker thread. -/// 2. `context` - Builder for a `PeerConnectionContext` which is owned by a PeerConnection worker -/// thread. This provides all the information required to create the underlying -/// connections to the peer and consumer. -/// 3. `control` - Contains the control messages which can be sent from the [PeerConnection] to -/// the [Worker], as well as a thin wrapper around [std::sync::mpsc::Sender]. -/// 4. `error` - Contains [PeerConnectionError] -/// 5. `worker` - Where all the work is done. Contains the code responsible for establishing -/// connections (peer and consumer), receiving messages to forward to the -/// consumer connection, updating the peer connection state from socket events -/// and receiving control messages and acting on them. -/// -/// ## PeerConnectionState -/// -/// +--------------------+ -/// | | -/// | Initial | -/// | | -/// +--------------------+ -/// | -/// | +------------------+ -/// +--------------------+ | | -/// | | +---------| Shutdown | -/// | Connecting | | | | -/// | |- | +------------------+ -/// +---------|----------+ \ +-----+ +------------------+ -/// | | | | | -/// | | |-------+ Disconnected | -/// Accepted / Connected | | | | -/// | /+-----+ +------------------+ -/// | / | +------------------+ -/// | / | | | -/// +--------------------- | | Failed | -/// | | +---------- | -/// | Connected | +------------------+ -/// | | -/// +--------------------+ -/// -/// [PeerConnection](./connection/struct.PeerConnection.html] -/// [Direction](../types/enum.Direction.html] -/// [NetAddress](../net_address/enum.NetAddress.html] -/// [Connection](../connection/struct.Connection.html] -/// [Worker](./worker/struct.Worker.html] -/// [ConnectionMonitor](../monitor/struct.ConnectionMonitor.html] -/// [PeerConnectionError](./error/struct.PeerConnectionError.html] -mod connection; -mod context; -mod control; -mod error; -mod worker; - -pub use self::{ - connection::{ConnectionId, PeerConnection, PeerConnectionProtoMessage, PeerConnectionSimpleState}, - context::{PeerConnectionContext, PeerConnectionContextBuilder}, - control::ControlMessage, - error::PeerConnectionError, -}; diff --git a/comms/src/connection/peer_connection/worker.rs b/comms/src/connection/peer_connection/worker.rs deleted file mode 100644 index 86e152d7ef..0000000000 --- a/comms/src/connection/peer_connection/worker.rs +++ /dev/null @@ -1,722 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use super::{ - connection::{ - ConnectionInfo, - PeerConnectionProtoMessage, - PeerConnectionSimpleState, - PeerConnectionState, - PeerConnectionStats, - }, - control::ControlMessage, - PeerConnectionContext, - PeerConnectionError, -}; -use crate::{ - connection::{ - connection::{Connection, EstablishedConnection}, - monitor::{ConnectionMonitor, SocketEvent, SocketEventType}, - types::{Direction, Linger, Result}, - ConnectionError, - InprocAddress, - NetAddress, - }, - message::{Frame, FrameSet}, -}; -use log::*; -use std::{ - sync::{ - mpsc::{sync_channel, Receiver, RecvTimeoutError, SyncSender}, - Arc, - RwLock, - }, - thread::{self, JoinHandle}, - time::Duration, -}; -use tari_utilities::message_format::MessageFormat; - -const LOG_TARGET: &str = "comms::connection::peer_connection::worker"; - -/// Send HWM for peer connections -const PEER_CONNECTION_SEND_HWM: i32 = 10; -/// Receive HWM for peer connections -const PEER_CONNECTION_RECV_HWM: i32 = 10; - -/// Set the allocated stack size for each PeerConnectionWorker thread -const THREAD_STACK_SIZE: usize = 64 * 1024; // 64kb - -/// Worker which: -/// - Establishes a connection to peer -/// - Establishes a connection to the message consumer -/// - Receives and handles ControlMessages -/// - Forwards frames to consumer -/// - Handles SocketEvents and updates shared connection state -pub(super) struct PeerConnectionWorker { - context: PeerConnectionContext, - sender: SyncSender, - receiver: Receiver, - identity: Option, - paused: bool, - monitor_addr: InprocAddress, - connection_state: Arc>, - connection_stats: Arc>, - retry_count: u16, -} - -impl PeerConnectionWorker { - /// Create a new Worker from the given context - pub fn new( - context: PeerConnectionContext, - connection_state: Arc>, - connection_stats: Arc>, - ) -> Self - { - let (sender, receiver) = sync_channel(10); - Self { - context, - sender, - receiver, - identity: None, - paused: false, - monitor_addr: InprocAddress::random(), - connection_state, - connection_stats, - retry_count: 0, - } - } - - /// Spawn a worker thread - pub fn spawn(mut self) -> Result>> { - { - // Set connecting state - let mut state_lock = acquire_write_lock!(self.connection_state); - *state_lock = PeerConnectionState::Connecting(Arc::new(self.sender.clone().into())); - } - - let handle = thread::Builder::new() - .name(format!("peer-conn-{}-thread", &self.context.id.to_short_id())) - .stack_size(THREAD_STACK_SIZE) - .spawn(move || -> Result<()> { - let result = self.run(); - - // Main loop exited, let's set the shared connection state. - self.handle_run_result(result)?; - - Ok(()) - }) - .map_err(|_| PeerConnectionError::ThreadInitializationError)?; - - Ok(handle) - } - - /// Handle the result for the worker loop and update connection state if necessary - fn handle_run_result(&mut self, result: Result<()>) -> Result<()> { - let mut lock = acquire_write_lock!(self.connection_state); - match result { - Ok(_) => { - info!( - target: LOG_TARGET, - "[{}] Peer connection shut down cleanly", self.context.peer_address - ); - // The loop exited cleanly. - match *lock { - // The connection is still in a connected state, transition to Shutdown - PeerConnectionState::Connected(_) | PeerConnectionState::Connecting(_) => { - *lock = PeerConnectionState::Shutdown; - }, - // Connection is in some other state, the loop exited without error - // so we won't change the state to preserve failed or disconnected states. - _ => {}, - } - }, - Err(err) => { - error!( - target: LOG_TARGET, - "[{}] Peer connection exited with an error: {:?}", self.context.peer_address, err - ); - // Loop failed, update the connection state to reflect that - *lock = match err { - ConnectionError::PeerError(err) => PeerConnectionState::Failed(err), - e => PeerConnectionState::Failed(PeerConnectionError::UnexpectedConnectionError(format!("{}", e))), - }; - }, - } - - Ok(()) - } - - /// The main loop for the worker. This is where the work is done. - /// The required connections are set up and messages processed. - fn run(&mut self) -> Result<()> { - let monitor = self.connect_monitor()?; - let peer_conn = self.establish_peer_connection()?; - let consumer = self.establish_sink_connection()?; - let addr = peer_conn.get_connected_address(); - - if let Some(a) = addr { - debug!(target: LOG_TARGET, "Starting peer connection worker thread on {}", a); - self.context.peer_address = a.clone().into(); - } - - if self.context.direction == Direction::Outbound { - debug!(target: LOG_TARGET, "Sending Identify to remote connection"); - self.identify(&peer_conn)?; - } - - loop { - if let Some(msg) = self.receive_control_msg()? { - match msg { - ControlMessage::Shutdown => { - debug!(target: LOG_TARGET, "[{:?}] Shutdown message received", addr); - peer_conn.set_linger(Linger::Never)?; - // Ensure that the peer connection is dropped as soon as possible. - // This somehow seemed to improve connection reliability. - drop(peer_conn); - break Ok(()); - }, - ControlMessage::SendMsg(frames) => { - debug!( - target: LOG_TARGET, - "[{:?}] SendMsg control message received ({} frames)", - addr, - frames.len() - ); - let payload = self.create_payload(PeerConnectionProtoMessage::Message, frames)?; - peer_conn.send(payload)?; - acquire_write_lock!(self.connection_stats).incr_message_sent(); - }, - ControlMessage::Pause => { - debug!(target: LOG_TARGET, "[{:?}] Pause control message received", addr); - self.paused = true; - }, - ControlMessage::Resume => { - debug!(target: LOG_TARGET, "[{:?}] Resume control message received", addr); - self.paused = false; - }, - ControlMessage::SetLinger(linger) => { - debug!( - target: LOG_TARGET, - "[{:?}] Setting linger to {:?} on peer connection", addr, linger - ); - // Log and ignore errors here since this is unlikely to happen or cause any issues - match peer_conn.set_linger(linger) { - Ok(_) => {}, - Err(err) => error!(target: LOG_TARGET, "Error setting linger on connection: {:?}", err), - } - }, - } - } - - if let Ok(event) = monitor.read(1) { - self.handle_socket_event(event)?; - } - - if !self.paused { - self.handle_frames(&peer_conn, &consumer)?; - } - } - } - - /// Handles socket events from the ConnectionMonitor. Updating connection - /// state as necessary. - fn handle_socket_event(&mut self, event: SocketEvent) -> Result<()> { - use SocketEventType::*; - - debug!(target: LOG_TARGET, "{:?}", event); - match event.event_type { - Disconnected => { - let mut lock = acquire_write_lock!(self.connection_state); - *lock = PeerConnectionState::Disconnected; - }, - Listening => { - self.retry_count = 0; - let mut lock = acquire_write_lock!(self.connection_state); - match *lock { - PeerConnectionState::Connecting(ref thread_ctl) => { - let info = ConnectionInfo { - control_messenger: thread_ctl.clone(), - connected_address: match self.context.peer_address { - NetAddress::IP(ref socket_addr) => Some(socket_addr.clone()), - _ => None, - }, - }; - info!( - target: LOG_TARGET, - "[{}] Listening on Inbound connection", self.context.peer_address - ); - *lock = PeerConnectionState::Listening(Arc::new(info)); - }, - PeerConnectionState::Connected(_) => { - warn!( - target: LOG_TARGET, - "[{}] Listening event when connected", self.context.peer_address - ); - }, - ref s => { - return Err(PeerConnectionError::StateError(format!( - "Unable to transition to connected state from state '{}'", - PeerConnectionSimpleState::from(s) - )) - .into()); - }, - } - }, - Connected => { - self.retry_count = 0; - self.transition_connected()?; - }, - BindFailed | AcceptFailed | HandshakeFailedNoDetail | HandshakeFailedProtocol | HandshakeFailedAuth => { - let mut lock = acquire_write_lock!(self.connection_state); - *lock = PeerConnectionState::Failed(PeerConnectionError::ConnectFailed); - }, - ConnectRetried => { - let mut lock = acquire_write_lock!(self.connection_state); - match *lock { - PeerConnectionState::Connecting(_) => { - self.retry_count += 1; - if self.retry_count >= self.context.max_retry_attempts { - *lock = PeerConnectionState::Failed(PeerConnectionError::ExceededMaxConnectRetryCount); - } - }, - _ => {}, - } - }, - _ => {}, - } - - Ok(()) - } - - fn transition_connected(&self) -> Result<()> { - let mut lock = acquire_write_lock!(self.connection_state); - - match *lock { - PeerConnectionState::Connecting(ref thread_ctl) => { - let info = ConnectionInfo { - control_messenger: thread_ctl.clone(), - connected_address: match self.context.peer_address { - NetAddress::IP(ref socket_addr) => Some(socket_addr.clone()), - _ => None, - }, - }; - info!(target: LOG_TARGET, "[{}] Connected", self.context.peer_address); - match self.context.direction { - Direction::Inbound => { - if self.identity.is_some() { - *lock = PeerConnectionState::Connected(Arc::new(info)); - } - }, - Direction::Outbound => { - *lock = PeerConnectionState::Connected(Arc::new(info)); - }, - } - }, - PeerConnectionState::Listening(ref info) => match self.context.direction { - Direction::Inbound => { - info!( - target: LOG_TARGET, - "Inbound connection listening on {}", self.context.peer_address - ); - if self.identity.is_some() { - *lock = PeerConnectionState::Connected(info.clone()); - } - }, - Direction::Outbound => { - return Err(PeerConnectionError::StateError( - "Should not happen: outbound connection was in listening state".to_string(), - ) - .into()); - }, - }, - PeerConnectionState::Connected(_) => { - warn!( - target: LOG_TARGET, - "[{}] Connected event when already connected", self.context.peer_address - ); - }, - ref s => { - return Err(PeerConnectionError::StateError(format!( - "Unable to transition to connected state from state '{}'", - PeerConnectionSimpleState::from(s) - )) - .into()); - }, - } - - debug!( - target: LOG_TARGET, - "[{}] Peer connection state is '{}'", - self.context.peer_address, - PeerConnectionSimpleState::from(&*lock) - ); - - Ok(()) - } - - /// Send PeerMessageType::Identify to remote peer - fn identify(&self, peer_conn: &EstablishedConnection) -> Result<()> { - let payload = self.create_payload(PeerConnectionProtoMessage::Identify, vec![vec![]])?; - peer_conn.send(payload) - } - - /// Connects the connection monitor to this worker's peer Connection. - fn connect_monitor(&self) -> Result { - let context = &self.context; - ConnectionMonitor::connect(&context.context, &self.monitor_addr) - } - - /// Handles PeerMessageType messages .Forwards frames from the source to the sink - fn handle_frames(&mut self, frontend: &EstablishedConnection, backend: &EstablishedConnection) -> Result<()> { - let context = &self.context; - if let Some(frames) = connection_try!(frontend.receive(10)) { - // Attempt to extract the parts of a peer message. - // If we can't extract the correct frames, we ignore the message - if let Some((identity, message_type, frames)) = self.extract_frame_parts(frames) { - match message_type { - PeerConnectionProtoMessage::Identify => { - if self.context.direction == Direction::Outbound { - warn!( - target: LOG_TARGET, - "Ignoring IDENTIFY message on outbound peer connection {:?}", self.context.id, - ); - return Ok(()); - } - - match self.identity { - Some(_) => { - warn!( - target: LOG_TARGET, - "Peer sent IDENT message when already set {:x?} {:x?}", self.identity, identity, - ); - }, - None => { - self.identity = identity; - self.transition_connected()?; - debug!( - target: LOG_TARGET, - "Peer sent IDENT, set peer connection identity to {:x?}", self.identity - ); - }, - } - }, - PeerConnectionProtoMessage::Message => { - acquire_write_lock!(self.connection_stats).incr_message_recv(); - - match context.direction { - // For a ZMQ_ROUTER, the first frame is the identity - Direction::Inbound => match self.identity { - Some(ref ident) => { - let identity = identity.expect( - "Invariant check: Inbound connections should always have an identity frame.", - ); - if identity != *ident { - return Err(PeerConnectionError::UnexpectedIdentity.into()); - } - }, - None => { - return Err(PeerConnectionError::IdentityNotEstablished.into()); - }, - }, - Direction::Outbound => {}, - } - - let payload = self.construct_consumer_payload(frames); - backend.send(&payload)?; - }, - PeerConnectionProtoMessage::Invalid => { - debug!( - target: LOG_TARGET, - "Peer sent invalid message type. Discarding the message" - ); - }, - } - } - } - Ok(()) - } - - fn extract_frame_parts( - &self, - mut frames: FrameSet, - ) -> Option<(Option, PeerConnectionProtoMessage, FrameSet)> - { - match self.context.direction { - Direction::Inbound => { - if frames.len() < 2 { - return None; - } - let identity = frames.drain(0..1).collect::().remove(0); - let message_type_u8 = frames.drain(0..1).collect::().remove(0).remove(0); - let message_type = PeerConnectionProtoMessage::from(message_type_u8); - - Some((Some(identity), message_type, frames)) - }, - Direction::Outbound => { - if frames.len() < 1 { - return None; - } - let message_type_u8 = frames.drain(0..1).collect::().remove(0).remove(0); - let message_type = PeerConnectionProtoMessage::from(message_type_u8); - - Some((None, message_type, frames)) - }, - } - } - - fn construct_consumer_payload(&self, frames: FrameSet) -> FrameSet { - let mut payload = Vec::with_capacity(2 + frames.len()); - payload.push(self.context.id.clone().into_inner()); - let forwardable = true; - payload.extend(forwardable.to_binary()); - payload.extend_from_slice(&frames); - payload - } - - /// Creates the payload to be sent to the underlying connection - fn create_payload(&self, message_type: PeerConnectionProtoMessage, frames: FrameSet) -> Result { - let context = &self.context; - - match context.direction { - // Add identity frame to the front of the payload for ROUTER socket - Direction::Inbound => match self.identity { - Some(ref ident) => { - let mut payload = Vec::with_capacity(2 + frames.len()); - payload.push(ident.clone()); - payload.push(vec![message_type as u8]); - payload.extend(frames); - - debug!( - target: LOG_TARGET, - "Created payload with identity frame {:x?} ({} frame(s))", - ident, - payload.len() - ); - Ok(payload) - }, - None => Err(PeerConnectionError::IdentityNotEstablished.into()), - }, - Direction::Outbound => { - let mut payload = Vec::with_capacity(1 + frames.len()); - payload.push(vec![message_type as u8]); - payload.extend(frames); - - Ok(payload) - }, - } - } - - /// Receive a `ControlMessage` on the control message channel - fn receive_control_msg(&self) -> Result> { - match self.receiver.recv_timeout(Duration::from_millis(5)) { - Ok(msg) => Ok(Some(msg)), - Err(e) => match e { - RecvTimeoutError::Disconnected => Err(PeerConnectionError::ControlPortDisconnected.into()), - RecvTimeoutError::Timeout => Ok(None), - }, - } - } - - /// Establish the connection to the peer address - fn establish_peer_connection(&self) -> Result { - let context = &self.context; - Connection::new(&context.context, context.direction.clone()) - .set_name(format!("peer-conn-{}", self.context.id).as_str()) - .set_linger(context.linger.clone()) - .set_heartbeat_interval(Duration::from_millis(1000)) - .set_heartbeat_timeout(Duration::from_millis(5000)) - .set_monitor_addr(self.monitor_addr.clone()) - .set_curve_encryption(context.curve_encryption.clone()) - .set_receive_hwm(PEER_CONNECTION_RECV_HWM) - .set_send_hwm(PEER_CONNECTION_SEND_HWM) - .set_socks_proxy_addr(context.socks_address.clone()) - .set_max_message_size(Some(context.max_msg_size)) - .establish(&context.peer_address) - } - - /// Establish the connection to the consumer - fn establish_sink_connection(&self) -> Result { - let context = &self.context; - Connection::new(&context.context, Direction::Outbound) - .set_name("peer-conn-sink") - .establish(&context.message_sink_address) - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::connection::{ - peer_connection::{control::ThreadControlMessenger, ConnectionId}, - types::Linger, - CurveEncryption, - ZmqContext, - }; - - fn make_thread_ctl() -> (Arc, Receiver) { - let (tx, rx) = sync_channel(1); - (Arc::new(tx.into()), rx) - } - - fn transition_connected_setup( - direction: Direction, - initial_state: PeerConnectionSimpleState, - identity: Option, - ) -> PeerConnectionWorker - { - let context = ZmqContext::new(); - let peer_address = "127.0.0.1:9000".parse().unwrap(); - - let (thread_ctl, receiver) = make_thread_ctl(); - let info = Arc::new(ConnectionInfo { - connected_address: None, - control_messenger: Arc::clone(&thread_ctl), - }); - let connection_state = match initial_state { - PeerConnectionSimpleState::Initial => PeerConnectionState::Initial, - PeerConnectionSimpleState::Connecting => PeerConnectionState::Connecting(Arc::clone(&thread_ctl)), - PeerConnectionSimpleState::Connected(_) => PeerConnectionState::Connected(info), - PeerConnectionSimpleState::Disconnected => PeerConnectionState::Disconnected, - PeerConnectionSimpleState::Shutdown => PeerConnectionState::Shutdown, - PeerConnectionSimpleState::Listening(_) => PeerConnectionState::Listening(info), - PeerConnectionSimpleState::Failed(err) => PeerConnectionState::Failed(err), - }; - - let context = PeerConnectionContext { - context, - message_sink_address: InprocAddress::random(), - peer_address, - direction, - linger: Linger::Indefinitely, - id: ConnectionId::default(), - curve_encryption: CurveEncryption::default(), - socks_address: None, - max_msg_size: 1024 * 1024, - max_retry_attempts: 1, - }; - PeerConnectionWorker { - context, - identity, - receiver, - sender: thread_ctl.get_sender().clone(), - connection_state: Arc::new(RwLock::new(connection_state)), - connection_stats: Arc::new(RwLock::new(PeerConnectionStats::new())), - monitor_addr: InprocAddress::random(), - retry_count: 1, - paused: false, - } - } - - #[test] - fn transition_connected() { - // Transition outbound to connected - let subject = transition_connected_setup(Direction::Outbound, PeerConnectionSimpleState::Connecting, None); - subject.transition_connected().unwrap(); - { - let lock = subject.connection_state.read().unwrap(); - match (&*lock).into() { - PeerConnectionSimpleState::Connected(_) => {}, - s => panic!("Unexpected state '{:?}'", s), - } - } - - // Transition connecting inbound without identity - let subject = transition_connected_setup(Direction::Inbound, PeerConnectionSimpleState::Connecting, None); - subject.transition_connected().unwrap(); - { - let lock = subject.connection_state.read().unwrap(); - match (&*lock).into() { - PeerConnectionSimpleState::Connecting => {}, - s => panic!("Unexpected state '{:?}'", s), - } - } - - // Transition connecting inbound with identity - let subject = transition_connected_setup( - Direction::Inbound, - PeerConnectionSimpleState::Connecting, - Some(Vec::new()), - ); - subject.transition_connected().unwrap(); - { - let lock = subject.connection_state.read().unwrap(); - match (&*lock).into() { - PeerConnectionSimpleState::Connected(_) => {}, - s => panic!("Unexpected state '{:?}'", s), - } - } - - // Transition listening inbound without identity - let subject = transition_connected_setup(Direction::Inbound, PeerConnectionSimpleState::Listening(None), None); - subject.transition_connected().unwrap(); - { - let lock = subject.connection_state.read().unwrap(); - match (&*lock).into() { - PeerConnectionSimpleState::Listening(None) => {}, - s => panic!("Unexpected state '{:?}'", s), - } - } - - // Transition listening inbound with identity - let subject = transition_connected_setup( - Direction::Inbound, - PeerConnectionSimpleState::Listening(None), - Some(Vec::new()), - ); - subject.transition_connected().unwrap(); - { - let lock = subject.connection_state.read().unwrap(); - match (&*lock).into() { - PeerConnectionSimpleState::Connected(_) => {}, - s => panic!("Unexpected state '{:?}'", s), - } - } - - // Transition listening outbound with identity - let subject = transition_connected_setup( - Direction::Outbound, - PeerConnectionSimpleState::Listening(None), - Some(Vec::new()), - ); - match subject.transition_connected().unwrap_err() { - ConnectionError::PeerError(PeerConnectionError::StateError(_)) => {}, - err => panic!("Unexpected error: {:?}", err), - } - - // Transition connected to connected - let subject = transition_connected_setup(Direction::Inbound, PeerConnectionSimpleState::Connected(None), None); - subject.transition_connected().unwrap(); - { - let lock = subject.connection_state.read().unwrap(); - match (&*lock).into() { - PeerConnectionSimpleState::Connected(_) => {}, - s => panic!("Unexpected state '{:?}'", s), - } - } - // Transition from other states - let subject = transition_connected_setup(Direction::Inbound, PeerConnectionSimpleState::Initial, None); - match subject.transition_connected().unwrap_err() { - ConnectionError::PeerError(PeerConnectionError::StateError(_)) => {}, - err => panic!("Unexpected error: {:?}", err), - } - } -} diff --git a/comms/src/connection/types.rs b/comms/src/connection/types.rs deleted file mode 100644 index eecec36a72..0000000000 --- a/comms/src/connection/types.rs +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::connection::ConnectionError; -use std::fmt; - -/// The types of socket available -pub enum SocketType { - Request, - Reply, - Router, - Dealer, - Pub, - Sub, - Push, - Pull, - Pair, -} - -/// Result type used by `comms::connection` module -pub type Result = std::result::Result; - -/// Represents the linger behavior of a connection. This can, depending on the chosen behavior, -/// allow a connection to finish sending messages before disconnecting. -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum Linger { - /// Linger until all messages have been sent - Indefinitely, - /// Don't linger, close the connection immediately - Never, - /// Linger for the specified time (in milliseconds) before disconnecting. - Timeout(u32), -} - -impl Default for Linger { - fn default() -> Self { - Linger::Never - } -} - -/// Direction of the connection -#[derive(Clone, Eq, PartialEq, Debug)] -pub enum Direction { - /// Connection listens for incoming connections - Inbound, - /// Connection establishes an outbound connection - Outbound, -} - -impl fmt::Display for Direction { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self) - } -} - -/// Used to select the method to use when establishing the connection. -pub enum SocketEstablishment { - /// Select bind or connect based on connection [Direction](./enum.Direction.html) - Auto, - /// Always bind on the socket - Bind, - /// Always connect on the socket - Connect, -} - -impl Default for SocketEstablishment { - fn default() -> Self { - SocketEstablishment::Auto - } -} diff --git a/comms/src/connection/zmq/context.rs b/comms/src/connection/zmq/context.rs deleted file mode 100644 index 45f9bb803d..0000000000 --- a/comms/src/connection/zmq/context.rs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use zmq; - -use crate::connection::{types::SocketType, zmq::ZmqError}; - -/// Thin wrapper of a [0MQ context]. -/// -/// [0MQ context]: http://api.zeromq.org/2-1:zmq#toc3 -#[derive(Clone, Default)] -pub struct ZmqContext(zmq::Context); - -impl ZmqContext { - pub fn new() -> Self { - Self(zmq::Context::new()) - } - - pub fn socket(&self, socket_type: SocketType) -> Result { - use SocketType::*; - - let zmq_socket_type = match socket_type { - Request => zmq::REQ, - Reply => zmq::REP, - Router => zmq::ROUTER, - Dealer => zmq::DEALER, - Pub => zmq::PUB, - Sub => zmq::SUB, - Push => zmq::PUSH, - Pull => zmq::PULL, - Pair => zmq::PAIR, - }; - - self.0 - .socket(zmq_socket_type) - .map_err(|e| ZmqError::SocketError(format!("{}", e))) - } -} diff --git a/comms/src/connection/zmq/curve_keypair.rs b/comms/src/connection/zmq/curve_keypair.rs deleted file mode 100644 index c2c43b9292..0000000000 --- a/comms/src/connection/zmq/curve_keypair.rs +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::connection::{types::Result, ConnectionError}; -use clear_on_drop::clear::Clear; -use serde::{Deserialize, Serialize}; -use zmq; - -//---------------------------------- Curve Encryption --------------------------------------------// - -/// Represents settings for asymmetric curve encryption. Every socket with encryption enabled -/// must either act as a server or client. -#[derive(Clone)] -pub enum CurveEncryption { - /// No encryption - None, - /// Act as a server which accepts all connections which have a public key corresponding to the - /// given secret key. - Server { secret_key: CurveSecretKey }, - /// Act as a client which connects to a server with a given server public key and a client keypair. - Client { - secret_key: CurveSecretKey, - public_key: CurvePublicKey, - server_public_key: CurvePublicKey, - }, -} - -impl CurveEncryption { - /// Generates a Curve25519 public/private keypair - pub fn generate_keypair() -> Result<(CurveSecretKey, CurvePublicKey)> { - let keypair = zmq::CurveKeyPair::new().map_err(|e| { - ConnectionError::CurveKeypairError(format!("Unable to generate new Curve25519 keypair: {}", e)) - })?; - - Ok((CurveSecretKey(keypair.secret_key), CurvePublicKey(keypair.public_key))) - } -} - -impl Default for CurveEncryption { - fn default() -> Self { - CurveEncryption::None - } -} - -//---------------------------------- Curve Secret Key --------------------------------------------// - -/// Represents a Curve25519 secret key -#[derive(Clone)] -pub struct CurveSecretKey(pub(crate) [u8; 32]); - -impl CurveSecretKey { - pub fn is_zero(&self) -> bool { - self.0.iter().all(|b| *b == 0) - } - - pub fn into_inner(self) -> [u8; 32] { - self.0 - } -} - -impl Default for CurveSecretKey { - fn default() -> Self { - Self([0u8; 32]) - } -} - -impl Drop for CurveSecretKey { - fn drop(&mut self) { - self.0.clear(); - } -} - -//---------------------------------- Curve Public Key --------------------------------------------// -#[derive(Clone, Serialize, Deserialize, Debug)] -/// Represents a Curve25519 public key -pub struct CurvePublicKey(pub(crate) [u8; 32]); - -impl CurvePublicKey { - pub fn is_zero(&self) -> bool { - self.0.iter().all(|b| *b == 0) - } - - pub fn into_inner(self) -> [u8; 32] { - self.0 - } -} - -impl Default for CurvePublicKey { - fn default() -> Self { - Self([0u8; 32]) - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - // Optimisations can cause this test to erroneously fail in release mode. The value is zeroed out on drop though. - #[cfg(debug_assertions)] - fn clears_secret_key_on_drop() { - use std::slice; - let ptr; - { - let sk = CurveEncryption::generate_keypair().unwrap().0; - ptr = sk.0.as_ptr() - } - - let zero = &[0u8; 32]; - unsafe { - assert_eq!(zero, slice::from_raw_parts(ptr, 32)); - } - } - - #[test] - fn default_is_zero() { - assert!(CurveSecretKey::default().is_zero()); - assert!(CurvePublicKey::default().is_zero()); - } -} diff --git a/comms/src/connection/zmq/endpoint.rs b/comms/src/connection/zmq/endpoint.rs deleted file mode 100644 index 153a385b62..0000000000 --- a/comms/src/connection/zmq/endpoint.rs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -/// Any type that can be represented as a ZeroMQ endpoint string -pub trait ZmqEndpoint { - /// Convert to a ZeroMQ endpoint string - fn to_zmq_endpoint(&self) -> String; -} - -impl ZmqEndpoint for &T -where T: ZmqEndpoint -{ - fn to_zmq_endpoint(&self) -> String { - (*self).to_zmq_endpoint() - } -} diff --git a/comms/src/connection/zmq/inproc_address.rs b/comms/src/connection/zmq/inproc_address.rs deleted file mode 100644 index c30f624b7a..0000000000 --- a/comms/src/connection/zmq/inproc_address.rs +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::connection::zmq::{ZmqEndpoint, ZmqError}; -use rand::{distributions::Alphanumeric, EntropyRng, Rng}; -use std::{fmt, iter, str::FromStr}; - -const DEFAULT_INPROC: &str = "inproc://default"; - -/// Represents a zMQ inproc address. More information [here](http://api.zeromq.org/2-1:zmq-inproc). -#[derive(Eq, PartialEq, Debug, Clone)] -pub struct InprocAddress(String); - -impl InprocAddress { - /// Generate a random InprocAddress. - pub fn random() -> Self { - let mut rng = EntropyRng::new(); - let rand_str: String = iter::repeat(()).map(|_| rng.sample(Alphanumeric)).take(8).collect(); - Self(format!("inproc://{}", rand_str)) - } - - pub fn is_default(&self) -> bool { - self.0 == DEFAULT_INPROC - } -} - -impl Default for InprocAddress { - fn default() -> Self { - Self(DEFAULT_INPROC.to_owned()) - } -} - -impl FromStr for InprocAddress { - type Err = ZmqError; - - fn from_str(s: &str) -> Result { - if s.len() > 9 && s.starts_with("inproc://") { - Ok(InprocAddress(s.to_owned())) - } else { - Err(ZmqError::MalformedInprocAddress) - } - } -} - -impl fmt::Display for InprocAddress { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -impl ZmqEndpoint for InprocAddress { - fn to_zmq_endpoint(&self) -> String { - self.0.to_string() - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn from_str() { - let addr = "inproc://扩".parse::().unwrap(); - assert_eq!("inproc://扩", addr.to_zmq_endpoint()); - - let result = "inporc://abc".parse::(); - assert!(result.is_err()); - let err = result.err().unwrap(); - assert_eq!(ZmqError::MalformedInprocAddress, err); - - let result = "inproc://".parse::(); - assert!(result.is_err()); - let err = result.err().unwrap(); - assert_eq!(ZmqError::MalformedInprocAddress, err); - } - - #[test] - fn default() { - let addr = InprocAddress::default(); - assert!(addr.is_default()); - let addr = InprocAddress::random(); - assert!(!addr.is_default()); - } -} diff --git a/comms/src/connection/zmq/mod.rs b/comms/src/connection/zmq/mod.rs deleted file mode 100644 index 2ae901f0b7..0000000000 --- a/comms/src/connection/zmq/mod.rs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -pub mod curve_keypair; - -mod context; -mod endpoint; -mod error; -mod inproc_address; - -pub use self::{ - context::ZmqContext, - curve_keypair::{CurveEncryption, CurvePublicKey, CurveSecretKey}, - endpoint::ZmqEndpoint, - error::ZmqError, - inproc_address::InprocAddress, -}; diff --git a/comms/src/connection_manager/common.rs b/comms/src/connection_manager/common.rs new file mode 100644 index 0000000000..30c5e40627 --- /dev/null +++ b/comms/src/connection_manager/common.rs @@ -0,0 +1,147 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::types::ConnectionDirection; +use crate::{ + connection_manager::error::ConnectionManagerError, + multiplexing::Yamux, + peer_manager::{AsyncPeerManager, NodeId, NodeIdentity, Peer, PeerFeatures, PeerFlags}, + proto::identity::PeerIdentityMsg, + protocol, + types::CommsPublicKey, +}; +use futures::StreamExt; +use log::*; +use tari_crypto::tari_utilities::ByteArray; + +const LOG_TARGET: &str = "comms::connection_manager::common"; + +pub async fn perform_identity_exchange( + muxer: &mut Yamux, + node_identity: &NodeIdentity, + direction: ConnectionDirection, +) -> Result +{ + let mut control = muxer.get_yamux_control(); + let stream = match direction { + ConnectionDirection::Inbound => muxer + .incoming_mut() + .next() + .await + .ok_or_else(|| ConnectionManagerError::IncomingListenerStreamClosed)??, + ConnectionDirection::Outbound => control.open_stream().await?, + }; + + debug!(target: LOG_TARGET, "{} substream opened to peer", direction); + + let peer_identity = protocol::identity_exchange(node_identity, direction, stream).await?; + Ok(peer_identity) +} + +/// Validate the node id against the given public key. Returns true if this is a valid base node +/// node id, otherwise false. +pub fn is_valid_base_node_node_id(node_id: &NodeId, public_key: &CommsPublicKey) -> bool { + match NodeId::from_key(public_key) { + Ok(expected_node_id) => &expected_node_id == node_id, + Err(_) => false, + } +} + +/// Validate the peer identity info. +/// +/// The following process is used to validate the peer: +/// 1. Check the offered node identity is a valid base node identity (TODO: This won't work for DAN nodes) +/// 1. Check if we know the peer, if so, is the peer banned, if so, return an error +/// 1. Check that the offered addresses are valid +/// 1. Update or add the peer, returning it's NodeId +pub fn validate_and_add_peer_from_peer_identity( + peer_manager: &AsyncPeerManager, + authenticated_public_key: CommsPublicKey, + peer_identity: PeerIdentityMsg, +) -> Result +{ + let peer_manager = peer_manager.inner(); + // Validate the given node id for base nodes + // TODO: This is technically a domain-level rule + let peer_node_id = + NodeId::from_bytes(&peer_identity.node_id).map_err(|_| ConnectionManagerError::PeerIdentityInvalidNodeId)?; + if !is_valid_base_node_node_id(&peer_node_id, &authenticated_public_key) { + return Err(ConnectionManagerError::PeerIdentityInvalidNodeId); + } + + // Check if we know the peer and if it is banned + let maybe_peer = match peer_manager.find_by_public_key(&authenticated_public_key) { + Ok(peer) if peer.is_banned() => return Err(ConnectionManagerError::PeerBanned), + Ok(peer) => Some(peer), + Err(err) if err.is_peer_not_found() => None, + Err(err) => return Err(err.into()), + }; + + let addresses = peer_identity + .addresses + .into_iter() + .filter_map(|addr_str| addr_str.parse().ok()) + .collect::>(); + + if addresses.len() == 0 { + return Err(ConnectionManagerError::PeerIdentityNoValidAddresses); + } + + // Add or update the peer + match maybe_peer { + Some(peer) => { + debug!( + target: LOG_TARGET, + "Peer '{}' already exists in peer list. Updating.", + peer.node_id.short_str() + ); + let mut conn_stats = peer.connection_stats; + conn_stats.set_connection_success(); + peer_manager.update_peer( + &authenticated_public_key, + Some(peer_node_id.clone()), + Some(addresses), + None, + Some(PeerFeatures::from_bits_truncate(peer_identity.features)), + Some(conn_stats), + )?; + }, + None => { + debug!( + target: LOG_TARGET, + "Peer '{}' does not exist in peer list. Adding.", + peer_node_id.short_str() + ); + let mut new_peer = Peer::new( + authenticated_public_key, + peer_node_id.clone(), + addresses.into(), + PeerFlags::empty(), + PeerFeatures::from_bits_truncate(peer_identity.features), + ); + new_peer.connection_stats.set_connection_success(); + peer_manager.add_peer(new_peer)?; + }, + } + + Ok(peer_node_id) +} diff --git a/comms/src/connection_manager/connections.rs b/comms/src/connection_manager/connections.rs deleted file mode 100644 index a5da5ae3d0..0000000000 --- a/comms/src/connection_manager/connections.rs +++ /dev/null @@ -1,339 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use log::*; - -use super::{ - error::ConnectionManagerError, - repository::{ConnectionRepository, Repository}, - types::PeerConnectionJoinHandle, - Result, -}; - -use crate::{connection::PeerConnection, peer_manager::node_id::NodeId}; - -use crate::connection::ConnectionError; -use std::{ - collections::HashMap, - sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard}, - time::Duration, -}; -use tari_utilities::thread_join::ThreadJoinWithTimeout; - -const LOG_TARGET: &str = "comms::connection_manager::connections"; - -/// Set the maximum waiting time for LivePeerConnections threads to join -const THREAD_JOIN_TIMEOUT_IN_MS: Duration = Duration::from_millis(100); - -/// Stores, and establishes the live peer connections -pub(super) struct LivePeerConnections { - repository: RwLock, - connection_thread_handles: RwLock>, - max_connections: usize, -} - -impl Default for LivePeerConnections { - fn default() -> Self { - Self { - repository: RwLock::new(ConnectionRepository::default()), - connection_thread_handles: RwLock::new(HashMap::new()), - max_connections: 100, - } - } -} - -impl LivePeerConnections { - #[cfg(test)] - pub fn new() -> Self { - Default::default() - } - - /// Create a new live peer connection - pub fn with_max_connections(max_connections: usize) -> Self { - Self { - max_connections, - ..Default::default() - } - } - - /// Get a connection by node id - pub fn get_connection(&self, node_id: &NodeId) -> Option> { - self.atomic_read(|lock| lock.get(node_id)) - } - - /// Get an active connection by node id - pub fn get_active_connection(&self, node_id: &NodeId) -> Option> { - self.atomic_read(|lock| { - lock.get(node_id) - .filter(|conn| conn.is_active()) - .map(|conn| conn.clone()) - }) - } - - /// Get number of active connections - pub fn get_active_connection_count(&self) -> usize { - self.atomic_read(|repo| repo.count_where(|conn| conn.is_active())) - } - - /// Add a connection to live peer connections - pub fn add_connection( - &self, - node_id: NodeId, - conn: Arc, - handle: PeerConnectionJoinHandle, - ) -> Result<()> - { - self.cleanup_inactive_connections(); - - self.atomic_write(|mut repo| { - let active_count = repo.count_where(|conn| conn.is_active()); - // If we're full drop the connection which has least recently been used - if active_count >= self.max_connections { - let recent_list = repo.sorted_recent_activity(); - if let Some(node_id) = recent_list.last().map(|(node_id, _)| node_id.clone().clone()) { - let conn = repo.remove(&node_id).expect( - "Invariant check: Unable to remove connection that was returned from \ - ConnectionRepository::sorted_recent_activity", - ); - - conn.shutdown() - .map_err(|err| ConnectionManagerError::ConnectionShutdownFailed(err))?; - } - } - - acquire_write_lock!(self.connection_thread_handles).insert(node_id.clone(), handle); - repo.insert(node_id, conn); - Ok(()) - }) - } - - /// Removes inactive connections from the live connection list - fn cleanup_inactive_connections(&self) { - self.atomic_write(|mut repo| { - // Drain the connections, and immediately drop them - let entries = repo.drain_filter(|(_, conn)| !conn.is_active() && !conn.is_initial()); - debug!(target: LOG_TARGET, "Discarding {} inactive connections", entries.len()); - }); - } - - /// If the connection exists, it is removed, shut down and returned. Otherwise - /// `ConnectionManagerError::PeerConnectionNotFound` is returned - pub fn shutdown_connection(&self, node_id: &NodeId) -> Result<(Arc, PeerConnectionJoinHandle)> { - self.atomic_write(|mut repo| { - let conn = repo - .remove(node_id) - .ok_or(ConnectionManagerError::PeerConnectionNotFound) - .map(|conn| conn.clone())?; - - let handle = acquire_write_lock!(self.connection_thread_handles) - .remove(node_id) - .expect( - "Invariant check: the peer connection join handle was not found. This is a bug as each peer \ - connection should have an associated join handle.", - ); - - debug!(target: LOG_TARGET, "Dropping connection for NodeID={}", node_id); - - conn.shutdown().map_err(ConnectionManagerError::ConnectionError)?; - - Ok((conn, handle)) - }) - } - - /// Send a shutdown signal to all peer connections, returning their worker thread handles - pub fn shutdown_all(self) -> HashMap { - info!(target: LOG_TARGET, "Shutting down all peer connections"); - self.atomic_read(|repo| { - repo.for_each(|conn| { - let _ = conn.shutdown(); - }); - }); - - acquire_lock!(self.connection_thread_handles, into_inner) - } - - /// Send a shutdown signal to all peer connections, and wait for all of them to - /// shut down, returning the result of the shutdown. - pub fn shutdown_joined(self) -> Vec> { - let handles = self.shutdown_all(); - - let mut results = vec![]; - for (_, handle) in handles.into_iter() { - results.push( - handle - .timeout_join(THREAD_JOIN_TIMEOUT_IN_MS) - .map_err(ConnectionError::ThreadJoinError) - .or_else(|err| { - error!(target: LOG_TARGET, "Failed to join: {:?}", err); - Err(err) - }), - ); - } - - results - } - - /// Returns true if the maximum number of connections has been reached, otherwise false - pub fn has_reached_max_active_connections(&self) -> bool { - let conn_count = self.get_active_connection_count(); - assert!( - conn_count <= self.max_connections, - "Invariant check: the active connection count is more than the max allowed connections. This is a bug as \ - active connections should never exceed max_connections." - ); - conn_count == self.max_connections - } - - /// Returns the number of connections (active and inactive) contained in the connection - /// repository. - #[cfg(test)] - pub fn repository_len(&self) -> usize { - let lock = acquire_read_lock!(self.repository); - lock.len() - } - - fn atomic_write(&self, f: F) -> T - where F: FnOnce(RwLockWriteGuard) -> T { - let lock = acquire_write_lock!(self.repository); - f(lock) - } - - fn atomic_read(&self, f: F) -> T - where F: FnOnce(RwLockReadGuard) -> T { - let lock = acquire_read_lock!(self.repository); - f(lock) - } -} - -#[cfg(test)] -mod test { - use super::*; - use rand::OsRng; - use std::thread; - use tari_crypto::{keys::PublicKey, ristretto::RistrettoPublicKey}; - - fn make_join_handle() -> PeerConnectionJoinHandle { - thread::spawn(move || Ok(())) - } - - fn make_node_id() -> NodeId { - let (_sk, pk) = RistrettoPublicKey::random_keypair(&mut OsRng::new().unwrap()); - NodeId::from_key(&pk).unwrap() - } - - #[test] - fn new() { - let connections = LivePeerConnections::new(); - assert_eq!(0, connections.get_active_connection_count()); - } - - #[test] - fn with_max_connections() { - let connections = LivePeerConnections::with_max_connections(10); - assert_eq!(0, connections.get_active_connection_count()); - assert_eq!(10, connections.max_connections); - } - - #[test] - fn crud() { - let connections = LivePeerConnections::new(); - - let node_id = make_node_id(); - let conn = Arc::new(PeerConnection::new()); - let join_handle = make_join_handle(); - - connections.add_connection(node_id.clone(), conn, join_handle).unwrap(); - assert_eq!( - 1, - acquire_read_lock!(connections.connection_thread_handles) - .values() - .count() - ); - connections.get_connection(&node_id).unwrap(); - connections.shutdown_connection(&node_id).unwrap(); - assert_eq!( - 0, - acquire_read_lock!(connections.connection_thread_handles) - .values() - .count() - ); - - assert_eq!(0, connections.get_active_connection_count()); - } - - #[test] - fn drop_connection_fail() { - let connections = LivePeerConnections::new(); - let node_id = make_node_id(); - match connections.shutdown_connection(&node_id) { - Err(ConnectionManagerError::PeerConnectionNotFound) => {}, - Err(err) => panic!("Unexpected error: {:?}", err), - Ok(_) => panic!("Unexpected Ok result"), - } - } - - #[test] - fn shutdown() { - let connections = LivePeerConnections::new(); - - for _i in 0..3 { - let node_id = make_node_id(); - let conn = Arc::new(PeerConnection::new()); - let join_handle = make_join_handle(); - - connections.add_connection(node_id, conn, join_handle).unwrap(); - } - - let results = connections.shutdown_joined(); - assert_eq!(3, results.len()); - assert!(results.iter().all(|r| r.is_ok())); - } - - #[test] - fn has_reached_max_active_connections() { - let connections = LivePeerConnections::with_max_connections(2); - - let add_active_conn = |node_id| { - let (conn, rx) = PeerConnection::new_with_connecting_state_for_test(); - let join_handle = make_join_handle(); - - (connections.add_connection(node_id, Arc::new(conn), join_handle), rx) - }; - - let mut receivers = Vec::new(); - let mut node_ids = Vec::new(); - for _ in 0..3 { - let node_id = make_node_id(); - let (res, rx) = add_active_conn(node_id.clone()); - node_ids.push(node_id); - receivers.push(rx); - res.unwrap(); - } - - assert_eq!(connections.repository_len(), 2); - assert!(connections.get_connection(&node_ids[0]).is_none()); - assert!(connections.get_connection(&node_ids[1]).is_some()); - assert!(connections.get_connection(&node_ids[2]).is_some()); - - drop(receivers); - } -} diff --git a/comms/src/connection_manager/dial_state.rs b/comms/src/connection_manager/dial_state.rs new file mode 100644 index 0000000000..0daaba3bec --- /dev/null +++ b/comms/src/connection_manager/dial_state.rs @@ -0,0 +1,72 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + connection_manager::{error::ConnectionManagerError, peer_connection::PeerConnection}, + peer_manager::Peer, +}; +use futures::channel::oneshot; +use tari_shutdown::ShutdownSignal; + +/// The state of the dial request +pub struct DialState { + /// Number of dial attempts + attempts: usize, + /// This peer being dialed + pub peer: Peer, + /// Cancel signal + cancel_signal: ShutdownSignal, + /// Reply channel for a connection result + pub reply_tx: oneshot::Sender>, +} + +impl DialState { + /// Create a new DialState for the given NodeId + pub fn new( + peer: Peer, + reply_tx: oneshot::Sender>, + cancel_signal: ShutdownSignal, + ) -> Self + { + Self { + peer, + attempts: 0, + reply_tx, + cancel_signal, + } + } + + /// Take ownership of the cancel receiver if this DialState has ownership of one + pub fn get_cancel_signal(&self) -> ShutdownSignal { + self.cancel_signal.clone() + } + + /// Increment the number of attempts + pub fn inc_attempts(&mut self) -> &mut Self { + self.attempts += 1; + self + } + + pub fn num_attempts(&self) -> usize { + self.attempts + } +} diff --git a/comms/src/connection_manager/dialer.rs b/comms/src/connection_manager/dialer.rs new file mode 100644 index 0000000000..22fdee474e --- /dev/null +++ b/comms/src/connection_manager/dialer.rs @@ -0,0 +1,545 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::{error::ConnectionManagerError, peer_connection::PeerConnection, types::ConnectionDirection}; +use crate::{ + backoff::Backoff, + connection_manager::{ + common, + dial_state::DialState, + manager::{ConnectionManagerConfig, ConnectionManagerEvent}, + peer_connection, + }, + multiaddr::Multiaddr, + multiplexing::Yamux, + noise::{NoiseConfig, NoiseSocket}, + peer_manager::{AsyncPeerManager, NodeId, NodeIdentity, Peer}, + protocol::ProtocolId, + transports::Transport, + types::CommsPublicKey, +}; +use futures::{ + channel::{mpsc, oneshot}, + future, + future::{BoxFuture, Either}, + pin_mut, + stream::{Fuse, FuturesUnordered}, + AsyncRead, + AsyncWrite, + FutureExt, + SinkExt, + StreamExt, +}; +use log::*; +use std::{collections::HashMap, sync::Arc}; +use tari_crypto::tari_utilities::hex::Hex; +use tari_shutdown::{Shutdown, ShutdownSignal}; +use tokio::{runtime, time}; + +const LOG_TARGET: &str = "comms::connection_manager::dialer"; + +type DialResult = Result<(NoiseSocket, Multiaddr), ConnectionManagerError>; +type DialFuturesUnordered = + FuturesUnordered)>>; + +#[derive(Debug)] +pub(crate) enum DialerRequest { + Dial( + Box, + oneshot::Sender>, + ), + CancelPendingDial(NodeId), +} + +pub struct Dialer { + executor: runtime::Handle, + config: ConnectionManagerConfig, + peer_manager: AsyncPeerManager, + node_identity: Arc, + transport: TTransport, + noise_config: NoiseConfig, + backoff: Arc, + request_rx: Fuse>, + cancel_signals: HashMap, + conn_man_notifier: mpsc::Sender, + shutdown: Option, + pending_dial_requests: HashMap>>>, + supported_protocols: Vec, +} + +impl Dialer +where + TTransport: Transport + Unpin + Send + Sync + Clone + 'static, + TTransport::Output: AsyncRead + AsyncWrite + Send + Sync + Unpin + 'static, + TBackoff: Backoff + Send + Sync + 'static, +{ + pub(crate) fn new( + executor: runtime::Handle, + config: ConnectionManagerConfig, + node_identity: Arc, + peer_manager: AsyncPeerManager, + transport: TTransport, + noise_config: NoiseConfig, + backoff: TBackoff, + request_rx: mpsc::Receiver, + conn_man_notifier: mpsc::Sender, + supported_protocols: Vec, + shutdown: ShutdownSignal, + ) -> Self + { + Self { + executor, + config, + node_identity, + peer_manager, + transport, + noise_config, + backoff: Arc::new(backoff), + request_rx: request_rx.fuse(), + cancel_signals: Default::default(), + conn_man_notifier, + shutdown: Some(shutdown), + pending_dial_requests: Default::default(), + supported_protocols, + } + } + + pub async fn run(mut self) { + let mut pending_dials = FuturesUnordered::new(); + let mut shutdown = self + .shutdown + .take() + .expect("Establisher initialized without a shutdown"); + debug!(target: LOG_TARGET, "Connection establisher started"); + loop { + futures::select! { + request = self.request_rx.select_next_some() => self.handle_request(&mut pending_dials, request), + (dial_state, dial_result) = pending_dials.select_next_some() => { + self.handle_dial_result(dial_state, dial_result).await; + } + _ = shutdown => { + info!(target: LOG_TARGET, "Connection establisher shutting down because the shutdown signal was received"); + self.cancel_all_dials(); + break; + } + } + } + } + + fn handle_request(&mut self, pending_dials: &mut DialFuturesUnordered, request: DialerRequest) { + use DialerRequest::*; + match request { + Dial(peer, reply_tx) => { + if !peer.is_persisted() { + log_if_error_fmt!( + target: LOG_TARGET, + reply_tx.send(Err(ConnectionManagerError::PeerNotPersisted)), + "Failed to send dial result for peer '{}'", + peer.node_id.short_str() + ); + return; + } + self.handle_dial_peer_request(pending_dials, *peer, reply_tx); + }, + CancelPendingDial(peer_id) => { + if let Some(mut s) = self.cancel_signals.remove(&peer_id) { + let _ = s.trigger(); + } + }, + } + } + + fn is_pending_dial(&self, node_id: &NodeId) -> bool { + self.cancel_signals.contains_key(node_id) + } + + fn cancel_all_dials(&mut self) { + debug!( + target: LOG_TARGET, + "Cancelling {} pending dial(s)", + self.cancel_signals.len() + ); + self.cancel_signals.drain().for_each(|(_, mut signal)| { + log_if_error_fmt!( + level: warn, + target: LOG_TARGET, + signal.trigger(), + "Shutdown trigger failed", + ); + }) + } + + async fn handle_dial_result( + &mut self, + dial_state: DialState, + dial_result: Result, + ) + { + let DialState { peer, reply_tx, .. } = dial_state; + + let node_id = peer.node_id.clone(); + let peer_id_short_str = peer.node_id.short_str(); + + let removed = self.cancel_signals.remove(&node_id); + drop(removed); + + match &dial_result { + Ok(conn) => { + debug!(target: LOG_TARGET, "Successfully dialed peer '{}'", peer_id_short_str); + self.notify_connection_manager(ConnectionManagerEvent::PeerConnected(conn.clone())) + .await + }, + Err(err) => { + debug!( + target: LOG_TARGET, + "Failed to dialed peer '{}' because '{:?}'", peer_id_short_str, err + ); + self.notify_connection_manager(ConnectionManagerEvent::PeerConnectFailed( + Box::new(node_id.clone()), + err.clone(), + )) + .await + }, + } + + if self.pending_dial_requests.contains_key(&node_id) { + self.reply_to_pending_requests(&node_id, dial_result.clone()); + } + + log_if_error_fmt!( + target: LOG_TARGET, + reply_tx.send(dial_result), + "Failed to send dial result reply for peer '{}'", + peer_id_short_str + ); + } + + pub async fn notify_connection_manager(&mut self, event: ConnectionManagerEvent) { + log_if_error!( + target: LOG_TARGET, + self.conn_man_notifier.send(event).await, + "Failed to publish event because '{error}'", + ); + } + + fn reply_to_pending_requests( + &mut self, + peer_node_id: &NodeId, + result: Result, + ) + { + self.pending_dial_requests + .remove(peer_node_id) + .and_then(|reply_oneshots| { + reply_oneshots.into_iter().for_each(|tx| { + log_if_error_fmt!( + target: LOG_TARGET, + tx.send(result.clone()), + "Failed to send dial result for peer '{}'", + peer_node_id.short_str() + ); + }); + Option::<()>::None + }); + } + + fn handle_dial_peer_request( + &mut self, + pending_dials: &mut DialFuturesUnordered, + peer: Peer, + reply_tx: oneshot::Sender>, + ) + { + if self.is_pending_dial(&peer.node_id) { + let entry = self + .pending_dial_requests + .entry(peer.node_id.clone()) + .or_insert(Vec::new()); + entry.push(reply_tx); + return; + } + + let transport = self.transport.clone(); + let dial_cancel = Shutdown::new(); + let cancel_signal = dial_cancel.to_signal(); + self.cancel_signals.insert(peer.node_id.clone(), dial_cancel); + + let backoff = Arc::clone(&self.backoff); + let max_attempts = self.config.max_dial_attempts; + + let dial_state = DialState::new(peer, reply_tx, cancel_signal); + let node_identity = Arc::clone(&self.node_identity); + let peer_manager = self.peer_manager.clone(); + let executor = self.executor.clone(); + let conn_man_notifier = self.conn_man_notifier.clone(); + let supported_protocols = self.supported_protocols.clone(); + let noise_config = self.noise_config.clone(); + + let dial_fut = async move { + let (dial_state, dial_result) = + Self::dial_peer_with_retry(dial_state, noise_config, transport, backoff, max_attempts).await; + + let cancel_signal = dial_state.get_cancel_signal(); + + match dial_result { + Ok((socket, addr)) => { + let authenticated_public_key = + match Self::check_authenticated_public_key(&socket, &dial_state.peer.public_key) { + Ok(pk) => pk, + Err(err) => { + return (dial_state, Err(err)); + }, + }; + + let upgrade_fut = Self::perform_socket_upgrade_procedure( + executor, + peer_manager, + node_identity, + socket, + addr, + authenticated_public_key, + conn_man_notifier, + supported_protocols, + ); + futures::pin_mut!(upgrade_fut); + let either = future::select(upgrade_fut, cancel_signal).await; + + match either { + Either::Left((result, _)) => (dial_state, result), + // Dial cancel was triggered + Either::Right(_) => (dial_state, Err(ConnectionManagerError::DialCancelled)), + } + }, + Err(err) => (dial_state, Err(err)), + } + }; + + pending_dials.push(dial_fut.boxed()); + } + + fn check_authenticated_public_key( + socket: &NoiseSocket, + expected_public_key: &CommsPublicKey, + ) -> Result + { + let authenticated_public_key = socket + .get_remote_public_key() + .ok_or_else(|| ConnectionManagerError::InvalidStaticPublicKey)?; + + if &authenticated_public_key != expected_public_key { + return Err(ConnectionManagerError::DialedPublicKeyMismatch); + } + + Ok(authenticated_public_key) + } + + async fn perform_socket_upgrade_procedure( + executor: runtime::Handle, + peer_manager: AsyncPeerManager, + node_identity: Arc, + socket: NoiseSocket, + dialed_addr: Multiaddr, + authenticated_public_key: CommsPublicKey, + conn_man_notifier: mpsc::Sender, + our_supported_protocols: Vec, + ) -> Result + { + static CONNECTION_DIRECTION: ConnectionDirection = ConnectionDirection::Outbound; + + let mut muxer = Yamux::upgrade_connection(executor.clone(), socket, CONNECTION_DIRECTION) + .await + .map_err(|err| ConnectionManagerError::YamuxUpgradeFailure(err.to_string()))?; + + trace!( + target: LOG_TARGET, + "Starting peer identity exchange for peer with public key '{}'", + authenticated_public_key + ); + let peer_identity = common::perform_identity_exchange(&mut muxer, &node_identity, CONNECTION_DIRECTION).await?; + + debug!( + target: LOG_TARGET, + "Peer identity exchange succeeded on Outbound connection for peer '{}'", + peer_identity.node_id.to_hex() + ); + trace!(target: LOG_TARGET, "{:?}", peer_identity); + + let peer_node_id = + common::validate_and_add_peer_from_peer_identity(&peer_manager, authenticated_public_key, peer_identity)?; + + debug!( + target: LOG_TARGET, + "[ThisNode={}] Peer '{}' added to peer list.", + node_identity.node_id().short_str(), + peer_node_id.short_str() + ); + + peer_connection::create( + executor, + muxer, + dialed_addr, + peer_node_id, + CONNECTION_DIRECTION, + conn_man_notifier, + our_supported_protocols, + ) + } + + async fn dial_peer_with_retry( + dial_state: DialState, + noise_config: NoiseConfig, + transport: TTransport, + backoff: Arc, + max_attempts: usize, + ) -> (DialState, DialResult) + { + // Container for dial state + let mut dial_state = Some(dial_state); + let mut transport = Some(transport); + + loop { + let mut current_state = dial_state.take().expect("dial_state must own current dial state"); + current_state.inc_attempts(); + let current_transport = transport.take().expect("transport must own current dial state"); + let backoff_duration = backoff.calculate_backoff(current_state.num_attempts()); + debug!( + target: LOG_TARGET, + "[Attempt {}] Will attempt connection to peer '{}' in {} second(s)", + current_state.num_attempts(), + current_state.peer.node_id.short_str(), + backoff_duration.as_secs() + ); + let mut delay = time::delay_for(backoff_duration).fuse(); + let mut cancel_signal = current_state.get_cancel_signal(); + futures::select! { + _ = delay => { + debug!(target: LOG_TARGET, "[Attempt {}] Connecting to peer '{}'", current_state.num_attempts(), current_state.peer.node_id.short_str()); + match Self::dial_peer(current_state, &noise_config, ¤t_transport).await { + (state, Ok((socket, addr))) => { + debug!(target: LOG_TARGET, "Dial succeeded for peer '{}' after {} attempt(s)", state.peer.node_id.short_str(), state.num_attempts()); + break (state, Ok((socket, addr))); + }, + // Inflight dial was cancelled + (state, Err(ConnectionManagerError::DialCancelled)) => break (state, Err(ConnectionManagerError::DialCancelled)), + (mut state, Err(err)) => { + if state.num_attempts() > max_attempts { + break (state, Err(ConnectionManagerError::ConnectFailedMaximumAttemptsReached)); + } + + // Put the dial state and transport back for the retry + dial_state = Some(state); + transport = Some(current_transport); + } + } + }, + // Delayed dial was cancelled + _ = cancel_signal => { + debug!(target: LOG_TARGET, "[Attempt {}] Connecting to peer '{}'...", current_state.num_attempts(), current_state.peer.node_id.short_str()); + break (current_state, Err(ConnectionManagerError::DialCancelled)); + } + } + } + } + + /// Attempts to dial a peer sequentially on all addresses. + /// Returns ownership of the given `DialState` and a success or failure result for the dial, + /// or None if the dial was cancelled inflight + async fn dial_peer( + dial_state: DialState, + noise_config: &NoiseConfig, + transport: &TTransport, + ) -> ( + DialState, + Result<(NoiseSocket, Multiaddr), ConnectionManagerError>, + ) + { + let mut addr_iter = dial_state.peer.addresses.address_iter(); + let cancel_signal = dial_state.get_cancel_signal(); + loop { + let result = match addr_iter.next() { + Some(address) => { + debug!( + target: LOG_TARGET, + "Attempting address '{}' for peer '{}'", + address, + dial_state.peer.node_id.short_str() + ); + + let dial_fut = async move { + let socket = transport + .dial(address.clone()) + .map_err(|err| ConnectionManagerError::TransportError(err.to_string()))? + .await + .map_err(|err| ConnectionManagerError::TransportError(err.to_string()))?; + debug!( + target: LOG_TARGET, + "Socket established on '{}'. Performing noise upgrade protocol", address + ); + let noise_socket = noise_config + .upgrade_socket(socket, ConnectionDirection::Outbound) + .await + .map_err(|err| ConnectionManagerError::NoiseError(err.to_string()))?; + Result::<_, ConnectionManagerError>::Ok(noise_socket) + }; + + pin_mut!(dial_fut); + let either = future::select(dial_fut, cancel_signal.clone()).await; + match either { + Either::Left((Ok(noise_socket), _)) => Ok((noise_socket, address.clone())), + Either::Left((Err(err), _)) => { + debug!( + target: LOG_TARGET, + "(Attempt {}) Dial failed on address '{}' for peer '{}' because '{}'", + dial_state.num_attempts(), + address, + dial_state.peer.node_id.short_str(), + err, + ); + // Try the next address + continue; + }, + Either::Right((cancel_result, _)) => { + debug!( + target: LOG_TARGET, + "Dial for peer '{}' cancelled", + dial_state.peer.node_id.short_str() + ); + log_if_error!( + level: warn, + target: LOG_TARGET, + cancel_result, + "Cancel channel error during dial: {}", + ); + Err(ConnectionManagerError::DialCancelled) + }, + } + }, + // No more addresses to try - returning failure + None => Err(ConnectionManagerError::DialConnectFailedAllAddresses), + }; + + drop(addr_iter); + + break (dial_state, result); + } + } +} diff --git a/comms/src/connection_manager/error.rs b/comms/src/connection_manager/error.rs index e6f983fd02..ad8b1935f5 100644 --- a/comms/src/connection_manager/error.rs +++ b/comms/src/connection_manager/error.rs @@ -1,80 +1,92 @@ -// Copyright 2019 The Tari Project +// Copyright 2019, The Tari Project // -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: // -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. // -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. // -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. // -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::{ - connection::ConnectionError, - control_service::{messages::RejectReason, ControlServiceError}, - message::MessageError, - peer_manager::{node_identity::NodeIdentityError, PeerManagerError}, + peer_manager::PeerManagerError, + protocol::{IdentityProtocolError, ProtocolError}, }; use derive_error::Error; -use tari_utilities::{ - ciphers::cipher::CipherError, - message_format::MessageFormatError, - thread_join::ThreadError, - ByteArrayError, -}; +use futures::channel::mpsc; -#[derive(Error, Debug)] +#[derive(Debug, Error, Clone)] pub enum ConnectionManagerError { - /// There are no available peer connection ports - NoAvailablePeerConnectionPort, - /// The peer connection could not be found - PeerConnectionNotFound, - /// The peer could not be found - PeerNotFound, - // Error establishing connection - ConnectionError(ConnectionError), - #[error(no_from)] - CurveEncryptionGenerateError(ConnectionError), - MessageFormatError(MessageFormatError), - MessageError(MessageError), - /// The global node identity has not been set - GlobalNodeIdentityNotSet, - SharedSecretSerializationError(ByteArrayError), - CipherError(CipherError), PeerManagerError(PeerManagerError), - /// The connection could not connect within the maximum number of attempts - MaxConnnectionAttemptsExceeded, - /// Problem creating or loading datastore - DatastoreError, - /// Connection timed out before it was able to connect - TimeoutBeforeConnected, - /// The maximum number of peer connections has been reached - MaxConnectionsReached, - /// Failed to shutdown a peer connection - #[error(no_from)] - ConnectionShutdownFailed(ConnectionError), - PeerConnectionThreadError(ThreadError), - #[error(msg_embedded, non_std, no_from)] - ControlServicePingPongFailed(String), - #[error(msg_embedded, non_std, no_from)] - SendRequestConnectionFailed(String), - /// Failed to receive a connection request outcome message - ConnectionRequestOutcomeRecvFail, - /// The request to establish a peer connection was rejected by the destination peer's control port - ConnectionRejected(RejectReason), - /// Failed to receive a connection request outcome before the timeout - ConnectionRequestOutcomeTimeout, - ControlServiceError(ControlServiceError), - NodeIdentityError(NodeIdentityError), + /// Cannot connect to peers which are not persisted in the peer manager database. + PeerNotPersisted, + /// Failed to send request to ConnectionManagerActor. Channel closed. + SendToActorFailed, + /// Request was canceled before the response could be sent + ActorRequestCanceled, + /// The dial reply channel was closed when sending a reply + DialReplyChannelClosed, + /// Failed to connect on all addresses for peer + DialConnectFailedAllAddresses, + /// Failed to connect to peer within the maximum number of attempts + ConnectFailedMaximumAttemptsReached, + #[error(msg_embedded, no_from, non_std)] + YamuxConnectionError(String), + /// Establisher channel is closed or full + /// Failed to perform yamux upgrade on socket + #[error(msg_embedded, no_from, non_std)] + YamuxUpgradeFailure(String), + /// Establisher channel is closed or full + EstablisherChannelError, + #[error(msg_embedded, no_from, non_std)] + TransportError(String), + /// The peer authenticated to a public key which did not match the dialed peer's public key + DialedPublicKeyMismatch, + /// The noise transport failed to provide a valid static public key for the peer + InvalidStaticPublicKey, + // This is a String because we need this error to be clonable so that we can + // send the same response to multiple requesters + #[error(msg_embedded, no_from, non_std)] + NoiseError(String), + /// Incoming listener stream unexpectedly closed + IncomingListenerStreamClosed, + /// The peer offered a NodeId that failed to validate against it's public key + PeerIdentityInvalidNodeId, + /// Peer is banned, denying connection + PeerBanned, + /// Unable to parse any of the network addresses offered by the connecting peer + PeerIdentityNoValidAddresses, + IdentityProtocolError(IdentityProtocolError), + /// The dial was cancelled + DialCancelled, + /// The peer is offline and will not be dialed + PeerOffline, +} + +impl From for ConnectionManagerError { + fn from(err: yamux::ConnectionError) -> Self { + ConnectionManagerError::YamuxConnectionError(err.to_string()) + } +} + +#[derive(Debug, Error)] +pub enum PeerConnectionError { + YamuxConnectionError(yamux::ConnectionError), + /// Internal oneshot reply channel was unexpectedly cancelled + InternalReplyCancelled, + /// Failed to send internal request + InternalRequestSendFailed(mpsc::SendError), + ProtocolError(ProtocolError), } diff --git a/comms/src/connection_manager/establisher.rs b/comms/src/connection_manager/establisher.rs deleted file mode 100644 index 2e6c79ea07..0000000000 --- a/comms/src/connection_manager/establisher.rs +++ /dev/null @@ -1,312 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use super::{error::ConnectionManagerError, types::PeerConnectionJoinHandle, Result}; -use crate::{ - connection::{ - curve_keypair::{CurvePublicKey, CurveSecretKey}, - net_address::ip::SocketAddress, - peer_connection::ConnectionId, - types::{Direction, Linger}, - Connection, - CurveEncryption, - EstablishedConnection, - InprocAddress, - NetAddress, - PeerConnection, - PeerConnectionContextBuilder, - ZmqContext, - }, - control_service::ControlServiceClient, - peer_manager::{NodeIdentity, Peer, PeerManager}, -}; -use log::*; -use std::{net::IpAddr, sync::Arc, time::Duration}; - -const LOG_TARGET: &str = "comms::connection_manager::establisher"; - -/// Configuration for peer connections which are produced by the ConnectionEstablisher -/// These are the common properties which are shared across all peer connections -#[derive(Clone)] -pub struct PeerConnectionConfig { - /// Maximum number of peer connections. Newer connections will be rejected until there are - /// less than `max_connections` active connections. - pub max_connections: usize, - /// Maximum size of inbound messages - messages larger than this will be dropped - pub max_message_size: u64, - /// The number of connection attempts to make to one address before giving up - pub max_connect_retries: u16, - /// The address of the SOCKS proxy to use for this connection - pub socks_proxy_address: Option, - /// The address to forward all the messages received from this peer connection - pub message_sink_address: InprocAddress, - /// The host to bind to when creating inbound connections - pub host: IpAddr, - /// The length of time to wait for the requested peer connection to be established before timing out. - /// Depending on the network, this should be long enough to allow a single back-and-forth - /// communication between peers. - pub peer_connection_establish_timeout: Duration, -} - -impl Default for PeerConnectionConfig { - fn default() -> Self { - Self { - max_connections: 100, - max_message_size: 1024 * 1024, - max_connect_retries: 5, - socks_proxy_address: None, - message_sink_address: Default::default(), - peer_connection_establish_timeout: Duration::from_secs(10), - host: "0.0.0.0".parse().unwrap(), - } - } -} - -/// ## ConnectionEstablisher -/// -/// This component is responsible for creating encrypted connections to peers and updating -/// the peer stats for failed/successful connection attempts. This component does not hold any -/// connections, but returns them so that the caller may use them as needed. This component does -/// not complete the peer connection protocol, it simply creates connections with some reliability. -pub struct ConnectionEstablisher { - context: ZmqContext, - config: PeerConnectionConfig, - node_identity: Arc, - peer_manager: Arc, -} - -impl ConnectionEstablisher { - /// Create a new ConnectionEstablisher. - pub fn new( - context: ZmqContext, - node_identity: Arc, - config: PeerConnectionConfig, - peer_manager: Arc, - ) -> Self - { - Self { - context, - node_identity, - config, - peer_manager, - } - } - - /// Returns the peer connection config - pub fn get_config(&self) -> &PeerConnectionConfig { - &self.config - } - - /// Attempt to establish a control service connection to one of the peer's addresses - /// - /// ### Arguments - /// - `peer`: `&Peer` - The peer to connect to - pub fn connect_control_service_client(&self, peer: &Peer) -> Result { - let config = &self.config; - - self.peer_manager - .reset_connection_attempts(&peer.node_id) - .map_err(ConnectionManagerError::PeerManagerError)?; - - info!( - target: LOG_TARGET, - "Starting {} attempt(s) to connect to control port for NodeId={}", - peer.addresses.len(), - peer.node_id - ); - - let maybe_client = self.attempt_control_port_connection_for_peer(&peer, || { - let address = self - .peer_manager - .get_best_net_address(&peer.node_id) - .map_err(ConnectionManagerError::PeerManagerError)?; - debug!(target: LOG_TARGET, "Attempting to connect to {}", address); - - let conn = Connection::new(&self.context, Direction::Outbound) - .set_name(format!("out-control-port-conn-{}", peer.node_id).as_str()) - .set_linger(Linger::Never) - .set_backlog(1) - .set_socks_proxy_addr(config.socks_proxy_address.clone()) - .set_max_message_size(Some(config.max_message_size)) - .establish(&address) - .map_err(ConnectionManagerError::ConnectionError)?; - - Ok((conn, address)) - })?; - - // Reset the connection attempts for this peer - // TODO(sdbondi): This is a good reason why peer manager shouldn't be managing connection - // attempts. Peer manager should be simplified a little to return an iterator - // sorted from 'best' to 'worst' net address without having to modify shared - // state. - self.peer_manager - .reset_connection_attempts(&peer.node_id) - .map_err(ConnectionManagerError::PeerManagerError)?; - - match maybe_client { - Some(client) => Ok(client), - None => Err(ConnectionManagerError::MaxConnnectionAttemptsExceeded), - } - } - - fn attempt_control_port_connection_for_peer( - &self, - peer: &Peer, - connection_factory: impl Fn() -> Result<(EstablishedConnection, NetAddress)>, - ) -> Result> - { - let num_attempts = peer.addresses.len(); - let mut current_attempts = 1; - loop { - let (conn, address) = connection_factory()?; - let client = ControlServiceClient::new(Arc::clone(&self.node_identity), peer.public_key.clone(), conn); - - if let Some(_) = client.ping_pong(self.config.peer_connection_establish_timeout).ok() { - self.peer_manager - .mark_successful_connection_attempt(&address) - .map_err(ConnectionManagerError::PeerManagerError)?; - - break Ok(Some(client)); - } - - self.peer_manager - .mark_failed_connection_attempt(&address) - .map_err(ConnectionManagerError::PeerManagerError)?; - - if current_attempts >= num_attempts { - break Ok(None); - } - - current_attempts += 1; - } - } - - /// Create a new outbound PeerConnection - /// - /// ### Arguments - /// `conn_id`: [ConnectionId] - The id to use for the connection - /// `address`: [NetAddress] - The [NetAddress] to connect to - /// `curve_public_key`: [&NetAddress] - The Curve25519 public key of the destination connection - /// - /// Returns an Arc<[PeerConnection]> in `Connected` state and the [std::thread::JoinHandle] of the - /// [PeerConnection] worker thread or an error. - pub fn establish_outbound_peer_connection( - &self, - conn_id: ConnectionId, - address: NetAddress, - curve_public_key: CurvePublicKey, - ) -> Result<(Arc, PeerConnectionJoinHandle)> - { - debug!(target: LOG_TARGET, "Establishing outbound connection to {}", address); - let (secret_key, public_key) = CurveEncryption::generate_keypair()?; - - let context = self - .new_context_builder() - .set_id(conn_id) - .set_direction(Direction::Outbound) - .set_address(address) - .set_curve_encryption(CurveEncryption::Client { - secret_key, - public_key, - server_public_key: curve_public_key, - }) - .build()?; - - let mut connection = PeerConnection::new(); - let worker_handle = connection.start(context)?; - connection - .wait_connected_or_failure(&self.config.peer_connection_establish_timeout) - .or_else(|err| { - error!(target: LOG_TARGET, "Outbound connection failed: {:?}", err); - Err(ConnectionManagerError::ConnectionError(err)) - })?; - - let connection = Arc::new(connection); - - Ok((connection, worker_handle)) - } - - /// Establish a new inbound peer connection. - /// - /// ### Arguments - /// `conn_id`: [ConnectionId] - The id to use for the connection - /// `curve_secret_key`: [&CurveSecretKey] - The zmq Curve25519 secret key for the connection - /// - /// Returns an Arc<[PeerConnection]> in `Listening` state and the [std::thread::JoinHandle] of the - /// [PeerConnection] worker thread or an error. - pub fn establish_inbound_peer_connection( - &self, - conn_id: ConnectionId, - curve_secret_key: CurveSecretKey, - ) -> Result<(Arc, PeerConnectionJoinHandle)> - { - // Providing port 0 tells the OS to allocate a port for us - let address = NetAddress::IP((self.config.host, 0).into()); - debug!(target: LOG_TARGET, "Establishing inbound connection to {}", address); - - let context = self - .new_context_builder() - .set_id(conn_id) - .set_direction(Direction::Inbound) - .set_address(address) - .set_curve_encryption(CurveEncryption::Server { - secret_key: curve_secret_key, - }) - .build()?; - - let mut connection = PeerConnection::new(); - let worker_handle = connection.start(context)?; - connection - .wait_listening_or_failure(&self.config.peer_connection_establish_timeout) - .or_else(|err| { - error!(target: LOG_TARGET, "Unable to establish inbound connection: {:?}", err); - Err(ConnectionManagerError::ConnectionError(err)) - })?; - - debug!( - target: LOG_TARGET, - "Inbound connection established on (NetAddress={:?}, SocketAddress={:?})", - connection.get_address(), - connection.get_connected_address() - ); - - let connection = Arc::new(connection); - - Ok((connection, worker_handle)) - } - - fn new_context_builder(&self) -> PeerConnectionContextBuilder { - let config = &self.config; - - let mut builder = PeerConnectionContextBuilder::new() - .set_context(&self.context) - .set_max_msg_size(config.max_message_size) - .set_message_sink_address(config.message_sink_address.clone()) - .set_max_retry_attempts(config.max_connect_retries); - - if let Some(ref addr) = config.socks_proxy_address { - builder = builder.set_socks_proxy(addr.clone()); - } - - builder - } -} diff --git a/comms/src/connection_manager/listener.rs b/comms/src/connection_manager/listener.rs new file mode 100644 index 0000000000..8aafdba47d --- /dev/null +++ b/comms/src/connection_manager/listener.rs @@ -0,0 +1,269 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::{ + common, + error::ConnectionManagerError, + peer_connection::{self, PeerConnection}, + types::ConnectionDirection, + ConnectionManagerConfig, + ConnectionManagerEvent, +}; +use crate::{ + bounded_executor::BoundedExecutor, + multiaddr::Multiaddr, + multiplexing::Yamux, + noise::NoiseConfig, + peer_manager::{AsyncPeerManager, NodeIdentity}, + protocol::ProtocolId, + transports::Transport, +}; +use futures::{channel::mpsc, AsyncRead, AsyncWrite, SinkExt, StreamExt}; +use log::*; +use std::sync::Arc; +use tari_crypto::tari_utilities::hex::Hex; +use tari_shutdown::ShutdownSignal; +use tokio::runtime; + +const LOG_TARGET: &str = "comms::connection_manager::listener"; + +pub struct PeerListener { + listen_address: Multiaddr, + executor: runtime::Handle, + bounded_executor: BoundedExecutor, + conn_man_notifier: mpsc::Sender, + shutdown_signal: Option, + transport: TTransport, + noise_config: NoiseConfig, + peer_manager: AsyncPeerManager, + node_identity: Arc, + listening_address: Option, + our_supported_protocols: Vec, +} + +impl PeerListener +where + TTransport: Transport, + TTransport::Output: AsyncRead + AsyncWrite + Send + Unpin + 'static, +{ + pub(crate) fn new( + executor: runtime::Handle, + config: ConnectionManagerConfig, + transport: TTransport, + noise_config: NoiseConfig, + conn_man_notifier: mpsc::Sender, + peer_manager: AsyncPeerManager, + node_identity: Arc, + supported_protocols: Vec, + shutdown_signal: ShutdownSignal, + ) -> Self + { + Self { + executor: executor.clone(), + listen_address: config.listener_address, + transport, + noise_config, + conn_man_notifier, + peer_manager, + node_identity, + shutdown_signal: Some(shutdown_signal), + listening_address: None, + our_supported_protocols: supported_protocols, + bounded_executor: BoundedExecutor::new(executor, config.max_simultaneous_inbound_connects), + } + } + + pub async fn run(mut self) { + let mut shutdown_signal = self + .shutdown_signal + .take() + .expect("PeerListener initialized without a ShutdownSignal"); + + match self.listen().await { + Ok((inbound, address)) => { + let inbound = inbound.fuse(); + futures::pin_mut!(inbound); + + info!(target: LOG_TARGET, "Listening for peer connections on '{}'", address); + self.listening_address = Some(address.clone()); + + self.send_event(ConnectionManagerEvent::Listening(address)).await; + + loop { + futures::select! { + inbound_result = inbound.select_next_some() => { + if let Some((inbound_future, peer_addr)) = log_if_error!(target: LOG_TARGET, inbound_result, "Inbound connection failed because '{error}'",) { + if let Some(socket) = log_if_error!(target: LOG_TARGET, inbound_future.await, "Inbound connection failed because '{error}'",) { + self.spawn_listen_task(socket, peer_addr).await; + } + } + }, + _ = shutdown_signal => { + info!(target: LOG_TARGET, "PeerListener is shutting down because the shutdown signal was triggered"); + break; + }, + } + } + }, + Err(err) => { + error!(target: LOG_TARGET, "PeerListener was unable to start because '{}'", err); + self.send_event(ConnectionManagerEvent::ListenFailed(err)).await; + }, + } + } + + async fn spawn_listen_task(&self, socket: TTransport::Output, peer_addr: Multiaddr) { + let executor = self.executor.clone(); + let node_identity = self.node_identity.clone(); + let peer_manager = self.peer_manager.clone(); + let mut conn_man_notifier = self.conn_man_notifier.clone(); + let noise_config = self.noise_config.clone(); + let our_supported_protocols = self.our_supported_protocols.clone(); + + // This will block (asynchronously) if we have reached the maximum simultaneous connections, creating + // back-pressure on nodes connecting to this node + self.bounded_executor + .spawn(async move { + let this_node_id_str = node_identity.node_id().short_str(); + let result = Self::perform_socket_upgrade_procedure( + executor, + node_identity, + peer_manager, + noise_config, + conn_man_notifier.clone(), + socket, + peer_addr, + our_supported_protocols, + ) + .await; + + match result { + Ok(peer_conn) => { + log_if_error!( + target: LOG_TARGET, + conn_man_notifier + .send(ConnectionManagerEvent::PeerConnected(peer_conn)) + .await, + "Failed to publish event because '{error}'", + ); + }, + Err(err) => { + debug!( + target: LOG_TARGET, + "[ThisNode={}] Peer connection upgrade failed for peer because '{:?}'", + this_node_id_str, + err + ); + log_if_error!( + target: LOG_TARGET, + conn_man_notifier + .send(ConnectionManagerEvent::PeerInboundConnectFailed(err)) + .await, + "Failed to publish event because '{error}'", + ); + }, + } + }) + .await; + } + + async fn send_event(&mut self, event: ConnectionManagerEvent) { + log_if_error_fmt!( + target: LOG_TARGET, + self.conn_man_notifier.send(event).await, + "Failed to send connection manager event in listener", + ); + } + + async fn perform_socket_upgrade_procedure( + executor: runtime::Handle, + node_identity: Arc, + peer_manager: AsyncPeerManager, + noise_config: NoiseConfig, + conn_man_notifier: mpsc::Sender, + socket: TTransport::Output, + peer_addr: Multiaddr, + our_supported_protocols: Vec, + ) -> Result + { + static CONNECTION_DIRECTION: ConnectionDirection = ConnectionDirection::Inbound; + debug!( + target: LOG_TARGET, + "Starting noise protocol upgrade for peer at address '{}'", peer_addr + ); + + let noise_socket = noise_config + .upgrade_socket(socket, CONNECTION_DIRECTION) + .await + .map_err(|err| ConnectionManagerError::NoiseError(err.to_string()))?; + + let authenticated_public_key = noise_socket + .get_remote_public_key() + .ok_or_else(|| ConnectionManagerError::InvalidStaticPublicKey)?; + + let mut muxer = Yamux::upgrade_connection(executor.clone(), noise_socket, CONNECTION_DIRECTION) + .await + .map_err(|err| ConnectionManagerError::YamuxUpgradeFailure(err.to_string()))?; + + trace!( + target: LOG_TARGET, + "Starting peer identity exchange for peer with public key '{}'", + authenticated_public_key + ); + let peer_identity = common::perform_identity_exchange(&mut muxer, &node_identity, CONNECTION_DIRECTION).await?; + + debug!( + target: LOG_TARGET, + "Peer identity exchange succeeded on Inbound connection for peer '{}'", + peer_identity.node_id.to_hex() + ); + trace!(target: LOG_TARGET, "{:?}", peer_identity); + + let peer_node_id = + common::validate_and_add_peer_from_peer_identity(&peer_manager, authenticated_public_key, peer_identity)?; + + debug!( + target: LOG_TARGET, + "[ThisNode={}] Peer '{}' added to peer list.", + node_identity.node_id().short_str(), + peer_node_id.short_str() + ); + + peer_connection::create( + executor, + muxer, + peer_addr, + peer_node_id, + CONNECTION_DIRECTION, + conn_man_notifier, + our_supported_protocols, + ) + } + + async fn listen(&self) -> Result<(TTransport::Listener, Multiaddr), ConnectionManagerError> { + self.transport + .listen(self.listen_address.clone()) + .map_err(|err| ConnectionManagerError::TransportError(err.to_string()))? + .await + .map_err(|err| ConnectionManagerError::TransportError(err.to_string())) + } +} diff --git a/comms/src/connection_manager/manager.rs b/comms/src/connection_manager/manager.rs index 449908e5a0..bbf5a6c99b 100644 --- a/comms/src/connection_manager/manager.rs +++ b/comms/src/connection_manager/manager.rs @@ -1,443 +1,586 @@ -// Copyright 2019 The Tari Project +// Copyright 2019, The Tari Project // -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: // -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. // -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. // -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. // -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use super::{ - connections::LivePeerConnections, - establisher::ConnectionEstablisher, - protocol::PeerConnectionProtocol, - ConnectionManagerError, - EstablishLockResult, - PeerConnectionConfig, - Result, + dialer::{Dialer, DialerRequest}, + error::ConnectionManagerError, + listener::PeerListener, + peer_connection::{ConnId, PeerConnection}, + requester::ConnectionManagerRequest, + types::ConnectionDirection, }; use crate::{ - connection::{ - zmq::InprocAddress, - ConnectionError, - CurveEncryption, - CurvePublicKey, - PeerConnection, - PeerConnectionState, - ZmqContext, - }, - control_service::messages::RejectReason, - peer_manager::{NodeId, NodeIdentity, Peer, PeerManager}, + backoff::Backoff, + noise::NoiseConfig, + peer_manager::{AsyncPeerManager, NodeId, NodeIdentity}, + protocol::{ProtocolEvent, ProtocolId, Protocols}, + transports::Transport, + types::DEFAULT_LISTENER_ADDRESS, }; -use log::*; -use std::{ - collections::HashMap, - result, - sync::{Arc, Mutex}, - time::Duration, +use futures::{ + channel::{mpsc, oneshot}, + stream::Fuse, + AsyncRead, + AsyncWrite, + SinkExt, + StreamExt, }; -use tari_utilities::thread_join::thread_join::ThreadJoinWithTimeout; +use log::*; +use multiaddr::Multiaddr; +use std::{collections::HashMap, fmt, sync::Arc}; +use tari_shutdown::{Shutdown, ShutdownSignal}; +use time::Duration; +use tokio::{runtime, sync::broadcast, task, time}; const LOG_TARGET: &str = "comms::connection_manager::manager"; -pub struct ConnectionManager { +const EVENT_CHANNEL_SIZE: usize = 32; +const ESTABLISHER_CHANNEL_SIZE: usize = 32; + +#[derive(Debug)] +pub enum ConnectionManagerEvent { + // Peer connection + PeerConnected(PeerConnection), + PeerDisconnected(Box), + PeerConnectFailed(Box, ConnectionManagerError), + PeerConnectWillClose(ConnId, Box, ConnectionDirection), + PeerInboundConnectFailed(ConnectionManagerError), + + // Listener + Listening(Multiaddr), + ListenFailed(ConnectionManagerError), + + // Substreams + NewInboundSubstream(Box, ProtocolId, yamux::Stream), +} + +impl fmt::Display for ConnectionManagerEvent { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + use ConnectionManagerEvent::*; + match self { + PeerConnected(conn) => write!(f, "PeerConnected({})", conn), + PeerDisconnected(node_id) => write!(f, "PeerDisconnected({})", node_id.short_str()), + PeerConnectFailed(node_id, err) => write!(f, "PeerConnectFailed({}, {:?})", node_id.short_str(), err), + PeerConnectWillClose(id, node_id, direction) => write!( + f, + "PeerConnectWillClose({}, {}, {})", + id, + node_id.short_str(), + direction + ), + PeerInboundConnectFailed(err) => write!(f, "PeerInboundConnectFailed({:?})", err), + Listening(addr) => write!(f, "Listening({})", addr), + ListenFailed(err) => write!(f, "ListenFailed({:?})", err), + NewInboundSubstream(node_id, protocol, _) => write!( + f, + "NewInboundSubstream({}, {}, Stream)", + node_id.short_str(), + String::from_utf8_lossy(protocol) + ), + } + } +} + +#[derive(Debug, Clone)] +pub struct ConnectionManagerConfig { + /// The address to listen on for incoming connections. This address must be supported by the transport. + /// Default: DEFAULT_LISTENER_ADDRESS constant + pub listener_address: Multiaddr, + /// The number of dial attempts to make before giving up. Default: 3 + pub max_dial_attempts: usize, + /// The maximum number of connection tasks that will be spawned at the same time. Once this limit is reached, peers + /// attempting to connect will have to wait for another connection attempt to complete. Default: 20 + pub max_simultaneous_inbound_connects: usize, + /// The period of time to keep the peer connection around before disconnecting. Default: 3s + pub disconnect_linger: Duration, +} + +impl Default for ConnectionManagerConfig { + fn default() -> Self { + Self { + listener_address: DEFAULT_LISTENER_ADDRESS + .parse() + .expect("DEFAULT_LISTENER_ADDRESS is malformed"), + max_dial_attempts: 3, + max_simultaneous_inbound_connects: 20, + disconnect_linger: Duration::from_secs(3), + } + } +} + +pub struct ConnectionManager { + executor: runtime::Handle, + config: ConnectionManagerConfig, + request_rx: Fuse>, + internal_event_rx: Fuse>, + dialer_tx: mpsc::Sender, + dialer: Option>, + listener: Option>, + peer_manager: AsyncPeerManager, node_identity: Arc, - connections: LivePeerConnections, - establisher: Arc, - peer_manager: Arc, - establish_locks: Mutex>>>, + active_connections: HashMap, + shutdown_signal: Option, + protocols: Protocols, + listener_address: Option, + listening_notifiers: Vec>, + connection_manager_events_tx: broadcast::Sender>, + complete_trigger: Shutdown, } -impl ConnectionManager { - /// Create a new connection manager +impl ConnectionManager +where + TTransport: Transport + Unpin + Send + Sync + Clone + 'static, + TTransport::Output: AsyncRead + AsyncWrite + Send + Sync + Unpin + 'static, + TBackoff: Backoff + Send + Sync + 'static, +{ pub fn new( - zmq_context: ZmqContext, + config: ConnectionManagerConfig, + executor: runtime::Handle, + transport: TTransport, + noise_config: NoiseConfig, + backoff: TBackoff, + request_rx: mpsc::Receiver, node_identity: Arc, - peer_manager: Arc, - config: PeerConnectionConfig, + peer_manager: AsyncPeerManager, + protocols: Protocols, + connection_manager_events_tx: broadcast::Sender>, + shutdown_signal: ShutdownSignal, ) -> Self { + let (internal_event_tx, internal_event_rx) = mpsc::channel(EVENT_CHANNEL_SIZE); + + let (establisher_tx, establisher_rx) = mpsc::channel(ESTABLISHER_CHANNEL_SIZE); + + let supported_protocols = protocols.get_supported_protocols(); + + let listener = PeerListener::new( + executor.clone(), + config.clone(), + transport.clone(), + noise_config.clone(), + internal_event_tx.clone(), + peer_manager.clone(), + Arc::clone(&node_identity), + supported_protocols.clone(), + shutdown_signal.clone(), + ); + + let dialer = Dialer::new( + executor.clone(), + config.clone(), + Arc::clone(&node_identity), + peer_manager.clone(), + transport, + noise_config, + backoff, + establisher_rx, + internal_event_tx, + supported_protocols, + shutdown_signal.clone(), + ); + Self { - connections: LivePeerConnections::with_max_connections(config.max_connections), - establisher: Arc::new(ConnectionEstablisher::new( - zmq_context, - Arc::clone(&node_identity), - config, - Arc::clone(&peer_manager), - )), + executor, + config, + shutdown_signal: Some(shutdown_signal), + request_rx: request_rx.fuse(), node_identity, peer_manager, - establish_locks: Mutex::new(HashMap::new()), + protocols, + internal_event_rx: internal_event_rx.fuse(), + dialer_tx: establisher_tx, + dialer: Some(dialer), + listener: Some(listener), + active_connections: Default::default(), + listener_address: None, + listening_notifiers: Vec::new(), + connection_manager_events_tx, + complete_trigger: Shutdown::new(), } } - /// Attempt to establish a connection to a given NodeId. If the connection exists - /// the existing connection is returned. - pub fn establish_connection_to_node_id(&self, node_id: &NodeId) -> Result> { - match self.peer_manager.find_with_node_id(node_id) { - Ok(peer) => self.establish_connection_to_peer(&peer), - Err(err) => Err(ConnectionManagerError::PeerManagerError(err)), - } + pub fn complete_signal(&self) -> ShutdownSignal { + self.complete_trigger.to_signal() } - /// Attempt to establish a connection to a given peer. If the connection exists - /// the existing connection is returned. - pub fn establish_connection_to_peer(&self, peer: &Peer) -> Result> { - self.with_establish_lock(&peer.node_id, || self.attempt_connection_to_peer(peer)) - } - - fn attempt_connection_to_peer(&self, peer: &Peer) -> Result> { - let maybe_conn = self.connections.get_connection(&peer.node_id); - let peer_conn = match maybe_conn { - Some(conn) => { - let state = conn.get_state(); - - match state { - PeerConnectionState::Initial | - PeerConnectionState::Disconnected | - PeerConnectionState::Shutdown => { - warn!( - target: LOG_TARGET, - "Peer connection state is '{}'. Attempting to reestablish connection to peer.", state - ); - // Ignore not found error when dropping - let _ = self.connections.shutdown_connection(&peer.node_id); - self.initiate_peer_connection(peer)? - }, - PeerConnectionState::Failed(err) => { - warn!( - target: LOG_TARGET, - "Peer connection for NodeId={} in failed state. Error({:?}) Attempting to reestablish.", - peer.node_id, - err - ); - // Ignore not found error when dropping - self.connections.shutdown_connection(&peer.node_id)?; - self.initiate_peer_connection(peer)? - }, - // Already have an active connection, just return it - PeerConnectionState::Listening(Some(address)) => { - debug!( - target: LOG_TARGET, - "Waiting for NodeId={} to connect at {}...", peer.node_id, address - ); - return Ok(conn); - }, - PeerConnectionState::Listening(None) => { - debug!( - target: LOG_TARGET, - "Listening on non-tcp socket for NodeId={}...", peer.node_id - ); - return Ok(conn); - }, - PeerConnectionState::Connecting => { - debug!(target: LOG_TARGET, "Still connecting to {}...", peer.node_id); - return Ok(conn); - }, - PeerConnectionState::Connected(Some(address)) => { - debug!("Connection already established to {}.", address); - return Ok(conn); - }, - PeerConnectionState::Connected(None) => { - debug!("Connection already established to non-TCP socket"); - return Ok(conn); - }, - } - }, - None => { - debug!( - target: LOG_TARGET, - "Peer connection does not exist for NodeId={}", peer.node_id - ); - - self.initiate_peer_connection(peer)? - }, - }; + pub async fn run(mut self) { + let mut shutdown = self + .shutdown_signal + .take() + .expect("ConnectionManager initialized without a shutdown"); - Ok(peer_conn.clone()) - } + self.run_listener(); + self.run_dialer(); - /// Establish an inbound connection for the given peer and pass it (and it's `CurvePublicKey`) to a callback. - /// That callback will determine whether the connection should be added to the live connection list. This - /// enables you to for instance, implement a connection protocol which decides if the connection manager - /// ultimately accepts the peer connection. - /// - /// ## Arguments - /// - /// - `peer`: &Peer - Create an inbound connection for this peer - /// - `with_connection`: This callback is called with the new connection. If `Ok(Some(connection))` is returned, the - /// connection is added to the live connection list, otherwise it is discarded - pub(crate) fn with_new_inbound_connection( - &self, - peer: &Peer, - with_connection: impl FnOnce(Arc, CurvePublicKey) -> result::Result>, E>, - ) -> Result<()> - where - E: Into, - { - // If we have reached the maximum connections, we won't allow new connections to be requested - if self.connections.has_reached_max_active_connections() { - return Err(ConnectionManagerError::MaxConnectionsReached); - } - - let (secret_key, public_key) = CurveEncryption::generate_keypair()?; + debug!(target: LOG_TARGET, "Connection manager started"); + loop { + futures::select! { + event = self.internal_event_rx.select_next_some() => { + self.handle_event(event).await; + }, - let (conn, join_handle) = self - .establisher - .establish_inbound_peer_connection(peer.node_id.clone().into(), secret_key)?; + request = self.request_rx.select_next_some() => { + self.handle_request(request).await; + }, - match with_connection(conn, public_key).map_err(Into::into)? { - Some(conn) => { - self.connections - .add_connection(peer.node_id.clone(), conn, join_handle)?; - }, - None => {}, + _ = shutdown => { + info!(target: LOG_TARGET, "ConnectionManager is shutting down because it received the shutdown signal"); + self.disconnect_all().await; + break; + } + } } - - Ok(()) } - /// Sends shutdown signals to all PeerConnections - pub fn shutdown(self) -> Vec> { - self.connections.shutdown_joined() - } - - /// Try to acquire an establish lock for the node ID. If a lock exists for the Node ID, - /// then return `EstablishLockResult::Collision` is returned. - pub fn try_acquire_establish_lock(&self, node_id: &NodeId, func: impl FnOnce() -> T) -> EstablishLockResult { - if acquire_lock!(self.establish_locks).contains_key(node_id) { - EstablishLockResult::Collision - } else { - self.with_establish_lock(node_id, || { - let res = func(); - EstablishLockResult::Ok(res) - }) + async fn disconnect_all(&mut self) { + let mut node_ids = Vec::with_capacity(self.active_connections.len()); + for (node_id, mut conn) in self.active_connections.drain() { + if log_if_error!( + target: LOG_TARGET, + conn.disconnect_silent().await, + "Failed to disconnect because '{error}'", + ) + .is_some() + { + node_ids.push(node_id); + } } - } - /// Lock a critical section for the given node id during connection establishment - pub fn with_establish_lock(&self, node_id: &NodeId, func: impl FnOnce() -> T) -> T { - // Return the lock for the given node id. If no lock exists create a new one and return it. - let nid_lock = { - let mut establish_locks = acquire_lock!(self.establish_locks); - match establish_locks.get(node_id) { - Some(lock) => lock.clone(), - None => { - let new_lock = Arc::new(Mutex::new(())); - establish_locks.insert(node_id.clone(), new_lock.clone()); - new_lock - }, - } - }; - - // Lock the lock for the NodeId - let _nid_lock_guard = acquire_lock!(nid_lock); - let ret = func(); - // Remove establish lock once done to release memory. This is safe because the function has already - // established the connection, so any subsequent calls will return the existing connection. - { - let mut establish_locks = acquire_lock!(self.establish_locks); - establish_locks.remove(node_id); + for node_id in node_ids { + self.publish_event(ConnectionManagerEvent::PeerDisconnected(Box::new(node_id))); } - ret } - /// Get the peer manager - pub(crate) fn peer_manager(&self) -> &PeerManager { - &self.peer_manager - } + fn run_listener(&mut self) { + let listener = self + .listener + .take() + .expect("ConnnectionManager initialized without a Listener"); - /// Shutdown a given peer's [PeerConnection] and return it if one exists, - /// otherwise None is returned. - /// - /// [PeerConnection]: ../../connection/peer_connection/index.html - pub(crate) fn shutdown_connection_for_peer(&self, peer: &Peer) -> Result>> { - match self.connections.shutdown_connection(&peer.node_id) { - Ok((conn, handle)) => { - handle - .timeout_join(Duration::from_millis(3000)) - .map_err(ConnectionManagerError::PeerConnectionThreadError)?; - Ok(Some(conn)) - }, - Err(ConnectionManagerError::PeerConnectionNotFound) => Ok(None), - Err(err) => Err(err), - } + self.executor.spawn(listener.run()); } - /// Return a connection for a peer if one exists, otherwise None is returned - pub(crate) fn get_connection(&self, peer: &Peer) -> Option> { - self.connections.get_connection(&peer.node_id) - } + fn run_dialer(&mut self) { + let dialer = self + .dialer + .take() + .expect("ConnnectionManager initialized without an Establisher"); - /// Return the number of _active_ peer connections currently managed by this instance - pub fn get_active_connection_count(&self) -> usize { - self.connections.get_active_connection_count() + self.executor.spawn(dialer.run()); } - pub fn get_message_sink_address(&self) -> &InprocAddress { - &self.establisher.get_config().message_sink_address + async fn handle_request(&mut self, request: ConnectionManagerRequest) { + use ConnectionManagerRequest::*; + match request { + DialPeer(node_id, is_forced, reply_tx) => match self.get_active_connection(&node_id) { + Some(conn) => { + debug!(target: LOG_TARGET, "[{}] Found existing active connection", conn); + log_if_error_fmt!( + target: LOG_TARGET, + reply_tx.send(Ok(conn.clone())), + "Failed to send reply for dial request for peer '{}'", + node_id.short_str() + ); + }, + None => { + debug!( + target: LOG_TARGET, + "[ThisNode={}] Existing peer connection NOT found. Attempting to establish a new connection \ + to peer '{}'.", + self.node_identity.node_id().short_str(), + node_id.short_str() + ); + self.dial_peer(node_id, reply_tx, is_forced).await + }, + }, + NotifyListening(reply_tx) => match self.listener_address.as_ref() { + Some(addr) => { + let _ = reply_tx.send(addr.clone()); + }, + None => { + self.listening_notifiers.push(reply_tx); + }, + }, + GetActiveConnection(node_id, reply_tx) => { + let _ = reply_tx.send(self.active_connections.get(&node_id).map(Clone::clone)); + }, + } } - fn initiate_peer_connection(&self, peer: &Peer) -> Result> { - let protocol = PeerConnectionProtocol::new(&self.node_identity, &self.establisher); - self.peer_manager - .reset_connection_attempts(&peer.node_id) - .map_err(ConnectionManagerError::PeerManagerError)?; - - protocol - .negotiate_peer_connection(peer) - .and_then(|(new_conn, join_handle)| { - let config = self.establisher.get_config(); + async fn handle_event(&mut self, event: ConnectionManagerEvent) { + use ConnectionManagerEvent::*; + + trace!( + target: LOG_TARGET, + "[ThisNode = {}] Received internal event '{}'", + self.node_identity.node_id().short_str(), + event + ); + + match event { + Listening(addr) => { + self.listener_address = Some(addr.clone()); + self.publish_event(ConnectionManagerEvent::Listening(addr.clone())); + for notifier in self.listening_notifiers.drain(..) { + let _ = notifier.send(addr.clone()); + } + }, + NewInboundSubstream(node_id, protocol, stream) => { + let proto_str = String::from_utf8_lossy(&protocol); debug!( target: LOG_TARGET, - "[{:?}] Waiting {}s for peer connection acceptance from remote peer ", - new_conn.get_address(), - config.peer_connection_establish_timeout.as_secs(), + "New inbound substream for peer '{}' speaking protocol '{}'", + node_id.short_str(), + proto_str ); + if let Err(err) = self + .protocols + .notify(&protocol, ProtocolEvent::NewInboundSubstream(node_id, stream)) + .await + { + error!( + target: LOG_TARGET, + "Error sending NewSubstream notification for protocol '{}' because '{:?}'", proto_str, err + ); + } + }, + PeerConnected(new_conn) => { + let node_id = new_conn.peer_node_id().clone(); - // Wait for peer connection to transition to connected state before continuing - new_conn - .wait_connected_or_failure(&config.peer_connection_establish_timeout) - .or_else(|err| { - info!( + if let Err(err) = self.peer_manager.set_last_connect_success(&node_id).await { + error!( + target: LOG_TARGET, + "set_last_connect_success failed because '{:?}'", err + ); + } + + // If we're dialing this node, let's cancel it + self.send_dialer_request(DialerRequest::CancelPendingDial(node_id.clone())) + .await; + + match self.active_connections.remove(&node_id) { + Some(existing_conn) => { + debug!( target: LOG_TARGET, - "Peer did not accept the connection within {:?} [NodeId={}] : {:?}", - config.peer_connection_establish_timeout, - peer.node_id, - err, + "Existing {} peer connection found for peer '{}'", + existing_conn.direction(), + existing_conn.peer_node_id() ); - Err(ConnectionManagerError::ConnectionError(err)) - })?; - debug!( - target: LOG_TARGET, - "[{:?}] Connection established. Adding to active peer connections.", - new_conn.get_address(), - ); - self.connections - .add_connection(peer.node_id.clone(), Arc::clone(&new_conn), join_handle)?; + if self.tie_break_existing_connection(&existing_conn, &new_conn) { + debug!( + target: LOG_TARGET, + "Disconnecting existing {} connection to peer '{}' because of simultaneous dial", + existing_conn.direction(), + existing_conn.peer_node_id().short_str() + ); + + self.publish_event(PeerConnectWillClose( + existing_conn.id(), + Box::new(existing_conn.peer_node_id().clone()), + existing_conn.direction(), + )); + self.delayed_disconnect(existing_conn); + self.active_connections.insert(node_id, new_conn.clone()); + self.publish_event(PeerConnected(new_conn)); + } else { + debug!( + target: LOG_TARGET, + "Disconnecting new {} connection to peer '{}' because of + simultaneous dial", + new_conn.direction(), + new_conn.peer_node_id().short_str() + ); + + self.delayed_disconnect(new_conn); + self.active_connections.insert(node_id, existing_conn); + } + }, + None => { + debug!( + target: LOG_TARGET, + "Adding new {} peer connection for peer '{}'", + new_conn.direction(), + new_conn.peer_node_id().short_str() + ); + self.active_connections.insert(node_id, new_conn.clone()); + self.publish_event(PeerConnected(new_conn)); + }, + } + }, + PeerDisconnected(node_id) => { + if self.active_connections.remove(&node_id).is_some() { + self.publish_event(PeerDisconnected(node_id)); + } + }, + PeerConnectFailed(node_id, err) => { + if let Err(err) = self.peer_manager.set_last_connect_failed(&node_id).await { + error!(target: LOG_TARGET, "set_peer_connect_failed failed because '{:?}'", err); + } + self.publish_event(PeerConnectFailed(node_id, err)); + }, + event => { + self.publish_event(event); + }, + } - Ok(new_conn) - }) - .or_else(|err| match err { - ConnectionManagerError::ConnectionRejected(reason) => self.handle_connection_rejection(peer, reason), - _ => { - warn!( - target: LOG_TARGET, - "Failed to establish peer connection to NodeId={}", peer.node_id - ); - warn!( - target: LOG_TARGET, - "Failed connection error for NodeId={}: {:?}", peer.node_id, err - ); - Err(err) - }, - }) + trace!( + target: LOG_TARGET, + "[ThisNode={}] {} active connection(s)", + self.node_identity.node_id().short_str(), + self.active_connections.len() + ); } - /// The peer is telling us that we already have a connection. This can occur if the connection has been made - /// by the remote peer while attempting to connect to it. Let's look for a connection and if we have one - fn handle_connection_rejection(&self, peer: &Peer, reason: RejectReason) -> Result> { - match reason { - RejectReason::ExistingConnection => self - .connections - .get_active_connection(&peer.node_id) - .ok_or(ConnectionManagerError::PeerConnectionNotFound), - _ => Err(ConnectionManagerError::ConnectionRejected(reason)), + #[inline] + async fn send_dialer_request(&mut self, req: DialerRequest) { + if let Err(err) = self.dialer_tx.send(req).await { + error!(target: LOG_TARGET, "Unable to send DialerRequest because '{}'", err); + } + } + + /// Two connections to the same peer have been created. This function deterministically determines which peer + /// connection to close. It does this by comparing our NodeId to that of the peer. This rule enables both sides to + /// agree which connection to disconnect + /// + /// Returns true if the existing connection should close, otherwise false if the new connection should be closed. + fn tie_break_existing_connection(&self, existing_conn: &PeerConnection, new_conn: &PeerConnection) -> bool { + debug_assert_eq!(existing_conn.peer_node_id(), new_conn.peer_node_id()); + let peer_node_id = existing_conn.peer_node_id(); + let our_node_id = self.node_identity.node_id(); + + use ConnectionDirection::*; + match (existing_conn.direction(), new_conn.direction()) { + // They connected to us twice for some reason. Drop the existing (older) connection + (Inbound, Inbound) => true, + // They connected to us at the same time we connected to them + (Inbound, Outbound) => peer_node_id > our_node_id, + // We connected to them at the same time as they connected to us + (Outbound, Inbound) => our_node_id > peer_node_id, + // We connected to them twice for some reason. Drop the newer connection. + (Outbound, Outbound) => false, } } -} -#[cfg(test)] -mod test { - use super::*; - use crate::{ - connection::{InprocAddress, NetAddress, ZmqContext}, - peer_manager::PeerFlags, - types::CommsPublicKey, - }; - use rand::rngs::OsRng; - use std::{thread, time::Duration}; - use tari_crypto::keys::PublicKey; - use tari_storage::HMapDatabase; - - fn setup() -> (ZmqContext, Arc, Arc) { - let context = ZmqContext::new(); - let node_identity = Arc::new(NodeIdentity::random_for_test(None)); - - let peer_manager = Arc::new(PeerManager::new(HMapDatabase::new()).unwrap()); - - (context, node_identity, peer_manager) + /// A 'gentle' disconnect starts by firing a `PeerConnectWillClose` event, waiting (lingering) for a period of time + /// and then disconnecting. This gives other components time to conclude their work before the connection is + /// closed. + fn delayed_disconnect(&mut self, mut conn: PeerConnection) -> task::JoinHandle<()> { + let linger = self.config.disconnect_linger; + debug!( + target: LOG_TARGET, + "{} connection for peer '{}' will close after {}ms", + conn.direction(), + conn.peer_node_id(), + linger.as_millis() + ); + + self.executor.spawn(async move { + debug!( + target: LOG_TARGET, + "Waiting for linger period ({}ms) to expire...", + linger.as_millis() + ); + time::delay_for(linger).await; + + match conn.disconnect_silent().await { + Ok(_) => {}, + Err(err) => { + error!(target: LOG_TARGET, "Failed to disconnect because '{:?}'", err); + }, + } + }) } - fn create_peer(address: NetAddress) -> Peer { - let (_, pk) = CommsPublicKey::random_keypair(&mut OsRng::new().unwrap()); - let node_id = NodeId::from_key(&pk).unwrap(); - Peer::new(pk, node_id, address.into(), PeerFlags::empty()) + fn publish_event(&self, event: ConnectionManagerEvent) { + let event = Arc::new(event); + if self.connection_manager_events_tx.send(event.clone()).is_err() { + trace!( + target: LOG_TARGET, + "Didn't send event '{}' because there are no subscribers", + event + ); + } } - #[test] - fn get_active_connection_count() { - let (context, node_identity, peer_manager) = setup(); - let manager = ConnectionManager::new(context, node_identity, peer_manager, PeerConnectionConfig { - peer_connection_establish_timeout: Duration::from_secs(5), - max_message_size: 1024, - host: "127.0.0.1".parse().unwrap(), - max_connect_retries: 3, - max_connections: 10, - message_sink_address: InprocAddress::random(), - socks_proxy_address: None, - }); - - assert_eq!(manager.get_active_connection_count(), 0); + #[inline] + fn get_active_connection(&self, node_id: &NodeId) -> Option<&PeerConnection> { + self.active_connections.get(node_id) } - #[test] - fn shutdown_connection_for_peer() { - let (context, node_identity, peer_manager) = setup(); - let manager = ConnectionManager::new(context, node_identity, peer_manager, PeerConnectionConfig { - peer_connection_establish_timeout: Duration::from_secs(5), - max_message_size: 1024, - host: "127.0.0.1".parse().unwrap(), - max_connect_retries: 3, - max_connections: 10, - message_sink_address: InprocAddress::random(), - socks_proxy_address: None, - }); - - assert_eq!(manager.get_active_connection_count(), 0); - - let address = "127.0.0.1:43456".parse::().unwrap(); - let peer = create_peer(address.clone()); - - assert!(manager.shutdown_connection_for_peer(&peer).unwrap().is_none()); - - let (peer_conn, rx) = PeerConnection::new_with_connecting_state_for_test(); - let peer_conn = Arc::new(peer_conn); - let join_handle = thread::spawn(|| Ok(())); - manager - .connections - .add_connection(peer.node_id.clone(), peer_conn, join_handle) - .unwrap(); - - match manager.shutdown_connection_for_peer(&peer).unwrap() { - Some(_) => {}, - None => panic!("shutdown_connection_for_peer did not return active peer connection"), - } + async fn dial_peer( + &mut self, + node_id: NodeId, + reply_tx: oneshot::Sender>, + force_dial: bool, + ) + { + match self.peer_manager.find_by_node_id(&node_id).await { + Ok(peer) => { + if !force_dial && peer.is_offline() { + debug!( + target: LOG_TARGET, + "Peer '{}' is offline (i.e. we failed to connect to them recently).", + peer.node_id.short_str() + ); + let _ = reply_tx.send(Err(ConnectionManagerError::PeerOffline)); + self.publish_event(ConnectionManagerEvent::PeerConnectFailed( + Box::new(peer.node_id), + ConnectionManagerError::PeerOffline, + )); + return; + } - drop(rx); + if let Err(err) = self.dialer_tx.try_send(DialerRequest::Dial(Box::new(peer), reply_tx)) { + error!( + target: LOG_TARGET, + "Failed to send request to establisher because '{}'", err + ); + // TODO: If the channel is full - we'll fail to dial. This function should block until the dial + // request channel has cleared + + match err.into_inner() { + DialerRequest::Dial(_, reply_tx) => { + log_if_error_fmt!( + target: LOG_TARGET, + reply_tx.send(Err(ConnectionManagerError::EstablisherChannelError)), + "Failed to send dial peer result for peer '{}'", + node_id.short_str() + ); + }, + _ => {}, + } + } + }, + Err(err) => { + error!(target: LOG_TARGET, "Failed to fetch peer to dial because '{}'", err); + log_if_error_fmt!( + level: warn, + target: LOG_TARGET, + reply_tx.send(Err(ConnectionManagerError::PeerManagerError(err))), + "Failed to send error reply when dialing peer '{}'", + node_id.short_str() + ); + }, + } } } diff --git a/comms/src/connection_manager/mod.rs b/comms/src/connection_manager/mod.rs index 3cc0256781..d2ea9bcb27 100644 --- a/comms/src/connection_manager/mod.rs +++ b/comms/src/connection_manager/mod.rs @@ -1,107 +1,44 @@ -// Copyright 2019 The Tari Project +// Copyright 2019, The Tari Project // -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: // -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. // -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. // -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. // -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod common; +mod dial_state; +mod dialer; +mod listener; -//! # ConnectionManager -//! -//! Responsible for establishing and managing active connections to peers. -//! -//! It consists of a number of components each with their own concern. -//! -//! - [ConnectionManager] -//! -//! The public interface for connection management. This uses the other components -//! to manage peer connections for tari_comms. -//! -//! - [LivePeerConnections] -//! -//! A container for [PeerConnection]s which have been created by the [ConnectionManager]. -//! -//! - [ConnectionEstablisher] -//! -//! Responsible for creating [PeerConnection]s. This is basically a factory for [PeerConnection]s -//! which first checks that the connection is connected before passing it back to the caller. -//! -//! - [PeerConnectionProtocol] -//! -//! Uses the [ConnectionEstablisher] to connect to a given peer's [ControlService], -//! open an inbound [PeerConnection] and send an [RequestConnection] message with -//! to the peer's [ControlService]. -//! -//! ```edition2018 -//! # use std::time::Duration; -//! # use std::sync::Arc; -//! # use tari_comms::connection_manager::{ConnectionManager, PeerConnectionConfig}; -//! # use tari_comms::peer_manager::{PeerManager, NodeIdentity}; -//! # use tari_comms::connection::{ZmqContext, InprocAddress}; -//! # use rand::OsRng; -//! # use tari_storage::lmdb_store::LMDBBuilder; -//! # use lmdb_zero::db; -//! # use tari_storage::LMDBWrapper; -//! -//! let node_identity = Arc::new(NodeIdentity::random(&mut OsRng::new().unwrap(), "127.0.0.1:9000".parse().unwrap()).unwrap()); -//! -//! let context = ZmqContext::new(); -//! -//! let database_name = "cm_peer_database"; -//! let datastore = LMDBBuilder::new() -//! .set_path("/tmp/") -//! .set_environment_size(10) -//! .set_max_number_of_databases(1) -//! .add_database(database_name, lmdb_zero::db::CREATE) -//! .build().unwrap(); -//! let peer_database = datastore.get_handle(database_name).unwrap(); -//! let peer_database = LMDBWrapper::new(Arc::new(peer_database)); -//! let peer_manager = Arc::new(PeerManager::new(peer_database).unwrap()); -//! -//! let manager = ConnectionManager::new(context, node_identity, peer_manager, PeerConnectionConfig { -//! peer_connection_establish_timeout: Duration::from_secs(5), -//! max_message_size: 1024, -//! max_connections: 10, -//! host: "127.0.0.1".parse().unwrap(), -//! max_connect_retries: 3, -//! message_sink_address: InprocAddress::random(), -//! socks_proxy_address: None, -//! }); -//! -//! // No active connections -//! assert_eq!(manager.get_active_connection_count(), 0); -//! ``` -//! -//! [ConnectionManager]: ./manager/struct.ConnectionManager.html -//! [LivePeerConnections]: ./connections/struct.LivePeerConnections.html -//! [ControlService]: ../control_service/index.html -//! [RequestConnection]: ../message/p2p/struct.RequestConnection.html -//! [Connecti]: ./connections/struct.LivePeerConnections.html -//! [PeerConnection]: ../connection/peer_connection/struct.PeerConnection.html -//! [ConnectionEstablisher]: ./establisher/struct.ConnectionEstablisher.html -mod connections; -mod error; -pub mod establisher; -mod manager; -mod protocol; -mod repository; mod types; +pub use types::ConnectionDirection; + +mod requester; +pub use requester::{ConnectionManagerRequest, ConnectionManagerRequester}; + +mod manager; +pub use manager::{ConnectionManager, ConnectionManagerConfig, ConnectionManagerEvent}; + +mod error; +pub use error::{ConnectionManagerError, PeerConnectionError}; -pub(crate) use self::types::EstablishLockResult; -pub use self::{error::ConnectionManagerError, establisher::PeerConnectionConfig, manager::ConnectionManager}; +mod peer_connection; +pub use peer_connection::{NegotiatedSubstream, PeerConnection, PeerConnectionRequest}; -type Result = std::result::Result; +#[cfg(test)] +mod tests; diff --git a/comms/src/connection_manager/peer_connection.rs b/comms/src/connection_manager/peer_connection.rs new file mode 100644 index 0000000000..d9ada42732 --- /dev/null +++ b/comms/src/connection_manager/peer_connection.rs @@ -0,0 +1,392 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::{ + error::{ConnectionManagerError, PeerConnectionError}, + manager::ConnectionManagerEvent, + types::ConnectionDirection, +}; +use crate::{ + multiplexing::{IncomingSubstreams, Yamux}, + peer_manager::NodeId, + protocol::{ProtocolId, ProtocolNegotiation}, + types::CommsSubstream, +}; +use futures::{ + channel::{mpsc, oneshot}, + stream::Fuse, + SinkExt, + StreamExt, +}; +use log::*; +use multiaddr::Multiaddr; +use std::{ + fmt, + sync::atomic::{AtomicUsize, Ordering}, +}; +use tari_shutdown::Shutdown; +use tokio::runtime; + +const LOG_TARGET: &str = "comms::connection_manager::peer_connection"; + +const PEER_REQUEST_BUFFER_SIZE: usize = 64; + +static ID_COUNTER: AtomicUsize = AtomicUsize::new(0); + +pub fn create( + executor: runtime::Handle, + connection: Yamux, + peer_addr: Multiaddr, + peer_node_id: NodeId, + direction: ConnectionDirection, + event_notifier: mpsc::Sender, + our_supported_protocols: Vec, +) -> Result +{ + trace!( + target: LOG_TARGET, + "(Peer={}) Socket successfully upgraded to multiplexed socket", + peer_node_id.short_str() + ); + let (peer_tx, peer_rx) = mpsc::channel(PEER_REQUEST_BUFFER_SIZE); + let id = ID_COUNTER.fetch_add(1, Ordering::Relaxed); // Monotonic + let peer_conn = PeerConnection::new(id, peer_tx, peer_node_id.clone(), peer_addr, direction); + let peer_actor = PeerConnectionActor::new( + id, + peer_node_id, + direction, + connection, + peer_rx, + event_notifier, + our_supported_protocols, + ); + executor.spawn(peer_actor.run()); + + Ok(peer_conn) +} + +#[derive(Debug)] +pub enum PeerConnectionRequest { + /// Open a new substream and negotiate the given protocol + OpenSubstream( + ProtocolId, + oneshot::Sender, PeerConnectionError>>, + ), + /// Disconnect all substreams and close the transport connection + Disconnect(bool, oneshot::Sender<()>), +} + +pub type ConnId = usize; + +/// Request handle for an active peer connection +#[derive(Clone, Debug)] +pub struct PeerConnection { + id: ConnId, + peer_node_id: NodeId, + request_tx: mpsc::Sender, + address: Multiaddr, + direction: ConnectionDirection, +} + +impl PeerConnection { + pub(crate) fn new( + id: ConnId, + request_tx: mpsc::Sender, + peer_node_id: NodeId, + address: Multiaddr, + direction: ConnectionDirection, + ) -> Self + { + Self { + id, + request_tx, + peer_node_id, + address, + direction, + } + } + + pub fn peer_node_id(&self) -> &NodeId { + &self.peer_node_id + } + + pub fn direction(&self) -> ConnectionDirection { + self.direction + } + + pub fn id(&self) -> ConnId { + self.id + } + + pub async fn open_substream>( + &mut self, + protocol_id: P, + ) -> Result, PeerConnectionError> + { + let (reply_tx, reply_rx) = oneshot::channel(); + self.request_tx + .send(PeerConnectionRequest::OpenSubstream(protocol_id.into(), reply_tx)) + .await?; + reply_rx + .await + .map_err(|_| PeerConnectionError::InternalReplyCancelled)? + } + + pub async fn disconnect(&mut self) -> Result<(), PeerConnectionError> { + let (reply_tx, reply_rx) = oneshot::channel(); + self.request_tx + .send(PeerConnectionRequest::Disconnect(false, reply_tx)) + .await?; + Ok(reply_rx + .await + .map_err(|_| PeerConnectionError::InternalReplyCancelled)?) + } + + pub(crate) async fn disconnect_silent(&mut self) -> Result<(), PeerConnectionError> { + let (reply_tx, reply_rx) = oneshot::channel(); + self.request_tx + .send(PeerConnectionRequest::Disconnect(true, reply_tx)) + .await?; + Ok(reply_rx + .await + .map_err(|_| PeerConnectionError::InternalReplyCancelled)?) + } +} + +impl fmt::Display for PeerConnection { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + f.debug_struct("PeerConnection") + .field("id", &self.id) + .field("peer_node_id", &self.peer_node_id.short_str()) + .field("direction", &self.direction.to_string()) + .field("address", &self.address.to_string()) + .finish() + } +} + +/// Actor for an active connection to a peer. +pub struct PeerConnectionActor { + id: ConnId, + peer_node_id: NodeId, + request_rx: Fuse>, + direction: ConnectionDirection, + incoming_substreams: Fuse, + substream_shutdown: Option, + control: yamux::Control, + event_notifier: mpsc::Sender, + supported_protocols: Vec, + shutdown: bool, +} + +impl PeerConnectionActor { + fn new( + id: ConnId, + peer_node_id: NodeId, + direction: ConnectionDirection, + connection: Yamux, + request_rx: mpsc::Receiver, + event_notifier: mpsc::Sender, + supported_protocols: Vec, + ) -> Self + { + Self { + id, + peer_node_id, + direction, + control: connection.get_yamux_control(), + incoming_substreams: connection.incoming().fuse(), + substream_shutdown: None, + request_rx: request_rx.fuse(), + event_notifier, + shutdown: false, + supported_protocols, + } + } + + pub async fn run(mut self) { + loop { + futures::select! { + request = self.request_rx.select_next_some() => self.handle_request(request).await, + + maybe_substream = self.incoming_substreams.next() => { + match maybe_substream { + Some(Ok(substream)) => { + if let Err(err) = self.handle_incoming_substream(substream).await { + error!( + target: LOG_TARGET, + "[{}] Incoming substream for peer '{}' failed to open because '{error}'", + self, + self.peer_node_id.short_str(), + error = err + ) + } + }, + Some(Err(err)) => { + warn!(target: LOG_TARGET, "[{}] Incoming substream error '{}'. Closing connection for peer '{}'", self, err, self.peer_node_id.short_str()); + self.disconnect(false).await; + }, + None => { + warn!(target: LOG_TARGET, "[{}] Peer '{}' closed the connection", self, self.peer_node_id.short_str()); + self.disconnect(false).await; + }, + } + } + } + + if self.shutdown { + break; + } + } + } + + async fn handle_request(&mut self, request: PeerConnectionRequest) { + use PeerConnectionRequest::*; + match request { + OpenSubstream(proto, reply_tx) => { + let result = self.open_negotiated_protocol_stream(proto).await; + log_if_error_fmt!( + target: LOG_TARGET, + reply_tx.send(result), + "Reply oneshot closed when sending reply", + ); + }, + Disconnect(silent, reply_tx) => { + debug!( + target: LOG_TARGET, + "[{}] Disconnect{}requested for {} connection to peer '{}'", + self, + if silent { " (silent) " } else { " " }, + self.direction, + self.peer_node_id.short_str() + ); + self.disconnect(silent).await; + let _ = reply_tx.send(()); + }, + } + } + + async fn handle_incoming_substream(&mut self, mut stream: yamux::Stream) -> Result<(), PeerConnectionError> { + let selected_protocol = ProtocolNegotiation::new(&mut stream) + .negotiate_protocol_inbound(&self.supported_protocols) + .await?; + + self.notify_event(ConnectionManagerEvent::NewInboundSubstream( + Box::new(self.peer_node_id.clone()), + selected_protocol, + stream, + )) + .await; + + Ok(()) + } + + async fn open_negotiated_protocol_stream( + &mut self, + protocol: ProtocolId, + ) -> Result, PeerConnectionError> + { + debug!( + target: LOG_TARGET, + "[{}] Negotiating protocol '{}' on new substream for peer '{}'", + self, + String::from_utf8_lossy(&protocol), + self.peer_node_id.short_str() + ); + let mut stream = self.control.open_stream().await?; + let selected_protocol = ProtocolNegotiation::new(&mut stream) + .negotiate_protocol_outbound(&[protocol]) + .await?; + Ok(NegotiatedSubstream::new(selected_protocol, stream)) + } + + async fn notify_event(&mut self, event: ConnectionManagerEvent) { + log_if_error!( + target: LOG_TARGET, + self.event_notifier.send(event).await, + "Failed to send connection manager notification because '{}'", + ); + } + + /// Disconnect this peer connection. + /// + /// # Arguments + /// + /// silent - true to supress the PeerDisconnected event, false to publish the event + async fn disconnect(&mut self, silent: bool) { + if let Err(err) = self.control.close().await { + warn!( + target: LOG_TARGET, + "[{}] Failed to politely close connection to peer '{}' because '{}'", + self, + self.peer_node_id.short_str(), + err + ); + } + trace!(target: LOG_TARGET, "Connection closed"); + + self.shutdown = true; + // Shut down the incoming substream task + self.substream_shutdown.as_mut().and_then(|shutdown| { + let _ = shutdown.trigger(); + Some(()) + }); + + if !silent { + self.notify_event(ConnectionManagerEvent::PeerDisconnected(Box::new( + self.peer_node_id.clone(), + ))) + .await; + } + } +} + +impl fmt::Display for PeerConnectionActor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "PeerConnection(id={}, peer_node_id={}, direction={})", + self.id, + self.peer_node_id.short_str(), + self.direction, + ) + } +} + +pub struct NegotiatedSubstream { + pub protocol: ProtocolId, + pub stream: TSubstream, +} + +impl NegotiatedSubstream { + pub fn new(protocol: ProtocolId, stream: TSubstream) -> Self { + Self { protocol, stream } + } +} + +impl fmt::Debug for NegotiatedSubstream { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("NegotiatedSubstream") + .field("protocol", &format!("{:?}", self.protocol)) + .field("stream", &"...".to_string()) + .finish() + } +} diff --git a/comms/src/connection_manager/protocol.rs b/comms/src/connection_manager/protocol.rs deleted file mode 100644 index ad2d3873df..0000000000 --- a/comms/src/connection_manager/protocol.rs +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use super::{establisher::ConnectionEstablisher, types::PeerConnectionJoinHandle, ConnectionManagerError, Result}; -use crate::{ - connection::{CurvePublicKey, NetAddress, PeerConnection}, - control_service::messages::ConnectRequestOutcome, - peer_manager::{NodeIdentity, Peer}, -}; -use log::*; -use std::sync::Arc; - -const LOG_TARGET: &str = "comms::connection_manager::protocol"; - -pub(super) struct PeerConnectionProtocol<'e, 'ni> { - node_identity: &'ni Arc, - establisher: &'e ConnectionEstablisher, -} - -impl<'e, 'ni> PeerConnectionProtocol<'e, 'ni> { - pub fn new(node_identity: &'ni Arc, establisher: &'e ConnectionEstablisher) -> Self { - Self { - node_identity, - establisher, - } - } - - /// Send Establish connection message to the peers control port to request a connection - pub fn negotiate_peer_connection(&self, peer: &Peer) -> Result<(Arc, PeerConnectionJoinHandle)> { - info!(target: LOG_TARGET, "[NodeId={}] Negotiating connection", peer.node_id); - - // 1. Establish control service connection - let control_client = self.establisher.connect_control_service_client(&peer)?; - info!( - target: LOG_TARGET, - "[NodeId={}] Established peer control port connection at address {:?}", - peer.node_id, - control_client.connection().get_connected_address() - ); - - // 2. Send a request to connect - control_client - .send_request_connection( - self.node_identity.control_service_address()?, - self.node_identity.identity.node_id.clone(), - ) - .map_err(|err| ConnectionManagerError::SendRequestConnectionFailed(format!("{:?}", err)))?; - - debug!( - target: LOG_TARGET, - "[NodeId={}] RequestConnection message sent", peer.node_id - ); - - let config = self.establisher.get_config(); - // 3. Receive a request to connect outcome - control_client - .receive_message(config.peer_connection_establish_timeout) - .map_err(|_| ConnectionManagerError::ConnectionRequestOutcomeRecvFail)? - // Abort! Did not receive a connection outcome before the timeout - .ok_or(ConnectionManagerError::ConnectionRequestOutcomeTimeout) - .and_then(|msg| match msg { - ConnectRequestOutcome::Accepted { - curve_public_key, - address, - } => { - info!( - target: LOG_TARGET, - "[NodeId={}] RequestConnection accepted by destination peer's control port", peer.node_id - ); - - // Connect to the requested peer connection and send a identify frame - let (new_peer_conn, join_handle) = - self.establish_requested_peer_connection(peer, curve_public_key, address)?; - - Ok((new_peer_conn, join_handle)) - }, - ConnectRequestOutcome::Rejected(reason) => { - info!( - target: LOG_TARGET, - "[NodeId={}] RequestConnection REJECTED by destination peer's control port. Reason: {}", - peer.node_id, - reason - ); - - // Abort! The connection request was rejected - Err(ConnectionManagerError::ConnectionRejected(reason)) - }, - }) - } - - fn establish_requested_peer_connection( - &self, - peer: &Peer, - curve_public_key: CurvePublicKey, - address: NetAddress, - ) -> Result<(Arc, PeerConnectionJoinHandle)> - { - debug!( - target: LOG_TARGET, - "[NodeId={}] Connecting to given peer connection at address {}", peer.node_id, address - ); - - let (conn, join_handle) = self.establisher.establish_outbound_peer_connection( - peer.node_id.clone().into(), - address, - curve_public_key, - )?; - - Ok((conn, join_handle)) - } -} diff --git a/comms/src/connection_manager/repository.rs b/comms/src/connection_manager/repository.rs deleted file mode 100644 index 2eeae9ef53..0000000000 --- a/comms/src/connection_manager/repository.rs +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::{connection::PeerConnection, peer_manager::node_id::NodeId}; -use chrono::Utc; -use std::{ - collections::HashMap, - sync::{Arc, RwLock}, -}; - -lazy_static! { - static ref PORT_ALLOCATIONS: RwLock> = RwLock::new(vec![]); -} - -pub trait Repository { - fn get(&self, id: &I) -> Option>; - fn has(&self, id: &I) -> bool; - fn size(&self) -> usize; - fn insert(&mut self, id: I, value: Arc); - fn remove(&mut self, id: &I) -> Option>; -} - -impl Repository for ConnectionRepository { - fn get(&self, node_id: &NodeId) -> Option> { - self.entries.get(node_id).cloned() - } - - fn has(&self, node_id: &NodeId) -> bool { - self.entries.contains_key(node_id) - } - - fn size(&self) -> usize { - self.entries.values().count() - } - - fn insert(&mut self, node_id: NodeId, entry: Arc) { - self.entries.insert(node_id, entry); - } - - fn remove(&mut self, node_id: &NodeId) -> Option> { - self.entries.remove(node_id) - } -} - -#[derive(Default)] -pub(super) struct ConnectionRepository { - entries: HashMap>, -} - -impl ConnectionRepository { - #[cfg(test)] - pub fn len(&self) -> usize { - self.entries.len() - } - - pub fn count_where

(&self, predicate: P) -> usize - where P: FnMut(&&Arc) -> bool { - self.entries.values().filter(predicate).count() - } - - pub fn for_each(&self, mut f: impl FnMut(&Arc)) { - for entry in self.entries.values() { - f(entry); - } - } - - pub fn drain_filter(&mut self, predicate: F) -> Vec<(NodeId, Arc)> - where F: FnMut(&(&NodeId, &Arc)) -> bool { - let to_remove = self - .entries - .iter() - .filter(predicate) - .map(|(node_id, _)| node_id.clone()) - .collect::>(); - - to_remove - .into_iter() - .map(|node_id| { - let conn = self - .entries - .remove(&node_id) - .expect("Invariant (drain_filter): node_id key to be removed from entries was not found"); - - (node_id, conn) - }) - .collect::>() - } - - pub fn sorted_recent_activity(&self) -> Vec<(&NodeId, &Arc)> { - let now = Utc::now().naive_utc(); - - let mut items = self.entries.iter().collect::>(); - items.sort_by(|(_, a), (_, b)| { - let a_duration = now.signed_duration_since(a.last_activity()); - let b_duration = now.signed_duration_since(b.last_activity()); - a_duration.cmp(&b_duration) - }); - items - } -} - -#[cfg(test)] -mod test { - use super::*; - use rand::OsRng; - use tari_crypto::{keys::PublicKey, ristretto::RistrettoPublicKey}; - - fn make_node_id() -> NodeId { - let (_sk, pk) = RistrettoPublicKey::random_keypair(&mut OsRng::new().unwrap()); - NodeId::from_key(&pk).unwrap() - } - - fn make_repo_with_connections(n: usize) -> (ConnectionRepository, Vec) { - let mut repo = ConnectionRepository::default(); - let mut node_ids = vec![]; - for _i in 0..n { - let node_id = make_node_id(); - let conn = Arc::new(PeerConnection::new()); - repo.insert(node_id.clone(), conn.clone()); - node_ids.push(node_id); - } - (repo, node_ids) - } - - #[test] - fn insert_get_remove() { - let (mut repo, node_ids) = make_repo_with_connections(2); - let unknown_node_id = make_node_id(); - - // Retrieve - assert!(repo.has(&node_ids[0])); - assert!(repo.has(&node_ids[1])); - assert!(!repo.has(&unknown_node_id)); - - assert!(repo.get(&node_ids[0]).is_some()); - assert!(repo.get(&node_ids[1]).is_some()); - assert!(repo.get(&unknown_node_id).is_none()); - - // Remove - assert!(repo.remove(&node_ids[0]).is_some()); - assert!(repo.remove(&unknown_node_id).is_none()); - - assert!(!repo.has(&node_ids[0])); - assert!(repo.has(&node_ids[1])); - assert!(!repo.has(&unknown_node_id)); - } - - #[test] - fn for_each() { - let (repo, _) = make_repo_with_connections(3); - - let mut count = 0; - repo.for_each(|_| { - count += 1; - }); - - assert_eq!(3, count); - } - - #[test] - fn count_where() { - let (repo, _) = make_repo_with_connections(4); - - let mut count = 0; - let total = repo.count_where(|_| { - count += 1; - count % 2 == 0 - }); - - assert_eq!(2, total); - } - - #[test] - fn drain_filter() { - let (mut repo, _) = make_repo_with_connections(4); - - // Get a copy of the expected node ids from the drain - let cloned = repo.entries.clone(); - let node_ids = cloned.keys().collect::>(); - let drained_node_id1 = node_ids[1].clone(); - let drained_node_id2 = node_ids[3].clone(); - let kept_node_id1 = node_ids[0]; - let kept_node_id2 = node_ids[2]; - - let mut i = 0; - let drained = repo.drain_filter(|_| { - i += 1; - i % 2 == 0 - }); - - assert_eq!(drained.len(), 2); - assert_eq!(drained[0].0, drained_node_id1); - assert_eq!(drained[1].0, drained_node_id2); - - assert_eq!(repo.entries.len(), 2); - assert!(repo.entries.contains_key(kept_node_id1)); - assert!(repo.entries.contains_key(kept_node_id2)); - } -} diff --git a/comms/src/connection_manager/requester.rs b/comms/src/connection_manager/requester.rs new file mode 100644 index 0000000000..fd37c6f344 --- /dev/null +++ b/comms/src/connection_manager/requester.rs @@ -0,0 +1,125 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::{error::ConnectionManagerError, peer_connection::PeerConnection}; +use crate::{connection_manager::manager::ConnectionManagerEvent, multiaddr::Multiaddr, peer_manager::NodeId}; +use futures::{ + channel::{mpsc, oneshot}, + SinkExt, +}; +use std::sync::Arc; +use tokio::sync::broadcast; + +/// Requests which are handled by the ConnectionManagerService +#[derive(Debug)] +pub enum ConnectionManagerRequest { + /// Dial a given peer by node id. + /// Parameters: + /// 1. Node Id to dial + /// 1. If true, attempt to dial the peer even if we recently failed to dial them and they are considered offline + DialPeer( + NodeId, + bool, + oneshot::Sender>, + ), + /// Register a oneshot to get triggered when the node is listening, or has failed to listen + NotifyListening(oneshot::Sender), + GetActiveConnection(NodeId, oneshot::Sender>), +} + +/// Responsible for constructing requests to the ConnectionManagerService +#[derive(Clone)] +pub struct ConnectionManagerRequester { + sender: mpsc::Sender, + event_tx: broadcast::Sender>, +} + +impl ConnectionManagerRequester { + /// Create a new ConnectionManagerRequester + pub fn new( + sender: mpsc::Sender, + event_tx: broadcast::Sender>, + ) -> Self + { + Self { sender, event_tx } + } +} + +impl ConnectionManagerRequester { + /// Returns a ConnectionManagerEvent stream + pub fn get_event_subscription(&self) -> broadcast::Receiver> { + self.event_tx.subscribe() + } + + /// Attempt to connect to a remote peer + pub async fn dial_peer(&mut self, node_id: NodeId) -> Result { + self.send_dial_peer(node_id, false).await + } + + /// Attempt to connect to a remote peer, even if we failed to contact the peer recently + pub async fn dial_peer_forced(&mut self, node_id: NodeId) -> Result { + self.send_dial_peer(node_id, true).await + } + + async fn send_dial_peer( + &mut self, + node_id: NodeId, + is_forced: bool, + ) -> Result + { + let (reply_tx, reply_rx) = oneshot::channel(); + self.sender + .send(ConnectionManagerRequest::DialPeer(node_id, is_forced, reply_tx)) + .await + .map_err(|_| ConnectionManagerError::SendToActorFailed)?; + reply_rx + .await + .map_err(|_| ConnectionManagerError::ActorRequestCanceled)? + } + + /// Return the listening address of this node's listener. This will asynchronously block until the listener has + /// initialized and a listening address has been established. + /// + /// This is useful when using "assigned port" addresses, such as /ip4/0.0.0.0/tcp/0 or /memory/0 for listening and + /// you wish to know the final assigned port. + pub async fn wait_until_listening(&mut self) -> Result { + let (reply_tx, reply_rx) = oneshot::channel(); + self.sender + .send(ConnectionManagerRequest::NotifyListening(reply_tx)) + .await + .map_err(|_| ConnectionManagerError::SendToActorFailed)?; + reply_rx.await.map_err(|_| ConnectionManagerError::ActorRequestCanceled) + } + + pub async fn get_active_connection( + &mut self, + node_id: NodeId, + ) -> Result, ConnectionManagerError> + { + let (reply_tx, reply_rx) = oneshot::channel(); + self.sender + .send(ConnectionManagerRequest::GetActiveConnection(node_id, reply_tx)) + .await + .map_err(|_| ConnectionManagerError::SendToActorFailed)?; + reply_rx.await.map_err(|_| ConnectionManagerError::ActorRequestCanceled) + } +} diff --git a/comms/src/connection_manager/tests/listener_dialer.rs b/comms/src/connection_manager/tests/listener_dialer.rs new file mode 100644 index 0000000000..fc08a74cec --- /dev/null +++ b/comms/src/connection_manager/tests/listener_dialer.rs @@ -0,0 +1,194 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + backoff::ConstantBackoff, + connection_manager::{ + dialer::{Dialer, DialerRequest}, + listener::PeerListener, + manager::ConnectionManagerEvent, + ConnectionManagerConfig, + }, + noise::NoiseConfig, + peer_manager::{Peer, PeerFeatures, PeerFlags}, + protocol::ProtocolId, + test_utils::{node_identity::build_node_identity, test_node::build_peer_manager}, + transports::MemoryTransport, +}; +use futures::{ + channel::{mpsc, oneshot}, + AsyncReadExt, + AsyncWriteExt, + SinkExt, + StreamExt, +}; +use multiaddr::Protocol; +use std::{error::Error, time::Duration}; +use tari_shutdown::Shutdown; +use tari_test_utils::unpack_enum; +use tokio::{runtime::Handle, time::timeout}; + +#[tokio_macros::test_basic] +async fn listen() -> Result<(), Box> { + let rt_handle = Handle::current(); + let (event_tx, mut event_rx) = mpsc::channel(1); + let mut shutdown = Shutdown::new(); + let peer_manager = build_peer_manager(); + let node_identity = build_node_identity(PeerFeatures::COMMUNICATION_NODE); + let noise_config = NoiseConfig::new(node_identity.clone()); + let listener = PeerListener::new( + rt_handle.clone(), + ConnectionManagerConfig { + listener_address: "/memory/0".parse()?, + ..Default::default() + }, + MemoryTransport, + noise_config.clone(), + event_tx.clone(), + peer_manager.into(), + node_identity, + vec![], + shutdown.to_signal(), + ); + + let listener_fut = rt_handle.spawn(listener.run()); + + let listen_event = event_rx.next().await.unwrap(); + unpack_enum!(ConnectionManagerEvent::Listening(address) = listen_event); + unpack_enum!(Protocol::Memory(port) = address.pop().unwrap()); + assert!(port > 0); + + shutdown.trigger().unwrap(); + + timeout(Duration::from_secs(5), listener_fut).await.unwrap().unwrap(); + + Ok(()) +} + +#[tokio_macros::test_basic] +async fn smoke() { + let rt_handle = Handle::current(); + // This test sets up Dialer and Listener components, uses the Dialer to dial the Listener, + // asserts the emitted events are correct, opens a substream, sends a small message over the substream, + // receives and checks the message and then disconnects and shuts down. + let (event_tx, mut event_rx) = mpsc::channel(10); + let mut shutdown = Shutdown::new(); + + let node_identity1 = build_node_identity(PeerFeatures::COMMUNICATION_NODE); + let noise_config1 = NoiseConfig::new(node_identity1.clone()); + let expected_proto = ProtocolId::from_static(b"/tari/test-proto"); + let supported_protocols = vec![expected_proto.clone()]; + let peer_manager1 = build_peer_manager(); + let listener = PeerListener::new( + rt_handle.clone(), + ConnectionManagerConfig { + listener_address: "/memory/0".parse().unwrap(), + ..Default::default() + }, + MemoryTransport, + noise_config1, + event_tx.clone(), + peer_manager1.clone().into(), + node_identity1.clone(), + supported_protocols.clone(), + shutdown.to_signal(), + ); + + let listener_fut = rt_handle.spawn(listener.run()); + + let node_identity2 = build_node_identity(PeerFeatures::COMMUNICATION_NODE); + let noise_config2 = NoiseConfig::new(node_identity2.clone()); + let (mut request_tx, request_rx) = mpsc::channel(1); + let peer_manager2 = build_peer_manager(); + let dialer = Dialer::new( + rt_handle.clone(), + ConnectionManagerConfig::default(), + node_identity2.clone(), + peer_manager2.clone().into(), + MemoryTransport, + noise_config2, + ConstantBackoff::new(Duration::from_millis(100)), + request_rx, + event_tx, + supported_protocols, + shutdown.to_signal(), + ); + + let dialer_fut = rt_handle.spawn(dialer.run()); + + // Get the listening address of the peer + let listen_event = event_rx.next().await.unwrap(); + unpack_enum!(ConnectionManagerEvent::Listening(address) = listen_event); + + let mut peer = Peer::new( + node_identity1.public_key().clone(), + node_identity1.node_id().clone(), + vec![address].into(), + PeerFlags::empty(), + PeerFeatures::COMMUNICATION_NODE, + ); + peer.set_id_for_test(1); + + let (reply_tx, reply_rx) = oneshot::channel(); + request_tx + .send(DialerRequest::Dial(Box::new(peer), reply_tx)) + .await + .unwrap(); + + let mut outbound_peer_conn = reply_rx.await.unwrap().unwrap(); + + // Open a substream + { + let mut out_stream = outbound_peer_conn.open_substream("/tari/test-proto").await.unwrap(); + out_stream.stream.write_all(b"HELLO").await.unwrap(); + out_stream.stream.flush().await.unwrap(); + } + + // Read PeerConnected events - we don't know which connection is which + unpack_enum!(ConnectionManagerEvent::PeerConnected(conn1) = event_rx.next().await.unwrap()); + unpack_enum!(ConnectionManagerEvent::PeerConnected(_conn2) = event_rx.next().await.unwrap()); + + // Next event should be a NewInboundSubstream has been received + let listen_event = event_rx.next().await.unwrap(); + { + unpack_enum!(ConnectionManagerEvent::NewInboundSubstream(node_id, proto, in_stream) = listen_event); + assert_eq!(&*node_id, node_identity2.node_id()); + assert_eq!(proto, expected_proto); + + let mut buf = [0u8; 5]; + in_stream.read_exact(&mut buf).await.unwrap(); + assert_eq!(buf, *b"HELLO"); + } + + conn1.disconnect().await.unwrap(); + + shutdown.trigger().unwrap(); + + let peer2 = peer_manager1.find_by_node_id(node_identity2.node_id()).unwrap(); + let peer1 = peer_manager2.find_by_node_id(node_identity1.node_id()).unwrap(); + + assert_eq!(&peer1.public_key, node_identity1.public_key()); + assert_eq!(&peer2.public_key, node_identity2.public_key()); + + timeout(Duration::from_secs(5), listener_fut).await.unwrap().unwrap(); + timeout(Duration::from_secs(5), dialer_fut).await.unwrap().unwrap(); +} diff --git a/comms/src/connection_manager/tests/manager.rs b/comms/src/connection_manager/tests/manager.rs new file mode 100644 index 0000000000..f7daa71742 --- /dev/null +++ b/comms/src/connection_manager/tests/manager.rs @@ -0,0 +1,334 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + backoff::ConstantBackoff, + connection_manager::{ + error::ConnectionManagerError, + manager::ConnectionManagerEvent, + ConnectionManager, + ConnectionManagerRequester, + PeerConnectionError, + }, + noise::NoiseConfig, + peer_manager::{NodeId, Peer, PeerFeatures, PeerFlags, PeerManagerError}, + protocol::{ProtocolEvent, ProtocolId, Protocols}, + test_utils::{ + node_identity::{build_node_identity, ordered_node_identities}, + test_node::{build_connection_manager, build_peer_manager, TestNodeConfig}, + }, + transports::MemoryTransport, +}; +use futures::{channel::mpsc, future, AsyncReadExt, AsyncWriteExt, StreamExt}; +use std::time::Duration; +use tari_shutdown::Shutdown; +use tari_test_utils::{collect_stream, unpack_enum}; +use tokio::{runtime::Handle, sync::broadcast}; +use tokio_macros as r#async; + +#[r#async::test_basic] +async fn connect_to_nonexistent_peer() { + let rt_handle = Handle::current(); + let node_identity = build_node_identity(PeerFeatures::empty()); + let noise_config = NoiseConfig::new(node_identity.clone()); + let (request_tx, request_rx) = mpsc::channel(1); + let (event_tx, _) = broadcast::channel(1); + let mut requester = ConnectionManagerRequester::new(request_tx, event_tx.clone()); + let mut shutdown = Shutdown::new(); + + let peer_manager = build_peer_manager(); + + let connection_manager = ConnectionManager::new( + Default::default(), + rt_handle.clone(), + MemoryTransport, + noise_config, + ConstantBackoff::new(Duration::from_secs(1)), + request_rx, + node_identity, + peer_manager.into(), + Protocols::new(), + event_tx, + shutdown.to_signal(), + ); + + rt_handle.spawn(connection_manager.run()); + + let result = requester.dial_peer(NodeId::default()).await; + unpack_enum!(Result::Err(err) = result); + match err { + ConnectionManagerError::PeerManagerError(PeerManagerError::PeerNotFoundError) => {}, + _ => panic!( + "Unexpected error. Expected \ + `ConnectionManagerError::PeerManagerError(PeerManagerError::PeerNotFoundError)`" + ), + } + + shutdown.trigger().unwrap(); +} + +#[r#async::test_basic] +async fn dial_success() { + const TEST_PROTO: ProtocolId = ProtocolId::from_static(b"/test/valid"); + let rt_handle = Handle::current(); + let shutdown = Shutdown::new(); + + let node_identity1 = build_node_identity(PeerFeatures::empty()); + let node_identity2 = build_node_identity(PeerFeatures::empty()); + + let (proto_tx1, _) = mpsc::channel(1); + let (proto_tx2, mut proto_rx2) = mpsc::channel(1); + + // Setup connection manager 1 + let peer_manager1 = build_peer_manager(); + let mut conn_man1 = build_connection_manager( + rt_handle.clone(), + TestNodeConfig { + node_identity: node_identity1.clone(), + ..Default::default() + }, + peer_manager1.clone(), + Protocols::new().add([TEST_PROTO], proto_tx1), + shutdown.to_signal(), + ); + + let public_address1 = conn_man1.wait_until_listening().await.unwrap(); + + let peer_manager2 = build_peer_manager(); + let mut conn_man2 = build_connection_manager( + rt_handle, + TestNodeConfig { + node_identity: node_identity2.clone(), + ..Default::default() + }, + peer_manager2.clone(), + Protocols::new().add([TEST_PROTO], proto_tx2), + shutdown.to_signal(), + ); + let mut subscription2 = conn_man2.get_event_subscription(); + let public_address2 = conn_man2.wait_until_listening().await.unwrap(); + + peer_manager1 + .add_peer(Peer::new( + node_identity2.public_key().clone(), + node_identity2.node_id().clone(), + vec![public_address2].into(), + PeerFlags::empty(), + PeerFeatures::COMMUNICATION_CLIENT, + )) + .unwrap(); + + peer_manager2 + .add_peer(Peer::new( + node_identity1.public_key().clone(), + node_identity1.node_id().clone(), + vec![public_address1].into(), + PeerFlags::empty(), + PeerFeatures::COMMUNICATION_CLIENT, + )) + .unwrap(); + + // Dial at the same time + let mut conn_out = conn_man1.dial_peer(node_identity2.node_id().clone()).await.unwrap(); + assert_eq!(conn_out.peer_node_id(), node_identity2.node_id()); + + let event = subscription2.next().await.unwrap().unwrap(); + unpack_enum!(ConnectionManagerEvent::Listening(_addr) = &*event); + + let event = subscription2.next().await.unwrap().unwrap(); + unpack_enum!(ConnectionManagerEvent::PeerConnected(conn_in) = &*event); + assert_eq!(conn_in.peer_node_id(), node_identity1.node_id()); + + let err = conn_out.open_substream("/tari/invalid").await.unwrap_err(); + unpack_enum!(PeerConnectionError::ProtocolError(_err) = err); + + let mut substream_out = conn_out.open_substream(TEST_PROTO).await.unwrap(); + assert_eq!(substream_out.protocol, TEST_PROTO); + + const MSG: &[u8] = b"Welease Woger!"; + substream_out.stream.write_all(MSG).await.unwrap(); + + let protocol_in = proto_rx2.next().await.unwrap(); + assert_eq!(protocol_in.protocol, &TEST_PROTO); + unpack_enum!(ProtocolEvent::NewInboundSubstream(node_id, substream_in) = protocol_in.event); + assert_eq!(&*node_id, node_identity1.node_id()); + + let mut buf = [0u8; MSG.len()]; + substream_in.read_exact(&mut buf).await.unwrap(); + assert_eq!(buf, MSG); +} + +fn count_string_occurrences(events: &[T], expected: &[&str]) -> usize +where + T: AsRef, + U: ToString, +{ + events + .iter() + .filter(|event| expected.iter().any(|exp| event.as_ref().to_string().starts_with(exp))) + .count() +} + +#[r#async::test_basic] +async fn dial_offline_peer() { + let rt_handle = Handle::current(); + let shutdown = Shutdown::new(); + + let node_identity = build_node_identity(PeerFeatures::empty()); + + let peer_manager = build_peer_manager(); + let mut conn_man = build_connection_manager( + rt_handle.clone(), + TestNodeConfig { + node_identity: node_identity.clone(), + ..Default::default() + }, + peer_manager.clone(), + Protocols::new(), + shutdown.to_signal(), + ); + + let public_address = conn_man.wait_until_listening().await.unwrap(); + let mut subscription = conn_man.get_event_subscription(); + + let mut peer = Peer::new( + node_identity.public_key().clone(), + node_identity.node_id().clone(), + vec![public_address].into(), + PeerFlags::empty(), + PeerFeatures::COMMUNICATION_CLIENT, + ); + + peer.connection_stats.set_connection_failed(); + assert_eq!(peer.is_offline(), false); + peer.connection_stats.set_connection_failed(); + assert_eq!(peer.is_offline(), true); + + peer_manager.add_peer(peer).unwrap(); + + let err = conn_man.dial_peer(node_identity.node_id().clone()).await.unwrap_err(); + unpack_enum!(ConnectionManagerError::PeerOffline = err); + + let event = subscription.next().await.unwrap().unwrap(); + + unpack_enum!(ConnectionManagerEvent::PeerConnectFailed(node_id, err) = &*event); + assert_eq!(&**node_id, node_identity.node_id()); + unpack_enum!(ConnectionManagerError::PeerOffline = err); +} + +#[r#async::test_basic] +async fn simultaneous_dial_events() { + let rt_handle = Handle::current(); + let mut shutdown = Shutdown::new(); + + let node_identities = ordered_node_identities(2); + + // Setup connection manager 1 + let peer_manager1 = build_peer_manager(); + let mut conn_man1 = build_connection_manager( + rt_handle.clone(), + TestNodeConfig { + node_identity: node_identities[0].clone(), + ..Default::default() + }, + peer_manager1.clone(), + Protocols::new(), + shutdown.to_signal(), + ); + + let subscription1 = conn_man1.get_event_subscription(); + let public_address1 = conn_man1.wait_until_listening().await.unwrap(); + + let peer_manager2 = build_peer_manager(); + let mut conn_man2 = build_connection_manager( + rt_handle, + TestNodeConfig { + node_identity: node_identities[1].clone(), + ..Default::default() + }, + peer_manager2.clone(), + Protocols::new(), + shutdown.to_signal(), + ); + let mut subscription2 = conn_man2.get_event_subscription(); + let public_address2 = conn_man2.wait_until_listening().await.unwrap(); + + peer_manager1 + .add_peer(Peer::new( + node_identities[1].public_key().clone(), + node_identities[1].node_id().clone(), + vec![public_address2].into(), + PeerFlags::empty(), + PeerFeatures::COMMUNICATION_CLIENT, + )) + .unwrap(); + + peer_manager2 + .add_peer(Peer::new( + node_identities[0].public_key().clone(), + node_identities[0].node_id().clone(), + vec![public_address1].into(), + PeerFlags::empty(), + PeerFeatures::COMMUNICATION_CLIENT, + )) + .unwrap(); + + // Dial at the same time + let (result1, result2) = future::join( + conn_man1.dial_peer(node_identities[1].node_id().clone()), + conn_man2.dial_peer(node_identities[0].node_id().clone()), + ) + .await; + + // Either dial could fail (due to being cancelled/rejected by tie breaking) but never both + match (result1, result2) { + (Ok(_), Ok(_)) => {}, + (Err(_), Ok(_)) => {}, + (Ok(_), Err(_)) => {}, + _ => panic!("unexpected simultaneous dial result"), + } + + // Wait for listening and peer connected events + let event = subscription2.next().await.unwrap().unwrap(); + unpack_enum!(ConnectionManagerEvent::Listening(_addr) = &*event); + + let event = subscription2.next().await.unwrap().unwrap(); + assert!(count_string_occurrences(&[event], &["PeerConnected", "PeerInboundConnectFailed"]) >= 1); + + shutdown.trigger().unwrap(); + drop(conn_man1); + drop(conn_man2); + + let events1 = collect_stream!(subscription1, timeout = Duration::from_secs(5)) + .into_iter() + .map(Result::unwrap) + .collect::>(); + + let events2 = collect_stream!(subscription2, timeout = Duration::from_secs(5)) + .into_iter() + .map(Result::unwrap) + .collect::>(); + + // TODO: Investigate why two PeerDisconnected events are sometimes received + assert!(count_string_occurrences(&events1, &["PeerDisconnected", "PeerConnectWillClose"]) >= 1); + assert!(count_string_occurrences(&events2, &["PeerDisconnected", "PeerConnectWillClose"]) >= 1); +} diff --git a/base_layer/core/tests/tests/mod.rs b/comms/src/connection_manager/tests/mod.rs similarity index 95% rename from base_layer/core/tests/tests/mod.rs rename to comms/src/connection_manager/tests/mod.rs index f86a94703f..1ce1f17029 100644 --- a/base_layer/core/tests/tests/mod.rs +++ b/comms/src/connection_manager/tests/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019 The Tari Project +// Copyright 2020, The Tari Project // // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the // following conditions are met: @@ -20,4 +20,5 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -mod block_validation; +mod listener_dialer; +mod manager; diff --git a/comms/src/connection_manager/types.rs b/comms/src/connection_manager/types.rs index 30827f1553..6d73ec5f62 100644 --- a/comms/src/connection_manager/types.rs +++ b/comms/src/connection_manager/types.rs @@ -1,33 +1,54 @@ -// Copyright 2019 The Tari Project +// Copyright 2020, The Tari Project // -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: // -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. // -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. // -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. // -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::thread::JoinHandle; +use std::fmt; -use crate::connection; +/// Direction of the connection relative to this node +#[derive(Clone, Copy, Eq, PartialEq, Debug)] +pub enum ConnectionDirection { + /// Connection listens for incoming connections + Inbound, + /// Connection establishes an outbound connection + Outbound, +} + +impl ConnectionDirection { + pub fn is_inbound(&self) -> bool { + match self { + ConnectionDirection::Inbound => true, + _ => false, + } + } -pub type PeerConnectionJoinHandle = JoinHandle>; + pub fn is_outbound(&self) -> bool { + match self { + ConnectionDirection::Outbound => true, + _ => false, + } + } +} -#[must_use] -pub enum EstablishLockResult { - Ok(T), - Collision, +impl fmt::Display for ConnectionDirection { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self) + } } diff --git a/comms/src/consts.rs b/comms/src/consts.rs index 7780220025..0af8fdc2cc 100644 --- a/comms/src/consts.rs +++ b/comms/src/consts.rs @@ -22,13 +22,13 @@ use std::time::Duration; -/// The maximum number of messages that can be stored using the MessageCache of the DHT -pub const DHT_MSG_CACHE_STORAGE_CAPACITY: usize = 1000; -/// The time-to-live duration used by the MessageCache for tracking received and handled messages -pub const DHT_MSG_CACHE_TTL: Duration = Duration::from_secs(300); -/// The number of neighbouring nodes that a received message will be forwarded to -pub const DHT_FORWARD_NODE_COUNT: usize = 8; +/// The maximum number of peers to return from the flood_identities method in peer manager +pub const PEER_MANAGER_MAX_FLOOD_PEERS: usize = 1000; -/// The default length of the underlying pub/sub buffer using to publish comms messages. -/// This const is used in the CommsBuilder. -pub const COMMS_BUILDER_IMS_DEFAULT_PUB_SUB_BUFFER_LENGTH: usize = 100; +/// The amount of time to consider a peer to be offline (i.e. dial to peer will fail without trying) after a failed +/// connection attempt +pub const PEER_OFFLINE_COOLDOWN_PERIOD: Duration = Duration::from_secs(60); + +/// The envelope version. This should be increased any time a change is made to the +/// envelope proto files. +pub const ENVELOPE_VERSION: u32 = 0; diff --git a/comms/src/control_service/client.rs b/comms/src/control_service/client.rs deleted file mode 100644 index 6e52b70042..0000000000 --- a/comms/src/control_service/client.rs +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use super::{ - messages::{ControlServiceRequestType, Ping, Pong, RequestPeerConnection}, - ControlServiceError, -}; -use crate::{ - connection::{Direction, EstablishedConnection, NetAddress}, - control_service::messages::ControlServiceResponseType, - message::{Message, MessageEnvelope, MessageFlags, MessageHeader, NodeDestination}, - peer_manager::{NodeId, NodeIdentity}, - types::CommsPublicKey, -}; -use std::{convert::TryInto, sync::Arc, time::Duration}; -use tari_utilities::message_format::MessageFormat; - -/// # ControlServiceClient -/// -/// This abstracts communication messages that can be sent and received to/from a [ControlService]. -/// -/// [ControlService]: ../service/struct.ControlService.html -pub struct ControlServiceClient { - connection: EstablishedConnection, - dest_public_key: CommsPublicKey, - node_identity: Arc, -} - -impl ControlServiceClient { - /// Create a new control service client - pub fn new( - node_identity: Arc, - dest_public_key: CommsPublicKey, - connection: EstablishedConnection, - ) -> Self - { - Self { - node_identity, - connection, - dest_public_key, - } - } - - /// Get a reference to the underlying connection - pub fn connection(&self) -> &EstablishedConnection { - &self.connection - } - - /// Send a Ping message - pub fn send_ping(&self) -> Result<(), ControlServiceError> { - self.send_msg(ControlServiceRequestType::Ping, Ping {}) - } - - /// Send a Ping message and wait until the given timeout for a Pong message. - pub fn ping_pong(&self, timeout: Duration) -> Result, ControlServiceError> { - self.send_msg(ControlServiceRequestType::Ping, Ping {})?; - - match self.receive_raw_message(timeout)? { - Some(msg) => { - let header = msg.deserialize_header()?; - match header.message_type { - ControlServiceResponseType::Pong => Ok(Some(msg.deserialize_message()?)), - _ => Err(ControlServiceError::ClientUnexpectedReply), - } - }, - None => Ok(None), - } - } - - /// Wait until the given timeout for any MessageFormat message _T_. - pub fn receive_message(&self, timeout: Duration) -> Result, ControlServiceError> - where T: MessageFormat { - match self.receive_raw_message(timeout)? { - Some(msg) => { - let message = msg.deserialize_message()?; - Ok(Some(message)) - }, - None => Ok(None), - } - } - - /// Wait until the given timeout for a raw [Message]. The [Message] signature is validated, otherwise - /// an error is returned. - /// - /// [Message]: ../../message/message/struct.Message.html - pub fn receive_raw_message(&self, timeout: Duration) -> Result, ControlServiceError> { - match connection_try!(self.connection.receive(timeout.as_millis() as u32)) { - Some(mut frames) => { - if self.connection.direction() == &Direction::Inbound { - frames.drain(0..1); - } - let envelope: MessageEnvelope = frames.try_into()?; - let header = envelope.deserialize_header()?; - if header.verify_signatures(envelope.body_frame().clone())? { - let msg = - envelope.deserialize_encrypted_body(&self.node_identity.secret_key, &self.dest_public_key)?; - Ok(Some(msg)) - } else { - Err(ControlServiceError::InvalidMessageSignature) - } - }, - None => Ok(None), - } - } - - /// Send a [RequestPeerConnection] message. - /// - /// [RequestPeerConnection]: ../messages/struct.RequestPeerConnection.html - pub fn send_request_connection( - &self, - control_service_address: NetAddress, - node_id: NodeId, - ) -> Result<(), ControlServiceError> - { - let msg = RequestPeerConnection { - control_service_address, - node_id, - }; - self.send_msg(ControlServiceRequestType::RequestPeerConnection, msg) - } - - fn send_msg(&self, message_type: ControlServiceRequestType, msg: T) -> Result<(), ControlServiceError> - where T: MessageFormat { - let envelope = self.construct_envelope(message_type, msg)?; - - self.connection - .send(envelope.into_frame_set()) - .map_err(ControlServiceError::ConnectionError) - } - - fn construct_envelope( - &self, - message_type: ControlServiceRequestType, - msg: T, - ) -> Result - where - T: MessageFormat, - { - let header = MessageHeader::new(message_type)?; - let msg = Message::from_message_format(header, msg)?; - - MessageEnvelope::construct( - &self.node_identity, - self.dest_public_key.clone(), - NodeDestination::PublicKey(self.dest_public_key.clone()), - msg.to_binary()?, - MessageFlags::ENCRYPTED, - ) - .map_err(ControlServiceError::MessageError) - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::connection::{Connection, Direction, InprocAddress, ZmqContext}; - use rand::rngs::OsRng; - use tari_crypto::keys::PublicKey; - - #[test] - fn construct_envelope() { - let addr = InprocAddress::random(); - let context = ZmqContext::new(); - let conn = Connection::new(&context, Direction::Outbound).establish(&addr).unwrap(); - let node_identity = Arc::new(NodeIdentity::random_for_test(Some("127.0.0.1:9000".parse().unwrap()))); - let (_, public_key) = CommsPublicKey::random_keypair(&mut OsRng::new().unwrap()); - - let client = ControlServiceClient::new(node_identity.clone(), public_key.clone(), conn); - let envelope = client - .construct_envelope(ControlServiceRequestType::Ping, Ping {}) - .unwrap(); - - let header = envelope.deserialize_header().unwrap(); - assert_eq!(header.origin_source, node_identity.identity.public_key); - assert_eq!(header.peer_source, node_identity.identity.public_key); - assert_eq!(header.dest, NodeDestination::PublicKey(public_key)); - assert_eq!(header.flags, MessageFlags::ENCRYPTED); - } -} diff --git a/comms/src/control_service/error.rs b/comms/src/control_service/error.rs deleted file mode 100644 index c1766d2af1..0000000000 --- a/comms/src/control_service/error.rs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::{ - connection::{ConnectionError, NetAddressError}, - message::MessageError, - peer_manager::{node_identity::NodeIdentityError, PeerManagerError}, -}; -use derive_error::Error; -use tari_utilities::{ciphers::cipher::CipherError, message_format::MessageFormatError, thread_join::ThreadError}; - -#[derive(Debug, Error)] -pub enum ControlServiceError { - #[error(no_from)] - BindFailed(ConnectionError), - MessageError(MessageError), - /// Received an invalid message which cannot be handled - MessageFormatError(MessageFormatError), - /// Failed to send control message to worker - ControlMessageSendFailed, - // Failed to join on worker thread - WorkerThreadJoinFailed(ThreadError), - PeerManagerError(PeerManagerError), - ConnectionError(ConnectionError), - /// The worker thread failed to start - WorkerThreadFailedToStart, - /// Received an unencrypted message. Discarding it. - ReceivedUnencryptedMessage, - CipherError(CipherError), - /// Peer is banned, refusing connection request - PeerBanned, - /// Received message with an invalid signature - InvalidMessageSignature, - // Client Errors - /// Received an unexpected reply - ClientUnexpectedReply, - NetAddressError(NetAddressError), - /// The connection address could not be established - ConnectionAddressNotEstablished, - #[error(non_std, no_from, msg_embedded)] - ConnectionProtocolFailed(String), - NodeIdentityError(NodeIdentityError), -} diff --git a/comms/src/control_service/messages.rs b/comms/src/control_service/messages.rs deleted file mode 100644 index 542bf3b0ca..0000000000 --- a/comms/src/control_service/messages.rs +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::{ - connection::{net_address::NetAddress, zmq::CurvePublicKey}, - peer_manager::NodeId, -}; -use derive_error::Error; -use serde::{Deserialize, Serialize}; - -/// Control service request message types -#[derive(Eq, PartialEq, Hash, Serialize, Deserialize)] -pub enum ControlServiceRequestType { - RequestPeerConnection, - Ping, -} - -/// Control service response message types -#[derive(Eq, PartialEq, Hash, Serialize, Deserialize)] -pub enum ControlServiceResponseType { - AcceptPeerConnection, - RejectPeerConnection, - Pong, - ConnectRequestOutcome, -} - -/// Details required to connect to the new [PeerConnection] -/// -/// [PeerConnection]: ../../connection/peer_connection/index.html -#[derive(Serialize, Deserialize, Debug)] -pub struct PeerConnectionDetails { - pub server_key: CurvePublicKey, - pub address: NetAddress, -} - -/// Represents an outcome for the request to establish a new [PeerConnection]. -/// -/// [PeerConnection]: ../../connection/peer_connection/index.html -#[derive(Serialize, Deserialize, Debug)] -pub enum ConnectRequestOutcome { - /// Accept response to a request to open a peer connection from a remote peer. - Accepted { - /// The zeroMQ Curve public key to use for the peer connection - curve_public_key: CurvePublicKey, - /// The address to which to connect - address: NetAddress, - }, - /// Reject response to a request to open a peer connection from a remote peer. - Rejected(RejectReason), -} - -/// Represents the reason for a peer connection request being rejected -#[derive(Error, Serialize, Deserialize, Debug)] -pub enum RejectReason { - /// Peer already has an existing active peer connection - ExistingConnection, - /// A connection collision has been detected, foreign node should abandon the connection attempt - CollisionDetected, -} - -/// This represents a request to open a peer connection -/// to a remote peer. -#[derive(Serialize, Deserialize, Debug)] -pub struct RequestPeerConnection { - pub control_service_address: NetAddress, - /// The node id of this node - pub node_id: NodeId, -} - -/// Sent to the control service to test liveness -#[derive(Serialize, Deserialize, Debug)] -pub struct Ping; - -/// Sent from the control service in response to a Ping to indicate liveness -#[derive(Serialize, Deserialize, Debug)] -pub struct Pong; diff --git a/comms/src/control_service/mod.rs b/comms/src/control_service/mod.rs deleted file mode 100644 index 4b2a462c67..0000000000 --- a/comms/src/control_service/mod.rs +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -//! # Control Service -//! -//! The control service listens on the configured address for [RequestConnection] messages -//! and decides whether to connect to the requested address. -//! -//! Once a control port connection has been established. The protocol for establishing a -//! peer connection is as follows: -//! -//! Node A Node B -//! + + -//! | ReqConn(n_id, addr) | -//! | +-------------------> | -//! | | Create Inbound PeerConnection -//! | Accept(c_pk, addr) | -//! | <------------------+ | --- -//! | | | Either Accept or Reject -//! | Reject(reason) | | -//! | <------------------+ | --- -//! | | -//! | | -//! | Connect to PeerConn | -//! | +-------------------> | -//! | | -//! + + -//! -//! ```edition2018 -//! # use tari_comms::{connection::*, control_service::*, dispatcher::*, connection_manager::*, peer_manager::*, types::*}; -//! # use std::{time::Duration, sync::Arc}; -//! # use std::collections::HashMap; -//! # use rand::OsRng; -//! # use tari_storage::lmdb_store::LMDBBuilder; -//! # use lmdb_zero::db; -//! # use tari_storage::LMDBWrapper; -//! -//! let node_identity = Arc::new(NodeIdentity::random(&mut OsRng::new().unwrap(), "127.0.0.1:9000".parse().unwrap()).unwrap()); -//! -//! let context = ZmqContext::new(); -//! let listener_address = "127.0.0.1:9000".parse::().unwrap(); -//! -//! let database_name = "cs_peer_database"; -//! let datastore = LMDBBuilder::new() -//! .set_path("/tmp/") -//! .set_environment_size(10) -//! .set_max_number_of_databases(2) -//! .add_database(database_name, lmdb_zero::db::CREATE) -//! .build().unwrap(); -//! let peer_database = datastore.get_handle(database_name).unwrap(); -//! let peer_database = LMDBWrapper::new(Arc::new(peer_database)); -//! let peer_manager = Arc::new(PeerManager::new(peer_database).unwrap()); -//! -//! let conn_manager = Arc::new(ConnectionManager::new(context.clone(), node_identity.clone(), peer_manager.clone(), PeerConnectionConfig { -//! max_message_size: 1024, -//! max_connect_retries: 1, -//! max_connections: 100, -//! socks_proxy_address: None, -//! message_sink_address: InprocAddress::random(), -//! host: "127.0.0.1".parse().unwrap(), -//! peer_connection_establish_timeout: Duration::from_secs(4), -//! })); -//! -//! let service = ControlService::with_default_config( -//! context, -//! node_identity, -//! ) -//! .serve(conn_manager) -//! .unwrap(); -//! -//! service.shutdown().unwrap(); -//! ``` -//! -//! [RequestConnection]: ./messages/struct.RequestConnection.html -mod client; -mod error; -pub mod messages; -mod service; -mod types; -mod worker; - -pub use self::{ - client::ControlServiceClient, - error::ControlServiceError, - messages::ControlServiceRequestType, - service::{ControlService, ControlServiceConfig, ControlServiceHandle}, -}; diff --git a/comms/src/control_service/service.rs b/comms/src/control_service/service.rs deleted file mode 100644 index acc71cd5cf..0000000000 --- a/comms/src/control_service/service.rs +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use super::{ - error::ControlServiceError, - types::{ControlMessage, Result}, - worker::ControlServiceWorker, -}; -use crate::{ - connection::{net_address::ip::SocketAddress, NetAddress, ZmqContext}, - connection_manager::ConnectionManager, - peer_manager::NodeIdentity, - types::DEFAULT_LISTENER_ADDRESS, -}; -use log::*; -use std::{ - sync::{mpsc::SyncSender, Arc}, - thread, - time::Duration, -}; -use tari_utilities::thread_join::ThreadJoinWithTimeout; - -const LOG_TARGET: &str = "comms::control_service::service"; - -/// Configuration for [ControlService] -#[derive(Clone)] -pub struct ControlServiceConfig { - /// Which address to open a port - pub listener_address: NetAddress, - /// Optional SOCKS proxy - pub socks_proxy_address: Option, - /// The timeout for the peer to connect to the inbound connection. - /// If this timeout expires the peer connection will be shut down and discarded. - pub requested_connection_timeout: Duration, -} - -impl Default for ControlServiceConfig { - fn default() -> Self { - let listener_address = DEFAULT_LISTENER_ADDRESS.parse::().unwrap(); - ControlServiceConfig { - listener_address, - socks_proxy_address: None, - requested_connection_timeout: Duration::from_secs(5), - } - } -} - -/// The service responsible for establishing new [PeerConnection]s. -/// When `serve` is called, a worker thread starts up which listens for -/// connections on the configured `listener_address`. -pub struct ControlService { - context: ZmqContext, - config: ControlServiceConfig, - node_identity: Arc, -} - -impl ControlService { - pub fn with_default_config(context: ZmqContext, node_identity: Arc) -> Self { - Self { - context, - config: Default::default(), - node_identity, - } - } -} - -impl ControlService { - pub fn new(context: ZmqContext, node_identity: Arc, config: ControlServiceConfig) -> Self { - Self { - context, - config, - node_identity, - } - } - - pub fn serve(self, connection_manager: Arc) -> Result { - let config = self.config; - Ok(ControlServiceWorker::start(self.context.clone(), self.node_identity, config, connection_manager)?.into()) - } -} - -/// This is retured from the `ControlService::serve` method. It s a thread-safe -/// handle which can send control messages to the [ControlService] worker. -#[derive(Debug)] -pub struct ControlServiceHandle { - handle: thread::JoinHandle>, - sender: SyncSender, -} - -impl ControlServiceHandle { - /// Send a [ControlMessage::Shutdown] message to the worker thread. - pub fn shutdown(&self) -> Result<()> { - warn!(target: LOG_TARGET, "CONTROL SERVICE SHUTDOWN"); - self.sender - .send(ControlMessage::Shutdown) - .map_err(|_| ControlServiceError::ControlMessageSendFailed) - } - - pub fn timeout_join(self, timeout: Duration) -> Result<()> { - self.handle - .timeout_join(timeout) - .map_err(ControlServiceError::WorkerThreadJoinFailed) - } -} - -impl From<(thread::JoinHandle>, SyncSender)> for ControlServiceHandle { - fn from((handle, sender): (thread::JoinHandle>, SyncSender)) -> Self { - Self { handle, sender } - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn control_service_has_default() { - let context = ZmqContext::new(); - let node_identity = Arc::new(NodeIdentity::random_for_test(None)); - let control_service = ControlService::with_default_config(context, node_identity); - assert_eq!( - control_service.config.listener_address, - DEFAULT_LISTENER_ADDRESS.parse::().unwrap() - ); - assert!(control_service.config.socks_proxy_address.is_none()); - } -} diff --git a/comms/src/control_service/types.rs b/comms/src/control_service/types.rs deleted file mode 100644 index a9b3b7211c..0000000000 --- a/comms/src/control_service/types.rs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use super::error::ControlServiceError; - -/// Control Messages for the control service worker -#[derive(Debug)] -pub enum ControlMessage { - Shutdown, -} - -/// ControlService result type -pub type Result = std::result::Result; diff --git a/comms/src/control_service/worker.rs b/comms/src/control_service/worker.rs deleted file mode 100644 index edaa218eb6..0000000000 --- a/comms/src/control_service/worker.rs +++ /dev/null @@ -1,503 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use super::{ - error::ControlServiceError, - messages::{ControlServiceRequestType, RequestPeerConnection}, - service::ControlServiceConfig, - types::{ControlMessage, Result}, -}; -use crate::{ - connection::{ - connection::EstablishedConnection, - types::Direction, - Connection, - ConnectionError, - CurvePublicKey, - NetAddress, - ZmqContext, - }, - connection_manager::{ConnectionManager, EstablishLockResult}, - control_service::messages::{ConnectRequestOutcome, ControlServiceResponseType, Pong, RejectReason}, - message::{ - Frame, - FrameSet, - Message, - MessageEnvelope, - MessageEnvelopeHeader, - MessageFlags, - MessageHeader, - NodeDestination, - }, - peer_manager::{NodeId, NodeIdentity, Peer, PeerFlags, PeerManagerError}, - types::{CommsCipher, CommsPublicKey}, -}; -use log::*; -use serde::{de::DeserializeOwned, Serialize}; -use std::{ - convert::TryInto, - sync::{ - mpsc::{sync_channel, Receiver, SyncSender}, - Arc, - }, - thread, - time::Duration, -}; -use tari_crypto::keys::DiffieHellmanSharedSecret; -use tari_utilities::{byte_array::ByteArray, ciphers::cipher::Cipher, message_format::MessageFormat}; - -const LOG_TARGET: &str = "comms::control_service::worker"; -/// The maximum message size allowed for the control service. -/// Messages will transparently drop if this size is exceeded. -const CONTROL_SERVICE_MAX_MSG_SIZE: u64 = 1024; // 1kb - -/// Set the allocated stack size for each ControlServiceWorker thread -const THREAD_STACK_SIZE: usize = 256 * 1024; // 256kb - -/// The [ControlService] worker is responsible for handling incoming messages -/// to the control port and dispatching them using the message dispatcher. -pub struct ControlServiceWorker { - config: ControlServiceConfig, - receiver: Receiver, - is_running: bool, - connection_manager: Arc, - node_identity: Arc, - listener: EstablishedConnection, -} - -impl ControlServiceWorker { - /// Start the worker - /// - /// # Arguments - /// - `context` - Connection context - /// - `config` - ControlServiceConfig - /// - `connection_manager` - the `ConnectionManager` - pub fn start( - context: ZmqContext, - node_identity: Arc, - config: ControlServiceConfig, - connection_manager: Arc, - ) -> Result<(thread::JoinHandle>, SyncSender)> - { - let (sender, receiver) = sync_channel(5); - - let handle = thread::Builder::new() - .name("control-service-worker-thread".to_string()) - .stack_size(THREAD_STACK_SIZE) - .spawn(move || { - info!( - target: LOG_TARGET, - "Control service starting on {}...", config.listener_address - ); - - let listener = Self::establish_listener(&context, &config)?; - let mut worker = Self::new(node_identity, config, connection_manager, receiver, listener); - - loop { - match worker.run() { - Ok(_) => { - info!(target: LOG_TARGET, "Control service exiting loop."); - break; - }, - - Err(err) => { - error!(target: LOG_TARGET, "Worker exited with an error: {:?}", err); - info!(target: LOG_TARGET, "Restarting control service after 1 second."); - thread::sleep(Duration::from_millis(1000)); - }, - } - } - - Ok(()) - }) - .map_err(|_| ControlServiceError::WorkerThreadFailedToStart)?; - - Ok((handle, sender)) - } - - fn new( - node_identity: Arc, - config: ControlServiceConfig, - connection_manager: Arc, - receiver: Receiver, - listener: EstablishedConnection, - ) -> Self - { - Self { - config, - connection_manager, - is_running: true, - node_identity, - receiver, - listener, - } - } - - fn run(&mut self) -> Result<()> { - debug!(target: LOG_TARGET, "Control service started"); - loop { - // Read incoming messages - if let Some(frames) = connection_try!(self.listener.receive(100)) { - debug!(target: LOG_TARGET, "Received {} frames", frames.len()); - match self.process_message(frames) { - Ok(_) => info!(target: LOG_TARGET, "Message processed"), - Err(err) => error!(target: LOG_TARGET, "Error when processing message: {:?}", err), - } - } - - // Process control messages - self.process_control_messages()?; - - if !self.is_running { - break; - } - } - - Ok(()) - } - - fn process_control_messages(&mut self) -> Result<()> { - if let Some(msg) = self.receiver.recv_timeout(Duration::from_millis(5)).ok() { - debug!(target: LOG_TARGET, "Received control message: {:?}", msg); - match msg { - ControlMessage::Shutdown => { - info!(target: LOG_TARGET, "Shutting down control service"); - self.is_running = false; - }, - } - } - Ok(()) - } - - fn process_message(&self, mut frames: FrameSet) -> Result<()> { - if frames.is_empty() { - // This case should never happen as ZMQ_ROUTER adds the identity frame - warn!(target: LOG_TARGET, "Received empty frames from socket."); - return Ok(()); - } - - let envelope: MessageEnvelope = frames - .drain(1..) - .collect::() - .try_into() - .map_err(ControlServiceError::MessageError)?; - - let identity_frame = frames - .pop() - .expect("Should not happen: drained all frames but the first, but then could not pop the first frame."); - - let envelope_header = envelope.deserialize_header()?; - if !envelope_header.flags.contains(MessageFlags::ENCRYPTED) { - return Err(ControlServiceError::ReceivedUnencryptedMessage); - } - - let maybe_peer = self.get_peer(&envelope_header.peer_source)?; - if maybe_peer.map(|p| p.is_banned()).unwrap_or(false) { - return Err(ControlServiceError::PeerBanned); - } - - let decrypted_body = self.decrypt_body(envelope.body_frame(), &envelope_header.origin_source)?; - let message = - Message::from_binary(decrypted_body.as_bytes()).map_err(ControlServiceError::MessageFormatError)?; - - debug!(target: LOG_TARGET, "Handling message"); - self.handle_message(envelope_header, identity_frame, message) - } - - fn handle_message( - &self, - envelope_header: MessageEnvelopeHeader, - identity_frame: Frame, - msg: Message, - ) -> Result<()> - { - let header = msg.deserialize_header().map_err(ControlServiceError::MessageError)?; - - match header.message_type { - ControlServiceRequestType::Ping => self.handle_ping(envelope_header, identity_frame), - ControlServiceRequestType::RequestPeerConnection => { - self.handle_request_connection(envelope_header, identity_frame, msg.deserialize_message()?) - }, - } - } - - fn handle_ping(&self, envelope_header: MessageEnvelopeHeader, identity_frame: Frame) -> Result<()> { - debug!(target: LOG_TARGET, "Got ping message"); - self.send_reply( - &envelope_header.peer_source, - identity_frame, - ControlServiceResponseType::Pong, - Pong {}, - ) - } - - fn handle_request_connection( - &self, - envelope_header: MessageEnvelopeHeader, - identity_frame: Frame, - message: RequestPeerConnection, - ) -> Result<()> - { - debug!( - target: LOG_TARGET, - "RequestConnection message received for NodeId {}", message.node_id - ); - - let pm = &self.connection_manager.peer_manager(); - let public_key = &envelope_header.peer_source; - let peer = match pm.find_with_public_key(&public_key) { - Ok(peer) => { - if peer.is_banned() { - return Err(ControlServiceError::PeerBanned); - } - - pm.update_peer( - &peer.public_key, - None, - Some(vec![message.control_service_address.clone()]), - None, - )?; - - peer - }, - Err(PeerManagerError::PeerNotFoundError) => { - let node_id = &message.node_id; - - let peer = Peer::new( - public_key.clone(), - node_id.clone(), - message.control_service_address.clone().into(), - PeerFlags::empty(), - ); - - pm.add_peer(peer.clone()) - .map_err(ControlServiceError::PeerManagerError)?; - peer - }, - Err(err) => return Err(ControlServiceError::PeerManagerError(err)), - }; - - // TODO: SECURITY The node ID is not a verified value at this point (PeerNotFoundError branch above). - // An attacker can insert any node id they want to get information about other peers connections - // to this node. For instance, if they already have an active connection. - // The public key should be used as that is validated by the message signature. - - let conn_manager = &self.connection_manager; - let establish_lock_result = conn_manager.try_acquire_establish_lock(&peer.node_id, || { - self.establish_connection_protocol(&peer, &envelope_header, identity_frame.clone()) - }); - - match establish_lock_result { - EstablishLockResult::Ok(result) => result, - EstablishLockResult::Collision => { - warn!( - target: LOG_TARGET, - "COLLISION DETECTED: this node is attempting to connect to the same node which is asking to \ - connect." - ); - if self.should_reject_collision(&peer.node_id) { - warn!( - target: LOG_TARGET, - "This connection attempt should be rejected. Rejecting the request to connect" - ); - self.reject_connection(&envelope_header, identity_frame, RejectReason::CollisionDetected)?; - Ok(()) - } else { - conn_manager.with_establish_lock(&peer.node_id, || { - self.establish_connection_protocol(&peer, &envelope_header, identity_frame) - }) - } - }, - } - } - - fn establish_connection_protocol( - &self, - peer: &Peer, - envelope_header: &MessageEnvelopeHeader, - identity_frame: Frame, - ) -> Result<()> - { - let conn_manager = &self.connection_manager; - if let Some(conn) = conn_manager.get_connection(peer) { - if conn.is_active() { - debug!( - target: LOG_TARGET, - "Already have active connection to peer. Rejecting the request for connection." - ); - self.reject_connection(&envelope_header, identity_frame, RejectReason::ExistingConnection)?; - return Ok(()); - } - } - - conn_manager - .with_new_inbound_connection(&peer, |new_inbound_conn, curve_public_key| { - let address = new_inbound_conn - .get_address() - .ok_or(ControlServiceError::ConnectionAddressNotEstablished)?; - - debug!( - target: LOG_TARGET, - "[NodeId={}] Inbound peer connection established on address {}", peer.node_id, address - ); - - // Create an address which can be connected to externally - let our_host = self.node_identity.control_service_address()?.host(); - let external_address = address - .maybe_port() - .map(|port| format!("{}:{}", our_host, port)) - .or(Some(our_host)) - .unwrap() - .parse() - .map_err(ControlServiceError::NetAddressError)?; - - debug!( - target: LOG_TARGET, - "Accepting peer connection request for NodeId={:?} on address {}", peer.node_id, external_address - ); - - self.accept_connection_request(&envelope_header, identity_frame, curve_public_key, external_address)?; - - match new_inbound_conn.wait_connected_or_failure(&self.config.requested_connection_timeout) { - Ok(_) => { - debug!( - target: LOG_TARGET, - "Connection to peer connection for NodeId {} succeeded", peer.node_id, - ); - - Ok(Some(new_inbound_conn)) - }, - Err(ConnectionError::Timeout) => Ok(None), - Err(err) => Err(ControlServiceError::ConnectionError(err)), - } - }) - .map_err(|err| ControlServiceError::ConnectionProtocolFailed(format!("{}", err)))?; - - Ok(()) - } - - fn should_reject_collision(&self, node_id: &NodeId) -> bool { - &self.node_identity.identity.node_id < node_id - } - - fn reject_connection( - &self, - envelope_header: &MessageEnvelopeHeader, - identity: Frame, - reason: RejectReason, - ) -> Result<()> - { - self.send_reply( - &envelope_header.peer_source, - identity, - ControlServiceResponseType::ConnectRequestOutcome, - ConnectRequestOutcome::Rejected(reason), - ) - } - - fn accept_connection_request( - &self, - envelope_header: &MessageEnvelopeHeader, - identity: Frame, - curve_public_key: CurvePublicKey, - address: NetAddress, - ) -> Result<()> - { - self.send_reply( - &envelope_header.peer_source, - identity, - ControlServiceResponseType::ConnectRequestOutcome, - ConnectRequestOutcome::Accepted { - curve_public_key, - address, - }, - ) - } - - fn get_peer(&self, public_key: &CommsPublicKey) -> Result> { - let peer_manager = &self.connection_manager.peer_manager(); - match peer_manager.find_with_public_key(public_key) { - Ok(peer) => Ok(Some(peer)), - Err(PeerManagerError::PeerNotFoundError) => Ok(None), - Err(err) => Err(ControlServiceError::PeerManagerError(err)), - } - } - - fn construct_envelope( - &self, - dest_public_key: &CommsPublicKey, - message_type: MT, - msg: T, - flags: MessageFlags, - ) -> Result - where - T: MessageFormat, - MT: Serialize + DeserializeOwned, - MT: MessageFormat, - { - let header = MessageHeader::new(message_type)?; - let msg = Message::from_message_format(header, msg).map_err(ControlServiceError::MessageError)?; - - MessageEnvelope::construct( - &self.node_identity, - dest_public_key.clone(), - NodeDestination::PublicKey(dest_public_key.clone()), - msg.to_binary().map_err(ControlServiceError::MessageFormatError)?, - flags, - ) - .map_err(ControlServiceError::MessageError) - } - - fn send_reply( - &self, - dest_public_key: &CommsPublicKey, - identity_frame: Frame, - message_type: ControlServiceResponseType, - msg: T, - ) -> Result<()> - where - T: MessageFormat, - { - let envelope = self.construct_envelope(dest_public_key, message_type, msg, MessageFlags::ENCRYPTED)?; - let mut frames = vec![identity_frame]; - - frames.extend(envelope.into_frame_set()); - - self.listener.send(frames).map_err(ControlServiceError::ConnectionError) - } - - fn decrypt_body(&self, body: &Frame, public_key: &CommsPublicKey) -> Result { - let ecdh_shared_secret = CommsPublicKey::shared_secret(&self.node_identity.secret_key, public_key).to_vec(); - CommsCipher::open_with_integral_nonce(&body, &ecdh_shared_secret).map_err(ControlServiceError::CipherError) - } - - fn establish_listener(context: &ZmqContext, config: &ControlServiceConfig) -> Result { - debug!(target: LOG_TARGET, "Binding on address: {}", config.listener_address); - Connection::new(&context, Direction::Inbound) - .set_name("Control Service Listener") - .set_receive_hwm(10) - .set_max_message_size(Some(CONTROL_SERVICE_MAX_MSG_SIZE)) - .set_socks_proxy_addr(config.socks_proxy_address.clone()) - .establish(&config.listener_address) - .map_err(ControlServiceError::BindFailed) - } -} diff --git a/comms/src/dispatcher/dispatcher.rs b/comms/src/dispatcher/dispatcher.rs deleted file mode 100644 index af7349a65f..0000000000 --- a/comms/src/dispatcher/dispatcher.rs +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::inbound_message_service::inbound_message_publisher::PublisherError; -use derive_error::Error; -use std::{collections::HashMap, error::Error, hash::Hash}; - -#[derive(Debug, Error, Clone)] -pub enum DispatchError { - /// A dispatch route was not defined for the specific message type - MessageHandlerNotDefined, - #[error(msg_embedded, non_std, no_from)] - ResolveFailed(String), - #[error(msg_embedded, non_std, no_from)] - HandlerError(String), - PublisherError(PublisherError), -} - -impl DispatchError { - pub fn resolve_failed() -> impl Fn(E) -> Self - where E: Error { - |err| DispatchError::ResolveFailed(format!("Dispatch resolve failed: {}", err)) - } - - pub fn handler_error() -> impl Fn(E) -> Self - where E: Error { - |err| DispatchError::HandlerError(format!("Handler error: {}", err)) - } -} - -#[derive(Debug, Error, Clone)] -pub enum HandlerError { - #[error(msg_embedded, non_std, no_from)] - Failed(String), -} - -impl HandlerError { - pub fn failed() -> impl Fn(E) -> Self - where E: Error { - |err| HandlerError::Failed(format!("Handler failed with error '{}'", err)) - } -} - -/// The signature of a handler function -type HandlerFunc = fn(msg: M) -> Result<(), E>; - -/// The trait bound for type parameter K on the dispatcher i.e the route key. -/// K must be Eq + Hash + {able to be sent across threads} + -/// 'static (all references must live as long as the program BUT there are no references -/// so therefore this is simply to satisfy the closure on `thread::spawn`) -/// This saves us from having to duplicate these trait bounds whenever we -/// want specify the type parameter K (like a type alias). -pub trait DispatchableKey: Eq + Hash + Send + Sync + 'static {} - -/// Implement this trait for all types which satisfy it's trait bounds. -impl DispatchableKey for T where T: Eq + Hash + Send + Sync + 'static {} - -/// A message type resolver. The resolver is called with the dispatched message. -/// The resolver should then decide which dispatch key should be used. -pub trait DispatchResolver //: Send + 'static -// where K: DispatchableKey -{ - fn resolve(&self, msg: &M) -> Result; -} - -/// Dispatcher pattern. Links handler function to "keys" which are resolved -/// be a given type implementing [DispatchResolver]. -/// -/// ## Type Parameters -/// `K` - The route key -/// `M` - The type which is passed into the handler -/// `R` - The resolver type -/// `E` - The type of error returned from the handler -pub struct Dispatcher -where R: DispatchResolver -{ - handlers: HashMap>, - catch_all: Option>, - resolver: R, -} - -impl Dispatcher -where - K: DispatchableKey, - R: DispatchResolver, - E: Error, -{ - /// Construct a new MessageDispatcher with no defined dispatch routes - pub fn new(resolver: R) -> Dispatcher { - Dispatcher { - handlers: HashMap::new(), - resolver, - catch_all: None, - } - } - - /// This function allows a new dispatch route to be specified and added to the handlers, all received messaged that - /// are of the dispatch type will be routed to the specified handler_function - pub fn route(mut self, path_key: K, handler: HandlerFunc) -> Self { - self.handlers.insert(path_key, handler); - self - } - - /// Set the handler to use if no other handlers match - pub fn catch_all(mut self, handler: HandlerFunc) -> Self { - self.catch_all = Some(handler); - self - } - - /// This function can be used to forward a message to the correct function handler - pub fn dispatch(&self, msg: M) -> Result<(), DispatchError> { - let route_type = self.resolver.resolve(&msg)?; - self.handlers - .get(&route_type) - .or_else(|| self.catch_all.as_ref()) - .ok_or(DispatchError::MessageHandlerNotDefined) - .and_then(|handler| { - handler(msg).map_err(|err| DispatchError::HandlerError(format!("Handler error: {:?}", err))) - }) - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_route_and_dispatch() { - #[derive(Debug, Hash, Eq, PartialEq)] - pub enum DispatchType { - Unknown, - Type1, - Type2, - Type3, - } - - pub struct Message { - pub data: String, - } - - pub struct TestResolver; - - impl DispatchResolver for TestResolver { - fn resolve(&self, msg: &Message) -> Result { - // Here you would usually look at the header for a message type - Ok(match msg.data.as_ref() { - "Type1" => DispatchType::Type1, - "Type2" => DispatchType::Type2, - _ => DispatchType::Type3, - }) - } - } - // Create a common variable to determine which handler function was called by the dispatcher - static mut CALLED_FN_TYPE: DispatchType = DispatchType::Unknown; - - fn test_fn1(_msg_data: Message) -> Result<(), DispatchError> { - unsafe { - CALLED_FN_TYPE = DispatchType::Type1; - } - Ok(()) - } - - fn test_fn2(_msg_data: Message) -> Result<(), DispatchError> { - unsafe { - CALLED_FN_TYPE = DispatchType::Type2; - } - Ok(()) - } - - fn test_fn3(_msg_data: Message) -> Result<(), DispatchError> { - unsafe { - CALLED_FN_TYPE = DispatchType::Type3; - } - Ok(()) - } - - let resolver = TestResolver {}; - - let message_dispatcher = Dispatcher::new(resolver) - .route(DispatchType::Type1, test_fn1) - .route(DispatchType::Type2, test_fn2) - .route(DispatchType::Type3, test_fn3); - // Test dispatch to default route - let msg_data = Message { data: "".to_string() }; - assert!(message_dispatcher.dispatch(msg_data).is_ok()); - unsafe { - assert_eq!(CALLED_FN_TYPE, DispatchType::Type3); - } - // Test dispatch to specified type route - let msg_data = Message { - data: "Type2".to_string(), - }; - assert!(message_dispatcher.dispatch(msg_data).is_ok()); - unsafe { - assert_eq!(CALLED_FN_TYPE, DispatchType::Type2); - } - } -} diff --git a/comms/src/dispatcher/mod.rs b/comms/src/dispatcher/mod.rs deleted file mode 100644 index 92b0ccf776..0000000000 --- a/comms/src/dispatcher/mod.rs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -//! # Dispatcher -//! -//! [HandlerFunc]s and associated "keys" are given to the dispatcher using the `route` method. -//! When dispatching a message, the key is resolver using the given [DispatchResolver] implementation. -//! The associated [HandlerFunc] is retrieved and called with the given type as the first parameter. -//! -//! [DispatchResolver]: ./dispatcher/trait.DispatchResolver.html -//! [HandlerFunc]: ./dispatcher/type.HandlerFunc.html -mod dispatcher; - -pub use self::dispatcher::*; diff --git a/comms/src/domain_subscriber.rs b/comms/src/domain_subscriber.rs deleted file mode 100644 index 869c074f2c..0000000000 --- a/comms/src/domain_subscriber.rs +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright 2019. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{futures::StreamExt, message::InboundMessage, peer_manager::PeerNodeIdentity, types::CommsPublicKey}; -use derive_error::Error; -use futures::{executor::block_on, future::select, stream::FusedStream, Stream}; -use std::fmt::Debug; -use tari_utilities::message_format::MessageFormat; - -#[derive(Debug, Error, PartialEq)] -pub enum DomainSubscriberError { - /// Subscription stream ended - SubscriptionStreamEnded, - /// Error reading from the stream - StreamError, - /// Message deserialization error - MessageError, - /// Subscription Reader is not initialized - SubscriptionReaderNotInitialized, -} - -/// Information about the message received -#[derive(Debug, Clone)] -pub struct MessageInfo { - pub peer_source: PeerNodeIdentity, - pub origin_source: CommsPublicKey, -} -pub struct SyncDomainSubscription { - subscription: Option, -} -impl SyncDomainSubscription -where S: Stream + Unpin + FusedStream -{ - pub fn new(stream: S) -> Self { - SyncDomainSubscription { - subscription: Some(stream), - } - } - - pub fn receive_messages(&mut self) -> Result, DomainSubscriberError> - where T: MessageFormat { - let subscription = self.subscription.take(); - - match subscription { - Some(mut s) => { - let (stream_messages, stream_complete): (Vec, bool) = block_on(async { - let mut result = Vec::new(); - let mut complete = false; - loop { - select!( - item = s.next() => { - if let Some(item) = item { - result.push(item) - } - }, - complete => { - complete = true; - break - }, - default => break, - ); - } - (result, complete) - }); - - let mut messages = Vec::new(); - - for m in stream_messages { - messages.push(( - MessageInfo { - peer_source: m.peer_source, - origin_source: m.origin_source, - }, - m.message - .deserialize_message() - .map_err(|_| DomainSubscriberError::MessageError)?, - )); - } - - if !stream_complete { - self.subscription = Some(s); - } - - return Ok(messages); - }, - None => return Err(DomainSubscriberError::SubscriptionStreamEnded), - } - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::{ - message::Message, - peer_manager::NodeIdentity, - pub_sub_channel::{pubsub_channel, TopicPayload}, - }; - use futures::{executor::block_on, future::select, stream::TryStreamExt, SinkExt}; - use serde::{Deserialize, Serialize}; - use std::sync::Arc; - #[test] - fn topic_pub_sub() { - let (mut publisher, subscriber_factory) = pubsub_channel(10); - - #[derive(Serialize, Deserialize, Debug, Clone)] - struct Dummy { - a: u32, - b: String, - } - - let node_id = NodeIdentity::random_for_test(None); - - let messages = vec![ - ("Topic1".to_string(), Dummy { - a: 1u32, - b: "one".to_string(), - }), - ("Topic2".to_string(), Dummy { - a: 2u32, - b: "two".to_string(), - }), - ("Topic1".to_string(), Dummy { - a: 3u32, - b: "three".to_string(), - }), - ("Topic2".to_string(), Dummy { - a: 4u32, - b: "four".to_string(), - }), - ("Topic1".to_string(), Dummy { - a: 5u32, - b: "five".to_string(), - }), - ("Topic2".to_string(), Dummy { - a: 6u32, - b: "size".to_string(), - }), - ("Topic1".to_string(), Dummy { - a: 7u32, - b: "seven".to_string(), - }), - ]; - - let serialized_messages = messages.iter().map(|m| { - TopicPayload::new( - m.0.clone(), - InboundMessage::new( - node_id.identity.clone(), - node_id.identity.public_key.clone(), - Message::from_message_format(m.0.clone(), m.1.clone()).unwrap(), - ), - ) - }); - - block_on(async { - for m in serialized_messages { - publisher.send(m).await.unwrap(); - } - }); - drop(publisher); - - let mut domain_sub = - SyncDomainSubscription::new(subscriber_factory.get_subscription("Topic1".to_string()).fuse()); - - let messages = domain_sub.receive_messages::().unwrap(); - - assert_eq!( - domain_sub.receive_messages::().unwrap_err(), - DomainSubscriberError::SubscriptionStreamEnded - ); - - assert_eq!(messages.len(), 4); - assert_eq!(messages[0].1.a, 1); - assert_eq!(messages[1].1.a, 3); - assert_eq!(messages[2].1.a, 5); - assert_eq!(messages[3].1.a, 7); - } -} diff --git a/comms/src/inbound_message_service/comms_msg_handlers.rs b/comms/src/inbound_message_service/comms_msg_handlers.rs deleted file mode 100644 index 5b4594878d..0000000000 --- a/comms/src/inbound_message_service/comms_msg_handlers.rs +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::{ - consts::DHT_FORWARD_NODE_COUNT, - dispatcher::{DispatchError, DispatchResolver, DispatchableKey}, - message::{InboundMessage, Message, MessageContext, MessageFlags, MessageHeader, NodeDestination}, - outbound_message_service::BroadcastStrategy, - types::MessageDispatcher, -}; -use log::*; -use serde::{de::DeserializeOwned, Serialize}; -use std::fmt::Debug; - -const LOG_TARGET: &str = "comms::inbound_message_service::handlers"; - -/// The comms_msg_dispatcher will determine the type of message and forward it to the the correct handler -#[derive(Eq, PartialEq, Hash, Clone, Debug)] -pub enum CommsDispatchType { - // Messages of this type must be handled - Handle, - // Messages of this type must be forwarded to peers - Forward, - // Messages of this type can be ignored and discarded - Discard, -} - -/// Specify what handler function should be called for messages with different comms level dispatch types -pub fn construct_comms_msg_dispatcher() -> MessageDispatcher> -where - MType: DispatchableKey, - MType: Serialize + DeserializeOwned, - MType: Debug, -{ - MessageDispatcher::new(InboundMessageServiceResolver {}) - .route(CommsDispatchType::Handle, handler_handle) - .route(CommsDispatchType::Forward, handler_forward) - .route(CommsDispatchType::Discard, handler_discard) -} - -#[derive(Clone)] -pub struct InboundMessageServiceResolver; - -impl DispatchResolver> for InboundMessageServiceResolver -where - MType: DispatchableKey, - MType: Serialize + DeserializeOwned, - MType: Debug, -{ - /// The dispatch type is determined from the content of the MessageContext, which is used to dispatch the message to - /// the correct handler - fn resolve(&self, message_context: &MessageContext) -> Result { - let message_envelope = &message_context.message_envelope; - - // Check destination of message - let message_envelope_header = message_envelope - .deserialize_header() - .map_err(|e| DispatchError::HandlerError(format!("{}", e)))?; - - // Verify source node message signature - if !message_envelope_header - .verify_signatures(message_envelope.body_frame().clone()) - .map_err(|e| DispatchError::HandlerError(format!("{}", e)))? - { - return Ok(CommsDispatchType::Discard); - } - - let node_identity = &message_context.node_identity; - let peer_manager = &message_context.peer_manager; - - match message_envelope_header.dest { - NodeDestination::Unknown => Ok(CommsDispatchType::Handle), - NodeDestination::PublicKey(dest_public_key) => { - if node_identity.identity.public_key == dest_public_key { - Ok(CommsDispatchType::Handle) - } else if message_context.forwardable { - Ok(CommsDispatchType::Forward) - } else { - Ok(CommsDispatchType::Discard) - } - }, - NodeDestination::NodeId(dest_node_id) => { - if peer_manager - .in_network_region(&dest_node_id, &node_identity.identity.node_id, DHT_FORWARD_NODE_COUNT) - .map_err(|e| DispatchError::HandlerError(format!("{}", e)))? - { - Ok(CommsDispatchType::Handle) - } else if message_context.forwardable { - Ok(CommsDispatchType::Forward) - } else { - Ok(CommsDispatchType::Discard) - } - }, - } - } -} - -fn handler_handle(message_context: MessageContext) -> Result<(), DispatchError> -where - MType: DispatchableKey, - MType: Serialize + DeserializeOwned, - MType: Debug, -{ - let envelope = &message_context.message_envelope; - debug!( - target: LOG_TARGET, - "Received message, wire format version {:x?}", - envelope.version_frame() - ); - - // Check encryption and retrieved Message - let message_envelope_header = envelope - .deserialize_header() - .map_err(|e| DispatchError::HandlerError(format!("{}", e)))?; - - debug!( - target: LOG_TARGET, - "Handling message with origin signature {:x?}", message_envelope_header.origin_signature - ); - let node_identity = &message_context.node_identity; - let message: Message; - if message_envelope_header.flags.contains(MessageFlags::ENCRYPTED) { - debug!(target: LOG_TARGET, "Attempting to decrypt message"); - match message_context - .message_envelope - .deserialize_encrypted_body(&node_identity.secret_key, &message_envelope_header.origin_source) - { - Ok(decrypted_message_body) => { - debug!(target: LOG_TARGET, "Message successfully decrypted"); - message = decrypted_message_body; - }, - Err(_) => { - if message_envelope_header.dest == NodeDestination::Unknown { - debug!( - target: LOG_TARGET, - "Unable to decrypt message with unknown recipient, forwarding..." - ); - // Message might have been for this node if it was able to decrypt it - if message_context.forwardable { - return handler_forward(message_context); - } else { - return handler_discard(message_context); - } - } else { - warn!(target: LOG_TARGET, "Unable to decrypt message addressed to this node"); - // Message was for this node but could not be decrypted - return handler_discard(message_context); - } - }, - } - } else { - debug!(target: LOG_TARGET, "Message not encrypted"); - message = message_context - .message_envelope - .deserialize_body() - .map_err(DispatchError::handler_error())?; - }; - - // Construct InboundMessage and dispatch to handler services using domain message broker - let header: MessageHeader = message.deserialize_header().unwrap(); //.map_err(DispatchError::handler_error())?; - - debug!(target: LOG_TARGET, "Received message type: {:?}", header.message_type); - let domain_message_context = InboundMessage::new( - message_context.peer.into(), - message_envelope_header.origin_source, - message, - ); - - debug!( - target: LOG_TARGET, - "Dispatching message type: {:?}", header.message_type - ); - - let imp = acquire_write_lock!(message_context.inbound_message_publisher); - imp.publish(header.message_type, domain_message_context)?; - Ok(()) -} - -fn handler_forward(message_context: MessageContext) -> Result<(), DispatchError> -where - MType: DispatchableKey, - MType: Serialize + DeserializeOwned, - MType: Debug, -{ - // Forward message using appropriate broadcast strategy based on the destination provided in the header - let envelope = message_context.message_envelope; - let message_envelope_header = envelope - .deserialize_header() - .map_err(|e| DispatchError::HandlerError(format!("{}", e)))?; - let broadcast_strategy = BroadcastStrategy::forward( - message_context.node_identity.identity.node_id.clone(), - &message_context.peer_manager, - message_envelope_header.dest, - vec![ - message_envelope_header.origin_source, - message_envelope_header.peer_source, - ], - ) - .map_err(|e| DispatchError::HandlerError(format!("{}", e)))?; - - debug!(target: LOG_TARGET, "Forwarding message"); - message_context - .outbound_message_service - .forward_message(broadcast_strategy, envelope) - .map_err(|e| DispatchError::HandlerError(format!("{}", e))) -} - -fn handler_discard(_message_context: MessageContext) -> Result<(), DispatchError> -where - MType: DispatchableKey, - MType: Serialize + DeserializeOwned, - MType: Debug, -{ - // TODO: Add logic for discarding a message - - Ok(()) -} diff --git a/comms/src/inbound_message_service/error.rs b/comms/src/inbound_message_service/error.rs deleted file mode 100644 index 278d148ead..0000000000 --- a/comms/src/inbound_message_service/error.rs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::{ - connection::{error::ConnectionError, DealerProxyError}, - inbound_message_service::message_cache::MessageCacheError, -}; -use derive_error::Error; -use tari_utilities::thread_join::ThreadError; - -/// Error type for InboundMessageService subsystem -#[derive(Debug, Error)] -pub enum InboundError { - /// Failed to connect to inbound socket - InboundConnectionError(ConnectionError), - DealerProxyError(DealerProxyError), - MessageCacheError(MessageCacheError), - #[error(msg_embedded, non_std, no_from)] - ControlSendError(String), - /// Unable to send a control message as the control sync sender is undefined - ControlSenderUndefined, - /// Could not join the InboundMessageWorker thread - ThreadJoinError(ThreadError), - /// The thread handle is undefined and could have not been properly created - ThreadHandleUndefined, - /// Inbound message worker thread failed to start - ThreadInitializationError, -} diff --git a/comms/src/inbound_message_service/inbound_message_publisher.rs b/comms/src/inbound_message_service/inbound_message_publisher.rs deleted file mode 100644 index 005520e500..0000000000 --- a/comms/src/inbound_message_service/inbound_message_publisher.rs +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2019. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::pub_sub_channel::{TopicPayload, TopicPublisher}; -use bus_queue::Publisher; -use derive_error::Error; -use futures::{executor::block_on, prelude::*}; -use log::*; -use std::{fmt::Debug, sync::Mutex}; - -const LOG_TARGET: &str = "comms::inbound_message_service::inbound_message_publisher"; - -#[derive(Clone, Debug, Error)] -pub enum PublisherError { - /// The Thread Safety has been breached and data access has become poisoned - PoisonedAccess, - /// Publisher is None inside Mutex, indicates dead lock - PublisherLock, - /// Publisher could not send message - PublisherSendError, -} - -pub struct InboundMessagePublisher -where - MType: Send + Sync + Debug, - T: Clone + Send + Sync, -{ - publisher: Mutex>>, -} - -impl InboundMessagePublisher -where - MType: Send + Sync + 'static + Debug, - T: Clone + Send + Sync + 'static, -{ - pub fn new(publisher: Publisher>) -> InboundMessagePublisher { - info!(target: LOG_TARGET, "Inbound Message Publisher created"); - InboundMessagePublisher { - publisher: Mutex::new(Some(publisher)), - } - } - - pub fn publish(&self, message_type: MType, message: T) -> Result<(), PublisherError> { - // TODO This mutex should not be required and is only present due the IMS workers being in their own threads. - // Future refactor will remove the need for the lock and this Option container - let mut publisher_lock = self.publisher.lock().map_err(|_| PublisherError::PoisonedAccess)?; - match publisher_lock.take() { - Some(mut p) => { - info!( - target: LOG_TARGET, - "Inbound message of type {:?} about to be published", message_type - ); - - block_on(async { p.send(TopicPayload::new(message_type, message)).await }) - .map_err(|_| PublisherError::PublisherSendError)?; - - *publisher_lock = Some(p); - - Ok(()) - }, - None => Err(PublisherError::PublisherLock), - } - } -} diff --git a/comms/src/inbound_message_service/inbound_message_service.rs b/comms/src/inbound_message_service/inbound_message_service.rs deleted file mode 100644 index ab5fc60763..0000000000 --- a/comms/src/inbound_message_service/inbound_message_service.rs +++ /dev/null @@ -1,299 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use super::{error::InboundError, inbound_message_worker::*}; -use crate::{ - connection::{ - peer_connection::ControlMessage, - zmq::{InprocAddress, ZmqContext}, - }, - dispatcher::DispatchableKey, - inbound_message_service::inbound_message_publisher::InboundMessagePublisher, - message::{InboundMessage, MessageContext}, - outbound_message_service::outbound_message_service::OutboundMessageService, - peer_manager::{peer_manager::PeerManager, NodeIdentity}, - types::MessageDispatcher, -}; -use log::*; -use serde::{de::DeserializeOwned, Serialize}; -use std::{ - fmt::Debug, - sync::{mpsc::SyncSender, Arc, RwLock}, - thread::JoinHandle, - time::Duration, -}; -use tari_utilities::thread_join::ThreadJoinWithTimeout; - -const LOG_TARGET: &str = "comms::inbound_message_service"; - -/// Set the maximum waiting time for InboundMessageWorker thread to join -const THREAD_JOIN_TIMEOUT_IN_MS: Duration = Duration::from_millis(100); - -#[derive(Clone, Copy)] -pub struct InboundMessageServiceConfig { - /// Timeout used for receiving messages from the message queue - pub worker_timeout_in_ms: Duration, - /// Timeout used for listening for control messages - pub control_timeout_in_ms: Duration, -} - -impl Default for InboundMessageServiceConfig { - fn default() -> Self { - InboundMessageServiceConfig { - worker_timeout_in_ms: Duration::from_millis(100), - control_timeout_in_ms: Duration::from_millis(5), - } - } -} - -/// The InboundMessageService manages the inbound message queue. The messages received from different peers are written -/// to, and accumulate in, the inbound message queue. The InboundMessageWorker will then retrieve messages from the -/// queue and dispatch them using the dispatcher, that will check signatures and decrypt the message before being sent -/// to the InboundMessageBroker. The InboundMessageBroker will then send it to the correct handler services. -pub struct InboundMessageService -where - MType: DispatchableKey, - MType: Serialize + DeserializeOwned, - MType: Debug, -{ - config: InboundMessageServiceConfig, - context: ZmqContext, - node_identity: Arc, - message_queue_address: InprocAddress, - message_dispatcher: Arc>>, - inbound_message_publisher: Arc>>, - outbound_message_service: Arc, - peer_manager: Arc, - worker_thread_handle: Option>, - worker_control_sender: Option>, -} - -impl InboundMessageService -where - MType: DispatchableKey, - MType: Serialize + DeserializeOwned, - MType: Debug, -{ - /// Creates a new InboundMessageService that will receive message on the message_queue_address that it will then - /// dispatch - pub fn new( - config: InboundMessageServiceConfig, - context: ZmqContext, - node_identity: Arc, - message_queue_address: InprocAddress, - message_dispatcher: Arc>>, - inbound_message_publisher: Arc>>, - outbound_message_service: Arc, - peer_manager: Arc, - ) -> Self - { - InboundMessageService { - config, - context: context.clone(), - node_identity, - message_queue_address, - message_dispatcher, - inbound_message_publisher, - outbound_message_service, - peer_manager, - worker_thread_handle: None, - worker_control_sender: None, - } - } - - /// Spawn an InboundMessageWorker for the InboundMessageService - pub fn start(&mut self) -> Result<(), InboundError> { - info!(target: LOG_TARGET, "Starting inbound message service"); - let worker = InboundMessageWorker::new( - self.config, - self.context.clone(), - self.node_identity.clone(), - self.message_queue_address.clone(), - self.message_dispatcher.clone(), - self.inbound_message_publisher.clone(), - self.outbound_message_service.clone(), - self.peer_manager.clone(), - ); - let (worker_thread_handle, worker_sync_sender) = worker.start()?; - self.worker_thread_handle = Some(worker_thread_handle); - self.worker_control_sender = Some(worker_sync_sender); - Ok(()) - } - - /// Tell the underlying worker thread to shut down - pub fn shutdown(self) -> Result<(), InboundError> { - self.worker_control_sender - .ok_or(InboundError::ControlSenderUndefined)? - .send(ControlMessage::Shutdown) - .map_err(|e| InboundError::ControlSendError(format!("Failed to send control message: {:?}", e)))?; - self.worker_thread_handle - .ok_or(InboundError::ThreadHandleUndefined)? - .timeout_join(THREAD_JOIN_TIMEOUT_IN_MS) - .map_err(InboundError::ThreadJoinError)?; - Ok(()) - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::{ - connection::{ - zmq::{InprocAddress, ZmqContext}, - Connection, - Direction, - NetAddress, - }, - futures::StreamExt, - inbound_message_service::comms_msg_handlers::*, - message::{ - InboundMessage, - Message, - MessageData, - MessageEnvelope, - MessageFlags, - MessageHeader, - NodeDestination, - }, - peer_manager::{peer_manager::PeerManager, NodeIdentity, Peer, PeerFlags}, - pub_sub_channel::pubsub_channel, - }; - use crossbeam_channel as channel; - use futures::{executor::block_on, future::select}; - use serde::{Deserialize, Serialize}; - use std::{sync::Arc, thread, time::Duration}; - use tari_storage::HMapDatabase; - use tari_utilities::message_format::MessageFormat; - fn pause() { - thread::sleep(Duration::from_millis(5)); - } - - fn create_message_data_buffer(node_identity: Arc, message_envelope_body: Message) -> Vec> { - let dest_public_key = node_identity.identity.public_key.clone(); // Send to self - let message_envelope = MessageEnvelope::construct( - &node_identity, - dest_public_key.clone(), - NodeDestination::Unknown, - message_envelope_body.to_binary().unwrap(), - MessageFlags::NONE, - ) - .unwrap(); - let message_data = MessageData::new(node_identity.identity.node_id.clone(), true, message_envelope); - message_data.clone().into_frame_set() - } - - #[test] - fn test_message_queue() { - let context = ZmqContext::new(); - let node_identity = Arc::new(NodeIdentity::random_for_test(None)); - - // Create a client that will write message to the inbound message pool - let message_queue_address = InprocAddress::random(); - let client_connection = Connection::new(&context, Direction::Outbound) - .establish(&message_queue_address) - .unwrap(); - - #[derive(Debug, Hash, Eq, PartialEq, Serialize, Deserialize)] - pub enum DomainBrokerType { - Type1, - } - - // Create MessageDispatcher, InboundMessagePublisher, PeerManager, OutboundMessageService and - let message_dispatcher = Arc::new(construct_comms_msg_dispatcher::()); - - const TEST_MESSAGE_COUNT: usize = 3; - let (publisher, subscriber) = pubsub_channel(TEST_MESSAGE_COUNT); - let imp = InboundMessagePublisher::new(publisher); - let mut message_subscription = subscriber.get_subscription(DomainBrokerType::Type1).fuse(); - let inbound_message_publisher = Arc::new(RwLock::new(imp)); - - let (message_sender, _) = channel::unbounded(); - let peer_manager = Arc::new(PeerManager::new(HMapDatabase::new()).unwrap()); - // Add peer to peer manager - let peer = Peer::new( - node_identity.identity.public_key.clone(), - node_identity.identity.node_id.clone(), - "127.0.0.1:9000".parse::().unwrap().into(), - PeerFlags::empty(), - ); - peer_manager.add_peer(peer).unwrap(); - let outbound_message_service = - Arc::new(OutboundMessageService::new(node_identity.clone(), message_sender, peer_manager.clone()).unwrap()); - let ims_config = InboundMessageServiceConfig::default(); - let mut inbound_message_service = InboundMessageService::new( - ims_config, - context, - node_identity.clone(), - message_queue_address, - message_dispatcher, - inbound_message_publisher, - outbound_message_service, - peer_manager, - ); - inbound_message_service.start().unwrap(); - - // Submit Messages to the InboundMessageService - pause(); - let mut message_envelope_body_list = Vec::new(); - for i in 0..TEST_MESSAGE_COUNT { - // Construct a test message - let message_header = MessageHeader::new(DomainBrokerType::Type1).unwrap(); - // Messages with the same message body will be discarded by the DuplicateMsgCache - let message_body = format!("Test Message Body {}", i).to_string().as_bytes().to_vec(); - let message_envelope_body = Message::from_message_format(message_header, message_body).unwrap(); - message_envelope_body_list.push(message_envelope_body.clone()); - let message_data_buffer = create_message_data_buffer(node_identity.clone(), message_envelope_body); - - client_connection.send(&message_data_buffer).unwrap(); - } - - // Check that all messages reached subscribers - std::thread::sleep(Duration::from_millis(1000)); - - let msgs: Vec = block_on(async { - let mut result = Vec::new(); - - loop { - select!( - item = message_subscription.next() => {if let Some(i) = item {result.push(i)}}, - default => break, - ); - } - result - }); - - assert_eq!(msgs.len(), TEST_MESSAGE_COUNT); - for m in msgs.iter() { - assert!(message_envelope_body_list.contains(&m.message)); - } - - // Test shutdown control - let _ = inbound_message_service.shutdown(); - std::thread::sleep(Duration::from_millis(200)); - - let message_header = MessageHeader::new(DomainBrokerType::Type1).unwrap(); - let message_body = "Test Message Body".as_bytes().to_vec(); - let message_envelope_body = Message::from_message_format(message_header, message_body).unwrap(); - let message_data_buffer = create_message_data_buffer(node_identity.clone(), message_envelope_body); - assert!(client_connection.send(&message_data_buffer).is_err()); - } -} diff --git a/comms/src/inbound_message_service/inbound_message_worker.rs b/comms/src/inbound_message_service/inbound_message_worker.rs deleted file mode 100644 index e6fe0fec36..0000000000 --- a/comms/src/inbound_message_service/inbound_message_worker.rs +++ /dev/null @@ -1,406 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use super::error::InboundError; -use crate::{ - connection::{ - peer_connection::ControlMessage, - zmq::{InprocAddress, ZmqContext}, - Connection, - ConnectionError, - Direction, - SocketEstablishment, - }, - dispatcher::DispatchableKey, - inbound_message_service::{ - inbound_message_publisher::InboundMessagePublisher, - inbound_message_service::InboundMessageServiceConfig, - MessageCache, - MessageCacheConfig, - }, - message::{Frame, FrameSet, InboundMessage, MessageContext, MessageData}, - outbound_message_service::outbound_message_service::OutboundMessageService, - peer_manager::{peer_manager::PeerManager, NodeId, NodeIdentity, Peer}, - types::MessageDispatcher, -}; -use log::*; -use serde::{de::DeserializeOwned, Serialize}; -use std::{ - convert::TryFrom, - fmt::Debug, - sync::{ - mpsc::{sync_channel, Receiver, SyncSender}, - Arc, - RwLock, - }, - thread, -}; - -const LOG_TARGET: &str = "comms::inbound_message_service::worker"; - -/// Set the allocated stack size for the InboundMessageWorker thread -const THREAD_STACK_SIZE: usize = 256 * 1024; // 256kb - -/// The InboundMessageWorker retrieve messages from the inbound message queue, creates a MessageContext for the message -/// that is then dispatch using the dispatcher. -pub struct InboundMessageWorker -where - MType: DispatchableKey, - MType: Serialize + DeserializeOwned, - MType: Debug, -{ - config: InboundMessageServiceConfig, - context: ZmqContext, - node_identity: Arc, - message_queue_address: InprocAddress, - message_dispatcher: Arc>>, - inbound_message_publisher: Arc>>, - outbound_message_service: Arc, - peer_manager: Arc, - msg_cache: MessageCache, - control_receiver: Option>, - is_running: bool, -} - -impl InboundMessageWorker -where - MType: DispatchableKey, - MType: Serialize + DeserializeOwned, - MType: Debug, -{ - /// Setup a new InboundMessageWorker that will read incoming messages and dispatch them using the message_dispatcher - /// and inbound_message_broker - pub fn new( - config: InboundMessageServiceConfig, - context: ZmqContext, - node_identity: Arc, - message_queue_address: InprocAddress, - message_dispatcher: Arc>>, - inbound_message_publisher: Arc>>, - outbound_message_service: Arc, - peer_manager: Arc, - ) -> Self - { - InboundMessageWorker { - config, - context, - node_identity, - message_queue_address, - message_dispatcher, - inbound_message_publisher, - outbound_message_service, - peer_manager, - msg_cache: MessageCache::new(MessageCacheConfig::default()), - control_receiver: None, - is_running: false, - } - } - - fn lookup_peer(&self, node_id: &NodeId) -> Option { - self.peer_manager.find_with_node_id(node_id).ok() - } - - // Main loop of worker thread - fn start_worker(&mut self) -> Result<(), InboundError> { - let inbound_connection = Connection::new(&self.context, Direction::Inbound) - .set_socket_establishment(SocketEstablishment::Bind) - .establish(&self.message_queue_address) - .map_err(InboundError::InboundConnectionError)?; - - // Retrieve, process and dispatch messages - loop { - // Check for control messages - self.process_control_messages(); - - if self.is_running { - match inbound_connection.receive(self.config.worker_timeout_in_ms.as_millis() as u32) { - Ok(mut frame_set) => { - // This strips off the two ZeroMQ Identity frames introduced by the transmission to this worker - debug!(target: LOG_TARGET, "Received {} frames", frame_set.len()); - let frame_set: FrameSet = frame_set.drain(1..).collect(); - - match MessageData::try_from(frame_set) { - Ok(message_data) => { - if !self.msg_cache.contains(message_data.message_envelope.body_frame()) { - self.msg_cache - .insert(message_data.message_envelope.body_frame().clone())?; - - let peer = match self.lookup_peer(&message_data.source_node_id) { - Some(peer) => peer, - None => { - warn!( - target: LOG_TARGET, - "Received unknown node id from peer connection. Discarding message \ - from NodeId={:?}", - message_data.source_node_id - ); - continue; - }, - }; - - let message_context = MessageContext::new( - self.node_identity.clone(), - peer, - message_data.forwardable, - message_data.message_envelope, - self.outbound_message_service.clone(), - self.peer_manager.clone(), - self.inbound_message_publisher.clone(), - ); - self.message_dispatcher.dispatch(message_context).unwrap_or_else(|e| { - warn!( - target: LOG_TARGET, - "Could not dispatch message to handler - Error: {:?}", e - ); - }); - } else { - debug!(target: LOG_TARGET, "Duplicate message discarded"); - } - }, - Err(e) => { - // if unable to deserialize the MessageHeader then MUST discard the - // message - warn!( - target: LOG_TARGET, - "Message discarded as it could not be deserialised - Error: {:?}", e - ); - }, - } - }, - Err(ConnectionError::Timeout) => (), - Err(e) => { - error!( - target: LOG_TARGET, - "Error receiving messages from message queue - Error: {}", e - ); - }, - }; - } else { - break; - } - } - Ok(()) - } - - /// Start the InboundMessageWorker thread, setup the control channel and start retrieving and dispatching incoming - /// messages to handlers - pub fn start(mut self) -> Result<(thread::JoinHandle<()>, SyncSender), InboundError> { - self.is_running = true; - let (control_sync_sender, control_receiver) = sync_channel(5); - self.control_receiver = Some(control_receiver); - - let thread_handle = thread::Builder::new() - .name("inbound-message-worker-thread".to_string()) - .stack_size(THREAD_STACK_SIZE) - .spawn(move || match self.start_worker() { - Ok(_) => (), - Err(e) => { - error!(target: LOG_TARGET, "Error starting inbound message worker: {:?}", e); - }, - }) - .map_err(|_| InboundError::ThreadInitializationError)?; - Ok((thread_handle, control_sync_sender)) - } - - /// Check for control messages to manage worker thread - fn process_control_messages(&mut self) { - match &self.control_receiver { - Some(control_receiver) => { - if let Some(control_msg) = control_receiver.recv_timeout(self.config.control_timeout_in_ms).ok() { - debug!(target: LOG_TARGET, "Received control message: {:?}", control_msg); - match control_msg { - ControlMessage::Shutdown => { - info!(target: LOG_TARGET, "Shutting down worker"); - self.is_running = false; - }, - _ => {}, - } - } - }, - None => warn!(target: LOG_TARGET, "Control receive not available for worker"), - } - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::{ - connection::{Connection, Direction, NetAddress}, - futures::StreamExt, - inbound_message_service::comms_msg_handlers::construct_comms_msg_dispatcher, - message::{InboundMessage, Message, MessageEnvelope, MessageFlags, MessageHeader, NodeDestination}, - peer_manager::{peer_manager::PeerManager, NodeIdentity, PeerFlags}, - pub_sub_channel::pubsub_channel, - }; - use crossbeam_channel as channel; - use futures::{executor::block_on, future::select}; - use serde::{Deserialize, Serialize}; - use std::{ - sync::Arc, - time::{self, Duration}, - }; - use tari_storage::HMapDatabase; - use tari_utilities::{message_format::MessageFormat, thread_join::ThreadJoinWithTimeout}; - fn pause() { - thread::sleep(Duration::from_millis(5)); - } - - #[test] - fn test_dispatch_to_multiple_service_handlers() { - let context = ZmqContext::new(); - let node_identity = Arc::new(NodeIdentity::random_for_test(None)); - - #[derive(Debug, Hash, Eq, PartialEq, Serialize, Deserialize)] - pub enum DomainBrokerType { - Type1, - Type2, - } - - let message_queue_address = InprocAddress::random(); - let message_dispatcher = Arc::new(construct_comms_msg_dispatcher::()); - - let (publisher, subscriber) = pubsub_channel(10); - let imp = InboundMessagePublisher::new(publisher); - let mut message_subscription_type1 = subscriber.get_subscription(DomainBrokerType::Type1).fuse(); - let mut message_subscription_type2 = subscriber.get_subscription(DomainBrokerType::Type2).fuse(); - let inbound_message_publisher = Arc::new(RwLock::new(imp)); - - let peer_manager = Arc::new(PeerManager::new(HMapDatabase::new()).unwrap()); - // Add peer to peer manager - let peer = Peer::new( - node_identity.identity.public_key.clone(), - node_identity.identity.node_id.clone(), - "127.0.0.1:9000".parse::().unwrap().into(), - PeerFlags::empty(), - ); - let (message_sender, _) = channel::unbounded(); - peer_manager.add_peer(peer).unwrap(); - let outbound_message_service = - Arc::new(OutboundMessageService::new(node_identity.clone(), message_sender, peer_manager.clone()).unwrap()); - let ims_config = InboundMessageServiceConfig::default(); - let worker = InboundMessageWorker::new( - ims_config, - context.clone(), - node_identity.clone(), - message_queue_address.clone(), - message_dispatcher, - inbound_message_publisher, - outbound_message_service, - peer_manager, - ); - let (thread_handle, control_sync_sender) = worker.start().unwrap(); - // Give worker sufficient time to spinup thread and create a socket - std::thread::sleep(time::Duration::from_millis(100)); - - // Create a client that will send messages to the message queue - let client_connection = Connection::new(&context, Direction::Outbound) - .establish(&message_queue_address) - .unwrap(); - - // Construct test message 1 - let message_header = MessageHeader::new(DomainBrokerType::Type1).unwrap(); - let message_body = "Test Message Body1".as_bytes().to_vec(); - let message_envelope_body1 = Message::from_message_format(message_header, message_body).unwrap(); - let dest_public_key = node_identity.identity.public_key.clone(); // Send to self - let message_envelope = MessageEnvelope::construct( - &node_identity, - dest_public_key.clone(), - NodeDestination::Unknown, - message_envelope_body1.to_binary().unwrap(), - MessageFlags::NONE, - ) - .unwrap(); - let message_data1 = MessageData::new( - NodeId::from_key(&node_identity.identity.public_key).unwrap(), - true, - message_envelope, - ); - let mut message1_frame_set = Vec::new(); - message1_frame_set.extend(message_data1.clone().into_frame_set()); - - // Construct test message 2 - let message_header = MessageHeader::new(DomainBrokerType::Type2).unwrap(); - let message_body = "Test Message Body2".as_bytes().to_vec(); - let message_envelope_body2 = Message::from_message_format(message_header, message_body).unwrap(); - let message_envelope = MessageEnvelope::construct( - &node_identity, - dest_public_key.clone(), - NodeDestination::Unknown, - message_envelope_body2.to_binary().unwrap(), - MessageFlags::NONE, - ) - .unwrap(); - let message_data2 = MessageData::new( - NodeId::from_key(&node_identity.identity.public_key).unwrap(), - true, - message_envelope, - ); - let mut message2_frame_set = Vec::new(); - message2_frame_set.extend(message_data2.clone().into_frame_set()); - - // Submit Messages to the Worker - pause(); - client_connection.send(message1_frame_set.clone()).unwrap(); - client_connection.send(message2_frame_set.clone()).unwrap(); - // Send duplicate message - client_connection.send(message2_frame_set).unwrap(); - - // Retrieve messages at handler services - std::thread::sleep(Duration::from_millis(100)); - - let msgs_type1: Vec = block_on(async { - let mut result = Vec::new(); - - loop { - select!( - item = message_subscription_type1.next() => {if let Some(i) = item {result.push(i)}}, - default => break, - ); - } - result - }); - assert_eq!(msgs_type1.len(), 1); - assert_eq!(msgs_type1[0].message, message_envelope_body1); - - let msgs_type2: Vec = block_on(async { - let mut result = Vec::new(); - - loop { - select!( - item = message_subscription_type2.next() => {if let Some(i) = item {result.push(i)}}, - default => break, - ); - } - result - }); - // Should only be 1 message as the duplicate must be rejected - assert_eq!(msgs_type2.len(), 1); - assert_eq!(msgs_type2[0].message, message_envelope_body2); - - // Test worker clean shutdown - control_sync_sender.send(ControlMessage::Shutdown).unwrap(); - std::thread::sleep(time::Duration::from_millis(200)); - let _ = thread_handle.timeout_join(Duration::from_millis(3000)); - assert!(client_connection.send(message1_frame_set).is_err()); - } -} diff --git a/comms/src/inbound_message_service/message_cache.rs b/comms/src/inbound_message_service/message_cache.rs deleted file mode 100644 index 3467efe2a5..0000000000 --- a/comms/src/inbound_message_service/message_cache.rs +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::consts::{DHT_MSG_CACHE_STORAGE_CAPACITY, DHT_MSG_CACHE_TTL}; -use derive_error::Error; -use std::{hash::Hash, time::Duration}; -use ttl_cache::TtlCache; - -#[derive(Debug, Error)] -pub enum MessageCacheError { - /// A duplicate entry existed in the message cache - DuplicateEntry, -} - -#[derive(Clone, Copy)] -pub struct MessageCacheConfig { - /// The maximum number of messages that can be tracked using the MessageCache - storage_capacity: usize, - /// The Time-to-live for each stored message - msg_ttl: Duration, -} - -impl Default for MessageCacheConfig { - fn default() -> Self { - MessageCacheConfig { - storage_capacity: DHT_MSG_CACHE_STORAGE_CAPACITY, - msg_ttl: DHT_MSG_CACHE_TTL, - } - } -} - -/// The MessageCache is used to track handled messages to ensure that processing resources are not wasted on -/// duplicate messages and that these duplicate messages are not sent to services or propagate through the network. -pub struct MessageCache { - config: MessageCacheConfig, - cache: TtlCache, -} - -impl MessageCache -where K: Eq + Hash -{ - /// Create a new MessageCache with the specified configuration - pub fn new(config: MessageCacheConfig) -> Self { - Self { - config, - cache: TtlCache::new(config.storage_capacity), - } - } - - /// Insert a new message into the MessageCache with a time-to-live starting at the insertion time. It will - /// return a DuplicateEntry Error if the message has already been added into the cache. - pub fn insert(&mut self, msg: K) -> Result<(), MessageCacheError> { - match self.cache.insert(msg, (), self.config.msg_ttl) { - Some(_) => Err(MessageCacheError::DuplicateEntry), - None => Ok(()), - } - } - - /// Check if the message is available in the MessageCache - pub fn contains(&self, msg: &K) -> bool { - self.cache.contains_key(msg) - } -} -#[cfg(test)] -mod test { - use super::*; - use std::{thread, time::Duration}; - - #[test] - fn test_msg_rlu_and_ttl() { - let mut msg_cache: MessageCache = MessageCache::new(MessageCacheConfig { - storage_capacity: 3, - msg_ttl: Duration::from_millis(100), - }); - let msg1 = "msg1".to_string(); - let msg2 = "msg2".to_string(); - let msg3 = "msg3".to_string(); - let msg4 = "msg4".to_string(); - - msg_cache.insert(msg1.clone()).unwrap(); - assert!(msg_cache.contains(&msg1)); - assert!(!msg_cache.contains(&msg2)); - - msg_cache.insert(msg2.clone()).unwrap(); - assert!(msg_cache.contains(&msg1)); - assert!(msg_cache.contains(&msg2)); - assert!(!msg_cache.contains(&msg3)); - - msg_cache.insert(msg3.clone()).unwrap(); - assert!(msg_cache.contains(&msg1)); - assert!(msg_cache.contains(&msg2)); - assert!(msg_cache.contains(&msg3)); - assert!(!msg_cache.contains(&msg4)); - - thread::sleep(Duration::from_millis(50)); - msg_cache.insert(msg4.clone()).unwrap(); - - // Due to storage limits, msg1 was removed when msg4 was added - assert!(!msg_cache.contains(&msg1)); - assert!(msg_cache.contains(&msg2)); - assert!(msg_cache.contains(&msg3)); - assert!(msg_cache.contains(&msg4)); - - // msg2 and msg3 would have reached their ttl thresholds - thread::sleep(Duration::from_millis(51)); - assert!(!msg_cache.contains(&msg2)); - assert!(!msg_cache.contains(&msg3)); - assert!(msg_cache.contains(&msg4)); - } -} diff --git a/comms/src/inbound_message_service/mod.rs b/comms/src/inbound_message_service/mod.rs deleted file mode 100644 index 02ae58ec4b..0000000000 --- a/comms/src/inbound_message_service/mod.rs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -//! # Inbound Message Service -//! -//! The inbound message service is responsible for receiving messages from the active [PeerConnection]s -//! and fair-dealing them to one of the worker threads for processing. -//! -//! Worker thread will perform the following tasks: -//! -//! 1. Validate the message signature against the sender's public key. -//! 2. Attempt to decrypt the message with a ECDH shared secret if the MessageFlags::ENCRYPTED flag is set. -//! 3. Check the destination [NodeId] or [CommsPublicKey] -//! 3. Should steps 1-3 fail, forward or discard the message as necessary. See [comms_msg_handlers]. -//! 4. Otherwise, dispatch the message to one of the configured message broker routes. See [InboundMessageBroker] -//! -//! [PeerConnection]: ../connection/peer_connection/index.html -//! [comms_msg_handlers]: ./comms_msg_handlers/struct.InboundMessageServiceResolver.html -//! [InboundMessageBroker]: ./inbound_message_broker/struct.InboundMessageBroker.html -pub mod comms_msg_handlers; -pub mod error; -pub mod inbound_message_publisher; -pub mod inbound_message_service; -pub mod inbound_message_worker; -pub mod message_cache; - -use crate::{message::InboundMessage, pub_sub_channel::TopicSubscriptionFactory}; - -pub use self::{ - error::InboundError, - message_cache::{MessageCache, MessageCacheConfig}, -}; - -pub type InboundTopicSubscriptionFactory = TopicSubscriptionFactory; diff --git a/comms/src/lib.rs b/comms/src/lib.rs index 729bcdb263..8c11d10a4a 100644 --- a/comms/src/lib.rs +++ b/comms/src/lib.rs @@ -5,10 +5,10 @@ //! See [CommsBuilder] for more information on using this library. //! //! [CommsBuilder]: ./builder/index.html -#![feature(checked_duration_since)] - -#[macro_use] -extern crate futures; +// Recursion limit for futures::select! +#![recursion_limit = "512"] +// Allow `type Future = impl Future` +#![feature(type_alias_impl_trait)] #[macro_use] extern crate lazy_static; @@ -16,20 +16,39 @@ extern crate lazy_static; #[macro_use] mod macros; -pub mod builder; -#[macro_use] -pub mod connection; -pub mod connection_manager; +mod connection_manager; mod consts; -pub mod control_service; -pub mod dispatcher; -pub mod domain_subscriber; -pub mod inbound_message_service; -pub mod message; -pub mod outbound_message_service; +mod multiplexing; +mod noise; +mod proto; +pub mod protocol; + +pub mod backoff; +pub mod bounded_executor; +pub mod compat; +pub mod memsocket; pub mod peer_manager; -pub mod pub_sub_channel; +#[macro_use] +pub mod message; +pub mod net_address; +pub mod pipeline; +pub mod socks; +pub mod tor; +pub mod transports; pub mod types; -mod utils; +#[macro_use] +pub mod utils; + +mod builder; +pub use builder::{BuiltCommsNode, CommsBuilder, CommsBuilderError, CommsNode}; + +// Re-exports +pub use bytes::Bytes; + +#[cfg(test)] +pub(crate) mod test_utils; -pub use self::builder::CommsBuilder; +pub mod multiaddr { + // Re-export so that client code does not have to have multiaddr as a dependency + pub use ::multiaddr::{Error, Multiaddr, Protocol}; +} diff --git a/comms/src/macros.rs b/comms/src/macros.rs index baac40831e..8c747714ae 100644 --- a/comms/src/macros.rs +++ b/comms/src/macros.rs @@ -20,7 +20,8 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -/// Creates a setter function used with the builder pattern +/// Creates a setter function used with the builder pattern. +/// The value is moved into the function and returned out. macro_rules! setter { ($func:ident, $name: ident, Option<$type: ty>) => { pub fn $func(mut self, val: $type) -> Self { @@ -36,21 +37,43 @@ macro_rules! setter { }; } -macro_rules! acquire_lock { - ($e:expr, $m:ident) => { - match $e.$m() { - Ok(lock) => lock, - Err(poisoned) => poisoned.into_inner(), +/// Creates a setter function used with the builder pattern +/// A mutable reference is taken and returned +macro_rules! setter_mut { + ($func:ident, $name: ident, Option<$type: ty>) => { + #[allow(dead_code)] + pub fn $func(&mut self, val: $type) -> &mut Self { + self.$name = Some(val); + self + } + }; + ($func:ident, $name: ident, $type: ty) => { + #[allow(dead_code)] + pub fn $func(&mut self, val: $type) -> &mut Self { + self.$name = val; + self } }; +} + +macro_rules! recover_lock { ($e:expr) => { - acquire_lock!($e, lock) + match $e { + Ok(lock) => lock, + Err(poisoned) => { + log::warn!(target: "comms", "Lock has been POISONED and will be silently recovered"); + poisoned.into_inner() + }, + } }; } -macro_rules! acquire_write_lock { +macro_rules! acquire_lock { + ($e:expr, $m:ident) => { + recover_lock!($e.$m()) + }; ($e:expr) => { - acquire_lock!($e, write) + recover_lock!($e.lock()) }; } @@ -59,3 +82,58 @@ macro_rules! acquire_read_lock { acquire_lock!($e, read) }; } + +/// Log an error if an `Err` is returned from the `$expr`. If the given expression is `Ok(v)`, +/// `Some(v)` is returned, otherwise `None` is returned (same as `Result::ok`). +/// Useful in cases where the error should be logged and ignored. +/// instead of writing `if let Err(err) = my_error_call() { error!(...) }`, you can write +/// `log_if_error!(my_error_call())` +/// +/// ```edition2018,no_compile +/// # use tari_common::log_if_error; +/// let opt = log_if_error!(target: "docs", level: debug, Result::<(), _>::Err("this will be logged as 'error' tag"), "Error: {error}"); +/// assert_eq!(opt, None); +/// ``` +#[macro_export] +macro_rules! log_if_error { + (level:$level:ident, target:$target:expr, $expr:expr, $msg:expr, $($args:tt),* $(,)*) => {{ + match $expr { + Ok(v) => Some(v), + Err(err) => { + log::$level!(target: $target, $msg, $($args,)* error = err); + None + } + } + }}; + (target:$target:expr, $expr:expr, $msg:expr, $($args:tt),* $(,)*) => {{ + log_if_error!(level:error, target:$target, $expr, $msg, $($args),*) + }}; + (level:$level:ident, $expr:expr, $msg:expr, $($args:tt),* $(,)*) => {{ + log_if_error!(level:$level, target:"$crate", $expr, $msg, $($args),*) + }}; + ($expr:expr, $msg:expr, $($args:tt)* $(,)*) => {{ + log_if_error!(level:error, target:"$crate", $expr, $msg, $($args),*) + }}; +} + +#[macro_export] +macro_rules! log_if_error_fmt { + (level: $level:ident, target: $target:expr, $expr:expr, $($args:tt)+) => {{ + match $expr { + Ok(v) => Some(v), + Err(_) => { + log::$level!(target: $target, $($args)+); + None + } + } + }}; + (level:$level:ident, $expr:expr, $($args:tt)+) => {{ + log_if_error_fmt!(level:$level, target: "$crate", $expr, $($args)+) + }}; + (target: $target:expr, $expr:expr , $($args:tt)+) => {{ + log_if_error_fmt!(level:error, target: $target, $expr, $($args)+) + }}; + ($msg:expr, $expr:expr, $($args:tt)+) => {{ + log_if_error_fmt!(level:error, target: "$crate", $expr, $($args)+) + }}; +} diff --git a/comms/src/memsocket/mod.rs b/comms/src/memsocket/mod.rs new file mode 100644 index 0000000000..105425b028 --- /dev/null +++ b/comms/src/memsocket/mod.rs @@ -0,0 +1,689 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Copyright (c) The Libra Core Contributors +// SPDX-License-Identifier: Apache-2.0 + +use bytes::{Buf, Bytes}; +use futures::{ + channel::mpsc::{self, UnboundedReceiver, UnboundedSender}, + io::{AsyncRead, AsyncWrite, Error, ErrorKind, Result}, + ready, + stream::{FusedStream, Stream}, + task::{Context, Poll}, +}; +use std::{ + collections::{hash_map::Entry, HashMap}, + num::NonZeroU16, + pin::Pin, + sync::Mutex, +}; + +lazy_static! { + static ref SWITCHBOARD: Mutex = Mutex::new(SwitchBoard(HashMap::default(), 1)); +} + +enum Slot { + InUse(T), + Acquired, +} + +impl Slot { + pub fn in_use(&self) -> Option<&T> { + match &self { + Slot::InUse(t) => Some(t), + _ => None, + } + } +} + +struct SwitchBoard(HashMap>>, u16); + +pub fn acquire_next_memsocket_port() -> NonZeroU16 { + let mut switchboard = (&*SWITCHBOARD).lock().unwrap(); + let port = loop { + let port = NonZeroU16::new(switchboard.1).unwrap_or_else(|| unreachable!()); + + // The switchboard is full and all ports are in use + if switchboard.0.len() == (std::u16::MAX - 1) as usize { + panic!("All memsocket addresses in use!"); + } + + // Instead of overflowing to 0, resume searching at port 1 since port 0 isn't a + // valid port to bind to. + if switchboard.1 == std::u16::MAX { + switchboard.1 = 1; + } else { + switchboard.1 += 1; + } + + if !switchboard.0.contains_key(&port) { + break port; + } + }; + + switchboard.0.insert(port, Slot::Acquired); + port +} + +pub fn release_memsocket_port(port: NonZeroU16) { + let mut switchboard = (&*SWITCHBOARD).lock().unwrap(); + if let Entry::Occupied(entry) = switchboard.0.entry(port) { + match *entry.get() { + Slot::Acquired => { + entry.remove_entry(); + }, + Slot::InUse(_) => panic!("cannot release memsocket port while InUse"), + } + } +} + +/// An in-memory socket server, listening for connections. +/// +/// After creating a `MemoryListener` by [`bind`]ing it to a socket address, it listens +/// for incoming connections. These can be accepted by awaiting elements from the +/// async stream of incoming connections, [`incoming`][`MemoryListener::incoming`]. +/// +/// The socket will be closed when the value is dropped. +/// +/// [`bind`]: #method.bind +/// [`MemoryListener::incoming`]: #method.incoming +/// +/// # Examples +/// +/// ```rust,no_run +/// use std::io::Result; +/// +/// use tari_comms::memsocket::{MemoryListener, MemorySocket}; +/// use futures::prelude::*; +/// +/// async fn write_stormlight(mut stream: MemorySocket) -> Result<()> { +/// let msg = b"The most important step a person can take is always the next one."; +/// stream.write_all(msg).await?; +/// stream.flush().await +/// } +/// +/// async fn listen() -> Result<()> { +/// let mut listener = MemoryListener::bind(16)?; +/// let mut incoming = listener.incoming(); +/// +/// // accept connections and process them serially +/// while let Some(stream) = incoming.next().await { +/// write_stormlight(stream?).await?; +/// } +/// Ok(()) +/// } +/// ``` +#[derive(Debug)] +pub struct MemoryListener { + incoming: UnboundedReceiver, + port: NonZeroU16, +} + +impl Drop for MemoryListener { + fn drop(&mut self) { + let mut switchboard = (&*SWITCHBOARD).lock().unwrap(); + // Remove the Sending side of the channel in the switchboard when + // MemoryListener is dropped + switchboard.0.remove(&self.port); + } +} + +impl MemoryListener { + /// Creates a new `MemoryListener` which will be bound to the specified + /// port. + /// + /// The returned listener is ready for accepting connections. + /// + /// Binding with a port number of 0 will request that a port be assigned + /// to this listener. The port allocated can be queried via the + /// [`local_addr`] method. + /// + /// # Examples + /// Create a MemoryListener bound to port 16: + /// + /// ```rust,no_run + /// use tari_comms::memsocket::MemoryListener; + /// + /// # fn main () -> ::std::io::Result<()> { + /// let listener = MemoryListener::bind(16)?; + /// # Ok(())} + /// ``` + /// + /// [`local_addr`]: #method.local_addr + pub fn bind(port: u16) -> Result { + let mut switchboard = (&*SWITCHBOARD).lock().unwrap(); + + // Get the port we should bind to. If 0 was given, use a random port + let port = if let Some(port) = NonZeroU16::new(port) { + match switchboard.0.get(&port) { + Some(Slot::InUse(_)) => return Err(ErrorKind::AddrInUse.into()), + Some(Slot::Acquired) | None => port, + } + } else { + loop { + let port = NonZeroU16::new(switchboard.1).unwrap_or_else(|| unreachable!()); + + // The switchboard is full and all ports are in use + if switchboard.0.len() == (std::u16::MAX - 1) as usize { + return Err(ErrorKind::AddrInUse.into()); + } + + // Instead of overflowing to 0, resume searching at port 1 since port 0 isn't a + // valid port to bind to. + if switchboard.1 == std::u16::MAX { + switchboard.1 = 1; + } else { + switchboard.1 += 1; + } + + if !switchboard.0.contains_key(&port) { + break port; + } + } + }; + + let (sender, receiver) = mpsc::unbounded(); + switchboard.0.insert(port, Slot::InUse(sender)); + + Ok(Self { + incoming: receiver, + port, + }) + } + + /// Returns the local address that this listener is bound to. + /// + /// This can be useful, for example, when binding to port 0 to figure out + /// which port was actually bound. + /// + /// # Examples + /// + /// ```rust + /// use tari_comms::memsocket::MemoryListener; + /// + /// # fn main () -> ::std::io::Result<()> { + /// let listener = MemoryListener::bind(16)?; + /// + /// assert_eq!(listener.local_addr(), 16); + /// # Ok(())} + /// ``` + pub fn local_addr(&self) -> u16 { + self.port.get() + } + + /// Consumes this listener, returning a stream of the sockets this listener + /// accepts. + /// + /// This method returns an implementation of the `Stream` trait which + /// resolves to the sockets the are accepted on this listener. + /// + /// # Examples + /// + /// ```rust,no_run + /// use futures::prelude::*; + /// use tari_comms::memsocket::MemoryListener; + /// + /// # async fn work () -> ::std::io::Result<()> { + /// let mut listener = MemoryListener::bind(16)?; + /// let mut incoming = listener.incoming(); + /// + /// // accept connections and process them serially + /// while let Some(stream) = incoming.next().await { + /// match stream { + /// Ok(stream) => { + /// println!("new connection!"); + /// }, + /// Err(e) => { /* connection failed */ } + /// } + /// } + /// # Ok(())} + /// ``` + pub fn incoming(&mut self) -> Incoming<'_> { + Incoming { inner: self } + } + + fn poll_accept(&mut self, context: &mut Context) -> Poll> { + match Pin::new(&mut self.incoming).poll_next(context) { + Poll::Ready(Some(socket)) => Poll::Ready(Ok(socket)), + Poll::Ready(None) => { + let err = Error::new(ErrorKind::Other, "MemoryListener unknown error"); + Poll::Ready(Err(err)) + }, + Poll::Pending => Poll::Pending, + } + } +} + +/// Stream returned by the `MemoryListener::incoming` function representing the +/// stream of sockets received from a listener. +#[must_use = "streams do nothing unless polled"] +#[derive(Debug)] +pub struct Incoming<'a> { + inner: &'a mut MemoryListener, +} + +impl<'a> Stream for Incoming<'a> { + type Item = Result; + + fn poll_next(mut self: Pin<&mut Self>, context: &mut Context) -> Poll> { + let socket = ready!(self.inner.poll_accept(context)?); + Poll::Ready(Some(Ok(socket))) + } +} + +/// An in-memory stream between two local sockets. +/// +/// A `MemorySocket` can either be created by connecting to an endpoint, via the +/// [`connect`] method, or by [accepting] a connection from a [listener]. +/// It can be read or written to using the `AsyncRead`, `AsyncWrite`, and related +/// extension traits in `futures::io`. +/// +/// # Examples +/// +/// ```rust, no_run +/// use futures::prelude::*; +/// use tari_comms::memsocket::MemorySocket; +/// +/// # async fn run() -> ::std::io::Result<()> { +/// let (mut socket_a, mut socket_b) = MemorySocket::new_pair(); +/// +/// socket_a.write_all(b"stormlight").await?; +/// socket_a.flush().await?; +/// +/// let mut buf = [0; 10]; +/// socket_b.read_exact(&mut buf).await?; +/// assert_eq!(&buf, b"stormlight"); +/// +/// # Ok(())} +/// ``` +/// +/// [`connect`]: struct.MemorySocket.html#method.connect +/// [accepting]: struct.MemoryListener.html#method.accept +/// [listener]: struct.MemoryListener.html +#[derive(Debug)] +pub struct MemorySocket { + incoming: UnboundedReceiver, + outgoing: UnboundedSender, + current_buffer: Option, + seen_eof: bool, +} + +impl MemorySocket { + /// Construct both sides of an in-memory socket. + /// + /// # Examples + /// + /// ```rust + /// use tari_comms::memsocket::MemorySocket; + /// + /// let (socket_a, socket_b) = MemorySocket::new_pair(); + /// ``` + pub fn new_pair() -> (Self, Self) { + let (a_tx, a_rx) = mpsc::unbounded(); + let (b_tx, b_rx) = mpsc::unbounded(); + let a = Self { + incoming: a_rx, + outgoing: b_tx, + current_buffer: None, + seen_eof: false, + }; + let b = Self { + incoming: b_rx, + outgoing: a_tx, + current_buffer: None, + seen_eof: false, + }; + + (a, b) + } + + /// Create a new in-memory Socket connected to the specified port. + /// + /// This function will create a new MemorySocket socket and attempt to connect it to + /// the `port` provided. + /// + /// # Examples + /// + /// ```rust,no_run + /// use tari_comms::memsocket::MemorySocket; + /// + /// # fn main () -> ::std::io::Result<()> { + /// let socket = MemorySocket::connect(16)?; + /// # Ok(())} + /// ``` + pub fn connect(port: u16) -> Result { + let mut switchboard = (&*SWITCHBOARD).lock().unwrap(); + + // Find port to connect to + let port = NonZeroU16::new(port).ok_or_else(|| ErrorKind::AddrNotAvailable)?; + + let sender = switchboard + .0 + .get_mut(&port) + .and_then(|slot| slot.in_use()) + .ok_or_else(|| ErrorKind::AddrNotAvailable)?; + + let (socket_a, socket_b) = Self::new_pair(); + // Send the socket to the listener + if let Err(e) = sender.unbounded_send(socket_a) { + if e.is_disconnected() { + return Err(ErrorKind::AddrNotAvailable.into()); + } + + unreachable!(); + } + + Ok(socket_b) + } +} + +impl AsyncRead for MemorySocket { + /// Attempt to read from the `AsyncRead` into `buf`. + fn poll_read(mut self: Pin<&mut Self>, mut context: &mut Context, buf: &mut [u8]) -> Poll> { + if self.incoming.is_terminated() { + if self.seen_eof { + return Poll::Ready(Err(ErrorKind::UnexpectedEof.into())); + } else { + self.seen_eof = true; + return Poll::Ready(Ok(0)); + } + } + + let mut bytes_read = 0; + + loop { + // If we're already filled up the buffer then we can return + if bytes_read == buf.len() { + return Poll::Ready(Ok(bytes_read)); + } + + match self.current_buffer { + // We have data to copy to buf + Some(ref mut current_buffer) if current_buffer.len() > 0 => { + let bytes_to_read = ::std::cmp::min(buf.len() - bytes_read, current_buffer.len()); + debug_assert!(bytes_to_read > 0); + + buf[bytes_read..(bytes_read + bytes_to_read)] + .copy_from_slice(current_buffer.slice(0..bytes_to_read).as_ref()); + + current_buffer.advance(bytes_to_read); + + bytes_read += bytes_to_read; + }, + + // Either we've exhausted our current buffer or don't have one + _ => { + self.current_buffer = { + match Pin::new(&mut self.incoming).poll_next(&mut context) { + Poll::Pending => { + // If we've read anything up to this point return the bytes read + if bytes_read > 0 { + return Poll::Ready(Ok(bytes_read)); + } else { + return Poll::Pending; + } + }, + Poll::Ready(Some(buf)) => Some(buf), + Poll::Ready(None) => return Poll::Ready(Ok(bytes_read)), + } + }; + }, + } + } + } +} + +impl AsyncWrite for MemorySocket { + /// Attempt to write bytes from `buf` into the outgoing channel. + fn poll_write(mut self: Pin<&mut Self>, context: &mut Context, buf: &[u8]) -> Poll> { + let len = buf.len(); + + match self.outgoing.poll_ready(context) { + Poll::Ready(Ok(())) => { + if let Err(e) = self.outgoing.start_send(Bytes::copy_from_slice(buf)) { + if e.is_disconnected() { + return Poll::Ready(Err(Error::new(ErrorKind::BrokenPipe, e))); + } + + // Unbounded channels should only ever have "Disconnected" errors + unreachable!(); + } + }, + Poll::Ready(Err(e)) => { + if e.is_disconnected() { + return Poll::Ready(Err(Error::new(ErrorKind::BrokenPipe, e))); + } + + // Unbounded channels should only ever have "Disconnected" errors + unreachable!(); + }, + Poll::Pending => return Poll::Pending, + } + + Poll::Ready(Ok(len)) + } + + /// Attempt to flush the channel. Cannot Fail. + fn poll_flush(self: Pin<&mut Self>, _context: &mut Context) -> Poll> { + Poll::Ready(Ok(())) + } + + /// Attempt to close the channel. Cannot Fail. + fn poll_close(self: Pin<&mut Self>, _context: &mut Context) -> Poll> { + self.outgoing.close_channel(); + + Poll::Ready(Ok(())) + } +} + +#[cfg(test)] +mod test { + use super::*; + use futures::{ + executor::block_on, + io::{AsyncReadExt, AsyncWriteExt}, + stream::StreamExt, + }; + use std::io::Result; + + #[test] + fn listener_bind() -> Result<()> { + let port = acquire_next_memsocket_port().into(); + let listener = MemoryListener::bind(port)?; + assert_eq!(listener.local_addr(), port); + + Ok(()) + } + + #[test] + fn simple_connect() -> Result<()> { + let port = acquire_next_memsocket_port().into(); + let mut listener = MemoryListener::bind(port)?; + + let mut dialer = MemorySocket::connect(port)?; + let mut listener_socket = block_on(listener.incoming().next()).unwrap()?; + + block_on(dialer.write_all(b"foo"))?; + block_on(dialer.flush())?; + + let mut buf = [0; 3]; + block_on(listener_socket.read_exact(&mut buf))?; + assert_eq!(&buf, b"foo"); + + Ok(()) + } + + #[test] + fn listen_on_port_zero() -> Result<()> { + let mut listener = MemoryListener::bind(0)?; + let listener_addr = listener.local_addr(); + + let mut dialer = MemorySocket::connect(listener_addr)?; + let mut listener_socket = block_on(listener.incoming().next()).unwrap()?; + + block_on(dialer.write_all(b"foo"))?; + block_on(dialer.flush())?; + + let mut buf = [0; 3]; + block_on(listener_socket.read_exact(&mut buf))?; + assert_eq!(&buf, b"foo"); + + block_on(listener_socket.write_all(b"bar"))?; + block_on(listener_socket.flush())?; + + let mut buf = [0; 3]; + block_on(dialer.read_exact(&mut buf))?; + assert_eq!(&buf, b"bar"); + + Ok(()) + } + + #[test] + fn listener_correctly_frees_port_on_drop() -> Result<()> { + fn connect_on_port(port: u16) -> Result<()> { + let mut listener = MemoryListener::bind(port)?; + let mut dialer = MemorySocket::connect(port)?; + let mut listener_socket = block_on(listener.incoming().next()).unwrap()?; + + block_on(dialer.write_all(b"foo"))?; + block_on(dialer.flush())?; + + let mut buf = [0; 3]; + block_on(listener_socket.read_exact(&mut buf))?; + assert_eq!(&buf, b"foo"); + + Ok(()) + } + + let port = acquire_next_memsocket_port().into(); + connect_on_port(port)?; + connect_on_port(port)?; + + Ok(()) + } + + #[test] + fn simple_write_read() -> Result<()> { + let (mut a, mut b) = MemorySocket::new_pair(); + + block_on(a.write_all(b"hello world"))?; + block_on(a.flush())?; + drop(a); + + let mut v = Vec::new(); + block_on(b.read_to_end(&mut v))?; + assert_eq!(v, b"hello world"); + + Ok(()) + } + + #[test] + fn partial_read() -> Result<()> { + let (mut a, mut b) = MemorySocket::new_pair(); + + block_on(a.write_all(b"foobar"))?; + block_on(a.flush())?; + + let mut buf = [0; 3]; + block_on(b.read_exact(&mut buf))?; + assert_eq!(&buf, b"foo"); + block_on(b.read_exact(&mut buf))?; + assert_eq!(&buf, b"bar"); + + Ok(()) + } + + #[test] + fn partial_read_write_both_sides() -> Result<()> { + let (mut a, mut b) = MemorySocket::new_pair(); + + block_on(a.write_all(b"foobar"))?; + block_on(a.flush())?; + block_on(b.write_all(b"stormlight"))?; + block_on(b.flush())?; + + let mut buf_a = [0; 5]; + let mut buf_b = [0; 3]; + block_on(a.read_exact(&mut buf_a))?; + assert_eq!(&buf_a, b"storm"); + block_on(b.read_exact(&mut buf_b))?; + assert_eq!(&buf_b, b"foo"); + + block_on(a.read_exact(&mut buf_a))?; + assert_eq!(&buf_a, b"light"); + block_on(b.read_exact(&mut buf_b))?; + assert_eq!(&buf_b, b"bar"); + + Ok(()) + } + + #[test] + fn many_small_writes() -> Result<()> { + let (mut a, mut b) = MemorySocket::new_pair(); + + block_on(a.write_all(b"words"))?; + block_on(a.write_all(b" "))?; + block_on(a.write_all(b"of"))?; + block_on(a.write_all(b" "))?; + block_on(a.write_all(b"radiance"))?; + block_on(a.flush())?; + drop(a); + + let mut buf = [0; 17]; + block_on(b.read_exact(&mut buf))?; + assert_eq!(&buf, b"words of radiance"); + + Ok(()) + } + + #[test] + fn read_zero_bytes() -> Result<()> { + let (mut a, mut b) = MemorySocket::new_pair(); + + block_on(a.write_all(b"way of kings"))?; + block_on(a.flush())?; + + let mut buf = [0; 12]; + block_on(b.read_exact(&mut buf[0..0]))?; + assert_eq!(buf, [0; 12]); + block_on(b.read_exact(&mut buf))?; + assert_eq!(&buf, b"way of kings"); + + Ok(()) + } + + #[test] + fn read_bytes_with_large_buffer() -> Result<()> { + let (mut a, mut b) = MemorySocket::new_pair(); + + block_on(a.write_all(b"way of kings"))?; + block_on(a.flush())?; + + let mut buf = [0; 20]; + let bytes_read = block_on(b.read(&mut buf))?; + assert_eq!(bytes_read, 12); + assert_eq!(&buf[0..12], b"way of kings"); + + Ok(()) + } +} diff --git a/comms/src/message/envelope.rs b/comms/src/message/envelope.rs index bcf45460ad..fcdf975c57 100644 --- a/comms/src/message/envelope.rs +++ b/comms/src/message/envelope.rs @@ -1,455 +1,222 @@ -// Copyright 2019 The Tari Project +// Copyright 2019, The Tari Project // -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: // -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. // -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. // -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. // -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use super::Message; +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::{MessageError, MessageFlags}; use crate::{ - message::{error::MessageError, Frame, FrameSet, MessageFlags, NodeDestination}, - peer_manager::NodeIdentity, - types::{CommsCipher, CommsPublicKey, MESSAGE_PROTOCOL_VERSION, WIRE_PROTOCOL_VERSION}, - utils::crypto, + consts::ENVELOPE_VERSION, + types::{CommsPublicKey, CommsSecretKey}, + utils::signature, }; -use rand::OsRng; -use serde::{Deserialize, Serialize}; -use std::convert::TryFrom; -use tari_crypto::keys::{DiffieHellmanSharedSecret, PublicKey}; -use tari_utilities::{ciphers::cipher::Cipher, message_format::MessageFormat}; - -const FRAMES_PER_MESSAGE: usize = 3; - -/// Generate the challenge for the origin signature -fn origin_challenge(dest: NodeDestination, mut body: Vec) -> Result, MessageError> { - let mut challenge = dest.to_binary().map_err(MessageError::MessageFormatError)?; - challenge.append(&mut body); - Ok(challenge) -} +use bytes::Bytes; +use rand::rngs::OsRng; +use std::convert::TryInto; +use tari_crypto::tari_utilities::{message_format::MessageFormat, ByteArray}; -/// Generate the challenge for the peer signature -fn peer_challenge(origin_signature: Vec, mut body: Vec) -> Result, MessageError> { - let mut challenge = origin_signature; - challenge.append(&mut body); - Ok(challenge) -} - -/// Generate a signature for the origin that confirms the dest and body -fn origin_signature( - node_identity: &NodeIdentity, - dest: NodeDestination, - body: Vec, -) -> Result, MessageError> -{ - let origin_signature = crypto::sign( - &mut OsRng::new().unwrap(), - node_identity.secret_key.clone(), - &origin_challenge(dest, body)?, - ) - .map_err(MessageError::SchnorrSignatureError)?; - origin_signature.to_binary().map_err(MessageError::MessageFormatError) -} - -/// Generate a signature for the peer that confirms the origin_source and body -fn peer_signature( - node_identity: &NodeIdentity, - origin_signature: Vec, - body: Vec, -) -> Result, MessageError> -{ - let peer_signature = crypto::sign( - &mut OsRng::new().unwrap(), - node_identity.secret_key.clone(), - &peer_challenge(origin_signature, body)?, - ) - .map_err(MessageError::SchnorrSignatureError)?; - peer_signature.to_binary().map_err(MessageError::MessageFormatError) -} +// Re-export protos +pub use crate::proto::envelope::*; /// Represents data that every message contains. /// As described in [RFC-0172](https://rfc.tari.com/RFC-0172_PeerToPeerMessagingProtocol.html#messaging-structure) -#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct MessageEnvelopeHeader { - pub version: u8, - pub origin_source: CommsPublicKey, - pub peer_source: CommsPublicKey, - pub dest: NodeDestination, - pub origin_signature: Vec, - pub peer_signature: Vec, + pub public_key: CommsPublicKey, + pub signature: Bytes, pub flags: MessageFlags, } -impl MessageEnvelopeHeader { - /// Verify that the signature provided is valid for the given body - pub fn verify_signatures(&self, body: Frame) -> Result { - let origin_verif = crypto::verify( - &self.origin_source, - self.origin_signature.as_slice(), - origin_challenge(self.dest.clone(), body.clone())?, - )?; - let peer_verif = crypto::verify( - &self.peer_source, - self.peer_signature.as_slice(), - peer_challenge(self.origin_signature.clone(), body)?, - )?; - Ok(origin_verif & peer_verif) - } -} - -/// Represents a message which is about to go on or has just come off the wire. -/// As described in [RFC-0172](https://rfc.tari.com/RFC-0172_PeerToPeerMessagingProtocol.html#messaging-structure) -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct MessageEnvelope { - frames: FrameSet, -} - -impl MessageEnvelope { - /// Create a new MessageEnvelope from four frames - pub fn new(version: Frame, header: Frame, body: Frame) -> Self { - MessageEnvelope { - frames: vec![version, header, body], - } - } - - /// Sign a message, construct a MessageEnvelopeHeader and return the resulting MessageEnvelope - pub fn construct( - node_identity: &NodeIdentity, - dest_public_key: CommsPublicKey, - dest: NodeDestination, - mut body: Frame, +impl Envelope { + /// Sign a message, construct an Envelope with a Header + pub fn construct_signed( + secret_key: &CommsSecretKey, + public_key: &CommsPublicKey, + body: Bytes, flags: MessageFlags, ) -> Result { - if flags.contains(MessageFlags::ENCRYPTED) { - body = encrypt_envelope_body(&node_identity.secret_key, &dest_public_key, &body)? + // Sign this body + let header_signature = { + let sig = + signature::sign(&mut OsRng, secret_key.clone(), &body).map_err(MessageError::SchnorrSignatureError)?; + sig.to_binary().map_err(MessageError::MessageFormatError) + }?; + + Ok(Envelope { + version: ENVELOPE_VERSION, + header: Some(EnvelopeHeader { + public_key: public_key.to_vec(), + signature: header_signature, + flags: flags.bits(), + }), + body: body.to_vec(), + }) + } + + /// Verify that the signature provided is valid for the given body + pub fn verify_signature(&self) -> Result { + match self + .header + .as_ref() + .map(|header| (header, header.get_comms_public_key())) + { + Some((header, Some(public_key))) => signature::verify(&public_key, &header.signature, &self.body), + _ => Ok(false), } + } - let origin_signature = origin_signature(node_identity, dest.clone(), body.clone())?; - let peer_signature = peer_signature(node_identity, origin_signature.clone(), body.clone())?; + /// Returns true if the message contains a valid public key in the header, otherwise + /// false + pub fn is_valid(&self) -> bool { + self.get_public_key().is_some() + } - let header = MessageEnvelopeHeader { - version: MESSAGE_PROTOCOL_VERSION, - origin_source: node_identity.identity.public_key.clone(), - peer_source: node_identity.identity.public_key.clone(), - dest, - origin_signature, - peer_signature, - flags, - }; + /// Returns a valid public key from the header of this envelope, or None if the + /// public key is invalid + pub fn get_public_key(&self) -> Option { + self.header.as_ref().and_then(|header| header.get_comms_public_key()) + } +} - Ok(Self::new( - vec![WIRE_PROTOCOL_VERSION], - header.to_binary().map_err(MessageError::MessageFormatError)?, - body, - )) +impl EnvelopeHeader { + pub fn get_comms_public_key(&self) -> Option { + CommsPublicKey::from_bytes(&self.public_key).ok() } +} - /// Modify and sign a forwarded MessageEnvelope - pub fn forward_construct( - node_identity: &NodeIdentity, - message_envelope: MessageEnvelope, - ) -> Result - { - let mut message_envelope_header = message_envelope.deserialize_header()?; - message_envelope_header.peer_source = node_identity.identity.public_key.clone(); - message_envelope_header.peer_signature = peer_signature( - node_identity, - message_envelope_header.origin_signature.clone(), - message_envelope.body_frame().clone(), - )?; +impl TryInto for EnvelopeHeader { + type Error = MessageError; - Ok(Self::new( - vec![WIRE_PROTOCOL_VERSION], - message_envelope_header - .to_binary() - .map_err(MessageError::MessageFormatError)?, - message_envelope.body_frame().clone(), - )) + fn try_into(self) -> Result { + Ok(MessageEnvelopeHeader { + public_key: self + .get_comms_public_key() + .ok_or_else(|| MessageError::InvalidHeaderPublicKey)?, + signature: self.signature.into(), + flags: MessageFlags::from_bits_truncate(self.flags), + }) } +} - /// Returns the frame that is expected to be version frame - pub fn version_frame(&self) -> &Frame { - &self.frames[0] - } +/// Wraps a number of `prost::Message`s in a EnvelopeBody +#[macro_export] +macro_rules! wrap_in_envelope_body { + ($($e:expr),+) => {{ + use $crate::message::MessageExt; + let mut envelope_body = $crate::message::EnvelopeBody::new(); + let mut error = None; + $( + match $e.to_encoded_bytes() { + Ok(bytes) => envelope_body.push_part(bytes), + Err(err) => { + if error.is_none() { + error = Some(err); + } + } + } + )* - /// Returns the frame that is expected to be header frame - pub fn header_frame(&self) -> &Frame { - &self.frames[1] - } + match error { + Some(err) => Err(err), + None => Ok(envelope_body), + } + }} +} - /// Returns the [MessageEnvelopeHeader] deserialized from the header frame - pub fn deserialize_header(&self) -> Result { - MessageEnvelopeHeader::from_binary(self.header_frame()).map_err(Into::into) +impl EnvelopeBody { + pub fn new() -> Self { + Self { + parts: Default::default(), + } } - /// Returns the frame that is expected to be body frame - pub fn body_frame(&self) -> &Frame { - &self.frames[2] + pub fn len(&self) -> usize { + self.parts.len() } - /// Returns the frame that is expected to be body frame - pub fn decrypted_body_frame( - &self, - dest_secret_key: &PK::K, - source_public_key: &PK, - ) -> Result - where - PK: PublicKey + DiffieHellmanSharedSecret, - { - let ecdh_shared_secret = PK::shared_secret(dest_secret_key, source_public_key).to_vec(); - let decrypted_frame: Frame = CommsCipher::open_with_integral_nonce(self.body_frame(), &ecdh_shared_secret) - .map_err(MessageError::CipherError)?; - Ok(decrypted_frame) + pub fn is_empty(&self) -> bool { + self.parts.is_empty() } - /// Returns the Message deserialized from the body frame - pub fn deserialize_body(&self) -> Result { - Message::from_binary(self.body_frame()).map_err(Into::into) + /// Removes and returns the part at the given index. None + /// is returned if the index is out of bounds + pub fn take_part(&mut self, index: usize) -> Option> { + Some(index) + .filter(|i| self.parts.len() > *i) + .map(|i| self.parts.remove(i)) } - /// Returns the decrypted and deserialized Message from the body frame - pub fn deserialize_encrypted_body( - &self, - dest_secret_key: &PK::K, - source_public_key: &PK, - ) -> Result - where - PK: PublicKey + DiffieHellmanSharedSecret, - { - Message::from_binary(&self.decrypted_body_frame(dest_secret_key, source_public_key)?).map_err(Into::into) + pub fn push_part(&mut self, part: Vec) { + self.parts.push(part) } - /// This struct is consumed and the contained FrameSet is returned. - pub fn into_frame_set(self) -> FrameSet { - self.frames + pub fn into_inner(self) -> Vec> { + self.parts } -} -impl TryFrom for MessageEnvelope { - type Error = MessageError; - - /// Returns a MessageEnvelope from a FrameSet - fn try_from(frames: FrameSet) -> Result { - if frames.len() != FRAMES_PER_MESSAGE { - return Err(MessageError::MalformedMultipart); + /// Decodes a part of the message body and returns the result. If the part index is out of range Ok(None) is + /// returned + pub fn decode_part(&self, index: usize) -> Result, MessageError> + where T: prost::Message + Default { + match self.parts.get(index) { + Some(part) => T::decode(part.as_slice()).map(Some).map_err(Into::into), + None => Ok(None), } - - Ok(MessageEnvelope { frames }) } } -/// Encrypt the message_envelope_body with the generated shared secret -fn encrypt_envelope_body( - source_secret_key: &PK::K, - dest_public_key: &PK, - message_body: &Frame, -) -> Result -where - PK: PublicKey + DiffieHellmanSharedSecret, -{ - let ecdh_shared_secret = PK::shared_secret(source_secret_key, dest_public_key).to_vec(); - CommsCipher::seal_with_integral_nonce(message_body, &ecdh_shared_secret).map_err(MessageError::CipherError) -} - #[cfg(test)] mod test { use super::*; - use crate::message::{MessageEnvelopeHeader, MessageFlags, NodeDestination}; - use rand; - use std::{convert::TryInto, sync::Arc}; - use tari_crypto::{keys::PublicKey, ristretto::RistrettoPublicKey}; - use tari_utilities::hex::to_hex; + use crate::message::MessageFlags; + use rand::rngs::OsRng; + use tari_crypto::keys::PublicKey; #[test] - fn try_from_valid() { - let example = vec![vec![1u8], vec![2u8], vec![3u8]]; - - let raw_message: Result = example.try_into(); - - assert!(raw_message.is_ok()); - let envelope = raw_message.unwrap(); - assert_eq!(envelope.version_frame(), &[1u8]); - assert_eq!(envelope.header_frame(), &[2u8]); - assert_eq!(envelope.body_frame(), &[3u8]); + fn construct_signed() { + let (sk, pk) = CommsPublicKey::random_keypair(&mut OsRng); + let envelope = Envelope::construct_signed(&sk, &pk, Bytes::new(), MessageFlags::all()).unwrap(); + assert_eq!(envelope.get_public_key().unwrap(), pk); + assert!(envelope.verify_signature().unwrap()); } #[test] - fn try_from_invalid() { - let example = vec![vec![1u8], vec![2u8]]; - - let raw_message: Result = example.try_into(); - - assert!(raw_message.is_err()); - let error = raw_message.err().unwrap(); - match error { - MessageError::MalformedMultipart => {}, - _ => panic!("Unexpected MessageError {:?}", error), - } - } - - #[test] - fn header() { - let (_sk, pk) = RistrettoPublicKey::random_keypair(&mut rand::OsRng::new().unwrap()); - let header = MessageEnvelopeHeader { - version: 0, - origin_source: pk.clone(), - peer_source: pk, - dest: NodeDestination::Unknown, - origin_signature: vec![0], - peer_signature: vec![0], - flags: MessageFlags::ENCRYPTED, + fn header_try_into() { + let header = EnvelopeHeader { + public_key: CommsPublicKey::default().to_vec(), + flags: MessageFlags::all().bits(), + signature: vec![1, 2, 3], }; - let envelope = MessageEnvelope::new(vec![0u8], header.to_binary().unwrap(), vec![0u8]); - - assert_eq!(header, envelope.deserialize_header().unwrap()); - } - - fn make_test_message_frame() -> Frame { - let message_header = "Test Message Header".as_bytes().to_vec(); - let message_body = "Test Message Body".as_bytes().to_vec(); - let message_envelope_body = Message::from_message_format(message_header, message_body).unwrap(); - message_envelope_body.to_binary().unwrap() + let msg_header: MessageEnvelopeHeader = header.try_into().unwrap(); + assert_eq!(msg_header.public_key, CommsPublicKey::default()); + assert_eq!(msg_header.flags, MessageFlags::all()); + assert_eq!(msg_header.signature, vec![1, 2, 3]); } #[test] - fn construct() { - let node_identity = Arc::new(NodeIdentity::random_for_test(None)); - let dest_public_key = &node_identity.identity.public_key; - - let message_envelope_body_frame = make_test_message_frame(); - - let envelope = MessageEnvelope::construct( - &node_identity, - dest_public_key.clone(), - NodeDestination::Unknown, - message_envelope_body_frame.clone(), - MessageFlags::NONE, - ) - .unwrap(); - assert_eq!("00", to_hex(envelope.version_frame())); - let header = MessageEnvelopeHeader::from_binary(envelope.header_frame()).unwrap(); - assert_eq!(dest_public_key, &header.origin_source); - assert_eq!(MessageFlags::NONE, header.flags); - assert_eq!(NodeDestination::Unknown, header.dest); - assert!(!header.origin_signature.is_empty()); - assert_eq!(&message_envelope_body_frame, envelope.body_frame()); - } - - #[test] - fn forward_construct() { - let origin_node_identity = Arc::new(NodeIdentity::random_for_test(None)); - let peer_node_identity = Arc::new(NodeIdentity::random_for_test(None)); - let dest_node_identity = Arc::new(NodeIdentity::random_for_test(None)); - - // Original MessageEnvelope - let message_envelope_body_frame = make_test_message_frame(); - let origin_envelope = MessageEnvelope::construct( - &origin_node_identity, - dest_node_identity.identity.public_key.clone(), - NodeDestination::Unknown, - message_envelope_body_frame.clone(), - MessageFlags::ENCRYPTED, - ) - .unwrap(); - - // Forwarded MessageEnvelope - let peer_envelope = MessageEnvelope::forward_construct(&peer_node_identity, origin_envelope).unwrap(); - let peer_header = MessageEnvelopeHeader::from_binary(peer_envelope.header_frame()).unwrap(); - - assert_eq!(peer_header.origin_source, origin_node_identity.identity.public_key); - assert_eq!(peer_header.peer_source, peer_node_identity.identity.public_key); - assert_eq!( - peer_envelope - .decrypted_body_frame( - &dest_node_identity.secret_key, - &origin_node_identity.identity.public_key - ) - .unwrap(), - message_envelope_body_frame - ); - assert!(peer_header - .verify_signatures(peer_envelope.body_frame().clone()) - .unwrap()); - } - - #[test] - fn construct_encrypted() { - let node_identity = Arc::new(NodeIdentity::random_for_test(None)); - let dest_public_key = &node_identity.identity.public_key; - - let message_envelope_body_frame = make_test_message_frame(); - let envelope = MessageEnvelope::construct( - &node_identity, - dest_public_key.clone(), - NodeDestination::Unknown, - message_envelope_body_frame.clone(), - MessageFlags::ENCRYPTED, - ) - .unwrap(); - - assert_ne!(&message_envelope_body_frame, envelope.body_frame()); - } - - #[test] - fn envelope_decrypt_message_body() { - let node_identity = Arc::new(NodeIdentity::random_for_test(None)); - let dest_secret_key = node_identity.secret_key.clone(); - let dest_public_key = &node_identity.identity.public_key; - - let message_envelope_body_frame = make_test_message_frame(); - let envelope = MessageEnvelope::construct( - &node_identity, - dest_public_key.clone(), - NodeDestination::Unknown, - message_envelope_body_frame.clone(), - MessageFlags::ENCRYPTED, - ) - .unwrap(); - - assert_eq!( - envelope - .deserialize_encrypted_body(&dest_secret_key, &node_identity.identity.public_key) - .unwrap(), - Message::from_binary(&message_envelope_body_frame).unwrap() - ); - } - - #[test] - fn message_header_verify_signature() { - let node_identity = Arc::new(NodeIdentity::random_for_test(None)); - let dest_public_key = &node_identity.identity.public_key; - - let message_envelope_body_frame = make_test_message_frame(); - let envelope = MessageEnvelope::construct( - &node_identity, - dest_public_key.clone(), - NodeDestination::Unknown, - message_envelope_body_frame.clone(), - MessageFlags::NONE, - ) - .unwrap(); - - let header = envelope.deserialize_header().unwrap(); - let mut body = envelope.body_frame().clone(); - assert!(header.verify_signatures(body.clone()).unwrap()); - - body.push(0); - assert!(!header.verify_signatures(body.clone()).unwrap()); + fn is_valid() { + let (sk, pk) = CommsPublicKey::random_keypair(&mut OsRng); + let mut envelope = Envelope::construct_signed(&sk, &pk, Bytes::new(), MessageFlags::all()).unwrap(); + assert_eq!(envelope.is_valid(), true); + envelope.header = None; + assert_eq!(envelope.is_valid(), false); } } diff --git a/comms/src/message/error.rs b/comms/src/message/error.rs index b130820f20..76b23f2761 100644 --- a/comms/src/message/error.rs +++ b/comms/src/message/error.rs @@ -22,13 +22,17 @@ use crate::peer_manager::node_id::NodeIdError; use derive_error::Error; -use tari_crypto::signatures::SchnorrSignatureError; -use tari_utilities::{ciphers::cipher::CipherError, message_format::MessageFormatError}; +use prost::{DecodeError, EncodeError}; +use tari_crypto::{ + signatures::SchnorrSignatureError, + tari_utilities::{ciphers::cipher::CipherError, message_format::MessageFormatError}, +}; +// TODO: only used by control service, so belongs in that module #[derive(Error, Debug)] pub enum MessageError { - /// Multipart message is malformed - MalformedMultipart, + /// Multipart message contained an invalid number of frames + InvalidMultipartMessageLength, /// Failed to serialize message SerializeFailed, /// Failed to deserialize message @@ -44,4 +48,13 @@ pub enum MessageError { NodeIdError(NodeIdError), /// Problem initializing the RNG RngError, + + /// The header contained an invalid public key + InvalidHeaderPublicKey, + /// Failed to decode protobuf message + DecodeError(DecodeError), + /// Failed to encode protobuf message + EncodeError(EncodeError), + /// Failed to decode message part of envelope body + EnvelopeBodyDecodeFailed, } diff --git a/comms/tests/support/helpers/asserts.rs b/comms/src/message/inbound.rs similarity index 67% rename from comms/tests/support/helpers/asserts.rs rename to comms/src/message/inbound.rs index ffbd923512..59216fe62c 100644 --- a/comms/tests/support/helpers/asserts.rs +++ b/comms/src/message/inbound.rs @@ -20,30 +20,31 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::{fmt::Debug, thread, time::Duration}; +use super::{MessageEnvelopeHeader, MessageTag}; +use crate::peer_manager::Peer; +use bytes::Bytes; +use std::sync::Arc; -pub fn assert_change(func: F, to: T, poll_count: usize) -where - F: Fn() -> T, - T: Eq + Debug, -{ - let mut i = 0; - loop { - let last_val = func(); - if last_val == to { - break; - } +/// Authenticated inbound message +#[derive(Clone, Debug)] +pub struct InboundMessage { + pub tag: MessageTag, + /// The deserialized message envelope header + pub envelope_header: MessageEnvelopeHeader, + /// The connected peer which sent this message + pub source_peer: Arc, + /// The raw message envelope + pub body: Bytes, +} - i += 1; - if i >= poll_count { - panic!( - "Value did not change to {:?} within {}ms (last value: {:?})", - to, - poll_count * 100, - last_val, - ); +impl InboundMessage { + /// Construct a new InboundMessage + pub fn new(source_peer: Arc, envelope_header: MessageEnvelopeHeader, body: Bytes) -> Self { + Self { + tag: MessageTag::new(), + source_peer, + envelope_header, + body, } - - thread::sleep(Duration::from_millis(100)); } } diff --git a/comms/src/message/message.rs b/comms/src/message/message.rs deleted file mode 100644 index 6dc2ab0a2a..0000000000 --- a/comms/src/message/message.rs +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::{ - message::{Frame, MessageError}, - types::CommsRng, -}; -use rand::RngCore; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use std::convert::TryFrom; -use tari_utilities::message_format::MessageFormat; - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct MessageHeader { - pub message_type: MType, - pub nonce: u64, -} - -impl MessageHeader { - /// Create a new MessageHeader with a random nonce - pub fn new(message_type: MType) -> Result { - Ok(Self { - message_type, - nonce: CommsRng::new().map_err(|_| MessageError::RngError)?.next_u64(), - }) - } -} - -/// Represents a Message as described in [RFC-0172](https://rfc.tari.com/RFC-0172_PeerToPeerMessagingProtocol.html#messaging-structure). -/// This message has been decrypted but the contents are still serialized -/// as described in [RFC-0171](https://rfc.tari.com/RFC-0171_MessageSerialisation.html) -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] -pub struct Message { - pub header: Frame, - pub body: Frame, -} - -impl Message { - /// Create a new Message from two MessageFormat types - pub fn from_message_format(header: H, msg: B) -> Result { - let header_frame = header.to_binary()?; - let body_frame = msg.to_binary()?; - Ok(Self { - header: header_frame, - body: body_frame, - }) - } - - /// Deserialize and return the header of the message - pub fn deserialize_header(&self) -> Result, MessageError> - where - MessageHeader: MessageFormat, - MType: DeserializeOwned, - { - MessageHeader::::from_binary(&self.header).map_err(Into::into) - } - - pub fn deserialize_message(&self) -> Result - where T: MessageFormat { - T::from_binary(&self.body).map_err(Into::into) - } -} - -impl TryFrom<(MType, T)> for Message -where - MessageHeader: MessageFormat, - T: MessageFormat, -{ - type Error = MessageError; - - fn try_from((message_type, msg): (MType, T)) -> Result { - Ok(Self::from_message_format(MessageHeader::new(message_type)?, msg)?) - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[derive(Serialize, Deserialize, Eq, PartialEq, Debug, Clone)] - struct TestHeader { - a: u32, - b: u64, - } - - #[derive(Serialize, Deserialize, Eq, PartialEq, Debug, Clone)] - struct TestMsg { - a: u32, - } - - #[test] - fn from_message_format() { - let header = TestHeader { a: 1, b: 2 }; - let body = TestMsg { a: 2 }; - - let msg = Message::from_message_format(header.clone(), body.clone()).unwrap(); - - let header2 = msg.deserialize_header::().unwrap(); - let body2 = msg.deserialize_message::().unwrap(); - - assert_eq!(header.a, header2.message_type); - assert_eq!(header.b, header2.nonce); - assert_eq!(body, body2); - } -} diff --git a/comms/src/message/message_context.rs b/comms/src/message/message_context.rs deleted file mode 100644 index b1eaeddde6..0000000000 --- a/comms/src/message/message_context.rs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{ - dispatcher::DispatchableKey, - inbound_message_service::inbound_message_publisher::InboundMessagePublisher, - message::{InboundMessage, MessageEnvelope}, - outbound_message_service::outbound_message_service::OutboundMessageService, - peer_manager::{peer_manager::PeerManager, NodeIdentity, Peer}, -}; -use serde::{de::DeserializeOwned, Serialize}; -use std::{ - fmt::Debug, - sync::{Arc, RwLock}, -}; - -#[derive(Clone)] -pub struct MessageContext -where MType: Send + Sync + Debug -{ - pub forwardable: bool, - pub message_envelope: MessageEnvelope, - pub peer: Peer, - pub outbound_message_service: Arc, - pub peer_manager: Arc, - pub inbound_message_publisher: Arc>>, - pub node_identity: Arc, -} - -impl MessageContext -where - MType: DispatchableKey, - MType: Serialize + DeserializeOwned, - MType: Debug, -{ - /// Construct a new MessageContext that consist of the peer connection information and the received message header - /// and body - pub fn new( - node_identity: Arc, - peer: Peer, - forwardable: bool, - message_envelope: MessageEnvelope, - outbound_message_service: Arc, - peer_manager: Arc, - inbound_message_publisher: Arc>>, - ) -> Self - { - MessageContext { - forwardable, - message_envelope, - peer, - node_identity, - outbound_message_service, - peer_manager, - inbound_message_publisher, - } - } -} diff --git a/comms/src/message/message_data.rs b/comms/src/message/message_data.rs deleted file mode 100644 index e8ed13adaa..0000000000 --- a/comms/src/message/message_data.rs +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::{ - message::{FrameSet, MessageEnvelope, MessageError}, - peer_manager::NodeId, -}; -use serde_derive::{Deserialize, Serialize}; -use std::convert::{TryFrom, TryInto}; -use tari_utilities::message_format::MessageFormat; - -/// Messages submitted to the inbound message pool are of type MessageData. This struct contains the received message -/// envelope from a peer, its node identity and the connection id associated with the received message. -#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] -pub struct MessageData { - pub source_node_id: NodeId, - pub forwardable: bool, - pub message_envelope: MessageEnvelope, -} - -impl MessageData { - /// Construct a new MessageData that consist of the peer connection information and the received message envelope - /// header and body - pub fn new(source_node_id: NodeId, forwardable: bool, message_envelope: MessageEnvelope) -> MessageData { - MessageData { - source_node_id, - forwardable, - message_envelope, - } - } - - /// Convert the MessageData into a FrameSet - pub fn into_frame_set(self) -> FrameSet { - let mut frame_set = Vec::new(); - frame_set.push(self.source_node_id.as_ref().to_vec()); - frame_set.extend(self.forwardable.to_binary()); - frame_set.extend(self.message_envelope.into_frame_set()); - frame_set - } -} - -impl TryFrom for MessageData { - type Error = MessageError; - - /// Attempt to create a MessageData from a FrameSet - fn try_from(mut frames: FrameSet) -> Result { - if frames.len() < 5 { - return Err(MessageError::MalformedMultipart); - }; - let source_node_id: NodeId = frames.remove(0).try_into().map_err(MessageError::NodeIdError)?; - let forwardable = bool::from_binary(&frames.remove(0))?; - let message_envelope: MessageEnvelope = frames.try_into()?; - Ok(MessageData { - message_envelope, - forwardable, - source_node_id, - }) - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::message::Frame; - use tari_crypto::{keys::PublicKey, ristretto::RistrettoPublicKey}; - - #[test] - fn test_try_from_and_into() { - let mut rng = rand::OsRng::new().unwrap(); - let (_, source_node_identity) = RistrettoPublicKey::random_keypair(&mut rng); - let version_frame: Frame = vec![10]; - let header_frame: Frame = vec![0, 1, 2, 3, 4]; - let body_frame: Frame = vec![5, 6, 7, 8, 9]; - let message_envelope = MessageEnvelope::new(version_frame, header_frame, body_frame); - let expected_message_data = - MessageData::new(NodeId::from_key(&source_node_identity).unwrap(), true, message_envelope); - // Convert MessageData to FrameSet - let message_data_buffer = expected_message_data.clone().into_frame_set(); - // Create MessageData from FrameSet - let message_data: MessageData = MessageData::try_from(message_data_buffer).unwrap(); - assert_eq!(expected_message_data, message_data); - } -} diff --git a/comms/src/message/mod.rs b/comms/src/message/mod.rs index e60481baad..d56383b369 100644 --- a/comms/src/message/mod.rs +++ b/comms/src/message/mod.rs @@ -51,8 +51,6 @@ //! //! - [MessageData] //! -//! This message is dispatched by the [InboundMessageBroker] to a [DomainConnector]. -//! //! [Frame]: ./tyoe.Frame.html //! [FrameSet]: ./tyoe.FrameSet.html //! [MessageEnvelope]: ./envelope/struct.MessageEnvelope.html @@ -60,47 +58,43 @@ //! [Message]: ./message/struct.Message.html //! [MessageHeader]: ./message/struct.MessageHeader.html //! [MessageData]: ./message/struct.MessageData.html -//! [InboundMessageBroker]: ../inbound_message_service/inbound_message_broker/struct.InboundMessageBroker.html //! [DomainConnector]: ../domain_connector/struct.DomainConnector.html -use crate::peer_manager::node_id::NodeId; use bitflags::*; use serde::{Deserialize, Serialize}; -mod domain_message_context; +#[macro_use] mod envelope; +pub use envelope::{Envelope, EnvelopeBody, EnvelopeHeader, MessageEnvelopeHeader}; + mod error; -mod message; -mod message_context; -mod message_data; +pub use error::MessageError; + +mod inbound; +pub use inbound::InboundMessage; + +mod outbound; +pub use outbound::OutboundMessage; -pub use self::{ - domain_message_context::*, - envelope::{MessageEnvelope, MessageEnvelopeHeader}, - error::MessageError, - message::{Message, MessageHeader}, - message_context::MessageContext, - message_data::*, -}; +mod tag; +pub use tag::MessageTag; -/// Represents a single message frame. -pub type Frame = Vec; -/// Represents a collection of frames which make up a multipart message. -pub type FrameSet = Vec; +pub trait MessageExt: prost::Message { + /// Encodes a message, allocating the buffer on the heap as necessary + fn to_encoded_bytes(&self) -> Result, MessageError> + where Self: Sized { + let mut buf = Vec::with_capacity(self.encoded_len()); + self.encode(&mut buf)?; + Ok(buf) + } +} +impl MessageExt for T {} bitflags! { /// Used to indicate characteristics of the incoming or outgoing message, such /// as whether the message is encrypted. - #[derive(Deserialize, Serialize)] - pub struct MessageFlags: u8 { + #[derive(Default, Deserialize, Serialize)] + pub struct MessageFlags: u32 { const NONE = 0b0000_0000; const ENCRYPTED = 0b0000_0001; } } - -/// Represents the ways a destination node can be represented. -#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] -pub enum NodeDestination

{ - Unknown, - PublicKey(P), - NodeId(NodeId), -} diff --git a/comms/src/message/outbound.rs b/comms/src/message/outbound.rs new file mode 100644 index 0000000000..0b74e6f864 --- /dev/null +++ b/comms/src/message/outbound.rs @@ -0,0 +1,86 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + message::{MessageFlags, MessageTag}, + peer_manager::NodeId, +}; +use bytes::Bytes; +use std::{ + fmt, + fmt::{Error, Formatter}, +}; + +/// Contains details required to build a message envelope and send a message to a peer. OutboundMessage will not copy +/// the body bytes when cloned and is 'cheap to clone(tm)'. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct OutboundMessage { + pub tag: MessageTag, + pub peer_node_id: NodeId, + pub flags: MessageFlags, + pub body: Bytes, +} + +impl OutboundMessage { + /// Create a new OutboundMessage + pub fn new(peer_node_id: NodeId, flags: MessageFlags, body: Bytes) -> OutboundMessage { + Self::with_tag(MessageTag::new(), peer_node_id, flags, body) + } + + /// Create a new OutboundMessage with the specified MessageTag + pub fn with_tag(tag: MessageTag, peer_node_id: NodeId, flags: MessageFlags, body: Bytes) -> OutboundMessage { + OutboundMessage { + tag, + peer_node_id, + flags, + body, + } + } +} + +impl fmt::Display for OutboundMessage { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { + write!( + f, + "OutboundMessage (tag: {}, {} bytes) for peer '{}'", + self.tag, + self.body.len(), + self.peer_node_id.short_str() + ) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn with_tag() { + static TEST_MSG: Bytes = Bytes::from_static(b"The ghost brigades"); + let node_id = NodeId::new(); + let tag = MessageTag::new(); + let subject = OutboundMessage::with_tag(tag, node_id.clone(), MessageFlags::empty(), TEST_MSG.clone()); + assert_eq!(tag, subject.tag); + assert_eq!(subject.body, TEST_MSG); + assert_eq!(subject.peer_node_id, node_id); + } +} diff --git a/infrastructure/tari_util/src/lib.rs b/comms/src/message/tag.rs similarity index 77% rename from infrastructure/tari_util/src/lib.rs rename to comms/src/message/tag.rs index 8e0f882f20..ea324e94d9 100644 --- a/infrastructure/tari_util/src/lib.rs +++ b/comms/src/message/tag.rs @@ -1,4 +1,4 @@ -// Copyright 2019 The Tari Project +// Copyright 2020, The Tari Project // // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the // following conditions are met: @@ -20,26 +20,21 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#[macro_use] -pub mod protobuf; +use rand::{rngs::OsRng, RngCore}; +use std::fmt; -#[allow(clippy::needless_range_loop)] -pub mod bit; -pub mod byte_array; -pub mod ciphers; -pub mod epoch_time; -pub mod extend_bytes; -pub mod fixed_set; -pub mod hash; -pub mod hex; -#[macro_use] -pub mod locks; -pub mod message_format; -pub mod thread_join; +/// Represents a tag for a message +#[derive(Clone, Debug, Copy, PartialEq, Eq, Hash)] +pub struct MessageTag(u64); -pub use self::extend_bytes::ExtendBytes; +impl MessageTag { + pub fn new() -> Self { + Self(OsRng.next_u64()) + } +} -pub use self::{ - byte_array::{ByteArray, ByteArrayError}, - hash::Hashable, -}; +impl fmt::Display for MessageTag { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "MessageTag({})", self.0) + } +} diff --git a/comms/src/multiplexing/mod.rs b/comms/src/multiplexing/mod.rs new file mode 100644 index 0000000000..ad321f7a41 --- /dev/null +++ b/comms/src/multiplexing/mod.rs @@ -0,0 +1,24 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod yamux; +pub use self::yamux::{Control, IncomingSubstreams, Yamux}; diff --git a/comms/src/multiplexing/yamux.rs b/comms/src/multiplexing/yamux.rs new file mode 100644 index 0000000000..b6b1946042 --- /dev/null +++ b/comms/src/multiplexing/yamux.rs @@ -0,0 +1,330 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::connection_manager::ConnectionDirection; +use futures::{ + channel::mpsc, + future, + future::Either, + io::{AsyncRead, AsyncWrite}, + task::Context, + SinkExt, + Stream, + StreamExt, +}; +use log::*; +use std::{io, pin::Pin, task::Poll}; +use tari_shutdown::{Shutdown, ShutdownSignal}; +use tokio::runtime; +use yamux::Mode; + +type IncomingRx = mpsc::Receiver>; +type IncomingTx = mpsc::Sender>; + +pub type Control = yamux::Control; + +const LOG_TARGET: &str = "comms::multiplexing::yamux"; + +pub struct Yamux { + control: Control, + incoming: IncomingSubstreams, +} + +const MAX_BUFFER_SIZE: u32 = 8 * 1024 * 1024; // 8MB +const RECEIVE_WINDOW: u32 = 4 * 1024 * 1024; // 4MB + +impl Yamux { + /// Upgrade the underlying socket to use yamux + pub async fn upgrade_connection( + executor: runtime::Handle, + socket: TSocket, + direction: ConnectionDirection, + ) -> io::Result + where + TSocket: AsyncRead + AsyncWrite + Send + Unpin + 'static, + { + let mode = match direction { + ConnectionDirection::Inbound => Mode::Server, + ConnectionDirection::Outbound => Mode::Client, + }; + + let mut config = yamux::Config::default(); + // Use OnRead mode instead of OnReceive mode to provide back pressure to the sending side. + // Caveat: the OnRead mode has the risk of deadlock, where both sides send data larger than + // receive window and don't read before finishing writes. + // This should never happen as the window size should be large enough for all protocol messages. + config.set_window_update_mode(yamux::WindowUpdateMode::OnRead); + // Because OnRead mode increases the RTT of window update, bigger buffer size and receive + // window size perform better. + config.set_max_buffer_size(MAX_BUFFER_SIZE as usize); + config.set_receive_window(RECEIVE_WINDOW); + + let connection = yamux::Connection::new(socket, config, mode); + let control = connection.control(); + + let incoming = Self::spawn_incoming_stream_worker(executor, connection); + + Ok(Self { control, incoming }) + } + + // yamux@0.4.0 requires the incoming substream stream be polled in order to make progress on requests from it's + // Control api. Here we spawn off a worker which will do this job + fn spawn_incoming_stream_worker( + executor: runtime::Handle, + connection: yamux::Connection, + ) -> IncomingSubstreams + where + TSocket: AsyncRead + AsyncWrite + Unpin + Send + 'static, + { + let shutdown = Shutdown::new(); + let (incoming_tx, incoming_rx) = mpsc::channel(10); + let stream = yamux::into_stream(connection).boxed(); + let incoming = IncomingWorker::new(stream, incoming_tx, shutdown.to_signal()); + executor.spawn(incoming.run()); + IncomingSubstreams::new(incoming_rx, shutdown) + } + + /// Get the yamux control struct + pub fn get_yamux_control(&self) -> yamux::Control { + self.control.clone() + } + + /// Returns a mutable reference to a `Stream` that emits substreams initiated by the remote + pub fn incoming_mut(&mut self) -> &mut IncomingSubstreams { + &mut self.incoming + } + + /// Consumes this object and returns a `Stream` that emits substreams initiated by the remote + pub fn incoming(self) -> IncomingSubstreams { + self.incoming + } +} + +pub struct IncomingSubstreams { + inner: IncomingRx, + shutdown: Shutdown, +} + +impl IncomingSubstreams { + pub fn new(inner: IncomingRx, shutdown: Shutdown) -> Self { + Self { inner, shutdown } + } +} + +impl Stream for IncomingSubstreams { + type Item = Result; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.inner).poll_next(cx) + } +} + +impl Drop for IncomingSubstreams { + fn drop(&mut self) { + let _ = self.shutdown.trigger(); + } +} + +struct IncomingWorker { + inner: S, + sender: mpsc::Sender>, + shutdown_signal: Option, +} + +impl IncomingWorker +where S: Stream> + Unpin +{ + pub fn new(stream: S, sender: IncomingTx, shutdown_signal: ShutdownSignal) -> Self { + Self { + inner: stream, + sender, + shutdown_signal: Some(shutdown_signal), + } + } + + pub async fn run(mut self) { + let mut signal = self.shutdown_signal.take(); + loop { + let either = future::select(self.inner.next(), signal.take().expect("cannot fail")).await; + match either { + Either::Left((Some(substream_result), sig)) => { + signal = Some(sig); + let _ = self.sender.send(substream_result).await; + }, + Either::Left((None, _)) => { + trace!( + target: LOG_TARGET, + "Incoming peer substream task is shutting down because the stream ended" + ); + break; + }, + Either::Right((_, _)) => { + trace!( + target: LOG_TARGET, + "Incoming peer substream task is shutting down because the shutdown signal was received" + ); + break; + }, + } + } + } +} + +#[cfg(test)] +mod test { + use crate::{connection_manager::ConnectionDirection, memsocket::MemorySocket, multiplexing::yamux::Yamux}; + use futures::{ + future, + io::{AsyncReadExt, AsyncWriteExt}, + StreamExt, + }; + use std::io; + use tokio::runtime::Handle; + + #[tokio_macros::test_basic] + async fn open_substream() -> io::Result<()> { + let (dialer, listener) = MemorySocket::new_pair(); + let msg = b"The Way of Kings"; + let rt_handle = Handle::current(); + + let dialer = Yamux::upgrade_connection(rt_handle.clone(), dialer, ConnectionDirection::Outbound) + .await + .unwrap(); + let mut dialer_control = dialer.get_yamux_control(); + + rt_handle.spawn(async move { + let mut substream = dialer_control.open_stream().await.unwrap(); + + substream.write_all(msg).await.unwrap(); + substream.flush().await.unwrap(); + substream.close().await.unwrap(); + }); + + let mut listener = Yamux::upgrade_connection(rt_handle.clone(), listener, ConnectionDirection::Inbound) + .await? + .incoming(); + let mut substream = listener + .next() + .await + .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "no substream"))? + .map_err(|err| io::Error::new(io::ErrorKind::Other, err))?; + + let mut buf = Vec::new(); + let _ = future::select(substream.read_to_end(&mut buf), listener.next()).await; + assert_eq!(buf, msg); + + Ok(()) + } + + #[tokio_macros::test_basic] + async fn close() -> io::Result<()> { + let (dialer, listener) = MemorySocket::new_pair(); + let msg = b"Words of Radiance"; + let rt_handle = Handle::current(); + + let dialer = Yamux::upgrade_connection(rt_handle.clone(), dialer, ConnectionDirection::Outbound).await?; + let mut dialer_control = dialer.get_yamux_control(); + + rt_handle.spawn(async move { + let mut substream = dialer_control.open_stream().await.unwrap(); + + substream.write_all(msg).await.unwrap(); + substream.flush().await.unwrap(); + + let mut buf = Vec::new(); + substream.read_to_end(&mut buf).await.unwrap(); + assert_eq!(buf, b""); + }); + + let mut incoming = Yamux::upgrade_connection(rt_handle.clone(), listener, ConnectionDirection::Inbound) + .await? + .incoming(); + let mut substream = incoming.next().await.unwrap().unwrap(); + rt_handle.spawn(async move { + incoming.next().await; + }); + + let mut buf = vec![0; msg.len()]; + substream.read_exact(&mut buf).await?; + assert_eq!(buf, msg); + + // Close the substream and then try to write to it + substream.close().await?; + + let result = substream.write_all(b"ignored message").await; + match result { + Ok(()) => panic!("Write should have failed"), + Err(e) => assert_eq!(e.kind(), io::ErrorKind::WriteZero), + } + + Ok(()) + } + + #[tokio_macros::test_basic] + async fn send_big_message() -> io::Result<()> { + let rt_handle = Handle::current(); + #[allow(non_upper_case_globals)] + static MiB: usize = 1 << 20; + static MSG_LEN: usize = 16 * MiB; + + let (dialer, listener) = MemorySocket::new_pair(); + + let dialer = Yamux::upgrade_connection(rt_handle.clone(), dialer, ConnectionDirection::Outbound).await?; + let mut dialer_control = dialer.get_yamux_control(); + // The incoming stream must be polled for the control to work + rt_handle.spawn(async move { + dialer.incoming().next().await; + }); + + rt_handle.spawn(async move { + let mut substream = dialer_control.open_stream().await.unwrap(); + + let msg = vec![0x55u8; MSG_LEN]; + substream.write_all(msg.as_slice()).await.unwrap(); + + let mut buf = vec![0u8; MSG_LEN]; + substream.read_exact(&mut buf).await.unwrap(); + substream.close().await.unwrap(); + + assert_eq!(buf.len(), MSG_LEN); + assert_eq!(buf, vec![0xAAu8; MSG_LEN]); + }); + + let mut incoming = Yamux::upgrade_connection(rt_handle.clone(), listener, ConnectionDirection::Inbound) + .await? + .incoming(); + let mut substream = incoming.next().await.unwrap().unwrap(); + rt_handle.spawn(async move { + incoming.next().await; + }); + + let mut buf = vec![0u8; MSG_LEN]; + substream.read_exact(&mut buf).await?; + assert_eq!(buf, vec![0x55u8; MSG_LEN]); + + let msg = vec![0xAAu8; MSG_LEN]; + substream.write_all(msg.as_slice()).await?; + substream.close().await?; + + Ok(()) + } +} diff --git a/comms/tests/mod.rs b/comms/src/net_address/mod.rs similarity index 90% rename from comms/tests/mod.rs rename to comms/src/net_address/mod.rs index ec6cac98f4..64407840ac 100644 --- a/comms/tests/mod.rs +++ b/comms/src/net_address/mod.rs @@ -20,11 +20,8 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#[macro_use] -extern crate lazy_static; +mod multiaddr_with_stats; +pub use multiaddr_with_stats::MutliaddrWithStats; -mod connection; -mod connection_manager; -mod control_service; -mod outbound_message_service; -mod support; +mod mutliaddresses_with_stats; +pub use mutliaddresses_with_stats::MultiaddressesWithStats; diff --git a/comms/src/connection/net_address/net_address_with_stats.rs b/comms/src/net_address/multiaddr_with_stats.rs similarity index 77% rename from comms/src/connection/net_address/net_address_with_stats.rs rename to comms/src/net_address/multiaddr_with_stats.rs index c44f0355a7..f1fd3dd740 100644 --- a/comms/src/connection/net_address/net_address_with_stats.rs +++ b/comms/src/net_address/multiaddr_with_stats.rs @@ -1,5 +1,5 @@ -use crate::connection::NetAddress; -use chrono::prelude::*; +use chrono::{DateTime, Utc}; +use multiaddr::Multiaddr; use serde::{Deserialize, Serialize}; use std::{ cmp::{Ord, Ordering}, @@ -10,8 +10,8 @@ use std::{ const MAX_LATENCY_SAMPLE_COUNT: u32 = 100; #[derive(Debug, Eq, Clone, Deserialize, Serialize)] -pub struct NetAddressWithStats { - pub net_address: NetAddress, +pub struct MutliaddrWithStats { + pub address: Multiaddr, pub last_seen: Option>, pub connection_attempts: u32, pub rejected_message_count: u32, @@ -19,11 +19,11 @@ pub struct NetAddressWithStats { latency_sample_count: u32, } -impl NetAddressWithStats { +impl MutliaddrWithStats { /// Constructs a new net address with zero stats - pub fn new(net_address: NetAddress) -> NetAddressWithStats { - NetAddressWithStats { - net_address, + pub fn new(address: Multiaddr) -> Self { + Self { + address, last_seen: None, connection_attempts: 0, rejected_message_count: 0, @@ -34,16 +34,16 @@ impl NetAddressWithStats { /// Constructs a new net address with usage stats pub fn new_with_stats( - net_address: NetAddress, + address: Multiaddr, last_seen: Option>, connection_attempts: u32, rejected_message_count: u32, avg_latency: Duration, latency_sample_count: u32, - ) -> NetAddressWithStats + ) -> Self { - NetAddressWithStats { - net_address, + Self { + address, last_seen, connection_attempts, rejected_message_count, @@ -97,17 +97,17 @@ impl NetAddressWithStats { self.connection_attempts += 1; } - /// Get as a NetAddress - pub fn as_net_address(&self) -> NetAddress { - self.clone().net_address + /// Get as a Multiaddr + pub fn as_net_address(&self) -> Multiaddr { + self.clone().address } } -impl From for NetAddressWithStats { +impl From for MutliaddrWithStats { /// Constructs a new net address with usage stats from a net address - fn from(net_address: NetAddress) -> Self { - NetAddressWithStats { - net_address, + fn from(net_address: Multiaddr) -> Self { + Self { + address: net_address, last_seen: None, connection_attempts: 0, rejected_message_count: 0, @@ -120,22 +120,28 @@ impl From for NetAddressWithStats { // Reliability ordering of net addresses: prioritize net addresses according to previous successful connections, // connection attempts, latency and last seen A lower ordering has a higher priority and a higher ordering has a lower // priority, this ordering switch allows searching for, and updating of net addresses to be performed more efficiently -impl Ord for NetAddressWithStats { - fn cmp(&self, other: &NetAddressWithStats) -> Ordering { +impl Ord for MutliaddrWithStats { + fn cmp(&self, other: &MutliaddrWithStats) -> Ordering { if self.last_seen.is_some() && other.last_seen.is_none() { return Ordering::Less; - } else if self.last_seen.is_none() && other.last_seen.is_some() { + } + + if self.last_seen.is_none() && other.last_seen.is_some() { return Ordering::Greater; } if self.connection_attempts < other.connection_attempts { return Ordering::Less; - } else if self.connection_attempts > other.connection_attempts { + } + + if self.connection_attempts > other.connection_attempts { return Ordering::Greater; } if self.latency_sample_count > 0 && other.latency_sample_count > 0 { if self.avg_latency < other.avg_latency { return Ordering::Less; - } else if self.avg_latency > other.avg_latency { + } + + if self.avg_latency > other.avg_latency { return Ordering::Greater; } } @@ -144,7 +150,9 @@ impl Ord for NetAddressWithStats { let other_last_seen = other.last_seen.unwrap(); if self_last_seen > other_last_seen { return Ordering::Less; - } else if self_last_seen < other_last_seen { + } + + if self_last_seen < other_last_seen { return Ordering::Greater; } } @@ -152,33 +160,33 @@ impl Ord for NetAddressWithStats { } } -impl PartialOrd for NetAddressWithStats { - fn partial_cmp(&self, other: &NetAddressWithStats) -> Option { +impl PartialOrd for MutliaddrWithStats { + fn partial_cmp(&self, other: &MutliaddrWithStats) -> Option { Some(self.cmp(other)) } } -impl PartialEq for NetAddressWithStats { - fn eq(&self, other: &NetAddressWithStats) -> bool { - self.net_address == other.net_address +impl PartialEq for MutliaddrWithStats { + fn eq(&self, other: &MutliaddrWithStats) -> bool { + self.address == other.address } } -impl fmt::Display for NetAddressWithStats { +impl fmt::Display for MutliaddrWithStats { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.net_address) + write!(f, "{}", self.address) } } #[cfg(test)] mod test { - use crate::connection::{net_address::net_address_with_stats::NetAddressWithStats, NetAddress}; + use super::*; use std::{thread, time::Duration}; #[test] fn test_update_latency() { - let net_address = "123.0.0.123:8000".parse::().unwrap(); - let mut net_address_with_stats = NetAddressWithStats::from(net_address); + let net_address = "/ip4/123.0.0.123/tcp/8000".parse::().unwrap(); + let mut net_address_with_stats = MutliaddrWithStats::from(net_address); let latency_measurement1 = Duration::from_millis(100); let latency_measurement2 = Duration::from_millis(200); let latency_measurement3 = Duration::from_millis(60); @@ -195,8 +203,8 @@ mod test { #[test] fn test_message_received_and_rejected() { - let net_address = "123.0.0.123:8000".parse::().unwrap(); - let mut net_address_with_stats = NetAddressWithStats::from(net_address); + let net_address = "/ip4/123.0.0.123/tcp/8000".parse::().unwrap(); + let mut net_address_with_stats = MutliaddrWithStats::from(net_address); assert!(net_address_with_stats.last_seen.is_none()); net_address_with_stats.mark_message_received(); assert!(net_address_with_stats.last_seen.is_some()); @@ -204,13 +212,13 @@ mod test { net_address_with_stats.mark_message_rejected(); net_address_with_stats.mark_message_rejected(); assert_eq!(net_address_with_stats.rejected_message_count, 2); - assert!(last_seen < net_address_with_stats.last_seen.unwrap()); + assert!(last_seen <= net_address_with_stats.last_seen.unwrap()); } #[test] fn test_successful_and_failed_connection_attempts() { - let net_address = "123.0.0.123:8000".parse::().unwrap(); - let mut net_address_with_stats = NetAddressWithStats::from(net_address); + let net_address = "/ip4/123.0.0.123/tcp/8000".parse::().unwrap(); + let mut net_address_with_stats = MutliaddrWithStats::from(net_address); net_address_with_stats.mark_failed_connection_attempt(); net_address_with_stats.mark_failed_connection_attempt(); assert!(net_address_with_stats.last_seen.is_none()); @@ -222,8 +230,8 @@ mod test { #[test] fn test_reseting_connection_attempts() { - let net_address = "123.0.0.123:8000".parse::().unwrap(); - let mut net_address_with_stats = NetAddressWithStats::from(net_address); + let net_address = "/ip4/123.0.0.123/tcp/8000".parse::().unwrap(); + let mut net_address_with_stats = MutliaddrWithStats::from(net_address); net_address_with_stats.mark_failed_connection_attempt(); net_address_with_stats.mark_failed_connection_attempt(); assert_eq!(net_address_with_stats.connection_attempts, 2); @@ -233,9 +241,9 @@ mod test { #[test] fn test_net_address_reliability_ordering() { - let net_address = "123.0.0.123:8000".parse::().unwrap(); - let mut na1 = NetAddressWithStats::from(net_address.clone()); - let mut na2 = NetAddressWithStats::from(net_address); + let net_address = "/ip4/123.0.0.123/tcp/8000".parse::().unwrap(); + let mut na1 = MutliaddrWithStats::from(net_address.clone()); + let mut na2 = MutliaddrWithStats::from(net_address); thread::sleep(Duration::from_millis(1)); na1.mark_successful_connection_attempt(); assert!(na1 < na2); diff --git a/comms/src/net_address/mutliaddresses_with_stats.rs b/comms/src/net_address/mutliaddresses_with_stats.rs new file mode 100644 index 0000000000..e33faaa22f --- /dev/null +++ b/comms/src/net_address/mutliaddresses_with_stats.rs @@ -0,0 +1,370 @@ +use super::multiaddr_with_stats::MutliaddrWithStats; +use chrono::{DateTime, Utc}; +use multiaddr::Multiaddr; +use serde::{Deserialize, Serialize}; +use std::{ops::Index, time::Duration}; + +/// This struct is used to store a set of different net addresses such as IPv4, IPv6, Tor or I2P for a single peer. +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Default)] +pub struct MultiaddressesWithStats { + pub addresses: Vec, + last_attempted: Option>, +} + +impl MultiaddressesWithStats { + /// Constructs a new list of addresses with usage stats from a list of net addresses + pub fn new(addresses: Vec) -> MultiaddressesWithStats { + MultiaddressesWithStats { + addresses, + last_attempted: None, + } + } + + /// Provides the date and time of the last successful communication with this peer + pub fn last_seen(&self) -> Option> { + let mut latest_valid_datetime: Option> = None; + for curr_address in &self.addresses { + if curr_address.last_seen.is_none() { + continue; + } + match latest_valid_datetime { + Some(latest_datetime) => { + if latest_datetime < curr_address.last_seen.unwrap() { + latest_valid_datetime = curr_address.last_seen; + } + }, + None => latest_valid_datetime = curr_address.last_seen, + } + } + latest_valid_datetime + } + + /// Return the time of last attempted connection to this collection of addresses + pub fn last_attempted(&self) -> Option> { + self.last_attempted + } + + /// Adds a new net address to the peer. This function will not add a duplicate if the address + /// already exists. + pub fn add_net_address(&mut self, net_address: &Multiaddr) { + if !self.addresses.iter().any(|x| x.address == *net_address) { + self.addresses.push(net_address.clone().into()); + self.addresses.sort(); + } + } + + /// Compares the existing set of net_addresses to the provided net_address set and remove missing net_addresses and + /// add new net_addresses without discarding the usage stats of the existing and remaining net_addresses. + pub fn update_net_addresses(&mut self, net_addresses: Vec) { + // Remove missing elements + let mut remove_indices: Vec = Vec::new(); + for index in 0..self.addresses.len() { + if net_addresses + .iter() + .all(|new_net_address| *new_net_address != self.addresses[index].address) + { + remove_indices.push(index); + } + } + for index in remove_indices.iter().rev() { + self.addresses.remove(*index); + } + // Add new elements + for new_net_address in &net_addresses { + if self + .addresses + .iter() + .all(|curr_net_address| curr_net_address.address != *new_net_address) + { + self.add_net_address(new_net_address); + } + } + self.addresses.sort(); + } + + /// Returns an iterator of addresses ordered from 'best' to 'worst' according to heuristics such as failed + /// connections and latency. + pub fn address_iter(&self) -> impl Iterator { + self.addresses.iter().map(|addr| &addr.address) + } + + /// Finds the specified address in the set and allow updating of its variables such as its usage stats + fn find_address_mut(&mut self, address: &Multiaddr) -> Option<&mut MutliaddrWithStats> { + self.addresses.iter_mut().find(|a| &a.address == address) + } + + /// The average connection latency of the provided net address will be updated to include the current measured + /// latency sample. + /// + /// Returns true if the address is contained in this instance, otherwise false + pub fn update_latency(&mut self, address: &Multiaddr, latency_measurement: Duration) -> bool { + match self.find_address_mut(address) { + Some(addr) => { + addr.update_latency(latency_measurement); + self.addresses.sort(); + true + }, + None => false, + } + } + + /// Mark that a message was received from the specified net address + /// + /// Returns true if the address is contained in this instance, otherwise false + pub fn mark_message_received(&mut self, address: &Multiaddr) -> bool { + match self.find_address_mut(address) { + Some(addr) => { + addr.mark_message_received(); + self.addresses.sort(); + true + }, + None => false, + } + } + + /// Mark that a rejected message was received from the specified net address + /// + /// Returns true if the address is contained in this instance, otherwise false + pub fn mark_message_rejected(&mut self, address: &Multiaddr) -> bool { + match self.find_address_mut(address) { + Some(addr) => { + addr.mark_message_rejected(); + self.addresses.sort(); + true + }, + None => false, + } + } + + /// Mark that a successful connection was established with the specified net address + /// + /// Returns true if the address is contained in this instance, otherwise false + pub fn mark_successful_connection_attempt(&mut self, address: &Multiaddr) -> bool { + match self.find_address_mut(address) { + Some(addr) => { + addr.mark_successful_connection_attempt(); + self.last_attempted = Some(Utc::now()); + self.addresses.sort(); + true + }, + None => false, + } + } + + /// Mark that a connection could not be established with the specified net address + /// + /// Returns true if the address is contained in this instance, otherwise false + pub fn mark_failed_connection_attempt(&mut self, address: &Multiaddr) -> bool { + match self.find_address_mut(address) { + Some(addr) => { + addr.mark_failed_connection_attempt(); + self.last_attempted = Some(Utc::now()); + self.addresses.sort(); + true + }, + None => false, + } + } + + /// Reset the connection attempts stat on all of this Peers net addresses to retry connection + /// + /// Returns true if the address is contained in this instance, otherwise false + pub fn reset_connection_attempts(&mut self) { + for a in self.addresses.iter_mut() { + a.reset_connection_attempts(); + } + self.addresses.sort(); + } + + /// Returns the number of addresses + pub fn len(&self) -> usize { + self.addresses.len() + } + + /// Returns if there are addresses or not + pub fn is_empty(&self) -> bool { + self.addresses.is_empty() + } +} + +impl Index for MultiaddressesWithStats { + type Output = MutliaddrWithStats; + + /// Returns the NetAddressWithStats at the given index + fn index(&self, index: usize) -> &Self::Output { + &self.addresses[index] + } +} + +impl From for MultiaddressesWithStats { + /// Constructs a new list of addresses with usage stats from a single net address + fn from(net_address: Multiaddr) -> Self { + MultiaddressesWithStats { + addresses: vec![MutliaddrWithStats::from(net_address)], + last_attempted: None, + } + } +} + +impl From> for MultiaddressesWithStats { + /// Constructs a new list of addresses with usage stats from a Vec + fn from(net_addresses: Vec) -> Self { + MultiaddressesWithStats { + addresses: net_addresses + .into_iter() + .map(MutliaddrWithStats::from) + .collect::>(), + last_attempted: None, + } + } +} + +impl From> for MultiaddressesWithStats { + /// Constructs NetAddressesWithStats from a list of addresses with usage stats + fn from(addresses: Vec) -> Self { + MultiaddressesWithStats { + addresses, + last_attempted: None, + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use multiaddr::Multiaddr; + + #[test] + fn test_index_impl() { + let net_address1 = "/ip4/123.0.0.123/tcp/8000".parse::().unwrap(); + let net_address2 = "/ip4/125.1.54.254/tcp/7999".parse::().unwrap(); + let net_address3 = "/ip4/175.6.3.145/tcp/8000".parse::().unwrap(); + let net_addresses: MultiaddressesWithStats = + vec![net_address1.clone(), net_address2.clone(), net_address3.clone()].into(); + + assert_eq!(net_addresses[0].address, net_address1); + assert_eq!(net_addresses[1].address, net_address2); + assert_eq!(net_addresses[2].address, net_address3); + } + + #[test] + fn test_last_seen() { + let net_address1 = "/ip4/123.0.0.123/tcp/8000".parse::().unwrap(); + let net_address2 = "/ip4/125.1.54.254/tcp/7999".parse::().unwrap(); + let net_address3 = "/ip4/175.6.3.145/tcp/8000".parse::().unwrap(); + let mut net_addresses = MultiaddressesWithStats::from(net_address1.clone()); + net_addresses.add_net_address(&net_address2); + net_addresses.add_net_address(&net_address3); + + assert!(net_addresses.mark_successful_connection_attempt(&net_address3)); + assert!(net_addresses.mark_successful_connection_attempt(&net_address1)); + assert!(net_addresses.mark_successful_connection_attempt(&net_address2)); + let desired_last_seen = net_addresses.addresses[0].last_seen; + let last_seen = net_addresses.last_seen(); + assert_eq!(desired_last_seen.unwrap(), last_seen.unwrap()); + } + + #[test] + fn test_add_net_address() { + let net_address1 = "/ip4/123.0.0.123/tcp/8000".parse::().unwrap(); + let net_address2 = "/ip4/125.1.54.254/tcp/7999".parse::().unwrap(); + let net_address3 = "/ip4/175.6.3.145/tcp/8000".parse::().unwrap(); + let mut net_addresses = MultiaddressesWithStats::from(net_address1.clone()); + net_addresses.add_net_address(&net_address2); + net_addresses.add_net_address(&net_address3); + // Add duplicate address, test add_net_address is idempotent + net_addresses.add_net_address(&net_address2); + assert_eq!(net_addresses.addresses.len(), 3); + assert_eq!(net_addresses.addresses[0].address, net_address1); + assert_eq!(net_addresses.addresses[1].address, net_address2); + assert_eq!(net_addresses.addresses[2].address, net_address3); + } + + #[test] + fn test_get_net_address() { + let net_address1 = "/ip4/123.0.0.123/tcp/8000".parse::().unwrap(); + let net_address2 = "/ip4/125.1.54.254/tcp/7999".parse::().unwrap(); + let net_address3 = "/ip4/175.6.3.145/tcp/8000".parse::().unwrap(); + let mut net_addresses = MultiaddressesWithStats::from(net_address1.clone()); + net_addresses.add_net_address(&net_address2); + net_addresses.add_net_address(&net_address3); + + let priority_address = net_addresses.address_iter().next().unwrap(); + assert_eq!(priority_address, &net_address1); + + assert!(net_addresses.update_latency(&net_address1, Duration::from_millis(250))); + assert!(net_addresses.update_latency(&net_address2, Duration::from_millis(50))); + assert!(net_addresses.update_latency(&net_address3, Duration::from_millis(100))); + let priority_address = net_addresses.address_iter().next().unwrap(); + assert_eq!(priority_address, &net_address2); + + assert!(net_addresses.mark_failed_connection_attempt(&net_address2)); + let priority_address = net_addresses.address_iter().next().unwrap(); + assert_eq!(priority_address, &net_address3); + } + + // TODO: Broken in release mode - investigate and fix + // #[test] + // fn test_stats_updates_on_addresses() { + // let net_address1 = "/ip4/123.0.0.123/tcp/8000".parse::().unwrap(); + // let net_address2 = "/ip4/125.1.54.254/tcp/7999".parse::().unwrap(); + // let net_address3 = "/ip4/175.6.3.145/tcp/8000".parse::().unwrap(); + // let mut addresses: Vec = Vec::new(); + // addresses.push(NetAddressWithStats::from(net_address1.clone())); + // addresses.push(NetAddressWithStats::from(net_address2.clone())); + // addresses.push(NetAddressWithStats::from(net_address3.clone())); + // let mut net_addresses = NetAddressesWithStats::new(addresses); + // + // assert!(net_addresses.update_latency(&net_address2, Duration::from_millis(200))); + // assert_eq!(net_addresses.addresses[0].avg_latency, Duration::from_millis(200)); + // assert_eq!(net_addresses.addresses[1].avg_latency, Duration::from_millis(0)); + // assert_eq!(net_addresses.addresses[2].avg_latency, Duration::from_millis(0)); + // + // thread::sleep(Duration::from_millis(1)); + // assert!(net_addresses.mark_message_received(&net_address1)); + // assert!(net_addresses.addresses[0].last_seen.is_some()); + // assert!(net_addresses.addresses[1].last_seen.is_some()); + // assert!(net_addresses.addresses[2].last_seen.is_none()); + // assert!(net_addresses.addresses[0].last_seen.unwrap() > net_addresses.addresses[1].last_seen.unwrap()); + // + // assert!(net_addresses.mark_message_rejected(&net_address2)); + // assert!(net_addresses.mark_message_rejected(&net_address3)); + // assert!(net_addresses.mark_message_rejected(&net_address3)); + // assert_eq!(net_addresses.addresses[0].rejected_message_count, 2); + // assert_eq!(net_addresses.addresses[1].rejected_message_count, 1); + // assert_eq!(net_addresses.addresses[2].rejected_message_count, 0); + // + // assert!(net_addresses.mark_failed_connection_attempt(&net_address1)); + // assert!(net_addresses.mark_failed_connection_attempt(&net_address2)); + // assert!(net_addresses.mark_failed_connection_attempt(&net_address3)); + // assert!(net_addresses.mark_failed_connection_attempt(&net_address1)); + // assert!(net_addresses.mark_successful_connection_attempt(&net_address2)); + // assert_eq!(net_addresses.addresses[0].connection_attempts, 0); + // assert_eq!(net_addresses.addresses[1].connection_attempts, 1); + // assert_eq!(net_addresses.addresses[2].connection_attempts, 2); + // } + + #[test] + fn test_resetting_all_connection_attempts() { + let net_address1 = "/ip4/123.0.0.123/tcp/8000".parse::().unwrap(); + let net_address2 = "/ip4/125.1.54.254/tcp/7999".parse::().unwrap(); + let net_address3 = "/ip4/175.6.3.145/tcp/8000".parse::().unwrap(); + let mut addresses: Vec = Vec::new(); + addresses.push(MutliaddrWithStats::from(net_address1.clone())); + addresses.push(MutliaddrWithStats::from(net_address2.clone())); + addresses.push(MutliaddrWithStats::from(net_address3.clone())); + let mut net_addresses = MultiaddressesWithStats::new(addresses); + assert!(net_addresses.mark_failed_connection_attempt(&net_address1)); + assert!(net_addresses.mark_failed_connection_attempt(&net_address2)); + assert!(net_addresses.mark_failed_connection_attempt(&net_address3)); + assert!(net_addresses.mark_failed_connection_attempt(&net_address1)); + + assert_eq!(net_addresses.addresses[0].connection_attempts, 1); + assert_eq!(net_addresses.addresses[1].connection_attempts, 1); + assert_eq!(net_addresses.addresses[2].connection_attempts, 2); + net_addresses.reset_connection_attempts(); + assert_eq!(net_addresses.addresses[0].connection_attempts, 0); + assert_eq!(net_addresses.addresses[1].connection_attempts, 0); + assert_eq!(net_addresses.addresses[2].connection_attempts, 0); + } +} diff --git a/comms/src/noise/config.rs b/comms/src/noise/config.rs new file mode 100644 index 0000000000..7776ade335 --- /dev/null +++ b/comms/src/noise/config.rs @@ -0,0 +1,155 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This file is heavily influenced by the Libra Noise protocol implementation. + +use crate::{ + connection_manager::ConnectionDirection, + noise::{ + crypto_resolver::TariCryptoResolver, + error::NoiseError, + socket::{Handshake, NoiseSocket}, + }, + peer_manager::NodeIdentity, +}; +use futures::{AsyncRead, AsyncWrite}; +use log::*; +use snow::{self, params::NoiseParams}; +use std::sync::Arc; +use tari_crypto::tari_utilities::ByteArray; + +const LOG_TARGET: &str = "comms::noise"; +pub(super) const NOISE_IX_PARAMETER: &str = "Noise_IX_25519_ChaChaPoly_BLAKE2b"; + +/// The Noise protocol configuration to be used to perform a protocol upgrade on an underlying +/// socket. +#[derive(Clone, Debug)] +pub struct NoiseConfig { + node_identity: Arc, + parameters: NoiseParams, +} + +impl NoiseConfig { + /// Create a new NoiseConfig with the provided keypair + pub fn new(node_identity: Arc) -> Self { + let parameters: NoiseParams = NOISE_IX_PARAMETER.parse().expect("Invalid noise parameters"); + Self { + node_identity, + parameters, + } + } + + /// Upgrades the given socket to using the noise protocol. The upgraded socket and the peer's static key + /// is returned. + pub async fn upgrade_socket( + &self, + socket: TSocket, + direction: ConnectionDirection, + ) -> Result, NoiseError> + where + TSocket: AsyncWrite + AsyncRead + Unpin, + { + let handshake_state = { + let builder = + snow::Builder::with_resolver(self.parameters.clone(), Box::new(TariCryptoResolver::default())) + .local_private_key(self.node_identity.secret_key().as_bytes()); + + match direction { + ConnectionDirection::Outbound => { + debug!(target: LOG_TARGET, "Starting noise initiator handshake "); + builder.build_initiator()? + }, + ConnectionDirection::Inbound => { + debug!(target: LOG_TARGET, "Starting noise responder handshake"); + builder.build_responder()? + }, + } + }; + + let handshake = Handshake::new(socket, handshake_state); + let socket = handshake.handshake_1rt().await.map_err(NoiseError::HandshakeFailed)?; + + Ok(socket) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::{memsocket::MemorySocket, peer_manager::PeerFeatures, test_utils::node_identity::build_node_identity}; + use futures::{future, AsyncReadExt, AsyncWriteExt, FutureExt}; + use snow::params::{BaseChoice, CipherChoice, DHChoice, HandshakePattern, HashChoice}; + use tokio::runtime::Runtime; + + fn check_noise_params(config: &NoiseConfig) { + assert_eq!(config.parameters.hash, HashChoice::Blake2b); + assert_eq!(config.parameters.name, NOISE_IX_PARAMETER); + assert_eq!(config.parameters.cipher, CipherChoice::ChaChaPoly); + assert_eq!(config.parameters.base, BaseChoice::Noise); + assert_eq!(config.parameters.dh, DHChoice::Curve25519); + assert_eq!(config.parameters.handshake.pattern, HandshakePattern::IX); + } + + #[test] + fn new() { + let node_identity = build_node_identity(PeerFeatures::COMMUNICATION_NODE); + let config = NoiseConfig::new(node_identity.clone()); + check_noise_params(&config); + assert_eq!(config.node_identity.public_key(), node_identity.public_key()); + } + + #[test] + fn upgrade_socket() { + let mut rt = Runtime::new().unwrap(); + + let node_identity1 = build_node_identity(PeerFeatures::COMMUNICATION_NODE); + let config1 = NoiseConfig::new(node_identity1.clone()); + + let node_identity2 = build_node_identity(PeerFeatures::COMMUNICATION_NODE); + let config2 = NoiseConfig::new(node_identity2.clone()); + + rt.block_on(async move { + let (in_socket, out_socket) = MemorySocket::new_pair(); + let (mut socket_in, mut socket_out) = future::join( + config1.upgrade_socket(in_socket, ConnectionDirection::Inbound), + config2.upgrade_socket(out_socket, ConnectionDirection::Outbound), + ) + .map(|(s1, s2)| (s1.unwrap(), s2.unwrap())) + .await; + + let in_pubkey = socket_in.get_remote_public_key().unwrap(); + let out_pubkey = socket_out.get_remote_public_key().unwrap(); + + assert_eq!(&in_pubkey, node_identity2.public_key()); + assert_eq!(&out_pubkey, node_identity1.public_key()); + + let sample = b"Children of time"; + socket_in.write_all(sample).await.unwrap(); + socket_in.flush().await.unwrap(); + socket_in.close().await.unwrap(); + + let mut read_buf = Vec::with_capacity(16); + socket_out.read_to_end(&mut read_buf).await.unwrap(); + assert_eq!(read_buf, sample); + }); + } +} diff --git a/comms/src/noise/crypto_resolver.rs b/comms/src/noise/crypto_resolver.rs new file mode 100644 index 0000000000..0bc9266c66 --- /dev/null +++ b/comms/src/noise/crypto_resolver.rs @@ -0,0 +1,157 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::types::{CommsPublicKey, CommsSecretKey}; +use rand::rngs::OsRng; +use snow::{ + params::{CipherChoice, DHChoice, HashChoice}, + resolvers::{CryptoResolver, DefaultResolver}, + types::{Cipher, Dh, Hash, Random}, +}; +use tari_crypto::{ + keys::{DiffieHellmanSharedSecret, PublicKey, SecretKey}, + tari_utilities::ByteArray, +}; + +macro_rules! copy_slice { + ($inslice:expr, $outslice:expr) => { + $outslice[..$inslice.len()].copy_from_slice(&$inslice[..]) + }; +} + +#[derive(Default)] +pub struct TariCryptoResolver(DefaultResolver); + +impl CryptoResolver for TariCryptoResolver { + fn resolve_rng(&self) -> Option> { + self.0.resolve_rng() + } + + fn resolve_dh(&self, choice: &DHChoice) -> Option> { + match *choice { + DHChoice::Curve25519 => Some(Box::new(CommsDiffieHellman::default())), + _ => None, + } + } + + fn resolve_hash(&self, choice: &HashChoice) -> Option> { + self.0.resolve_hash(choice) + } + + fn resolve_cipher(&self, choice: &CipherChoice) -> Option> { + self.0.resolve_cipher(choice) + } +} + +#[derive(Default)] +struct CommsDiffieHellman { + secret_key: CommsSecretKey, + public_key: CommsPublicKey, +} + +impl Dh for CommsDiffieHellman { + fn name(&self) -> &'static str { + static NAME: &str = "Ristretto"; + NAME + } + + fn pub_len(&self) -> usize { + CommsPublicKey::key_length() + } + + fn priv_len(&self) -> usize { + CommsSecretKey::key_length() + } + + fn set(&mut self, privkey: &[u8]) { + // `set` is used in the Builder, so this will panic if given an invalid secret key. + self.secret_key = CommsSecretKey::from_bytes(privkey).expect("invalid secret key"); + self.public_key = CommsPublicKey::from_secret_key(&self.secret_key); + } + + fn generate(&mut self, _: &mut dyn Random) { + // `&mut dyn Random` is unsized and cannot be used with `CommsSecretKey::random` + // COMMS_RNG fulfills the RNG requirements anyhow + self.secret_key = CommsSecretKey::random(&mut OsRng); + self.public_key = CommsPublicKey::from_secret_key(&self.secret_key); + } + + fn pubkey(&self) -> &[u8] { + &self.public_key.as_bytes() + } + + fn privkey(&self) -> &[u8] { + &self.secret_key.as_bytes() + } + + fn dh(&self, public_key: &[u8], out: &mut [u8]) -> Result<(), ()> { + let pk = CommsPublicKey::from_bytes(&public_key[..self.pub_len()]).map_err(|_| ())?; + let shared = CommsPublicKey::shared_secret(&self.secret_key, &pk); + let shared_bytes = shared.as_bytes(); + copy_slice!(shared_bytes, out); + Ok(()) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::noise::config::NOISE_IX_PARAMETER; + use snow::Keypair; + + fn build_keypair() -> Keypair { + snow::Builder::with_resolver( + NOISE_IX_PARAMETER.parse().unwrap(), + Box::new(TariCryptoResolver::default()), + ) + .generate_keypair() + .unwrap() + } + + #[test] + fn generate() { + let keypair = build_keypair(); + + let sk = CommsSecretKey::from_bytes(&keypair.private).unwrap(); + let expected_pk = CommsPublicKey::from_secret_key(&sk); + let pk = CommsPublicKey::from_bytes(&keypair.public).unwrap(); + assert_eq!(pk, expected_pk); + } + + #[test] + fn dh() { + let (secret_key, public_key) = CommsPublicKey::random_keypair(&mut OsRng); + let dh = CommsDiffieHellman { + public_key: public_key.clone(), + secret_key: secret_key.clone(), + }; + + let (secret_key2, public_key2) = CommsPublicKey::random_keypair(&mut OsRng); + let expected_shared = CommsPublicKey::shared_secret(&secret_key2, &public_key); + + let mut out = [0; 32]; + dh.dh(&public_key2.as_bytes(), &mut out).unwrap(); + let shared = CommsPublicKey::from_bytes(&out).unwrap(); + + assert_eq!(shared, expected_shared); + } +} diff --git a/comms/src/noise/error.rs b/comms/src/noise/error.rs new file mode 100644 index 0000000000..9877f88dcf --- /dev/null +++ b/comms/src/noise/error.rs @@ -0,0 +1,31 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use derive_error::Error; +use std::io; + +#[derive(Debug, Error)] +pub enum NoiseError { + SnowError(snow::Error), + #[error(no_from)] + HandshakeFailed(io::Error), +} diff --git a/comms/src/noise/mod.rs b/comms/src/noise/mod.rs new file mode 100644 index 0000000000..ba18e1234d --- /dev/null +++ b/comms/src/noise/mod.rs @@ -0,0 +1,32 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// TODO: Remove #[allow(dead_code)] when used +#[allow(dead_code)] +mod config; +mod crypto_resolver; +mod error; +mod socket; + +pub use config::NoiseConfig; +pub use error::NoiseError; +pub use socket::NoiseSocket; diff --git a/comms/src/noise/socket.rs b/comms/src/noise/socket.rs new file mode 100644 index 0000000000..661e43be39 --- /dev/null +++ b/comms/src/noise/socket.rs @@ -0,0 +1,757 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This file is a slightly modified version of the Libra NoiseSocket implementation. +// Copyright (c) The Libra Core Contributors +// SPDX-License-Identifier: Apache-2.0 + +//! Noise Socket + +use futures::ready; +use log::*; +use snow::{error::StateProblem, HandshakeState, TransportState}; +use std::{ + convert::TryInto, + io, + pin::Pin, + task::{Context, Poll}, +}; +// use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; +use crate::types::CommsPublicKey; +use futures::{io::Error, AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; +use tari_crypto::tari_utilities::ByteArray; + +const LOG_TARGET: &str = "comms::noise::socket"; + +const MAX_PAYLOAD_LENGTH: usize = u16::max_value() as usize; // 65535 + +// The maximum number of bytes that we can buffer is 16 bytes less than u16::max_value() because +// encrypted messages include a tag along with the payload. +const MAX_WRITE_BUFFER_LENGTH: usize = u16::max_value() as usize - 16; // 65519 + +/// Collection of buffers used for buffering data during the various read/write states of a +/// NoiseSocket +struct NoiseBuffers { + /// Encrypted frame read from the wire + read_encrypted: [u8; MAX_PAYLOAD_LENGTH], + /// Decrypted data read from the wire (produced by having snow decrypt the `read_encrypted` + /// buffer) + read_decrypted: [u8; MAX_PAYLOAD_LENGTH], + /// Unencrypted data intended to be written to the wire + write_decrypted: [u8; MAX_WRITE_BUFFER_LENGTH], + /// Encrypted data to write to the wire (produced by having snow encrypt the `write_decrypted` + /// buffer) + write_encrypted: [u8; MAX_PAYLOAD_LENGTH], +} + +impl NoiseBuffers { + fn new() -> Self { + Self { + read_encrypted: [0; MAX_PAYLOAD_LENGTH], + read_decrypted: [0; MAX_PAYLOAD_LENGTH], + write_decrypted: [0; MAX_WRITE_BUFFER_LENGTH], + write_encrypted: [0; MAX_PAYLOAD_LENGTH], + } + } +} + +/// Hand written Debug implementation in order to omit the printing of huge buffers of data +impl ::std::fmt::Debug for NoiseBuffers { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + f.debug_struct("NoiseBuffers").finish() + } +} + +/// Possible read states for a [NoiseSocket] +#[derive(Debug)] +enum ReadState { + /// Initial State + Init, + /// Read frame length + ReadFrameLen { buf: [u8; 2], offset: usize }, + /// Read encrypted frame + ReadFrame { frame_len: u16, offset: usize }, + /// Copy decrypted frame to provided buffer + CopyDecryptedFrame { decrypted_len: usize, offset: usize }, + /// End of file reached, result indicated if EOF was expected or not + Eof(Result<(), ()>), + /// Decryption Error + DecryptionError(snow::Error), +} + +/// Possible write states for a [NoiseSocket] +#[derive(Debug)] +enum WriteState { + /// Initial State + Init, + /// Buffer provided data + BufferData { offset: usize }, + /// Write frame length to the wire + WriteFrameLen { + frame_len: u16, + buf: [u8; 2], + offset: usize, + }, + /// Write encrypted frame to the wire + WriteEncryptedFrame { frame_len: u16, offset: usize }, + /// Flush the underlying socket + Flush, + /// End of file reached + Eof, + /// Encryption Error + EncryptionError(snow::Error), +} + +/// A Noise session with a remote +/// +/// Encrypts data to be written to and decrypts data that is read from the underlying socket using +/// the noise protocol. This is done by wrapping noise payloads in u16 (big endian) length prefix +/// frames. +#[derive(Debug)] +pub struct NoiseSocket { + socket: TSocket, + state: NoiseState, + buffers: Box, + read_state: ReadState, + write_state: WriteState, +} + +impl NoiseSocket { + fn new(socket: TSocket, session: NoiseState) -> Self { + Self { + socket, + state: session, + buffers: Box::new(NoiseBuffers::new()), + read_state: ReadState::Init, + write_state: WriteState::Init, + } + } + + /// Get the raw remote static key + pub fn get_remote_static(&self) -> Option<&[u8]> { + self.state.get_remote_static() + } + + /// Get the remote static key as a CommsPublicKey + pub fn get_remote_public_key(&self) -> Option { + self.get_remote_static() + .and_then(|s| CommsPublicKey::from_bytes(s).ok()) + } +} + +fn poll_write_all( + mut context: &mut Context, + mut socket: Pin<&mut TSocket>, + buf: &[u8], + offset: &mut usize, +) -> Poll> +where + TSocket: AsyncWrite, +{ + loop { + let n = ready!(socket.as_mut().poll_write(&mut context, &buf[*offset..]))?; + trace!( + target: LOG_TARGET, + "poll_write_all: wrote {}/{} bytes", + *offset + n, + buf.len() + ); + if n == 0 { + return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); + } + *offset += n; + assert!(*offset <= buf.len()); + + if *offset == buf.len() { + return Poll::Ready(Ok(())); + } + } +} + +/// Read a u16 frame length from `socket`. +/// +/// Can result in the following output: +/// 1) Ok(None) => EOF; remote graceful shutdown +/// 2) Err(UnexpectedEOF) => read 1 byte then hit EOF; remote died +/// 3) Ok(Some(n)) => new frame of length n +fn poll_read_u16frame_len( + context: &mut Context, + socket: Pin<&mut TSocket>, + buf: &mut [u8; 2], + offset: &mut usize, +) -> Poll>> +where + TSocket: AsyncRead, +{ + match ready!(poll_read_exact(context, socket, buf, offset)) { + Ok(()) => Poll::Ready(Ok(Some(u16::from_be_bytes(*buf)))), + Err(e) => { + if *offset == 0 && e.kind() == io::ErrorKind::UnexpectedEof { + return Poll::Ready(Ok(None)); + } + Poll::Ready(Err(e)) + }, + } +} + +fn poll_read_exact( + mut context: &mut Context, + mut socket: Pin<&mut TSocket>, + buf: &mut [u8], + offset: &mut usize, +) -> Poll> +where + TSocket: AsyncRead, +{ + loop { + let n = ready!(socket.as_mut().poll_read(&mut context, &mut buf[*offset..]))?; + trace!( + target: LOG_TARGET, + "poll_read_exact: read {}/{} bytes", + *offset + n, + buf.len() + ); + if n == 0 { + return Poll::Ready(Err(io::ErrorKind::UnexpectedEof.into())); + } + *offset += n; + assert!(*offset <= buf.len()); + + if *offset == buf.len() { + return Poll::Ready(Ok(())); + } + } +} + +impl NoiseSocket +where TSocket: AsyncRead + Unpin +{ + fn poll_read(&mut self, mut context: &mut Context, buf: &mut [u8]) -> Poll> { + loop { + trace!(target: LOG_TARGET, "NoiseSocket ReadState::{:?}", self.read_state); + match self.read_state { + ReadState::Init => { + self.read_state = ReadState::ReadFrameLen { buf: [0, 0], offset: 0 }; + }, + ReadState::ReadFrameLen { + ref mut buf, + ref mut offset, + } => { + match ready!(poll_read_u16frame_len( + &mut context, + Pin::new(&mut self.socket), + buf, + offset + )) { + Ok(Some(frame_len)) => { + // Empty Frame + if frame_len == 0 { + self.read_state = ReadState::Init; + } else { + self.read_state = ReadState::ReadFrame { frame_len, offset: 0 }; + } + }, + Ok(None) => { + self.read_state = ReadState::Eof(Ok(())); + }, + Err(e) => { + if e.kind() == io::ErrorKind::UnexpectedEof { + self.read_state = ReadState::Eof(Err(())); + } + return Poll::Ready(Err(e)); + }, + } + }, + ReadState::ReadFrame { + frame_len, + ref mut offset, + } => { + match ready!(poll_read_exact( + &mut context, + Pin::new(&mut self.socket), + &mut self.buffers.read_encrypted[..(frame_len as usize)], + offset + )) { + Ok(()) => { + match self.state.read_message( + &self.buffers.read_encrypted[..(frame_len as usize)], + &mut self.buffers.read_decrypted, + ) { + Ok(decrypted_len) => { + self.read_state = ReadState::CopyDecryptedFrame { + decrypted_len, + offset: 0, + }; + }, + Err(e) => { + error!(target: LOG_TARGET, "Decryption Error: {}", e); + self.read_state = ReadState::DecryptionError(e); + }, + } + }, + Err(e) => { + if e.kind() == io::ErrorKind::UnexpectedEof { + self.read_state = ReadState::Eof(Err(())); + } + return Poll::Ready(Err(e)); + }, + } + }, + ReadState::CopyDecryptedFrame { + decrypted_len, + ref mut offset, + } => { + let bytes_to_copy = ::std::cmp::min(decrypted_len as usize - *offset, buf.len()); + buf[..bytes_to_copy] + .copy_from_slice(&self.buffers.read_decrypted[*offset..(*offset + bytes_to_copy)]); + trace!( + target: LOG_TARGET, + "CopyDecryptedFrame: copied {}/{} bytes", + *offset + bytes_to_copy, + decrypted_len + ); + *offset += bytes_to_copy; + if *offset == decrypted_len as usize { + self.read_state = ReadState::Init; + } + return Poll::Ready(Ok(bytes_to_copy)); + }, + ReadState::Eof(Ok(())) => return Poll::Ready(Ok(0)), + ReadState::Eof(Err(())) => return Poll::Ready(Err(io::ErrorKind::UnexpectedEof.into())), + ReadState::DecryptionError(ref e) => { + return Poll::Ready(Err(io::Error::new( + io::ErrorKind::InvalidData, + format!("DecryptionError: {}", e), + ))) + }, + } + } + } +} + +impl AsyncRead for NoiseSocket +where TSocket: AsyncRead + Unpin +{ + fn poll_read(self: Pin<&mut Self>, context: &mut Context, buf: &mut [u8]) -> Poll> { + self.get_mut().poll_read(context, buf) + } +} + +impl NoiseSocket +where TSocket: AsyncWrite + Unpin +{ + fn poll_write_or_flush( + &mut self, + mut context: &mut Context, + buf: Option<&[u8]>, + ) -> Poll>> + { + loop { + trace!( + target: LOG_TARGET, + "NoiseSocket {} WriteState::{:?}", + if buf.is_some() { "poll_write" } else { "poll_flush" }, + self.write_state, + ); + match self.write_state { + WriteState::Init => { + if buf.is_some() { + self.write_state = WriteState::BufferData { offset: 0 }; + } else { + return Poll::Ready(Ok(None)); + } + }, + WriteState::BufferData { ref mut offset } => { + let bytes_buffered = if let Some(buf) = buf { + let bytes_to_copy = ::std::cmp::min(MAX_WRITE_BUFFER_LENGTH - *offset, buf.len()); + self.buffers.write_decrypted[*offset..(*offset + bytes_to_copy)] + .copy_from_slice(&buf[..bytes_to_copy]); + trace!( + target: LOG_TARGET, + "BufferData: buffered {}/{} bytes", + bytes_to_copy, + buf.len() + ); + *offset += bytes_to_copy; + Some(bytes_to_copy) + } else { + None + }; + + if buf.is_none() || *offset == MAX_WRITE_BUFFER_LENGTH { + match self.state.write_message( + &self.buffers.write_decrypted[..*offset], + &mut self.buffers.write_encrypted, + ) { + Ok(encrypted_len) => { + let frame_len = encrypted_len.try_into().expect("offset should be able to fit in u16"); + self.write_state = WriteState::WriteFrameLen { + frame_len, + buf: u16::to_be_bytes(frame_len), + offset: 0, + }; + }, + Err(e) => { + error!(target: LOG_TARGET, "Encryption Error: {}", e); + let err = io::Error::new(io::ErrorKind::InvalidData, format!("EncryptionError: {}", e)); + self.write_state = WriteState::EncryptionError(e); + return Poll::Ready(Err(err)); + }, + } + } + + if let Some(bytes_buffered) = bytes_buffered { + return Poll::Ready(Ok(Some(bytes_buffered))); + } + }, + WriteState::WriteFrameLen { + frame_len, + ref buf, + ref mut offset, + } => match ready!(poll_write_all(&mut context, Pin::new(&mut self.socket), buf, offset)) { + Ok(()) => { + self.write_state = WriteState::WriteEncryptedFrame { frame_len, offset: 0 }; + }, + Err(e) => { + if e.kind() == io::ErrorKind::WriteZero { + self.write_state = WriteState::Eof; + } + return Poll::Ready(Err(e)); + }, + }, + WriteState::WriteEncryptedFrame { + frame_len, + ref mut offset, + } => { + match ready!(poll_write_all( + &mut context, + Pin::new(&mut self.socket), + &self.buffers.write_encrypted[..(frame_len as usize)], + offset + )) { + Ok(()) => { + self.write_state = WriteState::Flush; + }, + Err(e) => { + if e.kind() == io::ErrorKind::WriteZero { + self.write_state = WriteState::Eof; + } + return Poll::Ready(Err(e)); + }, + } + }, + WriteState::Flush => { + ready!(Pin::new(&mut self.socket).poll_flush(&mut context))?; + self.write_state = WriteState::Init; + }, + WriteState::Eof => return Poll::Ready(Err(io::ErrorKind::WriteZero.into())), + WriteState::EncryptionError(ref e) => { + return Poll::Ready(Err(io::Error::new( + io::ErrorKind::InvalidData, + format!("EncryptionError: {}", e), + ))) + }, + } + } + } + + fn poll_write(&mut self, context: &mut Context, buf: &[u8]) -> Poll> { + if let Some(bytes_written) = ready!(self.poll_write_or_flush(context, Some(buf)))? { + Poll::Ready(Ok(bytes_written)) + } else { + unreachable!(); + } + } + + fn poll_flush(&mut self, context: &mut Context) -> Poll> { + if ready!(self.poll_write_or_flush(context, None))?.is_none() { + Poll::Ready(Ok(())) + } else { + unreachable!(); + } + } +} + +impl AsyncWrite for NoiseSocket +where TSocket: AsyncWrite + Unpin +{ + fn poll_write(self: Pin<&mut Self>, cx: &mut Context, buf: &[u8]) -> Poll> { + self.get_mut().poll_write(cx, buf) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + self.get_mut().poll_flush(cx) + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.socket).poll_close(cx) + } +} + +pub struct Handshake { + socket: NoiseSocket, +} + +impl Handshake { + pub fn new(socket: TSocket, state: HandshakeState) -> Self { + Self { + socket: NoiseSocket::new(socket, state.into()), + } + } +} + +impl Handshake +where TSocket: AsyncRead + AsyncWrite + Unpin +{ + /// Perform a Single Round-Trip noise IX handshake returning the underlying [NoiseSocket] + /// (switched to transport mode) upon success. + pub async fn handshake_1rt(mut self) -> io::Result> { + // The Dialer + if self.socket.state.is_initiator() { + // -> e, s + self.send().await?; + self.flush().await?; + + // <- e, ee, se, s, es + self.receive().await?; + } else { + // -> e, s + self.receive().await?; + + // <- e, ee, se, s, es + self.send().await?; + self.flush().await?; + } + + self.finish() + } + + async fn send(&mut self) -> io::Result { + self.socket.write(&[]).await + } + + async fn flush(&mut self) -> io::Result<()> { + self.socket.flush().await.map_err(Into::into) + } + + async fn receive(&mut self) -> io::Result { + self.socket.read(&mut []).await + } + + fn finish(self) -> io::Result> { + let transport_state = self + .socket + .state + .into_transport_mode() + .map_err(|err| io::Error::new(io::ErrorKind::Other, format!("Invalid snow state: {}", err)))?; + + Ok(NoiseSocket { + state: transport_state, + ..self.socket + }) + } +} + +#[derive(Debug)] +enum NoiseState { + HandshakeState(HandshakeState), + TransportState(TransportState), +} + +macro_rules! proxy_state_method { + (pub fn $name:ident(&mut self$(,)? $($arg_name:ident : $arg_type:ty),*) -> $ret:ty) => { + pub fn $name(&mut self, $($arg_name:$arg_type),*) -> $ret { + match self { + NoiseState::HandshakeState(state) => state.$name($($arg_name),*), + NoiseState::TransportState(state) => state.$name($($arg_name),*), + } + } + }; + (pub fn $name:ident(&self$(,)? $($arg_name:ident : $arg_type:ty),*) -> $ret:ty) => { + pub fn $name(&self, $($arg_name:$arg_type),*) -> $ret { + match self { + NoiseState::HandshakeState(state) => state.$name($($arg_name),*), + NoiseState::TransportState(state) => state.$name($($arg_name),*), + } + } + } +} + +impl NoiseState { + proxy_state_method!(pub fn write_message(&mut self, message: &[u8], payload: &mut [u8]) -> Result); + + proxy_state_method!(pub fn is_initiator(&self) -> bool); + + proxy_state_method!(pub fn read_message(&mut self, message: &[u8], payload: &mut [u8]) -> Result); + + proxy_state_method!(pub fn get_remote_static(&self) -> Option<&[u8]>); + + pub fn into_transport_mode(self) -> Result { + match self { + NoiseState::HandshakeState(state) => Ok(NoiseState::TransportState(state.into_transport_mode()?)), + _ => Err(snow::Error::State(StateProblem::HandshakeAlreadyFinished)), + } + } +} + +impl From for NoiseState { + fn from(state: HandshakeState) -> Self { + NoiseState::HandshakeState(state) + } +} + +impl From for NoiseState { + fn from(state: TransportState) -> Self { + NoiseState::TransportState(state) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::{memsocket::MemorySocket, noise::config::NOISE_IX_PARAMETER}; + use futures::future::join; + use snow::{params::NoiseParams, Builder, Error, Keypair}; + use std::io; + use tokio::runtime::Runtime; + + async fn build_test_connection( + ) -> Result<((Keypair, Handshake), (Keypair, Handshake)), Error> { + let parameters: NoiseParams = NOISE_IX_PARAMETER.parse().expect("Invalid protocol name"); + + let dialer_keypair = Builder::new(parameters.clone()).generate_keypair()?; + let listener_keypair = Builder::new(parameters.clone()).generate_keypair()?; + + let dialer_session = Builder::new(parameters.clone()) + .local_private_key(&dialer_keypair.private) + .build_initiator()?; + let listener_session = Builder::new(parameters.clone()) + .local_private_key(&listener_keypair.private) + .build_responder()?; + + let (dialer_socket, listener_socket) = MemorySocket::new_pair(); + let (dialer, listener) = ( + NoiseSocket::new(dialer_socket, dialer_session.into()), + NoiseSocket::new(listener_socket, listener_session.into()), + ); + + Ok(( + (dialer_keypair, Handshake { socket: dialer }), + (listener_keypair, Handshake { socket: listener }), + )) + } + + async fn perform_handshake( + dialer: Handshake, + listener: Handshake, + ) -> io::Result<(NoiseSocket, NoiseSocket)> + { + let (dialer_result, listener_result) = join(dialer.handshake_1rt(), listener.handshake_1rt()).await; + + Ok((dialer_result?, listener_result?)) + } + + #[tokio_macros::test] + async fn test_handshake() { + let ((dialer_keypair, dialer), (listener_keypair, listener)) = build_test_connection().await.unwrap(); + + let (dialer_socket, listener_socket) = perform_handshake(dialer, listener).await.unwrap(); + + assert_eq!( + dialer_socket.get_remote_static(), + Some(listener_keypair.public.as_ref()) + ); + assert_eq!( + listener_socket.get_remote_static(), + Some(dialer_keypair.public.as_ref()) + ); + } + + #[tokio_macros::test] + async fn simple_test() -> io::Result<()> { + let ((_dialer_keypair, dialer), (_listener_keypair, listener)) = build_test_connection().await.unwrap(); + + let (mut dialer_socket, mut listener_socket) = perform_handshake(dialer, listener).await?; + + dialer_socket.write_all(b"stormlight").await?; + dialer_socket.write_all(b" ").await?; + dialer_socket.write_all(b"archive").await?; + dialer_socket.flush().await?; + dialer_socket.close().await?; + + let mut buf = Vec::new(); + listener_socket.read_to_end(&mut buf).await?; + + assert_eq!(buf, b"stormlight archive"); + + Ok(()) + } + + #[tokio_macros::test] + async fn interleaved_writes() -> io::Result<()> { + let ((_dialer_keypair, dialer), (_listener_keypair, listener)) = build_test_connection().await.unwrap(); + + let (mut a, mut b) = perform_handshake(dialer, listener).await?; + + a.write_all(b"The Name of the Wind").await?; + a.flush().await?; + a.write_all(b"The Wise Man's Fear").await?; + a.flush().await?; + + b.write_all(b"The Doors of Stone").await?; + b.flush().await?; + + let mut buf = [0; 20]; + b.read_exact(&mut buf).await?; + assert_eq!(&buf, b"The Name of the Wind"); + let mut buf = [0; 19]; + b.read_exact(&mut buf).await?; + assert_eq!(&buf, b"The Wise Man's Fear"); + + let mut buf = [0; 18]; + a.read_exact(&mut buf).await?; + assert_eq!(&buf, b"The Doors of Stone"); + + Ok(()) + } + + #[test] + fn u16_max_writes() -> io::Result<()> { + // Current thread runtime stack overflows, so the full tokio runtime is used here + let mut rt = Runtime::new().unwrap(); + rt.block_on(async move { + let ((_dialer_keypair, dialer), (_listener_keypair, listener)) = build_test_connection().await.unwrap(); + + let (mut a, mut b) = perform_handshake(dialer, listener).await?; + + let buf_send = [1; MAX_PAYLOAD_LENGTH]; + a.write_all(&buf_send).await?; + a.flush().await?; + + let mut buf_receive = [0; MAX_PAYLOAD_LENGTH]; + b.read_exact(&mut buf_receive).await?; + assert_eq!(&buf_receive[..], &buf_send[..]); + + Ok(()) + }) + } +} diff --git a/comms/src/outbound_message_service/broadcast_strategy.rs b/comms/src/outbound_message_service/broadcast_strategy.rs deleted file mode 100644 index da26f6fccd..0000000000 --- a/comms/src/outbound_message_service/broadcast_strategy.rs +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::{ - consts::DHT_FORWARD_NODE_COUNT, - message::NodeDestination, - peer_manager::{node_id::NodeId, peer_manager::PeerManager, PeerManagerError}, - types::CommsPublicKey, -}; -use derive_error::Error; -use std::sync::Arc; - -#[derive(Debug, Error)] -pub enum BroadcastStrategyError { - PeerManagerError(PeerManagerError), -} - -#[derive(Debug)] -pub struct ClosestRequest { - pub n: usize, - pub node_id: NodeId, - pub excluded_peers: Vec, -} - -#[derive(Debug)] -pub enum BroadcastStrategy { - /// Send to a particular peer matching the given node ID - DirectNodeId(NodeId), - /// Send to a particular peer matching the given Public Key - DirectPublicKey(CommsPublicKey), - /// Send to all known Communication Node peers - Flood, - /// Send to all n nearest neighbour Communication Nodes - Closest(ClosestRequest), - /// Send to a random set of peers of size n that are Communication Nodes - Random(usize), -} - -impl BroadcastStrategy { - /// The forward function selects the most appropriate broadcast strategy based on the received messages destination - pub fn forward( - source_node_id: NodeId, - peer_manager: &Arc, - header_dest: NodeDestination, - excluded_peers: Vec, - ) -> Result - { - Ok(match header_dest { - NodeDestination::Unknown => { - // Send to the current nodes nearest neighbours - BroadcastStrategy::Closest(ClosestRequest { - n: DHT_FORWARD_NODE_COUNT, - node_id: source_node_id, - excluded_peers, - }) - }, - NodeDestination::PublicKey(dest_public_key) => { - if peer_manager.exists(&dest_public_key)? { - // Send to destination peer directly if the current node knows that peer - BroadcastStrategy::DirectPublicKey(dest_public_key) - } else { - // Send to the current nodes nearest neighbours - BroadcastStrategy::Closest(ClosestRequest { - n: DHT_FORWARD_NODE_COUNT, - node_id: source_node_id, - excluded_peers, - }) - } - }, - NodeDestination::NodeId(dest_node_id) => { - match peer_manager.find_with_node_id(&dest_node_id) { - Ok(dest_peer) => { - // Send to destination peer directly if the current node knows that peer - BroadcastStrategy::DirectPublicKey(dest_peer.public_key) - }, - Err(_) => { - // Send to peers that are closest to the destination network region - BroadcastStrategy::Closest(ClosestRequest { - n: DHT_FORWARD_NODE_COUNT, - node_id: dest_node_id, - excluded_peers, - }) - }, - } - }, - }) - } - - /// The discover function selects an appropriate broadcast strategy for the discovery of a specific node - pub fn discover( - source_node_id: NodeId, - dest_node_id: Option, - header_dest: NodeDestination, - excluded_peers: Vec, - ) -> Self - { - let network_location_node_id = match dest_node_id { - Some(node_id) => node_id, - None => match header_dest.clone() { - NodeDestination::Unknown => source_node_id, - NodeDestination::PublicKey(_) => source_node_id, - NodeDestination::NodeId(node_id) => node_id, - }, - }; - BroadcastStrategy::Closest(ClosestRequest { - n: DHT_FORWARD_NODE_COUNT, - node_id: network_location_node_id, - excluded_peers, - }) - } -} diff --git a/comms/src/outbound_message_service/mod.rs b/comms/src/outbound_message_service/mod.rs deleted file mode 100644 index 8726e53ef3..0000000000 --- a/comms/src/outbound_message_service/mod.rs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -//! # Outbound Message Service (OMS) -//! -//! Responsible for sending messages on the peer-to-peer network. -//! -//! In order to send a message the OMS: -//! -//! - evaluates and selects [Peer]'s according to the given [BroadcastStrategy], -//! - constructs, signs and optionally encrypts a [MessageEnvelope] for each selected [Peer], and -//! - forwards each constructed message frame to the [OutboundMessagePool] (OMP). -//! -//! # Broadcast Strategy -//! -//! Represents a strategy for selecting known [Peer]s from the [PeerManager]. -//! See [BroadcastStrategy] for more details. -//! -//! # Outbound Message Pool (OMP) -//! -//! Responsible for reliably sending messages to [Peer]s. -//! -//! The OMP reads from an [0MQ inproc] message queue. Each message received on this queue represents -//! a message which should be delivered to a single peer. A message is fair-dealt to a worker for -//! processing. The worker thread attempts to establish a [PeerConnection] to the given [Peer] -//! using the [ConnectionManager]. Once established, it uses the connection to send the -//! message. Once sent, it discards the message. If, for whatever reason, the message fails -//! to send, the message will be requeued and will try again later. If the message fails after -//! a configured number of attempts, the message is discarded. -//! -//! [BroadcastStrategy]: ./broadcast_strategy/enum.BroadcastStrategy.html -//! [MessageEnvelope]: ../message/struct.MessageEnvelope.html -//! [Peer]: ../peer_manager/peer/struct.Peer.html -//! [OutboundMessagePool]: ./outbound_message_pool/struct.OutboundMessagePool.html -//! [PeerConnection]: ../connection/peer_connection/index.html -//! [ConnectionManager]: ../connection_manager/index.html -//! [PeerManager]: ../peer_manager/index.html -//! [0MQ inproc]: http://api.zeromq.org/2-1:zmq-inproc - -pub mod broadcast_strategy; -pub mod error; -pub mod outbound_message_pool; -pub mod outbound_message_service; - -pub use self::{ - broadcast_strategy::{BroadcastStrategy, ClosestRequest}, - error::OutboundError, - outbound_message_pool::{OutboundMessage, OutboundMessagePool}, -}; diff --git a/comms/src/outbound_message_service/outbound_message_pool/error.rs b/comms/src/outbound_message_service/outbound_message_pool/error.rs deleted file mode 100644 index 096fbff489..0000000000 --- a/comms/src/outbound_message_service/outbound_message_pool/error.rs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::{ - connection::{ConnectionError, DealerProxyError}, - connection_manager::ConnectionManagerError, - peer_manager::PeerManagerError, -}; -use derive_error::Error; -use tari_utilities::message_format::MessageFormatError; - -#[derive(Error, Debug)] -pub enum OutboundMessagePoolError { - ConnectionError(ConnectionError), - /// Worker shut down sender disconnected before sending shutdown signal - WorkerShutdownSignalDisconnected, - #[error(msg_embedded, non_std, no_from)] - InvalidFrameFormat(String), - MessageFormatError(MessageFormatError), - PeerManagerError(PeerManagerError), - ConnectionManagerError(ConnectionManagerError), - DealerProxyError(DealerProxyError), - /// Unable to allocate message pool worker thread - ThreadInitializationError, - /// The message retry service has unexpectedly disconnected from it's channel - MessageRetryServiceDisconnected, -} diff --git a/comms/src/outbound_message_service/outbound_message_pool/mod.rs b/comms/src/outbound_message_service/outbound_message_pool/mod.rs deleted file mode 100644 index 83123e698c..0000000000 --- a/comms/src/outbound_message_service/outbound_message_pool/mod.rs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -mod error; -mod outbound_message; -mod pool; -mod retry_queue; -mod worker; - -pub use self::{ - error::OutboundMessagePoolError, - outbound_message::OutboundMessage, - pool::{OutboundMessagePool, OutboundMessagePoolConfig}, - retry_queue::RetryQueue, - worker::MessagePoolWorker, -}; diff --git a/comms/src/outbound_message_service/outbound_message_pool/outbound_message.rs b/comms/src/outbound_message_service/outbound_message_pool/outbound_message.rs deleted file mode 100644 index c5ca2d9268..0000000000 --- a/comms/src/outbound_message_service/outbound_message_pool/outbound_message.rs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::{message::FrameSet, peer_manager::node_id::NodeId}; -use serde::{Deserialize, Serialize}; - -/// The OutboundMessage has a copy of the MessageEnvelope. OutboundMessageService will create the -/// OutboundMessage and forward it to the OutboundMessagePool. -#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)] -pub struct OutboundMessage { - destination_node_id: NodeId, - message_frames: FrameSet, -} - -impl OutboundMessage { - /// Create a new OutboundMessage from the destination_node_id and message_frames - pub fn new(destination: NodeId, message_frames: FrameSet) -> OutboundMessage { - OutboundMessage { - destination_node_id: destination, - message_frames, - } - } - - /// Get a reference to the destination NodeID - pub fn destination_node_id(&self) -> &NodeId { - &self.destination_node_id - } - - /// Get a reference to the message frames - pub fn message_frames(&self) -> &FrameSet { - &self.message_frames - } - - /// Consume this wrapper and return ownership of the frames - pub fn into_frames(self) -> FrameSet { - self.message_frames - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn new() { - let node_id = NodeId::new(); - let subject = OutboundMessage::new(node_id.clone(), vec![vec![1]]); - assert_eq!(subject.message_frames[0].len(), 1); - assert_eq!(subject.destination_node_id, node_id); - } - - #[test] - fn getters() { - let node_id = NodeId::new(); - let frames = vec![vec![1]]; - let subject = OutboundMessage::new(node_id.clone(), frames.clone()); - - assert_eq!(subject.destination_node_id(), &node_id); - assert_eq!(subject.message_frames(), &frames); - assert_eq!(subject.into_frames(), frames); - } -} diff --git a/comms/src/outbound_message_service/outbound_message_pool/pool.rs b/comms/src/outbound_message_service/outbound_message_pool/pool.rs deleted file mode 100644 index d2a39b8d85..0000000000 --- a/comms/src/outbound_message_service/outbound_message_pool/pool.rs +++ /dev/null @@ -1,360 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use super::{MessagePoolWorker, RetryQueue}; -use crate::{ - connection_manager::ConnectionManager, - outbound_message_service::{ - outbound_message_pool::error::OutboundMessagePoolError, - OutboundError, - OutboundMessage, - }, - peer_manager::{NodeId, PeerManager}, -}; -use crossbeam_channel::{self as channel, Receiver, RecvTimeoutError, Sender}; -use crossbeam_deque::{Stealer, Worker}; -use log::*; -use std::{ - sync::Arc, - thread::{self, JoinHandle}, - time::Duration, -}; -use tari_utilities::thread_join::ThreadJoinWithTimeout; - -/// The default number of processing worker threads that will be created by the OutboundMessageService -pub const DEFAULT_NUM_OUTBOUND_MSG_WORKERS: usize = 4; - -const LOG_TARGET: &str = "comms::outbound_message_service::pool"; - -/// Set the maximum waiting time for Retry Service Threads and MessagePoolWorker threads to join -const MSG_POOL_WORKER_THREAD_JOIN_TIMEOUT: Duration = Duration::from_millis(3000); -const WORK_FORWARDER_THREAD_JOIN_TIMEOUT: Duration = Duration::from_millis(1500); - -#[derive(Clone, Copy)] -pub struct OutboundMessagePoolConfig { - /// How many workers to spawn - pub num_workers: usize, - /// How many times the pool will requeue a message to be sent - pub max_retries: u32, -} - -impl Default for OutboundMessagePoolConfig { - fn default() -> Self { - OutboundMessagePoolConfig { - num_workers: DEFAULT_NUM_OUTBOUND_MSG_WORKERS, - max_retries: 10, - } - } -} - -/// # OutboundMessagePool -/// -/// The OutboundMessagePool receives messages and forwards them to a pool of [MsgPoolWorker]s who's job it is to -/// reliably send the message, if possible. -/// -/// The pool starts the configured number of workers (see [OutboundMessagePoolConfig]) and distributes messages -/// between them using a [crossbeam_deque::Worker]. -/// -/// Messages to send are received on a [crossbeam_channel::Receiver]. A copy of the [Sender] side can be obtained -/// by calling the [OutboundMessagePool::sender] method. -/// -/// [crossbeam_channel::Receiver]: https://docs.rs/crossbeam-channel/0.3.9/crossbeam_channel/struct.Receiver.html -/// [Sender]: https://docs.rs/crossbeam-channel/0.3.9/crossbeam_channel/struct.Sender.html -/// [OutboundMessagePool::sender]: #method.sender -/// [crossbeam_deque::Worker]: https://docs.rs/crossbeam/0.7.2/crossbeam/deque/struct.Worker.html -/// [OutboundMessage]: ../outbound_message/struct.OutboundMessage.html -/// [OutboundMessagePoolConfig]: ./struct.OutboundMessagePoolConfig.html -/// [MsgPoolWorker]: ../worker/struct.MsgPoolWorker.html -pub struct OutboundMessagePool { - config: OutboundMessagePoolConfig, - message_tx: Sender, - message_rx: Option>, - peer_manager: Arc, - retry_queue: RetryQueue, - connection_manager: Arc, - worker_thread_handles: Vec>>, - work_forwarder_handle: Option>, - worker_shutdown_signals: Vec>, - work_forwarder_shutdown_tx: Sender<()>, - work_forwarder_shutdown_rx: Option>, -} -impl OutboundMessagePool { - /// Construct a new Outbound Message Pool. - /// - /// # Arguments - /// `config` - The configuration struct to use for the Outbound Message Pool - /// `peer_manager` - Arc to a PeerManager - /// `connection_manager` - Arc to a ConnectionManager - pub fn new( - config: OutboundMessagePoolConfig, - peer_manager: Arc, - connection_manager: Arc, - ) -> OutboundMessagePool - { - let (message_tx, message_rx) = channel::unbounded(); - let (shutdown_tx, shutdown_rx) = channel::bounded(1); - let retry_queue = RetryQueue::new(); - OutboundMessagePool { - config, - message_rx: Some(message_rx), - message_tx, - peer_manager, - connection_manager, - retry_queue, - worker_thread_handles: Vec::new(), - worker_shutdown_signals: Vec::new(), - work_forwarder_handle: None, - work_forwarder_shutdown_tx: shutdown_tx, - work_forwarder_shutdown_rx: Some(shutdown_rx), - } - } - - /// Returns a copy of the Sender which can be used to send messages for processing to the - /// OutboundMessagePool workers - pub fn sender(&self) -> Sender { - self.message_tx.clone() - } - - /// Starts a thread that reads from the message_source and pushes worker on the worker queue - fn start_work_forwarder(&mut self, worker: Worker) -> JoinHandle<()> { - let message_rx = self - .message_rx - .take() - .expect("Invariant: OutboundMessagePool was initialized without a message_rx"); - - let shutdown_rx = self - .work_forwarder_shutdown_rx - .take() - .expect("Invariant: OutboundMessagePool was initialized without a shutdown_rx"); - - thread::spawn(move || loop { - match shutdown_rx.recv_timeout(Duration::from_millis(1)) { - Ok(_) => break, - Err(RecvTimeoutError::Timeout) => {}, - Err(RecvTimeoutError::Disconnected) => { - warn!( - target: LOG_TARGET, - "Work forwarder shutdown signal disconnected unexpectedly" - ); - break; - }, - } - - match message_rx.recv_timeout(Duration::from_millis(1000)) { - Ok(msg) => worker.push(msg), - Err(RecvTimeoutError::Timeout) => {}, - Err(RecvTimeoutError::Disconnected) => { - warn!(target: LOG_TARGET, "Work forwarder sender disconnected unexpectedly"); - break; - }, - } - }) - } - - /// Start the Outbound Message Pool. - /// - /// This starts the configured number of workers and a worker forwarder. The forwarder forwards - /// work to the worker queue and the workers take work from the worker queue. - pub fn start(&mut self) -> Result<(), OutboundMessagePoolError> { - info!(target: LOG_TARGET, "Starting outbound message pool"); - - let worker = Worker::new_fifo(); - - info!(target: LOG_TARGET, "Starting {} OMP workers", self.config.num_workers); - for _ in 0..self.config.num_workers { - self.start_message_worker(worker.stealer(), self.retry_queue.clone())?; - } - - info!(target: LOG_TARGET, "Starting OMP work producer"); - let handle = self.start_work_forwarder(worker); - self.work_forwarder_handle = Some(handle); - - Ok(()) - } - - fn start_message_worker( - &mut self, - stealer: Stealer, - retry_queue: RetryQueue, - ) -> Result<(), OutboundMessagePoolError> - { - let (worker_thread_handle, worker_shutdown_signal) = MessagePoolWorker::start( - self.config, - stealer, - retry_queue, - self.peer_manager.clone(), - self.connection_manager.clone(), - )?; - - self.worker_thread_handles.push(worker_thread_handle); - self.worker_shutdown_signals.push(worker_shutdown_signal); - - Ok(()) - } - - /// Tell the underlying dealer thread, nessage retry service and workers to shut down - pub fn shutdown(self) -> Result<(), OutboundError> { - // Send Shutdown control message - for worker_shutdown_signal in self.worker_shutdown_signals { - worker_shutdown_signal.send(()).map_err(|e| { - OutboundError::ShutdownSignalSendError(format!( - "Failed to send shutdown signal to outbound workers: {:?}", - e - )) - })?; - } - - self.retry_queue.clear(); - // Send shutdown signal to message retry queue if it has been started - self.work_forwarder_shutdown_tx.send(()).map_err(|e| { - OutboundError::ShutdownSignalSendError(format!("Failed to send shutdown signal to work forwarder: {:?}", e)) - })?; - - if let Some(handle) = self.work_forwarder_handle { - handle - .timeout_join(WORK_FORWARDER_THREAD_JOIN_TIMEOUT) - .map_err(OutboundError::ThreadJoinError)?; - } - - // Join worker threads - for worker_thread_handle in self.worker_thread_handles { - worker_thread_handle - .timeout_join(MSG_POOL_WORKER_THREAD_JOIN_TIMEOUT) - .map_err(OutboundError::ThreadJoinError)?; - } - - Ok(()) - } -} - -#[cfg(test)] -mod test { - use crate::{ - connection::{InprocAddress, NetAddress, ZmqContext}, - connection_manager::{ConnectionManager, PeerConnectionConfig}, - outbound_message_service::{ - outbound_message_pool::{OutboundMessagePoolConfig, RetryQueue}, - OutboundMessagePool, - }, - peer_manager::{peer::PeerFlags, NodeId, NodeIdentity, Peer, PeerManager}, - }; - use crossbeam_deque::Worker; - use std::{sync::Arc, time::Duration}; - use tari_crypto::{keys::PublicKey, ristretto::RistrettoPublicKey}; - use tari_storage::HMapDatabase; - use tari_utilities::thread_join::ThreadJoinWithTimeout; - - fn make_peer_connection_config(consumer_address: InprocAddress) -> PeerConnectionConfig { - PeerConnectionConfig { - peer_connection_establish_timeout: Duration::from_millis(10), - max_message_size: 1024, - host: "127.0.0.1".parse().unwrap(), - max_connect_retries: 1, - max_connections: 10, - message_sink_address: consumer_address, - socks_proxy_address: None, - } - } - - fn outbound_message_pool_setup( - context: &ZmqContext, - ) -> (Arc, Arc, Arc) { - let node_identity = Arc::new(NodeIdentity::random_for_test(None)); - let peer_manager = Arc::new(PeerManager::new(HMapDatabase::new()).unwrap()); - - // Connection Manager - let connection_manager = Arc::new(ConnectionManager::new( - context.clone(), - node_identity.clone(), - peer_manager.clone(), - make_peer_connection_config(InprocAddress::random()), - )); - - (peer_manager, connection_manager, node_identity) - } - - #[test] - fn new() { - let context = ZmqContext::new(); - let (peer_manager, connection_manager, _) = outbound_message_pool_setup(&context); - let omp_config = OutboundMessagePoolConfig::default(); - let omp = OutboundMessagePool::new(omp_config.clone(), peer_manager.clone(), connection_manager.clone()); - assert_eq!(omp.worker_thread_handles.len(), 0); - assert_eq!(omp.worker_shutdown_signals.len(), 0); - assert!(omp.work_forwarder_shutdown_rx.is_some()); - assert!(omp.work_forwarder_handle.is_none()); - } - - #[test] - fn work_forwarder_shutdown() { - let context = ZmqContext::new(); - let (peer_manager, connection_manager, _) = outbound_message_pool_setup(&context); - let omp_config = OutboundMessagePoolConfig::default(); - let mut omp = OutboundMessagePool::new(omp_config.clone(), peer_manager.clone(), connection_manager.clone()); - - let worker = Worker::new_fifo(); - let handle = omp.start_work_forwarder(worker); - - omp.shutdown().unwrap(); - handle.timeout_join(Duration::from_millis(3000)).unwrap(); - } - - #[test] - fn start_message_worker() { - let context = ZmqContext::new(); - let (peer_manager, connection_manager, _) = outbound_message_pool_setup(&context); - let omp_config = OutboundMessagePoolConfig::default(); - let mut omp = OutboundMessagePool::new(omp_config.clone(), peer_manager.clone(), connection_manager.clone()); - assert_eq!(omp.worker_shutdown_signals.len(), 0); - assert_eq!(omp.worker_thread_handles.len(), 0); - - let worker = Worker::new_fifo(); - let retry_queue = RetryQueue::new(); - - omp.start_message_worker(worker.stealer(), retry_queue).unwrap(); - - assert_eq!(omp.worker_shutdown_signals.len(), 1); - assert_eq!(omp.worker_thread_handles.len(), 1); - - omp.shutdown().unwrap(); - } - - #[test] - fn clean_shutdown() { - let context = ZmqContext::new(); - let (peer_manager, connection_manager, _) = outbound_message_pool_setup(&context); - - // Add random peer - let mut rng = rand::OsRng::new().unwrap(); - let (_dest_sk, pk) = RistrettoPublicKey::random_keypair(&mut rng); - let node_id = NodeId::from_key(&pk.clone()).unwrap(); - let net_addresses = "127.0.0.1:45325".parse::().unwrap().into(); - let dest_peer = Peer::new(pk.clone(), node_id, net_addresses, PeerFlags::default()); - peer_manager.add_peer(dest_peer.clone()).unwrap(); - - let omp_config = OutboundMessagePoolConfig::default(); - let mut omp = OutboundMessagePool::new(omp_config.clone(), peer_manager.clone(), connection_manager.clone()); - - omp.start().unwrap(); - - omp.shutdown().unwrap(); - } -} diff --git a/comms/src/outbound_message_service/outbound_message_pool/retry_queue.rs b/comms/src/outbound_message_service/outbound_message_pool/retry_queue.rs deleted file mode 100644 index 4688a73337..0000000000 --- a/comms/src/outbound_message_service/outbound_message_pool/retry_queue.rs +++ /dev/null @@ -1,702 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use std::{ - cmp::{self, Ordering}, - collections::{BinaryHeap, HashMap, VecDeque}, - hash::Hash, - sync::{Arc, RwLock}, - time::{Duration, Instant}, -}; - -#[derive(Eq, PartialEq)] -struct ScheduledItem { - item: T, - scheduled_at: Instant, -} - -impl ScheduledItem { - pub fn new(item: T, scheduled_at: Instant) -> Self { - Self { item, scheduled_at } - } - - pub fn is_scheduled(&self) -> bool { - self.scheduled_at <= Instant::now() - } -} - -impl PartialOrd> for ScheduledItem -where ScheduledItem: Ord -{ - /// Orders from least to most time remaining from being scheduled - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for ScheduledItem -where ScheduledItem: Eq -{ - /// Orders from least to most time remaining from being scheduled - fn cmp(&self, other: &Self) -> Ordering { - let now = Instant::now(); - let sub_self = self.scheduled_at.checked_duration_since(now); - let sub_other = other.scheduled_at.checked_duration_since(now); - - match (sub_self, sub_other) { - // Both are "scheduled" - (None, None) => Ordering::Equal, - // self is "scheduled", other is not - (None, Some(_)) => Ordering::Greater, - // other is "scheduled", self is not - (Some(_), None) => Ordering::Less, - // Both aren't "scheduled", whichever one is closer to being scheduled is greater - (Some(a), Some(b)) => b.cmp(&a), - } - } -} - -/// # RetryBucket -/// -/// Represents an ordered grouping of work items, with methods which can be used to track attempts. -#[derive(Default)] -pub struct RetryBucket { - attempts: u32, - contents: VecDeque, -} - -impl RetryBucket { - /// Create a new RetryBucket with the given contents - pub fn new(contents: Vec) -> Self { - Self { - contents: contents.into(), - attempts: 1, - } - } - - /// Increment the number of attempts for this bucket - pub fn incr_attempts(&mut self) { - self.attempts += 1; - } - - /// Return the number of attempts for this bucket - pub fn attempts(&self) -> u32 { - self.attempts - } - - /// Pop an item from the front - pub fn pop_front(&mut self) -> Option { - self.contents.pop_front() - } - - /// Push an item onto the front - pub fn push_front(&mut self, item: T) { - self.contents.push_front(item) - } - - /// Push an item onto the back - pub fn push_back(&mut self, item: T) { - self.contents.push_back(item) - } - - /// Pour the contents of the given bucket into this one - pub fn pour_from(&mut self, bucket: &mut Self) { - bucket.contents.extend(self.contents.drain(..)) - } - - /// Get the future Instant in time that this bucket should be scheduled. Exponentially backing off - /// according to the number of attempts. - pub fn schedule(&self) -> Instant { - Instant::now() + Duration::from_secs(self.exponential_backoff_offset()) - } - - fn exponential_backoff_offset(&self) -> u64 { - if self.attempts == 0 { - return 0; - } - let secs = 0.5 * (f32::powf(2.0, self.attempts as f32) - 1.0); - cmp::max(2, secs.ceil() as u64) - } -} - -/// A lease represents an item which is either available for lease or already taken. -/// A taken lease may have items added. -/// -/// This is used by the RetryQueue to contain the RetryBuckets and keep track of whether -/// the buckets are available for lease or not. -enum Lease { - Available(T), - Taken(Option), -} - -impl Lease { - #[cfg(test)] - fn is_available(&self) -> bool { - match self { - Lease::Available(_) => true, - _ => false, - } - } - - #[cfg(test)] - fn is_taken(&self) -> bool { - match self { - Lease::Taken(_) => true, - _ => false, - } - } - - fn take(self) -> Option { - match self { - Lease::Available(t) => Some(t), - Lease::Taken(t) => t, - } - } - - fn borrow_inner_mut(&mut self) -> Option<&mut T> { - match self { - Lease::Available(t) => Some(t), - Lease::Taken(Some(t)) => Some(t), - Lease::Taken(None) => None, - } - } - - fn borrow_inner(&self) -> Option<&T> { - match self { - Lease::Available(t) => Some(t), - Lease::Taken(Some(t)) => Some(t), - Lease::Taken(None) => None, - } - } -} - -/// # RetryQueue -/// -/// A thread-safe data structure which provides a small API for leasing buckets of items related by their key. -/// -/// A common use-case for this is bundling related items of work together for sequential processing, where processing -/// related work items doesn't make sense. -/// -/// Buckets of related work can be leased by workers and processed. While a bucket is leased, more related work may come -/// in. To handle this case, a "temporary" bucket may be filled while the original loaned bucket is being processed. -/// Once the worker is done processing the bucket, it may return the bucket (with remaining messages, if any) -/// and the bucket lease will be available again for other workers, albeit with the temporary buckets contents added. -/// Alternatively, the worker may want to remove the lease under that key (all messages processed). In this case, -/// a call to the `remove()` method returns the bucket (in this case the temporary one) for processing by the worker. -/// -/// Each bucket is scheduled in time according to it's number of attempts (exponential backoff). -#[derive(Clone)] -pub struct RetryQueue -where K: Hash + Eq -{ - inner: Arc>>, -} - -impl RetryQueue -where - K: Hash + Eq, - K: Clone, -{ - /// Create a new RetryQueue - pub fn new() -> Self { - Self { - inner: Arc::new(RwLock::new(Inner::new())), - } - } - - /// Returns `true` of the queue contains the key, otherwise `false` - pub fn contains(&self, key: &K) -> bool { - let inner = acquire_read_lock!(self.inner); - inner.contains(key) - } - - /// Returns `true` if the queue is empty, otherwise `false` - pub fn is_empty(&self) -> bool { - let inner = acquire_read_lock!(self.inner); - inner.is_empty() - } - - /// Lease the next bucket if one is scheduled and available - pub fn lease_next(&self) -> Option<(K, RetryBucket)> { - let mut inner = acquire_write_lock!(self.inner); - inner.take_next() - } - - /// Return the leased bucket and make it available. Any bucket items added during the lease - /// are added to the returned bucket. Returns true if the lease was returned, otherwise false - pub fn return_lease(&self, key: &K, bucket: RetryBucket) -> bool { - let mut inner = acquire_write_lock!(self.inner); - // It is an error to return a lease that doesn't exist. - inner.return_lease(key, bucket) - } - - /// Add an item to a bucket. If a bucket doesn't exist for the given key one is created, otherwise - /// the message is appended to the available or temporary bucket. - pub fn add_item(&self, key: K, item: T) { - let mut inner = acquire_write_lock!(self.inner); - inner.add_item(key, item); - } - - /// Remove a bucket from the queue. If an available/temporary bucket exists, it is returned. - pub fn remove(&self, key: &K) -> Option> { - let mut inner = acquire_write_lock!(self.inner); - inner.remove(key) - } - - /// Clear the contents of the queue - pub fn clear(&self) { - let mut inner = acquire_write_lock!(self.inner); - inner.clear(); - } -} - -struct Inner -where K: Hash + Eq -{ - buckets: HashMap>>, - roster: BinaryHeap>, -} - -impl Inner -where - K: Hash + Eq, - K: Clone, -{ - fn new() -> Self { - Self { - buckets: HashMap::new(), - roster: BinaryHeap::new(), - } - } - - fn contains(&self, key: &K) -> bool { - self.buckets.contains_key(key) - } - - fn is_empty(&self) -> bool { - self.buckets.is_empty() - } - - fn clear(&mut self) { - self.buckets.clear(); - self.roster.clear(); - } - - fn add_item(&mut self, key: K, item: T) { - match self.buckets.get_mut(&key) { - // Messages have already been added for this key, so we simply add it to - // the existing available bucket - Some(Lease::Available(bucket)) => { - bucket.push_back(item); - }, - // The bucket has been taken, in the meantime other messages for the node have - // arrived. We fill a "taken" bucket with these messages. It is the job of - // the worker that has taken the original bucket to process these messages - // if it is able. It does this by removing the `Taken` Booking which returns - // the rest of the messages. - Some(Lease::Taken(Some(bucket))) => { - bucket.push_back(item); - }, - Some(Lease::Taken(v @ None)) => { - // Set the bucket as the "taken" bucket - *v = Some(RetryBucket::new(vec![item])); - }, - None => { - // Create a new bucket and schedule on the roster - let bucket = RetryBucket::new(vec![item]); - self.add_schedule_for_key(key.clone(), bucket.schedule()); - self.buckets.insert(key, Lease::Available(bucket)); - }, - } - } - - fn return_lease(&mut self, key: &K, mut bucket: RetryBucket) -> bool { - let is_lease_returned = match self.buckets.get_mut(key) { - Some(lease @ Lease::Taken(Some(_))) => { - let b = lease.borrow_inner_mut().expect("RetryBucket in Lease must be Some"); - b.pour_from(&mut bucket); - *lease = Lease::Available(bucket); - true - }, - Some(lease @ Lease::Taken(None)) => { - *lease = Lease::Available(bucket); - true - }, - _ => false, - }; - - // If we were able to put the bucket back, we need to rescheduled - // the bucket - if is_lease_returned { - let schedule = self - .buckets - .get(key) - .expect("Lease must exist for given key") - .borrow_inner() - .expect("RetryBucket must exist in Lease") - .schedule(); - - self.add_schedule_for_key(key.clone(), schedule); - } - - is_lease_returned - } - - fn add_schedule_for_key(&mut self, key: K, schedule: Instant) { - self.roster.push(ScheduledItem::new(key, schedule)); - } - - fn remove(&mut self, key: &K) -> Option> { - self.buckets.remove(key).and_then(|lease| lease.take()) - } - - fn take_next(&mut self) -> Option<(K, RetryBucket)> { - match self.roster.peek().filter(|schedule| schedule.is_scheduled()) { - Some(_) => { - let schedule = self.roster.pop().expect("Could not pop ScheduledItem after peek"); - let lease = self - .buckets - .insert(schedule.item.clone(), Lease::Taken(None)) - .expect("Invariant: Item in schedule does not exist in buckets"); - lease.take().map(|bucket| (schedule.item, bucket)) - }, - None => None, - } - } -} - -#[cfg(test)] -mod test { - use super::*; - - //---------------------------------- ScheduledItem --------------------------------------------// - #[test] - fn scheduled_item_new() { - let item = 123; - let instant = Instant::now(); - let scheduled = ScheduledItem::new(item, instant); - assert_eq!(scheduled.item, item); - assert_eq!(scheduled.scheduled_at, instant); - } - - #[test] - fn scheduled_item_is_scheduled() { - let instant = Instant::now(); - let scheduled = ScheduledItem::new(123, instant); - assert!(scheduled.is_scheduled()) - } - - #[test] - fn scheduled_item_ord() { - let instant = Instant::now(); - let scheduled1 = ScheduledItem::new(123, instant); - let scheduled2 = ScheduledItem::new(123, instant + Duration::from_secs(10)); - let scheduled3 = ScheduledItem::new(123, instant + Duration::from_secs(20)); - assert!(scheduled1 > scheduled2); - assert!(scheduled2 > scheduled3); - assert!(scheduled1 > scheduled3); - assert!(scheduled2 < scheduled1); - assert!(scheduled3 < scheduled2); - assert!(scheduled3 < scheduled1); - } - - //---------------------------------- RetryBucket --------------------------------------------// - - #[test] - fn retry_bucket_new() { - let bucket = RetryBucket::new(vec!["A"]); - assert_eq!(bucket.contents, vec!["A"]); - assert_eq!(bucket.attempts, 1); - } - - #[test] - fn retry_bucket_default() { - let bucket = RetryBucket::<()>::default(); - assert_eq!(bucket.contents, vec![]); - assert_eq!(bucket.attempts, 0); - } - - #[test] - fn retry_bucket_incr_attempts() { - let mut bucket = RetryBucket::<()>::default(); - bucket.incr_attempts(); - assert_eq!(bucket.attempts, 1); - bucket.incr_attempts(); - assert_eq!(bucket.attempts, 2); - } - - #[test] - fn retry_bucket_pop_front() { - let mut bucket = RetryBucket::new(vec![123, 456]); - assert_eq!(bucket.pop_front(), Some(123)); - assert_eq!(bucket.pop_front(), Some(456)); - assert_eq!(bucket.pop_front(), None); - assert_eq!(bucket.contents.len(), 0); - } - - #[test] - fn retry_bucket_push_front() { - let mut bucket = RetryBucket::new(vec![]); - bucket.push_front(123); - bucket.push_front(456); - assert_eq!(bucket.contents[0], 456); - assert_eq!(bucket.contents[1], 123); - } - - #[test] - fn retry_bucket_push_back() { - let mut bucket = RetryBucket::new(vec![]); - bucket.push_back(123); - bucket.push_back(456); - assert_eq!(bucket.contents[0], 123); - assert_eq!(bucket.contents[1], 456); - } - - #[test] - fn retry_bucket_pour_into() { - let mut bucket1 = RetryBucket::new(vec![123]); - let mut bucket2 = RetryBucket::new(vec![456]); - bucket2.pour_from(&mut bucket1); - assert_eq!(bucket1.contents[0], 123); - assert_eq!(bucket1.contents[1], 456); - } - - #[test] - fn retry_bucket_exponential_backoff_offset() { - let mut bucket = RetryBucket::<()>::default(); - bucket.attempts = 0; - assert_eq!(bucket.exponential_backoff_offset(), 0); - bucket.attempts = 1; - assert_eq!(bucket.exponential_backoff_offset(), 2); - bucket.attempts = 2; - assert_eq!(bucket.exponential_backoff_offset(), 2); - bucket.attempts = 3; - assert_eq!(bucket.exponential_backoff_offset(), 4); - bucket.attempts = 4; - assert_eq!(bucket.exponential_backoff_offset(), 8); - bucket.attempts = 5; - assert_eq!(bucket.exponential_backoff_offset(), 16); - bucket.attempts = 6; - assert_eq!(bucket.exponential_backoff_offset(), 32); - bucket.attempts = 7; - assert_eq!(bucket.exponential_backoff_offset(), 64); - bucket.attempts = 8; - assert_eq!(bucket.exponential_backoff_offset(), 128); - bucket.attempts = 9; - assert_eq!(bucket.exponential_backoff_offset(), 256); - bucket.attempts = 10; - assert_eq!(bucket.exponential_backoff_offset(), 512); - } - - #[test] - fn retry_bucket_schedule() { - let mut bucket = RetryBucket::<()>::default(); - bucket.attempts = 1; - assert!(bucket.schedule() > Instant::now()); - assert!(bucket.schedule() < Instant::now() + Duration::from_secs(3)); - } - - //---------------------------------- Lease --------------------------------------------// - - #[test] - fn lease_take() { - // Non-copy type - let s = "A".to_string(); - let lease = Lease::Available(s.clone()); - assert_eq!(lease.take(), Some(s)); - - let s = "A".to_string(); - let lease = Lease::Taken(Some(s.clone())); - assert_eq!(lease.take(), Some(s)); - - let lease = Lease::<()>::Taken(None); - assert_eq!(lease.take(), None); - } - - #[test] - fn lease_borrow_inner() { - let lease = Lease::Available(123); - assert_eq!(lease.borrow_inner(), Some(&123)); - - let lease = Lease::Taken(Some(123)); - assert_eq!(lease.borrow_inner(), Some(&123)); - - let lease = Lease::<()>::Taken(None); - assert_eq!(lease.borrow_inner(), None); - } - - #[test] - fn lease_borrow_inner_mut() { - let mut lease = Lease::Available(123); - assert_eq!(lease.borrow_inner_mut(), Some(&mut 123)); - - let mut lease = Lease::Taken(Some(123)); - assert_eq!(lease.borrow_inner_mut(), Some(&mut 123)); - - let mut lease = Lease::<()>::Taken(None); - assert_eq!(lease.borrow_inner_mut(), None); - } - - //---------------------------------- RetryBucket --------------------------------------------// - - #[test] - fn retry_queue_new() { - let retry_queue = RetryQueue::<(), ()>::new(); - let lock = acquire_read_lock!(retry_queue.inner); - assert!(lock.buckets.is_empty()); - assert!(lock.roster.is_empty()); - } - - #[test] - fn retry_queue_clear() { - let retry_queue = RetryQueue::new(); - retry_queue.add_item(1, 1); - retry_queue.add_item(1, 1); - retry_queue.add_item(2, 2); - retry_queue.add_item(2, 2); - - assert!(!retry_queue.is_empty()); - retry_queue.clear(); - assert!(retry_queue.is_empty()); - assert!(!retry_queue.contains(&1)); - assert!(!retry_queue.contains(&2)); - } - - #[test] - fn retry_queue_contains() { - let retry_queue = RetryQueue::new(); - assert!(!retry_queue.contains(&1)); - retry_queue.add_item(1, 1); - assert!(retry_queue.contains(&1)); - } - - #[test] - fn retry_queue_is_empty() { - let retry_queue = RetryQueue::new(); - assert!(retry_queue.is_empty()); - retry_queue.add_item(1, 1); - assert!(!retry_queue.is_empty()); - } - - fn util_modify_schedule(retry_queue: &RetryQueue, scheduled_at: Instant) { - // Ensure that the item is scheduled - let mut lock = acquire_write_lock!(retry_queue.inner); - let mut item = lock.roster.pop().unwrap(); - item.scheduled_at = scheduled_at; - lock.roster.push(item); - } - - #[test] - fn retry_queue_lease_next() { - let retry_queue = RetryQueue::new(); - assert!(retry_queue.lease_next().is_none()); - retry_queue.add_item(1, 2); - - util_modify_schedule(&retry_queue, Instant::now() - Duration::from_secs(1)); - let (k, mut bucket) = retry_queue.lease_next().unwrap(); - // We've leased the bucket, but retry queue still knows about the key - assert!(retry_queue.contains(&k)); - - assert_eq!(k, 1); - assert_eq!(bucket.pop_front(), Some(2)); - assert!(retry_queue.lease_next().is_none()); - - { - let lock = acquire_read_lock!(retry_queue.inner); - assert!(lock.buckets.get(&1).unwrap().is_taken()); - } - - // Required to remove the Taken lease - removing the Taken lease when - // done is part of the contract of using RetryQueue - retry_queue.remove(&1); - - retry_queue.add_item(1, 2); - util_modify_schedule(&retry_queue, Instant::now() + Duration::from_secs(10)); - assert!(retry_queue.lease_next().is_none()); - } - - #[test] - fn retry_queue_add_item() { - let retry_queue = RetryQueue::new(); - retry_queue.add_item(1, 2); - retry_queue.add_item(1, 3); - retry_queue.add_item(2, 3); - - { - let lock = acquire_read_lock!(retry_queue.inner); - assert_eq!(lock.buckets.len(), 2); - assert!(lock.buckets.contains_key(&1)); - assert!(lock.buckets.contains_key(&2)); - assert_eq!(lock.buckets.get(&1).unwrap().borrow_inner().unwrap().contents.len(), 2); - assert_eq!(lock.buckets.get(&2).unwrap().borrow_inner().unwrap().contents.len(), 1); - } - } - - #[test] - fn retry_queue_return_lease() { - let retry_queue = RetryQueue::new(); - retry_queue.add_item(1, 2); - retry_queue.add_item(1, 3); - - util_modify_schedule(&retry_queue, Instant::now() - Duration::from_millis(1)); - - let (k, bucket) = retry_queue.lease_next().unwrap(); - { - let lock = acquire_read_lock!(retry_queue.inner); - assert!(lock.buckets.get(&1).unwrap().is_taken()); - } - - // Items added while lease is taken should be appended to the returning bucket - retry_queue.add_item(1, 4); - retry_queue.add_item(1, 5); - - assert!(retry_queue.return_lease(&k, bucket)); - { - let lock = acquire_read_lock!(retry_queue.inner); - assert!(lock.buckets.get(&1).unwrap().is_available()); - assert_eq!(lock.buckets.get(&1).unwrap().borrow_inner().unwrap().contents.len(), 4); - // Check that the resulting contents of the bucket include the items added during the lease - assert_eq!(lock.buckets.get(&1).unwrap().borrow_inner().unwrap().contents[0], 2); - assert_eq!(lock.buckets.get(&1).unwrap().borrow_inner().unwrap().contents[1], 3); - assert_eq!(lock.buckets.get(&1).unwrap().borrow_inner().unwrap().contents[2], 4); - assert_eq!(lock.buckets.get(&1).unwrap().borrow_inner().unwrap().contents[3], 5); - } - } - - #[test] - fn retry_queue_remove() { - let retry_queue = RetryQueue::new(); - retry_queue.add_item(1, 2); - - retry_queue.remove(&1).unwrap(); - assert!(retry_queue.remove(&1).is_none()); - assert!(!retry_queue.contains(&1)); - } - - #[test] - fn retry_queue_clone() { - let retry_queue = RetryQueue::new(); - retry_queue.add_item(1, 2); - let clone = retry_queue.clone(); - assert!(clone.contains(&1)); - } -} diff --git a/comms/src/outbound_message_service/outbound_message_pool/worker.rs b/comms/src/outbound_message_service/outbound_message_pool/worker.rs deleted file mode 100644 index bcbf02c3a9..0000000000 --- a/comms/src/outbound_message_service/outbound_message_pool/worker.rs +++ /dev/null @@ -1,354 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use super::{ - error::OutboundMessagePoolError, - pool::OutboundMessagePoolConfig, - retry_queue::{RetryBucket, RetryQueue}, - OutboundMessage, -}; -use crate::{ - connection::PeerConnection, - connection_manager::ConnectionManager, - peer_manager::{NodeId, Peer, PeerManager}, -}; -use crossbeam_channel::{self as channel, Receiver, RecvTimeoutError, Sender}; -use crossbeam_deque::{Steal, Stealer}; -use log::*; -use std::{sync::Arc, thread, time::Duration}; - -const LOG_TARGET: &str = "comms::outbound_message_service::pool::worker"; -/// Set the allocated stack size for each MessagePoolWorker thread -const THREAD_STACK_SIZE: usize = 256 * 1024; // 256kb - -/// This is an instance of a single Worker thread for the Outbound Message Pool -pub struct MessagePoolWorker { - config: OutboundMessagePoolConfig, - stealer: Stealer, - retry_queue: RetryQueue, - peer_manager: Arc, - connection_manager: Arc, - shutdown_receiver: Receiver<()>, -} - -impl MessagePoolWorker { - /// Start the MessagePoolWorker thread - pub fn start( - config: OutboundMessagePoolConfig, - stealer: Stealer, - retry_queue: RetryQueue, - peer_manager: Arc, - connection_manager: Arc, - ) -> Result<(thread::JoinHandle>, Sender<()>), OutboundMessagePoolError> - { - let (shutdown_signal, shutdown_receiver) = channel::bounded(1); - let mut worker = Self { - config, - stealer, - retry_queue, - peer_manager, - connection_manager, - shutdown_receiver, - }; - - let thread_handle = thread::Builder::new() - .name("message-pool-worker-thread".to_string()) - .stack_size(THREAD_STACK_SIZE) - .spawn(move || loop { - match worker.run() { - Ok(_) => break Ok(()), - Err(err @ OutboundMessagePoolError::WorkerShutdownSignalDisconnected) => { - error!(target: LOG_TARGET, "Message Pool worker exited: {:?}", err); - error!( - target: LOG_TARGET, - "The shutdown signal disconnected likely because the outbound message pool went out of \ - scope before shutdown was called. Exiting worker with an error." - ); - break Err(err); - }, - Err(err) => { - error!(target: LOG_TARGET, "Outbound Message Pool worker exited: {:?}", err); - warn!(target: LOG_TARGET, "Restarting outbound message worker after failure."); - // Sleep so that if this service continually restarts, we don't get high CPU usage - thread::sleep(Duration::from_secs(1)); - }, - } - }) - .map_err(|_| OutboundMessagePoolError::ThreadInitializationError)?; - - Ok((thread_handle, shutdown_signal)) - } - - /// Start MessagePoolWorker which will connect to the inbound message dealer, accept messages from the queue, - /// attempt to send them and if it cannot send then requeue the message - fn run(&mut self) -> Result<(), OutboundMessagePoolError> { - loop { - match self.shutdown_receiver.recv_timeout(Duration::from_millis(5)) { - // Shut down signal received - Ok(_) => break, - // Nothing received - Err(RecvTimeoutError::Timeout) => {}, - // Sender disconnected before sending the shutdown signal, this is an error - Err(RecvTimeoutError::Disconnected) => { - return Err(OutboundMessagePoolError::WorkerShutdownSignalDisconnected) - }, - } - - // Check for new messages - match self.stealer.steal() { - Steal::Success(msg) => { - if self.retry_queue.contains(msg.destination_node_id()) { - // The retry queue has scheduled messages for this node_id, - // so rather than trying to send now, we add this to the - // retry queue so that they can all be sent in a batch when scheduled - // to do so - let node_id = msg.destination_node_id().clone(); - debug!( - target: LOG_TARGET, - "Messages for this NodeId ({}) are scheduled for retry, adding this message to the retry \ - queue", - node_id - ); - self.retry_queue.add_item(node_id, msg); - } else { - // Attempt to send a single message - match self.attempt(&msg) { - Ok(peer) => debug!( - target: LOG_TARGET, - "Message successfully sent to NodeId={}", peer.node_id - ), - Err(err) => { - debug!( - target: LOG_TARGET, - "Failed to send message to peer ({}). {:?}. Sending to failed queue.", - msg.destination_node_id(), - err, - ); - // Add to failed message queue - self.retry_queue.add_item(msg.destination_node_id().clone(), msg); - }, - } - } - }, - Steal::Empty => { - // No incoming messages, maybe the retry_queue has some work - if self.retry_queue.is_empty() { - // Nothing in retry queue, sleep for a bit - thread::sleep(Duration::from_millis(100)); - } else { - // Work on a bucket in the retry queue - self.process_retry_queue(); - } - }, - Steal::Retry => {}, - } - } - - Ok(()) - } - - fn process_retry_queue(&self) { - if let Some((node_id, bucket)) = self.retry_queue.lease_next() { - self.try_send_bucket(&node_id, bucket); - } - } - - fn try_send_bucket(&self, node_id: &NodeId, mut bucket: RetryBucket) { - match self.attempt_batch(&node_id, &mut bucket) { - Ok(_) => { - // Bucket has been sent - now we must send any messages that could have been - // scheduled while the lease on the bucket was out - if let Some(left_over) = self.retry_queue.remove(&node_id) { - self.try_send_bucket(node_id, left_over); - } - }, - Err(err) => { - debug!( - target: LOG_TARGET, - "(Attempt {} of {}) Failed to send message to peer ({}). {:?}.", - bucket.attempts(), - self.config.max_retries, - node_id, - err, - ); - // Message bucket failed to send - put the bucket (with remaining messages) back on the - // retry queue. - bucket.incr_attempts(); - if bucket.attempts() > self.config.max_retries { - debug!( - target: LOG_TARGET, - "Unable to send message to NodeId {} after {} attempts. Message bucket discarded.", - node_id, - bucket.attempts(), - ); - self.retry_queue.remove(node_id); - return; - } - - if !self.retry_queue.return_lease(&node_id, bucket) { - // This should never happen - debug_assert!(false, "return_lease called for bucket which doesn't exist"); - warn!( - target: LOG_TARGET, - "Lease on message bucket for NodeId '{}' was not returned", node_id - ); - } - }, - } - } - - fn attempt_batch( - &self, - node_id: &NodeId, - bucket: &mut RetryBucket, - ) -> Result<(), OutboundMessagePoolError> - { - self.attempt_establish_connection(&node_id) - .and_then(|(_, conn)| self.send_batch(&conn, bucket)) - .or_else(|err| { - debug!( - target: LOG_TARGET, - "(Attempt {} of {}) Failed to send message to peer ({}). {:?}. Sending to failed queue.", - bucket.attempts(), - self.config.max_retries, - node_id, - err, - ); - Err(err) - }) - } - - fn attempt(&self, msg: &OutboundMessage) -> Result { - self.attempt_establish_connection(msg.destination_node_id()) - .and_then(|(peer, conn)| self.send_msg(&conn, msg).map(|_| peer)) - } - - /// Attempt to send a message to the NodeId specified in the message. If the the attempt is not successful then mark - /// the failed connection attempt and requeue the message for another attempt - fn attempt_establish_connection( - &self, - node_id: &NodeId, - ) -> Result<(Peer, Arc), OutboundMessagePoolError> - { - let peer = self - .peer_manager - .find_with_node_id(node_id) - .map_err(OutboundMessagePoolError::PeerManagerError)?; - - let peer_connection = self - .connection_manager - .establish_connection_to_peer(&peer) - .map_err(OutboundMessagePoolError::ConnectionManagerError)?; - - Ok((peer, peer_connection)) - } - - fn send_batch( - &self, - connection: &PeerConnection, - bucket: &mut RetryBucket, - ) -> Result<(), OutboundMessagePoolError> - { - while let Some(msg) = bucket.pop_front() { - self.send_msg(connection, &msg).or_else(|err| { - bucket.push_front(msg); - Err(err) - })?; - } - - Ok(()) - } - - fn send_msg( - &self, - peer_connection: &PeerConnection, - msg: &OutboundMessage, - ) -> Result<(), OutboundMessagePoolError> - { - // TODO: Cloning here due to PeerConnection requiring ownership. Investigate if PeerConnection - // could send an Arc to eliminate the need to clone bytes. - let frames = msg.message_frames().clone(); - - peer_connection - .send(frames) - .map_err(OutboundMessagePoolError::ConnectionError) - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::{ - connection::{InprocAddress, ZmqContext}, - connection_manager::PeerConnectionConfig, - peer_manager::NodeIdentity, - }; - use crossbeam_deque::Worker; - use tari_storage::HMapDatabase; - use tari_utilities::thread_join::ThreadJoinWithTimeout; - - fn make_peer_connection_config(consumer_address: InprocAddress) -> PeerConnectionConfig { - PeerConnectionConfig { - peer_connection_establish_timeout: Duration::from_millis(10), - max_message_size: 1024, - host: "127.0.0.1".parse().unwrap(), - max_connect_retries: 1, - max_connections: 10, - message_sink_address: consumer_address, - socks_proxy_address: None, - } - } - - fn outbound_message_worker_setup() -> (Arc, Arc, Arc) { - let context = ZmqContext::new(); - let node_identity = Arc::new(NodeIdentity::random_for_test(None)); - let peer_manager = Arc::new(PeerManager::new(HMapDatabase::new()).unwrap()); - - // Connection Manager - let connection_manager = Arc::new(ConnectionManager::new( - context, - node_identity.clone(), - peer_manager.clone(), - make_peer_connection_config(InprocAddress::random()), - )); - - (peer_manager, connection_manager, node_identity) - } - - #[test] - fn start_shutdown() { - let (peer_manager, connection_manager, _) = outbound_message_worker_setup(); - let worker = Worker::new_fifo(); - let stealer = worker.stealer(); - let (handle, signal) = MessagePoolWorker::start( - OutboundMessagePoolConfig::default(), - stealer, - RetryQueue::new(), - peer_manager, - connection_manager, - ) - .unwrap(); - - signal.send(()).unwrap(); - handle.timeout_join(Duration::from_millis(3000)).unwrap(); - } -} diff --git a/comms/src/outbound_message_service/outbound_message_service.rs b/comms/src/outbound_message_service/outbound_message_service.rs deleted file mode 100644 index bc61f2480b..0000000000 --- a/comms/src/outbound_message_service/outbound_message_service.rs +++ /dev/null @@ -1,293 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use super::{outbound_message_pool::OutboundMessage, BroadcastStrategy, OutboundError}; -use crate::{ - message::{Frame, Message, MessageEnvelope, MessageError, MessageFlags, NodeDestination}, - peer_manager::{peer_manager::PeerManager, NodeIdentity}, -}; -use crossbeam_channel::Sender; -use std::{convert::TryInto, sync::Arc}; -use tari_utilities::message_format::MessageFormat; - -/// Handler functions use the OutboundMessageService to send messages to peers. The OutboundMessage service will receive -/// messages from handlers, apply a broadcasting strategy, encrypted and serialized the messages into OutboundMessages -/// and write them to the outbound message pool. -pub struct OutboundMessageService { - message_sender: Sender, - node_identity: Arc, - peer_manager: Arc, -} - -impl OutboundMessageService { - /// Constructs a new OutboundMessageService - pub fn new( - node_identity: Arc, - message_sender: Sender, - peer_manager: Arc, - ) -> Result - { - Ok(OutboundMessageService { - message_sender, - node_identity, - peer_manager, - }) - } - - /// Sends a domain-level message using the given BroadcastStrategy. - /// - /// *Arguments* - /// - /// - `broadcast_strategy`: [BroadcastStrategy] - /// - `flags`: MessageFlags - See [message module docs]. - /// - `message`: T - The message to send. - /// - /// [BroadcastStrategy]: ../broadcast_strategy/enum.BroadcastStrategy.html - /// [message module docs]: ../../message/index.html - pub fn send_message( - &self, - broadcast_strategy: BroadcastStrategy, - flags: MessageFlags, - message: T, - ) -> Result<(), OutboundError> - where - T: TryInto, - T: MessageFormat, - { - let msg = message.try_into().map_err(OutboundError::MessageSerializationError)?; - - let message_envelope_body = msg.to_binary().map_err(OutboundError::MessageFormatError)?; - self.send_raw(broadcast_strategy, flags, message_envelope_body) - } - - /// Handler functions use the send function to transmit a message to a peer or set of peers based on the - /// BroadcastStrategy - pub fn send_raw( - &self, - broadcast_strategy: BroadcastStrategy, - flags: MessageFlags, - message_envelope_body: Frame, - ) -> Result<(), OutboundError> - { - // Use the BroadcastStrategy to select appropriate peer(s) from PeerManager and then construct and send a - // personalised message to each selected peer - let selected_node_identities = self.peer_manager.get_broadcast_identities(broadcast_strategy)?; - - // Constructing a MessageEnvelope for each recipient - for dest_node_identity in selected_node_identities.into_iter() { - let message_envelope = MessageEnvelope::construct( - &self.node_identity, - dest_node_identity.public_key.clone(), - NodeDestination::NodeId(dest_node_identity.node_id.clone()), - message_envelope_body.clone(), - flags, - ) - .map_err(OutboundError::MessageSerializationError)?; - - let msg = OutboundMessage::new(dest_node_identity.node_id, message_envelope.into_frame_set()); - self.message_sender - .send(msg) - .map_err(|_| OutboundError::SyncSenderError)?; - } - Ok(()) - } - - /// Forwards a received message_envelope to other peers using the given BroadcastStrategy. - /// - /// *Arguments* - /// - /// - `broadcast_strategy`: [BroadcastStrategy] - /// - `message_envelope`: MessageEnvelope - The message to forward. - /// - /// [BroadcastStrategy]: ../broadcast_strategy/enum.BroadcastStrategy.html - pub fn forward_message( - &self, - broadcast_strategy: BroadcastStrategy, - message_envelope: MessageEnvelope, - ) -> Result<(), OutboundError> - { - // Use the BroadcastStrategy to select appropriate peer(s) from PeerManager and then forward the - // received message to each selected peer - let selected_node_identities = self.peer_manager.get_broadcast_identities(broadcast_strategy)?; - - // Modify MessageEnvelope for forwarding - let message_envelope = MessageEnvelope::forward_construct(&self.node_identity, message_envelope) - .map_err(OutboundError::MessageSerializationError)?; - let message_envelope_frames = message_envelope.into_frame_set(); - - // Constructing an OutboundMessage for each recipient - for dest_node_identity in selected_node_identities.into_iter() { - let msg = OutboundMessage::new(dest_node_identity.node_id, message_envelope_frames.clone()); - - self.message_sender - .send(msg) - .map_err(|_| OutboundError::SyncSenderError)?; - } - Ok(()) - } -} - -#[cfg(test)] -mod test { - use super::*; - - use crate::{ - connection::net_address::NetAddress, - message::Message, - peer_manager::{ - node_id::NodeId, - peer::{Peer, PeerFlags}, - }, - }; - use bitflags::_core::time::Duration; - use crossbeam_channel as channel; - use tari_crypto::{keys::PublicKey, ristretto::RistrettoPublicKey}; - use tari_storage::HMapDatabase; - - fn make_test_message_frame() -> Frame { - let message_header = "Test Message Header".as_bytes().to_vec(); - let message_body = "Test Message Body".as_bytes().to_vec(); - let message_envelope_body = Message::from_message_format(message_header, message_body).unwrap(); - message_envelope_body.to_binary().unwrap() - } - - #[test] - fn test_outbound_send() { - let mut rng = rand::OsRng::new().unwrap(); - let node_identity = Arc::new(NodeIdentity::random_for_test(None)); - - let (dest_sk, pk) = RistrettoPublicKey::random_keypair(&mut rng); - let node_id = NodeId::from_key(&pk).unwrap(); - let net_addresses = "127.0.0.1:55445".parse::().unwrap().into(); - let dest_peer = Peer::new(pk, node_id, net_addresses, PeerFlags::default()); - - // Setup OutboundMessageService and transmit a message to the destination - let peer_manager = Arc::new(PeerManager::new(HMapDatabase::new()).unwrap()); - peer_manager.add_peer(dest_peer.clone()).unwrap(); - - let (message_sender, message_receiver) = channel::unbounded(); - let outbound_message_service = - OutboundMessageService::new(node_identity.clone(), message_sender, peer_manager).unwrap(); - - // Construct and send OutboundMessage - let message_header = "Test Message Header".as_bytes().to_vec(); - let message_body = "Test Message Body".as_bytes().to_vec(); - let message = Message::from_message_format(message_header, message_body).unwrap(); - outbound_message_service - .send_raw( - BroadcastStrategy::DirectNodeId(dest_peer.node_id.clone()), - MessageFlags::ENCRYPTED, - message.to_binary().unwrap(), - ) - .unwrap(); - - let outbound_message = message_receiver.recv_timeout(Duration::from_millis(100)).unwrap(); - assert_eq!(outbound_message.destination_node_id(), &dest_peer.node_id); - let message_envelope: MessageEnvelope = outbound_message.message_frames().clone().try_into().unwrap(); - let message_envelope_header = message_envelope.deserialize_header().unwrap(); - assert_eq!(message_envelope_header.origin_source, node_identity.identity.public_key); - assert_eq!(message_envelope_header.peer_source, node_identity.identity.public_key); - assert_eq!( - message_envelope_header.dest, - NodeDestination::NodeId(dest_peer.node_id.clone()) - ); - assert!(message_envelope_header - .verify_signatures(message_envelope.body_frame().clone()) - .unwrap()); - assert_eq!(message_envelope_header.flags, MessageFlags::ENCRYPTED); - let decrypted_message = message_envelope - .deserialize_encrypted_body(&dest_sk, &node_identity.identity.public_key) - .unwrap(); - assert_eq!(message, decrypted_message); - } - - #[test] - fn test_outbound_forward() { - let (message_sender, message_receiver) = channel::unbounded(); - let origin_node_identity = Arc::new(NodeIdentity::random_for_test(None)); - let peer_node_identity = Arc::new(NodeIdentity::random_for_test(None)); - let dest_node_identity = Arc::new(NodeIdentity::random_for_test(None)); - - let net_addresses = "127.0.0.1:55445".parse::().unwrap().into(); - let dest_peer = Peer::new( - dest_node_identity.identity.public_key.clone(), - dest_node_identity.identity.node_id.clone(), - net_addresses, - PeerFlags::default(), - ); - - // Setup OutboundMessageService and transmit a message to the destination - let peer_manager = Arc::new(PeerManager::new(HMapDatabase::new()).unwrap()); - peer_manager.add_peer(dest_peer.clone()).unwrap(); - - let outbound_message_service = - OutboundMessageService::new(peer_node_identity.clone(), message_sender, peer_manager).unwrap(); - - // Origin constructs MessageEnvelope - let desire_message_body = make_test_message_frame(); - let origin_envelope = MessageEnvelope::construct( - &origin_node_identity, - dest_node_identity.identity.public_key.clone(), - NodeDestination::Unknown, - desire_message_body.clone(), - MessageFlags::ENCRYPTED, - ) - .unwrap(); - - // Peer receives MessageEnvelope from Origin, modifies and forwards it - let peer_envelope = MessageEnvelope::forward_construct(&peer_node_identity, origin_envelope).unwrap(); - - outbound_message_service - .forward_message( - BroadcastStrategy::DirectNodeId(dest_node_identity.identity.node_id.clone()), - peer_envelope, - ) - .unwrap(); - - let outbound_message = message_receiver.recv_timeout(Duration::from_millis(100)).unwrap(); - assert_eq!(outbound_message.destination_node_id(), &dest_peer.node_id); - let message_envelope: MessageEnvelope = outbound_message.message_frames().clone().try_into().unwrap(); - let message_envelope_header = message_envelope.deserialize_header().unwrap(); - assert_eq!( - message_envelope_header.origin_source, - origin_node_identity.identity.public_key - ); - assert_eq!( - message_envelope_header.peer_source, - peer_node_identity.identity.public_key - ); - assert_eq!(message_envelope_header.dest, NodeDestination::Unknown); - assert!(message_envelope_header - .verify_signatures(message_envelope.body_frame().clone()) - .unwrap()); - assert_eq!(message_envelope_header.flags, MessageFlags::ENCRYPTED); - assert_eq!( - desire_message_body, - message_envelope - .decrypted_body_frame( - &dest_node_identity.secret_key, - &origin_node_identity.identity.public_key - ) - .unwrap() - ); - } -} diff --git a/comms/src/peer_manager/async_peer_manager.rs b/comms/src/peer_manager/async_peer_manager.rs new file mode 100644 index 0000000000..a0d714bc12 --- /dev/null +++ b/comms/src/peer_manager/async_peer_manager.rs @@ -0,0 +1,177 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + multiaddr::Multiaddr, + peer_manager::{ + connection_stats::PeerConnectionStats, + NodeId, + Peer, + PeerFeatures, + PeerFlags, + PeerId, + PeerManager, + PeerManagerError, + }, + types::CommsPublicKey, +}; +use std::sync::Arc; +use tokio::task; + +#[derive(Clone)] +pub struct AsyncPeerManager { + peer_manager: Arc, +} + +impl AsyncPeerManager { + pub fn new(peer_manager: Arc) -> Self { + Self { peer_manager } + } + + pub async fn add_peer(&self, peer: Peer) -> Result { + self.blocking_call(move |pm| pm.add_peer(peer)).await? + } + + /// Get a peer matching the given node ID + pub async fn find_by_node_id(&self, node_id: &NodeId) -> Result { + // TODO: When tokio block_in_place is more stable, this clone may not be necessary + let node_id = node_id.clone(); + self.blocking_call(move |pm| pm.find_by_node_id(&node_id)).await? + } + + pub async fn direct_identity_node_id(&self, node_id: &NodeId) -> Result, PeerManagerError> { + let node_id = node_id.clone(); + self.blocking_call(move |pm| pm.direct_identity_node_id(&node_id)) + .await? + } + + pub async fn find_by_public_key(&self, public_key: &CommsPublicKey) -> Result { + let public_key = public_key.clone(); + self.blocking_call(move |pm| pm.find_by_public_key(&public_key)).await? + } + + pub async fn exists(&self, public_key: &CommsPublicKey) -> Result { + let public_key = public_key.clone(); + self.blocking_call(move |pm| Ok(pm.exists(&public_key))).await? + } + + /// Set the last connection to this peer as a success + pub async fn set_last_connect_success(&self, node_id: &NodeId) -> Result<(), PeerManagerError> { + let node_id = node_id.clone(); + self.blocking_call(move |pm| pm.set_last_connect_success(&node_id)) + .await? + } + + /// Set the last connection to this peer as a failure + pub async fn set_last_connect_failed(&self, node_id: &NodeId) -> Result<(), PeerManagerError> { + let node_id = node_id.clone(); + self.blocking_call(move |pm| pm.set_last_connect_failed(&node_id)) + .await? + } + + pub fn inner(&self) -> Arc { + self.peer_manager.clone() + } + + /// Updates fields for a peer. Any fields set to Some(xx) will be updated. All None + /// fields will remain the same. + pub async fn update_peer( + &self, + public_key: &CommsPublicKey, + node_id: Option, + net_addresses: Option>, + flags: Option, + peer_features: Option, + connection_stats: Option, + ) -> Result<(), PeerManagerError> + { + // TODO: When tokio block_in_place is more stable, this clone may not be necessary + let public_key = public_key.clone(); + self.blocking_call(move |pm| { + pm.update_peer( + &public_key, + node_id, + net_addresses, + flags, + peer_features, + connection_stats, + ) + }) + .await? + } + + async fn blocking_call(&self, f: F) -> Result + where + F: FnOnce(Arc) -> R + Send + 'static, + R: Send + 'static, + { + let peer_manager = self.peer_manager.clone(); + task::spawn_blocking(move || f(peer_manager)) + .await + .map_err(|_| PeerManagerError::BlockingTaskSpawnError) + } +} + +impl From> for AsyncPeerManager { + fn from(peer_manager: Arc) -> Self { + Self { peer_manager } + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::test_utils::peer_manager::build_peer_manager; + use rand::rngs::OsRng; + use tari_crypto::keys::PublicKey; + + #[tokio_macros::test_basic] + async fn update_peer() { + let pm = AsyncPeerManager::from(build_peer_manager()); + let pk = CommsPublicKey::default(); + let node_id = NodeId::default(); + + pm.add_peer(Peer::new( + pk.clone(), + node_id.clone(), + Default::default(), + Default::default(), + Default::default(), + )) + .await + .unwrap(); + + pm.find_by_node_id(&node_id).await.unwrap(); + + let (_, pk2) = CommsPublicKey::random_keypair(&mut OsRng); + let node_id2 = NodeId::from_key(&pk2).unwrap(); + + pm.update_peer(&pk, Some(node_id2.clone()), None, None, None, None) + .await + .unwrap(); + + pm.find_by_node_id(&node_id2).await.unwrap(); + + let err = pm.find_by_node_id(&node_id).await.unwrap_err(); + assert!(err.is_peer_not_found()); + } +} diff --git a/comms/src/peer_manager/connection_stats.rs b/comms/src/peer_manager/connection_stats.rs new file mode 100644 index 0000000000..b0d0fca25a --- /dev/null +++ b/comms/src/peer_manager/connection_stats.rs @@ -0,0 +1,167 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use chrono::{NaiveDateTime, Utc}; +use serde::{Deserialize, Serialize}; +use std::{ + fmt, + fmt::{Display, Formatter}, + time::Duration, +}; + +#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq)] +pub struct PeerConnectionStats { + /// The last time a connection was successfully made or, None if a successful + /// connection has never been made. + pub last_connected_at: Option, + /// Represents the last connection attempt + pub last_connection_attempt: LastConnectionAttempt, +} + +impl PeerConnectionStats { + pub fn new() -> Self { + Default::default() + } + + /// Sets the last connection as a success. `has_connected()` will return true from here on. + pub fn set_connection_success(&mut self) { + self.last_connected_at = Some(Utc::now().naive_utc()); + self.last_connection_attempt = LastConnectionAttempt::Succeeded(Utc::now().naive_utc()); + } + + /// Sets the last connection as a failure + pub fn set_connection_failed(&mut self) { + self.last_connection_attempt = LastConnectionAttempt::Failed { + failed_at: Utc::now().naive_utc(), + num_attempts: self.failed_attempts() + 1, + }; + } + + /// Returns true if a successful connection has ever been recorded, otherwise false + pub fn has_ever_connected(&self) -> bool { + self.last_connected_at.is_some() + } + + /// Returns the number of failed attempts. 0 is returned if the `last_connection_attempt` is not `Failed` + pub fn failed_attempts(&self) -> usize { + match self.last_connection_attempt { + LastConnectionAttempt::Failed { num_attempts, .. } => num_attempts, + _ => 0, + } + } + + /// Returns the date time (UTC) since the last failed connection occurred. None is returned if the + /// `last_connection_attempt` is not `Failed` + pub fn last_failed_at(&self) -> Option<&NaiveDateTime> { + match &self.last_connection_attempt { + LastConnectionAttempt::Failed { failed_at, .. } => Some(failed_at), + _ => None, + } + } + + /// Returns the Duration since the last failed connection occurred. None is returned if the + /// `last_connection_attempt` is not `Failed` + pub fn time_since_last_failure(&self) -> Option { + self.last_failed_at() + .map(|failed_at| Utc::now().naive_utc() - *failed_at) + .map(convert_to_std_duration) + } +} + +/// Peer connection statistics +#[derive(Debug, Clone, Deserialize, Serialize, PartialOrd, PartialEq)] +pub enum LastConnectionAttempt { + /// This node has never attempted to connect to this peer + Never, + /// The last connection attempt was successful + Succeeded(NaiveDateTime), + /// The last connection attempt failed. + Failed { + /// Timestamp of the last failed attempt + failed_at: NaiveDateTime, + /// Number of failed attempts in a row + num_attempts: usize, + }, +} + +/// Convert `chrono::Duration` to `std::time::Duration` +fn convert_to_std_duration(old_duration: chrono::Duration) -> Duration { + Duration::from_millis(old_duration.num_milliseconds() as u64) +} + +impl Default for LastConnectionAttempt { + fn default() -> Self { + LastConnectionAttempt::Never + } +} + +impl Display for LastConnectionAttempt { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { + writeln!(f, "Last connection to peer")?; + use LastConnectionAttempt::*; + match self { + Never => write!(f, "Connection never attempted"), + Succeeded(succeeded_at) => write!(f, "Connection succeeded at {}", succeeded_at), + Failed { + failed_at, + num_attempts, + } => write!( + f, + "Connection failed at {} after {} attempt(s)", + failed_at, num_attempts + ), + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn peer_connection_stats() { + let state = PeerConnectionStats::new(); + assert!(state.last_failed_at().is_none()); + assert_eq!(state.failed_attempts(), 0); + assert!(state.time_since_last_failure().is_none()); + assert_eq!(state.has_ever_connected(), false); + + let mut state = PeerConnectionStats::new(); + state.set_connection_success(); + assert!(state.last_failed_at().is_none()); + assert_eq!(state.failed_attempts(), 0); + assert!(state.time_since_last_failure().is_none()); + assert_eq!(state.has_ever_connected(), true); + + let mut state = PeerConnectionStats::new(); + state.set_connection_failed(); + state.set_connection_failed(); + state.set_connection_failed(); + assert!(state.last_failed_at().is_some()); + assert_eq!(state.failed_attempts(), 3); + assert!(state.time_since_last_failure().unwrap().as_millis() < 100); + + assert_eq!(state.has_ever_connected(), false); + state.set_connection_success(); + assert_eq!(state.has_ever_connected(), true); + } +} diff --git a/comms/src/peer_manager/error.rs b/comms/src/peer_manager/error.rs index 327401fb1e..f5587ee009 100644 --- a/comms/src/peer_manager/error.rs +++ b/comms/src/peer_manager/error.rs @@ -20,34 +20,34 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE -use crate::{connection::NetAddressError, peer_manager::node_id::NodeIdError}; use derive_error::Error; +use std::sync::PoisonError; use tari_storage::KeyValStoreError; -use tari_utilities::message_format::MessageFormatError; -#[derive(Debug, Error)] +#[derive(Debug, Error, Clone)] pub enum PeerManagerError { - /// The requested peer does not exist or could not be located + /// The requested peer does not exist PeerNotFoundError, - /// The Thread Safety has been breached and the data access has become poisoned - PoisonedAccess, - // A problem occurred during the serialization of the keys or data - SerializationError(MessageFormatError), - /// A problem occurred converting the serialized data into peers - DeserializationError, - /// The index doesn't relate to an existing peer - IndexOutOfBounds, - /// The requested operation can only be performed if the PeerManager is linked to a DataStore - DatastoreUndefined, - /// An empty response was received from the Datastore - EmptyDatastoreQuery, - // A NetAddressError occurred - NetAddressError(NetAddressError), /// The peer has been banned BannedPeer, - /// Problem initializing the RNG - RngError, // An problem has been encountered with the database DatabaseError(KeyValStoreError), - NodeIdError(NodeIdError), + /// Failed to spawn blocking task + BlockingTaskSpawnError, +} + +impl PeerManagerError { + /// Returns true if this error indicates that the peer is not found, otherwise false + pub fn is_peer_not_found(&self) -> bool { + match self { + PeerManagerError::PeerNotFoundError => true, + _ => false, + } + } +} + +impl From> for PeerManagerError { + fn from(_: PoisonError) -> Self { + PeerManagerError::DatabaseError(KeyValStoreError::PoisonedAccess) + } } diff --git a/comms/src/peer_manager/mod.rs b/comms/src/peer_manager/mod.rs index 4f75e3dfdd..c085f9015d 100644 --- a/comms/src/peer_manager/mod.rs +++ b/comms/src/peer_manager/mod.rs @@ -30,8 +30,8 @@ //! If the Peer Manager is instantiated with a provided DataStore it will provide persistence via the provided DataStore //! implementation. //! -//! ```edition2018 -//! # use tari_comms::peer_manager::{NodeId, Peer, PeerManager, PeerFlags}; +//! ```norun +//! # use tari_comms::peer_manager::{NodeId, Peer, PeerManager, PeerFlags, PeerFeatures}; //! # use tari_comms::types::CommsPublicKey; //! # use tari_comms::connection::{NetAddress, NetAddressesWithStats}; //! # use tari_crypto::keys::PublicKey; @@ -40,11 +40,11 @@ //! # use std::sync::Arc; //! # use tari_storage::LMDBWrapper; //! -//! let mut rng = rand::OsRng::new().unwrap(); +//! let mut rng = rand::rngs::OsRng; //! let (dest_sk, pk) = CommsPublicKey::random_keypair(&mut rng); //! let node_id = NodeId::from_key(&pk).unwrap(); //! let net_addresses = NetAddressesWithStats::from("1.2.3.4:8000".parse::().unwrap()); -//! let peer = Peer::new(pk, node_id.clone(), net_addresses, PeerFlags::default()); +//! let peer = Peer::new(pk, node_id.clone(), net_addresses, PeerFlags::default(), PeerFeatures::COMMUNICATION_NODE); //! let database_name = "pm_peer_database"; //! let datastore = LMDBBuilder::new() //! .set_path("/tmp/") @@ -58,21 +58,37 @@ //! //! peer_manager.add_peer(peer.clone()); //! -//! let returned_peer = peer_manager.find_with_node_id(&node_id).unwrap(); +//! let returned_peer = peer_manager.find_by_node_id(&node_id).unwrap(); //! ``` -pub mod error; +mod connection_stats; + +mod error; +pub use error::PeerManagerError; + pub mod node_id; -pub mod node_identity; -pub mod peer; -pub mod peer_key; -pub mod peer_manager; -pub mod peer_storage; +pub use node_id::NodeId; + +mod node_identity; +pub use node_identity::{NodeIdentity, NodeIdentityError}; + +mod peer; +pub use peer::{Peer, PeerFlags}; + +mod peer_features; +pub use peer_features::PeerFeatures; + +mod async_peer_manager; +pub use async_peer_manager::AsyncPeerManager; + +mod peer_id; +pub use peer_id::PeerId; + +mod peer_manager; +pub use peer_manager::PeerManager; + +mod peer_query; +pub use peer_query::{PeerQuery, PeerQuerySortBy}; -pub use self::{ - error::PeerManagerError, - node_id::NodeId, - node_identity::{NodeIdentity, PeerNodeIdentity}, - peer::{Peer, PeerFlags}, - peer_manager::PeerManager, -}; +mod peer_storage; +pub use peer_storage::PeerStorage; diff --git a/comms/src/peer_manager/node_features.rs b/comms/src/peer_manager/node_features.rs new file mode 100644 index 0000000000..475f1819f8 --- /dev/null +++ b/comms/src/peer_manager/node_features.rs @@ -0,0 +1,118 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use serde::{Deserialize, Serialize}; +use serde_repr::{Deserialize_repr, Serialize_repr}; + +/// Represents network features offered by nodes +#[derive(Serialize_repr, Deserialize_repr, Clone, Debug, Eq, PartialEq)] +#[repr(u8)] +pub enum NodeFeature { + /// This node is able to propagate messages + MESSAGE_PROPAGATION = 0, +} + +/// A collection of `NodeFeature`s. +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +pub struct NodeFeatures(Vec); + +impl Default for NodeFeatures { + fn default() -> Self { + Self::empty() + } +} + +impl NodeFeatures { + pub fn empty() -> Self { + Self::new(Vec::new()) + } + + pub fn new(features: Vec) -> Self { + Self(dedup(features)) + } + + #[inline] + pub fn inner(&self) -> &Vec { + &self.0 + } + + #[inline] + pub fn inner_mut(&mut self) -> &mut Vec { + &mut self.0 + } + + pub fn count(&self) -> usize { + self.inner().len() + } + + pub fn add(&mut self, feature: NodeFeature) { + if !self.contains(&feature) { + self.inner_mut().push(feature); + } + } + + pub fn contains(&self, feature: &NodeFeature) -> bool { + self.inner().contains(feature) + } +} + +fn dedup(features: Vec) -> Vec { + features.into_iter().fold(Vec::new(), |mut acc, feature| { + if !acc.contains(&feature) { + acc.push(feature) + } + acc + }) +} + +impl From for NodeFeatures +where T: AsRef<[NodeFeature]> +{ + fn from(items: T) -> Self { + NodeFeatures::new(items.as_ref().to_vec()) + } +} + +#[cfg(test)] +mod test { + use super::*; + use tari_crypto::tari_utilities::message_format::MessageFormat; + + #[test] + fn serialize_u8() { + assert_eq!(NodeFeature::MESSAGE_PROPAGATION.to_binary().unwrap(), &[0]); + } + + #[test] + fn dedup() { + let features: NodeFeatures = [NodeFeature::MESSAGE_PROPAGATION, NodeFeature::MESSAGE_PROPAGATION].into(); + assert_eq!(features.count(), 1); + } + + #[test] + fn add() { + let mut features = NodeFeatures::default(); + assert_eq!(features.contains(&NodeFeature::MESSAGE_PROPAGATION), false); + features.add(NodeFeature::MESSAGE_PROPAGATION); + assert_eq!(features.contains(&NodeFeature::MESSAGE_PROPAGATION), true); + } +} diff --git a/comms/src/peer_manager/node_id.rs b/comms/src/peer_manager/node_id.rs index c36da9453a..fd17c6094f 100644 --- a/comms/src/peer_manager/node_id.rs +++ b/comms/src/peer_manager/node_id.rs @@ -20,7 +20,10 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{connection::peer_connection::ConnectionId, message::Frame}; +use blake2::{ + digest::{Input, VariableOutput}, + VarBlake2b, +}; use derive_error::Error; use serde::{de, Deserialize, Deserializer, Serialize}; use std::{ @@ -30,20 +33,20 @@ use std::{ hash::{Hash, Hasher}, marker::PhantomData, }; -use tari_utilities::{ +use tari_crypto::tari_utilities::{ hex::{to_hex, Hex}, ByteArray, ByteArrayError, - Hashable, }; -const NODE_ID_ARRAY_SIZE: usize = 32; +const NODE_ID_ARRAY_SIZE: usize = 13; // 104-bit as per RFC-0151 type NodeIdArray = [u8; NODE_ID_ARRAY_SIZE]; -#[derive(Debug, Error)] +#[derive(Debug, Error, Clone)] pub enum NodeIdError { IncorrectByteCount, OutOfBounds, + DigestError, } /// Hold the XOR distance calculated between two NodeId's. This is used for DHT-style routing. @@ -102,8 +105,12 @@ impl NodeId { } /// Derive a node id from a public key: node_id=hash(public_key) - pub fn from_key(key: &K) -> Result { - Self::try_from(key.hash().as_slice()) + pub fn from_key(key: &K) -> Result { + let bytes = key.as_bytes(); + let mut hasher = VarBlake2b::new(NODE_ID_ARRAY_SIZE).map_err(|_| NodeIdError::DigestError)?; + hasher.input(bytes); + let v = hasher.vec_result(); + Self::try_from(v.as_slice()) } /// Generate a node id from a base layer registration using the block hash and public key @@ -117,15 +124,15 @@ impl NodeId { } /// Find and return the indices of the K nearest neighbours from the provided node id list - pub fn closest_indices(&self, node_ids: &Vec, k: usize) -> Result, NodeIdError> { + pub fn closest_indices(&self, node_ids: &[NodeId], k: usize) -> Result, NodeIdError> { if k > node_ids.len() { return Err(NodeIdError::OutOfBounds); } let mut indices: Vec = Vec::with_capacity(node_ids.len()); let mut dists: Vec = Vec::with_capacity(node_ids.len()); - for i in 0..node_ids.len() { + for (i, node_id) in node_ids.iter().enumerate() { indices.push(i); - dists.push(self.distance(&node_ids[i])); + dists.push(self.distance(node_id)) } // Perform partial sort of elements only up to K elements let mut nearest_node_indices: Vec = Vec::with_capacity(k); @@ -142,8 +149,8 @@ impl NodeId { } /// Find and return the node ids of the K nearest neighbours from the provided node id list - pub fn closest(&self, node_ids: &Vec, k: usize) -> Result, NodeIdError> { - let nearest_node_indices = self.closest_indices(&node_ids, k)?; + pub fn closest(&self, node_ids: &[NodeId], k: usize) -> Result, NodeIdError> { + let nearest_node_indices = self.closest_indices(&node_ids.to_vec(), k)?; let mut nearest_node_ids: Vec = Vec::with_capacity(nearest_node_indices.len()); for nearest in nearest_node_indices { nearest_node_ids.push(node_ids[nearest].clone()); @@ -154,6 +161,10 @@ impl NodeId { pub fn into_inner(self) -> NodeIdArray { self.0 } + + pub fn short_str(&self) -> String { + to_hex(&self.0[..8]) + } } impl ByteArray for NodeId { @@ -183,12 +194,18 @@ impl PartialOrd for NodeId { } } +impl Ord for NodeId { + fn cmp(&self, other: &Self) -> Ordering { + self.0.cmp(&other.0) + } +} + impl TryFrom<&[u8]> for NodeId { type Error = NodeIdError; /// Construct a node id from 32 bytes fn try_from(elements: &[u8]) -> Result { - if elements.len() >= 32 { + if elements.len() >= NODE_ID_ARRAY_SIZE { let mut bytes = [0; NODE_ID_ARRAY_SIZE]; bytes.copy_from_slice(&elements[0..NODE_ID_ARRAY_SIZE]); Ok(NodeId(bytes)) @@ -198,15 +215,6 @@ impl TryFrom<&[u8]> for NodeId { } } -impl TryFrom for NodeId { - type Error = NodeIdError; - - /// Try construct a node id from a frame - fn try_from(frame: Frame) -> Result { - frame.as_slice().try_into() - } -} - impl Hash for NodeId { /// Require the implementation of the Hash trait for Hashmaps fn hash(&self, state: &mut H) { @@ -226,12 +234,6 @@ impl fmt::Display for NodeId { } } -impl From for ConnectionId { - fn from(node_id: NodeId) -> Self { - ConnectionId::new(node_id.into_inner().to_vec()) - } -} - pub fn deserialize_node_id_from_hex<'de, D>(des: D) -> Result where D: Deserializer<'de> { struct KeyStringVisitor { @@ -257,30 +259,23 @@ where D: Deserializer<'de> { mod test { use super::*; use crate::types::{CommsPublicKey, CommsSecretKey}; - use tari_crypto::keys::{PublicKey, SecretKey}; - use tari_utilities::byte_array::ByteArray; + use tari_crypto::{ + keys::{PublicKey, SecretKey}, + tari_utilities::byte_array::ByteArray, + }; #[test] fn display() { - let node_id = NodeId::try_from( - [ - 144, 28, 106, 112, 220, 197, 216, 119, 9, 217, 42, 77, 159, 211, 53, 207, 0, 157, 5, 55, 235, 247, 160, - 195, 240, 48, 146, 168, 119, 15, 241, 54, - ] - .as_bytes(), - ) - .unwrap(); + let node_id = + NodeId::try_from(&[144u8, 28, 106, 112, 220, 197, 216, 119, 9, 217, 42, 77, 159, 211, 53][..]).unwrap(); let result = format!("{}", node_id); - assert_eq!( - "901c6a70dcc5d87709d92a4d9fd335cf009d0537ebf7a0c3f03092a8770ff136", - result - ); + assert_eq!("901c6a70dcc5d87709d92a4d9f", result); } #[test] fn test_from_public_key() { - let mut rng = rand::OsRng::new().unwrap(); + let mut rng = rand::rngs::OsRng; let sk = CommsSecretKey::random(&mut rng); let pk = CommsPublicKey::from_secret_key(&sk); let node_id = NodeId::from_key(&pk).unwrap(); @@ -345,121 +340,31 @@ mod test { #[test] fn test_closest() { let mut node_ids: Vec = Vec::new(); - node_ids.push( - NodeId::try_from( - [ - 144, 28, 106, 112, 220, 197, 216, 119, 9, 217, 42, 77, 159, 211, 53, 207, 245, 157, 5, 55, 235, - 247, 160, 195, 240, 48, 146, 168, 119, 15, 241, 54, - ] - .as_bytes(), - ) - .unwrap(), - ); - node_ids.push( - NodeId::try_from( - [ - 75, 249, 102, 1, 2, 166, 155, 37, 22, 54, 84, 98, 56, 62, 242, 115, 238, 149, 12, 239, 231, 217, - 35, 168, 106, 203, 199, 168, 147, 32, 234, 38, - ] - .as_bytes(), - ) - .unwrap(), - ); - node_ids.push( - NodeId::try_from( - [ - 60, 32, 246, 39, 108, 201, 214, 91, 30, 230, 3, 126, 31, 46, 66, 203, 27, 51, 240, 177, 230, 22, - 118, 102, 201, 55, 211, 147, 229, 26, 116, 103, - ] - .as_bytes(), - ) - .unwrap(), - ); - node_ids.push( - NodeId::try_from( - [ - 134, 116, 78, 53, 246, 206, 200, 147, 126, 96, 54, 113, 67, 56, 173, 52, 150, 35, 250, 18, 29, 87, - 231, 228, 125, 49, 95, 53, 103, 250, 54, 214, - ] - .as_bytes(), - ) - .unwrap(), - ); - node_ids.push( - NodeId::try_from( - [ - 75, 146, 162, 130, 22, 63, 247, 182, 156, 103, 174, 32, 134, 97, 41, 240, 180, 116, 2, 142, 53, - 197, 209, 113, 191, 205, 45, 151, 93, 167, 43, 72, - ] - .as_bytes(), - ) - .unwrap(), - ); - node_ids.push( - NodeId::try_from( - [ - 186, 43, 62, 14, 60, 214, 9, 180, 145, 122, 55, 160, 83, 83, 45, 185, 219, 206, 226, 128, 5, 26, - 20, 0, 192, 121, 216, 178, 134, 212, 51, 131, - ] - .as_bytes(), - ) - .unwrap(), - ); - node_ids.push( - NodeId::try_from( - [ - 143, 189, 32, 210, 30, 231, 82, 5, 86, 85, 28, 82, 154, 127, 90, 98, 108, 106, 186, 179, 36, 194, - 246, 209, 17, 244, 126, 108, 104, 187, 204, 213, - ] - .as_bytes(), - ) - .unwrap(), - ); - node_ids.push( - NodeId::try_from( - [ - 155, 210, 214, 160, 153, 70, 172, 234, 177, 178, 62, 82, 166, 202, 71, 205, 139, 247, 170, 91, 234, - 197, 239, 27, 14, 238, 97, 8, 28, 169, 96, 169, - ] - .as_bytes(), - ) - .unwrap(), - ); - node_ids.push( - NodeId::try_from( - [ - 173, 218, 34, 188, 211, 173, 235, 82, 18, 159, 55, 47, 242, 24, 95, 60, 208, 53, 97, 51, 43, 71, - 149, 89, 123, 150, 162, 67, 240, 208, 67, 56, - ] - .as_bytes(), - ) - .unwrap(), - ); - - let node_id = NodeId::try_from( - [ - 169, 125, 200, 137, 210, 73, 241, 238, 25, 108, 8, 48, 66, 29, 2, 117, 1, 252, 36, 214, 252, 38, 207, - 113, 175, 126, 36, 202, 215, 125, 114, 131, - ] - .as_bytes(), - ) - .unwrap(); + node_ids.push(NodeId::try_from(&[144, 28, 106, 112, 220, 197, 216, 119, 9, 217, 42, 77, 159][..]).unwrap()); + node_ids.push(NodeId::try_from(&[75, 249, 102, 1, 2, 166, 155, 37, 22, 54, 84, 98, 56][..]).unwrap()); + node_ids.push(NodeId::try_from(&[60, 32, 246, 39, 108, 201, 214, 91, 30, 230, 3, 126, 31][..]).unwrap()); + node_ids.push(NodeId::try_from(&[134, 116, 78, 53, 246, 206, 200, 147, 126, 96, 54, 113, 67][..]).unwrap()); + node_ids.push(NodeId::try_from(&[75, 146, 162, 130, 22, 63, 247, 182, 156, 103, 174, 32, 134][..]).unwrap()); + node_ids.push(NodeId::try_from(&[186, 43, 62, 14, 60, 214, 9, 180, 145, 122, 55, 160, 83][..]).unwrap()); + node_ids.push(NodeId::try_from(&[143, 189, 32, 210, 30, 231, 82, 5, 86, 85, 28, 82, 154][..]).unwrap()); + node_ids.push(NodeId::try_from(&[155, 210, 214, 160, 153, 70, 172, 234, 177, 178, 62, 82, 166][..]).unwrap()); + node_ids.push(NodeId::try_from(&[173, 218, 34, 188, 211, 173, 235, 82, 18, 159, 55, 47, 242][..]).unwrap()); + + let node_id = NodeId::try_from(&[169, 125, 200, 137, 210, 73, 241, 238, 25, 108, 8, 48, 66][..]).unwrap(); + let k = 3; match node_id.closest(&node_ids, k) { Ok(knn_node_ids) => { println!(" KNN = {:?}", knn_node_ids); assert_eq!(knn_node_ids.len(), k); assert_eq!(knn_node_ids[0].0, [ - 173, 218, 34, 188, 211, 173, 235, 82, 18, 159, 55, 47, 242, 24, 95, 60, 208, 53, 97, 51, 43, 71, - 149, 89, 123, 150, 162, 67, 240, 208, 67, 56 + 173, 218, 34, 188, 211, 173, 235, 82, 18, 159, 55, 47, 242 ]); assert_eq!(knn_node_ids[1].0, [ - 186, 43, 62, 14, 60, 214, 9, 180, 145, 122, 55, 160, 83, 83, 45, 185, 219, 206, 226, 128, 5, 26, - 20, 0, 192, 121, 216, 178, 134, 212, 51, 131 + 186, 43, 62, 14, 60, 214, 9, 180, 145, 122, 55, 160, 83 ]); assert_eq!(knn_node_ids[2].0, [ - 143, 189, 32, 210, 30, 231, 82, 5, 86, 85, 28, 82, 154, 127, 90, 98, 108, 106, 186, 179, 36, 194, - 246, 209, 17, 244, 126, 108, 104, 187, 204, 213 + 143, 189, 32, 210, 30, 231, 82, 5, 86, 85, 28, 82, 154 ]); }, Err(_e) => assert!(false), diff --git a/comms/src/peer_manager/node_identity.rs b/comms/src/peer_manager/node_identity.rs index ac7ee284c4..8a8ff11781 100644 --- a/comms/src/peer_manager/node_identity.rs +++ b/comms/src/peer_manager/node_identity.rs @@ -22,20 +22,23 @@ use super::node_id::deserialize_node_id_from_hex; use crate::{ - connection::NetAddress, peer_manager::{ node_id::{NodeId, NodeIdError}, Peer, + PeerFeatures, PeerFlags, }, types::{CommsPublicKey, CommsSecretKey}, }; use derive_error::Error; +use multiaddr::Multiaddr; use rand::{CryptoRng, Rng}; use serde::{Deserialize, Serialize}; use std::sync::RwLock; -use tari_crypto::keys::{PublicKey, SecretKey}; -use tari_utilities::hex::serialize_to_hex; +use tari_crypto::{ + keys::{PublicKey, SecretKey}, + tari_utilities::hex::serialize_to_hex, +}; #[derive(Debug, Error)] pub enum NodeIdentityError { @@ -44,89 +47,120 @@ pub enum NodeIdentityError { PoisonedAccess, } -/// Identity of this node -/// # Fields -/// `identity`: The public identity fields for this node -/// -/// `secret_key`: The secret key corresponding to the public key of this node -/// -/// `control_service_address`: The NetAddress of the local node's Control port -#[derive(Serialize, Deserialize)] +/// The public and private identity of this node on the network +#[derive(Debug, Serialize, Deserialize)] pub struct NodeIdentity { - pub identity: PeerNodeIdentity, - pub secret_key: CommsSecretKey, - control_service_address: RwLock, + #[serde(serialize_with = "serialize_to_hex")] + #[serde(deserialize_with = "deserialize_node_id_from_hex")] + node_id: NodeId, + public_key: CommsPublicKey, + features: PeerFeatures, + secret_key: CommsSecretKey, + public_address: RwLock, } impl NodeIdentity { /// Create a new NodeIdentity from the provided key pair and control service address pub fn new( secret_key: CommsSecretKey, - public_key: CommsPublicKey, - control_service_address: NetAddress, + public_address: Multiaddr, + features: PeerFeatures, ) -> Result { + let public_key = CommsPublicKey::from_secret_key(&secret_key); let node_id = NodeId::from_key(&public_key).map_err(NodeIdentityError::NodeIdError)?; Ok(NodeIdentity { - identity: PeerNodeIdentity::new(node_id, public_key), + node_id, + public_key, + features, secret_key, - control_service_address: RwLock::new(control_service_address), + public_address: RwLock::new(public_address), }) } /// Generates a new random NodeIdentity for CommsPublicKey - pub fn random(rng: &mut R, control_service_address: NetAddress) -> Result - where R: CryptoRng + Rng { + pub fn random( + rng: &mut R, + public_address: Multiaddr, + features: PeerFeatures, + ) -> Result + where + R: CryptoRng + Rng, + { let secret_key = CommsSecretKey::random(rng); let public_key = CommsPublicKey::from_secret_key(&secret_key); let node_id = NodeId::from_key(&public_key).map_err(NodeIdentityError::NodeIdError)?; Ok(NodeIdentity { - identity: PeerNodeIdentity::new(node_id, public_key), + node_id, + public_key, + features, secret_key, - control_service_address: RwLock::new(control_service_address), + public_address: RwLock::new(public_address), }) } - /// Retrieve the control_service_address - pub fn control_service_address(&self) -> Result { - Ok(self - .control_service_address - .read() - .map_err(|_| NodeIdentityError::PoisonedAccess)? - .clone()) + /// Retrieve the publicly accessible address that peers must connect to establish a connection + pub fn public_address(&self) -> Multiaddr { + acquire_read_lock!(self.public_address).clone() } /// Modify the control_service_address - pub fn set_control_service_address(&self, control_service_address: NetAddress) -> Result<(), NodeIdentityError> { + pub fn set_public_address(&self, address: Multiaddr) -> Result<(), NodeIdentityError> { *self - .control_service_address + .public_address .write() - .map_err(|_| NodeIdentityError::PoisonedAccess)? = control_service_address; + .map_err(|_| NodeIdentityError::PoisonedAccess)? = address; Ok(()) } - /// This returns a random NodeIdentity for testing purposes. This function can panic. If a control_service_address + /// This returns a random NodeIdentity for testing purposes. This function can panic. If public_address /// is None, 127.0.0.1:9000 will be used (i.e. the caller doesn't care what the control_service_address is). #[cfg(test)] - pub fn random_for_test(control_service_address: Option) -> Self { - use rand::OsRng; + pub fn random_for_test(public_address: Option, features: PeerFeatures) -> Self { Self::random( - &mut OsRng::new().unwrap(), - control_service_address.or("127.0.0.1:9000".parse().ok()).unwrap(), + &mut rand::rngs::OsRng, + public_address.or("/ip4/127.0.0.1/tcp/9000".parse().ok()).unwrap(), + features, ) .unwrap() } + + #[inline] + pub fn node_id(&self) -> &NodeId { + &self.node_id + } + + #[inline] + pub fn public_key(&self) -> &CommsPublicKey { + &self.public_key + } + + #[inline] + pub fn secret_key(&self) -> &CommsSecretKey { + &self.secret_key + } + + #[inline] + pub fn features(&self) -> &PeerFeatures { + &self.features + } + + #[inline] + pub fn has_peer_features(&self, peer_features: PeerFeatures) -> bool { + self.features().contains(peer_features) + } } impl From for Peer { fn from(node_identity: NodeIdentity) -> Peer { Peer::new( - node_identity.identity.public_key, - node_identity.identity.node_id, - node_identity.control_service_address.read().unwrap().clone().into(), + node_identity.public_key, + node_identity.node_id, + node_identity.public_address.read().unwrap().clone().into(), PeerFlags::empty(), + node_identity.features, ) } } @@ -134,36 +168,11 @@ impl From for Peer { impl Clone for NodeIdentity { fn clone(&self) -> Self { Self { - identity: self.identity.clone(), + node_id: self.node_id.clone(), + public_key: self.public_key.clone(), + features: self.features, secret_key: self.secret_key.clone(), - control_service_address: RwLock::new(self.control_service_address().unwrap()), - } - } -} - -/// The PeerNodeIdentity is a container that stores the public identity (NodeId, Identification Public Key pair) of a -/// single node -#[derive(Eq, PartialEq, Debug, Serialize, Deserialize, Clone)] -pub struct PeerNodeIdentity { - #[serde(serialize_with = "serialize_to_hex")] - #[serde(deserialize_with = "deserialize_node_id_from_hex")] - pub node_id: NodeId, - pub public_key: CommsPublicKey, -} - -impl PeerNodeIdentity { - /// Construct a new identity for a node that contains its NodeId and identification key pair - pub fn new(node_id: NodeId, public_key: CommsPublicKey) -> PeerNodeIdentity { - PeerNodeIdentity { node_id, public_key } - } -} - -/// Construct a PeerNodeIdentity from a Peer -impl From for PeerNodeIdentity { - fn from(peer: Peer) -> Self { - Self { - public_key: peer.public_key, - node_id: peer.node_id, + public_address: RwLock::new(self.public_address()), } } } diff --git a/comms/src/peer_manager/peer.rs b/comms/src/peer_manager/peer.rs index ce2e59b9f4..040ad45e1c 100644 --- a/comms/src/peer_manager/peer.rs +++ b/comms/src/peer_manager/peer.rs @@ -21,20 +21,18 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use super::node_id::deserialize_node_id_from_hex; -use bitflags::*; -use chrono::prelude::*; -use serde::{Deserialize, Serialize}; -use tari_utilities::hex::serialize_to_hex; - use crate::{ - connection::{ - net_address::{net_addresses::NetAddressesWithStats, NetAddressWithStats}, - NetAddress, - }, - peer_manager::{node_id::NodeId, PeerManagerError}, + consts::PEER_OFFLINE_COOLDOWN_PERIOD, + net_address::MultiaddressesWithStats, + peer_manager::{connection_stats::PeerConnectionStats, node_id::NodeId, peer_id::PeerId, PeerFeatures}, types::CommsPublicKey, }; -// TODO reputation metric? +use bitflags::bitflags; +use chrono::{DateTime, NaiveDateTime, Utc}; +use multiaddr::Multiaddr; +use serde::{Deserialize, Serialize}; +use std::fmt::Display; +use tari_crypto::tari_utilities::hex::serialize_to_hex; bitflags! { #[derive(Default, Deserialize, Serialize)] @@ -43,17 +41,27 @@ bitflags! { } } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PeerIdentity { + pub node_id: NodeId, + pub public_key: CommsPublicKey, +} + /// A Peer represents a communication peer that is identified by a Public Key and NodeId. The Peer struct maintains a /// collection of the NetAddressesWithStats that this Peer can be reached by. The struct also maintains a set of flags /// describing the status of the Peer. #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] pub struct Peer { + id: Option, pub public_key: CommsPublicKey, #[serde(serialize_with = "serialize_to_hex")] #[serde(deserialize_with = "deserialize_node_id_from_hex")] pub node_id: NodeId, - pub addresses: NetAddressesWithStats, + pub addresses: MultiaddressesWithStats, pub flags: PeerFlags, + pub features: PeerFeatures, + pub connection_stats: PeerConnectionStats, + pub added_at: NaiveDateTime, } impl Peer { @@ -61,40 +69,64 @@ impl Peer { pub fn new( public_key: CommsPublicKey, node_id: NodeId, - addresses: NetAddressesWithStats, + addresses: MultiaddressesWithStats, flags: PeerFlags, + features: PeerFeatures, ) -> Peer { Peer { + id: None, public_key, node_id, addresses, flags, + features, + connection_stats: Default::default(), + added_at: Utc::now().naive_utc(), } } - /// Constructs a new peer - pub fn from_public_key_and_address( - public_key: CommsPublicKey, - net_address: NetAddress, - ) -> Result - { - let node_id = NodeId::from_key(&public_key)?; - let addresses = NetAddressesWithStats::new(vec![NetAddressWithStats::new(net_address.clone())]); + /// Returns the peers local id if this peer is persisted. + /// + /// This method panics if the peer does not have a PeerId, and therefore is not persisted. + /// If the caller should be sure that the peer is persisted before calling this function. + /// This can be checked by using `Peer::is_persisted`. + #[inline] + pub fn id(&self) -> PeerId { + self.id.expect("call to Peer::id() when peer is not persisted") + } - Ok(Peer { - public_key, - node_id, - addresses, - flags: PeerFlags::empty(), - }) + pub fn is_persisted(&self) -> bool { + self.id.is_some() + } + + /// Returns true if the last connection attempt has failed within the constant + /// [PEER_OFFLINE_COOLDOWN_PERIOD](crate::consts::PEER_OFFLINE_COOLDOWN_PERIOD). + pub fn is_offline(&self) -> bool { + self.connection_stats.failed_attempts() > 1 && + self.connection_stats + .time_since_last_failure() + .map(|last_failure| last_failure <= PEER_OFFLINE_COOLDOWN_PERIOD) + .unwrap_or(false) + } + + pub(super) fn set_id(&mut self, id: PeerId) { + debug_assert!(self.id.is_none()); + self.id = Some(id); + } + + #[cfg(test)] + pub(crate) fn set_id_for_test(&mut self, id: PeerId) { + self.id = Some(id); } pub fn update( &mut self, node_id: Option, - net_addresses: Option>, + net_addresses: Option>, flags: Option, + features: Option, + connection_stats: Option, ) { if let Some(new_node_id) = node_id { @@ -106,6 +138,12 @@ impl Peer { if let Some(new_flags) = flags { self.flags = new_flags }; + if let Some(new_features) = features { + self.features = new_features; + } + if let Some(connection_stats) = connection_stats { + self.connection_stats = connection_stats; + } } /// Provides that date time of the last successful interaction with the peer @@ -113,6 +151,11 @@ impl Peer { self.addresses.last_seen() } + /// Returns true if this peer has the given feature, otherwise false + pub fn has_features(&self, features: PeerFeatures) -> bool { + self.features.contains(features) + } + /// Returns the ban status of the peer pub fn is_banned(&self) -> bool { self.flags.contains(PeerFlags::BANNED) @@ -124,25 +167,35 @@ impl Peer { } } +/// Display Peer as `[peer_id]: ` +impl Display for Peer { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.write_str(&format!( + "[{}]: {}", + self.id.map(|v| v.to_string()).unwrap_or("NoID".to_string()), + self.public_key, + )) + } +} + #[cfg(test)] mod test { use super::*; - use crate::{ - connection::{net_address::net_addresses::NetAddressesWithStats, NetAddress}, - peer_manager::node_id::NodeId, - types::CommsPublicKey, - }; + use crate::{net_address::MultiaddressesWithStats, peer_manager::NodeId, types::CommsPublicKey}; use serde_json::Value; - use tari_crypto::{keys::PublicKey, ristretto::RistrettoPublicKey}; - use tari_utilities::{hex::Hex, message_format::MessageFormat}; + use tari_crypto::{ + keys::PublicKey, + ristretto::RistrettoPublicKey, + tari_utilities::{hex::Hex, message_format::MessageFormat}, + }; #[test] fn test_is_and_set_banned() { - let mut rng = rand::OsRng::new().unwrap(); + let mut rng = rand::rngs::OsRng; let (_sk, pk) = RistrettoPublicKey::random_keypair(&mut rng); let node_id = NodeId::from_key(&pk).unwrap(); - let addresses = NetAddressesWithStats::from("123.0.0.123:8000".parse::().unwrap()); - let mut peer: Peer = Peer::new(pk, node_id, addresses, PeerFlags::default()); + let addresses = MultiaddressesWithStats::from("/ip4/123.0.0.123/tcp/8000".parse::().unwrap()); + let mut peer: Peer = Peer::new(pk, node_id, addresses, PeerFlags::default(), PeerFeatures::empty()); assert_eq!(peer.is_banned(), false); peer.set_banned(true); assert_eq!(peer.is_banned(), true); @@ -152,26 +205,29 @@ mod test { #[test] fn test_update() { - let mut rng = rand::OsRng::new().unwrap(); + let mut rng = rand::rngs::OsRng; let (_sk, public_key1) = RistrettoPublicKey::random_keypair(&mut rng); let node_id = NodeId::from_key(&public_key1).unwrap(); - let net_address1 = "124.0.0.124:7000".parse::().unwrap(); + let net_address1 = "/ip4/124.0.0.124/tcp/7000".parse::().unwrap(); let mut peer: Peer = Peer::new( public_key1.clone(), node_id, - NetAddressesWithStats::from(net_address1.clone()), + MultiaddressesWithStats::from(net_address1.clone()), PeerFlags::default(), + PeerFeatures::empty(), ); let (_sk, public_key2) = RistrettoPublicKey::random_keypair(&mut rng); let node_id2 = NodeId::from_key(&public_key2).unwrap(); - let net_address2 = "125.0.0.125:8000".parse::().unwrap(); - let net_address3 = "126.0.0.126:9000".parse::().unwrap(); + let net_address2 = "/ip4/125.0.0.125/tcp/8000".parse::().unwrap(); + let net_address3 = "/ip4/126.0.0.126/tcp/9000".parse::().unwrap(); peer.update( Some(node_id2.clone()), Some(vec![net_address2.clone(), net_address3.clone()]), Some(PeerFlags::BANNED), + Some(PeerFeatures::MESSAGE_PROPAGATION), + Some(PeerConnectionStats::new()), ); assert_eq!(peer.public_key, public_key1); @@ -180,31 +236,33 @@ mod test { .addresses .addresses .iter() - .any(|net_address_with_stats| net_address_with_stats.net_address == net_address1)); + .any(|net_address_with_stats| net_address_with_stats.address == net_address1)); assert!(peer .addresses .addresses .iter() - .any(|net_address_with_stats| net_address_with_stats.net_address == net_address2)); + .any(|net_address_with_stats| net_address_with_stats.address == net_address2)); assert!(peer .addresses .addresses .iter() - .any(|net_address_with_stats| net_address_with_stats.net_address == net_address3)); + .any(|net_address_with_stats| net_address_with_stats.address == net_address3)); assert_eq!(peer.flags, PeerFlags::BANNED); + assert_eq!(peer.has_features(PeerFeatures::MESSAGE_PROPAGATION), true); } #[test] fn json_ser_der() { let expected_pk_hex = "02622ace8f7303a31cafc63f8fc48fdc16e1c8c8d234b2f0d6685282a9076031"; - let expected_nodeid_hex = "5f517508fdaeef0aeae7b577336731dfb6fe60bbbde363a5712100109b5d0f69"; + let expected_nodeid_hex = "c1a7552e5d9e9b257c4008b965"; let pk = CommsPublicKey::from_hex(expected_pk_hex).unwrap(); let node_id = NodeId::from_key(&pk).unwrap(); let peer = Peer::new( pk, node_id, - "127.0.0.1:9000".parse::().unwrap().into(), + "/ip4/127.0.0.1/tcp/9000".parse::().unwrap().into(), PeerFlags::empty(), + PeerFeatures::empty(), ); let json = peer.to_json().unwrap(); diff --git a/comms/src/peer_manager/peer_features.rs b/comms/src/peer_manager/peer_features.rs new file mode 100644 index 0000000000..402840a320 --- /dev/null +++ b/comms/src/peer_manager/peer_features.rs @@ -0,0 +1,42 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use bitflags::bitflags; +use serde::{Deserialize, Serialize}; + +bitflags! { + #[derive(Serialize, Deserialize)] + pub struct PeerFeatures: u64 { + const NONE = 0b0000_0000; + const MESSAGE_PROPAGATION = 0b0000_0001; + const DHT_STORE_FORWARD = 0b0000_0010; + + const COMMUNICATION_NODE = Self::MESSAGE_PROPAGATION.bits | Self::DHT_STORE_FORWARD.bits; + const COMMUNICATION_CLIENT = Self::NONE.bits; + } +} + +impl Default for PeerFeatures { + fn default() -> Self { + PeerFeatures::NONE + } +} diff --git a/comms/src/peer_manager/peer_key.rs b/comms/src/peer_manager/peer_id.rs similarity index 88% rename from comms/src/peer_manager/peer_key.rs rename to comms/src/peer_manager/peer_id.rs index f645f79f8d..2e633bddce 100644 --- a/comms/src/peer_manager/peer_key.rs +++ b/comms/src/peer_manager/peer_id.rs @@ -20,11 +20,11 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use rand::RngCore; +use rand::{rngs::OsRng, RngCore}; -pub type PeerKey = u64; +/// Represents a local peer id. This number is meaningless outside of this node. +pub type PeerId = u64; -pub fn generate_peer_key(rng: &mut R) -> PeerKey -where R: RngCore { - rng.next_u64() +pub fn generate_peer_key() -> PeerId { + OsRng.next_u64() } diff --git a/comms/src/peer_manager/peer_manager.rs b/comms/src/peer_manager/peer_manager.rs index 59778695ac..e9d290bb5c 100644 --- a/comms/src/peer_manager/peer_manager.rs +++ b/comms/src/peer_manager/peer_manager.rs @@ -21,14 +21,20 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::{ - connection::net_address::NetAddress, - outbound_message_service::broadcast_strategy::BroadcastStrategy, - peer_manager::{node_id::NodeId, node_identity::PeerNodeIdentity, peer::Peer, peer_storage::PeerStorage}, + peer_manager::{ + connection_stats::PeerConnectionStats, + node_id::NodeId, + peer::{Peer, PeerFlags}, + peer_id::PeerId, + peer_storage::PeerStorage, + PeerFeatures, + PeerManagerError, + PeerQuery, + }, types::{CommsDatabase, CommsPublicKey}, }; - -use crate::peer_manager::{peer::PeerFlags, PeerManagerError}; -use std::{sync::RwLock, time::Duration}; +use multiaddr::Multiaddr; +use std::sync::RwLock; /// The PeerManager consist of a routing table of previously discovered peers. /// It also provides functionality to add, find and delete peers. A subset of peers can also be requested from the @@ -41,130 +47,124 @@ impl PeerManager { /// Constructs a new empty PeerManager pub fn new(database: CommsDatabase) -> Result { Ok(Self { - peer_storage: RwLock::new(PeerStorage::new(database)?), + peer_storage: RwLock::new(PeerStorage::new_indexed(database)?), }) } /// Adds a peer to the routing table of the PeerManager if the peer does not already exist. When a peer already /// exist, the stored version will be replaced with the newly provided peer. - pub fn add_peer(&self, peer: Peer) -> Result<(), PeerManagerError> { - self.peer_storage - .write() - .map_err(|_| PeerManagerError::PoisonedAccess)? - .add_peer(peer) + pub fn add_peer(&self, peer: Peer) -> Result { + self.peer_storage.write()?.add_peer(peer) } + /// Updates fields for a peer. Any fields set to Some(xx) will be updated. All None + /// fields will remain the same. pub fn update_peer( &self, public_key: &CommsPublicKey, node_id: Option, - net_addresses: Option>, + net_addresses: Option>, flags: Option, + peer_features: Option, + connection_stats: Option, ) -> Result<(), PeerManagerError> { - self.peer_storage - .write() - .map_err(|_| PeerManagerError::PoisonedAccess)? - .update_peer(public_key, node_id, net_addresses, flags) + self.peer_storage.write()?.update_peer( + public_key, + node_id, + net_addresses, + flags, + peer_features, + connection_stats, + ) + } + + /// Set the last connection to this peer as a success + pub fn set_last_connect_success(&self, node_id: &NodeId) -> Result<(), PeerManagerError> { + let mut storage = self.peer_storage.write()?; + let mut peer = storage.find_by_node_id(node_id)?; + peer.connection_stats.set_connection_success(); + storage.update_peer(&peer.public_key, None, None, None, None, Some(peer.connection_stats)) + } + + /// Set the last connection to this peer as a failure + pub fn set_last_connect_failed(&self, node_id: &NodeId) -> Result<(), PeerManagerError> { + let mut storage = self.peer_storage.write()?; + let mut peer = storage.find_by_node_id(node_id)?; + peer.connection_stats.set_connection_failed(); + storage.update_peer(&peer.public_key, None, None, None, None, Some(peer.connection_stats)) } /// The peer with the specified public_key will be removed from the PeerManager pub fn delete_peer(&self, node_id: &NodeId) -> Result<(), PeerManagerError> { - self.peer_storage - .write() - .map_err(|_| PeerManagerError::PoisonedAccess)? - .delete_peer(node_id) + self.peer_storage.write()?.delete_peer(node_id) } - /// Find the peer with the provided NodeID - pub fn find_with_node_id(&self, node_id: &NodeId) -> Result { - self.peer_storage - .read() - .map_err(|_| PeerManagerError::PoisonedAccess)? - .find_with_node_id(node_id) + /// Performs the given [PeerQuery]. + /// + /// [PeerQuery]: crate::peer_manager::peer_query::PeerQuery + pub fn perform_query(&self, peer_query: PeerQuery) -> Result, PeerManagerError> { + self.peer_storage.read()?.perform_query(peer_query) } - /// Find the peer with the provided PublicKey - pub fn find_with_public_key(&self, public_key: &CommsPublicKey) -> Result { - self.peer_storage - .read() - .map_err(|_| PeerManagerError::PoisonedAccess)? - .find_with_public_key(public_key) + /// Find the peer with the provided NodeID + pub fn find_by_node_id(&self, node_id: &NodeId) -> Result { + self.peer_storage.read()?.find_by_node_id(node_id) } - /// Find the peer with the provided NetAddress - pub fn find_with_net_address(&self, net_address: &NetAddress) -> Result { - self.peer_storage - .read() - .map_err(|_| PeerManagerError::PoisonedAccess)? - .find_with_net_address(net_address) + /// Find the peer with the provided PublicKey + pub fn find_by_public_key(&self, public_key: &CommsPublicKey) -> Result { + self.peer_storage.read()?.find_by_public_key(public_key) } /// Check if a peer exist using the specified public_key - pub fn exists(&self, public_key: &CommsPublicKey) -> Result { - Ok(self - .peer_storage - .read() - .map_err(|_| PeerManagerError::PoisonedAccess)? - .exists(public_key)) + pub fn exists(&self, public_key: &CommsPublicKey) -> bool { + acquire_read_lock!(self.peer_storage).exists(public_key) } /// Check if a peer exist using the specified node_id - pub fn exists_node_id(&self, node_id: &NodeId) -> Result { - Ok(self - .peer_storage - .read() - .map_err(|_| PeerManagerError::PoisonedAccess)? - .exists_node_id(node_id)) + pub fn exists_node_id(&self, node_id: &NodeId) -> bool { + acquire_read_lock!(self.peer_storage).exists_node_id(node_id) + } + + /// Get a peer matching the given node ID + pub fn direct_identity_node_id(&self, node_id: &NodeId) -> Result, PeerManagerError> { + match self.peer_storage.read()?.direct_identity_node_id(&node_id) { + Ok(peer) => Ok(Some(peer)), + Err(PeerManagerError::PeerNotFoundError) | Err(PeerManagerError::BannedPeer) => Ok(None), + Err(err) => Err(err), + } + } + + /// Get a peer matching the given public key + pub fn direct_identity_public_key(&self, public_key: &CommsPublicKey) -> Result, PeerManagerError> { + match self.peer_storage.read()?.direct_identity_public_key(&public_key) { + Ok(peer) => Ok(Some(peer)), + Err(PeerManagerError::PeerNotFoundError) | Err(PeerManagerError::BannedPeer) => Ok(None), + Err(err) => Err(err), + } + } + + /// Fetch all peers (except banned ones) + pub fn flood_peers(&self) -> Result, PeerManagerError> { + self.peer_storage.read()?.flood_peers() } - /// Request a sub-set of peers based on the provided BroadcastStrategy - pub fn get_broadcast_identities( + /// Fetch n nearest neighbour Communication Nodes + pub fn closest_peers( &self, - broadcast_strategy: BroadcastStrategy, - ) -> Result, PeerManagerError> + node_id: &NodeId, + n: usize, + excluded_peers: &[CommsPublicKey], + ) -> Result, PeerManagerError> { - match broadcast_strategy { - BroadcastStrategy::DirectNodeId(node_id) => { - // Send to a particular peer matching the given node ID - self.peer_storage - .read() - .map_err(|_| PeerManagerError::PoisonedAccess)? - .direct_identity_node_id(&node_id) - }, - BroadcastStrategy::DirectPublicKey(public_key) => { - // Send to a particular peer matching the given node ID - self.peer_storage - .read() - .map_err(|_| PeerManagerError::PoisonedAccess)? - .direct_identity_public_key(&public_key) - }, - BroadcastStrategy::Flood => { - // Send to all known Communication Node peers - self.peer_storage - .read() - .map_err(|_| PeerManagerError::PoisonedAccess)? - .flood_identities() - }, - BroadcastStrategy::Closest(closest_request) => { - // Send to all n nearest neighbour Communication Nodes - self.peer_storage - .read() - .map_err(|_| PeerManagerError::PoisonedAccess)? - .closest_identities( - &closest_request.node_id, - closest_request.n, - &closest_request.excluded_peers, - ) - }, - BroadcastStrategy::Random(n) => { - // Send to a random set of peers of size n that are Communication Nodes - self.peer_storage - .write() - .map_err(|_| PeerManagerError::PoisonedAccess)? - .random_identities(n) - }, - } + self.peer_storage.read()?.closest_peers(node_id, n, excluded_peers) + } + + /// Fetch n random peers + pub fn random_peers(&self, n: usize) -> Result, PeerManagerError> { + // Send to a random set of peers of size n that are Communication Nodes + self.peer_storage.read()?.random_peers(n) } /// Check if a specific node_id is in the network region of the N nearest neighbours of the region specified by @@ -176,89 +176,17 @@ impl PeerManager { n: usize, ) -> Result { - self.peer_storage - .read() - .map_err(|_| PeerManagerError::PoisonedAccess)? - .in_network_region(node_id, region_node_id, n) + self.peer_storage.read()?.in_network_region(node_id, region_node_id, n) } /// Thread safe access to peer - Changes the ban flag bit of the peer pub fn set_banned(&self, node_id: &NodeId, ban_flag: bool) -> Result<(), PeerManagerError> { - self.peer_storage - .write() - .map_err(|_| PeerManagerError::PoisonedAccess)? - .set_banned(node_id, ban_flag) + self.peer_storage.write()?.set_banned(node_id, ban_flag) } /// Thread safe access to peer - Adds a new net address to the peer if it doesn't yet exist - pub fn add_net_address(&self, node_id: &NodeId, net_address: &NetAddress) -> Result<(), PeerManagerError> { - self.peer_storage - .write() - .map_err(|_| PeerManagerError::PoisonedAccess)? - .add_net_address(node_id, net_address) - } - - /// Thread safe access to peer - Finds and returns the highest priority net address until all connection attempts - /// for each net address have been reached - pub fn get_best_net_address(&self, node_id: &NodeId) -> Result { - self.peer_storage - .write() - .map_err(|_| PeerManagerError::PoisonedAccess)? - .get_best_net_address(node_id) - } - - /// Thread safe access to peer - The average connection latency of the provided net address will be updated to - /// include the current measured latency sample - pub fn update_latency( - &self, - net_address: &NetAddress, - latency_measurement: Duration, - ) -> Result<(), PeerManagerError> - { - self.peer_storage - .write() - .map_err(|_| PeerManagerError::PoisonedAccess)? - .update_latency(net_address, latency_measurement) - } - - /// Thread safe access to peer - Mark that a message was received from the specified net address - pub fn mark_message_received(&self, net_address: &NetAddress) -> Result<(), PeerManagerError> { - self.peer_storage - .write() - .map_err(|_| PeerManagerError::PoisonedAccess)? - .mark_message_received(net_address) - } - - /// Thread safe access to peer - Mark that a rejected message was received from the specified net address - pub fn mark_message_rejected(&self, net_address: &NetAddress) -> Result<(), PeerManagerError> { - self.peer_storage - .write() - .map_err(|_| PeerManagerError::PoisonedAccess)? - .mark_message_rejected(net_address) - } - - /// Thread safe access to peer - Mark that a successful connection was established with the specified net address - pub fn mark_successful_connection_attempt(&self, net_address: &NetAddress) -> Result<(), PeerManagerError> { - self.peer_storage - .write() - .map_err(|_| PeerManagerError::PoisonedAccess)? - .mark_successful_connection_attempt(net_address) - } - - /// Thread safe access to peer - Mark that a connection could not be established with the specified net address - pub fn mark_failed_connection_attempt(&self, net_address: &NetAddress) -> Result<(), PeerManagerError> { - self.peer_storage - .write() - .map_err(|_| PeerManagerError::PoisonedAccess)? - .mark_failed_connection_attempt(net_address) - } - - /// Thread safe access to peer - Reset all connection attempts on all net addresses for peer - pub fn reset_connection_attempts(&self, node_id: &NodeId) -> Result<(), PeerManagerError> { - self.peer_storage - .write() - .map_err(|_| PeerManagerError::PoisonedAccess)? - .reset_connection_attempts(node_id) + pub fn add_net_address(&self, node_id: &NodeId, net_address: &Multiaddr) -> Result<(), PeerManagerError> { + self.peer_storage.write()?.add_net_address(node_id, net_address) } } @@ -266,22 +194,28 @@ impl PeerManager { mod test { use super::*; use crate::{ - connection::net_address::{net_addresses::NetAddressesWithStats, NetAddress}, - outbound_message_service::broadcast_strategy::ClosestRequest, + net_address::MultiaddressesWithStats, peer_manager::{ node_id::NodeId, peer::{Peer, PeerFlags}, + PeerFeatures, }, }; - use rand::OsRng; + use rand::rngs::OsRng; use tari_crypto::{keys::PublicKey, ristretto::RistrettoPublicKey}; - use tari_storage::HMapDatabase; + use tari_storage::HashmapDatabase; - fn create_test_peer(rng: &mut OsRng, ban_flag: bool) -> Peer { - let (_sk, pk) = RistrettoPublicKey::random_keypair(rng); + fn create_test_peer(ban_flag: bool) -> Peer { + let (_sk, pk) = RistrettoPublicKey::random_keypair(&mut OsRng); let node_id = NodeId::from_key(&pk).unwrap(); - let net_addresses = NetAddressesWithStats::from("1.2.3.4:8000".parse::().unwrap()); - let mut peer = Peer::new(pk, node_id, net_addresses, PeerFlags::default()); + let net_addresses = MultiaddressesWithStats::from("/ip4/1.2.3.4/tcp/8000".parse::().unwrap()); + let mut peer = Peer::new( + pk, + node_id, + net_addresses, + PeerFlags::default(), + PeerFeatures::MESSAGE_PROPAGATION, + ); peer.set_banned(ban_flag); peer } @@ -289,39 +223,40 @@ mod test { #[test] fn test_get_broadcast_identities() { // Create peer manager with random peers - let peer_manager = PeerManager::new(HMapDatabase::new()).unwrap(); + let peer_manager = PeerManager::new(HashmapDatabase::new()).unwrap(); let mut test_peers: Vec = Vec::new(); // Create 20 peers were the 1st and last one is bad - let mut rng = rand::OsRng::new().unwrap(); - test_peers.push(create_test_peer(&mut rng, true)); + let _rng = rand::rngs::OsRng; + test_peers.push(create_test_peer(true)); assert!(peer_manager.add_peer(test_peers[test_peers.len() - 1].clone()).is_ok()); for _i in 0..18 { - test_peers.push(create_test_peer(&mut rng, false)); + test_peers.push(create_test_peer(false)); assert!(peer_manager.add_peer(test_peers[test_peers.len() - 1].clone()).is_ok()); } - test_peers.push(create_test_peer(&mut rng, true)); + test_peers.push(create_test_peer(true)); assert!(peer_manager.add_peer(test_peers[test_peers.len() - 1].clone()).is_ok()); // Test Valid Direct - let identities = peer_manager - .get_broadcast_identities(BroadcastStrategy::DirectNodeId(test_peers[2].node_id.clone())) + let selected_peers = peer_manager + .direct_identity_node_id(&test_peers[2].node_id) + .unwrap() .unwrap(); - assert_eq!(identities.len(), 1); - assert_eq!(identities[0].node_id, test_peers[2].node_id); - assert_eq!(identities[0].public_key, test_peers[2].public_key); + assert_eq!(selected_peers.node_id, test_peers[2].node_id); + assert_eq!(selected_peers.public_key, test_peers[2].public_key); // Test Invalid Direct - let unmanaged_peer = create_test_peer(&mut rng, false); + let unmanaged_peer = create_test_peer(false); assert!(peer_manager - .get_broadcast_identities(BroadcastStrategy::DirectNodeId(unmanaged_peer.node_id.clone())) - .is_err()); + .direct_identity_node_id(&unmanaged_peer.node_id) + .unwrap() + .is_none()); // Test Flood - let identities = peer_manager.get_broadcast_identities(BroadcastStrategy::Flood).unwrap(); - assert_eq!(identities.len(), 18); - for peer_identity in &identities { + let selected_peers = peer_manager.flood_peers().unwrap(); + assert_eq!(selected_peers.len(), 18); + for peer_identity in &selected_peers { assert_eq!( peer_manager - .find_with_node_id(&peer_identity.node_id) + .find_by_node_id(&peer_identity.node_id) .unwrap() .is_banned(), false @@ -329,18 +264,14 @@ mod test { } // Test Closest - No exclusions - let identities = peer_manager - .get_broadcast_identities(BroadcastStrategy::Closest(ClosestRequest { - n: 3, - node_id: unmanaged_peer.node_id.clone(), - excluded_peers: Vec::new(), - })) + let selected_peers = peer_manager + .closest_peers(&unmanaged_peer.node_id, 3, &Vec::new()) .unwrap(); - assert_eq!(identities.len(), 3); + assert_eq!(selected_peers.len(), 3); // Remove current identity nodes from test peers let mut unused_peers: Vec = Vec::new(); for peer in &test_peers { - if !identities + if !selected_peers .iter() .any(|peer_identity| peer.node_id == peer_identity.node_id || peer.is_banned()) { @@ -348,7 +279,7 @@ mod test { } } // Check that none of the remaining unused peers have smaller distances compared to the selected peers - for peer_identity in &identities { + for peer_identity in &selected_peers { let selected_dist = unmanaged_peer.node_id.distance(&peer_identity.node_id); for unused_peer in &unused_peers { let unused_dist = unmanaged_peer.node_id.distance(&unused_peer.node_id); @@ -358,28 +289,23 @@ mod test { // Test Closest - With an exclusion let excluded_peers = vec![ - identities[0].public_key.clone(), // ,identities[1].public_key.clone() + selected_peers[0].public_key.clone(), // ,selected_peers[1].public_key.clone() ]; - let identities = peer_manager - .get_broadcast_identities(BroadcastStrategy::Closest(ClosestRequest { - n: 3, - node_id: unmanaged_peer.node_id.clone(), - excluded_peers: excluded_peers.clone(), - })) + let selected_peers = peer_manager + .closest_peers(&unmanaged_peer.node_id, 3, &excluded_peers) .unwrap(); - println!("identities.len()={:?}", identities.len()); - assert_eq!(identities.len(), 3); + assert_eq!(selected_peers.len(), 3); // Remove current identity nodes from test peers let mut unused_peers: Vec = Vec::new(); for peer in &test_peers { - if !identities.iter().any(|peer_identity| { + if !selected_peers.iter().any(|peer_identity| { peer.node_id == peer_identity.node_id || peer.is_banned() || excluded_peers.contains(&peer.public_key) }) { unused_peers.push(peer.clone()); } } // Check that none of the remaining unused peers have smaller distances compared to the selected peers - for peer_identity in &identities { + for peer_identity in &selected_peers { let selected_dist = unmanaged_peer.node_id.distance(&peer_identity.node_id); for unused_peer in &unused_peers { let unused_dist = unmanaged_peer.node_id.distance(&unused_peer.node_id); @@ -389,25 +315,21 @@ mod test { } // Test Random - let identities1 = peer_manager - .get_broadcast_identities(BroadcastStrategy::Random(10)) - .unwrap(); - let identities2 = peer_manager - .get_broadcast_identities(BroadcastStrategy::Random(10)) - .unwrap(); + let identities1 = peer_manager.random_peers(10).unwrap(); + let identities2 = peer_manager.random_peers(10).unwrap(); assert_ne!(identities1, identities2); } #[test] fn test_in_network_region() { - let mut rng = rand::OsRng::new().unwrap(); + let _rng = rand::rngs::OsRng; // Create peer manager with random peers - let peer_manager = PeerManager::new(HMapDatabase::new()).unwrap(); - let network_region_node_id = create_test_peer(&mut rng, false).node_id; + let peer_manager = PeerManager::new(HashmapDatabase::new()).unwrap(); + let network_region_node_id = create_test_peer(false).node_id; // Create peers let mut test_peers: Vec = Vec::new(); for _ in 0..10 { - test_peers.push(create_test_peer(&mut rng, false)); + test_peers.push(create_test_peer(false)); assert!(peer_manager.add_peer(test_peers[test_peers.len() - 1].clone()).is_ok()); } test_peers[0].set_banned(true); @@ -416,11 +338,7 @@ mod test { // Get nearest neighbours let n = 5; let nearest_identities = peer_manager - .get_broadcast_identities(BroadcastStrategy::Closest(ClosestRequest { - n, - node_id: network_region_node_id.clone(), - excluded_peers: Vec::new(), - })) + .closest_peers(&network_region_node_id, n, &Vec::new()) .unwrap(); for peer in &test_peers { @@ -438,52 +356,4 @@ mod test { } } } - - #[test] - fn test_peer_reset_connection_attempts() { - // Create peer manager with random peers - let peer_manager = PeerManager::new(HMapDatabase::new()).unwrap(); - let mut rng = rand::OsRng::new().unwrap(); - let peer = create_test_peer(&mut rng, false); - peer_manager.add_peer(peer.clone()).unwrap(); - - peer_manager - .mark_failed_connection_attempt(&peer.addresses.addresses[0].clone().as_net_address()) - .unwrap(); - peer_manager - .mark_failed_connection_attempt(&peer.addresses.addresses[0].clone().as_net_address()) - .unwrap(); - assert_eq!( - peer_manager - .find_with_node_id(&peer.node_id.clone()) - .unwrap() - .addresses - .addresses[0] - .connection_attempts, - 2 - ); - peer_manager.reset_connection_attempts(&peer.node_id.clone()).unwrap(); - assert_eq!( - peer_manager - .find_with_node_id(&peer.node_id.clone()) - .unwrap() - .addresses - .addresses[0] - .connection_attempts, - 0 - ); - } - - #[test] - fn test_adding_and_searching_by_net_address() { - // Create peer manager with random peers - let peer_manager = PeerManager::new(HMapDatabase::new()).unwrap(); - let mut rng = rand::OsRng::new().unwrap(); - let peer = create_test_peer(&mut rng, false); - peer_manager.add_peer(peer.clone()).unwrap(); - // Test NetAddress adding and searching - let net_address = NetAddress::from("1.2.3.4:7000".parse::().unwrap()); - assert!(peer_manager.add_net_address(&peer.node_id, &net_address).is_ok()); - assert!(peer_manager.find_with_net_address(&net_address).is_ok()); - } } diff --git a/comms/src/peer_manager/peer_query.rs b/comms/src/peer_manager/peer_query.rs new file mode 100644 index 0000000000..2762ebb7c6 --- /dev/null +++ b/comms/src/peer_manager/peer_query.rs @@ -0,0 +1,344 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::peer_manager::{peer_id::PeerId, NodeId, Peer, PeerManagerError}; +use std::cmp::min; +use tari_storage::{IterationResult, KeyValueStore}; + +type Predicate<'a> = Box bool + 'a>; + +/// Sort options for `PeerQuery` +#[derive(Debug, Clone)] +pub enum PeerQuerySortBy<'a> { + /// No sorting + None, + /// Sort by distance from a given node id + DistanceFrom(&'a NodeId), +} + +impl Default for PeerQuerySortBy<'_> { + fn default() -> Self { + PeerQuerySortBy::None + } +} + +/// Represents a query which can be performed on the peer database +#[derive(Default)] +pub struct PeerQuery<'a> { + select_predicate: Option>, + limit: Option, + sort_by: PeerQuerySortBy<'a>, +} + +impl<'a> PeerQuery<'a> { + /// Create a new `PeerQuery` + pub fn new() -> Self { + Default::default() + } + + /// Set the selection predicate. This predicate should return `true` to include a `Peer` + /// in the result set. + pub fn select_where(mut self, select_predicate: F) -> Self + where F: FnMut(&Peer) -> bool + 'a { + self.select_predicate = Some(Box::new(select_predicate)); + self + } + + /// Set a limit on the number of results returned + pub fn limit(mut self, limit: usize) -> Self { + self.limit = Some(limit); + self + } + + /// Sort by the given `PeerSortBy` criteria + pub fn sort_by(mut self, sort_by: PeerQuerySortBy<'a>) -> Self { + self.sort_by = sort_by; + self + } + + /// Returns a `PeerQueryExecutor` with this `PeerQuery` + pub(super) fn executor(self, store: &DS) -> PeerQueryExecutor<'a, '_, DS> + where DS: KeyValueStore { + PeerQueryExecutor::new(self, store) + } + + /// Returns true if the given limit is within the specified limit. If the limit + /// was not specified, this always returns true + fn within_limit(&self, limit: usize) -> bool { + self.limit.map(|inner_limit| inner_limit > limit).unwrap_or(true) + } + + /// Returns true if the specified select predicate returns true. If the + /// select predicate was not specified, this always returns true. + fn is_selected(&mut self, peer: &Peer) -> bool { + self.select_predicate + .as_mut() + .map(|predicate| (predicate)(peer)) + .unwrap_or(true) + } +} + +/// This struct executes the query using the given store +pub(super) struct PeerQueryExecutor<'a, 'b, DS> { + query: PeerQuery<'a>, + store: &'b DS, +} + +impl<'a, 'b, DS> PeerQueryExecutor<'a, 'b, DS> +where DS: KeyValueStore +{ + pub fn new(query: PeerQuery<'a>, store: &'b DS) -> Self { + Self { query, store } + } + + pub fn get_results(&mut self) -> Result, PeerManagerError> { + match self.query.sort_by { + PeerQuerySortBy::None => self.get_query_results(), + PeerQuerySortBy::DistanceFrom(node_id) => self.get_distance_sorted_results(node_id), + } + } + + pub fn get_distance_sorted_results(&mut self, node_id: &NodeId) -> Result, PeerManagerError> { + let mut peer_keys = Vec::new(); + let mut distances = Vec::new(); + self.store + .for_each_ok(|(peer_key, peer)| { + if self.query.is_selected(&peer) { + peer_keys.push(peer_key); + distances.push(node_id.distance(&peer.node_id)); + } + + IterationResult::Continue + }) + .map_err(PeerManagerError::DatabaseError)?; + + // Use all available peers up to a maximum of N + let max_available = self + .query + .limit + .map(|limit| min(peer_keys.len(), limit)) + .unwrap_or_else(|| peer_keys.len()); + if max_available == 0 { + return Ok(Vec::new()); + } + + // Perform partial sort of elements only up to N elements + let mut selected_peers = Vec::with_capacity(max_available); + for i in 0..max_available { + for j in (i + 1)..peer_keys.len() { + if distances[i] > distances[j] { + distances.swap(i, j); + peer_keys.swap(i, j); + } + } + let peer = self + .store + .get(&peer_keys[i]) + .map_err(PeerManagerError::DatabaseError)? + .ok_or(PeerManagerError::PeerNotFoundError)?; + + selected_peers.push(peer); + } + + Ok(selected_peers) + } + + pub fn get_query_results(&mut self) -> Result, PeerManagerError> { + let mut selected_peers = match self.query.limit { + Some(n) => Vec::with_capacity(n), + None => Vec::new(), + }; + + self.store + .for_each_ok(|(_, peer)| { + if self.query.within_limit(selected_peers.len()) { + if self.query.is_selected(&peer) { + selected_peers.push(peer); + } + } else { + return IterationResult::Break; + } + + IterationResult::Continue + }) + .map_err(PeerManagerError::DatabaseError)?; + + Ok(selected_peers) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::{ + net_address::MultiaddressesWithStats, + peer_manager::{ + node_id::NodeId, + peer::{Peer, PeerFlags}, + PeerFeatures, + }, + }; + use multiaddr::Multiaddr; + use rand::rngs::OsRng; + use std::iter::repeat_with; + use tari_crypto::{keys::PublicKey, ristretto::RistrettoPublicKey}; + use tari_storage::HashmapDatabase; + + fn create_test_peer(ban_flag: bool) -> Peer { + let (_sk, pk) = RistrettoPublicKey::random_keypair(&mut OsRng); + let node_id = NodeId::from_key(&pk).unwrap(); + let net_addresses = MultiaddressesWithStats::from("/ip4/1.2.3.4/tcp/8000".parse::().unwrap()); + let mut peer = Peer::new( + pk, + node_id, + net_addresses, + PeerFlags::default(), + PeerFeatures::MESSAGE_PROPAGATION, + ); + peer.set_banned(ban_flag); + peer + } + + #[test] + fn limit_query() { + // Create peer manager with random peers + let mut sample_peers = Vec::new(); + // Create 20 peers were the 1st and last one is bad + sample_peers.push(create_test_peer(true)); + let db = HashmapDatabase::new(); + let mut id_counter = 0; + + repeat_with(|| create_test_peer(false)).take(5).for_each(|peer| { + db.insert(id_counter, peer).unwrap(); + id_counter += 1; + }); + + let peers = PeerQuery::new().limit(4).executor(&db).get_results().unwrap(); + + assert_eq!(peers.len(), 4); + } + + #[test] + fn select_where_query() { + // Create peer manager with random peers + let mut sample_peers = Vec::new(); + // Create 20 peers were the 1st and last one is bad + let _rng = rand::rngs::OsRng; + sample_peers.push(create_test_peer(true)); + let db = HashmapDatabase::new(); + let mut id_counter = 0; + + repeat_with(|| create_test_peer(true)).take(2).for_each(|peer| { + db.insert(id_counter, peer).unwrap(); + id_counter += 1; + }); + + repeat_with(|| create_test_peer(false)).take(5).for_each(|peer| { + db.insert(id_counter, peer).unwrap(); + id_counter += 1; + }); + + let peers = PeerQuery::new() + .select_where(|peer| !peer.is_banned()) + .executor(&db) + .get_results() + .unwrap(); + + assert_eq!(peers.len(), 5); + assert!(peers.iter().all(|peer| !peer.is_banned())); + } + + #[test] + fn select_where_limit_query() { + // Create peer manager with random peers + let mut sample_peers = Vec::new(); + // Create 20 peers were the 1st and last one is bad + let _rng = rand::rngs::OsRng; + sample_peers.push(create_test_peer(true)); + let db = HashmapDatabase::new(); + let mut id_counter = 0; + + repeat_with(|| create_test_peer(true)).take(3).for_each(|peer| { + db.insert(id_counter, peer).unwrap(); + id_counter += 1; + }); + + repeat_with(|| create_test_peer(false)).take(5).for_each(|peer| { + db.insert(id_counter, peer).unwrap(); + id_counter += 1; + }); + + let peers = PeerQuery::new() + .select_where(|peer| peer.is_banned()) + .limit(2) + .executor(&db) + .get_results() + .unwrap(); + + assert_eq!(peers.len(), 2); + assert!(peers.iter().all(|peer| peer.is_banned())); + } + + #[test] + fn sort_by_query() { + // Create peer manager with random peers + let mut sample_peers = Vec::new(); + // Create 20 peers were the 1st and last one is bad + let _rng = rand::rngs::OsRng; + sample_peers.push(create_test_peer(true)); + let db = HashmapDatabase::new(); + let mut id_counter = 0; + + repeat_with(|| create_test_peer(true)).take(3).for_each(|peer| { + db.insert(id_counter, peer).unwrap(); + id_counter += 1; + }); + + repeat_with(|| create_test_peer(false)).take(5).for_each(|peer| { + db.insert(id_counter, peer).unwrap(); + id_counter += 1; + }); + + let node_id = NodeId::default(); + + let peers = PeerQuery::new() + .sort_by(PeerQuerySortBy::DistanceFrom(&node_id)) + .limit(2) + .executor(&db) + .get_results() + .unwrap(); + + assert_eq!(peers.len(), 2); + + db.for_each_ok(|(_, current_peer)| { + // Exclude selected peers + if !peers.contains(¤t_peer) { + // Every selected peer's distance from node_id is less than every other peer's distance from node_id + for selected_peer in &peers { + assert!(selected_peer.node_id.distance(&node_id) <= current_peer.node_id.distance(&node_id)); + } + } + IterationResult::Continue + }) + .unwrap(); + } +} diff --git a/comms/src/peer_manager/peer_storage.rs b/comms/src/peer_manager/peer_storage.rs index c7885e47e9..74b676eeb6 100644 --- a/comms/src/peer_manager/peer_storage.rs +++ b/comms/src/peer_manager/peer_storage.rs @@ -21,80 +21,92 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::{ - connection::net_address::NetAddress, + consts::PEER_MANAGER_MAX_FLOOD_PEERS, peer_manager::{ + connection_stats::PeerConnectionStats, node_id::{NodeDistance, NodeId}, - node_identity::PeerNodeIdentity, peer::{Peer, PeerFlags}, - peer_key::{generate_peer_key, PeerKey}, + peer_id::{generate_peer_key, PeerId}, + PeerFeatures, PeerManagerError, + PeerQuery, }, - types::{CommsPublicKey, CommsRng}, + types::{CommsDatabase, CommsPublicKey}, }; -use rand::Rng; -use std::{cmp::min, collections::HashMap, time::Duration}; -use tari_storage::KeyValueStore; +use log::*; +use multiaddr::Multiaddr; +use rand::{rngs::OsRng, Rng}; +use std::{cmp::min, collections::HashMap}; +use tari_storage::{IterationResult, KeyValueStore}; + +const LOG_TARGET: &str = "comms::peer_manager::peer_storage"; /// PeerStorage provides a mechanism to keep a datastore and a local copy of all peers in sync and allow fast searches /// using the node_id, public key or net_address of a peer. pub struct PeerStorage { - pub(crate) peers: DS, - node_id_hm: HashMap, - public_key_hm: HashMap, - net_address_hm: HashMap, - rng: CommsRng, + pub(crate) peer_db: DS, + public_key_index: HashMap, + node_id_index: HashMap, } impl PeerStorage -where DS: KeyValueStore +where DS: KeyValueStore { - /// Constructs a new empty PeerStorage system - pub fn new(database: DS) -> Result, PeerManagerError> { + /// Constructs a new PeerStorage, with indexes populated from the given datastore + pub fn new_indexed(database: DS) -> Result, PeerManagerError> { // Restore peers and hashmap links from database - let mut node_id_hm: HashMap = HashMap::new(); - let mut public_key_hm: HashMap = HashMap::new(); - let mut net_address_hm: HashMap = HashMap::new(); + let mut public_key_index = HashMap::new(); + let mut node_id_index = HashMap::new(); + let mut total_entries = 0; database - .for_each(|pair| { - let (peer_key, peer) = pair.unwrap(); - node_id_hm.insert(peer.node_id.clone(), peer_key); - public_key_hm.insert(peer.public_key.clone(), peer_key); - for net_address_with_stats in &peer.addresses.addresses { - net_address_hm.insert(net_address_with_stats.net_address.clone(), peer_key); - } + .for_each_ok(|(peer_key, peer)| { + total_entries += 1; + public_key_index.insert(peer.public_key, peer_key); + node_id_index.insert(peer.node_id, peer_key); + IterationResult::Continue }) .map_err(PeerManagerError::DatabaseError)?; + trace!( + target: LOG_TARGET, + "Peer storage is initialized. {} total entries.", + total_entries, + ); + Ok(PeerStorage { - peers: database, - node_id_hm, - public_key_hm, - net_address_hm, - rng: CommsRng::new().map_err(|_| PeerManagerError::RngError)?, + peer_db: database, + public_key_index, + node_id_index, }) } /// Adds a peer to the routing table of the PeerManager if the peer does not already exist. When a peer already /// exists, the stored version will be replaced with the newly provided peer. - pub fn add_peer(&mut self, peer: Peer) -> Result<(), PeerManagerError> { - match self.public_key_hm.get(&peer.public_key) { - Some(&peer_key) => { + pub fn add_peer(&mut self, mut peer: Peer) -> Result { + let (public_key, node_id) = (peer.public_key.clone(), peer.node_id.clone()); + match self.public_key_index.get(&peer.public_key).copied() { + Some(peer_key) => { + trace!(target: LOG_TARGET, "Replacing peer that has NodeId '{}'", peer.node_id); // Replace existing entry - self.remove_hashmap_links(peer_key)?; - self.add_hashmap_links(peer_key, &peer); - self.peers + peer.set_id(peer_key); + self.peer_db .insert(peer_key, peer) .map_err(PeerManagerError::DatabaseError)?; - Ok(()) + self.remove_index_links(peer_key); + self.add_index_links(peer_key, public_key, node_id); + Ok(peer_key) }, None => { // Add new entry - let peer_key = generate_peer_key(&mut self.rng); // Generate new random peer key - self.add_hashmap_links(peer_key, &peer); - self.peers + trace!(target: LOG_TARGET, "Adding peer with node id '{}'", peer.node_id); + // Generate new random peer key + let peer_key = generate_peer_key(); + peer.set_id(peer_key); + self.peer_db .insert(peer_key, peer) .map_err(PeerManagerError::DatabaseError)?; - Ok(()) + self.add_index_links(peer_key, public_key, node_id); + Ok(peer_key) }, } } @@ -105,26 +117,44 @@ where DS: KeyValueStore &mut self, public_key: &CommsPublicKey, node_id: Option, - net_addresses: Option>, + net_addresses: Option>, flags: Option, + peer_features: Option, + connection_stats: Option, ) -> Result<(), PeerManagerError> { - match self.public_key_hm.get(public_key) { + match self.public_key_index.get(public_key).copied() { Some(peer_key) => { - let peer_key = *peer_key; - self.remove_hashmap_links(peer_key)?; - - let mut stored_peer: Peer = self - .peers + let mut stored_peer = self + .peer_db .get(&peer_key) - .map_err(|e| PeerManagerError::DatabaseError(e))? - .ok_or(PeerManagerError::PeerNotFoundError)?; - stored_peer.update(node_id, net_addresses, flags); + .map_err(PeerManagerError::DatabaseError)? + .ok_or_else(|| PeerManagerError::PeerNotFoundError)?; + trace!(target: LOG_TARGET, "Updating peer '{}'", stored_peer.node_id); + + let must_update_node_id = node_id.is_some(); + if must_update_node_id { + trace!( + target: LOG_TARGET, + "Node id update from '{}' to '{}'", + stored_peer.node_id.short_str(), + node_id.as_ref().expect("already checked").short_str() + ); + } + stored_peer.update(node_id, net_addresses, flags, peer_features, connection_stats); + + let public_key = stored_peer.public_key.clone(); + let node_id = stored_peer.node_id.clone(); - self.add_hashmap_links(peer_key, &stored_peer); - self.peers + self.peer_db .insert(peer_key, stored_peer) - .map_err(|e| PeerManagerError::DatabaseError(e))?; + .map_err(PeerManagerError::DatabaseError)?; + + if must_update_node_id { + self.remove_index_links(peer_key); + self.add_index_links(peer_key, public_key, node_id); + } + Ok(()) }, None => Err(PeerManagerError::PeerNotFoundError), @@ -134,223 +164,184 @@ where DS: KeyValueStore /// The peer with the specified public_key will be removed from the PeerManager pub fn delete_peer(&mut self, node_id: &NodeId) -> Result<(), PeerManagerError> { let peer_key = *self - .node_id_hm + .node_id_index .get(&node_id) - .ok_or(PeerManagerError::PeerNotFoundError)?; - self.remove_hashmap_links(peer_key)?; - self.peers.delete(&peer_key).map_err(PeerManagerError::DatabaseError)?; + .ok_or_else(|| PeerManagerError::PeerNotFoundError)?; + self.peer_db + .delete(&peer_key) + .map_err(PeerManagerError::DatabaseError)?; + + self.remove_index_links(peer_key); Ok(()) } /// Add key pairs to the search hashmaps for a newly added or moved peer - fn add_hashmap_links(&mut self, peer_key: PeerKey, peer: &Peer) { - self.node_id_hm.insert(peer.node_id.clone(), peer_key); - self.public_key_hm.insert(peer.public_key.clone(), peer_key); - for net_address_with_stats in &peer.addresses.addresses { - self.net_address_hm - .insert(net_address_with_stats.net_address.clone(), peer_key); - } + fn add_index_links(&mut self, peer_key: PeerId, public_key: CommsPublicKey, node_id: NodeId) { + self.node_id_index.insert(node_id, peer_key); + self.public_key_index.insert(public_key, peer_key); } /// Remove the peer specified by a given index from the database and remove hashmap keys - fn remove_hashmap_links(&mut self, peer_key: PeerKey) -> Result<(), PeerManagerError> { - let peer: Peer = self - .peers - .get(&peer_key) - .map_err(PeerManagerError::DatabaseError)? - .ok_or(PeerManagerError::PeerNotFoundError)?; - self.node_id_hm.remove(&peer.node_id); - self.public_key_hm.remove(&peer.public_key); - for net_address_with_stats in &peer.addresses.addresses { - self.net_address_hm.remove(&net_address_with_stats.net_address); - } - Ok(()) + fn remove_index_links(&mut self, peer_key: PeerId) { + let initial_size_pk = self.public_key_index.len(); + let initial_size_node_id = self.node_id_index.len(); + self.public_key_index = self.public_key_index.drain().filter(|(_, k)| k != &peer_key).collect(); + self.node_id_index = self.node_id_index.drain().filter(|(_, k)| k != &peer_key).collect(); + debug_assert_eq!(initial_size_pk - 1, self.public_key_index.len()); + debug_assert_eq!(initial_size_node_id - 1, self.node_id_index.len()); } /// Find the peer with the provided NodeID - pub fn find_with_node_id(&self, node_id: &NodeId) -> Result { - let peer_key = *self - .node_id_hm - .get(&node_id) - .ok_or(PeerManagerError::PeerNotFoundError)?; - self.peers + pub fn find_by_node_id(&self, node_id: &NodeId) -> Result { + let peer_key = self + .node_id_index + .get(node_id) + .ok_or_else(|| PeerManagerError::PeerNotFoundError)?; + self.peer_db .get(&peer_key) .map_err(PeerManagerError::DatabaseError)? - .ok_or(PeerManagerError::PeerNotFoundError) + .ok_or_else(|| PeerManagerError::PeerNotFoundError) } /// Find the peer with the provided PublicKey - pub fn find_with_public_key(&self, public_key: &CommsPublicKey) -> Result { - let peer_key = *self - .public_key_hm - .get(&public_key) - .ok_or(PeerManagerError::PeerNotFoundError)?; - self.peers - .get(&peer_key) - .map_err(PeerManagerError::DatabaseError)? - .ok_or(PeerManagerError::PeerNotFoundError) - } - - /// Find the peer with the provided NetAddress - pub fn find_with_net_address(&self, net_address: &NetAddress) -> Result { - let peer_key = *self - .net_address_hm - .get(&net_address) - .ok_or(PeerManagerError::PeerNotFoundError)?; - self.peers - .get(&peer_key) + pub fn find_by_public_key(&self, public_key: &CommsPublicKey) -> Result { + let peer_key = self + .public_key_index + .get(public_key) + .ok_or_else(|| PeerManagerError::PeerNotFoundError)?; + self.peer_db + .get(peer_key) .map_err(PeerManagerError::DatabaseError)? - .ok_or(PeerManagerError::PeerNotFoundError) + .ok_or_else(|| PeerManagerError::PeerNotFoundError) } /// Check if a peer exist using the specified public_key pub fn exists(&self, public_key: &CommsPublicKey) -> bool { - self.public_key_hm.get(&public_key).is_some() + self.public_key_index.contains_key(public_key) } /// Check if a peer exist using the specified node_id pub fn exists_node_id(&self, node_id: &NodeId) -> bool { - self.node_id_hm.get(&node_id).is_some() + self.node_id_index.contains_key(node_id) } /// Constructs a single NodeIdentity for the peer corresponding to the provided NodeId - pub fn direct_identity_node_id(&self, node_id: &NodeId) -> Result, PeerManagerError> { - let peer_key = *self - .node_id_hm - .get(&node_id) - .ok_or(PeerManagerError::PeerNotFoundError)?; - let peer: Peer = self - .peers - .get(&peer_key) - .map_err(PeerManagerError::DatabaseError)? - .ok_or(PeerManagerError::PeerNotFoundError)?; + pub fn direct_identity_node_id(&self, node_id: &NodeId) -> Result { + let peer = self.find_by_node_id(node_id)?; + if peer.is_banned() { Err(PeerManagerError::BannedPeer) } else { - Ok(vec![PeerNodeIdentity::new(node_id.clone(), peer.public_key.clone())]) + Ok(peer) } } /// Constructs a single NodeIdentity for the peer corresponding to the provided NodeId - pub fn direct_identity_public_key( - &self, - public_key: &CommsPublicKey, - ) -> Result, PeerManagerError> - { - let peer_key = *self - .public_key_hm - .get(&public_key) - .ok_or(PeerManagerError::PeerNotFoundError)?; - let peer: Peer = self - .peers - .get(&peer_key) - .map_err(PeerManagerError::DatabaseError)? - .ok_or(PeerManagerError::PeerNotFoundError)?; + pub fn direct_identity_public_key(&self, public_key: &CommsPublicKey) -> Result { + let peer = self.find_by_public_key(public_key)?; if peer.is_banned() { Err(PeerManagerError::BannedPeer) } else { - Ok(vec![PeerNodeIdentity::new(peer.node_id.clone(), public_key.clone())]) + Ok(peer) } } - /// Compile a list of all known node identities that can be used for the flood BroadcastStrategy - pub fn flood_identities(&self) -> Result, PeerManagerError> { - // TODO: this list should only contain Communication Nodes - let mut identities: Vec = Vec::new(); - self.peers - .for_each(|pair| { - let (_, peer) = pair.unwrap(); - if !peer.is_banned() { - identities.push(PeerNodeIdentity::new(peer.node_id.clone(), peer.public_key.clone())); - } + /// Perform an ad-hoc query on the peer database. + pub fn perform_query(&self, query: PeerQuery) -> Result, PeerManagerError> { + query.executor(&self.peer_db).get_results() + } + + /// Compile a list of all known peers + pub fn flood_peers(&self) -> Result, PeerManagerError> { + self.peer_db + .filter_take(PEER_MANAGER_MAX_FLOOD_PEERS, |(_, peer)| { + !peer.is_banned() && peer.has_features(PeerFeatures::MESSAGE_PROPAGATION) }) - .map_err(PeerManagerError::DatabaseError)?; - Ok(identities) + .map(|pairs| pairs.into_iter().map(|(_, peer)| peer).collect()) + .map_err(PeerManagerError::DatabaseError) } - /// Compile a list of node identities that can be used for the closest BroadcastStrategy - pub fn closest_identities( + /// Compile a list of peers + pub fn closest_peers( &self, node_id: &NodeId, n: usize, - excluded_peers: &Vec, - ) -> Result, PeerManagerError> + excluded_peers: &[CommsPublicKey], + ) -> Result, PeerManagerError> { - let mut peer_keys: Vec = Vec::new(); - let mut dists: Vec = Vec::new(); - self.peers - .for_each(|pair| { - let (peer_key, peer) = pair.unwrap(); + let mut peer_keys = Vec::new(); + let mut dists = Vec::new(); + self.peer_db + .for_each_ok(|(peer_key, peer)| { if !peer.is_banned() && !excluded_peers.contains(&peer.public_key) { peer_keys.push(peer_key); dists.push(node_id.distance(&peer.node_id)); } + IterationResult::Continue }) .map_err(PeerManagerError::DatabaseError)?; // Use all available peers up to a maximum of N let max_available = min(peer_keys.len(), n); - if max_available > 0 { - // Perform partial sort of elements only up to N elements - let mut nearest_identities: Vec = Vec::with_capacity(max_available); - for i in 0..max_available { - for j in (i + 1)..peer_keys.len() { - if dists[i] > dists[j] { - dists.swap(i, j); - peer_keys.swap(i, j); - } + if max_available == 0 { + return Ok(Vec::new()); + } + + // Perform partial sort of elements only up to N elements + let mut nearest_identities = Vec::with_capacity(max_available); + for i in 0..max_available { + for j in (i + 1)..peer_keys.len() { + if dists[i] > dists[j] { + dists.swap(i, j); + peer_keys.swap(i, j); } - let peer: Peer = self - .peers - .get(&peer_keys[i]) - .map_err(PeerManagerError::DatabaseError)? - .ok_or(PeerManagerError::PeerNotFoundError)?; - nearest_identities.push(PeerNodeIdentity::new(peer.node_id.clone(), peer.public_key.clone())); } - Ok(nearest_identities) - } else { - Ok(Vec::new()) + let peer = self + .peer_db + .get(&peer_keys[i]) + .map_err(PeerManagerError::DatabaseError)? + .ok_or_else(|| PeerManagerError::PeerNotFoundError)?; + nearest_identities.push(peer); } + + Ok(nearest_identities) } - /// Compile a list of node identities that can be used for the random BroadcastStrategy - pub fn random_identities(&mut self, n: usize) -> Result, PeerManagerError> { + /// Compile a random list of peers of size _n_ + pub fn random_peers(&self, n: usize) -> Result, PeerManagerError> { // TODO: Send to a random set of Communication Nodes - let mut peer_keys: Vec = Vec::new(); - self.peers - .for_each(|pair| { - let (peer_key, peer) = pair.unwrap(); - if !peer.is_banned() { - peer_keys.push(peer_key); - } - }) + let mut peer_keys = self + .peer_db + .filter(|(_, peer)| !peer.is_banned()) + .map(|pairs| pairs.into_iter().map(|(k, _)| k).collect::>()) .map_err(PeerManagerError::DatabaseError)?; // Use all available peers up to a maximum of N let max_available = min(peer_keys.len(), n); - if max_available > 0 { - // Shuffle first n elements - for i in 0..max_available { - let j = self.rng.gen_range(0, peer_keys.len()); - peer_keys.swap(i, j); - } - // Compile list of first n shuffled elements - let mut random_identities: Vec = Vec::with_capacity(max_available); - for i in 0..max_available { - let peer: Peer = self - .peers - .get(&peer_keys[i]) - .map_err(PeerManagerError::DatabaseError)? - .ok_or(PeerManagerError::PeerNotFoundError)?; - random_identities.push(PeerNodeIdentity::new(peer.node_id.clone(), peer.public_key.clone())); - } - Ok(random_identities) - } else { - Ok(Vec::new()) + if max_available == 0 { + return Ok(Vec::new()); + } + + // Shuffle first n elements + for i in 0..max_available { + let j = OsRng.gen_range(0, peer_keys.len()); + peer_keys.swap(i, j); + } + // Compile list of first n shuffled elements + let mut random_identities = Vec::with_capacity(max_available); + // for i in 0..max_available { + for peer_key in peer_keys.iter().take(max_available) { + let peer = self + .peer_db + .get(peer_key) + .map_err(PeerManagerError::DatabaseError)? + .ok_or_else(|| PeerManagerError::PeerNotFoundError)?; + random_identities.push(peer); } + Ok(random_identities) } /// Check if a specific node_id is in the network region of the N nearest neighbours of the region specified by - /// region_node_id + /// region_node_id. If there are less than N known peers, this will _always_ return true pub fn in_network_region( &self, node_id: &NodeId, @@ -359,208 +350,70 @@ where DS: KeyValueStore ) -> Result { let region2node_dist = region_node_id.distance(node_id); - let mut dists: Vec = vec![NodeDistance::max_distance(); n]; + let mut dists = vec![NodeDistance::max_distance(); n]; let last_index = dists.len() - 1; - self.peers - .for_each(|pair| { - let (_, peer) = pair.unwrap(); + self.peer_db + .for_each_ok(|(_, peer)| { if !peer.is_banned() { let curr_dist = region_node_id.distance(&peer.node_id); for i in 0..dists.len() { if dists[i] > curr_dist { - dists.insert(i, curr_dist.clone()); + dists.insert(i, curr_dist); dists.pop(); break; } } + if region2node_dist > dists[last_index] { - return; + return IterationResult::Break; } } + + IterationResult::Continue }) .map_err(PeerManagerError::DatabaseError)?; + Ok(region2node_dist <= dists[last_index]) } /// Enables Thread safe access - Changes the ban flag bit of the peer pub fn set_banned(&mut self, node_id: &NodeId, ban_flag: bool) -> Result<(), PeerManagerError> { let peer_key = *self - .node_id_hm + .node_id_index .get(&node_id) - .ok_or(PeerManagerError::PeerNotFoundError)?; + .ok_or_else(|| PeerManagerError::PeerNotFoundError)?; let mut peer: Peer = self - .peers + .peer_db .get(&peer_key) .map_err(PeerManagerError::DatabaseError)? - .ok_or(PeerManagerError::PeerNotFoundError)?; + .ok_or_else(|| PeerManagerError::PeerNotFoundError)?; peer.set_banned(ban_flag); - self.peers + self.peer_db .insert(peer_key, peer) .map_err(PeerManagerError::DatabaseError) } /// Enables Thread safe access - Adds a new net address to the peer if it doesn't yet exist - pub fn add_net_address(&mut self, node_id: &NodeId, net_address: &NetAddress) -> Result<(), PeerManagerError> { + pub fn add_net_address(&mut self, node_id: &NodeId, net_address: &Multiaddr) -> Result<(), PeerManagerError> { let peer_key = *self - .node_id_hm + .node_id_index .get(&node_id) - .ok_or(PeerManagerError::PeerNotFoundError)?; + .ok_or_else(|| PeerManagerError::PeerNotFoundError)?; let mut peer: Peer = self - .peers + .peer_db .get(&peer_key) .map_err(PeerManagerError::DatabaseError)? - .ok_or(PeerManagerError::PeerNotFoundError)?; + .ok_or_else(|| PeerManagerError::PeerNotFoundError)?; peer.addresses.add_net_address(net_address); - self.net_address_hm.insert(net_address.clone(), peer_key); - self.peers - .insert(peer_key, peer) - .map_err(PeerManagerError::DatabaseError) - } - - /// Enables Thread safe access - Finds and returns the highest priority net address until all connection attempts - /// for each net address have been reached - pub fn get_best_net_address(&mut self, node_id: &NodeId) -> Result { - let peer_key = *self - .node_id_hm - .get(&node_id) - .ok_or(PeerManagerError::PeerNotFoundError)?; - let mut peer: Peer = self - .peers - .get(&peer_key) - .map_err(PeerManagerError::DatabaseError)? - .ok_or(PeerManagerError::PeerNotFoundError)?; - let best_net_address = peer - .addresses - .get_best_net_address() - .map_err(PeerManagerError::NetAddressError)?; - self.peers - .insert(peer_key, peer) - .map_err(PeerManagerError::DatabaseError)?; - Ok(best_net_address) - } - - /// Enables Thread safe access - The average connection latency of the provided net address will be updated to - /// include the current measured latency sample - pub fn update_latency( - &mut self, - net_address: &NetAddress, - latency_measurement: Duration, - ) -> Result<(), PeerManagerError> - { - let peer_key = *self - .net_address_hm - .get(&net_address) - .ok_or(PeerManagerError::PeerNotFoundError)?; - let mut peer: Peer = self - .peers - .get(&peer_key) - .map_err(PeerManagerError::DatabaseError)? - .ok_or(PeerManagerError::PeerNotFoundError)?; - peer.addresses - .update_latency(net_address, latency_measurement) - .map_err(PeerManagerError::NetAddressError)?; - self.peers - .insert(peer_key, peer) - .map_err(PeerManagerError::DatabaseError) - } - - /// Enables Thread safe access - Mark that a message was received from the specified net address - pub fn mark_message_received(&mut self, net_address: &NetAddress) -> Result<(), PeerManagerError> { - let peer_key = *self - .net_address_hm - .get(&net_address) - .ok_or(PeerManagerError::PeerNotFoundError)?; - let mut peer: Peer = self - .peers - .get(&peer_key) - .map_err(PeerManagerError::DatabaseError)? - .ok_or(PeerManagerError::PeerNotFoundError)?; - peer.addresses - .mark_message_received(net_address) - .map_err(PeerManagerError::NetAddressError)?; - self.peers - .insert(peer_key, peer) - .map_err(PeerManagerError::DatabaseError) - } - - /// Enables Thread safe access - Mark that a rejected message was received from the specified net address - pub fn mark_message_rejected(&mut self, net_address: &NetAddress) -> Result<(), PeerManagerError> { - let peer_key = *self - .net_address_hm - .get(&net_address) - .ok_or(PeerManagerError::PeerNotFoundError)?; - let mut peer: Peer = self - .peers - .get(&peer_key) - .map_err(PeerManagerError::DatabaseError)? - .ok_or(PeerManagerError::PeerNotFoundError)?; - peer.addresses - .mark_message_rejected(net_address) - .map_err(PeerManagerError::NetAddressError)?; - self.peers - .insert(peer_key, peer) - .map_err(PeerManagerError::DatabaseError) - } - - /// Enables Thread safe access - Mark that a successful connection was established with the specified net address - pub fn mark_successful_connection_attempt(&mut self, net_address: &NetAddress) -> Result<(), PeerManagerError> { - let peer_key = *self - .net_address_hm - .get(&net_address) - .ok_or(PeerManagerError::PeerNotFoundError)?; - let mut peer: Peer = self - .peers - .get(&peer_key) - .map_err(PeerManagerError::DatabaseError)? - .ok_or(PeerManagerError::PeerNotFoundError)?; - peer.addresses - .mark_successful_connection_attempt(net_address) - .map_err(PeerManagerError::NetAddressError)?; - self.peers - .insert(peer_key, peer) - .map_err(PeerManagerError::DatabaseError) - } - - /// Enables Thread safe access - Mark that a connection could not be established with the specified net address - pub fn mark_failed_connection_attempt(&mut self, net_address: &NetAddress) -> Result<(), PeerManagerError> { - let peer_key = *self - .net_address_hm - .get(&net_address) - .ok_or(PeerManagerError::PeerNotFoundError)?; - let mut peer: Peer = self - .peers - .get(&peer_key) - .map_err(PeerManagerError::DatabaseError)? - .ok_or(PeerManagerError::PeerNotFoundError)?; - peer.addresses - .mark_failed_connection_attempt(net_address) - .map_err(PeerManagerError::NetAddressError)?; - self.peers - .insert(peer_key, peer) - .map_err(PeerManagerError::DatabaseError) - } - - /// Enables Thread safe access - Finds a peer and if it exists resets all connection attempts on all net address - /// belonging to that peer - pub fn reset_connection_attempts(&mut self, node_id: &NodeId) -> Result<(), PeerManagerError> { - let peer_key = *self - .node_id_hm - .get(&node_id) - .ok_or(PeerManagerError::PeerNotFoundError)?; - let mut peer: Peer = self - .peers - .get(&peer_key) - .map_err(PeerManagerError::DatabaseError)? - .ok_or(PeerManagerError::PeerNotFoundError)?; - peer.addresses.reset_connection_attempts(); - self.peers + self.peer_db .insert(peer_key, peer) .map_err(PeerManagerError::DatabaseError) } +} - /// Returns the DataStore underlying PeerStorage if one exists - pub fn into_datastore(self) -> DS { - self.peers +impl Into for PeerStorage { + fn into(self) -> CommsDatabase { + self.peer_db } } @@ -568,288 +421,188 @@ where DS: KeyValueStore mod test { use super::*; use crate::{ - connection::net_address::{net_addresses::NetAddressesWithStats, NetAddress}, - peer_manager::peer::PeerFlags, + net_address::MultiaddressesWithStats, + peer_manager::{peer::PeerFlags, PeerFeatures}, }; - use std::{path::PathBuf, sync::Arc}; use tari_crypto::{keys::PublicKey, ristretto::RistrettoPublicKey}; - use tari_storage::{ - lmdb_store::{LMDBBuilder, LMDBError, LMDBStore}, - HMapDatabase, - LMDBWrapper, - }; - - fn get_path(name: &str) -> String { - let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - path.push("tests/data"); - path.push(name); - path.to_str().unwrap().to_string() - } - - fn init_datastore(name: &str) -> Result { - let path = get_path(name); - let _ = std::fs::create_dir(&path).unwrap_or_default(); - LMDBBuilder::new() - .set_path(&path) - .set_environment_size(10) - .set_max_number_of_databases(2) - .add_database(name, lmdb_zero::db::CREATE) - .build() - } - - fn clean_up_datastore(name: &str) { - std::fs::remove_dir_all(get_path(name)).unwrap(); - } + use tari_storage::HashmapDatabase; #[test] fn test_restore() { // Create Peers - let mut rng = rand::OsRng::new().unwrap(); + let mut rng = rand::rngs::OsRng; let (_sk, pk) = RistrettoPublicKey::random_keypair(&mut rng); let node_id = NodeId::from_key(&pk).unwrap(); - let net_address1 = NetAddress::from("1.2.3.4:8000".parse::().unwrap()); - let net_address2 = NetAddress::from("5.6.7.8:8000".parse::().unwrap()); - let net_address3 = NetAddress::from("5.6.7.8:7000".parse::().unwrap()); - let mut net_addresses = NetAddressesWithStats::from(net_address1.clone()); + let net_address1 = "/ip4/1.2.3.4/tcp/8000".parse::().unwrap(); + let net_address2 = "/ip4/5.6.7.8/tcp/8000".parse::().unwrap(); + let net_address3 = "/ip4/5.6.7.8/tcp/7000".parse::().unwrap(); + let mut net_addresses = MultiaddressesWithStats::from(net_address1.clone()); net_addresses.add_net_address(&net_address2); net_addresses.add_net_address(&net_address3); - let peer1 = Peer::new(pk, node_id, net_addresses, PeerFlags::default()); + let peer1 = Peer::new(pk, node_id, net_addresses, PeerFlags::default(), PeerFeatures::empty()); let (_sk, pk) = RistrettoPublicKey::random_keypair(&mut rng); let node_id = NodeId::from_key(&pk).unwrap(); - let net_address4 = NetAddress::from("9.10.11.12:7000".parse::().unwrap()); - let net_addresses = NetAddressesWithStats::from(net_address4.clone()); - let peer2: Peer = Peer::new(pk, node_id, net_addresses, PeerFlags::default()); + let net_address4 = "/ip4/9.10.11.12/tcp/7000".parse::().unwrap(); + let net_addresses = MultiaddressesWithStats::from(net_address4.clone()); + let peer2: Peer = Peer::new(pk, node_id, net_addresses, PeerFlags::default(), PeerFeatures::empty()); let (_sk, pk) = RistrettoPublicKey::random_keypair(&mut rng); let node_id = NodeId::from_key(&pk).unwrap(); - let net_address5 = NetAddress::from("13.14.15.16:6000".parse::().unwrap()); - let net_address6 = NetAddress::from("17.18.19.20:8000".parse::().unwrap()); - let mut net_addresses = NetAddressesWithStats::from(net_address5.clone()); + let net_address5 = "/ip4/13.14.15.16/tcp/6000".parse::().unwrap(); + let net_address6 = "/ip4/17.18.19.20/tcp/8000".parse::().unwrap(); + let mut net_addresses = MultiaddressesWithStats::from(net_address5.clone()); net_addresses.add_net_address(&net_address6); - let peer3 = Peer::new(pk, node_id, net_addresses, PeerFlags::default()); + let peer3 = Peer::new(pk, node_id, net_addresses, PeerFlags::default(), PeerFeatures::empty()); // Create new datastore with a peer database - let database_name = "pm_test_restore"; // Note: every test should have unique database + let mut db = Some(HashmapDatabase::new()); { - let datastore = init_datastore(database_name).unwrap(); - let peer_database = datastore.get_handle(database_name).unwrap(); - let db = LMDBWrapper::new(Arc::new(peer_database)); - let mut peer_storage = PeerStorage::new(db).unwrap(); + let mut peer_storage = PeerStorage::new_indexed(db.take().unwrap()).unwrap(); // Test adding and searching for peers assert!(peer_storage.add_peer(peer1.clone()).is_ok()); assert!(peer_storage.add_peer(peer2.clone()).is_ok()); assert!(peer_storage.add_peer(peer3.clone()).is_ok()); - assert_eq!(peer_storage.peers.size().unwrap(), 3); - assert!(peer_storage.find_with_public_key(&peer1.public_key).is_ok()); - assert!(peer_storage.find_with_public_key(&peer2.public_key).is_ok()); - assert!(peer_storage.find_with_public_key(&peer3.public_key).is_ok()); + assert_eq!(peer_storage.peer_db.size().unwrap(), 3); + assert!(peer_storage.find_by_public_key(&peer1.public_key).is_ok()); + assert!(peer_storage.find_by_public_key(&peer2.public_key).is_ok()); + assert!(peer_storage.find_by_public_key(&peer3.public_key).is_ok()); + db = Some(peer_storage.peer_db); } // Restore from existing database - let datastore = init_datastore(database_name).unwrap(); - let peer_database = datastore.get_handle(database_name).unwrap(); - let db = LMDBWrapper::new(Arc::new(peer_database)); - let peer_storage = PeerStorage::new(db).unwrap(); - - assert_eq!(peer_storage.peers.size().unwrap(), 3); - assert!(peer_storage.find_with_public_key(&peer1.public_key).is_ok()); - assert!(peer_storage.find_with_public_key(&peer2.public_key).is_ok()); - assert!(peer_storage.find_with_public_key(&peer3.public_key).is_ok()); + let peer_storage = PeerStorage::new_indexed(db.take().unwrap()).unwrap(); - clean_up_datastore(database_name); + assert_eq!(peer_storage.peer_db.size().unwrap(), 3); + assert!(peer_storage.find_by_public_key(&peer1.public_key).is_ok()); + assert!(peer_storage.find_by_public_key(&peer2.public_key).is_ok()); + assert!(peer_storage.find_by_public_key(&peer3.public_key).is_ok()); } #[test] fn test_add_delete_find_peer() { - let mut peer_storage = PeerStorage::new(HMapDatabase::new()).unwrap(); + let mut peer_storage = PeerStorage::new_indexed(HashmapDatabase::new()).unwrap(); // Create Peers - let mut rng = rand::OsRng::new().unwrap(); + let mut rng = rand::rngs::OsRng; let (_sk, pk) = RistrettoPublicKey::random_keypair(&mut rng); let node_id = NodeId::from_key(&pk).unwrap(); - let net_address1 = NetAddress::from("1.2.3.4:8000".parse::().unwrap()); - let net_address2 = NetAddress::from("5.6.7.8:8000".parse::().unwrap()); - let net_address3 = NetAddress::from("5.6.7.8:7000".parse::().unwrap()); - let mut net_addresses = NetAddressesWithStats::from(net_address1.clone()); + let net_address1 = "/ip4/1.2.3.4/tcp/8000".parse::().unwrap(); + let net_address2 = "/ip4/5.6.7.8/tcp/8000".parse::().unwrap(); + let net_address3 = "/ip4/5.6.7.8/tcp/7000".parse::().unwrap(); + let mut net_addresses = MultiaddressesWithStats::from(net_address1.clone()); net_addresses.add_net_address(&net_address2); net_addresses.add_net_address(&net_address3); - let peer1 = Peer::new(pk, node_id, net_addresses, PeerFlags::default()); + let peer1 = Peer::new(pk, node_id, net_addresses, PeerFlags::default(), PeerFeatures::empty()); let (_sk, pk) = RistrettoPublicKey::random_keypair(&mut rng); let node_id = NodeId::from_key(&pk).unwrap(); - let net_address4 = NetAddress::from("9.10.11.12:7000".parse::().unwrap()); - let net_addresses = NetAddressesWithStats::from(net_address4.clone()); - let peer2: Peer = Peer::new(pk, node_id, net_addresses, PeerFlags::default()); + let net_address4 = "/ip4/9.10.11.12/tcp/7000".parse::().unwrap(); + let net_addresses = MultiaddressesWithStats::from(net_address4.clone()); + let peer2: Peer = Peer::new(pk, node_id, net_addresses, PeerFlags::default(), PeerFeatures::empty()); let (_sk, pk) = RistrettoPublicKey::random_keypair(&mut rng); let node_id = NodeId::from_key(&pk).unwrap(); - let net_address5 = NetAddress::from("13.14.15.16:6000".parse::().unwrap()); - let net_address6 = NetAddress::from("17.18.19.20:8000".parse::().unwrap()); - let mut net_addresses = NetAddressesWithStats::from(net_address5.clone()); + let net_address5 = "/ip4/13.14.15.16/tcp/6000".parse::().unwrap(); + let net_address6 = "/ip4/17.18.19.20/tcp/8000".parse::().unwrap(); + let mut net_addresses = MultiaddressesWithStats::from(net_address5.clone()); net_addresses.add_net_address(&net_address6); - let peer3 = Peer::new(pk, node_id, net_addresses, PeerFlags::default()); + let peer3 = Peer::new(pk, node_id, net_addresses, PeerFlags::default(), PeerFeatures::empty()); // Test adding and searching for peers assert!(peer_storage.add_peer(peer1.clone()).is_ok()); assert!(peer_storage.add_peer(peer2.clone()).is_ok()); assert!(peer_storage.add_peer(peer3.clone()).is_ok()); - assert_eq!(peer_storage.peers.len().unwrap(), 3); + assert_eq!(peer_storage.peer_db.len().unwrap(), 3); assert_eq!( - peer_storage.find_with_public_key(&peer1.public_key).unwrap().public_key, + peer_storage.find_by_public_key(&peer1.public_key).unwrap().public_key, peer1.public_key ); assert_eq!( - peer_storage.find_with_public_key(&peer2.public_key).unwrap().public_key, + peer_storage.find_by_public_key(&peer2.public_key).unwrap().public_key, peer2.public_key ); assert_eq!( - peer_storage.find_with_public_key(&peer3.public_key).unwrap().public_key, + peer_storage.find_by_public_key(&peer3.public_key).unwrap().public_key, peer3.public_key ); assert_eq!( - peer_storage.find_with_node_id(&peer1.node_id).unwrap().node_id, + peer_storage.find_by_node_id(&peer1.node_id).unwrap().node_id, peer1.node_id ); assert_eq!( - peer_storage.find_with_node_id(&peer2.node_id).unwrap().node_id, + peer_storage.find_by_node_id(&peer2.node_id).unwrap().node_id, peer2.node_id ); assert_eq!( - peer_storage.find_with_node_id(&peer3.node_id).unwrap().node_id, + peer_storage.find_by_node_id(&peer3.node_id).unwrap().node_id, peer3.node_id ); - assert_eq!( - peer_storage.find_with_net_address(&net_address1).unwrap().public_key, - peer1.public_key - ); - assert_eq!( - peer_storage.find_with_net_address(&net_address2).unwrap().public_key, - peer1.public_key - ); - assert_eq!( - peer_storage.find_with_net_address(&net_address3).unwrap().public_key, - peer1.public_key - ); - assert_eq!( - peer_storage.find_with_net_address(&net_address4).unwrap().public_key, - peer2.public_key - ); - assert_eq!( - peer_storage.find_with_net_address(&net_address5).unwrap().public_key, - peer3.public_key - ); - assert_eq!( - peer_storage.find_with_net_address(&net_address6).unwrap().public_key, - peer3.public_key - ); - - assert!(peer_storage.find_with_public_key(&peer1.public_key).is_ok()); - assert!(peer_storage.find_with_public_key(&peer2.public_key).is_ok()); - assert!(peer_storage.find_with_public_key(&peer3.public_key).is_ok()); + assert!(peer_storage.find_by_public_key(&peer1.public_key).is_ok()); + assert!(peer_storage.find_by_public_key(&peer2.public_key).is_ok()); + assert!(peer_storage.find_by_public_key(&peer3.public_key).is_ok()); // Test delete of border case peer assert!(peer_storage.delete_peer(&peer3.node_id).is_ok()); - assert_eq!(peer_storage.peers.len().unwrap(), 2); + assert_eq!(peer_storage.peer_db.len().unwrap(), 2); assert_eq!( - peer_storage.find_with_public_key(&peer1.public_key).unwrap().public_key, + peer_storage.find_by_public_key(&peer1.public_key).unwrap().public_key, peer1.public_key ); assert_eq!( - peer_storage.find_with_public_key(&peer2.public_key).unwrap().public_key, + peer_storage.find_by_public_key(&peer2.public_key).unwrap().public_key, peer2.public_key ); - assert!(peer_storage.find_with_public_key(&peer3.public_key).is_err()); + assert!(peer_storage.find_by_public_key(&peer3.public_key).is_err()); assert_eq!( - peer_storage.find_with_node_id(&peer1.node_id).unwrap().node_id, + peer_storage.find_by_node_id(&peer1.node_id).unwrap().node_id, peer1.node_id ); assert_eq!( - peer_storage.find_with_node_id(&peer2.node_id).unwrap().node_id, + peer_storage.find_by_node_id(&peer2.node_id).unwrap().node_id, peer2.node_id ); - assert!(peer_storage.find_with_node_id(&peer3.node_id).is_err()); - - assert_eq!( - peer_storage.find_with_net_address(&net_address1).unwrap().public_key, - peer1.public_key - ); - assert_eq!( - peer_storage.find_with_net_address(&net_address2).unwrap().public_key, - peer1.public_key - ); - assert_eq!( - peer_storage.find_with_net_address(&net_address3).unwrap().public_key, - peer1.public_key - ); - assert_eq!( - peer_storage.find_with_net_address(&net_address4).unwrap().public_key, - peer2.public_key - ); - assert!(peer_storage.find_with_net_address(&net_address5).is_err()); - assert!(peer_storage.find_with_net_address(&net_address6).is_err()); + assert!(peer_storage.find_by_node_id(&peer3.node_id).is_err()); - assert!(peer_storage.find_with_public_key(&peer1.public_key).is_ok()); - assert!(peer_storage.find_with_public_key(&peer2.public_key).is_ok()); - assert!(peer_storage.find_with_public_key(&peer3.public_key).is_err()); + assert!(peer_storage.find_by_public_key(&peer1.public_key).is_ok()); + assert!(peer_storage.find_by_public_key(&peer2.public_key).is_ok()); + assert!(peer_storage.find_by_public_key(&peer3.public_key).is_err()); // Test of delete with moving behaviour assert!(peer_storage.add_peer(peer3.clone()).is_ok()); assert!(peer_storage.delete_peer(&peer2.node_id).is_ok()); - assert_eq!(peer_storage.peers.len().unwrap(), 2); + assert_eq!(peer_storage.peer_db.len().unwrap(), 2); assert_eq!( - peer_storage.find_with_public_key(&peer1.public_key).unwrap().public_key, + peer_storage.find_by_public_key(&peer1.public_key).unwrap().public_key, peer1.public_key ); - assert!(peer_storage.find_with_public_key(&peer2.public_key).is_err()); + assert!(peer_storage.find_by_public_key(&peer2.public_key).is_err()); assert_eq!( - peer_storage.find_with_public_key(&peer3.public_key).unwrap().public_key, + peer_storage.find_by_public_key(&peer3.public_key).unwrap().public_key, peer3.public_key ); assert_eq!( - peer_storage.find_with_node_id(&peer1.node_id).unwrap().node_id, + peer_storage.find_by_node_id(&peer1.node_id).unwrap().node_id, peer1.node_id ); - assert!(peer_storage.find_with_node_id(&peer2.node_id).is_err()); + assert!(peer_storage.find_by_node_id(&peer2.node_id).is_err()); assert_eq!( - peer_storage.find_with_node_id(&peer3.node_id).unwrap().node_id, + peer_storage.find_by_node_id(&peer3.node_id).unwrap().node_id, peer3.node_id ); - assert_eq!( - peer_storage.find_with_net_address(&net_address1).unwrap().public_key, - peer1.public_key - ); - assert_eq!( - peer_storage.find_with_net_address(&net_address2).unwrap().public_key, - peer1.public_key - ); - assert_eq!( - peer_storage.find_with_net_address(&net_address3).unwrap().public_key, - peer1.public_key - ); - assert!(peer_storage.find_with_net_address(&net_address4).is_err()); - assert_eq!( - peer_storage.find_with_net_address(&net_address5).unwrap().public_key, - peer3.public_key - ); - assert_eq!( - peer_storage.find_with_net_address(&net_address6).unwrap().public_key, - peer3.public_key - ); - - assert!(peer_storage.find_with_public_key(&peer1.public_key).is_ok()); - assert!(peer_storage.find_with_public_key(&peer2.public_key).is_err()); - assert!(peer_storage.find_with_public_key(&peer3.public_key).is_ok()); + assert!(peer_storage.find_by_public_key(&peer1.public_key).is_ok()); + assert!(peer_storage.find_by_public_key(&peer2.public_key).is_err()); + assert!(peer_storage.find_by_public_key(&peer3.public_key).is_ok()); } } diff --git a/comms/src/pipeline/builder.rs b/comms/src/pipeline/builder.rs new file mode 100644 index 0000000000..5981c11844 --- /dev/null +++ b/comms/src/pipeline/builder.rs @@ -0,0 +1,188 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + message::{InboundMessage, OutboundMessage}, + pipeline::SinkService, +}; +use derive_error::Error; +use futures::channel::mpsc; +use tower::Service; + +const DEFAULT_MAX_CONCURRENT_TASKS: usize = 50; +const DEFAULT_OUTBOUND_BUFFER_SIZE: usize = 50; + +type MpscSinkService = SinkService>; + +#[derive(Default)] +pub struct Builder { + max_concurrent_inbound_tasks: usize, + outbound_buffer_size: usize, + inbound: Option, + outbound_rx: Option>, + outbound_pipeline_factory: Option TOutSvc>>, +} + +impl Builder<(), (), ()> { + pub fn new() -> Self { + Self { + max_concurrent_inbound_tasks: DEFAULT_MAX_CONCURRENT_TASKS, + outbound_buffer_size: DEFAULT_OUTBOUND_BUFFER_SIZE, + inbound: None, + outbound_rx: None, + outbound_pipeline_factory: None, + } + } +} + +impl Builder { + pub fn max_concurrent_inbound_tasks(mut self, max_tasks: usize) -> Self { + self.max_concurrent_inbound_tasks = max_tasks; + self + } + + pub fn outbound_buffer_size(mut self, buf_size: usize) -> Self { + self.outbound_buffer_size = buf_size; + self + } + + pub fn with_outbound_pipeline(self, receiver: mpsc::Receiver, factory: F) -> Builder + where + // Factory function takes in a SinkService and returns a new composed service + F: FnOnce(MpscSinkService) -> S + 'static, + S: Service + Clone + Send + 'static, + { + Builder { + outbound_rx: Some(receiver), + outbound_pipeline_factory: Some(Box::new(factory)), + + max_concurrent_inbound_tasks: self.max_concurrent_inbound_tasks, + inbound: self.inbound, + outbound_buffer_size: self.outbound_buffer_size, + } + } + + pub fn with_inbound_pipeline(self, inbound: S) -> Builder + where S: Service + Clone + Send + 'static { + Builder { + inbound: Some(inbound), + + max_concurrent_inbound_tasks: self.max_concurrent_inbound_tasks, + outbound_rx: self.outbound_rx, + outbound_pipeline_factory: self.outbound_pipeline_factory, + outbound_buffer_size: self.outbound_buffer_size, + } + } +} + +impl Builder +where + TOutSvc: Service + Clone + Send + 'static, + TInSvc: Service + Clone + Send + 'static, +{ + fn build_outbound( + &mut self, + ) -> Result, TOutSvc>, PipelineBuilderError> { + let (out_sender, out_receiver) = mpsc::channel(self.outbound_buffer_size); + + let in_receiver = self + .outbound_rx + .take() + .ok_or_else(|| PipelineBuilderError::OutboundPipelineNotProvided)?; + let factory = self + .outbound_pipeline_factory + .take() + .ok_or_else(|| PipelineBuilderError::OutboundPipelineNotProvided)?; + let sink_service = SinkService::new(out_sender); + let pipeline = (factory)(sink_service); + Ok(OutboundPipelineConfig { + in_receiver, + pipeline, + out_receiver, + }) + } + + pub fn try_finish(mut self) -> Result, PipelineBuilderError> { + let inbound = self + .inbound + .take() + .ok_or_else(|| PipelineBuilderError::InboundNotProvided)?; + let outbound = self.build_outbound()?; + + Ok(Config { + max_concurrent_inbound_tasks: self.max_concurrent_inbound_tasks, + inbound, + outbound, + }) + } + + pub fn finish(self) -> Config { + self.try_finish().unwrap() + } +} + +pub struct OutboundPipelineConfig { + /// Messages read from this stream are passed to the pipeline + pub in_receiver: TInStream, + /// Receiver of `OutboundMessage`s coming from the pipeline + pub out_receiver: mpsc::Receiver, + /// The pipeline (`tower::Service`) to run for each in_stream message + pub pipeline: TPipeline, +} + +pub struct Config { + pub max_concurrent_inbound_tasks: usize, + pub inbound: TInSvc, + pub outbound: OutboundPipelineConfig, TOutSvc>, +} + +#[derive(Debug, Error)] +pub enum PipelineBuilderError { + /// Inbound pipeline was not provided + InboundNotProvided, + /// Outbound pipeline was not provided + OutboundPipelineNotProvided, +} + +#[cfg(test)] +mod test { + use super::*; + use futures::future; + use std::convert::identity; + use tower::service_fn; + + #[test] + fn minimal_usage() { + // Called when a message is sent on the given channel. + let (_, rx) = mpsc::channel::(1); + + let config = Builder::new() + .max_concurrent_inbound_tasks(50) + // Forward all messages on rx_out to the provided SinkService + .with_outbound_pipeline(rx, identity) + // Discard all inbound messages + .with_inbound_pipeline(service_fn(|_| future::ready(Result::<_, ()>::Ok(())))) + .finish(); + + assert_eq!(config.max_concurrent_inbound_tasks, 50); + } +} diff --git a/comms/src/pipeline/inbound.rs b/comms/src/pipeline/inbound.rs new file mode 100644 index 0000000000..eae6e75396 --- /dev/null +++ b/comms/src/pipeline/inbound.rs @@ -0,0 +1,108 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::bounded_executor::BoundedExecutor; +use futures::{stream::FusedStream, Stream, StreamExt}; +use log::*; +use std::fmt::Debug; +use tower::{Service, ServiceExt}; + +const LOG_TARGET: &str = "comms::pipeline::inbound"; + +/// Calls a Service with every item received from a Stream. +/// The difference between this can ServiceExt::call_all is +/// that ServicePipeline doesn't keep the result of the service +/// call and that it spawns a task for each incoming item. +pub struct Inbound { + executor: BoundedExecutor, + service: TSvc, + stream: TStream, +} + +impl Inbound +where + TStream: Stream + FusedStream + Unpin + Send + 'static, + TStream::Item: Send + 'static, + TSvc: Service + Clone + Send + 'static, + TSvc::Error: Debug + Send, + TSvc::Future: Send, +{ + pub fn new(executor: BoundedExecutor, stream: TStream, service: TSvc) -> Self { + Self { + executor, + stream, + service, + } + } + + pub async fn run(mut self) { + while let Some(item) = self.stream.next().await { + let service = self.service.clone(); + // Call the service in it's own spawned task + self.executor + .spawn(async move { + if let Err(err) = service.oneshot(item).await { + error!(target: LOG_TARGET, "Inbound pipeline error: {:?}", err); + } + }) + .await; + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use futures::{channel::mpsc, future, stream}; + use std::time::Duration; + use tari_test_utils::collect_stream; + use tokio::{runtime::Handle, time}; + use tower::service_fn; + + #[tokio_macros::test_basic] + async fn run() { + let items = vec![1, 2, 3, 4, 5, 6]; + let stream = stream::iter(items.clone()).fuse(); + + let (mut out_tx, out_rx) = mpsc::channel(items.len()); + + let executor = Handle::current(); + let pipeline = Inbound::new( + BoundedExecutor::new(executor.clone(), 1), + stream, + service_fn(move |req| { + out_tx.try_send(req).unwrap(); + future::ready(Result::<_, ()>::Ok(())) + }), + ); + let spawned_task = executor.spawn(pipeline.run()); + + let received = collect_stream!(out_rx, take = items.len(), timeout = Duration::from_secs(10)); + assert!(received.iter().all(|i| items.contains(i))); + + // Check that this task ends because the stream has closed + time::timeout(Duration::from_secs(5), spawned_task) + .await + .unwrap() + .unwrap(); + } +} diff --git a/comms/src/pipeline/mod.rs b/comms/src/pipeline/mod.rs new file mode 100644 index 0000000000..f8e6271b1a --- /dev/null +++ b/comms/src/pipeline/mod.rs @@ -0,0 +1,45 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +//! # Comms Pipeline +//! +//! Comms Pipeline contains the middleware layers that can be composed when processing +//! inbound and outbound comms messages. +//! +//! For example, should you want your messages to be encrypted, you'll add the EncryptionLayer to +//! the outbound middleware stack and the DecryptionLayer to the inbound stack. +//! +//! Middlewares use `tower_layer` and `tower_service`. A Pipeline is simply any service which +//! is `Service`. This service will usually +//! be composed of other services by using the `tower_util::ServiceBuilder`. + +mod builder; +pub use builder::{Builder, Config, PipelineBuilderError}; + +mod sink; +pub use sink::SinkService; + +mod inbound; +pub(crate) use inbound::Inbound; + +mod outbound; +pub(crate) use outbound::Outbound; diff --git a/comms/src/pipeline/outbound.rs b/comms/src/pipeline/outbound.rs new file mode 100644 index 0000000000..d63fb4fd59 --- /dev/null +++ b/comms/src/pipeline/outbound.rs @@ -0,0 +1,166 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + message::OutboundMessage, + pipeline::builder::OutboundPipelineConfig, + protocol::messaging::MessagingRequest, +}; +use futures::{channel::mpsc, future, future::Either, stream::FusedStream, SinkExt, Stream, StreamExt}; +use log::*; +use std::fmt::Debug; +use tokio::runtime; +use tower::{Service, ServiceExt}; + +const LOG_TARGET: &str = "comms::pipeline::outbound"; + +pub struct Outbound { + /// Executor used to spawn a pipeline for each received item on the stream + executor: runtime::Handle, + /// Outbound pipeline configuration containing the pipeline and it's in and out streams + config: OutboundPipelineConfig, + /// Request sender for Messaging + messaging_request_tx: mpsc::Sender, +} + +impl Outbound +where + TStream: Stream + FusedStream + Unpin + Send + 'static, + TStream::Item: Send + 'static, + TPipeline: Service + Clone + Send + 'static, + TPipeline::Error: Debug + Send, + TPipeline::Future: Send, +{ + pub fn new( + executor: runtime::Handle, + config: OutboundPipelineConfig, + messaging_request_tx: mpsc::Sender, + ) -> Self + { + Self { + executor, + config, + messaging_request_tx, + } + } + + pub async fn run(mut self) { + loop { + let either = future::select(self.config.in_receiver.next(), self.config.out_receiver.next()).await; + match either { + // Pipeline IN received a message. Spawn a new task for the pipeline + Either::Left((Some(msg), _)) => { + let pipeline = self.config.pipeline.clone(); + self.executor.spawn(async move { + if let Err(err) = pipeline.oneshot(msg).await { + error!(target: LOG_TARGET, "Outbound pipeline error: {:?}", err); + } + }); + }, + // Pipeline IN channel closed + Either::Left((None, _)) => { + info!( + target: LOG_TARGET, + "Outbound pipeline is shutting down because the in channel closed" + ); + break; + }, + // Pipeline OUT received a message + Either::Right((Some(out_msg), _)) => { + if self.messaging_request_tx.is_closed() { + // MessagingRequest channel closed + break; + } + self.send_messaging_request(out_msg).await; + }, + // Pipeline OUT channel closed + Either::Right((None, _)) => { + info!( + target: LOG_TARGET, + "Outbound pipeline is shutting down because the out channel closed" + ); + break; + }, + } + } + } + + async fn send_messaging_request(&mut self, out_msg: OutboundMessage) { + let msg_req = MessagingRequest::SendMessage(out_msg); + if let Err(err) = self.messaging_request_tx.send(msg_req).await { + error!( + target: LOG_TARGET, + "Failed to send OutboundMessage to Messaging protocol because '{}'", err + ); + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::pipeline::SinkService; + use bytes::Bytes; + use futures::stream; + use std::time::Duration; + use tari_test_utils::{collect_stream, unpack_enum}; + use tokio::{runtime::Handle, time}; + + #[tokio_macros::test_basic] + async fn run() { + const NUM_ITEMS: usize = 10; + let items = (0..NUM_ITEMS).map(|i| { + OutboundMessage::new( + Default::default(), + Default::default(), + Bytes::copy_from_slice(&i.to_be_bytes()), + ) + }); + let stream = stream::iter(items).fuse(); + let (out_tx, out_rx) = mpsc::channel(NUM_ITEMS); + let (msg_tx, msg_rx) = mpsc::channel(NUM_ITEMS); + let executor = Handle::current(); + + let pipeline = Outbound::new( + executor.clone(), + OutboundPipelineConfig { + in_receiver: stream, + out_receiver: out_rx, + pipeline: SinkService::new(out_tx), + }, + msg_tx, + ); + + let spawned_task = executor.spawn(pipeline.run()); + + let requests = collect_stream!(msg_rx, take = NUM_ITEMS, timeout = Duration::from_millis(5)); + for req in requests { + unpack_enum!(MessagingRequest::SendMessage(_o) = req); + } + + // Check that this task ends because the stream has closed + time::timeout(Duration::from_secs(5), spawned_task) + .await + .unwrap() + .unwrap(); + } +} diff --git a/comms/src/pipeline/sink.rs b/comms/src/pipeline/sink.rs new file mode 100644 index 0000000000..d9a211688c --- /dev/null +++ b/comms/src/pipeline/sink.rs @@ -0,0 +1,61 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use futures::{task::Context, Future, Sink, SinkExt}; +use log::*; +use std::{error::Error, pin::Pin, task::Poll}; +use tower::Service; + +const LOG_TARGET: &str = "comms::pipeline::sink"; + +/// A service which forwards and messages it gets to the given Sink +#[derive(Clone)] +pub struct SinkService(TSink); + +impl SinkService { + pub fn new(sink: TSink) -> Self { + SinkService(sink) + } +} + +impl Service for SinkService +where + TSink: Sink + Unpin + Clone + 'static, + TSink::Error: Error + Send + Sync + 'static, +{ + // A boxed error gives the most flexibility when building a pipeline. Using TSink::Error, in practise, requires a + // conversion between the error being used for other services and TSink::Error. + type Error = Box; + type Response = (); + + type Future = impl Future>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.0).poll_ready(cx).map_err(Into::into) + } + + fn call(&mut self, item: T) -> Self::Future { + let mut sink = self.0.clone(); + trace!(target: LOG_TARGET, "Sending item to sink"); + async move { sink.send(item).await.map_err(Into::into) } + } +} diff --git a/comms/src/proto/control_service/header.proto b/comms/src/proto/control_service/header.proto new file mode 100644 index 0000000000..3ad1c403bd --- /dev/null +++ b/comms/src/proto/control_service/header.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; + +package tari.comms.control_service; + +enum MessageType { + MessageTypeNone = 0; + MessageTypeRequestConnection = 1; + MessageTypePing = 2; + MessageTypeAcceptPeerConnection = 3; + MessageTypeRejectPeerConnection = 4; + MessageTypePong = 5; + MessageTypeConnectRequestOutcome = 6; +} + +message MessageHeader { + MessageType message_type = 1; +} diff --git a/comms/src/proto/control_service/ping.proto b/comms/src/proto/control_service/ping.proto new file mode 100644 index 0000000000..dc57a07082 --- /dev/null +++ b/comms/src/proto/control_service/ping.proto @@ -0,0 +1,6 @@ +syntax = "proto3"; + +package tari.comms.control_service; + +message PingMessage { } +message PongMessage { } diff --git a/comms/src/proto/control_service/request_connection.proto b/comms/src/proto/control_service/request_connection.proto new file mode 100644 index 0000000000..ec50a53d9a --- /dev/null +++ b/comms/src/proto/control_service/request_connection.proto @@ -0,0 +1,35 @@ +syntax = "proto3"; + +package tari.comms.control_service; + +message RequestConnectionMessage { + string control_service_address = 1; + bytes node_id = 2; + uint64 features = 3; +} + +// Represents the reason for a peer connection request being rejected +enum RejectReason { + // No reject reason given + RejectReasonNone = 0; + // Peer already has an existing active peer connection + RejectReasonExistingConnection = 1; + // A connection collision has been detected, foreign node should abandon the connection attempt + RejectReasonCollisionDetected = 2; +} + +// Represents an outcome for the request to establish a new [PeerConnection]. +// +// [PeerConnection]: ../../connection/peer_connection/index.html +message RequestConnectionOutcome { + // True if the connection is accepted, otherwise false + bool accepted = 1; + // The zeroMQ Curve public key to use for the peer connection + bytes curve_public_key = 2; + /// The address of the open port to connect to + string address = 3; + /// If this connection was not accepted, the rejection reason is given + RejectReason reject_reason = 4; + /// The identity to use when connecting + bytes identity = 5; +} \ No newline at end of file diff --git a/comms/src/proto/envelope.proto b/comms/src/proto/envelope.proto new file mode 100644 index 0000000000..56d37f31b7 --- /dev/null +++ b/comms/src/proto/envelope.proto @@ -0,0 +1,24 @@ +syntax = "proto3"; + +package tari.comms.envelope; + +/// Represents a message which is about to go on or has just come off the wire. +/// As described in [RFC-0172](https://rfc.tari.com/RFC-0172_PeerToPeerMessagingProtocol.html#messaging-structure) +message Envelope { + uint32 version = 1; + EnvelopeHeader header = 3; + bytes body = 4; +} + +message EnvelopeHeader { + bytes public_key = 1; + bytes signature = 2; + uint32 flags = 3; +} + +// Parts contained within an Envelope. This is used to tell if an encrypted +// message was successfully decrypted, by decrypting the envelope body and checking +// if deserialization succeeds. +message EnvelopeBody { + repeated bytes parts = 1; +} diff --git a/comms/src/proto/identity.proto b/comms/src/proto/identity.proto new file mode 100644 index 0000000000..76498492fd --- /dev/null +++ b/comms/src/proto/identity.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +package tari.comms.identity; + +message PeerIdentityMsg { + bytes node_id = 1; + repeated string addresses = 2; + uint64 features = 3; +} diff --git a/comms/src/proto/mod.rs b/comms/src/proto/mod.rs new file mode 100644 index 0000000000..b9a2db02d5 --- /dev/null +++ b/comms/src/proto/mod.rs @@ -0,0 +1,30 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#[path = "tari.comms.envelope.rs"] +pub(crate) mod envelope; + +#[path = "tari.comms.control_service.rs"] +pub(crate) mod control_service; + +#[path = "tari.comms.identity.rs"] +pub(crate) mod identity; diff --git a/comms/src/proto/tari.comms.control_service.rs b/comms/src/proto/tari.comms.control_service.rs new file mode 100644 index 0000000000..ca33091768 --- /dev/null +++ b/comms/src/proto/tari.comms.control_service.rs @@ -0,0 +1,61 @@ +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MessageHeader { + #[prost(enumeration = "MessageType", tag = "1")] + pub message_type: i32, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum MessageType { + None = 0, + RequestConnection = 1, + Ping = 2, + AcceptPeerConnection = 3, + RejectPeerConnection = 4, + Pong = 5, + ConnectRequestOutcome = 6, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RequestConnectionMessage { + #[prost(string, tag = "1")] + pub control_service_address: std::string::String, + #[prost(bytes, tag = "2")] + pub node_id: std::vec::Vec, + #[prost(uint64, tag = "3")] + pub features: u64, +} +/// Represents an outcome for the request to establish a new [PeerConnection]. +/// +/// [PeerConnection]: ../../connection/peer_connection/index.html +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RequestConnectionOutcome { + /// True if the connection is accepted, otherwise false + #[prost(bool, tag = "1")] + pub accepted: bool, + /// The zeroMQ Curve public key to use for the peer connection + #[prost(bytes, tag = "2")] + pub curve_public_key: std::vec::Vec, + //// The address of the open port to connect to + #[prost(string, tag = "3")] + pub address: std::string::String, + //// If this connection was not accepted, the rejection reason is given + #[prost(enumeration = "RejectReason", tag = "4")] + pub reject_reason: i32, + //// The identity to use when connecting + #[prost(bytes, tag = "5")] + pub identity: std::vec::Vec, +} +/// Represents the reason for a peer connection request being rejected +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum RejectReason { + /// No reject reason given + None = 0, + /// Peer already has an existing active peer connection + ExistingConnection = 1, + /// A connection collision has been detected, foreign node should abandon the connection attempt + CollisionDetected = 2, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PingMessage {} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PongMessage {} diff --git a/comms/src/proto/tari.comms.envelope.rs b/comms/src/proto/tari.comms.envelope.rs new file mode 100644 index 0000000000..f445fabea7 --- /dev/null +++ b/comms/src/proto/tari.comms.envelope.rs @@ -0,0 +1,28 @@ +//// Represents a message which is about to go on or has just come off the wire. +//// As described in [RFC-0172](https://rfc.tari.com/RFC-0172_PeerToPeerMessagingProtocol.html#messaging-structure) +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Envelope { + #[prost(uint32, tag = "1")] + pub version: u32, + #[prost(message, optional, tag = "3")] + pub header: ::std::option::Option, + #[prost(bytes, tag = "4")] + pub body: std::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct EnvelopeHeader { + #[prost(bytes, tag = "1")] + pub public_key: std::vec::Vec, + #[prost(bytes, tag = "2")] + pub signature: std::vec::Vec, + #[prost(uint32, tag = "3")] + pub flags: u32, +} +/// Parts contained within an Envelope. This is used to tell if an encrypted +/// message was successfully decrypted, by decrypting the envelope body and checking +/// if deserialization succeeds. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct EnvelopeBody { + #[prost(bytes, repeated, tag = "1")] + pub parts: ::std::vec::Vec>, +} diff --git a/comms/src/proto/tari.comms.identity.rs b/comms/src/proto/tari.comms.identity.rs new file mode 100644 index 0000000000..35e68152d2 --- /dev/null +++ b/comms/src/proto/tari.comms.identity.rs @@ -0,0 +1,9 @@ +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PeerIdentityMsg { + #[prost(bytes, tag = "1")] + pub node_id: std::vec::Vec, + #[prost(string, repeated, tag = "2")] + pub addresses: ::std::vec::Vec, + #[prost(uint64, tag = "3")] + pub features: u64, +} diff --git a/comms/src/protocol/error.rs b/comms/src/protocol/error.rs new file mode 100644 index 0000000000..543b09c15e --- /dev/null +++ b/comms/src/protocol/error.rs @@ -0,0 +1,39 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use derive_error::Error; +use futures::channel::mpsc; +use std::io; + +#[derive(Debug, Error)] +pub enum ProtocolError { + IoError(io::Error), + /// The ProtocolId was longer than 255 + ProtocolIdTooLong, + /// Protocol negotiation failed because the peer did not accept any protocols + ProtocolOutboundNegotiationFailed, + /// Protocol negotiation terminated by peer + ProtocolNegotiationTerminatedByPeer, + /// Protocol was not registered + ProtocolNotRegistered, + SendError(mpsc::SendError), +} diff --git a/comms/src/protocol/identity.rs b/comms/src/protocol/identity.rs new file mode 100644 index 0000000000..027fd7de1b --- /dev/null +++ b/comms/src/protocol/identity.rs @@ -0,0 +1,176 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::{ + compat::IoCompat, + connection_manager::ConnectionDirection, + message::MessageExt, + peer_manager::NodeIdentity, + proto::identity::PeerIdentityMsg, + protocol::{ProtocolError, ProtocolId, ProtocolNegotiation}, +}; +use derive_error::Error; +use futures::{AsyncRead, AsyncWrite, SinkExt, StreamExt}; +use log::*; +use prost::Message; +use std::io; +use tari_crypto::tari_utilities::ByteArray; +use tokio_util::codec::{Framed, LengthDelimitedCodec}; + +const IDENTITY_PROTOCOL: &[u8] = b"/tari/identity/1.0.0"; +const LOG_TARGET: &str = "comms::protocol::identity"; + +pub async fn identity_exchange( + node_identity: &NodeIdentity, + direction: ConnectionDirection, + mut socket: TSocket, +) -> Result +where + TSocket: AsyncRead + AsyncWrite + Unpin, +{ + // Negotiate the identity protocol + let mut negotiation = ProtocolNegotiation::new(&mut socket); + let proto = match direction { + ConnectionDirection::Outbound => { + debug!( + target: LOG_TARGET, + "[ThisNode={}] Starting Outbound identity exchange with peer.", + node_identity.node_id().short_str() + ); + negotiation + .negotiate_protocol_outbound(&[ProtocolId::from_static(IDENTITY_PROTOCOL)]) + .await? + }, + ConnectionDirection::Inbound => { + debug!( + target: LOG_TARGET, + "[ThisNode={}] Starting Inbound identity exchange with peer.", + node_identity.node_id().short_str() + ); + negotiation + .negotiate_protocol_inbound(&[ProtocolId::from_static(IDENTITY_PROTOCOL)]) + .await? + }, + }; + + debug_assert_eq!(proto, IDENTITY_PROTOCOL); + + // Create length-delimited frame codec + let mut framed = Framed::new(IoCompat::new(socket), LengthDelimitedCodec::new()); + + // Send this node's identity + let msg_bytes = PeerIdentityMsg { + node_id: node_identity.node_id().to_vec(), + addresses: vec![node_identity.public_address().to_string()], + features: node_identity.features().bits(), + } + .to_encoded_bytes() + .map_err(|_| IdentityProtocolError::ProtobufEncodingError)?; + + framed.send(msg_bytes.into()).await?; + framed.close().await?; + + // Receive the connecting nodes identity + let msg_bytes = framed + .next() + .await + .ok_or_else(|| IdentityProtocolError::PeerUnexpectedCloseConnection)??; + let identity_msg = PeerIdentityMsg::decode(msg_bytes)?; + + Ok(identity_msg) +} + +#[derive(Debug, Error, Clone)] +pub enum IdentityProtocolError { + #[error(msg_embedded, no_from, non_std)] + IoError(String), + #[error(msg_embedded, no_from, non_std)] + ProtocolError(String), + #[error(msg_embedded, no_from, non_std)] + ProtobufDecodeError(String), + /// Failed to encode protobuf message + ProtobufEncodingError, + /// Peer unexpectedly closed the connection + PeerUnexpectedCloseConnection, +} + +impl From for IdentityProtocolError { + fn from(err: ProtocolError) -> Self { + IdentityProtocolError::ProtocolError(err.to_string()) + } +} + +impl From for IdentityProtocolError { + fn from(err: io::Error) -> Self { + IdentityProtocolError::IoError(err.to_string()) + } +} + +impl From for IdentityProtocolError { + fn from(err: prost::DecodeError) -> Self { + IdentityProtocolError::ProtobufDecodeError(err.to_string()) + } +} + +#[cfg(test)] +mod test { + use crate::{ + connection_manager::ConnectionDirection, + peer_manager::PeerFeatures, + test_utils::node_identity::build_node_identity, + transports::{MemoryTransport, Transport}, + }; + use futures::{future, StreamExt}; + use tari_crypto::tari_utilities::ByteArray; + + #[tokio_macros::test_basic] + async fn identity_exchange() { + let transport = MemoryTransport; + let addr = "/memory/0".parse().unwrap(); + let (mut listener, addr) = transport.listen(addr).unwrap().await.unwrap(); + + let (out_sock, in_sock) = future::join(transport.dial(addr).unwrap(), listener.next()).await; + + let out_sock = out_sock.unwrap(); + let in_sock = in_sock.unwrap().map(|(f, _)| f).unwrap().await.unwrap(); + + let node_identity1 = build_node_identity(PeerFeatures::COMMUNICATION_NODE); + let node_identity2 = build_node_identity(PeerFeatures::COMMUNICATION_CLIENT); + + let (result1, result2) = future::join( + super::identity_exchange(&node_identity1, ConnectionDirection::Inbound, in_sock), + super::identity_exchange(&node_identity2, ConnectionDirection::Outbound, out_sock), + ) + .await; + + // Test node 1 gets node 2's details and vice versa + let identity2 = result1.unwrap(); + let identity1 = result2.unwrap(); + + assert_eq!(identity1.node_id, node_identity1.node_id().to_vec()); + assert_eq!(identity1.features, node_identity1.features().bits()); + assert_eq!(identity1.addresses, vec![node_identity1.public_address().to_string()]); + + assert_eq!(identity2.node_id, node_identity2.node_id().to_vec()); + assert_eq!(identity2.features, node_identity2.features().bits()); + assert_eq!(identity2.addresses, vec![node_identity2.public_address().to_string()]); + } +} diff --git a/infrastructure/crypto/src/ristretto/test_common.rs b/comms/src/protocol/messaging/error.rs similarity index 57% rename from infrastructure/crypto/src/ristretto/test_common.rs rename to comms/src/protocol/messaging/error.rs index e69cf9a007..f8b6d14829 100644 --- a/infrastructure/crypto/src/ristretto/test_common.rs +++ b/comms/src/protocol/messaging/error.rs @@ -1,4 +1,4 @@ -// Copyright 2019 The Tari Project +// Copyright 2020, The Tari Project // // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the // following conditions are met: @@ -21,13 +21,36 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::{ - keys::{PublicKey, SecretKey}, - ristretto::{RistrettoPublicKey, RistrettoSecretKey}, + connection_manager::PeerConnectionError, + message::{MessageError, OutboundMessage}, + peer_manager::PeerManagerError, + protocol::ProtocolError, }; -use rand; -pub fn get_keypair() -> (RistrettoSecretKey, RistrettoPublicKey) { - let mut rng = rand::OsRng::new().unwrap(); - let k = RistrettoSecretKey::random(&mut rng); - let pk = RistrettoPublicKey::from_secret_key(&k); - (k, pk) +use derive_error::Error; + +#[derive(Debug, Error)] +pub enum InboundMessagingError { + PeerManagerError(PeerManagerError), + /// Inbound message signatures are invalid + InvalidMessageSignature, + /// The received envelope is invalid + InvalidEnvelope, + /// The connected peer sent a public key which did not match the public key of the connected peer + PeerPublicKeyMismatch, + /// Failed to decode message + MessageDecodeError(prost::DecodeError), + MessageError(MessageError), +} +#[derive(Debug, Error)] +pub enum MessagingProtocolError { + /// Failed to send message + #[error(no_from, non_std)] + MessageSendFailed(OutboundMessage), // Msg returned to sender + ProtocolError(ProtocolError), + PeerConnectionError(PeerConnectionError), + /// Failed to dial peer + PeerDialFailed, + /// Failure when sending on an outbound substream + OutboundSubstreamFailure, + MessageError(MessageError), } diff --git a/comms/src/protocol/messaging/inbound.rs b/comms/src/protocol/messaging/inbound.rs new file mode 100644 index 0000000000..b66d294ab6 --- /dev/null +++ b/comms/src/protocol/messaging/inbound.rs @@ -0,0 +1,74 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + message::{Envelope, InboundMessage}, + peer_manager::Peer, + protocol::messaging::error::InboundMessagingError, +}; +use bytes::Bytes; +use log::*; +use prost::Message; +use std::{convert::TryInto, sync::Arc}; + +const LOG_TARGET: &str = "comms::protocol::messaging::inbound"; + +pub struct InboundMessaging; + +impl InboundMessaging { + /// Process a single received message from its raw serialized form i.e. a FrameSet + pub async fn process_message( + &self, + source_peer: Arc, + msg: &mut Bytes, + ) -> Result + { + let envelope = Envelope::decode(msg)?; + + let public_key = envelope + .get_public_key() + .ok_or_else(|| InboundMessagingError::InvalidEnvelope)?; + + trace!( + target: LOG_TARGET, + "Received message envelope version {} from peer '{}'", + envelope.version, + source_peer.node_id.short_str() + ); + + if source_peer.public_key != public_key { + return Err(InboundMessagingError::PeerPublicKeyMismatch); + } + + if !envelope.verify_signature()? { + return Err(InboundMessagingError::InvalidMessageSignature); + } + + // -- Message is authenticated -- + let Envelope { header, body, .. } = envelope; + let header = header.expect("already checked").try_into().expect("already checked"); + + let inbound_message = InboundMessage::new(source_peer, header, body.into()); + + Ok(inbound_message) + } +} diff --git a/comms/src/protocol/messaging/messaging.rs b/comms/src/protocol/messaging/messaging.rs new file mode 100644 index 0000000000..18b9344817 --- /dev/null +++ b/comms/src/protocol/messaging/messaging.rs @@ -0,0 +1,480 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::error::MessagingProtocolError; +use crate::{ + compat::IoCompat, + connection_manager::{ConnectionManagerEvent, ConnectionManagerRequester}, + message::{InboundMessage, MessageTag, OutboundMessage}, + peer_manager::{AsyncPeerManager, NodeId, NodeIdentity, Peer, PeerManagerError}, + protocol::{ + messaging::{inbound::InboundMessaging, outbound::OutboundMessaging}, + ProtocolEvent, + ProtocolNotification, + }, + types::CommsSubstream, +}; +use bytes::Bytes; +use derive_error::Error; +use futures::{channel::mpsc, stream::Fuse, AsyncRead, AsyncWrite, SinkExt, StreamExt}; +use log::*; +use std::{ + collections::{hash_map::Entry, HashMap}, + fmt, + sync::Arc, +}; +use tari_shutdown::{Shutdown, ShutdownSignal}; +use tokio::{runtime, sync::broadcast}; +use tokio_util::codec::{Framed, LengthDelimitedCodec}; + +const LOG_TARGET: &str = "comms::protocol::messaging"; +pub const MESSAGING_PROTOCOL: Bytes = Bytes::from_static(b"/tari/messaging/0.1.0"); +/// The size of the buffered channel used for _each_ peer's message queue +const MESSAGE_QUEUE_BUF_SIZE: usize = 20; + +pub type MessagingEventSender = broadcast::Sender>; +pub type MessagingEventReceiver = broadcast::Receiver>; + +/// Request types for MessagingProtocol +#[derive(Debug)] +pub enum MessagingRequest { + SendMessage(OutboundMessage), +} + +/// The reason for dial failure. This enum should contain simple variants which describe the kind of failure that +/// occurred +#[derive(Debug, Error, Copy, Clone)] +pub enum SendFailReason { + /// Dial was not attempted because the peer is offline + PeerOffline, + /// Dial was attempted, but failed + PeerDialFailed, + /// Outbound message envelope failed to serialize + EnvelopeFailedToSerialize, + /// Failed to open a messaging substream to peer + SubstreamOpenFailed, + /// Failed to send on substream channel + SubstreamSendFailed, +} + +#[derive(Clone, Debug)] +pub enum MessagingEvent { + MessageReceived(Box, MessageTag), + InvalidMessageReceived(Box), + SendMessageFailed(OutboundMessage, SendFailReason), + MessageSent(MessageTag), +} + +impl fmt::Display for MessagingEvent { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use MessagingEvent::*; + match self { + MessageReceived(node_id, tag) => write!(f, "MessageReceived({}, {})", node_id.short_str(), tag), + InvalidMessageReceived(node_id) => write!(f, "InvalidMessageReceived({})", node_id.short_str()), + SendMessageFailed(out_msg, reason) => write!(f, "SendMessageFailed({}, Reason = {})", out_msg, reason), + MessageSent(tag) => write!(f, "SendMessageSucceeded({})", tag), + } + } +} + +pub struct MessagingProtocol { + executor: runtime::Handle, + connection_manager_requester: ConnectionManagerRequester, + node_identity: Arc, + peer_manager: AsyncPeerManager, + proto_notification: Fuse>>, + active_queues: HashMap, mpsc::Sender>, + request_rx: Fuse>, + messaging_events_tx: MessagingEventSender, + inbound_message_tx: mpsc::Sender, + internal_messaging_event_tx: mpsc::Sender, + internal_messaging_event_rx: Fuse>, + retry_queue_tx: mpsc::Sender, + retry_queue_rx: Fuse>, + attempts: HashMap, + max_attempts: usize, + shutdown_signal: Option, + complete_trigger: Shutdown, +} + +impl MessagingProtocol { + pub fn new( + executor: runtime::Handle, + connection_manager_requester: ConnectionManagerRequester, + peer_manager: AsyncPeerManager, + node_identity: Arc, + proto_notification: mpsc::Receiver>, + request_rx: mpsc::Receiver, + messaging_events_tx: MessagingEventSender, + inbound_message_tx: mpsc::Sender, + max_attempts: usize, + shutdown_signal: ShutdownSignal, + ) -> Self + { + let (internal_messaging_event_tx, internal_messaging_event_rx) = mpsc::channel(50); + let (retry_queue_tx, retry_queue_rx) = mpsc::channel(50); + Self { + executor, + connection_manager_requester, + peer_manager, + node_identity, + proto_notification: proto_notification.fuse(), + request_rx: request_rx.fuse(), + active_queues: Default::default(), + messaging_events_tx, + internal_messaging_event_rx: internal_messaging_event_rx.fuse(), + internal_messaging_event_tx, + inbound_message_tx, + retry_queue_rx: retry_queue_rx.fuse(), + retry_queue_tx, + shutdown_signal: Some(shutdown_signal), + max_attempts, + attempts: Default::default(), + complete_trigger: Shutdown::new(), + } + } + + pub fn complete_signal(&self) -> ShutdownSignal { + self.complete_trigger.to_signal() + } + + pub async fn run(mut self) { + let mut shutdown_signal = self + .shutdown_signal + .take() + .expect("Messaging initialized without shutdown_signal"); + + let mut conn_man_events = self.connection_manager_requester.get_event_subscription().fuse(); + + loop { + futures::select! { + event = conn_man_events.select_next_some() => { + if let Some(event) = log_if_error!(target: LOG_TARGET, event, "Event error: '{error}'",) { + self.handle_conn_man_event(event).await; + } + }, + event = self.internal_messaging_event_rx.select_next_some() => { + self.handle_internal_messaging_event(event).await; + }, + out_msg = self.retry_queue_rx.select_next_some() => { + log_if_error!(target: LOG_TARGET, self.send_message(out_msg).await, "Failed to send message {error}",); + }, + req = self.request_rx.select_next_some() => { + log_if_error!( + target: LOG_TARGET, + self.handle_request(req).await, + "Failed to handle request because '{error}'", + ); + }, + notification = self.proto_notification.select_next_some() => { + self.handle_notification(notification).await; + }, + _ = shutdown_signal => { + info!(target: LOG_TARGET, "MessagingProtocol is shutting down because the shutdown signal was triggered"); + break; + } + complete => { + info!(target: LOG_TARGET, "MessagingProtocol is shutting down because all streams have completed"); + break; + } + } + } + } + + pub fn framed(socket: TSubstream) -> Framed, LengthDelimitedCodec> + where TSubstream: AsyncRead + AsyncWrite + Unpin { + Framed::new(IoCompat::new(socket), LengthDelimitedCodec::new()) + } + + async fn handle_internal_messaging_event(&mut self, event: MessagingEvent) { + use MessagingEvent::*; + trace!(target: LOG_TARGET, "Internal messaging event '{}'", event); + match event { + SendMessageFailed(out_msg, reason) => match self.attempts.entry(out_msg.tag) { + Entry::Occupied(mut entry) => match *entry.get() { + n if n >= self.max_attempts => { + warn!( + target: LOG_TARGET, + "Failed to send message '{}' to peer '{}' because '{}'.", + out_msg.tag, + out_msg.peer_node_id.short_str(), + reason + ); + let _ = self + .messaging_events_tx + .send(Arc::new(SendMessageFailed(out_msg, reason))); + }, + n => { + *entry.get_mut() = n + 1; + }, + }, + Entry::Vacant(entry) => { + if self.max_attempts == 0 { + let _ = self + .messaging_events_tx + .send(Arc::new(SendMessageFailed(out_msg, reason))); + } else { + match self.retry_queue_tx.send(out_msg).await { + Ok(_) => { + entry.insert(1); + }, + Err(err) => { + warn!(target: LOG_TARGET, "Failed to send to retry_queue '{:?}'", err); + }, + } + } + }, + }, + MessageSent(tag) => { + self.attempts.remove(&tag); + let _ = self.messaging_events_tx.send(Arc::new(MessageSent(tag))); + }, + evt => { + // Forward the event + let _ = self.messaging_events_tx.send(Arc::new(evt)); + }, + } + } + + async fn handle_conn_man_event(&mut self, event: Arc) { + trace!(target: LOG_TARGET, "ConnectionManagerEvent: {}", event); + use ConnectionManagerEvent::*; + match &*event { + PeerDisconnected(node_id) => { + if self.active_queues.remove(node_id).is_some() { + debug!( + target: LOG_TARGET, + "Removing active queue because peer '{}' disconnected", + node_id.short_str() + ); + } + }, + PeerConnectWillClose(_, node_id, direction) => { + if let Some(mut sender) = self.active_queues.remove(node_id) { + sender.close_channel(); + debug!( + target: LOG_TARGET, + "Removing active queue because {} peer connection '{}' will close", + direction, + node_id.short_str() + ); + } + }, + + _ => {}, + } + } + + async fn handle_request(&mut self, req: MessagingRequest) -> Result<(), MessagingProtocolError> { + use MessagingRequest::*; + match req { + SendMessage(msg) => { + if let Err(err) = self.send_message(msg).await { + debug!( + target: LOG_TARGET, + "MessagingProtocol encountered an error when sending a message: {}", err + ); + } + }, + } + + Ok(()) + } + + async fn send_message(&mut self, out_msg: OutboundMessage) -> Result<(), MessagingProtocolError> { + let sender = loop { + match self.active_queues.entry(Box::new(out_msg.peer_node_id.clone())) { + Entry::Occupied(entry) => { + if entry.get().is_closed() { + entry.remove(); + continue; + } + break entry.into_mut(); + }, + Entry::Vacant(entry) => { + let sender = Self::spawn_outbound_handler( + self.executor.clone(), + self.node_identity.clone(), + self.connection_manager_requester.clone(), + self.internal_messaging_event_tx.clone(), + out_msg.peer_node_id.clone(), + ) + .await?; + break entry.insert(sender); + }, + } + }; + + match sender.send(out_msg.clone()).await { + Ok(_) => Ok(()), + Err(err) => { + debug!( + target: LOG_TARGET, + "Failed to send message on channel because '{:?}'", err + ); + // Lazily remove Senders from the active queue if the MessagingProtocolHandler has shut down + if err.is_disconnected() { + self.active_queues.remove(&out_msg.peer_node_id); + } + Err(MessagingProtocolError::MessageSendFailed(out_msg)) + }, + } + } + + async fn spawn_outbound_handler( + executor: runtime::Handle, + our_node_identity: Arc, + conn_man_requester: ConnectionManagerRequester, + events_tx: mpsc::Sender, + peer_node_id: NodeId, + ) -> Result, MessagingProtocolError> + { + let (msg_tx, msg_rx) = mpsc::channel(MESSAGE_QUEUE_BUF_SIZE); + executor.spawn( + OutboundMessaging::new(conn_man_requester, our_node_identity, events_tx, msg_rx, peer_node_id).run(), + ); + Ok(msg_tx) + } + + async fn spawn_inbound_handler(&mut self, peer: Arc, substream: CommsSubstream) { + let messaging_events_tx = self.messaging_events_tx.clone(); + let mut inbound_message_tx = self.inbound_message_tx.clone(); + let mut framed_substream = Self::framed(substream); + let inbound = InboundMessaging; + + self.executor.spawn(async move { + while let Some(result) = framed_substream.next().await { + match result { + Ok(raw_msg) => { + trace!( + target: LOG_TARGET, + "Received message from peer '{}' ({} bytes)", + peer.node_id.short_str(), + raw_msg.len() + ); + + let mut raw_msg = raw_msg.freeze(); + let (event, in_msg) = match inbound.process_message(Arc::clone(&peer), &mut raw_msg).await { + Ok(inbound_msg) => ( + MessagingEvent::MessageReceived( + Box::new(inbound_msg.source_peer.node_id.clone()), + inbound_msg.tag, + ), + Some(inbound_msg), + ), + Err(err) => { + // TODO: #banheuristic + warn!( + target: LOG_TARGET, + "Received invalid message from peer '{}' ({})", + peer.node_id.short_str(), + err + ); + ( + MessagingEvent::InvalidMessageReceived(Box::new(peer.node_id.clone())), + None, + ) + }, + }; + + if let Some(in_msg) = in_msg { + if let Err(err) = inbound_message_tx.send(in_msg).await { + warn!( + target: LOG_TARGET, + "Failed to send InboundMessage for peer '{}' because '{}'", + peer.node_id.short_str(), + err + ); + + if err.is_disconnected() { + break; + } + } + } + + trace!(target: LOG_TARGET, "Inbound handler sending event '{:?}'", event); + if let Err(err) = messaging_events_tx.send(Arc::new(event)) { + debug!( + target: LOG_TARGET, + "Messaging event '{}' not sent for peer '{}' because there are no subscribers. \ + MessagingEvent dropped", + err.0, + peer.node_id.short_str(), + ); + } + }, + Err(err) => debug!( + target: LOG_TARGET, + "Failed to receive from peer '{}' because '{}'", + peer.node_id.short_str(), + err + ), + } + } + + trace!( + target: LOG_TARGET, + "Inbound messaging handler for peer '{}' has stopped", + peer.node_id.short_str() + ); + }); + } + + async fn handle_notification(&mut self, notification: ProtocolNotification) { + debug_assert_eq!(notification.protocol, MESSAGING_PROTOCOL); + match notification.event { + // Peer negotiated to speak the messaging protocol with us + ProtocolEvent::NewInboundSubstream(node_id, substream) => { + debug!( + target: LOG_TARGET, + "NewInboundSubstream for peer '{}'", + node_id.short_str() + ); + match self.peer_manager.find_by_node_id(&node_id).await { + Ok(peer) => { + // For an inbound substream, read messages from the peer and forward on the incoming_messages + // channel + self.spawn_inbound_handler(Arc::new(peer), substream).await; + }, + Err(PeerManagerError::PeerNotFoundError) => { + // This should never happen if everything is working correctly + + error!( + target: LOG_TARGET, + "[ThisNode={}] *** Could not find verified node_id '{}' in peer list. This should not \ + happen ***", + self.node_identity.node_id().short_str(), + node_id.short_str() + ); + }, + Err(err) => { + // This should never happen if everything is working correctly + error!( + target: LOG_TARGET, + "Peer manager error when handling protocol notification: '{:?}'", err + ); + }, + } + }, + } + } +} diff --git a/comms/src/protocol/messaging/mod.rs b/comms/src/protocol/messaging/mod.rs new file mode 100644 index 0000000000..c441dc55e3 --- /dev/null +++ b/comms/src/protocol/messaging/mod.rs @@ -0,0 +1,39 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod error; +mod inbound; +mod messaging; +mod outbound; + +pub use messaging::{ + MessagingEvent, + MessagingEventReceiver, + MessagingEventSender, + MessagingProtocol, + MessagingRequest, + SendFailReason, + MESSAGING_PROTOCOL, +}; + +#[cfg(test)] +mod test; diff --git a/comms/src/protocol/messaging/outbound.rs b/comms/src/protocol/messaging/outbound.rs new file mode 100644 index 0000000000..bf029fec36 --- /dev/null +++ b/comms/src/protocol/messaging/outbound.rs @@ -0,0 +1,234 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::{error::MessagingProtocolError, MessagingEvent, MessagingProtocol, SendFailReason, MESSAGING_PROTOCOL}; +use crate::{ + connection_manager::{ConnectionManagerError, ConnectionManagerRequester, NegotiatedSubstream, PeerConnection}, + message::{Envelope, MessageExt, OutboundMessage}, + peer_manager::{NodeId, NodeIdentity}, + types::CommsSubstream, +}; +use bytes::Bytes; +use futures::{channel::mpsc, SinkExt, StreamExt}; +use log::*; +use std::sync::Arc; + +const LOG_TARGET: &str = "comms::protocol::messaging::outbound"; + +pub struct OutboundMessaging { + conn_man_requester: ConnectionManagerRequester, + node_identity: Arc, + request_rx: mpsc::Receiver, + messaging_events_tx: mpsc::Sender, + peer_node_id: NodeId, +} + +impl OutboundMessaging { + pub fn new( + conn_man_requester: ConnectionManagerRequester, + node_identity: Arc, + messaging_events_tx: mpsc::Sender, + request_rx: mpsc::Receiver, + peer_node_id: NodeId, + ) -> Self + { + Self { + conn_man_requester, + node_identity, + request_rx, + messaging_events_tx, + peer_node_id, + } + } + + pub async fn run(mut self) -> Result<(), MessagingProtocolError> { + debug!( + target: LOG_TARGET, + "Attempting to dial peer '{}' if required", + self.peer_node_id.short_str() + ); + let conn = self.try_dial_peer().await?; + let substream = self.try_open_substream(conn).await?; + debug_assert_eq!(substream.protocol, MESSAGING_PROTOCOL); + self.start_forwarding_messages(substream.stream).await?; + + Ok(()) + } + + async fn try_dial_peer(&mut self) -> Result { + loop { + match self.conn_man_requester.dial_peer(self.peer_node_id.clone()).await { + Ok(conn) => break Ok(conn), + Err(ConnectionManagerError::DialCancelled) => { + error!( + target: LOG_TARGET, + "Dial was cancelled for peer '{}'. This is probably because of connection tie-breaking. \ + Retrying...", + self.peer_node_id.short_str(), + ); + continue; + }, + Err(err @ ConnectionManagerError::PeerOffline) => { + error!( + target: LOG_TARGET, + "MessagingProtocol failed to dial peer '{}' because '{:?}'", + self.peer_node_id.short_str(), + err + ); + self.flush_all_messages_to_failed_event(SendFailReason::PeerOffline) + .await; + break Err(MessagingProtocolError::PeerDialFailed); + }, + Err(err) => { + error!( + target: LOG_TARGET, + "MessagingProtocol failed to dial peer '{}' because '{:?}'", + self.peer_node_id.short_str(), + err + ); + self.flush_all_messages_to_failed_event(SendFailReason::PeerDialFailed) + .await; + break Err(MessagingProtocolError::PeerDialFailed); + }, + } + } + } + + async fn try_open_substream( + &mut self, + mut conn: PeerConnection, + ) -> Result, MessagingProtocolError> + { + match conn.open_substream(MESSAGING_PROTOCOL).await { + Ok(substream) => Ok(substream), + Err(err) => { + error!( + target: LOG_TARGET, + "MessagingProtocol failed to open a substream to peer '{}' because '{:?}'", + self.peer_node_id.short_str(), + err + ); + self.flush_all_messages_to_failed_event(SendFailReason::SubstreamOpenFailed) + .await; + Err(err.into()) + }, + } + } + + async fn start_forwarding_messages(mut self, substream: CommsSubstream) -> Result<(), MessagingProtocolError> { + let mut framed = MessagingProtocol::framed(substream); + while let Some(out_msg) = self.request_rx.next().await { + match self.to_envelope_bytes(&out_msg).await { + Ok(body) => { + trace!( + target: LOG_TARGET, + "Sending message ({} bytes) ({:?}) on outbound messaging substream", + body.len(), + out_msg.tag, + ); + if let Err(err) = framed.send(body).await { + debug!( + target: LOG_TARGET, + "[ThisNode={}] OutboundMessaging failed to send message to peer '{}' because '{}'", + self.node_identity.node_id().short_str(), + self.peer_node_id.short_str(), + err + ); + let _ = self + .messaging_events_tx + .send(MessagingEvent::SendMessageFailed( + out_msg, + SendFailReason::SubstreamSendFailed, + )) + .await; + // FATAL: Failed to send on the substream + self.flush_all_messages_to_failed_event(SendFailReason::SubstreamSendFailed) + .await; + return Err(MessagingProtocolError::OutboundSubstreamFailure); + } + + let _ = self + .messaging_events_tx + .send(MessagingEvent::MessageSent(out_msg.tag)) + .await; + }, + Err(err) => { + debug!( + target: LOG_TARGET, + "Failed to send message to peer '{}' because '{:?}'", + out_msg.peer_node_id.short_str(), + err + ); + + let _ = self + .messaging_events_tx + .send(MessagingEvent::SendMessageFailed( + out_msg, + SendFailReason::EnvelopeFailedToSerialize, + )) + .await; + }, + } + } + + Ok(()) + } + + async fn flush_all_messages_to_failed_event(&mut self, reason: SendFailReason) { + // Close the request channel so that we can read all the remaining messages and flush them + // to a failed event + self.request_rx.close(); + while let Some(out_msg) = self.request_rx.next().await { + let _ = self + .messaging_events_tx + .send(MessagingEvent::SendMessageFailed(out_msg, reason)) + .await; + } + } + + async fn to_envelope_bytes(&self, out_msg: &OutboundMessage) -> Result { + let OutboundMessage { + flags, + body, + peer_node_id, + .. + } = out_msg; + + let envelope = Envelope::construct_signed( + self.node_identity.secret_key(), + self.node_identity.public_key(), + body.clone(), + flags.clone(), + )?; + let body = envelope.to_encoded_bytes()?; + + trace!( + target: LOG_TARGET, + "[Node={}] Sending message ({} bytes) to peer '{}'", + self.node_identity.node_id().short_str(), + body.len(), + peer_node_id.short_str(), + ); + + Ok(body.into()) + } +} diff --git a/comms/src/protocol/messaging/test.rs b/comms/src/protocol/messaging/test.rs new file mode 100644 index 0000000000..189cff179c --- /dev/null +++ b/comms/src/protocol/messaging/test.rs @@ -0,0 +1,304 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::messaging::{ + MessagingEvent, + MessagingEventReceiver, + MessagingProtocol, + MessagingRequest, + MESSAGING_PROTOCOL, +}; +use crate::{ + message::{InboundMessage, MessageExt, MessageFlags, MessageTag, OutboundMessage}, + net_address::MultiaddressesWithStats, + peer_manager::{AsyncPeerManager, NodeId, NodeIdentity, Peer, PeerFeatures, PeerFlags}, + proto::envelope::Envelope, + protocol::{messaging::SendFailReason, ProtocolEvent, ProtocolNotification}, + test_utils::{ + create_connection_manager_mock, + create_peer_connection_mock_pair, + node_id, + node_identity::build_node_identity, + peer_manager::build_peer_manager, + transport, + ConnectionManagerMockState, + }, + types::{CommsPublicKey, CommsSubstream}, +}; +use bytes::Bytes; +use futures::{channel::mpsc, SinkExt, StreamExt}; +use prost::Message; +use rand::rngs::OsRng; +use std::{sync::Arc, time::Duration}; +use tari_crypto::keys::PublicKey; +use tari_shutdown::Shutdown; +use tari_test_utils::{collect_stream, unpack_enum}; +use tokio::{runtime::Handle, sync::broadcast, time}; +use tokio_macros as runtime; + +const TEST_MSG1: Bytes = Bytes::from_static(b"TEST_MSG1"); + +async fn spawn_messaging_protocol() -> ( + AsyncPeerManager, + Arc, + ConnectionManagerMockState, + mpsc::Sender>, + mpsc::Sender, + mpsc::Receiver, + MessagingEventReceiver, + Shutdown, +) { + let shutdown = Shutdown::new(); + let rt_handle = Handle::current(); + + let (requester, mock) = create_connection_manager_mock(10); + let mock_state = mock.get_shared_state(); + rt_handle.spawn(mock.run()); + + let peer_manager: AsyncPeerManager = build_peer_manager().into(); + let node_identity = build_node_identity(PeerFeatures::COMMUNICATION_CLIENT); + let (proto_tx, proto_rx) = mpsc::channel(10); + let (request_tx, request_rx) = mpsc::channel(10); + let (inbound_msg_tx, inbound_msg_rx) = mpsc::channel(100); + let (events_tx, events_rx) = broadcast::channel(100); + + let msg_proto = MessagingProtocol::new( + rt_handle.clone(), + requester, + peer_manager.clone(), + node_identity.clone(), + proto_rx, + request_rx, + events_tx, + inbound_msg_tx, + 0, + shutdown.to_signal(), + ); + rt_handle.spawn(msg_proto.run()); + + ( + peer_manager, + node_identity, + mock_state, + proto_tx, + request_tx, + inbound_msg_rx, + events_rx, + shutdown, + ) +} + +#[runtime::test_basic] +async fn new_inbound_substream_handling() { + let (peer_manager, _, _, mut proto_tx, _, mut inbound_msg_rx, mut events_rx, _shutdown) = + spawn_messaging_protocol().await; + + let expected_node_id = node_id::random(); + let (sk, pk) = CommsPublicKey::random_keypair(&mut OsRng); + peer_manager + .add_peer(Peer::new( + pk.clone(), + expected_node_id.clone(), + MultiaddressesWithStats::default(), + PeerFlags::empty(), + PeerFeatures::COMMUNICATION_CLIENT, + )) + .await + .unwrap(); + + // Create connected memory sockets - we use each end of the connection as if they exist on different nodes + let (_, muxer_ours, mut muxer_theirs) = transport::build_multiplexed_connections().await; + + // Notify the messaging protocol that a new substream has been established that wants to talk the messaging. + let stream_ours = muxer_ours.get_yamux_control().open_stream().await.unwrap(); + proto_tx + .send(ProtocolNotification::new( + MESSAGING_PROTOCOL.into(), + ProtocolEvent::NewInboundSubstream(Box::new(expected_node_id.clone()), stream_ours), + )) + .await + .unwrap(); + + let stream_theirs = muxer_theirs.incoming_mut().next().await.unwrap().unwrap(); + let mut framed_theirs = MessagingProtocol::framed(stream_theirs); + + let envelope = Envelope::construct_signed(&sk, &pk, TEST_MSG1, MessageFlags::empty()).unwrap(); + + framed_theirs + .send(Bytes::copy_from_slice(&envelope.to_encoded_bytes().unwrap())) + .await + .unwrap(); + + let in_msg = time::timeout(Duration::from_secs(5), inbound_msg_rx.next()) + .await + .unwrap() + .unwrap(); + assert_eq!(in_msg.source_peer.node_id, expected_node_id); + assert_eq!(in_msg.body, TEST_MSG1); + + let expected_tag = in_msg.tag; + let event = time::timeout(Duration::from_secs(5), events_rx.next()) + .await + .unwrap() + .unwrap() + .unwrap(); + unpack_enum!(MessagingEvent::MessageReceived(node_id, tag) = &*event); + assert_eq!(tag, &expected_tag); + assert_eq!(**node_id, expected_node_id); +} + +#[runtime::test_basic] +async fn send_message_request() { + let (_, node_identity, conn_man_mock, _, mut request_tx, _, _, _shutdown) = spawn_messaging_protocol().await; + + let peer_node_id = node_id::random(); + + let (conn1, peer_conn_mock1, _, peer_conn_mock2) = + create_peer_connection_mock_pair(1, node_identity.node_id().clone(), peer_node_id.clone()).await; + + // Add mock peer connection to connection manager mock for node 2 + conn_man_mock.add_active_connection(peer_node_id.clone(), conn1).await; + + // Send a message to node + let out_msg = OutboundMessage::new(peer_node_id, MessageFlags::NONE, TEST_MSG1); + request_tx.send(MessagingRequest::SendMessage(out_msg)).await.unwrap(); + + // Check that node got the message + let stream = peer_conn_mock2.next_incoming_substream().await.unwrap(); + let mut framed = MessagingProtocol::framed(stream); + let msg = framed.next().await.unwrap().unwrap(); + let msg = Envelope::decode(msg).unwrap(); + assert_eq!(msg.body, TEST_MSG1); + + // Got the call to create a substream + assert_eq!(peer_conn_mock1.call_count(), 1); +} + +#[runtime::test_basic] +async fn send_message_dial_failed() { + let (_, _, conn_manager_mock, _, mut request_tx, _, mut event_tx, _shutdown) = spawn_messaging_protocol().await; + + let node_id = node_id::random(); + let out_msg = OutboundMessage::new(node_id, MessageFlags::NONE, TEST_MSG1); + let expected_out_msg_tag = out_msg.tag; + // Send a message to node 2 + request_tx.send(MessagingRequest::SendMessage(out_msg)).await.unwrap(); + + let event = event_tx.next().await.unwrap().unwrap(); + unpack_enum!(MessagingEvent::SendMessageFailed(out_msg, reason) = &*event); + unpack_enum!(SendFailReason::PeerDialFailed = reason); + assert_eq!(out_msg.tag, expected_out_msg_tag); + + let calls = conn_manager_mock.take_calls().await; + assert_eq!(calls.len(), 1); + assert!(calls[0].starts_with("DialPeer")); +} + +#[runtime::test_basic] +async fn send_message_substream_bulk_failure() { + const NUM_MSGS: usize = 10; + let (_, node_identity, conn_manager_mock, _, mut request_tx, _, mut event_tx, _shutdown) = + spawn_messaging_protocol().await; + + let peer_node_id = node_id::random(); + + let (conn1, _, _, peer_conn_mock2) = + create_peer_connection_mock_pair(1, node_identity.node_id().clone(), peer_node_id.clone()).await; + + // Add mock peer connection to connection manager mock for node 2 + conn_manager_mock + .add_active_connection(peer_node_id.clone(), conn1) + .await; + + async fn send_msg(request_tx: &mut mpsc::Sender, node_id: NodeId) -> MessageTag { + let out_msg = OutboundMessage::new(node_id, MessageFlags::NONE, TEST_MSG1); + let msg_tag = out_msg.tag; + // Send a message to node 2 + request_tx.send(MessagingRequest::SendMessage(out_msg)).await.unwrap(); + msg_tag + } + + let mut expected_out_msg_tags = Vec::with_capacity(NUM_MSGS); + expected_out_msg_tags.push(send_msg(&mut request_tx, peer_node_id.clone()).await); + + let _ = peer_conn_mock2.next_incoming_substream().await.unwrap(); + // Close destination peer's channel before receiving the message + peer_conn_mock2.disconnect().await; + + for _ in 0..NUM_MSGS - 1 { + expected_out_msg_tags.push(send_msg(&mut request_tx, peer_node_id.clone()).await); + } + + let event = event_tx.next().await.unwrap().unwrap(); + unpack_enum!(MessagingEvent::MessageSent(tag) = &*event); + assert_eq!(tag, &expected_out_msg_tags.remove(0)); + + for _ in 0..NUM_MSGS - 1 { + let event = event_tx.next().await.unwrap().unwrap(); + unpack_enum!(MessagingEvent::SendMessageFailed(out_msg, reason) = &*event); + unpack_enum!(SendFailReason::SubstreamSendFailed = reason); + assert_eq!(out_msg.tag, expected_out_msg_tags.remove(0)); + } +} + +#[runtime::test_basic] +async fn many_concurrent_send_message_requests() { + const NUM_MSGS: usize = 100; + let (_, _, conn_man_mock, _, mut request_tx, _, events_rx, _shutdown) = spawn_messaging_protocol().await; + + let node_id1 = node_id::random(); + let node_id2 = node_id::random(); + + let (conn1, peer_conn_mock1, _, peer_conn_mock2) = + create_peer_connection_mock_pair(1, node_id1.clone(), node_id2.clone()).await; + + // Add mock peer connection to connection manager mock for node 2 + conn_man_mock.add_active_connection(node_id2.clone(), conn1).await; + + // Send many messages to node + let mut msg_tags = Vec::with_capacity(NUM_MSGS); + for _ in 0..NUM_MSGS { + let out_msg = OutboundMessage::new(node_id2.clone(), MessageFlags::NONE, TEST_MSG1); + msg_tags.push(out_msg.tag); + request_tx.send(MessagingRequest::SendMessage(out_msg)).await.unwrap(); + } + + // Check that the node got the messages + let stream = peer_conn_mock2.next_incoming_substream().await.unwrap(); + let framed = MessagingProtocol::framed(stream); + let messages = collect_stream!(framed, take = NUM_MSGS, timeout = Duration::from_secs(10)); + assert_eq!(messages.len(), NUM_MSGS); + + // Check that we got message success events + let events = collect_stream!(events_rx, take = NUM_MSGS, timeout = Duration::from_secs(10)); + assert_eq!(events.len(), NUM_MSGS); + for event in events { + let event = event.unwrap(); + unpack_enum!(MessagingEvent::MessageSent(tag) = &*event); + // Assert that each tag is emitted only once + let index = msg_tags.iter().position(|t| t == tag).unwrap(); + msg_tags.remove(index); + } + + // Got a single call to create a substream + assert_eq!(peer_conn_mock1.call_count(), 1); +} diff --git a/comms/src/protocol/mod.rs b/comms/src/protocol/mod.rs new file mode 100644 index 0000000000..5811616020 --- /dev/null +++ b/comms/src/protocol/mod.rs @@ -0,0 +1,37 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod error; +mod identity; +mod negotiation; +mod protocols; + +pub mod messaging; + +/// Represents a protocol id string (e.g. /tari/transactions/1.0.0). +/// This is atomically reference counted, so clones are shallow and cheap +pub type ProtocolId = bytes::Bytes; + +pub use error::ProtocolError; +pub use identity::{identity_exchange, IdentityProtocolError}; +pub use negotiation::ProtocolNegotiation; +pub use protocols::{ProtocolEvent, ProtocolNotification, Protocols}; diff --git a/comms/src/protocol/negotiation.rs b/comms/src/protocol/negotiation.rs new file mode 100644 index 0000000000..2900f5ed40 --- /dev/null +++ b/comms/src/protocol/negotiation.rs @@ -0,0 +1,201 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::{ProtocolError, ProtocolId}; +use bytes::{Bytes, BytesMut}; +use futures::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; +use log::*; +use std::convert::TryInto; + +const LOG_TARGET: &str = "comms::connection_manager::protocol"; + +const PROTOCOL_NOT_SUPPORTED: &[u8] = b"not-supported"; +const PROTOCOL_NEGOTIATION_TERMINATED: &[u8] = b"negotiation-terminated"; +const BUF_CAPACITY: usize = std::u8::MAX as usize + 1; +const MAX_ROUNDS_ALLOWED: u8 = 10; + +pub struct ProtocolNegotiation<'a, TSocket> { + buf: BytesMut, + socket: &'a mut TSocket, +} + +impl<'a, TSocket> ProtocolNegotiation<'a, TSocket> +where TSocket: AsyncRead + AsyncWrite + Unpin +{ + pub fn new(socket: &'a mut TSocket) -> Self { + Self { + socket, + buf: { + let mut buf = BytesMut::with_capacity(BUF_CAPACITY); + buf.resize(BUF_CAPACITY, 0); + buf.into() + }, + } + } + + /// Negotiate a protocol to speak. Since this node is initiating this interation, send each protocol this node + /// wishes to speak until the destination node agrees. + pub async fn negotiate_protocol_outbound(&mut self, selected_protocols: T) -> Result + where T: AsRef<[ProtocolId]> { + for protocol in selected_protocols.as_ref() { + self.write_frame_flush(protocol).await?; + + let proto = self.read_frame().await?; + // Friendly reply indicating that the maximum number of protocols in one 'session' has been reached + // This reply cannot be relied upon, so protocol negotiation should be used with a timeout + if proto.as_ref() == PROTOCOL_NEGOTIATION_TERMINATED { + return Err(ProtocolError::ProtocolNegotiationTerminatedByPeer); + } + if proto.as_ref() == protocol { + // Shallow copy + return Ok(protocol.clone()); + } + } + + // No more protocols to negotiate - let the peer know + self.write_frame_flush(&PROTOCOL_NEGOTIATION_TERMINATED.into()).await?; + + Err(ProtocolError::ProtocolOutboundNegotiationFailed) + } + + /// Negotiate a protocol to speak. Since this node is the responder, first we wait for a protocol to be sent and see + /// if it is in the supported protocol list. + pub async fn negotiate_protocol_inbound(&mut self, supported_protocols: T) -> Result + where T: AsRef<[ProtocolId]> { + for _ in 0..MAX_ROUNDS_ALLOWED { + let proto = self.read_frame().await?; + + // Allow the peer to send a friendly reply saying that it has no more protocols to negotiate. + // This reply cannot be relied upon, so protocol negotiation should be used with a timeout + if proto.as_ref() == PROTOCOL_NEGOTIATION_TERMINATED { + return Err(ProtocolError::ProtocolNegotiationTerminatedByPeer); + } + + match supported_protocols.as_ref().iter().find(|p| proto == p) { + Some(proto) => { + self.write_frame_flush(proto).await?; + // Shallow copy + return Ok(proto.clone()); + }, + None => { + self.write_frame_flush(&PROTOCOL_NOT_SUPPORTED.into()).await?; + }, + } + } + + // Maximum rounds reached - send a friendly reply to let the peer know to give up + self.write_frame_flush(&PROTOCOL_NEGOTIATION_TERMINATED.into()).await?; + + Err(ProtocolError::ProtocolOutboundNegotiationFailed) + } + + async fn read_frame(&mut self) -> Result { + self.socket.read_exact(&mut self.buf[..1]).await?; + // Len can never overrun the buffer because the buffer len is u8::MAX + 1 and the length delimiter + // is a u8. If that changes, then len should be checked here + let len = u8::from_be_bytes([self.buf[0]]) as usize; + self.socket.read_exact(&mut self.buf[1..len + 1]).await?; + trace!( + target: LOG_TARGET, + "Read frame '{}' ({} byte(s))", + String::from_utf8_lossy(&self.buf[1..len + 1]), + len + ); + Ok(Bytes::copy_from_slice(&self.buf[1..len + 1])) + } + + async fn write_frame_flush(&mut self, protocol: &ProtocolId) -> Result<(), ProtocolError> { + let len_byte = protocol + .len() + .try_into() + .map(|v: u8| v.to_be_bytes()) + .map_err(|_| ProtocolError::ProtocolIdTooLong)?; + self.socket.write_all(&len_byte).await?; + self.socket.write_all(&protocol).await?; + self.socket.flush().await?; + trace!( + target: LOG_TARGET, + "Wrote frame '{}' ({} byte(s))", + String::from_utf8_lossy(&protocol), + len_byte[0] + ); + Ok(()) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::memsocket::MemorySocket; + use futures::future; + use tari_test_utils::unpack_enum; + + #[tokio_macros::test_basic] + async fn negotiate_success() { + let (mut initiator, mut responder) = MemorySocket::new_pair(); + let mut negotiate_out = ProtocolNegotiation::new(&mut initiator); + let mut negotiate_in = ProtocolNegotiation::new(&mut responder); + + let supported_protocols = vec![b"B", b"A"] + .into_iter() + .map(|p| ProtocolId::from_static(p)) + .collect::>(); + let selected_protocols = vec![b"C", b"D", b"A"] + .into_iter() + .map(|p| ProtocolId::from_static(p)) + .collect::>(); + + let (in_proto, out_proto) = future::join( + negotiate_in.negotiate_protocol_inbound(supported_protocols), + negotiate_out.negotiate_protocol_outbound(selected_protocols), + ) + .await; + + assert_eq!(in_proto.unwrap(), ProtocolId::from_static(b"A")); + assert_eq!(out_proto.unwrap(), ProtocolId::from_static(b"A")); + } + + #[tokio_macros::test_basic] + async fn negotiate_fail() { + let (mut initiator, mut responder) = MemorySocket::new_pair(); + let mut negotiate_out = ProtocolNegotiation::new(&mut initiator); + let mut negotiate_in = ProtocolNegotiation::new(&mut responder); + + let supported_protocols = vec![b"A", b"B"] + .into_iter() + .map(|p| ProtocolId::from_static(p)) + .collect::>(); + let selected_protocols = vec![b"C", b"D", b"E"] + .into_iter() + .map(|p| ProtocolId::from_static(p)) + .collect::>(); + + let (in_proto, out_proto) = future::join( + negotiate_in.negotiate_protocol_inbound(supported_protocols), + negotiate_out.negotiate_protocol_outbound(selected_protocols), + ) + .await; + + unpack_enum!(ProtocolError::ProtocolNegotiationTerminatedByPeer = in_proto.unwrap_err()); + unpack_enum!(ProtocolError::ProtocolOutboundNegotiationFailed = out_proto.unwrap_err()); + } +} diff --git a/comms/src/protocol/protocols.rs b/comms/src/protocol/protocols.rs new file mode 100644 index 0000000000..0b70d1827a --- /dev/null +++ b/comms/src/protocol/protocols.rs @@ -0,0 +1,154 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + peer_manager::NodeId, + protocol::{ProtocolError, ProtocolId}, +}; +use futures::{channel::mpsc, SinkExt}; +use std::collections::HashMap; + +#[derive(Debug, Clone)] +pub enum ProtocolEvent { + NewInboundSubstream(Box, TSubstream), +} + +#[derive(Debug, Clone)] +pub struct ProtocolNotification { + pub event: ProtocolEvent, + pub protocol: ProtocolId, +} + +impl ProtocolNotification { + pub fn new(protocol: ProtocolId, event: ProtocolEvent) -> Self { + Self { protocol, event } + } +} + +pub struct Protocols { + protocols: HashMap>>, +} + +impl Clone for Protocols { + fn clone(&self) -> Self { + Self { + protocols: self.protocols.clone(), + } + } +} + +impl Default for Protocols { + fn default() -> Self { + Self { + protocols: HashMap::default(), + } + } +} + +impl Protocols { + pub fn new() -> Self { + Default::default() + } + + pub fn add>( + mut self, + protocols: I, + notifier: mpsc::Sender>, + ) -> Self + { + self.protocols + .extend(protocols.as_ref().iter().map(|p| (p.clone(), notifier.clone()))); + self + } + + pub fn get_supported_protocols(&self) -> Vec { + self.protocols.keys().cloned().collect() + } + + pub async fn notify( + &mut self, + protocol: &ProtocolId, + event: ProtocolEvent, + ) -> Result<(), ProtocolError> + { + match self.protocols.get_mut(protocol) { + Some(sender) => { + sender.send(ProtocolNotification::new(protocol.clone(), event)).await?; + Ok(()) + }, + None => Err(ProtocolError::ProtocolNotRegistered), + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use futures::StreamExt; + use tari_test_utils::unpack_enum; + + #[test] + fn add() { + let (tx, _) = mpsc::channel(1); + let protos = [ + ProtocolId::from_static(b"/tari/test/1"), + ProtocolId::from_static(b"/tari/test/2"), + ]; + let protocols = Protocols::<()>::new().add(&protos, tx); + + assert!(protocols.get_supported_protocols().iter().all(|p| protos.contains(p))); + } + + #[tokio_macros::test_basic] + async fn notify() { + let (tx, mut rx) = mpsc::channel(1); + let protos = [ProtocolId::from_static(b"/tari/test/1")]; + let mut protocols = Protocols::<()>::new().add(&protos, tx); + + protocols + .notify( + &protos[0], + ProtocolEvent::NewInboundSubstream(Box::new(NodeId::new()), ()), + ) + .await + .unwrap(); + + let notification = rx.next().await.unwrap(); + unpack_enum!(ProtocolEvent::NewInboundSubstream(peer_id, _s) = notification.event); + assert_eq!(*peer_id, NodeId::new()); + } + + #[tokio_macros::test_basic] + async fn notify_fail_not_registered() { + let mut protocols = Protocols::<()>::new(); + + let err = protocols + .notify( + &ProtocolId::from_static(b"/tari/test/0"), + ProtocolEvent::NewInboundSubstream(Box::new(NodeId::new()), ()), + ) + .await + .unwrap_err(); + + unpack_enum!(ProtocolError::ProtocolNotRegistered = err); + } +} diff --git a/comms/src/pub_sub_channel.rs b/comms/src/pub_sub_channel.rs deleted file mode 100644 index 8c9294831a..0000000000 --- a/comms/src/pub_sub_channel.rs +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright 2019. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// use super::async_::channel as async_channel; -// use super::async_::Publisher; -// use super::async_::Subscriber; -use bus_queue::{bounded, Publisher, Subscriber}; -use futures::{compat::Compat, future, prelude::*, stream::Fuse}; -use std::fmt::Debug; - -/// The container for a message that is passed along the pub-sub channel that contains a Topic to define the type of -/// message and the message itself. -#[derive(Debug)] -pub struct TopicPayload { - topic: T, - message: M, -} - -impl TopicPayload { - pub fn new(topic: T, message: M) -> Self { - Self { topic, message } - } -} - -pub type TopicPublisher = Publisher>; -pub type TopicSubscriber = Subscriber>; - -pub struct TopicSubscriptionFactory { - subscriber: TopicSubscriber, -} - -impl TopicSubscriptionFactory -where - T: Eq + Send, - M: Clone + Send, -{ - pub fn new(subscriber: TopicSubscriber) -> Self { - TopicSubscriptionFactory { subscriber } - } - - /// Provide a subscriber (which will be consumed) and a topic to filter it by and this function will return a stream - /// that yields only the desired messages - pub fn get_subscription(&self, topic: T) -> impl Stream { - self.subscriber.clone().filter_map(move |item| { - let result = if item.topic == topic { - Some(item.message.clone()) - } else { - None - }; - future::ready(result) - }) - } - - /// Provide a Compat wrapped version of the subscription stream for things that want to consume old-style streams - pub fn get_subscription_compat(&self, topic: T) -> Compat>> { - self.get_subscription(topic).map(|i| Ok(i)).compat() - } - - /// Provide a fused version of the subscription stream so that domain modules don't need to know about fuse() - pub fn get_subscription_fused(&self, topic: T) -> Fuse> { - self.get_subscription(topic).fuse() - } -} - -/// Create Topic based Pub-Sub channel which returns the Publisher side of the channel and TopicSubscriptionFactory -/// which can produce multiple subscribers for provided topics. -pub fn pubsub_channel( - size: usize, -) -> (TopicPublisher, TopicSubscriptionFactory) { - let (publisher, subscriber): (TopicPublisher, TopicSubscriber) = bounded(size); - - (publisher, TopicSubscriptionFactory::new(subscriber)) -} - -#[cfg(test)] -mod test { - use super::*; - use futures::{executor::block_on, future::select}; - - #[test] - fn topic_pub_sub() { - let (mut publisher, subscriber_factory) = pubsub_channel(10); - - #[derive(Debug, Clone)] - struct Dummy { - a: u32, - b: String, - } - - let messages = vec![ - TopicPayload::new("Topic1", Dummy { - a: 1u32, - b: "one".to_string(), - }), - TopicPayload::new("Topic2", Dummy { - a: 2u32, - b: "two".to_string(), - }), - TopicPayload::new("Topic1", Dummy { - a: 3u32, - b: "three".to_string(), - }), - TopicPayload::new("Topic2", Dummy { - a: 4u32, - b: "four".to_string(), - }), - TopicPayload::new("Topic1", Dummy { - a: 5u32, - b: "five".to_string(), - }), - TopicPayload::new("Topic2", Dummy { - a: 6u32, - b: "size".to_string(), - }), - TopicPayload::new("Topic1", Dummy { - a: 7u32, - b: "seven".to_string(), - }), - ]; - - block_on(async { - for m in messages { - publisher.send(m).await.unwrap(); - } - }); - - let mut sub1 = subscriber_factory.get_subscription("Topic1").fuse(); - - let topic1a = block_on(async { - let mut result = Vec::new(); - - loop { - select!( - item = sub1.next() => {if let Some(i) = item {result.push(i)}}, - default => break, - ); - } - result - }); - - assert_eq!(topic1a.len(), 4); - assert_eq!(topic1a[0].a, 1); - assert_eq!(topic1a[1].a, 3); - assert_eq!(topic1a[2].a, 5); - assert_eq!(topic1a[3].a, 7); - - let messages2 = vec![ - TopicPayload::new("Topic1", Dummy { - a: 11u32, - b: "one one".to_string(), - }), - TopicPayload::new("Topic2", Dummy { - a: 22u32, - b: "two two".to_string(), - }), - TopicPayload::new("Topic1", Dummy { - a: 33u32, - b: "three three".to_string(), - }), - ]; - - block_on(async move { - stream::iter(messages2).map(|i| Ok(i)).forward(publisher).await.unwrap(); - }); - - let topic1b = block_on(async { sub1.collect::>().await }); - - assert_eq!(topic1b.len(), 2); - assert_eq!(topic1b[0].a, 11); - assert_eq!(topic1b[1].a, 33); - - let sub2 = subscriber_factory.get_subscription("Topic2"); - - let topic2 = block_on(async { sub2.collect::>().await }); - - assert_eq!(topic2.len(), 4); - assert_eq!(topic2[0].a, 2); - assert_eq!(topic2[1].a, 4); - assert_eq!(topic2[2].a, 6); - assert_eq!(topic2[3].a, 22); - } -} diff --git a/comms/src/socks/client.rs b/comms/src/socks/client.rs new file mode 100644 index 0000000000..5e6f1b2430 --- /dev/null +++ b/comms/src/socks/client.rs @@ -0,0 +1,429 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Acknowledgement to @sticnarf for tokio-socks on which this code is based +use super::error::SocksError; +use data_encoding::BASE32; +use futures::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; +use multiaddr::{Multiaddr, Protocol}; +use std::{ + borrow::Cow, + net::{Ipv4Addr, Ipv6Addr}, +}; + +pub type Result = std::result::Result; + +/// Authentication methods +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Authentication { + None, + Password(String, String), +} + +impl Authentication { + fn id(&self) -> u8 { + match self { + Authentication::Password(_, _) => 0x02, + Authentication::None => 0x00, + } + } +} + +impl Default for Authentication { + fn default() -> Self { + Authentication::None + } +} + +#[repr(u8)] +#[derive(Clone, Copy)] +enum Command { + Connect = 0x01, + Bind = 0x02, + // UDP Associate command not supported + // Associate = 0x03, + TorResolve = 0xF0, + TorResolvePtr = 0xF1, +} + +/// A SOCKS5 socket connection. +pub struct Socks5Client { + protocol: SocksProtocol, + is_authenticated: bool, +} + +impl Socks5Client +where TSocket: AsyncRead + AsyncWrite + Unpin +{ + /// Create a new socks5 client with a socket already connected to the target proxy + pub fn new(socket: TSocket) -> Self { + Self { + protocol: SocksProtocol::new(socket), + is_authenticated: false, + } + } + + pub fn with_authentication(&mut self, auth: Authentication) -> Result<&mut Self> { + Self::validate_auth(&auth)?; + self.protocol.set_authentication(auth); + Ok(self) + } + + /// Connects to a address through a SOCKS5 proxy and returns the 'upgraded' socket. This consumes the + /// `Socks5Client` as once connected, the socks protocol does not recognise any further commands. + pub async fn connect(mut self, address: &Multiaddr) -> Result<(TSocket, Multiaddr)> { + let address = self.execute_command(Command::Connect, address).await?; + Ok((self.protocol.socket, address)) + } + + /// Requests the tor proxy to resolve a DNS address is resolved into an IP address. + /// This operation only works with the tor SOCKS proxy. + pub async fn tor_resolve(&mut self, address: &Multiaddr) -> Result { + self.protocol.send_command(Command::TorResolve, address).await + } + + /// Requests the tor proxy to reverse resolve an IP address into a DNS address if it is able. + /// This operation only works with the tor SOCKS proxy. + pub async fn tor_resolve_ptr(&mut self, address: &Multiaddr) -> Result { + self.protocol.send_command(Command::TorResolvePtr, address).await + } + + async fn execute_command(&mut self, command: Command, address: &Multiaddr) -> Result { + if !self.is_authenticated { + self.protocol.authenticate().await?; + self.is_authenticated = true; + } + + let address = self.protocol.send_command(command, address).await?; + + Ok(address) + } + + fn validate_auth(auth: &Authentication) -> Result<()> { + match auth { + Authentication::None => {}, + Authentication::Password(username, password) => { + let username_len = username.as_bytes().len(); + if username_len < 1 || username_len > 255 { + Err(SocksError::InvalidAuthValues("username length should between 1 to 255"))? + } + let password_len = password.as_bytes().len(); + if password_len < 1 || password_len > 255 { + Err(SocksError::InvalidAuthValues("password length should between 1 to 255"))? + } + }, + } + Ok(()) + } +} + +const SOCKS_BUFFER_LENGTH: usize = 513; + +struct SocksProtocol { + socket: TSocket, + authentication: Authentication, + buf: Box<[u8; SOCKS_BUFFER_LENGTH]>, + ptr: usize, + len: usize, +} + +impl SocksProtocol +where TSocket: AsyncRead + AsyncWrite + Unpin +{ + fn new(socket: TSocket) -> Self { + SocksProtocol { + socket, + authentication: Default::default(), + buf: Box::new([0; 513]), + ptr: 0, + len: 0, + } + } + + pub async fn authenticate(&mut self) -> Result<()> { + // Write request to connect/authenticate + self.prepare_send_auth_method_selection(); + self.write().await?; + + // Receive authentication method + self.prepare_recv_auth_method_selection(); + self.read().await?; + if self.buf[0] != 0x05 { + return Err(SocksError::InvalidResponseVersion); + } + match self.buf[1] { + 0x00 => { + // No auth + }, + 0x02 => { + self.password_authentication_protocol().await?; + }, + 0xff => { + return Err(SocksError::NoAcceptableAuthMethods); + }, + m if m != self.authentication.id() => return Err(SocksError::UnknownAuthMethod), + _ => unimplemented!(), + } + + Ok(()) + } + + pub fn set_authentication(&mut self, authentication: Authentication) { + self.authentication = authentication; + } + + pub async fn send_command(&mut self, command: Command, address: &Multiaddr) -> Result { + self.prepare_send_request(command, address)?; + self.write().await?; + self.receive_reply().await + } + + async fn password_authentication_protocol(&mut self) -> Result<()> { + self.prepare_send_password_auth(); + self.write().await?; + + self.prepare_recv_password_auth(); + self.read().await?; + + if self.buf[0] != 0x01 { + return Err(SocksError::InvalidResponseVersion); + } + if self.buf[1] != 0x00 { + return Err(SocksError::PasswordAuthFailure(self.buf[1])); + } + + Ok(()) + } + + async fn receive_reply(&mut self) -> Result { + self.prepare_recv_reply(); + self.ptr += self.read().await?; + if self.buf[0] != 0x05 { + return Err(SocksError::InvalidResponseVersion); + } + if self.buf[2] != 0x00 { + return Err(SocksError::InvalidReservedByte); + } + + match self.buf[1] { + 0x00 => {}, // succeeded + 0x01 => Err(SocksError::GeneralSocksServerFailure)?, + 0x02 => Err(SocksError::ConnectionNotAllowedByRuleset)?, + 0x03 => Err(SocksError::NetworkUnreachable)?, + 0x04 => Err(SocksError::HostUnreachable)?, + 0x05 => Err(SocksError::ConnectionRefused)?, + 0x06 => Err(SocksError::TtlExpired)?, + 0x07 => Err(SocksError::CommandNotSupported)?, + 0x08 => Err(SocksError::AddressTypeNotSupported)?, + _ => Err(SocksError::UnknownAuthMethod)?, + } + + match self.buf[3] { + // IPv4 + 0x01 => { + self.len = 10; + }, + // IPv6 + 0x04 => { + self.len = 22; + }, + // Domain + 0x03 => { + self.len = 5; + self.ptr += self.read().await?; + self.len += self.buf[4] as usize + 2; + }, + _ => Err(SocksError::UnknownAddressType)?, + } + + self.ptr += self.read().await?; + let address = match self.buf[3] { + // IPv4 + 0x01 => { + let mut ip = [0; 4]; + ip[..].copy_from_slice(&self.buf[4..8]); + let ip = Ipv4Addr::from(ip); + let port = u16::from_be_bytes([self.buf[8], self.buf[9]]); + let mut addr: Multiaddr = Protocol::Ip4(ip).into(); + addr.push(Protocol::Tcp(port)); + addr + }, + // IPv6 + 0x04 => { + let mut ip = [0; 16]; + ip[..].copy_from_slice(&self.buf[4..20]); + let ip = Ipv6Addr::from(ip); + let port = u16::from_be_bytes([self.buf[20], self.buf[21]]); + let mut addr: Multiaddr = Protocol::Ip6(ip).into(); + addr.push(Protocol::Tcp(port)); + addr + }, + // Domain + 0x03 => { + let domain_bytes = (&self.buf[5..(self.len - 2)]).to_vec(); + let domain = String::from_utf8(domain_bytes) + .map_err(|_| SocksError::InvalidTargetAddress("domain bytes are not a valid UTF-8 string"))?; + let port = u16::from_be_bytes([self.buf[self.len - 2], self.buf[self.len - 1]]); + let mut addr: Multiaddr = Protocol::Dns4(Cow::Owned(domain)).into(); + addr.push(Protocol::Tcp(port)); + addr + }, + _ => unreachable!(), + }; + + Ok(address) + } + + fn prepare_send_auth_method_selection(&mut self) { + self.ptr = 0; + self.buf[0] = 0x05; + match self.authentication { + Authentication::None => { + self.buf[1..3].copy_from_slice(&[1, 0x00]); + self.len = 3; + }, + Authentication::Password { .. } => { + self.buf[1..4].copy_from_slice(&[2, 0x00, 0x02]); + self.len = 4; + }, + } + } + + fn prepare_recv_auth_method_selection(&mut self) { + self.ptr = 0; + self.len = 2; + } + + fn prepare_send_password_auth(&mut self) { + match &self.authentication { + Authentication::Password(username, password) => { + self.ptr = 0; + self.buf[0] = 0x01; + let username_bytes = username.as_bytes(); + let username_len = username_bytes.len(); + self.buf[1] = username_len as u8; + self.buf[2..(2 + username_len)].copy_from_slice(username_bytes); + let password_bytes = password.as_bytes(); + let password_len = password_bytes.len(); + self.len = 3 + username_len + password_len; + self.buf[(2 + username_len)] = password_len as u8; + self.buf[(3 + username_len)..self.len].copy_from_slice(password_bytes); + }, + Authentication::None => unreachable!(), + } + } + + fn prepare_recv_password_auth(&mut self) { + self.ptr = 0; + self.len = 2; + } + + fn prepare_send_request(&mut self, command: Command, address: &Multiaddr) -> Result<()> { + self.ptr = 0; + self.buf[..3].copy_from_slice(&[0x05, command as u8, 0x00]); + let mut addr_iter = address.iter(); + let part1 = addr_iter + .next() + .ok_or_else(|| SocksError::InvalidTargetAddress("Address contained no components"))?; + + let part2 = addr_iter.next(); + + match (part1, part2) { + (Protocol::Ip4(ip), Some(Protocol::Tcp(port))) => { + self.buf[3] = 0x01; + self.buf[4..8].copy_from_slice(&ip.octets()); + self.buf[8..10].copy_from_slice(&port.to_be_bytes()); + self.len = 10; + }, + (Protocol::Ip6(ip), Some(Protocol::Tcp(port))) => { + self.buf[3] = 0x04; + self.buf[4..20].copy_from_slice(&ip.octets()); + self.buf[20..22].copy_from_slice(&port.to_be_bytes()); + self.len = 22; + }, + (Protocol::Dns4(domain), Some(Protocol::Tcp(port))) => { + self.buf[3] = 0x03; + let domain = domain.as_bytes(); + let len = domain.len(); + self.buf[4] = len as u8; + self.buf[5..5 + len].copy_from_slice(domain); + self.buf[(5 + len)..(7 + len)].copy_from_slice(&port.to_be_bytes()); + self.len = 7 + len; + }, + (p @ Protocol::Onion(_, _), None) => { + self.buf[3] = 0x03; + let (domain, port) = Self::extract_onion_address(p)?; + let len = domain.len(); + self.buf[4] = len as u8; + self.buf[5..5 + len].copy_from_slice(domain.as_bytes()); + self.buf[(5 + len)..(7 + len)].copy_from_slice(&port.to_be_bytes()); + self.len = 7 + len; + }, + (Protocol::Onion3(addr), None) => { + self.buf[3] = 0x03; + let port = addr.port(); + let domain = format!("{}.onion", BASE32.encode(addr.hash())); + let len = domain.len(); + self.buf[4] = len as u8; + self.buf[5..5 + len].copy_from_slice(domain.as_bytes()); + self.buf[(5 + len)..(7 + len)].copy_from_slice(&port.to_be_bytes()); + self.len = 7 + len; + }, + _ => return Err(SocksError::AddressTypeNotSupported), + } + Ok(()) + } + + fn extract_onion_address(p: Protocol<'_>) -> Result<(String, u16)> { + let onion_addr = p.to_string(); + let mut parts = onion_addr + .split('/') + .skip(2) + .next() + .expect("already checked") + .split(':'); + let domain = format!("{}.onion", parts.next().expect("already checked"),); + let port = parts + .next() + .expect("already checked") + .parse::() + .map_err(|_| SocksError::InvalidTargetAddress("Invalid onion address port"))?; + Ok((domain, port)) + } + + fn prepare_recv_reply(&mut self) { + self.ptr = 0; + self.len = 4; + } + + async fn write(&mut self) -> Result<()> { + self.socket + .write_all(&mut self.buf[self.ptr..self.len]) + .await + .map_err(Into::into) + } + + async fn read(&mut self) -> Result { + self.socket.read_exact(&mut self.buf[self.ptr..self.len]).await?; + Ok(self.len - self.ptr) + } +} diff --git a/base_layer/core/src/blockchain/error.rs b/comms/src/socks/error.rs similarity index 53% rename from base_layer/core/src/blockchain/error.rs rename to comms/src/socks/error.rs index 34313e3327..9888fb5932 100644 --- a/base_layer/core/src/blockchain/error.rs +++ b/comms/src/socks/error.rs @@ -1,4 +1,4 @@ -// Copyright 2019. The Tari Project +// Copyright 2019, The Tari Project // // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the // following conditions are met: @@ -19,40 +19,50 @@ // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Portions of this file were originally copyrighted (c) 2018 The Grin Developers, issued under the Apache License, -// Version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0. -// this file is used for all blockchain error types -use crate::blocks::block::BlockValidationError; use derive_error::Error; -use merklemountainrange::{error::MerkleMountainRangeError, merkle_storage::MerkleStorageError}; -use tari_storage::keyvalue_store::*; - -/// The ChainError is used to present all generic chain error of the actual blockchain -#[derive(Debug, Error)] -pub enum ChainError { - // Could not initialise state - InitStateError(DatastoreError), - // Some kind of processing error in the state - StateProcessingError(StateError), -} -/// The chainstate is used to present all generic chain error of the actual blockchain state #[derive(Debug, Error)] -pub enum StateError { - // could not create a database - StoreError(DatastoreError), - // MerklestorageError - StorageError(MerkleStorageError), - // Unkown commitment spent - SpentUnknownCommitment(MerkleMountainRangeError), - // provided mmr states in headers mismatch - HeaderStateMismatch, - // block is not correctly constructed - InvalidBlock(BlockValidationError), - // block is orphaned - OrphanBlock, - // Duplicate block - DuplicateBlock, +pub enum SocksError { + /// Failure caused by an IO error. + Io(std::io::Error), + /// Failure due to invalid target address. + #[error(no_from, non_std)] + InvalidTargetAddress(&'static str), + /// Proxy server unreachable. + ProxyServerUnreachable, + /// Proxy server returns an invalid version number. + InvalidResponseVersion, + /// No acceptable auth methods + NoAcceptableAuthMethods, + /// Unknown auth method + UnknownAuthMethod, + /// General SOCKS server failure + GeneralSocksServerFailure, + /// Connection not allowed by ruleset + ConnectionNotAllowedByRuleset, + /// Network unreachable + NetworkUnreachable, + /// Host unreachable + HostUnreachable, + /// Connection refused + ConnectionRefused, + /// TTL expired + TtlExpired, + /// Command not supported + CommandNotSupported, + /// Address type not supported + AddressTypeNotSupported, + /// Unknown error + UnknownError, + /// Invalid reserved byte + InvalidReservedByte, + /// Unknown address type + UnknownAddressType, + /// Invalid authentication values. It contains the detailed error message. + #[error(no_from, non_std)] + InvalidAuthValues(&'static str), + /// Password auth failure + #[error(no_from, non_std)] + PasswordAuthFailure(u8), } diff --git a/comms/src/socks/mod.rs b/comms/src/socks/mod.rs new file mode 100644 index 0000000000..7a41c41af1 --- /dev/null +++ b/comms/src/socks/mod.rs @@ -0,0 +1,29 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// TODO: Remove #[allow(dead_code)] once tests are in place +#[allow(dead_code)] +mod client; +#[allow(dead_code)] +mod error; + +pub use client::{Authentication, Socks5Client}; diff --git a/comms/src/test_utils/connection_manager_mock.rs b/comms/src/test_utils/connection_manager_mock.rs new file mode 100644 index 0000000000..51cbf6beae --- /dev/null +++ b/comms/src/test_utils/connection_manager_mock.rs @@ -0,0 +1,152 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + connection_manager::{ + ConnectionManagerError, + ConnectionManagerEvent, + ConnectionManagerRequest, + ConnectionManagerRequester, + PeerConnection, + }, + peer_manager::NodeId, +}; +use futures::{channel::mpsc, lock::Mutex, stream::Fuse, StreamExt}; +use std::{ + collections::HashMap, + sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, + }, +}; +use tokio::sync::broadcast; + +pub fn create_connection_manager_mock(buf_size: usize) -> (ConnectionManagerRequester, ConnectionManagerMock) { + let (tx, rx) = mpsc::channel(buf_size); + let (event_tx, _) = broadcast::channel(buf_size); + ( + ConnectionManagerRequester::new(tx, event_tx.clone()), + ConnectionManagerMock::new(rx.fuse(), event_tx), + ) +} + +#[derive(Debug, Clone)] +pub struct ConnectionManagerMockState { + call_count: Arc, + calls: Arc>>, + active_conns: Arc>>, + event_tx: broadcast::Sender>, +} + +impl ConnectionManagerMockState { + pub fn new(event_tx: broadcast::Sender>) -> Self { + Self { + call_count: Arc::new(AtomicUsize::new(0)), + calls: Arc::new(Mutex::new(Vec::new())), + event_tx, + active_conns: Arc::new(Mutex::new(HashMap::new())), + } + } + + fn inc_call_count(&self) { + self.call_count.fetch_add(1, Ordering::SeqCst); + } + + async fn add_call(&self, call_str: String) { + self.calls.lock().await.push(call_str); + } + + pub async fn take_calls(&self) -> Vec { + self.calls.lock().await.drain(..).collect() + } + + #[allow(dead_code)] + pub fn call_count(&self) -> usize { + self.call_count.load(Ordering::SeqCst) + } + + #[allow(dead_code)] + pub async fn add_active_connection(&self, node_id: NodeId, conn: PeerConnection) { + self.active_conns.lock().await.insert(node_id, conn); + } + + #[allow(dead_code)] + pub fn publish_event(&mut self, event: ConnectionManagerEvent) { + self.event_tx.send(Arc::new(event)).unwrap(); + } +} + +pub struct ConnectionManagerMock { + receiver: Fuse>, + state: ConnectionManagerMockState, +} + +impl ConnectionManagerMock { + pub fn new( + receiver: Fuse>, + event_tx: broadcast::Sender>, + ) -> Self + { + Self { + receiver, + state: ConnectionManagerMockState::new(event_tx), + } + } + + pub fn get_shared_state(&self) -> ConnectionManagerMockState { + self.state.clone() + } + + pub async fn run(mut self) { + while let Some(req) = self.receiver.next().await { + self.handle_request(req).await; + } + } + + async fn handle_request(&self, req: ConnectionManagerRequest) { + use ConnectionManagerRequest::*; + self.state.inc_call_count(); + self.state.add_call(format!("{:?}", req)).await; + match req { + DialPeer(node_id, _, reply_tx) => { + // Send Ok(conn) if we have an active connection, otherwise Err(DialConnectFailedAllAddresses) + reply_tx + .send( + self.state + .active_conns + .lock() + .await + .get(&node_id) + .map(Clone::clone) + .ok_or_else(|| ConnectionManagerError::DialConnectFailedAllAddresses), + ) + .unwrap(); + }, + NotifyListening(_reply_tx) => {}, + GetActiveConnection(node_id, reply_tx) => { + reply_tx + .send(self.state.active_conns.lock().await.get(&node_id).map(Clone::clone)) + .unwrap(); + }, + } + } +} diff --git a/comms/tests/support/factories/macros.rs b/comms/src/test_utils/factories/macros.rs similarity index 100% rename from comms/tests/support/factories/macros.rs rename to comms/src/test_utils/factories/macros.rs diff --git a/comms/tests/support/factories/mod.rs b/comms/src/test_utils/factories/mod.rs similarity index 94% rename from comms/tests/support/factories/mod.rs rename to comms/src/test_utils/factories/mod.rs index cffa39f20c..a4b9e598f5 100644 --- a/comms/tests/support/factories/mod.rs +++ b/comms/src/test_utils/factories/mod.rs @@ -21,23 +21,14 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use derive_error::Error; -use serde::export::fmt::Debug; +use std::fmt::Debug; #[macro_use] mod macros; -pub mod connection_manager; - pub mod net_address; - pub mod node_identity; - pub mod peer; - -pub mod peer_connection; - -pub mod peer_connection_context; - pub mod peer_manager; pub trait TestFactory: Default { diff --git a/comms/tests/support/factories/net_address.rs b/comms/src/test_utils/factories/net_address.rs similarity index 78% rename from comms/tests/support/factories/net_address.rs rename to comms/src/test_utils/factories/net_address.rs index 078da5d9fe..255c54b5fb 100644 --- a/comms/tests/support/factories/net_address.rs +++ b/comms/src/test_utils/factories/net_address.rs @@ -21,13 +21,9 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use super::{TestFactory, TestFactoryError}; - -use crate::support::helpers::ports::get_next_local_port; - -use tari_comms::connection::NetAddress; - -use rand::OsRng; +use multiaddr::{Multiaddr, Protocol}; use std::iter::repeat_with; +use tari_test_utils::address::get_next_local_port; pub fn create_many(n: usize) -> NetAddressesFactory { NetAddressesFactory::default().with_count(n) @@ -48,18 +44,18 @@ impl NetAddressesFactory { factory_setter!(with_net_address_factory, net_address_factory, NetAddressFactory); - fn make_one(&self) -> NetAddress { + fn make_one(&self) -> Multiaddr { self.net_address_factory.clone().build().unwrap() } } impl TestFactory for NetAddressesFactory { - type Object = Vec; + type Object = Vec; fn build(self) -> Result { Ok(repeat_with(|| self.make_one()) .take(self.count.or(Some(1)).unwrap()) - .collect::>()) + .collect()) } } @@ -67,7 +63,6 @@ impl TestFactory for NetAddressesFactory { #[derive(Clone)] pub struct NetAddressFactory { - rng: OsRng, port: Option, host: Option, is_use_os_port: bool, @@ -76,7 +71,6 @@ pub struct NetAddressFactory { impl Default for NetAddressFactory { fn default() -> Self { Self { - rng: OsRng::new().unwrap(), port: None, host: None, is_use_os_port: false, @@ -96,24 +90,22 @@ impl NetAddressFactory { } impl TestFactory for NetAddressFactory { - type Object = NetAddress; + type Object = Multiaddr; fn build(self) -> Result { - let host = self.host.clone().or(Some("127.0.0.1".to_string())).unwrap(); - let port = self - .port - .clone() - .or_else(|| { - if self.is_use_os_port { - Some(0) - } else { - Some(get_next_local_port()) - } - }) - .unwrap(); - - format!("{}:{}", host, port) - .parse() - .map_err(|err| TestFactoryError::BuildFailed(format!("Failed to build NetAddress: {:?}", err))) + let mut addr = format!("/ip4/{}", self.host.unwrap_or("127.0.0.1".to_string())) + .parse::() + .map_err(TestFactoryError::build_failed())?; + + let is_use_os_port = self.is_use_os_port; + addr.push(Protocol::Tcp(self.port.unwrap_or_else(|| { + if is_use_os_port { + 0 + } else { + get_next_local_port() + } + }))); + + Ok(addr) } } diff --git a/comms/tests/support/factories/node_identity.rs b/comms/src/test_utils/factories/node_identity.rs similarity index 70% rename from comms/tests/support/factories/node_identity.rs rename to comms/src/test_utils/factories/node_identity.rs index efd4e3cc49..5d3bff8627 100644 --- a/comms/tests/support/factories/node_identity.rs +++ b/comms/src/test_utils/factories/node_identity.rs @@ -21,13 +21,13 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use super::{TestFactory, TestFactoryError}; -use rand::OsRng; -use tari_comms::{ - connection::NetAddress, - peer_manager::NodeIdentity, - types::{CommsPublicKey, CommsSecretKey}, +use crate::{ + peer_manager::{NodeIdentity, PeerFeatures}, + types::CommsSecretKey, }; -use tari_crypto::keys::{PublicKey, SecretKey}; +use multiaddr::Multiaddr; +use rand::rngs::OsRng; +use tari_crypto::keys::SecretKey; pub fn create() -> NodeIdentityFactory { NodeIdentityFactory::default() @@ -35,21 +35,18 @@ pub fn create() -> NodeIdentityFactory { #[derive(Default, Clone)] pub struct NodeIdentityFactory { - control_service_address: Option, + control_service_address: Option, secret_key: Option, - public_key: Option, + // public_key: Option, + peer_features: PeerFeatures, } impl NodeIdentityFactory { - factory_setter!( - with_control_service_address, - control_service_address, - Option - ); + factory_setter!(with_control_service_address, control_service_address, Option); factory_setter!(with_secret_key, secret_key, Option); - factory_setter!(with_public_key, public_key, Option); + factory_setter!(with_peer_features, peer_features, PeerFeatures); } impl TestFactory for NodeIdentityFactory { @@ -57,21 +54,14 @@ impl TestFactory for NodeIdentityFactory { fn build(self) -> Result { // Generate a test identity, set it and return it - let secret_key = self - .secret_key - .or(Some(CommsSecretKey::random( - &mut OsRng::new().map_err(TestFactoryError::build_failed())?, - ))) - .unwrap(); - let public_key = self - .public_key - .or(Some(CommsPublicKey::from_secret_key(&secret_key))) - .unwrap(); + let secret_key = self.secret_key.or(Some(CommsSecretKey::random(&mut OsRng))).unwrap(); + let control_service_address = self .control_service_address .or(Some(super::net_address::create().build()?)) .unwrap(); - NodeIdentity::new(secret_key, public_key, control_service_address).map_err(TestFactoryError::build_failed()) + NodeIdentity::new(secret_key, control_service_address, self.peer_features) + .map_err(TestFactoryError::build_failed()) } } diff --git a/comms/tests/support/factories/peer.rs b/comms/src/test_utils/factories/peer.rs similarity index 80% rename from comms/tests/support/factories/peer.rs rename to comms/src/test_utils/factories/peer.rs index 738912f493..222bc02daa 100644 --- a/comms/tests/support/factories/peer.rs +++ b/comms/src/test_utils/factories/peer.rs @@ -21,16 +21,14 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use super::{net_address::NetAddressesFactory, TestFactory, TestFactoryError}; - -use tari_comms::{ - connection::NetAddress, - peer_manager::{NodeId, Peer, PeerFlags}, +use crate::{ + peer_manager::{NodeId, Peer, PeerFeatures, PeerFlags}, types::CommsPublicKey, }; - -use crate::support::makers::{comms_keys as ristretto_maker, node_id as node_id_maker}; - +use multiaddr::Multiaddr; +use rand::rngs::OsRng; use std::iter::repeat_with; +use tari_crypto::keys::PublicKey; pub fn create_many(n: usize) -> PeersFactory { PeersFactory::default().with_count(n) @@ -46,7 +44,8 @@ pub struct PeerFactory { flags: Option, public_key: Option, net_addresses_factory: NetAddressesFactory, - net_addresses: Option>, + net_addresses: Option>, + peer_features: PeerFeatures, } impl PeerFactory { @@ -56,39 +55,45 @@ impl PeerFactory { factory_setter!(with_public_key, public_key, Option); + factory_setter!(with_peer_features, peer_features, PeerFeatures); + factory_setter!(with_net_addresses_factory, net_addresses_factory, NetAddressesFactory); - factory_setter!(with_net_addresses, net_addresses, Option>); + factory_setter!(with_net_addresses, net_addresses, Option>); } impl TestFactory for PeerFactory { type Object = Peer; fn build(self) -> Result { - let node_id = self.node_id.clone().or(Some(node_id_maker::make_node_id())).unwrap(); let flags = self.flags.clone().or(Some(PeerFlags::empty())).unwrap().clone(); let public_key = self .public_key .clone() .or_else(|| { - let (_, pk) = ristretto_maker::make_random_keypair(); + let (_, pk) = CommsPublicKey::random_keypair(&mut OsRng); Some(pk) }) .unwrap(); - let addresses = - self.net_addresses - .or(self.net_addresses_factory.build().ok()) - .ok_or(TestFactoryError::BuildFailed(format!( - "Failed to build net addresses for peer" - )))?; + let node_id = self + .node_id + .clone() + .or_else(|| Some(NodeId::from_key(&public_key).unwrap())) + .unwrap(); + + let addresses = self + .net_addresses + .or(self.net_addresses_factory.build().ok()) + .ok_or_else(|| TestFactoryError::BuildFailed(format!("Failed to build net addresses for peer")))?; - Ok(Peer { + Ok(Peer::new( + public_key, node_id, + addresses.into(), flags, - public_key, - addresses: addresses.into(), - }) + self.peer_features, + )) } } diff --git a/comms/tests/support/factories/peer_manager.rs b/comms/src/test_utils/factories/peer_manager.rs similarity index 90% rename from comms/tests/support/factories/peer_manager.rs rename to comms/src/test_utils/factories/peer_manager.rs index 9d04ddb5a2..7f250b9388 100644 --- a/comms/tests/support/factories/peer_manager.rs +++ b/comms/src/test_utils/factories/peer_manager.rs @@ -21,8 +21,7 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use super::{peer::PeersFactory, TestFactory, TestFactoryError}; - -use tari_comms::{ +use crate::{ peer_manager::{Peer, PeerManager}, types::CommsDatabase, }; @@ -50,9 +49,9 @@ impl TestFactory for PeerManagerFactory { type Object = PeerManager; fn build(self) -> Result { - let database = self.database.ok_or(TestFactoryError::BuildFailed( - "Failed to build peer manager: database undefined".into(), - ))?; + let database = self + .database + .ok_or_else(|| TestFactoryError::BuildFailed("Failed to build peer manager: database undefined".into()))?; let pm = PeerManager::new(database) .map_err(|err| TestFactoryError::BuildFailed(format!("Failed to build peer manager: {:?}", err)))?; @@ -60,7 +59,7 @@ impl TestFactory for PeerManagerFactory { let peers = self .peers .or(self.peers_factory.build().ok()) - .ok_or(TestFactoryError::BuildFailed("Failed to build peers".into()))?; + .ok_or_else(|| TestFactoryError::BuildFailed("Failed to build peers".into()))?; for peer in peers { pm.add_peer(peer) .map_err(|err| TestFactoryError::BuildFailed(format!("Failed to build peer manager: {:?}", err)))?; diff --git a/comms/src/test_utils/mod.rs b/comms/src/test_utils/mod.rs new file mode 100644 index 0000000000..735079cf8f --- /dev/null +++ b/comms/src/test_utils/mod.rs @@ -0,0 +1,36 @@ +// Copyright 2019 The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#[allow(dead_code)] +pub mod factories; + +pub mod node_id; +pub mod node_identity; +pub mod peer_manager; +pub mod test_node; +pub mod transport; + +mod connection_manager_mock; +pub use connection_manager_mock::{create_connection_manager_mock, ConnectionManagerMockState}; + +mod peer_connection_mock; +pub use peer_connection_mock::{create_peer_connection_mock_pair, PeerConnectionMockState}; diff --git a/comms/src/test_utils/node_id.rs b/comms/src/test_utils/node_id.rs new file mode 100644 index 0000000000..02a8e6abc6 --- /dev/null +++ b/comms/src/test_utils/node_id.rs @@ -0,0 +1,30 @@ +// Copyright 2019 The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{peer_manager::NodeId, types::CommsPublicKey}; +use rand::rngs::OsRng; +use tari_crypto::keys::PublicKey; + +pub fn random() -> NodeId { + let (_, pk) = CommsPublicKey::random_keypair(&mut OsRng); + NodeId::from_key(&pk).unwrap() +} diff --git a/base_layer/core/tests/tests/block_validation.rs b/comms/src/test_utils/node_identity.rs similarity index 70% rename from base_layer/core/tests/tests/block_validation.rs rename to comms/src/test_utils/node_identity.rs index e624e19274..2febb697a5 100644 --- a/base_layer/core/tests/tests/block_validation.rs +++ b/comms/src/test_utils/node_identity.rs @@ -20,22 +20,20 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::support::simple_block_chain::*; -use std::fs; -use tari_core::consensus::ConsensusRules; +use crate::peer_manager::{NodeIdentity, PeerFeatures}; +use rand::rngs::OsRng; +use std::sync::Arc; +use tari_test_utils::address::get_next_local_address; -fn load_test_block_chain_from_file() -> SimpleBlockChain { - let read_json = fs::read_to_string("tests/chain/chain.json").unwrap(); - let blockchain: SimpleBlockChain = serde_json::from_str(&read_json).unwrap(); - blockchain +pub fn build_node_identity(features: PeerFeatures) -> Arc { + let public_addr = get_next_local_address().parse().unwrap(); + Arc::new(NodeIdentity::random(&mut OsRng, public_addr, features).unwrap()) } -//#[test] -fn test_valid_blocks() { - let rules = ConsensusRules::current(); - let chain = load_test_block_chain_from_file(); - for i in 0..chain.blocks.len() { - chain.blocks[i] - .check_internal_consistency(&rules) - .expect("Block validation failed") - } + +pub fn ordered_node_identities(n: usize) -> Vec> { + let mut ids = (0..n) + .map(|_| build_node_identity(PeerFeatures::default())) + .collect::>(); + ids.sort_unstable_by(|a, b| a.node_id().cmp(b.node_id())); + ids } diff --git a/comms/src/test_utils/peer_connection_mock.rs b/comms/src/test_utils/peer_connection_mock.rs new file mode 100644 index 0000000000..21deac9124 --- /dev/null +++ b/comms/src/test_utils/peer_connection_mock.rs @@ -0,0 +1,154 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + connection_manager::{ + ConnectionDirection, + NegotiatedSubstream, + PeerConnection, + PeerConnectionError, + PeerConnectionRequest, + }, + multiplexing, + multiplexing::{IncomingSubstreams, Yamux}, + peer_manager::NodeId, + test_utils::transport, +}; +use futures::{channel::mpsc, lock::Mutex, stream::Fuse, StreamExt}; +use std::sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, +}; +use tokio::runtime::Handle; + +pub async fn create_peer_connection_mock_pair( + buf_size: usize, + node_id_in: NodeId, + node_id_out: NodeId, +) -> ( + PeerConnection, + PeerConnectionMockState, + PeerConnection, + PeerConnectionMockState, +) +{ + let rt_handle = Handle::current(); + let (tx1, rx1) = mpsc::channel(buf_size); + let (tx2, rx2) = mpsc::channel(buf_size); + let (listen_addr, muxer_in, muxer_out) = transport::build_multiplexed_connections().await; + + // Start both mocks on current handle + let mock = PeerConnectionMock::new(rx1.fuse(), muxer_in); + let mock_state_in = mock.get_shared_state(); + rt_handle.spawn(mock.run()); + let mock = PeerConnectionMock::new(rx2.fuse(), muxer_out); + let mock_state_out = mock.get_shared_state(); + rt_handle.spawn(mock.run()); + + ( + PeerConnection::new(1, tx1, node_id_in, listen_addr.clone(), ConnectionDirection::Inbound), + mock_state_in, + PeerConnection::new(2, tx2, node_id_out, listen_addr, ConnectionDirection::Outbound), + mock_state_out, + ) +} + +#[derive(Debug, Clone)] +pub struct PeerConnectionMockState { + call_count: Arc, + mux_control: Arc>, + mux_incoming: Arc>, +} + +impl PeerConnectionMockState { + pub fn new(muxer: Yamux) -> Self { + Self { + call_count: Arc::new(AtomicUsize::new(0)), + mux_control: Arc::new(Mutex::new(muxer.get_yamux_control())), + mux_incoming: Arc::new(Mutex::new(muxer.incoming())), + } + } + + pub fn inc_call_count(&self) { + self.call_count.fetch_add(1, Ordering::SeqCst); + } + + pub fn call_count(&self) -> usize { + self.call_count.load(Ordering::SeqCst) + } + + pub async fn open_substream(&self) -> Result { + self.mux_control.lock().await.open_stream().await.map_err(Into::into) + } + + pub async fn next_incoming_substream(&self) -> Option { + self.mux_incoming.lock().await.next().await.map(Result::unwrap) + } + + pub async fn disconnect(&self) { + self.mux_control.lock().await.close().await.unwrap(); + } +} + +pub struct PeerConnectionMock { + receiver: Fuse>, + state: PeerConnectionMockState, +} + +impl PeerConnectionMock { + pub fn new(receiver: Fuse>, muxer: Yamux) -> Self { + Self { + receiver, + state: PeerConnectionMockState::new(muxer), + } + } + + pub fn get_shared_state(&self) -> PeerConnectionMockState { + self.state.clone() + } + + pub async fn run(mut self) { + while let Some(req) = self.receiver.next().await { + self.handle_request(req).await; + } + } + + async fn handle_request(&mut self, req: PeerConnectionRequest) { + use PeerConnectionRequest::*; + self.state.inc_call_count(); + match req { + OpenSubstream(protocol, reply_tx) => match self.state.open_substream().await { + Ok(stream) => { + let negotiated_substream = NegotiatedSubstream { protocol, stream }; + reply_tx.send(Ok(negotiated_substream)).unwrap(); + }, + Err(err) => { + reply_tx.send(Err(err.into())).unwrap(); + }, + }, + Disconnect(_, reply_tx) => { + self.state.disconnect().await; + reply_tx.send(()).unwrap(); + }, + } + } +} diff --git a/base_layer/p2p/src/services/service_name.rs b/comms/src/test_utils/peer_manager.rs similarity index 86% rename from base_layer/p2p/src/services/service_name.rs rename to comms/src/test_utils/peer_manager.rs index a561471013..fa94392532 100644 --- a/base_layer/p2p/src/services/service_name.rs +++ b/comms/src/test_utils/peer_manager.rs @@ -1,4 +1,4 @@ -// Copyright 2019 The Tari Project +// Copyright 2020, The Tari Project // // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the // following conditions are met: @@ -20,11 +20,9 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -/// Used to name services and retrieve service handles -#[derive(Eq, PartialEq, Hash)] -pub enum ServiceName { - /// Outbound comms service - CommsOutbound, - /// Liveness service - Liveness, +use crate::{peer_manager::PeerManager, types::CommsDatabase}; +use std::sync::Arc; + +pub fn build_peer_manager() -> Arc { + PeerManager::new(CommsDatabase::new()).map(Arc::new).unwrap() } diff --git a/comms/src/test_utils/test_node.rs b/comms/src/test_utils/test_node.rs new file mode 100644 index 0000000000..8197584ca8 --- /dev/null +++ b/comms/src/test_utils/test_node.rs @@ -0,0 +1,104 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + backoff::ConstantBackoff, + connection_manager::{ConnectionManager, ConnectionManagerConfig, ConnectionManagerRequester}, + noise::NoiseConfig, + peer_manager::{NodeIdentity, PeerFeatures, PeerManager}, + protocol::Protocols, + transports::MemoryTransport, +}; +use futures::channel::mpsc; +use rand::rngs::OsRng; +use std::{sync::Arc, time::Duration}; +use tari_shutdown::ShutdownSignal; +use tari_storage::HashmapDatabase; +use tokio::{runtime, sync::broadcast}; + +#[derive(Clone, Debug)] +pub struct TestNodeConfig { + pub transport: MemoryTransport, + pub dial_backoff_duration: Duration, + pub connection_manager_config: ConnectionManagerConfig, + pub node_identity: Arc, +} + +impl Default for TestNodeConfig { + fn default() -> Self { + let node_identity = Arc::new( + NodeIdentity::random( + &mut OsRng, + "/memory/0".parse().unwrap(), + PeerFeatures::COMMUNICATION_NODE, + ) + .unwrap(), + ); + + Self { + transport: MemoryTransport, + connection_manager_config: ConnectionManagerConfig { + listener_address: "/memory/0".parse().unwrap(), + ..Default::default() + }, + dial_backoff_duration: Duration::from_millis(200), + node_identity, + } + } +} + +pub fn build_connection_manager( + executor: runtime::Handle, + config: TestNodeConfig, + peer_manager: Arc, + protocols: Protocols, + shutdown: ShutdownSignal, +) -> ConnectionManagerRequester +{ + let noise_config = NoiseConfig::new(config.node_identity.clone()); + let (request_tx, request_rx) = mpsc::channel(10); + let (event_tx, _) = broadcast::channel(100); + + let requester = ConnectionManagerRequester::new(request_tx, event_tx.clone()); + + let connection_manager = ConnectionManager::new( + config.connection_manager_config, + executor.clone(), + config.transport, + noise_config, + ConstantBackoff::new(config.dial_backoff_duration), + request_rx, + config.node_identity, + peer_manager.into(), + protocols, + event_tx, + shutdown, + ); + + executor.spawn(connection_manager.run()); + + requester +} + +pub fn build_peer_manager() -> Arc { + Arc::new(PeerManager::new(HashmapDatabase::new()).unwrap()) +} diff --git a/comms/src/test_utils/transport.rs b/comms/src/test_utils/transport.rs new file mode 100644 index 0000000000..02bcc5adbe --- /dev/null +++ b/comms/src/test_utils/transport.rs @@ -0,0 +1,56 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + connection_manager::ConnectionDirection, + memsocket::MemorySocket, + multiaddr::Multiaddr, + multiplexing::Yamux, + transports::{MemoryTransport, Transport}, +}; +use futures::{future, StreamExt}; +use tokio::runtime::Handle; + +pub async fn build_connected_sockets() -> (Multiaddr, MemorySocket, MemorySocket) { + let (mut listener, addr) = MemoryTransport + .listen("/memory/0".parse().unwrap()) + .unwrap() + .await + .unwrap(); + let (dial_sock, listen_sock) = future::join(MemoryTransport.dial(addr.clone()).unwrap(), listener.next()).await; + (addr, dial_sock.unwrap(), listen_sock.unwrap().unwrap().0.await.unwrap()) +} + +pub async fn build_multiplexed_connections() -> (Multiaddr, Yamux, Yamux) { + let rt_handle = Handle::current(); + let (addr, socket_out, socket_in) = build_connected_sockets().await; + + let muxer_out = Yamux::upgrade_connection(rt_handle.clone(), socket_out, ConnectionDirection::Outbound) + .await + .unwrap(); + + let muxer_in = Yamux::upgrade_connection(rt_handle, socket_in, ConnectionDirection::Inbound) + .await + .unwrap(); + + (addr, muxer_out, muxer_in) +} diff --git a/comms/src/tor/client/client.rs b/comms/src/tor/client/client.rs new file mode 100644 index 0000000000..d67d26ac12 --- /dev/null +++ b/comms/src/tor/client/client.rs @@ -0,0 +1,490 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::{ + commands, + commands::{AddOnionFlag, AddOnionResponse, TorCommand}, + error::TorClientError, + parsers, + response::{ResponseLine, EVENT_CODE}, + types::{KeyBlob, KeyType, PortMapping}, + PrivateKey, +}; +use crate::{ + compat::IoCompat, + multiaddr::Multiaddr, + transports::{TcpTransport, Transport}, +}; +use futures::{AsyncRead, AsyncWrite, SinkExt, StreamExt}; +use std::{borrow::Cow, num::NonZeroU16}; +use tokio_util::codec::{Framed, LinesCodec}; + +/// Client for the Tor control port. +/// +/// See the [Tor Control Port Spec](https://gitweb.torproject.org/torspec.git/tree/control-spec.txt) for more details. +pub struct TorControlPortClient { + framed: Framed, LinesCodec>, +} + +impl TorControlPortClient<::Output> { + /// Connect using TCP to the given address. + pub async fn connect(addr: Multiaddr) -> Result { + let mut tcp = TcpTransport::new(); + tcp.set_nodelay(true); + let socket = tcp.dial(addr)?.await?; + Ok(Self::new(socket)) + } +} + +/// Represents tor control port authentication mechanisms +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Authentication { + /// No control port authentication required + None, + /// A hashed password will be sent to authenticate + HashedPassword(String), +} +impl Default for Authentication { + fn default() -> Self { + Authentication::None + } +} + +impl TorControlPortClient +where TSocket: AsyncRead + AsyncWrite + Unpin +{ + /// Create a new TorControlPortClient using the given socket + pub fn new(socket: TSocket) -> Self { + Self { + framed: Framed::new(IoCompat::new(socket), LinesCodec::new()), + } + } + + /// Authenticate with the tor control port + pub async fn authenticate(&mut self, authentication: &Authentication) -> Result<(), TorClientError> { + match authentication { + Authentication::None => { + self.send_line("AUTHENTICATE".to_string()).await?; + }, + Authentication::HashedPassword(passwd) => { + self.send_line(format!("AUTHENTICATE \"{}\"", passwd.replace("\"", "\\\""))) + .await?; + }, + } + + self.recv_ok().await?; + + Ok(()) + } + + /// The GETCONF command. Returns configuration keys matching the `conf_name`. + pub async fn get_conf<'a>(&mut self, conf_name: &'a str) -> Result>, TorClientError> { + let command = commands::get_conf(conf_name); + self.request_response(command).await + } + + /// The GETINFO command. Returns configuration keys matching the `conf_name`. + pub async fn get_info<'a>(&mut self, key_name: &'a str) -> Result, TorClientError> { + let command = commands::get_info(key_name); + let mut response = self.request_response(command).await?; + if response.len() == 0 { + return Err(TorClientError::ServerNoResponse); + } + Ok(response.remove(0)) + } + + /// The ADD_ONION command, used to create onion hidden services. + pub async fn add_onion_custom>( + &mut self, + key_type: KeyType, + key_blob: KeyBlob<'_>, + flags: Vec, + port: P, + num_streams: Option, + ) -> Result + { + let command = commands::AddOnion::new(key_type, key_blob, flags, port.into(), num_streams); + self.request_response(command).await + } + + /// The ADD_ONION command using a v2 key + pub async fn add_onion_v2>( + &mut self, + flags: Vec, + port: P, + num_streams: Option, + ) -> Result + { + self.add_onion_custom(KeyType::New, KeyBlob::Rsa1024, flags, port, num_streams) + .await + } + + /// The ADD_ONION command using the 'best' key. The 'best' key is determined by the tor proxy. At the time of + /// writing tor will select a Ed25519 key. + pub async fn add_onion>( + &mut self, + flags: Vec, + port: P, + num_streams: Option, + ) -> Result + { + self.add_onion_custom(KeyType::New, KeyBlob::Best, flags, port, num_streams) + .await + } + + /// The ADD_ONION command using the given `PrivateKey`. + pub async fn add_onion_from_private_key>( + &mut self, + private_key: &PrivateKey, + flags: Vec, + port: P, + num_streams: Option, + ) -> Result + { + let (key_type, key_blob) = match private_key { + PrivateKey::Rsa1024(key) => (KeyType::Rsa1024, KeyBlob::String(key)), + PrivateKey::Ed25519V3(key) => (KeyType::Ed25519V3, KeyBlob::String(key)), + }; + self.add_onion_custom(key_type, key_blob, flags, port, num_streams) + .await + } + + /// The DEL_ONION command. + pub async fn del_onion(&mut self, service_id: &str) -> Result<(), TorClientError> { + let command = commands::DelOnion::new(service_id); + self.request_response(command).await + } + + async fn request_response(&mut self, command: T) -> Result + where T::Error: Into { + self.send_line(command.to_command_string().map_err(Into::into)?).await?; + let responses = self.recv_next_responses().await?; + if responses.len() == 0 { + return Err(TorClientError::ServerNoResponse); + } + let response = command.parse_responses(responses).map_err(Into::into)?; + Ok(response) + } + + async fn send_line(&mut self, line: String) -> Result<(), TorClientError> { + self.framed.send(line).await.map_err(Into::into) + } + + async fn recv_ok(&mut self) -> Result<(), TorClientError> { + let line = self.receive_line().await?; + let resp = parsers::response_line(&line)?; + if resp.is_ok() { + Ok(()) + } else { + Err(TorClientError::TorCommandFailed(resp.value.into_owned())) + } + } + + async fn recv_next_responses(&mut self) -> Result>, TorClientError> { + let mut msgs = Vec::new(); + loop { + let line = self.receive_line().await?; + let mut msg = parsers::response_line(&line)?; + // Ignore event codes (for now) + if msg.code == EVENT_CODE { + continue; + } + if msg.is_multiline { + let lines = self.receive_multiline().await?; + msg.value = Cow::from(format!("{}\n{}", msg.value, lines.join("\n"))); + } + + let has_more = msg.has_more(); + msgs.push(msg.into_owned()); + if !has_more { + break; + } + } + + Ok(msgs) + } + + async fn receive_line(&mut self) -> Result { + let line = self + .framed + .next() + .await + .ok_or_else(|| TorClientError::UnexpectedEof)??; + + Ok(line) + } + + async fn receive_multiline(&mut self) -> Result, TorClientError> { + let mut lines = Vec::new(); + loop { + let line = self.receive_line().await?; + let trimmed = line.trim(); + if trimmed == "." { + break; + } + lines.push(trimmed.to_string()); + } + + Ok(lines) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::{ + memsocket::MemorySocket, + tor::client::{test_server, test_server::canned_responses, types::PrivateKey}, + }; + use futures::future; + use std::net::SocketAddr; + use tari_test_utils::unpack_enum; + + async fn setup_test() -> (TorControlPortClient, test_server::State) { + let (_, mock_state, socket) = test_server::spawn().await; + let tor = TorControlPortClient::new(socket); + (tor, mock_state) + } + + #[tokio_macros::test] + async fn connect() { + let (mut listener, addr) = TcpTransport::default() + .listen("/ip4/127.0.0.1/tcp/0".parse().unwrap()) + .unwrap() + .await + .unwrap(); + let (result_out, result_in) = future::join(TorControlPortClient::connect(addr), listener.next()).await; + + // Check that the connection is successfully made + result_out.unwrap(); + result_in.unwrap().unwrap().0.await.unwrap(); + } + + #[tokio_macros::test] + async fn authenticate() { + let (mut tor, mock_state) = setup_test().await; + + tor.authenticate(&Authentication::None).await.unwrap(); + let mut req = mock_state.take_requests().await; + assert_eq!(req.len(), 1); + assert_eq!(req.remove(0), "AUTHENTICATE"); + + tor.authenticate(&Authentication::HashedPassword("ab\"cde".to_string())) + .await + .unwrap(); + let mut req = mock_state.take_requests().await; + assert_eq!(req.len(), 1); + assert_eq!(req.remove(0), "AUTHENTICATE \"ab\\\"cde\""); + } + + #[tokio_macros::test] + async fn get_conf_ok() { + let (mut tor, mock_state) = setup_test().await; + + mock_state + .set_canned_response(canned_responses::GET_CONF_HIDDEN_SERVICE_PORT_OK) + .await; + + let results = tor.get_conf("HiddenServicePort").await.unwrap(); + assert_eq!(results.len(), 3); + assert_eq!(results[0], "8080"); + assert_eq!(results[1], "8081 127.0.0.1:9000"); + assert_eq!(results[2], "8082 127.0.0.1:9001"); + } + + #[tokio_macros::test] + async fn get_conf_err() { + let (mut tor, mock_state) = setup_test().await; + + mock_state.set_canned_response(canned_responses::ERR_552).await; + + let err = tor.get_conf("HiddenServicePort").await.unwrap_err(); + unpack_enum!(TorClientError::TorCommandFailed(_s) = err); + } + + #[tokio_macros::test] + async fn get_info_multiline_kv_ok() { + let (mut tor, mock_state) = setup_test().await; + + mock_state + .set_canned_response(canned_responses::GET_INFO_NET_LISTENERS_OK) + .await; + + let value = tor.get_info("net/listeners/socks").await.unwrap(); + assert_eq!(value, "127.0.0.1:9050"); + } + + #[tokio_macros::test] + async fn get_info_kv_multiline_value_ok() { + let (mut tor, mock_state) = setup_test().await; + + mock_state + .set_canned_response(canned_responses::GET_INFO_ONIONS_DETACHED_OK) + .await; + + let value = tor.get_info("onions/detached").await.unwrap(); + assert_eq!(value.split('\n').collect::>(), [ + "mochz2xppfziim5olr5f6q27poc4vfob2xxxxxxxxxxxxxxxxxxxxxxx", + "nhqdqym6j35rk7tdou4cdj4gjjqagimutxxxxxxxxxxxxxxxxxxxxxxx" + ]); + } + + #[tokio_macros::test] + async fn get_info_err() { + let (mut tor, mock_state) = setup_test().await; + + mock_state.set_canned_response(canned_responses::ERR_552).await; + + let err = tor.get_info("net/listeners/socks").await.unwrap_err(); + unpack_enum!(TorClientError::TorCommandFailed(_s) = err); + } + + #[tokio_macros::test] + async fn add_onion_from_private_key_ok() { + let (mut tor, mock_state) = setup_test().await; + + mock_state + .set_canned_response(canned_responses::ADD_ONION_RSA1024_OK) + .await; + + let private_key = PrivateKey::Rsa1024("dummy-key".into()); + let response = tor + .add_onion_from_private_key(&private_key, vec![], 8080, None) + .await + .unwrap(); + + assert_eq!(response.service_id, "62q4tswkxp74dtn7"); + assert!(response.private_key.is_none()); + + let request = mock_state.take_requests().await.pop().unwrap(); + assert_eq!(request, "ADD_ONION RSA1024:dummy-key Port=8080,127.0.0.1:8080"); + } + + #[tokio_macros::test] + async fn add_onion_ok() { + let (mut tor, mock_state) = setup_test().await; + + mock_state.set_canned_response(canned_responses::ADD_ONION_OK).await; + + let response = tor + .add_onion_custom( + KeyType::New, + KeyBlob::Best, + vec![], + 8080, + Some(NonZeroU16::new(10u16).unwrap()), + ) + .await + .unwrap(); + + assert_eq!( + response.service_id, + "qigbgbs4ue3ghbupsotgh73cmmkjrin2aprlyxsrnrvpmcmzy3g4wbid" + ); + assert_eq!( + response.private_key, + Some(PrivateKey::Ed25519V3( + "Pg3GEyssauPRW3jP6mHwKOxvl_fMsF0QsZC3DvQ8jZ9AxmfRvSP35m9l0vOYyOxkOqWM6ufjdYuM8Ae6cR2UdreG6".to_string() + )) + ); + + let request = mock_state.take_requests().await.pop().unwrap(); + assert_eq!(request, "ADD_ONION NEW:BEST NumStreams=10 Port=8080,127.0.0.1:8080"); + } + + #[tokio_macros::test] + async fn add_onion_discard_pk_ok() { + let (mut tor, mock_state) = setup_test().await; + + mock_state + .set_canned_response(canned_responses::ADD_ONION_DISCARDPK_OK) + .await; + + let response = tor + .add_onion_custom( + KeyType::Rsa1024, + KeyBlob::Rsa1024, + vec![ + AddOnionFlag::DiscardPK, + AddOnionFlag::Detach, + AddOnionFlag::BasicAuth, + AddOnionFlag::MaxStreamsCloseCircuit, + AddOnionFlag::NonAnonymous, + ], + PortMapping::new(8080, SocketAddr::from(([127u8, 0, 0, 1], 8081u16))), + None, + ) + .await + .unwrap(); + + assert_eq!( + response.service_id, + "qigbgbs4ue3ghbupsotgh73cmmkjrin2aprlyxsrnrvpmcmzy3g4wbid" + ); + assert_eq!(response.private_key, None); + + let request = mock_state.take_requests().await.pop().unwrap(); + assert_eq!( + request, + "ADD_ONION RSA1024:RSA1024 Flags=DiscardPK,Detach,BasicAuth,MaxStreamsCloseCircuit,NonAnonymous \ + Port=8080,127.0.0.1:8081" + ); + } + + #[tokio_macros::test] + async fn add_onion_err() { + let (mut tor, mock_state) = setup_test().await; + + mock_state.set_canned_response(canned_responses::ERR_552).await; + + let err = tor + .add_onion_custom(KeyType::Ed25519V3, KeyBlob::Ed25519V3, vec![], 8080, None) + .await + .unwrap_err(); + + unpack_enum!(TorClientError::TorCommandFailed(_s) = err); + } + + #[tokio_macros::test] + async fn del_onion_ok() { + let (mut tor, mock_state) = setup_test().await; + + mock_state.set_canned_response(canned_responses::OK).await; + + tor.del_onion("some-fake-id").await.unwrap(); + + let request = mock_state.take_requests().await.pop().unwrap(); + assert_eq!(request, "DEL_ONION some-fake-id"); + } + + #[tokio_macros::test] + async fn del_onion_err() { + let (mut tor, mock_state) = setup_test().await; + + mock_state.set_canned_response(canned_responses::ERR_552).await; + + tor.del_onion("some-fake-id").await.unwrap_err(); + + let request = mock_state.take_requests().await.pop().unwrap(); + assert_eq!(request, "DEL_ONION some-fake-id"); + } +} diff --git a/comms/src/tor/client/commands/add_onion.rs b/comms/src/tor/client/commands/add_onion.rs new file mode 100644 index 0000000000..b65fb27417 --- /dev/null +++ b/comms/src/tor/client/commands/add_onion.rs @@ -0,0 +1,200 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::tor::client::{ + commands::TorCommand, + error::TorClientError, + parsers, + parsers::ParseError, + response::ResponseLine, + types::{KeyBlob, KeyType, PortMapping, PrivateKey}, +}; +use std::{borrow::Cow, num::NonZeroU16}; + +pub enum AddOnionFlag { + /// The server should not include the newly generated private key as part of the response. + DiscardPK, + /// Do not associate the newly created Onion Service to the current control connection. + Detach, + /// Client authorization is required using the "basic" method (v2 only). + BasicAuth, + /// Add a non-anonymous Single Onion Service. Tor checks this flag matches its configured hidden service anonymity + /// mode. + NonAnonymous, + /// Close the circuit is the maximum streams allowed is reached. + MaxStreamsCloseCircuit, +} + +impl ToString for AddOnionFlag { + fn to_string(&self) -> String { + use AddOnionFlag::*; + match self { + DiscardPK => "DiscardPK".to_string(), + Detach => "Detach".to_string(), + BasicAuth => "BasicAuth".to_string(), + NonAnonymous => "NonAnonymous".to_string(), + MaxStreamsCloseCircuit => "MaxStreamsCloseCircuit".to_string(), + } + } +} + +/// The ADD_ONION command. +/// +/// This command instructs Tor to create onion hidden services. +pub struct AddOnion<'a> { + key_type: KeyType, + key_blob: KeyBlob<'a>, + flags: Vec, + port_mapping: PortMapping, + num_streams: Option, +} + +impl<'a> AddOnion<'a> { + pub fn new( + key_type: KeyType, + key_blob: KeyBlob<'a>, + flags: Vec, + port_mapping: PortMapping, + num_streams: Option, + ) -> Self + { + Self { + key_type, + key_blob, + flags, + port_mapping, + num_streams, + } + } +} + +impl TorCommand for AddOnion<'_> { + type Error = TorClientError; + type Output = AddOnionResponse; + + fn to_command_string(&self) -> Result { + let mut s = String::from("ADD_ONION "); + + s.push_str(self.key_type.as_tor_repr()); + s.push(':'); + s.push_str(self.key_blob.as_tor_repr()); + + if self.flags.len() > 0 { + let flags = self.flags.iter().map(|f| f.to_string()).collect::>().join(","); + s.push_str(&format!(" Flags={}", flags)); + } + + if let Some(num_streams) = self.num_streams { + s.push_str(&format!(" NumStreams={}", num_streams)); + } + + s.push_str(&format!( + " Port={},{}", + self.port_mapping.onion_port(), + self.port_mapping.proxied_address() + )); + + Ok(s) + } + + fn parse_responses(&self, mut responses: Vec>) -> Result { + let last_response = responses.pop().ok_or_else(|| TorClientError::UnexpectedEof)?; + if let Some(err) = last_response.err() { + if err.contains("Onion address collision") { + return Err(TorClientError::OnionAddressCollision); + } + return Err(TorClientError::TorCommandFailed(err.into_owned())); + } + + let mut service_id = None; + let mut private_key = None; + + for response in responses { + let (key, value) = parsers::key_value(&response.value)?; + match &*key { + "ServiceID" => { + service_id = Some(value.into_owned()); + }, + "PrivateKey" => { + let mut split = value.split(':'); + let key = split + .next() + .ok_or_else(|| ParseError("PrivateKey field was empty".to_string()))?; + + let value = split + .next() + .map(|v| Cow::from(v.to_owned())) + .ok_or_else(|| ParseError("Failed to parse private key".to_string()))?; + + private_key = match key { + "ED25519-V3" => Some(PrivateKey::Ed25519V3(value.into_owned())), + "RSA1024" => Some(PrivateKey::Rsa1024(value.into_owned())), + k => { + return Err( + ParseError(format!("Server returned unrecognised private key type '{}'", k)).into(), + ) + }, + }; + }, + _ => { + // Ignore key's we don't understand + }, + } + } + + let service_id = service_id.ok_or_else(|| TorClientError::AddOnionNoServiceId)?; + + Ok(AddOnionResponse { + service_id, + private_key, + onion_port: self.port_mapping.onion_port(), + }) + } +} + +#[derive(Debug, Clone)] +pub struct AddOnionResponse { + pub(crate) service_id: String, + pub(crate) private_key: Option, + pub(crate) onion_port: u16, +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn to_command_string() { + let key = "this-is-a-key".to_string(); + let command = AddOnion::new( + KeyType::New, + KeyBlob::String(&key), + vec![], + PortMapping::from_port(9090), + None, + ); + assert_eq!( + command.to_command_string().unwrap(), + format!("ADD_ONION NEW:{} Port=9090,127.0.0.1:9090", key) + ); + } +} diff --git a/comms/src/tor/client/commands/del_onion.rs b/comms/src/tor/client/commands/del_onion.rs new file mode 100644 index 0000000000..da27222c94 --- /dev/null +++ b/comms/src/tor/client/commands/del_onion.rs @@ -0,0 +1,65 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::tor::client::{commands::TorCommand, error::TorClientError, response::ResponseLine}; + +/// The DEL_ONION command. +/// +/// This instructs Tor to delete a hidden service. +pub struct DelOnion<'a> { + service_id: &'a str, +} + +impl<'a> DelOnion<'a> { + pub fn new(service_id: &'a str) -> Self { + Self { service_id } + } +} + +impl<'a> TorCommand for DelOnion<'a> { + type Error = TorClientError; + type Output = (); + + fn to_command_string(&self) -> Result { + Ok(format!("DEL_ONION {}", self.service_id)) + } + + fn parse_responses(&self, mut responses: Vec>) -> Result { + let last_response = responses.pop().ok_or_else(|| TorClientError::UnexpectedEof)?; + if let Some(err) = last_response.err() { + return Err(TorClientError::TorCommandFailed(err.into_owned())); + } + + Ok(()) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn to_command_string() { + let command = DelOnion::new("some-random-key"); + assert_eq!(command.to_command_string().unwrap(), "DEL_ONION some-random-key"); + } +} diff --git a/comms/src/tor/client/commands/key_value.rs b/comms/src/tor/client/commands/key_value.rs new file mode 100644 index 0000000000..a9232ee899 --- /dev/null +++ b/comms/src/tor/client/commands/key_value.rs @@ -0,0 +1,112 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::tor::client::{commands::TorCommand, error::TorClientError, parsers, response::ResponseLine}; +use std::{borrow::Cow, marker::PhantomData}; + +/// The GETCONF command. +/// +/// This command is used to query the Tor proxy configuration file. +pub fn get_conf(query: &str) -> KeyValueCommand<'_, '_> { + KeyValueCommand::new("GETCONF", &[query]) +} + +/// The GETINFO command. +/// +/// This command is used to retrieve Tor proxy configuration keys. +pub fn get_info(key_name: &str) -> KeyValueCommand<'_, '_> { + KeyValueCommand::new("GETINFO", &[key_name]) +} + +pub struct KeyValueCommand<'a, 'b> { + command: &'a str, + args: Vec<&'b str>, + _lifetime: PhantomData<&'b ()>, +} + +impl<'a, 'b> KeyValueCommand<'a, 'b> { + pub fn new(command: &'a str, args: &[&'b str]) -> Self { + Self { + command, + args: args.to_vec(), + _lifetime: PhantomData, + } + } +} + +impl<'a, 'b> TorCommand for KeyValueCommand<'a, 'b> { + type Error = TorClientError; + type Output = Vec>; + + fn to_command_string(&self) -> Result { + Ok(format!("{} {}", self.command, self.args.join(" "))) + } + + fn parse_responses(&self, mut responses: Vec>) -> Result { + if let Some(resp) = responses.iter().find(|v| v.is_err()) { + return Err(TorClientError::TorCommandFailed(resp.value.to_string())); + } + + if let Some(last_line) = responses.last() { + // Drop the last line if it's '250 OK' - some commands return it (GETINFO), some don't (GETCONF) + if last_line.value == "OK" { + let _ = responses.pop(); + } + } + + let responses = responses + .iter() + .map(|resp| parsers::key_value(&resp.value)) + .collect::>(); + + // Return the first parse error if any + if let Some(Err(err)) = responses.iter().find(|v| v.is_err()) { + return Err(err.clone().into()); + } + + Ok(responses + .iter() + .filter_map(|r| r.as_ref().ok()) + .map(|(_, value)| Cow::from(value.clone().into_owned())) + .collect()) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn to_command_string() { + let command = KeyValueCommand::new("GETCONF", &["HiddenServicePort"]); + assert_eq!(command.to_command_string().unwrap(), "GETCONF HiddenServicePort"); + + let command = KeyValueCommand::new("GETINFO", &["net/listeners/socks"]); + assert_eq!(command.to_command_string().unwrap(), "GETINFO net/listeners/socks"); + } + + // #[test] + // fn parse_responses() { + // let command = KeyValueCommand::new("", &[]); + // command.parse_responses(vec!) + // } +} diff --git a/comms/src/tor/client/commands/mod.rs b/comms/src/tor/client/commands/mod.rs new file mode 100644 index 0000000000..be9815e1ca --- /dev/null +++ b/comms/src/tor/client/commands/mod.rs @@ -0,0 +1,40 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::tor::client::response::ResponseLine; + +mod add_onion; +mod del_onion; +mod key_value; + +pub use add_onion::{AddOnion, AddOnionFlag, AddOnionResponse}; +pub use del_onion::DelOnion; +pub use key_value::{get_conf, get_info, KeyValueCommand}; + +pub trait TorCommand { + type Output; + type Error; + + fn to_command_string(&self) -> Result; + + fn parse_responses(&self, responses: Vec>) -> Result; +} diff --git a/comms/src/tor/client/error.rs b/comms/src/tor/client/error.rs new file mode 100644 index 0000000000..f5c6b9328f --- /dev/null +++ b/comms/src/tor/client/error.rs @@ -0,0 +1,56 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use super::parsers::ParseError; +use derive_error::Error; +use std::io; +use tokio_util::codec::LinesCodecError; + +#[derive(Debug, Error)] +pub enum TorClientError { + /// Failed to read/write line to socket. The maximum line length was exceeded. + MaxLineLengthExceeded, + Io(io::Error), + /// Command failed + #[error(no_from, non_std)] + TorCommandFailed(String), + /// Tor control port connection unexpectedly closed + UnexpectedEof, + ParseError(ParseError), + /// The server returned no response + ServerNoResponse, + /// Server did not return a ServiceID for ADD_ONION command + AddOnionNoServiceId, + /// The given service id was invalid + InvalidServiceId, + /// Onion address is exists + OnionAddressCollision, +} + +impl From for TorClientError { + fn from(err: LinesCodecError) -> Self { + use LinesCodecError::*; + match err { + MaxLineLengthExceeded => TorClientError::MaxLineLengthExceeded, + Io(err) => TorClientError::Io(err), + } + } +} diff --git a/comms/src/tor/client/mod.rs b/comms/src/tor/client/mod.rs new file mode 100644 index 0000000000..d20637d3a0 --- /dev/null +++ b/comms/src/tor/client/mod.rs @@ -0,0 +1,38 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod client; +pub use client::{Authentication, TorControlPortClient}; + +mod error; +pub use error::TorClientError; + +pub mod commands; + +mod parsers; +mod response; + +mod types; +pub use types::{KeyBlob, KeyType, PortMapping, PrivateKey}; + +#[cfg(test)] +mod test_server; diff --git a/infrastructure/crypto/src/common.rs b/comms/src/tor/client/parsers.rs similarity index 50% rename from infrastructure/crypto/src/common.rs rename to comms/src/tor/client/parsers.rs index 4370396623..242bf72e77 100644 --- a/infrastructure/crypto/src/common.rs +++ b/comms/src/tor/client/parsers.rs @@ -1,4 +1,4 @@ -// Copyright 2019 The Tari Project +// Copyright 2020, The Tari Project // // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the // following conditions are met: @@ -20,71 +20,56 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use blake2::VarBlake2b; -use digest::{ - generic_array::{typenum::U32, GenericArray}, - FixedOutput, - Input, - Reset, - VariableOutput, +use super::response::ResponseLine; +use nom::{ + bytes::complete::take_while1, + character::complete::{anychar, char as chr, digit1}, + combinator::map_res, + error::ErrorKind, }; +use std::{borrow::Cow, fmt}; -/// A convenience wrapper produce 256 bit hashes from Blake2b -#[derive(Clone, Debug)] -pub struct Blake256(VarBlake2b); +type NomErr<'a> = nom::Err<(&'a str, ErrorKind)>; -impl Blake256 { - pub fn new() -> Self { - let h = VarBlake2b::new(32).unwrap(); - Blake256(h) - } +#[derive(Debug, Clone)] +pub struct ParseError(pub String); - pub fn result(self) -> GenericArray { - self.fixed_result() +impl From> for ParseError { + fn from(err: NomErr<'_>) -> Self { + ParseError(err.to_string()) } } -impl Default for Blake256 { - fn default() -> Self { - let h = VarBlake2b::new(32).unwrap(); - Blake256(h) - } -} +impl std::error::Error for ParseError {} -impl Input for Blake256 { - fn input>(&mut self, data: B) { - (self.0).input(data); +impl fmt::Display for ParseError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "ParseError({})", self.0) } } -impl FixedOutput for Blake256 { - type OutputSize = U32; - - fn fixed_result(self) -> GenericArray { - let v = (self.0).vec_result(); - GenericArray::clone_from_slice(&v) +pub fn response_line(line: &str) -> Result, ParseError> { + let parser = map_res(digit1, |code: &str| code.parse::()); + let (rest, code) = parser(line)?; + let (rest, ch) = anychar(rest)?; + if ![' ', '-', '+'].contains(&ch) { + return Err(ParseError(format!( + "Unexpected response character '{}'. Expected ' ', '-' or '+'.", + ch + ))); } -} -impl Reset for Blake256 { - fn reset(&mut self) { - (self.0).reset() - } + Ok(ResponseLine { + has_more: ['-', '+'].contains(&ch), + is_multiline: ch == '+', + code, + value: rest.into(), + }) } -#[cfg(test)] -mod test { - use crate::common::Blake256; - use digest::Input; - use tari_utilities::hex; - - #[test] - fn blake256() { - let e = Blake256::new().chain(b"one").chain(b"two").result().to_vec(); - let h = hex::to_hex(&e); - assert_eq!( - h, - "03521c1777639fc6e5c3d8c3b4600870f18becc155ad7f8053d2c65bc78e4aa0".to_string() - ); - } +pub fn key_value(line: &str) -> Result<(Cow<'_, str>, Cow<'_, str>), ParseError> { + let (rest, identifier) = take_while1(|ch| ch != '=')(line)?; + let (rest, _) = chr('=')(rest)?; + let rest = rest.trim_matches('"').trim(); + Ok((identifier.into(), rest.into())) } diff --git a/comms/src/tor/client/response.rs b/comms/src/tor/client/response.rs new file mode 100644 index 0000000000..ca3d844377 --- /dev/null +++ b/comms/src/tor/client/response.rs @@ -0,0 +1,65 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::borrow::Cow; + +const OK_CODE: u16 = 250; +pub const EVENT_CODE: u16 = 650; + +/// Represents a single response line from the server. +pub struct ResponseLine<'a> { + pub(super) value: Cow<'a, str>, + pub(super) code: u16, + pub(super) has_more: bool, + pub(super) is_multiline: bool, +} + +impl<'a> ResponseLine<'a> { + pub fn is_ok(&self) -> bool { + self.code == OK_CODE + } + + pub fn has_more(&self) -> bool { + self.has_more + } + + pub fn into_owned<'b>(self) -> ResponseLine<'b> { + ResponseLine { + value: Cow::Owned(self.value.into_owned()), + code: self.code, + has_more: self.has_more, + is_multiline: self.is_multiline, + } + } + + pub fn is_err(&self) -> bool { + !self.is_ok() + } + + pub fn err(&self) -> Option> { + if self.is_err() { + Some(self.value.clone()) + } else { + None + } + } +} diff --git a/comms/src/tor/client/test_server.rs b/comms/src/tor/client/test_server.rs new file mode 100644 index 0000000000..6e9d4d01fa --- /dev/null +++ b/comms/src/tor/client/test_server.rs @@ -0,0 +1,130 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + compat::IoCompat, + memsocket::MemorySocket, + multiaddr::Multiaddr, + test_utils::transport::build_connected_sockets, +}; +use futures::{lock::Mutex, stream, SinkExt, StreamExt}; +use std::sync::Arc; +use tokio::runtime; +use tokio_util::codec::{Framed, LinesCodec}; + +pub async fn spawn() -> (Multiaddr, State, MemorySocket) { + let (addr, socket_out, socket_in) = build_connected_sockets().await; + + let server = TorControlPortTestServer::new(socket_in); + let state = server.get_shared_state(); + runtime::Handle::current().spawn(server.run()); + + (addr, state, socket_out) +} + +#[derive(Clone)] +pub struct State { + request_lines: Arc>>, + canned_response: Arc>>, +} + +impl State { + pub fn new() -> Self { + Self { + request_lines: Arc::new(Mutex::new(Vec::new())), + canned_response: Arc::new(Mutex::new(all_to_owned(canned_responses::OK))), + } + } + + pub async fn set_canned_response<'a, T: AsRef<[&'a str]>>(&self, lines: T) { + *self.canned_response.lock().await = all_to_owned(lines); + } + + pub async fn take_requests(&self) -> Vec { + self.request_lines.lock().await.drain(..).collect() + } +} + +pub struct TorControlPortTestServer { + socket: MemorySocket, + state: State, +} + +impl TorControlPortTestServer { + pub fn new(socket: MemorySocket) -> Self { + Self { + socket, + state: State::new(), + } + } + + pub fn get_shared_state(&self) -> State { + self.state.clone() + } + + pub async fn run(self) { + let mut framed = Framed::new(IoCompat::new(self.socket), LinesCodec::new()); + let state = self.state; + while let Some(msg) = framed.next().await { + state.request_lines.lock().await.push(msg.unwrap()); + let mut responses = stream::iter(state.canned_response.lock().await.clone()).map(Ok); + framed.send_all(&mut responses).await.unwrap(); + } + } +} + +fn all_to_owned<'a, T: AsRef<[&'a str]>>(strings: T) -> Vec { + strings.as_ref().into_iter().map(|s| (*s).to_owned()).collect() +} + +pub mod canned_responses { + pub const OK: &[&str] = &["250 OK"]; + pub const GET_CONF_HIDDEN_SERVICE_PORT_OK: &[&str] = &[ + "250-HiddenServicePort=8080", + "250-HiddenServicePort=8081 127.0.0.1:9000", + "250 HiddenServicePort=8082 127.0.0.1:9001", + ]; + + pub const GET_INFO_NET_LISTENERS_OK: &[&str] = &["250-net/listeners/socks=\"127.0.0.1:9050\"", "250 OK"]; + + pub const GET_INFO_ONIONS_DETACHED_OK: &[&str] = &[ + "250+onions/detached=", + "mochz2xppfziim5olr5f6q27poc4vfob2xxxxxxxxxxxxxxxxxxxxxxx", + "nhqdqym6j35rk7tdou4cdj4gjjqagimutxxxxxxxxxxxxxxxxxxxxxxx", + ".", + "250 OK", + ]; + + pub const ADD_ONION_OK: &[&str] = &[ + "250-ServiceID=qigbgbs4ue3ghbupsotgh73cmmkjrin2aprlyxsrnrvpmcmzy3g4wbid", + "250-PrivateKey=ED25519-V3:\ + Pg3GEyssauPRW3jP6mHwKOxvl_fMsF0QsZC3DvQ8jZ9AxmfRvSP35m9l0vOYyOxkOqWM6ufjdYuM8Ae6cR2UdreG6", + "250 OK", + ]; + pub const ADD_ONION_RSA1024_OK: &[&str] = &["250-ServiceID=62q4tswkxp74dtn7", "250 OK"]; + pub const ADD_ONION_DISCARDPK_OK: &[&str] = &[ + "250-ServiceID=qigbgbs4ue3ghbupsotgh73cmmkjrin2aprlyxsrnrvpmcmzy3g4wbid", + "250 OK", + ]; + + pub const ERR_552: &[&str] = &["552 Unrecognised configuration key \"dummy\""]; +} diff --git a/comms/src/tor/client/types.rs b/comms/src/tor/client/types.rs new file mode 100644 index 0000000000..2f0c52b43a --- /dev/null +++ b/comms/src/tor/client/types.rs @@ -0,0 +1,131 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use serde_derive::{Deserialize, Serialize}; +use std::{fmt, net::SocketAddr}; + +#[derive(Clone, Copy, Debug)] +pub enum KeyType { + /// The server should generate a key of algorithm KeyBlob + New, + /// The server should use the 1024-bit RSA key provided in as KeyBlob (v2). + Rsa1024, + /// The server should use the ED25519-V3 key provided in as KeyBlob (v3). + Ed25519V3, +} + +impl KeyType { + pub fn as_tor_repr(&self) -> &'static str { + match self { + KeyType::New => "NEW", + KeyType::Rsa1024 => "RSA1024", + KeyType::Ed25519V3 => "ED25519-V3", + } + } +} + +pub enum KeyBlob<'a> { + /// The server should generate a key using the "best" supported algorithm (KeyType == "NEW"). + Best, + /// The server should generate a 1024 bit RSA key (KeyType == "NEW") (v2). + Rsa1024, + /// The server should generate an ed25519 private key (KeyType == "NEW") (v3). + Ed25519V3, + /// A serialized private key (without whitespace) + String(&'a str), +} + +impl KeyBlob<'_> { + pub fn as_tor_repr(&self) -> &str { + match self { + KeyBlob::Best => "BEST", + KeyBlob::Rsa1024 => "RSA1024", + KeyBlob::Ed25519V3 => "ED25519-V3", + KeyBlob::String(priv_key) => priv_key, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum PrivateKey { + /// The server should use the 1024 bit RSA key provided in as KeyBlob (v2). + Rsa1024(String), + /// The server should use the ed25519 v3 key provided in as KeyBlob (v3). + Ed25519V3(String), +} + +impl Drop for PrivateKey { + fn drop(&mut self) { + use clear_on_drop::clear::Clear; + match self { + PrivateKey::Rsa1024(ref mut key) => { + Clear::clear(key); + }, + PrivateKey::Ed25519V3(ref mut key) => { + Clear::clear(key); + }, + } + } +} + +/// Represents a mapping between an onion port and a proxied address (usually 127.0.0.1:xxxx). +/// If the proxied_address is not specified, the default `127.0.0.1:[onion_port]` will be used. +#[derive(Debug, Clone)] +pub struct PortMapping(u16, SocketAddr); + +impl PortMapping { + /// Returns a new `PortMapping` with the proxied address set to 127.0.0.1:{onion_port} + pub fn from_port(onion_port: u16) -> Self { + Self(onion_port, ([127, 0, 0, 1], onion_port).into()) + } + + /// Returns a new `PortMapping` with the specified proxied address + pub fn new(onion_port: u16, proxied_address: SocketAddr) -> Self { + Self(onion_port, proxied_address) + } + + pub fn onion_port(&self) -> u16 { + self.0 + } + + pub fn proxied_address(&self) -> &SocketAddr { + &self.1 + } +} + +impl From for PortMapping { + fn from(onion_port: u16) -> Self { + Self::from_port(onion_port) + } +} + +impl, U: Into> From<(T, U)> for PortMapping { + fn from((port, addr): (T, U)) -> Self { + Self(port.into(), addr.into()) + } +} + +impl fmt::Display for PortMapping { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "PortMapping [{} -> {}]", self.0, self.1) + } +} diff --git a/comms/src/tor/hidden_service/builder.rs b/comms/src/tor/hidden_service/builder.rs new file mode 100644 index 0000000000..46a4d330fb --- /dev/null +++ b/comms/src/tor/hidden_service/builder.rs @@ -0,0 +1,224 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + multiaddr::Multiaddr, + socks, + tor::{ + client::commands::{AddOnionFlag, AddOnionResponse}, + Authentication, + HiddenService, + PortMapping, + TorClientError, + TorControlPortClient, + TorIdentity, + }, + utils::multiaddr::socketaddr_to_multiaddr, +}; +use bitflags::bitflags; +use derive_error::Error; +use futures::{AsyncRead, AsyncWrite}; +use log::*; +use std::net::SocketAddr; + +const LOG_TARGET: &str = "comms::tor::hidden_service"; + +#[derive(Debug, Error)] +pub enum HiddenServiceBuilderError { + /// Failed to parse SOCKS address returned by control port + FailedToParseSocksAddress, + /// The proxied port mapping was not provided. Use `with_proxied_port_mapping` to set it. + ProxiedPortMappingNotProvided, + /// The control server address was not provided. Use `with_control_server_address` to set it. + TorControlServerAddressNotProvided, + TorClientError(TorClientError), + /// The given tor service id is not a valid detached service id + InvalidDetachedServiceId, +} + +bitflags! { + /// Hidden service flags + #[derive(Default)] + pub struct HsFlags: u32 { + const NONE = 0x0; + /// Detach the service from the control server connection. This keeps the hidden service active even if comms is shutdown. + const DETACH = 0x1; + } +} + +/// Builder for Tor Hidden Services +#[derive(Default)] +pub struct HiddenServiceBuilder { + identity: Option, + port_mapping: Option, + control_server_addr: Option, + control_server_auth: Authentication, + socks_auth: socks::Authentication, + hs_flags: HsFlags, +} + +impl HiddenServiceBuilder { + pub fn new() -> Self { + Default::default() + } +} + +impl HiddenServiceBuilder { + /// The address of the Tor Control Port. An error will result if this is not provided. + setter!(with_control_server_address, control_server_addr, Option); + + /// Authentication settings for the Tor Control Port. + setter!(with_control_server_auth, control_server_auth, Authentication); + + /// Authentication to use for the SOCKS5 proxy. + setter!(with_socks_authentication, socks_auth, socks::Authentication); + + /// The identity of the hidden service. When set, this key is used to enable routing from the Tor network to + /// this address. If this is not set, a new service will be requested from the Tor Control Port. + setter!(with_tor_identity, identity, Option); + + /// Configuration flags for the hidden service + setter!(with_hs_flags, hs_flags, HsFlags); + + /// Set the PortMapping to use when creating this hidden service. A PortMapping maps a Tor port to a proxied address + /// (usually local). An error will result if this is not provided. + pub fn with_port_mapping>(mut self, port_mapping: P) -> Self { + self.port_mapping = Some(port_mapping.into()); + self + } +} + +impl HiddenServiceBuilder { + /// Create a HiddenService with the given builder parameters. + pub async fn finish(self) -> Result { + let proxied_port_mapping = self + .port_mapping + .ok_or(HiddenServiceBuilderError::ProxiedPortMappingNotProvided)?; + let control_server_addr = self + .control_server_addr + .ok_or(HiddenServiceBuilderError::TorControlServerAddressNotProvided)?; + + debug!( + target: LOG_TARGET, + "Building tor hidden service with control port '{}' and port mapping '{}'", + control_server_addr, + proxied_port_mapping + ); + + let mut client = TorControlPortClient::connect(control_server_addr).await?; + client.authenticate(&self.control_server_auth).await?; + + // Get configured SOCK5 address from Tor + let socks_addr = client + .get_info("net/listeners/socks") + .await? + .parse::() + .map(|addr| socketaddr_to_multiaddr(&addr)) + .map_err(|_| HiddenServiceBuilderError::FailedToParseSocksAddress)?; + + let proxied_addr = socketaddr_to_multiaddr(proxied_port_mapping.proxied_address()); + + // Initialize a onion hidden service - either from the given private key or by creating a new one + let onion_private_key; + let add_onion_resp = match self.identity { + Some(identity) => { + onion_private_key = identity.private_key.clone(); + Self::ensure_onion(&mut client, identity, proxied_port_mapping, self.hs_flags).await? + }, + None => { + let resp = client.add_onion(vec![], proxied_port_mapping, None).await?; + onion_private_key = resp + .private_key + .clone() + .expect("Tor server MUST return private key according to spec"); + resp + }, + }; + + debug!( + target: LOG_TARGET, + "Added hidden service with service id '{}' on port '{}'", + add_onion_resp.service_id, + add_onion_resp.onion_port + ); + + Ok(HiddenService { + socks_addr, + socks_auth: self.socks_auth, + service_id: add_onion_resp.service_id, + onion_port: add_onion_resp.onion_port, + private_key: onion_private_key, + proxied_addr, + client, + }) + } + + async fn ensure_onion( + client: &mut TorControlPortClient, + identity: TorIdentity, + port_mapping: PortMapping, + hs_flags: HsFlags, + ) -> Result + where + TSocket: AsyncRead + AsyncWrite + Unpin, + { + let mut flags = Vec::new(); + if hs_flags.contains(HsFlags::DETACH) { + flags.push(AddOnionFlag::Detach); + } + + let result = client + .add_onion_from_private_key(&identity.private_key, flags, port_mapping.clone(), None) + .await; + + match result { + Ok(resp) => Ok(resp), + Err(TorClientError::OnionAddressCollision) => { + debug!(target: LOG_TARGET, "Onion address is already registered."); + + let detached_str = client.get_info("onions/detached").await?; + debug!( + target: LOG_TARGET, + "Comparing active detached service IDs '{}' to expected service id '{}'", + detached_str.replace('\n', ", "), + identity.service_id + ); + let mut detached = detached_str.split('\n'); + + if detached.all(|svc_id| svc_id != identity.service_id) { + return Err(HiddenServiceBuilderError::InvalidDetachedServiceId); + } + + Ok(AddOnionResponse { + // TODO(sdbondi): This could be a different ORPort than the one requested in port mapping, I was not + // able to find a way to find the port mapping for the service. + // Setting the onion_port to be the same as the original port may cause + // confusion/break "just works"(tm) + onion_port: identity.onion_port, + service_id: identity.service_id, + private_key: Some(identity.private_key), + }) + }, + Err(err) => Err(err.into()), + } + } +} diff --git a/comms/src/tor/hidden_service/hidden_service.rs b/comms/src/tor/hidden_service/hidden_service.rs new file mode 100644 index 0000000000..8802d70af1 --- /dev/null +++ b/comms/src/tor/hidden_service/hidden_service.rs @@ -0,0 +1,116 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + multiaddr::Multiaddr, + socks, + tor::{PrivateKey, TorClientError, TorControlPortClient}, + transports::{SocksTransport, TcpTransport, Transport}, +}; +use serde_derive::{Deserialize, Serialize}; + +/// Handle for a Tor Hidden Service. This handle keeps the session to the Tor control port alive. +/// Once this is dropped, the hidden service will cease to be accessible. +pub struct HiddenService { + /// The client connection to the Tor Control Port. `AddOnionFlag::Detach` is not used, so we have to keep the Tor + /// Control port connection open. + pub(super) client: TorControlPortClient<::Output>, + /// The service id of the hidden service. + pub(super) service_id: String, + /// The SOCKS5 address obtained by querying the Tor control port and used to configure the `SocksTransport`. + pub(super) socks_addr: Multiaddr, + /// SOCKS5 authentication details used to configure the `SocksTransport`. + pub(super) socks_auth: socks::Authentication, + /// The Private Key for the hidden service. + pub(super) private_key: PrivateKey, + /// The onion port used. + pub(super) onion_port: u16, + /// The address where incoming traffic to the `onion_addr` will be forwarded to. + pub(super) proxied_addr: Multiaddr, +} + +impl HiddenService { + /// Delete the hidden service. Once this is called the hidden service will no longer be accessible. + pub async fn delete(&mut self) -> Result<(), TorClientError> { + self.client.del_onion(&self.service_id).await.map_err(Into::into) + } + + pub fn get_onion_address(&self) -> Multiaddr { + // service_id should always come from the tor control server, so the length can be relied on + multiaddr_from_service_id_and_port(&self.service_id, self.onion_port) + .expect("failed to create onion address from HiddenService service_id and onion_port") + } + + pub fn service_id(&self) -> &str { + &self.service_id + } + + pub fn private_key(&self) -> &PrivateKey { + &self.private_key + } + + pub fn proxied_address(&self) -> &Multiaddr { + &self.proxied_addr + } + + pub fn get_transport(&self) -> SocksTransport { + SocksTransport::new(self.socks_addr.clone(), self.socks_auth.clone()) + } + + pub fn get_tor_identity(&self) -> TorIdentity { + TorIdentity { + private_key: self.private_key.clone(), + service_id: self.service_id.clone(), + onion_port: self.onion_port, + } + } +} + +fn multiaddr_from_service_id_and_port(service_id: &str, onion_port: u16) -> Result { + const ONION_V2_LEN: usize = 16; + const ONION_V3_LEN: usize = 56; + match service_id.len() { + ONION_V2_LEN => format!("/onion/{}:{}", service_id, onion_port) + .parse() + .map_err(|_| TorClientError::InvalidServiceId), + ONION_V3_LEN => { + // This will fail until this PR is released (https://github.com/libp2p/rust-libp2p/pull/1354) + format!("/onion3/{}:{}", service_id, onion_port) + .parse() + .map_err(|_| TorClientError::InvalidServiceId) + }, + _ => Err(TorClientError::InvalidServiceId), + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct TorIdentity { + pub private_key: PrivateKey, + pub service_id: String, + pub onion_port: u16, +} + +impl TorIdentity { + pub fn try_get_onion_address(&self) -> Result { + multiaddr_from_service_id_and_port(&self.service_id, self.onion_port) + } +} diff --git a/comms/src/tor/hidden_service/mod.rs b/comms/src/tor/hidden_service/mod.rs new file mode 100644 index 0000000000..ed9a102aea --- /dev/null +++ b/comms/src/tor/hidden_service/mod.rs @@ -0,0 +1,27 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod builder; +mod hidden_service; + +pub use builder::{HiddenServiceBuilder, HiddenServiceBuilderError, HsFlags}; +pub use hidden_service::{HiddenService, TorIdentity}; diff --git a/comms/src/tor/mod.rs b/comms/src/tor/mod.rs new file mode 100644 index 0000000000..d0267cabad --- /dev/null +++ b/comms/src/tor/mod.rs @@ -0,0 +1,37 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +//! # Tor Hidden Services and Control Port Client +//! +//! These modules interact with the Tor Control Port to create hidden services on-the-fly. +//! +//! The [client](crate::tor::client) module contains the client library for the Tor Control Port. You can find the spec +//! [here](https://gitweb.torproject.org/torspec.git/tree/control-spec.txt). +//! +//! The [hidden_service](crate::tor::hidden_service) module contains code which sets up hidden services required for +//! `tari_comms` to function over Tor. + +mod client; +pub use client::{Authentication, KeyBlob, KeyType, PortMapping, PrivateKey, TorClientError, TorControlPortClient}; + +mod hidden_service; +pub use hidden_service::{HiddenService, HiddenServiceBuilder, HiddenServiceBuilderError, HsFlags, TorIdentity}; diff --git a/comms/src/transports/memory.rs b/comms/src/transports/memory.rs new file mode 100644 index 0000000000..4a297329fa --- /dev/null +++ b/comms/src/transports/memory.rs @@ -0,0 +1,186 @@ +// Copyright 2020, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Copyright (c) The Libra Core Contributors +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + memsocket, + memsocket::{MemoryListener, MemorySocket}, + transports::Transport, +}; +use futures::{future, stream::Stream, Future}; +use multiaddr::{Multiaddr, Protocol}; +use std::{ + io, + num::NonZeroU16, + pin::Pin, + task::{Context, Poll}, +}; + +/// Transport to build in-memory connections +#[derive(Debug, Default, Clone)] +pub struct MemoryTransport; + +impl MemoryTransport { + /// Acquire a free memory socket port. This port will not be used when using `/memory/0` or by subsequent calls to + /// `acquire_next_memsocket_port`. + pub fn acquire_next_memsocket_port() -> NonZeroU16 { + memsocket::acquire_next_memsocket_port() + } + + /// Release a memory socket port. This port could be used when using `/memory/0` or when calling to + /// `acquire_next_memsocket_port`. + pub fn release_next_memsocket_port(port: NonZeroU16) { + memsocket::release_memsocket_port(port); + } +} + +impl Transport for MemoryTransport { + type Error = io::Error; + type Inbound = future::Ready>; + type Listener = Listener; + type Output = MemorySocket; + + type DialFuture = impl Future>; + type ListenFuture = impl Future>; + + fn listen(&self, addr: Multiaddr) -> Result { + // parse_addr is not used in the async block because of a rust ICE (internal compiler error) + let port = parse_addr(&addr)?; + let listener = MemoryListener::bind(port)?; + let actual_port = listener.local_addr(); + let mut actual_addr = Multiaddr::empty(); + actual_addr.push(Protocol::Memory(u64::from(actual_port))); + Ok(future::ready(Ok((Listener { inner: listener }, actual_addr)))) + } + + fn dial(&self, addr: Multiaddr) -> Result { + // parse_addr is not used in the async block because of a rust ICE (internal compiler error) + let port = parse_addr(&addr)?; + Ok(future::ready(Ok(MemorySocket::connect(port)?))) + } +} + +fn parse_addr(addr: &Multiaddr) -> io::Result { + let mut iter = addr.iter(); + + let port = if let Some(Protocol::Memory(port)) = iter.next() { + port + } else { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!("Invalid Multiaddr '{:?}'", addr), + )); + }; + + if iter.next().is_some() { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!("Invalid Multiaddr '{:?}'", addr), + )); + } + + Ok(port as u16) +} + +#[must_use = "streams do nothing unless polled"] +#[derive(Debug)] +pub struct Listener { + inner: MemoryListener, +} + +impl Stream for Listener { + type Item = io::Result<(future::Ready>, Multiaddr)>; + + fn poll_next(mut self: Pin<&mut Self>, context: &mut Context) -> Poll> { + let mut incoming = self.inner.incoming(); + match Pin::new(&mut incoming).poll_next(context) { + Poll::Ready(Some(Ok(socket))) => { + // Dialer addresses for MemoryTransport don't make a ton of sense, + // so use port 0 to ensure they aren't used as an address to dial. + let dialer_addr = Protocol::Memory(0).into(); + Poll::Ready(Some(Ok((future::ready(Ok(socket)), dialer_addr)))) + }, + Poll::Ready(Some(Err(e))) => Poll::Ready(Some(Err(e))), + Poll::Ready(None) => Poll::Ready(None), + Poll::Pending => Poll::Pending, + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use futures::{ + future::join, + io::{AsyncReadExt, AsyncWriteExt}, + stream::StreamExt, + }; + + #[tokio_macros::test] + async fn simple_listen_and_dial() -> Result<(), ::std::io::Error> { + let t = MemoryTransport::default(); + + let (listener, addr) = t.listen("/memory/0".parse().unwrap())?.await?; + + let listener = async move { + let (item, _listener) = listener.into_future().await; + let (inbound, _addr) = item.unwrap().unwrap(); + let mut socket = inbound.await.unwrap(); + + let mut buf = Vec::new(); + socket.read_to_end(&mut buf).await.unwrap(); + assert_eq!(buf, b"hello world"); + }; + + let mut outbound = t.dial(addr)?.await?; + + let dialer = async move { + outbound.write_all(b"hello world").await.unwrap(); + outbound.flush().await.unwrap(); + }; + + join(dialer, listener).await; + Ok(()) + } + + #[test] + fn unsupported_multiaddrs() { + let t = MemoryTransport::default(); + + let result = t.listen("/ip4/127.0.0.1/tcp/0".parse().unwrap()); + assert!(result.is_err()); + + let result = t.dial("/ip4/127.0.0.1/tcp/22".parse().unwrap()); + assert!(result.is_err()); + } + + #[test] + fn acquire_release_memsocket_port() { + let port1 = MemoryTransport::acquire_next_memsocket_port(); + let port2 = MemoryTransport::acquire_next_memsocket_port(); + assert_ne!(port1, port2); + MemoryTransport::release_next_memsocket_port(port1); + MemoryTransport::release_next_memsocket_port(port2); + } +} diff --git a/comms/src/transports/mod.rs b/comms/src/transports/mod.rs new file mode 100644 index 0000000000..3398bb6d27 --- /dev/null +++ b/comms/src/transports/mod.rs @@ -0,0 +1,58 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Much of this module is inspired or (more or less) verbatim from the Libra codebase. +// Copyright (c) The Libra Core Contributors +// SPDX-License-Identifier: Apache-2.0 + +use futures::{Future, Stream}; +use multiaddr::Multiaddr; + +mod memory; +mod socks; +mod tcp; + +pub use memory::MemoryTransport; +pub use socks::SocksTransport; +pub use tcp::{TcpSocket, TcpTransport}; + +pub trait Transport { + /// The output of the transport after a connection is established + type Output; + /// Transport error type + type Error: std::error::Error + Send + Sync; + /// A future which resolves to `Self::Output` + type Inbound: Future> + Send; + /// A stream which emits `Self::InboundFuture` whenever a successful inbound connection is made + type Listener: Stream> + Send + Unpin; + + /// The future returned from the `listen` method. + type ListenFuture: Future> + Send + Unpin; + /// The future returned from the `dial` method. + type DialFuture: Future> + Send + Unpin; + + /// Listen for connections on the given multiaddr + fn listen(&self, addr: Multiaddr) -> Result; + + /// Connect (dial) to the given multiaddr + fn dial(&self, addr: Multiaddr) -> Result; +} diff --git a/comms/src/transports/socks.rs b/comms/src/transports/socks.rs new file mode 100644 index 0000000000..71696eb123 --- /dev/null +++ b/comms/src/transports/socks.rs @@ -0,0 +1,115 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + multiaddr::Multiaddr, + socks, + socks::Socks5Client, + transports::{tcp::TcpTransport, TcpSocket, Transport}, +}; +use futures::{Future, FutureExt}; +use std::{io, time::Duration}; + +/// SO_KEEPALIVE setting for the SOCKS TCP connection +const SOCKS_SO_KEEPALIVE: Duration = Duration::from_millis(1500); + +#[derive(Clone, Debug)] +struct SocksConfig { + proxy_address: Multiaddr, + authentication: socks::Authentication, +} + +#[derive(Clone, Debug)] +pub struct SocksTransport { + socks_config: SocksConfig, + tcp_transport: TcpTransport, +} + +impl SocksTransport { + pub fn new(proxy_address: Multiaddr, authentication: socks::Authentication) -> Self { + let mut tcp_transport = TcpTransport::new(); + tcp_transport.set_nodelay(true); + tcp_transport.set_keepalive(Some(SOCKS_SO_KEEPALIVE)); + + Self { + socks_config: SocksConfig { + proxy_address, + authentication, + }, + tcp_transport, + } + } + + async fn socks_connect( + tcp: TcpTransport, + socks_config: SocksConfig, + dest_addr: Multiaddr, + ) -> io::Result + { + // Create a new connection to the SOCKS proxy + let socks_conn = tcp.dial(socks_config.proxy_address)?.await?; + let mut client = Socks5Client::new(socks_conn); + + client + .with_authentication(socks_config.authentication) + .map_err(|err| io::Error::new(io::ErrorKind::Other, err))?; + + client + .connect(&dest_addr) + .await + .map(|(socket, _)| socket) + .map_err(|err| io::Error::new(io::ErrorKind::Other, err)) + } +} + +impl Transport for SocksTransport { + type Error = ::Error; + type Inbound = ::Inbound; + type ListenFuture = ::ListenFuture; + type Listener = ::Listener; + type Output = ::Output; + + type DialFuture = impl Future> + Unpin; + + fn listen(&self, addr: Multiaddr) -> Result { + self.tcp_transport.listen(addr) + } + + fn dial(&self, addr: Multiaddr) -> Result { + Ok(Self::socks_connect(self.tcp_transport.clone(), self.socks_config.clone(), addr).boxed()) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::socks::Authentication; + + #[test] + fn new() { + let proxy_address = "/ip4/127.0.0.1/tcp/1234".parse::().unwrap(); + let transport = SocksTransport::new(proxy_address.clone(), Default::default()); + + assert_eq!(transport.socks_config.proxy_address, proxy_address); + assert_eq!(transport.socks_config.authentication, Authentication::None); + } +} diff --git a/comms/src/transports/tcp.rs b/comms/src/transports/tcp.rs new file mode 100644 index 0000000000..b70200b852 --- /dev/null +++ b/comms/src/transports/tcp.rs @@ -0,0 +1,231 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use super::Transport; +use crate::utils::multiaddr::{multiaddr_to_socketaddr, socketaddr_to_multiaddr}; +use futures::{future, io::Error, ready, AsyncRead, AsyncWrite, Future, Stream}; +use multiaddr::Multiaddr; +use std::{ + io, + pin::Pin, + task::{Context, Poll}, + time::Duration, +}; +use tokio::{ + io::{AsyncRead as TokioAsyncRead, AsyncWrite as TokioAsyncWrite}, + net::{TcpListener, TcpStream}, +}; + +/// Transport implementation for TCP +#[derive(Debug, Clone, Default)] +pub struct TcpTransport { + recv_buffer_size: Option, + send_buffer_size: Option, + ttl: Option, + keepalive: Option>, + nodelay: Option, +} + +impl TcpTransport { + /// Sets `SO_RCVBUF` i.e the size of the receive buffer. + setter_mut!(set_recv_buffer_size, recv_buffer_size, Option); + + /// Sets `SO_SNDBUF` i.e. the size of the send buffer. + setter_mut!(set_send_buffer_size, send_buffer_size, Option); + + /// Sets `IP_TTL` i.e. the TTL of packets sent from this socket. + setter_mut!(set_ttl, ttl, Option); + + /// Sets `SO_KEEPALIVE` i.e. the interval to send keepalive probes, or None to disable. + setter_mut!(set_keepalive, keepalive, Option>); + + /// Sets `TCP_NODELAY` i.e enable/disable Nagle's algorithm. + setter_mut!(set_nodelay, nodelay, Option); + + /// Create a new TcpTransport + pub fn new() -> Self { + Default::default() + } + + /// Apply socket options to `TcpStream`. + fn configure(&self, socket: &TcpStream) -> io::Result<()> { + if let Some(keepalive) = self.keepalive { + socket.set_keepalive(keepalive)?; + } + + if let Some(ttl) = self.ttl { + socket.set_ttl(ttl)?; + } + + if let Some(nodelay) = self.nodelay { + socket.set_nodelay(nodelay)?; + } + + if let Some(recv_buffer_size) = self.recv_buffer_size { + socket.set_recv_buffer_size(recv_buffer_size)?; + } + + if let Some(send_buffer_size) = self.send_buffer_size { + socket.set_send_buffer_size(send_buffer_size)?; + } + + Ok(()) + } +} + +impl Transport for TcpTransport { + type Error = io::Error; + type Inbound = future::Ready>; + type Listener = TcpInbound; + type Output = TcpSocket; + + type DialFuture = impl Future>; + type ListenFuture = impl Future>; + + fn listen(&self, addr: Multiaddr) -> Result { + let config = self.clone(); + // multiaddr_to_socketaddr is not used in the async block because of a rust ICE (internal compiler error) + let socket_addr = multiaddr_to_socketaddr(&addr)?; + + Ok(Box::pin(async move { + let listener = TcpListener::bind(&socket_addr).await?; + let local_addr = socketaddr_to_multiaddr(&listener.local_addr()?); + Ok((TcpInbound::new(config, listener), local_addr)) + })) + } + + fn dial(&self, addr: Multiaddr) -> Result { + let socket_addr = multiaddr_to_socketaddr(&addr)?; + Ok(TcpOutbound::new( + Box::pin(TcpStream::connect(socket_addr)), + self.clone(), + )) + } +} + +pub struct TcpOutbound { + future: F, + config: TcpTransport, +} + +impl TcpOutbound { + pub fn new(future: F, config: TcpTransport) -> Self { + Self { config, future } + } +} + +impl Future for TcpOutbound +where F: Future> + Unpin +{ + type Output = io::Result; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let socket = ready!(Pin::new(&mut self.future).poll(cx))?; + self.config.configure(&socket)?; + Poll::Ready(Ok(TcpSocket::new(socket))) + } +} + +/// Wrapper around an Inbound stream. This ensures that any connecting `TcpStream` is configured according to the +/// transport +pub struct TcpInbound { + listener: TcpListener, + config: TcpTransport, +} + +impl TcpInbound { + pub fn new(config: TcpTransport, listener: TcpListener) -> Self { + Self { listener, config } + } +} + +impl Stream for TcpInbound { + type Item = io::Result<(future::Ready>, Multiaddr)>; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let (socket, addr) = ready!(self.listener.poll_accept(cx))?; + // Configure each socket + self.config.configure(&socket)?; + let peer_addr = socketaddr_to_multiaddr(&addr); + let fut = future::ready(Ok(TcpSocket::new(socket))); + Poll::Ready(Some(Ok((fut, peer_addr)))) + } +} + +/// TcpSocket is a wrapper struct for tokio `TcpStream` and implements +/// `futures-rs` AsyncRead/Write +pub struct TcpSocket { + inner: TcpStream, +} + +impl TcpSocket { + pub fn new(stream: TcpStream) -> Self { + Self { inner: stream } + } +} + +impl AsyncWrite for TcpSocket { + fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll> { + Pin::new(&mut self.inner).poll_write(cx, buf) + } + + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.inner).poll_flush(cx) + } + + fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.inner).poll_shutdown(cx) + } +} + +impl AsyncRead for TcpSocket { + fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll> { + Pin::new(&mut self.inner).poll_read(cx, buf) + } +} + +impl From for TcpSocket { + fn from(stream: TcpStream) -> Self { + Self { inner: stream } + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn configure() { + let mut tcp = TcpTransport::new(); + tcp.set_send_buffer_size(123) + .set_recv_buffer_size(456) + .set_nodelay(true) + .set_ttl(789) + .set_keepalive(Some(Duration::from_millis(100))); + + assert_eq!(tcp.send_buffer_size, Some(123)); + assert_eq!(tcp.recv_buffer_size, Some(456)); + assert_eq!(tcp.nodelay, Some(true)); + assert_eq!(tcp.ttl, Some(789)); + assert_eq!(tcp.keepalive, Some(Some(Duration::from_millis(100)))); + } +} diff --git a/comms/src/types.rs b/comms/src/types.rs index ceaea66169..ec062d78d4 100644 --- a/comms/src/types.rs +++ b/comms/src/types.rs @@ -20,27 +20,17 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{ - dispatcher::{DispatchError, Dispatcher}, - inbound_message_service::comms_msg_handlers::{CommsDispatchType, InboundMessageServiceResolver}, - peer_manager::{peer_key::PeerKey, Peer}, -}; +use crate::peer_manager::{Peer, PeerId}; use tari_crypto::{common::Blake256, keys::PublicKey, ristretto::RistrettoPublicKey}; use tari_storage::lmdb_store::LMDBStore; #[cfg(test)] -use tari_storage::HMapDatabase; +use tari_storage::HashmapDatabase; #[cfg(not(test))] use tari_storage::LMDBWrapper; -use tari_utilities::ciphers::chacha20::ChaCha20; - -/// The message protocol version for the MessageEnvelopeHeader -pub const MESSAGE_PROTOCOL_VERSION: u8 = 0; - -/// The wire protocol version for the MessageEnvelope wire format -pub const WIRE_PROTOCOL_VERSION: u8 = 0; /// The default port that control services listen on -pub const DEFAULT_LISTENER_ADDRESS: &str = "0.0.0.0:7899"; +pub const DEFAULT_CONTROL_PORT_ADDRESS: &str = "/ip4/0.0.0.0/tcp/7899"; +pub const DEFAULT_LISTENER_ADDRESS: &str = "/ip4/0.0.0.0/tcp/7898"; /// Specify the digest type for the signature challenges pub type Challenge = Blake256; @@ -50,18 +40,14 @@ pub type CommsPublicKey = RistrettoPublicKey; pub type CommsSecretKey = ::K; /// Specify the RNG that should be used for random selection -pub type CommsRng = rand::OsRng; - -/// Specify what cipher to use for encryption/decryption -pub type CommsCipher = ChaCha20; +pub type CommsRng = rand::rngs::OsRng; /// Datastore and Database used for persistence storage pub type CommsDataStore = LMDBStore; #[cfg(not(test))] -pub type CommsDatabase = LMDBWrapper; +pub type CommsDatabase = LMDBWrapper; #[cfg(test)] -pub type CommsDatabase = HMapDatabase; +pub type CommsDatabase = HashmapDatabase; -/// Dispatcher format for comms level dispatching to handlers -pub type MessageDispatcher = Dispatcher; +pub type CommsSubstream = yamux::Stream; diff --git a/comms/src/utils.rs b/comms/src/utils.rs deleted file mode 100644 index 98f3596cae..0000000000 --- a/comms/src/utils.rs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -pub mod crypto { - use crate::{ - message::MessageError, - types::{Challenge, CommsPublicKey}, - }; - use digest::Digest; - use rand::{CryptoRng, Rng}; - use tari_crypto::{ - keys::{PublicKey, SecretKey}, - signatures::{SchnorrSignature, SchnorrSignatureError}, - }; - use tari_utilities::message_format::MessageFormat; - - pub fn sign( - rng: &mut R, - secret_key: ::K, - body: B, - ) -> Result::K>, SchnorrSignatureError> - where - R: CryptoRng + Rng, - B: AsRef<[u8]>, - { - let challenge = Challenge::new().chain(body).result().to_vec(); - let nonce = ::K::random(rng); - SchnorrSignature::sign(secret_key, nonce, &challenge) - } - - /// Verify that the signature is valid for the message body - pub fn verify(public_key: &CommsPublicKey, signature: &[u8], body: B) -> Result - where B: AsRef<[u8]> { - let signature = SchnorrSignature::::K>::from_binary(signature) - .map_err(MessageError::MessageFormatError)?; - let challenge = Challenge::new().chain(body).result().to_vec(); - Ok(signature.verify_challenge(public_key, &challenge)) - } -} diff --git a/base_layer/core/tests/support/mod.rs b/comms/src/utils/mod.rs similarity index 95% rename from base_layer/core/tests/support/mod.rs rename to comms/src/utils/mod.rs index 1aed6de008..9fff94e1e2 100644 --- a/base_layer/core/tests/support/mod.rs +++ b/comms/src/utils/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2019 The Tari Project +// Copyright 2019, The Tari Project // // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the // following conditions are met: @@ -20,4 +20,5 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -pub mod simple_block_chain; +pub mod multiaddr; +pub mod signature; diff --git a/comms/src/utils/multiaddr.rs b/comms/src/utils/multiaddr.rs new file mode 100644 index 0000000000..2d6ef1fc39 --- /dev/null +++ b/comms/src/utils/multiaddr.rs @@ -0,0 +1,105 @@ +// Copyright 2019, The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::multiaddr::{Multiaddr, Protocol}; +use std::{ + io, + net::{IpAddr, SocketAddr}, +}; + +/// Convert a multiaddr to a socket address required for `TcpStream` +pub fn multiaddr_to_socketaddr(addr: &Multiaddr) -> io::Result { + let mut addr_iter = addr.iter(); + let network_proto = addr_iter + .next() + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, format!("Invalid address '{}'", addr)))?; + let transport_proto = addr_iter + .next() + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, format!("Invalid address '{}'", addr)))?; + + if addr_iter.next().is_some() { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!("Invalid address '{}'", addr), + )); + } + + match (network_proto, transport_proto) { + (Protocol::Ip4(host), Protocol::Tcp(port)) => Ok((host, port).into()), + (Protocol::Ip6(host), Protocol::Tcp(port)) => Ok((host, port).into()), + _ => Err(io::Error::new( + io::ErrorKind::InvalidInput, + format!("Invalid address '{}'", addr), + )), + } +} + +/// Convert a socket address to a multiaddress. Assumes the protocol is Tcp +pub fn socketaddr_to_multiaddr(socket_addr: &SocketAddr) -> Multiaddr { + let mut addr: Multiaddr = match socket_addr.ip() { + IpAddr::V4(addr) => Protocol::Ip4(addr).into(), + IpAddr::V6(addr) => Protocol::Ip6(addr).into(), + }; + addr.push(Protocol::Tcp(socket_addr.port())); + addr +} + +#[cfg(test)] +mod test { + use super::*; + use multiaddr::multiaddr; + use std::{net::Ipv4Addr, str::FromStr}; + + #[test] + fn multiaddr_to_socketaddr_ok() { + fn expect_success(addr: &str, expected_ip: &str) { + let addr = Multiaddr::from_str(addr).unwrap(); + let sock_addr = super::multiaddr_to_socketaddr(&addr).unwrap(); + assert_eq!(sock_addr.ip().to_string(), expected_ip); + } + + expect_success("/ip4/254.0.1.2/tcp/1234", "254.0.1.2"); + expect_success("/ip6/::1/tcp/1234", "::1"); + } + + #[test] + fn multiaddr_to_socketaddr_err() { + fn expect_fail(addr: &str) { + let addr = Multiaddr::from_str(addr).unwrap(); + let err = super::multiaddr_to_socketaddr(&addr).unwrap_err(); + assert_eq!(err.kind(), io::ErrorKind::InvalidInput); + } + + expect_fail("/ip4/254.0.1.2/tcp/1234/quic"); + expect_fail("/ip4/254.0.1.2"); + expect_fail("/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC"); + } + + #[test] + fn multiaddr_from_components() { + let ip: Ipv4Addr = "127.0.0.1".parse().unwrap(); + let addr = multiaddr!(Ip4(ip.clone()), Tcp(1456u16)); + let mut addr_iter = addr.iter(); + assert_eq!(addr_iter.next(), Some(Protocol::Ip4(ip))); + assert_eq!(addr_iter.next(), Some(Protocol::Tcp(1456))); + assert_eq!(addr_iter.next(), None); + } +} diff --git a/base_layer/core/src/transaction_protocol/test_common.rs b/comms/src/utils/signature.rs similarity index 56% rename from base_layer/core/src/transaction_protocol/test_common.rs rename to comms/src/utils/signature.rs index 27c30dc258..683e9e3c19 100644 --- a/base_layer/core/src/transaction_protocol/test_common.rs +++ b/comms/src/utils/signature.rs @@ -1,4 +1,4 @@ -// Copyright 2019. The Tari Project +// Copyright 2019, The Tari Project // // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the // following conditions are met: @@ -20,44 +20,37 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// Used in tests only - use crate::{ - tari_amount::*, - transaction::{OutputFeatures, TransactionInput, UnblindedOutput}, - types::{PrivateKey, PublicKey, COMMITMENT_FACTORY}, + message::MessageError, + types::{Challenge, CommsPublicKey}, }; +use digest::Digest; use rand::{CryptoRng, Rng}; use tari_crypto::{ - commitment::HomomorphicCommitmentFactory, - keys::{PublicKey as PK, SecretKey}, + keys::{PublicKey, SecretKey}, + signatures::{SchnorrSignature, SchnorrSignatureError}, + tari_utilities::message_format::MessageFormat, }; -pub struct TestParams { - pub spend_key: PrivateKey, - pub change_key: PrivateKey, - pub offset: PrivateKey, - pub nonce: PrivateKey, - pub public_nonce: PublicKey, -} - -impl TestParams { - pub fn new(rng: &mut R) -> TestParams { - let r = PrivateKey::random(rng); - TestParams { - spend_key: PrivateKey::random(rng), - change_key: PrivateKey::random(rng), - offset: PrivateKey::random(rng), - public_nonce: PublicKey::from_secret_key(&r), - nonce: r, - } - } +pub fn sign( + rng: &mut R, + secret_key: ::K, + body: B, +) -> Result::K>, SchnorrSignatureError> +where + R: CryptoRng + Rng, + B: AsRef<[u8]>, +{ + let challenge = Challenge::new().chain(body).result().to_vec(); + let nonce = ::K::random(rng); + SchnorrSignature::sign(secret_key, nonce, &challenge) } -pub fn make_input(rng: &mut R, val: MicroTari) -> (TransactionInput, UnblindedOutput) { - let key = PrivateKey::random(rng); - let v = PrivateKey::from(val); - let commitment = COMMITMENT_FACTORY.commit(&key, &v); - let input = TransactionInput::new(OutputFeatures::default(), commitment); - (input, UnblindedOutput::new(val, key, None)) +/// Verify that the signature is valid for the message body +pub fn verify(public_key: &CommsPublicKey, signature: &[u8], body: B) -> Result +where B: AsRef<[u8]> { + let signature = SchnorrSignature::::K>::from_binary(signature) + .map_err(MessageError::MessageFormatError)?; + let challenge = Challenge::new().chain(body).result().to_vec(); + Ok(signature.verify_challenge(public_key, &challenge)) } diff --git a/comms/tests/connection/connection.rs b/comms/tests/connection/connection.rs deleted file mode 100644 index a38fd2c2d9..0000000000 --- a/comms/tests/connection/connection.rs +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::support; -use std::time::Duration; -use tari_comms::connection::{ - connection::Connection, - error::ConnectionError, - types::{Direction, Linger}, - CurveEncryption, - InprocAddress, - NetAddress, - ZmqContext, -}; - -use crate::support::factories::{self, TestFactory}; - -#[test] -fn inbound_receive_timeout() { - let ctx = ZmqContext::new(); - - let addr = InprocAddress::random(); - - let conn = Connection::new(&ctx, Direction::Inbound) - .set_linger(Linger::Indefinitely) - .establish(&addr) - .unwrap(); - - let result = conn.receive(1); - assert!(result.is_err()); - let err = result.unwrap_err(); - match err { - ConnectionError::Timeout => {}, - _ => panic!("Unexpected error type: {:?}", err), - } -} - -#[test] -fn inbound_recv_send_inproc() { - let ctx = ZmqContext::new(); - - let addr = InprocAddress::random(); - - let req_rep_pattern = support::comms_patterns::async_request_reply(Direction::Outbound); - - let conn = Connection::new(&ctx, Direction::Inbound) - .set_linger(Linger::Indefinitely) - .establish(&addr) - .unwrap(); - - let signal = req_rep_pattern - .set_endpoint(addr.clone()) - .set_identity("boba") - .set_send_data(vec![ - "Just".as_bytes().to_vec(), - "Three".as_bytes().to_vec(), - "Messages".as_bytes().to_vec(), - ]) - .run(ctx.clone()); - - let frames = conn.receive(2000).unwrap(); - assert_eq!(frames.len(), 4); - assert_eq!("boba".as_bytes(), frames[0].as_slice()); - assert_eq!("Just".as_bytes(), frames[1].as_slice()); - assert_eq!("Three".as_bytes(), frames[2].as_slice()); - assert_eq!("Messages".as_bytes(), frames[3].as_slice()); - - conn.send(&["boba", "OK"]).unwrap(); - - // Wait for pattern to exit - signal.recv_timeout(Duration::from_millis(10)).unwrap(); -} - -#[test] -fn inbound_recv_send_encrypted_tcp() { - let ctx = ZmqContext::new(); - - let addr = factories::net_address::create().use_os_port().build().unwrap(); - - let req_rep_pattern = support::comms_patterns::async_request_reply(Direction::Outbound); - - let (sk, pk) = CurveEncryption::generate_keypair().unwrap(); - - let conn = Connection::new(&ctx, Direction::Inbound) - .set_linger(Linger::Indefinitely) - .set_curve_encryption(CurveEncryption::Server { secret_key: sk }) - .establish(&addr) - .unwrap(); - - let addr = NetAddress::from(conn.get_connected_address().clone().unwrap()); - - let signal = req_rep_pattern - .set_endpoint(addr.clone()) - .set_identity("the dude") - .set_server_public_key(pk) - .set_send_data(vec![(0..255).map(|i| i as u8).collect::>()]) - .run(ctx.clone()); - - let frames = conn.receive(2000).unwrap(); - assert_eq!(frames.len(), 2); - - conn.send(&["the dude", "OK"]).unwrap(); - - // Wait for pattern to exit - signal.recv_timeout(Duration::from_millis(10)).unwrap(); -} - -#[test] -fn outbound_send_recv_inproc() { - let ctx = ZmqContext::new(); - - let addr = InprocAddress::random(); - - let req_rep_pattern = support::comms_patterns::async_request_reply(Direction::Inbound); - - let signal = req_rep_pattern - .set_endpoint(addr.clone()) - .set_send_data(vec!["OK".as_bytes().to_vec()]) - .run(ctx.clone()); - - let conn = Connection::new(&ctx, Direction::Outbound) - .set_linger(Linger::Indefinitely) - .set_identity("identity") - .establish(&addr) - .unwrap(); - - conn.send(&["identity"]).unwrap(); - - let frames = conn.receive(2000).unwrap(); - - assert_eq!(1, frames.len()); - assert_eq!("OK", String::from_utf8_lossy(frames[0].as_slice())); - - // Wait for pattern to exit - signal.recv_timeout(Duration::from_millis(10)).unwrap(); -} - -#[test] -fn outbound_send_recv_encrypted_tcp() { - let ctx = ZmqContext::new(); - - let addr = factories::net_address::create().build().unwrap(); - - let req_rep_pattern = support::comms_patterns::async_request_reply(Direction::Inbound); - - let (sk, spk) = CurveEncryption::generate_keypair().unwrap(); - let (csk, cpk) = CurveEncryption::generate_keypair().unwrap(); - - let signal = req_rep_pattern - .set_endpoint(addr.clone()) - .set_secret_key(sk) - .set_send_data(vec!["OK".as_bytes().to_vec()]) - .run(ctx.clone()); - - let conn = Connection::new(&ctx, Direction::Outbound) - .set_linger(Linger::Indefinitely) - .set_curve_encryption(CurveEncryption::Client { - secret_key: csk, - public_key: cpk, - server_public_key: spk, - }) - .set_identity("identity") - .establish(&addr) - .unwrap(); - - conn.send(&["identity"]).unwrap(); - - let frames = conn.receive(2000).unwrap(); - - assert_eq!(1, frames.len()); - assert_eq!("OK", String::from_utf8_lossy(frames[0].as_slice())); - - // Wait for pattern to exit - signal.recv_timeout(Duration::from_millis(10)).unwrap(); -} diff --git a/comms/tests/connection/mod.rs b/comms/tests/connection/mod.rs deleted file mode 100644 index d8c7109e70..0000000000 --- a/comms/tests/connection/mod.rs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -mod connection; -mod monitor; -mod peer_connection; diff --git a/comms/tests/connection/monitor.rs b/comms/tests/connection/monitor.rs deleted file mode 100644 index 727d6f02e0..0000000000 --- a/comms/tests/connection/monitor.rs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use std::{thread, time::Duration}; -use tari_comms::connection::{ - connection::Connection, - monitor::{ConnectionMonitor, SocketEventType}, - types::Direction, - zmq::{ZmqContext, ZmqEndpoint}, - InprocAddress, - NetAddress, -}; - -use crate::support::factories::{self, TestFactory}; - -#[test] -fn recv_socket_events() { - let ctx = ZmqContext::new(); - let monitor_addr = InprocAddress::random(); - let address = factories::net_address::create().use_os_port().build().unwrap(); - - let monitor = ConnectionMonitor::connect(&ctx, &monitor_addr).unwrap(); - - let conn_in = Connection::new(&ctx, Direction::Inbound) - .set_monitor_addr(monitor_addr.clone()) - .establish(&address) - .unwrap(); - let connected_address = NetAddress::from(conn_in.get_connected_address().clone().unwrap()); - - { - // Connect and disconnect - let conn_out = Connection::new(&ctx, Direction::Outbound) - .establish(&connected_address) - .unwrap(); - conn_out.send(&["test".as_bytes()]).unwrap(); - - let _ = conn_in.receive(1000).unwrap(); - } - - thread::sleep(Duration::from_millis(10)); - // Collect events - let mut events = vec![]; - while let Ok(event) = monitor.read(10) { - events.push(event); - } - - let event = events.iter().find(|e| e.event_type == SocketEventType::Listening); - assert!(event.is_some(), "Expected to find event Listening"); - let event = event.unwrap(); - assert_eq!(event.address, connected_address.to_zmq_endpoint()); - - let event = events.iter().find(|e| e.event_type == SocketEventType::Accepted); - assert!(event.is_some(), "Expected to find event Accepted"); - let event = event.unwrap(); - assert_eq!(event.address, connected_address.to_zmq_endpoint()); - - let event = events.iter().find(|e| e.event_type == SocketEventType::Disconnected); - assert!(event.is_some(), "Expected to find event Disconnected"); - let event = event.unwrap(); - assert_eq!(event.address, connected_address.to_zmq_endpoint()); -} diff --git a/comms/tests/connection/peer_connection.rs b/comms/tests/connection/peer_connection.rs deleted file mode 100644 index ad195f3039..0000000000 --- a/comms/tests/connection/peer_connection.rs +++ /dev/null @@ -1,463 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::support::{ - factories::{self, TestFactory}, - helpers::asserts::assert_change, -}; -use std::time::Duration; -use tari_comms::connection::{ - peer_connection::PeerConnectionProtoMessage, - types::{Direction, Linger}, - Connection, - ConnectionError, - CurveEncryption, - InprocAddress, - NetAddress, - PeerConnection, - PeerConnectionContextBuilder, - PeerConnectionError, - ZmqContext, -}; - -#[test] -fn connection_in() { - let addr = factories::net_address::create().build().unwrap(); - let ctx = ZmqContext::new(); - - let (server_sk, server_pk) = CurveEncryption::generate_keypair().unwrap(); - let (client_sk, client_pk) = CurveEncryption::generate_keypair().unwrap(); - - let consumer_addr = InprocAddress::random(); - - // Initialize and start peer connection - let context = PeerConnectionContextBuilder::new() - .set_id("123") - .set_direction(Direction::Inbound) - .set_context(&ctx) - .set_message_sink_address(consumer_addr.clone()) - .set_curve_encryption(CurveEncryption::Server { secret_key: server_sk }) - .set_address(addr.clone()) - .build() - .unwrap(); - - let mut conn = PeerConnection::new(); - conn.start(context).unwrap(); - conn.wait_listening_or_failure(&Duration::from_millis(1000)).unwrap(); - - // Connect the message consumer - let consumer = Connection::new(&ctx, Direction::Inbound) - .establish(&consumer_addr) - .unwrap(); - - // Connect to the inbound connection and send a message - let sender = Connection::new(&ctx, Direction::Outbound) - .set_curve_encryption(CurveEncryption::Client { - server_public_key: server_pk, - secret_key: client_sk, - public_key: client_pk, - }) - .establish(&addr) - .unwrap(); - sender.send(&[&[PeerConnectionProtoMessage::Identify as u8]]).unwrap(); - sender - .send(&[&[PeerConnectionProtoMessage::Message as u8], &[1u8]]) - .unwrap(); - - // Receive the message from the consumer socket - let frames = consumer.receive(2000).unwrap(); - assert_eq!("123".as_bytes().to_vec(), frames[1]); - assert_eq!(vec![1u8], frames[2]); - - conn.send(vec![vec![111u8]]).unwrap(); - - let reply = sender.receive(100).unwrap(); - assert_eq!( - vec![vec![PeerConnectionProtoMessage::Message as u8], vec![111u8]], - reply - ); -} - -#[test] -fn connection_out() { - let addr = factories::net_address::create().build().unwrap(); - let ctx = ZmqContext::new(); - - let (server_sk, server_pk) = CurveEncryption::generate_keypair().unwrap(); - let (client_sk, client_pk) = CurveEncryption::generate_keypair().unwrap(); - - let consumer_addr = InprocAddress::random(); - - // Connect to the sender (peer) - let sender = Connection::new(&ctx, Direction::Inbound) - .set_name("Test sender") - .set_curve_encryption(CurveEncryption::Server { secret_key: server_sk }) - .establish(&addr) - .unwrap(); - - let conn_id = "123".as_bytes(); - - // Initialize and start peer connection - let context = PeerConnectionContextBuilder::new() - .set_id(conn_id.clone()) - .set_direction(Direction::Outbound) - .set_context(&ctx) - .set_message_sink_address(consumer_addr.clone()) - .set_curve_encryption(CurveEncryption::Client { - server_public_key: server_pk, - secret_key: client_sk, - public_key: client_pk, - }) - .set_address(addr.clone()) - .build() - .unwrap(); - - let mut conn = PeerConnection::new(); - - assert!(!conn.is_connected()); - conn.start(context).unwrap(); - conn.wait_connected_or_failure(&Duration::from_millis(2000)).unwrap(); - - // Connect the message consumer - let consumer = Connection::new(&ctx, Direction::Inbound) - .set_name("Test message sink") - .establish(&consumer_addr) - .unwrap(); - - conn.send(vec![vec![123u8]]).unwrap(); - - let ident = sender.receive(2000).unwrap(); - assert_eq!(vec![PeerConnectionProtoMessage::Identify as u8], ident[1]); - let data = sender.receive(2000).unwrap(); - assert_eq!(vec![123u8], data[2]); - - sender - .send(&[data[0].as_slice(), &[PeerConnectionProtoMessage::Message as u8], &[ - 123u8, - ]]) - .unwrap(); - let frames = consumer.receive(2000).unwrap(); - assert_eq!(conn_id.to_vec(), frames[1]); - assert_eq!(vec![1u8], frames[2]); - assert_eq!(vec![123u8], frames[3]); -} - -#[test] -fn connection_wait_connect_shutdown() { - let addr = factories::net_address::create().build().unwrap(); - let ctx = ZmqContext::new(); - - let receiver = Connection::new(&ctx, Direction::Inbound).establish(&addr).unwrap(); - - let consumer_addr = InprocAddress::random(); - - let context = PeerConnectionContextBuilder::new() - .set_id("123") - .set_direction(Direction::Outbound) - .set_context(&ctx) - .set_message_sink_address(consumer_addr.clone()) - .set_address(addr) - .build() - .unwrap(); - - let mut conn = PeerConnection::new(); - - assert!(!conn.is_connected()); - conn.start(context).unwrap(); - - conn.wait_connected_or_failure(&Duration::from_millis(2000)).unwrap(); - - conn.shutdown().unwrap(); - - assert!( - conn.wait_disconnected(&Duration::from_millis(2000)).is_ok(), - "Failed to shut down in 100ms" - ); - - drop(receiver); -} - -#[test] -fn connection_wait_connect_failed() { - let addr = factories::net_address::create().use_os_port().build().unwrap(); - let ctx = ZmqContext::new(); - - let consumer_addr = InprocAddress::random(); - - // This has nothing to connect to - let context = PeerConnectionContextBuilder::new() - .set_id("123") - .set_direction(Direction::Outbound) - .set_max_retry_attempts(1) - .set_context(&ctx) - .set_message_sink_address(consumer_addr.clone()) - .set_address(addr.clone()) - .build() - .unwrap(); - - let mut conn = PeerConnection::new(); - - assert!(!conn.is_connected()); - conn.start(context).unwrap(); - - let err = conn - .wait_connected_or_failure(&Duration::from_millis(2000)) - .unwrap_err(); - - assert!(conn.is_failed()); - match err { - ConnectionError::PeerError(err) => match err { - PeerConnectionError::ExceededMaxConnectRetryCount => {}, - _ => panic!("Unexpected connection error '{}'", err), - }, - _ => panic!("Unexpected connection error '{}'", err), - } -} - -#[test] -fn connection_pause_resume() { - let addr = factories::net_address::create().build().unwrap(); - let ctx = ZmqContext::new(); - - let consumer_addr = InprocAddress::random(); - - // Connect to the sender (peer) - let sender = Connection::new(&ctx, Direction::Outbound) - .set_linger(Linger::Indefinitely) - .establish(&addr) - .unwrap(); - let conn_id = "123".as_bytes(); - - // Initialize and start peer connection - let context = PeerConnectionContextBuilder::new() - .set_id(conn_id.clone()) - .set_direction(Direction::Inbound) - .set_context(&ctx) - .set_message_sink_address(consumer_addr.clone()) - .set_address(addr) - .build() - .unwrap(); - - let mut conn = PeerConnection::new(); - - assert!(!conn.is_connected()); - conn.start(context).unwrap(); - - conn.wait_listening_or_failure(&Duration::from_millis(2000)).unwrap(); - - // Connect the message consumer - let consumer = Connection::new(&ctx, Direction::Inbound) - .establish(&consumer_addr) - .unwrap(); - - let msg_type_frame = &[PeerConnectionProtoMessage::Message as u8]; - sender.send(&[&[PeerConnectionProtoMessage::Identify as u8]]).unwrap(); - sender.send(&[msg_type_frame, &[1u8]]).unwrap(); - - let frames = consumer.receive(2000).unwrap(); - assert_eq!(conn_id.to_vec(), frames[1]); - assert_eq!(vec![1u8], frames[2]); - - // Pause the connection - conn.pause().unwrap(); - - sender.send(&[msg_type_frame, &[2u8]]).unwrap(); - sender.send(&[msg_type_frame, &[3u8]]).unwrap(); - sender.send(&[msg_type_frame, &[4u8]]).unwrap(); - - let err = consumer.receive(3000).unwrap_err(); - assert!(err.is_timeout()); - - // Resume connection - conn.resume().unwrap(); - - // Should receive all the pending messages - let frames = consumer.receive(3000).unwrap(); - assert_eq!(vec![2u8], frames[3]); - let frames = consumer.receive(3000).unwrap(); - assert_eq!(vec![3u8], frames[3]); - let frames = consumer.receive(3000).unwrap(); - assert_eq!(vec![4u8], frames[3]); -} - -#[test] -fn connection_disconnect() { - let addr = factories::net_address::create().use_os_port().build().unwrap(); - let ctx = ZmqContext::new(); - - let consumer_addr = InprocAddress::random(); - - // Initialize and start peer connection - let context = PeerConnectionContextBuilder::new() - .set_id("123") - .set_direction(Direction::Inbound) - .set_context(&ctx) - .set_message_sink_address(consumer_addr.clone()) - .set_address(addr) - .build() - .unwrap(); - - let mut conn = PeerConnection::new(); - conn.start(context).unwrap(); - conn.wait_listening_or_failure(&Duration::from_millis(1000)).unwrap(); - let addr = NetAddress::from(conn.get_connected_address().unwrap()); - - { - // Connect to the inbound connection and send a message - let sender = Connection::new(&ctx, Direction::Outbound) - .set_linger(Linger::Indefinitely) - .establish(&addr) - .unwrap(); - sender.send(&[&[PeerConnectionProtoMessage::Identify as u8]]).unwrap(); - sender - .send(&[&[PeerConnectionProtoMessage::Message as u8], &[123u8]]) - .unwrap(); - } - - conn.wait_disconnected(&Duration::from_millis(2000)).unwrap(); -} - -#[test] -fn connection_stats() { - let addr = factories::net_address::create().build().unwrap(); - let ctx = ZmqContext::new(); - - let consumer_addr = InprocAddress::random(); - - // Connect to the sender (peer) - let sender = Connection::new(&ctx, Direction::Outbound) - .set_linger(Linger::Indefinitely) - .establish(&addr) - .unwrap(); - - // Initialize and start peer connection - let context = PeerConnectionContextBuilder::new() - .set_id("123".as_bytes()) - .set_direction(Direction::Inbound) - .set_context(&ctx) - .set_message_sink_address(consumer_addr.clone()) - .set_address(addr) - .build() - .unwrap(); - - let mut conn = PeerConnection::new(); - - assert!(!conn.is_connected()); - conn.start(context).unwrap(); - - let initial_stats = conn.connection_stats(); - let msg_type_frame = &[PeerConnectionProtoMessage::Message as u8]; - - sender.send(&[&[PeerConnectionProtoMessage::Identify as u8]]).unwrap(); - sender.send(&[msg_type_frame, &[1u8]]).unwrap(); - sender.send(&[msg_type_frame, &[2u8]]).unwrap(); - sender.send(&[msg_type_frame, &[3u8]]).unwrap(); - sender.send(&[msg_type_frame, &[4u8]]).unwrap(); - - conn.wait_connected_or_failure(&Duration::from_millis(2000)).unwrap(); - - conn.send(vec![vec![10u8]]).unwrap(); - conn.send(vec![vec![11u8]]).unwrap(); - conn.send(vec![vec![12u8]]).unwrap(); - - // Assert that receive stats update - assert_change( - || { - let stats = conn.connection_stats(); - stats.messages_recv() - }, - 4, - 40, - ); - - assert_change( - || { - let stats = conn.connection_stats(); - stats.messages_sent() - }, - 3, - 20, - ); - - let stats = conn.connection_stats(); - assert!(stats.last_activity() > initial_stats.last_activity()); -} - -#[test] -fn ignore_invalid_message_types() { - let addr = factories::net_address::create().build().unwrap(); - let ctx = ZmqContext::new(); - - let (server_sk, server_pk) = CurveEncryption::generate_keypair().unwrap(); - let (client_sk, client_pk) = CurveEncryption::generate_keypair().unwrap(); - - let consumer_addr = InprocAddress::random(); - - // Initialize and start peer connection - let context = PeerConnectionContextBuilder::new() - .set_id("123") - .set_direction(Direction::Inbound) - .set_context(&ctx) - .set_message_sink_address(consumer_addr.clone()) - .set_curve_encryption(CurveEncryption::Server { secret_key: server_sk }) - .set_address(addr.clone()) - .build() - .unwrap(); - - let mut conn = PeerConnection::new(); - conn.start(context).unwrap(); - conn.wait_listening_or_failure(&Duration::from_millis(1000)).unwrap(); - - // Connect the message consumer - let consumer = Connection::new(&ctx, Direction::Inbound) - .establish(&consumer_addr) - .unwrap(); - - // Connect to the inbound connection and send a message - let sender = Connection::new(&ctx, Direction::Outbound) - .set_curve_encryption(CurveEncryption::Client { - server_public_key: server_pk, - secret_key: client_sk, - public_key: client_pk, - }) - .establish(&addr) - .unwrap(); - - assert!(!conn.is_connected()); - sender.send(&[&[PeerConnectionProtoMessage::Identify as u8]]).unwrap(); - assert_change(|| conn.is_connected(), true, 10); - // Send invalid peer connection message type - sender.send(&[&[255], &[1u8]]).unwrap(); - sender - .send(&[&[PeerConnectionProtoMessage::Message as u8], &[1u8]]) - .unwrap(); - - // Receive the message from the consumer socket - let frames = consumer.receive(2000).unwrap(); - assert_eq!("123".as_bytes().to_vec(), frames[1]); - assert_eq!(vec![1u8], frames[2]); - assert_eq!(vec![1u8], frames[3]); - - // Test no more messages to receive. Since we have received above, the invalid message - // should be already ready to receive (10ms) if it was forwarded by the peer connection. - assert!(consumer.receive(10).is_err()); -} diff --git a/comms/tests/connection_manager/establisher.rs b/comms/tests/connection_manager/establisher.rs deleted file mode 100644 index 2ff027a7b8..0000000000 --- a/comms/tests/connection_manager/establisher.rs +++ /dev/null @@ -1,329 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::support::{ - factories::{self, TestFactory}, - helpers::ConnectionMessageCounter, -}; -use std::{path::PathBuf, sync::Arc, time::Duration}; -use tari_comms::{ - connection::{CurveEncryption, Direction, InprocAddress, NetAddress, ZmqContext}, - connection_manager::{establisher::ConnectionEstablisher, ConnectionManagerError, PeerConnectionConfig}, - control_service::messages::{ControlServiceResponseType, Pong}, - message::{Message, MessageEnvelope, MessageFlags, MessageHeader, NodeDestination}, -}; -use tari_storage::{ - lmdb_store::{LMDBBuilder, LMDBError, LMDBStore}, - LMDBWrapper, -}; -use tari_utilities::{message_format::MessageFormat, thread_join::ThreadJoinWithTimeout}; - -fn make_peer_connection_config(message_sink_address: InprocAddress) -> PeerConnectionConfig { - PeerConnectionConfig { - peer_connection_establish_timeout: Duration::from_secs(5), - max_message_size: 1024, - max_connections: 10, - host: "127.0.0.1".parse().unwrap(), - max_connect_retries: 3, - message_sink_address, - socks_proxy_address: None, - } -} - -fn get_path(name: &str) -> String { - let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - path.push("tests/data"); - path.push(name); - path.to_str().unwrap().to_string() -} - -fn init_datastore(name: &str) -> Result { - let path = get_path(name); - let _ = std::fs::create_dir(&path).unwrap_or_default(); - LMDBBuilder::new() - .set_path(&path) - .set_environment_size(10) - .set_max_number_of_databases(2) - .add_database(name, lmdb_zero::db::CREATE) - .build() -} - -fn clean_up_datastore(name: &str) { - std::fs::remove_dir_all(get_path(name)).unwrap(); -} - -// This tries to break the establisher by sending malformed messages. The establisher should -// disregard the malformed message and continue to try other addresses. Once all -// addresses fail, the correct error should be returned. -#[test] -fn establish_control_service_connection_fail() { - let context = ZmqContext::new(); - - let node_identity = factories::node_identity::create().build().map(Arc::new).unwrap(); - - let peers = factories::peer::create_many(2) - .with_factory(factories::peer::create().with_net_addresses_factory(factories::net_address::create_many(2))) - .build() - .unwrap(); - - // Setup a connection counter to act as a 'junk' endpoint for a peers control service. - let mut msg_counter1 = ConnectionMessageCounter::new(&context); - msg_counter1.set_response(vec!["JUNK".as_bytes().to_vec()]); - msg_counter1.start(peers[0].addresses[0].net_address.clone()); - - let mut msg_counter2 = ConnectionMessageCounter::new(&context); - msg_counter2.set_response(vec!["JUNK".as_bytes().to_vec()]); - msg_counter2.start(peers[0].addresses[1].net_address.clone()); - - // Note: every test should have unique database - let database_name = "establisher_establish_control_service_connection_fail"; - let datastore = init_datastore(database_name).unwrap(); - let database = datastore.get_handle(database_name).unwrap(); - let database = LMDBWrapper::new(Arc::new(database)); - let peer_manager = Arc::new( - factories::peer_manager::create() - .with_database(database) - .with_peers(peers.clone()) - .build() - .unwrap(), - ); - let config = make_peer_connection_config(InprocAddress::random()); - - let example_peer = &peers[0]; - - let establisher = ConnectionEstablisher::new(context.clone(), node_identity, config, peer_manager); - match establisher.connect_control_service_client(example_peer) { - Ok(_) => panic!("Unexpected success result"), - Err(ConnectionManagerError::MaxConnnectionAttemptsExceeded) => {}, - Err(err) => panic!("Unexpected error type: {:?}", err), - } - - msg_counter1.assert_count(1, 20); - msg_counter2.assert_count(1, 20); - - clean_up_datastore(database_name); -} - -#[test] -fn establish_control_service_connection_succeed() { - let context = ZmqContext::new(); - let address = factories::net_address::create().build().unwrap(); - // The node attempting to connect - let node_identity1 = factories::node_identity::create().build().map(Arc::new).unwrap(); - // The node being connected to - let node_identity2 = factories::node_identity::create().build().map(Arc::new).unwrap(); - - let example_peer = factories::peer::create() - .with_public_key(node_identity2.identity.public_key.clone()) - .with_net_addresses(vec![address]) - .build() - .unwrap(); - - // Setup a connection counter to act as a control service sending back a pong - let pong_response = { - let envelope = MessageEnvelope::construct( - &node_identity2, - node_identity1.identity.public_key.clone(), - NodeDestination::PublicKey(node_identity1.identity.public_key.clone()), - Message::from_message_format( - MessageHeader::new(ControlServiceResponseType::Pong).unwrap(), - Pong {}.to_binary().unwrap(), - ) - .unwrap() - .to_binary() - .unwrap(), - MessageFlags::ENCRYPTED, - ) - .unwrap(); - envelope.into_frame_set() - }; - - let address: NetAddress = example_peer.addresses[0].net_address.clone(); - - let mut msg_counter1 = ConnectionMessageCounter::new(&context); - msg_counter1.set_response(pong_response); - msg_counter1.start(address); - - // Setup peer manager - let database_name = "establisher_establish_control_service_connection_succeed"; // Note: every test should have unique database - let datastore = init_datastore(database_name).unwrap(); - let database = datastore.get_handle(database_name).unwrap(); - let database = LMDBWrapper::new(Arc::new(database)); - let peer_manager = Arc::new( - factories::peer_manager::create() - .with_database(database) - .with_peers(vec![example_peer.clone()]) - .build() - .unwrap(), - ); - - let config = make_peer_connection_config(InprocAddress::random()); - let establisher = ConnectionEstablisher::new(context.clone(), node_identity1, config, peer_manager); - let client = establisher.connect_control_service_client(&example_peer).unwrap(); - client.ping_pong(Duration::from_millis(3000)).unwrap(); - - msg_counter1.assert_count(2, 20); - - clean_up_datastore(database_name); -} - -#[test] -fn establish_peer_connection_outbound() { - let context = ZmqContext::new(); - let msg_sink_address = InprocAddress::random(); - let node_identity = factories::node_identity::create().build().map(Arc::new).unwrap(); - - // Setup a message counter to count the number of messages sent to the consumer address - let msg_counter = ConnectionMessageCounter::new(&context); - msg_counter.start(msg_sink_address.clone()); - - // Setup a peer connection - let (peer_curve_sk, peer_curve_pk) = CurveEncryption::generate_keypair().unwrap(); - let (other_peer_conn, other_peer_conn_handle) = factories::peer_connection::create() - .with_peer_connection_context_factory( - factories::peer_connection_context::create() - .with_message_sink_address(msg_sink_address.clone()) - .with_curve_keypair((peer_curve_sk, peer_curve_pk.clone())) - .with_context(&context) - .with_direction(Direction::Inbound), - ) - .build() - .unwrap(); - - other_peer_conn - .wait_listening_or_failure(&Duration::from_millis(2000)) - .unwrap(); - - let address = other_peer_conn.get_connected_address().unwrap().to_string(); - assert_ne!(address, "127.0.0.1:0"); - let address: NetAddress = other_peer_conn.get_connected_address().unwrap().into(); - - let example_peer = factories::peer::create() - .with_net_addresses(vec![address.clone()]) - .build() - .unwrap(); - - let database_name = "establisher_establish_peer_connection_outbound"; // Note: every test should have unique database - let datastore = init_datastore(database_name).unwrap(); - let database = datastore.get_handle(database_name).unwrap(); - let database = LMDBWrapper::new(Arc::new(database)); - let peer_manager = Arc::new( - factories::peer_manager::create() - .with_database(database) - .with_peers(vec![example_peer.clone()]) - .build() - .unwrap(), - ); - - let config = make_peer_connection_config(InprocAddress::random()); - let establisher = ConnectionEstablisher::new(context.clone(), node_identity, config, peer_manager); - let (connection, peer_conn_handle) = establisher - .establish_outbound_peer_connection(example_peer.node_id.clone().into(), address, peer_curve_pk) - .unwrap(); - - connection.send(vec!["HELLO".as_bytes().to_vec()]).unwrap(); - connection.send(vec!["TARI".as_bytes().to_vec()]).unwrap(); - - connection.shutdown().unwrap(); - connection.wait_disconnected(&Duration::from_millis(3000)).unwrap(); - - other_peer_conn.shutdown().unwrap(); - other_peer_conn.wait_disconnected(&Duration::from_millis(3000)).unwrap(); - other_peer_conn_handle - .timeout_join(Duration::from_millis(3000)) - .unwrap(); - - assert_eq!(msg_counter.count(), 2); - - peer_conn_handle.timeout_join(Duration::from_millis(3000)).unwrap(); - - clean_up_datastore(database_name); -} - -#[test] -fn establish_peer_connection_inbound() { - let context = ZmqContext::new(); - let msg_sink_address = InprocAddress::random(); - let node_identity = factories::node_identity::create().build().map(Arc::new).unwrap(); - - let (secret_key, public_key) = CurveEncryption::generate_keypair().unwrap(); - - let example_peer = factories::peer::create().build().unwrap(); - - let database_name = "establish_peer_connection_inbound"; // Note: every test should have unique database - let datastore = init_datastore(database_name).unwrap(); - let database = datastore.get_handle(database_name).unwrap(); - let database = LMDBWrapper::new(Arc::new(database)); - let peer_manager = Arc::new( - factories::peer_manager::create() - .with_database(database) - .with_peers(vec![example_peer.clone()]) - .build() - .unwrap(), - ); - - // Setup a message counter to count the number of messages sent to the consumer address - let msg_counter = ConnectionMessageCounter::new(&context); - msg_counter.start(msg_sink_address.clone()); - - // Create a connection establisher - let config = make_peer_connection_config(msg_sink_address.clone()); - let establisher = ConnectionEstablisher::new(context.clone(), node_identity, config, peer_manager); - let (connection, peer_conn_handle) = establisher - .establish_inbound_peer_connection(example_peer.node_id.clone().into(), secret_key) - .unwrap(); - - connection - .wait_listening_or_failure(&Duration::from_millis(3000)) - .unwrap(); - let address: NetAddress = connection.get_connected_address().unwrap().into(); - - // Setup a peer connection which will connect to our established inbound peer connection - let (other_peer_conn, other_peer_conn_handle) = factories::peer_connection::create() - .with_peer_connection_context_factory( - factories::peer_connection_context::create() - .with_context(&context) - .with_address(address) - .with_server_public_key(public_key.clone()) - .with_direction(Direction::Outbound), - ) - .build() - .unwrap(); - - other_peer_conn - .wait_connected_or_failure(&Duration::from_millis(3000)) - .unwrap(); - // Start sending messages - other_peer_conn.send(vec!["HELLO".as_bytes().to_vec()]).unwrap(); - other_peer_conn.send(vec!["TARI".as_bytes().to_vec()]).unwrap(); - let _ = other_peer_conn.shutdown(); - other_peer_conn.wait_disconnected(&Duration::from_millis(3000)).unwrap(); - - assert_eq!(msg_counter.count(), 2); - - peer_conn_handle.timeout_join(Duration::from_millis(3000)).unwrap(); - other_peer_conn_handle - .timeout_join(Duration::from_millis(3000)) - .unwrap(); - - clean_up_datastore(database_name); -} diff --git a/comms/tests/connection_manager/manager.rs b/comms/tests/connection_manager/manager.rs deleted file mode 100644 index 5bba10b3a1..0000000000 --- a/comms/tests/connection_manager/manager.rs +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::support::{ - factories::{self, TestFactory}, - helpers::ConnectionMessageCounter, -}; -use std::{path::PathBuf, sync::Arc, thread, time::Duration}; -use tari_comms::{ - connection::{types::Linger, InprocAddress, ZmqContext}, - connection_manager::PeerConnectionConfig, - control_service::{ControlService, ControlServiceConfig}, - peer_manager::{Peer, PeerManager}, - types::CommsDatabase, -}; -use tari_storage::{ - lmdb_store::{LMDBBuilder, LMDBError, LMDBStore}, - LMDBWrapper, -}; -use tari_utilities::thread_join::ThreadJoinWithTimeout; - -fn make_peer_connection_config(consumer_address: InprocAddress) -> PeerConnectionConfig { - PeerConnectionConfig { - peer_connection_establish_timeout: Duration::from_secs(5), - max_message_size: 1024, - max_connections: 10, - host: "127.0.0.1".parse().unwrap(), - max_connect_retries: 5, - message_sink_address: consumer_address, - socks_proxy_address: None, - } -} - -fn make_peer_manager(peers: Vec, database: CommsDatabase) -> Arc { - Arc::new( - factories::peer_manager::create() - .with_peers(peers) - .with_database(database) - .build() - .unwrap(), - ) -} - -fn get_path(name: &str) -> String { - let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - path.push("tests/data"); - path.push(name); - path.to_str().unwrap().to_string() -} - -fn init_datastore(name: &str) -> Result { - let path = get_path(name); - let _ = std::fs::create_dir(&path).unwrap_or_default(); - LMDBBuilder::new() - .set_path(&path) - .set_environment_size(10) - .set_max_number_of_databases(2) - .add_database(name, lmdb_zero::db::CREATE) - .build() -} - -fn clean_up_datastore(name: &str) { - std::fs::remove_dir_all(get_path(name)).unwrap(); -} - -fn pause() { - thread::sleep(Duration::from_millis(200)); -} - -#[test] -#[allow(non_snake_case)] -fn establish_peer_connection() { - let context = ZmqContext::new(); - - let node_A_identity = Arc::new(factories::node_identity::create().build().unwrap()); - - let node_B_consumer_address = InprocAddress::random(); - let node_B_msg_counter = ConnectionMessageCounter::new(&context); - node_B_msg_counter.start(node_B_consumer_address.clone()); - - //---------------------------------- Node B Setup --------------------------------------------// - - let node_B_control_port_address = factories::net_address::create().build().unwrap(); - let node_B_identity = Arc::new( - factories::node_identity::create() - .with_control_service_address(node_B_control_port_address.clone()) - .build() - .unwrap(), - ); - - let node_B_peer = factories::peer::create() - .with_net_addresses(vec![node_B_control_port_address.clone()]) - .with_public_key(node_B_identity.identity.public_key.clone()) - .build() - .unwrap(); - - // Node B knows no peers - let node_B_database_name = "connection_manager_node_B_peer_manager"; - let datastore = init_datastore(node_B_database_name).unwrap(); - let database = datastore.get_handle(node_B_database_name).unwrap(); - let database = LMDBWrapper::new(Arc::new(database)); - let node_B_peer_manager = make_peer_manager(vec![], database); - let node_B_connection_manager = Arc::new( - factories::connection_manager::create() - .with_context(context.clone()) - .with_node_identity(node_B_identity.clone()) - .with_peer_manager(node_B_peer_manager) - .with_peer_connection_config(make_peer_connection_config(node_B_consumer_address.clone())) - .build() - .unwrap(), - ); - - // Start node B's control service - let node_B_control_service = ControlService::new(context.clone(), node_B_identity.clone(), ControlServiceConfig { - socks_proxy_address: None, - listener_address: node_B_control_port_address, - requested_connection_timeout: Duration::from_millis(5000), - }) - .serve(node_B_connection_manager) - .unwrap(); - - // Give the control service a moment to start up - pause(); - - //---------------------------------- Node A setup --------------------------------------------// - - let node_A_consumer_address = InprocAddress::random(); - - // Add node B to node A's peer manager - let node_A_database_name = "connection_manager_node_A_peer_manager"; // Note: every test should have unique database - let datastore = init_datastore(node_A_database_name).unwrap(); - let database = datastore.get_handle(node_A_database_name).unwrap(); - let database = LMDBWrapper::new(Arc::new(database)); - let node_A_peer_manager = make_peer_manager(vec![node_B_peer.clone()], database); - let node_A_connection_manager = Arc::new( - factories::connection_manager::create() - .with_context(context.clone()) - .with_node_identity(node_A_identity.clone()) - .with_peer_manager(node_A_peer_manager) - .with_peer_connection_config(make_peer_connection_config(node_A_consumer_address)) - .build() - .unwrap(), - ); - - //------------------------------ Negotiate connection to node B -----------------------------------// - - let node_B_peer_copy = node_B_peer.clone(); - let node_A_connection_manager_cloned = node_A_connection_manager.clone(); - let handle1 = thread::spawn(move || -> Result<(), String> { - let to_node_B_conn = node_A_connection_manager_cloned - .establish_connection_to_peer(&node_B_peer) - .map_err(|err| format!("{:?}", err))?; - to_node_B_conn.set_linger(Linger::Indefinitely).unwrap(); - to_node_B_conn - .send(vec!["THREAD1".as_bytes().to_vec()]) - .map_err(|err| format!("{:?}", err))?; - Ok(()) - }); - - let node_A_connection_manager_cloned = node_A_connection_manager.clone(); - let handle2 = thread::spawn(move || -> Result<(), String> { - let to_node_B_conn = node_A_connection_manager_cloned - .establish_connection_to_peer(&node_B_peer_copy) - .map_err(|err| format!("{:?}", err))?; - to_node_B_conn.set_linger(Linger::Indefinitely).unwrap(); - to_node_B_conn - .send(vec!["THREAD2".as_bytes().to_vec()]) - .map_err(|err| format!("{:?}", err))?; - Ok(()) - }); - - handle1.timeout_join(Duration::from_millis(2000)).unwrap(); - handle2.timeout_join(Duration::from_millis(2000)).unwrap(); - - // Give the peer connections a moment to receive and the message sink connections to send - pause(); - - node_B_control_service.shutdown().unwrap(); - node_B_control_service - .timeout_join(Duration::from_millis(1000)) - .unwrap(); - - assert_eq!(node_A_connection_manager.get_active_connection_count(), 1); - node_B_msg_counter.assert_count(2, 20); - - match Arc::try_unwrap(node_A_connection_manager) { - Ok(manager) => manager.shutdown().into_iter().map(|r| r.unwrap()).collect::>(), - Err(_) => panic!("Unable to unwrap connection manager from Arc"), - }; - - clean_up_datastore(node_A_database_name); - clean_up_datastore(node_B_database_name); -} diff --git a/comms/tests/connection_manager/mod.rs b/comms/tests/connection_manager/mod.rs deleted file mode 100644 index 7d2f0ed95a..0000000000 --- a/comms/tests/connection_manager/mod.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -mod establisher; -mod manager; diff --git a/comms/tests/control_service/client.rs b/comms/tests/control_service/client.rs deleted file mode 100644 index b09a243c92..0000000000 --- a/comms/tests/control_service/client.rs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::support::factories::{self, TestFactory}; -use std::{sync::Arc, time::Duration}; -use tari_comms::{ - connection::{Connection, Direction, InprocAddress, ZmqContext}, - control_service::{messages::Ping, ControlServiceClient}, -}; - -#[test] -fn send_ping_recv_pong() { - let context = ZmqContext::new(); - let address = InprocAddress::random(); - - let outbound_conn = Connection::new(&context, Direction::Outbound) - .establish(&address) - .unwrap(); - let inbound_conn = Connection::new(&context, Direction::Inbound) - .establish(&address) - .unwrap(); - - let node_identity_1 = factories::node_identity::create().build().map(Arc::new).unwrap(); - let node_identity_2 = factories::node_identity::create().build().map(Arc::new).unwrap(); - - let out_client = ControlServiceClient::new( - node_identity_1.clone(), - node_identity_2.identity.public_key.clone(), - outbound_conn, - ); - out_client.send_ping().unwrap(); - - let in_client = ControlServiceClient::new( - node_identity_2.clone(), - node_identity_1.identity.public_key.clone(), - inbound_conn, - ); - - let _msg: Ping = in_client.receive_message(Duration::from_millis(2000)).unwrap().unwrap(); -} diff --git a/comms/tests/control_service/mod.rs b/comms/tests/control_service/mod.rs deleted file mode 100644 index a54693b553..0000000000 --- a/comms/tests/control_service/mod.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -mod client; -mod service; diff --git a/comms/tests/control_service/service.rs b/comms/tests/control_service/service.rs deleted file mode 100644 index 6b106c8125..0000000000 --- a/comms/tests/control_service/service.rs +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::support::{ - factories::{self, TestFactory}, - helpers::ConnectionMessageCounter, -}; -use std::{path::PathBuf, sync::Arc, time::Duration}; -use tari_comms::{ - connection::{types::Direction, Connection, InprocAddress, ZmqContext}, - connection_manager::{ConnectionManager, PeerConnectionConfig}, - control_service::{messages::ConnectRequestOutcome, ControlService, ControlServiceClient, ControlServiceConfig}, - peer_manager::{NodeId, NodeIdentity, Peer, PeerFlags, PeerManager}, -}; -use tari_storage::{ - lmdb_store::{LMDBBuilder, LMDBDatabase, LMDBError, LMDBStore}, - LMDBWrapper, -}; -use tari_utilities::thread_join::ThreadJoinWithTimeout; - -fn make_peer_manager(peers: Vec, database: LMDBDatabase) -> Arc { - Arc::new( - factories::peer_manager::create() - .with_peers(peers) - .with_database(LMDBWrapper::new(Arc::new(database))) - .build() - .unwrap(), - ) -} -fn get_path(name: &str) -> String { - let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - path.push("tests/data"); - path.push(name); - path.to_str().unwrap().to_string() -} - -// Initialize the datastore. Note: every test should have unique database name -fn init_datastore(name: &str) -> Result { - let path = get_path(name); - let _ = std::fs::create_dir(&path).unwrap_or_default(); - LMDBBuilder::new() - .set_path(&path) - .set_environment_size(10) - .set_max_number_of_databases(2) - .add_database(name, lmdb_zero::db::CREATE) - .build() -} - -fn clean_up_datastore(name: &str) { - std::fs::remove_dir_all(get_path(name)).unwrap(); -} - -fn setup( - database_name: &str, - peer_conn_config: PeerConnectionConfig, -) -> (ZmqContext, Arc, Arc, Arc) -{ - let node_identity = factories::node_identity::create().build().map(Arc::new).unwrap(); - let context = ZmqContext::new(); - let datastore = init_datastore(database_name).unwrap(); - let database = datastore.get_handle(database_name).unwrap(); - let peer_manager = make_peer_manager(vec![], database); - let connection_manager = factories::connection_manager::create() - .with_context(context.clone()) - .with_peer_connection_config(peer_conn_config) - .with_peer_manager(Arc::clone(&peer_manager)) - .build() - .map(Arc::new) - .unwrap(); - - (context, node_identity, peer_manager, connection_manager) -} - -#[test] -fn request_connection() { - let database_name = "control_service_request_connection"; - - let message_sink_address = InprocAddress::random(); - let peer_conn_config = PeerConnectionConfig { - message_sink_address, - ..Default::default() - }; - - let (context, node_identity_a, peer_manager, connection_manager) = setup(database_name, peer_conn_config.clone()); - - let msg_counter = ConnectionMessageCounter::new(&context); - msg_counter.start(peer_conn_config.message_sink_address.clone()); - - // Setup the destination peer's control service - let listener_address = factories::net_address::create().build().unwrap(); - let service_handle = ControlService::new(context.clone(), Arc::clone(&node_identity_a), ControlServiceConfig { - listener_address: listener_address.clone(), - socks_proxy_address: None, - requested_connection_timeout: Duration::from_millis(2000), - }) - .serve(connection_manager) - .unwrap(); - - // Setup the requesting peer - let node_identity_b = factories::node_identity::create().build().map(Arc::new).unwrap(); - // --- Client connection for the destination peer's control service - let client_conn = Connection::new(&context, Direction::Outbound) - .establish(&listener_address) - .unwrap(); - let client = ControlServiceClient::new( - Arc::clone(&node_identity_b), - node_identity_a.identity.public_key.clone(), - client_conn, - ); - - // --- Request a connection to the peer connection - client - .send_request_connection( - node_identity_b.control_service_address().unwrap(), - NodeId::from_key(&node_identity_b.identity.public_key).unwrap(), - ) - .unwrap(); - let outcome = client - .receive_message::(Duration::from_millis(3000)) - .unwrap() - .unwrap(); - - let peer = peer_manager - .find_with_public_key(&node_identity_b.identity.public_key) - .unwrap(); - assert_eq!(peer.public_key, node_identity_b.identity.public_key); - assert_eq!(peer.node_id, node_identity_b.identity.node_id); - assert_eq!( - peer.addresses[0], - node_identity_b.control_service_address().unwrap().into() - ); - assert_eq!(peer.flags, PeerFlags::empty()); - - match outcome { - ConnectRequestOutcome::Accepted { - address, - curve_public_key, - } => { - // --- Setup outbound peer connection to the requested address - let (peer_conn, peer_conn_handle) = factories::peer_connection::create() - .with_peer_connection_context_factory( - factories::peer_connection_context::create() - .with_context(&context) - .with_direction(Direction::Outbound) - .with_address(address.clone()) - .with_message_sink_address(peer_conn_config.message_sink_address.clone()) - .with_server_public_key(curve_public_key), - ) - .build() - .unwrap(); - - peer_conn - .wait_connected_or_failure(&Duration::from_millis(3000)) - .unwrap(); - - peer_conn.shutdown().unwrap(); - peer_conn_handle.timeout_join(Duration::from_millis(3000)).unwrap(); - }, - ConnectRequestOutcome::Rejected(reason) => panic!("Connection was rejected unexpectedly: {}", reason), - } - service_handle.shutdown().unwrap(); - service_handle.timeout_join(Duration::from_millis(3000)).unwrap(); - - clean_up_datastore(database_name); -} - -#[test] -fn ping_pong() { - let database_name = "control_service_ping_pong"; - let (context, node_identity, _, connection_manager) = setup(database_name, PeerConnectionConfig::default()); - - let listener_address = factories::net_address::create().build().unwrap(); - let service = ControlService::new(context.clone(), Arc::clone(&node_identity), ControlServiceConfig { - listener_address: listener_address.clone(), - socks_proxy_address: None, - requested_connection_timeout: Duration::from_millis(2000), - }) - .serve(connection_manager) - .unwrap(); - - let client_conn = Connection::new(&context, Direction::Outbound) - .establish(&listener_address) - .unwrap(); - let client = ControlServiceClient::new( - Arc::clone(&node_identity), - node_identity.identity.public_key.clone(), - client_conn, - ); - - client.ping_pong(Duration::from_millis(2000)).unwrap().unwrap(); - - service.shutdown().unwrap(); - service.timeout_join(Duration::from_millis(3000)).unwrap(); - - clean_up_datastore(database_name); -} diff --git a/comms/tests/data/.gitkeep b/comms/tests/data/.gitkeep deleted file mode 100644 index 79e790c1e5..0000000000 --- a/comms/tests/data/.gitkeep +++ /dev/null @@ -1 +0,0 @@ -Temp folder for LMDB database files \ No newline at end of file diff --git a/comms/tests/outbound_message_service/mod.rs b/comms/tests/outbound_message_service/mod.rs deleted file mode 100644 index 6461418670..0000000000 --- a/comms/tests/outbound_message_service/mod.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE - -mod outbound_message_pool; diff --git a/comms/tests/outbound_message_service/outbound_message_pool.rs b/comms/tests/outbound_message_service/outbound_message_pool.rs deleted file mode 100644 index a8bc771203..0000000000 --- a/comms/tests/outbound_message_service/outbound_message_pool.rs +++ /dev/null @@ -1,313 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE - -use crate::support::{ - factories::{self, TestFactory}, - helpers::ConnectionMessageCounter, -}; -use std::{fs, path::PathBuf, sync::Arc, thread, time::Duration}; -use tari_comms::{ - connection::{InprocAddress, ZmqContext}, - connection_manager::{ConnectionManager, PeerConnectionConfig}, - control_service::{ControlService, ControlServiceConfig}, - message::MessageFlags, - outbound_message_service::{ - outbound_message_pool::OutboundMessagePoolConfig, - outbound_message_service::OutboundMessageService, - BroadcastStrategy, - OutboundMessagePool, - }, - peer_manager::{Peer, PeerManager}, - types::CommsDatabase, -}; -use tari_storage::{ - lmdb_store::{LMDBBuilder, LMDBError, LMDBStore}, - LMDBWrapper, -}; - -fn make_peer_connection_config(message_sink_address: InprocAddress) -> PeerConnectionConfig { - PeerConnectionConfig { - peer_connection_establish_timeout: Duration::from_secs(5), - max_message_size: 1024, - max_connections: 10, - host: "127.0.0.1".parse().unwrap(), - max_connect_retries: 3, - message_sink_address, - socks_proxy_address: None, - } -} - -fn make_peer_manager(peers: Vec, database: CommsDatabase) -> Arc { - Arc::new( - factories::peer_manager::create() - .with_peers(peers) - .with_database(database) - .build() - .unwrap(), - ) -} - -fn get_path(name: &str) -> String { - let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - path.push("tests/data"); - path.push(name); - path.to_str().unwrap().to_string() -} - -fn init_datastore(name: &str) -> Result { - let path = get_path(name); - let _ = fs::create_dir(&path).unwrap_or_default(); - LMDBBuilder::new() - .set_path(&path) - .set_environment_size(10) - .set_max_number_of_databases(2) - .add_database(name, lmdb_zero::db::CREATE) - .build() -} - -fn clean_up_datastore(name: &str) { - fs::remove_dir_all(get_path(name)).unwrap(); -} - -/// This tests a message being sent through to the OMP where a peer (Node B) is awaiting alive and accepting -/// connections. -#[test] -#[allow(non_snake_case)] -fn outbound_message_pool_no_retry() { - let context = ZmqContext::new(); - let node_identity = Arc::new(factories::node_identity::create().build().unwrap()); - - //---------------------------------- Node B Setup --------------------------------------------// - - let node_B_msg_sink_address = InprocAddress::random(); - let node_B_control_port_address = factories::net_address::create().build().unwrap(); - - let node_B_msg_counter = ConnectionMessageCounter::new(&context); - node_B_msg_counter.start(node_B_msg_sink_address.clone()); - - let node_B_peer = factories::peer::create() - .with_net_addresses(vec![node_B_control_port_address.clone()]) - // Set node B's secret key to be the same as node A's so that we can generate the same shared secret - .with_public_key(node_identity.identity.public_key.clone()) - .build() - .unwrap(); - - // Node B knows no peers - let node_B_database_name = "omp_node_B_peer_manager"; // Note: every test should have unique database - let datastore = init_datastore(node_B_database_name).unwrap(); - let database = datastore.get_handle(node_B_database_name).unwrap(); - let database = LMDBWrapper::new(Arc::new(database)); - let node_B_peer_manager = make_peer_manager(vec![], database); - let node_B_connection_manager = Arc::new(ConnectionManager::new( - context.clone(), - node_identity.clone(), - node_B_peer_manager, - make_peer_connection_config(node_B_msg_sink_address.clone()), - )); - - // Start node B's control service - let node_B_control_service = ControlService::new(context.clone(), node_identity.clone(), ControlServiceConfig { - socks_proxy_address: None, - listener_address: node_B_control_port_address, - requested_connection_timeout: Duration::from_millis(2000), - }) - .serve(node_B_connection_manager) - .unwrap(); - - //---------------------------------- Node A setup --------------------------------------------// - - let node_A_msg_sink_address = InprocAddress::random(); - - // Add node B to node A's peer manager - let node_A_database_name = "omp_node_A_peer_manager"; // Note: every test should have unique database - let datastore = init_datastore(node_A_database_name).unwrap(); - let database = datastore.get_handle(node_A_database_name).unwrap(); - let database = LMDBWrapper::new(Arc::new(database)); - let node_A_peer_manager = make_peer_manager(vec![node_B_peer.clone()], database); - let node_A_connection_manager = Arc::new( - factories::connection_manager::create() - .with_peer_manager(node_A_peer_manager.clone()) - .with_peer_connection_config(make_peer_connection_config(node_A_msg_sink_address)) - .build() - .unwrap(), - ); - - // Setup Node A OMP and OMS - let omp_config = OutboundMessagePoolConfig::default(); - let mut omp = OutboundMessagePool::new( - omp_config.clone(), - node_A_peer_manager.clone(), - node_A_connection_manager.clone(), - ); - - let oms = OutboundMessageService::new(node_identity.clone(), omp.sender(), node_A_peer_manager.clone()).unwrap(); - - let oms2 = OutboundMessageService::new(node_identity.clone(), omp.sender(), node_A_peer_manager.clone()).unwrap(); - - omp.start().unwrap(); - let message_envelope_body = vec![0, 1, 2, 3]; - - // Send 8 message alternating two different OMS's - for _ in 0..4 { - oms.send_raw( - BroadcastStrategy::DirectNodeId(node_B_peer.node_id.clone()), - MessageFlags::ENCRYPTED, - message_envelope_body.clone(), - ) - .unwrap(); - oms2.send_raw( - BroadcastStrategy::DirectNodeId(node_B_peer.node_id.clone()), - MessageFlags::ENCRYPTED, - message_envelope_body.clone(), - ) - .unwrap(); - } - - node_B_msg_counter.assert_count(8, 30); - node_B_control_service.shutdown().unwrap(); - node_B_control_service - .timeout_join(Duration::from_millis(3000)) - .unwrap(); - - omp.shutdown().unwrap(); - clean_up_datastore(node_A_database_name); - clean_up_datastore(node_B_database_name); -} - -/// This tests the reliability of the OMP. -/// -/// This test is quite slow as it has to allow time for messages to send after a backoff period. -/// -/// 1. A message is sent through to node A's OMP, -/// 2. Node B is offline so the message is sent to the message retry service -/// 3. Node B comes online (control service is started up) -/// 4. The message retry service eventually sends the messages -/// 5. Assert that all messages have been received -#[test] -#[allow(non_snake_case)] -fn test_outbound_message_pool_fail_and_retry() { - let context = ZmqContext::new(); - - let node_A_identity = factories::node_identity::create().build().map(Arc::new).unwrap(); - //---------------------------------- Node B Setup --------------------------------------------// - - let node_B_msg_sink_address = InprocAddress::random(); - let node_B_msg_counter = ConnectionMessageCounter::new(&context); - node_B_msg_counter.start(node_B_msg_sink_address.clone()); - - let node_B_control_port_address = factories::net_address::create().build().unwrap(); - - let node_B_identity = factories::node_identity::create() - .with_control_service_address(node_B_control_port_address.clone()) - .build() - .map(Arc::new) - .unwrap(); - - let node_B_peer = factories::peer::create() - .with_net_addresses(vec![node_B_control_port_address.clone()]) - // Set node B's secret key to be the same as node A's so that we can generate the same shared secret - .with_public_key(node_B_identity.identity.public_key.clone()) - .build() - .unwrap(); - - //---------------------------------- Node A setup --------------------------------------------// - - let node_A_msg_sink_address = InprocAddress::random(); - - // Add node B to node A's peer manager - let database_name = "omp_test_outbound_message_pool_fail_and_retry1"; // Note: every test should have unique database - let datastore = init_datastore(database_name).unwrap(); - let database = datastore.get_handle(database_name).unwrap(); - let database = LMDBWrapper::new(Arc::new(database)); - let node_A_peer_manager = factories::peer_manager::create() - .with_peers(vec![node_B_peer.clone()]) - .with_database(database) - .build() - .map(Arc::new) - .unwrap(); - let node_A_connection_manager = factories::connection_manager::create() - .with_context(context.clone()) - .with_node_identity(node_A_identity.clone()) - .with_peer_manager(node_A_peer_manager.clone()) - .with_peer_connection_config(make_peer_connection_config(node_A_msg_sink_address)) - .build() - .map(Arc::new) - .unwrap(); - - // Setup Node A OMP and OMS - let omp_config = OutboundMessagePoolConfig::default(); - let mut omp = OutboundMessagePool::new( - omp_config.clone(), - node_A_peer_manager.clone(), - node_A_connection_manager.clone(), - ); - - let oms = OutboundMessageService::new(node_A_identity.clone(), omp.sender(), node_A_peer_manager.clone()).unwrap(); - - omp.start().unwrap(); - let message_envelope_body = vec![0, 1, 2, 3]; - - for _ in 0..5 { - oms.send_raw( - BroadcastStrategy::DirectNodeId(node_B_peer.node_id.clone()), - MessageFlags::ENCRYPTED, - message_envelope_body.clone(), - ) - .unwrap(); - } - - thread::sleep(Duration::from_millis(1000)); - - // Later, start node B's control service and test if we receive messages - let node_B_database_name = "omp_node_B_peer_manager"; // Note: every test should have unique database - let datastore = init_datastore(node_B_database_name).unwrap(); - let database = datastore.get_handle(node_B_database_name).unwrap(); - let database = LMDBWrapper::new(Arc::new(database)); - let node_B_peer_manager = make_peer_manager(vec![], database); - let node_B_connection_manager = factories::connection_manager::create() - .with_context(context.clone()) - .with_node_identity(node_B_identity.clone()) - .with_peer_manager(node_B_peer_manager.clone()) - .with_peer_connection_config(make_peer_connection_config(node_B_msg_sink_address)) - .build() - .map(Arc::new) - .unwrap(); - - // Start node B's control service - let node_B_control_service = ControlService::new(context.clone(), node_B_identity.clone(), ControlServiceConfig { - socks_proxy_address: None, - listener_address: node_B_control_port_address, - requested_connection_timeout: Duration::from_millis(2000), - }) - .serve(node_B_connection_manager) - .unwrap(); - - // We wait for the message to retry sending - node_B_msg_counter.assert_count(5, 150); - node_B_control_service.shutdown().unwrap(); - node_B_control_service - .timeout_join(Duration::from_millis(3000)) - .unwrap(); - omp.shutdown().unwrap(); - - clean_up_datastore(database_name); -} diff --git a/comms/tests/support/comms_patterns.rs b/comms/tests/support/comms_patterns.rs deleted file mode 100644 index 9ceb2b7ea6..0000000000 --- a/comms/tests/support/comms_patterns.rs +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use std::{ - sync::mpsc::{channel, Receiver}, - thread, -}; -use tari_comms::{ - connection::{ - types::{Direction, SocketType}, - zmq::{CurvePublicKey, CurveSecretKey, ZmqEndpoint}, - CurveEncryption, - ZmqContext, - }, - message::FrameSet, -}; - -/// Set the allocated stack size for each AsyncRequestReplyPattern thread -const THREAD_STACK_SIZE: usize = 32 * 1024; // 32kb - -/// Creates an [AsyncRequestReplyPattern]. -/// -/// [AsyncRequestReplyPattern]: struct.AsyncRequestReplyPattern.html -pub fn async_request_reply(direction: Direction) -> AsyncRequestReplyPattern -where T: ZmqEndpoint + Clone + Send + Sync + 'static { - AsyncRequestReplyPattern::new(direction) -} - -/// This pattern either sends a message and waits for a response or waits for -/// a response and sends a reply. -/// Once a response is received, the thread exits. This can be used to write functional -/// tests for request/reply flows. -pub struct AsyncRequestReplyPattern { - direction: Direction, - endpoint: Option, - identity: Option, - secret_key: Option, - server_public_key: Option, - frames: Option, -} - -impl AsyncRequestReplyPattern -where T: ZmqEndpoint + Clone + Send + Sync + 'static -{ - /// Create a new AsyncRequestReplyPattern - pub fn new(direction: Direction) -> Self { - AsyncRequestReplyPattern { - direction, - endpoint: None, - identity: None, - secret_key: None, - server_public_key: None, - frames: None, - } - } - - /// Set the endpoint to/from which data is sent and received - pub fn set_endpoint(mut self, v: T) -> Self { - self.endpoint = Some(v); - self - } - - /// Set the identity to use when sending data - pub fn set_identity(mut self, v: &str) -> Self { - self.identity = Some(v.to_string()); - self - } - - /// Set the secret key to use for encrypted connections. - pub fn set_secret_key(mut self, sk: CurveSecretKey) -> Self { - self.secret_key = Some(sk); - self - } - - /// Set the server public key to connect to a corresponding curve server. - pub fn set_server_public_key(mut self, spk: CurvePublicKey) -> Self { - self.server_public_key = Some(spk); - self - } - - /// Sets the data to send. - pub fn set_send_data(mut self, frames: FrameSet) -> Self { - self.frames = Some(frames); - self - } - - /// Start the thread and run the pattern! - pub fn run(self, ctx: ZmqContext) -> Receiver<()> { - let (tx, rx) = channel(); - let identity = self.identity.clone(); - let secret_key = self.secret_key.clone(); - let server_public_key = self.server_public_key.clone(); - let endpoint = self.endpoint.clone().unwrap(); - let msgs = self.frames.clone().unwrap(); - let direction = self.direction; - - thread::Builder::new() - .name("async-request-reply-pattern-thread".to_string()) - .stack_size(THREAD_STACK_SIZE) - .spawn(move || { - let socket = ctx.socket(SocketType::Dealer).unwrap(); - if let Some(i) = identity { - socket.set_identity(i.as_bytes()).unwrap(); - } - - socket.set_linger(100).unwrap(); - - match direction { - Direction::Inbound => { - if let Some(sk) = secret_key { - socket.set_curve_server(true).unwrap(); - socket.set_curve_secretkey(&sk.into_inner()).unwrap(); - } - socket.bind(endpoint.to_zmq_endpoint().as_str()).unwrap(); - - socket.recv_multipart(0).unwrap(); - - socket - .send_multipart(msgs.iter().map(|s| s.as_slice()).collect::>().as_slice(), 0) - .unwrap(); - - // Send thread done signal - tx.send(()).unwrap(); - }, - - Direction::Outbound => { - if let Some(spk) = server_public_key { - socket.set_curve_serverkey(&spk.into_inner()).unwrap(); - let keypair = CurveEncryption::generate_keypair().unwrap(); - socket.set_curve_publickey(&keypair.1.into_inner()).unwrap(); - socket.set_curve_secretkey(&keypair.0.into_inner()).unwrap(); - } - - socket.connect(endpoint.to_zmq_endpoint().as_str()).unwrap(); - socket - .send_multipart(msgs.iter().map(|s| s.as_slice()).collect::>().as_slice(), 0) - .unwrap(); - - socket.recv_multipart(0).unwrap(); - - // Send thread done signal - tx.send(()).unwrap(); - }, - } - }) - .unwrap(); - rx - } -} diff --git a/comms/tests/support/factories/connection_manager.rs b/comms/tests/support/factories/connection_manager.rs deleted file mode 100644 index 2f58a7f903..0000000000 --- a/comms/tests/support/factories/connection_manager.rs +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use std::sync::Arc; - -use super::{TestFactory, TestFactoryError}; - -use crate::support::factories::peer_manager::PeerManagerFactory; - -use crate::support::factories::node_identity::NodeIdentityFactory; -use tari_comms::{ - connection::ZmqContext, - connection_manager::{ConnectionManager, PeerConnectionConfig}, - peer_manager::{NodeIdentity, PeerManager}, -}; - -pub fn create() -> ConnectionManagerFactory { - ConnectionManagerFactory::default() -} - -#[derive(Default)] -pub struct ConnectionManagerFactory { - zmq_context: Option, - peer_connection_config: PeerConnectionConfig, - peer_manager: Option>, - peer_manager_factory: PeerManagerFactory, - node_identity_factory: NodeIdentityFactory, - node_identity: Option>, -} - -impl ConnectionManagerFactory { - factory_setter!( - with_peer_connection_config, - peer_connection_config, - PeerConnectionConfig - ); - - factory_setter!(with_peer_manager, peer_manager, Option>); - - factory_setter!(with_peer_manager_factory, peer_manager_factory, PeerManagerFactory); - - factory_setter!(with_context, zmq_context, Option); - - factory_setter!(with_node_identity, node_identity, Option>); - - factory_setter!(with_node_identity_factory, node_identity_factory, NodeIdentityFactory); -} - -impl TestFactory for ConnectionManagerFactory { - type Object = ConnectionManager; - - fn build(self) -> Result { - let zmq_context = self.zmq_context.unwrap_or(ZmqContext::new()); - - let peer_manager = match self.peer_manager { - Some(p) => p, - None => self.peer_manager_factory.build().map(Arc::new)?, - }; - - let node_identity = match self.node_identity { - Some(n) => n, - None => self.node_identity_factory.build().map(Arc::new)?, - }; - - let config = self.peer_connection_config; - - let conn_manager = ConnectionManager::new(zmq_context, node_identity, peer_manager, config); - - Ok(conn_manager) - } -} diff --git a/comms/tests/support/factories/peer_connection.rs b/comms/tests/support/factories/peer_connection.rs deleted file mode 100644 index 8eb4475761..0000000000 --- a/comms/tests/support/factories/peer_connection.rs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use super::{peer_connection_context::PeerConnectionContextFactory, TestFactory, TestFactoryError}; -use std::thread::JoinHandle; -use tari_comms::connection::{ConnectionError, PeerConnection}; - -pub fn create<'c>() -> PeerConnectionFactory<'c> { - PeerConnectionFactory::default() -} - -#[derive(Default)] -pub struct PeerConnectionFactory<'c> { - peer_connection_context_factory: PeerConnectionContextFactory<'c>, -} - -impl<'c> PeerConnectionFactory<'c> { - pub fn with_peer_connection_context_factory(mut self, context_factory: PeerConnectionContextFactory<'c>) -> Self { - self.peer_connection_context_factory = context_factory; - self - } -} - -impl<'c> TestFactory for PeerConnectionFactory<'c> { - type Object = (PeerConnection, JoinHandle>); - - fn build(self) -> Result { - let peer_conn_context = self - .peer_connection_context_factory - .build() - .map_err(TestFactoryError::build_failed())?; - - let mut conn = PeerConnection::new(); - let handle = conn - .start(peer_conn_context) - .map_err(TestFactoryError::build_failed())?; - - Ok((conn, handle)) - } -} diff --git a/comms/tests/support/factories/peer_connection_context.rs b/comms/tests/support/factories/peer_connection_context.rs deleted file mode 100644 index b7ce3a0fb0..0000000000 --- a/comms/tests/support/factories/peer_connection_context.rs +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use super::{TestFactory, TestFactoryError}; - -use rand::{OsRng, Rng}; -use tari_comms::connection::{ - peer_connection::{ConnectionId, PeerConnectionContext}, - types::Linger, - CurveEncryption, - CurvePublicKey, - CurveSecretKey, - Direction, - InprocAddress, - NetAddress, - PeerConnectionContextBuilder, - ZmqContext, -}; - -pub fn create<'c>() -> PeerConnectionContextFactory<'c> { - PeerConnectionContextFactory::default() -} - -#[derive(Default)] -pub struct PeerConnectionContextFactory<'c> { - direction: Option, - context: Option<&'c ZmqContext>, - connection_id: Option, - message_sink_address: Option, - server_public_key: Option, - curve_keypair: Option<(CurveSecretKey, CurvePublicKey)>, - address: Option, - linger: Option, -} - -fn random_connection_id() -> ConnectionId { - let rng = &mut OsRng::new().unwrap(); - (0..8).map(|_| rng.gen::()).collect::>().into() -} - -impl<'c> PeerConnectionContextFactory<'c> { - factory_setter!(with_direction, direction, Option); - - factory_setter!(with_connection_id, connection_id, Option); - - factory_setter!(with_message_sink_address, message_sink_address, Option); - - factory_setter!(with_server_public_key, server_public_key, Option); - - factory_setter!( - with_curve_keypair, - curve_keypair, - Option<(CurveSecretKey, CurvePublicKey)> - ); - - factory_setter!(with_address, address, Option); - - factory_setter!(with_linger, linger, Option); - - pub fn with_context(mut self, context: &'c ZmqContext) -> Self { - self.context = Some(context); - self - } -} - -impl<'c> TestFactory for PeerConnectionContextFactory<'c> { - type Object = PeerConnectionContext; - - fn build(self) -> Result { - let context = self.context.ok_or(TestFactoryError::BuildFailed( - "Context must be set for PeerConnectionContextFactory".into(), - ))?; - - let direction = self.direction.ok_or(TestFactoryError::BuildFailed( - "Must set direction on PeerConnectionContextFactory".into(), - ))?; - - let address = self.address.or(Some("127.0.0.1:0".parse().unwrap())).unwrap(); - - let mut builder = PeerConnectionContextBuilder::new() - .set_id(self.connection_id.clone().or(Some(random_connection_id())).unwrap()) - .set_linger(self.linger.or(Some(Linger::Indefinitely)).unwrap()) - .set_direction(direction.clone()) - .set_context(context) - .set_address(address) - .set_message_sink_address(self.message_sink_address.or(Some(InprocAddress::random())).unwrap()); - - let (secret_key, public_key) = self - .curve_keypair - .unwrap_or(CurveEncryption::generate_keypair().map_err(TestFactoryError::build_failed())?); - match direction { - Direction::Inbound => { - builder = builder.set_curve_encryption(CurveEncryption::Server { - secret_key: secret_key.clone(), - }); - }, - Direction::Outbound => { - let server_public_key = self.server_public_key.or(Some(public_key.clone())).unwrap(); - builder = builder.set_curve_encryption(CurveEncryption::Client { - secret_key: secret_key.clone(), - public_key: public_key.clone(), - server_public_key, - }); - }, - } - - let peer_context = builder.build().map_err(TestFactoryError::build_failed())?; - - Ok(peer_context) - } -} diff --git a/comms/tests/support/helpers/connection_message_counter.rs b/comms/tests/support/helpers/connection_message_counter.rs deleted file mode 100644 index 148a37b7dd..0000000000 --- a/comms/tests/support/helpers/connection_message_counter.rs +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use log::*; - -use tari_comms::connection::{zmq::ZmqEndpoint, Connection, Direction, ZmqContext}; - -use std::{ - sync::{Arc, RwLock}, - thread, - time::Duration, -}; -use tari_comms::message::FrameSet; - -const LOG_TARGET: &str = "comms::test_support::connection_message_counter"; - -/// Set the allocated stack size for each ConnectionMessageCounter thread -const THREAD_STACK_SIZE: usize = 64 * 1024; // 64kb - -pub struct ConnectionMessageCounter<'c> { - counter: Arc>, - context: &'c ZmqContext, - response: Option, -} - -impl<'c> ConnectionMessageCounter<'c> { - pub fn new(context: &'c ZmqContext) -> Self { - Self { - counter: Arc::new(RwLock::new(0)), - context, - response: None, - } - } - - pub fn set_response(&mut self, response: FrameSet) -> &mut Self { - self.response = Some(response); - self - } - - pub fn count(&self) -> u32 { - let counter_lock = acquire_read_lock!(self.counter); - *counter_lock - } - - pub fn assert_count(&self, count: u32, num_polls: usize) -> () { - for _i in 0..num_polls { - thread::sleep(Duration::from_millis(100)); - let curr_count = self.count(); - if curr_count == count { - return; - } - if curr_count > count { - panic!( - "Message count exceeded the expected count. Expected={} Actual={}", - count, curr_count - ); - } - } - panic!( - "Message count did not reach {} within {}ms. Count={}", - count, - num_polls * 100, - self.count() - ); - } - - pub fn start(&self, address: A) { - let counter = self.counter.clone(); - let context = self.context.clone(); - let address = address.clone(); - let response = self.response.clone(); - thread::Builder::new() - .name("connection-message-counter-thread".to_string()) - .stack_size(THREAD_STACK_SIZE) - .spawn(move || { - let connection = Connection::new(&context, Direction::Inbound) - .set_name("Message Counter") - .establish(&address) - .unwrap(); - - loop { - match connection.receive(1000) { - Ok(frames) => { - let mut counter_lock = acquire_write_lock!(counter); - *counter_lock += 1; - debug!(target: LOG_TARGET, "Added to message count (count={})", *counter_lock); - if let Some(ref r) = response { - let mut payload = vec![frames[0].clone()]; - payload.extend(r.clone()); - connection.send(payload).unwrap(); - } - }, - _ => { - debug!(target: LOG_TARGET, "Nothing received for 1 second..."); - }, - } - } - }) - .unwrap(); - } -} diff --git a/comms/tests/support/helpers/mod.rs b/comms/tests/support/helpers/mod.rs deleted file mode 100644 index cd2485c233..0000000000 --- a/comms/tests/support/helpers/mod.rs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -pub mod asserts; -pub mod connection_message_counter; -pub mod ports; - -pub use self::connection_message_counter::ConnectionMessageCounter; diff --git a/comms/tests/support/helpers/ports.rs b/comms/tests/support/helpers/ports.rs deleted file mode 100644 index 6618555b0f..0000000000 --- a/comms/tests/support/helpers/ports.rs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use std::{cmp, ops::Range, sync::Mutex}; - -const PORT_RANGE: Range = 40000..48000; - -lazy_static! { - /// Shared counter of ports which have been used - static ref PORT_COUNTER: Mutex = Mutex::new(PORT_RANGE.start); -} - -/// Maintains a counter of ports within a range (40000..48000), returning them in -/// sequence. Port numbers will wrap back to 40000 once the upper bound is exceeded. -pub fn get_next_local_port() -> u16 { - let mut lock = match PORT_COUNTER.lock() { - Ok(guard) => guard, - Err(_) => panic!("Poisoned PORT_COUNTER"), - }; - let port = { - *lock = cmp::max((*lock + 1) % PORT_RANGE.end, PORT_RANGE.start); - *lock - }; - port -} diff --git a/comms/tests/support/makers/comms_keys.rs b/comms/tests/support/makers/comms_keys.rs deleted file mode 100644 index b589bc598b..0000000000 --- a/comms/tests/support/makers/comms_keys.rs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use rand::OsRng; -use tari_crypto::{ - keys::PublicKey, - ristretto::{RistrettoPublicKey, RistrettoSecretKey}, -}; - -/// Creates a random keypair using OsRng -#[inline] -pub fn make_random_keypair() -> (RistrettoSecretKey, RistrettoPublicKey) { - RistrettoPublicKey::random_keypair(&mut OsRng::new().unwrap()) -} diff --git a/comms/tests/support/makers/mod.rs b/comms/tests/support/makers/mod.rs deleted file mode 100644 index 1f8e15d1e9..0000000000 --- a/comms/tests/support/makers/mod.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -pub mod comms_keys; -pub mod node_id; diff --git a/comms/tests/support/makers/node_id.rs b/comms/tests/support/makers/node_id.rs deleted file mode 100644 index 487bc89c1b..0000000000 --- a/comms/tests/support/makers/node_id.rs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use rand::{CryptoRng, OsRng, Rng}; -use tari_comms::peer_manager::NodeId; -use tari_crypto::{keys::PublicKey, ristretto::RistrettoPublicKey}; - -/// Creates a random node ID witht he given RNG -pub fn make_random_node_id_with_rng(rng: &mut RNG) -> NodeId { - let (_sk, pk) = RistrettoPublicKey::random_keypair(rng); - NodeId::from_key(&pk).unwrap() -} - -/// Creates a random node ID using OsRng -pub fn make_node_id() -> NodeId { - make_random_node_id_with_rng(&mut OsRng::new().unwrap()) -} diff --git a/comms/tests/support/mod.rs b/comms/tests/support/mod.rs deleted file mode 100644 index b6831c0ae0..0000000000 --- a/comms/tests/support/mod.rs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#[macro_use] -mod macros; - -pub mod comms_patterns; - -pub mod factories; -pub mod helpers; -pub mod makers; diff --git a/config/tari_config_sample.toml b/config/tari_config_sample.toml index d1e7fdb498..63c27527a8 100644 --- a/config/tari_config_sample.toml +++ b/config/tari_config_sample.toml @@ -57,6 +57,12 @@ # b) know what you are doing! #wallet_file = "~/.tari/wallet/wallet.dat" +#[base_node.transport.tor] +#control_address = "/ip4/127.0.0.1/tcp/9051" +#control_auth_type = "none" # or "password" +# Required for control_auth_type = "password" +#control_auth_password = "super-secure-password" + ######################################################################################################################## # # # Base Node Configuration Options # @@ -74,6 +80,7 @@ # testnet - the Tari test net #network = "mainnet" + # Configuration options for testnet [base_node.testnet] # The type of database backend to use. Currently supported options are "memory" and "lmdb". LMDB is recommnded for @@ -99,9 +106,11 @@ peer_seeds = [] # n - blocking_threads, where n is the number of cores on your machine, and blocking_thread is set above. #core_threads = 4 -# The address and port to listen for peer connections. This is the address that is advertised on the network so that +# The node's publicly-accessible hostname. This is the host name that is advertised on the network so that # peers can find you. -#address = "/ip4/172.2.3.4/tcp/18189" +# _NOTE_: If using the `tor` transport type, public_address will be ignored and an onion address will be +# automatically configured +#public_address = "/ip4/172.2.3.4/tcp/18189" # Enable the gRPC server for the base node. Set this to true if you want to enable third-party wallet software #grpc_enabled = false @@ -113,6 +122,36 @@ peer_seeds = [] # A path to the file that stores your node identity and secret key #identity_file = "~/.tari/testnet/node_id.json" +# -------------- Transport configuration -------------- +# Use TCP to connect to the Tari network. This transport can only communicate with TCP/IP addresses, so peers with +# e.g. tor onion addresses will not be contactable. +#transport = "tcp" +# # The address and port to listen for peer connections over TCP. +#tcp_listener_address = "/ip4/0.0.0.0/tcp/18189" + +# Configures the node to run over a tor hidden service using the Tor proxy. This transport recognises ip/tcp, +# onion v2, onion v3 and dns addresses. +#transport = "tor" +# Address of the tor control server +#tor_control_address = "/ip4/127.0.0.1/tcp/9051" +# Authentication to use for the tor control server +#tor_control_auth = "none" # or "password=xxxxxx" +# The onion port to use. +#tor_onion_port = 18141 +# The address to which traffic on the node's onion address will be forwarded +#tor_forward_address = "/ip4/127.0.0.1/tcp/18141" + +# Use a SOCKS5 proxy transport. This transport recognises any addresses supported by the proxy. +#transport = "socks5" +# The address of the SOCKS5 proxy +#socks5_proxy_address = "/ip4/127.0.0.1/tcp/9050" +# The address to which traffic will be forwarded +#socks5_listener_address = "/ip4/127.0.0.1/tcp/18189" +#socks5_auth = "none" # or "username_password=username:xxxxxxx" + +# A path to the file that stores the tor hidden service private key, if using the tor transport. +# tor_identity_file = "~/.tari/testnet/tor.key" + [base_node.mainnet] # The type of database backend to use. Currently supported options are "memory" and "lmdb". LMDB is recommnded for # almost all use cases. @@ -136,10 +175,6 @@ peer_seeds = [] # n - blocking_threads, where n is the number of cores on your machine, and blocking_thread is set above. #core_threads = 6 -# The address and port to listen for peer connections. This is the address that is advertised on the network so that -# peers can find you. You may specify more than one address here -#address = "tcp://0.0.0.0:18089" - # Enable the gRPC server for the base node. Set this to true if you want to enable third-party wallet software #grpc_enabled = false @@ -150,6 +185,139 @@ peer_seeds = [] # A path to the file that stores your node identity and secret key #identity_file = "~/.tari/mainnet/node_id.json" +# -------------- Transport configuration -------------- +# Use TCP to connect to the Tari network. This transport can only communicate with TCP/IP addresses, so peers with +# e.g. tor onion addresses will not be contactable. +#transport = "tcp" +# # The address and port to listen for peer connections over TCP. +#tcp_listener_address = "/ip4/0.0.0.0/tcp/18189" + +# Configures the node to run over a tor hidden service using the Tor proxy. This transport recognises ip/tcp, +# onion v2, onion v3 and dns addresses. +#transport = "tor" +# Address of the tor control server +#tor_control_address = "/ip4/127.0.0.1/tcp/9051" +# Authentication to use for the tor control server +#tor_control_auth = "none" # or "password=xxxxxx" +# The onion port to use. +#tor_onion_port = 18141 +# The address to which traffic on the node's onion address will be forwarded +#tor_forward_address = "/ip4/127.0.0.1/tcp/18141" + +# Use a SOCKS5 proxy transport. This transport recognises any addresses supported by the proxy. +#transport = "socks5" +# The address of the SOCKS5 proxy +#socks5_proxy_address = "/ip4/127.0.0.1/tcp/9050" +# The address to which traffic will be forwarded +#socks5_listener_address = "/ip4/127.0.0.1/tcp/18189" +#socks5_auth = "none" # or "username_password=username:xxxxxxx" + +# A path to the file that stores the tor hidden service private key, if using the tor transport +# tor_identity_file = "~/.tari/mainnet/tor.key" + +######################################################################################################################## +# # +# Mempool Configuration Options # +# # +######################################################################################################################## +[mempool.testnet] + +# The maximum period the mempool will wait for responses to requests made to base nodes [default: 60 seconds]. +# request_timeout = 60 + +# The maximum number of transactions that can be stored in the Unconfirmed Transaction pool. This is the main waiting +# area in the mempool and almost all transactions will end up in this pool before being mined. It's for this reason +# that this parameter will have the greatest impact on actual memory usage by your mempool. If you are not mining, +# you can reduce this parameter to reduce memory consumption by your node, at the expense of network bandwith. For +# reference, a single block can hold about 4,000 transactions +# Default = 40,000 transactions +# unconfirmed_pool_storage_capacity = 40000 + +# The maximum number of transactions that can be stored in the Orphan Transaction pool. This pool keep transactions +# that are 'orphans', i.e. transactions with inputs that don't exist in the UTXO set. If you're not mining, and +# memory usage is a concern, this can safely be set to zero. Even so, orphan transactions do not appear that often +# (it's usually a short chain of spends that are broadcast in quick succession). The other potential source of orphan +# transactions are from DOS attacks and setting the `tx_ttl` parameter to a low value is an effective countermeasure +# in this case. Default: 250 transactions +# orphan_pool_storage_capacity = 250 + +# The maximum amount of time an orphan transaction will be permitted to stay in the mempool before being rejected. +# This should be set to a fairly long enough to allow the parent transaction to arrive; but low enough also to thwart +# DOS attacks. Default: 300 seconds +#orphan_tx_ttl = 300 + +# The maximum number of transactions that can be stored in the Pending Transaction pool. This pool holds transactions +# that are valid, but cannot be included in a block yet becuase there is a consensus rule holding it back, usually a +# time lock. Once the conditions holding the transaction in the pending pool are resolved, the transaction will move +# into the unconfirmed pool. Default: 5,000 transactions +# pending_pool_storage_capacity = 5000 + +# The ReorgPool consists of all transactions that have recently been added to blocks. +# When a potential blockchain reorganization occurs the transactions can be recovered from the ReorgPool and can be +# added back into the UnconfirmedPool. Transactions in the ReOrg pool have a limited Time-to-live and will be removed +# from the pool when the Time-to-live thresholds is reached. Also, when the capacity of the pool has been reached, the +# oldest transactions will be removed to make space for incoming transactions. The pool capacity and TTL parameters +# have the same meaning as those for the pending pool, but applied to the re-org pool; obviously. +# Defaults: 10,000 transactions and 300 seconds +#reorg_pool_storage_capacity = 10_000 +#reorg_tx_ttl = 300 + +# The maximum number of transactions that can be skipped when compiling a set of highest priority transactions, +# skipping over large transactions are performed in an attempt to fit more transactions into the remaining space. +# This parameter only affects mining nodes. You can ignore it if you are only running a base node. Even so, changing +# this parameter should not affect profitabilty in any meaningful way, since the transaction weights are selected to +# closely mirror how much block space they take up +#weight_tx_skip_count = 20 + +[mempool.mainnet] + +# The maximum period the mempool will wait for responses to requests made to base nodes [default: 60 seconds]. +# request_timeout = 60 + +# The maximum number of transactions that can be stored in the Unconfirmed Transaction pool. This is the main waiting +# area in the mempool and almost all transactions will end up in this pool before being mined. It's for this reason +# that this parameter will have the greatest impact on actual memory usage by your mempool. If you are not mining, +# you can reduce this parameter to reduce memory consumption by your node, at the expense of network bandwith. For +# reference, a single block can hold about 4,000 transactions +# Default = 40,000 transactions +# unconfirmed_pool_storage_capacity = 40000 + +# The maximum number of transactions that can be stored in the Orphan Transaction pool. This pool keep transactions +# that are 'orphans', i.e. transactions with inputs that don't exist in the UTXO set. If you're not mining, and +# memory usage is a concern, this can safely be set to zero. Even so, orphan transactions do not appear that often +# (it's usually a short chain of spends that are broadcast in quick succession). The other potential source of orphan +# transactions are from DOS attacks and setting the `tx_ttl` parameter to a low value is an effective countermeasure +# in this case. Default: 250 transactions +# orphan_pool_storage_capacity = 250 + +# The maximum amount of time an orphan transaction will be permitted to stay in the mempool before being rejected. +# This should be set to a fairly long enough to allow the parent transaction to arrive; but low enough also to thwart +# DOS attacks. Default: 300 seconds +#orphan_tx_ttl = 300 + +# The maximum number of transactions that can be stored in the Pending Transaction pool. This pool holds transactions +# that are valid, but cannot be included in a block yet becuase there is a consensus rule holding it back, usually a +# time lock. Once the conditions holding the transaction in the pending pool are resolved, the transaction will move +# into the unconfirmed pool. Default: 5,000 transactions +# pending_pool_storage_capacity = 5000 + +# The ReorgPool consists of all transactions that have recently been added to blocks. +# When a potential blockchain reorganization occurs the transactions can be recovered from the ReorgPool and can be +# added back into the UnconfirmedPool. Transactions in the ReOrg pool have a limited Time-to-live and will be removed +# from the pool when the Time-to-live thresholds is reached. Also, when the capacity of the pool has been reached, the +# oldest transactions will be removed to make space for incoming transactions. The pool capacity and TTL parameters +# have the same meaning as those for the pending pool, but applied to the re-org pool; obviously. +# Defaults: 10,000 transactions and 300 seconds +#reorg_pool_storage_capacity = 10_000 +#reorg_tx_ttl = 300 + +# The maximum number of transactions that can be skipped when compiling a set of highest priority transactions, +# skipping over large transactions are performed in an attempt to fit more transactions into the remaining space. +# This parameter only affects mining nodes. You can ignore it if you are only running a base node. Even so, changing +# this parameter should not affect profitabilty in any meaningful way, since the transaction weights are selected to +# closely mirror how much block space they take up +#weight_tx_skip_count = 20 + ######################################################################################################################## # # # Validator Node Configuration Options # diff --git a/digital_assets_layer/core/Cargo.toml b/digital_assets_layer/core/Cargo.toml deleted file mode 100644 index 7da1ddc221..0000000000 --- a/digital_assets_layer/core/Cargo.toml +++ /dev/null @@ -1,5 +0,0 @@ -[package] -name = "da_core" -version = "0.0.2" - -[dependencies] diff --git a/digital_assets_layer/core/src/lib.rs b/digital_assets_layer/core/src/lib.rs deleted file mode 100644 index 8b13789179..0000000000 --- a/digital_assets_layer/core/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/doc/build.md b/doc/build.md deleted file mode 100644 index 3ca1bec7fd..0000000000 --- a/doc/build.md +++ /dev/null @@ -1,26 +0,0 @@ -# Build Instructions and Notes - -## Prerequisites - -Install ZeroMQ - -### Mac OS -```brew install pkg-config zmq``` - -### Debian, Ubuntu, Fedora, CentOS, RHEL, SUSE - -``` -echo "deb http://download.opensuse.org/repositories/network:/messaging:/zeromq:/release-stable/Debian_9.0/ ./" >> /etc/apt/sources.list -wget https://download.opensuse.org/repositories/network:/messaging:/zeromq:/release-stable/Debian_9.0/Release.key -O- | sudo apt-key add -apt-get install libzmq3-dev -``` - -### Other platforms -See [ZeroMQ documentation](http://zeromq.org/intro:get-the-software) - -## Build - -Make sure you are running nightly version: `nightly-2019-07-15` - -To build run -`cargo build` diff --git a/infrastructure/broadcast_channel/.gitignore b/infrastructure/broadcast_channel/.gitignore deleted file mode 100644 index f1df63c864..0000000000 --- a/infrastructure/broadcast_channel/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -/target -**/*.rs.bk -.idea/ -Cargo.lock -*.iml diff --git a/infrastructure/broadcast_channel/Cargo.toml b/infrastructure/broadcast_channel/Cargo.toml deleted file mode 100644 index 637e5204f3..0000000000 --- a/infrastructure/broadcast_channel/Cargo.toml +++ /dev/null @@ -1,32 +0,0 @@ -[package] -name = "tari_broadcast_channel" -version = "0.0.5" -authors = ["Filip Dulic ", "Vladan Popovic ", "Bojan Petrovic ", "The Tari Development Community"] -description = "Bounded non-blocking single-producer-multi-consumer broadcast channel" -license = "Apache-2.0/MIT" -keywords = ["pubsub", "lock-free", "queue","async","futures"] -repository = "https://github.com/tari-project/tari" -homepage = "https://tari.com" -readme = "README.md" -edition = "2018" - -[dependencies] -arc-swap = "0.4.2" -futures = { version = "=0.3.0-alpha.19", package = "futures-preview" } -crossbeam-channel = "0.3.9" - -[dev-dependencies] -criterion = "0.3.0" -tokio = "0.2.0-alpha.6" - -[[example]] -name = "raw-simple" -path = "examples/raw-simple.rs" - -[[example]] -name = "async-simple" -path = "examples/async-simple.rs" - -[[bench]] -name = "benches_main" -harness = false diff --git a/infrastructure/broadcast_channel/README.md b/infrastructure/broadcast_channel/README.md deleted file mode 100644 index 017d63e979..0000000000 --- a/infrastructure/broadcast_channel/README.md +++ /dev/null @@ -1,47 +0,0 @@ -# Bounded Non-Blocking Single-Producer, Multi-Consumer Broadcast Channel - -Parts of this code were forked from https://github.com/filipdulic/bus-queue. - -# Examples -## Simple bare usage -```rust -use tari_bus::bare_channel; - -fn main() { - let (tx, rx) = bare_channel(10); - (1..15).for_each(|x| tx.broadcast(x).unwrap()); - - let received: Vec = rx.map(|x| *x).collect(); - // Test that only the last 10 elements are in the received list. - let expected: Vec = (5..15).collect(); - - assert_eq!(expected, received); -} -``` - -```rust -use tari_bus::bounded; -use futures::executor::block_on; -use futures::stream; -use futures::StreamExt; - -fn main() { - let (publisher, subscriber1) = bounded(10); - let subscriber2 = subscriber1.clone(); - - block_on(async move { - stream::iter(1..15) - .map(|i| Ok(i)) - .forward(publisher) - .await - .unwrap(); - }); - - let received1: Vec = block_on(async { subscriber1.map(|x| *x).collect().await }); - let received2: Vec = block_on(async { subscriber2.map(|x| *x).collect().await }); - // Test that only the last 10 elements are in the received list. - let expected = (5..15).collect::>(); - assert_eq!(received1, expected); - assert_eq!(received2, expected); -} -``` diff --git a/infrastructure/broadcast_channel/benches/channel.rs b/infrastructure/broadcast_channel/benches/channel.rs deleted file mode 100644 index f9115031d0..0000000000 --- a/infrastructure/broadcast_channel/benches/channel.rs +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2019, The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use criterion::{criterion_group, Criterion}; -use futures::{future::join_all, SinkExt, StreamExt}; -use std::{iter::repeat, time::Duration}; -use tari_broadcast_channel::{bounded, raw_bounded}; -use tokio::runtime::Runtime; - -/// Raw broadcast channel benchmark -/// -/// NUM_SUBSCRIBERS receiving NUM_MESSAGES messages containing ITEM_SIZE bytes -fn bench_raw_channel(c: &mut Criterion) { - const ITEM_SIZE: usize = 1024; - const NUM_SUBSCRIBERS: usize = 1000; - const BUF_SIZE: usize = 1000; - const NUM_MESSAGES: usize = 1000; - - c.bench_function("raw channel", move |b| { - let (tx, rx) = raw_bounded(BUF_SIZE); - - let mut subscribers = repeat(&rx).cloned().take(NUM_SUBSCRIBERS).collect::>(); - - let mut sub_read = subscribers.iter_mut().map(|s| s.next().unwrap()); - - let mut counter = 0; - b.iter_with_setup( - || { - (0..) - .map(|v| repeat(v).take(ITEM_SIZE).collect::>()) - .take(NUM_MESSAGES) - .for_each(|v| { - tx.broadcast(v).unwrap(); - }); - }, - |_| { - assert!(sub_read.all(|v| (*v)[0] == counter)); - counter += 1; - }, - ); - }); -} - -/// Async broadcast channel benchmark -/// -/// NUM_SUBSCRIBERS receiving NUM_MESSAGES messages containing ITEM_SIZE bytes -fn bench_async_channel(c: &mut Criterion) { - const NUM_SUBSCRIBERS: usize = 1000; - const BUF_SIZE: usize = 50; - const NUM_MESSAGES: usize = 50; - - c.bench_function("async channel", move |b| { - let rt = Runtime::new().unwrap(); - let (mut publisher, subscriber) = bounded(BUF_SIZE); - - let mut subscribers = repeat(&subscriber).cloned().take(NUM_SUBSCRIBERS).collect::>(); - - b.iter_with_setup( - || { - (0..NUM_MESSAGES).for_each(|v| { - rt.block_on(publisher.send(v)).unwrap(); - }); - }, - |_| { - (0..NUM_MESSAGES).for_each(|i| { - let sub_read_all = subscribers.iter_mut().map(|s| s.next()); - let values = rt.block_on(join_all(sub_read_all)); - assert_eq!(values.len(), NUM_SUBSCRIBERS); - assert!(values.iter().all(|v| **(v.as_ref().unwrap()) == i)); - }) - }, - ); - }); -} - -criterion_group!( - name = benches; - config = Criterion::default() - .warm_up_time(Duration::from_millis(500)); - targets = bench_raw_channel, bench_async_channel -); diff --git a/infrastructure/broadcast_channel/examples/async-simple.rs b/infrastructure/broadcast_channel/examples/async-simple.rs deleted file mode 100644 index a736375a55..0000000000 --- a/infrastructure/broadcast_channel/examples/async-simple.rs +++ /dev/null @@ -1,18 +0,0 @@ -use futures::{executor::block_on, stream, StreamExt}; -use tari_broadcast_channel::bounded; - -fn main() { - let (publisher, subscriber1) = bounded(10); - let subscriber2 = subscriber1.clone(); - - block_on(async move { - stream::iter(1..15).map(Ok).forward(publisher).await.unwrap(); - }); - - let received1: Vec = block_on(async { subscriber1.map(|x| *x).collect().await }); - let received2: Vec = block_on(async { subscriber2.map(|x| *x).collect().await }); - // Test that only the last 10 elements are in the received list. - let expected = (5..15).collect::>(); - assert_eq!(received1, expected); - assert_eq!(received2, expected); -} diff --git a/infrastructure/broadcast_channel/examples/raw-simple.rs b/infrastructure/broadcast_channel/examples/raw-simple.rs deleted file mode 100644 index 661c59207a..0000000000 --- a/infrastructure/broadcast_channel/examples/raw-simple.rs +++ /dev/null @@ -1,12 +0,0 @@ -use tari_broadcast_channel::raw_bounded; - -fn main() { - let (tx, rx) = raw_bounded(10); - (1..15).for_each(|x| tx.broadcast(x).unwrap()); - - let received: Vec = rx.map(|x| *x).collect(); - // Test that only the last 10 elements are in the received list. - let expected: Vec = (5..15).collect(); - - assert_eq!(expected, received); -} diff --git a/infrastructure/broadcast_channel/src/async_channel.rs b/infrastructure/broadcast_channel/src/async_channel.rs deleted file mode 100644 index 4eb961f126..0000000000 --- a/infrastructure/broadcast_channel/src/async_channel.rs +++ /dev/null @@ -1,176 +0,0 @@ -use crate::channel::{bounded as raw_bounded, Receiver, SendError, Sender, TryRecvError}; -use crossbeam_channel as mpsc; -use futures::{ - task::{self, AtomicWaker}, - Poll, - Sink, - Stream, -}; -use std::{pin::Pin, sync::Arc}; - -pub fn bounded(size: usize) -> (Publisher, Subscriber) { - let (sender, receiver) = raw_bounded(size); - let (waker, sleeper) = alarm(); - (Publisher { sender, waker }, Subscriber { receiver, sleeper }) -} - -#[derive(Debug)] -pub struct Publisher { - sender: Sender, - waker: Waker, -} - -impl Sink for Publisher { - type Error = SendError; - - fn poll_ready(self: Pin<&mut Self>, _: &mut task::Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn start_send(mut self: Pin<&mut Self>, item: T) -> Result<(), Self::Error> { - self.waker.collect_new_wakers(); - self.sender.broadcast(item).and_then(|_| { - self.waker.wake_all(); - Ok(()) - }) - } - - fn poll_flush(self: Pin<&mut Self>, _: &mut task::Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn poll_close(self: Pin<&mut Self>, _: &mut task::Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } -} - -impl PartialEq for Publisher { - fn eq(&self, other: &Publisher) -> bool { - self.sender == other.sender - } -} - -impl Eq for Publisher {} - -pub struct Subscriber { - receiver: Receiver, - sleeper: Sleeper, -} - -impl Stream for Subscriber { - type Item = Arc; - - fn poll_next(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll> { - self.sleeper.register(cx.waker()); - match self.receiver.try_recv() { - Ok(item) => Poll::Ready(Some(item)), - Err(error) => match error { - TryRecvError::Empty => Poll::Pending, - TryRecvError::Disconnected => Poll::Ready(None), - }, - } - } -} - -impl Clone for Subscriber { - fn clone(&self) -> Self { - Self { - receiver: self.receiver.clone(), - sleeper: self.sleeper.clone(), - } - } -} - -impl PartialEq for Subscriber { - fn eq(&self, other: &Subscriber) -> bool { - self.receiver == other.receiver - } -} - -impl Eq for Subscriber {} - -// Helper struct used by sync and async implementations to wake Tasks / Threads -#[derive(Debug)] -pub struct Waker { - /// Vector of Wakers to use to wake up subscribers. - wakers: Vec>, - /// A mpsc Receiver used to receive Wakers - receiver: mpsc::Receiver>, -} - -impl Waker { - fn wake_all(&self) { - for waker in &self.wakers { - waker.wake(); - } - } - - /// Receive any new Wakers and add them to the wakers Vec. These will be used to wake up the - /// subscribers when a message is published - fn collect_new_wakers(&mut self) { - while let Ok(receiver) = self.receiver.try_recv() { - self.wakers.push(receiver); - } - } -} - -/// Helper struct used by sync and async implementations to register Tasks / Threads to -/// be woken up. -#[derive(Debug)] -pub struct Sleeper { - /// Current Waker to be woken up - waker: Arc, - /// mpsc Sender used to send Wakers to the Publisher - sender: mpsc::Sender>, -} - -impl Sleeper { - fn register(&self, waker: &task::Waker) { - self.waker.register(waker); - } -} - -impl Clone for Sleeper { - fn clone(&self) -> Self { - let waker = Arc::new(AtomicWaker::new()); - // Send the new waker to the publisher. - // If this fails (Receiver disconnected), presumably the Publisher - // has dropped and when this is polled for the first time, the - // Stream will end. - let _ = self.sender.send(Arc::clone(&waker)); - Self { - waker, - sender: self.sender.clone(), - } - } -} - -/// Function used to create a ( Waker, Sleeper ) tuple. -pub fn alarm() -> (Waker, Sleeper) { - let (sender, receiver) = mpsc::unbounded(); - let waker = Arc::new(AtomicWaker::new()); - let wakers = vec![Arc::clone(&waker)]; - (Waker { wakers, receiver }, Sleeper { waker, sender }) -} - -#[cfg(test)] -mod test { - use futures::{executor::block_on, stream, StreamExt}; - - #[test] - fn channel() { - let (publisher, subscriber1) = super::bounded(10); - let subscriber2 = subscriber1.clone(); - - block_on(async move { - stream::iter(1..15).map(|i| Ok(i)).forward(publisher).await.unwrap(); - }); - - let received1: Vec = block_on(async { subscriber1.map(|x| *x).collect().await }); - let received2: Vec = block_on(async { subscriber2.map(|x| *x).collect().await }); - // Test that only the last 10 elements are in the received list. - let expected = (5..15).collect::>(); - assert_eq!(received1, expected); - assert_eq!(received2, expected); - } -} diff --git a/infrastructure/broadcast_channel/src/atomic_counter.rs b/infrastructure/broadcast_channel/src/atomic_counter.rs deleted file mode 100644 index fec545bfa5..0000000000 --- a/infrastructure/broadcast_channel/src/atomic_counter.rs +++ /dev/null @@ -1,50 +0,0 @@ -use std::{ - fmt, - sync::atomic::{AtomicUsize, Ordering}, -}; - -pub struct AtomicCounter { - count: AtomicUsize, -} - -impl AtomicCounter { - pub fn new(c: usize) -> Self { - AtomicCounter { - count: AtomicUsize::new(c), - } - } - - #[inline] - pub fn get(&self) -> usize { - self.count.load(Ordering::Acquire) - } - - #[inline] - pub fn set(&self, val: usize) { - self.count.store(val, Ordering::Release); - } - - #[inline] - pub fn inc(&self) { - self.count.fetch_add(1, Ordering::AcqRel); - } - - #[inline] - pub fn dec(&self) { - self.count.fetch_sub(1, Ordering::AcqRel); - } -} - -impl fmt::Debug for AtomicCounter { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "AtomicCounter: {}", self.get()) - } -} - -impl PartialEq for AtomicCounter { - fn eq(&self, other: &AtomicCounter) -> bool { - self.count.load(Ordering::Acquire) == other.count.load(Ordering::Acquire) - } -} - -impl Eq for AtomicCounter {} diff --git a/infrastructure/broadcast_channel/src/channel.rs b/infrastructure/broadcast_channel/src/channel.rs deleted file mode 100644 index 9d33851662..0000000000 --- a/infrastructure/broadcast_channel/src/channel.rs +++ /dev/null @@ -1,286 +0,0 @@ -use crate::atomic_counter::AtomicCounter; -use arc_swap::ArcSwapOption; -use std::{ - fmt::Debug, - iter::Iterator, - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, - }, -}; - -// Use std mpsc's error types as our own -pub use std::sync::mpsc::{RecvError, RecvTimeoutError, SendError, TryRecvError}; - -/// Function used to create and initialise a (Sender, Receiver) tuple. -pub fn bounded(size: usize) -> (Sender, Receiver) { - let mut buffer = Vec::new(); - buffer.resize(size, ArcSwapOption::new(None)); - let buffer = Arc::new(buffer); - let wi = Arc::new(AtomicCounter::new(0)); - - let sub_count = Arc::new(AtomicCounter::new(1)); - let is_sender_available = Arc::new(AtomicBool::new(true)); - - ( - Sender { - buffer: buffer.clone(), - size, - wi: wi.clone(), - sub_count: sub_count.clone(), - is_available: is_sender_available.clone(), - }, - Receiver { - buffer, - size, - wi, - ri: AtomicCounter::new(0), - sub_count, - is_sender_available, - }, - ) -} - -/// Bare implementation of the publisher. -#[derive(Debug, Clone)] -pub struct Sender { - /// Shared reference to the circular buffer - buffer: Arc>>, - /// Size of the buffer - size: usize, - /// Write index pointer - wi: Arc, - /// Number of subscribers - sub_count: Arc, - /// true if this sender is still available - is_available: Arc, -} - -/// Bare implementation of the subscriber. -#[derive(Debug)] -pub struct Receiver { - /// Shared reference to the circular buffer - buffer: Arc>>, - /// Write index pointer - wi: Arc, - /// Read index pointer - ri: AtomicCounter, - /// This size of the buffer - size: usize, - /// Number of subscribers - sub_count: Arc, - /// true if the sender is available - is_sender_available: Arc, -} - -impl Sender { - /// Publishes values to the circular buffer at wi % size - /// - /// # Arguments - /// * `object` - owned object to be published - pub fn broadcast(&self, object: T) -> Result<(), SendError> { - if self.sub_count.get() == 0 { - return Err(SendError(object)); - } - self.buffer[self.wi.get() % self.size].store(Some(Arc::new(object))); - self.wi.inc(); - Ok(()) - } -} - -/// Drop trait is used to let subscribers know that publisher is no longer available. -impl Drop for Sender { - fn drop(&mut self) { - self.is_available.store(false, Ordering::Relaxed); - } -} - -impl PartialEq for Sender { - fn eq(&self, other: &Sender) -> bool { - Arc::ptr_eq(&self.buffer, &other.buffer) - } -} - -impl Eq for Sender {} - -impl Receiver { - /// Returns true if the sender is available, otherwise false - fn is_sender_available(&self) -> bool { - self.is_sender_available.load(Ordering::Relaxed) - } - - /// Receives some atomic reference to an object if queue is not empty, or None if it is. Never - /// Blocks - pub fn try_recv(&self) -> Result, TryRecvError> { - if self.ri.get() == self.wi.get() { - if self.is_sender_available() { - return Err(TryRecvError::Empty); - } else { - return Err(TryRecvError::Disconnected); - } - } - - // Reader has not read enough to keep up with (writer - buffer size) so - // set the reader pointer to be (writer - buffer size) - if self.wi.get() - self.ri.get() >= self.size { - self.ri.set(self.wi.get() - self.size); - } - let val = self.buffer[self.ri.get() % self.size].load_full().unwrap(); - self.ri.inc(); - Ok(val) - } -} - -/// Clone trait is used to create a Receiver which receives messages from the same Sender -impl Clone for Receiver { - fn clone(&self) -> Self { - self.sub_count.inc(); - Self { - buffer: self.buffer.clone(), - wi: self.wi.clone(), - ri: AtomicCounter::new(self.ri.get()), - size: self.size, - sub_count: Arc::clone(&self.sub_count), - is_sender_available: self.is_sender_available.clone(), - } - } -} - -impl Drop for Receiver { - fn drop(&mut self) { - self.sub_count.dec(); - } -} - -impl PartialEq for Receiver { - fn eq(&self, other: &Receiver) -> bool { - Arc::ptr_eq(&self.buffer, &other.buffer) && self.ri == other.ri - } -} - -impl Eq for Receiver {} - -impl Iterator for Receiver { - type Item = Arc; - - fn next(&mut self) -> Option { - self.try_recv().ok() - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn subcount() { - let (sender, receiver) = bounded::<()>(1); - let receiver2 = receiver.clone(); - assert_eq!(sender.sub_count.get(), 2); - assert_eq!(receiver.sub_count.get(), 2); - assert_eq!(receiver2.sub_count.get(), 2); - drop(receiver2); - - assert_eq!(sender.sub_count.get(), 1); - assert_eq!(receiver.sub_count.get(), 1); - } - - #[test] - fn eq() { - let (_sender, receiver) = bounded::<()>(1); - let receiver2 = receiver.clone(); - assert_eq!(receiver, receiver2); - } - - #[test] - fn bounded_channel() { - let (sender, receiver) = bounded(1); - let receiver2 = receiver.clone(); - sender.broadcast(123).unwrap(); - assert_eq!(*receiver.try_recv().unwrap(), 123); - assert_eq!(*receiver2.try_recv().unwrap(), 123); - } - - #[test] - fn bounded_channel_no_subs() { - let (sender, receiver) = bounded(1); - let rx2 = receiver.clone(); - drop(receiver); - assert!(sender.broadcast(123).is_ok()); - drop(rx2); - let err = sender.broadcast(123); - assert!(err.is_err()); - } - - #[test] - fn bounded_channel_no_sender() { - let (sender, receiver) = bounded::<()>(1); - drop(sender); - assert_eq!(receiver.is_sender_available(), false); - } - - #[test] - fn bounded_channel_size() { - let (sender, receiver) = bounded::<()>(3); - assert_eq!(sender.buffer.len(), 3); - assert_eq!(receiver.buffer.len(), 3); - } - - #[test] - fn bounded_within_size() { - let (sender, receiver) = bounded(3); - assert_eq!(sender.buffer.len(), 3); - - for i in 0..3 { - sender.broadcast(i).unwrap(); - } - - let values = receiver.into_iter().map(|v| *v).collect::>(); - assert_eq!(values, (0..=2).collect::>()); - } - - #[test] - fn bounded_overflow() { - let (sender, receiver) = bounded(3); - assert_eq!(sender.buffer.len(), 3); - - for i in 0..4 { - sender.broadcast(i).unwrap(); - } - - let values = receiver.into_iter().map(|v| *v).collect::>(); - assert_eq!(values, (1..=3).collect::>()); - } - - #[test] - fn bounded_overflow_with_reads() { - let (sender, receiver) = bounded(3); - assert_eq!(sender.buffer.len(), 3); - - for i in 0..3 { - sender.broadcast(i).unwrap(); - } - - assert_eq!(*receiver.try_recv().unwrap(), 0); - assert_eq!(*receiver.try_recv().unwrap(), 1); - - // "Cycle" buffer around twice - for i in 2..10 { - sender.broadcast(i).unwrap(); - } - - // Should be reading from the last element in the buffer - assert_eq!(*receiver.buffer[receiver.buffer.len() - 1].load_full().unwrap(), 7); - assert_eq!(*receiver.try_recv().unwrap(), 7); - - // Cloned receiver start reading where the original receiver left off - let receiver2 = receiver.clone(); - assert_eq!(*receiver2.try_recv().unwrap(), 8); - - sender.broadcast(10).unwrap(); - - // Test reader has moved forward in the buffer - let values = receiver.into_iter().map(|v| *v).collect::>(); - assert_eq!(values, (8..=10).collect::>()); - } -} diff --git a/infrastructure/broadcast_channel/src/lib.rs b/infrastructure/broadcast_channel/src/lib.rs deleted file mode 100644 index c0010daa25..0000000000 --- a/infrastructure/broadcast_channel/src/lib.rs +++ /dev/null @@ -1,97 +0,0 @@ -//! # Lock-free Bounded Non-Blocking Pub-Sub Queue -//! -//! This is a publish subscribe pattern queue, where the publisher is never blocked by -//! slow subscribers. The side effect is that slow subscribers will miss messages. The intended -//! use-case are high throughput streams where receiving the latest message is prioritized over -//! receiving the entire stream. Market Data Feeds, Live Streams, etc.... -//! -//! The underlying data-structure is a vector of Arc(s) eliminating the use of copies. -//! -//! ## Features -//! * Lock-Free Write/Read - Lock-Free for Publisher and Lock-Free for Subscribers. -//! * Bounded - Constant size of memory used, max is `sizeof(MsgObject)*(queue_size + sub_cnt + 1)`. This is an -//! edge-case where each subscriber is holding a ref to an object while the publisher has published a full length of -//! queue in the mean time. -//! * Non-Blocking - The queue never blocks the publisher, slow subscribers miss data proportinal to their speed. -//! * Pub-Sub - Every Subscriber that can keep up with the Publisher will recieve all the data the Publisher publishes. -//! * [`sync`]/[`async`] - both interfaces are provided, as well as a bare queue implementation without the thread -//! synchronisation ,and futures logic. -//! * std::sync::mpsc like interface - The API is modeled after the standard library mpsc queue, channel function are -//! used to create a tuple of (Publisher, Subscriber), while the Clone trait on Subscriber creates additional -//! subscribers to the same Publisher. -//! -//! [`sync::Publisher`], [`async::Publisher`], and [`BarePublisher`] are used to broadcast data to -//! [`sync::Subscriber`], [`async::Subscriber`], and [`BareSubscriber`] pools. Subscribers are -//! clone-able such that many threads, or futures, can receive data simultaneously. The only -//! limitation is that Subscribers have to keep up with the frequency of the Publisher. If a -//! Subscriber is slow it will drop data. -//! -//! ## Disconnection -//! -//! The broadcast and receive operations on channels will all return a [`Result`] -//! indicating whether the operation succeeded or not. An unsuccessful operation -//! is normally indicative of the other half of a channel having "hung up" by -//! being dropped in its corresponding thread. -//! -//! Once half of a channel has been deallocated, most operations can no longer -//! continue to make progress, so [`Err`] will be returned. Many applications -//! will continue to [`unwrap`] the results returned from this module, -//! instigating a propagation of failure among threads if one unexpectedly dies. -//! -//! -//! # Examples -//! ## Simple raw usage -//! ```rust -//! use tari_broadcast_channel::raw_bounded; -//! -//! fn main() { -//! let (tx, rx) = raw_bounded(10); -//! (1..15).for_each(|x| tx.broadcast(x).unwrap()); -//! -//! let received: Vec = rx.map(|x| *x).collect(); -//! // Test that only the last 10 elements are in the received list. -//! let expected: Vec = (5..15).collect(); -//! -//! assert_eq!(expected, received); -//! } -//! ``` -//! ## Simple asynchronous usage -//! ```rust -//! # use tari_broadcast_channel::bounded; -//! # use futures::executor::block_on; -//! # use futures::StreamExt; -//! # use futures::stream; -//! -//! let (publisher, subscriber1) = bounded(10); -//! let subscriber2 = subscriber1.clone(); -//! -//! block_on(async move { -//! stream::iter(1..15).map(|i| Ok(i)).forward(publisher).await.unwrap(); -//! }); -//! -//! let received1: Vec = block_on(async { subscriber1.map(|x| *x).collect().await }); -//! let received2: Vec = block_on(async { subscriber2.map(|x| *x).collect().await }); -//! // Test that only the last 10 elements are in the received list. -//! let expected = (5..15).collect::>(); -//! assert_eq!(received1, expected); -//! assert_eq!(received2, expected); -//! ``` -//! -//! [`BarePublisher`]: struct.BarePublisher.html -//! [`BareSubscriber`]: struct.BareSubscriber.html -//! [`sync`]: sync/index.html -//! [`async`]: async/index.html -//! [`sync::Publisher`]: sync/struct.Publisher.html -//! [`sync::Subscriber`]: sync/struct.Subscriber.html -//! [`async::Publisher`]: async/struct.Publisher.html -//! [`async::Subscriber`]: async/struct.Subscriber.html -//! [`Result`]: ../../../std/result/enum.Result.html -//! [`Err`]: ../../../std/result/enum.Result.html#variant.Err -//! [`unwrap`]: ../../../std/result/enum.Result.html#method.unwrap - -mod async_channel; -pub(crate) mod atomic_counter; -mod channel; - -pub use async_channel::{bounded, Publisher, Subscriber}; -pub use channel::{bounded as raw_bounded, SendError}; diff --git a/infrastructure/crypto/Cargo.toml b/infrastructure/crypto/Cargo.toml deleted file mode 100644 index a7b47792d1..0000000000 --- a/infrastructure/crypto/Cargo.toml +++ /dev/null @@ -1,44 +0,0 @@ -[package] -name = "tari_crypto" -description = "Tari Cryptography library" -authors = ["The Tari Development Community"] -repository = "https://github.com/tari-project/tari" -categories = ["cryptography"] -homepage = "https://tari.com" -readme = "README.md" -license = "BSD-3-Clause" -version = "0.0.6" -edition = "2018" - -[dependencies] -tari_utilities = { path = "../tari_util", version = "^0.0" } -base64 = "0.10.1" -digest = "0.8.0" -rand = "0.5.5" -clear_on_drop = "0.2.3" -curve25519-dalek = { version = "1.1.3" } -bulletproofs = { version = "1.0.2" } -merlin = "1.0.3" -sha2 = "0.8.0" -derive-error = "0.0.4" -blake2 = "0.8.0" -rmp-serde = "0.13.7" -serde = "1.0.89" -serde_json = "1.0" -lazy_static = "1.3.0" - -[features] -avx2 = ["curve25519-dalek/avx2_backend", "bulletproofs/avx2_backend"] - -[dev-dependencies] -criterion = "0.2" -bincode = "1.1.4" - -[lib] -# Disable benchmarks to allow Criterion to take over -bench = false - -[[bench]] -name = "benches" -path = "benches/mod.rs" -harness = false diff --git a/infrastructure/crypto/README.md b/infrastructure/crypto/README.md deleted file mode 100644 index 0a29740680..0000000000 --- a/infrastructure/crypto/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Tari Crypto - -This crate is part of the [Tari Cryptocurrency](https://tari.com) project. - -Major features of this library include: - -* Pedersen commitments -* Schnorr Signatures -* Generic Public and Secret Keys -* [Musig!](https://blockstream.com/2018/01/23/musig-key-aggregation-schnorr-signatures/) - -The `tari_crypto` crate makes heavy use of the excellent [Dalek](https://github.com/dalek-cryptography/curve25519-dalek) -libraries. The default implementation for Tari ECC is the [Ristretto255 curve](https://ristretto.group). \ No newline at end of file diff --git a/infrastructure/crypto/benches/range_proof.rs b/infrastructure/crypto/benches/range_proof.rs deleted file mode 100644 index 87d209412b..0000000000 --- a/infrastructure/crypto/benches/range_proof.rs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2019. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Portions of this file were originally copyrighted (c) 2018 The Grin Developers, issued under the Apache License, -// Version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0. - -use criterion::{criterion_group, Criterion}; -use rand::{OsRng, Rng}; -use std::time::Duration; -use tari_crypto::{ - commitment::HomomorphicCommitmentFactory, - keys::SecretKey, - range_proof::RangeProofService, - ristretto::{ - dalek_range_proof::DalekRangeProofService, - pedersen::{PedersenCommitment, PedersenCommitmentFactory}, - RistrettoSecretKey, - }, -}; - -fn setup(n: usize) -> (DalekRangeProofService, RistrettoSecretKey, u64, PedersenCommitment) { - let mut rng = OsRng::new().unwrap(); - let base = PedersenCommitmentFactory::default(); - let prover = DalekRangeProofService::new(n, &base).unwrap(); - let k = RistrettoSecretKey::random(&mut rng); - let n_max = 1u64 << (n - 1); - let v = rng.gen_range(1, n_max); - let c = base.commit_value(&k, v); - (prover, k, v, c) -} - -pub fn generate_rangeproof(c: &mut Criterion) { - c.bench_function_over_inputs( - "Generate range proofs", - |b, range| { - let (prover, k, v, _) = setup(**range); - b.iter(move || prover.construct_proof(&k, v).unwrap()); - }, - &[8, 16, 32, 64], - ); -} - -pub fn verify_rangeproof_valid(c: &mut Criterion) { - c.bench_function_over_inputs( - "Validate valid range proofs", - |b, range| { - let (prover, k, v, c) = setup(**range); - let proof = prover.construct_proof(&k, v).unwrap(); - b.iter(move || assert!(prover.verify(&proof, &c))); - }, - &[8, 16, 32, 64], - ); -} - -criterion_group!( -name = range_proofs; -config = Criterion::default().warm_up_time(Duration::from_millis(1_500)); -targets = generate_rangeproof, verify_rangeproof_valid -); diff --git a/infrastructure/crypto/benches/signatures.rs b/infrastructure/crypto/benches/signatures.rs deleted file mode 100644 index 45ecdc854d..0000000000 --- a/infrastructure/crypto/benches/signatures.rs +++ /dev/null @@ -1,75 +0,0 @@ -use criterion::{criterion_group, BatchSize, Criterion}; -use rand::{OsRng, RngCore}; -use std::time::Duration; -use tari_crypto::{ - keys::{PublicKey, SecretKey}, - ristretto::{RistrettoPublicKey, RistrettoSchnorr, RistrettoSecretKey}, -}; -use tari_utilities::byte_array::ByteArray; - -fn generate_secret_key(c: &mut Criterion) { - c.bench_function("Generate secret key", |b| { - let mut rng = OsRng::new().unwrap(); - b.iter(|| { - let _ = RistrettoSecretKey::random(&mut rng); - }); - }); -} - -fn native_keypair(c: &mut Criterion) { - c.bench_function("Generate key pair", |b| { - let mut rng = OsRng::new().unwrap(); - b.iter(|| RistrettoPublicKey::random_keypair(&mut rng)); - }); -} - -struct SigningData { - k: RistrettoSecretKey, - p: RistrettoPublicKey, - r: RistrettoSecretKey, - m: RistrettoSecretKey, -} - -fn gen_keypair() -> SigningData { - let mut rng = OsRng::new().unwrap(); - let mut msg = [0u8; 32]; - rng.fill_bytes(&mut msg); - let (k, p) = RistrettoPublicKey::random_keypair(&mut rng); - let r = RistrettoSecretKey::random(&mut rng); - let m = RistrettoSecretKey::from_bytes(&msg).unwrap(); - SigningData { k, p, r, m } -} - -fn sign_message(c: &mut Criterion) { - c.bench_function("Create RistrettoSchnorr", move |b| { - b.iter_batched( - gen_keypair, - |d| { - let _ = RistrettoSchnorr::sign(d.k, d.r, &d.m.to_vec()).unwrap(); - }, - BatchSize::SmallInput, - ); - }); - - // assert!(sig.verify(&p, &msg_key)); -} - -fn verify_message(c: &mut Criterion) { - c.bench_function("Verify RistrettoSchnorr", move |b| { - b.iter_batched( - || { - let d = gen_keypair(); - let s = RistrettoSchnorr::sign(d.k.clone(), d.r.clone(), &d.m.to_vec()).unwrap(); - (d, s) - }, - |(d, s)| assert!(s.verify(&d.p, &d.m)), - BatchSize::SmallInput, - ); - }); -} - -criterion_group!( -name = signatures; -config = Criterion::default().warm_up_time(Duration::from_millis(500)); -targets = generate_secret_key, native_keypair, sign_message, verify_message -); diff --git a/infrastructure/crypto/src/commitment.rs b/infrastructure/crypto/src/commitment.rs deleted file mode 100644 index fe7f597d6f..0000000000 --- a/infrastructure/crypto/src/commitment.rs +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::keys::PublicKey; -use serde::{Deserialize, Serialize}; -use std::{ - cmp::Ordering, - hash::{Hash, Hasher}, - ops::{Add, Sub}, -}; -use tari_utilities::{ByteArray, ByteArrayError}; - -/// A commitment is like a sealed envelope. You put some information inside the envelope, and then seal (commit) it. -/// You can't change what you've said, but also, no-one knows what you've said until you're ready to open (open) the -/// envelope and reveal its contents. Also it's a special envelope that can only be opened by a special opener that -/// you keep safe in your drawer. -/// -/// There are also different types of commitments that vary in their security guarantees, but all of them are -/// represented by binary data; so [HomomorphicCommitment](trait.HomomorphicCommitment.html) implements -/// [ByteArray](trait.ByteArray.html). -/// -/// The Homomorphic part means, more or less, that commitments follow some of the standard rules of -/// arithmetic. Adding two commitments is the same as committing to the sum of their parts: -/// $$ \begin{aligned} -/// C_1 &= v_1.G + k_1.H \\\\ -/// C_2 &= v_2.G + k_2.H \\\\ -/// \therefore C_1 + C_2 &= (v_1 + v_2)G + (k_1 + k_2)H -/// \end{aligned} $$ -#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] -#[serde(bound(deserialize = "P: PublicKey"))] -pub struct HomomorphicCommitment

(pub(crate) P) -where P: PublicKey; - -impl

HomomorphicCommitment

-where P: PublicKey -{ - pub fn as_public_key(&self) -> &P { - &self.0 - } - - pub fn from_public_key(p: &P) -> HomomorphicCommitment

{ - HomomorphicCommitment(p.clone()) - } -} - -impl

ByteArray for HomomorphicCommitment

-where P: PublicKey -{ - fn from_bytes(bytes: &[u8]) -> Result { - let p = P::from_bytes(bytes)?; - Ok(Self(p)) - } - - fn as_bytes(&self) -> &[u8] { - self.0.as_bytes() - } -} - -impl

PartialOrd for HomomorphicCommitment

-where P: PublicKey -{ - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.0.cmp(&other.0)) - } -} - -impl

Ord for HomomorphicCommitment

-where P: PublicKey -{ - fn cmp(&self, other: &Self) -> Ordering { - self.0.cmp(&other.0) - } -} - -/// Add two commitments together. Note! There is no check that the bases are equal. -impl<'b, P> Add for &'b HomomorphicCommitment

-where - P: PublicKey, - &'b P: Add<&'b P, Output = P>, -{ - type Output = HomomorphicCommitment

; - - fn add(self, rhs: &'b HomomorphicCommitment

) -> Self::Output { - HomomorphicCommitment(&self.0 + &rhs.0) - } -} - -/// Subtracts the left commitment from the right commitment. Note! There is no check that the bases are equal. -impl<'b, P> Sub for &'b HomomorphicCommitment

-where - P: PublicKey, - &'b P: Sub<&'b P, Output = P>, -{ - type Output = HomomorphicCommitment

; - - fn sub(self, rhs: &'b HomomorphicCommitment

) -> Self::Output { - HomomorphicCommitment(&self.0 - &rhs.0) - } -} - -impl Hash for HomomorphicCommitment

{ - fn hash(&self, state: &mut H) { - state.write(self.as_bytes()) - } -} - -pub trait HomomorphicCommitmentFactory { - type P: PublicKey; - - /// Create a new commitment with the value and blinding factor provided. The implementing type will provide the - /// base values - fn commit(&self, k: &::K, v: &::K) -> HomomorphicCommitment; - /// return an identity point for addition using the specified base point. This is a commitment to zero with a zero - /// blinding factor on the base point - fn zero(&self) -> HomomorphicCommitment; - /// Test whether the given keys open the given commitment - fn open( - &self, - k: &::K, - v: &::K, - commitment: &HomomorphicCommitment, - ) -> bool; - /// Create a commitment from a spending key and a integer value - fn commit_value(&self, k: &::K, value: u64) -> HomomorphicCommitment; - /// Test whether the given private key and value open the given commitment - fn open_value(&self, k: &::K, v: u64, commitment: &HomomorphicCommitment) -> bool; -} diff --git a/infrastructure/crypto/src/keys.rs b/infrastructure/crypto/src/keys.rs deleted file mode 100644 index 1136ff882f..0000000000 --- a/infrastructure/crypto/src/keys.rs +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -//! General definition of public-private key pairs for use in Tari. The traits and structs -//! defined here are used in the Tari domain logic layer exclusively (as opposed to any specific -//! implementation of ECC curve). The idea being that we can swap out the underlying -//! implementation without worrying too much about the impact on upstream code. - -use rand::{CryptoRng, Rng}; -use serde::{de::DeserializeOwned, ser::Serialize}; -use std::ops::Add; -use tari_utilities::ByteArray; - -/// A trait specifying common behaviour for representing `SecretKey`s. Specific elliptic curve -/// implementations need to implement this trait for them to be used in Tari. -/// -/// ## Example -/// -/// Assuming there is a Ristretto implementation, -/// ```edition2018 -/// # use tari_crypto::ristretto::{ RistrettoSecretKey, RistrettoPublicKey }; -/// # use tari_crypto::keys::{ SecretKey, PublicKey }; -/// # use rand; -/// let mut rng = rand::OsRng::new().unwrap(); -/// let k = RistrettoSecretKey::random(&mut rng); -/// let p = RistrettoPublicKey::from_secret_key(&k); -/// ``` -pub trait SecretKey: ByteArray + Clone + PartialEq + Eq + Add + Default { - fn key_length() -> usize; - fn random(rng: &mut R) -> Self; -} - -//---------------------------------------- Public Keys ----------------------------------------// - -/// A trait specifying common behaviour for representing `PublicKey`s. Specific elliptic curve -/// implementations need to implement this trait for them to be used in Tari. -/// -/// See [SecretKey](trait.SecretKey.html) for an example. -pub trait PublicKey: - ByteArray + Add + Clone + PartialOrd + Ord + Default + Serialize + DeserializeOwned -{ - type K: SecretKey; - /// Calculate the public key associated with the given secret key. This should not fail; if a - /// failure does occur (implementation error?), the function will panic. - fn from_secret_key(k: &Self::K) -> Self; - - fn key_length() -> usize; - - fn batch_mul(scalars: &[Self::K], points: &[Self]) -> Self; - - fn random_keypair(rng: &mut R) -> (Self::K, Self) { - let k = Self::K::random(rng); - let pk = Self::from_secret_key(&k); - (k, pk) - } -} - -/// This trait provides a common mechanism to calculate a shared secret using the private and public key of two parties -pub trait DiffieHellmanSharedSecret: ByteArray + Clone + PartialEq + Eq + Add + Default { - type PK: PublicKey; - /// Generate a shared secret from one party's private key and another party's public key - fn shared_secret(k: &::K, pk: &Self::PK) -> Self::PK; -} diff --git a/infrastructure/crypto/src/lib.rs b/infrastructure/crypto/src/lib.rs deleted file mode 100644 index ac6e7f2b71..0000000000 --- a/infrastructure/crypto/src/lib.rs +++ /dev/null @@ -1,18 +0,0 @@ -extern crate serde; -extern crate serde_json; - -#[macro_use] -extern crate lazy_static; - -#[macro_use] -pub mod macros; -pub mod commitment; -pub mod common; -pub mod keys; -pub mod musig; -pub mod range_proof; -pub mod signatures; - -// Implementations -#[allow(clippy::op_ref)] -pub mod ristretto; diff --git a/infrastructure/crypto/src/macros.rs b/infrastructure/crypto/src/macros.rs deleted file mode 100644 index 6b58903136..0000000000 --- a/infrastructure/crypto/src/macros.rs +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -/// Adds some variations of `Add` given a definition for `Add` that takes references. i.e. assuming we have -/// ```ignore -/// impl<'a, 'b> Add<&'b Foo> for &'a Foo { -/// type Output = Foo; -/// fn add(self, rhs: &'b rhs) -> Foo {...} -/// } -/// ``` -/// which allows `&foo1 + &foo2` This macro adds definitions for -/// * `&foo1 + foo2` -/// * `foo1 + &foo2` -/// * `foo1 + foo2` -macro_rules! define_add_variants { - (LHS = $lhs:ty, RHS = $rhs:ty, Output = $out:ty) => { - impl<'b> Add<&'b $rhs> for $lhs { - type Output = $out; - - fn add(self, rhs: &'b $rhs) -> $out { - &self + rhs - } - } - - impl<'a> Add<$rhs> for &'a $lhs { - type Output = $out; - - fn add(self, rhs: $rhs) -> $out { - self + &rhs - } - } - - impl Add<$rhs> for $lhs { - type Output = $out; - - fn add(self, rhs: $rhs) -> $out { - &self + &rhs - } - } - }; -} - -/// Add variations for `Sub` definitions, similar to those for `Add` -macro_rules! define_sub_variants { - (LHS = $lhs:ty, RHS = $rhs:ty, Output = $out:ty) => { - impl<'b> Sub<&'b $rhs> for $lhs { - type Output = $out; - - fn sub(self, rhs: &'b $rhs) -> $out { - &self - rhs - } - } - - impl<'a> Sub<$rhs> for &'a $lhs { - type Output = $out; - - fn sub(self, rhs: $rhs) -> $out { - self - &rhs - } - } - - impl Sub<$rhs> for $lhs { - type Output = $out; - - fn sub(self, rhs: $rhs) -> $out { - &self - &rhs - } - } - }; -} - -/// Add variations for `Mul` definitions, similar to those for `Add` -macro_rules! define_mul_variants { - (LHS = $lhs:ty, RHS = $rhs:ty, Output = $out:ty) => { - impl<'b> Mul<&'b $rhs> for $lhs { - type Output = $out; - - fn mul(self, rhs: &'b $rhs) -> $out { - &self * rhs - } - } - - impl<'a> Mul<$rhs> for &'a $lhs { - type Output = $out; - - fn mul(self, rhs: $rhs) -> $out { - self * &rhs - } - } - - impl Mul<$rhs> for $lhs { - type Output = $out; - - fn mul(self, rhs: $rhs) -> $out { - &self * &rhs - } - } - }; -} diff --git a/infrastructure/crypto/src/musig.rs b/infrastructure/crypto/src/musig.rs deleted file mode 100644 index e6d94d4546..0000000000 --- a/infrastructure/crypto/src/musig.rs +++ /dev/null @@ -1,276 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Portions of this file were originally copyrighted (c) 2018 The Grin Developers, issued under the Apache License, -// Version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0. - -use crate::keys::{PublicKey, SecretKey}; -use derive_error::Error; -use digest::Digest; -use std::{ops::Mul, prelude::v1::Vec}; - -//---------------------------------------------- Constants ------------------------------------------------// -pub const MAX_SIGNATURES: usize = 32768; // If you need more, call customer support - -//---------------------------------------------- Error Codes ------------------------------------------------// -#[derive(Clone, Debug, Error, PartialEq, Eq)] -pub enum MuSigError { - /// The number of public nonces must match the number of public keys in the joint key - #[error(no_from, non_std)] - MismatchedNonces, - /// The number of partial signatures must match the number of public keys in the joint key - #[error(no_from, non_std)] - MismatchedSignatures, - /// The aggregate signature did not verify - #[error(no_from, non_std)] - InvalidAggregateSignature, - /// A partial signature did not validate - #[error(no_from, non_std)] - InvalidPartialSignature(usize), - /// The participant list must be sorted before making this call - #[error(no_from, non_std)] - NotSorted, - /// The participant key is not in the list - #[error(no_from, non_std)] - ParticipantNotFound, - /// An attempt was made to perform an invalid MuSig state transition - #[error(no_from, non_std)] - InvalidStateTransition, - /// An attempt was made to add a duplicate public key to a MuSig signature - #[error(no_from, non_std)] - DuplicatePubKey, - /// There are too many parties in the MuSig signature - #[error(no_from, non_std)] - TooManyParticipants, - /// There are too few parties in the MuSig signature - #[error(no_from, non_std)] - NotEnoughParticipants, - /// A nonce hash is missing - #[error(no_from, non_std)] - MissingHash, - /// The message to be signed can only be set once - #[error(no_from, non_std)] - MessageAlreadySet, - /// The message to be signed MUST be set before the final nonce is added to the MuSig ceremony - #[error(no_from, non_std)] - MissingMessage, - /// The message to sign is invalid. have you hashed it? - #[error(no_from, non_std)] - InvalidMessage, - /// MuSig requires a hash function with a 32 byte digest - #[error(no_from, non_std)] - IncompatibleHashFunction, -} - -//---------------------------------------------- Joint Key ------------------------------------------------// - -/// The JointKey is a modified public key used in Signature aggregation schemes like MuSig which is not susceptible -/// to Rogue Key attacks. -/// -/// A joint key is calculated from _n_ participants by having each of them calculate: -/// $$ -/// L = H(P_1 || P_2 || \dots || P_n) -/// X = \sum H(L || P_i)P_i -/// X_i = k_i H(L || P_i).G -/// $$ -/// Concrete implementations of JointKey will also need to implement the MultiScalarMul trait, which allows them to -/// provide implementation-specific optimisations for dot-product operations. -pub struct JointKey -where - K: SecretKey, - P: PublicKey, -{ - pub_keys: Vec

, - musig_scalars: Vec, - common: K, - joint_pub_key: P, -} - -pub struct JointKeyBuilder -where - K: SecretKey, - P: PublicKey, -{ - num_signers: usize, - pub_keys: Vec

, -} - -impl JointKeyBuilder -where - K: SecretKey + Mul, - P: PublicKey, -{ - /// Create a new JointKey instance containing no participant keys, or return `TooManyParticipants` if n exceeds - /// `MAX_SIGNATURES` - pub fn new(n: usize) -> Result, MuSigError> { - if n > MAX_SIGNATURES { - return Err(MuSigError::TooManyParticipants); - } - if n == 0 { - return Err(MuSigError::NotEnoughParticipants); - } - Ok(JointKeyBuilder { - pub_keys: Vec::with_capacity(n), - num_signers: n, - }) - } - - /// The number of parties in the Joint key - pub fn num_signers(&self) -> usize { - self.num_signers - } - - /// Add a participant signer's public key to the JointKey - pub fn add_key(&mut self, pub_key: P) -> Result { - if self.key_exists(&pub_key) { - return Err(MuSigError::DuplicatePubKey); - } - // push panics on int overflow, so catch this here - let n = self.pub_keys.len(); - if n >= self.num_signers { - return Err(MuSigError::TooManyParticipants); - } - self.pub_keys.push(pub_key); - Ok(self.pub_keys.len()) - } - - /// Checks whether the given public key is in the participants list - pub fn key_exists(&self, key: &P) -> bool { - self.pub_keys.iter().any(|v| v == key) - } - - /// Checks whether the number of pub_keys is equal to `num_signers` - pub fn is_full(&self) -> bool { - self.pub_keys.len() == self.num_signers - } - - /// Add all the keys in `keys` to the participant list. - pub fn add_keys>(&mut self, keys: T) -> Result { - for k in keys { - self.add_key(k)?; - } - Ok(self.pub_keys.len()) - } - - /// Produce a sorted, immutable joint Musig public key from the gathered set of conventional public keys - pub fn build(mut self) -> Result, MuSigError> { - if !self.is_full() { - return Err(MuSigError::NotEnoughParticipants); - } - self.sort_keys(); - let common = self.calculate_common::(); - let musig_scalars = self.calculate_musig_scalars::(&common); - let joint_pub_key = JointKeyBuilder::calculate_joint_key::(&musig_scalars, &self.pub_keys); - Ok(JointKey { - pub_keys: self.pub_keys, - musig_scalars, - joint_pub_key, - common, - }) - } - - /// Utility function to calculate \\( \ell = H(P_1 || ... || P_n) \mod p \\) - /// # Panics - /// If the SecretKey implementation cannot construct a valid key from the given hash, the function will panic. - /// You should ensure that the SecretKey constructor protects against failures and that the hash digest given - /// produces a byte array of the correct length. - fn calculate_common(&self) -> K { - let mut common = D::new(); - for k in self.pub_keys.iter() { - common = common.chain(k.as_bytes()); - } - K::from_bytes(&common.result()) - .expect("Could not calculate Scalar from hash value. Your crypto/hash combination might be inconsistent") - } - - /// Private utility function to calculate \\( H(\ell || P_i) \mod p \\) - /// # Panics - /// If the SecretKey implementation cannot construct a valid key from the given hash, the function will panic. - /// You should ensure that the SecretKey constructor protects against failures and that the hash digest given - /// produces a byte array of the correct length. - fn calculate_partial_key(common: &[u8], pubkey: &P) -> K { - let k = D::new().chain(common).chain(pubkey.as_bytes()).result(); - K::from_bytes(&k) - .expect("Could not calculate Scalar from hash value. Your crypto/hash combination might be inconsistent") - } - - /// Sort the keys in the participant list. The order is determined by the `Ord` trait of the concrete public key - /// implementation used to construct the joint key. - /// **NB:** Sorting the keys will, usually, change the value of the joint key! - fn sort_keys(&mut self) { - self.pub_keys.sort_unstable(); - } - - /// Utility function that produces the vector of MuSig private key modifiers, \\( a_i = H(\ell || P_i) \\) - fn calculate_musig_scalars(&self, common: &K) -> Vec { - self.pub_keys - .iter() - .map(|p| JointKeyBuilder::calculate_partial_key::(common.as_bytes(), p)) - .collect() - } - - /// Calculate the value of the Joint MuSig public key. **NB**: you should usually sort the participant's keys - /// before calculating the joint key. - fn calculate_joint_key(scalars: &[K], pub_keys: &[P]) -> P { - P::batch_mul(scalars, pub_keys) - } -} - -impl JointKey -where - K: SecretKey, - P: PublicKey, -{ - /// Return the index of the given key in the joint key participants list. If the key isn't in the list, returns - /// `Err(ParticipantNotFound)` - pub fn index_of(&self, pubkey: &P) -> Result { - match self.pub_keys.binary_search(pubkey) { - Ok(i) => Ok(i), - Err(_) => Err(MuSigError::ParticipantNotFound), - } - } - - #[inline] - pub fn size(&self) -> usize { - self.pub_keys.len() - } - - #[inline] - pub fn get_pub_keys(&self, index: usize) -> &P { - &self.pub_keys[index] - } - - #[inline] - pub fn get_musig_scalar(&self, index: usize) -> &K { - &self.musig_scalars[index] - } - - #[inline] - pub fn get_common(&self) -> &K { - &self.common - } - - #[inline] - pub fn get_joint_pubkey(&self) -> &P { - &self.joint_pub_key - } -} diff --git a/infrastructure/crypto/src/ristretto/constants.rs b/infrastructure/crypto/src/ristretto/constants.rs deleted file mode 100644 index 79c3255ad4..0000000000 --- a/infrastructure/crypto/src/ristretto/constants.rs +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint}; - -/// These points on the Ristretto curve have been created by sequentially hashing the Generator point with SHA512 and -/// using the byte string representation of the hash as input into the `from_uniform_bytes` constructor in -/// [RistrettoPoint](Struct.RistrettoPoint.html). This process is validated with the `check_nums_points` test below. -pub const RISTRETTO_NUMS_POINTS_COMPRESSED: [CompressedRistretto; 10] = [ - CompressedRistretto([ - 144, 202, 17, 205, 108, 98, 39, 203, 10, 188, 57, 226, 113, 12, 68, 74, 230, 97, 126, 168, 24, 152, 231, 22, - 53, 63, 52, 16, 217, 101, 102, 5, - ]), - CompressedRistretto([ - 158, 163, 67, 196, 112, 228, 87, 33, 101, 243, 64, 56, 81, 223, 107, 32, 221, 251, 206, 241, 171, 132, 207, - 171, 15, 197, 139, 223, 124, 54, 254, 7, - ]), - CompressedRistretto([ - 48, 188, 62, 20, 154, 63, 125, 42, 172, 191, 231, 48, 225, 158, 154, 7, 119, 59, 83, 83, 219, 98, 32, 99, 185, - 44, 153, 54, 50, 173, 60, 7, - ]), - CompressedRistretto([ - 142, 16, 136, 206, 212, 150, 29, 136, 213, 177, 113, 189, 154, 52, 40, 68, 84, 120, 154, 69, 95, 70, 236, 55, - 82, 145, 49, 33, 36, 183, 30, 108, - ]), - CompressedRistretto([ - 112, 19, 255, 145, 136, 246, 135, 216, 133, 201, 90, 218, 110, 88, 11, 35, 141, 231, 33, 12, 85, 193, 246, 36, - 123, 31, 16, 101, 38, 8, 10, 85, - ]), - CompressedRistretto([ - 122, 234, 197, 53, 77, 120, 8, 171, 35, 80, 105, 62, 45, 2, 30, 42, 99, 188, 47, 231, 194, 119, 210, 5, 107, - 176, 108, 127, 141, 78, 6, 81, - ]), - CompressedRistretto([ - 228, 224, 63, 227, 33, 214, 87, 20, 172, 223, 193, 247, 88, 37, 111, 121, 204, 69, 49, 213, 30, 143, 121, 244, - 15, 194, 105, 198, 196, 117, 160, 65, - ]), - CompressedRistretto([ - 136, 214, 134, 144, 253, 111, 238, 89, 110, 128, 92, 250, 34, 30, 126, 40, 119, 21, 166, 201, 46, 148, 100, - 255, 196, 32, 172, 183, 12, 236, 51, 27, - ]), - CompressedRistretto([ - 204, 102, 24, 189, 15, 12, 192, 35, 132, 29, 173, 74, 19, 204, 46, 55, 166, 35, 14, 36, 48, 80, 214, 220, 196, - 201, 49, 208, 70, 224, 234, 3, - ]), - CompressedRistretto([ - 96, 230, 255, 101, 87, 7, 198, 66, 73, 210, 250, 146, 78, 49, 146, 182, 149, 220, 88, 44, 180, 246, 214, 140, - 180, 43, 155, 49, 24, 147, 237, 64, - ]), -]; - -lazy_static! { - pub static ref RISTRETTO_NUMS_POINTS: [RistrettoPoint; 10] = { - let mut arr = [RistrettoPoint::default(); 10]; - for i in 0..10 { - arr[i] = RISTRETTO_NUMS_POINTS_COMPRESSED[i].decompress().unwrap(); - } - arr - }; -} - -pub const RISTRETTO_PEDERSEN_H: CompressedRistretto = RISTRETTO_NUMS_POINTS_COMPRESSED[0]; - -#[cfg(test)] -mod test { - use crate::ristretto::constants::{RISTRETTO_NUMS_POINTS, RISTRETTO_NUMS_POINTS_COMPRESSED}; - use curve25519_dalek::{ - constants::RISTRETTO_BASEPOINT_POINT, - ristretto::{CompressedRistretto, RistrettoPoint}, - }; - use sha2::{Digest, Sha512}; - - /// Generate a set of NUMS points by sequentially hashing the Ristretto255 generator point. By using - /// `from_uniform_bytes`, the resulting point is a NUMS point if the input bytes are from a uniform distribution. - fn nums_ristretto(n: usize) -> (Vec, Vec) { - let mut val = RISTRETTO_BASEPOINT_POINT.compress().to_bytes(); - let mut points = Vec::with_capacity(n); - let mut compressed_points = Vec::with_capacity(n); - let mut a: [u8; 64] = [0; 64]; - for _ in 0..n { - let hashed_v = Sha512::digest(&val[..]); - a.copy_from_slice(&hashed_v); - let next_val = RistrettoPoint::from_uniform_bytes(&a); - points.push(next_val); - let next_compressed = next_val.compress(); - val = next_compressed.to_bytes(); - compressed_points.push(next_compressed); - } - (points, compressed_points) - } - - /// Confirm that the [RISTRETTO_NUM_POINTS array](Const.RISTRETTO_NUMS_POINTS.html) is generated with Nothing Up - /// My Sleeve (NUMS). - #[test] - pub fn check_nums_points() { - let n = RISTRETTO_NUMS_POINTS_COMPRESSED.len(); - let v_arr = nums_ristretto(n); - for i in 0..n { - assert_eq!(v_arr.0[i], RISTRETTO_NUMS_POINTS[i]); - assert_eq!(v_arr.1[i], RISTRETTO_NUMS_POINTS_COMPRESSED[i]); - } - } -} diff --git a/infrastructure/crypto/src/ristretto/dalek_range_proof.rs b/infrastructure/crypto/src/ristretto/dalek_range_proof.rs deleted file mode 100644 index 416851910d..0000000000 --- a/infrastructure/crypto/src/ristretto/dalek_range_proof.rs +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright 2019. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Portions of this file were originally copyrighted (c) 2018 The Grin Developers, issued under the Apache License, -// Version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0. - -use crate::{ - range_proof::{RangeProofError, RangeProofService}, - ristretto::{ - pedersen::{PedersenCommitment, PedersenCommitmentFactory}, - RistrettoPublicKey, - RistrettoSecretKey, - }, -}; -use bulletproofs::{BulletproofGens, PedersenGens, RangeProof as DalekProof}; -use merlin::Transcript; - -/// A wrapper around the Dalek library implementation of Bulletproof range proofs. -pub struct DalekRangeProofService { - range: usize, - pc_gens: PedersenGens, - bp_gens: BulletproofGens, -} - -const MASK: usize = 0b111_1000; // Mask for 8,16,32,64; the valid ranges on the Dalek library - -impl DalekRangeProofService { - /// Create a new RangeProofService. The Dalek library can only generate proofs for ranges between [0; 2^range), - /// where valid range values are 8, 16, 32 and 64. - pub fn new(range: usize, base: &PedersenCommitmentFactory) -> Result { - if range == 0 || (range | MASK != MASK) { - return Err(RangeProofError::InitializationError); - } - let pc_gens = PedersenGens { - B_blinding: base.G, - B: base.H, - }; - let bp_gens = BulletproofGens::new(64, 1); - Ok(DalekRangeProofService { - range, - pc_gens, - bp_gens, - }) - } -} - -impl RangeProofService for DalekRangeProofService { - type K = RistrettoSecretKey; - type P = Vec; - type PK = RistrettoPublicKey; - - fn construct_proof(&self, key: &RistrettoSecretKey, value: u64) -> Result, RangeProofError> { - let mut pt = Transcript::new(b"tari"); - let k = key.0; - let (proof, _) = DalekProof::prove_single(&self.bp_gens, &self.pc_gens, &mut pt, value, &k, self.range) - .map_err(|_| RangeProofError::ProofConstructionError)?; - Ok(proof.to_bytes()) - } - - fn verify(&self, proof: &Self::P, commitment: &PedersenCommitment) -> bool { - let rp = DalekProof::from_bytes(&proof).map_err(|_| RangeProofError::InvalidProof); - if rp.is_err() { - return false; - } - let rp = rp.unwrap(); - let mut pt = Transcript::new(b"tari"); - let c = &commitment.0; - rp.verify_single(&self.bp_gens, &self.pc_gens, &mut pt, &c.compressed, self.range) - .is_ok() - } -} - -#[cfg(test)] -mod test { - use crate::{ - commitment::HomomorphicCommitmentFactory, - keys::SecretKey, - range_proof::{RangeProofError, RangeProofService}, - ristretto::{ - dalek_range_proof::DalekRangeProofService, - pedersen::PedersenCommitmentFactory, - RistrettoSecretKey, - }, - }; - use rand::OsRng; - - #[test] - fn create_and_verify_proof() { - let base = PedersenCommitmentFactory::default(); - let n: usize = 5; - let prover = DalekRangeProofService::new(1 << 5, &base).unwrap(); - let mut rng = OsRng::new().unwrap(); - let k = RistrettoSecretKey::random(&mut rng); - let v = RistrettoSecretKey::from(42); - let commitment_factory: PedersenCommitmentFactory = PedersenCommitmentFactory::default(); - let c = commitment_factory.commit(&k, &v); - let proof = prover.construct_proof(&k, 42).unwrap(); - assert_eq!(proof.len(), (2 * n + 9) * 32); - assert!(prover.verify(&proof, &c)); - // Invalid value - let v2 = RistrettoSecretKey::from(43); - let c = commitment_factory.commit(&k, &v2); - assert_eq!(prover.verify(&proof, &c), false); - // Invalid key - let k = RistrettoSecretKey::random(&mut rng); - let c = commitment_factory.commit(&k, &v); - assert_eq!(prover.verify(&proof, &c), false); - // Both invalid - let c = commitment_factory.commit(&k, &v2); - assert_eq!(prover.verify(&proof, &c), false); - } - - #[test] - fn non_power_of_two_range() { - let base = PedersenCommitmentFactory::default(); - match DalekRangeProofService::new(10, &base) { - Err(RangeProofError::InitializationError) => (), - Err(_) => panic!("Wrong error type"), - Ok(_) => panic!("Should fail with non power of two range"), - } - } - - #[test] - fn cannot_create_proof_for_out_of_range_value() { - let base = PedersenCommitmentFactory::default(); - let prover = DalekRangeProofService::new(8, &base).unwrap(); - let in_range = 255; - let out_of_range = 256; - let mut rng = OsRng::new().unwrap(); - let k = RistrettoSecretKey::random(&mut rng); - // Test with value in range - let v = RistrettoSecretKey::from(in_range); - let commitment_factory = PedersenCommitmentFactory::default(); - let c = commitment_factory.commit(&k, &v); - let proof = prover.construct_proof(&k, in_range).unwrap(); - assert!(prover.verify(&proof, &c)); - // Test value out of range - let proof = prover.construct_proof(&k, out_of_range).unwrap(); - // Test every single value from 0..255 - the proof should fail for every one - for i in 0..257 { - let v = RistrettoSecretKey::from(i); - let c = commitment_factory.commit(&k, &v); - assert_eq!(prover.verify(&proof, &c), false); - } - } -} diff --git a/infrastructure/crypto/src/ristretto/musig.rs b/infrastructure/crypto/src/ristretto/musig.rs deleted file mode 100644 index 3f6c0a9126..0000000000 --- a/infrastructure/crypto/src/ristretto/musig.rs +++ /dev/null @@ -1,1089 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Portions of this file were originally copyrighted (c) 2018 The Grin Developers, issued under the Apache License, -// Version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0. - -use crate::{ - musig::{JointKey, JointKeyBuilder, MuSigError}, - ristretto::{RistrettoPublicKey, RistrettoSchnorr, RistrettoSecretKey}, - signatures::SchnorrSignature, -}; -use digest::Digest; -use std::marker::PhantomData; -use tari_utilities::{fixed_set::FixedSet, ByteArray}; - -//----------------------------------------- Constants and aliases ------------------------------------------------// - -type JKBuilder = JointKeyBuilder; -type JointPubKey = JointKey; -type MessageHash = Vec; -type MessageHashSlice = [u8]; - -/// MuSig signature aggregation. [MuSig](https://blockstream.com/2018/01/23/musig-key-aggregation-schnorr-signatures/) -/// is a 3-round signature aggregation protocol. -/// We assume that all the public keys are known and publicly accessible. A [Joint Public Key](structs.JointKey.html) -/// is constructed by all participants. -/// 1. In the first round, participants share the hash of their nonces. -/// 2. Participants then share their public nonce, \\( R_i \\), and all participants calculate the shared nonce, -/// \\( R = \sum R_i \\). -/// 3. Each participant then calculates a partial signature, with the final signature being the sum of all the -/// partial signatures. -/// -/// This protocol is implemented as a Finite State Machine. MuSig is a simple wrapper around a `MusigState` enum that -/// holds the various states that the MuSig protocol can be in, combined with a `MuSigEvents` enum that enumerates -/// the relevant input events that can occur. Any attempt to invoke an invalid transition, or any other failure -/// condition results in the `Failure` state; in which case the MuSig protocol should be abandoned. -/// -/// Rust's type system is leveraged to prevent any rewinding of state; old state variables are destroyed when -/// transitioning to new states. The MuSig variable also _takes ownership_ of the nonce key, reducing the risk of -/// nonce reuse (though obviously it doesn't eliminate it). Let's be clear: REUSING a nonce WILL result in your secret -/// key being discovered. See -/// [this post](https://tlu.tarilabs.com/cryptography/digital_signatures/introduction_schnorr_signatures.html#musig) -/// for details. -/// -/// The API is fairly straightforward and is best illustrated with an example. Alice and Bob are going to construct a -/// 2-of-2 aggregated signature. -/// -/// ```edition2018 -/// # use tari_crypto::ristretto::{ musig::RistrettoMuSig, ristretto_keys::* }; -/// # use tari_utilities::ByteArray; -/// # use tari_crypto::keys::PublicKey; -/// # use sha2::Sha256; -/// # use digest::Digest; -/// let mut rng = rand::OsRng::new().unwrap(); -/// // Create a new MuSig instance. The number of signing parties must be known at this time. -/// let mut alice = RistrettoMuSig::::new(2); -/// let mut bob = RistrettoMuSig::::new(2); -/// // Set the message. This can only be done once to prevent replay attacks. Any attempt to assign another -/// // message will result in a Failure state. -/// alice = alice.set_message(b"Discworld"); -/// bob = bob.set_message(b"Discworld"); -/// // Collect public keys -/// let (k_a, p_a) = RistrettoPublicKey::random_keypair(&mut rng); -/// let (k_b, p_b) = RistrettoPublicKey::random_keypair(&mut rng); -/// // Add public keys to MuSig (in any order. They get sorted automatically when _n_ keys have been collected. -/// alice = alice -/// .add_public_key(&p_a) -/// .add_public_key(&p_b); -/// bob = bob -/// .add_public_key(&p_b) -/// .add_public_key(&p_a); -/// // Round 1 - Collect nonce hashes - each party does this individually and keeps the secret keys secret. -/// let (r_a, pr_a) = RistrettoPublicKey::random_keypair(&mut rng); -/// let (r_b, pr_b) = RistrettoPublicKey::random_keypair(&mut rng); -/// let h_a = Sha256::digest(pr_a.as_bytes()).to_vec(); -/// let h_b = Sha256::digest(pr_b.as_bytes()).to_vec(); -/// bob = bob -/// .add_nonce_commitment(&p_b, h_b.clone()) -/// .add_nonce_commitment(&p_a, h_a.clone()); -/// // State automatically updates: -/// assert!(bob.is_collecting_nonces()); -/// alice = alice -/// .add_nonce_commitment(&p_a, h_a.clone()) -/// .add_nonce_commitment(&p_b, h_b.clone()); -/// assert!(alice.is_collecting_nonces()); -/// // Round 2 - Collect Nonces -/// bob = bob -/// .add_nonce(&p_b, pr_b.clone()) -/// .add_nonce(&p_a, pr_a.clone()); -/// assert!(bob.is_collecting_signatures()); -/// alice = alice -/// .add_nonce(&p_a, pr_a.clone()) -/// .add_nonce(&p_b, pr_b.clone()); -/// assert!(alice.is_collecting_signatures()); -/// // round 3 - Collect partial signatures -/// let s_a = alice.calculate_partial_signature(&p_a, &k_a, &r_a).unwrap(); -/// let s_b = bob.calculate_partial_signature(&p_b, &k_b, &r_b).unwrap(); -/// alice = alice -/// .add_signature(&s_a, true) -/// .add_signature(&s_b, true); -/// assert!(alice.is_finalized()); -/// bob = bob -/// .add_signature(&s_b, true) -/// .add_signature(&s_a, true); -/// assert!(bob.is_finalized()); -/// assert_eq!(alice.get_aggregated_signature(), bob.get_aggregated_signature()); -/// ``` -pub struct RistrettoMuSig { - state: MuSigState, - digest_type: PhantomData, -} - -//---------------------------------------------- RistrettoMuSig -------------------------------------------// - -impl RistrettoMuSig { - /// Create a new, empty MuSig ceremony for _n_ participants - pub fn new(n: usize) -> RistrettoMuSig { - let state = match Initialization::new::(n) { - Ok(s) => MuSigState::Initialization(s), - Err(e) => MuSigState::Failed(e), - }; - RistrettoMuSig { - state, - digest_type: PhantomData, - } - } - - /// Convenience wrapper function to determined whether a signing ceremony has failed - pub fn has_failed(&self) -> bool { - match self.state { - MuSigState::Failed(_) => true, - _ => false, - } - } - - /// IF `has_failed()` is true, you can obtain the specific error that caused the failure - pub fn failure_reason(&self) -> Option { - match &self.state { - MuSigState::Failed(e) => Some(e.clone()), - _ => None, - } - } - - /// Convenience function to determine whether we're in Round One of MuSig (nonce hash collection) - pub fn is_collecting_hashes(&self) -> bool { - match self.state { - MuSigState::NonceHashCollection(_) => true, - _ => false, - } - } - - /// Convenience function to determine whether we'rein Round Two of MuSig (public nonce collection) - pub fn is_collecting_nonces(&self) -> bool { - match self.state { - MuSigState::NonceCollection(_) => true, - _ => false, - } - } - - /// Convenience function to determine whether we're in Round Three of MuSig (partial signature collection) - pub fn is_collecting_signatures(&self) -> bool { - match self.state { - MuSigState::SignatureCollection(_) => true, - _ => false, - } - } - - /// Convenience function to determine whether The MuSig protocol is complete (the aggregate signature is ready) - pub fn is_finalized(&self) -> bool { - match self.state { - MuSigState::Finalized(_) => true, - _ => false, - } - } - - /// Return the index of the public key in the MuSig ceremony. If were still collecting public keys, the state has - /// been finalised, or the pub_key isn't in the list, then None is returned. - pub fn index_of(&self, pub_key: &RistrettoPublicKey) -> Option { - let joint_key = match &self.state { - MuSigState::NonceHashCollection(s) => &s.joint_key, - MuSigState::NonceCollection(s) => &s.joint_key, - MuSigState::SignatureCollection(s) => &s.joint_key, - _ => return None, - }; - joint_key.index_of(pub_key).ok() - } - - /// Add a public key to the MuSig ceremony. Public keys can only be added in the `Initialization` state and the - /// MuSig state will only progress to the next state (nonce hash collection) once exactly `n` unique public keys - /// have been added. - pub fn add_public_key(self, key: &RistrettoPublicKey) -> Self { - let key = key.clone(); - self.handle_event(MuSigEvent::AddKey(key)) - } - - /// Set the message to be signed in this MuSig ceremony - pub fn set_message(self, msg: &[u8]) -> Self { - let msg = D::digest(msg).to_vec(); - self.handle_event(MuSigEvent::SetMessage(msg)) - } - - /// Adds a Round 1 public nonce commitment to the MuSig state. Once _n_ commitments have been collected, the - /// MuSig state will automatically switch to nonce collection. - pub fn add_nonce_commitment(self, pub_key: &RistrettoPublicKey, commitment: MessageHash) -> Self { - self.handle_event(MuSigEvent::AddNonceHash(pub_key, commitment)) - } - - /// Adds a public nonce to the MuSig ceremony. Be careful never to re-use public nonces for different MuSig - /// ceremonies. This risk is mitigated by the MuSig object taking ownership of the nonce, meaning that you need - /// to explicitly call `clone()` on your nonce if you want to use it elsewhere. - /// The MuSig state will automatically switch to `SignatureCollection` once _n_ valid nonces have been collected. - pub fn add_nonce(self, pub_key: &RistrettoPublicKey, nonce: RistrettoPublicKey) -> Self { - self.handle_event(MuSigEvent::AddNonce(pub_key, nonce)) - } - - /// Adds a partial signature to the MuSig ceremony. Each party can calculate their own partial signature by - /// calling `calculate_partial_signature(k, r)` and share the result with the other signing parties. You can - /// choose to validate each partial signature as it is added (in which case, if the state reaches Finalized, the - /// aggregate signature will automatically be valid). This is slower than just checking the aggregate signature, - /// but you will also know exactly _which_ signature failed. - /// Otherwise pass `false` to `should_validate` and verify the aggregate signature. - pub fn add_signature(self, s: &RistrettoSchnorr, should_validate: bool) -> Self { - self.handle_event(MuSigEvent::AddPartialSig(Box::new(s.clone()), should_validate)) - } - - /// Return a reference to the standard challenge $$ H(R_{agg} || P_{agg} || m) $$, or `None` if the requisite data - /// isn't available - pub fn get_challenge(&self) -> Option<&RistrettoSecretKey> { - match &self.state { - MuSigState::SignatureCollection(s) => Some(&s.challenge), - MuSigState::Finalized(s) => Some(&s.challenge), - _ => None, - } - } - - /// If the MuSig ceremony is finalised, returns a reference to the aggregated signature, otherwise returns None. - /// This function returns a standard Schnorr signature, so you can use it anywhere you can use a - /// Schnorr signature. - pub fn get_aggregated_signature(&self) -> Option<&RistrettoSchnorr> { - match &self.state { - MuSigState::Finalized(s) => Some(&s.s_agg), - _ => None, - } - } - - /// Once all public keys have been collected, this function returns a reference to the joint public key as - /// defined by the MuSig algorithm. If public keys are still being collected, this returns None. - pub fn get_aggregated_public_key(&self) -> Option<&RistrettoPublicKey> { - match &self.state { - MuSigState::NonceHashCollection(s) => Some(s.joint_key.get_joint_pubkey()), - MuSigState::NonceCollection(s) => Some(s.joint_key.get_joint_pubkey()), - MuSigState::SignatureCollection(s) => Some(s.joint_key.get_joint_pubkey()), - MuSigState::Finalized(s) => Some(s.joint_key.get_joint_pubkey()), - _ => None, - } - } - - fn get_public_nonce(&self, index: usize) -> Option<&RistrettoPublicKey> { - match &self.state { - MuSigState::SignatureCollection(s) => s.public_nonces.get_item(index), - _ => None, - } - } - - /// Calculate my partial MuSig signature, based on the information collected in the MuSig ceremony to date, using - /// the secret key and secret nonce supplied. - pub fn calculate_partial_signature( - &self, - pub_key: &RistrettoPublicKey, - secret: &RistrettoSecretKey, - nonce: &RistrettoSecretKey, - ) -> Option - { - let index = self.index_of(pub_key)?; - let pub_nonce = self.get_public_nonce(index)?; - let ai = self.get_musig_scalar(pub_key)?; - let e = self.get_challenge()?; - let s = nonce + ai * e * secret; - let sig = SchnorrSignature::new(pub_nonce.clone(), s); - Some(sig) - } - - /// Once all public keys have been collected, this function returns a reference to the joint public key as - /// defined by the MuSig algorithm. If public keys are still being collected, this returns None. - pub fn get_musig_scalar(&self, pub_key: &RistrettoPublicKey) -> Option<&RistrettoSecretKey> { - let jk = match &self.state { - MuSigState::NonceHashCollection(s) => &s.joint_key, - MuSigState::NonceCollection(s) => &s.joint_key, - MuSigState::SignatureCollection(s) => &s.joint_key, - MuSigState::Finalized(s) => &s.joint_key, - _ => return None, - }; - match jk.index_of(pub_key) { - Ok(i) => Some(jk.get_musig_scalar(i)), - Err(_) => None, - } - } - - /// Private convenience function that returns a Failed state with the `InvalidStateTransition` error - fn invalid_transition() -> MuSigState { - MuSigState::Failed(MuSigError::InvalidStateTransition) - } - - /// Implement a finite state machine. Each combination of State and Event is handled here; for each combination, a - /// new state is determined, consuming the old one. If `MuSigState::Failed` is ever returned, the protocol must be - /// abandoned. - fn handle_event(self, event: MuSigEvent) -> Self { - let state = match self.state { - // On initialization, you can add keys until you reach `num_signers` at which point the state - // automatically flips to `NonceHashCollection`; we're forced to use nested patterns because of error - MuSigState::Initialization(s) => match event { - MuSigEvent::AddKey(p) => s.add_pubkey::(p), - MuSigEvent::SetMessage(m) => s.set_message(m), - _ => RistrettoMuSig::::invalid_transition(), - }, - // Nonce Hash collection - MuSigState::NonceHashCollection(s) => match event { - MuSigEvent::AddNonceHash(p, h) => s.add_nonce_hash::(p, h.clone()), - MuSigEvent::SetMessage(m) => s.set_message(m), - _ => RistrettoMuSig::::invalid_transition(), - }, - // Nonce Collection - MuSigState::NonceCollection(s) => match event { - MuSigEvent::AddNonce(p, nonce) => s.add_nonce::(p, nonce), - MuSigEvent::SetMessage(m) => s.set_message::(m), - _ => RistrettoMuSig::::invalid_transition(), - }, - // Signature collection - MuSigState::SignatureCollection(s) => match event { - MuSigEvent::AddPartialSig(sig, validate) => s.add_partial_signature::(*sig, validate), - _ => RistrettoMuSig::::invalid_transition(), - }, - // There's no way back from a Failed State. - MuSigState::Failed(_) => RistrettoMuSig::::invalid_transition(), - _ => RistrettoMuSig::::invalid_transition(), - }; - RistrettoMuSig { - state, - digest_type: PhantomData, - } - } -} - -//------------------------------------ RistrettoMuSig Event Definitions ---------------------------------------------// - -/// The set of possible input events that can occur during the MuSig signature aggregation protocol. -pub enum MuSigEvent<'a> { - /// This event is used to add a new public key to the pool of participant keys - AddKey(RistrettoPublicKey), - /// Provides the message to be signed for the MuSig protocol - SetMessage(MessageHash), - /// This event is used by participants to commit the the public nonce that they will be using the signature - /// aggregation ceremony - AddNonceHash(&'a RistrettoPublicKey, MessageHash), - /// This event is used to add a public nonce to the pool of nonces for a particular signing ceremony - AddNonce(&'a RistrettoPublicKey, RistrettoPublicKey), - /// In the 3rd round of MuSig, participants provide their partial signatures, after which any party can - /// calculate the aggregated signature. - AddPartialSig(Box, bool), -} - -//------------------------------------- RistrettoMuSig State Definitions ------------------------------------------// - -/// This (private) enum represents the set of states that define the MuSig protocol. Metadata relevant to a given -/// state is supplied as an associated struct of the same name as the struct. Illegal state transitions are prevented -/// by a) there being no way to move from a given state's methods to another state using an invalid transition and b) -/// the global `match` clause in the [RistrettoMuSig](structs.RistrettoMuSig.html) struct implementation. Any invalid -/// transition attempt leads to the `Failed` state. -enum MuSigState { - Initialization(Initialization), - NonceHashCollection(Box), - NonceCollection(Box), - SignatureCollection(Box), - Finalized(Box), - Failed(MuSigError), -} - -struct Initialization { - joint_key_builder: JKBuilder, - message: Option, -} - -impl Initialization { - pub fn new(n: usize) -> Result { - // Ristretto requires a 256 bit hash - if D::output_size() != 32 { - return Err(MuSigError::IncompatibleHashFunction); - } - let joint_key_builder = JKBuilder::new(n)?; - Ok(Initialization { - joint_key_builder, - message: None, - }) - } - - pub fn add_pubkey(mut self, key: RistrettoPublicKey) -> MuSigState { - match self.joint_key_builder.add_key(key) { - Ok(_) => { - if self.joint_key_builder.is_full() { - match self.joint_key_builder.build::() { - Ok(jk) => MuSigState::NonceHashCollection(Box::new(NonceHashCollection::new(jk, self.message))), - Err(e) => MuSigState::Failed(e), - } - } else { - MuSigState::Initialization(self) - } - }, - Err(e) => MuSigState::Failed(e), - } - } - - pub fn set_message(mut self, msg: MessageHash) -> MuSigState { - if self.message.is_some() { - return MuSigState::Failed(MuSigError::MessageAlreadySet); - } - self.message = Some(msg.to_vec()); - MuSigState::Initialization(self) - } -} - -struct NonceHashCollection { - joint_key: JointPubKey, - nonce_hashes: FixedSet, - message: Option, -} - -impl NonceHashCollection { - fn new(joint_key: JointPubKey, msg: Option) -> NonceHashCollection { - let n = joint_key.size(); - NonceHashCollection { - joint_key, - nonce_hashes: FixedSet::new(n), - message: msg, - } - } - - fn add_nonce_hash(mut self, pub_key: &RistrettoPublicKey, hash: MessageHash) -> MuSigState { - match self.joint_key.index_of(pub_key) { - Ok(i) => { - self.nonce_hashes.set_item(i, hash); - if self.nonce_hashes.is_full() { - MuSigState::NonceCollection(Box::new(NonceCollection::new(self))) - } else { - MuSigState::NonceHashCollection(Box::new(self)) - } - }, - Err(_) => MuSigState::Failed(MuSigError::ParticipantNotFound), - } - } - - pub fn set_message(mut self, msg: MessageHash) -> MuSigState { - if self.message.is_some() { - return MuSigState::Failed(MuSigError::MessageAlreadySet); - } - self.message = Some(msg); - MuSigState::NonceHashCollection(Box::new(self)) - } -} - -struct NonceCollection { - joint_key: JointPubKey, - nonce_hashes: FixedSet, - public_nonces: FixedSet, - message: Option, -} - -impl NonceCollection { - fn new(init: NonceHashCollection) -> NonceCollection { - let n = init.joint_key.size(); - NonceCollection { - joint_key: init.joint_key, - nonce_hashes: init.nonce_hashes, - public_nonces: FixedSet::new(n), - message: init.message, - } - } - - fn is_valid_nonce(nonce: &RistrettoPublicKey, expected: &MessageHashSlice) -> bool { - let calc = D::digest(nonce.as_bytes()).to_vec(); - &calc[..] == expected - } - - // We definitely want to consume `nonce` here to discourage nonce re-use - fn add_nonce(mut self, pub_key: &RistrettoPublicKey, nonce: RistrettoPublicKey) -> MuSigState { - match self.joint_key.index_of(pub_key) { - Ok(i) => { - // Check that the nonce matches the commitment - let expected = self.nonce_hashes.get_item(i); - if expected.is_none() { - return MuSigState::Failed(MuSigError::MissingHash); - } - if !NonceCollection::is_valid_nonce::(&nonce, expected.unwrap()) { - return MuSigState::Failed(MuSigError::MismatchedNonces); - } - self.public_nonces.set_item(i, nonce); - // Transition to round three iff we have all the nonces and the message has been set - if self.public_nonces.is_full() && self.message.is_some() { - MuSigState::SignatureCollection(Box::new(SignatureCollection::new::(self))) - } else { - MuSigState::NonceCollection(Box::new(self)) - } - }, - Err(_) => MuSigState::Failed(MuSigError::ParticipantNotFound), - } - } - - pub fn set_message(mut self, msg: MessageHash) -> MuSigState { - if self.message.is_some() { - return MuSigState::Failed(MuSigError::MessageAlreadySet); - } - self.message = Some(msg); - if self.public_nonces.is_full() { - MuSigState::SignatureCollection(Box::new(SignatureCollection::new::(self))) - } else { - MuSigState::NonceCollection(Box::new(self)) - } - } -} - -struct SignatureCollection { - joint_key: JointPubKey, - public_nonces: FixedSet, - partial_signatures: FixedSet, - challenge: RistrettoSecretKey, -} - -impl SignatureCollection { - fn new(init: NonceCollection) -> SignatureCollection { - let n = init.joint_key.size(); - let agg_nonce = init.public_nonces.sum().unwrap(); - let message = init.message.unwrap(); - let challenge = - SignatureCollection::calculate_challenge::(&agg_nonce, init.joint_key.get_joint_pubkey(), &message); - SignatureCollection { - joint_key: init.joint_key, - public_nonces: init.public_nonces, - partial_signatures: FixedSet::new(n), - challenge, - } - } - - fn calculate_challenge( - r_agg: &RistrettoPublicKey, - p_agg: &RistrettoPublicKey, - m: &MessageHashSlice, - ) -> RistrettoSecretKey - { - let e = D::new() - .chain(r_agg.as_bytes()) - .chain(p_agg.as_bytes()) - .chain(m) - .result(); - RistrettoSecretKey::from_bytes(&e).expect("Found a u256 that does not map to a valid Ristretto scalar") - } - - fn validate_partial_signature(&self, index: usize, signature: &RistrettoSchnorr) -> bool { - // s_i = r_i + a_i k_i e, so - // s_i.G = R_i + a_i P_i e - let pub_key = self.joint_key.get_pub_keys(index); - let a_i = self.joint_key.get_musig_scalar(index); - let p = a_i * pub_key; - let e = &self.challenge; - signature.verify(&p, e) - } - - fn calculate_agg_signature(&self) -> RistrettoSchnorr { - self.partial_signatures.sum().unwrap() - } - - fn set_signature(mut self, index: usize, signature: RistrettoSchnorr) -> MuSigState { - if !self.partial_signatures.set_item(index, signature) { - return MuSigState::Failed(MuSigError::MismatchedSignatures); - } - if self.partial_signatures.is_full() { - MuSigState::Finalized(Box::new(FinalizedMuSig::new(self))) - } else { - MuSigState::SignatureCollection(Box::new(self)) - } - } - - fn add_partial_signature(self, signature: RistrettoSchnorr, validate: bool) -> MuSigState { - match self.public_nonces.search(signature.get_public_nonce()) { - None => MuSigState::Failed(MuSigError::ParticipantNotFound), - Some(i) => { - if validate && !self.validate_partial_signature::(i, &signature) { - MuSigState::Failed(MuSigError::InvalidPartialSignature(i)) - } else { - self.set_signature::(i, signature) - } - }, - } - } -} - -struct FinalizedMuSig { - s_agg: RistrettoSchnorr, - challenge: RistrettoSecretKey, - joint_key: JointPubKey, -} - -impl FinalizedMuSig { - fn new(init: SignatureCollection) -> Self { - let s_agg = init.calculate_agg_signature(); - let joint_key = init.joint_key; - let challenge = init.challenge; - FinalizedMuSig { - s_agg, - challenge, - joint_key, - } - } -} - -//-------------------------------------------------------------------------------------------------------------------// -//------------------------------------ Tests -----------------------------------------// -//------------------------------------ -------------------------------------------------------------------------------// - -#[cfg(test)] -mod test { - use super::*; - use crate::keys::{PublicKey, SecretKey}; - use rand::{CryptoRng, Rng}; - use sha2::Sha256; - - struct MuSigTestData { - pub pub_keys: Vec, - pub indices: Vec, - // The position of the data in the sorted list - pub secret_keys: Vec, - pub nonces: Vec, - pub public_nonces: Vec, - pub r_agg: RistrettoPublicKey, - pub nonce_hashes: Vec, - pub partial_sigs: Vec, - } - - fn get_key_and_nonce( - rng: &mut R, - ) -> ( - RistrettoSecretKey, - RistrettoPublicKey, - RistrettoSecretKey, - RistrettoPublicKey, - MessageHash, - ) { - let (k, pubkey) = RistrettoPublicKey::random_keypair(rng); - let (r, nonce) = RistrettoPublicKey::random_keypair(rng); - let hash = Sha256::digest(nonce.as_bytes()).to_vec(); - (k, pubkey, r, nonce, hash) - } - - /// Utility test function that creates a MuSig ceremony at Round 1, where public keys have been set and now we - /// are ready to accept nonce hashes. - /// You can also optionally provide a message at this stage to be signed. - /// The function returns the MuSig struct as well as a data structure that holds the secret and public keys, the - /// nonces and public nonces, and the nonce hashes to aid with testing - fn create_round_one_musig(n: usize, msg: Option<&[u8]>) -> (RistrettoMuSig, MuSigTestData) { - let mut rng = rand::OsRng::new().unwrap(); - let mut musig = RistrettoMuSig::::new(n); - let mut pub_keys = Vec::with_capacity(n); - let mut secret_keys = Vec::with_capacity(n); - let mut nonces = Vec::with_capacity(n); - let mut public_nonces = Vec::with_capacity(n); - let mut nonce_hashes = Vec::with_capacity(n); - let partial_sigs = Vec::with_capacity(n); - for _ in 0..n { - let (k, pk, r, pr, h) = get_key_and_nonce(&mut rng); - secret_keys.push(k); - pub_keys.push(pk); - nonces.push(r); - public_nonces.push(pr); - nonce_hashes.push(h); - } - for p in &pub_keys { - musig = musig.add_public_key(p); - } - let mut r_agg = public_nonces[0].clone(); - for r in public_nonces[1..].iter() { - r_agg = r_agg + r; - } - assert_eq!(musig.has_failed(), false); - if msg.is_some() { - musig = musig.set_message(msg.unwrap()); - } - // We should now have switched to Round 1 automatically - assert!(musig.is_collecting_hashes()); - // Collect the positions of the pubkeys in the sorted list - let indices = pub_keys.iter().map(|p| musig.index_of(p).unwrap()).collect(); - (musig, MuSigTestData { - pub_keys, - indices, - secret_keys, - nonces, - public_nonces, - r_agg, - nonce_hashes, - partial_sigs, - }) - } - - /// Utility test function that creates a MuSig ceremony at Round 2 (nonce collection). Building on from - /// `create_round_one_musig`, this function calls `MuSig::add_nonce_commitment` for each nonce hash in the test - /// data structure leaving the MuSig structure ready to accept public nonces. If the message is supplied, it is - /// added after the nonce commitments have been added - fn create_round_two_musig(n: usize, msg: Option<&[u8]>) -> (RistrettoMuSig, MuSigTestData) { - let (mut musig, data) = create_round_one_musig(n, None); - for (p, h) in data.pub_keys.iter().zip(&data.nonce_hashes) { - musig = musig.add_nonce_commitment(p, h.clone()); - } - assert_eq!(musig.has_failed(), false); - if msg.is_some() { - musig = musig.set_message(&msg.unwrap()); - } - // We should now have switched to Round 2 automatically - assert!(musig.is_collecting_nonces()); - (musig, data) - } - - /// Utility test function that creates a MuSig ceremony at Round 3 (signature collection). This function takes - /// the result from `create_round_two_musig` and adds the public nonces found in `data`. If the message is - /// provided, it is added after this. The MuSig structure that is returned is ready to accept partial signatures - fn create_round_three_musig(n: usize, msg: Option<&[u8]>) -> (RistrettoMuSig, MuSigTestData) { - let (mut musig, mut data) = create_round_two_musig(n, None); - for (p, r) in data.pub_keys.iter().zip(&data.public_nonces) { - musig = musig.add_nonce(p, r.clone()) - } - if msg.is_some() { - musig = musig.set_message(&msg.unwrap()); - } - assert!(musig.is_collecting_signatures()); - let e = musig.get_challenge().unwrap(); - // Calculate partial signatures - for (i, r) in data.nonces.iter().enumerate() { - let k = data.secret_keys.get(i).unwrap(); - let ai = musig.get_musig_scalar(data.pub_keys.get(i).unwrap()).unwrap(); - let sig = r + ai * e * k; - data.partial_sigs - .push(RistrettoSchnorr::new(data.public_nonces[i].clone(), sig)); - } - (musig, data) - } - - /// Utility test function to create a finalised MuSig struct: All partial signatures have been collected and - /// verified, and the sum of partial signatures is returned independently - fn create_final_musig(n: usize, msg: &[u8]) -> (RistrettoMuSig, MuSigTestData, RistrettoSchnorr) { - let (mut musig, data) = create_round_three_musig(n, Some(msg)); - assert_eq!(musig.has_failed(), false); - // Add the partial signatures - for s in data.partial_sigs.iter() { - musig = musig.add_signature(&s, true); - assert_eq!( - musig.has_failed(), - false, - "Partial signature addition failed. {:?}", - musig.failure_reason() - ); - } - let mut iter = data.partial_sigs.iter(); - let v0 = iter.next().unwrap().clone(); - let sum = iter.fold(v0, |acc, s| s + acc); - assert!(musig.is_finalized()); - (musig, data, sum) - } - - #[test] - fn add_too_many_pub_keys() { - let mut rng = rand::OsRng::new().unwrap(); - let musig = RistrettoMuSig::::new(2); - let (_, p1) = RistrettoPublicKey::random_keypair(&mut rng); - let (_, p2) = RistrettoPublicKey::random_keypair(&mut rng); - let (_, p3) = RistrettoPublicKey::random_keypair(&mut rng); - let musig = musig.add_public_key(&p1).add_public_key(&p2); - assert_eq!(musig.has_failed(), false); - let musig = musig.add_public_key(&p3); - assert!(musig.has_failed()); - assert_eq!(musig.failure_reason(), Some(MuSigError::InvalidStateTransition)); - } - - #[test] - fn zero_sized_musig() { - let musig = RistrettoMuSig::::new(0); - assert!(musig.has_failed()); - assert_eq!(musig.failure_reason(), Some(MuSigError::NotEnoughParticipants)); - } - - #[test] - fn duplicate_pub_key() { - let mut rng = rand::OsRng::new().unwrap(); - let musig = RistrettoMuSig::::new(3); - let (_, p1) = RistrettoPublicKey::random_keypair(&mut rng); - let (_, p2) = RistrettoPublicKey::random_keypair(&mut rng); - let musig = musig.add_public_key(&p1).add_public_key(&p2).add_public_key(&p1); - assert!(musig.has_failed()); - assert_eq!(musig.failure_reason(), Some(MuSigError::DuplicatePubKey)); - } - - #[test] - fn add_msg_in_round_one() { - let (mut musig, _) = create_round_one_musig(5, None); - musig = musig.set_message(b"Hello Discworld"); - assert_eq!(musig.has_failed(), false); - // Haven't collected nonces yet, so challenge is still undefined - assert_eq!(musig.get_challenge(), None); - } - - #[test] - fn must_wait_until_full() { - let mut rng = rand::OsRng::new().unwrap(); - let musig = RistrettoMuSig::::new(3); - let (k1, p1) = RistrettoPublicKey::random_keypair(&mut rng); - let (_, p2) = RistrettoPublicKey::random_keypair(&mut rng); - let mut musig = musig.add_public_key(&p1).add_public_key(&p2); - assert_eq!(musig.has_failed(), false); - musig = musig.add_nonce_commitment(&p1, k1.to_vec()); - assert!(musig.has_failed()); - assert_eq!(musig.failure_reason(), Some(MuSigError::InvalidStateTransition)); - } - - #[test] - fn cannot_add_more_keys_after_round0() { - let mut rng = rand::OsRng::new().unwrap(); - let (_, p) = RistrettoPublicKey::random_keypair(&mut rng); - let (mut musig, _) = create_round_one_musig(25, None); - // We can't add pub keys anymore! - musig = musig.add_public_key(&p); - assert!(musig.has_failed()); - assert_eq!(musig.failure_reason(), Some(MuSigError::InvalidStateTransition)); - } - - #[test] - fn must_wait_for_all_nonce_hashes() { - let (mut musig, data) = create_round_one_musig(3, None); - musig = musig.add_nonce_commitment(&data.pub_keys[2], data.nonce_hashes[2].clone()); - assert_eq!(musig.has_failed(), false); - // Try add nonce before all hashes have been collected - musig = musig.add_nonce(&data.pub_keys[2], data.public_nonces[2].clone()); - assert_eq!(musig.failure_reason(), Some(MuSigError::InvalidStateTransition)); - } - - #[test] - fn can_add_hashes_in_any_order() { - let (mut musig, data) = create_round_one_musig(3, None); - musig = musig - .add_nonce_commitment(&data.pub_keys[2], data.nonce_hashes[2].clone()) - .add_nonce_commitment(&data.pub_keys[0], data.nonce_hashes[0].clone()) - .add_nonce_commitment(&data.pub_keys[1], data.nonce_hashes[1].clone()); - assert!(musig.is_collecting_nonces()); - } - - #[test] - fn can_add_nonces_in_any_order() { - let (mut musig, data) = create_round_two_musig(3, Some(b"message")); - musig = musig - .add_nonce(&data.pub_keys[2], data.public_nonces[2].clone()) - .add_nonce(&data.pub_keys[0], data.public_nonces[0].clone()) - .add_nonce(&data.pub_keys[1], data.public_nonces[1].clone()); - assert!(musig.is_collecting_signatures()); - } - - #[test] - fn invalid_nonce_causes_failure() { - let (mut musig, data) = create_round_two_musig(25, None); - musig = musig.add_nonce(&data.pub_keys[0], data.public_nonces[1].clone()); - assert!(musig.has_failed()); - assert_eq!(musig.failure_reason(), Some(MuSigError::MismatchedNonces)); - } - - #[test] - fn invalid_partial_signature_causes_failure() { - let mut rng = rand::OsRng::new().unwrap(); - let (mut musig, data) = create_round_three_musig(15, Some(b"message")); - let s = RistrettoSecretKey::random(&mut rng); - // Create a signature with a valid nonce, but the signature is invalid - let bad_sig = RistrettoSchnorr::new(data.public_nonces[1].clone(), s); - let index = data.indices[1]; - musig = musig.add_signature(&bad_sig, true); - assert!(musig.has_failed()); - assert_eq!(musig.failure_reason(), Some(MuSigError::InvalidPartialSignature(index))); - } - - #[test] - fn bad_partial_signature_causes_failure() { - let mut rng = rand::OsRng::new().unwrap(); - let (mut musig, _) = create_round_three_musig(3, Some(b"message")); - let (s, r) = RistrettoPublicKey::random_keypair(&mut rng); - // Create a signature with an invalid nonce - let bad_sig = RistrettoSchnorr::new(r, s); - musig = musig.add_signature(&bad_sig, true); - assert!(musig.has_failed()); - assert_eq!(musig.failure_reason(), Some(MuSigError::ParticipantNotFound)); - } - - #[test] - fn adding_pubkey_after_initialization_causes_failure() { - let mut rng = rand::OsRng::new().unwrap(); - let (_, p, _, _, _) = get_key_and_nonce(&mut rng); - let (mut musig, _) = create_round_one_musig(5, None); - musig = musig.add_public_key(&p); - assert!(musig.has_failed()); - - let (mut musig, _) = create_round_two_musig(5, None); - musig = musig.add_public_key(&p); - assert!(musig.has_failed()); - - let (mut musig, _) = create_round_three_musig(5, Some(b"message")); - musig = musig.add_public_key(&p); - assert!(musig.has_failed()); - } - - #[test] - fn aggregated_signature_validates() { - let (musig, data, s_agg) = create_final_musig(15, b"message"); - let sig = musig.get_aggregated_signature().unwrap(); - let p_agg = musig.get_aggregated_public_key().unwrap(); - let m_hash = Sha256::digest(b"message"); - let challenge = Sha256::new() - .chain(data.r_agg.as_bytes()) - .chain(p_agg.as_bytes()) - .chain(&m_hash) - .result(); - assert!(sig.verify_challenge(p_agg, &challenge)); - assert_eq!(&s_agg, sig); - } - - #[test] - fn multiparty_musig() { - // Everyone sets up their MuSig - let m = b"Discworld".to_vec(); - let (mut alice, data) = create_round_three_musig(2, Some(&m)); - // Aliases to Alice's and Bob's public key - let p_a = data.pub_keys.get(0).unwrap(); - let p_b = data.pub_keys.get(1).unwrap(); - let mut bob = RistrettoMuSig::::new(2); - // Setup Bob's MuSig - bob = bob - .add_public_key(p_b) - .add_public_key(p_a) - // Round 1 - Collect nonce hashes - .add_nonce_commitment(p_b, data.nonce_hashes[1].clone()) - .add_nonce_commitment(p_a, data.nonce_hashes[0].clone()) - // Round 2 - Collect Nonces - .add_nonce(p_b, data.public_nonces[1].clone()) - .add_nonce(p_a, data.public_nonces[0].clone()) - .set_message(&m); - assert!(bob.is_collecting_signatures()); - // round 3 - Collect partial signatures - let s_a = alice - .calculate_partial_signature(p_a, &data.secret_keys[0], &data.nonces[0]) - .unwrap(); - let s_b = bob - .calculate_partial_signature(p_b, &data.secret_keys[1], &data.nonces[1]) - .unwrap(); - alice = alice.add_signature(&s_a, true).add_signature(&s_b, true); - assert!(alice.is_finalized()); - bob = bob.add_signature(&s_b, true).add_signature(&s_a, true); - assert!(bob.is_finalized()); - assert_eq!(alice.get_aggregated_signature(), bob.get_aggregated_signature()); - } -} - -#[cfg(test)] -mod test_joint_key { - use super::*; - use crate::{keys::PublicKey, musig::MAX_SIGNATURES}; - use sha2::Sha256; - use tari_utilities::hex::Hex; - - #[test] - fn zero_sized_jk() { - let jk = JKBuilder::new(0); - assert_eq!(jk.err().unwrap(), MuSigError::NotEnoughParticipants); - } - - #[test] - fn too_many_participants() { - let jk = JKBuilder::new(MAX_SIGNATURES + 1); - assert_eq!(jk.err().unwrap(), MuSigError::TooManyParticipants); - } - - #[test] - fn too_many_keys() { - let mut rng = rand::OsRng::new().unwrap(); - let mut jk = JKBuilder::new(2).unwrap(); - assert_eq!(jk.num_signers(), 2); - let (_, p1) = RistrettoPublicKey::random_keypair(&mut rng); - let (_, p2) = RistrettoPublicKey::random_keypair(&mut rng); - let (_, p3) = RistrettoPublicKey::random_keypair(&mut rng); - // Add first key - assert_eq!(jk.key_exists(&p1), false); - assert_eq!(jk.add_key(p1).unwrap(), 1); - assert_eq!(jk.is_full(), false); - // Add second key - assert_eq!(jk.key_exists(&p2), false); - assert_eq!(jk.add_key(p2).unwrap(), 2); - assert!(jk.is_full()); - // Try add third key - assert_eq!(jk.key_exists(&p3), false); - assert_eq!(jk.add_key(p3).err(), Some(MuSigError::TooManyParticipants)); - } - - #[test] - fn duplicate_key() { - let mut rng = rand::OsRng::new().unwrap(); - let mut jk = JKBuilder::new(3).unwrap(); - let (_, p1) = RistrettoPublicKey::random_keypair(&mut rng); - let (_, p2) = RistrettoPublicKey::random_keypair(&mut rng); - // Add first key - assert_eq!(jk.key_exists(&p1), false); - assert_eq!(jk.add_key(p1.clone()).unwrap(), 1); - assert_eq!(jk.is_full(), false); - // Add second key - assert_eq!(jk.key_exists(&p2), false); - assert_eq!(jk.add_key(p2).unwrap(), 2); - assert_eq!(jk.is_full(), false); - // Try add third key - assert_eq!(jk.key_exists(&p1), true); - assert_eq!(jk.add_key(p1).err(), Some(MuSigError::DuplicatePubKey)); - } - - #[test] - fn three_keys() { - let mut key_builder = JKBuilder::new(3).unwrap(); - assert_eq!(key_builder.num_signers(), 3); - let p1 = - RistrettoPublicKey::from_hex("aa52e000df2e16f55fb1032fc33bc42742dad6bd5a8fc0be0167436c5948501f").unwrap(); - let p2 = - RistrettoPublicKey::from_hex("46376b80f409b29dc2b5f6f0c52591990896e5716f41477cd30085ab7f10301e").unwrap(); - let p3 = - RistrettoPublicKey::from_hex("e0c418f7c8d9c4cdd7395b93ea124f3ad99021bb681dfc3302a9d99a2e53e64e").unwrap(); - assert_eq!( - key_builder.add_keys(vec![p1.clone(), p2.clone(), p3.clone()]).unwrap(), - 3 - ); - assert!(key_builder.is_full()); - let joint_key = key_builder.build::().unwrap(); - assert_eq!(joint_key.size(), 3); - // The keys have been sorted - assert_eq!(joint_key.get_pub_keys(0), &p2); - assert_eq!(joint_key.get_pub_keys(1), &p1); - assert_eq!(joint_key.get_pub_keys(2), &p3); - // Calculate ell and partials - let ell = Sha256::new() - .chain(p2.as_bytes()) - .chain(p1.as_bytes()) - .chain(p3.as_bytes()) - .result() - .to_vec(); - // Check Ell - let ell = RistrettoSecretKey::from_vec(&ell).unwrap(); - assert_eq!(joint_key.get_common(), &ell); - // Check partial scalars - let hash = |p: &RistrettoPublicKey| { - let h = Sha256::new() - .chain(ell.as_bytes()) - .chain(p.as_bytes()) - .result() - .to_vec(); - RistrettoSecretKey::from_vec(&h).unwrap() - }; - let a1 = hash(&p1); - let a2 = hash(&p2); - let a3 = hash(&p3); - assert_eq!(joint_key.get_musig_scalar(0), &a2); - assert_eq!(joint_key.get_musig_scalar(1), &a1); - assert_eq!(joint_key.get_musig_scalar(2), &a3); - // Check joint public key - let key = (a1 * p1) + (a2 * p2) + (a3 * p3); - assert_eq!(joint_key.get_joint_pubkey(), &key); - } -} diff --git a/infrastructure/crypto/src/ristretto/pedersen.rs b/infrastructure/crypto/src/ristretto/pedersen.rs deleted file mode 100644 index fd0bfedc03..0000000000 --- a/infrastructure/crypto/src/ristretto/pedersen.rs +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::{ - commitment::HomomorphicCommitment, - ristretto::{constants::RISTRETTO_NUMS_POINTS, RistrettoPublicKey}, -}; -use curve25519_dalek::{constants::RISTRETTO_BASEPOINT_POINT, ristretto::RistrettoPoint, traits::MultiscalarMul}; - -use crate::{commitment::HomomorphicCommitmentFactory, ristretto::RistrettoSecretKey}; -use curve25519_dalek::scalar::Scalar; -use std::{borrow::Borrow, iter::Sum}; - -pub const RISTRETTO_PEDERSEN_G: RistrettoPoint = RISTRETTO_BASEPOINT_POINT; -lazy_static! { - pub static ref RISTRETTO_PEDERSEN_H: RistrettoPoint = RISTRETTO_NUMS_POINTS[0]; -} - -pub type PedersenCommitment = HomomorphicCommitment; - -#[derive(Debug, PartialEq, Eq, Clone)] -#[allow(non_snake_case)] -pub struct PedersenCommitmentFactory { - pub(crate) G: RistrettoPoint, - pub(crate) H: RistrettoPoint, -} - -impl PedersenCommitmentFactory { - /// Create a new Ristretto Commitment factory with the given points as the bases. It's very cheap to create - /// factories, since we only hold references to the static generator points. - #[allow(non_snake_case)] - pub fn new(G: RistrettoPoint, H: RistrettoPoint) -> PedersenCommitmentFactory { - PedersenCommitmentFactory { G, H } - } -} - -/// The default Ristretto Commitment factory uses the Base point for x25519 and its first Blake256 hash. -impl Default for PedersenCommitmentFactory { - fn default() -> Self { - PedersenCommitmentFactory::new(RISTRETTO_PEDERSEN_G, *RISTRETTO_PEDERSEN_H) - } -} - -impl HomomorphicCommitmentFactory for PedersenCommitmentFactory { - type P = RistrettoPublicKey; - - fn commit(&self, k: &RistrettoSecretKey, v: &RistrettoSecretKey) -> PedersenCommitment { - let c = RistrettoPoint::multiscalar_mul(&[v.0, k.0], &[self.H, self.G]); - HomomorphicCommitment(RistrettoPublicKey::new_from_pk(c)) - } - - fn zero(&self) -> PedersenCommitment { - let zero = Scalar::zero(); - let c = RistrettoPoint::multiscalar_mul(&[zero, zero], &[self.H, self.G]); - HomomorphicCommitment(RistrettoPublicKey::new_from_pk(c)) - } - - fn open(&self, k: &RistrettoSecretKey, v: &RistrettoSecretKey, commitment: &PedersenCommitment) -> bool { - let c_test = self.commit(k, v); - commitment.0 == c_test.0 - } - - fn commit_value(&self, k: &RistrettoSecretKey, value: u64) -> PedersenCommitment { - let v = RistrettoSecretKey::from(value); - self.commit(k, &v) - } - - fn open_value(&self, k: &RistrettoSecretKey, v: u64, commitment: &HomomorphicCommitment) -> bool { - let kv = RistrettoSecretKey::from(v); - self.open(k, &kv, commitment) - } -} - -impl Sum for PedersenCommitment -where T: Borrow -{ - fn sum(iter: I) -> Self - where I: Iterator { - let mut total = RistrettoPoint::default(); - for c in iter { - let commitment = c.borrow(); - total += (commitment.0).point - } - let sum = RistrettoPublicKey::new_from_pk(total); - HomomorphicCommitment(sum) - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::keys::SecretKey; - use rand; - use std::convert::From; - use tari_utilities::message_format::MessageFormat; - - #[test] - fn check_default_base() { - let base = PedersenCommitmentFactory::default(); - assert_eq!(base.G, RISTRETTO_PEDERSEN_G); - assert_eq!(base.H, *RISTRETTO_PEDERSEN_H) - } - - #[test] - fn check_g_ne_h() { - assert_ne!(RISTRETTO_PEDERSEN_G, *RISTRETTO_PEDERSEN_H); - } - - /// Simple test for open: Generate 100 random sets of scalars and calculate the Pedersen commitment for them. - /// Then check that the commitment = k.G + v.H, and that `open` returns `true` for `open(&k, &v)` - #[test] - #[allow(non_snake_case)] - fn check_open() { - let factory = PedersenCommitmentFactory::default(); - let H = RISTRETTO_PEDERSEN_H.clone(); - let mut rng = rand::OsRng::new().unwrap(); - for _ in 0..100 { - let v = RistrettoSecretKey::random(&mut rng); - let k = RistrettoSecretKey::random(&mut rng); - let c = factory.commit(&k, &v); - let c_calc: RistrettoPoint = v.0 * H + k.0 * RISTRETTO_PEDERSEN_G; - assert_eq!(RistrettoPoint::from(c.as_public_key()), c_calc); - assert!(factory.open(&k, &v, &c)); - // A different value doesn't open the commitment - assert!(!factory.open(&k, &(&v + &v), &c)); - // A different blinding factor doesn't open the commitment - assert!(!factory.open(&(&k + &v), &v, &c)); - } - } - - /// Test, for 100 random sets of scalars that the homomorphic property holds. i.e. - /// $$ - /// C = C_1 + C_2 = (k_1+k_2).G + (v_1+v_2).H - /// $$ - /// and - /// `open(k1+k2, v1+v2)` is true for _C_ - #[test] - fn check_homomorphism() { - let mut rng = rand::OsRng::new().unwrap(); - for _ in 0..100 { - let v1 = RistrettoSecretKey::random(&mut rng); - let v2 = RistrettoSecretKey::random(&mut rng); - let v_sum = &v1 + &v2; - let k1 = RistrettoSecretKey::random(&mut rng); - let k2 = RistrettoSecretKey::random(&mut rng); - let k_sum = &k1 + &k2; - let factory = PedersenCommitmentFactory::default(); - let c1 = factory.commit(&k1, &v1); - let c2 = factory.commit(&k2, &v2); - let c_sum = &c1 + &c2; - let c_sum2 = factory.commit(&k_sum, &v_sum); - assert!(factory.open(&k1, &v1, &c1)); - assert!(factory.open(&k2, &v2, &c2)); - assert_eq!(c_sum, c_sum2); - assert!(factory.open(&k_sum, &v_sum, &c_sum)); - } - } - - #[test] - fn sum_commitment_vector() { - let mut rng = rand::OsRng::new().unwrap(); - let mut v_sum = RistrettoSecretKey::default(); - let mut k_sum = RistrettoSecretKey::default(); - let zero = RistrettoSecretKey::default(); - let commitment_factory = PedersenCommitmentFactory::default(); - let mut c_sum = commitment_factory.commit(&zero, &zero); - let mut commitments = Vec::with_capacity(100); - for _ in 0..100 { - let v = RistrettoSecretKey::random(&mut rng); - v_sum = &v_sum + &v; - let k = RistrettoSecretKey::random(&mut rng); - k_sum = &k_sum + &k; - let c = commitment_factory.commit(&k, &v); - c_sum = &c_sum + &c; - commitments.push(c); - } - assert!(commitment_factory.open(&k_sum, &v_sum, &c_sum)); - assert_eq!(c_sum, commitments.iter().sum()); - } - - #[test] - fn serialize_deserialize() { - let mut rng = rand::OsRng::new().unwrap(); - let factory = PedersenCommitmentFactory::default(); - let k = RistrettoSecretKey::random(&mut rng); - let c = factory.commit_value(&k, 420); - // Base64 - let ser_c = c.to_base64().unwrap(); - let c2 = PedersenCommitment::from_base64(&ser_c).unwrap(); - assert!(factory.open_value(&k, 420, &c2)); - // MessagePack - let ser_c = c.to_binary().unwrap(); - let c2 = PedersenCommitment::from_binary(&ser_c).unwrap(); - assert!(factory.open_value(&k, 420, &c2)); - } -} diff --git a/infrastructure/crypto/src/ristretto/ristretto_keys.rs b/infrastructure/crypto/src/ristretto/ristretto_keys.rs deleted file mode 100644 index d7081c99dc..0000000000 --- a/infrastructure/crypto/src/ristretto/ristretto_keys.rs +++ /dev/null @@ -1,677 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -//! The Tari-compatible implementation of Ristretto based on the curve25519-dalek implementation -use crate::keys::{DiffieHellmanSharedSecret, PublicKey, SecretKey}; -use blake2::Blake2b; -use clear_on_drop::clear::Clear; -use curve25519_dalek::{ - constants::RISTRETTO_BASEPOINT_TABLE, - ristretto::{CompressedRistretto, RistrettoPoint}, - scalar::Scalar, - traits::MultiscalarMul, -}; -use digest::Digest; -use rand::{CryptoRng, Rng}; -use std::{ - cmp::Ordering, - fmt, - hash::{Hash, Hasher}, - ops::{Add, Mul, Sub}, -}; -use tari_utilities::{hex::Hex, ByteArray, ByteArrayError, ExtendBytes, Hashable}; - -type HashDigest = Blake2b; - -/// The [SecretKey](trait.SecretKey.html) implementation for [Ristretto](https://ristretto.group) is a thin wrapper -/// around the Dalek [Scalar](struct.Scalar.html) type, representing a 256-bit integer (mod the group order). -/// -/// ## Creating secret keys -/// [ByteArray](trait.ByteArray.html) and [SecretKeyFactory](trait.SecretKeyFactory.html) are implemented for -/// [SecretKey](struct .SecretKey.html), so any of the following work (note that hex strings and byte array are -/// little-endian): -/// -/// ```edition2018 -/// use tari_crypto::ristretto::RistrettoSecretKey; -/// use tari_utilities::{ ByteArray, hex::Hex }; -/// use tari_crypto::keys::SecretKey; -/// use rand; -/// -/// let mut rng = rand::OsRng::new().unwrap(); -/// let _k1 = RistrettoSecretKey::from_bytes(&[1,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]); -/// let _k2 = RistrettoSecretKey::from_hex(&"100000002000000030000000040000000"); -/// let _k3 = RistrettoSecretKey::random(&mut rng); -/// ``` -#[derive(PartialEq, Eq, Clone, Debug)] -pub struct RistrettoSecretKey(pub(crate) Scalar); - -const SCALAR_LENGTH: usize = 32; -const PUBLIC_KEY_LENGTH: usize = 32; - -//----------------------------------------- Ristretto Secret Key ------------------------------------------------// -impl SecretKey for RistrettoSecretKey { - fn key_length() -> usize { - SCALAR_LENGTH - } - - /// Return a random secret key on the `ristretto255` curve using the supplied CSPRNG. - fn random(rng: &mut R) -> Self { - RistrettoSecretKey(Scalar::random(rng)) - } -} - -//---------------------------------- Ristretto Secret Key Default -----------------------------------------------// - -impl Default for RistrettoSecretKey { - fn default() -> Self { - RistrettoSecretKey(Scalar::default()) - } -} - -//---------------------------------- Ristretto Secret Key Default -----------------------------------------------// - -/// Clear the secret key value in memory when it goes out of scope -impl Drop for RistrettoSecretKey { - fn drop(&mut self) { - self.0.clear(); - } -} - -//------------------------------------- Ristretto Secret Key ByteArray ---------------------------------------------// - -impl ByteArray for RistrettoSecretKey { - /// Create a secret key on the Ristretto255 curve using the given little-endian byte array. If the byte array is - /// not exactly 32 bytes long, `from_bytes` returns an error. This function is guaranteed to return a valid key - /// in the group since it performs a mod _l_ on the input. - fn from_bytes(bytes: &[u8]) -> Result - where Self: Sized { - if bytes.len() != 32 { - return Err(ByteArrayError::IncorrectLength); - } - let mut a = [0u8; 32]; - a.copy_from_slice(bytes); - let k = Scalar::from_bytes_mod_order(a); - Ok(RistrettoSecretKey(k)) - } - - /// Return the byte array for the secret key in little-endian order - fn as_bytes(&self) -> &[u8] { - self.0.as_bytes() - } -} - -impl Hash for RistrettoSecretKey { - /// Require the implementation of the Hash trait for Hashmaps - fn hash(&self, state: &mut H) { - self.as_bytes().hash(state); - } -} - -//---------------------------------- RistrettoSecretKey Mul / Add / Sub --------------------------------------------// - -impl<'a, 'b> Mul<&'b RistrettoPublicKey> for &'a RistrettoSecretKey { - type Output = RistrettoPublicKey; - - fn mul(self, rhs: &'b RistrettoPublicKey) -> RistrettoPublicKey { - let p = &self.0 * &rhs.point; - RistrettoPublicKey::new_from_pk(p) - } -} - -impl<'a, 'b> Add<&'b RistrettoSecretKey> for &'a RistrettoSecretKey { - type Output = RistrettoSecretKey; - - fn add(self, rhs: &'b RistrettoSecretKey) -> RistrettoSecretKey { - let k = &self.0 + &rhs.0; - RistrettoSecretKey(k) - } -} - -impl<'a, 'b> Sub<&'b RistrettoSecretKey> for &'a RistrettoSecretKey { - type Output = RistrettoSecretKey; - - fn sub(self, rhs: &'b RistrettoSecretKey) -> RistrettoSecretKey { - RistrettoSecretKey(&self.0 - &rhs.0) - } -} - -define_add_variants!( - LHS = RistrettoSecretKey, - RHS = RistrettoSecretKey, - Output = RistrettoSecretKey -); -define_sub_variants!( - LHS = RistrettoSecretKey, - RHS = RistrettoSecretKey, - Output = RistrettoSecretKey -); -define_mul_variants!( - LHS = RistrettoSecretKey, - RHS = RistrettoPublicKey, - Output = RistrettoPublicKey -); - -//--------------------------------------------- Conversions -------------------------------------------------// - -impl From for RistrettoSecretKey { - fn from(v: u64) -> Self { - let s = Scalar::from(v); - RistrettoSecretKey(s) - } -} - -//--------------------------------------------- Ristretto Public Key -------------------------------------------------// - -/// The [PublicKey](trait.PublicKey.html) implementation for `ristretto255` is a thin wrapper around the dalek -/// library's [RistrettoPoint](struct.RistrettoPoint.html). -/// -/// ## Creating public keys -/// Both [PublicKey](trait.PublicKey.html) and [ByteArray](trait.ByteArray.html) are implemented on -/// `RistrettoPublicKey` so all of the following will work: -/// ```edition2018 -/// use tari_crypto::ristretto::{ RistrettoPublicKey, RistrettoSecretKey }; -/// use tari_utilities::{ ByteArray, hex::Hex }; -/// use tari_crypto::keys::{ PublicKey, SecretKey }; -/// use rand; -/// -/// let mut rng = rand::OsRng::new().unwrap(); -/// let _p1 = RistrettoPublicKey::from_bytes(&[224, 196, 24, 247, 200, 217, 196, 205, 215, 57, 91, 147, 234, 18, 79, 58, 217, -/// 144, 33, 187, 104, 29, 252, 51, 2, 169, 217, 154, 46, 83, 230, 78]); -/// let _p2 = RistrettoPublicKey::from_hex(&"e882b131016b52c1d3337080187cf768423efccbb517bb495ab812c4160ff44e"); -/// let sk = RistrettoSecretKey::random(&mut rng); -/// let _p3 = RistrettoPublicKey::from_secret_key(&sk); -/// ``` -#[derive(Clone, Debug)] -pub struct RistrettoPublicKey { - pub(crate) point: RistrettoPoint, - pub(crate) compressed: CompressedRistretto, -} - -impl RistrettoPublicKey { - // Private constructor - pub(crate) fn new_from_pk(pk: RistrettoPoint) -> RistrettoPublicKey { - RistrettoPublicKey { - point: pk, - compressed: pk.compress(), - } - } -} - -impl PublicKey for RistrettoPublicKey { - type K = RistrettoSecretKey; - - /// Generates a new Public key from the given secret key - fn from_secret_key(k: &Self::K) -> RistrettoPublicKey { - let pk = &k.0 * &RISTRETTO_BASEPOINT_TABLE; - RistrettoPublicKey::new_from_pk(pk) - } - - fn key_length() -> usize { - PUBLIC_KEY_LENGTH - } - - fn batch_mul(scalars: &[Self::K], points: &[Self]) -> Self { - let p: Vec<&RistrettoPoint> = points.iter().map(|p| &p.point).collect(); - let s: Vec<&Scalar> = scalars.iter().map(|k| &k.0).collect(); - let p = RistrettoPoint::multiscalar_mul(s, p); - RistrettoPublicKey::new_from_pk(p) - } -} - -impl DiffieHellmanSharedSecret for RistrettoPublicKey { - type PK = RistrettoPublicKey; - - /// Generate a shared secret from one party's private key and another party's public key - fn shared_secret(k: &::K, pk: &Self::PK) -> Self::PK { - k * pk - } -} - -// Requires custom Hashable implementation for RistrettoPublicKey as CompressedRistretto doesnt implement this trait -impl Hashable for RistrettoPublicKey { - fn hash(&self) -> Vec { - let mut hasher = HashDigest::new(); - hasher.input(&self.to_vec()); - hasher.result().to_vec() - } -} - -// Requires custom Extendbytes implementation for RistrettoPublicKey as CompressedRistretto doesnt implement this trait -impl ExtendBytes for RistrettoPublicKey { - fn append_raw_bytes(&self, buf: &mut Vec) { - let bytes = self.as_bytes(); - buf.extend_from_slice(&bytes); - } -} - -impl Hash for RistrettoPublicKey { - /// Require the implementation of the Hash trait for Hashmaps - fn hash(&self, state: &mut H) { - self.as_bytes().hash(state); - } -} - -//---------------------------------- Ristretto Public Key Default -----------------------------------------------// - -impl Default for RistrettoPublicKey { - fn default() -> Self { - RistrettoPublicKey::new_from_pk(RistrettoPoint::default()) - } -} - -//------------------------------------ PublicKey Display impl ---------------------------------------------// - -impl fmt::Display for RistrettoPublicKey { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.to_hex()) - } -} - -//------------------------------------ PublicKey PartialEq, Eq, Ord impl ---------------------------------------------// - -impl PartialEq for RistrettoPublicKey { - fn eq(&self, other: &RistrettoPublicKey) -> bool { - // Although this is slower than `self.compressed == other.compressed`, expanded point comparison is an equal - // time comparision - self.point == other.point - } -} - -impl Eq for RistrettoPublicKey {} - -impl PartialOrd for RistrettoPublicKey { - fn partial_cmp(&self, other: &RistrettoPublicKey) -> Option { - self.compressed.to_bytes().partial_cmp(&other.compressed.to_bytes()) - } -} - -impl Ord for RistrettoPublicKey { - fn cmp(&self, other: &Self) -> Ordering { - self.compressed.to_bytes().cmp(&other.compressed.to_bytes()) - } -} - -//---------------------------------- PublicKey ByteArray implementation ---------------------------------------------// - -impl ByteArray for RistrettoPublicKey { - /// Create a new `RistrettoPublicKey` instance form the given byte array. The constructor returns errors under - /// the following circumstances: - /// * The byte array is not exactly 32 bytes - /// * The byte array does not represent a valid (compressed) point on the ristretto255 curve - fn from_bytes(bytes: &[u8]) -> Result - where Self: Sized { - // Check the length here, because The Ristretto constructor panics rather than returning an error - if bytes.len() != 32 { - return Err(ByteArrayError::IncorrectLength); - } - let pk = CompressedRistretto::from_slice(bytes); - match pk.decompress() { - None => Err(ByteArrayError::ConversionError( - "Invalid compressed Ristretto point".to_string(), - )), - Some(p) => Ok(RistrettoPublicKey::new_from_pk(p)), - } - } - - /// Return the little-endian byte array representation of the compressed public key - fn as_bytes(&self) -> &[u8] { - self.compressed.as_bytes() - } -} - -//---------------------------------- PublicKey Add / Sub / Mul ---------------------------------------------// - -impl<'a, 'b> Add<&'b RistrettoPublicKey> for &'a RistrettoPublicKey { - type Output = RistrettoPublicKey; - - fn add(self, rhs: &'b RistrettoPublicKey) -> RistrettoPublicKey { - let p_sum = &self.point + &rhs.point; - RistrettoPublicKey::new_from_pk(p_sum) - } -} - -impl<'a, 'b> Sub<&'b RistrettoPublicKey> for &'a RistrettoPublicKey { - type Output = RistrettoPublicKey; - - fn sub(self, rhs: &RistrettoPublicKey) -> RistrettoPublicKey { - let p_sum = &self.point - &rhs.point; - RistrettoPublicKey::new_from_pk(p_sum) - } -} - -impl<'a, 'b> Mul<&'b RistrettoSecretKey> for &'a RistrettoPublicKey { - type Output = RistrettoPublicKey; - - fn mul(self, rhs: &'b RistrettoSecretKey) -> RistrettoPublicKey { - let p = &rhs.0 * &self.point; - RistrettoPublicKey::new_from_pk(p) - } -} - -impl<'a, 'b> Mul<&'b RistrettoSecretKey> for &'a RistrettoSecretKey { - type Output = RistrettoSecretKey; - - fn mul(self, rhs: &'b RistrettoSecretKey) -> RistrettoSecretKey { - let p = &rhs.0 * &self.0; - RistrettoSecretKey(p) - } -} - -define_add_variants!( - LHS = RistrettoPublicKey, - RHS = RistrettoPublicKey, - Output = RistrettoPublicKey -); -define_sub_variants!( - LHS = RistrettoPublicKey, - RHS = RistrettoPublicKey, - Output = RistrettoPublicKey -); -define_mul_variants!( - LHS = RistrettoPublicKey, - RHS = RistrettoSecretKey, - Output = RistrettoPublicKey -); -define_mul_variants!( - LHS = RistrettoSecretKey, - RHS = RistrettoSecretKey, - Output = RistrettoSecretKey -); - -//---------------------------------- PublicKey From implementations -------------------------------------// - -impl From for Scalar { - fn from(k: RistrettoSecretKey) -> Self { - k.0 - } -} - -impl From for RistrettoPoint { - fn from(pk: RistrettoPublicKey) -> Self { - pk.point - } -} - -impl From<&RistrettoPublicKey> for RistrettoPoint { - fn from(pk: &RistrettoPublicKey) -> Self { - pk.point - } -} - -impl From for CompressedRistretto { - fn from(pk: RistrettoPublicKey) -> Self { - pk.compressed - } -} - -//--------------------------------------------------------------------------------------------------------------------// -// Tests // -//--------------------------------------------------------------------------------------------------------------------// - -#[cfg(test)] -mod test { - use super::*; - use crate::{keys::PublicKey, ristretto::test_common::get_keypair}; - use rand; - use tari_utilities::{message_format::MessageFormat, ByteArray}; - - fn assert_completely_equal(k1: &RistrettoPublicKey, k2: &RistrettoPublicKey) { - assert_eq!(k1, k2); - assert_eq!(k1.point, k2.point); - assert_eq!(k1.compressed, k2.compressed); - } - - #[test] - fn test_generation() { - let mut rng = rand::OsRng::new().unwrap(); - let k1 = RistrettoSecretKey::random(&mut rng); - let k2 = RistrettoSecretKey::random(&mut rng); - assert_ne!(k1, k2); - } - - #[test] - fn invalid_secret_key_bytes() { - RistrettoSecretKey::from_bytes(&[1, 2, 3]).expect_err("Secret keys should be 32 bytes"); - } - - #[test] - fn create_public_key() { - let encodings_of_small_multiples = [ - // This is the identity point - "0000000000000000000000000000000000000000000000000000000000000000", - // This is the basepoint - "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76", - // These are small multiples of the basepoint - "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919", - "94741f5d5d52755ece4f23f044ee27d5d1ea1e2bd196b462166b16152a9d0259", - "da80862773358b466ffadfe0b3293ab3d9fd53c5ea6c955358f568322daf6a57", - "e882b131016b52c1d3337080187cf768423efccbb517bb495ab812c4160ff44e", - "f64746d3c92b13050ed8d80236a7f0007c3b3f962f5ba793d19a601ebb1df403", - "44f53520926ec81fbd5a387845beb7df85a96a24ece18738bdcfa6a7822a176d", - "903293d8f2287ebe10e2374dc1a53e0bc887e592699f02d077d5263cdd55601c", - "02622ace8f7303a31cafc63f8fc48fdc16e1c8c8d234b2f0d6685282a9076031", - "20706fd788b2720a1ed2a5dad4952b01f413bcf0e7564de8cdc816689e2db95f", - "bce83f8ba5dd2fa572864c24ba1810f9522bc6004afe95877ac73241cafdab42", - "e4549ee16b9aa03099ca208c67adafcafa4c3f3e4e5303de6026e3ca8ff84460", - "aa52e000df2e16f55fb1032fc33bc42742dad6bd5a8fc0be0167436c5948501f", - "46376b80f409b29dc2b5f6f0c52591990896e5716f41477cd30085ab7f10301e", - "e0c418f7c8d9c4cdd7395b93ea124f3ad99021bb681dfc3302a9d99a2e53e64e", - ]; - let mut bytes = [0u8; 32]; - for i in 0u8..16 { - let pk = RistrettoPublicKey::from_hex(&encodings_of_small_multiples[i as usize]).unwrap(); - bytes[0] = i; - let sk = RistrettoSecretKey::from_bytes(&bytes).unwrap(); - let pk2 = RistrettoPublicKey::from_secret_key(&sk); - assert_eq!(pk, pk2); - } - } - - #[test] - fn secret_to_hex() { - let mut rng = rand::OsRng::new().unwrap(); - let sk = RistrettoSecretKey::random(&mut rng); - let hex = sk.to_hex(); - let sk2 = RistrettoSecretKey::from_hex(&hex).unwrap(); - assert_eq!(sk, sk2); - } - - #[test] - fn pubkey_to_hex() { - let mut rng = rand::OsRng::new().unwrap(); - let sk = RistrettoSecretKey::random(&mut rng); - let pk = RistrettoPublicKey::from_secret_key(&sk); - let hex = pk.to_hex(); - let pk2 = RistrettoPublicKey::from_hex(&hex).unwrap(); - assert_completely_equal(&pk, &pk2); - } - - #[test] - fn secret_to_vec() { - let mut rng = rand::OsRng::new().unwrap(); - let sk = RistrettoSecretKey::random(&mut rng); - let vec = sk.to_vec(); - let sk2 = RistrettoSecretKey::from_vec(&vec).unwrap(); - assert_eq!(sk, sk2); - } - - #[test] - fn public_to_vec() { - let mut rng = rand::OsRng::new().unwrap(); - let sk = RistrettoSecretKey::random(&mut rng); - let pk = RistrettoPublicKey::from_secret_key(&sk); - let vec = pk.to_vec(); - let pk2 = RistrettoPublicKey::from_vec(&vec).unwrap(); - assert_completely_equal(&pk, &pk2); - } - - #[test] - fn zero_plus_k_is_k() { - let zero = RistrettoSecretKey::default(); - let mut rng = rand::OsRng::new().unwrap(); - let k = RistrettoSecretKey::random(&mut rng); - assert_eq!(&k + &zero, k); - } - - /// These test vectors are from https://ristretto.group/test_vectors/ristretto255.html - #[test] - fn bad_keys() { - let bad_encodings = [ - // These are all bad because they're non-canonical field encodings. - "00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", - "f3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", - "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", - // These are all bad because they're negative field elements. - "0100000000000000000000000000000000000000000000000000000000000000", - "01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", - "ed57ffd8c914fb201471d1c3d245ce3c746fcbe63a3679d51b6a516ebebe0e20", - "c34c4e1826e5d403b78e246e88aa051c36ccf0aafebffe137d148a2bf9104562", - "c940e5a4404157cfb1628b108db051a8d439e1a421394ec4ebccb9ec92a8ac78", - "47cfc5497c53dc8e61c91d17fd626ffb1c49e2bca94eed052281b510b1117a24", - "f1c6165d33367351b0da8f6e4511010c68174a03b6581212c71c0e1d026c3c72", - "87260f7a2f12495118360f02c26a470f450dadf34a413d21042b43b9d93e1309", - // These are all bad because they give a nonsquare x^2. - "26948d35ca62e643e26a83177332e6b6afeb9d08e4268b650f1f5bbd8d81d371", - "4eac077a713c57b4f4397629a4145982c661f48044dd3f96427d40b147d9742f", - "de6a7b00deadc788eb6b6c8d20c0ae96c2f2019078fa604fee5b87d6e989ad7b", - "bcab477be20861e01e4a0e295284146a510150d9817763caf1a6f4b422d67042", - "2a292df7e32cababbd9de088d1d1abec9fc0440f637ed2fba145094dc14bea08", - "f4a9e534fc0d216c44b218fa0c42d99635a0127ee2e53c712f70609649fdff22", - "8268436f8c4126196cf64b3c7ddbda90746a378625f9813dd9b8457077256731", - "2810e5cbc2cc4d4eece54f61c6f69758e289aa7ab440b3cbeaa21995c2f4232b", - // These are all bad because they give a negative xy value. - "3eb858e78f5a7254d8c9731174a94f76755fd3941c0ac93735c07ba14579630e", - "a45fdc55c76448c049a1ab33f17023edfb2be3581e9c7aade8a6125215e04220", - "d483fe813c6ba647ebbfd3ec41adca1c6130c2beeee9d9bf065c8d151c5f396e", - "8a2e1d30050198c65a54483123960ccc38aef6848e1ec8f5f780e8523769ba32", - "32888462f8b486c68ad7dd9610be5192bbeaf3b443951ac1a8118419d9fa097b", - "227142501b9d4355ccba290404bde41575b037693cef1f438c47f8fbf35d1165", - "5c37cc491da847cfeb9281d407efc41e15144c876e0170b499a96a22ed31e01e", - "445425117cb8c90edcbc7c1cc0e74f747f2c1efa5630a967c64f287792a48a4b", - // This is s = -1, which causes y = 0. - "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", - ]; - // Test that all of the bad encodings are rejected - for bad_encoding in &bad_encodings { - RistrettoPublicKey::from_hex(bad_encoding).expect_err(&format!("Encoding {} should fail", bad_encoding)); - } - } - - #[test] - fn batch_mul() { - let (k1, p1) = get_keypair(); - let (k2, p2) = get_keypair(); - let p_slow = &(&k1 * &p1) + &(&k2 * &p2); - let b_batch = RistrettoPublicKey::batch_mul(&[k1, k2], &[p1, p2]); - assert_completely_equal(&p_slow, &b_batch); - } - - #[test] - fn create_keypair() { - let mut rng = rand::OsRng::new().unwrap(); - let (k, pk) = RistrettoPublicKey::random_keypair(&mut rng); - assert_completely_equal(&pk, &RistrettoPublicKey::from_secret_key(&k)); - } - - #[test] - fn secret_keys_are_cleared_after_drop() { - let zero = &vec![0u8; 32][..]; - let mut rng = rand::OsRng::new().unwrap(); - let ptr; - { - let k = RistrettoSecretKey::random(&mut rng); - ptr = (k.0).as_bytes().as_ptr(); - } - // In release mode, the memory can already be reclaimed by this stage due to optimisations, and so this test - // can fail in release mode, even though the values were effectively scrubbed. - if cfg!(debug_assertions) { - unsafe { - use std::slice; - assert_eq!(slice::from_raw_parts(ptr, 32), zero); - } - } - } - - #[test] - fn convert_from_u64() { - let k = RistrettoSecretKey::from(42u64); - assert_eq!( - k.to_hex(), - "2a00000000000000000000000000000000000000000000000000000000000000" - ); - let k = RistrettoSecretKey::from(256u64); - assert_eq!( - k.to_hex(), - "0001000000000000000000000000000000000000000000000000000000000000" - ); - let k = RistrettoSecretKey::from(100_000_000u64); - assert_eq!( - k.to_hex(), - "00e1f50500000000000000000000000000000000000000000000000000000000" - ); - } - - #[test] - fn serialize_deserialize_base64() { - let mut rng = rand::OsRng::new().unwrap(); - let (k, pk) = RistrettoPublicKey::random_keypair(&mut rng); - let ser_k = k.to_base64().unwrap(); - let ser_pk = pk.to_base64().unwrap(); - let k2: RistrettoSecretKey = RistrettoSecretKey::from_base64(&ser_k).unwrap(); - assert_eq!(k, k2, "Deserialised secret key"); - let pk2: RistrettoPublicKey = RistrettoPublicKey::from_base64(&ser_pk).unwrap(); - assert_completely_equal(&pk, &pk2); - } - - #[test] - fn serialize_deserialize_json() { - let mut rng = rand::OsRng::new().unwrap(); - let (k, pk) = RistrettoPublicKey::random_keypair(&mut rng); - let ser_k = k.to_json().unwrap(); - let ser_pk = pk.to_json().unwrap(); - println!("JSON pubkey: {} privkey: {}", ser_pk, ser_k); - let k2: RistrettoSecretKey = RistrettoSecretKey::from_json(&ser_k).unwrap(); - assert_eq!(k, k2, "Deserialised secret key"); - let pk2: RistrettoPublicKey = RistrettoPublicKey::from_json(&ser_pk).unwrap(); - assert_completely_equal(&pk, &pk2); - } - - #[test] - fn serialize_deserialize_binary() { - let mut rng = rand::OsRng::new().unwrap(); - let (k, pk) = RistrettoPublicKey::random_keypair(&mut rng); - let ser_k = k.to_binary().unwrap(); - let ser_pk = pk.to_binary().unwrap(); - let k2: RistrettoSecretKey = RistrettoSecretKey::from_binary(&ser_k).unwrap(); - assert_eq!(k, k2); - let pk2: RistrettoPublicKey = RistrettoPublicKey::from_binary(&ser_pk).unwrap(); - assert_completely_equal(&pk, &pk2); - } - - #[test] - fn display() { - let hex = "e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76"; - let pk = RistrettoPublicKey::from_hex(hex).unwrap(); - assert_eq!(format!("{}", pk), hex); - } -} diff --git a/infrastructure/crypto/src/ristretto/ristretto_sig.rs b/infrastructure/crypto/src/ristretto/ristretto_sig.rs deleted file mode 100644 index a9e27e9ccc..0000000000 --- a/infrastructure/crypto/src/ristretto/ristretto_sig.rs +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::{ - ristretto::{RistrettoPublicKey, RistrettoSecretKey}, - signatures::SchnorrSignature, -}; - -/// # A Schnorr signature implementation on Ristretto -/// -/// Find out more about [Schnorr signatures](https://tlu.tarilabs.com/cryptography/digital_signatures/introduction.html). -/// -/// `RistrettoSchnorr` utilises the [curve25519-dalek](https://github.com/dalek-cryptography/curve25519-dalek1) -/// implementation of `ristretto255` to provide Schnorr signature functionality. -/// -/// In short, a Schnorr sig is made up of the pair _(R, s)_, where _R_ is a public key (of a secret nonce) and _s_ is -/// the signature. -/// -/// ## Creating signatures -/// -/// You can create a `RisrettoSchnorr` from it's component parts: -/// -/// ```edition2018 -/// # use tari_crypto::ristretto::*; -/// # use tari_crypto::keys::*; -/// # use tari_crypto::signatures::SchnorrSignature; -/// # use tari_utilities::ByteArray; -/// # use tari_utilities::hex::Hex; -/// -/// let public_r = RistrettoPublicKey::from_hex("6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919").unwrap(); -/// let s = RistrettoSecretKey::from_bytes(b"10000000000000000000000000000000").unwrap(); -/// let sig = RistrettoSchnorr::new(public_r, s); -/// ``` -/// -/// or you can create a signature by signing a message: -/// -/// ```rust -/// # use tari_crypto::ristretto::*; -/// # use tari_crypto::keys::*; -/// # use tari_crypto::signatures::SchnorrSignature; -/// # use tari_crypto::common::*; -/// # use digest::Digest; -/// -/// fn get_keypair() -> (RistrettoSecretKey, RistrettoPublicKey) { -/// let mut rng = rand::OsRng::new().unwrap(); -/// let k = RistrettoSecretKey::random(&mut rng); -/// let pk = RistrettoPublicKey::from_secret_key(&k); -/// (k, pk) -/// } -/// -/// #[allow(non_snake_case)] -/// fn main() { -/// let (k, P) = get_keypair(); -/// let (r, R) = get_keypair(); -/// let e = Blake256::digest(b"Small Gods"); -/// let sig = RistrettoSchnorr::sign(k, r, &e); -/// } -/// ``` -/// -/// # Verifying signatures -/// -/// Given a signature, (R,s) and a Challenge, e, you can verify that the signature is valid by calling the `verify` -/// method: -/// -/// ```edition2018 -/// # use tari_crypto::ristretto::*; -/// # use tari_crypto::keys::*; -/// # use tari_crypto::signatures::SchnorrSignature; -/// # use tari_crypto::common::*; -/// # use tari_utilities::hex::*; -/// # use tari_utilities::ByteArray; -/// # use digest::Digest; -/// -/// # #[allow(non_snake_case)] -/// # fn main() { -/// let P = RistrettoPublicKey::from_hex("74896a30c89186b8194e25f8c1382f8d3081c5a182fb8f8a6d34f27fbefbfc70").unwrap(); -/// let R = RistrettoPublicKey::from_hex("fa14cb581ce5717248444721242e6b195a482d503a853dea4acb513074d8d803").unwrap(); -/// let s = RistrettoSecretKey::from_hex("bd0b253a619310340a4fa2de54cdd212eac7d088ee1dc47e305c3f6cbd020908").unwrap(); -/// let sig = RistrettoSchnorr::new(R, s); -/// let e = Blake256::digest(b"Maskerade"); -/// assert!(sig.verify_challenge(&P, &e)); -/// # } -/// ``` -pub type RistrettoSchnorr = SchnorrSignature; - -#[cfg(test)] -mod test { - use crate::{ - common::Blake256, - keys::{PublicKey, SecretKey}, - ristretto::{RistrettoPublicKey, RistrettoSchnorr, RistrettoSecretKey}, - }; - use digest::Digest; - use rand; - use tari_utilities::ByteArray; - - fn get_keypair() -> (RistrettoSecretKey, RistrettoPublicKey) { - let mut rng = rand::OsRng::new().unwrap(); - let k = RistrettoSecretKey::random(&mut rng); - let pk = RistrettoPublicKey::from_secret_key(&k); - (k, pk) - } - - /// Create a signature, and then verify it. Also checks that some invalid signatures fail to verify - #[test] - #[allow(non_snake_case)] - fn sign_and_verify_message() { - let (k, P) = get_keypair(); - let (r, R) = get_keypair(); - let e = Blake256::new() - .chain(P.as_bytes()) - .chain(R.as_bytes()) - .chain(b"Small Gods") - .result(); - let sig = RistrettoSchnorr::sign(k, r, &e).unwrap(); - let R_calc = sig.get_public_nonce(); - assert_eq!(R, *R_calc); - assert!(sig.verify_challenge(&P, &e)); - // Doesn't work for invalid credentials - assert!(!sig.verify_challenge(&R, &e)); - // Doesn't work for different challenge - let wrong_challenge = Blake256::digest(b"Guards! Guards!"); - assert!(!sig.verify_challenge(&P, &wrong_challenge)); - } - - /// This test checks that the linearity of Schnorr signatures hold, i.e. that s = s1 + s2 is validated by R1 + R2 - /// and P1 + P2. We do this by hand here rather than using the APIs to guard against regressions - #[test] - #[allow(non_snake_case)] - fn test_signature_addition() { - // Alice and Bob generate some keys and nonces - let (k1, P1) = get_keypair(); - let (r1, R1) = get_keypair(); - let (k2, P2) = get_keypair(); - let (r2, R2) = get_keypair(); - // Each of them creates the Challenge = H(R1 || R2 || P1 || P2 || m) - let e = Blake256::new() - .chain(R1.as_bytes()) - .chain(R2.as_bytes()) - .chain(P1.as_bytes()) - .chain(P2.as_bytes()) - .chain(b"Moving Pictures") - .result(); - // Calculate Alice's signature - let s1 = RistrettoSchnorr::sign(k1, r1, &e).unwrap(); - // Calculate Bob's signature - let s2 = RistrettoSchnorr::sign(k2, r2, &e).unwrap(); - // Now add the two signatures together - let s_agg = &s1 + &s2; - // Check that the multi-sig verifies - assert!(s_agg.verify_challenge(&(P1 + P2), &e)); - } -} diff --git a/infrastructure/crypto/src/ristretto/serialize.rs b/infrastructure/crypto/src/ristretto/serialize.rs deleted file mode 100644 index 0f6fda27c4..0000000000 --- a/infrastructure/crypto/src/ristretto/serialize.rs +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2019. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -//! Custom serializers for Ristretto keys -//! -//! The Dalek libraries only serialize to binary (understandably), but this has 2 yucky implications: -//! -//! 1. Exporting to "human readable" formats like JSON yield crappy looking 'binary arrays', e.g. /[12, 223, 65, .../] -//! 2. Reading back from JSON is broken because serde doesn't read this back as a byte string, but as a seq. -//! -//! The workaround is to have binary serialization by default, but if a struct is going to be saved in JSON format, -//! then you can override that behaviour with `with_serialize`, e.g. -//! -//! ```nocompile -//! #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -//! pub struct KeyManager { -//! #[serde(serialize_with = "serialise_to_hex", deserialize_with = "secret_from_hex")] -//! pub master_key: K, -//! pub branch_seed: String, -//! pub primary_key_index: usize, -//! digest_type: PhantomData, -//! } -//! ``` - -use crate::ristretto::{RistrettoPublicKey, RistrettoSecretKey}; -use serde::{ - de::{self, Visitor}, - Deserialize, - Deserializer, - Serialize, - Serializer, -}; -use std::fmt; -use tari_utilities::{byte_array::ByteArray, hex::Hex}; - -impl<'de> Deserialize<'de> for RistrettoPublicKey { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'de> { - struct RistrettoPubKeyVisitor; - - impl<'de> Visitor<'de> for RistrettoPubKeyVisitor { - type Value = RistrettoPublicKey; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a public key in binary format") - } - - fn visit_bytes(self, v: &[u8]) -> Result - where E: de::Error { - RistrettoPublicKey::from_bytes(v).map_err(E::custom) - } - } - - if deserializer.is_human_readable() { - let s = String::deserialize(deserializer)?; - RistrettoPublicKey::from_hex(&s).map_err(de::Error::custom) - } else { - deserializer.deserialize_bytes(RistrettoPubKeyVisitor) - } - } -} - -impl Serialize for RistrettoPublicKey { - fn serialize(&self, serializer: S) -> Result - where S: Serializer { - if serializer.is_human_readable() { - self.to_hex().serialize(serializer) - } else { - serializer.serialize_bytes(self.as_bytes()) - } - } -} - -impl<'de> Deserialize<'de> for RistrettoSecretKey { - fn deserialize(deserializer: D) -> Result - where D: Deserializer<'de> { - struct RistrettoVisitor; - - impl<'de> Visitor<'de> for RistrettoVisitor { - type Value = RistrettoSecretKey; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a secret key in binary format") - } - - fn visit_bytes(self, v: &[u8]) -> Result - where E: de::Error { - RistrettoSecretKey::from_bytes(v).map_err(E::custom) - } - } - - if deserializer.is_human_readable() { - let s = String::deserialize(deserializer)?; - RistrettoSecretKey::from_hex(&s).map_err(de::Error::custom) - } else { - deserializer.deserialize_bytes(RistrettoVisitor) - } - } -} - -impl Serialize for RistrettoSecretKey { - fn serialize(&self, serializer: S) -> Result - where S: Serializer { - if serializer.is_human_readable() { - self.to_hex().serialize(serializer) - } else { - serializer.serialize_bytes(self.as_bytes()) - } - } -} diff --git a/infrastructure/crypto/src/signatures.rs b/infrastructure/crypto/src/signatures.rs deleted file mode 100644 index 792347b7b4..0000000000 --- a/infrastructure/crypto/src/signatures.rs +++ /dev/null @@ -1,161 +0,0 @@ -//! Digital Signature module -//! This module defines generic traits for handling the digital signature operations, agnostic -//! of the underlying elliptic curve implementation - -use crate::keys::{PublicKey, SecretKey}; -use derive_error::Error; -use serde::{Deserialize, Serialize}; -use std::{ - cmp::Ordering, - ops::{Add, Mul}, -}; -use tari_utilities::{hex::Hex, ByteArray}; - -#[derive(Clone, Debug, Error, PartialEq, Eq, Deserialize, Serialize)] -pub enum SchnorrSignatureError { - // An invalid challenge was provided - InvalidChallenge, -} - -#[allow(non_snake_case)] -#[derive(PartialEq, Eq, Copy, Debug, Clone, Serialize, Deserialize, Hash)] -pub struct SchnorrSignature { - public_nonce: P, - signature: K, -} - -impl SchnorrSignature -where - P: PublicKey, - K: SecretKey, -{ - pub fn new(public_nonce: P, signature: K) -> Self { - SchnorrSignature { - public_nonce, - signature, - } - } - - pub fn calc_signature_verifier(&self) -> P { - P::from_secret_key(&self.signature) - } - - pub fn sign(secret: K, nonce: K, challenge: &[u8]) -> Result - where K: Add + Mul + Mul { - // s = r + e.k - let e = match K::from_bytes(challenge) { - Ok(e) => e, - Err(_) => return Err(SchnorrSignatureError::InvalidChallenge), - }; - let public_nonce = P::from_secret_key(&nonce); - let ek = e * secret; - let s = ek + nonce; - Ok(Self::new(public_nonce, s)) - } - - pub fn verify_challenge<'a>(&self, public_key: &'a P, challenge: &[u8]) -> bool - where - for<'b> &'b K: Mul<&'a P, Output = P>, - for<'b> &'b P: Add, - { - let e = match K::from_bytes(&challenge) { - Ok(e) => e, - Err(_) => return false, - }; - self.verify(public_key, &e) - } - - pub fn verify<'a>(&self, public_key: &'a P, challenge: &K) -> bool - where - for<'b> &'b K: Mul<&'a P, Output = P>, - for<'b> &'b P: Add, - { - let lhs = self.calc_signature_verifier(); - let rhs = &self.public_nonce + challenge * public_key; - // Implementors should make this a constant time comparison - lhs == rhs - } - - #[inline] - pub fn get_signature(&self) -> &K { - &self.signature - } - - #[inline] - pub fn get_public_nonce(&self) -> &P { - &self.public_nonce - } -} - -impl<'a, 'b, P, K> Add<&'b SchnorrSignature> for &'a SchnorrSignature -where - P: PublicKey, - &'a P: Add<&'b P, Output = P>, - K: SecretKey, - &'a K: Add<&'b K, Output = K>, -{ - type Output = SchnorrSignature; - - fn add(self, rhs: &'b SchnorrSignature) -> SchnorrSignature { - let r_sum = self.get_public_nonce() + rhs.get_public_nonce(); - let s_sum = self.get_signature() + rhs.get_signature(); - SchnorrSignature::new(r_sum, s_sum) - } -} - -impl<'a, P, K> Add> for &'a SchnorrSignature -where - P: PublicKey, - for<'b> &'a P: Add<&'b P, Output = P>, - K: SecretKey, - for<'b> &'a K: Add<&'b K, Output = K>, -{ - type Output = SchnorrSignature; - - fn add(self, rhs: SchnorrSignature) -> SchnorrSignature { - let r_sum = self.get_public_nonce() + rhs.get_public_nonce(); - let s_sum = self.get_signature() + rhs.get_signature(); - SchnorrSignature::new(r_sum, s_sum) - } -} - -impl Default for SchnorrSignature -where - P: PublicKey, - K: SecretKey, -{ - fn default() -> Self { - SchnorrSignature::new(P::default(), K::default()) - } -} - -/// Provide an efficient ordering algorithm for Schnorr signatures. It's probably not a good idea to implement `Ord` -/// for secret keys, but in this instance, the signature is publicly known and is simply a scalar, so we use the hex -/// representation of the scalar as the canonical ordering metric. This conversion is done if and only if the public -/// nonces are already equal, otherwise the public nonce ordering determines the SchnorrSignature order. -impl Ord for SchnorrSignature -where - P: Eq + Ord, - K: Eq + ByteArray, -{ - fn cmp(&self, other: &Self) -> Ordering { - match self.public_nonce.cmp(&other.public_nonce) { - Ordering::Equal => { - let this = self.signature.to_hex(); - let that = other.signature.to_hex(); - this.cmp(&that) - }, - v => v, - } - } -} - -impl PartialOrd for SchnorrSignature -where - P: Eq + Ord, - K: Eq + ByteArray, -{ - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} diff --git a/infrastructure/crypto/test_vectors/edDSA_testvectors b/infrastructure/crypto/test_vectors/edDSA_testvectors deleted file mode 100644 index 4234759c56..0000000000 --- a/infrastructure/crypto/test_vectors/edDSA_testvectors +++ /dev/null @@ -1,128 +0,0 @@ -9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a:d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a::e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b: -4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c:3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c:72:92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c0072: -c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025:fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025:af82:6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40aaf82: -0d4a05b07352a5436e180356da0ae6efa0345ff7fb1572575772e8005ed978e9e61a185bcef2613a6c7cb79763ce945d3b245d76114dd440bcf5f2dc1aa57057:e61a185bcef2613a6c7cb79763ce945d3b245d76114dd440bcf5f2dc1aa57057:cbc77b:d9868d52c2bebce5f3fa5a79891970f309cb6591e3e1702a70276fa97c24b3a8e58606c38c9758529da50ee31b8219cba45271c689afa60b0ea26c99db19b00ccbc77b: -6df9340c138cc188b5fe4464ebaa3f7fc206a2d55c3434707e74c9fc04e20ebbc0dac102c4533186e25dc43128472353eaabdb878b152aeb8e001f92d90233a7:c0dac102c4533186e25dc43128472353eaabdb878b152aeb8e001f92d90233a7:5f4c8989:124f6fc6b0d100842769e71bd530664d888df8507df6c56dedfdb509aeb93416e26b918d38aa06305df3095697c18b2aa832eaa52edc0ae49fbae5a85e150c075f4c8989: -b780381a65edf8b78f6945e8dbec7941ac049fd4c61040cf0c324357975a293ce253af0766804b869bb1595be9765b534886bbaab8305bf50dbc7f899bfb5f01:e253af0766804b869bb1595be9765b534886bbaab8305bf50dbc7f899bfb5f01:18b6bec097:b2fc46ad47af464478c199e1f8be169f1be6327c7f9a0a6689371ca94caf04064a01b22aff1520abd58951341603faed768cf78ce97ae7b038abfe456aa17c0918b6bec097: -78ae9effe6f245e924a7be63041146ebc670dbd3060cba67fbc6216febc44546fbcfbfa40505d7f2be444a33d185cc54e16d615260e1640b2b5087b83ee3643d:fbcfbfa40505d7f2be444a33d185cc54e16d615260e1640b2b5087b83ee3643d:89010d855972:6ed629fc1d9ce9e1468755ff636d5a3f40a5d9c91afd93b79d241830f7e5fa29854b8f20cc6eecbb248dbd8d16d14e99752194e4904d09c74d639518839d230089010d855972: -691865bfc82a1e4b574eecde4c7519093faf0cf867380234e3664645c61c5f7998a5e3a36e67aaba89888bf093de1ad963e774013b3902bfab356d8b90178a63:98a5e3a36e67aaba89888bf093de1ad963e774013b3902bfab356d8b90178a63:b4a8f381e70e7a:6e0af2fe55ae377a6b7a7278edfb419bd321e06d0df5e27037db8812e7e3529810fa5552f6c0020985ca17a0e02e036d7b222a24f99b77b75fdd16cb05568107b4a8f381e70e7a: -3b26516fb3dc88eb181b9ed73f0bcd52bcd6b4c788e4bcaf46057fd078bee073f81fb54a825fced95eb033afcd64314075abfb0abd20a970892503436f34b863:f81fb54a825fced95eb033afcd64314075abfb0abd20a970892503436f34b863:4284abc51bb67235:d6addec5afb0528ac17bb178d3e7f2887f9adbb1ad16e110545ef3bc57f9de2314a5c8388f723b8907be0f3ac90c6259bbe885ecc17645df3db7d488f805fa084284abc51bb67235: -edc6f5fbdd1cee4d101c063530a30490b221be68c036f5b07d0f953b745df192c1a49c66e617f9ef5ec66bc4c6564ca33de2a5fb5e1464062e6d6c6219155efd:c1a49c66e617f9ef5ec66bc4c6564ca33de2a5fb5e1464062e6d6c6219155efd:672bf8965d04bc5146:2c76a04af2391c147082e33faacdbe56642a1e134bd388620b852b901a6bc16ff6c9cc9404c41dea12ed281da067a1513866f9d964f8bdd24953856c50042901672bf8965d04bc5146: -4e7d21fb3b1897571a445833be0f9fd41cd62be3aa04040f8934e1fcbdcacd4531b2524b8348f7ab1dfafa675cc538e9a84e3fe5819e27c12ad8bbc1a36e4dff:31b2524b8348f7ab1dfafa675cc538e9a84e3fe5819e27c12ad8bbc1a36e4dff:33d7a786aded8c1bf691:28e4598c415ae9de01f03f9f3fab4e919e8bf537dd2b0cdf6e79b9e6559c9409d9151a4c40f083193937627c369488259e99da5a9f0a87497fa6696a5dd6ce0833d7a786aded8c1bf691: -a980f892db13c99a3e8971e965b2ff3d41eafd54093bc9f34d1fd22d84115bb644b57ee30cdb55829d0a5d4f046baef078f1e97a7f21b62d75f8e96ea139c35f:44b57ee30cdb55829d0a5d4f046baef078f1e97a7f21b62d75f8e96ea139c35f:3486f68848a65a0eb5507d:77d389e599630d934076329583cd4105a649a9292abc44cd28c40000c8e2f5ac7660a81c85b72af8452d7d25c070861dae91601c7803d656531650dd4e5c41003486f68848a65a0eb5507d: -5b5a619f8ce1c66d7ce26e5a2ae7b0c04febcd346d286c929e19d0d5973bfef96fe83693d011d111131c4f3fbaaa40a9d3d76b30012ff73bb0e39ec27ab18257:6fe83693d011d111131c4f3fbaaa40a9d3d76b30012ff73bb0e39ec27ab18257:5a8d9d0a22357e6655f9c785:0f9ad9793033a2fa06614b277d37381e6d94f65ac2a5a94558d09ed6ce922258c1a567952e863ac94297aec3c0d0c8ddf71084e504860bb6ba27449b55adc40e5a8d9d0a22357e6655f9c785: -940c89fe40a81dafbdb2416d14ae469119869744410c3303bfaa0241dac57800a2eb8c0501e30bae0cf842d2bde8dec7386f6b7fc3981b8c57c9792bb94cf2dd:a2eb8c0501e30bae0cf842d2bde8dec7386f6b7fc3981b8c57c9792bb94cf2dd:b87d3813e03f58cf19fd0b6395:d8bb64aad8c9955a115a793addd24f7f2b077648714f49c4694ec995b330d09d640df310f447fd7b6cb5c14f9fe9f490bcf8cfadbfd2169c8ac20d3b8af49a0cb87d3813e03f58cf19fd0b6395: -9acad959d216212d789a119252ebfe0c96512a23c73bd9f3b202292d6916a738cf3af898467a5b7a52d33d53bc037e2642a8da996903fc252217e9c033e2f291:cf3af898467a5b7a52d33d53bc037e2642a8da996903fc252217e9c033e2f291:55c7fa434f5ed8cdec2b7aeac173:6ee3fe81e23c60eb2312b2006b3b25e6838e02106623f844c44edb8dafd66ab0671087fd195df5b8f58a1d6e52af42908053d55c7321010092748795ef94cf0655c7fa434f5ed8cdec2b7aeac173: -d5aeee41eeb0e9d1bf8337f939587ebe296161e6bf5209f591ec939e1440c300fd2a565723163e29f53c9de3d5e8fbe36a7ab66e1439ec4eae9c0a604af291a5:fd2a565723163e29f53c9de3d5e8fbe36a7ab66e1439ec4eae9c0a604af291a5:0a688e79be24f866286d4646b5d81c:f68d04847e5b249737899c014d31c805c5007a62c0a10d50bb1538c5f35503951fbc1e08682f2cc0c92efe8f4985dec61dcbd54d4b94a22547d24451271c8b000a688e79be24f866286d4646b5d81c: -0a47d10452ae2febec518a1c7c362890c3fc1a49d34b03b6467d35c904a8362d34e5a8508c4743746962c066e4badea2201b8ab484de5c4f94476ccd2143955b:34e5a8508c4743746962c066e4badea2201b8ab484de5c4f94476ccd2143955b:c942fa7ac6b23ab7ff612fdc8e68ef39:2a3d27dc40d0a8127949a3b7f908b3688f63b7f14f651aacd715940bdbe27a0809aac142f47ab0e1e44fa490ba87ce5392f33a891539caf1ef4c367cae54500cc942fa7ac6b23ab7ff612fdc8e68ef39: -f8148f7506b775ef46fdc8e8c756516812d47d6cfbfa318c27c9a22641e56f170445e456dacc7d5b0bbed23c8200cdb74bdcb03e4c7b73f0a2b9b46eac5d4372:0445e456dacc7d5b0bbed23c8200cdb74bdcb03e4c7b73f0a2b9b46eac5d4372:7368724a5b0efb57d28d97622dbde725af:3653ccb21219202b8436fb41a32ba2618c4a133431e6e63463ceb3b6106c4d56e1d2ba165ba76eaad3dc39bffb130f1de3d8e6427db5b71938db4e272bc3e20b7368724a5b0efb57d28d97622dbde725af: -77f88691c4eff23ebb7364947092951a5ff3f10785b417e918823a552dab7c7574d29127f199d86a8676aec33b4ce3f225ccb191f52c191ccd1e8cca65213a6b:74d29127f199d86a8676aec33b4ce3f225ccb191f52c191ccd1e8cca65213a6b:bd8e05033f3a8bcdcbf4beceb70901c82e31:fbe929d743a03c17910575492f3092ee2a2bf14a60a3fcacec74a58c7334510fc262db582791322d6c8c41f1700adb80027ecabc14270b703444ae3ee7623e0abd8e05033f3a8bcdcbf4beceb70901c82e31: -ab6f7aee6a0837b334ba5eb1b2ad7fcecfab7e323cab187fe2e0a95d80eff1325b96dca497875bf9664c5e75facf3f9bc54bae913d66ca15ee85f1491ca24d2c:5b96dca497875bf9664c5e75facf3f9bc54bae913d66ca15ee85f1491ca24d2c:8171456f8b907189b1d779e26bc5afbb08c67a:73bca64e9dd0db88138eedfafcea8f5436cfb74bfb0e7733cf349baa0c49775c56d5934e1d38e36f39b7c5beb0a836510c45126f8ec4b6810519905b0ca07c098171456f8b907189b1d779e26bc5afbb08c67a: -8d135de7c8411bbdbd1b31e5dc678f2ac7109e792b60f38cd24936e8a898c32d1ca281938529896535a7714e3584085b86ef9fec723f42819fc8dd5d8c00817f:1ca281938529896535a7714e3584085b86ef9fec723f42819fc8dd5d8c00817f:8ba6a4c9a15a244a9c26bb2a59b1026f21348b49:a1adc2bc6a2d980662677e7fdff6424de7dba50f5795ca90fdf3e96e256f3285cac71d3360482e993d0294ba4ec7440c61affdf35fe83e6e04263937db93f1058ba6a4c9a15a244a9c26bb2a59b1026f21348b49: -0e765d720e705f9366c1ab8c3fa84c9a44370c06969f803296884b2846a652a47fae45dd0a05971026d410bc497af5be7d0827a82a145c203f625dfcb8b03ba8:7fae45dd0a05971026d410bc497af5be7d0827a82a145c203f625dfcb8b03ba8:1d566a6232bbaab3e6d8804bb518a498ed0f904986:bb61cf84de61862207c6a455258bc4db4e15eea0317ff88718b882a06b5cf6ec6fd20c5a269e5d5c805bafbcc579e2590af414c7c227273c102a10070cdfe80f1d566a6232bbaab3e6d8804bb518a498ed0f904986: -db36e326d676c2d19cc8fe0c14b709202ecfc761d27089eb6ea4b1bb021ecfa748359b850d23f0715d94bb8bb75e7e14322eaf14f06f28a805403fbda002fc85:48359b850d23f0715d94bb8bb75e7e14322eaf14f06f28a805403fbda002fc85:1b0afb0ac4ba9ab7b7172cddc9eb42bba1a64bce47d4:b6dcd09989dfbac54322a3ce87876e1d62134da998c79d24b50bd7a6a797d86a0e14dc9d7491d6c14a673c652cfbec9f962a38c945da3b2f0879d0b68a9213001b0afb0ac4ba9ab7b7172cddc9eb42bba1a64bce47d4: -c89955e0f7741d905df0730b3dc2b0ce1a13134e44fef3d40d60c020ef19df77fdb30673402faf1c8033714f3517e47cc0f91fe70cf3836d6c23636e3fd2287c:fdb30673402faf1c8033714f3517e47cc0f91fe70cf3836d6c23636e3fd2287c:507c94c8820d2a5793cbf3442b3d71936f35fe3afef316:7ef66e5e86f2360848e0014e94880ae2920ad8a3185a46b35d1e07dea8fa8ae4f6b843ba174d99fa7986654a0891c12a794455669375bf92af4cc2770b579e0c507c94c8820d2a5793cbf3442b3d71936f35fe3afef316: -4e62627fc221142478aee7f00781f817f662e3b75db29bb14ab47cf8e84104d6b1d39801892027d58a8c64335163195893bfc1b61dbeca3260497e1f30371107:b1d39801892027d58a8c64335163195893bfc1b61dbeca3260497e1f30371107:d3d615a8472d9962bb70c5b5466a3d983a4811046e2a0ef5:836afa764d9c48aa4770a4388b654e97b3c16f082967febca27f2fc47ddfd9244b03cfc729698acf5109704346b60b230f255430089ddc56912399d1122de70ad3d615a8472d9962bb70c5b5466a3d983a4811046e2a0ef5: -6b83d7da8908c3e7205b39864b56e5f3e17196a3fc9c2f5805aad0f5554c142dd0c846f97fe28585c0ee159015d64c56311c886eddcc185d296dbb165d2625d6:d0c846f97fe28585c0ee159015d64c56311c886eddcc185d296dbb165d2625d6:6ada80b6fa84f7034920789e8536b82d5e4678059aed27f71c:16e462a29a6dd498685a3718b3eed00cc1598601ee47820486032d6b9acc9bf89f57684e08d8c0f05589cda2882a05dc4c63f9d0431d6552710812433003bc086ada80b6fa84f7034920789e8536b82d5e4678059aed27f71c: -19a91fe23a4e9e33ecc474878f57c64cf154b394203487a7035e1ad9cd697b0d2bf32ba142ba4622d8f3e29ecd85eea07b9c47be9d64412c9b510b27dd218b23:2bf32ba142ba4622d8f3e29ecd85eea07b9c47be9d64412c9b510b27dd218b23:82cb53c4d5a013bae5070759ec06c3c6955ab7a4050958ec328c:881f5b8c5a030df0f75b6634b070dd27bd1ee3c08738ae349338b3ee6469bbf9760b13578a237d5182535ede121283027a90b5f865d63a6537dca07b44049a0f82cb53c4d5a013bae5070759ec06c3c6955ab7a4050958ec328c: -1d5b8cb6215c18141666baeefcf5d69dad5bea9a3493dddaa357a4397a13d4de94d23d977c33e49e5e4992c68f25ec99a27c41ce6b91f2bfa0cd8292fe962835:94d23d977c33e49e5e4992c68f25ec99a27c41ce6b91f2bfa0cd8292fe962835:a9a8cbb0ad585124e522abbfb40533bdd6f49347b55b18e8558cb0:3acd39bec8c3cd2b44299722b5850a0400c1443590fd4861d59aae7496acb3df73fc3fdf7969ae5f50ba47dddc435246e5fd376f6b891cd4c2caf5d614b6170ca9a8cbb0ad585124e522abbfb40533bdd6f49347b55b18e8558cb0: -6a91b3227c472299089bdce9356e726a40efd840f11002708b7ee55b64105ac29d084aa8b97a6b9bafa496dbc6f76f3306a116c9d917e681520a0f914369427e:9d084aa8b97a6b9bafa496dbc6f76f3306a116c9d917e681520a0f914369427e:5cb6f9aa59b80eca14f6a68fb40cf07b794e75171fba96262c1c6adc:f5875423781b66216cb5e8998de5d9ffc29d1d67107054ace3374503a9c3ef811577f269de81296744bd706f1ac478caf09b54cdf871b3f802bd57f9a6cb91015cb6f9aa59b80eca14f6a68fb40cf07b794e75171fba96262c1c6adc: -93eaa854d791f05372ce72b94fc6503b2ff8ae6819e6a21afe825e27ada9e4fb16cee8a3f2631834c88b670897ff0b08ce90cc147b4593b3f1f403727f7e7ad5:16cee8a3f2631834c88b670897ff0b08ce90cc147b4593b3f1f403727f7e7ad5:32fe27994124202153b5c70d3813fdee9c2aa6e7dc743d4d535f1840a5:d834197c1a3080614e0a5fa0aaaa808824f21c38d692e6ffbd200f7dfb3c8f44402a7382180b98ad0afc8eec1a02acecf3cb7fde627b9f18111f260ab1db9a0732fe27994124202153b5c70d3813fdee9c2aa6e7dc743d4d535f1840a5: -941cac69fb7b1815c57bb987c4d6c2ad2c35d5f9a3182a79d4ba13eab253a8ad23be323c562dfd71ce65f5bba56a74a3a6dfc36b573d2f94f635c7f9b4fd5a5b:23be323c562dfd71ce65f5bba56a74a3a6dfc36b573d2f94f635c7f9b4fd5a5b:bb3172795710fe00054d3b5dfef8a11623582da68bf8e46d72d27cece2aa:0f8fad1e6bde771b4f5420eac75c378bae6db5ac6650cd2bc210c1823b432b48e016b10595458ffab92f7a8989b293ceb8dfed6c243a2038fc06652aaaf16f02bb3172795710fe00054d3b5dfef8a11623582da68bf8e46d72d27cece2aa: -1acdbb793b0384934627470d795c3d1dd4d79cea59ef983f295b9b59179cbb283f60c7541afa76c019cf5aa82dcdb088ed9e4ed9780514aefb379dabc844f31a:3f60c7541afa76c019cf5aa82dcdb088ed9e4ed9780514aefb379dabc844f31a:7cf34f75c3dac9a804d0fcd09eba9b29c9484e8a018fa9e073042df88e3c56:be71ef4806cb041d885effd9e6b0fbb73d65d7cdec47a89c8a994892f4e55a568c4cc78d61f901e80dbb628b86a23ccd594e712b57fa94c2d67ec266348785077cf34f75c3dac9a804d0fcd09eba9b29c9484e8a018fa9e073042df88e3c56: -8ed7a797b9cea8a8370d419136bcdf683b759d2e3c6947f17e13e2485aa9d420b49f3a78b1c6a7fca8f3466f33bc0e929f01fba04306c2a7465f46c3759316d9:b49f3a78b1c6a7fca8f3466f33bc0e929f01fba04306c2a7465f46c3759316d9:a750c232933dc14b1184d86d8b4ce72e16d69744ba69818b6ac33b1d823bb2c3:04266c033b91c1322ceb3446c901ffcf3cc40c4034e887c9597ca1893ba7330becbbd8b48142ef35c012c6ba51a66df9308cb6268ad6b1e4b03e70102495790ba750c232933dc14b1184d86d8b4ce72e16d69744ba69818b6ac33b1d823bb2c3: -f2ab396fe8906e3e5633e99cabcd5b09df0859b516230b1e0450b580b65f616c8ea074245159a116aa7122a25ec16b891d625a68f33660423908f6bdc44f8c1b:8ea074245159a116aa7122a25ec16b891d625a68f33660423908f6bdc44f8c1b:5a44e34b746c5fd1898d552ab354d28fb4713856d7697dd63eb9bd6b99c280e187:a06a23d982d81ab883aae230adbc368a6a9977f003cebb00d4c2e4018490191a84d3a282fdbfb2fc88046e62de43e15fb575336b3c8b77d19ce6a009ce51f50c5a44e34b746c5fd1898d552ab354d28fb4713856d7697dd63eb9bd6b99c280e187: -550a41c013f79bab8f06e43ad1836d51312736a9713806fafe6645219eaa1f9daf6b7145474dc9954b9af93a9cdb34449d5b7c651c824d24e230b90033ce59c0:af6b7145474dc9954b9af93a9cdb34449d5b7c651c824d24e230b90033ce59c0:8bc4185e50e57d5f87f47515fe2b1837d585f0aae9e1ca383b3ec908884bb900ff27:16dc1e2b9fa909eefdc277ba16ebe207b8da5e91143cde78c5047a89f681c33c4e4e3428d5c928095903a811ec002d52a39ed7f8b3fe1927200c6dd0b9ab3e048bc4185e50e57d5f87f47515fe2b1837d585f0aae9e1ca383b3ec908884bb900ff27: -19ac3e272438c72ddf7b881964867cb3b31ff4c793bb7ea154613c1db068cb7ef85b80e050a1b9620db138bfc9e100327e25c257c59217b601f1f6ac9a413d3f:f85b80e050a1b9620db138bfc9e100327e25c257c59217b601f1f6ac9a413d3f:95872d5f789f95484e30cbb0e114028953b16f5c6a8d9f65c003a83543beaa46b38645:ea855d781cbea4682e350173cb89e8619ccfddb97cdce16f9a2f6f6892f46dbe68e04b12b8d88689a7a31670cdff409af98a93b49a34537b6aa009d2eb8b470195872d5f789f95484e30cbb0e114028953b16f5c6a8d9f65c003a83543beaa46b38645: -ca267de96c93c238fafb1279812059ab93ac03059657fd994f8fa5a09239c821017370c879090a81c7f272c2fc80e3aac2bc603fcb379afc98691160ab745b26:017370c879090a81c7f272c2fc80e3aac2bc603fcb379afc98691160ab745b26:e05f71e4e49a72ec550c44a3b85aca8f20ff26c3ee94a80f1b431c7d154ec9603ee02531:ac957f82335aa7141e96b59d63e3ccee95c3a2c47d026540c2af42dc9533d5fd81827d1679ad187aeaf37834915e75b147a9286806c8017516ba43dd051a5e0ce05f71e4e49a72ec550c44a3b85aca8f20ff26c3ee94a80f1b431c7d154ec9603ee02531: -3dff5e899475e7e91dd261322fab09980c52970de1da6e2e201660cc4fce7032f30162bac98447c4042fac05da448034629be2c6a58d30dfd578ba9fb5e3930b:f30162bac98447c4042fac05da448034629be2c6a58d30dfd578ba9fb5e3930b:938f0e77621bf3ea52c7c4911c5157c2d8a2a858093ef16aa9b107e69d98037ba139a3c382:5efe7a92ff9623089b3e3b78f352115366e26ba3fb1a416209bc029e9cadccd9f4affa333555a8f3a35a9d0f7c34b292cae77ec96fa3adfcaadee2d9ced8f805938f0e77621bf3ea52c7c4911c5157c2d8a2a858093ef16aa9b107e69d98037ba139a3c382: -9a6b847864e70cfe8ba6ab22fa0ca308c0cc8bec7141fbcaa3b81f5d1e1cfcfc34ad0fbdb2566507a81c2b1f8aa8f53dccaa64cc87ada91b903e900d07eee930:34ad0fbdb2566507a81c2b1f8aa8f53dccaa64cc87ada91b903e900d07eee930:838367471183c71f7e717724f89d401c3ad9863fd9cc7aa3cf33d3c529860cb581f3093d87da:2ab255169c489c54c732232e37c87349d486b1eba20509dbabe7fed329ef08fd75ba1cd145e67b2ea26cb5cc51cab343eeb085fe1fd7b0ec4c6afcd9b979f905838367471183c71f7e717724f89d401c3ad9863fd9cc7aa3cf33d3c529860cb581f3093d87da: -575be07afca5d063c238cd9b8028772cc49cda34471432a2e166e096e2219efc94e5eb4d5024f49d7ebf79817c8de11497dc2b55622a51ae123ffc749dbb16e0:94e5eb4d5024f49d7ebf79817c8de11497dc2b55622a51ae123ffc749dbb16e0:33e5918b66d33d55fe717ca34383eae78f0af82889caf6696e1ac9d95d1ffb32cba755f9e3503e:58271d44236f3b98c58fd7ae0d2f49ef2b6e3affdb225aa3ba555f0e11cc53c23ad19baf24346590d05d7d5390582082cf94d39cad6530ab93d13efb3927950633e5918b66d33d55fe717ca34383eae78f0af82889caf6696e1ac9d95d1ffb32cba755f9e3503e: -15ffb45514d43444d61fcb105e30e135fd268523dda20b82758b1794231104411772c5abc2d23fd2f9d1c3257be7bc3c1cd79cee40844b749b3a7743d2f964b8:1772c5abc2d23fd2f9d1c3257be7bc3c1cd79cee40844b749b3a7743d2f964b8:da9c5559d0ea51d255b6bd9d7638b876472f942b330fc0e2b30aea68d77368fce4948272991d257e:6828cd7624e793b8a4ceb96d3c2a975bf773e5ff6645f353614058621e58835289e7f31f42dfe6af6d736f2644511e320c0fa698582a79778d18730ed3e8cb08da9c5559d0ea51d255b6bd9d7638b876472f942b330fc0e2b30aea68d77368fce4948272991d257e: -fe0568642943b2e1afbfd1f10fe8df87a4236bea40dce742072cb21886eec1fa299ebd1f13177dbdb66a912bbf712038fdf73b06c3ac020c7b19126755d47f61:299ebd1f13177dbdb66a912bbf712038fdf73b06c3ac020c7b19126755d47f61:c59d0862ec1c9746abcc3cf83c9eeba2c7082a036a8cb57ce487e763492796d47e6e063a0c1feccc2d:d59e6dfcc6d7e3e2c58dec81e985d245e681acf6594a23c59214f7bed8015d813c7682b60b3583440311e72a8665ba2c96dec23ce826e160127e18132b030404c59d0862ec1c9746abcc3cf83c9eeba2c7082a036a8cb57ce487e763492796d47e6e063a0c1feccc2d: -5ecb16c2df27c8cf58e436a9d3affbd58e9538a92659a0f97c4c4f994635a8cada768b20c437dd3aa5f84bb6a077ffa34ab68501c5352b5cc3fdce7fe6c2398d:da768b20c437dd3aa5f84bb6a077ffa34ab68501c5352b5cc3fdce7fe6c2398d:56f1329d9a6be25a6159c72f12688dc8314e85dd9e7e4dc05bbecb7729e023c86f8e0937353f27c7ede9:1c723a20c6772426a670e4d5c4a97c6ebe9147f71bb0a415631e44406e290322e4ca977d348fe7856a8edc235d0fe95f7ed91aefddf28a77e2c7dbfd8f552f0a56f1329d9a6be25a6159c72f12688dc8314e85dd9e7e4dc05bbecb7729e023c86f8e0937353f27c7ede9: -d599d637b3c30a82a9984e2f758497d144de6f06b9fba04dd40fd949039d7c846791d8ce50a44689fc178727c5c3a1c959fbeed74ef7d8e7bd3c1ab4da31c51f:6791d8ce50a44689fc178727c5c3a1c959fbeed74ef7d8e7bd3c1ab4da31c51f:a7c04e8ba75d0a03d8b166ad7a1d77e1b91c7aaf7befdd99311fc3c54a684ddd971d5b3211c3eeaff1e54e:ebf10d9ac7c96108140e7def6fe9533d727646ff5b3af273c1df95762a66f32b65a09634d013f54b5dd6011f91bc336ca8b355ce33f8cfbec2535a4c427f8205a7c04e8ba75d0a03d8b166ad7a1d77e1b91c7aaf7befdd99311fc3c54a684ddd971d5b3211c3eeaff1e54e: -30ab8232fa7018f0ce6c39bd8f782fe2e159758bb0f2f4386c7f28cfd2c85898ecfb6a2bd42f31b61250ba5de7e46b4719afdfbc660db71a7bd1df7b0a3abe37:ecfb6a2bd42f31b61250ba5de7e46b4719afdfbc660db71a7bd1df7b0a3abe37:63b80b7956acbecf0c35e9ab06b914b0c7014fe1a4bbc0217240c1a33095d707953ed77b15d211adaf9b97dc:9af885344cc7239498f712df80bc01b80638291ed4a1d28baa5545017a72e2f65649ccf9603da6eb5bfab9f5543a6ca4a7af3866153c76bf66bf95def615b00c63b80b7956acbecf0c35e9ab06b914b0c7014fe1a4bbc0217240c1a33095d707953ed77b15d211adaf9b97dc: -0ddcdc872c7b748d40efe96c2881ae189d87f56148ed8af3ebbbc80324e38bdd588ddadcbcedf40df0e9697d8bb277c7bb1498fa1d26ce0a835a760b92ca7c85:588ddadcbcedf40df0e9697d8bb277c7bb1498fa1d26ce0a835a760b92ca7c85:65641cd402add8bf3d1d67dbeb6d41debfbef67e4317c35b0a6d5bbbae0e034de7d670ba1413d056f2d6f1de12:c179c09456e235fe24105afa6e8ec04637f8f943817cd098ba95387f9653b2add181a31447d92d1a1ddf1ceb0db62118de9dffb7dcd2424057cbdff5d41d040365641cd402add8bf3d1d67dbeb6d41debfbef67e4317c35b0a6d5bbbae0e034de7d670ba1413d056f2d6f1de12: -89f0d68299ba0a5a83f248ae0c169f8e3849a9b47bd4549884305c9912b46603aba3e795aab2012acceadd7b3bd9daeeed6ff5258bdcd7c93699c2a3836e3832:aba3e795aab2012acceadd7b3bd9daeeed6ff5258bdcd7c93699c2a3836e3832:4f1846dd7ad50e545d4cfbffbb1dc2ff145dc123754d08af4e44ecc0bc8c91411388bc7653e2d893d1eac2107d05:2c691fa8d487ce20d5d2fa41559116e0bbf4397cf5240e152556183541d66cf753582401a4388d390339dbef4d384743caa346f55f8daba68ba7b9131a8a6e0b4f1846dd7ad50e545d4cfbffbb1dc2ff145dc123754d08af4e44ecc0bc8c91411388bc7653e2d893d1eac2107d05: -0a3c1844e2db070fb24e3c95cb1cc6714ef84e2ccd2b9dd2f1460ebf7ecf13b172e409937e0610eb5c20b326dc6ea1bbbc0406701c5cd67d1fbde09192b07c01:72e409937e0610eb5c20b326dc6ea1bbbc0406701c5cd67d1fbde09192b07c01:4c8274d0ed1f74e2c86c08d955bde55b2d54327e82062a1f71f70d536fdc8722cdead7d22aaead2bfaa1ad00b82957:87f7fdf46095201e877a588fe3e5aaf476bd63138d8a878b89d6ac60631b3458b9d41a3c61a588e1db8d29a5968981b018776c588780922f5aa732ba6379dd054c8274d0ed1f74e2c86c08d955bde55b2d54327e82062a1f71f70d536fdc8722cdead7d22aaead2bfaa1ad00b82957: -c8d7a8818b98dfdb20839c871cb5c48e9e9470ca3ad35ba2613a5d3199c8ab2390d2efbba4d43e6b2b992ca16083dbcfa2b322383907b0ee75f3e95845d3c47f:90d2efbba4d43e6b2b992ca16083dbcfa2b322383907b0ee75f3e95845d3c47f:783e33c3acbdbb36e819f544a7781d83fc283d3309f5d3d12c8dcd6b0b3d0e89e38cfd3b4d0885661ca547fb9764abff:fa2e994421aef1d5856674813d05cbd2cf84ef5eb424af6ecd0dc6fdbdc2fe605fe985883312ecf34f59bfb2f1c9149e5b9cc9ecda05b2731130f3ed28ddae0b783e33c3acbdbb36e819f544a7781d83fc283d3309f5d3d12c8dcd6b0b3d0e89e38cfd3b4d0885661ca547fb9764abff: -b482703612d0c586f76cfcb21cfd2103c957251504a8c0ac4c86c9c6f3e429fffd711dc7dd3b1dfb9df9704be3e6b26f587fe7dd7ba456a91ba43fe51aec09ad:fd711dc7dd3b1dfb9df9704be3e6b26f587fe7dd7ba456a91ba43fe51aec09ad:29d77acfd99c7a0070a88feb6247a2bce9984fe3e6fbf19d4045042a21ab26cbd771e184a9a75f316b648c6920db92b87b:58832bdeb26feafc31b46277cf3fb5d7a17dfb7ccd9b1f58ecbe6feb979666828f239ba4d75219260ecac0acf40f0e5e2590f4caa16bbbcd8a155d347967a60729d77acfd99c7a0070a88feb6247a2bce9984fe3e6fbf19d4045042a21ab26cbd771e184a9a75f316b648c6920db92b87b: -84e50dd9a0f197e3893c38dbd91fafc344c1776d3a400e2f0f0ee7aa829eb8a22c50f870ee48b36b0ac2f8a5f336fb090b113050dbcc25e078200a6e16153eea:2c50f870ee48b36b0ac2f8a5f336fb090b113050dbcc25e078200a6e16153eea:f3992cde6493e671f1e129ddca8038b0abdb77bb9035f9f8be54bd5d68c1aeff724ff47d29344391dc536166b8671cbbf123:69e6a4491a63837316e86a5f4ba7cd0d731ecc58f1d0a264c67c89befdd8d3829d8de13b33cc0bf513931715c7809657e2bfb960e5c764c971d733746093e500f3992cde6493e671f1e129ddca8038b0abdb77bb9035f9f8be54bd5d68c1aeff724ff47d29344391dc536166b8671cbbf123: -b322d46577a2a991a4d1698287832a39c487ef776b4bff037a05c7f1812bdeeceb2bcadfd3eec2986baff32b98e7c4dbf03ff95d8ad5ff9aa9506e5472ff845f:eb2bcadfd3eec2986baff32b98e7c4dbf03ff95d8ad5ff9aa9506e5472ff845f:19f1bf5dcf1750c611f1c4a2865200504d82298edd72671f62a7b1471ac3d4a30f7de9e5da4108c52a4ce70a3e114a52a3b3c5:c7b55137317ca21e33489ff6a9bfab97c855dc6f85684a70a9125a261b56d5e6f149c5774d734f2d8debfc77b721896a8267c23768e9badb910eef83ec25880219f1bf5dcf1750c611f1c4a2865200504d82298edd72671f62a7b1471ac3d4a30f7de9e5da4108c52a4ce70a3e114a52a3b3c5: -960cab5034b9838d098d2dcbf4364bec16d388f6376d73a6273b70f82bbc98c05e3c19f2415acf729f829a4ebd5c40e1a6bc9fbca95703a9376087ed0937e51a:5e3c19f2415acf729f829a4ebd5c40e1a6bc9fbca95703a9376087ed0937e51a:f8b21962447b0a8f2e4279de411bea128e0be44b6915e6cda88341a68a0d818357db938eac73e0af6d31206b3948f8c48a447308:27d4c3a1811ef9d4360b3bdd133c2ccc30d02c2f248215776cb07ee4177f9b13fc42dd70a6c2fed8f225c7663c7f182e7ee8eccff20dc7b0e1d5834ec5b1ea01f8b21962447b0a8f2e4279de411bea128e0be44b6915e6cda88341a68a0d818357db938eac73e0af6d31206b3948f8c48a447308: -eb77b2638f23eebc82efe45ee9e5a0326637401e663ed029699b21e6443fb48e9ef27608961ac711de71a6e2d4d4663ea3ecd42fb7e4e8627c39622df4af0bbc:9ef27608961ac711de71a6e2d4d4663ea3ecd42fb7e4e8627c39622df4af0bbc:99e3d00934003ebafc3e9fdb687b0f5ff9d5782a4b1f56b9700046c077915602c3134e22fc90ed7e690fddd4433e2034dcb2dc99ab:18dc56d7bd9acd4f4daa78540b4ac8ff7aa9815f45a0bba370731a14eaabe96df8b5f37dbf8eae4cb15a64b244651e59d6a3d6761d9e3c50f2d0cbb09c05ec0699e3d00934003ebafc3e9fdb687b0f5ff9d5782a4b1f56b9700046c077915602c3134e22fc90ed7e690fddd4433e2034dcb2dc99ab: -b625aa89d3f7308715427b6c39bbac58effd3a0fb7316f7a22b99ee5922f2dc965a99c3e16fea894ec33c6b20d9105e2a04e2764a4769d9bbd4d8bacfeab4a2e:65a99c3e16fea894ec33c6b20d9105e2a04e2764a4769d9bbd4d8bacfeab4a2e:e07241dbd3adbe610bbe4d005dd46732a4c25086ecb8ec29cd7bca116e1bf9f53bfbf3e11fa49018d39ff1154a06668ef7df5c678e6a:01bb901d83b8b682d3614af46a807ba2691358feb775325d3423f549ff0aa5757e4e1a74e9c70f9721d8f354b319d4f4a1d91445c870fd0ffb94fed64664730de07241dbd3adbe610bbe4d005dd46732a4c25086ecb8ec29cd7bca116e1bf9f53bfbf3e11fa49018d39ff1154a06668ef7df5c678e6a: -b1c9f8bd03fe82e78f5c0fb06450f27dacdf716434db268275df3e1dc177af427fc88b1f7b3f11c629be671c21621f5c10672fafc8492da885742059ee6774cf:7fc88b1f7b3f11c629be671c21621f5c10672fafc8492da885742059ee6774cf:331da7a9c1f87b2ac91ee3b86d06c29163c05ed6f8d8a9725b471b7db0d6acec7f0f702487163f5eda020ca5b493f399e1c8d308c3c0c2:4b229951ef262f16978f7914bc672e7226c5f8379d2778c5a2dc0a2650869f7acfbd0bcd30fdb0619bb44fc1ae5939b87cc318133009c20395b6c7eb98107701331da7a9c1f87b2ac91ee3b86d06c29163c05ed6f8d8a9725b471b7db0d6acec7f0f702487163f5eda020ca5b493f399e1c8d308c3c0c2: -6d8cdb2e075f3a2f86137214cb236ceb89a6728bb4a200806bf3557fb78fac6957a04c7a5113cddfe49a4c124691d46c1f9cdc8f343f9dcb72a1330aeca71fda:57a04c7a5113cddfe49a4c124691d46c1f9cdc8f343f9dcb72a1330aeca71fda:7f318dbd121c08bfddfeff4f6aff4e45793251f8abf658403358238984360054f2a862c5bb83ed89025d2014a7a0cee50da3cb0e76bbb6bf:a6cbc947f9c87d1455cf1a708528c090f11ecee4855d1dbaadf47454a4de55fa4ce84b36d73a5b5f8f59298ccf21992df492ef34163d87753b7e9d32f2c3660b7f318dbd121c08bfddfeff4f6aff4e45793251f8abf658403358238984360054f2a862c5bb83ed89025d2014a7a0cee50da3cb0e76bbb6bf: -47adc6d6bf571ee9570ca0f75b604ac43e303e4ab339ca9b53cacc5be45b2ccba3f527a1c1f17dfeed92277347c9f98ab475de1755b0ab546b8a15d01b9bd0be:a3f527a1c1f17dfeed92277347c9f98ab475de1755b0ab546b8a15d01b9bd0be:ce497c5ff5a77990b7d8f8699eb1f5d8c0582f70cb7ac5c54d9d924913278bc654d37ea227590e15202217fc98dac4c0f3be2183d133315739:4e8c318343c306adbba60c92b75cb0569b9219d8a86e5d57752ed235fc109a43c2cf4e942cacf297279fbb28675347e08027722a4eb7395e00a17495d32edf0bce497c5ff5a77990b7d8f8699eb1f5d8c0582f70cb7ac5c54d9d924913278bc654d37ea227590e15202217fc98dac4c0f3be2183d133315739: -3c19b50b0fe47961719c381d0d8da9b9869d312f13e3298b97fb22f0af29cbbe0f7eda091499625e2bae8536ea35cda5483bd16a9c7e416b341d6f2c83343612:0f7eda091499625e2bae8536ea35cda5483bd16a9c7e416b341d6f2c83343612:8ddcd63043f55ec3bfc83dceae69d8f8b32f4cdb6e2aebd94b4314f8fe7287dcb62732c9052e7557fe63534338efb5b6254c5d41d2690cf5144f:efbd41f26a5d62685516f882b6ec74e0d5a71830d203c231248f26e99a9c6578ec900d68cdb8fa7216ad0d24f9ecbc9ffa655351666582f626645395a31fa7048ddcd63043f55ec3bfc83dceae69d8f8b32f4cdb6e2aebd94b4314f8fe7287dcb62732c9052e7557fe63534338efb5b6254c5d41d2690cf5144f: -34e1e9d539107eb86b393a5ccea1496d35bc7d5e9a8c5159d957e4e5852b3eb00ecb2601d5f7047428e9f909883a12420085f04ee2a88b6d95d3d7f2c932bd76:0ecb2601d5f7047428e9f909883a12420085f04ee2a88b6d95d3d7f2c932bd76:a6d4d0542cfe0d240a90507debacabce7cbbd48732353f4fad82c7bb7dbd9df8e7d9a16980a45186d8786c5ef65445bcc5b2ad5f660ffc7c8eaac0:32d22904d3e7012d6f5a441b0b4228064a5cf95b723a66b048a087ecd55920c31c204c3f2006891a85dd1932e3f1d614cfd633b5e63291c6d8166f3011431e09a6d4d0542cfe0d240a90507debacabce7cbbd48732353f4fad82c7bb7dbd9df8e7d9a16980a45186d8786c5ef65445bcc5b2ad5f660ffc7c8eaac0: -49dd473ede6aa3c866824a40ada4996c239a20d84c9365e4f0a4554f8031b9cf788de540544d3feb0c919240b390729be487e94b64ad973eb65b4669ecf23501:788de540544d3feb0c919240b390729be487e94b64ad973eb65b4669ecf23501:3a53594f3fba03029318f512b084a071ebd60baec7f55b028dc73bfc9c74e0ca496bf819dd92ab61cd8b74be3c0d6dcd128efc5ed3342cba124f726c:d2fde02791e720852507faa7c3789040d9ef86646321f313ac557f4002491542dd67d05c6990cdb0d495501fbc5d5188bfbb84dc1bf6098bee0603a47fc2690f3a53594f3fba03029318f512b084a071ebd60baec7f55b028dc73bfc9c74e0ca496bf819dd92ab61cd8b74be3c0d6dcd128efc5ed3342cba124f726c: -331c64da482b6b551373c36481a02d8136ecadbb01ab114b4470bf41607ac57152a00d96a3148b4726692d9eff89160ea9f99a5cc4389f361fed0bb16a42d521:52a00d96a3148b4726692d9eff89160ea9f99a5cc4389f361fed0bb16a42d521:20e1d05a0d5b32cc8150b8116cef39659dd5fb443ab15600f78e5b49c45326d9323f2850a63c3808859495ae273f58a51e9de9a145d774b40ba9d753d3:22c99aa946ead39ac7997562810c01c20b46bd610645bd2d56dcdcbaacc5452c74fbf4b8b1813b0e94c30d808ce5498e61d4f7ccbb4cc5f04dfc6140825a960020e1d05a0d5b32cc8150b8116cef39659dd5fb443ab15600f78e5b49c45326d9323f2850a63c3808859495ae273f58a51e9de9a145d774b40ba9d753d3: -5c0b96f2af8712122cf743c8f8dc77b6cd5570a7de13297bb3dde1886213cce20510eaf57d7301b0e1d527039bf4c6e292300a3a61b4765434f3203c100351b1:0510eaf57d7301b0e1d527039bf4c6e292300a3a61b4765434f3203c100351b1:54e0caa8e63919ca614b2bfd308ccfe50c9ea888e1ee4446d682cb5034627f97b05392c04e835556c31c52816a48e4fb196693206b8afb4408662b3cb575:06e5d8436ac7705b3a90f1631cdd38ec1a3fa49778a9b9f2fa5ebea4e7d560ada7dd26ff42fafa8ba420323742761aca6904940dc21bbef63ff72daab45d430b54e0caa8e63919ca614b2bfd308ccfe50c9ea888e1ee4446d682cb5034627f97b05392c04e835556c31c52816a48e4fb196693206b8afb4408662b3cb575: -bf5ba5d6a49dd5ef7b4d5d7d3e4ecc505c01f6ccee4c54b5ef7b40af6a4541401be034f813017b900d8990af45fad5b5214b573bd303ef7a75ef4b8c5c5b9842:1be034f813017b900d8990af45fad5b5214b573bd303ef7a75ef4b8c5c5b9842:16152c2e037b1c0d3219ced8e0674aee6b57834b55106c5344625322da638ecea2fc9a424a05ee9512d48fcf75dd8bd4691b3c10c28ec98ee1afa5b863d1c36795ed18105db3a9aabd9d2b4c1747adbaf1a56ffcc0c533c1c0faef331cdb79d961fa39f880a1b8b1164741822efb15a7259a465bef212855751fab66a897bfa211abe0ea2f2e1cd8a11d80e142cde1263eec267a3138ae1fcf4099db0ab53d64f336f4bcd7a363f6db112c0a2453051a0006f813aaf4ae948a2090619374fa58052409c28ef76225687df3cb2d1b0bfb43b09f47f1232f790e6d8dea759e57942099f4c4bd3390f28afc2098244961465c643fc8b29766af2bcbc5440b86e83608cfc937be98bb4827fd5e6b689adc2e26513db531076a6564396255a09975b7034dac06461b255642e3a7ed75fa9fc265011f5f6250382a84ac268d63ba64:279cace6fdaf3945e3837df474b28646143747632bede93e7a66f5ca291d2c24978512ca0cb8827c8c322685bd605503a5ec94dbae61bbdcae1e49650602bc0716152c2e037b1c0d3219ced8e0674aee6b57834b55106c5344625322da638ecea2fc9a424a05ee9512d48fcf75dd8bd4691b3c10c28ec98ee1afa5b863d1c36795ed18105db3a9aabd9d2b4c1747adbaf1a56ffcc0c533c1c0faef331cdb79d961fa39f880a1b8b1164741822efb15a7259a465bef212855751fab66a897bfa211abe0ea2f2e1cd8a11d80e142cde1263eec267a3138ae1fcf4099db0ab53d64f336f4bcd7a363f6db112c0a2453051a0006f813aaf4ae948a2090619374fa58052409c28ef76225687df3cb2d1b0bfb43b09f47f1232f790e6d8dea759e57942099f4c4bd3390f28afc2098244961465c643fc8b29766af2bcbc5440b86e83608cfc937be98bb4827fd5e6b689adc2e26513db531076a6564396255a09975b7034dac06461b255642e3a7ed75fa9fc265011f5f6250382a84ac268d63ba64: -65de297b70cbe80980500af0561a24db50001000125f4490366d8300d3128592ba8e2ad929bdcea538741042b57f2067d3153707a453770db9f3c4ca75504d24:ba8e2ad929bdcea538741042b57f2067d3153707a453770db9f3c4ca75504d24:131d8f4c2c94b153565b86592e770c987a443461b39aa2408b29e213ab057affc598b583739d6603a83fef0afc514721db0e76f9bd1b72b98c565cc8881af5747c0ba6f58c53dd2377da6c0d3aa805620cc4e75d52aabcba1f9b2849e08bd1b6b92e6f06615b814519606a02dc65a8609f5b29e9c2af5a894f7116ef28cfd1e7b76b64061732f7a5a3f8aa4c2e569e627a3f9749aa597be49d6b94436c352dd5fa7b83c92d2610faa32095ca302152d91a3c9776750e758ee8e9e402c6f5385eaa5df23850e54beb1be437a416c7115ed6aa6de13b55482532787e0bee34b83f3084406765635497c931b62a0518f1fbc2b891dc7262c7c6b67eda594fa530d74c9329bad5be94c287fbcde53aa80272b83322613d9368e5904076fdbcc88b2c0e59c10b02c448e00d1b3e7a9c9640feffb9523a8a60e1d83f04a4b8df69153b:7a9b736b01cc92a3349f1a3c32dbd91959825394ff443c567405e899c8185ce8fad9500e1fce89d95a6253c00477435acf04bff993de1b00495def0834ee1f07131d8f4c2c94b153565b86592e770c987a443461b39aa2408b29e213ab057affc598b583739d6603a83fef0afc514721db0e76f9bd1b72b98c565cc8881af5747c0ba6f58c53dd2377da6c0d3aa805620cc4e75d52aabcba1f9b2849e08bd1b6b92e6f06615b814519606a02dc65a8609f5b29e9c2af5a894f7116ef28cfd1e7b76b64061732f7a5a3f8aa4c2e569e627a3f9749aa597be49d6b94436c352dd5fa7b83c92d2610faa32095ca302152d91a3c9776750e758ee8e9e402c6f5385eaa5df23850e54beb1be437a416c7115ed6aa6de13b55482532787e0bee34b83f3084406765635497c931b62a0518f1fbc2b891dc7262c7c6b67eda594fa530d74c9329bad5be94c287fbcde53aa80272b83322613d9368e5904076fdbcc88b2c0e59c10b02c448e00d1b3e7a9c9640feffb9523a8a60e1d83f04a4b8df69153b: -0826e7333324e7ec8c764292f6015d4670e9b8d7c4a89e8d909e8ef435d18d15ffb2348ca8a018058be71d1512f376f91e8b0d552581254e107602217395e662:ffb2348ca8a018058be71d1512f376f91e8b0d552581254e107602217395e662:7f9e3e2f03c9df3d21b990f5a4af8295734afe783accc34fb1e9b8e95a0fd837af7e05c13cda0de8fadac9205265a0792b52563bdc2fee766348befcc56b88bbb95f154414fb186ec436aa62ea6fcabb11c017a9d2d15f67e595980e04c9313bc94fbc8c1134c2f40332bc7e311ac1ce11b505f8572ada7fbe196fba822d9a914492fa7185e9f3bea4687200a524c673a1cdf87eb3a140dcdb6a8875613488a2b00adf7175341c1c257635fa1a53a3e21d60c228399eea0991f112c60f653d7148e2c5ceb98f940831f070db1084d79156cc82c46bc9b8e884f3fa81be2da4cdda46bcaa24cc461f76ee647bb0f0f8c15ac5daa795b945e6f85bb310362e48d8095c782c61c52b481b4b002ad06ea74b8d306eff71abf21db710a8913cbe48332be0a0b3f31e0c7a6eba85ce33f357c7aeccd30bfb1a6574408b66fe404d31c3c5:4bac7fabec8724d81ab09ae130874d70b5213492104372f601ae5abb10532799373c4dad215876441f474e2c006be37c3c8f5f6f017d0870414fd276a8f428087f9e3e2f03c9df3d21b990f5a4af8295734afe783accc34fb1e9b8e95a0fd837af7e05c13cda0de8fadac9205265a0792b52563bdc2fee766348befcc56b88bbb95f154414fb186ec436aa62ea6fcabb11c017a9d2d15f67e595980e04c9313bc94fbc8c1134c2f40332bc7e311ac1ce11b505f8572ada7fbe196fba822d9a914492fa7185e9f3bea4687200a524c673a1cdf87eb3a140dcdb6a8875613488a2b00adf7175341c1c257635fa1a53a3e21d60c228399eea0991f112c60f653d7148e2c5ceb98f940831f070db1084d79156cc82c46bc9b8e884f3fa81be2da4cdda46bcaa24cc461f76ee647bb0f0f8c15ac5daa795b945e6f85bb310362e48d8095c782c61c52b481b4b002ad06ea74b8d306eff71abf21db710a8913cbe48332be0a0b3f31e0c7a6eba85ce33f357c7aeccd30bfb1a6574408b66fe404d31c3c5: -00ad6227977b5f38ccda994d928bba9086d2daeb013f8690db986648b90c1d4591a4ea005752b92cbebf99a8a5cbecd240ae3f016c44ad141b2e57ddc773dc8e:91a4ea005752b92cbebf99a8a5cbecd240ae3f016c44ad141b2e57ddc773dc8e:cb5bc5b98b2efce43543e91df041e0dbb53ed8f67bf0f197c52b2211e7a45e2e1ec818c1a80e10abf6a43535f5b79d974d8ae28a2295c0a6521763b607d5103c6aef3b2786bd5afd7563695660684337bc3090739fb1cd53a9d644139b6d4caec75bda7f2521fbfe676ab45b98cb317aa7ca79fc54a3d7c578466a6aa64e434e923465a7f211aa0c61681bb8486e90206a25250d3fdae6fb03299721e99e2a914910d91760089b5d281e131e6c836bc2de08f7e02c48d323c647e9536c00ec1039201c0362618c7d47aa8e7b9715ffc439987ae1d31154a6198c5aa11c128f4082f556c99baf103ecadc3b2f3b2ec5b469623bc03a53caf3814b16300aedbda538d676d1f607102639db2a62c446707ce6469bd873a0468225be88b0aef5d4020459b94b32fe2b0133e92e7ba54dd2a5397ed85f966ab39ed0730cca8e7dacb8a336:dc501db79fd782bc88cae792557d5d273f9ba560c7d90037fe84ac879d684f612a77452c4443e95c07b8be192c35769b17bbdfca42280de796d92119d833670dcb5bc5b98b2efce43543e91df041e0dbb53ed8f67bf0f197c52b2211e7a45e2e1ec818c1a80e10abf6a43535f5b79d974d8ae28a2295c0a6521763b607d5103c6aef3b2786bd5afd7563695660684337bc3090739fb1cd53a9d644139b6d4caec75bda7f2521fbfe676ab45b98cb317aa7ca79fc54a3d7c578466a6aa64e434e923465a7f211aa0c61681bb8486e90206a25250d3fdae6fb03299721e99e2a914910d91760089b5d281e131e6c836bc2de08f7e02c48d323c647e9536c00ec1039201c0362618c7d47aa8e7b9715ffc439987ae1d31154a6198c5aa11c128f4082f556c99baf103ecadc3b2f3b2ec5b469623bc03a53caf3814b16300aedbda538d676d1f607102639db2a62c446707ce6469bd873a0468225be88b0aef5d4020459b94b32fe2b0133e92e7ba54dd2a5397ed85f966ab39ed0730cca8e7dacb8a336: -1521c6dbd6f724de73eaf7b56264f01035c04e01c1f3eb3cbe83efd26c439ada2f61a26ffb68ba4f6e141529dc2617e8531c7151404808093b4fa7fedaea255d:2f61a26ffb68ba4f6e141529dc2617e8531c7151404808093b4fa7fedaea255d:3e3c7c490788e4b1d42f5cbcae3a9930bf617ebdff447f7be2ac2ba7cd5bcfc015760963e6fe5b956fb7cdb35bd5a17f5429ca664f437f08753a741c2bc8692b71a9115c582a25b2f74d329854d60b7817c079b3523aaff8793c2f72fff8cd10592c54e738df1d6452fb72da131c6731ea5c953c62ea177ac1f4735e5154477387109afae15f3ed6eeb08606e28c81d4386f03b9376924b6ef8d221ee29547f82a7ede48e1dc17723e3d42171eeaf96ac84bedc2a01dd86f4d085734fd69f91b5263e439083ff0318536adff4147308e3aafd1b58bb74f6fb0214a46fdcd3524f18df5a719ce57319e791b4ea606b499bfa57a60e707f94e18f1fed22f91bc79e6364a843f9cbf93825c465e9cae9072bc9d3ec4471f21ab2f7e99a633f587aac3db78ae9666a89a18008dd61d60218554411a65740ffd1ae3adc06595e3b7876407b6:a817ed23ec398a128601c1832dc6af7643bf3a5f517bcc579450fdb4759028f4966164125f6ebd0d6bf86ff298a39c766d0c21fdb0cbfdf81cd0eb1f03cd8a083e3c7c490788e4b1d42f5cbcae3a9930bf617ebdff447f7be2ac2ba7cd5bcfc015760963e6fe5b956fb7cdb35bd5a17f5429ca664f437f08753a741c2bc8692b71a9115c582a25b2f74d329854d60b7817c079b3523aaff8793c2f72fff8cd10592c54e738df1d6452fb72da131c6731ea5c953c62ea177ac1f4735e5154477387109afae15f3ed6eeb08606e28c81d4386f03b9376924b6ef8d221ee29547f82a7ede48e1dc17723e3d42171eeaf96ac84bedc2a01dd86f4d085734fd69f91b5263e439083ff0318536adff4147308e3aafd1b58bb74f6fb0214a46fdcd3524f18df5a719ce57319e791b4ea606b499bfa57a60e707f94e18f1fed22f91bc79e6364a843f9cbf93825c465e9cae9072bc9d3ec4471f21ab2f7e99a633f587aac3db78ae9666a89a18008dd61d60218554411a65740ffd1ae3adc06595e3b7876407b6: -17e5f0a8f34751babc5c723ecf339306992f39ea065ac140fcbc397d2dd32c4b4f1e23cc0f2f69c88ef9162ab5f8c59fb3b8ab2096b77e782c63c07c8c4f2b60:4f1e23cc0f2f69c88ef9162ab5f8c59fb3b8ab2096b77e782c63c07c8c4f2b60:c0fad790024019bd6fc08a7a92f5f2ac35cf6432e2eaa53d482f6e1204935336cb3ae65a63c24d0ec6539a10ee18760f2f520537774cdec6e96b55536011daa8f8bcb9cdaf6df5b34648448ac7d7cb7c6bd80d67fbf330f8765297766046a925ab52411d1604c3ed6a85173040125658a32cf4c854ef2813df2be6f3830e5eee5a6163a83ca8849f612991a31e9f88028e50bf8535e11755fad029d94cf25959f6695d09c1ba4315d40f7cf51b3f8166d02faba7511ecd8b1dded5f10cd6843455cff707ed225396c61d0820d20ada70d0c3619ff679422061c9f7c76e97d5a37af61fd62212d2dafc647ebbb979e61d9070ec03609a07f5fc57d119ae64b7a6ef92a5afae660a30ed48d702cc3128c633b4f19060a0578101729ee979f790f45bdbb5fe1a8a62f01a61a31d61af07030450fa0417323e9407bc76e73130e7c69d62e6a7:efe2cb63fe7b4fc98946dc82fb6998e741ed9ce6b9c1a93bb45bc0a7d8396d7405282b43fe363ba5b23589f8e1fae130e157ce888cd72d053d0cc19d257a4300c0fad790024019bd6fc08a7a92f5f2ac35cf6432e2eaa53d482f6e1204935336cb3ae65a63c24d0ec6539a10ee18760f2f520537774cdec6e96b55536011daa8f8bcb9cdaf6df5b34648448ac7d7cb7c6bd80d67fbf330f8765297766046a925ab52411d1604c3ed6a85173040125658a32cf4c854ef2813df2be6f3830e5eee5a6163a83ca8849f612991a31e9f88028e50bf8535e11755fad029d94cf25959f6695d09c1ba4315d40f7cf51b3f8166d02faba7511ecd8b1dded5f10cd6843455cff707ed225396c61d0820d20ada70d0c3619ff679422061c9f7c76e97d5a37af61fd62212d2dafc647ebbb979e61d9070ec03609a07f5fc57d119ae64b7a6ef92a5afae660a30ed48d702cc3128c633b4f19060a0578101729ee979f790f45bdbb5fe1a8a62f01a61a31d61af07030450fa0417323e9407bc76e73130e7c69d62e6a7: -0cd7aa7d605e44d5ffb97966b2cb93c189e4c5a85db87fad7ab8d62463c59b594889855fe4116b4913927f47f2273bf559c3b394a983631a25ae597033185e46:4889855fe4116b4913927f47f2273bf559c3b394a983631a25ae597033185e46:28a55dda6cd0844b6577c9d6da073a4dc35cbc98ac158ab54cf88fd20cc87e83c4bba2d74d82ce0f4854ec4db513de400465aaa5eee790bc84f16337072d3a91cde40d6e0df1ba0cc0645f5d5cbbb642381d7b9e211d25267a8acf77d1edb69c3a630f5b133d24f046a81bf22ff03b31d8447e12c3f7b77114a70cbd20bbd08b0b3827a6bbcf90409e344447a7fbc59bdd97d729071f8d71dcc33e6ef2cbab1d411edf13734db1dd9703276f5eb2d6aa2cb8952dd6712bfae809ce08c3aa502b8135713fac0a9c25b1d45b6a5831e02421bba65b81a596efa24b0576bd1dc7fdfb49be762875e81bd540722bc06140b9aa2ef7b84a801e41ded68d4546ac4873d9e7ced649b64fadaf0b5c4b6eb8d036315233f4326ca01e03393050cd027c24f67303fb846bd2c6b3dba06bed0d59a36289d24bd648f7db0b3a81346612593e3ddd18c557:bf9115fd3d02706e398d4bf3b02a82674ff3041508fd39d29f867e501634b9261f516a794f98738d7c7013a3f2f858ffdd08047fb6bf3dddfb4b4f4cbeef300328a55dda6cd0844b6577c9d6da073a4dc35cbc98ac158ab54cf88fd20cc87e83c4bba2d74d82ce0f4854ec4db513de400465aaa5eee790bc84f16337072d3a91cde40d6e0df1ba0cc0645f5d5cbbb642381d7b9e211d25267a8acf77d1edb69c3a630f5b133d24f046a81bf22ff03b31d8447e12c3f7b77114a70cbd20bbd08b0b3827a6bbcf90409e344447a7fbc59bdd97d729071f8d71dcc33e6ef2cbab1d411edf13734db1dd9703276f5eb2d6aa2cb8952dd6712bfae809ce08c3aa502b8135713fac0a9c25b1d45b6a5831e02421bba65b81a596efa24b0576bd1dc7fdfb49be762875e81bd540722bc06140b9aa2ef7b84a801e41ded68d4546ac4873d9e7ced649b64fadaf0b5c4b6eb8d036315233f4326ca01e03393050cd027c24f67303fb846bd2c6b3dba06bed0d59a36289d24bd648f7db0b3a81346612593e3ddd18c557: -33371d9e892f9875052ac8e325ba505e7477c1ace24ba7822643d43d0acef3de35929bded27c249c87d8b8d82f59260a575327b546c3a167c69f5992d5b8e006:35929bded27c249c87d8b8d82f59260a575327b546c3a167c69f5992d5b8e006:27a32efba28204be59b7ff5fe488ca158a91d5986091ecc4458b49e090dd37cbfede7c0f46186fabcbdff78d2844155808efffd873ed9c9261526e04e4f7050b8d7bd267a0fe3d5a449378d54a4febbd2f26824338e2aaaf35a32ff0f62504bda5c2e44abc63159f336cf25e6bb40ddb7d8825dff18fd51fc01951eaedcd33707007e1203ca58b4f7d242f8166a907e099932c001bfb1ec9a61e0ef2da4e8446af208201315d69681710d425d2400c387d7b9df321a4aec602b9c656c3e2310bff8756d18b802134b15604f4edc111149a9879e31241dd34f702f4c349617b13529769a772f5e52a89c098e0dca5920667893a250061b17991626eb9319298685be46b6a8b68422444fa5a36bcf3a687e2eccb9322c87dc80165da898930850b98fc863cada1aa99c6d61c451b9ccf4874c7f0e75b0a0c602f044812c71765adaf02025395b0:985ca446ddc007827cc8f2852cbd8115ef8c5975e9d7ce96d74dfed859aa14a4c15254006bea5e08359efe2625d715e0897ee5a16f151203be5010418637de0527a32efba28204be59b7ff5fe488ca158a91d5986091ecc4458b49e090dd37cbfede7c0f46186fabcbdff78d2844155808efffd873ed9c9261526e04e4f7050b8d7bd267a0fe3d5a449378d54a4febbd2f26824338e2aaaf35a32ff0f62504bda5c2e44abc63159f336cf25e6bb40ddb7d8825dff18fd51fc01951eaedcd33707007e1203ca58b4f7d242f8166a907e099932c001bfb1ec9a61e0ef2da4e8446af208201315d69681710d425d2400c387d7b9df321a4aec602b9c656c3e2310bff8756d18b802134b15604f4edc111149a9879e31241dd34f702f4c349617b13529769a772f5e52a89c098e0dca5920667893a250061b17991626eb9319298685be46b6a8b68422444fa5a36bcf3a687e2eccb9322c87dc80165da898930850b98fc863cada1aa99c6d61c451b9ccf4874c7f0e75b0a0c602f044812c71765adaf02025395b0: -beedb8073df58f8c1bffbdbd77ec7decb2c82a9babecefc0331507bdc2c2a7e7b27e908b805e296fc30d2e474b060cd50c0f6f520b3671712183bd89d4e733e9:b27e908b805e296fc30d2e474b060cd50c0f6f520b3671712183bd89d4e733e9:35ca57f0f915e5209d54ea4b871ffb585354df1b4a4a1796fbe4d6227d3e1aba5171ed0391a79e83e24d82fdafd15c17b28bf6c94d618c74d65264e58faaacd2902872fdd0efa22e8d2d7ce8e3b8197f0c3615b0a385235fa9fd8e4564ee6e6b1650b4cfb94d872c805c32d4f3a18f966461d3adbb605fa525884f8eb197627396ba4d995d78ac02948a0eaabb58519b9a8e2e7985cd1de2c71d8918d96a0168660ce17cddf364e3ec0d4bd90f2104751a1927ee1d23f3e7a69840ed040b00e5f6e4866ec58813149cc382aebf6162608c79574d553f47230e924a0ef1ebf55d8e1a52abb62a2d7ac86027c7c03cc83fa1949da29e2f3037ab986fd2fffe650e3149babae5a50b1ee9696f3babec72e29697c82422814d272085500fd837fe3c7a973ef4c169af12dd7f02700620bb045bdbf84623f326350570b3cadbc9aea4200b28287e17ab:8c890cccadc7760e1e82e43c44b3dc0b685a48b479ae13cc0a6b0557d0fb1cbabba63d2a96843412ea8d36c50acbf52b92cfb2dce49dc48af6ddcf8ee47a860835ca57f0f915e5209d54ea4b871ffb585354df1b4a4a1796fbe4d6227d3e1aba5171ed0391a79e83e24d82fdafd15c17b28bf6c94d618c74d65264e58faaacd2902872fdd0efa22e8d2d7ce8e3b8197f0c3615b0a385235fa9fd8e4564ee6e6b1650b4cfb94d872c805c32d4f3a18f966461d3adbb605fa525884f8eb197627396ba4d995d78ac02948a0eaabb58519b9a8e2e7985cd1de2c71d8918d96a0168660ce17cddf364e3ec0d4bd90f2104751a1927ee1d23f3e7a69840ed040b00e5f6e4866ec58813149cc382aebf6162608c79574d553f47230e924a0ef1ebf55d8e1a52abb62a2d7ac86027c7c03cc83fa1949da29e2f3037ab986fd2fffe650e3149babae5a50b1ee9696f3babec72e29697c82422814d272085500fd837fe3c7a973ef4c169af12dd7f02700620bb045bdbf84623f326350570b3cadbc9aea4200b28287e17ab: -9184ef618816832592bc8eb35f4ffd4ff98dfbf7776c90f2aad212ce7e03351e687b7726010d9bde2c90e573cd2a2a702ff28c4a2af70afc7315c94d575601e5:687b7726010d9bde2c90e573cd2a2a702ff28c4a2af70afc7315c94d575601e5:729eb7e54a9d00c58617af18c345b8dc6e5b4e0f57de2f3c02e54a2ec8f1425ec2e240775b5ab0c10f84ac8bafda4584f7e21c655faecd8030a98906bd68398f26b5d58d92b6cf045e9bd9743c74c9a342ec61ce57f37b981eac4d8bf034608866e985bb68686a68b4a2af88b992a2a6d2dc8ce88bfb0a36cf28bbab7024abfa2bea53313b66c906f4f7cf66970f540095bd0104aa4924dd82e15413c22679f847e48cd0c7ec1f677e005fec0177fbd5c559fc39add613991fbaeae4d24d39d309ef74647f8192cc4c62d0642028c76a1b951f6bc9639deb91ecc08be6043f2109705a42c7eae712649d91d96ccbbfb63d8d0dd6dd112160f61361ecdc6793929ca9aef9ab56944a6fa4a7df1e279eaf58ce8323a9cf62c94279fff7440fbc936baa61489c999330badcb9fc0e184bc5093f330cbb242f71fb378738fea10511dd438364d7f76bcc:b3c24e75132c563475422d5ea412b5c1e8e6e5ea1c08ead1393c412da134c9a1638284ea7e2ca032fe3d3e32a9066a8c8839903f6ef46e966bb5e492d8c2aa00729eb7e54a9d00c58617af18c345b8dc6e5b4e0f57de2f3c02e54a2ec8f1425ec2e240775b5ab0c10f84ac8bafda4584f7e21c655faecd8030a98906bd68398f26b5d58d92b6cf045e9bd9743c74c9a342ec61ce57f37b981eac4d8bf034608866e985bb68686a68b4a2af88b992a2a6d2dc8ce88bfb0a36cf28bbab7024abfa2bea53313b66c906f4f7cf66970f540095bd0104aa4924dd82e15413c22679f847e48cd0c7ec1f677e005fec0177fbd5c559fc39add613991fbaeae4d24d39d309ef74647f8192cc4c62d0642028c76a1b951f6bc9639deb91ecc08be6043f2109705a42c7eae712649d91d96ccbbfb63d8d0dd6dd112160f61361ecdc6793929ca9aef9ab56944a6fa4a7df1e279eaf58ce8323a9cf62c94279fff7440fbc936baa61489c999330badcb9fc0e184bc5093f330cbb242f71fb378738fea10511dd438364d7f76bcc: -354e13152ee1fe748a1252204c6527bdc1b1eb2eb53678150e6359924708d812d45ff6c5fb83e7bb9669aa8960deb7dbc665c988439b6c9ef672c6811dc8bcf6:d45ff6c5fb83e7bb9669aa8960deb7dbc665c988439b6c9ef672c6811dc8bcf6:8e5fccf66b1ba6169cb685733d9d0e0190361c90bcab95c163285a97fe356d2bdcde3c9380268805a384d063da09ccd9969cc3ff7431e60a8e9f869cd62faa0e356151b280bc526e577c2c538c9a724dc48bf88b70321d7e1eeedb3c4af706748c942e67bdabdb41bec2977b1523069e31e29b76300288f88a51b384b80cc2526f1679340ddec3881f5cd28b0378d9cd0a812b68dd3f68f7a23e1b54bee7466ac765cf38df04d67441dfa498c4bffc52045fa6d2dbcdbfa33dfaa77644ffccef0decdb6790c70a0d734ec287cc338cb5a909c0055189301169c4f7702c05c0911a27b16ef9ed934fa6a0ca7b13e413523422535647968030edc40cd73e7d6b345b7581f438316d68e3cd292b846d3f4f7c4862bc7e6b3fb89a27f6f60cd7db2e34ec9aae1013fe37acff8ad888cb9a593ef5e621eae5186c58b31dcfde22870e336d33f440f6b8d49a:de2b46e65f3decef34332e500f2e11306fbdcf1be85a1c1ee68ba3045dcec2c7be608d22927da1f44c0e2083ae622cf3c29d893887994efcfa2ca594f5051f038e5fccf66b1ba6169cb685733d9d0e0190361c90bcab95c163285a97fe356d2bdcde3c9380268805a384d063da09ccd9969cc3ff7431e60a8e9f869cd62faa0e356151b280bc526e577c2c538c9a724dc48bf88b70321d7e1eeedb3c4af706748c942e67bdabdb41bec2977b1523069e31e29b76300288f88a51b384b80cc2526f1679340ddec3881f5cd28b0378d9cd0a812b68dd3f68f7a23e1b54bee7466ac765cf38df04d67441dfa498c4bffc52045fa6d2dbcdbfa33dfaa77644ffccef0decdb6790c70a0d734ec287cc338cb5a909c0055189301169c4f7702c05c0911a27b16ef9ed934fa6a0ca7b13e413523422535647968030edc40cd73e7d6b345b7581f438316d68e3cd292b846d3f4f7c4862bc7e6b3fb89a27f6f60cd7db2e34ec9aae1013fe37acff8ad888cb9a593ef5e621eae5186c58b31dcfde22870e336d33f440f6b8d49a: -7ff62d4b3c4d99d342d4bb401d726b21e99f4ef592149fc311b68761f5567ff67fdfdb9eca29d3f01d9486d7e112ce03aa37b91326a4283b9c03999c5eda099a:7fdfdb9eca29d3f01d9486d7e112ce03aa37b91326a4283b9c03999c5eda099a:99c44c796572a4823fc6c3807730839173774c05dbfc1492ed0d00509a95a1de37274b3135ed0456a1718e576597dc13f2a2ab37a45c06cbb4a2d22afad4d5f3d90ab3d8da4dcdaa06d44f2219088401c5dceee26055c4782f78d7d63a380608e1bef89eeef338c2f0897da106fafce2fb2ebc5db669c7c172c9cfe77d3109d239fe5d005c8ee751511b5a88317c729b0d8b70b52f6bd3cda2fe865c77f36e4f1b635f336e036bd718bec90ee78a802811510c4058c1ba364017253aa842922e1dd7d7a0f0fc9c69e43fc4eaeffaaf1ae5fa5d2d73b43079617baba030923fe5b13d2c1c4fe6fac3f2db74e2020a734b6121a0302fce820ba0580ce6135348fdf0632e0008df03ee112168f5cfa0037a26a1f69b1f1317edf2a3ab367455a77e00691215d7aa3133c2159d3da2b134cf04f0defbf07a6064011e64dd14d4f8f064356655428804c2771a:058f79927fbf6178724815c7b11c63baaa90bcc15d7272be082f8a9141861c816433055f6cf6491424853f9ec78bb91ace913a93411b4e5ed58bc4ba5715c60a99c44c796572a4823fc6c3807730839173774c05dbfc1492ed0d00509a95a1de37274b3135ed0456a1718e576597dc13f2a2ab37a45c06cbb4a2d22afad4d5f3d90ab3d8da4dcdaa06d44f2219088401c5dceee26055c4782f78d7d63a380608e1bef89eeef338c2f0897da106fafce2fb2ebc5db669c7c172c9cfe77d3109d239fe5d005c8ee751511b5a88317c729b0d8b70b52f6bd3cda2fe865c77f36e4f1b635f336e036bd718bec90ee78a802811510c4058c1ba364017253aa842922e1dd7d7a0f0fc9c69e43fc4eaeffaaf1ae5fa5d2d73b43079617baba030923fe5b13d2c1c4fe6fac3f2db74e2020a734b6121a0302fce820ba0580ce6135348fdf0632e0008df03ee112168f5cfa0037a26a1f69b1f1317edf2a3ab367455a77e00691215d7aa3133c2159d3da2b134cf04f0defbf07a6064011e64dd14d4f8f064356655428804c2771a: -6cabadd03f8a2e6ebab96a74f80e18164e4d1b6baa678f5a82e25604af989aaf2a4a3179564194e00100c18bc35351d8b135bbae5b32b28fce1d7b6766ca4b32:2a4a3179564194e00100c18bc35351d8b135bbae5b32b28fce1d7b6766ca4b32:279f78cf3b9ccfc6e1b01e1a82f50ed172e9a8e1e702bb15661dd7dc3a456ff7a7a7fdfb081db3867079630c7f70fd753292ec60ecbf50632e9aa45b996505c66e6dc3c6ae892e21b6a8705e4bbae8f16a3378554b31fdb0139dcd15c96a8a7e4b88756a86d18db5dc74fd7691197dd88e2c7d5df52b049344cdc477c9cd7e89eda99ccfb1d00814d0152b9654df3279372ca5f18b1c946f2894a76b079ddb1c3cd61fbb969aeec9193a6b88fb7d136c07f9821e5c1074b4e93bcaf6fa14d0d1d7e1707589d77ec1337206e53a1f06cc26672ff95c13d5ff444766931ba30a0afdcdadd2098e9c41fd87a3f23cd16dbb0efbf8092ce33e327f42610990e1cee6cb8e54951aa081e69765ae4009aeed758e768de50c23d9a22b4a06dc4d19fc8cbd0cdef4c983461755d0a3b5d6a9c12253e09568339ff7e5f78c5fdf7ec89f9186a621a8c0eed11b67022e:4e65c6c1d493045e8a9250e397c1d1d30ffed24db66a8961aa458f8f0fcb760c39fe8657d7ab8f84000b96d519717cff71f926522c1efec7f8b2624eae55f60c279f78cf3b9ccfc6e1b01e1a82f50ed172e9a8e1e702bb15661dd7dc3a456ff7a7a7fdfb081db3867079630c7f70fd753292ec60ecbf50632e9aa45b996505c66e6dc3c6ae892e21b6a8705e4bbae8f16a3378554b31fdb0139dcd15c96a8a7e4b88756a86d18db5dc74fd7691197dd88e2c7d5df52b049344cdc477c9cd7e89eda99ccfb1d00814d0152b9654df3279372ca5f18b1c946f2894a76b079ddb1c3cd61fbb969aeec9193a6b88fb7d136c07f9821e5c1074b4e93bcaf6fa14d0d1d7e1707589d77ec1337206e53a1f06cc26672ff95c13d5ff444766931ba30a0afdcdadd2098e9c41fd87a3f23cd16dbb0efbf8092ce33e327f42610990e1cee6cb8e54951aa081e69765ae4009aeed758e768de50c23d9a22b4a06dc4d19fc8cbd0cdef4c983461755d0a3b5d6a9c12253e09568339ff7e5f78c5fdf7ec89f9186a621a8c0eed11b67022e: -0fa0c32c3ae34be51b92f91945405981a8e202488558a8e220c288c7d6a5532dd6aee62bd91fc9453635ffcc02b2f38dcab13285140380580ccdff0865df0492:d6aee62bd91fc9453635ffcc02b2f38dcab13285140380580ccdff0865df0492:53f44be0e5997ff07264cb64ba1359e2801def8755e64a2362bddaf597e672d021d34fface6d97e0f2b1f6ae625fd33d3c4f6e9ff7d0c73f1da8defb23f324975e921bb2473258177a16612567edf7d5760f3f3e3a6d26aaabc5fde4e2043f73fa70f128020933b1ba3b6bd69498e9503ea670f1ed880d3651f2e4c59e79cabc86e9b703394294112d5d8e213c317423b525a6df70106a9d658a262028b5f45100cb77d1150d8fe461eed434f241015f3276ad7b09a291b4a7f35e3c30051cbf13b1d4a7fa0c81a50f939e7c49673afdc87883c9e3e61f5a1df03755470fda74bf23ea88676b258a97a280d5f90b52b714b596035bae08c8d0fe6d94f8949559b1f27d7116cf59dd3cfbf18202a09c13f5c4fbc8d97225492887d32870c2297e34debd9876d6d01ac27a16b088b079079f2b20feb02537cda314c43cb2dca371b9df37ed11ec97e1a7a6993a:7e9ab85ee94fe4b35dcb545329a0ef25923de5c9dc23e7df1a7e77ab0dcfb89e03f4e785ca6429cb2b0df50da6230f733f00f33a45c4e576cd40bdb84f1ae00153f44be0e5997ff07264cb64ba1359e2801def8755e64a2362bddaf597e672d021d34fface6d97e0f2b1f6ae625fd33d3c4f6e9ff7d0c73f1da8defb23f324975e921bb2473258177a16612567edf7d5760f3f3e3a6d26aaabc5fde4e2043f73fa70f128020933b1ba3b6bd69498e9503ea670f1ed880d3651f2e4c59e79cabc86e9b703394294112d5d8e213c317423b525a6df70106a9d658a262028b5f45100cb77d1150d8fe461eed434f241015f3276ad7b09a291b4a7f35e3c30051cbf13b1d4a7fa0c81a50f939e7c49673afdc87883c9e3e61f5a1df03755470fda74bf23ea88676b258a97a280d5f90b52b714b596035bae08c8d0fe6d94f8949559b1f27d7116cf59dd3cfbf18202a09c13f5c4fbc8d97225492887d32870c2297e34debd9876d6d01ac27a16b088b079079f2b20feb02537cda314c43cb2dca371b9df37ed11ec97e1a7a6993a: -7b06f88026fa86f39fce2426f67cc5996bedd0cfc4b5ebb1b5e3edbb47e080aa3f1469ee6a2e7867e2e9012d402cf5a4861497c01df879a1deb1c539830b58de:3f1469ee6a2e7867e2e9012d402cf5a4861497c01df879a1deb1c539830b58de:71175d4e21721297d9176d817f4e785d9600d923f987fe0b26fd79d33a5ea5d1e818b71f0f92b8c73afddabdcc27f6d16e26aafa874cfd77a00e06c36b041487582bb933760f88b419127345776ea418f83522254fed33819bc5c95f8f8404cc144ebf1486c88515409d3433aaf519d9920f5256e629419e9a95580a35b069b8d25533dfcbc98ad36404a951808e01378c03266326d120046975fde07daef3266caacd821c1403499d7fdf17c033c8d8c3f28f162b5f09dfdaca06285f00c6cb986dfdf5151aa6639608b5b13e78d65a4368585b16138754fbd113835a686cd066c2b89bb0953c24d50e77bf0fc457c1e0fcf5d44da8db9a88f062be3b688d5cdcff1d1c00e81ec9d413882295b341fee8fa427dc109adeb5f284eec202f1bef115bf96b1782d3ccdeb682b69bf92d170c007d5df80e1ed962f677dc24a145a1e4e829e8dec0104e5f78365944:42f133e34e3eb7032a133ed781537ec62e44a5ce8381e5e0bf9e13a914a4b2c757811d6d3b1e86672424ea4230d10f7c610abb7069e61e319b4066a2bd7bc90071175d4e21721297d9176d817f4e785d9600d923f987fe0b26fd79d33a5ea5d1e818b71f0f92b8c73afddabdcc27f6d16e26aafa874cfd77a00e06c36b041487582bb933760f88b419127345776ea418f83522254fed33819bc5c95f8f8404cc144ebf1486c88515409d3433aaf519d9920f5256e629419e9a95580a35b069b8d25533dfcbc98ad36404a951808e01378c03266326d120046975fde07daef3266caacd821c1403499d7fdf17c033c8d8c3f28f162b5f09dfdaca06285f00c6cb986dfdf5151aa6639608b5b13e78d65a4368585b16138754fbd113835a686cd066c2b89bb0953c24d50e77bf0fc457c1e0fcf5d44da8db9a88f062be3b688d5cdcff1d1c00e81ec9d413882295b341fee8fa427dc109adeb5f284eec202f1bef115bf96b1782d3ccdeb682b69bf92d170c007d5df80e1ed962f677dc24a145a1e4e829e8dec0104e5f78365944: -c3f5e149968a24f4de9119531975f443015ccca305d7119ed4749e8bf6d94fc739aaccdb948a4038538a4588322f806bb129b5876c4bec51271afe4f49690045:39aaccdb948a4038538a4588322f806bb129b5876c4bec51271afe4f49690045:c46370e37f2e0cadcf93402f1f0cb048f52881ba750b7a43f56ab11ce348732fb57e7f9aaf8dfcbe455e14e983c248d026a27e7f148d5db5a53f94635702b895127771047a876d14107386c5e0ff8933345bbd7a936d990d33efa28c2ec4e4864ffd2ff576f7c88f954cfc1c459e883bb712dae3cdf6632066f1f4d13a509615b3360cadc5a307f23e52a51b40a6feebe0b18d0e9ee4e348f33cd81a8def222f6a59b12861d335bd9af85cc004be46f1d3a424f4870ae9dc587e5a4ade136b9370649348c33ac3bf1febeebffea37085ed59cac9d9e696470b234609e9a10a9d431ff91e69cb5135fd117ff58a36539744ebe70cea6973c00c7a4d57b62f4a7136d731b8e46ff18ec0ed69070031905075d8541d568cfce6eeb76242b7819a7b6a93552111bb88f165527cfa6966d39fcbe0a7dea008e39c7a3e577ab307cd1d0ea326833d52654e172955f3fcd4:5fa2b531677b00b85b0a313cbd479f55f4ab3ec5cfce5e454d2b74176ccc3399c899f9d6b51ed4c1e76185ac9fe730c4b4014044f7041185bc3c85722eb2ea02c46370e37f2e0cadcf93402f1f0cb048f52881ba750b7a43f56ab11ce348732fb57e7f9aaf8dfcbe455e14e983c248d026a27e7f148d5db5a53f94635702b895127771047a876d14107386c5e0ff8933345bbd7a936d990d33efa28c2ec4e4864ffd2ff576f7c88f954cfc1c459e883bb712dae3cdf6632066f1f4d13a509615b3360cadc5a307f23e52a51b40a6feebe0b18d0e9ee4e348f33cd81a8def222f6a59b12861d335bd9af85cc004be46f1d3a424f4870ae9dc587e5a4ade136b9370649348c33ac3bf1febeebffea37085ed59cac9d9e696470b234609e9a10a9d431ff91e69cb5135fd117ff58a36539744ebe70cea6973c00c7a4d57b62f4a7136d731b8e46ff18ec0ed69070031905075d8541d568cfce6eeb76242b7819a7b6a93552111bb88f165527cfa6966d39fcbe0a7dea008e39c7a3e577ab307cd1d0ea326833d52654e172955f3fcd4: -42305c9302f45ea6f87e26e2208fd94b3c4ad037b1b6c83cf6677aa1096a013c3b97b1f11ce45ba46ffbb25b76bfc5ad7b77f90cc69ed76115dea4029469d587:3b97b1f11ce45ba46ffbb25b76bfc5ad7b77f90cc69ed76115dea4029469d587:d110828d449198d675e74e8e39439fd15e75bf2cc1f430abfb245836885bafc420f754b89d2fbbf6dd3490792e7a4f766073cfe3b302d089831ace869e2730fde45c2121ec3ef217aa9c43fa7cc7e9ed0a01ad9f1d2fc3613638ca9fc193c98b37455bf5dbf8f38b64708dfdca6c21f0975f1017c5da5f6434bda9f033cec2a631ab50318e017b170b240bf01eb8b36c7e1cb59e7736ac34444208132a8f59e4f313d65d849c6a4fdf13e20ecaee3823e589a171b39b2489497b06e6ff58c2c9f1dc5d3aa3bd10e6443e22d42d07b783f79fd43a46e1cde314b663a95f7246dea131fcd46d1dc333c5454f86b2c4e2e424dea405cc2230d4dcd39a2eab2f92845cf6a7994192063f1202749ef52dcb96f2b79ed6a98118ca0b99ba2285490860eb4c61ab78b9ddc6acc7ad883fa5e96f9d029171223abf7573e36230e0a81f6c1311151473ee264f4b842e923dcb3b:18d05e5d01668e83f40fa3bbee28b388acf318d1b0b5ad668c672f345c8eda14c2f884cd2a9039459ce0810bc5b580fe70d3964a43edb49e73a6ff914bbf040cd110828d449198d675e74e8e39439fd15e75bf2cc1f430abfb245836885bafc420f754b89d2fbbf6dd3490792e7a4f766073cfe3b302d089831ace869e2730fde45c2121ec3ef217aa9c43fa7cc7e9ed0a01ad9f1d2fc3613638ca9fc193c98b37455bf5dbf8f38b64708dfdca6c21f0975f1017c5da5f6434bda9f033cec2a631ab50318e017b170b240bf01eb8b36c7e1cb59e7736ac34444208132a8f59e4f313d65d849c6a4fdf13e20ecaee3823e589a171b39b2489497b06e6ff58c2c9f1dc5d3aa3bd10e6443e22d42d07b783f79fd43a46e1cde314b663a95f7246dea131fcd46d1dc333c5454f86b2c4e2e424dea405cc2230d4dcd39a2eab2f92845cf6a7994192063f1202749ef52dcb96f2b79ed6a98118ca0b99ba2285490860eb4c61ab78b9ddc6acc7ad883fa5e96f9d029171223abf7573e36230e0a81f6c1311151473ee264f4b842e923dcb3b: -c57a43dcd7bab8516009546918d71ad459b7345efdca8d4f19929875c839d7222083b444236b9ab31d4e00c89d55c6260fee71ac1a47c4b5ba227404d382b82d:2083b444236b9ab31d4e00c89d55c6260fee71ac1a47c4b5ba227404d382b82d:a4f6d9c281cf81a28a0b9e77499aa24bde96cc1264374491c008294ee0af6f6e4bbb686396f59068d358e30fe9992db0c6f16680a1c71e27a4a907ac607d39bdc3258c7956482fb37996f4beb3e5051b8148019a1c256e2ee999ebc8ce64c54e07fedb4fbd8953ebd93b7d69ce5a0082edd6209d12d3619b4fd2eae916461f72a4ce727157251a19209bbff9fbdbd289436f3fcacc6b4e1318521a47839cba4b14f7d7a21e7b5d6b6a753d5804afcd2b1eb7779b92abab8afa8aa4fa51caec0b85dcd0fc2a0676036d3f56630a831ffeb502861dd89161c708a9c006c73c930ce5b94756426ff18aa112fb4eb9a68500b48d4eedbd4167b6ffd0a11d49443a173ce9d949436748fc0634f06bb08b8f3423f4463dba7b4d199b64df578117f0a2645f0b2a1e2ada27d286f76733f25b82ed1d48a5c3898d4ad621e50ed9060daad40a39532e4d1bf162ce36804d5d4e2d:1edef9bc036971f1fa88edf45393c802e6c1a1631c8a06871a09a320821dce40beca97e53a0361a955a4c6d60b8ca8e400c81340911ccb4f56284041cdbb1804a4f6d9c281cf81a28a0b9e77499aa24bde96cc1264374491c008294ee0af6f6e4bbb686396f59068d358e30fe9992db0c6f16680a1c71e27a4a907ac607d39bdc3258c7956482fb37996f4beb3e5051b8148019a1c256e2ee999ebc8ce64c54e07fedb4fbd8953ebd93b7d69ce5a0082edd6209d12d3619b4fd2eae916461f72a4ce727157251a19209bbff9fbdbd289436f3fcacc6b4e1318521a47839cba4b14f7d7a21e7b5d6b6a753d5804afcd2b1eb7779b92abab8afa8aa4fa51caec0b85dcd0fc2a0676036d3f56630a831ffeb502861dd89161c708a9c006c73c930ce5b94756426ff18aa112fb4eb9a68500b48d4eedbd4167b6ffd0a11d49443a173ce9d949436748fc0634f06bb08b8f3423f4463dba7b4d199b64df578117f0a2645f0b2a1e2ada27d286f76733f25b82ed1d48a5c3898d4ad621e50ed9060daad40a39532e4d1bf162ce36804d5d4e2d: -2dddb6b8fd04fa90ece1a709f8418f2e5d0c9c43afe7cfce19e6ad15a73476f78059de6a7c4776489ecc2e7d707ffce30285bf30a23f78d72db49cfd6ed0d492:8059de6a7c4776489ecc2e7d707ffce30285bf30a23f78d72db49cfd6ed0d492:474baa590a4cd72d5424e51d8257b3d44325bc4c5063a0033c86ebbe99ed7212184c19944d082a115379dd4cece973faa0bca6485bd25f3744a719e70aa0291e1b5a96e637c140616a98263357c76b6eb0083fe51414e386870d0fdc7dd9abe4ff6fb5bbf1e7b15dac3e08e2615f655c3104ceb32a4cc2c9e9c43cf282d346ac253ccc46b635ae040973b49735720ffb890469a567c5824e0c00d7ccd5509a718092a906461c4d6163eaf422418f5fc6e009fc3f529ac61a2f89bb8e0ed45d940c4c2331ff8d8e1d6d58d417d8fc2656a02e8701aee75aed918724eebe4a2cf4744c5c401e217023df68a6f6a0228bd05a679a697d8de7036b9ed269090d3c65486afb91e27954eb15b964665ede7ad008f12fb3a9d0e69c13b4254f43819e0818a4195f68b8a38ae81f3fcb1879c95ab4cd0ffc38e381089260cca967ace5a085b457ab5eb363852101377570f9ac9e38:c634ea7bf72e895a2e796e2834201415b8b45e05e045559284eb9052c0e84f62a5a9f0c9764f7576788c7228b19ef517c195497325a48a9344b147c12fd75509474baa590a4cd72d5424e51d8257b3d44325bc4c5063a0033c86ebbe99ed7212184c19944d082a115379dd4cece973faa0bca6485bd25f3744a719e70aa0291e1b5a96e637c140616a98263357c76b6eb0083fe51414e386870d0fdc7dd9abe4ff6fb5bbf1e7b15dac3e08e2615f655c3104ceb32a4cc2c9e9c43cf282d346ac253ccc46b635ae040973b49735720ffb890469a567c5824e0c00d7ccd5509a718092a906461c4d6163eaf422418f5fc6e009fc3f529ac61a2f89bb8e0ed45d940c4c2331ff8d8e1d6d58d417d8fc2656a02e8701aee75aed918724eebe4a2cf4744c5c401e217023df68a6f6a0228bd05a679a697d8de7036b9ed269090d3c65486afb91e27954eb15b964665ede7ad008f12fb3a9d0e69c13b4254f43819e0818a4195f68b8a38ae81f3fcb1879c95ab4cd0ffc38e381089260cca967ace5a085b457ab5eb363852101377570f9ac9e38: -5547f1004baedfce5cfc0850b05302374aad24f6163994ecd751df3af3c106207ce620787385ee1951ac49a77352ee0d6f8c5cd47df74e9e3216a6324fc7cf7f:7ce620787385ee1951ac49a77352ee0d6f8c5cd47df74e9e3216a6324fc7cf7f:a6c17eeb5b8066c2cd9a89667317a945a0c7c96996e77ae854c509c6cd0631e922ad04503af87a3c4628adafed7600d071c078a22e7f64bda08a362b38b26ca15006d38acf532d0dedea4177a2d33f06956d80e963848ec791b2762fa99449b4f1a1ed9b3f2580be3ac7d7f52fb14421d6222ba76f807750c6cbb0b16f0895fc73d9dfc587e1a9e5d1e58375fbab705b8f0c1fd7df8b3ad446f2f08459e7ed1af59556fbc966dc249c1cf604f3e677c8a09d4363608774bf3811bef0642748c55c516c7a580fa3499050acb30eed870d0d91174cb623e98c3ad121cf81f04e57d49b008424a98a31eeaaf5f38e000f903d48d215ed52f862d636a5a73607de85760167267efe30f8a26ebc5aa0c09f5b258d3361ca69d1d7ee07b59648179ab2170ec50c07f6616f216872529421a6334a4a1ed3d2671ef47bc9a92afb58314e832db8a9003408a0487503fe4f67770dd4b6:29df3ad589009c667baa5e72dabb4e53cb7876de4e7efe5cc21ead7fa878db57f97c1103ddb39a861eb88653c1d4ec3b4306e4584b47b8bc90423119e7e4af00a6c17eeb5b8066c2cd9a89667317a945a0c7c96996e77ae854c509c6cd0631e922ad04503af87a3c4628adafed7600d071c078a22e7f64bda08a362b38b26ca15006d38acf532d0dedea4177a2d33f06956d80e963848ec791b2762fa99449b4f1a1ed9b3f2580be3ac7d7f52fb14421d6222ba76f807750c6cbb0b16f0895fc73d9dfc587e1a9e5d1e58375fbab705b8f0c1fd7df8b3ad446f2f08459e7ed1af59556fbc966dc249c1cf604f3e677c8a09d4363608774bf3811bef0642748c55c516c7a580fa3499050acb30eed870d0d91174cb623e98c3ad121cf81f04e57d49b008424a98a31eeaaf5f38e000f903d48d215ed52f862d636a5a73607de85760167267efe30f8a26ebc5aa0c09f5b258d3361ca69d1d7ee07b59648179ab2170ec50c07f6616f216872529421a6334a4a1ed3d2671ef47bc9a92afb58314e832db8a9003408a0487503fe4f67770dd4b6: -3dd7203c237aefe9e38a201ff341490179905f9f100828da18fcbe58768b5760f067d7b2ff3a957e8373a7d42ef0832bcda84ebf287249a184a212a94c99ea5b:f067d7b2ff3a957e8373a7d42ef0832bcda84ebf287249a184a212a94c99ea5b:db28ed31ac04b0c2decee7a6b24fc9a082cc262ca7ccf2a247d6372ec3e9120ecedb4542ea593fea30335c5ab9dd318a3b4fd5834299cf3f53d9ef46137b273c390ec3c26a0b4470d0d94b77d82cae4b24587837b167bb7f8166710baeb3ee70af797316cb7d05fa57e468ae3f0bd449404d8528808b41fcca62f5e0a2aa5d8f3acab008cc5f6e5ab02777bdcde87f0a10ef06a4bb37fe02c94815cf76bfb8f5cdd865cc26dcb5cf492edfd547b535e2e6a6d8540956dcba62cfea19a9474406e934337e454270e01036ac45793b6b8aceda187a08d56a2ce4e98f42ea375b101a6b9fcb4231d171aa463eeb43586a4b82a387bcddaf71a80fd5c1f7292efc2bd8e70c11eaa817106061b6c461c4883d613cc06c7e2a03f73d90fc55cdc07265eefd36be72270383d6c676cae37c93691f1ae3d927b3a1cd963e4229757ae5231eea73a9f71515628305410ac2593b325cc631:4c036935a96abc0d050d907bedbe9946fb97439f039c742e051ccf09add7df44d17da98c2ca01bdc2424da1e4debf347f8fff48ac8030d2cc07f9575c044be04db28ed31ac04b0c2decee7a6b24fc9a082cc262ca7ccf2a247d6372ec3e9120ecedb4542ea593fea30335c5ab9dd318a3b4fd5834299cf3f53d9ef46137b273c390ec3c26a0b4470d0d94b77d82cae4b24587837b167bb7f8166710baeb3ee70af797316cb7d05fa57e468ae3f0bd449404d8528808b41fcca62f5e0a2aa5d8f3acab008cc5f6e5ab02777bdcde87f0a10ef06a4bb37fe02c94815cf76bfb8f5cdd865cc26dcb5cf492edfd547b535e2e6a6d8540956dcba62cfea19a9474406e934337e454270e01036ac45793b6b8aceda187a08d56a2ce4e98f42ea375b101a6b9fcb4231d171aa463eeb43586a4b82a387bcddaf71a80fd5c1f7292efc2bd8e70c11eaa817106061b6c461c4883d613cc06c7e2a03f73d90fc55cdc07265eefd36be72270383d6c676cae37c93691f1ae3d927b3a1cd963e4229757ae5231eea73a9f71515628305410ac2593b325cc631: -282775df9ebbd7c5a65f3a2b096e36ee64a8f8ea719da77758739e4e7476111da2b49646033a13937cad6b0e914e3cec54989c252ca5643d076555d8c55e56e0:a2b49646033a13937cad6b0e914e3cec54989c252ca5643d076555d8c55e56e0:14cc50c2973ea9d0187a73f71cb9f1ce07e739e049ec2b27e6613c10c26b73a2a966e01ac3be8b505aeaad1485c1c2a3c6c2b00f81b9e5f927b73bfd498601a7622e8544837aad02e72bf72196dc246902e58af253ad7e025e3666d3bfc46b5b02f0eb4a37c9554992abc8651de12fd813177379bb0ce172cd8aaf937f979642bc2ed7c7a430cb14c3cd3101b9f6b91ee3f542acdf017f8c2116297f4564768f4db95dad8a9bcdc8da4d8fb13ef6e2da0b1316d3c8c2f3ed836b35fe2fd33effb409e3bc1b0f85225d2a1de3bfc2d20563946475c4d7ca9fddbaf59ad8f8961d287ae7dd803e7af1fa612329b1bdc04e225600ae731bc01ae0925aed62ac50d46086f3646cf47b072f0d3b044b36f85cec729a8bb2b92883ca4dfb34a8ee8a0273b31af50982bb6131bfa11d55504b1f6f1a0a00438ca26d8ab4f48bcddc9d5a38851abede4151d5b70d720732a00abea2c8b979:15763973859402907d8dcb86adc24a2a168ba3abf2246173d6348afed51ef60b0c0edeff4e10bcef4c6e5778c8bc1f5e9ee0237373445b455155d23de127a20214cc50c2973ea9d0187a73f71cb9f1ce07e739e049ec2b27e6613c10c26b73a2a966e01ac3be8b505aeaad1485c1c2a3c6c2b00f81b9e5f927b73bfd498601a7622e8544837aad02e72bf72196dc246902e58af253ad7e025e3666d3bfc46b5b02f0eb4a37c9554992abc8651de12fd813177379bb0ce172cd8aaf937f979642bc2ed7c7a430cb14c3cd3101b9f6b91ee3f542acdf017f8c2116297f4564768f4db95dad8a9bcdc8da4d8fb13ef6e2da0b1316d3c8c2f3ed836b35fe2fd33effb409e3bc1b0f85225d2a1de3bfc2d20563946475c4d7ca9fddbaf59ad8f8961d287ae7dd803e7af1fa612329b1bdc04e225600ae731bc01ae0925aed62ac50d46086f3646cf47b072f0d3b044b36f85cec729a8bb2b92883ca4dfb34a8ee8a0273b31af50982bb6131bfa11d55504b1f6f1a0a00438ca26d8ab4f48bcddc9d5a38851abede4151d5b70d720732a00abea2c8b979: -4730a5cf9772d7d6665ba787bea4c95252e6ecd63ec62390547bf100c0a46375f9f094f7cc1d40f1926b5b22dce465784468b20ab349bc6d4fdf78d0042bbc5b:f9f094f7cc1d40f1926b5b22dce465784468b20ab349bc6d4fdf78d0042bbc5b:e7476d2e668420e1b0fadfbaa54286fa7fa890a87b8280e26078152295e1e6e55d1241435cc430a8693bb10cde4643f59cbfcc256f45f5090c909a14c7fc49d37bfc25af11e8f4c83f4c32d4aabf43b20fa382bb6622a1848f8ffc4dff3408bb4ec7c67a35b4cdaee5e279c0fc0a66093a9f36a60fdd65e6334a804e845c8530b6fda363b5640337d027243ccfb3c177f43e717896e46ead7f72ca06aa0ff1e77247121baf48be9a445f729ca1390fc46151cbd33fcbd7373f27a6ba55c92cbf6945b09b44b9a4e5800d403070ae66048997b2197f02181a097e563f9b9acc841139258a258bc610d3bd891637356b2edc8c184c35c65af91aaf7b1c16d74a5f5f862548139254ecf550631d5f8849afdb5b64cf366ff2633a93f3a18c39b5150245fb5f33c9e4e2d94af6963a70b88f9e7e519f8fa2a0f2e3749de883d0e6f052a949d0fc7153a8693f6d801d7352eb2f7a465c0e:552c7347bdfe131646ce0932d82a36d2c1b76d7c30ee890e0592e19f9d18b9a56f48d7a9b68c017da6b550c943af4a907baf317e419fbbc96f6cf4bfad42de00e7476d2e668420e1b0fadfbaa54286fa7fa890a87b8280e26078152295e1e6e55d1241435cc430a8693bb10cde4643f59cbfcc256f45f5090c909a14c7fc49d37bfc25af11e8f4c83f4c32d4aabf43b20fa382bb6622a1848f8ffc4dff3408bb4ec7c67a35b4cdaee5e279c0fc0a66093a9f36a60fdd65e6334a804e845c8530b6fda363b5640337d027243ccfb3c177f43e717896e46ead7f72ca06aa0ff1e77247121baf48be9a445f729ca1390fc46151cbd33fcbd7373f27a6ba55c92cbf6945b09b44b9a4e5800d403070ae66048997b2197f02181a097e563f9b9acc841139258a258bc610d3bd891637356b2edc8c184c35c65af91aaf7b1c16d74a5f5f862548139254ecf550631d5f8849afdb5b64cf366ff2633a93f3a18c39b5150245fb5f33c9e4e2d94af6963a70b88f9e7e519f8fa2a0f2e3749de883d0e6f052a949d0fc7153a8693f6d801d7352eb2f7a465c0e: -2770aadd1d123e9547832dfb2a837eba089179ef4f23abc4a53f2a714e423ee23c5fbb07530dd3a20ff35a500e3708926310fed8a899690232b42c15bd86e5dc:3c5fbb07530dd3a20ff35a500e3708926310fed8a899690232b42c15bd86e5dc:a5cc2055eba3cf6f0c6332c1f2ab5854870913b03ff7093bc94f335add44332231d9869f027d82efd5f1227144ab56e3222dc3ddccf062d9c1b0c1024d9b416dfa3ee8a7027923003465e0ffaefb75b9f29dc6bcf213adc5e318fd8ba93a7aa5bfb495de9d7c5e1a196cd3a2d7721f8ba785aa9052a1811c7fcc8f93932765059cab9c9b718945895ef26f3ac048d4cabf91a9e6aa83ac14d43156827837914eb763a23cba53f60f150f4b70203ec1833ff105849457a8da7327661fb23a554164e05fcf0146b10674964be6f6aa0acc94c41ad57180e5180d199bd9102f55d740e81789b15671bbd0670e6de5d97e1ae626d8a0ebc32c8fd9d24737274e47d2dd5941a272e72a598928ad109cde937bf248d57f5d2942983c51e2a89f8f054d5c48dfad8fcf1ffa97f7de6a3a43ca15fc6720efaec69f0836d84223f9776d111ec2bbc69b2dfd58be8ca12c072164b718cd7c246d64:f267715e9a84c7314f2d5869ef4ab8d2149a13f7e8e1c728c423906293b49ce6283454dd1c7b04741df2eabedc4d6ab1397dc95a679df04d2c17d66c79bb7601a5cc2055eba3cf6f0c6332c1f2ab5854870913b03ff7093bc94f335add44332231d9869f027d82efd5f1227144ab56e3222dc3ddccf062d9c1b0c1024d9b416dfa3ee8a7027923003465e0ffaefb75b9f29dc6bcf213adc5e318fd8ba93a7aa5bfb495de9d7c5e1a196cd3a2d7721f8ba785aa9052a1811c7fcc8f93932765059cab9c9b718945895ef26f3ac048d4cabf91a9e6aa83ac14d43156827837914eb763a23cba53f60f150f4b70203ec1833ff105849457a8da7327661fb23a554164e05fcf0146b10674964be6f6aa0acc94c41ad57180e5180d199bd9102f55d740e81789b15671bbd0670e6de5d97e1ae626d8a0ebc32c8fd9d24737274e47d2dd5941a272e72a598928ad109cde937bf248d57f5d2942983c51e2a89f8f054d5c48dfad8fcf1ffa97f7de6a3a43ca15fc6720efaec69f0836d84223f9776d111ec2bbc69b2dfd58be8ca12c072164b718cd7c246d64: -4fdab7c1600e70114b11f533242376af7614b4d5da046ac4bedea21d8a361598a25c9a94d6e4ecd95a4bd6805f762eb1c457a8d45d243238b1839cbba8f441cc:a25c9a94d6e4ecd95a4bd6805f762eb1c457a8d45d243238b1839cbba8f441cc:da405890d11a872c119dab5efcbff61e931f38eccca457edc626d3ea29ed4fe3154fafec1444da74343c06ad90ac9d17b511bcb73bb49d90bafb7c7ea800bd58411df1275c3cae71b700a5dab491a4261678587956aa4a219e1ac6dd3fb2cb8c46197218e726dc7ed234526a6b01c0d72cb93ab3f4f38a08e5940b3f61a72ad2789a0532000fac1d2d2e3ad632ac8b62bb3ff5b99d53597bf4d44b19674924df9b3db3d0253f74627ccab30031c85e291c58b5fa9167522a46746fc307036745d4f9817786e5d300e6c5d503125fea01dec3e3fedbf3861ca2627a0518fb2b24e5a7a014178719e9b345f7b249ce3a413280c8deb674f59a25be92a8ab6400c7c52b0728ae34e22b2ec200c1cbaba2ccd8af29249d17af60c36007a722fc80258a7bebab1cdaad7462a8b7588c2f7e27c6d07afcf60117fed11bd6859e75e3b4fcee3981881e95dd116827dd4b369af069d3c8f2676f8a:5075c090cfbeb6b01802af7f4da5aa4f434d5ee2f3530eebb75c85e08621f83edc08aa96693894a4277633ba81e19e9e55af5c495daa5e1a6f8cbb79c01c7207da405890d11a872c119dab5efcbff61e931f38eccca457edc626d3ea29ed4fe3154fafec1444da74343c06ad90ac9d17b511bcb73bb49d90bafb7c7ea800bd58411df1275c3cae71b700a5dab491a4261678587956aa4a219e1ac6dd3fb2cb8c46197218e726dc7ed234526a6b01c0d72cb93ab3f4f38a08e5940b3f61a72ad2789a0532000fac1d2d2e3ad632ac8b62bb3ff5b99d53597bf4d44b19674924df9b3db3d0253f74627ccab30031c85e291c58b5fa9167522a46746fc307036745d4f9817786e5d300e6c5d503125fea01dec3e3fedbf3861ca2627a0518fb2b24e5a7a014178719e9b345f7b249ce3a413280c8deb674f59a25be92a8ab6400c7c52b0728ae34e22b2ec200c1cbaba2ccd8af29249d17af60c36007a722fc80258a7bebab1cdaad7462a8b7588c2f7e27c6d07afcf60117fed11bd6859e75e3b4fcee3981881e95dd116827dd4b369af069d3c8f2676f8a: -264504604e70d72dc4474dbb34913e9c0f806dfe18c7879a41762a9e4390ec61eb2b518ce7dc71c91f3665581651fd03af84c46bf1fed2433222353bc7ec511d:eb2b518ce7dc71c91f3665581651fd03af84c46bf1fed2433222353bc7ec511d:901d70e67ed242f2ec1dda813d4c052cfb31fd00cfe5446bf3b93fdb950f952d94ef9c99d1c264a6b13c3554a264beb97ed20e6b5d66ad84db5d8f1de35c496f947a23270954051f8e4dbe0d3ef9ab3003dd47b859356cecb81c50affa68c15dadb5f864d5e1bb4d3bada6f3aba1c83c438d79a94bfb50b43879e9cef08a2bfb22fad943dbf7683779746e31c486f01fd644905048b112ee258042153f46d1c7772a0624bcd6941e9062cfda75dc8712533f4057335c298038cbca29ebdb560a295a88339692808eb3481fd9735ea414f620c143b2133f57bb64e44778a8ca70918202d157426102e1dfc0a8f7b1ae487b74f02792633154dfe74caa1b7088fda22fa8b9bc354c585f1567706e2955493870f54169e0d7691159df43897961d24a852ea970c514948f3b48f71ee586e72ec78db820f253e08db84f6f312c4333bd0b732fe75883507783e9a1fd4fbab8e5870f9bf7ad58aa:eea439a00f7e459b402b835150a779eed171ab971bd1b58dcc7f9386dadd583de8dc69e267121dde41f0f9493d450b16219cdf3c22f09482ce402fe17ca49e08901d70e67ed242f2ec1dda813d4c052cfb31fd00cfe5446bf3b93fdb950f952d94ef9c99d1c264a6b13c3554a264beb97ed20e6b5d66ad84db5d8f1de35c496f947a23270954051f8e4dbe0d3ef9ab3003dd47b859356cecb81c50affa68c15dadb5f864d5e1bb4d3bada6f3aba1c83c438d79a94bfb50b43879e9cef08a2bfb22fad943dbf7683779746e31c486f01fd644905048b112ee258042153f46d1c7772a0624bcd6941e9062cfda75dc8712533f4057335c298038cbca29ebdb560a295a88339692808eb3481fd9735ea414f620c143b2133f57bb64e44778a8ca70918202d157426102e1dfc0a8f7b1ae487b74f02792633154dfe74caa1b7088fda22fa8b9bc354c585f1567706e2955493870f54169e0d7691159df43897961d24a852ea970c514948f3b48f71ee586e72ec78db820f253e08db84f6f312c4333bd0b732fe75883507783e9a1fd4fbab8e5870f9bf7ad58aa: -2ca7447a3668b748b1fd3d52d2080d30e34d397bb2846caf8f659ac168788ca5ab331cd40a31d0173c0c8c1c17002532807bf89e3edb6d34c2dd8294632b9fbc:ab331cd40a31d0173c0c8c1c17002532807bf89e3edb6d34c2dd8294632b9fbc:a82bcd9424bffda0f2f5e9eae17835dbe468f61b785aab82934737a91c5f602cb7c617cdffe87cad726a4972e15a7b8ee147f062d2a5a4d89706b571fa8aa2b95981c78abeaaae86203fa2c0e07297406ea8c27111a86dbe1d5a7c3b7ae930904d9890f6d4abebd1412a73ad5feea64acf065d3e63b5cbe20cf20bbd2d8b94f9053ed5f66633482530124446605918de66455e8cf4b101a127233c4e27d5d55bf95bd3195d0340d43531fc75faf8dded5275bf89750de838fd10c31745be4ca41fa871cb0f9b016706a1a7e3c44bb90ac7a8ad51e272389292fd6c98ad7a069e76e3f5f3e0cc770b9e9b35a765d0d93712d7cdabd17e5d01dd8183af4ad9365db0a0fa41381fce60a081df1c5ab0f8c18f95a7a8b582dfff7f149ea579df0623b33b7508f0c663f01e3a2dcd9dfbee51cc615220fdaffdab51bdae42cb9f7fa9e3b7c69cc8ada5ccd642529ba514fdc54fcf2720b8f5d08b95:f93ada15ae9cd2b54f26f86f0c28392aed5eb6b6b44d01a4e33a54e7da37c38e8d53366f73fd85be642e4ec81236d163f0d025e76c8bbdd65d43df49f09c1f01a82bcd9424bffda0f2f5e9eae17835dbe468f61b785aab82934737a91c5f602cb7c617cdffe87cad726a4972e15a7b8ee147f062d2a5a4d89706b571fa8aa2b95981c78abeaaae86203fa2c0e07297406ea8c27111a86dbe1d5a7c3b7ae930904d9890f6d4abebd1412a73ad5feea64acf065d3e63b5cbe20cf20bbd2d8b94f9053ed5f66633482530124446605918de66455e8cf4b101a127233c4e27d5d55bf95bd3195d0340d43531fc75faf8dded5275bf89750de838fd10c31745be4ca41fa871cb0f9b016706a1a7e3c44bb90ac7a8ad51e272389292fd6c98ad7a069e76e3f5f3e0cc770b9e9b35a765d0d93712d7cdabd17e5d01dd8183af4ad9365db0a0fa41381fce60a081df1c5ab0f8c18f95a7a8b582dfff7f149ea579df0623b33b7508f0c663f01e3a2dcd9dfbee51cc615220fdaffdab51bdae42cb9f7fa9e3b7c69cc8ada5ccd642529ba514fdc54fcf2720b8f5d08b95: -494ea9bcce26885b7d17d1fc114448f239f0ce46e5f247b4c999fa86296924726901e5efae57536ba5fdd96b59657359065f25d391a1aa8cdc0d38bb5d53c139:6901e5efae57536ba5fdd96b59657359065f25d391a1aa8cdc0d38bb5d53c139:3badbfa5f5a8aa2cce0a60e686cdce654d24452f98fd54872e7395b39464380a0e185557ea134d095730864f4254d3dd946970c10c804fcc0899dfa024205be0f80b1c75449523324fe6a0751e47b4ff4822b8c33e9eaf1d1d96e0de3d4acd89696b7fcc03d49f92f82b9725700b350db1a87615369545561b8599f5ea920a310a8bafc0e8d7468cbf6f3820e943594afdd5166e4e3309dddd7694ef67e694f34fc62724ff96ac3364176f34e8a02b4cf569db5b8f77d58512aedabf0bcd1c2df12db3a9473f948c5c3243309aae46c49efd088b60f31a8a72ad7e5a35acc5d89fa66807eb5d3ba9cdf08d4753cb85089ee36f5c96b432b6928352afad58012225d6157f9e3611426df921b6d1d8374628a63031e9ffb90e42ffbba021f174f68503155430152c9155dc98ffa26c4fab065e1f8e4622c2f28a8cb043110b617441140f8e20adc16f799d1d5096b1f50532be5042d21b81ea46c7:548a093a680361b7dc56f14503b55eeec3b3f4fd4ca99d6aedce0830f7f4ae2f7328539b34c48fc9760922333dae9c7c017e7db73b8faa6c06be05e347992b063badbfa5f5a8aa2cce0a60e686cdce654d24452f98fd54872e7395b39464380a0e185557ea134d095730864f4254d3dd946970c10c804fcc0899dfa024205be0f80b1c75449523324fe6a0751e47b4ff4822b8c33e9eaf1d1d96e0de3d4acd89696b7fcc03d49f92f82b9725700b350db1a87615369545561b8599f5ea920a310a8bafc0e8d7468cbf6f3820e943594afdd5166e4e3309dddd7694ef67e694f34fc62724ff96ac3364176f34e8a02b4cf569db5b8f77d58512aedabf0bcd1c2df12db3a9473f948c5c3243309aae46c49efd088b60f31a8a72ad7e5a35acc5d89fa66807eb5d3ba9cdf08d4753cb85089ee36f5c96b432b6928352afad58012225d6157f9e3611426df921b6d1d8374628a63031e9ffb90e42ffbba021f174f68503155430152c9155dc98ffa26c4fab065e1f8e4622c2f28a8cb043110b617441140f8e20adc16f799d1d5096b1f50532be5042d21b81ea46c7: -00d735ebaee75dd579a40dfd82508274d01a1572df99b811d5b01190d82192e4ba02517c0fdd3e2614b3f7bf99ed9b492b80edf0495d230f881730ea45bc17c4:ba02517c0fdd3e2614b3f7bf99ed9b492b80edf0495d230f881730ea45bc17c4:59c0b69af95d074c88fdc8f063bfdc31b5f4a9bc9cecdffa8128e01e7c1937dde5eb0570b51b7b5d0a67a3555b4cdce2bca7a31a4fe8e1d03ab32b4035e6dadbf1532059ee01d3d9a7633a0e706a1154cab22a07cd74c06a3cb601244cf3cf35a35c3100ba47f31372a2da65dcff0d7a80a1055d8aa99212e899aad7f02e949e6fee4d3c9cefa85069eaff1f6ad06fc300c871ab82b2bedb934d20875c2a263242cdb7f9be192a8710b24c7ea98d43daec8baa5553c678a38f0e0adf7d3ff2dcc799a1dbad6eab1c3d9458a9db922f02e75cfab9d65c7336dae71895d5bb15cac203f2b38b9996c410f8655ad22d3c091c20b7f926d45e780128f19747462abc5c58932fbb9e0bc62d53868802f1b083f183b8a1f9434986d5cf97c04e2f3e145730cba98779c7fed0cab1c05d5e4653c6c3f6736260bc78ee4372862ffe9e90371d762c7432781f35ced884a4baca05653ef25f25a6f3d5628308:dcdc54611937d2bd06cacd9818b3be15ce7425427a75f50d197a337a3b8ba6714ef48866f243bd5ac7415e914517a2c1c5a953f432b99db0e620d64f74eb850559c0b69af95d074c88fdc8f063bfdc31b5f4a9bc9cecdffa8128e01e7c1937dde5eb0570b51b7b5d0a67a3555b4cdce2bca7a31a4fe8e1d03ab32b4035e6dadbf1532059ee01d3d9a7633a0e706a1154cab22a07cd74c06a3cb601244cf3cf35a35c3100ba47f31372a2da65dcff0d7a80a1055d8aa99212e899aad7f02e949e6fee4d3c9cefa85069eaff1f6ad06fc300c871ab82b2bedb934d20875c2a263242cdb7f9be192a8710b24c7ea98d43daec8baa5553c678a38f0e0adf7d3ff2dcc799a1dbad6eab1c3d9458a9db922f02e75cfab9d65c7336dae71895d5bb15cac203f2b38b9996c410f8655ad22d3c091c20b7f926d45e780128f19747462abc5c58932fbb9e0bc62d53868802f1b083f183b8a1f9434986d5cf97c04e2f3e145730cba98779c7fed0cab1c05d5e4653c6c3f6736260bc78ee4372862ffe9e90371d762c7432781f35ced884a4baca05653ef25f25a6f3d5628308: -8c34b905440b61911d1d8137c53d46a1a76d4609af973e18eb4c5709295627bbb69a8b2fdf5c20e734c2ffb294bc8ae1011d664f11afe7fbc471925cf72fa99d:b69a8b2fdf5c20e734c2ffb294bc8ae1011d664f11afe7fbc471925cf72fa99d:30b57a389b48a0beb1a48432bff6b314bded79c4a1763a5acb57cea1bfb4c6d016cf090f5bd05bbd114e33ae7c17782dfa264f46c45f8c599c603016fe9ff05b6b5a99e92fe713a4cd5c41b292ed2bb2e9cf33a440542e821ec82cbf665c3f02e3dc337d7fdb58e31b27cb2954541468814698510df18c85c81fad12db11ec6b966f4930da5646b991db97445097da30dab61cda53a41083cb96add19de6c5eec323bca9d3530e38c00b35af7360077601be6ac97f3030f930a27b90fe8b6911bae389065adc15e1882300e2a003274d23182d5efd5ba4b9130c07bd5c65fecb8b5cb7eb38836b318befdfd77de4d6ca0181f77ae5740891683225f549dd8426145c97c5818c319f7ab2d868e1a41ceab64c085116069897bf2ca3667652406155ed0646431b6de1ccc03b4279ae4d326679265dce82048e7298e1f87fcec0768ac0f5d8ff84f7210be54d411af8edea7217f4e59413121e148c60da:3e0b72073dc9375eedcca6c4fc1cd315938a050c92716bd2284f4629a962beec0b7d7cf16ab923d58f5b90d3901a8e5c75c8f17dab9998e007d8c49511973d0e30b57a389b48a0beb1a48432bff6b314bded79c4a1763a5acb57cea1bfb4c6d016cf090f5bd05bbd114e33ae7c17782dfa264f46c45f8c599c603016fe9ff05b6b5a99e92fe713a4cd5c41b292ed2bb2e9cf33a440542e821ec82cbf665c3f02e3dc337d7fdb58e31b27cb2954541468814698510df18c85c81fad12db11ec6b966f4930da5646b991db97445097da30dab61cda53a41083cb96add19de6c5eec323bca9d3530e38c00b35af7360077601be6ac97f3030f930a27b90fe8b6911bae389065adc15e1882300e2a003274d23182d5efd5ba4b9130c07bd5c65fecb8b5cb7eb38836b318befdfd77de4d6ca0181f77ae5740891683225f549dd8426145c97c5818c319f7ab2d868e1a41ceab64c085116069897bf2ca3667652406155ed0646431b6de1ccc03b4279ae4d326679265dce82048e7298e1f87fcec0768ac0f5d8ff84f7210be54d411af8edea7217f4e59413121e148c60da: -77a83e18c9f000eeff7deeac959ecba2206c0aa39d2f0e2aed5729482a7a022962b1b316135596bfbca6037ed847c61fb7f09fa36ce90abb7789b86f768b59dd:62b1b316135596bfbca6037ed847c61fb7f09fa36ce90abb7789b86f768b59dd:f3d5fa2acaefd858f1df26e03059cdcbc2468ad74afc993d0db9c4cde4113f8d55c7da71d38ba06520531c61fddb5f33d5f0353be2376e580711be45c0a30b1fa01b55e228c6fa35e3f95b67909fc7df3fd464d93d661a926f9d11f7550c17fbcc3496526e8f10e0c8916677b2be5b319b688f21e81aaa9482e5c93e64ce8c437b9c1e14fefed70a3fee568811dc31cadab3d5b220254465336dc4d97a3bd096b5e065e0cfbe82849e2c1905aca486533f0da7a61f1e9a55b8e2a83262deeb59f2b13d3a8aef5700845b83b25ae2183c0ddac0ce42f8d25674cb0d0d220a6de7c1858bb07d59a3372344d944602aa451d2b937db0fe6feca0beba81721fc361ea7509e2b6d397e1c191b56f54ab436d0d27ab4c061bd661ad1a4452387e8735754d07fa7ef4d4548b172582425b299046e6301b5ba6b914418f149cf722e10bde2e0d41700f12c8429fc897b7819da92292240cd45565458c9a7b29c12:1eaad8420ac12c99ac1ff4476678e3cbbe94da6a797f174664d5ee0f641433fb1e7cb2f5613e10805df8654cd8e0d45d96230932bc7f20b04eae836435134309f3d5fa2acaefd858f1df26e03059cdcbc2468ad74afc993d0db9c4cde4113f8d55c7da71d38ba06520531c61fddb5f33d5f0353be2376e580711be45c0a30b1fa01b55e228c6fa35e3f95b67909fc7df3fd464d93d661a926f9d11f7550c17fbcc3496526e8f10e0c8916677b2be5b319b688f21e81aaa9482e5c93e64ce8c437b9c1e14fefed70a3fee568811dc31cadab3d5b220254465336dc4d97a3bd096b5e065e0cfbe82849e2c1905aca486533f0da7a61f1e9a55b8e2a83262deeb59f2b13d3a8aef5700845b83b25ae2183c0ddac0ce42f8d25674cb0d0d220a6de7c1858bb07d59a3372344d944602aa451d2b937db0fe6feca0beba81721fc361ea7509e2b6d397e1c191b56f54ab436d0d27ab4c061bd661ad1a4452387e8735754d07fa7ef4d4548b172582425b299046e6301b5ba6b914418f149cf722e10bde2e0d41700f12c8429fc897b7819da92292240cd45565458c9a7b29c12: -73b03373ef1fd849005ecd6270dd9906f19f4439e40376cdbc520902bc976812663719e08ba3ba1666f6069a3f54991866b18cc6be41991b02eb3026ff9e155f:663719e08ba3ba1666f6069a3f54991866b18cc6be41991b02eb3026ff9e155f:d5c2deaba795c30aba321bc7de6996f0d90e4d05c747fb4dae8f3451895def6e16e72f38eace756f36635f8fb0b72a3a0c1f54663817a94d4fd346f835ab0e657f001a6f2cecb86d0825bd02639254f7f7f38ca99dbb86c64a633f73baf933aae3563281f4005e2d0e7cec9fbde8e588a957e211068be65b3d3d35bf4e8d5bb3478333df9ced9b2abaf48697994a145e9321499fc5ee560f4fbb6849e1ae8eb3d1de0083a21a03f6a6b28176f0130d3895e50e75e3d7d0947a7bc2c5b9ff69895d27791442ba8d0f2180712b567f712ea912f3b0d92c19342e0106ff1d87b46ad33af300b90855ba9769d366e79425d98e4de19905a04577707cbe625b84691781cd26bf62260b4a8bd605f77af6f970e1b3a112e8918344bd0d8d2e41dfd2ce9895b0246e50887aa3a577ff73be4b6ae60feb0ca36f6a5f8171ed209e5c566529c0940d9b4bd744ccee56e54a9a0c6e4da520dd315c2872b02db563703e:a40abe98fc69da8a1ff9ff5c2cca93632e975980ee8b82c3c376022d6524ab736d01b072f2b681b5f1cd3ea067012ed6d074e949c42327a366caa9e4750a3c08d5c2deaba795c30aba321bc7de6996f0d90e4d05c747fb4dae8f3451895def6e16e72f38eace756f36635f8fb0b72a3a0c1f54663817a94d4fd346f835ab0e657f001a6f2cecb86d0825bd02639254f7f7f38ca99dbb86c64a633f73baf933aae3563281f4005e2d0e7cec9fbde8e588a957e211068be65b3d3d35bf4e8d5bb3478333df9ced9b2abaf48697994a145e9321499fc5ee560f4fbb6849e1ae8eb3d1de0083a21a03f6a6b28176f0130d3895e50e75e3d7d0947a7bc2c5b9ff69895d27791442ba8d0f2180712b567f712ea912f3b0d92c19342e0106ff1d87b46ad33af300b90855ba9769d366e79425d98e4de19905a04577707cbe625b84691781cd26bf62260b4a8bd605f77af6f970e1b3a112e8918344bd0d8d2e41dfd2ce9895b0246e50887aa3a577ff73be4b6ae60feb0ca36f6a5f8171ed209e5c566529c0940d9b4bd744ccee56e54a9a0c6e4da520dd315c2872b02db563703e: -eab179e41ed5c889ffe6aabdc054faf1307c395e46e313e17a14fe01023ffa3086f34746d3f7a01ddbe322f1aca56d22856d38733a3a6900bb08e776450ec803:86f34746d3f7a01ddbe322f1aca56d22856d38733a3a6900bb08e776450ec803:971095cebe5031530224387c5c31966e389b8566390054cf45264b44e18964b7be52c33c4ffb259af16283438fa15dd66bc7791b7533ef10cb0beab524a6437626f4cc74512851adcc2fb129055a482c61107383fb7c5241831d5551634eef0dc0b8f9053a00971aa8fa1ae0898e4b481b6707e97c0f942040b339d92fc17bbade74675af243d8b2dafb15b1db55d12415b85f3037291930ab61600ba3431f8eb425be4491614728af101e81c091f348bc5ffd1bde6ae6cad5c15b3aa7358078cc4effb54a86e7f0e0c55e4cfe0a54605ed443fdf2aaba016585da617e77341d52889d75dd540d39fe8b7993ed705cfddea0cb0d5a731d6bfcdb816afaff47e963eedebdf241af5593353d6d401a34f029a8cdeb1904cc2caa4f9635cc2ba6b7b1a29da625ffc383be2f5a8f1fa4f39b2d4b4f4c2d8838ce258a04d4a120493fdf07f68c0ffd1c16b768a35c55fea2cac696b5c20efc10865cde8a64627dcd:143cb28027c2f82e375e5f340e7fe6e60ce7bd51000b49c74168af85e26ed2ed630ed2672090164cc54b052da694ebdd21a21b3053f4dcfd7895ea5f6c8aa80d971095cebe5031530224387c5c31966e389b8566390054cf45264b44e18964b7be52c33c4ffb259af16283438fa15dd66bc7791b7533ef10cb0beab524a6437626f4cc74512851adcc2fb129055a482c61107383fb7c5241831d5551634eef0dc0b8f9053a00971aa8fa1ae0898e4b481b6707e97c0f942040b339d92fc17bbade74675af243d8b2dafb15b1db55d12415b85f3037291930ab61600ba3431f8eb425be4491614728af101e81c091f348bc5ffd1bde6ae6cad5c15b3aa7358078cc4effb54a86e7f0e0c55e4cfe0a54605ed443fdf2aaba016585da617e77341d52889d75dd540d39fe8b7993ed705cfddea0cb0d5a731d6bfcdb816afaff47e963eedebdf241af5593353d6d401a34f029a8cdeb1904cc2caa4f9635cc2ba6b7b1a29da625ffc383be2f5a8f1fa4f39b2d4b4f4c2d8838ce258a04d4a120493fdf07f68c0ffd1c16b768a35c55fea2cac696b5c20efc10865cde8a64627dcd: -fbf146ebd51075570ec51ac410ae9f391db75b610ada6362b4dbd949656cfb66be7c2f5b21d746c8ea3245ce6f268e9da74e00fa85c9c475260c68fa1af6361f:be7c2f5b21d746c8ea3245ce6f268e9da74e00fa85c9c475260c68fa1af6361f:cd7ad4f17fcff73acc402dc102d09079b29aaf2a0f4b27cf6beeb1e2b23d19ab47deb3ae1becd68861ea279c46691738f4fff47c43047c4f8b56b6bbcc3fde0723d44120dcd307a6310dc4f366b8f3cd52db19b8266a487f7872391c45fe0d3248a7abf2c20022d3769547f683067dcc363cd22fd7cda3cadc15804056f0e2aa2b795008c598be7a961805e6df291ba3041c47ff5640275f46e6ae82092d21abcbcfba11e730216008822de3ce462400596da79f7ae5d1df8389112ad98868fa94fb0546bfe6a67aa8d28c4d32072d2eadd6256255f18c2382e662dfa922a680e06a43622c4871d27d1807f7b2703070c83db8dd929c06038b2183cb8e2b9ec4c778d7ecf9e9ffac77fa7737b055feac2e7982aeeec0b72f1bbca2424e1a844bbac79cb2e7400f81dc449d0560b521a7c16bb4167e6696586058a9b8ed2e5116690b77f2a17e5c0b16a83dcbd2e24552293e258b32ba7f844944379342698627:6768006fe0f201b217dd10eb05d4b82adcfeb2ecfc8373c3308f4150394811eb60491881a2e53d1289d96478e18a64c34b2a19832cdccfd96a2e4a0c469fdc0bcd7ad4f17fcff73acc402dc102d09079b29aaf2a0f4b27cf6beeb1e2b23d19ab47deb3ae1becd68861ea279c46691738f4fff47c43047c4f8b56b6bbcc3fde0723d44120dcd307a6310dc4f366b8f3cd52db19b8266a487f7872391c45fe0d3248a7abf2c20022d3769547f683067dcc363cd22fd7cda3cadc15804056f0e2aa2b795008c598be7a961805e6df291ba3041c47ff5640275f46e6ae82092d21abcbcfba11e730216008822de3ce462400596da79f7ae5d1df8389112ad98868fa94fb0546bfe6a67aa8d28c4d32072d2eadd6256255f18c2382e662dfa922a680e06a43622c4871d27d1807f7b2703070c83db8dd929c06038b2183cb8e2b9ec4c778d7ecf9e9ffac77fa7737b055feac2e7982aeeec0b72f1bbca2424e1a844bbac79cb2e7400f81dc449d0560b521a7c16bb4167e6696586058a9b8ed2e5116690b77f2a17e5c0b16a83dcbd2e24552293e258b32ba7f844944379342698627: -dff0eb6b426dea2fd33c1d3fc24df9b31b486facb7edb8502954a3e8da99d9fdc245085ece69fb9aa560d0c27fdb634f7a840d41d8463660fbe82483b0f3cc3a:c245085ece69fb9aa560d0c27fdb634f7a840d41d8463660fbe82483b0f3cc3a:e7c9e313d86160f4c74aa0ae07369ee22b27f81b3f69097affae28dae48483fb52a5c062306b59610f5cdbff6332b1960cd6f2b8f7b41578c20f0bc9637a0fdfc739d61f699a573f1c1a0b49294506cf4487965e5bb07bbf81803cb3d5cb3829c66c4bee7fc800ede216150934d277dea50edb097b992f11bb669fdf140bf6ae9fec46c3ea32f888fde9d154ea84f01c51265a7d3fef6eefc1ccdbffd1e2c897f05546a3b1ca11d9517cd667c660ec3960f7a8e5e80202a78d3a388b92f5c1dee14ae6acf8e17c841c9557c35a2eeced6e6af6372148e483ccd06c8fe344924e1019fb91cbf7941b9a176a073415867210670410c5dbd0ac4a50e6c0a509ddfdc555f60d696d41c77db8e6c84d5181f872755e64a721b061fcd68c463db4d32c9e01ea501267de22879d7fc12c8ca0379edb45abaa6e64dda2af6d40ccf24fbebad7b5a8d3e52007945ecd3ddc1e3efeb522581ac80e98c863ba0c590a3ed95cd1:6b48b10f545ddb7a89cd5829f4e5b20146cf6bc96e550d06f65de8bdae7ccdded26cd630f86c9266bccf88e924033e04f83a54f8290d7f734cf8673cca8f9703e7c9e313d86160f4c74aa0ae07369ee22b27f81b3f69097affae28dae48483fb52a5c062306b59610f5cdbff6332b1960cd6f2b8f7b41578c20f0bc9637a0fdfc739d61f699a573f1c1a0b49294506cf4487965e5bb07bbf81803cb3d5cb3829c66c4bee7fc800ede216150934d277dea50edb097b992f11bb669fdf140bf6ae9fec46c3ea32f888fde9d154ea84f01c51265a7d3fef6eefc1ccdbffd1e2c897f05546a3b1ca11d9517cd667c660ec3960f7a8e5e80202a78d3a388b92f5c1dee14ae6acf8e17c841c9557c35a2eeced6e6af6372148e483ccd06c8fe344924e1019fb91cbf7941b9a176a073415867210670410c5dbd0ac4a50e6c0a509ddfdc555f60d696d41c77db8e6c84d5181f872755e64a721b061fcd68c463db4d32c9e01ea501267de22879d7fc12c8ca0379edb45abaa6e64dda2af6d40ccf24fbebad7b5a8d3e52007945ecd3ddc1e3efeb522581ac80e98c863ba0c590a3ed95cd1: -9f32958c7679b90fd5036056a75ec2eb2f56ec1effc7c012461dc89a3a1674201d7269dcb6d1f584e662d4ce251de0aba290ef78b97d448afb1e5333f1976d26:1d7269dcb6d1f584e662d4ce251de0aba290ef78b97d448afb1e5333f1976d26:a56ba86c71360504087e745c41627092ad6b49a71e9daa5640e1044bf04d4f071ad728779e95d1e2460584e6f0773545da82d4814c9189a120f12f3e3819813e5b240d0f26436f70ee353b4d20cea54a1460b5b8f1008d6f95f3aa2d8f1e908fced50d624e3a096938b9353854b96da463a2798a5a312ec790842c10c446e3350c764bf5c972593b9987bf23256daa8894d47f22e85b97607e66fc08a12c789c4746080368d321bb9015a1155b65523ad8e99bb989b44eac756b0734acd7c6357c70b59743246d1652d91b0f9896965141345b9945cf34980452f3502974edb76b9c785fb0f4395266b055f3b5db8aab68e9d7102a1cd9ee3d142504f0e88b282e603a738e051d98de05d1fcc65b5f7e99c4111cc0aec489abd0ecad311bfc13e7d1653b9c31e81c998037f959d5cd980835aa0e0b09bcbed634391151da02bc01a36c9a5800afb984163a7bb815edbc0226eda0595c724ca9b3f8a71178f0d20a5a:9881a5763bdb259a3fefbba3d957162d6c70b804fa94ab613406a6ec42505b8789465ca1a9a33e1895988842270c55e5bdd5483f6b17b31781b593507a6c1808a56ba86c71360504087e745c41627092ad6b49a71e9daa5640e1044bf04d4f071ad728779e95d1e2460584e6f0773545da82d4814c9189a120f12f3e3819813e5b240d0f26436f70ee353b4d20cea54a1460b5b8f1008d6f95f3aa2d8f1e908fced50d624e3a096938b9353854b96da463a2798a5a312ec790842c10c446e3350c764bf5c972593b9987bf23256daa8894d47f22e85b97607e66fc08a12c789c4746080368d321bb9015a1155b65523ad8e99bb989b44eac756b0734acd7c6357c70b59743246d1652d91b0f9896965141345b9945cf34980452f3502974edb76b9c785fb0f4395266b055f3b5db8aab68e9d7102a1cd9ee3d142504f0e88b282e603a738e051d98de05d1fcc65b5f7e99c4111cc0aec489abd0ecad311bfc13e7d1653b9c31e81c998037f959d5cd980835aa0e0b09bcbed634391151da02bc01a36c9a5800afb984163a7bb815edbc0226eda0595c724ca9b3f8a71178f0d20a5a: -f86d6f766f88b00717b7d6327eb26cf3ceeba5385184426f9cfd8295e2421ff2cb1d250504754183704dbe21c323d66f9f9011758f6d8dab6f597b199662145b:cb1d250504754183704dbe21c323d66f9f9011758f6d8dab6f597b199662145b:da8423a6b7a18f20aa1f90ed2331b17b24067c40175bc25d8109e21d87ac00528eb3b2f66a2b52dc7ef2f8cecb75c76099cfa23db8da897043ba1cce31e2dfea46075f5e073203eaeb3d62c84c107b6dab33a14eaf149aa61850c15f5a58d88a15aba9196f9e495e8dbecbcf7e8444f5dd72a08a099d7f6209990b562974ea829ef11d29a920e3a799d0d92cb50d50f817631ab09de97c31e9a05f4d78d649fcd93a83752078ab3bb0e16c564d4fb07ca923c0374ba5bf1eea7e73668e135031feafcbb47cbc2ae30ec16a39b9c337e0a62eecdd80c0b7a04924ac3972da4fa9299c14b5a53d37b08bf02268b3bac9ea9355090eeb04ad87bee0593ba4e4443dda38a97afbf2db9952df63f178f3b4c52bcc132be8d9e26881213abdeb7e1c44c4061548909f0520f0dd7520fc408ea28c2cebc0f53063a2d30570e05350e52b390dd9b67662984847be9ad9b4cd50b069ffd29dd9c62ef14701f8d012a4a70c8431cc:ec61c0b292203a8f1d87235ede92b74723c8d23408423773ae50b1e9bc4464e03e446da9dce4c39f6dd159bea26c009ed00120bc36d4a247dc0d24bcefcc110cda8423a6b7a18f20aa1f90ed2331b17b24067c40175bc25d8109e21d87ac00528eb3b2f66a2b52dc7ef2f8cecb75c76099cfa23db8da897043ba1cce31e2dfea46075f5e073203eaeb3d62c84c107b6dab33a14eaf149aa61850c15f5a58d88a15aba9196f9e495e8dbecbcf7e8444f5dd72a08a099d7f6209990b562974ea829ef11d29a920e3a799d0d92cb50d50f817631ab09de97c31e9a05f4d78d649fcd93a83752078ab3bb0e16c564d4fb07ca923c0374ba5bf1eea7e73668e135031feafcbb47cbc2ae30ec16a39b9c337e0a62eecdd80c0b7a04924ac3972da4fa9299c14b5a53d37b08bf02268b3bac9ea9355090eeb04ad87bee0593ba4e4443dda38a97afbf2db9952df63f178f3b4c52bcc132be8d9e26881213abdeb7e1c44c4061548909f0520f0dd7520fc408ea28c2cebc0f53063a2d30570e05350e52b390dd9b67662984847be9ad9b4cd50b069ffd29dd9c62ef14701f8d012a4a70c8431cc: -a5b34cefab9479df8389d7e6f6c146aa8affb0bec837f78af64624a145cc344e7b0f4f24d9972bc6fe83826c52716ad1e0d7d19f123858cb3e99fa636ac9631a:7b0f4f24d9972bc6fe83826c52716ad1e0d7d19f123858cb3e99fa636ac9631a:e21e98af6c2bac70557eb0e864da2c2b4d6c0a39a059d3477251f6178a39676f4749e7fbea623f148a43a8b0fe0610506fa658abd2f5fa39198f2636b724db22d1aebc2ab07b2b6dbffdee8cece81e1af1493ec1964e16bf86ab258ca0feb77e3c8717e44038abe152c14be15660bf93b2d48d92c4ed7074d2494210621bcf204fba88c654d5ffe01e1a53d08f70bb237089dc807216ff6a85dbec3102237d42590778acf6c1dc566d5a2bb9a63bc21c329c272e5965baeeb0fe891de3cc8cbfa8e541a8881df68942e7ff8dc656bd08575f6aaf924a176d663b1a1f43574d11768c701b269561e55438dbebfd443d2115cb933d1cde4a915b54c325c27f499ef02bd012ff1f9a36390922887600fe712bcdc23eb5974a305372ad52951f83f0e58cc49e289841621917f1fcb0235147240dae4cf3b99b6ac6d8de94efe7c4436714508bcd0114c56068ff1b7c16d51bd906437874d6549ab5d8087896872ec8a09d7412:2fbd899d72b6d39e4f45b8b62cbbd5f3c0acb1ad8540913fa585877e91ccfef7bee50a4b0f9fedf5cc1e0d1953ad399c8389a93391e1b7c929af6d6f3b796c08e21e98af6c2bac70557eb0e864da2c2b4d6c0a39a059d3477251f6178a39676f4749e7fbea623f148a43a8b0fe0610506fa658abd2f5fa39198f2636b724db22d1aebc2ab07b2b6dbffdee8cece81e1af1493ec1964e16bf86ab258ca0feb77e3c8717e44038abe152c14be15660bf93b2d48d92c4ed7074d2494210621bcf204fba88c654d5ffe01e1a53d08f70bb237089dc807216ff6a85dbec3102237d42590778acf6c1dc566d5a2bb9a63bc21c329c272e5965baeeb0fe891de3cc8cbfa8e541a8881df68942e7ff8dc656bd08575f6aaf924a176d663b1a1f43574d11768c701b269561e55438dbebfd443d2115cb933d1cde4a915b54c325c27f499ef02bd012ff1f9a36390922887600fe712bcdc23eb5974a305372ad52951f83f0e58cc49e289841621917f1fcb0235147240dae4cf3b99b6ac6d8de94efe7c4436714508bcd0114c56068ff1b7c16d51bd906437874d6549ab5d8087896872ec8a09d7412: -ad75c9ce299c4d59393367d77a4c9f8df8dcec765c6dbd25b527fb7669913604b9910548fe6312a119c9993eebcfb9dc90030ffb0e4de2b7ccd23cbeb4fef71b:b9910548fe6312a119c9993eebcfb9dc90030ffb0e4de2b7ccd23cbeb4fef71b:62fc5ab67deb1fee9ab6cca3b88a1df1e589f0fd4a88f4aa7738948761fe84372c5b18e4655220c1d84d52acad32e229a5c756c20fc62fe4b4b4e5fd7077ae4ed5397aa796f2307ceedb6505b39297856f4aeb5e70938e36ee24a0ac7d9868306f6b53910623b7dc89a6672ad738576ed5d88831dd338321c8902bc2061f65e94d452fdfa0dc665cefb92308e52301bd4627006b363d06b775a395914d8c863e95a00d6893f3376134c429f56478145e4456f7a12d65bb2b8965d728cb2ddbb708f7125c237095a92195d92fa727a372f3545ae701f3808fee802c8967a76e8a940e55fb2d810bfb47ada156f0eda1829b159cf05c7f36cf3847d7b21de84c3dc0fe658347f79396a01139a508b60022db1c0e5aeef47e445e66f783e62c96597bdb16f209c08a9132c7573136170ee3ebf24261265a89fb4f10333375e20b33ab7403464f5249461c6853c5fddb9f58af816892910393a7077b799fdc3489720998feea86:6b7ef27bcfbf2b714985033764fccff555e3f5bc44610d6c8c62117cb3831a07f4a8bddb0eaed1d46b0289b15de1aa4dcc17d71be96a09e66ba4dc4627c7870562fc5ab67deb1fee9ab6cca3b88a1df1e589f0fd4a88f4aa7738948761fe84372c5b18e4655220c1d84d52acad32e229a5c756c20fc62fe4b4b4e5fd7077ae4ed5397aa796f2307ceedb6505b39297856f4aeb5e70938e36ee24a0ac7d9868306f6b53910623b7dc89a6672ad738576ed5d88831dd338321c8902bc2061f65e94d452fdfa0dc665cefb92308e52301bd4627006b363d06b775a395914d8c863e95a00d6893f3376134c429f56478145e4456f7a12d65bb2b8965d728cb2ddbb708f7125c237095a92195d92fa727a372f3545ae701f3808fee802c8967a76e8a940e55fb2d810bfb47ada156f0eda1829b159cf05c7f36cf3847d7b21de84c3dc0fe658347f79396a01139a508b60022db1c0e5aeef47e445e66f783e62c96597bdb16f209c08a9132c7573136170ee3ebf24261265a89fb4f10333375e20b33ab7403464f5249461c6853c5fddb9f58af816892910393a7077b799fdc3489720998feea86: -1ced574529b9b416977e92eb39448a8717cac2934a243a5c44fb44b73ccc16da85e167d5f062fee82014f3c8b1beaed8eefb2c22d8649c424b86b21b11eb8bda:85e167d5f062fee82014f3c8b1beaed8eefb2c22d8649c424b86b21b11eb8bda:1b3b953cce6d15303c61ca707609f70e7250f6c0deba56a8ce522b5986689651cdb848b842b2229661b8eeabfb8570749ed6c2b10a8fbf515053b5ea7d7a9228349e4646f9505e198029fec9ce0f38e4e0ca73625842d64caf8ced070a6e29c743586aa3db6d82993ac71fd38b783162d8fe04ffd0fa5cbc381d0e219c91937df6c973912fc02fda5377312468274c4bee6dca7f79c8b544861ed5babcf5c50e1473491be01708ac7c9ff58f1e40f855497ce9d7cc47b9410f2edd00f6496740243b8d03b2f5fa742b9c630867f77ac42f2b62c14e5ebddc7b647a05fff43670745f2851eff4909f5d27d57ae87f61e965ee60fdf97724c59267f2610b7ad5de919856d64d7c212659ce8656149b6a6d29d8f92b312be50b6e2a431d36ae022b00a6fe360e3af65432899c43be0427e36d21cfec81f21aa53b33db5ed2c37da8f96ac3e7dc67a1de37546cf7de1008c7e1adbe0f34fa7eb2434d94e6a13f4cf86a98d497622f:e0303aefe08a77738dcc657afbb9b835ed279613a53c73fdc5ddbfb350e5cff4d6c9bb43dc07c95bf4e23b64c40f8804c7169952e3c8d59a7197241bfed0740f1b3b953cce6d15303c61ca707609f70e7250f6c0deba56a8ce522b5986689651cdb848b842b2229661b8eeabfb8570749ed6c2b10a8fbf515053b5ea7d7a9228349e4646f9505e198029fec9ce0f38e4e0ca73625842d64caf8ced070a6e29c743586aa3db6d82993ac71fd38b783162d8fe04ffd0fa5cbc381d0e219c91937df6c973912fc02fda5377312468274c4bee6dca7f79c8b544861ed5babcf5c50e1473491be01708ac7c9ff58f1e40f855497ce9d7cc47b9410f2edd00f6496740243b8d03b2f5fa742b9c630867f77ac42f2b62c14e5ebddc7b647a05fff43670745f2851eff4909f5d27d57ae87f61e965ee60fdf97724c59267f2610b7ad5de919856d64d7c212659ce8656149b6a6d29d8f92b312be50b6e2a431d36ae022b00a6fe360e3af65432899c43be0427e36d21cfec81f21aa53b33db5ed2c37da8f96ac3e7dc67a1de37546cf7de1008c7e1adbe0f34fa7eb2434d94e6a13f4cf86a98d497622f: -f0790d93e2d3b84f61ef4c807147aba410e415e72b71b0d61d01026fed99da3defdf649fb033cf328e0b287796f8a25e9c6e2e871b33c2c21a4028a8a25a4b28:efdf649fb033cf328e0b287796f8a25e9c6e2e871b33c2c21a4028a8a25a4b28:7973e9f32d74805992eb65da0d637335e50eff0ce68ea2d1f3a02de704492b9cfbe7e7ba96fdb42bb821a513d73fc60402e92c855deaed73ffeaf70952029062c833e14ec1b14f144e2207f6a0e727e5a7e3cbab27d5972970f69518a15b093e740cc0ce11bf5248f0826b8a98bde8bf2c7082c97aff158d08371118c89021cc3974ae8f76d86673c3f824b62c79c4b41f40eaa8943738f03300f68cbe175468eb235a9ff0e6537f8714e97e8f08ca444e41191063b5fabd156e85dcf66606b81dad4a95065584b3e0658c20a706eaf4a0777da4d2e0cd2a0fca60109c2b4403db3f03cd4781c1fbb0272202bcb11687808c50cb98f64b7f3fd3d43333bb5a061b9e377090abb1e0a885cb26b73c163e63ff6451ff2f4ec8249c7e152bd03973a1e964e2b5b235281a938399a112a24529e383a560dc50bb1b622ad74ef35658dcb10ffe022568ac3ffae5b465a8ed7643e8561b352ee9944a35d882c712b187788a0abae5a22f:08773a6a78762cbb1e25fcbb29139941bdf16f4e09a1fa08fc701f32f933edd74c0ae983c12a0a5b020b6bcf44bb719dde8ed0781a8298265640e1608c98b3017973e9f32d74805992eb65da0d637335e50eff0ce68ea2d1f3a02de704492b9cfbe7e7ba96fdb42bb821a513d73fc60402e92c855deaed73ffeaf70952029062c833e14ec1b14f144e2207f6a0e727e5a7e3cbab27d5972970f69518a15b093e740cc0ce11bf5248f0826b8a98bde8bf2c7082c97aff158d08371118c89021cc3974ae8f76d86673c3f824b62c79c4b41f40eaa8943738f03300f68cbe175468eb235a9ff0e6537f8714e97e8f08ca444e41191063b5fabd156e85dcf66606b81dad4a95065584b3e0658c20a706eaf4a0777da4d2e0cd2a0fca60109c2b4403db3f03cd4781c1fbb0272202bcb11687808c50cb98f64b7f3fd3d43333bb5a061b9e377090abb1e0a885cb26b73c163e63ff6451ff2f4ec8249c7e152bd03973a1e964e2b5b235281a938399a112a24529e383a560dc50bb1b622ad74ef35658dcb10ffe022568ac3ffae5b465a8ed7643e8561b352ee9944a35d882c712b187788a0abae5a22f: -4cb9df7ce6fae9d62ba09e8eb70e4c969bdeafcb5ec7d7024326e6603b0621bf018069dd0eb44055a35cd8c77c37ca9fb1ad2417271385e134b2f4e81f52033c:018069dd0eb44055a35cd8c77c37ca9fb1ad2417271385e134b2f4e81f52033c:14627d6ea0e7895460759476dc74c42800ceef994327518151490d9df23067914e44788a12768ccb25471b9c3ba9d14fb436dcba38429b3a0456877763c49175d0e082683e07a9058f3685c6279307b2303d1221b9c29793d8a4877f6df51587384dadf751c5f7bfbd207d519622c37b51ceeee2c20d8269f8cb88d3fe43d6d434d5bbd0e203c1532d97ba552147227496c87f67b50bb76193add0144df1c176657585408362ca2ed04ad62acf1c25e341dfd1498d85b4b1349a8b0b9b02c43523c55853419bfed37d5a2cdf17dfbf1a3bd7759d6ae180f9d27dcd9a8933e29a7c0a30771eea7c2e0fa242925d2336dce585629057d844323964f6d3d11ff0b3f829a3be8c9f0468a6823d8e70ab5a2da21e15fa8b041a29812222e9c30b2bd9a12d1fdee6f87876e8ce81009637a8bb2236129a47ca74289ee4aad429ffe29f47430241ca8cc3848b7200fd6e1470651a9a0a6f72c9033e831df051408a6260f65cbaf6e012b18e:e33c07836c537d6bfbd0f4592d6e35b163499ba78dc7ffcec565d04f9a7db781943e29e6ce76763e9baddf57437fd9c6b03239a6e6850e4502a356c2e12c370514627d6ea0e7895460759476dc74c42800ceef994327518151490d9df23067914e44788a12768ccb25471b9c3ba9d14fb436dcba38429b3a0456877763c49175d0e082683e07a9058f3685c6279307b2303d1221b9c29793d8a4877f6df51587384dadf751c5f7bfbd207d519622c37b51ceeee2c20d8269f8cb88d3fe43d6d434d5bbd0e203c1532d97ba552147227496c87f67b50bb76193add0144df1c176657585408362ca2ed04ad62acf1c25e341dfd1498d85b4b1349a8b0b9b02c43523c55853419bfed37d5a2cdf17dfbf1a3bd7759d6ae180f9d27dcd9a8933e29a7c0a30771eea7c2e0fa242925d2336dce585629057d844323964f6d3d11ff0b3f829a3be8c9f0468a6823d8e70ab5a2da21e15fa8b041a29812222e9c30b2bd9a12d1fdee6f87876e8ce81009637a8bb2236129a47ca74289ee4aad429ffe29f47430241ca8cc3848b7200fd6e1470651a9a0a6f72c9033e831df051408a6260f65cbaf6e012b18e: -a136e009d53e5ef59d0946bc175663a86bc0fcd29eadd95cfc9d266037b1e4fb9c1806ec0454f58314eb8397d64287dee386640d8491aba364607688841715a0:9c1806ec0454f58314eb8397d64287dee386640d8491aba364607688841715a0:a49d1c3d49e13c2eda56868a8824aa9f8d2bf72f21955ebafd07b3bdc8e924de20936cee513d8a64a47173a3bd659eff1accff8244b26aae1a0c27fa891bf4d85e8fb1b76a6cab1e7f74c89ee07bb40d714326f09b3fd40632fad208ea816f9072028c14b5b54ecc1c5b7fc809e7e0786e2f11495e76017eb62aa4563f3d00ee84348d9838cd17649f6929a6d206f60e6fc82e0c3464b27e0e6abd22f4469bdfd4cb54f77e329b80f71bf42129ec13c9dfe192adfaa42ee3ddeeda385816fbad5f411938c63b560f4ecd94534be7d98725cd94c99ce492f0f069ba0ec08f877a7812ef27ae19d7a77be63f66bcf8d6cf3a1a61fc9cfef104c7462a21ca7f03afb5bb1ac8c75124b554e8d044b810d95ff8c9dd09a34484d8c4b6c95f95c3c22823f52ce844293724d5259191f1ba0929e2acdbb8b9a7a8adf0c52e78acdfdf057b0985881afbed4dbebdebbdae0a2b63bd4e90f96afdcbbd78f506309f9bdb650013cb73faed73904e:bc094ba91c115dee15d753361a75f3f03d6af45c92157e95dbe8d32194b6c5ce72b9dc66f73df12dca0b639f3e791d478616a1f8d7359a42c8eae0dda16b1606a49d1c3d49e13c2eda56868a8824aa9f8d2bf72f21955ebafd07b3bdc8e924de20936cee513d8a64a47173a3bd659eff1accff8244b26aae1a0c27fa891bf4d85e8fb1b76a6cab1e7f74c89ee07bb40d714326f09b3fd40632fad208ea816f9072028c14b5b54ecc1c5b7fc809e7e0786e2f11495e76017eb62aa4563f3d00ee84348d9838cd17649f6929a6d206f60e6fc82e0c3464b27e0e6abd22f4469bdfd4cb54f77e329b80f71bf42129ec13c9dfe192adfaa42ee3ddeeda385816fbad5f411938c63b560f4ecd94534be7d98725cd94c99ce492f0f069ba0ec08f877a7812ef27ae19d7a77be63f66bcf8d6cf3a1a61fc9cfef104c7462a21ca7f03afb5bb1ac8c75124b554e8d044b810d95ff8c9dd09a34484d8c4b6c95f95c3c22823f52ce844293724d5259191f1ba0929e2acdbb8b9a7a8adf0c52e78acdfdf057b0985881afbed4dbebdebbdae0a2b63bd4e90f96afdcbbd78f506309f9bdb650013cb73faed73904e: -ff0f1c57dd884fbeea6e2917282b79ba67f8a6851267b9f4636dafda33bd2b5bfef6378ad12a7c252fa6eb742b05064b41530ff019dc680ab544c027ea2836e7:fef6378ad12a7c252fa6eb742b05064b41530ff019dc680ab544c027ea2836e7:522a5e5eff5b5e98fad6878a9d72df6eb318622610a1e1a48183f5590ecef5a6df671b28be91c88cdf7ae2881147fe6c37c28b43f64cf981c455c59e765ce94e1b6491631deaeef6d1da9ebca88643c77f83eae2cfdd2d97f604fe45081d1be5c4ae2d875996b8b6fecd707d3fa219a93ba0488e55247b405e330cfb97d31a1361c9b2084bdb13fb0c058925db8c3c649c9a3e937b533cc6310fa3b16126fb3cc9bb2b35c5c8300015488a30fadca3c8871fa70dfdc7055bf8e631f20c9b2528311e324a7c4edd5462079f3441c9ecf55fa999e731372344fdc0d413e417aaa001a1b2d3d9bc000fec1b02bd7a88a812d9d8a66f9464764c070c93041eefb17ce74eff6d4aff75f0cbf6a789a9ecde74abe33130fca0da853aa7c3313ada3f0ae2f595c6796a93685e729dd18a669d6381825ab3f36a391e7525b2a807a52fa5ec2a030a8cf3b77337ac41fceb580e845eed655a48b547238c2e8137c92f8c27e585caad3106eee3814a:d5008486726cce330a29dd7e4d7474d735798201afd1206feb869a112e5b43523c06976761be3cf9b2716378273c94f93572a7d2b8982634e0755c632b449008522a5e5eff5b5e98fad6878a9d72df6eb318622610a1e1a48183f5590ecef5a6df671b28be91c88cdf7ae2881147fe6c37c28b43f64cf981c455c59e765ce94e1b6491631deaeef6d1da9ebca88643c77f83eae2cfdd2d97f604fe45081d1be5c4ae2d875996b8b6fecd707d3fa219a93ba0488e55247b405e330cfb97d31a1361c9b2084bdb13fb0c058925db8c3c649c9a3e937b533cc6310fa3b16126fb3cc9bb2b35c5c8300015488a30fadca3c8871fa70dfdc7055bf8e631f20c9b2528311e324a7c4edd5462079f3441c9ecf55fa999e731372344fdc0d413e417aaa001a1b2d3d9bc000fec1b02bd7a88a812d9d8a66f9464764c070c93041eefb17ce74eff6d4aff75f0cbf6a789a9ecde74abe33130fca0da853aa7c3313ada3f0ae2f595c6796a93685e729dd18a669d6381825ab3f36a391e7525b2a807a52fa5ec2a030a8cf3b77337ac41fceb580e845eed655a48b547238c2e8137c92f8c27e585caad3106eee3814a: -0bc6af64de5709d3dbc28f7ef6d3fe28b6de529f08f5857ccb910695de454f56fb491fc900237bdc7e9a119f27150cd911935cd3628749ff40ef41f3955bc8ac:fb491fc900237bdc7e9a119f27150cd911935cd3628749ff40ef41f3955bc8ac:ac7886e4f4172a22c95e8eea37437b375d72accedcee6cc6e816763301a2d8ef4d6f31a2c1d635818b7026a395ce0dafd71c5180893af76b7ea056c972d680eca01dcbdbae6b26f1c5f33fc988b824fbbe00cacc316469a3bae07aa7c8885af7f65f42e75cef94dbb9aab4825143c85070e7716b7612f64ef0b0166011d23eb5654aa098b02d8d71e57c8fa17bff2fe97dc8193177eadc09fb192d80aa92afa98720d4614817ff3c39d3acce18906fa3de09618931d0d7a60c4429cbfa20cf165c947929ac293ae6c06e7e8f25f1264291e3e1c98f5d93e6ecc2389bc60dbbf4a621b132c552a99c95d26d8d1af61138b570a0de4b497ebe8051c7273a98e6e7876d0b327503af3cb2cc4091ce1925cb2f2957f4ec56ee90f8a09dd57d6e83067a356a4cfe65b1b7a4465da2ab133b0efb5e7d4dbb811bcbbde712afbf0f7dd3f326222284b8c74eac7ad6257fa8c632b7da2559a6266e91e0ef90dbb0aa968f75376b693fcaa5da342221:dbc7134d1cd6b0813b53352714b6df939498e91cf37c324337d9c088a1b998347d26185b430900412929e4f63e910379fc42e355a4e98f6fee27dafad1957206ac7886e4f4172a22c95e8eea37437b375d72accedcee6cc6e816763301a2d8ef4d6f31a2c1d635818b7026a395ce0dafd71c5180893af76b7ea056c972d680eca01dcbdbae6b26f1c5f33fc988b824fbbe00cacc316469a3bae07aa7c8885af7f65f42e75cef94dbb9aab4825143c85070e7716b7612f64ef0b0166011d23eb5654aa098b02d8d71e57c8fa17bff2fe97dc8193177eadc09fb192d80aa92afa98720d4614817ff3c39d3acce18906fa3de09618931d0d7a60c4429cbfa20cf165c947929ac293ae6c06e7e8f25f1264291e3e1c98f5d93e6ecc2389bc60dbbf4a621b132c552a99c95d26d8d1af61138b570a0de4b497ebe8051c7273a98e6e7876d0b327503af3cb2cc4091ce1925cb2f2957f4ec56ee90f8a09dd57d6e83067a356a4cfe65b1b7a4465da2ab133b0efb5e7d4dbb811bcbbde712afbf0f7dd3f326222284b8c74eac7ad6257fa8c632b7da2559a6266e91e0ef90dbb0aa968f75376b693fcaa5da342221: -2f5e83bd5b412e71ae3e9084cd369efcc79bf6037c4b174dfd6a11fb0f5da218a22a6da29a5ef6240c49d8896e3a0f1a4281a266c77d383ee6f9d25ffacbb872:a22a6da29a5ef6240c49d8896e3a0f1a4281a266c77d383ee6f9d25ffacbb872:b766273f060ef3b2ae3340454a391b426bc2e97264f8674553eb00dd6ecfdd59b611d8d662929fec710d0e462020e12cdbf9c1ec8858e85671acf8b7b14424ce92079d7d801e2ad9acac036bc8d2dfaa72aa839bff30c0aa7e414a882c00b645ff9d31bcf5a54382def4d0142efa4f06e823257ff132ee968cdc6738c53f53b84c8df76e9f78dd5056cf3d4d5a80a8f84e3edec48520f2cb4583e708539355ef7aa86fb5a0e87a94dcf14f30a2cca568f139d9ce59eaf459a5c5916cc8f20b26aaf6c7c029379aedb05a07fe585ccac60307c1f58ca9f859157d06d06baa394aace79d51b8cb38cfa2598141e245624e5ab9b9d68731173348905315bf1a5ad61d1e8adaeb810e4e8a86d7c13537b0be860ab2ed35b73399b8808aa91d750f77943f8a8b7e89fdb50728aa3dbbd8a41a6e00756f438c9b9e9d55872df5a9068add8a972b7e43edad9ced2237ca1367be4b7cdb66a54ea12eef129471158610eaf28f99f7f686557dcdf644ea:9f80922bc8db32d0cc43f9936affebe7b2bc35a5d82277cd187b5d50dc7fc4c4832fffa34e9543806b485c04548e7c75429425e14d55d91fc1052efd8667430bb766273f060ef3b2ae3340454a391b426bc2e97264f8674553eb00dd6ecfdd59b611d8d662929fec710d0e462020e12cdbf9c1ec8858e85671acf8b7b14424ce92079d7d801e2ad9acac036bc8d2dfaa72aa839bff30c0aa7e414a882c00b645ff9d31bcf5a54382def4d0142efa4f06e823257ff132ee968cdc6738c53f53b84c8df76e9f78dd5056cf3d4d5a80a8f84e3edec48520f2cb4583e708539355ef7aa86fb5a0e87a94dcf14f30a2cca568f139d9ce59eaf459a5c5916cc8f20b26aaf6c7c029379aedb05a07fe585ccac60307c1f58ca9f859157d06d06baa394aace79d51b8cb38cfa2598141e245624e5ab9b9d68731173348905315bf1a5ad61d1e8adaeb810e4e8a86d7c13537b0be860ab2ed35b73399b8808aa91d750f77943f8a8b7e89fdb50728aa3dbbd8a41a6e00756f438c9b9e9d55872df5a9068add8a972b7e43edad9ced2237ca1367be4b7cdb66a54ea12eef129471158610eaf28f99f7f686557dcdf644ea: -722a2da50e42c11a61c9afac7be1a2fed2267d650f8f7d8e5bc706b807c1b91dfd0b964562f823721e649c3fedb432a76f91e0aead7c61d35f95ed7726d78589:fd0b964562f823721e649c3fedb432a76f91e0aead7c61d35f95ed7726d78589:173e8bb885e1f9081404acac999041d2ecfcb73f945e0db36e631d7cd1ab999eb717f34bf07874bf3d34e2530eb6085f4a9f88ae1b0f7d80f221456a8e9a8890b91a50192deaaacc0a1a615a87841e2c5a9e057957af6e48e78cc86198e32e7aa24dcf6cffa329bc72606d65b11682c8ba736cce22a05785df1146331e41609cf9ca711cf464958297138b58a9073f3bbf06ad8a85d135de66652104d88b49d27ad41e59bcc44c7fab68f53f0502e293ffcabaaf755927dfdffbfde3b35c080b5de4c8b785f4da64ef357bc0d1466a6a96560c3c4f3e3c0b563a003f5f95f237171bce1a001771a04ede7cdd9b8ca770fd36ef90e9fe0000a8d7685fd153cc7282de95920a8f8f0898d00bf0c6c933fe5bb9653ff146c4e2acd1a2e0c23c1244844dacf8652716302c2032f9c114679ed26b3ee3ab4a7b18bc4e3071f0977db57cd0ac68c0727a09b4f125fb64af2850b26c8a484263334e2da902d744737044e79ab1cf5b2f93a022b63d40cd:c2695a57172aaa31bd0890f231ca8eeec0287a87172669a899ad0891cea4c47579b50420e791cdec8c182c8a0e8dde21b2480b0cfd8111e28e5603347a352d04173e8bb885e1f9081404acac999041d2ecfcb73f945e0db36e631d7cd1ab999eb717f34bf07874bf3d34e2530eb6085f4a9f88ae1b0f7d80f221456a8e9a8890b91a50192deaaacc0a1a615a87841e2c5a9e057957af6e48e78cc86198e32e7aa24dcf6cffa329bc72606d65b11682c8ba736cce22a05785df1146331e41609cf9ca711cf464958297138b58a9073f3bbf06ad8a85d135de66652104d88b49d27ad41e59bcc44c7fab68f53f0502e293ffcabaaf755927dfdffbfde3b35c080b5de4c8b785f4da64ef357bc0d1466a6a96560c3c4f3e3c0b563a003f5f95f237171bce1a001771a04ede7cdd9b8ca770fd36ef90e9fe0000a8d7685fd153cc7282de95920a8f8f0898d00bf0c6c933fe5bb9653ff146c4e2acd1a2e0c23c1244844dacf8652716302c2032f9c114679ed26b3ee3ab4a7b18bc4e3071f0977db57cd0ac68c0727a09b4f125fb64af2850b26c8a484263334e2da902d744737044e79ab1cf5b2f93a022b63d40cd: -5fe9c3960ed5bd374cc94d42357e6a24dc7e3060788f726365defacf13cd12da0ce7b155c8b20ebdaacdc2aa23627e34b1f9ace980650a2530c7607d04814eb4:0ce7b155c8b20ebdaacdc2aa23627e34b1f9ace980650a2530c7607d04814eb4:c9490d83d9c3a9370f06c91af001685a02fe49b5ca667733fff189eee853ec1667a6c1b6c787e9244812d2d532866ab74dfc870d6f14033b6bcd39852a3900f8f08cd95a74cb8cbe02b8b8b51e993a06adfebd7fc9854ae5d29f4df9642871d0c5e470d903cfbcbd5adb3275628f28a80bf8c0f0376687dae673bf7a8547e80d4a9855ae2572fc2b205dc8a198016ddc9b50995f5b39f368f540504a551803d6dd5f874828e5541ded052894d9e2dc5e6aa351087e790c0dd5d9c4decb217e4db81c98a184b264e6daeac0f11e074cae2bfc899f54b419c65dcc22664a915fbfffac35cee0f286eb7b144933db933e16c4bcb650d537722489de236373fd8d65fc86118b6def37ca4608bc6ce927b65436ffda7f02bfbf88b045ae7d2c2b45a0b30c8f2a04df953221088c555fe9a5df260982a3d64df194ee952fa9a98c31b96493db6180d13d67c36716f95f8c0bd7a039ad990667ca34a83ac1a18c37dd7c7736aa6b9b6fc2b1ac0ce119ef77:379f9c54c413af0d192e9bc736b29da9d521e7ba7841d309f9bcc1e742ec4308fe9f7ba51e0b22aed487cb4aa3913b9bebfb3aacd38f4039f9bbbebe1ad80002c9490d83d9c3a9370f06c91af001685a02fe49b5ca667733fff189eee853ec1667a6c1b6c787e9244812d2d532866ab74dfc870d6f14033b6bcd39852a3900f8f08cd95a74cb8cbe02b8b8b51e993a06adfebd7fc9854ae5d29f4df9642871d0c5e470d903cfbcbd5adb3275628f28a80bf8c0f0376687dae673bf7a8547e80d4a9855ae2572fc2b205dc8a198016ddc9b50995f5b39f368f540504a551803d6dd5f874828e5541ded052894d9e2dc5e6aa351087e790c0dd5d9c4decb217e4db81c98a184b264e6daeac0f11e074cae2bfc899f54b419c65dcc22664a915fbfffac35cee0f286eb7b144933db933e16c4bcb650d537722489de236373fd8d65fc86118b6def37ca4608bc6ce927b65436ffda7f02bfbf88b045ae7d2c2b45a0b30c8f2a04df953221088c555fe9a5df260982a3d64df194ee952fa9a98c31b96493db6180d13d67c36716f95f8c0bd7a039ad990667ca34a83ac1a18c37dd7c7736aa6b9b6fc2b1ac0ce119ef77: -ec2fa541ac14b414149c3825eaa7001b795aa1957d4040dda92573904afa7ee471b363b2408404d7beecdef1e1f511bb6084658b532f7ea63d4e3f5f01c61d31:71b363b2408404d7beecdef1e1f511bb6084658b532f7ea63d4e3f5f01c61d31:2749fc7c4a729e0e0ad71b5b74eb9f9c534ebd02ffc9df4374d813bdd1ae4eb87f1350d5fdc563934515771763e6c33b50e64e0cd114573031d2186b6eca4fc802cddc7cc51d92a61345a17f6ac38cc74d84707a5156be9202dee3444652e79bae7f0d31bd17567961f65dd01a8e4bee38331938ce4b2b550691b99a4bc3c072d186df4b3344a5c8fbfbb9fd2f355f6107e410c3d0c798b68d3fb9c6f7ab5fe27e70871e86767698fe35b77ead4e435a9402cc9ed6a2657b059be0a21003c048bbf5e0ebd93cbb2e71e923cf5c728d1758cd817ad74b454a887126d653b95a7f25e5293b768c9fc5a9c35a2372e3741bc90fd66301427b10824bb4b1e9110bfba84c21a40eb8fed4497e91dc3ffd0438c514c0a8cb4cac6ad0256bf11d5aa7a9c7c00b669b015b0bf81425a21413e2ffb6edc0bd78e385c44fd74558e511c2c25fee1fec18d3990b8690300fa711e93d9854668f0187065e76e7113ae763c30ddd86720b5546a6c3c6f1c43bc67b14:84d18d56f964e3776759bba92c510c2b6d574555c3cddade212da90374554991e7d77e278d63e34693e1958078cc3685f8c41c1f5342e351899638ef612114012749fc7c4a729e0e0ad71b5b74eb9f9c534ebd02ffc9df4374d813bdd1ae4eb87f1350d5fdc563934515771763e6c33b50e64e0cd114573031d2186b6eca4fc802cddc7cc51d92a61345a17f6ac38cc74d84707a5156be9202dee3444652e79bae7f0d31bd17567961f65dd01a8e4bee38331938ce4b2b550691b99a4bc3c072d186df4b3344a5c8fbfbb9fd2f355f6107e410c3d0c798b68d3fb9c6f7ab5fe27e70871e86767698fe35b77ead4e435a9402cc9ed6a2657b059be0a21003c048bbf5e0ebd93cbb2e71e923cf5c728d1758cd817ad74b454a887126d653b95a7f25e5293b768c9fc5a9c35a2372e3741bc90fd66301427b10824bb4b1e9110bfba84c21a40eb8fed4497e91dc3ffd0438c514c0a8cb4cac6ad0256bf11d5aa7a9c7c00b669b015b0bf81425a21413e2ffb6edc0bd78e385c44fd74558e511c2c25fee1fec18d3990b8690300fa711e93d9854668f0187065e76e7113ae763c30ddd86720b5546a6c3c6f1c43bc67b14: -6132692a5ef27bf476b1e991e6c431a8c764f1aebd470282db3321bb7cb09c207a2d166184f9e5f73bea454486b041ceb5fc2314a7bd59cb718e79f0ec989d84:7a2d166184f9e5f73bea454486b041ceb5fc2314a7bd59cb718e79f0ec989d84:a9c0861665d8c2de06f9301da70afb27b3024b744c6b38b24259294c97b1d1cb4f0dcf7575a8ed454e2f0980f50313a77363415183fe9677a9eb1e06cb6d34a467cb7b0758d6f55c564b5ba15603e202b18856d89e72a23ab07d8853ff77da7aff1caebd7959f2c710ef31f5078a9f2cdae92641a1cc5f74d0c143ec42afbaa5f378a9e10d5bf74587fa5f49c156233247dafd3929acde888dc684337e40cdc5932e7eb73ffcc90b85c0ad460416691aefbd7efd07b657c350946a0e366b37a6c8089aba5c5fe3bbca064afbe9d47fbc83914af1cb43c2b2efa98e0a43be32ba823202001def36817251b65f9b0506cef6683642a46ed612f8ca81ee97bb04d317b517343ade2b77126d1f02a87b7604c8653b6748cf5488fa6d43df809faa19e69292d38c5d397dd8e20c7af7c5334ec977f5010a0f7cb5b89479ca06db4d12627f067d6c42186a6b1f8742f36ae709ba720e3cd898116666d81b190b9b9d2a72202cb690a03f3310429a71dc048cde:eb677f3347e1a1ea929efdf62bf9105a6c8f4993033b4f6d03cb0dbf9c742b270704e383ab7c0676bdb1ad0ce9b16673083c9602ec10ae1dd98e8748b336440ba9c0861665d8c2de06f9301da70afb27b3024b744c6b38b24259294c97b1d1cb4f0dcf7575a8ed454e2f0980f50313a77363415183fe9677a9eb1e06cb6d34a467cb7b0758d6f55c564b5ba15603e202b18856d89e72a23ab07d8853ff77da7aff1caebd7959f2c710ef31f5078a9f2cdae92641a1cc5f74d0c143ec42afbaa5f378a9e10d5bf74587fa5f49c156233247dafd3929acde888dc684337e40cdc5932e7eb73ffcc90b85c0ad460416691aefbd7efd07b657c350946a0e366b37a6c8089aba5c5fe3bbca064afbe9d47fbc83914af1cb43c2b2efa98e0a43be32ba823202001def36817251b65f9b0506cef6683642a46ed612f8ca81ee97bb04d317b517343ade2b77126d1f02a87b7604c8653b6748cf5488fa6d43df809faa19e69292d38c5d397dd8e20c7af7c5334ec977f5010a0f7cb5b89479ca06db4d12627f067d6c42186a6b1f8742f36ae709ba720e3cd898116666d81b190b9b9d2a72202cb690a03f3310429a71dc048cde: -f219b2101164aa9723bde3a7346f68a35061c01f9782072580ba32df903ba891f66b920d5aa1a6085495a1480539beba01ffe60e6a6388d1b2e8eda23355810e:f66b920d5aa1a6085495a1480539beba01ffe60e6a6388d1b2e8eda23355810e:015577d3e4a0ec1ab25930106343ff35ab4f1e0a8a2d844aadbb70e5fc5348ccb679c2295c51d702aaae7f6273ce70297b26cb7a253a3db94332e86a15b4a64491232791f7a8b082ee2834af30400e804647a532e9c454d2a0a7320130ab6d4d860073a34667ac25b7e5e2747ba9f5c94594fb68377ae260369c40713b4e32f23195bf91d3d7f1a2719bf408aad8d8a347b112e84b118817cb06513344021763035272a7db728a0ccdaa949c61715d0764140b3e8c01d20ff1593c7f2d55c4e82a1c0cb1ea58442bf80a741bca91f58ab0581b498ee9fe3c92ca654148ef75313543d1aff382befe1a93b02190ce0102175158e2071d02bacad8dbe9fb940fcb610c105ad52c80feb1ec4e524f4c0ec7983e9ce696fa4fcf4bf0514b8f0432b17d5448fc426fea2b01ac7b26c2aed769927534da22576fc1bba726e9d65be01b59f60a648ace2fc3e5e275789fa637cbbd84be3d6ac24457a6292cd656c7b569a52ffea7916b8d04b4f4a75be7ac95142f:17f0127ca3bafa5f4ee959cd60f772be87a0034961517e39a0a1d0f4b9e26db1336e60c82b352c4cbacdbbd11771c3774f8cc5a1a795d6e4f4ebd51def36770b015577d3e4a0ec1ab25930106343ff35ab4f1e0a8a2d844aadbb70e5fc5348ccb679c2295c51d702aaae7f6273ce70297b26cb7a253a3db94332e86a15b4a64491232791f7a8b082ee2834af30400e804647a532e9c454d2a0a7320130ab6d4d860073a34667ac25b7e5e2747ba9f5c94594fb68377ae260369c40713b4e32f23195bf91d3d7f1a2719bf408aad8d8a347b112e84b118817cb06513344021763035272a7db728a0ccdaa949c61715d0764140b3e8c01d20ff1593c7f2d55c4e82a1c0cb1ea58442bf80a741bca91f58ab0581b498ee9fe3c92ca654148ef75313543d1aff382befe1a93b02190ce0102175158e2071d02bacad8dbe9fb940fcb610c105ad52c80feb1ec4e524f4c0ec7983e9ce696fa4fcf4bf0514b8f0432b17d5448fc426fea2b01ac7b26c2aed769927534da22576fc1bba726e9d65be01b59f60a648ace2fc3e5e275789fa637cbbd84be3d6ac24457a6292cd656c7b569a52ffea7916b8d04b4f4a75be7ac95142f: -fc180035aec0f5ede7bda93bf77ade7a81ed06de07ee2e3aa8576be81608610a4f215e948cae243ee3143b80282ad792c780d2a6b75060ca1d290ca1a8e3151f:4f215e948cae243ee3143b80282ad792c780d2a6b75060ca1d290ca1a8e3151f:b5e8b01625664b222339e0f05f93a990ba48b56ae65439a17520932df011721e284dbe36f98631c066510098a68d7b692a3863e99d58db76ca5667c8043cb10bd7abbaf506529fbb23a5166be038affdb9a234c4f4fcf43bddd6b8d2ce772dd653ed115c095e232b269dd4888d2368cb1c66be29dd383fca67f66765b296564e37555f0c0e484504c591f006ea8533a12583ad2e48318ff6f324ecaf804b1bae04aa896743e67ef61ca383d58e42acfc6410de30776e3ba262373b9e1441943955101a4e768231ad9c6529eff6118dde5df02f94b8d6df2d99f27863b517243a579e7aaff311ea3a0282e47ca876fabc2280fce7adc984dd0b30885b1650f1471dfcb0522d49fec7d042f32a93bc368f076006ea01ec1c7412bf66f62dc88de2c0b74701a5614e855e9fa728fb1f1171385f96afbde70dea02e9aa94dc21848c26302b50ae91f9693a1864e4e095ae03cdc22ad28a0eb7db596779246712fab5f5da327efec3e79612de0a6ccaa536759b8e:a43a71c3a19c35660dae6f31a254b8c0ea3593fc8fca74d13640012b9e9473d4afe070db01e7fb399bf4ca6070e062180011285a67dd6858b761e46c6bd32004b5e8b01625664b222339e0f05f93a990ba48b56ae65439a17520932df011721e284dbe36f98631c066510098a68d7b692a3863e99d58db76ca5667c8043cb10bd7abbaf506529fbb23a5166be038affdb9a234c4f4fcf43bddd6b8d2ce772dd653ed115c095e232b269dd4888d2368cb1c66be29dd383fca67f66765b296564e37555f0c0e484504c591f006ea8533a12583ad2e48318ff6f324ecaf804b1bae04aa896743e67ef61ca383d58e42acfc6410de30776e3ba262373b9e1441943955101a4e768231ad9c6529eff6118dde5df02f94b8d6df2d99f27863b517243a579e7aaff311ea3a0282e47ca876fabc2280fce7adc984dd0b30885b1650f1471dfcb0522d49fec7d042f32a93bc368f076006ea01ec1c7412bf66f62dc88de2c0b74701a5614e855e9fa728fb1f1171385f96afbde70dea02e9aa94dc21848c26302b50ae91f9693a1864e4e095ae03cdc22ad28a0eb7db596779246712fab5f5da327efec3e79612de0a6ccaa536759b8e: -a2836a65427912122d25dcdfc99d7046fe9b53d5c1bb23617f11890e94ca93ed8c12bda214c8abb2286acffbf8112425040aab9f4d8bb7870b98da0159e882f1:8c12bda214c8abb2286acffbf8112425040aab9f4d8bb7870b98da0159e882f1:813d6061c56eae0ff53041c0244aa5e29e13ec0f3fb428d4beb8a99e04bca8c41bddb0db945f487efe38f2fc14a628fafa2462f860e4e34250eb4e93f139ab1b74a2614519e41ee2403be427930ab8bc82ec89ceafb60905bd4ddbbd13bdb19654314fc92373140b962e2258e038d71b9ec66b84ef8319e03551cb707e747f6c40ad476fbefdce71f3a7b67a1af1869bc6440686e7e0855e4f369d1d88b8099fba54714678627bba1aff41e7707bc97eddf890b0c08dce3e9800d24c6f61092ce28d481b5dea5c096c55d72f8946009131fb968e2bc8a054d825adab76740dcf0d758c8bf54ff38659e71b32bfe2e615aaabb0f5293085649cf60b9847bc62011ce3878af628984a5840a4ad5dae3702db367da0f8a165fed0517eb5c442b0145330241b97eeca733ba6688b9c129a61cd1236aff0e27bcf98c28b0fbeea55a3d7c7193d644b2749f986bd46af8938e8faaeafbd9cec3612ab005bd7c3eeafe9a31279ca6102560666ba16136ff1452f850adb:e6a9a6b436559a4320c45c0c2c4a2aedecb90d416d52c82680ac7330d062aebef3e9ac9f2c5ffa455c9be113013a2b282e5600fd306435ada83b1e48ba2a3605813d6061c56eae0ff53041c0244aa5e29e13ec0f3fb428d4beb8a99e04bca8c41bddb0db945f487efe38f2fc14a628fafa2462f860e4e34250eb4e93f139ab1b74a2614519e41ee2403be427930ab8bc82ec89ceafb60905bd4ddbbd13bdb19654314fc92373140b962e2258e038d71b9ec66b84ef8319e03551cb707e747f6c40ad476fbefdce71f3a7b67a1af1869bc6440686e7e0855e4f369d1d88b8099fba54714678627bba1aff41e7707bc97eddf890b0c08dce3e9800d24c6f61092ce28d481b5dea5c096c55d72f8946009131fb968e2bc8a054d825adab76740dcf0d758c8bf54ff38659e71b32bfe2e615aaabb0f5293085649cf60b9847bc62011ce3878af628984a5840a4ad5dae3702db367da0f8a165fed0517eb5c442b0145330241b97eeca733ba6688b9c129a61cd1236aff0e27bcf98c28b0fbeea55a3d7c7193d644b2749f986bd46af8938e8faaeafbd9cec3612ab005bd7c3eeafe9a31279ca6102560666ba16136ff1452f850adb: -f051af426d0c3282fafc8bf912ade1c24211a95ad200e1eef549320e1cb1a252fa87955e0ea13dde49d83dc22e63a2bdf1076725c2cc7f93c76511f28e7944f2:fa87955e0ea13dde49d83dc22e63a2bdf1076725c2cc7f93c76511f28e7944f2:b48d9f84762b3bcc66e96d76a616fa8fe8e01695251f47cfc1b7b17d60dc9f90d576ef64ee7d388504e2c9079638165a889696471c989a876f8f13b63b58d531fea4dd1229fc631668a047bfae2da281feae1b6de3ebe280abe0a82ee00fbfdc22ce2d10e06a0492ff1404dfc094c40b203bf55721dd787ed4e91d5517aaf58d3bdd35d44a65ae6ba75619b339b650518cefcc17493de27a3b5d41788f87edbde72610f181bf06e208e0eb7cdfe881d91a2d6cc77aa19c0fcf330fedb44675d800eb8cff9505d8887544a503cbe373c4847b19e8f3995726efd6649858595c57ccaf0cbc9eb25de83ba046bc9f1838ac7b8953dd81b81ac0f68d0e9338cb55402552afb6bc16949351b926d151a82efc695e8d7da0dd55099366789718ccbf36030bd2c3c109399be26cdb8b9e2a155f3b2cb1bfa71ab69a23625a4ac118fe91cb2c19788cf52a71d730d576b421d96982a51a2991daec440cda7e6cc3282b8312714278b819bfe2387eb96aa91d40173034f428:b8f713578a64466719aceb432fce302a87cf066bf3e102a350616921a840964bfc7e685d8fd17455ac3eb4861edcb8979d35e3a4bd82a078cd707721d733400eb48d9f84762b3bcc66e96d76a616fa8fe8e01695251f47cfc1b7b17d60dc9f90d576ef64ee7d388504e2c9079638165a889696471c989a876f8f13b63b58d531fea4dd1229fc631668a047bfae2da281feae1b6de3ebe280abe0a82ee00fbfdc22ce2d10e06a0492ff1404dfc094c40b203bf55721dd787ed4e91d5517aaf58d3bdd35d44a65ae6ba75619b339b650518cefcc17493de27a3b5d41788f87edbde72610f181bf06e208e0eb7cdfe881d91a2d6cc77aa19c0fcf330fedb44675d800eb8cff9505d8887544a503cbe373c4847b19e8f3995726efd6649858595c57ccaf0cbc9eb25de83ba046bc9f1838ac7b8953dd81b81ac0f68d0e9338cb55402552afb6bc16949351b926d151a82efc695e8d7da0dd55099366789718ccbf36030bd2c3c109399be26cdb8b9e2a155f3b2cb1bfa71ab69a23625a4ac118fe91cb2c19788cf52a71d730d576b421d96982a51a2991daec440cda7e6cc3282b8312714278b819bfe2387eb96aa91d40173034f428: -a103e92672c65f81ea5da1fff1a4038788479e941d503a756f4a755201a57c1dee63a5b69641217acbaf3339da829ec071b9931e5987153514d30140837a7af4:ee63a5b69641217acbaf3339da829ec071b9931e5987153514d30140837a7af4:b1984e9eec085d524c1eb3b95c89c84ae085be5dc65c326e19025e1210a1d50edbbba5d1370cf15d68d687eb113233e0fba50f9433c7d358773950c67931db8296bbcbecec888e87e71a2f7579fad2fa162b85fb97473c456b9a5ce2956676969c7bf4c45679085b62f2c224fc7f458794273f6d12c5f3e0d06951824d1cca3e2f904559ed28e2868b366d79d94dc98667b9b5924268f3e39b1291e5abe4a758f77019dacbb22bd8196e0a83a5677658836e96ca5635055a1e63d65d036a68d87ac2fd283fdda390319909c5cc7680368848873d597f298e0c6172308030ffd452bb1363617b316ed7cd949a165dc8abb53f991aef3f3e9502c5dfe4756b7c6bfdfe89f5e00febdd6afb0402818f11cf8d1d5864fe9da1b86e39aa935831506cf2400ea7ed75bd9533b23e202fe875d7d9638c89d11cb2d6e6021ae6bd27c7754810d35cd3a61494f27b16fc794e2cd2f0d3453ada933865db78c579571f8fc5c5c6be8eaffce6a852e5b3b1c524c49313d427abcb:2aa2035c2ce5b5e6ae161e168f3ad0d6592bcf2c4a049d3ed342fceb56be9c7cb372027573ae0178e8878ebefca7b030327b8aad41857de58cb78e1a00cbac05b1984e9eec085d524c1eb3b95c89c84ae085be5dc65c326e19025e1210a1d50edbbba5d1370cf15d68d687eb113233e0fba50f9433c7d358773950c67931db8296bbcbecec888e87e71a2f7579fad2fa162b85fb97473c456b9a5ce2956676969c7bf4c45679085b62f2c224fc7f458794273f6d12c5f3e0d06951824d1cca3e2f904559ed28e2868b366d79d94dc98667b9b5924268f3e39b1291e5abe4a758f77019dacbb22bd8196e0a83a5677658836e96ca5635055a1e63d65d036a68d87ac2fd283fdda390319909c5cc7680368848873d597f298e0c6172308030ffd452bb1363617b316ed7cd949a165dc8abb53f991aef3f3e9502c5dfe4756b7c6bfdfe89f5e00febdd6afb0402818f11cf8d1d5864fe9da1b86e39aa935831506cf2400ea7ed75bd9533b23e202fe875d7d9638c89d11cb2d6e6021ae6bd27c7754810d35cd3a61494f27b16fc794e2cd2f0d3453ada933865db78c579571f8fc5c5c6be8eaffce6a852e5b3b1c524c49313d427abcb: -d47c1b4b9e50cbb71fd07d096d91d87213d44b024373044761c4822f9d9df880f4e1cb86c8ca2cfee43e58594a8778436d3ea519704e00c1bbe48bbb1c9454f8:f4e1cb86c8ca2cfee43e58594a8778436d3ea519704e00c1bbe48bbb1c9454f8:88d7009d51de3d337eef0f215ea66ab830ec5a9e6823761c3b92ad93ea341db92ece67f4ef4ceb84194ae6926c3d014b2d59781f02e0b32f9a611222cb9a5850c6957cb8079ae64e0832a1f05e5d1a3c572f9d08f1437f76bb3b83b52967c3d48c3576848891c9658d4959eb80656d26cdba0810037c8a18318ff122f8aa8985c773cb317efa2f557f1c3896bcb162df5d87681bb787e7813aa2dea3b0c564d646a92861f444ca1407efbac3d12432cbb70a1d0eaffb11741d3718fedee2b83036189a6fc45a52f74fa487c18fd264a7945f6c9e44b011f5d86613f1939b19f4f4fdf53234057be3f005ad64eebf3c8ffb58cb40956c4336df01d4424b706a0e561d601708d12485e21bcb6d799d8d1d044b400064ec0944501406e70253947006cabbdb2dd6bd8cee4497653d9113a44d4de9b68d4c526fca0b9b0c18fe50fb917fdd9a914fb816108a73a6b3fff9e654e69c9cfe02b05c6c1b9d15c4e65cf31018b8100d784633ee1888eee3572aafa6f189ea22d0:627e7ca7e34ed6331d62b9541c1ea9a9292be7b0a65d805e266b5122272a82db7d765acc7e2a290d685804922f91ed04a3c382c03ff21a1768f584413c4e5f0088d7009d51de3d337eef0f215ea66ab830ec5a9e6823761c3b92ad93ea341db92ece67f4ef4ceb84194ae6926c3d014b2d59781f02e0b32f9a611222cb9a5850c6957cb8079ae64e0832a1f05e5d1a3c572f9d08f1437f76bb3b83b52967c3d48c3576848891c9658d4959eb80656d26cdba0810037c8a18318ff122f8aa8985c773cb317efa2f557f1c3896bcb162df5d87681bb787e7813aa2dea3b0c564d646a92861f444ca1407efbac3d12432cbb70a1d0eaffb11741d3718fedee2b83036189a6fc45a52f74fa487c18fd264a7945f6c9e44b011f5d86613f1939b19f4f4fdf53234057be3f005ad64eebf3c8ffb58cb40956c4336df01d4424b706a0e561d601708d12485e21bcb6d799d8d1d044b400064ec0944501406e70253947006cabbdb2dd6bd8cee4497653d9113a44d4de9b68d4c526fca0b9b0c18fe50fb917fdd9a914fb816108a73a6b3fff9e654e69c9cfe02b05c6c1b9d15c4e65cf31018b8100d784633ee1888eee3572aafa6f189ea22d0: -fc0c32c5eb6c71ea08dc2b300cbcef18fdde3ea20f68f21733237b4ddaab900e47c37d8a080857eb8777a6c0a9a5c927303faf5c320953b5de48e462e12d0062:47c37d8a080857eb8777a6c0a9a5c927303faf5c320953b5de48e462e12d0062:a7b1e2db6bdd96b3d51475603537a76b42b04d7ebd24fe515a887658e4a352e22109335639a59e2534811f4753b70209d0e4698e9d926088826c14689681ea00fa3a2fcaa0047ced3ef287e6172502b215e56497614d86b4cb26bcd77a2e172509360ee58893d01c0d0fb4d4abfe4dbd8d2a2f54190fa2f731c1ceac6829c3ddc9bfb2ffd70c57ba0c2b22d2326fbfe7390db8809f73547ff47b86c36f2bf7454e678c4f1c0fa870bd0e30bbf3278ec8d0c5e9b64aff0af64babc19b70f4cf9a41cb8f95d3cde24f456ba3571c8f021d38e591dec05cb5d1ca7b48f9da4bd734b069a9fd106500c1f408ab7fe8e4a6e6f3ed64da0ed24b01e33df8475f95fa9ed71d04dd30b3cd823755a3401bf5afae10ee7e18ec6fe637c3793fd434b48d7145130447e00299101052558b506554ec9c399f62941c3f414cbc352caa345b930adecfaddac91ee53d1451a65e06201026325de07c931f69bba868a7c87ee23c604ec6794332917dfe2c5b69669b659706917f71eddf96:6887c6e2b98a82af5ee3dfa7ca2cb25d9c10745620a82956acba85cb57c8ec24279fa42f092359a1b6bbeafba050f14b6288209e6ef7bc1e0a2b872c1138f305a7b1e2db6bdd96b3d51475603537a76b42b04d7ebd24fe515a887658e4a352e22109335639a59e2534811f4753b70209d0e4698e9d926088826c14689681ea00fa3a2fcaa0047ced3ef287e6172502b215e56497614d86b4cb26bcd77a2e172509360ee58893d01c0d0fb4d4abfe4dbd8d2a2f54190fa2f731c1ceac6829c3ddc9bfb2ffd70c57ba0c2b22d2326fbfe7390db8809f73547ff47b86c36f2bf7454e678c4f1c0fa870bd0e30bbf3278ec8d0c5e9b64aff0af64babc19b70f4cf9a41cb8f95d3cde24f456ba3571c8f021d38e591dec05cb5d1ca7b48f9da4bd734b069a9fd106500c1f408ab7fe8e4a6e6f3ed64da0ed24b01e33df8475f95fa9ed71d04dd30b3cd823755a3401bf5afae10ee7e18ec6fe637c3793fd434b48d7145130447e00299101052558b506554ec9c399f62941c3f414cbc352caa345b930adecfaddac91ee53d1451a65e06201026325de07c931f69bba868a7c87ee23c604ec6794332917dfe2c5b69669b659706917f71eddf96: -a8d73d639a23cc6a967ef31bcabb5d063e53e1eab8fcc7cab9bc3a17fde9c2f88daa9f4c8b1a44691bf44521f2f7ca45dc7fc61f6a4ce6f98faa41c2a74977d1:8daa9f4c8b1a44691bf44521f2f7ca45dc7fc61f6a4ce6f98faa41c2a74977d1:fd1fac3d53313b11acd29f5a83ac11896dab2530fa47865b2295c0d99dd67c36ed8e5fa549150c794c5549efb5c1d69114d5d607b23285b7212afaab57846a54ae67b9e880e07b6586607cecf6d4eed516a3a75511fe367d88eb871e6d71b7d6aa1367a01421b1088fc2d75e44954b73625c52da8a3a183c60be9da6050f59a453caa53520593671728d431877bfaac913a765fb6a56b75290b2a8aaac34afb9217ba1b0d5850ba0fdabf80969def0feee794ceb60614e3368e63ef20e4c32d341ec9b0328ea9fe139207ed7a626ff08943b415233db7cfcc845c9b63121d4ed52ec3748ab6a1f36b2103c7dc7e9303acea4ba8af7a3e07184fb491e891ede84f0dc41cadc3973028e879acd2031afc29a16092868e2c7f539fc1b792edab195a25ab9830661346b39ef53915de4af52c421eaf172e9da76a08c283a52df907f705d7e8599c5baae0c2af380c1bb46f93484a03f28374324b278992b50b7afa02552cafa503f034f8d866e9b720271dd68ccb685a85fffd1:c4dcef1a2453939b364b340250c3129431431d5ba3f47670ab07ce680c69bf28b678627c76a6360fc40dc109aa7dea371b825e46134f624572182acf3957e70ffd1fac3d53313b11acd29f5a83ac11896dab2530fa47865b2295c0d99dd67c36ed8e5fa549150c794c5549efb5c1d69114d5d607b23285b7212afaab57846a54ae67b9e880e07b6586607cecf6d4eed516a3a75511fe367d88eb871e6d71b7d6aa1367a01421b1088fc2d75e44954b73625c52da8a3a183c60be9da6050f59a453caa53520593671728d431877bfaac913a765fb6a56b75290b2a8aaac34afb9217ba1b0d5850ba0fdabf80969def0feee794ceb60614e3368e63ef20e4c32d341ec9b0328ea9fe139207ed7a626ff08943b415233db7cfcc845c9b63121d4ed52ec3748ab6a1f36b2103c7dc7e9303acea4ba8af7a3e07184fb491e891ede84f0dc41cadc3973028e879acd2031afc29a16092868e2c7f539fc1b792edab195a25ab9830661346b39ef53915de4af52c421eaf172e9da76a08c283a52df907f705d7e8599c5baae0c2af380c1bb46f93484a03f28374324b278992b50b7afa02552cafa503f034f8d866e9b720271dd68ccb685a85fffd1: -79c7dcb7d59a8df6b2b2ba0413059d89680995c20e916da01b8f067dc60cdeb4298743c73918bd556b28f8d4824a09b814752a7aeae7ee04875c53f4d6b108d9:298743c73918bd556b28f8d4824a09b814752a7aeae7ee04875c53f4d6b108d9:5fe202f5b33b7788810d2508a13b3114d69b8596e6eacda05a04a2eb597fa3279c208b5a5b65daacb699f144e1d660e78e139b578331abec5c3c35334454f03e832c8d6e2984df5d450ecb5d33582a78808a9c78f26ebcd1244ef52e3fa6dca115c1f0cb56e38eae0e5b39f5fd863dffd0b2fb5b958f2d739db312fc667a17b031c4c9f8c5a2ad577984cc4146c437580efd2152173fe0d5782cc2ae9831a8d9a04177256018ff7631e0b0d8a99cb28f008b320421e27a74c31359188663456d85e098c1ebd281701097b6ae5a871e5ccc02058a501416cb91c12cef5be6f1914370e563f1a1b2aa41f4b8ee84cd32a1d509e529787d14a445438d807ecd620e2fa26de0da6426864784d4a28f54103e609283b99ee9b2b699c980bbb7882c3ea68ddc90802ac232f2c8e84291987bf3c5240921b59cfa214969317673d0be7f34b1ca0e15ea73c7175401ce550be106b49e62f8db68695e740e0f3a3556a19f3c8e6b91ac1cc23e863fcd0f0d9eb7047aa631e0d2eb9bcc6b:7b7cbe44c771e4371bae13b0722babcc1064155732962f407cba2acd35381d42210bece822f4681121fd4dab745a1f3077922fba1a78045b712902baccac660e5fe202f5b33b7788810d2508a13b3114d69b8596e6eacda05a04a2eb597fa3279c208b5a5b65daacb699f144e1d660e78e139b578331abec5c3c35334454f03e832c8d6e2984df5d450ecb5d33582a78808a9c78f26ebcd1244ef52e3fa6dca115c1f0cb56e38eae0e5b39f5fd863dffd0b2fb5b958f2d739db312fc667a17b031c4c9f8c5a2ad577984cc4146c437580efd2152173fe0d5782cc2ae9831a8d9a04177256018ff7631e0b0d8a99cb28f008b320421e27a74c31359188663456d85e098c1ebd281701097b6ae5a871e5ccc02058a501416cb91c12cef5be6f1914370e563f1a1b2aa41f4b8ee84cd32a1d509e529787d14a445438d807ecd620e2fa26de0da6426864784d4a28f54103e609283b99ee9b2b699c980bbb7882c3ea68ddc90802ac232f2c8e84291987bf3c5240921b59cfa214969317673d0be7f34b1ca0e15ea73c7175401ce550be106b49e62f8db68695e740e0f3a3556a19f3c8e6b91ac1cc23e863fcd0f0d9eb7047aa631e0d2eb9bcc6b: -b9ced0412593fefed95e94ac965e5b23ff9d4b0e797db02bf497994d3b793e60c1629a723189959337f5535201e5d395ba0a03ea8c17660d0f8b6f6e6404bb12:c1629a723189959337f5535201e5d395ba0a03ea8c17660d0f8b6f6e6404bb12:555bb39c1899d57cabe428064c2d925f5fc4cf7059b95fb89a8e9e3a7e426c6c922d9e4d76984ea2383cabb4f2befd89c1f20eaa8a00dbe787cfa70ae2ae6aa90331cbbe580fa5a02184ed05e6c8e89d576af28aeeaf7c4e2500f358a00971a0a75920e854849bf332142975404f598c32e96982043d992bcd1a4fe819bb5634ad03467afc4ce05073f88ba1ba4ae8653a04665cf3f71690fe13343885bc5ebc0e5e62d882f43b7c68900ac9438bf4a81ce90169ec129ee63e2c675a1a5a67e27cc798c48cc23f51078f463b3b7cc14e3bcfd2e9b82c75240934cbdc50c4308f282f193122995606f40135100a291c55afdf8934eb8b61d81421674124dec3b88f9a73110a9e616f5b826b9d343f3ac0e9d7bdf4fd8b648b40f0098b3897a3a1cd65a64570059b8bc5c6743883074c88623c1f5a88c58969e21c692aca236833d3470b3eb09815e1138e9d0650c390eee977422193b00918be8a97cc6199b451b05b5730d1d13358cf74610678f7ac7f7895cc2efc456e03873b:f1b797ded8a6942b12626848340fb719fcddafd98f33e2992d357bfdd35933c7ac561e5b2f939464338c5666854ca885c4d046eb2c54e48a1b5ed266ad34de05555bb39c1899d57cabe428064c2d925f5fc4cf7059b95fb89a8e9e3a7e426c6c922d9e4d76984ea2383cabb4f2befd89c1f20eaa8a00dbe787cfa70ae2ae6aa90331cbbe580fa5a02184ed05e6c8e89d576af28aeeaf7c4e2500f358a00971a0a75920e854849bf332142975404f598c32e96982043d992bcd1a4fe819bb5634ad03467afc4ce05073f88ba1ba4ae8653a04665cf3f71690fe13343885bc5ebc0e5e62d882f43b7c68900ac9438bf4a81ce90169ec129ee63e2c675a1a5a67e27cc798c48cc23f51078f463b3b7cc14e3bcfd2e9b82c75240934cbdc50c4308f282f193122995606f40135100a291c55afdf8934eb8b61d81421674124dec3b88f9a73110a9e616f5b826b9d343f3ac0e9d7bdf4fd8b648b40f0098b3897a3a1cd65a64570059b8bc5c6743883074c88623c1f5a88c58969e21c692aca236833d3470b3eb09815e1138e9d0650c390eee977422193b00918be8a97cc6199b451b05b5730d1d13358cf74610678f7ac7f7895cc2efc456e03873b: -81da168f02d46bb87cda845da43f8a6cba2c016878d6f49c6f061a60f155a04aaff86e98093ca4c71b1b804c5fe451cfdf868250dea30345fa4b89bb09b6a53b:aff86e98093ca4c71b1b804c5fe451cfdf868250dea30345fa4b89bb09b6a53b:6bc6726a34a64aae76ab08c92b179e54ff5d2e65eb2c6c659ae8703cc245cbc2cf45a12b22c468ae61fd9a6627ad0626c9b1e5af412cb483eaee1db11b29f0a510c13e38020e09ae0eee762537a3e9d1a0c7b033d097fdc1f4f82629a9de9ef38da1cf96a940357d5f2e0e7e8dbc29db728a1e6aad876e5e053113d06420272b87cf0c40dfe03a544de96c7aea13ba0029b57b48d99dcc6a650492d78c4cdd1b28e1a115a7e3e7a7cb21333d4ff80858dfb67782c16354b8716596560d7d8e389eb15a052a0bf5d16eb54fb3e4973ad4984e72a187f5347d5b262c32b1647e42b6a53837096cc78c2a05ce1c6e12493a03f1a667584cb97f4fcd57ee944c65b7eed25f7ae0f3f6cede173fdfacf5af1db143730d18096664914ba4cfc6966f392022781c66a9417ca2680b51f63e4fba424ecfdbc6a2f01787d0e7484f8a8ab390aeaa6d1f7ed325d82feaa1692a4984fae43da87329b045da8f0a4f56b695aa935de152ce0385153720979a2b7006d405fcb0fba09e23b85fd19b:4aaca947e3f22cc8b8588ee030ace8f6b5f5711c2974f20cc18c3b655b07a5bc1366b59a1708032d12cae01ab794f8cbcc1a330874a75035db1d69422d2fc00c6bc6726a34a64aae76ab08c92b179e54ff5d2e65eb2c6c659ae8703cc245cbc2cf45a12b22c468ae61fd9a6627ad0626c9b1e5af412cb483eaee1db11b29f0a510c13e38020e09ae0eee762537a3e9d1a0c7b033d097fdc1f4f82629a9de9ef38da1cf96a940357d5f2e0e7e8dbc29db728a1e6aad876e5e053113d06420272b87cf0c40dfe03a544de96c7aea13ba0029b57b48d99dcc6a650492d78c4cdd1b28e1a115a7e3e7a7cb21333d4ff80858dfb67782c16354b8716596560d7d8e389eb15a052a0bf5d16eb54fb3e4973ad4984e72a187f5347d5b262c32b1647e42b6a53837096cc78c2a05ce1c6e12493a03f1a667584cb97f4fcd57ee944c65b7eed25f7ae0f3f6cede173fdfacf5af1db143730d18096664914ba4cfc6966f392022781c66a9417ca2680b51f63e4fba424ecfdbc6a2f01787d0e7484f8a8ab390aeaa6d1f7ed325d82feaa1692a4984fae43da87329b045da8f0a4f56b695aa935de152ce0385153720979a2b7006d405fcb0fba09e23b85fd19b: -af2e60da0f29bb1614fc3f193cc353331986b73f3f9a0aec9421b9473d6a4b6ac8bfe2835822199c6127b806fabeef0cb9ff59f3c81ff0cb89c556f55106af6a:c8bfe2835822199c6127b806fabeef0cb9ff59f3c81ff0cb89c556f55106af6a:7dbb77b88bda94f344416a06b096566c6e8b393931a8243a6cab75c361fde7dc536aec40cded83296a89e8c3bef7d787cfc49401a7b9183f138d5000619ff073c05e2f841d6008358f10a2da7dcfac3d4d70c20d2ec34c7b6d5cd1a734d6bbb11c5fd8d2bce32ac810ef82b4188aa8ea3cfc3032233dc0e2600e9db6e18bc22b10044a31c15baceaf5554de89d2a3466807f244414d080ff2963956c6e83c8e144ed0066088b476ddcb564403447d9159f9089aba2b4d5575c4d8ae66fc8690e7349ed40832e6369c024563ec493bfcc0fc9ac787ac841397fe133167283d80c42f006a99d39e82979da3fa9334bd9ede0d14b41b7466bcebbe8171bc804a645d3723274a1b92bf82fd993358744de92441903d436fd47f23d40052a3829367f202f0553b5e49b76c5e03fa6ce7c3cf5eeb21de967bec4dd355925384ebf96697e823762bac4d43a767c241a4cef724a970d00ff3a8ab3b83eed840075c74e90f306e330013260962161e9d0910de183622ce9a6b8d5144280550fc7:50f9f941a8da9f6240f76d2fa3b06dd6b2292ed32d1c05218097d34d8a19dfe553f76ae3c6b4a2ed20852128461540decf418f52d38e64037eec7771bd1afe007dbb77b88bda94f344416a06b096566c6e8b393931a8243a6cab75c361fde7dc536aec40cded83296a89e8c3bef7d787cfc49401a7b9183f138d5000619ff073c05e2f841d6008358f10a2da7dcfac3d4d70c20d2ec34c7b6d5cd1a734d6bbb11c5fd8d2bce32ac810ef82b4188aa8ea3cfc3032233dc0e2600e9db6e18bc22b10044a31c15baceaf5554de89d2a3466807f244414d080ff2963956c6e83c8e144ed0066088b476ddcb564403447d9159f9089aba2b4d5575c4d8ae66fc8690e7349ed40832e6369c024563ec493bfcc0fc9ac787ac841397fe133167283d80c42f006a99d39e82979da3fa9334bd9ede0d14b41b7466bcebbe8171bc804a645d3723274a1b92bf82fd993358744de92441903d436fd47f23d40052a3829367f202f0553b5e49b76c5e03fa6ce7c3cf5eeb21de967bec4dd355925384ebf96697e823762bac4d43a767c241a4cef724a970d00ff3a8ab3b83eed840075c74e90f306e330013260962161e9d0910de183622ce9a6b8d5144280550fc7: -605f90b53d8e4a3b48b97d745439f2a0807d83b8502e8e2979f03e8d376ac9feaa3fae4cfa6f6bfd14ba0afa36dcb1a2656f36541ad6b3e67f1794b06360a62f:aa3fae4cfa6f6bfd14ba0afa36dcb1a2656f36541ad6b3e67f1794b06360a62f:3bcdcac292ac9519024aaecee2b3e999ff5d3445e9f1eb60940f06b91275b6c5db2722ed4d82fe89605226530f3e6b0737b308cde8956184944f388a80042f6cba274c0f7d1192a0a96b0da6e2d6a61b76518fbee555773a414590a928b4cd545fccf58172f35857120eb96e75c5c8ac9ae3add367d51d34ac403446360ec10f553ea9f14fb2b8b78cba18c3e506b2f04097063a43b2d36431cce02caf11c5a4db8c821752e52985d5af1bfbf4c61572e3fadae3ad424acd81662ea5837a1143b9669391d7b9cfe230cffb3a7bb03f6591c25a4f01c0d2d4aca3e74db1997d3739c851f0327db919ff6e77f6c8a20fdd3e1594e92d01901ab9aef194fc893e70d78c8ae0f480001a515d4f9923ae6278e8927237d05db23e984c92a683882f57b1f1882a74a193ab6912ff241b9ffa662a0d47f29205f084dbde845baaeb5dd36ae6439a437642fa763b57e8dbe84e55813f0151e97e5b9de768b234b8db15c496d4bfcfa1388788972bb50ce030bc6e0ccf4fa7d00d343782f6ba8de0:dd0212e63288cbe14a4569b4d891da3c7f92727c5e7f9a801cf9d6827085e7095b669d7d45f882ca5f0745dccd24d87a57181320191e5b7a47c3f7f2dccbd7073bcdcac292ac9519024aaecee2b3e999ff5d3445e9f1eb60940f06b91275b6c5db2722ed4d82fe89605226530f3e6b0737b308cde8956184944f388a80042f6cba274c0f7d1192a0a96b0da6e2d6a61b76518fbee555773a414590a928b4cd545fccf58172f35857120eb96e75c5c8ac9ae3add367d51d34ac403446360ec10f553ea9f14fb2b8b78cba18c3e506b2f04097063a43b2d36431cce02caf11c5a4db8c821752e52985d5af1bfbf4c61572e3fadae3ad424acd81662ea5837a1143b9669391d7b9cfe230cffb3a7bb03f6591c25a4f01c0d2d4aca3e74db1997d3739c851f0327db919ff6e77f6c8a20fdd3e1594e92d01901ab9aef194fc893e70d78c8ae0f480001a515d4f9923ae6278e8927237d05db23e984c92a683882f57b1f1882a74a193ab6912ff241b9ffa662a0d47f29205f084dbde845baaeb5dd36ae6439a437642fa763b57e8dbe84e55813f0151e97e5b9de768b234b8db15c496d4bfcfa1388788972bb50ce030bc6e0ccf4fa7d00d343782f6ba8de0: -9e2c3d189838f4dd52ef0832886874c5ca493983ddadc07cbc570af2ee9d6209f68d3b81e73557ee1f08bd2d3f46a4718256a0f3cd8d2e03eb8fe882aab65c69:f68d3b81e73557ee1f08bd2d3f46a4718256a0f3cd8d2e03eb8fe882aab65c69:19485f5238ba82eadf5eff14ca75cd42e5d56fea69d5718cfb5b1d40d760899b450e66884558f3f25b7c3de9afc4738d7ac09da5dd4689bbfac07836f5e0be432b1ddcf1b1a075bc9815d0debc865d90bd5a0c5f5604d9b46ace816c57694ecc3d40d8f84df0ede2bc4d577775a027f725de0816f563fa88f88e077720ebb6ac02574604819824db7474d4d0b22cd1bc05768e0fb867ca1c1a7b90b34ab7a41afc66957266ac0c915934aaf31c0cf6927a4f03f23285e6f24afd5813849bb08c203ac2d0336dcbf80d77f6cf7120edfbcdf181db107ec8e00f32449c1d3f5c049a92694b4ea2c6ebe5e2b0f64b5ae50ad3374d246b3270057e724a27cf263b633ab65ecb7f5c266b8007618b10ac9ac83db0febc04fd863d9661ab6e58494766f71b9a867c5a7a4555f667c1af2e54588f162a41ce756407cc4161d607b6e0682980934caa1bef036f7330d9eef01ecc553583fee5994e533a46ca916f60f8b961ae01d20f7abf0df6141b604de733c636b42018cd5f1d1ef4f84cee40fc:38a31b6b465084738262a26c065fe5d9e2886bf9dd35cde05df9bad0cc7db401c750aa19e66090bce25a3c721201e60502c8c10454346648af065eab0ee7d80f19485f5238ba82eadf5eff14ca75cd42e5d56fea69d5718cfb5b1d40d760899b450e66884558f3f25b7c3de9afc4738d7ac09da5dd4689bbfac07836f5e0be432b1ddcf1b1a075bc9815d0debc865d90bd5a0c5f5604d9b46ace816c57694ecc3d40d8f84df0ede2bc4d577775a027f725de0816f563fa88f88e077720ebb6ac02574604819824db7474d4d0b22cd1bc05768e0fb867ca1c1a7b90b34ab7a41afc66957266ac0c915934aaf31c0cf6927a4f03f23285e6f24afd5813849bb08c203ac2d0336dcbf80d77f6cf7120edfbcdf181db107ec8e00f32449c1d3f5c049a92694b4ea2c6ebe5e2b0f64b5ae50ad3374d246b3270057e724a27cf263b633ab65ecb7f5c266b8007618b10ac9ac83db0febc04fd863d9661ab6e58494766f71b9a867c5a7a4555f667c1af2e54588f162a41ce756407cc4161d607b6e0682980934caa1bef036f7330d9eef01ecc553583fee5994e533a46ca916f60f8b961ae01d20f7abf0df6141b604de733c636b42018cd5f1d1ef4f84cee40fc: -575f8fb6c7465e92c250caeec1786224bc3eed729e463953a394c9849cba908f71bfa98f5bea790ff183d924e6655cea08d0aafb617f46d23a17a657f0a9b8b2:71bfa98f5bea790ff183d924e6655cea08d0aafb617f46d23a17a657f0a9b8b2:2cc372e25e53a138793064610e7ef25d9d7422e18e249675a72e79167f43baf452cbacb50182faf80798cc38597a44b307a536360b0bc1030f8397b94cbf147353dd2d671cb8cab219a2d7b9eb828e9635d2eab6eb08182cb03557783fd282aaf7b471747c84acf72debe4514524f8447bafccccec0a840feca9755ff9adb60301c2f25d4e3ba621df5ad72100c45d7a4b91559c725ab56bb29830e35f5a6faf87db23001f11ffba9c0c15440302065827a7d7aaaeab7b446abce333c0d30c3eae9c9da63eb1c0391d4269b12c45b660290611ac29c91dbd80dc6ed302a4d191f2923922f032ab1ac10ca7323b5241c5751c3c004ac39eb1267aa10017ed2dac6c934a250dda8cb06d5be9f563b827bf3c8d95fd7d2a7e7cc3acbee92538bd7ddfba3ab2dc9f791fac76cdf9cd6a6923534cf3e067108f6aa03e320d954085c218038a70cc768b972e49952b9fe171ee1be2a52cd469b8d36b84ee902cd9410db2777192e90070d2e7c56cb6a45f0a839c78c219203b6f1b33cb4504c6a7996427741e6874cf45c5fa5a38765a1ebf1796ce16e63ee509612c40f088cbceffa3affbc13b75a1b9c02c61a180a7e83b17884fe0ec0f2fe57c47e73a22f753eaf50fca655ebb19896b827a3474911c67853c58b4a78fd085a23239b9737ef8a7baff11ddce5f2cae0543f8b45d144ae6918b9a75293ec78ea618cd2cd08c971301cdfa0a9275c1bf441d4c1f878a2e733ce0a33b6ecdacbbf0bdb5c3643fa45a013979cd01396962897421129a88757c0d88b5ac7e44fdbd938ba4bc37de4929d53751fbb43d4e09a80e735244acada8e6749f77787f33763c7472df52934591591fb226c503c8be61a920a7d37eb1686b62216957844c43c484e58745775553:903b484cb24bc503cdced844614073256c6d5aa45f1f9f62c7f22e5649212bc1d6ef9eaa617b6b835a6de2beff2faac83d37a4a5fc5cc3b556f56edde2651f022cc372e25e53a138793064610e7ef25d9d7422e18e249675a72e79167f43baf452cbacb50182faf80798cc38597a44b307a536360b0bc1030f8397b94cbf147353dd2d671cb8cab219a2d7b9eb828e9635d2eab6eb08182cb03557783fd282aaf7b471747c84acf72debe4514524f8447bafccccec0a840feca9755ff9adb60301c2f25d4e3ba621df5ad72100c45d7a4b91559c725ab56bb29830e35f5a6faf87db23001f11ffba9c0c15440302065827a7d7aaaeab7b446abce333c0d30c3eae9c9da63eb1c0391d4269b12c45b660290611ac29c91dbd80dc6ed302a4d191f2923922f032ab1ac10ca7323b5241c5751c3c004ac39eb1267aa10017ed2dac6c934a250dda8cb06d5be9f563b827bf3c8d95fd7d2a7e7cc3acbee92538bd7ddfba3ab2dc9f791fac76cdf9cd6a6923534cf3e067108f6aa03e320d954085c218038a70cc768b972e49952b9fe171ee1be2a52cd469b8d36b84ee902cd9410db2777192e90070d2e7c56cb6a45f0a839c78c219203b6f1b33cb4504c6a7996427741e6874cf45c5fa5a38765a1ebf1796ce16e63ee509612c40f088cbceffa3affbc13b75a1b9c02c61a180a7e83b17884fe0ec0f2fe57c47e73a22f753eaf50fca655ebb19896b827a3474911c67853c58b4a78fd085a23239b9737ef8a7baff11ddce5f2cae0543f8b45d144ae6918b9a75293ec78ea618cd2cd08c971301cdfa0a9275c1bf441d4c1f878a2e733ce0a33b6ecdacbbf0bdb5c3643fa45a013979cd01396962897421129a88757c0d88b5ac7e44fdbd938ba4bc37de4929d53751fbb43d4e09a80e735244acada8e6749f77787f33763c7472df52934591591fb226c503c8be61a920a7d37eb1686b62216957844c43c484e58745775553: diff --git a/infrastructure/derive/Cargo.toml b/infrastructure/derive/Cargo.toml index 896bb44039..32f2f62e46 100644 --- a/infrastructure/derive/Cargo.toml +++ b/infrastructure/derive/Cargo.toml @@ -6,7 +6,7 @@ repository = "https://github.com/tari-project/tari" homepage = "https://tari.com" readme = "README.md" license = "BSD-3-Clause" -version = "0.0.6" +version = "0.0.9" edition = "2018" [lib] diff --git a/infrastructure/derive/src/extend_bytes.rs b/infrastructure/derive/src/extend_bytes.rs index b214d93750..57864b811f 100644 --- a/infrastructure/derive/src/extend_bytes.rs +++ b/infrastructure/derive/src/extend_bytes.rs @@ -49,7 +49,7 @@ fn handle_fields_for_extend_bytes(item: &Data) -> proc_macro2::TokenStream { for attr in &f.attrs { match attr.interpret_meta().unwrap() { syn::Meta::NameValue(ref val) => { - if val.ident.to_string() == "ExtendBytes" { + if val.ident == "ExtendBytes" { if let syn::Lit::Str(lit) = &val.lit { if lit.value() == "Ignore" { do_we_ignore_field = true; @@ -59,12 +59,12 @@ fn handle_fields_for_extend_bytes(item: &Data) -> proc_macro2::TokenStream { }, syn::Meta::List(ref val) => { // we have more than one property - if val.ident.to_string() == "ExtendBytes" { + if val.ident == "ExtendBytes" { // we have a hash command here, lets search for the sub command for nestedmeta in val.nested.iter() { if let syn::NestedMeta::Meta(meta) = nestedmeta { if let syn::Meta::Word(ref val) = meta { - if val.to_string() == "Ignore" { + if val == "Ignore" { do_we_ignore_field = true; } } diff --git a/infrastructure/derive/src/hashable.rs b/infrastructure/derive/src/hashable.rs index 7466ce9af5..31b9e7971a 100644 --- a/infrastructure/derive/src/hashable.rs +++ b/infrastructure/derive/src/hashable.rs @@ -29,16 +29,13 @@ pub fn create_derive_hashable(input: DeriveInput) -> proc_macro2::TokenStream { let object_name = &input.ident; let mut digest = None; for attr in &input.attrs { - match attr.interpret_meta().unwrap() { - syn::Meta::NameValue(val) => { - if val.ident.to_string() == "digest" { - if let syn::Lit::Str(lit) = &val.lit { - digest = Some(lit.value()); - } + if let syn::Meta::NameValue(val) = attr.interpret_meta().unwrap() { + if val.ident == "digest" { + if let syn::Lit::Str(lit) = &val.lit { + digest = Some(lit.value()); } - }, - _ => (), - }; + } + } } let item = input.data; let fields_text = handle_fields_for_hashable(&item); @@ -70,7 +67,7 @@ fn handle_fields_for_hashable(item: &Data) -> proc_macro2::TokenStream { for attr in &f.attrs { match attr.interpret_meta().unwrap() { syn::Meta::NameValue(ref val) => { - if val.ident.to_string() == "Hashable" { + if val.ident == "Hashable" { if let syn::Lit::Str(lit) = &val.lit { if lit.value() == "Ignore" { do_we_ignore_field = true; @@ -80,12 +77,12 @@ fn handle_fields_for_hashable(item: &Data) -> proc_macro2::TokenStream { }, syn::Meta::List(ref val) => { // we have more than one property - if val.ident.to_string() == "Hashable" { + if val.ident == "Hashable" { // we have a hash command here, lets search for the sub command for nestedmeta in val.nested.iter() { if let syn::NestedMeta::Meta(meta) = nestedmeta { if let syn::Meta::Word(ref val) = meta { - if val.to_string() == "Ignore" { + if val == "Ignore" { do_we_ignore_field = true; } } diff --git a/infrastructure/merklemountainrange/src/merklemountainrange.rs b/infrastructure/merklemountainrange/src/merklemountainrange.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/infrastructure/protobuf_build/Cargo.toml b/infrastructure/protobuf_build/Cargo.toml deleted file mode 100644 index 8530f4a3a0..0000000000 --- a/infrastructure/protobuf_build/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "tari_protobuf_build" -version = "0.0.5" -authors = ["The Tari Development Community"] -description = "Protobuf build code for Tari codebase" -keywords = ["protobuf", "build"] -repository = "https://github.com/tari-project/tari" -homepage = "https://tari.com" -readme = "README.md" -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -prost-build = "0.5.0" -sha2 = "0.8.0" diff --git a/infrastructure/pubsub/Cargo.toml b/infrastructure/pubsub/Cargo.toml deleted file mode 100644 index 67f5e08473..0000000000 --- a/infrastructure/pubsub/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "tari_pubsub" -version = "0.0.5" -authors = ["The Tari development Community"] -description = "Single publisher with multiple subscribers to topic messages" -keywords = ["pubsub", "async","futures", "topic"] -repository = "https://github.com/tari-project/tari" -homepage = "https://tari.com" -readme = "README.md" -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -tari_broadcast_channel = { version="^0.0", path = "../broadcast_channel" } -futures = { version = "=0.3.0-alpha.19", package = "futures-preview", features=["async-await"] } diff --git a/infrastructure/pubsub/src/lib.rs b/infrastructure/pubsub/src/lib.rs deleted file mode 100644 index e03c52ae48..0000000000 --- a/infrastructure/pubsub/src/lib.rs +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright 2019. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use futures::{future, prelude::*, stream::Fuse}; -use std::fmt::Debug; -use tari_broadcast_channel::{bounded, Publisher, Subscriber}; - -/// The container for a message that is passed along the pub-sub channel that contains a Topic to define the type of -/// message and the message itself. -#[derive(Debug)] -pub struct TopicPayload { - topic: T, - message: M, -} - -impl TopicPayload { - pub fn new(topic: T, message: M) -> Self { - Self { topic, message } - } - - pub fn topic(&self) -> &T { - &self.topic - } - - pub fn message(&self) -> &M { - &self.message - } -} - -pub type TopicPublisher = Publisher>; -pub type TopicSubscriber = Subscriber>; - -/// This structure hold the Subscriber end of a Pub-Sub channel and can be used to create new filtered subscriber -/// channels. -pub struct TopicSubscriptionFactory { - subscriber: TopicSubscriber, -} - -impl TopicSubscriptionFactory -where - T: Eq + Send, - M: Clone + Send, -{ - pub fn new(subscriber: TopicSubscriber) -> Self { - TopicSubscriptionFactory { subscriber } - } - - /// Provide a subscriber (which will be consumed) and a topic to filter it by and this function will return a stream - /// that yields only the desired messages - pub fn get_subscription(&self, topic: T) -> impl Stream { - self.subscriber.clone().filter_map(move |item| { - let result = if item.topic() == &topic { - Some(item.message.clone()) - } else { - None - }; - future::ready(result) - }) - } - - /// Provide a fused version of the subscription stream so that domain modules don't need to know about fuse() - pub fn get_subscription_fused(&self, topic: T) -> Fuse> { - self.get_subscription(topic).fuse() - } -} - -/// Create Topic based Pub-Sub channel which returns the Publisher side of the channel and TopicSubscriptionFactory -/// which can produce multiple subscribers for provided topics. -pub fn pubsub_channel( - size: usize, -) -> (TopicPublisher, TopicSubscriptionFactory) { - let (publisher, subscriber): (TopicPublisher, TopicSubscriber) = bounded(size); - (publisher, TopicSubscriptionFactory::new(subscriber)) -} - -#[cfg(test)] -mod test { - use super::*; - use futures::executor::block_on; - - #[test] - fn topic_pub_sub() { - let (mut publisher, subscriber_factory) = pubsub_channel(10); - - #[derive(Debug, Clone)] - struct Dummy { - a: u32, - b: String, - } - - let messages = vec![ - TopicPayload::new("Topic1", Dummy { - a: 1u32, - b: "one".to_string(), - }), - TopicPayload::new("Topic2", Dummy { - a: 2u32, - b: "two".to_string(), - }), - TopicPayload::new("Topic1", Dummy { - a: 3u32, - b: "three".to_string(), - }), - TopicPayload::new("Topic2", Dummy { - a: 4u32, - b: "four".to_string(), - }), - TopicPayload::new("Topic1", Dummy { - a: 5u32, - b: "five".to_string(), - }), - TopicPayload::new("Topic2", Dummy { - a: 6u32, - b: "size".to_string(), - }), - TopicPayload::new("Topic1", Dummy { - a: 7u32, - b: "seven".to_string(), - }), - ]; - - block_on(async { - for m in messages { - publisher.send(m).await.unwrap(); - } - }); - - let mut sub1 = subscriber_factory.get_subscription("Topic1").fuse(); - - let topic1a = block_on(async { - let mut result = Vec::new(); - - loop { - futures::select!( - item = sub1.select_next_some() => result.push(item), - default => break, - ); - } - result - }); - - assert_eq!(topic1a.len(), 4); - assert_eq!(topic1a[0].a, 1); - assert_eq!(topic1a[1].a, 3); - assert_eq!(topic1a[2].a, 5); - assert_eq!(topic1a[3].a, 7); - - let messages2 = vec![ - TopicPayload::new("Topic1", Dummy { - a: 11u32, - b: "one one".to_string(), - }), - TopicPayload::new("Topic2", Dummy { - a: 22u32, - b: "two two".to_string(), - }), - TopicPayload::new("Topic1", Dummy { - a: 33u32, - b: "three three".to_string(), - }), - ]; - - block_on(async move { - stream::iter(messages2).map(Ok).forward(publisher).await.unwrap(); - }); - - let topic1b = block_on(async { sub1.collect::>().await }); - - assert_eq!(topic1b.len(), 2); - assert_eq!(topic1b[0].a, 11); - assert_eq!(topic1b[1].a, 33); - - let sub2 = subscriber_factory.get_subscription("Topic2"); - - let topic2 = block_on(async { sub2.collect::>().await }); - - assert_eq!(topic2.len(), 4); - assert_eq!(topic2[0].a, 2); - assert_eq!(topic2[1].a, 4); - assert_eq!(topic2[2].a, 6); - assert_eq!(topic2[3].a, 22); - } -} diff --git a/infrastructure/shutdown/Cargo.toml b/infrastructure/shutdown/Cargo.toml index 9b4d4adcb0..017f6f1308 100644 --- a/infrastructure/shutdown/Cargo.toml +++ b/infrastructure/shutdown/Cargo.toml @@ -6,13 +6,13 @@ repository = "https://github.com/tari-project/tari" homepage = "https://tari.com" readme = "README.md" license = "BSD-3-Clause" -version = "0.0.5" +version = "0.0.9" edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -futures = { version = "=0.3.0-alpha.19", package = "futures-preview"} +futures = "^0.3.1" [dev-dependencies] -tokio = "0.2.0-alpha.6" +tokio = {version="^0.2", features=["rt-core"]} diff --git a/infrastructure/shutdown/README.md b/infrastructure/shutdown/README.md new file mode 100644 index 0000000000..2fad08e857 --- /dev/null +++ b/infrastructure/shutdown/README.md @@ -0,0 +1,33 @@ +# A convenient shutdown signal + +`ShutdownSignal` is a convenient wrapper around a one-shot channel that allows different threads to let each other know +that they should stop working. + +## Basic usage + +First, create the shutdown signal. + + let mut shutdown = Shutdown::new(); + +Use `to_signal` to create a future which will resolve when `Shutdown` is triggered. + + let signal = shutdown.to_signal(); + assert_eq!(shutdown.is_triggered(), false); + +You can clone the signal and move it into threads that need to be informed of when to shut down. We're using tokio here, +but this will work in any futures-based runtime: + + tokio::spawn(async move { + signal.await.unwrap(); + println!("Finished"); + }); + +Then when you want to trigger the shutdown signal, call `trigger`. All signals will resolve. + + shutdown.trigger().unwrap(); // "Finished" is printed + // Shutdown::trigger is idempotent + shutdown.trigger().unwrap(); + assert_eq!(shutdown.is_triggered(), true); + +_Note_: If the ShutdownSignal instance is dropped, it will trigger the signal, so the `Shutdown` instance should be held +as long as required by the application. diff --git a/infrastructure/shutdown/src/lib.rs b/infrastructure/shutdown/src/lib.rs index ffab74630d..ab2f003a41 100644 --- a/infrastructure/shutdown/src/lib.rs +++ b/infrastructure/shutdown/src/lib.rs @@ -93,6 +93,12 @@ impl Drop for Shutdown { } } +impl Default for Shutdown { + fn default() -> Self { + Self::new() + } +} + #[cfg(test)] mod test { use super::*; @@ -115,7 +121,6 @@ mod test { // Shutdown::trigger is idempotent shutdown.trigger().unwrap(); assert_eq!(shutdown.is_triggered(), true); - rt.shutdown_on_idle(); } #[test] @@ -129,7 +134,6 @@ mod test { signal.await.unwrap(); }); shutdown.trigger().unwrap(); - rt.shutdown_on_idle(); } #[test] @@ -143,7 +147,6 @@ mod test { signal.await.unwrap(); }); drop(shutdown); - rt.shutdown_on_idle(); } #[test] @@ -161,6 +164,5 @@ mod test { }); shutdown.trigger().unwrap(); assert_eq!(spy.load(Ordering::SeqCst), true); - rt.shutdown_on_idle(); } } diff --git a/infrastructure/storage/Cargo.toml b/infrastructure/storage/Cargo.toml index c94a619ec5..b4087bfd23 100644 --- a/infrastructure/storage/Cargo.toml +++ b/infrastructure/storage/Cargo.toml @@ -6,7 +6,7 @@ repository = "https://github.com/tari-project/tari" homepage = "https://tari.com" readme = "README.md" license = "BSD-3-Clause" -version = "0.0.5" +version = "0.0.9" edition = "2018" [dependencies] @@ -18,11 +18,10 @@ rmp = "0.8.7" rmp-serde = "0.13.7" serde = "1.0.80" serde_derive = "1.0.80" -tari_utilities = { path = "../tari_util", version = "^0.0" } +tari_utilities = "^0.1" bytes = "0.4.12" [dev-dependencies] rand = "0.5.5" sys-info = "0.5.6" env_logger = "0.6.2" - diff --git a/infrastructure/storage/src/key_val_store/lmdb_database.rs b/infrastructure/storage/src/key_val_store/lmdb_database.rs index 3bab53b27f..c78d7dca93 100644 --- a/infrastructure/storage/src/key_val_store/lmdb_database.rs +++ b/infrastructure/storage/src/key_val_store/lmdb_database.rs @@ -65,7 +65,7 @@ where fn insert(&self, key: K, value: V) -> Result<(), KeyValStoreError> { self.inner .insert::(&key, &value) - .map_err(|e| KeyValStoreError::DatabaseError(e.to_string())) + .map_err(|e| KeyValStoreError::DatabaseError(format!("{:?}", e))) } /// Get the value corresponding to the provided key from the key-value database. @@ -73,14 +73,14 @@ where where for<'t> V: serde::de::DeserializeOwned { self.inner .get::(key) - .map_err(|e| KeyValStoreError::DatabaseError(e.to_string())) + .map_err(|e| KeyValStoreError::DatabaseError(format!("{:?}", e))) } /// Returns the total number of entries recorded in the key-value database. fn size(&self) -> Result { self.inner .len() - .map_err(|e| KeyValStoreError::DatabaseError(e.to_string())) + .map_err(|e| KeyValStoreError::DatabaseError(format!("{:?}", e))) } /// Iterate over all the stored records and execute the function `f` for each pair in the key-value database. @@ -88,21 +88,21 @@ where where F: FnMut(Result<(K, V), KeyValStoreError>) -> IterationResult { self.inner .for_each::(f) - .map_err(|e| KeyValStoreError::DatabaseError(e.to_string())) + .map_err(|e| KeyValStoreError::DatabaseError(format!("{:?}", e))) } /// Checks whether a record exist in the key-value database that corresponds to the provided `key`. fn exists(&self, key: &K) -> Result { self.inner .contains_key::(key) - .map_err(|e| KeyValStoreError::DatabaseError(e.to_string())) + .map_err(|e| KeyValStoreError::DatabaseError(format!("{:?}", e))) } /// Remove the record from the key-value database that corresponds with the provided `key`. fn delete(&self, key: &K) -> Result<(), KeyValStoreError> { self.inner .remove::(key) - .map_err(|e| KeyValStoreError::DatabaseError(e.to_string())) + .map_err(|e| KeyValStoreError::DatabaseError(format!("{:?}", e))) } } @@ -138,68 +138,69 @@ mod test { #[test] fn test_lmdb_kvstore() { let database_name = "test_lmdb_kvstore"; // Note: every test should have unique database - let datastore = init_datastore(database_name).unwrap(); - let db = datastore.get_handle(database_name).unwrap(); - let db = LMDBWrapper::new(Arc::new(db)); - #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] - struct Foo { - value: String, - } - let key1 = 1 as u64; - let key2 = 2 as u64; - let key3 = 3 as u64; - let key4 = 4 as u64; - let val1 = Foo { - value: "one".to_string(), - }; - let val2 = Foo { - value: "two".to_string(), - }; - let val3 = Foo { - value: "three".to_string(), - }; - db.insert(1, val1.clone()).unwrap(); - db.insert(2, val2.clone()).unwrap(); - db.insert(3, val3.clone()).unwrap(); - - assert_eq!(db.get(&1).unwrap().unwrap(), val1); - assert_eq!(db.get(&2).unwrap().unwrap(), val2); - assert_eq!(db.get(&3).unwrap().unwrap(), val3); - assert!(db.get(&4).unwrap().is_none()); - assert_eq!(db.size().unwrap(), 3); - assert!(db.exists(&key1).unwrap()); - assert!(db.exists(&key2).unwrap()); - assert!(db.exists(&key3).unwrap()); - assert!(!db.exists(&key4).unwrap()); - - db.delete(&key2).unwrap(); - assert_eq!(db.get(&key1).unwrap().unwrap(), val1); - assert!(db.get(&key2).unwrap().is_none()); - assert_eq!(db.get(&key3).unwrap().unwrap(), val3); - assert!(db.get(&key4).unwrap().is_none()); - assert_eq!(db.size().unwrap(), 2); - assert!(db.exists(&key1).unwrap()); - assert!(!db.exists(&key2).unwrap()); - assert!(db.exists(&key3).unwrap()); - assert!(!db.exists(&key4).unwrap()); - - // Only Key1 and Key3 should be in key-value database, but order is not known - let mut key1_found = false; - let mut key3_found = false; - let _res = db.for_each(|pair| { - let (key, val) = pair.unwrap(); - if key == key1 { - key1_found = true; - assert_eq!(val, val1); - } else if key == key3 { - key3_found = true; - assert_eq!(val, val3); + { + let datastore = init_datastore(database_name).unwrap(); + let db = datastore.get_handle(database_name).unwrap(); + let db = LMDBWrapper::new(Arc::new(db)); + #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] + struct Foo { + value: String, } - IterationResult::Continue - }); - assert!(key1_found); - assert!(key3_found); - - clean_up_datastore(database_name); + let key1 = 1 as u64; + let key2 = 2 as u64; + let key3 = 3 as u64; + let key4 = 4 as u64; + let val1 = Foo { + value: "one".to_string(), + }; + let val2 = Foo { + value: "two".to_string(), + }; + let val3 = Foo { + value: "three".to_string(), + }; + db.insert(1, val1.clone()).unwrap(); + db.insert(2, val2.clone()).unwrap(); + db.insert(3, val3.clone()).unwrap(); + + assert_eq!(db.get(&1).unwrap().unwrap(), val1); + assert_eq!(db.get(&2).unwrap().unwrap(), val2); + assert_eq!(db.get(&3).unwrap().unwrap(), val3); + assert!(db.get(&4).unwrap().is_none()); + assert_eq!(db.size().unwrap(), 3); + assert!(db.exists(&key1).unwrap()); + assert!(db.exists(&key2).unwrap()); + assert!(db.exists(&key3).unwrap()); + assert!(!db.exists(&key4).unwrap()); + + db.delete(&key2).unwrap(); + assert_eq!(db.get(&key1).unwrap().unwrap(), val1); + assert!(db.get(&key2).unwrap().is_none()); + assert_eq!(db.get(&key3).unwrap().unwrap(), val3); + assert!(db.get(&key4).unwrap().is_none()); + assert_eq!(db.size().unwrap(), 2); + assert!(db.exists(&key1).unwrap()); + assert!(!db.exists(&key2).unwrap()); + assert!(db.exists(&key3).unwrap()); + assert!(!db.exists(&key4).unwrap()); + + // Only Key1 and Key3 should be in key-value database, but order is not known + let mut key1_found = false; + let mut key3_found = false; + let _res = db.for_each(|pair| { + let (key, val) = pair.unwrap(); + if key == key1 { + key1_found = true; + assert_eq!(val, val1); + } else if key == key3 { + key3_found = true; + assert_eq!(val, val3); + } + IterationResult::Continue + }); + assert!(key1_found); + assert!(key3_found); + } + clean_up_datastore(database_name); // In Windows file handles must be released before files can be deleted } } diff --git a/infrastructure/storage/src/lmdb_store/store.rs b/infrastructure/storage/src/lmdb_store/store.rs index cfff6b8028..23799673c0 100644 --- a/infrastructure/storage/src/lmdb_store/store.rs +++ b/infrastructure/storage/src/lmdb_store/store.rs @@ -125,7 +125,9 @@ impl LMDBBuilder { let mut builder = EnvBuilder::new()?; builder.set_mapsize(self.db_size_mb * 1024 * 1024)?; builder.set_maxdbs(max_dbs)?; - builder.open(&self.path, open::Flags::empty(), 0o600)? + // Using open::Flags::NOTLS does not compile!?! NOTLS=0x200000 + let flags = open::Flags::from_bits(0x200000).expect("LMDB open::Flag is correct"); + builder.open(&self.path, flags, 0o600)? }; let env = Arc::new(env); info!( diff --git a/infrastructure/storage/tests/lmdb.rs b/infrastructure/storage/tests/lmdb.rs index f654f07ab0..17efd1f284 100644 --- a/infrastructure/storage/tests/lmdb.rs +++ b/infrastructure/storage/tests/lmdb.rs @@ -94,11 +94,16 @@ fn clean_up(name: &str) { std::fs::remove_dir_all(get_path(name)).unwrap(); } +#[cfg(windows)] +const LINE_ENDING: &str = "\r\n"; +#[cfg(not(windows))] +const LINE_ENDING: &str = "\n"; + fn load_users() -> Vec { let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); path.push("tests/users.csv"); let f = std::fs::read_to_string(path).unwrap(); - f.split('\n').map(|s| User::new(s).unwrap()).collect() + f.split(LINE_ENDING).map(|s| User::new(s).unwrap()).collect() } fn insert_all_users(name: &str) -> (Vec, LMDBDatabase) { @@ -117,136 +122,150 @@ fn insert_all_users(name: &str) -> (Vec, LMDBDatabase) { #[test] fn single_thread() { - let users = load_users(); - let env = init("single_thread").unwrap(); - let db = env.get_handle("users").unwrap(); - for user in &users { - db.insert(&user.id, &user).unwrap(); - } - for user in users.iter() { - let check: User = db.get(&user.id).unwrap().unwrap(); - assert_eq!(check, *user); + { + let users = load_users(); + let env = init("single_thread").unwrap(); + let db = env.get_handle("users").unwrap(); + for user in &users { + db.insert(&user.id, &user).unwrap(); + } + for user in users.iter() { + let check: User = db.get(&user.id).unwrap().unwrap(); + assert_eq!(check, *user); + } + assert_eq!(db.len().unwrap(), 1000); } - assert_eq!(db.len().unwrap(), 1000); - clean_up("single_thread"); + clean_up("single_thread"); // In Windows file handles must be released before files can be deleted } #[test] fn multi_thread() { - let users_arc = Arc::new(load_users()); - let env = init("multi_thread").unwrap(); - let mut threads = Vec::new(); - for i in 0..10 { - let db = env.get_handle("users").unwrap(); - let users = users_arc.clone(); - threads.push(thread::spawn(move || { - for j in 0..100 { - let user = &users[i * 100 + j]; - db.insert(&user.id, user).unwrap(); - } - })); - } + { + let users_arc = Arc::new(load_users()); + let env = init("multi_thread").unwrap(); + let mut threads = Vec::new(); + for i in 0..10 { + let db = env.get_handle("users").unwrap(); + let users = users_arc.clone(); + threads.push(thread::spawn(move || { + for j in 0..100 { + let user = &users[i * 100 + j]; + db.insert(&user.id, user).unwrap(); + } + })); + } - for thread in threads { - thread.join().unwrap(); - } + for thread in threads { + thread.join().unwrap(); + } - env.log_info(); - let db = env.get_handle("users").unwrap(); - for user in users_arc.iter() { - let check: User = db.get(&user.id).unwrap().unwrap(); - assert_eq!(check, *user); + env.log_info(); + let db = env.get_handle("users").unwrap(); + for user in users_arc.iter() { + let check: User = db.get(&user.id).unwrap().unwrap(); + assert_eq!(check, *user); + } } - clean_up("multi_thread"); + clean_up("multi_thread"); // In Windows file handles must be released before files can be deleted } #[test] fn transactions() { - let (users, db) = insert_all_users("transactions"); - // Test the `exists` and value retrieval functions - let res = db.with_read_transaction::<_, User>(|txn| { - for user in users.iter() { - assert!(txn.exists(&user.id).unwrap()); - let check: User = txn.get(&user.id).unwrap().unwrap(); - assert_eq!(check, *user); - } - Ok(None) - }); - assert!(res.unwrap().is_none()); - clean_up("transactions"); + { + let (users, db) = insert_all_users("transactions"); + // Test the `exists` and value retrieval functions + let res = db.with_read_transaction::<_, User>(|txn| { + for user in users.iter() { + assert!(txn.exists(&user.id).unwrap()); + let check: User = txn.get(&user.id).unwrap().unwrap(); + assert_eq!(check, *user); + } + Ok(None) + }); + println!("{:?}", res); + assert!(res.unwrap().is_none()); + } + clean_up("transactions"); // In Windows file handles must be released before files can be deleted } /// Simultaneous writes in different threads #[test] fn multi_thread_writes() { - let env = init("multi-thread-writes").unwrap(); - let mut threads = Vec::new(); - for _ in 0..2 { + { + let env = init("multi-thread-writes").unwrap(); + let mut threads = Vec::new(); + for _ in 0..2 { + let db = env.get_handle("users").unwrap(); + threads.push(thread::spawn(move || { + let res = db.with_write_transaction(|mut txn| { + for j in 0..1000 { + txn.insert(&j, &j)?; + } + Ok(()) + }); + assert!(res.is_ok()); + })); + } + for thread in threads { + thread.join().unwrap() + } + env.log_info(); + let db = env.get_handle("users").unwrap(); - threads.push(thread::spawn(move || { + + assert_eq!(db.len().unwrap(), 1000); + for i in 0..1000 { + let value: i32 = db.get(&i).unwrap().unwrap(); + assert_eq!(i, value); + } + } + clean_up("multi-thread-writes"); // In Windows file handles must be released before files can be deleted +} + +/// Multiple write transactions in a single thread +#[test] +fn multi_writes() { + { + let env = init("multi-writes").unwrap(); + for i in 0..2 { + let db = env.get_handle("users").unwrap(); let res = db.with_write_transaction(|mut txn| { for j in 0..1000 { - txn.insert(&j, &j)?; + let v = i * 1000 + j; + txn.insert(&v, &v)?; } + db.log_info(); Ok(()) }); assert!(res.is_ok()); - })); - } - for thread in threads { - thread.join().unwrap() - } - env.log_info(); - - let db = env.get_handle("users").unwrap(); - - assert_eq!(db.len().unwrap(), 1000); - for i in 0..1000 { - let value: i32 = db.get(&i).unwrap().unwrap(); - assert_eq!(i, value); + } + env.flush().unwrap(); } - - clean_up("multi-thread-writes"); + clean_up("multi-writes"); // In Windows file handles must be released before files can be deleted } -/// Multiple write transactions in a single thread #[test] -fn multi_writes() { - let env = init("multi-writes").unwrap(); - for i in 0..2 { - let db = env.get_handle("users").unwrap(); - let res = db.with_write_transaction(|mut txn| { - for j in 0..1000 { - let v = i * 1000 + j; - txn.insert(&v, &v)?; - } - db.log_info(); - Ok(()) +fn pair_iterator() { + { + let (users, db) = insert_all_users("pair_iterator"); + let res = db.for_each::(|pair| { + let (key, user) = pair.unwrap(); + assert_eq!(user.id, key); + assert_eq!(users[key as usize - 1], user); + IterationResult::Continue }); assert!(res.is_ok()); } - env.flush().unwrap(); - clean_up("multi-writes"); -} - -#[test] -fn pair_iterator() { - let (users, db) = insert_all_users("pair_iterator"); - let res = db.for_each::(|pair| { - let (key, user) = pair.unwrap(); - assert_eq!(user.id, key); - assert_eq!(users[key as usize - 1], user); - IterationResult::Continue - }); - assert!(res.is_ok()); - clean_up("pair_iterator"); + clean_up("pair_iterator"); // In Windows file handles must be released before files can be deleted } #[test] fn exists_and_delete() { - let (_, db) = insert_all_users("delete"); - assert!(db.contains_key(&525u64).unwrap()); - db.remove(&525u64).unwrap(); - assert_eq!(db.contains_key(&525u64).unwrap(), false); - clean_up("delete"); + { + let (_, db) = insert_all_users("delete"); + assert!(db.contains_key(&525u64).unwrap()); + db.remove(&525u64).unwrap(); + assert_eq!(db.contains_key(&525u64).unwrap(), false); + } + clean_up("delete"); // In Windows file handles must be released before files can be deleted } diff --git a/infrastructure/tari_util/Cargo.toml b/infrastructure/tari_util/Cargo.toml deleted file mode 100644 index e4d5429205..0000000000 --- a/infrastructure/tari_util/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "tari_utilities" -description = "A set of useful and commonly used utilities that are used in several places in the Tari project." -authors = ["The Tari Development Community"] -repository = "https://github.com/tari-project/tari" -homepage = "https://tari.com" -readme = "README.md" -license = "BSD-3-Clause" -version = "0.0.6" -edition = "2018" - -[dependencies] -derive-error = "0.0.4" -clear_on_drop = "0.2.3" -chrono = { version = "0.4.9", features = ["serde"]} -bincode = "1.1.4" -base64 = "0.10.1" -serde_json = "1.0" -serde = {version = "1.0.102", features = ["derive"] } -rand = "0.5.5" -newtype-ops = "0.1.4" -bitflags = "1.2.1" - -[dev-dependencies] diff --git a/infrastructure/tari_util/README.md b/infrastructure/tari_util/README.md deleted file mode 100644 index 01dbd63501..0000000000 --- a/infrastructure/tari_util/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Tari Utilities - -This crate is part of the [Tari Cryptocurrency](https://tari.com) project. - -A set of useful and commonly used utilities that are used in several places in the Tari project. - -## bit - -integer to bit array functions - -## byte_array - -A trait that offers representation of datatypes as a byte array or hex string - -## hash - -Common hash functions used in Tari - -## hex - -Binary <-> Hex string conversions \ No newline at end of file diff --git a/infrastructure/tari_util/src/bit.rs b/infrastructure/tari_util/src/bit.rs deleted file mode 100644 index 74ff72fc03..0000000000 --- a/infrastructure/tari_util/src/bit.rs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -/// Converts a single input byte to 8 bits (little-endian) -pub fn byte_to_bits(value: u8) -> [bool; 8] { - let mut bits = [false; 8]; - for i in 0..8 { - bits[i] = value & (1 << i) != 0; - } - (bits) -} - -/// Converts a single input integer to a vector of bits (little-endian) -pub fn uint_to_bits(value: usize, bit_count: usize) -> Vec { - let mut bits: Vec = vec![false; bit_count]; - for i in 0..bit_count { - bits[i] = value & (1 << i) != 0; - } - (bits) -} - -/// Converts a array of input bits (little-endian) to a single byte -pub fn bits_to_byte(bits: [bool; 8]) -> u8 { - let mut value: u8 = 0; - for i in 0..8 { - value |= (bits[i] as u8) << i; - } - (value) -} - -/// Converts a vector of input bits (little-endian) to its integer representation -pub fn bits_to_uint(bits: &[bool]) -> usize { - let mut value: usize = 0; - for i in 0..bits.len() { - value |= (bits[i] as usize) << i; - } - (value) -} - -/// Converts a vector of input bytes to a vector of bits -pub fn bytes_to_bits(bytes: &[u8]) -> Vec { - let mut bits: Vec = vec![false; bytes.len() * 8]; - for i in 0..bytes.len() { - let bit_index = i * 8; - bits[bit_index..(bit_index + 8)].copy_from_slice(&byte_to_bits(bytes[i])); - } - (bits) -} - -/// Converts a vector of bits to a vector of bytes -pub fn bits_to_bytes(bits: &[bool]) -> Vec { - let mut bytes: Vec = vec![0; bits.len() / 8]; - let mut curr_bits: [bool; 8] = [false; 8]; - for i in 0..bytes.len() { - let byte_index = i * 8; - curr_bits.copy_from_slice(&bits[byte_index..(byte_index + 8)]); - bytes[i] = bits_to_byte(curr_bits) as u8; - } - (bytes) -} diff --git a/infrastructure/tari_util/src/byte_array.rs b/infrastructure/tari_util/src/byte_array.rs deleted file mode 100644 index f27015cea0..0000000000 --- a/infrastructure/tari_util/src/byte_array.rs +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::hex::{from_hex, to_hex, Hex, HexError}; -use derive_error::Error; - -#[derive(Debug, Error, PartialEq)] -pub enum ByteArrayError { - // Could not create a ByteArray when converting from a different format - #[error(msg_embedded, non_std, no_from)] - ConversionError(String), - // The input data was the incorrect length to perform the desired conversion - IncorrectLength, -} - -/// Many of the types in this crate are just large numbers (256 bit usually). This trait provides the common -/// functionality for types like secret keys, signatures, commitments etc. to be converted to and from byte arrays -/// and hexadecimal formats. -#[allow(clippy::ptr_arg)] -pub trait ByteArray: Sized { - /// Return the type as a byte vector - fn to_vec(&self) -> Vec { - self.as_bytes().to_vec() - } - - /// Try and convert the given byte vector to the implemented type. Any failures (incorrect string length etc) - /// return a [KeyError](enum.KeyError.html) with an explanatory note. - fn from_vec(v: &Vec) -> Result { - Self::from_bytes(v.as_slice()) - } - - /// Try and convert the given byte array to the implemented type. Any failures (incorrect array length, - /// implementation-specific checks, etc) return a [ByteArrayError](enum.ByteArrayError.html). - fn from_bytes(bytes: &[u8]) -> Result; - - /// Return the type as a byte array - fn as_bytes(&self) -> &[u8]; -} - -impl ByteArray for Vec { - fn to_vec(&self) -> Vec { - self.clone() - } - - fn from_vec(v: &Vec) -> Result { - Ok(v.clone()) - } - - fn from_bytes(bytes: &[u8]) -> Result { - Ok(bytes.to_vec()) - } - - fn as_bytes(&self) -> &[u8] { - Vec::as_slice(self) - } -} - -impl ByteArray for [u8; 32] { - fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != 32 { - return Err(ByteArrayError::IncorrectLength); - } - let mut a = [0u8; 32]; - a.copy_from_slice(bytes); - Ok(a) - } - - fn as_bytes(&self) -> &[u8] { - self - } -} - -impl Hex for T { - type T = T; - - fn from_hex(hex: &str) -> Result { - let v = from_hex(hex)?; - Self::from_vec(&v).map_err(|_| HexError::HexConversionError) - } - - fn to_hex(&self) -> String { - to_hex(&self.to_vec()) - } -} diff --git a/infrastructure/tari_util/src/ciphers/chacha20.rs b/infrastructure/tari_util/src/ciphers/chacha20.rs deleted file mode 100644 index 7d96adc344..0000000000 --- a/infrastructure/tari_util/src/ciphers/chacha20.rs +++ /dev/null @@ -1,497 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::{ - ciphers::cipher::{Cipher, CipherError}, - ByteArray, -}; -use clear_on_drop::clear::Clear; -use rand::{OsRng, RngCore}; -/// This in an implementation of the ChaCha20 stream cipher developed using the Internet Research Task Force (IRTF) 8439 RFC (https://tools.ietf.org/html/rfc8439) -/// ChaCha20 is a high-speed cipher proposed by D. Bernstein that is not sensitive to timing attacks (http://cr.yp.to/chacha/chacha-20080128.pdf). -/// Data used in the unit tests were derived from the examples from the IRTF 8439 RFC -use std::num::Wrapping; - -pub struct ChaCha20; - -impl ChaCha20 { - /// Perform a single chacha quarter round at the provided indices in the state - fn quarter_round(state: &mut [u32; 16], a_index: usize, b_index: usize, c_index: usize, d_index: usize) { - let mut a = Wrapping(state[a_index]); - let mut b = Wrapping(state[b_index]); - let mut c = Wrapping(state[c_index]); - let mut d = Wrapping(state[d_index]); - a += b; - d = Wrapping((d ^ a).0.rotate_left(16)); - c += d; - b = Wrapping((b ^ c).0.rotate_left(12)); - a += b; - d = Wrapping((d ^ a).0.rotate_left(8)); - c += d; - b = Wrapping((b ^ c).0.rotate_left(7)); - state[a_index] = a.0; - state[b_index] = b.0; - state[c_index] = c.0; - state[d_index] = d.0; - } - - /// Construct a chacha block by performing a number of column and diagonal quarter round operations - fn chacha20_block(state: &[u32; 16]) -> [u32; 16] { - let mut working_state = *state; - for _iter in 0..10 { - // 20 total => odd and even round performed for every iteration - // Odd round - Self::quarter_round(&mut working_state, 0, 4, 8, 12); - Self::quarter_round(&mut working_state, 1, 5, 9, 13); - Self::quarter_round(&mut working_state, 2, 6, 10, 14); - Self::quarter_round(&mut working_state, 3, 7, 11, 15); - // Even round - Self::quarter_round(&mut working_state, 0, 5, 10, 15); - Self::quarter_round(&mut working_state, 1, 6, 11, 12); - Self::quarter_round(&mut working_state, 2, 7, 8, 13); - Self::quarter_round(&mut working_state, 3, 4, 9, 14); - } - let mut output: [u32; 16] = [0; 16]; - for i in 0..output.len() { - output[i] = (Wrapping(working_state[i]) + Wrapping(state[i])).0; - } - (output) - } - - /// Construct an initial state from a 128-bit constant, 256-bit key, 96-bit nonce and a 32-bit block counter - #[allow(clippy::needless_range_loop)] - fn construct_state(key: &[u8; 32], nonce: &[u8; 12], counter: u32) -> [u32; 16] { - let constant: [u8; 16] = [101, 120, 112, 97, 110, 100, 32, 51, 50, 45, 98, 121, 116, 101, 32, 107]; // 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 - let mut state_bytes = constant.to_vec(); // 128 bit - state_bytes.extend_from_slice(key); // 256-bit - state_bytes.extend_from_slice(&counter.to_ne_bytes()); // 32-bit - state_bytes.extend_from_slice(nonce); // 96-bit - - // Convert [u8;64] to [u32;16] - let mut curr_bytes: [u8; 4] = [0; 4]; - let mut state: [u32; 16] = [0; 16]; - for i in 0..state.len() { - let byte_index = i * 4; - curr_bytes.copy_from_slice(&state_bytes[byte_index..(byte_index + 4)]); - state[i] = u32::from_le_bytes(curr_bytes); - } - state_bytes.clear(); - (state) - } - - /// Generate a keystream consisting of a number of chacha blocks - fn chacha20_cipher_keystream(key: &[u8; 32], nonce: &[u8; 12], block_count: usize) -> Vec { - const BYTES_PER_BLOCK: usize = 64; - let block_byte_count = block_count * BYTES_PER_BLOCK; - let mut cipher_bytes: Vec = Vec::with_capacity(block_byte_count as usize); - for counter in 1..=block_count as u32 { - let mut state = Self::construct_state(key, nonce, counter); - let cipher_block = Self::chacha20_block(&state); - state.clear(); - // convert cipher block to bytes - let mut block_bytes: Vec = Vec::with_capacity(BYTES_PER_BLOCK); - for block in cipher_block.iter() { - block_bytes.append(&mut block.to_ne_bytes().to_vec()) - } - cipher_bytes.append(&mut block_bytes) - } - (cipher_bytes) - } - - /// Encode the provided input bytes using a chacha20 keystream - fn encode_with_nonce(bytes: &[u8], key: &[u8; 32], nonce: &[u8; 12]) -> Vec { - const BYTES_PER_BLOCK: usize = 64; - let block_count = (bytes.len() as f64 / BYTES_PER_BLOCK as f64).ceil() as usize; - let cipher_bytes = Self::chacha20_cipher_keystream(key, nonce, block_count); - let mut encoded_bytes = Vec::with_capacity(bytes.len()); - for i in 0..bytes.len() { - encoded_bytes.push(cipher_bytes[i] ^ bytes[i]); - } - (encoded_bytes) - } - - /// Decode the provided input bytes using a chacha20 keystream - fn decode_with_nonce(bytes: &[u8], key: &[u8; 32], nonce: &[u8; 12]) -> Vec { - (Self::encode_with_nonce(bytes, &key, &nonce)) - } -} - -impl Cipher for ChaCha20 -where D: ByteArray -{ - fn seal(plain_text: &D, key: &[u8], nonce: &[u8]) -> Result, CipherError> { - // Validation - if key.len() != 32 { - return Err(CipherError::KeyLengthError); - } - if nonce.len() != 12 { - return Err(CipherError::NonceLengthError); - } - if plain_text.as_bytes().is_empty() { - return Err(CipherError::NoDataError); - } - - let mut sized_key = [0; 32]; - sized_key.copy_from_slice(key); - - let mut sized_nonce = [0; 12]; - sized_nonce.copy_from_slice(nonce); - - let cipher_text = ChaCha20::encode_with_nonce(plain_text.as_bytes(), &sized_key, &sized_nonce); - // Clear copied private data - sized_key.clear(); - sized_nonce.clear(); - - Ok(cipher_text) - } - - fn open(cipher_text: &[u8], key: &[u8], nonce: &[u8]) -> Result { - // Validation - if key.len() != 32 { - return Err(CipherError::KeyLengthError); - } - if nonce.len() != 12 { - return Err(CipherError::NonceLengthError); - } - if cipher_text.is_empty() { - return Err(CipherError::NoDataError); - } - - let mut sized_key = [0; 32]; - sized_key.copy_from_slice(key); - - let mut sized_nonce = [0; 12]; - sized_nonce.copy_from_slice(nonce); - - let plain_text = ChaCha20::decode_with_nonce(cipher_text, &sized_key, &sized_nonce); - // Clear copied private data - sized_key.clear(); - sized_nonce.clear(); - - Ok(D::from_vec(&plain_text)?) - } - - fn seal_with_integral_nonce(plain_text: &D, key: &[u8]) -> Result, CipherError> { - // Validation - if key.len() != 32 { - return Err(CipherError::KeyLengthError); - } - if plain_text.as_bytes().is_empty() { - return Err(CipherError::NoDataError); - } - - let mut sized_key = [0; 32]; - sized_key.copy_from_slice(key); - - let mut rng = OsRng::new().unwrap(); - let mut nonce = [0u8; 12]; - rng.fill_bytes(&mut nonce); - - let cipher_text = ChaCha20::encode_with_nonce(plain_text.as_bytes(), &sized_key, &nonce); - let mut nonce_with_cipher_text: Vec = nonce.to_vec(); - nonce_with_cipher_text.extend(cipher_text); - - // Clear copied private data - sized_key.clear(); - nonce.clear(); - - Ok(nonce_with_cipher_text) - } - - fn open_with_integral_nonce(cipher_text: &[u8], key: &[u8]) -> Result { - // Validation - if key.len() != 32 { - return Err(CipherError::KeyLengthError); - } - // If the cipher text is shorter than the required nonce length then the nonce is not properly included - if cipher_text.len() < 12 { - return Err(CipherError::NonceLengthError); - } else if cipher_text.len() < 13 { - return Err(CipherError::NoDataError); - } - - let mut sized_key = [0; 32]; - sized_key.copy_from_slice(key); - - let mut nonce = [0u8; 12]; - nonce.copy_from_slice(&cipher_text[0..12]); - - let plain_text = ChaCha20::decode_with_nonce(&cipher_text[12..], &sized_key, &nonce); - // Clear copied private data - sized_key.clear(); - nonce.clear(); - - Ok(D::from_vec(&plain_text)?) - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_quarter_round() { - // Partial state quarter round update - let mut state: [u32; 16] = [0; 16]; - state[0] = 286_331_153; - state[1] = 16_909_060; - state[2] = 2_609_737_539; - state[3] = 19_088_743; - ChaCha20::quarter_round(&mut state, 0, 1, 2, 3); - assert_eq!(state[0], 3_928_658_676); - assert_eq!(state[1], 3_407_673_550); - assert_eq!(state[2], 1_166_100_270); - assert_eq!(state[3], 1_484_899_515); - // Full state quarter round update - let mut state: [u32; 16] = [ - 2_274_701_792, - 3_320_640_381, - 1_365_533_105, - 3_383_111_562, - 1_153_568_499, - 865_120_127, - 3_657_197_835, - 710_897_996, - 1_396_123_495, - 2_953_467_441, - 2_538_361_882, - 899_586_403, - 1_553_404_001, - 1_029_904_009, - 546_888_150, - 2_447_102_752, - ]; - let desired_state: [u32; 16] = [ - 2_274_701_792, - 3_320_640_381, - 3_182_986_972, - 3_383_111_562, - 1_153_568_499, - 865_120_127, - 3_657_197_835, - 3_484_200_914, - 3_832_277_632, - 2_953_467_441, - 2_538_361_882, - 899_586_403, - 1_553_404_001, - 3_435_166_841, - 546_888_150, - 2_447_102_752, - ]; - ChaCha20::quarter_round(&mut state, 2, 7, 8, 13); - assert_eq!(state, desired_state); - } - - #[test] - fn test_init_state() { - let key: [u8; 32] = [ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, - 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - ]; - let nonce: [u8; 12] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x00]; - let counter: u32 = 1; - let state = ChaCha20::construct_state(&key, &nonce, counter); - let desired_state: [u32; 16] = [ - 1_634_760_805, - 857_760_878, - 2_036_477_234, - 1_797_285_236, - 50_462_976, - 117_835_012, - 185_207_048, - 252_579_084, - 319_951_120, - 387_323_156, - 454_695_192, - 522_067_228, - 1, - 0, - 1_241_513_984, - 0, - ]; - assert_eq!(state, desired_state); - } - - #[test] - fn test_chacha20_block() { - let state: [u32; 16] = [ - 1_634_760_805, - 857_760_878, - 2_036_477_234, - 1_797_285_236, - 50_462_976, - 117_835_012, - 185_207_048, - 252_579_084, - 319_951_120, - 387_323_156, - 454_695_192, - 522_067_228, - 1, - 150_994_944, - 1_241_513_984, - 0, - ]; - let block_state = ChaCha20::chacha20_block(&state); - let desired_block_state: [u32; 16] = [ - 3_840_405_776, - 358_169_553, - 534_581_072, - 3_295_748_259, - 3_354_710_471, - 57_196_595, - 2_594_841_092, - 1_315_755_203, - 1_180_992_210, - 162_176_775, - 98_026_004, - 2_718_075_865, - 3_516_666_549, - 3_108_902_622, - 3_900_952_779, - 1_312_575_650, - ]; - assert_eq!(block_state, desired_block_state); - } - - #[test] - fn test_chacha20_cipher_keystream() { - let key: [u8; 32] = [ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, - 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - ]; - let nonce: [u8; 12] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x00]; - let block_count: usize = 1; - let desired_cipher_bytes: Vec = vec![ - 0x22, 0x4f, 0x51, 0xf3, 0x40, 0x1b, 0xd9, 0xe1, 0x2f, 0xde, 0x27, 0x6f, 0xb8, 0x63, 0x1d, 0xed, 0x8c, 0x13, - 0x1f, 0x82, 0x3d, 0x2c, 0x06, 0xe2, 0x7e, 0x4f, 0xca, 0xec, 0x9e, 0xf3, 0xcf, 0x78, 0x8a, 0x3b, 0x0a, 0xa3, - 0x72, 0x60, 0x0a, 0x92, 0xb5, 0x79, 0x74, 0xcd, 0xed, 0x2b, 0x93, 0x34, 0x79, 0x4c, 0xba, 0x40, 0xc6, 0x3e, - 0x34, 0xcd, 0xea, 0x21, 0x2c, 0x4c, 0xf0, 0x7d, 0x41, 0xb7, - ]; - let cipher_bytes = ChaCha20::chacha20_cipher_keystream(&key, &nonce, block_count); - assert_eq!(cipher_bytes, desired_cipher_bytes); - } - - #[test] - fn test_encode_and_decode() { - let key: [u8; 32] = [ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, - 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - ]; - - let nonce: [u8; 12] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x00]; - // Text: "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen - // would be it." - // The test data bytes are from IRTF 8439 RFC - let data_bytes: Vec = vec![ - 0x4c, 0x61, 0x64, 0x69, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x47, 0x65, 0x6e, 0x74, 0x6c, 0x65, 0x6d, - 0x65, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x6f, 0x66, - 0x20, 0x27, 0x39, 0x39, 0x3a, 0x20, 0x49, 0x66, 0x20, 0x49, 0x20, 0x63, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6f, - 0x66, 0x66, 0x65, 0x72, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x6f, 0x6e, 0x65, 0x20, - 0x74, 0x69, 0x70, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x75, 0x74, 0x75, 0x72, 0x65, - 0x2c, 0x20, 0x73, 0x75, 0x6e, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20, - 0x62, 0x65, 0x20, 0x69, 0x74, 0x2e, - ]; - // Encode - let encoded_bytes = ChaCha20::encode_with_nonce(&data_bytes, &key, &nonce); - let desired_encoded_bytes: Vec = vec![ - 0x6e, 0x2e, 0x35, 0x9a, 0x25, 0x68, 0xf9, 0x80, 0x41, 0xba, 0x07, 0x28, 0xdd, 0x0d, 0x69, 0x81, 0xe9, 0x7e, - 0x7a, 0xec, 0x1d, 0x43, 0x60, 0xc2, 0x0a, 0x27, 0xaf, 0xcc, 0xfd, 0x9f, 0xae, 0x0b, 0xf9, 0x1b, 0x65, 0xc5, - 0x52, 0x47, 0x33, 0xab, 0x8f, 0x59, 0x3d, 0xab, 0xcd, 0x62, 0xb3, 0x57, 0x16, 0x39, 0xd6, 0x24, 0xe6, 0x51, - 0x52, 0xab, 0x8f, 0x53, 0x0c, 0x35, 0x9f, 0x08, 0x61, 0xd8, 0x07, 0xca, 0x0d, 0xbf, 0x50, 0x0d, 0x6a, 0x61, - 0x56, 0xa3, 0x8e, 0x08, 0x8a, 0x22, 0xb6, 0x5e, 0x52, 0xbc, 0x51, 0x4d, 0x16, 0xcc, 0xf8, 0x06, 0x81, 0x8c, - 0xe9, 0x1a, 0xb7, 0x79, 0x37, 0x36, 0x5a, 0xf9, 0x0b, 0xbf, 0x74, 0xa3, 0x5b, 0xe6, 0xb4, 0x0b, 0x8e, 0xed, - 0xf2, 0x78, 0x5e, 0x42, 0x87, 0x4d, - ]; - assert_eq!(encoded_bytes, desired_encoded_bytes); - // Decode - let decoded_bytes = ChaCha20::decode_with_nonce(&encoded_bytes, &key, &nonce); - assert_eq!(decoded_bytes, data_bytes); - } - - #[test] - fn test_cipher_trait() { - let key: Vec = vec![ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, - 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - ]; - - let nonce: Vec = vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x00]; - // Text: "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen - // would be it." The test data bytes are from IRTF 8439 RFC - let data_bytes: Vec = vec![ - 0x4c, 0x61, 0x64, 0x69, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x47, 0x65, 0x6e, 0x74, 0x6c, 0x65, 0x6d, - 0x65, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x6f, 0x66, - 0x20, 0x27, 0x39, 0x39, 0x3a, 0x20, 0x49, 0x66, 0x20, 0x49, 0x20, 0x63, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6f, - 0x66, 0x66, 0x65, 0x72, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x6f, 0x6e, 0x65, 0x20, - 0x74, 0x69, 0x70, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x75, 0x74, 0x75, 0x72, 0x65, - 0x2c, 0x20, 0x73, 0x75, 0x6e, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x77, 0x6f, 0x75, 0x6c, 0x64, 0x20, - 0x62, 0x65, 0x20, 0x69, 0x74, 0x2e, - ]; - - let desired_encoded_bytes: Vec = vec![ - 0x6e, 0x2e, 0x35, 0x9a, 0x25, 0x68, 0xf9, 0x80, 0x41, 0xba, 0x07, 0x28, 0xdd, 0x0d, 0x69, 0x81, 0xe9, 0x7e, - 0x7a, 0xec, 0x1d, 0x43, 0x60, 0xc2, 0x0a, 0x27, 0xaf, 0xcc, 0xfd, 0x9f, 0xae, 0x0b, 0xf9, 0x1b, 0x65, 0xc5, - 0x52, 0x47, 0x33, 0xab, 0x8f, 0x59, 0x3d, 0xab, 0xcd, 0x62, 0xb3, 0x57, 0x16, 0x39, 0xd6, 0x24, 0xe6, 0x51, - 0x52, 0xab, 0x8f, 0x53, 0x0c, 0x35, 0x9f, 0x08, 0x61, 0xd8, 0x07, 0xca, 0x0d, 0xbf, 0x50, 0x0d, 0x6a, 0x61, - 0x56, 0xa3, 0x8e, 0x08, 0x8a, 0x22, 0xb6, 0x5e, 0x52, 0xbc, 0x51, 0x4d, 0x16, 0xcc, 0xf8, 0x06, 0x81, 0x8c, - 0xe9, 0x1a, 0xb7, 0x79, 0x37, 0x36, 0x5a, 0xf9, 0x0b, 0xbf, 0x74, 0xa3, 0x5b, 0xe6, 0xb4, 0x0b, 0x8e, 0xed, - 0xf2, 0x78, 0x5e, 0x42, 0x87, 0x4d, - ]; - - assert_eq!( - ChaCha20::seal(&data_bytes, &key[..31].to_vec(), &nonce), - Err(CipherError::KeyLengthError) - ); - - assert_eq!( - ChaCha20::seal(&data_bytes, &key, &nonce[..11].to_vec()), - Err(CipherError::NonceLengthError) - ); - - let cipher_text = ChaCha20::seal(&data_bytes, &key, &nonce).unwrap(); - - assert_eq!(cipher_text, desired_encoded_bytes); - - let plain_text: Vec = ChaCha20::open(&cipher_text, &key, &nonce).unwrap(); - - assert_eq!(plain_text, data_bytes); - } - - #[test] - fn test_integral_nonce_cipher() { - let key: Vec = vec![ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, - 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - ]; - let data_bytes: Vec = "One Ring to rule them all, One Ring to find them, One Ring to bring them all, and \ - in the darkness bind them" - .as_bytes() - .to_vec(); - - let cipher_text = ChaCha20::seal_with_integral_nonce(&data_bytes, &key).unwrap(); - let plain_text: Vec = ChaCha20::open_with_integral_nonce(&cipher_text, &key).unwrap(); - - assert_eq!(plain_text, data_bytes); - } -} diff --git a/infrastructure/tari_util/src/ciphers/cipher.rs b/infrastructure/tari_util/src/ciphers/cipher.rs deleted file mode 100644 index 9a877819c5..0000000000 --- a/infrastructure/tari_util/src/ciphers/cipher.rs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::{ByteArray, ByteArrayError}; -use derive_error::Error; - -#[derive(Debug, Error, PartialEq)] -pub enum CipherError { - /// Provided key is the incorrect size to be used by the Cipher - KeyLengthError, - /// Provided Nonce is the incorrect size to be used by the Cipher - NonceLengthError, - /// No data was provided for encryption/decryption - NoDataError, - /// Byte Array conversion error - ByteArrayError(ByteArrayError), -} - -/// A trait describing an interface to a symmetrical encryption scheme -pub trait Cipher -where D: ByteArray -{ - /// Encrypt using a cipher and provided key and nonce - fn seal(plain_text: &D, key: &[u8], nonce: &[u8]) -> Result, CipherError>; - - /// Decrypt using a cipher and provided key and nonce - fn open(cipher_text: &[u8], key: &[u8], nonce: &[u8]) -> Result; - - /// Encrypt using a cipher and provided key, the nonce will be generate internally and appended to the cipher text - fn seal_with_integral_nonce(plain_text: &D, key: &[u8]) -> Result, CipherError>; - - /// Decrypt using a cipher and provided key. The integral nonce will be read from the cipher text - fn open_with_integral_nonce(cipher_text: &[u8], key: &[u8]) -> Result; -} diff --git a/infrastructure/tari_util/src/ciphers/mod.rs b/infrastructure/tari_util/src/ciphers/mod.rs deleted file mode 100644 index 9ddd83ba16..0000000000 --- a/infrastructure/tari_util/src/ciphers/mod.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE - -pub mod chacha20; -pub mod cipher; diff --git a/infrastructure/tari_util/src/epoch_time.rs b/infrastructure/tari_util/src/epoch_time.rs deleted file mode 100644 index 227233a816..0000000000 --- a/infrastructure/tari_util/src/epoch_time.rs +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2019. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use chrono::{DateTime, NaiveDateTime, Utc}; -use newtype_ops::newtype_ops; -use serde::{Deserialize, Serialize}; -use std::{fmt, ops::Div}; - -/// The timestamp, defined as the amount of seconds past from UNIX epoch. -#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Deserialize, Serialize)] -pub struct EpochTime(u64); - -impl EpochTime { - /// return UTC current as EpochTime - pub fn now() -> EpochTime { - EpochTime(Utc::now().timestamp() as u64) - } - - /// Return the EpochTime as a u64 - pub fn as_u64(&self) -> u64 { - self.0 - } - - /// This will return a new EpochTime increased by the amount of seconds given - /// This will panic if combined EpochTime and seconds are larger than U64::MAX - pub fn increase(&self, seconds: u64) -> EpochTime { - let num = seconds.checked_add(self.0); - let value = match num { - Some(v) => v, - _ => panic!("u64 overlfow in timestamp"), - }; - EpochTime(value) - } - - pub fn checked_sub(&self, other: EpochTime) -> Option { - match self.0.checked_sub(other.0) { - None => None, - Some(v) => Some(EpochTime(v)), - } - } -} - -impl Default for EpochTime { - fn default() -> Self { - EpochTime(Utc::now().timestamp() as u64) - } -} - -// You can only add or subtract EpochTime from EpochTime -newtype_ops! { [EpochTime] {add sub} {:=} Self Self } -newtype_ops! { [EpochTime] {add sub} {:=} &Self &Self } -newtype_ops! { [EpochTime] {add sub} {:=} Self &Self } - -// Multiplication and division of EpochTime by scalar is EpochTime -newtype_ops! { [EpochTime] {mul div rem} {:=} Self u64 } - -// Division of EpochTime by EpochTime is a EpochTime ratio (scalar) (newtype_ops doesn't handle this case) -impl Div for EpochTime { - type Output = u64; - - fn div(self, rhs: Self) -> Self::Output { - self.0 / rhs.0 - } -} - -impl fmt::Display for EpochTime { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -impl From for EpochTime { - fn from(value: u64) -> Self { - EpochTime(value) - } -} - -impl From> for EpochTime { - fn from(value: DateTime) -> Self { - EpochTime(value.timestamp() as u64) - } -} - -impl From for DateTime { - fn from(value: EpochTime) -> Self { - DateTime::::from_utc(NaiveDateTime::from_timestamp(value.0 as i64, 0), Utc) - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn add_epoch_time() { - assert_eq!(EpochTime::from(1_000) + EpochTime::from(8_000), EpochTime::from(9_000)); - assert_eq!(&EpochTime::from(15) + &EpochTime::from(5), EpochTime::from(20)); - } -} diff --git a/infrastructure/tari_util/src/extend_bytes.rs b/infrastructure/tari_util/src/extend_bytes.rs deleted file mode 100644 index bb61ddf8fe..0000000000 --- a/infrastructure/tari_util/src/extend_bytes.rs +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use chrono::{DateTime, Utc}; - -/// this trait allows us to call append_raw_bytes and get the raw bytes of the type -pub trait ExtendBytes { - fn append_raw_bytes(&self, buf: &mut Vec); -} - -impl ExtendBytes for Vec -where T: ExtendBytes -{ - fn append_raw_bytes(&self, buf: &mut Vec) { - for t in self { - t.append_raw_bytes(buf); - } - } -} - -impl ExtendBytes for [T] -where T: ExtendBytes -{ - fn append_raw_bytes(&self, buf: &mut Vec) { - for t in self { - t.append_raw_bytes(buf); - } - } -} - -impl ExtendBytes for str { - fn append_raw_bytes(&self, buf: &mut Vec) { - buf.extend(self.as_bytes()) - } -} - -impl ExtendBytes for &str { - fn append_raw_bytes(&self, buf: &mut Vec) { - buf.extend(self.as_bytes()) - } -} - -impl ExtendBytes for String { - fn append_raw_bytes(&self, buf: &mut Vec) { - buf.extend(self.as_bytes()) - } -} - -impl ExtendBytes for i8 { - fn append_raw_bytes(&self, buf: &mut Vec) { - let bytes = self.to_le_bytes(); - buf.extend_from_slice(&bytes); - } -} -impl ExtendBytes for i16 { - fn append_raw_bytes(&self, buf: &mut Vec) { - let bytes = self.to_le_bytes(); - buf.extend_from_slice(&bytes); - } -} -impl ExtendBytes for i32 { - fn append_raw_bytes(&self, buf: &mut Vec) { - let bytes = self.to_le_bytes(); - buf.extend_from_slice(&bytes); - } -} - -impl ExtendBytes for i128 { - fn append_raw_bytes(&self, buf: &mut Vec) { - let bytes = self.to_le_bytes(); - buf.extend_from_slice(&bytes); - } -} - -impl ExtendBytes for u8 { - fn append_raw_bytes(&self, buf: &mut Vec) { - let bytes = self.to_le_bytes(); - buf.extend_from_slice(&bytes); - } -} -impl ExtendBytes for u16 { - fn append_raw_bytes(&self, buf: &mut Vec) { - let bytes = self.to_le_bytes(); - buf.extend_from_slice(&bytes); - } -} -impl ExtendBytes for u32 { - fn append_raw_bytes(&self, buf: &mut Vec) { - let bytes = self.to_le_bytes(); - buf.extend_from_slice(&bytes); - } -} - -impl ExtendBytes for u64 { - fn append_raw_bytes(&self, buf: &mut Vec) { - let bytes = self.to_le_bytes(); - buf.extend_from_slice(&bytes); - } -} - -impl ExtendBytes for u128 { - fn append_raw_bytes(&self, buf: &mut Vec) { - let bytes = self.to_le_bytes(); - buf.extend_from_slice(&bytes); - } -} - -impl ExtendBytes for bool { - fn append_raw_bytes(&self, buf: &mut Vec) { - buf.extend_from_slice(if *self { &[1u8] } else { &[0u8] }); - } -} - -impl ExtendBytes for DateTime { - fn append_raw_bytes(&self, buf: &mut Vec) { - let bytes = self.timestamp().to_le_bytes(); - buf.extend_from_slice(&bytes); - } -} diff --git a/infrastructure/tari_util/src/fixed_set.rs b/infrastructure/tari_util/src/fixed_set.rs deleted file mode 100644 index dc63427f31..0000000000 --- a/infrastructure/tari_util/src/fixed_set.rs +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright 2019. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use std::ops::Add; - -#[derive(Clone, Debug)] -pub struct FixedSet { - items: Vec>, -} - -impl FixedSet { - /// Creates a new fixed set of size n. - pub fn new(n: usize) -> FixedSet { - FixedSet { items: vec![None; n] } - } - - /// Returns the size of the fixed set, NOT the number of items that have been set - pub fn size(&self) -> usize { - self.items.len() - } - - /// Set the `index`th item to `val`. Any existing item is overwritten. The set takes ownership of `val`. - pub fn set_item(&mut self, index: usize, val: T) -> bool { - if index >= self.items.len() { - return false; - } - self.items[index] = Some(val); - true - } - - /// Return a reference to the `index`th item, or `None` if that item has not been set yet. - pub fn get_item(&self, index: usize) -> Option<&T> { - match self.items.get(index) { - None => None, - Some(option) => option.as_ref(), - } - } - - /// Delete an item from the set by setting the `index`th value to None - pub fn clear_item(&mut self, index: usize) { - if index < self.items.len() { - self.items[index] = None; - } - } - - /// Returns true if every item in the set has been set. An empty set returns true as well. - pub fn is_full(&self) -> bool { - self.items.iter().all(Option::is_some) - } - - /// Return the index of the given item in the set by performing a linear search through the set - pub fn search(&self, val: &T) -> Option { - let key = self - .items - .iter() - .enumerate() - .find(|v| v.1.is_some() && v.1.as_ref().unwrap() == val); - match key { - Some(item) => Some(item.0), - None => None, - } - } - - /// Produces the sum of the values in the set, provided the set is full - pub fn sum(&self) -> Option - where for<'a> &'a T: Add<&'a T, Output = T> { - // This function uses HTRB to work: See https://doc.rust-lang.org/nomicon/hrtb.html - // or here https://users.rust-lang.org/t/lifetimes-for-type-constraint-where-one-reference-is-local/11087 - if self.size() == 0 { - return Some(T::default()); - } - if !self.is_full() { - return None; - } - let mut iter = self.items.iter().filter_map(Option::as_ref); - // Take the first item - let mut sum = iter.next().unwrap().clone(); - for v in iter { - sum = &sum + v; - } - Some(sum) - } - - /// Collects all non-empty elements of the set into a Vec instance - pub fn into_vec(self) -> Vec { - self.items.into_iter().filter_map(|v| v).collect() - } -} - -//------------------------------------------- Tests ---------------------------------------------// - -#[cfg(test)] -mod test { - use super::FixedSet; - - #[derive(Eq, PartialEq, Clone, Debug, Default)] - struct Foo { - baz: String, - } - - #[test] - fn zero_sized_fixed_set() { - let mut s = FixedSet::::new(0); - assert!(s.is_full(), "Set should be full"); - assert_eq!(s.set_item(1, 1), false, "Should not be able to set item"); - assert_eq!(s.get_item(0), None, "Should not return a value"); - assert_eq!(s.sum(), Some(0)); - } - - fn data(s: &str) -> Foo { - match s { - "patrician" => Foo { - baz: "The Patrician".into(), - }, - "rincewind" => Foo { - baz: "Rincewind".into(), - }, - "vimes" => Foo { - baz: "Commander Vimes".into(), - }, - "librarian" => Foo { - baz: "The Librarian".into(), - }, - "carrot" => Foo { - baz: "Captain Carrot".into(), - }, - _ => Foo { baz: "None".into() }, - } - } - - #[test] - fn small_set() { - let mut s = FixedSet::::new(3); - // Set is empty - assert_eq!(s.is_full(), false); - // Add an item - assert!(s.set_item(1, data("patrician"))); - assert_eq!(s.is_full(), false); - // Add an item - assert!(s.set_item(0, data("vimes"))); - assert_eq!(s.is_full(), false); - // Replace an item - assert!(s.set_item(1, data("rincewind"))); - assert_eq!(s.is_full(), false); - // Add item, filling set - assert!(s.set_item(2, data("carrot"))); - assert_eq!(s.is_full(), true); - // Try add an invalid item - assert_eq!(s.set_item(3, data("librarian")), false); - assert_eq!(s.is_full(), true); - // Clear an item - s.clear_item(1); - assert_eq!(s.is_full(), false); - // Check contents - assert_eq!(s.get_item(0).unwrap().baz, "Commander Vimes"); - assert!(s.get_item(1).is_none()); - assert_eq!(s.get_item(2).unwrap().baz, "Captain Carrot"); - // Size is 3 - assert_eq!(s.size(), 3); - // Slow search - assert_eq!(s.search(&data("carrot")), Some(2)); - assert_eq!(s.search(&data("vimes")), Some(0)); - assert_eq!(s.search(&data("librarian")), None); - } - - #[test] - fn sum_values() { - let mut s = FixedSet::::new(4); - s.set_item(0, 5); - assert_eq!(s.sum(), None); - s.set_item(1, 4); - assert_eq!(s.sum(), None); - s.set_item(2, 3); - assert_eq!(s.sum(), None); - s.set_item(3, 2); - assert_eq!(s.sum(), Some(14)); - s.set_item(1, 0); - assert_eq!(s.sum(), Some(10)); - } -} diff --git a/infrastructure/tari_util/src/hex.rs b/infrastructure/tari_util/src/hex.rs deleted file mode 100644 index b685925ee8..0000000000 --- a/infrastructure/tari_util/src/hex.rs +++ /dev/null @@ -1,127 +0,0 @@ -use derive_error::Error; -use serde::Serializer; -use std::{ - fmt::{LowerHex, Write}, - num::ParseIntError, -}; - -/// Any object implementing this trait has the ability to represent itself as a hexadecimal string and convert from it. -pub trait Hex { - type T; - /// Try and convert the given hexadecimal string to the type. Any failures (incorrect string length, non hex - /// characters, etc) return a [KeyError](enum.KeyError.html) with an explanatory note. - fn from_hex(hex: &str) -> Result; - - /// Return the hexadecimal string representation of the type - fn to_hex(&self) -> String; -} - -#[derive(Debug, Error)] -pub enum HexError { - /// Only hexadecimal characters (0-9,a-f) are permitted - InvalidCharacter(ParseIntError), - /// Hex string lengths must be a multiple of 2 - LengthError, - /// Invalid hex representation for the target type - HexConversionError, -} - -/// Encode the provided bytes into a hex string -pub fn to_hex(bytes: &[T]) -> String -where T: LowerHex { - let mut s = String::new(); - for byte in bytes { - write!(&mut s, "{:02x}", byte).expect("Unable to write"); - } - s -} - -/// Encode the provided vector of bytes into a hex string -pub fn to_hex_multiple(bytearray: &[Vec]) -> Vec { - let mut result = Vec::new(); - for bytes in bytearray { - result.push(to_hex(bytes)) - } - result -} - -/// Decode a hex string into bytes. -pub fn from_hex(hex_str: &str) -> Result, HexError> { - let hex_trim = hex_str.trim(); - let hex_trim = if (hex_trim.len() >= 2) && (&hex_trim[..2] == "0x") { - &hex_trim[2..] - } else { - hex_trim - }; - if hex_trim.len() % 2 == 1 { - return Err(HexError::LengthError); - } - let num_bytes = hex_trim.len() / 2; - let mut result = vec![0u8; num_bytes]; - for i in 0..num_bytes { - let val = u8::from_str_radix(&hex_trim[2 * i..2 * (i + 1)], 16); - result[i] = match val { - Ok(v) => v, - Err(e) => return Err(HexError::InvalidCharacter(e)), - } - } - Ok(result) -} - -pub fn serialize_to_hex(t: &T, ser: S) -> Result -where - S: Serializer, - T: Hex, -{ - ser.serialize_str(&t.to_hex()) -} - -#[cfg(test)] -mod test { - use super::*; - use std::error::Error; - - #[test] - fn test_to_hex() { - assert_eq!(to_hex(&[0, 0, 0, 0]), "00000000"); - assert_eq!(to_hex(&[10, 11, 12, 13]), "0a0b0c0d"); - assert_eq!(to_hex(&[0, 0, 0, 255]), "000000ff"); - } - - #[test] - fn test_from_hex() { - assert_eq!(from_hex(&"00000000").unwrap(), vec![0, 0, 0, 0]); - assert_eq!(from_hex(&"0a0b0c0d").unwrap(), vec![10, 11, 12, 13]); - assert_eq!(from_hex(&" 0a0b0c0d ").unwrap(), vec![10, 11, 12, 13]); - assert_eq!(from_hex(&"000000ff").unwrap(), vec![0, 0, 0, 255]); - assert_eq!(from_hex(&"0x800000ff").unwrap(), vec![128, 0, 0, 255]); - assert!(from_hex(&"800").is_err()); // Odd number of bytes - assert!(from_hex(&"8080gf").is_err()); // Invalid hex character g - } - - #[test] - fn length_error() { - let result = from_hex(&"800"); - assert!(result.is_err()); - let err = result.unwrap_err(); - match err { - HexError::LengthError => (), - _ => panic!(), - } - // Check that message is the doc message above - assert_eq!(err.description(), "Hex string lengths must be a multiple of 2"); - } - - #[test] - fn character_error() { - let result = from_hex(&"1234567890ABCDEFG1"); - assert!(result.is_err()); - let err = result.unwrap_err(); - match &err { - HexError::InvalidCharacter(e) => println!("{:?}", e), - _ => panic!(), - } - // Check that message is inherited from ParseIntError - assert_eq!(err.description(), "invalid digit found in string"); - } -} diff --git a/infrastructure/tari_util/src/message_format.rs b/infrastructure/tari_util/src/message_format.rs deleted file mode 100644 index ff0f5695b9..0000000000 --- a/infrastructure/tari_util/src/message_format.rs +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright 2018 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use base64; -use derive_error::Error; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use serde_json; - -#[derive(Debug, Error)] -pub enum MessageFormatError { - // An error occurred serialising an object into binary - #[error(no_from, no_std)] - BinarySerializeError, - // An error occurred deserialising binary data into an object - #[error(no_from, no_std)] - BinaryDeserializeError, - // An error occurred de-/serialising an object from/into JSON - JSONError(serde_json::error::Error), - // An error occurred deserialising an object from Base64 - Base64DeserializeError(base64::DecodeError), -} - -pub trait MessageFormat: Sized { - fn to_binary(&self) -> Result, MessageFormatError>; - fn to_json(&self) -> Result; - fn to_base64(&self) -> Result; - - fn from_binary(msg: &[u8]) -> Result; - fn from_json(msg: &str) -> Result; - fn from_base64(msg: &str) -> Result; -} - -impl MessageFormat for T -where T: DeserializeOwned + Serialize -{ - fn to_binary(&self) -> Result, MessageFormatError> { - bincode::serialize(self).map_err(|_| MessageFormatError::BinarySerializeError) - } - - fn to_json(&self) -> Result { - serde_json::to_string(self).map_err(MessageFormatError::JSONError) - } - - fn to_base64(&self) -> Result { - let val = self.to_binary()?; - Ok(base64::encode(&val)) - } - - fn from_binary(msg: &[u8]) -> Result { - bincode::deserialize(msg).map_err(|_| MessageFormatError::BinaryDeserializeError) - } - - fn from_json(msg: &str) -> Result { - let mut de = serde_json::Deserializer::from_reader(msg.as_bytes()); - Deserialize::deserialize(&mut de).map_err(MessageFormatError::JSONError) - } - - fn from_base64(msg: &str) -> Result { - let buf = base64::decode(msg)?; - Self::from_binary(&buf) - } -} - -#[cfg(test)] -mod test { - use super::*; - use base64::DecodeError as Base64Error; - use serde::{Deserialize, Serialize}; - - #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] - struct TestMessage { - key: String, - value: u64, - sub_message: Option>, - } - - impl TestMessage { - pub fn new(key: &str, value: u64) -> TestMessage { - TestMessage { - key: key.to_string(), - value, - sub_message: None, - } - } - - pub fn set_sub_message(&mut self, msg: TestMessage) { - self.sub_message = Some(Box::new(msg)); - } - } - - #[test] - fn binary_simple() { - let val = TestMessage::new("twenty", 20); - let msg = val.to_binary().unwrap(); - assert_eq!( - msg, - b"\x06\x00\x00\x00\x00\x00\x00\x00\x74\x77\x65\x6e\x74\x79\x14\x00\x00\x00\x00\x00\x00\x00\x00" - ); - let val2 = TestMessage::from_binary(&msg).unwrap(); - assert_eq!(val, val2); - } - - #[test] - fn base64_simple() { - let val = TestMessage::new("twenty", 20); - let msg = val.to_base64().unwrap(); - assert_eq!(msg, "BgAAAAAAAAB0d2VudHkUAAAAAAAAAAA="); - let val2 = TestMessage::from_base64(&msg).unwrap(); - assert_eq!(val, val2); - } - - #[test] - fn json_simple() { - let val = TestMessage::new("twenty", 20); - let msg = val.to_json().unwrap(); - assert_eq!(msg, "{\"key\":\"twenty\",\"value\":20,\"sub_message\":null}"); - let val2 = TestMessage::from_json(&msg).unwrap(); - assert_eq!(val, val2); - } - - #[test] - fn nested_message() { - let inner = TestMessage::new("today", 100); - let mut val = TestMessage::new("tomorrow", 50); - val.set_sub_message(inner); - - let msg_json = val.to_json().unwrap(); - assert_eq!( - msg_json, - "{\"key\":\"tomorrow\",\"value\":50,\"sub_message\":{\"key\":\"today\",\"value\":100,\"sub_message\":\ - null}}" - ); - - let msg_base64 = val.to_base64().unwrap(); - assert_eq!( - msg_base64, - "CAAAAAAAAAB0b21vcnJvdzIAAAAAAAAAAQUAAAAAAAAAdG9kYXlkAAAAAAAAAAA=" - ); - - let msg_bin = val.to_binary().unwrap(); - assert_eq!( - msg_bin, - b"\x08\x00\x00\x00\x00\x00\x00\x00\x74\x6f\x6d\x6f\x72\x72\x6f\x77\x32\x00\x00\x00\x00\x00\x00\x00\x01\x05\x00\x00\x00\x00\x00\x00\x00\x74\x6f\x64\x61\x79\x64\x00\x00\x00\x00\x00\x00\x00\x00".to_vec() - ); - - let val2 = TestMessage::from_json(&msg_json).unwrap(); - assert_eq!(val, val2); - - let val2 = TestMessage::from_base64(&msg_base64).unwrap(); - assert_eq!(val, val2); - - let val2 = TestMessage::from_binary(&msg_bin).unwrap(); - assert_eq!(val, val2); - } - - #[test] - fn fail_json() { - let err = TestMessage::from_json("{\"key\":5}").err().unwrap(); - match err { - MessageFormatError::JSONError(e) => { - assert_eq!(e.line(), 1); - assert_eq!(e.column(), 9); - assert!(e.is_data()); - }, - _ => panic!("JSON conversion should fail"), - }; - } - - #[test] - fn fail_base64() { - let err = TestMessage::from_base64("aaaaa$aaaaa").err().unwrap(); - match err { - MessageFormatError::Base64DeserializeError(Base64Error::InvalidByte(offset, val)) => { - assert_eq!(offset, 5); - assert_eq!(val, b'$'); - }, - _ => panic!("Base64 conversion should fail"), - }; - - let err = TestMessage::from_base64("j6h0b21vcnJvdzKTpXRvZGF5ZMA=").err().unwrap(); - match err { - MessageFormatError::BinaryDeserializeError => {}, - _ => panic!("Base64 conversion should fail"), - }; - } - - #[test] - fn fail_binary() { - let err = TestMessage::from_binary(b"").err().unwrap(); - match err { - MessageFormatError::BinaryDeserializeError => {}, - _ => { - panic!("Base64 conversion should fail"); - }, - } - } -} diff --git a/infrastructure/tari_util/src/thread_join/mod.rs b/infrastructure/tari_util/src/thread_join/mod.rs deleted file mode 100644 index 47120eb6eb..0000000000 --- a/infrastructure/tari_util/src/thread_join/mod.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -pub mod error; -pub mod thread_join; - -pub use self::{error::ThreadError, thread_join::ThreadJoinWithTimeout}; diff --git a/infrastructure/tari_util/src/thread_join/thread_join.rs b/infrastructure/tari_util/src/thread_join/thread_join.rs deleted file mode 100644 index 3ee2991561..0000000000 --- a/infrastructure/tari_util/src/thread_join/thread_join.rs +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2019 The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::thread_join::ThreadError; -use std::{ - sync::mpsc::{sync_channel, RecvTimeoutError, SyncSender}, - thread::{self, JoinHandle}, - time::Duration, -}; - -#[derive(Debug)] -pub enum StatusMessage { - /// Successfully joined the thread - Ok, - /// An error occurred attempting to join the thread - Error, -} - -/// Spawn a single thread that will attempt to join the specified thread -fn spawn_join_thread(thread_handle: JoinHandle, status_sync_sender: SyncSender) -where T: 'static { - thread::spawn(move || match thread_handle.join() { - Ok(_) => status_sync_sender.send(StatusMessage::Ok).unwrap(), - Err(_) => status_sync_sender.send(StatusMessage::Error).unwrap(), - }); -} - -/// Perform a thread join with a timeout on the JoinHandle, it has a configurable timeout -fn timeout_join(thread_handle: JoinHandle, timeout_in_ms: Duration) -> Result<(), ThreadError> -where T: 'static { - let (status_sync_sender, status_receiver) = sync_channel(5); - spawn_join_thread(thread_handle, status_sync_sender); - - // Check for status messages - match status_receiver.recv_timeout(timeout_in_ms) { - Ok(status_msg) => match status_msg { - StatusMessage::Ok => Ok(()), - StatusMessage::Error => Err(ThreadError::JoinError), - }, - Err(RecvTimeoutError::Timeout) => Err(ThreadError::TimeoutReached), - Err(RecvTimeoutError::Disconnected) => Err(ThreadError::ChannelDisconnected), - } -} - -pub trait ThreadJoinWithTimeout { - /// Attempt to join the current thread with a configurable timeout - fn timeout_join(self, timeout_in_ms: Duration) -> Result<(), ThreadError>; -} - -/// Extend JoinHandle to have member functions that enable join with a timeout -impl ThreadJoinWithTimeout for JoinHandle -where T: 'static -{ - fn timeout_join(self, timeout: Duration) -> Result<(), ThreadError> { - timeout_join(self, timeout) - } -} - -#[cfg(test)] -mod test { - use crate::thread_join::ThreadJoinWithTimeout; - use std::{thread, time::Duration}; - - #[test] - fn test_normal_thread_join() { - // Create a blocking thread - let thread_handle = thread::spawn(move || { - thread::sleep(Duration::from_millis(50)); - }); - - let join_timeout_in_ms = Duration::from_millis(100); - assert!(thread_handle.timeout_join(join_timeout_in_ms).is_ok()); - } - - #[test] - fn test_thread_join_with_timeout() { - // Create a blocking thread - let thread_handle = thread::spawn(move || { - thread::sleep(Duration::from_millis(50)); - }); - - let join_timeout_in_ms = Duration::from_millis(25); - assert!(thread_handle.timeout_join(join_timeout_in_ms).is_err()); - } -} diff --git a/infrastructure/test_utils/Cargo.toml b/infrastructure/test_utils/Cargo.toml index 1f94d8d244..9fda9b9919 100644 --- a/infrastructure/test_utils/Cargo.toml +++ b/infrastructure/test_utils/Cargo.toml @@ -1,16 +1,17 @@ [package] name = "tari_test_utils" -version = "0.0.6" +description = "Utility functions used in Tari test functions" +version = "0.0.9" authors = ["The Tari Development Community"] edition = "2018" +license = "BSD-3-Clause" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -futures-test = { version = "0.3.0-alpha.18", package = "futures-test-preview" } -futures= {version= "0.3.0-alpha.18", package="futures-preview"} +futures-test = { version = "^0.3.1" } +futures = {version= "^0.3.1"} rand = "0.7.0" -tokio = "0.2.0-alpha.4" -tokio-executor = "0.2.0-alpha.4" +tokio = {version= "0.2.10", features=["rt-threaded", "time", "io-driver"]} lazy_static = "1.3.0" tempdir = "0.3.7" diff --git a/infrastructure/test_utils/README.md b/infrastructure/test_utils/README.md new file mode 100644 index 0000000000..c347fb1a4e --- /dev/null +++ b/infrastructure/test_utils/README.md @@ -0,0 +1 @@ +# Tari test utils \ No newline at end of file diff --git a/infrastructure/test_utils/src/address.rs b/infrastructure/test_utils/src/address.rs index 9e221c1c8c..3eb4556afa 100644 --- a/infrastructure/test_utils/src/address.rs +++ b/infrastructure/test_utils/src/address.rs @@ -30,18 +30,18 @@ lazy_static! { static ref PORT_COUNTER: Mutex = Mutex::new(PORT_RANGE.start); } -/// Maintains a counter of ports within a range (40000..48000), returning them in -/// sequence. Port numbers will wrap back to 40000 once the upper bound is exceeded. +/// Maintains a counter of ports within a range (20000..30000), returning them in +/// sequence. Port numbers will wrap back to 20000 once the upper bound is exceeded. +/// This range is chosen because OS-assigned ports are not typically in this range. pub fn get_next_local_port() -> u16 { let mut lock = match PORT_COUNTER.lock() { Ok(guard) => guard, Err(_) => panic!("Poisoned PORT_COUNTER"), }; - let port = { + { *lock = cmp::max((*lock + 1) % PORT_RANGE.end, PORT_RANGE.start); *lock - }; - port + } } /// Returns a local address with the next port in specified range. diff --git a/infrastructure/test_utils/src/enums.rs b/infrastructure/test_utils/src/enums.rs index 2d778ce46f..0e3e232f12 100644 --- a/infrastructure/test_utils/src/enums.rs +++ b/infrastructure/test_utils/src/enums.rs @@ -20,7 +20,8 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -/// Unpack the tuple or struct variant variables from an enum. +/// Unpack the tuple or struct from an enum variant. +/// Each extracted variable is decalred as mutable for maximum flexibility. /// /// ```edition2018 /// # use tari_test_utils::unpack_enum; @@ -52,21 +53,22 @@ #[macro_export] macro_rules! unpack_enum { ($($enum_key:ident)::+ { $($idents:tt),* } = $enum:expr) => { - let ($($idents),+) = match $enum { + let ($(mut $idents),+) = match $enum { $($enum_key)::+{$($idents),+} => ($($idents),+), - _ => panic!("Unexpected enum variant given to unpack_enum"), + v => panic!("Unexpected enum variant '{:?}' given to unpack_enum", v), }; }; ($($enum_key:ident)::+ ( $($idents:tt),* ) = $enum:expr) => { - let ($($idents),+) = match $enum { + let ($(mut $idents),+) = match $enum { $($enum_key)::+($($idents),+) => ($($idents),+), - _ => panic!("Unexpected enum variant given to unpack_enum"), + v => panic!("Unexpected enum variant '{:?}' given to unpack_enum", v), }; }; + // This matches `MyEnum::First = some_var` and will panic if the enum does not match ($($enum_key:ident)::+ = $enum:expr) => { match $enum { $($enum_key)::+ => {}, - _ => panic!("Unexpected enum variant given to unpack_enum"), + v => panic!("Unexpected enum variant '{:?}' given to unpack_enum", v), }; }; } diff --git a/infrastructure/test_utils/src/futures/async_assert_eventually.rs b/infrastructure/test_utils/src/futures/async_assert_eventually.rs index 7d44ffead9..89961e7a3a 100644 --- a/infrastructure/test_utils/src/futures/async_assert_eventually.rs +++ b/infrastructure/test_utils/src/futures/async_assert_eventually.rs @@ -46,7 +46,7 @@ macro_rules! async_assert_eventually { $max_attempts ); } - tokio::timer::delay(Instant::now() + $interval).await; + tokio::time::delay_for($interval).await; value = $check_expr; } }}; diff --git a/infrastructure/test_utils/src/futures/mod.rs b/infrastructure/test_utils/src/futures/mod.rs index a12b9b75f9..e95ce26045 100644 --- a/infrastructure/test_utils/src/futures/mod.rs +++ b/infrastructure/test_utils/src/futures/mod.rs @@ -81,8 +81,8 @@ mod test { use futures::{ future::{self, FutureExt}, task::Context, - Poll, }; + use std::task::Poll; #[test] fn counter_context() { diff --git a/infrastructure/test_utils/src/runtime.rs b/infrastructure/test_utils/src/runtime.rs index 62dcbe91a3..1a20e46784 100644 --- a/infrastructure/test_utils/src/runtime.rs +++ b/infrastructure/test_utils/src/runtime.rs @@ -20,84 +20,77 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::sync::{ - atomic::{AtomicBool, Ordering}, - Arc, -}; -use tokio::runtime::Runtime; +use futures::FutureExt; +use std::{future::Future, pin::Pin}; +use tokio::{runtime, runtime::Runtime, task, task::JoinError}; -pub fn create_runtime(passed: Arc) -> Runtime { +pub fn create_runtime() -> Runtime { tokio::runtime::Builder::new() - .blocking_threads(4) - // Run the work scheduler on one thread so we can really see the effects of using `blocking` above - .core_threads(1) - .panic_handler(move |_| passed.store(false, Ordering::SeqCst)) + .threaded_scheduler() + .enable_io() + .enable_time() + .max_threads(8) + .core_threads(4) .build() .expect("Could not create runtime") } /// Create a runtime and report if it panics. If there are tasks still running after the panic, this /// will carry on running forever. -/// TODO: Create a test harness which allows test cleanup or at least fail the test after a timeout pub fn test_async(f: F) -where F: FnOnce(&Runtime) { - let passed = Arc::new(AtomicBool::new(true)); - let rt: Runtime = create_runtime(passed.clone()); - f(&rt); - rt.shutdown_on_idle(); - assert!(passed.load(Ordering::SeqCst), "Panic occurred in spawned future"); +where F: FnOnce(&mut TestRuntime) { + let mut rt = TestRuntime::from(create_runtime()); + f(&mut rt); + let handles = rt.handles.drain(..).collect::>(); + for h in handles { + rt.block_on(h).unwrap(); + } } -// TODO: WIP for a test harness which allows shutdown code to be run in the case of a successful/failed test -// pub fn run_with_complete(f: F, on_complete: FComplete) -// where F: FnOnce(&Runtime) { -// let passed = Arc::new(AtomicBool::new(true)); -// let passed_inner = Arc::clone(&passed); -// let (panic_tx, panic_rx) = oneshot::channel(); -// let (complete_tx, complete_rx) = oneshot::channel(); -// let panic_tx = Mutex::new(Some(panic_tx)); -// let complete_tx = Mutex::new(Some(complete_tx)); -// let rt = tokio::runtime::Builder::new() -// .blocking_threads(4) -// // Run the work scheduler on one thread so we can really see the effects of using `blocking` above -// .core_threads(1) -// .panic_handler(move |_| { -// panic_tx.lock().unwrap().take().map(|tx| tx.send(()).unwrap()); -// }) -// .build() -// .expect("Could not create runtime"); -// -// f(&rt, complete_tx); -// -// rt.block_on(async move { -// let did_panic = futures::future::select(panic_rx, complete_rx).await; -// match did_panic { -// Either::Right(_) => passed_inner.store(true, Ordering::SeqCst), -// Either::Left(_) => passed_inner.store(false, Ordering::SeqCst), -// }; -// }); -// -// assert!(passed.load(Ordering::SeqCst), "Panic occurred in spawned future"); -//} -// -//#[cfg(test)] -// mod test { -// use super::*; -// -// #[test] -// fn test_run_with_complete() { -// let on_complete_called = Arc::new(AtomicBool::new(false)); -// let on_complete_called_inner = on_complete_called.clone(); -// run_with_complete( -// |rt| { -// rt.block_on(futures::future::ready(())); -// }, -// || { -// async move { -// on_complete_called_inner.store(true, Ordering::SeqCst); -// } -// }, -// ); -// assert_eq!(on_complete_called.load(Ordering::SeqCst), true); -// } -//} +pub struct TestRuntime { + inner: Runtime, + handles: Vec>>>>, +} + +impl TestRuntime { + pub fn block_on(&mut self, future: F) -> F::Output { + self.inner.block_on(future) + } + + pub fn spawn(&mut self, future: F) + where + F: Future + Send + 'static, + F::Output: Send + 'static, + { + let handle = self.inner.spawn(future); + self.handles.push( + handle + .map(|result| match result { + Ok(_) => Ok(()), + Err(err) => Err(err), + }) + .boxed(), + ); + } + + pub fn spawn_unchecked(&mut self, future: F) -> task::JoinHandle + where + F: Future + Send + 'static, + F::Output: Send + 'static, + { + self.inner.spawn(future) + } + + pub fn handle(&self) -> &runtime::Handle { + self.inner.handle() + } +} + +impl From for TestRuntime { + fn from(rt: Runtime) -> Self { + Self { + inner: rt, + handles: Vec::new(), + } + } +} diff --git a/infrastructure/test_utils/src/streams/mod.rs b/infrastructure/test_utils/src/streams/mod.rs index 5f10374bd2..5a32be4a5b 100644 --- a/infrastructure/test_utils/src/streams/mod.rs +++ b/infrastructure/test_utils/src/streams/mod.rs @@ -23,6 +23,7 @@ use std::{collections::HashMap, hash::Hash}; #[allow(dead_code)] +#[allow(clippy::mutable_key_type)] // Note: Clippy Breaks with Interior Mutability Error pub fn get_item_counts(items: I) -> HashMap where I: IntoIterator, @@ -35,7 +36,9 @@ where }) } -/// Collect $take items from a stream or timeout for Duration $timeout. Requires the `tokio` runtime +/// Collect $take items from a stream or timeout for Duration $timeout. +/// +/// Requires the `tokio` runtime and should be used in an async context. /// /// ```edition2018 /// # use tokio::runtime::Runtime; @@ -43,26 +46,26 @@ where /// # use std::time::Duration; /// # use tari_test_utils::collect_stream; /// -/// let rt = Runtime::new().unwrap(); +/// let mut rt = Runtime::new().unwrap(); /// let stream = stream::iter(1..10); -/// assert_eq!(collect_stream!(rt, stream, take=3, timeout=Duration::from_secs(1)), vec![1,2,3]); +/// assert_eq!(rt.block_on(async { collect_stream!(stream, take=3, timeout=Duration::from_secs(1)) }), vec![1,2,3]); /// ``` #[macro_export] macro_rules! collect_stream { - ($runtime:expr, $stream:expr, take=$take:expr, timeout=$timeout:expr $(,)?) => {{ - use futures::StreamExt; - use tokio::future::FutureExt; + ($stream:expr, take=$take:expr, timeout=$timeout:expr $(,)?) => {{ + use futures::{FutureExt, StreamExt}; + use tokio::time; - $runtime - .block_on($stream.take($take).collect::>().timeout($timeout)) + time::timeout($timeout, $stream.take($take).collect::>()) + .await .expect(format!("Timeout before stream could collect {} item(s)", $take).as_str()) }}; - ($runtime:expr, $stream:expr, timeout=$timeout:expr $(,)?) => {{ - use futures::StreamExt; - use tokio::future::FutureExt; + ($stream:expr, timeout=$timeout:expr $(,)?) => {{ + use futures::{FutureExt, StreamExt}; + use tokio::time; - $runtime - .block_on($stream.collect::>().timeout($timeout)) + time::timeout($timeout, $stream.collect::>()) + .await .expect("Stream did not close within timeout") }}; } @@ -75,21 +78,21 @@ macro_rules! collect_stream { /// # use std::time::Duration; /// # use tari_test_utils::collect_stream_count; /// -/// let rt = Runtime::new().unwrap(); +/// let mut rt = Runtime::new().unwrap(); /// let stream = stream::iter(vec![1,2,2,3,2]); -/// assert_eq!(collect_stream_count!(rt, stream, timeout=Duration::from_secs(1)).get(&2), Some(&3)); +/// assert_eq!(rt.block_on(async { collect_stream_count!(stream, timeout=Duration::from_secs(1)) }).get(&2), Some(&3)); /// ``` #[macro_export] macro_rules! collect_stream_count { - ($runtime:expr, $stream:expr, take=$take:expr, timeout=$timeout:expr$(,)?) => {{ + ($stream:expr, take=$take:expr, timeout=$timeout:expr$(,)?) => {{ use std::collections::HashMap; - let items = $crate::collect_stream!($runtime, $stream, take = $take, timeout = $timeout); + let items = $crate::collect_stream!($stream, take = $take, timeout = $timeout); $crate::streams::get_item_counts(items) }}; - ($runtime:expr, $stream:expr, timeout=$timeout:expr $(,)?) => {{ + ($stream:expr, timeout=$timeout:expr $(,)?) => {{ use std::collections::HashMap; - let items = $crate::collect_stream!($runtime, $stream, timeout = $timeout); + let items = $crate::collect_stream!($stream, timeout = $timeout); $crate::streams::get_item_counts(items) }}; } diff --git a/rust-toolchain b/rust-toolchain index fb7648c5aa..ee412581d8 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly-2019-10-04 +nightly-2020-01-08 diff --git a/rustfmt.toml b/rustfmt.toml index c1597a3a43..89f940df13 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -7,7 +7,7 @@ imports_layout = "HorizontalVertical" merge_imports = true match_block_trailing_comma = true max_width = 120 -newline_style = "Unix" +newline_style = "Native" normalize_comments = true reorder_imports = true reorder_modules = true @@ -25,4 +25,4 @@ where_single_line = true wrap_comments = true overflow_delimited_expr = true edition = "2018" -ignore=["comms/yamux", "comms/rust-multiaddr"] \ No newline at end of file +ignore=[] \ No newline at end of file diff --git a/scripts/code_coverage_crypto.sh b/scripts/code_coverage_crypto.sh deleted file mode 100755 index ca19ffb8d6..0000000000 --- a/scripts/code_coverage_crypto.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -# Check if in script directory -path=$(pwd) -primary_dir=$(basename $path) -if [ "$primary_dir" != "scripts" ]; then - cd scripts -fi - -./code_coverage.sh "tari_crypto" "infrastructure/crypto/" diff --git a/scripts/code_coverage_merklemountainrange.sh b/scripts/code_coverage_merklemountainrange.sh deleted file mode 100755 index 470c448797..0000000000 --- a/scripts/code_coverage_merklemountainrange.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -# Check if in script directory -path=$(pwd) -primary_dir=$(basename $path) -if [ "$primary_dir" != "scripts" ]; then - cd scripts -fi - -./code_coverage.sh "merklemountainrange" "infrastructure/merklemountainrange/" diff --git a/scripts/publish_crates.sh b/scripts/publish_crates.sh index b3a09f6679..5678990d77 100755 --- a/scripts/publish_crates.sh +++ b/scripts/publish_crates.sh @@ -1,21 +1,21 @@ #!/usr/bin/env bash # NB: The order these are listed in is IMPORTANT! Dependencies must go first packages=${@:-' -infrastructure/tari_util infrastructure/derive -infrastructure/crypto +infrastructure/shutdown infrastructure/storage +infrastructure/test_utils common comms -base_layer/transactions +comms/dht base_layer/p2p -base_layer/core -base_layer/keymanager -base_layer/mining base_layer/mmr base_layer/service_framework +base_layer/core +base_layer/key_manager base_layer/wallet base_layer/wallet_ffi +applications/tari_base_node '} p_arr=($packages) @@ -24,6 +24,7 @@ function build_package { for p in "${list[@]}"; do echo "************************ Building $path/$p package ************************" cargo publish --manifest-path=./${p}/Cargo.toml + sleep 5 # Wait for crates.io to register any dependent packages done echo "************************ $path packages built ************************" diff --git a/scripts/test_in_docker.sh b/scripts/test_in_docker.sh index 6ab144e2a7..eda500e0a0 100755 --- a/scripts/test_in_docker.sh +++ b/scripts/test_in_docker.sh @@ -2,8 +2,8 @@ # Run the Tari test suite locally inside a suitable docker container -IMAGE=quay.io/tarilabs/rust_tari-build-with-deps:nightly-2019-10-04 -TOOLCHAIN_VERSION=nightly-2019-10-04 +IMAGE=quay.io/tarilabs/rust_tari-build-with-deps:nightly-2020-01-08 +TOOLCHAIN_VERSION=nightly-2020-01-08 CONTAINER=tari_test echo "Deleting old container" @@ -11,19 +11,11 @@ docker rm -f $CONTAINER echo "Checking for docker image" docker pull $IMAGE -echo "Creating doc -ker container.." +echo "Creating docker container.." # sleep infinity is used to keep the container alive forever docker run -dv`pwd`:/src --name $CONTAINER $IMAGE /bin/sleep infinity echo "Container is ready" -#echo "Copying source files..." -## The simplest and laziest approach to not copying 6+GB of irrelevant files to the container -#cargo clean -## We could pull this from git, but this lets you test local changes -#docker cp . $CONTAINER:/src/ -#echo "Source files copied." - CMD="cd src; cargo build; cargo test" docker exec -ti $CONTAINER /bin/bash -c "$CMD" diff --git a/scripts/update_crate_metadata.sh b/scripts/update_crate_metadata.sh index d278a97511..5bcc7fe898 100755 --- a/scripts/update_crate_metadata.sh +++ b/scripts/update_crate_metadata.sh @@ -8,19 +8,21 @@ fi function update_versions { packages=${@:-' - infrastructure/crypto infrastructure/derive + infrastructure/shutdown infrastructure/storage - infrastructure/tari_util + infrastructure/test_utils base_layer/core base_layer/keymanager - base_layer/mining base_layer/mmr base_layer/p2p base_layer/service_framework base_layer/wallet + base_layer/wallet_ffi common comms + comms/dht + applications/tari_base_node '} p_arr=($packages)